From bb756eebdac6fd24e8919e2c43f7d2c8c4091f59 Mon Sep 17 00:00:00 2001 From: RajithaY Date: Tue, 25 Apr 2017 03:31:15 -0700 Subject: Adding qemu as a submodule of KVMFORNFV This Patch includes the changes to add qemu as a submodule to kvmfornfv repo and make use of the updated latest qemu for the execution of all testcase Change-Id: I1280af507a857675c7f81d30c95255635667bdd7 Signed-off-by:RajithaY --- qemu/hw/9pfs/9p-handle.c | 709 ----- qemu/hw/9pfs/9p-local.c | 1282 -------- qemu/hw/9pfs/9p-posix-acl.c | 184 -- qemu/hw/9pfs/9p-proxy.c | 1220 -------- qemu/hw/9pfs/9p-proxy.h | 95 - qemu/hw/9pfs/9p-synth.c | 574 ---- qemu/hw/9pfs/9p-synth.h | 51 - qemu/hw/9pfs/9p-xattr-user.c | 127 - qemu/hw/9pfs/9p-xattr.c | 164 - qemu/hw/9pfs/9p-xattr.h | 120 - qemu/hw/9pfs/9p.c | 3380 -------------------- qemu/hw/9pfs/9p.h | 324 -- qemu/hw/9pfs/Makefile.objs | 9 - qemu/hw/9pfs/codir.c | 169 - qemu/hw/9pfs/cofile.c | 276 -- qemu/hw/9pfs/cofs.c | 365 --- qemu/hw/9pfs/coth.c | 42 - qemu/hw/9pfs/coth.h | 99 - qemu/hw/9pfs/coxattr.c | 108 - qemu/hw/9pfs/virtio-9p-device.c | 211 -- qemu/hw/9pfs/virtio-9p.h | 31 - qemu/hw/Makefile.objs | 38 - qemu/hw/acpi/Makefile.objs | 8 - qemu/hw/acpi/acpi_interface.c | 16 - qemu/hw/acpi/aml-build.c | 1565 ---------- qemu/hw/acpi/bios-linker-loader.c | 240 -- qemu/hw/acpi/core.c | 717 ----- qemu/hw/acpi/cpu_hotplug.c | 79 - qemu/hw/acpi/cpu_hotplug_acpi_table.c | 136 - qemu/hw/acpi/ich9.c | 477 --- qemu/hw/acpi/memory_hotplug.c | 312 -- qemu/hw/acpi/memory_hotplug_acpi_table.c | 262 -- qemu/hw/acpi/nvdimm.c | 706 ----- qemu/hw/acpi/pcihp.c | 336 -- qemu/hw/acpi/piix4.c | 645 ---- qemu/hw/acpi/tco.c | 265 -- qemu/hw/alpha/Makefile.objs | 1 - qemu/hw/alpha/alpha_sys.h | 21 - qemu/hw/alpha/dp264.c | 184 -- qemu/hw/alpha/pci.c | 94 - qemu/hw/alpha/typhoon.c | 959 ------ qemu/hw/arm/Makefile.objs | 19 - qemu/hw/arm/allwinner-a10.c | 142 - qemu/hw/arm/armv7m.c | 265 -- qemu/hw/arm/ast2400.c | 140 - qemu/hw/arm/bcm2835_peripherals.c | 312 -- qemu/hw/arm/bcm2836.c | 184 -- qemu/hw/arm/boot.c | 994 ------ qemu/hw/arm/collie.c | 68 - qemu/hw/arm/cubieboard.c | 86 - qemu/hw/arm/digic.c | 123 - qemu/hw/arm/digic_boards.c | 160 - qemu/hw/arm/exynos4210.c | 378 --- qemu/hw/arm/exynos4_boards.c | 186 -- qemu/hw/arm/fsl-imx25.c | 313 -- qemu/hw/arm/fsl-imx31.c | 287 -- qemu/hw/arm/gumstix.c | 159 - qemu/hw/arm/highbank.c | 441 --- qemu/hw/arm/imx25_pdk.c | 153 - qemu/hw/arm/integratorcp.c | 674 ---- qemu/hw/arm/kzm.c | 147 - qemu/hw/arm/mainstone.c | 199 -- qemu/hw/arm/musicpal.c | 1751 ----------- qemu/hw/arm/netduino2.c | 49 - qemu/hw/arm/nseries.c | 1454 --------- qemu/hw/arm/omap1.c | 4086 ------------------------- qemu/hw/arm/omap2.c | 2691 ---------------- qemu/hw/arm/omap_sx1.c | 256 -- qemu/hw/arm/palm.c | 280 -- qemu/hw/arm/palmetto-bmc.c | 68 - qemu/hw/arm/pxa2xx.c | 2358 -------------- qemu/hw/arm/pxa2xx_gpio.c | 357 --- qemu/hw/arm/pxa2xx_pic.c | 340 -- qemu/hw/arm/raspi.c | 172 -- qemu/hw/arm/realview.c | 463 --- qemu/hw/arm/spitz.c | 1178 ------- qemu/hw/arm/stellaris.c | 1475 --------- qemu/hw/arm/stm32f205_soc.c | 165 - qemu/hw/arm/strongarm.c | 1662 ---------- qemu/hw/arm/strongarm.h | 68 - qemu/hw/arm/sysbus-fdt.c | 542 ---- qemu/hw/arm/tosa.c | 303 -- qemu/hw/arm/versatilepb.c | 448 --- qemu/hw/arm/vexpress.c | 806 ----- qemu/hw/arm/virt-acpi-build.c | 755 ----- qemu/hw/arm/virt.c | 1435 --------- qemu/hw/arm/xilinx_zynq.c | 318 -- qemu/hw/arm/xlnx-ep108.c | 115 - qemu/hw/arm/xlnx-zynqmp.c | 403 --- qemu/hw/arm/z2.c | 382 --- qemu/hw/audio/Makefile.objs | 18 - qemu/hw/audio/ac97.c | 1431 --------- qemu/hw/audio/adlib.c | 386 --- qemu/hw/audio/cs4231.c | 187 -- qemu/hw/audio/cs4231a.c | 715 ----- qemu/hw/audio/es1370.c | 1080 ------- qemu/hw/audio/fmopl.c | 1391 --------- qemu/hw/audio/fmopl.h | 174 -- qemu/hw/audio/gus.c | 321 -- qemu/hw/audio/gusemu.h | 104 - qemu/hw/audio/gusemu_hal.c | 555 ---- qemu/hw/audio/gusemu_mixer.c | 241 -- qemu/hw/audio/gustate.h | 132 - qemu/hw/audio/hda-codec-common.h | 456 --- qemu/hw/audio/hda-codec.c | 732 ----- qemu/hw/audio/intel-hda-defs.h | 717 ----- qemu/hw/audio/intel-hda.c | 1344 -------- qemu/hw/audio/intel-hda.h | 72 - qemu/hw/audio/lm4549.c | 335 -- qemu/hw/audio/lm4549.h | 43 - qemu/hw/audio/marvell_88w8618.c | 307 -- qemu/hw/audio/milkymist-ac97.c | 349 --- qemu/hw/audio/pcspk.c | 214 -- qemu/hw/audio/pl041.c | 650 ---- qemu/hw/audio/pl041.h | 135 - qemu/hw/audio/pl041.hx | 81 - qemu/hw/audio/sb16.c | 1436 --------- qemu/hw/audio/wm8750.c | 723 ----- qemu/hw/block/Makefile.objs | 15 - qemu/hw/block/block.c | 93 - qemu/hw/block/cdrom.c | 156 - qemu/hw/block/dataplane/Makefile.objs | 1 - qemu/hw/block/dataplane/virtio-blk.c | 290 -- qemu/hw/block/dataplane/virtio-blk.h | 31 - qemu/hw/block/ecc.c | 91 - qemu/hw/block/fdc.c | 2738 ----------------- qemu/hw/block/hd-geometry.c | 166 - qemu/hw/block/m25p80.c | 1005 ------ qemu/hw/block/nand.c | 801 ----- qemu/hw/block/nvme.c | 942 ------ qemu/hw/block/nvme.h | 713 ----- qemu/hw/block/onenand.c | 850 ----- qemu/hw/block/pflash_cfi01.c | 970 ------ qemu/hw/block/pflash_cfi02.c | 797 ----- qemu/hw/block/tc58128.c | 181 -- qemu/hw/block/virtio-blk.c | 980 ------ qemu/hw/block/xen_blkif.h | 119 - qemu/hw/block/xen_disk.c | 1121 ------- qemu/hw/bt/Makefile.objs | 3 - qemu/hw/bt/core.c | 145 - qemu/hw/bt/hci-csr.c | 455 --- qemu/hw/bt/hci.c | 2272 -------------- qemu/hw/bt/hid.c | 554 ---- qemu/hw/bt/l2cap.c | 1366 --------- qemu/hw/bt/sdp.c | 979 ------ qemu/hw/char/Makefile.objs | 30 - qemu/hw/char/bcm2835_aux.c | 316 -- qemu/hw/char/cadence_uart.c | 540 ---- qemu/hw/char/debugcon.c | 142 - qemu/hw/char/digic-uart.c | 198 -- qemu/hw/char/escc.c | 1056 ------- qemu/hw/char/etraxfs_ser.c | 254 -- qemu/hw/char/exynos4210_uart.c | 678 ---- qemu/hw/char/grlib_apbuart.c | 299 -- qemu/hw/char/imx_serial.c | 367 --- qemu/hw/char/ipoctal232.c | 604 ---- qemu/hw/char/lm32_juart.c | 165 - qemu/hw/char/lm32_uart.c | 305 -- qemu/hw/char/mcf_uart.c | 308 -- qemu/hw/char/milkymist-uart.c | 256 -- qemu/hw/char/omap_uart.c | 188 -- qemu/hw/char/parallel.c | 645 ---- qemu/hw/char/pl011.c | 343 --- qemu/hw/char/sclpconsole-lm.c | 384 --- qemu/hw/char/sclpconsole.c | 286 -- qemu/hw/char/serial-isa.c | 147 - qemu/hw/char/serial-pci.c | 276 -- qemu/hw/char/serial.c | 966 ------ qemu/hw/char/sh_serial.c | 409 --- qemu/hw/char/spapr_vty.c | 249 -- qemu/hw/char/stm32f2xx_usart.c | 233 -- qemu/hw/char/virtio-console.c | 218 -- qemu/hw/char/virtio-serial-bus.c | 1149 ------- qemu/hw/char/xen_console.c | 297 -- qemu/hw/char/xilinx_uartlite.c | 249 -- qemu/hw/core/Makefile.objs | 17 - qemu/hw/core/empty_slot.c | 103 - qemu/hw/core/fw-path-provider.c | 53 - qemu/hw/core/hotplug.c | 60 - qemu/hw/core/irq.c | 159 - qemu/hw/core/loader.c | 1190 ------- qemu/hw/core/machine.c | 570 ---- qemu/hw/core/nmi.c | 106 - qemu/hw/core/null-machine.c | 30 - qemu/hw/core/platform-bus.c | 252 -- qemu/hw/core/ptimer.c | 232 -- qemu/hw/core/qdev-properties-system.c | 415 --- qemu/hw/core/qdev-properties.c | 1131 ------- qemu/hw/core/qdev.c | 1370 --------- qemu/hw/core/stream.c | 33 - qemu/hw/core/sysbus.c | 370 --- qemu/hw/core/uboot_image.h | 158 - qemu/hw/cpu/Makefile.objs | 5 - qemu/hw/cpu/a15mpcore.c | 158 - qemu/hw/cpu/a9mpcore.c | 192 -- qemu/hw/cpu/arm11mpcore.c | 174 -- qemu/hw/cpu/realview_mpcore.c | 141 - qemu/hw/cris/Makefile.objs | 2 - qemu/hw/cris/axis_dev88.c | 365 --- qemu/hw/cris/boot.c | 102 - qemu/hw/cris/boot.h | 15 - qemu/hw/display/Makefile.objs | 45 - qemu/hw/display/ads7846.c | 178 -- qemu/hw/display/bcm2835_fb.c | 425 --- qemu/hw/display/blizzard.c | 987 ------ qemu/hw/display/blizzard_template.h | 146 - qemu/hw/display/cg3.c | 399 --- qemu/hw/display/cirrus_vga.c | 3091 ------------------- qemu/hw/display/cirrus_vga_rop.h | 207 -- qemu/hw/display/cirrus_vga_rop2.h | 281 -- qemu/hw/display/exynos4210_fimd.c | 1952 ------------ qemu/hw/display/framebuffer.c | 125 - qemu/hw/display/framebuffer.h | 65 - qemu/hw/display/g364fb.c | 559 ---- qemu/hw/display/jazz_led.c | 314 -- qemu/hw/display/milkymist-tmu2.c | 495 --- qemu/hw/display/milkymist-vgafb.c | 354 --- qemu/hw/display/milkymist-vgafb_template.h | 74 - qemu/hw/display/omap_dss.c | 1091 ------- qemu/hw/display/omap_lcd_template.h | 175 -- qemu/hw/display/omap_lcdc.c | 420 --- qemu/hw/display/pl110.c | 539 ---- qemu/hw/display/pl110_template.h | 399 --- qemu/hw/display/pxa2xx_lcd.c | 1065 ------- qemu/hw/display/pxa2xx_template.h | 447 --- qemu/hw/display/qxl-logger.c | 276 -- qemu/hw/display/qxl-render.c | 298 -- qemu/hw/display/qxl.c | 2359 -------------- qemu/hw/display/qxl.h | 174 -- qemu/hw/display/sm501.c | 1458 --------- qemu/hw/display/sm501_template.h | 145 - qemu/hw/display/ssd0303.c | 331 -- qemu/hw/display/ssd0323.c | 402 --- qemu/hw/display/tc6393xb.c | 598 ---- qemu/hw/display/tc6393xb_template.h | 72 - qemu/hw/display/tcx.c | 1106 ------- qemu/hw/display/vga-helpers.h | 439 --- qemu/hw/display/vga-isa-mm.c | 143 - qemu/hw/display/vga-isa.c | 106 - qemu/hw/display/vga-pci.c | 387 --- qemu/hw/display/vga.c | 2289 -------------- qemu/hw/display/vga.h | 159 - qemu/hw/display/vga_int.h | 227 -- qemu/hw/display/virtio-gpu-3d.c | 606 ---- qemu/hw/display/virtio-gpu-pci.c | 77 - qemu/hw/display/virtio-gpu.c | 1087 ------- qemu/hw/display/virtio-vga.c | 193 -- qemu/hw/display/vmware_vga.c | 1370 --------- qemu/hw/display/xenfb.c | 1004 ------ qemu/hw/dma/Makefile.objs | 14 - qemu/hw/dma/bcm2835_dma.c | 409 --- qemu/hw/dma/etraxfs_dma.c | 783 ----- qemu/hw/dma/i82374.c | 156 - qemu/hw/dma/i8257.c | 643 ---- qemu/hw/dma/omap_dma.c | 2103 ------------- qemu/hw/dma/pl080.c | 423 --- qemu/hw/dma/pl330.c | 1668 ---------- qemu/hw/dma/puv3_dma.c | 114 - qemu/hw/dma/pxa2xx_dma.c | 576 ---- qemu/hw/dma/rc4030.c | 842 ----- qemu/hw/dma/soc_dma.c | 362 --- qemu/hw/dma/sparc32_dma.c | 323 -- qemu/hw/dma/sun4m_iommu.c | 393 --- qemu/hw/dma/xilinx_axidma.c | 666 ---- qemu/hw/gpio/Makefile.objs | 9 - qemu/hw/gpio/gpio_key.c | 104 - qemu/hw/gpio/imx_gpio.c | 350 --- qemu/hw/gpio/max7310.c | 218 -- qemu/hw/gpio/mpc8xxx.c | 218 -- qemu/hw/gpio/omap_gpio.c | 822 ----- qemu/hw/gpio/pl061.c | 403 --- qemu/hw/gpio/puv3_gpio.c | 146 - qemu/hw/gpio/zaurus.c | 302 -- qemu/hw/i2c/Makefile.objs | 8 - qemu/hw/i2c/bitbang_i2c.c | 253 -- qemu/hw/i2c/bitbang_i2c.h | 14 - qemu/hw/i2c/core.c | 246 -- qemu/hw/i2c/exynos4210_i2c.c | 337 -- qemu/hw/i2c/imx_i2c.c | 337 -- qemu/hw/i2c/omap_i2c.c | 509 --- qemu/hw/i2c/pm_smbus.c | 228 -- qemu/hw/i2c/smbus.c | 362 --- qemu/hw/i2c/smbus_eeprom.c | 159 - qemu/hw/i2c/smbus_ich9.c | 130 - qemu/hw/i2c/versatile_i2c.c | 114 - qemu/hw/i386/Makefile.objs | 10 - qemu/hw/i386/acpi-build.c | 2950 ------------------ qemu/hw/i386/acpi-build.h | 7 - qemu/hw/i386/intel_iommu.c | 2057 ------------- qemu/hw/i386/intel_iommu_internal.h | 391 --- qemu/hw/i386/kvm/Makefile.objs | 1 - qemu/hw/i386/kvm/apic.c | 219 -- qemu/hw/i386/kvm/clock.c | 196 -- qemu/hw/i386/kvm/i8254.c | 337 -- qemu/hw/i386/kvm/i8259.c | 163 - qemu/hw/i386/kvm/ioapic.c | 179 -- qemu/hw/i386/kvm/pci-assign.c | 1898 ------------ qemu/hw/i386/kvmvapic.c | 866 ------ qemu/hw/i386/multiboot.c | 375 --- qemu/hw/i386/multiboot.h | 14 - qemu/hw/i386/pc.c | 2017 ------------ qemu/hw/i386/pc_piix.c | 1062 ------- qemu/hw/i386/pc_q35.c | 318 -- qemu/hw/i386/pc_sysfw.c | 253 -- qemu/hw/i386/pci-assign-load-rom.c | 85 - qemu/hw/i386/xen/Makefile.objs | 1 - qemu/hw/i386/xen/xen_apic.c | 95 - qemu/hw/i386/xen/xen_platform.c | 455 --- qemu/hw/i386/xen/xen_pvdevice.c | 137 - qemu/hw/ide/Makefile.objs | 12 - qemu/hw/ide/ahci.c | 1832 ----------- qemu/hw/ide/ahci.h | 405 --- qemu/hw/ide/atapi.c | 1367 --------- qemu/hw/ide/cmd646.c | 435 --- qemu/hw/ide/core.c | 2842 ----------------- qemu/hw/ide/ich.c | 191 -- qemu/hw/ide/internal.h | 635 ---- qemu/hw/ide/isa.c | 135 - qemu/hw/ide/macio.c | 642 ---- qemu/hw/ide/microdrive.c | 638 ---- qemu/hw/ide/mmio.c | 184 -- qemu/hw/ide/pci.c | 468 --- qemu/hw/ide/pci.h | 76 - qemu/hw/ide/piix.c | 295 -- qemu/hw/ide/qdev.c | 370 --- qemu/hw/ide/via.c | 236 -- qemu/hw/input/Makefile.objs | 19 - qemu/hw/input/adb.c | 598 ---- qemu/hw/input/hid.c | 618 ---- qemu/hw/input/lm832x.c | 525 ---- qemu/hw/input/milkymist-softusb.c | 319 -- qemu/hw/input/pckbd.c | 595 ---- qemu/hw/input/pl050.c | 206 -- qemu/hw/input/ps2.c | 813 ----- qemu/hw/input/pxa2xx_keypad.c | 335 -- qemu/hw/input/stellaris_input.c | 88 - qemu/hw/input/tsc2005.c | 595 ---- qemu/hw/input/tsc210x.c | 1273 -------- qemu/hw/input/virtio-input-hid.c | 525 ---- qemu/hw/input/virtio-input-host.c | 257 -- qemu/hw/input/virtio-input.c | 343 --- qemu/hw/input/vmmouse.c | 304 -- qemu/hw/intc/Makefile.objs | 34 - qemu/hw/intc/allwinner-a10-pic.c | 213 -- qemu/hw/intc/apic.c | 906 ------ qemu/hw/intc/apic_common.c | 451 --- qemu/hw/intc/arm_gic.c | 1385 --------- qemu/hw/intc/arm_gic_common.c | 290 -- qemu/hw/intc/arm_gic_kvm.c | 607 ---- qemu/hw/intc/arm_gicv2m.c | 194 -- qemu/hw/intc/arm_gicv3_common.c | 142 - qemu/hw/intc/arm_gicv3_kvm.c | 151 - qemu/hw/intc/armv7m_nvic.c | 581 ---- qemu/hw/intc/aspeed_vic.c | 339 -- qemu/hw/intc/bcm2835_ic.c | 237 -- qemu/hw/intc/bcm2836_control.c | 304 -- qemu/hw/intc/etraxfs_pic.c | 194 -- qemu/hw/intc/exynos4210_combiner.c | 459 --- qemu/hw/intc/exynos4210_gic.c | 472 --- qemu/hw/intc/gic_internal.h | 103 - qemu/hw/intc/grlib_irqmp.c | 375 --- qemu/hw/intc/heathrow_pic.c | 214 -- qemu/hw/intc/i8259.c | 524 ---- qemu/hw/intc/i8259_common.c | 163 - qemu/hw/intc/imx_avic.c | 363 --- qemu/hw/intc/ioapic.c | 338 -- qemu/hw/intc/ioapic_common.c | 180 -- qemu/hw/intc/lm32_pic.c | 203 -- qemu/hw/intc/omap_intc.c | 672 ---- qemu/hw/intc/openpic.c | 1664 ---------- qemu/hw/intc/openpic_kvm.c | 296 -- qemu/hw/intc/pl190.c | 293 -- qemu/hw/intc/puv3_intc.c | 141 - qemu/hw/intc/realview_gic.c | 89 - qemu/hw/intc/s390_flic.c | 100 - qemu/hw/intc/s390_flic_kvm.c | 435 --- qemu/hw/intc/sh_intc.c | 515 ---- qemu/hw/intc/slavio_intctl.c | 472 --- qemu/hw/intc/vgic_common.h | 35 - qemu/hw/intc/xics.c | 1093 ------- qemu/hw/intc/xics_kvm.c | 512 ---- qemu/hw/intc/xilinx_intc.c | 202 -- qemu/hw/ipack/Makefile.objs | 2 - qemu/hw/ipack/ipack.c | 121 - qemu/hw/ipack/tpci200.c | 656 ---- qemu/hw/ipmi/Makefile.objs | 5 - qemu/hw/ipmi/ipmi.c | 151 - qemu/hw/ipmi/ipmi_bmc_extern.c | 519 ---- qemu/hw/ipmi/ipmi_bmc_sim.c | 1810 ----------- qemu/hw/ipmi/isa_ipmi_bt.c | 530 ---- qemu/hw/ipmi/isa_ipmi_kcs.c | 495 --- qemu/hw/isa/Makefile.objs | 8 - qemu/hw/isa/apm.c | 103 - qemu/hw/isa/i82378.c | 148 - qemu/hw/isa/isa-bus.c | 310 -- qemu/hw/isa/lpc_ich9.c | 752 ----- qemu/hw/isa/pc87312.c | 404 --- qemu/hw/isa/piix4.c | 142 - qemu/hw/isa/vt82c686.c | 515 ---- qemu/hw/lm32/Makefile.objs | 3 - qemu/hw/lm32/lm32.h | 29 - qemu/hw/lm32/lm32_boards.c | 334 -- qemu/hw/lm32/lm32_hwsetup.h | 180 -- qemu/hw/lm32/milkymist-hw.h | 208 -- qemu/hw/lm32/milkymist.c | 223 -- qemu/hw/m68k/Makefile.objs | 4 - qemu/hw/m68k/an5206.c | 104 - qemu/hw/m68k/dummy_m68k.c | 84 - qemu/hw/m68k/mcf5206.c | 551 ---- qemu/hw/m68k/mcf5208.c | 308 -- qemu/hw/m68k/mcf_intc.c | 171 -- qemu/hw/mem/Makefile.objs | 2 - qemu/hw/mem/nvdimm.c | 47 - qemu/hw/mem/pc-dimm.c | 447 --- qemu/hw/microblaze/Makefile.objs | 3 - qemu/hw/microblaze/boot.c | 215 -- qemu/hw/microblaze/boot.h | 12 - qemu/hw/microblaze/petalogix_ml605_mmu.c | 221 -- qemu/hw/microblaze/petalogix_s3adsp1800_mmu.c | 139 - qemu/hw/mips/Makefile.objs | 6 - qemu/hw/mips/addr.c | 40 - qemu/hw/mips/cps.c | 180 -- qemu/hw/mips/cputimer.c | 163 - qemu/hw/mips/gt64xxx_pci.c | 1259 -------- qemu/hw/mips/mips_fulong2e.c | 392 --- qemu/hw/mips/mips_int.c | 79 - qemu/hw/mips/mips_jazz.c | 391 --- qemu/hw/mips/mips_malta.c | 1270 -------- qemu/hw/mips/mips_mipssim.c | 244 -- qemu/hw/mips/mips_r4k.c | 311 -- qemu/hw/misc/Makefile.objs | 52 - qemu/hw/misc/a9scu.c | 153 - qemu/hw/misc/applesmc.c | 280 -- qemu/hw/misc/arm11scu.c | 101 - qemu/hw/misc/arm_integrator_debug.c | 100 - qemu/hw/misc/arm_l2x0.c | 199 -- qemu/hw/misc/arm_sysctl.c | 658 ---- qemu/hw/misc/bcm2835_mbox.c | 335 -- qemu/hw/misc/bcm2835_property.c | 424 --- qemu/hw/misc/cbus.c | 619 ---- qemu/hw/misc/debugexit.c | 77 - qemu/hw/misc/eccmemctl.c | 345 --- qemu/hw/misc/edu.c | 407 --- qemu/hw/misc/exynos4210_pmu.c | 503 --- qemu/hw/misc/hyperv_testdev.c | 168 - qemu/hw/misc/imx25_ccm.c | 317 -- qemu/hw/misc/imx31_ccm.c | 344 --- qemu/hw/misc/imx6_ccm.c | 774 ----- qemu/hw/misc/imx_ccm.c | 85 - qemu/hw/misc/ivshmem.c | 1323 -------- qemu/hw/misc/macio/Makefile.objs | 3 - qemu/hw/misc/macio/cuda.c | 964 ------ qemu/hw/misc/macio/mac_dbdma.c | 820 ----- qemu/hw/misc/macio/macio.c | 449 --- qemu/hw/misc/max111x.c | 215 -- qemu/hw/misc/milkymist-hpdmc.c | 175 -- qemu/hw/misc/milkymist-pfpu.c | 549 ---- qemu/hw/misc/mips_cmgcr.c | 160 - qemu/hw/misc/mips_cpc.c | 177 -- qemu/hw/misc/mips_itu.c | 521 ---- qemu/hw/misc/mst_fpga.c | 267 -- qemu/hw/misc/omap_clk.c | 1265 -------- qemu/hw/misc/omap_gpmc.c | 897 ------ qemu/hw/misc/omap_l4.c | 164 - qemu/hw/misc/omap_sdrc.c | 169 - qemu/hw/misc/omap_tap.c | 118 - qemu/hw/misc/pc-testdev.c | 207 -- qemu/hw/misc/pci-testdev.c | 335 -- qemu/hw/misc/puv3_pm.c | 154 - qemu/hw/misc/pvpanic.c | 144 - qemu/hw/misc/sga.c | 68 - qemu/hw/misc/slavio_misc.c | 518 ---- qemu/hw/misc/stm32f2xx_syscfg.c | 161 - qemu/hw/misc/tmp105.c | 274 -- qemu/hw/misc/tmp105.h | 47 - qemu/hw/misc/vmport.c | 182 -- qemu/hw/misc/zynq-xadc.c | 303 -- qemu/hw/misc/zynq_slcr.c | 459 --- qemu/hw/moxie/Makefile.objs | 2 - qemu/hw/moxie/moxiesim.c | 161 - qemu/hw/net/Makefile.objs | 43 - qemu/hw/net/allwinner_emac.c | 534 ---- qemu/hw/net/cadence_gem.c | 1267 -------- qemu/hw/net/dp8393x.c | 912 ------ qemu/hw/net/e1000.c | 1959 ------------ qemu/hw/net/e1000_regs.h | 908 ------ qemu/hw/net/eepro100.c | 2114 ------------- qemu/hw/net/etraxfs_eth.c | 648 ---- qemu/hw/net/fsl_etsec/etsec.c | 457 --- qemu/hw/net/fsl_etsec/etsec.h | 176 -- qemu/hw/net/fsl_etsec/miim.c | 147 - qemu/hw/net/fsl_etsec/registers.c | 296 -- qemu/hw/net/fsl_etsec/registers.h | 319 -- qemu/hw/net/fsl_etsec/rings.c | 654 ---- qemu/hw/net/imx_fec.c | 711 ----- qemu/hw/net/lan9118.c | 1399 --------- qemu/hw/net/lance.c | 184 -- qemu/hw/net/mcf_fec.c | 535 ---- qemu/hw/net/milkymist-minimac2.c | 544 ---- qemu/hw/net/mipsnet.c | 287 -- qemu/hw/net/ne2000-isa.c | 155 - qemu/hw/net/ne2000.c | 796 ----- qemu/hw/net/ne2000.h | 39 - qemu/hw/net/opencores_eth.c | 765 ----- qemu/hw/net/pcnet-pci.c | 375 --- qemu/hw/net/pcnet.c | 1761 ----------- qemu/hw/net/pcnet.h | 67 - qemu/hw/net/rocker/qmp-norocker.c | 51 - qemu/hw/net/rocker/rocker.c | 1584 ---------- qemu/hw/net/rocker/rocker.h | 84 - qemu/hw/net/rocker/rocker_desc.c | 374 --- qemu/hw/net/rocker/rocker_desc.h | 53 - qemu/hw/net/rocker/rocker_fp.c | 269 -- qemu/hw/net/rocker/rocker_fp.h | 54 - qemu/hw/net/rocker/rocker_hw.h | 493 --- qemu/hw/net/rocker/rocker_of_dpa.c | 2627 ---------------- qemu/hw/net/rocker/rocker_of_dpa.h | 22 - qemu/hw/net/rocker/rocker_tlv.h | 244 -- qemu/hw/net/rocker/rocker_world.c | 102 - qemu/hw/net/rocker/rocker_world.h | 61 - qemu/hw/net/rtl8139.c | 3509 --------------------- qemu/hw/net/smc91c111.c | 825 ----- qemu/hw/net/spapr_llan.c | 820 ----- qemu/hw/net/stellaris_enet.c | 514 ---- qemu/hw/net/vhost_net.c | 473 --- qemu/hw/net/virtio-net.c | 1950 ------------ qemu/hw/net/vmware_utils.h | 140 - qemu/hw/net/vmxnet3.c | 2723 ---------------- qemu/hw/net/vmxnet3.h | 759 ----- qemu/hw/net/vmxnet_debug.h | 148 - qemu/hw/net/vmxnet_rx_pkt.c | 187 -- qemu/hw/net/vmxnet_rx_pkt.h | 174 -- qemu/hw/net/vmxnet_tx_pkt.c | 581 ---- qemu/hw/net/vmxnet_tx_pkt.h | 146 - qemu/hw/net/xen_nic.c | 412 --- qemu/hw/net/xgmac.c | 433 --- qemu/hw/net/xilinx_axienet.c | 1084 ------- qemu/hw/net/xilinx_ethlite.c | 276 -- qemu/hw/nvram/Makefile.objs | 5 - qemu/hw/nvram/ds1225y.c | 170 - qemu/hw/nvram/eeprom93xx.c | 337 -- qemu/hw/nvram/fw_cfg.c | 1099 ------- qemu/hw/nvram/mac_nvram.c | 218 -- qemu/hw/nvram/spapr_nvram.c | 252 -- qemu/hw/openrisc/Makefile.objs | 2 - qemu/hw/openrisc/cputimer.c | 112 - qemu/hw/openrisc/openrisc_sim.c | 148 - qemu/hw/openrisc/pic_cpu.c | 61 - qemu/hw/pci-bridge/Makefile.objs | 7 - qemu/hw/pci-bridge/dec.c | 162 - qemu/hw/pci-bridge/dec.h | 10 - qemu/hw/pci-bridge/i82801b11.c | 116 - qemu/hw/pci-bridge/ioh3420.c | 218 -- qemu/hw/pci-bridge/ioh3420.h | 6 - qemu/hw/pci-bridge/pci_bridge_dev.c | 257 -- qemu/hw/pci-bridge/pci_expander_bridge.c | 363 --- qemu/hw/pci-bridge/xio3130_downstream.c | 206 -- qemu/hw/pci-bridge/xio3130_downstream.h | 11 - qemu/hw/pci-bridge/xio3130_upstream.c | 179 -- qemu/hw/pci-bridge/xio3130_upstream.h | 10 - qemu/hw/pci-host/Makefile.objs | 18 - qemu/hw/pci-host/apb.c | 871 ------ qemu/hw/pci-host/bonito.c | 858 ------ qemu/hw/pci-host/gpex.c | 155 - qemu/hw/pci-host/grackle.c | 169 - qemu/hw/pci-host/pam.c | 70 - qemu/hw/pci-host/piix.c | 888 ------ qemu/hw/pci-host/ppce500.c | 551 ---- qemu/hw/pci-host/prep.c | 406 --- qemu/hw/pci-host/q35.c | 560 ---- qemu/hw/pci-host/uninorth.c | 533 ---- qemu/hw/pci-host/versatile.c | 549 ---- qemu/hw/pci/Makefile.objs | 9 - qemu/hw/pci/msi.c | 421 --- qemu/hw/pci/msix.c | 621 ---- qemu/hw/pci/pci-stub.c | 37 - qemu/hw/pci/pci.c | 2517 --------------- qemu/hw/pci/pci_bridge.c | 419 --- qemu/hw/pci/pci_host.c | 207 -- qemu/hw/pci/pcie.c | 649 ---- qemu/hw/pci/pcie_aer.c | 1039 ------- qemu/hw/pci/pcie_host.c | 147 - qemu/hw/pci/pcie_port.c | 179 -- qemu/hw/pci/shpc.c | 721 ----- qemu/hw/pci/slotid_cap.c | 46 - qemu/hw/pcmcia/Makefile.objs | 2 - qemu/hw/pcmcia/pcmcia.c | 25 - qemu/hw/pcmcia/pxa2xx.c | 265 -- qemu/hw/ppc/Makefile.objs | 23 - qemu/hw/ppc/e500-ccsr.h | 17 - qemu/hw/ppc/e500.c | 1082 ------- qemu/hw/ppc/e500.h | 29 - qemu/hw/ppc/e500plat.c | 68 - qemu/hw/ppc/mac.h | 187 -- qemu/hw/ppc/mac_newworld.c | 535 ---- qemu/hw/ppc/mac_oldworld.c | 379 --- qemu/hw/ppc/mpc8544_guts.c | 143 - qemu/hw/ppc/mpc8544ds.c | 60 - qemu/hw/ppc/ppc.c | 1345 -------- qemu/hw/ppc/ppc405.h | 81 - qemu/hw/ppc/ppc405_boards.c | 664 ---- qemu/hw/ppc/ppc405_uc.c | 2555 ---------------- qemu/hw/ppc/ppc440_bamboo.c | 300 -- qemu/hw/ppc/ppc4xx_devs.c | 735 ----- qemu/hw/ppc/ppc4xx_pci.c | 393 --- qemu/hw/ppc/ppc_booke.c | 368 --- qemu/hw/ppc/ppce500_spin.c | 225 -- qemu/hw/ppc/prep.c | 679 ---- qemu/hw/ppc/spapr.c | 2485 --------------- qemu/hw/ppc/spapr_drc.c | 843 ----- qemu/hw/ppc/spapr_events.c | 610 ---- qemu/hw/ppc/spapr_hcall.c | 1117 ------- qemu/hw/ppc/spapr_iommu.c | 512 ---- qemu/hw/ppc/spapr_pci.c | 1919 ------------ qemu/hw/ppc/spapr_pci_vfio.c | 239 -- qemu/hw/ppc/spapr_rng.c | 191 -- qemu/hw/ppc/spapr_rtas.c | 785 ----- qemu/hw/ppc/spapr_rtc.c | 212 -- qemu/hw/ppc/spapr_vio.c | 706 ----- qemu/hw/ppc/virtex_ml507.c | 308 -- qemu/hw/s390x/Makefile.objs | 13 - qemu/hw/s390x/css.c | 1646 ---------- qemu/hw/s390x/css.h | 126 - qemu/hw/s390x/event-facility.c | 449 --- qemu/hw/s390x/ipl.c | 298 -- qemu/hw/s390x/ipl.h | 53 - qemu/hw/s390x/s390-pci-bus.c | 621 ---- qemu/hw/s390x/s390-pci-bus.h | 256 -- qemu/hw/s390x/s390-pci-inst.c | 846 ----- qemu/hw/s390x/s390-pci-inst.h | 289 -- qemu/hw/s390x/s390-skeys-kvm.c | 76 - qemu/hw/s390x/s390-skeys.c | 415 --- qemu/hw/s390x/s390-virtio-ccw.c | 381 --- qemu/hw/s390x/s390-virtio-hcall.c | 41 - qemu/hw/s390x/s390-virtio.c | 210 -- qemu/hw/s390x/s390-virtio.h | 32 - qemu/hw/s390x/sclp.c | 618 ---- qemu/hw/s390x/sclpcpu.c | 101 - qemu/hw/s390x/sclpquiesce.c | 143 - qemu/hw/s390x/virtio-ccw.c | 1928 ------------ qemu/hw/s390x/virtio-ccw.h | 211 -- qemu/hw/scsi/Makefile.objs | 14 - qemu/hw/scsi/esp-pci.c | 531 ---- qemu/hw/scsi/esp.c | 745 ----- qemu/hw/scsi/lsi53c895a.c | 2163 ------------- qemu/hw/scsi/megasas.c | 2548 --------------- qemu/hw/scsi/mfi.h | 1272 -------- qemu/hw/scsi/mpi.h | 1153 ------- qemu/hw/scsi/mptconfig.c | 905 ------ qemu/hw/scsi/mptendian.c | 204 -- qemu/hw/scsi/mptsas.c | 1442 --------- qemu/hw/scsi/mptsas.h | 100 - qemu/hw/scsi/scsi-bus.c | 2062 ------------- qemu/hw/scsi/scsi-disk.c | 2828 ----------------- qemu/hw/scsi/scsi-generic.c | 616 ---- qemu/hw/scsi/spapr_vscsi.c | 1304 -------- qemu/hw/scsi/srp.h | 247 -- qemu/hw/scsi/vhost-scsi.c | 352 --- qemu/hw/scsi/viosrp.h | 216 -- qemu/hw/scsi/virtio-scsi-dataplane.c | 208 -- qemu/hw/scsi/virtio-scsi.c | 1054 ------- qemu/hw/scsi/vmw_pvscsi.c | 1305 -------- qemu/hw/scsi/vmw_pvscsi.h | 434 --- qemu/hw/sd/Makefile.objs | 8 - qemu/hw/sd/core.c | 146 - qemu/hw/sd/milkymist-memcard.c | 317 -- qemu/hw/sd/omap_mmc.c | 647 ---- qemu/hw/sd/pl181.c | 528 ---- qemu/hw/sd/pxa2xx_mmci.c | 598 ---- qemu/hw/sd/sd.c | 1979 ------------ qemu/hw/sd/sdhci-internal.h | 232 -- qemu/hw/sd/sdhci.c | 1394 --------- qemu/hw/sd/ssi-sd.c | 290 -- qemu/hw/sh4/Makefile.objs | 4 - qemu/hw/sh4/r2d.c | 367 --- qemu/hw/sh4/sh7750.c | 842 ----- qemu/hw/sh4/sh7750_regnames.c | 98 - qemu/hw/sh4/sh7750_regnames.h | 6 - qemu/hw/sh4/sh7750_regs.h | 1277 -------- qemu/hw/sh4/sh_pci.c | 204 -- qemu/hw/sh4/shix.c | 101 - qemu/hw/smbios/Makefile.objs | 1 - qemu/hw/smbios/smbios.c | 1112 ------- qemu/hw/sparc/Makefile.objs | 1 - qemu/hw/sparc/leon3.c | 229 -- qemu/hw/sparc/sun4m.c | 1572 ---------- qemu/hw/sparc64/Makefile.objs | 1 - qemu/hw/sparc64/sun4u.c | 1010 ------ qemu/hw/ssi/Makefile.objs | 6 - qemu/hw/ssi/omap_spi.c | 374 --- qemu/hw/ssi/pl022.c | 327 -- qemu/hw/ssi/ssi.c | 175 -- qemu/hw/ssi/xilinx_spi.c | 391 --- qemu/hw/ssi/xilinx_spips.c | 734 ----- qemu/hw/timer/Makefile.objs | 35 - qemu/hw/timer/a9gtimer.c | 372 --- qemu/hw/timer/allwinner-a10-pit.c | 296 -- qemu/hw/timer/arm_mptimer.c | 303 -- qemu/hw/timer/arm_timer.c | 409 --- qemu/hw/timer/aspeed_timer.c | 449 --- qemu/hw/timer/cadence_ttc.c | 492 --- qemu/hw/timer/digic-timer.c | 163 - qemu/hw/timer/ds1338.c | 243 -- qemu/hw/timer/etraxfs_timer.c | 358 --- qemu/hw/timer/exynos4210_mct.c | 1480 --------- qemu/hw/timer/exynos4210_pwm.c | 424 --- qemu/hw/timer/exynos4210_rtc.c | 595 ---- qemu/hw/timer/grlib_gptimer.c | 412 --- qemu/hw/timer/hpet.c | 789 ----- qemu/hw/timer/i8254.c | 384 --- qemu/hw/timer/i8254_common.c | 307 -- qemu/hw/timer/imx_epit.c | 344 --- qemu/hw/timer/imx_gpt.c | 467 --- qemu/hw/timer/lm32_timer.c | 236 -- qemu/hw/timer/m48t59.c | 947 ------ qemu/hw/timer/mc146818rtc.c | 971 ------ qemu/hw/timer/milkymist-sysctl.c | 342 --- qemu/hw/timer/omap_gptimer.c | 488 --- qemu/hw/timer/omap_synctimer.c | 104 - qemu/hw/timer/pl031.c | 270 -- qemu/hw/timer/puv3_ost.c | 157 - qemu/hw/timer/pxa2xx_timer.c | 605 ---- qemu/hw/timer/sh_timer.c | 335 -- qemu/hw/timer/slavio_timer.c | 435 --- qemu/hw/timer/stm32f2xx_timer.c | 329 -- qemu/hw/timer/twl92230.c | 889 ------ qemu/hw/timer/xilinx_timer.c | 266 -- qemu/hw/tpm/Makefile.objs | 2 - qemu/hw/tpm/tpm_int.h | 75 - qemu/hw/tpm/tpm_passthrough.c | 566 ---- qemu/hw/tpm/tpm_tis.c | 1101 ------- qemu/hw/tpm/tpm_tis.h | 70 - qemu/hw/tpm/tpm_util.c | 127 - qemu/hw/tpm/tpm_util.h | 28 - qemu/hw/tricore/Makefile.objs | 1 - qemu/hw/tricore/tricore_testboard.c | 129 - qemu/hw/unicore32/Makefile.objs | 4 - qemu/hw/unicore32/puv3.c | 148 - qemu/hw/usb/Makefile.objs | 40 - qemu/hw/usb/bus.c | 763 ----- qemu/hw/usb/ccid-card-emulated.c | 606 ---- qemu/hw/usb/ccid-card-passthru.c | 416 --- qemu/hw/usb/ccid.h | 65 - qemu/hw/usb/combined-packet.c | 188 -- qemu/hw/usb/core.c | 795 ----- qemu/hw/usb/desc-msos.c | 239 -- qemu/hw/usb/desc.c | 804 ----- qemu/hw/usb/desc.h | 244 -- qemu/hw/usb/dev-audio.c | 703 ----- qemu/hw/usb/dev-bluetooth.c | 580 ---- qemu/hw/usb/dev-hid.c | 883 ------ qemu/hw/usb/dev-hub.c | 596 ---- qemu/hw/usb/dev-mtp.c | 1414 --------- qemu/hw/usb/dev-network.c | 1455 --------- qemu/hw/usb/dev-serial.c | 652 ---- qemu/hw/usb/dev-smartcard-reader.c | 1507 --------- qemu/hw/usb/dev-storage.c | 872 ------ qemu/hw/usb/dev-uas.c | 961 ------ qemu/hw/usb/dev-wacom.c | 386 --- qemu/hw/usb/hcd-ehci-pci.c | 272 -- qemu/hw/usb/hcd-ehci-sysbus.c | 230 -- qemu/hw/usb/hcd-ehci.c | 2549 --------------- qemu/hw/usb/hcd-ehci.h | 383 --- qemu/hw/usb/hcd-musb.c | 1553 ---------- qemu/hw/usb/hcd-ohci.c | 2164 ------------- qemu/hw/usb/hcd-uhci.c | 1435 --------- qemu/hw/usb/hcd-xhci.c | 3917 ------------------------ qemu/hw/usb/host-legacy.c | 144 - qemu/hw/usb/host-libusb.c | 1688 ---------- qemu/hw/usb/host-stub.c | 48 - qemu/hw/usb/host.h | 44 - qemu/hw/usb/libhw.c | 71 - qemu/hw/usb/quirks-ftdi-ids.h | 1255 -------- qemu/hw/usb/quirks-pl2303-ids.h | 150 - qemu/hw/usb/quirks.c | 54 - qemu/hw/usb/quirks.h | 910 ------ qemu/hw/usb/redirect.c | 2526 --------------- qemu/hw/usb/tusb6010.c | 817 ----- qemu/hw/vfio/Makefile.objs | 7 - qemu/hw/vfio/amd-xgbe.c | 56 - qemu/hw/vfio/calxeda-xgmac.c | 56 - qemu/hw/vfio/common.c | 1192 -------- qemu/hw/vfio/pci-quirks.c | 1205 -------- qemu/hw/vfio/pci.c | 2734 ----------------- qemu/hw/vfio/pci.h | 162 - qemu/hw/vfio/platform.c | 705 ----- qemu/hw/virtio/Makefile.objs | 7 - qemu/hw/virtio/vhost-backend.c | 212 -- qemu/hw/virtio/vhost-user.c | 657 ---- qemu/hw/virtio/vhost.c | 1301 -------- qemu/hw/virtio/virtio-balloon.c | 535 ---- qemu/hw/virtio/virtio-bus.c | 182 -- qemu/hw/virtio/virtio-mmio.c | 580 ---- qemu/hw/virtio/virtio-pci.c | 2534 --------------- qemu/hw/virtio/virtio-pci.h | 317 -- qemu/hw/virtio/virtio-rng.c | 278 -- qemu/hw/virtio/virtio.c | 1926 ------------ qemu/hw/watchdog/Makefile.objs | 4 - qemu/hw/watchdog/watchdog.c | 149 - qemu/hw/watchdog/wdt_diag288.c | 142 - qemu/hw/watchdog/wdt_i6300esb.c | 465 --- qemu/hw/watchdog/wdt_ib700.c | 157 - qemu/hw/xen/Makefile.objs | 5 - qemu/hw/xen/xen-host-pci-device.c | 404 --- qemu/hw/xen/xen-host-pci-device.h | 58 - qemu/hw/xen/xen_backend.c | 802 ----- qemu/hw/xen/xen_devconfig.c | 176 -- qemu/hw/xen/xen_pt.c | 974 ------ qemu/hw/xen/xen_pt.h | 335 -- qemu/hw/xen/xen_pt_config_init.c | 2090 ------------- qemu/hw/xen/xen_pt_graphics.c | 275 -- qemu/hw/xen/xen_pt_msi.c | 635 ---- qemu/hw/xenpv/Makefile.objs | 4 - qemu/hw/xenpv/xen_domainbuild.c | 302 -- qemu/hw/xenpv/xen_domainbuild.h | 13 - qemu/hw/xenpv/xen_machine_pv.c | 112 - qemu/hw/xtensa/Makefile.objs | 3 - qemu/hw/xtensa/bootparam.h | 49 - qemu/hw/xtensa/pic_cpu.c | 167 - qemu/hw/xtensa/sim.c | 119 - qemu/hw/xtensa/xtfpga.c | 516 ---- 821 files changed, 416358 deletions(-) delete mode 100644 qemu/hw/9pfs/9p-handle.c delete mode 100644 qemu/hw/9pfs/9p-local.c delete mode 100644 qemu/hw/9pfs/9p-posix-acl.c delete mode 100644 qemu/hw/9pfs/9p-proxy.c delete mode 100644 qemu/hw/9pfs/9p-proxy.h delete mode 100644 qemu/hw/9pfs/9p-synth.c delete mode 100644 qemu/hw/9pfs/9p-synth.h delete mode 100644 qemu/hw/9pfs/9p-xattr-user.c delete mode 100644 qemu/hw/9pfs/9p-xattr.c delete mode 100644 qemu/hw/9pfs/9p-xattr.h delete mode 100644 qemu/hw/9pfs/9p.c delete mode 100644 qemu/hw/9pfs/9p.h delete mode 100644 qemu/hw/9pfs/Makefile.objs delete mode 100644 qemu/hw/9pfs/codir.c delete mode 100644 qemu/hw/9pfs/cofile.c delete mode 100644 qemu/hw/9pfs/cofs.c delete mode 100644 qemu/hw/9pfs/coth.c delete mode 100644 qemu/hw/9pfs/coth.h delete mode 100644 qemu/hw/9pfs/coxattr.c delete mode 100644 qemu/hw/9pfs/virtio-9p-device.c delete mode 100644 qemu/hw/9pfs/virtio-9p.h delete mode 100644 qemu/hw/Makefile.objs delete mode 100644 qemu/hw/acpi/Makefile.objs delete mode 100644 qemu/hw/acpi/acpi_interface.c delete mode 100644 qemu/hw/acpi/aml-build.c delete mode 100644 qemu/hw/acpi/bios-linker-loader.c delete mode 100644 qemu/hw/acpi/core.c delete mode 100644 qemu/hw/acpi/cpu_hotplug.c delete mode 100644 qemu/hw/acpi/cpu_hotplug_acpi_table.c delete mode 100644 qemu/hw/acpi/ich9.c delete mode 100644 qemu/hw/acpi/memory_hotplug.c delete mode 100644 qemu/hw/acpi/memory_hotplug_acpi_table.c delete mode 100644 qemu/hw/acpi/nvdimm.c delete mode 100644 qemu/hw/acpi/pcihp.c delete mode 100644 qemu/hw/acpi/piix4.c delete mode 100644 qemu/hw/acpi/tco.c delete mode 100644 qemu/hw/alpha/Makefile.objs delete mode 100644 qemu/hw/alpha/alpha_sys.h delete mode 100644 qemu/hw/alpha/dp264.c delete mode 100644 qemu/hw/alpha/pci.c delete mode 100644 qemu/hw/alpha/typhoon.c delete mode 100644 qemu/hw/arm/Makefile.objs delete mode 100644 qemu/hw/arm/allwinner-a10.c delete mode 100644 qemu/hw/arm/armv7m.c delete mode 100644 qemu/hw/arm/ast2400.c delete mode 100644 qemu/hw/arm/bcm2835_peripherals.c delete mode 100644 qemu/hw/arm/bcm2836.c delete mode 100644 qemu/hw/arm/boot.c delete mode 100644 qemu/hw/arm/collie.c delete mode 100644 qemu/hw/arm/cubieboard.c delete mode 100644 qemu/hw/arm/digic.c delete mode 100644 qemu/hw/arm/digic_boards.c delete mode 100644 qemu/hw/arm/exynos4210.c delete mode 100644 qemu/hw/arm/exynos4_boards.c delete mode 100644 qemu/hw/arm/fsl-imx25.c delete mode 100644 qemu/hw/arm/fsl-imx31.c delete mode 100644 qemu/hw/arm/gumstix.c delete mode 100644 qemu/hw/arm/highbank.c delete mode 100644 qemu/hw/arm/imx25_pdk.c delete mode 100644 qemu/hw/arm/integratorcp.c delete mode 100644 qemu/hw/arm/kzm.c delete mode 100644 qemu/hw/arm/mainstone.c delete mode 100644 qemu/hw/arm/musicpal.c delete mode 100644 qemu/hw/arm/netduino2.c delete mode 100644 qemu/hw/arm/nseries.c delete mode 100644 qemu/hw/arm/omap1.c delete mode 100644 qemu/hw/arm/omap2.c delete mode 100644 qemu/hw/arm/omap_sx1.c delete mode 100644 qemu/hw/arm/palm.c delete mode 100644 qemu/hw/arm/palmetto-bmc.c delete mode 100644 qemu/hw/arm/pxa2xx.c delete mode 100644 qemu/hw/arm/pxa2xx_gpio.c delete mode 100644 qemu/hw/arm/pxa2xx_pic.c delete mode 100644 qemu/hw/arm/raspi.c delete mode 100644 qemu/hw/arm/realview.c delete mode 100644 qemu/hw/arm/spitz.c delete mode 100644 qemu/hw/arm/stellaris.c delete mode 100644 qemu/hw/arm/stm32f205_soc.c delete mode 100644 qemu/hw/arm/strongarm.c delete mode 100644 qemu/hw/arm/strongarm.h delete mode 100644 qemu/hw/arm/sysbus-fdt.c delete mode 100644 qemu/hw/arm/tosa.c delete mode 100644 qemu/hw/arm/versatilepb.c delete mode 100644 qemu/hw/arm/vexpress.c delete mode 100644 qemu/hw/arm/virt-acpi-build.c delete mode 100644 qemu/hw/arm/virt.c delete mode 100644 qemu/hw/arm/xilinx_zynq.c delete mode 100644 qemu/hw/arm/xlnx-ep108.c delete mode 100644 qemu/hw/arm/xlnx-zynqmp.c delete mode 100644 qemu/hw/arm/z2.c delete mode 100644 qemu/hw/audio/Makefile.objs delete mode 100644 qemu/hw/audio/ac97.c delete mode 100644 qemu/hw/audio/adlib.c delete mode 100644 qemu/hw/audio/cs4231.c delete mode 100644 qemu/hw/audio/cs4231a.c delete mode 100644 qemu/hw/audio/es1370.c delete mode 100644 qemu/hw/audio/fmopl.c delete mode 100644 qemu/hw/audio/fmopl.h delete mode 100644 qemu/hw/audio/gus.c delete mode 100644 qemu/hw/audio/gusemu.h delete mode 100644 qemu/hw/audio/gusemu_hal.c delete mode 100644 qemu/hw/audio/gusemu_mixer.c delete mode 100644 qemu/hw/audio/gustate.h delete mode 100644 qemu/hw/audio/hda-codec-common.h delete mode 100644 qemu/hw/audio/hda-codec.c delete mode 100644 qemu/hw/audio/intel-hda-defs.h delete mode 100644 qemu/hw/audio/intel-hda.c delete mode 100644 qemu/hw/audio/intel-hda.h delete mode 100644 qemu/hw/audio/lm4549.c delete mode 100644 qemu/hw/audio/lm4549.h delete mode 100644 qemu/hw/audio/marvell_88w8618.c delete mode 100644 qemu/hw/audio/milkymist-ac97.c delete mode 100644 qemu/hw/audio/pcspk.c delete mode 100644 qemu/hw/audio/pl041.c delete mode 100644 qemu/hw/audio/pl041.h delete mode 100644 qemu/hw/audio/pl041.hx delete mode 100644 qemu/hw/audio/sb16.c delete mode 100644 qemu/hw/audio/wm8750.c delete mode 100644 qemu/hw/block/Makefile.objs delete mode 100644 qemu/hw/block/block.c delete mode 100644 qemu/hw/block/cdrom.c delete mode 100644 qemu/hw/block/dataplane/Makefile.objs delete mode 100644 qemu/hw/block/dataplane/virtio-blk.c delete mode 100644 qemu/hw/block/dataplane/virtio-blk.h delete mode 100644 qemu/hw/block/ecc.c delete mode 100644 qemu/hw/block/fdc.c delete mode 100644 qemu/hw/block/hd-geometry.c delete mode 100644 qemu/hw/block/m25p80.c delete mode 100644 qemu/hw/block/nand.c delete mode 100644 qemu/hw/block/nvme.c delete mode 100644 qemu/hw/block/nvme.h delete mode 100644 qemu/hw/block/onenand.c delete mode 100644 qemu/hw/block/pflash_cfi01.c delete mode 100644 qemu/hw/block/pflash_cfi02.c delete mode 100644 qemu/hw/block/tc58128.c delete mode 100644 qemu/hw/block/virtio-blk.c delete mode 100644 qemu/hw/block/xen_blkif.h delete mode 100644 qemu/hw/block/xen_disk.c delete mode 100644 qemu/hw/bt/Makefile.objs delete mode 100644 qemu/hw/bt/core.c delete mode 100644 qemu/hw/bt/hci-csr.c delete mode 100644 qemu/hw/bt/hci.c delete mode 100644 qemu/hw/bt/hid.c delete mode 100644 qemu/hw/bt/l2cap.c delete mode 100644 qemu/hw/bt/sdp.c delete mode 100644 qemu/hw/char/Makefile.objs delete mode 100644 qemu/hw/char/bcm2835_aux.c delete mode 100644 qemu/hw/char/cadence_uart.c delete mode 100644 qemu/hw/char/debugcon.c delete mode 100644 qemu/hw/char/digic-uart.c delete mode 100644 qemu/hw/char/escc.c delete mode 100644 qemu/hw/char/etraxfs_ser.c delete mode 100644 qemu/hw/char/exynos4210_uart.c delete mode 100644 qemu/hw/char/grlib_apbuart.c delete mode 100644 qemu/hw/char/imx_serial.c delete mode 100644 qemu/hw/char/ipoctal232.c delete mode 100644 qemu/hw/char/lm32_juart.c delete mode 100644 qemu/hw/char/lm32_uart.c delete mode 100644 qemu/hw/char/mcf_uart.c delete mode 100644 qemu/hw/char/milkymist-uart.c delete mode 100644 qemu/hw/char/omap_uart.c delete mode 100644 qemu/hw/char/parallel.c delete mode 100644 qemu/hw/char/pl011.c delete mode 100644 qemu/hw/char/sclpconsole-lm.c delete mode 100644 qemu/hw/char/sclpconsole.c delete mode 100644 qemu/hw/char/serial-isa.c delete mode 100644 qemu/hw/char/serial-pci.c delete mode 100644 qemu/hw/char/serial.c delete mode 100644 qemu/hw/char/sh_serial.c delete mode 100644 qemu/hw/char/spapr_vty.c delete mode 100644 qemu/hw/char/stm32f2xx_usart.c delete mode 100644 qemu/hw/char/virtio-console.c delete mode 100644 qemu/hw/char/virtio-serial-bus.c delete mode 100644 qemu/hw/char/xen_console.c delete mode 100644 qemu/hw/char/xilinx_uartlite.c delete mode 100644 qemu/hw/core/Makefile.objs delete mode 100644 qemu/hw/core/empty_slot.c delete mode 100644 qemu/hw/core/fw-path-provider.c delete mode 100644 qemu/hw/core/hotplug.c delete mode 100644 qemu/hw/core/irq.c delete mode 100644 qemu/hw/core/loader.c delete mode 100644 qemu/hw/core/machine.c delete mode 100644 qemu/hw/core/nmi.c delete mode 100644 qemu/hw/core/null-machine.c delete mode 100644 qemu/hw/core/platform-bus.c delete mode 100644 qemu/hw/core/ptimer.c delete mode 100644 qemu/hw/core/qdev-properties-system.c delete mode 100644 qemu/hw/core/qdev-properties.c delete mode 100644 qemu/hw/core/qdev.c delete mode 100644 qemu/hw/core/stream.c delete mode 100644 qemu/hw/core/sysbus.c delete mode 100644 qemu/hw/core/uboot_image.h delete mode 100644 qemu/hw/cpu/Makefile.objs delete mode 100644 qemu/hw/cpu/a15mpcore.c delete mode 100644 qemu/hw/cpu/a9mpcore.c delete mode 100644 qemu/hw/cpu/arm11mpcore.c delete mode 100644 qemu/hw/cpu/realview_mpcore.c delete mode 100644 qemu/hw/cris/Makefile.objs delete mode 100644 qemu/hw/cris/axis_dev88.c delete mode 100644 qemu/hw/cris/boot.c delete mode 100644 qemu/hw/cris/boot.h delete mode 100644 qemu/hw/display/Makefile.objs delete mode 100644 qemu/hw/display/ads7846.c delete mode 100644 qemu/hw/display/bcm2835_fb.c delete mode 100644 qemu/hw/display/blizzard.c delete mode 100644 qemu/hw/display/blizzard_template.h delete mode 100644 qemu/hw/display/cg3.c delete mode 100644 qemu/hw/display/cirrus_vga.c delete mode 100644 qemu/hw/display/cirrus_vga_rop.h delete mode 100644 qemu/hw/display/cirrus_vga_rop2.h delete mode 100644 qemu/hw/display/exynos4210_fimd.c delete mode 100644 qemu/hw/display/framebuffer.c delete mode 100644 qemu/hw/display/framebuffer.h delete mode 100644 qemu/hw/display/g364fb.c delete mode 100644 qemu/hw/display/jazz_led.c delete mode 100644 qemu/hw/display/milkymist-tmu2.c delete mode 100644 qemu/hw/display/milkymist-vgafb.c delete mode 100644 qemu/hw/display/milkymist-vgafb_template.h delete mode 100644 qemu/hw/display/omap_dss.c delete mode 100644 qemu/hw/display/omap_lcd_template.h delete mode 100644 qemu/hw/display/omap_lcdc.c delete mode 100644 qemu/hw/display/pl110.c delete mode 100644 qemu/hw/display/pl110_template.h delete mode 100644 qemu/hw/display/pxa2xx_lcd.c delete mode 100644 qemu/hw/display/pxa2xx_template.h delete mode 100644 qemu/hw/display/qxl-logger.c delete mode 100644 qemu/hw/display/qxl-render.c delete mode 100644 qemu/hw/display/qxl.c delete mode 100644 qemu/hw/display/qxl.h delete mode 100644 qemu/hw/display/sm501.c delete mode 100644 qemu/hw/display/sm501_template.h delete mode 100644 qemu/hw/display/ssd0303.c delete mode 100644 qemu/hw/display/ssd0323.c delete mode 100644 qemu/hw/display/tc6393xb.c delete mode 100644 qemu/hw/display/tc6393xb_template.h delete mode 100644 qemu/hw/display/tcx.c delete mode 100644 qemu/hw/display/vga-helpers.h delete mode 100644 qemu/hw/display/vga-isa-mm.c delete mode 100644 qemu/hw/display/vga-isa.c delete mode 100644 qemu/hw/display/vga-pci.c delete mode 100644 qemu/hw/display/vga.c delete mode 100644 qemu/hw/display/vga.h delete mode 100644 qemu/hw/display/vga_int.h delete mode 100644 qemu/hw/display/virtio-gpu-3d.c delete mode 100644 qemu/hw/display/virtio-gpu-pci.c delete mode 100644 qemu/hw/display/virtio-gpu.c delete mode 100644 qemu/hw/display/virtio-vga.c delete mode 100644 qemu/hw/display/vmware_vga.c delete mode 100644 qemu/hw/display/xenfb.c delete mode 100644 qemu/hw/dma/Makefile.objs delete mode 100644 qemu/hw/dma/bcm2835_dma.c delete mode 100644 qemu/hw/dma/etraxfs_dma.c delete mode 100644 qemu/hw/dma/i82374.c delete mode 100644 qemu/hw/dma/i8257.c delete mode 100644 qemu/hw/dma/omap_dma.c delete mode 100644 qemu/hw/dma/pl080.c delete mode 100644 qemu/hw/dma/pl330.c delete mode 100644 qemu/hw/dma/puv3_dma.c delete mode 100644 qemu/hw/dma/pxa2xx_dma.c delete mode 100644 qemu/hw/dma/rc4030.c delete mode 100644 qemu/hw/dma/soc_dma.c delete mode 100644 qemu/hw/dma/sparc32_dma.c delete mode 100644 qemu/hw/dma/sun4m_iommu.c delete mode 100644 qemu/hw/dma/xilinx_axidma.c delete mode 100644 qemu/hw/gpio/Makefile.objs delete mode 100644 qemu/hw/gpio/gpio_key.c delete mode 100644 qemu/hw/gpio/imx_gpio.c delete mode 100644 qemu/hw/gpio/max7310.c delete mode 100644 qemu/hw/gpio/mpc8xxx.c delete mode 100644 qemu/hw/gpio/omap_gpio.c delete mode 100644 qemu/hw/gpio/pl061.c delete mode 100644 qemu/hw/gpio/puv3_gpio.c delete mode 100644 qemu/hw/gpio/zaurus.c delete mode 100644 qemu/hw/i2c/Makefile.objs delete mode 100644 qemu/hw/i2c/bitbang_i2c.c delete mode 100644 qemu/hw/i2c/bitbang_i2c.h delete mode 100644 qemu/hw/i2c/core.c delete mode 100644 qemu/hw/i2c/exynos4210_i2c.c delete mode 100644 qemu/hw/i2c/imx_i2c.c delete mode 100644 qemu/hw/i2c/omap_i2c.c delete mode 100644 qemu/hw/i2c/pm_smbus.c delete mode 100644 qemu/hw/i2c/smbus.c delete mode 100644 qemu/hw/i2c/smbus_eeprom.c delete mode 100644 qemu/hw/i2c/smbus_ich9.c delete mode 100644 qemu/hw/i2c/versatile_i2c.c delete mode 100644 qemu/hw/i386/Makefile.objs delete mode 100644 qemu/hw/i386/acpi-build.c delete mode 100644 qemu/hw/i386/acpi-build.h delete mode 100644 qemu/hw/i386/intel_iommu.c delete mode 100644 qemu/hw/i386/intel_iommu_internal.h delete mode 100644 qemu/hw/i386/kvm/Makefile.objs delete mode 100644 qemu/hw/i386/kvm/apic.c delete mode 100644 qemu/hw/i386/kvm/clock.c delete mode 100644 qemu/hw/i386/kvm/i8254.c delete mode 100644 qemu/hw/i386/kvm/i8259.c delete mode 100644 qemu/hw/i386/kvm/ioapic.c delete mode 100644 qemu/hw/i386/kvm/pci-assign.c delete mode 100644 qemu/hw/i386/kvmvapic.c delete mode 100644 qemu/hw/i386/multiboot.c delete mode 100644 qemu/hw/i386/multiboot.h delete mode 100644 qemu/hw/i386/pc.c delete mode 100644 qemu/hw/i386/pc_piix.c delete mode 100644 qemu/hw/i386/pc_q35.c delete mode 100644 qemu/hw/i386/pc_sysfw.c delete mode 100644 qemu/hw/i386/pci-assign-load-rom.c delete mode 100644 qemu/hw/i386/xen/Makefile.objs delete mode 100644 qemu/hw/i386/xen/xen_apic.c delete mode 100644 qemu/hw/i386/xen/xen_platform.c delete mode 100644 qemu/hw/i386/xen/xen_pvdevice.c delete mode 100644 qemu/hw/ide/Makefile.objs delete mode 100644 qemu/hw/ide/ahci.c delete mode 100644 qemu/hw/ide/ahci.h delete mode 100644 qemu/hw/ide/atapi.c delete mode 100644 qemu/hw/ide/cmd646.c delete mode 100644 qemu/hw/ide/core.c delete mode 100644 qemu/hw/ide/ich.c delete mode 100644 qemu/hw/ide/internal.h delete mode 100644 qemu/hw/ide/isa.c delete mode 100644 qemu/hw/ide/macio.c delete mode 100644 qemu/hw/ide/microdrive.c delete mode 100644 qemu/hw/ide/mmio.c delete mode 100644 qemu/hw/ide/pci.c delete mode 100644 qemu/hw/ide/pci.h delete mode 100644 qemu/hw/ide/piix.c delete mode 100644 qemu/hw/ide/qdev.c delete mode 100644 qemu/hw/ide/via.c delete mode 100644 qemu/hw/input/Makefile.objs delete mode 100644 qemu/hw/input/adb.c delete mode 100644 qemu/hw/input/hid.c delete mode 100644 qemu/hw/input/lm832x.c delete mode 100644 qemu/hw/input/milkymist-softusb.c delete mode 100644 qemu/hw/input/pckbd.c delete mode 100644 qemu/hw/input/pl050.c delete mode 100644 qemu/hw/input/ps2.c delete mode 100644 qemu/hw/input/pxa2xx_keypad.c delete mode 100644 qemu/hw/input/stellaris_input.c delete mode 100644 qemu/hw/input/tsc2005.c delete mode 100644 qemu/hw/input/tsc210x.c delete mode 100644 qemu/hw/input/virtio-input-hid.c delete mode 100644 qemu/hw/input/virtio-input-host.c delete mode 100644 qemu/hw/input/virtio-input.c delete mode 100644 qemu/hw/input/vmmouse.c delete mode 100644 qemu/hw/intc/Makefile.objs delete mode 100644 qemu/hw/intc/allwinner-a10-pic.c delete mode 100644 qemu/hw/intc/apic.c delete mode 100644 qemu/hw/intc/apic_common.c delete mode 100644 qemu/hw/intc/arm_gic.c delete mode 100644 qemu/hw/intc/arm_gic_common.c delete mode 100644 qemu/hw/intc/arm_gic_kvm.c delete mode 100644 qemu/hw/intc/arm_gicv2m.c delete mode 100644 qemu/hw/intc/arm_gicv3_common.c delete mode 100644 qemu/hw/intc/arm_gicv3_kvm.c delete mode 100644 qemu/hw/intc/armv7m_nvic.c delete mode 100644 qemu/hw/intc/aspeed_vic.c delete mode 100644 qemu/hw/intc/bcm2835_ic.c delete mode 100644 qemu/hw/intc/bcm2836_control.c delete mode 100644 qemu/hw/intc/etraxfs_pic.c delete mode 100644 qemu/hw/intc/exynos4210_combiner.c delete mode 100644 qemu/hw/intc/exynos4210_gic.c delete mode 100644 qemu/hw/intc/gic_internal.h delete mode 100644 qemu/hw/intc/grlib_irqmp.c delete mode 100644 qemu/hw/intc/heathrow_pic.c delete mode 100644 qemu/hw/intc/i8259.c delete mode 100644 qemu/hw/intc/i8259_common.c delete mode 100644 qemu/hw/intc/imx_avic.c delete mode 100644 qemu/hw/intc/ioapic.c delete mode 100644 qemu/hw/intc/ioapic_common.c delete mode 100644 qemu/hw/intc/lm32_pic.c delete mode 100644 qemu/hw/intc/omap_intc.c delete mode 100644 qemu/hw/intc/openpic.c delete mode 100644 qemu/hw/intc/openpic_kvm.c delete mode 100644 qemu/hw/intc/pl190.c delete mode 100644 qemu/hw/intc/puv3_intc.c delete mode 100644 qemu/hw/intc/realview_gic.c delete mode 100644 qemu/hw/intc/s390_flic.c delete mode 100644 qemu/hw/intc/s390_flic_kvm.c delete mode 100644 qemu/hw/intc/sh_intc.c delete mode 100644 qemu/hw/intc/slavio_intctl.c delete mode 100644 qemu/hw/intc/vgic_common.h delete mode 100644 qemu/hw/intc/xics.c delete mode 100644 qemu/hw/intc/xics_kvm.c delete mode 100644 qemu/hw/intc/xilinx_intc.c delete mode 100644 qemu/hw/ipack/Makefile.objs delete mode 100644 qemu/hw/ipack/ipack.c delete mode 100644 qemu/hw/ipack/tpci200.c delete mode 100644 qemu/hw/ipmi/Makefile.objs delete mode 100644 qemu/hw/ipmi/ipmi.c delete mode 100644 qemu/hw/ipmi/ipmi_bmc_extern.c delete mode 100644 qemu/hw/ipmi/ipmi_bmc_sim.c delete mode 100644 qemu/hw/ipmi/isa_ipmi_bt.c delete mode 100644 qemu/hw/ipmi/isa_ipmi_kcs.c delete mode 100644 qemu/hw/isa/Makefile.objs delete mode 100644 qemu/hw/isa/apm.c delete mode 100644 qemu/hw/isa/i82378.c delete mode 100644 qemu/hw/isa/isa-bus.c delete mode 100644 qemu/hw/isa/lpc_ich9.c delete mode 100644 qemu/hw/isa/pc87312.c delete mode 100644 qemu/hw/isa/piix4.c delete mode 100644 qemu/hw/isa/vt82c686.c delete mode 100644 qemu/hw/lm32/Makefile.objs delete mode 100644 qemu/hw/lm32/lm32.h delete mode 100644 qemu/hw/lm32/lm32_boards.c delete mode 100644 qemu/hw/lm32/lm32_hwsetup.h delete mode 100644 qemu/hw/lm32/milkymist-hw.h delete mode 100644 qemu/hw/lm32/milkymist.c delete mode 100644 qemu/hw/m68k/Makefile.objs delete mode 100644 qemu/hw/m68k/an5206.c delete mode 100644 qemu/hw/m68k/dummy_m68k.c delete mode 100644 qemu/hw/m68k/mcf5206.c delete mode 100644 qemu/hw/m68k/mcf5208.c delete mode 100644 qemu/hw/m68k/mcf_intc.c delete mode 100644 qemu/hw/mem/Makefile.objs delete mode 100644 qemu/hw/mem/nvdimm.c delete mode 100644 qemu/hw/mem/pc-dimm.c delete mode 100644 qemu/hw/microblaze/Makefile.objs delete mode 100644 qemu/hw/microblaze/boot.c delete mode 100644 qemu/hw/microblaze/boot.h delete mode 100644 qemu/hw/microblaze/petalogix_ml605_mmu.c delete mode 100644 qemu/hw/microblaze/petalogix_s3adsp1800_mmu.c delete mode 100644 qemu/hw/mips/Makefile.objs delete mode 100644 qemu/hw/mips/addr.c delete mode 100644 qemu/hw/mips/cps.c delete mode 100644 qemu/hw/mips/cputimer.c delete mode 100644 qemu/hw/mips/gt64xxx_pci.c delete mode 100644 qemu/hw/mips/mips_fulong2e.c delete mode 100644 qemu/hw/mips/mips_int.c delete mode 100644 qemu/hw/mips/mips_jazz.c delete mode 100644 qemu/hw/mips/mips_malta.c delete mode 100644 qemu/hw/mips/mips_mipssim.c delete mode 100644 qemu/hw/mips/mips_r4k.c delete mode 100644 qemu/hw/misc/Makefile.objs delete mode 100644 qemu/hw/misc/a9scu.c delete mode 100644 qemu/hw/misc/applesmc.c delete mode 100644 qemu/hw/misc/arm11scu.c delete mode 100644 qemu/hw/misc/arm_integrator_debug.c delete mode 100644 qemu/hw/misc/arm_l2x0.c delete mode 100644 qemu/hw/misc/arm_sysctl.c delete mode 100644 qemu/hw/misc/bcm2835_mbox.c delete mode 100644 qemu/hw/misc/bcm2835_property.c delete mode 100644 qemu/hw/misc/cbus.c delete mode 100644 qemu/hw/misc/debugexit.c delete mode 100644 qemu/hw/misc/eccmemctl.c delete mode 100644 qemu/hw/misc/edu.c delete mode 100644 qemu/hw/misc/exynos4210_pmu.c delete mode 100644 qemu/hw/misc/hyperv_testdev.c delete mode 100644 qemu/hw/misc/imx25_ccm.c delete mode 100644 qemu/hw/misc/imx31_ccm.c delete mode 100644 qemu/hw/misc/imx6_ccm.c delete mode 100644 qemu/hw/misc/imx_ccm.c delete mode 100644 qemu/hw/misc/ivshmem.c delete mode 100644 qemu/hw/misc/macio/Makefile.objs delete mode 100644 qemu/hw/misc/macio/cuda.c delete mode 100644 qemu/hw/misc/macio/mac_dbdma.c delete mode 100644 qemu/hw/misc/macio/macio.c delete mode 100644 qemu/hw/misc/max111x.c delete mode 100644 qemu/hw/misc/milkymist-hpdmc.c delete mode 100644 qemu/hw/misc/milkymist-pfpu.c delete mode 100644 qemu/hw/misc/mips_cmgcr.c delete mode 100644 qemu/hw/misc/mips_cpc.c delete mode 100644 qemu/hw/misc/mips_itu.c delete mode 100644 qemu/hw/misc/mst_fpga.c delete mode 100644 qemu/hw/misc/omap_clk.c delete mode 100644 qemu/hw/misc/omap_gpmc.c delete mode 100644 qemu/hw/misc/omap_l4.c delete mode 100644 qemu/hw/misc/omap_sdrc.c delete mode 100644 qemu/hw/misc/omap_tap.c delete mode 100644 qemu/hw/misc/pc-testdev.c delete mode 100644 qemu/hw/misc/pci-testdev.c delete mode 100644 qemu/hw/misc/puv3_pm.c delete mode 100644 qemu/hw/misc/pvpanic.c delete mode 100644 qemu/hw/misc/sga.c delete mode 100644 qemu/hw/misc/slavio_misc.c delete mode 100644 qemu/hw/misc/stm32f2xx_syscfg.c delete mode 100644 qemu/hw/misc/tmp105.c delete mode 100644 qemu/hw/misc/tmp105.h delete mode 100644 qemu/hw/misc/vmport.c delete mode 100644 qemu/hw/misc/zynq-xadc.c delete mode 100644 qemu/hw/misc/zynq_slcr.c delete mode 100644 qemu/hw/moxie/Makefile.objs delete mode 100644 qemu/hw/moxie/moxiesim.c delete mode 100644 qemu/hw/net/Makefile.objs delete mode 100644 qemu/hw/net/allwinner_emac.c delete mode 100644 qemu/hw/net/cadence_gem.c delete mode 100644 qemu/hw/net/dp8393x.c delete mode 100644 qemu/hw/net/e1000.c delete mode 100644 qemu/hw/net/e1000_regs.h delete mode 100644 qemu/hw/net/eepro100.c delete mode 100644 qemu/hw/net/etraxfs_eth.c delete mode 100644 qemu/hw/net/fsl_etsec/etsec.c delete mode 100644 qemu/hw/net/fsl_etsec/etsec.h delete mode 100644 qemu/hw/net/fsl_etsec/miim.c delete mode 100644 qemu/hw/net/fsl_etsec/registers.c delete mode 100644 qemu/hw/net/fsl_etsec/registers.h delete mode 100644 qemu/hw/net/fsl_etsec/rings.c delete mode 100644 qemu/hw/net/imx_fec.c delete mode 100644 qemu/hw/net/lan9118.c delete mode 100644 qemu/hw/net/lance.c delete mode 100644 qemu/hw/net/mcf_fec.c delete mode 100644 qemu/hw/net/milkymist-minimac2.c delete mode 100644 qemu/hw/net/mipsnet.c delete mode 100644 qemu/hw/net/ne2000-isa.c delete mode 100644 qemu/hw/net/ne2000.c delete mode 100644 qemu/hw/net/ne2000.h delete mode 100644 qemu/hw/net/opencores_eth.c delete mode 100644 qemu/hw/net/pcnet-pci.c delete mode 100644 qemu/hw/net/pcnet.c delete mode 100644 qemu/hw/net/pcnet.h delete mode 100644 qemu/hw/net/rocker/qmp-norocker.c delete mode 100644 qemu/hw/net/rocker/rocker.c delete mode 100644 qemu/hw/net/rocker/rocker.h delete mode 100644 qemu/hw/net/rocker/rocker_desc.c delete mode 100644 qemu/hw/net/rocker/rocker_desc.h delete mode 100644 qemu/hw/net/rocker/rocker_fp.c delete mode 100644 qemu/hw/net/rocker/rocker_fp.h delete mode 100644 qemu/hw/net/rocker/rocker_hw.h delete mode 100644 qemu/hw/net/rocker/rocker_of_dpa.c delete mode 100644 qemu/hw/net/rocker/rocker_of_dpa.h delete mode 100644 qemu/hw/net/rocker/rocker_tlv.h delete mode 100644 qemu/hw/net/rocker/rocker_world.c delete mode 100644 qemu/hw/net/rocker/rocker_world.h delete mode 100644 qemu/hw/net/rtl8139.c delete mode 100644 qemu/hw/net/smc91c111.c delete mode 100644 qemu/hw/net/spapr_llan.c delete mode 100644 qemu/hw/net/stellaris_enet.c delete mode 100644 qemu/hw/net/vhost_net.c delete mode 100644 qemu/hw/net/virtio-net.c delete mode 100644 qemu/hw/net/vmware_utils.h delete mode 100644 qemu/hw/net/vmxnet3.c delete mode 100644 qemu/hw/net/vmxnet3.h delete mode 100644 qemu/hw/net/vmxnet_debug.h delete mode 100644 qemu/hw/net/vmxnet_rx_pkt.c delete mode 100644 qemu/hw/net/vmxnet_rx_pkt.h delete mode 100644 qemu/hw/net/vmxnet_tx_pkt.c delete mode 100644 qemu/hw/net/vmxnet_tx_pkt.h delete mode 100644 qemu/hw/net/xen_nic.c delete mode 100644 qemu/hw/net/xgmac.c delete mode 100644 qemu/hw/net/xilinx_axienet.c delete mode 100644 qemu/hw/net/xilinx_ethlite.c delete mode 100644 qemu/hw/nvram/Makefile.objs delete mode 100644 qemu/hw/nvram/ds1225y.c delete mode 100644 qemu/hw/nvram/eeprom93xx.c delete mode 100644 qemu/hw/nvram/fw_cfg.c delete mode 100644 qemu/hw/nvram/mac_nvram.c delete mode 100644 qemu/hw/nvram/spapr_nvram.c delete mode 100644 qemu/hw/openrisc/Makefile.objs delete mode 100644 qemu/hw/openrisc/cputimer.c delete mode 100644 qemu/hw/openrisc/openrisc_sim.c delete mode 100644 qemu/hw/openrisc/pic_cpu.c delete mode 100644 qemu/hw/pci-bridge/Makefile.objs delete mode 100644 qemu/hw/pci-bridge/dec.c delete mode 100644 qemu/hw/pci-bridge/dec.h delete mode 100644 qemu/hw/pci-bridge/i82801b11.c delete mode 100644 qemu/hw/pci-bridge/ioh3420.c delete mode 100644 qemu/hw/pci-bridge/ioh3420.h delete mode 100644 qemu/hw/pci-bridge/pci_bridge_dev.c delete mode 100644 qemu/hw/pci-bridge/pci_expander_bridge.c delete mode 100644 qemu/hw/pci-bridge/xio3130_downstream.c delete mode 100644 qemu/hw/pci-bridge/xio3130_downstream.h delete mode 100644 qemu/hw/pci-bridge/xio3130_upstream.c delete mode 100644 qemu/hw/pci-bridge/xio3130_upstream.h delete mode 100644 qemu/hw/pci-host/Makefile.objs delete mode 100644 qemu/hw/pci-host/apb.c delete mode 100644 qemu/hw/pci-host/bonito.c delete mode 100644 qemu/hw/pci-host/gpex.c delete mode 100644 qemu/hw/pci-host/grackle.c delete mode 100644 qemu/hw/pci-host/pam.c delete mode 100644 qemu/hw/pci-host/piix.c delete mode 100644 qemu/hw/pci-host/ppce500.c delete mode 100644 qemu/hw/pci-host/prep.c delete mode 100644 qemu/hw/pci-host/q35.c delete mode 100644 qemu/hw/pci-host/uninorth.c delete mode 100644 qemu/hw/pci-host/versatile.c delete mode 100644 qemu/hw/pci/Makefile.objs delete mode 100644 qemu/hw/pci/msi.c delete mode 100644 qemu/hw/pci/msix.c delete mode 100644 qemu/hw/pci/pci-stub.c delete mode 100644 qemu/hw/pci/pci.c delete mode 100644 qemu/hw/pci/pci_bridge.c delete mode 100644 qemu/hw/pci/pci_host.c delete mode 100644 qemu/hw/pci/pcie.c delete mode 100644 qemu/hw/pci/pcie_aer.c delete mode 100644 qemu/hw/pci/pcie_host.c delete mode 100644 qemu/hw/pci/pcie_port.c delete mode 100644 qemu/hw/pci/shpc.c delete mode 100644 qemu/hw/pci/slotid_cap.c delete mode 100644 qemu/hw/pcmcia/Makefile.objs delete mode 100644 qemu/hw/pcmcia/pcmcia.c delete mode 100644 qemu/hw/pcmcia/pxa2xx.c delete mode 100644 qemu/hw/ppc/Makefile.objs delete mode 100644 qemu/hw/ppc/e500-ccsr.h delete mode 100644 qemu/hw/ppc/e500.c delete mode 100644 qemu/hw/ppc/e500.h delete mode 100644 qemu/hw/ppc/e500plat.c delete mode 100644 qemu/hw/ppc/mac.h delete mode 100644 qemu/hw/ppc/mac_newworld.c delete mode 100644 qemu/hw/ppc/mac_oldworld.c delete mode 100644 qemu/hw/ppc/mpc8544_guts.c delete mode 100644 qemu/hw/ppc/mpc8544ds.c delete mode 100644 qemu/hw/ppc/ppc.c delete mode 100644 qemu/hw/ppc/ppc405.h delete mode 100644 qemu/hw/ppc/ppc405_boards.c delete mode 100644 qemu/hw/ppc/ppc405_uc.c delete mode 100644 qemu/hw/ppc/ppc440_bamboo.c delete mode 100644 qemu/hw/ppc/ppc4xx_devs.c delete mode 100644 qemu/hw/ppc/ppc4xx_pci.c delete mode 100644 qemu/hw/ppc/ppc_booke.c delete mode 100644 qemu/hw/ppc/ppce500_spin.c delete mode 100644 qemu/hw/ppc/prep.c delete mode 100644 qemu/hw/ppc/spapr.c delete mode 100644 qemu/hw/ppc/spapr_drc.c delete mode 100644 qemu/hw/ppc/spapr_events.c delete mode 100644 qemu/hw/ppc/spapr_hcall.c delete mode 100644 qemu/hw/ppc/spapr_iommu.c delete mode 100644 qemu/hw/ppc/spapr_pci.c delete mode 100644 qemu/hw/ppc/spapr_pci_vfio.c delete mode 100644 qemu/hw/ppc/spapr_rng.c delete mode 100644 qemu/hw/ppc/spapr_rtas.c delete mode 100644 qemu/hw/ppc/spapr_rtc.c delete mode 100644 qemu/hw/ppc/spapr_vio.c delete mode 100644 qemu/hw/ppc/virtex_ml507.c delete mode 100644 qemu/hw/s390x/Makefile.objs delete mode 100644 qemu/hw/s390x/css.c delete mode 100644 qemu/hw/s390x/css.h delete mode 100644 qemu/hw/s390x/event-facility.c delete mode 100644 qemu/hw/s390x/ipl.c delete mode 100644 qemu/hw/s390x/ipl.h delete mode 100644 qemu/hw/s390x/s390-pci-bus.c delete mode 100644 qemu/hw/s390x/s390-pci-bus.h delete mode 100644 qemu/hw/s390x/s390-pci-inst.c delete mode 100644 qemu/hw/s390x/s390-pci-inst.h delete mode 100644 qemu/hw/s390x/s390-skeys-kvm.c delete mode 100644 qemu/hw/s390x/s390-skeys.c delete mode 100644 qemu/hw/s390x/s390-virtio-ccw.c delete mode 100644 qemu/hw/s390x/s390-virtio-hcall.c delete mode 100644 qemu/hw/s390x/s390-virtio.c delete mode 100644 qemu/hw/s390x/s390-virtio.h delete mode 100644 qemu/hw/s390x/sclp.c delete mode 100644 qemu/hw/s390x/sclpcpu.c delete mode 100644 qemu/hw/s390x/sclpquiesce.c delete mode 100644 qemu/hw/s390x/virtio-ccw.c delete mode 100644 qemu/hw/s390x/virtio-ccw.h delete mode 100644 qemu/hw/scsi/Makefile.objs delete mode 100644 qemu/hw/scsi/esp-pci.c delete mode 100644 qemu/hw/scsi/esp.c delete mode 100644 qemu/hw/scsi/lsi53c895a.c delete mode 100644 qemu/hw/scsi/megasas.c delete mode 100644 qemu/hw/scsi/mfi.h delete mode 100644 qemu/hw/scsi/mpi.h delete mode 100644 qemu/hw/scsi/mptconfig.c delete mode 100644 qemu/hw/scsi/mptendian.c delete mode 100644 qemu/hw/scsi/mptsas.c delete mode 100644 qemu/hw/scsi/mptsas.h delete mode 100644 qemu/hw/scsi/scsi-bus.c delete mode 100644 qemu/hw/scsi/scsi-disk.c delete mode 100644 qemu/hw/scsi/scsi-generic.c delete mode 100644 qemu/hw/scsi/spapr_vscsi.c delete mode 100644 qemu/hw/scsi/srp.h delete mode 100644 qemu/hw/scsi/vhost-scsi.c delete mode 100644 qemu/hw/scsi/viosrp.h delete mode 100644 qemu/hw/scsi/virtio-scsi-dataplane.c delete mode 100644 qemu/hw/scsi/virtio-scsi.c delete mode 100644 qemu/hw/scsi/vmw_pvscsi.c delete mode 100644 qemu/hw/scsi/vmw_pvscsi.h delete mode 100644 qemu/hw/sd/Makefile.objs delete mode 100644 qemu/hw/sd/core.c delete mode 100644 qemu/hw/sd/milkymist-memcard.c delete mode 100644 qemu/hw/sd/omap_mmc.c delete mode 100644 qemu/hw/sd/pl181.c delete mode 100644 qemu/hw/sd/pxa2xx_mmci.c delete mode 100644 qemu/hw/sd/sd.c delete mode 100644 qemu/hw/sd/sdhci-internal.h delete mode 100644 qemu/hw/sd/sdhci.c delete mode 100644 qemu/hw/sd/ssi-sd.c delete mode 100644 qemu/hw/sh4/Makefile.objs delete mode 100644 qemu/hw/sh4/r2d.c delete mode 100644 qemu/hw/sh4/sh7750.c delete mode 100644 qemu/hw/sh4/sh7750_regnames.c delete mode 100644 qemu/hw/sh4/sh7750_regnames.h delete mode 100644 qemu/hw/sh4/sh7750_regs.h delete mode 100644 qemu/hw/sh4/sh_pci.c delete mode 100644 qemu/hw/sh4/shix.c delete mode 100644 qemu/hw/smbios/Makefile.objs delete mode 100644 qemu/hw/smbios/smbios.c delete mode 100644 qemu/hw/sparc/Makefile.objs delete mode 100644 qemu/hw/sparc/leon3.c delete mode 100644 qemu/hw/sparc/sun4m.c delete mode 100644 qemu/hw/sparc64/Makefile.objs delete mode 100644 qemu/hw/sparc64/sun4u.c delete mode 100644 qemu/hw/ssi/Makefile.objs delete mode 100644 qemu/hw/ssi/omap_spi.c delete mode 100644 qemu/hw/ssi/pl022.c delete mode 100644 qemu/hw/ssi/ssi.c delete mode 100644 qemu/hw/ssi/xilinx_spi.c delete mode 100644 qemu/hw/ssi/xilinx_spips.c delete mode 100644 qemu/hw/timer/Makefile.objs delete mode 100644 qemu/hw/timer/a9gtimer.c delete mode 100644 qemu/hw/timer/allwinner-a10-pit.c delete mode 100644 qemu/hw/timer/arm_mptimer.c delete mode 100644 qemu/hw/timer/arm_timer.c delete mode 100644 qemu/hw/timer/aspeed_timer.c delete mode 100644 qemu/hw/timer/cadence_ttc.c delete mode 100644 qemu/hw/timer/digic-timer.c delete mode 100644 qemu/hw/timer/ds1338.c delete mode 100644 qemu/hw/timer/etraxfs_timer.c delete mode 100644 qemu/hw/timer/exynos4210_mct.c delete mode 100644 qemu/hw/timer/exynos4210_pwm.c delete mode 100644 qemu/hw/timer/exynos4210_rtc.c delete mode 100644 qemu/hw/timer/grlib_gptimer.c delete mode 100644 qemu/hw/timer/hpet.c delete mode 100644 qemu/hw/timer/i8254.c delete mode 100644 qemu/hw/timer/i8254_common.c delete mode 100644 qemu/hw/timer/imx_epit.c delete mode 100644 qemu/hw/timer/imx_gpt.c delete mode 100644 qemu/hw/timer/lm32_timer.c delete mode 100644 qemu/hw/timer/m48t59.c delete mode 100644 qemu/hw/timer/mc146818rtc.c delete mode 100644 qemu/hw/timer/milkymist-sysctl.c delete mode 100644 qemu/hw/timer/omap_gptimer.c delete mode 100644 qemu/hw/timer/omap_synctimer.c delete mode 100644 qemu/hw/timer/pl031.c delete mode 100644 qemu/hw/timer/puv3_ost.c delete mode 100644 qemu/hw/timer/pxa2xx_timer.c delete mode 100644 qemu/hw/timer/sh_timer.c delete mode 100644 qemu/hw/timer/slavio_timer.c delete mode 100644 qemu/hw/timer/stm32f2xx_timer.c delete mode 100644 qemu/hw/timer/twl92230.c delete mode 100644 qemu/hw/timer/xilinx_timer.c delete mode 100644 qemu/hw/tpm/Makefile.objs delete mode 100644 qemu/hw/tpm/tpm_int.h delete mode 100644 qemu/hw/tpm/tpm_passthrough.c delete mode 100644 qemu/hw/tpm/tpm_tis.c delete mode 100644 qemu/hw/tpm/tpm_tis.h delete mode 100644 qemu/hw/tpm/tpm_util.c delete mode 100644 qemu/hw/tpm/tpm_util.h delete mode 100644 qemu/hw/tricore/Makefile.objs delete mode 100644 qemu/hw/tricore/tricore_testboard.c delete mode 100644 qemu/hw/unicore32/Makefile.objs delete mode 100644 qemu/hw/unicore32/puv3.c delete mode 100644 qemu/hw/usb/Makefile.objs delete mode 100644 qemu/hw/usb/bus.c delete mode 100644 qemu/hw/usb/ccid-card-emulated.c delete mode 100644 qemu/hw/usb/ccid-card-passthru.c delete mode 100644 qemu/hw/usb/ccid.h delete mode 100644 qemu/hw/usb/combined-packet.c delete mode 100644 qemu/hw/usb/core.c delete mode 100644 qemu/hw/usb/desc-msos.c delete mode 100644 qemu/hw/usb/desc.c delete mode 100644 qemu/hw/usb/desc.h delete mode 100644 qemu/hw/usb/dev-audio.c delete mode 100644 qemu/hw/usb/dev-bluetooth.c delete mode 100644 qemu/hw/usb/dev-hid.c delete mode 100644 qemu/hw/usb/dev-hub.c delete mode 100644 qemu/hw/usb/dev-mtp.c delete mode 100644 qemu/hw/usb/dev-network.c delete mode 100644 qemu/hw/usb/dev-serial.c delete mode 100644 qemu/hw/usb/dev-smartcard-reader.c delete mode 100644 qemu/hw/usb/dev-storage.c delete mode 100644 qemu/hw/usb/dev-uas.c delete mode 100644 qemu/hw/usb/dev-wacom.c delete mode 100644 qemu/hw/usb/hcd-ehci-pci.c delete mode 100644 qemu/hw/usb/hcd-ehci-sysbus.c delete mode 100644 qemu/hw/usb/hcd-ehci.c delete mode 100644 qemu/hw/usb/hcd-ehci.h delete mode 100644 qemu/hw/usb/hcd-musb.c delete mode 100644 qemu/hw/usb/hcd-ohci.c delete mode 100644 qemu/hw/usb/hcd-uhci.c delete mode 100644 qemu/hw/usb/hcd-xhci.c delete mode 100644 qemu/hw/usb/host-legacy.c delete mode 100644 qemu/hw/usb/host-libusb.c delete mode 100644 qemu/hw/usb/host-stub.c delete mode 100644 qemu/hw/usb/host.h delete mode 100644 qemu/hw/usb/libhw.c delete mode 100644 qemu/hw/usb/quirks-ftdi-ids.h delete mode 100644 qemu/hw/usb/quirks-pl2303-ids.h delete mode 100644 qemu/hw/usb/quirks.c delete mode 100644 qemu/hw/usb/quirks.h delete mode 100644 qemu/hw/usb/redirect.c delete mode 100644 qemu/hw/usb/tusb6010.c delete mode 100644 qemu/hw/vfio/Makefile.objs delete mode 100644 qemu/hw/vfio/amd-xgbe.c delete mode 100644 qemu/hw/vfio/calxeda-xgmac.c delete mode 100644 qemu/hw/vfio/common.c delete mode 100644 qemu/hw/vfio/pci-quirks.c delete mode 100644 qemu/hw/vfio/pci.c delete mode 100644 qemu/hw/vfio/pci.h delete mode 100644 qemu/hw/vfio/platform.c delete mode 100644 qemu/hw/virtio/Makefile.objs delete mode 100644 qemu/hw/virtio/vhost-backend.c delete mode 100644 qemu/hw/virtio/vhost-user.c delete mode 100644 qemu/hw/virtio/vhost.c delete mode 100644 qemu/hw/virtio/virtio-balloon.c delete mode 100644 qemu/hw/virtio/virtio-bus.c delete mode 100644 qemu/hw/virtio/virtio-mmio.c delete mode 100644 qemu/hw/virtio/virtio-pci.c delete mode 100644 qemu/hw/virtio/virtio-pci.h delete mode 100644 qemu/hw/virtio/virtio-rng.c delete mode 100644 qemu/hw/virtio/virtio.c delete mode 100644 qemu/hw/watchdog/Makefile.objs delete mode 100644 qemu/hw/watchdog/watchdog.c delete mode 100644 qemu/hw/watchdog/wdt_diag288.c delete mode 100644 qemu/hw/watchdog/wdt_i6300esb.c delete mode 100644 qemu/hw/watchdog/wdt_ib700.c delete mode 100644 qemu/hw/xen/Makefile.objs delete mode 100644 qemu/hw/xen/xen-host-pci-device.c delete mode 100644 qemu/hw/xen/xen-host-pci-device.h delete mode 100644 qemu/hw/xen/xen_backend.c delete mode 100644 qemu/hw/xen/xen_devconfig.c delete mode 100644 qemu/hw/xen/xen_pt.c delete mode 100644 qemu/hw/xen/xen_pt.h delete mode 100644 qemu/hw/xen/xen_pt_config_init.c delete mode 100644 qemu/hw/xen/xen_pt_graphics.c delete mode 100644 qemu/hw/xen/xen_pt_msi.c delete mode 100644 qemu/hw/xenpv/Makefile.objs delete mode 100644 qemu/hw/xenpv/xen_domainbuild.c delete mode 100644 qemu/hw/xenpv/xen_domainbuild.h delete mode 100644 qemu/hw/xenpv/xen_machine_pv.c delete mode 100644 qemu/hw/xtensa/Makefile.objs delete mode 100644 qemu/hw/xtensa/bootparam.h delete mode 100644 qemu/hw/xtensa/pic_cpu.c delete mode 100644 qemu/hw/xtensa/sim.c delete mode 100644 qemu/hw/xtensa/xtfpga.c (limited to 'qemu/hw') diff --git a/qemu/hw/9pfs/9p-handle.c b/qemu/hw/9pfs/9p-handle.c deleted file mode 100644 index 894041488..000000000 --- a/qemu/hw/9pfs/9p-handle.c +++ /dev/null @@ -1,709 +0,0 @@ -/* - * 9p handle callback - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Aneesh Kumar K.V - * - * 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 "9p.h" -#include "9p-xattr.h" -#include -#include -#include -#include -#include -#include "qemu/xattr.h" -#include "qemu/cutils.h" -#include "qemu/error-report.h" -#include -#ifdef CONFIG_LINUX_MAGIC_H -#include -#endif -#include - -#ifndef XFS_SUPER_MAGIC -#define XFS_SUPER_MAGIC 0x58465342 -#endif -#ifndef EXT2_SUPER_MAGIC -#define EXT2_SUPER_MAGIC 0xEF53 -#endif -#ifndef REISERFS_SUPER_MAGIC -#define REISERFS_SUPER_MAGIC 0x52654973 -#endif -#ifndef BTRFS_SUPER_MAGIC -#define BTRFS_SUPER_MAGIC 0x9123683E -#endif - -struct handle_data { - int mountfd; - int handle_bytes; -}; - -static inline int name_to_handle(int dirfd, const char *name, - struct file_handle *fh, int *mnt_id, int flags) -{ - return name_to_handle_at(dirfd, name, fh, mnt_id, flags); -} - -static inline int open_by_handle(int mountfd, const char *fh, int flags) -{ - return open_by_handle_at(mountfd, (struct file_handle *)fh, flags); -} - -static int handle_update_file_cred(int dirfd, const char *name, FsCred *credp) -{ - int fd, ret; - fd = openat(dirfd, name, O_NONBLOCK | O_NOFOLLOW); - if (fd < 0) { - return fd; - } - ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH); - if (ret < 0) { - goto err_out; - } - ret = fchmod(fd, credp->fc_mode & 07777); -err_out: - close(fd); - return ret; -} - - -static int handle_lstat(FsContext *fs_ctx, V9fsPath *fs_path, - struct stat *stbuf) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)fs_ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_PATH); - if (fd < 0) { - return fd; - } - ret = fstatat(fd, "", stbuf, AT_EMPTY_PATH); - close(fd); - return ret; -} - -static ssize_t handle_readlink(FsContext *fs_ctx, V9fsPath *fs_path, - char *buf, size_t bufsz) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)fs_ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_PATH); - if (fd < 0) { - return fd; - } - ret = readlinkat(fd, "", buf, bufsz); - close(fd); - return ret; -} - -static int handle_close(FsContext *ctx, V9fsFidOpenState *fs) -{ - return close(fs->fd); -} - -static int handle_closedir(FsContext *ctx, V9fsFidOpenState *fs) -{ - return closedir(fs->dir); -} - -static int handle_open(FsContext *ctx, V9fsPath *fs_path, - int flags, V9fsFidOpenState *fs) -{ - struct handle_data *data = (struct handle_data *)ctx->private; - - fs->fd = open_by_handle(data->mountfd, fs_path->data, flags); - return fs->fd; -} - -static int handle_opendir(FsContext *ctx, - V9fsPath *fs_path, V9fsFidOpenState *fs) -{ - int ret; - ret = handle_open(ctx, fs_path, O_DIRECTORY, fs); - if (ret < 0) { - return -1; - } - fs->dir = fdopendir(ret); - if (!fs->dir) { - return -1; - } - return 0; -} - -static void handle_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) -{ - rewinddir(fs->dir); -} - -static off_t handle_telldir(FsContext *ctx, V9fsFidOpenState *fs) -{ - return telldir(fs->dir); -} - -static int handle_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, - struct dirent *entry, - struct dirent **result) -{ - return readdir_r(fs->dir, entry, result); -} - -static void handle_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) -{ - seekdir(fs->dir, off); -} - -static ssize_t handle_preadv(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ -#ifdef CONFIG_PREADV - return preadv(fs->fd, iov, iovcnt, offset); -#else - int err = lseek(fs->fd, offset, SEEK_SET); - if (err == -1) { - return err; - } else { - return readv(fs->fd, iov, iovcnt); - } -#endif -} - -static ssize_t handle_pwritev(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ - ssize_t ret; -#ifdef CONFIG_PREADV - ret = pwritev(fs->fd, iov, iovcnt, offset); -#else - int err = lseek(fs->fd, offset, SEEK_SET); - if (err == -1) { - return err; - } else { - ret = writev(fs->fd, iov, iovcnt); - } -#endif -#ifdef CONFIG_SYNC_FILE_RANGE - if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { - /* - * Initiate a writeback. This is not a data integrity sync. - * We want to ensure that we don't leave dirty pages in the cache - * after write when writeout=immediate is sepcified. - */ - sync_file_range(fs->fd, offset, ret, - SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); - } -#endif - return ret; -} - -static int handle_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)fs_ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); - if (fd < 0) { - return fd; - } - ret = fchmod(fd, credp->fc_mode); - close(fd); - return ret; -} - -static int handle_mknod(FsContext *fs_ctx, V9fsPath *dir_path, - const char *name, FsCred *credp) -{ - int dirfd, ret; - struct handle_data *data = (struct handle_data *)fs_ctx->private; - - dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); - if (dirfd < 0) { - return dirfd; - } - ret = mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev); - if (!ret) { - ret = handle_update_file_cred(dirfd, name, credp); - } - close(dirfd); - return ret; -} - -static int handle_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, - const char *name, FsCred *credp) -{ - int dirfd, ret; - struct handle_data *data = (struct handle_data *)fs_ctx->private; - - dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); - if (dirfd < 0) { - return dirfd; - } - ret = mkdirat(dirfd, name, credp->fc_mode); - if (!ret) { - ret = handle_update_file_cred(dirfd, name, credp); - } - close(dirfd); - return ret; -} - -static int handle_fstat(FsContext *fs_ctx, int fid_type, - V9fsFidOpenState *fs, struct stat *stbuf) -{ - int fd; - - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); - } else { - fd = fs->fd; - } - return fstat(fd, stbuf); -} - -static int handle_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, - int flags, FsCred *credp, V9fsFidOpenState *fs) -{ - int ret; - int dirfd, fd; - struct handle_data *data = (struct handle_data *)fs_ctx->private; - - dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); - if (dirfd < 0) { - return dirfd; - } - fd = openat(dirfd, name, flags | O_NOFOLLOW, credp->fc_mode); - if (fd >= 0) { - ret = handle_update_file_cred(dirfd, name, credp); - if (ret < 0) { - close(fd); - fd = ret; - } else { - fs->fd = fd; - } - } - close(dirfd); - return fd; -} - - -static int handle_symlink(FsContext *fs_ctx, const char *oldpath, - V9fsPath *dir_path, const char *name, FsCred *credp) -{ - int fd, dirfd, ret; - struct handle_data *data = (struct handle_data *)fs_ctx->private; - - dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); - if (dirfd < 0) { - return dirfd; - } - ret = symlinkat(oldpath, dirfd, name); - if (!ret) { - fd = openat(dirfd, name, O_PATH | O_NOFOLLOW); - if (fd < 0) { - ret = fd; - goto err_out; - } - ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH); - close(fd); - } -err_out: - close(dirfd); - return ret; -} - -static int handle_link(FsContext *ctx, V9fsPath *oldpath, - V9fsPath *dirpath, const char *name) -{ - int oldfd, newdirfd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - - oldfd = open_by_handle(data->mountfd, oldpath->data, O_PATH); - if (oldfd < 0) { - return oldfd; - } - newdirfd = open_by_handle(data->mountfd, dirpath->data, O_PATH); - if (newdirfd < 0) { - close(oldfd); - return newdirfd; - } - ret = linkat(oldfd, "", newdirfd, name, AT_EMPTY_PATH); - close(newdirfd); - close(oldfd); - return ret; -} - -static int handle_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK | O_WRONLY); - if (fd < 0) { - return fd; - } - ret = ftruncate(fd, size); - close(fd); - return ret; -} - -static int handle_rename(FsContext *ctx, const char *oldpath, - const char *newpath) -{ - errno = EOPNOTSUPP; - return -1; -} - -static int handle_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)fs_ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_PATH); - if (fd < 0) { - return fd; - } - ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH); - close(fd); - return ret; -} - -static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path, - const struct timespec *buf) -{ - int ret; -#ifdef CONFIG_UTIMENSAT - int fd; - struct handle_data *data = (struct handle_data *)ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); - if (fd < 0) { - return fd; - } - ret = futimens(fd, buf); - close(fd); -#else - ret = -1; - errno = ENOSYS; -#endif - return ret; -} - -static int handle_remove(FsContext *ctx, const char *path) -{ - errno = EOPNOTSUPP; - return -1; -} - -static int handle_fsync(FsContext *ctx, int fid_type, - V9fsFidOpenState *fs, int datasync) -{ - int fd; - - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); - } else { - fd = fs->fd; - } - - if (datasync) { - return qemu_fdatasync(fd); - } else { - return fsync(fd); - } -} - -static int handle_statfs(FsContext *ctx, V9fsPath *fs_path, - struct statfs *stbuf) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); - if (fd < 0) { - return fd; - } - ret = fstatfs(fd, stbuf); - close(fd); - return ret; -} - -static ssize_t handle_lgetxattr(FsContext *ctx, V9fsPath *fs_path, - const char *name, void *value, size_t size) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); - if (fd < 0) { - return fd; - } - ret = fgetxattr(fd, name, value, size); - close(fd); - return ret; -} - -static ssize_t handle_llistxattr(FsContext *ctx, V9fsPath *fs_path, - void *value, size_t size) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); - if (fd < 0) { - return fd; - } - ret = flistxattr(fd, value, size); - close(fd); - return ret; -} - -static int handle_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, - void *value, size_t size, int flags) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); - if (fd < 0) { - return fd; - } - ret = fsetxattr(fd, name, value, size, flags); - close(fd); - return ret; -} - -static int handle_lremovexattr(FsContext *ctx, V9fsPath *fs_path, - const char *name) -{ - int fd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); - if (fd < 0) { - return fd; - } - ret = fremovexattr(fd, name); - close(fd); - return ret; -} - -static int handle_name_to_path(FsContext *ctx, V9fsPath *dir_path, - const char *name, V9fsPath *target) -{ - char *buffer; - struct file_handle *fh; - int dirfd, ret, mnt_id; - struct handle_data *data = (struct handle_data *)ctx->private; - - /* "." and ".." are not allowed */ - if (!strcmp(name, ".") || !strcmp(name, "..")) { - errno = EINVAL; - return -1; - - } - if (dir_path) { - dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); - } else { - /* relative to export root */ - buffer = rpath(ctx, "."); - dirfd = open(buffer, O_DIRECTORY); - g_free(buffer); - } - if (dirfd < 0) { - return dirfd; - } - fh = g_malloc(sizeof(struct file_handle) + data->handle_bytes); - fh->handle_bytes = data->handle_bytes; - /* add a "./" at the beginning of the path */ - buffer = g_strdup_printf("./%s", name); - /* flag = 0 imply don't follow symlink */ - ret = name_to_handle(dirfd, buffer, fh, &mnt_id, 0); - if (!ret) { - target->data = (char *)fh; - target->size = sizeof(struct file_handle) + data->handle_bytes; - } else { - g_free(fh); - } - close(dirfd); - g_free(buffer); - return ret; -} - -static int handle_renameat(FsContext *ctx, V9fsPath *olddir, - const char *old_name, V9fsPath *newdir, - const char *new_name) -{ - int olddirfd, newdirfd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - - olddirfd = open_by_handle(data->mountfd, olddir->data, O_PATH); - if (olddirfd < 0) { - return olddirfd; - } - newdirfd = open_by_handle(data->mountfd, newdir->data, O_PATH); - if (newdirfd < 0) { - close(olddirfd); - return newdirfd; - } - ret = renameat(olddirfd, old_name, newdirfd, new_name); - close(newdirfd); - close(olddirfd); - return ret; -} - -static int handle_unlinkat(FsContext *ctx, V9fsPath *dir, - const char *name, int flags) -{ - int dirfd, ret; - struct handle_data *data = (struct handle_data *)ctx->private; - int rflags; - - dirfd = open_by_handle(data->mountfd, dir->data, O_PATH); - if (dirfd < 0) { - return dirfd; - } - - rflags = 0; - if (flags & P9_DOTL_AT_REMOVEDIR) { - rflags |= AT_REMOVEDIR; - } - - ret = unlinkat(dirfd, name, rflags); - - close(dirfd); - return ret; -} - -static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path, - mode_t st_mode, uint64_t *st_gen) -{ -#ifdef FS_IOC_GETVERSION - int err; - V9fsFidOpenState fid_open; - - /* - * Do not try to open special files like device nodes, fifos etc - * We can get fd for regular files and directories only - */ - if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { - errno = ENOTTY; - return -1; - } - err = handle_open(ctx, path, O_RDONLY, &fid_open); - if (err < 0) { - return err; - } - err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); - handle_close(ctx, &fid_open); - return err; -#else - errno = ENOTTY; - return -1; -#endif -} - -static int handle_init(FsContext *ctx) -{ - int ret, mnt_id; - struct statfs stbuf; - struct file_handle fh; - struct handle_data *data = g_malloc(sizeof(struct handle_data)); - - data->mountfd = open(ctx->fs_root, O_DIRECTORY); - if (data->mountfd < 0) { - ret = data->mountfd; - goto err_out; - } - ret = statfs(ctx->fs_root, &stbuf); - if (!ret) { - switch (stbuf.f_type) { - case EXT2_SUPER_MAGIC: - case BTRFS_SUPER_MAGIC: - case REISERFS_SUPER_MAGIC: - case XFS_SUPER_MAGIC: - ctx->exops.get_st_gen = handle_ioc_getversion; - break; - } - } - memset(&fh, 0, sizeof(struct file_handle)); - ret = name_to_handle(data->mountfd, ".", &fh, &mnt_id, 0); - if (ret && errno == EOVERFLOW) { - data->handle_bytes = fh.handle_bytes; - ctx->private = data; - ret = 0; - goto out; - } - /* we got 0 byte handle ? */ - ret = -1; - close(data->mountfd); -err_out: - g_free(data); -out: - return ret; -} - -static int handle_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse) -{ - const char *sec_model = qemu_opt_get(opts, "security_model"); - const char *path = qemu_opt_get(opts, "path"); - - if (sec_model) { - error_report("Invalid argument security_model specified with handle fsdriver"); - return -1; - } - - if (!path) { - error_report("fsdev: No path specified"); - return -1; - } - fse->path = g_strdup(path); - return 0; - -} - -FileOperations handle_ops = { - .parse_opts = handle_parse_opts, - .init = handle_init, - .lstat = handle_lstat, - .readlink = handle_readlink, - .close = handle_close, - .closedir = handle_closedir, - .open = handle_open, - .opendir = handle_opendir, - .rewinddir = handle_rewinddir, - .telldir = handle_telldir, - .readdir_r = handle_readdir_r, - .seekdir = handle_seekdir, - .preadv = handle_preadv, - .pwritev = handle_pwritev, - .chmod = handle_chmod, - .mknod = handle_mknod, - .mkdir = handle_mkdir, - .fstat = handle_fstat, - .open2 = handle_open2, - .symlink = handle_symlink, - .link = handle_link, - .truncate = handle_truncate, - .rename = handle_rename, - .chown = handle_chown, - .utimensat = handle_utimensat, - .remove = handle_remove, - .fsync = handle_fsync, - .statfs = handle_statfs, - .lgetxattr = handle_lgetxattr, - .llistxattr = handle_llistxattr, - .lsetxattr = handle_lsetxattr, - .lremovexattr = handle_lremovexattr, - .name_to_path = handle_name_to_path, - .renameat = handle_renameat, - .unlinkat = handle_unlinkat, -}; diff --git a/qemu/hw/9pfs/9p-local.c b/qemu/hw/9pfs/9p-local.c deleted file mode 100644 index 16f45f485..000000000 --- a/qemu/hw/9pfs/9p-local.c +++ /dev/null @@ -1,1282 +0,0 @@ -/* - * 9p Posix callback - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Anthony Liguori - * - * 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 "9p.h" -#include "9p-xattr.h" -#include "fsdev/qemu-fsdev.h" /* local_ops */ -#include -#include -#include -#include -#include -#include "qemu/xattr.h" -#include "qemu/cutils.h" -#include "qemu/error-report.h" -#include -#include -#ifdef CONFIG_LINUX_MAGIC_H -#include -#endif -#include - -#ifndef XFS_SUPER_MAGIC -#define XFS_SUPER_MAGIC 0x58465342 -#endif -#ifndef EXT2_SUPER_MAGIC -#define EXT2_SUPER_MAGIC 0xEF53 -#endif -#ifndef REISERFS_SUPER_MAGIC -#define REISERFS_SUPER_MAGIC 0x52654973 -#endif -#ifndef BTRFS_SUPER_MAGIC -#define BTRFS_SUPER_MAGIC 0x9123683E -#endif - -#define VIRTFS_META_DIR ".virtfs_metadata" - -static char *local_mapped_attr_path(FsContext *ctx, const char *path) -{ - int dirlen; - const char *name = strrchr(path, '/'); - if (name) { - dirlen = name - path; - ++name; - } else { - name = path; - dirlen = 0; - } - return g_strdup_printf("%s/%.*s/%s/%s", ctx->fs_root, - dirlen, path, VIRTFS_META_DIR, name); -} - -static FILE *local_fopen(const char *path, const char *mode) -{ - int fd, o_mode = 0; - FILE *fp; - int flags = O_NOFOLLOW; - /* - * only supports two modes - */ - if (mode[0] == 'r') { - flags |= O_RDONLY; - } else if (mode[0] == 'w') { - flags |= O_WRONLY | O_TRUNC | O_CREAT; - o_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; - } else { - return NULL; - } - fd = open(path, flags, o_mode); - if (fd == -1) { - return NULL; - } - fp = fdopen(fd, mode); - if (!fp) { - close(fd); - } - return fp; -} - -#define ATTR_MAX 100 -static void local_mapped_file_attr(FsContext *ctx, const char *path, - struct stat *stbuf) -{ - FILE *fp; - char buf[ATTR_MAX]; - char *attr_path; - - attr_path = local_mapped_attr_path(ctx, path); - fp = local_fopen(attr_path, "r"); - g_free(attr_path); - if (!fp) { - return; - } - memset(buf, 0, ATTR_MAX); - while (fgets(buf, ATTR_MAX, fp)) { - if (!strncmp(buf, "virtfs.uid", 10)) { - stbuf->st_uid = atoi(buf+11); - } else if (!strncmp(buf, "virtfs.gid", 10)) { - stbuf->st_gid = atoi(buf+11); - } else if (!strncmp(buf, "virtfs.mode", 11)) { - stbuf->st_mode = atoi(buf+12); - } else if (!strncmp(buf, "virtfs.rdev", 11)) { - stbuf->st_rdev = atoi(buf+12); - } - memset(buf, 0, ATTR_MAX); - } - fclose(fp); -} - -static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf) -{ - int err; - char *buffer; - char *path = fs_path->data; - - buffer = rpath(fs_ctx, path); - err = lstat(buffer, stbuf); - if (err) { - goto err_out; - } - if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - /* Actual credentials are part of extended attrs */ - uid_t tmp_uid; - gid_t tmp_gid; - mode_t tmp_mode; - dev_t tmp_dev; - if (getxattr(buffer, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) { - stbuf->st_uid = le32_to_cpu(tmp_uid); - } - if (getxattr(buffer, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) { - stbuf->st_gid = le32_to_cpu(tmp_gid); - } - if (getxattr(buffer, "user.virtfs.mode", - &tmp_mode, sizeof(mode_t)) > 0) { - stbuf->st_mode = le32_to_cpu(tmp_mode); - } - if (getxattr(buffer, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) { - stbuf->st_rdev = le64_to_cpu(tmp_dev); - } - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - local_mapped_file_attr(fs_ctx, path, stbuf); - } - -err_out: - g_free(buffer); - return err; -} - -static int local_create_mapped_attr_dir(FsContext *ctx, const char *path) -{ - int err; - char *attr_dir; - char *tmp_path = g_strdup(path); - - attr_dir = g_strdup_printf("%s/%s/%s", - ctx->fs_root, dirname(tmp_path), VIRTFS_META_DIR); - - err = mkdir(attr_dir, 0700); - if (err < 0 && errno == EEXIST) { - err = 0; - } - g_free(attr_dir); - g_free(tmp_path); - return err; -} - -static int local_set_mapped_file_attr(FsContext *ctx, - const char *path, FsCred *credp) -{ - FILE *fp; - int ret = 0; - char buf[ATTR_MAX]; - char *attr_path; - int uid = -1, gid = -1, mode = -1, rdev = -1; - - attr_path = local_mapped_attr_path(ctx, path); - fp = local_fopen(attr_path, "r"); - if (!fp) { - goto create_map_file; - } - memset(buf, 0, ATTR_MAX); - while (fgets(buf, ATTR_MAX, fp)) { - if (!strncmp(buf, "virtfs.uid", 10)) { - uid = atoi(buf+11); - } else if (!strncmp(buf, "virtfs.gid", 10)) { - gid = atoi(buf+11); - } else if (!strncmp(buf, "virtfs.mode", 11)) { - mode = atoi(buf+12); - } else if (!strncmp(buf, "virtfs.rdev", 11)) { - rdev = atoi(buf+12); - } - memset(buf, 0, ATTR_MAX); - } - fclose(fp); - goto update_map_file; - -create_map_file: - ret = local_create_mapped_attr_dir(ctx, path); - if (ret < 0) { - goto err_out; - } - -update_map_file: - fp = local_fopen(attr_path, "w"); - if (!fp) { - ret = -1; - goto err_out; - } - - if (credp->fc_uid != -1) { - uid = credp->fc_uid; - } - if (credp->fc_gid != -1) { - gid = credp->fc_gid; - } - if (credp->fc_mode != -1) { - mode = credp->fc_mode; - } - if (credp->fc_rdev != -1) { - rdev = credp->fc_rdev; - } - - - if (uid != -1) { - fprintf(fp, "virtfs.uid=%d\n", uid); - } - if (gid != -1) { - fprintf(fp, "virtfs.gid=%d\n", gid); - } - if (mode != -1) { - fprintf(fp, "virtfs.mode=%d\n", mode); - } - if (rdev != -1) { - fprintf(fp, "virtfs.rdev=%d\n", rdev); - } - fclose(fp); - -err_out: - g_free(attr_path); - return ret; -} - -static int local_set_xattr(const char *path, FsCred *credp) -{ - int err; - - if (credp->fc_uid != -1) { - uint32_t tmp_uid = cpu_to_le32(credp->fc_uid); - err = setxattr(path, "user.virtfs.uid", &tmp_uid, sizeof(uid_t), 0); - if (err) { - return err; - } - } - if (credp->fc_gid != -1) { - uint32_t tmp_gid = cpu_to_le32(credp->fc_gid); - err = setxattr(path, "user.virtfs.gid", &tmp_gid, sizeof(gid_t), 0); - if (err) { - return err; - } - } - if (credp->fc_mode != -1) { - uint32_t tmp_mode = cpu_to_le32(credp->fc_mode); - err = setxattr(path, "user.virtfs.mode", &tmp_mode, sizeof(mode_t), 0); - if (err) { - return err; - } - } - if (credp->fc_rdev != -1) { - uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev); - err = setxattr(path, "user.virtfs.rdev", &tmp_rdev, sizeof(dev_t), 0); - if (err) { - return err; - } - } - return 0; -} - -static int local_post_create_passthrough(FsContext *fs_ctx, const char *path, - FsCred *credp) -{ - char *buffer; - - buffer = rpath(fs_ctx, path); - if (lchown(buffer, credp->fc_uid, credp->fc_gid) < 0) { - /* - * If we fail to change ownership and if we are - * using security model none. Ignore the error - */ - if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { - goto err; - } - } - - if (chmod(buffer, credp->fc_mode & 07777) < 0) { - goto err; - } - - g_free(buffer); - return 0; -err: - g_free(buffer); - return -1; -} - -static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, - char *buf, size_t bufsz) -{ - ssize_t tsize = -1; - char *buffer; - char *path = fs_path->data; - - if ((fs_ctx->export_flags & V9FS_SM_MAPPED) || - (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) { - int fd; - buffer = rpath(fs_ctx, path); - fd = open(buffer, O_RDONLY | O_NOFOLLOW); - g_free(buffer); - if (fd == -1) { - return -1; - } - do { - tsize = read(fd, (void *)buf, bufsz); - } while (tsize == -1 && errno == EINTR); - close(fd); - } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || - (fs_ctx->export_flags & V9FS_SM_NONE)) { - buffer = rpath(fs_ctx, path); - tsize = readlink(buffer, buf, bufsz); - g_free(buffer); - } - return tsize; -} - -static int local_close(FsContext *ctx, V9fsFidOpenState *fs) -{ - return close(fs->fd); -} - -static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) -{ - return closedir(fs->dir); -} - -static int local_open(FsContext *ctx, V9fsPath *fs_path, - int flags, V9fsFidOpenState *fs) -{ - char *buffer; - char *path = fs_path->data; - - buffer = rpath(ctx, path); - fs->fd = open(buffer, flags | O_NOFOLLOW); - g_free(buffer); - return fs->fd; -} - -static int local_opendir(FsContext *ctx, - V9fsPath *fs_path, V9fsFidOpenState *fs) -{ - char *buffer; - char *path = fs_path->data; - - buffer = rpath(ctx, path); - fs->dir = opendir(buffer); - g_free(buffer); - if (!fs->dir) { - return -1; - } - return 0; -} - -static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) -{ - rewinddir(fs->dir); -} - -static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) -{ - return telldir(fs->dir); -} - -static int local_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, - struct dirent *entry, - struct dirent **result) -{ - int ret; - -again: - ret = readdir_r(fs->dir, entry, result); - if (ctx->export_flags & V9FS_SM_MAPPED) { - entry->d_type = DT_UNKNOWN; - } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { - if (!ret && *result != NULL && - !strcmp(entry->d_name, VIRTFS_META_DIR)) { - /* skp the meta data directory */ - goto again; - } - entry->d_type = DT_UNKNOWN; - } - return ret; -} - -static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) -{ - seekdir(fs->dir, off); -} - -static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ -#ifdef CONFIG_PREADV - return preadv(fs->fd, iov, iovcnt, offset); -#else - int err = lseek(fs->fd, offset, SEEK_SET); - if (err == -1) { - return err; - } else { - return readv(fs->fd, iov, iovcnt); - } -#endif -} - -static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ - ssize_t ret -; -#ifdef CONFIG_PREADV - ret = pwritev(fs->fd, iov, iovcnt, offset); -#else - int err = lseek(fs->fd, offset, SEEK_SET); - if (err == -1) { - return err; - } else { - ret = writev(fs->fd, iov, iovcnt); - } -#endif -#ifdef CONFIG_SYNC_FILE_RANGE - if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { - /* - * Initiate a writeback. This is not a data integrity sync. - * We want to ensure that we don't leave dirty pages in the cache - * after write when writeout=immediate is sepcified. - */ - sync_file_range(fs->fd, offset, ret, - SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); - } -#endif - return ret; -} - -static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) -{ - char *buffer; - int ret = -1; - char *path = fs_path->data; - - if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - buffer = rpath(fs_ctx, path); - ret = local_set_xattr(buffer, credp); - g_free(buffer); - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - return local_set_mapped_file_attr(fs_ctx, path, credp); - } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || - (fs_ctx->export_flags & V9FS_SM_NONE)) { - buffer = rpath(fs_ctx, path); - ret = chmod(buffer, credp->fc_mode); - g_free(buffer); - } - return ret; -} - -static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, - const char *name, FsCred *credp) -{ - char *path; - int err = -1; - int serrno = 0; - V9fsString fullname; - char *buffer = NULL; - - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - path = fullname.data; - - /* Determine the security model */ - if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - buffer = rpath(fs_ctx, path); - err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0); - if (err == -1) { - goto out; - } - err = local_set_xattr(buffer, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - - buffer = rpath(fs_ctx, path); - err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0); - if (err == -1) { - goto out; - } - err = local_set_mapped_file_attr(fs_ctx, path, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || - (fs_ctx->export_flags & V9FS_SM_NONE)) { - buffer = rpath(fs_ctx, path); - err = mknod(buffer, credp->fc_mode, credp->fc_rdev); - if (err == -1) { - goto out; - } - err = local_post_create_passthrough(fs_ctx, path, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } - goto out; - -err_end: - remove(buffer); - errno = serrno; -out: - g_free(buffer); - v9fs_string_free(&fullname); - return err; -} - -static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, - const char *name, FsCred *credp) -{ - char *path; - int err = -1; - int serrno = 0; - V9fsString fullname; - char *buffer = NULL; - - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - path = fullname.data; - - /* Determine the security model */ - if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - buffer = rpath(fs_ctx, path); - err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS); - if (err == -1) { - goto out; - } - credp->fc_mode = credp->fc_mode|S_IFDIR; - err = local_set_xattr(buffer, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - buffer = rpath(fs_ctx, path); - err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS); - if (err == -1) { - goto out; - } - credp->fc_mode = credp->fc_mode|S_IFDIR; - err = local_set_mapped_file_attr(fs_ctx, path, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || - (fs_ctx->export_flags & V9FS_SM_NONE)) { - buffer = rpath(fs_ctx, path); - err = mkdir(buffer, credp->fc_mode); - if (err == -1) { - goto out; - } - err = local_post_create_passthrough(fs_ctx, path, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } - goto out; - -err_end: - remove(buffer); - errno = serrno; -out: - g_free(buffer); - v9fs_string_free(&fullname); - return err; -} - -static int local_fstat(FsContext *fs_ctx, int fid_type, - V9fsFidOpenState *fs, struct stat *stbuf) -{ - int err, fd; - - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); - } else { - fd = fs->fd; - } - - err = fstat(fd, stbuf); - if (err) { - return err; - } - if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - /* Actual credentials are part of extended attrs */ - uid_t tmp_uid; - gid_t tmp_gid; - mode_t tmp_mode; - dev_t tmp_dev; - - if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) { - stbuf->st_uid = le32_to_cpu(tmp_uid); - } - if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) { - stbuf->st_gid = le32_to_cpu(tmp_gid); - } - if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) { - stbuf->st_mode = le32_to_cpu(tmp_mode); - } - if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) { - stbuf->st_rdev = le64_to_cpu(tmp_dev); - } - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - errno = EOPNOTSUPP; - return -1; - } - return err; -} - -static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, - int flags, FsCred *credp, V9fsFidOpenState *fs) -{ - char *path; - int fd = -1; - int err = -1; - int serrno = 0; - V9fsString fullname; - char *buffer = NULL; - - /* - * Mark all the open to not follow symlinks - */ - flags |= O_NOFOLLOW; - - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - path = fullname.data; - - /* Determine the security model */ - if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - buffer = rpath(fs_ctx, path); - fd = open(buffer, flags, SM_LOCAL_MODE_BITS); - if (fd == -1) { - err = fd; - goto out; - } - credp->fc_mode = credp->fc_mode|S_IFREG; - /* Set cleint credentials in xattr */ - err = local_set_xattr(buffer, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - buffer = rpath(fs_ctx, path); - fd = open(buffer, flags, SM_LOCAL_MODE_BITS); - if (fd == -1) { - err = fd; - goto out; - } - credp->fc_mode = credp->fc_mode|S_IFREG; - /* Set client credentials in .virtfs_metadata directory files */ - err = local_set_mapped_file_attr(fs_ctx, path, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || - (fs_ctx->export_flags & V9FS_SM_NONE)) { - buffer = rpath(fs_ctx, path); - fd = open(buffer, flags, credp->fc_mode); - if (fd == -1) { - err = fd; - goto out; - } - err = local_post_create_passthrough(fs_ctx, path, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } - err = fd; - fs->fd = fd; - goto out; - -err_end: - close(fd); - remove(buffer); - errno = serrno; -out: - g_free(buffer); - v9fs_string_free(&fullname); - return err; -} - - -static int local_symlink(FsContext *fs_ctx, const char *oldpath, - V9fsPath *dir_path, const char *name, FsCred *credp) -{ - int err = -1; - int serrno = 0; - char *newpath; - V9fsString fullname; - char *buffer = NULL; - - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - newpath = fullname.data; - - /* Determine the security model */ - if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - int fd; - ssize_t oldpath_size, write_size; - buffer = rpath(fs_ctx, newpath); - fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS); - if (fd == -1) { - err = fd; - goto out; - } - /* Write the oldpath (target) to the file. */ - oldpath_size = strlen(oldpath); - do { - write_size = write(fd, (void *)oldpath, oldpath_size); - } while (write_size == -1 && errno == EINTR); - - if (write_size != oldpath_size) { - serrno = errno; - close(fd); - err = -1; - goto err_end; - } - close(fd); - /* Set cleint credentials in symlink's xattr */ - credp->fc_mode = credp->fc_mode|S_IFLNK; - err = local_set_xattr(buffer, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - int fd; - ssize_t oldpath_size, write_size; - buffer = rpath(fs_ctx, newpath); - fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS); - if (fd == -1) { - err = fd; - goto out; - } - /* Write the oldpath (target) to the file. */ - oldpath_size = strlen(oldpath); - do { - write_size = write(fd, (void *)oldpath, oldpath_size); - } while (write_size == -1 && errno == EINTR); - - if (write_size != oldpath_size) { - serrno = errno; - close(fd); - err = -1; - goto err_end; - } - close(fd); - /* Set cleint credentials in symlink's xattr */ - credp->fc_mode = credp->fc_mode|S_IFLNK; - err = local_set_mapped_file_attr(fs_ctx, newpath, credp); - if (err == -1) { - serrno = errno; - goto err_end; - } - } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || - (fs_ctx->export_flags & V9FS_SM_NONE)) { - buffer = rpath(fs_ctx, newpath); - err = symlink(oldpath, buffer); - if (err) { - goto out; - } - err = lchown(buffer, credp->fc_uid, credp->fc_gid); - if (err == -1) { - /* - * If we fail to change ownership and if we are - * using security model none. Ignore the error - */ - if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { - serrno = errno; - goto err_end; - } else - err = 0; - } - } - goto out; - -err_end: - remove(buffer); - errno = serrno; -out: - g_free(buffer); - v9fs_string_free(&fullname); - return err; -} - -static int local_link(FsContext *ctx, V9fsPath *oldpath, - V9fsPath *dirpath, const char *name) -{ - int ret; - V9fsString newpath; - char *buffer, *buffer1; - - v9fs_string_init(&newpath); - v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name); - - buffer = rpath(ctx, oldpath->data); - buffer1 = rpath(ctx, newpath.data); - ret = link(buffer, buffer1); - g_free(buffer); - g_free(buffer1); - - /* now link the virtfs_metadata files */ - if (!ret && (ctx->export_flags & V9FS_SM_MAPPED_FILE)) { - /* Link the .virtfs_metadata files. Create the metada directory */ - ret = local_create_mapped_attr_dir(ctx, newpath.data); - if (ret < 0) { - goto err_out; - } - buffer = local_mapped_attr_path(ctx, oldpath->data); - buffer1 = local_mapped_attr_path(ctx, newpath.data); - ret = link(buffer, buffer1); - g_free(buffer); - g_free(buffer1); - if (ret < 0 && errno != ENOENT) { - goto err_out; - } - } -err_out: - v9fs_string_free(&newpath); - return ret; -} - -static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) -{ - char *buffer; - int ret; - char *path = fs_path->data; - - buffer = rpath(ctx, path); - ret = truncate(buffer, size); - g_free(buffer); - return ret; -} - -static int local_rename(FsContext *ctx, const char *oldpath, - const char *newpath) -{ - int err; - char *buffer, *buffer1; - - if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { - err = local_create_mapped_attr_dir(ctx, newpath); - if (err < 0) { - return err; - } - /* rename the .virtfs_metadata files */ - buffer = local_mapped_attr_path(ctx, oldpath); - buffer1 = local_mapped_attr_path(ctx, newpath); - err = rename(buffer, buffer1); - g_free(buffer); - g_free(buffer1); - if (err < 0 && errno != ENOENT) { - return err; - } - } - - buffer = rpath(ctx, oldpath); - buffer1 = rpath(ctx, newpath); - err = rename(buffer, buffer1); - g_free(buffer); - g_free(buffer1); - return err; -} - -static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) -{ - char *buffer; - int ret = -1; - char *path = fs_path->data; - - if ((credp->fc_uid == -1 && credp->fc_gid == -1) || - (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || - (fs_ctx->export_flags & V9FS_SM_NONE)) { - buffer = rpath(fs_ctx, path); - ret = lchown(buffer, credp->fc_uid, credp->fc_gid); - g_free(buffer); - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { - buffer = rpath(fs_ctx, path); - ret = local_set_xattr(buffer, credp); - g_free(buffer); - } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { - return local_set_mapped_file_attr(fs_ctx, path, credp); - } - return ret; -} - -static int local_utimensat(FsContext *s, V9fsPath *fs_path, - const struct timespec *buf) -{ - char *buffer; - int ret; - char *path = fs_path->data; - - buffer = rpath(s, path); - ret = qemu_utimens(buffer, buf); - g_free(buffer); - return ret; -} - -static int local_remove(FsContext *ctx, const char *path) -{ - int err; - struct stat stbuf; - char *buffer; - - if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { - buffer = rpath(ctx, path); - err = lstat(buffer, &stbuf); - g_free(buffer); - if (err) { - goto err_out; - } - /* - * If directory remove .virtfs_metadata contained in the - * directory - */ - if (S_ISDIR(stbuf.st_mode)) { - buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root, - path, VIRTFS_META_DIR); - err = remove(buffer); - g_free(buffer); - if (err < 0 && errno != ENOENT) { - /* - * We didn't had the .virtfs_metadata file. May be file created - * in non-mapped mode ?. Ignore ENOENT. - */ - goto err_out; - } - } - /* - * Now remove the name from parent directory - * .virtfs_metadata directory - */ - buffer = local_mapped_attr_path(ctx, path); - err = remove(buffer); - g_free(buffer); - if (err < 0 && errno != ENOENT) { - /* - * We didn't had the .virtfs_metadata file. May be file created - * in non-mapped mode ?. Ignore ENOENT. - */ - goto err_out; - } - } - - buffer = rpath(ctx, path); - err = remove(buffer); - g_free(buffer); -err_out: - return err; -} - -static int local_fsync(FsContext *ctx, int fid_type, - V9fsFidOpenState *fs, int datasync) -{ - int fd; - - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); - } else { - fd = fs->fd; - } - - if (datasync) { - return qemu_fdatasync(fd); - } else { - return fsync(fd); - } -} - -static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) -{ - char *buffer; - int ret; - char *path = fs_path->data; - - buffer = rpath(s, path); - ret = statfs(buffer, stbuf); - g_free(buffer); - return ret; -} - -static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, - const char *name, void *value, size_t size) -{ - char *path = fs_path->data; - - return v9fs_get_xattr(ctx, path, name, value, size); -} - -static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, - void *value, size_t size) -{ - char *path = fs_path->data; - - return v9fs_list_xattr(ctx, path, value, size); -} - -static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, - void *value, size_t size, int flags) -{ - char *path = fs_path->data; - - return v9fs_set_xattr(ctx, path, name, value, size, flags); -} - -static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, - const char *name) -{ - char *path = fs_path->data; - - return v9fs_remove_xattr(ctx, path, name); -} - -static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, - const char *name, V9fsPath *target) -{ - if (dir_path) { - v9fs_string_sprintf((V9fsString *)target, "%s/%s", - dir_path->data, name); - } else { - v9fs_string_sprintf((V9fsString *)target, "%s", name); - } - /* Bump the size for including terminating NULL */ - target->size++; - return 0; -} - -static int local_renameat(FsContext *ctx, V9fsPath *olddir, - const char *old_name, V9fsPath *newdir, - const char *new_name) -{ - int ret; - V9fsString old_full_name, new_full_name; - - v9fs_string_init(&old_full_name); - v9fs_string_init(&new_full_name); - - v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name); - v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name); - - ret = local_rename(ctx, old_full_name.data, new_full_name.data); - v9fs_string_free(&old_full_name); - v9fs_string_free(&new_full_name); - return ret; -} - -static int local_unlinkat(FsContext *ctx, V9fsPath *dir, - const char *name, int flags) -{ - int ret; - V9fsString fullname; - char *buffer; - - v9fs_string_init(&fullname); - - v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name); - if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { - if (flags == AT_REMOVEDIR) { - /* - * If directory remove .virtfs_metadata contained in the - * directory - */ - buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root, - fullname.data, VIRTFS_META_DIR); - ret = remove(buffer); - g_free(buffer); - if (ret < 0 && errno != ENOENT) { - /* - * We didn't had the .virtfs_metadata file. May be file created - * in non-mapped mode ?. Ignore ENOENT. - */ - goto err_out; - } - } - /* - * Now remove the name from parent directory - * .virtfs_metadata directory. - */ - buffer = local_mapped_attr_path(ctx, fullname.data); - ret = remove(buffer); - g_free(buffer); - if (ret < 0 && errno != ENOENT) { - /* - * We didn't had the .virtfs_metadata file. May be file created - * in non-mapped mode ?. Ignore ENOENT. - */ - goto err_out; - } - } - /* Remove the name finally */ - buffer = rpath(ctx, fullname.data); - ret = remove(buffer); - g_free(buffer); - -err_out: - v9fs_string_free(&fullname); - return ret; -} - -static int local_ioc_getversion(FsContext *ctx, V9fsPath *path, - mode_t st_mode, uint64_t *st_gen) -{ -#ifdef FS_IOC_GETVERSION - int err; - V9fsFidOpenState fid_open; - - /* - * Do not try to open special files like device nodes, fifos etc - * We can get fd for regular files and directories only - */ - if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { - errno = ENOTTY; - return -1; - } - err = local_open(ctx, path, O_RDONLY, &fid_open); - if (err < 0) { - return err; - } - err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); - local_close(ctx, &fid_open); - return err; -#else - errno = ENOTTY; - return -1; -#endif -} - -static int local_init(FsContext *ctx) -{ - int err = 0; - struct statfs stbuf; - - if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { - ctx->xops = passthrough_xattr_ops; - } else if (ctx->export_flags & V9FS_SM_MAPPED) { - ctx->xops = mapped_xattr_ops; - } else if (ctx->export_flags & V9FS_SM_NONE) { - ctx->xops = none_xattr_ops; - } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { - /* - * xattr operation for mapped-file and passthrough - * remain same. - */ - ctx->xops = passthrough_xattr_ops; - } - ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; -#ifdef FS_IOC_GETVERSION - /* - * use ioc_getversion only if the iocl is definied - */ - err = statfs(ctx->fs_root, &stbuf); - if (!err) { - switch (stbuf.f_type) { - case EXT2_SUPER_MAGIC: - case BTRFS_SUPER_MAGIC: - case REISERFS_SUPER_MAGIC: - case XFS_SUPER_MAGIC: - ctx->exops.get_st_gen = local_ioc_getversion; - break; - } - } -#endif - return err; -} - -static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse) -{ - const char *sec_model = qemu_opt_get(opts, "security_model"); - const char *path = qemu_opt_get(opts, "path"); - - if (!sec_model) { - 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; - } - - if (!strcmp(sec_model, "passthrough")) { - fse->export_flags |= V9FS_SM_PASSTHROUGH; - } else if (!strcmp(sec_model, "mapped") || - !strcmp(sec_model, "mapped-xattr")) { - fse->export_flags |= V9FS_SM_MAPPED; - } else if (!strcmp(sec_model, "none")) { - fse->export_flags |= V9FS_SM_NONE; - } else if (!strcmp(sec_model, "mapped-file")) { - fse->export_flags |= V9FS_SM_MAPPED_FILE; - } else { - 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) { - error_report("fsdev: No path specified"); - return -1; - } - fse->path = g_strdup(path); - - return 0; -} - -FileOperations local_ops = { - .parse_opts = local_parse_opts, - .init = local_init, - .lstat = local_lstat, - .readlink = local_readlink, - .close = local_close, - .closedir = local_closedir, - .open = local_open, - .opendir = local_opendir, - .rewinddir = local_rewinddir, - .telldir = local_telldir, - .readdir_r = local_readdir_r, - .seekdir = local_seekdir, - .preadv = local_preadv, - .pwritev = local_pwritev, - .chmod = local_chmod, - .mknod = local_mknod, - .mkdir = local_mkdir, - .fstat = local_fstat, - .open2 = local_open2, - .symlink = local_symlink, - .link = local_link, - .truncate = local_truncate, - .rename = local_rename, - .chown = local_chown, - .utimensat = local_utimensat, - .remove = local_remove, - .fsync = local_fsync, - .statfs = local_statfs, - .lgetxattr = local_lgetxattr, - .llistxattr = local_llistxattr, - .lsetxattr = local_lsetxattr, - .lremovexattr = local_lremovexattr, - .name_to_path = local_name_to_path, - .renameat = local_renameat, - .unlinkat = local_unlinkat, -}; diff --git a/qemu/hw/9pfs/9p-posix-acl.c b/qemu/hw/9pfs/9p-posix-acl.c deleted file mode 100644 index ec003181c..000000000 --- a/qemu/hw/9pfs/9p-posix-acl.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * 9p system.posix* xattr callback - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Aneesh Kumar K.V - * - * 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/xattr.h" -#include "9p.h" -#include "fsdev/file-op-9p.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" -#define ACL_ACCESS "system.posix_acl_access" -#define ACL_DEFAULT "system.posix_acl_default" - -static ssize_t mp_pacl_getxattr(FsContext *ctx, const char *path, - const char *name, void *value, size_t size) -{ - char *buffer; - ssize_t ret; - - buffer = rpath(ctx, path); - ret = lgetxattr(buffer, MAP_ACL_ACCESS, value, size); - g_free(buffer); - return ret; -} - -static ssize_t mp_pacl_listxattr(FsContext *ctx, const char *path, - char *name, void *value, size_t osize) -{ - ssize_t len = sizeof(ACL_ACCESS); - - if (!value) { - return len; - } - - if (osize < len) { - errno = ERANGE; - return -1; - } - - /* len includes the trailing NUL */ - memcpy(value, ACL_ACCESS, len); - return 0; -} - -static int mp_pacl_setxattr(FsContext *ctx, const char *path, const char *name, - void *value, size_t size, int flags) -{ - char *buffer; - int ret; - - buffer = rpath(ctx, path); - ret = lsetxattr(buffer, MAP_ACL_ACCESS, value, size, flags); - g_free(buffer); - return ret; -} - -static int mp_pacl_removexattr(FsContext *ctx, - const char *path, const char *name) -{ - int ret; - char *buffer; - - buffer = rpath(ctx, path); - ret = lremovexattr(buffer, MAP_ACL_ACCESS); - if (ret == -1 && errno == ENODATA) { - /* - * We don't get ENODATA error when trying to remove a - * posix acl that is not present. So don't throw the error - * even in case of mapped security model - */ - errno = 0; - ret = 0; - } - g_free(buffer); - return ret; -} - -static ssize_t mp_dacl_getxattr(FsContext *ctx, const char *path, - const char *name, void *value, size_t size) -{ - char *buffer; - ssize_t ret; - - buffer = rpath(ctx, path); - ret = lgetxattr(buffer, MAP_ACL_DEFAULT, value, size); - g_free(buffer); - return ret; -} - -static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path, - char *name, void *value, size_t osize) -{ - ssize_t len = sizeof(ACL_DEFAULT); - - if (!value) { - return len; - } - - if (osize < len) { - errno = ERANGE; - return -1; - } - - /* len includes the trailing NUL */ - memcpy(value, ACL_DEFAULT, len); - return 0; -} - -static int mp_dacl_setxattr(FsContext *ctx, const char *path, const char *name, - void *value, size_t size, int flags) -{ - char *buffer; - int ret; - - buffer = rpath(ctx, path); - ret = lsetxattr(buffer, MAP_ACL_DEFAULT, value, size, flags); - g_free(buffer); - return ret; -} - -static int mp_dacl_removexattr(FsContext *ctx, - const char *path, const char *name) -{ - int ret; - char *buffer; - - buffer = rpath(ctx, path); - ret = lremovexattr(buffer, MAP_ACL_DEFAULT); - if (ret == -1 && errno == ENODATA) { - /* - * We don't get ENODATA error when trying to remove a - * posix acl that is not present. So don't throw the error - * even in case of mapped security model - */ - errno = 0; - ret = 0; - } - g_free(buffer); - return ret; -} - - -XattrOperations mapped_pacl_xattr = { - .name = "system.posix_acl_access", - .getxattr = mp_pacl_getxattr, - .setxattr = mp_pacl_setxattr, - .listxattr = mp_pacl_listxattr, - .removexattr = mp_pacl_removexattr, -}; - -XattrOperations mapped_dacl_xattr = { - .name = "system.posix_acl_default", - .getxattr = mp_dacl_getxattr, - .setxattr = mp_dacl_setxattr, - .listxattr = mp_dacl_listxattr, - .removexattr = mp_dacl_removexattr, -}; - -XattrOperations passthrough_acl_xattr = { - .name = "system.posix_acl_", - .getxattr = pt_getxattr, - .setxattr = pt_setxattr, - .listxattr = pt_listxattr, - .removexattr = pt_removexattr, -}; - -XattrOperations none_acl_xattr = { - .name = "system.posix_acl_", - .getxattr = notsup_getxattr, - .setxattr = notsup_setxattr, - .listxattr = notsup_listxattr, - .removexattr = notsup_removexattr, -}; diff --git a/qemu/hw/9pfs/9p-proxy.c b/qemu/hw/9pfs/9p-proxy.c deleted file mode 100644 index 00a4eb2a7..000000000 --- a/qemu/hw/9pfs/9p-proxy.c +++ /dev/null @@ -1,1220 +0,0 @@ -/* - * 9p Proxy callback - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * M. Mohan Kumar - * - * 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 -#include -#include "9p.h" -#include "qemu/cutils.h" -#include "qemu/error-report.h" -#include "fsdev/qemu-fsdev.h" -#include "9p-proxy.h" - -typedef struct V9fsProxy { - int sockfd; - QemuMutex mutex; - struct iovec in_iovec; - struct iovec out_iovec; -} V9fsProxy; - -/* - * Return received file descriptor on success in *status. - * errno is also returned on *status (which will be < 0) - * return < 0 on transport error. - */ -static int v9fs_receivefd(int sockfd, int *status) -{ - struct iovec iov; - struct msghdr msg; - struct cmsghdr *cmsg; - int retval, data, fd; - union MsgControl msg_control; - - iov.iov_base = &data; - iov.iov_len = sizeof(data); - - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = &msg_control; - msg.msg_controllen = sizeof(msg_control); - - do { - retval = recvmsg(sockfd, &msg, 0); - } while (retval < 0 && errno == EINTR); - if (retval <= 0) { - return retval; - } - /* - * data is set to V9FS_FD_VALID, if ancillary data is sent. If this - * request doesn't need ancillary data (fd) or an error occurred, - * data is set to negative errno value. - */ - if (data != V9FS_FD_VALID) { - *status = data; - return 0; - } - /* - * File descriptor (fd) is sent in the ancillary data. Check if we - * indeed received it. One of the reasons to fail to receive it is if - * we exceeded the maximum number of file descriptors! - */ - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || - cmsg->cmsg_level != SOL_SOCKET || - cmsg->cmsg_type != SCM_RIGHTS) { - continue; - } - fd = *((int *)CMSG_DATA(cmsg)); - *status = fd; - return 0; - } - *status = -ENFILE; /* Ancillary data sent but not received */ - return 0; -} - -static ssize_t socket_read(int sockfd, void *buff, size_t size) -{ - ssize_t retval, total = 0; - - while (size) { - retval = read(sockfd, buff, size); - if (retval == 0) { - return -EIO; - } - if (retval < 0) { - if (errno == EINTR) { - continue; - } - return -errno; - } - size -= retval; - buff += retval; - total += retval; - } - return total; -} - -/* Converts proxy_statfs to VFS statfs structure */ -static void prstatfs_to_statfs(struct statfs *stfs, ProxyStatFS *prstfs) -{ - memset(stfs, 0, sizeof(*stfs)); - stfs->f_type = prstfs->f_type; - stfs->f_bsize = prstfs->f_bsize; - stfs->f_blocks = prstfs->f_blocks; - stfs->f_bfree = prstfs->f_bfree; - stfs->f_bavail = prstfs->f_bavail; - stfs->f_files = prstfs->f_files; - stfs->f_ffree = prstfs->f_ffree; - stfs->f_fsid.__val[0] = prstfs->f_fsid[0] & 0xFFFFFFFFU; - stfs->f_fsid.__val[1] = prstfs->f_fsid[1] >> 32 & 0xFFFFFFFFU; - stfs->f_namelen = prstfs->f_namelen; - stfs->f_frsize = prstfs->f_frsize; -} - -/* Converts proxy_stat structure to VFS stat structure */ -static void prstat_to_stat(struct stat *stbuf, ProxyStat *prstat) -{ - memset(stbuf, 0, sizeof(*stbuf)); - stbuf->st_dev = prstat->st_dev; - stbuf->st_ino = prstat->st_ino; - stbuf->st_nlink = prstat->st_nlink; - stbuf->st_mode = prstat->st_mode; - stbuf->st_uid = prstat->st_uid; - stbuf->st_gid = prstat->st_gid; - stbuf->st_rdev = prstat->st_rdev; - stbuf->st_size = prstat->st_size; - stbuf->st_blksize = prstat->st_blksize; - stbuf->st_blocks = prstat->st_blocks; - stbuf->st_atim.tv_sec = prstat->st_atim_sec; - stbuf->st_atim.tv_nsec = prstat->st_atim_nsec; - stbuf->st_mtime = prstat->st_mtim_sec; - stbuf->st_mtim.tv_nsec = prstat->st_mtim_nsec; - stbuf->st_ctime = prstat->st_ctim_sec; - stbuf->st_ctim.tv_nsec = prstat->st_ctim_nsec; -} - -/* - * Response contains two parts - * {header, data} - * header.type == T_ERROR, data -> -errno - * header.type == T_SUCCESS, data -> response - * size of errno/response is given by header.size - * returns < 0, on transport error. response is - * valid only if status >= 0. - */ -static int v9fs_receive_response(V9fsProxy *proxy, int type, - int *status, void *response) -{ - int retval; - ProxyHeader header; - struct iovec *reply = &proxy->in_iovec; - - *status = 0; - reply->iov_len = 0; - retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ); - if (retval < 0) { - return retval; - } - reply->iov_len = PROXY_HDR_SZ; - proxy_unmarshal(reply, 0, "dd", &header.type, &header.size); - /* - * if response size > PROXY_MAX_IO_SZ, read the response but ignore it and - * return -ENOBUFS - */ - if (header.size > PROXY_MAX_IO_SZ) { - int count; - while (header.size > 0) { - count = MIN(PROXY_MAX_IO_SZ, header.size); - count = socket_read(proxy->sockfd, reply->iov_base, count); - if (count < 0) { - return count; - } - header.size -= count; - } - *status = -ENOBUFS; - return 0; - } - - retval = socket_read(proxy->sockfd, - reply->iov_base + PROXY_HDR_SZ, header.size); - if (retval < 0) { - return retval; - } - reply->iov_len += header.size; - /* there was an error during processing request */ - if (header.type == T_ERROR) { - int ret; - ret = proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status); - if (ret < 0) { - *status = ret; - } - return 0; - } - - switch (type) { - case T_LSTAT: { - ProxyStat prstat; - retval = proxy_unmarshal(reply, PROXY_HDR_SZ, - "qqqdddqqqqqqqqqq", &prstat.st_dev, - &prstat.st_ino, &prstat.st_nlink, - &prstat.st_mode, &prstat.st_uid, - &prstat.st_gid, &prstat.st_rdev, - &prstat.st_size, &prstat.st_blksize, - &prstat.st_blocks, - &prstat.st_atim_sec, &prstat.st_atim_nsec, - &prstat.st_mtim_sec, &prstat.st_mtim_nsec, - &prstat.st_ctim_sec, &prstat.st_ctim_nsec); - prstat_to_stat(response, &prstat); - break; - } - case T_STATFS: { - ProxyStatFS prstfs; - retval = proxy_unmarshal(reply, PROXY_HDR_SZ, - "qqqqqqqqqqq", &prstfs.f_type, - &prstfs.f_bsize, &prstfs.f_blocks, - &prstfs.f_bfree, &prstfs.f_bavail, - &prstfs.f_files, &prstfs.f_ffree, - &prstfs.f_fsid[0], &prstfs.f_fsid[1], - &prstfs.f_namelen, &prstfs.f_frsize); - prstatfs_to_statfs(response, &prstfs); - break; - } - case T_READLINK: { - V9fsString target; - v9fs_string_init(&target); - retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &target); - strcpy(response, target.data); - v9fs_string_free(&target); - break; - } - case T_LGETXATTR: - case T_LLISTXATTR: { - V9fsString xattr; - v9fs_string_init(&xattr); - retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &xattr); - memcpy(response, xattr.data, xattr.size); - v9fs_string_free(&xattr); - break; - } - case T_GETVERSION: - proxy_unmarshal(reply, PROXY_HDR_SZ, "q", response); - break; - default: - return -1; - } - if (retval < 0) { - *status = retval; - } - return 0; -} - -/* - * return < 0 on transport error. - * *status is valid only if return >= 0 - */ -static int v9fs_receive_status(V9fsProxy *proxy, - struct iovec *reply, int *status) -{ - int retval; - ProxyHeader header; - - *status = 0; - reply->iov_len = 0; - retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ); - if (retval < 0) { - return retval; - } - reply->iov_len = PROXY_HDR_SZ; - proxy_unmarshal(reply, 0, "dd", &header.type, &header.size); - if (header.size != sizeof(int)) { - *status = -ENOBUFS; - return 0; - } - retval = socket_read(proxy->sockfd, - reply->iov_base + PROXY_HDR_SZ, header.size); - if (retval < 0) { - return retval; - } - reply->iov_len += header.size; - proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status); - return 0; -} - -/* - * Proxy->header and proxy->request written to socket by QEMU process. - * This request read by proxy helper process - * returns 0 on success and -errno on error - */ -static int v9fs_request(V9fsProxy *proxy, int type, - void *response, const char *fmt, ...) -{ - dev_t rdev; - va_list ap; - int size = 0; - int retval = 0; - uint64_t offset; - ProxyHeader header = { 0, 0}; - struct timespec spec[2]; - int flags, mode, uid, gid; - V9fsString *name, *value; - V9fsString *path, *oldpath; - struct iovec *iovec = NULL, *reply = NULL; - - qemu_mutex_lock(&proxy->mutex); - - if (proxy->sockfd == -1) { - retval = -EIO; - goto err_out; - } - iovec = &proxy->out_iovec; - reply = &proxy->in_iovec; - va_start(ap, fmt); - switch (type) { - case T_OPEN: - path = va_arg(ap, V9fsString *); - flags = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, flags); - if (retval > 0) { - header.size = retval; - header.type = T_OPEN; - } - break; - case T_CREATE: - path = va_arg(ap, V9fsString *); - flags = va_arg(ap, int); - mode = va_arg(ap, int); - uid = va_arg(ap, int); - gid = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdddd", path, - flags, mode, uid, gid); - if (retval > 0) { - header.size = retval; - header.type = T_CREATE; - } - break; - case T_MKNOD: - path = va_arg(ap, V9fsString *); - mode = va_arg(ap, int); - rdev = va_arg(ap, long int); - uid = va_arg(ap, int); - gid = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsdq", - uid, gid, path, mode, rdev); - if (retval > 0) { - header.size = retval; - header.type = T_MKNOD; - } - break; - case T_MKDIR: - path = va_arg(ap, V9fsString *); - mode = va_arg(ap, int); - uid = va_arg(ap, int); - gid = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsd", - uid, gid, path, mode); - if (retval > 0) { - header.size = retval; - header.type = T_MKDIR; - } - break; - case T_SYMLINK: - oldpath = va_arg(ap, V9fsString *); - path = va_arg(ap, V9fsString *); - uid = va_arg(ap, int); - gid = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddss", - uid, gid, oldpath, path); - if (retval > 0) { - header.size = retval; - header.type = T_SYMLINK; - } - break; - case T_LINK: - oldpath = va_arg(ap, V9fsString *); - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", - oldpath, path); - if (retval > 0) { - header.size = retval; - header.type = T_LINK; - } - break; - case T_LSTAT: - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); - if (retval > 0) { - header.size = retval; - header.type = T_LSTAT; - } - break; - case T_READLINK: - path = va_arg(ap, V9fsString *); - size = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, size); - if (retval > 0) { - header.size = retval; - header.type = T_READLINK; - } - break; - case T_STATFS: - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); - if (retval > 0) { - header.size = retval; - header.type = T_STATFS; - } - break; - case T_CHMOD: - path = va_arg(ap, V9fsString *); - mode = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, mode); - if (retval > 0) { - header.size = retval; - header.type = T_CHMOD; - } - break; - case T_CHOWN: - path = va_arg(ap, V9fsString *); - uid = va_arg(ap, int); - gid = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdd", path, uid, gid); - if (retval > 0) { - header.size = retval; - header.type = T_CHOWN; - } - break; - case T_TRUNCATE: - path = va_arg(ap, V9fsString *); - offset = va_arg(ap, uint64_t); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sq", path, offset); - if (retval > 0) { - header.size = retval; - header.type = T_TRUNCATE; - } - break; - case T_UTIME: - path = va_arg(ap, V9fsString *); - spec[0].tv_sec = va_arg(ap, long); - spec[0].tv_nsec = va_arg(ap, long); - spec[1].tv_sec = va_arg(ap, long); - spec[1].tv_nsec = va_arg(ap, long); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sqqqq", path, - spec[0].tv_sec, spec[1].tv_nsec, - spec[1].tv_sec, spec[1].tv_nsec); - if (retval > 0) { - header.size = retval; - header.type = T_UTIME; - } - break; - case T_RENAME: - oldpath = va_arg(ap, V9fsString *); - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", oldpath, path); - if (retval > 0) { - header.size = retval; - header.type = T_RENAME; - } - break; - case T_REMOVE: - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); - if (retval > 0) { - header.size = retval; - header.type = T_REMOVE; - } - break; - case T_LGETXATTR: - size = va_arg(ap, int); - path = va_arg(ap, V9fsString *); - name = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, - "dss", size, path, name); - if (retval > 0) { - header.size = retval; - header.type = T_LGETXATTR; - } - break; - case T_LLISTXATTR: - size = va_arg(ap, int); - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ds", size, path); - if (retval > 0) { - header.size = retval; - header.type = T_LLISTXATTR; - } - break; - case T_LSETXATTR: - path = va_arg(ap, V9fsString *); - name = va_arg(ap, V9fsString *); - value = va_arg(ap, V9fsString *); - size = va_arg(ap, int); - flags = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sssdd", - path, name, value, size, flags); - if (retval > 0) { - header.size = retval; - header.type = T_LSETXATTR; - } - break; - case T_LREMOVEXATTR: - path = va_arg(ap, V9fsString *); - name = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", path, name); - if (retval > 0) { - header.size = retval; - header.type = T_LREMOVEXATTR; - } - break; - case T_GETVERSION: - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); - if (retval > 0) { - header.size = retval; - header.type = T_GETVERSION; - } - break; - default: - error_report("Invalid type %d", type); - retval = -EINVAL; - break; - } - va_end(ap); - - if (retval < 0) { - goto err_out; - } - - /* marshal the header details */ - proxy_marshal(iovec, 0, "dd", header.type, header.size); - header.size += PROXY_HDR_SZ; - - retval = qemu_write_full(proxy->sockfd, iovec->iov_base, header.size); - if (retval != header.size) { - goto close_error; - } - - switch (type) { - case T_OPEN: - case T_CREATE: - /* - * A file descriptor is returned as response for - * T_OPEN,T_CREATE on success - */ - if (v9fs_receivefd(proxy->sockfd, &retval) < 0) { - goto close_error; - } - break; - case T_MKNOD: - case T_MKDIR: - case T_SYMLINK: - case T_LINK: - case T_CHMOD: - case T_CHOWN: - case T_RENAME: - case T_TRUNCATE: - case T_UTIME: - case T_REMOVE: - case T_LSETXATTR: - case T_LREMOVEXATTR: - if (v9fs_receive_status(proxy, reply, &retval) < 0) { - goto close_error; - } - break; - case T_LSTAT: - case T_READLINK: - case T_STATFS: - case T_GETVERSION: - if (v9fs_receive_response(proxy, type, &retval, response) < 0) { - goto close_error; - } - break; - case T_LGETXATTR: - case T_LLISTXATTR: - if (!size) { - if (v9fs_receive_status(proxy, reply, &retval) < 0) { - goto close_error; - } - } else { - if (v9fs_receive_response(proxy, type, &retval, response) < 0) { - goto close_error; - } - } - break; - } - -err_out: - qemu_mutex_unlock(&proxy->mutex); - return retval; - -close_error: - close(proxy->sockfd); - proxy->sockfd = -1; - qemu_mutex_unlock(&proxy->mutex); - return -EIO; -} - -static int proxy_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf) -{ - int retval; - retval = v9fs_request(fs_ctx->private, T_LSTAT, stbuf, "s", fs_path); - if (retval < 0) { - errno = -retval; - return -1; - } - return retval; -} - -static ssize_t proxy_readlink(FsContext *fs_ctx, V9fsPath *fs_path, - char *buf, size_t bufsz) -{ - int retval; - retval = v9fs_request(fs_ctx->private, T_READLINK, buf, "sd", - fs_path, bufsz); - if (retval < 0) { - errno = -retval; - return -1; - } - return strlen(buf); -} - -static int proxy_close(FsContext *ctx, V9fsFidOpenState *fs) -{ - return close(fs->fd); -} - -static int proxy_closedir(FsContext *ctx, V9fsFidOpenState *fs) -{ - return closedir(fs->dir); -} - -static int proxy_open(FsContext *ctx, V9fsPath *fs_path, - int flags, V9fsFidOpenState *fs) -{ - fs->fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd", fs_path, flags); - if (fs->fd < 0) { - errno = -fs->fd; - fs->fd = -1; - } - return fs->fd; -} - -static int proxy_opendir(FsContext *ctx, - V9fsPath *fs_path, V9fsFidOpenState *fs) -{ - int serrno, fd; - - fs->dir = NULL; - fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd", fs_path, O_DIRECTORY); - if (fd < 0) { - errno = -fd; - return -1; - } - fs->dir = fdopendir(fd); - if (!fs->dir) { - serrno = errno; - close(fd); - errno = serrno; - return -1; - } - return 0; -} - -static void proxy_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) -{ - rewinddir(fs->dir); -} - -static off_t proxy_telldir(FsContext *ctx, V9fsFidOpenState *fs) -{ - return telldir(fs->dir); -} - -static int proxy_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, - struct dirent *entry, - struct dirent **result) -{ - return readdir_r(fs->dir, entry, result); -} - -static void proxy_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) -{ - seekdir(fs->dir, off); -} - -static ssize_t proxy_preadv(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ - ssize_t ret; -#ifdef CONFIG_PREADV - ret = preadv(fs->fd, iov, iovcnt, offset); -#else - ret = lseek(fs->fd, offset, SEEK_SET); - if (ret >= 0) { - ret = readv(fs->fd, iov, iovcnt); - } -#endif - return ret; -} - -static ssize_t proxy_pwritev(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ - ssize_t ret; - -#ifdef CONFIG_PREADV - ret = pwritev(fs->fd, iov, iovcnt, offset); -#else - ret = lseek(fs->fd, offset, SEEK_SET); - if (ret >= 0) { - ret = writev(fs->fd, iov, iovcnt); - } -#endif -#ifdef CONFIG_SYNC_FILE_RANGE - if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { - /* - * Initiate a writeback. This is not a data integrity sync. - * We want to ensure that we don't leave dirty pages in the cache - * after write when writeout=immediate is sepcified. - */ - sync_file_range(fs->fd, offset, ret, - SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); - } -#endif - return ret; -} - -static int proxy_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) -{ - int retval; - retval = v9fs_request(fs_ctx->private, T_CHMOD, NULL, "sd", - fs_path, credp->fc_mode); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_mknod(FsContext *fs_ctx, V9fsPath *dir_path, - const char *name, FsCred *credp) -{ - int retval; - V9fsString fullname; - - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - - retval = v9fs_request(fs_ctx->private, T_MKNOD, NULL, "sdqdd", - &fullname, credp->fc_mode, credp->fc_rdev, - credp->fc_uid, credp->fc_gid); - v9fs_string_free(&fullname); - if (retval < 0) { - errno = -retval; - retval = -1; - } - return retval; -} - -static int proxy_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, - const char *name, FsCred *credp) -{ - int retval; - V9fsString fullname; - - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - - retval = v9fs_request(fs_ctx->private, T_MKDIR, NULL, "sddd", &fullname, - credp->fc_mode, credp->fc_uid, credp->fc_gid); - v9fs_string_free(&fullname); - if (retval < 0) { - errno = -retval; - retval = -1; - } - v9fs_string_free(&fullname); - return retval; -} - -static int proxy_fstat(FsContext *fs_ctx, int fid_type, - V9fsFidOpenState *fs, struct stat *stbuf) -{ - int fd; - - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); - } else { - fd = fs->fd; - } - return fstat(fd, stbuf); -} - -static int proxy_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, - int flags, FsCred *credp, V9fsFidOpenState *fs) -{ - V9fsString fullname; - - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - - fs->fd = v9fs_request(fs_ctx->private, T_CREATE, NULL, "sdddd", - &fullname, flags, credp->fc_mode, - credp->fc_uid, credp->fc_gid); - v9fs_string_free(&fullname); - if (fs->fd < 0) { - errno = -fs->fd; - fs->fd = -1; - } - return fs->fd; -} - -static int proxy_symlink(FsContext *fs_ctx, const char *oldpath, - V9fsPath *dir_path, const char *name, FsCred *credp) -{ - int retval; - V9fsString fullname, target; - - v9fs_string_init(&fullname); - v9fs_string_init(&target); - - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - v9fs_string_sprintf(&target, "%s", oldpath); - - retval = v9fs_request(fs_ctx->private, T_SYMLINK, NULL, "ssdd", - &target, &fullname, credp->fc_uid, credp->fc_gid); - v9fs_string_free(&fullname); - v9fs_string_free(&target); - if (retval < 0) { - errno = -retval; - retval = -1; - } - return retval; -} - -static int proxy_link(FsContext *ctx, V9fsPath *oldpath, - V9fsPath *dirpath, const char *name) -{ - int retval; - V9fsString newpath; - - v9fs_string_init(&newpath); - v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name); - - retval = v9fs_request(ctx->private, T_LINK, NULL, "ss", oldpath, &newpath); - v9fs_string_free(&newpath); - if (retval < 0) { - errno = -retval; - retval = -1; - } - return retval; -} - -static int proxy_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) -{ - int retval; - - retval = v9fs_request(ctx->private, T_TRUNCATE, NULL, "sq", fs_path, size); - if (retval < 0) { - errno = -retval; - return -1; - } - return 0; -} - -static int proxy_rename(FsContext *ctx, const char *oldpath, - const char *newpath) -{ - int retval; - V9fsString oldname, newname; - - v9fs_string_init(&oldname); - v9fs_string_init(&newname); - - v9fs_string_sprintf(&oldname, "%s", oldpath); - v9fs_string_sprintf(&newname, "%s", newpath); - retval = v9fs_request(ctx->private, T_RENAME, NULL, "ss", - &oldname, &newname); - v9fs_string_free(&oldname); - v9fs_string_free(&newname); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) -{ - int retval; - retval = v9fs_request(fs_ctx->private, T_CHOWN, NULL, "sdd", - fs_path, credp->fc_uid, credp->fc_gid); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_utimensat(FsContext *s, V9fsPath *fs_path, - const struct timespec *buf) -{ - int retval; - retval = v9fs_request(s->private, T_UTIME, NULL, "sqqqq", - fs_path, - buf[0].tv_sec, buf[0].tv_nsec, - buf[1].tv_sec, buf[1].tv_nsec); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_remove(FsContext *ctx, const char *path) -{ - int retval; - V9fsString name; - v9fs_string_init(&name); - v9fs_string_sprintf(&name, "%s", path); - retval = v9fs_request(ctx->private, T_REMOVE, NULL, "s", &name); - v9fs_string_free(&name); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_fsync(FsContext *ctx, int fid_type, - V9fsFidOpenState *fs, int datasync) -{ - int fd; - - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir); - } else { - fd = fs->fd; - } - - if (datasync) { - return qemu_fdatasync(fd); - } else { - return fsync(fd); - } -} - -static int proxy_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) -{ - int retval; - retval = v9fs_request(s->private, T_STATFS, stbuf, "s", fs_path); - if (retval < 0) { - errno = -retval; - return -1; - } - return retval; -} - -static ssize_t proxy_lgetxattr(FsContext *ctx, V9fsPath *fs_path, - const char *name, void *value, size_t size) -{ - int retval; - V9fsString xname; - - v9fs_string_init(&xname); - v9fs_string_sprintf(&xname, "%s", name); - retval = v9fs_request(ctx->private, T_LGETXATTR, value, "dss", size, - fs_path, &xname); - v9fs_string_free(&xname); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static ssize_t proxy_llistxattr(FsContext *ctx, V9fsPath *fs_path, - void *value, size_t size) -{ - int retval; - retval = v9fs_request(ctx->private, T_LLISTXATTR, value, "ds", size, - fs_path); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, - void *value, size_t size, int flags) -{ - int retval; - V9fsString xname, xvalue; - - v9fs_string_init(&xname); - v9fs_string_sprintf(&xname, "%s", name); - - v9fs_string_init(&xvalue); - xvalue.size = size; - xvalue.data = g_malloc(size); - memcpy(xvalue.data, value, size); - - retval = v9fs_request(ctx->private, T_LSETXATTR, value, "sssdd", - fs_path, &xname, &xvalue, size, flags); - v9fs_string_free(&xname); - v9fs_string_free(&xvalue); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_lremovexattr(FsContext *ctx, V9fsPath *fs_path, - const char *name) -{ - int retval; - V9fsString xname; - - v9fs_string_init(&xname); - v9fs_string_sprintf(&xname, "%s", name); - retval = v9fs_request(ctx->private, T_LREMOVEXATTR, NULL, "ss", - fs_path, &xname); - v9fs_string_free(&xname); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_name_to_path(FsContext *ctx, V9fsPath *dir_path, - const char *name, V9fsPath *target) -{ - if (dir_path) { - v9fs_string_sprintf((V9fsString *)target, "%s/%s", - dir_path->data, name); - } else { - v9fs_string_sprintf((V9fsString *)target, "%s", name); - } - /* Bump the size for including terminating NULL */ - target->size++; - return 0; -} - -static int proxy_renameat(FsContext *ctx, V9fsPath *olddir, - const char *old_name, V9fsPath *newdir, - const char *new_name) -{ - int ret; - V9fsString old_full_name, new_full_name; - - v9fs_string_init(&old_full_name); - v9fs_string_init(&new_full_name); - - v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name); - v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name); - - ret = proxy_rename(ctx, old_full_name.data, new_full_name.data); - v9fs_string_free(&old_full_name); - v9fs_string_free(&new_full_name); - return ret; -} - -static int proxy_unlinkat(FsContext *ctx, V9fsPath *dir, - const char *name, int flags) -{ - int ret; - V9fsString fullname; - v9fs_string_init(&fullname); - - v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name); - ret = proxy_remove(ctx, fullname.data); - v9fs_string_free(&fullname); - - return ret; -} - -static int proxy_ioc_getversion(FsContext *fs_ctx, V9fsPath *path, - mode_t st_mode, uint64_t *st_gen) -{ - int err; - - /* Do not try to open special files like device nodes, fifos etc - * we can get fd for regular files and directories only - */ - if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { - errno = ENOTTY; - return -1; - } - err = v9fs_request(fs_ctx->private, T_GETVERSION, st_gen, "s", path); - if (err < 0) { - errno = -err; - err = -1; - } - return err; -} - -static int connect_namedsocket(const char *path) -{ - int sockfd, size; - struct sockaddr_un helper; - - if (strlen(path) >= sizeof(helper.sun_path)) { - error_report("Socket name too long"); - return -1; - } - sockfd = socket(AF_UNIX, SOCK_STREAM, 0); - if (sockfd < 0) { - 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) { - error_report("Failed to connect to %s: %s", path, strerror(errno)); - close(sockfd); - return -1; - } - - /* remove the socket for security reasons */ - unlink(path); - return sockfd; -} - -static int proxy_parse_opts(QemuOpts *opts, struct FsDriverEntry *fs) -{ - const char *socket = qemu_opt_get(opts, "socket"); - const char *sock_fd = qemu_opt_get(opts, "sock_fd"); - - if (!socket && !sock_fd) { - error_report("Must specify either socket or sock_fd"); - return -1; - } - if (socket && sock_fd) { - error_report("Both socket and sock_fd options specified"); - return -1; - } - if (socket) { - fs->path = g_strdup(socket); - fs->export_flags = V9FS_PROXY_SOCK_NAME; - } else { - fs->path = g_strdup(sock_fd); - fs->export_flags = V9FS_PROXY_SOCK_FD; - } - return 0; -} - -static int proxy_init(FsContext *ctx) -{ - V9fsProxy *proxy = g_malloc(sizeof(V9fsProxy)); - int sock_id; - - if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) { - sock_id = connect_namedsocket(ctx->fs_root); - } else { - sock_id = atoi(ctx->fs_root); - if (sock_id < 0) { - error_report("Socket descriptor not initialized"); - } - } - if (sock_id < 0) { - g_free(proxy); - return -1; - } - g_free(ctx->fs_root); - ctx->fs_root = NULL; - - proxy->in_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); - proxy->in_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; - proxy->out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); - proxy->out_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; - - ctx->private = proxy; - proxy->sockfd = sock_id; - qemu_mutex_init(&proxy->mutex); - - ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; - ctx->exops.get_st_gen = proxy_ioc_getversion; - return 0; -} - -FileOperations proxy_ops = { - .parse_opts = proxy_parse_opts, - .init = proxy_init, - .lstat = proxy_lstat, - .readlink = proxy_readlink, - .close = proxy_close, - .closedir = proxy_closedir, - .open = proxy_open, - .opendir = proxy_opendir, - .rewinddir = proxy_rewinddir, - .telldir = proxy_telldir, - .readdir_r = proxy_readdir_r, - .seekdir = proxy_seekdir, - .preadv = proxy_preadv, - .pwritev = proxy_pwritev, - .chmod = proxy_chmod, - .mknod = proxy_mknod, - .mkdir = proxy_mkdir, - .fstat = proxy_fstat, - .open2 = proxy_open2, - .symlink = proxy_symlink, - .link = proxy_link, - .truncate = proxy_truncate, - .rename = proxy_rename, - .chown = proxy_chown, - .utimensat = proxy_utimensat, - .remove = proxy_remove, - .fsync = proxy_fsync, - .statfs = proxy_statfs, - .lgetxattr = proxy_lgetxattr, - .llistxattr = proxy_llistxattr, - .lsetxattr = proxy_lsetxattr, - .lremovexattr = proxy_lremovexattr, - .name_to_path = proxy_name_to_path, - .renameat = proxy_renameat, - .unlinkat = proxy_unlinkat, -}; diff --git a/qemu/hw/9pfs/9p-proxy.h b/qemu/hw/9pfs/9p-proxy.h deleted file mode 100644 index ba9ca203d..000000000 --- a/qemu/hw/9pfs/9p-proxy.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 9p Proxy callback - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * M. Mohan Kumar - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - */ -#ifndef _QEMU_9P_PROXY_H -#define _QEMU_9P_PROXY_H - -#define PROXY_MAX_IO_SZ (64 * 1024) -#define V9FS_FD_VALID INT_MAX - -/* - * proxy iovec only support one element and - * marsha/unmarshal doesn't do little endian conversion. - */ -#define proxy_unmarshal(in_sg, offset, fmt, args...) \ - v9fs_iov_unmarshal(in_sg, 1, offset, 0, fmt, ##args) -#define proxy_marshal(out_sg, offset, fmt, args...) \ - v9fs_iov_marshal(out_sg, 1, offset, 0, fmt, ##args) - -union MsgControl { - struct cmsghdr cmsg; - char control[CMSG_SPACE(sizeof(int))]; -}; - -typedef struct { - uint32_t type; - uint32_t size; -} ProxyHeader; - -#define PROXY_HDR_SZ (sizeof(ProxyHeader)) - -enum { - T_SUCCESS = 0, - T_ERROR, - T_OPEN, - T_CREATE, - T_MKNOD, - T_MKDIR, - T_SYMLINK, - T_LINK, - T_LSTAT, - T_READLINK, - T_STATFS, - T_CHMOD, - T_CHOWN, - T_TRUNCATE, - T_UTIME, - T_RENAME, - T_REMOVE, - T_LGETXATTR, - T_LLISTXATTR, - T_LSETXATTR, - T_LREMOVEXATTR, - T_GETVERSION, -}; - -typedef struct { - uint64_t st_dev; - uint64_t st_ino; - uint64_t st_nlink; - uint32_t st_mode; - uint32_t st_uid; - uint32_t st_gid; - uint64_t st_rdev; - uint64_t st_size; - uint64_t st_blksize; - uint64_t st_blocks; - uint64_t st_atim_sec; - uint64_t st_atim_nsec; - uint64_t st_mtim_sec; - uint64_t st_mtim_nsec; - uint64_t st_ctim_sec; - uint64_t st_ctim_nsec; -} ProxyStat; - -typedef struct { - uint64_t f_type; - uint64_t f_bsize; - uint64_t f_blocks; - uint64_t f_bfree; - uint64_t f_bavail; - uint64_t f_files; - uint64_t f_ffree; - uint64_t f_fsid[2]; - uint64_t f_namelen; - uint64_t f_frsize; -} ProxyStatFS; -#endif diff --git a/qemu/hw/9pfs/9p-synth.c b/qemu/hw/9pfs/9p-synth.c deleted file mode 100644 index f1475dfd6..000000000 --- a/qemu/hw/9pfs/9p-synth.c +++ /dev/null @@ -1,574 +0,0 @@ -/* - * Virtio 9p synthetic file system support - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Malahal Naineni - * Aneesh Kumar K.V - * - * 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/virtio/virtio.h" -#include "9p.h" -#include "9p-xattr.h" -#include "fsdev/qemu-fsdev.h" -#include "9p-synth.h" -#include "qemu/rcu.h" -#include "qemu/rcu_queue.h" -#include "qemu/cutils.h" - -/* Root node for synth file system */ -static V9fsSynthNode v9fs_synth_root = { - .name = "/", - .actual_attr = { - .mode = 0555 | S_IFDIR, - .nlink = 1, - }, - .attr = &v9fs_synth_root.actual_attr, -}; - -static QemuMutex v9fs_synth_mutex; -static int v9fs_synth_node_count; -/* set to 1 when the synth fs is ready */ -static int v9fs_synth_fs; - -static V9fsSynthNode *v9fs_add_dir_node(V9fsSynthNode *parent, int mode, - const char *name, - V9fsSynthNodeAttr *attr, int inode) -{ - V9fsSynthNode *node; - - /* Add directory type and remove write bits */ - mode = ((mode & 0777) | S_IFDIR) & ~(S_IWUSR | S_IWGRP | S_IWOTH); - node = g_malloc0(sizeof(V9fsSynthNode)); - if (attr) { - /* We are adding .. or . entries */ - node->attr = attr; - node->attr->nlink++; - } else { - node->attr = &node->actual_attr; - node->attr->inode = inode; - node->attr->nlink = 1; - /* We don't allow write to directories */ - node->attr->mode = mode; - node->attr->write = NULL; - node->attr->read = NULL; - } - node->private = node; - pstrcpy(node->name, sizeof(node->name), name); - QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling); - return node; -} - -int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, - const char *name, V9fsSynthNode **result) -{ - int ret; - V9fsSynthNode *node, *tmp; - - if (!v9fs_synth_fs) { - return EAGAIN; - } - if (!name || (strlen(name) >= NAME_MAX)) { - return EINVAL; - } - if (!parent) { - parent = &v9fs_synth_root; - } - qemu_mutex_lock(&v9fs_synth_mutex); - QLIST_FOREACH(tmp, &parent->child, sibling) { - if (!strcmp(tmp->name, name)) { - ret = EEXIST; - goto err_out; - } - } - /* Add the name */ - node = v9fs_add_dir_node(parent, mode, name, NULL, v9fs_synth_node_count++); - v9fs_add_dir_node(node, parent->attr->mode, "..", - parent->attr, parent->attr->inode); - v9fs_add_dir_node(node, node->attr->mode, ".", - node->attr, node->attr->inode); - *result = node; - ret = 0; -err_out: - qemu_mutex_unlock(&v9fs_synth_mutex); - return ret; -} - -int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, - const char *name, v9fs_synth_read read, - v9fs_synth_write write, void *arg) -{ - int ret; - V9fsSynthNode *node, *tmp; - - if (!v9fs_synth_fs) { - return EAGAIN; - } - if (!name || (strlen(name) >= NAME_MAX)) { - return EINVAL; - } - if (!parent) { - parent = &v9fs_synth_root; - } - - qemu_mutex_lock(&v9fs_synth_mutex); - QLIST_FOREACH(tmp, &parent->child, sibling) { - if (!strcmp(tmp->name, name)) { - ret = EEXIST; - goto err_out; - } - } - /* Add file type and remove write bits */ - mode = ((mode & 0777) | S_IFREG); - node = g_malloc0(sizeof(V9fsSynthNode)); - node->attr = &node->actual_attr; - node->attr->inode = v9fs_synth_node_count++; - node->attr->nlink = 1; - node->attr->read = read; - node->attr->write = write; - node->attr->mode = mode; - node->private = arg; - pstrcpy(node->name, sizeof(node->name), name); - QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling); - ret = 0; -err_out: - qemu_mutex_unlock(&v9fs_synth_mutex); - return ret; -} - -static void v9fs_synth_fill_statbuf(V9fsSynthNode *node, struct stat *stbuf) -{ - stbuf->st_dev = 0; - stbuf->st_ino = node->attr->inode; - stbuf->st_mode = node->attr->mode; - stbuf->st_nlink = node->attr->nlink; - stbuf->st_uid = 0; - stbuf->st_gid = 0; - stbuf->st_rdev = 0; - stbuf->st_size = 0; - stbuf->st_blksize = 0; - stbuf->st_blocks = 0; - stbuf->st_atime = 0; - stbuf->st_mtime = 0; - stbuf->st_ctime = 0; -} - -static int v9fs_synth_lstat(FsContext *fs_ctx, - V9fsPath *fs_path, struct stat *stbuf) -{ - V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; - - v9fs_synth_fill_statbuf(node, stbuf); - return 0; -} - -static int v9fs_synth_fstat(FsContext *fs_ctx, int fid_type, - V9fsFidOpenState *fs, struct stat *stbuf) -{ - V9fsSynthOpenState *synth_open = fs->private; - v9fs_synth_fill_statbuf(synth_open->node, stbuf); - return 0; -} - -static int v9fs_synth_opendir(FsContext *ctx, - V9fsPath *fs_path, V9fsFidOpenState *fs) -{ - V9fsSynthOpenState *synth_open; - V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; - - synth_open = g_malloc(sizeof(*synth_open)); - synth_open->node = node; - node->open_count++; - fs->private = synth_open; - return 0; -} - -static int v9fs_synth_closedir(FsContext *ctx, V9fsFidOpenState *fs) -{ - V9fsSynthOpenState *synth_open = fs->private; - V9fsSynthNode *node = synth_open->node; - - node->open_count--; - g_free(synth_open); - fs->private = NULL; - return 0; -} - -static off_t v9fs_synth_telldir(FsContext *ctx, V9fsFidOpenState *fs) -{ - V9fsSynthOpenState *synth_open = fs->private; - return synth_open->offset; -} - -static void v9fs_synth_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) -{ - V9fsSynthOpenState *synth_open = fs->private; - synth_open->offset = off; -} - -static void v9fs_synth_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) -{ - v9fs_synth_seekdir(ctx, fs, 0); -} - -static void v9fs_synth_direntry(V9fsSynthNode *node, - struct dirent *entry, off_t off) -{ - strcpy(entry->d_name, node->name); - entry->d_ino = node->attr->inode; - entry->d_off = off + 1; -} - -static int v9fs_synth_get_dentry(V9fsSynthNode *dir, struct dirent *entry, - struct dirent **result, off_t off) -{ - int i = 0; - V9fsSynthNode *node; - - rcu_read_lock(); - QLIST_FOREACH(node, &dir->child, sibling) { - /* This is the off child of the directory */ - if (i == off) { - break; - } - i++; - } - rcu_read_unlock(); - if (!node) { - /* end of directory */ - *result = NULL; - return 0; - } - v9fs_synth_direntry(node, entry, off); - *result = entry; - return 0; -} - -static int v9fs_synth_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, - struct dirent *entry, struct dirent **result) -{ - int ret; - V9fsSynthOpenState *synth_open = fs->private; - V9fsSynthNode *node = synth_open->node; - ret = v9fs_synth_get_dentry(node, entry, result, synth_open->offset); - if (!ret && *result != NULL) { - synth_open->offset++; - } - return ret; -} - -static int v9fs_synth_open(FsContext *ctx, V9fsPath *fs_path, - int flags, V9fsFidOpenState *fs) -{ - V9fsSynthOpenState *synth_open; - V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data; - - synth_open = g_malloc(sizeof(*synth_open)); - synth_open->node = node; - node->open_count++; - fs->private = synth_open; - return 0; -} - -static int v9fs_synth_open2(FsContext *fs_ctx, V9fsPath *dir_path, - const char *name, int flags, - FsCred *credp, V9fsFidOpenState *fs) -{ - errno = ENOSYS; - return -1; -} - -static int v9fs_synth_close(FsContext *ctx, V9fsFidOpenState *fs) -{ - V9fsSynthOpenState *synth_open = fs->private; - V9fsSynthNode *node = synth_open->node; - - node->open_count--; - g_free(synth_open); - fs->private = NULL; - return 0; -} - -static ssize_t v9fs_synth_pwritev(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ - int i, count = 0, wcount; - V9fsSynthOpenState *synth_open = fs->private; - V9fsSynthNode *node = synth_open->node; - if (!node->attr->write) { - errno = EPERM; - return -1; - } - for (i = 0; i < iovcnt; i++) { - wcount = node->attr->write(iov[i].iov_base, iov[i].iov_len, - offset, node->private); - offset += wcount; - count += wcount; - /* If we wrote less than requested. we are done */ - if (wcount < iov[i].iov_len) { - break; - } - } - return count; -} - -static ssize_t v9fs_synth_preadv(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ - int i, count = 0, rcount; - V9fsSynthOpenState *synth_open = fs->private; - V9fsSynthNode *node = synth_open->node; - if (!node->attr->read) { - errno = EPERM; - return -1; - } - for (i = 0; i < iovcnt; i++) { - rcount = node->attr->read(iov[i].iov_base, iov[i].iov_len, - offset, node->private); - offset += rcount; - count += rcount; - /* If we read less than requested. we are done */ - if (rcount < iov[i].iov_len) { - break; - } - } - return count; -} - -static int v9fs_synth_truncate(FsContext *ctx, V9fsPath *path, off_t offset) -{ - errno = ENOSYS; - return -1; -} - -static int v9fs_synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_mknod(FsContext *fs_ctx, V9fsPath *path, - const char *buf, FsCred *credp) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_mkdir(FsContext *fs_ctx, V9fsPath *path, - const char *buf, FsCred *credp) -{ - errno = EPERM; - return -1; -} - -static ssize_t v9fs_synth_readlink(FsContext *fs_ctx, V9fsPath *path, - char *buf, size_t bufsz) -{ - errno = ENOSYS; - return -1; -} - -static int v9fs_synth_symlink(FsContext *fs_ctx, const char *oldpath, - V9fsPath *newpath, const char *buf, FsCred *credp) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_link(FsContext *fs_ctx, V9fsPath *oldpath, - V9fsPath *newpath, const char *buf) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_rename(FsContext *ctx, const char *oldpath, - const char *newpath) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_chown(FsContext *fs_ctx, V9fsPath *path, FsCred *credp) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_utimensat(FsContext *fs_ctx, V9fsPath *path, - const struct timespec *buf) -{ - errno = EPERM; - return 0; -} - -static int v9fs_synth_remove(FsContext *ctx, const char *path) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_fsync(FsContext *ctx, int fid_type, - V9fsFidOpenState *fs, int datasync) -{ - errno = ENOSYS; - return 0; -} - -static int v9fs_synth_statfs(FsContext *s, V9fsPath *fs_path, - struct statfs *stbuf) -{ - stbuf->f_type = 0xABCD; - stbuf->f_bsize = 512; - stbuf->f_blocks = 0; - stbuf->f_files = v9fs_synth_node_count; - stbuf->f_namelen = NAME_MAX; - return 0; -} - -static ssize_t v9fs_synth_lgetxattr(FsContext *ctx, V9fsPath *path, - const char *name, void *value, size_t size) -{ - errno = ENOTSUP; - return -1; -} - -static ssize_t v9fs_synth_llistxattr(FsContext *ctx, V9fsPath *path, - void *value, size_t size) -{ - errno = ENOTSUP; - return -1; -} - -static int v9fs_synth_lsetxattr(FsContext *ctx, V9fsPath *path, - const char *name, void *value, - size_t size, int flags) -{ - errno = ENOTSUP; - return -1; -} - -static int v9fs_synth_lremovexattr(FsContext *ctx, - V9fsPath *path, const char *name) -{ - errno = ENOTSUP; - return -1; -} - -static int v9fs_synth_name_to_path(FsContext *ctx, V9fsPath *dir_path, - const char *name, V9fsPath *target) -{ - V9fsSynthNode *node; - V9fsSynthNode *dir_node; - - /* "." and ".." are not allowed */ - if (!strcmp(name, ".") || !strcmp(name, "..")) { - errno = EINVAL; - return -1; - - } - if (!dir_path) { - dir_node = &v9fs_synth_root; - } else { - dir_node = *(V9fsSynthNode **)dir_path->data; - } - if (!strcmp(name, "/")) { - node = dir_node; - goto out; - } - /* search for the name in the childern */ - rcu_read_lock(); - QLIST_FOREACH(node, &dir_node->child, sibling) { - if (!strcmp(node->name, name)) { - break; - } - } - rcu_read_unlock(); - - if (!node) { - errno = ENOENT; - return -1; - } -out: - /* Copy the node pointer to fid */ - target->data = g_malloc(sizeof(void *)); - memcpy(target->data, &node, sizeof(void *)); - target->size = sizeof(void *); - return 0; -} - -static int v9fs_synth_renameat(FsContext *ctx, V9fsPath *olddir, - const char *old_name, V9fsPath *newdir, - const char *new_name) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_unlinkat(FsContext *ctx, V9fsPath *dir, - const char *name, int flags) -{ - errno = EPERM; - return -1; -} - -static int v9fs_synth_init(FsContext *ctx) -{ - QLIST_INIT(&v9fs_synth_root.child); - qemu_mutex_init(&v9fs_synth_mutex); - - /* Add "." and ".." entries for root */ - v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode, - "..", v9fs_synth_root.attr, v9fs_synth_root.attr->inode); - v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode, - ".", v9fs_synth_root.attr, v9fs_synth_root.attr->inode); - - /* Mark the subsystem is ready for use */ - v9fs_synth_fs = 1; - return 0; -} - -FileOperations synth_ops = { - .init = v9fs_synth_init, - .lstat = v9fs_synth_lstat, - .readlink = v9fs_synth_readlink, - .close = v9fs_synth_close, - .closedir = v9fs_synth_closedir, - .open = v9fs_synth_open, - .opendir = v9fs_synth_opendir, - .rewinddir = v9fs_synth_rewinddir, - .telldir = v9fs_synth_telldir, - .readdir_r = v9fs_synth_readdir_r, - .seekdir = v9fs_synth_seekdir, - .preadv = v9fs_synth_preadv, - .pwritev = v9fs_synth_pwritev, - .chmod = v9fs_synth_chmod, - .mknod = v9fs_synth_mknod, - .mkdir = v9fs_synth_mkdir, - .fstat = v9fs_synth_fstat, - .open2 = v9fs_synth_open2, - .symlink = v9fs_synth_symlink, - .link = v9fs_synth_link, - .truncate = v9fs_synth_truncate, - .rename = v9fs_synth_rename, - .chown = v9fs_synth_chown, - .utimensat = v9fs_synth_utimensat, - .remove = v9fs_synth_remove, - .fsync = v9fs_synth_fsync, - .statfs = v9fs_synth_statfs, - .lgetxattr = v9fs_synth_lgetxattr, - .llistxattr = v9fs_synth_llistxattr, - .lsetxattr = v9fs_synth_lsetxattr, - .lremovexattr = v9fs_synth_lremovexattr, - .name_to_path = v9fs_synth_name_to_path, - .renameat = v9fs_synth_renameat, - .unlinkat = v9fs_synth_unlinkat, -}; diff --git a/qemu/hw/9pfs/9p-synth.h b/qemu/hw/9pfs/9p-synth.h deleted file mode 100644 index 82962512a..000000000 --- a/qemu/hw/9pfs/9p-synth.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 9p - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Aneesh Kumar K.V - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ -#ifndef HW_9PFS_SYNTH_H -#define HW_9PFS_SYNTH_H 1 - - -typedef struct V9fsSynthNode V9fsSynthNode; -typedef ssize_t (*v9fs_synth_read)(void *buf, int len, off_t offset, - void *arg); -typedef ssize_t (*v9fs_synth_write)(void *buf, int len, off_t offset, - void *arg); -typedef struct V9fsSynthNodeAttr { - int mode; - int inode; - int nlink; - v9fs_synth_read read; - v9fs_synth_write write; -} V9fsSynthNodeAttr; - -struct V9fsSynthNode { - QLIST_HEAD(, V9fsSynthNode) child; - QLIST_ENTRY(V9fsSynthNode) sibling; - char name[NAME_MAX]; - V9fsSynthNodeAttr *attr; - V9fsSynthNodeAttr actual_attr; - void *private; - int open_count; -}; - -typedef struct V9fsSynthOpenState { - off_t offset; - V9fsSynthNode *node; -} V9fsSynthOpenState; - -extern int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, - const char *name, V9fsSynthNode **result); -extern int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, - const char *name, v9fs_synth_read read, - v9fs_synth_write write, void *arg); - -#endif diff --git a/qemu/hw/9pfs/9p-xattr-user.c b/qemu/hw/9pfs/9p-xattr-user.c deleted file mode 100644 index f87530c8b..000000000 --- a/qemu/hw/9pfs/9p-xattr-user.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 9p user. xattr callback - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Aneesh Kumar K.V - * - * 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 "9p.h" -#include "fsdev/file-op-9p.h" -#include "9p-xattr.h" - - -static ssize_t mp_user_getxattr(FsContext *ctx, const char *path, - const char *name, void *value, size_t size) -{ - char *buffer; - ssize_t ret; - - if (strncmp(name, "user.virtfs.", 12) == 0) { - /* - * Don't allow fetch of user.virtfs namesapce - * in case of mapped security - */ - errno = ENOATTR; - return -1; - } - buffer = rpath(ctx, path); - ret = lgetxattr(buffer, name, value, size); - g_free(buffer); - return ret; -} - -static ssize_t mp_user_listxattr(FsContext *ctx, const char *path, - char *name, void *value, size_t size) -{ - int name_size = strlen(name) + 1; - if (strncmp(name, "user.virtfs.", 12) == 0) { - - /* check if it is a mapped posix acl */ - if (strncmp(name, "user.virtfs.system.posix_acl_", 29) == 0) { - /* adjust the name and size */ - name += 12; - name_size -= 12; - } else { - /* - * Don't allow fetch of user.virtfs namesapce - * in case of mapped security - */ - return 0; - } - } - if (!value) { - return name_size; - } - - if (size < name_size) { - errno = ERANGE; - return -1; - } - - /* name_size includes the trailing NUL. */ - memcpy(value, name, name_size); - return name_size; -} - -static int mp_user_setxattr(FsContext *ctx, const char *path, const char *name, - void *value, size_t size, int flags) -{ - char *buffer; - int ret; - - if (strncmp(name, "user.virtfs.", 12) == 0) { - /* - * Don't allow fetch of user.virtfs namesapce - * in case of mapped security - */ - errno = EACCES; - return -1; - } - buffer = rpath(ctx, path); - ret = lsetxattr(buffer, name, value, size, flags); - g_free(buffer); - return ret; -} - -static int mp_user_removexattr(FsContext *ctx, - const char *path, const char *name) -{ - char *buffer; - int ret; - - if (strncmp(name, "user.virtfs.", 12) == 0) { - /* - * Don't allow fetch of user.virtfs namesapce - * in case of mapped security - */ - errno = EACCES; - return -1; - } - buffer = rpath(ctx, path); - ret = lremovexattr(buffer, name); - g_free(buffer); - return ret; -} - -XattrOperations mapped_user_xattr = { - .name = "user.", - .getxattr = mp_user_getxattr, - .setxattr = mp_user_setxattr, - .listxattr = mp_user_listxattr, - .removexattr = mp_user_removexattr, -}; - -XattrOperations passthrough_user_xattr = { - .name = "user.", - .getxattr = pt_getxattr, - .setxattr = pt_setxattr, - .listxattr = pt_listxattr, - .removexattr = pt_removexattr, -}; diff --git a/qemu/hw/9pfs/9p-xattr.c b/qemu/hw/9pfs/9p-xattr.c deleted file mode 100644 index 5d8595ed9..000000000 --- a/qemu/hw/9pfs/9p-xattr.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * 9p xattr callback - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Aneesh Kumar K.V - * - * 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 "9p.h" -#include "fsdev/file-op-9p.h" -#include "9p-xattr.h" - - -static XattrOperations *get_xattr_operations(XattrOperations **h, - const char *name) -{ - XattrOperations *xops; - for (xops = *(h)++; xops != NULL; xops = *(h)++) { - if (!strncmp(name, xops->name, strlen(xops->name))) { - return xops; - } - } - return NULL; -} - -ssize_t v9fs_get_xattr(FsContext *ctx, const char *path, - const char *name, void *value, size_t size) -{ - XattrOperations *xops = get_xattr_operations(ctx->xops, name); - if (xops) { - return xops->getxattr(ctx, path, name, value, size); - } - errno = EOPNOTSUPP; - return -1; -} - -ssize_t pt_listxattr(FsContext *ctx, const char *path, - char *name, void *value, size_t size) -{ - int name_size = strlen(name) + 1; - if (!value) { - return name_size; - } - - if (size < name_size) { - errno = ERANGE; - return -1; - } - - /* no need for strncpy: name_size is strlen(name)+1 */ - memcpy(value, name, name_size); - return name_size; -} - - -/* - * Get the list and pass to each layer to find out whether - * to send the data or not - */ -ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, - void *value, size_t vsize) -{ - ssize_t size = 0; - char *buffer; - void *ovalue = value; - XattrOperations *xops; - char *orig_value, *orig_value_start; - ssize_t xattr_len, parsed_len = 0, attr_len; - - /* Get the actual len */ - buffer = rpath(ctx, path); - xattr_len = llistxattr(buffer, value, 0); - if (xattr_len <= 0) { - g_free(buffer); - return xattr_len; - } - - /* Now fetch the xattr and find the actual size */ - orig_value = g_malloc(xattr_len); - xattr_len = llistxattr(buffer, orig_value, xattr_len); - g_free(buffer); - - /* store the orig pointer */ - orig_value_start = orig_value; - while (xattr_len > parsed_len) { - xops = get_xattr_operations(ctx->xops, orig_value); - if (!xops) { - goto next_entry; - } - - if (!value) { - size += xops->listxattr(ctx, path, orig_value, value, vsize); - } else { - size = xops->listxattr(ctx, path, orig_value, value, vsize); - if (size < 0) { - goto err_out; - } - value += size; - vsize -= size; - } -next_entry: - /* Got the next entry */ - attr_len = strlen(orig_value) + 1; - parsed_len += attr_len; - orig_value += attr_len; - } - if (value) { - size = value - ovalue; - } - -err_out: - g_free(orig_value_start); - return size; -} - -int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name, - void *value, size_t size, int flags) -{ - XattrOperations *xops = get_xattr_operations(ctx->xops, name); - if (xops) { - return xops->setxattr(ctx, path, name, value, size, flags); - } - errno = EOPNOTSUPP; - return -1; - -} - -int v9fs_remove_xattr(FsContext *ctx, - const char *path, const char *name) -{ - XattrOperations *xops = get_xattr_operations(ctx->xops, name); - if (xops) { - return xops->removexattr(ctx, path, name); - } - errno = EOPNOTSUPP; - return -1; - -} - -XattrOperations *mapped_xattr_ops[] = { - &mapped_user_xattr, - &mapped_pacl_xattr, - &mapped_dacl_xattr, - NULL, -}; - -XattrOperations *passthrough_xattr_ops[] = { - &passthrough_user_xattr, - &passthrough_acl_xattr, - NULL, -}; - -/* for .user none model should be same as passthrough */ -XattrOperations *none_xattr_ops[] = { - &passthrough_user_xattr, - &none_acl_xattr, - NULL, -}; diff --git a/qemu/hw/9pfs/9p-xattr.h b/qemu/hw/9pfs/9p-xattr.h deleted file mode 100644 index 4d39a2026..000000000 --- a/qemu/hw/9pfs/9p-xattr.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 9p - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Aneesh Kumar K.V - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ -#ifndef _QEMU_9P_XATTR_H -#define _QEMU_9P_XATTR_H - -#include "qemu/xattr.h" - -typedef struct xattr_operations -{ - const char *name; - ssize_t (*getxattr)(FsContext *ctx, const char *path, - const char *name, void *value, size_t size); - ssize_t (*listxattr)(FsContext *ctx, const char *path, - char *name, void *value, size_t size); - int (*setxattr)(FsContext *ctx, const char *path, const char *name, - void *value, size_t size, int flags); - int (*removexattr)(FsContext *ctx, - const char *path, const char *name); -} XattrOperations; - - -extern XattrOperations mapped_user_xattr; -extern XattrOperations passthrough_user_xattr; - -extern XattrOperations mapped_pacl_xattr; -extern XattrOperations mapped_dacl_xattr; -extern XattrOperations passthrough_acl_xattr; -extern XattrOperations none_acl_xattr; - -extern XattrOperations *mapped_xattr_ops[]; -extern XattrOperations *passthrough_xattr_ops[]; -extern XattrOperations *none_xattr_ops[]; - -ssize_t v9fs_get_xattr(FsContext *ctx, const char *path, const char *name, - void *value, size_t size); -ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, void *value, - size_t vsize); -int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name, - void *value, size_t size, int flags); -int v9fs_remove_xattr(FsContext *ctx, const char *path, const char *name); -ssize_t pt_listxattr(FsContext *ctx, const char *path, char *name, void *value, - size_t size); - -static inline ssize_t pt_getxattr(FsContext *ctx, const char *path, - const char *name, void *value, size_t size) -{ - char *buffer; - ssize_t ret; - - buffer = rpath(ctx, path); - ret = lgetxattr(buffer, name, value, size); - g_free(buffer); - return ret; -} - -static inline int pt_setxattr(FsContext *ctx, const char *path, - const char *name, void *value, - size_t size, int flags) -{ - char *buffer; - int ret; - - buffer = rpath(ctx, path); - ret = lsetxattr(buffer, name, value, size, flags); - g_free(buffer); - return ret; -} - -static inline int pt_removexattr(FsContext *ctx, - const char *path, const char *name) -{ - char *buffer; - int ret; - - buffer = rpath(ctx, path); - ret = lremovexattr(path, name); - g_free(buffer); - return ret; -} - -static inline ssize_t notsup_getxattr(FsContext *ctx, const char *path, - const char *name, void *value, - size_t size) -{ - errno = ENOTSUP; - return -1; -} - -static inline int notsup_setxattr(FsContext *ctx, const char *path, - const char *name, void *value, - size_t size, int flags) -{ - errno = ENOTSUP; - return -1; -} - -static inline ssize_t notsup_listxattr(FsContext *ctx, const char *path, - char *name, void *value, size_t size) -{ - return 0; -} - -static inline int notsup_removexattr(FsContext *ctx, - const char *path, const char *name) -{ - errno = ENOTSUP; - return -1; -} - -#endif diff --git a/qemu/hw/9pfs/9p.c b/qemu/hw/9pfs/9p.c deleted file mode 100644 index f5e30125f..000000000 --- a/qemu/hw/9pfs/9p.c +++ /dev/null @@ -1,3380 +0,0 @@ -/* - * Virtio 9p backend - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Anthony Liguori - * - * 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/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 "9p-xattr.h" -#include "coth.h" -#include "trace.h" -#include "migration/migration.h" - -int open_fd_hw; -int total_open_fd; -static int open_fd_rc; - -enum { - Oread = 0x00, - Owrite = 0x01, - Ordwr = 0x02, - Oexec = 0x03, - Oexcl = 0x04, - Otrunc = 0x10, - Orexec = 0x20, - Orclose = 0x40, - 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; - - switch (mode & 3) { - case Oread: - ret = O_RDONLY; - break; - case Ordwr: - ret = O_RDWR; - break; - case Owrite: - ret = O_WRONLY; - break; - case Oexec: - ret = O_RDONLY; - break; - } - - if (mode & Otrunc) { - ret |= O_TRUNC; - } - - if (mode & Oappend) { - ret |= O_APPEND; - } - - if (mode & Oexcl) { - ret |= O_EXCL; - } - - return ret; -} - -struct dotl_openflag_map { - int dotl_flag; - int open_flag; -}; - -static int dotl_to_open_flags(int flags) -{ - int i; - /* - * We have same bits for P9_DOTL_READONLY, P9_DOTL_WRONLY - * and P9_DOTL_NOACCESS - */ - int oflags = flags & O_ACCMODE; - - struct dotl_openflag_map dotl_oflag_map[] = { - { P9_DOTL_CREATE, O_CREAT }, - { P9_DOTL_EXCL, O_EXCL }, - { P9_DOTL_NOCTTY , O_NOCTTY }, - { P9_DOTL_TRUNC, O_TRUNC }, - { P9_DOTL_APPEND, O_APPEND }, - { P9_DOTL_NONBLOCK, O_NONBLOCK } , - { P9_DOTL_DSYNC, O_DSYNC }, - { P9_DOTL_FASYNC, FASYNC }, - { P9_DOTL_DIRECT, O_DIRECT }, - { P9_DOTL_LARGEFILE, O_LARGEFILE }, - { P9_DOTL_DIRECTORY, O_DIRECTORY }, - { P9_DOTL_NOFOLLOW, O_NOFOLLOW }, - { P9_DOTL_NOATIME, O_NOATIME }, - { P9_DOTL_SYNC, O_SYNC }, - }; - - for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) { - if (flags & dotl_oflag_map[i].dotl_flag) { - oflags |= dotl_oflag_map[i].open_flag; - } - } - - return oflags; -} - -void cred_init(FsCred *credp) -{ - credp->fc_uid = -1; - credp->fc_gid = -1; - credp->fc_mode = -1; - credp->fc_rdev = -1; -} - -static int get_dotl_openflags(V9fsState *s, int oflags) -{ - int flags; - /* - * Filter the client open flags - */ - flags = dotl_to_open_flags(oflags); - flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT); - /* - * Ignore direct disk access hint until the server supports it. - */ - flags &= ~O_DIRECT; - return flags; -} - -void v9fs_path_init(V9fsPath *path) -{ - path->data = NULL; - path->size = 0; -} - -void v9fs_path_free(V9fsPath *path) -{ - g_free(path->data); - path->data = NULL; - path->size = 0; -} - -void v9fs_path_copy(V9fsPath *lhs, V9fsPath *rhs) -{ - v9fs_path_free(lhs); - lhs->data = g_malloc(rhs->size); - memcpy(lhs->data, rhs->data, rhs->size); - lhs->size = rhs->size; -} - -int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath, - const char *name, V9fsPath *path) -{ - int err; - err = s->ops->name_to_path(&s->ctx, dirpath, name, path); - if (err < 0) { - err = -errno; - } - return err; -} - -/* - * Return TRUE if s1 is an ancestor of s2. - * - * E.g. "a/b" is an ancestor of "a/b/c" but not of "a/bc/d". - * As a special case, We treat s1 as ancestor of s2 if they are same! - */ -static int v9fs_path_is_ancestor(V9fsPath *s1, V9fsPath *s2) -{ - if (!strncmp(s1->data, s2->data, s1->size - 1)) { - if (s2->data[s1->size - 1] == '\0' || s2->data[s1->size - 1] == '/') { - return 1; - } - } - return 0; -} - -static size_t v9fs_string_size(V9fsString *str) -{ - return str->size; -} - -/* - * returns 0 if fid got re-opened, 1 if not, < 0 on error */ -static int v9fs_reopen_fid(V9fsPDU *pdu, V9fsFidState *f) -{ - int err = 1; - if (f->fid_type == P9_FID_FILE) { - if (f->fs.fd == -1) { - do { - err = v9fs_co_open(pdu, f, f->open_flags); - } while (err == -EINTR && !pdu->cancelled); - } - } else if (f->fid_type == P9_FID_DIR) { - if (f->fs.dir == NULL) { - do { - err = v9fs_co_opendir(pdu, f); - } while (err == -EINTR && !pdu->cancelled); - } - } - return err; -} - -static V9fsFidState *get_fid(V9fsPDU *pdu, int32_t fid) -{ - int err; - V9fsFidState *f; - V9fsState *s = pdu->s; - - for (f = s->fid_list; f; f = f->next) { - BUG_ON(f->clunked); - if (f->fid == fid) { - /* - * Update the fid ref upfront so that - * we don't get reclaimed when we yield - * in open later. - */ - f->ref++; - /* - * check whether we need to reopen the - * file. We might have closed the fd - * while trying to free up some file - * descriptors. - */ - err = v9fs_reopen_fid(pdu, f); - if (err < 0) { - f->ref--; - return NULL; - } - /* - * Mark the fid as referenced so that the LRU - * reclaim won't close the file descriptor - */ - f->flags |= FID_REFERENCED; - return f; - } - } - return NULL; -} - -static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid) -{ - V9fsFidState *f; - - for (f = s->fid_list; f; f = f->next) { - /* If fid is already there return NULL */ - BUG_ON(f->clunked); - if (f->fid == fid) { - return NULL; - } - } - f = g_malloc0(sizeof(V9fsFidState)); - f->fid = fid; - f->fid_type = P9_FID_NONE; - f->ref = 1; - /* - * Mark the fid as referenced so that the LRU - * reclaim won't close the file descriptor - */ - f->flags |= FID_REFERENCED; - f->next = s->fid_list; - s->fid_list = f; - - return f; -} - -static int v9fs_xattr_fid_clunk(V9fsPDU *pdu, V9fsFidState *fidp) -{ - int retval = 0; - - if (fidp->fs.xattr.copied_len == -1) { - /* getxattr/listxattr fid */ - goto free_value; - } - /* - * if this is fid for setxattr. clunk should - * result in setxattr localcall - */ - if (fidp->fs.xattr.len != fidp->fs.xattr.copied_len) { - /* clunk after partial write */ - retval = -EINVAL; - goto free_out; - } - if (fidp->fs.xattr.len) { - retval = v9fs_co_lsetxattr(pdu, &fidp->path, &fidp->fs.xattr.name, - fidp->fs.xattr.value, - fidp->fs.xattr.len, - fidp->fs.xattr.flags); - } else { - retval = v9fs_co_lremovexattr(pdu, &fidp->path, &fidp->fs.xattr.name); - } -free_out: - v9fs_string_free(&fidp->fs.xattr.name); -free_value: - g_free(fidp->fs.xattr.value); - return retval; -} - -static int free_fid(V9fsPDU *pdu, V9fsFidState *fidp) -{ - int retval = 0; - - if (fidp->fid_type == P9_FID_FILE) { - /* If we reclaimed the fd no need to close */ - if (fidp->fs.fd != -1) { - retval = v9fs_co_close(pdu, &fidp->fs); - } - } else if (fidp->fid_type == P9_FID_DIR) { - if (fidp->fs.dir != NULL) { - retval = v9fs_co_closedir(pdu, &fidp->fs); - } - } else if (fidp->fid_type == P9_FID_XATTR) { - retval = v9fs_xattr_fid_clunk(pdu, fidp); - } - v9fs_path_free(&fidp->path); - g_free(fidp); - return retval; -} - -static int put_fid(V9fsPDU *pdu, V9fsFidState *fidp) -{ - BUG_ON(!fidp->ref); - fidp->ref--; - /* - * Don't free the fid if it is in reclaim list - */ - if (!fidp->ref && fidp->clunked) { - if (fidp->fid == pdu->s->root_fid) { - /* - * if the clunked fid is root fid then we - * have unmounted the fs on the client side. - * delete the migration blocker. Ideally, this - * should be hooked to transport close notification - */ - if (pdu->s->migration_blocker) { - migrate_del_blocker(pdu->s->migration_blocker); - error_free(pdu->s->migration_blocker); - pdu->s->migration_blocker = NULL; - } - } - return free_fid(pdu, fidp); - } - return 0; -} - -static V9fsFidState *clunk_fid(V9fsState *s, int32_t fid) -{ - V9fsFidState **fidpp, *fidp; - - for (fidpp = &s->fid_list; *fidpp; fidpp = &(*fidpp)->next) { - if ((*fidpp)->fid == fid) { - break; - } - } - if (*fidpp == NULL) { - return NULL; - } - fidp = *fidpp; - *fidpp = fidp->next; - fidp->clunked = 1; - return fidp; -} - -void v9fs_reclaim_fd(V9fsPDU *pdu) -{ - int reclaim_count = 0; - V9fsState *s = pdu->s; - V9fsFidState *f, *reclaim_list = NULL; - - for (f = s->fid_list; f; f = f->next) { - /* - * Unlink fids cannot be reclaimed. Check - * for them and skip them. Also skip fids - * currently being operated on. - */ - if (f->ref || f->flags & FID_NON_RECLAIMABLE) { - continue; - } - /* - * if it is a recently referenced fid - * we leave the fid untouched and clear the - * reference bit. We come back to it later - * in the next iteration. (a simple LRU without - * moving list elements around) - */ - if (f->flags & FID_REFERENCED) { - f->flags &= ~FID_REFERENCED; - continue; - } - /* - * Add fids to reclaim list. - */ - if (f->fid_type == P9_FID_FILE) { - if (f->fs.fd != -1) { - /* - * Up the reference count so that - * a clunk request won't free this fid - */ - f->ref++; - f->rclm_lst = reclaim_list; - reclaim_list = f; - f->fs_reclaim.fd = f->fs.fd; - f->fs.fd = -1; - reclaim_count++; - } - } else if (f->fid_type == P9_FID_DIR) { - if (f->fs.dir != NULL) { - /* - * Up the reference count so that - * a clunk request won't free this fid - */ - f->ref++; - f->rclm_lst = reclaim_list; - reclaim_list = f; - f->fs_reclaim.dir = f->fs.dir; - f->fs.dir = NULL; - reclaim_count++; - } - } - if (reclaim_count >= open_fd_rc) { - break; - } - } - /* - * Now close the fid in reclaim list. Free them if they - * are already clunked. - */ - while (reclaim_list) { - f = reclaim_list; - reclaim_list = f->rclm_lst; - if (f->fid_type == P9_FID_FILE) { - v9fs_co_close(pdu, &f->fs_reclaim); - } else if (f->fid_type == P9_FID_DIR) { - v9fs_co_closedir(pdu, &f->fs_reclaim); - } - f->rclm_lst = NULL; - /* - * Now drop the fid reference, free it - * if clunked. - */ - put_fid(pdu, f); - } -} - -static int v9fs_mark_fids_unreclaim(V9fsPDU *pdu, V9fsPath *path) -{ - int err; - V9fsState *s = pdu->s; - V9fsFidState *fidp, head_fid; - - head_fid.next = s->fid_list; - for (fidp = s->fid_list; fidp; fidp = fidp->next) { - if (fidp->path.size != path->size) { - continue; - } - if (!memcmp(fidp->path.data, path->data, path->size)) { - /* Mark the fid non reclaimable. */ - fidp->flags |= FID_NON_RECLAIMABLE; - - /* reopen the file/dir if already closed */ - err = v9fs_reopen_fid(pdu, fidp); - if (err < 0) { - return -1; - } - /* - * Go back to head of fid list because - * the list could have got updated when - * switched to the worker thread - */ - if (err == 0) { - fidp = &head_fid; - } - } - } - return 0; -} - -static void virtfs_reset(V9fsPDU *pdu) -{ - V9fsState *s = pdu->s; - V9fsFidState *fidp = NULL; - - /* Free all fids */ - while (s->fid_list) { - fidp = s->fid_list; - s->fid_list = fidp->next; - - if (fidp->ref) { - fidp->clunked = 1; - } else { - free_fid(pdu, fidp); - } - } - if (fidp) { - /* One or more unclunked fids found... */ - error_report("9pfs:%s: One or more uncluncked fids " - "found during reset", __func__); - } -} - -#define P9_QID_TYPE_DIR 0x80 -#define P9_QID_TYPE_SYMLINK 0x02 - -#define P9_STAT_MODE_DIR 0x80000000 -#define P9_STAT_MODE_APPEND 0x40000000 -#define P9_STAT_MODE_EXCL 0x20000000 -#define P9_STAT_MODE_MOUNT 0x10000000 -#define P9_STAT_MODE_AUTH 0x08000000 -#define P9_STAT_MODE_TMP 0x04000000 -#define P9_STAT_MODE_SYMLINK 0x02000000 -#define P9_STAT_MODE_LINK 0x01000000 -#define P9_STAT_MODE_DEVICE 0x00800000 -#define P9_STAT_MODE_NAMED_PIPE 0x00200000 -#define P9_STAT_MODE_SOCKET 0x00100000 -#define P9_STAT_MODE_SETUID 0x00080000 -#define P9_STAT_MODE_SETGID 0x00040000 -#define P9_STAT_MODE_SETVTX 0x00010000 - -#define P9_STAT_MODE_TYPE_BITS (P9_STAT_MODE_DIR | \ - P9_STAT_MODE_SYMLINK | \ - P9_STAT_MODE_LINK | \ - P9_STAT_MODE_DEVICE | \ - P9_STAT_MODE_NAMED_PIPE | \ - P9_STAT_MODE_SOCKET) - -/* This is the algorithm from ufs in spfs */ -static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp) -{ - size_t size; - - memset(&qidp->path, 0, sizeof(qidp->path)); - size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path)); - memcpy(&qidp->path, &stbuf->st_ino, size); - qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8); - qidp->type = 0; - if (S_ISDIR(stbuf->st_mode)) { - qidp->type |= P9_QID_TYPE_DIR; - } - if (S_ISLNK(stbuf->st_mode)) { - qidp->type |= P9_QID_TYPE_SYMLINK; - } -} - -static int fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp, V9fsQID *qidp) -{ - struct stat stbuf; - int err; - - err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); - if (err < 0) { - return err; - } - stat_to_qid(&stbuf, qidp); - return 0; -} - -V9fsPDU *pdu_alloc(V9fsState *s) -{ - V9fsPDU *pdu = NULL; - - if (!QLIST_EMPTY(&s->free_list)) { - pdu = QLIST_FIRST(&s->free_list); - QLIST_REMOVE(pdu, next); - QLIST_INSERT_HEAD(&s->active_list, pdu, next); - } - return pdu; -} - -void pdu_free(V9fsPDU *pdu) -{ - if (pdu) { - V9fsState *s = pdu->s; - /* - * Cancelled pdu are added back to the freelist - * by flush request . - */ - if (!pdu->cancelled) { - QLIST_REMOVE(pdu, next); - QLIST_INSERT_HEAD(&s->free_list, pdu, next); - } - } -} - -/* - * We don't do error checking for pdu_marshal/unmarshal here - * because we always expect to have enough space to encode - * error details - */ -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; - len = 7; - - if (s->proto_version != V9FS_PROTO_2000L) { - V9fsString str; - - str.data = strerror(err); - str.size = strlen(str.data); - - len += pdu_marshal(pdu, len, "s", &str); - id = P9_RERROR; - } - - len += pdu_marshal(pdu, len, "d", err); - - if (s->proto_version == V9FS_PROTO_2000L) { - id = P9_RLERROR; - } - trace_v9fs_rerror(pdu->tag, pdu->id, err); /* Trace ERROR */ - } - - /* fill out the header */ - pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag); - - /* keep these in sync */ - pdu->size = len; - pdu->id = id; - - pdu_push_and_notify(pdu); - - /* Now wakeup anybody waiting in flush for this request */ - qemu_co_queue_next(&pdu->complete); - - pdu_free(pdu); -} - -static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension) -{ - mode_t ret; - - ret = mode & 0777; - if (mode & P9_STAT_MODE_DIR) { - ret |= S_IFDIR; - } - - if (mode & P9_STAT_MODE_SYMLINK) { - ret |= S_IFLNK; - } - if (mode & P9_STAT_MODE_SOCKET) { - ret |= S_IFSOCK; - } - if (mode & P9_STAT_MODE_NAMED_PIPE) { - ret |= S_IFIFO; - } - if (mode & P9_STAT_MODE_DEVICE) { - if (extension->size && extension->data[0] == 'c') { - ret |= S_IFCHR; - } else { - ret |= S_IFBLK; - } - } - - if (!(ret&~0777)) { - ret |= S_IFREG; - } - - if (mode & P9_STAT_MODE_SETUID) { - ret |= S_ISUID; - } - if (mode & P9_STAT_MODE_SETGID) { - ret |= S_ISGID; - } - if (mode & P9_STAT_MODE_SETVTX) { - ret |= S_ISVTX; - } - - return ret; -} - -static int donttouch_stat(V9fsStat *stat) -{ - if (stat->type == -1 && - stat->dev == -1 && - stat->qid.type == -1 && - stat->qid.version == -1 && - stat->qid.path == -1 && - stat->mode == -1 && - stat->atime == -1 && - stat->mtime == -1 && - stat->length == -1 && - !stat->name.size && - !stat->uid.size && - !stat->gid.size && - !stat->muid.size && - stat->n_uid == -1 && - stat->n_gid == -1 && - stat->n_muid == -1) { - return 1; - } - - return 0; -} - -static void v9fs_stat_init(V9fsStat *stat) -{ - v9fs_string_init(&stat->name); - v9fs_string_init(&stat->uid); - v9fs_string_init(&stat->gid); - v9fs_string_init(&stat->muid); - v9fs_string_init(&stat->extension); -} - -static void v9fs_stat_free(V9fsStat *stat) -{ - v9fs_string_free(&stat->name); - v9fs_string_free(&stat->uid); - v9fs_string_free(&stat->gid); - v9fs_string_free(&stat->muid); - v9fs_string_free(&stat->extension); -} - -static uint32_t stat_to_v9mode(const struct stat *stbuf) -{ - uint32_t mode; - - mode = stbuf->st_mode & 0777; - if (S_ISDIR(stbuf->st_mode)) { - mode |= P9_STAT_MODE_DIR; - } - - if (S_ISLNK(stbuf->st_mode)) { - mode |= P9_STAT_MODE_SYMLINK; - } - - if (S_ISSOCK(stbuf->st_mode)) { - mode |= P9_STAT_MODE_SOCKET; - } - - if (S_ISFIFO(stbuf->st_mode)) { - mode |= P9_STAT_MODE_NAMED_PIPE; - } - - if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) { - mode |= P9_STAT_MODE_DEVICE; - } - - if (stbuf->st_mode & S_ISUID) { - mode |= P9_STAT_MODE_SETUID; - } - - if (stbuf->st_mode & S_ISGID) { - mode |= P9_STAT_MODE_SETGID; - } - - if (stbuf->st_mode & S_ISVTX) { - mode |= P9_STAT_MODE_SETVTX; - } - - return mode; -} - -static int stat_to_v9stat(V9fsPDU *pdu, V9fsPath *name, - const struct stat *stbuf, - V9fsStat *v9stat) -{ - int err; - const char *str; - - memset(v9stat, 0, sizeof(*v9stat)); - - stat_to_qid(stbuf, &v9stat->qid); - v9stat->mode = stat_to_v9mode(stbuf); - v9stat->atime = stbuf->st_atime; - v9stat->mtime = stbuf->st_mtime; - v9stat->length = stbuf->st_size; - - v9fs_string_null(&v9stat->uid); - v9fs_string_null(&v9stat->gid); - v9fs_string_null(&v9stat->muid); - - v9stat->n_uid = stbuf->st_uid; - v9stat->n_gid = stbuf->st_gid; - v9stat->n_muid = 0; - - v9fs_string_null(&v9stat->extension); - - if (v9stat->mode & P9_STAT_MODE_SYMLINK) { - err = v9fs_co_readlink(pdu, name, &v9stat->extension); - if (err < 0) { - return err; - } - } else if (v9stat->mode & P9_STAT_MODE_DEVICE) { - v9fs_string_sprintf(&v9stat->extension, "%c %u %u", - S_ISCHR(stbuf->st_mode) ? 'c' : 'b', - major(stbuf->st_rdev), minor(stbuf->st_rdev)); - } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) { - v9fs_string_sprintf(&v9stat->extension, "%s %lu", - "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink); - } - - str = strrchr(name->data, '/'); - if (str) { - str += 1; - } else { - str = name->data; - } - - v9fs_string_sprintf(&v9stat->name, "%s", str); - - v9stat->size = 61 + - v9fs_string_size(&v9stat->name) + - v9fs_string_size(&v9stat->uid) + - v9fs_string_size(&v9stat->gid) + - v9fs_string_size(&v9stat->muid) + - v9fs_string_size(&v9stat->extension); - return 0; -} - -#define P9_STATS_MODE 0x00000001ULL -#define P9_STATS_NLINK 0x00000002ULL -#define P9_STATS_UID 0x00000004ULL -#define P9_STATS_GID 0x00000008ULL -#define P9_STATS_RDEV 0x00000010ULL -#define P9_STATS_ATIME 0x00000020ULL -#define P9_STATS_MTIME 0x00000040ULL -#define P9_STATS_CTIME 0x00000080ULL -#define P9_STATS_INO 0x00000100ULL -#define P9_STATS_SIZE 0x00000200ULL -#define P9_STATS_BLOCKS 0x00000400ULL - -#define P9_STATS_BTIME 0x00000800ULL -#define P9_STATS_GEN 0x00001000ULL -#define P9_STATS_DATA_VERSION 0x00002000ULL - -#define P9_STATS_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */ -#define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */ - - -static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf, - V9fsStatDotl *v9lstat) -{ - memset(v9lstat, 0, sizeof(*v9lstat)); - - v9lstat->st_mode = stbuf->st_mode; - v9lstat->st_nlink = stbuf->st_nlink; - v9lstat->st_uid = stbuf->st_uid; - v9lstat->st_gid = stbuf->st_gid; - v9lstat->st_rdev = stbuf->st_rdev; - v9lstat->st_size = stbuf->st_size; - v9lstat->st_blksize = stbuf->st_blksize; - v9lstat->st_blocks = stbuf->st_blocks; - v9lstat->st_atime_sec = stbuf->st_atime; - v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec; - v9lstat->st_mtime_sec = stbuf->st_mtime; - v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec; - v9lstat->st_ctime_sec = stbuf->st_ctime; - v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; - /* Currently we only support BASIC fields in stat */ - v9lstat->st_result_mask = P9_STATS_BASIC; - - stat_to_qid(stbuf, &v9lstat->qid); -} - -static void print_sg(struct iovec *sg, int cnt) -{ - int i; - - printf("sg[%d]: {", cnt); - for (i = 0; i < cnt; i++) { - if (i) { - printf(", "); - } - printf("(%p, %zd)", sg[i].iov_base, sg[i].iov_len); - } - printf("}\n"); -} - -/* Will call this only for path name based fid */ -static void v9fs_fix_path(V9fsPath *dst, V9fsPath *src, int len) -{ - V9fsPath str; - v9fs_path_init(&str); - v9fs_path_copy(&str, dst); - v9fs_string_sprintf((V9fsString *)dst, "%s%s", src->data, str.data+len); - v9fs_path_free(&str); - /* +1 to include terminating NULL */ - dst->size++; -} - -static inline bool is_ro_export(FsContext *ctx) -{ - return ctx->export_flags & V9FS_RDONLY; -} - -static void v9fs_version(void *opaque) -{ - ssize_t err; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - V9fsString version; - size_t offset = 7; - - v9fs_string_init(&version); - err = pdu_unmarshal(pdu, offset, "ds", &s->msize, &version); - if (err < 0) { - offset = err; - goto out; - } - trace_v9fs_version(pdu->tag, pdu->id, s->msize, version.data); - - virtfs_reset(pdu); - - if (!strcmp(version.data, "9P2000.u")) { - s->proto_version = V9FS_PROTO_2000U; - } else if (!strcmp(version.data, "9P2000.L")) { - s->proto_version = V9FS_PROTO_2000L; - } else { - v9fs_string_sprintf(&version, "unknown"); - } - - err = pdu_marshal(pdu, offset, "ds", s->msize, &version); - if (err < 0) { - offset = err; - goto out; - } - offset += err; - trace_v9fs_version_return(pdu->tag, pdu->id, s->msize, version.data); -out: - pdu_complete(pdu, offset); - v9fs_string_free(&version); -} - -static void v9fs_attach(void *opaque) -{ - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - int32_t fid, afid, n_uname; - V9fsString uname, aname; - V9fsFidState *fidp; - size_t offset = 7; - V9fsQID qid; - ssize_t err; - - v9fs_string_init(&uname); - v9fs_string_init(&aname); - err = pdu_unmarshal(pdu, offset, "ddssd", &fid, - &afid, &uname, &aname, &n_uname); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_attach(pdu->tag, pdu->id, fid, afid, uname.data, aname.data); - - fidp = alloc_fid(s, fid); - if (fidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - fidp->uid = n_uname; - err = v9fs_co_name_to_path(pdu, NULL, "/", &fidp->path); - if (err < 0) { - err = -EINVAL; - clunk_fid(s, fid); - goto out; - } - err = fid_to_qid(pdu, fidp, &qid); - if (err < 0) { - err = -EINVAL; - clunk_fid(s, fid); - goto out; - } - err = pdu_marshal(pdu, offset, "Q", &qid); - if (err < 0) { - clunk_fid(s, fid); - goto out; - } - err += offset; - trace_v9fs_attach_return(pdu->tag, pdu->id, - qid.type, qid.version, qid.path); - /* - * disable migration if we haven't done already. - * attach could get called multiple times for the same export. - */ - if (!s->migration_blocker) { - s->root_fid = fid; - error_setg(&s->migration_blocker, - "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'", - s->ctx.fs_root ? s->ctx.fs_root : "NULL", s->tag); - migrate_add_blocker(s->migration_blocker); - } -out: - put_fid(pdu, fidp); -out_nofid: - pdu_complete(pdu, err); - v9fs_string_free(&uname); - v9fs_string_free(&aname); -} - -static void v9fs_stat(void *opaque) -{ - int32_t fid; - V9fsStat v9stat; - ssize_t err = 0; - size_t offset = 7; - struct stat stbuf; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - - err = pdu_unmarshal(pdu, offset, "d", &fid); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_stat(pdu->tag, pdu->id, fid); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); - if (err < 0) { - goto out; - } - err = stat_to_v9stat(pdu, &fidp->path, &stbuf, &v9stat); - if (err < 0) { - goto out; - } - err = pdu_marshal(pdu, offset, "wS", 0, &v9stat); - if (err < 0) { - v9fs_stat_free(&v9stat); - goto out; - } - trace_v9fs_stat_return(pdu->tag, pdu->id, v9stat.mode, - v9stat.atime, v9stat.mtime, v9stat.length); - err += offset; - v9fs_stat_free(&v9stat); -out: - put_fid(pdu, fidp); -out_nofid: - pdu_complete(pdu, err); -} - -static void v9fs_getattr(void *opaque) -{ - int32_t fid; - size_t offset = 7; - ssize_t retval = 0; - struct stat stbuf; - V9fsFidState *fidp; - uint64_t request_mask; - V9fsStatDotl v9stat_dotl; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - retval = pdu_unmarshal(pdu, offset, "dq", &fid, &request_mask); - if (retval < 0) { - goto out_nofid; - } - trace_v9fs_getattr(pdu->tag, pdu->id, fid, request_mask); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - retval = -ENOENT; - goto out_nofid; - } - /* - * Currently we only support BASIC fields in stat, so there is no - * need to look at request_mask. - */ - retval = v9fs_co_lstat(pdu, &fidp->path, &stbuf); - if (retval < 0) { - goto out; - } - stat_to_v9stat_dotl(s, &stbuf, &v9stat_dotl); - - /* fill st_gen if requested and supported by underlying fs */ - if (request_mask & P9_STATS_GEN) { - retval = v9fs_co_st_gen(pdu, &fidp->path, stbuf.st_mode, &v9stat_dotl); - switch (retval) { - case 0: - /* we have valid st_gen: update result mask */ - v9stat_dotl.st_result_mask |= P9_STATS_GEN; - break; - case -EINTR: - /* request cancelled, e.g. by Tflush */ - goto out; - default: - /* failed to get st_gen: not fatal, ignore */ - break; - } - } - retval = pdu_marshal(pdu, offset, "A", &v9stat_dotl); - if (retval < 0) { - goto out; - } - retval += offset; - trace_v9fs_getattr_return(pdu->tag, pdu->id, v9stat_dotl.st_result_mask, - v9stat_dotl.st_mode, v9stat_dotl.st_uid, - v9stat_dotl.st_gid); -out: - put_fid(pdu, fidp); -out_nofid: - pdu_complete(pdu, retval); -} - -/* Attribute flags */ -#define P9_ATTR_MODE (1 << 0) -#define P9_ATTR_UID (1 << 1) -#define P9_ATTR_GID (1 << 2) -#define P9_ATTR_SIZE (1 << 3) -#define P9_ATTR_ATIME (1 << 4) -#define P9_ATTR_MTIME (1 << 5) -#define P9_ATTR_CTIME (1 << 6) -#define P9_ATTR_ATIME_SET (1 << 7) -#define P9_ATTR_MTIME_SET (1 << 8) - -#define P9_ATTR_MASK 127 - -static void v9fs_setattr(void *opaque) -{ - int err = 0; - int32_t fid; - V9fsFidState *fidp; - size_t offset = 7; - V9fsIattr v9iattr; - V9fsPDU *pdu = opaque; - - err = pdu_unmarshal(pdu, offset, "dI", &fid, &v9iattr); - if (err < 0) { - goto out_nofid; - } - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - if (v9iattr.valid & P9_ATTR_MODE) { - err = v9fs_co_chmod(pdu, &fidp->path, v9iattr.mode); - if (err < 0) { - goto out; - } - } - if (v9iattr.valid & (P9_ATTR_ATIME | P9_ATTR_MTIME)) { - struct timespec times[2]; - if (v9iattr.valid & P9_ATTR_ATIME) { - if (v9iattr.valid & P9_ATTR_ATIME_SET) { - times[0].tv_sec = v9iattr.atime_sec; - times[0].tv_nsec = v9iattr.atime_nsec; - } else { - times[0].tv_nsec = UTIME_NOW; - } - } else { - times[0].tv_nsec = UTIME_OMIT; - } - if (v9iattr.valid & P9_ATTR_MTIME) { - if (v9iattr.valid & P9_ATTR_MTIME_SET) { - times[1].tv_sec = v9iattr.mtime_sec; - times[1].tv_nsec = v9iattr.mtime_nsec; - } else { - times[1].tv_nsec = UTIME_NOW; - } - } else { - times[1].tv_nsec = UTIME_OMIT; - } - err = v9fs_co_utimensat(pdu, &fidp->path, times); - if (err < 0) { - goto out; - } - } - /* - * If the only valid entry in iattr is ctime we can call - * chown(-1,-1) to update the ctime of the file - */ - if ((v9iattr.valid & (P9_ATTR_UID | P9_ATTR_GID)) || - ((v9iattr.valid & P9_ATTR_CTIME) - && !((v9iattr.valid & P9_ATTR_MASK) & ~P9_ATTR_CTIME))) { - if (!(v9iattr.valid & P9_ATTR_UID)) { - v9iattr.uid = -1; - } - if (!(v9iattr.valid & P9_ATTR_GID)) { - v9iattr.gid = -1; - } - err = v9fs_co_chown(pdu, &fidp->path, v9iattr.uid, - v9iattr.gid); - if (err < 0) { - goto out; - } - } - if (v9iattr.valid & (P9_ATTR_SIZE)) { - err = v9fs_co_truncate(pdu, &fidp->path, v9iattr.size); - if (err < 0) { - goto out; - } - } - err = offset; -out: - put_fid(pdu, fidp); -out_nofid: - pdu_complete(pdu, err); -} - -static int v9fs_walk_marshal(V9fsPDU *pdu, uint16_t nwnames, V9fsQID *qids) -{ - int i; - ssize_t err; - size_t offset = 7; - - err = pdu_marshal(pdu, offset, "w", nwnames); - if (err < 0) { - return err; - } - offset += err; - for (i = 0; i < nwnames; i++) { - err = pdu_marshal(pdu, offset, "Q", &qids[i]); - if (err < 0) { - return err; - } - offset += err; - } - return offset; -} - -static void v9fs_walk(void *opaque) -{ - int name_idx; - V9fsQID *qids = NULL; - int i, err = 0; - V9fsPath dpath, path; - uint16_t nwnames; - struct stat stbuf; - size_t offset = 7; - int32_t fid, newfid; - V9fsString *wnames = NULL; - V9fsFidState *fidp; - V9fsFidState *newfidp = NULL; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - err = pdu_unmarshal(pdu, offset, "ddw", &fid, &newfid, &nwnames); - if (err < 0) { - pdu_complete(pdu, err); - return ; - } - offset += err; - - trace_v9fs_walk(pdu->tag, pdu->id, fid, newfid, nwnames); - - if (nwnames && nwnames <= P9_MAXWELEM) { - wnames = g_malloc0(sizeof(wnames[0]) * nwnames); - qids = g_malloc0(sizeof(qids[0]) * nwnames); - for (i = 0; i < nwnames; i++) { - err = pdu_unmarshal(pdu, offset, "s", &wnames[i]); - if (err < 0) { - goto out_nofid; - } - offset += err; - } - } else if (nwnames > P9_MAXWELEM) { - err = -EINVAL; - goto out_nofid; - } - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - v9fs_path_init(&dpath); - v9fs_path_init(&path); - /* - * Both dpath and path initially poin to fidp. - * Needed to handle request with nwnames == 0 - */ - v9fs_path_copy(&dpath, &fidp->path); - v9fs_path_copy(&path, &fidp->path); - for (name_idx = 0; name_idx < nwnames; name_idx++) { - err = v9fs_co_name_to_path(pdu, &dpath, wnames[name_idx].data, &path); - if (err < 0) { - goto out; - } - err = v9fs_co_lstat(pdu, &path, &stbuf); - if (err < 0) { - goto out; - } - stat_to_qid(&stbuf, &qids[name_idx]); - v9fs_path_copy(&dpath, &path); - } - if (fid == newfid) { - BUG_ON(fidp->fid_type != P9_FID_NONE); - v9fs_path_copy(&fidp->path, &path); - } else { - newfidp = alloc_fid(s, newfid); - if (newfidp == NULL) { - err = -EINVAL; - goto out; - } - newfidp->uid = fidp->uid; - v9fs_path_copy(&newfidp->path, &path); - } - err = v9fs_walk_marshal(pdu, nwnames, qids); - trace_v9fs_walk_return(pdu->tag, pdu->id, nwnames, qids); -out: - put_fid(pdu, fidp); - if (newfidp) { - put_fid(pdu, newfidp); - } - v9fs_path_free(&dpath); - v9fs_path_free(&path); -out_nofid: - pdu_complete(pdu, err); - if (nwnames && nwnames <= P9_MAXWELEM) { - for (name_idx = 0; name_idx < nwnames; name_idx++) { - v9fs_string_free(&wnames[name_idx]); - } - g_free(wnames); - g_free(qids); - } -} - -static int32_t get_iounit(V9fsPDU *pdu, V9fsPath *path) -{ - struct statfs stbuf; - int32_t iounit = 0; - V9fsState *s = pdu->s; - - /* - * iounit should be multiples of f_bsize (host filesystem block size - * and as well as less than (client msize - P9_IOHDRSZ)) - */ - if (!v9fs_co_statfs(pdu, path, &stbuf)) { - iounit = stbuf.f_bsize; - iounit *= (s->msize - P9_IOHDRSZ)/stbuf.f_bsize; - } - if (!iounit) { - iounit = s->msize - P9_IOHDRSZ; - } - return iounit; -} - -static void v9fs_open(void *opaque) -{ - int flags; - int32_t fid; - int32_t mode; - V9fsQID qid; - int iounit = 0; - ssize_t err = 0; - size_t offset = 7; - struct stat stbuf; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - if (s->proto_version == V9FS_PROTO_2000L) { - err = pdu_unmarshal(pdu, offset, "dd", &fid, &mode); - } else { - uint8_t modebyte; - err = pdu_unmarshal(pdu, offset, "db", &fid, &modebyte); - mode = modebyte; - } - if (err < 0) { - goto out_nofid; - } - trace_v9fs_open(pdu->tag, pdu->id, fid, mode); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - BUG_ON(fidp->fid_type != P9_FID_NONE); - - err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); - if (err < 0) { - goto out; - } - stat_to_qid(&stbuf, &qid); - if (S_ISDIR(stbuf.st_mode)) { - err = v9fs_co_opendir(pdu, fidp); - if (err < 0) { - goto out; - } - fidp->fid_type = P9_FID_DIR; - err = pdu_marshal(pdu, offset, "Qd", &qid, 0); - if (err < 0) { - goto out; - } - err += offset; - } else { - if (s->proto_version == V9FS_PROTO_2000L) { - flags = get_dotl_openflags(s, mode); - } else { - flags = omode_to_uflags(mode); - } - if (is_ro_export(&s->ctx)) { - if (mode & O_WRONLY || mode & O_RDWR || - mode & O_APPEND || mode & O_TRUNC) { - err = -EROFS; - goto out; - } - } - err = v9fs_co_open(pdu, fidp, flags); - if (err < 0) { - goto out; - } - fidp->fid_type = P9_FID_FILE; - fidp->open_flags = flags; - if (flags & O_EXCL) { - /* - * We let the host file system do O_EXCL check - * We should not reclaim such fd - */ - fidp->flags |= FID_NON_RECLAIMABLE; - } - iounit = get_iounit(pdu, &fidp->path); - err = pdu_marshal(pdu, offset, "Qd", &qid, iounit); - if (err < 0) { - goto out; - } - err += offset; - } - trace_v9fs_open_return(pdu->tag, pdu->id, - qid.type, qid.version, qid.path, iounit); -out: - put_fid(pdu, fidp); -out_nofid: - pdu_complete(pdu, err); -} - -static void v9fs_lcreate(void *opaque) -{ - int32_t dfid, flags, mode; - gid_t gid; - ssize_t err = 0; - ssize_t offset = 7; - V9fsString name; - V9fsFidState *fidp; - struct stat stbuf; - V9fsQID qid; - int32_t iounit; - V9fsPDU *pdu = opaque; - - v9fs_string_init(&name); - err = pdu_unmarshal(pdu, offset, "dsddd", &dfid, - &name, &flags, &mode, &gid); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_lcreate(pdu->tag, pdu->id, dfid, flags, mode, gid); - - fidp = get_fid(pdu, dfid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - - flags = get_dotl_openflags(pdu->s, flags); - err = v9fs_co_open2(pdu, fidp, &name, gid, - flags | O_CREAT, mode, &stbuf); - if (err < 0) { - goto out; - } - fidp->fid_type = P9_FID_FILE; - fidp->open_flags = flags; - if (flags & O_EXCL) { - /* - * We let the host file system do O_EXCL check - * We should not reclaim such fd - */ - fidp->flags |= FID_NON_RECLAIMABLE; - } - iounit = get_iounit(pdu, &fidp->path); - stat_to_qid(&stbuf, &qid); - err = pdu_marshal(pdu, offset, "Qd", &qid, iounit); - if (err < 0) { - goto out; - } - err += offset; - trace_v9fs_lcreate_return(pdu->tag, pdu->id, - qid.type, qid.version, qid.path, iounit); -out: - put_fid(pdu, fidp); -out_nofid: - pdu_complete(pdu, err); - v9fs_string_free(&name); -} - -static void v9fs_fsync(void *opaque) -{ - int err; - int32_t fid; - int datasync; - size_t offset = 7; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - - err = pdu_unmarshal(pdu, offset, "dd", &fid, &datasync); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_fsync(pdu->tag, pdu->id, fid, datasync); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - err = v9fs_co_fsync(pdu, fidp, datasync); - if (!err) { - err = offset; - } - put_fid(pdu, fidp); -out_nofid: - pdu_complete(pdu, err); -} - -static void v9fs_clunk(void *opaque) -{ - int err; - int32_t fid; - size_t offset = 7; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - err = pdu_unmarshal(pdu, offset, "d", &fid); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_clunk(pdu->tag, pdu->id, fid); - - fidp = clunk_fid(s, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - /* - * Bump the ref so that put_fid will - * free the fid. - */ - fidp->ref++; - err = put_fid(pdu, fidp); - if (!err) { - err = offset; - } -out_nofid: - pdu_complete(pdu, err); -} - -static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, - uint64_t off, uint32_t max_count) -{ - ssize_t err; - 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; - if (read_count > max_count) { - read_count = max_count; - } else if (read_count < 0) { - /* - * read beyond XATTR value - */ - read_count = 0; - } - err = pdu_marshal(pdu, offset, "d", read_count); - if (err < 0) { - return err; - } - offset += err; - - err = v9fs_pack(elem->in_sg, elem->in_num, offset, - ((char *)fidp->fs.xattr.value) + off, - read_count); - if (err < 0) { - return err; - } - offset += err; - return offset; -} - -static int v9fs_do_readdir_with_stat(V9fsPDU *pdu, - V9fsFidState *fidp, uint32_t max_count) -{ - V9fsPath path; - V9fsStat v9stat; - int len, err = 0; - int32_t count = 0; - struct stat stbuf; - off_t saved_dir_pos; - struct dirent *dent, *result; - - /* save the directory position */ - saved_dir_pos = v9fs_co_telldir(pdu, fidp); - if (saved_dir_pos < 0) { - return saved_dir_pos; - } - - dent = g_malloc(sizeof(struct dirent)); - - while (1) { - v9fs_path_init(&path); - err = v9fs_co_readdir_r(pdu, fidp, dent, &result); - if (err || !result) { - break; - } - err = v9fs_co_name_to_path(pdu, &fidp->path, dent->d_name, &path); - if (err < 0) { - goto out; - } - err = v9fs_co_lstat(pdu, &path, &stbuf); - if (err < 0) { - goto out; - } - err = stat_to_v9stat(pdu, &path, &stbuf, &v9stat); - if (err < 0) { - goto out; - } - /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */ - len = pdu_marshal(pdu, 11 + count, "S", &v9stat); - if ((len != (v9stat.size + 2)) || ((count + len) > max_count)) { - /* Ran out of buffer. Set dir back to old position and return */ - v9fs_co_seekdir(pdu, fidp, saved_dir_pos); - v9fs_stat_free(&v9stat); - v9fs_path_free(&path); - g_free(dent); - return count; - } - count += len; - v9fs_stat_free(&v9stat); - v9fs_path_free(&path); - saved_dir_pos = dent->d_off; - } -out: - g_free(dent); - v9fs_path_free(&path); - if (err < 0) { - return err; - } - return count; -} - -/* - * Create a QEMUIOVector for a sub-region of PDU iovecs - * - * @qiov: uninitialized QEMUIOVector - * @skip: number of bytes to skip from beginning of PDU - * @size: number of bytes to include - * @is_write: true - write, false - read - * - * The resulting QEMUIOVector has heap-allocated iovecs and must be cleaned up - * with qemu_iovec_destroy(). - */ -static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu, - size_t skip, size_t size, - bool is_write) -{ - QEMUIOVector elem; - struct iovec *iov; - unsigned int niov; - - virtio_init_iov_from_pdu(pdu, &iov, &niov, is_write); - - qemu_iovec_init_external(&elem, iov, niov); - qemu_iovec_init(qiov, niov); - qemu_iovec_concat(qiov, &elem, skip, size); -} - -static void v9fs_read(void *opaque) -{ - int32_t fid; - uint64_t off; - ssize_t err = 0; - int32_t count = 0; - size_t offset = 7; - uint32_t max_count; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - err = pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &max_count); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_read(pdu->tag, pdu->id, fid, off, max_count); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - if (fidp->fid_type == P9_FID_DIR) { - - if (off == 0) { - v9fs_co_rewinddir(pdu, fidp); - } - count = v9fs_do_readdir_with_stat(pdu, fidp, max_count); - if (count < 0) { - err = count; - goto out; - } - err = pdu_marshal(pdu, offset, "d", count); - if (err < 0) { - goto out; - } - err += offset + count; - } else if (fidp->fid_type == P9_FID_FILE) { - QEMUIOVector qiov_full; - QEMUIOVector qiov; - int32_t len; - - v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset + 4, max_count, false); - qemu_iovec_init(&qiov, qiov_full.niov); - do { - qemu_iovec_reset(&qiov); - qemu_iovec_concat(&qiov, &qiov_full, count, qiov_full.size - count); - if (0) { - print_sg(qiov.iov, qiov.niov); - } - /* Loop in case of EINTR */ - do { - len = v9fs_co_preadv(pdu, fidp, qiov.iov, qiov.niov, off); - if (len >= 0) { - off += len; - count += len; - } - } while (len == -EINTR && !pdu->cancelled); - if (len < 0) { - /* IO error return the error */ - err = len; - goto out; - } - } while (count < max_count && len > 0); - err = pdu_marshal(pdu, offset, "d", count); - if (err < 0) { - goto out; - } - err += offset + count; - qemu_iovec_destroy(&qiov); - qemu_iovec_destroy(&qiov_full); - } else if (fidp->fid_type == P9_FID_XATTR) { - err = v9fs_xattr_read(s, pdu, fidp, off, max_count); - } else { - err = -EINVAL; - } - trace_v9fs_read_return(pdu->tag, pdu->id, count, err); -out: - put_fid(pdu, fidp); -out_nofid: - pdu_complete(pdu, err); -} - -static size_t v9fs_readdir_data_size(V9fsString *name) -{ - /* - * Size of each dirent on the wire: size of qid (13) + size of offset (8) - * size of type (1) + size of name.size (2) + strlen(name.data) - */ - return 24 + v9fs_string_size(name); -} - -static int v9fs_do_readdir(V9fsPDU *pdu, - V9fsFidState *fidp, int32_t max_count) -{ - size_t size; - V9fsQID qid; - V9fsString name; - int len, err = 0; - int32_t count = 0; - off_t saved_dir_pos; - struct dirent *dent, *result; - - /* save the directory position */ - saved_dir_pos = v9fs_co_telldir(pdu, fidp); - if (saved_dir_pos < 0) { - return saved_dir_pos; - } - - dent = g_malloc(sizeof(struct dirent)); - - while (1) { - err = v9fs_co_readdir_r(pdu, fidp, dent, &result); - if (err || !result) { - break; - } - v9fs_string_init(&name); - v9fs_string_sprintf(&name, "%s", dent->d_name); - if ((count + v9fs_readdir_data_size(&name)) > max_count) { - /* Ran out of buffer. Set dir back to old position and return */ - v9fs_co_seekdir(pdu, fidp, saved_dir_pos); - v9fs_string_free(&name); - g_free(dent); - return count; - } - /* - * Fill up just the path field of qid because the client uses - * only that. To fill the entire qid structure we will have - * to stat each dirent found, which is expensive - */ - size = MIN(sizeof(dent->d_ino), sizeof(qid.path)); - memcpy(&qid.path, &dent->d_ino, size); - /* Fill the other fields with dummy values */ - qid.type = 0; - qid.version = 0; - - /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */ - len = pdu_marshal(pdu, 11 + count, "Qqbs", - &qid, dent->d_off, - dent->d_type, &name); - if (len < 0) { - v9fs_co_seekdir(pdu, fidp, saved_dir_pos); - v9fs_string_free(&name); - g_free(dent); - return len; - } - count += len; - v9fs_string_free(&name); - saved_dir_pos = dent->d_off; - } - g_free(dent); - if (err < 0) { - return err; - } - return count; -} - -static void v9fs_readdir(void *opaque) -{ - int32_t fid; - V9fsFidState *fidp; - ssize_t retval = 0; - size_t offset = 7; - uint64_t initial_offset; - int32_t count; - uint32_t max_count; - V9fsPDU *pdu = opaque; - - retval = pdu_unmarshal(pdu, offset, "dqd", &fid, - &initial_offset, &max_count); - if (retval < 0) { - goto out_nofid; - } - trace_v9fs_readdir(pdu->tag, pdu->id, fid, initial_offset, max_count); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - retval = -EINVAL; - goto out_nofid; - } - if (!fidp->fs.dir) { - retval = -EINVAL; - goto out; - } - if (initial_offset == 0) { - v9fs_co_rewinddir(pdu, fidp); - } else { - v9fs_co_seekdir(pdu, fidp, initial_offset); - } - count = v9fs_do_readdir(pdu, fidp, max_count); - if (count < 0) { - retval = count; - goto out; - } - retval = pdu_marshal(pdu, offset, "d", count); - if (retval < 0) { - goto out; - } - retval += count + offset; - trace_v9fs_readdir_return(pdu->tag, pdu->id, count, retval); -out: - put_fid(pdu, fidp); -out_nofid: - pdu_complete(pdu, retval); -} - -static int v9fs_xattr_write(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, - uint64_t off, uint32_t count, - struct iovec *sg, int cnt) -{ - int i, to_copy; - ssize_t err = 0; - int write_count; - int64_t xattr_len; - size_t offset = 7; - - - xattr_len = fidp->fs.xattr.len; - write_count = xattr_len - off; - if (write_count > count) { - write_count = count; - } else if (write_count < 0) { - /* - * write beyond XATTR value len specified in - * xattrcreate - */ - err = -ENOSPC; - goto out; - } - err = pdu_marshal(pdu, offset, "d", write_count); - if (err < 0) { - return err; - } - err += offset; - fidp->fs.xattr.copied_len += write_count; - /* - * Now copy the content from sg list - */ - for (i = 0; i < cnt; i++) { - if (write_count > sg[i].iov_len) { - to_copy = sg[i].iov_len; - } else { - to_copy = write_count; - } - memcpy((char *)fidp->fs.xattr.value + off, sg[i].iov_base, to_copy); - /* updating vs->off since we are not using below */ - off += to_copy; - write_count -= to_copy; - } -out: - return err; -} - -static void v9fs_write(void *opaque) -{ - ssize_t err; - int32_t fid; - uint64_t off; - uint32_t count; - int32_t len = 0; - int32_t total = 0; - size_t offset = 7; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - QEMUIOVector qiov_full; - QEMUIOVector qiov; - - err = pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &count); - if (err < 0) { - pdu_complete(pdu, err); - return; - } - offset += err; - v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset, count, true); - trace_v9fs_write(pdu->tag, pdu->id, fid, off, count, qiov_full.niov); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - if (fidp->fid_type == P9_FID_FILE) { - if (fidp->fs.fd == -1) { - err = -EINVAL; - goto out; - } - } else if (fidp->fid_type == P9_FID_XATTR) { - /* - * setxattr operation - */ - err = v9fs_xattr_write(s, pdu, fidp, off, count, - qiov_full.iov, qiov_full.niov); - goto out; - } else { - err = -EINVAL; - goto out; - } - qemu_iovec_init(&qiov, qiov_full.niov); - do { - qemu_iovec_reset(&qiov); - qemu_iovec_concat(&qiov, &qiov_full, total, qiov_full.size - total); - if (0) { - print_sg(qiov.iov, qiov.niov); - } - /* Loop in case of EINTR */ - do { - len = v9fs_co_pwritev(pdu, fidp, qiov.iov, qiov.niov, off); - if (len >= 0) { - off += len; - total += len; - } - } while (len == -EINTR && !pdu->cancelled); - if (len < 0) { - /* IO error return the error */ - err = len; - goto out_qiov; - } - } while (total < count && len > 0); - - offset = 7; - err = pdu_marshal(pdu, offset, "d", total); - if (err < 0) { - goto out; - } - err += offset; - trace_v9fs_write_return(pdu->tag, pdu->id, total, err); -out_qiov: - qemu_iovec_destroy(&qiov); -out: - put_fid(pdu, fidp); -out_nofid: - qemu_iovec_destroy(&qiov_full); - pdu_complete(pdu, err); -} - -static void v9fs_create(void *opaque) -{ - int32_t fid; - int err = 0; - size_t offset = 7; - V9fsFidState *fidp; - V9fsQID qid; - int32_t perm; - int8_t mode; - V9fsPath path; - struct stat stbuf; - V9fsString name; - V9fsString extension; - int iounit; - V9fsPDU *pdu = opaque; - - v9fs_path_init(&path); - v9fs_string_init(&name); - v9fs_string_init(&extension); - err = pdu_unmarshal(pdu, offset, "dsdbs", &fid, &name, - &perm, &mode, &extension); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_create(pdu->tag, pdu->id, fid, name.data, perm, mode); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - if (perm & P9_STAT_MODE_DIR) { - err = v9fs_co_mkdir(pdu, fidp, &name, perm & 0777, - fidp->uid, -1, &stbuf); - if (err < 0) { - goto out; - } - err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); - if (err < 0) { - goto out; - } - v9fs_path_copy(&fidp->path, &path); - err = v9fs_co_opendir(pdu, fidp); - if (err < 0) { - goto out; - } - fidp->fid_type = P9_FID_DIR; - } else if (perm & P9_STAT_MODE_SYMLINK) { - err = v9fs_co_symlink(pdu, fidp, &name, - extension.data, -1 , &stbuf); - if (err < 0) { - goto out; - } - err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); - if (err < 0) { - goto out; - } - v9fs_path_copy(&fidp->path, &path); - } else if (perm & P9_STAT_MODE_LINK) { - int32_t ofid = atoi(extension.data); - V9fsFidState *ofidp = get_fid(pdu, ofid); - if (ofidp == NULL) { - err = -EINVAL; - goto out; - } - err = v9fs_co_link(pdu, ofidp, fidp, &name); - put_fid(pdu, ofidp); - if (err < 0) { - goto out; - } - err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); - if (err < 0) { - fidp->fid_type = P9_FID_NONE; - goto out; - } - v9fs_path_copy(&fidp->path, &path); - err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); - if (err < 0) { - fidp->fid_type = P9_FID_NONE; - goto out; - } - } else if (perm & P9_STAT_MODE_DEVICE) { - char ctype; - uint32_t major, minor; - mode_t nmode = 0; - - if (sscanf(extension.data, "%c %u %u", &ctype, &major, &minor) != 3) { - err = -errno; - goto out; - } - - switch (ctype) { - case 'c': - nmode = S_IFCHR; - break; - case 'b': - nmode = S_IFBLK; - break; - default: - err = -EIO; - goto out; - } - - nmode |= perm & 0777; - err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, - makedev(major, minor), nmode, &stbuf); - if (err < 0) { - goto out; - } - err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); - if (err < 0) { - goto out; - } - v9fs_path_copy(&fidp->path, &path); - } else if (perm & P9_STAT_MODE_NAMED_PIPE) { - err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, - 0, S_IFIFO | (perm & 0777), &stbuf); - if (err < 0) { - goto out; - } - err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); - if (err < 0) { - goto out; - } - v9fs_path_copy(&fidp->path, &path); - } else if (perm & P9_STAT_MODE_SOCKET) { - err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, - 0, S_IFSOCK | (perm & 0777), &stbuf); - if (err < 0) { - goto out; - } - err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path); - if (err < 0) { - goto out; - } - v9fs_path_copy(&fidp->path, &path); - } else { - err = v9fs_co_open2(pdu, fidp, &name, -1, - omode_to_uflags(mode)|O_CREAT, perm, &stbuf); - if (err < 0) { - goto out; - } - fidp->fid_type = P9_FID_FILE; - fidp->open_flags = omode_to_uflags(mode); - if (fidp->open_flags & O_EXCL) { - /* - * We let the host file system do O_EXCL check - * We should not reclaim such fd - */ - fidp->flags |= FID_NON_RECLAIMABLE; - } - } - iounit = get_iounit(pdu, &fidp->path); - stat_to_qid(&stbuf, &qid); - err = pdu_marshal(pdu, offset, "Qd", &qid, iounit); - if (err < 0) { - goto out; - } - err += offset; - trace_v9fs_create_return(pdu->tag, pdu->id, - qid.type, qid.version, qid.path, iounit); -out: - put_fid(pdu, fidp); -out_nofid: - pdu_complete(pdu, err); - v9fs_string_free(&name); - v9fs_string_free(&extension); - v9fs_path_free(&path); -} - -static void v9fs_symlink(void *opaque) -{ - V9fsPDU *pdu = opaque; - V9fsString name; - V9fsString symname; - V9fsFidState *dfidp; - V9fsQID qid; - struct stat stbuf; - int32_t dfid; - int err = 0; - gid_t gid; - size_t offset = 7; - - v9fs_string_init(&name); - v9fs_string_init(&symname); - err = pdu_unmarshal(pdu, offset, "dssd", &dfid, &name, &symname, &gid); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_symlink(pdu->tag, pdu->id, dfid, name.data, symname.data, gid); - - dfidp = get_fid(pdu, dfid); - if (dfidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - err = v9fs_co_symlink(pdu, dfidp, &name, symname.data, gid, &stbuf); - if (err < 0) { - goto out; - } - stat_to_qid(&stbuf, &qid); - err = pdu_marshal(pdu, offset, "Q", &qid); - if (err < 0) { - goto out; - } - err += offset; - trace_v9fs_symlink_return(pdu->tag, pdu->id, - qid.type, qid.version, qid.path); -out: - put_fid(pdu, dfidp); -out_nofid: - pdu_complete(pdu, err); - v9fs_string_free(&name); - v9fs_string_free(&symname); -} - -static void v9fs_flush(void *opaque) -{ - ssize_t err; - int16_t tag; - size_t offset = 7; - V9fsPDU *cancel_pdu; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - err = pdu_unmarshal(pdu, offset, "w", &tag); - if (err < 0) { - pdu_complete(pdu, err); - return; - } - trace_v9fs_flush(pdu->tag, pdu->id, tag); - - QLIST_FOREACH(cancel_pdu, &s->active_list, next) { - if (cancel_pdu->tag == tag) { - break; - } - } - if (cancel_pdu) { - cancel_pdu->cancelled = 1; - /* - * Wait for pdu to complete. - */ - qemu_co_queue_wait(&cancel_pdu->complete); - cancel_pdu->cancelled = 0; - pdu_free(cancel_pdu); - } - pdu_complete(pdu, 7); -} - -static void v9fs_link(void *opaque) -{ - V9fsPDU *pdu = opaque; - int32_t dfid, oldfid; - V9fsFidState *dfidp, *oldfidp; - V9fsString name; - size_t offset = 7; - int err = 0; - - v9fs_string_init(&name); - err = pdu_unmarshal(pdu, offset, "dds", &dfid, &oldfid, &name); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_link(pdu->tag, pdu->id, dfid, oldfid, name.data); - - dfidp = get_fid(pdu, dfid); - if (dfidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - - oldfidp = get_fid(pdu, oldfid); - if (oldfidp == NULL) { - err = -ENOENT; - goto out; - } - err = v9fs_co_link(pdu, oldfidp, dfidp, &name); - if (!err) { - err = offset; - } -out: - put_fid(pdu, dfidp); -out_nofid: - v9fs_string_free(&name); - pdu_complete(pdu, err); -} - -/* Only works with path name based fid */ -static void v9fs_remove(void *opaque) -{ - int32_t fid; - int err = 0; - size_t offset = 7; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - - err = pdu_unmarshal(pdu, offset, "d", &fid); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_remove(pdu->tag, pdu->id, fid); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - /* if fs driver is not path based, return EOPNOTSUPP */ - if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) { - err = -EOPNOTSUPP; - goto out_err; - } - /* - * IF the file is unlinked, we cannot reopen - * the file later. So don't reclaim fd - */ - err = v9fs_mark_fids_unreclaim(pdu, &fidp->path); - if (err < 0) { - goto out_err; - } - err = v9fs_co_remove(pdu, &fidp->path); - if (!err) { - err = offset; - } -out_err: - /* For TREMOVE we need to clunk the fid even on failed remove */ - clunk_fid(pdu->s, fidp->fid); - put_fid(pdu, fidp); -out_nofid: - pdu_complete(pdu, err); -} - -static void v9fs_unlinkat(void *opaque) -{ - int err = 0; - V9fsString name; - int32_t dfid, flags; - size_t offset = 7; - V9fsPath path; - V9fsFidState *dfidp; - V9fsPDU *pdu = opaque; - - v9fs_string_init(&name); - err = pdu_unmarshal(pdu, offset, "dsd", &dfid, &name, &flags); - if (err < 0) { - goto out_nofid; - } - dfidp = get_fid(pdu, dfid); - if (dfidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - /* - * IF the file is unlinked, we cannot reopen - * the file later. So don't reclaim fd - */ - v9fs_path_init(&path); - err = v9fs_co_name_to_path(pdu, &dfidp->path, name.data, &path); - if (err < 0) { - goto out_err; - } - err = v9fs_mark_fids_unreclaim(pdu, &path); - if (err < 0) { - goto out_err; - } - err = v9fs_co_unlinkat(pdu, &dfidp->path, &name, flags); - if (!err) { - err = offset; - } -out_err: - put_fid(pdu, dfidp); - v9fs_path_free(&path); -out_nofid: - pdu_complete(pdu, err); - v9fs_string_free(&name); -} - - -/* Only works with path name based fid */ -static int v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp, - int32_t newdirfid, V9fsString *name) -{ - char *end; - int err = 0; - V9fsPath new_path; - V9fsFidState *tfidp; - V9fsState *s = pdu->s; - V9fsFidState *dirfidp = NULL; - char *old_name, *new_name; - - v9fs_path_init(&new_path); - if (newdirfid != -1) { - dirfidp = get_fid(pdu, newdirfid); - if (dirfidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - BUG_ON(dirfidp->fid_type != P9_FID_NONE); - v9fs_co_name_to_path(pdu, &dirfidp->path, name->data, &new_path); - } else { - old_name = fidp->path.data; - end = strrchr(old_name, '/'); - if (end) { - end++; - } else { - end = old_name; - } - new_name = g_malloc0(end - old_name + name->size + 1); - strncat(new_name, old_name, end - old_name); - strncat(new_name + (end - old_name), name->data, name->size); - v9fs_co_name_to_path(pdu, NULL, new_name, &new_path); - g_free(new_name); - } - err = v9fs_co_rename(pdu, &fidp->path, &new_path); - if (err < 0) { - goto out; - } - /* - * Fixup fid's pointing to the old name to - * start pointing to the new name - */ - for (tfidp = s->fid_list; tfidp; tfidp = tfidp->next) { - if (v9fs_path_is_ancestor(&fidp->path, &tfidp->path)) { - /* replace the name */ - v9fs_fix_path(&tfidp->path, &new_path, strlen(fidp->path.data)); - } - } -out: - if (dirfidp) { - put_fid(pdu, dirfidp); - } - v9fs_path_free(&new_path); -out_nofid: - return err; -} - -/* Only works with path name based fid */ -static void v9fs_rename(void *opaque) -{ - int32_t fid; - ssize_t err = 0; - size_t offset = 7; - V9fsString name; - int32_t newdirfid; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - v9fs_string_init(&name); - err = pdu_unmarshal(pdu, offset, "dds", &fid, &newdirfid, &name); - if (err < 0) { - goto out_nofid; - } - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - BUG_ON(fidp->fid_type != P9_FID_NONE); - /* if fs driver is not path based, return EOPNOTSUPP */ - if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) { - err = -EOPNOTSUPP; - goto out; - } - v9fs_path_write_lock(s); - err = v9fs_complete_rename(pdu, fidp, newdirfid, &name); - v9fs_path_unlock(s); - if (!err) { - err = offset; - } -out: - put_fid(pdu, fidp); -out_nofid: - pdu_complete(pdu, err); - v9fs_string_free(&name); -} - -static void v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir, - V9fsString *old_name, V9fsPath *newdir, - V9fsString *new_name) -{ - V9fsFidState *tfidp; - V9fsPath oldpath, newpath; - V9fsState *s = pdu->s; - - - v9fs_path_init(&oldpath); - v9fs_path_init(&newpath); - v9fs_co_name_to_path(pdu, olddir, old_name->data, &oldpath); - v9fs_co_name_to_path(pdu, newdir, new_name->data, &newpath); - - /* - * Fixup fid's pointing to the old name to - * start pointing to the new name - */ - for (tfidp = s->fid_list; tfidp; tfidp = tfidp->next) { - if (v9fs_path_is_ancestor(&oldpath, &tfidp->path)) { - /* replace the name */ - v9fs_fix_path(&tfidp->path, &newpath, strlen(oldpath.data)); - } - } - v9fs_path_free(&oldpath); - v9fs_path_free(&newpath); -} - -static int v9fs_complete_renameat(V9fsPDU *pdu, int32_t olddirfid, - V9fsString *old_name, int32_t newdirfid, - V9fsString *new_name) -{ - int err = 0; - V9fsState *s = pdu->s; - V9fsFidState *newdirfidp = NULL, *olddirfidp = NULL; - - olddirfidp = get_fid(pdu, olddirfid); - if (olddirfidp == NULL) { - err = -ENOENT; - goto out; - } - if (newdirfid != -1) { - newdirfidp = get_fid(pdu, newdirfid); - if (newdirfidp == NULL) { - err = -ENOENT; - goto out; - } - } else { - newdirfidp = get_fid(pdu, olddirfid); - } - - err = v9fs_co_renameat(pdu, &olddirfidp->path, old_name, - &newdirfidp->path, new_name); - if (err < 0) { - goto out; - } - if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { - /* Only for path based fid we need to do the below fixup */ - v9fs_fix_fid_paths(pdu, &olddirfidp->path, old_name, - &newdirfidp->path, new_name); - } -out: - if (olddirfidp) { - put_fid(pdu, olddirfidp); - } - if (newdirfidp) { - put_fid(pdu, newdirfidp); - } - return err; -} - -static void v9fs_renameat(void *opaque) -{ - ssize_t err = 0; - size_t offset = 7; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - int32_t olddirfid, newdirfid; - V9fsString old_name, new_name; - - v9fs_string_init(&old_name); - v9fs_string_init(&new_name); - err = pdu_unmarshal(pdu, offset, "dsds", &olddirfid, - &old_name, &newdirfid, &new_name); - if (err < 0) { - goto out_err; - } - - v9fs_path_write_lock(s); - err = v9fs_complete_renameat(pdu, olddirfid, - &old_name, newdirfid, &new_name); - v9fs_path_unlock(s); - if (!err) { - err = offset; - } - -out_err: - pdu_complete(pdu, err); - v9fs_string_free(&old_name); - v9fs_string_free(&new_name); -} - -static void v9fs_wstat(void *opaque) -{ - int32_t fid; - int err = 0; - int16_t unused; - V9fsStat v9stat; - size_t offset = 7; - struct stat stbuf; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - - v9fs_stat_init(&v9stat); - err = pdu_unmarshal(pdu, offset, "dwS", &fid, &unused, &v9stat); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_wstat(pdu->tag, pdu->id, fid, - v9stat.mode, v9stat.atime, v9stat.mtime); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - /* do we need to sync the file? */ - if (donttouch_stat(&v9stat)) { - err = v9fs_co_fsync(pdu, fidp, 0); - goto out; - } - if (v9stat.mode != -1) { - uint32_t v9_mode; - err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); - if (err < 0) { - goto out; - } - v9_mode = stat_to_v9mode(&stbuf); - if ((v9stat.mode & P9_STAT_MODE_TYPE_BITS) != - (v9_mode & P9_STAT_MODE_TYPE_BITS)) { - /* Attempting to change the type */ - err = -EIO; - goto out; - } - err = v9fs_co_chmod(pdu, &fidp->path, - v9mode_to_mode(v9stat.mode, - &v9stat.extension)); - if (err < 0) { - goto out; - } - } - if (v9stat.mtime != -1 || v9stat.atime != -1) { - struct timespec times[2]; - if (v9stat.atime != -1) { - times[0].tv_sec = v9stat.atime; - times[0].tv_nsec = 0; - } else { - times[0].tv_nsec = UTIME_OMIT; - } - if (v9stat.mtime != -1) { - times[1].tv_sec = v9stat.mtime; - times[1].tv_nsec = 0; - } else { - times[1].tv_nsec = UTIME_OMIT; - } - err = v9fs_co_utimensat(pdu, &fidp->path, times); - if (err < 0) { - goto out; - } - } - if (v9stat.n_gid != -1 || v9stat.n_uid != -1) { - err = v9fs_co_chown(pdu, &fidp->path, v9stat.n_uid, v9stat.n_gid); - if (err < 0) { - goto out; - } - } - if (v9stat.name.size != 0) { - err = v9fs_complete_rename(pdu, fidp, -1, &v9stat.name); - if (err < 0) { - goto out; - } - } - if (v9stat.length != -1) { - err = v9fs_co_truncate(pdu, &fidp->path, v9stat.length); - if (err < 0) { - goto out; - } - } - err = offset; -out: - put_fid(pdu, fidp); -out_nofid: - v9fs_stat_free(&v9stat); - pdu_complete(pdu, err); -} - -static int v9fs_fill_statfs(V9fsState *s, V9fsPDU *pdu, struct statfs *stbuf) -{ - uint32_t f_type; - uint32_t f_bsize; - uint64_t f_blocks; - uint64_t f_bfree; - uint64_t f_bavail; - uint64_t f_files; - uint64_t f_ffree; - uint64_t fsid_val; - uint32_t f_namelen; - size_t offset = 7; - int32_t bsize_factor; - - /* - * compute bsize factor based on host file system block size - * and client msize - */ - bsize_factor = (s->msize - P9_IOHDRSZ)/stbuf->f_bsize; - if (!bsize_factor) { - bsize_factor = 1; - } - f_type = stbuf->f_type; - f_bsize = stbuf->f_bsize; - f_bsize *= bsize_factor; - /* - * f_bsize is adjusted(multiplied) by bsize factor, so we need to - * adjust(divide) the number of blocks, free blocks and available - * blocks by bsize factor - */ - f_blocks = stbuf->f_blocks/bsize_factor; - f_bfree = stbuf->f_bfree/bsize_factor; - f_bavail = stbuf->f_bavail/bsize_factor; - f_files = stbuf->f_files; - f_ffree = stbuf->f_ffree; - fsid_val = (unsigned int) stbuf->f_fsid.__val[0] | - (unsigned long long)stbuf->f_fsid.__val[1] << 32; - f_namelen = stbuf->f_namelen; - - return pdu_marshal(pdu, offset, "ddqqqqqqd", - f_type, f_bsize, f_blocks, f_bfree, - f_bavail, f_files, f_ffree, - fsid_val, f_namelen); -} - -static void v9fs_statfs(void *opaque) -{ - int32_t fid; - ssize_t retval = 0; - size_t offset = 7; - V9fsFidState *fidp; - struct statfs stbuf; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - retval = pdu_unmarshal(pdu, offset, "d", &fid); - if (retval < 0) { - goto out_nofid; - } - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - retval = -ENOENT; - goto out_nofid; - } - retval = v9fs_co_statfs(pdu, &fidp->path, &stbuf); - if (retval < 0) { - goto out; - } - retval = v9fs_fill_statfs(s, pdu, &stbuf); - if (retval < 0) { - goto out; - } - retval += offset; -out: - put_fid(pdu, fidp); -out_nofid: - pdu_complete(pdu, retval); -} - -static void v9fs_mknod(void *opaque) -{ - - int mode; - gid_t gid; - int32_t fid; - V9fsQID qid; - int err = 0; - int major, minor; - size_t offset = 7; - V9fsString name; - struct stat stbuf; - V9fsFidState *fidp; - V9fsPDU *pdu = opaque; - - v9fs_string_init(&name); - err = pdu_unmarshal(pdu, offset, "dsdddd", &fid, &name, &mode, - &major, &minor, &gid); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_mknod(pdu->tag, pdu->id, fid, mode, major, minor); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, gid, - makedev(major, minor), mode, &stbuf); - if (err < 0) { - goto out; - } - stat_to_qid(&stbuf, &qid); - err = pdu_marshal(pdu, offset, "Q", &qid); - if (err < 0) { - goto out; - } - err += offset; - trace_v9fs_mknod_return(pdu->tag, pdu->id, - qid.type, qid.version, qid.path); -out: - put_fid(pdu, fidp); -out_nofid: - pdu_complete(pdu, err); - v9fs_string_free(&name); -} - -/* - * Implement posix byte range locking code - * Server side handling of locking code is very simple, because 9p server in - * QEMU can handle only one client. And most of the lock handling - * (like conflict, merging) etc is done by the VFS layer itself, so no need to - * do any thing in * qemu 9p server side lock code path. - * So when a TLOCK request comes, always return success - */ -static void v9fs_lock(void *opaque) -{ - int8_t status; - V9fsFlock flock; - size_t offset = 7; - struct stat stbuf; - V9fsFidState *fidp; - int32_t fid, err = 0; - V9fsPDU *pdu = opaque; - - status = P9_LOCK_ERROR; - v9fs_string_init(&flock.client_id); - err = pdu_unmarshal(pdu, offset, "dbdqqds", &fid, &flock.type, - &flock.flags, &flock.start, &flock.length, - &flock.proc_id, &flock.client_id); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_lock(pdu->tag, pdu->id, fid, - flock.type, flock.start, flock.length); - - - /* We support only block flag now (that too ignored currently) */ - if (flock.flags & ~P9_LOCK_FLAGS_BLOCK) { - err = -EINVAL; - goto out_nofid; - } - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - err = v9fs_co_fstat(pdu, fidp, &stbuf); - if (err < 0) { - goto out; - } - status = P9_LOCK_SUCCESS; -out: - put_fid(pdu, fidp); -out_nofid: - err = pdu_marshal(pdu, offset, "b", status); - if (err > 0) { - err += offset; - } - trace_v9fs_lock_return(pdu->tag, pdu->id, status); - pdu_complete(pdu, err); - v9fs_string_free(&flock.client_id); -} - -/* - * When a TGETLOCK request comes, always return success because all lock - * handling is done by client's VFS layer. - */ -static void v9fs_getlock(void *opaque) -{ - size_t offset = 7; - struct stat stbuf; - V9fsFidState *fidp; - V9fsGetlock glock; - int32_t fid, err = 0; - V9fsPDU *pdu = opaque; - - v9fs_string_init(&glock.client_id); - err = pdu_unmarshal(pdu, offset, "dbqqds", &fid, &glock.type, - &glock.start, &glock.length, &glock.proc_id, - &glock.client_id); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_getlock(pdu->tag, pdu->id, fid, - glock.type, glock.start, glock.length); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - err = v9fs_co_fstat(pdu, fidp, &stbuf); - if (err < 0) { - goto out; - } - glock.type = P9_LOCK_TYPE_UNLCK; - err = pdu_marshal(pdu, offset, "bqqds", glock.type, - glock.start, glock.length, glock.proc_id, - &glock.client_id); - if (err < 0) { - goto out; - } - err += offset; - trace_v9fs_getlock_return(pdu->tag, pdu->id, glock.type, glock.start, - glock.length, glock.proc_id); -out: - put_fid(pdu, fidp); -out_nofid: - pdu_complete(pdu, err); - v9fs_string_free(&glock.client_id); -} - -static void v9fs_mkdir(void *opaque) -{ - V9fsPDU *pdu = opaque; - size_t offset = 7; - int32_t fid; - struct stat stbuf; - V9fsQID qid; - V9fsString name; - V9fsFidState *fidp; - gid_t gid; - int mode; - int err = 0; - - v9fs_string_init(&name); - err = pdu_unmarshal(pdu, offset, "dsdd", &fid, &name, &mode, &gid); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_mkdir(pdu->tag, pdu->id, fid, name.data, mode, gid); - - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - err = v9fs_co_mkdir(pdu, fidp, &name, mode, fidp->uid, gid, &stbuf); - if (err < 0) { - goto out; - } - stat_to_qid(&stbuf, &qid); - err = pdu_marshal(pdu, offset, "Q", &qid); - if (err < 0) { - goto out; - } - err += offset; - trace_v9fs_mkdir_return(pdu->tag, pdu->id, - qid.type, qid.version, qid.path, err); -out: - put_fid(pdu, fidp); -out_nofid: - pdu_complete(pdu, err); - v9fs_string_free(&name); -} - -static void v9fs_xattrwalk(void *opaque) -{ - int64_t size; - V9fsString name; - ssize_t err = 0; - size_t offset = 7; - int32_t fid, newfid; - V9fsFidState *file_fidp; - V9fsFidState *xattr_fidp = NULL; - V9fsPDU *pdu = opaque; - V9fsState *s = pdu->s; - - v9fs_string_init(&name); - err = pdu_unmarshal(pdu, offset, "dds", &fid, &newfid, &name); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_xattrwalk(pdu->tag, pdu->id, fid, newfid, name.data); - - file_fidp = get_fid(pdu, fid); - if (file_fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - xattr_fidp = alloc_fid(s, newfid); - if (xattr_fidp == NULL) { - err = -EINVAL; - goto out; - } - v9fs_path_copy(&xattr_fidp->path, &file_fidp->path); - if (name.data == NULL) { - /* - * listxattr request. Get the size first - */ - size = v9fs_co_llistxattr(pdu, &xattr_fidp->path, NULL, 0); - if (size < 0) { - err = size; - clunk_fid(s, xattr_fidp->fid); - goto out; - } - /* - * Read the xattr value - */ - xattr_fidp->fs.xattr.len = size; - xattr_fidp->fid_type = P9_FID_XATTR; - xattr_fidp->fs.xattr.copied_len = -1; - if (size) { - xattr_fidp->fs.xattr.value = g_malloc(size); - err = v9fs_co_llistxattr(pdu, &xattr_fidp->path, - xattr_fidp->fs.xattr.value, - xattr_fidp->fs.xattr.len); - if (err < 0) { - clunk_fid(s, xattr_fidp->fid); - goto out; - } - } - err = pdu_marshal(pdu, offset, "q", size); - if (err < 0) { - goto out; - } - err += offset; - } else { - /* - * specific xattr fid. We check for xattr - * presence also collect the xattr size - */ - size = v9fs_co_lgetxattr(pdu, &xattr_fidp->path, - &name, NULL, 0); - if (size < 0) { - err = size; - clunk_fid(s, xattr_fidp->fid); - goto out; - } - /* - * Read the xattr value - */ - xattr_fidp->fs.xattr.len = size; - xattr_fidp->fid_type = P9_FID_XATTR; - xattr_fidp->fs.xattr.copied_len = -1; - if (size) { - xattr_fidp->fs.xattr.value = g_malloc(size); - err = v9fs_co_lgetxattr(pdu, &xattr_fidp->path, - &name, xattr_fidp->fs.xattr.value, - xattr_fidp->fs.xattr.len); - if (err < 0) { - clunk_fid(s, xattr_fidp->fid); - goto out; - } - } - err = pdu_marshal(pdu, offset, "q", size); - if (err < 0) { - goto out; - } - err += offset; - } - trace_v9fs_xattrwalk_return(pdu->tag, pdu->id, size); -out: - put_fid(pdu, file_fidp); - if (xattr_fidp) { - put_fid(pdu, xattr_fidp); - } -out_nofid: - pdu_complete(pdu, err); - v9fs_string_free(&name); -} - -static void v9fs_xattrcreate(void *opaque) -{ - int flags; - int32_t fid; - int64_t size; - ssize_t err = 0; - V9fsString name; - size_t offset = 7; - V9fsFidState *file_fidp; - V9fsFidState *xattr_fidp; - V9fsPDU *pdu = opaque; - - v9fs_string_init(&name); - err = pdu_unmarshal(pdu, offset, "dsqd", &fid, &name, &size, &flags); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_xattrcreate(pdu->tag, pdu->id, fid, name.data, size, flags); - - file_fidp = get_fid(pdu, fid); - if (file_fidp == NULL) { - err = -EINVAL; - goto out_nofid; - } - /* Make the file fid point to xattr */ - xattr_fidp = file_fidp; - xattr_fidp->fid_type = P9_FID_XATTR; - xattr_fidp->fs.xattr.copied_len = 0; - xattr_fidp->fs.xattr.len = size; - xattr_fidp->fs.xattr.flags = flags; - v9fs_string_init(&xattr_fidp->fs.xattr.name); - v9fs_string_copy(&xattr_fidp->fs.xattr.name, &name); - xattr_fidp->fs.xattr.value = g_malloc(size); - err = offset; - put_fid(pdu, file_fidp); -out_nofid: - pdu_complete(pdu, err); - v9fs_string_free(&name); -} - -static void v9fs_readlink(void *opaque) -{ - V9fsPDU *pdu = opaque; - size_t offset = 7; - V9fsString target; - int32_t fid; - int err = 0; - V9fsFidState *fidp; - - err = pdu_unmarshal(pdu, offset, "d", &fid); - if (err < 0) { - goto out_nofid; - } - trace_v9fs_readlink(pdu->tag, pdu->id, fid); - fidp = get_fid(pdu, fid); - if (fidp == NULL) { - err = -ENOENT; - goto out_nofid; - } - - v9fs_string_init(&target); - err = v9fs_co_readlink(pdu, &fidp->path, &target); - if (err < 0) { - goto out; - } - err = pdu_marshal(pdu, offset, "s", &target); - if (err < 0) { - v9fs_string_free(&target); - goto out; - } - err += offset; - trace_v9fs_readlink_return(pdu->tag, pdu->id, target.data); - v9fs_string_free(&target); -out: - put_fid(pdu, fidp); -out_nofid: - pdu_complete(pdu, err); -} - -static CoroutineEntry *pdu_co_handlers[] = { - [P9_TREADDIR] = v9fs_readdir, - [P9_TSTATFS] = v9fs_statfs, - [P9_TGETATTR] = v9fs_getattr, - [P9_TSETATTR] = v9fs_setattr, - [P9_TXATTRWALK] = v9fs_xattrwalk, - [P9_TXATTRCREATE] = v9fs_xattrcreate, - [P9_TMKNOD] = v9fs_mknod, - [P9_TRENAME] = v9fs_rename, - [P9_TLOCK] = v9fs_lock, - [P9_TGETLOCK] = v9fs_getlock, - [P9_TRENAMEAT] = v9fs_renameat, - [P9_TREADLINK] = v9fs_readlink, - [P9_TUNLINKAT] = v9fs_unlinkat, - [P9_TMKDIR] = v9fs_mkdir, - [P9_TVERSION] = v9fs_version, - [P9_TLOPEN] = v9fs_open, - [P9_TATTACH] = v9fs_attach, - [P9_TSTAT] = v9fs_stat, - [P9_TWALK] = v9fs_walk, - [P9_TCLUNK] = v9fs_clunk, - [P9_TFSYNC] = v9fs_fsync, - [P9_TOPEN] = v9fs_open, - [P9_TREAD] = v9fs_read, -#if 0 - [P9_TAUTH] = v9fs_auth, -#endif - [P9_TFLUSH] = v9fs_flush, - [P9_TLINK] = v9fs_link, - [P9_TSYMLINK] = v9fs_symlink, - [P9_TCREATE] = v9fs_create, - [P9_TLCREATE] = v9fs_lcreate, - [P9_TWRITE] = v9fs_write, - [P9_TWSTAT] = v9fs_wstat, - [P9_TREMOVE] = v9fs_remove, -}; - -static void v9fs_op_not_supp(void *opaque) -{ - V9fsPDU *pdu = opaque; - pdu_complete(pdu, -EOPNOTSUPP); -} - -static void v9fs_fs_ro(void *opaque) -{ - V9fsPDU *pdu = opaque; - pdu_complete(pdu, -EROFS); -} - -static inline bool is_read_only_op(V9fsPDU *pdu) -{ - switch (pdu->id) { - case P9_TREADDIR: - case P9_TSTATFS: - case P9_TGETATTR: - case P9_TXATTRWALK: - case P9_TLOCK: - case P9_TGETLOCK: - case P9_TREADLINK: - case P9_TVERSION: - case P9_TLOPEN: - case P9_TATTACH: - case P9_TSTAT: - case P9_TWALK: - case P9_TCLUNK: - case P9_TFSYNC: - case P9_TOPEN: - case P9_TREAD: - case P9_TAUTH: - case P9_TFLUSH: - return 1; - default: - return 0; - } -} - -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)) { - handler = v9fs_op_not_supp; - } else { - handler = pdu_co_handlers[pdu->id]; - } - - if (is_ro_export(&s->ctx) && !is_read_only_op(pdu)) { - handler = v9fs_fs_ro; - } - co = qemu_coroutine_create(handler); - qemu_coroutine_enter(co, pdu); -} - -/* Returns 0 on success, 1 on failure. */ -int v9fs_device_realize_common(V9fsState *s, Error **errp) -{ - 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; - } - - s->tag = g_strdup(s->fsconf.tag); - s->ctx.uid = -1; - - s->ops = fse->ops; - - s->fid_list = NULL; - qemu_co_rwlock_init(&s->rename_lock); - - 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; - } - - /* - * 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); - - rc = 0; -out: - if (rc) { - g_free(s->ctx.fs_root); - g_free(s->tag); - v9fs_path_free(&path); - } - 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__)) v9fs_set_fd_limit(void) -{ - struct rlimit rlim; - if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { - error_report("Failed to get the resource limit"); - exit(1); - } - open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur/3); - open_fd_rc = rlim.rlim_cur/2; -} diff --git a/qemu/hw/9pfs/9p.h b/qemu/hw/9pfs/9p.h deleted file mode 100644 index 1a19418a8..000000000 --- a/qemu/hw/9pfs/9p.h +++ /dev/null @@ -1,324 +0,0 @@ -#ifndef _QEMU_9P_H -#define _QEMU_9P_H - -#include -#include -#include -#include -#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 deleted file mode 100644 index da0ae0cfd..000000000 --- a/qemu/hw/9pfs/Makefile.objs +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 91df7f7a7..000000000 --- a/qemu/hw/9pfs/codir.c +++ /dev/null @@ -1,169 +0,0 @@ - -/* - * Virtio 9p backend - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Aneesh Kumar K.V - * - * 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 "fsdev/qemu-fsdev.h" -#include "qemu/thread.h" -#include "qemu/coroutine.h" -#include "coth.h" - -int v9fs_co_readdir_r(V9fsPDU *pdu, V9fsFidState *fidp, struct dirent *dent, - struct dirent **result) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_co_run_in_worker( - { - errno = 0; - err = s->ops->readdir_r(&s->ctx, &fidp->fs, dent, result); - if (!*result && errno) { - err = -errno; - } else { - err = 0; - } - }); - return err; -} - -off_t v9fs_co_telldir(V9fsPDU *pdu, V9fsFidState *fidp) -{ - off_t err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_co_run_in_worker( - { - err = s->ops->telldir(&s->ctx, &fidp->fs); - if (err < 0) { - err = -errno; - } - }); - return err; -} - -void v9fs_co_seekdir(V9fsPDU *pdu, V9fsFidState *fidp, off_t offset) -{ - V9fsState *s = pdu->s; - if (v9fs_request_cancelled(pdu)) { - return; - } - v9fs_co_run_in_worker( - { - s->ops->seekdir(&s->ctx, &fidp->fs, offset); - }); -} - -void v9fs_co_rewinddir(V9fsPDU *pdu, V9fsFidState *fidp) -{ - V9fsState *s = pdu->s; - if (v9fs_request_cancelled(pdu)) { - return; - } - v9fs_co_run_in_worker( - { - s->ops->rewinddir(&s->ctx, &fidp->fs); - }); -} - -int v9fs_co_mkdir(V9fsPDU *pdu, V9fsFidState *fidp, V9fsString *name, - mode_t mode, uid_t uid, gid_t gid, struct stat *stbuf) -{ - int err; - FsCred cred; - V9fsPath path; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - cred_init(&cred); - cred.fc_mode = mode; - cred.fc_uid = uid; - cred.fc_gid = gid; - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->mkdir(&s->ctx, &fidp->path, name->data, &cred); - if (err < 0) { - err = -errno; - } else { - v9fs_path_init(&path); - err = v9fs_name_to_path(s, &fidp->path, name->data, &path); - if (!err) { - err = s->ops->lstat(&s->ctx, &path, stbuf); - if (err < 0) { - err = -errno; - } - } - v9fs_path_free(&path); - } - }); - v9fs_path_unlock(s); - return err; -} - -int v9fs_co_opendir(V9fsPDU *pdu, V9fsFidState *fidp) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->opendir(&s->ctx, &fidp->path, &fidp->fs); - if (err < 0) { - err = -errno; - } else { - err = 0; - } - }); - v9fs_path_unlock(s); - if (!err) { - total_open_fd++; - if (total_open_fd > open_fd_hw) { - v9fs_reclaim_fd(pdu); - } - } - return err; -} - -int v9fs_co_closedir(V9fsPDU *pdu, V9fsFidOpenState *fs) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_co_run_in_worker( - { - err = s->ops->closedir(&s->ctx, fs); - if (err < 0) { - err = -errno; - } - }); - if (!err) { - total_open_fd--; - } - return err; -} diff --git a/qemu/hw/9pfs/cofile.c b/qemu/hw/9pfs/cofile.c deleted file mode 100644 index 293483e0c..000000000 --- a/qemu/hw/9pfs/cofile.c +++ /dev/null @@ -1,276 +0,0 @@ - -/* - * Virtio 9p backend - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Aneesh Kumar K.V - * - * 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 "fsdev/qemu-fsdev.h" -#include "qemu/thread.h" -#include "qemu/coroutine.h" -#include "coth.h" - -int v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t st_mode, - V9fsStatDotl *v9stat) -{ - int err = 0; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - if (s->ctx.exops.get_st_gen) { - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ctx.exops.get_st_gen(&s->ctx, path, st_mode, - &v9stat->st_gen); - if (err < 0) { - err = -errno; - } - }); - v9fs_path_unlock(s); - } - return err; -} - -int v9fs_co_lstat(V9fsPDU *pdu, V9fsPath *path, struct stat *stbuf) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->lstat(&s->ctx, path, stbuf); - if (err < 0) { - err = -errno; - } - }); - v9fs_path_unlock(s); - return err; -} - -int v9fs_co_fstat(V9fsPDU *pdu, V9fsFidState *fidp, struct stat *stbuf) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_co_run_in_worker( - { - err = s->ops->fstat(&s->ctx, fidp->fid_type, &fidp->fs, stbuf); - if (err < 0) { - err = -errno; - } - }); - /* - * Some FS driver (local:mapped-file) can't support fetching attributes - * using file descriptor. Use Path name in that case. - */ - if (err == -EOPNOTSUPP) { - err = v9fs_co_lstat(pdu, &fidp->path, stbuf); - if (err == -ENOENT) { - /* - * fstat on an unlinked file. Work with partial results - * returned from s->ops->fstat - */ - err = 0; - } - } - return err; -} - -int v9fs_co_open(V9fsPDU *pdu, V9fsFidState *fidp, int flags) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->open(&s->ctx, &fidp->path, flags, &fidp->fs); - if (err == -1) { - err = -errno; - } else { - err = 0; - } - }); - v9fs_path_unlock(s); - if (!err) { - total_open_fd++; - if (total_open_fd > open_fd_hw) { - v9fs_reclaim_fd(pdu); - } - } - return err; -} - -int v9fs_co_open2(V9fsPDU *pdu, V9fsFidState *fidp, V9fsString *name, gid_t gid, - int flags, int mode, struct stat *stbuf) -{ - int err; - FsCred cred; - V9fsPath path; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - cred_init(&cred); - cred.fc_mode = mode & 07777; - cred.fc_uid = fidp->uid; - cred.fc_gid = gid; - /* - * Hold the directory fid lock so that directory path name - * don't change. Read lock is fine because this fid cannot - * be used by any other operation. - */ - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->open2(&s->ctx, &fidp->path, - name->data, flags, &cred, &fidp->fs); - if (err < 0) { - err = -errno; - } else { - v9fs_path_init(&path); - err = v9fs_name_to_path(s, &fidp->path, name->data, &path); - if (!err) { - err = s->ops->lstat(&s->ctx, &path, stbuf); - if (err < 0) { - err = -errno; - s->ops->close(&s->ctx, &fidp->fs); - } else { - v9fs_path_copy(&fidp->path, &path); - } - } else { - s->ops->close(&s->ctx, &fidp->fs); - } - v9fs_path_free(&path); - } - }); - v9fs_path_unlock(s); - if (!err) { - total_open_fd++; - if (total_open_fd > open_fd_hw) { - v9fs_reclaim_fd(pdu); - } - } - return err; -} - -int v9fs_co_close(V9fsPDU *pdu, V9fsFidOpenState *fs) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_co_run_in_worker( - { - err = s->ops->close(&s->ctx, fs); - if (err < 0) { - err = -errno; - } - }); - if (!err) { - total_open_fd--; - } - return err; -} - -int v9fs_co_fsync(V9fsPDU *pdu, V9fsFidState *fidp, int datasync) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_co_run_in_worker( - { - err = s->ops->fsync(&s->ctx, fidp->fid_type, &fidp->fs, datasync); - if (err < 0) { - err = -errno; - } - }); - return err; -} - -int v9fs_co_link(V9fsPDU *pdu, V9fsFidState *oldfid, - V9fsFidState *newdirfid, V9fsString *name) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->link(&s->ctx, &oldfid->path, - &newdirfid->path, name->data); - if (err < 0) { - err = -errno; - } - }); - v9fs_path_unlock(s); - return err; -} - -int v9fs_co_pwritev(V9fsPDU *pdu, V9fsFidState *fidp, - struct iovec *iov, int iovcnt, int64_t offset) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_co_run_in_worker( - { - err = s->ops->pwritev(&s->ctx, &fidp->fs, iov, iovcnt, offset); - if (err < 0) { - err = -errno; - } - }); - return err; -} - -int v9fs_co_preadv(V9fsPDU *pdu, V9fsFidState *fidp, - struct iovec *iov, int iovcnt, int64_t offset) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_co_run_in_worker( - { - err = s->ops->preadv(&s->ctx, &fidp->fs, iov, iovcnt, offset); - if (err < 0) { - err = -errno; - } - }); - return err; -} diff --git a/qemu/hw/9pfs/cofs.c b/qemu/hw/9pfs/cofs.c deleted file mode 100644 index 18c81cb3d..000000000 --- a/qemu/hw/9pfs/cofs.c +++ /dev/null @@ -1,365 +0,0 @@ - -/* - * Virtio 9p backend - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Aneesh Kumar K.V - * - * 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 "fsdev/qemu-fsdev.h" -#include "qemu/thread.h" -#include "qemu/coroutine.h" -#include "coth.h" - -static ssize_t __readlink(V9fsState *s, V9fsPath *path, V9fsString *buf) -{ - ssize_t len, maxlen = PATH_MAX; - - buf->data = g_malloc(PATH_MAX); - for(;;) { - len = s->ops->readlink(&s->ctx, path, buf->data, maxlen); - if (len < 0) { - g_free(buf->data); - buf->data = NULL; - buf->size = 0; - break; - } else if (len == maxlen) { - /* - * We dodn't have space to put the NULL or we have more - * to read. Increase the size and try again - */ - maxlen *= 2; - g_free(buf->data); - buf->data = g_malloc(maxlen); - continue; - } - /* - * Null terminate the readlink output - */ - buf->data[len] = '\0'; - buf->size = len; - break; - } - return len; -} - -int v9fs_co_readlink(V9fsPDU *pdu, V9fsPath *path, V9fsString *buf) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = __readlink(s, path, buf); - if (err < 0) { - err = -errno; - } - }); - v9fs_path_unlock(s); - return err; -} - -int v9fs_co_statfs(V9fsPDU *pdu, V9fsPath *path, struct statfs *stbuf) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->statfs(&s->ctx, path, stbuf); - if (err < 0) { - err = -errno; - } - }); - v9fs_path_unlock(s); - return err; -} - -int v9fs_co_chmod(V9fsPDU *pdu, V9fsPath *path, mode_t mode) -{ - int err; - FsCred cred; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - cred_init(&cred); - cred.fc_mode = mode; - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->chmod(&s->ctx, path, &cred); - if (err < 0) { - err = -errno; - } - }); - v9fs_path_unlock(s); - return err; -} - -int v9fs_co_utimensat(V9fsPDU *pdu, V9fsPath *path, - struct timespec times[2]) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->utimensat(&s->ctx, path, times); - if (err < 0) { - err = -errno; - } - }); - v9fs_path_unlock(s); - return err; -} - -int v9fs_co_chown(V9fsPDU *pdu, V9fsPath *path, uid_t uid, gid_t gid) -{ - int err; - FsCred cred; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - cred_init(&cred); - cred.fc_uid = uid; - cred.fc_gid = gid; - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->chown(&s->ctx, path, &cred); - if (err < 0) { - err = -errno; - } - }); - v9fs_path_unlock(s); - return err; -} - -int v9fs_co_truncate(V9fsPDU *pdu, V9fsPath *path, off_t size) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->truncate(&s->ctx, path, size); - if (err < 0) { - err = -errno; - } - }); - v9fs_path_unlock(s); - return err; -} - -int v9fs_co_mknod(V9fsPDU *pdu, V9fsFidState *fidp, V9fsString *name, uid_t uid, - gid_t gid, dev_t dev, mode_t mode, struct stat *stbuf) -{ - int err; - V9fsPath path; - FsCred cred; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - cred_init(&cred); - cred.fc_uid = uid; - cred.fc_gid = gid; - cred.fc_mode = mode; - cred.fc_rdev = dev; - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->mknod(&s->ctx, &fidp->path, name->data, &cred); - if (err < 0) { - err = -errno; - } else { - v9fs_path_init(&path); - err = v9fs_name_to_path(s, &fidp->path, name->data, &path); - if (!err) { - err = s->ops->lstat(&s->ctx, &path, stbuf); - if (err < 0) { - err = -errno; - } - } - v9fs_path_free(&path); - } - }); - v9fs_path_unlock(s); - return err; -} - -/* Only works with path name based fid */ -int v9fs_co_remove(V9fsPDU *pdu, V9fsPath *path) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->remove(&s->ctx, path->data); - if (err < 0) { - err = -errno; - } - }); - v9fs_path_unlock(s); - return err; -} - -int v9fs_co_unlinkat(V9fsPDU *pdu, V9fsPath *path, V9fsString *name, int flags) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->unlinkat(&s->ctx, path, name->data, flags); - if (err < 0) { - err = -errno; - } - }); - v9fs_path_unlock(s); - return err; -} - -/* Only work with path name based fid */ -int v9fs_co_rename(V9fsPDU *pdu, V9fsPath *oldpath, V9fsPath *newpath) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_co_run_in_worker( - { - err = s->ops->rename(&s->ctx, oldpath->data, newpath->data); - if (err < 0) { - err = -errno; - } - }); - return err; -} - -int v9fs_co_renameat(V9fsPDU *pdu, V9fsPath *olddirpath, V9fsString *oldname, - V9fsPath *newdirpath, V9fsString *newname) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_co_run_in_worker( - { - err = s->ops->renameat(&s->ctx, olddirpath, oldname->data, - newdirpath, newname->data); - if (err < 0) { - err = -errno; - } - }); - return err; -} - -int v9fs_co_symlink(V9fsPDU *pdu, V9fsFidState *dfidp, V9fsString *name, - const char *oldpath, gid_t gid, struct stat *stbuf) -{ - int err; - FsCred cred; - V9fsPath path; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - cred_init(&cred); - cred.fc_uid = dfidp->uid; - cred.fc_gid = gid; - cred.fc_mode = 0777; - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->symlink(&s->ctx, oldpath, &dfidp->path, - name->data, &cred); - if (err < 0) { - err = -errno; - } else { - v9fs_path_init(&path); - err = v9fs_name_to_path(s, &dfidp->path, name->data, &path); - if (!err) { - err = s->ops->lstat(&s->ctx, &path, stbuf); - if (err < 0) { - err = -errno; - } - } - v9fs_path_free(&path); - } - }); - v9fs_path_unlock(s); - return err; -} - -/* - * For path name based fid we don't block. So we can - * directly call the fs driver ops. - */ -int v9fs_co_name_to_path(V9fsPDU *pdu, V9fsPath *dirpath, - const char *name, V9fsPath *path) -{ - int err; - V9fsState *s = pdu->s; - - if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { - err = s->ops->name_to_path(&s->ctx, dirpath, name, path); - if (err < 0) { - err = -errno; - } - } else { - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_co_run_in_worker( - { - err = s->ops->name_to_path(&s->ctx, dirpath, name, path); - if (err < 0) { - err = -errno; - } - }); - } - return err; -} diff --git a/qemu/hw/9pfs/coth.c b/qemu/hw/9pfs/coth.c deleted file mode 100644 index 464293ef2..000000000 --- a/qemu/hw/9pfs/coth.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 9p backend - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Harsh Prateek Bora - * Venkateswararao Jujjuri(JV) - * - * 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/coth.h b/qemu/hw/9pfs/coth.h deleted file mode 100644 index 209fc6a9a..000000000 --- a/qemu/hw/9pfs/coth.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 9p backend - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Harsh Prateek Bora - * Venkateswararao Jujjuri(JV) - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#ifndef _QEMU_9P_COTH_H -#define _QEMU_9P_COTH_H - -#include "qemu/thread.h" -#include "qemu/coroutine.h" -#include "virtio-9p.h" - -/* - * we want to use bottom half because we want to make sure the below - * sequence of events. - * - * 1. Yield the coroutine in the QEMU thread. - * 2. Submit the coroutine to a worker thread. - * 3. Enter the coroutine in the worker thread. - * we cannot swap step 1 and 2, because that would imply worker thread - * can enter coroutine while step1 is still running - */ -#define v9fs_co_run_in_worker(code_block) \ - do { \ - QEMUBH *co_bh; \ - co_bh = qemu_bh_new(co_run_in_worker_bh, \ - qemu_coroutine_self()); \ - qemu_bh_schedule(co_bh); \ - /* \ - * yield in qemu thread and re-enter back \ - * in worker thread \ - */ \ - qemu_coroutine_yield(); \ - qemu_bh_delete(co_bh); \ - code_block; \ - /* re-enter back to qemu thread */ \ - qemu_coroutine_yield(); \ - } while (0) - -extern void co_run_in_worker_bh(void *); -extern int v9fs_init_worker_threads(void); -extern int v9fs_co_readlink(V9fsPDU *, V9fsPath *, V9fsString *); -extern int v9fs_co_readdir_r(V9fsPDU *, V9fsFidState *, - struct dirent *, struct dirent **result); -extern off_t v9fs_co_telldir(V9fsPDU *, V9fsFidState *); -extern void v9fs_co_seekdir(V9fsPDU *, V9fsFidState *, off_t); -extern void v9fs_co_rewinddir(V9fsPDU *, V9fsFidState *); -extern int v9fs_co_statfs(V9fsPDU *, V9fsPath *, struct statfs *); -extern int v9fs_co_lstat(V9fsPDU *, V9fsPath *, struct stat *); -extern int v9fs_co_chmod(V9fsPDU *, V9fsPath *, mode_t); -extern int v9fs_co_utimensat(V9fsPDU *, V9fsPath *, struct timespec [2]); -extern int v9fs_co_chown(V9fsPDU *, V9fsPath *, uid_t, gid_t); -extern int v9fs_co_truncate(V9fsPDU *, V9fsPath *, off_t); -extern int v9fs_co_llistxattr(V9fsPDU *, V9fsPath *, void *, size_t); -extern int v9fs_co_lgetxattr(V9fsPDU *, V9fsPath *, - V9fsString *, void *, size_t); -extern int v9fs_co_mknod(V9fsPDU *, V9fsFidState *, V9fsString *, uid_t, - gid_t, dev_t, mode_t, struct stat *); -extern int v9fs_co_mkdir(V9fsPDU *, V9fsFidState *, V9fsString *, - mode_t, uid_t, gid_t, struct stat *); -extern int v9fs_co_remove(V9fsPDU *, V9fsPath *); -extern int v9fs_co_rename(V9fsPDU *, V9fsPath *, V9fsPath *); -extern int v9fs_co_unlinkat(V9fsPDU *, V9fsPath *, V9fsString *, int flags); -extern int v9fs_co_renameat(V9fsPDU *, V9fsPath *, V9fsString *, - V9fsPath *, V9fsString *); -extern int v9fs_co_fstat(V9fsPDU *, V9fsFidState *, struct stat *); -extern int v9fs_co_opendir(V9fsPDU *, V9fsFidState *); -extern int v9fs_co_open(V9fsPDU *, V9fsFidState *, int); -extern int v9fs_co_open2(V9fsPDU *, V9fsFidState *, V9fsString *, - gid_t, int, int, struct stat *); -extern int v9fs_co_lsetxattr(V9fsPDU *, V9fsPath *, V9fsString *, - void *, size_t, int); -extern int v9fs_co_lremovexattr(V9fsPDU *, V9fsPath *, V9fsString *); -extern int v9fs_co_closedir(V9fsPDU *, V9fsFidOpenState *); -extern int v9fs_co_close(V9fsPDU *, V9fsFidOpenState *); -extern int v9fs_co_fsync(V9fsPDU *, V9fsFidState *, int); -extern int v9fs_co_symlink(V9fsPDU *, V9fsFidState *, V9fsString *, - const char *, gid_t, struct stat *); -extern int v9fs_co_link(V9fsPDU *, V9fsFidState *, - V9fsFidState *, V9fsString *); -extern int v9fs_co_pwritev(V9fsPDU *, V9fsFidState *, - struct iovec *, int, int64_t); -extern int v9fs_co_preadv(V9fsPDU *, V9fsFidState *, - struct iovec *, int, int64_t); -extern int v9fs_co_name_to_path(V9fsPDU *, V9fsPath *, - const char *, V9fsPath *); -extern int v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t, - V9fsStatDotl *v9stat); - -#endif diff --git a/qemu/hw/9pfs/coxattr.c b/qemu/hw/9pfs/coxattr.c deleted file mode 100644 index 6ad96ea9f..000000000 --- a/qemu/hw/9pfs/coxattr.c +++ /dev/null @@ -1,108 +0,0 @@ - -/* - * Virtio 9p backend - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Aneesh Kumar K.V - * - * 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 "fsdev/qemu-fsdev.h" -#include "qemu/thread.h" -#include "qemu/coroutine.h" -#include "coth.h" - -int v9fs_co_llistxattr(V9fsPDU *pdu, V9fsPath *path, void *value, size_t size) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->llistxattr(&s->ctx, path, value, size); - if (err < 0) { - err = -errno; - } - }); - v9fs_path_unlock(s); - return err; -} - -int v9fs_co_lgetxattr(V9fsPDU *pdu, V9fsPath *path, - V9fsString *xattr_name, - void *value, size_t size) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->lgetxattr(&s->ctx, path, - xattr_name->data, - value, size); - if (err < 0) { - err = -errno; - } - }); - v9fs_path_unlock(s); - return err; -} - -int v9fs_co_lsetxattr(V9fsPDU *pdu, V9fsPath *path, - V9fsString *xattr_name, void *value, - size_t size, int flags) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->lsetxattr(&s->ctx, path, - xattr_name->data, value, - size, flags); - if (err < 0) { - err = -errno; - } - }); - v9fs_path_unlock(s); - return err; -} - -int v9fs_co_lremovexattr(V9fsPDU *pdu, V9fsPath *path, - V9fsString *xattr_name) -{ - int err; - V9fsState *s = pdu->s; - - if (v9fs_request_cancelled(pdu)) { - return -EINTR; - } - v9fs_path_read_lock(s); - v9fs_co_run_in_worker( - { - err = s->ops->lremovexattr(&s->ctx, path, xattr_name->data); - if (err < 0) { - err = -errno; - } - }); - v9fs_path_unlock(s); - return err; -} diff --git a/qemu/hw/9pfs/virtio-9p-device.c b/qemu/hw/9pfs/virtio-9p-device.c deleted file mode 100644 index a38850ee8..000000000 --- a/qemu/hw/9pfs/virtio-9p-device.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Virtio 9p backend - * - * Copyright IBM, Corp. 2010 - * - * Authors: - * Anthony Liguori - * - * 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/virtio/virtio.h" -#include "hw/i386/pc.h" -#include "qemu/sockets.h" -#include "virtio-9p.h" -#include "fsdev/qemu-fsdev.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) -{ - virtio_add_feature(&features, VIRTIO_9P_MOUNT_TAG); - return features; -} - -static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config) -{ - int len; - struct virtio_9p_config *cfg; - 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, 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); - V9fsVirtioState *v = VIRTIO_9P(dev); - V9fsState *s = &v->state; - - if (v9fs_device_realize_common(s, errp)) { - goto out; - } - - 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); - -out: - return; -} - -static void virtio_9p_device_unrealize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - V9fsVirtioState *v = VIRTIO_9P(dev); - V9fsState *s = &v->state; - - virtio_cleanup(vdev); - unregister_savevm(dev, "virtio-9p", v); - v9fs_device_unrealize_common(s, errp); -} - -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]; - - return v9fs_iov_vmarshal(elem->in_sg, elem->in_num, offset, 1, fmt, ap); -} - -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]; - - return v9fs_iov_vunmarshal(elem->out_sg, elem->out_num, offset, 1, fmt, ap); -} - -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; - } -} - -/* virtio-9p device */ - -static Property virtio_9p_properties[] = { - DEFINE_PROP_STRING("mount_tag", V9fsVirtioState, state.fsconf.tag), - DEFINE_PROP_STRING("fsdev", V9fsVirtioState, state.fsconf.fsdev_id), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_9p_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - - 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; -} - -static const TypeInfo virtio_device_info = { - .name = TYPE_VIRTIO_9P, - .parent = TYPE_VIRTIO_DEVICE, - .instance_size = sizeof(V9fsVirtioState), - .class_init = virtio_9p_class_init, -}; - -static void virtio_9p_register_types(void) -{ - type_register_static(&virtio_device_info); -} - -type_init(virtio_9p_register_types) diff --git a/qemu/hw/9pfs/virtio-9p.h b/qemu/hw/9pfs/virtio-9p.h deleted file mode 100644 index 7f6d88553..000000000 --- a/qemu/hw/9pfs/virtio-9p.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef _QEMU_VIRTIO_9P_H -#define _QEMU_VIRTIO_9P_H - -#include "standard-headers/linux/virtio_9p.h" -#include "hw/virtio/virtio.h" -#include "9p.h" - -typedef struct V9fsVirtioState -{ - VirtIODevice parent_obj; - VirtQueue *vq; - size_t config_size; - V9fsPDU pdus[MAX_REQ]; - VirtQueueElement *elems[MAX_REQ]; - V9fsState state; -} V9fsVirtioState; - -extern void virtio_9p_push_and_notify(V9fsPDU *pdu); - -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(V9fsVirtioState, (obj), TYPE_VIRTIO_9P) - -#endif diff --git a/qemu/hw/Makefile.objs b/qemu/hw/Makefile.objs deleted file mode 100644 index 4a07ed434..000000000 --- a/qemu/hw/Makefile.objs +++ /dev/null @@ -1,38 +0,0 @@ -devices-dirs-$(call land, $(CONFIG_VIRTIO),$(call land,$(CONFIG_VIRTFS),$(CONFIG_PCI))) += 9pfs/ -devices-dirs-$(CONFIG_ACPI) += acpi/ -devices-dirs-$(CONFIG_SOFTMMU) += audio/ -devices-dirs-$(CONFIG_SOFTMMU) += block/ -devices-dirs-$(CONFIG_SOFTMMU) += bt/ -devices-dirs-$(CONFIG_SOFTMMU) += char/ -devices-dirs-$(CONFIG_SOFTMMU) += cpu/ -devices-dirs-$(CONFIG_SOFTMMU) += display/ -devices-dirs-$(CONFIG_SOFTMMU) += dma/ -devices-dirs-$(CONFIG_SOFTMMU) += gpio/ -devices-dirs-$(CONFIG_SOFTMMU) += i2c/ -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/ -devices-dirs-$(CONFIG_SOFTMMU) += nvram/ -devices-dirs-$(CONFIG_SOFTMMU) += pci/ -devices-dirs-$(CONFIG_PCI) += pci-bridge/ pci-host/ -devices-dirs-$(CONFIG_SOFTMMU) += pcmcia/ -devices-dirs-$(CONFIG_SOFTMMU) += scsi/ -devices-dirs-$(CONFIG_SOFTMMU) += sd/ -devices-dirs-$(CONFIG_SOFTMMU) += ssi/ -devices-dirs-$(CONFIG_SOFTMMU) += timer/ -devices-dirs-$(CONFIG_TPM) += tpm/ -devices-dirs-$(CONFIG_SOFTMMU) += usb/ -devices-dirs-$(CONFIG_SOFTMMU) += vfio/ -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 deleted file mode 100644 index faee86c5c..000000000 --- a/qemu/hw/acpi/Makefile.objs +++ /dev/null @@ -1,8 +0,0 @@ -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 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 deleted file mode 100644 index d82131326..000000000 --- a/qemu/hw/acpi/acpi_interface.c +++ /dev/null @@ -1,16 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/acpi/acpi_dev_interface.h" -#include "qemu/module.h" - -static void register_types(void) -{ - static const TypeInfo acpi_dev_if_info = { - .name = TYPE_ACPI_DEVICE_IF, - .parent = TYPE_INTERFACE, - .class_size = sizeof(AcpiDeviceIfClass), - }; - - type_register_static(&acpi_dev_if_info); -} - -type_init(register_types) diff --git a/qemu/hw/acpi/aml-build.c b/qemu/hw/acpi/aml-build.c deleted file mode 100644 index ab89ca638..000000000 --- a/qemu/hw/acpi/aml-build.c +++ /dev/null @@ -1,1565 +0,0 @@ -/* Support for generating ACPI tables and passing them to Guests - * - * Copyright (C) 2015 Red Hat Inc - * - * Author: Michael S. Tsirkin - * Author: Igor Mammedov - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include -#include "hw/acpi/aml-build.h" -#include "qemu/bswap.h" -#include "qemu/bitops.h" -#include "hw/acpi/bios-linker-loader.h" - -static GArray *build_alloc_array(void) -{ - return g_array_new(false, true /* clear */, 1); -} - -static void build_free_array(GArray *array) -{ - g_array_free(array, true); -} - -static void build_prepend_byte(GArray *array, uint8_t val) -{ - g_array_prepend_val(array, val); -} - -static void build_append_byte(GArray *array, uint8_t val) -{ - g_array_append_val(array, val); -} - -static void build_append_array(GArray *array, GArray *val) -{ - g_array_append_vals(array, val->data, val->len); -} - -#define ACPI_NAMESEG_LEN 4 - -static void -build_append_nameseg(GArray *array, const char *seg) -{ - int len; - - len = strlen(seg); - assert(len <= ACPI_NAMESEG_LEN); - - g_array_append_vals(array, seg, len); - /* Pad up to ACPI_NAMESEG_LEN characters if necessary. */ - g_array_append_vals(array, "____", ACPI_NAMESEG_LEN - len); -} - -static void GCC_FMT_ATTR(2, 0) -build_append_namestringv(GArray *array, const char *format, va_list ap) -{ - char *s; - char **segs; - char **segs_iter; - int seg_count = 0; - - s = g_strdup_vprintf(format, ap); - segs = g_strsplit(s, ".", 0); - g_free(s); - - /* count segments */ - segs_iter = segs; - while (*segs_iter) { - ++segs_iter; - ++seg_count; - } - /* - * ACPI 5.0 spec: 20.2.2 Name Objects Encoding: - * "SegCount can be from 1 to 255" - */ - assert(seg_count > 0 && seg_count <= 255); - - /* handle RootPath || PrefixPath */ - s = *segs; - while (*s == '\\' || *s == '^') { - build_append_byte(array, *s); - ++s; - } - - switch (seg_count) { - case 1: - if (!*s) { - build_append_byte(array, 0x00); /* NullName */ - } else { - build_append_nameseg(array, s); - } - break; - - case 2: - build_append_byte(array, 0x2E); /* DualNamePrefix */ - build_append_nameseg(array, s); - build_append_nameseg(array, segs[1]); - break; - default: - build_append_byte(array, 0x2F); /* MultiNamePrefix */ - build_append_byte(array, seg_count); - - /* handle the 1st segment manually due to prefix/root path */ - build_append_nameseg(array, s); - - /* add the rest of segments */ - segs_iter = segs + 1; - while (*segs_iter) { - build_append_nameseg(array, *segs_iter); - ++segs_iter; - } - break; - } - g_strfreev(segs); -} - -GCC_FMT_ATTR(2, 3) -static void build_append_namestring(GArray *array, const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - build_append_namestringv(array, format, ap); - va_end(ap); -} - -/* 5.4 Definition Block Encoding */ -enum { - PACKAGE_LENGTH_1BYTE_SHIFT = 6, /* Up to 63 - use extra 2 bits. */ - PACKAGE_LENGTH_2BYTE_SHIFT = 4, - PACKAGE_LENGTH_3BYTE_SHIFT = 12, - PACKAGE_LENGTH_4BYTE_SHIFT = 20, -}; - -static void -build_prepend_package_length(GArray *package, unsigned length, bool incl_self) -{ - uint8_t byte; - unsigned length_bytes; - - if (length + 1 < (1 << PACKAGE_LENGTH_1BYTE_SHIFT)) { - length_bytes = 1; - } else if (length + 2 < (1 << PACKAGE_LENGTH_3BYTE_SHIFT)) { - length_bytes = 2; - } else if (length + 3 < (1 << PACKAGE_LENGTH_4BYTE_SHIFT)) { - length_bytes = 3; - } else { - length_bytes = 4; - } - - /* - * NamedField uses PkgLength encoding but it doesn't include length - * of PkgLength itself. - */ - if (incl_self) { - /* - * PkgLength is the length of the inclusive length of the data - * and PkgLength's length itself when used for terms with - * explitit length. - */ - length += length_bytes; - } - - switch (length_bytes) { - case 1: - byte = length; - build_prepend_byte(package, byte); - return; - case 4: - byte = length >> PACKAGE_LENGTH_4BYTE_SHIFT; - build_prepend_byte(package, byte); - length &= (1 << PACKAGE_LENGTH_4BYTE_SHIFT) - 1; - /* fall through */ - case 3: - byte = length >> PACKAGE_LENGTH_3BYTE_SHIFT; - build_prepend_byte(package, byte); - length &= (1 << PACKAGE_LENGTH_3BYTE_SHIFT) - 1; - /* fall through */ - case 2: - byte = length >> PACKAGE_LENGTH_2BYTE_SHIFT; - build_prepend_byte(package, byte); - length &= (1 << PACKAGE_LENGTH_2BYTE_SHIFT) - 1; - /* fall through */ - } - /* - * Most significant two bits of byte zero indicate how many following bytes - * are in PkgLength encoding. - */ - byte = ((length_bytes - 1) << PACKAGE_LENGTH_1BYTE_SHIFT) | length; - build_prepend_byte(package, byte); -} - -static void -build_append_pkg_length(GArray *array, unsigned length, bool incl_self) -{ - GArray *tmp = build_alloc_array(); - - build_prepend_package_length(tmp, length, incl_self); - build_append_array(array, tmp); - build_free_array(tmp); -} - -static void build_package(GArray *package, uint8_t op) -{ - build_prepend_package_length(package, package->len, true); - build_prepend_byte(package, op); -} - -static void build_extop_package(GArray *package, uint8_t op) -{ - build_package(package, op); - build_prepend_byte(package, 0x5B); /* ExtOpPrefix */ -} - -static void build_append_int_noprefix(GArray *table, uint64_t value, int size) -{ - int i; - - for (i = 0; i < size; ++i) { - build_append_byte(table, value & 0xFF); - value = value >> 8; - } -} - -static void build_append_int(GArray *table, uint64_t value) -{ - if (value == 0x00) { - build_append_byte(table, 0x00); /* ZeroOp */ - } else if (value == 0x01) { - build_append_byte(table, 0x01); /* OneOp */ - } else if (value <= 0xFF) { - build_append_byte(table, 0x0A); /* BytePrefix */ - build_append_int_noprefix(table, value, 1); - } else if (value <= 0xFFFF) { - build_append_byte(table, 0x0B); /* WordPrefix */ - build_append_int_noprefix(table, value, 2); - } else if (value <= 0xFFFFFFFF) { - build_append_byte(table, 0x0C); /* DWordPrefix */ - build_append_int_noprefix(table, value, 4); - } else { - build_append_byte(table, 0x0E); /* QWordPrefix */ - build_append_int_noprefix(table, value, 8); - } -} - -/* - * 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) -{ - Aml *var = g_new0(typeof(*var), 1); - - g_ptr_array_add(alloc_list, var); - var->block_flags = AML_NO_OPCODE; - var->buf = build_alloc_array(); - return var; -} - -static Aml *aml_opcode(uint8_t op) -{ - Aml *var = aml_alloc(); - - var->op = op; - var->block_flags = AML_OPCODE; - return var; -} - -static Aml *aml_bundle(uint8_t op, AmlBlockFlags flags) -{ - Aml *var = aml_alloc(); - - var->op = op; - var->block_flags = flags; - return var; -} - -static void aml_free(gpointer data, gpointer user_data) -{ - Aml *var = data; - build_free_array(var->buf); - g_free(var); -} - -Aml *init_aml_allocator(void) -{ - Aml *var; - - assert(!alloc_list); - alloc_list = g_ptr_array_new(); - var = aml_alloc(); - return var; -} - -void free_aml_allocator(void) -{ - g_ptr_array_foreach(alloc_list, aml_free, NULL); - g_ptr_array_free(alloc_list, true); - alloc_list = 0; -} - -/* pack data with DefBuffer encoding */ -static void build_buffer(GArray *array, uint8_t op) -{ - GArray *data = build_alloc_array(); - - build_append_int(data, array->len); - g_array_prepend_vals(array, data->data, data->len); - build_free_array(data); - build_package(array, op); -} - -void aml_append(Aml *parent_ctx, Aml *child) -{ - GArray *buf = build_alloc_array(); - build_append_array(buf, child->buf); - - switch (child->block_flags) { - case AML_OPCODE: - build_append_byte(parent_ctx->buf, child->op); - break; - case AML_EXT_PACKAGE: - build_extop_package(buf, child->op); - break; - case AML_PACKAGE: - build_package(buf, child->op); - break; - case AML_RES_TEMPLATE: - build_append_byte(buf, 0x79); /* EndTag */ - /* - * checksum operations are treated as succeeded if checksum - * field is zero. [ACPI Spec 1.0b, 6.4.2.8 End Tag] - */ - build_append_byte(buf, 0); - /* fall through, to pack resources in buffer */ - case AML_BUFFER: - build_buffer(buf, child->op); - break; - case AML_NO_OPCODE: - break; - default: - assert(0); - break; - } - build_append_array(parent_ctx->buf, buf); - build_free_array(buf); -} - -/* ACPI 1.0b: 16.2.5.1 Namespace Modifier Objects Encoding: DefScope */ -Aml *aml_scope(const char *name_format, ...) -{ - va_list ap; - Aml *var = aml_bundle(0x10 /* ScopeOp */, AML_PACKAGE); - va_start(ap, name_format); - build_append_namestringv(var->buf, name_format, ap); - va_end(ap); - return var; -} - -/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefReturn */ -Aml *aml_return(Aml *val) -{ - Aml *var = aml_opcode(0xA4 /* ReturnOp */); - aml_append(var, val); - return var; -} - -/* - * ACPI 1.0b: 16.2.3 Data Objects Encoding: - * encodes: ByteConst, WordConst, DWordConst, QWordConst, ZeroOp, OneOp - */ -Aml *aml_int(const uint64_t val) -{ - Aml *var = aml_alloc(); - build_append_int(var->buf, val); - return var; -} - -/* - * helper to construct NameString, which returns Aml object - * for using with aml_append or other aml_* terms - */ -Aml *aml_name(const char *name_format, ...) -{ - va_list ap; - Aml *var = aml_alloc(); - va_start(ap, name_format); - build_append_namestringv(var->buf, name_format, ap); - va_end(ap); - return var; -} - -/* ACPI 1.0b: 16.2.5.1 Namespace Modifier Objects Encoding: DefName */ -Aml *aml_name_decl(const char *name, Aml *val) -{ - Aml *var = aml_opcode(0x08 /* NameOp */); - build_append_namestring(var->buf, "%s", name); - aml_append(var, val); - return var; -} - -/* ACPI 1.0b: 16.2.6.1 Arg Objects Encoding */ -Aml *aml_arg(int pos) -{ - Aml *var; - uint8_t op = 0x68 /* ARG0 op */ + pos; - - assert(pos <= 6); - var = aml_opcode(op); - 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) -{ - Aml *var = aml_opcode(0x70 /* StoreOp */); - aml_append(var, val); - aml_append(var, target); - return var; -} - -/** - * 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(op); - aml_append(var, arg1); - aml_append(var, arg2); - 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 *dst) -{ - 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); - return var; -} - -/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefShiftLeft */ -Aml *aml_shiftleft(Aml *arg1, Aml *count) -{ - 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 *dst) -{ - return build_opcode_2arg_dst(0x7A /* ShiftRightOp */, arg1, count, dst); -} - -/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLLess */ -Aml *aml_lless(Aml *arg1, Aml *arg2) -{ - Aml *var = aml_opcode(0x95 /* LLessOp */); - aml_append(var, arg1); - aml_append(var, arg2); - return var; -} - -/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAdd */ -Aml *aml_add(Aml *arg1, Aml *arg2, Aml *dst) -{ - 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 */ -Aml *aml_increment(Aml *arg) -{ - Aml *var = aml_opcode(0x75 /* IncrementOp */); - aml_append(var, 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) -{ - return build_opcode_2arg_dst(0x88 /* IndexOp */, arg1, idx, NULL); -} - -/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefNotify */ -Aml *aml_notify(Aml *arg1, Aml *arg2) -{ - Aml *var = aml_opcode(0x86 /* NotifyOp */); - aml_append(var, arg1); - aml_append(var, arg2); - return var; -} - -/* 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(); - build_append_namestring(var->buf, "%s", method); - aml_append(var, arg1); - return var; -} - -/* helper to call method with 2 arguments */ -Aml *aml_call2(const char *method, Aml *arg1, Aml *arg2) -{ - Aml *var = aml_alloc(); - build_append_namestring(var->buf, "%s", method); - aml_append(var, arg1); - aml_append(var, arg2); - return var; -} - -/* helper to call method with 3 arguments */ -Aml *aml_call3(const char *method, Aml *arg1, Aml *arg2, Aml *arg3) -{ - Aml *var = aml_alloc(); - build_append_namestring(var->buf, "%s", method); - aml_append(var, arg1); - aml_append(var, arg2); - aml_append(var, arg3); - return var; -} - -/* helper to call method with 4 arguments */ -Aml *aml_call4(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4) -{ - Aml *var = aml_alloc(); - build_append_namestring(var->buf, "%s", method); - aml_append(var, arg1); - aml_append(var, arg2); - aml_append(var, arg3); - aml_append(var, arg4); - return var; -} - -/* - * 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) - */ -Aml *aml_memory32_fixed(uint32_t addr, uint32_t size, - AmlReadAndWrite read_and_write) -{ - Aml *var = aml_alloc(); - build_append_byte(var->buf, 0x86); /* Memory32Fixed Resource Descriptor */ - build_append_byte(var->buf, 9); /* Length, bits[7:0] value = 9 */ - build_append_byte(var->buf, 0); /* Length, bits[15:8] value = 0 */ - build_append_byte(var->buf, read_and_write); /* Write status, 1 rw 0 ro */ - - /* Range base address */ - build_append_byte(var->buf, extract32(addr, 0, 8)); /* bits[7:0] */ - build_append_byte(var->buf, extract32(addr, 8, 8)); /* bits[15:8] */ - build_append_byte(var->buf, extract32(addr, 16, 8)); /* bits[23:16] */ - build_append_byte(var->buf, extract32(addr, 24, 8)); /* bits[31:24] */ - - /* Range length */ - build_append_byte(var->buf, extract32(size, 0, 8)); /* bits[7:0] */ - build_append_byte(var->buf, extract32(size, 8, 8)); /* bits[15:8] */ - build_append_byte(var->buf, extract32(size, 16, 8)); /* bits[23:16] */ - build_append_byte(var->buf, extract32(size, 24, 8)); /* bits[31:24] */ - return var; -} - -/* - * ACPI 5.0: 6.4.3.6 Extended Interrupt Descriptor - * Type 1, Large Item Name 0x9 - */ -Aml *aml_interrupt(AmlConsumerAndProducer con_and_pro, - AmlLevelAndEdge level_and_edge, - AmlActiveHighAndLow high_and_low, AmlShared shared, - 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, 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, irq_count); /* Interrupt table length */ - - /* Interrupt Number List */ - for (i = 0; i < irq_count; i++) { - build_append_int_noprefix(var->buf, irq_list[i], 4); - } - return var; -} - -/* ACPI 1.0b: 6.4.2.5 I/O Port Descriptor */ -Aml *aml_io(AmlIODecode dec, uint16_t min_base, uint16_t max_base, - uint8_t aln, uint8_t len) -{ - Aml *var = aml_alloc(); - build_append_byte(var->buf, 0x47); /* IO port descriptor */ - build_append_byte(var->buf, dec); - build_append_byte(var->buf, min_base & 0xff); - build_append_byte(var->buf, (min_base >> 8) & 0xff); - build_append_byte(var->buf, max_base & 0xff); - build_append_byte(var->buf, (max_base >> 8) & 0xff); - build_append_byte(var->buf, aln); - build_append_byte(var->buf, len); - return var; -} - -/* - * ACPI 1.0b: 6.4.2.1.1 ASL Macro for IRQ Descriptor - * - * More verbose description at: - * ACPI 5.0: 19.5.64 IRQNoFlags (Interrupt Resource Descriptor Macro) - * 6.4.2.1 IRQ Descriptor - */ -Aml *aml_irq_no_flags(uint8_t irq) -{ - uint16_t irq_mask; - Aml *var = aml_alloc(); - - assert(irq < 16); - build_append_byte(var->buf, 0x22); /* IRQ descriptor 2 byte form */ - - irq_mask = 1U << irq; - build_append_byte(var->buf, irq_mask & 0xFF); /* IRQ mask bits[7:0] */ - build_append_byte(var->buf, irq_mask >> 8); /* IRQ mask bits[15:8] */ - return var; -} - -/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLNot */ -Aml *aml_lnot(Aml *arg) -{ - Aml *var = aml_opcode(0x92 /* LNotOp */); - aml_append(var, arg); - return var; -} - -/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLEqual */ -Aml *aml_equal(Aml *arg1, Aml *arg2) -{ - Aml *var = aml_opcode(0x93 /* LequalOp */); - aml_append(var, arg1); - aml_append(var, 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) -{ - Aml *var = aml_bundle(0xA0 /* IfOp */, AML_PACKAGE); - aml_append(var, predicate); - return var; -} - -/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefElse */ -Aml *aml_else(void) -{ - Aml *var = aml_bundle(0xA1 /* ElseOp */, AML_PACKAGE); - return var; -} - -/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefWhile */ -Aml *aml_while(Aml *predicate) -{ - Aml *var = aml_bundle(0xA2 /* WhileOp */, AML_PACKAGE); - aml_append(var, predicate); - return var; -} - -/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefMethod */ -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, methodflags); /* MethodFlags: ArgCount */ - return var; -} - -/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefDevice */ -Aml *aml_device(const char *name_format, ...) -{ - va_list ap; - Aml *var = aml_bundle(0x82 /* DeviceOp */, AML_EXT_PACKAGE); - va_start(ap, name_format); - build_append_namestringv(var->buf, name_format, ap); - va_end(ap); - return var; -} - -/* ACPI 1.0b: 6.4.1 ASL Macros for Resource Descriptors */ -Aml *aml_resource_template(void) -{ - /* ResourceTemplate is a buffer of Resources with EndTag at the end */ - Aml *var = aml_bundle(0x11 /* BufferOp */, AML_RES_TEMPLATE); - return var; -} - -/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefBuffer - * Pass byte_list as NULL to request uninitialized buffer to reserve space. - */ -Aml *aml_buffer(int buffer_size, uint8_t *byte_list) -{ - int i; - Aml *var = aml_bundle(0x11 /* BufferOp */, AML_BUFFER); - - for (i = 0; i < buffer_size; i++) { - if (byte_list == NULL) { - build_append_byte(var->buf, 0x0); - } else { - build_append_byte(var->buf, byte_list[i]); - } - } - - return var; -} - -/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefPackage */ -Aml *aml_package(uint8_t num_elements) -{ - Aml *var = aml_bundle(0x12 /* PackageOp */, AML_PACKAGE); - build_append_byte(var->buf, num_elements); - return var; -} - -/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefOpRegion */ -Aml *aml_operation_region(const char *name, AmlRegionSpace rs, - 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); - aml_append(var, offset); - build_append_int(var->buf, len); - return var; -} - -/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: NamedField */ -Aml *aml_named_field(const char *name, unsigned length) -{ - Aml *var = aml_alloc(); - build_append_nameseg(var->buf, name); - build_append_pkg_length(var->buf, length, false); - return var; -} - -/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: ReservedField */ -Aml *aml_reserved_field(unsigned length) -{ - Aml *var = aml_alloc(); - /* ReservedField := 0x00 PkgLength */ - build_append_byte(var->buf, 0x00); - build_append_pkg_length(var->buf, length, false); - return var; -} - -/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefField */ -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; -} - -static -Aml *create_field_common(int opcode, Aml *srcbuf, Aml *index, const char *name) -{ - 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, ...) -{ - Aml *var = aml_opcode(0x0D /* StringPrefix */); - va_list ap; - char *s; - int len; - - va_start(ap, name_format); - len = g_vasprintf(&s, name_format, ap); - va_end(ap); - - g_array_append_vals(var->buf, s, len + 1); - g_free(s); - - return var; -} - -/* ACPI 1.0b: 16.2.6.2 Local Objects Encoding */ -Aml *aml_local(int num) -{ - Aml *var; - uint8_t op = 0x60 /* Local0Op */ + num; - - assert(num <= 7); - var = aml_opcode(op); - return var; -} - -/* ACPI 2.0a: 17.2.2 Data Objects Encoding: DefVarPackage */ -Aml *aml_varpackage(uint32_t num_elements) -{ - Aml *var = aml_bundle(0x13 /* VarPackageOp */, AML_PACKAGE); - build_append_int(var->buf, num_elements); - return var; -} - -/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefProcessor */ -Aml *aml_processor(uint8_t proc_id, uint32_t pblk_addr, uint8_t pblk_len, - const char *name_format, ...) -{ - va_list ap; - Aml *var = aml_bundle(0x83 /* ProcessorOp */, AML_EXT_PACKAGE); - va_start(ap, name_format); - build_append_namestringv(var->buf, name_format, ap); - va_end(ap); - build_append_byte(var->buf, proc_id); /* ProcID */ - build_append_int_noprefix(var->buf, pblk_addr, sizeof(pblk_addr)); - build_append_byte(var->buf, pblk_len); /* PblkLen */ - return var; -} - -static uint8_t Hex2Digit(char c) -{ - if (c >= 'A') { - return c - 'A' + 10; - } - - return c - '0'; -} - -/* ACPI 1.0b: 15.2.3.6.4.1 EISAID Macro - Convert EISA ID String To Integer */ -Aml *aml_eisaid(const char *str) -{ - Aml *var = aml_alloc(); - uint32_t id; - - g_assert(strlen(str) == 7); - id = (str[0] - 0x40) << 26 | - (str[1] - 0x40) << 21 | - (str[2] - 0x40) << 16 | - Hex2Digit(str[3]) << 12 | - Hex2Digit(str[4]) << 8 | - Hex2Digit(str[5]) << 4 | - Hex2Digit(str[6]); - - build_append_byte(var->buf, 0x0C); /* DWordPrefix */ - build_append_int_noprefix(var->buf, bswap32(id), sizeof(id)); - return var; -} - -/* ACPI 1.0b: 6.4.3.5.5 Word Address Space Descriptor: bytes 3-5 */ -static Aml *aml_as_desc_header(AmlResourceType type, AmlMinFixed min_fixed, - AmlMaxFixed max_fixed, AmlDecode dec, - uint8_t type_flags) -{ - uint8_t flags = max_fixed | min_fixed | dec; - Aml *var = aml_alloc(); - - build_append_byte(var->buf, type); - build_append_byte(var->buf, flags); - build_append_byte(var->buf, type_flags); /* Type Specific Flags */ - return var; -} - -/* ACPI 1.0b: 6.4.3.5.5 Word Address Space Descriptor */ -static Aml *aml_word_as_desc(AmlResourceType type, AmlMinFixed min_fixed, - AmlMaxFixed max_fixed, AmlDecode dec, - uint16_t addr_gran, uint16_t addr_min, - uint16_t addr_max, uint16_t addr_trans, - uint16_t len, uint8_t type_flags) -{ - Aml *var = aml_alloc(); - - build_append_byte(var->buf, 0x88); /* Word Address Space Descriptor */ - /* minimum length since we do not encode optional fields */ - build_append_byte(var->buf, 0x0D); - build_append_byte(var->buf, 0x0); - - aml_append(var, - aml_as_desc_header(type, min_fixed, max_fixed, dec, type_flags)); - build_append_int_noprefix(var->buf, addr_gran, sizeof(addr_gran)); - build_append_int_noprefix(var->buf, addr_min, sizeof(addr_min)); - build_append_int_noprefix(var->buf, addr_max, sizeof(addr_max)); - build_append_int_noprefix(var->buf, addr_trans, sizeof(addr_trans)); - build_append_int_noprefix(var->buf, len, sizeof(len)); - return var; -} - -/* ACPI 1.0b: 6.4.3.5.3 DWord Address Space Descriptor */ -static Aml *aml_dword_as_desc(AmlResourceType type, AmlMinFixed min_fixed, - AmlMaxFixed max_fixed, AmlDecode dec, - uint32_t addr_gran, uint32_t addr_min, - uint32_t addr_max, uint32_t addr_trans, - uint32_t len, uint8_t type_flags) -{ - Aml *var = aml_alloc(); - - build_append_byte(var->buf, 0x87); /* DWord Address Space Descriptor */ - /* minimum length since we do not encode optional fields */ - build_append_byte(var->buf, 23); - build_append_byte(var->buf, 0x0); - - - aml_append(var, - aml_as_desc_header(type, min_fixed, max_fixed, dec, type_flags)); - build_append_int_noprefix(var->buf, addr_gran, sizeof(addr_gran)); - build_append_int_noprefix(var->buf, addr_min, sizeof(addr_min)); - build_append_int_noprefix(var->buf, addr_max, sizeof(addr_max)); - build_append_int_noprefix(var->buf, addr_trans, sizeof(addr_trans)); - build_append_int_noprefix(var->buf, len, sizeof(len)); - return var; -} - -/* ACPI 1.0b: 6.4.3.5.1 QWord Address Space Descriptor */ -static Aml *aml_qword_as_desc(AmlResourceType type, AmlMinFixed min_fixed, - AmlMaxFixed max_fixed, AmlDecode dec, - uint64_t addr_gran, uint64_t addr_min, - uint64_t addr_max, uint64_t addr_trans, - uint64_t len, uint8_t type_flags) -{ - Aml *var = aml_alloc(); - - build_append_byte(var->buf, 0x8A); /* QWord Address Space Descriptor */ - /* minimum length since we do not encode optional fields */ - build_append_byte(var->buf, 0x2B); - build_append_byte(var->buf, 0x0); - - aml_append(var, - aml_as_desc_header(type, min_fixed, max_fixed, dec, type_flags)); - build_append_int_noprefix(var->buf, addr_gran, sizeof(addr_gran)); - build_append_int_noprefix(var->buf, addr_min, sizeof(addr_min)); - build_append_int_noprefix(var->buf, addr_max, sizeof(addr_max)); - build_append_int_noprefix(var->buf, addr_trans, sizeof(addr_trans)); - build_append_int_noprefix(var->buf, len, sizeof(len)); - return var; -} - -/* - * ACPI 1.0b: 6.4.3.5.6 ASL Macros for WORD Address Descriptor - * - * More verbose description at: - * ACPI 5.0: 19.5.141 WordBusNumber (Word Bus Number Resource Descriptor Macro) - */ -Aml *aml_word_bus_number(AmlMinFixed min_fixed, AmlMaxFixed max_fixed, - AmlDecode dec, uint16_t addr_gran, - uint16_t addr_min, uint16_t addr_max, - uint16_t addr_trans, uint16_t len) - -{ - return aml_word_as_desc(AML_BUS_NUMBER_RANGE, min_fixed, max_fixed, dec, - addr_gran, addr_min, addr_max, addr_trans, len, 0); -} - -/* - * ACPI 1.0b: 6.4.3.5.6 ASL Macros for WORD Address Descriptor - * - * More verbose description at: - * ACPI 5.0: 19.5.142 WordIO (Word IO Resource Descriptor Macro) - */ -Aml *aml_word_io(AmlMinFixed min_fixed, AmlMaxFixed max_fixed, - AmlDecode dec, AmlISARanges isa_ranges, - uint16_t addr_gran, uint16_t addr_min, - uint16_t addr_max, uint16_t addr_trans, - uint16_t len) - -{ - return aml_word_as_desc(AML_IO_RANGE, min_fixed, max_fixed, dec, - addr_gran, addr_min, addr_max, addr_trans, len, - isa_ranges); -} - -/* - * ACPI 1.0b: 6.4.3.5.4 ASL Macros for DWORD Address Descriptor - * - * More verbose description at: - * ACPI 5.0: 19.5.33 DWordIO (DWord IO Resource Descriptor Macro) - */ -Aml *aml_dword_io(AmlMinFixed min_fixed, AmlMaxFixed max_fixed, - AmlDecode dec, AmlISARanges isa_ranges, - uint32_t addr_gran, uint32_t addr_min, - uint32_t addr_max, uint32_t addr_trans, - uint32_t len) - -{ - return aml_dword_as_desc(AML_IO_RANGE, min_fixed, max_fixed, dec, - addr_gran, addr_min, addr_max, addr_trans, len, - isa_ranges); -} - -/* - * ACPI 1.0b: 6.4.3.5.4 ASL Macros for DWORD Address Space Descriptor - * - * More verbose description at: - * ACPI 5.0: 19.5.34 DWordMemory (DWord Memory Resource Descriptor Macro) - */ -Aml *aml_dword_memory(AmlDecode dec, AmlMinFixed min_fixed, - AmlMaxFixed max_fixed, AmlCacheable cacheable, - AmlReadAndWrite read_and_write, - uint32_t addr_gran, uint32_t addr_min, - uint32_t addr_max, uint32_t addr_trans, - uint32_t len) -{ - uint8_t flags = read_and_write | (cacheable << 1); - - return aml_dword_as_desc(AML_MEMORY_RANGE, min_fixed, max_fixed, - dec, addr_gran, addr_min, addr_max, - addr_trans, len, flags); -} - -/* - * ACPI 1.0b: 6.4.3.5.2 ASL Macros for QWORD Address Space Descriptor - * - * More verbose description at: - * ACPI 5.0: 19.5.102 QWordMemory (QWord Memory Resource Descriptor Macro) - */ -Aml *aml_qword_memory(AmlDecode dec, AmlMinFixed min_fixed, - AmlMaxFixed max_fixed, AmlCacheable cacheable, - AmlReadAndWrite read_and_write, - uint64_t addr_gran, uint64_t addr_min, - uint64_t addr_max, uint64_t addr_trans, - uint64_t len) -{ - uint8_t flags = read_and_write | (cacheable << 1); - - return aml_qword_as_desc(AML_MEMORY_RANGE, min_fixed, max_fixed, - dec, addr_gran, addr_min, addr_max, - 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; - - hi = Hex2Digit(src[0]); - assert(hi >= 0); - assert(hi <= 15); - - lo = Hex2Digit(src[1]); - assert(lo >= 0); - assert(lo <= 15); - return (hi << 4) | lo; -} - -/* - * ACPI 3.0: 17.5.124 ToUUID (Convert String to UUID Macro) - * e.g. UUID: aabbccdd-eeff-gghh-iijj-kkllmmnnoopp - * call aml_touuid("aabbccdd-eeff-gghh-iijj-kkllmmnnoopp"); - */ -Aml *aml_touuid(const char *uuid) -{ - Aml *var = aml_bundle(0x11 /* BufferOp */, AML_BUFFER); - - assert(strlen(uuid) == 36); - assert(uuid[8] == '-'); - assert(uuid[13] == '-'); - assert(uuid[18] == '-'); - assert(uuid[23] == '-'); - - build_append_byte(var->buf, Hex2Byte(uuid + 6)); /* dd - at offset 00 */ - build_append_byte(var->buf, Hex2Byte(uuid + 4)); /* cc - at offset 01 */ - build_append_byte(var->buf, Hex2Byte(uuid + 2)); /* bb - at offset 02 */ - build_append_byte(var->buf, Hex2Byte(uuid + 0)); /* aa - at offset 03 */ - - build_append_byte(var->buf, Hex2Byte(uuid + 11)); /* ff - at offset 04 */ - build_append_byte(var->buf, Hex2Byte(uuid + 9)); /* ee - at offset 05 */ - - build_append_byte(var->buf, Hex2Byte(uuid + 16)); /* hh - at offset 06 */ - build_append_byte(var->buf, Hex2Byte(uuid + 14)); /* gg - at offset 07 */ - - build_append_byte(var->buf, Hex2Byte(uuid + 19)); /* ii - at offset 08 */ - build_append_byte(var->buf, Hex2Byte(uuid + 21)); /* jj - at offset 09 */ - - build_append_byte(var->buf, Hex2Byte(uuid + 24)); /* kk - at offset 10 */ - build_append_byte(var->buf, Hex2Byte(uuid + 26)); /* ll - at offset 11 */ - build_append_byte(var->buf, Hex2Byte(uuid + 28)); /* mm - at offset 12 */ - build_append_byte(var->buf, Hex2Byte(uuid + 30)); /* nn - at offset 13 */ - build_append_byte(var->buf, Hex2Byte(uuid + 32)); /* oo - at offset 14 */ - build_append_byte(var->buf, Hex2Byte(uuid + 34)); /* pp - at offset 15 */ - - return var; -} - -/* - * ACPI 2.0b: 16.2.3.6.4.3 Unicode Macro (Convert Ascii String To Unicode) - */ -Aml *aml_unicode(const char *str) -{ - int i = 0; - Aml *var = aml_bundle(0x11 /* BufferOp */, AML_BUFFER); - - do { - build_append_byte(var->buf, str[i]); - build_append_byte(var->buf, 0); - i++; - } while (i <= strlen(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, - const char *oem_id, const char *oem_table_id) -{ - memcpy(&h->signature, sig, 4); - h->length = cpu_to_le32(len); - h->revision = rev; - - 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, h, len, &h->checksum); -} - -void *acpi_data_push(GArray *table_data, unsigned size) -{ - unsigned off = table_data->len; - g_array_set_size(table_data, off + size); - return table_data->data + off; -} - -unsigned acpi_data_len(GArray *table) -{ - assert(g_array_get_element_size(table) == 1); - return table->len; -} - -void acpi_add_table(GArray *table_offsets, GArray *table_data) -{ - uint32_t offset = cpu_to_le32(table_data->len); - g_array_append_val(table_offsets, offset); -} - -void acpi_build_tables_init(AcpiBuildTables *tables) -{ - tables->rsdp = g_array_new(false, true /* clear */, 1); - tables->table_data = g_array_new(false, true /* clear */, 1); - tables->tcpalog = g_array_new(false, true /* clear */, 1); - tables->linker = bios_linker_loader_init(); -} - -void acpi_build_tables_cleanup(AcpiBuildTables *tables, bool mfre) -{ - void *linker_data = bios_linker_loader_cleanup(tables->linker); - g_free(linker_data); - g_array_free(tables->rsdp, true); - g_array_free(tables->table_data, true); - g_array_free(tables->tcpalog, mfre); -} - -/* Build rsdt table */ -void -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; - int i; - const int table_data_len = (sizeof(uint32_t) * table_offsets->len); - - rsdt_len = sizeof(*rsdt) + table_data_len; - rsdt = acpi_data_push(table_data, rsdt_len); - memcpy(rsdt->table_offset_entry, table_offsets->data, table_data_len); - for (i = 0; i < table_offsets->len; ++i) { - /* rsdt->table_offset_entry to be filled by Guest linker */ - bios_linker_loader_add_pointer(linker, - ACPI_BUILD_TABLE_FILE, - ACPI_BUILD_TABLE_FILE, - table_data, &rsdt->table_offset_entry[i], - sizeof(uint32_t)); - } - build_header(linker, table_data, - (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 deleted file mode 100644 index 5153ab151..000000000 --- a/qemu/hw/acpi/bios-linker-loader.c +++ /dev/null @@ -1,240 +0,0 @@ -/* Dynamic linker/loader of ACPI tables - * - * Copyright (C) 2013 Red Hat Inc - * - * Author: Michael S. Tsirkin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "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 { - uint32_t command; - union { - /* - * COMMAND_ALLOCATE - allocate a table from @alloc.file - * subject to @alloc.align alignment (must be power of 2) - * and @alloc.zone (can be HIGH or FSEG) requirements. - * - * Must appear exactly once for each file, and before - * this file is referenced by any other command. - */ - struct { - char file[BIOS_LINKER_LOADER_FILESZ]; - uint32_t align; - uint8_t zone; - } alloc; - - /* - * COMMAND_ADD_POINTER - patch the table (originating from - * @dest_file) at @pointer.offset, by adding a pointer to the table - * originating from @src_file. 1,2,4 or 8 byte unsigned - * addition is used depending on @pointer.size. - */ - struct { - char dest_file[BIOS_LINKER_LOADER_FILESZ]; - char src_file[BIOS_LINKER_LOADER_FILESZ]; - uint32_t offset; - uint8_t size; - } pointer; - - /* - * COMMAND_ADD_CHECKSUM - calculate checksum of the range specified by - * @cksum_start and @cksum_length fields, - * and then add the value at @cksum.offset. - * Checksum simply sums -X for each byte X in the range - * using 8-bit math. - */ - struct { - char file[BIOS_LINKER_LOADER_FILESZ]; - uint32_t offset; - uint32_t start; - uint32_t length; - } cksum; - - /* padding */ - char pad[124]; - }; -} QEMU_PACKED; -typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry; - -enum { - BIOS_LINKER_LOADER_COMMAND_ALLOCATE = 0x1, - BIOS_LINKER_LOADER_COMMAND_ADD_POINTER = 0x2, - BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM = 0x3, -}; - -enum { - BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH = 0x1, - 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); -} - -/* Free linker wrapper and return the linker array. */ -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, - bool alloc_fseg) -{ - 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 = 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, - 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_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, - GArray *table, void *pointer, - uint8_t pointer_size) -{ - BiosLinkerLoaderEntry entry; - 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, - sizeof entry.pointer.dest_file - 1); - strncpy(entry.pointer.src_file, src_file, - sizeof entry.pointer.src_file - 1); - entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_POINTER); - entry.pointer.offset = cpu_to_le32(offset); - entry.pointer.size = pointer_size; - assert(pointer_size == 1 || pointer_size == 2 || - pointer_size == 4 || pointer_size == 8); - - g_array_append_vals(linker, &entry, sizeof entry); -} diff --git a/qemu/hw/acpi/core.c b/qemu/hw/acpi/core.c deleted file mode 100644 index 6a2f45214..000000000 --- a/qemu/hw/acpi/core.c +++ /dev/null @@ -1,717 +0,0 @@ -/* - * ACPI implementation - * - * 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, see - * - * 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" -#include "hw/acpi/acpi.h" -#include "hw/nvram/fw_cfg.h" -#include "qemu/config-file.h" -#include "qapi/opts-visitor.h" -#include "qapi-visit.h" -#include "qapi-event.h" - -struct acpi_table_header { - uint16_t _length; /* our length, not actual part of the hdr */ - /* allows easier parsing for fw_cfg clients */ - char sig[4]; /* ACPI signature (4 ASCII characters) */ - uint32_t length; /* Length of table, in bytes, including header */ - uint8_t revision; /* ACPI Specification minor version # */ - uint8_t checksum; /* To make sum of entire table == 0 */ - char oem_id[6]; /* OEM identification */ - char oem_table_id[8]; /* OEM table identification */ - uint32_t oem_revision; /* OEM revision number */ - char asl_compiler_id[4]; /* ASL compiler vendor ID */ - uint32_t asl_compiler_revision; /* ASL compiler revision number */ -} QEMU_PACKED; - -#define ACPI_TABLE_HDR_SIZE sizeof(struct acpi_table_header) -#define ACPI_TABLE_PFX_SIZE sizeof(uint16_t) /* size of the extra prefix */ - -static const char unsigned dfl_hdr[ACPI_TABLE_HDR_SIZE - ACPI_TABLE_PFX_SIZE] = - "QEMU\0\0\0\0\1\0" /* sig (4), len(4), revno (1), csum (1) */ - "QEMUQEQEMUQEMU\1\0\0\0" /* OEM id (6), table (8), revno (4) */ - "QEMU\1\0\0\0" /* ASL compiler ID (4), version (4) */ - ; - -char unsigned *acpi_tables; -size_t acpi_tables_len; - -static QemuOptsList qemu_acpi_opts = { - .name = "acpi", - .implied_opt_name = "data", - .head = QTAILQ_HEAD_INITIALIZER(qemu_acpi_opts.head), - .desc = { { 0 } } /* validated with OptsVisitor */ -}; - -static void acpi_register_config(void) -{ - qemu_add_opts(&qemu_acpi_opts); -} - -opts_init(acpi_register_config); - -static int acpi_checksum(const uint8_t *data, int len) -{ - int sum, i; - sum = 0; - for (i = 0; i < len; i++) { - sum += data[i]; - } - return (-sum) & 0xff; -} - - -/* Install a copy of the ACPI table specified in @blob. - * - * If @has_header is set, @blob starts with the System Description Table Header - * structure. Otherwise, "dfl_hdr" is prepended. In any case, each header field - * is optionally overwritten from @hdrs. - * - * It is valid to call this function with - * (@blob == NULL && bloblen == 0 && !has_header). - * - * @hdrs->file and @hdrs->data are ignored. - * - * SIZE_MAX is considered "infinity" in this function. - * - * The number of tables that can be installed is not limited, but the 16-bit - * counter at the beginning of "acpi_tables" wraps around after UINT16_MAX. - */ -static void acpi_table_install(const char unsigned *blob, size_t bloblen, - bool has_header, - const struct AcpiTableOptions *hdrs, - Error **errp) -{ - size_t body_start; - const char unsigned *hdr_src; - size_t body_size, acpi_payload_size; - struct acpi_table_header *ext_hdr; - unsigned changed_fields; - - /* Calculate where the ACPI table body starts within the blob, plus where - * to copy the ACPI table header from. - */ - if (has_header) { - /* _length | ACPI header in blob | blob body - * ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ - * ACPI_TABLE_PFX_SIZE sizeof dfl_hdr body_size - * == body_start - * - * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - * acpi_payload_size == bloblen - */ - body_start = sizeof dfl_hdr; - - if (bloblen < body_start) { - error_setg(errp, "ACPI table claiming to have header is too " - "short, available: %zu, expected: %zu", bloblen, - body_start); - return; - } - hdr_src = blob; - } else { - /* _length | ACPI header in template | blob body - * ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^ - * ACPI_TABLE_PFX_SIZE sizeof dfl_hdr body_size - * == bloblen - * - * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - * acpi_payload_size - */ - body_start = 0; - hdr_src = dfl_hdr; - } - body_size = bloblen - body_start; - acpi_payload_size = sizeof dfl_hdr + body_size; - - if (acpi_payload_size > UINT16_MAX) { - error_setg(errp, "ACPI table too big, requested: %zu, max: %u", - acpi_payload_size, (unsigned)UINT16_MAX); - return; - } - - /* We won't fail from here on. Initialize / extend the globals. */ - if (acpi_tables == NULL) { - acpi_tables_len = sizeof(uint16_t); - acpi_tables = g_malloc0(acpi_tables_len); - } - - acpi_tables = g_realloc(acpi_tables, acpi_tables_len + - ACPI_TABLE_PFX_SIZE + - sizeof dfl_hdr + body_size); - - ext_hdr = (struct acpi_table_header *)(acpi_tables + acpi_tables_len); - acpi_tables_len += ACPI_TABLE_PFX_SIZE; - - memcpy(acpi_tables + acpi_tables_len, hdr_src, sizeof dfl_hdr); - acpi_tables_len += sizeof dfl_hdr; - - if (blob != NULL) { - memcpy(acpi_tables + acpi_tables_len, blob + body_start, body_size); - acpi_tables_len += body_size; - } - - /* increase number of tables */ - stw_le_p(acpi_tables, lduw_le_p(acpi_tables) + 1u); - - /* Update the header fields. The strings need not be NUL-terminated. */ - changed_fields = 0; - ext_hdr->_length = cpu_to_le16(acpi_payload_size); - - if (hdrs->has_sig) { - strncpy(ext_hdr->sig, hdrs->sig, sizeof ext_hdr->sig); - ++changed_fields; - } - - if (has_header && le32_to_cpu(ext_hdr->length) != acpi_payload_size) { - fprintf(stderr, - "warning: ACPI table has wrong length, header says " - "%" PRIu32 ", actual size %zu bytes\n", - le32_to_cpu(ext_hdr->length), acpi_payload_size); - } - ext_hdr->length = cpu_to_le32(acpi_payload_size); - - if (hdrs->has_rev) { - ext_hdr->revision = hdrs->rev; - ++changed_fields; - } - - ext_hdr->checksum = 0; - - if (hdrs->has_oem_id) { - strncpy(ext_hdr->oem_id, hdrs->oem_id, sizeof ext_hdr->oem_id); - ++changed_fields; - } - if (hdrs->has_oem_table_id) { - strncpy(ext_hdr->oem_table_id, hdrs->oem_table_id, - sizeof ext_hdr->oem_table_id); - ++changed_fields; - } - if (hdrs->has_oem_rev) { - ext_hdr->oem_revision = cpu_to_le32(hdrs->oem_rev); - ++changed_fields; - } - if (hdrs->has_asl_compiler_id) { - strncpy(ext_hdr->asl_compiler_id, hdrs->asl_compiler_id, - sizeof ext_hdr->asl_compiler_id); - ++changed_fields; - } - if (hdrs->has_asl_compiler_rev) { - ext_hdr->asl_compiler_revision = cpu_to_le32(hdrs->asl_compiler_rev); - ++changed_fields; - } - - if (!has_header && changed_fields == 0) { - fprintf(stderr, "warning: ACPI table: no headers are specified\n"); - } - - /* recalculate checksum */ - ext_hdr->checksum = acpi_checksum((const char unsigned *)ext_hdr + - ACPI_TABLE_PFX_SIZE, acpi_payload_size); -} - -void acpi_table_add(const QemuOpts *opts, Error **errp) -{ - AcpiTableOptions *hdrs = NULL; - Error *err = NULL; - char **pathnames = NULL; - char **cur; - size_t bloblen = 0; - char unsigned *blob = NULL; - - { - OptsVisitor *ov; - - ov = opts_visitor_new(opts); - visit_type_AcpiTableOptions(opts_get_visitor(ov), NULL, &hdrs, &err); - opts_visitor_cleanup(ov); - } - - if (err) { - goto out; - } - if (hdrs->has_file == hdrs->has_data) { - error_setg(&err, "'-acpitable' requires one of 'data' or 'file'"); - goto out; - } - - pathnames = g_strsplit(hdrs->has_file ? hdrs->file : hdrs->data, ":", 0); - if (pathnames == NULL || pathnames[0] == NULL) { - error_setg(&err, "'-acpitable' requires at least one pathname"); - goto out; - } - - /* now read in the data files, reallocating buffer as needed */ - for (cur = pathnames; *cur; ++cur) { - int fd = open(*cur, O_RDONLY | O_BINARY); - - if (fd < 0) { - error_setg(&err, "can't open file %s: %s", *cur, strerror(errno)); - goto out; - } - - for (;;) { - char unsigned data[8192]; - ssize_t r; - - r = read(fd, data, sizeof data); - if (r == 0) { - break; - } else if (r > 0) { - blob = g_realloc(blob, bloblen + r); - memcpy(blob + bloblen, data, r); - bloblen += r; - } else if (errno != EINTR) { - error_setg(&err, "can't read file %s: %s", - *cur, strerror(errno)); - close(fd); - goto out; - } - } - - close(fd); - } - - acpi_table_install(blob, bloblen, hdrs->has_file, hdrs, &err); - -out: - g_free(blob); - g_strfreev(pathnames); - qapi_free_AcpiTableOptions(hdrs); - - error_propagate(errp, err); -} - -static bool acpi_table_builtin = false; - -void acpi_table_add_builtin(const QemuOpts *opts, Error **errp) -{ - acpi_table_builtin = true; - acpi_table_add(opts, errp); -} - -unsigned acpi_table_len(void *current) -{ - struct acpi_table_header *hdr = current - sizeof(hdr->_length); - return hdr->_length; -} - -static -void *acpi_table_hdr(void *h) -{ - struct acpi_table_header *hdr = h; - return &hdr->sig; -} - -uint8_t *acpi_table_first(void) -{ - if (acpi_table_builtin || !acpi_tables) { - return NULL; - } - return acpi_table_hdr(acpi_tables + ACPI_TABLE_PFX_SIZE); -} - -uint8_t *acpi_table_next(uint8_t *current) -{ - uint8_t *next = current + acpi_table_len(current); - - if (next - acpi_tables >= acpi_tables_len) { - return NULL; - } else { - return acpi_table_hdr(next); - } -} - -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); - WakeupReason *reason = data; - - switch (*reason) { - case QEMU_WAKEUP_REASON_RTC: - ar->pm1.evt.sts |= - (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_RT_CLOCK_STATUS); - break; - case QEMU_WAKEUP_REASON_PMTIMER: - ar->pm1.evt.sts |= - (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_TIMER_STATUS); - break; - case QEMU_WAKEUP_REASON_OTHER: - /* ACPI_BITMASK_WAKE_STATUS should be set on resume. - Pretend that resume was caused by power button */ - ar->pm1.evt.sts |= - (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_POWER_BUTTON_STATUS); - break; - default: - break; - } -} - -/* ACPI PM1a EVT */ -uint16_t acpi_pm1_evt_get_sts(ACPIREGS *ar) -{ - /* Compare ns-clock, not PM timer ticks, because - 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, - NANOSECONDS_PER_SECOND, PM_TIMER_FREQUENCY)) { - ar->pm1.evt.sts |= ACPI_BITMASK_TIMER_STATUS; - } - return ar->pm1.evt.sts; -} - -static void acpi_pm1_evt_write_sts(ACPIREGS *ar, uint16_t val) -{ - uint16_t pm1_sts = acpi_pm1_evt_get_sts(ar); - if (pm1_sts & val & ACPI_BITMASK_TIMER_STATUS) { - /* if TMRSTS is reset, then compute the new overflow time */ - acpi_pm_tmr_calc_overflow_time(ar); - } - ar->pm1.evt.sts &= ~val; -} - -static void acpi_pm1_evt_write_en(ACPIREGS *ar, uint16_t val) -{ - ar->pm1.evt.en = val; - qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC, - val & ACPI_BITMASK_RT_CLOCK_ENABLE); - qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER, - val & ACPI_BITMASK_TIMER_ENABLE); -} - -void acpi_pm1_evt_power_down(ACPIREGS *ar) -{ - if (ar->pm1.evt.en & ACPI_BITMASK_POWER_BUTTON_ENABLE) { - ar->pm1.evt.sts |= ACPI_BITMASK_POWER_BUTTON_STATUS; - ar->tmr.update_sci(ar); - } -} - -void acpi_pm1_evt_reset(ACPIREGS *ar) -{ - ar->pm1.evt.sts = 0; - ar->pm1.evt.en = 0; - qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC, 0); - qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER, 0); -} - -static uint64_t acpi_pm_evt_read(void *opaque, hwaddr addr, unsigned width) -{ - ACPIREGS *ar = opaque; - switch (addr) { - case 0: - return acpi_pm1_evt_get_sts(ar); - case 2: - return ar->pm1.evt.en; - default: - return 0; - } -} - -static void acpi_pm_evt_write(void *opaque, hwaddr addr, uint64_t val, - unsigned width) -{ - ACPIREGS *ar = opaque; - switch (addr) { - case 0: - acpi_pm1_evt_write_sts(ar, val); - ar->pm1.evt.update_sci(ar); - break; - case 2: - acpi_pm1_evt_write_en(ar, val); - ar->pm1.evt.update_sci(ar); - break; - } -} - -static const MemoryRegionOps acpi_pm_evt_ops = { - .read = acpi_pm_evt_read, - .write = acpi_pm_evt_write, - .valid.min_access_size = 2, - .valid.max_access_size = 2, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -void acpi_pm1_evt_init(ACPIREGS *ar, acpi_update_sci_fn update_sci, - MemoryRegion *parent) -{ - ar->pm1.evt.update_sci = update_sci; - memory_region_init_io(&ar->pm1.evt.io, memory_region_owner(parent), - &acpi_pm_evt_ops, ar, "acpi-evt", 4); - memory_region_add_subregion(parent, 0, &ar->pm1.evt.io); -} - -/* ACPI PM_TMR */ -void acpi_pm_tmr_update(ACPIREGS *ar, bool enable) -{ - int64_t expire_time; - - /* schedule a timer interruption if needed */ - if (enable) { - expire_time = muldiv64(ar->tmr.overflow_time, NANOSECONDS_PER_SECOND, - PM_TIMER_FREQUENCY); - timer_mod(ar->tmr.timer, expire_time); - } else { - timer_del(ar->tmr.timer); - } -} - -void acpi_pm_tmr_calc_overflow_time(ACPIREGS *ar) -{ - int64_t d = acpi_pm_tmr_get_clock(); - ar->tmr.overflow_time = (d + 0x800000LL) & ~0x7fffffLL; -} - -static uint32_t acpi_pm_tmr_get(ACPIREGS *ar) -{ - uint32_t d = acpi_pm_tmr_get_clock(); - return d & 0xffffff; -} - -static void acpi_pm_tmr_timer(void *opaque) -{ - ACPIREGS *ar = opaque; - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_PMTIMER); - ar->tmr.update_sci(ar); -} - -static uint64_t acpi_pm_tmr_read(void *opaque, hwaddr addr, unsigned width) -{ - return acpi_pm_tmr_get(opaque); -} - -static void acpi_pm_tmr_write(void *opaque, hwaddr addr, uint64_t val, - unsigned width) -{ - /* nothing */ -} - -static const MemoryRegionOps acpi_pm_tmr_ops = { - .read = acpi_pm_tmr_read, - .write = acpi_pm_tmr_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -void acpi_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci, - MemoryRegion *parent) -{ - ar->tmr.update_sci = 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_add_subregion(parent, 8, &ar->tmr.io); -} - -void acpi_pm_tmr_reset(ACPIREGS *ar) -{ - ar->tmr.overflow_time = 0; - timer_del(ar->tmr.timer); -} - -/* ACPI PM1aCNT */ -static void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val) -{ - ar->pm1.cnt.cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE); - - if (val & ACPI_BITMASK_SLEEP_ENABLE) { - /* change suspend type */ - uint16_t sus_typ = (val >> 10) & 7; - switch(sus_typ) { - case 0: /* soft power off */ - qemu_system_shutdown_request(); - break; - case 1: - qemu_system_suspend_request(); - break; - default: - if (sus_typ == ar->pm1.cnt.s4_val) { /* S4 request */ - qapi_event_send_suspend_disk(&error_abort); - qemu_system_shutdown_request(); - } - break; - } - } -} - -void acpi_pm1_cnt_update(ACPIREGS *ar, - bool sci_enable, bool sci_disable) -{ - /* ACPI specs 3.0, 4.7.2.5 */ - if (sci_enable) { - ar->pm1.cnt.cnt |= ACPI_BITMASK_SCI_ENABLE; - } else if (sci_disable) { - ar->pm1.cnt.cnt &= ~ACPI_BITMASK_SCI_ENABLE; - } -} - -static uint64_t acpi_pm_cnt_read(void *opaque, hwaddr addr, unsigned width) -{ - ACPIREGS *ar = opaque; - return ar->pm1.cnt.cnt; -} - -static void acpi_pm_cnt_write(void *opaque, hwaddr addr, uint64_t val, - unsigned width) -{ - acpi_pm1_cnt_write(opaque, val); -} - -static const MemoryRegionOps acpi_pm_cnt_ops = { - .read = acpi_pm_cnt_read, - .write = acpi_pm_cnt_write, - .valid.min_access_size = 2, - .valid.max_access_size = 2, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent, - bool disable_s3, bool disable_s4, uint8_t s4_val) -{ - FWCfgState *fw_cfg; - - ar->pm1.cnt.s4_val = s4_val; - ar->wakeup.notify = acpi_notify_wakeup; - qemu_register_wakeup_notifier(&ar->wakeup); - memory_region_init_io(&ar->pm1.cnt.io, memory_region_owner(parent), - &acpi_pm_cnt_ops, ar, "acpi-cnt", 2); - memory_region_add_subregion(parent, 4, &ar->pm1.cnt.io); - - fw_cfg = fw_cfg_find(); - if (fw_cfg) { - uint8_t suspend[6] = {128, 0, 0, 129, 128, 128}; - suspend[3] = 1 | ((!disable_s3) << 7); - suspend[4] = s4_val | ((!disable_s4) << 7); - - fw_cfg_add_file(fw_cfg, "etc/system-states", g_memdup(suspend, 6), 6); - } -} - -void acpi_pm1_cnt_reset(ACPIREGS *ar) -{ - ar->pm1.cnt.cnt = 0; -} - -/* ACPI GPE */ -void acpi_gpe_init(ACPIREGS *ar, uint8_t len) -{ - ar->gpe.len = len; - /* 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) -{ - memset(ar->gpe.sts, 0, ar->gpe.len / 2); - memset(ar->gpe.en, 0, ar->gpe.len / 2); -} - -static uint8_t *acpi_gpe_ioport_get_ptr(ACPIREGS *ar, uint32_t addr) -{ - uint8_t *cur = NULL; - - if (addr < ar->gpe.len / 2) { - cur = ar->gpe.sts + addr; - } else if (addr < ar->gpe.len) { - cur = ar->gpe.en + addr - ar->gpe.len / 2; - } else { - abort(); - } - - return cur; -} - -void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val) -{ - uint8_t *cur; - - cur = acpi_gpe_ioport_get_ptr(ar, addr); - if (addr < ar->gpe.len / 2) { - /* GPE_STS */ - *cur = (*cur) & ~val; - } else if (addr < ar->gpe.len) { - /* GPE_EN */ - *cur = val; - } else { - abort(); - } -} - -uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr) -{ - uint8_t *cur; - uint32_t val; - - cur = acpi_gpe_ioport_get_ptr(ar, addr); - val = 0; - if (cur != NULL) { - val = *cur; - } - - return val; -} - -void acpi_send_gpe_event(ACPIREGS *ar, qemu_irq irq, - AcpiGPEStatusBits status) -{ - ar->gpe.sts[0] |= status; - acpi_update_sci(ar, irq); -} - -void acpi_update_sci(ACPIREGS *regs, qemu_irq irq) -{ - int sci_level, pm1a_sts; - - pm1a_sts = acpi_pm1_evt_get_sts(regs); - - sci_level = ((pm1a_sts & - regs->pm1.evt.en & ACPI_BITMASK_PM1_COMMON_ENABLED) != 0) || - ((regs->gpe.sts[0] & regs->gpe.en[0]) != 0); - - qemu_set_irq(irq, sci_level); - - /* schedule a timer interruption if needed */ - acpi_pm_tmr_update(regs, - (regs->pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) && - !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS)); -} diff --git a/qemu/hw/acpi/cpu_hotplug.c b/qemu/hw/acpi/cpu_hotplug.c deleted file mode 100644 index 4d86743fd..000000000 --- a/qemu/hw/acpi/cpu_hotplug.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * QEMU ACPI hotplug utilities - * - * Copyright (C) 2013 Red Hat Inc - * - * Authors: - * Igor Mammedov - * - * 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) -{ - AcpiCpuHotplug *cpus = opaque; - uint64_t val = cpus->sts[addr]; - - return val; -} - -static void cpu_status_write(void *opaque, hwaddr addr, uint64_t data, - unsigned int size) -{ - /* TODO: implement VCPU removal on guest signal that CPU can be removed */ -} - -static const MemoryRegionOps AcpiCpuHotplug_ops = { - .read = cpu_status_read, - .write = cpu_status_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void acpi_set_cpu_present_bit(AcpiCpuHotplug *g, CPUState *cpu, - Error **errp) -{ - CPUClass *k = CPU_GET_CLASS(cpu); - int64_t cpu_id; - - cpu_id = k->get_arch_id(cpu); - if ((cpu_id / 8) >= ACPI_GPE_PROC_LEN) { - error_setg(errp, "acpi: invalid cpu id: %" PRIi64, cpu_id); - return; - } - - g->sts[cpu_id / 8] |= (1 << (cpu_id % 8)); -} - -void acpi_cpu_plug_cb(ACPIREGS *ar, qemu_irq irq, - AcpiCpuHotplug *g, DeviceState *dev, Error **errp) -{ - acpi_set_cpu_present_bit(g, CPU(dev), errp); - if (*errp != NULL) { - return; - } - - acpi_send_gpe_event(ar, irq, ACPI_CPU_HOTPLUG_STATUS); -} - -void acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, - AcpiCpuHotplug *gpe_cpu, uint16_t base) -{ - CPUState *cpu; - - CPU_FOREACH(cpu) { - acpi_set_cpu_present_bit(gpe_cpu, cpu, &error_abort); - } - memory_region_init_io(&gpe_cpu->io, owner, &AcpiCpuHotplug_ops, - gpe_cpu, "acpi-cpu-hotplug", ACPI_GPE_PROC_LEN); - memory_region_add_subregion(parent, base, &gpe_cpu->io); -} diff --git a/qemu/hw/acpi/cpu_hotplug_acpi_table.c b/qemu/hw/acpi/cpu_hotplug_acpi_table.c deleted file mode 100644 index 97bb1092a..000000000 --- a/qemu/hw/acpi/cpu_hotplug_acpi_table.c +++ /dev/null @@ -1,136 +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 . - */ - -#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 deleted file mode 100644 index 27e978f5f..000000000 --- a/qemu/hw/acpi/ich9.c +++ /dev/null @@ -1,477 +0,0 @@ -/* - * ACPI implementation - * - * Copyright (c) 2006 Fabrice Bellard - * Copyright (c) 2009 Isaku Yamahata - * VA Linux Systems Japan K.K. - * Copyright (C) 2012 Jason Baron - * - * This is based on acpi.c. - * - * 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, see - * - * 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" -#include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "hw/acpi/acpi.h" -#include "hw/acpi/tco.h" -#include "sysemu/kvm.h" -#include "exec/address-spaces.h" - -#include "hw/i386/ich9.h" -#include "hw/mem/pc-dimm.h" - -//#define DEBUG - -#ifdef DEBUG -#define ICH9_DEBUG(fmt, ...) \ -do { printf("%s "fmt, __func__, ## __VA_ARGS__); } while (0) -#else -#define ICH9_DEBUG(fmt, ...) do { } while (0) -#endif - -static void ich9_pm_update_sci_fn(ACPIREGS *regs) -{ - ICH9LPCPMRegs *pm = container_of(regs, ICH9LPCPMRegs, acpi_regs); - acpi_update_sci(&pm->acpi_regs, pm->irq); -} - -static uint64_t ich9_gpe_readb(void *opaque, hwaddr addr, unsigned width) -{ - ICH9LPCPMRegs *pm = opaque; - return acpi_gpe_ioport_readb(&pm->acpi_regs, addr); -} - -static void ich9_gpe_writeb(void *opaque, hwaddr addr, uint64_t val, - unsigned width) -{ - ICH9LPCPMRegs *pm = opaque; - acpi_gpe_ioport_writeb(&pm->acpi_regs, addr, val); - acpi_update_sci(&pm->acpi_regs, pm->irq); -} - -static const MemoryRegionOps ich9_gpe_ops = { - .read = ich9_gpe_readb, - .write = ich9_gpe_writeb, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .impl.min_access_size = 1, - .impl.max_access_size = 1, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static uint64_t ich9_smi_readl(void *opaque, hwaddr addr, unsigned width) -{ - ICH9LPCPMRegs *pm = opaque; - switch (addr) { - case 0: - return pm->smi_en; - case 4: - return pm->smi_sts; - default: - return 0; - } -} - -static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val, - unsigned width) -{ - ICH9LPCPMRegs *pm = opaque; - TCOIORegs *tr = &pm->tco_regs; - uint64_t tco_en; - - switch (addr) { - case 0: - tco_en = pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN; - /* once TCO_LOCK bit is set, TCO_EN bit cannot be overwritten */ - if (tr->tco.cnt1 & TCO_LOCK) { - val = (val & ~ICH9_PMIO_SMI_EN_TCO_EN) | tco_en; - } - pm->smi_en &= ~pm->smi_en_wmask; - pm->smi_en |= (val & pm->smi_en_wmask); - break; - } -} - -static const MemoryRegionOps ich9_smi_ops = { - .read = ich9_smi_readl, - .write = ich9_smi_writel, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base) -{ - ICH9_DEBUG("to 0x%x\n", pm_io_base); - - assert((pm_io_base & ICH9_PMIO_MASK) == 0); - - pm->pm_io_base = pm_io_base; - memory_region_transaction_begin(); - memory_region_set_enabled(&pm->io, pm->pm_io_base != 0); - memory_region_set_address(&pm->io, pm->pm_io_base); - memory_region_transaction_commit(); -} - -static int ich9_pm_post_load(void *opaque, int version_id) -{ - ICH9LPCPMRegs *pm = opaque; - uint32_t pm_io_base = pm->pm_io_base; - pm->pm_io_base = 0; - ich9_pm_iospace_update(pm, pm_io_base); - return 0; -} - -#define VMSTATE_GPE_ARRAY(_field, _state) \ - { \ - .name = (stringify(_field)), \ - .version_id = 0, \ - .num = ICH9_PMIO_GPE0_LEN, \ - .info = &vmstate_info_uint8, \ - .size = sizeof(uint8_t), \ - .flags = VMS_ARRAY | VMS_POINTER, \ - .offset = vmstate_offset_pointer(_state, _field, uint8_t), \ - } - -static bool vmstate_test_use_memhp(void *opaque) -{ - ICH9LPCPMRegs *s = opaque; - return s->acpi_memory_hotplug.is_enabled; -} - -static const VMStateDescription vmstate_memhp_state = { - .name = "ich9_pm/memhp", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .needed = vmstate_test_use_memhp, - .fields = (VMStateField[]) { - VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, ICH9LPCPMRegs), - VMSTATE_END_OF_LIST() - } -}; - -static bool vmstate_test_use_tco(void *opaque) -{ - ICH9LPCPMRegs *s = opaque; - return s->enable_tco; -} - -static const VMStateDescription vmstate_tco_io_state = { - .name = "ich9_pm/tco", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .needed = vmstate_test_use_tco, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1, vmstate_tco_io_sts, - TCOIORegs), - VMSTATE_END_OF_LIST() - } -}; - -const VMStateDescription vmstate_ich9_pm = { - .name = "ich9_pm", - .version_id = 1, - .minimum_version_id = 1, - .post_load = ich9_pm_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT16(acpi_regs.pm1.evt.sts, ICH9LPCPMRegs), - VMSTATE_UINT16(acpi_regs.pm1.evt.en, ICH9LPCPMRegs), - VMSTATE_UINT16(acpi_regs.pm1.cnt.cnt, ICH9LPCPMRegs), - VMSTATE_TIMER_PTR(acpi_regs.tmr.timer, ICH9LPCPMRegs), - VMSTATE_INT64(acpi_regs.tmr.overflow_time, ICH9LPCPMRegs), - VMSTATE_GPE_ARRAY(acpi_regs.gpe.sts, ICH9LPCPMRegs), - VMSTATE_GPE_ARRAY(acpi_regs.gpe.en, ICH9LPCPMRegs), - VMSTATE_UINT32(smi_en, ICH9LPCPMRegs), - VMSTATE_UINT32(smi_sts, ICH9LPCPMRegs), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_memhp_state, - &vmstate_tco_io_state, - NULL - } -}; - -static void pm_reset(void *opaque) -{ - ICH9LPCPMRegs *pm = opaque; - ich9_pm_iospace_update(pm, 0); - - acpi_pm1_evt_reset(&pm->acpi_regs); - acpi_pm1_cnt_reset(&pm->acpi_regs); - acpi_pm_tmr_reset(&pm->acpi_regs); - acpi_gpe_reset(&pm->acpi_regs); - - pm->smi_en = 0; - if (!pm->smm_enabled) { - /* Mark SMM as already inited to prevent SMM from running. */ - pm->smi_en |= ICH9_PMIO_SMI_EN_APMC_EN; - } - pm->smi_en_wmask = ~0; - - acpi_update_sci(&pm->acpi_regs, pm->irq); -} - -static void pm_powerdown_req(Notifier *n, void *opaque) -{ - ICH9LPCPMRegs *pm = container_of(n, ICH9LPCPMRegs, powerdown_notifier); - - acpi_pm1_evt_power_down(&pm->acpi_regs); -} - -void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, - bool smm_enabled, - qemu_irq sci_irq) -{ - memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE); - memory_region_set_enabled(&pm->io, false); - memory_region_add_subregion(pci_address_space_io(lpc_pci), - 0, &pm->io); - - acpi_pm_tmr_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io); - acpi_pm1_evt_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io); - acpi_pm1_cnt_init(&pm->acpi_regs, &pm->io, pm->disable_s3, pm->disable_s4, - pm->s4_val); - - acpi_gpe_init(&pm->acpi_regs, ICH9_PMIO_GPE0_LEN); - memory_region_init_io(&pm->io_gpe, OBJECT(lpc_pci), &ich9_gpe_ops, pm, - "acpi-gpe0", ICH9_PMIO_GPE0_LEN); - memory_region_add_subregion(&pm->io, ICH9_PMIO_GPE0_STS, &pm->io_gpe); - - memory_region_init_io(&pm->io_smi, OBJECT(lpc_pci), &ich9_smi_ops, pm, - "acpi-smi", 8); - memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi); - - pm->smm_enabled = smm_enabled; - - pm->enable_tco = true; - acpi_pm_tco_init(&pm->tco_regs, &pm->io); - - pm->irq = sci_irq; - qemu_register_reset(pm_reset, pm); - pm->powerdown_notifier.notify = pm_powerdown_req; - qemu_register_powerdown_notifier(&pm->powerdown_notifier); - - acpi_cpu_hotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci), - &pm->gpe_cpu, ICH9_CPU_HOTPLUG_IO_BASE); - - if (pm->acpi_memory_hotplug.is_enabled) { - acpi_memory_hotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci), - &pm->acpi_memory_hotplug); - } -} - -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, name, &value, errp); -} - -static bool ich9_pm_get_memory_hotplug_support(Object *obj, Error **errp) -{ - ICH9LPCState *s = ICH9_LPC_DEVICE(obj); - - return s->pm.acpi_memory_hotplug.is_enabled; -} - -static void ich9_pm_set_memory_hotplug_support(Object *obj, bool value, - Error **errp) -{ - ICH9LPCState *s = ICH9_LPC_DEVICE(obj); - - s->pm.acpi_memory_hotplug.is_enabled = value; -} - -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, name, &value, 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, name, &value, &local_err); - if (local_err) { - goto out; - } - pm->disable_s3 = value; -out: - error_propagate(errp, local_err); -} - -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, name, &value, 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, name, &value, &local_err); - if (local_err) { - goto out; - } - pm->disable_s4 = value; -out: - error_propagate(errp, local_err); -} - -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, name, &value, 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, name, &value, &local_err); - if (local_err) { - goto out; - } - pm->s4_val = value; -out: - error_propagate(errp, local_err); -} - -static bool ich9_pm_get_enable_tco(Object *obj, Error **errp) -{ - ICH9LPCState *s = ICH9_LPC_DEVICE(obj); - return s->pm.enable_tco; -} - -static void ich9_pm_set_enable_tco(Object *obj, bool value, Error **errp) -{ - ICH9LPCState *s = ICH9_LPC_DEVICE(obj); - s->pm.enable_tco = value; -} - -void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp) -{ - static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN; - pm->acpi_memory_hotplug.is_enabled = true; - pm->disable_s3 = 0; - pm->disable_s4 = 0; - pm->s4_val = 2; - - object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE, - &pm->pm_io_base, errp); - object_property_add(obj, ACPI_PM_PROP_GPE0_BLK, "uint32", - ich9_pm_get_gpe0_blk, - NULL, NULL, pm, NULL); - object_property_add_uint32_ptr(obj, ACPI_PM_PROP_GPE0_BLK_LEN, - &gpe0_len, errp); - object_property_add_bool(obj, "memory-hotplug-support", - ich9_pm_get_memory_hotplug_support, - ich9_pm_set_memory_hotplug_support, - NULL); - object_property_add(obj, ACPI_PM_PROP_S3_DISABLED, "uint8", - ich9_pm_get_disable_s3, - ich9_pm_set_disable_s3, - NULL, pm, NULL); - object_property_add(obj, ACPI_PM_PROP_S4_DISABLED, "uint8", - ich9_pm_get_disable_s4, - ich9_pm_set_disable_s4, - NULL, pm, NULL); - object_property_add(obj, ACPI_PM_PROP_S4_VAL, "uint8", - ich9_pm_get_s4_val, - ich9_pm_set_s4_val, - NULL, pm, NULL); - object_property_add_bool(obj, ACPI_PM_PROP_TCO_ENABLED, - ich9_pm_get_enable_tco, - ich9_pm_set_enable_tco, - NULL); -} - -void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp) -{ - if (pm->acpi_memory_hotplug.is_enabled && - object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - acpi_memory_plug_cb(&pm->acpi_regs, pm->irq, &pm->acpi_memory_hotplug, - dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { - acpi_cpu_plug_cb(&pm->acpi_regs, pm->irq, &pm->gpe_cpu, dev, errp); - } else { - error_setg(errp, "acpi: device plug request for not supported device" - " type: %s", object_get_typename(OBJECT(dev))); - } -} - -void ich9_pm_device_unplug_request_cb(ICH9LPCPMRegs *pm, DeviceState *dev, - Error **errp) -{ - if (pm->acpi_memory_hotplug.is_enabled && - object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - acpi_memory_unplug_request_cb(&pm->acpi_regs, pm->irq, - &pm->acpi_memory_hotplug, dev, errp); - } else { - error_setg(errp, "acpi: device unplug request for not supported device" - " type: %s", object_get_typename(OBJECT(dev))); - } -} - -void ich9_pm_device_unplug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, - Error **errp) -{ - if (pm->acpi_memory_hotplug.is_enabled && - object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - acpi_memory_unplug_cb(&pm->acpi_memory_hotplug, dev, errp); - } else { - error_setg(errp, "acpi: device unplug for not supported device" - " type: %s", object_get_typename(OBJECT(dev))); - } -} - -void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list) -{ - ICH9LPCState *s = ICH9_LPC_DEVICE(adev); - - acpi_memory_ospm_status(&s->pm.acpi_memory_hotplug, list); -} diff --git a/qemu/hw/acpi/memory_hotplug.c b/qemu/hw/acpi/memory_hotplug.c deleted file mode 100644 index f65a3a21e..000000000 --- a/qemu/hw/acpi/memory_hotplug.c +++ /dev/null @@ -1,312 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/acpi/memory_hotplug.h" -#include "hw/acpi/pc-hotplug.h" -#include "hw/mem/pc-dimm.h" -#include "hw/boards.h" -#include "hw/qdev-core.h" -#include "trace.h" -#include "qapi-event.h" - -static ACPIOSTInfo *acpi_memory_device_status(int slot, MemStatus *mdev) -{ - ACPIOSTInfo *info = g_new0(ACPIOSTInfo, 1); - - info->slot_type = ACPI_SLOT_TYPE_DIMM; - info->slot = g_strdup_printf("%d", slot); - info->source = mdev->ost_event; - info->status = mdev->ost_status; - if (mdev->dimm) { - DeviceState *dev = DEVICE(mdev->dimm); - if (dev->id) { - info->device = g_strdup(dev->id); - info->has_device = true; - } - } - return info; -} - -void acpi_memory_ospm_status(MemHotplugState *mem_st, ACPIOSTInfoList ***list) -{ - int i; - - for (i = 0; i < mem_st->dev_count; i++) { - ACPIOSTInfoList *elem = g_new0(ACPIOSTInfoList, 1); - elem->value = acpi_memory_device_status(i, &mem_st->devs[i]); - elem->next = NULL; - **list = elem; - *list = &elem->next; - } -} - -static uint64_t acpi_memory_hotplug_read(void *opaque, hwaddr addr, - unsigned int size) -{ - uint32_t val = 0; - MemHotplugState *mem_st = opaque; - MemStatus *mdev; - Object *o; - - if (mem_st->selector >= mem_st->dev_count) { - trace_mhp_acpi_invalid_slot_selected(mem_st->selector); - return 0; - } - - mdev = &mem_st->devs[mem_st->selector]; - o = OBJECT(mdev->dimm); - switch (addr) { - case 0x0: /* Lo part of phys address where DIMM is mapped */ - val = o ? object_property_get_int(o, PC_DIMM_ADDR_PROP, NULL) : 0; - trace_mhp_acpi_read_addr_lo(mem_st->selector, val); - break; - case 0x4: /* Hi part of phys address where DIMM is mapped */ - val = o ? object_property_get_int(o, PC_DIMM_ADDR_PROP, NULL) >> 32 : 0; - trace_mhp_acpi_read_addr_hi(mem_st->selector, val); - break; - case 0x8: /* Lo part of DIMM size */ - val = o ? object_property_get_int(o, PC_DIMM_SIZE_PROP, NULL) : 0; - trace_mhp_acpi_read_size_lo(mem_st->selector, val); - break; - case 0xc: /* Hi part of DIMM size */ - val = o ? object_property_get_int(o, PC_DIMM_SIZE_PROP, NULL) >> 32 : 0; - trace_mhp_acpi_read_size_hi(mem_st->selector, val); - break; - case 0x10: /* node proximity for _PXM method */ - val = o ? object_property_get_int(o, PC_DIMM_NODE_PROP, NULL) : 0; - trace_mhp_acpi_read_pxm(mem_st->selector, val); - break; - case 0x14: /* pack and return is_* fields */ - val |= mdev->is_enabled ? 1 : 0; - val |= mdev->is_inserting ? 2 : 0; - val |= mdev->is_removing ? 4 : 0; - trace_mhp_acpi_read_flags(mem_st->selector, val); - break; - default: - val = ~0; - break; - } - return val; -} - -static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data, - unsigned int size) -{ - MemHotplugState *mem_st = opaque; - MemStatus *mdev; - ACPIOSTInfo *info; - DeviceState *dev = NULL; - HotplugHandler *hotplug_ctrl = NULL; - Error *local_err = NULL; - - if (!mem_st->dev_count) { - return; - } - - if (addr) { - if (mem_st->selector >= mem_st->dev_count) { - trace_mhp_acpi_invalid_slot_selected(mem_st->selector); - return; - } - } - - switch (addr) { - case 0x0: /* DIMM slot selector */ - mem_st->selector = data; - trace_mhp_acpi_write_slot(mem_st->selector); - break; - case 0x4: /* _OST event */ - mdev = &mem_st->devs[mem_st->selector]; - if (data == 1) { - /* TODO: handle device insert OST event */ - } else if (data == 3) { - /* TODO: handle device remove OST event */ - } - mdev->ost_event = data; - trace_mhp_acpi_write_ost_ev(mem_st->selector, mdev->ost_event); - break; - case 0x8: /* _OST status */ - mdev = &mem_st->devs[mem_st->selector]; - mdev->ost_status = data; - trace_mhp_acpi_write_ost_status(mem_st->selector, mdev->ost_status); - /* TODO: implement memory removal on guest signal */ - - info = acpi_memory_device_status(mem_st->selector, mdev); - qapi_event_send_acpi_device_ost(info, &error_abort); - qapi_free_ACPIOSTInfo(info); - break; - case 0x14: /* set is_* fields */ - mdev = &mem_st->devs[mem_st->selector]; - if (data & 2) { /* clear insert event */ - mdev->is_inserting = false; - trace_mhp_acpi_clear_insert_evt(mem_st->selector); - } else if (data & 4) { - mdev->is_removing = false; - trace_mhp_acpi_clear_remove_evt(mem_st->selector); - } else if (data & 8) { - if (!mdev->is_enabled) { - trace_mhp_acpi_ejecting_invalid_slot(mem_st->selector); - break; - } - - dev = DEVICE(mdev->dimm); - hotplug_ctrl = qdev_get_hotplug_handler(dev); - /* call pc-dimm unplug cb */ - hotplug_handler_unplug(hotplug_ctrl, dev, &local_err); - if (local_err) { - trace_mhp_acpi_pc_dimm_delete_failed(mem_st->selector); - 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); - } - break; - default: - break; - } - -} -static const MemoryRegionOps acpi_memory_hotplug_ops = { - .read = acpi_memory_hotplug_read, - .write = acpi_memory_hotplug_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 4, - }, -}; - -void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner, - MemHotplugState *state) -{ - MachineState *machine = MACHINE(qdev_get_machine()); - - state->dev_count = machine->ram_slots; - if (!state->dev_count) { - return; - } - - state->devs = g_malloc0(sizeof(*state->devs) * state->dev_count); - memory_region_init_io(&state->io, owner, &acpi_memory_hotplug_ops, state, - "acpi-mem-hotplug", ACPI_MEMORY_HOTPLUG_IO_LEN); - memory_region_add_subregion(as, ACPI_MEMORY_HOTPLUG_BASE, &state->io); -} - -/** - * acpi_memory_slot_status: - * @mem_st: memory hotplug state - * @dev: device - * @errp: set in case of an error - * - * Obtain a single memory slot status. - * - * This function will be called by memory unplug request cb and unplug cb. - */ -static MemStatus * -acpi_memory_slot_status(MemHotplugState *mem_st, - DeviceState *dev, Error **errp) -{ - Error *local_err = NULL; - int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, - &local_err); - - if (local_err) { - error_propagate(errp, local_err); - return NULL; - } - - if (slot >= mem_st->dev_count) { - char *dev_path = object_get_canonical_path(OBJECT(dev)); - error_setg(errp, "acpi_memory_slot_status: " - "device [%s] returned invalid memory slot[%d]", - dev_path, slot); - g_free(dev_path); - return NULL; - } - - return &mem_st->devs[slot]; -} - -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) { - return; - } - - mdev->dimm = dev; - mdev->is_enabled = true; - if (dev->hotplugged) { - mdev->is_inserting = true; - - /* do ACPI magic */ - acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS); - } -} - -void acpi_memory_unplug_request_cb(ACPIREGS *ar, qemu_irq irq, - MemHotplugState *mem_st, - DeviceState *dev, Error **errp) -{ - MemStatus *mdev; - - mdev = acpi_memory_slot_status(mem_st, dev, errp); - if (!mdev) { - return; - } - - mdev->is_removing = true; - - /* Do ACPI magic */ - acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS); -} - -void acpi_memory_unplug_cb(MemHotplugState *mem_st, - DeviceState *dev, Error **errp) -{ - MemStatus *mdev; - - mdev = acpi_memory_slot_status(mem_st, dev, errp); - if (!mdev) { - return; - } - - mdev->is_enabled = false; - mdev->dimm = NULL; -} - -static const VMStateDescription vmstate_memhp_sts = { - .name = "memory hotplug device state", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_BOOL(is_enabled, MemStatus), - VMSTATE_BOOL(is_inserting, MemStatus), - VMSTATE_UINT32(ost_event, MemStatus), - VMSTATE_UINT32(ost_status, MemStatus), - VMSTATE_END_OF_LIST() - } -}; - -const VMStateDescription vmstate_memory_hotplug = { - .name = "memory hotplug state", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(selector, MemHotplugState), - VMSTATE_STRUCT_VARRAY_POINTER_UINT32(devs, MemHotplugState, dev_count, - vmstate_memhp_sts, MemStatus), - VMSTATE_END_OF_LIST() - } -}; diff --git a/qemu/hw/acpi/memory_hotplug_acpi_table.c b/qemu/hw/acpi/memory_hotplug_acpi_table.c deleted file mode 100644 index c75660215..000000000 --- a/qemu/hw/acpi/memory_hotplug_acpi_table.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Memory hotplug AML code of DSDT ACPI table - * - * Copyright (C) 2015 Red Hat Inc - * - * Author: Igor Mammedov - * - * 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 deleted file mode 100644 index 9531340e5..000000000 --- a/qemu/hw/acpi/nvdimm.c +++ /dev/null @@ -1,706 +0,0 @@ -/* - * NVDIMM ACPI Implementation - * - * Copyright(C) 2015 Intel Corporation. - * - * Author: - * Xiao Guangrong - * - * 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 - */ - -#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 deleted file mode 100644 index 71f4c4e14..000000000 --- a/qemu/hw/acpi/pcihp.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * QEMU<->ACPI BIOS PCI hotplug interface - * - * QEMU supports PCI hotplug via ACPI. This module - * implements the interface between QEMU and the ACPI BIOS. - * Interface specification - see docs/specs/acpi_pci_hotplug.txt - * - * Copyright (c) 2013, Red Hat Inc, Michael S. Tsirkin (mst@redhat.com) - * 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, see - * - * 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/acpi/pcihp.h" - -#include "hw/hw.h" -#include "hw/i386/pc.h" -#include "hw/pci/pci.h" -#include "hw/acpi/acpi.h" -#include "sysemu/sysemu.h" -#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" - -//#define DEBUG - -#ifdef DEBUG -# define ACPI_PCIHP_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) -#else -# define ACPI_PCIHP_DPRINTF(format, ...) do { } while (0) -#endif - -#define ACPI_PCIHP_ADDR 0xae00 -#define ACPI_PCIHP_SIZE 0x0014 -#define ACPI_PCIHP_LEGACY_SIZE 0x000f -#define PCI_UP_BASE 0x0000 -#define PCI_DOWN_BASE 0x0004 -#define PCI_EJ_BASE 0x0008 -#define PCI_RMV_BASE 0x000c -#define PCI_SEL_BASE 0x0010 - -typedef struct AcpiPciHpFind { - int bsel; - PCIBus *bus; -} AcpiPciHpFind; - -static int acpi_pcihp_get_bsel(PCIBus *bus) -{ - Error *local_err = NULL; - int64_t bsel = object_property_get_int(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, - &local_err); - - if (local_err || bsel < 0 || bsel >= ACPI_PCIHP_MAX_HOTPLUG_BUS) { - if (local_err) { - error_free(local_err); - } - return -1; - } else { - return bsel; - } -} - -static void acpi_pcihp_test_hotplug_bus(PCIBus *bus, void *opaque) -{ - AcpiPciHpFind *find = opaque; - if (find->bsel == acpi_pcihp_get_bsel(bus)) { - find->bus = bus; - } -} - -static PCIBus *acpi_pcihp_find_hotplug_bus(AcpiPciHpState *s, int bsel) -{ - AcpiPciHpFind find = { .bsel = bsel, .bus = NULL }; - - if (bsel < 0) { - return NULL; - } - - pci_for_each_bus(s->root, acpi_pcihp_test_hotplug_bus, &find); - - /* Make bsel 0 eject root bus if bsel property is not set, - * for compatibility with non acpi setups. - * TODO: really needed? - */ - if (!bsel && !find.bus) { - find.bus = s->root; - } - return find.bus; -} - -static bool acpi_pcihp_pc_no_hotplug(AcpiPciHpState *s, PCIDevice *dev) -{ - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); - DeviceClass *dc = DEVICE_GET_CLASS(dev); - /* - * ACPI doesn't allow hotplug of bridge devices. Don't allow - * hot-unplug of bridge devices unless they were added by hotplug - * (and so, not described by acpi). - */ - return (pc->is_bridge && !dev->qdev.hotplugged) || !dc->hotpluggable; -} - -static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slots) -{ - BusChild *kid, *next; - int slot = ctz32(slots); - PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel); - - if (!bus) { - return; - } - - /* Mark request as complete */ - s->acpi_pcihp_pci_status[bsel].down &= ~(1U << slot); - s->acpi_pcihp_pci_status[bsel].up &= ~(1U << slot); - - QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) { - DeviceState *qdev = kid->child; - PCIDevice *dev = PCI_DEVICE(qdev); - if (PCI_SLOT(dev->devfn) == slot) { - if (!acpi_pcihp_pc_no_hotplug(s, dev)) { - object_unparent(OBJECT(qdev)); - } - } - } -} - -static void acpi_pcihp_update_hotplug_bus(AcpiPciHpState *s, int bsel) -{ - BusChild *kid, *next; - PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel); - - /* Execute any pending removes during reset */ - while (s->acpi_pcihp_pci_status[bsel].down) { - acpi_pcihp_eject_slot(s, bsel, s->acpi_pcihp_pci_status[bsel].down); - } - - s->acpi_pcihp_pci_status[bsel].hotplug_enable = ~0; - - if (!bus) { - return; - } - QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) { - DeviceState *qdev = kid->child; - PCIDevice *pdev = PCI_DEVICE(qdev); - int slot = PCI_SLOT(pdev->devfn); - - if (acpi_pcihp_pc_no_hotplug(s, pdev)) { - s->acpi_pcihp_pci_status[bsel].hotplug_enable &= ~(1U << slot); - } - } -} - -static void acpi_pcihp_update(AcpiPciHpState *s) -{ - int i; - - for (i = 0; i < ACPI_PCIHP_MAX_HOTPLUG_BUS; ++i) { - acpi_pcihp_update_hotplug_bus(s, i); - } -} - -void acpi_pcihp_reset(AcpiPciHpState *s) -{ - acpi_pcihp_update(s); -} - -void acpi_pcihp_device_plug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s, - DeviceState *dev, Error **errp) -{ - PCIDevice *pdev = PCI_DEVICE(dev); - int slot = PCI_SLOT(pdev->devfn); - int bsel = acpi_pcihp_get_bsel(pdev->bus); - if (bsel < 0) { - error_setg(errp, "Unsupported bus. Bus doesn't have property '" - ACPI_PCIHP_PROP_BSEL "' set"); - return; - } - - /* Don't send event when device is enabled during qemu machine creation: - * it is present on boot, no hotplug event is necessary. We do send an - * event when the device is disabled later. */ - if (!dev->hotplugged) { - return; - } - - s->acpi_pcihp_pci_status[bsel].up |= (1U << slot); - - acpi_send_gpe_event(ar, irq, ACPI_PCI_HOTPLUG_STATUS); -} - -void acpi_pcihp_device_unplug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s, - DeviceState *dev, Error **errp) -{ - PCIDevice *pdev = PCI_DEVICE(dev); - int slot = PCI_SLOT(pdev->devfn); - int bsel = acpi_pcihp_get_bsel(pdev->bus); - if (bsel < 0) { - error_setg(errp, "Unsupported bus. Bus doesn't have property '" - ACPI_PCIHP_PROP_BSEL "' set"); - return; - } - - s->acpi_pcihp_pci_status[bsel].down |= (1U << slot); - - acpi_send_gpe_event(ar, irq, ACPI_PCI_HOTPLUG_STATUS); -} - -static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size) -{ - AcpiPciHpState *s = opaque; - uint32_t val = 0; - int bsel = s->hotplug_select; - - if (bsel < 0 || bsel >= ACPI_PCIHP_MAX_HOTPLUG_BUS) { - return 0; - } - - switch (addr) { - case PCI_UP_BASE: - val = s->acpi_pcihp_pci_status[bsel].up; - if (!s->legacy_piix) { - s->acpi_pcihp_pci_status[bsel].up = 0; - } - ACPI_PCIHP_DPRINTF("pci_up_read %" PRIu32 "\n", val); - break; - case PCI_DOWN_BASE: - val = s->acpi_pcihp_pci_status[bsel].down; - ACPI_PCIHP_DPRINTF("pci_down_read %" PRIu32 "\n", val); - break; - case PCI_EJ_BASE: - /* No feature defined yet */ - ACPI_PCIHP_DPRINTF("pci_features_read %" PRIu32 "\n", val); - break; - case PCI_RMV_BASE: - val = s->acpi_pcihp_pci_status[bsel].hotplug_enable; - ACPI_PCIHP_DPRINTF("pci_rmv_read %" PRIu32 "\n", val); - break; - case PCI_SEL_BASE: - val = s->hotplug_select; - ACPI_PCIHP_DPRINTF("pci_sel_read %" PRIu32 "\n", val); - default: - break; - } - - return val; -} - -static void pci_write(void *opaque, hwaddr addr, uint64_t data, - unsigned int size) -{ - AcpiPciHpState *s = opaque; - switch (addr) { - case PCI_EJ_BASE: - if (s->hotplug_select >= ACPI_PCIHP_MAX_HOTPLUG_BUS) { - break; - } - acpi_pcihp_eject_slot(s, s->hotplug_select, data); - ACPI_PCIHP_DPRINTF("pciej write %" HWADDR_PRIx " <== %" PRIu64 "\n", - addr, data); - break; - case PCI_SEL_BASE: - s->hotplug_select = data; - ACPI_PCIHP_DPRINTF("pcisel write %" HWADDR_PRIx " <== %" PRIu64 "\n", - addr, data); - default: - break; - } -} - -static const MemoryRegionOps acpi_pcihp_io_ops = { - .read = pci_read, - .write = pci_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, - MemoryRegion *address_space_io, bool bridges_enabled) -{ - s->io_len = ACPI_PCIHP_SIZE; - s->io_base = ACPI_PCIHP_ADDR; - - s->root= root_bus; - s->legacy_piix = !bridges_enabled; - - if (s->legacy_piix) { - unsigned *bus_bsel = g_malloc(sizeof *bus_bsel); - - s->io_len = ACPI_PCIHP_LEGACY_SIZE; - - *bus_bsel = ACPI_PCIHP_BSEL_DEFAULT; - object_property_add_uint32_ptr(OBJECT(root_bus), ACPI_PCIHP_PROP_BSEL, - bus_bsel, NULL); - } - - memory_region_init_io(&s->io, owner, &acpi_pcihp_io_ops, s, - "acpi-pci-hotplug", s->io_len); - memory_region_add_subregion(address_space_io, s->io_base, &s->io); - - object_property_add_uint16_ptr(owner, ACPI_PCIHP_IO_BASE_PROP, &s->io_base, - &error_abort); - object_property_add_uint16_ptr(owner, ACPI_PCIHP_IO_LEN_PROP, &s->io_len, - &error_abort); -} - -const VMStateDescription vmstate_acpi_pcihp_pci_status = { - .name = "acpi_pcihp_pci_status", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(up, AcpiPciHpPciStatus), - VMSTATE_UINT32(down, AcpiPciHpPciStatus), - VMSTATE_END_OF_LIST() - } -}; diff --git a/qemu/hw/acpi/piix4.c b/qemu/hw/acpi/piix4.c deleted file mode 100644 index 16abdf162..000000000 --- a/qemu/hw/acpi/piix4.c +++ /dev/null @@ -1,645 +0,0 @@ -/* - * ACPI implementation - * - * 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, see - * - * 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" -#include "hw/i2c/pm_smbus.h" -#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" -#include "exec/address-spaces.h" -#include "hw/acpi/piix4.h" -#include "hw/acpi/pcihp.h" -#include "hw/acpi/cpu_hotplug.h" -#include "hw/hotplug.h" -#include "hw/mem/pc-dimm.h" -#include "hw/acpi/memory_hotplug.h" -#include "hw/acpi/acpi_dev_interface.h" -#include "hw/xen/xen.h" - -//#define DEBUG - -#ifdef DEBUG -# define PIIX4_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) -#else -# define PIIX4_DPRINTF(format, ...) do { } while (0) -#endif - -#define GPE_BASE 0xafe0 -#define GPE_LEN 4 - -struct pci_status { - uint32_t up; /* deprecated, maintained for migration compatibility */ - uint32_t down; -}; - -typedef struct PIIX4PMState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - MemoryRegion io; - uint32_t io_base; - - MemoryRegion io_gpe; - ACPIREGS ar; - - APMState apm; - - PMSMBus smb; - uint32_t smb_io_base; - - qemu_irq irq; - qemu_irq smi_irq; - int smm_enabled; - Notifier machine_ready; - Notifier powerdown_notifier; - - AcpiPciHpState acpi_pci_hotplug; - bool use_acpi_pci_hotplug; - - uint8_t disable_s3; - uint8_t disable_s4; - uint8_t s4_val; - - AcpiCpuHotplug gpe_cpu; - - MemHotplugState acpi_memory_hotplug; -} PIIX4PMState; - -#define TYPE_PIIX4_PM "PIIX4_PM" - -#define PIIX4_PM(obj) \ - OBJECT_CHECK(PIIX4PMState, (obj), TYPE_PIIX4_PM) - -static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, - PCIBus *bus, PIIX4PMState *s); - -#define ACPI_ENABLE 0xf1 -#define ACPI_DISABLE 0xf0 - -static void pm_tmr_timer(ACPIREGS *ar) -{ - PIIX4PMState *s = container_of(ar, PIIX4PMState, ar); - acpi_update_sci(&s->ar, s->irq); -} - -static void apm_ctrl_changed(uint32_t val, void *arg) -{ - PIIX4PMState *s = arg; - PCIDevice *d = PCI_DEVICE(s); - - /* ACPI specs 3.0, 4.7.2.5 */ - acpi_pm1_cnt_update(&s->ar, val == ACPI_ENABLE, val == ACPI_DISABLE); - if (val == ACPI_ENABLE || val == ACPI_DISABLE) { - return; - } - - if (d->config[0x5b] & (1 << 1)) { - if (s->smi_irq) { - qemu_irq_raise(s->smi_irq); - } - } -} - -static void pm_io_space_update(PIIX4PMState *s) -{ - PCIDevice *d = PCI_DEVICE(s); - - s->io_base = le32_to_cpu(*(uint32_t *)(d->config + 0x40)); - s->io_base &= 0xffc0; - - memory_region_transaction_begin(); - memory_region_set_enabled(&s->io, d->config[0x80] & 1); - memory_region_set_address(&s->io, s->io_base); - memory_region_transaction_commit(); -} - -static void smbus_io_space_update(PIIX4PMState *s) -{ - PCIDevice *d = PCI_DEVICE(s); - - s->smb_io_base = le32_to_cpu(*(uint32_t *)(d->config + 0x90)); - s->smb_io_base &= 0xffc0; - - memory_region_transaction_begin(); - memory_region_set_enabled(&s->smb.io, d->config[0xd2] & 1); - memory_region_set_address(&s->smb.io, s->smb_io_base); - memory_region_transaction_commit(); -} - -static void pm_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len) -{ - pci_default_write_config(d, address, val, len); - if (range_covers_byte(address, len, 0x80) || - ranges_overlap(address, len, 0x40, 4)) { - pm_io_space_update((PIIX4PMState *)d); - } - if (range_covers_byte(address, len, 0xd2) || - ranges_overlap(address, len, 0x90, 4)) { - smbus_io_space_update((PIIX4PMState *)d); - } -} - -static int vmstate_acpi_post_load(void *opaque, int version_id) -{ - PIIX4PMState *s = opaque; - - pm_io_space_update(s); - return 0; -} - -#define VMSTATE_GPE_ARRAY(_field, _state) \ - { \ - .name = (stringify(_field)), \ - .version_id = 0, \ - .info = &vmstate_info_uint16, \ - .size = sizeof(uint16_t), \ - .flags = VMS_SINGLE | VMS_POINTER, \ - .offset = vmstate_offset_pointer(_state, _field, uint8_t), \ - } - -static const VMStateDescription vmstate_gpe = { - .name = "gpe", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_GPE_ARRAY(sts, ACPIGPE), - VMSTATE_GPE_ARRAY(en, ACPIGPE), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_pci_status = { - .name = "pci_status", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(up, struct AcpiPciHpPciStatus), - VMSTATE_UINT32(down, struct AcpiPciHpPciStatus), - VMSTATE_END_OF_LIST() - } -}; - -static int acpi_load_old(QEMUFile *f, void *opaque, int version_id) -{ - PIIX4PMState *s = opaque; - int ret, i; - uint16_t temp; - - ret = pci_device_load(PCI_DEVICE(s), f); - if (ret < 0) { - return ret; - } - qemu_get_be16s(f, &s->ar.pm1.evt.sts); - qemu_get_be16s(f, &s->ar.pm1.evt.en); - qemu_get_be16s(f, &s->ar.pm1.cnt.cnt); - - ret = vmstate_load_state(f, &vmstate_apm, &s->apm, 1); - if (ret) { - return ret; - } - - timer_get(f, s->ar.tmr.timer); - qemu_get_sbe64s(f, &s->ar.tmr.overflow_time); - - qemu_get_be16s(f, (uint16_t *)s->ar.gpe.sts); - for (i = 0; i < 3; i++) { - qemu_get_be16s(f, &temp); - } - - qemu_get_be16s(f, (uint16_t *)s->ar.gpe.en); - for (i = 0; i < 3; i++) { - qemu_get_be16s(f, &temp); - } - - ret = vmstate_load_state(f, &vmstate_pci_status, - &s->acpi_pci_hotplug.acpi_pcihp_pci_status[ACPI_PCIHP_BSEL_DEFAULT], 1); - return ret; -} - -static bool vmstate_test_use_acpi_pci_hotplug(void *opaque, int version_id) -{ - PIIX4PMState *s = opaque; - return s->use_acpi_pci_hotplug; -} - -static bool vmstate_test_no_use_acpi_pci_hotplug(void *opaque, int version_id) -{ - PIIX4PMState *s = opaque; - return !s->use_acpi_pci_hotplug; -} - -static bool vmstate_test_use_memhp(void *opaque) -{ - PIIX4PMState *s = opaque; - return s->acpi_memory_hotplug.is_enabled; -} - -static const VMStateDescription vmstate_memhp_state = { - .name = "piix4_pm/memhp", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .needed = vmstate_test_use_memhp, - .fields = (VMStateField[]) { - VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, PIIX4PMState), - VMSTATE_END_OF_LIST() - } -}; - -/* qemu-kvm 1.2 uses version 3 but advertised as 2 - * To support incoming qemu-kvm 1.2 migration, change version_id - * and minimum_version_id to 2 below (which breaks migration from - * qemu 1.2). - * - */ -static const VMStateDescription vmstate_acpi = { - .name = "piix4_pm", - .version_id = 3, - .minimum_version_id = 3, - .minimum_version_id_old = 1, - .load_state_old = acpi_load_old, - .post_load = vmstate_acpi_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, PIIX4PMState), - VMSTATE_UINT16(ar.pm1.evt.sts, PIIX4PMState), - VMSTATE_UINT16(ar.pm1.evt.en, PIIX4PMState), - VMSTATE_UINT16(ar.pm1.cnt.cnt, PIIX4PMState), - VMSTATE_STRUCT(apm, PIIX4PMState, 0, vmstate_apm, APMState), - VMSTATE_TIMER_PTR(ar.tmr.timer, PIIX4PMState), - VMSTATE_INT64(ar.tmr.overflow_time, PIIX4PMState), - VMSTATE_STRUCT(ar.gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE), - VMSTATE_STRUCT_TEST( - acpi_pci_hotplug.acpi_pcihp_pci_status[ACPI_PCIHP_BSEL_DEFAULT], - PIIX4PMState, - vmstate_test_no_use_acpi_pci_hotplug, - 2, vmstate_pci_status, - struct AcpiPciHpPciStatus), - VMSTATE_PCI_HOTPLUG(acpi_pci_hotplug, PIIX4PMState, - vmstate_test_use_acpi_pci_hotplug), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_memhp_state, - NULL - } -}; - -static void piix4_reset(void *opaque) -{ - PIIX4PMState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - uint8_t *pci_conf = d->config; - - pci_conf[0x58] = 0; - pci_conf[0x59] = 0; - pci_conf[0x5a] = 0; - pci_conf[0x5b] = 0; - - pci_conf[0x40] = 0x01; /* PM io base read only bit */ - pci_conf[0x80] = 0; - - if (!s->smm_enabled) { - /* Mark SMM as already inited (until KVM supports SMM). */ - pci_conf[0x5B] = 0x02; - } - pm_io_space_update(s); - acpi_pcihp_reset(&s->acpi_pci_hotplug); -} - -static void piix4_pm_powerdown_req(Notifier *n, void *opaque) -{ - PIIX4PMState *s = container_of(n, PIIX4PMState, powerdown_notifier); - - assert(s != NULL); - acpi_pm1_evt_power_down(&s->ar); -} - -static void piix4_device_plug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - PIIX4PMState *s = PIIX4_PM(hotplug_dev); - - if (s->acpi_memory_hotplug.is_enabled && - object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - acpi_memory_plug_cb(&s->ar, s->irq, &s->acpi_memory_hotplug, dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { - acpi_pcihp_device_plug_cb(&s->ar, s->irq, &s->acpi_pci_hotplug, dev, - errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { - acpi_cpu_plug_cb(&s->ar, s->irq, &s->gpe_cpu, dev, errp); - } else { - error_setg(errp, "acpi: device plug request for not supported device" - " type: %s", object_get_typename(OBJECT(dev))); - } -} - -static void piix4_device_unplug_request_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - PIIX4PMState *s = PIIX4_PM(hotplug_dev); - - if (s->acpi_memory_hotplug.is_enabled && - object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - acpi_memory_unplug_request_cb(&s->ar, s->irq, &s->acpi_memory_hotplug, - dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { - acpi_pcihp_device_unplug_cb(&s->ar, s->irq, &s->acpi_pci_hotplug, dev, - errp); - } else { - error_setg(errp, "acpi: device unplug request for not supported device" - " type: %s", object_get_typename(OBJECT(dev))); - } -} - -static void piix4_device_unplug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - PIIX4PMState *s = PIIX4_PM(hotplug_dev); - - if (s->acpi_memory_hotplug.is_enabled && - object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - acpi_memory_unplug_cb(&s->acpi_memory_hotplug, dev, errp); - } else { - error_setg(errp, "acpi: device unplug for not supported device" - " type: %s", object_get_typename(OBJECT(dev))); - } -} - -static void piix4_update_bus_hotplug(PCIBus *pci_bus, void *opaque) -{ - PIIX4PMState *s = opaque; - - qbus_set_hotplug_handler(BUS(pci_bus), DEVICE(s), &error_abort); -} - -static void piix4_pm_machine_ready(Notifier *n, void *opaque) -{ - PIIX4PMState *s = container_of(n, PIIX4PMState, machine_ready); - PCIDevice *d = PCI_DEVICE(s); - MemoryRegion *io_as = pci_address_space_io(d); - uint8_t *pci_conf; - - pci_conf = d->config; - pci_conf[0x5f] = 0x10 | - (memory_region_present(io_as, 0x378) ? 0x80 : 0); - pci_conf[0x63] = 0x60; - pci_conf[0x67] = (memory_region_present(io_as, 0x3f8) ? 0x08 : 0) | - (memory_region_present(io_as, 0x2f8) ? 0x90 : 0); - - if (s->use_acpi_pci_hotplug) { - pci_for_each_bus(d->bus, piix4_update_bus_hotplug, s); - } else { - piix4_update_bus_hotplug(d->bus, s); - } -} - -static void piix4_pm_add_propeties(PIIX4PMState *s) -{ - static const uint8_t acpi_enable_cmd = ACPI_ENABLE; - static const uint8_t acpi_disable_cmd = ACPI_DISABLE; - static const uint32_t gpe0_blk = GPE_BASE; - static const uint32_t gpe0_blk_len = GPE_LEN; - static const uint16_t sci_int = 9; - - object_property_add_uint8_ptr(OBJECT(s), ACPI_PM_PROP_ACPI_ENABLE_CMD, - &acpi_enable_cmd, NULL); - object_property_add_uint8_ptr(OBJECT(s), ACPI_PM_PROP_ACPI_DISABLE_CMD, - &acpi_disable_cmd, NULL); - object_property_add_uint32_ptr(OBJECT(s), ACPI_PM_PROP_GPE0_BLK, - &gpe0_blk, NULL); - object_property_add_uint32_ptr(OBJECT(s), ACPI_PM_PROP_GPE0_BLK_LEN, - &gpe0_blk_len, NULL); - object_property_add_uint16_ptr(OBJECT(s), ACPI_PM_PROP_SCI_INT, - &sci_int, NULL); - object_property_add_uint32_ptr(OBJECT(s), ACPI_PM_PROP_PM_IO_BASE, - &s->io_base, NULL); -} - -static void piix4_pm_realize(PCIDevice *dev, Error **errp) -{ - PIIX4PMState *s = PIIX4_PM(dev); - uint8_t *pci_conf; - - pci_conf = dev->config; - pci_conf[0x06] = 0x80; - pci_conf[0x07] = 0x02; - pci_conf[0x09] = 0x00; - pci_conf[0x3d] = 0x01; // interrupt pin 1 - - /* APM */ - apm_init(dev, &s->apm, apm_ctrl_changed, s); - - if (!s->smm_enabled) { - /* Mark SMM as already inited to prevent SMM from running. KVM does not - * support SMM mode. */ - pci_conf[0x5B] = 0x02; - } - - /* XXX: which specification is used ? The i82731AB has different - mappings */ - pci_conf[0x90] = s->smb_io_base | 1; - pci_conf[0x91] = s->smb_io_base >> 8; - pci_conf[0xd2] = 0x09; - pm_smbus_init(DEVICE(dev), &s->smb); - memory_region_set_enabled(&s->smb.io, pci_conf[0xd2] & 1); - memory_region_add_subregion(pci_address_space_io(dev), - s->smb_io_base, &s->smb.io); - - memory_region_init(&s->io, OBJECT(s), "piix4-pm", 64); - memory_region_set_enabled(&s->io, false); - memory_region_add_subregion(pci_address_space_io(dev), - 0, &s->io); - - acpi_pm_tmr_init(&s->ar, pm_tmr_timer, &s->io); - acpi_pm1_evt_init(&s->ar, pm_tmr_timer, &s->io); - acpi_pm1_cnt_init(&s->ar, &s->io, s->disable_s3, s->disable_s4, s->s4_val); - acpi_gpe_init(&s->ar, GPE_LEN); - - s->powerdown_notifier.notify = piix4_pm_powerdown_req; - qemu_register_powerdown_notifier(&s->powerdown_notifier); - - s->machine_ready.notify = piix4_pm_machine_ready; - qemu_add_machine_init_done_notifier(&s->machine_ready); - qemu_register_reset(piix4_reset, s); - - piix4_acpi_system_hot_add_init(pci_address_space_io(dev), dev->bus, s); - - piix4_pm_add_propeties(s); -} - -Object *piix4_pm_find(void) -{ - bool ambig; - Object *o = object_resolve_path_type("", TYPE_PIIX4_PM, &ambig); - - if (ambig || !o) { - return NULL; - } - return o; -} - -I2CBus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, - qemu_irq sci_irq, qemu_irq smi_irq, - int smm_enabled, DeviceState **piix4_pm) -{ - DeviceState *dev; - PIIX4PMState *s; - - dev = DEVICE(pci_create(bus, devfn, TYPE_PIIX4_PM)); - qdev_prop_set_uint32(dev, "smb_io_base", smb_io_base); - if (piix4_pm) { - *piix4_pm = dev; - } - - s = PIIX4_PM(dev); - s->irq = sci_irq; - s->smi_irq = smi_irq; - s->smm_enabled = smm_enabled; - if (xen_enabled()) { - s->use_acpi_pci_hotplug = false; - } - - qdev_init_nofail(dev); - - return s->smb.smbus; -} - -static uint64_t gpe_readb(void *opaque, hwaddr addr, unsigned width) -{ - PIIX4PMState *s = opaque; - uint32_t val = acpi_gpe_ioport_readb(&s->ar, addr); - - PIIX4_DPRINTF("gpe read %" HWADDR_PRIx " == %" PRIu32 "\n", addr, val); - return val; -} - -static void gpe_writeb(void *opaque, hwaddr addr, uint64_t val, - unsigned width) -{ - PIIX4PMState *s = opaque; - - acpi_gpe_ioport_writeb(&s->ar, addr, val); - acpi_update_sci(&s->ar, s->irq); - - PIIX4_DPRINTF("gpe write %" HWADDR_PRIx " <== %" PRIu64 "\n", addr, val); -} - -static const MemoryRegionOps piix4_gpe_ops = { - .read = gpe_readb, - .write = gpe_writeb, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .impl.min_access_size = 1, - .impl.max_access_size = 1, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, - PCIBus *bus, PIIX4PMState *s) -{ - memory_region_init_io(&s->io_gpe, OBJECT(s), &piix4_gpe_ops, s, - "acpi-gpe0", GPE_LEN); - memory_region_add_subregion(parent, GPE_BASE, &s->io_gpe); - - acpi_pcihp_init(OBJECT(s), &s->acpi_pci_hotplug, bus, parent, - s->use_acpi_pci_hotplug); - - acpi_cpu_hotplug_init(parent, OBJECT(s), &s->gpe_cpu, - PIIX4_CPU_HOTPLUG_IO_BASE); - - if (s->acpi_memory_hotplug.is_enabled) { - acpi_memory_hotplug_init(parent, OBJECT(s), &s->acpi_memory_hotplug); - } -} - -static void piix4_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list) -{ - PIIX4PMState *s = PIIX4_PM(adev); - - acpi_memory_ospm_status(&s->acpi_memory_hotplug, list); -} - -static Property piix4_pm_properties[] = { - DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0), - DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 0), - DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_DISABLED, PIIX4PMState, disable_s4, 0), - DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_VAL, PIIX4PMState, s4_val, 2), - DEFINE_PROP_BOOL("acpi-pci-hotplug-with-bridge-support", PIIX4PMState, - use_acpi_pci_hotplug, true), - DEFINE_PROP_BOOL("memory-hotplug-support", PIIX4PMState, - acpi_memory_hotplug.is_enabled, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void piix4_pm_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(klass); - - k->realize = piix4_pm_realize; - k->config_write = pm_write_config; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82371AB_3; - k->revision = 0x03; - k->class_id = PCI_CLASS_BRIDGE_OTHER; - dc->desc = "PM"; - dc->vmsd = &vmstate_acpi; - dc->props = piix4_pm_properties; - /* - * Reason: part of PIIX4 southbridge, needs to be wired up, - * e.g. by mips_malta_init() - */ - dc->cannot_instantiate_with_device_add_yet = true; - dc->hotpluggable = false; - hc->plug = piix4_device_plug_cb; - hc->unplug_request = piix4_device_unplug_request_cb; - hc->unplug = piix4_device_unplug_cb; - adevc->ospm_status = piix4_ospm_status; -} - -static const TypeInfo piix4_pm_info = { - .name = TYPE_PIIX4_PM, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PIIX4PMState), - .class_init = piix4_pm_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { TYPE_ACPI_DEVICE_IF }, - { } - } -}; - -static void piix4_pm_register_types(void) -{ - type_register_static(&piix4_pm_info); -} - -type_init(piix4_pm_register_types) diff --git a/qemu/hw/acpi/tco.c b/qemu/hw/acpi/tco.c deleted file mode 100644 index 8ce7daf23..000000000 --- a/qemu/hw/acpi/tco.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * QEMU ICH9 TCO emulation - * - * Copyright (c) 2015 Paulo Alcantara - * - * 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" - -#include "hw/acpi/tco.h" - -//#define DEBUG - -#ifdef DEBUG -#define TCO_DEBUG(fmt, ...) \ - do { \ - fprintf(stderr, "%s "fmt, __func__, ## __VA_ARGS__); \ - } while (0) -#else -#define TCO_DEBUG(fmt, ...) do { } while (0) -#endif - -enum { - TCO_RLD_DEFAULT = 0x0000, - TCO_DAT_IN_DEFAULT = 0x00, - TCO_DAT_OUT_DEFAULT = 0x00, - TCO1_STS_DEFAULT = 0x0000, - TCO2_STS_DEFAULT = 0x0000, - TCO1_CNT_DEFAULT = 0x0000, - TCO2_CNT_DEFAULT = 0x0008, - TCO_MESSAGE1_DEFAULT = 0x00, - TCO_MESSAGE2_DEFAULT = 0x00, - TCO_WDCNT_DEFAULT = 0x00, - TCO_TMR_DEFAULT = 0x0004, - SW_IRQ_GEN_DEFAULT = 0x03, -}; - -static inline void tco_timer_reload(TCOIORegs *tr) -{ - tr->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - ((int64_t)(tr->tco.tmr & TCO_TMR_MASK) * TCO_TICK_NSEC); - timer_mod(tr->tco_timer, tr->expire_time); -} - -static inline void tco_timer_stop(TCOIORegs *tr) -{ - tr->expire_time = -1; -} - -static void tco_timer_expired(void *opaque) -{ - TCOIORegs *tr = opaque; - ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs); - ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm); - uint32_t gcs = pci_get_long(lpc->chip_config + ICH9_CC_GCS); - - tr->tco.rld = 0; - tr->tco.sts1 |= TCO_TIMEOUT; - if (++tr->timeouts_no == 2) { - tr->tco.sts2 |= TCO_SECOND_TO_STS; - tr->tco.sts2 |= TCO_BOOT_STS; - tr->timeouts_no = 0; - - if (!lpc->pin_strap.spkr_hi && !(gcs & ICH9_CC_GCS_NO_REBOOT)) { - watchdog_perform_action(); - tco_timer_stop(tr); - return; - } - } - - if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) { - ich9_generate_smi(); - } else { - ich9_generate_nmi(); - } - tr->tco.rld = tr->tco.tmr; - tco_timer_reload(tr); -} - -/* NOTE: values of 0 or 1 will be ignored by ICH */ -static inline int can_start_tco_timer(TCOIORegs *tr) -{ - return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1; -} - -static uint32_t tco_ioport_readw(TCOIORegs *tr, uint32_t addr) -{ - uint16_t rld; - - switch (addr) { - case TCO_RLD: - if (tr->expire_time != -1) { - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - int64_t elapsed = (tr->expire_time - now) / TCO_TICK_NSEC; - rld = (uint16_t)elapsed | (tr->tco.rld & ~TCO_RLD_MASK); - } else { - rld = tr->tco.rld; - } - return rld; - case TCO_DAT_IN: - return tr->tco.din; - case TCO_DAT_OUT: - return tr->tco.dout; - case TCO1_STS: - return tr->tco.sts1; - case TCO2_STS: - return tr->tco.sts2; - case TCO1_CNT: - return tr->tco.cnt1; - case TCO2_CNT: - return tr->tco.cnt2; - case TCO_MESSAGE1: - return tr->tco.msg1; - case TCO_MESSAGE2: - return tr->tco.msg2; - case TCO_WDCNT: - return tr->tco.wdcnt; - case TCO_TMR: - return tr->tco.tmr; - case SW_IRQ_GEN: - return tr->sw_irq_gen; - } - return 0; -} - -static void tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val) -{ - switch (addr) { - case TCO_RLD: - tr->timeouts_no = 0; - if (can_start_tco_timer(tr)) { - tr->tco.rld = tr->tco.tmr; - tco_timer_reload(tr); - } else { - tr->tco.rld = val; - } - break; - case TCO_DAT_IN: - tr->tco.din = val; - tr->tco.sts1 |= SW_TCO_SMI; - ich9_generate_smi(); - break; - case TCO_DAT_OUT: - tr->tco.dout = val; - tr->tco.sts1 |= TCO_INT_STS; - /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits */ - break; - case TCO1_STS: - tr->tco.sts1 = val & TCO1_STS_MASK; - break; - case TCO2_STS: - tr->tco.sts2 = val & TCO2_STS_MASK; - break; - case TCO1_CNT: - val &= TCO1_CNT_MASK; - /* - * once TCO_LOCK bit is set, it can not be cleared by software. a reset - * is required to change this bit from 1 to 0 -- it defaults to 0. - */ - tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK); - if (can_start_tco_timer(tr)) { - tr->tco.rld = tr->tco.tmr; - tco_timer_reload(tr); - } else { - tco_timer_stop(tr); - } - break; - case TCO2_CNT: - tr->tco.cnt2 = val; - break; - case TCO_MESSAGE1: - tr->tco.msg1 = val; - break; - case TCO_MESSAGE2: - tr->tco.msg2 = val; - break; - case TCO_WDCNT: - tr->tco.wdcnt = val; - break; - case TCO_TMR: - tr->tco.tmr = val; - break; - case SW_IRQ_GEN: - tr->sw_irq_gen = val; - break; - } -} - -static uint64_t tco_io_readw(void *opaque, hwaddr addr, unsigned width) -{ - TCOIORegs *tr = opaque; - return tco_ioport_readw(tr, addr); -} - -static void tco_io_writew(void *opaque, hwaddr addr, uint64_t val, - unsigned width) -{ - TCOIORegs *tr = opaque; - tco_ioport_writew(tr, addr, val); -} - -static const MemoryRegionOps tco_io_ops = { - .read = tco_io_readw, - .write = tco_io_writew, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .impl.min_access_size = 1, - .impl.max_access_size = 2, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent) -{ - *tr = (TCOIORegs) { - .tco = { - .rld = TCO_RLD_DEFAULT, - .din = TCO_DAT_IN_DEFAULT, - .dout = TCO_DAT_OUT_DEFAULT, - .sts1 = TCO1_STS_DEFAULT, - .sts2 = TCO2_STS_DEFAULT, - .cnt1 = TCO1_CNT_DEFAULT, - .cnt2 = TCO2_CNT_DEFAULT, - .msg1 = TCO_MESSAGE1_DEFAULT, - .msg2 = TCO_MESSAGE2_DEFAULT, - .wdcnt = TCO_WDCNT_DEFAULT, - .tmr = TCO_TMR_DEFAULT, - }, - .sw_irq_gen = SW_IRQ_GEN_DEFAULT, - .tco_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tco_timer_expired, tr), - .expire_time = -1, - .timeouts_no = 0, - }; - memory_region_init_io(&tr->io, memory_region_owner(parent), - &tco_io_ops, tr, "sm-tco", ICH9_PMIO_TCO_LEN); - memory_region_add_subregion(parent, ICH9_PMIO_TCO_RLD, &tr->io); -} - -const VMStateDescription vmstate_tco_io_sts = { - .name = "tco io device status", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT16(tco.rld, TCOIORegs), - VMSTATE_UINT8(tco.din, TCOIORegs), - VMSTATE_UINT8(tco.dout, TCOIORegs), - VMSTATE_UINT16(tco.sts1, TCOIORegs), - VMSTATE_UINT16(tco.sts2, TCOIORegs), - VMSTATE_UINT16(tco.cnt1, TCOIORegs), - VMSTATE_UINT16(tco.cnt2, TCOIORegs), - VMSTATE_UINT8(tco.msg1, TCOIORegs), - VMSTATE_UINT8(tco.msg2, TCOIORegs), - VMSTATE_UINT8(tco.wdcnt, TCOIORegs), - VMSTATE_UINT16(tco.tmr, TCOIORegs), - VMSTATE_UINT8(sw_irq_gen, TCOIORegs), - VMSTATE_TIMER_PTR(tco_timer, TCOIORegs), - VMSTATE_INT64(expire_time, TCOIORegs), - VMSTATE_UINT8(timeouts_no, TCOIORegs), - VMSTATE_END_OF_LIST() - } -}; diff --git a/qemu/hw/alpha/Makefile.objs b/qemu/hw/alpha/Makefile.objs deleted file mode 100644 index 5c742756f..000000000 --- a/qemu/hw/alpha/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -obj-y += dp264.o pci.o typhoon.o diff --git a/qemu/hw/alpha/alpha_sys.h b/qemu/hw/alpha/alpha_sys.h deleted file mode 100644 index e11025b4b..000000000 --- a/qemu/hw/alpha/alpha_sys.h +++ /dev/null @@ -1,21 +0,0 @@ -/* Alpha cores and system support chips. */ - -#ifndef HW_ALPHA_H -#define HW_ALPHA_H 1 - -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "hw/ide.h" -#include "hw/i386/pc.h" -#include "hw/irq.h" - - -PCIBus *typhoon_init(ram_addr_t, ISABus **, qemu_irq *, AlphaCPU *[4], - pci_map_irq_fn); - -/* alpha_pci.c. */ -extern const MemoryRegionOps alpha_pci_ignore_ops; -extern const MemoryRegionOps alpha_pci_conf1_ops; -extern const MemoryRegionOps alpha_pci_iack_ops; - -#endif diff --git a/qemu/hw/alpha/dp264.c b/qemu/hw/alpha/dp264.c deleted file mode 100644 index f1267b544..000000000 --- a/qemu/hw/alpha/dp264.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * QEMU Alpha DP264/CLIPPER hardware system emulator. - * - * Choose CLIPPER IRQ mappings over, say, DP264, MONET, or WEBBRICK - * variants because CLIPPER doesn't have an SMC669 SuperIO controller - * 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 - -static uint64_t cpu_alpha_superpage_to_phys(void *opaque, uint64_t addr) -{ - if (((addr >> 41) & 3) == 2) { - addr &= 0xffffffffffull; - } - return addr; -} - -/* Note that there are at least 3 viewpoints of IRQ numbers on Alpha systems. - (0) The dev_irq_n lines into the cpu, which we totally ignore, - (1) The DRIR lines in the typhoon chipset, - (2) The "vector" aka mangled interrupt number reported by SRM PALcode, - (3) The interrupt number assigned by the kernel. - The following function is concerned with (1) only. */ - -static int clipper_pci_map_irq(PCIDevice *d, int irq_num) -{ - int slot = d->devfn >> 3; - - assert(irq_num >= 0 && irq_num <= 3); - - return (slot + 1) * 4 + irq_num; -} - -static void clipper_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; - AlphaCPU *cpus[4]; - PCIBus *pci_bus; - ISABus *isa_bus; - qemu_irq rtc_irq; - long size, i; - char *palcode_filename; - uint64_t palcode_entry, palcode_low, palcode_high; - uint64_t kernel_entry, kernel_low, kernel_high; - - /* Create up to 4 cpus. */ - memset(cpus, 0, sizeof(cpus)); - for (i = 0; i < smp_cpus; ++i) { - cpus[i] = cpu_alpha_init(cpu_model ? cpu_model : "ev67"); - } - - cpus[0]->env.trap_arg0 = ram_size; - cpus[0]->env.trap_arg1 = 0; - cpus[0]->env.trap_arg2 = smp_cpus; - - /* Init the chipset. */ - pci_bus = typhoon_init(ram_size, &isa_bus, &rtc_irq, cpus, - clipper_pci_map_irq); - - /* Since we have an SRM-compatible PALcode, use the SRM epoch. */ - rtc_init(isa_bus, 1900, rtc_irq); - - pit_init(isa_bus, 0x40, 0, NULL); - isa_create_simple(isa_bus, "i8042"); - - /* VGA setup. Don't bother loading the bios. */ - pci_vga_init(pci_bus); - - /* Serial code setup. */ - serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS); - - /* Network setup. e1000 is good enough, failing Tulip support. */ - for (i = 0; i < nb_nics; i++) { - pci_nic_init_nofail(&nd_table[i], pci_bus, "e1000", NULL); - } - - /* IDE disk setup. */ - { - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - ide_drive_get(hd, ARRAY_SIZE(hd)); - - pci_cmd646_ide_init(pci_bus, hd, 0); - } - - /* Load PALcode. Given that this is not "real" cpu palcode, - but one explicitly written for the emulation, we might as - well load it directly from and ELF image. */ - palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, - bios_name ? bios_name : "palcode-clipper"); - if (palcode_filename == NULL) { - 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); - if (size < 0) { - error_report("could not load palcode '%s'", palcode_filename); - exit(1); - } - g_free(palcode_filename); - - /* Start all cpus at the PALcode RESET entry point. */ - for (i = 0; i < smp_cpus; ++i) { - cpus[i]->env.pal_mode = 1; - cpus[i]->env.pc = palcode_entry; - cpus[i]->env.palbr = palcode_entry; - } - - /* Load a kernel. */ - if (kernel_filename) { - uint64_t param_offset; - - size = load_elf(kernel_filename, cpu_alpha_superpage_to_phys, - NULL, &kernel_entry, &kernel_low, &kernel_high, - 0, EM_ALPHA, 0, 0); - if (size < 0) { - error_report("could not load kernel '%s'", kernel_filename); - exit(1); - } - - cpus[0]->env.trap_arg1 = kernel_entry; - - param_offset = kernel_low - 0x6000; - - if (kernel_cmdline) { - pstrcpy_targphys("cmdline", param_offset, 0x100, kernel_cmdline); - } - - if (initrd_filename) { - long initrd_base, initrd_size; - - initrd_size = get_image_size(initrd_filename); - if (initrd_size < 0) { - error_report("could not load initial ram disk '%s'", - initrd_filename); - exit(1); - } - - /* Put the initrd image as high in memory as possible. */ - initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK; - load_image_targphys(initrd_filename, initrd_base, - ram_size - initrd_base); - - address_space_stq(&address_space_memory, param_offset + 0x100, - initrd_base + 0xfffffc0000000000ULL, - MEMTXATTRS_UNSPECIFIED, - NULL); - address_space_stq(&address_space_memory, param_offset + 0x108, - initrd_size, MEMTXATTRS_UNSPECIFIED, NULL); - } - } -} - -static void clipper_machine_init(MachineClass *mc) -{ - mc->desc = "Alpha DP264/CLIPPER"; - mc->init = clipper_init; - mc->max_cpus = 4; - mc->is_default = 1; -} - -DEFINE_MACHINE("clipper", clipper_machine_init) diff --git a/qemu/hw/alpha/pci.c b/qemu/hw/alpha/pci.c deleted file mode 100644 index 5baa0eaf1..000000000 --- a/qemu/hw/alpha/pci.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * QEMU Alpha PCI support functions. - * - * Some of this isn't very Alpha specific at all. - * - * ??? Sparse memory access not implemented. - */ - -#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. */ - -static uint64_t ignore_read(void *opaque, hwaddr addr, unsigned size) -{ - return 0; -} - -static void ignore_write(void *opaque, hwaddr addr, uint64_t v, unsigned size) -{ -} - -const MemoryRegionOps alpha_pci_ignore_ops = { - .read = ignore_read, - .write = ignore_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 8, - }, - .impl = { - .min_access_size = 1, - .max_access_size = 8, - }, -}; - - -/* PCI config space reads/writes, to byte-word addressable memory. */ -static uint64_t bw_conf1_read(void *opaque, hwaddr addr, - unsigned size) -{ - PCIBus *b = opaque; - return pci_data_read(b, addr, size); -} - -static void bw_conf1_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - PCIBus *b = opaque; - pci_data_write(b, addr, val, size); -} - -const MemoryRegionOps alpha_pci_conf1_ops = { - .read = bw_conf1_read, - .write = bw_conf1_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 4, - }, -}; - -/* PCI/EISA Interrupt Acknowledge Cycle. */ - -static uint64_t iack_read(void *opaque, hwaddr addr, unsigned size) -{ - return pic_read_irq(isa_pic); -} - -static void special_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - trace_alpha_pci_iack_write(); -} - -const MemoryRegionOps alpha_pci_iack_ops = { - .read = iack_read, - .write = special_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; diff --git a/qemu/hw/alpha/typhoon.c b/qemu/hw/alpha/typhoon.c deleted file mode 100644 index 97721b535..000000000 --- a/qemu/hw/alpha/typhoon.c +++ /dev/null @@ -1,959 +0,0 @@ -/* - * DEC 21272 (TSUNAMI/TYPHOON) chipset emulation. - * - * Written by Richard Henderson. - * - * 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" -#include "sysemu/sysemu.h" -#include "alpha_sys.h" -#include "exec/address-spaces.h" - - -#define TYPE_TYPHOON_PCI_HOST_BRIDGE "typhoon-pcihost" - -typedef struct TyphoonCchip { - MemoryRegion region; - uint64_t misc; - uint64_t drir; - uint64_t dim[4]; - uint32_t iic[4]; - AlphaCPU *cpu[4]; -} TyphoonCchip; - -typedef struct TyphoonWindow { - uint64_t wba; - uint64_t wsm; - uint64_t tba; -} TyphoonWindow; - -typedef struct TyphoonPchip { - MemoryRegion region; - MemoryRegion reg_iack; - MemoryRegion reg_mem; - MemoryRegion reg_io; - MemoryRegion reg_conf; - - AddressSpace iommu_as; - MemoryRegion iommu; - - uint64_t ctl; - TyphoonWindow win[4]; -} TyphoonPchip; - -#define TYPHOON_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(TyphoonState, (obj), TYPE_TYPHOON_PCI_HOST_BRIDGE) - -typedef struct TyphoonState { - PCIHostState parent_obj; - - TyphoonCchip cchip; - TyphoonPchip pchip; - MemoryRegion dchip_region; - MemoryRegion ram_region; -} TyphoonState; - -/* Called when one of DRIR or DIM changes. */ -static void cpu_irq_change(AlphaCPU *cpu, uint64_t req) -{ - /* If there are any non-masked interrupts, tell the cpu. */ - if (cpu != NULL) { - CPUState *cs = CPU(cpu); - if (req) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } - } -} - -static uint64_t cchip_read(void *opaque, hwaddr addr, unsigned size) -{ - CPUState *cpu = current_cpu; - TyphoonState *s = opaque; - uint64_t ret = 0; - - switch (addr) { - case 0x0000: - /* CSC: Cchip System Configuration Register. */ - /* All sorts of data here; probably the only thing relevant is - PIP<14> Pchip 1 Present = 0. */ - break; - - case 0x0040: - /* MTR: Memory Timing Register. */ - /* All sorts of stuff related to real DRAM. */ - break; - - case 0x0080: - /* MISC: Miscellaneous Register. */ - ret = s->cchip.misc | (cpu->cpu_index & 3); - break; - - case 0x00c0: - /* MPD: Memory Presence Detect Register. */ - break; - - case 0x0100: /* AAR0 */ - case 0x0140: /* AAR1 */ - case 0x0180: /* AAR2 */ - case 0x01c0: /* AAR3 */ - /* AAR: Array Address Register. */ - /* All sorts of information about DRAM. */ - break; - - case 0x0200: - /* DIM0: Device Interrupt Mask Register, CPU0. */ - ret = s->cchip.dim[0]; - break; - case 0x0240: - /* DIM1: Device Interrupt Mask Register, CPU1. */ - ret = s->cchip.dim[1]; - break; - case 0x0280: - /* DIR0: Device Interrupt Request Register, CPU0. */ - ret = s->cchip.dim[0] & s->cchip.drir; - break; - case 0x02c0: - /* DIR1: Device Interrupt Request Register, CPU1. */ - ret = s->cchip.dim[1] & s->cchip.drir; - break; - case 0x0300: - /* DRIR: Device Raw Interrupt Request Register. */ - ret = s->cchip.drir; - break; - - case 0x0340: - /* PRBEN: Probe Enable Register. */ - break; - - case 0x0380: - /* IIC0: Interval Ignore Count Register, CPU0. */ - ret = s->cchip.iic[0]; - break; - case 0x03c0: - /* IIC1: Interval Ignore Count Register, CPU1. */ - ret = s->cchip.iic[1]; - break; - - case 0x0400: /* MPR0 */ - case 0x0440: /* MPR1 */ - case 0x0480: /* MPR2 */ - case 0x04c0: /* MPR3 */ - /* MPR: Memory Programming Register. */ - break; - - case 0x0580: - /* TTR: TIGbus Timing Register. */ - /* All sorts of stuff related to interrupt delivery timings. */ - break; - case 0x05c0: - /* TDR: TIGbug Device Timing Register. */ - break; - - case 0x0600: - /* DIM2: Device Interrupt Mask Register, CPU2. */ - ret = s->cchip.dim[2]; - break; - case 0x0640: - /* DIM3: Device Interrupt Mask Register, CPU3. */ - ret = s->cchip.dim[3]; - break; - case 0x0680: - /* DIR2: Device Interrupt Request Register, CPU2. */ - ret = s->cchip.dim[2] & s->cchip.drir; - break; - case 0x06c0: - /* DIR3: Device Interrupt Request Register, CPU3. */ - ret = s->cchip.dim[3] & s->cchip.drir; - break; - - case 0x0700: - /* IIC2: Interval Ignore Count Register, CPU2. */ - ret = s->cchip.iic[2]; - break; - case 0x0740: - /* IIC3: Interval Ignore Count Register, CPU3. */ - ret = s->cchip.iic[3]; - break; - - case 0x0780: - /* PWR: Power Management Control. */ - break; - - case 0x0c00: /* CMONCTLA */ - case 0x0c40: /* CMONCTLB */ - case 0x0c80: /* CMONCNT01 */ - case 0x0cc0: /* CMONCNT23 */ - break; - - default: - cpu_unassigned_access(cpu, addr, false, false, 0, size); - return -1; - } - - return ret; -} - -static uint64_t dchip_read(void *opaque, hwaddr addr, unsigned size) -{ - /* Skip this. It's all related to DRAM timing and setup. */ - return 0; -} - -static uint64_t pchip_read(void *opaque, hwaddr addr, unsigned size) -{ - TyphoonState *s = opaque; - uint64_t ret = 0; - - switch (addr) { - case 0x0000: - /* WSBA0: Window Space Base Address Register. */ - ret = s->pchip.win[0].wba; - break; - case 0x0040: - /* WSBA1 */ - ret = s->pchip.win[1].wba; - break; - case 0x0080: - /* WSBA2 */ - ret = s->pchip.win[2].wba; - break; - case 0x00c0: - /* WSBA3 */ - ret = s->pchip.win[3].wba; - break; - - case 0x0100: - /* WSM0: Window Space Mask Register. */ - ret = s->pchip.win[0].wsm; - break; - case 0x0140: - /* WSM1 */ - ret = s->pchip.win[1].wsm; - break; - case 0x0180: - /* WSM2 */ - ret = s->pchip.win[2].wsm; - break; - case 0x01c0: - /* WSM3 */ - ret = s->pchip.win[3].wsm; - break; - - case 0x0200: - /* TBA0: Translated Base Address Register. */ - ret = s->pchip.win[0].tba; - break; - case 0x0240: - /* TBA1 */ - ret = s->pchip.win[1].tba; - break; - case 0x0280: - /* TBA2 */ - ret = s->pchip.win[2].tba; - break; - case 0x02c0: - /* TBA3 */ - ret = s->pchip.win[3].tba; - break; - - case 0x0300: - /* PCTL: Pchip Control Register. */ - ret = s->pchip.ctl; - break; - case 0x0340: - /* PLAT: Pchip Master Latency Register. */ - break; - case 0x03c0: - /* PERROR: Pchip Error Register. */ - break; - case 0x0400: - /* PERRMASK: Pchip Error Mask Register. */ - break; - case 0x0440: - /* PERRSET: Pchip Error Set Register. */ - break; - case 0x0480: - /* TLBIV: Translation Buffer Invalidate Virtual Register (WO). */ - break; - case 0x04c0: - /* TLBIA: Translation Buffer Invalidate All Register (WO). */ - break; - case 0x0500: /* PMONCTL */ - case 0x0540: /* PMONCNT */ - case 0x0800: /* SPRST */ - break; - - default: - cpu_unassigned_access(current_cpu, addr, false, false, 0, size); - return -1; - } - - return ret; -} - -static void cchip_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - TyphoonState *s = opaque; - uint64_t oldval, newval; - - switch (addr) { - case 0x0000: - /* CSC: Cchip System Configuration Register. */ - /* All sorts of data here; nothing relevant RW. */ - break; - - case 0x0040: - /* MTR: Memory Timing Register. */ - /* All sorts of stuff related to real DRAM. */ - break; - - case 0x0080: - /* MISC: Miscellaneous Register. */ - newval = oldval = s->cchip.misc; - newval &= ~(val & 0x10000ff0); /* W1C fields */ - if (val & 0x100000) { - newval &= ~0xff0000ull; /* ACL clears ABT and ABW */ - } else { - newval |= val & 0x00f00000; /* ABT field is W1S */ - if ((newval & 0xf0000) == 0) { - newval |= val & 0xf0000; /* ABW field is W1S iff zero */ - } - } - newval |= (val & 0xf000) >> 4; /* IPREQ field sets IPINTR. */ - - newval &= ~0xf0000000000ull; /* WO and RW fields */ - newval |= val & 0xf0000000000ull; - s->cchip.misc = newval; - - /* Pass on changes to IPI and ITI state. */ - if ((newval ^ oldval) & 0xff0) { - int i; - for (i = 0; i < 4; ++i) { - AlphaCPU *cpu = s->cchip.cpu[i]; - if (cpu != NULL) { - CPUState *cs = CPU(cpu); - /* IPI can be either cleared or set by the write. */ - if (newval & (1 << (i + 8))) { - cpu_interrupt(cs, CPU_INTERRUPT_SMP); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_SMP); - } - - /* ITI can only be cleared by the write. */ - if ((newval & (1 << (i + 4))) == 0) { - cpu_reset_interrupt(cs, CPU_INTERRUPT_TIMER); - } - } - } - } - break; - - case 0x00c0: - /* MPD: Memory Presence Detect Register. */ - break; - - case 0x0100: /* AAR0 */ - case 0x0140: /* AAR1 */ - case 0x0180: /* AAR2 */ - case 0x01c0: /* AAR3 */ - /* AAR: Array Address Register. */ - /* All sorts of information about DRAM. */ - break; - - case 0x0200: /* DIM0 */ - /* DIM: Device Interrupt Mask Register, CPU0. */ - s->cchip.dim[0] = val; - cpu_irq_change(s->cchip.cpu[0], val & s->cchip.drir); - break; - case 0x0240: /* DIM1 */ - /* DIM: Device Interrupt Mask Register, CPU1. */ - s->cchip.dim[0] = val; - cpu_irq_change(s->cchip.cpu[1], val & s->cchip.drir); - break; - - case 0x0280: /* DIR0 (RO) */ - case 0x02c0: /* DIR1 (RO) */ - case 0x0300: /* DRIR (RO) */ - break; - - case 0x0340: - /* PRBEN: Probe Enable Register. */ - break; - - case 0x0380: /* IIC0 */ - s->cchip.iic[0] = val & 0xffffff; - break; - case 0x03c0: /* IIC1 */ - s->cchip.iic[1] = val & 0xffffff; - break; - - case 0x0400: /* MPR0 */ - case 0x0440: /* MPR1 */ - case 0x0480: /* MPR2 */ - case 0x04c0: /* MPR3 */ - /* MPR: Memory Programming Register. */ - break; - - case 0x0580: - /* TTR: TIGbus Timing Register. */ - /* All sorts of stuff related to interrupt delivery timings. */ - break; - case 0x05c0: - /* TDR: TIGbug Device Timing Register. */ - break; - - case 0x0600: - /* DIM2: Device Interrupt Mask Register, CPU2. */ - s->cchip.dim[2] = val; - cpu_irq_change(s->cchip.cpu[2], val & s->cchip.drir); - break; - case 0x0640: - /* DIM3: Device Interrupt Mask Register, CPU3. */ - s->cchip.dim[3] = val; - cpu_irq_change(s->cchip.cpu[3], val & s->cchip.drir); - break; - - case 0x0680: /* DIR2 (RO) */ - case 0x06c0: /* DIR3 (RO) */ - break; - - case 0x0700: /* IIC2 */ - s->cchip.iic[2] = val & 0xffffff; - break; - case 0x0740: /* IIC3 */ - s->cchip.iic[3] = val & 0xffffff; - break; - - case 0x0780: - /* PWR: Power Management Control. */ - break; - - case 0x0c00: /* CMONCTLA */ - case 0x0c40: /* CMONCTLB */ - case 0x0c80: /* CMONCNT01 */ - case 0x0cc0: /* CMONCNT23 */ - break; - - default: - cpu_unassigned_access(current_cpu, addr, true, false, 0, size); - return; - } -} - -static void dchip_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - /* Skip this. It's all related to DRAM timing and setup. */ -} - -static void pchip_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - TyphoonState *s = opaque; - uint64_t oldval; - - switch (addr) { - case 0x0000: - /* WSBA0: Window Space Base Address Register. */ - s->pchip.win[0].wba = val & 0xfff00003u; - break; - case 0x0040: - /* WSBA1 */ - s->pchip.win[1].wba = val & 0xfff00003u; - break; - case 0x0080: - /* WSBA2 */ - s->pchip.win[2].wba = val & 0xfff00003u; - break; - case 0x00c0: - /* WSBA3 */ - s->pchip.win[3].wba = (val & 0x80fff00001ull) | 2; - break; - - case 0x0100: - /* WSM0: Window Space Mask Register. */ - s->pchip.win[0].wsm = val & 0xfff00000u; - break; - case 0x0140: - /* WSM1 */ - s->pchip.win[1].wsm = val & 0xfff00000u; - break; - case 0x0180: - /* WSM2 */ - s->pchip.win[2].wsm = val & 0xfff00000u; - break; - case 0x01c0: - /* WSM3 */ - s->pchip.win[3].wsm = val & 0xfff00000u; - break; - - case 0x0200: - /* TBA0: Translated Base Address Register. */ - s->pchip.win[0].tba = val & 0x7fffffc00ull; - break; - case 0x0240: - /* TBA1 */ - s->pchip.win[1].tba = val & 0x7fffffc00ull; - break; - case 0x0280: - /* TBA2 */ - s->pchip.win[2].tba = val & 0x7fffffc00ull; - break; - case 0x02c0: - /* TBA3 */ - s->pchip.win[3].tba = val & 0x7fffffc00ull; - break; - - case 0x0300: - /* PCTL: Pchip Control Register. */ - oldval = s->pchip.ctl; - oldval &= ~0x00001cff0fc7ffull; /* RW fields */ - oldval |= val & 0x00001cff0fc7ffull; - s->pchip.ctl = oldval; - break; - - case 0x0340: - /* PLAT: Pchip Master Latency Register. */ - break; - case 0x03c0: - /* PERROR: Pchip Error Register. */ - break; - case 0x0400: - /* PERRMASK: Pchip Error Mask Register. */ - break; - case 0x0440: - /* PERRSET: Pchip Error Set Register. */ - break; - - case 0x0480: - /* TLBIV: Translation Buffer Invalidate Virtual Register. */ - break; - - case 0x04c0: - /* TLBIA: Translation Buffer Invalidate All Register (WO). */ - break; - - case 0x0500: - /* PMONCTL */ - case 0x0540: - /* PMONCNT */ - case 0x0800: - /* SPRST */ - break; - - default: - cpu_unassigned_access(current_cpu, addr, true, false, 0, size); - return; - } -} - -static const MemoryRegionOps cchip_ops = { - .read = cchip_read, - .write = cchip_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 8, - .max_access_size = 8, - }, - .impl = { - .min_access_size = 8, - .max_access_size = 8, - }, -}; - -static const MemoryRegionOps dchip_ops = { - .read = dchip_read, - .write = dchip_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 8, - .max_access_size = 8, - }, - .impl = { - .min_access_size = 8, - .max_access_size = 8, - }, -}; - -static const MemoryRegionOps pchip_ops = { - .read = pchip_read, - .write = pchip_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 8, - .max_access_size = 8, - }, - .impl = { - .min_access_size = 8, - .max_access_size = 8, - }, -}; - -/* A subroutine of typhoon_translate_iommu that builds an IOMMUTLBEntry - using the given translated address and mask. */ -static bool make_iommu_tlbe(hwaddr taddr, hwaddr mask, IOMMUTLBEntry *ret) -{ - *ret = (IOMMUTLBEntry) { - .target_as = &address_space_memory, - .translated_addr = taddr, - .addr_mask = mask, - .perm = IOMMU_RW, - }; - return true; -} - -/* A subroutine of typhoon_translate_iommu that handles scatter-gather - translation, given the address of the PTE. */ -static bool pte_translate(hwaddr pte_addr, IOMMUTLBEntry *ret) -{ - uint64_t pte = address_space_ldq(&address_space_memory, pte_addr, - MEMTXATTRS_UNSPECIFIED, NULL); - - /* Check valid bit. */ - if ((pte & 1) == 0) { - return false; - } - - return make_iommu_tlbe((pte & 0x3ffffe) << 12, 0x1fff, ret); -} - -/* A subroutine of typhoon_translate_iommu that handles one of the - four single-address-cycle translation windows. */ -static bool window_translate(TyphoonWindow *win, hwaddr addr, - IOMMUTLBEntry *ret) -{ - uint32_t wba = win->wba; - uint64_t wsm = win->wsm; - uint64_t tba = win->tba; - uint64_t wsm_ext = wsm | 0xfffff; - - /* Check for window disabled. */ - if ((wba & 1) == 0) { - return false; - } - - /* Check for window hit. */ - if ((addr & ~wsm_ext) != (wba & 0xfff00000u)) { - return false; - } - - if (wba & 2) { - /* Scatter-gather translation. */ - hwaddr pte_addr; - - /* See table 10-6, Generating PTE address for PCI DMA Address. */ - pte_addr = tba & ~(wsm >> 10); - pte_addr |= (addr & (wsm | 0xfe000)) >> 10; - return pte_translate(pte_addr, ret); - } else { - /* Direct-mapped translation. */ - return make_iommu_tlbe(tba & ~wsm_ext, wsm_ext, ret); - } -} - -/* Handle PCI-to-system address translation. */ -/* TODO: A translation failure here ought to set PCI error codes on the - Pchip and generate a machine check interrupt. */ -static IOMMUTLBEntry typhoon_translate_iommu(MemoryRegion *iommu, hwaddr addr, - bool is_write) -{ - TyphoonPchip *pchip = container_of(iommu, TyphoonPchip, iommu); - IOMMUTLBEntry ret; - int i; - - if (addr <= 0xffffffffu) { - /* Single-address cycle. */ - - /* Check for the Window Hole, inhibiting matching. */ - if ((pchip->ctl & 0x20) - && addr >= 0x80000 - && addr <= 0xfffff) { - goto failure; - } - - /* Check the first three windows. */ - for (i = 0; i < 3; ++i) { - if (window_translate(&pchip->win[i], addr, &ret)) { - goto success; - } - } - - /* Check the fourth window for DAC disable. */ - if ((pchip->win[3].wba & 0x80000000000ull) == 0 - && window_translate(&pchip->win[3], addr, &ret)) { - goto success; - } - } else { - /* Double-address cycle. */ - - if (addr >= 0x10000000000ull && addr < 0x20000000000ull) { - /* Check for the DMA monster window. */ - if (pchip->ctl & 0x40) { - /* See 10.1.4.4; in particular <39:35> is ignored. */ - make_iommu_tlbe(0, 0x007ffffffffull, &ret); - goto success; - } - } - - if (addr >= 0x80000000000ull && addr <= 0xfffffffffffull) { - /* Check the fourth window for DAC enable and window enable. */ - if ((pchip->win[3].wba & 0x80000000001ull) == 0x80000000001ull) { - uint64_t pte_addr; - - pte_addr = pchip->win[3].tba & 0x7ffc00000ull; - pte_addr |= (addr & 0xffffe000u) >> 10; - if (pte_translate(pte_addr, &ret)) { - goto success; - } - } - } - } - - failure: - ret = (IOMMUTLBEntry) { .perm = IOMMU_NONE }; - success: - return ret; -} - -static const MemoryRegionIOMMUOps typhoon_iommu_ops = { - .translate = typhoon_translate_iommu, -}; - -static AddressSpace *typhoon_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) -{ - TyphoonState *s = opaque; - return &s->pchip.iommu_as; -} - -static void typhoon_set_irq(void *opaque, int irq, int level) -{ - TyphoonState *s = opaque; - uint64_t drir; - int i; - - /* Set/Reset the bit in CCHIP.DRIR based on IRQ+LEVEL. */ - drir = s->cchip.drir; - if (level) { - drir |= 1ull << irq; - } else { - drir &= ~(1ull << irq); - } - s->cchip.drir = drir; - - for (i = 0; i < 4; ++i) { - cpu_irq_change(s->cchip.cpu[i], s->cchip.dim[i] & drir); - } -} - -static void typhoon_set_isa_irq(void *opaque, int irq, int level) -{ - typhoon_set_irq(opaque, 55, level); -} - -static void typhoon_set_timer_irq(void *opaque, int irq, int level) -{ - TyphoonState *s = opaque; - int i; - - /* Thankfully, the mc146818rtc code doesn't track the IRQ state, - and so we don't have to worry about missing interrupts just - because we never actually ACK the interrupt. Just ignore any - case of the interrupt level going low. */ - if (level == 0) { - return; - } - - /* Deliver the interrupt to each CPU, considering each CPU's IIC. */ - for (i = 0; i < 4; ++i) { - AlphaCPU *cpu = s->cchip.cpu[i]; - if (cpu != NULL) { - uint32_t iic = s->cchip.iic[i]; - - /* ??? The verbage in Section 10.2.2.10 isn't 100% clear. - Bit 24 is the OverFlow bit, RO, and set when the count - decrements past 0. When is OF cleared? My guess is that - OF is actually cleared when the IIC is written, and that - the ICNT field always decrements. At least, that's an - interpretation that makes sense, and "allows the CPU to - determine exactly how mant interval timer ticks were - skipped". At least within the next 4M ticks... */ - - iic = ((iic - 1) & 0x1ffffff) | (iic & 0x1000000); - s->cchip.iic[i] = iic; - - if (iic & 0x1000000) { - /* Set the ITI bit for this cpu. */ - s->cchip.misc |= 1 << (i + 4); - /* And signal the interrupt. */ - cpu_interrupt(CPU(cpu), CPU_INTERRUPT_TIMER); - } - } - } -} - -static void typhoon_alarm_timer(void *opaque) -{ - TyphoonState *s = (TyphoonState *)((uintptr_t)opaque & ~3); - int cpu = (uintptr_t)opaque & 3; - - /* Set the ITI bit for this cpu. */ - s->cchip.misc |= 1 << (cpu + 4); - cpu_interrupt(CPU(s->cchip.cpu[cpu]), CPU_INTERRUPT_TIMER); -} - -PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, - qemu_irq *p_rtc_irq, - AlphaCPU *cpus[4], pci_map_irq_fn sys_map_irq) -{ - const uint64_t MB = 1024 * 1024; - const uint64_t GB = 1024 * MB; - MemoryRegion *addr_space = get_system_memory(); - DeviceState *dev; - TyphoonState *s; - PCIHostState *phb; - PCIBus *b; - int i; - - dev = qdev_create(NULL, TYPE_TYPHOON_PCI_HOST_BRIDGE); - qdev_init_nofail(dev); - - s = TYPHOON_PCI_HOST_BRIDGE(dev); - phb = PCI_HOST_BRIDGE(dev); - - s->cchip.misc = 0x800000000ull; /* Revision: Typhoon. */ - s->pchip.win[3].wba = 2; /* Window 3 SG always enabled. */ - - /* Remember the CPUs so that we can deliver interrupts to them. */ - for (i = 0; i < 4; i++) { - AlphaCPU *cpu = cpus[i]; - s->cchip.cpu[i] = cpu; - if (cpu != NULL) { - cpu->alarm_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - typhoon_alarm_timer, - (void *)((uintptr_t)s + i)); - } - } - - *p_rtc_irq = qemu_allocate_irq(typhoon_set_timer_irq, s, 0); - - /* Main memory region, 0x00.0000.0000. Real hardware supports 32GB, - but the address space hole reserved at this point is 8TB. */ - memory_region_allocate_system_memory(&s->ram_region, OBJECT(s), "ram", - ram_size); - memory_region_add_subregion(addr_space, 0, &s->ram_region); - - /* TIGbus, 0x801.0000.0000, 1GB. */ - /* ??? The TIGbus is used for delivering interrupts, and access to - the flash ROM. I'm not sure that we need to implement it at all. */ - - /* Pchip0 CSRs, 0x801.8000.0000, 256MB. */ - memory_region_init_io(&s->pchip.region, OBJECT(s), &pchip_ops, s, "pchip0", - 256*MB); - memory_region_add_subregion(addr_space, 0x80180000000ULL, - &s->pchip.region); - - /* Cchip CSRs, 0x801.A000.0000, 256MB. */ - memory_region_init_io(&s->cchip.region, OBJECT(s), &cchip_ops, s, "cchip0", - 256*MB); - memory_region_add_subregion(addr_space, 0x801a0000000ULL, - &s->cchip.region); - - /* Dchip CSRs, 0x801.B000.0000, 256MB. */ - memory_region_init_io(&s->dchip_region, OBJECT(s), &dchip_ops, s, "dchip0", - 256*MB); - memory_region_add_subregion(addr_space, 0x801b0000000ULL, - &s->dchip_region); - - /* Pchip0 PCI memory, 0x800.0000.0000, 4GB. */ - memory_region_init(&s->pchip.reg_mem, OBJECT(s), "pci0-mem", 4*GB); - memory_region_add_subregion(addr_space, 0x80000000000ULL, - &s->pchip.reg_mem); - - /* Pchip0 PCI I/O, 0x801.FC00.0000, 32MB. */ - memory_region_init_io(&s->pchip.reg_io, OBJECT(s), &alpha_pci_ignore_ops, - NULL, "pci0-io", 32*MB); - memory_region_add_subregion(addr_space, 0x801fc000000ULL, - &s->pchip.reg_io); - - b = pci_register_bus(dev, "pci", - typhoon_set_irq, sys_map_irq, s, - &s->pchip.reg_mem, &s->pchip.reg_io, - 0, 64, TYPE_PCI_BUS); - phb->bus = b; - - /* Host memory as seen from the PCI side, via the IOMMU. */ - memory_region_init_iommu(&s->pchip.iommu, OBJECT(s), &typhoon_iommu_ops, - "iommu-typhoon", UINT64_MAX); - address_space_init(&s->pchip.iommu_as, &s->pchip.iommu, "pchip0-pci"); - pci_setup_iommu(b, typhoon_pci_dma_iommu, s); - - /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */ - memory_region_init_io(&s->pchip.reg_iack, OBJECT(s), &alpha_pci_iack_ops, - b, "pci0-iack", 64*MB); - memory_region_add_subregion(addr_space, 0x801f8000000ULL, - &s->pchip.reg_iack); - - /* Pchip0 PCI configuration, 0x801.FE00.0000, 16MB. */ - memory_region_init_io(&s->pchip.reg_conf, OBJECT(s), &alpha_pci_conf1_ops, - b, "pci0-conf", 16*MB); - memory_region_add_subregion(addr_space, 0x801fe000000ULL, - &s->pchip.reg_conf); - - /* For the record, these are the mappings for the second PCI bus. - We can get away with not implementing them because we indicate - via the Cchip.CSC bit that Pchip1 is not present. */ - /* Pchip1 PCI memory, 0x802.0000.0000, 4GB. */ - /* Pchip1 CSRs, 0x802.8000.0000, 256MB. */ - /* Pchip1 PCI special/interrupt acknowledge, 0x802.F800.0000, 64MB. */ - /* Pchip1 PCI I/O, 0x802.FC00.0000, 32MB. */ - /* Pchip1 PCI configuration, 0x802.FE00.0000, 16MB. */ - - /* Init the ISA bus. */ - /* ??? Technically there should be a cy82c693ub pci-isa bridge. */ - { - qemu_irq *isa_irqs; - - *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); - } - - return b; -} - -static int typhoon_pcihost_init(SysBusDevice *dev) -{ - return 0; -} - -static void typhoon_pcihost_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = typhoon_pcihost_init; -} - -static const TypeInfo typhoon_pcihost_info = { - .name = TYPE_TYPHOON_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(TyphoonState), - .class_init = typhoon_pcihost_class_init, -}; - -static void typhoon_register_types(void) -{ - type_register_static(&typhoon_pcihost_info); -} - -type_init(typhoon_register_types) diff --git a/qemu/hw/arm/Makefile.objs b/qemu/hw/arm/Makefile.objs deleted file mode 100644 index 954c9fe15..000000000 --- a/qemu/hw/arm/Makefile.objs +++ /dev/null @@ -1,19 +0,0 @@ -obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o -obj-$(CONFIG_DIGIC) += digic_boards.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 -obj-y += netduino2.o -obj-y += sysbus-fdt.o - -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 deleted file mode 100644 index ca15d1c8c..000000000 --- a/qemu/hw/arm/allwinner-a10.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Allwinner A10 SoC emulation - * - * Copyright (C) 2013 Li Guang - * Written by Li Guang - * - * 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. - */ - -#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" - -static void aw_a10_init(Object *obj) -{ - AwA10State *s = AW_A10(obj); - - object_initialize(&s->cpu, sizeof(s->cpu), "cortex-a8-" TYPE_ARM_CPU); - object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL); - - object_initialize(&s->intc, sizeof(s->intc), TYPE_AW_A10_PIC); - qdev_set_parent_bus(DEVICE(&s->intc), sysbus_get_default()); - - object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT); - qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default()); - - object_initialize(&s->emac, sizeof(s->emac), TYPE_AW_EMAC); - qdev_set_parent_bus(DEVICE(&s->emac), sysbus_get_default()); - /* FIXME use qdev NIC properties instead of nd_table[] */ - if (nd_table[0].used) { - 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) -{ - AwA10State *s = AW_A10(dev); - SysBusDevice *sysbusdev; - uint8_t i; - qemu_irq fiq, irq; - Error *err = NULL; - - object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - irq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ); - fiq = qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ); - - object_property_set_bool(OBJECT(&s->intc), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - sysbusdev = SYS_BUS_DEVICE(&s->intc); - sysbus_mmio_map(sysbusdev, 0, AW_A10_PIC_REG_BASE); - sysbus_connect_irq(sysbusdev, 0, irq); - sysbus_connect_irq(sysbusdev, 1, fiq); - for (i = 0; i < AW_A10_PIC_INT_NR; i++) { - s->irq[i] = qdev_get_gpio_in(DEVICE(&s->intc), i); - } - - object_property_set_bool(OBJECT(&s->timer), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - sysbusdev = SYS_BUS_DEVICE(&s->timer); - sysbus_mmio_map(sysbusdev, 0, AW_A10_PIT_REG_BASE); - sysbus_connect_irq(sysbusdev, 0, s->irq[22]); - sysbus_connect_irq(sysbusdev, 1, s->irq[23]); - sysbus_connect_irq(sysbusdev, 2, s->irq[24]); - sysbus_connect_irq(sysbusdev, 3, s->irq[25]); - sysbus_connect_irq(sysbusdev, 4, s->irq[67]); - sysbus_connect_irq(sysbusdev, 5, s->irq[68]); - - object_property_set_bool(OBJECT(&s->emac), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - sysbusdev = SYS_BUS_DEVICE(&s->emac); - 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); -} - -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 = { - .name = TYPE_AW_A10, - .parent = TYPE_DEVICE, - .instance_size = sizeof(AwA10State), - .instance_init = aw_a10_init, - .class_init = aw_a10_class_init, -}; - -static void aw_a10_register_types(void) -{ - type_register_static(&aw_a10_type_info); -} - -type_init(aw_a10_register_types) diff --git a/qemu/hw/arm/armv7m.c b/qemu/hw/arm/armv7m.c deleted file mode 100644 index bb2a22d96..000000000 --- a/qemu/hw/arm/armv7m.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * ARMV7M System emulation. - * - * Copyright (c) 2006-2007 CodeSourcery. - * Written by Paul Brook - * - * 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" -#include "elf.h" -#include "sysemu/qtest.h" -#include "qemu/error-report.h" - -/* Bitbanded IO. Each word corresponds to a single bit. */ - -/* Get the byte address of the real memory for a bitband access. */ -static inline uint32_t bitband_addr(void * opaque, uint32_t addr) -{ - uint32_t res; - - res = *(uint32_t *)opaque; - res |= (addr & 0x1ffffff) >> 5; - return res; - -} - -static uint32_t bitband_readb(void *opaque, hwaddr offset) -{ - uint8_t v; - cpu_physical_memory_read(bitband_addr(opaque, offset), &v, 1); - return (v & (1 << ((offset >> 2) & 7))) != 0; -} - -static void bitband_writeb(void *opaque, hwaddr offset, - uint32_t value) -{ - uint32_t addr; - uint8_t mask; - uint8_t v; - addr = bitband_addr(opaque, offset); - mask = (1 << ((offset >> 2) & 7)); - cpu_physical_memory_read(addr, &v, 1); - if (value & 1) - v |= mask; - else - v &= ~mask; - cpu_physical_memory_write(addr, &v, 1); -} - -static uint32_t bitband_readw(void *opaque, hwaddr offset) -{ - uint32_t addr; - uint16_t mask; - uint16_t v; - addr = bitband_addr(opaque, offset) & ~1; - mask = (1 << ((offset >> 2) & 15)); - mask = tswap16(mask); - cpu_physical_memory_read(addr, &v, 2); - return (v & mask) != 0; -} - -static void bitband_writew(void *opaque, hwaddr offset, - uint32_t value) -{ - uint32_t addr; - uint16_t mask; - uint16_t v; - addr = bitband_addr(opaque, offset) & ~1; - mask = (1 << ((offset >> 2) & 15)); - mask = tswap16(mask); - cpu_physical_memory_read(addr, &v, 2); - if (value & 1) - v |= mask; - else - v &= ~mask; - cpu_physical_memory_write(addr, &v, 2); -} - -static uint32_t bitband_readl(void *opaque, hwaddr offset) -{ - uint32_t addr; - uint32_t mask; - uint32_t v; - addr = bitband_addr(opaque, offset) & ~3; - mask = (1 << ((offset >> 2) & 31)); - mask = tswap32(mask); - cpu_physical_memory_read(addr, &v, 4); - return (v & mask) != 0; -} - -static void bitband_writel(void *opaque, hwaddr offset, - uint32_t value) -{ - uint32_t addr; - uint32_t mask; - uint32_t v; - addr = bitband_addr(opaque, offset) & ~3; - mask = (1 << ((offset >> 2) & 31)); - mask = tswap32(mask); - cpu_physical_memory_read(addr, &v, 4); - if (value & 1) - v |= mask; - else - v &= ~mask; - cpu_physical_memory_write(addr, &v, 4); -} - -static const MemoryRegionOps bitband_ops = { - .old_mmio = { - .read = { bitband_readb, bitband_readw, bitband_readl, }, - .write = { bitband_writeb, bitband_writew, bitband_writel, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -#define TYPE_BITBAND "ARM,bitband-memory" -#define BITBAND(obj) OBJECT_CHECK(BitBandState, (obj), TYPE_BITBAND) - -typedef struct { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - uint32_t base; -} BitBandState; - -static int bitband_init(SysBusDevice *dev) -{ - BitBandState *s = BITBAND(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &bitband_ops, &s->base, - "bitband", 0x02000000); - sysbus_init_mmio(dev, &s->iomem); - return 0; -} - -static void armv7m_bitband_init(void) -{ - DeviceState *dev; - - dev = qdev_create(NULL, TYPE_BITBAND); - qdev_prop_set_uint32(dev, "base", 0x20000000); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x22000000); - - dev = qdev_create(NULL, TYPE_BITBAND); - qdev_prop_set_uint32(dev, "base", 0x40000000); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x42000000); -} - -/* Board init. */ - -static void armv7m_reset(void *opaque) -{ - ARMCPU *cpu = opaque; - - cpu_reset(CPU(cpu)); -} - -/* Init CPU and memory for a v7-M based board. - mem_size is in bytes. - Returns the NVIC array. */ - -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; - int image_size; - uint64_t entry; - uint64_t lowaddr; - int big_endian; - MemoryRegion *hack = g_new(MemoryRegion, 1); - - if (cpu_model == NULL) { - cpu_model = "cortex-m3"; - } - cpu = cpu_arm_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - env = &cpu->env; - - armv7m_bitband_init(); - - nvic = qdev_create(NULL, "armv7m_nvic"); - qdev_prop_set_uint32(nvic, "num-irq", num_irq); - env->nvic = nvic; - qdev_init_nofail(nvic); - sysbus_connect_irq(SYS_BUS_DEVICE(nvic), 0, - qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ)); - -#ifdef TARGET_WORDS_BIGENDIAN - big_endian = 1; -#else - big_endian = 0; -#endif - - if (!kernel_filename && !qtest_enabled()) { - fprintf(stderr, "Guest image must be specified (using -kernel)\n"); - exit(1); - } - - if (kernel_filename) { - image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr, - NULL, big_endian, EM_ARM, 1, 0); - if (image_size < 0) { - image_size = load_image_targphys(kernel_filename, 0, mem_size); - lowaddr = 0; - } - if (image_size < 0) { - error_report("Could not load kernel '%s'", kernel_filename); - exit(1); - } - } - - /* 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_fatal); - vmstate_register_ram_global(hack); - memory_region_add_subregion(system_memory, 0xfffff000, hack); - - qemu_register_reset(armv7m_reset, cpu); - return nvic; -} - -static Property bitband_properties[] = { - DEFINE_PROP_UINT32("base", BitBandState, base, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void bitband_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = bitband_init; - dc->props = bitband_properties; -} - -static const TypeInfo bitband_info = { - .name = TYPE_BITBAND, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(BitBandState), - .class_init = bitband_class_init, -}; - -static void armv7m_register_types(void) -{ - type_register_static(&bitband_info); -} - -type_init(armv7m_register_types) diff --git a/qemu/hw/arm/ast2400.c b/qemu/hw/arm/ast2400.c deleted file mode 100644 index 03f993863..000000000 --- a/qemu/hw/arm/ast2400.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * AST2400 SoC - * - * Andrew Jeffery - * Jeremy Kerr - * - * 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 deleted file mode 100644 index 234d51843..000000000 --- a/qemu/hw/arm/bcm2835_peripherals.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * 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 deleted file mode 100644 index 8451190a1..000000000 --- a/qemu/hw/arm/bcm2836.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * 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 deleted file mode 100644 index 587694557..000000000 --- a/qemu/hw/arm/boot.c +++ /dev/null @@ -1,994 +0,0 @@ -/* - * ARM kernel loader. - * - * Copyright (c) 2006-2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ - -#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" -#include "elf.h" -#include "sysemu/device_tree.h" -#include "qemu/config-file.h" -#include "exec/address-spaces.h" - -/* Kernel boot protocol is specified in the kernel docs - * Documentation/arm/Booting and Documentation/arm64/booting.txt - * They have different preferred image load offsets from system RAM base. - */ -#define KERNEL_ARGS_ADDR 0x100 -#define KERNEL_LOAD_ADDR 0x00010000 -#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_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; - -typedef struct ARMInsnFixup { - uint32_t insn; - FixupType fixup; -} ARMInsnFixup; - -static const ARMInsnFixup bootloader_aarch64[] = { - { 0x580000c0 }, /* ldr x0, arg ; Load the lower 32-bits of DTB */ - { 0xaa1f03e1 }, /* mov x1, xzr */ - { 0xaa1f03e2 }, /* mov x2, xzr */ - { 0xaa1f03e3 }, /* mov x3, xzr */ - { 0x58000084 }, /* ldr x4, entry ; Load the lower 32-bits of kernel entry */ - { 0xd61f0080 }, /* br x4 ; Jump to the kernel entry point */ - { 0, FIXUP_ARGPTR }, /* arg: .word @DTB Lower 32-bits */ - { 0 }, /* .word @DTB Higher 32-bits */ - { 0, FIXUP_ENTRYPOINT }, /* entry: .word @Kernel Entry Lower 32-bits */ - { 0 }, /* .word @Kernel Entry Higher 32-bits */ - { 0, FIXUP_TERMINATOR } -}; - -/* 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] */ - { 0xe59ff004 }, /* ldr pc, [pc, #4] */ - { 0, FIXUP_BOARDID }, - { 0, FIXUP_ARGPTR }, - { 0, FIXUP_ENTRYPOINT }, - { 0, FIXUP_TERMINATOR } -}; - -/* Handling for secondary CPU boot in a multicore system. - * Unlike the uniprocessor/primary CPU boot, this is platform - * dependent. The default code here is based on the secondary - * CPU boot protocol used on realview/vexpress boards, with - * some parameterisation to increase its flexibility. - * QEMU platform models for which this code is not appropriate - * should override write_secondary_boot and secondary_cpu_reset_hook - * instead. - * - * This code enables the interrupt controllers for the secondary - * CPUs and then puts all the secondary CPUs into a loop waiting - * for an interprocessor interrupt and polling a configurable - * location for the kernel secondary CPU entry point. - */ -#define DSB_INSN 0xf57ff04f -#define CP15_DSB_INSN 0xee070f9a /* mcr cp15, 0, r0, c7, c10, 4 */ - -static const ARMInsnFixup smpboot[] = { - { 0xe59f2028 }, /* ldr r2, gic_cpu_if */ - { 0xe59f0028 }, /* ldr r0, bootreg_addr */ - { 0xe3a01001 }, /* mov r1, #1 */ - { 0xe5821000 }, /* str r1, [r2] - set GICC_CTLR.Enable */ - { 0xe3a010ff }, /* mov r1, #0xff */ - { 0xe5821004 }, /* str r1, [r2, 4] - set GIC_PMR.Priority to 0xff */ - { 0, FIXUP_DSB }, /* dsb */ - { 0xe320f003 }, /* wfi */ - { 0xe5901000 }, /* ldr r1, [r0] */ - { 0xe1110001 }, /* tst r1, r1 */ - { 0x0afffffb }, /* beq */ - { 0xe12fff11 }, /* bx r1 */ - { 0, FIXUP_GIC_CPU_IF }, /* gic_cpu_if: .word 0x.... */ - { 0, FIXUP_BOOTREG }, /* bootreg_addr: .word 0x.... */ - { 0, FIXUP_TERMINATOR } -}; - -static void write_bootloader(const char *name, hwaddr addr, - const ARMInsnFixup *insns, uint32_t *fixupcontext) -{ - /* Fix up the specified bootloader fragment and write it into - * guest memory using rom_add_blob_fixed(). fixupcontext is - * an array giving the values to write in for the fixup types - * which write a value into the code array. - */ - int i, len; - uint32_t *code; - - len = 0; - while (insns[len].fixup != FIXUP_TERMINATOR) { - len++; - } - - code = g_new0(uint32_t, len); - - for (i = 0; i < len; i++) { - uint32_t insn = insns[i].insn; - FixupType fixup = insns[i].fixup; - - switch (fixup) { - case FIXUP_NONE: - break; - case FIXUP_BOARDID: - case FIXUP_BOARD_SETUP: - case FIXUP_ARGPTR: - case FIXUP_ENTRYPOINT: - case FIXUP_GIC_CPU_IF: - case FIXUP_BOOTREG: - case FIXUP_DSB: - insn = fixupcontext[fixup]; - break; - default: - abort(); - } - code[i] = tswap32(insn); - } - - rom_add_blob_fixed(name, code, len * sizeof(uint32_t), addr); - - g_free(code); -} - -static void default_write_secondary(ARMCPU *cpu, - const struct arm_boot_info *info) -{ - uint32_t fixupcontext[FIXUP_MAX]; - - fixupcontext[FIXUP_GIC_CPU_IF] = info->gic_cpu_if_addr; - fixupcontext[FIXUP_BOOTREG] = info->smp_bootreg_addr; - if (arm_feature(&cpu->env, ARM_FEATURE_V7)) { - fixupcontext[FIXUP_DSB] = DSB_INSN; - } else { - fixupcontext[FIXUP_DSB] = CP15_DSB_INSN; - } - - write_bootloader("smpboot", info->smp_loader_start, - 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) -{ - CPUState *cs = CPU(cpu); - - address_space_stl_notdirty(&address_space_memory, info->smp_bootreg_addr, - 0, MEMTXATTRS_UNSPECIFIED, NULL); - cpu_set_pc(cs, info->smp_loader_start); -} - -static inline bool have_dtb(const struct arm_boot_info *info) -{ - return info->dtb_filename || info->get_dtb; -} - -#define WRITE_WORD(p, value) do { \ - address_space_stl_notdirty(&address_space_memory, p, value, \ - MEMTXATTRS_UNSPECIFIED, NULL); \ - p += 4; \ -} while (0) - -static void set_kernel_args(const struct arm_boot_info *info) -{ - int initrd_size = info->initrd_size; - hwaddr base = info->loader_start; - hwaddr p; - - p = base + KERNEL_ARGS_ADDR; - /* ATAG_CORE */ - WRITE_WORD(p, 5); - WRITE_WORD(p, 0x54410001); - WRITE_WORD(p, 1); - WRITE_WORD(p, 0x1000); - WRITE_WORD(p, 0); - /* ATAG_MEM */ - /* TODO: handle multiple chips on one ATAG list */ - WRITE_WORD(p, 4); - WRITE_WORD(p, 0x54410002); - WRITE_WORD(p, info->ram_size); - WRITE_WORD(p, info->loader_start); - if (initrd_size) { - /* ATAG_INITRD2 */ - WRITE_WORD(p, 4); - WRITE_WORD(p, 0x54420005); - WRITE_WORD(p, info->initrd_start); - WRITE_WORD(p, initrd_size); - } - if (info->kernel_cmdline && *info->kernel_cmdline) { - /* ATAG_CMDLINE */ - int cmdline_size; - - cmdline_size = strlen(info->kernel_cmdline); - cpu_physical_memory_write(p + 8, info->kernel_cmdline, - cmdline_size + 1); - cmdline_size = (cmdline_size >> 2) + 1; - WRITE_WORD(p, cmdline_size + 2); - WRITE_WORD(p, 0x54410009); - p += cmdline_size * 4; - } - if (info->atag_board) { - /* ATAG_BOARD */ - int atag_board_len; - uint8_t atag_board_buf[0x1000]; - - atag_board_len = (info->atag_board(info, atag_board_buf) + 3) & ~3; - WRITE_WORD(p, (atag_board_len + 8) >> 2); - WRITE_WORD(p, 0x414f4d50); - cpu_physical_memory_write(p, atag_board_buf, atag_board_len); - p += atag_board_len; - } - /* ATAG_END */ - WRITE_WORD(p, 0); - WRITE_WORD(p, 0); -} - -static void set_kernel_args_old(const struct arm_boot_info *info) -{ - hwaddr p; - const char *s; - int initrd_size = info->initrd_size; - hwaddr base = info->loader_start; - - /* see linux/include/asm-arm/setup.h */ - p = base + KERNEL_ARGS_ADDR; - /* page_size */ - WRITE_WORD(p, 4096); - /* nr_pages */ - WRITE_WORD(p, info->ram_size / 4096); - /* ramdisk_size */ - WRITE_WORD(p, 0); -#define FLAG_READONLY 1 -#define FLAG_RDLOAD 4 -#define FLAG_RDPROMPT 8 - /* flags */ - WRITE_WORD(p, FLAG_READONLY | FLAG_RDLOAD | FLAG_RDPROMPT); - /* rootdev */ - WRITE_WORD(p, (31 << 8) | 0); /* /dev/mtdblock0 */ - /* video_num_cols */ - WRITE_WORD(p, 0); - /* video_num_rows */ - WRITE_WORD(p, 0); - /* video_x */ - WRITE_WORD(p, 0); - /* video_y */ - WRITE_WORD(p, 0); - /* memc_control_reg */ - WRITE_WORD(p, 0); - /* unsigned char sounddefault */ - /* unsigned char adfsdrives */ - /* unsigned char bytes_per_char_h */ - /* unsigned char bytes_per_char_v */ - WRITE_WORD(p, 0); - /* pages_in_bank[4] */ - WRITE_WORD(p, 0); - WRITE_WORD(p, 0); - WRITE_WORD(p, 0); - WRITE_WORD(p, 0); - /* pages_in_vram */ - WRITE_WORD(p, 0); - /* initrd_start */ - if (initrd_size) { - WRITE_WORD(p, info->initrd_start); - } else { - WRITE_WORD(p, 0); - } - /* initrd_size */ - WRITE_WORD(p, initrd_size); - /* rd_start */ - WRITE_WORD(p, 0); - /* system_rev */ - WRITE_WORD(p, 0); - /* system_serial_low */ - WRITE_WORD(p, 0); - /* system_serial_high */ - WRITE_WORD(p, 0); - /* mem_fclk_21285 */ - WRITE_WORD(p, 0); - /* zero unused fields */ - while (p < base + KERNEL_ARGS_ADDR + 256 + 1024) { - WRITE_WORD(p, 0); - } - s = info->kernel_cmdline; - if (s) { - cpu_physical_memory_write(p, s, strlen(s) + 1); - } else { - WRITE_WORD(p, 0); - } -} - -/** - * load_dtb() - load a device tree binary image into memory - * @addr: the address to load the image at - * @binfo: struct describing the boot environment - * @addr_limit: upper limit of the available memory area at @addr - * - * Load a device tree supplied by the machine or by the user with the - * '-dtb' command line option, and put it at offset @addr in target - * memory. - * - * If @addr_limit contains a meaningful value (i.e., it is strictly greater - * than @addr), the device tree is only loaded if its size does not exceed - * the limit. - * - * Returns: the size of the device tree image on success, - * 0 if the image size exceeds the limit, - * -1 on errors. - * - * Note: Must not be called unless have_dtb(binfo) is true. - */ -static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo, - hwaddr addr_limit) -{ - void *fdt = NULL; - int size, rc; - uint32_t acells, scells; - - if (binfo->dtb_filename) { - char *filename; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, binfo->dtb_filename); - if (!filename) { - fprintf(stderr, "Couldn't open dtb file %s\n", binfo->dtb_filename); - goto fail; - } - - fdt = load_device_tree(filename, &size); - if (!fdt) { - fprintf(stderr, "Couldn't open dtb file %s\n", filename); - g_free(filename); - goto fail; - } - g_free(filename); - } else { - fdt = binfo->get_dtb(binfo, &size); - if (!fdt) { - fprintf(stderr, "Board was unable to create a dtb blob\n"); - goto fail; - } - } - - if (addr_limit > addr && size > (addr_limit - addr)) { - /* Installing the device tree blob at addr would exceed addr_limit. - * Whether this constitutes failure is up to the caller to decide, - * so just return 0 as size, i.e., no error. - */ - g_free(fdt); - return 0; - } - - 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; - } - - if (scells < 2 && binfo->ram_size >= (1ULL << 32)) { - /* This is user error so deserves a friendlier error message - * than the failure of setprop_sized_cells would provide - */ - fprintf(stderr, "qemu: dtb file not compatible with " - "RAM size > 4GB\n"); - goto fail; - } - - rc = qemu_fdt_setprop_sized_cells(fdt, "/memory", "reg", - acells, binfo->loader_start, - scells, binfo->ram_size); - if (rc < 0) { - fprintf(stderr, "couldn't set /memory/reg\n"); - goto fail; - } - - if (binfo->kernel_cmdline && *binfo->kernel_cmdline) { - rc = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", - binfo->kernel_cmdline); - if (rc < 0) { - fprintf(stderr, "couldn't set /chosen/bootargs\n"); - goto fail; - } - } - - if (binfo->initrd_size) { - rc = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", - binfo->initrd_start); - if (rc < 0) { - fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); - goto fail; - } - - rc = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", - binfo->initrd_start + binfo->initrd_size); - if (rc < 0) { - fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); - goto fail; - } - } - - if (binfo->modify_dtb) { - binfo->modify_dtb(binfo, fdt); - } - - qemu_fdt_dumpdtb(fdt, size); - - /* Put the DTB into the memory map as a ROM image: this will ensure - * the DTB is copied again upon reset, even if addr points into RAM. - */ - rom_add_blob_fixed("dtb", fdt, size, addr); - - g_free(fdt); - - return size; - -fail: - g_free(fdt); - return -1; -} - -static void do_cpu_reset(void *opaque) -{ - ARMCPU *cpu = opaque; - CPUState *cs = CPU(cpu); - CPUARMState *env = &cpu->env; - const struct arm_boot_info *info = env->boot_info; - - 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; - } - cpu_set_pc(cs, entry); - } else { - /* If we are booting Linux then we need to check whether we are - * booting into secure or non-secure state and adjust the state - * accordingly. Out of reset, ARM is defined to be in secure state - * (SCR.NS = 0), we change that here if non-secure boot has been - * requested. - */ - if (arm_feature(env, ARM_FEATURE_EL3)) { - /* AArch64 is defined to come out of reset into EL3 if enabled. - * If we are booting Linux then we need to adjust our EL as - * Linux expects us to be in EL2 or EL1. AArch32 resets into - * SVC, which Linux expects, so no privilege/exception level to - * 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; - } - } - - /* Set to non-secure if not a secure boot */ - if (!info->secure_boot && - (cs != first_cpu || !info->secure_board_setup)) { - /* Linux expects non-secure state */ - env->cp15.scr_el3 |= SCR_NS; - } - } - - if (cs == first_cpu) { - cpu_set_pc(cs, info->loader_start); - - if (!have_dtb(info)) { - if (old_param) { - set_kernel_args_old(info); - } else { - set_kernel_args(info); - } - } - } else { - info->secondary_cpu_reset_hook(cpu, info); - } - } - } -} - -/** - * load_image_to_fw_cfg() - Load an image file into an fw_cfg entry identified - * by key. - * @fw_cfg: The firmware config instance to store the data in. - * @size_key: The firmware config key to store the size of the loaded - * data under, with fw_cfg_add_i32(). - * @data_key: The firmware config key to store the loaded data under, - * with fw_cfg_add_bytes(). - * @image_name: The name of the image file to load. If it is NULL, the - * function returns without doing anything. - * @try_decompress: Whether the image should be decompressed (gunzipped) before - * adding it to fw_cfg. If decompression fails, the image is - * loaded as-is. - * - * In case of failure, the function prints an error message to stderr and the - * process exits with status 1. - */ -static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key, - uint16_t data_key, const char *image_name, - bool try_decompress) -{ - size_t size = -1; - uint8_t *data; - - if (image_name == NULL) { - return; - } - - if (try_decompress) { - size = load_image_gzipped_buffer(image_name, - LOAD_IMAGE_MAX_GUNZIP_BYTES, &data); - } - - if (size == (size_t)-1) { - gchar *contents; - gsize length; - - if (!g_file_get_contents(image_name, &contents, &length, NULL)) { - fprintf(stderr, "failed to load \"%s\"\n", image_name); - exit(1); - } - size = length; - data = (uint8_t *)contents; - } - - fw_cfg_add_i32(fw_cfg, size_key, size); - 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; - int kernel_size; - int initrd_size; - int is_linux = 0; - uint64_t elf_entry, elf_low_addr, elf_high_addr; - int elf_machine; - hwaddr entry, kernel_load_offset; - static const ARMInsnFixup *primary_loader; - ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier, - notifier, notifier); - ARMCPU *cpu = n->cpu; - 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) { - - if (have_dtb(info)) { - /* If we have a device tree blob, but no kernel to supply it to (or - * the kernel is supposed to be loaded by the bootloader), copy the - * DTB to the base of RAM for the bootloader to pick up. - */ - if (load_dtb(info->loader_start, info, 0) < 0) { - exit(1); - } - } - - if (info->kernel_filename) { - FWCfgState *fw_cfg; - bool try_decompressing_kernel; - - fw_cfg = fw_cfg_find(); - try_decompressing_kernel = arm_feature(&cpu->env, - ARM_FEATURE_AARCH64); - - /* Expose the kernel, the command line, and the initrd in fw_cfg. - * We don't process them here at all, it's all left to the - * firmware. - */ - load_image_to_fw_cfg(fw_cfg, - FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA, - info->kernel_filename, - try_decompressing_kernel); - load_image_to_fw_cfg(fw_cfg, - FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA, - info->initrd_filename, false); - - if (info->kernel_cmdline) { - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, - strlen(info->kernel_cmdline) + 1); - fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, - info->kernel_cmdline); - } - } - - /* We will start from address 0 (typically a boot ROM image) in the - * same way as hardware. - */ - return; - } - - if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { - primary_loader = bootloader_aarch64; - kernel_load_offset = KERNEL64_LOAD_ADDR; - 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; - } - - info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb"); - - if (!info->secondary_cpu_reset_hook) { - info->secondary_cpu_reset_hook = default_reset_secondary; - } - if (!info->write_secondary_boot) { - info->write_secondary_boot = default_write_secondary; - } - - if (info->nb_cpus == 0) - info->nb_cpus = 1; - - /* 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 - * enough room for a decent sized initrd, and on boards with large - * amounts of RAM we must avoid the initrd being so far up in RAM - * that it is outside lowmem and inaccessible to the kernel. - * So for boards with less than 256MB of RAM we put the initrd - * halfway into RAM, and for boards with 256MB of RAM or more we put - * the initrd at 128MB. - */ - info->initrd_start = info->loader_start + - MIN(info->ram_size / 2, 128 * 1024 * 1024); - - /* Assume that raw images are linux kernels, and ELF images are not. */ - 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. - */ - if (elf_low_addr > info->loader_start - || elf_high_addr < info->loader_start) { - /* Pass elf_low_addr as address limit to load_dtb if it may be - * pointing into RAM, otherwise pass '0' (no limit) - */ - if (elf_low_addr < info->loader_start) { - elf_low_addr = 0; - } - if (load_dtb(info->loader_start, info, elf_low_addr) < 0) { - exit(1); - } - } - } - entry = elf_entry; - if (kernel_size < 0) { - kernel_size = load_uimage(info->kernel_filename, &entry, NULL, - &is_linux, NULL, NULL); - } - /* On aarch64, it's the bootloader's job to uncompress the kernel. */ - if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) { - entry = info->loader_start + kernel_load_offset; - kernel_size = load_image_gzipped(info->kernel_filename, entry, - info->ram_size - kernel_load_offset); - is_linux = 1; - } - if (kernel_size < 0) { - entry = info->loader_start + kernel_load_offset; - kernel_size = load_image_targphys(info->kernel_filename, entry, - info->ram_size - kernel_load_offset); - is_linux = 1; - } - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - info->kernel_filename); - exit(1); - } - info->entry = entry; - if (is_linux) { - uint32_t fixupcontext[FIXUP_MAX]; - - if (info->initrd_filename) { - initrd_size = load_ramdisk(info->initrd_filename, - info->initrd_start, - info->ram_size - - info->initrd_start); - if (initrd_size < 0) { - initrd_size = load_image_targphys(info->initrd_filename, - info->initrd_start, - info->ram_size - - info->initrd_start); - } - if (initrd_size < 0) { - fprintf(stderr, "qemu: could not load initrd '%s'\n", - info->initrd_filename); - exit(1); - } - } else { - initrd_size = 0; - } - 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. - */ - if (have_dtb(info)) { - hwaddr align; - hwaddr dtb_start; - - if (elf_machine == EM_AARCH64) { - /* - * Some AArch64 kernels on early bootup map the fdt region as - * - * [ ALIGN_DOWN(fdt, 2MB) ... ALIGN_DOWN(fdt, 2MB) + 2MB ] - * - * Let's play safe and prealign it to 2MB to give us some space. - */ - align = 2 * 1024 * 1024; - } else { - /* - * Some 32bit kernels will trash anything in the 4K page the - * initrd ends in, so make sure the DTB isn't caught up in that. - */ - align = 4096; - } - - /* Place the DTB after the initrd in memory with alignment. */ - dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size, align); - if (load_dtb(dtb_start, info, 0) < 0) { - exit(1); - } - fixupcontext[FIXUP_ARGPTR] = dtb_start; - } else { - fixupcontext[FIXUP_ARGPTR] = info->loader_start + KERNEL_ARGS_ADDR; - if (info->ram_size >= (1ULL << 32)) { - fprintf(stderr, "qemu: RAM size must be less than 4GB to boot" - " Linux kernel using ATAGS (try passing a device tree" - " using -dtb)\n"); - exit(1); - } - } - fixupcontext[FIXUP_ENTRYPOINT] = entry; - - write_bootloader("bootloader", info->loader_start, - primary_loader, fixupcontext); - - 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; - - for (cs = CPU(cpu); cs; cs = CPU_NEXT(cs)) { - ARM_CPU(cs)->env.boot_info = info; - } -} - -void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) -{ - CPUState *cs; - - info->load_kernel_notifier.cpu = cpu; - info->load_kernel_notifier.notifier.notify = arm_load_kernel_notify; - qemu_add_machine_init_done_notifier(&info->load_kernel_notifier.notifier); - - /* CPU objects (unlike devices) are not automatically reset on system - * reset, so we must always register a handler to do so. If we're - * actually loading a kernel, the handler is also responsible for - * arranging that we start it correctly. - */ - for (cs = CPU(cpu); cs; cs = CPU_NEXT(cs)) { - 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 deleted file mode 100644 index 8bb308a42..000000000 --- a/qemu/hw/arm/collie.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * SA-1110-based Sharp Zaurus SL-5500 platform. - * - * Copyright (C) 2011 Dmitry Eremin-Solenikov - * - * This code is licensed under GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "hw/boards.h" -#include "hw/devices.h" -#include "strongarm.h" -#include "hw/arm/arm.h" -#include "hw/block/flash.h" -#include "sysemu/block-backend.h" -#include "exec/address-spaces.h" - -static struct arm_boot_info collie_binfo = { - .loader_start = SA_SDCS0, - .ram_size = 0x20000000, -}; - -static void collie_init(MachineState *machine) -{ - 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; - StrongARMState *s; - DriveInfo *dinfo; - MemoryRegion *sysmem = get_system_memory(); - - if (!cpu_model) { - cpu_model = "sa1110"; - } - - s = sa1110_init(sysmem, collie_binfo.ram_size, cpu_model); - - dinfo = drive_get(IF_PFLASH, 0, 0); - pflash_cfi01_register(SA_CS0, NULL, "collie.fl1", 0x02000000, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - (64 * 1024), 512, 4, 0x00, 0x00, 0x00, 0x00, 0); - - dinfo = drive_get(IF_PFLASH, 0, 1); - pflash_cfi01_register(SA_CS1, NULL, "collie.fl2", 0x02000000, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - (64 * 1024), 512, 4, 0x00, 0x00, 0x00, 0x00, 0); - - sysbus_create_simple("scoop", 0x40800000, NULL); - - collie_binfo.kernel_filename = kernel_filename; - collie_binfo.kernel_cmdline = kernel_cmdline; - collie_binfo.initrd_filename = initrd_filename; - collie_binfo.board_id = 0x208; - arm_load_kernel(s->cpu, &collie_binfo); -} - -static void collie_machine_init(MachineClass *mc) -{ - mc->desc = "Sharp SL-5500 (Collie) PDA (SA-1110)"; - mc->init = collie_init; -} - -DEFINE_MACHINE("collie", collie_machine_init) diff --git a/qemu/hw/arm/cubieboard.c b/qemu/hw/arm/cubieboard.c deleted file mode 100644 index fbd78ed01..000000000 --- a/qemu/hw/arm/cubieboard.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * cubieboard emulation - * - * Copyright (C) 2013 Li Guang - * Written by Li Guang - * - * 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. - */ - -#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" -#include "hw/arm/allwinner-a10.h" - -static struct arm_boot_info cubieboard_binfo = { - .loader_start = AW_A10_SDRAM_BASE, - .board_id = 0x1008, -}; - -typedef struct CubieBoardState { - AwA10State *a10; - MemoryRegion sdram; -} CubieBoardState; - -static void cubieboard_init(MachineState *machine) -{ - CubieBoardState *s = g_new(CubieBoardState, 1); - Error *err = NULL; - - s->a10 = AW_A10(object_new(TYPE_AW_A10)); - - object_property_set_int(OBJECT(&s->a10->emac), 1, "phy-addr", &err); - if (err != NULL) { - 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_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_reportf_err(err, "Couldn't set clk1 frequency: "); - exit(1); - } - - object_property_set_bool(OBJECT(s->a10), true, "realized", &err); - if (err != NULL) { - error_reportf_err(err, "Couldn't realize Allwinner A10: "); - exit(1); - } - - memory_region_allocate_system_memory(&s->sdram, NULL, "cubieboard.ram", - machine->ram_size); - memory_region_add_subregion(get_system_memory(), AW_A10_SDRAM_BASE, - &s->sdram); - - cubieboard_binfo.ram_size = machine->ram_size; - cubieboard_binfo.kernel_filename = machine->kernel_filename; - cubieboard_binfo.kernel_cmdline = machine->kernel_cmdline; - arm_load_kernel(&s->a10->cpu, &cubieboard_binfo); -} - -static void cubieboard_machine_init(MachineClass *mc) -{ - mc->desc = "cubietech cubieboard"; - mc->init = cubieboard_init; -} - -DEFINE_MACHINE("cubieboard", cubieboard_machine_init) diff --git a/qemu/hw/arm/digic.c b/qemu/hw/arm/digic.c deleted file mode 100644 index e0f973032..000000000 --- a/qemu/hw/arm/digic.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * QEMU model of the Canon DIGIC SoC. - * - * Copyright (C) 2013 Antony Pavlov - * - * This model is based on reverse engineering efforts - * made by CHDK (http://chdk.wikia.com) and - * Magic Lantern (http://www.magiclantern.fm) projects - * contributors. - * - * 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. - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/arm/digic.h" - -#define DIGIC4_TIMER_BASE(n) (0xc0210000 + (n) * 0x100) - -#define DIGIC_UART_BASE 0xc0800000 - -static void digic_init(Object *obj) -{ - DigicState *s = DIGIC(obj); - DeviceState *dev; - int i; - - object_initialize(&s->cpu, sizeof(s->cpu), "arm946-" TYPE_ARM_CPU); - object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL); - - for (i = 0; i < DIGIC4_NB_TIMERS; i++) { -#define DIGIC_TIMER_NAME_MLEN 11 - char name[DIGIC_TIMER_NAME_MLEN]; - - object_initialize(&s->timer[i], sizeof(s->timer[i]), TYPE_DIGIC_TIMER); - dev = DEVICE(&s->timer[i]); - qdev_set_parent_bus(dev, sysbus_get_default()); - snprintf(name, DIGIC_TIMER_NAME_MLEN, "timer[%d]", i); - object_property_add_child(obj, name, OBJECT(&s->timer[i]), NULL); - } - - object_initialize(&s->uart, sizeof(s->uart), TYPE_DIGIC_UART); - dev = DEVICE(&s->uart); - qdev_set_parent_bus(dev, sysbus_get_default()); - object_property_add_child(obj, "uart", OBJECT(&s->uart), NULL); -} - -static void digic_realize(DeviceState *dev, Error **errp) -{ - DigicState *s = DIGIC(dev); - Error *err = NULL; - SysBusDevice *sbd; - int i; - - object_property_set_bool(OBJECT(&s->cpu), true, "reset-hivecs", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - - object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - - for (i = 0; i < DIGIC4_NB_TIMERS; i++) { - object_property_set_bool(OBJECT(&s->timer[i]), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - - sbd = SYS_BUS_DEVICE(&s->timer[i]); - sysbus_mmio_map(sbd, 0, DIGIC4_TIMER_BASE(i)); - } - - object_property_set_bool(OBJECT(&s->uart), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - - sbd = SYS_BUS_DEVICE(&s->uart); - sysbus_mmio_map(sbd, 0, DIGIC_UART_BASE); -} - -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 = { - .name = TYPE_DIGIC, - .parent = TYPE_DEVICE, - .instance_size = sizeof(DigicState), - .instance_init = digic_init, - .class_init = digic_class_init, -}; - -static void digic_register_types(void) -{ - type_register_static(&digic_type_info); -} - -type_init(digic_register_types) diff --git a/qemu/hw/arm/digic_boards.c b/qemu/hw/arm/digic_boards.c deleted file mode 100644 index 520c8e9ff..000000000 --- a/qemu/hw/arm/digic_boards.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * QEMU model of the Canon DIGIC boards (cameras indeed :). - * - * Copyright (C) 2013 Antony Pavlov - * - * This model is based on reverse engineering efforts - * made by CHDK (http://chdk.wikia.com) and - * Magic Lantern (http://www.magiclantern.fm) projects - * contributors. - * - * See docs here: - * http://magiclantern.wikia.com/wiki/Register_Map - * - * 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. - * - */ - -#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" -#include "hw/arm/digic.h" -#include "hw/block/flash.h" -#include "hw/loader.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" - -#define DIGIC4_ROM0_BASE 0xf0000000 -#define DIGIC4_ROM1_BASE 0xf8000000 -#define DIGIC4_ROM_MAX_SIZE 0x08000000 - -typedef struct DigicBoardState { - DigicState *digic; - MemoryRegion ram; -} DigicBoardState; - -typedef struct DigicBoard { - hwaddr ram_size; - void (*add_rom0)(DigicBoardState *, hwaddr, const char *); - const char *rom0_def_filename; - void (*add_rom1)(DigicBoardState *, hwaddr, const char *); - const char *rom1_def_filename; -} DigicBoard; - -static void digic4_board_setup_ram(DigicBoardState *s, hwaddr ram_size) -{ - memory_region_allocate_system_memory(&s->ram, NULL, "ram", ram_size); - memory_region_add_subregion(get_system_memory(), 0, &s->ram); -} - -static void digic4_board_init(DigicBoard *board) -{ - Error *err = NULL; - - DigicBoardState *s = g_new(DigicBoardState, 1); - - s->digic = DIGIC(object_new(TYPE_DIGIC)); - object_property_set_bool(OBJECT(s->digic), true, "realized", &err); - if (err != NULL) { - error_reportf_err(err, "Couldn't realize DIGIC SoC: "); - exit(1); - } - - digic4_board_setup_ram(s, board->ram_size); - - if (board->add_rom0) { - board->add_rom0(s, DIGIC4_ROM0_BASE, board->rom0_def_filename); - } - - if (board->add_rom1) { - board->add_rom1(s, DIGIC4_ROM1_BASE, board->rom1_def_filename); - } -} - -static void digic_load_rom(DigicBoardState *s, hwaddr addr, - hwaddr max_size, const char *def_filename) -{ - target_long rom_size; - const char *filename; - - if (qtest_enabled()) { - /* qtest runs no code so don't attempt a ROM load which - * could fail and result in a spurious test failure. - */ - return; - } - - if (bios_name) { - filename = bios_name; - } else { - filename = def_filename; - } - - if (filename) { - char *fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, filename); - - if (!fn) { - error_report("Couldn't find rom image '%s'.", filename); - exit(1); - } - - rom_size = load_image_targphys(fn, addr, max_size); - if (rom_size < 0 || rom_size > max_size) { - error_report("Couldn't load rom image '%s'.", filename); - exit(1); - } - g_free(fn); - } -} - -/* - * Samsung K8P3215UQB - * 64M Bit (4Mx16) Page Mode / Multi-Bank NOR Flash Memory - */ -static void digic4_add_k8p3215uqb_rom(DigicBoardState *s, hwaddr addr, - const char *def_filename) -{ -#define FLASH_K8P3215UQB_SIZE (4 * 1024 * 1024) -#define FLASH_K8P3215UQB_SECTOR_SIZE (64 * 1024) - - pflash_cfi02_register(addr, NULL, "pflash", FLASH_K8P3215UQB_SIZE, - NULL, FLASH_K8P3215UQB_SECTOR_SIZE, - FLASH_K8P3215UQB_SIZE / FLASH_K8P3215UQB_SECTOR_SIZE, - DIGIC4_ROM_MAX_SIZE / FLASH_K8P3215UQB_SIZE, - 4, - 0x00EC, 0x007E, 0x0003, 0x0001, - 0x0555, 0x2aa, 0); - - digic_load_rom(s, addr, FLASH_K8P3215UQB_SIZE, def_filename); -} - -static DigicBoard digic4_board_canon_a1100 = { - .ram_size = 64 * 1024 * 1024, - .add_rom1 = digic4_add_k8p3215uqb_rom, - .rom1_def_filename = "canon-a1100-rom1.bin", -}; - -static void canon_a1100_init(MachineState *machine) -{ - digic4_board_init(&digic4_board_canon_a1100); -} - -static void canon_a1100_machine_init(MachineClass *mc) -{ - mc->desc = "Canon PowerShot A1100 IS"; - mc->init = &canon_a1100_init; -} - -DEFINE_MACHINE("canon-a1100", canon_a1100_machine_init) diff --git a/qemu/hw/arm/exynos4210.c b/qemu/hw/arm/exynos4210.c deleted file mode 100644 index be3c96d21..000000000 --- a/qemu/hw/arm/exynos4210.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Samsung exynos4210 SoC emulation - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved. - * Maksim Kozlov - * Evgeny Voevodin - * Igor Mitsyanko - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/boards.h" -#include "sysemu/sysemu.h" -#include "hw/sysbus.h" -#include "hw/arm/arm.h" -#include "hw/loader.h" -#include "hw/arm/exynos4210.h" -#include "hw/usb/hcd-ehci.h" - -#define EXYNOS4210_CHIPID_ADDR 0x10000000 - -/* PWM */ -#define EXYNOS4210_PWM_BASE_ADDR 0x139D0000 - -/* RTC */ -#define EXYNOS4210_RTC_BASE_ADDR 0x10070000 - -/* MCT */ -#define EXYNOS4210_MCT_BASE_ADDR 0x10050000 - -/* I2C */ -#define EXYNOS4210_I2C_SHIFT 0x00010000 -#define EXYNOS4210_I2C_BASE_ADDR 0x13860000 -/* Interrupt Group of External Interrupt Combiner for I2C */ -#define EXYNOS4210_I2C_INTG 27 -#define EXYNOS4210_HDMI_INTG 16 - -/* UART's definitions */ -#define EXYNOS4210_UART0_BASE_ADDR 0x13800000 -#define EXYNOS4210_UART1_BASE_ADDR 0x13810000 -#define EXYNOS4210_UART2_BASE_ADDR 0x13820000 -#define EXYNOS4210_UART3_BASE_ADDR 0x13830000 -#define EXYNOS4210_UART0_FIFO_SIZE 256 -#define EXYNOS4210_UART1_FIFO_SIZE 64 -#define EXYNOS4210_UART2_FIFO_SIZE 16 -#define EXYNOS4210_UART3_FIFO_SIZE 16 -/* Interrupt Group of External Interrupt Combiner for UART */ -#define EXYNOS4210_UART_INT_GRP 26 - -/* External GIC */ -#define EXYNOS4210_EXT_GIC_CPU_BASE_ADDR 0x10480000 -#define EXYNOS4210_EXT_GIC_DIST_BASE_ADDR 0x10490000 - -/* Combiner */ -#define EXYNOS4210_EXT_COMBINER_BASE_ADDR 0x10440000 -#define EXYNOS4210_INT_COMBINER_BASE_ADDR 0x10448000 - -/* PMU SFR base address */ -#define EXYNOS4210_PMU_BASE_ADDR 0x10020000 - -/* Display controllers (FIMD) */ -#define EXYNOS4210_FIMD0_BASE_ADDR 0x11C00000 - -/* EHCI */ -#define EXYNOS4210_EHCI_BASE_ADDR 0x12580000 - -static uint8_t chipid_and_omr[] = { 0x11, 0x02, 0x21, 0x43, - 0x09, 0x00, 0x00, 0x00 }; - -static uint64_t exynos4210_chipid_and_omr_read(void *opaque, hwaddr offset, - unsigned size) -{ - assert(offset < sizeof(chipid_and_omr)); - return chipid_and_omr[offset]; -} - -static void exynos4210_chipid_and_omr_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - return; -} - -static const MemoryRegionOps exynos4210_chipid_and_omr_ops = { - .read = exynos4210_chipid_and_omr_read, - .write = exynos4210_chipid_and_omr_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .max_access_size = 1, - } -}; - -void exynos4210_write_secondary(ARMCPU *cpu, - const struct arm_boot_info *info) -{ - int n; - uint32_t smpboot[] = { - 0xe59f3034, /* ldr r3, External gic_cpu_if */ - 0xe59f2034, /* ldr r2, Internal gic_cpu_if */ - 0xe59f0034, /* ldr r0, startaddr */ - 0xe3a01001, /* mov r1, #1 */ - 0xe5821000, /* str r1, [r2] */ - 0xe5831000, /* str r1, [r3] */ - 0xe3a010ff, /* mov r1, #0xff */ - 0xe5821004, /* str r1, [r2, #4] */ - 0xe5831004, /* str r1, [r3, #4] */ - 0xf57ff04f, /* dsb */ - 0xe320f003, /* wfi */ - 0xe5901000, /* ldr r1, [r0] */ - 0xe1110001, /* tst r1, r1 */ - 0x0afffffb, /* beq */ - 0xe12fff11, /* bx r1 */ - EXYNOS4210_EXT_GIC_CPU_BASE_ADDR, - 0, /* gic_cpu_if: base address of Internal GIC CPU interface */ - 0 /* bootreg: Boot register address is held here */ - }; - smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr; - smpboot[ARRAY_SIZE(smpboot) - 2] = info->gic_cpu_if_addr; - for (n = 0; n < ARRAY_SIZE(smpboot); n++) { - smpboot[n] = tswap32(smpboot[n]); - } - rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot), - info->smp_loader_start); -} - -Exynos4210State *exynos4210_init(MemoryRegion *system_mem, - unsigned long ram_size) -{ - int i, n; - Exynos4210State *s = g_new(Exynos4210State, 1); - qemu_irq gate_irq[EXYNOS4210_NCPUS][EXYNOS4210_IRQ_GATE_NINPUTS]; - unsigned long mem_size; - DeviceState *dev; - SysBusDevice *busdev; - ObjectClass *cpu_oc; - - cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, "cortex-a9"); - assert(cpu_oc); - - for (n = 0; n < EXYNOS4210_NCPUS; n++) { - Object *cpuobj = object_new(object_class_get_name(cpu_oc)); - - /* 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", &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", &error_fatal); - } - - /*** IRQs ***/ - - s->irq_table = exynos4210_init_irq(&s->irqs); - - /* IRQ Gate */ - for (i = 0; i < EXYNOS4210_NCPUS; i++) { - dev = qdev_create(NULL, "exynos4210.irq_gate"); - qdev_prop_set_uint32(dev, "n_in", EXYNOS4210_IRQ_GATE_NINPUTS); - qdev_init_nofail(dev); - /* Get IRQ Gate input in gate_irq */ - for (n = 0; n < EXYNOS4210_IRQ_GATE_NINPUTS; n++) { - gate_irq[i][n] = qdev_get_gpio_in(dev, n); - } - busdev = SYS_BUS_DEVICE(dev); - - /* Connect IRQ Gate output to CPU's IRQ line */ - sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(DEVICE(s->cpu[i]), ARM_CPU_IRQ)); - } - - /* Private memory region and Internal GIC */ - dev = qdev_create(NULL, "a9mpcore_priv"); - qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS); - qdev_init_nofail(dev); - busdev = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(busdev, 0, EXYNOS4210_SMP_PRIVATE_BASE_ADDR); - for (n = 0; n < EXYNOS4210_NCPUS; n++) { - sysbus_connect_irq(busdev, n, gate_irq[n][0]); - } - for (n = 0; n < EXYNOS4210_INT_GIC_NIRQ; n++) { - s->irqs.int_gic_irq[n] = qdev_get_gpio_in(dev, n); - } - - /* Cache controller */ - sysbus_create_simple("l2x0", EXYNOS4210_L2X0_BASE_ADDR, NULL); - - /* External GIC */ - dev = qdev_create(NULL, "exynos4210.gic"); - qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS); - qdev_init_nofail(dev); - busdev = SYS_BUS_DEVICE(dev); - /* Map CPU interface */ - sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_GIC_CPU_BASE_ADDR); - /* Map Distributer interface */ - sysbus_mmio_map(busdev, 1, EXYNOS4210_EXT_GIC_DIST_BASE_ADDR); - for (n = 0; n < EXYNOS4210_NCPUS; n++) { - sysbus_connect_irq(busdev, n, gate_irq[n][1]); - } - for (n = 0; n < EXYNOS4210_EXT_GIC_NIRQ; n++) { - s->irqs.ext_gic_irq[n] = qdev_get_gpio_in(dev, n); - } - - /* Internal Interrupt Combiner */ - dev = qdev_create(NULL, "exynos4210.combiner"); - qdev_init_nofail(dev); - busdev = SYS_BUS_DEVICE(dev); - for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) { - sysbus_connect_irq(busdev, n, s->irqs.int_gic_irq[n]); - } - exynos4210_combiner_get_gpioin(&s->irqs, dev, 0); - sysbus_mmio_map(busdev, 0, EXYNOS4210_INT_COMBINER_BASE_ADDR); - - /* External Interrupt Combiner */ - dev = qdev_create(NULL, "exynos4210.combiner"); - qdev_prop_set_uint32(dev, "external", 1); - qdev_init_nofail(dev); - busdev = SYS_BUS_DEVICE(dev); - for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) { - sysbus_connect_irq(busdev, n, s->irqs.ext_gic_irq[n]); - } - exynos4210_combiner_get_gpioin(&s->irqs, dev, 1); - sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_COMBINER_BASE_ADDR); - - /* Initialize board IRQs. */ - exynos4210_init_board_irqs(&s->irqs); - - /*** Memory ***/ - - /* Chip-ID and OMR */ - memory_region_init_io(&s->chipid_mem, NULL, &exynos4210_chipid_and_omr_ops, - NULL, "exynos4210.chipid", sizeof(chipid_and_omr)); - memory_region_add_subregion(system_mem, EXYNOS4210_CHIPID_ADDR, - &s->chipid_mem); - - /* Internal ROM */ - memory_region_init_ram(&s->irom_mem, NULL, "exynos4210.irom", - 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, - &s->irom_mem); - /* mirror of iROM */ - memory_region_init_alias(&s->irom_alias_mem, NULL, "exynos4210.irom_alias", - &s->irom_mem, - 0, - EXYNOS4210_IROM_SIZE); - memory_region_set_readonly(&s->irom_alias_mem, true); - memory_region_add_subregion(system_mem, EXYNOS4210_IROM_MIRROR_BASE_ADDR, - &s->irom_alias_mem); - - /* Internal RAM */ - memory_region_init_ram(&s->iram_mem, NULL, "exynos4210.iram", - 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); - - /* DRAM */ - 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_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_fatal); - vmstate_register_ram_global(&s->dram0_mem); - memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR, - &s->dram0_mem); - - /* PMU. - * The only reason of existence at the moment is that secondary CPU boot - * loader uses PMU INFORM5 register as a holding pen. - */ - sysbus_create_simple("exynos4210.pmu", EXYNOS4210_PMU_BASE_ADDR, NULL); - - /* PWM */ - sysbus_create_varargs("exynos4210.pwm", EXYNOS4210_PWM_BASE_ADDR, - s->irq_table[exynos4210_get_irq(22, 0)], - s->irq_table[exynos4210_get_irq(22, 1)], - s->irq_table[exynos4210_get_irq(22, 2)], - s->irq_table[exynos4210_get_irq(22, 3)], - s->irq_table[exynos4210_get_irq(22, 4)], - NULL); - /* RTC */ - sysbus_create_varargs("exynos4210.rtc", EXYNOS4210_RTC_BASE_ADDR, - s->irq_table[exynos4210_get_irq(23, 0)], - s->irq_table[exynos4210_get_irq(23, 1)], - NULL); - - /* Multi Core Timer */ - dev = qdev_create(NULL, "exynos4210.mct"); - qdev_init_nofail(dev); - busdev = SYS_BUS_DEVICE(dev); - for (n = 0; n < 4; n++) { - /* Connect global timer interrupts to Combiner gpio_in */ - sysbus_connect_irq(busdev, n, - s->irq_table[exynos4210_get_irq(1, 4 + n)]); - } - /* Connect local timer interrupts to Combiner gpio_in */ - sysbus_connect_irq(busdev, 4, - s->irq_table[exynos4210_get_irq(51, 0)]); - sysbus_connect_irq(busdev, 5, - s->irq_table[exynos4210_get_irq(35, 3)]); - sysbus_mmio_map(busdev, 0, EXYNOS4210_MCT_BASE_ADDR); - - /*** I2C ***/ - for (n = 0; n < EXYNOS4210_I2C_NUMBER; n++) { - uint32_t addr = EXYNOS4210_I2C_BASE_ADDR + EXYNOS4210_I2C_SHIFT * n; - qemu_irq i2c_irq; - - if (n < 8) { - i2c_irq = s->irq_table[exynos4210_get_irq(EXYNOS4210_I2C_INTG, n)]; - } else { - i2c_irq = s->irq_table[exynos4210_get_irq(EXYNOS4210_HDMI_INTG, 1)]; - } - - dev = qdev_create(NULL, "exynos4210.i2c"); - qdev_init_nofail(dev); - busdev = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(busdev, 0, i2c_irq); - sysbus_mmio_map(busdev, 0, addr); - s->i2c_if[n] = (I2CBus *)qdev_get_child_bus(dev, "i2c"); - } - - - /*** UARTs ***/ - exynos4210_uart_create(EXYNOS4210_UART0_BASE_ADDR, - EXYNOS4210_UART0_FIFO_SIZE, 0, NULL, - s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 0)]); - - exynos4210_uart_create(EXYNOS4210_UART1_BASE_ADDR, - EXYNOS4210_UART1_FIFO_SIZE, 1, NULL, - s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 1)]); - - exynos4210_uart_create(EXYNOS4210_UART2_BASE_ADDR, - EXYNOS4210_UART2_FIFO_SIZE, 2, NULL, - s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 2)]); - - exynos4210_uart_create(EXYNOS4210_UART3_BASE_ADDR, - EXYNOS4210_UART3_FIFO_SIZE, 3, NULL, - s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 3)]); - - /*** Display controller (FIMD) ***/ - sysbus_create_varargs("exynos4210.fimd", EXYNOS4210_FIMD0_BASE_ADDR, - s->irq_table[exynos4210_get_irq(11, 0)], - s->irq_table[exynos4210_get_irq(11, 1)], - s->irq_table[exynos4210_get_irq(11, 2)], - NULL); - - sysbus_create_simple(TYPE_EXYNOS4210_EHCI, EXYNOS4210_EHCI_BASE_ADDR, - s->irq_table[exynos4210_get_irq(28, 3)]); - - return s; -} diff --git a/qemu/hw/arm/exynos4_boards.c b/qemu/hw/arm/exynos4_boards.c deleted file mode 100644 index 0efa19405..000000000 --- a/qemu/hw/arm/exynos4_boards.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Samsung exynos4 SoC based boards emulation - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved. - * Maksim Kozlov - * Evgeny Voevodin - * Igor Mitsyanko - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "cpu.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "hw/sysbus.h" -#include "net/net.h" -#include "hw/arm/arm.h" -#include "exec/address-spaces.h" -#include "hw/arm/exynos4210.h" -#include "hw/boards.h" - -#undef DEBUG - -//#define DEBUG - -#ifdef DEBUG - #undef PRINT_DEBUG - #define PRINT_DEBUG(fmt, args...) \ - do { \ - fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ - } while (0) -#else - #define PRINT_DEBUG(fmt, args...) do {} while (0) -#endif - -#define SMDK_LAN9118_BASE_ADDR 0x05000000 - -typedef enum Exynos4BoardType { - EXYNOS4_BOARD_NURI, - EXYNOS4_BOARD_SMDKC210, - EXYNOS4_NUM_OF_BOARDS -} Exynos4BoardType; - -static int exynos4_board_id[EXYNOS4_NUM_OF_BOARDS] = { - [EXYNOS4_BOARD_NURI] = 0xD33, - [EXYNOS4_BOARD_SMDKC210] = 0xB16, -}; - -static int exynos4_board_smp_bootreg_addr[EXYNOS4_NUM_OF_BOARDS] = { - [EXYNOS4_BOARD_NURI] = EXYNOS4210_SECOND_CPU_BOOTREG, - [EXYNOS4_BOARD_SMDKC210] = EXYNOS4210_SECOND_CPU_BOOTREG, -}; - -static unsigned long exynos4_board_ram_size[EXYNOS4_NUM_OF_BOARDS] = { - [EXYNOS4_BOARD_NURI] = 0x40000000, - [EXYNOS4_BOARD_SMDKC210] = 0x40000000, -}; - -static struct arm_boot_info exynos4_board_binfo = { - .loader_start = EXYNOS4210_BASE_BOOT_ADDR, - .smp_loader_start = EXYNOS4210_SMP_BOOT_ADDR, - .nb_cpus = EXYNOS4210_NCPUS, - .write_secondary_boot = exynos4210_write_secondary, -}; - -static void lan9215_init(uint32_t base, qemu_irq irq) -{ - DeviceState *dev; - SysBusDevice *s; - - /* This should be a 9215 but the 9118 is close enough */ - if (nd_table[0].used) { - qemu_check_nic_model(&nd_table[0], "lan9118"); - dev = qdev_create(NULL, "lan9118"); - qdev_set_nic_properties(dev, &nd_table[0]); - qdev_prop_set_uint32(dev, "mode_16bit", 1); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(s, 0, base); - sysbus_connect_irq(s, 0, 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", - mc->name, EXYNOS4210_NCPUS); - } - - exynos4_board_binfo.ram_size = exynos4_board_ram_size[board_type]; - exynos4_board_binfo.board_id = exynos4_board_id[board_type]; - exynos4_board_binfo.smp_bootreg_addr = - exynos4_board_smp_bootreg_addr[board_type]; - exynos4_board_binfo.kernel_filename = machine->kernel_filename; - exynos4_board_binfo.initrd_filename = machine->initrd_filename; - exynos4_board_binfo.kernel_cmdline = machine->kernel_cmdline; - exynos4_board_binfo.gic_cpu_if_addr = - EXYNOS4210_SMP_PRIVATE_BASE_ADDR + 0x100; - - PRINT_DEBUG("\n ram_size: %luMiB [0x%08lx]\n" - " kernel_filename: %s\n" - " kernel_cmdline: %s\n" - " initrd_filename: %s\n", - exynos4_board_ram_size[board_type] / 1048576, - exynos4_board_ram_size[board_type], - machine->kernel_filename, - machine->kernel_cmdline, - machine->initrd_filename); - - return exynos4210_init(get_system_memory(), - exynos4_board_ram_size[board_type]); -} - -static void nuri_init(MachineState *machine) -{ - exynos4_boards_init_common(machine, EXYNOS4_BOARD_NURI); - - arm_load_kernel(ARM_CPU(first_cpu), &exynos4_board_binfo); -} - -static void smdkc210_init(MachineState *machine) -{ - Exynos4210State *s = exynos4_boards_init_common(machine, - EXYNOS4_BOARD_SMDKC210); - - lan9215_init(SMDK_LAN9118_BASE_ADDR, - qemu_irq_invert(s->irq_table[exynos4210_get_irq(37, 1)])); - arm_load_kernel(ARM_CPU(first_cpu), &exynos4_board_binfo); -} - -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_machines_init(void) -{ - type_register_static(&nuri_type); - type_register_static(&smdkc210_type); -} - -type_init(exynos4_machines_init) diff --git a/qemu/hw/arm/fsl-imx25.c b/qemu/hw/arm/fsl-imx25.c deleted file mode 100644 index 2f878b935..000000000 --- a/qemu/hw/arm/fsl-imx25.c +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (c) 2013 Jean-Christophe Dubois - * - * i.MX25 SOC emulation. - * - * Based on hw/arm/xlnx-zynqmp.c - * - * Copyright (C) 2015 Xilinx Inc - * Written by Peter Crosthwaite - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "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 deleted file mode 100644 index 31a3a8791..000000000 --- a/qemu/hw/arm/fsl-imx31.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright (c) 2013 Jean-Christophe Dubois - * - * 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 . - */ - -#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 deleted file mode 100644 index d59d9ba4e..000000000 --- a/qemu/hw/arm/gumstix.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Gumstix Platforms - * - * Copyright (c) 2007 by Thorsten Zitterell - * - * Code based on spitz platform by Andrzej Zaborowski - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -/* - * Example usage: - * - * connex: - * ======= - * create image: - * # dd of=flash bs=1k count=16k if=/dev/zero - * # dd of=flash bs=1k conv=notrunc if=u-boot.bin - * # dd of=flash bs=1k conv=notrunc seek=256 if=rootfs.arm_nofpu.jffs2 - * start it: - * # qemu-system-arm -M connex -pflash flash -monitor null -nographic - * - * verdex: - * ======= - * create image: - * # dd of=flash bs=1k count=32k if=/dev/zero - * # dd of=flash bs=1k conv=notrunc if=u-boot.bin - * # dd of=flash bs=1k conv=notrunc seek=256 if=rootfs.arm_nofpu.jffs2 - * # dd of=flash bs=1k conv=notrunc seek=31744 if=uImage - * start it: - * # 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" -#include "hw/block/flash.h" -#include "hw/devices.h" -#include "hw/boards.h" -#include "sysemu/block-backend.h" -#include "exec/address-spaces.h" -#include "sysemu/qtest.h" - -static const int sector_len = 128 * 1024; - -static void connex_init(MachineState *machine) -{ - PXA2xxState *cpu; - DriveInfo *dinfo; - int be; - MemoryRegion *address_space_mem = get_system_memory(); - - uint32_t connex_rom = 0x01000000; - uint32_t connex_ram = 0x04000000; - - cpu = pxa255_init(address_space_mem, connex_ram); - - dinfo = drive_get(IF_PFLASH, 0, 0); - if (!dinfo && !qtest_enabled()) { - fprintf(stderr, "A flash image must be given with the " - "'pflash' parameter\n"); - exit(1); - } - -#ifdef TARGET_WORDS_BIGENDIAN - be = 1; -#else - be = 0; -#endif - if (!pflash_cfi01_register(0x00000000, NULL, "connext.rom", connex_rom, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - sector_len, connex_rom / sector_len, - 2, 0, 0, 0, 0, be)) { - fprintf(stderr, "qemu: Error registering flash memory.\n"); - exit(1); - } - - /* Interrupt line of NIC is connected to GPIO line 36 */ - smc91c111_init(&nd_table[0], 0x04000300, - qdev_get_gpio_in(cpu->gpio, 36)); -} - -static void verdex_init(MachineState *machine) -{ - const char *cpu_model = machine->cpu_model; - PXA2xxState *cpu; - DriveInfo *dinfo; - int be; - MemoryRegion *address_space_mem = get_system_memory(); - - uint32_t verdex_rom = 0x02000000; - uint32_t verdex_ram = 0x10000000; - - cpu = pxa270_init(address_space_mem, verdex_ram, cpu_model ?: "pxa270-c0"); - - dinfo = drive_get(IF_PFLASH, 0, 0); - if (!dinfo && !qtest_enabled()) { - fprintf(stderr, "A flash image must be given with the " - "'pflash' parameter\n"); - exit(1); - } - -#ifdef TARGET_WORDS_BIGENDIAN - be = 1; -#else - be = 0; -#endif - if (!pflash_cfi01_register(0x00000000, NULL, "verdex.rom", verdex_rom, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - sector_len, verdex_rom / sector_len, - 2, 0, 0, 0, 0, be)) { - fprintf(stderr, "qemu: Error registering flash memory.\n"); - exit(1); - } - - /* Interrupt line of NIC is connected to GPIO line 99 */ - smc91c111_init(&nd_table[0], 0x04000300, - qdev_get_gpio_in(cpu->gpio, 99)); -} - -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 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) -{ - type_register_static(&connex_type); - type_register_static(&verdex_type); -} - -type_init(gumstix_machine_init) diff --git a/qemu/hw/arm/highbank.c b/qemu/hw/arm/highbank.c deleted file mode 100644 index d9930c0d3..000000000 --- a/qemu/hw/arm/highbank.c +++ /dev/null @@ -1,441 +0,0 @@ -/* - * Calxeda Highbank SoC emulation - * - * Copyright (c) 2010-2012 Calxeda - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2 or later, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "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" -#include "exec/address-spaces.h" -#include "qemu/error-report.h" - -#define SMP_BOOT_ADDR 0x100 -#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; - uint32_t smpboot[] = { - 0xee100fb0, /* mrc p15, 0, r0, c0, c0, 5 - read current core id */ - 0xe210000f, /* ands r0, r0, #0x0f */ - 0xe3a03040, /* mov r3, #0x40 - jump address is 0x40 + 0x10 * core id */ - 0xe0830200, /* add r0, r3, r0, lsl #4 */ - 0xe59f2024, /* ldr r2, privbase */ - 0xe3a01001, /* mov r1, #1 */ - 0xe5821100, /* str r1, [r2, #256] - set GICC_CTLR.Enable */ - 0xe3a010ff, /* mov r1, #0xff */ - 0xe5821104, /* str r1, [r2, #260] - set GICC_PMR.Priority to 0xff */ - 0xf57ff04f, /* dsb */ - 0xe320f003, /* wfi */ - 0xe5901000, /* ldr r1, [r0] */ - 0xe1110001, /* tst r1, r1 */ - 0x0afffffb, /* beq */ - 0xe12fff11, /* bx r1 */ - MPCORE_PERIPHBASE /* privbase: MPCore peripheral base address. */ - }; - for (n = 0; n < ARRAY_SIZE(smpboot); n++) { - smpboot[n] = tswap32(smpboot[n]); - } - rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot), SMP_BOOT_ADDR); -} - -static void hb_reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info) -{ - CPUARMState *env = &cpu->env; - - switch (info->nb_cpus) { - case 4: - address_space_stl_notdirty(&address_space_memory, - SMP_BOOT_REG + 0x30, 0, - MEMTXATTRS_UNSPECIFIED, NULL); - case 3: - address_space_stl_notdirty(&address_space_memory, - SMP_BOOT_REG + 0x20, 0, - MEMTXATTRS_UNSPECIFIED, NULL); - case 2: - address_space_stl_notdirty(&address_space_memory, - SMP_BOOT_REG + 0x10, 0, - MEMTXATTRS_UNSPECIFIED, NULL); - env->regs[15] = SMP_BOOT_ADDR; - break; - default: - break; - } -} - -#define NUM_REGS 0x200 -static void hb_regs_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - uint32_t *regs = opaque; - - if (offset == 0xf00) { - if (value == 1 || value == 2) { - qemu_system_reset_request(); - } else if (value == 3) { - qemu_system_shutdown_request(); - } - } - - regs[offset/4] = value; -} - -static uint64_t hb_regs_read(void *opaque, hwaddr offset, - unsigned size) -{ - uint32_t *regs = opaque; - uint32_t value = regs[offset/4]; - - if ((offset == 0x100) || (offset == 0x108) || (offset == 0x10C)) { - value |= 0x30000000; - } - - return value; -} - -static const MemoryRegionOps hb_mem_ops = { - .read = hb_regs_read, - .write = hb_regs_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -#define TYPE_HIGHBANK_REGISTERS "highbank-regs" -#define HIGHBANK_REGISTERS(obj) \ - OBJECT_CHECK(HighbankRegsState, (obj), TYPE_HIGHBANK_REGISTERS) - -typedef struct { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - uint32_t regs[NUM_REGS]; -} HighbankRegsState; - -static VMStateDescription vmstate_highbank_regs = { - .name = "highbank-regs", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, HighbankRegsState, NUM_REGS), - VMSTATE_END_OF_LIST(), - }, -}; - -static void highbank_regs_reset(DeviceState *dev) -{ - HighbankRegsState *s = HIGHBANK_REGISTERS(dev); - - s->regs[0x40] = 0x05F20121; - s->regs[0x41] = 0x2; - s->regs[0x42] = 0x05F30121; - s->regs[0x43] = 0x05F40121; -} - -static int highbank_regs_init(SysBusDevice *dev) -{ - HighbankRegsState *s = HIGHBANK_REGISTERS(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &hb_mem_ops, s->regs, - "highbank_regs", 0x1000); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static void highbank_regs_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - sbc->init = highbank_regs_init; - dc->desc = "Calxeda Highbank registers"; - dc->vmsd = &vmstate_highbank_regs; - dc->reset = highbank_regs_reset; -} - -static const TypeInfo highbank_regs_info = { - .name = TYPE_HIGHBANK_REGISTERS, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(HighbankRegsState), - .class_init = highbank_regs_class_init, -}; - -static void highbank_regs_register_types(void) -{ - type_register_static(&highbank_regs_info); -} - -type_init(highbank_regs_register_types) - -static struct arm_boot_info highbank_binfo; - -enum cxmachines { - CALXEDA_HIGHBANK, - CALXEDA_MIDWAY, -}; - -/* ram_size must be set to match the upper bound of memory in the - * device tree (linux/arch/arm/boot/dts/highbank.dts), which is - * normally 0xff900000 or -m 4089. When running this board on a - * 32-bit host, set the reg value of memory to 0xf7ff00000 in the - * device tree and pass -m 2047 to QEMU. - */ -static void calxeda_init(MachineState *machine, enum cxmachines machine_id) -{ - 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; - DeviceState *dev = NULL; - SysBusDevice *busdev; - qemu_irq pic[128]; - int n; - qemu_irq cpu_irq[4]; - qemu_irq cpu_fiq[4]; - MemoryRegion *sysram; - MemoryRegion *dram; - MemoryRegion *sysmem; - char *sysboot_filename; - - 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; - - cpuobj = object_new(object_class_get_name(oc)); - cpu = ARM_CPU(cpuobj); - - 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", &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); - } - - sysmem = get_system_memory(); - dram = g_new(MemoryRegion, 1); - memory_region_allocate_system_memory(dram, NULL, "highbank.dram", ram_size); - /* SDRAM at address zero. */ - memory_region_add_subregion(sysmem, 0, dram); - - sysram = g_new(MemoryRegion, 1); - memory_region_init_ram(sysram, NULL, "highbank.sysram", 0x8000, - &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) { - error_report("Unable to load %s", bios_name); - exit(1); - } - g_free(sysboot_filename); - } else { - error_report("Unable to find %s", bios_name); - exit(1); - } - } - - switch (machine_id) { - case CALXEDA_HIGHBANK: - dev = qdev_create(NULL, "l2x0"); - qdev_init_nofail(dev); - busdev = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(busdev, 0, 0xfff12000); - - dev = qdev_create(NULL, "a9mpcore_priv"); - break; - case CALXEDA_MIDWAY: - dev = qdev_create(NULL, "a15mpcore_priv"); - break; - } - qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); - qdev_prop_set_uint32(dev, "num-irq", NIRQ_GIC); - qdev_init_nofail(dev); - busdev = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE); - for (n = 0; n < smp_cpus; n++) { - sysbus_connect_irq(busdev, n, cpu_irq[n]); - sysbus_connect_irq(busdev, n + smp_cpus, cpu_fiq[n]); - } - - for (n = 0; n < 128; n++) { - pic[n] = qdev_get_gpio_in(dev, n); - } - - dev = qdev_create(NULL, "sp804"); - qdev_prop_set_uint32(dev, "freq0", 150000000); - qdev_prop_set_uint32(dev, "freq1", 150000000); - qdev_init_nofail(dev); - busdev = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(busdev, 0, 0xfff34000); - sysbus_connect_irq(busdev, 0, pic[18]); - sysbus_create_simple("pl011", 0xfff36000, pic[20]); - - dev = qdev_create(NULL, "highbank-regs"); - qdev_init_nofail(dev); - busdev = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(busdev, 0, 0xfff3c000); - - sysbus_create_simple("pl061", 0xfff30000, pic[14]); - sysbus_create_simple("pl061", 0xfff31000, pic[15]); - sysbus_create_simple("pl061", 0xfff32000, pic[16]); - sysbus_create_simple("pl061", 0xfff33000, pic[17]); - sysbus_create_simple("pl031", 0xfff35000, pic[19]); - sysbus_create_simple("pl022", 0xfff39000, pic[23]); - - sysbus_create_simple("sysbus-ahci", 0xffe08000, pic[83]); - - if (nd_table[0].used) { - qemu_check_nic_model(&nd_table[0], "xgmac"); - dev = qdev_create(NULL, "xgmac"); - qdev_set_nic_properties(dev, &nd_table[0]); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xfff50000); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[77]); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, pic[78]); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, pic[79]); - - qemu_check_nic_model(&nd_table[1], "xgmac"); - dev = qdev_create(NULL, "xgmac"); - qdev_set_nic_properties(dev, &nd_table[1]); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xfff51000); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[80]); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, pic[81]); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, pic[82]); - } - - highbank_binfo.ram_size = ram_size; - highbank_binfo.kernel_filename = kernel_filename; - highbank_binfo.kernel_cmdline = kernel_cmdline; - highbank_binfo.initrd_filename = initrd_filename; - /* highbank requires a dtb in order to boot, and the dtb will override - * the board ID. The following value is ignored, so set it to -1 to be - * clear that the value is meaningless. - */ - highbank_binfo.board_id = -1; - highbank_binfo.nb_cpus = smp_cpus; - 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); -} - -static void highbank_init(MachineState *machine) -{ - calxeda_init(machine, CALXEDA_HIGHBANK); -} - -static void midway_init(MachineState *machine) -{ - calxeda_init(machine, CALXEDA_MIDWAY); -} - -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 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) -{ - type_register_static(&highbank_type); - type_register_static(&midway_type); -} - -type_init(calxeda_machines_init) diff --git a/qemu/hw/arm/imx25_pdk.c b/qemu/hw/arm/imx25_pdk.c deleted file mode 100644 index 025b60843..000000000 --- a/qemu/hw/arm/imx25_pdk.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2013 Jean-Christophe Dubois - * - * 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 . - */ - -#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 deleted file mode 100644 index e31bca6e7..000000000 --- a/qemu/hw/arm/integratorcp.c +++ /dev/null @@ -1,674 +0,0 @@ -/* - * ARM Integrator CP System emulation. - * - * Copyright (c) 2005-2007 CodeSourcery. - * Written by Paul Brook - * - * 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" -#include "hw/arm/arm.h" -#include "hw/misc/arm_integrator_debug.h" -#include "net/net.h" -#include "exec/address-spaces.h" -#include "sysemu/sysemu.h" -#include "qemu/error-report.h" - -#define TYPE_INTEGRATOR_CM "integrator_core" -#define INTEGRATOR_CM(obj) \ - OBJECT_CHECK(IntegratorCMState, (obj), TYPE_INTEGRATOR_CM) - -typedef struct IntegratorCMState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - uint32_t memsz; - MemoryRegion flash; - uint32_t cm_osc; - uint32_t cm_ctrl; - uint32_t cm_lock; - uint32_t cm_auxosc; - uint32_t cm_sdram; - uint32_t cm_init; - uint32_t cm_flags; - uint32_t cm_nvflags; - uint32_t cm_refcnt_offset; - uint32_t int_level; - uint32_t irq_enabled; - uint32_t fiq_enabled; -} IntegratorCMState; - -static uint8_t integrator_spd[128] = { - 128, 8, 4, 11, 9, 1, 64, 0, 2, 0xa0, 0xa0, 0, 0, 8, 0, 1, - 0xe, 4, 0x1c, 1, 2, 0x20, 0xc0, 0, 0, 0, 0, 0x30, 0x28, 0x30, 0x28, 0x40 -}; - -static uint64_t integratorcm_read(void *opaque, hwaddr offset, - unsigned size) -{ - IntegratorCMState *s = opaque; - if (offset >= 0x100 && offset < 0x200) { - /* CM_SPD */ - if (offset >= 0x180) - return 0; - return integrator_spd[offset >> 2]; - } - switch (offset >> 2) { - case 0: /* CM_ID */ - return 0x411a3001; - case 1: /* CM_PROC */ - return 0; - case 2: /* CM_OSC */ - return s->cm_osc; - case 3: /* CM_CTRL */ - return s->cm_ctrl; - case 4: /* CM_STAT */ - return 0x00100000; - case 5: /* CM_LOCK */ - if (s->cm_lock == 0xa05f) { - return 0x1a05f; - } else { - return s->cm_lock; - } - case 6: /* CM_LMBUSCNT */ - /* ??? High frequency timer. */ - hw_error("integratorcm_read: CM_LMBUSCNT"); - case 7: /* CM_AUXOSC */ - return s->cm_auxosc; - case 8: /* CM_SDRAM */ - return s->cm_sdram; - case 9: /* CM_INIT */ - return s->cm_init; - case 10: /* CM_REFCNT */ - /* This register, CM_REFCNT, provides a 32-bit count value. - * The count increments at the fixed reference clock frequency of 24MHz - * and can be used as a real-time counter. - */ - return (uint32_t)muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24, - 1000) - s->cm_refcnt_offset; - case 12: /* CM_FLAGS */ - return s->cm_flags; - case 14: /* CM_NVFLAGS */ - return s->cm_nvflags; - case 16: /* CM_IRQ_STAT */ - return s->int_level & s->irq_enabled; - case 17: /* CM_IRQ_RSTAT */ - return s->int_level; - case 18: /* CM_IRQ_ENSET */ - return s->irq_enabled; - case 20: /* CM_SOFT_INTSET */ - return s->int_level & 1; - case 24: /* CM_FIQ_STAT */ - return s->int_level & s->fiq_enabled; - case 25: /* CM_FIQ_RSTAT */ - return s->int_level; - case 26: /* CM_FIQ_ENSET */ - return s->fiq_enabled; - case 32: /* CM_VOLTAGE_CTL0 */ - case 33: /* CM_VOLTAGE_CTL1 */ - case 34: /* CM_VOLTAGE_CTL2 */ - case 35: /* CM_VOLTAGE_CTL3 */ - /* ??? Voltage control unimplemented. */ - return 0; - default: - hw_error("integratorcm_read: Unimplemented offset 0x%x\n", - (int)offset); - return 0; - } -} - -static void integratorcm_do_remap(IntegratorCMState *s) -{ - /* Sync memory region state with CM_CTRL REMAP bit: - * bit 0 => flash at address 0; bit 1 => RAM - */ - memory_region_set_enabled(&s->flash, !(s->cm_ctrl & 4)); -} - -static void integratorcm_set_ctrl(IntegratorCMState *s, uint32_t value) -{ - if (value & 8) { - qemu_system_reset_request(); - } - if ((s->cm_ctrl ^ value) & 1) { - /* (value & 1) != 0 means the green "MISC LED" is lit. - * We don't have any nice place to display LEDs. printf is a bad - * idea because Linux uses the LED as a heartbeat and the output - * will swamp anything else on the terminal. - */ - } - /* Note that the RESET bit [3] always reads as zero */ - s->cm_ctrl = (s->cm_ctrl & ~5) | (value & 5); - integratorcm_do_remap(s); -} - -static void integratorcm_update(IntegratorCMState *s) -{ - /* ??? The CPU irq/fiq is raised when either the core module or base PIC - are active. */ - if (s->int_level & (s->irq_enabled | s->fiq_enabled)) - hw_error("Core module interrupt\n"); -} - -static void integratorcm_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - IntegratorCMState *s = opaque; - switch (offset >> 2) { - case 2: /* CM_OSC */ - if (s->cm_lock == 0xa05f) - s->cm_osc = value; - break; - case 3: /* CM_CTRL */ - integratorcm_set_ctrl(s, value); - break; - case 5: /* CM_LOCK */ - s->cm_lock = value & 0xffff; - break; - case 7: /* CM_AUXOSC */ - if (s->cm_lock == 0xa05f) - s->cm_auxosc = value; - break; - case 8: /* CM_SDRAM */ - s->cm_sdram = value; - break; - case 9: /* CM_INIT */ - /* ??? This can change the memory bus frequency. */ - s->cm_init = value; - break; - case 12: /* CM_FLAGSS */ - s->cm_flags |= value; - break; - case 13: /* CM_FLAGSC */ - s->cm_flags &= ~value; - break; - case 14: /* CM_NVFLAGSS */ - s->cm_nvflags |= value; - break; - case 15: /* CM_NVFLAGSS */ - s->cm_nvflags &= ~value; - break; - case 18: /* CM_IRQ_ENSET */ - s->irq_enabled |= value; - integratorcm_update(s); - break; - case 19: /* CM_IRQ_ENCLR */ - s->irq_enabled &= ~value; - integratorcm_update(s); - break; - case 20: /* CM_SOFT_INTSET */ - s->int_level |= (value & 1); - integratorcm_update(s); - break; - case 21: /* CM_SOFT_INTCLR */ - s->int_level &= ~(value & 1); - integratorcm_update(s); - break; - case 26: /* CM_FIQ_ENSET */ - s->fiq_enabled |= value; - integratorcm_update(s); - break; - case 27: /* CM_FIQ_ENCLR */ - s->fiq_enabled &= ~value; - integratorcm_update(s); - break; - case 32: /* CM_VOLTAGE_CTL0 */ - case 33: /* CM_VOLTAGE_CTL1 */ - case 34: /* CM_VOLTAGE_CTL2 */ - case 35: /* CM_VOLTAGE_CTL3 */ - /* ??? Voltage control unimplemented. */ - break; - default: - hw_error("integratorcm_write: Unimplemented offset 0x%x\n", - (int)offset); - break; - } -} - -/* Integrator/CM control registers. */ - -static const MemoryRegionOps integratorcm_ops = { - .read = integratorcm_read, - .write = integratorcm_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int integratorcm_init(SysBusDevice *dev) -{ - IntegratorCMState *s = INTEGRATOR_CM(dev); - - s->cm_osc = 0x01000048; - /* ??? What should the high bits of this value be? */ - s->cm_auxosc = 0x0007feff; - s->cm_sdram = 0x00011122; - if (s->memsz >= 256) { - integrator_spd[31] = 64; - s->cm_sdram |= 0x10; - } else if (s->memsz >= 128) { - integrator_spd[31] = 32; - s->cm_sdram |= 0x0c; - } else if (s->memsz >= 64) { - integrator_spd[31] = 16; - s->cm_sdram |= 0x08; - } else if (s->memsz >= 32) { - integrator_spd[31] = 4; - s->cm_sdram |= 0x04; - } else { - integrator_spd[31] = 2; - } - memcpy(integrator_spd + 73, "QEMU-MEMORY", 11); - s->cm_init = 0x00000112; - 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_fatal); - vmstate_register_ram_global(&s->flash); - - memory_region_init_io(&s->iomem, OBJECT(s), &integratorcm_ops, s, - "integratorcm", 0x00800000); - sysbus_init_mmio(dev, &s->iomem); - - integratorcm_do_remap(s); - /* ??? Save/restore. */ - return 0; -} - -/* Integrator/CP hardware emulation. */ -/* Primary interrupt controller. */ - -#define TYPE_INTEGRATOR_PIC "integrator_pic" -#define INTEGRATOR_PIC(obj) \ - OBJECT_CHECK(icp_pic_state, (obj), TYPE_INTEGRATOR_PIC) - -typedef struct icp_pic_state { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - uint32_t level; - uint32_t irq_enabled; - uint32_t fiq_enabled; - qemu_irq parent_irq; - qemu_irq parent_fiq; -} icp_pic_state; - -static void icp_pic_update(icp_pic_state *s) -{ - uint32_t flags; - - flags = (s->level & s->irq_enabled); - qemu_set_irq(s->parent_irq, flags != 0); - flags = (s->level & s->fiq_enabled); - qemu_set_irq(s->parent_fiq, flags != 0); -} - -static void icp_pic_set_irq(void *opaque, int irq, int level) -{ - icp_pic_state *s = (icp_pic_state *)opaque; - if (level) - s->level |= 1 << irq; - else - s->level &= ~(1 << irq); - icp_pic_update(s); -} - -static uint64_t icp_pic_read(void *opaque, hwaddr offset, - unsigned size) -{ - icp_pic_state *s = (icp_pic_state *)opaque; - - switch (offset >> 2) { - case 0: /* IRQ_STATUS */ - return s->level & s->irq_enabled; - case 1: /* IRQ_RAWSTAT */ - return s->level; - case 2: /* IRQ_ENABLESET */ - return s->irq_enabled; - case 4: /* INT_SOFTSET */ - return s->level & 1; - case 8: /* FRQ_STATUS */ - return s->level & s->fiq_enabled; - case 9: /* FRQ_RAWSTAT */ - return s->level; - case 10: /* FRQ_ENABLESET */ - return s->fiq_enabled; - case 3: /* IRQ_ENABLECLR */ - case 5: /* INT_SOFTCLR */ - case 11: /* FRQ_ENABLECLR */ - default: - printf ("icp_pic_read: Bad register offset 0x%x\n", (int)offset); - return 0; - } -} - -static void icp_pic_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - icp_pic_state *s = (icp_pic_state *)opaque; - - switch (offset >> 2) { - case 2: /* IRQ_ENABLESET */ - s->irq_enabled |= value; - break; - case 3: /* IRQ_ENABLECLR */ - s->irq_enabled &= ~value; - break; - case 4: /* INT_SOFTSET */ - if (value & 1) - icp_pic_set_irq(s, 0, 1); - break; - case 5: /* INT_SOFTCLR */ - if (value & 1) - icp_pic_set_irq(s, 0, 0); - break; - case 10: /* FRQ_ENABLESET */ - s->fiq_enabled |= value; - break; - case 11: /* FRQ_ENABLECLR */ - s->fiq_enabled &= ~value; - break; - case 0: /* IRQ_STATUS */ - case 1: /* IRQ_RAWSTAT */ - case 8: /* FRQ_STATUS */ - case 9: /* FRQ_RAWSTAT */ - default: - printf ("icp_pic_write: Bad register offset 0x%x\n", (int)offset); - return; - } - icp_pic_update(s); -} - -static const MemoryRegionOps icp_pic_ops = { - .read = icp_pic_read, - .write = icp_pic_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int icp_pic_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - icp_pic_state *s = INTEGRATOR_PIC(dev); - - qdev_init_gpio_in(dev, icp_pic_set_irq, 32); - sysbus_init_irq(sbd, &s->parent_irq); - sysbus_init_irq(sbd, &s->parent_fiq); - memory_region_init_io(&s->iomem, OBJECT(s), &icp_pic_ops, s, - "icp-pic", 0x00800000); - sysbus_init_mmio(sbd, &s->iomem); - return 0; -} - -/* CP control registers. */ - -#define TYPE_ICP_CONTROL_REGS "icp-ctrl-regs" -#define ICP_CONTROL_REGS(obj) \ - OBJECT_CHECK(ICPCtrlRegsState, (obj), TYPE_ICP_CONTROL_REGS) - -typedef struct ICPCtrlRegsState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - - qemu_irq mmc_irq; - uint32_t intreg_state; -} ICPCtrlRegsState; - -#define ICP_GPIO_MMC_WPROT "mmc-wprot" -#define ICP_GPIO_MMC_CARDIN "mmc-cardin" - -#define ICP_INTREG_WPROT (1 << 0) -#define ICP_INTREG_CARDIN (1 << 3) - -static uint64_t icp_control_read(void *opaque, hwaddr offset, - unsigned size) -{ - ICPCtrlRegsState *s = opaque; - - switch (offset >> 2) { - case 0: /* CP_IDFIELD */ - return 0x41034003; - case 1: /* CP_FLASHPROG */ - return 0; - case 2: /* CP_INTREG */ - return s->intreg_state; - case 3: /* CP_DECODE */ - return 0x11; - default: - hw_error("icp_control_read: Bad offset %x\n", (int)offset); - return 0; - } -} - -static void icp_control_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - ICPCtrlRegsState *s = opaque; - - switch (offset >> 2) { - case 2: /* CP_INTREG */ - s->intreg_state &= ~(value & ICP_INTREG_CARDIN); - qemu_set_irq(s->mmc_irq, !!(s->intreg_state & ICP_INTREG_CARDIN)); - break; - case 1: /* CP_FLASHPROG */ - case 3: /* CP_DECODE */ - /* Nothing interesting implemented yet. */ - break; - default: - hw_error("icp_control_write: Bad offset %x\n", (int)offset); - } -} - -static const MemoryRegionOps icp_control_ops = { - .read = icp_control_read, - .write = icp_control_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void icp_control_mmc_wprot(void *opaque, int line, int level) -{ - ICPCtrlRegsState *s = opaque; - - s->intreg_state &= ~ICP_INTREG_WPROT; - if (level) { - s->intreg_state |= ICP_INTREG_WPROT; - } -} - -static void icp_control_mmc_cardin(void *opaque, int line, int level) -{ - ICPCtrlRegsState *s = opaque; - - /* line is released by writing to CP_INTREG */ - if (level) { - s->intreg_state |= ICP_INTREG_CARDIN; - qemu_set_irq(s->mmc_irq, 1); - } -} - -static void icp_control_init(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - ICPCtrlRegsState *s = ICP_CONTROL_REGS(obj); - DeviceState *dev = DEVICE(obj); - - memory_region_init_io(&s->iomem, OBJECT(s), &icp_control_ops, s, - "icp_ctrl_regs", 0x00800000); - sysbus_init_mmio(sbd, &s->iomem); - - qdev_init_gpio_in_named(dev, icp_control_mmc_wprot, ICP_GPIO_MMC_WPROT, 1); - qdev_init_gpio_in_named(dev, icp_control_mmc_cardin, - ICP_GPIO_MMC_CARDIN, 1); - sysbus_init_irq(sbd, &s->mmc_irq); -} - - -/* Board init. */ - -static struct arm_boot_info integrator_binfo = { - .loader_start = 0x0, - .board_id = 0x113, -}; - -static void integratorcp_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; - ObjectClass *cpu_oc; - Object *cpuobj; - ARMCPU *cpu; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *ram_alias = g_new(MemoryRegion, 1); - qemu_irq pic[32]; - DeviceState *dev, *sic, *icp; - int i; - - if (!cpu_model) { - cpu_model = "arm926"; - } - - cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); - if (!cpu_oc) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - - cpuobj = object_new(object_class_get_name(cpu_oc)); - - /* By default 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", &error_fatal); - } - - object_property_set_bool(cpuobj, true, "realized", &error_fatal); - - cpu = ARM_CPU(cpuobj); - - memory_region_allocate_system_memory(ram, NULL, "integrator.ram", - ram_size); - /* ??? On a real system the first 1Mb is mapped as SSRAM or boot flash. */ - /* ??? RAM should repeat to fill physical memory space. */ - /* SDRAM at address zero*/ - memory_region_add_subregion(address_space_mem, 0, ram); - /* And again at address 0x80000000 */ - memory_region_init_alias(ram_alias, NULL, "ram.alias", ram, 0, ram_size); - memory_region_add_subregion(address_space_mem, 0x80000000, ram_alias); - - dev = qdev_create(NULL, TYPE_INTEGRATOR_CM); - qdev_prop_set_uint32(dev, "memsz", ram_size >> 20); - qdev_init_nofail(dev); - sysbus_mmio_map((SysBusDevice *)dev, 0, 0x10000000); - - dev = sysbus_create_varargs(TYPE_INTEGRATOR_PIC, 0x14000000, - qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ), - qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ), - NULL); - for (i = 0; i < 32; i++) { - pic[i] = qdev_get_gpio_in(dev, i); - } - sic = sysbus_create_simple(TYPE_INTEGRATOR_PIC, 0xca000000, pic[26]); - sysbus_create_varargs("integrator_pit", 0x13000000, - pic[5], pic[6], pic[7], NULL); - sysbus_create_simple("pl031", 0x15000000, pic[8]); - sysbus_create_simple("pl011", 0x16000000, pic[1]); - sysbus_create_simple("pl011", 0x17000000, pic[2]); - icp = sysbus_create_simple(TYPE_ICP_CONTROL_REGS, 0xcb000000, - qdev_get_gpio_in(sic, 3)); - sysbus_create_simple("pl050_keyboard", 0x18000000, pic[3]); - sysbus_create_simple("pl050_mouse", 0x19000000, pic[4]); - sysbus_create_simple(TYPE_INTEGRATOR_DEBUG, 0x1a000000, 0); - - dev = sysbus_create_varargs("pl181", 0x1c000000, pic[23], pic[24], NULL); - qdev_connect_gpio_out(dev, 0, - qdev_get_gpio_in_named(icp, ICP_GPIO_MMC_WPROT, 0)); - qdev_connect_gpio_out(dev, 1, - qdev_get_gpio_in_named(icp, ICP_GPIO_MMC_CARDIN, 0)); - - if (nd_table[0].used) - smc91c111_init(&nd_table[0], 0xc8000000, pic[27]); - - sysbus_create_simple("pl110", 0xc0000000, pic[22]); - - integrator_binfo.ram_size = ram_size; - integrator_binfo.kernel_filename = kernel_filename; - integrator_binfo.kernel_cmdline = kernel_cmdline; - integrator_binfo.initrd_filename = initrd_filename; - arm_load_kernel(cpu, &integrator_binfo); -} - -static void integratorcp_machine_init(MachineClass *mc) -{ - mc->desc = "ARM Integrator/CP (ARM926EJ-S)"; - mc->init = integratorcp_init; -} - -DEFINE_MACHINE("integratorcp", integratorcp_machine_init) - -static Property core_properties[] = { - DEFINE_PROP_UINT32("memsz", IntegratorCMState, memsz, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void core_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = integratorcm_init; - dc->props = core_properties; -} - -static const TypeInfo core_info = { - .name = TYPE_INTEGRATOR_CM, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IntegratorCMState), - .class_init = core_class_init, -}; - -static void icp_pic_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = icp_pic_init; -} - -static const TypeInfo icp_pic_info = { - .name = TYPE_INTEGRATOR_PIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(icp_pic_state), - .class_init = icp_pic_class_init, -}; - -static const TypeInfo icp_ctrl_regs_info = { - .name = TYPE_ICP_CONTROL_REGS, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ICPCtrlRegsState), - .instance_init = icp_control_init, -}; - -static void integratorcp_register_types(void) -{ - type_register_static(&icp_pic_info); - type_register_static(&core_info); - type_register_static(&icp_ctrl_regs_info); -} - -type_init(integratorcp_register_types) diff --git a/qemu/hw/arm/kzm.c b/qemu/hw/arm/kzm.c deleted file mode 100644 index 2c96ee33b..000000000 --- a/qemu/hw/arm/kzm.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * KZM Board System emulation. - * - * Copyright (c) 2008 OKL and 2011 NICTA - * Written by Hans at OK-Labs - * Updated by Peter Chubb. - * - * This code is licensed under the GPL, version 2 or later. - * See the file `COPYING' in the top level directory. - * - * It (partially) emulates a Kyoto Microcomputer - * KZM-ARM11-01 evaluation board, with a Freescale - * i.MX31 SoC - */ - -#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 "net/net.h" -#include "hw/devices.h" -#include "hw/char/serial.h" -#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_RAM_ADDR, - .board_id = 1722, -}; - -static void kzm_init(MachineState *machine) -{ - 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; - } - - 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; - } - - if (nd_table[0].used) { - 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(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 = 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; - - if (!qtest_enabled()) { - arm_load_kernel(&s->soc.cpu, &kzm_binfo); - } -} - -static void kzm_machine_init(MachineClass *mc) -{ - mc->desc = "ARM KZM Emulation Baseboard (ARM1136)"; - mc->init = kzm_init; -} - -DEFINE_MACHINE("kzm", kzm_machine_init) diff --git a/qemu/hw/arm/mainstone.c b/qemu/hw/arm/mainstone.c deleted file mode 100644 index 454acc5d2..000000000 --- a/qemu/hw/arm/mainstone.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - * PXA270-based Intel Mainstone platforms. - * - * Copyright (c) 2007 by Armin Kuster or - * - * - * Code based on spitz platform by Andrzej Zaborowski - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/hw.h" -#include "hw/arm/pxa.h" -#include "hw/arm/arm.h" -#include "net/net.h" -#include "hw/devices.h" -#include "hw/boards.h" -#include "hw/block/flash.h" -#include "sysemu/block-backend.h" -#include "hw/sysbus.h" -#include "exec/address-spaces.h" -#include "sysemu/qtest.h" - -/* Device addresses */ -#define MST_FPGA_PHYS 0x08000000 -#define MST_ETH_PHYS 0x10000300 -#define MST_FLASH_0 0x00000000 -#define MST_FLASH_1 0x04000000 - -/* IRQ definitions */ -#define MMC_IRQ 0 -#define USIM_IRQ 1 -#define USBC_IRQ 2 -#define ETHERNET_IRQ 3 -#define AC97_IRQ 4 -#define PEN_IRQ 5 -#define MSINS_IRQ 6 -#define EXBRD_IRQ 7 -#define S0_CD_IRQ 9 -#define S0_STSCHG_IRQ 10 -#define S0_IRQ 11 -#define S1_CD_IRQ 13 -#define S1_STSCHG_IRQ 14 -#define S1_IRQ 15 - -static const struct keymap map[0xE0] = { - [0 ... 0xDF] = { -1, -1 }, - [0x1e] = {0,0}, /* a */ - [0x30] = {0,1}, /* b */ - [0x2e] = {0,2}, /* c */ - [0x20] = {0,3}, /* d */ - [0x12] = {0,4}, /* e */ - [0x21] = {0,5}, /* f */ - [0x22] = {1,0}, /* g */ - [0x23] = {1,1}, /* h */ - [0x17] = {1,2}, /* i */ - [0x24] = {1,3}, /* j */ - [0x25] = {1,4}, /* k */ - [0x26] = {1,5}, /* l */ - [0x32] = {2,0}, /* m */ - [0x31] = {2,1}, /* n */ - [0x18] = {2,2}, /* o */ - [0x19] = {2,3}, /* p */ - [0x10] = {2,4}, /* q */ - [0x13] = {2,5}, /* r */ - [0x1f] = {3,0}, /* s */ - [0x14] = {3,1}, /* t */ - [0x16] = {3,2}, /* u */ - [0x2f] = {3,3}, /* v */ - [0x11] = {3,4}, /* w */ - [0x2d] = {3,5}, /* x */ - [0x15] = {4,2}, /* y */ - [0x2c] = {4,3}, /* z */ - [0xc7] = {5,0}, /* Home */ - [0x2a] = {5,1}, /* shift */ - /* - * There are two matrix positions which map to space, - * but QEMU can only use one of them for the reverse - * mapping, so simply use the second one. - */ - /* [0x39] = {5,2}, space */ - [0x39] = {5,3}, /* space */ - /* - * Matrix position {5,4} and other keys are missing here. - * TODO: Compare with Linux code and test real hardware. - */ - [0x1c] = {5,5}, /* enter (TODO: might be wrong) */ - [0xc8] = {6,0}, /* up */ - [0xd0] = {6,1}, /* down */ - [0xcb] = {6,2}, /* left */ - [0xcd] = {6,3}, /* right */ -}; - -enum mainstone_model_e { mainstone }; - -#define MAINSTONE_RAM 0x04000000 -#define MAINSTONE_ROM 0x00800000 -#define MAINSTONE_FLASH 0x02000000 - -static struct arm_boot_info mainstone_binfo = { - .loader_start = PXA2XX_SDRAM_BASE, - .ram_size = 0x04000000, -}; - -static void mainstone_common_init(MemoryRegion *address_space_mem, - MachineState *machine, - enum mainstone_model_e model, int arm_id) -{ - uint32_t sector_len = 256 * 1024; - hwaddr mainstone_flash_base[] = { MST_FLASH_0, MST_FLASH_1 }; - PXA2xxState *mpu; - DeviceState *mst_irq; - DriveInfo *dinfo; - int i; - int be; - MemoryRegion *rom = g_new(MemoryRegion, 1); - const char *cpu_model = machine->cpu_model; - - if (!cpu_model) - cpu_model = "pxa270-c5"; - - /* 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_fatal); - vmstate_register_ram_global(rom); - memory_region_set_readonly(rom, true); - memory_region_add_subregion(address_space_mem, 0, rom); - -#ifdef TARGET_WORDS_BIGENDIAN - be = 1; -#else - be = 0; -#endif - /* There are two 32MiB flash devices on the board */ - for (i = 0; i < 2; i ++) { - dinfo = drive_get(IF_PFLASH, 0, i); - if (!dinfo) { - if (qtest_enabled()) { - break; - } - fprintf(stderr, "Two flash images must be given with the " - "'pflash' parameter\n"); - exit(1); - } - - if (!pflash_cfi01_register(mainstone_flash_base[i], NULL, - i ? "mainstone.flash1" : "mainstone.flash0", - MAINSTONE_FLASH, - blk_by_legacy_dinfo(dinfo), - sector_len, MAINSTONE_FLASH / sector_len, - 4, 0, 0, 0, 0, be)) { - fprintf(stderr, "qemu: Error registering flash memory.\n"); - exit(1); - } - } - - mst_irq = sysbus_create_simple("mainstone-fpga", MST_FPGA_PHYS, - qdev_get_gpio_in(mpu->gpio, 0)); - - /* setup keypad */ - pxa27x_register_keypad(mpu->kp, map, 0xe0); - - /* MMC/SD host */ - pxa2xx_mmci_handlers(mpu->mmc, NULL, qdev_get_gpio_in(mst_irq, MMC_IRQ)); - - pxa2xx_pcmcia_set_irq_cb(mpu->pcmcia[0], - qdev_get_gpio_in(mst_irq, S0_IRQ), - qdev_get_gpio_in(mst_irq, S0_CD_IRQ)); - pxa2xx_pcmcia_set_irq_cb(mpu->pcmcia[1], - qdev_get_gpio_in(mst_irq, S1_IRQ), - qdev_get_gpio_in(mst_irq, S1_CD_IRQ)); - - smc91c111_init(&nd_table[0], MST_ETH_PHYS, - qdev_get_gpio_in(mst_irq, ETHERNET_IRQ)); - - mainstone_binfo.kernel_filename = machine->kernel_filename; - mainstone_binfo.kernel_cmdline = machine->kernel_cmdline; - mainstone_binfo.initrd_filename = machine->initrd_filename; - mainstone_binfo.board_id = arm_id; - arm_load_kernel(mpu->cpu, &mainstone_binfo); -} - -static void mainstone_init(MachineState *machine) -{ - mainstone_common_init(get_system_memory(), machine, mainstone, 0x196); -} - -static void mainstone2_machine_init(MachineClass *mc) -{ - mc->desc = "Mainstone II (PXA27x)"; - mc->init = mainstone_init; -} - -DEFINE_MACHINE("mainstone", mainstone2_machine_init) diff --git a/qemu/hw/arm/musicpal.c b/qemu/hw/arm/musicpal.c deleted file mode 100644 index 7a4cc07dd..000000000 --- a/qemu/hw/arm/musicpal.c +++ /dev/null @@ -1,1751 +0,0 @@ -/* - * Marvell MV88W8618 / Freecom MusicPal emulation. - * - * Copyright (c) 2008 Jan Kiszka - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#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" -#include "net/net.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" -#include "hw/char/serial.h" -#include "qemu/timer.h" -#include "hw/ptimer.h" -#include "hw/block/flash.h" -#include "ui/console.h" -#include "hw/i2c/i2c.h" -#include "sysemu/block-backend.h" -#include "exec/address-spaces.h" -#include "ui/pixel_ops.h" - -#define MP_MISC_BASE 0x80002000 -#define MP_MISC_SIZE 0x00001000 - -#define MP_ETH_BASE 0x80008000 -#define MP_ETH_SIZE 0x00001000 - -#define MP_WLAN_BASE 0x8000C000 -#define MP_WLAN_SIZE 0x00000800 - -#define MP_UART1_BASE 0x8000C840 -#define MP_UART2_BASE 0x8000C940 - -#define MP_GPIO_BASE 0x8000D000 -#define MP_GPIO_SIZE 0x00001000 - -#define MP_FLASHCFG_BASE 0x90006000 -#define MP_FLASHCFG_SIZE 0x00001000 - -#define MP_AUDIO_BASE 0x90007000 - -#define MP_PIC_BASE 0x90008000 -#define MP_PIC_SIZE 0x00001000 - -#define MP_PIT_BASE 0x90009000 -#define MP_PIT_SIZE 0x00001000 - -#define MP_LCD_BASE 0x9000c000 -#define MP_LCD_SIZE 0x00001000 - -#define MP_SRAM_BASE 0xC0000000 -#define MP_SRAM_SIZE 0x00020000 - -#define MP_RAM_DEFAULT_SIZE 32*1024*1024 -#define MP_FLASH_SIZE_MAX 32*1024*1024 - -#define MP_TIMER1_IRQ 4 -#define MP_TIMER2_IRQ 5 -#define MP_TIMER3_IRQ 6 -#define MP_TIMER4_IRQ 7 -#define MP_EHCI_IRQ 8 -#define MP_ETH_IRQ 9 -#define MP_UART1_IRQ 11 -#define MP_UART2_IRQ 11 -#define MP_GPIO_IRQ 12 -#define MP_RTC_IRQ 28 -#define MP_AUDIO_IRQ 30 - -/* Wolfson 8750 I2C address */ -#define MP_WM_ADDR 0x1A - -/* Ethernet register offsets */ -#define MP_ETH_SMIR 0x010 -#define MP_ETH_PCXR 0x408 -#define MP_ETH_SDCMR 0x448 -#define MP_ETH_ICR 0x450 -#define MP_ETH_IMR 0x458 -#define MP_ETH_FRDP0 0x480 -#define MP_ETH_FRDP1 0x484 -#define MP_ETH_FRDP2 0x488 -#define MP_ETH_FRDP3 0x48C -#define MP_ETH_CRDP0 0x4A0 -#define MP_ETH_CRDP1 0x4A4 -#define MP_ETH_CRDP2 0x4A8 -#define MP_ETH_CRDP3 0x4AC -#define MP_ETH_CTDP0 0x4E0 -#define MP_ETH_CTDP1 0x4E4 - -/* MII PHY access */ -#define MP_ETH_SMIR_DATA 0x0000FFFF -#define MP_ETH_SMIR_ADDR 0x03FF0000 -#define MP_ETH_SMIR_OPCODE (1 << 26) /* Read value */ -#define MP_ETH_SMIR_RDVALID (1 << 27) - -/* PHY registers */ -#define MP_ETH_PHY1_BMSR 0x00210000 -#define MP_ETH_PHY1_PHYSID1 0x00410000 -#define MP_ETH_PHY1_PHYSID2 0x00610000 - -#define MP_PHY_BMSR_LINK 0x0004 -#define MP_PHY_BMSR_AUTONEG 0x0008 - -#define MP_PHY_88E3015 0x01410E20 - -/* TX descriptor status */ -#define MP_ETH_TX_OWN (1U << 31) - -/* RX descriptor status */ -#define MP_ETH_RX_OWN (1U << 31) - -/* Interrupt cause/mask bits */ -#define MP_ETH_IRQ_RX_BIT 0 -#define MP_ETH_IRQ_RX (1 << MP_ETH_IRQ_RX_BIT) -#define MP_ETH_IRQ_TXHI_BIT 2 -#define MP_ETH_IRQ_TXLO_BIT 3 - -/* Port config bits */ -#define MP_ETH_PCXR_2BSM_BIT 28 /* 2-byte incoming suffix */ - -/* SDMA command bits */ -#define MP_ETH_CMD_TXHI (1 << 23) -#define MP_ETH_CMD_TXLO (1 << 22) - -typedef struct mv88w8618_tx_desc { - uint32_t cmdstat; - uint16_t res; - uint16_t bytes; - uint32_t buffer; - uint32_t next; -} mv88w8618_tx_desc; - -typedef struct mv88w8618_rx_desc { - uint32_t cmdstat; - uint16_t bytes; - uint16_t buffer_size; - uint32_t buffer; - uint32_t next; -} mv88w8618_rx_desc; - -#define TYPE_MV88W8618_ETH "mv88w8618_eth" -#define MV88W8618_ETH(obj) \ - OBJECT_CHECK(mv88w8618_eth_state, (obj), TYPE_MV88W8618_ETH) - -typedef struct mv88w8618_eth_state { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - qemu_irq irq; - uint32_t smir; - uint32_t icr; - uint32_t imr; - int mmio_index; - uint32_t vlan_header; - uint32_t tx_queue[2]; - uint32_t rx_queue[4]; - uint32_t frx_queue[4]; - uint32_t cur_rx[4]; - NICState *nic; - NICConf conf; -} mv88w8618_eth_state; - -static void eth_rx_desc_put(uint32_t addr, mv88w8618_rx_desc *desc) -{ - cpu_to_le32s(&desc->cmdstat); - cpu_to_le16s(&desc->bytes); - cpu_to_le16s(&desc->buffer_size); - cpu_to_le32s(&desc->buffer); - cpu_to_le32s(&desc->next); - cpu_physical_memory_write(addr, desc, sizeof(*desc)); -} - -static void eth_rx_desc_get(uint32_t addr, mv88w8618_rx_desc *desc) -{ - cpu_physical_memory_read(addr, desc, sizeof(*desc)); - le32_to_cpus(&desc->cmdstat); - le16_to_cpus(&desc->bytes); - le16_to_cpus(&desc->buffer_size); - le32_to_cpus(&desc->buffer); - le32_to_cpus(&desc->next); -} - -static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) -{ - mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); - uint32_t desc_addr; - mv88w8618_rx_desc desc; - int i; - - for (i = 0; i < 4; i++) { - desc_addr = s->cur_rx[i]; - if (!desc_addr) { - continue; - } - do { - eth_rx_desc_get(desc_addr, &desc); - if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) { - cpu_physical_memory_write(desc.buffer + s->vlan_header, - buf, size); - desc.bytes = size + s->vlan_header; - desc.cmdstat &= ~MP_ETH_RX_OWN; - s->cur_rx[i] = desc.next; - - s->icr |= MP_ETH_IRQ_RX; - if (s->icr & s->imr) { - qemu_irq_raise(s->irq); - } - eth_rx_desc_put(desc_addr, &desc); - return size; - } - desc_addr = desc.next; - } while (desc_addr != s->rx_queue[i]); - } - return size; -} - -static void eth_tx_desc_put(uint32_t addr, mv88w8618_tx_desc *desc) -{ - cpu_to_le32s(&desc->cmdstat); - cpu_to_le16s(&desc->res); - cpu_to_le16s(&desc->bytes); - cpu_to_le32s(&desc->buffer); - cpu_to_le32s(&desc->next); - cpu_physical_memory_write(addr, desc, sizeof(*desc)); -} - -static void eth_tx_desc_get(uint32_t addr, mv88w8618_tx_desc *desc) -{ - cpu_physical_memory_read(addr, desc, sizeof(*desc)); - le32_to_cpus(&desc->cmdstat); - le16_to_cpus(&desc->res); - le16_to_cpus(&desc->bytes); - le32_to_cpus(&desc->buffer); - le32_to_cpus(&desc->next); -} - -static void eth_send(mv88w8618_eth_state *s, int queue_index) -{ - uint32_t desc_addr = s->tx_queue[queue_index]; - mv88w8618_tx_desc desc; - uint32_t next_desc; - uint8_t buf[2048]; - int len; - - do { - eth_tx_desc_get(desc_addr, &desc); - next_desc = desc.next; - if (desc.cmdstat & MP_ETH_TX_OWN) { - len = desc.bytes; - if (len < 2048) { - cpu_physical_memory_read(desc.buffer, buf, len); - qemu_send_packet(qemu_get_queue(s->nic), buf, len); - } - desc.cmdstat &= ~MP_ETH_TX_OWN; - s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index); - eth_tx_desc_put(desc_addr, &desc); - } - desc_addr = next_desc; - } while (desc_addr != s->tx_queue[queue_index]); -} - -static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset, - unsigned size) -{ - mv88w8618_eth_state *s = opaque; - - switch (offset) { - case MP_ETH_SMIR: - if (s->smir & MP_ETH_SMIR_OPCODE) { - switch (s->smir & MP_ETH_SMIR_ADDR) { - case MP_ETH_PHY1_BMSR: - return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG | - MP_ETH_SMIR_RDVALID; - case MP_ETH_PHY1_PHYSID1: - return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID; - case MP_ETH_PHY1_PHYSID2: - return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID; - default: - return MP_ETH_SMIR_RDVALID; - } - } - return 0; - - case MP_ETH_ICR: - return s->icr; - - case MP_ETH_IMR: - return s->imr; - - case MP_ETH_FRDP0 ... MP_ETH_FRDP3: - return s->frx_queue[(offset - MP_ETH_FRDP0)/4]; - - case MP_ETH_CRDP0 ... MP_ETH_CRDP3: - return s->rx_queue[(offset - MP_ETH_CRDP0)/4]; - - case MP_ETH_CTDP0 ... MP_ETH_CTDP1: - return s->tx_queue[(offset - MP_ETH_CTDP0)/4]; - - default: - return 0; - } -} - -static void mv88w8618_eth_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - mv88w8618_eth_state *s = opaque; - - switch (offset) { - case MP_ETH_SMIR: - s->smir = value; - break; - - case MP_ETH_PCXR: - s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2; - break; - - case MP_ETH_SDCMR: - if (value & MP_ETH_CMD_TXHI) { - eth_send(s, 1); - } - if (value & MP_ETH_CMD_TXLO) { - eth_send(s, 0); - } - if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) { - qemu_irq_raise(s->irq); - } - break; - - case MP_ETH_ICR: - s->icr &= value; - break; - - case MP_ETH_IMR: - s->imr = value; - if (s->icr & s->imr) { - qemu_irq_raise(s->irq); - } - break; - - case MP_ETH_FRDP0 ... MP_ETH_FRDP3: - s->frx_queue[(offset - MP_ETH_FRDP0)/4] = value; - break; - - case MP_ETH_CRDP0 ... MP_ETH_CRDP3: - s->rx_queue[(offset - MP_ETH_CRDP0)/4] = - s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value; - break; - - case MP_ETH_CTDP0 ... MP_ETH_CTDP1: - s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value; - break; - } -} - -static const MemoryRegionOps mv88w8618_eth_ops = { - .read = mv88w8618_eth_read, - .write = mv88w8618_eth_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void eth_cleanup(NetClientState *nc) -{ - mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - -static NetClientInfo net_mv88w8618_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = eth_receive, - .cleanup = eth_cleanup, -}; - -static int mv88w8618_eth_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - mv88w8618_eth_state *s = MV88W8618_ETH(dev); - - sysbus_init_irq(sbd, &s->irq); - s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); - memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_eth_ops, s, - "mv88w8618-eth", MP_ETH_SIZE); - sysbus_init_mmio(sbd, &s->iomem); - return 0; -} - -static const VMStateDescription mv88w8618_eth_vmsd = { - .name = "mv88w8618_eth", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(smir, mv88w8618_eth_state), - VMSTATE_UINT32(icr, mv88w8618_eth_state), - VMSTATE_UINT32(imr, mv88w8618_eth_state), - VMSTATE_UINT32(vlan_header, mv88w8618_eth_state), - VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2), - VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4), - VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4), - VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4), - VMSTATE_END_OF_LIST() - } -}; - -static Property mv88w8618_eth_properties[] = { - DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void mv88w8618_eth_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = mv88w8618_eth_init; - dc->vmsd = &mv88w8618_eth_vmsd; - dc->props = mv88w8618_eth_properties; -} - -static const TypeInfo mv88w8618_eth_info = { - .name = TYPE_MV88W8618_ETH, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(mv88w8618_eth_state), - .class_init = mv88w8618_eth_class_init, -}; - -/* LCD register offsets */ -#define MP_LCD_IRQCTRL 0x180 -#define MP_LCD_IRQSTAT 0x184 -#define MP_LCD_SPICTRL 0x1ac -#define MP_LCD_INST 0x1bc -#define MP_LCD_DATA 0x1c0 - -/* Mode magics */ -#define MP_LCD_SPI_DATA 0x00100011 -#define MP_LCD_SPI_CMD 0x00104011 -#define MP_LCD_SPI_INVALID 0x00000000 - -/* Commmands */ -#define MP_LCD_INST_SETPAGE0 0xB0 -/* ... */ -#define MP_LCD_INST_SETPAGE7 0xB7 - -#define MP_LCD_TEXTCOLOR 0xe0e0ff /* RRGGBB */ - -#define TYPE_MUSICPAL_LCD "musicpal_lcd" -#define MUSICPAL_LCD(obj) \ - OBJECT_CHECK(musicpal_lcd_state, (obj), TYPE_MUSICPAL_LCD) - -typedef struct musicpal_lcd_state { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - uint32_t brightness; - uint32_t mode; - uint32_t irqctrl; - uint32_t page; - uint32_t page_off; - QemuConsole *con; - uint8_t video_ram[128*64/8]; -} musicpal_lcd_state; - -static uint8_t scale_lcd_color(musicpal_lcd_state *s, uint8_t col) -{ - switch (s->brightness) { - case 7: - return col; - case 0: - return 0; - default: - return (col * s->brightness) / 7; - } -} - -#define SET_LCD_PIXEL(depth, type) \ -static inline void glue(set_lcd_pixel, depth) \ - (musicpal_lcd_state *s, int x, int y, type col) \ -{ \ - int dx, dy; \ - DisplaySurface *surface = qemu_console_surface(s->con); \ - type *pixel = &((type *) surface_data(surface))[(y * 128 * 3 + x) * 3]; \ -\ - for (dy = 0; dy < 3; dy++, pixel += 127 * 3) \ - for (dx = 0; dx < 3; dx++, pixel++) \ - *pixel = col; \ -} -SET_LCD_PIXEL(8, uint8_t) -SET_LCD_PIXEL(16, uint16_t) -SET_LCD_PIXEL(32, uint32_t) - -static void lcd_refresh(void *opaque) -{ - musicpal_lcd_state *s = opaque; - DisplaySurface *surface = qemu_console_surface(s->con); - int x, y, col; - - switch (surface_bits_per_pixel(surface)) { - case 0: - return; -#define LCD_REFRESH(depth, func) \ - case depth: \ - col = func(scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 16) & 0xff), \ - scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 8) & 0xff), \ - scale_lcd_color(s, MP_LCD_TEXTCOLOR & 0xff)); \ - for (x = 0; x < 128; x++) { \ - for (y = 0; y < 64; y++) { \ - if (s->video_ram[x + (y/8)*128] & (1 << (y % 8))) { \ - glue(set_lcd_pixel, depth)(s, x, y, col); \ - } else { \ - glue(set_lcd_pixel, depth)(s, x, y, 0); \ - } \ - } \ - } \ - break; - LCD_REFRESH(8, rgb_to_pixel8) - LCD_REFRESH(16, rgb_to_pixel16) - LCD_REFRESH(32, (is_surface_bgr(surface) ? - rgb_to_pixel32bgr : rgb_to_pixel32)) - default: - hw_error("unsupported colour depth %i\n", - surface_bits_per_pixel(surface)); - } - - dpy_gfx_update(s->con, 0, 0, 128*3, 64*3); -} - -static void lcd_invalidate(void *opaque) -{ -} - -static void musicpal_lcd_gpio_brightness_in(void *opaque, int irq, int level) -{ - musicpal_lcd_state *s = opaque; - s->brightness &= ~(1 << irq); - s->brightness |= level << irq; -} - -static uint64_t musicpal_lcd_read(void *opaque, hwaddr offset, - unsigned size) -{ - musicpal_lcd_state *s = opaque; - - switch (offset) { - case MP_LCD_IRQCTRL: - return s->irqctrl; - - default: - return 0; - } -} - -static void musicpal_lcd_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - musicpal_lcd_state *s = opaque; - - switch (offset) { - case MP_LCD_IRQCTRL: - s->irqctrl = value; - break; - - case MP_LCD_SPICTRL: - if (value == MP_LCD_SPI_DATA || value == MP_LCD_SPI_CMD) { - s->mode = value; - } else { - s->mode = MP_LCD_SPI_INVALID; - } - break; - - case MP_LCD_INST: - if (value >= MP_LCD_INST_SETPAGE0 && value <= MP_LCD_INST_SETPAGE7) { - s->page = value - MP_LCD_INST_SETPAGE0; - s->page_off = 0; - } - break; - - case MP_LCD_DATA: - if (s->mode == MP_LCD_SPI_CMD) { - if (value >= MP_LCD_INST_SETPAGE0 && - value <= MP_LCD_INST_SETPAGE7) { - s->page = value - MP_LCD_INST_SETPAGE0; - s->page_off = 0; - } - } else if (s->mode == MP_LCD_SPI_DATA) { - s->video_ram[s->page*128 + s->page_off] = value; - s->page_off = (s->page_off + 1) & 127; - } - break; - } -} - -static const MemoryRegionOps musicpal_lcd_ops = { - .read = musicpal_lcd_read, - .write = musicpal_lcd_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const GraphicHwOps musicpal_gfx_ops = { - .invalidate = lcd_invalidate, - .gfx_update = lcd_refresh, -}; - -static int musicpal_lcd_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - musicpal_lcd_state *s = MUSICPAL_LCD(dev); - - s->brightness = 7; - - memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_lcd_ops, s, - "musicpal-lcd", MP_LCD_SIZE); - sysbus_init_mmio(sbd, &s->iomem); - - s->con = graphic_console_init(dev, 0, &musicpal_gfx_ops, s); - qemu_console_resize(s->con, 128*3, 64*3); - - qdev_init_gpio_in(dev, musicpal_lcd_gpio_brightness_in, 3); - - return 0; -} - -static const VMStateDescription musicpal_lcd_vmsd = { - .name = "musicpal_lcd", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(brightness, musicpal_lcd_state), - VMSTATE_UINT32(mode, musicpal_lcd_state), - VMSTATE_UINT32(irqctrl, musicpal_lcd_state), - VMSTATE_UINT32(page, musicpal_lcd_state), - VMSTATE_UINT32(page_off, musicpal_lcd_state), - VMSTATE_BUFFER(video_ram, musicpal_lcd_state), - VMSTATE_END_OF_LIST() - } -}; - -static void musicpal_lcd_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = musicpal_lcd_init; - dc->vmsd = &musicpal_lcd_vmsd; -} - -static const TypeInfo musicpal_lcd_info = { - .name = TYPE_MUSICPAL_LCD, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(musicpal_lcd_state), - .class_init = musicpal_lcd_class_init, -}; - -/* PIC register offsets */ -#define MP_PIC_STATUS 0x00 -#define MP_PIC_ENABLE_SET 0x08 -#define MP_PIC_ENABLE_CLR 0x0C - -#define TYPE_MV88W8618_PIC "mv88w8618_pic" -#define MV88W8618_PIC(obj) \ - OBJECT_CHECK(mv88w8618_pic_state, (obj), TYPE_MV88W8618_PIC) - -typedef struct mv88w8618_pic_state { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - uint32_t level; - uint32_t enabled; - qemu_irq parent_irq; -} mv88w8618_pic_state; - -static void mv88w8618_pic_update(mv88w8618_pic_state *s) -{ - qemu_set_irq(s->parent_irq, (s->level & s->enabled)); -} - -static void mv88w8618_pic_set_irq(void *opaque, int irq, int level) -{ - mv88w8618_pic_state *s = opaque; - - if (level) { - s->level |= 1 << irq; - } else { - s->level &= ~(1 << irq); - } - mv88w8618_pic_update(s); -} - -static uint64_t mv88w8618_pic_read(void *opaque, hwaddr offset, - unsigned size) -{ - mv88w8618_pic_state *s = opaque; - - switch (offset) { - case MP_PIC_STATUS: - return s->level & s->enabled; - - default: - return 0; - } -} - -static void mv88w8618_pic_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - mv88w8618_pic_state *s = opaque; - - switch (offset) { - case MP_PIC_ENABLE_SET: - s->enabled |= value; - break; - - case MP_PIC_ENABLE_CLR: - s->enabled &= ~value; - s->level &= ~value; - break; - } - mv88w8618_pic_update(s); -} - -static void mv88w8618_pic_reset(DeviceState *d) -{ - mv88w8618_pic_state *s = MV88W8618_PIC(d); - - s->level = 0; - s->enabled = 0; -} - -static const MemoryRegionOps mv88w8618_pic_ops = { - .read = mv88w8618_pic_read, - .write = mv88w8618_pic_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int mv88w8618_pic_init(SysBusDevice *dev) -{ - mv88w8618_pic_state *s = MV88W8618_PIC(dev); - - qdev_init_gpio_in(DEVICE(dev), mv88w8618_pic_set_irq, 32); - sysbus_init_irq(dev, &s->parent_irq); - memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pic_ops, s, - "musicpal-pic", MP_PIC_SIZE); - sysbus_init_mmio(dev, &s->iomem); - return 0; -} - -static const VMStateDescription mv88w8618_pic_vmsd = { - .name = "mv88w8618_pic", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(level, mv88w8618_pic_state), - VMSTATE_UINT32(enabled, mv88w8618_pic_state), - VMSTATE_END_OF_LIST() - } -}; - -static void mv88w8618_pic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = mv88w8618_pic_init; - dc->reset = mv88w8618_pic_reset; - dc->vmsd = &mv88w8618_pic_vmsd; -} - -static const TypeInfo mv88w8618_pic_info = { - .name = TYPE_MV88W8618_PIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(mv88w8618_pic_state), - .class_init = mv88w8618_pic_class_init, -}; - -/* PIT register offsets */ -#define MP_PIT_TIMER1_LENGTH 0x00 -/* ... */ -#define MP_PIT_TIMER4_LENGTH 0x0C -#define MP_PIT_CONTROL 0x10 -#define MP_PIT_TIMER1_VALUE 0x14 -/* ... */ -#define MP_PIT_TIMER4_VALUE 0x20 -#define MP_BOARD_RESET 0x34 - -/* Magic board reset value (probably some watchdog behind it) */ -#define MP_BOARD_RESET_MAGIC 0x10000 - -typedef struct mv88w8618_timer_state { - ptimer_state *ptimer; - uint32_t limit; - int freq; - qemu_irq irq; -} mv88w8618_timer_state; - -#define TYPE_MV88W8618_PIT "mv88w8618_pit" -#define MV88W8618_PIT(obj) \ - OBJECT_CHECK(mv88w8618_pit_state, (obj), TYPE_MV88W8618_PIT) - -typedef struct mv88w8618_pit_state { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - mv88w8618_timer_state timer[4]; -} mv88w8618_pit_state; - -static void mv88w8618_timer_tick(void *opaque) -{ - mv88w8618_timer_state *s = opaque; - - qemu_irq_raise(s->irq); -} - -static void mv88w8618_timer_init(SysBusDevice *dev, mv88w8618_timer_state *s, - uint32_t freq) -{ - QEMUBH *bh; - - sysbus_init_irq(dev, &s->irq); - s->freq = freq; - - bh = qemu_bh_new(mv88w8618_timer_tick, s); - s->ptimer = ptimer_init(bh); -} - -static uint64_t mv88w8618_pit_read(void *opaque, hwaddr offset, - unsigned size) -{ - mv88w8618_pit_state *s = opaque; - mv88w8618_timer_state *t; - - switch (offset) { - case MP_PIT_TIMER1_VALUE ... MP_PIT_TIMER4_VALUE: - t = &s->timer[(offset-MP_PIT_TIMER1_VALUE) >> 2]; - return ptimer_get_count(t->ptimer); - - default: - return 0; - } -} - -static void mv88w8618_pit_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - mv88w8618_pit_state *s = opaque; - mv88w8618_timer_state *t; - int i; - - switch (offset) { - case MP_PIT_TIMER1_LENGTH ... MP_PIT_TIMER4_LENGTH: - t = &s->timer[offset >> 2]; - t->limit = value; - if (t->limit > 0) { - ptimer_set_limit(t->ptimer, t->limit, 1); - } else { - ptimer_stop(t->ptimer); - } - break; - - case MP_PIT_CONTROL: - for (i = 0; i < 4; i++) { - t = &s->timer[i]; - if (value & 0xf && t->limit > 0) { - ptimer_set_limit(t->ptimer, t->limit, 0); - ptimer_set_freq(t->ptimer, t->freq); - ptimer_run(t->ptimer, 0); - } else { - ptimer_stop(t->ptimer); - } - value >>= 4; - } - break; - - case MP_BOARD_RESET: - if (value == MP_BOARD_RESET_MAGIC) { - qemu_system_reset_request(); - } - break; - } -} - -static void mv88w8618_pit_reset(DeviceState *d) -{ - mv88w8618_pit_state *s = MV88W8618_PIT(d); - int i; - - for (i = 0; i < 4; i++) { - ptimer_stop(s->timer[i].ptimer); - s->timer[i].limit = 0; - } -} - -static const MemoryRegionOps mv88w8618_pit_ops = { - .read = mv88w8618_pit_read, - .write = mv88w8618_pit_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int mv88w8618_pit_init(SysBusDevice *dev) -{ - mv88w8618_pit_state *s = MV88W8618_PIT(dev); - int i; - - /* Letting them all run at 1 MHz is likely just a pragmatic - * simplification. */ - for (i = 0; i < 4; i++) { - mv88w8618_timer_init(dev, &s->timer[i], 1000000); - } - - memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_pit_ops, s, - "musicpal-pit", MP_PIT_SIZE); - sysbus_init_mmio(dev, &s->iomem); - return 0; -} - -static const VMStateDescription mv88w8618_timer_vmsd = { - .name = "timer", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PTIMER(ptimer, mv88w8618_timer_state), - VMSTATE_UINT32(limit, mv88w8618_timer_state), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription mv88w8618_pit_vmsd = { - .name = "mv88w8618_pit", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(timer, mv88w8618_pit_state, 4, 1, - mv88w8618_timer_vmsd, mv88w8618_timer_state), - VMSTATE_END_OF_LIST() - } -}; - -static void mv88w8618_pit_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = mv88w8618_pit_init; - dc->reset = mv88w8618_pit_reset; - dc->vmsd = &mv88w8618_pit_vmsd; -} - -static const TypeInfo mv88w8618_pit_info = { - .name = TYPE_MV88W8618_PIT, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(mv88w8618_pit_state), - .class_init = mv88w8618_pit_class_init, -}; - -/* Flash config register offsets */ -#define MP_FLASHCFG_CFGR0 0x04 - -#define TYPE_MV88W8618_FLASHCFG "mv88w8618_flashcfg" -#define MV88W8618_FLASHCFG(obj) \ - OBJECT_CHECK(mv88w8618_flashcfg_state, (obj), TYPE_MV88W8618_FLASHCFG) - -typedef struct mv88w8618_flashcfg_state { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - uint32_t cfgr0; -} mv88w8618_flashcfg_state; - -static uint64_t mv88w8618_flashcfg_read(void *opaque, - hwaddr offset, - unsigned size) -{ - mv88w8618_flashcfg_state *s = opaque; - - switch (offset) { - case MP_FLASHCFG_CFGR0: - return s->cfgr0; - - default: - return 0; - } -} - -static void mv88w8618_flashcfg_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - mv88w8618_flashcfg_state *s = opaque; - - switch (offset) { - case MP_FLASHCFG_CFGR0: - s->cfgr0 = value; - break; - } -} - -static const MemoryRegionOps mv88w8618_flashcfg_ops = { - .read = mv88w8618_flashcfg_read, - .write = mv88w8618_flashcfg_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int mv88w8618_flashcfg_init(SysBusDevice *dev) -{ - mv88w8618_flashcfg_state *s = MV88W8618_FLASHCFG(dev); - - s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */ - memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_flashcfg_ops, s, - "musicpal-flashcfg", MP_FLASHCFG_SIZE); - sysbus_init_mmio(dev, &s->iomem); - return 0; -} - -static const VMStateDescription mv88w8618_flashcfg_vmsd = { - .name = "mv88w8618_flashcfg", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(cfgr0, mv88w8618_flashcfg_state), - VMSTATE_END_OF_LIST() - } -}; - -static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = mv88w8618_flashcfg_init; - dc->vmsd = &mv88w8618_flashcfg_vmsd; -} - -static const TypeInfo mv88w8618_flashcfg_info = { - .name = TYPE_MV88W8618_FLASHCFG, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(mv88w8618_flashcfg_state), - .class_init = mv88w8618_flashcfg_class_init, -}; - -/* Misc register offsets */ -#define MP_MISC_BOARD_REVISION 0x18 - -#define MP_BOARD_REVISION 0x31 - -typedef struct { - SysBusDevice parent_obj; - MemoryRegion iomem; -} MusicPalMiscState; - -#define TYPE_MUSICPAL_MISC "musicpal-misc" -#define MUSICPAL_MISC(obj) \ - OBJECT_CHECK(MusicPalMiscState, (obj), TYPE_MUSICPAL_MISC) - -static uint64_t musicpal_misc_read(void *opaque, hwaddr offset, - unsigned size) -{ - switch (offset) { - case MP_MISC_BOARD_REVISION: - return MP_BOARD_REVISION; - - default: - return 0; - } -} - -static void musicpal_misc_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ -} - -static const MemoryRegionOps musicpal_misc_ops = { - .read = musicpal_misc_read, - .write = musicpal_misc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void musicpal_misc_init(Object *obj) -{ - SysBusDevice *sd = SYS_BUS_DEVICE(obj); - MusicPalMiscState *s = MUSICPAL_MISC(obj); - - memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_misc_ops, NULL, - "musicpal-misc", MP_MISC_SIZE); - sysbus_init_mmio(sd, &s->iomem); -} - -static const TypeInfo musicpal_misc_info = { - .name = TYPE_MUSICPAL_MISC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_init = musicpal_misc_init, - .instance_size = sizeof(MusicPalMiscState), -}; - -/* WLAN register offsets */ -#define MP_WLAN_MAGIC1 0x11c -#define MP_WLAN_MAGIC2 0x124 - -static uint64_t mv88w8618_wlan_read(void *opaque, hwaddr offset, - unsigned size) -{ - switch (offset) { - /* Workaround to allow loading the binary-only wlandrv.ko crap - * from the original Freecom firmware. */ - case MP_WLAN_MAGIC1: - return ~3; - case MP_WLAN_MAGIC2: - return -1; - - default: - return 0; - } -} - -static void mv88w8618_wlan_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ -} - -static const MemoryRegionOps mv88w8618_wlan_ops = { - .read = mv88w8618_wlan_read, - .write =mv88w8618_wlan_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int mv88w8618_wlan_init(SysBusDevice *dev) -{ - MemoryRegion *iomem = g_new(MemoryRegion, 1); - - memory_region_init_io(iomem, OBJECT(dev), &mv88w8618_wlan_ops, NULL, - "musicpal-wlan", MP_WLAN_SIZE); - sysbus_init_mmio(dev, iomem); - return 0; -} - -/* GPIO register offsets */ -#define MP_GPIO_OE_LO 0x008 -#define MP_GPIO_OUT_LO 0x00c -#define MP_GPIO_IN_LO 0x010 -#define MP_GPIO_IER_LO 0x014 -#define MP_GPIO_IMR_LO 0x018 -#define MP_GPIO_ISR_LO 0x020 -#define MP_GPIO_OE_HI 0x508 -#define MP_GPIO_OUT_HI 0x50c -#define MP_GPIO_IN_HI 0x510 -#define MP_GPIO_IER_HI 0x514 -#define MP_GPIO_IMR_HI 0x518 -#define MP_GPIO_ISR_HI 0x520 - -/* GPIO bits & masks */ -#define MP_GPIO_LCD_BRIGHTNESS 0x00070000 -#define MP_GPIO_I2C_DATA_BIT 29 -#define MP_GPIO_I2C_CLOCK_BIT 30 - -/* LCD brightness bits in GPIO_OE_HI */ -#define MP_OE_LCD_BRIGHTNESS 0x0007 - -#define TYPE_MUSICPAL_GPIO "musicpal_gpio" -#define MUSICPAL_GPIO(obj) \ - OBJECT_CHECK(musicpal_gpio_state, (obj), TYPE_MUSICPAL_GPIO) - -typedef struct musicpal_gpio_state { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - uint32_t lcd_brightness; - uint32_t out_state; - uint32_t in_state; - uint32_t ier; - uint32_t imr; - uint32_t isr; - qemu_irq irq; - qemu_irq out[5]; /* 3 brightness out + 2 lcd (data and clock ) */ -} musicpal_gpio_state; - -static void musicpal_gpio_brightness_update(musicpal_gpio_state *s) { - int i; - uint32_t brightness; - - /* compute brightness ratio */ - switch (s->lcd_brightness) { - case 0x00000007: - brightness = 0; - break; - - case 0x00020000: - brightness = 1; - break; - - case 0x00020001: - brightness = 2; - break; - - case 0x00040000: - brightness = 3; - break; - - case 0x00010006: - brightness = 4; - break; - - case 0x00020005: - brightness = 5; - break; - - case 0x00040003: - brightness = 6; - break; - - case 0x00030004: - default: - brightness = 7; - } - - /* set lcd brightness GPIOs */ - for (i = 0; i <= 2; i++) { - qemu_set_irq(s->out[i], (brightness >> i) & 1); - } -} - -static void musicpal_gpio_pin_event(void *opaque, int pin, int level) -{ - musicpal_gpio_state *s = opaque; - uint32_t mask = 1 << pin; - uint32_t delta = level << pin; - uint32_t old = s->in_state & mask; - - s->in_state &= ~mask; - s->in_state |= delta; - - if ((old ^ delta) && - ((level && (s->imr & mask)) || (!level && (s->ier & mask)))) { - s->isr = mask; - qemu_irq_raise(s->irq); - } -} - -static uint64_t musicpal_gpio_read(void *opaque, hwaddr offset, - unsigned size) -{ - musicpal_gpio_state *s = opaque; - - switch (offset) { - case MP_GPIO_OE_HI: /* used for LCD brightness control */ - return s->lcd_brightness & MP_OE_LCD_BRIGHTNESS; - - case MP_GPIO_OUT_LO: - return s->out_state & 0xFFFF; - case MP_GPIO_OUT_HI: - return s->out_state >> 16; - - case MP_GPIO_IN_LO: - return s->in_state & 0xFFFF; - case MP_GPIO_IN_HI: - return s->in_state >> 16; - - case MP_GPIO_IER_LO: - return s->ier & 0xFFFF; - case MP_GPIO_IER_HI: - return s->ier >> 16; - - case MP_GPIO_IMR_LO: - return s->imr & 0xFFFF; - case MP_GPIO_IMR_HI: - return s->imr >> 16; - - case MP_GPIO_ISR_LO: - return s->isr & 0xFFFF; - case MP_GPIO_ISR_HI: - return s->isr >> 16; - - default: - return 0; - } -} - -static void musicpal_gpio_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - musicpal_gpio_state *s = opaque; - switch (offset) { - case MP_GPIO_OE_HI: /* used for LCD brightness control */ - s->lcd_brightness = (s->lcd_brightness & MP_GPIO_LCD_BRIGHTNESS) | - (value & MP_OE_LCD_BRIGHTNESS); - musicpal_gpio_brightness_update(s); - break; - - case MP_GPIO_OUT_LO: - s->out_state = (s->out_state & 0xFFFF0000) | (value & 0xFFFF); - break; - case MP_GPIO_OUT_HI: - s->out_state = (s->out_state & 0xFFFF) | (value << 16); - s->lcd_brightness = (s->lcd_brightness & 0xFFFF) | - (s->out_state & MP_GPIO_LCD_BRIGHTNESS); - musicpal_gpio_brightness_update(s); - qemu_set_irq(s->out[3], (s->out_state >> MP_GPIO_I2C_DATA_BIT) & 1); - qemu_set_irq(s->out[4], (s->out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1); - break; - - case MP_GPIO_IER_LO: - s->ier = (s->ier & 0xFFFF0000) | (value & 0xFFFF); - break; - case MP_GPIO_IER_HI: - s->ier = (s->ier & 0xFFFF) | (value << 16); - break; - - case MP_GPIO_IMR_LO: - s->imr = (s->imr & 0xFFFF0000) | (value & 0xFFFF); - break; - case MP_GPIO_IMR_HI: - s->imr = (s->imr & 0xFFFF) | (value << 16); - break; - } -} - -static const MemoryRegionOps musicpal_gpio_ops = { - .read = musicpal_gpio_read, - .write = musicpal_gpio_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void musicpal_gpio_reset(DeviceState *d) -{ - musicpal_gpio_state *s = MUSICPAL_GPIO(d); - - s->lcd_brightness = 0; - s->out_state = 0; - s->in_state = 0xffffffff; - s->ier = 0; - s->imr = 0; - s->isr = 0; -} - -static int musicpal_gpio_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - musicpal_gpio_state *s = MUSICPAL_GPIO(dev); - - sysbus_init_irq(sbd, &s->irq); - - memory_region_init_io(&s->iomem, OBJECT(s), &musicpal_gpio_ops, s, - "musicpal-gpio", MP_GPIO_SIZE); - sysbus_init_mmio(sbd, &s->iomem); - - qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out)); - - qdev_init_gpio_in(dev, musicpal_gpio_pin_event, 32); - - return 0; -} - -static const VMStateDescription musicpal_gpio_vmsd = { - .name = "musicpal_gpio", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(lcd_brightness, musicpal_gpio_state), - VMSTATE_UINT32(out_state, musicpal_gpio_state), - VMSTATE_UINT32(in_state, musicpal_gpio_state), - VMSTATE_UINT32(ier, musicpal_gpio_state), - VMSTATE_UINT32(imr, musicpal_gpio_state), - VMSTATE_UINT32(isr, musicpal_gpio_state), - VMSTATE_END_OF_LIST() - } -}; - -static void musicpal_gpio_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = musicpal_gpio_init; - dc->reset = musicpal_gpio_reset; - dc->vmsd = &musicpal_gpio_vmsd; -} - -static const TypeInfo musicpal_gpio_info = { - .name = TYPE_MUSICPAL_GPIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(musicpal_gpio_state), - .class_init = musicpal_gpio_class_init, -}; - -/* Keyboard codes & masks */ -#define KEY_RELEASED 0x80 -#define KEY_CODE 0x7f - -#define KEYCODE_TAB 0x0f -#define KEYCODE_ENTER 0x1c -#define KEYCODE_F 0x21 -#define KEYCODE_M 0x32 - -#define KEYCODE_EXTENDED 0xe0 -#define KEYCODE_UP 0x48 -#define KEYCODE_DOWN 0x50 -#define KEYCODE_LEFT 0x4b -#define KEYCODE_RIGHT 0x4d - -#define MP_KEY_WHEEL_VOL (1 << 0) -#define MP_KEY_WHEEL_VOL_INV (1 << 1) -#define MP_KEY_WHEEL_NAV (1 << 2) -#define MP_KEY_WHEEL_NAV_INV (1 << 3) -#define MP_KEY_BTN_FAVORITS (1 << 4) -#define MP_KEY_BTN_MENU (1 << 5) -#define MP_KEY_BTN_VOLUME (1 << 6) -#define MP_KEY_BTN_NAVIGATION (1 << 7) - -#define TYPE_MUSICPAL_KEY "musicpal_key" -#define MUSICPAL_KEY(obj) \ - OBJECT_CHECK(musicpal_key_state, (obj), TYPE_MUSICPAL_KEY) - -typedef struct musicpal_key_state { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - uint32_t kbd_extended; - uint32_t pressed_keys; - qemu_irq out[8]; -} musicpal_key_state; - -static void musicpal_key_event(void *opaque, int keycode) -{ - musicpal_key_state *s = opaque; - uint32_t event = 0; - int i; - - if (keycode == KEYCODE_EXTENDED) { - s->kbd_extended = 1; - return; - } - - if (s->kbd_extended) { - switch (keycode & KEY_CODE) { - case KEYCODE_UP: - event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV; - break; - - case KEYCODE_DOWN: - event = MP_KEY_WHEEL_NAV; - break; - - case KEYCODE_LEFT: - event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV; - break; - - case KEYCODE_RIGHT: - event = MP_KEY_WHEEL_VOL; - break; - } - } else { - switch (keycode & KEY_CODE) { - case KEYCODE_F: - event = MP_KEY_BTN_FAVORITS; - break; - - case KEYCODE_TAB: - event = MP_KEY_BTN_VOLUME; - break; - - case KEYCODE_ENTER: - event = MP_KEY_BTN_NAVIGATION; - break; - - case KEYCODE_M: - event = MP_KEY_BTN_MENU; - break; - } - /* Do not repeat already pressed buttons */ - if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { - event = 0; - } - } - - if (event) { - /* Raise GPIO pin first if repeating a key */ - if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) { - for (i = 0; i <= 7; i++) { - if (event & (1 << i)) { - qemu_set_irq(s->out[i], 1); - } - } - } - for (i = 0; i <= 7; i++) { - if (event & (1 << i)) { - qemu_set_irq(s->out[i], !!(keycode & KEY_RELEASED)); - } - } - if (keycode & KEY_RELEASED) { - s->pressed_keys &= ~event; - } else { - s->pressed_keys |= event; - } - } - - s->kbd_extended = 0; -} - -static int musicpal_key_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - musicpal_key_state *s = MUSICPAL_KEY(dev); - - memory_region_init(&s->iomem, OBJECT(s), "dummy", 0); - sysbus_init_mmio(sbd, &s->iomem); - - s->kbd_extended = 0; - s->pressed_keys = 0; - - qdev_init_gpio_out(dev, s->out, ARRAY_SIZE(s->out)); - - qemu_add_kbd_event_handler(musicpal_key_event, s); - - return 0; -} - -static const VMStateDescription musicpal_key_vmsd = { - .name = "musicpal_key", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(kbd_extended, musicpal_key_state), - VMSTATE_UINT32(pressed_keys, musicpal_key_state), - VMSTATE_END_OF_LIST() - } -}; - -static void musicpal_key_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = musicpal_key_init; - dc->vmsd = &musicpal_key_vmsd; -} - -static const TypeInfo musicpal_key_info = { - .name = TYPE_MUSICPAL_KEY, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(musicpal_key_state), - .class_init = musicpal_key_class_init, -}; - -static struct arm_boot_info musicpal_binfo = { - .loader_start = 0x0, - .board_id = 0x20e, -}; - -static void musicpal_init(MachineState *machine) -{ - 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; - qemu_irq pic[32]; - DeviceState *dev; - DeviceState *i2c_dev; - DeviceState *lcd_dev; - DeviceState *key_dev; - DeviceState *wm8750_dev; - SysBusDevice *s; - I2CBus *i2c; - int i; - unsigned long flash_size; - DriveInfo *dinfo; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *sram = g_new(MemoryRegion, 1); - - if (!cpu_model) { - cpu_model = "arm926"; - } - cpu = cpu_arm_init(cpu_model); - if (!cpu) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - - /* For now we use a fixed - the original - RAM size */ - memory_region_allocate_system_memory(ram, NULL, "musicpal.ram", - MP_RAM_DEFAULT_SIZE); - memory_region_add_subregion(address_space_mem, 0, ram); - - memory_region_init_ram(sram, NULL, "musicpal.sram", MP_SRAM_SIZE, - &error_fatal); - vmstate_register_ram_global(sram); - memory_region_add_subregion(address_space_mem, MP_SRAM_BASE, sram); - - dev = sysbus_create_simple(TYPE_MV88W8618_PIC, MP_PIC_BASE, - qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ)); - for (i = 0; i < 32; i++) { - pic[i] = qdev_get_gpio_in(dev, i); - } - sysbus_create_varargs(TYPE_MV88W8618_PIT, MP_PIT_BASE, pic[MP_TIMER1_IRQ], - pic[MP_TIMER2_IRQ], pic[MP_TIMER3_IRQ], - pic[MP_TIMER4_IRQ], NULL); - - if (serial_hds[0]) { - serial_mm_init(address_space_mem, MP_UART1_BASE, 2, pic[MP_UART1_IRQ], - 1825000, serial_hds[0], DEVICE_NATIVE_ENDIAN); - } - if (serial_hds[1]) { - serial_mm_init(address_space_mem, MP_UART2_BASE, 2, pic[MP_UART2_IRQ], - 1825000, serial_hds[1], DEVICE_NATIVE_ENDIAN); - } - - /* Register flash */ - dinfo = drive_get(IF_PFLASH, 0, 0); - if (dinfo) { - BlockBackend *blk = blk_by_legacy_dinfo(dinfo); - - flash_size = blk_getlength(blk); - if (flash_size != 8*1024*1024 && flash_size != 16*1024*1024 && - flash_size != 32*1024*1024) { - fprintf(stderr, "Invalid flash image size\n"); - exit(1); - } - - /* - * The original U-Boot accesses the flash at 0xFE000000 instead of - * 0xFF800000 (if there is 8 MB flash). So remap flash access if the - * image is smaller than 32 MB. - */ -#ifdef TARGET_WORDS_BIGENDIAN - pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL, - "musicpal.flash", flash_size, - blk, 0x10000, (flash_size + 0xffff) >> 16, - MP_FLASH_SIZE_MAX / flash_size, - 2, 0x00BF, 0x236D, 0x0000, 0x0000, - 0x5555, 0x2AAA, 1); -#else - pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL, - "musicpal.flash", flash_size, - blk, 0x10000, (flash_size + 0xffff) >> 16, - MP_FLASH_SIZE_MAX / flash_size, - 2, 0x00BF, 0x236D, 0x0000, 0x0000, - 0x5555, 0x2AAA, 0); -#endif - - } - sysbus_create_simple(TYPE_MV88W8618_FLASHCFG, MP_FLASHCFG_BASE, NULL); - - qemu_check_nic_model(&nd_table[0], "mv88w8618"); - dev = qdev_create(NULL, TYPE_MV88W8618_ETH); - qdev_set_nic_properties(dev, &nd_table[0]); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, MP_ETH_BASE); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[MP_ETH_IRQ]); - - sysbus_create_simple("mv88w8618_wlan", MP_WLAN_BASE, NULL); - - sysbus_create_simple(TYPE_MUSICPAL_MISC, MP_MISC_BASE, NULL); - - dev = sysbus_create_simple(TYPE_MUSICPAL_GPIO, MP_GPIO_BASE, - pic[MP_GPIO_IRQ]); - i2c_dev = sysbus_create_simple("gpio_i2c", -1, NULL); - i2c = (I2CBus *)qdev_get_child_bus(i2c_dev, "i2c"); - - lcd_dev = sysbus_create_simple(TYPE_MUSICPAL_LCD, MP_LCD_BASE, NULL); - key_dev = sysbus_create_simple(TYPE_MUSICPAL_KEY, -1, NULL); - - /* I2C read data */ - qdev_connect_gpio_out(i2c_dev, 0, - qdev_get_gpio_in(dev, MP_GPIO_I2C_DATA_BIT)); - /* I2C data */ - qdev_connect_gpio_out(dev, 3, qdev_get_gpio_in(i2c_dev, 0)); - /* I2C clock */ - qdev_connect_gpio_out(dev, 4, qdev_get_gpio_in(i2c_dev, 1)); - - for (i = 0; i < 3; i++) { - qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(lcd_dev, i)); - } - for (i = 0; i < 4; i++) { - qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 8)); - } - for (i = 4; i < 8; i++) { - qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 15)); - } - - wm8750_dev = i2c_create_slave(i2c, "wm8750", MP_WM_ADDR); - dev = qdev_create(NULL, "mv88w8618_audio"); - s = SYS_BUS_DEVICE(dev); - qdev_prop_set_ptr(dev, "wm8750", wm8750_dev); - qdev_init_nofail(dev); - sysbus_mmio_map(s, 0, MP_AUDIO_BASE); - sysbus_connect_irq(s, 0, pic[MP_AUDIO_IRQ]); - - musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE; - musicpal_binfo.kernel_filename = kernel_filename; - musicpal_binfo.kernel_cmdline = kernel_cmdline; - musicpal_binfo.initrd_filename = initrd_filename; - arm_load_kernel(cpu, &musicpal_binfo); -} - -static void musicpal_machine_init(MachineClass *mc) -{ - mc->desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)"; - mc->init = musicpal_init; -} - -DEFINE_MACHINE("musicpal", musicpal_machine_init) - -static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = mv88w8618_wlan_init; -} - -static const TypeInfo mv88w8618_wlan_info = { - .name = "mv88w8618_wlan", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysBusDevice), - .class_init = mv88w8618_wlan_class_init, -}; - -static void musicpal_register_types(void) -{ - type_register_static(&mv88w8618_pic_info); - type_register_static(&mv88w8618_pit_info); - type_register_static(&mv88w8618_flashcfg_info); - type_register_static(&mv88w8618_eth_info); - type_register_static(&mv88w8618_wlan_info); - type_register_static(&musicpal_lcd_info); - type_register_static(&musicpal_gpio_info); - type_register_static(&musicpal_key_info); - type_register_static(&musicpal_misc_info); -} - -type_init(musicpal_register_types) diff --git a/qemu/hw/arm/netduino2.c b/qemu/hw/arm/netduino2.c deleted file mode 100644 index 23d792837..000000000 --- a/qemu/hw/arm/netduino2.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Netduino 2 Machine Model - * - * Copyright (c) 2014 Alistair Francis - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/boards.h" -#include "qemu/error-report.h" -#include "hw/arm/stm32f205_soc.h" - -static void netduino2_init(MachineState *machine) -{ - DeviceState *dev; - - 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", &error_fatal); -} - -static void netduino2_machine_init(MachineClass *mc) -{ - mc->desc = "Netduino 2 Machine"; - mc->init = netduino2_init; -} - -DEFINE_MACHINE("netduino2", netduino2_machine_init) diff --git a/qemu/hw/arm/nseries.c b/qemu/hw/arm/nseries.c deleted file mode 100644 index 538250555..000000000 --- a/qemu/hw/arm/nseries.c +++ /dev/null @@ -1,1454 +0,0 @@ -/* - * Nokia N-series internet tablets. - * - * Copyright (C) 2007 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#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" -#include "hw/irq.h" -#include "ui/console.h" -#include "hw/boards.h" -#include "hw/i2c/i2c.h" -#include "hw/devices.h" -#include "hw/block/flash.h" -#include "hw/hw.h" -#include "hw/bt.h" -#include "hw/loader.h" -#include "sysemu/block-backend.h" -#include "hw/sysbus.h" -#include "exec/address-spaces.h" - -/* Nokia N8x0 support */ -struct n800_s { - struct omap_mpu_state_s *mpu; - - struct rfbi_chip_s blizzard; - struct { - void *opaque; - uint32_t (*txrx)(void *opaque, uint32_t value, int len); - uWireSlave *chip; - } ts; - - int keymap[0x80]; - DeviceState *kbd; - - DeviceState *usb; - void *retu; - void *tahvo; - DeviceState *nand; -}; - -/* GPIO pins */ -#define N8X0_TUSB_ENABLE_GPIO 0 -#define N800_MMC2_WP_GPIO 8 -#define N800_UNKNOWN_GPIO0 9 /* out */ -#define N810_MMC2_VIOSD_GPIO 9 -#define N810_HEADSET_AMP_GPIO 10 -#define N800_CAM_TURN_GPIO 12 -#define N810_GPS_RESET_GPIO 12 -#define N800_BLIZZARD_POWERDOWN_GPIO 15 -#define N800_MMC1_WP_GPIO 23 -#define N810_MMC2_VSD_GPIO 23 -#define N8X0_ONENAND_GPIO 26 -#define N810_BLIZZARD_RESET_GPIO 30 -#define N800_UNKNOWN_GPIO2 53 /* out */ -#define N8X0_TUSB_INT_GPIO 58 -#define N8X0_BT_WKUP_GPIO 61 -#define N8X0_STI_GPIO 62 -#define N8X0_CBUS_SEL_GPIO 64 -#define N8X0_CBUS_DAT_GPIO 65 -#define N8X0_CBUS_CLK_GPIO 66 -#define N8X0_WLAN_IRQ_GPIO 87 -#define N8X0_BT_RESET_GPIO 92 -#define N8X0_TEA5761_CS_GPIO 93 -#define N800_UNKNOWN_GPIO 94 -#define N810_TSC_RESET_GPIO 94 -#define N800_CAM_ACT_GPIO 95 -#define N810_GPS_WAKEUP_GPIO 95 -#define N8X0_MMC_CS_GPIO 96 -#define N8X0_WLAN_PWR_GPIO 97 -#define N8X0_BT_HOST_WKUP_GPIO 98 -#define N810_SPEAKER_AMP_GPIO 101 -#define N810_KB_LOCK_GPIO 102 -#define N800_TSC_TS_GPIO 103 -#define N810_TSC_TS_GPIO 106 -#define N8X0_HEADPHONE_GPIO 107 -#define N8X0_RETU_GPIO 108 -#define N800_TSC_KP_IRQ_GPIO 109 -#define N810_KEYBOARD_GPIO 109 -#define N800_BAT_COVER_GPIO 110 -#define N810_SLIDE_GPIO 110 -#define N8X0_TAHVO_GPIO 111 -#define N800_UNKNOWN_GPIO4 112 /* out */ -#define N810_SLEEPX_LED_GPIO 112 -#define N800_TSC_RESET_GPIO 118 /* ? */ -#define N810_AIC33_RESET_GPIO 118 -#define N800_TSC_UNKNOWN_GPIO 119 /* out */ -#define N8X0_TMP105_GPIO 125 - -/* Config */ -#define BT_UART 0 -#define XLDR_LL_UART 1 - -/* Addresses on the I2C bus 0 */ -#define N810_TLV320AIC33_ADDR 0x18 /* Audio CODEC */ -#define N8X0_TCM825x_ADDR 0x29 /* Camera */ -#define N810_LP5521_ADDR 0x32 /* LEDs */ -#define N810_TSL2563_ADDR 0x3d /* Light sensor */ -#define N810_LM8323_ADDR 0x45 /* Keyboard */ -/* Addresses on the I2C bus 1 */ -#define N8X0_TMP105_ADDR 0x48 /* Temperature sensor */ -#define N8X0_MENELAUS_ADDR 0x72 /* Power management */ - -/* Chipselects on GPMC NOR interface */ -#define N8X0_ONENAND_CS 0 -#define N8X0_USB_ASYNC_CS 1 -#define N8X0_USB_SYNC_CS 4 - -#define N8X0_BD_ADDR 0x00, 0x1a, 0x89, 0x9e, 0x3e, 0x81 - -static void n800_mmc_cs_cb(void *opaque, int line, int level) -{ - /* TODO: this seems to actually be connected to the menelaus, to - * which also both MMC slots connect. */ - omap_mmc_enable((struct omap_mmc_s *) opaque, !level); -} - -static void n8x0_gpio_setup(struct n800_s *s) -{ - qdev_connect_gpio_out(s->mpu->gpio, N8X0_MMC_CS_GPIO, - qemu_allocate_irq(n800_mmc_cs_cb, s->mpu->mmc, 0)); - qemu_irq_lower(qdev_get_gpio_in(s->mpu->gpio, N800_BAT_COVER_GPIO)); -} - -#define MAEMO_CAL_HEADER(...) \ - 'C', 'o', 'n', 'F', 0x02, 0x00, 0x04, 0x00, \ - __VA_ARGS__, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -static const uint8_t n8x0_cal_wlan_mac[] = { - MAEMO_CAL_HEADER('w', 'l', 'a', 'n', '-', 'm', 'a', 'c') - 0x1c, 0x00, 0x00, 0x00, 0x47, 0xd6, 0x69, 0xb3, - 0x30, 0x08, 0xa0, 0x83, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x89, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, -}; - -static const uint8_t n8x0_cal_bt_id[] = { - MAEMO_CAL_HEADER('b', 't', '-', 'i', 'd', 0, 0, 0) - 0x0a, 0x00, 0x00, 0x00, 0xa3, 0x4b, 0xf6, 0x96, - 0xa8, 0xeb, 0xb2, 0x41, 0x00, 0x00, 0x00, 0x00, - N8X0_BD_ADDR, -}; - -static void n8x0_nand_setup(struct n800_s *s) -{ - char *otp_region; - DriveInfo *dinfo; - - s->nand = qdev_create(NULL, "onenand"); - qdev_prop_set_uint16(s->nand, "manufacturer_id", NAND_MFR_SAMSUNG); - /* Either 0x40 or 0x48 are OK for the device ID */ - qdev_prop_set_uint16(s->nand, "device_id", 0x48); - qdev_prop_set_uint16(s->nand, "version_id", 0); - qdev_prop_set_int32(s->nand, "shift", 1); - dinfo = drive_get(IF_MTD, 0, 0); - if (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, - qdev_get_gpio_in(s->mpu->gpio, N8X0_ONENAND_GPIO)); - omap_gpmc_attach(s->mpu->gpmc, N8X0_ONENAND_CS, - sysbus_mmio_get_region(SYS_BUS_DEVICE(s->nand), 0)); - otp_region = onenand_raw_otp(s->nand); - - memcpy(otp_region + 0x000, n8x0_cal_wlan_mac, sizeof(n8x0_cal_wlan_mac)); - memcpy(otp_region + 0x800, n8x0_cal_bt_id, sizeof(n8x0_cal_bt_id)); - /* XXX: in theory should also update the OOB for both pages */ -} - -static qemu_irq n8x0_system_powerdown; - -static void n8x0_powerdown_req(Notifier *n, void *opaque) -{ - qemu_irq_raise(n8x0_system_powerdown); -} - -static Notifier n8x0_system_powerdown_notifier = { - .notify = n8x0_powerdown_req -}; - -static void n8x0_i2c_setup(struct n800_s *s) -{ - DeviceState *dev; - qemu_irq tmp_irq = qdev_get_gpio_in(s->mpu->gpio, N8X0_TMP105_GPIO); - I2CBus *i2c = omap_i2c_bus(s->mpu->i2c[0]); - - /* Attach a menelaus PM chip */ - dev = i2c_create_slave(i2c, "twl92230", N8X0_MENELAUS_ADDR); - qdev_connect_gpio_out(dev, 3, - qdev_get_gpio_in(s->mpu->ih[0], - OMAP_INT_24XX_SYS_NIRQ)); - - n8x0_system_powerdown = qdev_get_gpio_in(dev, 3); - qemu_register_powerdown_notifier(&n8x0_system_powerdown_notifier); - - /* Attach a TMP105 PM chip (A0 wired to ground) */ - dev = i2c_create_slave(i2c, "tmp105", N8X0_TMP105_ADDR); - qdev_connect_gpio_out(dev, 0, tmp_irq); -} - -/* Touchscreen and keypad controller */ -static MouseTransformInfo n800_pointercal = { - .x = 800, - .y = 480, - .a = { 14560, -68, -3455208, -39, -9621, 35152972, 65536 }, -}; - -static MouseTransformInfo n810_pointercal = { - .x = 800, - .y = 480, - .a = { 15041, 148, -4731056, 171, -10238, 35933380, 65536 }, -}; - -#define RETU_KEYCODE 61 /* F3 */ - -static void n800_key_event(void *opaque, int keycode) -{ - struct n800_s *s = (struct n800_s *) opaque; - int code = s->keymap[keycode & 0x7f]; - - if (code == -1) { - if ((keycode & 0x7f) == RETU_KEYCODE) { - retu_key_event(s->retu, !(keycode & 0x80)); - } - return; - } - - tsc210x_key_event(s->ts.chip, code, !(keycode & 0x80)); -} - -static const int n800_keys[16] = { - -1, - 72, /* Up */ - 63, /* Home (F5) */ - -1, - 75, /* Left */ - 28, /* Enter */ - 77, /* Right */ - -1, - 1, /* Cycle (ESC) */ - 80, /* Down */ - 62, /* Menu (F4) */ - -1, - 66, /* Zoom- (F8) */ - 64, /* FullScreen (F6) */ - 65, /* Zoom+ (F7) */ - -1, -}; - -static void n800_tsc_kbd_setup(struct n800_s *s) -{ - int i; - - /* XXX: are the three pins inverted inside the chip between the - * tsc and the cpu (N4111)? */ - qemu_irq penirq = NULL; /* NC */ - qemu_irq kbirq = qdev_get_gpio_in(s->mpu->gpio, N800_TSC_KP_IRQ_GPIO); - qemu_irq dav = qdev_get_gpio_in(s->mpu->gpio, N800_TSC_TS_GPIO); - - s->ts.chip = tsc2301_init(penirq, kbirq, dav); - s->ts.opaque = s->ts.chip->opaque; - s->ts.txrx = tsc210x_txrx; - - for (i = 0; i < 0x80; i++) { - s->keymap[i] = -1; - } - for (i = 0; i < 0x10; i++) { - if (n800_keys[i] >= 0) { - s->keymap[n800_keys[i]] = i; - } - } - - qemu_add_kbd_event_handler(n800_key_event, s); - - tsc210x_set_transform(s->ts.chip, &n800_pointercal); -} - -static void n810_tsc_setup(struct n800_s *s) -{ - qemu_irq pintdav = qdev_get_gpio_in(s->mpu->gpio, N810_TSC_TS_GPIO); - - s->ts.opaque = tsc2005_init(pintdav); - s->ts.txrx = tsc2005_txrx; - - tsc2005_set_transform(s->ts.opaque, &n810_pointercal); -} - -/* N810 Keyboard controller */ -static void n810_key_event(void *opaque, int keycode) -{ - struct n800_s *s = (struct n800_s *) opaque; - int code = s->keymap[keycode & 0x7f]; - - if (code == -1) { - if ((keycode & 0x7f) == RETU_KEYCODE) { - retu_key_event(s->retu, !(keycode & 0x80)); - } - return; - } - - lm832x_key_event(s->kbd, code, !(keycode & 0x80)); -} - -#define M 0 - -static int n810_keys[0x80] = { - [0x01] = 16, /* Q */ - [0x02] = 37, /* K */ - [0x03] = 24, /* O */ - [0x04] = 25, /* P */ - [0x05] = 14, /* Backspace */ - [0x06] = 30, /* A */ - [0x07] = 31, /* S */ - [0x08] = 32, /* D */ - [0x09] = 33, /* F */ - [0x0a] = 34, /* G */ - [0x0b] = 35, /* H */ - [0x0c] = 36, /* J */ - - [0x11] = 17, /* W */ - [0x12] = 62, /* Menu (F4) */ - [0x13] = 38, /* L */ - [0x14] = 40, /* ' (Apostrophe) */ - [0x16] = 44, /* Z */ - [0x17] = 45, /* X */ - [0x18] = 46, /* C */ - [0x19] = 47, /* V */ - [0x1a] = 48, /* B */ - [0x1b] = 49, /* N */ - [0x1c] = 42, /* Shift (Left shift) */ - [0x1f] = 65, /* Zoom+ (F7) */ - - [0x21] = 18, /* E */ - [0x22] = 39, /* ; (Semicolon) */ - [0x23] = 12, /* - (Minus) */ - [0x24] = 13, /* = (Equal) */ - [0x2b] = 56, /* Fn (Left Alt) */ - [0x2c] = 50, /* M */ - [0x2f] = 66, /* Zoom- (F8) */ - - [0x31] = 19, /* R */ - [0x32] = 29 | M, /* Right Ctrl */ - [0x34] = 57, /* Space */ - [0x35] = 51, /* , (Comma) */ - [0x37] = 72 | M, /* Up */ - [0x3c] = 82 | M, /* Compose (Insert) */ - [0x3f] = 64, /* FullScreen (F6) */ - - [0x41] = 20, /* T */ - [0x44] = 52, /* . (Dot) */ - [0x46] = 77 | M, /* Right */ - [0x4f] = 63, /* Home (F5) */ - [0x51] = 21, /* Y */ - [0x53] = 80 | M, /* Down */ - [0x55] = 28, /* Enter */ - [0x5f] = 1, /* Cycle (ESC) */ - - [0x61] = 22, /* U */ - [0x64] = 75 | M, /* Left */ - - [0x71] = 23, /* I */ -#if 0 - [0x75] = 28 | M, /* KP Enter (KP Enter) */ -#else - [0x75] = 15, /* KP Enter (Tab) */ -#endif -}; - -#undef M - -static void n810_kbd_setup(struct n800_s *s) -{ - qemu_irq kbd_irq = qdev_get_gpio_in(s->mpu->gpio, N810_KEYBOARD_GPIO); - int i; - - for (i = 0; i < 0x80; i++) { - s->keymap[i] = -1; - } - for (i = 0; i < 0x80; i++) { - if (n810_keys[i] > 0) { - s->keymap[n810_keys[i]] = i; - } - } - - qemu_add_kbd_event_handler(n810_key_event, s); - - /* Attach the LM8322 keyboard to the I2C bus, - * should happen in n8x0_i2c_setup and s->kbd be initialised here. */ - s->kbd = i2c_create_slave(omap_i2c_bus(s->mpu->i2c[0]), - "lm8323", N810_LM8323_ADDR); - qdev_connect_gpio_out(s->kbd, 0, kbd_irq); -} - -/* LCD MIPI DBI-C controller (URAL) */ -struct mipid_s { - int resp[4]; - int param[4]; - int p; - int pm; - int cmd; - - int sleep; - int booster; - int te; - int selfcheck; - int partial; - int normal; - int vscr; - int invert; - int onoff; - int gamma; - uint32_t id; -}; - -static void mipid_reset(struct mipid_s *s) -{ - s->pm = 0; - s->cmd = 0; - - s->sleep = 1; - s->booster = 0; - s->selfcheck = - (1 << 7) | /* Register loading OK. */ - (1 << 5) | /* The chip is attached. */ - (1 << 4); /* Display glass still in one piece. */ - s->te = 0; - s->partial = 0; - s->normal = 1; - s->vscr = 0; - s->invert = 0; - s->onoff = 1; - s->gamma = 0; -} - -static uint32_t mipid_txrx(void *opaque, uint32_t cmd, int len) -{ - struct mipid_s *s = (struct mipid_s *) opaque; - uint8_t ret; - - if (len > 9) { - hw_error("%s: FIXME: bad SPI word width %i\n", __FUNCTION__, len); - } - - if (s->p >= ARRAY_SIZE(s->resp)) { - ret = 0; - } else { - ret = s->resp[s->p++]; - } - if (s->pm-- > 0) { - s->param[s->pm] = cmd; - } else { - s->cmd = cmd; - } - - switch (s->cmd) { - case 0x00: /* NOP */ - break; - - case 0x01: /* SWRESET */ - mipid_reset(s); - break; - - case 0x02: /* BSTROFF */ - s->booster = 0; - break; - case 0x03: /* BSTRON */ - s->booster = 1; - break; - - case 0x04: /* RDDID */ - s->p = 0; - s->resp[0] = (s->id >> 16) & 0xff; - s->resp[1] = (s->id >> 8) & 0xff; - s->resp[2] = (s->id >> 0) & 0xff; - break; - - case 0x06: /* RD_RED */ - case 0x07: /* RD_GREEN */ - /* XXX the bootloader sometimes issues RD_BLUE meaning RDDID so - * for the bootloader one needs to change this. */ - case 0x08: /* RD_BLUE */ - s->p = 0; - /* TODO: return first pixel components */ - s->resp[0] = 0x01; - break; - - case 0x09: /* RDDST */ - s->p = 0; - s->resp[0] = s->booster << 7; - s->resp[1] = (5 << 4) | (s->partial << 2) | - (s->sleep << 1) | s->normal; - s->resp[2] = (s->vscr << 7) | (s->invert << 5) | - (s->onoff << 2) | (s->te << 1) | (s->gamma >> 2); - s->resp[3] = s->gamma << 6; - break; - - case 0x0a: /* RDDPM */ - s->p = 0; - s->resp[0] = (s->onoff << 2) | (s->normal << 3) | (s->sleep << 4) | - (s->partial << 5) | (s->sleep << 6) | (s->booster << 7); - break; - case 0x0b: /* RDDMADCTR */ - s->p = 0; - s->resp[0] = 0; - break; - case 0x0c: /* RDDCOLMOD */ - s->p = 0; - s->resp[0] = 5; /* 65K colours */ - break; - case 0x0d: /* RDDIM */ - s->p = 0; - s->resp[0] = (s->invert << 5) | (s->vscr << 7) | s->gamma; - break; - case 0x0e: /* RDDSM */ - s->p = 0; - s->resp[0] = s->te << 7; - break; - case 0x0f: /* RDDSDR */ - s->p = 0; - s->resp[0] = s->selfcheck; - break; - - case 0x10: /* SLPIN */ - s->sleep = 1; - break; - case 0x11: /* SLPOUT */ - s->sleep = 0; - s->selfcheck ^= 1 << 6; /* POFF self-diagnosis Ok */ - break; - - case 0x12: /* PTLON */ - s->partial = 1; - s->normal = 0; - s->vscr = 0; - break; - case 0x13: /* NORON */ - s->partial = 0; - s->normal = 1; - s->vscr = 0; - break; - - case 0x20: /* INVOFF */ - s->invert = 0; - break; - case 0x21: /* INVON */ - s->invert = 1; - break; - - case 0x22: /* APOFF */ - case 0x23: /* APON */ - goto bad_cmd; - - case 0x25: /* WRCNTR */ - if (s->pm < 0) { - s->pm = 1; - } - goto bad_cmd; - - case 0x26: /* GAMSET */ - if (!s->pm) { - s->gamma = ctz32(s->param[0] & 0xf); - if (s->gamma == 32) { - s->gamma = -1; /* XXX: should this be 0? */ - } - } else if (s->pm < 0) { - s->pm = 1; - } - break; - - case 0x28: /* DISPOFF */ - s->onoff = 0; - break; - case 0x29: /* DISPON */ - s->onoff = 1; - break; - - case 0x2a: /* CASET */ - case 0x2b: /* RASET */ - case 0x2c: /* RAMWR */ - case 0x2d: /* RGBSET */ - case 0x2e: /* RAMRD */ - case 0x30: /* PTLAR */ - case 0x33: /* SCRLAR */ - goto bad_cmd; - - case 0x34: /* TEOFF */ - s->te = 0; - break; - case 0x35: /* TEON */ - if (!s->pm) { - s->te = 1; - } else if (s->pm < 0) { - s->pm = 1; - } - break; - - case 0x36: /* MADCTR */ - goto bad_cmd; - - case 0x37: /* VSCSAD */ - s->partial = 0; - s->normal = 0; - s->vscr = 1; - break; - - case 0x38: /* IDMOFF */ - case 0x39: /* IDMON */ - case 0x3a: /* COLMOD */ - goto bad_cmd; - - case 0xb0: /* CLKINT / DISCTL */ - case 0xb1: /* CLKEXT */ - if (s->pm < 0) { - s->pm = 2; - } - break; - - case 0xb4: /* FRMSEL */ - break; - - case 0xb5: /* FRM8SEL */ - case 0xb6: /* TMPRNG / INIESC */ - case 0xb7: /* TMPHIS / NOP2 */ - case 0xb8: /* TMPREAD / MADCTL */ - case 0xba: /* DISTCTR */ - case 0xbb: /* EPVOL */ - goto bad_cmd; - - case 0xbd: /* Unknown */ - s->p = 0; - s->resp[0] = 0; - s->resp[1] = 1; - break; - - case 0xc2: /* IFMOD */ - if (s->pm < 0) { - s->pm = 2; - } - break; - - case 0xc6: /* PWRCTL */ - case 0xc7: /* PPWRCTL */ - case 0xd0: /* EPWROUT */ - case 0xd1: /* EPWRIN */ - case 0xd4: /* RDEV */ - case 0xd5: /* RDRR */ - goto bad_cmd; - - case 0xda: /* RDID1 */ - s->p = 0; - s->resp[0] = (s->id >> 16) & 0xff; - break; - case 0xdb: /* RDID2 */ - s->p = 0; - s->resp[0] = (s->id >> 8) & 0xff; - break; - case 0xdc: /* RDID3 */ - s->p = 0; - s->resp[0] = (s->id >> 0) & 0xff; - break; - - default: - bad_cmd: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: unknown command %02x\n", __func__, s->cmd); - break; - } - - return ret; -} - -static void *mipid_init(void) -{ - struct mipid_s *s = (struct mipid_s *) g_malloc0(sizeof(*s)); - - s->id = 0x838f03; - mipid_reset(s); - - return s; -} - -static void n8x0_spi_setup(struct n800_s *s) -{ - void *tsc = s->ts.opaque; - void *mipid = mipid_init(); - - omap_mcspi_attach(s->mpu->mcspi[0], s->ts.txrx, tsc, 0); - omap_mcspi_attach(s->mpu->mcspi[0], mipid_txrx, mipid, 1); -} - -/* This task is normally performed by the bootloader. If we're loading - * a kernel directly, we need to enable the Blizzard ourselves. */ -static void n800_dss_init(struct rfbi_chip_s *chip) -{ - uint8_t *fb_blank; - - chip->write(chip->opaque, 0, 0x2a); /* LCD Width register */ - chip->write(chip->opaque, 1, 0x64); - chip->write(chip->opaque, 0, 0x2c); /* LCD HNDP register */ - chip->write(chip->opaque, 1, 0x1e); - chip->write(chip->opaque, 0, 0x2e); /* LCD Height 0 register */ - chip->write(chip->opaque, 1, 0xe0); - chip->write(chip->opaque, 0, 0x30); /* LCD Height 1 register */ - chip->write(chip->opaque, 1, 0x01); - chip->write(chip->opaque, 0, 0x32); /* LCD VNDP register */ - chip->write(chip->opaque, 1, 0x06); - chip->write(chip->opaque, 0, 0x68); /* Display Mode register */ - chip->write(chip->opaque, 1, 1); /* Enable bit */ - - chip->write(chip->opaque, 0, 0x6c); - chip->write(chip->opaque, 1, 0x00); /* Input X Start Position */ - chip->write(chip->opaque, 1, 0x00); /* Input X Start Position */ - chip->write(chip->opaque, 1, 0x00); /* Input Y Start Position */ - chip->write(chip->opaque, 1, 0x00); /* Input Y Start Position */ - chip->write(chip->opaque, 1, 0x1f); /* Input X End Position */ - chip->write(chip->opaque, 1, 0x03); /* Input X End Position */ - chip->write(chip->opaque, 1, 0xdf); /* Input Y End Position */ - chip->write(chip->opaque, 1, 0x01); /* Input Y End Position */ - chip->write(chip->opaque, 1, 0x00); /* Output X Start Position */ - chip->write(chip->opaque, 1, 0x00); /* Output X Start Position */ - chip->write(chip->opaque, 1, 0x00); /* Output Y Start Position */ - chip->write(chip->opaque, 1, 0x00); /* Output Y Start Position */ - chip->write(chip->opaque, 1, 0x1f); /* Output X End Position */ - chip->write(chip->opaque, 1, 0x03); /* Output X End Position */ - chip->write(chip->opaque, 1, 0xdf); /* Output Y End Position */ - chip->write(chip->opaque, 1, 0x01); /* Output Y End Position */ - chip->write(chip->opaque, 1, 0x01); /* Input Data Format */ - chip->write(chip->opaque, 1, 0x01); /* Data Source Select */ - - fb_blank = memset(g_malloc(800 * 480 * 2), 0xff, 800 * 480 * 2); - /* Display Memory Data Port */ - chip->block(chip->opaque, 1, fb_blank, 800 * 480 * 2, 800); - g_free(fb_blank); -} - -static void n8x0_dss_setup(struct n800_s *s) -{ - s->blizzard.opaque = s1d13745_init(NULL); - s->blizzard.block = s1d13745_write_block; - s->blizzard.write = s1d13745_write; - s->blizzard.read = s1d13745_read; - - omap_rfbi_attach(s->mpu->dss, 0, &s->blizzard); -} - -static void n8x0_cbus_setup(struct n800_s *s) -{ - qemu_irq dat_out = qdev_get_gpio_in(s->mpu->gpio, N8X0_CBUS_DAT_GPIO); - qemu_irq retu_irq = qdev_get_gpio_in(s->mpu->gpio, N8X0_RETU_GPIO); - qemu_irq tahvo_irq = qdev_get_gpio_in(s->mpu->gpio, N8X0_TAHVO_GPIO); - - CBus *cbus = cbus_init(dat_out); - - qdev_connect_gpio_out(s->mpu->gpio, N8X0_CBUS_CLK_GPIO, cbus->clk); - qdev_connect_gpio_out(s->mpu->gpio, N8X0_CBUS_DAT_GPIO, cbus->dat); - qdev_connect_gpio_out(s->mpu->gpio, N8X0_CBUS_SEL_GPIO, cbus->sel); - - cbus_attach(cbus, s->retu = retu_init(retu_irq, 1)); - cbus_attach(cbus, s->tahvo = tahvo_init(tahvo_irq, 1)); -} - -static void n8x0_uart_setup(struct n800_s *s) -{ - CharDriverState *radio = uart_hci_init( - qdev_get_gpio_in(s->mpu->gpio, N8X0_BT_HOST_WKUP_GPIO)); - - qdev_connect_gpio_out(s->mpu->gpio, N8X0_BT_RESET_GPIO, - csrhci_pins_get(radio)[csrhci_pin_reset]); - qdev_connect_gpio_out(s->mpu->gpio, N8X0_BT_WKUP_GPIO, - csrhci_pins_get(radio)[csrhci_pin_wakeup]); - - omap_uart_attach(s->mpu->uart[BT_UART], radio); -} - -static void n8x0_usb_setup(struct n800_s *s) -{ - SysBusDevice *dev; - s->usb = qdev_create(NULL, "tusb6010"); - dev = SYS_BUS_DEVICE(s->usb); - qdev_init_nofail(s->usb); - sysbus_connect_irq(dev, 0, - qdev_get_gpio_in(s->mpu->gpio, N8X0_TUSB_INT_GPIO)); - /* Using the NOR interface */ - omap_gpmc_attach(s->mpu->gpmc, N8X0_USB_ASYNC_CS, - sysbus_mmio_get_region(dev, 0)); - omap_gpmc_attach(s->mpu->gpmc, N8X0_USB_SYNC_CS, - sysbus_mmio_get_region(dev, 1)); - qdev_connect_gpio_out(s->mpu->gpio, N8X0_TUSB_ENABLE_GPIO, - qdev_get_gpio_in(s->usb, 0)); /* tusb_pwr */ -} - -/* Setup done before the main bootloader starts by some early setup code - * - used when we want to run the main bootloader in emulation. This - * isn't documented. */ -static uint32_t n800_pinout[104] = { - 0x080f00d8, 0x00d40808, 0x03080808, 0x080800d0, - 0x00dc0808, 0x0b0f0f00, 0x080800b4, 0x00c00808, - 0x08080808, 0x180800c4, 0x00b80000, 0x08080808, - 0x080800bc, 0x00cc0808, 0x08081818, 0x18180128, - 0x01241800, 0x18181818, 0x000000f0, 0x01300000, - 0x00001b0b, 0x1b0f0138, 0x00e0181b, 0x1b031b0b, - 0x180f0078, 0x00740018, 0x0f0f0f1a, 0x00000080, - 0x007c0000, 0x00000000, 0x00000088, 0x00840000, - 0x00000000, 0x00000094, 0x00980300, 0x0f180003, - 0x0000008c, 0x00900f0f, 0x0f0f1b00, 0x0f00009c, - 0x01140000, 0x1b1b0f18, 0x0818013c, 0x01400008, - 0x00001818, 0x000b0110, 0x010c1800, 0x0b030b0f, - 0x181800f4, 0x00f81818, 0x00000018, 0x000000fc, - 0x00401808, 0x00000000, 0x0f1b0030, 0x003c0008, - 0x00000000, 0x00000038, 0x00340000, 0x00000000, - 0x1a080070, 0x00641a1a, 0x08080808, 0x08080060, - 0x005c0808, 0x08080808, 0x08080058, 0x00540808, - 0x08080808, 0x0808006c, 0x00680808, 0x08080808, - 0x000000a8, 0x00b00000, 0x08080808, 0x000000a0, - 0x00a40000, 0x00000000, 0x08ff0050, 0x004c0808, - 0xffffffff, 0xffff0048, 0x0044ffff, 0xffffffff, - 0x000000ac, 0x01040800, 0x08080b0f, 0x18180100, - 0x01081818, 0x0b0b1808, 0x1a0300e4, 0x012c0b1a, - 0x02020018, 0x0b000134, 0x011c0800, 0x0b1b1b00, - 0x0f0000c8, 0x00ec181b, 0x000f0f02, 0x00180118, - 0x01200000, 0x0f0b1b1b, 0x0f0200e8, 0x0000020b, -}; - -static void n800_setup_nolo_tags(void *sram_base) -{ - int i; - uint32_t *p = sram_base + 0x8000; - uint32_t *v = sram_base + 0xa000; - - memset(p, 0, 0x3000); - - strcpy((void *) (p + 0), "QEMU N800"); - - strcpy((void *) (p + 8), "F5"); - - stl_p(p + 10, 0x04f70000); - strcpy((void *) (p + 9), "RX-34"); - - /* RAM size in MB? */ - stl_p(p + 12, 0x80); - - /* Pointer to the list of tags */ - stl_p(p + 13, OMAP2_SRAM_BASE + 0x9000); - - /* The NOLO tags start here */ - p = sram_base + 0x9000; -#define ADD_TAG(tag, len) \ - stw_p((uint16_t *) p + 0, tag); \ - stw_p((uint16_t *) p + 1, len); p++; \ - stl_p(p++, OMAP2_SRAM_BASE | (((void *) v - sram_base) & 0xffff)); - - /* OMAP STI console? Pin out settings? */ - ADD_TAG(0x6e01, 414); - for (i = 0; i < ARRAY_SIZE(n800_pinout); i++) { - stl_p(v++, n800_pinout[i]); - } - - /* Kernel memsize? */ - ADD_TAG(0x6e05, 1); - stl_p(v++, 2); - - /* NOLO serial console */ - ADD_TAG(0x6e02, 4); - stl_p(v++, XLDR_LL_UART); /* UART number (1 - 3) */ - -#if 0 - /* CBUS settings (Retu/AVilma) */ - ADD_TAG(0x6e03, 6); - stw_p((uint16_t *) v + 0, 65); /* CBUS GPIO0 */ - stw_p((uint16_t *) v + 1, 66); /* CBUS GPIO1 */ - stw_p((uint16_t *) v + 2, 64); /* CBUS GPIO2 */ - v += 2; -#endif - - /* Nokia ASIC BB5 (Retu/Tahvo) */ - ADD_TAG(0x6e0a, 4); - stw_p((uint16_t *) v + 0, 111); /* "Retu" interrupt GPIO */ - stw_p((uint16_t *) v + 1, 108); /* "Tahvo" interrupt GPIO */ - v++; - - /* LCD console? */ - ADD_TAG(0x6e04, 4); - stw_p((uint16_t *) v + 0, 30); /* ??? */ - stw_p((uint16_t *) v + 1, 24); /* ??? */ - v++; - -#if 0 - /* LCD settings */ - ADD_TAG(0x6e06, 2); - stw_p((uint16_t *) (v++), 15); /* ??? */ -#endif - - /* I^2C (Menelaus) */ - ADD_TAG(0x6e07, 4); - stl_p(v++, 0x00720000); /* ??? */ - - /* Unknown */ - ADD_TAG(0x6e0b, 6); - stw_p((uint16_t *) v + 0, 94); /* ??? */ - stw_p((uint16_t *) v + 1, 23); /* ??? */ - stw_p((uint16_t *) v + 2, 0); /* ??? */ - v += 2; - - /* OMAP gpio switch info */ - ADD_TAG(0x6e0c, 80); - strcpy((void *) v, "bat_cover"); v += 3; - stw_p((uint16_t *) v + 0, 110); /* GPIO num ??? */ - stw_p((uint16_t *) v + 1, 1); /* GPIO num ??? */ - v += 2; - strcpy((void *) v, "cam_act"); v += 3; - stw_p((uint16_t *) v + 0, 95); /* GPIO num ??? */ - stw_p((uint16_t *) v + 1, 32); /* GPIO num ??? */ - v += 2; - strcpy((void *) v, "cam_turn"); v += 3; - stw_p((uint16_t *) v + 0, 12); /* GPIO num ??? */ - stw_p((uint16_t *) v + 1, 33); /* GPIO num ??? */ - v += 2; - strcpy((void *) v, "headphone"); v += 3; - stw_p((uint16_t *) v + 0, 107); /* GPIO num ??? */ - stw_p((uint16_t *) v + 1, 17); /* GPIO num ??? */ - v += 2; - - /* Bluetooth */ - ADD_TAG(0x6e0e, 12); - stl_p(v++, 0x5c623d01); /* ??? */ - stl_p(v++, 0x00000201); /* ??? */ - stl_p(v++, 0x00000000); /* ??? */ - - /* CX3110x WLAN settings */ - ADD_TAG(0x6e0f, 8); - stl_p(v++, 0x00610025); /* ??? */ - stl_p(v++, 0xffff0057); /* ??? */ - - /* MMC host settings */ - ADD_TAG(0x6e10, 12); - stl_p(v++, 0xffff000f); /* ??? */ - stl_p(v++, 0xffffffff); /* ??? */ - stl_p(v++, 0x00000060); /* ??? */ - - /* OneNAND chip select */ - ADD_TAG(0x6e11, 10); - stl_p(v++, 0x00000401); /* ??? */ - stl_p(v++, 0x0002003a); /* ??? */ - stl_p(v++, 0x00000002); /* ??? */ - - /* TEA5761 sensor settings */ - ADD_TAG(0x6e12, 2); - stl_p(v++, 93); /* GPIO num ??? */ - -#if 0 - /* Unknown tag */ - ADD_TAG(6e09, 0); - - /* Kernel UART / console */ - ADD_TAG(6e12, 0); -#endif - - /* End of the list */ - stl_p(p++, 0x00000000); - stl_p(p++, 0x00000000); -} - -/* This task is normally performed by the bootloader. If we're loading - * a kernel directly, we need to set up GPMC mappings ourselves. */ -static void n800_gpmc_init(struct n800_s *s) -{ - uint32_t config7 = - (0xf << 8) | /* MASKADDRESS */ - (1 << 6) | /* CSVALID */ - (4 << 0); /* BASEADDRESS */ - - cpu_physical_memory_write(0x6800a078, /* GPMC_CONFIG7_0 */ - &config7, sizeof(config7)); -} - -/* Setup sequence done by the bootloader */ -static void n8x0_boot_init(void *opaque) -{ - struct n800_s *s = (struct n800_s *) opaque; - uint32_t buf; - - /* PRCM setup */ -#define omap_writel(addr, val) \ - buf = (val); \ - cpu_physical_memory_write(addr, &buf, sizeof(buf)) - - omap_writel(0x48008060, 0x41); /* PRCM_CLKSRC_CTRL */ - omap_writel(0x48008070, 1); /* PRCM_CLKOUT_CTRL */ - omap_writel(0x48008078, 0); /* PRCM_CLKEMUL_CTRL */ - omap_writel(0x48008090, 0); /* PRCM_VOLTSETUP */ - omap_writel(0x48008094, 0); /* PRCM_CLKSSETUP */ - omap_writel(0x48008098, 0); /* PRCM_POLCTRL */ - omap_writel(0x48008140, 2); /* CM_CLKSEL_MPU */ - omap_writel(0x48008148, 0); /* CM_CLKSTCTRL_MPU */ - omap_writel(0x48008158, 1); /* RM_RSTST_MPU */ - omap_writel(0x480081c8, 0x15); /* PM_WKDEP_MPU */ - omap_writel(0x480081d4, 0x1d4); /* PM_EVGENCTRL_MPU */ - omap_writel(0x480081d8, 0); /* PM_EVEGENONTIM_MPU */ - omap_writel(0x480081dc, 0); /* PM_EVEGENOFFTIM_MPU */ - omap_writel(0x480081e0, 0xc); /* PM_PWSTCTRL_MPU */ - omap_writel(0x48008200, 0x047e7ff7); /* CM_FCLKEN1_CORE */ - omap_writel(0x48008204, 0x00000004); /* CM_FCLKEN2_CORE */ - omap_writel(0x48008210, 0x047e7ff1); /* CM_ICLKEN1_CORE */ - omap_writel(0x48008214, 0x00000004); /* CM_ICLKEN2_CORE */ - omap_writel(0x4800821c, 0x00000000); /* CM_ICLKEN4_CORE */ - omap_writel(0x48008230, 0); /* CM_AUTOIDLE1_CORE */ - omap_writel(0x48008234, 0); /* CM_AUTOIDLE2_CORE */ - omap_writel(0x48008238, 7); /* CM_AUTOIDLE3_CORE */ - omap_writel(0x4800823c, 0); /* CM_AUTOIDLE4_CORE */ - omap_writel(0x48008240, 0x04360626); /* CM_CLKSEL1_CORE */ - omap_writel(0x48008244, 0x00000014); /* CM_CLKSEL2_CORE */ - omap_writel(0x48008248, 0); /* CM_CLKSTCTRL_CORE */ - omap_writel(0x48008300, 0x00000000); /* CM_FCLKEN_GFX */ - omap_writel(0x48008310, 0x00000000); /* CM_ICLKEN_GFX */ - omap_writel(0x48008340, 0x00000001); /* CM_CLKSEL_GFX */ - omap_writel(0x48008400, 0x00000004); /* CM_FCLKEN_WKUP */ - omap_writel(0x48008410, 0x00000004); /* CM_ICLKEN_WKUP */ - omap_writel(0x48008440, 0x00000000); /* CM_CLKSEL_WKUP */ - omap_writel(0x48008500, 0x000000cf); /* CM_CLKEN_PLL */ - omap_writel(0x48008530, 0x0000000c); /* CM_AUTOIDLE_PLL */ - omap_writel(0x48008540, /* CM_CLKSEL1_PLL */ - (0x78 << 12) | (6 << 8)); - omap_writel(0x48008544, 2); /* CM_CLKSEL2_PLL */ - - /* GPMC setup */ - n800_gpmc_init(s); - - /* Video setup */ - n800_dss_init(&s->blizzard); - - /* CPU setup */ - s->mpu->cpu->env.GE = 0x5; - - /* If the machine has a slided keyboard, open it */ - if (s->kbd) { - qemu_irq_raise(qdev_get_gpio_in(s->mpu->gpio, N810_SLIDE_GPIO)); - } -} - -#define OMAP_TAG_NOKIA_BT 0x4e01 -#define OMAP_TAG_WLAN_CX3110X 0x4e02 -#define OMAP_TAG_CBUS 0x4e03 -#define OMAP_TAG_EM_ASIC_BB5 0x4e04 - -static struct omap_gpiosw_info_s { - const char *name; - int line; - int type; -} n800_gpiosw_info[] = { - { - "bat_cover", N800_BAT_COVER_GPIO, - OMAP_GPIOSW_TYPE_COVER | OMAP_GPIOSW_INVERTED, - }, { - "cam_act", N800_CAM_ACT_GPIO, - OMAP_GPIOSW_TYPE_ACTIVITY, - }, { - "cam_turn", N800_CAM_TURN_GPIO, - OMAP_GPIOSW_TYPE_ACTIVITY | OMAP_GPIOSW_INVERTED, - }, { - "headphone", N8X0_HEADPHONE_GPIO, - OMAP_GPIOSW_TYPE_CONNECTION | OMAP_GPIOSW_INVERTED, - }, - { NULL } -}, n810_gpiosw_info[] = { - { - "gps_reset", N810_GPS_RESET_GPIO, - OMAP_GPIOSW_TYPE_ACTIVITY | OMAP_GPIOSW_OUTPUT, - }, { - "gps_wakeup", N810_GPS_WAKEUP_GPIO, - OMAP_GPIOSW_TYPE_ACTIVITY | OMAP_GPIOSW_OUTPUT, - }, { - "headphone", N8X0_HEADPHONE_GPIO, - OMAP_GPIOSW_TYPE_CONNECTION | OMAP_GPIOSW_INVERTED, - }, { - "kb_lock", N810_KB_LOCK_GPIO, - OMAP_GPIOSW_TYPE_COVER | OMAP_GPIOSW_INVERTED, - }, { - "sleepx_led", N810_SLEEPX_LED_GPIO, - OMAP_GPIOSW_TYPE_ACTIVITY | OMAP_GPIOSW_INVERTED | OMAP_GPIOSW_OUTPUT, - }, { - "slide", N810_SLIDE_GPIO, - OMAP_GPIOSW_TYPE_COVER | OMAP_GPIOSW_INVERTED, - }, - { NULL } -}; - -static struct omap_partition_info_s { - uint32_t offset; - uint32_t size; - int mask; - const char *name; -} n800_part_info[] = { - { 0x00000000, 0x00020000, 0x3, "bootloader" }, - { 0x00020000, 0x00060000, 0x0, "config" }, - { 0x00080000, 0x00200000, 0x0, "kernel" }, - { 0x00280000, 0x00200000, 0x3, "initfs" }, - { 0x00480000, 0x0fb80000, 0x3, "rootfs" }, - - { 0, 0, 0, NULL } -}, n810_part_info[] = { - { 0x00000000, 0x00020000, 0x3, "bootloader" }, - { 0x00020000, 0x00060000, 0x0, "config" }, - { 0x00080000, 0x00220000, 0x0, "kernel" }, - { 0x002a0000, 0x00400000, 0x0, "initfs" }, - { 0x006a0000, 0x0f960000, 0x0, "rootfs" }, - - { 0, 0, 0, NULL } -}; - -static bdaddr_t n8x0_bd_addr = {{ N8X0_BD_ADDR }}; - -static int n8x0_atag_setup(void *p, int model) -{ - uint8_t *b; - uint16_t *w; - uint32_t *l; - struct omap_gpiosw_info_s *gpiosw; - struct omap_partition_info_s *partition; - const char *tag; - - w = p; - - stw_p(w++, OMAP_TAG_UART); /* u16 tag */ - stw_p(w++, 4); /* u16 len */ - stw_p(w++, (1 << 2) | (1 << 1) | (1 << 0)); /* uint enabled_uarts */ - w++; - -#if 0 - stw_p(w++, OMAP_TAG_SERIAL_CONSOLE); /* u16 tag */ - stw_p(w++, 4); /* u16 len */ - stw_p(w++, XLDR_LL_UART + 1); /* u8 console_uart */ - stw_p(w++, 115200); /* u32 console_speed */ -#endif - - stw_p(w++, OMAP_TAG_LCD); /* u16 tag */ - stw_p(w++, 36); /* u16 len */ - strcpy((void *) w, "QEMU LCD panel"); /* char panel_name[16] */ - w += 8; - strcpy((void *) w, "blizzard"); /* char ctrl_name[16] */ - w += 8; - stw_p(w++, N810_BLIZZARD_RESET_GPIO); /* TODO: n800 s16 nreset_gpio */ - stw_p(w++, 24); /* u8 data_lines */ - - stw_p(w++, OMAP_TAG_CBUS); /* u16 tag */ - stw_p(w++, 8); /* u16 len */ - stw_p(w++, N8X0_CBUS_CLK_GPIO); /* s16 clk_gpio */ - stw_p(w++, N8X0_CBUS_DAT_GPIO); /* s16 dat_gpio */ - stw_p(w++, N8X0_CBUS_SEL_GPIO); /* s16 sel_gpio */ - w++; - - stw_p(w++, OMAP_TAG_EM_ASIC_BB5); /* u16 tag */ - stw_p(w++, 4); /* u16 len */ - stw_p(w++, N8X0_RETU_GPIO); /* s16 retu_irq_gpio */ - stw_p(w++, N8X0_TAHVO_GPIO); /* s16 tahvo_irq_gpio */ - - gpiosw = (model == 810) ? n810_gpiosw_info : n800_gpiosw_info; - for (; gpiosw->name; gpiosw++) { - stw_p(w++, OMAP_TAG_GPIO_SWITCH); /* u16 tag */ - stw_p(w++, 20); /* u16 len */ - strcpy((void *) w, gpiosw->name); /* char name[12] */ - w += 6; - stw_p(w++, gpiosw->line); /* u16 gpio */ - stw_p(w++, gpiosw->type); - stw_p(w++, 0); - stw_p(w++, 0); - } - - stw_p(w++, OMAP_TAG_NOKIA_BT); /* u16 tag */ - stw_p(w++, 12); /* u16 len */ - b = (void *) w; - stb_p(b++, 0x01); /* u8 chip_type (CSR) */ - stb_p(b++, N8X0_BT_WKUP_GPIO); /* u8 bt_wakeup_gpio */ - stb_p(b++, N8X0_BT_HOST_WKUP_GPIO); /* u8 host_wakeup_gpio */ - stb_p(b++, N8X0_BT_RESET_GPIO); /* u8 reset_gpio */ - stb_p(b++, BT_UART + 1); /* u8 bt_uart */ - memcpy(b, &n8x0_bd_addr, 6); /* u8 bd_addr[6] */ - b += 6; - stb_p(b++, 0x02); /* u8 bt_sysclk (38.4) */ - w = (void *) b; - - stw_p(w++, OMAP_TAG_WLAN_CX3110X); /* u16 tag */ - stw_p(w++, 8); /* u16 len */ - stw_p(w++, 0x25); /* u8 chip_type */ - stw_p(w++, N8X0_WLAN_PWR_GPIO); /* s16 power_gpio */ - stw_p(w++, N8X0_WLAN_IRQ_GPIO); /* s16 irq_gpio */ - stw_p(w++, -1); /* s16 spi_cs_gpio */ - - stw_p(w++, OMAP_TAG_MMC); /* u16 tag */ - stw_p(w++, 16); /* u16 len */ - if (model == 810) { - stw_p(w++, 0x23f); /* unsigned flags */ - stw_p(w++, -1); /* s16 power_pin */ - stw_p(w++, -1); /* s16 switch_pin */ - stw_p(w++, -1); /* s16 wp_pin */ - stw_p(w++, 0x240); /* unsigned flags */ - stw_p(w++, 0xc000); /* s16 power_pin */ - stw_p(w++, 0x0248); /* s16 switch_pin */ - stw_p(w++, 0xc000); /* s16 wp_pin */ - } else { - stw_p(w++, 0xf); /* unsigned flags */ - stw_p(w++, -1); /* s16 power_pin */ - stw_p(w++, -1); /* s16 switch_pin */ - stw_p(w++, -1); /* s16 wp_pin */ - stw_p(w++, 0); /* unsigned flags */ - stw_p(w++, 0); /* s16 power_pin */ - stw_p(w++, 0); /* s16 switch_pin */ - stw_p(w++, 0); /* s16 wp_pin */ - } - - stw_p(w++, OMAP_TAG_TEA5761); /* u16 tag */ - stw_p(w++, 4); /* u16 len */ - stw_p(w++, N8X0_TEA5761_CS_GPIO); /* u16 enable_gpio */ - w++; - - partition = (model == 810) ? n810_part_info : n800_part_info; - for (; partition->name; partition++) { - stw_p(w++, OMAP_TAG_PARTITION); /* u16 tag */ - stw_p(w++, 28); /* u16 len */ - strcpy((void *) w, partition->name); /* char name[16] */ - l = (void *) (w + 8); - stl_p(l++, partition->size); /* unsigned int size */ - stl_p(l++, partition->offset); /* unsigned int offset */ - stl_p(l++, partition->mask); /* unsigned int mask_flags */ - w = (void *) l; - } - - stw_p(w++, OMAP_TAG_BOOT_REASON); /* u16 tag */ - stw_p(w++, 12); /* u16 len */ -#if 0 - strcpy((void *) w, "por"); /* char reason_str[12] */ - strcpy((void *) w, "charger"); /* char reason_str[12] */ - strcpy((void *) w, "32wd_to"); /* char reason_str[12] */ - strcpy((void *) w, "sw_rst"); /* char reason_str[12] */ - strcpy((void *) w, "mbus"); /* char reason_str[12] */ - strcpy((void *) w, "unknown"); /* char reason_str[12] */ - strcpy((void *) w, "swdg_to"); /* char reason_str[12] */ - strcpy((void *) w, "sec_vio"); /* char reason_str[12] */ - strcpy((void *) w, "pwr_key"); /* char reason_str[12] */ - strcpy((void *) w, "rtc_alarm"); /* char reason_str[12] */ -#else - strcpy((void *) w, "pwr_key"); /* char reason_str[12] */ -#endif - w += 6; - - tag = (model == 810) ? "RX-44" : "RX-34"; - stw_p(w++, OMAP_TAG_VERSION_STR); /* u16 tag */ - stw_p(w++, 24); /* u16 len */ - strcpy((void *) w, "product"); /* char component[12] */ - w += 6; - strcpy((void *) w, tag); /* char version[12] */ - w += 6; - - stw_p(w++, OMAP_TAG_VERSION_STR); /* u16 tag */ - stw_p(w++, 24); /* u16 len */ - strcpy((void *) w, "hw-build"); /* char component[12] */ - w += 6; - strcpy((void *) w, "QEMU "); - pstrcat((void *) w, 12, qemu_hw_version()); /* char version[12] */ - w += 6; - - tag = (model == 810) ? "1.1.10-qemu" : "1.1.6-qemu"; - stw_p(w++, OMAP_TAG_VERSION_STR); /* u16 tag */ - stw_p(w++, 24); /* u16 len */ - strcpy((void *) w, "nolo"); /* char component[12] */ - w += 6; - strcpy((void *) w, tag); /* char version[12] */ - w += 6; - - return (void *) w - p; -} - -static int n800_atag_setup(const struct arm_boot_info *info, void *p) -{ - return n8x0_atag_setup(p, 800); -} - -static int n810_atag_setup(const struct arm_boot_info *info, void *p) -{ - return n8x0_atag_setup(p, 810); -} - -static void n8x0_init(MachineState *machine, - struct arm_boot_info *binfo, int model) -{ - MemoryRegion *sysmem = get_system_memory(); - struct n800_s *s = (struct n800_s *) g_malloc0(sizeof(*s)); - int sdram_size = binfo->ram_size; - - s->mpu = omap2420_mpu_init(sysmem, sdram_size, machine->cpu_model); - - /* Setup peripherals - * - * Believed external peripherals layout in the N810: - * (spi bus 1) - * tsc2005 - * lcd_mipid - * (spi bus 2) - * Conexant cx3110x (WLAN) - * optional: pc2400m (WiMAX) - * (i2c bus 0) - * TLV320AIC33 (audio codec) - * TCM825x (camera by Toshiba) - * lp5521 (clever LEDs) - * tsl2563 (light sensor, hwmon, model 7, rev. 0) - * lm8323 (keypad, manf 00, rev 04) - * (i2c bus 1) - * tmp105 (temperature sensor, hwmon) - * menelaus (pm) - * (somewhere on i2c - maybe N800-only) - * tea5761 (FM tuner) - * (serial 0) - * GPS - * (some serial port) - * csr41814 (Bluetooth) - */ - n8x0_gpio_setup(s); - n8x0_nand_setup(s); - n8x0_i2c_setup(s); - if (model == 800) { - n800_tsc_kbd_setup(s); - } else if (model == 810) { - n810_tsc_setup(s); - n810_kbd_setup(s); - } - n8x0_spi_setup(s); - n8x0_dss_setup(s); - n8x0_cbus_setup(s); - n8x0_uart_setup(s); - if (usb_enabled()) { - n8x0_usb_setup(s); - } - - if (machine->kernel_filename) { - /* Or at the linux loader. */ - binfo->kernel_filename = machine->kernel_filename; - binfo->kernel_cmdline = machine->kernel_cmdline; - binfo->initrd_filename = machine->initrd_filename; - arm_load_kernel(s->mpu->cpu, binfo); - - qemu_register_reset(n8x0_boot_init, s); - } - - if (option_rom[0].name && - (machine->boot_order[0] == 'n' || !machine->kernel_filename)) { - uint8_t nolo_tags[0x10000]; - /* No, wait, better start at the ROM. */ - s->mpu->cpu->env.regs[15] = OMAP2_Q2_BASE + 0x400000; - - /* This is intended for loading the `secondary.bin' program from - * Nokia images (the NOLO bootloader). The entry point seems - * to be at OMAP2_Q2_BASE + 0x400000. - * - * The `2nd.bin' files contain some kind of earlier boot code and - * for them the entry point needs to be set to OMAP2_SRAM_BASE. - * - * The code above is for loading the `zImage' file from Nokia - * images. */ - load_image_targphys(option_rom[0].name, - OMAP2_Q2_BASE + 0x400000, - sdram_size - 0x400000); - - n800_setup_nolo_tags(nolo_tags); - cpu_physical_memory_write(OMAP2_SRAM_BASE, nolo_tags, 0x10000); - } -} - -static struct arm_boot_info n800_binfo = { - .loader_start = OMAP2_Q2_BASE, - /* Actually two chips of 0x4000000 bytes each */ - .ram_size = 0x08000000, - .board_id = 0x4f7, - .atag_board = n800_atag_setup, -}; - -static struct arm_boot_info n810_binfo = { - .loader_start = OMAP2_Q2_BASE, - /* Actually two chips of 0x4000000 bytes each */ - .ram_size = 0x08000000, - /* 0x60c and 0x6bf (WiMAX Edition) have been assigned but are not - * used by some older versions of the bootloader and 5555 is used - * instead (including versions that shipped with many devices). */ - .board_id = 0x60c, - .atag_board = n810_atag_setup, -}; - -static void n800_init(MachineState *machine) -{ - n8x0_init(machine, &n800_binfo, 800); -} - -static void n810_init(MachineState *machine) -{ - n8x0_init(machine, &n810_binfo, 810); -} - -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 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) -{ - type_register_static(&n800_type); - type_register_static(&n810_type); -} - -type_init(nseries_machine_init) diff --git a/qemu/hw/arm/omap1.c b/qemu/hw/arm/omap1.c deleted file mode 100644 index b3cf0ec69..000000000 --- a/qemu/hw/arm/omap1.c +++ /dev/null @@ -1,4086 +0,0 @@ -/* - * TI OMAP processors emulation. - * - * Copyright (C) 2006-2008 Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#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" -#include "hw/arm/omap.h" -#include "sysemu/sysemu.h" -#include "hw/arm/soc_dma.h" -#include "sysemu/block-backend.h" -#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) -{ - uint8_t ret; - - OMAP_8B_REG(addr); - cpu_physical_memory_read(addr, &ret, 1); - return ret; -} - -void omap_badwidth_write8(void *opaque, hwaddr addr, - uint32_t value) -{ - uint8_t val8 = value; - - OMAP_8B_REG(addr); - cpu_physical_memory_write(addr, &val8, 1); -} - -uint32_t omap_badwidth_read16(void *opaque, hwaddr addr) -{ - uint16_t ret; - - OMAP_16B_REG(addr); - cpu_physical_memory_read(addr, &ret, 2); - return ret; -} - -void omap_badwidth_write16(void *opaque, hwaddr addr, - uint32_t value) -{ - uint16_t val16 = value; - - OMAP_16B_REG(addr); - cpu_physical_memory_write(addr, &val16, 2); -} - -uint32_t omap_badwidth_read32(void *opaque, hwaddr addr) -{ - uint32_t ret; - - OMAP_32B_REG(addr); - cpu_physical_memory_read(addr, &ret, 4); - return ret; -} - -void omap_badwidth_write32(void *opaque, hwaddr addr, - uint32_t value) -{ - OMAP_32B_REG(addr); - cpu_physical_memory_write(addr, &value, 4); -} - -/* MPU OS timers */ -struct omap_mpu_timer_s { - MemoryRegion iomem; - qemu_irq irq; - omap_clk clk; - uint32_t val; - int64_t time; - QEMUTimer *timer; - QEMUBH *tick; - int64_t rate; - int it_ena; - - int enable; - int ptv; - int ar; - int st; - uint32_t reset_val; -}; - -static inline uint32_t omap_timer_read(struct omap_mpu_timer_s *timer) -{ - uint64_t distance = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - timer->time; - - if (timer->st && timer->enable && timer->rate) - return timer->val - muldiv64(distance >> (timer->ptv + 1), - timer->rate, NANOSECONDS_PER_SECOND); - else - return timer->val; -} - -static inline void omap_timer_sync(struct omap_mpu_timer_s *timer) -{ - timer->val = omap_timer_read(timer); - timer->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); -} - -static inline void omap_timer_update(struct omap_mpu_timer_s *timer) -{ - int64_t expires; - - if (timer->enable && timer->st && timer->rate) { - timer->val = timer->reset_val; /* Should skip this on clk enable */ - expires = muldiv64((uint64_t) timer->val << (timer->ptv + 1), - 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 - * to make systems like PalmOS run in acceptable time. PalmOS - * 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 > (NANOSECONDS_PER_SECOND >> 10) || timer->ar) { - timer_mod(timer->timer, timer->time + expires); - } else { - qemu_bh_schedule(timer->tick); - } - } else - timer_del(timer->timer); -} - -static void omap_timer_fire(void *opaque) -{ - struct omap_mpu_timer_s *timer = opaque; - - if (!timer->ar) { - timer->val = 0; - timer->st = 0; - } - - if (timer->it_ena) - /* Edge-triggered irq */ - qemu_irq_pulse(timer->irq); -} - -static void omap_timer_tick(void *opaque) -{ - struct omap_mpu_timer_s *timer = (struct omap_mpu_timer_s *) opaque; - - omap_timer_sync(timer); - omap_timer_fire(timer); - omap_timer_update(timer); -} - -static void omap_timer_clk_update(void *opaque, int line, int on) -{ - struct omap_mpu_timer_s *timer = (struct omap_mpu_timer_s *) opaque; - - omap_timer_sync(timer); - timer->rate = on ? omap_clk_getrate(timer->clk) : 0; - omap_timer_update(timer); -} - -static void omap_timer_clk_setup(struct omap_mpu_timer_s *timer) -{ - omap_clk_adduser(timer->clk, - qemu_allocate_irq(omap_timer_clk_update, timer, 0)); - timer->rate = omap_clk_getrate(timer->clk); -} - -static uint64_t omap_mpu_timer_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* CNTL_TIMER */ - return (s->enable << 5) | (s->ptv << 2) | (s->ar << 1) | s->st; - - case 0x04: /* LOAD_TIM */ - break; - - case 0x08: /* READ_TIM */ - return omap_timer_read(s); - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_mpu_timer_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *) opaque; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x00: /* CNTL_TIMER */ - omap_timer_sync(s); - s->enable = (value >> 5) & 1; - s->ptv = (value >> 2) & 7; - s->ar = (value >> 1) & 1; - s->st = value & 1; - omap_timer_update(s); - return; - - case 0x04: /* LOAD_TIM */ - s->reset_val = value; - return; - - case 0x08: /* READ_TIM */ - OMAP_RO_REG(addr); - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_mpu_timer_ops = { - .read = omap_mpu_timer_read, - .write = omap_mpu_timer_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void omap_mpu_timer_reset(struct omap_mpu_timer_s *s) -{ - timer_del(s->timer); - s->enable = 0; - s->reset_val = 31337; - s->val = 0; - s->ptv = 0; - s->ar = 0; - s->st = 0; - s->it_ena = 1; -} - -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 = g_new0(struct omap_mpu_timer_s, 1); - - s->irq = irq; - s->clk = clk; - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, omap_timer_tick, s); - s->tick = qemu_bh_new(omap_timer_fire, s); - omap_mpu_timer_reset(s); - omap_timer_clk_setup(s); - - memory_region_init_io(&s->iomem, NULL, &omap_mpu_timer_ops, s, - "omap-mpu-timer", 0x100); - - memory_region_add_subregion(system_memory, base, &s->iomem); - - return s; -} - -/* Watchdog timer */ -struct omap_watchdog_timer_s { - struct omap_mpu_timer_s timer; - MemoryRegion iomem; - uint8_t last_wr; - int mode; - int free; - int reset; -}; - -static uint64_t omap_wd_timer_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *) opaque; - - if (size != 2) { - return omap_badwidth_read16(opaque, addr); - } - - switch (addr) { - case 0x00: /* CNTL_TIMER */ - return (s->timer.ptv << 9) | (s->timer.ar << 8) | - (s->timer.st << 7) | (s->free << 1); - - case 0x04: /* READ_TIMER */ - return omap_timer_read(&s->timer); - - case 0x08: /* TIMER_MODE */ - return s->mode << 15; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_wd_timer_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *) opaque; - - if (size != 2) { - omap_badwidth_write16(opaque, addr, value); - return; - } - - switch (addr) { - case 0x00: /* CNTL_TIMER */ - omap_timer_sync(&s->timer); - s->timer.ptv = (value >> 9) & 7; - s->timer.ar = (value >> 8) & 1; - s->timer.st = (value >> 7) & 1; - s->free = (value >> 1) & 1; - omap_timer_update(&s->timer); - break; - - case 0x04: /* LOAD_TIMER */ - s->timer.reset_val = value & 0xffff; - break; - - case 0x08: /* TIMER_MODE */ - if (!s->mode && ((value >> 15) & 1)) - omap_clk_get(s->timer.clk); - s->mode |= (value >> 15) & 1; - if (s->last_wr == 0xf5) { - if ((value & 0xff) == 0xa0) { - if (s->mode) { - s->mode = 0; - omap_clk_put(s->timer.clk); - } - } else { - /* XXX: on T|E hardware somehow this has no effect, - * on Zire 71 it works as specified. */ - s->reset = 1; - qemu_system_reset_request(); - } - } - s->last_wr = value & 0xff; - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_wd_timer_ops = { - .read = omap_wd_timer_read, - .write = omap_wd_timer_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_wd_timer_reset(struct omap_watchdog_timer_s *s) -{ - timer_del(s->timer.timer); - if (!s->mode) - omap_clk_get(s->timer.clk); - s->mode = 1; - s->free = 1; - s->reset = 0; - s->timer.enable = 1; - s->timer.it_ena = 1; - s->timer.reset_val = 0xffff; - s->timer.val = 0; - s->timer.st = 0; - s->timer.ptv = 0; - s->timer.ar = 0; - omap_timer_update(&s->timer); -} - -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 = g_new0(struct omap_watchdog_timer_s, 1); - - s->timer.irq = irq; - s->timer.clk = clk; - s->timer.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, omap_timer_tick, &s->timer); - omap_wd_timer_reset(s); - omap_timer_clk_setup(&s->timer); - - memory_region_init_io(&s->iomem, NULL, &omap_wd_timer_ops, s, - "omap-wd-timer", 0x100); - memory_region_add_subregion(memory, base, &s->iomem); - - return s; -} - -/* 32-kHz timer */ -struct omap_32khz_timer_s { - struct omap_mpu_timer_s timer; - MemoryRegion iomem; -}; - -static uint64_t omap_os_timer_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_32khz_timer_s *s = (struct omap_32khz_timer_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (offset) { - case 0x00: /* TVR */ - return s->timer.reset_val; - - case 0x04: /* TCR */ - return omap_timer_read(&s->timer); - - case 0x08: /* CR */ - return (s->timer.ar << 3) | (s->timer.it_ena << 2) | s->timer.st; - - default: - break; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_os_timer_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_32khz_timer_s *s = (struct omap_32khz_timer_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (offset) { - case 0x00: /* TVR */ - s->timer.reset_val = value & 0x00ffffff; - break; - - case 0x04: /* TCR */ - OMAP_RO_REG(addr); - break; - - case 0x08: /* CR */ - s->timer.ar = (value >> 3) & 1; - s->timer.it_ena = (value >> 2) & 1; - if (s->timer.st != (value & 1) || (value & 2)) { - omap_timer_sync(&s->timer); - s->timer.enable = value & 1; - s->timer.st = value & 1; - omap_timer_update(&s->timer); - } - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_os_timer_ops = { - .read = omap_os_timer_read, - .write = omap_os_timer_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_os_timer_reset(struct omap_32khz_timer_s *s) -{ - timer_del(s->timer.timer); - s->timer.enable = 0; - s->timer.it_ena = 0; - s->timer.reset_val = 0x00ffffff; - s->timer.val = 0; - s->timer.st = 0; - s->timer.ptv = 0; - s->timer.ar = 1; -} - -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 = g_new0(struct omap_32khz_timer_s, 1); - - s->timer.irq = irq; - s->timer.clk = clk; - s->timer.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, omap_timer_tick, &s->timer); - omap_os_timer_reset(s); - omap_timer_clk_setup(&s->timer); - - memory_region_init_io(&s->iomem, NULL, &omap_os_timer_ops, s, - "omap-os-timer", 0x800); - memory_region_add_subregion(memory, base, &s->iomem); - - return s; -} - -/* Ultra Low-Power Device Module */ -static uint64_t omap_ulpd_pm_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; - uint16_t ret; - - if (size != 2) { - return omap_badwidth_read16(opaque, addr); - } - - switch (addr) { - case 0x14: /* IT_STATUS */ - ret = s->ulpd_pm_regs[addr >> 2]; - s->ulpd_pm_regs[addr >> 2] = 0; - qemu_irq_lower(qdev_get_gpio_in(s->ih[1], OMAP_INT_GAUGE_32K)); - return ret; - - case 0x18: /* Reserved */ - case 0x1c: /* Reserved */ - case 0x20: /* Reserved */ - case 0x28: /* Reserved */ - case 0x2c: /* Reserved */ - OMAP_BAD_REG(addr); - /* fall through */ - case 0x00: /* COUNTER_32_LSB */ - case 0x04: /* COUNTER_32_MSB */ - case 0x08: /* COUNTER_HIGH_FREQ_LSB */ - case 0x0c: /* COUNTER_HIGH_FREQ_MSB */ - case 0x10: /* GAUGING_CTRL */ - case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */ - case 0x30: /* CLOCK_CTRL */ - case 0x34: /* SOFT_REQ */ - case 0x38: /* COUNTER_32_FIQ */ - case 0x3c: /* DPLL_CTRL */ - case 0x40: /* STATUS_REQ */ - /* XXX: check clk::usecount state for every clock */ - case 0x48: /* LOCL_TIME */ - case 0x4c: /* APLL_CTRL */ - case 0x50: /* POWER_CTRL */ - return s->ulpd_pm_regs[addr >> 2]; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static inline void omap_ulpd_clk_update(struct omap_mpu_state_s *s, - uint16_t diff, uint16_t value) -{ - if (diff & (1 << 4)) /* USB_MCLK_EN */ - omap_clk_onoff(omap_findclk(s, "usb_clk0"), (value >> 4) & 1); - if (diff & (1 << 5)) /* DIS_USB_PVCI_CLK */ - omap_clk_onoff(omap_findclk(s, "usb_w2fc_ck"), (~value >> 5) & 1); -} - -static inline void omap_ulpd_req_update(struct omap_mpu_state_s *s, - uint16_t diff, uint16_t value) -{ - if (diff & (1 << 0)) /* SOFT_DPLL_REQ */ - omap_clk_canidle(omap_findclk(s, "dpll4"), (~value >> 0) & 1); - if (diff & (1 << 1)) /* SOFT_COM_REQ */ - omap_clk_canidle(omap_findclk(s, "com_mclk_out"), (~value >> 1) & 1); - if (diff & (1 << 2)) /* SOFT_SDW_REQ */ - omap_clk_canidle(omap_findclk(s, "bt_mclk_out"), (~value >> 2) & 1); - if (diff & (1 << 3)) /* SOFT_USB_REQ */ - omap_clk_canidle(omap_findclk(s, "usb_clk0"), (~value >> 3) & 1); -} - -static void omap_ulpd_pm_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; - int64_t now, ticks; - int div, mult; - static const int bypass_div[4] = { 1, 2, 4, 4 }; - uint16_t diff; - - if (size != 2) { - omap_badwidth_write16(opaque, addr, value); - return; - } - - switch (addr) { - case 0x00: /* COUNTER_32_LSB */ - case 0x04: /* COUNTER_32_MSB */ - case 0x08: /* COUNTER_HIGH_FREQ_LSB */ - case 0x0c: /* COUNTER_HIGH_FREQ_MSB */ - case 0x14: /* IT_STATUS */ - case 0x40: /* STATUS_REQ */ - OMAP_RO_REG(addr); - break; - - case 0x10: /* GAUGING_CTRL */ - /* Bits 0 and 1 seem to be confused in the OMAP 310 TRM */ - if ((s->ulpd_pm_regs[addr >> 2] ^ value) & 1) { - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - if (value & 1) - s->ulpd_gauge_start = now; - else { - now -= s->ulpd_gauge_start; - - /* 32-kHz ticks */ - 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, 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 */ - s->ulpd_pm_regs[0x14 >> 2] |= 1 << 1; - - s->ulpd_pm_regs[0x14 >> 2] |= 1 << 0; /* IT_GAUGING */ - qemu_irq_raise(qdev_get_gpio_in(s->ih[1], OMAP_INT_GAUGE_32K)); - } - } - s->ulpd_pm_regs[addr >> 2] = value; - break; - - case 0x18: /* Reserved */ - case 0x1c: /* Reserved */ - case 0x20: /* Reserved */ - case 0x28: /* Reserved */ - case 0x2c: /* Reserved */ - OMAP_BAD_REG(addr); - /* fall through */ - case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */ - case 0x38: /* COUNTER_32_FIQ */ - case 0x48: /* LOCL_TIME */ - case 0x50: /* POWER_CTRL */ - s->ulpd_pm_regs[addr >> 2] = value; - break; - - case 0x30: /* CLOCK_CTRL */ - diff = s->ulpd_pm_regs[addr >> 2] ^ value; - s->ulpd_pm_regs[addr >> 2] = value & 0x3f; - omap_ulpd_clk_update(s, diff, value); - break; - - case 0x34: /* SOFT_REQ */ - diff = s->ulpd_pm_regs[addr >> 2] ^ value; - s->ulpd_pm_regs[addr >> 2] = value & 0x1f; - omap_ulpd_req_update(s, diff, value); - break; - - case 0x3c: /* DPLL_CTRL */ - /* XXX: OMAP310 TRM claims bit 3 is PLL_ENABLE, and bit 4 is - * omitted altogether, probably a typo. */ - /* This register has identical semantics with DPLL(1:3) control - * registers, see omap_dpll_write() */ - diff = s->ulpd_pm_regs[addr >> 2] & value; - s->ulpd_pm_regs[addr >> 2] = value & 0x2fff; - if (diff & (0x3ff << 2)) { - if (value & (1 << 4)) { /* PLL_ENABLE */ - div = ((value >> 5) & 3) + 1; /* PLL_DIV */ - mult = MIN((value >> 7) & 0x1f, 1); /* PLL_MULT */ - } else { - div = bypass_div[((value >> 2) & 3)]; /* BYPASS_DIV */ - mult = 1; - } - omap_clk_setrate(omap_findclk(s, "dpll4"), div, mult); - } - - /* Enter the desired mode. */ - s->ulpd_pm_regs[addr >> 2] = - (s->ulpd_pm_regs[addr >> 2] & 0xfffe) | - ((s->ulpd_pm_regs[addr >> 2] >> 4) & 1); - - /* Act as if the lock is restored. */ - s->ulpd_pm_regs[addr >> 2] |= 2; - break; - - case 0x4c: /* APLL_CTRL */ - diff = s->ulpd_pm_regs[addr >> 2] & value; - s->ulpd_pm_regs[addr >> 2] = value & 0xf; - if (diff & (1 << 0)) /* APLL_NDPLL_SWITCH */ - omap_clk_reparent(omap_findclk(s, "ck_48m"), omap_findclk(s, - (value & (1 << 0)) ? "apll" : "dpll4")); - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_ulpd_pm_ops = { - .read = omap_ulpd_pm_read, - .write = omap_ulpd_pm_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_ulpd_pm_reset(struct omap_mpu_state_s *mpu) -{ - mpu->ulpd_pm_regs[0x00 >> 2] = 0x0001; - mpu->ulpd_pm_regs[0x04 >> 2] = 0x0000; - mpu->ulpd_pm_regs[0x08 >> 2] = 0x0001; - mpu->ulpd_pm_regs[0x0c >> 2] = 0x0000; - mpu->ulpd_pm_regs[0x10 >> 2] = 0x0000; - mpu->ulpd_pm_regs[0x18 >> 2] = 0x01; - mpu->ulpd_pm_regs[0x1c >> 2] = 0x01; - mpu->ulpd_pm_regs[0x20 >> 2] = 0x01; - mpu->ulpd_pm_regs[0x24 >> 2] = 0x03ff; - mpu->ulpd_pm_regs[0x28 >> 2] = 0x01; - mpu->ulpd_pm_regs[0x2c >> 2] = 0x01; - omap_ulpd_clk_update(mpu, mpu->ulpd_pm_regs[0x30 >> 2], 0x0000); - mpu->ulpd_pm_regs[0x30 >> 2] = 0x0000; - omap_ulpd_req_update(mpu, mpu->ulpd_pm_regs[0x34 >> 2], 0x0000); - mpu->ulpd_pm_regs[0x34 >> 2] = 0x0000; - mpu->ulpd_pm_regs[0x38 >> 2] = 0x0001; - mpu->ulpd_pm_regs[0x3c >> 2] = 0x2211; - mpu->ulpd_pm_regs[0x40 >> 2] = 0x0000; /* FIXME: dump a real STATUS_REQ */ - mpu->ulpd_pm_regs[0x48 >> 2] = 0x960; - mpu->ulpd_pm_regs[0x4c >> 2] = 0x08; - mpu->ulpd_pm_regs[0x50 >> 2] = 0x08; - omap_clk_setrate(omap_findclk(mpu, "dpll4"), 1, 4); - omap_clk_reparent(omap_findclk(mpu, "ck_48m"), omap_findclk(mpu, "dpll4")); -} - -static void omap_ulpd_pm_init(MemoryRegion *system_memory, - hwaddr base, - struct omap_mpu_state_s *mpu) -{ - memory_region_init_io(&mpu->ulpd_pm_iomem, NULL, &omap_ulpd_pm_ops, mpu, - "omap-ulpd-pm", 0x800); - memory_region_add_subregion(system_memory, base, &mpu->ulpd_pm_iomem); - omap_ulpd_pm_reset(mpu); -} - -/* OMAP Pin Configuration */ -static uint64_t omap_pin_cfg_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* FUNC_MUX_CTRL_0 */ - case 0x04: /* FUNC_MUX_CTRL_1 */ - case 0x08: /* FUNC_MUX_CTRL_2 */ - return s->func_mux_ctrl[addr >> 2]; - - case 0x0c: /* COMP_MODE_CTRL_0 */ - return s->comp_mode_ctrl[0]; - - case 0x10: /* FUNC_MUX_CTRL_3 */ - case 0x14: /* FUNC_MUX_CTRL_4 */ - case 0x18: /* FUNC_MUX_CTRL_5 */ - case 0x1c: /* FUNC_MUX_CTRL_6 */ - case 0x20: /* FUNC_MUX_CTRL_7 */ - case 0x24: /* FUNC_MUX_CTRL_8 */ - case 0x28: /* FUNC_MUX_CTRL_9 */ - case 0x2c: /* FUNC_MUX_CTRL_A */ - case 0x30: /* FUNC_MUX_CTRL_B */ - case 0x34: /* FUNC_MUX_CTRL_C */ - case 0x38: /* FUNC_MUX_CTRL_D */ - return s->func_mux_ctrl[(addr >> 2) - 1]; - - case 0x40: /* PULL_DWN_CTRL_0 */ - case 0x44: /* PULL_DWN_CTRL_1 */ - case 0x48: /* PULL_DWN_CTRL_2 */ - case 0x4c: /* PULL_DWN_CTRL_3 */ - return s->pull_dwn_ctrl[(addr & 0xf) >> 2]; - - case 0x50: /* GATE_INH_CTRL_0 */ - return s->gate_inh_ctrl[0]; - - case 0x60: /* VOLTAGE_CTRL_0 */ - return s->voltage_ctrl[0]; - - case 0x70: /* TEST_DBG_CTRL_0 */ - return s->test_dbg_ctrl[0]; - - case 0x80: /* MOD_CONF_CTRL_0 */ - return s->mod_conf_ctrl[0]; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static inline void omap_pin_funcmux0_update(struct omap_mpu_state_s *s, - uint32_t diff, uint32_t value) -{ - if (s->compat1509) { - if (diff & (1 << 9)) /* BLUETOOTH */ - omap_clk_onoff(omap_findclk(s, "bt_mclk_out"), - (~value >> 9) & 1); - if (diff & (1 << 7)) /* USB.CLKO */ - omap_clk_onoff(omap_findclk(s, "usb.clko"), - (value >> 7) & 1); - } -} - -static inline void omap_pin_funcmux1_update(struct omap_mpu_state_s *s, - uint32_t diff, uint32_t value) -{ - if (s->compat1509) { - if (diff & (1U << 31)) { - /* MCBSP3_CLK_HIZ_DI */ - omap_clk_onoff(omap_findclk(s, "mcbsp3.clkx"), (value >> 31) & 1); - } - if (diff & (1 << 1)) { - /* CLK32K */ - omap_clk_onoff(omap_findclk(s, "clk32k_out"), (~value >> 1) & 1); - } - } -} - -static inline void omap_pin_modconf1_update(struct omap_mpu_state_s *s, - uint32_t diff, uint32_t value) -{ - if (diff & (1U << 31)) { - /* CONF_MOD_UART3_CLK_MODE_R */ - omap_clk_reparent(omap_findclk(s, "uart3_ck"), - omap_findclk(s, ((value >> 31) & 1) ? - "ck_48m" : "armper_ck")); - } - if (diff & (1 << 30)) /* CONF_MOD_UART2_CLK_MODE_R */ - omap_clk_reparent(omap_findclk(s, "uart2_ck"), - omap_findclk(s, ((value >> 30) & 1) ? - "ck_48m" : "armper_ck")); - if (diff & (1 << 29)) /* CONF_MOD_UART1_CLK_MODE_R */ - omap_clk_reparent(omap_findclk(s, "uart1_ck"), - omap_findclk(s, ((value >> 29) & 1) ? - "ck_48m" : "armper_ck")); - if (diff & (1 << 23)) /* CONF_MOD_MMC_SD_CLK_REQ_R */ - omap_clk_reparent(omap_findclk(s, "mmc_ck"), - omap_findclk(s, ((value >> 23) & 1) ? - "ck_48m" : "armper_ck")); - if (diff & (1 << 12)) /* CONF_MOD_COM_MCLK_12_48_S */ - omap_clk_reparent(omap_findclk(s, "com_mclk_out"), - omap_findclk(s, ((value >> 12) & 1) ? - "ck_48m" : "armper_ck")); - if (diff & (1 << 9)) /* CONF_MOD_USB_HOST_HHC_UHO */ - omap_clk_onoff(omap_findclk(s, "usb_hhc_ck"), (value >> 9) & 1); -} - -static void omap_pin_cfg_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; - uint32_t diff; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x00: /* FUNC_MUX_CTRL_0 */ - diff = s->func_mux_ctrl[addr >> 2] ^ value; - s->func_mux_ctrl[addr >> 2] = value; - omap_pin_funcmux0_update(s, diff, value); - return; - - case 0x04: /* FUNC_MUX_CTRL_1 */ - diff = s->func_mux_ctrl[addr >> 2] ^ value; - s->func_mux_ctrl[addr >> 2] = value; - omap_pin_funcmux1_update(s, diff, value); - return; - - case 0x08: /* FUNC_MUX_CTRL_2 */ - s->func_mux_ctrl[addr >> 2] = value; - return; - - case 0x0c: /* COMP_MODE_CTRL_0 */ - s->comp_mode_ctrl[0] = value; - s->compat1509 = (value != 0x0000eaef); - omap_pin_funcmux0_update(s, ~0, s->func_mux_ctrl[0]); - omap_pin_funcmux1_update(s, ~0, s->func_mux_ctrl[1]); - return; - - case 0x10: /* FUNC_MUX_CTRL_3 */ - case 0x14: /* FUNC_MUX_CTRL_4 */ - case 0x18: /* FUNC_MUX_CTRL_5 */ - case 0x1c: /* FUNC_MUX_CTRL_6 */ - case 0x20: /* FUNC_MUX_CTRL_7 */ - case 0x24: /* FUNC_MUX_CTRL_8 */ - case 0x28: /* FUNC_MUX_CTRL_9 */ - case 0x2c: /* FUNC_MUX_CTRL_A */ - case 0x30: /* FUNC_MUX_CTRL_B */ - case 0x34: /* FUNC_MUX_CTRL_C */ - case 0x38: /* FUNC_MUX_CTRL_D */ - s->func_mux_ctrl[(addr >> 2) - 1] = value; - return; - - case 0x40: /* PULL_DWN_CTRL_0 */ - case 0x44: /* PULL_DWN_CTRL_1 */ - case 0x48: /* PULL_DWN_CTRL_2 */ - case 0x4c: /* PULL_DWN_CTRL_3 */ - s->pull_dwn_ctrl[(addr & 0xf) >> 2] = value; - return; - - case 0x50: /* GATE_INH_CTRL_0 */ - s->gate_inh_ctrl[0] = value; - return; - - case 0x60: /* VOLTAGE_CTRL_0 */ - s->voltage_ctrl[0] = value; - return; - - case 0x70: /* TEST_DBG_CTRL_0 */ - s->test_dbg_ctrl[0] = value; - return; - - case 0x80: /* MOD_CONF_CTRL_0 */ - diff = s->mod_conf_ctrl[0] ^ value; - s->mod_conf_ctrl[0] = value; - omap_pin_modconf1_update(s, diff, value); - return; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_pin_cfg_ops = { - .read = omap_pin_cfg_read, - .write = omap_pin_cfg_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_pin_cfg_reset(struct omap_mpu_state_s *mpu) -{ - /* Start in Compatibility Mode. */ - mpu->compat1509 = 1; - omap_pin_funcmux0_update(mpu, mpu->func_mux_ctrl[0], 0); - omap_pin_funcmux1_update(mpu, mpu->func_mux_ctrl[1], 0); - omap_pin_modconf1_update(mpu, mpu->mod_conf_ctrl[0], 0); - memset(mpu->func_mux_ctrl, 0, sizeof(mpu->func_mux_ctrl)); - memset(mpu->comp_mode_ctrl, 0, sizeof(mpu->comp_mode_ctrl)); - memset(mpu->pull_dwn_ctrl, 0, sizeof(mpu->pull_dwn_ctrl)); - memset(mpu->gate_inh_ctrl, 0, sizeof(mpu->gate_inh_ctrl)); - memset(mpu->voltage_ctrl, 0, sizeof(mpu->voltage_ctrl)); - memset(mpu->test_dbg_ctrl, 0, sizeof(mpu->test_dbg_ctrl)); - memset(mpu->mod_conf_ctrl, 0, sizeof(mpu->mod_conf_ctrl)); -} - -static void omap_pin_cfg_init(MemoryRegion *system_memory, - hwaddr base, - struct omap_mpu_state_s *mpu) -{ - memory_region_init_io(&mpu->pin_cfg_iomem, NULL, &omap_pin_cfg_ops, mpu, - "omap-pin-cfg", 0x800); - memory_region_add_subregion(system_memory, base, &mpu->pin_cfg_iomem); - omap_pin_cfg_reset(mpu); -} - -/* Device Identification, Die Identification */ -static uint64_t omap_id_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0xfffe1800: /* DIE_ID_LSB */ - return 0xc9581f0e; - case 0xfffe1804: /* DIE_ID_MSB */ - return 0xa8858bfa; - - case 0xfffe2000: /* PRODUCT_ID_LSB */ - return 0x00aaaafc; - case 0xfffe2004: /* PRODUCT_ID_MSB */ - return 0xcafeb574; - - case 0xfffed400: /* JTAG_ID_LSB */ - switch (s->mpu_model) { - case omap310: - return 0x03310315; - case omap1510: - return 0x03310115; - default: - hw_error("%s: bad mpu model\n", __FUNCTION__); - } - break; - - case 0xfffed404: /* JTAG_ID_MSB */ - switch (s->mpu_model) { - case omap310: - return 0xfb57402f; - case omap1510: - return 0xfb47002f; - default: - hw_error("%s: bad mpu model\n", __FUNCTION__); - } - break; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_id_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - OMAP_BAD_REG(addr); -} - -static const MemoryRegionOps omap_id_ops = { - .read = omap_id_read, - .write = omap_id_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_id_init(MemoryRegion *memory, struct omap_mpu_state_s *mpu) -{ - memory_region_init_io(&mpu->id_iomem, NULL, &omap_id_ops, mpu, - "omap-id", 0x100000000ULL); - memory_region_init_alias(&mpu->id_iomem_e18, NULL, "omap-id-e18", &mpu->id_iomem, - 0xfffe1800, 0x800); - memory_region_add_subregion(memory, 0xfffe1800, &mpu->id_iomem_e18); - memory_region_init_alias(&mpu->id_iomem_ed4, NULL, "omap-id-ed4", &mpu->id_iomem, - 0xfffed400, 0x100); - memory_region_add_subregion(memory, 0xfffed400, &mpu->id_iomem_ed4); - if (!cpu_is_omap15xx(mpu)) { - memory_region_init_alias(&mpu->id_iomem_ed4, NULL, "omap-id-e20", - &mpu->id_iomem, 0xfffe2000, 0x800); - memory_region_add_subregion(memory, 0xfffe2000, &mpu->id_iomem_e20); - } -} - -/* MPUI Control (Dummy) */ -static uint64_t omap_mpui_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* CTRL */ - return s->mpui_ctrl; - case 0x04: /* DEBUG_ADDR */ - return 0x01ffffff; - case 0x08: /* DEBUG_DATA */ - return 0xffffffff; - case 0x0c: /* DEBUG_FLAG */ - return 0x00000800; - case 0x10: /* STATUS */ - return 0x00000000; - - /* Not in OMAP310 */ - case 0x14: /* DSP_STATUS */ - case 0x18: /* DSP_BOOT_CONFIG */ - return 0x00000000; - case 0x1c: /* DSP_MPUI_CONFIG */ - return 0x0000ffff; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_mpui_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x00: /* CTRL */ - s->mpui_ctrl = value & 0x007fffff; - break; - - case 0x04: /* DEBUG_ADDR */ - case 0x08: /* DEBUG_DATA */ - case 0x0c: /* DEBUG_FLAG */ - case 0x10: /* STATUS */ - /* Not in OMAP310 */ - case 0x14: /* DSP_STATUS */ - OMAP_RO_REG(addr); - break; - case 0x18: /* DSP_BOOT_CONFIG */ - case 0x1c: /* DSP_MPUI_CONFIG */ - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_mpui_ops = { - .read = omap_mpui_read, - .write = omap_mpui_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_mpui_reset(struct omap_mpu_state_s *s) -{ - s->mpui_ctrl = 0x0003ff1b; -} - -static void omap_mpui_init(MemoryRegion *memory, hwaddr base, - struct omap_mpu_state_s *mpu) -{ - memory_region_init_io(&mpu->mpui_iomem, NULL, &omap_mpui_ops, mpu, - "omap-mpui", 0x100); - memory_region_add_subregion(memory, base, &mpu->mpui_iomem); - - omap_mpui_reset(mpu); -} - -/* TIPB Bridges */ -struct omap_tipb_bridge_s { - qemu_irq abort; - MemoryRegion iomem; - - int width_intr; - uint16_t control; - uint16_t alloc; - uint16_t buffer; - uint16_t enh_control; -}; - -static uint64_t omap_tipb_bridge_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *) opaque; - - if (size < 2) { - return omap_badwidth_read16(opaque, addr); - } - - switch (addr) { - case 0x00: /* TIPB_CNTL */ - return s->control; - case 0x04: /* TIPB_BUS_ALLOC */ - return s->alloc; - case 0x08: /* MPU_TIPB_CNTL */ - return s->buffer; - case 0x0c: /* ENHANCED_TIPB_CNTL */ - return s->enh_control; - case 0x10: /* ADDRESS_DBG */ - case 0x14: /* DATA_DEBUG_LOW */ - case 0x18: /* DATA_DEBUG_HIGH */ - return 0xffff; - case 0x1c: /* DEBUG_CNTR_SIG */ - return 0x00f8; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_tipb_bridge_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *) opaque; - - if (size < 2) { - omap_badwidth_write16(opaque, addr, value); - return; - } - - switch (addr) { - case 0x00: /* TIPB_CNTL */ - s->control = value & 0xffff; - break; - - case 0x04: /* TIPB_BUS_ALLOC */ - s->alloc = value & 0x003f; - break; - - case 0x08: /* MPU_TIPB_CNTL */ - s->buffer = value & 0x0003; - break; - - case 0x0c: /* ENHANCED_TIPB_CNTL */ - s->width_intr = !(value & 2); - s->enh_control = value & 0x000f; - break; - - case 0x10: /* ADDRESS_DBG */ - case 0x14: /* DATA_DEBUG_LOW */ - case 0x18: /* DATA_DEBUG_HIGH */ - case 0x1c: /* DEBUG_CNTR_SIG */ - OMAP_RO_REG(addr); - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_tipb_bridge_ops = { - .read = omap_tipb_bridge_read, - .write = omap_tipb_bridge_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_tipb_bridge_reset(struct omap_tipb_bridge_s *s) -{ - s->control = 0xffff; - s->alloc = 0x0009; - s->buffer = 0x0000; - s->enh_control = 0x000f; -} - -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 = g_new0(struct omap_tipb_bridge_s, 1); - - s->abort = abort_irq; - omap_tipb_bridge_reset(s); - - memory_region_init_io(&s->iomem, NULL, &omap_tipb_bridge_ops, s, - "omap-tipb-bridge", 0x100); - memory_region_add_subregion(memory, base, &s->iomem); - - return s; -} - -/* Dummy Traffic Controller's Memory Interface */ -static uint64_t omap_tcmi_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; - uint32_t ret; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* IMIF_PRIO */ - case 0x04: /* EMIFS_PRIO */ - case 0x08: /* EMIFF_PRIO */ - case 0x0c: /* EMIFS_CONFIG */ - case 0x10: /* EMIFS_CS0_CONFIG */ - case 0x14: /* EMIFS_CS1_CONFIG */ - case 0x18: /* EMIFS_CS2_CONFIG */ - case 0x1c: /* EMIFS_CS3_CONFIG */ - case 0x24: /* EMIFF_MRS */ - case 0x28: /* TIMEOUT1 */ - case 0x2c: /* TIMEOUT2 */ - case 0x30: /* TIMEOUT3 */ - case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */ - case 0x40: /* EMIFS_CFG_DYN_WAIT */ - return s->tcmi_regs[addr >> 2]; - - case 0x20: /* EMIFF_SDRAM_CONFIG */ - ret = s->tcmi_regs[addr >> 2]; - s->tcmi_regs[addr >> 2] &= ~1; /* XXX: Clear SLRF on SDRAM access */ - /* XXX: We can try using the VGA_DIRTY flag for this */ - return ret; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_tcmi_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x00: /* IMIF_PRIO */ - case 0x04: /* EMIFS_PRIO */ - case 0x08: /* EMIFF_PRIO */ - case 0x10: /* EMIFS_CS0_CONFIG */ - case 0x14: /* EMIFS_CS1_CONFIG */ - case 0x18: /* EMIFS_CS2_CONFIG */ - case 0x1c: /* EMIFS_CS3_CONFIG */ - case 0x20: /* EMIFF_SDRAM_CONFIG */ - case 0x24: /* EMIFF_MRS */ - case 0x28: /* TIMEOUT1 */ - case 0x2c: /* TIMEOUT2 */ - case 0x30: /* TIMEOUT3 */ - case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */ - case 0x40: /* EMIFS_CFG_DYN_WAIT */ - s->tcmi_regs[addr >> 2] = value; - break; - case 0x0c: /* EMIFS_CONFIG */ - s->tcmi_regs[addr >> 2] = (value & 0xf) | (1 << 4); - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_tcmi_ops = { - .read = omap_tcmi_read, - .write = omap_tcmi_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_tcmi_reset(struct omap_mpu_state_s *mpu) -{ - mpu->tcmi_regs[0x00 >> 2] = 0x00000000; - mpu->tcmi_regs[0x04 >> 2] = 0x00000000; - mpu->tcmi_regs[0x08 >> 2] = 0x00000000; - mpu->tcmi_regs[0x0c >> 2] = 0x00000010; - mpu->tcmi_regs[0x10 >> 2] = 0x0010fffb; - mpu->tcmi_regs[0x14 >> 2] = 0x0010fffb; - mpu->tcmi_regs[0x18 >> 2] = 0x0010fffb; - mpu->tcmi_regs[0x1c >> 2] = 0x0010fffb; - mpu->tcmi_regs[0x20 >> 2] = 0x00618800; - mpu->tcmi_regs[0x24 >> 2] = 0x00000037; - mpu->tcmi_regs[0x28 >> 2] = 0x00000000; - mpu->tcmi_regs[0x2c >> 2] = 0x00000000; - mpu->tcmi_regs[0x30 >> 2] = 0x00000000; - mpu->tcmi_regs[0x3c >> 2] = 0x00000003; - mpu->tcmi_regs[0x40 >> 2] = 0x00000000; -} - -static void omap_tcmi_init(MemoryRegion *memory, hwaddr base, - struct omap_mpu_state_s *mpu) -{ - memory_region_init_io(&mpu->tcmi_iomem, NULL, &omap_tcmi_ops, mpu, - "omap-tcmi", 0x100); - memory_region_add_subregion(memory, base, &mpu->tcmi_iomem); - omap_tcmi_reset(mpu); -} - -/* Digital phase-locked loops control */ -struct dpll_ctl_s { - MemoryRegion iomem; - uint16_t mode; - omap_clk dpll; -}; - -static uint64_t omap_dpll_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct dpll_ctl_s *s = (struct dpll_ctl_s *) opaque; - - if (size != 2) { - return omap_badwidth_read16(opaque, addr); - } - - if (addr == 0x00) /* CTL_REG */ - return s->mode; - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_dpll_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct dpll_ctl_s *s = (struct dpll_ctl_s *) opaque; - uint16_t diff; - static const int bypass_div[4] = { 1, 2, 4, 4 }; - int div, mult; - - if (size != 2) { - omap_badwidth_write16(opaque, addr, value); - return; - } - - if (addr == 0x00) { /* CTL_REG */ - /* See omap_ulpd_pm_write() too */ - diff = s->mode & value; - s->mode = value & 0x2fff; - if (diff & (0x3ff << 2)) { - if (value & (1 << 4)) { /* PLL_ENABLE */ - div = ((value >> 5) & 3) + 1; /* PLL_DIV */ - mult = MIN((value >> 7) & 0x1f, 1); /* PLL_MULT */ - } else { - div = bypass_div[((value >> 2) & 3)]; /* BYPASS_DIV */ - mult = 1; - } - omap_clk_setrate(s->dpll, div, mult); - } - - /* Enter the desired mode. */ - s->mode = (s->mode & 0xfffe) | ((s->mode >> 4) & 1); - - /* Act as if the lock is restored. */ - s->mode |= 2; - } else { - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_dpll_ops = { - .read = omap_dpll_read, - .write = omap_dpll_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_dpll_reset(struct dpll_ctl_s *s) -{ - s->mode = 0x2002; - omap_clk_setrate(s->dpll, 1, 1); -} - -static struct dpll_ctl_s *omap_dpll_init(MemoryRegion *memory, - hwaddr base, omap_clk clk) -{ - struct dpll_ctl_s *s = g_malloc0(sizeof(*s)); - memory_region_init_io(&s->iomem, NULL, &omap_dpll_ops, s, "omap-dpll", 0x100); - - s->dpll = clk; - omap_dpll_reset(s); - - memory_region_add_subregion(memory, base, &s->iomem); - return s; -} - -/* MPU Clock/Reset/Power Mode Control */ -static uint64_t omap_clkm_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; - - if (size != 2) { - return omap_badwidth_read16(opaque, addr); - } - - switch (addr) { - case 0x00: /* ARM_CKCTL */ - return s->clkm.arm_ckctl; - - case 0x04: /* ARM_IDLECT1 */ - return s->clkm.arm_idlect1; - - case 0x08: /* ARM_IDLECT2 */ - return s->clkm.arm_idlect2; - - case 0x0c: /* ARM_EWUPCT */ - return s->clkm.arm_ewupct; - - case 0x10: /* ARM_RSTCT1 */ - return s->clkm.arm_rstct1; - - case 0x14: /* ARM_RSTCT2 */ - return s->clkm.arm_rstct2; - - case 0x18: /* ARM_SYSST */ - return (s->clkm.clocking_scheme << 11) | s->clkm.cold_start; - - case 0x1c: /* ARM_CKOUT1 */ - return s->clkm.arm_ckout1; - - case 0x20: /* ARM_CKOUT2 */ - break; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static inline void omap_clkm_ckctl_update(struct omap_mpu_state_s *s, - uint16_t diff, uint16_t value) -{ - omap_clk clk; - - if (diff & (1 << 14)) { /* ARM_INTHCK_SEL */ - if (value & (1 << 14)) - /* Reserved */; - else { - clk = omap_findclk(s, "arminth_ck"); - omap_clk_reparent(clk, omap_findclk(s, "tc_ck")); - } - } - if (diff & (1 << 12)) { /* ARM_TIMXO */ - clk = omap_findclk(s, "armtim_ck"); - if (value & (1 << 12)) - omap_clk_reparent(clk, omap_findclk(s, "clkin")); - else - omap_clk_reparent(clk, omap_findclk(s, "ck_gen1")); - } - /* XXX: en_dspck */ - if (diff & (3 << 10)) { /* DSPMMUDIV */ - clk = omap_findclk(s, "dspmmu_ck"); - omap_clk_setrate(clk, 1 << ((value >> 10) & 3), 1); - } - if (diff & (3 << 8)) { /* TCDIV */ - clk = omap_findclk(s, "tc_ck"); - omap_clk_setrate(clk, 1 << ((value >> 8) & 3), 1); - } - if (diff & (3 << 6)) { /* DSPDIV */ - clk = omap_findclk(s, "dsp_ck"); - omap_clk_setrate(clk, 1 << ((value >> 6) & 3), 1); - } - if (diff & (3 << 4)) { /* ARMDIV */ - clk = omap_findclk(s, "arm_ck"); - omap_clk_setrate(clk, 1 << ((value >> 4) & 3), 1); - } - if (diff & (3 << 2)) { /* LCDDIV */ - clk = omap_findclk(s, "lcd_ck"); - omap_clk_setrate(clk, 1 << ((value >> 2) & 3), 1); - } - if (diff & (3 << 0)) { /* PERDIV */ - clk = omap_findclk(s, "armper_ck"); - omap_clk_setrate(clk, 1 << ((value >> 0) & 3), 1); - } -} - -static inline void omap_clkm_idlect1_update(struct omap_mpu_state_s *s, - uint16_t diff, uint16_t value) -{ - omap_clk clk; - - if (value & (1 << 11)) { /* SETARM_IDLE */ - cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_HALT); - } - if (!(value & (1 << 10))) /* WKUP_MODE */ - qemu_system_shutdown_request(); /* XXX: disable wakeup from IRQ */ - -#define SET_CANIDLE(clock, bit) \ - if (diff & (1 << bit)) { \ - clk = omap_findclk(s, clock); \ - omap_clk_canidle(clk, (value >> bit) & 1); \ - } - SET_CANIDLE("mpuwd_ck", 0) /* IDLWDT_ARM */ - SET_CANIDLE("armxor_ck", 1) /* IDLXORP_ARM */ - SET_CANIDLE("mpuper_ck", 2) /* IDLPER_ARM */ - SET_CANIDLE("lcd_ck", 3) /* IDLLCD_ARM */ - SET_CANIDLE("lb_ck", 4) /* IDLLB_ARM */ - SET_CANIDLE("hsab_ck", 5) /* IDLHSAB_ARM */ - SET_CANIDLE("tipb_ck", 6) /* IDLIF_ARM */ - SET_CANIDLE("dma_ck", 6) /* IDLIF_ARM */ - SET_CANIDLE("tc_ck", 6) /* IDLIF_ARM */ - SET_CANIDLE("dpll1", 7) /* IDLDPLL_ARM */ - SET_CANIDLE("dpll2", 7) /* IDLDPLL_ARM */ - SET_CANIDLE("dpll3", 7) /* IDLDPLL_ARM */ - SET_CANIDLE("mpui_ck", 8) /* IDLAPI_ARM */ - SET_CANIDLE("armtim_ck", 9) /* IDLTIM_ARM */ -} - -static inline void omap_clkm_idlect2_update(struct omap_mpu_state_s *s, - uint16_t diff, uint16_t value) -{ - omap_clk clk; - -#define SET_ONOFF(clock, bit) \ - if (diff & (1 << bit)) { \ - clk = omap_findclk(s, clock); \ - omap_clk_onoff(clk, (value >> bit) & 1); \ - } - SET_ONOFF("mpuwd_ck", 0) /* EN_WDTCK */ - SET_ONOFF("armxor_ck", 1) /* EN_XORPCK */ - SET_ONOFF("mpuper_ck", 2) /* EN_PERCK */ - SET_ONOFF("lcd_ck", 3) /* EN_LCDCK */ - SET_ONOFF("lb_ck", 4) /* EN_LBCK */ - SET_ONOFF("hsab_ck", 5) /* EN_HSABCK */ - SET_ONOFF("mpui_ck", 6) /* EN_APICK */ - SET_ONOFF("armtim_ck", 7) /* EN_TIMCK */ - SET_CANIDLE("dma_ck", 8) /* DMACK_REQ */ - SET_ONOFF("arm_gpio_ck", 9) /* EN_GPIOCK */ - SET_ONOFF("lbfree_ck", 10) /* EN_LBFREECK */ -} - -static inline void omap_clkm_ckout1_update(struct omap_mpu_state_s *s, - uint16_t diff, uint16_t value) -{ - omap_clk clk; - - if (diff & (3 << 4)) { /* TCLKOUT */ - clk = omap_findclk(s, "tclk_out"); - switch ((value >> 4) & 3) { - case 1: - omap_clk_reparent(clk, omap_findclk(s, "ck_gen3")); - omap_clk_onoff(clk, 1); - break; - case 2: - omap_clk_reparent(clk, omap_findclk(s, "tc_ck")); - omap_clk_onoff(clk, 1); - break; - default: - omap_clk_onoff(clk, 0); - } - } - if (diff & (3 << 2)) { /* DCLKOUT */ - clk = omap_findclk(s, "dclk_out"); - switch ((value >> 2) & 3) { - case 0: - omap_clk_reparent(clk, omap_findclk(s, "dspmmu_ck")); - break; - case 1: - omap_clk_reparent(clk, omap_findclk(s, "ck_gen2")); - break; - case 2: - omap_clk_reparent(clk, omap_findclk(s, "dsp_ck")); - break; - case 3: - omap_clk_reparent(clk, omap_findclk(s, "ck_ref14")); - break; - } - } - if (diff & (3 << 0)) { /* ACLKOUT */ - clk = omap_findclk(s, "aclk_out"); - switch ((value >> 0) & 3) { - case 1: - omap_clk_reparent(clk, omap_findclk(s, "ck_gen1")); - omap_clk_onoff(clk, 1); - break; - case 2: - omap_clk_reparent(clk, omap_findclk(s, "arm_ck")); - omap_clk_onoff(clk, 1); - break; - case 3: - omap_clk_reparent(clk, omap_findclk(s, "ck_ref14")); - omap_clk_onoff(clk, 1); - break; - default: - omap_clk_onoff(clk, 0); - } - } -} - -static void omap_clkm_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; - uint16_t diff; - omap_clk clk; - static const char *clkschemename[8] = { - "fully synchronous", "fully asynchronous", "synchronous scalable", - "mix mode 1", "mix mode 2", "bypass mode", "mix mode 3", "mix mode 4", - }; - - if (size != 2) { - omap_badwidth_write16(opaque, addr, value); - return; - } - - switch (addr) { - case 0x00: /* ARM_CKCTL */ - diff = s->clkm.arm_ckctl ^ value; - s->clkm.arm_ckctl = value & 0x7fff; - omap_clkm_ckctl_update(s, diff, value); - return; - - case 0x04: /* ARM_IDLECT1 */ - diff = s->clkm.arm_idlect1 ^ value; - s->clkm.arm_idlect1 = value & 0x0fff; - omap_clkm_idlect1_update(s, diff, value); - return; - - case 0x08: /* ARM_IDLECT2 */ - diff = s->clkm.arm_idlect2 ^ value; - s->clkm.arm_idlect2 = value & 0x07ff; - omap_clkm_idlect2_update(s, diff, value); - return; - - case 0x0c: /* ARM_EWUPCT */ - s->clkm.arm_ewupct = value & 0x003f; - return; - - case 0x10: /* ARM_RSTCT1 */ - diff = s->clkm.arm_rstct1 ^ value; - s->clkm.arm_rstct1 = value & 0x0007; - if (value & 9) { - qemu_system_reset_request(); - s->clkm.cold_start = 0xa; - } - if (diff & ~value & 4) { /* DSP_RST */ - omap_mpui_reset(s); - omap_tipb_bridge_reset(s->private_tipb); - omap_tipb_bridge_reset(s->public_tipb); - } - if (diff & 2) { /* DSP_EN */ - clk = omap_findclk(s, "dsp_ck"); - omap_clk_canidle(clk, (~value >> 1) & 1); - } - return; - - case 0x14: /* ARM_RSTCT2 */ - s->clkm.arm_rstct2 = value & 0x0001; - return; - - case 0x18: /* ARM_SYSST */ - if ((s->clkm.clocking_scheme ^ (value >> 11)) & 7) { - s->clkm.clocking_scheme = (value >> 11) & 7; - printf("%s: clocking scheme set to %s\n", __FUNCTION__, - clkschemename[s->clkm.clocking_scheme]); - } - s->clkm.cold_start &= value & 0x3f; - return; - - case 0x1c: /* ARM_CKOUT1 */ - diff = s->clkm.arm_ckout1 ^ value; - s->clkm.arm_ckout1 = value & 0x003f; - omap_clkm_ckout1_update(s, diff, value); - return; - - case 0x20: /* ARM_CKOUT2 */ - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_clkm_ops = { - .read = omap_clkm_read, - .write = omap_clkm_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t omap_clkdsp_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; - CPUState *cpu = CPU(s->cpu); - - if (size != 2) { - return omap_badwidth_read16(opaque, addr); - } - - switch (addr) { - case 0x04: /* DSP_IDLECT1 */ - return s->clkm.dsp_idlect1; - - case 0x08: /* DSP_IDLECT2 */ - return s->clkm.dsp_idlect2; - - case 0x14: /* DSP_RSTCT2 */ - return s->clkm.dsp_rstct2; - - case 0x18: /* DSP_SYSST */ - cpu = CPU(s->cpu); - return (s->clkm.clocking_scheme << 11) | s->clkm.cold_start | - (cpu->halted << 6); /* Quite useless... */ - } - - OMAP_BAD_REG(addr); - return 0; -} - -static inline void omap_clkdsp_idlect1_update(struct omap_mpu_state_s *s, - uint16_t diff, uint16_t value) -{ - omap_clk clk; - - SET_CANIDLE("dspxor_ck", 1); /* IDLXORP_DSP */ -} - -static inline void omap_clkdsp_idlect2_update(struct omap_mpu_state_s *s, - uint16_t diff, uint16_t value) -{ - omap_clk clk; - - SET_ONOFF("dspxor_ck", 1); /* EN_XORPCK */ -} - -static void omap_clkdsp_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; - uint16_t diff; - - if (size != 2) { - omap_badwidth_write16(opaque, addr, value); - return; - } - - switch (addr) { - case 0x04: /* DSP_IDLECT1 */ - diff = s->clkm.dsp_idlect1 ^ value; - s->clkm.dsp_idlect1 = value & 0x01f7; - omap_clkdsp_idlect1_update(s, diff, value); - break; - - case 0x08: /* DSP_IDLECT2 */ - s->clkm.dsp_idlect2 = value & 0x0037; - diff = s->clkm.dsp_idlect1 ^ value; - omap_clkdsp_idlect2_update(s, diff, value); - break; - - case 0x14: /* DSP_RSTCT2 */ - s->clkm.dsp_rstct2 = value & 0x0001; - break; - - case 0x18: /* DSP_SYSST */ - s->clkm.cold_start &= value & 0x3f; - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_clkdsp_ops = { - .read = omap_clkdsp_read, - .write = omap_clkdsp_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_clkm_reset(struct omap_mpu_state_s *s) -{ - if (s->wdt && s->wdt->reset) - s->clkm.cold_start = 0x6; - s->clkm.clocking_scheme = 0; - omap_clkm_ckctl_update(s, ~0, 0x3000); - s->clkm.arm_ckctl = 0x3000; - omap_clkm_idlect1_update(s, s->clkm.arm_idlect1 ^ 0x0400, 0x0400); - s->clkm.arm_idlect1 = 0x0400; - omap_clkm_idlect2_update(s, s->clkm.arm_idlect2 ^ 0x0100, 0x0100); - s->clkm.arm_idlect2 = 0x0100; - s->clkm.arm_ewupct = 0x003f; - s->clkm.arm_rstct1 = 0x0000; - s->clkm.arm_rstct2 = 0x0000; - s->clkm.arm_ckout1 = 0x0015; - s->clkm.dpll1_mode = 0x2002; - omap_clkdsp_idlect1_update(s, s->clkm.dsp_idlect1 ^ 0x0040, 0x0040); - s->clkm.dsp_idlect1 = 0x0040; - omap_clkdsp_idlect2_update(s, ~0, 0x0000); - s->clkm.dsp_idlect2 = 0x0000; - s->clkm.dsp_rstct2 = 0x0000; -} - -static void omap_clkm_init(MemoryRegion *memory, hwaddr mpu_base, - hwaddr dsp_base, struct omap_mpu_state_s *s) -{ - memory_region_init_io(&s->clkm_iomem, NULL, &omap_clkm_ops, s, - "omap-clkm", 0x100); - memory_region_init_io(&s->clkdsp_iomem, NULL, &omap_clkdsp_ops, s, - "omap-clkdsp", 0x1000); - - s->clkm.arm_idlect1 = 0x03ff; - s->clkm.arm_idlect2 = 0x0100; - s->clkm.dsp_idlect1 = 0x0002; - omap_clkm_reset(s); - s->clkm.cold_start = 0x3a; - - memory_region_add_subregion(memory, mpu_base, &s->clkm_iomem); - memory_region_add_subregion(memory, dsp_base, &s->clkdsp_iomem); -} - -/* MPU I/O */ -struct omap_mpuio_s { - qemu_irq irq; - qemu_irq kbd_irq; - qemu_irq *in; - qemu_irq handler[16]; - qemu_irq wakeup; - MemoryRegion iomem; - - uint16_t inputs; - uint16_t outputs; - uint16_t dir; - uint16_t edge; - uint16_t mask; - uint16_t ints; - - uint16_t debounce; - uint16_t latch; - uint8_t event; - - uint8_t buttons[5]; - uint8_t row_latch; - uint8_t cols; - int kbd_mask; - int clk; -}; - -static void omap_mpuio_set(void *opaque, int line, int level) -{ - struct omap_mpuio_s *s = (struct omap_mpuio_s *) opaque; - uint16_t prev = s->inputs; - - if (level) - s->inputs |= 1 << line; - else - s->inputs &= ~(1 << line); - - if (((1 << line) & s->dir & ~s->mask) && s->clk) { - if ((s->edge & s->inputs & ~prev) | (~s->edge & ~s->inputs & prev)) { - s->ints |= 1 << line; - qemu_irq_raise(s->irq); - /* TODO: wakeup */ - } - if ((s->event & (1 << 0)) && /* SET_GPIO_EVENT_MODE */ - (s->event >> 1) == line) /* PIN_SELECT */ - s->latch = s->inputs; - } -} - -static void omap_mpuio_kbd_update(struct omap_mpuio_s *s) -{ - int i; - uint8_t *row, rows = 0, cols = ~s->cols; - - for (row = s->buttons + 4, i = 1 << 4; i; row --, i >>= 1) - if (*row & cols) - rows |= i; - - qemu_set_irq(s->kbd_irq, rows && !s->kbd_mask && s->clk); - s->row_latch = ~rows; -} - -static uint64_t omap_mpuio_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_mpuio_s *s = (struct omap_mpuio_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - uint16_t ret; - - if (size != 2) { - return omap_badwidth_read16(opaque, addr); - } - - switch (offset) { - case 0x00: /* INPUT_LATCH */ - return s->inputs; - - case 0x04: /* OUTPUT_REG */ - return s->outputs; - - case 0x08: /* IO_CNTL */ - return s->dir; - - case 0x10: /* KBR_LATCH */ - return s->row_latch; - - case 0x14: /* KBC_REG */ - return s->cols; - - case 0x18: /* GPIO_EVENT_MODE_REG */ - return s->event; - - case 0x1c: /* GPIO_INT_EDGE_REG */ - return s->edge; - - case 0x20: /* KBD_INT */ - return (~s->row_latch & 0x1f) && !s->kbd_mask; - - case 0x24: /* GPIO_INT */ - ret = s->ints; - s->ints &= s->mask; - if (ret) - qemu_irq_lower(s->irq); - return ret; - - case 0x28: /* KBD_MASKIT */ - return s->kbd_mask; - - case 0x2c: /* GPIO_MASKIT */ - return s->mask; - - case 0x30: /* GPIO_DEBOUNCING_REG */ - return s->debounce; - - case 0x34: /* GPIO_LATCH_REG */ - return s->latch; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_mpuio_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_mpuio_s *s = (struct omap_mpuio_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - uint16_t diff; - int ln; - - if (size != 2) { - omap_badwidth_write16(opaque, addr, value); - return; - } - - switch (offset) { - case 0x04: /* OUTPUT_REG */ - diff = (s->outputs ^ value) & ~s->dir; - s->outputs = value; - while ((ln = ctz32(diff)) != 32) { - if (s->handler[ln]) - qemu_set_irq(s->handler[ln], (value >> ln) & 1); - diff &= ~(1 << ln); - } - break; - - case 0x08: /* IO_CNTL */ - diff = s->outputs & (s->dir ^ value); - s->dir = value; - - value = s->outputs & ~s->dir; - while ((ln = ctz32(diff)) != 32) { - if (s->handler[ln]) - qemu_set_irq(s->handler[ln], (value >> ln) & 1); - diff &= ~(1 << ln); - } - break; - - case 0x14: /* KBC_REG */ - s->cols = value; - omap_mpuio_kbd_update(s); - break; - - case 0x18: /* GPIO_EVENT_MODE_REG */ - s->event = value & 0x1f; - break; - - case 0x1c: /* GPIO_INT_EDGE_REG */ - s->edge = value; - break; - - case 0x28: /* KBD_MASKIT */ - s->kbd_mask = value & 1; - omap_mpuio_kbd_update(s); - break; - - case 0x2c: /* GPIO_MASKIT */ - s->mask = value; - break; - - case 0x30: /* GPIO_DEBOUNCING_REG */ - s->debounce = value & 0x1ff; - break; - - case 0x00: /* INPUT_LATCH */ - case 0x10: /* KBR_LATCH */ - case 0x20: /* KBD_INT */ - case 0x24: /* GPIO_INT */ - case 0x34: /* GPIO_LATCH_REG */ - OMAP_RO_REG(addr); - return; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_mpuio_ops = { - .read = omap_mpuio_read, - .write = omap_mpuio_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_mpuio_reset(struct omap_mpuio_s *s) -{ - s->inputs = 0; - s->outputs = 0; - s->dir = ~0; - s->event = 0; - s->edge = 0; - s->kbd_mask = 0; - s->mask = 0; - s->debounce = 0; - s->latch = 0; - s->ints = 0; - s->row_latch = 0x1f; - s->clk = 1; -} - -static void omap_mpuio_onoff(void *opaque, int line, int on) -{ - struct omap_mpuio_s *s = (struct omap_mpuio_s *) opaque; - - s->clk = on; - if (on) - omap_mpuio_kbd_update(s); -} - -static struct omap_mpuio_s *omap_mpuio_init(MemoryRegion *memory, - hwaddr base, - qemu_irq kbd_int, qemu_irq gpio_int, qemu_irq wakeup, - omap_clk clk) -{ - struct omap_mpuio_s *s = g_new0(struct omap_mpuio_s, 1); - - s->irq = gpio_int; - s->kbd_irq = kbd_int; - s->wakeup = wakeup; - s->in = qemu_allocate_irqs(omap_mpuio_set, s, 16); - omap_mpuio_reset(s); - - memory_region_init_io(&s->iomem, NULL, &omap_mpuio_ops, s, - "omap-mpuio", 0x800); - memory_region_add_subregion(memory, base, &s->iomem); - - omap_clk_adduser(clk, qemu_allocate_irq(omap_mpuio_onoff, s, 0)); - - return s; -} - -qemu_irq *omap_mpuio_in_get(struct omap_mpuio_s *s) -{ - return s->in; -} - -void omap_mpuio_out_set(struct omap_mpuio_s *s, int line, qemu_irq handler) -{ - if (line >= 16 || line < 0) - hw_error("%s: No GPIO line %i\n", __FUNCTION__, line); - s->handler[line] = handler; -} - -void omap_mpuio_key(struct omap_mpuio_s *s, int row, int col, int down) -{ - if (row >= 5 || row < 0) - hw_error("%s: No key %i-%i\n", __FUNCTION__, col, row); - - if (down) - s->buttons[row] |= 1 << col; - else - s->buttons[row] &= ~(1 << col); - - omap_mpuio_kbd_update(s); -} - -/* MicroWire Interface */ -struct omap_uwire_s { - MemoryRegion iomem; - qemu_irq txirq; - qemu_irq rxirq; - qemu_irq txdrq; - - uint16_t txbuf; - uint16_t rxbuf; - uint16_t control; - uint16_t setup[5]; - - uWireSlave *chip[4]; -}; - -static void omap_uwire_transfer_start(struct omap_uwire_s *s) -{ - int chipselect = (s->control >> 10) & 3; /* INDEX */ - uWireSlave *slave = s->chip[chipselect]; - - if ((s->control >> 5) & 0x1f) { /* NB_BITS_WR */ - if (s->control & (1 << 12)) /* CS_CMD */ - if (slave && slave->send) - slave->send(slave->opaque, - s->txbuf >> (16 - ((s->control >> 5) & 0x1f))); - s->control &= ~(1 << 14); /* CSRB */ - /* TODO: depending on s->setup[4] bits [1:0] assert an IRQ or - * a DRQ. When is the level IRQ supposed to be reset? */ - } - - if ((s->control >> 0) & 0x1f) { /* NB_BITS_RD */ - if (s->control & (1 << 12)) /* CS_CMD */ - if (slave && slave->receive) - s->rxbuf = slave->receive(slave->opaque); - s->control |= 1 << 15; /* RDRB */ - /* TODO: depending on s->setup[4] bits [1:0] assert an IRQ or - * a DRQ. When is the level IRQ supposed to be reset? */ - } -} - -static uint64_t omap_uwire_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_uwire_s *s = (struct omap_uwire_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - - if (size != 2) { - return omap_badwidth_read16(opaque, addr); - } - - switch (offset) { - case 0x00: /* RDR */ - s->control &= ~(1 << 15); /* RDRB */ - return s->rxbuf; - - case 0x04: /* CSR */ - return s->control; - - case 0x08: /* SR1 */ - return s->setup[0]; - case 0x0c: /* SR2 */ - return s->setup[1]; - case 0x10: /* SR3 */ - return s->setup[2]; - case 0x14: /* SR4 */ - return s->setup[3]; - case 0x18: /* SR5 */ - return s->setup[4]; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_uwire_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_uwire_s *s = (struct omap_uwire_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - - if (size != 2) { - omap_badwidth_write16(opaque, addr, value); - return; - } - - switch (offset) { - case 0x00: /* TDR */ - s->txbuf = value; /* TD */ - if ((s->setup[4] & (1 << 2)) && /* AUTO_TX_EN */ - ((s->setup[4] & (1 << 3)) || /* CS_TOGGLE_TX_EN */ - (s->control & (1 << 12)))) { /* CS_CMD */ - s->control |= 1 << 14; /* CSRB */ - omap_uwire_transfer_start(s); - } - break; - - case 0x04: /* CSR */ - s->control = value & 0x1fff; - if (value & (1 << 13)) /* START */ - omap_uwire_transfer_start(s); - break; - - case 0x08: /* SR1 */ - s->setup[0] = value & 0x003f; - break; - - case 0x0c: /* SR2 */ - s->setup[1] = value & 0x0fc0; - break; - - case 0x10: /* SR3 */ - s->setup[2] = value & 0x0003; - break; - - case 0x14: /* SR4 */ - s->setup[3] = value & 0x0001; - break; - - case 0x18: /* SR5 */ - s->setup[4] = value & 0x000f; - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_uwire_ops = { - .read = omap_uwire_read, - .write = omap_uwire_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_uwire_reset(struct omap_uwire_s *s) -{ - s->control = 0; - s->setup[0] = 0; - s->setup[1] = 0; - s->setup[2] = 0; - s->setup[3] = 0; - s->setup[4] = 0; -} - -static struct omap_uwire_s *omap_uwire_init(MemoryRegion *system_memory, - hwaddr base, - qemu_irq txirq, qemu_irq rxirq, - qemu_irq dma, - omap_clk clk) -{ - struct omap_uwire_s *s = g_new0(struct omap_uwire_s, 1); - - s->txirq = txirq; - s->rxirq = rxirq; - s->txdrq = dma; - omap_uwire_reset(s); - - memory_region_init_io(&s->iomem, NULL, &omap_uwire_ops, s, "omap-uwire", 0x800); - memory_region_add_subregion(system_memory, base, &s->iomem); - - return s; -} - -void omap_uwire_attach(struct omap_uwire_s *s, - uWireSlave *slave, int chipselect) -{ - if (chipselect < 0 || chipselect > 3) { - fprintf(stderr, "%s: Bad chipselect %i\n", __FUNCTION__, chipselect); - exit(-1); - } - - s->chip[chipselect] = slave; -} - -/* Pseudonoise Pulse-Width Light Modulator */ -struct omap_pwl_s { - MemoryRegion iomem; - uint8_t output; - uint8_t level; - uint8_t enable; - int clk; -}; - -static void omap_pwl_update(struct omap_pwl_s *s) -{ - int output = (s->clk && s->enable) ? s->level : 0; - - if (output != s->output) { - s->output = output; - printf("%s: Backlight now at %i/256\n", __FUNCTION__, output); - } -} - -static uint64_t omap_pwl_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_pwl_s *s = (struct omap_pwl_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - - if (size != 1) { - return omap_badwidth_read8(opaque, addr); - } - - switch (offset) { - case 0x00: /* PWL_LEVEL */ - return s->level; - case 0x04: /* PWL_CTRL */ - return s->enable; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_pwl_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_pwl_s *s = (struct omap_pwl_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - - if (size != 1) { - omap_badwidth_write8(opaque, addr, value); - return; - } - - switch (offset) { - case 0x00: /* PWL_LEVEL */ - s->level = value; - omap_pwl_update(s); - break; - case 0x04: /* PWL_CTRL */ - s->enable = value & 1; - omap_pwl_update(s); - break; - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_pwl_ops = { - .read = omap_pwl_read, - .write = omap_pwl_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_pwl_reset(struct omap_pwl_s *s) -{ - s->output = 0; - s->level = 0; - s->enable = 0; - s->clk = 1; - omap_pwl_update(s); -} - -static void omap_pwl_clk_update(void *opaque, int line, int on) -{ - struct omap_pwl_s *s = (struct omap_pwl_s *) opaque; - - s->clk = on; - omap_pwl_update(s); -} - -static struct omap_pwl_s *omap_pwl_init(MemoryRegion *system_memory, - hwaddr base, - omap_clk clk) -{ - struct omap_pwl_s *s = g_malloc0(sizeof(*s)); - - omap_pwl_reset(s); - - memory_region_init_io(&s->iomem, NULL, &omap_pwl_ops, s, - "omap-pwl", 0x800); - memory_region_add_subregion(system_memory, base, &s->iomem); - - omap_clk_adduser(clk, qemu_allocate_irq(omap_pwl_clk_update, s, 0)); - return s; -} - -/* Pulse-Width Tone module */ -struct omap_pwt_s { - MemoryRegion iomem; - uint8_t frc; - uint8_t vrc; - uint8_t gcr; - omap_clk clk; -}; - -static uint64_t omap_pwt_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_pwt_s *s = (struct omap_pwt_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - - if (size != 1) { - return omap_badwidth_read8(opaque, addr); - } - - switch (offset) { - case 0x00: /* FRC */ - return s->frc; - case 0x04: /* VCR */ - return s->vrc; - case 0x08: /* GCR */ - return s->gcr; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_pwt_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_pwt_s *s = (struct omap_pwt_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - - if (size != 1) { - omap_badwidth_write8(opaque, addr, value); - return; - } - - switch (offset) { - case 0x00: /* FRC */ - s->frc = value & 0x3f; - break; - case 0x04: /* VRC */ - if ((value ^ s->vrc) & 1) { - if (value & 1) - printf("%s: %iHz buzz on\n", __FUNCTION__, (int) - /* 1.5 MHz from a 12-MHz or 13-MHz PWT_CLK */ - ((omap_clk_getrate(s->clk) >> 3) / - /* Pre-multiplexer divider */ - ((s->gcr & 2) ? 1 : 154) / - /* Octave multiplexer */ - (2 << (value & 3)) * - /* 101/107 divider */ - ((value & (1 << 2)) ? 101 : 107) * - /* 49/55 divider */ - ((value & (1 << 3)) ? 49 : 55) * - /* 50/63 divider */ - ((value & (1 << 4)) ? 50 : 63) * - /* 80/127 divider */ - ((value & (1 << 5)) ? 80 : 127) / - (107 * 55 * 63 * 127))); - else - printf("%s: silence!\n", __FUNCTION__); - } - s->vrc = value & 0x7f; - break; - case 0x08: /* GCR */ - s->gcr = value & 3; - break; - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_pwt_ops = { - .read =omap_pwt_read, - .write = omap_pwt_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_pwt_reset(struct omap_pwt_s *s) -{ - s->frc = 0; - s->vrc = 0; - s->gcr = 0; -} - -static struct omap_pwt_s *omap_pwt_init(MemoryRegion *system_memory, - hwaddr base, - omap_clk clk) -{ - struct omap_pwt_s *s = g_malloc0(sizeof(*s)); - s->clk = clk; - omap_pwt_reset(s); - - memory_region_init_io(&s->iomem, NULL, &omap_pwt_ops, s, - "omap-pwt", 0x800); - memory_region_add_subregion(system_memory, base, &s->iomem); - return s; -} - -/* Real-time Clock module */ -struct omap_rtc_s { - MemoryRegion iomem; - qemu_irq irq; - qemu_irq alarm; - QEMUTimer *clk; - - uint8_t interrupts; - uint8_t status; - int16_t comp_reg; - int running; - int pm_am; - int auto_comp; - int round; - struct tm alarm_tm; - time_t alarm_ti; - - struct tm current_tm; - time_t ti; - uint64_t tick; -}; - -static void omap_rtc_interrupts_update(struct omap_rtc_s *s) -{ - /* s->alarm is level-triggered */ - qemu_set_irq(s->alarm, (s->status >> 6) & 1); -} - -static void omap_rtc_alarm_update(struct omap_rtc_s *s) -{ - s->alarm_ti = mktimegm(&s->alarm_tm); - if (s->alarm_ti == -1) - printf("%s: conversion failed\n", __FUNCTION__); -} - -static uint64_t omap_rtc_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_rtc_s *s = (struct omap_rtc_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - uint8_t i; - - if (size != 1) { - return omap_badwidth_read8(opaque, addr); - } - - switch (offset) { - case 0x00: /* SECONDS_REG */ - return to_bcd(s->current_tm.tm_sec); - - case 0x04: /* MINUTES_REG */ - return to_bcd(s->current_tm.tm_min); - - case 0x08: /* HOURS_REG */ - if (s->pm_am) - return ((s->current_tm.tm_hour > 11) << 7) | - to_bcd(((s->current_tm.tm_hour - 1) % 12) + 1); - else - return to_bcd(s->current_tm.tm_hour); - - case 0x0c: /* DAYS_REG */ - return to_bcd(s->current_tm.tm_mday); - - case 0x10: /* MONTHS_REG */ - return to_bcd(s->current_tm.tm_mon + 1); - - case 0x14: /* YEARS_REG */ - return to_bcd(s->current_tm.tm_year % 100); - - case 0x18: /* WEEK_REG */ - return s->current_tm.tm_wday; - - case 0x20: /* ALARM_SECONDS_REG */ - return to_bcd(s->alarm_tm.tm_sec); - - case 0x24: /* ALARM_MINUTES_REG */ - return to_bcd(s->alarm_tm.tm_min); - - case 0x28: /* ALARM_HOURS_REG */ - if (s->pm_am) - return ((s->alarm_tm.tm_hour > 11) << 7) | - to_bcd(((s->alarm_tm.tm_hour - 1) % 12) + 1); - else - return to_bcd(s->alarm_tm.tm_hour); - - case 0x2c: /* ALARM_DAYS_REG */ - return to_bcd(s->alarm_tm.tm_mday); - - case 0x30: /* ALARM_MONTHS_REG */ - return to_bcd(s->alarm_tm.tm_mon + 1); - - case 0x34: /* ALARM_YEARS_REG */ - return to_bcd(s->alarm_tm.tm_year % 100); - - case 0x40: /* RTC_CTRL_REG */ - return (s->pm_am << 3) | (s->auto_comp << 2) | - (s->round << 1) | s->running; - - case 0x44: /* RTC_STATUS_REG */ - i = s->status; - s->status &= ~0x3d; - return i; - - case 0x48: /* RTC_INTERRUPTS_REG */ - return s->interrupts; - - case 0x4c: /* RTC_COMP_LSB_REG */ - return ((uint16_t) s->comp_reg) & 0xff; - - case 0x50: /* RTC_COMP_MSB_REG */ - return ((uint16_t) s->comp_reg) >> 8; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_rtc_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_rtc_s *s = (struct omap_rtc_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - struct tm new_tm; - time_t ti[2]; - - if (size != 1) { - omap_badwidth_write8(opaque, addr, value); - return; - } - - switch (offset) { - case 0x00: /* SECONDS_REG */ -#ifdef ALMDEBUG - printf("RTC SEC_REG <-- %02x\n", value); -#endif - s->ti -= s->current_tm.tm_sec; - s->ti += from_bcd(value); - return; - - case 0x04: /* MINUTES_REG */ -#ifdef ALMDEBUG - printf("RTC MIN_REG <-- %02x\n", value); -#endif - s->ti -= s->current_tm.tm_min * 60; - s->ti += from_bcd(value) * 60; - return; - - case 0x08: /* HOURS_REG */ -#ifdef ALMDEBUG - printf("RTC HRS_REG <-- %02x\n", value); -#endif - s->ti -= s->current_tm.tm_hour * 3600; - if (s->pm_am) { - s->ti += (from_bcd(value & 0x3f) & 12) * 3600; - s->ti += ((value >> 7) & 1) * 43200; - } else - s->ti += from_bcd(value & 0x3f) * 3600; - return; - - case 0x0c: /* DAYS_REG */ -#ifdef ALMDEBUG - printf("RTC DAY_REG <-- %02x\n", value); -#endif - s->ti -= s->current_tm.tm_mday * 86400; - s->ti += from_bcd(value) * 86400; - return; - - case 0x10: /* MONTHS_REG */ -#ifdef ALMDEBUG - printf("RTC MTH_REG <-- %02x\n", value); -#endif - memcpy(&new_tm, &s->current_tm, sizeof(new_tm)); - new_tm.tm_mon = from_bcd(value); - ti[0] = mktimegm(&s->current_tm); - ti[1] = mktimegm(&new_tm); - - if (ti[0] != -1 && ti[1] != -1) { - s->ti -= ti[0]; - s->ti += ti[1]; - } else { - /* A less accurate version */ - s->ti -= s->current_tm.tm_mon * 2592000; - s->ti += from_bcd(value) * 2592000; - } - return; - - case 0x14: /* YEARS_REG */ -#ifdef ALMDEBUG - printf("RTC YRS_REG <-- %02x\n", value); -#endif - memcpy(&new_tm, &s->current_tm, sizeof(new_tm)); - new_tm.tm_year += from_bcd(value) - (new_tm.tm_year % 100); - ti[0] = mktimegm(&s->current_tm); - ti[1] = mktimegm(&new_tm); - - if (ti[0] != -1 && ti[1] != -1) { - s->ti -= ti[0]; - s->ti += ti[1]; - } else { - /* A less accurate version */ - s->ti -= (time_t)(s->current_tm.tm_year % 100) * 31536000; - s->ti += (time_t)from_bcd(value) * 31536000; - } - return; - - case 0x18: /* WEEK_REG */ - return; /* Ignored */ - - case 0x20: /* ALARM_SECONDS_REG */ -#ifdef ALMDEBUG - printf("ALM SEC_REG <-- %02x\n", value); -#endif - s->alarm_tm.tm_sec = from_bcd(value); - omap_rtc_alarm_update(s); - return; - - case 0x24: /* ALARM_MINUTES_REG */ -#ifdef ALMDEBUG - printf("ALM MIN_REG <-- %02x\n", value); -#endif - s->alarm_tm.tm_min = from_bcd(value); - omap_rtc_alarm_update(s); - return; - - case 0x28: /* ALARM_HOURS_REG */ -#ifdef ALMDEBUG - printf("ALM HRS_REG <-- %02x\n", value); -#endif - if (s->pm_am) - s->alarm_tm.tm_hour = - ((from_bcd(value & 0x3f)) % 12) + - ((value >> 7) & 1) * 12; - else - s->alarm_tm.tm_hour = from_bcd(value); - omap_rtc_alarm_update(s); - return; - - case 0x2c: /* ALARM_DAYS_REG */ -#ifdef ALMDEBUG - printf("ALM DAY_REG <-- %02x\n", value); -#endif - s->alarm_tm.tm_mday = from_bcd(value); - omap_rtc_alarm_update(s); - return; - - case 0x30: /* ALARM_MONTHS_REG */ -#ifdef ALMDEBUG - printf("ALM MON_REG <-- %02x\n", value); -#endif - s->alarm_tm.tm_mon = from_bcd(value); - omap_rtc_alarm_update(s); - return; - - case 0x34: /* ALARM_YEARS_REG */ -#ifdef ALMDEBUG - printf("ALM YRS_REG <-- %02x\n", value); -#endif - s->alarm_tm.tm_year = from_bcd(value); - omap_rtc_alarm_update(s); - return; - - case 0x40: /* RTC_CTRL_REG */ -#ifdef ALMDEBUG - printf("RTC CONTROL <-- %02x\n", value); -#endif - s->pm_am = (value >> 3) & 1; - s->auto_comp = (value >> 2) & 1; - s->round = (value >> 1) & 1; - s->running = value & 1; - s->status &= 0xfd; - s->status |= s->running << 1; - return; - - case 0x44: /* RTC_STATUS_REG */ -#ifdef ALMDEBUG - printf("RTC STATUSL <-- %02x\n", value); -#endif - s->status &= ~((value & 0xc0) ^ 0x80); - omap_rtc_interrupts_update(s); - return; - - case 0x48: /* RTC_INTERRUPTS_REG */ -#ifdef ALMDEBUG - printf("RTC INTRS <-- %02x\n", value); -#endif - s->interrupts = value; - return; - - case 0x4c: /* RTC_COMP_LSB_REG */ -#ifdef ALMDEBUG - printf("RTC COMPLSB <-- %02x\n", value); -#endif - s->comp_reg &= 0xff00; - s->comp_reg |= 0x00ff & value; - return; - - case 0x50: /* RTC_COMP_MSB_REG */ -#ifdef ALMDEBUG - printf("RTC COMPMSB <-- %02x\n", value); -#endif - s->comp_reg &= 0x00ff; - s->comp_reg |= 0xff00 & (value << 8); - return; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_rtc_ops = { - .read = omap_rtc_read, - .write = omap_rtc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_rtc_tick(void *opaque) -{ - struct omap_rtc_s *s = opaque; - - if (s->round) { - /* Round to nearest full minute. */ - if (s->current_tm.tm_sec < 30) - s->ti -= s->current_tm.tm_sec; - else - s->ti += 60 - s->current_tm.tm_sec; - - s->round = 0; - } - - localtime_r(&s->ti, &s->current_tm); - - if ((s->interrupts & 0x08) && s->ti == s->alarm_ti) { - s->status |= 0x40; - omap_rtc_interrupts_update(s); - } - - if (s->interrupts & 0x04) - switch (s->interrupts & 3) { - case 0: - s->status |= 0x04; - qemu_irq_pulse(s->irq); - break; - case 1: - if (s->current_tm.tm_sec) - break; - s->status |= 0x08; - qemu_irq_pulse(s->irq); - break; - case 2: - if (s->current_tm.tm_sec || s->current_tm.tm_min) - break; - s->status |= 0x10; - qemu_irq_pulse(s->irq); - break; - case 3: - if (s->current_tm.tm_sec || - s->current_tm.tm_min || s->current_tm.tm_hour) - break; - s->status |= 0x20; - qemu_irq_pulse(s->irq); - break; - } - - /* Move on */ - if (s->running) - s->ti ++; - s->tick += 1000; - - /* - * Every full hour add a rough approximation of the compensation - * register to the 32kHz Timer (which drives the RTC) value. - */ - if (s->auto_comp && !s->current_tm.tm_sec && !s->current_tm.tm_min) - s->tick += s->comp_reg * 1000 / 32768; - - timer_mod(s->clk, s->tick); -} - -static void omap_rtc_reset(struct omap_rtc_s *s) -{ - struct tm tm; - - s->interrupts = 0; - s->comp_reg = 0; - s->running = 0; - s->pm_am = 0; - s->auto_comp = 0; - s->round = 0; - s->tick = qemu_clock_get_ms(rtc_clock); - memset(&s->alarm_tm, 0, sizeof(s->alarm_tm)); - s->alarm_tm.tm_mday = 0x01; - s->status = 1 << 7; - qemu_get_timedate(&tm, 0); - s->ti = mktimegm(&tm); - - omap_rtc_alarm_update(s); - omap_rtc_tick(s); -} - -static struct omap_rtc_s *omap_rtc_init(MemoryRegion *system_memory, - hwaddr base, - qemu_irq timerirq, qemu_irq alarmirq, - omap_clk clk) -{ - struct omap_rtc_s *s = g_new0(struct omap_rtc_s, 1); - - s->irq = timerirq; - s->alarm = alarmirq; - s->clk = timer_new_ms(rtc_clock, omap_rtc_tick, s); - - omap_rtc_reset(s); - - memory_region_init_io(&s->iomem, NULL, &omap_rtc_ops, s, - "omap-rtc", 0x800); - memory_region_add_subregion(system_memory, base, &s->iomem); - - return s; -} - -/* Multi-channel Buffered Serial Port interfaces */ -struct omap_mcbsp_s { - MemoryRegion iomem; - qemu_irq txirq; - qemu_irq rxirq; - qemu_irq txdrq; - qemu_irq rxdrq; - - uint16_t spcr[2]; - uint16_t rcr[2]; - uint16_t xcr[2]; - uint16_t srgr[2]; - uint16_t mcr[2]; - uint16_t pcr; - uint16_t rcer[8]; - uint16_t xcer[8]; - int tx_rate; - int rx_rate; - int tx_req; - int rx_req; - - I2SCodec *codec; - QEMUTimer *source_timer; - QEMUTimer *sink_timer; -}; - -static void omap_mcbsp_intr_update(struct omap_mcbsp_s *s) -{ - int irq; - - switch ((s->spcr[0] >> 4) & 3) { /* RINTM */ - case 0: - irq = (s->spcr[0] >> 1) & 1; /* RRDY */ - break; - case 3: - irq = (s->spcr[0] >> 3) & 1; /* RSYNCERR */ - break; - default: - irq = 0; - break; - } - - if (irq) - qemu_irq_pulse(s->rxirq); - - switch ((s->spcr[1] >> 4) & 3) { /* XINTM */ - case 0: - irq = (s->spcr[1] >> 1) & 1; /* XRDY */ - break; - case 3: - irq = (s->spcr[1] >> 3) & 1; /* XSYNCERR */ - break; - default: - irq = 0; - break; - } - - if (irq) - qemu_irq_pulse(s->txirq); -} - -static void omap_mcbsp_rx_newdata(struct omap_mcbsp_s *s) -{ - if ((s->spcr[0] >> 1) & 1) /* RRDY */ - s->spcr[0] |= 1 << 2; /* RFULL */ - s->spcr[0] |= 1 << 1; /* RRDY */ - qemu_irq_raise(s->rxdrq); - omap_mcbsp_intr_update(s); -} - -static void omap_mcbsp_source_tick(void *opaque) -{ - struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; - static const int bps[8] = { 0, 1, 1, 2, 2, 2, -255, -255 }; - - if (!s->rx_rate) - return; - if (s->rx_req) - printf("%s: Rx FIFO overrun\n", __FUNCTION__); - - s->rx_req = s->rx_rate << bps[(s->rcr[0] >> 5) & 7]; - - omap_mcbsp_rx_newdata(s); - timer_mod(s->source_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - NANOSECONDS_PER_SECOND); -} - -static void omap_mcbsp_rx_start(struct omap_mcbsp_s *s) -{ - if (!s->codec || !s->codec->rts) - omap_mcbsp_source_tick(s); - else if (s->codec->in.len) { - s->rx_req = s->codec->in.len; - omap_mcbsp_rx_newdata(s); - } -} - -static void omap_mcbsp_rx_stop(struct omap_mcbsp_s *s) -{ - timer_del(s->source_timer); -} - -static void omap_mcbsp_rx_done(struct omap_mcbsp_s *s) -{ - s->spcr[0] &= ~(1 << 1); /* RRDY */ - qemu_irq_lower(s->rxdrq); - omap_mcbsp_intr_update(s); -} - -static void omap_mcbsp_tx_newdata(struct omap_mcbsp_s *s) -{ - s->spcr[1] |= 1 << 1; /* XRDY */ - qemu_irq_raise(s->txdrq); - omap_mcbsp_intr_update(s); -} - -static void omap_mcbsp_sink_tick(void *opaque) -{ - struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; - static const int bps[8] = { 0, 1, 1, 2, 2, 2, -255, -255 }; - - if (!s->tx_rate) - return; - if (s->tx_req) - printf("%s: Tx FIFO underrun\n", __FUNCTION__); - - s->tx_req = s->tx_rate << bps[(s->xcr[0] >> 5) & 7]; - - omap_mcbsp_tx_newdata(s); - timer_mod(s->sink_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - NANOSECONDS_PER_SECOND); -} - -static void omap_mcbsp_tx_start(struct omap_mcbsp_s *s) -{ - if (!s->codec || !s->codec->cts) - omap_mcbsp_sink_tick(s); - else if (s->codec->out.size) { - s->tx_req = s->codec->out.size; - omap_mcbsp_tx_newdata(s); - } -} - -static void omap_mcbsp_tx_done(struct omap_mcbsp_s *s) -{ - s->spcr[1] &= ~(1 << 1); /* XRDY */ - qemu_irq_lower(s->txdrq); - omap_mcbsp_intr_update(s); - if (s->codec && s->codec->cts) - s->codec->tx_swallow(s->codec->opaque); -} - -static void omap_mcbsp_tx_stop(struct omap_mcbsp_s *s) -{ - s->tx_req = 0; - omap_mcbsp_tx_done(s); - timer_del(s->sink_timer); -} - -static void omap_mcbsp_req_update(struct omap_mcbsp_s *s) -{ - int prev_rx_rate, prev_tx_rate; - int rx_rate = 0, tx_rate = 0; - int cpu_rate = 1500000; /* XXX */ - - /* TODO: check CLKSTP bit */ - if (s->spcr[1] & (1 << 6)) { /* GRST */ - if (s->spcr[0] & (1 << 0)) { /* RRST */ - if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ - (s->pcr & (1 << 8))) { /* CLKRM */ - if (~s->pcr & (1 << 7)) /* SCLKME */ - rx_rate = cpu_rate / - ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ - } else - if (s->codec) - rx_rate = s->codec->rx_rate; - } - - if (s->spcr[1] & (1 << 0)) { /* XRST */ - if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ - (s->pcr & (1 << 9))) { /* CLKXM */ - if (~s->pcr & (1 << 7)) /* SCLKME */ - tx_rate = cpu_rate / - ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ - } else - if (s->codec) - tx_rate = s->codec->tx_rate; - } - } - prev_tx_rate = s->tx_rate; - prev_rx_rate = s->rx_rate; - s->tx_rate = tx_rate; - s->rx_rate = rx_rate; - - if (s->codec) - s->codec->set_rate(s->codec->opaque, rx_rate, tx_rate); - - if (!prev_tx_rate && tx_rate) - omap_mcbsp_tx_start(s); - else if (s->tx_rate && !tx_rate) - omap_mcbsp_tx_stop(s); - - if (!prev_rx_rate && rx_rate) - omap_mcbsp_rx_start(s); - else if (prev_tx_rate && !tx_rate) - omap_mcbsp_rx_stop(s); -} - -static uint64_t omap_mcbsp_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - uint16_t ret; - - if (size != 2) { - return omap_badwidth_read16(opaque, addr); - } - - switch (offset) { - case 0x00: /* DRR2 */ - if (((s->rcr[0] >> 5) & 7) < 3) /* RWDLEN1 */ - return 0x0000; - /* Fall through. */ - case 0x02: /* DRR1 */ - if (s->rx_req < 2) { - printf("%s: Rx FIFO underrun\n", __FUNCTION__); - omap_mcbsp_rx_done(s); - } else { - s->tx_req -= 2; - if (s->codec && s->codec->in.len >= 2) { - ret = s->codec->in.fifo[s->codec->in.start ++] << 8; - ret |= s->codec->in.fifo[s->codec->in.start ++]; - s->codec->in.len -= 2; - } else - ret = 0x0000; - if (!s->tx_req) - omap_mcbsp_rx_done(s); - return ret; - } - return 0x0000; - - case 0x04: /* DXR2 */ - case 0x06: /* DXR1 */ - return 0x0000; - - case 0x08: /* SPCR2 */ - return s->spcr[1]; - case 0x0a: /* SPCR1 */ - return s->spcr[0]; - case 0x0c: /* RCR2 */ - return s->rcr[1]; - case 0x0e: /* RCR1 */ - return s->rcr[0]; - case 0x10: /* XCR2 */ - return s->xcr[1]; - case 0x12: /* XCR1 */ - return s->xcr[0]; - case 0x14: /* SRGR2 */ - return s->srgr[1]; - case 0x16: /* SRGR1 */ - return s->srgr[0]; - case 0x18: /* MCR2 */ - return s->mcr[1]; - case 0x1a: /* MCR1 */ - return s->mcr[0]; - case 0x1c: /* RCERA */ - return s->rcer[0]; - case 0x1e: /* RCERB */ - return s->rcer[1]; - case 0x20: /* XCERA */ - return s->xcer[0]; - case 0x22: /* XCERB */ - return s->xcer[1]; - case 0x24: /* PCR0 */ - return s->pcr; - case 0x26: /* RCERC */ - return s->rcer[2]; - case 0x28: /* RCERD */ - return s->rcer[3]; - case 0x2a: /* XCERC */ - return s->xcer[2]; - case 0x2c: /* XCERD */ - return s->xcer[3]; - case 0x2e: /* RCERE */ - return s->rcer[4]; - case 0x30: /* RCERF */ - return s->rcer[5]; - case 0x32: /* XCERE */ - return s->xcer[4]; - case 0x34: /* XCERF */ - return s->xcer[5]; - case 0x36: /* RCERG */ - return s->rcer[6]; - case 0x38: /* RCERH */ - return s->rcer[7]; - case 0x3a: /* XCERG */ - return s->xcer[6]; - case 0x3c: /* XCERH */ - return s->xcer[7]; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_mcbsp_writeh(void *opaque, hwaddr addr, - uint32_t value) -{ - struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - - switch (offset) { - case 0x00: /* DRR2 */ - case 0x02: /* DRR1 */ - OMAP_RO_REG(addr); - return; - - case 0x04: /* DXR2 */ - if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */ - return; - /* Fall through. */ - case 0x06: /* DXR1 */ - if (s->tx_req > 1) { - s->tx_req -= 2; - if (s->codec && s->codec->cts) { - s->codec->out.fifo[s->codec->out.len ++] = (value >> 8) & 0xff; - s->codec->out.fifo[s->codec->out.len ++] = (value >> 0) & 0xff; - } - if (s->tx_req < 2) - omap_mcbsp_tx_done(s); - } else - printf("%s: Tx FIFO overrun\n", __FUNCTION__); - return; - - case 0x08: /* SPCR2 */ - s->spcr[1] &= 0x0002; - s->spcr[1] |= 0x03f9 & value; - s->spcr[1] |= 0x0004 & (value << 2); /* XEMPTY := XRST */ - if (~value & 1) /* XRST */ - s->spcr[1] &= ~6; - omap_mcbsp_req_update(s); - return; - case 0x0a: /* SPCR1 */ - s->spcr[0] &= 0x0006; - s->spcr[0] |= 0xf8f9 & value; - if (value & (1 << 15)) /* DLB */ - printf("%s: Digital Loopback mode enable attempt\n", __FUNCTION__); - if (~value & 1) { /* RRST */ - s->spcr[0] &= ~6; - s->rx_req = 0; - omap_mcbsp_rx_done(s); - } - omap_mcbsp_req_update(s); - return; - - case 0x0c: /* RCR2 */ - s->rcr[1] = value & 0xffff; - return; - case 0x0e: /* RCR1 */ - s->rcr[0] = value & 0x7fe0; - return; - case 0x10: /* XCR2 */ - s->xcr[1] = value & 0xffff; - return; - case 0x12: /* XCR1 */ - s->xcr[0] = value & 0x7fe0; - return; - case 0x14: /* SRGR2 */ - s->srgr[1] = value & 0xffff; - omap_mcbsp_req_update(s); - return; - case 0x16: /* SRGR1 */ - s->srgr[0] = value & 0xffff; - omap_mcbsp_req_update(s); - return; - case 0x18: /* MCR2 */ - s->mcr[1] = value & 0x03e3; - if (value & 3) /* XMCM */ - printf("%s: Tx channel selection mode enable attempt\n", - __FUNCTION__); - return; - case 0x1a: /* MCR1 */ - s->mcr[0] = value & 0x03e1; - if (value & 1) /* RMCM */ - printf("%s: Rx channel selection mode enable attempt\n", - __FUNCTION__); - return; - case 0x1c: /* RCERA */ - s->rcer[0] = value & 0xffff; - return; - case 0x1e: /* RCERB */ - s->rcer[1] = value & 0xffff; - return; - case 0x20: /* XCERA */ - s->xcer[0] = value & 0xffff; - return; - case 0x22: /* XCERB */ - s->xcer[1] = value & 0xffff; - return; - case 0x24: /* PCR0 */ - s->pcr = value & 0x7faf; - return; - case 0x26: /* RCERC */ - s->rcer[2] = value & 0xffff; - return; - case 0x28: /* RCERD */ - s->rcer[3] = value & 0xffff; - return; - case 0x2a: /* XCERC */ - s->xcer[2] = value & 0xffff; - return; - case 0x2c: /* XCERD */ - s->xcer[3] = value & 0xffff; - return; - case 0x2e: /* RCERE */ - s->rcer[4] = value & 0xffff; - return; - case 0x30: /* RCERF */ - s->rcer[5] = value & 0xffff; - return; - case 0x32: /* XCERE */ - s->xcer[4] = value & 0xffff; - return; - case 0x34: /* XCERF */ - s->xcer[5] = value & 0xffff; - return; - case 0x36: /* RCERG */ - s->rcer[6] = value & 0xffff; - return; - case 0x38: /* RCERH */ - s->rcer[7] = value & 0xffff; - return; - case 0x3a: /* XCERG */ - s->xcer[6] = value & 0xffff; - return; - case 0x3c: /* XCERH */ - s->xcer[7] = value & 0xffff; - return; - } - - OMAP_BAD_REG(addr); -} - -static void omap_mcbsp_writew(void *opaque, hwaddr addr, - uint32_t value) -{ - struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - - if (offset == 0x04) { /* DXR */ - if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */ - return; - if (s->tx_req > 3) { - s->tx_req -= 4; - if (s->codec && s->codec->cts) { - s->codec->out.fifo[s->codec->out.len ++] = - (value >> 24) & 0xff; - s->codec->out.fifo[s->codec->out.len ++] = - (value >> 16) & 0xff; - s->codec->out.fifo[s->codec->out.len ++] = - (value >> 8) & 0xff; - s->codec->out.fifo[s->codec->out.len ++] = - (value >> 0) & 0xff; - } - if (s->tx_req < 4) - omap_mcbsp_tx_done(s); - } else - printf("%s: Tx FIFO overrun\n", __FUNCTION__); - return; - } - - omap_badwidth_write16(opaque, addr, value); -} - -static void omap_mcbsp_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - switch (size) { - case 2: - omap_mcbsp_writeh(opaque, addr, value); - break; - case 4: - omap_mcbsp_writew(opaque, addr, value); - break; - default: - omap_badwidth_write16(opaque, addr, value); - } -} - -static const MemoryRegionOps omap_mcbsp_ops = { - .read = omap_mcbsp_read, - .write = omap_mcbsp_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_mcbsp_reset(struct omap_mcbsp_s *s) -{ - memset(&s->spcr, 0, sizeof(s->spcr)); - memset(&s->rcr, 0, sizeof(s->rcr)); - memset(&s->xcr, 0, sizeof(s->xcr)); - s->srgr[0] = 0x0001; - s->srgr[1] = 0x2000; - memset(&s->mcr, 0, sizeof(s->mcr)); - memset(&s->pcr, 0, sizeof(s->pcr)); - memset(&s->rcer, 0, sizeof(s->rcer)); - memset(&s->xcer, 0, sizeof(s->xcer)); - s->tx_req = 0; - s->rx_req = 0; - s->tx_rate = 0; - s->rx_rate = 0; - timer_del(s->source_timer); - timer_del(s->sink_timer); -} - -static struct omap_mcbsp_s *omap_mcbsp_init(MemoryRegion *system_memory, - hwaddr base, - qemu_irq txirq, qemu_irq rxirq, - qemu_irq *dma, omap_clk clk) -{ - struct omap_mcbsp_s *s = g_new0(struct omap_mcbsp_s, 1); - - s->txirq = txirq; - s->rxirq = rxirq; - s->txdrq = dma[0]; - s->rxdrq = dma[1]; - s->sink_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, omap_mcbsp_sink_tick, s); - s->source_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, omap_mcbsp_source_tick, s); - omap_mcbsp_reset(s); - - memory_region_init_io(&s->iomem, NULL, &omap_mcbsp_ops, s, "omap-mcbsp", 0x800); - memory_region_add_subregion(system_memory, base, &s->iomem); - - return s; -} - -static void omap_mcbsp_i2s_swallow(void *opaque, int line, int level) -{ - struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; - - if (s->rx_rate) { - s->rx_req = s->codec->in.len; - omap_mcbsp_rx_newdata(s); - } -} - -static void omap_mcbsp_i2s_start(void *opaque, int line, int level) -{ - struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; - - if (s->tx_rate) { - s->tx_req = s->codec->out.size; - omap_mcbsp_tx_newdata(s); - } -} - -void omap_mcbsp_i2s_attach(struct omap_mcbsp_s *s, I2SCodec *slave) -{ - s->codec = slave; - slave->rx_swallow = qemu_allocate_irq(omap_mcbsp_i2s_swallow, s, 0); - slave->tx_start = qemu_allocate_irq(omap_mcbsp_i2s_start, s, 0); -} - -/* LED Pulse Generators */ -struct omap_lpg_s { - MemoryRegion iomem; - QEMUTimer *tm; - - uint8_t control; - uint8_t power; - int64_t on; - int64_t period; - int clk; - int cycle; -}; - -static void omap_lpg_tick(void *opaque) -{ - struct omap_lpg_s *s = opaque; - - if (s->cycle) - timer_mod(s->tm, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + s->period - s->on); - else - timer_mod(s->tm, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + s->on); - - s->cycle = !s->cycle; - printf("%s: LED is %s\n", __FUNCTION__, s->cycle ? "on" : "off"); -} - -static void omap_lpg_update(struct omap_lpg_s *s) -{ - int64_t on, period = 1, ticks = 1000; - static const int per[8] = { 1, 2, 4, 8, 12, 16, 20, 24 }; - - if (~s->control & (1 << 6)) /* LPGRES */ - on = 0; - else if (s->control & (1 << 7)) /* PERM_ON */ - on = period; - else { - period = muldiv64(ticks, per[s->control & 7], /* PERCTRL */ - 256 / 32); - on = (s->clk && s->power) ? muldiv64(ticks, - per[(s->control >> 3) & 7], 256) : 0; /* ONCTRL */ - } - - timer_del(s->tm); - if (on == period && s->on < s->period) - printf("%s: LED is on\n", __FUNCTION__); - else if (on == 0 && s->on) - printf("%s: LED is off\n", __FUNCTION__); - else if (on && (on != s->on || period != s->period)) { - s->cycle = 0; - s->on = on; - s->period = period; - omap_lpg_tick(s); - return; - } - - s->on = on; - s->period = period; -} - -static void omap_lpg_reset(struct omap_lpg_s *s) -{ - s->control = 0x00; - s->power = 0x00; - s->clk = 1; - omap_lpg_update(s); -} - -static uint64_t omap_lpg_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_lpg_s *s = (struct omap_lpg_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - - if (size != 1) { - return omap_badwidth_read8(opaque, addr); - } - - switch (offset) { - case 0x00: /* LCR */ - return s->control; - - case 0x04: /* PMR */ - return s->power; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_lpg_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_lpg_s *s = (struct omap_lpg_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - - if (size != 1) { - omap_badwidth_write8(opaque, addr, value); - return; - } - - switch (offset) { - case 0x00: /* LCR */ - if (~value & (1 << 6)) /* LPGRES */ - omap_lpg_reset(s); - s->control = value & 0xff; - omap_lpg_update(s); - return; - - case 0x04: /* PMR */ - s->power = value & 0x01; - omap_lpg_update(s); - return; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_lpg_ops = { - .read = omap_lpg_read, - .write = omap_lpg_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_lpg_clk_update(void *opaque, int line, int on) -{ - struct omap_lpg_s *s = (struct omap_lpg_s *) opaque; - - s->clk = on; - omap_lpg_update(s); -} - -static struct omap_lpg_s *omap_lpg_init(MemoryRegion *system_memory, - hwaddr base, omap_clk clk) -{ - struct omap_lpg_s *s = g_new0(struct omap_lpg_s, 1); - - s->tm = timer_new_ms(QEMU_CLOCK_VIRTUAL, omap_lpg_tick, s); - - omap_lpg_reset(s); - - memory_region_init_io(&s->iomem, NULL, &omap_lpg_ops, s, "omap-lpg", 0x800); - memory_region_add_subregion(system_memory, base, &s->iomem); - - omap_clk_adduser(clk, qemu_allocate_irq(omap_lpg_clk_update, s, 0)); - - return s; -} - -/* MPUI Peripheral Bridge configuration */ -static uint64_t omap_mpui_io_read(void *opaque, hwaddr addr, - unsigned size) -{ - if (size != 2) { - return omap_badwidth_read16(opaque, addr); - } - - if (addr == OMAP_MPUI_BASE) /* CMR */ - return 0xfe4d; - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_mpui_io_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - /* FIXME: infinite loop */ - omap_badwidth_write16(opaque, addr, value); -} - -static const MemoryRegionOps omap_mpui_io_ops = { - .read = omap_mpui_io_read, - .write = omap_mpui_io_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_setup_mpui_io(MemoryRegion *system_memory, - struct omap_mpu_state_s *mpu) -{ - memory_region_init_io(&mpu->mpui_io_iomem, NULL, &omap_mpui_io_ops, mpu, - "omap-mpui-io", 0x7fff); - memory_region_add_subregion(system_memory, OMAP_MPUI_BASE, - &mpu->mpui_io_iomem); -} - -/* General chip reset */ -static void omap1_mpu_reset(void *opaque) -{ - struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque; - - omap_dma_reset(mpu->dma); - omap_mpu_timer_reset(mpu->timer[0]); - omap_mpu_timer_reset(mpu->timer[1]); - omap_mpu_timer_reset(mpu->timer[2]); - omap_wd_timer_reset(mpu->wdt); - omap_os_timer_reset(mpu->os_timer); - omap_lcdc_reset(mpu->lcd); - omap_ulpd_pm_reset(mpu); - omap_pin_cfg_reset(mpu); - omap_mpui_reset(mpu); - omap_tipb_bridge_reset(mpu->private_tipb); - omap_tipb_bridge_reset(mpu->public_tipb); - omap_dpll_reset(mpu->dpll[0]); - omap_dpll_reset(mpu->dpll[1]); - omap_dpll_reset(mpu->dpll[2]); - omap_uart_reset(mpu->uart[0]); - omap_uart_reset(mpu->uart[1]); - omap_uart_reset(mpu->uart[2]); - omap_mmc_reset(mpu->mmc); - omap_mpuio_reset(mpu->mpuio); - omap_uwire_reset(mpu->microwire); - omap_pwl_reset(mpu->pwl); - omap_pwt_reset(mpu->pwt); - omap_rtc_reset(mpu->rtc); - omap_mcbsp_reset(mpu->mcbsp1); - omap_mcbsp_reset(mpu->mcbsp2); - omap_mcbsp_reset(mpu->mcbsp3); - omap_lpg_reset(mpu->led[0]); - omap_lpg_reset(mpu->led[1]); - omap_clkm_reset(mpu); - cpu_reset(CPU(mpu->cpu)); -} - -static const struct omap_map_s { - hwaddr phys_dsp; - hwaddr phys_mpu; - uint32_t size; - const char *name; -} omap15xx_dsp_mm[] = { - /* Strobe 0 */ - { 0xe1010000, 0xfffb0000, 0x800, "UART1 BT" }, /* CS0 */ - { 0xe1010800, 0xfffb0800, 0x800, "UART2 COM" }, /* CS1 */ - { 0xe1011800, 0xfffb1800, 0x800, "McBSP1 audio" }, /* CS3 */ - { 0xe1012000, 0xfffb2000, 0x800, "MCSI2 communication" }, /* CS4 */ - { 0xe1012800, 0xfffb2800, 0x800, "MCSI1 BT u-Law" }, /* CS5 */ - { 0xe1013000, 0xfffb3000, 0x800, "uWire" }, /* CS6 */ - { 0xe1013800, 0xfffb3800, 0x800, "I^2C" }, /* CS7 */ - { 0xe1014000, 0xfffb4000, 0x800, "USB W2FC" }, /* CS8 */ - { 0xe1014800, 0xfffb4800, 0x800, "RTC" }, /* CS9 */ - { 0xe1015000, 0xfffb5000, 0x800, "MPUIO" }, /* CS10 */ - { 0xe1015800, 0xfffb5800, 0x800, "PWL" }, /* CS11 */ - { 0xe1016000, 0xfffb6000, 0x800, "PWT" }, /* CS12 */ - { 0xe1017000, 0xfffb7000, 0x800, "McBSP3" }, /* CS14 */ - { 0xe1017800, 0xfffb7800, 0x800, "MMC" }, /* CS15 */ - { 0xe1019000, 0xfffb9000, 0x800, "32-kHz timer" }, /* CS18 */ - { 0xe1019800, 0xfffb9800, 0x800, "UART3" }, /* CS19 */ - { 0xe101c800, 0xfffbc800, 0x800, "TIPB switches" }, /* CS25 */ - /* Strobe 1 */ - { 0xe101e000, 0xfffce000, 0x800, "GPIOs" }, /* CS28 */ - - { 0 } -}; - -static void omap_setup_dsp_mapping(MemoryRegion *system_memory, - const struct omap_map_s *map) -{ - MemoryRegion *io; - - for (; map->phys_dsp; map ++) { - io = g_new(MemoryRegion, 1); - memory_region_init_alias(io, NULL, map->name, - system_memory, map->phys_mpu, map->size); - memory_region_add_subregion(system_memory, map->phys_dsp, io); - } -} - -void omap_mpu_wakeup(void *opaque, int irq, int req) -{ - struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque; - CPUState *cpu = CPU(mpu->cpu); - - if (cpu->halted) { - cpu_interrupt(cpu, CPU_INTERRUPT_EXITTB); - } -} - -static const struct dma_irq_map omap1_dma_irq_map[] = { - { 0, OMAP_INT_DMA_CH0_6 }, - { 0, OMAP_INT_DMA_CH1_7 }, - { 0, OMAP_INT_DMA_CH2_8 }, - { 0, OMAP_INT_DMA_CH3 }, - { 0, OMAP_INT_DMA_CH4 }, - { 0, OMAP_INT_DMA_CH5 }, - { 1, OMAP_INT_1610_DMA_CH6 }, - { 1, OMAP_INT_1610_DMA_CH7 }, - { 1, OMAP_INT_1610_DMA_CH8 }, - { 1, OMAP_INT_1610_DMA_CH9 }, - { 1, OMAP_INT_1610_DMA_CH10 }, - { 1, OMAP_INT_1610_DMA_CH11 }, - { 1, OMAP_INT_1610_DMA_CH12 }, - { 1, OMAP_INT_1610_DMA_CH13 }, - { 1, OMAP_INT_1610_DMA_CH14 }, - { 1, OMAP_INT_1610_DMA_CH15 } -}; - -/* DMA ports for OMAP1 */ -static int omap_validate_emiff_addr(struct omap_mpu_state_s *s, - hwaddr addr) -{ - return range_covers_byte(OMAP_EMIFF_BASE, s->sdram_size, addr); -} - -static int omap_validate_emifs_addr(struct omap_mpu_state_s *s, - hwaddr addr) -{ - return range_covers_byte(OMAP_EMIFS_BASE, OMAP_EMIFF_BASE - OMAP_EMIFS_BASE, - addr); -} - -static int omap_validate_imif_addr(struct omap_mpu_state_s *s, - hwaddr addr) -{ - return range_covers_byte(OMAP_IMIF_BASE, s->sram_size, addr); -} - -static int omap_validate_tipb_addr(struct omap_mpu_state_s *s, - hwaddr addr) -{ - return range_covers_byte(0xfffb0000, 0xffff0000 - 0xfffb0000, addr); -} - -static int omap_validate_local_addr(struct omap_mpu_state_s *s, - hwaddr addr) -{ - return range_covers_byte(OMAP_LOCALBUS_BASE, 0x1000000, addr); -} - -static int omap_validate_tipb_mpui_addr(struct omap_mpu_state_s *s, - hwaddr addr) -{ - return range_covers_byte(0xe1010000, 0xe1020004 - 0xe1010000, addr); -} - -struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, - unsigned long sdram_size, - const char *core) -{ - int i; - struct omap_mpu_state_s *s = g_new0(struct omap_mpu_state_s, 1); - qemu_irq dma_irqs[6]; - DriveInfo *dinfo; - SysBusDevice *busdev; - - if (!core) - core = "ti925t"; - - /* Core */ - s->mpu_model = omap310; - s->cpu = cpu_arm_init(core); - if (s->cpu == NULL) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - s->sdram_size = sdram_size; - s->sram_size = OMAP15XX_SRAM_SIZE; - - s->wakeup = qemu_allocate_irq(omap_mpu_wakeup, s, 0); - - /* Clocks */ - omap_clk_init(s); - - /* Memory-mapped stuff */ - memory_region_allocate_system_memory(&s->emiff_ram, NULL, "omap1.dram", - 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_fatal); - vmstate_register_ram_global(&s->imif_ram); - memory_region_add_subregion(system_memory, OMAP_IMIF_BASE, &s->imif_ram); - - omap_clkm_init(system_memory, 0xfffece00, 0xe1008000, s); - - s->ih[0] = qdev_create(NULL, "omap-intc"); - qdev_prop_set_uint32(s->ih[0], "size", 0x100); - qdev_prop_set_ptr(s->ih[0], "clk", omap_findclk(s, "arminth_ck")); - qdev_init_nofail(s->ih[0]); - busdev = SYS_BUS_DEVICE(s->ih[0]); - sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ)); - sysbus_connect_irq(busdev, 1, - qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_FIQ)); - sysbus_mmio_map(busdev, 0, 0xfffecb00); - s->ih[1] = qdev_create(NULL, "omap-intc"); - qdev_prop_set_uint32(s->ih[1], "size", 0x800); - qdev_prop_set_ptr(s->ih[1], "clk", omap_findclk(s, "arminth_ck")); - qdev_init_nofail(s->ih[1]); - busdev = SYS_BUS_DEVICE(s->ih[1]); - sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(s->ih[0], OMAP_INT_15XX_IH2_IRQ)); - /* The second interrupt controller's FIQ output is not wired up */ - sysbus_mmio_map(busdev, 0, 0xfffe0000); - - for (i = 0; i < 6; i++) { - dma_irqs[i] = qdev_get_gpio_in(s->ih[omap1_dma_irq_map[i].ih], - omap1_dma_irq_map[i].intr); - } - s->dma = omap_dma_init(0xfffed800, dma_irqs, system_memory, - qdev_get_gpio_in(s->ih[0], OMAP_INT_DMA_LCD), - s, omap_findclk(s, "dma_ck"), omap_dma_3_1); - - s->port[emiff ].addr_valid = omap_validate_emiff_addr; - s->port[emifs ].addr_valid = omap_validate_emifs_addr; - s->port[imif ].addr_valid = omap_validate_imif_addr; - s->port[tipb ].addr_valid = omap_validate_tipb_addr; - s->port[local ].addr_valid = omap_validate_local_addr; - s->port[tipb_mpui].addr_valid = omap_validate_tipb_mpui_addr; - - /* Register SDRAM and SRAM DMA ports for fast transfers. */ - soc_dma_port_add_mem(s->dma, memory_region_get_ram_ptr(&s->emiff_ram), - OMAP_EMIFF_BASE, s->sdram_size); - soc_dma_port_add_mem(s->dma, memory_region_get_ram_ptr(&s->imif_ram), - OMAP_IMIF_BASE, s->sram_size); - - s->timer[0] = omap_mpu_timer_init(system_memory, 0xfffec500, - qdev_get_gpio_in(s->ih[0], OMAP_INT_TIMER1), - omap_findclk(s, "mputim_ck")); - s->timer[1] = omap_mpu_timer_init(system_memory, 0xfffec600, - qdev_get_gpio_in(s->ih[0], OMAP_INT_TIMER2), - omap_findclk(s, "mputim_ck")); - s->timer[2] = omap_mpu_timer_init(system_memory, 0xfffec700, - qdev_get_gpio_in(s->ih[0], OMAP_INT_TIMER3), - omap_findclk(s, "mputim_ck")); - - s->wdt = omap_wd_timer_init(system_memory, 0xfffec800, - qdev_get_gpio_in(s->ih[0], OMAP_INT_WD_TIMER), - omap_findclk(s, "armwdt_ck")); - - s->os_timer = omap_os_timer_init(system_memory, 0xfffb9000, - qdev_get_gpio_in(s->ih[1], OMAP_INT_OS_TIMER), - omap_findclk(s, "clk32-kHz")); - - s->lcd = omap_lcdc_init(system_memory, 0xfffec000, - qdev_get_gpio_in(s->ih[0], OMAP_INT_LCD_CTRL), - omap_dma_get_lcdch(s->dma), - omap_findclk(s, "lcd_ck")); - - omap_ulpd_pm_init(system_memory, 0xfffe0800, s); - omap_pin_cfg_init(system_memory, 0xfffe1000, s); - omap_id_init(system_memory, s); - - omap_mpui_init(system_memory, 0xfffec900, s); - - s->private_tipb = omap_tipb_bridge_init(system_memory, 0xfffeca00, - qdev_get_gpio_in(s->ih[0], OMAP_INT_BRIDGE_PRIV), - omap_findclk(s, "tipb_ck")); - s->public_tipb = omap_tipb_bridge_init(system_memory, 0xfffed300, - qdev_get_gpio_in(s->ih[0], OMAP_INT_BRIDGE_PUB), - omap_findclk(s, "tipb_ck")); - - omap_tcmi_init(system_memory, 0xfffecc00, s); - - s->uart[0] = omap_uart_init(0xfffb0000, - qdev_get_gpio_in(s->ih[1], OMAP_INT_UART1), - omap_findclk(s, "uart1_ck"), - omap_findclk(s, "uart1_ck"), - s->drq[OMAP_DMA_UART1_TX], s->drq[OMAP_DMA_UART1_RX], - "uart1", - serial_hds[0]); - s->uart[1] = omap_uart_init(0xfffb0800, - qdev_get_gpio_in(s->ih[1], OMAP_INT_UART2), - omap_findclk(s, "uart2_ck"), - omap_findclk(s, "uart2_ck"), - s->drq[OMAP_DMA_UART2_TX], s->drq[OMAP_DMA_UART2_RX], - "uart2", - serial_hds[0] ? serial_hds[1] : NULL); - s->uart[2] = omap_uart_init(0xfffb9800, - qdev_get_gpio_in(s->ih[0], OMAP_INT_UART3), - omap_findclk(s, "uart3_ck"), - omap_findclk(s, "uart3_ck"), - s->drq[OMAP_DMA_UART3_TX], s->drq[OMAP_DMA_UART3_RX], - "uart3", - serial_hds[0] && serial_hds[1] ? serial_hds[2] : NULL); - - s->dpll[0] = omap_dpll_init(system_memory, 0xfffecf00, - omap_findclk(s, "dpll1")); - s->dpll[1] = omap_dpll_init(system_memory, 0xfffed000, - omap_findclk(s, "dpll2")); - s->dpll[2] = omap_dpll_init(system_memory, 0xfffed100, - omap_findclk(s, "dpll3")); - - dinfo = drive_get(IF_SD, 0, 0); - if (!dinfo) { - fprintf(stderr, "qemu: missing SecureDigital device\n"); - exit(1); - } - s->mmc = omap_mmc_init(0xfffb7800, system_memory, - blk_by_legacy_dinfo(dinfo), - qdev_get_gpio_in(s->ih[1], OMAP_INT_OQN), - &s->drq[OMAP_DMA_MMC_TX], - omap_findclk(s, "mmc_ck")); - - s->mpuio = omap_mpuio_init(system_memory, 0xfffb5000, - qdev_get_gpio_in(s->ih[1], OMAP_INT_KEYBOARD), - qdev_get_gpio_in(s->ih[1], OMAP_INT_MPUIO), - s->wakeup, omap_findclk(s, "clk32-kHz")); - - s->gpio = qdev_create(NULL, "omap-gpio"); - qdev_prop_set_int32(s->gpio, "mpu_model", s->mpu_model); - qdev_prop_set_ptr(s->gpio, "clk", omap_findclk(s, "arm_gpio_ck")); - qdev_init_nofail(s->gpio); - sysbus_connect_irq(SYS_BUS_DEVICE(s->gpio), 0, - qdev_get_gpio_in(s->ih[0], OMAP_INT_GPIO_BANK1)); - sysbus_mmio_map(SYS_BUS_DEVICE(s->gpio), 0, 0xfffce000); - - s->microwire = omap_uwire_init(system_memory, 0xfffb3000, - qdev_get_gpio_in(s->ih[1], OMAP_INT_uWireTX), - qdev_get_gpio_in(s->ih[1], OMAP_INT_uWireRX), - s->drq[OMAP_DMA_UWIRE_TX], omap_findclk(s, "mpuper_ck")); - - s->pwl = omap_pwl_init(system_memory, 0xfffb5800, - omap_findclk(s, "armxor_ck")); - s->pwt = omap_pwt_init(system_memory, 0xfffb6000, - omap_findclk(s, "armxor_ck")); - - s->i2c[0] = qdev_create(NULL, "omap_i2c"); - qdev_prop_set_uint8(s->i2c[0], "revision", 0x11); - qdev_prop_set_ptr(s->i2c[0], "fclk", omap_findclk(s, "mpuper_ck")); - qdev_init_nofail(s->i2c[0]); - busdev = SYS_BUS_DEVICE(s->i2c[0]); - sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(s->ih[1], OMAP_INT_I2C)); - sysbus_connect_irq(busdev, 1, s->drq[OMAP_DMA_I2C_TX]); - sysbus_connect_irq(busdev, 2, s->drq[OMAP_DMA_I2C_RX]); - sysbus_mmio_map(busdev, 0, 0xfffb3800); - - s->rtc = omap_rtc_init(system_memory, 0xfffb4800, - qdev_get_gpio_in(s->ih[1], OMAP_INT_RTC_TIMER), - qdev_get_gpio_in(s->ih[1], OMAP_INT_RTC_ALARM), - omap_findclk(s, "clk32-kHz")); - - s->mcbsp1 = omap_mcbsp_init(system_memory, 0xfffb1800, - qdev_get_gpio_in(s->ih[1], OMAP_INT_McBSP1TX), - qdev_get_gpio_in(s->ih[1], OMAP_INT_McBSP1RX), - &s->drq[OMAP_DMA_MCBSP1_TX], omap_findclk(s, "dspxor_ck")); - s->mcbsp2 = omap_mcbsp_init(system_memory, 0xfffb1000, - qdev_get_gpio_in(s->ih[0], - OMAP_INT_310_McBSP2_TX), - qdev_get_gpio_in(s->ih[0], - OMAP_INT_310_McBSP2_RX), - &s->drq[OMAP_DMA_MCBSP2_TX], omap_findclk(s, "mpuper_ck")); - s->mcbsp3 = omap_mcbsp_init(system_memory, 0xfffb7000, - qdev_get_gpio_in(s->ih[1], OMAP_INT_McBSP3TX), - qdev_get_gpio_in(s->ih[1], OMAP_INT_McBSP3RX), - &s->drq[OMAP_DMA_MCBSP3_TX], omap_findclk(s, "dspxor_ck")); - - s->led[0] = omap_lpg_init(system_memory, - 0xfffbd000, omap_findclk(s, "clk32-kHz")); - s->led[1] = omap_lpg_init(system_memory, - 0xfffbd800, omap_findclk(s, "clk32-kHz")); - - /* Register mappings not currenlty implemented: - * MCSI2 Comm fffb2000 - fffb27ff (not mapped on OMAP310) - * MCSI1 Bluetooth fffb2800 - fffb2fff (not mapped on OMAP310) - * USB W2FC fffb4000 - fffb47ff - * Camera Interface fffb6800 - fffb6fff - * USB Host fffba000 - fffba7ff - * FAC fffba800 - fffbafff - * HDQ/1-Wire fffbc000 - fffbc7ff - * TIPB switches fffbc800 - fffbcfff - * Mailbox fffcf000 - fffcf7ff - * Local bus IF fffec100 - fffec1ff - * Local bus MMU fffec200 - fffec2ff - * DSP MMU fffed200 - fffed2ff - */ - - omap_setup_dsp_mapping(system_memory, omap15xx_dsp_mm); - omap_setup_mpui_io(system_memory, s); - - qemu_register_reset(omap1_mpu_reset, s); - - return s; -} diff --git a/qemu/hw/arm/omap2.c b/qemu/hw/arm/omap2.c deleted file mode 100644 index 3a0d77714..000000000 --- a/qemu/hw/arm/omap2.c +++ /dev/null @@ -1,2691 +0,0 @@ -/* - * TI OMAP processors emulation. - * - * Copyright (C) 2007-2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#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" -#include "hw/hw.h" -#include "hw/arm/arm.h" -#include "hw/arm/omap.h" -#include "sysemu/sysemu.h" -#include "qemu/timer.h" -#include "sysemu/char.h" -#include "hw/block/flash.h" -#include "hw/arm/soc_dma.h" -#include "hw/sysbus.h" -#include "audio/audio.h" - -/* Enhanced Audio Controller (CODEC only) */ -struct omap_eac_s { - qemu_irq irq; - MemoryRegion iomem; - - uint16_t sysconfig; - uint8_t config[4]; - uint8_t control; - uint8_t address; - uint16_t data; - uint8_t vtol; - uint8_t vtsl; - uint16_t mixer; - uint16_t gain[4]; - uint8_t att; - uint16_t max[7]; - - struct { - qemu_irq txdrq; - qemu_irq rxdrq; - uint32_t (*txrx)(void *opaque, uint32_t, int); - void *opaque; - -#define EAC_BUF_LEN 1024 - uint32_t rxbuf[EAC_BUF_LEN]; - int rxoff; - int rxlen; - int rxavail; - uint32_t txbuf[EAC_BUF_LEN]; - int txlen; - int txavail; - - int enable; - int rate; - - uint16_t config[4]; - - /* These need to be moved to the actual codec */ - QEMUSoundCard card; - SWVoiceIn *in_voice; - SWVoiceOut *out_voice; - int hw_enable; - } codec; - - struct { - uint8_t control; - uint16_t config; - } modem, bt; -}; - -static inline void omap_eac_interrupt_update(struct omap_eac_s *s) -{ - qemu_set_irq(s->irq, (s->codec.config[1] >> 14) & 1); /* AURDI */ -} - -static inline void omap_eac_in_dmarequest_update(struct omap_eac_s *s) -{ - qemu_set_irq(s->codec.rxdrq, (s->codec.rxavail || s->codec.rxlen) && - ((s->codec.config[1] >> 12) & 1)); /* DMAREN */ -} - -static inline void omap_eac_out_dmarequest_update(struct omap_eac_s *s) -{ - qemu_set_irq(s->codec.txdrq, s->codec.txlen < s->codec.txavail && - ((s->codec.config[1] >> 11) & 1)); /* DMAWEN */ -} - -static inline void omap_eac_in_refill(struct omap_eac_s *s) -{ - int left = MIN(EAC_BUF_LEN - s->codec.rxlen, s->codec.rxavail) << 2; - int start = ((s->codec.rxoff + s->codec.rxlen) & (EAC_BUF_LEN - 1)) << 2; - int leftwrap = MIN(left, (EAC_BUF_LEN << 2) - start); - int recv = 1; - uint8_t *buf = (uint8_t *) s->codec.rxbuf + start; - - left -= leftwrap; - start = 0; - while (leftwrap && (recv = AUD_read(s->codec.in_voice, buf + start, - leftwrap)) > 0) { /* Be defensive */ - start += recv; - leftwrap -= recv; - } - if (recv <= 0) - s->codec.rxavail = 0; - else - s->codec.rxavail -= start >> 2; - s->codec.rxlen += start >> 2; - - if (recv > 0 && left > 0) { - start = 0; - while (left && (recv = AUD_read(s->codec.in_voice, - (uint8_t *) s->codec.rxbuf + start, - left)) > 0) { /* Be defensive */ - start += recv; - left -= recv; - } - if (recv <= 0) - s->codec.rxavail = 0; - else - s->codec.rxavail -= start >> 2; - s->codec.rxlen += start >> 2; - } -} - -static inline void omap_eac_out_empty(struct omap_eac_s *s) -{ - int left = s->codec.txlen << 2; - int start = 0; - int sent = 1; - - while (left && (sent = AUD_write(s->codec.out_voice, - (uint8_t *) s->codec.txbuf + start, - left)) > 0) { /* Be defensive */ - start += sent; - left -= sent; - } - - if (!sent) { - s->codec.txavail = 0; - omap_eac_out_dmarequest_update(s); - } - - if (start) - s->codec.txlen = 0; -} - -static void omap_eac_in_cb(void *opaque, int avail_b) -{ - struct omap_eac_s *s = (struct omap_eac_s *) opaque; - - s->codec.rxavail = avail_b >> 2; - omap_eac_in_refill(s); - /* TODO: possibly discard current buffer if overrun */ - omap_eac_in_dmarequest_update(s); -} - -static void omap_eac_out_cb(void *opaque, int free_b) -{ - struct omap_eac_s *s = (struct omap_eac_s *) opaque; - - s->codec.txavail = free_b >> 2; - if (s->codec.txlen) - omap_eac_out_empty(s); - else - omap_eac_out_dmarequest_update(s); -} - -static void omap_eac_enable_update(struct omap_eac_s *s) -{ - s->codec.enable = !(s->codec.config[1] & 1) && /* EACPWD */ - (s->codec.config[1] & 2) && /* AUDEN */ - s->codec.hw_enable; -} - -static const int omap_eac_fsint[4] = { - 8000, - 11025, - 22050, - 44100, -}; - -static const int omap_eac_fsint2[8] = { - 8000, - 11025, - 22050, - 44100, - 48000, - 0, 0, 0, -}; - -static const int omap_eac_fsint3[16] = { - 8000, - 11025, - 16000, - 22050, - 24000, - 32000, - 44100, - 48000, - 0, 0, 0, 0, 0, 0, 0, 0, -}; - -static void omap_eac_rate_update(struct omap_eac_s *s) -{ - int fsint[3]; - - fsint[2] = (s->codec.config[3] >> 9) & 0xf; - fsint[1] = (s->codec.config[2] >> 0) & 0x7; - fsint[0] = (s->codec.config[0] >> 6) & 0x3; - if (fsint[2] < 0xf) - s->codec.rate = omap_eac_fsint3[fsint[2]]; - else if (fsint[1] < 0x7) - s->codec.rate = omap_eac_fsint2[fsint[1]]; - else - s->codec.rate = omap_eac_fsint[fsint[0]]; -} - -static void omap_eac_volume_update(struct omap_eac_s *s) -{ - /* TODO */ -} - -static void omap_eac_format_update(struct omap_eac_s *s) -{ - struct audsettings fmt; - - /* The hardware buffers at most one sample */ - if (s->codec.rxlen) - s->codec.rxlen = 1; - - if (s->codec.in_voice) { - AUD_set_active_in(s->codec.in_voice, 0); - AUD_close_in(&s->codec.card, s->codec.in_voice); - s->codec.in_voice = NULL; - } - if (s->codec.out_voice) { - omap_eac_out_empty(s); - AUD_set_active_out(s->codec.out_voice, 0); - AUD_close_out(&s->codec.card, s->codec.out_voice); - s->codec.out_voice = NULL; - s->codec.txavail = 0; - } - /* Discard what couldn't be written */ - s->codec.txlen = 0; - - omap_eac_enable_update(s); - if (!s->codec.enable) - return; - - omap_eac_rate_update(s); - fmt.endianness = ((s->codec.config[0] >> 8) & 1); /* LI_BI */ - fmt.nchannels = ((s->codec.config[0] >> 10) & 1) ? 2 : 1; /* MN_ST */ - fmt.freq = s->codec.rate; - /* TODO: signedness possibly depends on the CODEC hardware - or - * does I2S specify it? */ - /* All register writes are 16 bits so we we store 16-bit samples - * in the buffers regardless of AGCFR[B8_16] value. */ - fmt.fmt = AUD_FMT_U16; - - s->codec.in_voice = AUD_open_in(&s->codec.card, s->codec.in_voice, - "eac.codec.in", s, omap_eac_in_cb, &fmt); - s->codec.out_voice = AUD_open_out(&s->codec.card, s->codec.out_voice, - "eac.codec.out", s, omap_eac_out_cb, &fmt); - - omap_eac_volume_update(s); - - AUD_set_active_in(s->codec.in_voice, 1); - AUD_set_active_out(s->codec.out_voice, 1); -} - -static void omap_eac_reset(struct omap_eac_s *s) -{ - s->sysconfig = 0; - s->config[0] = 0x0c; - s->config[1] = 0x09; - s->config[2] = 0xab; - s->config[3] = 0x03; - s->control = 0x00; - s->address = 0x00; - s->data = 0x0000; - s->vtol = 0x00; - s->vtsl = 0x00; - s->mixer = 0x0000; - s->gain[0] = 0xe7e7; - s->gain[1] = 0x6767; - s->gain[2] = 0x6767; - s->gain[3] = 0x6767; - s->att = 0xce; - s->max[0] = 0; - s->max[1] = 0; - s->max[2] = 0; - s->max[3] = 0; - s->max[4] = 0; - s->max[5] = 0; - s->max[6] = 0; - - s->modem.control = 0x00; - s->modem.config = 0x0000; - s->bt.control = 0x00; - s->bt.config = 0x0000; - s->codec.config[0] = 0x0649; - s->codec.config[1] = 0x0000; - s->codec.config[2] = 0x0007; - s->codec.config[3] = 0x1ffc; - s->codec.rxoff = 0; - s->codec.rxlen = 0; - s->codec.txlen = 0; - s->codec.rxavail = 0; - s->codec.txavail = 0; - - omap_eac_format_update(s); - omap_eac_interrupt_update(s); -} - -static uint64_t omap_eac_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_eac_s *s = (struct omap_eac_s *) opaque; - uint32_t ret; - - if (size != 2) { - return omap_badwidth_read16(opaque, addr); - } - - switch (addr) { - case 0x000: /* CPCFR1 */ - return s->config[0]; - case 0x004: /* CPCFR2 */ - return s->config[1]; - case 0x008: /* CPCFR3 */ - return s->config[2]; - case 0x00c: /* CPCFR4 */ - return s->config[3]; - - case 0x010: /* CPTCTL */ - return s->control | ((s->codec.rxavail + s->codec.rxlen > 0) << 7) | - ((s->codec.txlen < s->codec.txavail) << 5); - - case 0x014: /* CPTTADR */ - return s->address; - case 0x018: /* CPTDATL */ - return s->data & 0xff; - case 0x01c: /* CPTDATH */ - return s->data >> 8; - case 0x020: /* CPTVSLL */ - return s->vtol; - case 0x024: /* CPTVSLH */ - return s->vtsl | (3 << 5); /* CRDY1 | CRDY2 */ - case 0x040: /* MPCTR */ - return s->modem.control; - case 0x044: /* MPMCCFR */ - return s->modem.config; - case 0x060: /* BPCTR */ - return s->bt.control; - case 0x064: /* BPMCCFR */ - return s->bt.config; - case 0x080: /* AMSCFR */ - return s->mixer; - case 0x084: /* AMVCTR */ - return s->gain[0]; - case 0x088: /* AM1VCTR */ - return s->gain[1]; - case 0x08c: /* AM2VCTR */ - return s->gain[2]; - case 0x090: /* AM3VCTR */ - return s->gain[3]; - case 0x094: /* ASTCTR */ - return s->att; - case 0x098: /* APD1LCR */ - return s->max[0]; - case 0x09c: /* APD1RCR */ - return s->max[1]; - case 0x0a0: /* APD2LCR */ - return s->max[2]; - case 0x0a4: /* APD2RCR */ - return s->max[3]; - case 0x0a8: /* APD3LCR */ - return s->max[4]; - case 0x0ac: /* APD3RCR */ - return s->max[5]; - case 0x0b0: /* APD4R */ - return s->max[6]; - case 0x0b4: /* ADWR */ - /* This should be write-only? Docs list it as read-only. */ - return 0x0000; - case 0x0b8: /* ADRDR */ - if (likely(s->codec.rxlen > 1)) { - ret = s->codec.rxbuf[s->codec.rxoff ++]; - s->codec.rxlen --; - s->codec.rxoff &= EAC_BUF_LEN - 1; - return ret; - } else if (s->codec.rxlen) { - ret = s->codec.rxbuf[s->codec.rxoff ++]; - s->codec.rxlen --; - s->codec.rxoff &= EAC_BUF_LEN - 1; - if (s->codec.rxavail) - omap_eac_in_refill(s); - omap_eac_in_dmarequest_update(s); - return ret; - } - return 0x0000; - case 0x0bc: /* AGCFR */ - return s->codec.config[0]; - case 0x0c0: /* AGCTR */ - return s->codec.config[1] | ((s->codec.config[1] & 2) << 14); - case 0x0c4: /* AGCFR2 */ - return s->codec.config[2]; - case 0x0c8: /* AGCFR3 */ - return s->codec.config[3]; - case 0x0cc: /* MBPDMACTR */ - case 0x0d0: /* MPDDMARR */ - case 0x0d8: /* MPUDMARR */ - case 0x0e4: /* BPDDMARR */ - case 0x0ec: /* BPUDMARR */ - return 0x0000; - - case 0x100: /* VERSION_NUMBER */ - return 0x0010; - - case 0x104: /* SYSCONFIG */ - return s->sysconfig; - - case 0x108: /* SYSSTATUS */ - return 1 | 0xe; /* RESETDONE | stuff */ - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_eac_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_eac_s *s = (struct omap_eac_s *) opaque; - - if (size != 2) { - omap_badwidth_write16(opaque, addr, value); - return; - } - - switch (addr) { - case 0x098: /* APD1LCR */ - case 0x09c: /* APD1RCR */ - case 0x0a0: /* APD2LCR */ - case 0x0a4: /* APD2RCR */ - case 0x0a8: /* APD3LCR */ - case 0x0ac: /* APD3RCR */ - case 0x0b0: /* APD4R */ - case 0x0b8: /* ADRDR */ - case 0x0d0: /* MPDDMARR */ - case 0x0d8: /* MPUDMARR */ - case 0x0e4: /* BPDDMARR */ - case 0x0ec: /* BPUDMARR */ - case 0x100: /* VERSION_NUMBER */ - case 0x108: /* SYSSTATUS */ - OMAP_RO_REG(addr); - return; - - case 0x000: /* CPCFR1 */ - s->config[0] = value & 0xff; - omap_eac_format_update(s); - break; - case 0x004: /* CPCFR2 */ - s->config[1] = value & 0xff; - omap_eac_format_update(s); - break; - case 0x008: /* CPCFR3 */ - s->config[2] = value & 0xff; - omap_eac_format_update(s); - break; - case 0x00c: /* CPCFR4 */ - s->config[3] = value & 0xff; - omap_eac_format_update(s); - break; - - case 0x010: /* CPTCTL */ - /* Assuming TXF and TXE bits are read-only... */ - s->control = value & 0x5f; - omap_eac_interrupt_update(s); - break; - - case 0x014: /* CPTTADR */ - s->address = value & 0xff; - break; - case 0x018: /* CPTDATL */ - s->data &= 0xff00; - s->data |= value & 0xff; - break; - case 0x01c: /* CPTDATH */ - s->data &= 0x00ff; - s->data |= value << 8; - break; - case 0x020: /* CPTVSLL */ - s->vtol = value & 0xf8; - break; - case 0x024: /* CPTVSLH */ - s->vtsl = value & 0x9f; - break; - case 0x040: /* MPCTR */ - s->modem.control = value & 0x8f; - break; - case 0x044: /* MPMCCFR */ - s->modem.config = value & 0x7fff; - break; - case 0x060: /* BPCTR */ - s->bt.control = value & 0x8f; - break; - case 0x064: /* BPMCCFR */ - s->bt.config = value & 0x7fff; - break; - case 0x080: /* AMSCFR */ - s->mixer = value & 0x0fff; - break; - case 0x084: /* AMVCTR */ - s->gain[0] = value & 0xffff; - break; - case 0x088: /* AM1VCTR */ - s->gain[1] = value & 0xff7f; - break; - case 0x08c: /* AM2VCTR */ - s->gain[2] = value & 0xff7f; - break; - case 0x090: /* AM3VCTR */ - s->gain[3] = value & 0xff7f; - break; - case 0x094: /* ASTCTR */ - s->att = value & 0xff; - break; - - case 0x0b4: /* ADWR */ - s->codec.txbuf[s->codec.txlen ++] = value; - if (unlikely(s->codec.txlen == EAC_BUF_LEN || - s->codec.txlen == s->codec.txavail)) { - if (s->codec.txavail) - omap_eac_out_empty(s); - /* Discard what couldn't be written */ - s->codec.txlen = 0; - } - break; - - case 0x0bc: /* AGCFR */ - s->codec.config[0] = value & 0x07ff; - omap_eac_format_update(s); - break; - case 0x0c0: /* AGCTR */ - s->codec.config[1] = value & 0x780f; - omap_eac_format_update(s); - break; - case 0x0c4: /* AGCFR2 */ - s->codec.config[2] = value & 0x003f; - omap_eac_format_update(s); - break; - case 0x0c8: /* AGCFR3 */ - s->codec.config[3] = value & 0xffff; - omap_eac_format_update(s); - break; - case 0x0cc: /* MBPDMACTR */ - case 0x0d4: /* MPDDMAWR */ - case 0x0e0: /* MPUDMAWR */ - case 0x0e8: /* BPDDMAWR */ - case 0x0f0: /* BPUDMAWR */ - break; - - case 0x104: /* SYSCONFIG */ - if (value & (1 << 1)) /* SOFTRESET */ - omap_eac_reset(s); - s->sysconfig = value & 0x31d; - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_eac_ops = { - .read = omap_eac_read, - .write = omap_eac_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -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 = g_new0(struct omap_eac_s, 1); - - s->irq = irq; - s->codec.rxdrq = *drq ++; - s->codec.txdrq = *drq; - omap_eac_reset(s); - - AUD_register_card("OMAP EAC", &s->codec.card); - - memory_region_init_io(&s->iomem, NULL, &omap_eac_ops, s, "omap.eac", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &s->iomem); - - return s; -} - -/* STI/XTI (emulation interface) console - reverse engineered only */ -struct omap_sti_s { - qemu_irq irq; - MemoryRegion iomem; - MemoryRegion iomem_fifo; - CharDriverState *chr; - - uint32_t sysconfig; - uint32_t systest; - uint32_t irqst; - uint32_t irqen; - uint32_t clkcontrol; - uint32_t serial_config; -}; - -#define STI_TRACE_CONSOLE_CHANNEL 239 -#define STI_TRACE_CONTROL_CHANNEL 253 - -static inline void omap_sti_interrupt_update(struct omap_sti_s *s) -{ - qemu_set_irq(s->irq, s->irqst & s->irqen); -} - -static void omap_sti_reset(struct omap_sti_s *s) -{ - s->sysconfig = 0; - s->irqst = 0; - s->irqen = 0; - s->clkcontrol = 0; - s->serial_config = 0; - - omap_sti_interrupt_update(s); -} - -static uint64_t omap_sti_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_sti_s *s = (struct omap_sti_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* STI_REVISION */ - return 0x10; - - case 0x10: /* STI_SYSCONFIG */ - return s->sysconfig; - - case 0x14: /* STI_SYSSTATUS / STI_RX_STATUS / XTI_SYSSTATUS */ - return 0x00; - - case 0x18: /* STI_IRQSTATUS */ - return s->irqst; - - case 0x1c: /* STI_IRQSETEN / STI_IRQCLREN */ - return s->irqen; - - case 0x24: /* STI_ER / STI_DR / XTI_TRACESELECT */ - case 0x28: /* STI_RX_DR / XTI_RXDATA */ - /* TODO */ - return 0; - - case 0x2c: /* STI_CLK_CTRL / XTI_SCLKCRTL */ - return s->clkcontrol; - - case 0x30: /* STI_SERIAL_CFG / XTI_SCONFIG */ - return s->serial_config; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_sti_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_sti_s *s = (struct omap_sti_s *) opaque; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x00: /* STI_REVISION */ - case 0x14: /* STI_SYSSTATUS / STI_RX_STATUS / XTI_SYSSTATUS */ - OMAP_RO_REG(addr); - return; - - case 0x10: /* STI_SYSCONFIG */ - if (value & (1 << 1)) /* SOFTRESET */ - omap_sti_reset(s); - s->sysconfig = value & 0xfe; - break; - - case 0x18: /* STI_IRQSTATUS */ - s->irqst &= ~value; - omap_sti_interrupt_update(s); - break; - - case 0x1c: /* STI_IRQSETEN / STI_IRQCLREN */ - s->irqen = value & 0xffff; - omap_sti_interrupt_update(s); - break; - - case 0x2c: /* STI_CLK_CTRL / XTI_SCLKCRTL */ - s->clkcontrol = value & 0xff; - break; - - case 0x30: /* STI_SERIAL_CFG / XTI_SCONFIG */ - s->serial_config = value & 0xff; - break; - - case 0x24: /* STI_ER / STI_DR / XTI_TRACESELECT */ - case 0x28: /* STI_RX_DR / XTI_RXDATA */ - /* TODO */ - return; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_sti_ops = { - .read = omap_sti_read, - .write = omap_sti_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t omap_sti_fifo_read(void *opaque, hwaddr addr, - unsigned size) -{ - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_sti_fifo_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_sti_s *s = (struct omap_sti_s *) opaque; - int ch = addr >> 6; - uint8_t byte = value; - - if (size != 1) { - omap_badwidth_write8(opaque, addr, size); - return; - } - - if (ch == STI_TRACE_CONTROL_CHANNEL) { - /* Flush channel value. */ - qemu_chr_fe_write(s->chr, (const uint8_t *) "\r", 1); - } else if (ch == STI_TRACE_CONSOLE_CHANNEL || 1) { - if (value == 0xc0 || value == 0xc3) { - /* Open channel ch. */ - } else if (value == 0x00) - qemu_chr_fe_write(s->chr, (const uint8_t *) "\n", 1); - else - qemu_chr_fe_write(s->chr, &byte, 1); - } -} - -static const MemoryRegionOps omap_sti_fifo_ops = { - .read = omap_sti_fifo_read, - .write = omap_sti_fifo_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta, - MemoryRegion *sysmem, - hwaddr channel_base, qemu_irq irq, omap_clk clk, - CharDriverState *chr) -{ - struct omap_sti_s *s = g_new0(struct omap_sti_s, 1); - - s->irq = irq; - omap_sti_reset(s); - - s->chr = chr ?: qemu_chr_new("null", "null", NULL); - - memory_region_init_io(&s->iomem, NULL, &omap_sti_ops, s, "omap.sti", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &s->iomem); - - memory_region_init_io(&s->iomem_fifo, NULL, &omap_sti_fifo_ops, s, - "omap.sti.fifo", 0x10000); - memory_region_add_subregion(sysmem, channel_base, &s->iomem_fifo); - - return s; -} - -/* L4 Interconnect */ -#define L4TA(n) (n) -#define L4TAO(n) ((n) + 39) - -static const struct omap_l4_region_s omap_l4_region[125] = { - [ 1] = { 0x40800, 0x800, 32 }, /* Initiator agent */ - [ 2] = { 0x41000, 0x1000, 32 }, /* Link agent */ - [ 0] = { 0x40000, 0x800, 32 }, /* Address and protection */ - [ 3] = { 0x00000, 0x1000, 32 | 16 | 8 }, /* System Control and Pinout */ - [ 4] = { 0x01000, 0x1000, 32 | 16 | 8 }, /* L4TAO1 */ - [ 5] = { 0x04000, 0x1000, 32 | 16 }, /* 32K Timer */ - [ 6] = { 0x05000, 0x1000, 32 | 16 | 8 }, /* L4TAO2 */ - [ 7] = { 0x08000, 0x800, 32 }, /* PRCM Region A */ - [ 8] = { 0x08800, 0x800, 32 }, /* PRCM Region B */ - [ 9] = { 0x09000, 0x1000, 32 | 16 | 8 }, /* L4TAO */ - [ 10] = { 0x12000, 0x1000, 32 | 16 | 8 }, /* Test (BCM) */ - [ 11] = { 0x13000, 0x1000, 32 | 16 | 8 }, /* L4TA1 */ - [ 12] = { 0x14000, 0x1000, 32 }, /* Test/emulation (TAP) */ - [ 13] = { 0x15000, 0x1000, 32 | 16 | 8 }, /* L4TA2 */ - [ 14] = { 0x18000, 0x1000, 32 | 16 | 8 }, /* GPIO1 */ - [ 16] = { 0x1a000, 0x1000, 32 | 16 | 8 }, /* GPIO2 */ - [ 18] = { 0x1c000, 0x1000, 32 | 16 | 8 }, /* GPIO3 */ - [ 19] = { 0x1e000, 0x1000, 32 | 16 | 8 }, /* GPIO4 */ - [ 15] = { 0x19000, 0x1000, 32 | 16 | 8 }, /* Quad GPIO TOP */ - [ 17] = { 0x1b000, 0x1000, 32 | 16 | 8 }, /* L4TA3 */ - [ 20] = { 0x20000, 0x1000, 32 | 16 | 8 }, /* WD Timer 1 (Secure) */ - [ 22] = { 0x22000, 0x1000, 32 | 16 | 8 }, /* WD Timer 2 (OMAP) */ - [ 21] = { 0x21000, 0x1000, 32 | 16 | 8 }, /* Dual WD timer TOP */ - [ 23] = { 0x23000, 0x1000, 32 | 16 | 8 }, /* L4TA4 */ - [ 24] = { 0x28000, 0x1000, 32 | 16 | 8 }, /* GP Timer 1 */ - [ 25] = { 0x29000, 0x1000, 32 | 16 | 8 }, /* L4TA7 */ - [ 26] = { 0x48000, 0x2000, 32 | 16 | 8 }, /* Emulation (ARM11ETB) */ - [ 27] = { 0x4a000, 0x1000, 32 | 16 | 8 }, /* L4TA9 */ - [ 28] = { 0x50000, 0x400, 32 | 16 | 8 }, /* Display top */ - [ 29] = { 0x50400, 0x400, 32 | 16 | 8 }, /* Display control */ - [ 30] = { 0x50800, 0x400, 32 | 16 | 8 }, /* Display RFBI */ - [ 31] = { 0x50c00, 0x400, 32 | 16 | 8 }, /* Display encoder */ - [ 32] = { 0x51000, 0x1000, 32 | 16 | 8 }, /* L4TA10 */ - [ 33] = { 0x52000, 0x400, 32 | 16 | 8 }, /* Camera top */ - [ 34] = { 0x52400, 0x400, 32 | 16 | 8 }, /* Camera core */ - [ 35] = { 0x52800, 0x400, 32 | 16 | 8 }, /* Camera DMA */ - [ 36] = { 0x52c00, 0x400, 32 | 16 | 8 }, /* Camera MMU */ - [ 37] = { 0x53000, 0x1000, 32 | 16 | 8 }, /* L4TA11 */ - [ 38] = { 0x56000, 0x1000, 32 | 16 | 8 }, /* sDMA */ - [ 39] = { 0x57000, 0x1000, 32 | 16 | 8 }, /* L4TA12 */ - [ 40] = { 0x58000, 0x1000, 32 | 16 | 8 }, /* SSI top */ - [ 41] = { 0x59000, 0x1000, 32 | 16 | 8 }, /* SSI GDD */ - [ 42] = { 0x5a000, 0x1000, 32 | 16 | 8 }, /* SSI Port1 */ - [ 43] = { 0x5b000, 0x1000, 32 | 16 | 8 }, /* SSI Port2 */ - [ 44] = { 0x5c000, 0x1000, 32 | 16 | 8 }, /* L4TA13 */ - [ 45] = { 0x5e000, 0x1000, 32 | 16 | 8 }, /* USB OTG */ - [ 46] = { 0x5f000, 0x1000, 32 | 16 | 8 }, /* L4TAO4 */ - [ 47] = { 0x60000, 0x1000, 32 | 16 | 8 }, /* Emulation (WIN_TRACER1SDRC) */ - [ 48] = { 0x61000, 0x1000, 32 | 16 | 8 }, /* L4TA14 */ - [ 49] = { 0x62000, 0x1000, 32 | 16 | 8 }, /* Emulation (WIN_TRACER2GPMC) */ - [ 50] = { 0x63000, 0x1000, 32 | 16 | 8 }, /* L4TA15 */ - [ 51] = { 0x64000, 0x1000, 32 | 16 | 8 }, /* Emulation (WIN_TRACER3OCM) */ - [ 52] = { 0x65000, 0x1000, 32 | 16 | 8 }, /* L4TA16 */ - [ 53] = { 0x66000, 0x300, 32 | 16 | 8 }, /* Emulation (WIN_TRACER4L4) */ - [ 54] = { 0x67000, 0x1000, 32 | 16 | 8 }, /* L4TA17 */ - [ 55] = { 0x68000, 0x1000, 32 | 16 | 8 }, /* Emulation (XTI) */ - [ 56] = { 0x69000, 0x1000, 32 | 16 | 8 }, /* L4TA18 */ - [ 57] = { 0x6a000, 0x1000, 16 | 8 }, /* UART1 */ - [ 58] = { 0x6b000, 0x1000, 32 | 16 | 8 }, /* L4TA19 */ - [ 59] = { 0x6c000, 0x1000, 16 | 8 }, /* UART2 */ - [ 60] = { 0x6d000, 0x1000, 32 | 16 | 8 }, /* L4TA20 */ - [ 61] = { 0x6e000, 0x1000, 16 | 8 }, /* UART3 */ - [ 62] = { 0x6f000, 0x1000, 32 | 16 | 8 }, /* L4TA21 */ - [ 63] = { 0x70000, 0x1000, 16 }, /* I2C1 */ - [ 64] = { 0x71000, 0x1000, 32 | 16 | 8 }, /* L4TAO5 */ - [ 65] = { 0x72000, 0x1000, 16 }, /* I2C2 */ - [ 66] = { 0x73000, 0x1000, 32 | 16 | 8 }, /* L4TAO6 */ - [ 67] = { 0x74000, 0x1000, 16 }, /* McBSP1 */ - [ 68] = { 0x75000, 0x1000, 32 | 16 | 8 }, /* L4TAO7 */ - [ 69] = { 0x76000, 0x1000, 16 }, /* McBSP2 */ - [ 70] = { 0x77000, 0x1000, 32 | 16 | 8 }, /* L4TAO8 */ - [ 71] = { 0x24000, 0x1000, 32 | 16 | 8 }, /* WD Timer 3 (DSP) */ - [ 72] = { 0x25000, 0x1000, 32 | 16 | 8 }, /* L4TA5 */ - [ 73] = { 0x26000, 0x1000, 32 | 16 | 8 }, /* WD Timer 4 (IVA) */ - [ 74] = { 0x27000, 0x1000, 32 | 16 | 8 }, /* L4TA6 */ - [ 75] = { 0x2a000, 0x1000, 32 | 16 | 8 }, /* GP Timer 2 */ - [ 76] = { 0x2b000, 0x1000, 32 | 16 | 8 }, /* L4TA8 */ - [ 77] = { 0x78000, 0x1000, 32 | 16 | 8 }, /* GP Timer 3 */ - [ 78] = { 0x79000, 0x1000, 32 | 16 | 8 }, /* L4TA22 */ - [ 79] = { 0x7a000, 0x1000, 32 | 16 | 8 }, /* GP Timer 4 */ - [ 80] = { 0x7b000, 0x1000, 32 | 16 | 8 }, /* L4TA23 */ - [ 81] = { 0x7c000, 0x1000, 32 | 16 | 8 }, /* GP Timer 5 */ - [ 82] = { 0x7d000, 0x1000, 32 | 16 | 8 }, /* L4TA24 */ - [ 83] = { 0x7e000, 0x1000, 32 | 16 | 8 }, /* GP Timer 6 */ - [ 84] = { 0x7f000, 0x1000, 32 | 16 | 8 }, /* L4TA25 */ - [ 85] = { 0x80000, 0x1000, 32 | 16 | 8 }, /* GP Timer 7 */ - [ 86] = { 0x81000, 0x1000, 32 | 16 | 8 }, /* L4TA26 */ - [ 87] = { 0x82000, 0x1000, 32 | 16 | 8 }, /* GP Timer 8 */ - [ 88] = { 0x83000, 0x1000, 32 | 16 | 8 }, /* L4TA27 */ - [ 89] = { 0x84000, 0x1000, 32 | 16 | 8 }, /* GP Timer 9 */ - [ 90] = { 0x85000, 0x1000, 32 | 16 | 8 }, /* L4TA28 */ - [ 91] = { 0x86000, 0x1000, 32 | 16 | 8 }, /* GP Timer 10 */ - [ 92] = { 0x87000, 0x1000, 32 | 16 | 8 }, /* L4TA29 */ - [ 93] = { 0x88000, 0x1000, 32 | 16 | 8 }, /* GP Timer 11 */ - [ 94] = { 0x89000, 0x1000, 32 | 16 | 8 }, /* L4TA30 */ - [ 95] = { 0x8a000, 0x1000, 32 | 16 | 8 }, /* GP Timer 12 */ - [ 96] = { 0x8b000, 0x1000, 32 | 16 | 8 }, /* L4TA31 */ - [ 97] = { 0x90000, 0x1000, 16 }, /* EAC */ - [ 98] = { 0x91000, 0x1000, 32 | 16 | 8 }, /* L4TA32 */ - [ 99] = { 0x92000, 0x1000, 16 }, /* FAC */ - [100] = { 0x93000, 0x1000, 32 | 16 | 8 }, /* L4TA33 */ - [101] = { 0x94000, 0x1000, 32 | 16 | 8 }, /* IPC (MAILBOX) */ - [102] = { 0x95000, 0x1000, 32 | 16 | 8 }, /* L4TA34 */ - [103] = { 0x98000, 0x1000, 32 | 16 | 8 }, /* SPI1 */ - [104] = { 0x99000, 0x1000, 32 | 16 | 8 }, /* L4TA35 */ - [105] = { 0x9a000, 0x1000, 32 | 16 | 8 }, /* SPI2 */ - [106] = { 0x9b000, 0x1000, 32 | 16 | 8 }, /* L4TA36 */ - [107] = { 0x9c000, 0x1000, 16 | 8 }, /* MMC SDIO */ - [108] = { 0x9d000, 0x1000, 32 | 16 | 8 }, /* L4TAO9 */ - [109] = { 0x9e000, 0x1000, 32 | 16 | 8 }, /* MS_PRO */ - [110] = { 0x9f000, 0x1000, 32 | 16 | 8 }, /* L4TAO10 */ - [111] = { 0xa0000, 0x1000, 32 }, /* RNG */ - [112] = { 0xa1000, 0x1000, 32 | 16 | 8 }, /* L4TAO11 */ - [113] = { 0xa2000, 0x1000, 32 }, /* DES3DES */ - [114] = { 0xa3000, 0x1000, 32 | 16 | 8 }, /* L4TAO12 */ - [115] = { 0xa4000, 0x1000, 32 }, /* SHA1MD5 */ - [116] = { 0xa5000, 0x1000, 32 | 16 | 8 }, /* L4TAO13 */ - [117] = { 0xa6000, 0x1000, 32 }, /* AES */ - [118] = { 0xa7000, 0x1000, 32 | 16 | 8 }, /* L4TA37 */ - [119] = { 0xa8000, 0x2000, 32 }, /* PKA */ - [120] = { 0xaa000, 0x1000, 32 | 16 | 8 }, /* L4TA38 */ - [121] = { 0xb0000, 0x1000, 32 }, /* MG */ - [122] = { 0xb1000, 0x1000, 32 | 16 | 8 }, - [123] = { 0xb2000, 0x1000, 32 }, /* HDQ/1-Wire */ - [124] = { 0xb3000, 0x1000, 32 | 16 | 8 }, /* L4TA39 */ -}; - -static const struct omap_l4_agent_info_s omap_l4_agent_info[54] = { - { 0, 0, 3, 2 }, /* L4IA initiatior agent */ - { L4TAO(1), 3, 2, 1 }, /* Control and pinout module */ - { L4TAO(2), 5, 2, 1 }, /* 32K timer */ - { L4TAO(3), 7, 3, 2 }, /* PRCM */ - { L4TA(1), 10, 2, 1 }, /* BCM */ - { L4TA(2), 12, 2, 1 }, /* Test JTAG */ - { L4TA(3), 14, 6, 3 }, /* Quad GPIO */ - { L4TA(4), 20, 4, 3 }, /* WD timer 1/2 */ - { L4TA(7), 24, 2, 1 }, /* GP timer 1 */ - { L4TA(9), 26, 2, 1 }, /* ATM11 ETB */ - { L4TA(10), 28, 5, 4 }, /* Display subsystem */ - { L4TA(11), 33, 5, 4 }, /* Camera subsystem */ - { L4TA(12), 38, 2, 1 }, /* sDMA */ - { L4TA(13), 40, 5, 4 }, /* SSI */ - { L4TAO(4), 45, 2, 1 }, /* USB */ - { L4TA(14), 47, 2, 1 }, /* Win Tracer1 */ - { L4TA(15), 49, 2, 1 }, /* Win Tracer2 */ - { L4TA(16), 51, 2, 1 }, /* Win Tracer3 */ - { L4TA(17), 53, 2, 1 }, /* Win Tracer4 */ - { L4TA(18), 55, 2, 1 }, /* XTI */ - { L4TA(19), 57, 2, 1 }, /* UART1 */ - { L4TA(20), 59, 2, 1 }, /* UART2 */ - { L4TA(21), 61, 2, 1 }, /* UART3 */ - { L4TAO(5), 63, 2, 1 }, /* I2C1 */ - { L4TAO(6), 65, 2, 1 }, /* I2C2 */ - { L4TAO(7), 67, 2, 1 }, /* McBSP1 */ - { L4TAO(8), 69, 2, 1 }, /* McBSP2 */ - { L4TA(5), 71, 2, 1 }, /* WD Timer 3 (DSP) */ - { L4TA(6), 73, 2, 1 }, /* WD Timer 4 (IVA) */ - { L4TA(8), 75, 2, 1 }, /* GP Timer 2 */ - { L4TA(22), 77, 2, 1 }, /* GP Timer 3 */ - { L4TA(23), 79, 2, 1 }, /* GP Timer 4 */ - { L4TA(24), 81, 2, 1 }, /* GP Timer 5 */ - { L4TA(25), 83, 2, 1 }, /* GP Timer 6 */ - { L4TA(26), 85, 2, 1 }, /* GP Timer 7 */ - { L4TA(27), 87, 2, 1 }, /* GP Timer 8 */ - { L4TA(28), 89, 2, 1 }, /* GP Timer 9 */ - { L4TA(29), 91, 2, 1 }, /* GP Timer 10 */ - { L4TA(30), 93, 2, 1 }, /* GP Timer 11 */ - { L4TA(31), 95, 2, 1 }, /* GP Timer 12 */ - { L4TA(32), 97, 2, 1 }, /* EAC */ - { L4TA(33), 99, 2, 1 }, /* FAC */ - { L4TA(34), 101, 2, 1 }, /* IPC */ - { L4TA(35), 103, 2, 1 }, /* SPI1 */ - { L4TA(36), 105, 2, 1 }, /* SPI2 */ - { L4TAO(9), 107, 2, 1 }, /* MMC SDIO */ - { L4TAO(10), 109, 2, 1 }, - { L4TAO(11), 111, 2, 1 }, /* RNG */ - { L4TAO(12), 113, 2, 1 }, /* DES3DES */ - { L4TAO(13), 115, 2, 1 }, /* SHA1MD5 */ - { L4TA(37), 117, 2, 1 }, /* AES */ - { L4TA(38), 119, 2, 1 }, /* PKA */ - { -1, 121, 2, 1 }, - { L4TA(39), 123, 2, 1 }, /* HDQ/1-Wire */ -}; - -#define omap_l4ta(bus, cs) \ - omap_l4ta_get(bus, omap_l4_region, omap_l4_agent_info, L4TA(cs)) -#define omap_l4tao(bus, cs) \ - omap_l4ta_get(bus, omap_l4_region, omap_l4_agent_info, L4TAO(cs)) - -/* Power, Reset, and Clock Management */ -struct omap_prcm_s { - qemu_irq irq[3]; - struct omap_mpu_state_s *mpu; - MemoryRegion iomem0; - MemoryRegion iomem1; - - uint32_t irqst[3]; - uint32_t irqen[3]; - - uint32_t sysconfig; - uint32_t voltctrl; - uint32_t scratch[20]; - - uint32_t clksrc[1]; - uint32_t clkout[1]; - uint32_t clkemul[1]; - uint32_t clkpol[1]; - uint32_t clksel[8]; - uint32_t clken[12]; - uint32_t clkctrl[4]; - uint32_t clkidle[7]; - uint32_t setuptime[2]; - - uint32_t wkup[3]; - uint32_t wken[3]; - uint32_t wkst[3]; - uint32_t rst[4]; - uint32_t rstctrl[1]; - uint32_t power[4]; - uint32_t rsttime_wkup; - - uint32_t ev; - uint32_t evtime[2]; - - int dpll_lock, apll_lock[2]; -}; - -static void omap_prcm_int_update(struct omap_prcm_s *s, int dom) -{ - qemu_set_irq(s->irq[dom], s->irqst[dom] & s->irqen[dom]); - /* XXX or is the mask applied before PRCM_IRQSTATUS_* ? */ -} - -static uint64_t omap_prcm_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_prcm_s *s = (struct omap_prcm_s *) opaque; - uint32_t ret; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x000: /* PRCM_REVISION */ - return 0x10; - - case 0x010: /* PRCM_SYSCONFIG */ - return s->sysconfig; - - case 0x018: /* PRCM_IRQSTATUS_MPU */ - return s->irqst[0]; - - case 0x01c: /* PRCM_IRQENABLE_MPU */ - return s->irqen[0]; - - case 0x050: /* PRCM_VOLTCTRL */ - return s->voltctrl; - case 0x054: /* PRCM_VOLTST */ - return s->voltctrl & 3; - - case 0x060: /* PRCM_CLKSRC_CTRL */ - return s->clksrc[0]; - case 0x070: /* PRCM_CLKOUT_CTRL */ - return s->clkout[0]; - case 0x078: /* PRCM_CLKEMUL_CTRL */ - return s->clkemul[0]; - case 0x080: /* PRCM_CLKCFG_CTRL */ - case 0x084: /* PRCM_CLKCFG_STATUS */ - return 0; - - case 0x090: /* PRCM_VOLTSETUP */ - return s->setuptime[0]; - - case 0x094: /* PRCM_CLKSSETUP */ - return s->setuptime[1]; - - case 0x098: /* PRCM_POLCTRL */ - return s->clkpol[0]; - - case 0x0b0: /* GENERAL_PURPOSE1 */ - case 0x0b4: /* GENERAL_PURPOSE2 */ - case 0x0b8: /* GENERAL_PURPOSE3 */ - case 0x0bc: /* GENERAL_PURPOSE4 */ - case 0x0c0: /* GENERAL_PURPOSE5 */ - case 0x0c4: /* GENERAL_PURPOSE6 */ - case 0x0c8: /* GENERAL_PURPOSE7 */ - case 0x0cc: /* GENERAL_PURPOSE8 */ - case 0x0d0: /* GENERAL_PURPOSE9 */ - case 0x0d4: /* GENERAL_PURPOSE10 */ - case 0x0d8: /* GENERAL_PURPOSE11 */ - case 0x0dc: /* GENERAL_PURPOSE12 */ - case 0x0e0: /* GENERAL_PURPOSE13 */ - case 0x0e4: /* GENERAL_PURPOSE14 */ - case 0x0e8: /* GENERAL_PURPOSE15 */ - case 0x0ec: /* GENERAL_PURPOSE16 */ - case 0x0f0: /* GENERAL_PURPOSE17 */ - case 0x0f4: /* GENERAL_PURPOSE18 */ - case 0x0f8: /* GENERAL_PURPOSE19 */ - case 0x0fc: /* GENERAL_PURPOSE20 */ - return s->scratch[(addr - 0xb0) >> 2]; - - case 0x140: /* CM_CLKSEL_MPU */ - return s->clksel[0]; - case 0x148: /* CM_CLKSTCTRL_MPU */ - return s->clkctrl[0]; - - case 0x158: /* RM_RSTST_MPU */ - return s->rst[0]; - case 0x1c8: /* PM_WKDEP_MPU */ - return s->wkup[0]; - case 0x1d4: /* PM_EVGENCTRL_MPU */ - return s->ev; - case 0x1d8: /* PM_EVEGENONTIM_MPU */ - return s->evtime[0]; - case 0x1dc: /* PM_EVEGENOFFTIM_MPU */ - return s->evtime[1]; - case 0x1e0: /* PM_PWSTCTRL_MPU */ - return s->power[0]; - case 0x1e4: /* PM_PWSTST_MPU */ - return 0; - - case 0x200: /* CM_FCLKEN1_CORE */ - return s->clken[0]; - case 0x204: /* CM_FCLKEN2_CORE */ - return s->clken[1]; - case 0x210: /* CM_ICLKEN1_CORE */ - return s->clken[2]; - case 0x214: /* CM_ICLKEN2_CORE */ - return s->clken[3]; - case 0x21c: /* CM_ICLKEN4_CORE */ - return s->clken[4]; - - case 0x220: /* CM_IDLEST1_CORE */ - /* TODO: check the actual iclk status */ - return 0x7ffffff9; - case 0x224: /* CM_IDLEST2_CORE */ - /* TODO: check the actual iclk status */ - return 0x00000007; - case 0x22c: /* CM_IDLEST4_CORE */ - /* TODO: check the actual iclk status */ - return 0x0000001f; - - case 0x230: /* CM_AUTOIDLE1_CORE */ - return s->clkidle[0]; - case 0x234: /* CM_AUTOIDLE2_CORE */ - return s->clkidle[1]; - case 0x238: /* CM_AUTOIDLE3_CORE */ - return s->clkidle[2]; - case 0x23c: /* CM_AUTOIDLE4_CORE */ - return s->clkidle[3]; - - case 0x240: /* CM_CLKSEL1_CORE */ - return s->clksel[1]; - case 0x244: /* CM_CLKSEL2_CORE */ - return s->clksel[2]; - - case 0x248: /* CM_CLKSTCTRL_CORE */ - return s->clkctrl[1]; - - case 0x2a0: /* PM_WKEN1_CORE */ - return s->wken[0]; - case 0x2a4: /* PM_WKEN2_CORE */ - return s->wken[1]; - - case 0x2b0: /* PM_WKST1_CORE */ - return s->wkst[0]; - case 0x2b4: /* PM_WKST2_CORE */ - return s->wkst[1]; - case 0x2c8: /* PM_WKDEP_CORE */ - return 0x1e; - - case 0x2e0: /* PM_PWSTCTRL_CORE */ - return s->power[1]; - case 0x2e4: /* PM_PWSTST_CORE */ - return 0x000030 | (s->power[1] & 0xfc00); - - case 0x300: /* CM_FCLKEN_GFX */ - return s->clken[5]; - case 0x310: /* CM_ICLKEN_GFX */ - return s->clken[6]; - case 0x320: /* CM_IDLEST_GFX */ - /* TODO: check the actual iclk status */ - return 0x00000001; - case 0x340: /* CM_CLKSEL_GFX */ - return s->clksel[3]; - case 0x348: /* CM_CLKSTCTRL_GFX */ - return s->clkctrl[2]; - case 0x350: /* RM_RSTCTRL_GFX */ - return s->rstctrl[0]; - case 0x358: /* RM_RSTST_GFX */ - return s->rst[1]; - case 0x3c8: /* PM_WKDEP_GFX */ - return s->wkup[1]; - - case 0x3e0: /* PM_PWSTCTRL_GFX */ - return s->power[2]; - case 0x3e4: /* PM_PWSTST_GFX */ - return s->power[2] & 3; - - case 0x400: /* CM_FCLKEN_WKUP */ - return s->clken[7]; - case 0x410: /* CM_ICLKEN_WKUP */ - return s->clken[8]; - case 0x420: /* CM_IDLEST_WKUP */ - /* TODO: check the actual iclk status */ - return 0x0000003f; - case 0x430: /* CM_AUTOIDLE_WKUP */ - return s->clkidle[4]; - case 0x440: /* CM_CLKSEL_WKUP */ - return s->clksel[4]; - case 0x450: /* RM_RSTCTRL_WKUP */ - return 0; - case 0x454: /* RM_RSTTIME_WKUP */ - return s->rsttime_wkup; - case 0x458: /* RM_RSTST_WKUP */ - return s->rst[2]; - case 0x4a0: /* PM_WKEN_WKUP */ - return s->wken[2]; - case 0x4b0: /* PM_WKST_WKUP */ - return s->wkst[2]; - - case 0x500: /* CM_CLKEN_PLL */ - return s->clken[9]; - case 0x520: /* CM_IDLEST_CKGEN */ - ret = 0x0000070 | (s->apll_lock[0] << 9) | (s->apll_lock[1] << 8); - if (!(s->clksel[6] & 3)) - /* Core uses 32-kHz clock */ - ret |= 3 << 0; - else if (!s->dpll_lock) - /* DPLL not locked, core uses ref_clk */ - ret |= 1 << 0; - else - /* Core uses DPLL */ - ret |= 2 << 0; - return ret; - case 0x530: /* CM_AUTOIDLE_PLL */ - return s->clkidle[5]; - case 0x540: /* CM_CLKSEL1_PLL */ - return s->clksel[5]; - case 0x544: /* CM_CLKSEL2_PLL */ - return s->clksel[6]; - - case 0x800: /* CM_FCLKEN_DSP */ - return s->clken[10]; - case 0x810: /* CM_ICLKEN_DSP */ - return s->clken[11]; - case 0x820: /* CM_IDLEST_DSP */ - /* TODO: check the actual iclk status */ - return 0x00000103; - case 0x830: /* CM_AUTOIDLE_DSP */ - return s->clkidle[6]; - case 0x840: /* CM_CLKSEL_DSP */ - return s->clksel[7]; - case 0x848: /* CM_CLKSTCTRL_DSP */ - return s->clkctrl[3]; - case 0x850: /* RM_RSTCTRL_DSP */ - return 0; - case 0x858: /* RM_RSTST_DSP */ - return s->rst[3]; - case 0x8c8: /* PM_WKDEP_DSP */ - return s->wkup[2]; - case 0x8e0: /* PM_PWSTCTRL_DSP */ - return s->power[3]; - case 0x8e4: /* PM_PWSTST_DSP */ - return 0x008030 | (s->power[3] & 0x3003); - - case 0x8f0: /* PRCM_IRQSTATUS_DSP */ - return s->irqst[1]; - case 0x8f4: /* PRCM_IRQENABLE_DSP */ - return s->irqen[1]; - - case 0x8f8: /* PRCM_IRQSTATUS_IVA */ - return s->irqst[2]; - case 0x8fc: /* PRCM_IRQENABLE_IVA */ - return s->irqen[2]; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_prcm_apll_update(struct omap_prcm_s *s) -{ - int mode[2]; - - mode[0] = (s->clken[9] >> 6) & 3; - s->apll_lock[0] = (mode[0] == 3); - mode[1] = (s->clken[9] >> 2) & 3; - s->apll_lock[1] = (mode[1] == 3); - /* TODO: update clocks */ - - if (mode[0] == 1 || mode[0] == 2 || mode[1] == 1 || mode[1] == 2) - fprintf(stderr, "%s: bad EN_54M_PLL or bad EN_96M_PLL\n", - __FUNCTION__); -} - -static void omap_prcm_dpll_update(struct omap_prcm_s *s) -{ - omap_clk dpll = omap_findclk(s->mpu, "dpll"); - omap_clk dpll_x2 = omap_findclk(s->mpu, "dpll"); - omap_clk core = omap_findclk(s->mpu, "core_clk"); - int mode = (s->clken[9] >> 0) & 3; - int mult, div; - - mult = (s->clksel[5] >> 12) & 0x3ff; - div = (s->clksel[5] >> 8) & 0xf; - if (mult == 0 || mult == 1) - mode = 1; /* Bypass */ - - s->dpll_lock = 0; - switch (mode) { - case 0: - fprintf(stderr, "%s: bad EN_DPLL\n", __FUNCTION__); - break; - case 1: /* Low-power bypass mode (Default) */ - case 2: /* Fast-relock bypass mode */ - omap_clk_setrate(dpll, 1, 1); - omap_clk_setrate(dpll_x2, 1, 1); - break; - case 3: /* Lock mode */ - s->dpll_lock = 1; /* After 20 FINT cycles (ref_clk / (div + 1)). */ - - omap_clk_setrate(dpll, div + 1, mult); - omap_clk_setrate(dpll_x2, div + 1, mult * 2); - break; - } - - switch ((s->clksel[6] >> 0) & 3) { - case 0: - omap_clk_reparent(core, omap_findclk(s->mpu, "clk32-kHz")); - break; - case 1: - omap_clk_reparent(core, dpll); - break; - case 2: - /* Default */ - omap_clk_reparent(core, dpll_x2); - break; - case 3: - fprintf(stderr, "%s: bad CORE_CLK_SRC\n", __FUNCTION__); - break; - } -} - -static void omap_prcm_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_prcm_s *s = (struct omap_prcm_s *) opaque; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x000: /* PRCM_REVISION */ - case 0x054: /* PRCM_VOLTST */ - case 0x084: /* PRCM_CLKCFG_STATUS */ - case 0x1e4: /* PM_PWSTST_MPU */ - case 0x220: /* CM_IDLEST1_CORE */ - case 0x224: /* CM_IDLEST2_CORE */ - case 0x22c: /* CM_IDLEST4_CORE */ - case 0x2c8: /* PM_WKDEP_CORE */ - case 0x2e4: /* PM_PWSTST_CORE */ - case 0x320: /* CM_IDLEST_GFX */ - case 0x3e4: /* PM_PWSTST_GFX */ - case 0x420: /* CM_IDLEST_WKUP */ - case 0x520: /* CM_IDLEST_CKGEN */ - case 0x820: /* CM_IDLEST_DSP */ - case 0x8e4: /* PM_PWSTST_DSP */ - OMAP_RO_REG(addr); - return; - - case 0x010: /* PRCM_SYSCONFIG */ - s->sysconfig = value & 1; - break; - - case 0x018: /* PRCM_IRQSTATUS_MPU */ - s->irqst[0] &= ~value; - omap_prcm_int_update(s, 0); - break; - case 0x01c: /* PRCM_IRQENABLE_MPU */ - s->irqen[0] = value & 0x3f; - omap_prcm_int_update(s, 0); - break; - - case 0x050: /* PRCM_VOLTCTRL */ - s->voltctrl = value & 0xf1c3; - break; - - case 0x060: /* PRCM_CLKSRC_CTRL */ - s->clksrc[0] = value & 0xdb; - /* TODO update clocks */ - break; - - case 0x070: /* PRCM_CLKOUT_CTRL */ - s->clkout[0] = value & 0xbbbb; - /* TODO update clocks */ - break; - - case 0x078: /* PRCM_CLKEMUL_CTRL */ - s->clkemul[0] = value & 1; - /* TODO update clocks */ - break; - - case 0x080: /* PRCM_CLKCFG_CTRL */ - break; - - case 0x090: /* PRCM_VOLTSETUP */ - s->setuptime[0] = value & 0xffff; - break; - case 0x094: /* PRCM_CLKSSETUP */ - s->setuptime[1] = value & 0xffff; - break; - - case 0x098: /* PRCM_POLCTRL */ - s->clkpol[0] = value & 0x701; - break; - - case 0x0b0: /* GENERAL_PURPOSE1 */ - case 0x0b4: /* GENERAL_PURPOSE2 */ - case 0x0b8: /* GENERAL_PURPOSE3 */ - case 0x0bc: /* GENERAL_PURPOSE4 */ - case 0x0c0: /* GENERAL_PURPOSE5 */ - case 0x0c4: /* GENERAL_PURPOSE6 */ - case 0x0c8: /* GENERAL_PURPOSE7 */ - case 0x0cc: /* GENERAL_PURPOSE8 */ - case 0x0d0: /* GENERAL_PURPOSE9 */ - case 0x0d4: /* GENERAL_PURPOSE10 */ - case 0x0d8: /* GENERAL_PURPOSE11 */ - case 0x0dc: /* GENERAL_PURPOSE12 */ - case 0x0e0: /* GENERAL_PURPOSE13 */ - case 0x0e4: /* GENERAL_PURPOSE14 */ - case 0x0e8: /* GENERAL_PURPOSE15 */ - case 0x0ec: /* GENERAL_PURPOSE16 */ - case 0x0f0: /* GENERAL_PURPOSE17 */ - case 0x0f4: /* GENERAL_PURPOSE18 */ - case 0x0f8: /* GENERAL_PURPOSE19 */ - case 0x0fc: /* GENERAL_PURPOSE20 */ - s->scratch[(addr - 0xb0) >> 2] = value; - break; - - case 0x140: /* CM_CLKSEL_MPU */ - s->clksel[0] = value & 0x1f; - /* TODO update clocks */ - break; - case 0x148: /* CM_CLKSTCTRL_MPU */ - s->clkctrl[0] = value & 0x1f; - break; - - case 0x158: /* RM_RSTST_MPU */ - s->rst[0] &= ~value; - break; - case 0x1c8: /* PM_WKDEP_MPU */ - s->wkup[0] = value & 0x15; - break; - - case 0x1d4: /* PM_EVGENCTRL_MPU */ - s->ev = value & 0x1f; - break; - case 0x1d8: /* PM_EVEGENONTIM_MPU */ - s->evtime[0] = value; - break; - case 0x1dc: /* PM_EVEGENOFFTIM_MPU */ - s->evtime[1] = value; - break; - - case 0x1e0: /* PM_PWSTCTRL_MPU */ - s->power[0] = value & 0xc0f; - break; - - case 0x200: /* CM_FCLKEN1_CORE */ - s->clken[0] = value & 0xbfffffff; - /* TODO update clocks */ - /* The EN_EAC bit only gets/puts func_96m_clk. */ - break; - case 0x204: /* CM_FCLKEN2_CORE */ - s->clken[1] = value & 0x00000007; - /* TODO update clocks */ - break; - case 0x210: /* CM_ICLKEN1_CORE */ - s->clken[2] = value & 0xfffffff9; - /* TODO update clocks */ - /* The EN_EAC bit only gets/puts core_l4_iclk. */ - break; - case 0x214: /* CM_ICLKEN2_CORE */ - s->clken[3] = value & 0x00000007; - /* TODO update clocks */ - break; - case 0x21c: /* CM_ICLKEN4_CORE */ - s->clken[4] = value & 0x0000001f; - /* TODO update clocks */ - break; - - case 0x230: /* CM_AUTOIDLE1_CORE */ - s->clkidle[0] = value & 0xfffffff9; - /* TODO update clocks */ - break; - case 0x234: /* CM_AUTOIDLE2_CORE */ - s->clkidle[1] = value & 0x00000007; - /* TODO update clocks */ - break; - case 0x238: /* CM_AUTOIDLE3_CORE */ - s->clkidle[2] = value & 0x00000007; - /* TODO update clocks */ - break; - case 0x23c: /* CM_AUTOIDLE4_CORE */ - s->clkidle[3] = value & 0x0000001f; - /* TODO update clocks */ - break; - - case 0x240: /* CM_CLKSEL1_CORE */ - s->clksel[1] = value & 0x0fffbf7f; - /* TODO update clocks */ - break; - - case 0x244: /* CM_CLKSEL2_CORE */ - s->clksel[2] = value & 0x00fffffc; - /* TODO update clocks */ - break; - - case 0x248: /* CM_CLKSTCTRL_CORE */ - s->clkctrl[1] = value & 0x7; - break; - - case 0x2a0: /* PM_WKEN1_CORE */ - s->wken[0] = value & 0x04667ff8; - break; - case 0x2a4: /* PM_WKEN2_CORE */ - s->wken[1] = value & 0x00000005; - break; - - case 0x2b0: /* PM_WKST1_CORE */ - s->wkst[0] &= ~value; - break; - case 0x2b4: /* PM_WKST2_CORE */ - s->wkst[1] &= ~value; - break; - - case 0x2e0: /* PM_PWSTCTRL_CORE */ - s->power[1] = (value & 0x00fc3f) | (1 << 2); - break; - - case 0x300: /* CM_FCLKEN_GFX */ - s->clken[5] = value & 6; - /* TODO update clocks */ - break; - case 0x310: /* CM_ICLKEN_GFX */ - s->clken[6] = value & 1; - /* TODO update clocks */ - break; - case 0x340: /* CM_CLKSEL_GFX */ - s->clksel[3] = value & 7; - /* TODO update clocks */ - break; - case 0x348: /* CM_CLKSTCTRL_GFX */ - s->clkctrl[2] = value & 1; - break; - case 0x350: /* RM_RSTCTRL_GFX */ - s->rstctrl[0] = value & 1; - /* TODO: reset */ - break; - case 0x358: /* RM_RSTST_GFX */ - s->rst[1] &= ~value; - break; - case 0x3c8: /* PM_WKDEP_GFX */ - s->wkup[1] = value & 0x13; - break; - case 0x3e0: /* PM_PWSTCTRL_GFX */ - s->power[2] = (value & 0x00c0f) | (3 << 2); - break; - - case 0x400: /* CM_FCLKEN_WKUP */ - s->clken[7] = value & 0xd; - /* TODO update clocks */ - break; - case 0x410: /* CM_ICLKEN_WKUP */ - s->clken[8] = value & 0x3f; - /* TODO update clocks */ - break; - case 0x430: /* CM_AUTOIDLE_WKUP */ - s->clkidle[4] = value & 0x0000003f; - /* TODO update clocks */ - break; - case 0x440: /* CM_CLKSEL_WKUP */ - s->clksel[4] = value & 3; - /* TODO update clocks */ - break; - case 0x450: /* RM_RSTCTRL_WKUP */ - /* TODO: reset */ - if (value & 2) - qemu_system_reset_request(); - break; - case 0x454: /* RM_RSTTIME_WKUP */ - s->rsttime_wkup = value & 0x1fff; - break; - case 0x458: /* RM_RSTST_WKUP */ - s->rst[2] &= ~value; - break; - case 0x4a0: /* PM_WKEN_WKUP */ - s->wken[2] = value & 0x00000005; - break; - case 0x4b0: /* PM_WKST_WKUP */ - s->wkst[2] &= ~value; - break; - - case 0x500: /* CM_CLKEN_PLL */ - if (value & 0xffffff30) - fprintf(stderr, "%s: write 0s in CM_CLKEN_PLL for " - "future compatibility\n", __FUNCTION__); - if ((s->clken[9] ^ value) & 0xcc) { - s->clken[9] &= ~0xcc; - s->clken[9] |= value & 0xcc; - omap_prcm_apll_update(s); - } - if ((s->clken[9] ^ value) & 3) { - s->clken[9] &= ~3; - s->clken[9] |= value & 3; - omap_prcm_dpll_update(s); - } - break; - case 0x530: /* CM_AUTOIDLE_PLL */ - s->clkidle[5] = value & 0x000000cf; - /* TODO update clocks */ - break; - case 0x540: /* CM_CLKSEL1_PLL */ - if (value & 0xfc4000d7) - fprintf(stderr, "%s: write 0s in CM_CLKSEL1_PLL for " - "future compatibility\n", __FUNCTION__); - if ((s->clksel[5] ^ value) & 0x003fff00) { - s->clksel[5] = value & 0x03bfff28; - omap_prcm_dpll_update(s); - } - /* TODO update the other clocks */ - - s->clksel[5] = value & 0x03bfff28; - break; - case 0x544: /* CM_CLKSEL2_PLL */ - if (value & ~3) - fprintf(stderr, "%s: write 0s in CM_CLKSEL2_PLL[31:2] for " - "future compatibility\n", __FUNCTION__); - if (s->clksel[6] != (value & 3)) { - s->clksel[6] = value & 3; - omap_prcm_dpll_update(s); - } - break; - - case 0x800: /* CM_FCLKEN_DSP */ - s->clken[10] = value & 0x501; - /* TODO update clocks */ - break; - case 0x810: /* CM_ICLKEN_DSP */ - s->clken[11] = value & 0x2; - /* TODO update clocks */ - break; - case 0x830: /* CM_AUTOIDLE_DSP */ - s->clkidle[6] = value & 0x2; - /* TODO update clocks */ - break; - case 0x840: /* CM_CLKSEL_DSP */ - s->clksel[7] = value & 0x3fff; - /* TODO update clocks */ - break; - case 0x848: /* CM_CLKSTCTRL_DSP */ - s->clkctrl[3] = value & 0x101; - break; - case 0x850: /* RM_RSTCTRL_DSP */ - /* TODO: reset */ - break; - case 0x858: /* RM_RSTST_DSP */ - s->rst[3] &= ~value; - break; - case 0x8c8: /* PM_WKDEP_DSP */ - s->wkup[2] = value & 0x13; - break; - case 0x8e0: /* PM_PWSTCTRL_DSP */ - s->power[3] = (value & 0x03017) | (3 << 2); - break; - - case 0x8f0: /* PRCM_IRQSTATUS_DSP */ - s->irqst[1] &= ~value; - omap_prcm_int_update(s, 1); - break; - case 0x8f4: /* PRCM_IRQENABLE_DSP */ - s->irqen[1] = value & 0x7; - omap_prcm_int_update(s, 1); - break; - - case 0x8f8: /* PRCM_IRQSTATUS_IVA */ - s->irqst[2] &= ~value; - omap_prcm_int_update(s, 2); - break; - case 0x8fc: /* PRCM_IRQENABLE_IVA */ - s->irqen[2] = value & 0x7; - omap_prcm_int_update(s, 2); - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_prcm_ops = { - .read = omap_prcm_read, - .write = omap_prcm_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_prcm_reset(struct omap_prcm_s *s) -{ - s->sysconfig = 0; - s->irqst[0] = 0; - s->irqst[1] = 0; - s->irqst[2] = 0; - s->irqen[0] = 0; - s->irqen[1] = 0; - s->irqen[2] = 0; - s->voltctrl = 0x1040; - s->ev = 0x14; - s->evtime[0] = 0; - s->evtime[1] = 0; - s->clkctrl[0] = 0; - s->clkctrl[1] = 0; - s->clkctrl[2] = 0; - s->clkctrl[3] = 0; - s->clken[1] = 7; - s->clken[3] = 7; - s->clken[4] = 0; - s->clken[5] = 0; - s->clken[6] = 0; - s->clken[7] = 0xc; - s->clken[8] = 0x3e; - s->clken[9] = 0x0d; - s->clken[10] = 0; - s->clken[11] = 0; - s->clkidle[0] = 0; - s->clkidle[2] = 7; - s->clkidle[3] = 0; - s->clkidle[4] = 0; - s->clkidle[5] = 0x0c; - s->clkidle[6] = 0; - s->clksel[0] = 0x01; - s->clksel[1] = 0x02100121; - s->clksel[2] = 0x00000000; - s->clksel[3] = 0x01; - s->clksel[4] = 0; - s->clksel[7] = 0x0121; - s->wkup[0] = 0x15; - s->wkup[1] = 0x13; - s->wkup[2] = 0x13; - s->wken[0] = 0x04667ff8; - s->wken[1] = 0x00000005; - s->wken[2] = 5; - s->wkst[0] = 0; - s->wkst[1] = 0; - s->wkst[2] = 0; - s->power[0] = 0x00c; - s->power[1] = 4; - s->power[2] = 0x0000c; - s->power[3] = 0x14; - s->rstctrl[0] = 1; - s->rst[3] = 1; - omap_prcm_apll_update(s); - omap_prcm_dpll_update(s); -} - -static void omap_prcm_coldreset(struct omap_prcm_s *s) -{ - s->setuptime[0] = 0; - s->setuptime[1] = 0; - memset(&s->scratch, 0, sizeof(s->scratch)); - s->rst[0] = 0x01; - s->rst[1] = 0x00; - s->rst[2] = 0x01; - s->clken[0] = 0; - s->clken[2] = 0; - s->clkidle[1] = 0; - s->clksel[5] = 0; - s->clksel[6] = 2; - s->clksrc[0] = 0x43; - s->clkout[0] = 0x0303; - s->clkemul[0] = 0; - s->clkpol[0] = 0x100; - s->rsttime_wkup = 0x1002; - - omap_prcm_reset(s); -} - -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 = g_new0(struct omap_prcm_s, 1); - - s->irq[0] = mpu_int; - s->irq[1] = dsp_int; - s->irq[2] = iva_int; - s->mpu = mpu; - omap_prcm_coldreset(s); - - memory_region_init_io(&s->iomem0, NULL, &omap_prcm_ops, s, "omap.pcrm0", - omap_l4_region_size(ta, 0)); - memory_region_init_io(&s->iomem1, NULL, &omap_prcm_ops, s, "omap.pcrm1", - omap_l4_region_size(ta, 1)); - omap_l4_attach(ta, 0, &s->iomem0); - omap_l4_attach(ta, 1, &s->iomem1); - - return s; -} - -/* System and Pinout control */ -struct omap_sysctl_s { - struct omap_mpu_state_s *mpu; - MemoryRegion iomem; - - uint32_t sysconfig; - uint32_t devconfig; - uint32_t psaconfig; - uint32_t padconf[0x45]; - uint8_t obs; - uint32_t msuspendmux[5]; -}; - -static uint32_t omap_sysctl_read8(void *opaque, hwaddr addr) -{ - - struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque; - int pad_offset, byte_offset; - int value; - - switch (addr) { - case 0x030 ... 0x140: /* CONTROL_PADCONF - only used in the POP */ - pad_offset = (addr - 0x30) >> 2; - byte_offset = (addr - 0x30) & (4 - 1); - - value = s->padconf[pad_offset]; - value = (value >> (byte_offset * 8)) & 0xff; - - return value; - - default: - break; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static uint32_t omap_sysctl_read(void *opaque, hwaddr addr) -{ - struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque; - - switch (addr) { - case 0x000: /* CONTROL_REVISION */ - return 0x20; - - case 0x010: /* CONTROL_SYSCONFIG */ - return s->sysconfig; - - case 0x030 ... 0x140: /* CONTROL_PADCONF - only used in the POP */ - return s->padconf[(addr - 0x30) >> 2]; - - case 0x270: /* CONTROL_DEBOBS */ - return s->obs; - - case 0x274: /* CONTROL_DEVCONF */ - return s->devconfig; - - case 0x28c: /* CONTROL_EMU_SUPPORT */ - return 0; - - case 0x290: /* CONTROL_MSUSPENDMUX_0 */ - return s->msuspendmux[0]; - case 0x294: /* CONTROL_MSUSPENDMUX_1 */ - return s->msuspendmux[1]; - case 0x298: /* CONTROL_MSUSPENDMUX_2 */ - return s->msuspendmux[2]; - case 0x29c: /* CONTROL_MSUSPENDMUX_3 */ - return s->msuspendmux[3]; - case 0x2a0: /* CONTROL_MSUSPENDMUX_4 */ - return s->msuspendmux[4]; - case 0x2a4: /* CONTROL_MSUSPENDMUX_5 */ - return 0; - - case 0x2b8: /* CONTROL_PSA_CTRL */ - return s->psaconfig; - case 0x2bc: /* CONTROL_PSA_CMD */ - case 0x2c0: /* CONTROL_PSA_VALUE */ - return 0; - - case 0x2b0: /* CONTROL_SEC_CTRL */ - return 0x800000f1; - case 0x2d0: /* CONTROL_SEC_EMU */ - return 0x80000015; - case 0x2d4: /* CONTROL_SEC_TAP */ - return 0x8000007f; - case 0x2b4: /* CONTROL_SEC_TEST */ - case 0x2f0: /* CONTROL_SEC_STATUS */ - case 0x2f4: /* CONTROL_SEC_ERR_STATUS */ - /* Secure mode is not present on general-pusrpose device. Outside - * secure mode these values cannot be read or written. */ - return 0; - - case 0x2d8: /* CONTROL_OCM_RAM_PERM */ - return 0xff; - case 0x2dc: /* CONTROL_OCM_PUB_RAM_ADD */ - case 0x2e0: /* CONTROL_EXT_SEC_RAM_START_ADD */ - case 0x2e4: /* CONTROL_EXT_SEC_RAM_STOP_ADD */ - /* No secure mode so no Extended Secure RAM present. */ - return 0; - - case 0x2f8: /* CONTROL_STATUS */ - /* Device Type => General-purpose */ - return 0x0300; - case 0x2fc: /* CONTROL_GENERAL_PURPOSE_STATUS */ - - case 0x300: /* CONTROL_RPUB_KEY_H_0 */ - case 0x304: /* CONTROL_RPUB_KEY_H_1 */ - case 0x308: /* CONTROL_RPUB_KEY_H_2 */ - case 0x30c: /* CONTROL_RPUB_KEY_H_3 */ - return 0xdecafbad; - - case 0x310: /* CONTROL_RAND_KEY_0 */ - case 0x314: /* CONTROL_RAND_KEY_1 */ - case 0x318: /* CONTROL_RAND_KEY_2 */ - case 0x31c: /* CONTROL_RAND_KEY_3 */ - case 0x320: /* CONTROL_CUST_KEY_0 */ - case 0x324: /* CONTROL_CUST_KEY_1 */ - case 0x330: /* CONTROL_TEST_KEY_0 */ - case 0x334: /* CONTROL_TEST_KEY_1 */ - case 0x338: /* CONTROL_TEST_KEY_2 */ - case 0x33c: /* CONTROL_TEST_KEY_3 */ - case 0x340: /* CONTROL_TEST_KEY_4 */ - case 0x344: /* CONTROL_TEST_KEY_5 */ - case 0x348: /* CONTROL_TEST_KEY_6 */ - case 0x34c: /* CONTROL_TEST_KEY_7 */ - case 0x350: /* CONTROL_TEST_KEY_8 */ - case 0x354: /* CONTROL_TEST_KEY_9 */ - /* Can only be accessed in secure mode and when C_FieldAccEnable - * bit is set in CONTROL_SEC_CTRL. - * TODO: otherwise an interconnect access error is generated. */ - return 0; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_sysctl_write8(void *opaque, hwaddr addr, - uint32_t value) -{ - struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque; - int pad_offset, byte_offset; - int prev_value; - - switch (addr) { - case 0x030 ... 0x140: /* CONTROL_PADCONF - only used in the POP */ - pad_offset = (addr - 0x30) >> 2; - byte_offset = (addr - 0x30) & (4 - 1); - - prev_value = s->padconf[pad_offset]; - prev_value &= ~(0xff << (byte_offset * 8)); - prev_value |= ((value & 0x1f1f1f1f) << (byte_offset * 8)) & 0x1f1f1f1f; - s->padconf[pad_offset] = prev_value; - break; - - default: - OMAP_BAD_REG(addr); - break; - } -} - -static void omap_sysctl_write(void *opaque, hwaddr addr, - uint32_t value) -{ - struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque; - - switch (addr) { - case 0x000: /* CONTROL_REVISION */ - case 0x2a4: /* CONTROL_MSUSPENDMUX_5 */ - case 0x2c0: /* CONTROL_PSA_VALUE */ - case 0x2f8: /* CONTROL_STATUS */ - case 0x2fc: /* CONTROL_GENERAL_PURPOSE_STATUS */ - case 0x300: /* CONTROL_RPUB_KEY_H_0 */ - case 0x304: /* CONTROL_RPUB_KEY_H_1 */ - case 0x308: /* CONTROL_RPUB_KEY_H_2 */ - case 0x30c: /* CONTROL_RPUB_KEY_H_3 */ - case 0x310: /* CONTROL_RAND_KEY_0 */ - case 0x314: /* CONTROL_RAND_KEY_1 */ - case 0x318: /* CONTROL_RAND_KEY_2 */ - case 0x31c: /* CONTROL_RAND_KEY_3 */ - case 0x320: /* CONTROL_CUST_KEY_0 */ - case 0x324: /* CONTROL_CUST_KEY_1 */ - case 0x330: /* CONTROL_TEST_KEY_0 */ - case 0x334: /* CONTROL_TEST_KEY_1 */ - case 0x338: /* CONTROL_TEST_KEY_2 */ - case 0x33c: /* CONTROL_TEST_KEY_3 */ - case 0x340: /* CONTROL_TEST_KEY_4 */ - case 0x344: /* CONTROL_TEST_KEY_5 */ - case 0x348: /* CONTROL_TEST_KEY_6 */ - case 0x34c: /* CONTROL_TEST_KEY_7 */ - case 0x350: /* CONTROL_TEST_KEY_8 */ - case 0x354: /* CONTROL_TEST_KEY_9 */ - OMAP_RO_REG(addr); - return; - - case 0x010: /* CONTROL_SYSCONFIG */ - s->sysconfig = value & 0x1e; - break; - - case 0x030 ... 0x140: /* CONTROL_PADCONF - only used in the POP */ - /* XXX: should check constant bits */ - s->padconf[(addr - 0x30) >> 2] = value & 0x1f1f1f1f; - break; - - case 0x270: /* CONTROL_DEBOBS */ - s->obs = value & 0xff; - break; - - case 0x274: /* CONTROL_DEVCONF */ - s->devconfig = value & 0xffffc7ff; - break; - - case 0x28c: /* CONTROL_EMU_SUPPORT */ - break; - - case 0x290: /* CONTROL_MSUSPENDMUX_0 */ - s->msuspendmux[0] = value & 0x3fffffff; - break; - case 0x294: /* CONTROL_MSUSPENDMUX_1 */ - s->msuspendmux[1] = value & 0x3fffffff; - break; - case 0x298: /* CONTROL_MSUSPENDMUX_2 */ - s->msuspendmux[2] = value & 0x3fffffff; - break; - case 0x29c: /* CONTROL_MSUSPENDMUX_3 */ - s->msuspendmux[3] = value & 0x3fffffff; - break; - case 0x2a0: /* CONTROL_MSUSPENDMUX_4 */ - s->msuspendmux[4] = value & 0x3fffffff; - break; - - case 0x2b8: /* CONTROL_PSA_CTRL */ - s->psaconfig = value & 0x1c; - s->psaconfig |= (value & 0x20) ? 2 : 1; - break; - case 0x2bc: /* CONTROL_PSA_CMD */ - break; - - case 0x2b0: /* CONTROL_SEC_CTRL */ - case 0x2b4: /* CONTROL_SEC_TEST */ - case 0x2d0: /* CONTROL_SEC_EMU */ - case 0x2d4: /* CONTROL_SEC_TAP */ - case 0x2d8: /* CONTROL_OCM_RAM_PERM */ - case 0x2dc: /* CONTROL_OCM_PUB_RAM_ADD */ - case 0x2e0: /* CONTROL_EXT_SEC_RAM_START_ADD */ - case 0x2e4: /* CONTROL_EXT_SEC_RAM_STOP_ADD */ - case 0x2f0: /* CONTROL_SEC_STATUS */ - case 0x2f4: /* CONTROL_SEC_ERR_STATUS */ - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_sysctl_ops = { - .old_mmio = { - .read = { - omap_sysctl_read8, - omap_badwidth_read32, /* TODO */ - omap_sysctl_read, - }, - .write = { - omap_sysctl_write8, - omap_badwidth_write32, /* TODO */ - omap_sysctl_write, - }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_sysctl_reset(struct omap_sysctl_s *s) -{ - /* (power-on reset) */ - s->sysconfig = 0; - s->obs = 0; - s->devconfig = 0x0c000000; - s->msuspendmux[0] = 0x00000000; - s->msuspendmux[1] = 0x00000000; - s->msuspendmux[2] = 0x00000000; - s->msuspendmux[3] = 0x00000000; - s->msuspendmux[4] = 0x00000000; - s->psaconfig = 1; - - s->padconf[0x00] = 0x000f0f0f; - s->padconf[0x01] = 0x00000000; - s->padconf[0x02] = 0x00000000; - s->padconf[0x03] = 0x00000000; - s->padconf[0x04] = 0x00000000; - s->padconf[0x05] = 0x00000000; - s->padconf[0x06] = 0x00000000; - s->padconf[0x07] = 0x00000000; - s->padconf[0x08] = 0x08080800; - s->padconf[0x09] = 0x08080808; - s->padconf[0x0a] = 0x08080808; - s->padconf[0x0b] = 0x08080808; - s->padconf[0x0c] = 0x08080808; - s->padconf[0x0d] = 0x08080800; - s->padconf[0x0e] = 0x08080808; - s->padconf[0x0f] = 0x08080808; - s->padconf[0x10] = 0x18181808; /* | 0x07070700 if SBoot3 */ - s->padconf[0x11] = 0x18181818; /* | 0x07070707 if SBoot3 */ - s->padconf[0x12] = 0x18181818; /* | 0x07070707 if SBoot3 */ - s->padconf[0x13] = 0x18181818; /* | 0x07070707 if SBoot3 */ - s->padconf[0x14] = 0x18181818; /* | 0x00070707 if SBoot3 */ - s->padconf[0x15] = 0x18181818; - s->padconf[0x16] = 0x18181818; /* | 0x07000000 if SBoot3 */ - s->padconf[0x17] = 0x1f001f00; - s->padconf[0x18] = 0x1f1f1f1f; - s->padconf[0x19] = 0x00000000; - s->padconf[0x1a] = 0x1f180000; - s->padconf[0x1b] = 0x00001f1f; - s->padconf[0x1c] = 0x1f001f00; - s->padconf[0x1d] = 0x00000000; - s->padconf[0x1e] = 0x00000000; - s->padconf[0x1f] = 0x08000000; - s->padconf[0x20] = 0x08080808; - s->padconf[0x21] = 0x08080808; - s->padconf[0x22] = 0x0f080808; - s->padconf[0x23] = 0x0f0f0f0f; - s->padconf[0x24] = 0x000f0f0f; - s->padconf[0x25] = 0x1f1f1f0f; - s->padconf[0x26] = 0x080f0f1f; - s->padconf[0x27] = 0x070f1808; - s->padconf[0x28] = 0x0f070707; - s->padconf[0x29] = 0x000f0f1f; - s->padconf[0x2a] = 0x0f0f0f1f; - s->padconf[0x2b] = 0x08000000; - s->padconf[0x2c] = 0x0000001f; - s->padconf[0x2d] = 0x0f0f1f00; - s->padconf[0x2e] = 0x1f1f0f0f; - s->padconf[0x2f] = 0x0f1f1f1f; - s->padconf[0x30] = 0x0f0f0f0f; - s->padconf[0x31] = 0x0f1f0f1f; - s->padconf[0x32] = 0x0f0f0f0f; - s->padconf[0x33] = 0x0f1f0f1f; - s->padconf[0x34] = 0x1f1f0f0f; - s->padconf[0x35] = 0x0f0f1f1f; - s->padconf[0x36] = 0x0f0f1f0f; - s->padconf[0x37] = 0x0f0f0f0f; - s->padconf[0x38] = 0x1f18180f; - s->padconf[0x39] = 0x1f1f1f1f; - s->padconf[0x3a] = 0x00001f1f; - s->padconf[0x3b] = 0x00000000; - s->padconf[0x3c] = 0x00000000; - s->padconf[0x3d] = 0x0f0f0f0f; - s->padconf[0x3e] = 0x18000f0f; - s->padconf[0x3f] = 0x00070000; - s->padconf[0x40] = 0x00000707; - s->padconf[0x41] = 0x0f1f0700; - s->padconf[0x42] = 0x1f1f070f; - s->padconf[0x43] = 0x0008081f; - s->padconf[0x44] = 0x00000800; -} - -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 = g_new0(struct omap_sysctl_s, 1); - - s->mpu = mpu; - omap_sysctl_reset(s); - - memory_region_init_io(&s->iomem, NULL, &omap_sysctl_ops, s, "omap.sysctl", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &s->iomem); - - return s; -} - -/* General chip reset */ -static void omap2_mpu_reset(void *opaque) -{ - struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque; - - omap_dma_reset(mpu->dma); - omap_prcm_reset(mpu->prcm); - omap_sysctl_reset(mpu->sysc); - omap_gp_timer_reset(mpu->gptimer[0]); - omap_gp_timer_reset(mpu->gptimer[1]); - omap_gp_timer_reset(mpu->gptimer[2]); - omap_gp_timer_reset(mpu->gptimer[3]); - omap_gp_timer_reset(mpu->gptimer[4]); - omap_gp_timer_reset(mpu->gptimer[5]); - omap_gp_timer_reset(mpu->gptimer[6]); - omap_gp_timer_reset(mpu->gptimer[7]); - omap_gp_timer_reset(mpu->gptimer[8]); - omap_gp_timer_reset(mpu->gptimer[9]); - omap_gp_timer_reset(mpu->gptimer[10]); - omap_gp_timer_reset(mpu->gptimer[11]); - omap_synctimer_reset(mpu->synctimer); - omap_sdrc_reset(mpu->sdrc); - omap_gpmc_reset(mpu->gpmc); - omap_dss_reset(mpu->dss); - omap_uart_reset(mpu->uart[0]); - omap_uart_reset(mpu->uart[1]); - omap_uart_reset(mpu->uart[2]); - omap_mmc_reset(mpu->mmc); - omap_mcspi_reset(mpu->mcspi[0]); - omap_mcspi_reset(mpu->mcspi[1]); - cpu_reset(CPU(mpu->cpu)); -} - -static int omap2_validate_addr(struct omap_mpu_state_s *s, - hwaddr addr) -{ - return 1; -} - -static const struct dma_irq_map omap2_dma_irq_map[] = { - { 0, OMAP_INT_24XX_SDMA_IRQ0 }, - { 0, OMAP_INT_24XX_SDMA_IRQ1 }, - { 0, OMAP_INT_24XX_SDMA_IRQ2 }, - { 0, OMAP_INT_24XX_SDMA_IRQ3 }, -}; - -struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, - unsigned long sdram_size, - const char *core) -{ - struct omap_mpu_state_s *s = g_new0(struct omap_mpu_state_s, 1); - qemu_irq dma_irqs[4]; - DriveInfo *dinfo; - int i; - SysBusDevice *busdev; - struct omap_target_agent_s *ta; - - /* Core */ - s->mpu_model = omap2420; - s->cpu = cpu_arm_init(core ?: "arm1136-r2"); - if (s->cpu == NULL) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - s->sdram_size = sdram_size; - s->sram_size = OMAP242X_SRAM_SIZE; - - s->wakeup = qemu_allocate_irq(omap_mpu_wakeup, s, 0); - - /* Clocks */ - omap_clk_init(s); - - /* Memory-mapped stuff */ - memory_region_allocate_system_memory(&s->sdram, NULL, "omap2.dram", - 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_fatal); - vmstate_register_ram_global(&s->sram); - memory_region_add_subregion(sysmem, OMAP2_SRAM_BASE, &s->sram); - - s->l4 = omap_l4_init(sysmem, OMAP2_L4_BASE, 54); - - /* Actually mapped at any 2K boundary in the ARM11 private-peripheral if */ - s->ih[0] = qdev_create(NULL, "omap2-intc"); - qdev_prop_set_uint8(s->ih[0], "revision", 0x21); - qdev_prop_set_ptr(s->ih[0], "fclk", omap_findclk(s, "mpu_intc_fclk")); - qdev_prop_set_ptr(s->ih[0], "iclk", omap_findclk(s, "mpu_intc_iclk")); - qdev_init_nofail(s->ih[0]); - busdev = SYS_BUS_DEVICE(s->ih[0]); - sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ)); - sysbus_connect_irq(busdev, 1, - qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_FIQ)); - sysbus_mmio_map(busdev, 0, 0x480fe000); - s->prcm = omap_prcm_init(omap_l4tao(s->l4, 3), - qdev_get_gpio_in(s->ih[0], - OMAP_INT_24XX_PRCM_MPU_IRQ), - NULL, NULL, s); - - s->sysc = omap_sysctl_init(omap_l4tao(s->l4, 1), - omap_findclk(s, "omapctrl_iclk"), s); - - for (i = 0; i < 4; i++) { - dma_irqs[i] = qdev_get_gpio_in(s->ih[omap2_dma_irq_map[i].ih], - omap2_dma_irq_map[i].intr); - } - s->dma = omap_dma4_init(0x48056000, dma_irqs, sysmem, s, 256, 32, - omap_findclk(s, "sdma_iclk"), - omap_findclk(s, "sdma_fclk")); - s->port->addr_valid = omap2_validate_addr; - - /* Register SDRAM and SRAM ports for fast DMA transfers. */ - soc_dma_port_add_mem(s->dma, memory_region_get_ram_ptr(&s->sdram), - OMAP2_Q2_BASE, s->sdram_size); - soc_dma_port_add_mem(s->dma, memory_region_get_ram_ptr(&s->sram), - OMAP2_SRAM_BASE, s->sram_size); - - s->uart[0] = omap2_uart_init(sysmem, omap_l4ta(s->l4, 19), - qdev_get_gpio_in(s->ih[0], - OMAP_INT_24XX_UART1_IRQ), - omap_findclk(s, "uart1_fclk"), - omap_findclk(s, "uart1_iclk"), - s->drq[OMAP24XX_DMA_UART1_TX], - s->drq[OMAP24XX_DMA_UART1_RX], - "uart1", - serial_hds[0]); - s->uart[1] = omap2_uart_init(sysmem, omap_l4ta(s->l4, 20), - qdev_get_gpio_in(s->ih[0], - OMAP_INT_24XX_UART2_IRQ), - omap_findclk(s, "uart2_fclk"), - omap_findclk(s, "uart2_iclk"), - s->drq[OMAP24XX_DMA_UART2_TX], - s->drq[OMAP24XX_DMA_UART2_RX], - "uart2", - serial_hds[0] ? serial_hds[1] : NULL); - s->uart[2] = omap2_uart_init(sysmem, omap_l4ta(s->l4, 21), - qdev_get_gpio_in(s->ih[0], - OMAP_INT_24XX_UART3_IRQ), - omap_findclk(s, "uart3_fclk"), - omap_findclk(s, "uart3_iclk"), - s->drq[OMAP24XX_DMA_UART3_TX], - s->drq[OMAP24XX_DMA_UART3_RX], - "uart3", - serial_hds[0] && serial_hds[1] ? serial_hds[2] : NULL); - - s->gptimer[0] = omap_gp_timer_init(omap_l4ta(s->l4, 7), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER1), - omap_findclk(s, "wu_gpt1_clk"), - omap_findclk(s, "wu_l4_iclk")); - s->gptimer[1] = omap_gp_timer_init(omap_l4ta(s->l4, 8), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER2), - omap_findclk(s, "core_gpt2_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[2] = omap_gp_timer_init(omap_l4ta(s->l4, 22), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER3), - omap_findclk(s, "core_gpt3_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[3] = omap_gp_timer_init(omap_l4ta(s->l4, 23), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER4), - omap_findclk(s, "core_gpt4_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[4] = omap_gp_timer_init(omap_l4ta(s->l4, 24), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER5), - omap_findclk(s, "core_gpt5_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[5] = omap_gp_timer_init(omap_l4ta(s->l4, 25), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER6), - omap_findclk(s, "core_gpt6_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[6] = omap_gp_timer_init(omap_l4ta(s->l4, 26), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER7), - omap_findclk(s, "core_gpt7_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[7] = omap_gp_timer_init(omap_l4ta(s->l4, 27), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER8), - omap_findclk(s, "core_gpt8_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[8] = omap_gp_timer_init(omap_l4ta(s->l4, 28), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER9), - omap_findclk(s, "core_gpt9_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[9] = omap_gp_timer_init(omap_l4ta(s->l4, 29), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER10), - omap_findclk(s, "core_gpt10_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[10] = omap_gp_timer_init(omap_l4ta(s->l4, 30), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER11), - omap_findclk(s, "core_gpt11_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[11] = omap_gp_timer_init(omap_l4ta(s->l4, 31), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER12), - omap_findclk(s, "core_gpt12_clk"), - omap_findclk(s, "core_l4_iclk")); - - omap_tap_init(omap_l4ta(s->l4, 2), s); - - s->synctimer = omap_synctimer_init(omap_l4tao(s->l4, 2), s, - omap_findclk(s, "clk32-kHz"), - omap_findclk(s, "core_l4_iclk")); - - s->i2c[0] = qdev_create(NULL, "omap_i2c"); - qdev_prop_set_uint8(s->i2c[0], "revision", 0x34); - qdev_prop_set_ptr(s->i2c[0], "iclk", omap_findclk(s, "i2c1.iclk")); - qdev_prop_set_ptr(s->i2c[0], "fclk", omap_findclk(s, "i2c1.fclk")); - qdev_init_nofail(s->i2c[0]); - busdev = SYS_BUS_DEVICE(s->i2c[0]); - sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_I2C1_IRQ)); - sysbus_connect_irq(busdev, 1, s->drq[OMAP24XX_DMA_I2C1_TX]); - sysbus_connect_irq(busdev, 2, s->drq[OMAP24XX_DMA_I2C1_RX]); - sysbus_mmio_map(busdev, 0, omap_l4_region_base(omap_l4tao(s->l4, 5), 0)); - - s->i2c[1] = qdev_create(NULL, "omap_i2c"); - qdev_prop_set_uint8(s->i2c[1], "revision", 0x34); - qdev_prop_set_ptr(s->i2c[1], "iclk", omap_findclk(s, "i2c2.iclk")); - qdev_prop_set_ptr(s->i2c[1], "fclk", omap_findclk(s, "i2c2.fclk")); - qdev_init_nofail(s->i2c[1]); - busdev = SYS_BUS_DEVICE(s->i2c[1]); - sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_I2C2_IRQ)); - sysbus_connect_irq(busdev, 1, s->drq[OMAP24XX_DMA_I2C2_TX]); - sysbus_connect_irq(busdev, 2, s->drq[OMAP24XX_DMA_I2C2_RX]); - sysbus_mmio_map(busdev, 0, omap_l4_region_base(omap_l4tao(s->l4, 6), 0)); - - s->gpio = qdev_create(NULL, "omap2-gpio"); - qdev_prop_set_int32(s->gpio, "mpu_model", s->mpu_model); - qdev_prop_set_ptr(s->gpio, "iclk", omap_findclk(s, "gpio_iclk")); - qdev_prop_set_ptr(s->gpio, "fclk0", omap_findclk(s, "gpio1_dbclk")); - qdev_prop_set_ptr(s->gpio, "fclk1", omap_findclk(s, "gpio2_dbclk")); - qdev_prop_set_ptr(s->gpio, "fclk2", omap_findclk(s, "gpio3_dbclk")); - qdev_prop_set_ptr(s->gpio, "fclk3", omap_findclk(s, "gpio4_dbclk")); - if (s->mpu_model == omap2430) { - qdev_prop_set_ptr(s->gpio, "fclk4", omap_findclk(s, "gpio5_dbclk")); - } - qdev_init_nofail(s->gpio); - busdev = SYS_BUS_DEVICE(s->gpio); - sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPIO_BANK1)); - sysbus_connect_irq(busdev, 3, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPIO_BANK2)); - sysbus_connect_irq(busdev, 6, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPIO_BANK3)); - sysbus_connect_irq(busdev, 9, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPIO_BANK4)); - if (s->mpu_model == omap2430) { - sysbus_connect_irq(busdev, 12, - qdev_get_gpio_in(s->ih[0], - OMAP_INT_243X_GPIO_BANK5)); - } - ta = omap_l4ta(s->l4, 3); - sysbus_mmio_map(busdev, 0, omap_l4_region_base(ta, 1)); - sysbus_mmio_map(busdev, 1, omap_l4_region_base(ta, 0)); - sysbus_mmio_map(busdev, 2, omap_l4_region_base(ta, 2)); - sysbus_mmio_map(busdev, 3, omap_l4_region_base(ta, 4)); - sysbus_mmio_map(busdev, 4, omap_l4_region_base(ta, 5)); - - s->sdrc = omap_sdrc_init(sysmem, 0x68009000); - s->gpmc = omap_gpmc_init(s, 0x6800a000, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPMC_IRQ), - s->drq[OMAP24XX_DMA_GPMC]); - - dinfo = drive_get(IF_SD, 0, 0); - if (!dinfo) { - fprintf(stderr, "qemu: missing SecureDigital device\n"); - exit(1); - } - s->mmc = omap2_mmc_init(omap_l4tao(s->l4, 9), - blk_by_legacy_dinfo(dinfo), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_MMC_IRQ), - &s->drq[OMAP24XX_DMA_MMC1_TX], - omap_findclk(s, "mmc_fclk"), omap_findclk(s, "mmc_iclk")); - - s->mcspi[0] = omap_mcspi_init(omap_l4ta(s->l4, 35), 4, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_MCSPI1_IRQ), - &s->drq[OMAP24XX_DMA_SPI1_TX0], - omap_findclk(s, "spi1_fclk"), - omap_findclk(s, "spi1_iclk")); - s->mcspi[1] = omap_mcspi_init(omap_l4ta(s->l4, 36), 2, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_MCSPI2_IRQ), - &s->drq[OMAP24XX_DMA_SPI2_TX0], - omap_findclk(s, "spi2_fclk"), - omap_findclk(s, "spi2_iclk")); - - s->dss = omap_dss_init(omap_l4ta(s->l4, 10), sysmem, 0x68000800, - /* XXX wire M_IRQ_25, D_L2_IRQ_30 and I_IRQ_13 together */ - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_DSS_IRQ), - s->drq[OMAP24XX_DMA_DSS], - omap_findclk(s, "dss_clk1"), omap_findclk(s, "dss_clk2"), - omap_findclk(s, "dss_54m_clk"), - omap_findclk(s, "dss_l3_iclk"), - omap_findclk(s, "dss_l4_iclk")); - - omap_sti_init(omap_l4ta(s->l4, 18), sysmem, 0x54000000, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_STI), - omap_findclk(s, "emul_ck"), - serial_hds[0] && serial_hds[1] && serial_hds[2] ? - serial_hds[3] : NULL); - - s->eac = omap_eac_init(omap_l4ta(s->l4, 32), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_EAC_IRQ), - /* Ten consecutive lines */ - &s->drq[OMAP24XX_DMA_EAC_AC_RD], - omap_findclk(s, "func_96m_clk"), - omap_findclk(s, "core_l4_iclk")); - - /* All register mappings (includin those not currenlty implemented): - * SystemControlMod 48000000 - 48000fff - * SystemControlL4 48001000 - 48001fff - * 32kHz Timer Mod 48004000 - 48004fff - * 32kHz Timer L4 48005000 - 48005fff - * PRCM ModA 48008000 - 480087ff - * PRCM ModB 48008800 - 48008fff - * PRCM L4 48009000 - 48009fff - * TEST-BCM Mod 48012000 - 48012fff - * TEST-BCM L4 48013000 - 48013fff - * TEST-TAP Mod 48014000 - 48014fff - * TEST-TAP L4 48015000 - 48015fff - * GPIO1 Mod 48018000 - 48018fff - * GPIO Top 48019000 - 48019fff - * GPIO2 Mod 4801a000 - 4801afff - * GPIO L4 4801b000 - 4801bfff - * GPIO3 Mod 4801c000 - 4801cfff - * GPIO4 Mod 4801e000 - 4801efff - * WDTIMER1 Mod 48020000 - 48010fff - * WDTIMER Top 48021000 - 48011fff - * WDTIMER2 Mod 48022000 - 48012fff - * WDTIMER L4 48023000 - 48013fff - * WDTIMER3 Mod 48024000 - 48014fff - * WDTIMER3 L4 48025000 - 48015fff - * WDTIMER4 Mod 48026000 - 48016fff - * WDTIMER4 L4 48027000 - 48017fff - * GPTIMER1 Mod 48028000 - 48018fff - * GPTIMER1 L4 48029000 - 48019fff - * GPTIMER2 Mod 4802a000 - 4801afff - * GPTIMER2 L4 4802b000 - 4801bfff - * L4-Config AP 48040000 - 480407ff - * L4-Config IP 48040800 - 48040fff - * L4-Config LA 48041000 - 48041fff - * ARM11ETB Mod 48048000 - 48049fff - * ARM11ETB L4 4804a000 - 4804afff - * DISPLAY Top 48050000 - 480503ff - * DISPLAY DISPC 48050400 - 480507ff - * DISPLAY RFBI 48050800 - 48050bff - * DISPLAY VENC 48050c00 - 48050fff - * DISPLAY L4 48051000 - 48051fff - * CAMERA Top 48052000 - 480523ff - * CAMERA core 48052400 - 480527ff - * CAMERA DMA 48052800 - 48052bff - * CAMERA MMU 48052c00 - 48052fff - * CAMERA L4 48053000 - 48053fff - * SDMA Mod 48056000 - 48056fff - * SDMA L4 48057000 - 48057fff - * SSI Top 48058000 - 48058fff - * SSI GDD 48059000 - 48059fff - * SSI Port1 4805a000 - 4805afff - * SSI Port2 4805b000 - 4805bfff - * SSI L4 4805c000 - 4805cfff - * USB Mod 4805e000 - 480fefff - * USB L4 4805f000 - 480fffff - * WIN_TRACER1 Mod 48060000 - 48060fff - * WIN_TRACER1 L4 48061000 - 48061fff - * WIN_TRACER2 Mod 48062000 - 48062fff - * WIN_TRACER2 L4 48063000 - 48063fff - * WIN_TRACER3 Mod 48064000 - 48064fff - * WIN_TRACER3 L4 48065000 - 48065fff - * WIN_TRACER4 Top 48066000 - 480660ff - * WIN_TRACER4 ETT 48066100 - 480661ff - * WIN_TRACER4 WT 48066200 - 480662ff - * WIN_TRACER4 L4 48067000 - 48067fff - * XTI Mod 48068000 - 48068fff - * XTI L4 48069000 - 48069fff - * UART1 Mod 4806a000 - 4806afff - * UART1 L4 4806b000 - 4806bfff - * UART2 Mod 4806c000 - 4806cfff - * UART2 L4 4806d000 - 4806dfff - * UART3 Mod 4806e000 - 4806efff - * UART3 L4 4806f000 - 4806ffff - * I2C1 Mod 48070000 - 48070fff - * I2C1 L4 48071000 - 48071fff - * I2C2 Mod 48072000 - 48072fff - * I2C2 L4 48073000 - 48073fff - * McBSP1 Mod 48074000 - 48074fff - * McBSP1 L4 48075000 - 48075fff - * McBSP2 Mod 48076000 - 48076fff - * McBSP2 L4 48077000 - 48077fff - * GPTIMER3 Mod 48078000 - 48078fff - * GPTIMER3 L4 48079000 - 48079fff - * GPTIMER4 Mod 4807a000 - 4807afff - * GPTIMER4 L4 4807b000 - 4807bfff - * GPTIMER5 Mod 4807c000 - 4807cfff - * GPTIMER5 L4 4807d000 - 4807dfff - * GPTIMER6 Mod 4807e000 - 4807efff - * GPTIMER6 L4 4807f000 - 4807ffff - * GPTIMER7 Mod 48080000 - 48080fff - * GPTIMER7 L4 48081000 - 48081fff - * GPTIMER8 Mod 48082000 - 48082fff - * GPTIMER8 L4 48083000 - 48083fff - * GPTIMER9 Mod 48084000 - 48084fff - * GPTIMER9 L4 48085000 - 48085fff - * GPTIMER10 Mod 48086000 - 48086fff - * GPTIMER10 L4 48087000 - 48087fff - * GPTIMER11 Mod 48088000 - 48088fff - * GPTIMER11 L4 48089000 - 48089fff - * GPTIMER12 Mod 4808a000 - 4808afff - * GPTIMER12 L4 4808b000 - 4808bfff - * EAC Mod 48090000 - 48090fff - * EAC L4 48091000 - 48091fff - * FAC Mod 48092000 - 48092fff - * FAC L4 48093000 - 48093fff - * MAILBOX Mod 48094000 - 48094fff - * MAILBOX L4 48095000 - 48095fff - * SPI1 Mod 48098000 - 48098fff - * SPI1 L4 48099000 - 48099fff - * SPI2 Mod 4809a000 - 4809afff - * SPI2 L4 4809b000 - 4809bfff - * MMC/SDIO Mod 4809c000 - 4809cfff - * MMC/SDIO L4 4809d000 - 4809dfff - * MS_PRO Mod 4809e000 - 4809efff - * MS_PRO L4 4809f000 - 4809ffff - * RNG Mod 480a0000 - 480a0fff - * RNG L4 480a1000 - 480a1fff - * DES3DES Mod 480a2000 - 480a2fff - * DES3DES L4 480a3000 - 480a3fff - * SHA1MD5 Mod 480a4000 - 480a4fff - * SHA1MD5 L4 480a5000 - 480a5fff - * AES Mod 480a6000 - 480a6fff - * AES L4 480a7000 - 480a7fff - * PKA Mod 480a8000 - 480a9fff - * PKA L4 480aa000 - 480aafff - * MG Mod 480b0000 - 480b0fff - * MG L4 480b1000 - 480b1fff - * HDQ/1-wire Mod 480b2000 - 480b2fff - * HDQ/1-wire L4 480b3000 - 480b3fff - * MPU interrupt 480fe000 - 480fefff - * STI channel base 54000000 - 5400ffff - * IVA RAM 5c000000 - 5c01ffff - * IVA ROM 5c020000 - 5c027fff - * IMG_BUF_A 5c040000 - 5c040fff - * IMG_BUF_B 5c042000 - 5c042fff - * VLCDS 5c048000 - 5c0487ff - * IMX_COEF 5c049000 - 5c04afff - * IMX_CMD 5c051000 - 5c051fff - * VLCDQ 5c053000 - 5c0533ff - * VLCDH 5c054000 - 5c054fff - * SEQ_CMD 5c055000 - 5c055fff - * IMX_REG 5c056000 - 5c0560ff - * VLCD_REG 5c056100 - 5c0561ff - * SEQ_REG 5c056200 - 5c0562ff - * IMG_BUF_REG 5c056300 - 5c0563ff - * SEQIRQ_REG 5c056400 - 5c0564ff - * OCP_REG 5c060000 - 5c060fff - * SYSC_REG 5c070000 - 5c070fff - * MMU_REG 5d000000 - 5d000fff - * sDMA R 68000400 - 680005ff - * sDMA W 68000600 - 680007ff - * Display Control 68000800 - 680009ff - * DSP subsystem 68000a00 - 68000bff - * MPU subsystem 68000c00 - 68000dff - * IVA subsystem 68001000 - 680011ff - * USB 68001200 - 680013ff - * Camera 68001400 - 680015ff - * VLYNQ (firewall) 68001800 - 68001bff - * VLYNQ 68001e00 - 68001fff - * SSI 68002000 - 680021ff - * L4 68002400 - 680025ff - * DSP (firewall) 68002800 - 68002bff - * DSP subsystem 68002e00 - 68002fff - * IVA (firewall) 68003000 - 680033ff - * IVA 68003600 - 680037ff - * GFX 68003a00 - 68003bff - * CMDWR emulation 68003c00 - 68003dff - * SMS 68004000 - 680041ff - * OCM 68004200 - 680043ff - * GPMC 68004400 - 680045ff - * RAM (firewall) 68005000 - 680053ff - * RAM (err login) 68005400 - 680057ff - * ROM (firewall) 68005800 - 68005bff - * ROM (err login) 68005c00 - 68005fff - * GPMC (firewall) 68006000 - 680063ff - * GPMC (err login) 68006400 - 680067ff - * SMS (err login) 68006c00 - 68006fff - * SMS registers 68008000 - 68008fff - * SDRC registers 68009000 - 68009fff - * GPMC registers 6800a000 6800afff - */ - - qemu_register_reset(omap2_mpu_reset, s); - - return s; -} diff --git a/qemu/hw/arm/omap_sx1.c b/qemu/hw/arm/omap_sx1.c deleted file mode 100644 index 5d74026cb..000000000 --- a/qemu/hw/arm/omap_sx1.c +++ /dev/null @@ -1,256 +0,0 @@ -/* omap_sx1.c Support for the Siemens SX1 smartphone emulation. - * - * Copyright (C) 2008 - * Jean-Christophe PLAGNIOL-VILLARD - * Copyright (C) 2007 Vladimir Ananiev - * - * based on PalmOne's (TM) PDAs support (palm.c) - */ - -/* - * PalmOne's (TM) PDAs. - * - * Copyright (C) 2006-2007 Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/hw.h" -#include "ui/console.h" -#include "hw/arm/omap.h" -#include "hw/boards.h" -#include "hw/arm/arm.h" -#include "hw/block/flash.h" -#include "sysemu/block-backend.h" -#include "sysemu/qtest.h" -#include "exec/address-spaces.h" - -/*****************************************************************************/ -/* Siemens SX1 Cellphone V1 */ -/* - ARM OMAP310 processor - * - SRAM 192 kB - * - SDRAM 32 MB at 0x10000000 - * - Boot flash 16 MB at 0x00000000 - * - Application flash 8 MB at 0x04000000 - * - 3 serial ports - * - 1 SecureDigital - * - 1 LCD display - * - 1 RTC - */ - -/*****************************************************************************/ -/* Siemens SX1 Cellphone V2 */ -/* - ARM OMAP310 processor - * - SRAM 192 kB - * - SDRAM 32 MB at 0x10000000 - * - Boot flash 32 MB at 0x00000000 - * - 3 serial ports - * - 1 SecureDigital - * - 1 LCD display - * - 1 RTC - */ - -static uint64_t static_read(void *opaque, hwaddr offset, - unsigned size) -{ - uint32_t *val = (uint32_t *) opaque; - uint32_t mask = (4 / size) - 1; - - return *val >> ((offset & mask) << 3); -} - -static void static_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ -#ifdef SPY - printf("%s: value %" PRIx64 " %u bytes written at 0x%x\n", - __func__, value, size, (int)offset); -#endif -} - -static const MemoryRegionOps static_ops = { - .read = static_read, - .write = static_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -#define sdram_size 0x02000000 -#define sector_size (128 * 1024) -#define flash0_size (16 * 1024 * 1024) -#define flash1_size ( 8 * 1024 * 1024) -#define flash2_size (32 * 1024 * 1024) -#define total_ram_v1 (sdram_size + flash0_size + flash1_size + OMAP15XX_SRAM_SIZE) -#define total_ram_v2 (sdram_size + flash2_size + OMAP15XX_SRAM_SIZE) - -static struct arm_boot_info sx1_binfo = { - .loader_start = OMAP_EMIFF_BASE, - .ram_size = sdram_size, - .board_id = 0x265, -}; - -static void sx1_init(MachineState *machine, const int version) -{ - struct omap_mpu_state_s *mpu; - MemoryRegion *address_space = get_system_memory(); - MemoryRegion *flash = g_new(MemoryRegion, 1); - MemoryRegion *cs = g_new(MemoryRegion, 4); - static uint32_t cs0val = 0x00213090; - static uint32_t cs1val = 0x00215070; - static uint32_t cs2val = 0x00001139; - static uint32_t cs3val = 0x00001139; - DriveInfo *dinfo; - int fl_idx; - uint32_t flash_size = flash0_size; - int be; - - if (version == 2) { - flash_size = flash2_size; - } - - mpu = omap310_mpu_init(address_space, sx1_binfo.ram_size, - machine->cpu_model); - - /* External Flash (EMIFS) */ - memory_region_init_ram(flash, NULL, "omap_sx1.flash0-0", flash_size, - &error_fatal); - vmstate_register_ram_global(flash); - memory_region_set_readonly(flash, true); - memory_region_add_subregion(address_space, OMAP_CS0_BASE, flash); - - memory_region_init_io(&cs[0], NULL, &static_ops, &cs0val, - "sx1.cs0", OMAP_CS0_SIZE - flash_size); - memory_region_add_subregion(address_space, - OMAP_CS0_BASE + flash_size, &cs[0]); - - - memory_region_init_io(&cs[2], NULL, &static_ops, &cs2val, - "sx1.cs2", OMAP_CS2_SIZE); - memory_region_add_subregion(address_space, - OMAP_CS2_BASE, &cs[2]); - - memory_region_init_io(&cs[3], NULL, &static_ops, &cs3val, - "sx1.cs3", OMAP_CS3_SIZE); - memory_region_add_subregion(address_space, - OMAP_CS2_BASE, &cs[3]); - - fl_idx = 0; -#ifdef TARGET_WORDS_BIGENDIAN - be = 1; -#else - be = 0; -#endif - - if ((dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) { - if (!pflash_cfi01_register(OMAP_CS0_BASE, NULL, - "omap_sx1.flash0-1", flash_size, - blk_by_legacy_dinfo(dinfo), - sector_size, flash_size / sector_size, - 4, 0, 0, 0, 0, be)) { - fprintf(stderr, "qemu: Error registering flash memory %d.\n", - fl_idx); - } - fl_idx++; - } - - if ((version == 1) && - (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_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); - - memory_region_init_io(&cs[1], NULL, &static_ops, &cs1val, - "sx1.cs1", OMAP_CS1_SIZE - flash1_size); - memory_region_add_subregion(address_space, - OMAP_CS1_BASE + flash1_size, &cs[1]); - - if (!pflash_cfi01_register(OMAP_CS1_BASE, NULL, - "omap_sx1.flash1-1", flash1_size, - blk_by_legacy_dinfo(dinfo), - sector_size, flash1_size / sector_size, - 4, 0, 0, 0, 0, be)) { - fprintf(stderr, "qemu: Error registering flash memory %d.\n", - fl_idx); - } - fl_idx++; - } else { - memory_region_init_io(&cs[1], NULL, &static_ops, &cs1val, - "sx1.cs1", OMAP_CS1_SIZE); - memory_region_add_subregion(address_space, - OMAP_CS1_BASE, &cs[1]); - } - - if (!machine->kernel_filename && !fl_idx && !qtest_enabled()) { - fprintf(stderr, "Kernel or Flash image must be specified\n"); - exit(1); - } - - /* Load the kernel. */ - sx1_binfo.kernel_filename = machine->kernel_filename; - sx1_binfo.kernel_cmdline = machine->kernel_cmdline; - sx1_binfo.initrd_filename = machine->initrd_filename; - arm_load_kernel(mpu->cpu, &sx1_binfo); - - /* TODO: fix next line */ - //~ qemu_console_resize(ds, 640, 480); -} - -static void sx1_init_v1(MachineState *machine) -{ - sx1_init(machine, 1); -} - -static void sx1_init_v2(MachineState *machine) -{ - sx1_init(machine, 2); -} - -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 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) -{ - type_register_static(&sx1_machine_v1_type); - type_register_static(&sx1_machine_v2_type); -} - -type_init(sx1_machine_init) diff --git a/qemu/hw/arm/palm.c b/qemu/hw/arm/palm.c deleted file mode 100644 index 7f460732e..000000000 --- a/qemu/hw/arm/palm.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * PalmOne's (TM) PDAs. - * - * Copyright (C) 2006-2007 Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/hw.h" -#include "audio/audio.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "ui/console.h" -#include "hw/arm/omap.h" -#include "hw/boards.h" -#include "hw/arm/arm.h" -#include "hw/devices.h" -#include "hw/loader.h" -#include "exec/address-spaces.h" - -static uint32_t static_readb(void *opaque, hwaddr offset) -{ - uint32_t *val = (uint32_t *) opaque; - return *val >> ((offset & 3) << 3); -} - -static uint32_t static_readh(void *opaque, hwaddr offset) -{ - uint32_t *val = (uint32_t *) opaque; - return *val >> ((offset & 1) << 3); -} - -static uint32_t static_readw(void *opaque, hwaddr offset) -{ - uint32_t *val = (uint32_t *) opaque; - return *val >> ((offset & 0) << 3); -} - -static void static_write(void *opaque, hwaddr offset, - uint32_t value) -{ -#ifdef SPY - printf("%s: value %08lx written at " PA_FMT "\n", - __FUNCTION__, value, offset); -#endif -} - -static const MemoryRegionOps static_ops = { - .old_mmio = { - .read = { static_readb, static_readh, static_readw, }, - .write = { static_write, static_write, static_write, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* Palm Tunsgten|E support */ - -/* Shared GPIOs */ -#define PALMTE_USBDETECT_GPIO 0 -#define PALMTE_USB_OR_DC_GPIO 1 -#define PALMTE_TSC_GPIO 4 -#define PALMTE_PINTDAV_GPIO 6 -#define PALMTE_MMC_WP_GPIO 8 -#define PALMTE_MMC_POWER_GPIO 9 -#define PALMTE_HDQ_GPIO 11 -#define PALMTE_HEADPHONES_GPIO 14 -#define PALMTE_SPEAKER_GPIO 15 -/* MPU private GPIOs */ -#define PALMTE_DC_GPIO 2 -#define PALMTE_MMC_SWITCH_GPIO 4 -#define PALMTE_MMC1_GPIO 6 -#define PALMTE_MMC2_GPIO 7 -#define PALMTE_MMC3_GPIO 11 - -static MouseTransformInfo palmte_pointercal = { - .x = 320, - .y = 320, - .a = { -5909, 8, 22465308, 104, 7644, -1219972, 65536 }, -}; - -static void palmte_microwire_setup(struct omap_mpu_state_s *cpu) -{ - uWireSlave *tsc; - - tsc = tsc2102_init(qdev_get_gpio_in(cpu->gpio, PALMTE_PINTDAV_GPIO)); - - omap_uwire_attach(cpu->microwire, tsc, 0); - omap_mcbsp_i2s_attach(cpu->mcbsp1, tsc210x_codec(tsc)); - - tsc210x_set_transform(tsc, &palmte_pointercal); -} - -static struct { - int row; - int column; -} palmte_keymap[0x80] = { - [0 ... 0x7f] = { -1, -1 }, - [0x3b] = { 0, 0 }, /* F1 -> Calendar */ - [0x3c] = { 1, 0 }, /* F2 -> Contacts */ - [0x3d] = { 2, 0 }, /* F3 -> Tasks List */ - [0x3e] = { 3, 0 }, /* F4 -> Note Pad */ - [0x01] = { 4, 0 }, /* Esc -> Power */ - [0x4b] = { 0, 1 }, /* Left */ - [0x50] = { 1, 1 }, /* Down */ - [0x48] = { 2, 1 }, /* Up */ - [0x4d] = { 3, 1 }, /* Right */ - [0x4c] = { 4, 1 }, /* Centre */ - [0x39] = { 4, 1 }, /* Spc -> Centre */ -}; - -static void palmte_button_event(void *opaque, int keycode) -{ - struct omap_mpu_state_s *cpu = (struct omap_mpu_state_s *) opaque; - - if (palmte_keymap[keycode & 0x7f].row != -1) - omap_mpuio_key(cpu->mpuio, - palmte_keymap[keycode & 0x7f].row, - palmte_keymap[keycode & 0x7f].column, - !(keycode & 0x80)); -} - -static void palmte_onoff_gpios(void *opaque, int line, int level) -{ - switch (line) { - case 0: - printf("%s: current to MMC/SD card %sabled.\n", - __FUNCTION__, level ? "dis" : "en"); - break; - case 1: - printf("%s: internal speaker amplifier %s.\n", - __FUNCTION__, level ? "down" : "on"); - break; - - /* These LCD & Audio output signals have not been identified yet. */ - case 2: - case 3: - case 4: - printf("%s: LCD GPIO%i %s.\n", - __FUNCTION__, line - 1, level ? "high" : "low"); - break; - case 5: - case 6: - printf("%s: Audio GPIO%i %s.\n", - __FUNCTION__, line - 4, level ? "high" : "low"); - break; - } -} - -static void palmte_gpio_setup(struct omap_mpu_state_s *cpu) -{ - qemu_irq *misc_gpio; - - omap_mmc_handlers(cpu->mmc, - qdev_get_gpio_in(cpu->gpio, PALMTE_MMC_WP_GPIO), - qemu_irq_invert(omap_mpuio_in_get(cpu->mpuio) - [PALMTE_MMC_SWITCH_GPIO])); - - misc_gpio = qemu_allocate_irqs(palmte_onoff_gpios, cpu, 7); - qdev_connect_gpio_out(cpu->gpio, PALMTE_MMC_POWER_GPIO, misc_gpio[0]); - qdev_connect_gpio_out(cpu->gpio, PALMTE_SPEAKER_GPIO, misc_gpio[1]); - qdev_connect_gpio_out(cpu->gpio, 11, misc_gpio[2]); - qdev_connect_gpio_out(cpu->gpio, 12, misc_gpio[3]); - qdev_connect_gpio_out(cpu->gpio, 13, misc_gpio[4]); - omap_mpuio_out_set(cpu->mpuio, 1, misc_gpio[5]); - omap_mpuio_out_set(cpu->mpuio, 3, misc_gpio[6]); - - /* Reset some inputs to initial state. */ - qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_USBDETECT_GPIO)); - qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_USB_OR_DC_GPIO)); - qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, 4)); - qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_HEADPHONES_GPIO)); - qemu_irq_lower(omap_mpuio_in_get(cpu->mpuio)[PALMTE_DC_GPIO]); - qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[6]); - qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[7]); - qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[11]); -} - -static struct arm_boot_info palmte_binfo = { - .loader_start = OMAP_EMIFF_BASE, - .ram_size = 0x02000000, - .board_id = 0x331, -}; - -static void palmte_init(MachineState *machine) -{ - 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; - MemoryRegion *address_space_mem = get_system_memory(); - struct omap_mpu_state_s *mpu; - int flash_size = 0x00800000; - int sdram_size = palmte_binfo.ram_size; - static uint32_t cs0val = 0xffffffff; - static uint32_t cs1val = 0x0000e1a0; - static uint32_t cs2val = 0x0000e1a0; - static uint32_t cs3val = 0xe1a0e1a0; - int rom_size, rom_loaded = 0; - MemoryRegion *flash = g_new(MemoryRegion, 1); - MemoryRegion *cs = g_new(MemoryRegion, 4); - - mpu = omap310_mpu_init(address_space_mem, sdram_size, cpu_model); - - /* External Flash (EMIFS) */ - memory_region_init_ram(flash, NULL, "palmte.flash", flash_size, - &error_fatal); - vmstate_register_ram_global(flash); - memory_region_set_readonly(flash, true); - memory_region_add_subregion(address_space_mem, OMAP_CS0_BASE, flash); - - memory_region_init_io(&cs[0], NULL, &static_ops, &cs0val, "palmte-cs0", - OMAP_CS0_SIZE - flash_size); - memory_region_add_subregion(address_space_mem, OMAP_CS0_BASE + flash_size, - &cs[0]); - memory_region_init_io(&cs[1], NULL, &static_ops, &cs1val, "palmte-cs1", - OMAP_CS1_SIZE); - memory_region_add_subregion(address_space_mem, OMAP_CS1_BASE, &cs[1]); - memory_region_init_io(&cs[2], NULL, &static_ops, &cs2val, "palmte-cs2", - OMAP_CS2_SIZE); - memory_region_add_subregion(address_space_mem, OMAP_CS2_BASE, &cs[2]); - memory_region_init_io(&cs[3], NULL, &static_ops, &cs3val, "palmte-cs3", - OMAP_CS3_SIZE); - memory_region_add_subregion(address_space_mem, OMAP_CS3_BASE, &cs[3]); - - palmte_microwire_setup(mpu); - - qemu_add_kbd_event_handler(palmte_button_event, mpu); - - palmte_gpio_setup(mpu); - - /* Setup initial (reset) machine state */ - if (nb_option_roms) { - rom_size = get_image_size(option_rom[0].name); - if (rom_size > flash_size) { - fprintf(stderr, "%s: ROM image too big (%x > %x)\n", - __FUNCTION__, rom_size, flash_size); - rom_size = 0; - } - if (rom_size > 0) { - rom_size = load_image_targphys(option_rom[0].name, OMAP_CS0_BASE, - flash_size); - rom_loaded = 1; - } - if (rom_size < 0) { - fprintf(stderr, "%s: error loading '%s'\n", - __FUNCTION__, option_rom[0].name); - } - } - - if (!rom_loaded && !kernel_filename && !qtest_enabled()) { - fprintf(stderr, "Kernel or ROM image must be specified\n"); - exit(1); - } - - /* Load the kernel. */ - palmte_binfo.kernel_filename = kernel_filename; - palmte_binfo.kernel_cmdline = kernel_cmdline; - palmte_binfo.initrd_filename = initrd_filename; - arm_load_kernel(mpu->cpu, &palmte_binfo); -} - -static void palmte_machine_init(MachineClass *mc) -{ - mc->desc = "Palm Tungsten|E aka. Cheetah PDA (OMAP310)"; - mc->init = palmte_init; -} - -DEFINE_MACHINE("cheetah", palmte_machine_init) diff --git a/qemu/hw/arm/palmetto-bmc.c b/qemu/hw/arm/palmetto-bmc.c deleted file mode 100644 index 89ebd92b9..000000000 --- a/qemu/hw/arm/palmetto-bmc.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * OpenPOWER Palmetto BMC - * - * Andrew Jeffery - * - * 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 deleted file mode 100644 index 1a8c36033..000000000 --- a/qemu/hw/arm/pxa2xx.c +++ /dev/null @@ -1,2358 +0,0 @@ -/* - * Intel XScale PXA255/270 processor support. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * 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/ssi.h" -#include "sysemu/char.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "qemu/cutils.h" - -static struct { - hwaddr io_base; - int irqn; -} pxa255_serial[] = { - { 0x40100000, PXA2XX_PIC_FFUART }, - { 0x40200000, PXA2XX_PIC_BTUART }, - { 0x40700000, PXA2XX_PIC_STUART }, - { 0x41600000, PXA25X_PIC_HWUART }, - { 0, 0 } -}, pxa270_serial[] = { - { 0x40100000, PXA2XX_PIC_FFUART }, - { 0x40200000, PXA2XX_PIC_BTUART }, - { 0x40700000, PXA2XX_PIC_STUART }, - { 0, 0 } -}; - -typedef struct PXASSPDef { - hwaddr io_base; - int irqn; -} PXASSPDef; - -#if 0 -static PXASSPDef pxa250_ssp[] = { - { 0x41000000, PXA2XX_PIC_SSP }, - { 0, 0 } -}; -#endif - -static PXASSPDef pxa255_ssp[] = { - { 0x41000000, PXA2XX_PIC_SSP }, - { 0x41400000, PXA25X_PIC_NSSP }, - { 0, 0 } -}; - -#if 0 -static PXASSPDef pxa26x_ssp[] = { - { 0x41000000, PXA2XX_PIC_SSP }, - { 0x41400000, PXA25X_PIC_NSSP }, - { 0x41500000, PXA26X_PIC_ASSP }, - { 0, 0 } -}; -#endif - -static PXASSPDef pxa27x_ssp[] = { - { 0x41000000, PXA2XX_PIC_SSP }, - { 0x41700000, PXA27X_PIC_SSP2 }, - { 0x41900000, PXA2XX_PIC_SSP3 }, - { 0, 0 } -}; - -#define PMCR 0x00 /* Power Manager Control register */ -#define PSSR 0x04 /* Power Manager Sleep Status register */ -#define PSPR 0x08 /* Power Manager Scratch-Pad register */ -#define PWER 0x0c /* Power Manager Wake-Up Enable register */ -#define PRER 0x10 /* Power Manager Rising-Edge Detect Enable register */ -#define PFER 0x14 /* Power Manager Falling-Edge Detect Enable register */ -#define PEDR 0x18 /* Power Manager Edge-Detect Status register */ -#define PCFR 0x1c /* Power Manager General Configuration register */ -#define PGSR0 0x20 /* Power Manager GPIO Sleep-State register 0 */ -#define PGSR1 0x24 /* Power Manager GPIO Sleep-State register 1 */ -#define PGSR2 0x28 /* Power Manager GPIO Sleep-State register 2 */ -#define PGSR3 0x2c /* Power Manager GPIO Sleep-State register 3 */ -#define RCSR 0x30 /* Reset Controller Status register */ -#define PSLR 0x34 /* Power Manager Sleep Configuration register */ -#define PTSR 0x38 /* Power Manager Standby Configuration register */ -#define PVCR 0x40 /* Power Manager Voltage Change Control register */ -#define PUCR 0x4c /* Power Manager USIM Card Control/Status register */ -#define PKWR 0x50 /* Power Manager Keyboard Wake-Up Enable register */ -#define PKSR 0x54 /* Power Manager Keyboard Level-Detect Status */ -#define PCMD0 0x80 /* Power Manager I2C Command register File 0 */ -#define PCMD31 0xfc /* Power Manager I2C Command register File 31 */ - -static uint64_t pxa2xx_pm_read(void *opaque, hwaddr addr, - unsigned size) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (addr) { - case PMCR ... PCMD31: - if (addr & 3) - goto fail; - - return s->pm_regs[addr >> 2]; - default: - fail: - printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr); - break; - } - return 0; -} - -static void pxa2xx_pm_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (addr) { - case PMCR: - /* Clear the write-one-to-clear bits... */ - s->pm_regs[addr >> 2] &= ~(value & 0x2a); - /* ...and set the plain r/w bits */ - s->pm_regs[addr >> 2] &= ~0x15; - s->pm_regs[addr >> 2] |= value & 0x15; - break; - - case PSSR: /* Read-clean registers */ - case RCSR: - case PKSR: - s->pm_regs[addr >> 2] &= ~value; - break; - - default: /* Read-write registers */ - if (!(addr & 3)) { - s->pm_regs[addr >> 2] = value; - break; - } - - printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr); - break; - } -} - -static const MemoryRegionOps pxa2xx_pm_ops = { - .read = pxa2xx_pm_read, - .write = pxa2xx_pm_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_pxa2xx_pm = { - .name = "pxa2xx_pm", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(pm_regs, PXA2xxState, 0x40), - VMSTATE_END_OF_LIST() - } -}; - -#define CCCR 0x00 /* Core Clock Configuration register */ -#define CKEN 0x04 /* Clock Enable register */ -#define OSCC 0x08 /* Oscillator Configuration register */ -#define CCSR 0x0c /* Core Clock Status register */ - -static uint64_t pxa2xx_cm_read(void *opaque, hwaddr addr, - unsigned size) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (addr) { - case CCCR: - case CKEN: - case OSCC: - return s->cm_regs[addr >> 2]; - - case CCSR: - return s->cm_regs[CCCR >> 2] | (3 << 28); - - default: - printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr); - break; - } - return 0; -} - -static void pxa2xx_cm_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (addr) { - case CCCR: - case CKEN: - s->cm_regs[addr >> 2] = value; - break; - - case OSCC: - s->cm_regs[addr >> 2] &= ~0x6c; - s->cm_regs[addr >> 2] |= value & 0x6e; - if ((value >> 1) & 1) /* OON */ - s->cm_regs[addr >> 2] |= 1 << 0; /* Oscillator is now stable */ - break; - - default: - printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr); - break; - } -} - -static const MemoryRegionOps pxa2xx_cm_ops = { - .read = pxa2xx_cm_read, - .write = pxa2xx_cm_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_pxa2xx_cm = { - .name = "pxa2xx_cm", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(cm_regs, PXA2xxState, 4), - VMSTATE_UINT32(clkcfg, PXA2xxState), - VMSTATE_UINT32(pmnc, PXA2xxState), - VMSTATE_END_OF_LIST() - } -}; - -static uint64_t pxa2xx_clkcfg_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - PXA2xxState *s = (PXA2xxState *)ri->opaque; - return s->clkcfg; -} - -static void pxa2xx_clkcfg_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - PXA2xxState *s = (PXA2xxState *)ri->opaque; - s->clkcfg = value & 0xf; - if (value & 2) { - printf("%s: CPU frequency change attempt\n", __func__); - } -} - -static void pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - PXA2xxState *s = (PXA2xxState *)ri->opaque; - static const char *pwrmode[8] = { - "Normal", "Idle", "Deep-idle", "Standby", - "Sleep", "reserved (!)", "reserved (!)", "Deep-sleep", - }; - - if (value & 8) { - printf("%s: CPU voltage change attempt\n", __func__); - } - switch (value & 7) { - case 0: - /* Do nothing */ - break; - - case 1: - /* Idle */ - if (!(s->cm_regs[CCCR >> 2] & (1U << 31))) { /* CPDIS */ - cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_HALT); - break; - } - /* Fall through. */ - - case 2: - /* Deep-Idle */ - cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_HALT); - s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ - goto message; - - case 3: - s->cpu->env.uncached_cpsr = ARM_CPU_MODE_SVC; - s->cpu->env.daif = PSTATE_A | PSTATE_F | PSTATE_I; - s->cpu->env.cp15.sctlr_ns = 0; - s->cpu->env.cp15.cpacr_el1 = 0; - s->cpu->env.cp15.ttbr0_el[1] = 0; - s->cpu->env.cp15.dacr_ns = 0; - s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */ - s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ - - /* - * The scratch-pad register is almost universally used - * for storing the return address on suspend. For the - * lack of a resuming bootloader, perform a jump - * directly to that address. - */ - memset(s->cpu->env.regs, 0, 4 * 15); - s->cpu->env.regs[15] = s->pm_regs[PSPR >> 2]; - -#if 0 - buffer = 0xe59ff000; /* ldr pc, [pc, #0] */ - cpu_physical_memory_write(0, &buffer, 4); - buffer = s->pm_regs[PSPR >> 2]; - cpu_physical_memory_write(8, &buffer, 4); -#endif - - /* Suspend */ - cpu_interrupt(current_cpu, CPU_INTERRUPT_HALT); - - goto message; - - default: - message: - printf("%s: machine entered %s mode\n", __func__, - pwrmode[value & 7]); - } -} - -static uint64_t pxa2xx_cppmnc_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - PXA2xxState *s = (PXA2xxState *)ri->opaque; - return s->pmnc; -} - -static void pxa2xx_cppmnc_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - PXA2xxState *s = (PXA2xxState *)ri->opaque; - s->pmnc = value; -} - -static uint64_t pxa2xx_cpccnt_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - PXA2xxState *s = (PXA2xxState *)ri->opaque; - if (s->pmnc & 1) { - return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - } else { - return 0; - } -} - -static const ARMCPRegInfo pxa_cp_reginfo[] = { - /* cp14 crm==1: perf registers */ - { .name = "CPPMNC", .cp = 14, .crn = 0, .crm = 1, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_IO, - .readfn = pxa2xx_cppmnc_read, .writefn = pxa2xx_cppmnc_write }, - { .name = "CPCCNT", .cp = 14, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_IO, - .readfn = pxa2xx_cpccnt_read, .writefn = arm_cp_write_ignore }, - { .name = "CPINTEN", .cp = 14, .crn = 4, .crm = 1, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPFLAG", .cp = 14, .crn = 5, .crm = 1, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPEVTSEL", .cp = 14, .crn = 8, .crm = 1, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - /* cp14 crm==2: performance count registers */ - { .name = "CPPMN0", .cp = 14, .crn = 0, .crm = 2, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPPMN1", .cp = 14, .crn = 1, .crm = 2, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPPMN2", .cp = 14, .crn = 2, .crm = 2, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPPMN3", .cp = 14, .crn = 2, .crm = 3, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - /* cp14 crn==6: CLKCFG */ - { .name = "CLKCFG", .cp = 14, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_IO, - .readfn = pxa2xx_clkcfg_read, .writefn = pxa2xx_clkcfg_write }, - /* cp14 crn==7: PWRMODE */ - { .name = "PWRMODE", .cp = 14, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_IO, - .readfn = arm_cp_read_zero, .writefn = pxa2xx_pwrmode_write }, - REGINFO_SENTINEL -}; - -static void pxa2xx_setup_cp14(PXA2xxState *s) -{ - define_arm_cp_regs_with_opaque(s->cpu, pxa_cp_reginfo, s); -} - -#define MDCNFG 0x00 /* SDRAM Configuration register */ -#define MDREFR 0x04 /* SDRAM Refresh Control register */ -#define MSC0 0x08 /* Static Memory Control register 0 */ -#define MSC1 0x0c /* Static Memory Control register 1 */ -#define MSC2 0x10 /* Static Memory Control register 2 */ -#define MECR 0x14 /* Expansion Memory Bus Config register */ -#define SXCNFG 0x1c /* Synchronous Static Memory Config register */ -#define MCMEM0 0x28 /* PC Card Memory Socket 0 Timing register */ -#define MCMEM1 0x2c /* PC Card Memory Socket 1 Timing register */ -#define MCATT0 0x30 /* PC Card Attribute Socket 0 register */ -#define MCATT1 0x34 /* PC Card Attribute Socket 1 register */ -#define MCIO0 0x38 /* PC Card I/O Socket 0 Timing register */ -#define MCIO1 0x3c /* PC Card I/O Socket 1 Timing register */ -#define MDMRS 0x40 /* SDRAM Mode Register Set Config register */ -#define BOOT_DEF 0x44 /* Boot-time Default Configuration register */ -#define ARB_CNTL 0x48 /* Arbiter Control register */ -#define BSCNTR0 0x4c /* Memory Buffer Strength Control register 0 */ -#define BSCNTR1 0x50 /* Memory Buffer Strength Control register 1 */ -#define LCDBSCNTR 0x54 /* LCD Buffer Strength Control register */ -#define MDMRSLP 0x58 /* Low Power SDRAM Mode Set Config register */ -#define BSCNTR2 0x5c /* Memory Buffer Strength Control register 2 */ -#define BSCNTR3 0x60 /* Memory Buffer Strength Control register 3 */ -#define SA1110 0x64 /* SA-1110 Memory Compatibility register */ - -static uint64_t pxa2xx_mm_read(void *opaque, hwaddr addr, - unsigned size) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (addr) { - case MDCNFG ... SA1110: - if ((addr & 3) == 0) - return s->mm_regs[addr >> 2]; - - default: - printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr); - break; - } - return 0; -} - -static void pxa2xx_mm_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (addr) { - case MDCNFG ... SA1110: - if ((addr & 3) == 0) { - s->mm_regs[addr >> 2] = value; - break; - } - - default: - printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr); - break; - } -} - -static const MemoryRegionOps pxa2xx_mm_ops = { - .read = pxa2xx_mm_read, - .write = pxa2xx_mm_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_pxa2xx_mm = { - .name = "pxa2xx_mm", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(mm_regs, PXA2xxState, 0x1a), - VMSTATE_END_OF_LIST() - } -}; - -#define TYPE_PXA2XX_SSP "pxa2xx-ssp" -#define PXA2XX_SSP(obj) \ - OBJECT_CHECK(PXA2xxSSPState, (obj), TYPE_PXA2XX_SSP) - -/* Synchronous Serial Ports */ -typedef struct { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - qemu_irq irq; - uint32_t enable; - SSIBus *bus; - - uint32_t sscr[2]; - uint32_t sspsp; - uint32_t ssto; - uint32_t ssitr; - uint32_t sssr; - uint8_t sstsa; - uint8_t ssrsa; - uint8_t ssacd; - - uint32_t rx_fifo[16]; - uint32_t rx_level; - uint32_t rx_start; -} PXA2xxSSPState; - -static bool pxa2xx_ssp_vmstate_validate(void *opaque, int version_id) -{ - PXA2xxSSPState *s = opaque; - - return s->rx_start < sizeof(s->rx_fifo); -} - -static const VMStateDescription vmstate_pxa2xx_ssp = { - .name = "pxa2xx-ssp", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(enable, PXA2xxSSPState), - VMSTATE_UINT32_ARRAY(sscr, PXA2xxSSPState, 2), - VMSTATE_UINT32(sspsp, PXA2xxSSPState), - VMSTATE_UINT32(ssto, PXA2xxSSPState), - VMSTATE_UINT32(ssitr, PXA2xxSSPState), - VMSTATE_UINT32(sssr, PXA2xxSSPState), - VMSTATE_UINT8(sstsa, PXA2xxSSPState), - VMSTATE_UINT8(ssrsa, PXA2xxSSPState), - VMSTATE_UINT8(ssacd, PXA2xxSSPState), - VMSTATE_UINT32(rx_level, PXA2xxSSPState), - VMSTATE_UINT32(rx_start, PXA2xxSSPState), - VMSTATE_VALIDATE("fifo is 16 bytes", pxa2xx_ssp_vmstate_validate), - VMSTATE_UINT32_ARRAY(rx_fifo, PXA2xxSSPState, 16), - VMSTATE_END_OF_LIST() - } -}; - -#define SSCR0 0x00 /* SSP Control register 0 */ -#define SSCR1 0x04 /* SSP Control register 1 */ -#define SSSR 0x08 /* SSP Status register */ -#define SSITR 0x0c /* SSP Interrupt Test register */ -#define SSDR 0x10 /* SSP Data register */ -#define SSTO 0x28 /* SSP Time-Out register */ -#define SSPSP 0x2c /* SSP Programmable Serial Protocol register */ -#define SSTSA 0x30 /* SSP TX Time Slot Active register */ -#define SSRSA 0x34 /* SSP RX Time Slot Active register */ -#define SSTSS 0x38 /* SSP Time Slot Status register */ -#define SSACD 0x3c /* SSP Audio Clock Divider register */ - -/* Bitfields for above registers */ -#define SSCR0_SPI(x) (((x) & 0x30) == 0x00) -#define SSCR0_SSP(x) (((x) & 0x30) == 0x10) -#define SSCR0_UWIRE(x) (((x) & 0x30) == 0x20) -#define SSCR0_PSP(x) (((x) & 0x30) == 0x30) -#define SSCR0_SSE (1 << 7) -#define SSCR0_RIM (1 << 22) -#define SSCR0_TIM (1 << 23) -#define SSCR0_MOD (1U << 31) -#define SSCR0_DSS(x) (((((x) >> 16) & 0x10) | ((x) & 0xf)) + 1) -#define SSCR1_RIE (1 << 0) -#define SSCR1_TIE (1 << 1) -#define SSCR1_LBM (1 << 2) -#define SSCR1_MWDS (1 << 5) -#define SSCR1_TFT(x) ((((x) >> 6) & 0xf) + 1) -#define SSCR1_RFT(x) ((((x) >> 10) & 0xf) + 1) -#define SSCR1_EFWR (1 << 14) -#define SSCR1_PINTE (1 << 18) -#define SSCR1_TINTE (1 << 19) -#define SSCR1_RSRE (1 << 20) -#define SSCR1_TSRE (1 << 21) -#define SSCR1_EBCEI (1 << 29) -#define SSITR_INT (7 << 5) -#define SSSR_TNF (1 << 2) -#define SSSR_RNE (1 << 3) -#define SSSR_TFS (1 << 5) -#define SSSR_RFS (1 << 6) -#define SSSR_ROR (1 << 7) -#define SSSR_PINT (1 << 18) -#define SSSR_TINT (1 << 19) -#define SSSR_EOC (1 << 20) -#define SSSR_TUR (1 << 21) -#define SSSR_BCE (1 << 23) -#define SSSR_RW 0x00bc0080 - -static void pxa2xx_ssp_int_update(PXA2xxSSPState *s) -{ - int level = 0; - - level |= s->ssitr & SSITR_INT; - level |= (s->sssr & SSSR_BCE) && (s->sscr[1] & SSCR1_EBCEI); - level |= (s->sssr & SSSR_TUR) && !(s->sscr[0] & SSCR0_TIM); - level |= (s->sssr & SSSR_EOC) && (s->sssr & (SSSR_TINT | SSSR_PINT)); - level |= (s->sssr & SSSR_TINT) && (s->sscr[1] & SSCR1_TINTE); - level |= (s->sssr & SSSR_PINT) && (s->sscr[1] & SSCR1_PINTE); - level |= (s->sssr & SSSR_ROR) && !(s->sscr[0] & SSCR0_RIM); - level |= (s->sssr & SSSR_RFS) && (s->sscr[1] & SSCR1_RIE); - level |= (s->sssr & SSSR_TFS) && (s->sscr[1] & SSCR1_TIE); - qemu_set_irq(s->irq, !!level); -} - -static void pxa2xx_ssp_fifo_update(PXA2xxSSPState *s) -{ - s->sssr &= ~(0xf << 12); /* Clear RFL */ - s->sssr &= ~(0xf << 8); /* Clear TFL */ - s->sssr &= ~SSSR_TFS; - s->sssr &= ~SSSR_TNF; - if (s->enable) { - s->sssr |= ((s->rx_level - 1) & 0xf) << 12; - if (s->rx_level >= SSCR1_RFT(s->sscr[1])) - s->sssr |= SSSR_RFS; - else - s->sssr &= ~SSSR_RFS; - if (s->rx_level) - s->sssr |= SSSR_RNE; - else - s->sssr &= ~SSSR_RNE; - /* TX FIFO is never filled, so it is always in underrun - condition if SSP is enabled */ - s->sssr |= SSSR_TFS; - s->sssr |= SSSR_TNF; - } - - pxa2xx_ssp_int_update(s); -} - -static uint64_t pxa2xx_ssp_read(void *opaque, hwaddr addr, - unsigned size) -{ - PXA2xxSSPState *s = (PXA2xxSSPState *) opaque; - uint32_t retval; - - switch (addr) { - case SSCR0: - return s->sscr[0]; - case SSCR1: - return s->sscr[1]; - case SSPSP: - return s->sspsp; - case SSTO: - return s->ssto; - case SSITR: - return s->ssitr; - case SSSR: - return s->sssr | s->ssitr; - case SSDR: - if (!s->enable) - return 0xffffffff; - if (s->rx_level < 1) { - printf("%s: SSP Rx Underrun\n", __FUNCTION__); - return 0xffffffff; - } - s->rx_level --; - retval = s->rx_fifo[s->rx_start ++]; - s->rx_start &= 0xf; - pxa2xx_ssp_fifo_update(s); - return retval; - case SSTSA: - return s->sstsa; - case SSRSA: - return s->ssrsa; - case SSTSS: - return 0; - case SSACD: - return s->ssacd; - default: - printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr); - break; - } - return 0; -} - -static void pxa2xx_ssp_write(void *opaque, hwaddr addr, - uint64_t value64, unsigned size) -{ - PXA2xxSSPState *s = (PXA2xxSSPState *) opaque; - uint32_t value = value64; - - switch (addr) { - case SSCR0: - s->sscr[0] = value & 0xc7ffffff; - s->enable = value & SSCR0_SSE; - if (value & SSCR0_MOD) - printf("%s: Attempt to use network mode\n", __FUNCTION__); - if (s->enable && SSCR0_DSS(value) < 4) - printf("%s: Wrong data size: %i bits\n", __FUNCTION__, - SSCR0_DSS(value)); - if (!(value & SSCR0_SSE)) { - s->sssr = 0; - s->ssitr = 0; - s->rx_level = 0; - } - pxa2xx_ssp_fifo_update(s); - break; - - case SSCR1: - s->sscr[1] = value; - if (value & (SSCR1_LBM | SSCR1_EFWR)) - printf("%s: Attempt to use SSP test mode\n", __FUNCTION__); - pxa2xx_ssp_fifo_update(s); - break; - - case SSPSP: - s->sspsp = value; - break; - - case SSTO: - s->ssto = value; - break; - - case SSITR: - s->ssitr = value & SSITR_INT; - pxa2xx_ssp_int_update(s); - break; - - case SSSR: - s->sssr &= ~(value & SSSR_RW); - pxa2xx_ssp_int_update(s); - break; - - case SSDR: - if (SSCR0_UWIRE(s->sscr[0])) { - if (s->sscr[1] & SSCR1_MWDS) - value &= 0xffff; - else - value &= 0xff; - } else - /* Note how 32bits overflow does no harm here */ - value &= (1 << SSCR0_DSS(s->sscr[0])) - 1; - - /* Data goes from here to the Tx FIFO and is shifted out from - * there directly to the slave, no need to buffer it. - */ - if (s->enable) { - uint32_t readval; - readval = ssi_transfer(s->bus, value); - if (s->rx_level < 0x10) { - s->rx_fifo[(s->rx_start + s->rx_level ++) & 0xf] = readval; - } else { - s->sssr |= SSSR_ROR; - } - } - pxa2xx_ssp_fifo_update(s); - break; - - case SSTSA: - s->sstsa = value; - break; - - case SSRSA: - s->ssrsa = value; - break; - - case SSACD: - s->ssacd = value; - break; - - default: - printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr); - break; - } -} - -static const MemoryRegionOps pxa2xx_ssp_ops = { - .read = pxa2xx_ssp_read, - .write = pxa2xx_ssp_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pxa2xx_ssp_reset(DeviceState *d) -{ - PXA2xxSSPState *s = PXA2XX_SSP(d); - - s->enable = 0; - s->sscr[0] = s->sscr[1] = 0; - s->sspsp = 0; - s->ssto = 0; - s->ssitr = 0; - s->sssr = 0; - s->sstsa = 0; - s->ssrsa = 0; - s->ssacd = 0; - s->rx_start = s->rx_level = 0; -} - -static int pxa2xx_ssp_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - PXA2xxSSPState *s = PXA2XX_SSP(dev); - - sysbus_init_irq(sbd, &s->irq); - - memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_ssp_ops, s, - "pxa2xx-ssp", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - - s->bus = ssi_create_bus(dev, "ssi"); - return 0; -} - -/* Real-Time Clock */ -#define RCNR 0x00 /* RTC Counter register */ -#define RTAR 0x04 /* RTC Alarm register */ -#define RTSR 0x08 /* RTC Status register */ -#define RTTR 0x0c /* RTC Timer Trim register */ -#define RDCR 0x10 /* RTC Day Counter register */ -#define RYCR 0x14 /* RTC Year Counter register */ -#define RDAR1 0x18 /* RTC Wristwatch Day Alarm register 1 */ -#define RYAR1 0x1c /* RTC Wristwatch Year Alarm register 1 */ -#define RDAR2 0x20 /* RTC Wristwatch Day Alarm register 2 */ -#define RYAR2 0x24 /* RTC Wristwatch Year Alarm register 2 */ -#define SWCR 0x28 /* RTC Stopwatch Counter register */ -#define SWAR1 0x2c /* RTC Stopwatch Alarm register 1 */ -#define SWAR2 0x30 /* RTC Stopwatch Alarm register 2 */ -#define RTCPICR 0x34 /* RTC Periodic Interrupt Counter register */ -#define PIAR 0x38 /* RTC Periodic Interrupt Alarm register */ - -#define TYPE_PXA2XX_RTC "pxa2xx_rtc" -#define PXA2XX_RTC(obj) \ - OBJECT_CHECK(PXA2xxRTCState, (obj), TYPE_PXA2XX_RTC) - -typedef struct { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - uint32_t rttr; - uint32_t rtsr; - uint32_t rtar; - uint32_t rdar1; - uint32_t rdar2; - uint32_t ryar1; - uint32_t ryar2; - uint32_t swar1; - uint32_t swar2; - uint32_t piar; - uint32_t last_rcnr; - uint32_t last_rdcr; - uint32_t last_rycr; - uint32_t last_swcr; - uint32_t last_rtcpicr; - int64_t last_hz; - int64_t last_sw; - int64_t last_pi; - QEMUTimer *rtc_hz; - QEMUTimer *rtc_rdal1; - QEMUTimer *rtc_rdal2; - QEMUTimer *rtc_swal1; - QEMUTimer *rtc_swal2; - QEMUTimer *rtc_pi; - qemu_irq rtc_irq; -} PXA2xxRTCState; - -static inline void pxa2xx_rtc_int_update(PXA2xxRTCState *s) -{ - qemu_set_irq(s->rtc_irq, !!(s->rtsr & 0x2553)); -} - -static void pxa2xx_rtc_hzupdate(PXA2xxRTCState *s) -{ - int64_t rt = qemu_clock_get_ms(rtc_clock); - s->last_rcnr += ((rt - s->last_hz) << 15) / - (1000 * ((s->rttr & 0xffff) + 1)); - s->last_rdcr += ((rt - s->last_hz) << 15) / - (1000 * ((s->rttr & 0xffff) + 1)); - s->last_hz = rt; -} - -static void pxa2xx_rtc_swupdate(PXA2xxRTCState *s) -{ - int64_t rt = qemu_clock_get_ms(rtc_clock); - if (s->rtsr & (1 << 12)) - s->last_swcr += (rt - s->last_sw) / 10; - s->last_sw = rt; -} - -static void pxa2xx_rtc_piupdate(PXA2xxRTCState *s) -{ - int64_t rt = qemu_clock_get_ms(rtc_clock); - if (s->rtsr & (1 << 15)) - s->last_swcr += rt - s->last_pi; - s->last_pi = rt; -} - -static inline void pxa2xx_rtc_alarm_update(PXA2xxRTCState *s, - uint32_t rtsr) -{ - if ((rtsr & (1 << 2)) && !(rtsr & (1 << 0))) - timer_mod(s->rtc_hz, s->last_hz + - (((s->rtar - s->last_rcnr) * 1000 * - ((s->rttr & 0xffff) + 1)) >> 15)); - else - timer_del(s->rtc_hz); - - if ((rtsr & (1 << 5)) && !(rtsr & (1 << 4))) - timer_mod(s->rtc_rdal1, s->last_hz + - (((s->rdar1 - s->last_rdcr) * 1000 * - ((s->rttr & 0xffff) + 1)) >> 15)); /* TODO: fixup */ - else - timer_del(s->rtc_rdal1); - - if ((rtsr & (1 << 7)) && !(rtsr & (1 << 6))) - timer_mod(s->rtc_rdal2, s->last_hz + - (((s->rdar2 - s->last_rdcr) * 1000 * - ((s->rttr & 0xffff) + 1)) >> 15)); /* TODO: fixup */ - else - timer_del(s->rtc_rdal2); - - if ((rtsr & 0x1200) == 0x1200 && !(rtsr & (1 << 8))) - timer_mod(s->rtc_swal1, s->last_sw + - (s->swar1 - s->last_swcr) * 10); /* TODO: fixup */ - else - timer_del(s->rtc_swal1); - - if ((rtsr & 0x1800) == 0x1800 && !(rtsr & (1 << 10))) - timer_mod(s->rtc_swal2, s->last_sw + - (s->swar2 - s->last_swcr) * 10); /* TODO: fixup */ - else - timer_del(s->rtc_swal2); - - if ((rtsr & 0xc000) == 0xc000 && !(rtsr & (1 << 13))) - timer_mod(s->rtc_pi, s->last_pi + - (s->piar & 0xffff) - s->last_rtcpicr); - else - timer_del(s->rtc_pi); -} - -static inline void pxa2xx_rtc_hz_tick(void *opaque) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - s->rtsr |= (1 << 0); - pxa2xx_rtc_alarm_update(s, s->rtsr); - pxa2xx_rtc_int_update(s); -} - -static inline void pxa2xx_rtc_rdal1_tick(void *opaque) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - s->rtsr |= (1 << 4); - pxa2xx_rtc_alarm_update(s, s->rtsr); - pxa2xx_rtc_int_update(s); -} - -static inline void pxa2xx_rtc_rdal2_tick(void *opaque) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - s->rtsr |= (1 << 6); - pxa2xx_rtc_alarm_update(s, s->rtsr); - pxa2xx_rtc_int_update(s); -} - -static inline void pxa2xx_rtc_swal1_tick(void *opaque) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - s->rtsr |= (1 << 8); - pxa2xx_rtc_alarm_update(s, s->rtsr); - pxa2xx_rtc_int_update(s); -} - -static inline void pxa2xx_rtc_swal2_tick(void *opaque) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - s->rtsr |= (1 << 10); - pxa2xx_rtc_alarm_update(s, s->rtsr); - pxa2xx_rtc_int_update(s); -} - -static inline void pxa2xx_rtc_pi_tick(void *opaque) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - s->rtsr |= (1 << 13); - pxa2xx_rtc_piupdate(s); - s->last_rtcpicr = 0; - pxa2xx_rtc_alarm_update(s, s->rtsr); - pxa2xx_rtc_int_update(s); -} - -static uint64_t pxa2xx_rtc_read(void *opaque, hwaddr addr, - unsigned size) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - - switch (addr) { - case RTTR: - return s->rttr; - case RTSR: - return s->rtsr; - case RTAR: - return s->rtar; - case RDAR1: - return s->rdar1; - case RDAR2: - return s->rdar2; - case RYAR1: - return s->ryar1; - case RYAR2: - return s->ryar2; - case SWAR1: - return s->swar1; - case SWAR2: - return s->swar2; - case PIAR: - return s->piar; - case RCNR: - return s->last_rcnr + - ((qemu_clock_get_ms(rtc_clock) - s->last_hz) << 15) / - (1000 * ((s->rttr & 0xffff) + 1)); - case RDCR: - return s->last_rdcr + - ((qemu_clock_get_ms(rtc_clock) - s->last_hz) << 15) / - (1000 * ((s->rttr & 0xffff) + 1)); - case RYCR: - return s->last_rycr; - case SWCR: - if (s->rtsr & (1 << 12)) - return s->last_swcr + - (qemu_clock_get_ms(rtc_clock) - s->last_sw) / 10; - else - return s->last_swcr; - default: - printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr); - break; - } - return 0; -} - -static void pxa2xx_rtc_write(void *opaque, hwaddr addr, - uint64_t value64, unsigned size) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - uint32_t value = value64; - - switch (addr) { - case RTTR: - if (!(s->rttr & (1U << 31))) { - pxa2xx_rtc_hzupdate(s); - s->rttr = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - } - break; - - case RTSR: - if ((s->rtsr ^ value) & (1 << 15)) - pxa2xx_rtc_piupdate(s); - - if ((s->rtsr ^ value) & (1 << 12)) - pxa2xx_rtc_swupdate(s); - - if (((s->rtsr ^ value) & 0x4aac) | (value & ~0xdaac)) - pxa2xx_rtc_alarm_update(s, value); - - s->rtsr = (value & 0xdaac) | (s->rtsr & ~(value & ~0xdaac)); - pxa2xx_rtc_int_update(s); - break; - - case RTAR: - s->rtar = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case RDAR1: - s->rdar1 = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case RDAR2: - s->rdar2 = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case RYAR1: - s->ryar1 = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case RYAR2: - s->ryar2 = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case SWAR1: - pxa2xx_rtc_swupdate(s); - s->swar1 = value; - s->last_swcr = 0; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case SWAR2: - s->swar2 = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case PIAR: - s->piar = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case RCNR: - pxa2xx_rtc_hzupdate(s); - s->last_rcnr = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case RDCR: - pxa2xx_rtc_hzupdate(s); - s->last_rdcr = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case RYCR: - s->last_rycr = value; - break; - - case SWCR: - pxa2xx_rtc_swupdate(s); - s->last_swcr = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case RTCPICR: - pxa2xx_rtc_piupdate(s); - s->last_rtcpicr = value & 0xffff; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - default: - printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr); - } -} - -static const MemoryRegionOps pxa2xx_rtc_ops = { - .read = pxa2xx_rtc_read, - .write = pxa2xx_rtc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int pxa2xx_rtc_init(SysBusDevice *dev) -{ - PXA2xxRTCState *s = PXA2XX_RTC(dev); - struct tm tm; - int wom; - - s->rttr = 0x7fff; - s->rtsr = 0; - - qemu_get_timedate(&tm, 0); - wom = ((tm.tm_mday - 1) / 7) + 1; - - s->last_rcnr = (uint32_t) mktimegm(&tm); - s->last_rdcr = (wom << 20) | ((tm.tm_wday + 1) << 17) | - (tm.tm_hour << 12) | (tm.tm_min << 6) | tm.tm_sec; - s->last_rycr = ((tm.tm_year + 1900) << 9) | - ((tm.tm_mon + 1) << 5) | tm.tm_mday; - s->last_swcr = (tm.tm_hour << 19) | - (tm.tm_min << 13) | (tm.tm_sec << 7); - s->last_rtcpicr = 0; - s->last_hz = s->last_sw = s->last_pi = qemu_clock_get_ms(rtc_clock); - - s->rtc_hz = timer_new_ms(rtc_clock, pxa2xx_rtc_hz_tick, s); - s->rtc_rdal1 = timer_new_ms(rtc_clock, pxa2xx_rtc_rdal1_tick, s); - s->rtc_rdal2 = timer_new_ms(rtc_clock, pxa2xx_rtc_rdal2_tick, s); - s->rtc_swal1 = timer_new_ms(rtc_clock, pxa2xx_rtc_swal1_tick, s); - s->rtc_swal2 = timer_new_ms(rtc_clock, pxa2xx_rtc_swal2_tick, s); - s->rtc_pi = timer_new_ms(rtc_clock, pxa2xx_rtc_pi_tick, s); - - sysbus_init_irq(dev, &s->rtc_irq); - - memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_rtc_ops, s, - "pxa2xx-rtc", 0x10000); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static void pxa2xx_rtc_pre_save(void *opaque) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - - pxa2xx_rtc_hzupdate(s); - pxa2xx_rtc_piupdate(s); - pxa2xx_rtc_swupdate(s); -} - -static int pxa2xx_rtc_post_load(void *opaque, int version_id) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - - pxa2xx_rtc_alarm_update(s, s->rtsr); - - return 0; -} - -static const VMStateDescription vmstate_pxa2xx_rtc_regs = { - .name = "pxa2xx_rtc", - .version_id = 0, - .minimum_version_id = 0, - .pre_save = pxa2xx_rtc_pre_save, - .post_load = pxa2xx_rtc_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(rttr, PXA2xxRTCState), - VMSTATE_UINT32(rtsr, PXA2xxRTCState), - VMSTATE_UINT32(rtar, PXA2xxRTCState), - VMSTATE_UINT32(rdar1, PXA2xxRTCState), - VMSTATE_UINT32(rdar2, PXA2xxRTCState), - VMSTATE_UINT32(ryar1, PXA2xxRTCState), - VMSTATE_UINT32(ryar2, PXA2xxRTCState), - VMSTATE_UINT32(swar1, PXA2xxRTCState), - VMSTATE_UINT32(swar2, PXA2xxRTCState), - VMSTATE_UINT32(piar, PXA2xxRTCState), - VMSTATE_UINT32(last_rcnr, PXA2xxRTCState), - VMSTATE_UINT32(last_rdcr, PXA2xxRTCState), - VMSTATE_UINT32(last_rycr, PXA2xxRTCState), - VMSTATE_UINT32(last_swcr, PXA2xxRTCState), - VMSTATE_UINT32(last_rtcpicr, PXA2xxRTCState), - VMSTATE_INT64(last_hz, PXA2xxRTCState), - VMSTATE_INT64(last_sw, PXA2xxRTCState), - VMSTATE_INT64(last_pi, PXA2xxRTCState), - VMSTATE_END_OF_LIST(), - }, -}; - -static void pxa2xx_rtc_sysbus_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = pxa2xx_rtc_init; - dc->desc = "PXA2xx RTC Controller"; - dc->vmsd = &vmstate_pxa2xx_rtc_regs; -} - -static const TypeInfo pxa2xx_rtc_sysbus_info = { - .name = TYPE_PXA2XX_RTC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxRTCState), - .class_init = pxa2xx_rtc_sysbus_class_init, -}; - -/* I2C Interface */ - -#define TYPE_PXA2XX_I2C_SLAVE "pxa2xx-i2c-slave" -#define PXA2XX_I2C_SLAVE(obj) \ - OBJECT_CHECK(PXA2xxI2CSlaveState, (obj), TYPE_PXA2XX_I2C_SLAVE) - -typedef struct PXA2xxI2CSlaveState { - I2CSlave parent_obj; - - PXA2xxI2CState *host; -} PXA2xxI2CSlaveState; - -#define TYPE_PXA2XX_I2C "pxa2xx_i2c" -#define PXA2XX_I2C(obj) \ - OBJECT_CHECK(PXA2xxI2CState, (obj), TYPE_PXA2XX_I2C) - -struct PXA2xxI2CState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - PXA2xxI2CSlaveState *slave; - I2CBus *bus; - qemu_irq irq; - uint32_t offset; - uint32_t region_size; - - uint16_t control; - uint16_t status; - uint8_t ibmr; - uint8_t data; -}; - -#define IBMR 0x80 /* I2C Bus Monitor register */ -#define IDBR 0x88 /* I2C Data Buffer register */ -#define ICR 0x90 /* I2C Control register */ -#define ISR 0x98 /* I2C Status register */ -#define ISAR 0xa0 /* I2C Slave Address register */ - -static void pxa2xx_i2c_update(PXA2xxI2CState *s) -{ - uint16_t level = 0; - level |= s->status & s->control & (1 << 10); /* BED */ - level |= (s->status & (1 << 7)) && (s->control & (1 << 9)); /* IRF */ - level |= (s->status & (1 << 6)) && (s->control & (1 << 8)); /* ITE */ - level |= s->status & (1 << 9); /* SAD */ - qemu_set_irq(s->irq, !!level); -} - -/* These are only stubs now. */ -static void pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event) -{ - PXA2xxI2CSlaveState *slave = PXA2XX_I2C_SLAVE(i2c); - PXA2xxI2CState *s = slave->host; - - switch (event) { - case I2C_START_SEND: - s->status |= (1 << 9); /* set SAD */ - s->status &= ~(1 << 0); /* clear RWM */ - break; - case I2C_START_RECV: - s->status |= (1 << 9); /* set SAD */ - s->status |= 1 << 0; /* set RWM */ - break; - case I2C_FINISH: - s->status |= (1 << 4); /* set SSD */ - break; - case I2C_NACK: - s->status |= 1 << 1; /* set ACKNAK */ - break; - } - pxa2xx_i2c_update(s); -} - -static int pxa2xx_i2c_rx(I2CSlave *i2c) -{ - PXA2xxI2CSlaveState *slave = PXA2XX_I2C_SLAVE(i2c); - PXA2xxI2CState *s = slave->host; - - if ((s->control & (1 << 14)) || !(s->control & (1 << 6))) { - return 0; - } - - if (s->status & (1 << 0)) { /* RWM */ - s->status |= 1 << 6; /* set ITE */ - } - pxa2xx_i2c_update(s); - - return s->data; -} - -static int pxa2xx_i2c_tx(I2CSlave *i2c, uint8_t data) -{ - PXA2xxI2CSlaveState *slave = PXA2XX_I2C_SLAVE(i2c); - PXA2xxI2CState *s = slave->host; - - if ((s->control & (1 << 14)) || !(s->control & (1 << 6))) { - return 1; - } - - if (!(s->status & (1 << 0))) { /* RWM */ - s->status |= 1 << 7; /* set IRF */ - s->data = data; - } - pxa2xx_i2c_update(s); - - return 1; -} - -static uint64_t pxa2xx_i2c_read(void *opaque, hwaddr addr, - unsigned size) -{ - PXA2xxI2CState *s = (PXA2xxI2CState *) opaque; - I2CSlave *slave; - - addr -= s->offset; - switch (addr) { - case ICR: - return s->control; - case ISR: - return s->status | (i2c_bus_busy(s->bus) << 2); - case ISAR: - slave = I2C_SLAVE(s->slave); - return slave->address; - case IDBR: - return s->data; - case IBMR: - if (s->status & (1 << 2)) - s->ibmr ^= 3; /* Fake SCL and SDA pin changes */ - else - s->ibmr = 0; - return s->ibmr; - default: - printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr); - break; - } - return 0; -} - -static void pxa2xx_i2c_write(void *opaque, hwaddr addr, - uint64_t value64, unsigned size) -{ - PXA2xxI2CState *s = (PXA2xxI2CState *) opaque; - uint32_t value = value64; - int ack; - - addr -= s->offset; - switch (addr) { - case ICR: - s->control = value & 0xfff7; - if ((value & (1 << 3)) && (value & (1 << 6))) { /* TB and IUE */ - /* TODO: slave mode */ - if (value & (1 << 0)) { /* START condition */ - if (s->data & 1) - s->status |= 1 << 0; /* set RWM */ - else - s->status &= ~(1 << 0); /* clear RWM */ - ack = !i2c_start_transfer(s->bus, s->data >> 1, s->data & 1); - } else { - if (s->status & (1 << 0)) { /* RWM */ - s->data = i2c_recv(s->bus); - if (value & (1 << 2)) /* ACKNAK */ - i2c_nack(s->bus); - ack = 1; - } else - ack = !i2c_send(s->bus, s->data); - } - - if (value & (1 << 1)) /* STOP condition */ - i2c_end_transfer(s->bus); - - if (ack) { - if (value & (1 << 0)) /* START condition */ - s->status |= 1 << 6; /* set ITE */ - else - if (s->status & (1 << 0)) /* RWM */ - s->status |= 1 << 7; /* set IRF */ - else - s->status |= 1 << 6; /* set ITE */ - s->status &= ~(1 << 1); /* clear ACKNAK */ - } else { - s->status |= 1 << 6; /* set ITE */ - s->status |= 1 << 10; /* set BED */ - s->status |= 1 << 1; /* set ACKNAK */ - } - } - if (!(value & (1 << 3)) && (value & (1 << 6))) /* !TB and IUE */ - if (value & (1 << 4)) /* MA */ - i2c_end_transfer(s->bus); - pxa2xx_i2c_update(s); - break; - - case ISR: - s->status &= ~(value & 0x07f0); - pxa2xx_i2c_update(s); - break; - - case ISAR: - i2c_set_slave_address(I2C_SLAVE(s->slave), value & 0x7f); - break; - - case IDBR: - s->data = value & 0xff; - break; - - default: - printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr); - } -} - -static const MemoryRegionOps pxa2xx_i2c_ops = { - .read = pxa2xx_i2c_read, - .write = pxa2xx_i2c_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_pxa2xx_i2c_slave = { - .name = "pxa2xx_i2c_slave", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_I2C_SLAVE(parent_obj, PXA2xxI2CSlaveState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_pxa2xx_i2c = { - .name = "pxa2xx_i2c", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT16(control, PXA2xxI2CState), - VMSTATE_UINT16(status, PXA2xxI2CState), - VMSTATE_UINT8(ibmr, PXA2xxI2CState), - VMSTATE_UINT8(data, PXA2xxI2CState), - VMSTATE_STRUCT_POINTER(slave, PXA2xxI2CState, - vmstate_pxa2xx_i2c_slave, PXA2xxI2CSlaveState), - VMSTATE_END_OF_LIST() - } -}; - -static int pxa2xx_i2c_slave_init(I2CSlave *i2c) -{ - /* Nothing to do. */ - return 0; -} - -static void pxa2xx_i2c_slave_class_init(ObjectClass *klass, void *data) -{ - I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - - k->init = pxa2xx_i2c_slave_init; - k->event = pxa2xx_i2c_event; - k->recv = pxa2xx_i2c_rx; - k->send = pxa2xx_i2c_tx; -} - -static const TypeInfo pxa2xx_i2c_slave_info = { - .name = TYPE_PXA2XX_I2C_SLAVE, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(PXA2xxI2CSlaveState), - .class_init = pxa2xx_i2c_slave_class_init, -}; - -PXA2xxI2CState *pxa2xx_i2c_init(hwaddr base, - qemu_irq irq, uint32_t region_size) -{ - DeviceState *dev; - SysBusDevice *i2c_dev; - PXA2xxI2CState *s; - I2CBus *i2cbus; - - dev = qdev_create(NULL, TYPE_PXA2XX_I2C); - qdev_prop_set_uint32(dev, "size", region_size + 1); - qdev_prop_set_uint32(dev, "offset", base & region_size); - qdev_init_nofail(dev); - - i2c_dev = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(i2c_dev, 0, base & ~region_size); - sysbus_connect_irq(i2c_dev, 0, irq); - - s = PXA2XX_I2C(i2c_dev); - /* FIXME: Should the slave device really be on a separate bus? */ - i2cbus = i2c_init_bus(dev, "dummy"); - dev = i2c_create_slave(i2cbus, TYPE_PXA2XX_I2C_SLAVE, 0); - s->slave = PXA2XX_I2C_SLAVE(dev); - s->slave->host = s; - - return s; -} - -static int pxa2xx_i2c_initfn(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - PXA2xxI2CState *s = PXA2XX_I2C(dev); - - s->bus = i2c_init_bus(dev, "i2c"); - - memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_i2c_ops, s, - "pxa2xx-i2c", s->region_size); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - - return 0; -} - -I2CBus *pxa2xx_i2c_bus(PXA2xxI2CState *s) -{ - return s->bus; -} - -static Property pxa2xx_i2c_properties[] = { - DEFINE_PROP_UINT32("size", PXA2xxI2CState, region_size, 0x10000), - DEFINE_PROP_UINT32("offset", PXA2xxI2CState, offset, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pxa2xx_i2c_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = pxa2xx_i2c_initfn; - dc->desc = "PXA2xx I2C Bus Controller"; - dc->vmsd = &vmstate_pxa2xx_i2c; - dc->props = pxa2xx_i2c_properties; -} - -static const TypeInfo pxa2xx_i2c_info = { - .name = TYPE_PXA2XX_I2C, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxI2CState), - .class_init = pxa2xx_i2c_class_init, -}; - -/* PXA Inter-IC Sound Controller */ -static void pxa2xx_i2s_reset(PXA2xxI2SState *i2s) -{ - i2s->rx_len = 0; - i2s->tx_len = 0; - i2s->fifo_len = 0; - i2s->clk = 0x1a; - i2s->control[0] = 0x00; - i2s->control[1] = 0x00; - i2s->status = 0x00; - i2s->mask = 0x00; -} - -#define SACR_TFTH(val) ((val >> 8) & 0xf) -#define SACR_RFTH(val) ((val >> 12) & 0xf) -#define SACR_DREC(val) (val & (1 << 3)) -#define SACR_DPRL(val) (val & (1 << 4)) - -static inline void pxa2xx_i2s_update(PXA2xxI2SState *i2s) -{ - int rfs, tfs; - rfs = SACR_RFTH(i2s->control[0]) < i2s->rx_len && - !SACR_DREC(i2s->control[1]); - tfs = (i2s->tx_len || i2s->fifo_len < SACR_TFTH(i2s->control[0])) && - i2s->enable && !SACR_DPRL(i2s->control[1]); - - qemu_set_irq(i2s->rx_dma, rfs); - qemu_set_irq(i2s->tx_dma, tfs); - - i2s->status &= 0xe0; - if (i2s->fifo_len < 16 || !i2s->enable) - i2s->status |= 1 << 0; /* TNF */ - if (i2s->rx_len) - i2s->status |= 1 << 1; /* RNE */ - if (i2s->enable) - i2s->status |= 1 << 2; /* BSY */ - if (tfs) - i2s->status |= 1 << 3; /* TFS */ - if (rfs) - i2s->status |= 1 << 4; /* RFS */ - if (!(i2s->tx_len && i2s->enable)) - i2s->status |= i2s->fifo_len << 8; /* TFL */ - i2s->status |= MAX(i2s->rx_len, 0xf) << 12; /* RFL */ - - qemu_set_irq(i2s->irq, i2s->status & i2s->mask); -} - -#define SACR0 0x00 /* Serial Audio Global Control register */ -#define SACR1 0x04 /* Serial Audio I2S/MSB-Justified Control register */ -#define SASR0 0x0c /* Serial Audio Interface and FIFO Status register */ -#define SAIMR 0x14 /* Serial Audio Interrupt Mask register */ -#define SAICR 0x18 /* Serial Audio Interrupt Clear register */ -#define SADIV 0x60 /* Serial Audio Clock Divider register */ -#define SADR 0x80 /* Serial Audio Data register */ - -static uint64_t pxa2xx_i2s_read(void *opaque, hwaddr addr, - unsigned size) -{ - PXA2xxI2SState *s = (PXA2xxI2SState *) opaque; - - switch (addr) { - case SACR0: - return s->control[0]; - case SACR1: - return s->control[1]; - case SASR0: - return s->status; - case SAIMR: - return s->mask; - case SAICR: - return 0; - case SADIV: - return s->clk; - case SADR: - if (s->rx_len > 0) { - s->rx_len --; - pxa2xx_i2s_update(s); - return s->codec_in(s->opaque); - } - return 0; - default: - printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr); - break; - } - return 0; -} - -static void pxa2xx_i2s_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - PXA2xxI2SState *s = (PXA2xxI2SState *) opaque; - uint32_t *sample; - - switch (addr) { - case SACR0: - if (value & (1 << 3)) /* RST */ - pxa2xx_i2s_reset(s); - s->control[0] = value & 0xff3d; - if (!s->enable && (value & 1) && s->tx_len) { /* ENB */ - for (sample = s->fifo; s->fifo_len > 0; s->fifo_len --, sample ++) - s->codec_out(s->opaque, *sample); - s->status &= ~(1 << 7); /* I2SOFF */ - } - if (value & (1 << 4)) /* EFWR */ - printf("%s: Attempt to use special function\n", __FUNCTION__); - s->enable = (value & 9) == 1; /* ENB && !RST*/ - pxa2xx_i2s_update(s); - break; - case SACR1: - s->control[1] = value & 0x0039; - if (value & (1 << 5)) /* ENLBF */ - printf("%s: Attempt to use loopback function\n", __FUNCTION__); - if (value & (1 << 4)) /* DPRL */ - s->fifo_len = 0; - pxa2xx_i2s_update(s); - break; - case SAIMR: - s->mask = value & 0x0078; - pxa2xx_i2s_update(s); - break; - case SAICR: - s->status &= ~(value & (3 << 5)); - pxa2xx_i2s_update(s); - break; - case SADIV: - s->clk = value & 0x007f; - break; - case SADR: - if (s->tx_len && s->enable) { - s->tx_len --; - pxa2xx_i2s_update(s); - s->codec_out(s->opaque, value); - } else if (s->fifo_len < 16) { - s->fifo[s->fifo_len ++] = value; - pxa2xx_i2s_update(s); - } - break; - default: - printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr); - } -} - -static const MemoryRegionOps pxa2xx_i2s_ops = { - .read = pxa2xx_i2s_read, - .write = pxa2xx_i2s_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_pxa2xx_i2s = { - .name = "pxa2xx_i2s", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(control, PXA2xxI2SState, 2), - VMSTATE_UINT32(status, PXA2xxI2SState), - VMSTATE_UINT32(mask, PXA2xxI2SState), - VMSTATE_UINT32(clk, PXA2xxI2SState), - VMSTATE_INT32(enable, PXA2xxI2SState), - VMSTATE_INT32(rx_len, PXA2xxI2SState), - VMSTATE_INT32(tx_len, PXA2xxI2SState), - VMSTATE_INT32(fifo_len, PXA2xxI2SState), - VMSTATE_END_OF_LIST() - } -}; - -static void pxa2xx_i2s_data_req(void *opaque, int tx, int rx) -{ - PXA2xxI2SState *s = (PXA2xxI2SState *) opaque; - uint32_t *sample; - - /* Signal FIFO errors */ - if (s->enable && s->tx_len) - s->status |= 1 << 5; /* TUR */ - if (s->enable && s->rx_len) - s->status |= 1 << 6; /* ROR */ - - /* Should be tx - MIN(tx, s->fifo_len) but we don't really need to - * handle the cases where it makes a difference. */ - s->tx_len = tx - s->fifo_len; - s->rx_len = rx; - /* Note that is s->codec_out wasn't set, we wouldn't get called. */ - if (s->enable) - for (sample = s->fifo; s->fifo_len; s->fifo_len --, sample ++) - s->codec_out(s->opaque, *sample); - pxa2xx_i2s_update(s); -} - -static PXA2xxI2SState *pxa2xx_i2s_init(MemoryRegion *sysmem, - hwaddr base, - qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma) -{ - PXA2xxI2SState *s = g_new0(PXA2xxI2SState, 1); - - s->irq = irq; - s->rx_dma = rx_dma; - s->tx_dma = tx_dma; - s->data_req = pxa2xx_i2s_data_req; - - pxa2xx_i2s_reset(s); - - memory_region_init_io(&s->iomem, NULL, &pxa2xx_i2s_ops, s, - "pxa2xx-i2s", 0x100000); - memory_region_add_subregion(sysmem, base, &s->iomem); - - vmstate_register(NULL, base, &vmstate_pxa2xx_i2s, s); - - return s; -} - -/* PXA Fast Infra-red Communications Port */ -#define TYPE_PXA2XX_FIR "pxa2xx-fir" -#define PXA2XX_FIR(obj) OBJECT_CHECK(PXA2xxFIrState, (obj), TYPE_PXA2XX_FIR) - -struct PXA2xxFIrState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - qemu_irq irq; - qemu_irq rx_dma; - qemu_irq tx_dma; - uint32_t enable; - CharDriverState *chr; - - uint8_t control[3]; - uint8_t status[2]; - - uint32_t rx_len; - uint32_t rx_start; - uint8_t rx_fifo[64]; -}; - -static void pxa2xx_fir_reset(DeviceState *d) -{ - PXA2xxFIrState *s = PXA2XX_FIR(d); - - s->control[0] = 0x00; - s->control[1] = 0x00; - s->control[2] = 0x00; - s->status[0] = 0x00; - s->status[1] = 0x00; - s->enable = 0; -} - -static inline void pxa2xx_fir_update(PXA2xxFIrState *s) -{ - static const int tresh[4] = { 8, 16, 32, 0 }; - int intr = 0; - if ((s->control[0] & (1 << 4)) && /* RXE */ - s->rx_len >= tresh[s->control[2] & 3]) /* TRIG */ - s->status[0] |= 1 << 4; /* RFS */ - else - s->status[0] &= ~(1 << 4); /* RFS */ - if (s->control[0] & (1 << 3)) /* TXE */ - s->status[0] |= 1 << 3; /* TFS */ - else - s->status[0] &= ~(1 << 3); /* TFS */ - if (s->rx_len) - s->status[1] |= 1 << 2; /* RNE */ - else - s->status[1] &= ~(1 << 2); /* RNE */ - if (s->control[0] & (1 << 4)) /* RXE */ - s->status[1] |= 1 << 0; /* RSY */ - else - s->status[1] &= ~(1 << 0); /* RSY */ - - intr |= (s->control[0] & (1 << 5)) && /* RIE */ - (s->status[0] & (1 << 4)); /* RFS */ - intr |= (s->control[0] & (1 << 6)) && /* TIE */ - (s->status[0] & (1 << 3)); /* TFS */ - intr |= (s->control[2] & (1 << 4)) && /* TRAIL */ - (s->status[0] & (1 << 6)); /* EOC */ - intr |= (s->control[0] & (1 << 2)) && /* TUS */ - (s->status[0] & (1 << 1)); /* TUR */ - intr |= s->status[0] & 0x25; /* FRE, RAB, EIF */ - - qemu_set_irq(s->rx_dma, (s->status[0] >> 4) & 1); - qemu_set_irq(s->tx_dma, (s->status[0] >> 3) & 1); - - qemu_set_irq(s->irq, intr && s->enable); -} - -#define ICCR0 0x00 /* FICP Control register 0 */ -#define ICCR1 0x04 /* FICP Control register 1 */ -#define ICCR2 0x08 /* FICP Control register 2 */ -#define ICDR 0x0c /* FICP Data register */ -#define ICSR0 0x14 /* FICP Status register 0 */ -#define ICSR1 0x18 /* FICP Status register 1 */ -#define ICFOR 0x1c /* FICP FIFO Occupancy Status register */ - -static uint64_t pxa2xx_fir_read(void *opaque, hwaddr addr, - unsigned size) -{ - PXA2xxFIrState *s = (PXA2xxFIrState *) opaque; - uint8_t ret; - - switch (addr) { - case ICCR0: - return s->control[0]; - case ICCR1: - return s->control[1]; - case ICCR2: - return s->control[2]; - case ICDR: - s->status[0] &= ~0x01; - s->status[1] &= ~0x72; - if (s->rx_len) { - s->rx_len --; - ret = s->rx_fifo[s->rx_start ++]; - s->rx_start &= 63; - pxa2xx_fir_update(s); - return ret; - } - printf("%s: Rx FIFO underrun.\n", __FUNCTION__); - break; - case ICSR0: - return s->status[0]; - case ICSR1: - return s->status[1] | (1 << 3); /* TNF */ - case ICFOR: - return s->rx_len; - default: - printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr); - break; - } - return 0; -} - -static void pxa2xx_fir_write(void *opaque, hwaddr addr, - uint64_t value64, unsigned size) -{ - PXA2xxFIrState *s = (PXA2xxFIrState *) opaque; - uint32_t value = value64; - uint8_t ch; - - switch (addr) { - case ICCR0: - s->control[0] = value; - if (!(value & (1 << 4))) /* RXE */ - s->rx_len = s->rx_start = 0; - if (!(value & (1 << 3))) { /* TXE */ - /* Nop */ - } - s->enable = value & 1; /* ITR */ - if (!s->enable) - s->status[0] = 0; - pxa2xx_fir_update(s); - break; - case ICCR1: - s->control[1] = value; - break; - case ICCR2: - s->control[2] = value & 0x3f; - pxa2xx_fir_update(s); - break; - case ICDR: - if (s->control[2] & (1 << 2)) /* TXP */ - ch = value; - else - ch = ~value; - if (s->chr && s->enable && (s->control[0] & (1 << 3))) /* TXE */ - qemu_chr_fe_write(s->chr, &ch, 1); - break; - case ICSR0: - s->status[0] &= ~(value & 0x66); - pxa2xx_fir_update(s); - break; - case ICFOR: - break; - default: - printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr); - } -} - -static const MemoryRegionOps pxa2xx_fir_ops = { - .read = pxa2xx_fir_read, - .write = pxa2xx_fir_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int pxa2xx_fir_is_empty(void *opaque) -{ - PXA2xxFIrState *s = (PXA2xxFIrState *) opaque; - return (s->rx_len < 64); -} - -static void pxa2xx_fir_rx(void *opaque, const uint8_t *buf, int size) -{ - PXA2xxFIrState *s = (PXA2xxFIrState *) opaque; - if (!(s->control[0] & (1 << 4))) /* RXE */ - return; - - while (size --) { - s->status[1] |= 1 << 4; /* EOF */ - if (s->rx_len >= 64) { - s->status[1] |= 1 << 6; /* ROR */ - break; - } - - if (s->control[2] & (1 << 3)) /* RXP */ - s->rx_fifo[(s->rx_start + s->rx_len ++) & 63] = *(buf ++); - else - s->rx_fifo[(s->rx_start + s->rx_len ++) & 63] = ~*(buf ++); - } - - pxa2xx_fir_update(s); -} - -static void pxa2xx_fir_event(void *opaque, int event) -{ -} - -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, obj, &pxa2xx_fir_ops, s, - "pxa2xx-fir", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - sysbus_init_irq(sbd, &s->rx_dma); - sysbus_init_irq(sbd, &s->tx_dma); -} - -static void pxa2xx_fir_realize(DeviceState *dev, Error **errp) -{ - PXA2xxFIrState *s = PXA2XX_FIR(dev); - - if (s->chr) { - qemu_chr_fe_claim_no_fail(s->chr); - qemu_chr_add_handlers(s->chr, pxa2xx_fir_is_empty, - pxa2xx_fir_rx, pxa2xx_fir_event, s); - } -} - -static bool pxa2xx_fir_vmstate_validate(void *opaque, int version_id) -{ - PXA2xxFIrState *s = opaque; - - return s->rx_start < ARRAY_SIZE(s->rx_fifo); -} - -static const VMStateDescription pxa2xx_fir_vmsd = { - .name = "pxa2xx-fir", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(enable, PXA2xxFIrState), - VMSTATE_UINT8_ARRAY(control, PXA2xxFIrState, 3), - VMSTATE_UINT8_ARRAY(status, PXA2xxFIrState, 2), - VMSTATE_UINT32(rx_len, PXA2xxFIrState), - VMSTATE_UINT32(rx_start, PXA2xxFIrState), - VMSTATE_VALIDATE("fifo is 64 bytes", pxa2xx_fir_vmstate_validate), - VMSTATE_UINT8_ARRAY(rx_fifo, PXA2xxFIrState, 64), - VMSTATE_END_OF_LIST() - } -}; - -static Property pxa2xx_fir_properties[] = { - DEFINE_PROP_CHR("chardev", PXA2xxFIrState, chr), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pxa2xx_fir_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = pxa2xx_fir_realize; - dc->vmsd = &pxa2xx_fir_vmsd; - dc->props = pxa2xx_fir_properties; - dc->reset = pxa2xx_fir_reset; -} - -static const TypeInfo pxa2xx_fir_info = { - .name = TYPE_PXA2XX_FIR, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxFIrState), - .class_init = pxa2xx_fir_class_init, - .instance_init = pxa2xx_fir_instance_init, -}; - -static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem, - hwaddr base, - qemu_irq irq, qemu_irq rx_dma, - qemu_irq tx_dma, - CharDriverState *chr) -{ - DeviceState *dev; - SysBusDevice *sbd; - - dev = qdev_create(NULL, TYPE_PXA2XX_FIR); - qdev_prop_set_chr(dev, "chardev", chr); - qdev_init_nofail(dev); - sbd = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(sbd, 0, base); - sysbus_connect_irq(sbd, 0, irq); - sysbus_connect_irq(sbd, 1, rx_dma); - sysbus_connect_irq(sbd, 2, tx_dma); - return PXA2XX_FIR(dev); -} - -static void pxa2xx_reset(void *opaque, int line, int level) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - - if (level && (s->pm_regs[PCFR >> 2] & 0x10)) { /* GPR_EN */ - cpu_reset(CPU(s->cpu)); - /* TODO: reset peripherals */ - } -} - -/* Initialise a PXA270 integrated chip (ARM based core). */ -PXA2xxState *pxa270_init(MemoryRegion *address_space, - unsigned int sdram_size, const char *revision) -{ - PXA2xxState *s; - int i; - DriveInfo *dinfo; - s = g_new0(PXA2xxState, 1); - - if (revision && strncmp(revision, "pxa27", 5)) { - fprintf(stderr, "Machine requires a PXA27x processor.\n"); - exit(1); - } - if (!revision) - revision = "pxa270"; - - s->cpu = cpu_arm_init(revision); - if (s->cpu == NULL) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - s->reset = qemu_allocate_irq(pxa2xx_reset, s, 0); - - /* SDRAM & Internal Memory Storage */ - memory_region_init_ram(&s->sdram, NULL, "pxa270.sdram", sdram_size, - &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_fatal); - vmstate_register_ram_global(&s->internal); - memory_region_add_subregion(address_space, PXA2XX_INTERNAL_BASE, - &s->internal); - - s->pic = pxa2xx_pic_init(0x40d00000, s->cpu); - - s->dma = pxa27x_dma_init(0x40000000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_DMA)); - - sysbus_create_varargs("pxa27x-timer", 0x40a00000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 0), - qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 1), - qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 2), - qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 3), - qdev_get_gpio_in(s->pic, PXA27X_PIC_OST_4_11), - NULL); - - s->gpio = pxa2xx_gpio_init(0x40e00000, s->cpu, s->pic, 121); - - dinfo = drive_get(IF_SD, 0, 0); - if (!dinfo) { - fprintf(stderr, "qemu: missing SecureDigital device\n"); - exit(1); - } - s->mmc = pxa2xx_mmci_init(address_space, 0x41100000, - blk_by_legacy_dinfo(dinfo), - qdev_get_gpio_in(s->pic, PXA2XX_PIC_MMC), - qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_MMCI), - qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI)); - - for (i = 0; pxa270_serial[i].io_base; i++) { - if (serial_hds[i]) { - serial_mm_init(address_space, pxa270_serial[i].io_base, 2, - qdev_get_gpio_in(s->pic, pxa270_serial[i].irqn), - 14857000 / 16, serial_hds[i], - DEVICE_NATIVE_ENDIAN); - } else { - break; - } - } - if (serial_hds[i]) - s->fir = pxa2xx_fir_init(address_space, 0x40800000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_ICP), - qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_ICP), - qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_ICP), - serial_hds[i]); - - s->lcd = pxa2xx_lcdc_init(address_space, 0x44000000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_LCD)); - - s->cm_base = 0x41300000; - s->cm_regs[CCCR >> 2] = 0x02000210; /* 416.0 MHz */ - s->clkcfg = 0x00000009; /* Turbo mode active */ - memory_region_init_io(&s->cm_iomem, NULL, &pxa2xx_cm_ops, s, "pxa2xx-cm", 0x1000); - memory_region_add_subregion(address_space, s->cm_base, &s->cm_iomem); - vmstate_register(NULL, 0, &vmstate_pxa2xx_cm, s); - - pxa2xx_setup_cp14(s); - - s->mm_base = 0x48000000; - s->mm_regs[MDMRS >> 2] = 0x00020002; - s->mm_regs[MDREFR >> 2] = 0x03ca4000; - s->mm_regs[MECR >> 2] = 0x00000001; /* Two PC Card sockets */ - memory_region_init_io(&s->mm_iomem, NULL, &pxa2xx_mm_ops, s, "pxa2xx-mm", 0x1000); - memory_region_add_subregion(address_space, s->mm_base, &s->mm_iomem); - vmstate_register(NULL, 0, &vmstate_pxa2xx_mm, s); - - s->pm_base = 0x40f00000; - memory_region_init_io(&s->pm_iomem, NULL, &pxa2xx_pm_ops, s, "pxa2xx-pm", 0x100); - memory_region_add_subregion(address_space, s->pm_base, &s->pm_iomem); - vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s); - - for (i = 0; pxa27x_ssp[i].io_base; 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, - qdev_get_gpio_in(s->pic, pxa27x_ssp[i].irqn)); - s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi"); - } - - if (usb_enabled()) { - sysbus_create_simple("sysbus-ohci", 0x4c000000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1)); - } - - s->pcmcia[0] = pxa2xx_pcmcia_init(address_space, 0x20000000); - s->pcmcia[1] = pxa2xx_pcmcia_init(address_space, 0x30000000); - - sysbus_create_simple(TYPE_PXA2XX_RTC, 0x40900000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_RTCALARM)); - - s->i2c[0] = pxa2xx_i2c_init(0x40301600, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2C), 0xffff); - s->i2c[1] = pxa2xx_i2c_init(0x40f00100, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_PWRI2C), 0xff); - - s->i2s = pxa2xx_i2s_init(address_space, 0x40400000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2S), - qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_I2S), - qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_I2S)); - - s->kp = pxa27x_keypad_init(address_space, 0x41500000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_KEYPAD)); - - /* GPIO1 resets the processor */ - /* The handler can be overridden by board-specific code */ - qdev_connect_gpio_out(s->gpio, 1, s->reset); - return s; -} - -/* Initialise a PXA255 integrated chip (ARM based core). */ -PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) -{ - PXA2xxState *s; - int i; - DriveInfo *dinfo; - - s = g_new0(PXA2xxState, 1); - - s->cpu = cpu_arm_init("pxa255"); - if (s->cpu == NULL) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - s->reset = qemu_allocate_irq(pxa2xx_reset, s, 0); - - /* SDRAM & Internal Memory Storage */ - memory_region_init_ram(&s->sdram, NULL, "pxa255.sdram", sdram_size, - &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_fatal); - vmstate_register_ram_global(&s->internal); - memory_region_add_subregion(address_space, PXA2XX_INTERNAL_BASE, - &s->internal); - - s->pic = pxa2xx_pic_init(0x40d00000, s->cpu); - - s->dma = pxa255_dma_init(0x40000000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_DMA)); - - sysbus_create_varargs("pxa25x-timer", 0x40a00000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 0), - qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 1), - qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 2), - qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 3), - NULL); - - s->gpio = pxa2xx_gpio_init(0x40e00000, s->cpu, s->pic, 85); - - dinfo = drive_get(IF_SD, 0, 0); - if (!dinfo) { - fprintf(stderr, "qemu: missing SecureDigital device\n"); - exit(1); - } - s->mmc = pxa2xx_mmci_init(address_space, 0x41100000, - blk_by_legacy_dinfo(dinfo), - qdev_get_gpio_in(s->pic, PXA2XX_PIC_MMC), - qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_MMCI), - qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI)); - - for (i = 0; pxa255_serial[i].io_base; i++) { - if (serial_hds[i]) { - serial_mm_init(address_space, pxa255_serial[i].io_base, 2, - qdev_get_gpio_in(s->pic, pxa255_serial[i].irqn), - 14745600 / 16, serial_hds[i], - DEVICE_NATIVE_ENDIAN); - } else { - break; - } - } - if (serial_hds[i]) - s->fir = pxa2xx_fir_init(address_space, 0x40800000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_ICP), - qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_ICP), - qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_ICP), - serial_hds[i]); - - s->lcd = pxa2xx_lcdc_init(address_space, 0x44000000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_LCD)); - - s->cm_base = 0x41300000; - s->cm_regs[CCCR >> 2] = 0x02000210; /* 416.0 MHz */ - s->clkcfg = 0x00000009; /* Turbo mode active */ - memory_region_init_io(&s->cm_iomem, NULL, &pxa2xx_cm_ops, s, "pxa2xx-cm", 0x1000); - memory_region_add_subregion(address_space, s->cm_base, &s->cm_iomem); - vmstate_register(NULL, 0, &vmstate_pxa2xx_cm, s); - - pxa2xx_setup_cp14(s); - - s->mm_base = 0x48000000; - s->mm_regs[MDMRS >> 2] = 0x00020002; - s->mm_regs[MDREFR >> 2] = 0x03ca4000; - s->mm_regs[MECR >> 2] = 0x00000001; /* Two PC Card sockets */ - memory_region_init_io(&s->mm_iomem, NULL, &pxa2xx_mm_ops, s, "pxa2xx-mm", 0x1000); - memory_region_add_subregion(address_space, s->mm_base, &s->mm_iomem); - vmstate_register(NULL, 0, &vmstate_pxa2xx_mm, s); - - s->pm_base = 0x40f00000; - memory_region_init_io(&s->pm_iomem, NULL, &pxa2xx_pm_ops, s, "pxa2xx-pm", 0x100); - memory_region_add_subregion(address_space, s->pm_base, &s->pm_iomem); - vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s); - - for (i = 0; pxa255_ssp[i].io_base; 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, - qdev_get_gpio_in(s->pic, pxa255_ssp[i].irqn)); - s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi"); - } - - if (usb_enabled()) { - sysbus_create_simple("sysbus-ohci", 0x4c000000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1)); - } - - s->pcmcia[0] = pxa2xx_pcmcia_init(address_space, 0x20000000); - s->pcmcia[1] = pxa2xx_pcmcia_init(address_space, 0x30000000); - - sysbus_create_simple(TYPE_PXA2XX_RTC, 0x40900000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_RTCALARM)); - - s->i2c[0] = pxa2xx_i2c_init(0x40301600, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2C), 0xffff); - s->i2c[1] = pxa2xx_i2c_init(0x40f00100, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_PWRI2C), 0xff); - - s->i2s = pxa2xx_i2s_init(address_space, 0x40400000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2S), - qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_I2S), - qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_I2S)); - - /* GPIO1 resets the processor */ - /* The handler can be overridden by board-specific code */ - qdev_connect_gpio_out(s->gpio, 1, s->reset); - return s; -} - -static void pxa2xx_ssp_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - sdc->init = pxa2xx_ssp_init; - dc->reset = pxa2xx_ssp_reset; - dc->vmsd = &vmstate_pxa2xx_ssp; -} - -static const TypeInfo pxa2xx_ssp_info = { - .name = TYPE_PXA2XX_SSP, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxSSPState), - .class_init = pxa2xx_ssp_class_init, -}; - -static void pxa2xx_register_types(void) -{ - type_register_static(&pxa2xx_i2c_slave_info); - type_register_static(&pxa2xx_ssp_info); - type_register_static(&pxa2xx_i2c_info); - type_register_static(&pxa2xx_rtc_sysbus_info); - type_register_static(&pxa2xx_fir_info); -} - -type_init(pxa2xx_register_types) diff --git a/qemu/hw/arm/pxa2xx_gpio.c b/qemu/hw/arm/pxa2xx_gpio.c deleted file mode 100644 index 67e7e7094..000000000 --- a/qemu/hw/arm/pxa2xx_gpio.c +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Intel XScale PXA255/270 GPIO controller emulation. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "hw/arm/pxa.h" - -#define PXA2XX_GPIO_BANKS 4 - -#define TYPE_PXA2XX_GPIO "pxa2xx-gpio" -#define PXA2XX_GPIO(obj) \ - OBJECT_CHECK(PXA2xxGPIOInfo, (obj), TYPE_PXA2XX_GPIO) - -typedef struct PXA2xxGPIOInfo PXA2xxGPIOInfo; -struct PXA2xxGPIOInfo { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - qemu_irq irq0, irq1, irqX; - int lines; - int ncpu; - ARMCPU *cpu; - - /* XXX: GNU C vectors are more suitable */ - uint32_t ilevel[PXA2XX_GPIO_BANKS]; - uint32_t olevel[PXA2XX_GPIO_BANKS]; - uint32_t dir[PXA2XX_GPIO_BANKS]; - uint32_t rising[PXA2XX_GPIO_BANKS]; - uint32_t falling[PXA2XX_GPIO_BANKS]; - uint32_t status[PXA2XX_GPIO_BANKS]; - uint32_t gafr[PXA2XX_GPIO_BANKS * 2]; - - uint32_t prev_level[PXA2XX_GPIO_BANKS]; - qemu_irq handler[PXA2XX_GPIO_BANKS * 32]; - qemu_irq read_notify; -}; - -static struct { - enum { - GPIO_NONE, - GPLR, - GPSR, - GPCR, - GPDR, - GRER, - GFER, - GEDR, - GAFR_L, - GAFR_U, - } reg; - int bank; -} pxa2xx_gpio_regs[0x200] = { - [0 ... 0x1ff] = { GPIO_NONE, 0 }, -#define PXA2XX_REG(reg, a0, a1, a2, a3) \ - [a0] = { reg, 0 }, [a1] = { reg, 1 }, [a2] = { reg, 2 }, [a3] = { reg, 3 }, - - PXA2XX_REG(GPLR, 0x000, 0x004, 0x008, 0x100) - PXA2XX_REG(GPSR, 0x018, 0x01c, 0x020, 0x118) - PXA2XX_REG(GPCR, 0x024, 0x028, 0x02c, 0x124) - PXA2XX_REG(GPDR, 0x00c, 0x010, 0x014, 0x10c) - PXA2XX_REG(GRER, 0x030, 0x034, 0x038, 0x130) - PXA2XX_REG(GFER, 0x03c, 0x040, 0x044, 0x13c) - PXA2XX_REG(GEDR, 0x048, 0x04c, 0x050, 0x148) - PXA2XX_REG(GAFR_L, 0x054, 0x05c, 0x064, 0x06c) - PXA2XX_REG(GAFR_U, 0x058, 0x060, 0x068, 0x070) -}; - -static void pxa2xx_gpio_irq_update(PXA2xxGPIOInfo *s) -{ - if (s->status[0] & (1 << 0)) - qemu_irq_raise(s->irq0); - else - qemu_irq_lower(s->irq0); - - if (s->status[0] & (1 << 1)) - qemu_irq_raise(s->irq1); - else - qemu_irq_lower(s->irq1); - - if ((s->status[0] & ~3) | s->status[1] | s->status[2] | s->status[3]) - qemu_irq_raise(s->irqX); - else - qemu_irq_lower(s->irqX); -} - -/* Bitmap of pins used as standby and sleep wake-up sources. */ -static const int pxa2xx_gpio_wake[PXA2XX_GPIO_BANKS] = { - 0x8003fe1b, 0x002001fc, 0xec080000, 0x0012007f, -}; - -static void pxa2xx_gpio_set(void *opaque, int line, int level) -{ - PXA2xxGPIOInfo *s = (PXA2xxGPIOInfo *) opaque; - CPUState *cpu = CPU(s->cpu); - int bank; - uint32_t mask; - - if (line >= s->lines) { - printf("%s: No GPIO pin %i\n", __FUNCTION__, line); - return; - } - - bank = line >> 5; - mask = 1U << (line & 31); - - if (level) { - s->status[bank] |= s->rising[bank] & mask & - ~s->ilevel[bank] & ~s->dir[bank]; - s->ilevel[bank] |= mask; - } else { - s->status[bank] |= s->falling[bank] & mask & - s->ilevel[bank] & ~s->dir[bank]; - s->ilevel[bank] &= ~mask; - } - - if (s->status[bank] & mask) - pxa2xx_gpio_irq_update(s); - - /* Wake-up GPIOs */ - if (cpu->halted && (mask & ~s->dir[bank] & pxa2xx_gpio_wake[bank])) { - cpu_interrupt(cpu, CPU_INTERRUPT_EXITTB); - } -} - -static void pxa2xx_gpio_handler_update(PXA2xxGPIOInfo *s) { - uint32_t level, diff; - int i, bit, line; - for (i = 0; i < PXA2XX_GPIO_BANKS; i ++) { - level = s->olevel[i] & s->dir[i]; - - for (diff = s->prev_level[i] ^ level; diff; diff ^= 1 << bit) { - bit = ctz32(diff); - line = bit + 32 * i; - qemu_set_irq(s->handler[line], (level >> bit) & 1); - } - - s->prev_level[i] = level; - } -} - -static uint64_t pxa2xx_gpio_read(void *opaque, hwaddr offset, - unsigned size) -{ - PXA2xxGPIOInfo *s = (PXA2xxGPIOInfo *) opaque; - uint32_t ret; - int bank; - if (offset >= 0x200) - return 0; - - bank = pxa2xx_gpio_regs[offset].bank; - switch (pxa2xx_gpio_regs[offset].reg) { - case GPDR: /* GPIO Pin-Direction registers */ - return s->dir[bank]; - - case GPSR: /* GPIO Pin-Output Set registers */ - qemu_log_mask(LOG_GUEST_ERROR, - "pxa2xx GPIO: read from write only register GPSR\n"); - return 0; - - case GPCR: /* GPIO Pin-Output Clear registers */ - qemu_log_mask(LOG_GUEST_ERROR, - "pxa2xx GPIO: read from write only register GPCR\n"); - return 0; - - case GRER: /* GPIO Rising-Edge Detect Enable registers */ - return s->rising[bank]; - - case GFER: /* GPIO Falling-Edge Detect Enable registers */ - return s->falling[bank]; - - case GAFR_L: /* GPIO Alternate Function registers */ - return s->gafr[bank * 2]; - - case GAFR_U: /* GPIO Alternate Function registers */ - return s->gafr[bank * 2 + 1]; - - case GPLR: /* GPIO Pin-Level registers */ - ret = (s->olevel[bank] & s->dir[bank]) | - (s->ilevel[bank] & ~s->dir[bank]); - qemu_irq_raise(s->read_notify); - return ret; - - case GEDR: /* GPIO Edge Detect Status registers */ - return s->status[bank]; - - default: - hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset); - } - - return 0; -} - -static void pxa2xx_gpio_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxGPIOInfo *s = (PXA2xxGPIOInfo *) opaque; - int bank; - if (offset >= 0x200) - return; - - bank = pxa2xx_gpio_regs[offset].bank; - switch (pxa2xx_gpio_regs[offset].reg) { - case GPDR: /* GPIO Pin-Direction registers */ - s->dir[bank] = value; - pxa2xx_gpio_handler_update(s); - break; - - case GPSR: /* GPIO Pin-Output Set registers */ - s->olevel[bank] |= value; - pxa2xx_gpio_handler_update(s); - break; - - case GPCR: /* GPIO Pin-Output Clear registers */ - s->olevel[bank] &= ~value; - pxa2xx_gpio_handler_update(s); - break; - - case GRER: /* GPIO Rising-Edge Detect Enable registers */ - s->rising[bank] = value; - break; - - case GFER: /* GPIO Falling-Edge Detect Enable registers */ - s->falling[bank] = value; - break; - - case GAFR_L: /* GPIO Alternate Function registers */ - s->gafr[bank * 2] = value; - break; - - case GAFR_U: /* GPIO Alternate Function registers */ - s->gafr[bank * 2 + 1] = value; - break; - - case GEDR: /* GPIO Edge Detect Status registers */ - s->status[bank] &= ~value; - pxa2xx_gpio_irq_update(s); - break; - - default: - hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset); - } -} - -static const MemoryRegionOps pxa_gpio_ops = { - .read = pxa2xx_gpio_read, - .write = pxa2xx_gpio_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -DeviceState *pxa2xx_gpio_init(hwaddr base, - ARMCPU *cpu, DeviceState *pic, int lines) -{ - CPUState *cs = CPU(cpu); - DeviceState *dev; - - dev = qdev_create(NULL, TYPE_PXA2XX_GPIO); - qdev_prop_set_int32(dev, "lines", lines); - qdev_prop_set_int32(dev, "ncpu", cs->cpu_index); - qdev_init_nofail(dev); - - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, - qdev_get_gpio_in(pic, PXA2XX_PIC_GPIO_0)); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, - qdev_get_gpio_in(pic, PXA2XX_PIC_GPIO_1)); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, - qdev_get_gpio_in(pic, PXA2XX_PIC_GPIO_X)); - - return dev; -} - -static int pxa2xx_gpio_initfn(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - PXA2xxGPIOInfo *s = PXA2XX_GPIO(dev); - - s->cpu = ARM_CPU(qemu_get_cpu(s->ncpu)); - - qdev_init_gpio_in(dev, pxa2xx_gpio_set, s->lines); - qdev_init_gpio_out(dev, s->handler, s->lines); - - memory_region_init_io(&s->iomem, OBJECT(s), &pxa_gpio_ops, s, "pxa2xx-gpio", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq0); - sysbus_init_irq(sbd, &s->irq1); - sysbus_init_irq(sbd, &s->irqX); - - return 0; -} - -/* - * Registers a callback to notify on GPLR reads. This normally - * shouldn't be needed but it is used for the hack on Spitz machines. - */ -void pxa2xx_gpio_read_notifier(DeviceState *dev, qemu_irq handler) -{ - PXA2xxGPIOInfo *s = PXA2XX_GPIO(dev); - - s->read_notify = handler; -} - -static const VMStateDescription vmstate_pxa2xx_gpio_regs = { - .name = "pxa2xx-gpio", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(ilevel, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), - VMSTATE_UINT32_ARRAY(olevel, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), - VMSTATE_UINT32_ARRAY(dir, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), - VMSTATE_UINT32_ARRAY(rising, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), - VMSTATE_UINT32_ARRAY(falling, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), - VMSTATE_UINT32_ARRAY(status, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), - VMSTATE_UINT32_ARRAY(gafr, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS * 2), - VMSTATE_UINT32_ARRAY(prev_level, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), - VMSTATE_END_OF_LIST(), - }, -}; - -static Property pxa2xx_gpio_properties[] = { - DEFINE_PROP_INT32("lines", PXA2xxGPIOInfo, lines, 0), - DEFINE_PROP_INT32("ncpu", PXA2xxGPIOInfo, ncpu, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pxa2xx_gpio_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = pxa2xx_gpio_initfn; - dc->desc = "PXA2xx GPIO controller"; - dc->props = pxa2xx_gpio_properties; - dc->vmsd = &vmstate_pxa2xx_gpio_regs; -} - -static const TypeInfo pxa2xx_gpio_info = { - .name = TYPE_PXA2XX_GPIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxGPIOInfo), - .class_init = pxa2xx_gpio_class_init, -}; - -static void pxa2xx_gpio_register_types(void) -{ - type_register_static(&pxa2xx_gpio_info); -} - -type_init(pxa2xx_gpio_register_types) diff --git a/qemu/hw/arm/pxa2xx_pic.c b/qemu/hw/arm/pxa2xx_pic.c deleted file mode 100644 index 7e51532cd..000000000 --- a/qemu/hw/arm/pxa2xx_pic.c +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Intel XScale PXA Programmable Interrupt Controller. - * - * Copyright (c) 2006 Openedhand Ltd. - * Copyright (c) 2006 Thorsten Zitterell - * Written by Andrzej Zaborowski - * - * 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" - -#define ICIP 0x00 /* Interrupt Controller IRQ Pending register */ -#define ICMR 0x04 /* Interrupt Controller Mask register */ -#define ICLR 0x08 /* Interrupt Controller Level register */ -#define ICFP 0x0c /* Interrupt Controller FIQ Pending register */ -#define ICPR 0x10 /* Interrupt Controller Pending register */ -#define ICCR 0x14 /* Interrupt Controller Control register */ -#define ICHP 0x18 /* Interrupt Controller Highest Priority register */ -#define IPR0 0x1c /* Interrupt Controller Priority register 0 */ -#define IPR31 0x98 /* Interrupt Controller Priority register 31 */ -#define ICIP2 0x9c /* Interrupt Controller IRQ Pending register 2 */ -#define ICMR2 0xa0 /* Interrupt Controller Mask register 2 */ -#define ICLR2 0xa4 /* Interrupt Controller Level register 2 */ -#define ICFP2 0xa8 /* Interrupt Controller FIQ Pending register 2 */ -#define ICPR2 0xac /* Interrupt Controller Pending register 2 */ -#define IPR32 0xb0 /* Interrupt Controller Priority register 32 */ -#define IPR39 0xcc /* Interrupt Controller Priority register 39 */ - -#define PXA2XX_PIC_SRCS 40 - -#define TYPE_PXA2XX_PIC "pxa2xx_pic" -#define PXA2XX_PIC(obj) \ - OBJECT_CHECK(PXA2xxPICState, (obj), TYPE_PXA2XX_PIC) - -typedef struct { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - ARMCPU *cpu; - uint32_t int_enabled[2]; - uint32_t int_pending[2]; - uint32_t is_fiq[2]; - uint32_t int_idle; - uint32_t priority[PXA2XX_PIC_SRCS]; -} PXA2xxPICState; - -static void pxa2xx_pic_update(void *opaque) -{ - uint32_t mask[2]; - PXA2xxPICState *s = (PXA2xxPICState *) opaque; - CPUState *cpu = CPU(s->cpu); - - if (cpu->halted) { - mask[0] = s->int_pending[0] & (s->int_enabled[0] | s->int_idle); - mask[1] = s->int_pending[1] & (s->int_enabled[1] | s->int_idle); - if (mask[0] || mask[1]) { - cpu_interrupt(cpu, CPU_INTERRUPT_EXITTB); - } - } - - mask[0] = s->int_pending[0] & s->int_enabled[0]; - mask[1] = s->int_pending[1] & s->int_enabled[1]; - - if ((mask[0] & s->is_fiq[0]) || (mask[1] & s->is_fiq[1])) { - cpu_interrupt(cpu, CPU_INTERRUPT_FIQ); - } else { - cpu_reset_interrupt(cpu, CPU_INTERRUPT_FIQ); - } - - if ((mask[0] & ~s->is_fiq[0]) || (mask[1] & ~s->is_fiq[1])) { - cpu_interrupt(cpu, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD); - } -} - -/* Note: Here level means state of the signal on a pin, not - * IRQ/FIQ distinction as in PXA Developer Manual. */ -static void pxa2xx_pic_set_irq(void *opaque, int irq, int level) -{ - PXA2xxPICState *s = (PXA2xxPICState *) opaque; - int int_set = (irq >= 32); - irq &= 31; - - if (level) - s->int_pending[int_set] |= 1 << irq; - else - s->int_pending[int_set] &= ~(1 << irq); - - pxa2xx_pic_update(opaque); -} - -static inline uint32_t pxa2xx_pic_highest(PXA2xxPICState *s) { - int i, int_set, irq; - uint32_t bit, mask[2]; - uint32_t ichp = 0x003f003f; /* Both IDs invalid */ - - mask[0] = s->int_pending[0] & s->int_enabled[0]; - mask[1] = s->int_pending[1] & s->int_enabled[1]; - - for (i = PXA2XX_PIC_SRCS - 1; i >= 0; i --) { - irq = s->priority[i] & 0x3f; - if ((s->priority[i] & (1U << 31)) && irq < PXA2XX_PIC_SRCS) { - /* Source peripheral ID is valid. */ - bit = 1 << (irq & 31); - int_set = (irq >= 32); - - if (mask[int_set] & bit & s->is_fiq[int_set]) { - /* FIQ asserted */ - ichp &= 0xffff0000; - ichp |= (1 << 15) | irq; - } - - if (mask[int_set] & bit & ~s->is_fiq[int_set]) { - /* IRQ asserted */ - ichp &= 0x0000ffff; - ichp |= (1U << 31) | (irq << 16); - } - } - } - - return ichp; -} - -static uint64_t pxa2xx_pic_mem_read(void *opaque, hwaddr offset, - unsigned size) -{ - PXA2xxPICState *s = (PXA2xxPICState *) opaque; - - switch (offset) { - case ICIP: /* IRQ Pending register */ - return s->int_pending[0] & ~s->is_fiq[0] & s->int_enabled[0]; - case ICIP2: /* IRQ Pending register 2 */ - return s->int_pending[1] & ~s->is_fiq[1] & s->int_enabled[1]; - case ICMR: /* Mask register */ - return s->int_enabled[0]; - case ICMR2: /* Mask register 2 */ - return s->int_enabled[1]; - case ICLR: /* Level register */ - return s->is_fiq[0]; - case ICLR2: /* Level register 2 */ - return s->is_fiq[1]; - case ICCR: /* Idle mask */ - return (s->int_idle == 0); - case ICFP: /* FIQ Pending register */ - return s->int_pending[0] & s->is_fiq[0] & s->int_enabled[0]; - case ICFP2: /* FIQ Pending register 2 */ - return s->int_pending[1] & s->is_fiq[1] & s->int_enabled[1]; - case ICPR: /* Pending register */ - return s->int_pending[0]; - case ICPR2: /* Pending register 2 */ - return s->int_pending[1]; - case IPR0 ... IPR31: - return s->priority[0 + ((offset - IPR0 ) >> 2)]; - case IPR32 ... IPR39: - return s->priority[32 + ((offset - IPR32) >> 2)]; - case ICHP: /* Highest Priority register */ - return pxa2xx_pic_highest(s); - default: - printf("%s: Bad register offset " REG_FMT "\n", __FUNCTION__, offset); - return 0; - } -} - -static void pxa2xx_pic_mem_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxPICState *s = (PXA2xxPICState *) opaque; - - switch (offset) { - case ICMR: /* Mask register */ - s->int_enabled[0] = value; - break; - case ICMR2: /* Mask register 2 */ - s->int_enabled[1] = value; - break; - case ICLR: /* Level register */ - s->is_fiq[0] = value; - break; - case ICLR2: /* Level register 2 */ - s->is_fiq[1] = value; - break; - case ICCR: /* Idle mask */ - s->int_idle = (value & 1) ? 0 : ~0; - break; - case IPR0 ... IPR31: - s->priority[0 + ((offset - IPR0 ) >> 2)] = value & 0x8000003f; - break; - case IPR32 ... IPR39: - s->priority[32 + ((offset - IPR32) >> 2)] = value & 0x8000003f; - break; - default: - printf("%s: Bad register offset " REG_FMT "\n", __FUNCTION__, offset); - return; - } - pxa2xx_pic_update(opaque); -} - -/* Interrupt Controller Coprocessor Space Register Mapping */ -static const int pxa2xx_cp_reg_map[0x10] = { - [0x0 ... 0xf] = -1, - [0x0] = ICIP, - [0x1] = ICMR, - [0x2] = ICLR, - [0x3] = ICFP, - [0x4] = ICPR, - [0x5] = ICHP, - [0x6] = ICIP2, - [0x7] = ICMR2, - [0x8] = ICLR2, - [0x9] = ICFP2, - [0xa] = ICPR2, -}; - -static uint64_t pxa2xx_pic_cp_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - int offset = pxa2xx_cp_reg_map[ri->crn]; - return pxa2xx_pic_mem_read(ri->opaque, offset, 4); -} - -static void pxa2xx_pic_cp_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - int offset = pxa2xx_cp_reg_map[ri->crn]; - pxa2xx_pic_mem_write(ri->opaque, offset, value, 4); -} - -#define REGINFO_FOR_PIC_CP(NAME, CRN) \ - { .name = NAME, .cp = 6, .crn = CRN, .crm = 0, .opc1 = 0, .opc2 = 0, \ - .access = PL1_RW, .type = ARM_CP_IO, \ - .readfn = pxa2xx_pic_cp_read, .writefn = pxa2xx_pic_cp_write } - -static const ARMCPRegInfo pxa_pic_cp_reginfo[] = { - REGINFO_FOR_PIC_CP("ICIP", 0), - REGINFO_FOR_PIC_CP("ICMR", 1), - REGINFO_FOR_PIC_CP("ICLR", 2), - REGINFO_FOR_PIC_CP("ICFP", 3), - REGINFO_FOR_PIC_CP("ICPR", 4), - REGINFO_FOR_PIC_CP("ICHP", 5), - REGINFO_FOR_PIC_CP("ICIP2", 6), - REGINFO_FOR_PIC_CP("ICMR2", 7), - REGINFO_FOR_PIC_CP("ICLR2", 8), - REGINFO_FOR_PIC_CP("ICFP2", 9), - REGINFO_FOR_PIC_CP("ICPR2", 0xa), - REGINFO_SENTINEL -}; - -static const MemoryRegionOps pxa2xx_pic_ops = { - .read = pxa2xx_pic_mem_read, - .write = pxa2xx_pic_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int pxa2xx_pic_post_load(void *opaque, int version_id) -{ - pxa2xx_pic_update(opaque); - return 0; -} - -DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu) -{ - DeviceState *dev = qdev_create(NULL, TYPE_PXA2XX_PIC); - PXA2xxPICState *s = PXA2XX_PIC(dev); - - s->cpu = cpu; - - s->int_pending[0] = 0; - s->int_pending[1] = 0; - s->int_enabled[0] = 0; - s->int_enabled[1] = 0; - s->is_fiq[0] = 0; - s->is_fiq[1] = 0; - - qdev_init_nofail(dev); - - qdev_init_gpio_in(dev, pxa2xx_pic_set_irq, PXA2XX_PIC_SRCS); - - /* Enable IC memory-mapped registers access. */ - memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_pic_ops, s, - "pxa2xx-pic", 0x00100000); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - - /* Enable IC coprocessor access. */ - define_arm_cp_regs_with_opaque(cpu, pxa_pic_cp_reginfo, s); - - return dev; -} - -static VMStateDescription vmstate_pxa2xx_pic_regs = { - .name = "pxa2xx_pic", - .version_id = 0, - .minimum_version_id = 0, - .post_load = pxa2xx_pic_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(int_enabled, PXA2xxPICState, 2), - VMSTATE_UINT32_ARRAY(int_pending, PXA2xxPICState, 2), - VMSTATE_UINT32_ARRAY(is_fiq, PXA2xxPICState, 2), - VMSTATE_UINT32(int_idle, PXA2xxPICState), - VMSTATE_UINT32_ARRAY(priority, PXA2xxPICState, PXA2XX_PIC_SRCS), - VMSTATE_END_OF_LIST(), - }, -}; - -static int pxa2xx_pic_initfn(SysBusDevice *dev) -{ - return 0; -} - -static void pxa2xx_pic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = pxa2xx_pic_initfn; - dc->desc = "PXA2xx PIC"; - dc->vmsd = &vmstate_pxa2xx_pic_regs; -} - -static const TypeInfo pxa2xx_pic_info = { - .name = TYPE_PXA2XX_PIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxPICState), - .class_init = pxa2xx_pic_class_init, -}; - -static void pxa2xx_pic_register_types(void) -{ - type_register_static(&pxa2xx_pic_info); -} - -type_init(pxa2xx_pic_register_types) diff --git a/qemu/hw/arm/raspi.c b/qemu/hw/arm/raspi.c deleted file mode 100644 index 2b295f14c..000000000 --- a/qemu/hw/arm/raspi.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * 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 deleted file mode 100644 index 3222b360e..000000000 --- a/qemu/hw/arm/realview.c +++ /dev/null @@ -1,463 +0,0 @@ -/* - * ARM RealView Baseboard System emulation. - * - * Copyright (c) 2006-2007 CodeSourcery. - * Written by Paul Brook - * - * 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" -#include "hw/devices.h" -#include "hw/pci/pci.h" -#include "net/net.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" -#include "hw/i2c/i2c.h" -#include "sysemu/block-backend.h" -#include "exec/address-spaces.h" -#include "qemu/error-report.h" - -#define SMP_BOOT_ADDR 0xe0000000 -#define SMP_BOOTREG_ADDR 0x10000030 - -/* Board init. */ - -static struct arm_boot_info realview_binfo = { - .smp_loader_start = SMP_BOOT_ADDR, - .smp_bootreg_addr = SMP_BOOTREG_ADDR, -}; - -/* The following two lists must be consistent. */ -enum realview_board_type { - BOARD_EB, - BOARD_EB_MPCORE, - BOARD_PB_A8, - BOARD_PBX_A9, -}; - -static const int realview_board_id[] = { - 0x33b, - 0x33b, - 0x769, - 0x76d -}; - -static void realview_init(MachineState *machine, - enum realview_board_type board_type) -{ - ARMCPU *cpu = NULL; - CPUARMState *env; - ObjectClass *cpu_oc; - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *ram_lo; - MemoryRegion *ram_hi = g_new(MemoryRegion, 1); - MemoryRegion *ram_alias = g_new(MemoryRegion, 1); - MemoryRegion *ram_hack = g_new(MemoryRegion, 1); - DeviceState *dev, *sysctl, *gpio2, *pl041; - SysBusDevice *busdev; - qemu_irq pic[64]; - qemu_irq mmc_irq[2]; - PCIBus *pci_bus = NULL; - NICInfo *nd; - I2CBus *i2c; - int n; - int done_nic = 0; - qemu_irq cpu_irq[4]; - int is_mpcore = 0; - int is_pb = 0; - uint32_t proc_id = 0; - uint32_t sys_id; - ram_addr_t low_ram_size; - ram_addr_t ram_size = machine->ram_size; - hwaddr periphbase = 0; - - switch (board_type) { - case BOARD_EB: - break; - case BOARD_EB_MPCORE: - is_mpcore = 1; - periphbase = 0x10100000; - break; - case BOARD_PB_A8: - is_pb = 1; - break; - case BOARD_PBX_A9: - is_mpcore = 1; - is_pb = 1; - periphbase = 0x1f000000; - break; - } - - cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, machine->cpu_model); - if (!cpu_oc) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - - for (n = 0; n < smp_cpus; n++) { - Object *cpuobj = object_new(object_class_get_name(cpu_oc)); - - /* 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", &error_fatal); - } - - if (is_pb && is_mpcore) { - object_property_set_int(cpuobj, periphbase, "reset-cbar", - &error_fatal); - } - - object_property_set_bool(cpuobj, true, "realized", &error_fatal); - - cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpuobj), ARM_CPU_IRQ); - } - cpu = ARM_CPU(first_cpu); - env = &cpu->env; - if (arm_feature(env, ARM_FEATURE_V7)) { - if (is_mpcore) { - proc_id = 0x0c000000; - } else { - proc_id = 0x0e000000; - } - } else if (arm_feature(env, ARM_FEATURE_V6K)) { - proc_id = 0x06000000; - } else if (arm_feature(env, ARM_FEATURE_V6)) { - proc_id = 0x04000000; - } else { - proc_id = 0x02000000; - } - - if (is_pb && ram_size > 0x20000000) { - /* Core tile RAM. */ - ram_lo = g_new(MemoryRegion, 1); - low_ram_size = ram_size - 0x20000000; - ram_size = 0x20000000; - memory_region_init_ram(ram_lo, NULL, "realview.lowmem", low_ram_size, - &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_fatal); - vmstate_register_ram_global(ram_hi); - low_ram_size = ram_size; - if (low_ram_size > 0x10000000) - low_ram_size = 0x10000000; - /* SDRAM at address zero. */ - memory_region_init_alias(ram_alias, NULL, "realview.alias", - ram_hi, 0, low_ram_size); - memory_region_add_subregion(sysmem, 0, ram_alias); - if (is_pb) { - /* And again at a high address. */ - memory_region_add_subregion(sysmem, 0x70000000, ram_hi); - } else { - ram_size = low_ram_size; - } - - sys_id = is_pb ? 0x01780500 : 0xc1400400; - sysctl = qdev_create(NULL, "realview_sysctl"); - qdev_prop_set_uint32(sysctl, "sys_id", sys_id); - qdev_prop_set_uint32(sysctl, "proc_id", proc_id); - qdev_init_nofail(sysctl); - sysbus_mmio_map(SYS_BUS_DEVICE(sysctl), 0, 0x10000000); - - if (is_mpcore) { - dev = qdev_create(NULL, is_pb ? "a9mpcore_priv": "realview_mpcore"); - qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); - qdev_init_nofail(dev); - busdev = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(busdev, 0, periphbase); - for (n = 0; n < smp_cpus; n++) { - sysbus_connect_irq(busdev, n, cpu_irq[n]); - } - sysbus_create_varargs("l2x0", periphbase + 0x2000, NULL); - /* Both A9 and 11MPCore put the GIC CPU i/f at base + 0x100 */ - realview_binfo.gic_cpu_if_addr = periphbase + 0x100; - } else { - uint32_t gic_addr = is_pb ? 0x1e000000 : 0x10040000; - /* For now just create the nIRQ GIC, and ignore the others. */ - dev = sysbus_create_simple("realview_gic", gic_addr, cpu_irq[0]); - } - for (n = 0; n < 64; n++) { - pic[n] = qdev_get_gpio_in(dev, n); - } - - pl041 = qdev_create(NULL, "pl041"); - qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512); - qdev_init_nofail(pl041); - sysbus_mmio_map(SYS_BUS_DEVICE(pl041), 0, 0x10004000); - sysbus_connect_irq(SYS_BUS_DEVICE(pl041), 0, pic[19]); - - sysbus_create_simple("pl050_keyboard", 0x10006000, pic[20]); - sysbus_create_simple("pl050_mouse", 0x10007000, pic[21]); - - sysbus_create_simple("pl011", 0x10009000, pic[12]); - sysbus_create_simple("pl011", 0x1000a000, pic[13]); - sysbus_create_simple("pl011", 0x1000b000, pic[14]); - sysbus_create_simple("pl011", 0x1000c000, pic[15]); - - /* DMA controller is optional, apparently. */ - sysbus_create_simple("pl081", 0x10030000, pic[24]); - - sysbus_create_simple("sp804", 0x10011000, pic[4]); - sysbus_create_simple("sp804", 0x10012000, pic[5]); - - sysbus_create_simple("pl061", 0x10013000, pic[6]); - sysbus_create_simple("pl061", 0x10014000, pic[7]); - gpio2 = sysbus_create_simple("pl061", 0x10015000, pic[8]); - - sysbus_create_simple("pl111", 0x10020000, pic[23]); - - dev = sysbus_create_varargs("pl181", 0x10005000, pic[17], pic[18], NULL); - /* Wire up MMC card detect and read-only signals. These have - * to go to both the PL061 GPIO and the sysctl register. - * Note that the PL181 orders these lines (readonly,inserted) - * and the PL061 has them the other way about. Also the card - * detect line is inverted. - */ - mmc_irq[0] = qemu_irq_split( - qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_WPROT), - qdev_get_gpio_in(gpio2, 1)); - mmc_irq[1] = qemu_irq_split( - qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_CARDIN), - qemu_irq_invert(qdev_get_gpio_in(gpio2, 0))); - qdev_connect_gpio_out(dev, 0, mmc_irq[0]); - qdev_connect_gpio_out(dev, 1, mmc_irq[1]); - - sysbus_create_simple("pl031", 0x10017000, pic[10]); - - if (!is_pb) { - dev = qdev_create(NULL, "realview_pci"); - busdev = SYS_BUS_DEVICE(dev); - qdev_init_nofail(dev); - sysbus_mmio_map(busdev, 0, 0x10019000); /* PCI controller registers */ - sysbus_mmio_map(busdev, 1, 0x60000000); /* PCI self-config */ - sysbus_mmio_map(busdev, 2, 0x61000000); /* PCI config */ - sysbus_mmio_map(busdev, 3, 0x62000000); /* PCI I/O */ - sysbus_mmio_map(busdev, 4, 0x63000000); /* PCI memory window 1 */ - sysbus_mmio_map(busdev, 5, 0x64000000); /* PCI memory window 2 */ - sysbus_mmio_map(busdev, 6, 0x68000000); /* PCI memory window 3 */ - sysbus_connect_irq(busdev, 0, pic[48]); - sysbus_connect_irq(busdev, 1, pic[49]); - sysbus_connect_irq(busdev, 2, pic[50]); - sysbus_connect_irq(busdev, 3, pic[51]); - pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci"); - if (usb_enabled()) { - pci_create_simple(pci_bus, -1, "pci-ohci"); - } - n = drive_get_max_bus(IF_SCSI); - while (n >= 0) { - pci_create_simple(pci_bus, -1, "lsi53c895a"); - n--; - } - } - for(n = 0; n < nb_nics; n++) { - nd = &nd_table[n]; - - if (!done_nic && (!nd->model || - strcmp(nd->model, is_pb ? "lan9118" : "smc91c111") == 0)) { - if (is_pb) { - lan9118_init(nd, 0x4e000000, pic[28]); - } else { - smc91c111_init(nd, 0x4e000000, pic[28]); - } - done_nic = 1; - } else { - if (pci_bus) { - pci_nic_init_nofail(nd, pci_bus, "rtl8139", NULL); - } - } - } - - dev = sysbus_create_simple("versatile_i2c", 0x10002000, NULL); - i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); - i2c_create_slave(i2c, "ds1338", 0x68); - - /* Memory map for RealView Emulation Baseboard: */ - /* 0x10000000 System registers. */ - /* 0x10001000 System controller. */ - /* 0x10002000 Two-Wire Serial Bus. */ - /* 0x10003000 Reserved. */ - /* 0x10004000 AACI. */ - /* 0x10005000 MCI. */ - /* 0x10006000 KMI0. */ - /* 0x10007000 KMI1. */ - /* 0x10008000 Character LCD. (EB) */ - /* 0x10009000 UART0. */ - /* 0x1000a000 UART1. */ - /* 0x1000b000 UART2. */ - /* 0x1000c000 UART3. */ - /* 0x1000d000 SSPI. */ - /* 0x1000e000 SCI. */ - /* 0x1000f000 Reserved. */ - /* 0x10010000 Watchdog. */ - /* 0x10011000 Timer 0+1. */ - /* 0x10012000 Timer 2+3. */ - /* 0x10013000 GPIO 0. */ - /* 0x10014000 GPIO 1. */ - /* 0x10015000 GPIO 2. */ - /* 0x10002000 Two-Wire Serial Bus - DVI. (PB) */ - /* 0x10017000 RTC. */ - /* 0x10018000 DMC. */ - /* 0x10019000 PCI controller config. */ - /* 0x10020000 CLCD. */ - /* 0x10030000 DMA Controller. */ - /* 0x10040000 GIC1. (EB) */ - /* 0x10050000 GIC2. (EB) */ - /* 0x10060000 GIC3. (EB) */ - /* 0x10070000 GIC4. (EB) */ - /* 0x10080000 SMC. */ - /* 0x1e000000 GIC1. (PB) */ - /* 0x1e001000 GIC2. (PB) */ - /* 0x1e002000 GIC3. (PB) */ - /* 0x1e003000 GIC4. (PB) */ - /* 0x40000000 NOR flash. */ - /* 0x44000000 DoC flash. */ - /* 0x48000000 SRAM. */ - /* 0x4c000000 Configuration flash. */ - /* 0x4e000000 Ethernet. */ - /* 0x4f000000 USB. */ - /* 0x50000000 PISMO. */ - /* 0x54000000 PISMO. */ - /* 0x58000000 PISMO. */ - /* 0x5c000000 PISMO. */ - /* 0x60000000 PCI. */ - /* 0x60000000 PCI Self Config. */ - /* 0x61000000 PCI Config. */ - /* 0x62000000 PCI IO. */ - /* 0x63000000 PCI mem 0. */ - /* 0x64000000 PCI mem 1. */ - /* 0x68000000 PCI mem 2. */ - - /* ??? Hack to map an additional page of ram for the secondary CPU - startup code. I guess this works on real hardware because the - 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_fatal); - vmstate_register_ram_global(ram_hack); - memory_region_add_subregion(sysmem, SMP_BOOT_ADDR, ram_hack); - - realview_binfo.ram_size = ram_size; - realview_binfo.kernel_filename = machine->kernel_filename; - realview_binfo.kernel_cmdline = machine->kernel_cmdline; - realview_binfo.initrd_filename = machine->initrd_filename; - realview_binfo.nb_cpus = smp_cpus; - realview_binfo.board_id = realview_board_id[board_type]; - realview_binfo.loader_start = (board_type == BOARD_PB_A8 ? 0x70000000 : 0); - arm_load_kernel(ARM_CPU(first_cpu), &realview_binfo); -} - -static void realview_eb_init(MachineState *machine) -{ - if (!machine->cpu_model) { - machine->cpu_model = "arm926"; - } - realview_init(machine, BOARD_EB); -} - -static void realview_eb_mpcore_init(MachineState *machine) -{ - if (!machine->cpu_model) { - machine->cpu_model = "arm11mpcore"; - } - realview_init(machine, BOARD_EB_MPCORE); -} - -static void realview_pb_a8_init(MachineState *machine) -{ - if (!machine->cpu_model) { - machine->cpu_model = "cortex-a8"; - } - realview_init(machine, BOARD_PB_A8); -} - -static void realview_pbx_a9_init(MachineState *machine) -{ - if (!machine->cpu_model) { - machine->cpu_model = "cortex-a9"; - } - realview_init(machine, BOARD_PBX_A9); -} - -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 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 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 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) -{ - 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); -} - -type_init(realview_machine_init) diff --git a/qemu/hw/arm/spitz.c b/qemu/hw/arm/spitz.c deleted file mode 100644 index bf61d63b5..000000000 --- a/qemu/hw/arm/spitz.c +++ /dev/null @@ -1,1178 +0,0 @@ -/* - * PXA270-based Clamshell PDA platforms. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#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/ssi.h" -#include "hw/block/flash.h" -#include "qemu/timer.h" -#include "hw/devices.h" -#include "hw/arm/sharpsl.h" -#include "ui/console.h" -#include "audio/audio.h" -#include "hw/boards.h" -#include "sysemu/block-backend.h" -#include "hw/sysbus.h" -#include "exec/address-spaces.h" - -#undef REG_FMT -#define REG_FMT "0x%02lx" - -/* Spitz Flash */ -#define FLASH_BASE 0x0c000000 -#define FLASH_ECCLPLB 0x00 /* Line parity 7 - 0 bit */ -#define FLASH_ECCLPUB 0x04 /* Line parity 15 - 8 bit */ -#define FLASH_ECCCP 0x08 /* Column parity 5 - 0 bit */ -#define FLASH_ECCCNTR 0x0c /* ECC byte counter */ -#define FLASH_ECCCLRR 0x10 /* Clear ECC */ -#define FLASH_FLASHIO 0x14 /* Flash I/O */ -#define FLASH_FLASHCTL 0x18 /* Flash Control */ - -#define FLASHCTL_CE0 (1 << 0) -#define FLASHCTL_CLE (1 << 1) -#define FLASHCTL_ALE (1 << 2) -#define FLASHCTL_WP (1 << 3) -#define FLASHCTL_CE1 (1 << 4) -#define FLASHCTL_RYBY (1 << 5) -#define FLASHCTL_NCE (FLASHCTL_CE0 | FLASHCTL_CE1) - -#define TYPE_SL_NAND "sl-nand" -#define SL_NAND(obj) OBJECT_CHECK(SLNANDState, (obj), TYPE_SL_NAND) - -typedef struct { - SysBusDevice parent_obj; - - MemoryRegion iomem; - DeviceState *nand; - uint8_t ctl; - uint8_t manf_id; - uint8_t chip_id; - ECCState ecc; -} SLNANDState; - -static uint64_t sl_read(void *opaque, hwaddr addr, unsigned size) -{ - SLNANDState *s = (SLNANDState *) opaque; - int ryby; - - switch (addr) { -#define BSHR(byte, from, to) ((s->ecc.lp[byte] >> (from - to)) & (1 << to)) - case FLASH_ECCLPLB: - return BSHR(0, 4, 0) | BSHR(0, 5, 2) | BSHR(0, 6, 4) | BSHR(0, 7, 6) | - BSHR(1, 4, 1) | BSHR(1, 5, 3) | BSHR(1, 6, 5) | BSHR(1, 7, 7); - -#define BSHL(byte, from, to) ((s->ecc.lp[byte] << (to - from)) & (1 << to)) - case FLASH_ECCLPUB: - return BSHL(0, 0, 0) | BSHL(0, 1, 2) | BSHL(0, 2, 4) | BSHL(0, 3, 6) | - BSHL(1, 0, 1) | BSHL(1, 1, 3) | BSHL(1, 2, 5) | BSHL(1, 3, 7); - - case FLASH_ECCCP: - return s->ecc.cp; - - case FLASH_ECCCNTR: - return s->ecc.count & 0xff; - - case FLASH_FLASHCTL: - nand_getpins(s->nand, &ryby); - if (ryby) - return s->ctl | FLASHCTL_RYBY; - else - return s->ctl; - - case FLASH_FLASHIO: - if (size == 4) { - return ecc_digest(&s->ecc, nand_getio(s->nand)) | - (ecc_digest(&s->ecc, nand_getio(s->nand)) << 16); - } - return ecc_digest(&s->ecc, nand_getio(s->nand)); - - default: - zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr); - } - return 0; -} - -static void sl_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - SLNANDState *s = (SLNANDState *) opaque; - - switch (addr) { - case FLASH_ECCCLRR: - /* Value is ignored. */ - ecc_reset(&s->ecc); - break; - - case FLASH_FLASHCTL: - s->ctl = value & 0xff & ~FLASHCTL_RYBY; - nand_setpins(s->nand, - s->ctl & FLASHCTL_CLE, - s->ctl & FLASHCTL_ALE, - s->ctl & FLASHCTL_NCE, - s->ctl & FLASHCTL_WP, - 0); - break; - - case FLASH_FLASHIO: - nand_setio(s->nand, ecc_digest(&s->ecc, value & 0xff)); - break; - - default: - zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr); - } -} - -enum { - FLASH_128M, - FLASH_1024M, -}; - -static const MemoryRegionOps sl_ops = { - .read = sl_read, - .write = sl_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void sl_flash_register(PXA2xxState *cpu, int size) -{ - DeviceState *dev; - - dev = qdev_create(NULL, TYPE_SL_NAND); - - qdev_prop_set_uint8(dev, "manf_id", NAND_MFR_SAMSUNG); - if (size == FLASH_128M) - qdev_prop_set_uint8(dev, "chip_id", 0x73); - else if (size == FLASH_1024M) - qdev_prop_set_uint8(dev, "chip_id", 0xf1); - - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, FLASH_BASE); -} - -static int sl_nand_init(SysBusDevice *dev) -{ - SLNANDState *s = SL_NAND(dev); - DriveInfo *nand; - - s->ctl = 0; - /* FIXME use a qdev drive property instead of drive_get() */ - nand = drive_get(IF_MTD, 0, 0); - s->nand = nand_init(nand ? blk_by_legacy_dinfo(nand) : NULL, - s->manf_id, s->chip_id); - - memory_region_init_io(&s->iomem, OBJECT(s), &sl_ops, s, "sl", 0x40); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -/* Spitz Keyboard */ - -#define SPITZ_KEY_STROBE_NUM 11 -#define SPITZ_KEY_SENSE_NUM 7 - -static const int spitz_gpio_key_sense[SPITZ_KEY_SENSE_NUM] = { - 12, 17, 91, 34, 36, 38, 39 -}; - -static const int spitz_gpio_key_strobe[SPITZ_KEY_STROBE_NUM] = { - 88, 23, 24, 25, 26, 27, 52, 103, 107, 108, 114 -}; - -/* Eighth additional row maps the special keys */ -static int spitz_keymap[SPITZ_KEY_SENSE_NUM + 1][SPITZ_KEY_STROBE_NUM] = { - { 0x1d, 0x02, 0x04, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0e, 0x3f, 0x40 }, - { -1 , 0x03, 0x05, 0x13, 0x15, 0x09, 0x17, 0x18, 0x19, 0x41, 0x42 }, - { 0x0f, 0x10, 0x12, 0x14, 0x22, 0x16, 0x24, 0x25, -1 , -1 , -1 }, - { 0x3c, 0x11, 0x1f, 0x21, 0x2f, 0x23, 0x32, 0x26, -1 , 0x36, -1 }, - { 0x3b, 0x1e, 0x20, 0x2e, 0x30, 0x31, 0x34, -1 , 0x1c, 0x2a, -1 }, - { 0x44, 0x2c, 0x2d, 0x0c, 0x39, 0x33, -1 , 0x48, -1 , -1 , 0x38 }, - { 0x37, 0x3d, -1 , 0x45, 0x57, 0x58, 0x4b, 0x50, 0x4d, -1 , -1 }, - { 0x52, 0x43, 0x01, 0x47, 0x49, -1 , -1 , -1 , -1 , -1 , -1 }, -}; - -#define SPITZ_GPIO_AK_INT 13 /* Remote control */ -#define SPITZ_GPIO_SYNC 16 /* Sync button */ -#define SPITZ_GPIO_ON_KEY 95 /* Power button */ -#define SPITZ_GPIO_SWA 97 /* Lid */ -#define SPITZ_GPIO_SWB 96 /* Tablet mode */ - -/* The special buttons are mapped to unused keys */ -static const int spitz_gpiomap[5] = { - SPITZ_GPIO_AK_INT, SPITZ_GPIO_SYNC, SPITZ_GPIO_ON_KEY, - SPITZ_GPIO_SWA, SPITZ_GPIO_SWB, -}; - -#define TYPE_SPITZ_KEYBOARD "spitz-keyboard" -#define SPITZ_KEYBOARD(obj) \ - OBJECT_CHECK(SpitzKeyboardState, (obj), TYPE_SPITZ_KEYBOARD) - -typedef struct { - SysBusDevice parent_obj; - - qemu_irq sense[SPITZ_KEY_SENSE_NUM]; - qemu_irq gpiomap[5]; - int keymap[0x80]; - uint16_t keyrow[SPITZ_KEY_SENSE_NUM]; - uint16_t strobe_state; - uint16_t sense_state; - - uint16_t pre_map[0x100]; - uint16_t modifiers; - uint16_t imodifiers; - uint8_t fifo[16]; - int fifopos, fifolen; - QEMUTimer *kbdtimer; -} SpitzKeyboardState; - -static void spitz_keyboard_sense_update(SpitzKeyboardState *s) -{ - int i; - uint16_t strobe, sense = 0; - for (i = 0; i < SPITZ_KEY_SENSE_NUM; i ++) { - strobe = s->keyrow[i] & s->strobe_state; - if (strobe) { - sense |= 1 << i; - if (!(s->sense_state & (1 << i))) - qemu_irq_raise(s->sense[i]); - } else if (s->sense_state & (1 << i)) - qemu_irq_lower(s->sense[i]); - } - - s->sense_state = sense; -} - -static void spitz_keyboard_strobe(void *opaque, int line, int level) -{ - SpitzKeyboardState *s = (SpitzKeyboardState *) opaque; - - if (level) - s->strobe_state |= 1 << line; - else - s->strobe_state &= ~(1 << line); - spitz_keyboard_sense_update(s); -} - -static void spitz_keyboard_keydown(SpitzKeyboardState *s, int keycode) -{ - int spitz_keycode = s->keymap[keycode & 0x7f]; - if (spitz_keycode == -1) - return; - - /* Handle the additional keys */ - if ((spitz_keycode >> 4) == SPITZ_KEY_SENSE_NUM) { - qemu_set_irq(s->gpiomap[spitz_keycode & 0xf], (keycode < 0x80)); - return; - } - - if (keycode & 0x80) - s->keyrow[spitz_keycode >> 4] &= ~(1 << (spitz_keycode & 0xf)); - else - s->keyrow[spitz_keycode >> 4] |= 1 << (spitz_keycode & 0xf); - - spitz_keyboard_sense_update(s); -} - -#define SPITZ_MOD_SHIFT (1 << 7) -#define SPITZ_MOD_CTRL (1 << 8) -#define SPITZ_MOD_FN (1 << 9) - -#define QUEUE_KEY(c) s->fifo[(s->fifopos + s->fifolen ++) & 0xf] = c - -static void spitz_keyboard_handler(void *opaque, int keycode) -{ - SpitzKeyboardState *s = opaque; - uint16_t code; - int mapcode; - switch (keycode) { - case 0x2a: /* Left Shift */ - s->modifiers |= 1; - break; - case 0xaa: - s->modifiers &= ~1; - break; - case 0x36: /* Right Shift */ - s->modifiers |= 2; - break; - case 0xb6: - s->modifiers &= ~2; - break; - case 0x1d: /* Control */ - s->modifiers |= 4; - break; - case 0x9d: - s->modifiers &= ~4; - break; - case 0x38: /* Alt */ - s->modifiers |= 8; - break; - case 0xb8: - s->modifiers &= ~8; - break; - } - - code = s->pre_map[mapcode = ((s->modifiers & 3) ? - (keycode | SPITZ_MOD_SHIFT) : - (keycode & ~SPITZ_MOD_SHIFT))]; - - if (code != mapcode) { -#if 0 - if ((code & SPITZ_MOD_SHIFT) && !(s->modifiers & 1)) { - QUEUE_KEY(0x2a | (keycode & 0x80)); - } - if ((code & SPITZ_MOD_CTRL) && !(s->modifiers & 4)) { - QUEUE_KEY(0x1d | (keycode & 0x80)); - } - if ((code & SPITZ_MOD_FN) && !(s->modifiers & 8)) { - QUEUE_KEY(0x38 | (keycode & 0x80)); - } - if ((code & SPITZ_MOD_FN) && (s->modifiers & 1)) { - QUEUE_KEY(0x2a | (~keycode & 0x80)); - } - if ((code & SPITZ_MOD_FN) && (s->modifiers & 2)) { - QUEUE_KEY(0x36 | (~keycode & 0x80)); - } -#else - if (keycode & 0x80) { - if ((s->imodifiers & 1 ) && !(s->modifiers & 1)) - QUEUE_KEY(0x2a | 0x80); - if ((s->imodifiers & 4 ) && !(s->modifiers & 4)) - QUEUE_KEY(0x1d | 0x80); - if ((s->imodifiers & 8 ) && !(s->modifiers & 8)) - QUEUE_KEY(0x38 | 0x80); - if ((s->imodifiers & 0x10) && (s->modifiers & 1)) - QUEUE_KEY(0x2a); - if ((s->imodifiers & 0x20) && (s->modifiers & 2)) - QUEUE_KEY(0x36); - s->imodifiers = 0; - } else { - if ((code & SPITZ_MOD_SHIFT) && - !((s->modifiers | s->imodifiers) & 1)) { - QUEUE_KEY(0x2a); - s->imodifiers |= 1; - } - if ((code & SPITZ_MOD_CTRL) && - !((s->modifiers | s->imodifiers) & 4)) { - QUEUE_KEY(0x1d); - s->imodifiers |= 4; - } - if ((code & SPITZ_MOD_FN) && - !((s->modifiers | s->imodifiers) & 8)) { - QUEUE_KEY(0x38); - s->imodifiers |= 8; - } - if ((code & SPITZ_MOD_FN) && (s->modifiers & 1) && - !(s->imodifiers & 0x10)) { - QUEUE_KEY(0x2a | 0x80); - s->imodifiers |= 0x10; - } - if ((code & SPITZ_MOD_FN) && (s->modifiers & 2) && - !(s->imodifiers & 0x20)) { - QUEUE_KEY(0x36 | 0x80); - s->imodifiers |= 0x20; - } - } -#endif - } - - QUEUE_KEY((code & 0x7f) | (keycode & 0x80)); -} - -static void spitz_keyboard_tick(void *opaque) -{ - SpitzKeyboardState *s = (SpitzKeyboardState *) opaque; - - if (s->fifolen) { - spitz_keyboard_keydown(s, s->fifo[s->fifopos ++]); - s->fifolen --; - if (s->fifopos >= 16) - s->fifopos = 0; - } - - timer_mod(s->kbdtimer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - NANOSECONDS_PER_SECOND / 32); -} - -static void spitz_keyboard_pre_map(SpitzKeyboardState *s) -{ - int i; - for (i = 0; i < 0x100; i ++) - s->pre_map[i] = i; - s->pre_map[0x02 | SPITZ_MOD_SHIFT] = 0x02 | SPITZ_MOD_SHIFT; /* exclam */ - s->pre_map[0x28 | SPITZ_MOD_SHIFT] = 0x03 | SPITZ_MOD_SHIFT; /* quotedbl */ - s->pre_map[0x04 | SPITZ_MOD_SHIFT] = 0x04 | SPITZ_MOD_SHIFT; /* # */ - s->pre_map[0x05 | SPITZ_MOD_SHIFT] = 0x05 | SPITZ_MOD_SHIFT; /* dollar */ - s->pre_map[0x06 | SPITZ_MOD_SHIFT] = 0x06 | SPITZ_MOD_SHIFT; /* percent */ - s->pre_map[0x08 | SPITZ_MOD_SHIFT] = 0x07 | SPITZ_MOD_SHIFT; /* ampersand */ - s->pre_map[0x28] = 0x08 | SPITZ_MOD_SHIFT; /* ' */ - s->pre_map[0x0a | SPITZ_MOD_SHIFT] = 0x09 | SPITZ_MOD_SHIFT; /* ( */ - s->pre_map[0x0b | SPITZ_MOD_SHIFT] = 0x0a | SPITZ_MOD_SHIFT; /* ) */ - s->pre_map[0x29 | SPITZ_MOD_SHIFT] = 0x0b | SPITZ_MOD_SHIFT; /* tilde */ - s->pre_map[0x03 | SPITZ_MOD_SHIFT] = 0x0c | SPITZ_MOD_SHIFT; /* at */ - s->pre_map[0xd3] = 0x0e | SPITZ_MOD_FN; /* Delete */ - s->pre_map[0x3a] = 0x0f | SPITZ_MOD_FN; /* Caps_Lock */ - s->pre_map[0x07 | SPITZ_MOD_SHIFT] = 0x11 | SPITZ_MOD_FN; /* ^ */ - s->pre_map[0x0d] = 0x12 | SPITZ_MOD_FN; /* equal */ - s->pre_map[0x0d | SPITZ_MOD_SHIFT] = 0x13 | SPITZ_MOD_FN; /* plus */ - s->pre_map[0x1a] = 0x14 | SPITZ_MOD_FN; /* [ */ - s->pre_map[0x1b] = 0x15 | SPITZ_MOD_FN; /* ] */ - s->pre_map[0x1a | SPITZ_MOD_SHIFT] = 0x16 | SPITZ_MOD_FN; /* { */ - s->pre_map[0x1b | SPITZ_MOD_SHIFT] = 0x17 | SPITZ_MOD_FN; /* } */ - s->pre_map[0x27] = 0x22 | SPITZ_MOD_FN; /* semicolon */ - s->pre_map[0x27 | SPITZ_MOD_SHIFT] = 0x23 | SPITZ_MOD_FN; /* colon */ - s->pre_map[0x09 | SPITZ_MOD_SHIFT] = 0x24 | SPITZ_MOD_FN; /* asterisk */ - s->pre_map[0x2b] = 0x25 | SPITZ_MOD_FN; /* backslash */ - s->pre_map[0x2b | SPITZ_MOD_SHIFT] = 0x26 | SPITZ_MOD_FN; /* bar */ - s->pre_map[0x0c | SPITZ_MOD_SHIFT] = 0x30 | SPITZ_MOD_FN; /* _ */ - s->pre_map[0x33 | SPITZ_MOD_SHIFT] = 0x33 | SPITZ_MOD_FN; /* less */ - s->pre_map[0x35] = 0x33 | SPITZ_MOD_SHIFT; /* slash */ - s->pre_map[0x34 | SPITZ_MOD_SHIFT] = 0x34 | SPITZ_MOD_FN; /* greater */ - s->pre_map[0x35 | SPITZ_MOD_SHIFT] = 0x34 | SPITZ_MOD_SHIFT; /* question */ - s->pre_map[0x49] = 0x48 | SPITZ_MOD_FN; /* Page_Up */ - s->pre_map[0x51] = 0x50 | SPITZ_MOD_FN; /* Page_Down */ - - s->modifiers = 0; - s->imodifiers = 0; - s->fifopos = 0; - s->fifolen = 0; -} - -#undef SPITZ_MOD_SHIFT -#undef SPITZ_MOD_CTRL -#undef SPITZ_MOD_FN - -static int spitz_keyboard_post_load(void *opaque, int version_id) -{ - SpitzKeyboardState *s = (SpitzKeyboardState *) opaque; - - /* Release all pressed keys */ - memset(s->keyrow, 0, sizeof(s->keyrow)); - spitz_keyboard_sense_update(s); - s->modifiers = 0; - s->imodifiers = 0; - s->fifopos = 0; - s->fifolen = 0; - - return 0; -} - -static void spitz_keyboard_register(PXA2xxState *cpu) -{ - int i; - DeviceState *dev; - SpitzKeyboardState *s; - - dev = sysbus_create_simple(TYPE_SPITZ_KEYBOARD, -1, NULL); - s = SPITZ_KEYBOARD(dev); - - for (i = 0; i < SPITZ_KEY_SENSE_NUM; i ++) - qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(cpu->gpio, spitz_gpio_key_sense[i])); - - for (i = 0; i < 5; i ++) - s->gpiomap[i] = qdev_get_gpio_in(cpu->gpio, spitz_gpiomap[i]); - - if (!graphic_rotate) - s->gpiomap[4] = qemu_irq_invert(s->gpiomap[4]); - - for (i = 0; i < 5; i++) - qemu_set_irq(s->gpiomap[i], 0); - - for (i = 0; i < SPITZ_KEY_STROBE_NUM; i ++) - qdev_connect_gpio_out(cpu->gpio, spitz_gpio_key_strobe[i], - qdev_get_gpio_in(dev, i)); - - timer_mod(s->kbdtimer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - - qemu_add_kbd_event_handler(spitz_keyboard_handler, s); -} - -static int spitz_keyboard_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - SpitzKeyboardState *s = SPITZ_KEYBOARD(dev); - int i, j; - - for (i = 0; i < 0x80; i ++) - s->keymap[i] = -1; - for (i = 0; i < SPITZ_KEY_SENSE_NUM + 1; i ++) - for (j = 0; j < SPITZ_KEY_STROBE_NUM; j ++) - if (spitz_keymap[i][j] != -1) - s->keymap[spitz_keymap[i][j]] = (i << 4) | j; - - spitz_keyboard_pre_map(s); - - s->kbdtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, spitz_keyboard_tick, s); - qdev_init_gpio_in(dev, spitz_keyboard_strobe, SPITZ_KEY_STROBE_NUM); - qdev_init_gpio_out(dev, s->sense, SPITZ_KEY_SENSE_NUM); - - return 0; -} - -/* LCD backlight controller */ - -#define LCDTG_RESCTL 0x00 -#define LCDTG_PHACTRL 0x01 -#define LCDTG_DUTYCTRL 0x02 -#define LCDTG_POWERREG0 0x03 -#define LCDTG_POWERREG1 0x04 -#define LCDTG_GPOR3 0x05 -#define LCDTG_PICTRL 0x06 -#define LCDTG_POLCTRL 0x07 - -typedef struct { - SSISlave ssidev; - uint32_t bl_intensity; - uint32_t bl_power; -} SpitzLCDTG; - -static void spitz_bl_update(SpitzLCDTG *s) -{ - if (s->bl_power && s->bl_intensity) - zaurus_printf("LCD Backlight now at %i/63\n", s->bl_intensity); - else - zaurus_printf("LCD Backlight now off\n"); -} - -/* FIXME: Implement GPIO properly and remove this hack. */ -static SpitzLCDTG *spitz_lcdtg; - -static inline void spitz_bl_bit5(void *opaque, int line, int level) -{ - SpitzLCDTG *s = spitz_lcdtg; - int prev = s->bl_intensity; - - if (level) - s->bl_intensity &= ~0x20; - else - s->bl_intensity |= 0x20; - - if (s->bl_power && prev != s->bl_intensity) - spitz_bl_update(s); -} - -static inline void spitz_bl_power(void *opaque, int line, int level) -{ - SpitzLCDTG *s = spitz_lcdtg; - s->bl_power = !!level; - spitz_bl_update(s); -} - -static uint32_t spitz_lcdtg_transfer(SSISlave *dev, uint32_t value) -{ - SpitzLCDTG *s = FROM_SSI_SLAVE(SpitzLCDTG, dev); - int addr; - addr = value >> 5; - value &= 0x1f; - - switch (addr) { - case LCDTG_RESCTL: - if (value) - zaurus_printf("LCD in QVGA mode\n"); - else - zaurus_printf("LCD in VGA mode\n"); - break; - - case LCDTG_DUTYCTRL: - s->bl_intensity &= ~0x1f; - s->bl_intensity |= value; - if (s->bl_power) - spitz_bl_update(s); - break; - - case LCDTG_POWERREG0: - /* Set common voltage to M62332FP */ - break; - } - return 0; -} - -static int spitz_lcdtg_init(SSISlave *dev) -{ - SpitzLCDTG *s = FROM_SSI_SLAVE(SpitzLCDTG, dev); - - spitz_lcdtg = s; - s->bl_power = 0; - s->bl_intensity = 0x20; - - return 0; -} - -/* SSP devices */ - -#define CORGI_SSP_PORT 2 - -#define SPITZ_GPIO_LCDCON_CS 53 -#define SPITZ_GPIO_ADS7846_CS 14 -#define SPITZ_GPIO_MAX1111_CS 20 -#define SPITZ_GPIO_TP_INT 11 - -static DeviceState *max1111; - -/* "Demux" the signal based on current chipselect */ -typedef struct { - SSISlave ssidev; - SSIBus *bus[3]; - uint32_t enable[3]; -} CorgiSSPState; - -static uint32_t corgi_ssp_transfer(SSISlave *dev, uint32_t value) -{ - CorgiSSPState *s = FROM_SSI_SLAVE(CorgiSSPState, dev); - int i; - - for (i = 0; i < 3; i++) { - if (s->enable[i]) { - return ssi_transfer(s->bus[i], value); - } - } - return 0; -} - -static void corgi_ssp_gpio_cs(void *opaque, int line, int level) -{ - CorgiSSPState *s = (CorgiSSPState *)opaque; - assert(line >= 0 && line < 3); - s->enable[line] = !level; -} - -#define MAX1111_BATT_VOLT 1 -#define MAX1111_BATT_TEMP 2 -#define MAX1111_ACIN_VOLT 3 - -#define SPITZ_BATTERY_TEMP 0xe0 /* About 2.9V */ -#define SPITZ_BATTERY_VOLT 0xd0 /* About 4.0V */ -#define SPITZ_CHARGEON_ACIN 0x80 /* About 5.0V */ - -static void spitz_adc_temp_on(void *opaque, int line, int level) -{ - if (!max1111) - return; - - if (level) - max111x_set_input(max1111, MAX1111_BATT_TEMP, SPITZ_BATTERY_TEMP); - else - max111x_set_input(max1111, MAX1111_BATT_TEMP, 0); -} - -static int corgi_ssp_init(SSISlave *d) -{ - DeviceState *dev = DEVICE(d); - CorgiSSPState *s = FROM_SSI_SLAVE(CorgiSSPState, d); - - qdev_init_gpio_in(dev, corgi_ssp_gpio_cs, 3); - s->bus[0] = ssi_create_bus(dev, "ssi0"); - s->bus[1] = ssi_create_bus(dev, "ssi1"); - s->bus[2] = ssi_create_bus(dev, "ssi2"); - - return 0; -} - -static void spitz_ssp_attach(PXA2xxState *cpu) -{ - DeviceState *mux; - DeviceState *dev; - void *bus; - - mux = ssi_create_slave(cpu->ssp[CORGI_SSP_PORT - 1], "corgi-ssp"); - - bus = qdev_get_child_bus(mux, "ssi0"); - ssi_create_slave(bus, "spitz-lcdtg"); - - bus = qdev_get_child_bus(mux, "ssi1"); - dev = ssi_create_slave(bus, "ads7846"); - qdev_connect_gpio_out(dev, 0, - qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_TP_INT)); - - bus = qdev_get_child_bus(mux, "ssi2"); - max1111 = ssi_create_slave(bus, "max1111"); - max111x_set_input(max1111, MAX1111_BATT_VOLT, SPITZ_BATTERY_VOLT); - max111x_set_input(max1111, MAX1111_BATT_TEMP, 0); - max111x_set_input(max1111, MAX1111_ACIN_VOLT, SPITZ_CHARGEON_ACIN); - - qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_LCDCON_CS, - qdev_get_gpio_in(mux, 0)); - qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_ADS7846_CS, - qdev_get_gpio_in(mux, 1)); - qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_MAX1111_CS, - qdev_get_gpio_in(mux, 2)); -} - -/* CF Microdrive */ - -static void spitz_microdrive_attach(PXA2xxState *cpu, int slot) -{ - PCMCIACardState *md; - DriveInfo *dinfo; - - dinfo = drive_get(IF_IDE, 0, 0); - if (!dinfo || dinfo->media_cd) - return; - md = dscm1xxxx_init(dinfo); - pxa2xx_pcmcia_attach(cpu->pcmcia[slot], md); -} - -/* Wm8750 and Max7310 on I2C */ - -#define AKITA_MAX_ADDR 0x18 -#define SPITZ_WM_ADDRL 0x1b -#define SPITZ_WM_ADDRH 0x1a - -#define SPITZ_GPIO_WM 5 - -static void spitz_wm8750_addr(void *opaque, int line, int level) -{ - I2CSlave *wm = (I2CSlave *) opaque; - if (level) - i2c_set_slave_address(wm, SPITZ_WM_ADDRH); - else - i2c_set_slave_address(wm, SPITZ_WM_ADDRL); -} - -static void spitz_i2c_setup(PXA2xxState *cpu) -{ - /* Attach the CPU on one end of our I2C bus. */ - I2CBus *bus = pxa2xx_i2c_bus(cpu->i2c[0]); - - DeviceState *wm; - - /* Attach a WM8750 to the bus */ - wm = i2c_create_slave(bus, "wm8750", 0); - - spitz_wm8750_addr(wm, 0, 0); - qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_WM, - qemu_allocate_irq(spitz_wm8750_addr, wm, 0)); - /* .. and to the sound interface. */ - cpu->i2s->opaque = wm; - cpu->i2s->codec_out = wm8750_dac_dat; - cpu->i2s->codec_in = wm8750_adc_dat; - wm8750_data_req_set(wm, cpu->i2s->data_req, cpu->i2s); -} - -static void spitz_akita_i2c_setup(PXA2xxState *cpu) -{ - /* Attach a Max7310 to Akita I2C bus. */ - i2c_create_slave(pxa2xx_i2c_bus(cpu->i2c[0]), "max7310", - AKITA_MAX_ADDR); -} - -/* Other peripherals */ - -static void spitz_out_switch(void *opaque, int line, int level) -{ - switch (line) { - case 0: - zaurus_printf("Charging %s.\n", level ? "off" : "on"); - break; - case 1: - zaurus_printf("Discharging %s.\n", level ? "on" : "off"); - break; - case 2: - zaurus_printf("Green LED %s.\n", level ? "on" : "off"); - break; - case 3: - zaurus_printf("Orange LED %s.\n", level ? "on" : "off"); - break; - case 4: - spitz_bl_bit5(opaque, line, level); - break; - case 5: - spitz_bl_power(opaque, line, level); - break; - case 6: - spitz_adc_temp_on(opaque, line, level); - break; - } -} - -#define SPITZ_SCP_LED_GREEN 1 -#define SPITZ_SCP_JK_B 2 -#define SPITZ_SCP_CHRG_ON 3 -#define SPITZ_SCP_MUTE_L 4 -#define SPITZ_SCP_MUTE_R 5 -#define SPITZ_SCP_CF_POWER 6 -#define SPITZ_SCP_LED_ORANGE 7 -#define SPITZ_SCP_JK_A 8 -#define SPITZ_SCP_ADC_TEMP_ON 9 -#define SPITZ_SCP2_IR_ON 1 -#define SPITZ_SCP2_AKIN_PULLUP 2 -#define SPITZ_SCP2_BACKLIGHT_CONT 7 -#define SPITZ_SCP2_BACKLIGHT_ON 8 -#define SPITZ_SCP2_MIC_BIAS 9 - -static void spitz_scoop_gpio_setup(PXA2xxState *cpu, - DeviceState *scp0, DeviceState *scp1) -{ - qemu_irq *outsignals = qemu_allocate_irqs(spitz_out_switch, cpu, 8); - - qdev_connect_gpio_out(scp0, SPITZ_SCP_CHRG_ON, outsignals[0]); - qdev_connect_gpio_out(scp0, SPITZ_SCP_JK_B, outsignals[1]); - qdev_connect_gpio_out(scp0, SPITZ_SCP_LED_GREEN, outsignals[2]); - qdev_connect_gpio_out(scp0, SPITZ_SCP_LED_ORANGE, outsignals[3]); - - if (scp1) { - qdev_connect_gpio_out(scp1, SPITZ_SCP2_BACKLIGHT_CONT, outsignals[4]); - qdev_connect_gpio_out(scp1, SPITZ_SCP2_BACKLIGHT_ON, outsignals[5]); - } - - qdev_connect_gpio_out(scp0, SPITZ_SCP_ADC_TEMP_ON, outsignals[6]); -} - -#define SPITZ_GPIO_HSYNC 22 -#define SPITZ_GPIO_SD_DETECT 9 -#define SPITZ_GPIO_SD_WP 81 -#define SPITZ_GPIO_ON_RESET 89 -#define SPITZ_GPIO_BAT_COVER 90 -#define SPITZ_GPIO_CF1_IRQ 105 -#define SPITZ_GPIO_CF1_CD 94 -#define SPITZ_GPIO_CF2_IRQ 106 -#define SPITZ_GPIO_CF2_CD 93 - -static int spitz_hsync; - -static void spitz_lcd_hsync_handler(void *opaque, int line, int level) -{ - PXA2xxState *cpu = (PXA2xxState *) opaque; - qemu_set_irq(qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_HSYNC), spitz_hsync); - spitz_hsync ^= 1; -} - -static void spitz_gpio_setup(PXA2xxState *cpu, int slots) -{ - qemu_irq lcd_hsync; - /* - * Bad hack: We toggle the LCD hsync GPIO on every GPIO status - * read to satisfy broken guests that poll-wait for hsync. - * Simulating a real hsync event would be less practical and - * wouldn't guarantee that a guest ever exits the loop. - */ - spitz_hsync = 0; - lcd_hsync = qemu_allocate_irq(spitz_lcd_hsync_handler, cpu, 0); - pxa2xx_gpio_read_notifier(cpu->gpio, lcd_hsync); - pxa2xx_lcd_vsync_notifier(cpu->lcd, lcd_hsync); - - /* MMC/SD host */ - pxa2xx_mmci_handlers(cpu->mmc, - qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_SD_WP), - qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_SD_DETECT)); - - /* Battery lock always closed */ - qemu_irq_raise(qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_BAT_COVER)); - - /* Handle reset */ - qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_ON_RESET, cpu->reset); - - /* PCMCIA signals: card's IRQ and Card-Detect */ - if (slots >= 1) - pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[0], - qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF1_IRQ), - qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF1_CD)); - if (slots >= 2) - pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[1], - qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF2_IRQ), - qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF2_CD)); -} - -/* Board init. */ -enum spitz_model_e { spitz, akita, borzoi, terrier }; - -#define SPITZ_RAM 0x04000000 -#define SPITZ_ROM 0x00800000 - -static struct arm_boot_info spitz_binfo = { - .loader_start = PXA2XX_SDRAM_BASE, - .ram_size = 0x04000000, -}; - -static void spitz_common_init(MachineState *machine, - enum spitz_model_e model, int arm_id) -{ - PXA2xxState *mpu; - DeviceState *scp0, *scp1 = NULL; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *rom = g_new(MemoryRegion, 1); - const char *cpu_model = machine->cpu_model; - - if (!cpu_model) - cpu_model = (model == terrier) ? "pxa270-c5" : "pxa270-c0"; - - /* Setup CPU & memory */ - mpu = pxa270_init(address_space_mem, spitz_binfo.ram_size, cpu_model); - - sl_flash_register(mpu, (model == spitz) ? FLASH_128M : FLASH_1024M); - - 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); - - /* Setup peripherals */ - spitz_keyboard_register(mpu); - - spitz_ssp_attach(mpu); - - scp0 = sysbus_create_simple("scoop", 0x10800000, NULL); - if (model != akita) { - scp1 = sysbus_create_simple("scoop", 0x08800040, NULL); - } - - spitz_scoop_gpio_setup(mpu, scp0, scp1); - - spitz_gpio_setup(mpu, (model == akita) ? 1 : 2); - - spitz_i2c_setup(mpu); - - if (model == akita) - spitz_akita_i2c_setup(mpu); - - if (model == terrier) - /* A 6.0 GB microdrive is permanently sitting in CF slot 1. */ - spitz_microdrive_attach(mpu, 1); - else if (model != akita) - /* A 4.0 GB microdrive is permanently sitting in CF slot 0. */ - spitz_microdrive_attach(mpu, 0); - - spitz_binfo.kernel_filename = machine->kernel_filename; - spitz_binfo.kernel_cmdline = machine->kernel_cmdline; - spitz_binfo.initrd_filename = machine->initrd_filename; - spitz_binfo.board_id = arm_id; - arm_load_kernel(mpu->cpu, &spitz_binfo); - sl_bootparam_write(SL_PXA_PARAM_BASE); -} - -static void spitz_init(MachineState *machine) -{ - spitz_common_init(machine, spitz, 0x2c9); -} - -static void borzoi_init(MachineState *machine) -{ - spitz_common_init(machine, borzoi, 0x33f); -} - -static void akita_init(MachineState *machine) -{ - spitz_common_init(machine, akita, 0x2e8); -} - -static void terrier_init(MachineState *machine) -{ - spitz_common_init(machine, terrier, 0x33f); -} - -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 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 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 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) -{ - type_register_static(&akitapda_type); - type_register_static(&spitzpda_type); - type_register_static(&borzoipda_type); - type_register_static(&terrierpda_type); -} - -type_init(spitz_machine_init) - -static bool is_version_0(void *opaque, int version_id) -{ - return version_id == 0; -} - -static VMStateDescription vmstate_sl_nand_info = { - .name = "sl-nand", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT8(ctl, SLNANDState), - VMSTATE_STRUCT(ecc, SLNANDState, 0, vmstate_ecc_state, ECCState), - VMSTATE_END_OF_LIST(), - }, -}; - -static Property sl_nand_properties[] = { - DEFINE_PROP_UINT8("manf_id", SLNANDState, manf_id, NAND_MFR_SAMSUNG), - DEFINE_PROP_UINT8("chip_id", SLNANDState, chip_id, 0xf1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void sl_nand_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = sl_nand_init; - dc->vmsd = &vmstate_sl_nand_info; - dc->props = sl_nand_properties; - /* Reason: init() method uses drive_get() */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo sl_nand_info = { - .name = TYPE_SL_NAND, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SLNANDState), - .class_init = sl_nand_class_init, -}; - -static VMStateDescription vmstate_spitz_kbd = { - .name = "spitz-keyboard", - .version_id = 1, - .minimum_version_id = 0, - .post_load = spitz_keyboard_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT16(sense_state, SpitzKeyboardState), - VMSTATE_UINT16(strobe_state, SpitzKeyboardState), - VMSTATE_UNUSED_TEST(is_version_0, 5), - VMSTATE_END_OF_LIST(), - }, -}; - -static void spitz_keyboard_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = spitz_keyboard_init; - dc->vmsd = &vmstate_spitz_kbd; -} - -static const TypeInfo spitz_keyboard_info = { - .name = TYPE_SPITZ_KEYBOARD, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SpitzKeyboardState), - .class_init = spitz_keyboard_class_init, -}; - -static const VMStateDescription vmstate_corgi_ssp_regs = { - .name = "corgi-ssp", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_SSI_SLAVE(ssidev, CorgiSSPState), - VMSTATE_UINT32_ARRAY(enable, CorgiSSPState, 3), - VMSTATE_END_OF_LIST(), - } -}; - -static void corgi_ssp_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - - k->init = corgi_ssp_init; - k->transfer = corgi_ssp_transfer; - dc->vmsd = &vmstate_corgi_ssp_regs; -} - -static const TypeInfo corgi_ssp_info = { - .name = "corgi-ssp", - .parent = TYPE_SSI_SLAVE, - .instance_size = sizeof(CorgiSSPState), - .class_init = corgi_ssp_class_init, -}; - -static const VMStateDescription vmstate_spitz_lcdtg_regs = { - .name = "spitz-lcdtg", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_SSI_SLAVE(ssidev, SpitzLCDTG), - VMSTATE_UINT32(bl_intensity, SpitzLCDTG), - VMSTATE_UINT32(bl_power, SpitzLCDTG), - VMSTATE_END_OF_LIST(), - } -}; - -static void spitz_lcdtg_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - - k->init = spitz_lcdtg_init; - k->transfer = spitz_lcdtg_transfer; - dc->vmsd = &vmstate_spitz_lcdtg_regs; -} - -static const TypeInfo spitz_lcdtg_info = { - .name = "spitz-lcdtg", - .parent = TYPE_SSI_SLAVE, - .instance_size = sizeof(SpitzLCDTG), - .class_init = spitz_lcdtg_class_init, -}; - -static void spitz_register_types(void) -{ - type_register_static(&corgi_ssp_info); - type_register_static(&spitz_lcdtg_info); - type_register_static(&spitz_keyboard_info); - type_register_static(&sl_nand_info); -} - -type_init(spitz_register_types) diff --git a/qemu/hw/arm/stellaris.c b/qemu/hw/arm/stellaris.c deleted file mode 100644 index c1766f856..000000000 --- a/qemu/hw/arm/stellaris.c +++ /dev/null @@ -1,1475 +0,0 @@ -/* - * Luminary Micro Stellaris peripherals - * - * Copyright (c) 2006 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/sysbus.h" -#include "hw/ssi/ssi.h" -#include "hw/arm/arm.h" -#include "hw/devices.h" -#include "qemu/timer.h" -#include "hw/i2c/i2c.h" -#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 -#define GPIO_C 2 -#define GPIO_D 3 -#define GPIO_E 4 -#define GPIO_F 5 -#define GPIO_G 6 - -#define BP_OLED_I2C 0x01 -#define BP_OLED_SSI 0x02 -#define BP_GAMEPAD 0x04 - -#define NUM_IRQ_LINES 64 - -typedef const struct { - const char *name; - uint32_t did0; - uint32_t did1; - uint32_t dc0; - uint32_t dc1; - uint32_t dc2; - uint32_t dc3; - uint32_t dc4; - uint32_t peripherals; -} stellaris_board_info; - -/* General purpose timer module. */ - -#define TYPE_STELLARIS_GPTM "stellaris-gptm" -#define STELLARIS_GPTM(obj) \ - OBJECT_CHECK(gptm_state, (obj), TYPE_STELLARIS_GPTM) - -typedef struct gptm_state { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t config; - uint32_t mode[2]; - uint32_t control; - uint32_t state; - uint32_t mask; - uint32_t load[2]; - uint32_t match[2]; - uint32_t prescale[2]; - uint32_t match_prescale[2]; - uint32_t rtc; - int64_t tick[2]; - struct gptm_state *opaque[2]; - QEMUTimer *timer[2]; - /* The timers have an alternate output used to trigger the ADC. */ - qemu_irq trigger; - qemu_irq irq; -} gptm_state; - -static void gptm_update_irq(gptm_state *s) -{ - int level; - level = (s->state & s->mask) != 0; - qemu_set_irq(s->irq, level); -} - -static void gptm_stop(gptm_state *s, int n) -{ - timer_del(s->timer[n]); -} - -static void gptm_reload(gptm_state *s, int n, int reset) -{ - int64_t tick; - if (reset) - tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - else - tick = s->tick[n]; - - if (s->config == 0) { - /* 32-bit CountDown. */ - uint32_t count; - count = s->load[0] | (s->load[1] << 16); - tick += (int64_t)count * system_clock_scale; - } else if (s->config == 1) { - /* 32-bit RTC. 1Hz tick. */ - tick += NANOSECONDS_PER_SECOND; - } else if (s->mode[n] == 0xa) { - /* PWM mode. Not implemented. */ - } else { - hw_error("TODO: 16-bit timer mode 0x%x\n", s->mode[n]); - } - s->tick[n] = tick; - timer_mod(s->timer[n], tick); -} - -static void gptm_tick(void *opaque) -{ - gptm_state **p = (gptm_state **)opaque; - gptm_state *s; - int n; - - s = *p; - n = p - s->opaque; - if (s->config == 0) { - s->state |= 1; - if ((s->control & 0x20)) { - /* Output trigger. */ - qemu_irq_pulse(s->trigger); - } - if (s->mode[0] & 1) { - /* One-shot. */ - s->control &= ~1; - } else { - /* Periodic. */ - gptm_reload(s, 0, 0); - } - } else if (s->config == 1) { - /* RTC. */ - uint32_t match; - s->rtc++; - match = s->match[0] | (s->match[1] << 16); - if (s->rtc > match) - s->rtc = 0; - if (s->rtc == 0) { - s->state |= 8; - } - gptm_reload(s, 0, 0); - } else if (s->mode[n] == 0xa) { - /* PWM mode. Not implemented. */ - } else { - hw_error("TODO: 16-bit timer mode 0x%x\n", s->mode[n]); - } - gptm_update_irq(s); -} - -static uint64_t gptm_read(void *opaque, hwaddr offset, - unsigned size) -{ - gptm_state *s = (gptm_state *)opaque; - - switch (offset) { - case 0x00: /* CFG */ - return s->config; - case 0x04: /* TAMR */ - return s->mode[0]; - case 0x08: /* TBMR */ - return s->mode[1]; - case 0x0c: /* CTL */ - return s->control; - case 0x18: /* IMR */ - return s->mask; - case 0x1c: /* RIS */ - return s->state; - case 0x20: /* MIS */ - return s->state & s->mask; - case 0x24: /* CR */ - return 0; - case 0x28: /* TAILR */ - return s->load[0] | ((s->config < 4) ? (s->load[1] << 16) : 0); - case 0x2c: /* TBILR */ - return s->load[1]; - case 0x30: /* TAMARCHR */ - return s->match[0] | ((s->config < 4) ? (s->match[1] << 16) : 0); - case 0x34: /* TBMATCHR */ - return s->match[1]; - case 0x38: /* TAPR */ - return s->prescale[0]; - case 0x3c: /* TBPR */ - return s->prescale[1]; - case 0x40: /* TAPMR */ - return s->match_prescale[0]; - case 0x44: /* TBPMR */ - return s->match_prescale[1]; - case 0x48: /* TAR */ - if (s->config == 1) { - return s->rtc; - } - qemu_log_mask(LOG_UNIMP, - "GPTM: read of TAR but timer read not supported"); - return 0; - case 0x4c: /* TBR */ - qemu_log_mask(LOG_UNIMP, - "GPTM: read of TBR but timer read not supported"); - return 0; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "GPTM: read at bad offset 0x%x\n", (int)offset); - return 0; - } -} - -static void gptm_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - gptm_state *s = (gptm_state *)opaque; - uint32_t oldval; - - /* The timers should be disabled before changing the configuration. - We take advantage of this and defer everything until the timer - is enabled. */ - switch (offset) { - case 0x00: /* CFG */ - s->config = value; - break; - case 0x04: /* TAMR */ - s->mode[0] = value; - break; - case 0x08: /* TBMR */ - s->mode[1] = value; - break; - case 0x0c: /* CTL */ - oldval = s->control; - s->control = value; - /* TODO: Implement pause. */ - if ((oldval ^ value) & 1) { - if (value & 1) { - gptm_reload(s, 0, 1); - } else { - gptm_stop(s, 0); - } - } - if (((oldval ^ value) & 0x100) && s->config >= 4) { - if (value & 0x100) { - gptm_reload(s, 1, 1); - } else { - gptm_stop(s, 1); - } - } - break; - case 0x18: /* IMR */ - s->mask = value & 0x77; - gptm_update_irq(s); - break; - case 0x24: /* CR */ - s->state &= ~value; - break; - case 0x28: /* TAILR */ - s->load[0] = value & 0xffff; - if (s->config < 4) { - s->load[1] = value >> 16; - } - break; - case 0x2c: /* TBILR */ - s->load[1] = value & 0xffff; - break; - case 0x30: /* TAMARCHR */ - s->match[0] = value & 0xffff; - if (s->config < 4) { - s->match[1] = value >> 16; - } - break; - case 0x34: /* TBMATCHR */ - s->match[1] = value >> 16; - break; - case 0x38: /* TAPR */ - s->prescale[0] = value; - break; - case 0x3c: /* TBPR */ - s->prescale[1] = value; - break; - case 0x40: /* TAPMR */ - s->match_prescale[0] = value; - break; - case 0x44: /* TBPMR */ - s->match_prescale[0] = value; - break; - default: - hw_error("gptm_write: Bad offset 0x%x\n", (int)offset); - } - gptm_update_irq(s); -} - -static const MemoryRegionOps gptm_ops = { - .read = gptm_read, - .write = gptm_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_stellaris_gptm = { - .name = "stellaris_gptm", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(config, gptm_state), - VMSTATE_UINT32_ARRAY(mode, gptm_state, 2), - VMSTATE_UINT32(control, gptm_state), - VMSTATE_UINT32(state, gptm_state), - VMSTATE_UINT32(mask, gptm_state), - VMSTATE_UNUSED(8), - VMSTATE_UINT32_ARRAY(load, gptm_state, 2), - VMSTATE_UINT32_ARRAY(match, gptm_state, 2), - VMSTATE_UINT32_ARRAY(prescale, gptm_state, 2), - VMSTATE_UINT32_ARRAY(match_prescale, gptm_state, 2), - VMSTATE_UINT32(rtc, gptm_state), - VMSTATE_INT64_ARRAY(tick, gptm_state, 2), - VMSTATE_TIMER_PTR_ARRAY(timer, gptm_state, 2), - VMSTATE_END_OF_LIST() - } -}; - -static int stellaris_gptm_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - gptm_state *s = STELLARIS_GPTM(dev); - - sysbus_init_irq(sbd, &s->irq); - qdev_init_gpio_out(dev, &s->trigger, 1); - - memory_region_init_io(&s->iomem, OBJECT(s), &gptm_ops, s, - "gptm", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - - s->opaque[0] = s->opaque[1] = s; - s->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[0]); - s->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[1]); - vmstate_register(dev, -1, &vmstate_stellaris_gptm, s); - return 0; -} - - -/* System controller. */ - -typedef struct { - MemoryRegion iomem; - uint32_t pborctl; - uint32_t ldopctl; - uint32_t int_status; - uint32_t int_mask; - uint32_t resc; - uint32_t rcc; - uint32_t rcc2; - uint32_t rcgc[3]; - uint32_t scgc[3]; - uint32_t dcgc[3]; - uint32_t clkvclr; - uint32_t ldoarst; - uint32_t user0; - uint32_t user1; - qemu_irq irq; - stellaris_board_info *board; -} ssys_state; - -static void ssys_update(ssys_state *s) -{ - qemu_set_irq(s->irq, (s->int_status & s->int_mask) != 0); -} - -static uint32_t pllcfg_sandstorm[16] = { - 0x31c0, /* 1 Mhz */ - 0x1ae0, /* 1.8432 Mhz */ - 0x18c0, /* 2 Mhz */ - 0xd573, /* 2.4576 Mhz */ - 0x37a6, /* 3.57954 Mhz */ - 0x1ae2, /* 3.6864 Mhz */ - 0x0c40, /* 4 Mhz */ - 0x98bc, /* 4.906 Mhz */ - 0x935b, /* 4.9152 Mhz */ - 0x09c0, /* 5 Mhz */ - 0x4dee, /* 5.12 Mhz */ - 0x0c41, /* 6 Mhz */ - 0x75db, /* 6.144 Mhz */ - 0x1ae6, /* 7.3728 Mhz */ - 0x0600, /* 8 Mhz */ - 0x585b /* 8.192 Mhz */ -}; - -static uint32_t pllcfg_fury[16] = { - 0x3200, /* 1 Mhz */ - 0x1b20, /* 1.8432 Mhz */ - 0x1900, /* 2 Mhz */ - 0xf42b, /* 2.4576 Mhz */ - 0x37e3, /* 3.57954 Mhz */ - 0x1b21, /* 3.6864 Mhz */ - 0x0c80, /* 4 Mhz */ - 0x98ee, /* 4.906 Mhz */ - 0xd5b4, /* 4.9152 Mhz */ - 0x0a00, /* 5 Mhz */ - 0x4e27, /* 5.12 Mhz */ - 0x1902, /* 6 Mhz */ - 0xec1c, /* 6.144 Mhz */ - 0x1b23, /* 7.3728 Mhz */ - 0x0640, /* 8 Mhz */ - 0xb11c /* 8.192 Mhz */ -}; - -#define DID0_VER_MASK 0x70000000 -#define DID0_VER_0 0x00000000 -#define DID0_VER_1 0x10000000 - -#define DID0_CLASS_MASK 0x00FF0000 -#define DID0_CLASS_SANDSTORM 0x00000000 -#define DID0_CLASS_FURY 0x00010000 - -static int ssys_board_class(const ssys_state *s) -{ - uint32_t did0 = s->board->did0; - switch (did0 & DID0_VER_MASK) { - case DID0_VER_0: - return DID0_CLASS_SANDSTORM; - case DID0_VER_1: - switch (did0 & DID0_CLASS_MASK) { - case DID0_CLASS_SANDSTORM: - case DID0_CLASS_FURY: - return did0 & DID0_CLASS_MASK; - } - /* for unknown classes, fall through */ - default: - hw_error("ssys_board_class: Unknown class 0x%08x\n", did0); - } -} - -static uint64_t ssys_read(void *opaque, hwaddr offset, - unsigned size) -{ - ssys_state *s = (ssys_state *)opaque; - - switch (offset) { - case 0x000: /* DID0 */ - return s->board->did0; - case 0x004: /* DID1 */ - return s->board->did1; - case 0x008: /* DC0 */ - return s->board->dc0; - case 0x010: /* DC1 */ - return s->board->dc1; - case 0x014: /* DC2 */ - return s->board->dc2; - case 0x018: /* DC3 */ - return s->board->dc3; - case 0x01c: /* DC4 */ - return s->board->dc4; - case 0x030: /* PBORCTL */ - return s->pborctl; - case 0x034: /* LDOPCTL */ - return s->ldopctl; - case 0x040: /* SRCR0 */ - return 0; - case 0x044: /* SRCR1 */ - return 0; - case 0x048: /* SRCR2 */ - return 0; - case 0x050: /* RIS */ - return s->int_status; - case 0x054: /* IMC */ - return s->int_mask; - case 0x058: /* MISC */ - return s->int_status & s->int_mask; - case 0x05c: /* RESC */ - return s->resc; - case 0x060: /* RCC */ - return s->rcc; - case 0x064: /* PLLCFG */ - { - int xtal; - xtal = (s->rcc >> 6) & 0xf; - switch (ssys_board_class(s)) { - case DID0_CLASS_FURY: - return pllcfg_fury[xtal]; - case DID0_CLASS_SANDSTORM: - return pllcfg_sandstorm[xtal]; - default: - hw_error("ssys_read: Unhandled class for PLLCFG read.\n"); - return 0; - } - } - case 0x070: /* RCC2 */ - return s->rcc2; - case 0x100: /* RCGC0 */ - return s->rcgc[0]; - case 0x104: /* RCGC1 */ - return s->rcgc[1]; - case 0x108: /* RCGC2 */ - return s->rcgc[2]; - case 0x110: /* SCGC0 */ - return s->scgc[0]; - case 0x114: /* SCGC1 */ - return s->scgc[1]; - case 0x118: /* SCGC2 */ - return s->scgc[2]; - case 0x120: /* DCGC0 */ - return s->dcgc[0]; - case 0x124: /* DCGC1 */ - return s->dcgc[1]; - case 0x128: /* DCGC2 */ - return s->dcgc[2]; - case 0x150: /* CLKVCLR */ - return s->clkvclr; - case 0x160: /* LDOARST */ - return s->ldoarst; - case 0x1e0: /* USER0 */ - return s->user0; - case 0x1e4: /* USER1 */ - return s->user1; - default: - hw_error("ssys_read: Bad offset 0x%x\n", (int)offset); - return 0; - } -} - -static bool ssys_use_rcc2(ssys_state *s) -{ - return (s->rcc2 >> 31) & 0x1; -} - -/* - * Caculate the sys. clock period in ms. - */ -static void ssys_calculate_system_clock(ssys_state *s) -{ - if (ssys_use_rcc2(s)) { - system_clock_scale = 5 * (((s->rcc2 >> 23) & 0x3f) + 1); - } else { - system_clock_scale = 5 * (((s->rcc >> 23) & 0xf) + 1); - } -} - -static void ssys_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - ssys_state *s = (ssys_state *)opaque; - - switch (offset) { - case 0x030: /* PBORCTL */ - s->pborctl = value & 0xffff; - break; - case 0x034: /* LDOPCTL */ - s->ldopctl = value & 0x1f; - break; - case 0x040: /* SRCR0 */ - case 0x044: /* SRCR1 */ - case 0x048: /* SRCR2 */ - fprintf(stderr, "Peripheral reset not implemented\n"); - break; - case 0x054: /* IMC */ - s->int_mask = value & 0x7f; - break; - case 0x058: /* MISC */ - s->int_status &= ~value; - break; - case 0x05c: /* RESC */ - s->resc = value & 0x3f; - break; - case 0x060: /* RCC */ - if ((s->rcc & (1 << 13)) != 0 && (value & (1 << 13)) == 0) { - /* PLL enable. */ - s->int_status |= (1 << 6); - } - s->rcc = value; - ssys_calculate_system_clock(s); - break; - case 0x070: /* RCC2 */ - if (ssys_board_class(s) == DID0_CLASS_SANDSTORM) { - break; - } - - if ((s->rcc2 & (1 << 13)) != 0 && (value & (1 << 13)) == 0) { - /* PLL enable. */ - s->int_status |= (1 << 6); - } - s->rcc2 = value; - ssys_calculate_system_clock(s); - break; - case 0x100: /* RCGC0 */ - s->rcgc[0] = value; - break; - case 0x104: /* RCGC1 */ - s->rcgc[1] = value; - break; - case 0x108: /* RCGC2 */ - s->rcgc[2] = value; - break; - case 0x110: /* SCGC0 */ - s->scgc[0] = value; - break; - case 0x114: /* SCGC1 */ - s->scgc[1] = value; - break; - case 0x118: /* SCGC2 */ - s->scgc[2] = value; - break; - case 0x120: /* DCGC0 */ - s->dcgc[0] = value; - break; - case 0x124: /* DCGC1 */ - s->dcgc[1] = value; - break; - case 0x128: /* DCGC2 */ - s->dcgc[2] = value; - break; - case 0x150: /* CLKVCLR */ - s->clkvclr = value; - break; - case 0x160: /* LDOARST */ - s->ldoarst = value; - break; - default: - hw_error("ssys_write: Bad offset 0x%x\n", (int)offset); - } - ssys_update(s); -} - -static const MemoryRegionOps ssys_ops = { - .read = ssys_read, - .write = ssys_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void ssys_reset(void *opaque) -{ - ssys_state *s = (ssys_state *)opaque; - - s->pborctl = 0x7ffd; - s->rcc = 0x078e3ac0; - - if (ssys_board_class(s) == DID0_CLASS_SANDSTORM) { - s->rcc2 = 0; - } else { - s->rcc2 = 0x07802810; - } - s->rcgc[0] = 1; - s->scgc[0] = 1; - s->dcgc[0] = 1; - ssys_calculate_system_clock(s); -} - -static int stellaris_sys_post_load(void *opaque, int version_id) -{ - ssys_state *s = opaque; - - ssys_calculate_system_clock(s); - - return 0; -} - -static const VMStateDescription vmstate_stellaris_sys = { - .name = "stellaris_sys", - .version_id = 2, - .minimum_version_id = 1, - .post_load = stellaris_sys_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(pborctl, ssys_state), - VMSTATE_UINT32(ldopctl, ssys_state), - VMSTATE_UINT32(int_mask, ssys_state), - VMSTATE_UINT32(int_status, ssys_state), - VMSTATE_UINT32(resc, ssys_state), - VMSTATE_UINT32(rcc, ssys_state), - VMSTATE_UINT32_V(rcc2, ssys_state, 2), - VMSTATE_UINT32_ARRAY(rcgc, ssys_state, 3), - VMSTATE_UINT32_ARRAY(scgc, ssys_state, 3), - VMSTATE_UINT32_ARRAY(dcgc, ssys_state, 3), - VMSTATE_UINT32(clkvclr, ssys_state), - VMSTATE_UINT32(ldoarst, ssys_state), - VMSTATE_END_OF_LIST() - } -}; - -static int stellaris_sys_init(uint32_t base, qemu_irq irq, - stellaris_board_info * board, - uint8_t *macaddr) -{ - ssys_state *s; - - s = g_new0(ssys_state, 1); - s->irq = irq; - s->board = board; - /* Most devices come preprogrammed with a MAC address in the user data. */ - s->user0 = macaddr[0] | (macaddr[1] << 8) | (macaddr[2] << 16); - s->user1 = macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16); - - memory_region_init_io(&s->iomem, NULL, &ssys_ops, s, "ssys", 0x00001000); - memory_region_add_subregion(get_system_memory(), base, &s->iomem); - ssys_reset(s); - vmstate_register(NULL, -1, &vmstate_stellaris_sys, s); - return 0; -} - - -/* I2C controller. */ - -#define TYPE_STELLARIS_I2C "stellaris-i2c" -#define STELLARIS_I2C(obj) \ - OBJECT_CHECK(stellaris_i2c_state, (obj), TYPE_STELLARIS_I2C) - -typedef struct { - SysBusDevice parent_obj; - - I2CBus *bus; - qemu_irq irq; - MemoryRegion iomem; - uint32_t msa; - uint32_t mcs; - uint32_t mdr; - uint32_t mtpr; - uint32_t mimr; - uint32_t mris; - uint32_t mcr; -} stellaris_i2c_state; - -#define STELLARIS_I2C_MCS_BUSY 0x01 -#define STELLARIS_I2C_MCS_ERROR 0x02 -#define STELLARIS_I2C_MCS_ADRACK 0x04 -#define STELLARIS_I2C_MCS_DATACK 0x08 -#define STELLARIS_I2C_MCS_ARBLST 0x10 -#define STELLARIS_I2C_MCS_IDLE 0x20 -#define STELLARIS_I2C_MCS_BUSBSY 0x40 - -static uint64_t stellaris_i2c_read(void *opaque, hwaddr offset, - unsigned size) -{ - stellaris_i2c_state *s = (stellaris_i2c_state *)opaque; - - switch (offset) { - case 0x00: /* MSA */ - return s->msa; - case 0x04: /* MCS */ - /* We don't emulate timing, so the controller is never busy. */ - return s->mcs | STELLARIS_I2C_MCS_IDLE; - case 0x08: /* MDR */ - return s->mdr; - case 0x0c: /* MTPR */ - return s->mtpr; - case 0x10: /* MIMR */ - return s->mimr; - case 0x14: /* MRIS */ - return s->mris; - case 0x18: /* MMIS */ - return s->mris & s->mimr; - case 0x20: /* MCR */ - return s->mcr; - default: - hw_error("strllaris_i2c_read: Bad offset 0x%x\n", (int)offset); - return 0; - } -} - -static void stellaris_i2c_update(stellaris_i2c_state *s) -{ - int level; - - level = (s->mris & s->mimr) != 0; - qemu_set_irq(s->irq, level); -} - -static void stellaris_i2c_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - stellaris_i2c_state *s = (stellaris_i2c_state *)opaque; - - switch (offset) { - case 0x00: /* MSA */ - s->msa = value & 0xff; - break; - case 0x04: /* MCS */ - if ((s->mcr & 0x10) == 0) { - /* Disabled. Do nothing. */ - break; - } - /* Grab the bus if this is starting a transfer. */ - if ((value & 2) && (s->mcs & STELLARIS_I2C_MCS_BUSBSY) == 0) { - if (i2c_start_transfer(s->bus, s->msa >> 1, s->msa & 1)) { - s->mcs |= STELLARIS_I2C_MCS_ARBLST; - } else { - s->mcs &= ~STELLARIS_I2C_MCS_ARBLST; - s->mcs |= STELLARIS_I2C_MCS_BUSBSY; - } - } - /* If we don't have the bus then indicate an error. */ - if (!i2c_bus_busy(s->bus) - || (s->mcs & STELLARIS_I2C_MCS_BUSBSY) == 0) { - s->mcs |= STELLARIS_I2C_MCS_ERROR; - break; - } - s->mcs &= ~STELLARIS_I2C_MCS_ERROR; - if (value & 1) { - /* Transfer a byte. */ - /* TODO: Handle errors. */ - if (s->msa & 1) { - /* Recv */ - s->mdr = i2c_recv(s->bus) & 0xff; - } else { - /* Send */ - i2c_send(s->bus, s->mdr); - } - /* Raise an interrupt. */ - s->mris |= 1; - } - if (value & 4) { - /* Finish transfer. */ - i2c_end_transfer(s->bus); - s->mcs &= ~STELLARIS_I2C_MCS_BUSBSY; - } - break; - case 0x08: /* MDR */ - s->mdr = value & 0xff; - break; - case 0x0c: /* MTPR */ - s->mtpr = value & 0xff; - break; - case 0x10: /* MIMR */ - s->mimr = 1; - break; - case 0x1c: /* MICR */ - s->mris &= ~value; - break; - case 0x20: /* MCR */ - if (value & 1) - hw_error( - "stellaris_i2c_write: Loopback not implemented\n"); - if (value & 0x20) - hw_error( - "stellaris_i2c_write: Slave mode not implemented\n"); - s->mcr = value & 0x31; - break; - default: - hw_error("stellaris_i2c_write: Bad offset 0x%x\n", - (int)offset); - } - stellaris_i2c_update(s); -} - -static void stellaris_i2c_reset(stellaris_i2c_state *s) -{ - if (s->mcs & STELLARIS_I2C_MCS_BUSBSY) - i2c_end_transfer(s->bus); - - s->msa = 0; - s->mcs = 0; - s->mdr = 0; - s->mtpr = 1; - s->mimr = 0; - s->mris = 0; - s->mcr = 0; - stellaris_i2c_update(s); -} - -static const MemoryRegionOps stellaris_i2c_ops = { - .read = stellaris_i2c_read, - .write = stellaris_i2c_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_stellaris_i2c = { - .name = "stellaris_i2c", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(msa, stellaris_i2c_state), - VMSTATE_UINT32(mcs, stellaris_i2c_state), - VMSTATE_UINT32(mdr, stellaris_i2c_state), - VMSTATE_UINT32(mtpr, stellaris_i2c_state), - VMSTATE_UINT32(mimr, stellaris_i2c_state), - VMSTATE_UINT32(mris, stellaris_i2c_state), - VMSTATE_UINT32(mcr, stellaris_i2c_state), - VMSTATE_END_OF_LIST() - } -}; - -static int stellaris_i2c_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - stellaris_i2c_state *s = STELLARIS_I2C(dev); - I2CBus *bus; - - sysbus_init_irq(sbd, &s->irq); - bus = i2c_init_bus(dev, "i2c"); - s->bus = bus; - - memory_region_init_io(&s->iomem, OBJECT(s), &stellaris_i2c_ops, s, - "i2c", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - /* ??? For now we only implement the master interface. */ - stellaris_i2c_reset(s); - vmstate_register(dev, -1, &vmstate_stellaris_i2c, s); - return 0; -} - -/* Analogue to Digital Converter. This is only partially implemented, - enough for applications that use a combined ADC and timer tick. */ - -#define STELLARIS_ADC_EM_CONTROLLER 0 -#define STELLARIS_ADC_EM_COMP 1 -#define STELLARIS_ADC_EM_EXTERNAL 4 -#define STELLARIS_ADC_EM_TIMER 5 -#define STELLARIS_ADC_EM_PWM0 6 -#define STELLARIS_ADC_EM_PWM1 7 -#define STELLARIS_ADC_EM_PWM2 8 - -#define STELLARIS_ADC_FIFO_EMPTY 0x0100 -#define STELLARIS_ADC_FIFO_FULL 0x1000 - -#define TYPE_STELLARIS_ADC "stellaris-adc" -#define STELLARIS_ADC(obj) \ - OBJECT_CHECK(stellaris_adc_state, (obj), TYPE_STELLARIS_ADC) - -typedef struct StellarisADCState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t actss; - uint32_t ris; - uint32_t im; - uint32_t emux; - uint32_t ostat; - uint32_t ustat; - uint32_t sspri; - uint32_t sac; - struct { - uint32_t state; - uint32_t data[16]; - } fifo[4]; - uint32_t ssmux[4]; - uint32_t ssctl[4]; - uint32_t noise; - qemu_irq irq[4]; -} stellaris_adc_state; - -static uint32_t stellaris_adc_fifo_read(stellaris_adc_state *s, int n) -{ - int tail; - - tail = s->fifo[n].state & 0xf; - if (s->fifo[n].state & STELLARIS_ADC_FIFO_EMPTY) { - s->ustat |= 1 << n; - } else { - s->fifo[n].state = (s->fifo[n].state & ~0xf) | ((tail + 1) & 0xf); - s->fifo[n].state &= ~STELLARIS_ADC_FIFO_FULL; - if (tail + 1 == ((s->fifo[n].state >> 4) & 0xf)) - s->fifo[n].state |= STELLARIS_ADC_FIFO_EMPTY; - } - return s->fifo[n].data[tail]; -} - -static void stellaris_adc_fifo_write(stellaris_adc_state *s, int n, - uint32_t value) -{ - int head; - - /* TODO: Real hardware has limited size FIFOs. We have a full 16 entry - FIFO fir each sequencer. */ - head = (s->fifo[n].state >> 4) & 0xf; - if (s->fifo[n].state & STELLARIS_ADC_FIFO_FULL) { - s->ostat |= 1 << n; - return; - } - s->fifo[n].data[head] = value; - head = (head + 1) & 0xf; - s->fifo[n].state &= ~STELLARIS_ADC_FIFO_EMPTY; - s->fifo[n].state = (s->fifo[n].state & ~0xf0) | (head << 4); - if ((s->fifo[n].state & 0xf) == head) - s->fifo[n].state |= STELLARIS_ADC_FIFO_FULL; -} - -static void stellaris_adc_update(stellaris_adc_state *s) -{ - int level; - int n; - - for (n = 0; n < 4; n++) { - level = (s->ris & s->im & (1 << n)) != 0; - qemu_set_irq(s->irq[n], level); - } -} - -static void stellaris_adc_trigger(void *opaque, int irq, int level) -{ - stellaris_adc_state *s = (stellaris_adc_state *)opaque; - int n; - - for (n = 0; n < 4; n++) { - if ((s->actss & (1 << n)) == 0) { - continue; - } - - if (((s->emux >> (n * 4)) & 0xff) != 5) { - continue; - } - - /* Some applications use the ADC as a random number source, so introduce - some variation into the signal. */ - s->noise = s->noise * 314159 + 1; - /* ??? actual inputs not implemented. Return an arbitrary value. */ - stellaris_adc_fifo_write(s, n, 0x200 + ((s->noise >> 16) & 7)); - s->ris |= (1 << n); - stellaris_adc_update(s); - } -} - -static void stellaris_adc_reset(stellaris_adc_state *s) -{ - int n; - - for (n = 0; n < 4; n++) { - s->ssmux[n] = 0; - s->ssctl[n] = 0; - s->fifo[n].state = STELLARIS_ADC_FIFO_EMPTY; - } -} - -static uint64_t stellaris_adc_read(void *opaque, hwaddr offset, - unsigned size) -{ - stellaris_adc_state *s = (stellaris_adc_state *)opaque; - - /* TODO: Implement this. */ - if (offset >= 0x40 && offset < 0xc0) { - int n; - n = (offset - 0x40) >> 5; - switch (offset & 0x1f) { - case 0x00: /* SSMUX */ - return s->ssmux[n]; - case 0x04: /* SSCTL */ - return s->ssctl[n]; - case 0x08: /* SSFIFO */ - return stellaris_adc_fifo_read(s, n); - case 0x0c: /* SSFSTAT */ - return s->fifo[n].state; - default: - break; - } - } - switch (offset) { - case 0x00: /* ACTSS */ - return s->actss; - case 0x04: /* RIS */ - return s->ris; - case 0x08: /* IM */ - return s->im; - case 0x0c: /* ISC */ - return s->ris & s->im; - case 0x10: /* OSTAT */ - return s->ostat; - case 0x14: /* EMUX */ - return s->emux; - case 0x18: /* USTAT */ - return s->ustat; - case 0x20: /* SSPRI */ - return s->sspri; - case 0x30: /* SAC */ - return s->sac; - default: - hw_error("strllaris_adc_read: Bad offset 0x%x\n", - (int)offset); - return 0; - } -} - -static void stellaris_adc_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - stellaris_adc_state *s = (stellaris_adc_state *)opaque; - - /* TODO: Implement this. */ - if (offset >= 0x40 && offset < 0xc0) { - int n; - n = (offset - 0x40) >> 5; - switch (offset & 0x1f) { - case 0x00: /* SSMUX */ - s->ssmux[n] = value & 0x33333333; - return; - case 0x04: /* SSCTL */ - if (value != 6) { - hw_error("ADC: Unimplemented sequence %" PRIx64 "\n", - value); - } - s->ssctl[n] = value; - return; - default: - break; - } - } - switch (offset) { - case 0x00: /* ACTSS */ - s->actss = value & 0xf; - break; - case 0x08: /* IM */ - s->im = value; - break; - case 0x0c: /* ISC */ - s->ris &= ~value; - break; - case 0x10: /* OSTAT */ - s->ostat &= ~value; - break; - case 0x14: /* EMUX */ - s->emux = value; - break; - case 0x18: /* USTAT */ - s->ustat &= ~value; - break; - case 0x20: /* SSPRI */ - s->sspri = value; - break; - case 0x28: /* PSSI */ - hw_error("Not implemented: ADC sample initiate\n"); - break; - case 0x30: /* SAC */ - s->sac = value; - break; - default: - hw_error("stellaris_adc_write: Bad offset 0x%x\n", (int)offset); - } - stellaris_adc_update(s); -} - -static const MemoryRegionOps stellaris_adc_ops = { - .read = stellaris_adc_read, - .write = stellaris_adc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_stellaris_adc = { - .name = "stellaris_adc", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(actss, stellaris_adc_state), - VMSTATE_UINT32(ris, stellaris_adc_state), - VMSTATE_UINT32(im, stellaris_adc_state), - VMSTATE_UINT32(emux, stellaris_adc_state), - VMSTATE_UINT32(ostat, stellaris_adc_state), - VMSTATE_UINT32(ustat, stellaris_adc_state), - VMSTATE_UINT32(sspri, stellaris_adc_state), - VMSTATE_UINT32(sac, stellaris_adc_state), - VMSTATE_UINT32(fifo[0].state, stellaris_adc_state), - VMSTATE_UINT32_ARRAY(fifo[0].data, stellaris_adc_state, 16), - VMSTATE_UINT32(ssmux[0], stellaris_adc_state), - VMSTATE_UINT32(ssctl[0], stellaris_adc_state), - VMSTATE_UINT32(fifo[1].state, stellaris_adc_state), - VMSTATE_UINT32_ARRAY(fifo[1].data, stellaris_adc_state, 16), - VMSTATE_UINT32(ssmux[1], stellaris_adc_state), - VMSTATE_UINT32(ssctl[1], stellaris_adc_state), - VMSTATE_UINT32(fifo[2].state, stellaris_adc_state), - VMSTATE_UINT32_ARRAY(fifo[2].data, stellaris_adc_state, 16), - VMSTATE_UINT32(ssmux[2], stellaris_adc_state), - VMSTATE_UINT32(ssctl[2], stellaris_adc_state), - VMSTATE_UINT32(fifo[3].state, stellaris_adc_state), - VMSTATE_UINT32_ARRAY(fifo[3].data, stellaris_adc_state, 16), - VMSTATE_UINT32(ssmux[3], stellaris_adc_state), - VMSTATE_UINT32(ssctl[3], stellaris_adc_state), - VMSTATE_UINT32(noise, stellaris_adc_state), - VMSTATE_END_OF_LIST() - } -}; - -static int stellaris_adc_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - stellaris_adc_state *s = STELLARIS_ADC(dev); - int n; - - for (n = 0; n < 4; n++) { - sysbus_init_irq(sbd, &s->irq[n]); - } - - memory_region_init_io(&s->iomem, OBJECT(s), &stellaris_adc_ops, s, - "adc", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - stellaris_adc_reset(s); - qdev_init_gpio_in(dev, stellaris_adc_trigger, 1); - vmstate_register(dev, -1, &vmstate_stellaris_adc, s); - 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", - 0, - 0x0032000e, - 0x001f001f, /* dc0 */ - 0x001132bf, - 0x01071013, - 0x3f0f01ff, - 0x0000001f, - BP_OLED_I2C - }, - { "LM3S6965EVB", - 0x10010002, - 0x1073402e, - 0x00ff007f, /* dc0 */ - 0x001133ff, - 0x030f5317, - 0x0f0f87ff, - 0x5000007f, - BP_OLED_SSI | BP_GAMEPAD - } -}; - -static void stellaris_init(const char *kernel_filename, const char *cpu_model, - stellaris_board_info *board) -{ - static const int uart_irq[] = {5, 6, 33, 34}; - static const int timer_irq[] = {19, 21, 23, 35}; - static const uint32_t gpio_addr[7] = - { 0x40004000, 0x40005000, 0x40006000, 0x40007000, - 0x40024000, 0x40025000, 0x40026000}; - static const int gpio_irq[7] = {0, 1, 2, 3, 4, 30, 31}; - - DeviceState *gpio_dev[7], *nvic; - qemu_irq gpio_in[7][8]; - qemu_irq gpio_out[7][8]; - qemu_irq adc; - int sram_size; - int flash_size; - I2CBus *i2c; - DeviceState *dev; - int i; - int j; - - MemoryRegion *sram = g_new(MemoryRegion, 1); - MemoryRegion *flash = g_new(MemoryRegion, 1); - MemoryRegion *system_memory = get_system_memory(); - - flash_size = (((board->dc0 & 0xffff) + 1) << 1) * 1024; - sram_size = ((board->dc0 >> 18) + 1) * 1024; - - /* Flash programming is done via the SCU, so pretend it is ROM. */ - memory_region_init_ram(flash, NULL, "stellaris.flash", flash_size, - &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_fatal); - vmstate_register_ram_global(sram); - memory_region_add_subregion(system_memory, 0x20000000, sram); - - 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, - 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; - } - for (i = 0; i < 4; i++) { - if (board->dc2 & (0x10000 << i)) { - dev = sysbus_create_simple(TYPE_STELLARIS_GPTM, - 0x40030000 + i * 0x1000, - 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, 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], - 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; - } - } - } - - if (board->dc2 & (1 << 12)) { - 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); - } - } - - for (i = 0; i < 4; i++) { - if (board->dc2 & (1 << i)) { - sysbus_create_simple("pl011_luminary", 0x4000c000 + i * 0x1000, - qdev_get_gpio_in(nvic, uart_irq[i])); - } - } - if (board->dc2 & (1 << 4)) { - dev = sysbus_create_simple("pl022", 0x40008000, - qdev_get_gpio_in(nvic, 7)); - if (board->peripherals & BP_OLED_SSI) { - void *bus; - DeviceState *sddev; - DeviceState *ssddev; - - /* Some boards have both an OLED controller and SD card connected to - * the same SSI port, with the SD card chip select connected to a - * GPIO pin. Technically the OLED chip select is connected to the - * SSI Fss pin. We do not bother emulating that as both devices - * should never be selected simultaneously, and our OLED controller - * ignores stray 0xff commands that occur when deselecting the SD - * card. - */ - bus = qdev_get_child_bus(dev, "ssi"); - - sddev = ssi_create_slave(bus, "ssi-sd"); - ssddev = ssi_create_slave(bus, "ssd0323"); - gpio_out[GPIO_D][0] = qemu_irq_split( - qdev_get_gpio_in_named(sddev, SSI_GPIO_CS, 0), - qdev_get_gpio_in_named(ssddev, SSI_GPIO_CS, 0)); - gpio_out[GPIO_C][7] = qdev_get_gpio_in(ssddev, 0); - - /* Make sure the select pin is high. */ - qemu_irq_raise(gpio_out[GPIO_D][0]); - } - } - if (board->dc4 & (1 << 28)) { - DeviceState *enet; - - qemu_check_nic_model(&nd_table[0], "stellaris"); - - enet = qdev_create(NULL, "stellaris_enet"); - 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, qdev_get_gpio_in(nvic, 42)); - } - if (board->peripherals & BP_GAMEPAD) { - qemu_irq gpad_irq[5]; - static const int gpad_keycode[5] = { 0xc8, 0xd0, 0xcb, 0xcd, 0x1d }; - - gpad_irq[0] = qemu_irq_invert(gpio_in[GPIO_E][0]); /* up */ - gpad_irq[1] = qemu_irq_invert(gpio_in[GPIO_E][1]); /* down */ - gpad_irq[2] = qemu_irq_invert(gpio_in[GPIO_E][2]); /* left */ - gpad_irq[3] = qemu_irq_invert(gpio_in[GPIO_E][3]); /* right */ - gpad_irq[4] = qemu_irq_invert(gpio_in[GPIO_F][1]); /* select */ - - stellaris_gamepad_init(5, gpad_irq, gpad_keycode); - } - for (i = 0; i < 7; i++) { - if (board->dc4 & (1 << i)) { - for (j = 0; j < 8; j++) { - if (gpio_out[i][j]) { - qdev_connect_gpio_out(gpio_dev[i], j, gpio_out[i][j]); - } - } - } - } -} - -/* FIXME: Figure out how to generate these from stellaris_boards. */ -static void lm3s811evb_init(MachineState *machine) -{ - const char *cpu_model = machine->cpu_model; - const char *kernel_filename = machine->kernel_filename; - stellaris_init(kernel_filename, cpu_model, &stellaris_boards[0]); -} - -static void lm3s6965evb_init(MachineState *machine) -{ - const char *cpu_model = machine->cpu_model; - const char *kernel_filename = machine->kernel_filename; - stellaris_init(kernel_filename, cpu_model, &stellaris_boards[1]); -} - -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 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) -{ - type_register_static(&lm3s811evb_type); - type_register_static(&lm3s6965evb_type); -} - -type_init(stellaris_machine_init) - -static void stellaris_i2c_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = stellaris_i2c_init; -} - -static const TypeInfo stellaris_i2c_info = { - .name = TYPE_STELLARIS_I2C, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(stellaris_i2c_state), - .class_init = stellaris_i2c_class_init, -}; - -static void stellaris_gptm_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = stellaris_gptm_init; -} - -static const TypeInfo stellaris_gptm_info = { - .name = TYPE_STELLARIS_GPTM, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(gptm_state), - .class_init = stellaris_gptm_class_init, -}; - -static void stellaris_adc_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = stellaris_adc_init; -} - -static const TypeInfo stellaris_adc_info = { - .name = TYPE_STELLARIS_ADC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(stellaris_adc_state), - .class_init = stellaris_adc_class_init, -}; - -static void stellaris_register_types(void) -{ - type_register_static(&stellaris_i2c_info); - type_register_static(&stellaris_gptm_info); - type_register_static(&stellaris_adc_info); -} - -type_init(stellaris_register_types) diff --git a/qemu/hw/arm/stm32f205_soc.c b/qemu/hw/arm/stm32f205_soc.c deleted file mode 100644 index a5ea1e237..000000000 --- a/qemu/hw/arm/stm32f205_soc.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * STM32F205 SoC - * - * Copyright (c) 2014 Alistair Francis - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/arm/arm.h" -#include "exec/address-spaces.h" -#include "hw/arm/stm32f205_soc.h" - -/* At the moment only Timer 2 to 5 are modelled */ -static const uint32_t timer_addr[STM_NUM_TIMERS] = { 0x40000000, 0x40000400, - 0x40000800, 0x40000C00 }; -static const uint32_t usart_addr[STM_NUM_USARTS] = { 0x40011000, 0x40004400, - 0x40004800, 0x40004C00, 0x40005000, 0x40011400 }; - -static const int timer_irq[STM_NUM_TIMERS] = {28, 29, 30, 50}; -static const int usart_irq[STM_NUM_USARTS] = {37, 38, 39, 52, 53, 71}; - -static void stm32f205_soc_initfn(Object *obj) -{ - STM32F205State *s = STM32F205_SOC(obj); - int i; - - object_initialize(&s->syscfg, sizeof(s->syscfg), TYPE_STM32F2XX_SYSCFG); - qdev_set_parent_bus(DEVICE(&s->syscfg), sysbus_get_default()); - - for (i = 0; i < STM_NUM_USARTS; i++) { - object_initialize(&s->usart[i], sizeof(s->usart[i]), - TYPE_STM32F2XX_USART); - qdev_set_parent_bus(DEVICE(&s->usart[i]), sysbus_get_default()); - } - - for (i = 0; i < STM_NUM_TIMERS; i++) { - object_initialize(&s->timer[i], sizeof(s->timer[i]), - TYPE_STM32F2XX_TIMER); - qdev_set_parent_bus(DEVICE(&s->timer[i]), sysbus_get_default()); - } -} - -static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) -{ - STM32F205State *s = STM32F205_SOC(dev_soc); - DeviceState *syscfgdev, *usartdev, *timerdev, *nvic; - SysBusDevice *syscfgbusdev, *usartbusdev, *timerbusdev; - Error *err = NULL; - int i; - - MemoryRegion *system_memory = get_system_memory(); - MemoryRegion *sram = g_new(MemoryRegion, 1); - MemoryRegion *flash = g_new(MemoryRegion, 1); - MemoryRegion *flash_alias = g_new(MemoryRegion, 1); - - memory_region_init_ram(flash, NULL, "STM32F205.flash", FLASH_SIZE, - &error_fatal); - memory_region_init_alias(flash_alias, NULL, "STM32F205.flash.alias", - flash, 0, FLASH_SIZE); - - vmstate_register_ram_global(flash); - - memory_region_set_readonly(flash, true); - memory_region_set_readonly(flash_alias, true); - - memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, flash); - memory_region_add_subregion(system_memory, 0, flash_alias); - - memory_region_init_ram(sram, NULL, "STM32F205.sram", SRAM_SIZE, - &error_fatal); - vmstate_register_ram_global(sram); - memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram); - - nvic = armv7m_init(get_system_memory(), FLASH_SIZE, 96, - s->kernel_filename, s->cpu_model); - - /* System configuration controller */ - syscfgdev = DEVICE(&s->syscfg); - object_property_set_bool(OBJECT(&s->syscfg), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - syscfgbusdev = SYS_BUS_DEVICE(syscfgdev); - sysbus_mmio_map(syscfgbusdev, 0, 0x40013800); - 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++) { - usartdev = DEVICE(&(s->usart[i])); - object_property_set_bool(OBJECT(&s->usart[i]), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - usartbusdev = SYS_BUS_DEVICE(usartdev); - sysbus_mmio_map(usartbusdev, 0, usart_addr[i]); - sysbus_connect_irq(usartbusdev, 0, - qdev_get_gpio_in(nvic, usart_irq[i])); - } - - /* Timer 2 to 5 */ - for (i = 0; i < STM_NUM_TIMERS; i++) { - timerdev = DEVICE(&(s->timer[i])); - qdev_prop_set_uint64(timerdev, "clock-frequency", 1000000000); - object_property_set_bool(OBJECT(&s->timer[i]), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - timerbusdev = SYS_BUS_DEVICE(timerdev); - sysbus_mmio_map(timerbusdev, 0, timer_addr[i]); - sysbus_connect_irq(timerbusdev, 0, - qdev_get_gpio_in(nvic, timer_irq[i])); - } -} - -static Property stm32f205_soc_properties[] = { - DEFINE_PROP_STRING("kernel-filename", STM32F205State, kernel_filename), - DEFINE_PROP_STRING("cpu-model", STM32F205State, cpu_model), - DEFINE_PROP_END_OF_LIST(), -}; - -static void stm32f205_soc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = stm32f205_soc_realize; - dc->props = stm32f205_soc_properties; -} - -static const TypeInfo stm32f205_soc_info = { - .name = TYPE_STM32F205_SOC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(STM32F205State), - .instance_init = stm32f205_soc_initfn, - .class_init = stm32f205_soc_class_init, -}; - -static void stm32f205_soc_types(void) -{ - type_register_static(&stm32f205_soc_info); -} - -type_init(stm32f205_soc_types) diff --git a/qemu/hw/arm/strongarm.c b/qemu/hw/arm/strongarm.c deleted file mode 100644 index 1eeb1ab39..000000000 --- a/qemu/hw/arm/strongarm.c +++ /dev/null @@ -1,1662 +0,0 @@ -/* - * StrongARM SA-1100/SA-1110 emulation - * - * Copyright (C) 2011 Dmitry Eremin-Solenikov - * - * Largely based on StrongARM emulation: - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * UART code based on QEMU 16550A UART emulation - * Copyright (c) 2003-2004 Fabrice Bellard - * Copyright (c) 2008 Citrix Systems, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - * 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 "cpu.h" -#include "hw/boards.h" -#include "hw/sysbus.h" -#include "strongarm.h" -#include "qemu/error-report.h" -#include "hw/arm/arm.h" -#include "sysemu/char.h" -#include "sysemu/sysemu.h" -#include "hw/ssi/ssi.h" -#include "qemu/cutils.h" - -//#define DEBUG - -/* - TODO - - Implement cp15, c14 ? - - Implement cp15, c15 !!! (idle used in L) - - Implement idle mode handling/DIM - - Implement sleep mode/Wake sources - - Implement reset control - - Implement memory control regs - - PCMCIA handling - - Maybe support MBGNT/MBREQ - - DMA channels - - GPCLK - - IrDA - - MCP - - Enhance UART with modem signals - */ - -#ifdef DEBUG -# define DPRINTF(format, ...) printf(format , ## __VA_ARGS__) -#else -# define DPRINTF(format, ...) do { } while (0) -#endif - -static struct { - hwaddr io_base; - int irq; -} sa_serial[] = { - { 0x80010000, SA_PIC_UART1 }, - { 0x80030000, SA_PIC_UART2 }, - { 0x80050000, SA_PIC_UART3 }, - { 0, 0 } -}; - -/* Interrupt Controller */ - -#define TYPE_STRONGARM_PIC "strongarm_pic" -#define STRONGARM_PIC(obj) \ - OBJECT_CHECK(StrongARMPICState, (obj), TYPE_STRONGARM_PIC) - -typedef struct StrongARMPICState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq irq; - qemu_irq fiq; - - uint32_t pending; - uint32_t enabled; - uint32_t is_fiq; - uint32_t int_idle; -} StrongARMPICState; - -#define ICIP 0x00 -#define ICMR 0x04 -#define ICLR 0x08 -#define ICFP 0x10 -#define ICPR 0x20 -#define ICCR 0x0c - -#define SA_PIC_SRCS 32 - - -static void strongarm_pic_update(void *opaque) -{ - StrongARMPICState *s = opaque; - - /* FIXME: reflect DIM */ - qemu_set_irq(s->fiq, s->pending & s->enabled & s->is_fiq); - qemu_set_irq(s->irq, s->pending & s->enabled & ~s->is_fiq); -} - -static void strongarm_pic_set_irq(void *opaque, int irq, int level) -{ - StrongARMPICState *s = opaque; - - if (level) { - s->pending |= 1 << irq; - } else { - s->pending &= ~(1 << irq); - } - - strongarm_pic_update(s); -} - -static uint64_t strongarm_pic_mem_read(void *opaque, hwaddr offset, - unsigned size) -{ - StrongARMPICState *s = opaque; - - switch (offset) { - case ICIP: - return s->pending & ~s->is_fiq & s->enabled; - case ICMR: - return s->enabled; - case ICLR: - return s->is_fiq; - case ICCR: - return s->int_idle == 0; - case ICFP: - return s->pending & s->is_fiq & s->enabled; - case ICPR: - return s->pending; - default: - printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n", - __func__, offset); - return 0; - } -} - -static void strongarm_pic_mem_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - StrongARMPICState *s = opaque; - - switch (offset) { - case ICMR: - s->enabled = value; - break; - case ICLR: - s->is_fiq = value; - break; - case ICCR: - s->int_idle = (value & 1) ? 0 : ~0; - break; - default: - printf("%s: Bad register offset 0x" TARGET_FMT_plx "\n", - __func__, offset); - break; - } - strongarm_pic_update(s); -} - -static const MemoryRegionOps strongarm_pic_ops = { - .read = strongarm_pic_mem_read, - .write = strongarm_pic_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int strongarm_pic_initfn(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - StrongARMPICState *s = STRONGARM_PIC(dev); - - qdev_init_gpio_in(dev, strongarm_pic_set_irq, SA_PIC_SRCS); - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_pic_ops, s, - "pic", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - sysbus_init_irq(sbd, &s->fiq); - - return 0; -} - -static int strongarm_pic_post_load(void *opaque, int version_id) -{ - strongarm_pic_update(opaque); - return 0; -} - -static VMStateDescription vmstate_strongarm_pic_regs = { - .name = "strongarm_pic", - .version_id = 0, - .minimum_version_id = 0, - .post_load = strongarm_pic_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(pending, StrongARMPICState), - VMSTATE_UINT32(enabled, StrongARMPICState), - VMSTATE_UINT32(is_fiq, StrongARMPICState), - VMSTATE_UINT32(int_idle, StrongARMPICState), - VMSTATE_END_OF_LIST(), - }, -}; - -static void strongarm_pic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = strongarm_pic_initfn; - dc->desc = "StrongARM PIC"; - dc->vmsd = &vmstate_strongarm_pic_regs; -} - -static const TypeInfo strongarm_pic_info = { - .name = TYPE_STRONGARM_PIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(StrongARMPICState), - .class_init = strongarm_pic_class_init, -}; - -/* Real-Time Clock */ -#define RTAR 0x00 /* RTC Alarm register */ -#define RCNR 0x04 /* RTC Counter register */ -#define RTTR 0x08 /* RTC Timer Trim register */ -#define RTSR 0x10 /* RTC Status register */ - -#define RTSR_AL (1 << 0) /* RTC Alarm detected */ -#define RTSR_HZ (1 << 1) /* RTC 1Hz detected */ -#define RTSR_ALE (1 << 2) /* RTC Alarm enable */ -#define RTSR_HZE (1 << 3) /* RTC 1Hz enable */ - -/* 16 LSB of RTTR are clockdiv for internal trim logic, - * trim delete isn't emulated, so - * f = 32 768 / (RTTR_trim + 1) */ - -#define TYPE_STRONGARM_RTC "strongarm-rtc" -#define STRONGARM_RTC(obj) \ - OBJECT_CHECK(StrongARMRTCState, (obj), TYPE_STRONGARM_RTC) - -typedef struct StrongARMRTCState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t rttr; - uint32_t rtsr; - uint32_t rtar; - uint32_t last_rcnr; - int64_t last_hz; - QEMUTimer *rtc_alarm; - QEMUTimer *rtc_hz; - qemu_irq rtc_irq; - qemu_irq rtc_hz_irq; -} StrongARMRTCState; - -static inline void strongarm_rtc_int_update(StrongARMRTCState *s) -{ - qemu_set_irq(s->rtc_irq, s->rtsr & RTSR_AL); - qemu_set_irq(s->rtc_hz_irq, s->rtsr & RTSR_HZ); -} - -static void strongarm_rtc_hzupdate(StrongARMRTCState *s) -{ - int64_t rt = qemu_clock_get_ms(rtc_clock); - s->last_rcnr += ((rt - s->last_hz) << 15) / - (1000 * ((s->rttr & 0xffff) + 1)); - s->last_hz = rt; -} - -static inline void strongarm_rtc_timer_update(StrongARMRTCState *s) -{ - if ((s->rtsr & RTSR_HZE) && !(s->rtsr & RTSR_HZ)) { - timer_mod(s->rtc_hz, s->last_hz + 1000); - } else { - timer_del(s->rtc_hz); - } - - if ((s->rtsr & RTSR_ALE) && !(s->rtsr & RTSR_AL)) { - timer_mod(s->rtc_alarm, s->last_hz + - (((s->rtar - s->last_rcnr) * 1000 * - ((s->rttr & 0xffff) + 1)) >> 15)); - } else { - timer_del(s->rtc_alarm); - } -} - -static inline void strongarm_rtc_alarm_tick(void *opaque) -{ - StrongARMRTCState *s = opaque; - s->rtsr |= RTSR_AL; - strongarm_rtc_timer_update(s); - strongarm_rtc_int_update(s); -} - -static inline void strongarm_rtc_hz_tick(void *opaque) -{ - StrongARMRTCState *s = opaque; - s->rtsr |= RTSR_HZ; - strongarm_rtc_timer_update(s); - strongarm_rtc_int_update(s); -} - -static uint64_t strongarm_rtc_read(void *opaque, hwaddr addr, - unsigned size) -{ - StrongARMRTCState *s = opaque; - - switch (addr) { - case RTTR: - return s->rttr; - case RTSR: - return s->rtsr; - case RTAR: - return s->rtar; - case RCNR: - return s->last_rcnr + - ((qemu_clock_get_ms(rtc_clock) - s->last_hz) << 15) / - (1000 * ((s->rttr & 0xffff) + 1)); - default: - printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); - return 0; - } -} - -static void strongarm_rtc_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - StrongARMRTCState *s = opaque; - uint32_t old_rtsr; - - switch (addr) { - case RTTR: - strongarm_rtc_hzupdate(s); - s->rttr = value; - strongarm_rtc_timer_update(s); - break; - - case RTSR: - old_rtsr = s->rtsr; - s->rtsr = (value & (RTSR_ALE | RTSR_HZE)) | - (s->rtsr & ~(value & (RTSR_AL | RTSR_HZ))); - - if (s->rtsr != old_rtsr) { - strongarm_rtc_timer_update(s); - } - - strongarm_rtc_int_update(s); - break; - - case RTAR: - s->rtar = value; - strongarm_rtc_timer_update(s); - break; - - case RCNR: - strongarm_rtc_hzupdate(s); - s->last_rcnr = value; - strongarm_rtc_timer_update(s); - break; - - default: - printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); - } -} - -static const MemoryRegionOps strongarm_rtc_ops = { - .read = strongarm_rtc_read, - .write = strongarm_rtc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int strongarm_rtc_init(SysBusDevice *dev) -{ - StrongARMRTCState *s = STRONGARM_RTC(dev); - struct tm tm; - - s->rttr = 0x0; - s->rtsr = 0; - - qemu_get_timedate(&tm, 0); - - s->last_rcnr = (uint32_t) mktimegm(&tm); - s->last_hz = qemu_clock_get_ms(rtc_clock); - - s->rtc_alarm = timer_new_ms(rtc_clock, strongarm_rtc_alarm_tick, s); - s->rtc_hz = timer_new_ms(rtc_clock, strongarm_rtc_hz_tick, s); - - sysbus_init_irq(dev, &s->rtc_irq); - sysbus_init_irq(dev, &s->rtc_hz_irq); - - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_rtc_ops, s, - "rtc", 0x10000); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static void strongarm_rtc_pre_save(void *opaque) -{ - StrongARMRTCState *s = opaque; - - strongarm_rtc_hzupdate(s); -} - -static int strongarm_rtc_post_load(void *opaque, int version_id) -{ - StrongARMRTCState *s = opaque; - - strongarm_rtc_timer_update(s); - strongarm_rtc_int_update(s); - - return 0; -} - -static const VMStateDescription vmstate_strongarm_rtc_regs = { - .name = "strongarm-rtc", - .version_id = 0, - .minimum_version_id = 0, - .pre_save = strongarm_rtc_pre_save, - .post_load = strongarm_rtc_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(rttr, StrongARMRTCState), - VMSTATE_UINT32(rtsr, StrongARMRTCState), - VMSTATE_UINT32(rtar, StrongARMRTCState), - VMSTATE_UINT32(last_rcnr, StrongARMRTCState), - VMSTATE_INT64(last_hz, StrongARMRTCState), - VMSTATE_END_OF_LIST(), - }, -}; - -static void strongarm_rtc_sysbus_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = strongarm_rtc_init; - dc->desc = "StrongARM RTC Controller"; - dc->vmsd = &vmstate_strongarm_rtc_regs; -} - -static const TypeInfo strongarm_rtc_sysbus_info = { - .name = TYPE_STRONGARM_RTC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(StrongARMRTCState), - .class_init = strongarm_rtc_sysbus_class_init, -}; - -/* GPIO */ -#define GPLR 0x00 -#define GPDR 0x04 -#define GPSR 0x08 -#define GPCR 0x0c -#define GRER 0x10 -#define GFER 0x14 -#define GEDR 0x18 -#define GAFR 0x1c - -#define TYPE_STRONGARM_GPIO "strongarm-gpio" -#define STRONGARM_GPIO(obj) \ - OBJECT_CHECK(StrongARMGPIOInfo, (obj), TYPE_STRONGARM_GPIO) - -typedef struct StrongARMGPIOInfo StrongARMGPIOInfo; -struct StrongARMGPIOInfo { - SysBusDevice busdev; - MemoryRegion iomem; - qemu_irq handler[28]; - qemu_irq irqs[11]; - qemu_irq irqX; - - uint32_t ilevel; - uint32_t olevel; - uint32_t dir; - uint32_t rising; - uint32_t falling; - uint32_t status; - uint32_t gafr; - - uint32_t prev_level; -}; - - -static void strongarm_gpio_irq_update(StrongARMGPIOInfo *s) -{ - int i; - for (i = 0; i < 11; i++) { - qemu_set_irq(s->irqs[i], s->status & (1 << i)); - } - - qemu_set_irq(s->irqX, (s->status & ~0x7ff)); -} - -static void strongarm_gpio_set(void *opaque, int line, int level) -{ - StrongARMGPIOInfo *s = opaque; - uint32_t mask; - - mask = 1 << line; - - if (level) { - s->status |= s->rising & mask & - ~s->ilevel & ~s->dir; - s->ilevel |= mask; - } else { - s->status |= s->falling & mask & - s->ilevel & ~s->dir; - s->ilevel &= ~mask; - } - - if (s->status & mask) { - strongarm_gpio_irq_update(s); - } -} - -static void strongarm_gpio_handler_update(StrongARMGPIOInfo *s) -{ - uint32_t level, diff; - int bit; - - level = s->olevel & s->dir; - - for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) { - bit = ctz32(diff); - qemu_set_irq(s->handler[bit], (level >> bit) & 1); - } - - s->prev_level = level; -} - -static uint64_t strongarm_gpio_read(void *opaque, hwaddr offset, - unsigned size) -{ - StrongARMGPIOInfo *s = opaque; - - switch (offset) { - case GPDR: /* GPIO Pin-Direction registers */ - return s->dir; - - case GPSR: /* GPIO Pin-Output Set registers */ - qemu_log_mask(LOG_GUEST_ERROR, - "strongarm GPIO: read from write only register GPSR\n"); - return 0; - - case GPCR: /* GPIO Pin-Output Clear registers */ - qemu_log_mask(LOG_GUEST_ERROR, - "strongarm GPIO: read from write only register GPCR\n"); - return 0; - - case GRER: /* GPIO Rising-Edge Detect Enable registers */ - return s->rising; - - case GFER: /* GPIO Falling-Edge Detect Enable registers */ - return s->falling; - - case GAFR: /* GPIO Alternate Function registers */ - return s->gafr; - - case GPLR: /* GPIO Pin-Level registers */ - return (s->olevel & s->dir) | - (s->ilevel & ~s->dir); - - case GEDR: /* GPIO Edge Detect Status registers */ - return s->status; - - default: - printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset); - } - - return 0; -} - -static void strongarm_gpio_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - StrongARMGPIOInfo *s = opaque; - - switch (offset) { - case GPDR: /* GPIO Pin-Direction registers */ - s->dir = value; - strongarm_gpio_handler_update(s); - break; - - case GPSR: /* GPIO Pin-Output Set registers */ - s->olevel |= value; - strongarm_gpio_handler_update(s); - break; - - case GPCR: /* GPIO Pin-Output Clear registers */ - s->olevel &= ~value; - strongarm_gpio_handler_update(s); - break; - - case GRER: /* GPIO Rising-Edge Detect Enable registers */ - s->rising = value; - break; - - case GFER: /* GPIO Falling-Edge Detect Enable registers */ - s->falling = value; - break; - - case GAFR: /* GPIO Alternate Function registers */ - s->gafr = value; - break; - - case GEDR: /* GPIO Edge Detect Status registers */ - s->status &= ~value; - strongarm_gpio_irq_update(s); - break; - - default: - printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset); - } -} - -static const MemoryRegionOps strongarm_gpio_ops = { - .read = strongarm_gpio_read, - .write = strongarm_gpio_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static DeviceState *strongarm_gpio_init(hwaddr base, - DeviceState *pic) -{ - DeviceState *dev; - int i; - - dev = qdev_create(NULL, TYPE_STRONGARM_GPIO); - qdev_init_nofail(dev); - - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - for (i = 0; i < 12; i++) - sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, - qdev_get_gpio_in(pic, SA_PIC_GPIO0_EDGE + i)); - - return dev; -} - -static int strongarm_gpio_initfn(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - StrongARMGPIOInfo *s = STRONGARM_GPIO(dev); - int i; - - qdev_init_gpio_in(dev, strongarm_gpio_set, 28); - qdev_init_gpio_out(dev, s->handler, 28); - - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_gpio_ops, s, - "gpio", 0x1000); - - sysbus_init_mmio(sbd, &s->iomem); - for (i = 0; i < 11; i++) { - sysbus_init_irq(sbd, &s->irqs[i]); - } - sysbus_init_irq(sbd, &s->irqX); - - return 0; -} - -static const VMStateDescription vmstate_strongarm_gpio_regs = { - .name = "strongarm-gpio", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(ilevel, StrongARMGPIOInfo), - VMSTATE_UINT32(olevel, StrongARMGPIOInfo), - VMSTATE_UINT32(dir, StrongARMGPIOInfo), - VMSTATE_UINT32(rising, StrongARMGPIOInfo), - VMSTATE_UINT32(falling, StrongARMGPIOInfo), - VMSTATE_UINT32(status, StrongARMGPIOInfo), - VMSTATE_UINT32(gafr, StrongARMGPIOInfo), - VMSTATE_UINT32(prev_level, StrongARMGPIOInfo), - VMSTATE_END_OF_LIST(), - }, -}; - -static void strongarm_gpio_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = strongarm_gpio_initfn; - dc->desc = "StrongARM GPIO controller"; - dc->vmsd = &vmstate_strongarm_gpio_regs; -} - -static const TypeInfo strongarm_gpio_info = { - .name = TYPE_STRONGARM_GPIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(StrongARMGPIOInfo), - .class_init = strongarm_gpio_class_init, -}; - -/* Peripheral Pin Controller */ -#define PPDR 0x00 -#define PPSR 0x04 -#define PPAR 0x08 -#define PSDR 0x0c -#define PPFR 0x10 - -#define TYPE_STRONGARM_PPC "strongarm-ppc" -#define STRONGARM_PPC(obj) \ - OBJECT_CHECK(StrongARMPPCInfo, (obj), TYPE_STRONGARM_PPC) - -typedef struct StrongARMPPCInfo StrongARMPPCInfo; -struct StrongARMPPCInfo { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq handler[28]; - - uint32_t ilevel; - uint32_t olevel; - uint32_t dir; - uint32_t ppar; - uint32_t psdr; - uint32_t ppfr; - - uint32_t prev_level; -}; - -static void strongarm_ppc_set(void *opaque, int line, int level) -{ - StrongARMPPCInfo *s = opaque; - - if (level) { - s->ilevel |= 1 << line; - } else { - s->ilevel &= ~(1 << line); - } -} - -static void strongarm_ppc_handler_update(StrongARMPPCInfo *s) -{ - uint32_t level, diff; - int bit; - - level = s->olevel & s->dir; - - for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) { - bit = ctz32(diff); - qemu_set_irq(s->handler[bit], (level >> bit) & 1); - } - - s->prev_level = level; -} - -static uint64_t strongarm_ppc_read(void *opaque, hwaddr offset, - unsigned size) -{ - StrongARMPPCInfo *s = opaque; - - switch (offset) { - case PPDR: /* PPC Pin Direction registers */ - return s->dir | ~0x3fffff; - - case PPSR: /* PPC Pin State registers */ - return (s->olevel & s->dir) | - (s->ilevel & ~s->dir) | - ~0x3fffff; - - case PPAR: - return s->ppar | ~0x41000; - - case PSDR: - return s->psdr; - - case PPFR: - return s->ppfr | ~0x7f001; - - default: - printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset); - } - - return 0; -} - -static void strongarm_ppc_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - StrongARMPPCInfo *s = opaque; - - switch (offset) { - case PPDR: /* PPC Pin Direction registers */ - s->dir = value & 0x3fffff; - strongarm_ppc_handler_update(s); - break; - - case PPSR: /* PPC Pin State registers */ - s->olevel = value & s->dir & 0x3fffff; - strongarm_ppc_handler_update(s); - break; - - case PPAR: - s->ppar = value & 0x41000; - break; - - case PSDR: - s->psdr = value & 0x3fffff; - break; - - case PPFR: - s->ppfr = value & 0x7f001; - break; - - default: - printf("%s: Bad offset 0x" TARGET_FMT_plx "\n", __func__, offset); - } -} - -static const MemoryRegionOps strongarm_ppc_ops = { - .read = strongarm_ppc_read, - .write = strongarm_ppc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int strongarm_ppc_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - StrongARMPPCInfo *s = STRONGARM_PPC(dev); - - qdev_init_gpio_in(dev, strongarm_ppc_set, 22); - qdev_init_gpio_out(dev, s->handler, 22); - - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_ppc_ops, s, - "ppc", 0x1000); - - sysbus_init_mmio(sbd, &s->iomem); - - return 0; -} - -static const VMStateDescription vmstate_strongarm_ppc_regs = { - .name = "strongarm-ppc", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(ilevel, StrongARMPPCInfo), - VMSTATE_UINT32(olevel, StrongARMPPCInfo), - VMSTATE_UINT32(dir, StrongARMPPCInfo), - VMSTATE_UINT32(ppar, StrongARMPPCInfo), - VMSTATE_UINT32(psdr, StrongARMPPCInfo), - VMSTATE_UINT32(ppfr, StrongARMPPCInfo), - VMSTATE_UINT32(prev_level, StrongARMPPCInfo), - VMSTATE_END_OF_LIST(), - }, -}; - -static void strongarm_ppc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = strongarm_ppc_init; - dc->desc = "StrongARM PPC controller"; - dc->vmsd = &vmstate_strongarm_ppc_regs; -} - -static const TypeInfo strongarm_ppc_info = { - .name = TYPE_STRONGARM_PPC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(StrongARMPPCInfo), - .class_init = strongarm_ppc_class_init, -}; - -/* UART Ports */ -#define UTCR0 0x00 -#define UTCR1 0x04 -#define UTCR2 0x08 -#define UTCR3 0x0c -#define UTDR 0x14 -#define UTSR0 0x1c -#define UTSR1 0x20 - -#define UTCR0_PE (1 << 0) /* Parity enable */ -#define UTCR0_OES (1 << 1) /* Even parity */ -#define UTCR0_SBS (1 << 2) /* 2 stop bits */ -#define UTCR0_DSS (1 << 3) /* 8-bit data */ - -#define UTCR3_RXE (1 << 0) /* Rx enable */ -#define UTCR3_TXE (1 << 1) /* Tx enable */ -#define UTCR3_BRK (1 << 2) /* Force Break */ -#define UTCR3_RIE (1 << 3) /* Rx int enable */ -#define UTCR3_TIE (1 << 4) /* Tx int enable */ -#define UTCR3_LBM (1 << 5) /* Loopback */ - -#define UTSR0_TFS (1 << 0) /* Tx FIFO nearly empty */ -#define UTSR0_RFS (1 << 1) /* Rx FIFO nearly full */ -#define UTSR0_RID (1 << 2) /* Receiver Idle */ -#define UTSR0_RBB (1 << 3) /* Receiver begin break */ -#define UTSR0_REB (1 << 4) /* Receiver end break */ -#define UTSR0_EIF (1 << 5) /* Error in FIFO */ - -#define UTSR1_RNE (1 << 1) /* Receive FIFO not empty */ -#define UTSR1_TNF (1 << 2) /* Transmit FIFO not full */ -#define UTSR1_PRE (1 << 3) /* Parity error */ -#define UTSR1_FRE (1 << 4) /* Frame error */ -#define UTSR1_ROR (1 << 5) /* Receive Over Run */ - -#define RX_FIFO_PRE (1 << 8) -#define RX_FIFO_FRE (1 << 9) -#define RX_FIFO_ROR (1 << 10) - -#define TYPE_STRONGARM_UART "strongarm-uart" -#define STRONGARM_UART(obj) \ - OBJECT_CHECK(StrongARMUARTState, (obj), TYPE_STRONGARM_UART) - -typedef struct StrongARMUARTState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - CharDriverState *chr; - qemu_irq irq; - - uint8_t utcr0; - uint16_t brd; - uint8_t utcr3; - uint8_t utsr0; - uint8_t utsr1; - - uint8_t tx_fifo[8]; - uint8_t tx_start; - uint8_t tx_len; - uint16_t rx_fifo[12]; /* value + error flags in high bits */ - uint8_t rx_start; - uint8_t rx_len; - - uint64_t char_transmit_time; /* time to transmit a char in ticks*/ - bool wait_break_end; - QEMUTimer *rx_timeout_timer; - QEMUTimer *tx_timer; -} StrongARMUARTState; - -static void strongarm_uart_update_status(StrongARMUARTState *s) -{ - uint16_t utsr1 = 0; - - if (s->tx_len != 8) { - utsr1 |= UTSR1_TNF; - } - - if (s->rx_len != 0) { - uint16_t ent = s->rx_fifo[s->rx_start]; - - utsr1 |= UTSR1_RNE; - if (ent & RX_FIFO_PRE) { - s->utsr1 |= UTSR1_PRE; - } - if (ent & RX_FIFO_FRE) { - s->utsr1 |= UTSR1_FRE; - } - if (ent & RX_FIFO_ROR) { - s->utsr1 |= UTSR1_ROR; - } - } - - s->utsr1 = utsr1; -} - -static void strongarm_uart_update_int_status(StrongARMUARTState *s) -{ - uint16_t utsr0 = s->utsr0 & - (UTSR0_REB | UTSR0_RBB | UTSR0_RID); - int i; - - if ((s->utcr3 & UTCR3_TXE) && - (s->utcr3 & UTCR3_TIE) && - s->tx_len <= 4) { - utsr0 |= UTSR0_TFS; - } - - if ((s->utcr3 & UTCR3_RXE) && - (s->utcr3 & UTCR3_RIE) && - s->rx_len > 4) { - utsr0 |= UTSR0_RFS; - } - - for (i = 0; i < s->rx_len && i < 4; i++) - if (s->rx_fifo[(s->rx_start + i) % 12] & ~0xff) { - utsr0 |= UTSR0_EIF; - break; - } - - s->utsr0 = utsr0; - qemu_set_irq(s->irq, utsr0); -} - -static void strongarm_uart_update_parameters(StrongARMUARTState *s) -{ - int speed, parity, data_bits, stop_bits, frame_size; - QEMUSerialSetParams ssp; - - /* Start bit. */ - frame_size = 1; - if (s->utcr0 & UTCR0_PE) { - /* Parity bit. */ - frame_size++; - if (s->utcr0 & UTCR0_OES) { - parity = 'E'; - } else { - parity = 'O'; - } - } else { - parity = 'N'; - } - if (s->utcr0 & UTCR0_SBS) { - stop_bits = 2; - } else { - stop_bits = 1; - } - - data_bits = (s->utcr0 & UTCR0_DSS) ? 8 : 7; - frame_size += data_bits + stop_bits; - speed = 3686400 / 16 / (s->brd + 1); - ssp.speed = speed; - ssp.parity = parity; - ssp.data_bits = data_bits; - ssp.stop_bits = stop_bits; - 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); - } - - DPRINTF(stderr, "%s speed=%d parity=%c data=%d stop=%d\n", s->chr->label, - speed, parity, data_bits, stop_bits); -} - -static void strongarm_uart_rx_to(void *opaque) -{ - StrongARMUARTState *s = opaque; - - if (s->rx_len) { - s->utsr0 |= UTSR0_RID; - strongarm_uart_update_int_status(s); - } -} - -static void strongarm_uart_rx_push(StrongARMUARTState *s, uint16_t c) -{ - if ((s->utcr3 & UTCR3_RXE) == 0) { - /* rx disabled */ - return; - } - - if (s->wait_break_end) { - s->utsr0 |= UTSR0_REB; - s->wait_break_end = false; - } - - if (s->rx_len < 12) { - s->rx_fifo[(s->rx_start + s->rx_len) % 12] = c; - s->rx_len++; - } else - s->rx_fifo[(s->rx_start + 11) % 12] |= RX_FIFO_ROR; -} - -static int strongarm_uart_can_receive(void *opaque) -{ - StrongARMUARTState *s = opaque; - - if (s->rx_len == 12) { - return 0; - } - /* It's best not to get more than 2/3 of RX FIFO, so advertise that much */ - if (s->rx_len < 8) { - return 8 - s->rx_len; - } - return 1; -} - -static void strongarm_uart_receive(void *opaque, const uint8_t *buf, int size) -{ - StrongARMUARTState *s = opaque; - int i; - - for (i = 0; i < size; i++) { - strongarm_uart_rx_push(s, buf[i]); - } - - /* call the timeout receive callback in 3 char transmit time */ - timer_mod(s->rx_timeout_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->char_transmit_time * 3); - - strongarm_uart_update_status(s); - strongarm_uart_update_int_status(s); -} - -static void strongarm_uart_event(void *opaque, int event) -{ - StrongARMUARTState *s = opaque; - if (event == CHR_EVENT_BREAK) { - s->utsr0 |= UTSR0_RBB; - strongarm_uart_rx_push(s, RX_FIFO_FRE); - s->wait_break_end = true; - strongarm_uart_update_status(s); - strongarm_uart_update_int_status(s); - } -} - -static void strongarm_uart_tx(void *opaque) -{ - StrongARMUARTState *s = opaque; - uint64_t new_xmit_ts = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - if (s->utcr3 & UTCR3_LBM) /* loopback */ { - strongarm_uart_receive(s, &s->tx_fifo[s->tx_start], 1); - } else if (s->chr) { - qemu_chr_fe_write(s->chr, &s->tx_fifo[s->tx_start], 1); - } - - s->tx_start = (s->tx_start + 1) % 8; - s->tx_len--; - if (s->tx_len) { - timer_mod(s->tx_timer, new_xmit_ts + s->char_transmit_time); - } - strongarm_uart_update_status(s); - strongarm_uart_update_int_status(s); -} - -static uint64_t strongarm_uart_read(void *opaque, hwaddr addr, - unsigned size) -{ - StrongARMUARTState *s = opaque; - uint16_t ret; - - switch (addr) { - case UTCR0: - return s->utcr0; - - case UTCR1: - return s->brd >> 8; - - case UTCR2: - return s->brd & 0xff; - - case UTCR3: - return s->utcr3; - - case UTDR: - if (s->rx_len != 0) { - ret = s->rx_fifo[s->rx_start]; - s->rx_start = (s->rx_start + 1) % 12; - s->rx_len--; - strongarm_uart_update_status(s); - strongarm_uart_update_int_status(s); - return ret; - } - return 0; - - case UTSR0: - return s->utsr0; - - case UTSR1: - return s->utsr1; - - default: - printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); - return 0; - } -} - -static void strongarm_uart_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - StrongARMUARTState *s = opaque; - - switch (addr) { - case UTCR0: - s->utcr0 = value & 0x7f; - strongarm_uart_update_parameters(s); - break; - - case UTCR1: - s->brd = (s->brd & 0xff) | ((value & 0xf) << 8); - strongarm_uart_update_parameters(s); - break; - - case UTCR2: - s->brd = (s->brd & 0xf00) | (value & 0xff); - strongarm_uart_update_parameters(s); - break; - - case UTCR3: - s->utcr3 = value & 0x3f; - if ((s->utcr3 & UTCR3_RXE) == 0) { - s->rx_len = 0; - } - if ((s->utcr3 & UTCR3_TXE) == 0) { - s->tx_len = 0; - } - strongarm_uart_update_status(s); - strongarm_uart_update_int_status(s); - break; - - case UTDR: - if ((s->utcr3 & UTCR3_TXE) && s->tx_len != 8) { - s->tx_fifo[(s->tx_start + s->tx_len) % 8] = value; - s->tx_len++; - strongarm_uart_update_status(s); - strongarm_uart_update_int_status(s); - if (s->tx_len == 1) { - strongarm_uart_tx(s); - } - } - break; - - case UTSR0: - s->utsr0 = s->utsr0 & ~(value & - (UTSR0_REB | UTSR0_RBB | UTSR0_RID)); - strongarm_uart_update_int_status(s); - break; - - default: - printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); - } -} - -static const MemoryRegionOps strongarm_uart_ops = { - .read = strongarm_uart_read, - .write = strongarm_uart_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int strongarm_uart_init(SysBusDevice *dev) -{ - StrongARMUARTState *s = STRONGARM_UART(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_uart_ops, s, - "uart", 0x10000); - sysbus_init_mmio(dev, &s->iomem); - sysbus_init_irq(dev, &s->irq); - - s->rx_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_rx_to, s); - s->tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, strongarm_uart_tx, s); - - if (s->chr) { - qemu_chr_add_handlers(s->chr, - strongarm_uart_can_receive, - strongarm_uart_receive, - strongarm_uart_event, - s); - } - - return 0; -} - -static void strongarm_uart_reset(DeviceState *dev) -{ - StrongARMUARTState *s = STRONGARM_UART(dev); - - s->utcr0 = UTCR0_DSS; /* 8 data, no parity */ - s->brd = 23; /* 9600 */ - /* enable send & recv - this actually violates spec */ - s->utcr3 = UTCR3_TXE | UTCR3_RXE; - - s->rx_len = s->tx_len = 0; - - strongarm_uart_update_parameters(s); - strongarm_uart_update_status(s); - strongarm_uart_update_int_status(s); -} - -static int strongarm_uart_post_load(void *opaque, int version_id) -{ - StrongARMUARTState *s = opaque; - - strongarm_uart_update_parameters(s); - strongarm_uart_update_status(s); - strongarm_uart_update_int_status(s); - - /* tx and restart timer */ - if (s->tx_len) { - strongarm_uart_tx(s); - } - - /* restart rx timeout timer */ - if (s->rx_len) { - timer_mod(s->rx_timeout_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->char_transmit_time * 3); - } - - return 0; -} - -static const VMStateDescription vmstate_strongarm_uart_regs = { - .name = "strongarm-uart", - .version_id = 0, - .minimum_version_id = 0, - .post_load = strongarm_uart_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(utcr0, StrongARMUARTState), - VMSTATE_UINT16(brd, StrongARMUARTState), - VMSTATE_UINT8(utcr3, StrongARMUARTState), - VMSTATE_UINT8(utsr0, StrongARMUARTState), - VMSTATE_UINT8_ARRAY(tx_fifo, StrongARMUARTState, 8), - VMSTATE_UINT8(tx_start, StrongARMUARTState), - VMSTATE_UINT8(tx_len, StrongARMUARTState), - VMSTATE_UINT16_ARRAY(rx_fifo, StrongARMUARTState, 12), - VMSTATE_UINT8(rx_start, StrongARMUARTState), - VMSTATE_UINT8(rx_len, StrongARMUARTState), - VMSTATE_BOOL(wait_break_end, StrongARMUARTState), - VMSTATE_END_OF_LIST(), - }, -}; - -static Property strongarm_uart_properties[] = { - DEFINE_PROP_CHR("chardev", StrongARMUARTState, chr), - DEFINE_PROP_END_OF_LIST(), -}; - -static void strongarm_uart_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = strongarm_uart_init; - dc->desc = "StrongARM UART controller"; - dc->reset = strongarm_uart_reset; - dc->vmsd = &vmstate_strongarm_uart_regs; - dc->props = strongarm_uart_properties; -} - -static const TypeInfo strongarm_uart_info = { - .name = TYPE_STRONGARM_UART, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(StrongARMUARTState), - .class_init = strongarm_uart_class_init, -}; - -/* Synchronous Serial Ports */ - -#define TYPE_STRONGARM_SSP "strongarm-ssp" -#define STRONGARM_SSP(obj) \ - OBJECT_CHECK(StrongARMSSPState, (obj), TYPE_STRONGARM_SSP) - -typedef struct StrongARMSSPState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq irq; - SSIBus *bus; - - uint16_t sscr[2]; - uint16_t sssr; - - uint16_t rx_fifo[8]; - uint8_t rx_level; - uint8_t rx_start; -} StrongARMSSPState; - -#define SSCR0 0x60 /* SSP Control register 0 */ -#define SSCR1 0x64 /* SSP Control register 1 */ -#define SSDR 0x6c /* SSP Data register */ -#define SSSR 0x74 /* SSP Status register */ - -/* Bitfields for above registers */ -#define SSCR0_SPI(x) (((x) & 0x30) == 0x00) -#define SSCR0_SSP(x) (((x) & 0x30) == 0x10) -#define SSCR0_UWIRE(x) (((x) & 0x30) == 0x20) -#define SSCR0_PSP(x) (((x) & 0x30) == 0x30) -#define SSCR0_SSE (1 << 7) -#define SSCR0_DSS(x) (((x) & 0xf) + 1) -#define SSCR1_RIE (1 << 0) -#define SSCR1_TIE (1 << 1) -#define SSCR1_LBM (1 << 2) -#define SSSR_TNF (1 << 2) -#define SSSR_RNE (1 << 3) -#define SSSR_TFS (1 << 5) -#define SSSR_RFS (1 << 6) -#define SSSR_ROR (1 << 7) -#define SSSR_RW 0x0080 - -static void strongarm_ssp_int_update(StrongARMSSPState *s) -{ - int level = 0; - - level |= (s->sssr & SSSR_ROR); - level |= (s->sssr & SSSR_RFS) && (s->sscr[1] & SSCR1_RIE); - level |= (s->sssr & SSSR_TFS) && (s->sscr[1] & SSCR1_TIE); - qemu_set_irq(s->irq, level); -} - -static void strongarm_ssp_fifo_update(StrongARMSSPState *s) -{ - s->sssr &= ~SSSR_TFS; - s->sssr &= ~SSSR_TNF; - if (s->sscr[0] & SSCR0_SSE) { - if (s->rx_level >= 4) { - s->sssr |= SSSR_RFS; - } else { - s->sssr &= ~SSSR_RFS; - } - if (s->rx_level) { - s->sssr |= SSSR_RNE; - } else { - s->sssr &= ~SSSR_RNE; - } - /* TX FIFO is never filled, so it is always in underrun - condition if SSP is enabled */ - s->sssr |= SSSR_TFS; - s->sssr |= SSSR_TNF; - } - - strongarm_ssp_int_update(s); -} - -static uint64_t strongarm_ssp_read(void *opaque, hwaddr addr, - unsigned size) -{ - StrongARMSSPState *s = opaque; - uint32_t retval; - - switch (addr) { - case SSCR0: - return s->sscr[0]; - case SSCR1: - return s->sscr[1]; - case SSSR: - return s->sssr; - case SSDR: - if (~s->sscr[0] & SSCR0_SSE) { - return 0xffffffff; - } - if (s->rx_level < 1) { - printf("%s: SSP Rx Underrun\n", __func__); - return 0xffffffff; - } - s->rx_level--; - retval = s->rx_fifo[s->rx_start++]; - s->rx_start &= 0x7; - strongarm_ssp_fifo_update(s); - return retval; - default: - printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); - break; - } - return 0; -} - -static void strongarm_ssp_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - StrongARMSSPState *s = opaque; - - switch (addr) { - case SSCR0: - s->sscr[0] = value & 0xffbf; - if ((s->sscr[0] & SSCR0_SSE) && SSCR0_DSS(value) < 4) { - printf("%s: Wrong data size: %i bits\n", __func__, - (int)SSCR0_DSS(value)); - } - if (!(value & SSCR0_SSE)) { - s->sssr = 0; - s->rx_level = 0; - } - strongarm_ssp_fifo_update(s); - break; - - case SSCR1: - s->sscr[1] = value & 0x2f; - if (value & SSCR1_LBM) { - printf("%s: Attempt to use SSP LBM mode\n", __func__); - } - strongarm_ssp_fifo_update(s); - break; - - case SSSR: - s->sssr &= ~(value & SSSR_RW); - strongarm_ssp_int_update(s); - break; - - case SSDR: - if (SSCR0_UWIRE(s->sscr[0])) { - value &= 0xff; - } else - /* Note how 32bits overflow does no harm here */ - value &= (1 << SSCR0_DSS(s->sscr[0])) - 1; - - /* Data goes from here to the Tx FIFO and is shifted out from - * there directly to the slave, no need to buffer it. - */ - if (s->sscr[0] & SSCR0_SSE) { - uint32_t readval; - if (s->sscr[1] & SSCR1_LBM) { - readval = value; - } else { - readval = ssi_transfer(s->bus, value); - } - - if (s->rx_level < 0x08) { - s->rx_fifo[(s->rx_start + s->rx_level++) & 0x7] = readval; - } else { - s->sssr |= SSSR_ROR; - } - } - strongarm_ssp_fifo_update(s); - break; - - default: - printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __func__, addr); - break; - } -} - -static const MemoryRegionOps strongarm_ssp_ops = { - .read = strongarm_ssp_read, - .write = strongarm_ssp_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int strongarm_ssp_post_load(void *opaque, int version_id) -{ - StrongARMSSPState *s = opaque; - - strongarm_ssp_fifo_update(s); - - return 0; -} - -static int strongarm_ssp_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - StrongARMSSPState *s = STRONGARM_SSP(dev); - - sysbus_init_irq(sbd, &s->irq); - - memory_region_init_io(&s->iomem, OBJECT(s), &strongarm_ssp_ops, s, - "ssp", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - - s->bus = ssi_create_bus(dev, "ssi"); - return 0; -} - -static void strongarm_ssp_reset(DeviceState *dev) -{ - StrongARMSSPState *s = STRONGARM_SSP(dev); - - s->sssr = 0x03; /* 3 bit data, SPI, disabled */ - s->rx_start = 0; - s->rx_level = 0; -} - -static const VMStateDescription vmstate_strongarm_ssp_regs = { - .name = "strongarm-ssp", - .version_id = 0, - .minimum_version_id = 0, - .post_load = strongarm_ssp_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT16_ARRAY(sscr, StrongARMSSPState, 2), - VMSTATE_UINT16(sssr, StrongARMSSPState), - VMSTATE_UINT16_ARRAY(rx_fifo, StrongARMSSPState, 8), - VMSTATE_UINT8(rx_start, StrongARMSSPState), - VMSTATE_UINT8(rx_level, StrongARMSSPState), - VMSTATE_END_OF_LIST(), - }, -}; - -static void strongarm_ssp_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = strongarm_ssp_init; - dc->desc = "StrongARM SSP controller"; - dc->reset = strongarm_ssp_reset; - dc->vmsd = &vmstate_strongarm_ssp_regs; -} - -static const TypeInfo strongarm_ssp_info = { - .name = TYPE_STRONGARM_SSP, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(StrongARMSSPState), - .class_init = strongarm_ssp_class_init, -}; - -/* Main CPU functions */ -StrongARMState *sa1110_init(MemoryRegion *sysmem, - unsigned int sdram_size, const char *rev) -{ - StrongARMState *s; - int i; - - s = g_new0(StrongARMState, 1); - - if (!rev) { - rev = "sa1110-b5"; - } - - if (strncmp(rev, "sa1110", 6)) { - error_report("Machine requires a SA1110 processor."); - exit(1); - } - - s->cpu = cpu_arm_init(rev); - - if (!s->cpu) { - error_report("Unable to find CPU definition"); - exit(1); - } - - memory_region_allocate_system_memory(&s->sdram, NULL, "strongarm.sdram", - sdram_size); - memory_region_add_subregion(sysmem, SA_SDCS0, &s->sdram); - - s->pic = sysbus_create_varargs("strongarm_pic", 0x90050000, - qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ), - qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_FIQ), - NULL); - - sysbus_create_varargs("pxa25x-timer", 0x90000000, - qdev_get_gpio_in(s->pic, SA_PIC_OSTC0), - qdev_get_gpio_in(s->pic, SA_PIC_OSTC1), - qdev_get_gpio_in(s->pic, SA_PIC_OSTC2), - qdev_get_gpio_in(s->pic, SA_PIC_OSTC3), - NULL); - - sysbus_create_simple(TYPE_STRONGARM_RTC, 0x90010000, - qdev_get_gpio_in(s->pic, SA_PIC_RTC_ALARM)); - - s->gpio = strongarm_gpio_init(0x90040000, s->pic); - - s->ppc = sysbus_create_varargs(TYPE_STRONGARM_PPC, 0x90060000, NULL); - - for (i = 0; sa_serial[i].io_base; i++) { - DeviceState *dev = qdev_create(NULL, TYPE_STRONGARM_UART); - qdev_prop_set_chr(dev, "chardev", serial_hds[i]); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, - sa_serial[i].io_base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, - qdev_get_gpio_in(s->pic, sa_serial[i].irq)); - } - - s->ssp = sysbus_create_varargs(TYPE_STRONGARM_SSP, 0x80070000, - qdev_get_gpio_in(s->pic, SA_PIC_SSP), NULL); - s->ssp_bus = (SSIBus *)qdev_get_child_bus(s->ssp, "ssi"); - - return s; -} - -static void strongarm_register_types(void) -{ - type_register_static(&strongarm_pic_info); - type_register_static(&strongarm_rtc_sysbus_info); - type_register_static(&strongarm_gpio_info); - type_register_static(&strongarm_ppc_info); - type_register_static(&strongarm_uart_info); - type_register_static(&strongarm_ssp_info); -} - -type_init(strongarm_register_types) diff --git a/qemu/hw/arm/strongarm.h b/qemu/hw/arm/strongarm.h deleted file mode 100644 index 2893f9444..000000000 --- a/qemu/hw/arm/strongarm.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef _STRONGARM_H -#define _STRONGARM_H - -#include "exec/memory.h" - -#define SA_CS0 0x00000000 -#define SA_CS1 0x08000000 -#define SA_CS2 0x10000000 -#define SA_CS3 0x18000000 -#define SA_PCMCIA_CS0 0x20000000 -#define SA_PCMCIA_CS1 0x30000000 -#define SA_CS4 0x40000000 -#define SA_CS5 0x48000000 -/* system registers here */ -#define SA_SDCS0 0xc0000000 -#define SA_SDCS1 0xc8000000 -#define SA_SDCS2 0xd0000000 -#define SA_SDCS3 0xd8000000 - -enum { - SA_PIC_GPIO0_EDGE = 0, - SA_PIC_GPIO1_EDGE, - SA_PIC_GPIO2_EDGE, - SA_PIC_GPIO3_EDGE, - SA_PIC_GPIO4_EDGE, - SA_PIC_GPIO5_EDGE, - SA_PIC_GPIO6_EDGE, - SA_PIC_GPIO7_EDGE, - SA_PIC_GPIO8_EDGE, - SA_PIC_GPIO9_EDGE, - SA_PIC_GPIO10_EDGE, - SA_PIC_GPIOX_EDGE, - SA_PIC_LCD, - SA_PIC_UDC, - SA_PIC_RSVD1, - SA_PIC_UART1, - SA_PIC_UART2, - SA_PIC_UART3, - SA_PIC_MCP, - SA_PIC_SSP, - SA_PIC_DMA_CH0, - SA_PIC_DMA_CH1, - SA_PIC_DMA_CH2, - SA_PIC_DMA_CH3, - SA_PIC_DMA_CH4, - SA_PIC_DMA_CH5, - SA_PIC_OSTC0, - SA_PIC_OSTC1, - SA_PIC_OSTC2, - SA_PIC_OSTC3, - SA_PIC_RTC_HZ, - SA_PIC_RTC_ALARM, -}; - -typedef struct { - ARMCPU *cpu; - MemoryRegion sdram; - DeviceState *pic; - DeviceState *gpio; - DeviceState *ppc; - DeviceState *ssp; - SSIBus *ssp_bus; -} StrongARMState; - -StrongARMState *sa1110_init(MemoryRegion *sysmem, - unsigned int sdram_size, const char *rev); - -#endif diff --git a/qemu/hw/arm/sysbus-fdt.c b/qemu/hw/arm/sysbus-fdt.c deleted file mode 100644 index 5debb3348..000000000 --- a/qemu/hw/arm/sysbus-fdt.c +++ /dev/null @@ -1,542 +0,0 @@ -/* - * ARM Platform Bus device tree generation helpers - * - * Copyright (c) 2014 Linaro Limited - * - * Authors: - * Alex Graf - * Eric Auger - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2 or later, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include -#include "qemu-common.h" -#ifdef CONFIG_LINUX -#include -#endif -#include "hw/arm/sysbus-fdt.h" -#include "qemu/error-report.h" -#include "sysemu/device_tree.h" -#include "hw/platform-bus.h" -#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" - -/* - * internal struct that contains the information to create dynamic - * sysbus device node - */ -typedef struct PlatformBusFDTData { - void *fdt; /* device tree handle */ - int irq_start; /* index of the first IRQ usable by platform bus devices */ - const char *pbus_node_name; /* name of the platform bus node */ - PlatformBusDevice *pbus; -} PlatformBusFDTData; - -/* - * struct used when calling the machine init done notifier - * that constructs the fdt nodes of platform bus devices - */ -typedef struct PlatformBusFDTNotifierParams { - Notifier notifier; - ARMPlatformBusFDTParams *fdt_params; -} PlatformBusFDTNotifierParams; - -/* struct that associates a device type name and a node creation function */ -typedef struct NodeCreationPair { - const char *typename; - 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 */ - -/** - * add_calxeda_midway_xgmac_fdt_node - * - * Generates a simple node with following properties: - * compatible string, regs, interrupts, dma-coherent - */ -static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque) -{ - PlatformBusFDTData *data = opaque; - PlatformBusDevice *pbus = data->pbus; - void *fdt = data->fdt; - const char *parent_node = data->pbus_node_name; - int compat_str_len, i; - char *nodename; - uint32_t *irq_attr, *reg_attr; - uint64_t mmio_base, irq_number; - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); - VFIODevice *vbasedev = &vdev->vbasedev; - - 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(fdt, nodename); - - compat_str_len = strlen(vdev->compat) + 1; - qemu_fdt_setprop(fdt, nodename, "compatible", - vdev->compat, compat_str_len); - - qemu_fdt_setprop(fdt, nodename, "dma-coherent", "", 0); - - 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(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); - irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI); - } - qemu_fdt_setprop(fdt, nodename, "interrupts", - irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t)); - 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); - g_free(reg_attr); - g_free(nodename); - 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 */ -}; - -/* Generic Code */ - -/** - * add_fdt_node - add the device tree node of a dynamic sysbus device - * - * @sbdev: handle to the sysbus device - * @opaque: handle to the PlatformBusFDTData - * - * Checks the sysbus type belongs to the list of device types that - * are dynamically instantiable and if so call the node creation - * function. - */ -static int add_fdt_node(SysBusDevice *sbdev, void *opaque) -{ - int i, ret; - - for (i = 0; i < ARRAY_SIZE(add_fdt_node_functions); i++) { - if (!strcmp(object_get_typename(OBJECT(sbdev)), - add_fdt_node_functions[i].typename)) { - ret = add_fdt_node_functions[i].add_fdt_node_fn(sbdev, opaque); - assert(!ret); - return 0; - } - } - error_report("Device %s can not be dynamically instantiated", - qdev_fw_name(DEVICE(sbdev))); - exit(1); -} - -/** - * add_all_platform_bus_fdt_nodes - create all the platform bus nodes - * - * builds the parent platform bus node and all the nodes of dynamic - * sysbus devices attached to it. - */ -static void add_all_platform_bus_fdt_nodes(ARMPlatformBusFDTParams *fdt_params) -{ - const char platcomp[] = "qemu,platform\0simple-bus"; - PlatformBusDevice *pbus; - DeviceState *dev; - gchar *node; - uint64_t addr, size; - int irq_start, dtb_size; - struct arm_boot_info *info = fdt_params->binfo; - const ARMPlatformBusSystemParams *params = fdt_params->system_params; - const char *intc = fdt_params->intc; - void *fdt = info->get_dtb(info, &dtb_size); - - /* - * If the user provided a dtb, we assume the dynamic sysbus nodes - * already are integrated there. This corresponds to a use case where - * the dynamic sysbus nodes are complex and their generation is not yet - * supported. In that case the user can take charge of the guest dt - * while qemu takes charge of the qom stuff. - */ - if (info->dtb_filename) { - return; - } - - assert(fdt); - - node = g_strdup_printf("/platform@%"PRIx64, params->platform_bus_base); - addr = params->platform_bus_base; - size = params->platform_bus_size; - irq_start = params->platform_bus_first_irq; - - /* Create a /platform node that we can put all devices into */ - qemu_fdt_add_subnode(fdt, node); - qemu_fdt_setprop(fdt, node, "compatible", platcomp, sizeof(platcomp)); - - /* Our platform bus region is less than 32bits, so 1 cell is enough for - * address and size - */ - qemu_fdt_setprop_cells(fdt, node, "#size-cells", 1); - qemu_fdt_setprop_cells(fdt, node, "#address-cells", 1); - qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, size); - - qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", intc); - - dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE); - pbus = PLATFORM_BUS_DEVICE(dev); - - /* We can only create dt nodes for dynamic devices when they're ready */ - assert(pbus->done_gathering); - - PlatformBusFDTData data = { - .fdt = fdt, - .irq_start = irq_start, - .pbus_node_name = node, - .pbus = pbus, - }; - - /* Loop through all dynamic sysbus devices and create their node */ - foreach_dynamic_sysbus_device(add_fdt_node, &data); - - g_free(node); -} - -static void platform_bus_fdt_notify(Notifier *notifier, void *data) -{ - PlatformBusFDTNotifierParams *p = DO_UPCAST(PlatformBusFDTNotifierParams, - notifier, notifier); - - add_all_platform_bus_fdt_nodes(p->fdt_params); - g_free(p->fdt_params); - g_free(p); -} - -void arm_register_platform_bus_fdt_creator(ARMPlatformBusFDTParams *fdt_params) -{ - PlatformBusFDTNotifierParams *p = g_new(PlatformBusFDTNotifierParams, 1); - - p->fdt_params = fdt_params; - p->notifier.notify = platform_bus_fdt_notify; - qemu_add_machine_init_done_notifier(&p->notifier); -} diff --git a/qemu/hw/arm/tosa.c b/qemu/hw/arm/tosa.c deleted file mode 100644 index 4e9494f94..000000000 --- a/qemu/hw/arm/tosa.c +++ /dev/null @@ -1,303 +0,0 @@ -/* vim:set shiftwidth=4 ts=4 et: */ -/* - * PXA255 Sharp Zaurus SL-6000 PDA platform - * - * Copyright (c) 2008 Dmitry Baryshkov - * - * Code based on spitz platform by Andrzej Zaborowski - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/hw.h" -#include "hw/arm/pxa.h" -#include "hw/arm/arm.h" -#include "hw/devices.h" -#include "hw/arm/sharpsl.h" -#include "hw/pcmcia.h" -#include "hw/boards.h" -#include "hw/i2c/i2c.h" -#include "hw/ssi/ssi.h" -#include "sysemu/block-backend.h" -#include "hw/sysbus.h" -#include "exec/address-spaces.h" - -#define TOSA_RAM 0x04000000 -#define TOSA_ROM 0x00800000 - -#define TOSA_GPIO_USB_IN (5) -#define TOSA_GPIO_nSD_DETECT (9) -#define TOSA_GPIO_ON_RESET (19) -#define TOSA_GPIO_CF_IRQ (21) /* CF slot0 Ready */ -#define TOSA_GPIO_CF_CD (13) -#define TOSA_GPIO_TC6393XB_INT (15) -#define TOSA_GPIO_JC_CF_IRQ (36) /* CF slot1 Ready */ - -#define TOSA_SCOOP_GPIO_BASE 1 -#define TOSA_GPIO_IR_POWERDWN (TOSA_SCOOP_GPIO_BASE + 2) -#define TOSA_GPIO_SD_WP (TOSA_SCOOP_GPIO_BASE + 3) -#define TOSA_GPIO_PWR_ON (TOSA_SCOOP_GPIO_BASE + 4) - -#define TOSA_SCOOP_JC_GPIO_BASE 1 -#define TOSA_GPIO_BT_LED (TOSA_SCOOP_JC_GPIO_BASE + 0) -#define TOSA_GPIO_NOTE_LED (TOSA_SCOOP_JC_GPIO_BASE + 1) -#define TOSA_GPIO_CHRG_ERR_LED (TOSA_SCOOP_JC_GPIO_BASE + 2) -#define TOSA_GPIO_TC6393XB_L3V_ON (TOSA_SCOOP_JC_GPIO_BASE + 5) -#define TOSA_GPIO_WLAN_LED (TOSA_SCOOP_JC_GPIO_BASE + 7) - -#define DAC_BASE 0x4e -#define DAC_CH1 0 -#define DAC_CH2 1 - -static void tosa_microdrive_attach(PXA2xxState *cpu) -{ - PCMCIACardState *md; - DriveInfo *dinfo; - - dinfo = drive_get(IF_IDE, 0, 0); - if (!dinfo || dinfo->media_cd) - return; - md = dscm1xxxx_init(dinfo); - pxa2xx_pcmcia_attach(cpu->pcmcia[0], md); -} - -static void tosa_out_switch(void *opaque, int line, int level) -{ - switch (line) { - case 0: - fprintf(stderr, "blue LED %s.\n", level ? "on" : "off"); - break; - case 1: - fprintf(stderr, "green LED %s.\n", level ? "on" : "off"); - break; - case 2: - fprintf(stderr, "amber LED %s.\n", level ? "on" : "off"); - break; - case 3: - fprintf(stderr, "wlan LED %s.\n", level ? "on" : "off"); - break; - default: - fprintf(stderr, "Uhandled out event: %d = %d\n", line, level); - break; - } -} - - -static void tosa_gpio_setup(PXA2xxState *cpu, - DeviceState *scp0, - DeviceState *scp1, - TC6393xbState *tmio) -{ - qemu_irq *outsignals = qemu_allocate_irqs(tosa_out_switch, cpu, 4); - /* MMC/SD host */ - pxa2xx_mmci_handlers(cpu->mmc, - qdev_get_gpio_in(scp0, TOSA_GPIO_SD_WP), - qemu_irq_invert(qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_nSD_DETECT))); - - /* Handle reset */ - qdev_connect_gpio_out(cpu->gpio, TOSA_GPIO_ON_RESET, cpu->reset); - - /* PCMCIA signals: card's IRQ and Card-Detect */ - pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[0], - qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_CF_IRQ), - qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_CF_CD)); - - pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[1], - qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_JC_CF_IRQ), - NULL); - - qdev_connect_gpio_out(scp1, TOSA_GPIO_BT_LED, outsignals[0]); - qdev_connect_gpio_out(scp1, TOSA_GPIO_NOTE_LED, outsignals[1]); - qdev_connect_gpio_out(scp1, TOSA_GPIO_CHRG_ERR_LED, outsignals[2]); - qdev_connect_gpio_out(scp1, TOSA_GPIO_WLAN_LED, outsignals[3]); - - qdev_connect_gpio_out(scp1, TOSA_GPIO_TC6393XB_L3V_ON, tc6393xb_l3v_get(tmio)); - - /* UDC Vbus */ - qemu_irq_raise(qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_USB_IN)); -} - -static uint32_t tosa_ssp_tansfer(SSISlave *dev, uint32_t value) -{ - fprintf(stderr, "TG: %d %02x\n", value >> 5, value & 0x1f); - return 0; -} - -static int tosa_ssp_init(SSISlave *dev) -{ - /* Nothing to do. */ - return 0; -} - -#define TYPE_TOSA_DAC "tosa_dac" -#define TOSA_DAC(obj) OBJECT_CHECK(TosaDACState, (obj), TYPE_TOSA_DAC) - -typedef struct { - I2CSlave parent_obj; - - int len; - char buf[3]; -} TosaDACState; - -static int tosa_dac_send(I2CSlave *i2c, uint8_t data) -{ - TosaDACState *s = TOSA_DAC(i2c); - - s->buf[s->len] = data; - if (s->len ++ > 2) { -#ifdef VERBOSE - fprintf(stderr, "%s: message too long (%i bytes)\n", __FUNCTION__, s->len); -#endif - return 1; - } - - if (s->len == 2) { - fprintf(stderr, "dac: channel %d value 0x%02x\n", - s->buf[0], s->buf[1]); - } - - return 0; -} - -static void tosa_dac_event(I2CSlave *i2c, enum i2c_event event) -{ - TosaDACState *s = TOSA_DAC(i2c); - - s->len = 0; - switch (event) { - case I2C_START_SEND: - break; - case I2C_START_RECV: - printf("%s: recv not supported!!!\n", __FUNCTION__); - break; - case I2C_FINISH: -#ifdef VERBOSE - if (s->len < 2) - printf("%s: message too short (%i bytes)\n", __FUNCTION__, s->len); - if (s->len > 2) - printf("%s: message too long\n", __FUNCTION__); -#endif - break; - default: - break; - } -} - -static int tosa_dac_recv(I2CSlave *s) -{ - printf("%s: recv not supported!!!\n", __FUNCTION__); - return -1; -} - -static int tosa_dac_init(I2CSlave *i2c) -{ - /* Nothing to do. */ - return 0; -} - -static void tosa_tg_init(PXA2xxState *cpu) -{ - I2CBus *bus = pxa2xx_i2c_bus(cpu->i2c[0]); - i2c_create_slave(bus, TYPE_TOSA_DAC, DAC_BASE); - ssi_create_slave(cpu->ssp[1], "tosa-ssp"); -} - - -static struct arm_boot_info tosa_binfo = { - .loader_start = PXA2XX_SDRAM_BASE, - .ram_size = 0x04000000, -}; - -static void tosa_init(MachineState *machine) -{ - 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; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *rom = g_new(MemoryRegion, 1); - PXA2xxState *mpu; - TC6393xbState *tmio; - DeviceState *scp0, *scp1; - - if (!cpu_model) - cpu_model = "pxa255"; - - mpu = pxa255_init(address_space_mem, tosa_binfo.ram_size); - - 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); - - tmio = tc6393xb_init(address_space_mem, 0x10000000, - qdev_get_gpio_in(mpu->gpio, TOSA_GPIO_TC6393XB_INT)); - - scp0 = sysbus_create_simple("scoop", 0x08800000, NULL); - scp1 = sysbus_create_simple("scoop", 0x14800040, NULL); - - tosa_gpio_setup(mpu, scp0, scp1, tmio); - - tosa_microdrive_attach(mpu); - - tosa_tg_init(mpu); - - tosa_binfo.kernel_filename = kernel_filename; - tosa_binfo.kernel_cmdline = kernel_cmdline; - tosa_binfo.initrd_filename = initrd_filename; - tosa_binfo.board_id = 0x208; - arm_load_kernel(mpu->cpu, &tosa_binfo); - sl_bootparam_write(SL_PXA_PARAM_BASE); -} - -static void tosapda_machine_init(MachineClass *mc) -{ - mc->desc = "Sharp SL-6000 (Tosa) PDA (PXA255)"; - mc->init = tosa_init; -} - -DEFINE_MACHINE("tosa", tosapda_machine_init) - -static void tosa_dac_class_init(ObjectClass *klass, void *data) -{ - I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - - k->init = tosa_dac_init; - k->event = tosa_dac_event; - k->recv = tosa_dac_recv; - k->send = tosa_dac_send; -} - -static const TypeInfo tosa_dac_info = { - .name = TYPE_TOSA_DAC, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(TosaDACState), - .class_init = tosa_dac_class_init, -}; - -static void tosa_ssp_class_init(ObjectClass *klass, void *data) -{ - SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - - k->init = tosa_ssp_init; - k->transfer = tosa_ssp_tansfer; -} - -static const TypeInfo tosa_ssp_info = { - .name = "tosa-ssp", - .parent = TYPE_SSI_SLAVE, - .instance_size = sizeof(SSISlave), - .class_init = tosa_ssp_class_init, -}; - -static void tosa_register_types(void) -{ - type_register_static(&tosa_dac_info); - type_register_static(&tosa_ssp_info); -} - -type_init(tosa_register_types) diff --git a/qemu/hw/arm/versatilepb.c b/qemu/hw/arm/versatilepb.c deleted file mode 100644 index e5a80c2d2..000000000 --- a/qemu/hw/arm/versatilepb.c +++ /dev/null @@ -1,448 +0,0 @@ -/* - * ARM Versatile Platform/Application Baseboard System emulation. - * - * Copyright (c) 2005-2007 CodeSourcery. - * Written by Paul Brook - * - * 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" -#include "net/net.h" -#include "sysemu/sysemu.h" -#include "hw/pci/pci.h" -#include "hw/i2c/i2c.h" -#include "hw/boards.h" -#include "sysemu/block-backend.h" -#include "exec/address-spaces.h" -#include "hw/block/flash.h" -#include "qemu/error-report.h" - -#define VERSATILE_FLASH_ADDR 0x34000000 -#define VERSATILE_FLASH_SIZE (64 * 1024 * 1024) -#define VERSATILE_FLASH_SECT_SIZE (256 * 1024) - -/* Primary interrupt controller. */ - -#define TYPE_VERSATILE_PB_SIC "versatilepb_sic" -#define VERSATILE_PB_SIC(obj) \ - OBJECT_CHECK(vpb_sic_state, (obj), TYPE_VERSATILE_PB_SIC) - -typedef struct vpb_sic_state { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t level; - uint32_t mask; - uint32_t pic_enable; - qemu_irq parent[32]; - int irq; -} vpb_sic_state; - -static const VMStateDescription vmstate_vpb_sic = { - .name = "versatilepb_sic", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(level, vpb_sic_state), - VMSTATE_UINT32(mask, vpb_sic_state), - VMSTATE_UINT32(pic_enable, vpb_sic_state), - VMSTATE_END_OF_LIST() - } -}; - -static void vpb_sic_update(vpb_sic_state *s) -{ - uint32_t flags; - - flags = s->level & s->mask; - qemu_set_irq(s->parent[s->irq], flags != 0); -} - -static void vpb_sic_update_pic(vpb_sic_state *s) -{ - int i; - uint32_t mask; - - for (i = 21; i <= 30; i++) { - mask = 1u << i; - if (!(s->pic_enable & mask)) - continue; - qemu_set_irq(s->parent[i], (s->level & mask) != 0); - } -} - -static void vpb_sic_set_irq(void *opaque, int irq, int level) -{ - vpb_sic_state *s = (vpb_sic_state *)opaque; - if (level) - s->level |= 1u << irq; - else - s->level &= ~(1u << irq); - if (s->pic_enable & (1u << irq)) - qemu_set_irq(s->parent[irq], level); - vpb_sic_update(s); -} - -static uint64_t vpb_sic_read(void *opaque, hwaddr offset, - unsigned size) -{ - vpb_sic_state *s = (vpb_sic_state *)opaque; - - switch (offset >> 2) { - case 0: /* STATUS */ - return s->level & s->mask; - case 1: /* RAWSTAT */ - return s->level; - case 2: /* ENABLE */ - return s->mask; - case 4: /* SOFTINT */ - return s->level & 1; - case 8: /* PICENABLE */ - return s->pic_enable; - default: - printf ("vpb_sic_read: Bad register offset 0x%x\n", (int)offset); - return 0; - } -} - -static void vpb_sic_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - vpb_sic_state *s = (vpb_sic_state *)opaque; - - switch (offset >> 2) { - case 2: /* ENSET */ - s->mask |= value; - break; - case 3: /* ENCLR */ - s->mask &= ~value; - break; - case 4: /* SOFTINTSET */ - if (value) - s->mask |= 1; - break; - case 5: /* SOFTINTCLR */ - if (value) - s->mask &= ~1u; - break; - case 8: /* PICENSET */ - s->pic_enable |= (value & 0x7fe00000); - vpb_sic_update_pic(s); - break; - case 9: /* PICENCLR */ - s->pic_enable &= ~value; - vpb_sic_update_pic(s); - break; - default: - printf ("vpb_sic_write: Bad register offset 0x%x\n", (int)offset); - return; - } - vpb_sic_update(s); -} - -static const MemoryRegionOps vpb_sic_ops = { - .read = vpb_sic_read, - .write = vpb_sic_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int vpb_sic_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - vpb_sic_state *s = VERSATILE_PB_SIC(dev); - int i; - - qdev_init_gpio_in(dev, vpb_sic_set_irq, 32); - for (i = 0; i < 32; i++) { - sysbus_init_irq(sbd, &s->parent[i]); - } - s->irq = 31; - memory_region_init_io(&s->iomem, OBJECT(s), &vpb_sic_ops, s, - "vpb-sic", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - return 0; -} - -/* Board init. */ - -/* The AB and PB boards both use the same core, just with different - peripherals and expansion busses. For now we emulate a subset of the - PB peripherals and just change the board ID. */ - -static struct arm_boot_info versatile_binfo; - -static void versatile_init(MachineState *machine, int board_id) -{ - ObjectClass *cpu_oc; - Object *cpuobj; - ARMCPU *cpu; - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - qemu_irq pic[32]; - qemu_irq sic[32]; - DeviceState *dev, *sysctl; - SysBusDevice *busdev; - DeviceState *pl041; - PCIBus *pci_bus; - NICInfo *nd; - I2CBus *i2c; - int n; - int done_smc = 0; - DriveInfo *dinfo; - - if (!machine->cpu_model) { - machine->cpu_model = "arm926"; - } - - cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, machine->cpu_model); - if (!cpu_oc) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - - cpuobj = object_new(object_class_get_name(cpu_oc)); - - /* By default 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", &error_fatal); - } - - object_property_set_bool(cpuobj, true, "realized", &error_fatal); - - cpu = ARM_CPU(cpuobj); - - memory_region_allocate_system_memory(ram, NULL, "versatile.ram", - machine->ram_size); - /* ??? RAM should repeat to fill physical memory space. */ - /* SDRAM at address zero. */ - memory_region_add_subregion(sysmem, 0, ram); - - sysctl = qdev_create(NULL, "realview_sysctl"); - qdev_prop_set_uint32(sysctl, "sys_id", 0x41007004); - qdev_prop_set_uint32(sysctl, "proc_id", 0x02000000); - qdev_init_nofail(sysctl); - sysbus_mmio_map(SYS_BUS_DEVICE(sysctl), 0, 0x10000000); - - dev = sysbus_create_varargs("pl190", 0x10140000, - qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ), - qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ), - NULL); - for (n = 0; n < 32; n++) { - pic[n] = qdev_get_gpio_in(dev, n); - } - dev = sysbus_create_simple(TYPE_VERSATILE_PB_SIC, 0x10003000, NULL); - for (n = 0; n < 32; n++) { - sysbus_connect_irq(SYS_BUS_DEVICE(dev), n, pic[n]); - sic[n] = qdev_get_gpio_in(dev, n); - } - - sysbus_create_simple("pl050_keyboard", 0x10006000, sic[3]); - sysbus_create_simple("pl050_mouse", 0x10007000, sic[4]); - - dev = qdev_create(NULL, "versatile_pci"); - busdev = SYS_BUS_DEVICE(dev); - qdev_init_nofail(dev); - sysbus_mmio_map(busdev, 0, 0x10001000); /* PCI controller regs */ - sysbus_mmio_map(busdev, 1, 0x41000000); /* PCI self-config */ - sysbus_mmio_map(busdev, 2, 0x42000000); /* PCI config */ - sysbus_mmio_map(busdev, 3, 0x43000000); /* PCI I/O */ - sysbus_mmio_map(busdev, 4, 0x44000000); /* PCI memory window 1 */ - sysbus_mmio_map(busdev, 5, 0x50000000); /* PCI memory window 2 */ - sysbus_mmio_map(busdev, 6, 0x60000000); /* PCI memory window 3 */ - sysbus_connect_irq(busdev, 0, sic[27]); - sysbus_connect_irq(busdev, 1, sic[28]); - sysbus_connect_irq(busdev, 2, sic[29]); - sysbus_connect_irq(busdev, 3, sic[30]); - pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci"); - - for(n = 0; n < nb_nics; n++) { - nd = &nd_table[n]; - - if (!done_smc && (!nd->model || strcmp(nd->model, "smc91c111") == 0)) { - smc91c111_init(nd, 0x10010000, sic[25]); - done_smc = 1; - } else { - pci_nic_init_nofail(nd, pci_bus, "rtl8139", NULL); - } - } - if (usb_enabled()) { - pci_create_simple(pci_bus, -1, "pci-ohci"); - } - n = drive_get_max_bus(IF_SCSI); - while (n >= 0) { - pci_create_simple(pci_bus, -1, "lsi53c895a"); - n--; - } - - sysbus_create_simple("pl011", 0x101f1000, pic[12]); - sysbus_create_simple("pl011", 0x101f2000, pic[13]); - sysbus_create_simple("pl011", 0x101f3000, pic[14]); - sysbus_create_simple("pl011", 0x10009000, sic[6]); - - sysbus_create_simple("pl080", 0x10130000, pic[17]); - sysbus_create_simple("sp804", 0x101e2000, pic[4]); - sysbus_create_simple("sp804", 0x101e3000, pic[5]); - - sysbus_create_simple("pl061", 0x101e4000, pic[6]); - sysbus_create_simple("pl061", 0x101e5000, pic[7]); - sysbus_create_simple("pl061", 0x101e6000, pic[8]); - sysbus_create_simple("pl061", 0x101e7000, pic[9]); - - /* The versatile/PB actually has a modified Color LCD controller - that includes hardware cursor support from the PL111. */ - dev = sysbus_create_simple("pl110_versatile", 0x10120000, pic[16]); - /* Wire up the mux control signals from the SYS_CLCD register */ - qdev_connect_gpio_out(sysctl, 0, qdev_get_gpio_in(dev, 0)); - - sysbus_create_varargs("pl181", 0x10005000, sic[22], sic[1], NULL); - sysbus_create_varargs("pl181", 0x1000b000, sic[23], sic[2], NULL); - - /* Add PL031 Real Time Clock. */ - sysbus_create_simple("pl031", 0x101e8000, pic[10]); - - dev = sysbus_create_simple("versatile_i2c", 0x10002000, NULL); - i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); - i2c_create_slave(i2c, "ds1338", 0x68); - - /* Add PL041 AACI Interface to the LM4549 codec */ - pl041 = qdev_create(NULL, "pl041"); - qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512); - qdev_init_nofail(pl041); - sysbus_mmio_map(SYS_BUS_DEVICE(pl041), 0, 0x10004000); - sysbus_connect_irq(SYS_BUS_DEVICE(pl041), 0, sic[24]); - - /* Memory map for Versatile/PB: */ - /* 0x10000000 System registers. */ - /* 0x10001000 PCI controller config registers. */ - /* 0x10002000 Serial bus interface. */ - /* 0x10003000 Secondary interrupt controller. */ - /* 0x10004000 AACI (audio). */ - /* 0x10005000 MMCI0. */ - /* 0x10006000 KMI0 (keyboard). */ - /* 0x10007000 KMI1 (mouse). */ - /* 0x10008000 Character LCD Interface. */ - /* 0x10009000 UART3. */ - /* 0x1000a000 Smart card 1. */ - /* 0x1000b000 MMCI1. */ - /* 0x10010000 Ethernet. */ - /* 0x10020000 USB. */ - /* 0x10100000 SSMC. */ - /* 0x10110000 MPMC. */ - /* 0x10120000 CLCD Controller. */ - /* 0x10130000 DMA Controller. */ - /* 0x10140000 Vectored interrupt controller. */ - /* 0x101d0000 AHB Monitor Interface. */ - /* 0x101e0000 System Controller. */ - /* 0x101e1000 Watchdog Interface. */ - /* 0x101e2000 Timer 0/1. */ - /* 0x101e3000 Timer 2/3. */ - /* 0x101e4000 GPIO port 0. */ - /* 0x101e5000 GPIO port 1. */ - /* 0x101e6000 GPIO port 2. */ - /* 0x101e7000 GPIO port 3. */ - /* 0x101e8000 RTC. */ - /* 0x101f0000 Smart card 0. */ - /* 0x101f1000 UART0. */ - /* 0x101f2000 UART1. */ - /* 0x101f3000 UART2. */ - /* 0x101f4000 SSPI. */ - /* 0x34000000 NOR Flash */ - - dinfo = drive_get(IF_PFLASH, 0, 0); - if (!pflash_cfi01_register(VERSATILE_FLASH_ADDR, NULL, "versatile.flash", - VERSATILE_FLASH_SIZE, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - VERSATILE_FLASH_SECT_SIZE, - VERSATILE_FLASH_SIZE / VERSATILE_FLASH_SECT_SIZE, - 4, 0x0089, 0x0018, 0x0000, 0x0, 0)) { - fprintf(stderr, "qemu: Error registering flash memory.\n"); - } - - versatile_binfo.ram_size = machine->ram_size; - versatile_binfo.kernel_filename = machine->kernel_filename; - versatile_binfo.kernel_cmdline = machine->kernel_cmdline; - versatile_binfo.initrd_filename = machine->initrd_filename; - versatile_binfo.board_id = board_id; - arm_load_kernel(cpu, &versatile_binfo); -} - -static void vpb_init(MachineState *machine) -{ - versatile_init(machine, 0x183); -} - -static void vab_init(MachineState *machine) -{ - versatile_init(machine, 0x25e); -} - -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 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) -{ - type_register_static(&versatilepb_type); - type_register_static(&versatileab_type); -} - -type_init(versatile_machine_init) - -static void vpb_sic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = vpb_sic_init; - dc->vmsd = &vmstate_vpb_sic; -} - -static const TypeInfo vpb_sic_info = { - .name = TYPE_VERSATILE_PB_SIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(vpb_sic_state), - .class_init = vpb_sic_class_init, -}; - -static void versatilepb_register_types(void) -{ - type_register_static(&vpb_sic_info); -} - -type_init(versatilepb_register_types) diff --git a/qemu/hw/arm/vexpress.c b/qemu/hw/arm/vexpress.c deleted file mode 100644 index 70b3e701e..000000000 --- a/qemu/hw/arm/vexpress.c +++ /dev/null @@ -1,806 +0,0 @@ -/* - * ARM Versatile Express emulation. - * - * Copyright (c) 2010 - 2011 B Labs Ltd. - * Copyright (c) 2011 Linaro Limited - * Written by Bahadir Balban, Amit Mahajan, Peter Maydell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - * 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-common.h" -#include "cpu.h" -#include "hw/sysbus.h" -#include "hw/arm/arm.h" -#include "hw/arm/primecell.h" -#include "hw/devices.h" -#include "net/net.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" -#include "hw/loader.h" -#include "exec/address-spaces.h" -#include "sysemu/block-backend.h" -#include "hw/block/flash.h" -#include "sysemu/device_tree.h" -#include "qemu/error-report.h" -#include - -#define VEXPRESS_BOARD_ID 0x8e0 -#define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024) -#define VEXPRESS_FLASH_SECT_SIZE (256 * 1024) - -/* Number of virtio transports to create (0..8; limited by - * number of available IRQ lines). - */ -#define NUM_VIRTIO_TRANSPORTS 4 - -/* Address maps for peripherals: - * the Versatile Express motherboard has two possible maps, - * the "legacy" one (used for A9) and the "Cortex-A Series" - * map (used for newer cores). - * Individual daughterboards can also have different maps for - * their peripherals. - */ - -enum { - VE_SYSREGS, - VE_SP810, - VE_SERIALPCI, - VE_PL041, - VE_MMCI, - VE_KMI0, - VE_KMI1, - VE_UART0, - VE_UART1, - VE_UART2, - VE_UART3, - VE_WDT, - VE_TIMER01, - VE_TIMER23, - VE_SERIALDVI, - VE_RTC, - VE_COMPACTFLASH, - VE_CLCD, - VE_NORFLASH0, - VE_NORFLASH1, - VE_NORFLASHALIAS, - VE_SRAM, - VE_VIDEORAM, - VE_ETHERNET, - VE_USB, - VE_DAPROM, - VE_VIRTIO, -}; - -static hwaddr motherboard_legacy_map[] = { - [VE_NORFLASHALIAS] = 0, - /* CS7: 0x10000000 .. 0x10020000 */ - [VE_SYSREGS] = 0x10000000, - [VE_SP810] = 0x10001000, - [VE_SERIALPCI] = 0x10002000, - [VE_PL041] = 0x10004000, - [VE_MMCI] = 0x10005000, - [VE_KMI0] = 0x10006000, - [VE_KMI1] = 0x10007000, - [VE_UART0] = 0x10009000, - [VE_UART1] = 0x1000a000, - [VE_UART2] = 0x1000b000, - [VE_UART3] = 0x1000c000, - [VE_WDT] = 0x1000f000, - [VE_TIMER01] = 0x10011000, - [VE_TIMER23] = 0x10012000, - [VE_VIRTIO] = 0x10013000, - [VE_SERIALDVI] = 0x10016000, - [VE_RTC] = 0x10017000, - [VE_COMPACTFLASH] = 0x1001a000, - [VE_CLCD] = 0x1001f000, - /* CS0: 0x40000000 .. 0x44000000 */ - [VE_NORFLASH0] = 0x40000000, - /* CS1: 0x44000000 .. 0x48000000 */ - [VE_NORFLASH1] = 0x44000000, - /* CS2: 0x48000000 .. 0x4a000000 */ - [VE_SRAM] = 0x48000000, - /* CS3: 0x4c000000 .. 0x50000000 */ - [VE_VIDEORAM] = 0x4c000000, - [VE_ETHERNET] = 0x4e000000, - [VE_USB] = 0x4f000000, -}; - -static hwaddr motherboard_aseries_map[] = { - [VE_NORFLASHALIAS] = 0, - /* CS0: 0x08000000 .. 0x0c000000 */ - [VE_NORFLASH0] = 0x08000000, - /* CS4: 0x0c000000 .. 0x10000000 */ - [VE_NORFLASH1] = 0x0c000000, - /* CS5: 0x10000000 .. 0x14000000 */ - /* CS1: 0x14000000 .. 0x18000000 */ - [VE_SRAM] = 0x14000000, - /* CS2: 0x18000000 .. 0x1c000000 */ - [VE_VIDEORAM] = 0x18000000, - [VE_ETHERNET] = 0x1a000000, - [VE_USB] = 0x1b000000, - /* CS3: 0x1c000000 .. 0x20000000 */ - [VE_DAPROM] = 0x1c000000, - [VE_SYSREGS] = 0x1c010000, - [VE_SP810] = 0x1c020000, - [VE_SERIALPCI] = 0x1c030000, - [VE_PL041] = 0x1c040000, - [VE_MMCI] = 0x1c050000, - [VE_KMI0] = 0x1c060000, - [VE_KMI1] = 0x1c070000, - [VE_UART0] = 0x1c090000, - [VE_UART1] = 0x1c0a0000, - [VE_UART2] = 0x1c0b0000, - [VE_UART3] = 0x1c0c0000, - [VE_WDT] = 0x1c0f0000, - [VE_TIMER01] = 0x1c110000, - [VE_TIMER23] = 0x1c120000, - [VE_VIRTIO] = 0x1c130000, - [VE_SERIALDVI] = 0x1c160000, - [VE_RTC] = 0x1c170000, - [VE_COMPACTFLASH] = 0x1c1a0000, - [VE_CLCD] = 0x1c1f0000, -}; - -/* Structure defining the peculiarities of a specific daughterboard */ - -typedef struct VEDBoardInfo VEDBoardInfo; - -typedef struct { - MachineClass parent; - VEDBoardInfo *daughterboard; -} VexpressMachineClass; - -typedef struct { - MachineState parent; - bool secure; -} VexpressMachineState; - -#define TYPE_VEXPRESS_MACHINE "vexpress" -#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) \ - OBJECT_GET_CLASS(VexpressMachineClass, obj, TYPE_VEXPRESS_MACHINE) -#define VEXPRESS_MACHINE_CLASS(klass) \ - OBJECT_CLASS_CHECK(VexpressMachineClass, klass, TYPE_VEXPRESS_MACHINE) - -typedef void DBoardInitFn(const VexpressMachineState *machine, - ram_addr_t ram_size, - const char *cpu_model, - qemu_irq *pic); - -struct VEDBoardInfo { - struct arm_boot_info bootinfo; - const hwaddr *motherboard_map; - hwaddr loader_start; - const hwaddr gic_cpu_if_addr; - uint32_t proc_id; - uint32_t num_voltage_sensors; - const uint32_t *voltages; - uint32_t num_clocks; - const uint32_t *clocks; - DBoardInitFn *init; -}; - -static void init_cpus(const char *cpu_model, const char *privdev, - hwaddr periphbase, qemu_irq *pic, bool secure) -{ - ObjectClass *cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); - DeviceState *dev; - SysBusDevice *busdev; - int n; - - if (!cpu_oc) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - - /* Create the actual CPUs */ - for (n = 0; n < smp_cpus; n++) { - Object *cpuobj = object_new(object_class_get_name(cpu_oc)); - - if (!secure) { - object_property_set_bool(cpuobj, false, "has_el3", NULL); - } - - if (object_property_find(cpuobj, "reset-cbar", NULL)) { - object_property_set_int(cpuobj, periphbase, - "reset-cbar", &error_abort); - } - object_property_set_bool(cpuobj, true, "realized", &error_fatal); - } - - /* Create the private peripheral devices (including the GIC); - * this must happen after the CPUs are created because a15mpcore_priv - * wires itself up to the CPU's generic_timer gpio out lines. - */ - dev = qdev_create(NULL, privdev); - qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); - qdev_init_nofail(dev); - busdev = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(busdev, 0, periphbase); - - /* Interrupts [42:0] are from the motherboard; - * [47:43] are reserved; [63:48] are daughterboard - * peripherals. Note that some documentation numbers - * external interrupts starting from 32 (because there - * are internal interrupts 0..31). - */ - for (n = 0; n < 64; n++) { - pic[n] = qdev_get_gpio_in(dev, n); - } - - /* Connect the CPUs to the GIC */ - for (n = 0; n < smp_cpus; n++) { - DeviceState *cpudev = DEVICE(qemu_get_cpu(n)); - - sysbus_connect_irq(busdev, n, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); - sysbus_connect_irq(busdev, n + smp_cpus, - qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); - } -} - -static void a9_daughterboard_init(const VexpressMachineState *vms, - ram_addr_t ram_size, - const char *cpu_model, - qemu_irq *pic) -{ - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *lowram = g_new(MemoryRegion, 1); - ram_addr_t low_ram_size; - - if (!cpu_model) { - cpu_model = "cortex-a9"; - } - - if (ram_size > 0x40000000) { - /* 1GB is the maximum the address space permits */ - fprintf(stderr, "vexpress-a9: cannot model more than 1GB RAM\n"); - exit(1); - } - - memory_region_allocate_system_memory(ram, NULL, "vexpress.highmem", - ram_size); - low_ram_size = ram_size; - if (low_ram_size > 0x4000000) { - low_ram_size = 0x4000000; - } - /* RAM is from 0x60000000 upwards. The bottom 64MB of the - * address space should in theory be remappable to various - * things including ROM or RAM; we always map the RAM there. - */ - memory_region_init_alias(lowram, NULL, "vexpress.lowmem", ram, 0, low_ram_size); - memory_region_add_subregion(sysmem, 0x0, lowram); - memory_region_add_subregion(sysmem, 0x60000000, ram); - - /* 0x1e000000 A9MPCore (SCU) private memory region */ - init_cpus(cpu_model, "a9mpcore_priv", 0x1e000000, pic, vms->secure); - - /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */ - - /* 0x10020000 PL111 CLCD (daughterboard) */ - sysbus_create_simple("pl111", 0x10020000, pic[44]); - - /* 0x10060000 AXI RAM */ - /* 0x100e0000 PL341 Dynamic Memory Controller */ - /* 0x100e1000 PL354 Static Memory Controller */ - /* 0x100e2000 System Configuration Controller */ - - sysbus_create_simple("sp804", 0x100e4000, pic[48]); - /* 0x100e5000 SP805 Watchdog module */ - /* 0x100e6000 BP147 TrustZone Protection Controller */ - /* 0x100e9000 PL301 'Fast' AXI matrix */ - /* 0x100ea000 PL301 'Slow' AXI matrix */ - /* 0x100ec000 TrustZone Address Space Controller */ - /* 0x10200000 CoreSight debug APB */ - /* 0x1e00a000 PL310 L2 Cache Controller */ - sysbus_create_varargs("l2x0", 0x1e00a000, NULL); -} - -/* Voltage values for SYS_CFG_VOLT daughterboard registers; - * values are in microvolts. - */ -static const uint32_t a9_voltages[] = { - 1000000, /* VD10 : 1.0V : SoC internal logic voltage */ - 1000000, /* VD10_S2 : 1.0V : PL310, L2 cache, RAM, non-PL310 logic */ - 1000000, /* VD10_S3 : 1.0V : Cortex-A9, cores, MPEs, SCU, PL310 logic */ - 1800000, /* VCC1V8 : 1.8V : DDR2 SDRAM, test chip DDR2 I/O supply */ - 900000, /* DDR2VTT : 0.9V : DDR2 SDRAM VTT termination voltage */ - 3300000, /* VCC3V3 : 3.3V : local board supply for misc external logic */ -}; - -/* Reset values for daughterboard oscillators (in Hz) */ -static const uint32_t a9_clocks[] = { - 45000000, /* AMBA AXI ACLK: 45MHz */ - 23750000, /* daughterboard CLCD clock: 23.75MHz */ - 66670000, /* Test chip reference clock: 66.67MHz */ -}; - -static VEDBoardInfo a9_daughterboard = { - .motherboard_map = motherboard_legacy_map, - .loader_start = 0x60000000, - .gic_cpu_if_addr = 0x1e000100, - .proc_id = 0x0c000191, - .num_voltage_sensors = ARRAY_SIZE(a9_voltages), - .voltages = a9_voltages, - .num_clocks = ARRAY_SIZE(a9_clocks), - .clocks = a9_clocks, - .init = a9_daughterboard_init, -}; - -static void a15_daughterboard_init(const VexpressMachineState *vms, - ram_addr_t ram_size, - const char *cpu_model, - qemu_irq *pic) -{ - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *sram = g_new(MemoryRegion, 1); - - if (!cpu_model) { - cpu_model = "cortex-a15"; - } - - { - /* We have to use a separate 64 bit variable here to avoid the gcc - * "comparison is always false due to limited range of data type" - * warning if we are on a host where ram_addr_t is 32 bits. - */ - uint64_t rsz = ram_size; - if (rsz > (30ULL * 1024 * 1024 * 1024)) { - fprintf(stderr, "vexpress-a15: cannot model more than 30GB RAM\n"); - exit(1); - } - } - - memory_region_allocate_system_memory(ram, NULL, "vexpress.highmem", - ram_size); - /* RAM is from 0x80000000 upwards; there is no low-memory alias for it. */ - memory_region_add_subregion(sysmem, 0x80000000, ram); - - /* 0x2c000000 A15MPCore private memory region (GIC) */ - init_cpus(cpu_model, "a15mpcore_priv", 0x2c000000, pic, vms->secure); - - /* A15 daughterboard peripherals: */ - - /* 0x20000000: CoreSight interfaces: not modelled */ - /* 0x2a000000: PL301 AXI interconnect: not modelled */ - /* 0x2a420000: SCC: not modelled */ - /* 0x2a430000: system counter: not modelled */ - /* 0x2b000000: HDLCD controller: not modelled */ - /* 0x2b060000: SP805 watchdog: not modelled */ - /* 0x2b0a0000: PL341 dynamic memory controller: not modelled */ - /* 0x2e000000: system SRAM */ - memory_region_init_ram(sram, NULL, "vexpress.a15sram", 0x10000, - &error_fatal); - vmstate_register_ram_global(sram); - memory_region_add_subregion(sysmem, 0x2e000000, sram); - - /* 0x7ffb0000: DMA330 DMA controller: not modelled */ - /* 0x7ffd0000: PL354 static memory controller: not modelled */ -} - -static const uint32_t a15_voltages[] = { - 900000, /* Vcore: 0.9V : CPU core voltage */ -}; - -static const uint32_t a15_clocks[] = { - 60000000, /* OSCCLK0: 60MHz : CPU_CLK reference */ - 0, /* OSCCLK1: reserved */ - 0, /* OSCCLK2: reserved */ - 0, /* OSCCLK3: reserved */ - 40000000, /* OSCCLK4: 40MHz : external AXI master clock */ - 23750000, /* OSCCLK5: 23.75MHz : HDLCD PLL reference */ - 50000000, /* OSCCLK6: 50MHz : static memory controller clock */ - 60000000, /* OSCCLK7: 60MHz : SYSCLK reference */ - 40000000, /* OSCCLK8: 40MHz : DDR2 PLL reference */ -}; - -static VEDBoardInfo a15_daughterboard = { - .motherboard_map = motherboard_aseries_map, - .loader_start = 0x80000000, - .gic_cpu_if_addr = 0x2c002000, - .proc_id = 0x14000237, - .num_voltage_sensors = ARRAY_SIZE(a15_voltages), - .voltages = a15_voltages, - .num_clocks = ARRAY_SIZE(a15_clocks), - .clocks = a15_clocks, - .init = a15_daughterboard_init, -}; - -static int add_virtio_mmio_node(void *fdt, uint32_t acells, uint32_t scells, - hwaddr addr, hwaddr size, uint32_t intc, - int irq) -{ - /* Add a virtio_mmio node to the device tree blob: - * virtio_mmio@ADDRESS { - * compatible = "virtio,mmio"; - * reg = ; - * interrupt-parent = <&intc>; - * interrupts = <0, irq, 1>; - * } - * (Note that the format of the interrupts property is dependent on the - * interrupt controller that interrupt-parent points to; these are for - * the ARM GIC and indicate an SPI interrupt, rising-edge-triggered.) - */ - int rc; - char *nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, addr); - - rc = qemu_fdt_add_subnode(fdt, nodename); - rc |= qemu_fdt_setprop_string(fdt, nodename, - "compatible", "virtio,mmio"); - rc |= qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", - acells, addr, scells, size); - qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", intc); - qemu_fdt_setprop_cells(fdt, nodename, "interrupts", 0, irq, 1); - g_free(nodename); - if (rc) { - return -1; - } - return 0; -} - -static uint32_t find_int_controller(void *fdt) -{ - /* Find the FDT node corresponding to the interrupt controller - * for virtio-mmio devices. We do this by scanning the fdt for - * a node with the right compatibility, since we know there is - * only one GIC on a vexpress board. - * We return the phandle of the node, or 0 if none was found. - */ - const char *compat = "arm,cortex-a9-gic"; - int offset; - - offset = fdt_node_offset_by_compatible(fdt, -1, compat); - if (offset >= 0) { - return fdt_get_phandle(fdt, offset); - } - return 0; -} - -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", - 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 - * happen with older device tree blobs. - */ - fprintf(stderr, "QEMU: warning: couldn't find interrupt controller in " - "dtb; will not include virtio-mmio devices in the dtb.\n"); - } else { - int i; - const hwaddr *map = daughterboard->motherboard_map; - - /* We iterate backwards here because adding nodes - * to the dtb puts them in last-first. - */ - for (i = NUM_VIRTIO_TRANSPORTS - 1; i >= 0; i--) { - add_virtio_mmio_node(fdt, acells, scells, - map[VE_VIRTIO] + 0x200 * i, - 0x200, intc, 40 + i); - } - } -} - - -/* Open code a private version of pflash registration since we - * need to set non-default device width for VExpress platform. - */ -static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name, - DriveInfo *di) -{ - DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); - - if (di) { - qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(di), - &error_abort); - } - - qdev_prop_set_uint32(dev, "num-blocks", - VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE); - qdev_prop_set_uint64(dev, "sector-length", VEXPRESS_FLASH_SECT_SIZE); - qdev_prop_set_uint8(dev, "width", 4); - qdev_prop_set_uint8(dev, "device-width", 2); - qdev_prop_set_bit(dev, "big-endian", false); - qdev_prop_set_uint16(dev, "id0", 0x89); - qdev_prop_set_uint16(dev, "id1", 0x18); - qdev_prop_set_uint16(dev, "id2", 0x00); - qdev_prop_set_uint16(dev, "id3", 0x00); - qdev_prop_set_string(dev, "name", name); - qdev_init_nofail(dev); - - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - return OBJECT_CHECK(pflash_t, (dev), "cfi.pflash01"); -} - -static void vexpress_common_init(MachineState *machine) -{ - VexpressMachineState *vms = VEXPRESS_MACHINE(machine); - VexpressMachineClass *vmc = VEXPRESS_MACHINE_GET_CLASS(machine); - VEDBoardInfo *daughterboard = vmc->daughterboard; - DeviceState *dev, *sysctl, *pl041; - qemu_irq pic[64]; - uint32_t sys_id; - DriveInfo *dinfo; - pflash_t *pflash0; - ram_addr_t vram_size, sram_size; - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *vram = g_new(MemoryRegion, 1); - MemoryRegion *sram = g_new(MemoryRegion, 1); - MemoryRegion *flashalias = g_new(MemoryRegion, 1); - MemoryRegion *flash0mem; - const hwaddr *map = daughterboard->motherboard_map; - int i; - - daughterboard->init(vms, machine->ram_size, machine->cpu_model, pic); - - /* - * If a bios file was provided, attempt to map it into memory - */ - if (bios_name) { - char *fn; - int image_size; - - if (drive_get(IF_PFLASH, 0, 0)) { - error_report("The contents of the first flash device may be " - "specified with -bios or with -drive if=pflash... " - "but you cannot use both options at once"); - exit(1); - } - fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (!fn) { - error_report("Could not find ROM image '%s'", bios_name); - exit(1); - } - image_size = load_image_targphys(fn, map[VE_NORFLASH0], - VEXPRESS_FLASH_SIZE); - g_free(fn); - if (image_size < 0) { - error_report("Could not load ROM image '%s'", bios_name); - exit(1); - } - } - - /* Motherboard peripherals: the wiring is the same but the - * addresses vary between the legacy and A-Series memory maps. - */ - - sys_id = 0x1190f500; - - sysctl = qdev_create(NULL, "realview_sysctl"); - qdev_prop_set_uint32(sysctl, "sys_id", sys_id); - qdev_prop_set_uint32(sysctl, "proc_id", daughterboard->proc_id); - qdev_prop_set_uint32(sysctl, "len-db-voltage", - daughterboard->num_voltage_sensors); - for (i = 0; i < daughterboard->num_voltage_sensors; i++) { - char *propname = g_strdup_printf("db-voltage[%d]", i); - qdev_prop_set_uint32(sysctl, propname, daughterboard->voltages[i]); - g_free(propname); - } - qdev_prop_set_uint32(sysctl, "len-db-clock", - daughterboard->num_clocks); - for (i = 0; i < daughterboard->num_clocks; i++) { - char *propname = g_strdup_printf("db-clock[%d]", i); - qdev_prop_set_uint32(sysctl, propname, daughterboard->clocks[i]); - g_free(propname); - } - qdev_init_nofail(sysctl); - sysbus_mmio_map(SYS_BUS_DEVICE(sysctl), 0, map[VE_SYSREGS]); - - /* VE_SP810: not modelled */ - /* VE_SERIALPCI: not modelled */ - - pl041 = qdev_create(NULL, "pl041"); - qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512); - qdev_init_nofail(pl041); - sysbus_mmio_map(SYS_BUS_DEVICE(pl041), 0, map[VE_PL041]); - sysbus_connect_irq(SYS_BUS_DEVICE(pl041), 0, pic[11]); - - dev = sysbus_create_varargs("pl181", map[VE_MMCI], pic[9], pic[10], NULL); - /* Wire up MMC card detect and read-only signals */ - qdev_connect_gpio_out(dev, 0, - qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_WPROT)); - qdev_connect_gpio_out(dev, 1, - qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_CARDIN)); - - sysbus_create_simple("pl050_keyboard", map[VE_KMI0], pic[12]); - sysbus_create_simple("pl050_mouse", map[VE_KMI1], pic[13]); - - sysbus_create_simple("pl011", map[VE_UART0], pic[5]); - sysbus_create_simple("pl011", map[VE_UART1], pic[6]); - sysbus_create_simple("pl011", map[VE_UART2], pic[7]); - sysbus_create_simple("pl011", map[VE_UART3], pic[8]); - - sysbus_create_simple("sp804", map[VE_TIMER01], pic[2]); - sysbus_create_simple("sp804", map[VE_TIMER23], pic[3]); - - /* VE_SERIALDVI: not modelled */ - - sysbus_create_simple("pl031", map[VE_RTC], pic[4]); /* RTC */ - - /* VE_COMPACTFLASH: not modelled */ - - sysbus_create_simple("pl111", map[VE_CLCD], pic[14]); - - dinfo = drive_get_next(IF_PFLASH); - pflash0 = ve_pflash_cfi01_register(map[VE_NORFLASH0], "vexpress.flash0", - dinfo); - if (!pflash0) { - fprintf(stderr, "vexpress: error registering flash 0.\n"); - exit(1); - } - - if (map[VE_NORFLASHALIAS] != -1) { - /* Map flash 0 as an alias into low memory */ - flash0mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(pflash0), 0); - memory_region_init_alias(flashalias, NULL, "vexpress.flashalias", - flash0mem, 0, VEXPRESS_FLASH_SIZE); - memory_region_add_subregion(sysmem, map[VE_NORFLASHALIAS], flashalias); - } - - dinfo = drive_get_next(IF_PFLASH); - if (!ve_pflash_cfi01_register(map[VE_NORFLASH1], "vexpress.flash1", - dinfo)) { - fprintf(stderr, "vexpress: error registering flash 1.\n"); - exit(1); - } - - sram_size = 0x2000000; - memory_region_init_ram(sram, NULL, "vexpress.sram", sram_size, - &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_fatal); - vmstate_register_ram_global(vram); - memory_region_add_subregion(sysmem, map[VE_VIDEORAM], vram); - - /* 0x4e000000 LAN9118 Ethernet */ - if (nd_table[0].used) { - lan9118_init(&nd_table[0], map[VE_ETHERNET], pic[15]); - } - - /* VE_USB: not modelled */ - - /* VE_DAPROM: not modelled */ - - /* Create mmio transports, so the user can create virtio backends - * (which will be automatically plugged in to the transports). If - * no backend is created the transport will just sit harmlessly idle. - */ - for (i = 0; i < NUM_VIRTIO_TRANSPORTS; i++) { - sysbus_create_simple("virtio-mmio", map[VE_VIRTIO] + 0x200 * i, - pic[40 + i]); - } - - daughterboard->bootinfo.ram_size = machine->ram_size; - daughterboard->bootinfo.kernel_filename = machine->kernel_filename; - daughterboard->bootinfo.kernel_cmdline = machine->kernel_cmdline; - daughterboard->bootinfo.initrd_filename = machine->initrd_filename; - daughterboard->bootinfo.nb_cpus = smp_cpus; - daughterboard->bootinfo.board_id = VEXPRESS_BOARD_ID; - daughterboard->bootinfo.loader_start = daughterboard->loader_start; - daughterboard->bootinfo.smp_loader_start = map[VE_SRAM]; - daughterboard->bootinfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30; - daughterboard->bootinfo.gic_cpu_if_addr = daughterboard->gic_cpu_if_addr; - daughterboard->bootinfo.modify_dtb = vexpress_modify_dtb; - /* Indicate that when booting Linux we should be in secure state */ - daughterboard->bootinfo.secure_boot = true; - arm_load_kernel(ARM_CPU(first_cpu), &daughterboard->bootinfo); -} - -static bool vexpress_get_secure(Object *obj, Error **errp) -{ - VexpressMachineState *vms = VEXPRESS_MACHINE(obj); - - return vms->secure; -} - -static void vexpress_set_secure(Object *obj, bool value, Error **errp) -{ - VexpressMachineState *vms = VEXPRESS_MACHINE(obj); - - vms->secure = value; -} - -static void vexpress_instance_init(Object *obj) -{ - VexpressMachineState *vms = VEXPRESS_MACHINE(obj); - - /* EL3 is enabled by default on vexpress */ - vms->secure = true; - object_property_add_bool(obj, "secure", vexpress_get_secure, - vexpress_set_secure, NULL); - object_property_set_description(obj, "secure", - "Set on/off to enable/disable the ARM " - "Security Extensions (TrustZone)", - NULL); -} - -static void vexpress_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "ARM Versatile Express"; - mc->init = vexpress_common_init; - mc->block_default_type = IF_SCSI; - mc->max_cpus = 4; -} - -static void vexpress_a9_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - VexpressMachineClass *vmc = VEXPRESS_MACHINE_CLASS(oc); - - mc->desc = "ARM Versatile Express for Cortex-A9"; - - vmc->daughterboard = &a9_daughterboard; -} - -static void vexpress_a15_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - VexpressMachineClass *vmc = VEXPRESS_MACHINE_CLASS(oc); - - mc->desc = "ARM Versatile Express for Cortex-A15"; - - vmc->daughterboard = &a15_daughterboard; -} - -static const TypeInfo vexpress_info = { - .name = TYPE_VEXPRESS_MACHINE, - .parent = TYPE_MACHINE, - .abstract = true, - .instance_size = sizeof(VexpressMachineState), - .instance_init = vexpress_instance_init, - .class_size = sizeof(VexpressMachineClass), - .class_init = vexpress_class_init, -}; - -static const TypeInfo vexpress_a9_info = { - .name = TYPE_VEXPRESS_A9_MACHINE, - .parent = TYPE_VEXPRESS_MACHINE, - .class_init = vexpress_a9_class_init, -}; - -static const TypeInfo vexpress_a15_info = { - .name = TYPE_VEXPRESS_A15_MACHINE, - .parent = TYPE_VEXPRESS_MACHINE, - .class_init = vexpress_a15_class_init, -}; - -static void vexpress_machine_init(void) -{ - type_register_static(&vexpress_info); - type_register_static(&vexpress_a9_info); - type_register_static(&vexpress_a15_info); -} - -type_init(vexpress_machine_init); diff --git a/qemu/hw/arm/virt-acpi-build.c b/qemu/hw/arm/virt-acpi-build.c deleted file mode 100644 index f51fe396c..000000000 --- a/qemu/hw/arm/virt-acpi-build.c +++ /dev/null @@ -1,755 +0,0 @@ -/* Support for generating ACPI tables and passing them to Guests - * - * ARM virt ACPI generation - * - * Copyright (C) 2008-2010 Kevin O'Connor - * Copyright (C) 2006 Fabrice Bellard - * Copyright (C) 2013 Red Hat Inc - * - * Author: Michael S. Tsirkin - * - * Copyright (c) 2015 HUAWEI TECHNOLOGIES CO.,LTD. - * - * Author: Shannon Zhao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "hw/arm/virt-acpi-build.h" -#include "qemu/bitmap.h" -#include "trace.h" -#include "qom/cpu.h" -#include "target-arm/cpu.h" -#include "hw/acpi/acpi-defs.h" -#include "hw/acpi/acpi.h" -#include "hw/nvram/fw_cfg.h" -#include "hw/acpi/bios-linker-loader.h" -#include "hw/loader.h" -#include "hw/hw.h" -#include "hw/acpi/aml-build.h" -#include "hw/pci/pcie_host.h" -#include "hw/pci/pci.h" - -#define ARM_SPI_BASE 32 -#define ACPI_POWER_BUTTON_DEVICE "PWRB" - -static void acpi_dsdt_add_cpus(Aml *scope, int smp_cpus) -{ - uint16_t i; - - for (i = 0; i < smp_cpus; i++) { - Aml *dev = aml_device("C%03x", i); - aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0007"))); - aml_append(dev, aml_name_decl("_UID", aml_int(i))); - aml_append(scope, dev); - } -} - -static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, - uint32_t uart_irq) -{ - Aml *dev = aml_device("COM0"); - aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0011"))); - aml_append(dev, aml_name_decl("_UID", aml_int(0))); - - Aml *crs = aml_resource_template(); - aml_append(crs, aml_memory32_fixed(uart_memmap->base, - uart_memmap->size, AML_READ_WRITE)); - aml_append(crs, - aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, - 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 - * in the SPCR table, i.e. SPCR.base_address.address == _ADR. - */ - aml_append(dev, aml_name_decl("_ADR", aml_int(uart_memmap->base))); - - aml_append(scope, dev); -} - -static void acpi_dsdt_add_fw_cfg(Aml *scope, const MemMapEntry *fw_cfg_memmap) -{ - 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(fw_cfg_memmap->base, - fw_cfg_memmap->size, AML_READ_WRITE)); - aml_append(dev, aml_name_decl("_CRS", crs)); - aml_append(scope, dev); -} - -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 / 2; - - dev = aml_device("FLS0"); - aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015"))); - aml_append(dev, aml_name_decl("_UID", aml_int(0))); - - crs = aml_resource_template(); - aml_append(crs, aml_memory32_fixed(base, size, AML_READ_WRITE)); - aml_append(dev, aml_name_decl("_CRS", crs)); - aml_append(scope, dev); - - dev = aml_device("FLS1"); - aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015"))); - aml_append(dev, aml_name_decl("_UID", aml_int(1))); - crs = aml_resource_template(); - aml_append(crs, aml_memory32_fixed(base + size, size, AML_READ_WRITE)); - aml_append(dev, aml_name_decl("_CRS", crs)); - aml_append(scope, dev); -} - -static void acpi_dsdt_add_virtio(Aml *scope, - const MemMapEntry *virtio_mmio_memmap, - uint32_t mmio_irq, int num) -{ - hwaddr base = virtio_mmio_memmap->base; - hwaddr size = virtio_mmio_memmap->size; - 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))); - - Aml *crs = aml_resource_template(); - 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, 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, - uint32_t irq, bool use_highmem) -{ - Aml *method, *crs, *ifctx, *UUID, *ifctx1, *elsectx, *buf; - int i, bus_no; - hwaddr base_mmio = memmap[VIRT_PCIE_MMIO].base; - hwaddr size_mmio = memmap[VIRT_PCIE_MMIO].size; - hwaddr base_pio = memmap[VIRT_PCIE_PIO].base; - hwaddr size_pio = memmap[VIRT_PCIE_PIO].size; - hwaddr base_ecam = memmap[VIRT_PCIE_ECAM].base; - hwaddr size_ecam = memmap[VIRT_PCIE_ECAM].size; - int nr_pcie_buses = size_ecam / PCIE_MMCFG_SIZE_MIN; - - Aml *dev = aml_device("%s", "PCI0"); - aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A08"))); - aml_append(dev, aml_name_decl("_CID", aml_string("PNP0A03"))); - aml_append(dev, aml_name_decl("_SEG", aml_int(0))); - aml_append(dev, aml_name_decl("_BBN", aml_int(0))); - 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); - for (bus_no = 0; bus_no < nr_pcie_buses; bus_no++) { - for (i = 0; i < PCI_NUM_PINS; i++) { - int gsi = (i + bus_no) % PCI_NUM_PINS; - Aml *pkg = aml_package(4); - aml_append(pkg, aml_int((bus_no << 16) | 0xFFFF)); - aml_append(pkg, aml_int(i)); - aml_append(pkg, aml_name("GSI%d", gsi)); - aml_append(pkg, aml_int(0)); - aml_append(rt_pkg, pkg); - } - } - aml_append(dev, aml_name_decl("_PRT", rt_pkg)); - - /* 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, &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, &irqs, 1)); - aml_append(dev_gsi, aml_name_decl("_CRS", crs)); - method = aml_method("_SRS", 1, AML_NOTSERIALIZED); - aml_append(dev_gsi, method); - aml_append(dev, dev_gsi); - } - - 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, AML_NOTSERIALIZED); - Aml *rbuf = aml_resource_template(); - aml_append(rbuf, - aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, - 0x0000, 0x0000, nr_pcie_buses - 1, 0x0000, - nr_pcie_buses)); - aml_append(rbuf, - aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, - AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000, base_mmio, - base_mmio + size_mmio - 1, 0x0000, size_mmio)); - aml_append(rbuf, - aml_dword_io(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, - 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); - - /* 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, AML_NOTSERIALIZED); - aml_append(method, - aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1")); - - /* PCI Firmware Specification 3.0 - * 4.5.1. _OSC Interface for PCI Host Bridge Devices - * The _OSC interface for a PCI/PCI-X/PCI Express hierarchy is - * identified by the Universal Unique IDentifier (UUID) - * 33DB4D5B-1FF7-401C-9657-7441C03DD766 - */ - UUID = aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766"); - ifctx = aml_if(aml_equal(aml_arg(0), UUID)); - aml_append(ifctx, - aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2")); - aml_append(ifctx, - aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3")); - aml_append(ifctx, aml_store(aml_name("CDW2"), aml_name("SUPP"))); - aml_append(ifctx, aml_store(aml_name("CDW3"), aml_name("CTRL"))); - 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), 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), NULL), - aml_name("CDW1"))); - aml_append(ifctx, ifctx1); - - aml_append(ifctx, aml_store(aml_name("CTRL"), aml_name("CDW3"))); - aml_append(ifctx, aml_return(aml_arg(3))); - aml_append(method, ifctx); - - elsectx = aml_else(); - aml_append(elsectx, aml_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, AML_NOTSERIALIZED); - - /* PCI Firmware Specification 3.0 - * 4.6.1. _DSM for PCI Express Slot Information - * The UUID in _DSM in this context is - * {E5C937D0-3553-4D7A-9117-EA4D19C3434D} - */ - UUID = aml_touuid("E5C937D0-3553-4D7A-9117-EA4D19C3434D"); - ifctx = aml_if(aml_equal(aml_arg(0), UUID)); - ifctx1 = aml_if(aml_equal(aml_arg(2), aml_int(0))); - uint8_t byte_list[1] = {1}; - buf = aml_buffer(1, byte_list); - aml_append(ifctx1, aml_return(buf)); - aml_append(ifctx, ifctx1); - aml_append(method, ifctx); - - byte_list[0] = 0; - buf = aml_buffer(1, byte_list); - aml_append(method, aml_return(buf)); - aml_append(dev, method); - - Aml *dev_rp0 = aml_device("%s", "RP0"); - aml_append(dev_rp0, aml_name_decl("_ADR", aml_int(0))); - aml_append(dev, dev_rp0); - 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) -{ - AcpiRsdpDescriptor *rsdp = acpi_data_push(rsdp_table, sizeof *rsdp); - - bios_linker_loader_alloc(linker, ACPI_BUILD_RSDP_FILE, 16, - true /* fseg memory */); - - memcpy(&rsdp->signature, "RSD PTR ", sizeof(rsdp->signature)); - memcpy(rsdp->oem_id, ACPI_BUILD_APPNAME6, sizeof(rsdp->oem_id)); - rsdp->length = cpu_to_le32(sizeof(*rsdp)); - rsdp->revision = 0x02; - - /* Point to RSDT */ - rsdp->rsdt_physical_address = cpu_to_le32(rsdt); - /* Address to be filled by Guest linker */ - bios_linker_loader_add_pointer(linker, ACPI_BUILD_RSDP_FILE, - ACPI_BUILD_TABLE_FILE, - rsdp_table, &rsdp->rsdt_physical_address, - sizeof rsdp->rsdt_physical_address); - rsdp->checksum = 0; - /* Checksum to be filled by Guest linker */ - bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE, - rsdp_table, rsdp, sizeof *rsdp, - &rsdp->checksum); - - return rsdp_table; -} - -static void -build_spcr(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) -{ - AcpiSerialPortConsoleRedirection *spcr; - const MemMapEntry *uart_memmap = &guest_info->memmap[VIRT_UART]; - int irq = guest_info->irqmap[VIRT_UART] + ARM_SPI_BASE; - - spcr = acpi_data_push(table_data, sizeof(*spcr)); - - spcr->interface_type = 0x3; /* ARM PL011 UART */ - - spcr->base_address.space_id = AML_SYSTEM_MEMORY; - spcr->base_address.bit_width = 8; - spcr->base_address.bit_offset = 0; - spcr->base_address.access_width = 1; - spcr->base_address.address = cpu_to_le64(uart_memmap->base); - - spcr->interrupt_types = (1 << 3); /* Bit[3] ARMH GIC interrupt */ - spcr->gsi = cpu_to_le32(irq); /* Global System Interrupt */ - - spcr->baud = 3; /* Baud Rate: 3 = 9600 */ - spcr->parity = 0; /* No Parity */ - spcr->stopbits = 1; /* 1 Stop bit */ - spcr->flowctrl = (1 << 1); /* Bit[1] = RTS/CTS hardware flow control */ - spcr->term_type = 0; /* Terminal Type: 0 = VT100 */ - - 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, - NULL, NULL); -} - -static void -build_mcfg(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) -{ - AcpiTableMcfg *mcfg; - const MemMapEntry *memmap = guest_info->memmap; - int len = sizeof(*mcfg) + sizeof(mcfg->allocation[0]); - - mcfg = acpi_data_push(table_data, len); - mcfg->allocation[0].address = cpu_to_le64(memmap[VIRT_PCIE_ECAM].base); - - /* Only a single allocation so no need to play with segments */ - mcfg->allocation[0].pci_segment = cpu_to_le16(0); - mcfg->allocation[0].start_bus_number = 0; - 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, NULL, NULL); -} - -/* GTDT */ -static void -build_gtdt(GArray *table_data, GArray *linker) -{ - int gtdt_start = table_data->len; - AcpiGenericTimerTable *gtdt; - - gtdt = acpi_data_push(table_data, sizeof *gtdt); - /* The interrupt values are the same with the device tree when adding 16 */ - gtdt->secure_el1_interrupt = ARCH_TIMER_S_EL1_IRQ + 16; - 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 | ACPI_GTDT_ALWAYS_ON; - - gtdt->virtual_timer_interrupt = ARCH_TIMER_VIRT_IRQ + 16; - gtdt->virtual_timer_flags = ACPI_EDGE_SENSITIVE; - - gtdt->non_secure_el2_interrupt = ARCH_TIMER_NS_EL2_IRQ + 16; - gtdt->non_secure_el2_flags = ACPI_EDGE_SENSITIVE; - - build_header(linker, table_data, - (void *)(table_data->data + gtdt_start), "GTDT", - table_data->len - gtdt_start, 2, NULL, NULL); -} - -/* MADT */ -static void -build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) -{ - int madt_start = table_data->len; - const MemMapEntry *memmap = guest_info->memmap; - const int *irqmap = guest_info->irqmap; - AcpiMultipleApicTable *madt; - AcpiMadtGenericDistributor *gicd; - AcpiMadtGenericMsiFrame *gic_msi; - int i; - - 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); - if (guest_info->gic_version == 2) { - gicc->base_address = memmap[VIRT_GIC_CPU].base; - } - gicc->cpu_interface_number = i; - gicc->arm_mpidr = armcpu->mp_affinity; - gicc->uid = i; - gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED); - } - - 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, NULL, NULL); -} - -/* FADT */ -static void -build_fadt(GArray *table_data, GArray *linker, unsigned dsdt) -{ - AcpiFadtDescriptorRev5_1 *fadt = acpi_data_push(table_data, sizeof(*fadt)); - - /* Hardware Reduced = 1 and use PSCI 0.2+ and with HVC */ - fadt->flags = cpu_to_le32(1 << ACPI_FADT_F_HW_REDUCED_ACPI); - fadt->arm_boot_flags = cpu_to_le16((1 << ACPI_FADT_ARM_USE_PSCI_G_0_2) | - (1 << ACPI_FADT_ARM_PSCI_USE_HVC)); - - /* ACPI v5.1 (fadt->revision.fadt->minor_revision) */ - fadt->minor_revision = 0x1; - - fadt->dsdt = cpu_to_le32(dsdt); - /* DSDT address to be filled by Guest linker */ - bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, - ACPI_BUILD_TABLE_FILE, - table_data, &fadt->dsdt, - sizeof fadt->dsdt); - - build_header(linker, table_data, - (void *)fadt, "FACP", sizeof(*fadt), 5, NULL, NULL); -} - -/* DSDT */ -static void -build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) -{ - Aml *scope, *dsdt; - const MemMapEntry *memmap = guest_info->memmap; - const int *irqmap = guest_info->irqmap; - - dsdt = init_aml_allocator(); - /* 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_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), - 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); - - /* copy AML table into ACPI tables blob and patch header there */ - 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, NULL, NULL); - free_aml_allocator(); -} - -typedef -struct AcpiBuildState { - /* Copy of table in RAM (for patching). */ - MemoryRegion *table_mr; - MemoryRegion *rsdp_mr; - MemoryRegion *linker_mr; - /* Is table patched? */ - bool patched; - VirtGuestInfo *guest_info; -} AcpiBuildState; - -static -void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) -{ - GArray *table_offsets; - unsigned dsdt, rsdt; - GArray *tables_blob = tables->table_data; - - table_offsets = g_array_new(false, true /* clear */, - sizeof(uint32_t)); - - bios_linker_loader_alloc(tables->linker, ACPI_BUILD_TABLE_FILE, - 64, false /* high memory */); - - /* - * The ACPI v5.1 tables for Hardware-reduced ACPI platform are: - * RSDP - * RSDT - * FADT - * GTDT - * MADT - * MCFG - * DSDT - */ - - /* DSDT is pointed to by FADT */ - dsdt = tables_blob->len; - build_dsdt(tables_blob, tables->linker, guest_info); - - /* FADT MADT GTDT MCFG SPCR pointed to by RSDT */ - acpi_add_table(table_offsets, tables_blob); - build_fadt(tables_blob, tables->linker, dsdt); - - acpi_add_table(table_offsets, tables_blob); - build_madt(tables_blob, tables->linker, guest_info); - - acpi_add_table(table_offsets, tables_blob); - build_gtdt(tables_blob, tables->linker); - - acpi_add_table(table_offsets, tables_blob); - build_mcfg(tables_blob, tables->linker, guest_info); - - acpi_add_table(table_offsets, tables_blob); - build_spcr(tables_blob, tables->linker, guest_info); - - /* RSDT is pointed to by RSDP */ - rsdt = tables_blob->len; - 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); - - /* Cleanup memory that's no longer used. */ - g_array_free(table_offsets, true); -} - -static void acpi_ram_update(MemoryRegion *mr, GArray *data) -{ - uint32_t size = acpi_data_len(data); - - /* Make sure RAM size is correct - in case it got changed - * e.g. by migration */ - memory_region_ram_resize(mr, size, &error_abort); - - memcpy(memory_region_get_ram_ptr(mr), data->data, size); - memory_region_set_dirty(mr, 0, size); -} - -static void virt_acpi_build_update(void *build_opaque) -{ - AcpiBuildState *build_state = build_opaque; - AcpiBuildTables tables; - - /* No state to update or already patched? Nothing to do. */ - if (!build_state || build_state->patched) { - return; - } - build_state->patched = true; - - acpi_build_tables_init(&tables); - - virt_acpi_build(build_state->guest_info, &tables); - - acpi_ram_update(build_state->table_mr, tables.table_data); - acpi_ram_update(build_state->rsdp_mr, tables.rsdp); - acpi_ram_update(build_state->linker_mr, tables.linker); - - - acpi_build_tables_cleanup(&tables, true); -} - -static void virt_acpi_build_reset(void *build_opaque) -{ - AcpiBuildState *build_state = build_opaque; - build_state->patched = false; -} - -static MemoryRegion *acpi_add_rom_blob(AcpiBuildState *build_state, - GArray *blob, const char *name, - uint64_t max_size) -{ - return rom_add_blob(name, blob->data, acpi_data_len(blob), max_size, -1, - name, virt_acpi_build_update, build_state); -} - -static const VMStateDescription vmstate_virt_acpi_build = { - .name = "virt_acpi_build", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_BOOL(patched, AcpiBuildState), - VMSTATE_END_OF_LIST() - }, -}; - -void virt_acpi_setup(VirtGuestInfo *guest_info) -{ - AcpiBuildTables tables; - AcpiBuildState *build_state; - - if (!guest_info->fw_cfg) { - trace_virt_acpi_setup(); - return; - } - - if (!acpi_enabled) { - trace_virt_acpi_setup(); - return; - } - - build_state = g_malloc0(sizeof *build_state); - build_state->guest_info = guest_info; - - acpi_build_tables_init(&tables); - virt_acpi_build(build_state->guest_info, &tables); - - /* Now expose it all to Guest */ - build_state->table_mr = acpi_add_rom_blob(build_state, tables.table_data, - ACPI_BUILD_TABLE_FILE, - ACPI_BUILD_TABLE_MAX_SIZE); - assert(build_state->table_mr != NULL); - - 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, - tables.tcpalog->data, acpi_data_len(tables.tcpalog)); - - build_state->rsdp_mr = acpi_add_rom_blob(build_state, tables.rsdp, - ACPI_BUILD_RSDP_FILE, 0); - - qemu_register_reset(virt_acpi_build_reset, build_state); - virt_acpi_build_reset(build_state); - vmstate_register(NULL, 0, &vmstate_virt_acpi_build, build_state); - - /* Cleanup tables but don't free the memory: we track it - * in build_state. - */ - acpi_build_tables_cleanup(&tables, false); -} diff --git a/qemu/hw/arm/virt.c b/qemu/hw/arm/virt.c deleted file mode 100644 index 56d35c771..000000000 --- a/qemu/hw/arm/virt.c +++ /dev/null @@ -1,1435 +0,0 @@ -/* - * ARM mach-virt emulation - * - * Copyright (c) 2013 Linaro Limited - * - * 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 . - * - * Emulate a virtual board which works by passing Linux all the information - * it needs about what devices are present via the device tree. - * There are some restrictions about what we can do here: - * + we can only present devices whose Linux drivers will work based - * purely on the device tree with no platform data at all - * + we want to present a very stripped-down minimalist platform, - * both because this reduces the security attack surface from the guest - * and also because it reduces our exposure to being broken when - * the kernel updates its device tree bindings and requires further - * information in a device binding that we aren't providing. - * 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" -#include "hw/arm/virt.h" -#include "hw/devices.h" -#include "net/net.h" -#include "sysemu/block-backend.h" -#include "sysemu/device_tree.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" -#include "hw/boards.h" -#include "hw/loader.h" -#include "exec/address-spaces.h" -#include "qemu/bitops.h" -#include "qemu/error-report.h" -#include "hw/pci-host/gpex.h" -#include "hw/arm/virt-acpi-build.h" -#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 - -#define PLATFORM_BUS_NUM_IRQS 64 - -static ARMPlatformBusSystemParams platform_bus_params; - -typedef struct VirtBoardInfo { - struct arm_boot_info bootinfo; - const char *cpu_model; - const MemMapEntry *memmap; - const int *irqmap; - int smp_cpus; - void *fdt; - int fdt_size; - uint32_t clock_phandle; - uint32_t gic_phandle; - uint32_t v2m_phandle; - bool using_psci; -} VirtBoardInfo; - -typedef struct { - MachineClass parent; - VirtBoardInfo *daughterboard; -} VirtMachineClass; - -typedef struct { - MachineState parent; - bool secure; - bool highmem; - int32_t gic_version; -} VirtMachineState; - -#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) \ - OBJECT_GET_CLASS(VirtMachineClass, obj, TYPE_VIRT_MACHINE) -#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. - * 256MB..1GB is reserved for possible future PCI support (ie where the - * PCI memory window will go if we add a PCI host controller). - * 1GB and up is RAM (which may happily spill over into the - * high memory region beyond 4GB). - * This represents a compromise between how much RAM can be given to - * a 32 bit VM and leaving space for expansion and in particular for PCI. - * Note that devices should generally be placed at multiples of 0x10000, - * to accommodate guests using 64K pages. - */ -static const MemMapEntry a15memmap[] = { - /* Space up to 0x8000000 is reserved for a boot ROM */ - [VIRT_FLASH] = { 0, 0x08000000 }, - [VIRT_CPUPERIPHS] = { 0x08000000, 0x00020000 }, - /* GIC distributor and CPU interfaces sit inside the CPU peripheral space */ - [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, 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, 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 */ -}; - -static VirtBoardInfo machines[] = { - { - .cpu_model = "cortex-a15", - .memmap = a15memmap, - .irqmap = a15irqmap, - }, - { - .cpu_model = "cortex-a53", - .memmap = a15memmap, - .irqmap = a15irqmap, - }, - { - .cpu_model = "cortex-a57", - .memmap = a15memmap, - .irqmap = a15irqmap, - }, - { - .cpu_model = "host", - .memmap = a15memmap, - .irqmap = a15irqmap, - }, -}; - -static VirtBoardInfo *find_machine_info(const char *cpu) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(machines); i++) { - if (strcmp(cpu, machines[i].cpu_model) == 0) { - return &machines[i]; - } - } - return NULL; -} - -static void create_fdt(VirtBoardInfo *vbi) -{ - void *fdt = create_device_tree(&vbi->fdt_size); - - if (!fdt) { - error_report("create_device_tree() failed"); - exit(1); - } - - vbi->fdt = fdt; - - /* Header */ - qemu_fdt_setprop_string(fdt, "/", "compatible", "linux,dummy-virt"); - qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); - qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); - - /* - * /chosen and /memory nodes must exist for load_dtb - * to fill in necessary properties later - */ - qemu_fdt_add_subnode(fdt, "/chosen"); - qemu_fdt_add_subnode(fdt, "/memory"); - qemu_fdt_setprop_string(fdt, "/memory", "device_type", "memory"); - - /* Clock node, for the benefit of the UART. The kernel device tree - * binding documentation claims the PL011 node clock properties are - * optional but in practice if you omit them the kernel refuses to - * probe for the device. - */ - vbi->clock_phandle = qemu_fdt_alloc_phandle(fdt); - qemu_fdt_add_subnode(fdt, "/apb-pclk"); - qemu_fdt_setprop_string(fdt, "/apb-pclk", "compatible", "fixed-clock"); - qemu_fdt_setprop_cell(fdt, "/apb-pclk", "#clock-cells", 0x0); - qemu_fdt_setprop_cell(fdt, "/apb-pclk", "clock-frequency", 24000000); - qemu_fdt_setprop_string(fdt, "/apb-pclk", "clock-output-names", - "clk24mhz"); - qemu_fdt_setprop_cell(fdt, "/apb-pclk", "phandle", vbi->clock_phandle); - -} - -static void fdt_add_psci_node(const VirtBoardInfo *vbi) -{ - uint32_t cpu_suspend_fn; - uint32_t cpu_off_fn; - uint32_t cpu_on_fn; - uint32_t migrate_fn; - 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"; - qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp)); - - cpu_off_fn = QEMU_PSCI_0_2_FN_CPU_OFF; - if (arm_feature(&armcpu->env, ARM_FEATURE_AARCH64)) { - cpu_suspend_fn = QEMU_PSCI_0_2_FN64_CPU_SUSPEND; - cpu_on_fn = QEMU_PSCI_0_2_FN64_CPU_ON; - migrate_fn = QEMU_PSCI_0_2_FN64_MIGRATE; - } else { - cpu_suspend_fn = QEMU_PSCI_0_2_FN_CPU_SUSPEND; - cpu_on_fn = QEMU_PSCI_0_2_FN_CPU_ON; - migrate_fn = QEMU_PSCI_0_2_FN_MIGRATE; - } - } else { - qemu_fdt_setprop_string(fdt, "/psci", "compatible", "arm,psci"); - - cpu_suspend_fn = QEMU_PSCI_0_1_FN_CPU_SUSPEND; - cpu_off_fn = QEMU_PSCI_0_1_FN_CPU_OFF; - cpu_on_fn = QEMU_PSCI_0_1_FN_CPU_ON; - migrate_fn = QEMU_PSCI_0_1_FN_MIGRATE; - } - - /* We adopt the PSCI spec's nomenclature, and use 'conduit' to refer - * to the instruction that should be used to invoke PSCI functions. - * However, the device tree binding uses 'method' instead, so that is - * what we should use here. - */ - qemu_fdt_setprop_string(fdt, "/psci", "method", "hvc"); - - qemu_fdt_setprop_cell(fdt, "/psci", "cpu_suspend", cpu_suspend_fn); - qemu_fdt_setprop_cell(fdt, "/psci", "cpu_off", cpu_off_fn); - qemu_fdt_setprop_cell(fdt, "/psci", "cpu_on", cpu_on_fn); - qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn); -} - -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 - * they are edge-triggered. - */ - ARMCPU *armcpu; - uint32_t irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI; - - 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"); - - armcpu = ARM_CPU(qemu_get_cpu(0)); - if (arm_feature(&armcpu->env, ARM_FEATURE_V8)) { - const char compat[] = "arm,armv8-timer\0arm,armv7-timer"; - qemu_fdt_setprop(vbi->fdt, "/timer", "compatible", - compat, sizeof(compat)); - } else { - 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, - GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_VIRT_IRQ, irqflags, - GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_NS_EL2_IRQ, irqflags); -} - -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", addr_cells); - qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#size-cells", 0x0); - - for (cpu = vbi->smp_cpus - 1; cpu >= 0; cpu--) { - char *nodename = g_strdup_printf("/cpus/cpu@%d", cpu); - ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu)); - - qemu_fdt_add_subnode(vbi->fdt, nodename); - qemu_fdt_setprop_string(vbi->fdt, nodename, "device_type", "cpu"); - qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", - armcpu->dtb_compatible); - - if (vbi->using_psci && vbi->smp_cpus > 1) { - qemu_fdt_setprop_string(vbi->fdt, nodename, - "enable-method", "psci"); - } - - 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); - } -} - -static void fdt_add_v2m_gic_node(VirtBoardInfo *vbi) -{ - vbi->v2m_phandle = qemu_fdt_alloc_phandle(vbi->fdt); - qemu_fdt_add_subnode(vbi->fdt, "/intc/v2m"); - qemu_fdt_setprop_string(vbi->fdt, "/intc/v2m", "compatible", - "arm,gic-v2m-frame"); - qemu_fdt_setprop(vbi->fdt, "/intc/v2m", "msi-controller", NULL, 0); - qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc/v2m", "reg", - 2, vbi->memmap[VIRT_GIC_V2M].base, - 2, vbi->memmap[VIRT_GIC_V2M].size); - qemu_fdt_setprop_cell(vbi->fdt, "/intc/v2m", "phandle", vbi->v2m_phandle); -} - -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"); - qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#interrupt-cells", 3); - qemu_fdt_setprop(vbi->fdt, "/intc", "interrupt-controller", NULL, 0); - 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); -} - -static void create_v2m(VirtBoardInfo *vbi, qemu_irq *pic) -{ - int i; - int irq = vbi->irqmap[VIRT_GIC_V2M]; - DeviceState *dev; - - dev = qdev_create(NULL, "arm-gicv2m"); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vbi->memmap[VIRT_GIC_V2M].base); - qdev_prop_set_uint32(dev, "base-spi", irq); - qdev_prop_set_uint32(dev, "num-spi", NUM_GICV2M_SPIS); - qdev_init_nofail(dev); - - for (i = 0; i < NUM_GICV2M_SPIS; i++) { - sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]); - } - - fdt_add_v2m_gic_node(vbi); -} - -static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, int type, bool secure) -{ - /* We create a standalone GIC */ - DeviceState *gicdev; - SysBusDevice *gicbusdev; - const char *gictype; - int i; - - gictype = (type == 3) ? gicv3_class_name() : gic_class_name(); - - gicdev = qdev_create(NULL, gictype); - 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); - 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 - * the CPU's IRQ input. - */ - for (i = 0; i < smp_cpus; i++) { - DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); - 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. - */ - 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, - qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); - } - - for (i = 0; i < NUM_IRQS; i++) { - pic[i] = qdev_get_gpio_in(gicdev, i); - } - - fdt_add_gic_node(vbi, type); - - if (type == 2) { - create_v2m(vbi, pic); - } -} - -static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic, int uart, - MemoryRegion *mem) -{ - char *nodename; - 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); - - 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); - /* Note that we can't use setprop_string because of the embedded NUL */ - qemu_fdt_setprop(vbi->fdt, nodename, "compatible", - compat, sizeof(compat)); - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", - 2, base, 2, size); - qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts", - GIC_FDT_IRQ_TYPE_SPI, irq, - GIC_FDT_IRQ_FLAGS_LEVEL_HI); - qemu_fdt_setprop_cells(vbi->fdt, nodename, "clocks", - vbi->clock_phandle, vbi->clock_phandle); - qemu_fdt_setprop(vbi->fdt, nodename, "clock-names", - clocknames, sizeof(clocknames)); - - 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); -} - -static void create_rtc(const VirtBoardInfo *vbi, qemu_irq *pic) -{ - char *nodename; - hwaddr base = vbi->memmap[VIRT_RTC].base; - hwaddr size = vbi->memmap[VIRT_RTC].size; - int irq = vbi->irqmap[VIRT_RTC]; - const char compat[] = "arm,pl031\0arm,primecell"; - - sysbus_create_simple("pl031", base, pic[irq]); - - nodename = g_strdup_printf("/pl031@%" PRIx64, base); - qemu_fdt_add_subnode(vbi->fdt, nodename); - qemu_fdt_setprop(vbi->fdt, nodename, "compatible", compat, sizeof(compat)); - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", - 2, base, 2, size); - 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"); - 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; - hwaddr size = vbi->memmap[VIRT_MMIO].size; - - /* We create the transports in forwards order. Since qbus_realize() - * prepends (not appends) new child buses, the incrementing loop below will - * create a list of virtio-mmio buses with decreasing base addresses. - * - * When a -device option is processed from the command line, - * qbus_find_recursive() picks the next free virtio-mmio bus in forwards - * order. The upshot is that -device options in increasing command line - * order are mapped to virtio-mmio buses with decreasing base addresses. - * - * When this code was originally written, that arrangement ensured that the - * guest Linux kernel would give the lowest "name" (/dev/vda, eth0, etc) to - * the first -device on the command line. (The end-to-end order is a - * function of this loop, qbus_realize(), qbus_find_recursive(), and the - * guest kernel's name-to-address assignment strategy.) - * - * Meanwhile, the kernel's traversal seems to have been reversed; see eg. - * the message, if not necessarily the code, of commit 70161ff336. - * Therefore the loop now establishes the inverse of the original intent. - * - * Unfortunately, we can't counteract the kernel change by reversing the - * loop; it would break existing command lines. - * - * In any case, the kernel makes no guarantee about the stability of - * enumeration order of virtio devices (as demonstrated by it changing - * between kernel versions). For reliable and stable identification - * of disks users must use UUIDs or similar mechanisms. - */ - for (i = 0; i < NUM_VIRTIO_TRANSPORTS; i++) { - int irq = vbi->irqmap[VIRT_MMIO] + i; - hwaddr base = vbi->memmap[VIRT_MMIO].base + i * size; - - sysbus_create_simple("virtio-mmio", base, pic[irq]); - } - - /* We add dtb nodes in reverse order so that they appear in the finished - * device tree lowest address first. - * - * Note that this mapping is independent of the loop above. The previous - * loop influences virtio device to virtio transport assignment, whereas - * this loop controls how virtio transports are laid out in the dtb. - */ - for (i = NUM_VIRTIO_TRANSPORTS - 1; i >= 0; i--) { - char *nodename; - int irq = vbi->irqmap[VIRT_MMIO] + i; - hwaddr base = vbi->memmap[VIRT_MMIO].base + i * size; - - nodename = g_strdup_printf("/virtio_mmio@%" PRIx64, base); - qemu_fdt_add_subnode(vbi->fdt, nodename); - qemu_fdt_setprop_string(vbi->fdt, nodename, - "compatible", "virtio,mmio"); - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", - 2, base, 2, size); - qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts", - GIC_FDT_IRQ_TYPE_SPI, irq, - GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); - g_free(nodename); - } -} - -static void create_one_flash(const char *name, hwaddr flashbase, - 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) { - qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo), - &error_abort); - } - - qdev_prop_set_uint32(dev, "num-blocks", flashsize / sectorlength); - qdev_prop_set_uint64(dev, "sector-length", sectorlength); - qdev_prop_set_uint8(dev, "width", 4); - qdev_prop_set_uint8(dev, "device-width", 2); - qdev_prop_set_bit(dev, "big-endian", false); - qdev_prop_set_uint16(dev, "id0", 0x89); - qdev_prop_set_uint16(dev, "id1", 0x18); - qdev_prop_set_uint16(dev, "id2", 0x00); - qdev_prop_set_uint16(dev, "id3", 0x00); - qdev_prop_set_string(dev, "name", name); - qdev_init_nofail(dev); - - memory_region_add_subregion(sysmem, flashbase, - sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); - - if (file) { - char *fn; - int image_size; - - if (drive_get(IF_PFLASH, 0, 0)) { - error_report("The contents of the first flash device may be " - "specified with -bios or with -drive if=pflash... " - "but you cannot use both options at once"); - exit(1); - } - fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, file); - if (!fn) { - error_report("Could not find ROM image '%s'", file); - exit(1); - } - 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'", 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, - bios_name, secure_sysmem); - create_one_flash("virt.flash1", flashbase + flashsize, flashsize, - NULL, sysmem); - - 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, 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, base + 16, as); - - nodename = g_strdup_printf("/fw-cfg@%" PRIx64, base); - qemu_fdt_add_subnode(vbi->fdt, nodename); - qemu_fdt_setprop_string(vbi->fdt, nodename, - "compatible", "qemu,fw-cfg-mmio"); - qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", - 2, base, 2, size); - g_free(nodename); -} - -static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle, - int first_irq, const char *nodename) -{ - int devfn, pin; - uint32_t full_irq_map[4 * 4 * 10] = { 0 }; - uint32_t *irq_map = full_irq_map; - - for (devfn = 0; devfn <= 0x18; devfn += 0x8) { - for (pin = 0; pin < 4; pin++) { - int irq_type = GIC_FDT_IRQ_TYPE_SPI; - int irq_nr = first_irq + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS); - int irq_level = GIC_FDT_IRQ_FLAGS_LEVEL_HI; - int i; - - uint32_t map[] = { - devfn << 8, 0, 0, /* devfn */ - pin + 1, /* PCI pin */ - gic_phandle, 0, 0, irq_type, irq_nr, irq_level }; /* GIC irq */ - - /* Convert map to big endian */ - for (i = 0; i < 10; i++) { - irq_map[i] = cpu_to_be32(map[i]); - } - irq_map += 10; - } - } - - qemu_fdt_setprop(vbi->fdt, nodename, "interrupt-map", - full_irq_map, sizeof(full_irq_map)); - - qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupt-map-mask", - 0x1800, 0, 0, /* devfn (PCI_SLOT(3)) */ - 0x7 /* PCI irq */); -} - -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; - hwaddr size_ecam = vbi->memmap[VIRT_PCIE_ECAM].size; - hwaddr base = base_mmio; - int nr_pcie_buses = size_ecam / PCIE_MMCFG_SIZE_MIN; - int irq = vbi->irqmap[VIRT_PCIE]; - MemoryRegion *mmio_alias; - MemoryRegion *mmio_reg; - MemoryRegion *ecam_alias; - MemoryRegion *ecam_reg; - DeviceState *dev; - char *nodename; - int i; - PCIHostState *pci; - - dev = qdev_create(NULL, TYPE_GPEX_HOST); - qdev_init_nofail(dev); - - /* Map only the first size_ecam bytes of ECAM space */ - ecam_alias = g_new0(MemoryRegion, 1); - ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); - memory_region_init_alias(ecam_alias, OBJECT(dev), "pcie-ecam", - ecam_reg, 0, size_ecam); - memory_region_add_subregion(get_system_memory(), base_ecam, ecam_alias); - - /* Map the MMIO window into system address space so as to expose - * the section of PCI MMIO space which starts at the same base address - * (ie 1:1 mapping for that part of PCI MMIO space visible through - * the window). - */ - mmio_alias = g_new0(MemoryRegion, 1); - mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); - memory_region_init_alias(mmio_alias, OBJECT(dev), "pcie-mmio", - 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); - - for (i = 0; i < GPEX_NUM_IRQS; i++) { - 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, - "compatible", "pci-host-ecam-generic"); - qemu_fdt_setprop_string(vbi->fdt, nodename, "device_type", "pci"); - qemu_fdt_setprop_cell(vbi->fdt, nodename, "#address-cells", 3); - qemu_fdt_setprop_cell(vbi->fdt, nodename, "#size-cells", 2); - qemu_fdt_setprop_cells(vbi->fdt, nodename, "bus-range", 0, - nr_pcie_buses - 1); - - 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); - - 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); - - g_free(nodename); -} - -static void create_platform_bus(VirtBoardInfo *vbi, qemu_irq *pic) -{ - DeviceState *dev; - SysBusDevice *s; - int i; - ARMPlatformBusFDTParams *fdt_params = g_new(ARMPlatformBusFDTParams, 1); - MemoryRegion *sysmem = get_system_memory(); - - platform_bus_params.platform_bus_base = vbi->memmap[VIRT_PLATFORM_BUS].base; - platform_bus_params.platform_bus_size = vbi->memmap[VIRT_PLATFORM_BUS].size; - platform_bus_params.platform_bus_first_irq = vbi->irqmap[VIRT_PLATFORM_BUS]; - platform_bus_params.platform_bus_num_irqs = PLATFORM_BUS_NUM_IRQS; - - fdt_params->system_params = &platform_bus_params; - fdt_params->binfo = &vbi->bootinfo; - fdt_params->intc = "/intc"; - /* - * register a machine init done notifier that creates the device tree - * nodes of the platform bus and its children dynamic sysbus devices - */ - arm_register_platform_bus_fdt_creator(fdt_params); - - dev = qdev_create(NULL, TYPE_PLATFORM_BUS_DEVICE); - dev->id = TYPE_PLATFORM_BUS_DEVICE; - qdev_prop_set_uint32(dev, "num_irqs", - platform_bus_params.platform_bus_num_irqs); - qdev_prop_set_uint32(dev, "mmio_size", - platform_bus_params.platform_bus_size); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - - for (i = 0; i < platform_bus_params.platform_bus_num_irqs; i++) { - int irqn = platform_bus_params.platform_bus_first_irq + i; - sysbus_connect_irq(s, i, pic[irqn]); - } - - memory_region_add_subregion(sysmem, - platform_bus_params.platform_bus_base, - 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; - - *fdt_size = board->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) -{ - VirtMachineState *vms = VIRT_MACHINE(machine); - qemu_irq pic[NUM_IRQS]; - MemoryRegion *sysmem = get_system_memory(); - 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); - - vbi = find_machine_info(cpustr[0]); - - if (!vbi) { - error_report("mach-virt: CPU %s not supported", cpustr[0]); - 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 %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++) { - ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpustr[0]); - CPUClass *cc = CPU_CLASS(oc); - Object *cpuobj; - Error *err = NULL; - char *cpuopts = g_strdup(cpustr[1]); - - if (!oc) { - error_report("Unable to find CPU definition"); - exit(1); - } - cpuobj = object_new(object_class_get_name(oc)); - - /* Handle any CPU options specified by the user */ - cc->parse_features(CPU(cpuobj), cpuopts, &err); - g_free(cpuopts); - if (err) { - error_report_err(err); - exit(1); - } - - if (!vms->secure) { - object_property_set_bool(cpuobj, false, "has_el3", 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); - } - } - - if (object_property_find(cpuobj, "reset-cbar", NULL)) { - object_property_set_int(cpuobj, vbi->memmap[VIRT_CPUPERIPHS].base, - "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, gic_version); - fdt_add_cpu_nodes(vbi); - fdt_add_psci_node(vbi); - - memory_region_allocate_system_memory(ram, NULL, "mach-virt.ram", - machine->ram_size); - memory_region_add_subregion(sysmem, vbi->memmap[VIRT_MEM].base, ram); - - create_flash(vbi, sysmem, secure_sysmem ? secure_sysmem : sysmem); - - create_gic(vbi, pic, gic_version, vms->secure); - - create_uart(vbi, pic, VIRT_UART, sysmem); - - 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, 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 - * no backend is created the transport will just sit harmlessly idle. - */ - create_virtio_devices(vbi, pic); - - 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); - - vbi->bootinfo.ram_size = machine->ram_size; - vbi->bootinfo.kernel_filename = machine->kernel_filename; - vbi->bootinfo.kernel_cmdline = machine->kernel_cmdline; - vbi->bootinfo.initrd_filename = machine->initrd_filename; - vbi->bootinfo.nb_cpus = smp_cpus; - vbi->bootinfo.board_id = -1; - vbi->bootinfo.loader_start = vbi->memmap[VIRT_MEM].base; - vbi->bootinfo.get_dtb = machvirt_dtb; - vbi->bootinfo.firmware_loaded = firmware_loaded; - arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo); - - /* - * arm_load_kernel machine init done notifier registration must - * happen before the platform_bus_create call. In this latter, - * another notifier is registered which adds platform bus nodes. - * Notifiers are executed in registration reverse order. - */ - create_platform_bus(vbi, pic); -} - -static bool virt_get_secure(Object *obj, Error **errp) -{ - VirtMachineState *vms = VIRT_MACHINE(obj); - - return vms->secure; -} - -static void virt_set_secure(Object *obj, bool value, Error **errp) -{ - VirtMachineState *vms = VIRT_MACHINE(obj); - - vms->secure = value; -} - -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 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_2_6_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - static GlobalProperty compat_props[] = { - { /* end of list */ } - }; - - mc->desc = "QEMU 2.6 ARM Virtual Machine"; - mc->alias = "virt"; - mc->compat_props = compat_props; -} - -static const TypeInfo machvirt_info = { - .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); -} - -type_init(machvirt_machine_init); diff --git a/qemu/hw/arm/xilinx_zynq.c b/qemu/hw/arm/xilinx_zynq.c deleted file mode 100644 index 98b17c9ae..000000000 --- a/qemu/hw/arm/xilinx_zynq.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Xilinx Zynq Baseboard System emulation. - * - * Copyright (c) 2010 Xilinx. - * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.croshtwaite@petalogix.com) - * Copyright (c) 2012 Petalogix Pty Ltd. - * Written by Haibing Ma - * - * 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 . - */ - -#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" -#include "exec/address-spaces.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" -#include "hw/block/flash.h" -#include "sysemu/block-backend.h" -#include "hw/loader.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 -#define NUM_QSPI_BUSSES 2 - -#define FLASH_SIZE (64 * 1024 * 1024) -#define FLASH_SECTOR_SIZE (128 * 1024) - -#define IRQ_OFFSET 32 /* pic interrupts start from index 32 */ - -#define MPCORE_PERIPHBASE 0xF8F00000 -#define ZYNQ_BOARD_MIDR 0x413FC090 - -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) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_create(NULL, "cadence_gem"); - if (nd->used) { - qemu_check_nic_model(nd, "cadence_gem"); - qdev_set_nic_properties(dev, nd); - } - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(s, 0, base); - sysbus_connect_irq(s, 0, irq); -} - -static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq, - bool is_qspi) -{ - DeviceState *dev; - SysBusDevice *busdev; - SSIBus *spi; - DeviceState *flash_dev; - int i, j; - int num_busses = is_qspi ? NUM_QSPI_BUSSES : 1; - int num_ss = is_qspi ? NUM_QSPI_FLASHES : NUM_SPI_FLASHES; - - dev = qdev_create(NULL, is_qspi ? "xlnx.ps7-qspi" : "xlnx.ps7-spi"); - qdev_prop_set_uint8(dev, "num-txrx-bytes", is_qspi ? 4 : 1); - qdev_prop_set_uint8(dev, "num-ss-bits", num_ss); - qdev_prop_set_uint8(dev, "num-busses", num_busses); - qdev_init_nofail(dev); - busdev = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(busdev, 0, base_addr); - if (is_qspi) { - sysbus_mmio_map(busdev, 1, 0xFC000000); - } - sysbus_connect_irq(busdev, 0, irq); - - for (i = 0; i < num_busses; ++i) { - char bus_name[16]; - qemu_irq cs_line; - - snprintf(bus_name, 16, "spi%d", i); - spi = (SSIBus *)qdev_get_child_bus(dev, bus_name); - - for (j = 0; j < num_ss; ++j) { - flash_dev = ssi_create_slave(spi, "n25q128"); - - cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0); - sysbus_connect_irq(busdev, i * num_ss + j + 1, cs_line); - } - } - -} - -static void zynq_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; - ObjectClass *cpu_oc; - ARMCPU *cpu; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ext_ram = g_new(MemoryRegion, 1); - MemoryRegion *ocm_ram = g_new(MemoryRegion, 1); - DeviceState *dev, *carddev; - SysBusDevice *busdev; - DriveInfo *di; - BlockBackend *blk; - qemu_irq pic[64]; - int n; - - if (!cpu_model) { - cpu_model = "cortex-a9"; - } - cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); - - cpu = ARM_CPU(object_new(object_class_get_name(cpu_oc))); - - /* 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(OBJECT(cpu), "has_el3", NULL)) { - object_property_set_bool(OBJECT(cpu), false, "has_el3", &error_fatal); - } - - 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) { - ram_size = 0x80000000; - } - - /* DDR remapped to address zero. */ - memory_region_allocate_system_memory(ext_ram, NULL, "zynq.ext_ram", - ram_size); - memory_region_add_subregion(address_space_mem, 0, ext_ram); - - /* 256K of on-chip memory */ - memory_region_init_ram(ocm_ram, NULL, "zynq.ocm_ram", 256 << 10, - &error_fatal); - vmstate_register_ram_global(ocm_ram); - memory_region_add_subregion(address_space_mem, 0xFFFC0000, ocm_ram); - - DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0); - - /* AMD */ - pflash_cfi02_register(0xe2000000, NULL, "zynq.pflash", FLASH_SIZE, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - FLASH_SECTOR_SIZE, - FLASH_SIZE/FLASH_SECTOR_SIZE, 1, - 1, 0x0066, 0x0022, 0x0000, 0x0000, 0x0555, 0x2aa, - 0); - - dev = qdev_create(NULL, "xilinx,zynq_slcr"); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xF8000000); - - dev = qdev_create(NULL, "a9mpcore_priv"); - qdev_prop_set_uint32(dev, "num-cpu", 1); - qdev_init_nofail(dev); - busdev = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE); - sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ)); - - for (n = 0; n < 64; n++) { - pic[n] = qdev_get_gpio_in(dev, n); - } - - zynq_init_spi_flashes(0xE0006000, pic[58-IRQ_OFFSET], false); - zynq_init_spi_flashes(0xE0007000, pic[81-IRQ_OFFSET], false); - zynq_init_spi_flashes(0xE000D000, pic[51-IRQ_OFFSET], true); - - sysbus_create_simple("xlnx,ps7-usb", 0xE0002000, pic[53-IRQ_OFFSET]); - sysbus_create_simple("xlnx,ps7-usb", 0xE0003000, pic[76-IRQ_OFFSET]); - - sysbus_create_simple("cadence_uart", 0xE0000000, pic[59-IRQ_OFFSET]); - sysbus_create_simple("cadence_uart", 0xE0001000, pic[82-IRQ_OFFSET]); - - sysbus_create_varargs("cadence_ttc", 0xF8001000, - pic[42-IRQ_OFFSET], pic[43-IRQ_OFFSET], pic[44-IRQ_OFFSET], NULL); - sysbus_create_varargs("cadence_ttc", 0xF8002000, - pic[69-IRQ_OFFSET], pic[70-IRQ_OFFSET], pic[71-IRQ_OFFSET], NULL); - - gem_init(&nd_table[0], 0xE000B000, pic[54-IRQ_OFFSET]); - gem_init(&nd_table[1], 0xE000C000, pic[77-IRQ_OFFSET]); - - dev = qdev_create(NULL, "generic-sdhci"); - qdev_init_nofail(dev); - 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); - qdev_prop_set_uint8(dev, "num_events", 16); - - qdev_prop_set_uint8(dev, "data_width", 64); - qdev_prop_set_uint8(dev, "wr_cap", 8); - qdev_prop_set_uint8(dev, "wr_q_dep", 16); - qdev_prop_set_uint8(dev, "rd_cap", 8); - qdev_prop_set_uint8(dev, "rd_q_dep", 16); - qdev_prop_set_uint16(dev, "data_buffer_dep", 256); - - qdev_init_nofail(dev); - busdev = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(busdev, 0, 0xF8003000); - sysbus_connect_irq(busdev, 0, pic[45-IRQ_OFFSET]); /* abort irq line */ - for (n = 0; n < 8; ++n) { /* event irqs */ - sysbus_connect_irq(busdev, n + 1, pic[dma_irqs[n] - IRQ_OFFSET]); - } - - zynq_binfo.ram_size = ram_size; - zynq_binfo.kernel_filename = kernel_filename; - zynq_binfo.kernel_cmdline = kernel_cmdline; - zynq_binfo.initrd_filename = initrd_filename; - 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 void zynq_machine_init(MachineClass *mc) -{ - 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; -} - -DEFINE_MACHINE("xilinx-zynq-a9", zynq_machine_init) diff --git a/qemu/hw/arm/xlnx-ep108.c b/qemu/hw/arm/xlnx-ep108.c deleted file mode 100644 index 5f480182b..000000000 --- a/qemu/hw/arm/xlnx-ep108.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Xilinx ZynqMP EP108 board - * - * Copyright (C) 2015 Xilinx Inc - * Written by Peter Crosthwaite - * - * 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. - */ - -#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" -#include "exec/address-spaces.h" - -typedef struct XlnxEP108 { - XlnxZynqMPState soc; - MemoryRegion ddr_ram; -} XlnxEP108; - -static struct arm_boot_info xlnx_ep108_binfo; - -static void xlnx_ep108_init(MachineState *machine) -{ - XlnxEP108 *s = g_new0(XlnxEP108, 1); - 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_link(OBJECT(&s->soc), OBJECT(&s->ddr_ram), - "ddr-ram", &error_abort); - - 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; - - 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); - } - - 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); - - 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; - xlnx_ep108_binfo.loader_start = 0; - arm_load_kernel(s->soc.boot_cpu_ptr, &xlnx_ep108_binfo); -} - -static void xlnx_ep108_machine_init(MachineClass *mc) -{ - mc->desc = "Xilinx ZynqMP EP108 board"; - mc->init = xlnx_ep108_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 deleted file mode 100644 index 4d504da64..000000000 --- a/qemu/hw/arm/xlnx-zynqmp.c +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Xilinx Zynq MPSoC emulation - * - * Copyright (C) 2015 Xilinx Inc - * Written by Peter Crosthwaite - * - * 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. - */ - -#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" - -#define GIC_NUM_SPI_INTR 160 - -#define ARM_PHYS_TIMER_PPI 30 -#define ARM_VIRT_TIMER_PPI 27 - -#define GIC_BASE_ADDR 0xf9000000 -#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, -}; - -static const int gem_intr[XLNX_ZYNQMP_NUM_GEMS] = { - 57, 59, 61, 63, -}; - -static const uint64_t uart_addr[XLNX_ZYNQMP_NUM_UARTS] = { - 0xFF000000, 0xFF010000, -}; - -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; -} XlnxZynqMPGICRegion; - -static const XlnxZynqMPGICRegion xlnx_zynqmp_gic_regions[] = { - { .region_index = 0, .address = GIC_DIST_ADDR, }, - { .region_index = 1, .address = GIC_CPU_ADDR, }, -}; - -static inline int arm_gic_ppi_index(int cpu_nr, int ppi_index) -{ - return GIC_NUM_SPI_INTR + cpu_nr * GIC_INTERNAL + ppi_index; -} - -static void xlnx_zynqmp_init(Object *obj) -{ - XlnxZynqMPState *s = XLNX_ZYNQMP(obj); - int i; - - for (i = 0; i < XLNX_ZYNQMP_NUM_APU_CPUS; i++) { - object_initialize(&s->apu_cpu[i], sizeof(s->apu_cpu[i]), - "cortex-a53-" TYPE_ARM_CPU); - object_property_add_child(obj, "apu-cpu[*]", OBJECT(&s->apu_cpu[i]), - &error_abort); - } - - for (i = 0; i < XLNX_ZYNQMP_NUM_RPU_CPUS; i++) { - object_initialize(&s->rpu_cpu[i], sizeof(s->rpu_cpu[i]), - "cortex-r5-" TYPE_ARM_CPU); - object_property_add_child(obj, "rpu-cpu[*]", OBJECT(&s->rpu_cpu[i]), - &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()); - - for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) { - object_initialize(&s->gem[i], sizeof(s->gem[i]), TYPE_CADENCE_GEM); - qdev_set_parent_bus(DEVICE(&s->gem[i]), sysbus_get_default()); - } - - for (i = 0; i < XLNX_ZYNQMP_NUM_UARTS; i++) { - 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) -{ - 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); - return; - } - assert(ARRAY_SIZE(xlnx_zynqmp_gic_regions) == XLNX_ZYNQMP_GIC_REGIONS); - for (i = 0; i < XLNX_ZYNQMP_GIC_REGIONS; i++) { - SysBusDevice *gic = SYS_BUS_DEVICE(&s->gic); - const XlnxZynqMPGICRegion *r = &xlnx_zynqmp_gic_regions[i]; - MemoryRegion *mr = sysbus_mmio_get_region(gic, r->region_index); - uint32_t addr = r->address; - int j; - - sysbus_mmio_map(gic, r->region_index, addr); - - for (j = 0; j < XLNX_ZYNQMP_GIC_ALIASES; j++) { - MemoryRegion *alias = &s->gic_mr[i][j]; - - addr += XLNX_ZYNQMP_GIC_REGION_SIZE; - memory_region_init_alias(alias, OBJECT(s), "zynqmp-gic-alias", mr, - 0, XLNX_ZYNQMP_GIC_REGION_SIZE); - memory_region_add_subregion(system_memory, addr, alias); - } - } - - for (i = 0; i < XLNX_ZYNQMP_NUM_APU_CPUS; i++) { - qemu_irq irq; - char *name; - - object_property_set_int(OBJECT(&s->apu_cpu[i]), QEMU_PSCI_CONDUIT_SMC, - "psci-conduit", &error_abort); - - name = object_get_canonical_path_component(OBJECT(&s->apu_cpu[i])); - if (strcmp(name, boot_cpu)) { - /* Secondary CPUs start in PSCI powered-down state */ - object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, - "start-powered-off", &error_abort); - } else { - s->boot_cpu_ptr = &s->apu_cpu[i]; - } - g_free(name); - - object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR, - "reset-cbar", &error_abort); - object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized", - &err); - if (err) { - error_propagate(errp, err); - return; - } - - sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i, - qdev_get_gpio_in(DEVICE(&s->apu_cpu[i]), - ARM_CPU_IRQ)); - irq = qdev_get_gpio_in(DEVICE(&s->gic), - arm_gic_ppi_index(i, ARM_PHYS_TIMER_PPI)); - qdev_connect_gpio_out(DEVICE(&s->apu_cpu[i]), 0, irq); - irq = qdev_get_gpio_in(DEVICE(&s->gic), - arm_gic_ppi_index(i, ARM_VIRT_TIMER_PPI)); - qdev_connect_gpio_out(DEVICE(&s->apu_cpu[i]), 1, irq); - } - - for (i = 0; i < XLNX_ZYNQMP_NUM_RPU_CPUS; i++) { - char *name; - - name = object_get_canonical_path_component(OBJECT(&s->rpu_cpu[i])); - if (strcmp(name, boot_cpu)) { - /* Secondary CPUs start in PSCI powered-down state */ - object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, - "start-powered-off", &error_abort); - } else { - s->boot_cpu_ptr = &s->rpu_cpu[i]; - } - g_free(name); - - object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "reset-hivecs", - &error_abort); - object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "realized", - &err); - if (err) { - error_propagate(errp, err); - return; - } - } - - if (!s->boot_cpu_ptr) { - error_setg(errp, "ZynqMP Boot cpu %s not found", boot_cpu); - return; - } - - for (i = 0; i < GIC_NUM_SPI_INTR; i++) { - gic_spi[i] = qdev_get_gpio_in(DEVICE(&s->gic), i); - } - - for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) { - NICInfo *nd = &nd_table[i]; - - if (nd->used) { - qemu_check_nic_model(nd, TYPE_CADENCE_GEM); - qdev_set_nic_properties(DEVICE(&s->gem[i]), nd); - } - object_property_set_bool(OBJECT(&s->gem[i]), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem[i]), 0, gem_addr[i]); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem[i]), 0, - gic_spi[gem_intr[i]]); - } - - 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); - 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[] = { - DEFINE_PROP_STRING("boot-cpu", XlnxZynqMPState, boot_cpu), - DEFINE_PROP_END_OF_LIST() -}; - -static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - 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 = { - .name = TYPE_XLNX_ZYNQMP, - .parent = TYPE_DEVICE, - .instance_size = sizeof(XlnxZynqMPState), - .instance_init = xlnx_zynqmp_init, - .class_init = xlnx_zynqmp_class_init, -}; - -static void xlnx_zynqmp_register_types(void) -{ - type_register_static(&xlnx_zynqmp_type_info); -} - -type_init(xlnx_zynqmp_register_types) diff --git a/qemu/hw/arm/z2.c b/qemu/hw/arm/z2.c deleted file mode 100644 index aea895a50..000000000 --- a/qemu/hw/arm/z2.c +++ /dev/null @@ -1,382 +0,0 @@ -/* - * PXA270-based Zipit Z2 device - * - * Copyright (c) 2011 by Vasily Khoruzhick - * - * Code is based on mainstone platform. - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#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/ssi.h" -#include "hw/boards.h" -#include "sysemu/sysemu.h" -#include "hw/block/flash.h" -#include "sysemu/block-backend.h" -#include "ui/console.h" -#include "audio/audio.h" -#include "exec/address-spaces.h" -#include "sysemu/qtest.h" - -#ifdef DEBUG_Z2 -#define DPRINTF(fmt, ...) \ - printf(fmt, ## __VA_ARGS__) -#else -#define DPRINTF(fmt, ...) -#endif - -static const struct keymap map[0x100] = { - [0 ... 0xff] = { -1, -1 }, - [0x3b] = {0, 0}, /* Option = F1 */ - [0xc8] = {0, 1}, /* Up */ - [0xd0] = {0, 2}, /* Down */ - [0xcb] = {0, 3}, /* Left */ - [0xcd] = {0, 4}, /* Right */ - [0xcf] = {0, 5}, /* End */ - [0x0d] = {0, 6}, /* KPPLUS */ - [0xc7] = {1, 0}, /* Home */ - [0x10] = {1, 1}, /* Q */ - [0x17] = {1, 2}, /* I */ - [0x22] = {1, 3}, /* G */ - [0x2d] = {1, 4}, /* X */ - [0x1c] = {1, 5}, /* Enter */ - [0x0c] = {1, 6}, /* KPMINUS */ - [0xc9] = {2, 0}, /* PageUp */ - [0x11] = {2, 1}, /* W */ - [0x18] = {2, 2}, /* O */ - [0x23] = {2, 3}, /* H */ - [0x2e] = {2, 4}, /* C */ - [0x38] = {2, 5}, /* LeftAlt */ - [0xd1] = {3, 0}, /* PageDown */ - [0x12] = {3, 1}, /* E */ - [0x19] = {3, 2}, /* P */ - [0x24] = {3, 3}, /* J */ - [0x2f] = {3, 4}, /* V */ - [0x2a] = {3, 5}, /* LeftShift */ - [0x01] = {4, 0}, /* Esc */ - [0x13] = {4, 1}, /* R */ - [0x1e] = {4, 2}, /* A */ - [0x25] = {4, 3}, /* K */ - [0x30] = {4, 4}, /* B */ - [0x1d] = {4, 5}, /* LeftCtrl */ - [0x0f] = {5, 0}, /* Tab */ - [0x14] = {5, 1}, /* T */ - [0x1f] = {5, 2}, /* S */ - [0x26] = {5, 3}, /* L */ - [0x31] = {5, 4}, /* N */ - [0x39] = {5, 5}, /* Space */ - [0x3c] = {6, 0}, /* Stop = F2 */ - [0x15] = {6, 1}, /* Y */ - [0x20] = {6, 2}, /* D */ - [0x0e] = {6, 3}, /* Backspace */ - [0x32] = {6, 4}, /* M */ - [0x33] = {6, 5}, /* Comma */ - [0x3d] = {7, 0}, /* Play = F3 */ - [0x16] = {7, 1}, /* U */ - [0x21] = {7, 2}, /* F */ - [0x2c] = {7, 3}, /* Z */ - [0x27] = {7, 4}, /* Semicolon */ - [0x34] = {7, 5}, /* Dot */ -}; - -#define Z2_RAM_SIZE 0x02000000 -#define Z2_FLASH_BASE 0x00000000 -#define Z2_FLASH_SIZE 0x00800000 - -static struct arm_boot_info z2_binfo = { - .loader_start = PXA2XX_SDRAM_BASE, - .ram_size = Z2_RAM_SIZE, -}; - -#define Z2_GPIO_SD_DETECT 96 -#define Z2_GPIO_AC_IN 0 -#define Z2_GPIO_KEY_ON 1 -#define Z2_GPIO_LCD_CS 88 - -typedef struct { - SSISlave ssidev; - int32_t selected; - int32_t enabled; - uint8_t buf[3]; - uint32_t cur_reg; - int pos; -} ZipitLCD; - -static uint32_t zipit_lcd_transfer(SSISlave *dev, uint32_t value) -{ - ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev); - uint16_t val; - if (z->selected) { - z->buf[z->pos] = value & 0xff; - z->pos++; - } - if (z->pos == 3) { - switch (z->buf[0]) { - case 0x74: - DPRINTF("%s: reg: 0x%.2x\n", __func__, z->buf[2]); - z->cur_reg = z->buf[2]; - break; - case 0x76: - val = z->buf[1] << 8 | z->buf[2]; - DPRINTF("%s: value: 0x%.4x\n", __func__, val); - if (z->cur_reg == 0x22 && val == 0x0000) { - z->enabled = 1; - printf("%s: LCD enabled\n", __func__); - } else if (z->cur_reg == 0x10 && val == 0x0000) { - z->enabled = 0; - printf("%s: LCD disabled\n", __func__); - } - break; - default: - DPRINTF("%s: unknown command!\n", __func__); - break; - } - z->pos = 0; - } - return 0; -} - -static void z2_lcd_cs(void *opaque, int line, int level) -{ - ZipitLCD *z2_lcd = opaque; - z2_lcd->selected = !level; -} - -static int zipit_lcd_init(SSISlave *dev) -{ - ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev); - z->selected = 0; - z->enabled = 0; - z->pos = 0; - - return 0; -} - -static VMStateDescription vmstate_zipit_lcd_state = { - .name = "zipit-lcd", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_SSI_SLAVE(ssidev, ZipitLCD), - VMSTATE_INT32(selected, ZipitLCD), - VMSTATE_INT32(enabled, ZipitLCD), - VMSTATE_BUFFER(buf, ZipitLCD), - VMSTATE_UINT32(cur_reg, ZipitLCD), - VMSTATE_INT32(pos, ZipitLCD), - VMSTATE_END_OF_LIST(), - } -}; - -static void zipit_lcd_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - - k->init = zipit_lcd_init; - k->transfer = zipit_lcd_transfer; - dc->vmsd = &vmstate_zipit_lcd_state; -} - -static const TypeInfo zipit_lcd_info = { - .name = "zipit-lcd", - .parent = TYPE_SSI_SLAVE, - .instance_size = sizeof(ZipitLCD), - .class_init = zipit_lcd_class_init, -}; - -#define TYPE_AER915 "aer915" -#define AER915(obj) OBJECT_CHECK(AER915State, (obj), TYPE_AER915) - -typedef struct AER915State { - I2CSlave parent_obj; - - int len; - uint8_t buf[3]; -} AER915State; - -static int aer915_send(I2CSlave *i2c, uint8_t data) -{ - AER915State *s = AER915(i2c); - - s->buf[s->len] = data; - if (s->len++ > 2) { - DPRINTF("%s: message too long (%i bytes)\n", - __func__, s->len); - return 1; - } - - if (s->len == 2) { - DPRINTF("%s: reg %d value 0x%02x\n", __func__, - s->buf[0], s->buf[1]); - } - - return 0; -} - -static void aer915_event(I2CSlave *i2c, enum i2c_event event) -{ - AER915State *s = AER915(i2c); - - switch (event) { - case I2C_START_SEND: - s->len = 0; - break; - case I2C_START_RECV: - if (s->len != 1) { - DPRINTF("%s: short message!?\n", __func__); - } - break; - case I2C_FINISH: - break; - default: - break; - } -} - -static int aer915_recv(I2CSlave *slave) -{ - AER915State *s = AER915(slave); - int retval = 0x00; - - switch (s->buf[0]) { - /* Return hardcoded battery voltage, - * 0xf0 means ~4.1V - */ - case 0x02: - retval = 0xf0; - break; - /* Return 0x00 for other regs, - * we don't know what they are for, - * anyway they return 0x00 on real hardware. - */ - default: - break; - } - - return retval; -} - -static int aer915_init(I2CSlave *i2c) -{ - /* Nothing to do. */ - return 0; -} - -static VMStateDescription vmstate_aer915_state = { - .name = "aer915", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32(len, AER915State), - VMSTATE_BUFFER(buf, AER915State), - VMSTATE_END_OF_LIST(), - } -}; - -static void aer915_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - - k->init = aer915_init; - k->event = aer915_event; - k->recv = aer915_recv; - k->send = aer915_send; - dc->vmsd = &vmstate_aer915_state; -} - -static const TypeInfo aer915_info = { - .name = TYPE_AER915, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(AER915State), - .class_init = aer915_class_init, -}; - -static void z2_init(MachineState *machine) -{ - 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; - MemoryRegion *address_space_mem = get_system_memory(); - uint32_t sector_len = 0x10000; - PXA2xxState *mpu; - DriveInfo *dinfo; - int be; - void *z2_lcd; - I2CBus *bus; - DeviceState *wm; - - if (!cpu_model) { - cpu_model = "pxa270-c5"; - } - - /* Setup CPU & memory */ - mpu = pxa270_init(address_space_mem, z2_binfo.ram_size, cpu_model); - -#ifdef TARGET_WORDS_BIGENDIAN - be = 1; -#else - be = 0; -#endif - dinfo = drive_get(IF_PFLASH, 0, 0); - if (!dinfo && !qtest_enabled()) { - fprintf(stderr, "Flash image must be given with the " - "'pflash' parameter\n"); - exit(1); - } - - if (!pflash_cfi01_register(Z2_FLASH_BASE, - NULL, "z2.flash0", Z2_FLASH_SIZE, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - sector_len, Z2_FLASH_SIZE / sector_len, - 4, 0, 0, 0, 0, be)) { - fprintf(stderr, "qemu: Error registering flash memory.\n"); - exit(1); - } - - /* setup keypad */ - pxa27x_register_keypad(mpu->kp, map, 0x100); - - /* MMC/SD host */ - pxa2xx_mmci_handlers(mpu->mmc, - NULL, - qdev_get_gpio_in(mpu->gpio, Z2_GPIO_SD_DETECT)); - - type_register_static(&zipit_lcd_info); - type_register_static(&aer915_info); - z2_lcd = ssi_create_slave(mpu->ssp[1], "zipit-lcd"); - bus = pxa2xx_i2c_bus(mpu->i2c[0]); - i2c_create_slave(bus, TYPE_AER915, 0x55); - wm = i2c_create_slave(bus, "wm8750", 0x1b); - mpu->i2s->opaque = wm; - mpu->i2s->codec_out = wm8750_dac_dat; - mpu->i2s->codec_in = wm8750_adc_dat; - wm8750_data_req_set(wm, mpu->i2s->data_req, mpu->i2s); - - qdev_connect_gpio_out(mpu->gpio, Z2_GPIO_LCD_CS, - qemu_allocate_irq(z2_lcd_cs, z2_lcd, 0)); - - z2_binfo.kernel_filename = kernel_filename; - z2_binfo.kernel_cmdline = kernel_cmdline; - z2_binfo.initrd_filename = initrd_filename; - z2_binfo.board_id = 0x6dd; - arm_load_kernel(mpu->cpu, &z2_binfo); -} - -static void z2_machine_init(MachineClass *mc) -{ - mc->desc = "Zipit Z2 (PXA27x)"; - mc->init = z2_init; -} - -DEFINE_MACHINE("z2", z2_machine_init) diff --git a/qemu/hw/audio/Makefile.objs b/qemu/hw/audio/Makefile.objs deleted file mode 100644 index 7ce85a2e8..000000000 --- a/qemu/hw/audio/Makefile.objs +++ /dev/null @@ -1,18 +0,0 @@ -# Sound -common-obj-$(CONFIG_SB16) += sb16.o -common-obj-$(CONFIG_ES1370) += es1370.o -common-obj-$(CONFIG_AC97) += ac97.o -common-obj-$(CONFIG_ADLIB) += fmopl.o adlib.o -common-obj-$(CONFIG_GUS) += gus.o gusemu_hal.o gusemu_mixer.o -common-obj-$(CONFIG_CS4231A) += cs4231a.o -common-obj-$(CONFIG_HDA) += intel-hda.o hda-codec.o - -common-obj-$(CONFIG_PCSPK) += pcspk.o -common-obj-$(CONFIG_WM8750) += wm8750.o -common-obj-$(CONFIG_PL041) += pl041.o lm4549.o - -common-obj-$(CONFIG_CS4231) += cs4231.o -common-obj-$(CONFIG_MARVELL_88W8618) += marvell_88w8618.o -common-obj-$(CONFIG_MILKYMIST) += milkymist-ac97.o - -$(obj)/adlib.o $(obj)/fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0 diff --git a/qemu/hw/audio/ac97.c b/qemu/hw/audio/ac97.c deleted file mode 100644 index cbd959e0b..000000000 --- a/qemu/hw/audio/ac97.c +++ /dev/null @@ -1,1431 +0,0 @@ -/* - * Copyright (C) 2006 InnoTek Systemberatung GmbH - * - * This file is part of VirtualBox Open Source Edition (OSE), as - * available from http://www.virtualbox.org. This file 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, - * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE - * distribution. VirtualBox OSE is distributed in the hope that it will - * be useful, but WITHOUT ANY WARRANTY of any kind. - * - * If you received this file as part of a commercial VirtualBox - * distribution, then only the terms of your commercial VirtualBox - * license agreement apply instead of the previous paragraph. - * - * 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/audio/audio.h" -#include "audio/audio.h" -#include "hw/pci/pci.h" -#include "sysemu/dma.h" - -enum { - AC97_Reset = 0x00, - AC97_Master_Volume_Mute = 0x02, - AC97_Headphone_Volume_Mute = 0x04, - AC97_Master_Volume_Mono_Mute = 0x06, - AC97_Master_Tone_RL = 0x08, - AC97_PC_BEEP_Volume_Mute = 0x0A, - AC97_Phone_Volume_Mute = 0x0C, - AC97_Mic_Volume_Mute = 0x0E, - AC97_Line_In_Volume_Mute = 0x10, - AC97_CD_Volume_Mute = 0x12, - AC97_Video_Volume_Mute = 0x14, - AC97_Aux_Volume_Mute = 0x16, - AC97_PCM_Out_Volume_Mute = 0x18, - AC97_Record_Select = 0x1A, - AC97_Record_Gain_Mute = 0x1C, - AC97_Record_Gain_Mic_Mute = 0x1E, - AC97_General_Purpose = 0x20, - AC97_3D_Control = 0x22, - AC97_AC_97_RESERVED = 0x24, - AC97_Powerdown_Ctrl_Stat = 0x26, - AC97_Extended_Audio_ID = 0x28, - AC97_Extended_Audio_Ctrl_Stat = 0x2A, - AC97_PCM_Front_DAC_Rate = 0x2C, - AC97_PCM_Surround_DAC_Rate = 0x2E, - AC97_PCM_LFE_DAC_Rate = 0x30, - AC97_PCM_LR_ADC_Rate = 0x32, - AC97_MIC_ADC_Rate = 0x34, - AC97_6Ch_Vol_C_LFE_Mute = 0x36, - AC97_6Ch_Vol_L_R_Surround_Mute = 0x38, - AC97_Vendor_Reserved = 0x58, - AC97_Sigmatel_Analog = 0x6c, /* We emulate a Sigmatel codec */ - AC97_Sigmatel_Dac2Invert = 0x6e, /* We emulate a Sigmatel codec */ - AC97_Vendor_ID1 = 0x7c, - AC97_Vendor_ID2 = 0x7e -}; - -#define SOFT_VOLUME -#define SR_FIFOE 16 /* rwc */ -#define SR_BCIS 8 /* rwc */ -#define SR_LVBCI 4 /* rwc */ -#define SR_CELV 2 /* ro */ -#define SR_DCH 1 /* ro */ -#define SR_VALID_MASK ((1 << 5) - 1) -#define SR_WCLEAR_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI) -#define SR_RO_MASK (SR_DCH | SR_CELV) -#define SR_INT_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI) - -#define CR_IOCE 16 /* rw */ -#define CR_FEIE 8 /* rw */ -#define CR_LVBIE 4 /* rw */ -#define CR_RR 2 /* rw */ -#define CR_RPBM 1 /* rw */ -#define CR_VALID_MASK ((1 << 5) - 1) -#define CR_DONT_CLEAR_MASK (CR_IOCE | CR_FEIE | CR_LVBIE) - -#define GC_WR 4 /* rw */ -#define GC_CR 2 /* rw */ -#define GC_VALID_MASK ((1 << 6) - 1) - -#define GS_MD3 (1<<17) /* rw */ -#define GS_AD3 (1<<16) /* rw */ -#define GS_RCS (1<<15) /* rwc */ -#define GS_B3S12 (1<<14) /* ro */ -#define GS_B2S12 (1<<13) /* ro */ -#define GS_B1S12 (1<<12) /* ro */ -#define GS_S1R1 (1<<11) /* rwc */ -#define GS_S0R1 (1<<10) /* rwc */ -#define GS_S1CR (1<<9) /* ro */ -#define GS_S0CR (1<<8) /* ro */ -#define GS_MINT (1<<7) /* ro */ -#define GS_POINT (1<<6) /* ro */ -#define GS_PIINT (1<<5) /* ro */ -#define GS_RSRVD ((1<<4)|(1<<3)) -#define GS_MOINT (1<<2) /* ro */ -#define GS_MIINT (1<<1) /* ro */ -#define GS_GSCI 1 /* rwc */ -#define GS_RO_MASK (GS_B3S12| \ - GS_B2S12| \ - GS_B1S12| \ - GS_S1CR| \ - GS_S0CR| \ - GS_MINT| \ - GS_POINT| \ - GS_PIINT| \ - GS_RSRVD| \ - GS_MOINT| \ - GS_MIINT) -#define GS_VALID_MASK ((1 << 18) - 1) -#define GS_WCLEAR_MASK (GS_RCS|GS_S1R1|GS_S0R1|GS_GSCI) - -#define BD_IOC (1<<31) -#define BD_BUP (1<<30) - -#define EACS_VRA 1 -#define EACS_VRM 8 - -#define MUTE_SHIFT 15 - -#define REC_MASK 7 -enum { - REC_MIC = 0, - REC_CD, - REC_VIDEO, - REC_AUX, - REC_LINE_IN, - REC_STEREO_MIX, - REC_MONO_MIX, - REC_PHONE -}; - -typedef struct BD { - uint32_t addr; - uint32_t ctl_len; -} BD; - -typedef struct AC97BusMasterRegs { - uint32_t bdbar; /* rw 0 */ - uint8_t civ; /* ro 0 */ - uint8_t lvi; /* rw 0 */ - uint16_t sr; /* rw 1 */ - uint16_t picb; /* ro 0 */ - uint8_t piv; /* ro 0 */ - uint8_t cr; /* rw 0 */ - unsigned int bd_valid; - BD bd; -} AC97BusMasterRegs; - -typedef struct AC97LinkState { - PCIDevice dev; - QEMUSoundCard card; - uint32_t use_broken_id; - uint32_t glob_cnt; - uint32_t glob_sta; - uint32_t cas; - uint32_t last_samp; - AC97BusMasterRegs bm_regs[3]; - uint8_t mixer_data[256]; - SWVoiceIn *voice_pi; - SWVoiceOut *voice_po; - SWVoiceIn *voice_mc; - int invalid_freq[3]; - uint8_t silence[128]; - int bup_flag; - MemoryRegion io_nam; - MemoryRegion io_nabm; -} AC97LinkState; - -enum { - BUP_SET = 1, - BUP_LAST = 2 -}; - -#ifdef DEBUG_AC97 -#define dolog(...) AUD_log ("ac97", __VA_ARGS__) -#else -#define dolog(...) -#endif - -#define MKREGS(prefix, start) \ -enum { \ - prefix ## _BDBAR = start, \ - prefix ## _CIV = start + 4, \ - prefix ## _LVI = start + 5, \ - prefix ## _SR = start + 6, \ - prefix ## _PICB = start + 8, \ - prefix ## _PIV = start + 10, \ - prefix ## _CR = start + 11 \ -} - -enum { - PI_INDEX = 0, - PO_INDEX, - MC_INDEX, - LAST_INDEX -}; - -MKREGS (PI, PI_INDEX * 16); -MKREGS (PO, PO_INDEX * 16); -MKREGS (MC, MC_INDEX * 16); - -enum { - GLOB_CNT = 0x2c, - GLOB_STA = 0x30, - CAS = 0x34 -}; - -#define GET_BM(index) (((index) >> 4) & 3) - -static void po_callback (void *opaque, int free); -static void pi_callback (void *opaque, int avail); -static void mc_callback (void *opaque, int avail); - -static void warm_reset (AC97LinkState *s) -{ - (void) s; -} - -static void cold_reset (AC97LinkState * s) -{ - (void) s; -} - -static void fetch_bd (AC97LinkState *s, AC97BusMasterRegs *r) -{ - uint8_t b[8]; - - pci_dma_read (&s->dev, r->bdbar + r->civ * 8, b, 8); - r->bd_valid = 1; - r->bd.addr = le32_to_cpu (*(uint32_t *) &b[0]) & ~3; - r->bd.ctl_len = le32_to_cpu (*(uint32_t *) &b[4]); - r->picb = r->bd.ctl_len & 0xffff; - dolog ("bd %2d addr=%#x ctl=%#06x len=%#x(%d bytes)\n", - r->civ, r->bd.addr, r->bd.ctl_len >> 16, - r->bd.ctl_len & 0xffff, - (r->bd.ctl_len & 0xffff) << 1); -} - -static void update_sr (AC97LinkState *s, AC97BusMasterRegs *r, uint32_t new_sr) -{ - int event = 0; - int level = 0; - uint32_t new_mask = new_sr & SR_INT_MASK; - uint32_t old_mask = r->sr & SR_INT_MASK; - uint32_t masks[] = {GS_PIINT, GS_POINT, GS_MINT}; - - if (new_mask ^ old_mask) { - /** @todo is IRQ deasserted when only one of status bits is cleared? */ - if (!new_mask) { - event = 1; - level = 0; - } - else { - if ((new_mask & SR_LVBCI) && (r->cr & CR_LVBIE)) { - event = 1; - level = 1; - } - if ((new_mask & SR_BCIS) && (r->cr & CR_IOCE)) { - event = 1; - level = 1; - } - } - } - - r->sr = new_sr; - - dolog ("IOC%d LVB%d sr=%#x event=%d level=%d\n", - r->sr & SR_BCIS, r->sr & SR_LVBCI, - r->sr, - event, level); - - if (!event) - return; - - if (level) { - s->glob_sta |= masks[r - s->bm_regs]; - dolog ("set irq level=1\n"); - pci_irq_assert(&s->dev); - } - else { - s->glob_sta &= ~masks[r - s->bm_regs]; - dolog ("set irq level=0\n"); - pci_irq_deassert(&s->dev); - } -} - -static void voice_set_active (AC97LinkState *s, int bm_index, int on) -{ - switch (bm_index) { - case PI_INDEX: - AUD_set_active_in (s->voice_pi, on); - break; - - case PO_INDEX: - AUD_set_active_out (s->voice_po, on); - break; - - case MC_INDEX: - AUD_set_active_in (s->voice_mc, on); - break; - - default: - AUD_log ("ac97", "invalid bm_index(%d) in voice_set_active", bm_index); - break; - } -} - -static void reset_bm_regs (AC97LinkState *s, AC97BusMasterRegs *r) -{ - dolog ("reset_bm_regs\n"); - r->bdbar = 0; - r->civ = 0; - r->lvi = 0; - /** todo do we need to do that? */ - update_sr (s, r, SR_DCH); - r->picb = 0; - r->piv = 0; - r->cr = r->cr & CR_DONT_CLEAR_MASK; - r->bd_valid = 0; - - voice_set_active (s, r - s->bm_regs, 0); - memset (s->silence, 0, sizeof (s->silence)); -} - -static void mixer_store (AC97LinkState *s, uint32_t i, uint16_t v) -{ - if (i + 2 > sizeof (s->mixer_data)) { - dolog ("mixer_store: index %d out of bounds %zd\n", - i, sizeof (s->mixer_data)); - return; - } - - s->mixer_data[i + 0] = v & 0xff; - s->mixer_data[i + 1] = v >> 8; -} - -static uint16_t mixer_load (AC97LinkState *s, uint32_t i) -{ - uint16_t val = 0xffff; - - if (i + 2 > sizeof (s->mixer_data)) { - dolog ("mixer_load: index %d out of bounds %zd\n", - i, sizeof (s->mixer_data)); - } - else { - val = s->mixer_data[i + 0] | (s->mixer_data[i + 1] << 8); - } - - return val; -} - -static void open_voice (AC97LinkState *s, int index, int freq) -{ - struct audsettings as; - - as.freq = freq; - as.nchannels = 2; - as.fmt = AUD_FMT_S16; - as.endianness = 0; - - if (freq > 0) { - s->invalid_freq[index] = 0; - switch (index) { - case PI_INDEX: - s->voice_pi = AUD_open_in ( - &s->card, - s->voice_pi, - "ac97.pi", - s, - pi_callback, - &as - ); - break; - - case PO_INDEX: - s->voice_po = AUD_open_out ( - &s->card, - s->voice_po, - "ac97.po", - s, - po_callback, - &as - ); - break; - - case MC_INDEX: - s->voice_mc = AUD_open_in ( - &s->card, - s->voice_mc, - "ac97.mc", - s, - mc_callback, - &as - ); - break; - } - } - else { - s->invalid_freq[index] = freq; - switch (index) { - case PI_INDEX: - AUD_close_in (&s->card, s->voice_pi); - s->voice_pi = NULL; - break; - - case PO_INDEX: - AUD_close_out (&s->card, s->voice_po); - s->voice_po = NULL; - break; - - case MC_INDEX: - AUD_close_in (&s->card, s->voice_mc); - s->voice_mc = NULL; - break; - } - } -} - -static void reset_voices (AC97LinkState *s, uint8_t active[LAST_INDEX]) -{ - uint16_t freq; - - freq = mixer_load (s, AC97_PCM_LR_ADC_Rate); - open_voice (s, PI_INDEX, freq); - AUD_set_active_in (s->voice_pi, active[PI_INDEX]); - - freq = mixer_load (s, AC97_PCM_Front_DAC_Rate); - open_voice (s, PO_INDEX, freq); - AUD_set_active_out (s->voice_po, active[PO_INDEX]); - - freq = mixer_load (s, AC97_MIC_ADC_Rate); - open_voice (s, MC_INDEX, freq); - AUD_set_active_in (s->voice_mc, active[MC_INDEX]); -} - -static void get_volume (uint16_t vol, uint16_t mask, int inverse, - int *mute, uint8_t *lvol, uint8_t *rvol) -{ - *mute = (vol >> MUTE_SHIFT) & 1; - *rvol = (255 * (vol & mask)) / mask; - *lvol = (255 * ((vol >> 8) & mask)) / mask; - - if (inverse) { - *rvol = 255 - *rvol; - *lvol = 255 - *lvol; - } -} - -static void update_combined_volume_out (AC97LinkState *s) -{ - uint8_t lvol, rvol, plvol, prvol; - int mute, pmute; - - get_volume (mixer_load (s, AC97_Master_Volume_Mute), 0x3f, 1, - &mute, &lvol, &rvol); - get_volume (mixer_load (s, AC97_PCM_Out_Volume_Mute), 0x1f, 1, - &pmute, &plvol, &prvol); - - mute = mute | pmute; - lvol = (lvol * plvol) / 255; - rvol = (rvol * prvol) / 255; - - AUD_set_volume_out (s->voice_po, mute, lvol, rvol); -} - -static void update_volume_in (AC97LinkState *s) -{ - uint8_t lvol, rvol; - int mute; - - get_volume (mixer_load (s, AC97_Record_Gain_Mute), 0x0f, 0, - &mute, &lvol, &rvol); - - AUD_set_volume_in (s->voice_pi, mute, lvol, rvol); -} - -static void set_volume (AC97LinkState *s, int index, uint32_t val) -{ - switch (index) { - case AC97_Master_Volume_Mute: - val &= 0xbf3f; - mixer_store (s, index, val); - update_combined_volume_out (s); - break; - case AC97_PCM_Out_Volume_Mute: - val &= 0x9f1f; - mixer_store (s, index, val); - update_combined_volume_out (s); - break; - case AC97_Record_Gain_Mute: - val &= 0x8f0f; - mixer_store (s, index, val); - update_volume_in (s); - break; - } -} - -static void record_select (AC97LinkState *s, uint32_t val) -{ - uint8_t rs = val & REC_MASK; - uint8_t ls = (val >> 8) & REC_MASK; - mixer_store (s, AC97_Record_Select, rs | (ls << 8)); -} - -static void mixer_reset (AC97LinkState *s) -{ - uint8_t active[LAST_INDEX]; - - dolog ("mixer_reset\n"); - memset (s->mixer_data, 0, sizeof (s->mixer_data)); - memset (active, 0, sizeof (active)); - mixer_store (s, AC97_Reset , 0x0000); /* 6940 */ - mixer_store (s, AC97_Headphone_Volume_Mute , 0x0000); - mixer_store (s, AC97_Master_Volume_Mono_Mute , 0x0000); - mixer_store (s, AC97_Master_Tone_RL, 0x0000); - mixer_store (s, AC97_PC_BEEP_Volume_Mute , 0x0000); - mixer_store (s, AC97_Phone_Volume_Mute , 0x0000); - mixer_store (s, AC97_Mic_Volume_Mute , 0x0000); - mixer_store (s, AC97_Line_In_Volume_Mute , 0x0000); - mixer_store (s, AC97_CD_Volume_Mute , 0x0000); - mixer_store (s, AC97_Video_Volume_Mute , 0x0000); - mixer_store (s, AC97_Aux_Volume_Mute , 0x0000); - mixer_store (s, AC97_Record_Gain_Mic_Mute , 0x0000); - mixer_store (s, AC97_General_Purpose , 0x0000); - mixer_store (s, AC97_3D_Control , 0x0000); - mixer_store (s, AC97_Powerdown_Ctrl_Stat , 0x000f); - - /* - * Sigmatel 9700 (STAC9700) - */ - mixer_store (s, AC97_Vendor_ID1 , 0x8384); - mixer_store (s, AC97_Vendor_ID2 , 0x7600); /* 7608 */ - - mixer_store (s, AC97_Extended_Audio_ID , 0x0809); - mixer_store (s, AC97_Extended_Audio_Ctrl_Stat, 0x0009); - mixer_store (s, AC97_PCM_Front_DAC_Rate , 0xbb80); - mixer_store (s, AC97_PCM_Surround_DAC_Rate , 0xbb80); - mixer_store (s, AC97_PCM_LFE_DAC_Rate , 0xbb80); - mixer_store (s, AC97_PCM_LR_ADC_Rate , 0xbb80); - mixer_store (s, AC97_MIC_ADC_Rate , 0xbb80); - - record_select (s, 0); - set_volume (s, AC97_Master_Volume_Mute, 0x8000); - set_volume (s, AC97_PCM_Out_Volume_Mute, 0x8808); - set_volume (s, AC97_Record_Gain_Mute, 0x8808); - - reset_voices (s, active); -} - -/** - * Native audio mixer - * I/O Reads - */ -static uint32_t nam_readb (void *opaque, uint32_t addr) -{ - AC97LinkState *s = opaque; - dolog ("U nam readb %#x\n", addr); - s->cas = 0; - return ~0U; -} - -static uint32_t nam_readw (void *opaque, uint32_t addr) -{ - AC97LinkState *s = opaque; - uint32_t val = ~0U; - uint32_t index = addr; - s->cas = 0; - val = mixer_load (s, index); - return val; -} - -static uint32_t nam_readl (void *opaque, uint32_t addr) -{ - AC97LinkState *s = opaque; - dolog ("U nam readl %#x\n", addr); - s->cas = 0; - return ~0U; -} - -/** - * Native audio mixer - * I/O Writes - */ -static void nam_writeb (void *opaque, uint32_t addr, uint32_t val) -{ - AC97LinkState *s = opaque; - dolog ("U nam writeb %#x <- %#x\n", addr, val); - s->cas = 0; -} - -static void nam_writew (void *opaque, uint32_t addr, uint32_t val) -{ - AC97LinkState *s = opaque; - uint32_t index = addr; - s->cas = 0; - switch (index) { - case AC97_Reset: - mixer_reset (s); - break; - case AC97_Powerdown_Ctrl_Stat: - val &= ~0x800f; - val |= mixer_load (s, index) & 0xf; - mixer_store (s, index, val); - break; - case AC97_PCM_Out_Volume_Mute: - case AC97_Master_Volume_Mute: - case AC97_Record_Gain_Mute: - set_volume (s, index, val); - break; - case AC97_Record_Select: - record_select (s, val); - break; - case AC97_Vendor_ID1: - case AC97_Vendor_ID2: - dolog ("Attempt to write vendor ID to %#x\n", val); - break; - case AC97_Extended_Audio_ID: - dolog ("Attempt to write extended audio ID to %#x\n", val); - break; - case AC97_Extended_Audio_Ctrl_Stat: - if (!(val & EACS_VRA)) { - mixer_store (s, AC97_PCM_Front_DAC_Rate, 0xbb80); - mixer_store (s, AC97_PCM_LR_ADC_Rate, 0xbb80); - open_voice (s, PI_INDEX, 48000); - open_voice (s, PO_INDEX, 48000); - } - if (!(val & EACS_VRM)) { - mixer_store (s, AC97_MIC_ADC_Rate, 0xbb80); - open_voice (s, MC_INDEX, 48000); - } - dolog ("Setting extended audio control to %#x\n", val); - mixer_store (s, AC97_Extended_Audio_Ctrl_Stat, val); - break; - case AC97_PCM_Front_DAC_Rate: - if (mixer_load (s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) { - mixer_store (s, index, val); - dolog ("Set front DAC rate to %d\n", val); - open_voice (s, PO_INDEX, val); - } - else { - dolog ("Attempt to set front DAC rate to %d, " - "but VRA is not set\n", - val); - } - break; - case AC97_MIC_ADC_Rate: - if (mixer_load (s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRM) { - mixer_store (s, index, val); - dolog ("Set MIC ADC rate to %d\n", val); - open_voice (s, MC_INDEX, val); - } - else { - dolog ("Attempt to set MIC ADC rate to %d, " - "but VRM is not set\n", - val); - } - break; - case AC97_PCM_LR_ADC_Rate: - if (mixer_load (s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) { - mixer_store (s, index, val); - dolog ("Set front LR ADC rate to %d\n", val); - open_voice (s, PI_INDEX, val); - } - else { - dolog ("Attempt to set LR ADC rate to %d, but VRA is not set\n", - val); - } - break; - case AC97_Headphone_Volume_Mute: - case AC97_Master_Volume_Mono_Mute: - case AC97_Master_Tone_RL: - case AC97_PC_BEEP_Volume_Mute: - case AC97_Phone_Volume_Mute: - case AC97_Mic_Volume_Mute: - case AC97_Line_In_Volume_Mute: - case AC97_CD_Volume_Mute: - case AC97_Video_Volume_Mute: - case AC97_Aux_Volume_Mute: - case AC97_Record_Gain_Mic_Mute: - case AC97_General_Purpose: - case AC97_3D_Control: - case AC97_Sigmatel_Analog: - case AC97_Sigmatel_Dac2Invert: - /* None of the features in these regs are emulated, so they are RO */ - break; - default: - dolog ("U nam writew %#x <- %#x\n", addr, val); - mixer_store (s, index, val); - break; - } -} - -static void nam_writel (void *opaque, uint32_t addr, uint32_t val) -{ - AC97LinkState *s = opaque; - dolog ("U nam writel %#x <- %#x\n", addr, val); - s->cas = 0; -} - -/** - * Native audio bus master - * I/O Reads - */ -static uint32_t nabm_readb (void *opaque, uint32_t addr) -{ - AC97LinkState *s = opaque; - AC97BusMasterRegs *r = NULL; - uint32_t index = addr; - uint32_t val = ~0U; - - switch (index) { - case CAS: - dolog ("CAS %d\n", s->cas); - val = s->cas; - s->cas = 1; - break; - case PI_CIV: - case PO_CIV: - case MC_CIV: - r = &s->bm_regs[GET_BM (index)]; - val = r->civ; - dolog ("CIV[%d] -> %#x\n", GET_BM (index), val); - break; - case PI_LVI: - case PO_LVI: - case MC_LVI: - r = &s->bm_regs[GET_BM (index)]; - val = r->lvi; - dolog ("LVI[%d] -> %#x\n", GET_BM (index), val); - break; - case PI_PIV: - case PO_PIV: - case MC_PIV: - r = &s->bm_regs[GET_BM (index)]; - val = r->piv; - dolog ("PIV[%d] -> %#x\n", GET_BM (index), val); - break; - case PI_CR: - case PO_CR: - case MC_CR: - r = &s->bm_regs[GET_BM (index)]; - val = r->cr; - dolog ("CR[%d] -> %#x\n", GET_BM (index), val); - break; - case PI_SR: - case PO_SR: - case MC_SR: - r = &s->bm_regs[GET_BM (index)]; - val = r->sr & 0xff; - dolog ("SRb[%d] -> %#x\n", GET_BM (index), val); - break; - default: - dolog ("U nabm readb %#x -> %#x\n", addr, val); - break; - } - return val; -} - -static uint32_t nabm_readw (void *opaque, uint32_t addr) -{ - AC97LinkState *s = opaque; - AC97BusMasterRegs *r = NULL; - uint32_t index = addr; - uint32_t val = ~0U; - - switch (index) { - case PI_SR: - case PO_SR: - case MC_SR: - r = &s->bm_regs[GET_BM (index)]; - val = r->sr; - dolog ("SR[%d] -> %#x\n", GET_BM (index), val); - break; - case PI_PICB: - case PO_PICB: - case MC_PICB: - r = &s->bm_regs[GET_BM (index)]; - val = r->picb; - dolog ("PICB[%d] -> %#x\n", GET_BM (index), val); - break; - default: - dolog ("U nabm readw %#x -> %#x\n", addr, val); - break; - } - return val; -} - -static uint32_t nabm_readl (void *opaque, uint32_t addr) -{ - AC97LinkState *s = opaque; - AC97BusMasterRegs *r = NULL; - uint32_t index = addr; - uint32_t val = ~0U; - - switch (index) { - case PI_BDBAR: - case PO_BDBAR: - case MC_BDBAR: - r = &s->bm_regs[GET_BM (index)]; - val = r->bdbar; - dolog ("BMADDR[%d] -> %#x\n", GET_BM (index), val); - break; - case PI_CIV: - case PO_CIV: - case MC_CIV: - r = &s->bm_regs[GET_BM (index)]; - val = r->civ | (r->lvi << 8) | (r->sr << 16); - dolog ("CIV LVI SR[%d] -> %#x, %#x, %#x\n", GET_BM (index), - r->civ, r->lvi, r->sr); - break; - case PI_PICB: - case PO_PICB: - case MC_PICB: - r = &s->bm_regs[GET_BM (index)]; - val = r->picb | (r->piv << 16) | (r->cr << 24); - dolog ("PICB PIV CR[%d] -> %#x %#x %#x %#x\n", GET_BM (index), - val, r->picb, r->piv, r->cr); - break; - case GLOB_CNT: - val = s->glob_cnt; - dolog ("glob_cnt -> %#x\n", val); - break; - case GLOB_STA: - val = s->glob_sta | GS_S0CR; - dolog ("glob_sta -> %#x\n", val); - break; - default: - dolog ("U nabm readl %#x -> %#x\n", addr, val); - break; - } - return val; -} - -/** - * Native audio bus master - * I/O Writes - */ -static void nabm_writeb (void *opaque, uint32_t addr, uint32_t val) -{ - AC97LinkState *s = opaque; - AC97BusMasterRegs *r = NULL; - uint32_t index = addr; - switch (index) { - case PI_LVI: - case PO_LVI: - case MC_LVI: - r = &s->bm_regs[GET_BM (index)]; - if ((r->cr & CR_RPBM) && (r->sr & SR_DCH)) { - r->sr &= ~(SR_DCH | SR_CELV); - r->civ = r->piv; - r->piv = (r->piv + 1) % 32; - fetch_bd (s, r); - } - r->lvi = val % 32; - dolog ("LVI[%d] <- %#x\n", GET_BM (index), val); - break; - case PI_CR: - case PO_CR: - case MC_CR: - r = &s->bm_regs[GET_BM (index)]; - if (val & CR_RR) { - reset_bm_regs (s, r); - } - else { - r->cr = val & CR_VALID_MASK; - if (!(r->cr & CR_RPBM)) { - voice_set_active (s, r - s->bm_regs, 0); - r->sr |= SR_DCH; - } - else { - r->civ = r->piv; - r->piv = (r->piv + 1) % 32; - fetch_bd (s, r); - r->sr &= ~SR_DCH; - voice_set_active (s, r - s->bm_regs, 1); - } - } - dolog ("CR[%d] <- %#x (cr %#x)\n", GET_BM (index), val, r->cr); - break; - case PI_SR: - case PO_SR: - case MC_SR: - r = &s->bm_regs[GET_BM (index)]; - r->sr |= val & ~(SR_RO_MASK | SR_WCLEAR_MASK); - update_sr (s, r, r->sr & ~(val & SR_WCLEAR_MASK)); - dolog ("SR[%d] <- %#x (sr %#x)\n", GET_BM (index), val, r->sr); - break; - default: - dolog ("U nabm writeb %#x <- %#x\n", addr, val); - break; - } -} - -static void nabm_writew (void *opaque, uint32_t addr, uint32_t val) -{ - AC97LinkState *s = opaque; - AC97BusMasterRegs *r = NULL; - uint32_t index = addr; - switch (index) { - case PI_SR: - case PO_SR: - case MC_SR: - r = &s->bm_regs[GET_BM (index)]; - r->sr |= val & ~(SR_RO_MASK | SR_WCLEAR_MASK); - update_sr (s, r, r->sr & ~(val & SR_WCLEAR_MASK)); - dolog ("SR[%d] <- %#x (sr %#x)\n", GET_BM (index), val, r->sr); - break; - default: - dolog ("U nabm writew %#x <- %#x\n", addr, val); - break; - } -} - -static void nabm_writel (void *opaque, uint32_t addr, uint32_t val) -{ - AC97LinkState *s = opaque; - AC97BusMasterRegs *r = NULL; - uint32_t index = addr; - switch (index) { - case PI_BDBAR: - case PO_BDBAR: - case MC_BDBAR: - r = &s->bm_regs[GET_BM (index)]; - r->bdbar = val & ~3; - dolog ("BDBAR[%d] <- %#x (bdbar %#x)\n", - GET_BM (index), val, r->bdbar); - break; - case GLOB_CNT: - if (val & GC_WR) - warm_reset (s); - if (val & GC_CR) - cold_reset (s); - if (!(val & (GC_WR | GC_CR))) - s->glob_cnt = val & GC_VALID_MASK; - dolog ("glob_cnt <- %#x (glob_cnt %#x)\n", val, s->glob_cnt); - break; - case GLOB_STA: - s->glob_sta &= ~(val & GS_WCLEAR_MASK); - s->glob_sta |= (val & ~(GS_WCLEAR_MASK | GS_RO_MASK)) & GS_VALID_MASK; - dolog ("glob_sta <- %#x (glob_sta %#x)\n", val, s->glob_sta); - break; - default: - dolog ("U nabm writel %#x <- %#x\n", addr, val); - break; - } -} - -static int write_audio (AC97LinkState *s, AC97BusMasterRegs *r, - int max, int *stop) -{ - uint8_t tmpbuf[4096]; - uint32_t addr = r->bd.addr; - uint32_t temp = r->picb << 1; - uint32_t written = 0; - int to_copy = 0; - temp = audio_MIN (temp, max); - - if (!temp) { - *stop = 1; - return 0; - } - - while (temp) { - int copied; - to_copy = audio_MIN (temp, sizeof (tmpbuf)); - pci_dma_read (&s->dev, addr, tmpbuf, to_copy); - copied = AUD_write (s->voice_po, tmpbuf, to_copy); - dolog ("write_audio max=%x to_copy=%x copied=%x\n", - max, to_copy, copied); - if (!copied) { - *stop = 1; - break; - } - temp -= copied; - addr += copied; - written += copied; - } - - if (!temp) { - if (to_copy < 4) { - dolog ("whoops\n"); - s->last_samp = 0; - } - else { - s->last_samp = *(uint32_t *) &tmpbuf[to_copy - 4]; - } - } - - r->bd.addr = addr; - return written; -} - -static void write_bup (AC97LinkState *s, int elapsed) -{ - dolog ("write_bup\n"); - if (!(s->bup_flag & BUP_SET)) { - if (s->bup_flag & BUP_LAST) { - int i; - uint8_t *p = s->silence; - for (i = 0; i < sizeof (s->silence) / 4; i++, p += 4) { - *(uint32_t *) p = s->last_samp; - } - } - else { - memset (s->silence, 0, sizeof (s->silence)); - } - s->bup_flag |= BUP_SET; - } - - while (elapsed) { - int temp = audio_MIN (elapsed, sizeof (s->silence)); - while (temp) { - int copied = AUD_write (s->voice_po, s->silence, temp); - if (!copied) - return; - temp -= copied; - elapsed -= copied; - } - } -} - -static int read_audio (AC97LinkState *s, AC97BusMasterRegs *r, - int max, int *stop) -{ - uint8_t tmpbuf[4096]; - uint32_t addr = r->bd.addr; - uint32_t temp = r->picb << 1; - uint32_t nread = 0; - int to_copy = 0; - SWVoiceIn *voice = (r - s->bm_regs) == MC_INDEX ? s->voice_mc : s->voice_pi; - - temp = audio_MIN (temp, max); - - if (!temp) { - *stop = 1; - return 0; - } - - while (temp) { - int acquired; - to_copy = audio_MIN (temp, sizeof (tmpbuf)); - acquired = AUD_read (voice, tmpbuf, to_copy); - if (!acquired) { - *stop = 1; - break; - } - pci_dma_write (&s->dev, addr, tmpbuf, acquired); - temp -= acquired; - addr += acquired; - nread += acquired; - } - - r->bd.addr = addr; - return nread; -} - -static void transfer_audio (AC97LinkState *s, int index, int elapsed) -{ - AC97BusMasterRegs *r = &s->bm_regs[index]; - int stop = 0; - - if (s->invalid_freq[index]) { - AUD_log ("ac97", "attempt to use voice %d with invalid frequency %d\n", - index, s->invalid_freq[index]); - return; - } - - if (r->sr & SR_DCH) { - if (r->cr & CR_RPBM) { - switch (index) { - case PO_INDEX: - write_bup (s, elapsed); - break; - } - } - return; - } - - while ((elapsed >> 1) && !stop) { - int temp; - - if (!r->bd_valid) { - dolog ("invalid bd\n"); - fetch_bd (s, r); - } - - if (!r->picb) { - dolog ("fresh bd %d is empty %#x %#x\n", - r->civ, r->bd.addr, r->bd.ctl_len); - if (r->civ == r->lvi) { - r->sr |= SR_DCH; /* CELV? */ - s->bup_flag = 0; - break; - } - r->sr &= ~SR_CELV; - r->civ = r->piv; - r->piv = (r->piv + 1) % 32; - fetch_bd (s, r); - return; - } - - switch (index) { - case PO_INDEX: - temp = write_audio (s, r, elapsed, &stop); - elapsed -= temp; - r->picb -= (temp >> 1); - break; - - case PI_INDEX: - case MC_INDEX: - temp = read_audio (s, r, elapsed, &stop); - elapsed -= temp; - r->picb -= (temp >> 1); - break; - } - - if (!r->picb) { - uint32_t new_sr = r->sr & ~SR_CELV; - - if (r->bd.ctl_len & BD_IOC) { - new_sr |= SR_BCIS; - } - - if (r->civ == r->lvi) { - dolog ("Underrun civ (%d) == lvi (%d)\n", r->civ, r->lvi); - - new_sr |= SR_LVBCI | SR_DCH | SR_CELV; - stop = 1; - s->bup_flag = (r->bd.ctl_len & BD_BUP) ? BUP_LAST : 0; - } - else { - r->civ = r->piv; - r->piv = (r->piv + 1) % 32; - fetch_bd (s, r); - } - - update_sr (s, r, new_sr); - } - } -} - -static void pi_callback (void *opaque, int avail) -{ - transfer_audio (opaque, PI_INDEX, avail); -} - -static void mc_callback (void *opaque, int avail) -{ - transfer_audio (opaque, MC_INDEX, avail); -} - -static void po_callback (void *opaque, int free) -{ - transfer_audio (opaque, PO_INDEX, free); -} - -static const VMStateDescription vmstate_ac97_bm_regs = { - .name = "ac97_bm_regs", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32 (bdbar, AC97BusMasterRegs), - VMSTATE_UINT8 (civ, AC97BusMasterRegs), - VMSTATE_UINT8 (lvi, AC97BusMasterRegs), - VMSTATE_UINT16 (sr, AC97BusMasterRegs), - VMSTATE_UINT16 (picb, AC97BusMasterRegs), - VMSTATE_UINT8 (piv, AC97BusMasterRegs), - VMSTATE_UINT8 (cr, AC97BusMasterRegs), - VMSTATE_UINT32 (bd_valid, AC97BusMasterRegs), - VMSTATE_UINT32 (bd.addr, AC97BusMasterRegs), - VMSTATE_UINT32 (bd.ctl_len, AC97BusMasterRegs), - VMSTATE_END_OF_LIST () - } -}; - -static int ac97_post_load (void *opaque, int version_id) -{ - uint8_t active[LAST_INDEX]; - AC97LinkState *s = opaque; - - record_select (s, mixer_load (s, AC97_Record_Select)); - set_volume (s, AC97_Master_Volume_Mute, - mixer_load (s, AC97_Master_Volume_Mute)); - set_volume (s, AC97_PCM_Out_Volume_Mute, - mixer_load (s, AC97_PCM_Out_Volume_Mute)); - set_volume (s, AC97_Record_Gain_Mute, - mixer_load (s, AC97_Record_Gain_Mute)); - - active[PI_INDEX] = !!(s->bm_regs[PI_INDEX].cr & CR_RPBM); - active[PO_INDEX] = !!(s->bm_regs[PO_INDEX].cr & CR_RPBM); - active[MC_INDEX] = !!(s->bm_regs[MC_INDEX].cr & CR_RPBM); - reset_voices (s, active); - - s->bup_flag = 0; - s->last_samp = 0; - return 0; -} - -static bool is_version_2 (void *opaque, int version_id) -{ - return version_id == 2; -} - -static const VMStateDescription vmstate_ac97 = { - .name = "ac97", - .version_id = 3, - .minimum_version_id = 2, - .post_load = ac97_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE (dev, AC97LinkState), - VMSTATE_UINT32 (glob_cnt, AC97LinkState), - VMSTATE_UINT32 (glob_sta, AC97LinkState), - VMSTATE_UINT32 (cas, AC97LinkState), - VMSTATE_STRUCT_ARRAY (bm_regs, AC97LinkState, 3, 1, - vmstate_ac97_bm_regs, AC97BusMasterRegs), - VMSTATE_BUFFER (mixer_data, AC97LinkState), - VMSTATE_UNUSED_TEST (is_version_2, 3), - VMSTATE_END_OF_LIST () - } -}; - -static uint64_t nam_read(void *opaque, hwaddr addr, unsigned size) -{ - if ((addr / size) > 256) { - return -1; - } - - switch (size) { - case 1: - return nam_readb(opaque, addr); - case 2: - return nam_readw(opaque, addr); - case 4: - return nam_readl(opaque, addr); - default: - return -1; - } -} - -static void nam_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - if ((addr / size) > 256) { - return; - } - - switch (size) { - case 1: - nam_writeb(opaque, addr, val); - break; - case 2: - nam_writew(opaque, addr, val); - break; - case 4: - nam_writel(opaque, addr, val); - break; - } -} - -static const MemoryRegionOps ac97_io_nam_ops = { - .read = nam_read, - .write = nam_write, - .impl = { - .min_access_size = 1, - .max_access_size = 4, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static uint64_t nabm_read(void *opaque, hwaddr addr, unsigned size) -{ - if ((addr / size) > 64) { - return -1; - } - - switch (size) { - case 1: - return nabm_readb(opaque, addr); - case 2: - return nabm_readw(opaque, addr); - case 4: - return nabm_readl(opaque, addr); - default: - return -1; - } -} - -static void nabm_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - if ((addr / size) > 64) { - return; - } - - switch (size) { - case 1: - nabm_writeb(opaque, addr, val); - break; - case 2: - nabm_writew(opaque, addr, val); - break; - case 4: - nabm_writel(opaque, addr, val); - break; - } -} - - -static const MemoryRegionOps ac97_io_nabm_ops = { - .read = nabm_read, - .write = nabm_write, - .impl = { - .min_access_size = 1, - .max_access_size = 4, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void ac97_on_reset (DeviceState *dev) -{ - AC97LinkState *s = container_of(dev, AC97LinkState, dev.qdev); - - reset_bm_regs (s, &s->bm_regs[0]); - reset_bm_regs (s, &s->bm_regs[1]); - reset_bm_regs (s, &s->bm_regs[2]); - - /* - * Reset the mixer too. The Windows XP driver seems to rely on - * this. At least it wants to read the vendor id before it resets - * the codec manually. - */ - mixer_reset (s); -} - -static void ac97_realize(PCIDevice *dev, Error **errp) -{ - AC97LinkState *s = DO_UPCAST (AC97LinkState, dev, dev); - uint8_t *c = s->dev.config; - - /* TODO: no need to override */ - c[PCI_COMMAND] = 0x00; /* pcicmd pci command rw, ro */ - c[PCI_COMMAND + 1] = 0x00; - - /* TODO: */ - c[PCI_STATUS] = PCI_STATUS_FAST_BACK; /* pcists pci status rwc, ro */ - c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_MEDIUM >> 8; - - c[PCI_CLASS_PROG] = 0x00; /* pi programming interface ro */ - - /* TODO set when bar is registered. no need to override. */ - /* nabmar native audio mixer base address rw */ - c[PCI_BASE_ADDRESS_0] = PCI_BASE_ADDRESS_SPACE_IO; - c[PCI_BASE_ADDRESS_0 + 1] = 0x00; - c[PCI_BASE_ADDRESS_0 + 2] = 0x00; - c[PCI_BASE_ADDRESS_0 + 3] = 0x00; - - /* TODO set when bar is registered. no need to override. */ - /* nabmbar native audio bus mastering base address rw */ - c[PCI_BASE_ADDRESS_0 + 4] = PCI_BASE_ADDRESS_SPACE_IO; - c[PCI_BASE_ADDRESS_0 + 5] = 0x00; - c[PCI_BASE_ADDRESS_0 + 6] = 0x00; - c[PCI_BASE_ADDRESS_0 + 7] = 0x00; - - if (s->use_broken_id) { - c[PCI_SUBSYSTEM_VENDOR_ID] = 0x86; - c[PCI_SUBSYSTEM_VENDOR_ID + 1] = 0x80; - c[PCI_SUBSYSTEM_ID] = 0x00; - c[PCI_SUBSYSTEM_ID + 1] = 0x00; - } - - c[PCI_INTERRUPT_LINE] = 0x00; /* intr_ln interrupt line rw */ - c[PCI_INTERRUPT_PIN] = 0x01; /* intr_pn interrupt pin ro */ - - memory_region_init_io (&s->io_nam, OBJECT(s), &ac97_io_nam_ops, s, - "ac97-nam", 1024); - memory_region_init_io (&s->io_nabm, OBJECT(s), &ac97_io_nabm_ops, s, - "ac97-nabm", 256); - pci_register_bar (&s->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_nam); - pci_register_bar (&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_nabm); - AUD_register_card ("ac97", &s->card); - ac97_on_reset (&s->dev.qdev); -} - -static int ac97_init (PCIBus *bus) -{ - pci_create_simple (bus, -1, "AC97"); - return 0; -} - -static Property ac97_properties[] = { - DEFINE_PROP_UINT32 ("use_broken_id", AC97LinkState, use_broken_id, 0), - DEFINE_PROP_END_OF_LIST (), -}; - -static void ac97_class_init (ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS (klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS (klass); - - k->realize = ac97_realize; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82801AA_5; - k->revision = 0x01; - k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO; - set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - dc->desc = "Intel 82801AA AC97 Audio"; - dc->vmsd = &vmstate_ac97; - dc->props = ac97_properties; - dc->reset = ac97_on_reset; -} - -static const TypeInfo ac97_info = { - .name = "AC97", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof (AC97LinkState), - .class_init = ac97_class_init, -}; - -static void ac97_register_types (void) -{ - type_register_static (&ac97_info); - pci_register_soundhw("ac97", "Intel 82801AA AC97 Audio", ac97_init); -} - -type_init (ac97_register_types) diff --git a/qemu/hw/audio/adlib.c b/qemu/hw/audio/adlib.c deleted file mode 100644 index 7836446fc..000000000 --- a/qemu/hw/audio/adlib.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * QEMU Proxy for OPL2/3 emulation by MAME team - * - * Copyright (c) 2004-2005 Vassili Karpov (malc) - * - * 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/audio/audio.h" -#include "audio/audio.h" -#include "hw/isa/isa.h" - -//#define DEBUG - -#define ADLIB_KILL_TIMERS 1 - -#ifdef HAS_YMF262 -#define ADLIB_DESC "Yamaha YMF262 (OPL3)" -#else -#define ADLIB_DESC "Yamaha YM3812 (OPL2)" -#endif - -#ifdef DEBUG -#include "qemu/timer.h" -#endif - -#define dolog(...) AUD_log ("adlib", __VA_ARGS__) -#ifdef DEBUG -#define ldebug(...) dolog (__VA_ARGS__) -#else -#define ldebug(...) -#endif - -#ifdef HAS_YMF262 -#include "ymf262.h" -void YMF262UpdateOneQEMU (int which, INT16 *dst, int length); -#define SHIFT 2 -#else -#include "fmopl.h" -#define SHIFT 1 -#endif - -#define TYPE_ADLIB "adlib" -#define ADLIB(obj) OBJECT_CHECK(AdlibState, (obj), TYPE_ADLIB) - -typedef struct { - ISADevice parent_obj; - - QEMUSoundCard card; - uint32_t freq; - uint32_t port; - int ticking[2]; - int enabled; - int active; - int bufpos; -#ifdef DEBUG - int64_t exp[2]; -#endif - int16_t *mixbuf; - uint64_t dexp[2]; - SWVoiceOut *voice; - int left, pos, samples; - QEMUAudioTimeStamp ats; -#ifndef HAS_YMF262 - FM_OPL *opl; -#endif - PortioList port_list; -} AdlibState; - -static AdlibState *glob_adlib; - -static void adlib_stop_opl_timer (AdlibState *s, size_t n) -{ -#ifdef HAS_YMF262 - YMF262TimerOver (0, n); -#else - OPLTimerOver (s->opl, n); -#endif - s->ticking[n] = 0; -} - -static void adlib_kill_timers (AdlibState *s) -{ - size_t i; - - for (i = 0; i < 2; ++i) { - if (s->ticking[i]) { - uint64_t delta; - - delta = AUD_get_elapsed_usec_out (s->voice, &s->ats); - ldebug ( - "delta = %f dexp = %f expired => %d\n", - delta / 1000000.0, - s->dexp[i] / 1000000.0, - delta >= s->dexp[i] - ); - if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) { - adlib_stop_opl_timer (s, i); - AUD_init_time_stamp_out (s->voice, &s->ats); - } - } - } -} - -static void adlib_write(void *opaque, uint32_t nport, uint32_t val) -{ - AdlibState *s = opaque; - int a = nport & 3; - - s->active = 1; - AUD_set_active_out (s->voice, 1); - - adlib_kill_timers (s); - -#ifdef HAS_YMF262 - YMF262Write (0, a, val); -#else - OPLWrite (s->opl, a, val); -#endif -} - -static uint32_t adlib_read(void *opaque, uint32_t nport) -{ - AdlibState *s = opaque; - uint8_t data; - int a = nport & 3; - - adlib_kill_timers (s); - -#ifdef HAS_YMF262 - data = YMF262Read (0, a); -#else - data = OPLRead (s->opl, a); -#endif - return data; -} - -static void timer_handler (int c, double interval_Sec) -{ - AdlibState *s = glob_adlib; - unsigned n = c & 1; -#ifdef DEBUG - double interval; - int64_t exp; -#endif - - if (interval_Sec == 0.0) { - s->ticking[n] = 0; - return; - } - - s->ticking[n] = 1; -#ifdef DEBUG - interval = NANOSECONDS_PER_SECOND * interval_Sec; - exp = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + interval; - s->exp[n] = exp; -#endif - - s->dexp[n] = interval_Sec * 1000000.0; - AUD_init_time_stamp_out (s->voice, &s->ats); -} - -static int write_audio (AdlibState *s, int samples) -{ - int net = 0; - int pos = s->pos; - - while (samples) { - int nbytes, wbytes, wsampl; - - nbytes = samples << SHIFT; - wbytes = AUD_write ( - s->voice, - s->mixbuf + (pos << (SHIFT - 1)), - nbytes - ); - - if (wbytes) { - wsampl = wbytes >> SHIFT; - - samples -= wsampl; - pos = (pos + wsampl) % s->samples; - - net += wsampl; - } - else { - break; - } - } - - return net; -} - -static void adlib_callback (void *opaque, int free) -{ - AdlibState *s = opaque; - int samples, net = 0, to_play, written; - - samples = free >> SHIFT; - if (!(s->active && s->enabled) || !samples) { - return; - } - - to_play = audio_MIN (s->left, samples); - while (to_play) { - written = write_audio (s, to_play); - - if (written) { - s->left -= written; - samples -= written; - to_play -= written; - s->pos = (s->pos + written) % s->samples; - } - else { - return; - } - } - - samples = audio_MIN (samples, s->samples - s->pos); - if (!samples) { - return; - } - -#ifdef HAS_YMF262 - YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples); -#else - YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples); -#endif - - while (samples) { - written = write_audio (s, samples); - - if (written) { - net += written; - samples -= written; - s->pos = (s->pos + written) % s->samples; - } - else { - s->left = samples; - return; - } - } -} - -static void Adlib_fini (AdlibState *s) -{ -#ifdef HAS_YMF262 - YMF262Shutdown (); -#else - if (s->opl) { - OPLDestroy (s->opl); - s->opl = NULL; - } -#endif - - g_free(s->mixbuf); - - s->active = 0; - s->enabled = 0; - AUD_remove_card (&s->card); -} - -static MemoryRegionPortio adlib_portio_list[] = { - { 0, 4, 1, .read = adlib_read, .write = adlib_write, }, - { 0, 2, 1, .read = adlib_read, .write = adlib_write, }, - { 0x388, 4, 1, .read = adlib_read, .write = adlib_write, }, - PORTIO_END_OF_LIST(), -}; - -static void adlib_realizefn (DeviceState *dev, Error **errp) -{ - AdlibState *s = ADLIB(dev); - struct audsettings as; - - if (glob_adlib) { - error_setg (errp, "Cannot create more than 1 adlib device"); - return; - } - glob_adlib = s; - -#ifdef HAS_YMF262 - if (YMF262Init (1, 14318180, s->freq)) { - error_setg (errp, "YMF262Init %d failed", s->freq); - return; - } - else { - YMF262SetTimerHandler (0, timer_handler, 0); - s->enabled = 1; - } -#else - s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, s->freq); - if (!s->opl) { - error_setg (errp, "OPLCreate %d failed", s->freq); - return; - } - else { - OPLSetTimerHandler (s->opl, timer_handler, 0); - s->enabled = 1; - } -#endif - - as.freq = s->freq; - as.nchannels = SHIFT; - as.fmt = AUD_FMT_S16; - as.endianness = AUDIO_HOST_ENDIANNESS; - - AUD_register_card ("adlib", &s->card); - - s->voice = AUD_open_out ( - &s->card, - s->voice, - "adlib", - s, - adlib_callback, - &as - ); - if (!s->voice) { - Adlib_fini (s); - error_setg (errp, "Initializing audio voice failed"); - return; - } - - s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT; - s->mixbuf = g_malloc0 (s->samples << SHIFT); - - adlib_portio_list[0].offset = s->port; - adlib_portio_list[1].offset = s->port + 8; - portio_list_init (&s->port_list, OBJECT(s), adlib_portio_list, s, "adlib"); - portio_list_add (&s->port_list, isa_address_space_io(&s->parent_obj), 0); -} - -static Property adlib_properties[] = { - DEFINE_PROP_UINT32 ("iobase", AdlibState, port, 0x220), - DEFINE_PROP_UINT32 ("freq", AdlibState, freq, 44100), - DEFINE_PROP_END_OF_LIST (), -}; - -static void adlib_class_initfn (ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS (klass); - - dc->realize = adlib_realizefn; - set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - dc->desc = ADLIB_DESC; - dc->props = adlib_properties; -} - -static const TypeInfo adlib_info = { - .name = TYPE_ADLIB, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof (AdlibState), - .class_init = adlib_class_initfn, -}; - -static int Adlib_init (ISABus *bus) -{ - isa_create_simple (bus, TYPE_ADLIB); - return 0; -} - -static void adlib_register_types (void) -{ - type_register_static (&adlib_info); - isa_register_soundhw("adlib", ADLIB_DESC, Adlib_init); -} - -type_init (adlib_register_types) diff --git a/qemu/hw/audio/cs4231.c b/qemu/hw/audio/cs4231.c deleted file mode 100644 index caf97c169..000000000 --- a/qemu/hw/audio/cs4231.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - * QEMU Crystal CS4231 audio chip emulation - * - * Copyright (c) 2006 Fabrice Bellard - * - * 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/sysbus.h" -#include "trace.h" - -/* - * In addition to Crystal CS4231 there is a DMA controller on Sparc. - */ -#define CS_SIZE 0x40 -#define CS_REGS 16 -#define CS_DREGS 32 -#define CS_MAXDREG (CS_DREGS - 1) - -#define TYPE_CS4231 "SUNW,CS4231" -#define CS4231(obj) \ - OBJECT_CHECK(CSState, (obj), TYPE_CS4231) - -typedef struct CSState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq irq; - uint32_t regs[CS_REGS]; - uint8_t dregs[CS_DREGS]; -} CSState; - -#define CS_RAP(s) ((s)->regs[0] & CS_MAXDREG) -#define CS_VER 0xa0 -#define CS_CDC_VER 0x8a - -static void cs_reset(DeviceState *d) -{ - CSState *s = CS4231(d); - - memset(s->regs, 0, CS_REGS * 4); - memset(s->dregs, 0, CS_DREGS); - s->dregs[12] = CS_CDC_VER; - s->dregs[25] = CS_VER; -} - -static uint64_t cs_mem_read(void *opaque, hwaddr addr, - unsigned size) -{ - CSState *s = opaque; - uint32_t saddr, ret; - - saddr = addr >> 2; - switch (saddr) { - case 1: - switch (CS_RAP(s)) { - case 3: // Write only - ret = 0; - break; - default: - ret = s->dregs[CS_RAP(s)]; - break; - } - trace_cs4231_mem_readl_dreg(CS_RAP(s), ret); - break; - default: - ret = s->regs[saddr]; - trace_cs4231_mem_readl_reg(saddr, ret); - break; - } - return ret; -} - -static void cs_mem_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - CSState *s = opaque; - uint32_t saddr; - - saddr = addr >> 2; - trace_cs4231_mem_writel_reg(saddr, s->regs[saddr], val); - switch (saddr) { - case 1: - trace_cs4231_mem_writel_dreg(CS_RAP(s), s->dregs[CS_RAP(s)], val); - switch(CS_RAP(s)) { - case 11: - case 25: // Read only - break; - case 12: - val &= 0x40; - val |= CS_CDC_VER; // Codec version - s->dregs[CS_RAP(s)] = val; - break; - default: - s->dregs[CS_RAP(s)] = val; - break; - } - break; - case 2: // Read only - break; - case 4: - if (val & 1) { - cs_reset(DEVICE(s)); - } - val &= 0x7f; - s->regs[saddr] = val; - break; - default: - s->regs[saddr] = val; - break; - } -} - -static const MemoryRegionOps cs_mem_ops = { - .read = cs_mem_read, - .write = cs_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_cs4231 = { - .name ="cs4231", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, CSState, CS_REGS), - VMSTATE_UINT8_ARRAY(dregs, CSState, CS_DREGS), - VMSTATE_END_OF_LIST() - } -}; - -static int cs4231_init1(SysBusDevice *dev) -{ - CSState *s = CS4231(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &cs_mem_ops, s, "cs4321", - CS_SIZE); - sysbus_init_mmio(dev, &s->iomem); - sysbus_init_irq(dev, &s->irq); - - return 0; -} - -static Property cs4231_properties[] = { - {.name = NULL}, -}; - -static void cs4231_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = cs4231_init1; - dc->reset = cs_reset; - dc->vmsd = &vmstate_cs4231; - dc->props = cs4231_properties; -} - -static const TypeInfo cs4231_info = { - .name = TYPE_CS4231, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(CSState), - .class_init = cs4231_class_init, -}; - -static void cs4231_register_types(void) -{ - type_register_static(&cs4231_info); -} - -type_init(cs4231_register_types) diff --git a/qemu/hw/audio/cs4231a.c b/qemu/hw/audio/cs4231a.c deleted file mode 100644 index 3ecd0582b..000000000 --- a/qemu/hw/audio/cs4231a.c +++ /dev/null @@ -1,715 +0,0 @@ -/* - * QEMU Crystal CS4231 audio chip emulation - * - * Copyright (c) 2006 Fabrice Bellard - * - * 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/audio/audio.h" -#include "audio/audio.h" -#include "hw/isa/isa.h" -#include "hw/qdev.h" -#include "qemu/timer.h" - -/* - Missing features: - ADC - Loopback - Timer - ADPCM - More... -*/ - -/* #define DEBUG */ -/* #define DEBUG_XLAW */ - -static struct { - int aci_counter; -} conf = {1}; - -#ifdef DEBUG -#define dolog(...) AUD_log ("cs4231a", __VA_ARGS__) -#else -#define dolog(...) -#endif - -#define lwarn(...) AUD_log ("cs4231a", "warning: " __VA_ARGS__) -#define lerr(...) AUD_log ("cs4231a", "error: " __VA_ARGS__) - -#define CS_REGS 16 -#define CS_DREGS 32 - -#define TYPE_CS4231A "cs4231a" -#define CS4231A(obj) OBJECT_CHECK (CSState, (obj), TYPE_CS4231A) - -typedef struct CSState { - ISADevice dev; - QEMUSoundCard card; - MemoryRegion ioports; - qemu_irq pic; - uint32_t regs[CS_REGS]; - uint8_t dregs[CS_DREGS]; - uint32_t irq; - uint32_t dma; - uint32_t port; - IsaDma *isa_dma; - int shift; - int dma_running; - int audio_free; - int transferred; - int aci_counter; - SWVoiceOut *voice; - int16_t *tab; -} CSState; - -#define MODE2 (1 << 6) -#define MCE (1 << 6) -#define PMCE (1 << 4) -#define CMCE (1 << 5) -#define TE (1 << 6) -#define PEN (1 << 0) -#define INT (1 << 0) -#define IEN (1 << 1) -#define PPIO (1 << 6) -#define PI (1 << 4) -#define CI (1 << 5) -#define TI (1 << 6) - -enum { - Index_Address, - Index_Data, - Status, - PIO_Data -}; - -enum { - Left_ADC_Input_Control, - Right_ADC_Input_Control, - Left_AUX1_Input_Control, - Right_AUX1_Input_Control, - Left_AUX2_Input_Control, - Right_AUX2_Input_Control, - Left_DAC_Output_Control, - Right_DAC_Output_Control, - FS_And_Playback_Data_Format, - Interface_Configuration, - Pin_Control, - Error_Status_And_Initialization, - MODE_And_ID, - Loopback_Control, - Playback_Upper_Base_Count, - Playback_Lower_Base_Count, - Alternate_Feature_Enable_I, - Alternate_Feature_Enable_II, - Left_Line_Input_Control, - Right_Line_Input_Control, - Timer_Low_Base, - Timer_High_Base, - RESERVED, - Alternate_Feature_Enable_III, - Alternate_Feature_Status, - Version_Chip_ID, - Mono_Input_And_Output_Control, - RESERVED_2, - Capture_Data_Format, - RESERVED_3, - Capture_Upper_Base_Count, - Capture_Lower_Base_Count -}; - -static int freqs[2][8] = { - { 8000, 16000, 27420, 32000, -1, -1, 48000, 9000 }, - { 5510, 11025, 18900, 22050, 37800, 44100, 33075, 6620 } -}; - -/* Tables courtesy http://hazelware.luggle.com/tutorials/mulawcompression.html */ -static int16_t MuLawDecompressTable[256] = -{ - -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, - -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, - -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, - -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316, - -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, - -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, - -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, - -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, - -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, - -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, - -876, -844, -812, -780, -748, -716, -684, -652, - -620, -588, -556, -524, -492, -460, -428, -396, - -372, -356, -340, -324, -308, -292, -276, -260, - -244, -228, -212, -196, -180, -164, -148, -132, - -120, -112, -104, -96, -88, -80, -72, -64, - -56, -48, -40, -32, -24, -16, -8, 0, - 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, - 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, - 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, - 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, - 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, - 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, - 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, - 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, - 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, - 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, - 876, 844, 812, 780, 748, 716, 684, 652, - 620, 588, 556, 524, 492, 460, 428, 396, - 372, 356, 340, 324, 308, 292, 276, 260, - 244, 228, 212, 196, 180, 164, 148, 132, - 120, 112, 104, 96, 88, 80, 72, 64, - 56, 48, 40, 32, 24, 16, 8, 0 -}; - -static int16_t ALawDecompressTable[256] = -{ - -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, - -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, - -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, - -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, - -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944, - -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136, - -11008,-10496,-12032,-11520,-8960, -8448, -9984, -9472, - -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568, - -344, -328, -376, -360, -280, -264, -312, -296, - -472, -456, -504, -488, -408, -392, -440, -424, - -88, -72, -120, -104, -24, -8, -56, -40, - -216, -200, -248, -232, -152, -136, -184, -168, - -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, - -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, - -688, -656, -752, -720, -560, -528, -624, -592, - -944, -912, -1008, -976, -816, -784, -880, -848, - 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, - 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, - 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, - 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, - 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, - 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, - 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, - 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, - 344, 328, 376, 360, 280, 264, 312, 296, - 472, 456, 504, 488, 408, 392, 440, 424, - 88, 72, 120, 104, 24, 8, 56, 40, - 216, 200, 248, 232, 152, 136, 184, 168, - 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, - 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, - 688, 656, 752, 720, 560, 528, 624, 592, - 944, 912, 1008, 976, 816, 784, 880, 848 -}; - -static void cs4231a_reset (DeviceState *dev) -{ - CSState *s = CS4231A (dev); - - s->regs[Index_Address] = 0x40; - s->regs[Index_Data] = 0x00; - s->regs[Status] = 0x00; - s->regs[PIO_Data] = 0x00; - - s->dregs[Left_ADC_Input_Control] = 0x00; - s->dregs[Right_ADC_Input_Control] = 0x00; - s->dregs[Left_AUX1_Input_Control] = 0x88; - s->dregs[Right_AUX1_Input_Control] = 0x88; - s->dregs[Left_AUX2_Input_Control] = 0x88; - s->dregs[Right_AUX2_Input_Control] = 0x88; - s->dregs[Left_DAC_Output_Control] = 0x80; - s->dregs[Right_DAC_Output_Control] = 0x80; - s->dregs[FS_And_Playback_Data_Format] = 0x00; - s->dregs[Interface_Configuration] = 0x08; - s->dregs[Pin_Control] = 0x00; - s->dregs[Error_Status_And_Initialization] = 0x00; - s->dregs[MODE_And_ID] = 0x8a; - s->dregs[Loopback_Control] = 0x00; - s->dregs[Playback_Upper_Base_Count] = 0x00; - s->dregs[Playback_Lower_Base_Count] = 0x00; - s->dregs[Alternate_Feature_Enable_I] = 0x00; - s->dregs[Alternate_Feature_Enable_II] = 0x00; - s->dregs[Left_Line_Input_Control] = 0x88; - s->dregs[Right_Line_Input_Control] = 0x88; - s->dregs[Timer_Low_Base] = 0x00; - s->dregs[Timer_High_Base] = 0x00; - s->dregs[RESERVED] = 0x00; - s->dregs[Alternate_Feature_Enable_III] = 0x00; - s->dregs[Alternate_Feature_Status] = 0x00; - s->dregs[Version_Chip_ID] = 0xa0; - s->dregs[Mono_Input_And_Output_Control] = 0xa0; - s->dregs[RESERVED_2] = 0x00; - s->dregs[Capture_Data_Format] = 0x00; - s->dregs[RESERVED_3] = 0x00; - s->dregs[Capture_Upper_Base_Count] = 0x00; - s->dregs[Capture_Lower_Base_Count] = 0x00; -} - -static void cs_audio_callback (void *opaque, int free) -{ - CSState *s = opaque; - s->audio_free = free; -} - -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) - val = (1 << 4) | (1 << 5); -#endif - - xtal = val & 1; - as.freq = freqs[xtal][(val >> 1) & 7]; - - if (as.freq == -1) { - lerr ("unsupported frequency (val=%#x)\n", val); - goto error; - } - - as.nchannels = (val & (1 << 4)) ? 2 : 1; - as.endianness = 0; - s->tab = NULL; - - switch ((val >> 5) & ((s->dregs[MODE_And_ID] & MODE2) ? 7 : 3)) { - case 0: - as.fmt = AUD_FMT_U8; - s->shift = as.nchannels == 2; - break; - - case 1: - s->tab = MuLawDecompressTable; - goto x_law; - case 3: - s->tab = ALawDecompressTable; - x_law: - as.fmt = AUD_FMT_S16; - as.endianness = AUDIO_HOST_ENDIANNESS; - s->shift = as.nchannels == 2; - break; - - case 6: - as.endianness = 1; - case 2: - as.fmt = AUD_FMT_S16; - s->shift = as.nchannels; - break; - - case 7: - case 4: - lerr ("attempt to use reserved format value (%#x)\n", val); - goto error; - - case 5: - lerr ("ADPCM 4 bit IMA compatible format is not supported\n"); - goto error; - } - - s->voice = AUD_open_out ( - &s->card, - s->voice, - "cs4231a", - s, - cs_audio_callback, - &as - ); - - if (s->dregs[Interface_Configuration] & PEN) { - if (!s->dma_running) { - k->hold_DREQ(s->isa_dma, s->dma); - AUD_set_active_out (s->voice, 1); - s->transferred = 0; - } - s->dma_running = 1; - } - else { - if (s->dma_running) { - k->release_DREQ(s->isa_dma, s->dma); - AUD_set_active_out (s->voice, 0); - } - s->dma_running = 0; - } - return; - - error: - if (s->dma_running) { - k->release_DREQ(s->isa_dma, s->dma); - AUD_set_active_out (s->voice, 0); - } -} - -static uint64_t cs_read (void *opaque, hwaddr addr, unsigned size) -{ - CSState *s = opaque; - uint32_t saddr, iaddr, ret; - - saddr = addr; - iaddr = ~0U; - - switch (saddr) { - case Index_Address: - ret = s->regs[saddr] & ~0x80; - break; - - case Index_Data: - if (!(s->dregs[MODE_And_ID] & MODE2)) - iaddr = s->regs[Index_Address] & 0x0f; - else - iaddr = s->regs[Index_Address] & 0x1f; - - ret = s->dregs[iaddr]; - if (iaddr == Error_Status_And_Initialization) { - /* keep SEAL happy */ - if (s->aci_counter) { - ret |= 1 << 5; - s->aci_counter -= 1; - } - } - break; - - default: - ret = s->regs[saddr]; - break; - } - dolog ("read %d:%d -> %d\n", saddr, iaddr, ret); - return ret; -} - -static void cs_write (void *opaque, hwaddr addr, - uint64_t val64, unsigned size) -{ - CSState *s = opaque; - uint32_t saddr, iaddr, val; - - saddr = addr; - val = val64; - - switch (saddr) { - case Index_Address: - if (!(s->regs[Index_Address] & MCE) && (val & MCE) - && (s->dregs[Interface_Configuration] & (3 << 3))) - s->aci_counter = conf.aci_counter; - - s->regs[Index_Address] = val & ~(1 << 7); - break; - - case Index_Data: - if (!(s->dregs[MODE_And_ID] & MODE2)) - iaddr = s->regs[Index_Address] & 0x0f; - else - iaddr = s->regs[Index_Address] & 0x1f; - - switch (iaddr) { - case RESERVED: - case RESERVED_2: - case RESERVED_3: - lwarn ("attempt to write %#x to reserved indirect register %d\n", - val, iaddr); - break; - - case FS_And_Playback_Data_Format: - if (s->regs[Index_Address] & MCE) { - cs_reset_voices (s, val); - } - else { - if (s->dregs[Alternate_Feature_Status] & PMCE) { - val = (val & ~0x0f) | (s->dregs[iaddr] & 0x0f); - cs_reset_voices (s, val); - } - else { - lwarn ("[P]MCE(%#x, %#x) is not set, val=%#x\n", - s->regs[Index_Address], - s->dregs[Alternate_Feature_Status], - val); - break; - } - } - s->dregs[iaddr] = val; - break; - - case Interface_Configuration: - val &= ~(1 << 5); /* D5 is reserved */ - s->dregs[iaddr] = val; - if (val & PPIO) { - lwarn ("PIO is not supported (%#x)\n", val); - break; - } - if (val & PEN) { - if (!s->dma_running) { - cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]); - } - } - else { - if (s->dma_running) { - 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; - } - } - break; - - case Error_Status_And_Initialization: - lwarn ("attempt to write to read only register %d\n", iaddr); - break; - - case MODE_And_ID: - dolog ("val=%#x\n", val); - if (val & MODE2) - s->dregs[iaddr] |= MODE2; - else - s->dregs[iaddr] &= ~MODE2; - break; - - case Alternate_Feature_Enable_I: - if (val & TE) - lerr ("timer is not yet supported\n"); - s->dregs[iaddr] = val; - break; - - case Alternate_Feature_Status: - if ((s->dregs[iaddr] & PI) && !(val & PI)) { - /* XXX: TI CI */ - qemu_irq_lower (s->pic); - s->regs[Status] &= ~INT; - } - s->dregs[iaddr] = val; - break; - - case Version_Chip_ID: - lwarn ("write to Version_Chip_ID register %#x\n", val); - s->dregs[iaddr] = val; - break; - - default: - s->dregs[iaddr] = val; - break; - } - dolog ("written value %#x to indirect register %d\n", val, iaddr); - break; - - case Status: - if (s->regs[Status] & INT) { - qemu_irq_lower (s->pic); - } - s->regs[Status] &= ~INT; - s->dregs[Alternate_Feature_Status] &= ~(PI | CI | TI); - break; - - case PIO_Data: - lwarn ("attempt to write value %#x to PIO register\n", val); - break; - } -} - -static int cs_write_audio (CSState *s, int nchan, int dma_pos, - int dma_len, int len) -{ - int temp, net; - uint8_t tmpbuf[4096]; - IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); - - temp = len; - net = 0; - - while (temp) { - int left = dma_len - dma_pos; - int copied; - size_t to_copy; - - to_copy = audio_MIN (temp, left); - if (to_copy > sizeof (tmpbuf)) { - to_copy = sizeof (tmpbuf); - } - - copied = k->read_memory(s->isa_dma, nchan, tmpbuf, dma_pos, to_copy); - if (s->tab) { - int i; - int16_t linbuf[4096]; - - for (i = 0; i < copied; ++i) - linbuf[i] = s->tab[tmpbuf[i]]; - copied = AUD_write (s->voice, linbuf, copied << 1); - copied >>= 1; - } - else { - copied = AUD_write (s->voice, tmpbuf, copied); - } - - temp -= copied; - dma_pos = (dma_pos + copied) % dma_len; - net += copied; - - if (!copied) { - break; - } - } - - return net; -} - -static int cs_dma_read (void *opaque, int nchan, int dma_pos, int dma_len) -{ - CSState *s = opaque; - int copy, written; - int till = -1; - - copy = s->voice ? (s->audio_free >> (s->tab != NULL)) : dma_len; - - if (s->dregs[Pin_Control] & IEN) { - till = (s->dregs[Playback_Lower_Base_Count] - | (s->dregs[Playback_Upper_Base_Count] << 8)) << s->shift; - till -= s->transferred; - copy = audio_MIN (till, copy); - } - - if ((copy <= 0) || (dma_len <= 0)) { - return dma_pos; - } - - written = cs_write_audio (s, nchan, dma_pos, dma_len, copy); - - dma_pos = (dma_pos + written) % dma_len; - s->audio_free -= (written << (s->tab != NULL)); - - if (written == till) { - s->regs[Status] |= INT; - s->dregs[Alternate_Feature_Status] |= PI; - s->transferred = 0; - qemu_irq_raise (s->pic); - } - else { - s->transferred += written; - } - - return dma_pos; -} - -static int cs4231a_pre_load (void *opaque) -{ - CSState *s = opaque; - - if (s->dma_running) { - 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; - return 0; -} - -static int cs4231a_post_load (void *opaque, int version_id) -{ - CSState *s = opaque; - - if (s->dma_running && (s->dregs[Interface_Configuration] & PEN)) { - s->dma_running = 0; - cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]); - } - return 0; -} - -static const VMStateDescription vmstate_cs4231a = { - .name = "cs4231a", - .version_id = 1, - .minimum_version_id = 1, - .pre_load = cs4231a_pre_load, - .post_load = cs4231a_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY (regs, CSState, CS_REGS), - VMSTATE_BUFFER (dregs, CSState), - VMSTATE_INT32 (dma_running, CSState), - VMSTATE_INT32 (audio_free, CSState), - VMSTATE_INT32 (transferred, CSState), - VMSTATE_INT32 (aci_counter, CSState), - VMSTATE_END_OF_LIST () - } -}; - -static const MemoryRegionOps cs_ioport_ops = { - .read = cs_read, - .write = cs_write, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - } -}; - -static void cs4231a_initfn (Object *obj) -{ - CSState *s = CS4231A (obj); - - memory_region_init_io (&s->ioports, OBJECT(s), &cs_ioport_ops, s, - "cs4231a", 4); -} - -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); - - AUD_register_card ("cs4231a", &s->card); -} - -static int cs4231a_init (ISABus *bus) -{ - isa_create_simple (bus, TYPE_CS4231A); - return 0; -} - -static Property cs4231a_properties[] = { - DEFINE_PROP_UINT32 ("iobase", CSState, port, 0x534), - DEFINE_PROP_UINT32 ("irq", CSState, irq, 9), - DEFINE_PROP_UINT32 ("dma", CSState, dma, 3), - DEFINE_PROP_END_OF_LIST (), -}; - -static void cs4231a_class_initfn (ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS (klass); - - dc->realize = cs4231a_realizefn; - dc->reset = cs4231a_reset; - set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - dc->desc = "Crystal Semiconductor CS4231A"; - dc->vmsd = &vmstate_cs4231a; - dc->props = cs4231a_properties; -} - -static const TypeInfo cs4231a_info = { - .name = TYPE_CS4231A, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof (CSState), - .instance_init = cs4231a_initfn, - .class_init = cs4231a_class_initfn, -}; - -static void cs4231a_register_types (void) -{ - type_register_static (&cs4231a_info); - isa_register_soundhw("cs4231a", "CS4231A", cs4231a_init); -} - -type_init (cs4231a_register_types) diff --git a/qemu/hw/audio/es1370.c b/qemu/hw/audio/es1370.c deleted file mode 100644 index 8449b5f43..000000000 --- a/qemu/hw/audio/es1370.c +++ /dev/null @@ -1,1080 +0,0 @@ -/* - * QEMU ES1370 emulation - * - * Copyright (c) 2005 Vassili Karpov (malc) - * - * 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. - */ - -/* #define DEBUG_ES1370 */ -/* #define VERBOSE_ES1370 */ -#define SILENT_ES1370 - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/audio/audio.h" -#include "audio/audio.h" -#include "hw/pci/pci.h" -#include "sysemu/dma.h" - -/* Missing stuff: - SCTRL_P[12](END|ST)INC - SCTRL_P1SCTRLD - SCTRL_P2DACSEN - CTRL_DAC_SYNC - MIDI - non looped mode - surely more -*/ - -/* - Following macros and samplerate array were copied verbatim from - Linux kernel 2.4.30: drivers/sound/es1370.c - - Copyright (C) 1998-2001, 2003 Thomas Sailer (t.sailer@alumni.ethz.ch) -*/ - -/* Start blatant GPL violation */ - -#define ES1370_REG_CONTROL 0x00 -#define ES1370_REG_STATUS 0x04 -#define ES1370_REG_UART_DATA 0x08 -#define ES1370_REG_UART_STATUS 0x09 -#define ES1370_REG_UART_CONTROL 0x09 -#define ES1370_REG_UART_TEST 0x0a -#define ES1370_REG_MEMPAGE 0x0c -#define ES1370_REG_CODEC 0x10 -#define ES1370_REG_SERIAL_CONTROL 0x20 -#define ES1370_REG_DAC1_SCOUNT 0x24 -#define ES1370_REG_DAC2_SCOUNT 0x28 -#define ES1370_REG_ADC_SCOUNT 0x2c - -#define ES1370_REG_DAC1_FRAMEADR 0xc30 -#define ES1370_REG_DAC1_FRAMECNT 0xc34 -#define ES1370_REG_DAC2_FRAMEADR 0xc38 -#define ES1370_REG_DAC2_FRAMECNT 0xc3c -#define ES1370_REG_ADC_FRAMEADR 0xd30 -#define ES1370_REG_ADC_FRAMECNT 0xd34 -#define ES1370_REG_PHANTOM_FRAMEADR 0xd38 -#define ES1370_REG_PHANTOM_FRAMECNT 0xd3c - -static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 }; - -#define DAC2_SRTODIV(x) (((1411200+(x)/2)/(x))-2) -#define DAC2_DIVTOSR(x) (1411200/((x)+2)) - -#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */ -#define CTRL_XCTL1 0x40000000 /* electret mic bias */ -#define CTRL_OPEN 0x20000000 /* no function, can be read and written */ -#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */ -#define CTRL_SH_PCLKDIV 16 -#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */ -#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */ -#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025, 2=22050, 3=44100 */ -#define CTRL_SH_WTSRSEL 12 -#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */ -#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */ -#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 = MPEG */ -#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */ -#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */ -#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */ -#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */ -#define CTRL_ADC_EN 0x00000010 /* enable ADC */ -#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */ -#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably at address 0x200) */ -#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */ -#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */ - -#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */ -#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in progress */ -#define STAT_CBUSY 0x00000200 /* 1 = codec busy */ -#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */ -#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */ -#define STAT_SH_VC 5 -#define STAT_MCCB 0x00000010 /* CCB int pending */ -#define STAT_UART 0x00000008 /* UART int pending */ -#define STAT_DAC1 0x00000004 /* DAC1 int pending */ -#define STAT_DAC2 0x00000002 /* DAC2 int pending */ -#define STAT_ADC 0x00000001 /* ADC int pending */ - -#define USTAT_RXINT 0x80 /* UART rx int pending */ -#define USTAT_TXINT 0x04 /* UART tx int pending */ -#define USTAT_TXRDY 0x02 /* UART tx ready */ -#define USTAT_RXRDY 0x01 /* UART rx ready */ - -#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */ -#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */ -#define UCTRL_ENA_TXINT 0x20 /* enable TX int */ -#define UCTRL_CNTRL 0x03 /* control field */ -#define UCTRL_CNTRL_SWR 0x03 /* software reset command */ - -#define SCTRL_P2ENDINC 0x00380000 /* */ -#define SCTRL_SH_P2ENDINC 19 -#define SCTRL_P2STINC 0x00070000 /* */ -#define SCTRL_SH_P2STINC 16 -#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */ -#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */ -#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */ -#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */ -#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */ -#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */ -#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */ -#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */ -#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */ -#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */ -#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */ -#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */ -#define SCTRL_R1FMT 0x00000030 /* format mask */ -#define SCTRL_SH_R1FMT 4 -#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */ -#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */ -#define SCTRL_P2FMT 0x0000000c /* format mask */ -#define SCTRL_SH_P2FMT 2 -#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */ -#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */ -#define SCTRL_P1FMT 0x00000003 /* format mask */ -#define SCTRL_SH_P1FMT 0 - -/* End blatant GPL violation */ - -#define NB_CHANNELS 3 -#define DAC1_CHANNEL 0 -#define DAC2_CHANNEL 1 -#define ADC_CHANNEL 2 - -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); - -#ifdef DEBUG_ES1370 - -#define ldebug(...) AUD_log ("es1370", __VA_ARGS__) - -static void print_ctl (uint32_t val) -{ - char buf[1024]; - - buf[0] = '\0'; -#define a(n) if (val & CTRL_##n) strcat (buf, " "#n) - a (ADC_STOP); - a (XCTL1); - a (OPEN); - a (MSFMTSEL); - a (M_SBB); - a (DAC_SYNC); - a (CCB_INTRM); - a (M_CB); - a (XCTL0); - a (BREQ); - a (DAC1_EN); - a (DAC2_EN); - a (ADC_EN); - a (UART_EN); - a (JYSTK_EN); - a (CDC_EN); - a (SERR_DIS); -#undef a - AUD_log ("es1370", "ctl - PCLKDIV %d(DAC2 freq %d), freq %d,%s\n", - (val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV, - DAC2_DIVTOSR ((val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), - dac1_samplerate[(val & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], - buf); -} - -static void print_sctl (uint32_t val) -{ - static const char *fmt_names[] = {"8M", "8S", "16M", "16S"}; - char buf[1024]; - - buf[0] = '\0'; - -#define a(n) if (val & SCTRL_##n) strcat (buf, " "#n) -#define b(n) if (!(val & SCTRL_##n)) strcat (buf, " "#n) - b (R1LOOPSEL); - b (P2LOOPSEL); - b (P1LOOPSEL); - a (P2PAUSE); - a (P1PAUSE); - a (R1INTEN); - a (P2INTEN); - a (P1INTEN); - a (P1SCTRLD); - a (P2DACSEN); - if (buf[0]) { - strcat (buf, "\n "); - } - else { - buf[0] = ' '; - buf[1] = '\0'; - } -#undef b -#undef a - AUD_log ("es1370", - "%s" - "p2_end_inc %d, p2_st_inc %d, r1_fmt %s, p2_fmt %s, p1_fmt %s\n", - buf, - (val & SCTRL_P2ENDINC) >> SCTRL_SH_P2ENDINC, - (val & SCTRL_P2STINC) >> SCTRL_SH_P2STINC, - fmt_names [(val >> SCTRL_SH_R1FMT) & 3], - fmt_names [(val >> SCTRL_SH_P2FMT) & 3], - fmt_names [(val >> SCTRL_SH_P1FMT) & 3] - ); -} -#else -#define ldebug(...) -#define print_ctl(...) -#define print_sctl(...) -#endif - -#ifdef VERBOSE_ES1370 -#define dolog(...) AUD_log ("es1370", __VA_ARGS__) -#else -#define dolog(...) -#endif - -#ifndef SILENT_ES1370 -#define lwarn(...) AUD_log ("es1370: warning", __VA_ARGS__) -#else -#define lwarn(...) -#endif - -struct chan { - uint32_t shift; - uint32_t leftover; - uint32_t scount; - uint32_t frame_addr; - uint32_t frame_cnt; -}; - -typedef struct ES1370State { - PCIDevice dev; - QEMUSoundCard card; - MemoryRegion io; - struct chan chan[NB_CHANNELS]; - SWVoiceOut *dac_voice[2]; - SWVoiceIn *adc_voice; - - uint32_t ctl; - uint32_t status; - uint32_t mempage; - uint32_t codec; - uint32_t sctl; -} ES1370State; - -struct chan_bits { - uint32_t ctl_en; - uint32_t stat_int; - uint32_t sctl_pause; - uint32_t sctl_inten; - uint32_t sctl_fmt; - uint32_t sctl_sh_fmt; - uint32_t sctl_loopsel; - void (*calc_freq) (ES1370State *s, uint32_t ctl, - 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, - uint32_t *old_freq, - uint32_t *new_freq); - -static const struct chan_bits es1370_chan_bits[] = { - {CTRL_DAC1_EN, STAT_DAC1, SCTRL_P1PAUSE, SCTRL_P1INTEN, - SCTRL_P1FMT, SCTRL_SH_P1FMT, SCTRL_P1LOOPSEL, - es1370_dac1_calc_freq}, - - {CTRL_DAC2_EN, STAT_DAC2, SCTRL_P2PAUSE, SCTRL_P2INTEN, - SCTRL_P2FMT, SCTRL_SH_P2FMT, SCTRL_P2LOOPSEL, - es1370_dac2_and_adc_calc_freq}, - - {CTRL_ADC_EN, STAT_ADC, 0, SCTRL_R1INTEN, - SCTRL_R1FMT, SCTRL_SH_R1FMT, SCTRL_R1LOOPSEL, - es1370_dac2_and_adc_calc_freq} -}; - -static void es1370_update_status (ES1370State *s, uint32_t new_status) -{ - uint32_t level = new_status & (STAT_DAC1 | STAT_DAC2 | STAT_ADC); - - if (level) { - s->status = new_status | STAT_INTR; - } - else { - s->status = new_status & ~STAT_INTR; - } - pci_set_irq(&s->dev, !!level); -} - -static void es1370_reset (ES1370State *s) -{ - size_t i; - - s->ctl = 1; - s->status = 0x60; - s->mempage = 0; - s->codec = 0; - s->sctl = 0; - - for (i = 0; i < NB_CHANNELS; ++i) { - struct chan *d = &s->chan[i]; - d->scount = 0; - d->leftover = 0; - if (i == ADC_CHANNEL) { - AUD_close_in (&s->card, s->adc_voice); - s->adc_voice = NULL; - } - else { - AUD_close_out (&s->card, s->dac_voice[i]); - s->dac_voice[i] = NULL; - } - } - pci_irq_deassert(&s->dev); -} - -static void es1370_maybe_lower_irq (ES1370State *s, uint32_t sctl) -{ - uint32_t new_status = s->status; - - if (!(sctl & SCTRL_P1INTEN) && (s->sctl & SCTRL_P1INTEN)) { - new_status &= ~STAT_DAC1; - } - - if (!(sctl & SCTRL_P2INTEN) && (s->sctl & SCTRL_P2INTEN)) { - new_status &= ~STAT_DAC2; - } - - if (!(sctl & SCTRL_R1INTEN) && (s->sctl & SCTRL_R1INTEN)) { - new_status &= ~STAT_ADC; - } - - if (new_status != s->status) { - es1370_update_status (s, new_status); - } -} - -static void es1370_dac1_calc_freq (ES1370State *s, uint32_t ctl, - uint32_t *old_freq, uint32_t *new_freq) - -{ - *old_freq = dac1_samplerate[(s->ctl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; - *new_freq = dac1_samplerate[(ctl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; -} - -static void es1370_dac2_and_adc_calc_freq (ES1370State *s, uint32_t ctl, - uint32_t *old_freq, - uint32_t *new_freq) - -{ - uint32_t old_pclkdiv, new_pclkdiv; - - new_pclkdiv = (ctl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV; - old_pclkdiv = (s->ctl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV; - *new_freq = DAC2_DIVTOSR (new_pclkdiv); - *old_freq = DAC2_DIVTOSR (old_pclkdiv); -} - -static void es1370_update_voices (ES1370State *s, uint32_t ctl, uint32_t sctl) -{ - size_t i; - uint32_t old_freq, new_freq, old_fmt, new_fmt; - - for (i = 0; i < NB_CHANNELS; ++i) { - struct chan *d = &s->chan[i]; - const struct chan_bits *b = &es1370_chan_bits[i]; - - new_fmt = (sctl & b->sctl_fmt) >> b->sctl_sh_fmt; - old_fmt = (s->sctl & b->sctl_fmt) >> b->sctl_sh_fmt; - - b->calc_freq (s, ctl, &old_freq, &new_freq); - - if ((old_fmt != new_fmt) || (old_freq != new_freq)) { - d->shift = (new_fmt & 1) + (new_fmt >> 1); - ldebug ("channel %zu, freq = %d, nchannels %d, fmt %d, shift %d\n", - i, - new_freq, - 1 << (new_fmt & 1), - (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8, - d->shift); - if (new_freq) { - struct audsettings as; - - as.freq = new_freq; - as.nchannels = 1 << (new_fmt & 1); - as.fmt = (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8; - as.endianness = 0; - - if (i == ADC_CHANNEL) { - s->adc_voice = - AUD_open_in ( - &s->card, - s->adc_voice, - "es1370.adc", - s, - es1370_adc_callback, - &as - ); - } - else { - s->dac_voice[i] = - AUD_open_out ( - &s->card, - s->dac_voice[i], - i ? "es1370.dac2" : "es1370.dac1", - s, - i ? es1370_dac2_callback : es1370_dac1_callback, - &as - ); - } - } - } - - if (((ctl ^ s->ctl) & b->ctl_en) - || ((sctl ^ s->sctl) & b->sctl_pause)) { - int on = (ctl & b->ctl_en) && !(sctl & b->sctl_pause); - - if (i == ADC_CHANNEL) { - AUD_set_active_in (s->adc_voice, on); - } - else { - AUD_set_active_out (s->dac_voice[i], on); - } - } - } - - s->ctl = ctl; - s->sctl = sctl; -} - -static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr) -{ - addr &= 0xff; - if (addr >= 0x30 && addr <= 0x3f) - addr |= s->mempage << 8; - return addr; -} - -static void es1370_writeb(void *opaque, uint32_t addr, uint32_t val) -{ - ES1370State *s = opaque; - uint32_t shift, mask; - - addr = es1370_fixup (s, addr); - - switch (addr) { - case ES1370_REG_CONTROL: - case ES1370_REG_CONTROL + 1: - case ES1370_REG_CONTROL + 2: - case ES1370_REG_CONTROL + 3: - shift = (addr - ES1370_REG_CONTROL) << 3; - mask = 0xff << shift; - val = (s->ctl & ~mask) | ((val & 0xff) << shift); - es1370_update_voices (s, val, s->sctl); - print_ctl (val); - break; - case ES1370_REG_MEMPAGE: - s->mempage = val; - break; - case ES1370_REG_SERIAL_CONTROL: - case ES1370_REG_SERIAL_CONTROL + 1: - case ES1370_REG_SERIAL_CONTROL + 2: - case ES1370_REG_SERIAL_CONTROL + 3: - shift = (addr - ES1370_REG_SERIAL_CONTROL) << 3; - mask = 0xff << shift; - val = (s->sctl & ~mask) | ((val & 0xff) << shift); - es1370_maybe_lower_irq (s, val); - es1370_update_voices (s, s->ctl, val); - print_sctl (val); - break; - default: - lwarn ("writeb %#x <- %#x\n", addr, val); - break; - } -} - -static void es1370_writew(void *opaque, uint32_t addr, uint32_t val) -{ - ES1370State *s = opaque; - addr = es1370_fixup (s, addr); - uint32_t shift, mask; - struct chan *d = &s->chan[0]; - - switch (addr) { - case ES1370_REG_CODEC: - dolog ("ignored codec write address %#x, data %#x\n", - (val >> 8) & 0xff, val & 0xff); - s->codec = val; - break; - - case ES1370_REG_CONTROL: - case ES1370_REG_CONTROL + 2: - shift = (addr != ES1370_REG_CONTROL) << 4; - mask = 0xffff << shift; - val = (s->ctl & ~mask) | ((val & 0xffff) << shift); - es1370_update_voices (s, val, s->sctl); - print_ctl (val); - break; - - case ES1370_REG_ADC_SCOUNT: - d++; - case ES1370_REG_DAC2_SCOUNT: - d++; - case ES1370_REG_DAC1_SCOUNT: - d->scount = (d->scount & ~0xffff) | (val & 0xffff); - break; - - default: - lwarn ("writew %#x <- %#x\n", addr, val); - break; - } -} - -static void es1370_writel(void *opaque, uint32_t addr, uint32_t val) -{ - ES1370State *s = opaque; - struct chan *d = &s->chan[0]; - - addr = es1370_fixup (s, addr); - - switch (addr) { - case ES1370_REG_CONTROL: - es1370_update_voices (s, val, s->sctl); - print_ctl (val); - break; - - case ES1370_REG_MEMPAGE: - s->mempage = val & 0xf; - break; - - case ES1370_REG_SERIAL_CONTROL: - es1370_maybe_lower_irq (s, val); - es1370_update_voices (s, s->ctl, val); - print_sctl (val); - break; - - case ES1370_REG_ADC_SCOUNT: - d++; - case ES1370_REG_DAC2_SCOUNT: - d++; - case ES1370_REG_DAC1_SCOUNT: - d->scount = (val & 0xffff) | (d->scount & ~0xffff); - ldebug ("chan %td CURR_SAMP_CT %d, SAMP_CT %d\n", - d - &s->chan[0], val >> 16, (val & 0xffff)); - break; - - case ES1370_REG_ADC_FRAMEADR: - d++; - case ES1370_REG_DAC2_FRAMEADR: - d++; - case ES1370_REG_DAC1_FRAMEADR: - d->frame_addr = val; - ldebug ("chan %td frame address %#x\n", d - &s->chan[0], val); - break; - - case ES1370_REG_PHANTOM_FRAMECNT: - lwarn ("writing to phantom frame count %#x\n", val); - break; - case ES1370_REG_PHANTOM_FRAMEADR: - lwarn ("writing to phantom frame address %#x\n", val); - break; - - case ES1370_REG_ADC_FRAMECNT: - d++; - case ES1370_REG_DAC2_FRAMECNT: - d++; - case ES1370_REG_DAC1_FRAMECNT: - d->frame_cnt = val; - d->leftover = 0; - ldebug ("chan %td frame count %d, buffer size %d\n", - d - &s->chan[0], val >> 16, val & 0xffff); - break; - - default: - lwarn ("writel %#x <- %#x\n", addr, val); - break; - } -} - -static uint32_t es1370_readb(void *opaque, uint32_t addr) -{ - ES1370State *s = opaque; - uint32_t val; - - addr = es1370_fixup (s, addr); - - switch (addr) { - case 0x1b: /* Legacy */ - lwarn ("Attempt to read from legacy register\n"); - val = 5; - break; - case ES1370_REG_MEMPAGE: - val = s->mempage; - break; - case ES1370_REG_CONTROL + 0: - case ES1370_REG_CONTROL + 1: - case ES1370_REG_CONTROL + 2: - case ES1370_REG_CONTROL + 3: - val = s->ctl >> ((addr - ES1370_REG_CONTROL) << 3); - break; - case ES1370_REG_STATUS + 0: - case ES1370_REG_STATUS + 1: - case ES1370_REG_STATUS + 2: - case ES1370_REG_STATUS + 3: - val = s->status >> ((addr - ES1370_REG_STATUS) << 3); - break; - default: - val = ~0; - lwarn ("readb %#x -> %#x\n", addr, val); - break; - } - return val; -} - -static uint32_t es1370_readw(void *opaque, uint32_t addr) -{ - ES1370State *s = opaque; - struct chan *d = &s->chan[0]; - uint32_t val; - - addr = es1370_fixup (s, addr); - - switch (addr) { - case ES1370_REG_ADC_SCOUNT + 2: - d++; - case ES1370_REG_DAC2_SCOUNT + 2: - d++; - case ES1370_REG_DAC1_SCOUNT + 2: - val = d->scount >> 16; - break; - - case ES1370_REG_ADC_FRAMECNT: - d++; - case ES1370_REG_DAC2_FRAMECNT: - d++; - case ES1370_REG_DAC1_FRAMECNT: - val = d->frame_cnt & 0xffff; - break; - - case ES1370_REG_ADC_FRAMECNT + 2: - d++; - case ES1370_REG_DAC2_FRAMECNT + 2: - d++; - case ES1370_REG_DAC1_FRAMECNT + 2: - val = d->frame_cnt >> 16; - break; - - default: - val = ~0; - lwarn ("readw %#x -> %#x\n", addr, val); - break; - } - - return val; -} - -static uint32_t es1370_readl(void *opaque, uint32_t addr) -{ - ES1370State *s = opaque; - uint32_t val; - struct chan *d = &s->chan[0]; - - addr = es1370_fixup (s, addr); - - switch (addr) { - case ES1370_REG_CONTROL: - val = s->ctl; - break; - case ES1370_REG_STATUS: - val = s->status; - break; - case ES1370_REG_MEMPAGE: - val = s->mempage; - break; - case ES1370_REG_CODEC: - val = s->codec; - break; - case ES1370_REG_SERIAL_CONTROL: - val = s->sctl; - break; - - case ES1370_REG_ADC_SCOUNT: - d++; - case ES1370_REG_DAC2_SCOUNT: - d++; - case ES1370_REG_DAC1_SCOUNT: - val = d->scount; -#ifdef DEBUG_ES1370 - { - uint32_t curr_count = d->scount >> 16; - uint32_t count = d->scount & 0xffff; - - curr_count <<= d->shift; - count <<= d->shift; - dolog ("read scount curr %d, total %d\n", curr_count, count); - } -#endif - break; - - case ES1370_REG_ADC_FRAMECNT: - d++; - case ES1370_REG_DAC2_FRAMECNT: - d++; - case ES1370_REG_DAC1_FRAMECNT: - val = d->frame_cnt; -#ifdef DEBUG_ES1370 - { - uint32_t size = ((d->frame_cnt & 0xffff) + 1) << 2; - uint32_t curr = ((d->frame_cnt >> 16) + 1) << 2; - if (curr > size) { - dolog ("read framecnt curr %d, size %d %d\n", curr, size, - curr > size); - } - } -#endif - break; - - case ES1370_REG_ADC_FRAMEADR: - d++; - case ES1370_REG_DAC2_FRAMEADR: - d++; - case ES1370_REG_DAC1_FRAMEADR: - val = d->frame_addr; - break; - - case ES1370_REG_PHANTOM_FRAMECNT: - val = ~0U; - lwarn ("reading from phantom frame count\n"); - break; - case ES1370_REG_PHANTOM_FRAMEADR: - val = ~0U; - lwarn ("reading from phantom frame address\n"); - break; - - default: - val = ~0U; - lwarn ("readl %#x -> %#x\n", addr, val); - break; - } - return val; -} - -static void es1370_transfer_audio (ES1370State *s, struct chan *d, int loop_sel, - int max, int *irq) -{ - uint8_t tmpbuf[4096]; - uint32_t addr = d->frame_addr; - int sc = d->scount & 0xffff; - int csc = d->scount >> 16; - int csc_bytes = (csc + 1) << d->shift; - int cnt = d->frame_cnt >> 16; - int size = d->frame_cnt & 0xffff; - int left = ((size - cnt + 1) << 2) + d->leftover; - int transferred = 0; - int temp = audio_MIN (max, audio_MIN (left, csc_bytes)); - int index = d - &s->chan[0]; - - addr += (cnt << 2) + d->leftover; - - if (index == ADC_CHANNEL) { - while (temp) { - int acquired, to_copy; - - to_copy = audio_MIN ((size_t) temp, sizeof (tmpbuf)); - acquired = AUD_read (s->adc_voice, tmpbuf, to_copy); - if (!acquired) - break; - - pci_dma_write (&s->dev, addr, tmpbuf, acquired); - - temp -= acquired; - addr += acquired; - transferred += acquired; - } - } - else { - SWVoiceOut *voice = s->dac_voice[index]; - - while (temp) { - int copied, to_copy; - - to_copy = audio_MIN ((size_t) temp, sizeof (tmpbuf)); - pci_dma_read (&s->dev, addr, tmpbuf, to_copy); - copied = AUD_write (voice, tmpbuf, to_copy); - if (!copied) - break; - temp -= copied; - addr += copied; - transferred += copied; - } - } - - if (csc_bytes == transferred) { - *irq = 1; - d->scount = sc | (sc << 16); - ldebug ("sc = %d, rate = %f\n", - (sc + 1) << d->shift, - (sc + 1) / (double) 44100); - } - else { - *irq = 0; - d->scount = sc | (((csc_bytes - transferred - 1) >> d->shift) << 16); - } - - cnt += (transferred + d->leftover) >> 2; - - if (s->sctl & loop_sel) { - /* Bah, how stupid is that having a 0 represent true value? - i just spent few hours on this shit */ - AUD_log ("es1370: warning", "non looping mode\n"); - } - else { - d->frame_cnt = size; - - if ((uint32_t) cnt <= d->frame_cnt) - d->frame_cnt |= cnt << 16; - } - - d->leftover = (transferred + d->leftover) & 3; -} - -static void es1370_run_channel (ES1370State *s, size_t chan, int free_or_avail) -{ - uint32_t new_status = s->status; - int max_bytes, irq; - struct chan *d = &s->chan[chan]; - const struct chan_bits *b = &es1370_chan_bits[chan]; - - if (!(s->ctl & b->ctl_en) || (s->sctl & b->sctl_pause)) { - return; - } - - max_bytes = free_or_avail; - max_bytes &= ~((1 << d->shift) - 1); - if (!max_bytes) { - return; - } - - es1370_transfer_audio (s, d, b->sctl_loopsel, max_bytes, &irq); - - if (irq) { - if (s->sctl & b->sctl_inten) { - new_status |= b->stat_int; - } - } - - if (new_status != s->status) { - es1370_update_status (s, new_status); - } -} - -static void es1370_dac1_callback (void *opaque, int free) -{ - ES1370State *s = opaque; - - es1370_run_channel (s, DAC1_CHANNEL, free); -} - -static void es1370_dac2_callback (void *opaque, int free) -{ - ES1370State *s = opaque; - - es1370_run_channel (s, DAC2_CHANNEL, free); -} - -static void es1370_adc_callback (void *opaque, int avail) -{ - ES1370State *s = opaque; - - es1370_run_channel (s, ADC_CHANNEL, avail); -} - -static uint64_t es1370_read(void *opaque, hwaddr addr, - unsigned size) -{ - switch (size) { - case 1: - return es1370_readb(opaque, addr); - case 2: - return es1370_readw(opaque, addr); - case 4: - return es1370_readl(opaque, addr); - default: - return -1; - } -} - -static void es1370_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - switch (size) { - case 1: - es1370_writeb(opaque, addr, val); - break; - case 2: - es1370_writew(opaque, addr, val); - break; - case 4: - es1370_writel(opaque, addr, val); - break; - } -} - -static const MemoryRegionOps es1370_io_ops = { - .read = es1370_read, - .write = es1370_write, - .impl = { - .min_access_size = 1, - .max_access_size = 4, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const VMStateDescription vmstate_es1370_channel = { - .name = "es1370_channel", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32 (shift, struct chan), - VMSTATE_UINT32 (leftover, struct chan), - VMSTATE_UINT32 (scount, struct chan), - VMSTATE_UINT32 (frame_addr, struct chan), - VMSTATE_UINT32 (frame_cnt, struct chan), - VMSTATE_END_OF_LIST () - } -}; - -static int es1370_post_load (void *opaque, int version_id) -{ - uint32_t ctl, sctl; - ES1370State *s = opaque; - size_t i; - - for (i = 0; i < NB_CHANNELS; ++i) { - if (i == ADC_CHANNEL) { - if (s->adc_voice) { - AUD_close_in (&s->card, s->adc_voice); - s->adc_voice = NULL; - } - } - else { - if (s->dac_voice[i]) { - AUD_close_out (&s->card, s->dac_voice[i]); - s->dac_voice[i] = NULL; - } - } - } - - ctl = s->ctl; - sctl = s->sctl; - s->ctl = 0; - s->sctl = 0; - es1370_update_voices (s, ctl, sctl); - return 0; -} - -static const VMStateDescription vmstate_es1370 = { - .name = "es1370", - .version_id = 2, - .minimum_version_id = 2, - .post_load = es1370_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE (dev, ES1370State), - VMSTATE_STRUCT_ARRAY (chan, ES1370State, NB_CHANNELS, 2, - vmstate_es1370_channel, struct chan), - VMSTATE_UINT32 (ctl, ES1370State), - VMSTATE_UINT32 (status, ES1370State), - VMSTATE_UINT32 (mempage, ES1370State), - VMSTATE_UINT32 (codec, ES1370State), - VMSTATE_UINT32 (sctl, ES1370State), - VMSTATE_END_OF_LIST () - } -}; - -static void es1370_on_reset (void *opaque) -{ - ES1370State *s = opaque; - es1370_reset (s); -} - -static void es1370_realize(PCIDevice *dev, Error **errp) -{ - ES1370State *s = ES1370(dev); - uint8_t *c = s->dev.config; - - c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_SLOW >> 8; - -#if 0 - c[PCI_CAPABILITY_LIST] = 0xdc; - c[PCI_INTERRUPT_LINE] = 10; - c[0xdc] = 0x00; -#endif - - c[PCI_INTERRUPT_PIN] = 1; - c[PCI_MIN_GNT] = 0x0c; - c[PCI_MAX_LAT] = 0x80; - - memory_region_init_io (&s->io, OBJECT(s), &es1370_io_ops, s, "es1370", 256); - pci_register_bar (&s->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io); - qemu_register_reset (es1370_on_reset, s); - - AUD_register_card ("es1370", &s->card); - es1370_reset (s); -} - -static int es1370_init (PCIBus *bus) -{ - pci_create_simple (bus, -1, TYPE_ES1370); - return 0; -} - -static void es1370_class_init (ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS (klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS (klass); - - k->realize = es1370_realize; - k->vendor_id = PCI_VENDOR_ID_ENSONIQ; - k->device_id = PCI_DEVICE_ID_ENSONIQ_ES1370; - k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO; - k->subsystem_vendor_id = 0x4942; - k->subsystem_id = 0x4c4c; - set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - dc->desc = "ENSONIQ AudioPCI ES1370"; - dc->vmsd = &vmstate_es1370; -} - -static const TypeInfo es1370_info = { - .name = TYPE_ES1370, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof (ES1370State), - .class_init = es1370_class_init, -}; - -static void es1370_register_types (void) -{ - type_register_static (&es1370_info); - pci_register_soundhw("es1370", "ENSONIQ AudioPCI ES1370", es1370_init); -} - -type_init (es1370_register_types) - diff --git a/qemu/hw/audio/fmopl.c b/qemu/hw/audio/fmopl.c deleted file mode 100644 index 731110fe8..000000000 --- a/qemu/hw/audio/fmopl.c +++ /dev/null @@ -1,1391 +0,0 @@ -/* -** -** File: fmopl.c -- software implementation of FM sound generator -** -** Copyright (C) 1999,2000 Tatsuyuki Satoh , MultiArcadeMachineEmurator development -** -** Version 0.37a -** -*/ - -/* - preliminary : - Problem : - note: -*/ - -/* This version of fmopl.c is a fork of the MAME one, relicensed under the LGPL. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#define HAS_YM3812 1 - -#include "qemu/osdep.h" -#include -//#include "driver.h" /* use M.A.M.E. */ -#include "fmopl.h" - -#ifndef PI -#define PI 3.14159265358979323846 -#endif - -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -#endif - -/* -------------------- for debug --------------------- */ -/* #define OPL_OUTPUT_LOG */ -#ifdef OPL_OUTPUT_LOG -static FILE *opl_dbg_fp = NULL; -static FM_OPL *opl_dbg_opl[16]; -static int opl_dbg_maxchip,opl_dbg_chip; -#endif - -/* -------------------- preliminary define section --------------------- */ -/* attack/decay rate time rate */ -#define OPL_ARRATE 141280 /* RATE 4 = 2826.24ms @ 3.6MHz */ -#define OPL_DRRATE 1956000 /* RATE 4 = 39280.64ms @ 3.6MHz */ - -#define DELTAT_MIXING_LEVEL (1) /* DELTA-T ADPCM MIXING LEVEL */ - -#define FREQ_BITS 24 /* frequency turn */ - -/* counter bits = 20 , octerve 7 */ -#define FREQ_RATE (1<<(FREQ_BITS-20)) -#define TL_BITS (FREQ_BITS+2) - -/* final output shift , limit minimum and maximum */ -#define OPL_OUTSB (TL_BITS+3-16) /* OPL output final shift 16bit */ -#define OPL_MAXOUT (0x7fff<=LOG_LEVEL ) logerror x -#define LOG(n,x) - -/* --------------------- subroutines --------------------- */ - -static inline int Limit( int val, int max, int min ) { - if ( val > max ) - val = max; - else if ( val < min ) - val = min; - - return val; -} - -/* status set and IRQ handling */ -static inline void OPL_STATUS_SET(FM_OPL *OPL,int flag) -{ - /* set status flag */ - OPL->status |= flag; - if(!(OPL->status & 0x80)) - { - if(OPL->status & OPL->statusmask) - { /* IRQ on */ - OPL->status |= 0x80; - /* callback user interrupt handler (IRQ is OFF to ON) */ - if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1); - } - } -} - -/* status reset and IRQ handling */ -static inline void OPL_STATUS_RESET(FM_OPL *OPL,int flag) -{ - /* reset status flag */ - OPL->status &=~flag; - if((OPL->status & 0x80)) - { - if (!(OPL->status & OPL->statusmask) ) - { - OPL->status &= 0x7f; - /* callback user interrupt handler (IRQ is ON to OFF) */ - if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0); - } - } -} - -/* IRQ mask set */ -static inline void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag) -{ - OPL->statusmask = flag; - /* IRQ handling check */ - OPL_STATUS_SET(OPL,0); - OPL_STATUS_RESET(OPL,0); -} - -/* ----- key on ----- */ -static inline void OPL_KEYON(OPL_SLOT *SLOT) -{ - /* sin wave restart */ - SLOT->Cnt = 0; - /* set attack */ - SLOT->evm = ENV_MOD_AR; - SLOT->evs = SLOT->evsa; - SLOT->evc = EG_AST; - SLOT->eve = EG_AED; -} -/* ----- key off ----- */ -static inline void OPL_KEYOFF(OPL_SLOT *SLOT) -{ - if( SLOT->evm > ENV_MOD_RR) - { - /* set envelope counter from envleope output */ - SLOT->evm = ENV_MOD_RR; - if( !(SLOT->evc&EG_DST) ) - //SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<evc = EG_DST; - SLOT->eve = EG_DED; - SLOT->evs = SLOT->evsr; - } -} - -/* ---------- calcrate Envelope Generator & Phase Generator ---------- */ -/* return : envelope output */ -static inline UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT ) -{ - /* calcrate envelope generator */ - if( (SLOT->evc+=SLOT->evs) >= SLOT->eve ) - { - switch( SLOT->evm ){ - case ENV_MOD_AR: /* ATTACK -> DECAY1 */ - /* next DR */ - SLOT->evm = ENV_MOD_DR; - SLOT->evc = EG_DST; - SLOT->eve = SLOT->SL; - SLOT->evs = SLOT->evsd; - break; - case ENV_MOD_DR: /* DECAY -> SL or RR */ - SLOT->evc = SLOT->SL; - SLOT->eve = EG_DED; - if(SLOT->eg_typ) - { - SLOT->evs = 0; - } - else - { - SLOT->evm = ENV_MOD_RR; - SLOT->evs = SLOT->evsr; - } - break; - case ENV_MOD_RR: /* RR -> OFF */ - SLOT->evc = EG_OFF; - SLOT->eve = EG_OFF+1; - SLOT->evs = 0; - break; - } - } - /* calcrate envelope */ - return SLOT->TLL+ENV_CURVE[SLOT->evc>>ENV_BITS]+(SLOT->ams ? ams : 0); -} - -/* set algorithm connection */ -static void set_algorithm( OPL_CH *CH) -{ - INT32 *carrier = &outd[0]; - CH->connect1 = CH->CON ? carrier : &feedback2; - CH->connect2 = carrier; -} - -/* ---------- frequency counter for operater update ---------- */ -static inline void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) -{ - int ksr; - - /* frequency step counter */ - SLOT->Incr = CH->fc * SLOT->mul; - ksr = CH->kcode >> SLOT->KSR; - - if( SLOT->ksr != ksr ) - { - SLOT->ksr = ksr; - /* attack , decay rate recalcration */ - SLOT->evsa = SLOT->AR[ksr]; - SLOT->evsd = SLOT->DR[ksr]; - SLOT->evsr = SLOT->RR[ksr]; - } - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); -} - -/* set multi,am,vib,EG-TYP,KSR,mul */ -static inline void set_mul(FM_OPL *OPL,int slot,int v) -{ - OPL_CH *CH = &OPL->P_CH[slot/2]; - OPL_SLOT *SLOT = &CH->SLOT[slot&1]; - - SLOT->mul = MUL_TABLE[v&0x0f]; - SLOT->KSR = (v&0x10) ? 0 : 2; - SLOT->eg_typ = (v&0x20)>>5; - SLOT->vib = (v&0x40); - SLOT->ams = (v&0x80); - CALC_FCSLOT(CH,SLOT); -} - -/* set ksl & tl */ -static inline void set_ksl_tl(FM_OPL *OPL,int slot,int v) -{ - OPL_CH *CH = &OPL->P_CH[slot/2]; - OPL_SLOT *SLOT = &CH->SLOT[slot&1]; - int ksl = v>>6; /* 0 / 1.5 / 3 / 6 db/OCT */ - - SLOT->ksl = ksl ? 3-ksl : 31; - SLOT->TL = (v&0x3f)*(0.75/EG_STEP); /* 0.75db step */ - - if( !(OPL->mode&0x80) ) - { /* not CSM latch total level */ - SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); - } -} - -/* set attack rate & decay rate */ -static inline void set_ar_dr(FM_OPL *OPL,int slot,int v) -{ - OPL_CH *CH = &OPL->P_CH[slot/2]; - OPL_SLOT *SLOT = &CH->SLOT[slot&1]; - int ar = v>>4; - int dr = v&0x0f; - - SLOT->AR = ar ? &OPL->AR_TABLE[ar<<2] : RATE_0; - SLOT->evsa = SLOT->AR[SLOT->ksr]; - if( SLOT->evm == ENV_MOD_AR ) SLOT->evs = SLOT->evsa; - - SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0; - SLOT->evsd = SLOT->DR[SLOT->ksr]; - if( SLOT->evm == ENV_MOD_DR ) SLOT->evs = SLOT->evsd; -} - -/* set sustain level & release rate */ -static inline void set_sl_rr(FM_OPL *OPL,int slot,int v) -{ - OPL_CH *CH = &OPL->P_CH[slot/2]; - OPL_SLOT *SLOT = &CH->SLOT[slot&1]; - int sl = v>>4; - int rr = v & 0x0f; - - SLOT->SL = SL_TABLE[sl]; - if( SLOT->evm == ENV_MOD_DR ) SLOT->eve = SLOT->SL; - SLOT->RR = &OPL->DR_TABLE[rr<<2]; - SLOT->evsr = SLOT->RR[SLOT->ksr]; - if( SLOT->evm == ENV_MOD_RR ) SLOT->evs = SLOT->evsr; -} - -/* operator output calcrator */ -#define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt+con)/(0x1000000/SIN_ENT))&(SIN_ENT-1)][env] -/* ---------- calcrate one of channel ---------- */ -static inline void OPL_CALC_CH( OPL_CH *CH ) -{ - UINT32 env_out; - OPL_SLOT *SLOT; - - feedback2 = 0; - /* SLOT 1 */ - SLOT = &CH->SLOT[SLOT1]; - env_out=OPL_CALC_SLOT(SLOT); - if( env_out < EG_ENT-1 ) - { - /* PG */ - if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); - else SLOT->Cnt += SLOT->Incr; - /* connectoion */ - if(CH->FB) - { - int feedback1 = (CH->op1_out[0]+CH->op1_out[1])>>CH->FB; - CH->op1_out[1] = CH->op1_out[0]; - *CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT,env_out,feedback1); - } - else - { - *CH->connect1 += OP_OUT(SLOT,env_out,0); - } - }else - { - CH->op1_out[1] = CH->op1_out[0]; - CH->op1_out[0] = 0; - } - /* SLOT 2 */ - SLOT = &CH->SLOT[SLOT2]; - env_out=OPL_CALC_SLOT(SLOT); - if( env_out < EG_ENT-1 ) - { - /* PG */ - if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); - else SLOT->Cnt += SLOT->Incr; - /* connectoion */ - outd[0] += OP_OUT(SLOT,env_out, feedback2); - } -} - -/* ---------- calcrate rhythm block ---------- */ -#define WHITE_NOISE_db 6.0 -static inline void OPL_CALC_RH( OPL_CH *CH ) -{ - UINT32 env_tam,env_sd,env_top,env_hh; - int whitenoise = (rand()&1)*(WHITE_NOISE_db/EG_STEP); - INT32 tone8; - - OPL_SLOT *SLOT; - int env_out; - - /* BD : same as FM serial mode and output level is large */ - feedback2 = 0; - /* SLOT 1 */ - SLOT = &CH[6].SLOT[SLOT1]; - env_out=OPL_CALC_SLOT(SLOT); - if( env_out < EG_ENT-1 ) - { - /* PG */ - if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); - else SLOT->Cnt += SLOT->Incr; - /* connectoion */ - if(CH[6].FB) - { - int feedback1 = (CH[6].op1_out[0]+CH[6].op1_out[1])>>CH[6].FB; - CH[6].op1_out[1] = CH[6].op1_out[0]; - feedback2 = CH[6].op1_out[0] = OP_OUT(SLOT,env_out,feedback1); - } - else - { - feedback2 = OP_OUT(SLOT,env_out,0); - } - }else - { - feedback2 = 0; - CH[6].op1_out[1] = CH[6].op1_out[0]; - CH[6].op1_out[0] = 0; - } - /* SLOT 2 */ - SLOT = &CH[6].SLOT[SLOT2]; - env_out=OPL_CALC_SLOT(SLOT); - if( env_out < EG_ENT-1 ) - { - /* PG */ - if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); - else SLOT->Cnt += SLOT->Incr; - /* connectoion */ - outd[0] += OP_OUT(SLOT,env_out, feedback2)*2; - } - - // SD (17) = mul14[fnum7] + white noise - // TAM (15) = mul15[fnum8] - // TOP (18) = fnum6(mul18[fnum8]+whitenoise) - // HH (14) = fnum7(mul18[fnum8]+whitenoise) + white noise - env_sd =OPL_CALC_SLOT(SLOT7_2) + whitenoise; - env_tam=OPL_CALC_SLOT(SLOT8_1); - env_top=OPL_CALC_SLOT(SLOT8_2); - env_hh =OPL_CALC_SLOT(SLOT7_1) + whitenoise; - - /* PG */ - if(SLOT7_1->vib) SLOT7_1->Cnt += (2*SLOT7_1->Incr*vib/VIB_RATE); - else SLOT7_1->Cnt += 2*SLOT7_1->Incr; - if(SLOT7_2->vib) SLOT7_2->Cnt += ((CH[7].fc*8)*vib/VIB_RATE); - else SLOT7_2->Cnt += (CH[7].fc*8); - if(SLOT8_1->vib) SLOT8_1->Cnt += (SLOT8_1->Incr*vib/VIB_RATE); - else SLOT8_1->Cnt += SLOT8_1->Incr; - if(SLOT8_2->vib) SLOT8_2->Cnt += ((CH[8].fc*48)*vib/VIB_RATE); - else SLOT8_2->Cnt += (CH[8].fc*48); - - tone8 = OP_OUT(SLOT8_2,whitenoise,0 ); - - /* SD */ - if( env_sd < EG_ENT-1 ) - outd[0] += OP_OUT(SLOT7_1,env_sd, 0)*8; - /* TAM */ - if( env_tam < EG_ENT-1 ) - outd[0] += OP_OUT(SLOT8_1,env_tam, 0)*2; - /* TOP-CY */ - if( env_top < EG_ENT-1 ) - outd[0] += OP_OUT(SLOT7_2,env_top,tone8)*2; - /* HH */ - if( env_hh < EG_ENT-1 ) - outd[0] += OP_OUT(SLOT7_2,env_hh,tone8)*2; -} - -/* ----------- initialize time tabls ----------- */ -static void init_timetables( FM_OPL *OPL , int ARRATE , int DRRATE ) -{ - int i; - double rate; - - /* make attack rate & decay rate tables */ - for (i = 0;i < 4;i++) OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0; - for (i = 4;i <= 60;i++){ - rate = OPL->freqbase; /* frequency rate */ - if( i < 60 ) rate *= 1.0+(i&3)*0.25; /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */ - rate *= 1<<((i>>2)-1); /* b2-5 : shift bit */ - rate *= (double)(EG_ENT<AR_TABLE[i] = rate / ARRATE; - OPL->DR_TABLE[i] = rate / DRRATE; - } - for (i = 60; i < ARRAY_SIZE(OPL->AR_TABLE); i++) - { - OPL->AR_TABLE[i] = EG_AED-1; - OPL->DR_TABLE[i] = OPL->DR_TABLE[60]; - } -#if 0 - for (i = 0;i < 64 ;i++){ /* make for overflow area */ - LOG(LOG_WAR, ("rate %2d , ar %f ms , dr %f ms\n", i, - ((double)(EG_ENT<AR_TABLE[i]) * (1000.0 / OPL->rate), - ((double)(EG_ENT<DR_TABLE[i]) * (1000.0 / OPL->rate) )); - } -#endif -} - -/* ---------- generic table initialize ---------- */ -static int OPLOpenTable( void ) -{ - int s,t; - double rate; - int i,j; - double pom; - - /* allocate dynamic tables */ - if( (TL_TABLE = malloc(TL_MAX*2*sizeof(INT32))) == NULL) - return 0; - if( (SIN_TABLE = malloc(SIN_ENT*4 *sizeof(INT32 *))) == NULL) - { - free(TL_TABLE); - return 0; - } - if( (AMS_TABLE = malloc(AMS_ENT*2 *sizeof(INT32))) == NULL) - { - free(TL_TABLE); - free(SIN_TABLE); - return 0; - } - if( (VIB_TABLE = malloc(VIB_ENT*2 *sizeof(INT32))) == NULL) - { - free(TL_TABLE); - free(SIN_TABLE); - free(AMS_TABLE); - return 0; - } - /* make total level table */ - for (t = 0;t < EG_ENT-1 ;t++){ - rate = ((1< voltage */ - TL_TABLE[ t] = (int)rate; - TL_TABLE[TL_MAX+t] = -TL_TABLE[t]; -/* LOG(LOG_INF,("TotalLevel(%3d) = %x\n",t,TL_TABLE[t]));*/ - } - /* fill volume off area */ - for ( t = EG_ENT-1; t < TL_MAX ;t++){ - TL_TABLE[t] = TL_TABLE[TL_MAX+t] = 0; - } - - /* make sinwave table (total level offet) */ - /* degree 0 = degree 180 = off */ - SIN_TABLE[0] = SIN_TABLE[SIN_ENT/2] = &TL_TABLE[EG_ENT-1]; - for (s = 1;s <= SIN_ENT/4;s++){ - pom = sin(2*PI*s/SIN_ENT); /* sin */ - pom = 20*log10(1/pom); /* decibel */ - j = pom / EG_STEP; /* TL_TABLE steps */ - - /* degree 0 - 90 , degree 180 - 90 : plus section */ - SIN_TABLE[ s] = SIN_TABLE[SIN_ENT/2-s] = &TL_TABLE[j]; - /* degree 180 - 270 , degree 360 - 270 : minus section */ - SIN_TABLE[SIN_ENT/2+s] = SIN_TABLE[SIN_ENT -s] = &TL_TABLE[TL_MAX+j]; -/* LOG(LOG_INF,("sin(%3d) = %f:%f db\n",s,pom,(double)j * EG_STEP));*/ - } - for (s = 0;s < SIN_ENT;s++) - { - SIN_TABLE[SIN_ENT*1+s] = s<(SIN_ENT/2) ? SIN_TABLE[s] : &TL_TABLE[EG_ENT]; - SIN_TABLE[SIN_ENT*2+s] = SIN_TABLE[s % (SIN_ENT/2)]; - SIN_TABLE[SIN_ENT*3+s] = (s/(SIN_ENT/4))&1 ? &TL_TABLE[EG_ENT] : SIN_TABLE[SIN_ENT*2+s]; - } - - /* envelope counter -> envelope output table */ - for (i=0; i= EG_ENT ) pom = EG_ENT-1; */ - ENV_CURVE[i] = (int)pom; - /* DECAY ,RELEASE curve */ - ENV_CURVE[(EG_DST>>ENV_BITS)+i]= i; - } - /* off */ - ENV_CURVE[EG_OFF>>ENV_BITS]= EG_ENT-1; - /* make LFO ams table */ - for (i=0; iSLOT[SLOT1]; - OPL_SLOT *slot2 = &CH->SLOT[SLOT2]; - /* all key off */ - OPL_KEYOFF(slot1); - OPL_KEYOFF(slot2); - /* total level latch */ - slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl); - slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl); - /* key on */ - CH->op1_out[0] = CH->op1_out[1] = 0; - OPL_KEYON(slot1); - OPL_KEYON(slot2); -} - -/* ---------- opl initialize ---------- */ -static void OPL_initialize(FM_OPL *OPL) -{ - int fn; - - /* frequency base */ - OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72 : 0; - /* Timer base time */ - OPL->TimerBase = 1.0/((double)OPL->clock / 72.0 ); - /* make time tables */ - init_timetables( OPL , OPL_ARRATE , OPL_DRRATE ); - /* make fnumber -> increment counter table */ - for( fn=0 ; fn < 1024 ; fn++ ) - { - OPL->FN_TABLE[fn] = OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2; - } - /* LFO freq.table */ - OPL->amsIncr = OPL->rate ? (double)AMS_ENT*(1<rate * 3.7 * ((double)OPL->clock/3600000) : 0; - OPL->vibIncr = OPL->rate ? (double)VIB_ENT*(1<rate * 6.4 * ((double)OPL->clock/3600000) : 0; -} - -/* ---------- write a OPL registers ---------- */ -static void OPLWriteReg(FM_OPL *OPL, int r, int v) -{ - OPL_CH *CH; - int slot; - int block_fnum; - - switch(r&0xe0) - { - case 0x00: /* 00-1f:control */ - switch(r&0x1f) - { - case 0x01: - /* wave selector enable */ - if(OPL->type&OPL_TYPE_WAVESEL) - { - OPL->wavesel = v&0x20; - if(!OPL->wavesel) - { - /* preset compatible mode */ - int c; - for(c=0;cmax_ch;c++) - { - OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0]; - OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0]; - } - } - } - return; - case 0x02: /* Timer 1 */ - OPL->T[0] = (256-v)*4; - break; - case 0x03: /* Timer 2 */ - OPL->T[1] = (256-v)*16; - return; - case 0x04: /* IRQ clear / mask and Timer enable */ - if(v&0x80) - { /* IRQ flag clear */ - OPL_STATUS_RESET(OPL,0x7f); - } - else - { /* set IRQ mask ,timer enable*/ - UINT8 st1 = v&1; - UINT8 st2 = (v>>1)&1; - /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */ - OPL_STATUS_RESET(OPL,v&0x78); - OPL_STATUSMASK_SET(OPL,((~v)&0x78)|0x01); - /* timer 2 */ - if(OPL->st[1] != st2) - { - double interval = st2 ? (double)OPL->T[1]*OPL->TimerBase : 0.0; - OPL->st[1] = st2; - if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+1,interval); - } - /* timer 1 */ - if(OPL->st[0] != st1) - { - double interval = st1 ? (double)OPL->T[0]*OPL->TimerBase : 0.0; - OPL->st[0] = st1; - if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+0,interval); - } - } - return; -#if BUILD_Y8950 - case 0x06: /* Key Board OUT */ - if(OPL->type&OPL_TYPE_KEYBOARD) - { - if(OPL->keyboardhandler_w) - OPL->keyboardhandler_w(OPL->keyboard_param,v); - else - LOG(LOG_WAR,("OPL:write unmapped KEYBOARD port\n")); - } - return; - case 0x07: /* DELTA-T control : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */ - if(OPL->type&OPL_TYPE_ADPCM) - YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v); - return; - case 0x08: /* MODE,DELTA-T : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */ - OPL->mode = v; - v&=0x1f; /* for DELTA-T unit */ - case 0x09: /* START ADD */ - case 0x0a: - case 0x0b: /* STOP ADD */ - case 0x0c: - case 0x0d: /* PRESCALE */ - case 0x0e: - case 0x0f: /* ADPCM data */ - case 0x10: /* DELTA-N */ - case 0x11: /* DELTA-N */ - case 0x12: /* EG-CTRL */ - if(OPL->type&OPL_TYPE_ADPCM) - YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v); - return; -#if 0 - case 0x15: /* DAC data */ - case 0x16: - case 0x17: /* SHIFT */ - return; - case 0x18: /* I/O CTRL (Direction) */ - if(OPL->type&OPL_TYPE_IO) - OPL->portDirection = v&0x0f; - return; - case 0x19: /* I/O DATA */ - if(OPL->type&OPL_TYPE_IO) - { - OPL->portLatch = v; - if(OPL->porthandler_w) - OPL->porthandler_w(OPL->port_param,v&OPL->portDirection); - } - return; - case 0x1a: /* PCM data */ - return; -#endif -#endif - } - break; - case 0x20: /* am,vib,ksr,eg type,mul */ - slot = slot_array[r&0x1f]; - if(slot == -1) return; - set_mul(OPL,slot,v); - return; - case 0x40: - slot = slot_array[r&0x1f]; - if(slot == -1) return; - set_ksl_tl(OPL,slot,v); - return; - case 0x60: - slot = slot_array[r&0x1f]; - if(slot == -1) return; - set_ar_dr(OPL,slot,v); - return; - case 0x80: - slot = slot_array[r&0x1f]; - if(slot == -1) return; - set_sl_rr(OPL,slot,v); - return; - case 0xa0: - switch(r) - { - case 0xbd: - /* amsep,vibdep,r,bd,sd,tom,tc,hh */ - { - UINT8 rkey = OPL->rhythm^v; - OPL->ams_table = &AMS_TABLE[v&0x80 ? AMS_ENT : 0]; - OPL->vib_table = &VIB_TABLE[v&0x40 ? VIB_ENT : 0]; - OPL->rhythm = v&0x3f; - if(OPL->rhythm&0x20) - { -#if 0 - usrintf_showmessage("OPL Rhythm mode select"); -#endif - /* BD key on/off */ - if(rkey&0x10) - { - if(v&0x10) - { - OPL->P_CH[6].op1_out[0] = OPL->P_CH[6].op1_out[1] = 0; - OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT1]); - OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT2]); - } - else - { - OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1]); - OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2]); - } - } - /* SD key on/off */ - if(rkey&0x08) - { - if(v&0x08) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT2]); - else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2]); - }/* TAM key on/off */ - if(rkey&0x04) - { - if(v&0x04) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT1]); - else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1]); - } - /* TOP-CY key on/off */ - if(rkey&0x02) - { - if(v&0x02) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT2]); - else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2]); - } - /* HH key on/off */ - if(rkey&0x01) - { - if(v&0x01) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT1]); - else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1]); - } - } - } - return; - } - /* keyon,block,fnum */ - if( (r&0x0f) > 8) return; - CH = &OPL->P_CH[r&0x0f]; - if(!(r&0x10)) - { /* a0-a8 */ - block_fnum = (CH->block_fnum&0x1f00) | v; - } - else - { /* b0-b8 */ - int keyon = (v>>5)&1; - block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); - if(CH->keyon != keyon) - { - if( (CH->keyon=keyon) ) - { - CH->op1_out[0] = CH->op1_out[1] = 0; - OPL_KEYON(&CH->SLOT[SLOT1]); - OPL_KEYON(&CH->SLOT[SLOT2]); - } - else - { - OPL_KEYOFF(&CH->SLOT[SLOT1]); - OPL_KEYOFF(&CH->SLOT[SLOT2]); - } - } - } - /* update */ - if(CH->block_fnum != block_fnum) - { - int blockRv = 7-(block_fnum>>10); - int fnum = block_fnum&0x3ff; - CH->block_fnum = block_fnum; - - CH->ksl_base = KSL_TABLE[block_fnum>>6]; - CH->fc = OPL->FN_TABLE[fnum]>>blockRv; - CH->kcode = CH->block_fnum>>9; - if( (OPL->mode&0x40) && CH->block_fnum&0x100) CH->kcode |=1; - CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); - CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); - } - return; - case 0xc0: - /* FB,C */ - if( (r&0x0f) > 8) return; - CH = &OPL->P_CH[r&0x0f]; - { - int feedback = (v>>1)&7; - CH->FB = feedback ? (8+1) - feedback : 0; - CH->CON = v&1; - set_algorithm(CH); - } - return; - case 0xe0: /* wave type */ - slot = slot_array[r&0x1f]; - if(slot == -1) return; - CH = &OPL->P_CH[slot/2]; - if(OPL->wavesel) - { - /* LOG(LOG_INF,("OPL SLOT %d wave select %d\n",slot,v&3)); */ - CH->SLOT[slot&1].wavetable = &SIN_TABLE[(v&0x03)*SIN_ENT]; - } - return; - } -} - -/* lock/unlock for common table */ -static int OPL_LockTable(void) -{ - num_lock++; - if(num_lock>1) return 0; - /* first time */ - cur_chip = NULL; - /* allocate total level table (128kb space) */ - if( !OPLOpenTable() ) - { - num_lock--; - return -1; - } - return 0; -} - -static void OPL_UnLockTable(void) -{ - if(num_lock) num_lock--; - if(num_lock) return; - /* last time */ - cur_chip = NULL; - OPLCloseTable(); -} - -#if (BUILD_YM3812 || BUILD_YM3526) -/*******************************************************************************/ -/* YM3812 local section */ -/*******************************************************************************/ - -/* ---------- update one of chip ----------- */ -void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) -{ - int i; - int data; - OPLSAMPLE *buf = buffer; - UINT32 amsCnt = OPL->amsCnt; - UINT32 vibCnt = OPL->vibCnt; - UINT8 rhythm = OPL->rhythm&0x20; - OPL_CH *CH,*R_CH; - - if( (void *)OPL != cur_chip ){ - cur_chip = (void *)OPL; - /* channel pointers */ - S_CH = OPL->P_CH; - E_CH = &S_CH[9]; - /* rhythm slot */ - SLOT7_1 = &S_CH[7].SLOT[SLOT1]; - SLOT7_2 = &S_CH[7].SLOT[SLOT2]; - SLOT8_1 = &S_CH[8].SLOT[SLOT1]; - SLOT8_2 = &S_CH[8].SLOT[SLOT2]; - /* LFO state */ - amsIncr = OPL->amsIncr; - vibIncr = OPL->vibIncr; - ams_table = OPL->ams_table; - vib_table = OPL->vib_table; - } - R_CH = rhythm ? &S_CH[6] : E_CH; - for( i=0; i < length ; i++ ) - { - /* channel A channel B channel C */ - /* LFO */ - ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT]; - vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT]; - outd[0] = 0; - /* FM part */ - for(CH=S_CH ; CH < R_CH ; CH++) - OPL_CALC_CH(CH); - /* Rythn part */ - if(rhythm) - OPL_CALC_RH(S_CH); - /* limit check */ - data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT ); - /* store to sound buffer */ - buf[i] = data >> OPL_OUTSB; - } - - OPL->amsCnt = amsCnt; - OPL->vibCnt = vibCnt; -#ifdef OPL_OUTPUT_LOG - if(opl_dbg_fp) - { - for(opl_dbg_chip=0;opl_dbg_chipamsCnt; - UINT32 vibCnt = OPL->vibCnt; - UINT8 rhythm = OPL->rhythm&0x20; - OPL_CH *CH,*R_CH; - YM_DELTAT *DELTAT = OPL->deltat; - - /* setup DELTA-T unit */ - YM_DELTAT_DECODE_PRESET(DELTAT); - - if( (void *)OPL != cur_chip ){ - cur_chip = (void *)OPL; - /* channel pointers */ - S_CH = OPL->P_CH; - E_CH = &S_CH[9]; - /* rhythm slot */ - SLOT7_1 = &S_CH[7].SLOT[SLOT1]; - SLOT7_2 = &S_CH[7].SLOT[SLOT2]; - SLOT8_1 = &S_CH[8].SLOT[SLOT1]; - SLOT8_2 = &S_CH[8].SLOT[SLOT2]; - /* LFO state */ - amsIncr = OPL->amsIncr; - vibIncr = OPL->vibIncr; - ams_table = OPL->ams_table; - vib_table = OPL->vib_table; - } - R_CH = rhythm ? &S_CH[6] : E_CH; - for( i=0; i < length ; i++ ) - { - /* channel A channel B channel C */ - /* LFO */ - ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT]; - vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT]; - outd[0] = 0; - /* deltaT ADPCM */ - if( DELTAT->portstate ) - YM_DELTAT_ADPCM_CALC(DELTAT); - /* FM part */ - for(CH=S_CH ; CH < R_CH ; CH++) - OPL_CALC_CH(CH); - /* Rythn part */ - if(rhythm) - OPL_CALC_RH(S_CH); - /* limit check */ - data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT ); - /* store to sound buffer */ - buf[i] = data >> OPL_OUTSB; - } - OPL->amsCnt = amsCnt; - OPL->vibCnt = vibCnt; - /* deltaT START flag */ - if( !DELTAT->portstate ) - OPL->status &= 0xfe; -} -#endif - -/* ---------- reset one of chip ---------- */ -void OPLResetChip(FM_OPL *OPL) -{ - int c,s; - int i; - - /* reset chip */ - OPL->mode = 0; /* normal mode */ - OPL_STATUS_RESET(OPL,0x7f); - /* reset with register write */ - OPLWriteReg(OPL,0x01,0); /* wabesel disable */ - OPLWriteReg(OPL,0x02,0); /* Timer1 */ - OPLWriteReg(OPL,0x03,0); /* Timer2 */ - OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */ - for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0); - /* reset operator parameter */ - for( c = 0 ; c < OPL->max_ch ; c++ ) - { - OPL_CH *CH = &OPL->P_CH[c]; - /* OPL->P_CH[c].PAN = OPN_CENTER; */ - for(s = 0 ; s < 2 ; s++ ) - { - /* wave table */ - CH->SLOT[s].wavetable = &SIN_TABLE[0]; - /* CH->SLOT[s].evm = ENV_MOD_RR; */ - CH->SLOT[s].evc = EG_OFF; - CH->SLOT[s].eve = EG_OFF+1; - CH->SLOT[s].evs = 0; - } - } -#if BUILD_Y8950 - if(OPL->type&OPL_TYPE_ADPCM) - { - YM_DELTAT *DELTAT = OPL->deltat; - - DELTAT->freqbase = OPL->freqbase; - DELTAT->output_pointer = outd; - DELTAT->portshift = 5; - DELTAT->output_range = DELTAT_MIXING_LEVEL<P_CH = (OPL_CH *)ptr; ptr+=sizeof(OPL_CH)*max_ch; -#if BUILD_Y8950 - if(type&OPL_TYPE_ADPCM) OPL->deltat = (YM_DELTAT *)ptr; ptr+=sizeof(YM_DELTAT); -#endif - /* set channel state pointer */ - OPL->type = type; - OPL->clock = clock; - OPL->rate = rate; - OPL->max_ch = max_ch; - /* init grobal tables */ - OPL_initialize(OPL); - /* reset chip */ - OPLResetChip(OPL); -#ifdef OPL_OUTPUT_LOG - if(!opl_dbg_fp) - { - opl_dbg_fp = fopen("opllog.opl","wb"); - opl_dbg_maxchip = 0; - } - if(opl_dbg_fp) - { - opl_dbg_opl[opl_dbg_maxchip] = OPL; - fprintf(opl_dbg_fp,"%c%c%c%c%c%c",0x00+opl_dbg_maxchip, - type, - clock&0xff, - (clock/0x100)&0xff, - (clock/0x10000)&0xff, - (clock/0x1000000)&0xff); - opl_dbg_maxchip++; - } -#endif - return OPL; -} - -/* ---------- Destroy one of vietual YM3812 ---------- */ -void OPLDestroy(FM_OPL *OPL) -{ -#ifdef OPL_OUTPUT_LOG - if(opl_dbg_fp) - { - fclose(opl_dbg_fp); - opl_dbg_fp = NULL; - } -#endif - OPL_UnLockTable(); - free(OPL); -} - -/* ---------- Option handlers ---------- */ - -void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset) -{ - OPL->TimerHandler = TimerHandler; - OPL->TimerParam = channelOffset; -} -void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param) -{ - OPL->IRQHandler = IRQHandler; - OPL->IRQParam = param; -} -void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param) -{ - OPL->UpdateHandler = UpdateHandler; - OPL->UpdateParam = param; -} -#if BUILD_Y8950 -void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param) -{ - OPL->porthandler_w = PortHandler_w; - OPL->porthandler_r = PortHandler_r; - OPL->port_param = param; -} - -void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param) -{ - OPL->keyboardhandler_w = KeyboardHandler_w; - OPL->keyboardhandler_r = KeyboardHandler_r; - OPL->keyboard_param = param; -} -#endif -/* ---------- YM3812 I/O interface ---------- */ -int OPLWrite(FM_OPL *OPL,int a,int v) -{ - if( !(a&1) ) - { /* address port */ - OPL->address = v & 0xff; - } - else - { /* data port */ - if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); -#ifdef OPL_OUTPUT_LOG - if(opl_dbg_fp) - { - for(opl_dbg_chip=0;opl_dbg_chipaddress,v); - } -#endif - OPLWriteReg(OPL,OPL->address,v); - } - return OPL->status>>7; -} - -unsigned char OPLRead(FM_OPL *OPL,int a) -{ - if( !(a&1) ) - { /* status port */ - return OPL->status & (OPL->statusmask|0x80); - } - /* data port */ - switch(OPL->address) - { - case 0x05: /* KeyBoard IN */ - if(OPL->type&OPL_TYPE_KEYBOARD) - { - if(OPL->keyboardhandler_r) - return OPL->keyboardhandler_r(OPL->keyboard_param); - else { - LOG(LOG_WAR,("OPL:read unmapped KEYBOARD port\n")); - } - } - return 0; -#if 0 - case 0x0f: /* ADPCM-DATA */ - return 0; -#endif - case 0x19: /* I/O DATA */ - if(OPL->type&OPL_TYPE_IO) - { - if(OPL->porthandler_r) - return OPL->porthandler_r(OPL->port_param); - else { - LOG(LOG_WAR,("OPL:read unmapped I/O port\n")); - } - } - return 0; - case 0x1a: /* PCM-DATA */ - return 0; - } - return 0; -} - -int OPLTimerOver(FM_OPL *OPL,int c) -{ - if( c ) - { /* Timer B */ - OPL_STATUS_SET(OPL,0x20); - } - else - { /* Timer A */ - OPL_STATUS_SET(OPL,0x40); - /* CSM mode key,TL control */ - if( OPL->mode & 0x80 ) - { /* CSM mode total level latch and auto key on */ - int ch; - if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); - for(ch=0;ch<9;ch++) - CSMKeyControll( &OPL->P_CH[ch] ); - } - } - /* reload timer */ - if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+c,(double)OPL->T[c]*OPL->TimerBase); - return OPL->status>>7; -} diff --git a/qemu/hw/audio/fmopl.h b/qemu/hw/audio/fmopl.h deleted file mode 100644 index 24ba5f480..000000000 --- a/qemu/hw/audio/fmopl.h +++ /dev/null @@ -1,174 +0,0 @@ -#ifndef __FMOPL_H_ -#define __FMOPL_H_ - -/* --- select emulation chips --- */ -#define BUILD_YM3812 (HAS_YM3812) -//#define BUILD_YM3526 (HAS_YM3526) -//#define BUILD_Y8950 (HAS_Y8950) - -/* --- system optimize --- */ -/* select bit size of output : 8 or 16 */ -#define OPL_OUTPUT_BIT 16 - -/* compiler dependence */ -#ifndef OSD_CPU_H -#define OSD_CPU_H -typedef unsigned char UINT8; /* unsigned 8bit */ -typedef unsigned short UINT16; /* unsigned 16bit */ -typedef unsigned int UINT32; /* unsigned 32bit */ -typedef signed char INT8; /* signed 8bit */ -typedef signed short INT16; /* signed 16bit */ -typedef signed int INT32; /* signed 32bit */ -#endif - -#if (OPL_OUTPUT_BIT==16) -typedef INT16 OPLSAMPLE; -#endif -#if (OPL_OUTPUT_BIT==8) -typedef unsigned char OPLSAMPLE; -#endif - - -#if BUILD_Y8950 -#include "ymdeltat.h" -#endif - -typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec); -typedef void (*OPL_IRQHANDLER)(int param,int irq); -typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us); -typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data); -typedef unsigned char (*OPL_PORTHANDLER_R)(int param); - -/* !!!!! here is private section , do not access there member direct !!!!! */ - -#define OPL_TYPE_WAVESEL 0x01 /* waveform select */ -#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */ -#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */ -#define OPL_TYPE_IO 0x08 /* I/O port */ - -/* Saving is necessary for member of the 'R' mark for suspend/resume */ -/* ---------- OPL one of slot ---------- */ -typedef struct fm_opl_slot { - INT32 TL; /* total level :TL << 8 */ - INT32 TLL; /* adjusted now TL */ - UINT8 KSR; /* key scale rate :(shift down bit) */ - INT32 *AR; /* attack rate :&AR_TABLE[AR<<2] */ - INT32 *DR; /* decay rate :&DR_TALBE[DR<<2] */ - INT32 SL; /* sustin level :SL_TALBE[SL] */ - INT32 *RR; /* release rate :&DR_TABLE[RR<<2] */ - UINT8 ksl; /* keyscale level :(shift down bits) */ - UINT8 ksr; /* key scale rate :kcode>>KSR */ - UINT32 mul; /* multiple :ML_TABLE[ML] */ - UINT32 Cnt; /* frequency count : */ - UINT32 Incr; /* frequency step : */ - /* envelope generator state */ - UINT8 eg_typ; /* envelope type flag */ - UINT8 evm; /* envelope phase */ - INT32 evc; /* envelope counter */ - INT32 eve; /* envelope counter end point */ - INT32 evs; /* envelope counter step */ - INT32 evsa; /* envelope step for AR :AR[ksr] */ - INT32 evsd; /* envelope step for DR :DR[ksr] */ - INT32 evsr; /* envelope step for RR :RR[ksr] */ - /* LFO */ - UINT8 ams; /* ams flag */ - UINT8 vib; /* vibrate flag */ - /* wave selector */ - INT32 **wavetable; -}OPL_SLOT; - -/* ---------- OPL one of channel ---------- */ -typedef struct fm_opl_channel { - OPL_SLOT SLOT[2]; - UINT8 CON; /* connection type */ - UINT8 FB; /* feed back :(shift down bit) */ - INT32 *connect1; /* slot1 output pointer */ - INT32 *connect2; /* slot2 output pointer */ - INT32 op1_out[2]; /* slot1 output for selfeedback */ - /* phase generator state */ - UINT32 block_fnum; /* block+fnum : */ - UINT8 kcode; /* key code : KeyScaleCode */ - UINT32 fc; /* Freq. Increment base */ - UINT32 ksl_base; /* KeyScaleLevel Base step */ - UINT8 keyon; /* key on/off flag */ -} OPL_CH; - -/* OPL state */ -typedef struct fm_opl_f { - UINT8 type; /* chip type */ - int clock; /* master clock (Hz) */ - int rate; /* sampling rate (Hz) */ - double freqbase; /* frequency base */ - double TimerBase; /* Timer base time (==sampling time) */ - UINT8 address; /* address register */ - UINT8 status; /* status flag */ - UINT8 statusmask; /* status mask */ - UINT32 mode; /* Reg.08 : CSM , notesel,etc. */ - /* Timer */ - int T[2]; /* timer counter */ - UINT8 st[2]; /* timer enable */ - /* FM channel slots */ - OPL_CH *P_CH; /* pointer of CH */ - int max_ch; /* maximum channel */ - /* Rhythm sention */ - UINT8 rhythm; /* Rhythm mode , key flag */ -#if BUILD_Y8950 - /* Delta-T ADPCM unit (Y8950) */ - YM_DELTAT *deltat; /* DELTA-T ADPCM */ -#endif - /* Keyboard / I/O interface unit (Y8950) */ - UINT8 portDirection; - UINT8 portLatch; - OPL_PORTHANDLER_R porthandler_r; - OPL_PORTHANDLER_W porthandler_w; - int port_param; - OPL_PORTHANDLER_R keyboardhandler_r; - OPL_PORTHANDLER_W keyboardhandler_w; - int keyboard_param; - /* time tables */ - INT32 AR_TABLE[75]; /* atttack rate tables */ - INT32 DR_TABLE[75]; /* decay rate tables */ - UINT32 FN_TABLE[1024]; /* fnumber -> increment counter */ - /* LFO */ - INT32 *ams_table; - INT32 *vib_table; - INT32 amsCnt; - INT32 amsIncr; - INT32 vibCnt; - INT32 vibIncr; - /* wave selector enable flag */ - UINT8 wavesel; - /* external event callback handler */ - OPL_TIMERHANDLER TimerHandler; /* TIMER handler */ - int TimerParam; /* TIMER parameter */ - OPL_IRQHANDLER IRQHandler; /* IRQ handler */ - int IRQParam; /* IRQ parameter */ - OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */ - int UpdateParam; /* stream update parameter */ -} FM_OPL; - -/* ---------- Generic interface section ---------- */ -#define OPL_TYPE_YM3526 (0) -#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL) -#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO) - -FM_OPL *OPLCreate(int type, int clock, int rate); -void OPLDestroy(FM_OPL *OPL); -void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset); -void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param); -void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param); -/* Y8950 port handlers */ -void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param); -void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param); - -void OPLResetChip(FM_OPL *OPL); -int OPLWrite(FM_OPL *OPL,int a,int v); -unsigned char OPLRead(FM_OPL *OPL,int a); -int OPLTimerOver(FM_OPL *OPL,int c); - -/* YM3626/YM3812 local section */ -void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length); - -void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length); - -#endif diff --git a/qemu/hw/audio/gus.c b/qemu/hw/audio/gus.c deleted file mode 100644 index 9dd6947be..000000000 --- a/qemu/hw/audio/gus.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * QEMU Proxy for Gravis Ultrasound GF1 emulation by Tibor "TS" Schütz - * - * Copyright (c) 2002-2005 Vassili Karpov (malc) - * - * 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/audio/audio.h" -#include "audio/audio.h" -#include "hw/isa/isa.h" -#include "gusemu.h" -#include "gustate.h" - -#define dolog(...) AUD_log ("audio", __VA_ARGS__) -#ifdef DEBUG -#define ldebug(...) dolog (__VA_ARGS__) -#else -#define ldebug(...) -#endif - -#ifdef HOST_WORDS_BIGENDIAN -#define GUS_ENDIANNESS 1 -#else -#define GUS_ENDIANNESS 0 -#endif - -#define TYPE_GUS "gus" -#define GUS(obj) OBJECT_CHECK (GUSState, (obj), TYPE_GUS) - -typedef struct GUSState { - ISADevice dev; - GUSEmuState emu; - QEMUSoundCard card; - uint32_t freq; - uint32_t port; - int pos, left, shift, irqs; - GUSsample *mixbuf; - uint8_t himem[1024 * 1024 + 32 + 4096]; - int samples; - SWVoiceOut *voice; - int64_t last_ticks; - qemu_irq pic; - IsaDma *isa_dma; -} GUSState; - -static uint32_t gus_readb(void *opaque, uint32_t nport) -{ - GUSState *s = opaque; - - return gus_read (&s->emu, nport, 1); -} - -static void gus_writeb(void *opaque, uint32_t nport, uint32_t val) -{ - GUSState *s = opaque; - - gus_write (&s->emu, nport, 1, val); -} - -static int write_audio (GUSState *s, int samples) -{ - int net = 0; - int pos = s->pos; - - while (samples) { - int nbytes, wbytes, wsampl; - - nbytes = samples << s->shift; - wbytes = AUD_write ( - s->voice, - s->mixbuf + (pos << (s->shift - 1)), - nbytes - ); - - if (wbytes) { - wsampl = wbytes >> s->shift; - - samples -= wsampl; - pos = (pos + wsampl) % s->samples; - - net += wsampl; - } - else { - break; - } - } - - return net; -} - -static void GUS_callback (void *opaque, int free) -{ - int samples, to_play, net = 0; - GUSState *s = opaque; - - samples = free >> s->shift; - to_play = audio_MIN (samples, s->left); - - while (to_play) { - int written = write_audio (s, to_play); - - if (!written) { - goto reset; - } - - s->left -= written; - to_play -= written; - samples -= written; - net += written; - } - - samples = audio_MIN (samples, s->samples); - if (samples) { - gus_mixvoices (&s->emu, s->freq, samples, s->mixbuf); - - while (samples) { - int written = write_audio (s, samples); - if (!written) { - break; - } - samples -= written; - net += written; - } - } - s->left = samples; - - reset: - gus_irqgen (&s->emu, muldiv64 (net, 1000000, s->freq)); -} - -int GUS_irqrequest (GUSEmuState *emu, int hwirq, int n) -{ - GUSState *s = emu->opaque; - /* qemu_irq_lower (s->pic); */ - qemu_irq_raise (s->pic); - s->irqs += n; - ldebug ("irqrequest %d %d %d\n", hwirq, n, s->irqs); - return n; -} - -void GUS_irqclear (GUSEmuState *emu, int hwirq) -{ - GUSState *s = emu->opaque; - ldebug ("irqclear %d %d\n", hwirq, s->irqs); - qemu_irq_lower (s->pic); - s->irqs -= 1; -#ifdef IRQ_STORM - if (s->irqs > 0) { - qemu_irq_raise (s->pic[hwirq]); - } -#endif -} - -void GUS_dmarequest (GUSEmuState *emu) -{ - GUSState *s = emu->opaque; - IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); - ldebug ("dma request %d\n", 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 = 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 = 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) { - k->release_DREQ(s->isa_dma, s->emu.gusdma); - } - return dma_len; -} - -static const VMStateDescription vmstate_gus = { - .name = "gus", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_INT32 (pos, GUSState), - VMSTATE_INT32 (left, GUSState), - VMSTATE_INT32 (shift, GUSState), - VMSTATE_INT32 (irqs, GUSState), - VMSTATE_INT32 (samples, GUSState), - VMSTATE_INT64 (last_ticks, GUSState), - VMSTATE_BUFFER (himem, GUSState), - VMSTATE_END_OF_LIST () - } -}; - -static const MemoryRegionPortio gus_portio_list1[] = { - {0x000, 1, 1, .write = gus_writeb }, - {0x006, 10, 1, .read = gus_readb, .write = gus_writeb }, - {0x100, 8, 1, .read = gus_readb, .write = gus_writeb }, - PORTIO_END_OF_LIST (), -}; - -static const MemoryRegionPortio gus_portio_list2[] = { - {0, 2, 1, .read = gus_readb }, - PORTIO_END_OF_LIST (), -}; - -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); - - as.freq = s->freq; - as.nchannels = 2; - as.fmt = AUD_FMT_S16; - as.endianness = GUS_ENDIANNESS; - - s->voice = AUD_open_out ( - &s->card, - NULL, - "gus", - s, - GUS_callback, - &as - ); - - if (!s->voice) { - AUD_remove_card (&s->card); - error_setg(errp, "No voice"); - return; - } - - s->shift = 2; - s->samples = AUD_get_buffer_size_out (s->voice) >> s->shift; - s->mixbuf = g_malloc0 (s->samples << s->shift); - - isa_register_portio_list (d, s->port, gus_portio_list1, s, "gus"); - isa_register_portio_list (d, (s->port + 0x100) & 0xf00, - gus_portio_list2, s, "gus"); - - 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; - isa_init_irq (d, &s->pic, s->emu.gusirq); - - AUD_set_active_out (s->voice, 1); -} - -static int GUS_init (ISABus *bus) -{ - isa_create_simple (bus, TYPE_GUS); - return 0; -} - -static Property gus_properties[] = { - DEFINE_PROP_UINT32 ("freq", GUSState, freq, 44100), - DEFINE_PROP_UINT32 ("iobase", GUSState, port, 0x240), - DEFINE_PROP_UINT32 ("irq", GUSState, emu.gusirq, 7), - DEFINE_PROP_UINT32 ("dma", GUSState, emu.gusdma, 3), - DEFINE_PROP_END_OF_LIST (), -}; - -static void gus_class_initfn (ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS (klass); - - dc->realize = gus_realizefn; - set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - dc->desc = "Gravis Ultrasound GF1"; - dc->vmsd = &vmstate_gus; - dc->props = gus_properties; -} - -static const TypeInfo gus_info = { - .name = TYPE_GUS, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof (GUSState), - .class_init = gus_class_initfn, -}; - -static void gus_register_types (void) -{ - type_register_static (&gus_info); - isa_register_soundhw("gus", "Gravis Ultrasound GF1", GUS_init); -} - -type_init (gus_register_types) diff --git a/qemu/hw/audio/gusemu.h b/qemu/hw/audio/gusemu.h deleted file mode 100644 index b7f075126..000000000 --- a/qemu/hw/audio/gusemu.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * GUSEMU32 - API - * - * Copyright (C) 2000-2007 Tibor "TS" Schütz - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef GUSEMU_H -#define GUSEMU_H - -/* data types (need to be adjusted if neither a VC6 nor a C99 compatible compiler is used) */ - -#if defined _WIN32 && defined _MSC_VER /* doesn't support other win32 compilers yet, do it yourself... */ - typedef unsigned char GUSbyte; - typedef unsigned short GUSword; - typedef unsigned int GUSdword; - typedef signed char GUSchar; - typedef signed short GUSsample; -#else - typedef int8_t GUSchar; - typedef uint8_t GUSbyte; - typedef uint16_t GUSword; - typedef uint32_t GUSdword; - typedef int16_t GUSsample; -#endif - -typedef struct _GUSEmuState -{ - GUSbyte *himemaddr; /* 1024*1024 bytes used for storing uploaded samples (+32 additional bytes for read padding) */ - GUSbyte *gusdatapos; /* (gusdataend-gusdata) bytes used for storing emulated GF1/mixer register states (32*32+4 bytes in initial GUSemu32 version) */ - uint32_t gusirq; - uint32_t gusdma; - unsigned int timer1fraction; - unsigned int timer2fraction; - void *opaque; -} GUSEmuState; - -/* ** Callback functions needed: */ -/* NMI is defined as hwirq=-1 (not supported (yet?)) */ -/* GUS_irqrequest returns the number of IRQs actually scheduled into the virtual machine */ -/* Level triggered IRQ simulations normally return 1 */ -/* Event triggered IRQ simulation can safely ignore GUS_irqclear calls */ -int GUS_irqrequest(GUSEmuState *state, int hwirq, int num);/* needed in both mixer and bus emulation functions. */ -void GUS_irqclear( GUSEmuState *state, int hwirq); /* used by gus_write() only - can be left empty for mixer functions */ -void GUS_dmarequest(GUSEmuState *state); /* used by gus_write() only - can be left empty for mixer functions */ - -/* ** ISA bus interface functions: */ - -/* Port I/O handlers */ -/* support the following ports: */ -/* 2x0,2x6,2x8...2xF,3x0...3x7; */ -/* optional: 388,389 (at least writes should be forwarded or some GUS detection algorithms will fail) */ -/* data is passed in host byte order */ -unsigned int gus_read( GUSEmuState *state, int port, int size); -void gus_write(GUSEmuState *state, int port, int size, unsigned int data); -/* size is given in bytes (1 for byte, 2 for word) */ - -/* DMA data transfer function */ -/* data pointed to is passed in native x86 order */ -void gus_dma_transferdata(GUSEmuState *state, char *dma_addr, unsigned int count, int TC); -/* Called back by GUS_start_DMA as soon as the emulated DMA controller is ready for a transfer to or from GUS */ -/* (might be immediately if the DMA controller was programmed first) */ -/* dma_addr is an already translated address directly pointing to the beginning of the memory block */ -/* do not forget to update DMA states after the call, including the DREQ and TC flags */ -/* it is possible to break down a single transfer into multiple ones, but take care that: */ -/* -dma_count is actually count-1 */ -/* -before and during a transfer, DREQ is set and TC cleared */ -/* -when calling gus_dma_transferdata(), TC is only set true for call transferring the last byte */ -/* -after the last transfer, DREQ is cleared and TC is set */ - -/* ** GF1 mixer emulation functions: */ -/* Usually, gus_irqgen should be called directly after gus_mixvoices if you can meet the recommended ranges. */ -/* If the interrupts are executed immediately (i.e., are synchronous), it may be useful to break this */ -/* down into a sequence of gus_mixvoice();gus_irqgen(); calls while mixing an audio block. */ -/* If the interrupts are asynchronous, it may be needed to use a separate thread mixing into a temporary */ -/* audio buffer in order to avoid quality loss caused by large numsamples and elapsed_time values. */ - -void gus_mixvoices(GUSEmuState *state, unsigned int playback_freq, unsigned int numsamples, GUSsample *bufferpos); -/* recommended range: 10 < numsamples < 100 */ -/* lower values may result in increased rounding error, higher values often cause audible timing delays */ - -void gus_irqgen(GUSEmuState *state, unsigned int elapsed_time); -/* recommended range: 80us < elapsed_time < max(1000us, numsamples/playback_freq) */ -/* lower values won´t provide any benefit at all, higher values can cause audible timing delays */ -/* note: masked timers are also calculated by this function, thus it might be needed even without any IRQs in use! */ - -#endif /* gusemu.h */ diff --git a/qemu/hw/audio/gusemu_hal.c b/qemu/hw/audio/gusemu_hal.c deleted file mode 100644 index 973d6b9f4..000000000 --- a/qemu/hw/audio/gusemu_hal.c +++ /dev/null @@ -1,555 +0,0 @@ -/* - * GUSEMU32 - bus interface part - * - * Copyright (C) 2000-2007 Tibor "TS" Schütz - * - * 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. - */ - -/* - * 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" - -#define GUSregb(position) (* (gusptr+(position))) -#define GUSregw(position) (*(GUSword *) (gusptr+(position))) -#define GUSregd(position) (*(GUSdword *)(gusptr+(position))) - -/* size given in bytes */ -unsigned int gus_read(GUSEmuState * state, int port, int size) -{ - int value_read = 0; - - GUSbyte *gusptr; - gusptr = state->gusdatapos; - GUSregd(portaccesses)++; - - switch (port & 0xff0f) - { - /* MixerCtrlReg (read not supported on GUS classic) */ - /* case 0x200: return GUSregb(MixerCtrlReg2x0); */ - case 0x206: /* IRQstatReg / SB2x6IRQ */ - /* adlib/sb bits set in port handlers */ - /* timer/voice bits set in gus_irqgen() */ - /* dma bit set in gus_dma_transferdata */ - /* midi not implemented yet */ - return GUSregb(IRQStatReg2x6); - /* case 0x308: */ /* AdLib388 */ - case 0x208: - if (GUSregb(GUS45TimerCtrl) & 1) - return GUSregb(TimerStatus2x8); - return GUSregb(AdLibStatus2x8); /* AdLibStatus */ - case 0x309: /* AdLib389 */ - case 0x209: - return GUSregb(AdLibData2x9); /* AdLibData */ - case 0x20A: - return GUSregb(AdLibCommand2xA); /* AdLib2x8_2xA */ - -#if 0 - case 0x20B: /* GUS hidden registers (read not supported on GUS classic) */ - switch (GUSregb(RegCtrl_2xF) & 0x07) - { - case 0: /* IRQ/DMA select */ - if (GUSregb(MixerCtrlReg2x0) & 0x40) - return GUSregb(IRQ_2xB); /* control register select bit */ - else - return GUSregb(DMA_2xB); - /* case 1-5: */ /* general purpose emulation regs */ - /* return ... */ /* + status reset reg (write only) */ - case 6: - return GUSregb(Jumper_2xB); /* Joystick/MIDI enable (JumperReg) */ - default:; - } - break; -#endif - - case 0x20C: /* SB2xCd */ - value_read = GUSregb(SB2xCd); - if (GUSregb(StatRead_2xF) & 0x20) - GUSregb(SB2xCd) ^= 0x80; /* toggle MSB on read */ - return value_read; - /* case 0x20D: */ /* SB2xD is write only -> 2xE writes to it*/ - case 0x20E: - if (GUSregb(RegCtrl_2xF) & 0x80) /* 2xE read IRQ enabled? */ - { - GUSregb(StatRead_2xF) |= 0x80; - GUS_irqrequest(state, state->gusirq, 1); - } - return GUSregb(SB2xE); /* SB2xE */ - case 0x20F: /* StatRead_2xF */ - /*set/clear fixed bits */ - /*value_read = (GUSregb(StatRead_2xF) & 0xf9)|1; */ /*(LSB not set on GUS classic!)*/ - value_read = (GUSregb(StatRead_2xF) & 0xf9); - if (GUSregb(MixerCtrlReg2x0) & 0x08) - value_read |= 2; /* DMA/IRQ enabled flag */ - return value_read; - /* case 0x300: */ /* MIDI (not implemented) */ - /* case 0x301: */ /* MIDI (not implemented) */ - case 0x302: - return GUSregb(VoiceSelReg3x2); /* VoiceSelReg */ - case 0x303: - return GUSregb(FunkSelReg3x3); /* FunkSelReg */ - case 0x304: /* DataRegLoByte3x4 + DataRegWord3x4 */ - case 0x305: /* DataRegHiByte3x5 */ - switch (GUSregb(FunkSelReg3x3)) - { - /* common functions */ - case 0x41: /* DramDMAContrReg */ - value_read = GUSregb(GUS41DMACtrl); /* &0xfb */ - GUSregb(GUS41DMACtrl) &= 0xbb; - if (state->gusdma >= 4) - value_read |= 0x04; - if (GUSregb(IRQStatReg2x6) & 0x80) - { - value_read |= 0x40; - GUSregb(IRQStatReg2x6) &= 0x7f; - if (!GUSregb(IRQStatReg2x6)) - GUS_irqclear(state, state->gusirq); - } - return (GUSbyte) value_read; - /* DramDMAmemPosReg */ - /* case 0x42: value_read=GUSregw(GUS42DMAStart); break;*/ - /* 43h+44h write only */ - case 0x45: - return GUSregb(GUS45TimerCtrl); /* TimerCtrlReg */ - /* 46h+47h write only */ - /* 48h: samp freq - write only */ - case 0x49: - return GUSregb(GUS49SampCtrl) & 0xbf; /* SampCtrlReg */ - /* case 4bh: */ /* joystick trim not supported */ - /* case 0x4c: return GUSregb(GUS4cReset); */ /* GUSreset: write only*/ - /* voice specific functions */ - case 0x80: - case 0x81: - case 0x82: - case 0x83: - case 0x84: - case 0x85: - case 0x86: - case 0x87: - case 0x88: - case 0x89: - case 0x8a: - case 0x8b: - case 0x8c: - case 0x8d: - { - int offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f); - offset += ((int) GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Funktion*2 */ - value_read = GUSregw(offset); - } - break; - /* voice unspecific functions */ - case 0x8e: /* NumVoice */ - return GUSregb(NumVoices); - case 0x8f: /* irqstatreg */ - /* (pseudo IRQ-FIFO is processed during a gus_write(0x3X3,0x8f)) */ - return GUSregb(SynVoiceIRQ8f); - default: - return 0xffff; - } - if (size == 1) - { - if ((port & 0xff0f) == 0x305) - value_read = value_read >> 8; - value_read &= 0xff; - } - return (GUSword) value_read; - /* case 0x306: */ /* Mixer/Version info */ - /* return 0xff; */ /* Pre 3.6 boards, ICS mixer NOT present */ - case 0x307: /* DRAMaccess */ - { - GUSbyte *adr; - adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff); - return *adr; - } - default:; - } - return 0xffff; -} - -void gus_write(GUSEmuState * state, int port, int size, unsigned int data) -{ - GUSbyte *gusptr; - gusptr = state->gusdatapos; - GUSregd(portaccesses)++; - - switch (port & 0xff0f) - { - case 0x200: /* MixerCtrlReg */ - GUSregb(MixerCtrlReg2x0) = (GUSbyte) data; - break; - case 0x206: /* IRQstatReg / SB2x6IRQ */ - if (GUSregb(GUS45TimerCtrl) & 0x20) /* SB IRQ enabled? -> set 2x6IRQ bit */ - { - GUSregb(TimerStatus2x8) |= 0x08; - GUSregb(IRQStatReg2x6) = 0x10; - GUS_irqrequest(state, state->gusirq, 1); - } - break; - case 0x308: /* AdLib 388h */ - case 0x208: /* AdLibCommandReg */ - GUSregb(AdLibCommand2xA) = (GUSbyte) data; - break; - case 0x309: /* AdLib 389h */ - case 0x209: /* AdLibDataReg */ - if ((GUSregb(AdLibCommand2xA) == 0x04) && (!(GUSregb(GUS45TimerCtrl) & 1))) /* GUS auto timer mode enabled? */ - { - if (data & 0x80) - GUSregb(TimerStatus2x8) &= 0x1f; /* AdLib IRQ reset? -> clear maskable adl. timer int regs */ - else - GUSregb(TimerDataReg2x9) = (GUSbyte) data; - } - else - { - GUSregb(AdLibData2x9) = (GUSbyte) data; - if (GUSregb(GUS45TimerCtrl) & 0x02) - { - GUSregb(TimerStatus2x8) |= 0x01; - GUSregb(IRQStatReg2x6) = 0x10; - GUS_irqrequest(state, state->gusirq, 1); - } - } - break; - case 0x20A: - GUSregb(AdLibStatus2x8) = (GUSbyte) data; - break; /* AdLibStatus2x8 */ - case 0x20B: /* GUS hidden registers */ - switch (GUSregb(RegCtrl_2xF) & 0x7) - { - case 0: - if (GUSregb(MixerCtrlReg2x0) & 0x40) - GUSregb(IRQ_2xB) = (GUSbyte) data; /* control register select bit */ - else - GUSregb(DMA_2xB) = (GUSbyte) data; - break; - /* case 1-4: general purpose emulation regs */ - case 5: /* clear stat reg 2xF */ - GUSregb(StatRead_2xF) = 0; /* ToDo: is this identical with GUS classic? */ - if (!GUSregb(IRQStatReg2x6)) - GUS_irqclear(state, state->gusirq); - break; - case 6: /* Jumper reg (Joystick/MIDI enable) */ - GUSregb(Jumper_2xB) = (GUSbyte) data; - break; - default:; - } - break; - case 0x20C: /* SB2xCd */ - if (GUSregb(GUS45TimerCtrl) & 0x20) - { - GUSregb(TimerStatus2x8) |= 0x10; /* SB IRQ enabled? -> set 2xCIRQ bit */ - GUSregb(IRQStatReg2x6) = 0x10; - GUS_irqrequest(state, state->gusirq, 1); - } - case 0x20D: /* SB2xCd no IRQ */ - GUSregb(SB2xCd) = (GUSbyte) data; - break; - case 0x20E: /* SB2xE */ - GUSregb(SB2xE) = (GUSbyte) data; - break; - case 0x20F: - GUSregb(RegCtrl_2xF) = (GUSbyte) data; - break; /* CtrlReg2xF */ - case 0x302: /* VoiceSelReg */ - GUSregb(VoiceSelReg3x2) = (GUSbyte) data; - break; - case 0x303: /* FunkSelReg */ - GUSregb(FunkSelReg3x3) = (GUSbyte) data; - if ((GUSbyte) data == 0x8f) /* set irqstatreg, get voicereg and clear IRQ */ - { - int voice; - if (GUSregd(voicewavetableirq)) /* WavetableIRQ */ - { - for (voice = 0; voice < 31; voice++) - { - if (GUSregd(voicewavetableirq) & (1 << voice)) - { - GUSregd(voicewavetableirq) ^= (1 << voice); /* clear IRQ bit */ - GUSregb(voice << 5) &= 0x7f; /* clear voice reg irq bit */ - if (!GUSregd(voicewavetableirq)) - GUSregb(IRQStatReg2x6) &= 0xdf; - if (!GUSregb(IRQStatReg2x6)) - GUS_irqclear(state, state->gusirq); - GUSregb(SynVoiceIRQ8f) = voice | 0x60; /* (bit==0 => IRQ wartend) */ - return; - } - } - } - else if (GUSregd(voicevolrampirq)) /* VolRamp IRQ */ - { - for (voice = 0; voice < 31; voice++) - { - if (GUSregd(voicevolrampirq) & (1 << voice)) - { - GUSregd(voicevolrampirq) ^= (1 << voice); /* clear IRQ bit */ - GUSregb((voice << 5) + VSRVolRampControl) &= 0x7f; /* clear voice volume reg irq bit */ - if (!GUSregd(voicevolrampirq)) - GUSregb(IRQStatReg2x6) &= 0xbf; - if (!GUSregb(IRQStatReg2x6)) - GUS_irqclear(state, state->gusirq); - GUSregb(SynVoiceIRQ8f) = voice | 0x80; /* (bit==0 => IRQ wartend) */ - return; - } - } - } - GUSregb(SynVoiceIRQ8f) = 0xe8; /* kein IRQ wartet */ - } - break; - case 0x304: - case 0x305: - { - GUSword writedata = (GUSword) data; - GUSword readmask = 0x0000; - if (size == 1) - { - readmask = 0xff00; - writedata &= 0xff; - if ((port & 0xff0f) == 0x305) - { - writedata = (GUSword) (writedata << 8); - readmask = 0x00ff; - } - } - switch (GUSregb(FunkSelReg3x3)) - { - /* voice specific functions */ - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - { - int offset; - if (!(GUSregb(GUS4cReset) & 0x01)) - break; /* reset flag active? */ - offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f); - offset += (GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Funktion*2 */ - GUSregw(offset) = (GUSword) ((GUSregw(offset) & readmask) | writedata); - } - break; - /* voice unspecific functions */ - case 0x0e: /* NumVoices */ - GUSregb(NumVoices) = (GUSbyte) data; - break; - /* case 0x0f: */ /* read only */ - /* common functions */ - case 0x41: /* DramDMAContrReg */ - GUSregb(GUS41DMACtrl) = (GUSbyte) data; - if (data & 0x01) - GUS_dmarequest(state); - break; - case 0x42: /* DramDMAmemPosReg */ - GUSregw(GUS42DMAStart) = (GUSregw(GUS42DMAStart) & readmask) | writedata; - GUSregb(GUS50DMAHigh) &= 0xf; /* compatibility stuff... */ - break; - case 0x43: /* DRAMaddrLo */ - GUSregd(GUSDRAMPOS24bit) = - (GUSregd(GUSDRAMPOS24bit) & (readmask | 0xff0000)) | writedata; - break; - case 0x44: /* DRAMaddrHi */ - GUSregd(GUSDRAMPOS24bit) = - (GUSregd(GUSDRAMPOS24bit) & 0xffff) | ((data & 0x0f) << 16); - break; - case 0x45: /* TCtrlReg */ - GUSregb(GUS45TimerCtrl) = (GUSbyte) data; - if (!(data & 0x20)) - GUSregb(TimerStatus2x8) &= 0xe7; /* sb IRQ dis? -> clear 2x8/2xC sb IRQ flags */ - if (!(data & 0x02)) - GUSregb(TimerStatus2x8) &= 0xfe; /* adlib data IRQ dis? -> clear 2x8 adlib IRQ flag */ - if (!(GUSregb(TimerStatus2x8) & 0x19)) - GUSregb(IRQStatReg2x6) &= 0xef; /* 0xe6; $$clear IRQ if both IRQ bits are inactive or cleared */ - /* catch up delayed timer IRQs: */ - if ((GUSregw(TimerIRQs) > 1) && (GUSregb(TimerDataReg2x9) & 3)) - { - if (GUSregb(TimerDataReg2x9) & 1) /* start timer 1 (80us decrement rate) */ - { - if (!(GUSregb(TimerDataReg2x9) & 0x40)) - GUSregb(TimerStatus2x8) |= 0xc0; /* maskable bits */ - if (data & 4) /* timer1 irq enable */ - { - GUSregb(TimerStatus2x8) |= 4; /* nonmaskable bit */ - GUSregb(IRQStatReg2x6) |= 4; /* timer 1 irq pending */ - } - } - if (GUSregb(TimerDataReg2x9) & 2) /* start timer 2 (320us decrement rate) */ - { - if (!(GUSregb(TimerDataReg2x9) & 0x20)) - GUSregb(TimerStatus2x8) |= 0xa0; /* maskable bits */ - if (data & 8) /* timer2 irq enable */ - { - GUSregb(TimerStatus2x8) |= 2; /* nonmaskable bit */ - GUSregb(IRQStatReg2x6) |= 8; /* timer 2 irq pending */ - } - } - GUSregw(TimerIRQs)--; - if (GUSregw(BusyTimerIRQs) > 1) - GUSregw(BusyTimerIRQs)--; - else - GUSregw(BusyTimerIRQs) = - GUS_irqrequest(state, state->gusirq, GUSregw(TimerIRQs)); - } - else - GUSregw(TimerIRQs) = 0; - - if (!(data & 0x04)) - { - GUSregb(TimerStatus2x8) &= 0xfb; /* clear non-maskable timer1 bit */ - GUSregb(IRQStatReg2x6) &= 0xfb; - } - if (!(data & 0x08)) - { - GUSregb(TimerStatus2x8) &= 0xfd; /* clear non-maskable timer2 bit */ - GUSregb(IRQStatReg2x6) &= 0xf7; - } - if (!GUSregb(IRQStatReg2x6)) - GUS_irqclear(state, state->gusirq); - break; - case 0x46: /* Counter1 */ - GUSregb(GUS46Counter1) = (GUSbyte) data; - break; - case 0x47: /* Counter2 */ - GUSregb(GUS47Counter2) = (GUSbyte) data; - break; - /* case 0x48: */ /* sampling freq reg not emulated (same as interwave) */ - case 0x49: /* SampCtrlReg */ - GUSregb(GUS49SampCtrl) = (GUSbyte) data; - break; - /* case 0x4b: */ /* joystick trim not emulated */ - case 0x4c: /* GUSreset */ - GUSregb(GUS4cReset) = (GUSbyte) data; - if (!(GUSregb(GUS4cReset) & 1)) /* reset... */ - { - GUSregd(voicewavetableirq) = 0; - GUSregd(voicevolrampirq) = 0; - GUSregw(TimerIRQs) = 0; - GUSregw(BusyTimerIRQs) = 0; - GUSregb(NumVoices) = 0xcd; - GUSregb(IRQStatReg2x6) = 0; - GUSregb(TimerStatus2x8) = 0; - GUSregb(AdLibData2x9) = 0; - GUSregb(TimerDataReg2x9) = 0; - GUSregb(GUS41DMACtrl) = 0; - GUSregb(GUS45TimerCtrl) = 0; - GUSregb(GUS49SampCtrl) = 0; - GUSregb(GUS4cReset) &= 0xf9; /* clear IRQ and DAC enable bits */ - GUS_irqclear(state, state->gusirq); - } - /* IRQ enable bit checked elsewhere */ - /* EnableDAC bit may be used by external callers */ - break; - } - } - break; - case 0x307: /* DRAMaccess */ - { - GUSbyte *adr; - adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff); - *adr = (GUSbyte) data; - } - break; - } -} - -/* Attention when breaking up a single DMA transfer to multiple ones: - * it may lead to multiple terminal count interrupts and broken transfers: - * - * 1. Whenever you transfer a piece of data, the gusemu callback is invoked - * 2. The callback may generate a TC irq (if the register was set up to do so) - * 3. The irq may result in the program using the GUS to reprogram the GUS - * - * Some programs also decide to upload by just checking if TC occurs - * (via interrupt or a cleared GUS dma flag) - * and then start the next transfer, without checking DMA state - * - * Thus: Always make sure to set the TC flag correctly! - * - * Note that the genuine GUS had a granularity of 16 bytes/words for low/high DMA - * while later cards had atomic granularity provided by an additional GUS50DMAHigh register - * GUSemu also uses this register to support byte-granular transfers for better compatibility - * with emulators other than GUSemu32 - */ - -void gus_dma_transferdata(GUSEmuState * state, char *dma_addr, unsigned int count, int TC) -{ - /* this function gets called by the callback function as soon as a DMA transfer is about to start - * dma_addr is a translated address within accessible memory, not the physical one, - * count is (real dma count register)+1 - * note that the amount of bytes transferred is fully determined by values in the DMA registers - * do not forget to update DMA states after transferring the entire block: - * DREQ cleared & TC asserted after the _whole_ transfer */ - - char *srcaddr; - char *destaddr; - char msbmask = 0; - GUSbyte *gusptr; - gusptr = state->gusdatapos; - - srcaddr = dma_addr; /* system memory address */ - { - int offset = (GUSregw(GUS42DMAStart) << 4) + (GUSregb(GUS50DMAHigh) & 0xf); - if (state->gusdma >= 4) - offset = (offset & 0xc0000) + (2 * (offset & 0x1fff0)); /* 16 bit address translation */ - destaddr = (char *) state->himemaddr + offset; /* wavetable RAM address */ - } - - GUSregw(GUS42DMAStart) += (GUSword) (count >> 4); /* ToDo: add 16bit GUS page limit? */ - GUSregb(GUS50DMAHigh) = (GUSbyte) ((count + GUSregb(GUS50DMAHigh)) & 0xf); /* ToDo: add 16bit GUS page limit? */ - - if (GUSregb(GUS41DMACtrl) & 0x02) /* direction, 0 := sysram->gusram */ - { - char *tmpaddr = destaddr; - destaddr = srcaddr; - srcaddr = tmpaddr; - } - - if ((GUSregb(GUS41DMACtrl) & 0x80) && (!(GUSregb(GUS41DMACtrl) & 0x02))) - msbmask = (const char) 0x80; /* invert MSB */ - for (; count > 0; count--) - { - if (GUSregb(GUS41DMACtrl) & 0x40) - *(destaddr++) = *(srcaddr++); /* 16 bit lobyte */ - else - *(destaddr++) = (msbmask ^ (*(srcaddr++))); /* 8 bit */ - if (state->gusdma >= 4) - *(destaddr++) = (msbmask ^ (*(srcaddr++))); /* 16 bit hibyte */ - } - - if (TC) - { - (GUSregb(GUS41DMACtrl)) &= 0xfe; /* clear DMA request bit */ - if (GUSregb(GUS41DMACtrl) & 0x20) /* DMA terminal count IRQ */ - { - GUSregb(IRQStatReg2x6) |= 0x80; - GUS_irqrequest(state, state->gusirq, 1); - } - } -} diff --git a/qemu/hw/audio/gusemu_mixer.c b/qemu/hw/audio/gusemu_mixer.c deleted file mode 100644 index 701e8fb0e..000000000 --- a/qemu/hw/audio/gusemu_mixer.c +++ /dev/null @@ -1,241 +0,0 @@ -/* - * GUSEMU32 - mixing engine (similar to Interwave GF1 compatibility) - * - * Copyright (C) 2000-2007 Tibor "TS" Schütz - * - * 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 "gusemu.h" -#include "gustate.h" - -#define GUSregb(position) (* (gusptr+(position))) -#define GUSregw(position) (*(GUSword *) (gusptr+(position))) -#define GUSregd(position) (*(GUSdword *)(gusptr+(position))) - -#define GUSvoice(position) (*(GUSword *)(voiceptr+(position))) - -/* samples are always 16bit stereo (4 bytes each, first right then left interleaved) */ -void gus_mixvoices(GUSEmuState * state, unsigned int playback_freq, unsigned int numsamples, - GUSsample *bufferpos) -{ - /* note that byte registers are stored in the upper half of each voice register! */ - GUSbyte *gusptr; - int Voice; - GUSword *voiceptr; - - unsigned int count; - for (count = 0; count < numsamples * 2; count++) - *(bufferpos + count) = 0; /* clear */ - - gusptr = state->gusdatapos; - voiceptr = (GUSword *) gusptr; - if (!(GUSregb(GUS4cReset) & 0x01)) /* reset flag active? */ - return; - - for (Voice = 0; Voice <= (GUSregb(NumVoices) & 31); Voice++) - { - if (GUSvoice(wVSRControl) & 0x200) - GUSvoice(wVSRControl) |= 0x100; /* voice stop request */ - if (GUSvoice(wVSRVolRampControl) & 0x200) - GUSvoice(wVSRVolRampControl) |= 0x100; /* Volume ramp stop request */ - if (!(GUSvoice(wVSRControl) & GUSvoice(wVSRVolRampControl) & 0x100)) /* neither voice nor volume calculation active - save some time here ;) */ - { - unsigned int sample; - - unsigned int LoopStart = (GUSvoice(wVSRLoopStartHi) << 16) | GUSvoice(wVSRLoopStartLo); /* 23.9 format */ - unsigned int LoopEnd = (GUSvoice(wVSRLoopEndHi) << 16) | GUSvoice(wVSRLoopEndLo); /* 23.9 format */ - unsigned int CurrPos = (GUSvoice(wVSRCurrPosHi) << 16) | GUSvoice(wVSRCurrPosLo); /* 23.9 format */ - int VoiceIncrement = ((((unsigned long) GUSvoice(wVSRFreq) * 44100) / playback_freq) * (14 >> 1)) / - ((GUSregb(NumVoices) & 31) + 1); /* 6.10 increment/frame to 23.9 increment/sample */ - - int PanningPos = (GUSvoice(wVSRPanning) >> 8) & 0xf; - - unsigned int Volume32 = 32 * GUSvoice(wVSRCurrVol); /* 32 times larger than original gus for maintaining precision while ramping */ - unsigned int StartVol32 = (GUSvoice(wVSRVolRampStartVol) & 0xff00) * 32; - unsigned int EndVol32 = (GUSvoice(wVSRVolRampEndVol) & 0xff00) * 32; - int VolumeIncrement32 = (32 * 16 * (GUSvoice(wVSRVolRampRate) & 0x3f00) >> 8) >> ((((GUSvoice(wVSRVolRampRate) & 0xc000) >> 8) >> 6) * 3); /* including 1/8/64/512 volume speed divisor */ - VolumeIncrement32 = (((VolumeIncrement32 * 44100 / 2) / playback_freq) * 14) / ((GUSregb(NumVoices) & 31) + 1); /* adjust ramping speed to playback speed */ - - if (GUSvoice(wVSRControl) & 0x4000) - VoiceIncrement = -VoiceIncrement; /* reverse playback */ - if (GUSvoice(wVSRVolRampControl) & 0x4000) - VolumeIncrement32 = -VolumeIncrement32; /* reverse ramping */ - - for (sample = 0; sample < numsamples; sample++) - { - int sample1, sample2, Volume; - if (GUSvoice(wVSRControl) & 0x400) /* 16bit */ - { - int offset = ((CurrPos >> 9) & 0xc0000) + (((CurrPos >> 9) & 0x1ffff) << 1); - GUSchar *adr; - adr = (GUSchar *) state->himemaddr + offset; - sample1 = (*adr & 0xff) + (*(adr + 1) * 256); - sample2 = (*(adr + 2) & 0xff) + (*(adr + 2 + 1) * 256); - } - else /* 8bit */ - { - int offset = (CurrPos >> 9) & 0xfffff; - GUSchar *adr; - adr = (GUSchar *) state->himemaddr + offset; - sample1 = (*adr) * 256; - sample2 = (*(adr + 1)) * 256; - } - - Volume = ((((Volume32 >> (4 + 5)) & 0xff) + 256) << (Volume32 >> ((4 + 8) + 5))) / 512; /* semi-logarithmic volume, +5 due to additional precision */ - sample1 = (((sample1 * Volume) >> 16) * (512 - (CurrPos % 512))) / 512; - sample2 = (((sample2 * Volume) >> 16) * (CurrPos % 512)) / 512; - sample1 += sample2; - - if (!(GUSvoice(wVSRVolRampControl) & 0x100)) - { - Volume32 += VolumeIncrement32; - if ((GUSvoice(wVSRVolRampControl) & 0x4000) ? (Volume32 <= StartVol32) : (Volume32 >= EndVol32)) /* ramp up boundary cross */ - { - if (GUSvoice(wVSRVolRampControl) & 0x2000) - GUSvoice(wVSRVolRampControl) |= 0x8000; /* volramp IRQ enabled? -> IRQ wait flag */ - if (GUSvoice(wVSRVolRampControl) & 0x800) /* loop enabled */ - { - if (GUSvoice(wVSRVolRampControl) & 0x1000) /* bidir. loop */ - { - GUSvoice(wVSRVolRampControl) ^= 0x4000; /* toggle dir */ - VolumeIncrement32 = -VolumeIncrement32; - } - else - Volume32 = (GUSvoice(wVSRVolRampControl) & 0x4000) ? EndVol32 : StartVol32; /* unidir. loop ramp */ - } - else - { - GUSvoice(wVSRVolRampControl) |= 0x100; - Volume32 = - (GUSvoice(wVSRVolRampControl) & 0x4000) ? StartVol32 : EndVol32; - } - } - } - if ((GUSvoice(wVSRVolRampControl) & 0xa000) == 0xa000) /* volramp IRQ set and enabled? */ - { - GUSregd(voicevolrampirq) |= 1 << Voice; /* set irq slot */ - } - else - { - GUSregd(voicevolrampirq) &= (~(1 << Voice)); /* clear irq slot */ - GUSvoice(wVSRVolRampControl) &= 0x7f00; - } - - if (!(GUSvoice(wVSRControl) & 0x100)) - { - CurrPos += VoiceIncrement; - if ((GUSvoice(wVSRControl) & 0x4000) ? (CurrPos <= LoopStart) : (CurrPos >= LoopEnd)) /* playback boundary cross */ - { - if (GUSvoice(wVSRControl) & 0x2000) - GUSvoice(wVSRControl) |= 0x8000; /* voice IRQ enabled -> IRQ wait flag */ - if (GUSvoice(wVSRControl) & 0x800) /* loop enabled */ - { - if (GUSvoice(wVSRControl) & 0x1000) /* pingpong loop */ - { - GUSvoice(wVSRControl) ^= 0x4000; /* toggle dir */ - VoiceIncrement = -VoiceIncrement; - } - else - CurrPos = (GUSvoice(wVSRControl) & 0x4000) ? LoopEnd : LoopStart; /* unidir. loop */ - } - else if (!(GUSvoice(wVSRVolRampControl) & 0x400)) - GUSvoice(wVSRControl) |= 0x100; /* loop disabled, rollover check */ - } - } - if ((GUSvoice(wVSRControl) & 0xa000) == 0xa000) /* wavetable IRQ set and enabled? */ - { - GUSregd(voicewavetableirq) |= 1 << Voice; /* set irq slot */ - } - else - { - GUSregd(voicewavetableirq) &= (~(1 << Voice)); /* clear irq slot */ - GUSvoice(wVSRControl) &= 0x7f00; - } - - /* mix samples into buffer */ - *(bufferpos + 2 * sample) += (GUSsample) ((sample1 * PanningPos) >> 4); /* right */ - *(bufferpos + 2 * sample + 1) += (GUSsample) ((sample1 * (15 - PanningPos)) >> 4); /* left */ - } - /* write back voice and volume */ - GUSvoice(wVSRCurrVol) = Volume32 / 32; - GUSvoice(wVSRCurrPosHi) = CurrPos >> 16; - GUSvoice(wVSRCurrPosLo) = CurrPos & 0xffff; - } - voiceptr += 16; /* next voice */ - } -} - -void gus_irqgen(GUSEmuState * state, unsigned int elapsed_time) -/* time given in microseconds */ -{ - int requestedIRQs = 0; - GUSbyte *gusptr; - gusptr = state->gusdatapos; - if (GUSregb(TimerDataReg2x9) & 1) /* start timer 1 (80us decrement rate) */ - { - unsigned int timer1fraction = state->timer1fraction; - int newtimerirqs; - newtimerirqs = (elapsed_time + timer1fraction) / (80 * (256 - GUSregb(GUS46Counter1))); - state->timer1fraction = (elapsed_time + timer1fraction) % (80 * (256 - GUSregb(GUS46Counter1))); - if (newtimerirqs) - { - if (!(GUSregb(TimerDataReg2x9) & 0x40)) - GUSregb(TimerStatus2x8) |= 0xc0; /* maskable bits */ - if (GUSregb(GUS45TimerCtrl) & 4) /* timer1 irq enable */ - { - GUSregb(TimerStatus2x8) |= 4; /* nonmaskable bit */ - GUSregb(IRQStatReg2x6) |= 4; /* timer 1 irq pending */ - GUSregw(TimerIRQs) += newtimerirqs; - requestedIRQs += newtimerirqs; - } - } - } - if (GUSregb(TimerDataReg2x9) & 2) /* start timer 2 (320us decrement rate) */ - { - unsigned int timer2fraction = state->timer2fraction; - int newtimerirqs; - newtimerirqs = (elapsed_time + timer2fraction) / (320 * (256 - GUSregb(GUS47Counter2))); - state->timer2fraction = (elapsed_time + timer2fraction) % (320 * (256 - GUSregb(GUS47Counter2))); - if (newtimerirqs) - { - if (!(GUSregb(TimerDataReg2x9) & 0x20)) - GUSregb(TimerStatus2x8) |= 0xa0; /* maskable bits */ - if (GUSregb(GUS45TimerCtrl) & 8) /* timer2 irq enable */ - { - GUSregb(TimerStatus2x8) |= 2; /* nonmaskable bit */ - GUSregb(IRQStatReg2x6) |= 8; /* timer 2 irq pending */ - GUSregw(TimerIRQs) += newtimerirqs; - requestedIRQs += newtimerirqs; - } - } - } - if (GUSregb(GUS4cReset) & 0x4) /* synth IRQ enable */ - { - if (GUSregd(voicewavetableirq)) - GUSregb(IRQStatReg2x6) |= 0x20; - if (GUSregd(voicevolrampirq)) - GUSregb(IRQStatReg2x6) |= 0x40; - } - if ((!requestedIRQs) && GUSregb(IRQStatReg2x6)) - requestedIRQs++; - if (GUSregb(IRQStatReg2x6)) - GUSregw(BusyTimerIRQs) = GUS_irqrequest(state, state->gusirq, requestedIRQs); -} diff --git a/qemu/hw/audio/gustate.h b/qemu/hw/audio/gustate.h deleted file mode 100644 index ece903abb..000000000 --- a/qemu/hw/audio/gustate.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * GUSEMU32 - persistent GUS register state - * - * Copyright (C) 2000-2007 Tibor "TS" Schütz - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef GUSTATE_H -#define GUSTATE_H - -/*state block offset*/ -#define gusdata (0) - -/* data stored using this structure is in host byte order! */ - -/*access type*/ -#define PortRead (0) -#define PortWrite (1) - -#define Port8Bitacc (0) -#define Port16Bitacc (1) - -/*voice register offsets (in bytes)*/ -#define VSRegs (0) -#define VSRControl (0) -#define VSRegsEnd (VSRControl+VSRegs + 32*(16*2)) -#define VSRFreq (2) -#define VSRLoopStartHi (4) -#define VSRLoopStartLo (6) -#define VSRLoopEndHi (8) -#define VSRLoopEndLo (10) -#define VSRVolRampRate (12) -#define VSRVolRampStartVol (14) -#define VSRVolRampEndVol (16) -#define VSRCurrVol (18) -#define VSRCurrPosHi (20) -#define VSRCurrPosLo (22) -#define VSRPanning (24) -#define VSRVolRampControl (26) - -/*voice register offsets (in words)*/ -#define wVSRegs (0) -#define wVSRControl (0) -#define wVSRegsEnd (wVSRControl+wVSRegs + 32*(16)) -#define wVSRFreq (1) -#define wVSRLoopStartHi (2) -#define wVSRLoopStartLo (3) -#define wVSRLoopEndHi (4) -#define wVSRLoopEndLo (5) -#define wVSRVolRampRate (6) -#define wVSRVolRampStartVol (7) -#define wVSRVolRampEndVol (8) -#define wVSRCurrVol (9) -#define wVSRCurrPosHi (10) -#define wVSRCurrPosLo (11) -#define wVSRPanning (12) -#define wVSRVolRampControl (13) - -/*GUS register state block: 32 voices, padding filled with remaining registers*/ -#define DataRegLoByte3x4 (VSRVolRampControl+2) -#define DataRegWord3x4 (DataRegLoByte3x4) -#define DataRegHiByte3x5 (VSRVolRampControl+2 +1) -#define DMA_2xB (VSRVolRampControl+2+2) -#define IRQ_2xB (VSRVolRampControl+2+3) - -#define RegCtrl_2xF (VSRVolRampControl+2+(16*2)) -#define Jumper_2xB (VSRVolRampControl+2+(16*2)+1) -#define GUS42DMAStart (VSRVolRampControl+2+(16*2)+2) - -#define GUS43DRAMIOlo (VSRVolRampControl+2+(16*2)*2) -#define GUSDRAMPOS24bit (GUS43DRAMIOlo) -#define GUS44DRAMIOhi (VSRVolRampControl+2+(16*2)*2+2) - -#define voicewavetableirq (VSRVolRampControl+2+(16*2)*3) /* voice IRQ pseudoqueue: 1 bit per voice */ - -#define voicevolrampirq (VSRVolRampControl+2+(16*2)*4) /* voice IRQ pseudoqueue: 1 bit per voice */ - -#define startvoices (VSRVolRampControl+2+(16*2)*5) /* statistics / optimizations */ - -#define IRQStatReg2x6 (VSRVolRampControl+2+(16*2)*6) -#define TimerStatus2x8 (VSRVolRampControl+2+(16*2)*6+1) -#define TimerDataReg2x9 (VSRVolRampControl+2+(16*2)*6+2) -#define MixerCtrlReg2x0 (VSRVolRampControl+2+(16*2)*6+3) - -#define VoiceSelReg3x2 (VSRVolRampControl+2+(16*2)*7) -#define FunkSelReg3x3 (VSRVolRampControl+2+(16*2)*7+1) -#define AdLibStatus2x8 (VSRVolRampControl+2+(16*2)*7+2) -#define StatRead_2xF (VSRVolRampControl+2+(16*2)*7+3) - -#define GUS48SampSpeed (VSRVolRampControl+2+(16*2)*8) -#define GUS41DMACtrl (VSRVolRampControl+2+(16*2)*8+1) -#define GUS45TimerCtrl (VSRVolRampControl+2+(16*2)*8+2) -#define GUS46Counter1 (VSRVolRampControl+2+(16*2)*8+3) - -#define GUS47Counter2 (VSRVolRampControl+2+(16*2)*9) -#define GUS49SampCtrl (VSRVolRampControl+2+(16*2)*9+1) -#define GUS4cReset (VSRVolRampControl+2+(16*2)*9+2) -#define NumVoices (VSRVolRampControl+2+(16*2)*9+3) - -#define TimerIRQs (VSRVolRampControl+2+(16*2)*10) /* delayed IRQ, statistics */ -#define BusyTimerIRQs (VSRVolRampControl+2+(16*2)*10+2) /* delayed IRQ, statistics */ - -#define AdLibCommand2xA (VSRVolRampControl+2+(16*2)*11) -#define AdLibData2x9 (VSRVolRampControl+2+(16*2)*11+1) -#define SB2xCd (VSRVolRampControl+2+(16*2)*11+2) -#define SB2xE (VSRVolRampControl+2+(16*2)*11+3) - -#define SynVoiceIRQ8f (VSRVolRampControl+2+(16*2)*12) -#define GUS50DMAHigh (VSRVolRampControl+2+(16*2)*12+1) - -#define portaccesses (VSRegsEnd) /* statistics / suspend mode */ - -#define gusdataend (VSRegsEnd+4) - -#endif /* gustate.h */ diff --git a/qemu/hw/audio/hda-codec-common.h b/qemu/hw/audio/hda-codec-common.h deleted file mode 100644 index b4fdb51e8..000000000 --- a/qemu/hw/audio/hda-codec-common.h +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Common code to disable/enable mixer emulation at run time - * - * Copyright (C) 2013 Red Hat, Inc. - * - * Written by Bandan Das - * with important bits picked up from hda-codec.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 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 . - */ - -/* - * HDA codec descriptions - */ - -#ifdef HDA_MIXER -#define QEMU_HDA_ID_OUTPUT ((QEMU_HDA_ID_VENDOR << 16) | 0x12) -#define QEMU_HDA_ID_DUPLEX ((QEMU_HDA_ID_VENDOR << 16) | 0x22) -#define QEMU_HDA_ID_MICRO ((QEMU_HDA_ID_VENDOR << 16) | 0x32) -#define QEMU_HDA_AMP_CAPS \ - (AC_AMPCAP_MUTE | \ - (QEMU_HDA_AMP_STEPS << AC_AMPCAP_OFFSET_SHIFT) | \ - (QEMU_HDA_AMP_STEPS << AC_AMPCAP_NUM_STEPS_SHIFT) | \ - (3 << AC_AMPCAP_STEP_SIZE_SHIFT)) -#else -#define QEMU_HDA_ID_OUTPUT ((QEMU_HDA_ID_VENDOR << 16) | 0x11) -#define QEMU_HDA_ID_DUPLEX ((QEMU_HDA_ID_VENDOR << 16) | 0x21) -#define QEMU_HDA_ID_MICRO ((QEMU_HDA_ID_VENDOR << 16) | 0x31) -#define QEMU_HDA_AMP_CAPS QEMU_HDA_AMP_NONE -#endif - - -/* common: audio output widget */ -static const desc_param glue(common_params_audio_dac_, PARAM)[] = { - { - .id = AC_PAR_AUDIO_WIDGET_CAP, - .val = ((AC_WID_AUD_OUT << AC_WCAP_TYPE_SHIFT) | - AC_WCAP_FORMAT_OVRD | - AC_WCAP_AMP_OVRD | - AC_WCAP_OUT_AMP | - AC_WCAP_STEREO), - },{ - .id = AC_PAR_PCM, - .val = QEMU_HDA_PCM_FORMATS, - },{ - .id = AC_PAR_STREAM, - .val = AC_SUPFMT_PCM, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_CAPS, - }, -}; - -/* common: audio input widget */ -static const desc_param glue(common_params_audio_adc_, PARAM)[] = { - { - .id = AC_PAR_AUDIO_WIDGET_CAP, - .val = ((AC_WID_AUD_IN << AC_WCAP_TYPE_SHIFT) | - AC_WCAP_CONN_LIST | - AC_WCAP_FORMAT_OVRD | - AC_WCAP_AMP_OVRD | - AC_WCAP_IN_AMP | - AC_WCAP_STEREO), - },{ - .id = AC_PAR_CONNLIST_LEN, - .val = 1, - },{ - .id = AC_PAR_PCM, - .val = QEMU_HDA_PCM_FORMATS, - },{ - .id = AC_PAR_STREAM, - .val = AC_SUPFMT_PCM, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_CAPS, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - }, -}; - -/* common: pin widget (line-out) */ -static const desc_param glue(common_params_audio_lineout_, PARAM)[] = { - { - .id = AC_PAR_AUDIO_WIDGET_CAP, - .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) | - AC_WCAP_CONN_LIST | - AC_WCAP_STEREO), - },{ - .id = AC_PAR_PIN_CAP, - .val = AC_PINCAP_OUT, - },{ - .id = AC_PAR_CONNLIST_LEN, - .val = 1, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - }, -}; - -/* common: pin widget (line-in) */ -static const desc_param glue(common_params_audio_linein_, PARAM)[] = { - { - .id = AC_PAR_AUDIO_WIDGET_CAP, - .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) | - AC_WCAP_STEREO), - },{ - .id = AC_PAR_PIN_CAP, - .val = AC_PINCAP_IN, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - }, -}; - -/* output: root node */ -static const desc_param glue(output_params_root_, PARAM)[] = { - { - .id = AC_PAR_VENDOR_ID, - .val = QEMU_HDA_ID_OUTPUT, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_OUTPUT, - },{ - .id = AC_PAR_REV_ID, - .val = 0x00100101, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00010001, - }, -}; - -/* output: audio function */ -static const desc_param glue(output_params_audio_func_, PARAM)[] = { - { - .id = AC_PAR_FUNCTION_TYPE, - .val = AC_GRP_AUDIO_FUNCTION, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_OUTPUT, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00020002, - },{ - .id = AC_PAR_PCM, - .val = QEMU_HDA_PCM_FORMATS, - },{ - .id = AC_PAR_STREAM, - .val = AC_SUPFMT_PCM, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_GPIO_CAP, - .val = 0, - },{ - .id = AC_PAR_AUDIO_FG_CAP, - .val = 0x00000808, - },{ - .id = AC_PAR_POWER_STATE, - .val = 0, - }, -}; - -/* output: nodes */ -static const desc_node glue(output_nodes_, PARAM)[] = { - { - .nid = AC_NODE_ROOT, - .name = "root", - .params = glue(output_params_root_, PARAM), - .nparams = ARRAY_SIZE(glue(output_params_root_, PARAM)), - },{ - .nid = 1, - .name = "func", - .params = glue(output_params_audio_func_, PARAM), - .nparams = ARRAY_SIZE(glue(output_params_audio_func_, PARAM)), - },{ - .nid = 2, - .name = "dac", - .params = glue(common_params_audio_dac_, PARAM), - .nparams = ARRAY_SIZE(glue(common_params_audio_dac_, PARAM)), - .stindex = 0, - },{ - .nid = 3, - .name = "out", - .params = glue(common_params_audio_lineout_, PARAM), - .nparams = ARRAY_SIZE(glue(common_params_audio_lineout_, PARAM)), - .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | - (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) | - (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | - (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | - 0x10), - .pinctl = AC_PINCTL_OUT_EN, - .conn = (uint32_t[]) { 2 }, - } -}; - -/* output: codec */ -static const desc_codec glue(output_, PARAM) = { - .name = "output", - .iid = QEMU_HDA_ID_OUTPUT, - .nodes = glue(output_nodes_, PARAM), - .nnodes = ARRAY_SIZE(glue(output_nodes_, PARAM)), -}; - -/* duplex: root node */ -static const desc_param glue(duplex_params_root_, PARAM)[] = { - { - .id = AC_PAR_VENDOR_ID, - .val = QEMU_HDA_ID_DUPLEX, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_DUPLEX, - },{ - .id = AC_PAR_REV_ID, - .val = 0x00100101, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00010001, - }, -}; - -/* duplex: audio function */ -static const desc_param glue(duplex_params_audio_func_, PARAM)[] = { - { - .id = AC_PAR_FUNCTION_TYPE, - .val = AC_GRP_AUDIO_FUNCTION, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_DUPLEX, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00020004, - },{ - .id = AC_PAR_PCM, - .val = QEMU_HDA_PCM_FORMATS, - },{ - .id = AC_PAR_STREAM, - .val = AC_SUPFMT_PCM, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_GPIO_CAP, - .val = 0, - },{ - .id = AC_PAR_AUDIO_FG_CAP, - .val = 0x00000808, - },{ - .id = AC_PAR_POWER_STATE, - .val = 0, - }, -}; - -/* duplex: nodes */ -static const desc_node glue(duplex_nodes_, PARAM)[] = { - { - .nid = AC_NODE_ROOT, - .name = "root", - .params = glue(duplex_params_root_, PARAM), - .nparams = ARRAY_SIZE(glue(duplex_params_root_, PARAM)), - },{ - .nid = 1, - .name = "func", - .params = glue(duplex_params_audio_func_, PARAM), - .nparams = ARRAY_SIZE(glue(duplex_params_audio_func_, PARAM)), - },{ - .nid = 2, - .name = "dac", - .params = glue(common_params_audio_dac_, PARAM), - .nparams = ARRAY_SIZE(glue(common_params_audio_dac_, PARAM)), - .stindex = 0, - },{ - .nid = 3, - .name = "out", - .params = glue(common_params_audio_lineout_, PARAM), - .nparams = ARRAY_SIZE(glue(common_params_audio_lineout_, PARAM)), - .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | - (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) | - (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | - (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | - 0x10), - .pinctl = AC_PINCTL_OUT_EN, - .conn = (uint32_t[]) { 2 }, - },{ - .nid = 4, - .name = "adc", - .params = glue(common_params_audio_adc_, PARAM), - .nparams = ARRAY_SIZE(glue(common_params_audio_adc_, PARAM)), - .stindex = 1, - .conn = (uint32_t[]) { 5 }, - },{ - .nid = 5, - .name = "in", - .params = glue(common_params_audio_linein_, PARAM), - .nparams = ARRAY_SIZE(glue(common_params_audio_linein_, PARAM)), - .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | - (AC_JACK_LINE_IN << AC_DEFCFG_DEVICE_SHIFT) | - (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | - (AC_JACK_COLOR_RED << AC_DEFCFG_COLOR_SHIFT) | - 0x20), - .pinctl = AC_PINCTL_IN_EN, - } -}; - -/* duplex: codec */ -static const desc_codec glue(duplex_, PARAM) = { - .name = "duplex", - .iid = QEMU_HDA_ID_DUPLEX, - .nodes = glue(duplex_nodes_, PARAM), - .nnodes = ARRAY_SIZE(glue(duplex_nodes_, PARAM)), -}; - -/* micro: root node */ -static const desc_param glue(micro_params_root_, PARAM)[] = { - { - .id = AC_PAR_VENDOR_ID, - .val = QEMU_HDA_ID_MICRO, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_MICRO, - },{ - .id = AC_PAR_REV_ID, - .val = 0x00100101, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00010001, - }, -}; - -/* micro: audio function */ -static const desc_param glue(micro_params_audio_func_, PARAM)[] = { - { - .id = AC_PAR_FUNCTION_TYPE, - .val = AC_GRP_AUDIO_FUNCTION, - },{ - .id = AC_PAR_SUBSYSTEM_ID, - .val = QEMU_HDA_ID_MICRO, - },{ - .id = AC_PAR_NODE_COUNT, - .val = 0x00020004, - },{ - .id = AC_PAR_PCM, - .val = QEMU_HDA_PCM_FORMATS, - },{ - .id = AC_PAR_STREAM, - .val = AC_SUPFMT_PCM, - },{ - .id = AC_PAR_AMP_IN_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_AMP_OUT_CAP, - .val = QEMU_HDA_AMP_NONE, - },{ - .id = AC_PAR_GPIO_CAP, - .val = 0, - },{ - .id = AC_PAR_AUDIO_FG_CAP, - .val = 0x00000808, - },{ - .id = AC_PAR_POWER_STATE, - .val = 0, - }, -}; - -/* micro: nodes */ -static const desc_node glue(micro_nodes_, PARAM)[] = { - { - .nid = AC_NODE_ROOT, - .name = "root", - .params = glue(micro_params_root_, PARAM), - .nparams = ARRAY_SIZE(glue(micro_params_root_, PARAM)), - },{ - .nid = 1, - .name = "func", - .params = glue(micro_params_audio_func_, PARAM), - .nparams = ARRAY_SIZE(glue(micro_params_audio_func_, PARAM)), - },{ - .nid = 2, - .name = "dac", - .params = glue(common_params_audio_dac_, PARAM), - .nparams = ARRAY_SIZE(glue(common_params_audio_dac_, PARAM)), - .stindex = 0, - },{ - .nid = 3, - .name = "out", - .params = glue(common_params_audio_lineout_, PARAM), - .nparams = ARRAY_SIZE(glue(common_params_audio_lineout_, PARAM)), - .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | - (AC_JACK_SPEAKER << AC_DEFCFG_DEVICE_SHIFT) | - (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | - (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | - 0x10), - .pinctl = AC_PINCTL_OUT_EN, - .conn = (uint32_t[]) { 2 }, - },{ - .nid = 4, - .name = "adc", - .params = glue(common_params_audio_adc_, PARAM), - .nparams = ARRAY_SIZE(glue(common_params_audio_adc_, PARAM)), - .stindex = 1, - .conn = (uint32_t[]) { 5 }, - },{ - .nid = 5, - .name = "in", - .params = glue(common_params_audio_linein_, PARAM), - .nparams = ARRAY_SIZE(glue(common_params_audio_linein_, PARAM)), - .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | - (AC_JACK_MIC_IN << AC_DEFCFG_DEVICE_SHIFT) | - (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | - (AC_JACK_COLOR_RED << AC_DEFCFG_COLOR_SHIFT) | - 0x20), - .pinctl = AC_PINCTL_IN_EN, - } -}; - -/* micro: codec */ -static const desc_codec glue(micro_, PARAM) = { - .name = "micro", - .iid = QEMU_HDA_ID_MICRO, - .nodes = glue(micro_nodes_, PARAM), - .nnodes = ARRAY_SIZE(glue(micro_nodes_, PARAM)), -}; - -#undef PARAM -#undef HDA_MIXER -#undef QEMU_HDA_ID_OUTPUT -#undef QEMU_HDA_ID_DUPLEX -#undef QEMU_HDA_ID_MICRO -#undef QEMU_HDA_AMP_CAPS diff --git a/qemu/hw/audio/hda-codec.c b/qemu/hw/audio/hda-codec.c deleted file mode 100644 index 52d4640e6..000000000 --- a/qemu/hw/audio/hda-codec.c +++ /dev/null @@ -1,732 +0,0 @@ -/* - * Copyright (C) 2010 Red Hat, Inc. - * - * written by Gerd Hoffmann - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "intel-hda.h" -#include "intel-hda-defs.h" -#include "audio/audio.h" - -/* -------------------------------------------------------------------------- */ - -typedef struct desc_param { - uint32_t id; - uint32_t val; -} desc_param; - -typedef struct desc_node { - uint32_t nid; - const char *name; - const desc_param *params; - uint32_t nparams; - uint32_t config; - uint32_t pinctl; - uint32_t *conn; - uint32_t stindex; -} desc_node; - -typedef struct desc_codec { - const char *name; - uint32_t iid; - const desc_node *nodes; - uint32_t nnodes; -} desc_codec; - -static const desc_param* hda_codec_find_param(const desc_node *node, uint32_t id) -{ - int i; - - for (i = 0; i < node->nparams; i++) { - if (node->params[i].id == id) { - return &node->params[i]; - } - } - return NULL; -} - -static const desc_node* hda_codec_find_node(const desc_codec *codec, uint32_t nid) -{ - int i; - - for (i = 0; i < codec->nnodes; i++) { - if (codec->nodes[i].nid == nid) { - return &codec->nodes[i]; - } - } - return NULL; -} - -static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as) -{ - if (format & AC_FMT_TYPE_NON_PCM) { - return; - } - - as->freq = (format & AC_FMT_BASE_44K) ? 44100 : 48000; - - switch ((format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT) { - case 1: as->freq *= 2; break; - case 2: as->freq *= 3; break; - case 3: as->freq *= 4; break; - } - - switch ((format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT) { - case 1: as->freq /= 2; break; - case 2: as->freq /= 3; break; - case 3: as->freq /= 4; break; - case 4: as->freq /= 5; break; - case 5: as->freq /= 6; break; - case 6: as->freq /= 7; break; - case 7: as->freq /= 8; break; - } - - switch (format & AC_FMT_BITS_MASK) { - case AC_FMT_BITS_8: as->fmt = AUD_FMT_S8; break; - case AC_FMT_BITS_16: as->fmt = AUD_FMT_S16; break; - case AC_FMT_BITS_32: as->fmt = AUD_FMT_S32; break; - } - - as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1; -} - -/* -------------------------------------------------------------------------- */ -/* - * HDA codec descriptions - */ - -/* some defines */ - -#define QEMU_HDA_ID_VENDOR 0x1af4 -#define QEMU_HDA_PCM_FORMATS (AC_SUPPCM_BITS_16 | \ - 0x1fc /* 16 -> 96 kHz */) -#define QEMU_HDA_AMP_NONE (0) -#define QEMU_HDA_AMP_STEPS 0x4a - -#define PARAM mixemu -#define HDA_MIXER -#include "hda-codec-common.h" - -#define PARAM nomixemu -#include "hda-codec-common.h" - -/* -------------------------------------------------------------------------- */ - -static const char *fmt2name[] = { - [ AUD_FMT_U8 ] = "PCM-U8", - [ AUD_FMT_S8 ] = "PCM-S8", - [ AUD_FMT_U16 ] = "PCM-U16", - [ AUD_FMT_S16 ] = "PCM-S16", - [ AUD_FMT_U32 ] = "PCM-U32", - [ AUD_FMT_S32 ] = "PCM-S32", -}; - -typedef struct HDAAudioState HDAAudioState; -typedef struct HDAAudioStream HDAAudioStream; - -struct HDAAudioStream { - HDAAudioState *state; - const desc_node *node; - bool output, running; - uint32_t stream; - uint32_t channel; - uint32_t format; - uint32_t gain_left, gain_right; - bool mute_left, mute_right; - struct audsettings as; - union { - SWVoiceIn *in; - SWVoiceOut *out; - } voice; - uint8_t buf[HDA_BUFFER_SIZE]; - uint32_t bpos; -}; - -#define TYPE_HDA_AUDIO "hda-audio" -#define HDA_AUDIO(obj) OBJECT_CHECK(HDAAudioState, (obj), TYPE_HDA_AUDIO) - -struct HDAAudioState { - HDACodecDevice hda; - const char *name; - - QEMUSoundCard card; - const desc_codec *desc; - HDAAudioStream st[4]; - bool running_compat[16]; - bool running_real[2 * 16]; - - /* properties */ - uint32_t debug; - bool mixer; -}; - -static void hda_audio_input_cb(void *opaque, int avail) -{ - HDAAudioStream *st = opaque; - int recv = 0; - int len; - bool rc; - - while (avail - recv >= sizeof(st->buf)) { - if (st->bpos != sizeof(st->buf)) { - len = AUD_read(st->voice.in, st->buf + st->bpos, - sizeof(st->buf) - st->bpos); - st->bpos += len; - recv += len; - if (st->bpos != sizeof(st->buf)) { - break; - } - } - rc = hda_codec_xfer(&st->state->hda, st->stream, false, - st->buf, sizeof(st->buf)); - if (!rc) { - break; - } - st->bpos = 0; - } -} - -static void hda_audio_output_cb(void *opaque, int avail) -{ - HDAAudioStream *st = opaque; - int sent = 0; - int len; - bool rc; - - while (avail - sent >= sizeof(st->buf)) { - if (st->bpos == sizeof(st->buf)) { - rc = hda_codec_xfer(&st->state->hda, st->stream, true, - st->buf, sizeof(st->buf)); - if (!rc) { - break; - } - st->bpos = 0; - } - len = AUD_write(st->voice.out, st->buf + st->bpos, - sizeof(st->buf) - st->bpos); - st->bpos += len; - sent += len; - if (st->bpos != sizeof(st->buf)) { - break; - } - } -} - -static void hda_audio_set_running(HDAAudioStream *st, bool running) -{ - if (st->node == NULL) { - return; - } - if (st->running == running) { - return; - } - st->running = running; - dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name, - st->running ? "on" : "off", st->stream); - if (st->output) { - AUD_set_active_out(st->voice.out, st->running); - } else { - AUD_set_active_in(st->voice.in, st->running); - } -} - -static void hda_audio_set_amp(HDAAudioStream *st) -{ - bool muted; - uint32_t left, right; - - if (st->node == NULL) { - return; - } - - muted = st->mute_left && st->mute_right; - left = st->mute_left ? 0 : st->gain_left; - right = st->mute_right ? 0 : st->gain_right; - - left = left * 255 / QEMU_HDA_AMP_STEPS; - right = right * 255 / QEMU_HDA_AMP_STEPS; - - if (!st->state->mixer) { - return; - } - if (st->output) { - AUD_set_volume_out(st->voice.out, muted, left, right); - } else { - AUD_set_volume_in(st->voice.in, muted, left, right); - } -} - -static void hda_audio_setup(HDAAudioStream *st) -{ - if (st->node == NULL) { - return; - } - - dprint(st->state, 1, "%s: format: %d x %s @ %d Hz\n", - st->node->name, st->as.nchannels, - fmt2name[st->as.fmt], st->as.freq); - - if (st->output) { - st->voice.out = AUD_open_out(&st->state->card, st->voice.out, - st->node->name, st, - hda_audio_output_cb, &st->as); - } else { - st->voice.in = AUD_open_in(&st->state->card, st->voice.in, - st->node->name, st, - hda_audio_input_cb, &st->as); - } -} - -static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data) -{ - HDAAudioState *a = HDA_AUDIO(hda); - HDAAudioStream *st; - const desc_node *node = NULL; - const desc_param *param; - uint32_t verb, payload, response, count, shift; - - if ((data & 0x70000) == 0x70000) { - /* 12/8 id/payload */ - verb = (data >> 8) & 0xfff; - payload = data & 0x00ff; - } else { - /* 4/16 id/payload */ - verb = (data >> 8) & 0xf00; - payload = data & 0xffff; - } - - node = hda_codec_find_node(a->desc, nid); - if (node == NULL) { - goto fail; - } - dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n", - __FUNCTION__, nid, node->name, verb, payload); - - switch (verb) { - /* all nodes */ - case AC_VERB_PARAMETERS: - param = hda_codec_find_param(node, payload); - if (param == NULL) { - goto fail; - } - hda_codec_response(hda, true, param->val); - break; - case AC_VERB_GET_SUBSYSTEM_ID: - hda_codec_response(hda, true, a->desc->iid); - break; - - /* all functions */ - case AC_VERB_GET_CONNECT_LIST: - param = hda_codec_find_param(node, AC_PAR_CONNLIST_LEN); - count = param ? param->val : 0; - response = 0; - shift = 0; - while (payload < count && shift < 32) { - response |= node->conn[payload] << shift; - payload++; - shift += 8; - } - hda_codec_response(hda, true, response); - break; - - /* pin widget */ - case AC_VERB_GET_CONFIG_DEFAULT: - hda_codec_response(hda, true, node->config); - break; - case AC_VERB_GET_PIN_WIDGET_CONTROL: - hda_codec_response(hda, true, node->pinctl); - break; - case AC_VERB_SET_PIN_WIDGET_CONTROL: - if (node->pinctl != payload) { - dprint(a, 1, "unhandled pin control bit\n"); - } - hda_codec_response(hda, true, 0); - break; - - /* audio in/out widget */ - case AC_VERB_SET_CHANNEL_STREAMID: - st = a->st + node->stindex; - if (st->node == NULL) { - goto fail; - } - hda_audio_set_running(st, false); - st->stream = (payload >> 4) & 0x0f; - st->channel = payload & 0x0f; - dprint(a, 2, "%s: stream %d, channel %d\n", - st->node->name, st->stream, st->channel); - hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]); - hda_codec_response(hda, true, 0); - break; - case AC_VERB_GET_CONV: - st = a->st + node->stindex; - if (st->node == NULL) { - goto fail; - } - response = st->stream << 4 | st->channel; - hda_codec_response(hda, true, response); - break; - case AC_VERB_SET_STREAM_FORMAT: - st = a->st + node->stindex; - if (st->node == NULL) { - goto fail; - } - st->format = payload; - hda_codec_parse_fmt(st->format, &st->as); - hda_audio_setup(st); - hda_codec_response(hda, true, 0); - break; - case AC_VERB_GET_STREAM_FORMAT: - st = a->st + node->stindex; - if (st->node == NULL) { - goto fail; - } - hda_codec_response(hda, true, st->format); - break; - case AC_VERB_GET_AMP_GAIN_MUTE: - st = a->st + node->stindex; - if (st->node == NULL) { - goto fail; - } - if (payload & AC_AMP_GET_LEFT) { - response = st->gain_left | (st->mute_left ? AC_AMP_MUTE : 0); - } else { - response = st->gain_right | (st->mute_right ? AC_AMP_MUTE : 0); - } - hda_codec_response(hda, true, response); - break; - case AC_VERB_SET_AMP_GAIN_MUTE: - st = a->st + node->stindex; - if (st->node == NULL) { - goto fail; - } - dprint(a, 1, "amp (%s): %s%s%s%s index %d gain %3d %s\n", - st->node->name, - (payload & AC_AMP_SET_OUTPUT) ? "o" : "-", - (payload & AC_AMP_SET_INPUT) ? "i" : "-", - (payload & AC_AMP_SET_LEFT) ? "l" : "-", - (payload & AC_AMP_SET_RIGHT) ? "r" : "-", - (payload & AC_AMP_SET_INDEX) >> AC_AMP_SET_INDEX_SHIFT, - (payload & AC_AMP_GAIN), - (payload & AC_AMP_MUTE) ? "muted" : ""); - if (payload & AC_AMP_SET_LEFT) { - st->gain_left = payload & AC_AMP_GAIN; - st->mute_left = payload & AC_AMP_MUTE; - } - if (payload & AC_AMP_SET_RIGHT) { - st->gain_right = payload & AC_AMP_GAIN; - st->mute_right = payload & AC_AMP_MUTE; - } - hda_audio_set_amp(st); - hda_codec_response(hda, true, 0); - break; - - /* not supported */ - case AC_VERB_SET_POWER_STATE: - case AC_VERB_GET_POWER_STATE: - case AC_VERB_GET_SDI_SELECT: - hda_codec_response(hda, true, 0); - break; - default: - goto fail; - } - return; - -fail: - dprint(a, 1, "%s: not handled: nid %d (%s), verb 0x%x, payload 0x%x\n", - __FUNCTION__, nid, node ? node->name : "?", verb, payload); - hda_codec_response(hda, true, 0); -} - -static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output) -{ - HDAAudioState *a = HDA_AUDIO(hda); - int s; - - a->running_compat[stnr] = running; - a->running_real[output * 16 + stnr] = running; - for (s = 0; s < ARRAY_SIZE(a->st); s++) { - if (a->st[s].node == NULL) { - continue; - } - if (a->st[s].output != output) { - continue; - } - if (a->st[s].stream != stnr) { - continue; - } - hda_audio_set_running(&a->st[s], running); - } -} - -static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc) -{ - HDAAudioState *a = HDA_AUDIO(hda); - HDAAudioStream *st; - const desc_node *node; - const desc_param *param; - uint32_t i, type; - - a->desc = desc; - a->name = object_get_typename(OBJECT(a)); - dprint(a, 1, "%s: cad %d\n", __FUNCTION__, a->hda.cad); - - AUD_register_card("hda", &a->card); - for (i = 0; i < a->desc->nnodes; i++) { - node = a->desc->nodes + i; - param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP); - if (param == NULL) { - continue; - } - type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; - switch (type) { - case AC_WID_AUD_OUT: - case AC_WID_AUD_IN: - assert(node->stindex < ARRAY_SIZE(a->st)); - st = a->st + node->stindex; - st->state = a; - st->node = node; - if (type == AC_WID_AUD_OUT) { - /* unmute output by default */ - st->gain_left = QEMU_HDA_AMP_STEPS; - st->gain_right = QEMU_HDA_AMP_STEPS; - st->bpos = sizeof(st->buf); - st->output = true; - } else { - st->output = false; - } - st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 | - (1 << AC_FMT_CHAN_SHIFT); - hda_codec_parse_fmt(st->format, &st->as); - hda_audio_setup(st); - break; - } - } - return 0; -} - -static int hda_audio_exit(HDACodecDevice *hda) -{ - HDAAudioState *a = HDA_AUDIO(hda); - HDAAudioStream *st; - int i; - - dprint(a, 1, "%s\n", __FUNCTION__); - for (i = 0; i < ARRAY_SIZE(a->st); i++) { - st = a->st + i; - if (st->node == NULL) { - continue; - } - if (st->output) { - AUD_close_out(&a->card, st->voice.out); - } else { - AUD_close_in(&a->card, st->voice.in); - } - } - AUD_remove_card(&a->card); - return 0; -} - -static int hda_audio_post_load(void *opaque, int version) -{ - HDAAudioState *a = opaque; - HDAAudioStream *st; - int i; - - dprint(a, 1, "%s\n", __FUNCTION__); - if (version == 1) { - /* assume running_compat[] is for output streams */ - for (i = 0; i < ARRAY_SIZE(a->running_compat); i++) - a->running_real[16 + i] = a->running_compat[i]; - } - - for (i = 0; i < ARRAY_SIZE(a->st); i++) { - st = a->st + i; - if (st->node == NULL) - continue; - hda_codec_parse_fmt(st->format, &st->as); - hda_audio_setup(st); - hda_audio_set_amp(st); - hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]); - } - return 0; -} - -static void hda_audio_reset(DeviceState *dev) -{ - HDAAudioState *a = HDA_AUDIO(dev); - HDAAudioStream *st; - int i; - - dprint(a, 1, "%s\n", __func__); - for (i = 0; i < ARRAY_SIZE(a->st); i++) { - st = a->st + i; - if (st->node != NULL) { - hda_audio_set_running(st, false); - } - } -} - -static const VMStateDescription vmstate_hda_audio_stream = { - .name = "hda-audio-stream", - .version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(stream, HDAAudioStream), - VMSTATE_UINT32(channel, HDAAudioStream), - VMSTATE_UINT32(format, HDAAudioStream), - VMSTATE_UINT32(gain_left, HDAAudioStream), - VMSTATE_UINT32(gain_right, HDAAudioStream), - VMSTATE_BOOL(mute_left, HDAAudioStream), - VMSTATE_BOOL(mute_right, HDAAudioStream), - VMSTATE_UINT32(bpos, HDAAudioStream), - VMSTATE_BUFFER(buf, HDAAudioStream), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_hda_audio = { - .name = "hda-audio", - .version_id = 2, - .post_load = hda_audio_post_load, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0, - vmstate_hda_audio_stream, - HDAAudioStream), - VMSTATE_BOOL_ARRAY(running_compat, HDAAudioState, 16), - VMSTATE_BOOL_ARRAY_V(running_real, HDAAudioState, 2 * 16, 2), - VMSTATE_END_OF_LIST() - } -}; - -static Property hda_audio_properties[] = { - DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), - DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static int hda_audio_init_output(HDACodecDevice *hda) -{ - HDAAudioState *a = HDA_AUDIO(hda); - - if (!a->mixer) { - return hda_audio_init(hda, &output_nomixemu); - } else { - return hda_audio_init(hda, &output_mixemu); - } -} - -static int hda_audio_init_duplex(HDACodecDevice *hda) -{ - HDAAudioState *a = HDA_AUDIO(hda); - - if (!a->mixer) { - return hda_audio_init(hda, &duplex_nomixemu); - } else { - return hda_audio_init(hda, &duplex_mixemu); - } -} - -static int hda_audio_init_micro(HDACodecDevice *hda) -{ - HDAAudioState *a = HDA_AUDIO(hda); - - if (!a->mixer) { - return hda_audio_init(hda, µ_nomixemu); - } else { - return hda_audio_init(hda, µ_mixemu); - } -} - -static void hda_audio_base_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); - - k->exit = hda_audio_exit; - k->command = hda_audio_command; - k->stream = hda_audio_stream; - set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - dc->reset = hda_audio_reset; - dc->vmsd = &vmstate_hda_audio; - dc->props = hda_audio_properties; -} - -static const TypeInfo hda_audio_info = { - .name = TYPE_HDA_AUDIO, - .parent = TYPE_HDA_CODEC_DEVICE, - .class_init = hda_audio_base_class_init, - .abstract = true, -}; - -static void hda_audio_output_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); - - k->init = hda_audio_init_output; - dc->desc = "HDA Audio Codec, output-only (line-out)"; -} - -static const TypeInfo hda_audio_output_info = { - .name = "hda-output", - .parent = TYPE_HDA_AUDIO, - .instance_size = sizeof(HDAAudioState), - .class_init = hda_audio_output_class_init, -}; - -static void hda_audio_duplex_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); - - k->init = hda_audio_init_duplex; - dc->desc = "HDA Audio Codec, duplex (line-out, line-in)"; -} - -static const TypeInfo hda_audio_duplex_info = { - .name = "hda-duplex", - .parent = TYPE_HDA_AUDIO, - .instance_size = sizeof(HDAAudioState), - .class_init = hda_audio_duplex_class_init, -}; - -static void hda_audio_micro_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); - - k->init = hda_audio_init_micro; - dc->desc = "HDA Audio Codec, duplex (speaker, microphone)"; -} - -static const TypeInfo hda_audio_micro_info = { - .name = "hda-micro", - .parent = TYPE_HDA_AUDIO, - .instance_size = sizeof(HDAAudioState), - .class_init = hda_audio_micro_class_init, -}; - -static void hda_audio_register_types(void) -{ - type_register_static(&hda_audio_info); - type_register_static(&hda_audio_output_info); - type_register_static(&hda_audio_duplex_info); - type_register_static(&hda_audio_micro_info); -} - -type_init(hda_audio_register_types) diff --git a/qemu/hw/audio/intel-hda-defs.h b/qemu/hw/audio/intel-hda-defs.h deleted file mode 100644 index 2e37e5b87..000000000 --- a/qemu/hw/audio/intel-hda-defs.h +++ /dev/null @@ -1,717 +0,0 @@ -#ifndef HW_INTEL_HDA_DEFS_H -#define HW_INTEL_HDA_DEFS_H - -/* qemu */ -#define HDA_BUFFER_SIZE 256 - -/* --------------------------------------------------------------------- */ -/* from linux/sound/pci/hda/hda_intel.c */ - -/* - * registers - */ -#define ICH6_REG_GCAP 0x00 -#define ICH6_GCAP_64OK (1 << 0) /* 64bit address support */ -#define ICH6_GCAP_NSDO (3 << 1) /* # of serial data out signals */ -#define ICH6_GCAP_BSS (31 << 3) /* # of bidirectional streams */ -#define ICH6_GCAP_ISS (15 << 8) /* # of input streams */ -#define ICH6_GCAP_OSS (15 << 12) /* # of output streams */ -#define ICH6_REG_VMIN 0x02 -#define ICH6_REG_VMAJ 0x03 -#define ICH6_REG_OUTPAY 0x04 -#define ICH6_REG_INPAY 0x06 -#define ICH6_REG_GCTL 0x08 -#define ICH6_GCTL_RESET (1 << 0) /* controller reset */ -#define ICH6_GCTL_FCNTRL (1 << 1) /* flush control */ -#define ICH6_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */ -#define ICH6_REG_WAKEEN 0x0c -#define ICH6_REG_STATESTS 0x0e -#define ICH6_REG_GSTS 0x10 -#define ICH6_GSTS_FSTS (1 << 1) /* flush status */ -#define ICH6_REG_INTCTL 0x20 -#define ICH6_REG_INTSTS 0x24 -#define ICH6_REG_WALLCLK 0x30 /* 24Mhz source */ -#define ICH6_REG_SYNC 0x34 -#define ICH6_REG_CORBLBASE 0x40 -#define ICH6_REG_CORBUBASE 0x44 -#define ICH6_REG_CORBWP 0x48 -#define ICH6_REG_CORBRP 0x4a -#define ICH6_CORBRP_RST (1 << 15) /* read pointer reset */ -#define ICH6_REG_CORBCTL 0x4c -#define ICH6_CORBCTL_RUN (1 << 1) /* enable DMA */ -#define ICH6_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */ -#define ICH6_REG_CORBSTS 0x4d -#define ICH6_CORBSTS_CMEI (1 << 0) /* memory error indication */ -#define ICH6_REG_CORBSIZE 0x4e - -#define ICH6_REG_RIRBLBASE 0x50 -#define ICH6_REG_RIRBUBASE 0x54 -#define ICH6_REG_RIRBWP 0x58 -#define ICH6_RIRBWP_RST (1 << 15) /* write pointer reset */ -#define ICH6_REG_RINTCNT 0x5a -#define ICH6_REG_RIRBCTL 0x5c -#define ICH6_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */ -#define ICH6_RBCTL_DMA_EN (1 << 1) /* enable DMA */ -#define ICH6_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */ -#define ICH6_REG_RIRBSTS 0x5d -#define ICH6_RBSTS_IRQ (1 << 0) /* response irq */ -#define ICH6_RBSTS_OVERRUN (1 << 2) /* overrun irq */ -#define ICH6_REG_RIRBSIZE 0x5e - -#define ICH6_REG_IC 0x60 -#define ICH6_REG_IR 0x64 -#define ICH6_REG_IRS 0x68 -#define ICH6_IRS_VALID (1<<1) -#define ICH6_IRS_BUSY (1<<0) - -#define ICH6_REG_DPLBASE 0x70 -#define ICH6_REG_DPUBASE 0x74 -#define ICH6_DPLBASE_ENABLE 0x1 /* Enable position buffer */ - -/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ -enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; - -/* stream register offsets from stream base */ -#define ICH6_REG_SD_CTL 0x00 -#define ICH6_REG_SD_STS 0x03 -#define ICH6_REG_SD_LPIB 0x04 -#define ICH6_REG_SD_CBL 0x08 -#define ICH6_REG_SD_LVI 0x0c -#define ICH6_REG_SD_FIFOW 0x0e -#define ICH6_REG_SD_FIFOSIZE 0x10 -#define ICH6_REG_SD_FORMAT 0x12 -#define ICH6_REG_SD_BDLPL 0x18 -#define ICH6_REG_SD_BDLPU 0x1c - -/* PCI space */ -#define ICH6_PCIREG_TCSEL 0x44 - -/* - * other constants - */ - -/* max number of SDs */ -/* ICH, ATI and VIA have 4 playback and 4 capture */ -#define ICH6_NUM_CAPTURE 4 -#define ICH6_NUM_PLAYBACK 4 - -/* ULI has 6 playback and 5 capture */ -#define ULI_NUM_CAPTURE 5 -#define ULI_NUM_PLAYBACK 6 - -/* ATI HDMI has 1 playback and 0 capture */ -#define ATIHDMI_NUM_CAPTURE 0 -#define ATIHDMI_NUM_PLAYBACK 1 - -/* TERA has 4 playback and 3 capture */ -#define TERA_NUM_CAPTURE 3 -#define TERA_NUM_PLAYBACK 4 - -/* this number is statically defined for simplicity */ -#define MAX_AZX_DEV 16 - -/* max number of fragments - we may use more if allocating more pages for BDL */ -#define BDL_SIZE 4096 -#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16) -#define AZX_MAX_FRAG 32 -/* max buffer size - no h/w limit, you can increase as you like */ -#define AZX_MAX_BUF_SIZE (1024*1024*1024) - -/* RIRB int mask: overrun[2], response[0] */ -#define RIRB_INT_RESPONSE 0x01 -#define RIRB_INT_OVERRUN 0x04 -#define RIRB_INT_MASK 0x05 - -/* STATESTS int mask: S3,SD2,SD1,SD0 */ -#define AZX_MAX_CODECS 8 -#define AZX_DEFAULT_CODECS 4 -#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1) - -/* SD_CTL bits */ -#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */ -#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */ -#define SD_CTL_STRIPE (3 << 16) /* stripe control */ -#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */ -#define SD_CTL_DIR (1 << 19) /* bi-directional stream */ -#define SD_CTL_STREAM_TAG_MASK (0xf << 20) -#define SD_CTL_STREAM_TAG_SHIFT 20 - -/* SD_CTL and SD_STS */ -#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */ -#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */ -#define SD_INT_COMPLETE 0x04 /* completion interrupt */ -#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\ - SD_INT_COMPLETE) - -/* SD_STS */ -#define SD_STS_FIFO_READY 0x20 /* FIFO ready */ - -/* INTCTL and INTSTS */ -#define ICH6_INT_ALL_STREAM 0xff /* all stream interrupts */ -#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */ -#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */ - -/* below are so far hardcoded - should read registers in future */ -#define ICH6_MAX_CORB_ENTRIES 256 -#define ICH6_MAX_RIRB_ENTRIES 256 - -/* position fix mode */ -enum { - POS_FIX_AUTO, - POS_FIX_LPIB, - POS_FIX_POSBUF, -}; - -/* Defines for ATI HD Audio support in SB450 south bridge */ -#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42 -#define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02 - -/* Defines for Nvidia HDA support */ -#define NVIDIA_HDA_TRANSREG_ADDR 0x4e -#define NVIDIA_HDA_ENABLE_COHBITS 0x0f -#define NVIDIA_HDA_ISTRM_COH 0x4d -#define NVIDIA_HDA_OSTRM_COH 0x4c -#define NVIDIA_HDA_ENABLE_COHBIT 0x01 - -/* Defines for Intel SCH HDA snoop control */ -#define INTEL_SCH_HDA_DEVC 0x78 -#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11) - -/* Define IN stream 0 FIFO size offset in VIA controller */ -#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90 -/* Define VIA HD Audio Device ID*/ -#define VIA_HDAC_DEVICE_ID 0x3288 - -/* HD Audio class code */ -#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 - -/* --------------------------------------------------------------------- */ -/* from linux/sound/pci/hda/hda_codec.h */ - -/* - * nodes - */ -#define AC_NODE_ROOT 0x00 - -/* - * function group types - */ -enum { - AC_GRP_AUDIO_FUNCTION = 0x01, - AC_GRP_MODEM_FUNCTION = 0x02, -}; - -/* - * widget types - */ -enum { - AC_WID_AUD_OUT, /* Audio Out */ - AC_WID_AUD_IN, /* Audio In */ - AC_WID_AUD_MIX, /* Audio Mixer */ - AC_WID_AUD_SEL, /* Audio Selector */ - AC_WID_PIN, /* Pin Complex */ - AC_WID_POWER, /* Power */ - AC_WID_VOL_KNB, /* Volume Knob */ - AC_WID_BEEP, /* Beep Generator */ - AC_WID_VENDOR = 0x0f /* Vendor specific */ -}; - -/* - * GET verbs - */ -#define AC_VERB_GET_STREAM_FORMAT 0x0a00 -#define AC_VERB_GET_AMP_GAIN_MUTE 0x0b00 -#define AC_VERB_GET_PROC_COEF 0x0c00 -#define AC_VERB_GET_COEF_INDEX 0x0d00 -#define AC_VERB_PARAMETERS 0x0f00 -#define AC_VERB_GET_CONNECT_SEL 0x0f01 -#define AC_VERB_GET_CONNECT_LIST 0x0f02 -#define AC_VERB_GET_PROC_STATE 0x0f03 -#define AC_VERB_GET_SDI_SELECT 0x0f04 -#define AC_VERB_GET_POWER_STATE 0x0f05 -#define AC_VERB_GET_CONV 0x0f06 -#define AC_VERB_GET_PIN_WIDGET_CONTROL 0x0f07 -#define AC_VERB_GET_UNSOLICITED_RESPONSE 0x0f08 -#define AC_VERB_GET_PIN_SENSE 0x0f09 -#define AC_VERB_GET_BEEP_CONTROL 0x0f0a -#define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c -#define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d -#define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e /* unused */ -#define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f -/* f10-f1a: GPIO */ -#define AC_VERB_GET_GPIO_DATA 0x0f15 -#define AC_VERB_GET_GPIO_MASK 0x0f16 -#define AC_VERB_GET_GPIO_DIRECTION 0x0f17 -#define AC_VERB_GET_GPIO_WAKE_MASK 0x0f18 -#define AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK 0x0f19 -#define AC_VERB_GET_GPIO_STICKY_MASK 0x0f1a -#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c -/* f20: AFG/MFG */ -#define AC_VERB_GET_SUBSYSTEM_ID 0x0f20 -#define AC_VERB_GET_CVT_CHAN_COUNT 0x0f2d -#define AC_VERB_GET_HDMI_DIP_SIZE 0x0f2e -#define AC_VERB_GET_HDMI_ELDD 0x0f2f -#define AC_VERB_GET_HDMI_DIP_INDEX 0x0f30 -#define AC_VERB_GET_HDMI_DIP_DATA 0x0f31 -#define AC_VERB_GET_HDMI_DIP_XMIT 0x0f32 -#define AC_VERB_GET_HDMI_CP_CTRL 0x0f33 -#define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34 - -/* - * SET verbs - */ -#define AC_VERB_SET_STREAM_FORMAT 0x200 -#define AC_VERB_SET_AMP_GAIN_MUTE 0x300 -#define AC_VERB_SET_PROC_COEF 0x400 -#define AC_VERB_SET_COEF_INDEX 0x500 -#define AC_VERB_SET_CONNECT_SEL 0x701 -#define AC_VERB_SET_PROC_STATE 0x703 -#define AC_VERB_SET_SDI_SELECT 0x704 -#define AC_VERB_SET_POWER_STATE 0x705 -#define AC_VERB_SET_CHANNEL_STREAMID 0x706 -#define AC_VERB_SET_PIN_WIDGET_CONTROL 0x707 -#define AC_VERB_SET_UNSOLICITED_ENABLE 0x708 -#define AC_VERB_SET_PIN_SENSE 0x709 -#define AC_VERB_SET_BEEP_CONTROL 0x70a -#define AC_VERB_SET_EAPD_BTLENABLE 0x70c -#define AC_VERB_SET_DIGI_CONVERT_1 0x70d -#define AC_VERB_SET_DIGI_CONVERT_2 0x70e -#define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f -#define AC_VERB_SET_GPIO_DATA 0x715 -#define AC_VERB_SET_GPIO_MASK 0x716 -#define AC_VERB_SET_GPIO_DIRECTION 0x717 -#define AC_VERB_SET_GPIO_WAKE_MASK 0x718 -#define AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK 0x719 -#define AC_VERB_SET_GPIO_STICKY_MASK 0x71a -#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c -#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d -#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e -#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 0x71f -#define AC_VERB_SET_EAPD 0x788 -#define AC_VERB_SET_CODEC_RESET 0x7ff -#define AC_VERB_SET_CVT_CHAN_COUNT 0x72d -#define AC_VERB_SET_HDMI_DIP_INDEX 0x730 -#define AC_VERB_SET_HDMI_DIP_DATA 0x731 -#define AC_VERB_SET_HDMI_DIP_XMIT 0x732 -#define AC_VERB_SET_HDMI_CP_CTRL 0x733 -#define AC_VERB_SET_HDMI_CHAN_SLOT 0x734 - -/* - * Parameter IDs - */ -#define AC_PAR_VENDOR_ID 0x00 -#define AC_PAR_SUBSYSTEM_ID 0x01 -#define AC_PAR_REV_ID 0x02 -#define AC_PAR_NODE_COUNT 0x04 -#define AC_PAR_FUNCTION_TYPE 0x05 -#define AC_PAR_AUDIO_FG_CAP 0x08 -#define AC_PAR_AUDIO_WIDGET_CAP 0x09 -#define AC_PAR_PCM 0x0a -#define AC_PAR_STREAM 0x0b -#define AC_PAR_PIN_CAP 0x0c -#define AC_PAR_AMP_IN_CAP 0x0d -#define AC_PAR_CONNLIST_LEN 0x0e -#define AC_PAR_POWER_STATE 0x0f -#define AC_PAR_PROC_CAP 0x10 -#define AC_PAR_GPIO_CAP 0x11 -#define AC_PAR_AMP_OUT_CAP 0x12 -#define AC_PAR_VOL_KNB_CAP 0x13 -#define AC_PAR_HDMI_LPCM_CAP 0x20 - -/* - * AC_VERB_PARAMETERS results (32bit) - */ - -/* Function Group Type */ -#define AC_FGT_TYPE (0xff<<0) -#define AC_FGT_TYPE_SHIFT 0 -#define AC_FGT_UNSOL_CAP (1<<8) - -/* Audio Function Group Capabilities */ -#define AC_AFG_OUT_DELAY (0xf<<0) -#define AC_AFG_IN_DELAY (0xf<<8) -#define AC_AFG_BEEP_GEN (1<<16) - -/* Audio Widget Capabilities */ -#define AC_WCAP_STEREO (1<<0) /* stereo I/O */ -#define AC_WCAP_IN_AMP (1<<1) /* AMP-in present */ -#define AC_WCAP_OUT_AMP (1<<2) /* AMP-out present */ -#define AC_WCAP_AMP_OVRD (1<<3) /* AMP-parameter override */ -#define AC_WCAP_FORMAT_OVRD (1<<4) /* format override */ -#define AC_WCAP_STRIPE (1<<5) /* stripe */ -#define AC_WCAP_PROC_WID (1<<6) /* Proc Widget */ -#define AC_WCAP_UNSOL_CAP (1<<7) /* Unsol capable */ -#define AC_WCAP_CONN_LIST (1<<8) /* connection list */ -#define AC_WCAP_DIGITAL (1<<9) /* digital I/O */ -#define AC_WCAP_POWER (1<<10) /* power control */ -#define AC_WCAP_LR_SWAP (1<<11) /* L/R swap */ -#define AC_WCAP_CP_CAPS (1<<12) /* content protection */ -#define AC_WCAP_CHAN_CNT_EXT (7<<13) /* channel count ext */ -#define AC_WCAP_DELAY (0xf<<16) -#define AC_WCAP_DELAY_SHIFT 16 -#define AC_WCAP_TYPE (0xf<<20) -#define AC_WCAP_TYPE_SHIFT 20 - -/* supported PCM rates and bits */ -#define AC_SUPPCM_RATES (0xfff << 0) -#define AC_SUPPCM_BITS_8 (1<<16) -#define AC_SUPPCM_BITS_16 (1<<17) -#define AC_SUPPCM_BITS_20 (1<<18) -#define AC_SUPPCM_BITS_24 (1<<19) -#define AC_SUPPCM_BITS_32 (1<<20) - -/* supported PCM stream format */ -#define AC_SUPFMT_PCM (1<<0) -#define AC_SUPFMT_FLOAT32 (1<<1) -#define AC_SUPFMT_AC3 (1<<2) - -/* GP I/O count */ -#define AC_GPIO_IO_COUNT (0xff<<0) -#define AC_GPIO_O_COUNT (0xff<<8) -#define AC_GPIO_O_COUNT_SHIFT 8 -#define AC_GPIO_I_COUNT (0xff<<16) -#define AC_GPIO_I_COUNT_SHIFT 16 -#define AC_GPIO_UNSOLICITED (1<<30) -#define AC_GPIO_WAKE (1<<31) - -/* Converter stream, channel */ -#define AC_CONV_CHANNEL (0xf<<0) -#define AC_CONV_STREAM (0xf<<4) -#define AC_CONV_STREAM_SHIFT 4 - -/* Input converter SDI select */ -#define AC_SDI_SELECT (0xf<<0) - -/* stream format id */ -#define AC_FMT_CHAN_SHIFT 0 -#define AC_FMT_CHAN_MASK (0x0f << 0) -#define AC_FMT_BITS_SHIFT 4 -#define AC_FMT_BITS_MASK (7 << 4) -#define AC_FMT_BITS_8 (0 << 4) -#define AC_FMT_BITS_16 (1 << 4) -#define AC_FMT_BITS_20 (2 << 4) -#define AC_FMT_BITS_24 (3 << 4) -#define AC_FMT_BITS_32 (4 << 4) -#define AC_FMT_DIV_SHIFT 8 -#define AC_FMT_DIV_MASK (7 << 8) -#define AC_FMT_MULT_SHIFT 11 -#define AC_FMT_MULT_MASK (7 << 11) -#define AC_FMT_BASE_SHIFT 14 -#define AC_FMT_BASE_48K (0 << 14) -#define AC_FMT_BASE_44K (1 << 14) -#define AC_FMT_TYPE_SHIFT 15 -#define AC_FMT_TYPE_PCM (0 << 15) -#define AC_FMT_TYPE_NON_PCM (1 << 15) - -/* Unsolicited response control */ -#define AC_UNSOL_TAG (0x3f<<0) -#define AC_UNSOL_ENABLED (1<<7) -#define AC_USRSP_EN AC_UNSOL_ENABLED - -/* Unsolicited responses */ -#define AC_UNSOL_RES_TAG (0x3f<<26) -#define AC_UNSOL_RES_TAG_SHIFT 26 -#define AC_UNSOL_RES_SUBTAG (0x1f<<21) -#define AC_UNSOL_RES_SUBTAG_SHIFT 21 -#define AC_UNSOL_RES_ELDV (1<<1) /* ELD Data valid (for HDMI) */ -#define AC_UNSOL_RES_PD (1<<0) /* pinsense detect */ -#define AC_UNSOL_RES_CP_STATE (1<<1) /* content protection */ -#define AC_UNSOL_RES_CP_READY (1<<0) /* content protection */ - -/* Pin widget capabilies */ -#define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */ -#define AC_PINCAP_TRIG_REQ (1<<1) /* trigger required */ -#define AC_PINCAP_PRES_DETECT (1<<2) /* presence detect capable */ -#define AC_PINCAP_HP_DRV (1<<3) /* headphone drive capable */ -#define AC_PINCAP_OUT (1<<4) /* output capable */ -#define AC_PINCAP_IN (1<<5) /* input capable */ -#define AC_PINCAP_BALANCE (1<<6) /* balanced I/O capable */ -/* Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification, - * but is marked reserved in the Intel HDA specification. - */ -#define AC_PINCAP_LR_SWAP (1<<7) /* L/R swap */ -/* Note: The same bit as LR_SWAP is newly defined as HDMI capability - * in HD-audio specification - */ -#define AC_PINCAP_HDMI (1<<7) /* HDMI pin */ -#define AC_PINCAP_DP (1<<24) /* DisplayPort pin, can - * coexist with AC_PINCAP_HDMI - */ -#define AC_PINCAP_VREF (0x37<<8) -#define AC_PINCAP_VREF_SHIFT 8 -#define AC_PINCAP_EAPD (1<<16) /* EAPD capable */ -#define AC_PINCAP_HBR (1<<27) /* High Bit Rate */ -/* Vref status (used in pin cap) */ -#define AC_PINCAP_VREF_HIZ (1<<0) /* Hi-Z */ -#define AC_PINCAP_VREF_50 (1<<1) /* 50% */ -#define AC_PINCAP_VREF_GRD (1<<2) /* ground */ -#define AC_PINCAP_VREF_80 (1<<4) /* 80% */ -#define AC_PINCAP_VREF_100 (1<<5) /* 100% */ - -/* Amplifier capabilities */ -#define AC_AMPCAP_OFFSET (0x7f<<0) /* 0dB offset */ -#define AC_AMPCAP_OFFSET_SHIFT 0 -#define AC_AMPCAP_NUM_STEPS (0x7f<<8) /* number of steps */ -#define AC_AMPCAP_NUM_STEPS_SHIFT 8 -#define AC_AMPCAP_STEP_SIZE (0x7f<<16) /* step size 0-32dB - * in 0.25dB - */ -#define AC_AMPCAP_STEP_SIZE_SHIFT 16 -#define AC_AMPCAP_MUTE (1<<31) /* mute capable */ -#define AC_AMPCAP_MUTE_SHIFT 31 - -/* Connection list */ -#define AC_CLIST_LENGTH (0x7f<<0) -#define AC_CLIST_LONG (1<<7) - -/* Supported power status */ -#define AC_PWRST_D0SUP (1<<0) -#define AC_PWRST_D1SUP (1<<1) -#define AC_PWRST_D2SUP (1<<2) -#define AC_PWRST_D3SUP (1<<3) -#define AC_PWRST_D3COLDSUP (1<<4) -#define AC_PWRST_S3D3COLDSUP (1<<29) -#define AC_PWRST_CLKSTOP (1<<30) -#define AC_PWRST_EPSS (1U<<31) - -/* Power state values */ -#define AC_PWRST_SETTING (0xf<<0) -#define AC_PWRST_ACTUAL (0xf<<4) -#define AC_PWRST_ACTUAL_SHIFT 4 -#define AC_PWRST_D0 0x00 -#define AC_PWRST_D1 0x01 -#define AC_PWRST_D2 0x02 -#define AC_PWRST_D3 0x03 - -/* Processing capabilies */ -#define AC_PCAP_BENIGN (1<<0) -#define AC_PCAP_NUM_COEF (0xff<<8) -#define AC_PCAP_NUM_COEF_SHIFT 8 - -/* Volume knobs capabilities */ -#define AC_KNBCAP_NUM_STEPS (0x7f<<0) -#define AC_KNBCAP_DELTA (1<<7) - -/* HDMI LPCM capabilities */ -#define AC_LPCMCAP_48K_CP_CHNS (0x0f<<0) /* max channels w/ CP-on */ -#define AC_LPCMCAP_48K_NO_CHNS (0x0f<<4) /* max channels w/o CP-on */ -#define AC_LPCMCAP_48K_20BIT (1<<8) /* 20b bitrate supported */ -#define AC_LPCMCAP_48K_24BIT (1<<9) /* 24b bitrate supported */ -#define AC_LPCMCAP_96K_CP_CHNS (0x0f<<10) /* max channels w/ CP-on */ -#define AC_LPCMCAP_96K_NO_CHNS (0x0f<<14) /* max channels w/o CP-on */ -#define AC_LPCMCAP_96K_20BIT (1<<18) /* 20b bitrate supported */ -#define AC_LPCMCAP_96K_24BIT (1<<19) /* 24b bitrate supported */ -#define AC_LPCMCAP_192K_CP_CHNS (0x0f<<20) /* max channels w/ CP-on */ -#define AC_LPCMCAP_192K_NO_CHNS (0x0f<<24) /* max channels w/o CP-on */ -#define AC_LPCMCAP_192K_20BIT (1<<28) /* 20b bitrate supported */ -#define AC_LPCMCAP_192K_24BIT (1<<29) /* 24b bitrate supported */ -#define AC_LPCMCAP_44K (1<<30) /* 44.1kHz support */ -#define AC_LPCMCAP_44K_MS (1<<31) /* 44.1kHz-multiplies support */ - -/* - * Control Parameters - */ - -/* Amp gain/mute */ -#define AC_AMP_MUTE (1<<7) -#define AC_AMP_GAIN (0x7f) -#define AC_AMP_GET_INDEX (0xf<<0) - -#define AC_AMP_GET_LEFT (1<<13) -#define AC_AMP_GET_RIGHT (0<<13) -#define AC_AMP_GET_OUTPUT (1<<15) -#define AC_AMP_GET_INPUT (0<<15) - -#define AC_AMP_SET_INDEX (0xf<<8) -#define AC_AMP_SET_INDEX_SHIFT 8 -#define AC_AMP_SET_RIGHT (1<<12) -#define AC_AMP_SET_LEFT (1<<13) -#define AC_AMP_SET_INPUT (1<<14) -#define AC_AMP_SET_OUTPUT (1<<15) - -/* DIGITAL1 bits */ -#define AC_DIG1_ENABLE (1<<0) -#define AC_DIG1_V (1<<1) -#define AC_DIG1_VCFG (1<<2) -#define AC_DIG1_EMPHASIS (1<<3) -#define AC_DIG1_COPYRIGHT (1<<4) -#define AC_DIG1_NONAUDIO (1<<5) -#define AC_DIG1_PROFESSIONAL (1<<6) -#define AC_DIG1_LEVEL (1<<7) - -/* DIGITAL2 bits */ -#define AC_DIG2_CC (0x7f<<0) - -/* Pin widget control - 8bit */ -#define AC_PINCTL_EPT (0x3<<0) -#define AC_PINCTL_EPT_NATIVE 0 -#define AC_PINCTL_EPT_HBR 3 -#define AC_PINCTL_VREFEN (0x7<<0) -#define AC_PINCTL_VREF_HIZ 0 /* Hi-Z */ -#define AC_PINCTL_VREF_50 1 /* 50% */ -#define AC_PINCTL_VREF_GRD 2 /* ground */ -#define AC_PINCTL_VREF_80 4 /* 80% */ -#define AC_PINCTL_VREF_100 5 /* 100% */ -#define AC_PINCTL_IN_EN (1<<5) -#define AC_PINCTL_OUT_EN (1<<6) -#define AC_PINCTL_HP_EN (1<<7) - -/* Pin sense - 32bit */ -#define AC_PINSENSE_IMPEDANCE_MASK (0x7fffffff) -#define AC_PINSENSE_PRESENCE (1<<31) -#define AC_PINSENSE_ELDV (1<<30) /* ELD valid (HDMI) */ - -/* EAPD/BTL enable - 32bit */ -#define AC_EAPDBTL_BALANCED (1<<0) -#define AC_EAPDBTL_EAPD (1<<1) -#define AC_EAPDBTL_LR_SWAP (1<<2) - -/* HDMI ELD data */ -#define AC_ELDD_ELD_VALID (1<<31) -#define AC_ELDD_ELD_DATA 0xff - -/* HDMI DIP size */ -#define AC_DIPSIZE_ELD_BUF (1<<3) /* ELD buf size of packet size */ -#define AC_DIPSIZE_PACK_IDX (0x07<<0) /* packet index */ - -/* HDMI DIP index */ -#define AC_DIPIDX_PACK_IDX (0x07<<5) /* packet idnex */ -#define AC_DIPIDX_BYTE_IDX (0x1f<<0) /* byte index */ - -/* HDMI DIP xmit (transmit) control */ -#define AC_DIPXMIT_MASK (0x3<<6) -#define AC_DIPXMIT_DISABLE (0x0<<6) /* disable xmit */ -#define AC_DIPXMIT_ONCE (0x2<<6) /* xmit once then disable */ -#define AC_DIPXMIT_BEST (0x3<<6) /* best effort */ - -/* HDMI content protection (CP) control */ -#define AC_CPCTRL_CES (1<<9) /* current encryption state */ -#define AC_CPCTRL_READY (1<<8) /* ready bit */ -#define AC_CPCTRL_SUBTAG (0x1f<<3) /* subtag for unsol-resp */ -#define AC_CPCTRL_STATE (3<<0) /* current CP request state */ - -/* Converter channel <-> HDMI slot mapping */ -#define AC_CVTMAP_HDMI_SLOT (0xf<<0) /* HDMI slot number */ -#define AC_CVTMAP_CHAN (0xf<<4) /* converter channel number */ - -/* configuration default - 32bit */ -#define AC_DEFCFG_SEQUENCE (0xf<<0) -#define AC_DEFCFG_DEF_ASSOC (0xf<<4) -#define AC_DEFCFG_ASSOC_SHIFT 4 -#define AC_DEFCFG_MISC (0xf<<8) -#define AC_DEFCFG_MISC_SHIFT 8 -#define AC_DEFCFG_MISC_NO_PRESENCE (1<<0) -#define AC_DEFCFG_COLOR (0xf<<12) -#define AC_DEFCFG_COLOR_SHIFT 12 -#define AC_DEFCFG_CONN_TYPE (0xf<<16) -#define AC_DEFCFG_CONN_TYPE_SHIFT 16 -#define AC_DEFCFG_DEVICE (0xf<<20) -#define AC_DEFCFG_DEVICE_SHIFT 20 -#define AC_DEFCFG_LOCATION (0x3f<<24) -#define AC_DEFCFG_LOCATION_SHIFT 24 -#define AC_DEFCFG_PORT_CONN (0x3<<30) -#define AC_DEFCFG_PORT_CONN_SHIFT 30 - -/* device device types (0x0-0xf) */ -enum { - AC_JACK_LINE_OUT, - AC_JACK_SPEAKER, - AC_JACK_HP_OUT, - AC_JACK_CD, - AC_JACK_SPDIF_OUT, - AC_JACK_DIG_OTHER_OUT, - AC_JACK_MODEM_LINE_SIDE, - AC_JACK_MODEM_HAND_SIDE, - AC_JACK_LINE_IN, - AC_JACK_AUX, - AC_JACK_MIC_IN, - AC_JACK_TELEPHONY, - AC_JACK_SPDIF_IN, - AC_JACK_DIG_OTHER_IN, - AC_JACK_OTHER = 0xf, -}; - -/* jack connection types (0x0-0xf) */ -enum { - AC_JACK_CONN_UNKNOWN, - AC_JACK_CONN_1_8, - AC_JACK_CONN_1_4, - AC_JACK_CONN_ATAPI, - AC_JACK_CONN_RCA, - AC_JACK_CONN_OPTICAL, - AC_JACK_CONN_OTHER_DIGITAL, - AC_JACK_CONN_OTHER_ANALOG, - AC_JACK_CONN_DIN, - AC_JACK_CONN_XLR, - AC_JACK_CONN_RJ11, - AC_JACK_CONN_COMB, - AC_JACK_CONN_OTHER = 0xf, -}; - -/* jack colors (0x0-0xf) */ -enum { - AC_JACK_COLOR_UNKNOWN, - AC_JACK_COLOR_BLACK, - AC_JACK_COLOR_GREY, - AC_JACK_COLOR_BLUE, - AC_JACK_COLOR_GREEN, - AC_JACK_COLOR_RED, - AC_JACK_COLOR_ORANGE, - AC_JACK_COLOR_YELLOW, - AC_JACK_COLOR_PURPLE, - AC_JACK_COLOR_PINK, - AC_JACK_COLOR_WHITE = 0xe, - AC_JACK_COLOR_OTHER, -}; - -/* Jack location (0x0-0x3f) */ -/* common case */ -enum { - AC_JACK_LOC_NONE, - AC_JACK_LOC_REAR, - AC_JACK_LOC_FRONT, - AC_JACK_LOC_LEFT, - AC_JACK_LOC_RIGHT, - AC_JACK_LOC_TOP, - AC_JACK_LOC_BOTTOM, -}; -/* bits 4-5 */ -enum { - AC_JACK_LOC_EXTERNAL = 0x00, - AC_JACK_LOC_INTERNAL = 0x10, - AC_JACK_LOC_SEPARATE = 0x20, - AC_JACK_LOC_OTHER = 0x30, -}; -enum { - /* external on primary chasis */ - AC_JACK_LOC_REAR_PANEL = 0x07, - AC_JACK_LOC_DRIVE_BAY, - /* internal */ - AC_JACK_LOC_RISER = 0x17, - AC_JACK_LOC_HDMI, - AC_JACK_LOC_ATAPI, - /* others */ - AC_JACK_LOC_MOBILE_IN = 0x37, - AC_JACK_LOC_MOBILE_OUT, -}; - -/* Port connectivity (0-3) */ -enum { - AC_JACK_PORT_COMPLEX, - AC_JACK_PORT_NONE, - AC_JACK_PORT_FIXED, - AC_JACK_PORT_BOTH, -}; - -/* max. connections to a widget */ -#define HDA_MAX_CONNECTIONS 32 - -/* max. codec address */ -#define HDA_MAX_CODEC_ADDRESS 0x0f - -/* max number of PCM devics per card */ -#define HDA_MAX_PCMS 10 - -/* --------------------------------------------------------------------- */ - -#endif diff --git a/qemu/hw/audio/intel-hda.c b/qemu/hw/audio/intel-hda.c deleted file mode 100644 index d372d4ab9..000000000 --- a/qemu/hw/audio/intel-hda.c +++ /dev/null @@ -1,1344 +0,0 @@ -/* - * Copyright (C) 2010 Red Hat, Inc. - * - * written by Gerd Hoffmann - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "hw/pci/msi.h" -#include "qemu/timer.h" -#include "hw/audio/audio.h" -#include "intel-hda.h" -#include "intel-hda-defs.h" -#include "sysemu/dma.h" - -/* --------------------------------------------------------------------- */ -/* hda bus */ - -static Property hda_props[] = { - DEFINE_PROP_UINT32("cad", HDACodecDevice, cad, -1), - DEFINE_PROP_END_OF_LIST() -}; - -static const TypeInfo hda_codec_bus_info = { - .name = TYPE_HDA_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(HDACodecBus), -}; - -void hda_codec_bus_init(DeviceState *dev, HDACodecBus *bus, size_t bus_size, - hda_codec_response_func response, - hda_codec_xfer_func xfer) -{ - qbus_create_inplace(bus, bus_size, TYPE_HDA_BUS, dev, NULL); - bus->response = response; - bus->xfer = xfer; -} - -static int hda_codec_dev_init(DeviceState *qdev) -{ - HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, qdev->parent_bus); - HDACodecDevice *dev = DO_UPCAST(HDACodecDevice, qdev, qdev); - HDACodecDeviceClass *cdc = HDA_CODEC_DEVICE_GET_CLASS(dev); - - if (dev->cad == -1) { - dev->cad = bus->next_cad; - } - if (dev->cad >= 15) { - return -1; - } - bus->next_cad = dev->cad + 1; - return cdc->init(dev); -} - -static int hda_codec_dev_exit(DeviceState *qdev) -{ - HDACodecDevice *dev = DO_UPCAST(HDACodecDevice, qdev, qdev); - HDACodecDeviceClass *cdc = HDA_CODEC_DEVICE_GET_CLASS(dev); - - if (cdc->exit) { - cdc->exit(dev); - } - return 0; -} - -HDACodecDevice *hda_codec_find(HDACodecBus *bus, uint32_t cad) -{ - BusChild *kid; - HDACodecDevice *cdev; - - QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { - DeviceState *qdev = kid->child; - cdev = DO_UPCAST(HDACodecDevice, qdev, qdev); - if (cdev->cad == cad) { - return cdev; - } - } - return NULL; -} - -void hda_codec_response(HDACodecDevice *dev, bool solicited, uint32_t response) -{ - HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus); - bus->response(dev, solicited, response); -} - -bool hda_codec_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, - uint8_t *buf, uint32_t len) -{ - HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus); - return bus->xfer(dev, stnr, output, buf, len); -} - -/* --------------------------------------------------------------------- */ -/* intel hda emulation */ - -typedef struct IntelHDAStream IntelHDAStream; -typedef struct IntelHDAState IntelHDAState; -typedef struct IntelHDAReg IntelHDAReg; - -typedef struct bpl { - uint64_t addr; - uint32_t len; - uint32_t flags; -} bpl; - -struct IntelHDAStream { - /* registers */ - uint32_t ctl; - uint32_t lpib; - uint32_t cbl; - uint32_t lvi; - uint32_t fmt; - uint32_t bdlp_lbase; - uint32_t bdlp_ubase; - - /* state */ - bpl *bpl; - uint32_t bentries; - uint32_t bsize, be, bp; -}; - -struct IntelHDAState { - PCIDevice pci; - const char *name; - HDACodecBus codecs; - - /* registers */ - uint32_t g_ctl; - uint32_t wake_en; - uint32_t state_sts; - uint32_t int_ctl; - uint32_t int_sts; - uint32_t wall_clk; - - uint32_t corb_lbase; - uint32_t corb_ubase; - uint32_t corb_rp; - uint32_t corb_wp; - uint32_t corb_ctl; - uint32_t corb_sts; - uint32_t corb_size; - - uint32_t rirb_lbase; - uint32_t rirb_ubase; - uint32_t rirb_wp; - uint32_t rirb_cnt; - uint32_t rirb_ctl; - uint32_t rirb_sts; - uint32_t rirb_size; - - uint32_t dp_lbase; - uint32_t dp_ubase; - - uint32_t icw; - uint32_t irr; - uint32_t ics; - - /* streams */ - IntelHDAStream st[8]; - - /* state */ - MemoryRegion mmio; - uint32_t rirb_count; - int64_t wall_base_ns; - - /* debug logging */ - const IntelHDAReg *last_reg; - uint32_t last_val; - uint32_t last_write; - uint32_t last_sec; - uint32_t repeat_count; - - /* properties */ - uint32_t debug; - uint32_t msi; - bool old_msi_addr; -}; - -#define TYPE_INTEL_HDA_GENERIC "intel-hda-generic" - -#define INTEL_HDA(obj) \ - OBJECT_CHECK(IntelHDAState, (obj), TYPE_INTEL_HDA_GENERIC) - -struct IntelHDAReg { - const char *name; /* register name */ - uint32_t size; /* size in bytes */ - uint32_t reset; /* reset value */ - uint32_t wmask; /* write mask */ - uint32_t wclear; /* write 1 to clear bits */ - uint32_t offset; /* location in IntelHDAState */ - uint32_t shift; /* byte access entries for dwords */ - uint32_t stream; - void (*whandler)(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old); - void (*rhandler)(IntelHDAState *d, const IntelHDAReg *reg); -}; - -static void intel_hda_reset(DeviceState *dev); - -/* --------------------------------------------------------------------- */ - -static hwaddr intel_hda_addr(uint32_t lbase, uint32_t ubase) -{ - hwaddr addr; - - addr = ((uint64_t)ubase << 32) | lbase; - return addr; -} - -static void intel_hda_update_int_sts(IntelHDAState *d) -{ - uint32_t sts = 0; - uint32_t i; - - /* update controller status */ - if (d->rirb_sts & ICH6_RBSTS_IRQ) { - sts |= (1 << 30); - } - if (d->rirb_sts & ICH6_RBSTS_OVERRUN) { - sts |= (1 << 30); - } - if (d->state_sts & d->wake_en) { - sts |= (1 << 30); - } - - /* update stream status */ - for (i = 0; i < 8; i++) { - /* buffer completion interrupt */ - if (d->st[i].ctl & (1 << 26)) { - sts |= (1 << i); - } - } - - /* update global status */ - if (sts & d->int_ctl) { - sts |= (1U << 31); - } - - d->int_sts = sts; -} - -static void intel_hda_update_irq(IntelHDAState *d) -{ - int msi = d->msi && msi_enabled(&d->pci); - int level; - - intel_hda_update_int_sts(d); - if (d->int_sts & (1U << 31) && d->int_ctl & (1U << 31)) { - level = 1; - } else { - level = 0; - } - dprint(d, 2, "%s: level %d [%s]\n", __FUNCTION__, - level, msi ? "msi" : "intx"); - if (msi) { - if (level) { - msi_notify(&d->pci, 0); - } - } else { - pci_set_irq(&d->pci, level); - } -} - -static int intel_hda_send_command(IntelHDAState *d, uint32_t verb) -{ - uint32_t cad, nid, data; - HDACodecDevice *codec; - HDACodecDeviceClass *cdc; - - cad = (verb >> 28) & 0x0f; - if (verb & (1 << 27)) { - /* indirect node addressing, not specified in HDA 1.0 */ - dprint(d, 1, "%s: indirect node addressing (guest bug?)\n", __FUNCTION__); - return -1; - } - nid = (verb >> 20) & 0x7f; - data = verb & 0xfffff; - - codec = hda_codec_find(&d->codecs, cad); - if (codec == NULL) { - dprint(d, 1, "%s: addressed non-existing codec\n", __FUNCTION__); - return -1; - } - cdc = HDA_CODEC_DEVICE_GET_CLASS(codec); - cdc->command(codec, nid, data); - return 0; -} - -static void intel_hda_corb_run(IntelHDAState *d) -{ - hwaddr addr; - uint32_t rp, verb; - - if (d->ics & ICH6_IRS_BUSY) { - dprint(d, 2, "%s: [icw] verb 0x%08x\n", __FUNCTION__, d->icw); - intel_hda_send_command(d, d->icw); - return; - } - - for (;;) { - if (!(d->corb_ctl & ICH6_CORBCTL_RUN)) { - dprint(d, 2, "%s: !run\n", __FUNCTION__); - return; - } - if ((d->corb_rp & 0xff) == d->corb_wp) { - dprint(d, 2, "%s: corb ring empty\n", __FUNCTION__); - return; - } - if (d->rirb_count == d->rirb_cnt) { - dprint(d, 2, "%s: rirb count reached\n", __FUNCTION__); - return; - } - - rp = (d->corb_rp + 1) & 0xff; - addr = intel_hda_addr(d->corb_lbase, d->corb_ubase); - verb = ldl_le_pci_dma(&d->pci, addr + 4*rp); - d->corb_rp = rp; - - dprint(d, 2, "%s: [rp 0x%x] verb 0x%08x\n", __FUNCTION__, rp, verb); - intel_hda_send_command(d, verb); - } -} - -static void intel_hda_response(HDACodecDevice *dev, bool solicited, uint32_t response) -{ - HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus); - IntelHDAState *d = container_of(bus, IntelHDAState, codecs); - hwaddr addr; - uint32_t wp, ex; - - if (d->ics & ICH6_IRS_BUSY) { - dprint(d, 2, "%s: [irr] response 0x%x, cad 0x%x\n", - __FUNCTION__, response, dev->cad); - d->irr = response; - d->ics &= ~(ICH6_IRS_BUSY | 0xf0); - d->ics |= (ICH6_IRS_VALID | (dev->cad << 4)); - return; - } - - if (!(d->rirb_ctl & ICH6_RBCTL_DMA_EN)) { - dprint(d, 1, "%s: rirb dma disabled, drop codec response\n", __FUNCTION__); - return; - } - - ex = (solicited ? 0 : (1 << 4)) | dev->cad; - wp = (d->rirb_wp + 1) & 0xff; - addr = intel_hda_addr(d->rirb_lbase, d->rirb_ubase); - stl_le_pci_dma(&d->pci, addr + 8*wp, response); - stl_le_pci_dma(&d->pci, addr + 8*wp + 4, ex); - d->rirb_wp = wp; - - dprint(d, 2, "%s: [wp 0x%x] response 0x%x, extra 0x%x\n", - __FUNCTION__, wp, response, ex); - - d->rirb_count++; - if (d->rirb_count == d->rirb_cnt) { - dprint(d, 2, "%s: rirb count reached (%d)\n", __FUNCTION__, d->rirb_count); - if (d->rirb_ctl & ICH6_RBCTL_IRQ_EN) { - d->rirb_sts |= ICH6_RBSTS_IRQ; - intel_hda_update_irq(d); - } - } else if ((d->corb_rp & 0xff) == d->corb_wp) { - dprint(d, 2, "%s: corb ring empty (%d/%d)\n", __FUNCTION__, - d->rirb_count, d->rirb_cnt); - if (d->rirb_ctl & ICH6_RBCTL_IRQ_EN) { - d->rirb_sts |= ICH6_RBSTS_IRQ; - intel_hda_update_irq(d); - } - } -} - -static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, - uint8_t *buf, uint32_t len) -{ - HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus); - IntelHDAState *d = container_of(bus, IntelHDAState, codecs); - hwaddr addr; - uint32_t s, copy, left; - IntelHDAStream *st; - bool irq = false; - - st = output ? d->st + 4 : d->st; - for (s = 0; s < 4; s++) { - if (stnr == ((st[s].ctl >> 20) & 0x0f)) { - st = st + s; - break; - } - } - if (s == 4) { - return false; - } - if (st->bpl == NULL) { - return false; - } - if (st->ctl & (1 << 26)) { - /* - * Wait with the next DMA xfer until the guest - * has acked the buffer completion interrupt - */ - return false; - } - - left = len; - while (left > 0) { - copy = left; - if (copy > st->bsize - st->lpib) - copy = st->bsize - st->lpib; - if (copy > st->bpl[st->be].len - st->bp) - copy = st->bpl[st->be].len - st->bp; - - dprint(d, 3, "dma: entry %d, pos %d/%d, copy %d\n", - st->be, st->bp, st->bpl[st->be].len, copy); - - pci_dma_rw(&d->pci, st->bpl[st->be].addr + st->bp, buf, copy, !output); - st->lpib += copy; - st->bp += copy; - buf += copy; - left -= copy; - - if (st->bpl[st->be].len == st->bp) { - /* bpl entry filled */ - if (st->bpl[st->be].flags & 0x01) { - irq = true; - } - st->bp = 0; - st->be++; - if (st->be == st->bentries) { - /* bpl wrap around */ - st->be = 0; - st->lpib = 0; - } - } - } - if (d->dp_lbase & 0x01) { - s = st - d->st; - addr = intel_hda_addr(d->dp_lbase & ~0x01, d->dp_ubase); - stl_le_pci_dma(&d->pci, addr + 8*s, st->lpib); - } - dprint(d, 3, "dma: --\n"); - - if (irq) { - st->ctl |= (1 << 26); /* buffer completion interrupt */ - intel_hda_update_irq(d); - } - return true; -} - -static void intel_hda_parse_bdl(IntelHDAState *d, IntelHDAStream *st) -{ - hwaddr addr; - uint8_t buf[16]; - uint32_t i; - - addr = intel_hda_addr(st->bdlp_lbase, st->bdlp_ubase); - st->bentries = st->lvi +1; - g_free(st->bpl); - st->bpl = g_malloc(sizeof(bpl) * st->bentries); - for (i = 0; i < st->bentries; i++, addr += 16) { - pci_dma_read(&d->pci, addr, buf, 16); - st->bpl[i].addr = le64_to_cpu(*(uint64_t *)buf); - st->bpl[i].len = le32_to_cpu(*(uint32_t *)(buf + 8)); - st->bpl[i].flags = le32_to_cpu(*(uint32_t *)(buf + 12)); - dprint(d, 1, "bdl/%d: 0x%" PRIx64 " +0x%x, 0x%x\n", - i, st->bpl[i].addr, st->bpl[i].len, st->bpl[i].flags); - } - - st->bsize = st->cbl; - st->lpib = 0; - st->be = 0; - st->bp = 0; -} - -static void intel_hda_notify_codecs(IntelHDAState *d, uint32_t stream, bool running, bool output) -{ - BusChild *kid; - HDACodecDevice *cdev; - - QTAILQ_FOREACH(kid, &d->codecs.qbus.children, sibling) { - DeviceState *qdev = kid->child; - HDACodecDeviceClass *cdc; - - cdev = DO_UPCAST(HDACodecDevice, qdev, qdev); - cdc = HDA_CODEC_DEVICE_GET_CLASS(cdev); - if (cdc->stream) { - cdc->stream(cdev, stream, running, output); - } - } -} - -/* --------------------------------------------------------------------- */ - -static void intel_hda_set_g_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) -{ - if ((d->g_ctl & ICH6_GCTL_RESET) == 0) { - intel_hda_reset(DEVICE(d)); - } -} - -static void intel_hda_set_wake_en(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) -{ - intel_hda_update_irq(d); -} - -static void intel_hda_set_state_sts(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) -{ - intel_hda_update_irq(d); -} - -static void intel_hda_set_int_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) -{ - intel_hda_update_irq(d); -} - -static void intel_hda_get_wall_clk(IntelHDAState *d, const IntelHDAReg *reg) -{ - int64_t ns; - - ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - d->wall_base_ns; - d->wall_clk = (uint32_t)(ns * 24 / 1000); /* 24 MHz */ -} - -static void intel_hda_set_corb_wp(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) -{ - intel_hda_corb_run(d); -} - -static void intel_hda_set_corb_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) -{ - intel_hda_corb_run(d); -} - -static void intel_hda_set_rirb_wp(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) -{ - if (d->rirb_wp & ICH6_RIRBWP_RST) { - d->rirb_wp = 0; - } -} - -static void intel_hda_set_rirb_sts(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) -{ - intel_hda_update_irq(d); - - if ((old & ICH6_RBSTS_IRQ) && !(d->rirb_sts & ICH6_RBSTS_IRQ)) { - /* cleared ICH6_RBSTS_IRQ */ - d->rirb_count = 0; - intel_hda_corb_run(d); - } -} - -static void intel_hda_set_ics(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) -{ - if (d->ics & ICH6_IRS_BUSY) { - intel_hda_corb_run(d); - } -} - -static void intel_hda_set_st_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) -{ - bool output = reg->stream >= 4; - IntelHDAStream *st = d->st + reg->stream; - - if (st->ctl & 0x01) { - /* reset */ - dprint(d, 1, "st #%d: reset\n", reg->stream); - st->ctl = SD_STS_FIFO_READY << 24; - } - if ((st->ctl & 0x02) != (old & 0x02)) { - uint32_t stnr = (st->ctl >> 20) & 0x0f; - /* run bit flipped */ - if (st->ctl & 0x02) { - /* start */ - dprint(d, 1, "st #%d: start %d (ring buf %d bytes)\n", - reg->stream, stnr, st->cbl); - intel_hda_parse_bdl(d, st); - intel_hda_notify_codecs(d, stnr, true, output); - } else { - /* stop */ - dprint(d, 1, "st #%d: stop %d\n", reg->stream, stnr); - intel_hda_notify_codecs(d, stnr, false, output); - } - } - intel_hda_update_irq(d); -} - -/* --------------------------------------------------------------------- */ - -#define ST_REG(_n, _o) (0x80 + (_n) * 0x20 + (_o)) - -static const struct IntelHDAReg regtab[] = { - /* global */ - [ ICH6_REG_GCAP ] = { - .name = "GCAP", - .size = 2, - .reset = 0x4401, - }, - [ ICH6_REG_VMIN ] = { - .name = "VMIN", - .size = 1, - }, - [ ICH6_REG_VMAJ ] = { - .name = "VMAJ", - .size = 1, - .reset = 1, - }, - [ ICH6_REG_OUTPAY ] = { - .name = "OUTPAY", - .size = 2, - .reset = 0x3c, - }, - [ ICH6_REG_INPAY ] = { - .name = "INPAY", - .size = 2, - .reset = 0x1d, - }, - [ ICH6_REG_GCTL ] = { - .name = "GCTL", - .size = 4, - .wmask = 0x0103, - .offset = offsetof(IntelHDAState, g_ctl), - .whandler = intel_hda_set_g_ctl, - }, - [ ICH6_REG_WAKEEN ] = { - .name = "WAKEEN", - .size = 2, - .wmask = 0x7fff, - .offset = offsetof(IntelHDAState, wake_en), - .whandler = intel_hda_set_wake_en, - }, - [ ICH6_REG_STATESTS ] = { - .name = "STATESTS", - .size = 2, - .wmask = 0x7fff, - .wclear = 0x7fff, - .offset = offsetof(IntelHDAState, state_sts), - .whandler = intel_hda_set_state_sts, - }, - - /* interrupts */ - [ ICH6_REG_INTCTL ] = { - .name = "INTCTL", - .size = 4, - .wmask = 0xc00000ff, - .offset = offsetof(IntelHDAState, int_ctl), - .whandler = intel_hda_set_int_ctl, - }, - [ ICH6_REG_INTSTS ] = { - .name = "INTSTS", - .size = 4, - .wmask = 0xc00000ff, - .wclear = 0xc00000ff, - .offset = offsetof(IntelHDAState, int_sts), - }, - - /* misc */ - [ ICH6_REG_WALLCLK ] = { - .name = "WALLCLK", - .size = 4, - .offset = offsetof(IntelHDAState, wall_clk), - .rhandler = intel_hda_get_wall_clk, - }, - [ ICH6_REG_WALLCLK + 0x2000 ] = { - .name = "WALLCLK(alias)", - .size = 4, - .offset = offsetof(IntelHDAState, wall_clk), - .rhandler = intel_hda_get_wall_clk, - }, - - /* dma engine */ - [ ICH6_REG_CORBLBASE ] = { - .name = "CORBLBASE", - .size = 4, - .wmask = 0xffffff80, - .offset = offsetof(IntelHDAState, corb_lbase), - }, - [ ICH6_REG_CORBUBASE ] = { - .name = "CORBUBASE", - .size = 4, - .wmask = 0xffffffff, - .offset = offsetof(IntelHDAState, corb_ubase), - }, - [ ICH6_REG_CORBWP ] = { - .name = "CORBWP", - .size = 2, - .wmask = 0xff, - .offset = offsetof(IntelHDAState, corb_wp), - .whandler = intel_hda_set_corb_wp, - }, - [ ICH6_REG_CORBRP ] = { - .name = "CORBRP", - .size = 2, - .wmask = 0x80ff, - .offset = offsetof(IntelHDAState, corb_rp), - }, - [ ICH6_REG_CORBCTL ] = { - .name = "CORBCTL", - .size = 1, - .wmask = 0x03, - .offset = offsetof(IntelHDAState, corb_ctl), - .whandler = intel_hda_set_corb_ctl, - }, - [ ICH6_REG_CORBSTS ] = { - .name = "CORBSTS", - .size = 1, - .wmask = 0x01, - .wclear = 0x01, - .offset = offsetof(IntelHDAState, corb_sts), - }, - [ ICH6_REG_CORBSIZE ] = { - .name = "CORBSIZE", - .size = 1, - .reset = 0x42, - .offset = offsetof(IntelHDAState, corb_size), - }, - [ ICH6_REG_RIRBLBASE ] = { - .name = "RIRBLBASE", - .size = 4, - .wmask = 0xffffff80, - .offset = offsetof(IntelHDAState, rirb_lbase), - }, - [ ICH6_REG_RIRBUBASE ] = { - .name = "RIRBUBASE", - .size = 4, - .wmask = 0xffffffff, - .offset = offsetof(IntelHDAState, rirb_ubase), - }, - [ ICH6_REG_RIRBWP ] = { - .name = "RIRBWP", - .size = 2, - .wmask = 0x8000, - .offset = offsetof(IntelHDAState, rirb_wp), - .whandler = intel_hda_set_rirb_wp, - }, - [ ICH6_REG_RINTCNT ] = { - .name = "RINTCNT", - .size = 2, - .wmask = 0xff, - .offset = offsetof(IntelHDAState, rirb_cnt), - }, - [ ICH6_REG_RIRBCTL ] = { - .name = "RIRBCTL", - .size = 1, - .wmask = 0x07, - .offset = offsetof(IntelHDAState, rirb_ctl), - }, - [ ICH6_REG_RIRBSTS ] = { - .name = "RIRBSTS", - .size = 1, - .wmask = 0x05, - .wclear = 0x05, - .offset = offsetof(IntelHDAState, rirb_sts), - .whandler = intel_hda_set_rirb_sts, - }, - [ ICH6_REG_RIRBSIZE ] = { - .name = "RIRBSIZE", - .size = 1, - .reset = 0x42, - .offset = offsetof(IntelHDAState, rirb_size), - }, - - [ ICH6_REG_DPLBASE ] = { - .name = "DPLBASE", - .size = 4, - .wmask = 0xffffff81, - .offset = offsetof(IntelHDAState, dp_lbase), - }, - [ ICH6_REG_DPUBASE ] = { - .name = "DPUBASE", - .size = 4, - .wmask = 0xffffffff, - .offset = offsetof(IntelHDAState, dp_ubase), - }, - - [ ICH6_REG_IC ] = { - .name = "ICW", - .size = 4, - .wmask = 0xffffffff, - .offset = offsetof(IntelHDAState, icw), - }, - [ ICH6_REG_IR ] = { - .name = "IRR", - .size = 4, - .offset = offsetof(IntelHDAState, irr), - }, - [ ICH6_REG_IRS ] = { - .name = "ICS", - .size = 2, - .wmask = 0x0003, - .wclear = 0x0002, - .offset = offsetof(IntelHDAState, ics), - .whandler = intel_hda_set_ics, - }, - -#define HDA_STREAM(_t, _i) \ - [ ST_REG(_i, ICH6_REG_SD_CTL) ] = { \ - .stream = _i, \ - .name = _t stringify(_i) " CTL", \ - .size = 4, \ - .wmask = 0x1cff001f, \ - .offset = offsetof(IntelHDAState, st[_i].ctl), \ - .whandler = intel_hda_set_st_ctl, \ - }, \ - [ ST_REG(_i, ICH6_REG_SD_CTL) + 2] = { \ - .stream = _i, \ - .name = _t stringify(_i) " CTL(stnr)", \ - .size = 1, \ - .shift = 16, \ - .wmask = 0x00ff0000, \ - .offset = offsetof(IntelHDAState, st[_i].ctl), \ - .whandler = intel_hda_set_st_ctl, \ - }, \ - [ ST_REG(_i, ICH6_REG_SD_STS)] = { \ - .stream = _i, \ - .name = _t stringify(_i) " CTL(sts)", \ - .size = 1, \ - .shift = 24, \ - .wmask = 0x1c000000, \ - .wclear = 0x1c000000, \ - .offset = offsetof(IntelHDAState, st[_i].ctl), \ - .whandler = intel_hda_set_st_ctl, \ - .reset = SD_STS_FIFO_READY << 24 \ - }, \ - [ ST_REG(_i, ICH6_REG_SD_LPIB) ] = { \ - .stream = _i, \ - .name = _t stringify(_i) " LPIB", \ - .size = 4, \ - .offset = offsetof(IntelHDAState, st[_i].lpib), \ - }, \ - [ ST_REG(_i, ICH6_REG_SD_LPIB) + 0x2000 ] = { \ - .stream = _i, \ - .name = _t stringify(_i) " LPIB(alias)", \ - .size = 4, \ - .offset = offsetof(IntelHDAState, st[_i].lpib), \ - }, \ - [ ST_REG(_i, ICH6_REG_SD_CBL) ] = { \ - .stream = _i, \ - .name = _t stringify(_i) " CBL", \ - .size = 4, \ - .wmask = 0xffffffff, \ - .offset = offsetof(IntelHDAState, st[_i].cbl), \ - }, \ - [ ST_REG(_i, ICH6_REG_SD_LVI) ] = { \ - .stream = _i, \ - .name = _t stringify(_i) " LVI", \ - .size = 2, \ - .wmask = 0x00ff, \ - .offset = offsetof(IntelHDAState, st[_i].lvi), \ - }, \ - [ ST_REG(_i, ICH6_REG_SD_FIFOSIZE) ] = { \ - .stream = _i, \ - .name = _t stringify(_i) " FIFOS", \ - .size = 2, \ - .reset = HDA_BUFFER_SIZE, \ - }, \ - [ ST_REG(_i, ICH6_REG_SD_FORMAT) ] = { \ - .stream = _i, \ - .name = _t stringify(_i) " FMT", \ - .size = 2, \ - .wmask = 0x7f7f, \ - .offset = offsetof(IntelHDAState, st[_i].fmt), \ - }, \ - [ ST_REG(_i, ICH6_REG_SD_BDLPL) ] = { \ - .stream = _i, \ - .name = _t stringify(_i) " BDLPL", \ - .size = 4, \ - .wmask = 0xffffff80, \ - .offset = offsetof(IntelHDAState, st[_i].bdlp_lbase), \ - }, \ - [ ST_REG(_i, ICH6_REG_SD_BDLPU) ] = { \ - .stream = _i, \ - .name = _t stringify(_i) " BDLPU", \ - .size = 4, \ - .wmask = 0xffffffff, \ - .offset = offsetof(IntelHDAState, st[_i].bdlp_ubase), \ - }, \ - - HDA_STREAM("IN", 0) - HDA_STREAM("IN", 1) - HDA_STREAM("IN", 2) - HDA_STREAM("IN", 3) - - HDA_STREAM("OUT", 4) - HDA_STREAM("OUT", 5) - HDA_STREAM("OUT", 6) - HDA_STREAM("OUT", 7) - -}; - -static const IntelHDAReg *intel_hda_reg_find(IntelHDAState *d, hwaddr addr) -{ - const IntelHDAReg *reg; - - if (addr >= ARRAY_SIZE(regtab)) { - goto noreg; - } - reg = regtab+addr; - if (reg->name == NULL) { - goto noreg; - } - return reg; - -noreg: - dprint(d, 1, "unknown register, addr 0x%x\n", (int) addr); - return NULL; -} - -static uint32_t *intel_hda_reg_addr(IntelHDAState *d, const IntelHDAReg *reg) -{ - uint8_t *addr = (void*)d; - - addr += reg->offset; - return (uint32_t*)addr; -} - -static void intel_hda_reg_write(IntelHDAState *d, const IntelHDAReg *reg, uint32_t val, - uint32_t wmask) -{ - uint32_t *addr; - uint32_t old; - - if (!reg) { - return; - } - - if (d->debug) { - time_t now = time(NULL); - if (d->last_write && d->last_reg == reg && d->last_val == val) { - d->repeat_count++; - if (d->last_sec != now) { - dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count); - d->last_sec = now; - d->repeat_count = 0; - } - } else { - if (d->repeat_count) { - dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count); - } - dprint(d, 2, "write %-16s: 0x%x (%x)\n", reg->name, val, wmask); - d->last_write = 1; - d->last_reg = reg; - d->last_val = val; - d->last_sec = now; - d->repeat_count = 0; - } - } - assert(reg->offset != 0); - - addr = intel_hda_reg_addr(d, reg); - old = *addr; - - if (reg->shift) { - val <<= reg->shift; - wmask <<= reg->shift; - } - wmask &= reg->wmask; - *addr &= ~wmask; - *addr |= wmask & val; - *addr &= ~(val & reg->wclear); - - if (reg->whandler) { - reg->whandler(d, reg, old); - } -} - -static uint32_t intel_hda_reg_read(IntelHDAState *d, const IntelHDAReg *reg, - uint32_t rmask) -{ - uint32_t *addr, ret; - - if (!reg) { - return 0; - } - - if (reg->rhandler) { - reg->rhandler(d, reg); - } - - if (reg->offset == 0) { - /* constant read-only register */ - ret = reg->reset; - } else { - addr = intel_hda_reg_addr(d, reg); - ret = *addr; - if (reg->shift) { - ret >>= reg->shift; - } - ret &= rmask; - } - if (d->debug) { - time_t now = time(NULL); - if (!d->last_write && d->last_reg == reg && d->last_val == ret) { - d->repeat_count++; - if (d->last_sec != now) { - dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count); - d->last_sec = now; - d->repeat_count = 0; - } - } else { - if (d->repeat_count) { - dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count); - } - dprint(d, 2, "read %-16s: 0x%x (%x)\n", reg->name, ret, rmask); - d->last_write = 0; - d->last_reg = reg; - d->last_val = ret; - d->last_sec = now; - d->repeat_count = 0; - } - } - return ret; -} - -static void intel_hda_regs_reset(IntelHDAState *d) -{ - uint32_t *addr; - int i; - - for (i = 0; i < ARRAY_SIZE(regtab); i++) { - if (regtab[i].name == NULL) { - continue; - } - if (regtab[i].offset == 0) { - continue; - } - addr = intel_hda_reg_addr(d, regtab + i); - *addr = regtab[i].reset; - } -} - -/* --------------------------------------------------------------------- */ - -static void intel_hda_mmio_writeb(void *opaque, hwaddr addr, uint32_t val) -{ - IntelHDAState *d = opaque; - const IntelHDAReg *reg = intel_hda_reg_find(d, addr); - - intel_hda_reg_write(d, reg, val, 0xff); -} - -static void intel_hda_mmio_writew(void *opaque, hwaddr addr, uint32_t val) -{ - IntelHDAState *d = opaque; - const IntelHDAReg *reg = intel_hda_reg_find(d, addr); - - intel_hda_reg_write(d, reg, val, 0xffff); -} - -static void intel_hda_mmio_writel(void *opaque, hwaddr addr, uint32_t val) -{ - IntelHDAState *d = opaque; - const IntelHDAReg *reg = intel_hda_reg_find(d, addr); - - intel_hda_reg_write(d, reg, val, 0xffffffff); -} - -static uint32_t intel_hda_mmio_readb(void *opaque, hwaddr addr) -{ - IntelHDAState *d = opaque; - const IntelHDAReg *reg = intel_hda_reg_find(d, addr); - - return intel_hda_reg_read(d, reg, 0xff); -} - -static uint32_t intel_hda_mmio_readw(void *opaque, hwaddr addr) -{ - IntelHDAState *d = opaque; - const IntelHDAReg *reg = intel_hda_reg_find(d, addr); - - return intel_hda_reg_read(d, reg, 0xffff); -} - -static uint32_t intel_hda_mmio_readl(void *opaque, hwaddr addr) -{ - IntelHDAState *d = opaque; - const IntelHDAReg *reg = intel_hda_reg_find(d, addr); - - return intel_hda_reg_read(d, reg, 0xffffffff); -} - -static const MemoryRegionOps intel_hda_mmio_ops = { - .old_mmio = { - .read = { - intel_hda_mmio_readb, - intel_hda_mmio_readw, - intel_hda_mmio_readl, - }, - .write = { - intel_hda_mmio_writeb, - intel_hda_mmio_writew, - intel_hda_mmio_writel, - }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* --------------------------------------------------------------------- */ - -static void intel_hda_reset(DeviceState *dev) -{ - BusChild *kid; - IntelHDAState *d = INTEL_HDA(dev); - HDACodecDevice *cdev; - - intel_hda_regs_reset(d); - d->wall_base_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - /* reset codecs */ - QTAILQ_FOREACH(kid, &d->codecs.qbus.children, sibling) { - DeviceState *qdev = kid->child; - cdev = DO_UPCAST(HDACodecDevice, qdev, qdev); - device_reset(DEVICE(cdev)); - d->state_sts |= (1 << cdev->cad); - } - intel_hda_update_irq(d); -} - -static void intel_hda_realize(PCIDevice *pci, Error **errp) -{ - IntelHDAState *d = INTEL_HDA(pci); - uint8_t *conf = d->pci.config; - - d->name = object_get_typename(OBJECT(d)); - - pci_config_set_interrupt_pin(conf, 1); - - /* HDCTL off 0x40 bit 0 selects signaling mode (1-HDA, 0 - Ac97) 18.1.19 */ - conf[0x40] = 0x01; - - memory_region_init_io(&d->mmio, OBJECT(d), &intel_hda_mmio_ops, d, - "intel-hda", 0x4000); - pci_register_bar(&d->pci, 0, 0, &d->mmio); - if (d->msi) { - msi_init(&d->pci, d->old_msi_addr ? 0x50 : 0x60, 1, true, false); - } - - hda_codec_bus_init(DEVICE(pci), &d->codecs, sizeof(d->codecs), - intel_hda_response, intel_hda_xfer); -} - -static void intel_hda_exit(PCIDevice *pci) -{ - IntelHDAState *d = INTEL_HDA(pci); - - msi_uninit(&d->pci); -} - -static int intel_hda_post_load(void *opaque, int version) -{ - IntelHDAState* d = opaque; - int i; - - dprint(d, 1, "%s\n", __FUNCTION__); - for (i = 0; i < ARRAY_SIZE(d->st); i++) { - if (d->st[i].ctl & 0x02) { - intel_hda_parse_bdl(d, &d->st[i]); - } - } - intel_hda_update_irq(d); - return 0; -} - -static const VMStateDescription vmstate_intel_hda_stream = { - .name = "intel-hda-stream", - .version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(ctl, IntelHDAStream), - VMSTATE_UINT32(lpib, IntelHDAStream), - VMSTATE_UINT32(cbl, IntelHDAStream), - VMSTATE_UINT32(lvi, IntelHDAStream), - VMSTATE_UINT32(fmt, IntelHDAStream), - VMSTATE_UINT32(bdlp_lbase, IntelHDAStream), - VMSTATE_UINT32(bdlp_ubase, IntelHDAStream), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_intel_hda = { - .name = "intel-hda", - .version_id = 1, - .post_load = intel_hda_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(pci, IntelHDAState), - - /* registers */ - VMSTATE_UINT32(g_ctl, IntelHDAState), - VMSTATE_UINT32(wake_en, IntelHDAState), - VMSTATE_UINT32(state_sts, IntelHDAState), - VMSTATE_UINT32(int_ctl, IntelHDAState), - VMSTATE_UINT32(int_sts, IntelHDAState), - VMSTATE_UINT32(wall_clk, IntelHDAState), - VMSTATE_UINT32(corb_lbase, IntelHDAState), - VMSTATE_UINT32(corb_ubase, IntelHDAState), - VMSTATE_UINT32(corb_rp, IntelHDAState), - VMSTATE_UINT32(corb_wp, IntelHDAState), - VMSTATE_UINT32(corb_ctl, IntelHDAState), - VMSTATE_UINT32(corb_sts, IntelHDAState), - VMSTATE_UINT32(corb_size, IntelHDAState), - VMSTATE_UINT32(rirb_lbase, IntelHDAState), - VMSTATE_UINT32(rirb_ubase, IntelHDAState), - VMSTATE_UINT32(rirb_wp, IntelHDAState), - VMSTATE_UINT32(rirb_cnt, IntelHDAState), - VMSTATE_UINT32(rirb_ctl, IntelHDAState), - VMSTATE_UINT32(rirb_sts, IntelHDAState), - VMSTATE_UINT32(rirb_size, IntelHDAState), - VMSTATE_UINT32(dp_lbase, IntelHDAState), - VMSTATE_UINT32(dp_ubase, IntelHDAState), - VMSTATE_UINT32(icw, IntelHDAState), - VMSTATE_UINT32(irr, IntelHDAState), - VMSTATE_UINT32(ics, IntelHDAState), - VMSTATE_STRUCT_ARRAY(st, IntelHDAState, 8, 0, - vmstate_intel_hda_stream, - IntelHDAStream), - - /* additional state info */ - VMSTATE_UINT32(rirb_count, IntelHDAState), - VMSTATE_INT64(wall_base_ns, IntelHDAState), - - VMSTATE_END_OF_LIST() - } -}; - -static Property intel_hda_properties[] = { - DEFINE_PROP_UINT32("debug", IntelHDAState, debug, 0), - DEFINE_PROP_UINT32("msi", IntelHDAState, msi, 1), - DEFINE_PROP_BOOL("old_msi_addr", IntelHDAState, old_msi_addr, false), - DEFINE_PROP_END_OF_LIST(), -}; - -static void intel_hda_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = intel_hda_realize; - k->exit = intel_hda_exit; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->class_id = PCI_CLASS_MULTIMEDIA_HD_AUDIO; - dc->reset = intel_hda_reset; - dc->vmsd = &vmstate_intel_hda; - dc->props = intel_hda_properties; -} - -static void intel_hda_class_init_ich6(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->device_id = 0x2668; - k->revision = 1; - set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - dc->desc = "Intel HD Audio Controller (ich6)"; -} - -static void intel_hda_class_init_ich9(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->device_id = 0x293e; - k->revision = 3; - set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - dc->desc = "Intel HD Audio Controller (ich9)"; -} - -static const TypeInfo intel_hda_info = { - .name = TYPE_INTEL_HDA_GENERIC, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(IntelHDAState), - .class_init = intel_hda_class_init, - .abstract = true, -}; - -static const TypeInfo intel_hda_info_ich6 = { - .name = "intel-hda", - .parent = TYPE_INTEL_HDA_GENERIC, - .class_init = intel_hda_class_init_ich6, -}; - -static const TypeInfo intel_hda_info_ich9 = { - .name = "ich9-intel-hda", - .parent = TYPE_INTEL_HDA_GENERIC, - .class_init = intel_hda_class_init_ich9, -}; - -static void hda_codec_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - k->init = hda_codec_dev_init; - k->exit = hda_codec_dev_exit; - set_bit(DEVICE_CATEGORY_SOUND, k->categories); - k->bus_type = TYPE_HDA_BUS; - k->props = hda_props; -} - -static const TypeInfo hda_codec_device_type_info = { - .name = TYPE_HDA_CODEC_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(HDACodecDevice), - .abstract = true, - .class_size = sizeof(HDACodecDeviceClass), - .class_init = hda_codec_device_class_init, -}; - -/* - * create intel hda controller with codec attached to it, - * so '-soundhw hda' works. - */ -static int intel_hda_and_codec_init(PCIBus *bus) -{ - DeviceState *controller; - BusState *hdabus; - DeviceState *codec; - - controller = DEVICE(pci_create_simple(bus, -1, "intel-hda")); - hdabus = QLIST_FIRST(&controller->child_bus); - codec = qdev_create(hdabus, "hda-duplex"); - qdev_init_nofail(codec); - return 0; -} - -static void intel_hda_register_types(void) -{ - type_register_static(&hda_codec_bus_info); - type_register_static(&intel_hda_info); - type_register_static(&intel_hda_info_ich6); - type_register_static(&intel_hda_info_ich9); - type_register_static(&hda_codec_device_type_info); - pci_register_soundhw("hda", "Intel HD Audio", intel_hda_and_codec_init); -} - -type_init(intel_hda_register_types) diff --git a/qemu/hw/audio/intel-hda.h b/qemu/hw/audio/intel-hda.h deleted file mode 100644 index d784bcf5f..000000000 --- a/qemu/hw/audio/intel-hda.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef HW_INTEL_HDA_H -#define HW_INTEL_HDA_H - -#include "hw/qdev.h" - -/* --------------------------------------------------------------------- */ -/* hda bus */ - -#define TYPE_HDA_CODEC_DEVICE "hda-codec" -#define HDA_CODEC_DEVICE(obj) \ - OBJECT_CHECK(HDACodecDevice, (obj), TYPE_HDA_CODEC_DEVICE) -#define HDA_CODEC_DEVICE_CLASS(klass) \ - OBJECT_CLASS_CHECK(HDACodecDeviceClass, (klass), TYPE_HDA_CODEC_DEVICE) -#define HDA_CODEC_DEVICE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(HDACodecDeviceClass, (obj), TYPE_HDA_CODEC_DEVICE) - -#define TYPE_HDA_BUS "HDA" -#define HDA_BUS(obj) OBJECT_CHECK(HDACodecBus, (obj), TYPE_HDA_BUS) - -typedef struct HDACodecBus HDACodecBus; -typedef struct HDACodecDevice HDACodecDevice; - -typedef void (*hda_codec_response_func)(HDACodecDevice *dev, - bool solicited, uint32_t response); -typedef bool (*hda_codec_xfer_func)(HDACodecDevice *dev, - uint32_t stnr, bool output, - uint8_t *buf, uint32_t len); - -struct HDACodecBus { - BusState qbus; - uint32_t next_cad; - hda_codec_response_func response; - hda_codec_xfer_func xfer; -}; - -typedef struct HDACodecDeviceClass -{ - DeviceClass parent_class; - - int (*init)(HDACodecDevice *dev); - int (*exit)(HDACodecDevice *dev); - void (*command)(HDACodecDevice *dev, uint32_t nid, uint32_t data); - void (*stream)(HDACodecDevice *dev, uint32_t stnr, bool running, bool output); -} HDACodecDeviceClass; - -struct HDACodecDevice { - DeviceState qdev; - uint32_t cad; /* codec address */ -}; - -void hda_codec_bus_init(DeviceState *dev, HDACodecBus *bus, size_t bus_size, - hda_codec_response_func response, - hda_codec_xfer_func xfer); -HDACodecDevice *hda_codec_find(HDACodecBus *bus, uint32_t cad); - -void hda_codec_response(HDACodecDevice *dev, bool solicited, uint32_t response); -bool hda_codec_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, - uint8_t *buf, uint32_t len); - -/* --------------------------------------------------------------------- */ - -#define dprint(_dev, _level, _fmt, ...) \ - do { \ - if (_dev->debug >= _level) { \ - fprintf(stderr, "%s: ", _dev->name); \ - fprintf(stderr, _fmt, ## __VA_ARGS__); \ - } \ - } while (0) - -/* --------------------------------------------------------------------- */ - -#endif diff --git a/qemu/hw/audio/lm4549.c b/qemu/hw/audio/lm4549.c deleted file mode 100644 index a46f2301a..000000000 --- a/qemu/hw/audio/lm4549.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - * LM4549 Audio Codec Interface - * - * Copyright (c) 2011 - * Written by Mathieu Sonet - www.elasticsheep.com - * - * This code is licensed under the GPL. - * - * ***************************************************************** - * - * This driver emulates the LM4549 codec. - * - * 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" - -#if 0 -#define LM4549_DEBUG 1 -#endif - -#if 0 -#define LM4549_DUMP_DAC_INPUT 1 -#endif - -#ifdef LM4549_DEBUG -#define DPRINTF(fmt, ...) \ -do { printf("lm4549: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while (0) -#endif - -#if defined(LM4549_DUMP_DAC_INPUT) -static FILE *fp_dac_input; -#endif - -/* LM4549 register list */ -enum { - LM4549_Reset = 0x00, - LM4549_Master_Volume = 0x02, - LM4549_Line_Out_Volume = 0x04, - LM4549_Master_Volume_Mono = 0x06, - LM4549_PC_Beep_Volume = 0x0A, - LM4549_Phone_Volume = 0x0C, - LM4549_Mic_Volume = 0x0E, - LM4549_Line_In_Volume = 0x10, - LM4549_CD_Volume = 0x12, - LM4549_Video_Volume = 0x14, - LM4549_Aux_Volume = 0x16, - LM4549_PCM_Out_Volume = 0x18, - LM4549_Record_Select = 0x1A, - LM4549_Record_Gain = 0x1C, - LM4549_General_Purpose = 0x20, - LM4549_3D_Control = 0x22, - LM4549_Powerdown_Ctrl_Stat = 0x26, - LM4549_Ext_Audio_ID = 0x28, - LM4549_Ext_Audio_Stat_Ctrl = 0x2A, - LM4549_PCM_Front_DAC_Rate = 0x2C, - LM4549_PCM_ADC_Rate = 0x32, - LM4549_Vendor_ID1 = 0x7C, - LM4549_Vendor_ID2 = 0x7E -}; - -static void lm4549_reset(lm4549_state *s) -{ - uint16_t *regfile = s->regfile; - - regfile[LM4549_Reset] = 0x0d50; - regfile[LM4549_Master_Volume] = 0x8008; - regfile[LM4549_Line_Out_Volume] = 0x8000; - regfile[LM4549_Master_Volume_Mono] = 0x8000; - regfile[LM4549_PC_Beep_Volume] = 0x0000; - regfile[LM4549_Phone_Volume] = 0x8008; - regfile[LM4549_Mic_Volume] = 0x8008; - regfile[LM4549_Line_In_Volume] = 0x8808; - regfile[LM4549_CD_Volume] = 0x8808; - regfile[LM4549_Video_Volume] = 0x8808; - regfile[LM4549_Aux_Volume] = 0x8808; - regfile[LM4549_PCM_Out_Volume] = 0x8808; - regfile[LM4549_Record_Select] = 0x0000; - regfile[LM4549_Record_Gain] = 0x8000; - regfile[LM4549_General_Purpose] = 0x0000; - regfile[LM4549_3D_Control] = 0x0101; - regfile[LM4549_Powerdown_Ctrl_Stat] = 0x000f; - regfile[LM4549_Ext_Audio_ID] = 0x0001; - regfile[LM4549_Ext_Audio_Stat_Ctrl] = 0x0000; - regfile[LM4549_PCM_Front_DAC_Rate] = 0xbb80; - regfile[LM4549_PCM_ADC_Rate] = 0xbb80; - regfile[LM4549_Vendor_ID1] = 0x4e53; - regfile[LM4549_Vendor_ID2] = 0x4331; -} - -static void lm4549_audio_transfer(lm4549_state *s) -{ - uint32_t written_bytes, written_samples; - uint32_t i; - - /* Activate the voice */ - AUD_set_active_out(s->voice, 1); - s->voice_is_active = 1; - - /* Try to write the buffer content */ - written_bytes = AUD_write(s->voice, s->buffer, - s->buffer_level * sizeof(uint16_t)); - written_samples = written_bytes >> 1; - -#if defined(LM4549_DUMP_DAC_INPUT) - fwrite(s->buffer, sizeof(uint8_t), written_bytes, fp_dac_input); -#endif - - s->buffer_level -= written_samples; - - if (s->buffer_level > 0) { - /* Move the data back to the start of the buffer */ - for (i = 0; i < s->buffer_level; i++) { - s->buffer[i] = s->buffer[i + written_samples]; - } - } -} - -static void lm4549_audio_out_callback(void *opaque, int free) -{ - lm4549_state *s = (lm4549_state *)opaque; - static uint32_t prev_buffer_level; - -#ifdef LM4549_DEBUG - int size = AUD_get_buffer_size_out(s->voice); - DPRINTF("audio_out_callback size = %i free = %i\n", size, free); -#endif - - /* Detect that no data are consumed - => disable the voice */ - if (s->buffer_level == prev_buffer_level) { - AUD_set_active_out(s->voice, 0); - s->voice_is_active = 0; - } - prev_buffer_level = s->buffer_level; - - /* Check if a buffer transfer is pending */ - if (s->buffer_level == LM4549_BUFFER_SIZE) { - lm4549_audio_transfer(s); - - /* Request more data */ - if (s->data_req_cb != NULL) { - (s->data_req_cb)(s->opaque); - } - } -} - -uint32_t lm4549_read(lm4549_state *s, hwaddr offset) -{ - uint16_t *regfile = s->regfile; - uint32_t value = 0; - - /* Read the stored value */ - assert(offset < 128); - value = regfile[offset]; - - DPRINTF("read [0x%02x] = 0x%04x\n", offset, value); - - return value; -} - -void lm4549_write(lm4549_state *s, - hwaddr offset, uint32_t value) -{ - uint16_t *regfile = s->regfile; - - assert(offset < 128); - DPRINTF("write [0x%02x] = 0x%04x\n", offset, value); - - switch (offset) { - case LM4549_Reset: - lm4549_reset(s); - break; - - case LM4549_PCM_Front_DAC_Rate: - regfile[LM4549_PCM_Front_DAC_Rate] = value; - DPRINTF("DAC rate change = %i\n", value); - - /* Re-open a voice with the new sample rate */ - struct audsettings as; - as.freq = value; - as.nchannels = 2; - as.fmt = AUD_FMT_S16; - as.endianness = 0; - - s->voice = AUD_open_out( - &s->card, - s->voice, - "lm4549.out", - s, - lm4549_audio_out_callback, - &as - ); - break; - - case LM4549_Powerdown_Ctrl_Stat: - value &= ~0xf; - value |= regfile[LM4549_Powerdown_Ctrl_Stat] & 0xf; - regfile[LM4549_Powerdown_Ctrl_Stat] = value; - break; - - case LM4549_Ext_Audio_ID: - case LM4549_Vendor_ID1: - case LM4549_Vendor_ID2: - DPRINTF("Write to read-only register 0x%x\n", (int)offset); - break; - - default: - /* Store the new value */ - regfile[offset] = value; - break; - } -} - -uint32_t lm4549_write_samples(lm4549_state *s, uint32_t left, uint32_t right) -{ - /* The left and right samples are in 20-bit resolution. - The LM4549 has 18-bit resolution and only uses the bits [19:2]. - This model supports 16-bit playback. - */ - - if (s->buffer_level > LM4549_BUFFER_SIZE - 2) { - DPRINTF("write_sample Buffer full\n"); - return 0; - } - - /* Store 16-bit samples in the buffer */ - s->buffer[s->buffer_level++] = (left >> 4); - s->buffer[s->buffer_level++] = (right >> 4); - - if (s->buffer_level == LM4549_BUFFER_SIZE) { - /* Trigger the transfer of the buffer to the audio host */ - lm4549_audio_transfer(s); - } - - return 1; -} - -static int lm4549_post_load(void *opaque, int version_id) -{ - lm4549_state *s = (lm4549_state *)opaque; - uint16_t *regfile = s->regfile; - - /* Re-open a voice with the current sample rate */ - uint32_t freq = regfile[LM4549_PCM_Front_DAC_Rate]; - - DPRINTF("post_load freq = %i\n", freq); - DPRINTF("post_load voice_is_active = %i\n", s->voice_is_active); - - struct audsettings as; - as.freq = freq; - as.nchannels = 2; - as.fmt = AUD_FMT_S16; - as.endianness = 0; - - s->voice = AUD_open_out( - &s->card, - s->voice, - "lm4549.out", - s, - lm4549_audio_out_callback, - &as - ); - - /* Request data */ - if (s->voice_is_active == 1) { - lm4549_audio_out_callback(s, AUD_get_buffer_size_out(s->voice)); - } - - return 0; -} - -void lm4549_init(lm4549_state *s, lm4549_callback data_req_cb, void* opaque) -{ - struct audsettings as; - - /* Store the callback and opaque pointer */ - s->data_req_cb = data_req_cb; - s->opaque = opaque; - - /* Init the registers */ - lm4549_reset(s); - - /* Register an audio card */ - AUD_register_card("lm4549", &s->card); - - /* Open a default voice */ - as.freq = 48000; - as.nchannels = 2; - as.fmt = AUD_FMT_S16; - as.endianness = 0; - - s->voice = AUD_open_out( - &s->card, - s->voice, - "lm4549.out", - s, - lm4549_audio_out_callback, - &as - ); - - AUD_set_volume_out(s->voice, 0, 255, 255); - - s->voice_is_active = 0; - - /* Reset the input buffer */ - memset(s->buffer, 0x00, sizeof(s->buffer)); - s->buffer_level = 0; - -#if defined(LM4549_DUMP_DAC_INPUT) - fp_dac_input = fopen("lm4549_dac_input.pcm", "wb"); - if (!fp_dac_input) { - hw_error("Unable to open lm4549_dac_input.pcm for writing\n"); - } -#endif -} - -const VMStateDescription vmstate_lm4549_state = { - .name = "lm4549_state", - .version_id = 1, - .minimum_version_id = 1, - .post_load = lm4549_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(voice_is_active, lm4549_state), - VMSTATE_UINT16_ARRAY(regfile, lm4549_state, 128), - VMSTATE_UINT16_ARRAY(buffer, lm4549_state, LM4549_BUFFER_SIZE), - VMSTATE_UINT32(buffer_level, lm4549_state), - VMSTATE_END_OF_LIST() - } -}; diff --git a/qemu/hw/audio/lm4549.h b/qemu/hw/audio/lm4549.h deleted file mode 100644 index 812a7a444..000000000 --- a/qemu/hw/audio/lm4549.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * LM4549 Audio Codec Interface - * - * Copyright (c) 2011 - * Written by Mathieu Sonet - www.elasticsheep.com - * - * This code is licensed under the GPL. - * - * ***************************************************************** - */ - -#ifndef HW_LM4549_H -#define HW_LM4549_H - -#include "audio/audio.h" - -typedef void (*lm4549_callback)(void *opaque); - -#define LM4549_BUFFER_SIZE (512 * 2) /* 512 16-bit stereo samples */ - - -typedef struct { - QEMUSoundCard card; - SWVoiceOut *voice; - uint32_t voice_is_active; - - uint16_t regfile[128]; - lm4549_callback data_req_cb; - void *opaque; - - uint16_t buffer[LM4549_BUFFER_SIZE]; - uint32_t buffer_level; -} lm4549_state; - -extern const VMStateDescription vmstate_lm4549_state; - - -void lm4549_init(lm4549_state *s, lm4549_callback data_req, void *opaque); -uint32_t lm4549_read(lm4549_state *s, hwaddr offset); -void lm4549_write(lm4549_state *s, hwaddr offset, uint32_t value); -uint32_t lm4549_write_samples(lm4549_state *s, uint32_t left, uint32_t right); - -#endif /* #ifndef HW_LM4549_H */ diff --git a/qemu/hw/audio/marvell_88w8618.c b/qemu/hw/audio/marvell_88w8618.c deleted file mode 100644 index a6ca1806b..000000000 --- a/qemu/hw/audio/marvell_88w8618.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Marvell 88w8618 audio emulation extracted from - * Marvell MV88w8618 / Freecom MusicPal emulation. - * - * Copyright (c) 2008 Jan Kiszka - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "hw/hw.h" -#include "hw/i2c/i2c.h" -#include "audio/audio.h" - -#define MP_AUDIO_SIZE 0x00001000 - -/* Audio register offsets */ -#define MP_AUDIO_PLAYBACK_MODE 0x00 -#define MP_AUDIO_CLOCK_DIV 0x18 -#define MP_AUDIO_IRQ_STATUS 0x20 -#define MP_AUDIO_IRQ_ENABLE 0x24 -#define MP_AUDIO_TX_START_LO 0x28 -#define MP_AUDIO_TX_THRESHOLD 0x2C -#define MP_AUDIO_TX_STATUS 0x38 -#define MP_AUDIO_TX_START_HI 0x40 - -/* Status register and IRQ enable bits */ -#define MP_AUDIO_TX_HALF (1 << 6) -#define MP_AUDIO_TX_FULL (1 << 7) - -/* Playback mode bits */ -#define MP_AUDIO_16BIT_SAMPLE (1 << 0) -#define MP_AUDIO_PLAYBACK_EN (1 << 7) -#define MP_AUDIO_CLOCK_24MHZ (1 << 9) -#define MP_AUDIO_MONO (1 << 14) - -#define TYPE_MV88W8618_AUDIO "mv88w8618_audio" -#define MV88W8618_AUDIO(obj) \ - OBJECT_CHECK(mv88w8618_audio_state, (obj), TYPE_MV88W8618_AUDIO) - -typedef struct mv88w8618_audio_state { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq irq; - uint32_t playback_mode; - uint32_t status; - uint32_t irq_enable; - uint32_t phys_buf; - uint32_t target_buffer; - uint32_t threshold; - uint32_t play_pos; - uint32_t last_free; - uint32_t clock_div; - void *wm; -} mv88w8618_audio_state; - -static void mv88w8618_audio_callback(void *opaque, int free_out, int free_in) -{ - mv88w8618_audio_state *s = opaque; - int16_t *codec_buffer; - int8_t buf[4096]; - int8_t *mem_buffer; - int pos, block_size; - - if (!(s->playback_mode & MP_AUDIO_PLAYBACK_EN)) { - return; - } - if (s->playback_mode & MP_AUDIO_16BIT_SAMPLE) { - free_out <<= 1; - } - if (!(s->playback_mode & MP_AUDIO_MONO)) { - free_out <<= 1; - } - block_size = s->threshold / 2; - if (free_out - s->last_free < block_size) { - return; - } - if (block_size > 4096) { - return; - } - cpu_physical_memory_read(s->target_buffer + s->play_pos, buf, block_size); - mem_buffer = buf; - if (s->playback_mode & MP_AUDIO_16BIT_SAMPLE) { - if (s->playback_mode & MP_AUDIO_MONO) { - codec_buffer = wm8750_dac_buffer(s->wm, block_size >> 1); - for (pos = 0; pos < block_size; pos += 2) { - *codec_buffer++ = *(int16_t *)mem_buffer; - *codec_buffer++ = *(int16_t *)mem_buffer; - mem_buffer += 2; - } - } else { - memcpy(wm8750_dac_buffer(s->wm, block_size >> 2), - (uint32_t *)mem_buffer, block_size); - } - } else { - if (s->playback_mode & MP_AUDIO_MONO) { - codec_buffer = wm8750_dac_buffer(s->wm, block_size); - for (pos = 0; pos < block_size; pos++) { - *codec_buffer++ = cpu_to_le16(256 * *mem_buffer); - *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++); - } - } else { - codec_buffer = wm8750_dac_buffer(s->wm, block_size >> 1); - for (pos = 0; pos < block_size; pos += 2) { - *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++); - *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++); - } - } - } - wm8750_dac_commit(s->wm); - - s->last_free = free_out - block_size; - - if (s->play_pos == 0) { - s->status |= MP_AUDIO_TX_HALF; - s->play_pos = block_size; - } else { - s->status |= MP_AUDIO_TX_FULL; - s->play_pos = 0; - } - - if (s->status & s->irq_enable) { - qemu_irq_raise(s->irq); - } -} - -static void mv88w8618_audio_clock_update(mv88w8618_audio_state *s) -{ - int rate; - - if (s->playback_mode & MP_AUDIO_CLOCK_24MHZ) { - rate = 24576000 / 64; /* 24.576MHz */ - } else { - rate = 11289600 / 64; /* 11.2896MHz */ - } - rate /= ((s->clock_div >> 8) & 0xff) + 1; - - wm8750_set_bclk_in(s->wm, rate); -} - -static uint64_t mv88w8618_audio_read(void *opaque, hwaddr offset, - unsigned size) -{ - mv88w8618_audio_state *s = opaque; - - switch (offset) { - case MP_AUDIO_PLAYBACK_MODE: - return s->playback_mode; - - case MP_AUDIO_CLOCK_DIV: - return s->clock_div; - - case MP_AUDIO_IRQ_STATUS: - return s->status; - - case MP_AUDIO_IRQ_ENABLE: - return s->irq_enable; - - case MP_AUDIO_TX_STATUS: - return s->play_pos >> 2; - - default: - return 0; - } -} - -static void mv88w8618_audio_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - mv88w8618_audio_state *s = opaque; - - switch (offset) { - case MP_AUDIO_PLAYBACK_MODE: - if (value & MP_AUDIO_PLAYBACK_EN && - !(s->playback_mode & MP_AUDIO_PLAYBACK_EN)) { - s->status = 0; - s->last_free = 0; - s->play_pos = 0; - } - s->playback_mode = value; - mv88w8618_audio_clock_update(s); - break; - - case MP_AUDIO_CLOCK_DIV: - s->clock_div = value; - s->last_free = 0; - s->play_pos = 0; - mv88w8618_audio_clock_update(s); - break; - - case MP_AUDIO_IRQ_STATUS: - s->status &= ~value; - break; - - case MP_AUDIO_IRQ_ENABLE: - s->irq_enable = value; - if (s->status & s->irq_enable) { - qemu_irq_raise(s->irq); - } - break; - - case MP_AUDIO_TX_START_LO: - s->phys_buf = (s->phys_buf & 0xFFFF0000) | (value & 0xFFFF); - s->target_buffer = s->phys_buf; - s->play_pos = 0; - s->last_free = 0; - break; - - case MP_AUDIO_TX_THRESHOLD: - s->threshold = (value + 1) * 4; - break; - - case MP_AUDIO_TX_START_HI: - s->phys_buf = (s->phys_buf & 0xFFFF) | (value << 16); - s->target_buffer = s->phys_buf; - s->play_pos = 0; - s->last_free = 0; - break; - } -} - -static void mv88w8618_audio_reset(DeviceState *d) -{ - mv88w8618_audio_state *s = MV88W8618_AUDIO(d); - - s->playback_mode = 0; - s->status = 0; - s->irq_enable = 0; - s->clock_div = 0; - s->threshold = 0; - s->phys_buf = 0; -} - -static const MemoryRegionOps mv88w8618_audio_ops = { - .read = mv88w8618_audio_read, - .write = mv88w8618_audio_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int mv88w8618_audio_init(SysBusDevice *dev) -{ - mv88w8618_audio_state *s = MV88W8618_AUDIO(dev); - - sysbus_init_irq(dev, &s->irq); - - wm8750_data_req_set(s->wm, mv88w8618_audio_callback, s); - - memory_region_init_io(&s->iomem, OBJECT(s), &mv88w8618_audio_ops, s, - "audio", MP_AUDIO_SIZE); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static const VMStateDescription mv88w8618_audio_vmsd = { - .name = "mv88w8618_audio", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(playback_mode, mv88w8618_audio_state), - VMSTATE_UINT32(status, mv88w8618_audio_state), - VMSTATE_UINT32(irq_enable, mv88w8618_audio_state), - VMSTATE_UINT32(phys_buf, mv88w8618_audio_state), - VMSTATE_UINT32(target_buffer, mv88w8618_audio_state), - VMSTATE_UINT32(threshold, mv88w8618_audio_state), - VMSTATE_UINT32(play_pos, mv88w8618_audio_state), - VMSTATE_UINT32(last_free, mv88w8618_audio_state), - VMSTATE_UINT32(clock_div, mv88w8618_audio_state), - VMSTATE_END_OF_LIST() - } -}; - -static Property mv88w8618_audio_properties[] = { - DEFINE_PROP_PTR("wm8750", mv88w8618_audio_state, wm), - {/* end of list */}, -}; - -static void mv88w8618_audio_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = mv88w8618_audio_init; - dc->reset = mv88w8618_audio_reset; - dc->vmsd = &mv88w8618_audio_vmsd; - dc->props = mv88w8618_audio_properties; - /* Reason: pointer property "wm8750" */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo mv88w8618_audio_info = { - .name = TYPE_MV88W8618_AUDIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(mv88w8618_audio_state), - .class_init = mv88w8618_audio_class_init, -}; - -static void mv88w8618_register_types(void) -{ - type_register_static(&mv88w8618_audio_info); -} - -type_init(mv88w8618_register_types) diff --git a/qemu/hw/audio/milkymist-ac97.c b/qemu/hw/audio/milkymist-ac97.c deleted file mode 100644 index 6a3b53674..000000000 --- a/qemu/hw/audio/milkymist-ac97.c +++ /dev/null @@ -1,349 +0,0 @@ -/* - * QEMU model of the Milkymist System Controller. - * - * Copyright (c) 2010 Michael Walle - * - * 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 . - * - * - * Specification available at: - * http://www.milkymist.org/socdoc/ac97.pdf - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "audio/audio.h" -#include "qemu/error-report.h" - -enum { - R_AC97_CTRL = 0, - R_AC97_ADDR, - R_AC97_DATAOUT, - R_AC97_DATAIN, - R_D_CTRL, - R_D_ADDR, - R_D_REMAINING, - R_RESERVED, - R_U_CTRL, - R_U_ADDR, - R_U_REMAINING, - R_MAX -}; - -enum { - AC97_CTRL_RQEN = (1<<0), - AC97_CTRL_WRITE = (1<<1), -}; - -enum { - CTRL_EN = (1<<0), -}; - -#define TYPE_MILKYMIST_AC97 "milkymist-ac97" -#define MILKYMIST_AC97(obj) \ - OBJECT_CHECK(MilkymistAC97State, (obj), TYPE_MILKYMIST_AC97) - -struct MilkymistAC97State { - SysBusDevice parent_obj; - - MemoryRegion regs_region; - - QEMUSoundCard card; - SWVoiceIn *voice_in; - SWVoiceOut *voice_out; - - uint32_t regs[R_MAX]; - - qemu_irq crrequest_irq; - qemu_irq crreply_irq; - qemu_irq dmar_irq; - qemu_irq dmaw_irq; -}; -typedef struct MilkymistAC97State MilkymistAC97State; - -static void update_voices(MilkymistAC97State *s) -{ - if (s->regs[R_D_CTRL] & CTRL_EN) { - AUD_set_active_out(s->voice_out, 1); - } else { - AUD_set_active_out(s->voice_out, 0); - } - - if (s->regs[R_U_CTRL] & CTRL_EN) { - AUD_set_active_in(s->voice_in, 1); - } else { - AUD_set_active_in(s->voice_in, 0); - } -} - -static uint64_t ac97_read(void *opaque, hwaddr addr, - unsigned size) -{ - MilkymistAC97State *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_AC97_CTRL: - case R_AC97_ADDR: - case R_AC97_DATAOUT: - case R_AC97_DATAIN: - case R_D_CTRL: - case R_D_ADDR: - case R_D_REMAINING: - case R_U_CTRL: - case R_U_ADDR: - case R_U_REMAINING: - r = s->regs[addr]; - break; - - default: - error_report("milkymist_ac97: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_milkymist_ac97_memory_read(addr << 2, r); - - return r; -} - -static void ac97_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - MilkymistAC97State *s = opaque; - - trace_milkymist_ac97_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_AC97_CTRL: - /* always raise an IRQ according to the direction */ - if (value & AC97_CTRL_RQEN) { - if (value & AC97_CTRL_WRITE) { - trace_milkymist_ac97_pulse_irq_crrequest(); - qemu_irq_pulse(s->crrequest_irq); - } else { - trace_milkymist_ac97_pulse_irq_crreply(); - qemu_irq_pulse(s->crreply_irq); - } - } - - /* RQEN is self clearing */ - s->regs[addr] = value & ~AC97_CTRL_RQEN; - break; - case R_D_CTRL: - case R_U_CTRL: - s->regs[addr] = value; - update_voices(s); - break; - case R_AC97_ADDR: - case R_AC97_DATAOUT: - case R_AC97_DATAIN: - case R_D_ADDR: - case R_D_REMAINING: - case R_U_ADDR: - case R_U_REMAINING: - s->regs[addr] = value; - break; - - default: - error_report("milkymist_ac97: write access to unknown register 0x" - TARGET_FMT_plx, addr); - break; - } - -} - -static const MemoryRegionOps ac97_mmio_ops = { - .read = ac97_read, - .write = ac97_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void ac97_in_cb(void *opaque, int avail_b) -{ - MilkymistAC97State *s = opaque; - uint8_t buf[4096]; - uint32_t remaining = s->regs[R_U_REMAINING]; - int temp = audio_MIN(remaining, avail_b); - uint32_t addr = s->regs[R_U_ADDR]; - int transferred = 0; - - trace_milkymist_ac97_in_cb(avail_b, remaining); - - /* prevent from raising an IRQ */ - if (temp == 0) { - return; - } - - while (temp) { - int acquired, to_copy; - - to_copy = audio_MIN(temp, sizeof(buf)); - acquired = AUD_read(s->voice_in, buf, to_copy); - if (!acquired) { - break; - } - - cpu_physical_memory_write(addr, buf, acquired); - - temp -= acquired; - addr += acquired; - transferred += acquired; - } - - trace_milkymist_ac97_in_cb_transferred(transferred); - - s->regs[R_U_ADDR] = addr; - s->regs[R_U_REMAINING] -= transferred; - - if ((s->regs[R_U_CTRL] & CTRL_EN) && (s->regs[R_U_REMAINING] == 0)) { - trace_milkymist_ac97_pulse_irq_dmaw(); - qemu_irq_pulse(s->dmaw_irq); - } -} - -static void ac97_out_cb(void *opaque, int free_b) -{ - MilkymistAC97State *s = opaque; - uint8_t buf[4096]; - uint32_t remaining = s->regs[R_D_REMAINING]; - int temp = audio_MIN(remaining, free_b); - uint32_t addr = s->regs[R_D_ADDR]; - int transferred = 0; - - trace_milkymist_ac97_out_cb(free_b, remaining); - - /* prevent from raising an IRQ */ - if (temp == 0) { - return; - } - - while (temp) { - int copied, to_copy; - - to_copy = audio_MIN(temp, sizeof(buf)); - cpu_physical_memory_read(addr, buf, to_copy); - copied = AUD_write(s->voice_out, buf, to_copy); - if (!copied) { - break; - } - temp -= copied; - addr += copied; - transferred += copied; - } - - trace_milkymist_ac97_out_cb_transferred(transferred); - - s->regs[R_D_ADDR] = addr; - s->regs[R_D_REMAINING] -= transferred; - - if ((s->regs[R_D_CTRL] & CTRL_EN) && (s->regs[R_D_REMAINING] == 0)) { - trace_milkymist_ac97_pulse_irq_dmar(); - qemu_irq_pulse(s->dmar_irq); - } -} - -static void milkymist_ac97_reset(DeviceState *d) -{ - MilkymistAC97State *s = MILKYMIST_AC97(d); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } - - AUD_set_active_in(s->voice_in, 0); - AUD_set_active_out(s->voice_out, 0); -} - -static int ac97_post_load(void *opaque, int version_id) -{ - MilkymistAC97State *s = opaque; - - update_voices(s); - - return 0; -} - -static int milkymist_ac97_init(SysBusDevice *dev) -{ - MilkymistAC97State *s = MILKYMIST_AC97(dev); - - struct audsettings as; - sysbus_init_irq(dev, &s->crrequest_irq); - sysbus_init_irq(dev, &s->crreply_irq); - sysbus_init_irq(dev, &s->dmar_irq); - sysbus_init_irq(dev, &s->dmaw_irq); - - AUD_register_card("Milkymist AC'97", &s->card); - - as.freq = 48000; - as.nchannels = 2; - as.fmt = AUD_FMT_S16; - as.endianness = 1; - - s->voice_in = AUD_open_in(&s->card, s->voice_in, - "mm_ac97.in", s, ac97_in_cb, &as); - s->voice_out = AUD_open_out(&s->card, s->voice_out, - "mm_ac97.out", s, ac97_out_cb, &as); - - memory_region_init_io(&s->regs_region, OBJECT(s), &ac97_mmio_ops, s, - "milkymist-ac97", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); - - return 0; -} - -static const VMStateDescription vmstate_milkymist_ac97 = { - .name = "milkymist-ac97", - .version_id = 1, - .minimum_version_id = 1, - .post_load = ac97_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, MilkymistAC97State, R_MAX), - VMSTATE_END_OF_LIST() - } -}; - -static void milkymist_ac97_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = milkymist_ac97_init; - dc->reset = milkymist_ac97_reset; - dc->vmsd = &vmstate_milkymist_ac97; -} - -static const TypeInfo milkymist_ac97_info = { - .name = TYPE_MILKYMIST_AC97, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MilkymistAC97State), - .class_init = milkymist_ac97_class_init, -}; - -static void milkymist_ac97_register_types(void) -{ - type_register_static(&milkymist_ac97_info); -} - -type_init(milkymist_ac97_register_types) diff --git a/qemu/hw/audio/pcspk.c b/qemu/hw/audio/pcspk.c deleted file mode 100644 index f9afc8eda..000000000 --- a/qemu/hw/audio/pcspk.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * QEMU PC speaker emulation - * - * Copyright (c) 2006 Joachim Henke - * - * 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/i386/pc.h" -#include "hw/isa/isa.h" -#include "hw/audio/audio.h" -#include "audio/audio.h" -#include "qemu/timer.h" -#include "hw/timer/i8254.h" -#include "hw/audio/pcspk.h" - -#define PCSPK_BUF_LEN 1792 -#define PCSPK_SAMPLE_RATE 32000 -#define PCSPK_MAX_FREQ (PCSPK_SAMPLE_RATE >> 1) -#define PCSPK_MIN_COUNT ((PIT_FREQ + PCSPK_MAX_FREQ - 1) / PCSPK_MAX_FREQ) - -#define PC_SPEAKER(obj) OBJECT_CHECK(PCSpkState, (obj), TYPE_PC_SPEAKER) - -typedef struct { - ISADevice parent_obj; - - MemoryRegion ioport; - uint32_t iobase; - uint8_t sample_buf[PCSPK_BUF_LEN]; - QEMUSoundCard card; - SWVoiceOut *voice; - void *pit; - unsigned int pit_count; - unsigned int samples; - unsigned int play_pos; - int data_on; - int dummy_refresh_clock; -} PCSpkState; - -static const char *s_spk = "pcspk"; -static PCSpkState *pcspk_state; - -static inline void generate_samples(PCSpkState *s) -{ - unsigned int i; - - if (s->pit_count) { - const uint32_t m = PCSPK_SAMPLE_RATE * s->pit_count; - const uint32_t n = ((uint64_t)PIT_FREQ << 32) / m; - - /* multiple of wavelength for gapless looping */ - s->samples = (PCSPK_BUF_LEN * PIT_FREQ / m * m / (PIT_FREQ >> 1) + 1) >> 1; - for (i = 0; i < s->samples; ++i) - s->sample_buf[i] = (64 & (n * i >> 25)) - 32; - } else { - s->samples = PCSPK_BUF_LEN; - for (i = 0; i < PCSPK_BUF_LEN; ++i) - s->sample_buf[i] = 128; /* silence */ - } -} - -static void pcspk_callback(void *opaque, int free) -{ - PCSpkState *s = opaque; - PITChannelInfo ch; - unsigned int n; - - pit_get_channel_info(s->pit, 2, &ch); - - if (ch.mode != 3) { - return; - } - - n = ch.initial_count; - /* avoid frequencies that are not reproducible with sample rate */ - if (n < PCSPK_MIN_COUNT) - n = 0; - - if (s->pit_count != n) { - s->pit_count = n; - s->play_pos = 0; - generate_samples(s); - } - - while (free > 0) { - n = audio_MIN(s->samples - s->play_pos, (unsigned int)free); - n = AUD_write(s->voice, &s->sample_buf[s->play_pos], n); - if (!n) - break; - s->play_pos = (s->play_pos + n) % s->samples; - free -= n; - } -} - -static int pcspk_audio_init(ISABus *bus) -{ - PCSpkState *s = pcspk_state; - struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUD_FMT_U8, 0}; - - AUD_register_card(s_spk, &s->card); - - s->voice = AUD_open_out(&s->card, s->voice, s_spk, s, pcspk_callback, &as); - if (!s->voice) { - AUD_log(s_spk, "Could not open voice\n"); - return -1; - } - - return 0; -} - -static uint64_t pcspk_io_read(void *opaque, hwaddr addr, - unsigned size) -{ - PCSpkState *s = opaque; - PITChannelInfo ch; - - pit_get_channel_info(s->pit, 2, &ch); - - s->dummy_refresh_clock ^= (1 << 4); - - return ch.gate | (s->data_on << 1) | s->dummy_refresh_clock | - (ch.out << 5); -} - -static void pcspk_io_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - PCSpkState *s = opaque; - const int gate = val & 1; - - s->data_on = (val >> 1) & 1; - pit_set_gate(s->pit, 2, gate); - if (s->voice) { - if (gate) /* restart */ - s->play_pos = 0; - AUD_set_active_out(s->voice, gate & s->data_on); - } -} - -static const MemoryRegionOps pcspk_io_ops = { - .read = pcspk_io_read, - .write = pcspk_io_write, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void pcspk_initfn(Object *obj) -{ - PCSpkState *s = PC_SPEAKER(obj); - - memory_region_init_io(&s->ioport, OBJECT(s), &pcspk_io_ops, s, "pcspk", 1); -} - -static void pcspk_realizefn(DeviceState *dev, Error **errp) -{ - ISADevice *isadev = ISA_DEVICE(dev); - PCSpkState *s = PC_SPEAKER(dev); - - isa_register_ioport(isadev, &s->ioport, s->iobase); - - pcspk_state = s; -} - -static Property pcspk_properties[] = { - DEFINE_PROP_UINT32("iobase", PCSpkState, iobase, -1), - DEFINE_PROP_PTR("pit", PCSpkState, pit), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pcspk_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = pcspk_realizefn; - set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - dc->props = pcspk_properties; - /* Reason: pointer property "pit", realize sets global pcspk_state */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo pcspk_info = { - .name = TYPE_PC_SPEAKER, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(PCSpkState), - .instance_init = pcspk_initfn, - .class_init = pcspk_class_initfn, -}; - -static void pcspk_register(void) -{ - type_register_static(&pcspk_info); - isa_register_soundhw("pcspk", "PC speaker", pcspk_audio_init); -} -type_init(pcspk_register) diff --git a/qemu/hw/audio/pl041.c b/qemu/hw/audio/pl041.c deleted file mode 100644 index 4717bc9b9..000000000 --- a/qemu/hw/audio/pl041.c +++ /dev/null @@ -1,650 +0,0 @@ -/* - * Arm PrimeCell PL041 Advanced Audio Codec Interface - * - * Copyright (c) 2011 - * Written by Mathieu Sonet - www.elasticsheep.com - * - * This code is licensed under the GPL. - * - * ***************************************************************** - * - * This driver emulates the ARM AACI interface - * connected to a LM4549 codec. - * - * Limitations: - * - Supports only a playback on one channel (Versatile/Vexpress) - * - Supports only one TX FIFO in compact-mode or non-compact mode. - * - Supports playback of 12, 16, 18 and 20 bits samples. - * - Record is not supported. - * - The PL041 is hardwired to a LM4549 codec. - * - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" - -#include "pl041.h" -#include "lm4549.h" - -#if 0 -#define PL041_DEBUG_LEVEL 1 -#endif - -#if defined(PL041_DEBUG_LEVEL) && (PL041_DEBUG_LEVEL >= 1) -#define DBG_L1(fmt, ...) \ -do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DBG_L1(fmt, ...) \ -do { } while (0) -#endif - -#if defined(PL041_DEBUG_LEVEL) && (PL041_DEBUG_LEVEL >= 2) -#define DBG_L2(fmt, ...) \ -do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DBG_L2(fmt, ...) \ -do { } while (0) -#endif - - -#define MAX_FIFO_DEPTH (1024) -#define DEFAULT_FIFO_DEPTH (8) - -#define SLOT1_RW (1 << 19) - -/* This FIFO only stores 20-bit samples on 32-bit words. - So its level is independent of the selected mode */ -typedef struct { - uint32_t level; - uint32_t data[MAX_FIFO_DEPTH]; -} pl041_fifo; - -typedef struct { - pl041_fifo tx_fifo; - uint8_t tx_enabled; - uint8_t tx_compact_mode; - uint8_t tx_sample_size; - - pl041_fifo rx_fifo; - uint8_t rx_enabled; - uint8_t rx_compact_mode; - uint8_t rx_sample_size; -} pl041_channel; - -#define TYPE_PL041 "pl041" -#define PL041(obj) OBJECT_CHECK(PL041State, (obj), TYPE_PL041) - -typedef struct PL041State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq irq; - - uint32_t fifo_depth; /* FIFO depth in non-compact mode */ - - pl041_regfile regs; - pl041_channel fifo1; - lm4549_state codec; -} PL041State; - - -static const unsigned char pl041_default_id[8] = { - 0x41, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 -}; - -#if defined(PL041_DEBUG_LEVEL) -#define REGISTER(name, offset) #name, -static const char *pl041_regs_name[] = { - #include "pl041.hx" -}; -#undef REGISTER -#endif - - -#if defined(PL041_DEBUG_LEVEL) -static const char *get_reg_name(hwaddr offset) -{ - if (offset <= PL041_dr1_7) { - return pl041_regs_name[offset >> 2]; - } - - return "unknown"; -} -#endif - -static uint8_t pl041_compute_periphid3(PL041State *s) -{ - uint8_t id3 = 1; /* One channel */ - - /* Add the fifo depth information */ - switch (s->fifo_depth) { - case 8: - id3 |= 0 << 3; - break; - case 32: - id3 |= 1 << 3; - break; - case 64: - id3 |= 2 << 3; - break; - case 128: - id3 |= 3 << 3; - break; - case 256: - id3 |= 4 << 3; - break; - case 512: - id3 |= 5 << 3; - break; - case 1024: - id3 |= 6 << 3; - break; - case 2048: - id3 |= 7 << 3; - break; - } - - return id3; -} - -static void pl041_reset(PL041State *s) -{ - DBG_L1("pl041_reset\n"); - - memset(&s->regs, 0x00, sizeof(pl041_regfile)); - - s->regs.slfr = SL1TXEMPTY | SL2TXEMPTY | SL12TXEMPTY; - s->regs.sr1 = TXFE | RXFE | TXHE; - s->regs.isr1 = 0; - - memset(&s->fifo1, 0x00, sizeof(s->fifo1)); -} - - -static void pl041_fifo1_write(PL041State *s, uint32_t value) -{ - pl041_channel *channel = &s->fifo1; - pl041_fifo *fifo = &s->fifo1.tx_fifo; - - /* Push the value in the FIFO */ - if (channel->tx_compact_mode == 0) { - /* Non-compact mode */ - - if (fifo->level < s->fifo_depth) { - /* Pad the value with 0 to obtain a 20-bit sample */ - switch (channel->tx_sample_size) { - case 12: - value = (value << 8) & 0xFFFFF; - break; - case 16: - value = (value << 4) & 0xFFFFF; - break; - case 18: - value = (value << 2) & 0xFFFFF; - break; - case 20: - default: - break; - } - - /* Store the sample in the FIFO */ - fifo->data[fifo->level++] = value; - } -#if defined(PL041_DEBUG_LEVEL) - else { - DBG_L1("fifo1 write: overrun\n"); - } -#endif - } else { - /* Compact mode */ - - if ((fifo->level + 2) < s->fifo_depth) { - uint32_t i = 0; - uint32_t sample = 0; - - for (i = 0; i < 2; i++) { - sample = value & 0xFFFF; - value = value >> 16; - - /* Pad each sample with 0 to obtain a 20-bit sample */ - switch (channel->tx_sample_size) { - case 12: - sample = sample << 8; - break; - case 16: - default: - sample = sample << 4; - break; - } - - /* Store the sample in the FIFO */ - fifo->data[fifo->level++] = sample; - } - } -#if defined(PL041_DEBUG_LEVEL) - else { - DBG_L1("fifo1 write: overrun\n"); - } -#endif - } - - /* Update the status register */ - if (fifo->level > 0) { - s->regs.sr1 &= ~(TXUNDERRUN | TXFE); - } - - if (fifo->level >= (s->fifo_depth / 2)) { - s->regs.sr1 &= ~TXHE; - } - - if (fifo->level >= s->fifo_depth) { - s->regs.sr1 |= TXFF; - } - - DBG_L2("fifo1_push sr1 = 0x%08x\n", s->regs.sr1); -} - -static void pl041_fifo1_transmit(PL041State *s) -{ - pl041_channel *channel = &s->fifo1; - pl041_fifo *fifo = &s->fifo1.tx_fifo; - uint32_t slots = s->regs.txcr1 & TXSLOT_MASK; - uint32_t written_samples; - - /* Check if FIFO1 transmit is enabled */ - if ((channel->tx_enabled) && (slots & (TXSLOT3 | TXSLOT4))) { - if (fifo->level >= (s->fifo_depth / 2)) { - int i; - - DBG_L1("Transfer FIFO level = %i\n", fifo->level); - - /* Try to transfer the whole FIFO */ - for (i = 0; i < (fifo->level / 2); i++) { - uint32_t left = fifo->data[i * 2]; - uint32_t right = fifo->data[i * 2 + 1]; - - /* Transmit two 20-bit samples to the codec */ - if (lm4549_write_samples(&s->codec, left, right) == 0) { - DBG_L1("Codec buffer full\n"); - break; - } - } - - written_samples = i * 2; - if (written_samples > 0) { - /* Update the FIFO level */ - fifo->level -= written_samples; - - /* Move back the pending samples to the start of the FIFO */ - for (i = 0; i < fifo->level; i++) { - fifo->data[i] = fifo->data[written_samples + i]; - } - - /* Update the status register */ - s->regs.sr1 &= ~TXFF; - - if (fifo->level <= (s->fifo_depth / 2)) { - s->regs.sr1 |= TXHE; - } - - if (fifo->level == 0) { - s->regs.sr1 |= TXFE | TXUNDERRUN; - DBG_L1("Empty FIFO\n"); - } - } - } - } -} - -static void pl041_isr1_update(PL041State *s) -{ - /* Update ISR1 */ - if (s->regs.sr1 & TXUNDERRUN) { - s->regs.isr1 |= URINTR; - } else { - s->regs.isr1 &= ~URINTR; - } - - if (s->regs.sr1 & TXHE) { - s->regs.isr1 |= TXINTR; - } else { - s->regs.isr1 &= ~TXINTR; - } - - if (!(s->regs.sr1 & TXBUSY) && (s->regs.sr1 & TXFE)) { - s->regs.isr1 |= TXCINTR; - } else { - s->regs.isr1 &= ~TXCINTR; - } - - /* Update the irq state */ - qemu_set_irq(s->irq, ((s->regs.isr1 & s->regs.ie1) > 0) ? 1 : 0); - DBG_L2("Set interrupt sr1 = 0x%08x isr1 = 0x%08x masked = 0x%08x\n", - s->regs.sr1, s->regs.isr1, s->regs.isr1 & s->regs.ie1); -} - -static void pl041_request_data(void *opaque) -{ - PL041State *s = (PL041State *)opaque; - - /* Trigger pending transfers */ - pl041_fifo1_transmit(s); - pl041_isr1_update(s); -} - -static uint64_t pl041_read(void *opaque, hwaddr offset, - unsigned size) -{ - PL041State *s = (PL041State *)opaque; - int value; - - if ((offset >= PL041_periphid0) && (offset <= PL041_pcellid3)) { - if (offset == PL041_periphid3) { - value = pl041_compute_periphid3(s); - } else { - value = pl041_default_id[(offset - PL041_periphid0) >> 2]; - } - - DBG_L1("pl041_read [0x%08x] => 0x%08x\n", offset, value); - return value; - } else if (offset <= PL041_dr4_7) { - value = *((uint32_t *)&s->regs + (offset >> 2)); - } else { - DBG_L1("pl041_read: Reserved offset %x\n", (int)offset); - return 0; - } - - switch (offset) { - case PL041_allints: - value = s->regs.isr1 & 0x7F; - break; - } - - DBG_L1("pl041_read [0x%08x] %s => 0x%08x\n", offset, - get_reg_name(offset), value); - - return value; -} - -static void pl041_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PL041State *s = (PL041State *)opaque; - uint16_t control, data; - uint32_t result; - - DBG_L1("pl041_write [0x%08x] %s <= 0x%08x\n", offset, - get_reg_name(offset), (unsigned int)value); - - /* Write the register */ - if (offset <= PL041_dr4_7) { - *((uint32_t *)&s->regs + (offset >> 2)) = value; - } else { - DBG_L1("pl041_write: Reserved offset %x\n", (int)offset); - return; - } - - /* Execute the actions */ - switch (offset) { - case PL041_txcr1: - { - pl041_channel *channel = &s->fifo1; - - uint32_t txen = s->regs.txcr1 & TXEN; - uint32_t tsize = (s->regs.txcr1 & TSIZE_MASK) >> TSIZE_MASK_BIT; - uint32_t compact_mode = (s->regs.txcr1 & TXCOMPACT) ? 1 : 0; -#if defined(PL041_DEBUG_LEVEL) - uint32_t slots = (s->regs.txcr1 & TXSLOT_MASK) >> TXSLOT_MASK_BIT; - uint32_t txfen = (s->regs.txcr1 & TXFEN) > 0 ? 1 : 0; -#endif - - DBG_L1("=> txen = %i slots = 0x%01x tsize = %i compact = %i " - "txfen = %i\n", txen, slots, tsize, compact_mode, txfen); - - channel->tx_enabled = txen; - channel->tx_compact_mode = compact_mode; - - switch (tsize) { - case 0: - channel->tx_sample_size = 16; - break; - case 1: - channel->tx_sample_size = 18; - break; - case 2: - channel->tx_sample_size = 20; - break; - case 3: - channel->tx_sample_size = 12; - break; - } - - DBG_L1("TX enabled = %i\n", channel->tx_enabled); - DBG_L1("TX compact mode = %i\n", channel->tx_compact_mode); - DBG_L1("TX sample width = %i\n", channel->tx_sample_size); - - /* Check if compact mode is allowed with selected tsize */ - if (channel->tx_compact_mode == 1) { - if ((channel->tx_sample_size == 18) || - (channel->tx_sample_size == 20)) { - channel->tx_compact_mode = 0; - DBG_L1("Compact mode not allowed with 18/20-bit sample size\n"); - } - } - - break; - } - case PL041_sl1tx: - s->regs.slfr &= ~SL1TXEMPTY; - - control = (s->regs.sl1tx >> 12) & 0x7F; - data = (s->regs.sl2tx >> 4) & 0xFFFF; - - if ((s->regs.sl1tx & SLOT1_RW) == 0) { - /* Write operation */ - lm4549_write(&s->codec, control, data); - } else { - /* Read operation */ - result = lm4549_read(&s->codec, control); - - /* Store the returned value */ - s->regs.sl1rx = s->regs.sl1tx & ~SLOT1_RW; - s->regs.sl2rx = result << 4; - - s->regs.slfr &= ~(SL1RXBUSY | SL2RXBUSY); - s->regs.slfr |= SL1RXVALID | SL2RXVALID; - } - break; - - case PL041_sl2tx: - s->regs.sl2tx = value; - s->regs.slfr &= ~SL2TXEMPTY; - break; - - case PL041_intclr: - DBG_L1("=> Clear interrupt intclr = 0x%08x isr1 = 0x%08x\n", - s->regs.intclr, s->regs.isr1); - - if (s->regs.intclr & TXUEC1) { - s->regs.sr1 &= ~TXUNDERRUN; - } - break; - - case PL041_maincr: - { -#if defined(PL041_DEBUG_LEVEL) - char debug[] = " AACIFE SL1RXEN SL1TXEN"; - if (!(value & AACIFE)) { - debug[0] = '!'; - } - if (!(value & SL1RXEN)) { - debug[8] = '!'; - } - if (!(value & SL1TXEN)) { - debug[17] = '!'; - } - DBG_L1("%s\n", debug); -#endif - - if ((s->regs.maincr & AACIFE) == 0) { - pl041_reset(s); - } - break; - } - - case PL041_dr1_0: - case PL041_dr1_1: - case PL041_dr1_2: - case PL041_dr1_3: - pl041_fifo1_write(s, value); - break; - } - - /* Transmit the FIFO content */ - pl041_fifo1_transmit(s); - - /* Update the ISR1 register */ - pl041_isr1_update(s); -} - -static void pl041_device_reset(DeviceState *d) -{ - PL041State *s = PL041(d); - - pl041_reset(s); -} - -static const MemoryRegionOps pl041_ops = { - .read = pl041_read, - .write = pl041_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int pl041_init(SysBusDevice *dev) -{ - PL041State *s = PL041(dev); - - DBG_L1("pl041_init 0x%08x\n", (uint32_t)s); - - /* Check the device properties */ - switch (s->fifo_depth) { - case 8: - case 32: - case 64: - case 128: - case 256: - case 512: - case 1024: - case 2048: - break; - case 16: - default: - /* NC FIFO depth of 16 is not allowed because its id bits in - AACIPERIPHID3 overlap with the id for the default NC FIFO depth */ - qemu_log_mask(LOG_UNIMP, - "pl041: unsupported non-compact fifo depth [%i]\n", - s->fifo_depth); - return -1; - } - - /* Connect the device to the sysbus */ - memory_region_init_io(&s->iomem, OBJECT(s), &pl041_ops, s, "pl041", 0x1000); - sysbus_init_mmio(dev, &s->iomem); - sysbus_init_irq(dev, &s->irq); - - /* Init the codec */ - lm4549_init(&s->codec, &pl041_request_data, (void *)s); - - return 0; -} - -static const VMStateDescription vmstate_pl041_regfile = { - .name = "pl041_regfile", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { -#define REGISTER(name, offset) VMSTATE_UINT32(name, pl041_regfile), - #include "pl041.hx" -#undef REGISTER - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_pl041_fifo = { - .name = "pl041_fifo", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(level, pl041_fifo), - VMSTATE_UINT32_ARRAY(data, pl041_fifo, MAX_FIFO_DEPTH), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_pl041_channel = { - .name = "pl041_channel", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(tx_fifo, pl041_channel, 0, - vmstate_pl041_fifo, pl041_fifo), - VMSTATE_UINT8(tx_enabled, pl041_channel), - VMSTATE_UINT8(tx_compact_mode, pl041_channel), - VMSTATE_UINT8(tx_sample_size, pl041_channel), - VMSTATE_STRUCT(rx_fifo, pl041_channel, 0, - vmstate_pl041_fifo, pl041_fifo), - VMSTATE_UINT8(rx_enabled, pl041_channel), - VMSTATE_UINT8(rx_compact_mode, pl041_channel), - VMSTATE_UINT8(rx_sample_size, pl041_channel), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_pl041 = { - .name = "pl041", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(fifo_depth, PL041State), - VMSTATE_STRUCT(regs, PL041State, 0, - vmstate_pl041_regfile, pl041_regfile), - VMSTATE_STRUCT(fifo1, PL041State, 0, - vmstate_pl041_channel, pl041_channel), - VMSTATE_STRUCT(codec, PL041State, 0, - vmstate_lm4549_state, lm4549_state), - VMSTATE_END_OF_LIST() - } -}; - -static Property pl041_device_properties[] = { - /* Non-compact FIFO depth property */ - DEFINE_PROP_UINT32("nc_fifo_depth", PL041State, fifo_depth, - DEFAULT_FIFO_DEPTH), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pl041_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = pl041_init; - set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - dc->reset = pl041_device_reset; - dc->vmsd = &vmstate_pl041; - dc->props = pl041_device_properties; -} - -static const TypeInfo pl041_device_info = { - .name = TYPE_PL041, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PL041State), - .class_init = pl041_device_class_init, -}; - -static void pl041_register_types(void) -{ - type_register_static(&pl041_device_info); -} - -type_init(pl041_register_types) diff --git a/qemu/hw/audio/pl041.h b/qemu/hw/audio/pl041.h deleted file mode 100644 index 427ab6d6f..000000000 --- a/qemu/hw/audio/pl041.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Arm PrimeCell PL041 Advanced Audio Codec Interface - * - * Copyright (c) 2011 - * Written by Mathieu Sonet - www.elasticsheep.com - * - * This code is licensed under the GPL. - * - * ***************************************************************** - */ - -#ifndef HW_PL041_H -#define HW_PL041_H - -/* Register file */ -#define REGISTER(name, offset) uint32_t name; -typedef struct { - #include "pl041.hx" -} pl041_regfile; -#undef REGISTER - -/* Register addresses */ -#define REGISTER(name, offset) PL041_##name = offset, -enum { - #include "pl041.hx" - - PL041_periphid0 = 0xFE0, - PL041_periphid1 = 0xFE4, - PL041_periphid2 = 0xFE8, - PL041_periphid3 = 0xFEC, - PL041_pcellid0 = 0xFF0, - PL041_pcellid1 = 0xFF4, - PL041_pcellid2 = 0xFF8, - PL041_pcellid3 = 0xFFC, -}; -#undef REGISTER - -/* Register bits */ - -/* IEx */ -#define TXCIE (1 << 0) -#define RXTIE (1 << 1) -#define TXIE (1 << 2) -#define RXIE (1 << 3) -#define RXOIE (1 << 4) -#define TXUIE (1 << 5) -#define RXTOIE (1 << 6) - -/* TXCRx */ -#define TXEN (1 << 0) -#define TXSLOT1 (1 << 1) -#define TXSLOT2 (1 << 2) -#define TXSLOT3 (1 << 3) -#define TXSLOT4 (1 << 4) -#define TXCOMPACT (1 << 15) -#define TXFEN (1 << 16) - -#define TXSLOT_MASK_BIT (1) -#define TXSLOT_MASK (0xFFF << TXSLOT_MASK_BIT) - -#define TSIZE_MASK_BIT (13) -#define TSIZE_MASK (0x3 << TSIZE_MASK_BIT) - -#define TSIZE_16BITS (0x0 << TSIZE_MASK_BIT) -#define TSIZE_18BITS (0x1 << TSIZE_MASK_BIT) -#define TSIZE_20BITS (0x2 << TSIZE_MASK_BIT) -#define TSIZE_12BITS (0x3 << TSIZE_MASK_BIT) - -/* SRx */ -#define RXFE (1 << 0) -#define TXFE (1 << 1) -#define RXHF (1 << 2) -#define TXHE (1 << 3) -#define RXFF (1 << 4) -#define TXFF (1 << 5) -#define RXBUSY (1 << 6) -#define TXBUSY (1 << 7) -#define RXOVERRUN (1 << 8) -#define TXUNDERRUN (1 << 9) -#define RXTIMEOUT (1 << 10) -#define RXTOFE (1 << 11) - -/* ISRx */ -#define TXCINTR (1 << 0) -#define RXTOINTR (1 << 1) -#define TXINTR (1 << 2) -#define RXINTR (1 << 3) -#define ORINTR (1 << 4) -#define URINTR (1 << 5) -#define RXTOFEINTR (1 << 6) - -/* SLFR */ -#define SL1RXBUSY (1 << 0) -#define SL1TXBUSY (1 << 1) -#define SL2RXBUSY (1 << 2) -#define SL2TXBUSY (1 << 3) -#define SL12RXBUSY (1 << 4) -#define SL12TXBUSY (1 << 5) -#define SL1RXVALID (1 << 6) -#define SL1TXEMPTY (1 << 7) -#define SL2RXVALID (1 << 8) -#define SL2TXEMPTY (1 << 9) -#define SL12RXVALID (1 << 10) -#define SL12TXEMPTY (1 << 11) -#define RAWGPIOINT (1 << 12) -#define RWIS (1 << 13) - -/* MAINCR */ -#define AACIFE (1 << 0) -#define LOOPBACK (1 << 1) -#define LOWPOWER (1 << 2) -#define SL1RXEN (1 << 3) -#define SL1TXEN (1 << 4) -#define SL2RXEN (1 << 5) -#define SL2TXEN (1 << 6) -#define SL12RXEN (1 << 7) -#define SL12TXEN (1 << 8) -#define DMAENABLE (1 << 9) - -/* INTCLR */ -#define WISC (1 << 0) -#define RXOEC1 (1 << 1) -#define RXOEC2 (1 << 2) -#define RXOEC3 (1 << 3) -#define RXOEC4 (1 << 4) -#define TXUEC1 (1 << 5) -#define TXUEC2 (1 << 6) -#define TXUEC3 (1 << 7) -#define TXUEC4 (1 << 8) -#define RXTOFEC1 (1 << 9) -#define RXTOFEC2 (1 << 10) -#define RXTOFEC3 (1 << 11) -#define RXTOFEC4 (1 << 12) - -#endif /* #ifndef HW_PL041_H */ diff --git a/qemu/hw/audio/pl041.hx b/qemu/hw/audio/pl041.hx deleted file mode 100644 index dd7188cbc..000000000 --- a/qemu/hw/audio/pl041.hx +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Arm PrimeCell PL041 Advanced Audio Codec Interface - * - * Copyright (c) 2011 - * Written by Mathieu Sonet - www.elasticsheep.com - * - * This code is licensed under the GPL. - * - * ***************************************************************** - */ - -/* PL041 register file description */ - -REGISTER( rxcr1, 0x00 ) -REGISTER( txcr1, 0x04 ) -REGISTER( sr1, 0x08 ) -REGISTER( isr1, 0x0C ) -REGISTER( ie1, 0x10 ) -REGISTER( rxcr2, 0x14 ) -REGISTER( txcr2, 0x18 ) -REGISTER( sr2, 0x1C ) -REGISTER( isr2, 0x20 ) -REGISTER( ie2, 0x24 ) -REGISTER( rxcr3, 0x28 ) -REGISTER( txcr3, 0x2C ) -REGISTER( sr3, 0x30 ) -REGISTER( isr3, 0x34 ) -REGISTER( ie3, 0x38 ) -REGISTER( rxcr4, 0x3C ) -REGISTER( txcr4, 0x40 ) -REGISTER( sr4, 0x44 ) -REGISTER( isr4, 0x48 ) -REGISTER( ie4, 0x4C ) -REGISTER( sl1rx, 0x50 ) -REGISTER( sl1tx, 0x54 ) -REGISTER( sl2rx, 0x58 ) -REGISTER( sl2tx, 0x5C ) -REGISTER( sl12rx, 0x60 ) -REGISTER( sl12tx, 0x64 ) -REGISTER( slfr, 0x68 ) -REGISTER( slistat, 0x6C ) -REGISTER( slien, 0x70 ) -REGISTER( intclr, 0x74 ) -REGISTER( maincr, 0x78 ) -REGISTER( reset, 0x7C ) -REGISTER( sync, 0x80 ) -REGISTER( allints, 0x84 ) -REGISTER( mainfr, 0x88 ) -REGISTER( unused, 0x8C ) -REGISTER( dr1_0, 0x90 ) -REGISTER( dr1_1, 0x94 ) -REGISTER( dr1_2, 0x98 ) -REGISTER( dr1_3, 0x9C ) -REGISTER( dr1_4, 0xA0 ) -REGISTER( dr1_5, 0xA4 ) -REGISTER( dr1_6, 0xA8 ) -REGISTER( dr1_7, 0xAC ) -REGISTER( dr2_0, 0xB0 ) -REGISTER( dr2_1, 0xB4 ) -REGISTER( dr2_2, 0xB8 ) -REGISTER( dr2_3, 0xBC ) -REGISTER( dr2_4, 0xC0 ) -REGISTER( dr2_5, 0xC4 ) -REGISTER( dr2_6, 0xC8 ) -REGISTER( dr2_7, 0xCC ) -REGISTER( dr3_0, 0xD0 ) -REGISTER( dr3_1, 0xD4 ) -REGISTER( dr3_2, 0xD8 ) -REGISTER( dr3_3, 0xDC ) -REGISTER( dr3_4, 0xE0 ) -REGISTER( dr3_5, 0xE4 ) -REGISTER( dr3_6, 0xE8 ) -REGISTER( dr3_7, 0xEC ) -REGISTER( dr4_0, 0xF0 ) -REGISTER( dr4_1, 0xF4 ) -REGISTER( dr4_2, 0xF8 ) -REGISTER( dr4_3, 0xFC ) -REGISTER( dr4_4, 0x100 ) -REGISTER( dr4_5, 0x104 ) -REGISTER( dr4_6, 0x108 ) -REGISTER( dr4_7, 0x10C ) diff --git a/qemu/hw/audio/sb16.c b/qemu/hw/audio/sb16.c deleted file mode 100644 index 3a4a57ac3..000000000 --- a/qemu/hw/audio/sb16.c +++ /dev/null @@ -1,1436 +0,0 @@ -/* - * QEMU Soundblaster 16 emulation - * - * Copyright (c) 2003-2005 Vassili Karpov (malc) - * - * 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/audio/audio.h" -#include "audio/audio.h" -#include "hw/isa/isa.h" -#include "hw/qdev.h" -#include "qemu/timer.h" -#include "qemu/host-utils.h" - -#define dolog(...) AUD_log ("sb16", __VA_ARGS__) - -/* #define DEBUG */ -/* #define DEBUG_SB16_MOST */ - -#ifdef DEBUG -#define ldebug(...) dolog (__VA_ARGS__) -#else -#define ldebug(...) -#endif - -static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992."; - -#define TYPE_SB16 "sb16" -#define SB16(obj) OBJECT_CHECK (SB16State, (obj), TYPE_SB16) - -typedef struct SB16State { - ISADevice parent_obj; - - QEMUSoundCard card; - qemu_irq pic; - uint32_t irq; - uint32_t dma; - uint32_t hdma; - uint32_t port; - uint32_t ver; - IsaDma *isa_dma; - IsaDma *isa_hdma; - - int in_index; - int out_data_len; - int fmt_stereo; - int fmt_signed; - int fmt_bits; - audfmt_e fmt; - int dma_auto; - int block_size; - int fifo; - int freq; - int time_const; - int speaker; - int needed_bytes; - int cmd; - int use_hdma; - int highspeed; - int can_write; - - int v2x6; - - uint8_t csp_param; - uint8_t csp_value; - uint8_t csp_mode; - uint8_t csp_regs[256]; - uint8_t csp_index; - uint8_t csp_reg83[4]; - int csp_reg83r; - int csp_reg83w; - - uint8_t in2_data[10]; - uint8_t out_data[50]; - uint8_t test_reg; - uint8_t last_read_byte; - int nzero; - - int left_till_irq; - - int dma_running; - int bytes_per_second; - int align; - int audio_free; - SWVoiceOut *voice; - - QEMUTimer *aux_ts; - /* mixer state */ - int mixer_nreg; - uint8_t mixer_regs[256]; -} SB16State; - -static void SB_audio_callback (void *opaque, int free); - -static int magic_of_irq (int irq) -{ - switch (irq) { - case 5: - return 2; - case 7: - return 4; - case 9: - return 1; - case 10: - return 8; - default: - dolog ("bad irq %d\n", irq); - return 2; - } -} - -static int irq_of_magic (int magic) -{ - switch (magic) { - case 1: - return 9; - case 2: - return 5; - case 4: - return 7; - case 8: - return 10; - default: - dolog ("bad irq magic %d\n", magic); - return -1; - } -} - -#if 0 -static void log_dsp (SB16State *dsp) -{ - ldebug ("%s:%s:%d:%s:dmasize=%d:freq=%d:const=%d:speaker=%d\n", - dsp->fmt_stereo ? "Stereo" : "Mono", - dsp->fmt_signed ? "Signed" : "Unsigned", - dsp->fmt_bits, - dsp->dma_auto ? "Auto" : "Single", - dsp->block_size, - dsp->freq, - dsp->time_const, - dsp->speaker); -} -#endif - -static void speaker (SB16State *s, int on) -{ - s->speaker = on; - /* AUD_enable (s->voice, 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) { - k->hold_DREQ(isa_dma, dma); - AUD_set_active_out (s->voice, 1); - } - else { - k->release_DREQ(isa_dma, dma); - AUD_set_active_out (s->voice, 0); - } -} - -static void aux_timer (void *opaque) -{ - SB16State *s = opaque; - s->can_write = 1; - qemu_irq_raise (s->pic); -} - -#define DMA8_AUTO 1 -#define DMA8_HIGH 2 - -static void continue_dma8 (SB16State *s) -{ - if (s->freq > 0) { - struct audsettings as; - - s->audio_free = 0; - - as.freq = s->freq; - as.nchannels = 1 << s->fmt_stereo; - as.fmt = s->fmt; - as.endianness = 0; - - s->voice = AUD_open_out ( - &s->card, - s->voice, - "sb16", - s, - SB_audio_callback, - &as - ); - } - - control (s, 1); -} - -static void dma_cmd8 (SB16State *s, int mask, int dma_len) -{ - s->fmt = AUD_FMT_U8; - s->use_hdma = 0; - s->fmt_bits = 8; - s->fmt_signed = 0; - s->fmt_stereo = (s->mixer_regs[0x0e] & 2) != 0; - if (-1 == s->time_const) { - if (s->freq <= 0) - s->freq = 11025; - } - else { - int tmp = (256 - s->time_const); - s->freq = (1000000 + (tmp / 2)) / tmp; - } - - if (dma_len != -1) { - s->block_size = dma_len << s->fmt_stereo; - } - else { - /* This is apparently the only way to make both Act1/PL - and SecondReality/FC work - - Act1 sets block size via command 0x48 and it's an odd number - SR does the same with even number - Both use stereo, and Creatives own documentation states that - 0x48 sets block size in bytes less one.. go figure */ - s->block_size &= ~s->fmt_stereo; - } - - s->freq >>= s->fmt_stereo; - s->left_till_irq = s->block_size; - s->bytes_per_second = (s->freq << s->fmt_stereo); - /* s->highspeed = (mask & DMA8_HIGH) != 0; */ - s->dma_auto = (mask & DMA8_AUTO) != 0; - s->align = (1 << s->fmt_stereo) - 1; - - if (s->block_size & s->align) { - dolog ("warning: misaligned block size %d, alignment %d\n", - s->block_size, s->align + 1); - } - - ldebug ("freq %d, stereo %d, sign %d, bits %d, " - "dma %d, auto %d, fifo %d, high %d\n", - s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits, - s->block_size, s->dma_auto, s->fifo, s->highspeed); - - continue_dma8 (s); - speaker (s, 1); -} - -static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len) -{ - s->use_hdma = cmd < 0xc0; - s->fifo = (cmd >> 1) & 1; - s->dma_auto = (cmd >> 2) & 1; - s->fmt_signed = (d0 >> 4) & 1; - s->fmt_stereo = (d0 >> 5) & 1; - - switch (cmd >> 4) { - case 11: - s->fmt_bits = 16; - break; - - case 12: - s->fmt_bits = 8; - break; - } - - if (-1 != s->time_const) { -#if 1 - int tmp = 256 - s->time_const; - s->freq = (1000000 + (tmp / 2)) / tmp; -#else - /* s->freq = 1000000 / ((255 - s->time_const) << s->fmt_stereo); */ - s->freq = 1000000 / ((255 - s->time_const)); -#endif - s->time_const = -1; - } - - s->block_size = dma_len + 1; - s->block_size <<= (s->fmt_bits == 16); - if (!s->dma_auto) { - /* It is clear that for DOOM and auto-init this value - shouldn't take stereo into account, while Miles Sound Systems - setsound.exe with single transfer mode wouldn't work without it - wonders of SB16 yet again */ - s->block_size <<= s->fmt_stereo; - } - - ldebug ("freq %d, stereo %d, sign %d, bits %d, " - "dma %d, auto %d, fifo %d, high %d\n", - s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits, - s->block_size, s->dma_auto, s->fifo, s->highspeed); - - if (16 == s->fmt_bits) { - if (s->fmt_signed) { - s->fmt = AUD_FMT_S16; - } - else { - s->fmt = AUD_FMT_U16; - } - } - else { - if (s->fmt_signed) { - s->fmt = AUD_FMT_S8; - } - else { - s->fmt = AUD_FMT_U8; - } - } - - s->left_till_irq = s->block_size; - - s->bytes_per_second = (s->freq << s->fmt_stereo) << (s->fmt_bits == 16); - s->highspeed = 0; - s->align = (1 << (s->fmt_stereo + (s->fmt_bits == 16))) - 1; - if (s->block_size & s->align) { - dolog ("warning: misaligned block size %d, alignment %d\n", - s->block_size, s->align + 1); - } - - if (s->freq) { - struct audsettings as; - - s->audio_free = 0; - - as.freq = s->freq; - as.nchannels = 1 << s->fmt_stereo; - as.fmt = s->fmt; - as.endianness = 0; - - s->voice = AUD_open_out ( - &s->card, - s->voice, - "sb16", - s, - SB_audio_callback, - &as - ); - } - - control (s, 1); - speaker (s, 1); -} - -static inline void dsp_out_data (SB16State *s, uint8_t val) -{ - ldebug ("outdata %#x\n", val); - if ((size_t) s->out_data_len < sizeof (s->out_data)) { - s->out_data[s->out_data_len++] = val; - } -} - -static inline uint8_t dsp_get_data (SB16State *s) -{ - if (s->in_index) { - return s->in2_data[--s->in_index]; - } - else { - dolog ("buffer underflow\n"); - return 0; - } -} - -static void command (SB16State *s, uint8_t cmd) -{ - ldebug ("command %#x\n", cmd); - - if (cmd > 0xaf && cmd < 0xd0) { - if (cmd & 8) { - dolog ("ADC not yet supported (command %#x)\n", cmd); - } - - switch (cmd >> 4) { - case 11: - case 12: - break; - default: - dolog ("%#x wrong bits\n", cmd); - } - s->needed_bytes = 3; - } - else { - s->needed_bytes = 0; - - switch (cmd) { - case 0x03: - dsp_out_data (s, 0x10); /* s->csp_param); */ - goto warn; - - case 0x04: - s->needed_bytes = 1; - goto warn; - - case 0x05: - s->needed_bytes = 2; - goto warn; - - case 0x08: - /* __asm__ ("int3"); */ - goto warn; - - case 0x0e: - s->needed_bytes = 2; - goto warn; - - case 0x09: - dsp_out_data (s, 0xf8); - goto warn; - - case 0x0f: - s->needed_bytes = 1; - goto warn; - - case 0x10: - s->needed_bytes = 1; - goto warn; - - case 0x14: - s->needed_bytes = 2; - s->block_size = 0; - break; - - case 0x1c: /* Auto-Initialize DMA DAC, 8-bit */ - dma_cmd8 (s, DMA8_AUTO, -1); - break; - - case 0x20: /* Direct ADC, Juice/PL */ - dsp_out_data (s, 0xff); - goto warn; - - case 0x35: - dolog ("0x35 - MIDI command not implemented\n"); - break; - - case 0x40: - s->freq = -1; - s->time_const = -1; - s->needed_bytes = 1; - break; - - case 0x41: - s->freq = -1; - s->time_const = -1; - s->needed_bytes = 2; - break; - - case 0x42: - s->freq = -1; - s->time_const = -1; - s->needed_bytes = 2; - goto warn; - - case 0x45: - dsp_out_data (s, 0xaa); - goto warn; - - case 0x47: /* Continue Auto-Initialize DMA 16bit */ - break; - - case 0x48: - s->needed_bytes = 2; - break; - - case 0x74: - s->needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */ - dolog ("0x75 - DMA DAC, 4-bit ADPCM not implemented\n"); - break; - - case 0x75: /* DMA DAC, 4-bit ADPCM Reference */ - s->needed_bytes = 2; - dolog ("0x74 - DMA DAC, 4-bit ADPCM Reference not implemented\n"); - break; - - case 0x76: /* DMA DAC, 2.6-bit ADPCM */ - s->needed_bytes = 2; - dolog ("0x74 - DMA DAC, 2.6-bit ADPCM not implemented\n"); - break; - - case 0x77: /* DMA DAC, 2.6-bit ADPCM Reference */ - s->needed_bytes = 2; - dolog ("0x74 - DMA DAC, 2.6-bit ADPCM Reference not implemented\n"); - break; - - case 0x7d: - dolog ("0x7d - Autio-Initialize DMA DAC, 4-bit ADPCM Reference\n"); - dolog ("not implemented\n"); - break; - - case 0x7f: - dolog ( - "0x7d - Autio-Initialize DMA DAC, 2.6-bit ADPCM Reference\n" - ); - dolog ("not implemented\n"); - break; - - case 0x80: - s->needed_bytes = 2; - break; - - case 0x90: - case 0x91: - dma_cmd8 (s, ((cmd & 1) == 0) | DMA8_HIGH, -1); - break; - - case 0xd0: /* halt DMA operation. 8bit */ - control (s, 0); - break; - - case 0xd1: /* speaker on */ - speaker (s, 1); - break; - - case 0xd3: /* speaker off */ - speaker (s, 0); - break; - - case 0xd4: /* continue DMA operation. 8bit */ - /* KQ6 (or maybe Sierras audblst.drv in general) resets - the frequency between halt/continue */ - continue_dma8 (s); - break; - - case 0xd5: /* halt DMA operation. 16bit */ - control (s, 0); - break; - - case 0xd6: /* continue DMA operation. 16bit */ - control (s, 1); - break; - - case 0xd9: /* exit auto-init DMA after this block. 16bit */ - s->dma_auto = 0; - break; - - case 0xda: /* exit auto-init DMA after this block. 8bit */ - s->dma_auto = 0; - break; - - case 0xe0: /* DSP identification */ - s->needed_bytes = 1; - break; - - case 0xe1: - dsp_out_data (s, s->ver & 0xff); - dsp_out_data (s, s->ver >> 8); - break; - - case 0xe2: - s->needed_bytes = 1; - goto warn; - - case 0xe3: - { - int i; - for (i = sizeof (e3) - 1; i >= 0; --i) - dsp_out_data (s, e3[i]); - } - break; - - case 0xe4: /* write test reg */ - s->needed_bytes = 1; - break; - - case 0xe7: - dolog ("Attempt to probe for ESS (0xe7)?\n"); - break; - - case 0xe8: /* read test reg */ - dsp_out_data (s, s->test_reg); - break; - - case 0xf2: - case 0xf3: - dsp_out_data (s, 0xaa); - s->mixer_regs[0x82] |= (cmd == 0xf2) ? 1 : 2; - qemu_irq_raise (s->pic); - break; - - case 0xf9: - s->needed_bytes = 1; - goto warn; - - case 0xfa: - dsp_out_data (s, 0); - goto warn; - - case 0xfc: /* FIXME */ - dsp_out_data (s, 0); - goto warn; - - default: - dolog ("Unrecognized command %#x\n", cmd); - break; - } - } - - if (!s->needed_bytes) { - ldebug ("\n"); - } - - exit: - if (!s->needed_bytes) { - s->cmd = -1; - } - else { - s->cmd = cmd; - } - return; - - warn: - dolog ("warning: command %#x,%d is not truly understood yet\n", - cmd, s->needed_bytes); - goto exit; - -} - -static uint16_t dsp_get_lohi (SB16State *s) -{ - uint8_t hi = dsp_get_data (s); - uint8_t lo = dsp_get_data (s); - return (hi << 8) | lo; -} - -static uint16_t dsp_get_hilo (SB16State *s) -{ - uint8_t lo = dsp_get_data (s); - uint8_t hi = dsp_get_data (s); - return (hi << 8) | lo; -} - -static void complete (SB16State *s) -{ - int d0, d1, d2; - ldebug ("complete command %#x, in_index %d, needed_bytes %d\n", - s->cmd, s->in_index, s->needed_bytes); - - if (s->cmd > 0xaf && s->cmd < 0xd0) { - d2 = dsp_get_data (s); - d1 = dsp_get_data (s); - d0 = dsp_get_data (s); - - if (s->cmd & 8) { - dolog ("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", - s->cmd, d0, d1, d2); - } - else { - ldebug ("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", - s->cmd, d0, d1, d2); - dma_cmd (s, s->cmd, d0, d1 + (d2 << 8)); - } - } - else { - switch (s->cmd) { - case 0x04: - s->csp_mode = dsp_get_data (s); - s->csp_reg83r = 0; - s->csp_reg83w = 0; - ldebug ("CSP command 0x04: mode=%#x\n", s->csp_mode); - break; - - case 0x05: - s->csp_param = dsp_get_data (s); - s->csp_value = dsp_get_data (s); - ldebug ("CSP command 0x05: param=%#x value=%#x\n", - s->csp_param, - s->csp_value); - break; - - case 0x0e: - d0 = dsp_get_data (s); - d1 = dsp_get_data (s); - ldebug ("write CSP register %d <- %#x\n", d1, d0); - if (d1 == 0x83) { - ldebug ("0x83[%d] <- %#x\n", s->csp_reg83r, d0); - s->csp_reg83[s->csp_reg83r % 4] = d0; - s->csp_reg83r += 1; - } - else { - s->csp_regs[d1] = d0; - } - break; - - case 0x0f: - d0 = dsp_get_data (s); - ldebug ("read CSP register %#x -> %#x, mode=%#x\n", - d0, s->csp_regs[d0], s->csp_mode); - if (d0 == 0x83) { - ldebug ("0x83[%d] -> %#x\n", - s->csp_reg83w, - s->csp_reg83[s->csp_reg83w % 4]); - dsp_out_data (s, s->csp_reg83[s->csp_reg83w % 4]); - s->csp_reg83w += 1; - } - else { - dsp_out_data (s, s->csp_regs[d0]); - } - break; - - case 0x10: - d0 = dsp_get_data (s); - dolog ("cmd 0x10 d0=%#x\n", d0); - break; - - case 0x14: - dma_cmd8 (s, 0, dsp_get_lohi (s) + 1); - break; - - case 0x40: - s->time_const = dsp_get_data (s); - ldebug ("set time const %d\n", s->time_const); - break; - - case 0x42: /* FT2 sets output freq with this, go figure */ -#if 0 - dolog ("cmd 0x42 might not do what it think it should\n"); -#endif - case 0x41: - s->freq = dsp_get_hilo (s); - ldebug ("set freq %d\n", s->freq); - break; - - case 0x48: - s->block_size = dsp_get_lohi (s) + 1; - ldebug ("set dma block len %d\n", s->block_size); - break; - - case 0x74: - case 0x75: - case 0x76: - case 0x77: - /* ADPCM stuff, ignore */ - break; - - case 0x80: - { - int freq, samples, bytes; - int64_t ticks; - - 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, NANOSECONDS_PER_SECOND, freq); - if (ticks < NANOSECONDS_PER_SECOND / 1024) { - qemu_irq_raise (s->pic); - } - else { - if (s->aux_ts) { - timer_mod ( - s->aux_ts, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ticks - ); - } - } - ldebug ("mix silence %d %d %" PRId64 "\n", samples, bytes, ticks); - } - break; - - case 0xe0: - d0 = dsp_get_data (s); - s->out_data_len = 0; - ldebug ("E0 data = %#x\n", d0); - dsp_out_data (s, ~d0); - break; - - case 0xe2: -#ifdef DEBUG - d0 = dsp_get_data (s); - dolog ("E2 = %#x\n", d0); -#endif - break; - - case 0xe4: - s->test_reg = dsp_get_data (s); - break; - - case 0xf9: - d0 = dsp_get_data (s); - ldebug ("command 0xf9 with %#x\n", d0); - switch (d0) { - case 0x0e: - dsp_out_data (s, 0xff); - break; - - case 0x0f: - dsp_out_data (s, 0x07); - break; - - case 0x37: - dsp_out_data (s, 0x38); - break; - - default: - dsp_out_data (s, 0x00); - break; - } - break; - - default: - dolog ("complete: unrecognized command %#x\n", s->cmd); - return; - } - } - - ldebug ("\n"); - s->cmd = -1; -} - -static void legacy_reset (SB16State *s) -{ - struct audsettings as; - - s->freq = 11025; - s->fmt_signed = 0; - s->fmt_bits = 8; - s->fmt_stereo = 0; - - as.freq = s->freq; - as.nchannels = 1; - as.fmt = AUD_FMT_U8; - as.endianness = 0; - - s->voice = AUD_open_out ( - &s->card, - s->voice, - "sb16", - s, - SB_audio_callback, - &as - ); - - /* Not sure about that... */ - /* AUD_set_active_out (s->voice, 1); */ -} - -static void reset (SB16State *s) -{ - qemu_irq_lower (s->pic); - if (s->dma_auto) { - qemu_irq_raise (s->pic); - qemu_irq_lower (s->pic); - } - - s->mixer_regs[0x82] = 0; - s->dma_auto = 0; - s->in_index = 0; - s->out_data_len = 0; - s->left_till_irq = 0; - s->needed_bytes = 0; - s->block_size = -1; - s->nzero = 0; - s->highspeed = 0; - s->v2x6 = 0; - s->cmd = -1; - - dsp_out_data (s, 0xaa); - speaker (s, 0); - control (s, 0); - legacy_reset (s); -} - -static void dsp_write(void *opaque, uint32_t nport, uint32_t val) -{ - SB16State *s = opaque; - int iport; - - iport = nport - s->port; - - ldebug ("write %#x <- %#x\n", nport, val); - switch (iport) { - case 0x06: - switch (val) { - case 0x00: - if (s->v2x6 == 1) { - reset (s); - } - s->v2x6 = 0; - break; - - case 0x01: - case 0x03: /* FreeBSD kludge */ - s->v2x6 = 1; - break; - - case 0xc6: - s->v2x6 = 0; /* Prince of Persia, csp.sys, diagnose.exe */ - break; - - case 0xb8: /* Panic */ - reset (s); - break; - - case 0x39: - dsp_out_data (s, 0x38); - reset (s); - s->v2x6 = 0x39; - break; - - default: - s->v2x6 = val; - break; - } - break; - - case 0x0c: /* write data or command | write status */ -/* if (s->highspeed) */ -/* break; */ - - if (s->needed_bytes == 0) { - command (s, val); -#if 0 - if (0 == s->needed_bytes) { - log_dsp (s); - } -#endif - } - else { - if (s->in_index == sizeof (s->in2_data)) { - dolog ("in data overrun\n"); - } - else { - s->in2_data[s->in_index++] = val; - if (s->in_index == s->needed_bytes) { - s->needed_bytes = 0; - complete (s); -#if 0 - log_dsp (s); -#endif - } - } - } - break; - - default: - ldebug ("(nport=%#x, val=%#x)\n", nport, val); - break; - } -} - -static uint32_t dsp_read(void *opaque, uint32_t nport) -{ - SB16State *s = opaque; - int iport, retval, ack = 0; - - iport = nport - s->port; - - switch (iport) { - case 0x06: /* reset */ - retval = 0xff; - break; - - case 0x0a: /* read data */ - if (s->out_data_len) { - retval = s->out_data[--s->out_data_len]; - s->last_read_byte = retval; - } - else { - if (s->cmd != -1) { - dolog ("empty output buffer for command %#x\n", - s->cmd); - } - retval = s->last_read_byte; - /* goto error; */ - } - break; - - case 0x0c: /* 0 can write */ - retval = s->can_write ? 0 : 0x80; - break; - - case 0x0d: /* timer interrupt clear */ - /* dolog ("timer interrupt clear\n"); */ - retval = 0; - break; - - case 0x0e: /* data available status | irq 8 ack */ - retval = (!s->out_data_len || s->highspeed) ? 0 : 0x80; - if (s->mixer_regs[0x82] & 1) { - ack = 1; - s->mixer_regs[0x82] &= ~1; - qemu_irq_lower (s->pic); - } - break; - - case 0x0f: /* irq 16 ack */ - retval = 0xff; - if (s->mixer_regs[0x82] & 2) { - ack = 1; - s->mixer_regs[0x82] &= ~2; - qemu_irq_lower (s->pic); - } - break; - - default: - goto error; - } - - if (!ack) { - ldebug ("read %#x -> %#x\n", nport, retval); - } - - return retval; - - error: - dolog ("warning: dsp_read %#x error\n", nport); - return 0xff; -} - -static void reset_mixer (SB16State *s) -{ - int i; - - memset (s->mixer_regs, 0xff, 0x7f); - memset (s->mixer_regs + 0x83, 0xff, sizeof (s->mixer_regs) - 0x83); - - s->mixer_regs[0x02] = 4; /* master volume 3bits */ - s->mixer_regs[0x06] = 4; /* MIDI volume 3bits */ - s->mixer_regs[0x08] = 0; /* CD volume 3bits */ - s->mixer_regs[0x0a] = 0; /* voice volume 2bits */ - - /* d5=input filt, d3=lowpass filt, d1,d2=input source */ - s->mixer_regs[0x0c] = 0; - - /* d5=output filt, d1=stereo switch */ - s->mixer_regs[0x0e] = 0; - - /* voice volume L d5,d7, R d1,d3 */ - s->mixer_regs[0x04] = (4 << 5) | (4 << 1); - /* master ... */ - s->mixer_regs[0x22] = (4 << 5) | (4 << 1); - /* MIDI ... */ - s->mixer_regs[0x26] = (4 << 5) | (4 << 1); - - for (i = 0x30; i < 0x48; i++) { - s->mixer_regs[i] = 0x20; - } -} - -static void mixer_write_indexb(void *opaque, uint32_t nport, uint32_t val) -{ - SB16State *s = opaque; - (void) nport; - s->mixer_nreg = val; -} - -static void mixer_write_datab(void *opaque, uint32_t nport, uint32_t val) -{ - SB16State *s = opaque; - - (void) nport; - ldebug ("mixer_write [%#x] <- %#x\n", s->mixer_nreg, val); - - switch (s->mixer_nreg) { - case 0x00: - reset_mixer (s); - break; - - case 0x80: - { - int irq = irq_of_magic (val); - ldebug ("setting irq to %d (val=%#x)\n", irq, val); - if (irq > 0) { - s->irq = irq; - } - } - break; - - case 0x81: - { - int dma, hdma; - - dma = ctz32 (val & 0xf); - hdma = ctz32 (val & 0xf0); - if (dma != s->dma || hdma != s->hdma) { - dolog ( - "attempt to change DMA " - "8bit %d(%d), 16bit %d(%d) (val=%#x)\n", - dma, s->dma, hdma, s->hdma, val); - } -#if 0 - s->dma = dma; - s->hdma = hdma; -#endif - } - break; - - case 0x82: - dolog ("attempt to write into IRQ status register (val=%#x)\n", - val); - return; - - default: - if (s->mixer_nreg >= 0x80) { - ldebug ("attempt to write mixer[%#x] <- %#x\n", s->mixer_nreg, val); - } - break; - } - - s->mixer_regs[s->mixer_nreg] = val; -} - -static uint32_t mixer_read(void *opaque, uint32_t nport) -{ - SB16State *s = opaque; - - (void) nport; -#ifndef DEBUG_SB16_MOST - if (s->mixer_nreg != 0x82) { - ldebug ("mixer_read[%#x] -> %#x\n", - s->mixer_nreg, s->mixer_regs[s->mixer_nreg]); - } -#else - ldebug ("mixer_read[%#x] -> %#x\n", - s->mixer_nreg, s->mixer_regs[s->mixer_nreg]); -#endif - return s->mixer_regs[s->mixer_nreg]; -} - -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]; - - temp = len; - net = 0; - - while (temp) { - int left = dma_len - dma_pos; - int copied; - size_t to_copy; - - to_copy = audio_MIN (temp, left); - if (to_copy > sizeof (tmpbuf)) { - to_copy = sizeof (tmpbuf); - } - - copied = k->read_memory(isa_dma, nchan, tmpbuf, dma_pos, to_copy); - copied = AUD_write (s->voice, tmpbuf, copied); - - temp -= copied; - dma_pos = (dma_pos + copied) % dma_len; - net += copied; - - if (!copied) { - break; - } - } - - return net; -} - -static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) -{ - SB16State *s = opaque; - int till, copy, written, free; - - if (s->block_size <= 0) { - dolog ("invalid block size=%d nchan=%d dma_pos=%d dma_len=%d\n", - s->block_size, nchan, dma_pos, dma_len); - return dma_pos; - } - - if (s->left_till_irq < 0) { - s->left_till_irq = s->block_size; - } - - if (s->voice) { - free = s->audio_free & ~s->align; - if ((free <= 0) || !dma_len) { - return dma_pos; - } - } - else { - free = dma_len; - } - - copy = free; - till = s->left_till_irq; - -#ifdef DEBUG_SB16_MOST - dolog ("pos:%06d %d till:%d len:%d\n", - dma_pos, free, till, dma_len); -#endif - - if (till <= copy) { - if (s->dma_auto == 0) { - copy = till; - } - } - - written = write_audio (s, nchan, dma_pos, dma_len, copy); - dma_pos = (dma_pos + written) % dma_len; - s->left_till_irq -= written; - - if (s->left_till_irq <= 0) { - s->mixer_regs[0x82] |= (nchan & 4) ? 2 : 1; - qemu_irq_raise (s->pic); - if (s->dma_auto == 0) { - control (s, 0); - speaker (s, 0); - } - } - -#ifdef DEBUG_SB16_MOST - ldebug ("pos %5d free %5d size %5d till % 5d copy %5d written %5d size %5d\n", - dma_pos, free, dma_len, s->left_till_irq, copy, written, - s->block_size); -#endif - - while (s->left_till_irq <= 0) { - s->left_till_irq = s->block_size + s->left_till_irq; - } - - return dma_pos; -} - -static void SB_audio_callback (void *opaque, int free) -{ - SB16State *s = opaque; - s->audio_free = free; -} - -static int sb16_post_load (void *opaque, int version_id) -{ - SB16State *s = opaque; - - if (s->voice) { - AUD_close_out (&s->card, s->voice); - s->voice = NULL; - } - - if (s->dma_running) { - if (s->freq) { - struct audsettings as; - - s->audio_free = 0; - - as.freq = s->freq; - as.nchannels = 1 << s->fmt_stereo; - as.fmt = s->fmt; - as.endianness = 0; - - s->voice = AUD_open_out ( - &s->card, - s->voice, - "sb16", - s, - SB_audio_callback, - &as - ); - } - - control (s, 1); - speaker (s, s->speaker); - } - return 0; -} - -static const VMStateDescription vmstate_sb16 = { - .name = "sb16", - .version_id = 1, - .minimum_version_id = 1, - .post_load = sb16_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32 (irq, SB16State), - VMSTATE_UINT32 (dma, SB16State), - VMSTATE_UINT32 (hdma, SB16State), - VMSTATE_UINT32 (port, SB16State), - VMSTATE_UINT32 (ver, SB16State), - VMSTATE_INT32 (in_index, SB16State), - VMSTATE_INT32 (out_data_len, SB16State), - VMSTATE_INT32 (fmt_stereo, SB16State), - VMSTATE_INT32 (fmt_signed, SB16State), - VMSTATE_INT32 (fmt_bits, SB16State), - VMSTATE_UINT32 (fmt, SB16State), - VMSTATE_INT32 (dma_auto, SB16State), - VMSTATE_INT32 (block_size, SB16State), - VMSTATE_INT32 (fifo, SB16State), - VMSTATE_INT32 (freq, SB16State), - VMSTATE_INT32 (time_const, SB16State), - VMSTATE_INT32 (speaker, SB16State), - VMSTATE_INT32 (needed_bytes, SB16State), - VMSTATE_INT32 (cmd, SB16State), - VMSTATE_INT32 (use_hdma, SB16State), - VMSTATE_INT32 (highspeed, SB16State), - VMSTATE_INT32 (can_write, SB16State), - VMSTATE_INT32 (v2x6, SB16State), - - VMSTATE_UINT8 (csp_param, SB16State), - VMSTATE_UINT8 (csp_value, SB16State), - VMSTATE_UINT8 (csp_mode, SB16State), - VMSTATE_UINT8 (csp_param, SB16State), - VMSTATE_BUFFER (csp_regs, SB16State), - VMSTATE_UINT8 (csp_index, SB16State), - VMSTATE_BUFFER (csp_reg83, SB16State), - VMSTATE_INT32 (csp_reg83r, SB16State), - VMSTATE_INT32 (csp_reg83w, SB16State), - - VMSTATE_BUFFER (in2_data, SB16State), - VMSTATE_BUFFER (out_data, SB16State), - VMSTATE_UINT8 (test_reg, SB16State), - VMSTATE_UINT8 (last_read_byte, SB16State), - - VMSTATE_INT32 (nzero, SB16State), - VMSTATE_INT32 (left_till_irq, SB16State), - VMSTATE_INT32 (dma_running, SB16State), - VMSTATE_INT32 (bytes_per_second, SB16State), - VMSTATE_INT32 (align, SB16State), - - VMSTATE_INT32 (mixer_nreg, SB16State), - VMSTATE_BUFFER (mixer_regs, SB16State), - - VMSTATE_END_OF_LIST () - } -}; - -static const MemoryRegionPortio sb16_ioport_list[] = { - { 4, 1, 1, .write = mixer_write_indexb }, - { 5, 1, 1, .read = mixer_read, .write = mixer_write_datab }, - { 6, 1, 1, .read = dsp_read, .write = dsp_write }, - { 10, 1, 1, .read = dsp_read }, - { 12, 1, 1, .write = dsp_write }, - { 12, 4, 1, .read = dsp_read }, - PORTIO_END_OF_LIST (), -}; - - -static void sb16_initfn (Object *obj) -{ - SB16State *s = SB16 (obj); - - s->cmd = -1; -} - -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); - - s->mixer_regs[0x80] = magic_of_irq (s->irq); - s->mixer_regs[0x81] = (1 << s->dma) | (1 << s->hdma); - s->mixer_regs[0x82] = 2 << 5; - - s->csp_regs[5] = 1; - s->csp_regs[9] = 0xf8; - - reset_mixer (s); - s->aux_ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, aux_timer, s); - if (!s->aux_ts) { - dolog ("warning: Could not create auxiliary timer\n"); - } - - isa_register_portio_list (isadev, s->port, sb16_ioport_list, s, "sb16"); - - 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); -} - -static int SB16_init (ISABus *bus) -{ - isa_create_simple (bus, TYPE_SB16); - return 0; -} - -static Property sb16_properties[] = { - DEFINE_PROP_UINT32 ("version", SB16State, ver, 0x0405), /* 4.5 */ - DEFINE_PROP_UINT32 ("iobase", SB16State, port, 0x220), - DEFINE_PROP_UINT32 ("irq", SB16State, irq, 5), - DEFINE_PROP_UINT32 ("dma", SB16State, dma, 1), - DEFINE_PROP_UINT32 ("dma16", SB16State, hdma, 5), - DEFINE_PROP_END_OF_LIST (), -}; - -static void sb16_class_initfn (ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS (klass); - - dc->realize = sb16_realizefn; - set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - dc->desc = "Creative Sound Blaster 16"; - dc->vmsd = &vmstate_sb16; - dc->props = sb16_properties; -} - -static const TypeInfo sb16_info = { - .name = TYPE_SB16, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof (SB16State), - .instance_init = sb16_initfn, - .class_init = sb16_class_initfn, -}; - -static void sb16_register_types (void) -{ - type_register_static (&sb16_info); - isa_register_soundhw("sb16", "Creative Sound Blaster 16", SB16_init); -} - -type_init (sb16_register_types) diff --git a/qemu/hw/audio/wm8750.c b/qemu/hw/audio/wm8750.c deleted file mode 100644 index 0c6500e96..000000000 --- a/qemu/hw/audio/wm8750.c +++ /dev/null @@ -1,723 +0,0 @@ -/* - * WM8750 audio CODEC. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This file is licensed under GNU GPL. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/i2c/i2c.h" -#include "audio/audio.h" - -#define IN_PORT_N 3 -#define OUT_PORT_N 3 - -#define CODEC "wm8750" - -typedef struct { - int adc; - int adc_hz; - int dac; - int dac_hz; -} WMRate; - -#define TYPE_WM8750 "wm8750" -#define WM8750(obj) OBJECT_CHECK(WM8750State, (obj), TYPE_WM8750) - -typedef struct WM8750State { - I2CSlave parent_obj; - - uint8_t i2c_data[2]; - int i2c_len; - QEMUSoundCard card; - SWVoiceIn *adc_voice[IN_PORT_N]; - SWVoiceOut *dac_voice[OUT_PORT_N]; - int enable; - void (*data_req)(void *, int, int); - void *opaque; - uint8_t data_in[4096]; - uint8_t data_out[4096]; - int idx_in, req_in; - int idx_out, req_out; - - SWVoiceOut **out[2]; - uint8_t outvol[7], outmute[2]; - SWVoiceIn **in[2]; - uint8_t invol[4], inmute[2]; - - uint8_t diff[2], pol, ds, monomix[2], alc, mute; - uint8_t path[4], mpath[2], power, format; - const WMRate *rate; - uint8_t rate_vmstate; - int adc_hz, dac_hz, ext_adc_hz, ext_dac_hz, master; -} WM8750State; - -/* pow(10.0, -i / 20.0) * 255, i = 0..42 */ -static const uint8_t wm8750_vol_db_table[] = { - 255, 227, 203, 181, 161, 143, 128, 114, 102, 90, 81, 72, 64, 57, 51, 45, - 40, 36, 32, 29, 26, 23, 20, 18, 16, 14, 13, 11, 10, 9, 8, 7, 6, 6, 5, 5, - 4, 4, 3, 3, 3, 2, 2 -}; - -#define WM8750_OUTVOL_TRANSFORM(x) wm8750_vol_db_table[(0x7f - x) / 3] -#define WM8750_INVOL_TRANSFORM(x) (x << 2) - -static inline void wm8750_in_load(WM8750State *s) -{ - if (s->idx_in + s->req_in <= sizeof(s->data_in)) - return; - s->idx_in = audio_MAX(0, (int) sizeof(s->data_in) - s->req_in); - AUD_read(*s->in[0], s->data_in + s->idx_in, - sizeof(s->data_in) - s->idx_in); -} - -static inline void wm8750_out_flush(WM8750State *s) -{ - int sent = 0; - while (sent < s->idx_out) - sent += AUD_write(*s->out[0], s->data_out + sent, s->idx_out - sent) - ?: s->idx_out; - s->idx_out = 0; -} - -static void wm8750_audio_in_cb(void *opaque, int avail_b) -{ - WM8750State *s = (WM8750State *) opaque; - s->req_in = avail_b; - s->data_req(s->opaque, s->req_out >> 2, avail_b >> 2); -} - -static void wm8750_audio_out_cb(void *opaque, int free_b) -{ - WM8750State *s = (WM8750State *) opaque; - - if (s->idx_out >= free_b) { - s->idx_out = free_b; - s->req_out = 0; - wm8750_out_flush(s); - } else - s->req_out = free_b - s->idx_out; - - s->data_req(s->opaque, s->req_out >> 2, s->req_in >> 2); -} - -static const WMRate wm_rate_table[] = { - { 256, 48000, 256, 48000 }, /* SR: 00000 */ - { 384, 48000, 384, 48000 }, /* SR: 00001 */ - { 256, 48000, 1536, 8000 }, /* SR: 00010 */ - { 384, 48000, 2304, 8000 }, /* SR: 00011 */ - { 1536, 8000, 256, 48000 }, /* SR: 00100 */ - { 2304, 8000, 384, 48000 }, /* SR: 00101 */ - { 1536, 8000, 1536, 8000 }, /* SR: 00110 */ - { 2304, 8000, 2304, 8000 }, /* SR: 00111 */ - { 1024, 12000, 1024, 12000 }, /* SR: 01000 */ - { 1526, 12000, 1536, 12000 }, /* SR: 01001 */ - { 768, 16000, 768, 16000 }, /* SR: 01010 */ - { 1152, 16000, 1152, 16000 }, /* SR: 01011 */ - { 384, 32000, 384, 32000 }, /* SR: 01100 */ - { 576, 32000, 576, 32000 }, /* SR: 01101 */ - { 128, 96000, 128, 96000 }, /* SR: 01110 */ - { 192, 96000, 192, 96000 }, /* SR: 01111 */ - { 256, 44100, 256, 44100 }, /* SR: 10000 */ - { 384, 44100, 384, 44100 }, /* SR: 10001 */ - { 256, 44100, 1408, 8018 }, /* SR: 10010 */ - { 384, 44100, 2112, 8018 }, /* SR: 10011 */ - { 1408, 8018, 256, 44100 }, /* SR: 10100 */ - { 2112, 8018, 384, 44100 }, /* SR: 10101 */ - { 1408, 8018, 1408, 8018 }, /* SR: 10110 */ - { 2112, 8018, 2112, 8018 }, /* SR: 10111 */ - { 1024, 11025, 1024, 11025 }, /* SR: 11000 */ - { 1536, 11025, 1536, 11025 }, /* SR: 11001 */ - { 512, 22050, 512, 22050 }, /* SR: 11010 */ - { 768, 22050, 768, 22050 }, /* SR: 11011 */ - { 512, 24000, 512, 24000 }, /* SR: 11100 */ - { 768, 24000, 768, 24000 }, /* SR: 11101 */ - { 128, 88200, 128, 88200 }, /* SR: 11110 */ - { 192, 88200, 192, 88200 }, /* SR: 11111 */ -}; - -static void wm8750_vol_update(WM8750State *s) -{ - /* FIXME: multiply all volumes by s->invol[2], s->invol[3] */ - - AUD_set_volume_in(s->adc_voice[0], s->mute, - s->inmute[0] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[0]), - s->inmute[1] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[1])); - AUD_set_volume_in(s->adc_voice[1], s->mute, - s->inmute[0] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[0]), - s->inmute[1] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[1])); - AUD_set_volume_in(s->adc_voice[2], s->mute, - s->inmute[0] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[0]), - s->inmute[1] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[1])); - - /* FIXME: multiply all volumes by s->outvol[0], s->outvol[1] */ - - /* Speaker: LOUT2VOL ROUT2VOL */ - AUD_set_volume_out(s->dac_voice[0], s->mute, - s->outmute[0] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[4]), - s->outmute[1] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[5])); - - /* Headphone: LOUT1VOL ROUT1VOL */ - AUD_set_volume_out(s->dac_voice[1], s->mute, - s->outmute[0] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[2]), - s->outmute[1] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[3])); - - /* MONOOUT: MONOVOL MONOVOL */ - AUD_set_volume_out(s->dac_voice[2], s->mute, - s->outmute[0] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[6]), - s->outmute[1] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[6])); -} - -static void wm8750_set_format(WM8750State *s) -{ - int i; - struct audsettings in_fmt; - struct audsettings out_fmt; - - wm8750_out_flush(s); - - if (s->in[0] && *s->in[0]) - AUD_set_active_in(*s->in[0], 0); - if (s->out[0] && *s->out[0]) - AUD_set_active_out(*s->out[0], 0); - - for (i = 0; i < IN_PORT_N; i ++) - if (s->adc_voice[i]) { - AUD_close_in(&s->card, s->adc_voice[i]); - s->adc_voice[i] = NULL; - } - for (i = 0; i < OUT_PORT_N; i ++) - if (s->dac_voice[i]) { - AUD_close_out(&s->card, s->dac_voice[i]); - s->dac_voice[i] = NULL; - } - - if (!s->enable) - return; - - /* Setup input */ - in_fmt.endianness = 0; - in_fmt.nchannels = 2; - in_fmt.freq = s->adc_hz; - in_fmt.fmt = AUD_FMT_S16; - - s->adc_voice[0] = AUD_open_in(&s->card, s->adc_voice[0], - CODEC ".input1", s, wm8750_audio_in_cb, &in_fmt); - s->adc_voice[1] = AUD_open_in(&s->card, s->adc_voice[1], - CODEC ".input2", s, wm8750_audio_in_cb, &in_fmt); - s->adc_voice[2] = AUD_open_in(&s->card, s->adc_voice[2], - CODEC ".input3", s, wm8750_audio_in_cb, &in_fmt); - - /* Setup output */ - out_fmt.endianness = 0; - out_fmt.nchannels = 2; - out_fmt.freq = s->dac_hz; - out_fmt.fmt = AUD_FMT_S16; - - s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0], - CODEC ".speaker", s, wm8750_audio_out_cb, &out_fmt); - s->dac_voice[1] = AUD_open_out(&s->card, s->dac_voice[1], - CODEC ".headphone", s, wm8750_audio_out_cb, &out_fmt); - /* MONOMIX is also in stereo for simplicity */ - s->dac_voice[2] = AUD_open_out(&s->card, s->dac_voice[2], - CODEC ".monomix", s, wm8750_audio_out_cb, &out_fmt); - /* no sense emulating OUT3 which is a mix of other outputs */ - - wm8750_vol_update(s); - - /* We should connect the left and right channels to their - * respective inputs/outputs but we have completely no need - * for mixing or combining paths to different ports, so we - * connect both channels to where the left channel is routed. */ - if (s->in[0] && *s->in[0]) - AUD_set_active_in(*s->in[0], 1); - if (s->out[0] && *s->out[0]) - AUD_set_active_out(*s->out[0], 1); -} - -static void wm8750_clk_update(WM8750State *s, int ext) -{ - if (s->master || !s->ext_dac_hz) - s->dac_hz = s->rate->dac_hz; - else - s->dac_hz = s->ext_dac_hz; - - if (s->master || !s->ext_adc_hz) - s->adc_hz = s->rate->adc_hz; - else - s->adc_hz = s->ext_adc_hz; - - if (s->master || (!s->ext_dac_hz && !s->ext_adc_hz)) { - if (!ext) - wm8750_set_format(s); - } else { - if (ext) - wm8750_set_format(s); - } -} - -static void wm8750_reset(I2CSlave *i2c) -{ - WM8750State *s = WM8750(i2c); - - s->rate = &wm_rate_table[0]; - s->enable = 0; - wm8750_clk_update(s, 1); - s->diff[0] = 0; - s->diff[1] = 0; - s->ds = 0; - s->alc = 0; - s->in[0] = &s->adc_voice[0]; - s->invol[0] = 0x17; - s->invol[1] = 0x17; - s->invol[2] = 0xc3; - s->invol[3] = 0xc3; - s->out[0] = &s->dac_voice[0]; - s->outvol[0] = 0xff; - s->outvol[1] = 0xff; - s->outvol[2] = 0x79; - s->outvol[3] = 0x79; - s->outvol[4] = 0x79; - s->outvol[5] = 0x79; - s->outvol[6] = 0x79; - s->inmute[0] = 0; - s->inmute[1] = 0; - s->outmute[0] = 0; - s->outmute[1] = 0; - s->mute = 1; - s->path[0] = 0; - s->path[1] = 0; - s->path[2] = 0; - s->path[3] = 0; - s->mpath[0] = 0; - s->mpath[1] = 0; - s->format = 0x0a; - s->idx_in = sizeof(s->data_in); - s->req_in = 0; - s->idx_out = 0; - s->req_out = 0; - wm8750_vol_update(s); - s->i2c_len = 0; -} - -static void wm8750_event(I2CSlave *i2c, enum i2c_event event) -{ - WM8750State *s = WM8750(i2c); - - switch (event) { - case I2C_START_SEND: - s->i2c_len = 0; - break; - case I2C_FINISH: -#ifdef VERBOSE - if (s->i2c_len < 2) - printf("%s: message too short (%i bytes)\n", - __FUNCTION__, s->i2c_len); -#endif - break; - default: - break; - } -} - -#define WM8750_LINVOL 0x00 -#define WM8750_RINVOL 0x01 -#define WM8750_LOUT1V 0x02 -#define WM8750_ROUT1V 0x03 -#define WM8750_ADCDAC 0x05 -#define WM8750_IFACE 0x07 -#define WM8750_SRATE 0x08 -#define WM8750_LDAC 0x0a -#define WM8750_RDAC 0x0b -#define WM8750_BASS 0x0c -#define WM8750_TREBLE 0x0d -#define WM8750_RESET 0x0f -#define WM8750_3D 0x10 -#define WM8750_ALC1 0x11 -#define WM8750_ALC2 0x12 -#define WM8750_ALC3 0x13 -#define WM8750_NGATE 0x14 -#define WM8750_LADC 0x15 -#define WM8750_RADC 0x16 -#define WM8750_ADCTL1 0x17 -#define WM8750_ADCTL2 0x18 -#define WM8750_PWR1 0x19 -#define WM8750_PWR2 0x1a -#define WM8750_ADCTL3 0x1b -#define WM8750_ADCIN 0x1f -#define WM8750_LADCIN 0x20 -#define WM8750_RADCIN 0x21 -#define WM8750_LOUTM1 0x22 -#define WM8750_LOUTM2 0x23 -#define WM8750_ROUTM1 0x24 -#define WM8750_ROUTM2 0x25 -#define WM8750_MOUTM1 0x26 -#define WM8750_MOUTM2 0x27 -#define WM8750_LOUT2V 0x28 -#define WM8750_ROUT2V 0x29 -#define WM8750_MOUTV 0x2a - -static int wm8750_tx(I2CSlave *i2c, uint8_t data) -{ - WM8750State *s = WM8750(i2c); - uint8_t cmd; - uint16_t value; - - if (s->i2c_len >= 2) { -#ifdef VERBOSE - printf("%s: long message (%i bytes)\n", __func__, s->i2c_len); -#endif - return 1; - } - s->i2c_data[s->i2c_len ++] = data; - if (s->i2c_len != 2) - return 0; - - cmd = s->i2c_data[0] >> 1; - value = ((s->i2c_data[0] << 8) | s->i2c_data[1]) & 0x1ff; - - switch (cmd) { - case WM8750_LADCIN: /* ADC Signal Path Control (Left) */ - s->diff[0] = (((value >> 6) & 3) == 3); /* LINSEL */ - if (s->diff[0]) - s->in[0] = &s->adc_voice[0 + s->ds * 1]; - else - s->in[0] = &s->adc_voice[((value >> 6) & 3) * 1 + 0]; - break; - - case WM8750_RADCIN: /* ADC Signal Path Control (Right) */ - s->diff[1] = (((value >> 6) & 3) == 3); /* RINSEL */ - if (s->diff[1]) - s->in[1] = &s->adc_voice[0 + s->ds * 1]; - else - s->in[1] = &s->adc_voice[((value >> 6) & 3) * 1 + 0]; - break; - - case WM8750_ADCIN: /* ADC Input Mode */ - s->ds = (value >> 8) & 1; /* DS */ - if (s->diff[0]) - s->in[0] = &s->adc_voice[0 + s->ds * 1]; - if (s->diff[1]) - s->in[1] = &s->adc_voice[0 + s->ds * 1]; - s->monomix[0] = (value >> 6) & 3; /* MONOMIX */ - break; - - case WM8750_ADCTL1: /* Additional Control (1) */ - s->monomix[1] = (value >> 1) & 1; /* DMONOMIX */ - break; - - case WM8750_PWR1: /* Power Management (1) */ - s->enable = ((value >> 6) & 7) == 3; /* VMIDSEL, VREF */ - wm8750_set_format(s); - break; - - case WM8750_LINVOL: /* Left Channel PGA */ - s->invol[0] = value & 0x3f; /* LINVOL */ - s->inmute[0] = (value >> 7) & 1; /* LINMUTE */ - wm8750_vol_update(s); - break; - - case WM8750_RINVOL: /* Right Channel PGA */ - s->invol[1] = value & 0x3f; /* RINVOL */ - s->inmute[1] = (value >> 7) & 1; /* RINMUTE */ - wm8750_vol_update(s); - break; - - case WM8750_ADCDAC: /* ADC and DAC Control */ - s->pol = (value >> 5) & 3; /* ADCPOL */ - s->mute = (value >> 3) & 1; /* DACMU */ - wm8750_vol_update(s); - break; - - case WM8750_ADCTL3: /* Additional Control (3) */ - break; - - case WM8750_LADC: /* Left ADC Digital Volume */ - s->invol[2] = value & 0xff; /* LADCVOL */ - wm8750_vol_update(s); - break; - - case WM8750_RADC: /* Right ADC Digital Volume */ - s->invol[3] = value & 0xff; /* RADCVOL */ - wm8750_vol_update(s); - break; - - case WM8750_ALC1: /* ALC Control (1) */ - s->alc = (value >> 7) & 3; /* ALCSEL */ - break; - - case WM8750_NGATE: /* Noise Gate Control */ - case WM8750_3D: /* 3D enhance */ - break; - - case WM8750_LDAC: /* Left Channel Digital Volume */ - s->outvol[0] = value & 0xff; /* LDACVOL */ - wm8750_vol_update(s); - break; - - case WM8750_RDAC: /* Right Channel Digital Volume */ - s->outvol[1] = value & 0xff; /* RDACVOL */ - wm8750_vol_update(s); - break; - - case WM8750_BASS: /* Bass Control */ - break; - - case WM8750_LOUTM1: /* Left Mixer Control (1) */ - s->path[0] = (value >> 8) & 1; /* LD2LO */ - /* TODO: mute/unmute respective paths */ - wm8750_vol_update(s); - break; - - case WM8750_LOUTM2: /* Left Mixer Control (2) */ - s->path[1] = (value >> 8) & 1; /* RD2LO */ - /* TODO: mute/unmute respective paths */ - wm8750_vol_update(s); - break; - - case WM8750_ROUTM1: /* Right Mixer Control (1) */ - s->path[2] = (value >> 8) & 1; /* LD2RO */ - /* TODO: mute/unmute respective paths */ - wm8750_vol_update(s); - break; - - case WM8750_ROUTM2: /* Right Mixer Control (2) */ - s->path[3] = (value >> 8) & 1; /* RD2RO */ - /* TODO: mute/unmute respective paths */ - wm8750_vol_update(s); - break; - - case WM8750_MOUTM1: /* Mono Mixer Control (1) */ - s->mpath[0] = (value >> 8) & 1; /* LD2MO */ - /* TODO: mute/unmute respective paths */ - wm8750_vol_update(s); - break; - - case WM8750_MOUTM2: /* Mono Mixer Control (2) */ - s->mpath[1] = (value >> 8) & 1; /* RD2MO */ - /* TODO: mute/unmute respective paths */ - wm8750_vol_update(s); - break; - - case WM8750_LOUT1V: /* LOUT1 Volume */ - s->outvol[2] = value & 0x7f; /* LOUT1VOL */ - wm8750_vol_update(s); - break; - - case WM8750_LOUT2V: /* LOUT2 Volume */ - s->outvol[4] = value & 0x7f; /* LOUT2VOL */ - wm8750_vol_update(s); - break; - - case WM8750_ROUT1V: /* ROUT1 Volume */ - s->outvol[3] = value & 0x7f; /* ROUT1VOL */ - wm8750_vol_update(s); - break; - - case WM8750_ROUT2V: /* ROUT2 Volume */ - s->outvol[5] = value & 0x7f; /* ROUT2VOL */ - wm8750_vol_update(s); - break; - - case WM8750_MOUTV: /* MONOOUT Volume */ - s->outvol[6] = value & 0x7f; /* MONOOUTVOL */ - wm8750_vol_update(s); - break; - - case WM8750_ADCTL2: /* Additional Control (2) */ - break; - - case WM8750_PWR2: /* Power Management (2) */ - s->power = value & 0x7e; - /* TODO: mute/unmute respective paths */ - wm8750_vol_update(s); - break; - - case WM8750_IFACE: /* Digital Audio Interface Format */ - s->format = value; - s->master = (value >> 6) & 1; /* MS */ - wm8750_clk_update(s, s->master); - break; - - case WM8750_SRATE: /* Clocking and Sample Rate Control */ - s->rate = &wm_rate_table[(value >> 1) & 0x1f]; - wm8750_clk_update(s, 0); - break; - - case WM8750_RESET: /* Reset */ - wm8750_reset(I2C_SLAVE(s)); - break; - -#ifdef VERBOSE - default: - printf("%s: unknown register %02x\n", __FUNCTION__, cmd); -#endif - } - - return 0; -} - -static int wm8750_rx(I2CSlave *i2c) -{ - return 0x00; -} - -static void wm8750_pre_save(void *opaque) -{ - WM8750State *s = opaque; - - s->rate_vmstate = s->rate - wm_rate_table; -} - -static int wm8750_post_load(void *opaque, int version_id) -{ - WM8750State *s = opaque; - - s->rate = &wm_rate_table[s->rate_vmstate & 0x1f]; - return 0; -} - -static const VMStateDescription vmstate_wm8750 = { - .name = CODEC, - .version_id = 0, - .minimum_version_id = 0, - .pre_save = wm8750_pre_save, - .post_load = wm8750_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8_ARRAY(i2c_data, WM8750State, 2), - VMSTATE_INT32(i2c_len, WM8750State), - VMSTATE_INT32(enable, WM8750State), - VMSTATE_INT32(idx_in, WM8750State), - VMSTATE_INT32(req_in, WM8750State), - VMSTATE_INT32(idx_out, WM8750State), - VMSTATE_INT32(req_out, WM8750State), - VMSTATE_UINT8_ARRAY(outvol, WM8750State, 7), - VMSTATE_UINT8_ARRAY(outmute, WM8750State, 2), - VMSTATE_UINT8_ARRAY(invol, WM8750State, 4), - VMSTATE_UINT8_ARRAY(inmute, WM8750State, 2), - VMSTATE_UINT8_ARRAY(diff, WM8750State, 2), - VMSTATE_UINT8(pol, WM8750State), - VMSTATE_UINT8(ds, WM8750State), - VMSTATE_UINT8_ARRAY(monomix, WM8750State, 2), - VMSTATE_UINT8(alc, WM8750State), - VMSTATE_UINT8(mute, WM8750State), - VMSTATE_UINT8_ARRAY(path, WM8750State, 4), - VMSTATE_UINT8_ARRAY(mpath, WM8750State, 2), - VMSTATE_UINT8(format, WM8750State), - VMSTATE_UINT8(power, WM8750State), - VMSTATE_UINT8(rate_vmstate, WM8750State), - VMSTATE_I2C_SLAVE(parent_obj, WM8750State), - VMSTATE_END_OF_LIST() - } -}; - -static int wm8750_init(I2CSlave *i2c) -{ - WM8750State *s = WM8750(i2c); - - AUD_register_card(CODEC, &s->card); - wm8750_reset(I2C_SLAVE(s)); - - return 0; -} - -#if 0 -static void wm8750_fini(I2CSlave *i2c) -{ - WM8750State *s = WM8750(i2c); - - wm8750_reset(I2C_SLAVE(s)); - AUD_remove_card(&s->card); - g_free(s); -} -#endif - -void wm8750_data_req_set(DeviceState *dev, - void (*data_req)(void *, int, int), void *opaque) -{ - WM8750State *s = WM8750(dev); - - s->data_req = data_req; - s->opaque = opaque; -} - -void wm8750_dac_dat(void *opaque, uint32_t sample) -{ - WM8750State *s = (WM8750State *) opaque; - - *(uint32_t *) &s->data_out[s->idx_out] = sample; - s->req_out -= 4; - s->idx_out += 4; - if (s->idx_out >= sizeof(s->data_out) || s->req_out <= 0) - wm8750_out_flush(s); -} - -void *wm8750_dac_buffer(void *opaque, int samples) -{ - WM8750State *s = (WM8750State *) opaque; - /* XXX: Should check if there are samples free samples available */ - void *ret = s->data_out + s->idx_out; - - s->idx_out += samples << 2; - s->req_out -= samples << 2; - return ret; -} - -void wm8750_dac_commit(void *opaque) -{ - WM8750State *s = (WM8750State *) opaque; - - wm8750_out_flush(s); -} - -uint32_t wm8750_adc_dat(void *opaque) -{ - WM8750State *s = (WM8750State *) opaque; - uint32_t *data; - - if (s->idx_in >= sizeof(s->data_in)) - wm8750_in_load(s); - - data = (uint32_t *) &s->data_in[s->idx_in]; - s->req_in -= 4; - s->idx_in += 4; - return *data; -} - -void wm8750_set_bclk_in(void *opaque, int new_hz) -{ - WM8750State *s = (WM8750State *) opaque; - - s->ext_adc_hz = new_hz; - s->ext_dac_hz = new_hz; - wm8750_clk_update(s, 1); -} - -static void wm8750_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); - - sc->init = wm8750_init; - sc->event = wm8750_event; - sc->recv = wm8750_rx; - sc->send = wm8750_tx; - dc->vmsd = &vmstate_wm8750; -} - -static const TypeInfo wm8750_info = { - .name = TYPE_WM8750, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(WM8750State), - .class_init = wm8750_class_init, -}; - -static void wm8750_register_types(void) -{ - type_register_static(&wm8750_info); -} - -type_init(wm8750_register_types) diff --git a/qemu/hw/block/Makefile.objs b/qemu/hw/block/Makefile.objs deleted file mode 100644 index d4c3ab758..000000000 --- a/qemu/hw/block/Makefile.objs +++ /dev/null @@ -1,15 +0,0 @@ -common-obj-y += block.o cdrom.o hd-geometry.o -common-obj-$(CONFIG_FDC) += fdc.o -common-obj-$(CONFIG_SSI_M25P80) += m25p80.o -common-obj-$(CONFIG_NAND) += nand.o -common-obj-$(CONFIG_PFLASH_CFI01) += pflash_cfi01.o -common-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o -common-obj-$(CONFIG_XEN_BACKEND) += xen_disk.o -common-obj-$(CONFIG_ECC) += ecc.o -common-obj-$(CONFIG_ONENAND) += onenand.o -common-obj-$(CONFIG_NVME_PCI) += nvme.o - -obj-$(CONFIG_SH4) += tc58128.o - -obj-$(CONFIG_VIRTIO) += virtio-blk.o -obj-$(CONFIG_VIRTIO) += dataplane/ diff --git a/qemu/hw/block/block.c b/qemu/hw/block/block.c deleted file mode 100644 index 97a59d4fa..000000000 --- a/qemu/hw/block/block.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Common code for block device models - * - * Copyright (C) 2012 Red Hat, Inc. - * - * 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 "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) -{ - DriveInfo *dinfo; - - if (!*serial) { - /* try to fall back to value set with legacy -drive serial=... */ - dinfo = blk_legacy_dinfo(conf->blk); - if (dinfo) { - *serial = g_strdup(dinfo->serial); - } - } -} - -void blkconf_blocksizes(BlockConf *conf) -{ - BlockBackend *blk = conf->blk; - BlockSizes blocksizes; - int backend_ret; - - backend_ret = blk_probe_blocksizes(blk, &blocksizes); - /* fill in detected values if they are not defined via qemu command line */ - if (!conf->physical_block_size) { - if (!backend_ret) { - conf->physical_block_size = blocksizes.phys; - } else { - conf->physical_block_size = BDRV_SECTOR_SIZE; - } - } - if (!conf->logical_block_size) { - if (!backend_ret) { - conf->logical_block_size = blocksizes.log; - } else { - conf->logical_block_size = BDRV_SECTOR_SIZE; - } - } -} - -void blkconf_geometry(BlockConf *conf, int *ptrans, - unsigned cyls_max, unsigned heads_max, unsigned secs_max, - Error **errp) -{ - DriveInfo *dinfo; - - if (!conf->cyls && !conf->heads && !conf->secs) { - /* try to fall back to value set with legacy -drive cyls=... */ - dinfo = blk_legacy_dinfo(conf->blk); - if (dinfo) { - conf->cyls = dinfo->cyls; - conf->heads = dinfo->heads; - conf->secs = dinfo->secs; - if (ptrans) { - *ptrans = dinfo->trans; - } - } - } - if (!conf->cyls && !conf->heads && !conf->secs) { - hd_geometry_guess(conf->blk, - &conf->cyls, &conf->heads, &conf->secs, - ptrans); - } else if (ptrans && *ptrans == BIOS_ATA_TRANSLATION_AUTO) { - *ptrans = hd_bios_chs_auto_trans(conf->cyls, conf->heads, conf->secs); - } - if (conf->cyls || conf->heads || conf->secs) { - if (conf->cyls < 1 || conf->cyls > cyls_max) { - error_setg(errp, "cyls must be between 1 and %u", cyls_max); - return; - } - if (conf->heads < 1 || conf->heads > heads_max) { - error_setg(errp, "heads must be between 1 and %u", heads_max); - return; - } - if (conf->secs < 1 || conf->secs > secs_max) { - error_setg(errp, "secs must be between 1 and %u", secs_max); - return; - } - } -} diff --git a/qemu/hw/block/cdrom.c b/qemu/hw/block/cdrom.c deleted file mode 100644 index da937fe33..000000000 --- a/qemu/hw/block/cdrom.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * QEMU ATAPI CD-ROM Emulator - * - * Copyright (c) 2006 Fabrice Bellard - * - * 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. - */ - -/* ??? 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" - -static void lba_to_msf(uint8_t *buf, int lba) -{ - lba += 150; - buf[0] = (lba / 75) / 60; - buf[1] = (lba / 75) % 60; - buf[2] = lba % 75; -} - -/* same toc as bochs. Return -1 if error or the toc length */ -/* XXX: check this */ -int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track) -{ - uint8_t *q; - int len; - - if (start_track > 1 && start_track != 0xaa) - return -1; - q = buf + 2; - *q++ = 1; /* first session */ - *q++ = 1; /* last session */ - if (start_track <= 1) { - *q++ = 0; /* reserved */ - *q++ = 0x14; /* ADR, control */ - *q++ = 1; /* track number */ - *q++ = 0; /* reserved */ - if (msf) { - *q++ = 0; /* reserved */ - lba_to_msf(q, 0); - q += 3; - } else { - /* sector 0 */ - stl_be_p(q, 0); - q += 4; - } - } - /* lead out track */ - *q++ = 0; /* reserved */ - *q++ = 0x16; /* ADR, control */ - *q++ = 0xaa; /* track number */ - *q++ = 0; /* reserved */ - if (msf) { - *q++ = 0; /* reserved */ - lba_to_msf(q, nb_sectors); - q += 3; - } else { - stl_be_p(q, nb_sectors); - q += 4; - } - len = q - buf; - stw_be_p(buf, len - 2); - return len; -} - -/* mostly same info as PearPc */ -int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num) -{ - uint8_t *q; - int len; - - q = buf + 2; - *q++ = 1; /* first session */ - *q++ = 1; /* last session */ - - *q++ = 1; /* session number */ - *q++ = 0x14; /* data track */ - *q++ = 0; /* track number */ - *q++ = 0xa0; /* lead-in */ - *q++ = 0; /* min */ - *q++ = 0; /* sec */ - *q++ = 0; /* frame */ - *q++ = 0; - *q++ = 1; /* first track */ - *q++ = 0x00; /* disk type */ - *q++ = 0x00; - - *q++ = 1; /* session number */ - *q++ = 0x14; /* data track */ - *q++ = 0; /* track number */ - *q++ = 0xa1; - *q++ = 0; /* min */ - *q++ = 0; /* sec */ - *q++ = 0; /* frame */ - *q++ = 0; - *q++ = 1; /* last track */ - *q++ = 0x00; - *q++ = 0x00; - - *q++ = 1; /* session number */ - *q++ = 0x14; /* data track */ - *q++ = 0; /* track number */ - *q++ = 0xa2; /* lead-out */ - *q++ = 0; /* min */ - *q++ = 0; /* sec */ - *q++ = 0; /* frame */ - if (msf) { - *q++ = 0; /* reserved */ - lba_to_msf(q, nb_sectors); - q += 3; - } else { - stl_be_p(q, nb_sectors); - q += 4; - } - - *q++ = 1; /* session number */ - *q++ = 0x14; /* ADR, control */ - *q++ = 0; /* track number */ - *q++ = 1; /* point */ - *q++ = 0; /* min */ - *q++ = 0; /* sec */ - *q++ = 0; /* frame */ - if (msf) { - *q++ = 0; - lba_to_msf(q, 0); - q += 3; - } else { - *q++ = 0; - *q++ = 0; - *q++ = 0; - *q++ = 0; - } - - len = q - buf; - stw_be_p(buf, len - 2); - return len; -} diff --git a/qemu/hw/block/dataplane/Makefile.objs b/qemu/hw/block/dataplane/Makefile.objs deleted file mode 100644 index e786f6642..000000000 --- a/qemu/hw/block/dataplane/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -obj-y += virtio-blk.o diff --git a/qemu/hw/block/dataplane/virtio-blk.c b/qemu/hw/block/dataplane/virtio-blk.c deleted file mode 100644 index 3cb97c9a2..000000000 --- a/qemu/hw/block/dataplane/virtio-blk.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Dedicated thread for virtio-blk I/O processing - * - * Copyright 2012 IBM, Corp. - * Copyright 2012 Red Hat, Inc. and/or its affiliates - * - * Authors: - * Stefan Hajnoczi - * - * 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 "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 "sysemu/block-backend.h" -#include "hw/virtio/virtio-blk.h" -#include "virtio-blk.h" -#include "block/aio.h" -#include "hw/virtio/virtio-bus.h" -#include "qom/object_interfaces.h" - -struct VirtIOBlockDataPlane { - bool starting; - bool stopping; - - VirtIOBlkConf *conf; - - VirtIODevice *vdev; - 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; - AioContext *ctx; - - /* Operation blocker on BDS */ - Error *blocker; -}; - -/* Raise an interrupt to signal guest, if necessary */ -void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s) -{ - qemu_bh_schedule(s->bh); -} - -static void notify_guest_bh(void *opaque) -{ - VirtIOBlockDataPlane *s = opaque; - - if (!virtio_should_notify(s->vdev, s->vq)) { - return; - } - - event_notifier_set(s->guest_notifier); -} - -static void data_plane_set_up_op_blockers(VirtIOBlockDataPlane *s) -{ - 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); -} - -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 data_plane_blk_insert_notifier(Notifier *n, void *data) -{ - VirtIOBlockDataPlane *s = container_of(n, VirtIOBlockDataPlane, - insert_notifier); - assert(s->conf->conf.blk == data); - data_plane_set_up_op_blockers(s); -} - -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 */ -void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, - VirtIOBlockDataPlane **dataplane, - Error **errp) -{ - VirtIOBlockDataPlane *s; - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - - *dataplane = NULL; - - 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 dataplane " - "(transport does not support notifiers)"); - return; - } - - /* 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, errp)) { - error_prepend(errp, "cannot start dataplane thread: "); - return; - } - - s = g_new0(VirtIOBlockDataPlane, 1); - s->vdev = vdev; - s->conf = conf; - - if (conf->iothread) { - s->iothread = conf->iothread; - object_ref(OBJECT(s->iothread)); - } - s->ctx = iothread_get_aio_context(s->iothread); - s->bh = aio_bh_new(s->ctx, notify_guest_bh, s); - - 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; -} - -/* Context: QEMU global mutex held */ -void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s) -{ - if (!s) { - return; - } - - virtio_blk_data_plane_stop(s); - 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); - int r; - - if (vblk->dataplane_started || s->starting) { - return; - } - - s->starting = true; - s->vq = virtio_get_queue(s->vdev, 0); - - /* Set up guest notifier (irq) */ - r = k->set_guest_notifiers(qbus->parent, 1, true); - if (r != 0) { - fprintf(stderr, "virtio-blk failed to set guest notifier (%d), " - "ensure -enable-kvm is set\n", r); - goto fail_guest_notifiers; - } - s->guest_notifier = virtio_queue_get_guest_notifier(s->vq); - - /* Set up virtqueue notify */ - r = k->set_host_notifier(qbus->parent, 0, true); - if (r != 0) { - fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r); - goto fail_host_notifier; - } - - s->starting = false; - 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(s->vq)); - - /* Get this show started by hooking up our callbacks */ - aio_context_acquire(s->ctx); - 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: - vblk->dataplane_disabled = true; - s->starting = false; - vblk->dataplane_started = true; -} - -/* Context: QEMU global mutex held */ -void virtio_blk_data_plane_stop(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); - - if (!vblk->dataplane_started || s->stopping) { - return; - } - - /* Better luck next time. */ - if (vblk->dataplane_disabled) { - vblk->dataplane_disabled = false; - vblk->dataplane_started = false; - return; - } - s->stopping = true; - trace_virtio_blk_data_plane_stop(s); - - aio_context_acquire(s->ctx); - - /* Stop notifications for new requests from guest */ - 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); - - k->set_host_notifier(qbus->parent, 0, false); - - /* Clean up guest notifier (irq) */ - k->set_guest_notifiers(qbus->parent, 1, 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 deleted file mode 100644 index 0714c11a2..000000000 --- a/qemu/hw/block/dataplane/virtio-blk.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Dedicated thread for virtio-blk I/O processing - * - * Copyright 2012 IBM, Corp. - * Copyright 2012 Red Hat, Inc. and/or its affiliates - * - * Authors: - * Stefan Hajnoczi - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef HW_DATAPLANE_VIRTIO_BLK_H -#define HW_DATAPLANE_VIRTIO_BLK_H - -#include "hw/virtio/virtio.h" - -typedef struct VirtIOBlockDataPlane VirtIOBlockDataPlane; - -void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, - VirtIOBlockDataPlane **dataplane, - Error **errp); -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 deleted file mode 100644 index 48311d260..000000000 --- a/qemu/hw/block/ecc.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Calculate Error-correcting Codes. Used by NAND Flash controllers - * (not by NAND chips). - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/block/flash.h" - -/* - * Pre-calculated 256-way 1 byte column parity. Table borrowed from Linux. - */ -static const uint8_t nand_ecc_precalc_table[] = { - 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, - 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, - 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, - 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, - 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, - 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, - 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, - 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, - 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, - 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, - 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, - 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, - 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, - 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, - 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, - 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, - 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, - 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, - 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, - 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, - 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, - 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, - 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, - 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, - 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, - 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, - 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, - 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, - 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, - 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, - 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, - 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, -}; - -/* Update ECC parity count. */ -uint8_t ecc_digest(ECCState *s, uint8_t sample) -{ - uint8_t idx = nand_ecc_precalc_table[sample]; - - s->cp ^= idx & 0x3f; - if (idx & 0x40) { - s->lp[0] ^= ~s->count; - s->lp[1] ^= s->count; - } - s->count ++; - - return sample; -} - -/* Reinitialise the counters. */ -void ecc_reset(ECCState *s) -{ - s->lp[0] = 0x0000; - s->lp[1] = 0x0000; - s->cp = 0x00; - s->count = 0; -} - -/* Save/restore */ -VMStateDescription vmstate_ecc_state = { - .name = "ecc-state", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT8(cp, ECCState), - VMSTATE_UINT16_ARRAY(lp, ECCState, 2), - VMSTATE_UINT16(count, ECCState), - VMSTATE_END_OF_LIST(), - }, -}; diff --git a/qemu/hw/block/fdc.c b/qemu/hw/block/fdc.c deleted file mode 100644 index 372227569..000000000 --- a/qemu/hw/block/fdc.c +++ /dev/null @@ -1,2738 +0,0 @@ -/* - * QEMU Floppy disk emulator (Intel 82078) - * - * Copyright (c) 2003, 2007 Jocelyn Mayer - * Copyright (c) 2008 Hervé Poussineau - * - * 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. - */ -/* - * The controller is used in Sun4m systems in a slightly different - * 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" -#include "hw/sysbus.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" -#include "qemu/log.h" - -/********************************************************/ -/* debug Floppy devices */ - -#define DEBUG_FLOPPY 0 - -#define FLOPPY_DPRINTF(fmt, ...) \ - do { \ - if (DEBUG_FLOPPY) { \ - fprintf(stderr, "FLOPPY: " fmt , ## __VA_ARGS__); \ - } \ - } while (0) - -/********************************************************/ -/* Floppy drive emulation */ - -typedef enum FDriveRate { - FDRIVE_RATE_500K = 0x00, /* 500 Kbps */ - FDRIVE_RATE_300K = 0x01, /* 300 Kbps */ - FDRIVE_RATE_250K = 0x02, /* 250 Kbps */ - FDRIVE_RATE_1M = 0x03, /* 1 Mbps */ -} FDriveRate; - -typedef enum FDriveSize { - FDRIVE_SIZE_UNKNOWN, - FDRIVE_SIZE_350, - FDRIVE_SIZE_525, -} FDriveSize; - -typedef struct FDFormat { - 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 */ - { 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 */ - { 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 */ - { 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 */ - { 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 */ - { 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 */ - { 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 */ - { 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... */ - { FLOPPY_DRIVE_TYPE_144, 9, 80, 0, FDRIVE_RATE_250K, }, /* 3.5" 720 */ - /* end */ - { FLOPPY_DRIVE_TYPE_NONE, -1, -1, 0, 0, }, -}; - -static FDriveSize drive_size(FloppyDriveType drive) -{ - 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; - } -} - -#define GET_CUR_DRV(fdctrl) ((fdctrl)->cur_drv) -#define SET_CUR_DRV(fdctrl, drive) ((fdctrl)->cur_drv = (drive)) - -/* Will always be a fixed parameter for us */ -#define FD_SECTOR_LEN 512 -#define FD_SECTOR_SC 2 /* Sector size code */ -#define FD_RESET_SENSEI_COUNT 4 /* Number of sense interrupts on RESET */ - -typedef struct FDCtrl FDCtrl; - -/* Floppy disk drive emulation */ -typedef enum FDiskFlags { - FDISK_DBL_SIDES = 0x01, -} FDiskFlags; - -typedef struct FDrive { - FDCtrl *fdctrl; - BlockBackend *blk; - /* Drive status */ - 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 */ - uint16_t bps; /* Bytes per sector */ - 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->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) - -static int fd_sector_calc(uint8_t head, uint8_t track, uint8_t sect, - uint8_t last_sect, uint8_t num_sides) -{ - return (((track * num_sides) + head) * last_sect) + sect - 1; -} - -/* Returns current position, in sectors, for given drive */ -static int fd_sector(FDrive *drv) -{ - return fd_sector_calc(drv->head, drv->track, drv->sect, drv->last_sect, - NUM_SIDES(drv)); -} - -/* Seek to a new position: - * returns 0 if already on right track - * returns 1 if track changed - * returns 2 if track is invalid - * returns 3 if sector is invalid - * returns 4 if seek is disabled - */ -static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect, - int enable_seek) -{ - uint32_t sector; - int ret; - - if (track > drv->max_track || - (head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) { - FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n", - head, track, sect, 1, - (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1, - drv->max_track, drv->last_sect); - return 2; - } - if (sect > drv->last_sect) { - FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n", - head, track, sect, 1, - (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1, - drv->max_track, drv->last_sect); - return 3; - } - sector = fd_sector_calc(head, track, sect, drv->last_sect, NUM_SIDES(drv)); - ret = 0; - if (sector != fd_sector(drv)) { -#if 0 - if (!enable_seek) { - FLOPPY_DPRINTF("error: no implicit seek %d %02x %02x" - " (max=%d %02x %02x)\n", - head, track, sect, 1, drv->max_track, - drv->last_sect); - return 4; - } -#endif - drv->head = head; - if (drv->track != track) { - if (drv->blk != NULL && blk_is_inserted(drv->blk)) { - drv->media_changed = 0; - } - ret = 1; - } - drv->track = track; - drv->sect = sect; - } - - if (drv->blk == NULL || !blk_is_inserted(drv->blk)) { - ret = 2; - } - - return ret; -} - -/* Set drive back to track 0 */ -static void fd_recalibrate(FDrive *drv) -{ - FLOPPY_DPRINTF("recalibrate\n"); - 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 rc; - - FLOPPY_DPRINTF("revalidate\n"); - if (drv->blk != NULL) { - drv->ro = blk_is_read_only(drv->blk); - if (!blk_is_inserted(drv->blk)) { - FLOPPY_DPRINTF("No disk in drive\n"); - 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"); - } - } - } 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; - } -} - -/********************************************************/ -/* Intel 82078 floppy disk controller emulation */ - -static void fdctrl_reset(FDCtrl *fdctrl, int do_irq); -static void fdctrl_to_command_phase(FDCtrl *fdctrl); -static int fdctrl_transfer_handler (void *opaque, int nchan, - int dma_pos, int dma_len); -static void fdctrl_raise_irq(FDCtrl *fdctrl); -static FDrive *get_cur_drv(FDCtrl *fdctrl); - -static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl); -static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl); -static uint32_t fdctrl_read_dor(FDCtrl *fdctrl); -static void fdctrl_write_dor(FDCtrl *fdctrl, uint32_t value); -static uint32_t fdctrl_read_tape(FDCtrl *fdctrl); -static void fdctrl_write_tape(FDCtrl *fdctrl, uint32_t value); -static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl); -static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value); -static uint32_t fdctrl_read_data(FDCtrl *fdctrl); -static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value); -static uint32_t fdctrl_read_dir(FDCtrl *fdctrl); -static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value); - -enum { - FD_DIR_WRITE = 0, - FD_DIR_READ = 1, - FD_DIR_SCANE = 2, - FD_DIR_SCANL = 3, - FD_DIR_SCANH = 4, - FD_DIR_VERIFY = 5, -}; - -enum { - FD_STATE_MULTI = 0x01, /* multi track flag */ - FD_STATE_FORMAT = 0x02, /* format flag */ -}; - -enum { - FD_REG_SRA = 0x00, - FD_REG_SRB = 0x01, - FD_REG_DOR = 0x02, - FD_REG_TDR = 0x03, - FD_REG_MSR = 0x04, - FD_REG_DSR = 0x04, - FD_REG_FIFO = 0x05, - FD_REG_DIR = 0x07, - FD_REG_CCR = 0x07, -}; - -enum { - FD_CMD_READ_TRACK = 0x02, - FD_CMD_SPECIFY = 0x03, - FD_CMD_SENSE_DRIVE_STATUS = 0x04, - FD_CMD_WRITE = 0x05, - FD_CMD_READ = 0x06, - FD_CMD_RECALIBRATE = 0x07, - FD_CMD_SENSE_INTERRUPT_STATUS = 0x08, - FD_CMD_WRITE_DELETED = 0x09, - FD_CMD_READ_ID = 0x0a, - FD_CMD_READ_DELETED = 0x0c, - FD_CMD_FORMAT_TRACK = 0x0d, - FD_CMD_DUMPREG = 0x0e, - FD_CMD_SEEK = 0x0f, - FD_CMD_VERSION = 0x10, - FD_CMD_SCAN_EQUAL = 0x11, - FD_CMD_PERPENDICULAR_MODE = 0x12, - FD_CMD_CONFIGURE = 0x13, - FD_CMD_LOCK = 0x14, - FD_CMD_VERIFY = 0x16, - FD_CMD_POWERDOWN_MODE = 0x17, - FD_CMD_PART_ID = 0x18, - FD_CMD_SCAN_LOW_OR_EQUAL = 0x19, - FD_CMD_SCAN_HIGH_OR_EQUAL = 0x1d, - FD_CMD_SAVE = 0x2e, - FD_CMD_OPTION = 0x33, - FD_CMD_RESTORE = 0x4e, - FD_CMD_DRIVE_SPECIFICATION_COMMAND = 0x8e, - FD_CMD_RELATIVE_SEEK_OUT = 0x8f, - FD_CMD_FORMAT_AND_WRITE = 0xcd, - FD_CMD_RELATIVE_SEEK_IN = 0xcf, -}; - -enum { - FD_CONFIG_PRETRK = 0xff, /* Pre-compensation set to track 0 */ - FD_CONFIG_FIFOTHR = 0x0f, /* FIFO threshold set to 1 byte */ - FD_CONFIG_POLL = 0x10, /* Poll enabled */ - FD_CONFIG_EFIFO = 0x20, /* FIFO disabled */ - FD_CONFIG_EIS = 0x40, /* No implied seeks */ -}; - -enum { - FD_SR0_DS0 = 0x01, - FD_SR0_DS1 = 0x02, - FD_SR0_HEAD = 0x04, - FD_SR0_EQPMT = 0x10, - FD_SR0_SEEK = 0x20, - FD_SR0_ABNTERM = 0x40, - FD_SR0_INVCMD = 0x80, - FD_SR0_RDYCHG = 0xc0, -}; - -enum { - FD_SR1_MA = 0x01, /* Missing address mark */ - FD_SR1_NW = 0x02, /* Not writable */ - FD_SR1_EC = 0x80, /* End of cylinder */ -}; - -enum { - FD_SR2_SNS = 0x04, /* Scan not satisfied */ - FD_SR2_SEH = 0x08, /* Scan equal hit */ -}; - -enum { - FD_SRA_DIR = 0x01, - FD_SRA_nWP = 0x02, - FD_SRA_nINDX = 0x04, - FD_SRA_HDSEL = 0x08, - FD_SRA_nTRK0 = 0x10, - FD_SRA_STEP = 0x20, - FD_SRA_nDRV2 = 0x40, - FD_SRA_INTPEND = 0x80, -}; - -enum { - FD_SRB_MTR0 = 0x01, - FD_SRB_MTR1 = 0x02, - FD_SRB_WGATE = 0x04, - FD_SRB_RDATA = 0x08, - FD_SRB_WDATA = 0x10, - FD_SRB_DR0 = 0x20, -}; - -enum { -#if MAX_FD == 4 - FD_DOR_SELMASK = 0x03, -#else - FD_DOR_SELMASK = 0x01, -#endif - FD_DOR_nRESET = 0x04, - FD_DOR_DMAEN = 0x08, - FD_DOR_MOTEN0 = 0x10, - FD_DOR_MOTEN1 = 0x20, - FD_DOR_MOTEN2 = 0x40, - FD_DOR_MOTEN3 = 0x80, -}; - -enum { -#if MAX_FD == 4 - FD_TDR_BOOTSEL = 0x0c, -#else - FD_TDR_BOOTSEL = 0x04, -#endif -}; - -enum { - FD_DSR_DRATEMASK= 0x03, - FD_DSR_PWRDOWN = 0x40, - FD_DSR_SWRESET = 0x80, -}; - -enum { - FD_MSR_DRV0BUSY = 0x01, - FD_MSR_DRV1BUSY = 0x02, - FD_MSR_DRV2BUSY = 0x04, - FD_MSR_DRV3BUSY = 0x08, - FD_MSR_CMDBUSY = 0x10, - FD_MSR_NONDMA = 0x20, - FD_MSR_DIO = 0x40, - FD_MSR_RQM = 0x80, -}; - -enum { - FD_DIR_DSKCHG = 0x80, -}; - -/* - * See chapter 5.0 "Controller phases" of the spec: - * - * Command phase: - * The host writes a command and its parameters into the FIFO. The command - * phase is completed when all parameters for the command have been supplied, - * and execution phase is entered. - * - * Execution phase: - * Data transfers, either DMA or non-DMA. For non-DMA transfers, the FIFO - * contains the payload now, otherwise it's unused. When all bytes of the - * required data have been transferred, the state is switched to either result - * phase (if the command produces status bytes) or directly back into the - * command phase for the next command. - * - * Result phase: - * The host reads out the FIFO, which contains one or more result bytes now. - */ -enum { - /* Only for migration: reconstruct phase from registers like qemu 2.3 */ - FD_PHASE_RECONSTRUCT = 0, - - FD_PHASE_COMMAND = 1, - FD_PHASE_EXECUTION = 2, - FD_PHASE_RESULT = 3, -}; - -#define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI) -#define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT) - -struct FDCtrl { - MemoryRegion iomem; - qemu_irq irq; - /* Controller state */ - QEMUTimer *result_timer; - int dma_chann; - uint8_t phase; - IsaDma *dma; - /* Controller's identification */ - uint8_t version; - /* HW */ - uint8_t sra; - uint8_t srb; - uint8_t dor; - uint8_t dor_vmstate; /* only used as temp during vmstate */ - uint8_t tdr; - uint8_t dsr; - uint8_t msr; - uint8_t cur_drv; - uint8_t status0; - uint8_t status1; - uint8_t status2; - /* Command FIFO */ - uint8_t *fifo; - int32_t fifo_size; - uint32_t data_pos; - uint32_t data_len; - uint8_t data_state; - uint8_t data_dir; - uint8_t eot; /* last wanted sector */ - /* States kept only to be returned back */ - /* precompensation */ - uint8_t precomp_trk; - uint8_t config; - uint8_t lock; - /* Power down config (also with status regB access mode */ - uint8_t pwrd; - /* Floppy drives */ - uint8_t num_floppies; - 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) - -typedef struct FDCtrlSysBus { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - struct FDCtrl state; -} FDCtrlSysBus; - -#define ISA_FDC(obj) OBJECT_CHECK(FDCtrlISABus, (obj), TYPE_ISA_FDC) - -typedef struct FDCtrlISABus { - ISADevice parent_obj; - - uint32_t iobase; - uint32_t irq; - uint32_t dma; - struct FDCtrl state; - int32_t bootindexA; - int32_t bootindexB; -} FDCtrlISABus; - -static uint32_t fdctrl_read (void *opaque, uint32_t reg) -{ - FDCtrl *fdctrl = opaque; - uint32_t retval; - - reg &= 7; - switch (reg) { - case FD_REG_SRA: - retval = fdctrl_read_statusA(fdctrl); - break; - case FD_REG_SRB: - retval = fdctrl_read_statusB(fdctrl); - break; - case FD_REG_DOR: - retval = fdctrl_read_dor(fdctrl); - break; - case FD_REG_TDR: - retval = fdctrl_read_tape(fdctrl); - break; - case FD_REG_MSR: - retval = fdctrl_read_main_status(fdctrl); - break; - case FD_REG_FIFO: - retval = fdctrl_read_data(fdctrl); - break; - case FD_REG_DIR: - retval = fdctrl_read_dir(fdctrl); - break; - default: - retval = (uint32_t)(-1); - break; - } - FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval); - - return retval; -} - -static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value) -{ - FDCtrl *fdctrl = opaque; - - FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value); - - reg &= 7; - switch (reg) { - case FD_REG_DOR: - fdctrl_write_dor(fdctrl, value); - break; - case FD_REG_TDR: - fdctrl_write_tape(fdctrl, value); - break; - case FD_REG_DSR: - fdctrl_write_rate(fdctrl, value); - break; - case FD_REG_FIFO: - fdctrl_write_data(fdctrl, value); - break; - case FD_REG_CCR: - fdctrl_write_ccr(fdctrl, value); - break; - default: - break; - } -} - -static uint64_t fdctrl_read_mem (void *opaque, hwaddr reg, - unsigned ize) -{ - return fdctrl_read(opaque, (uint32_t)reg); -} - -static void fdctrl_write_mem (void *opaque, hwaddr reg, - uint64_t value, unsigned size) -{ - fdctrl_write(opaque, (uint32_t)reg, value); -} - -static const MemoryRegionOps fdctrl_mem_ops = { - .read = fdctrl_read_mem, - .write = fdctrl_write_mem, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const MemoryRegionOps fdctrl_mem_strict_ops = { - .read = fdctrl_read_mem, - .write = fdctrl_write_mem, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static bool fdrive_media_changed_needed(void *opaque) -{ - FDrive *drive = opaque; - - return (drive->blk != NULL && drive->media_changed != 1); -} - -static const VMStateDescription vmstate_fdrive_media_changed = { - .name = "fdrive/media_changed", - .version_id = 1, - .minimum_version_id = 1, - .needed = fdrive_media_changed_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(media_changed, FDrive), - VMSTATE_END_OF_LIST() - } -}; - -static bool fdrive_media_rate_needed(void *opaque) -{ - FDrive *drive = opaque; - - return drive->fdctrl->check_media_rate; -} - -static const VMStateDescription vmstate_fdrive_media_rate = { - .name = "fdrive/media_rate", - .version_id = 1, - .minimum_version_id = 1, - .needed = fdrive_media_rate_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(media_rate, FDrive), - VMSTATE_END_OF_LIST() - } -}; - -static bool fdrive_perpendicular_needed(void *opaque) -{ - FDrive *drive = opaque; - - return drive->perpendicular != 0; -} - -static const VMStateDescription vmstate_fdrive_perpendicular = { - .name = "fdrive/perpendicular", - .version_id = 1, - .minimum_version_id = 1, - .needed = fdrive_perpendicular_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(perpendicular, FDrive), - VMSTATE_END_OF_LIST() - } -}; - -static int fdrive_post_load(void *opaque, int version_id) -{ - fd_revalidate(opaque); - return 0; -} - -static const VMStateDescription vmstate_fdrive = { - .name = "fdrive", - .version_id = 1, - .minimum_version_id = 1, - .post_load = fdrive_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(head, FDrive), - VMSTATE_UINT8(track, FDrive), - VMSTATE_UINT8(sect, FDrive), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_fdrive_media_changed, - &vmstate_fdrive_media_rate, - &vmstate_fdrive_perpendicular, - NULL - } -}; - -/* - * Reconstructs the phase from register values according to the logic that was - * implemented in qemu 2.3. This is the default value that is used if the phase - * subsection is not present on migration. - * - * Don't change this function to reflect newer qemu versions, it is part of - * the migration ABI. - */ -static int reconstruct_phase(FDCtrl *fdctrl) -{ - if (fdctrl->msr & FD_MSR_NONDMA) { - return FD_PHASE_EXECUTION; - } else if ((fdctrl->msr & FD_MSR_RQM) == 0) { - /* qemu 2.3 disabled RQM only during DMA transfers */ - return FD_PHASE_EXECUTION; - } else if (fdctrl->msr & FD_MSR_DIO) { - return FD_PHASE_RESULT; - } else { - return FD_PHASE_COMMAND; - } -} - -static void fdc_pre_save(void *opaque) -{ - FDCtrl *s = opaque; - - s->dor_vmstate = s->dor | GET_CUR_DRV(s); -} - -static int fdc_pre_load(void *opaque) -{ - FDCtrl *s = opaque; - s->phase = FD_PHASE_RECONSTRUCT; - return 0; -} - -static int fdc_post_load(void *opaque, int version_id) -{ - FDCtrl *s = opaque; - - SET_CUR_DRV(s, s->dor_vmstate & FD_DOR_SELMASK); - s->dor = s->dor_vmstate & ~FD_DOR_SELMASK; - - if (s->phase == FD_PHASE_RECONSTRUCT) { - s->phase = reconstruct_phase(s); - } - - return 0; -} - -static bool fdc_reset_sensei_needed(void *opaque) -{ - FDCtrl *s = opaque; - - return s->reset_sensei != 0; -} - -static const VMStateDescription vmstate_fdc_reset_sensei = { - .name = "fdc/reset_sensei", - .version_id = 1, - .minimum_version_id = 1, - .needed = fdc_reset_sensei_needed, - .fields = (VMStateField[]) { - VMSTATE_INT32(reset_sensei, FDCtrl), - VMSTATE_END_OF_LIST() - } -}; - -static bool fdc_result_timer_needed(void *opaque) -{ - FDCtrl *s = opaque; - - return timer_pending(s->result_timer); -} - -static const VMStateDescription vmstate_fdc_result_timer = { - .name = "fdc/result_timer", - .version_id = 1, - .minimum_version_id = 1, - .needed = fdc_result_timer_needed, - .fields = (VMStateField[]) { - VMSTATE_TIMER_PTR(result_timer, FDCtrl), - VMSTATE_END_OF_LIST() - } -}; - -static bool fdc_phase_needed(void *opaque) -{ - FDCtrl *fdctrl = opaque; - - return reconstruct_phase(fdctrl) != fdctrl->phase; -} - -static const VMStateDescription vmstate_fdc_phase = { - .name = "fdc/phase", - .version_id = 1, - .minimum_version_id = 1, - .needed = fdc_phase_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(phase, FDCtrl), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_fdc = { - .name = "fdc", - .version_id = 2, - .minimum_version_id = 2, - .pre_save = fdc_pre_save, - .pre_load = fdc_pre_load, - .post_load = fdc_post_load, - .fields = (VMStateField[]) { - /* Controller State */ - VMSTATE_UINT8(sra, FDCtrl), - VMSTATE_UINT8(srb, FDCtrl), - VMSTATE_UINT8(dor_vmstate, FDCtrl), - VMSTATE_UINT8(tdr, FDCtrl), - VMSTATE_UINT8(dsr, FDCtrl), - VMSTATE_UINT8(msr, FDCtrl), - VMSTATE_UINT8(status0, FDCtrl), - VMSTATE_UINT8(status1, FDCtrl), - VMSTATE_UINT8(status2, FDCtrl), - /* Command FIFO */ - VMSTATE_VARRAY_INT32(fifo, FDCtrl, fifo_size, 0, vmstate_info_uint8, - uint8_t), - VMSTATE_UINT32(data_pos, FDCtrl), - VMSTATE_UINT32(data_len, FDCtrl), - VMSTATE_UINT8(data_state, FDCtrl), - VMSTATE_UINT8(data_dir, FDCtrl), - VMSTATE_UINT8(eot, FDCtrl), - /* States kept only to be returned back */ - VMSTATE_UINT8(timer0, FDCtrl), - VMSTATE_UINT8(timer1, FDCtrl), - VMSTATE_UINT8(precomp_trk, FDCtrl), - VMSTATE_UINT8(config, FDCtrl), - VMSTATE_UINT8(lock, FDCtrl), - VMSTATE_UINT8(pwrd, FDCtrl), - VMSTATE_UINT8_EQUAL(num_floppies, FDCtrl), - VMSTATE_STRUCT_ARRAY(drives, FDCtrl, MAX_FD, 1, - vmstate_fdrive, FDrive), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_fdc_reset_sensei, - &vmstate_fdc_result_timer, - &vmstate_fdc_phase, - NULL - } -}; - -static void fdctrl_external_reset_sysbus(DeviceState *d) -{ - FDCtrlSysBus *sys = SYSBUS_FDC(d); - FDCtrl *s = &sys->state; - - fdctrl_reset(s, 0); -} - -static void fdctrl_external_reset_isa(DeviceState *d) -{ - FDCtrlISABus *isa = ISA_FDC(d); - FDCtrl *s = &isa->state; - - fdctrl_reset(s, 0); -} - -static void fdctrl_handle_tc(void *opaque, int irq, int level) -{ - //FDCtrl *s = opaque; - - if (level) { - // XXX - FLOPPY_DPRINTF("TC pulsed\n"); - } -} - -/* Change IRQ state */ -static void fdctrl_reset_irq(FDCtrl *fdctrl) -{ - fdctrl->status0 = 0; - if (!(fdctrl->sra & FD_SRA_INTPEND)) - return; - FLOPPY_DPRINTF("Reset interrupt\n"); - qemu_set_irq(fdctrl->irq, 0); - fdctrl->sra &= ~FD_SRA_INTPEND; -} - -static void fdctrl_raise_irq(FDCtrl *fdctrl) -{ - if (!(fdctrl->sra & FD_SRA_INTPEND)) { - qemu_set_irq(fdctrl->irq, 1); - fdctrl->sra |= FD_SRA_INTPEND; - } - - fdctrl->reset_sensei = 0; - FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", fdctrl->status0); -} - -/* Reset controller */ -static void fdctrl_reset(FDCtrl *fdctrl, int do_irq) -{ - int i; - - FLOPPY_DPRINTF("reset controller\n"); - fdctrl_reset_irq(fdctrl); - /* Initialise controller */ - fdctrl->sra = 0; - fdctrl->srb = 0xc0; - if (!fdctrl->drives[1].blk) { - fdctrl->sra |= FD_SRA_nDRV2; - } - fdctrl->cur_drv = 0; - fdctrl->dor = FD_DOR_nRESET; - fdctrl->dor |= (fdctrl->dma_chann != -1) ? FD_DOR_DMAEN : 0; - fdctrl->msr = FD_MSR_RQM; - fdctrl->reset_sensei = 0; - timer_del(fdctrl->result_timer); - /* FIFO state */ - fdctrl->data_pos = 0; - fdctrl->data_len = 0; - fdctrl->data_state = 0; - fdctrl->data_dir = FD_DIR_WRITE; - for (i = 0; i < MAX_FD; i++) - fd_recalibrate(&fdctrl->drives[i]); - fdctrl_to_command_phase(fdctrl); - if (do_irq) { - fdctrl->status0 |= FD_SR0_RDYCHG; - fdctrl_raise_irq(fdctrl); - fdctrl->reset_sensei = FD_RESET_SENSEI_COUNT; - } -} - -static inline FDrive *drv0(FDCtrl *fdctrl) -{ - return &fdctrl->drives[(fdctrl->tdr & FD_TDR_BOOTSEL) >> 2]; -} - -static inline FDrive *drv1(FDCtrl *fdctrl) -{ - if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (1 << 2)) - return &fdctrl->drives[1]; - else - return &fdctrl->drives[0]; -} - -#if MAX_FD == 4 -static inline FDrive *drv2(FDCtrl *fdctrl) -{ - if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (2 << 2)) - return &fdctrl->drives[2]; - else - return &fdctrl->drives[1]; -} - -static inline FDrive *drv3(FDCtrl *fdctrl) -{ - if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (3 << 2)) - return &fdctrl->drives[3]; - else - return &fdctrl->drives[2]; -} -#endif - -static FDrive *get_cur_drv(FDCtrl *fdctrl) -{ - switch (fdctrl->cur_drv) { - case 0: return drv0(fdctrl); - case 1: return drv1(fdctrl); -#if MAX_FD == 4 - case 2: return drv2(fdctrl); - case 3: return drv3(fdctrl); -#endif - default: return NULL; - } -} - -/* Status A register : 0x00 (read-only) */ -static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl) -{ - uint32_t retval = fdctrl->sra; - - FLOPPY_DPRINTF("status register A: 0x%02x\n", retval); - - return retval; -} - -/* Status B register : 0x01 (read-only) */ -static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl) -{ - uint32_t retval = fdctrl->srb; - - FLOPPY_DPRINTF("status register B: 0x%02x\n", retval); - - return retval; -} - -/* Digital output register : 0x02 */ -static uint32_t fdctrl_read_dor(FDCtrl *fdctrl) -{ - uint32_t retval = fdctrl->dor; - - /* Selected drive */ - retval |= fdctrl->cur_drv; - FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval); - - return retval; -} - -static void fdctrl_write_dor(FDCtrl *fdctrl, uint32_t value) -{ - FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value); - - /* Motors */ - if (value & FD_DOR_MOTEN0) - fdctrl->srb |= FD_SRB_MTR0; - else - fdctrl->srb &= ~FD_SRB_MTR0; - if (value & FD_DOR_MOTEN1) - fdctrl->srb |= FD_SRB_MTR1; - else - fdctrl->srb &= ~FD_SRB_MTR1; - - /* Drive */ - if (value & 1) - fdctrl->srb |= FD_SRB_DR0; - else - fdctrl->srb &= ~FD_SRB_DR0; - - /* Reset */ - if (!(value & FD_DOR_nRESET)) { - if (fdctrl->dor & FD_DOR_nRESET) { - FLOPPY_DPRINTF("controller enter RESET state\n"); - } - } else { - if (!(fdctrl->dor & FD_DOR_nRESET)) { - FLOPPY_DPRINTF("controller out of RESET state\n"); - fdctrl_reset(fdctrl, 1); - fdctrl->dsr &= ~FD_DSR_PWRDOWN; - } - } - /* Selected drive */ - fdctrl->cur_drv = value & FD_DOR_SELMASK; - - fdctrl->dor = value; -} - -/* Tape drive register : 0x03 */ -static uint32_t fdctrl_read_tape(FDCtrl *fdctrl) -{ - uint32_t retval = fdctrl->tdr; - - FLOPPY_DPRINTF("tape drive register: 0x%02x\n", retval); - - return retval; -} - -static void fdctrl_write_tape(FDCtrl *fdctrl, uint32_t value) -{ - /* Reset mode */ - if (!(fdctrl->dor & FD_DOR_nRESET)) { - FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); - return; - } - FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value); - /* Disk boot selection indicator */ - fdctrl->tdr = value & FD_TDR_BOOTSEL; - /* Tape indicators: never allow */ -} - -/* Main status register : 0x04 (read) */ -static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl) -{ - uint32_t retval = fdctrl->msr; - - fdctrl->dsr &= ~FD_DSR_PWRDOWN; - fdctrl->dor |= FD_DOR_nRESET; - - FLOPPY_DPRINTF("main status register: 0x%02x\n", retval); - - return retval; -} - -/* Data select rate register : 0x04 (write) */ -static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value) -{ - /* Reset mode */ - if (!(fdctrl->dor & FD_DOR_nRESET)) { - FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); - return; - } - FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value); - /* Reset: autoclear */ - if (value & FD_DSR_SWRESET) { - fdctrl->dor &= ~FD_DOR_nRESET; - fdctrl_reset(fdctrl, 1); - fdctrl->dor |= FD_DOR_nRESET; - } - if (value & FD_DSR_PWRDOWN) { - fdctrl_reset(fdctrl, 1); - } - fdctrl->dsr = value; -} - -/* Configuration control register: 0x07 (write) */ -static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value) -{ - /* Reset mode */ - if (!(fdctrl->dor & FD_DOR_nRESET)) { - FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); - return; - } - FLOPPY_DPRINTF("configuration control register set to 0x%02x\n", value); - - /* Only the rate selection bits used in AT mode, and we - * store those in the DSR. - */ - fdctrl->dsr = (fdctrl->dsr & ~FD_DSR_DRATEMASK) | - (value & FD_DSR_DRATEMASK); -} - -static int fdctrl_media_changed(FDrive *drv) -{ - return drv->media_changed; -} - -/* Digital input register : 0x07 (read-only) */ -static uint32_t fdctrl_read_dir(FDCtrl *fdctrl) -{ - uint32_t retval = 0; - - if (fdctrl_media_changed(get_cur_drv(fdctrl))) { - retval |= FD_DIR_DSKCHG; - } - if (retval != 0) { - FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval); - } - - return retval; -} - -/* Clear the FIFO and update the state for receiving the next command */ -static void fdctrl_to_command_phase(FDCtrl *fdctrl) -{ - fdctrl->phase = FD_PHASE_COMMAND; - fdctrl->data_dir = FD_DIR_WRITE; - fdctrl->data_pos = 0; - fdctrl->data_len = 1; /* Accept command byte, adjust for params later */ - fdctrl->msr &= ~(FD_MSR_CMDBUSY | FD_MSR_DIO); - fdctrl->msr |= FD_MSR_RQM; -} - -/* Update the state to allow the guest to read out the command status. - * @fifo_len is the number of result bytes to be read out. */ -static void fdctrl_to_result_phase(FDCtrl *fdctrl, int fifo_len) -{ - fdctrl->phase = FD_PHASE_RESULT; - fdctrl->data_dir = FD_DIR_READ; - fdctrl->data_len = fifo_len; - fdctrl->data_pos = 0; - fdctrl->msr |= FD_MSR_CMDBUSY | FD_MSR_RQM | FD_MSR_DIO; -} - -/* Set an error: unimplemented/unknown command */ -static void fdctrl_unimplemented(FDCtrl *fdctrl, int direction) -{ - qemu_log_mask(LOG_UNIMP, "fdc: unimplemented command 0x%02x\n", - fdctrl->fifo[0]); - fdctrl->fifo[0] = FD_SR0_INVCMD; - fdctrl_to_result_phase(fdctrl, 1); -} - -/* Seek to next sector - * returns 0 when end of track reached (for DBL_SIDES on head 1) - * otherwise returns 1 - */ -static int fdctrl_seek_to_next_sect(FDCtrl *fdctrl, FDrive *cur_drv) -{ - FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d)\n", - cur_drv->head, cur_drv->track, cur_drv->sect, - fd_sector(cur_drv)); - /* XXX: cur_drv->sect >= cur_drv->last_sect should be an - error in fact */ - uint8_t new_head = cur_drv->head; - uint8_t new_track = cur_drv->track; - uint8_t new_sect = cur_drv->sect; - - int ret = 1; - - if (new_sect >= cur_drv->last_sect || - new_sect == fdctrl->eot) { - new_sect = 1; - if (FD_MULTI_TRACK(fdctrl->data_state)) { - if (new_head == 0 && - (cur_drv->flags & FDISK_DBL_SIDES) != 0) { - new_head = 1; - } else { - new_head = 0; - new_track++; - fdctrl->status0 |= FD_SR0_SEEK; - if ((cur_drv->flags & FDISK_DBL_SIDES) == 0) { - ret = 0; - } - } - } else { - fdctrl->status0 |= FD_SR0_SEEK; - new_track++; - ret = 0; - } - if (ret == 1) { - FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n", - new_head, new_track, new_sect, fd_sector(cur_drv)); - } - } else { - new_sect++; - } - fd_seek(cur_drv, new_head, new_track, new_sect, 1); - return ret; -} - -/* Callback for transfer end (stop or abort) */ -static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0, - uint8_t status1, uint8_t status2) -{ - FDrive *cur_drv; - cur_drv = get_cur_drv(fdctrl); - - fdctrl->status0 &= ~(FD_SR0_DS0 | FD_SR0_DS1 | FD_SR0_HEAD); - fdctrl->status0 |= GET_CUR_DRV(fdctrl); - if (cur_drv->head) { - fdctrl->status0 |= FD_SR0_HEAD; - } - fdctrl->status0 |= status0; - - FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n", - status0, status1, status2, fdctrl->status0); - fdctrl->fifo[0] = fdctrl->status0; - fdctrl->fifo[1] = status1; - fdctrl->fifo[2] = status2; - fdctrl->fifo[3] = cur_drv->track; - fdctrl->fifo[4] = cur_drv->head; - fdctrl->fifo[5] = cur_drv->sect; - fdctrl->fifo[6] = FD_SECTOR_SC; - fdctrl->data_dir = FD_DIR_READ; - if (!(fdctrl->msr & FD_MSR_NONDMA)) { - 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; - - fdctrl_to_result_phase(fdctrl, 7); - fdctrl_raise_irq(fdctrl); -} - -/* Prepare a data transfer (either DMA or FIFO) */ -static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv; - uint8_t kh, kt, ks; - - SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); - cur_drv = get_cur_drv(fdctrl); - kt = fdctrl->fifo[2]; - kh = fdctrl->fifo[3]; - ks = fdctrl->fifo[4]; - FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n", - GET_CUR_DRV(fdctrl), kh, kt, ks, - fd_sector_calc(kh, kt, ks, cur_drv->last_sect, - NUM_SIDES(cur_drv))); - switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) { - case 2: - /* sect too big */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); - fdctrl->fifo[3] = kt; - fdctrl->fifo[4] = kh; - fdctrl->fifo[5] = ks; - return; - case 3: - /* track too big */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00); - fdctrl->fifo[3] = kt; - fdctrl->fifo[4] = kh; - fdctrl->fifo[5] = ks; - return; - case 4: - /* No seek enabled */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); - fdctrl->fifo[3] = kt; - fdctrl->fifo[4] = kh; - fdctrl->fifo[5] = ks; - return; - case 1: - fdctrl->status0 |= FD_SR0_SEEK; - break; - default: - break; - } - - /* Check the data rate. If the programmed data rate does not match - * the currently inserted medium, the operation has to fail. */ - if (fdctrl->check_media_rate && - (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) { - FLOPPY_DPRINTF("data rate mismatch (fdc=%d, media=%d)\n", - fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate); - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00); - fdctrl->fifo[3] = kt; - fdctrl->fifo[4] = kh; - fdctrl->fifo[5] = ks; - return; - } - - /* Set the FIFO state */ - fdctrl->data_dir = direction; - fdctrl->data_pos = 0; - assert(fdctrl->msr & FD_MSR_CMDBUSY); - if (fdctrl->fifo[0] & 0x80) - fdctrl->data_state |= FD_STATE_MULTI; - else - fdctrl->data_state &= ~FD_STATE_MULTI; - if (fdctrl->fifo[5] == 0) { - fdctrl->data_len = fdctrl->fifo[8]; - } else { - int tmp; - fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]); - tmp = (fdctrl->fifo[6] - ks + 1); - if (fdctrl->fifo[0] & 0x80) - tmp += fdctrl->fifo[6]; - fdctrl->data_len *= tmp; - } - fdctrl->eot = fdctrl->fifo[6]; - if (fdctrl->dor & FD_DOR_DMAEN) { - 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 = 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); - 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... - */ - k->hold_DREQ(fdctrl->dma, fdctrl->dma_chann); - k->schedule(fdctrl->dma); - } else { - /* Start transfer */ - fdctrl_transfer_handler(fdctrl, fdctrl->dma_chann, 0, - fdctrl->data_len); - } - return; - } else { - FLOPPY_DPRINTF("bad dma_mode=%d direction=%d\n", dma_mode, - direction); - } - } - FLOPPY_DPRINTF("start non-DMA transfer\n"); - fdctrl->msr |= FD_MSR_NONDMA | FD_MSR_RQM; - if (direction != FD_DIR_WRITE) - fdctrl->msr |= FD_MSR_DIO; - /* IO based transfer: calculate len */ - fdctrl_raise_irq(fdctrl); -} - -/* Prepare a transfer of deleted data */ -static void fdctrl_start_transfer_del(FDCtrl *fdctrl, int direction) -{ - qemu_log_mask(LOG_UNIMP, "fdctrl_start_transfer_del() unimplemented\n"); - - /* We don't handle deleted data, - * so we don't return *ANYTHING* - */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); -} - -/* handlers for DMA transfers */ -static int fdctrl_transfer_handler (void *opaque, int nchan, - int dma_pos, int dma_len) -{ - FDCtrl *fdctrl; - 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) - status2 = FD_SR2_SNS; - if (dma_len > fdctrl->data_len) - dma_len = fdctrl->data_len; - if (cur_drv->blk == NULL) { - if (fdctrl->data_dir == FD_DIR_WRITE) - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); - else - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); - len = 0; - goto transfer_error; - } - rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; - for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) { - len = dma_len - fdctrl->data_pos; - if (len + rel_pos > FD_SECTOR_LEN) - len = FD_SECTOR_LEN - rel_pos; - FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x " - "(%d-0x%08x 0x%08x)\n", len, dma_len, fdctrl->data_pos, - fdctrl->data_len, GET_CUR_DRV(fdctrl), cur_drv->head, - cur_drv->track, cur_drv->sect, fd_sector(cur_drv), - fd_sector(cur_drv) * FD_SECTOR_LEN); - if (fdctrl->data_dir != FD_DIR_WRITE || - len < FD_SECTOR_LEN || rel_pos != 0) { - /* READ & SCAN commands and realign to a sector for WRITE */ - if (blk_read(cur_drv->blk, fd_sector(cur_drv), - fdctrl->fifo, 1) < 0) { - FLOPPY_DPRINTF("Floppy: error getting sector %d\n", - fd_sector(cur_drv)); - /* Sure, image size is too small... */ - memset(fdctrl->fifo, 0, FD_SECTOR_LEN); - } - } - switch (fdctrl->data_dir) { - case FD_DIR_READ: - /* READ commands */ - k->write_memory(fdctrl->dma, nchan, fdctrl->fifo + rel_pos, - fdctrl->data_pos, len); - break; - case FD_DIR_WRITE: - /* WRITE commands */ - if (cur_drv->ro) { - /* Handle readonly medium early, no need to do DMA, touch the - * LED or attempt any writes. A real floppy doesn't attempt - * to write to readonly media either. */ - fdctrl_stop_transfer(fdctrl, - FD_SR0_ABNTERM | FD_SR0_SEEK, FD_SR1_NW, - 0x00); - goto transfer_error; - } - - 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", - fd_sector(cur_drv)); - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); - goto transfer_error; - } - break; - case FD_DIR_VERIFY: - /* VERIFY commands */ - break; - default: - /* SCAN commands */ - { - uint8_t tmpbuf[FD_SECTOR_LEN]; - int ret; - 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; - goto end_transfer; - } - if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) || - (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) { - status2 = 0x00; - goto end_transfer; - } - } - break; - } - fdctrl->data_pos += len; - rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; - if (rel_pos == 0) { - /* Seek to next sector */ - if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) - break; - } - } - end_transfer: - len = fdctrl->data_pos - start_pos; - FLOPPY_DPRINTF("end transfer %d %d %d\n", - fdctrl->data_pos, len, fdctrl->data_len); - if (fdctrl->data_dir == FD_DIR_SCANE || - fdctrl->data_dir == FD_DIR_SCANL || - fdctrl->data_dir == FD_DIR_SCANH) - status2 = FD_SR2_SEH; - fdctrl->data_len -= len; - fdctrl_stop_transfer(fdctrl, status0, status1, status2); - transfer_error: - - return len; -} - -/* Data register : 0x05 */ -static uint32_t fdctrl_read_data(FDCtrl *fdctrl) -{ - FDrive *cur_drv; - uint32_t retval = 0; - uint32_t pos; - - cur_drv = get_cur_drv(fdctrl); - fdctrl->dsr &= ~FD_DSR_PWRDOWN; - if (!(fdctrl->msr & FD_MSR_RQM) || !(fdctrl->msr & FD_MSR_DIO)) { - FLOPPY_DPRINTF("error: controller not ready for reading\n"); - return 0; - } - - /* If data_len spans multiple sectors, the current position in the FIFO - * wraps around while fdctrl->data_pos is the real position in the whole - * request. */ - pos = fdctrl->data_pos; - pos %= FD_SECTOR_LEN; - - switch (fdctrl->phase) { - case FD_PHASE_EXECUTION: - assert(fdctrl->msr & FD_MSR_NONDMA); - if (pos == 0) { - if (fdctrl->data_pos != 0) - if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) { - FLOPPY_DPRINTF("error seeking to next sector %d\n", - fd_sector(cur_drv)); - return 0; - } - if (blk_read(cur_drv->blk, fd_sector(cur_drv), fdctrl->fifo, 1) - < 0) { - FLOPPY_DPRINTF("error getting sector %d\n", - fd_sector(cur_drv)); - /* Sure, image size is too small... */ - memset(fdctrl->fifo, 0, FD_SECTOR_LEN); - } - } - - if (++fdctrl->data_pos == fdctrl->data_len) { - fdctrl->msr &= ~FD_MSR_RQM; - fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); - } - break; - - case FD_PHASE_RESULT: - assert(!(fdctrl->msr & FD_MSR_NONDMA)); - if (++fdctrl->data_pos == fdctrl->data_len) { - fdctrl->msr &= ~FD_MSR_RQM; - fdctrl_to_command_phase(fdctrl); - fdctrl_reset_irq(fdctrl); - } - break; - - case FD_PHASE_COMMAND: - default: - abort(); - } - - retval = fdctrl->fifo[pos]; - FLOPPY_DPRINTF("data register: 0x%02x\n", retval); - - return retval; -} - -static void fdctrl_format_sector(FDCtrl *fdctrl) -{ - FDrive *cur_drv; - uint8_t kh, kt, ks; - - SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); - cur_drv = get_cur_drv(fdctrl); - kt = fdctrl->fifo[6]; - kh = fdctrl->fifo[7]; - ks = fdctrl->fifo[8]; - FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n", - GET_CUR_DRV(fdctrl), kh, kt, ks, - fd_sector_calc(kh, kt, ks, cur_drv->last_sect, - NUM_SIDES(cur_drv))); - switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) { - case 2: - /* sect too big */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); - fdctrl->fifo[3] = kt; - fdctrl->fifo[4] = kh; - fdctrl->fifo[5] = ks; - return; - case 3: - /* track too big */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00); - fdctrl->fifo[3] = kt; - fdctrl->fifo[4] = kh; - fdctrl->fifo[5] = ks; - return; - case 4: - /* No seek enabled */ - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); - fdctrl->fifo[3] = kt; - fdctrl->fifo[4] = kh; - fdctrl->fifo[5] = ks; - return; - case 1: - fdctrl->status0 |= FD_SR0_SEEK; - break; - default: - break; - } - memset(fdctrl->fifo, 0, FD_SECTOR_LEN); - if (cur_drv->blk == NULL || - blk_write(cur_drv->blk, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { - FLOPPY_DPRINTF("error formatting sector %d\n", fd_sector(cur_drv)); - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00); - } else { - if (cur_drv->sect == cur_drv->last_sect) { - fdctrl->data_state &= ~FD_STATE_FORMAT; - /* Last sector done */ - fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); - } else { - /* More to do */ - fdctrl->data_pos = 0; - fdctrl->data_len = 4; - } - } -} - -static void fdctrl_handle_lock(FDCtrl *fdctrl, int direction) -{ - fdctrl->lock = (fdctrl->fifo[0] & 0x80) ? 1 : 0; - fdctrl->fifo[0] = fdctrl->lock << 4; - fdctrl_to_result_phase(fdctrl, 1); -} - -static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv = get_cur_drv(fdctrl); - - /* Drives position */ - fdctrl->fifo[0] = drv0(fdctrl)->track; - fdctrl->fifo[1] = drv1(fdctrl)->track; -#if MAX_FD == 4 - fdctrl->fifo[2] = drv2(fdctrl)->track; - fdctrl->fifo[3] = drv3(fdctrl)->track; -#else - fdctrl->fifo[2] = 0; - fdctrl->fifo[3] = 0; -#endif - /* timers */ - fdctrl->fifo[4] = fdctrl->timer0; - fdctrl->fifo[5] = (fdctrl->timer1 << 1) | (fdctrl->dor & FD_DOR_DMAEN ? 1 : 0); - fdctrl->fifo[6] = cur_drv->last_sect; - fdctrl->fifo[7] = (fdctrl->lock << 7) | - (cur_drv->perpendicular << 2); - fdctrl->fifo[8] = fdctrl->config; - fdctrl->fifo[9] = fdctrl->precomp_trk; - fdctrl_to_result_phase(fdctrl, 10); -} - -static void fdctrl_handle_version(FDCtrl *fdctrl, int direction) -{ - /* Controller's version */ - fdctrl->fifo[0] = fdctrl->version; - fdctrl_to_result_phase(fdctrl, 1); -} - -static void fdctrl_handle_partid(FDCtrl *fdctrl, int direction) -{ - fdctrl->fifo[0] = 0x41; /* Stepping 1 */ - fdctrl_to_result_phase(fdctrl, 1); -} - -static void fdctrl_handle_restore(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv = get_cur_drv(fdctrl); - - /* Drives position */ - drv0(fdctrl)->track = fdctrl->fifo[3]; - drv1(fdctrl)->track = fdctrl->fifo[4]; -#if MAX_FD == 4 - drv2(fdctrl)->track = fdctrl->fifo[5]; - drv3(fdctrl)->track = fdctrl->fifo[6]; -#endif - /* timers */ - fdctrl->timer0 = fdctrl->fifo[7]; - fdctrl->timer1 = fdctrl->fifo[8]; - cur_drv->last_sect = fdctrl->fifo[9]; - fdctrl->lock = fdctrl->fifo[10] >> 7; - cur_drv->perpendicular = (fdctrl->fifo[10] >> 2) & 0xF; - fdctrl->config = fdctrl->fifo[11]; - fdctrl->precomp_trk = fdctrl->fifo[12]; - fdctrl->pwrd = fdctrl->fifo[13]; - fdctrl_to_command_phase(fdctrl); -} - -static void fdctrl_handle_save(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv = get_cur_drv(fdctrl); - - fdctrl->fifo[0] = 0; - fdctrl->fifo[1] = 0; - /* Drives position */ - fdctrl->fifo[2] = drv0(fdctrl)->track; - fdctrl->fifo[3] = drv1(fdctrl)->track; -#if MAX_FD == 4 - fdctrl->fifo[4] = drv2(fdctrl)->track; - fdctrl->fifo[5] = drv3(fdctrl)->track; -#else - fdctrl->fifo[4] = 0; - fdctrl->fifo[5] = 0; -#endif - /* timers */ - fdctrl->fifo[6] = fdctrl->timer0; - fdctrl->fifo[7] = fdctrl->timer1; - fdctrl->fifo[8] = cur_drv->last_sect; - fdctrl->fifo[9] = (fdctrl->lock << 7) | - (cur_drv->perpendicular << 2); - fdctrl->fifo[10] = fdctrl->config; - fdctrl->fifo[11] = fdctrl->precomp_trk; - fdctrl->fifo[12] = fdctrl->pwrd; - fdctrl->fifo[13] = 0; - fdctrl->fifo[14] = 0; - fdctrl_to_result_phase(fdctrl, 15); -} - -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) + - (NANOSECONDS_PER_SECOND / 50)); -} - -static void fdctrl_handle_format_track(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv; - - SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); - cur_drv = get_cur_drv(fdctrl); - fdctrl->data_state |= FD_STATE_FORMAT; - if (fdctrl->fifo[0] & 0x80) - fdctrl->data_state |= FD_STATE_MULTI; - else - fdctrl->data_state &= ~FD_STATE_MULTI; - cur_drv->bps = - fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2]; -#if 0 - cur_drv->last_sect = - cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] : - fdctrl->fifo[3] / 2; -#else - cur_drv->last_sect = fdctrl->fifo[3]; -#endif - /* TODO: implement format using DMA expected by the Bochs BIOS - * and Linux fdformat (read 3 bytes per sector via DMA and fill - * the sector with the specified fill byte - */ - fdctrl->data_state &= ~FD_STATE_FORMAT; - fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); -} - -static void fdctrl_handle_specify(FDCtrl *fdctrl, int direction) -{ - fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF; - fdctrl->timer1 = fdctrl->fifo[2] >> 1; - if (fdctrl->fifo[2] & 1) - fdctrl->dor &= ~FD_DOR_DMAEN; - else - fdctrl->dor |= FD_DOR_DMAEN; - /* No result back */ - fdctrl_to_command_phase(fdctrl); -} - -static void fdctrl_handle_sense_drive_status(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv; - - SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); - cur_drv = get_cur_drv(fdctrl); - cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; - /* 1 Byte status back */ - fdctrl->fifo[0] = (cur_drv->ro << 6) | - (cur_drv->track == 0 ? 0x10 : 0x00) | - (cur_drv->head << 2) | - GET_CUR_DRV(fdctrl) | - 0x28; - fdctrl_to_result_phase(fdctrl, 1); -} - -static void fdctrl_handle_recalibrate(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv; - - SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); - cur_drv = get_cur_drv(fdctrl); - fd_recalibrate(cur_drv); - fdctrl_to_command_phase(fdctrl); - /* Raise Interrupt */ - fdctrl->status0 |= FD_SR0_SEEK; - fdctrl_raise_irq(fdctrl); -} - -static void fdctrl_handle_sense_interrupt_status(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv = get_cur_drv(fdctrl); - - if (fdctrl->reset_sensei > 0) { - fdctrl->fifo[0] = - FD_SR0_RDYCHG + FD_RESET_SENSEI_COUNT - fdctrl->reset_sensei; - fdctrl->reset_sensei--; - } else if (!(fdctrl->sra & FD_SRA_INTPEND)) { - fdctrl->fifo[0] = FD_SR0_INVCMD; - fdctrl_to_result_phase(fdctrl, 1); - return; - } else { - fdctrl->fifo[0] = - (fdctrl->status0 & ~(FD_SR0_HEAD | FD_SR0_DS1 | FD_SR0_DS0)) - | GET_CUR_DRV(fdctrl); - } - - fdctrl->fifo[1] = cur_drv->track; - fdctrl_to_result_phase(fdctrl, 2); - fdctrl_reset_irq(fdctrl); - fdctrl->status0 = FD_SR0_RDYCHG; -} - -static void fdctrl_handle_seek(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv; - - SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); - cur_drv = get_cur_drv(fdctrl); - fdctrl_to_command_phase(fdctrl); - /* The seek command just sends step pulses to the drive and doesn't care if - * there is a medium inserted of if it's banging the head against the drive. - */ - fd_seek(cur_drv, cur_drv->head, fdctrl->fifo[2], cur_drv->sect, 1); - /* Raise Interrupt */ - fdctrl->status0 |= FD_SR0_SEEK; - fdctrl_raise_irq(fdctrl); -} - -static void fdctrl_handle_perpendicular_mode(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv = get_cur_drv(fdctrl); - - if (fdctrl->fifo[1] & 0x80) - cur_drv->perpendicular = fdctrl->fifo[1] & 0x7; - /* No result back */ - fdctrl_to_command_phase(fdctrl); -} - -static void fdctrl_handle_configure(FDCtrl *fdctrl, int direction) -{ - fdctrl->config = fdctrl->fifo[2]; - fdctrl->precomp_trk = fdctrl->fifo[3]; - /* No result back */ - fdctrl_to_command_phase(fdctrl); -} - -static void fdctrl_handle_powerdown_mode(FDCtrl *fdctrl, int direction) -{ - fdctrl->pwrd = fdctrl->fifo[1]; - fdctrl->fifo[0] = fdctrl->fifo[1]; - fdctrl_to_result_phase(fdctrl, 1); -} - -static void fdctrl_handle_option(FDCtrl *fdctrl, int direction) -{ - /* No result back */ - fdctrl_to_command_phase(fdctrl); -} - -static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv = get_cur_drv(fdctrl); - uint32_t pos; - - pos = fdctrl->data_pos - 1; - pos %= FD_SECTOR_LEN; - if (fdctrl->fifo[pos] & 0x80) { - /* Command parameters done */ - if (fdctrl->fifo[pos] & 0x40) { - fdctrl->fifo[0] = fdctrl->fifo[1]; - fdctrl->fifo[2] = 0; - fdctrl->fifo[3] = 0; - fdctrl_to_result_phase(fdctrl, 4); - } else { - fdctrl_to_command_phase(fdctrl); - } - } else if (fdctrl->data_len > 7) { - /* ERROR */ - fdctrl->fifo[0] = 0x80 | - (cur_drv->head << 2) | GET_CUR_DRV(fdctrl); - fdctrl_to_result_phase(fdctrl, 1); - } -} - -static void fdctrl_handle_relative_seek_in(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv; - - SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); - cur_drv = get_cur_drv(fdctrl); - if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) { - fd_seek(cur_drv, cur_drv->head, cur_drv->max_track - 1, - cur_drv->sect, 1); - } else { - fd_seek(cur_drv, cur_drv->head, - cur_drv->track + fdctrl->fifo[2], cur_drv->sect, 1); - } - fdctrl_to_command_phase(fdctrl); - /* Raise Interrupt */ - fdctrl->status0 |= FD_SR0_SEEK; - fdctrl_raise_irq(fdctrl); -} - -static void fdctrl_handle_relative_seek_out(FDCtrl *fdctrl, int direction) -{ - FDrive *cur_drv; - - SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); - cur_drv = get_cur_drv(fdctrl); - if (fdctrl->fifo[2] > cur_drv->track) { - fd_seek(cur_drv, cur_drv->head, 0, cur_drv->sect, 1); - } else { - fd_seek(cur_drv, cur_drv->head, - cur_drv->track - fdctrl->fifo[2], cur_drv->sect, 1); - } - fdctrl_to_command_phase(fdctrl); - /* Raise Interrupt */ - fdctrl->status0 |= FD_SR0_SEEK; - fdctrl_raise_irq(fdctrl); -} - -/* - * Handlers for the execution phase of each command - */ -typedef struct FDCtrlCommand { - uint8_t value; - uint8_t mask; - const char* name; - int parameters; - void (*handler)(FDCtrl *fdctrl, int direction); - int direction; -} FDCtrlCommand; - -static const FDCtrlCommand handlers[] = { - { FD_CMD_READ, 0x1f, "READ", 8, fdctrl_start_transfer, FD_DIR_READ }, - { FD_CMD_WRITE, 0x3f, "WRITE", 8, fdctrl_start_transfer, FD_DIR_WRITE }, - { FD_CMD_SEEK, 0xff, "SEEK", 2, fdctrl_handle_seek }, - { FD_CMD_SENSE_INTERRUPT_STATUS, 0xff, "SENSE INTERRUPT STATUS", 0, fdctrl_handle_sense_interrupt_status }, - { FD_CMD_RECALIBRATE, 0xff, "RECALIBRATE", 1, fdctrl_handle_recalibrate }, - { FD_CMD_FORMAT_TRACK, 0xbf, "FORMAT TRACK", 5, fdctrl_handle_format_track }, - { FD_CMD_READ_TRACK, 0xbf, "READ TRACK", 8, fdctrl_start_transfer, FD_DIR_READ }, - { FD_CMD_RESTORE, 0xff, "RESTORE", 17, fdctrl_handle_restore }, /* part of READ DELETED DATA */ - { FD_CMD_SAVE, 0xff, "SAVE", 0, fdctrl_handle_save }, /* part of READ DELETED DATA */ - { FD_CMD_READ_DELETED, 0x1f, "READ DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_READ }, - { FD_CMD_SCAN_EQUAL, 0x1f, "SCAN EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANE }, - { FD_CMD_VERIFY, 0x1f, "VERIFY", 8, fdctrl_start_transfer, FD_DIR_VERIFY }, - { FD_CMD_SCAN_LOW_OR_EQUAL, 0x1f, "SCAN LOW OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANL }, - { FD_CMD_SCAN_HIGH_OR_EQUAL, 0x1f, "SCAN HIGH OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANH }, - { FD_CMD_WRITE_DELETED, 0x3f, "WRITE DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_WRITE }, - { FD_CMD_READ_ID, 0xbf, "READ ID", 1, fdctrl_handle_readid }, - { FD_CMD_SPECIFY, 0xff, "SPECIFY", 2, fdctrl_handle_specify }, - { FD_CMD_SENSE_DRIVE_STATUS, 0xff, "SENSE DRIVE STATUS", 1, fdctrl_handle_sense_drive_status }, - { FD_CMD_PERPENDICULAR_MODE, 0xff, "PERPENDICULAR MODE", 1, fdctrl_handle_perpendicular_mode }, - { FD_CMD_CONFIGURE, 0xff, "CONFIGURE", 3, fdctrl_handle_configure }, - { FD_CMD_POWERDOWN_MODE, 0xff, "POWERDOWN MODE", 2, fdctrl_handle_powerdown_mode }, - { FD_CMD_OPTION, 0xff, "OPTION", 1, fdctrl_handle_option }, - { FD_CMD_DRIVE_SPECIFICATION_COMMAND, 0xff, "DRIVE SPECIFICATION COMMAND", 5, fdctrl_handle_drive_specification_command }, - { FD_CMD_RELATIVE_SEEK_OUT, 0xff, "RELATIVE SEEK OUT", 2, fdctrl_handle_relative_seek_out }, - { FD_CMD_FORMAT_AND_WRITE, 0xff, "FORMAT AND WRITE", 10, fdctrl_unimplemented }, - { FD_CMD_RELATIVE_SEEK_IN, 0xff, "RELATIVE SEEK IN", 2, fdctrl_handle_relative_seek_in }, - { FD_CMD_LOCK, 0x7f, "LOCK", 0, fdctrl_handle_lock }, - { FD_CMD_DUMPREG, 0xff, "DUMPREG", 0, fdctrl_handle_dumpreg }, - { FD_CMD_VERSION, 0xff, "VERSION", 0, fdctrl_handle_version }, - { FD_CMD_PART_ID, 0xff, "PART ID", 0, fdctrl_handle_partid }, - { FD_CMD_WRITE, 0x1f, "WRITE (BeOS)", 8, fdctrl_start_transfer, FD_DIR_WRITE }, /* not in specification ; BeOS 4.5 bug */ - { 0, 0, "unknown", 0, fdctrl_unimplemented }, /* default handler */ -}; -/* Associate command to an index in the 'handlers' array */ -static uint8_t command_to_handler[256]; - -static const FDCtrlCommand *get_command(uint8_t cmd) -{ - int idx; - - idx = command_to_handler[cmd]; - FLOPPY_DPRINTF("%s command\n", handlers[idx].name); - return &handlers[idx]; -} - -static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value) -{ - FDrive *cur_drv; - const FDCtrlCommand *cmd; - uint32_t pos; - - /* Reset mode */ - if (!(fdctrl->dor & FD_DOR_nRESET)) { - FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); - return; - } - if (!(fdctrl->msr & FD_MSR_RQM) || (fdctrl->msr & FD_MSR_DIO)) { - FLOPPY_DPRINTF("error: controller not ready for writing\n"); - return; - } - fdctrl->dsr &= ~FD_DSR_PWRDOWN; - - FLOPPY_DPRINTF("%s: %02x\n", __func__, value); - - /* If data_len spans multiple sectors, the current position in the FIFO - * wraps around while fdctrl->data_pos is the real position in the whole - * request. */ - pos = fdctrl->data_pos++; - pos %= FD_SECTOR_LEN; - fdctrl->fifo[pos] = value; - - if (fdctrl->data_pos == fdctrl->data_len) { - fdctrl->msr &= ~FD_MSR_RQM; - } - - switch (fdctrl->phase) { - case FD_PHASE_EXECUTION: - /* For DMA requests, RQM should be cleared during execution phase, so - * we would have errored out above. */ - assert(fdctrl->msr & FD_MSR_NONDMA); - - /* FIFO data write */ - if (pos == FD_SECTOR_LEN - 1 || - fdctrl->data_pos == fdctrl->data_len) { - cur_drv = get_cur_drv(fdctrl); - if (blk_write(cur_drv->blk, fd_sector(cur_drv), fdctrl->fifo, 1) - < 0) { - FLOPPY_DPRINTF("error writing sector %d\n", - fd_sector(cur_drv)); - break; - } - if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) { - FLOPPY_DPRINTF("error seeking to next sector %d\n", - fd_sector(cur_drv)); - break; - } - } - - /* Switch to result phase when done with the transfer */ - if (fdctrl->data_pos == fdctrl->data_len) { - fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); - } - break; - - case FD_PHASE_COMMAND: - assert(!(fdctrl->msr & FD_MSR_NONDMA)); - assert(fdctrl->data_pos < FD_SECTOR_LEN); - - if (pos == 0) { - /* The first byte specifies the command. Now we start reading - * as many parameters as this command requires. */ - cmd = get_command(value); - fdctrl->data_len = cmd->parameters + 1; - if (cmd->parameters) { - fdctrl->msr |= FD_MSR_RQM; - } - fdctrl->msr |= FD_MSR_CMDBUSY; - } - - if (fdctrl->data_pos == fdctrl->data_len) { - /* We have all parameters now, execute the command */ - fdctrl->phase = FD_PHASE_EXECUTION; - - if (fdctrl->data_state & FD_STATE_FORMAT) { - fdctrl_format_sector(fdctrl); - break; - } - - cmd = get_command(fdctrl->fifo[0]); - FLOPPY_DPRINTF("Calling handler for '%s'\n", cmd->name); - cmd->handler(fdctrl, cmd->direction); - } - break; - - case FD_PHASE_RESULT: - default: - abort(); - } -} - -static void fdctrl_result_timer(void *opaque) -{ - FDCtrl *fdctrl = opaque; - FDrive *cur_drv = get_cur_drv(fdctrl); - - /* Pretend we are spinning. - * This is needed for Coherent, which uses READ ID to check for - * sector interleaving. - */ - if (cur_drv->last_sect != 0) { - cur_drv->sect = (cur_drv->sect % cur_drv->last_sect) + 1; - } - /* READ_ID can't automatically succeed! */ - if (fdctrl->check_media_rate && - (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) { - FLOPPY_DPRINTF("read id rate mismatch (fdc=%d, media=%d)\n", - fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate); - fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00); - } else { - fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); - } -} - -static void fdctrl_change_cb(void *opaque, bool load) -{ - FDrive *drive = opaque; - - drive->media_changed = 1; - drive->media_validated = false; - fd_revalidate(drive); -} - -static const BlockDevOps fdctrl_block_ops = { - .change_media_cb = fdctrl_change_cb, -}; - -/* Init functions */ -static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp) -{ - unsigned int i; - FDrive *drive; - - for (i = 0; i < MAX_FD; i++) { - drive = &fdctrl->drives[i]; - drive->fdctrl = fdctrl; - - if (drive->blk) { - if (blk_get_on_error(drive->blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC) { - error_setg(errp, "fdc doesn't support drive option werror"); - return; - } - if (blk_get_on_error(drive->blk, 1) != BLOCKDEV_ON_ERROR_REPORT) { - error_setg(errp, "fdc doesn't support drive option rerror"); - return; - } - } - - fd_init(drive); - if (drive->blk) { - blk_set_dev_ops(drive->blk, &fdctrl_block_ops, drive); - pick_drive_type(drive); - } - fd_revalidate(drive); - } -} - -ISADevice *fdctrl_init_isa(ISABus *bus, DriveInfo **fds) -{ - DeviceState *dev; - ISADevice *isadev; - - isadev = isa_try_create(bus, TYPE_ISA_FDC); - if (!isadev) { - return NULL; - } - dev = DEVICE(isadev); - - if (fds[0]) { - qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fds[0]), - &error_fatal); - } - if (fds[1]) { - qdev_prop_set_drive(dev, "driveB", blk_by_legacy_dinfo(fds[1]), - &error_fatal); - } - qdev_init_nofail(dev); - - return isadev; -} - -void fdctrl_init_sysbus(qemu_irq irq, int dma_chann, - hwaddr mmio_base, DriveInfo **fds) -{ - FDCtrl *fdctrl; - DeviceState *dev; - SysBusDevice *sbd; - FDCtrlSysBus *sys; - - dev = qdev_create(NULL, "sysbus-fdc"); - sys = SYSBUS_FDC(dev); - fdctrl = &sys->state; - fdctrl->dma_chann = dma_chann; /* FIXME */ - if (fds[0]) { - qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fds[0]), - &error_fatal); - } - if (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); - sysbus_connect_irq(sbd, 0, irq); - sysbus_mmio_map(sbd, 0, mmio_base); -} - -void sun4m_fdctrl_init(qemu_irq irq, hwaddr io_base, - DriveInfo **fds, qemu_irq *fdc_tc) -{ - DeviceState *dev; - FDCtrlSysBus *sys; - - dev = qdev_create(NULL, "SUNW,fdtwo"); - if (fds[0]) { - qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(fds[0]), - &error_fatal); - } - qdev_init_nofail(dev); - sys = SYSBUS_FDC(dev); - sysbus_connect_irq(SYS_BUS_DEVICE(sys), 0, irq); - sysbus_mmio_map(SYS_BUS_DEVICE(sys), 0, io_base); - *fdc_tc = qdev_get_gpio_in(dev, 0); -} - -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; - for (i = ARRAY_SIZE(handlers) - 1; i >= 0; i--) { - for (j = 0; j < sizeof(command_to_handler); j++) { - if ((j & handlers[i].mask) == handlers[i].value) { - command_to_handler[j] = i; - } - } - } - } - - FLOPPY_DPRINTF("init controller\n"); - fdctrl->fifo = qemu_memalign(512, FD_SECTOR_LEN); - fdctrl->fifo_size = 512; - fdctrl->result_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - fdctrl_result_timer, fdctrl); - - fdctrl->version = 0x90; /* Intel 82078 controller */ - fdctrl->config = FD_CONFIG_EIS | FD_CONFIG_EFIFO; /* Implicit seek, polling & FIFO enabled */ - fdctrl->num_floppies = MAX_FD; - - if (fdctrl->dma_chann != -1) { - 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); -} - -static const MemoryRegionPortio fdc_portio_list[] = { - { 1, 5, 1, .read = fdctrl_read, .write = fdctrl_write }, - { 7, 1, 1, .read = fdctrl_read, .write = fdctrl_write }, - PORTIO_END_OF_LIST(), -}; - -static void isabus_fdc_realize(DeviceState *dev, Error **errp) -{ - ISADevice *isadev = ISA_DEVICE(dev); - FDCtrlISABus *isa = ISA_FDC(dev); - FDCtrl *fdctrl = &isa->state; - Error *err = NULL; - - isa_register_portio_list(isadev, isa->iobase, fdc_portio_list, fdctrl, - "fdc"); - - 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); - if (err != NULL) { - error_propagate(errp, err); - return; - } -} - -static void sysbus_fdc_initfn(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - FDCtrlSysBus *sys = SYSBUS_FDC(obj); - FDCtrl *fdctrl = &sys->state; - - fdctrl->dma_chann = -1; - - memory_region_init_io(&fdctrl->iomem, obj, &fdctrl_mem_ops, fdctrl, - "fdc", 0x08); - sysbus_init_mmio(sbd, &fdctrl->iomem); -} - -static void sun4m_fdc_initfn(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(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); -} - -static void sysbus_fdc_common_initfn(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - FDCtrlSysBus *sys = SYSBUS_FDC(obj); - FDCtrl *fdctrl = &sys->state; - - qdev_set_legacy_instance_id(dev, 0 /* io */, 2); /* FIXME */ - - sysbus_init_irq(sbd, &fdctrl->irq); - qdev_init_gpio_in(dev, fdctrl_handle_tc, 1); -} - -static void sysbus_fdc_common_realize(DeviceState *dev, Error **errp) -{ - FDCtrlSysBus *sys = SYSBUS_FDC(dev); - FDCtrl *fdctrl = &sys->state; - - fdctrl_realize_common(fdctrl, errp); -} - -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, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(state, FDCtrlISABus, 0, vmstate_fdc, FDCtrl), - VMSTATE_END_OF_LIST() - } -}; - -static Property isa_fdc_properties[] = { - DEFINE_PROP_UINT32("iobase", FDCtrlISABus, iobase, 0x3f0), - DEFINE_PROP_UINT32("irq", FDCtrlISABus, irq, 6), - DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2), - DEFINE_PROP_DRIVE("driveA", FDCtrlISABus, state.drives[0].blk), - 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(), -}; - -static void isabus_fdc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = isabus_fdc_realize; - dc->fw_name = "fdc"; - dc->reset = fdctrl_external_reset_isa; - dc->vmsd = &vmstate_isa_fdc; - dc->props = isa_fdc_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static void isabus_fdc_instance_init(Object *obj) -{ - FDCtrlISABus *isa = ISA_FDC(obj); - - device_add_bootindex_property(obj, &isa->bootindexA, - "bootindexA", "/floppy@0", - DEVICE(obj), NULL); - device_add_bootindex_property(obj, &isa->bootindexB, - "bootindexB", "/floppy@1", - DEVICE(obj), NULL); -} - -static const TypeInfo isa_fdc_info = { - .name = TYPE_ISA_FDC, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(FDCtrlISABus), - .class_init = isabus_fdc_class_init, - .instance_init = isabus_fdc_instance_init, -}; - -static const VMStateDescription vmstate_sysbus_fdc ={ - .name = "fdc", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(state, FDCtrlSysBus, 0, vmstate_fdc, FDCtrl), - VMSTATE_END_OF_LIST() - } -}; - -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(), -}; - -static void sysbus_fdc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->props = sysbus_fdc_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo sysbus_fdc_info = { - .name = "sysbus-fdc", - .parent = TYPE_SYSBUS_FDC, - .instance_init = sysbus_fdc_initfn, - .class_init = sysbus_fdc_class_init, -}; - -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(), -}; - -static void sun4m_fdc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->props = sun4m_fdc_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo sun4m_fdc_info = { - .name = "SUNW,fdtwo", - .parent = TYPE_SYSBUS_FDC, - .instance_init = sun4m_fdc_initfn, - .class_init = sun4m_fdc_class_init, -}; - -static void sysbus_fdc_common_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = sysbus_fdc_common_realize; - dc->reset = fdctrl_external_reset_sysbus; - dc->vmsd = &vmstate_sysbus_fdc; -} - -static const TypeInfo sysbus_fdc_type_info = { - .name = TYPE_SYSBUS_FDC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(FDCtrlSysBus), - .instance_init = sysbus_fdc_common_initfn, - .abstract = true, - .class_init = sysbus_fdc_common_class_init, -}; - -static void fdc_register_types(void) -{ - type_register_static(&isa_fdc_info); - type_register_static(&sysbus_fdc_type_info); - type_register_static(&sysbus_fdc_info); - type_register_static(&sun4m_fdc_info); -} - -type_init(fdc_register_types) diff --git a/qemu/hw/block/hd-geometry.c b/qemu/hw/block/hd-geometry.c deleted file mode 100644 index 6d02192db..000000000 --- a/qemu/hw/block/hd-geometry.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Hard disk geometry utilities - * - * Copyright (C) 2012 Red Hat, Inc. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright (c) 2003 Fabrice Bellard - * - * 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/block-backend.h" -#include "hw/block/block.h" -#include "trace.h" - -struct partition { - uint8_t boot_ind; /* 0x80 - active */ - uint8_t head; /* starting head */ - uint8_t sector; /* starting sector */ - uint8_t cyl; /* starting cylinder */ - uint8_t sys_ind; /* What partition type */ - uint8_t end_head; /* end head */ - uint8_t end_sector; /* end sector */ - uint8_t end_cyl; /* end cylinder */ - uint32_t start_sect; /* starting sector counting from 0 */ - uint32_t nr_sects; /* nr of sectors in partition */ -} QEMU_PACKED; - -/* try to guess the disk logical geometry from the MSDOS partition table. - Return 0 if OK, -1 if could not guess */ -static int guess_disk_lchs(BlockBackend *blk, - int *pcylinders, int *pheads, int *psectors) -{ - uint8_t buf[BDRV_SECTOR_SIZE]; - int i, heads, sectors, cylinders; - struct partition *p; - uint32_t nr_sects; - uint64_t nb_sectors; - - blk_get_geometry(blk, &nb_sectors); - - /** - * The function will be invoked during startup not only in sync I/O mode, - * but also in async I/O mode. So the I/O throttling function has to - * be disabled temporarily here, not permanently. - */ - if (blk_read_unthrottled(blk, 0, buf, 1) < 0) { - return -1; - } - /* test msdos magic */ - if (buf[510] != 0x55 || buf[511] != 0xaa) { - return -1; - } - for (i = 0; i < 4; i++) { - p = ((struct partition *)(buf + 0x1be)) + i; - nr_sects = le32_to_cpu(p->nr_sects); - if (nr_sects && p->end_head) { - /* We make the assumption that the partition terminates on - a cylinder boundary */ - heads = p->end_head + 1; - sectors = p->end_sector & 63; - if (sectors == 0) { - continue; - } - cylinders = nb_sectors / (heads * sectors); - if (cylinders < 1 || cylinders > 16383) { - continue; - } - *pheads = heads; - *psectors = sectors; - *pcylinders = cylinders; - trace_hd_geometry_lchs_guess(blk, cylinders, heads, sectors); - return 0; - } - } - return -1; -} - -static void guess_chs_for_size(BlockBackend *blk, - uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs) -{ - uint64_t nb_sectors; - int cylinders; - - blk_get_geometry(blk, &nb_sectors); - - cylinders = nb_sectors / (16 * 63); - if (cylinders > 16383) { - cylinders = 16383; - } else if (cylinders < 2) { - cylinders = 2; - } - *pcyls = cylinders; - *pheads = 16; - *psecs = 63; -} - -void hd_geometry_guess(BlockBackend *blk, - uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs, - int *ptrans) -{ - int cylinders, heads, secs, translation; - HDGeometry geo; - - /* Try to probe the backing device geometry, otherwise fallback - to the old logic. (as of 12/2014 probing only succeeds on DASDs) */ - if (blk_probe_geometry(blk, &geo) == 0) { - *pcyls = geo.cylinders; - *psecs = geo.sectors; - *pheads = geo.heads; - translation = BIOS_ATA_TRANSLATION_NONE; - } else if (guess_disk_lchs(blk, &cylinders, &heads, &secs) < 0) { - /* no LCHS guess: use a standard physical disk geometry */ - guess_chs_for_size(blk, pcyls, pheads, psecs); - translation = hd_bios_chs_auto_trans(*pcyls, *pheads, *psecs); - } else if (heads > 16) { - /* LCHS guess with heads > 16 means that a BIOS LBA - translation was active, so a standard physical disk - geometry is OK */ - guess_chs_for_size(blk, pcyls, pheads, psecs); - translation = *pcyls * *pheads <= 131072 - ? BIOS_ATA_TRANSLATION_LARGE - : BIOS_ATA_TRANSLATION_LBA; - } else { - /* LCHS guess with heads <= 16: use as physical geometry */ - *pcyls = cylinders; - *pheads = heads; - *psecs = secs; - /* disable any translation to be in sync with - the logical geometry */ - translation = BIOS_ATA_TRANSLATION_NONE; - } - if (ptrans) { - *ptrans = translation; - } - trace_hd_geometry_guess(blk, *pcyls, *pheads, *psecs, translation); -} - -int hd_bios_chs_auto_trans(uint32_t cyls, uint32_t heads, uint32_t secs) -{ - return cyls <= 1024 && heads <= 16 && secs <= 63 - ? BIOS_ATA_TRANSLATION_NONE - : BIOS_ATA_TRANSLATION_LBA; -} diff --git a/qemu/hw/block/m25p80.c b/qemu/hw/block/m25p80.c deleted file mode 100644 index 906b71257..000000000 --- a/qemu/hw/block/m25p80.c +++ /dev/null @@ -1,1005 +0,0 @@ -/* - * ST M25P80 emulator. Emulate all SPI flash devices based on the m25p80 command - * set. Known devices table current as of Jun/2012 and taken from linux. - * See drivers/mtd/devices/m25p80.c. - * - * Copyright (C) 2011 Edgar E. Iglesias - * Copyright (C) 2012 Peter A. G. Crosthwaite - * Copyright (C) 2012 PetaLogix - * - * 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) a later version 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 . - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "hw/ssi/ssi.h" -#include "qemu/bitops.h" - -#ifndef M25P80_ERR_DEBUG -#define M25P80_ERR_DEBUG 0 -#endif - -#define DB_PRINT_L(level, ...) do { \ - if (M25P80_ERR_DEBUG > (level)) { \ - fprintf(stderr, ": %s: ", __func__); \ - fprintf(stderr, ## __VA_ARGS__); \ - } \ -} while (0); - -/* Fields for FlashPartInfo->flags */ - -/* erase capabilities */ -#define ER_4K 1 -#define ER_32K 2 -/* set to allow the page program command to write 0s back to 1. Useful for - * modelling EEPROM with SPI flash command set - */ -#define EEPROM 0x100 - -/* 16 MiB max in 3 byte address mode */ -#define MAX_3BYTES_SIZE 0x1000000 - -typedef struct FlashPartInfo { - const char *part_name; - /* jedec code. (jedec >> 16) & 0xff is the 1st byte, >> 8 the 2nd etc */ - uint32_t jedec; - /* extended jedec code */ - uint16_t ext_jedec; - /* there is confusion between manufacturers as to what a sector is. In this - * device model, a "sector" is the size that is erased by the ERASE_SECTOR - * command (opcode 0xd8). - */ - uint32_t sector_size; - uint32_t n_sectors; - uint32_t page_size; - uint16_t flags; -} FlashPartInfo; - -/* adapted from linux */ - -#define INFO(_part_name, _jedec, _ext_jedec, _sector_size, _n_sectors, _flags)\ - .part_name = (_part_name),\ - .jedec = (_jedec),\ - .ext_jedec = (_ext_jedec),\ - .sector_size = (_sector_size),\ - .n_sectors = (_n_sectors),\ - .page_size = 256,\ - .flags = (_flags),\ - -#define JEDEC_NUMONYX 0x20 -#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) }, - { INFO("at25fs040", 0x1f6604, 0, 64 << 10, 8, ER_4K) }, - - { INFO("at25df041a", 0x1f4401, 0, 64 << 10, 8, ER_4K) }, - { INFO("at25df321a", 0x1f4701, 0, 64 << 10, 64, ER_4K) }, - { INFO("at25df641", 0x1f4800, 0, 64 << 10, 128, ER_4K) }, - - { INFO("at26f004", 0x1f0400, 0, 64 << 10, 8, ER_4K) }, - { INFO("at26df081a", 0x1f4501, 0, 64 << 10, 16, ER_4K) }, - { INFO("at26df161a", 0x1f4601, 0, 64 << 10, 32, ER_4K) }, - { INFO("at26df321", 0x1f4700, 0, 64 << 10, 64, ER_4K) }, - - { 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) }, - { INFO("en25q32b", 0x1c3016, 0, 64 << 10, 64, 0) }, - { INFO("en25p64", 0x1c2017, 0, 64 << 10, 128, 0) }, - { INFO("en25q64", 0x1c3017, 0, 64 << 10, 128, ER_4K) }, - - /* GigaDevice */ - { INFO("gd25q32", 0xc84016, 0, 64 << 10, 64, ER_4K) }, - { INFO("gd25q64", 0xc84017, 0, 64 << 10, 128, ER_4K) }, - - /* Intel/Numonyx -- xxxs33b */ - { INFO("160s33b", 0x898911, 0, 64 << 10, 32, 0) }, - { INFO("320s33b", 0x898912, 0, 64 << 10, 64, 0) }, - { INFO("640s33b", 0x898913, 0, 64 << 10, 128, 0) }, - { INFO("n25q064", 0x20ba17, 0, 64 << 10, 128, 0) }, - - /* Macronix */ - { INFO("mx25l2005a", 0xc22012, 0, 64 << 10, 4, ER_4K) }, - { INFO("mx25l4005a", 0xc22013, 0, 64 << 10, 8, ER_4K) }, - { INFO("mx25l8005", 0xc22014, 0, 64 << 10, 16, 0) }, - { INFO("mx25l1606e", 0xc22015, 0, 64 << 10, 32, ER_4K) }, - { INFO("mx25l3205d", 0xc22016, 0, 64 << 10, 64, 0) }, - { INFO("mx25l6405d", 0xc22017, 0, 64 << 10, 128, 0) }, - { INFO("mx25l12805d", 0xc22018, 0, 64 << 10, 256, 0) }, - { INFO("mx25l12855e", 0xc22618, 0, 64 << 10, 256, 0) }, - { INFO("mx25l25635e", 0xc22019, 0, 64 << 10, 512, 0) }, - { INFO("mx25l25655e", 0xc22619, 0, 64 << 10, 512, 0) }, - - /* Micron */ - { INFO("n25q032a11", 0x20bb16, 0, 64 << 10, 64, ER_4K) }, - { INFO("n25q032a13", 0x20ba16, 0, 64 << 10, 64, ER_4K) }, - { INFO("n25q064a11", 0x20bb17, 0, 64 << 10, 128, ER_4K) }, - { INFO("n25q064a13", 0x20ba17, 0, 64 << 10, 128, ER_4K) }, - { INFO("n25q128a11", 0x20bb18, 0, 64 << 10, 256, ER_4K) }, - { INFO("n25q128a13", 0x20ba18, 0, 64 << 10, 256, ER_4K) }, - { INFO("n25q256a11", 0x20bb19, 0, 64 << 10, 512, ER_4K) }, - { INFO("n25q256a13", 0x20ba19, 0, 64 << 10, 512, ER_4K) }, - - /* Spansion -- single (large) sector size only, at least - * for the chips listed here (without boot sectors). - */ - { INFO("s25sl032p", 0x010215, 0x4d00, 64 << 10, 64, ER_4K) }, - { INFO("s25sl064p", 0x010216, 0x4d00, 64 << 10, 128, ER_4K) }, - { INFO("s25fl256s0", 0x010219, 0x4d00, 256 << 10, 128, 0) }, - { INFO("s25fl256s1", 0x010219, 0x4d01, 64 << 10, 512, 0) }, - { INFO("s25fl512s", 0x010220, 0x4d00, 256 << 10, 256, 0) }, - { INFO("s70fl01gs", 0x010221, 0x4d00, 256 << 10, 256, 0) }, - { INFO("s25sl12800", 0x012018, 0x0300, 256 << 10, 64, 0) }, - { INFO("s25sl12801", 0x012018, 0x0301, 64 << 10, 256, 0) }, - { INFO("s25fl129p0", 0x012018, 0x4d00, 256 << 10, 64, 0) }, - { INFO("s25fl129p1", 0x012018, 0x4d01, 64 << 10, 256, 0) }, - { INFO("s25sl004a", 0x010212, 0, 64 << 10, 8, 0) }, - { INFO("s25sl008a", 0x010213, 0, 64 << 10, 16, 0) }, - { INFO("s25sl016a", 0x010214, 0, 64 << 10, 32, 0) }, - { INFO("s25sl032a", 0x010215, 0, 64 << 10, 64, 0) }, - { INFO("s25sl064a", 0x010216, 0, 64 << 10, 128, 0) }, - { INFO("s25fl016k", 0xef4015, 0, 64 << 10, 32, ER_4K | ER_32K) }, - { INFO("s25fl064k", 0xef4017, 0, 64 << 10, 128, ER_4K | ER_32K) }, - - /* SST -- large erase sizes are "overlays", "sectors" are 4<< 10 */ - { INFO("sst25vf040b", 0xbf258d, 0, 64 << 10, 8, ER_4K) }, - { INFO("sst25vf080b", 0xbf258e, 0, 64 << 10, 16, ER_4K) }, - { INFO("sst25vf016b", 0xbf2541, 0, 64 << 10, 32, ER_4K) }, - { INFO("sst25vf032b", 0xbf254a, 0, 64 << 10, 64, ER_4K) }, - { INFO("sst25wf512", 0xbf2501, 0, 64 << 10, 1, ER_4K) }, - { 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) }, - { INFO("m25p10", 0x202011, 0, 32 << 10, 4, 0) }, - { INFO("m25p20", 0x202012, 0, 64 << 10, 4, 0) }, - { INFO("m25p40", 0x202013, 0, 64 << 10, 8, 0) }, - { INFO("m25p80", 0x202014, 0, 64 << 10, 16, 0) }, - { INFO("m25p16", 0x202015, 0, 64 << 10, 32, 0) }, - { INFO("m25p32", 0x202016, 0, 64 << 10, 64, 0) }, - { INFO("m25p64", 0x202017, 0, 64 << 10, 128, 0) }, - { INFO("m25p128", 0x202018, 0, 256 << 10, 64, 0) }, - { INFO("n25q032", 0x20ba16, 0, 64 << 10, 64, 0) }, - - { INFO("m45pe10", 0x204011, 0, 64 << 10, 2, 0) }, - { INFO("m45pe80", 0x204014, 0, 64 << 10, 16, 0) }, - { INFO("m45pe16", 0x204015, 0, 64 << 10, 32, 0) }, - - { INFO("m25pe20", 0x208012, 0, 64 << 10, 4, 0) }, - { INFO("m25pe80", 0x208014, 0, 64 << 10, 16, 0) }, - { INFO("m25pe16", 0x208015, 0, 64 << 10, 32, ER_4K) }, - - { INFO("m25px32", 0x207116, 0, 64 << 10, 64, ER_4K) }, - { INFO("m25px32-s0", 0x207316, 0, 64 << 10, 64, ER_4K) }, - { INFO("m25px32-s1", 0x206316, 0, 64 << 10, 64, ER_4K) }, - { INFO("m25px64", 0x207117, 0, 64 << 10, 128, 0) }, - - /* Winbond -- w25x "blocks" are 64k, "sectors" are 4KiB */ - { INFO("w25x10", 0xef3011, 0, 64 << 10, 2, ER_4K) }, - { INFO("w25x20", 0xef3012, 0, 64 << 10, 4, ER_4K) }, - { INFO("w25x40", 0xef3013, 0, 64 << 10, 8, ER_4K) }, - { INFO("w25x80", 0xef3014, 0, 64 << 10, 16, ER_4K) }, - { INFO("w25x16", 0xef3015, 0, 64 << 10, 32, ER_4K) }, - { INFO("w25x32", 0xef3016, 0, 64 << 10, 64, ER_4K) }, - { INFO("w25q32", 0xef4016, 0, 64 << 10, 64, ER_4K) }, - { INFO("w25q32dw", 0xef6016, 0, 64 << 10, 64, ER_4K) }, - { INFO("w25x64", 0xef3017, 0, 64 << 10, 128, ER_4K) }, - { INFO("w25q64", 0xef4017, 0, 64 << 10, 128, ER_4K) }, - { INFO("w25q80", 0xef5014, 0, 64 << 10, 16, ER_4K) }, - { INFO("w25q80bl", 0xef4014, 0, 64 << 10, 16, ER_4K) }, - { INFO("w25q256", 0xef4019, 0, 64 << 10, 512, ER_4K) }, - - { 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 { - NOP = 0, - WRSR = 0x1, - WRDI = 0x4, - RDSR = 0x5, - WREN = 0x6, - JEDEC_READ = 0x9f, - BULK_ERASE = 0xc7, - READ_FSR = 0x70, - - 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 = 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 { - STATE_IDLE, - STATE_PAGE_PROGRAM, - STATE_READ, - STATE_COLLECTING_DATA, - STATE_READING_DATA, -} CMDState; - -typedef struct Flash { - SSISlave parent_obj; - - BlockBackend *blk; - - uint8_t *storage; - uint32_t size; - int page_size; - - uint8_t state; - uint8_t data[16]; - uint32_t len; - uint32_t pos; - 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; - - const FlashPartInfo *pi; - -} Flash; - -typedef struct M25P80Class { - SSISlaveClass parent_class; - FlashPartInfo *pi; -} M25P80Class; - -#define TYPE_M25P80 "m25p80-generic" -#define M25P80(obj) \ - OBJECT_CHECK(Flash, (obj), TYPE_M25P80) -#define M25P80_CLASS(klass) \ - OBJECT_CLASS_CHECK(M25P80Class, (klass), TYPE_M25P80) -#define M25P80_GET_CLASS(obj) \ - OBJECT_GET_CLASS(M25P80Class, (obj), TYPE_M25P80) - -static void blk_sync_complete(void *opaque, int ret) -{ - /* do nothing. Masters do not directly interact with the backing store, - * only the working copy so no mutexing required. - */ -} - -static void flash_sync_page(Flash *s, int page) -{ - int blk_sector, nb_sectors; - QEMUIOVector iov; - - if (!s->blk || blk_is_read_only(s->blk)) { - return; - } - - blk_sector = (page * s->pi->page_size) / BDRV_SECTOR_SIZE; - nb_sectors = DIV_ROUND_UP(s->pi->page_size, BDRV_SECTOR_SIZE); - qemu_iovec_init(&iov, 1); - qemu_iovec_add(&iov, s->storage + blk_sector * BDRV_SECTOR_SIZE, - nb_sectors * BDRV_SECTOR_SIZE); - blk_aio_writev(s->blk, blk_sector, &iov, nb_sectors, blk_sync_complete, - NULL); -} - -static inline void flash_sync_area(Flash *s, int64_t off, int64_t len) -{ - int64_t start, end, nb_sectors; - QEMUIOVector iov; - - if (!s->blk || blk_is_read_only(s->blk)) { - return; - } - - assert(!(len % BDRV_SECTOR_SIZE)); - start = off / BDRV_SECTOR_SIZE; - end = (off + len) / BDRV_SECTOR_SIZE; - nb_sectors = end - start; - qemu_iovec_init(&iov, 1); - qemu_iovec_add(&iov, s->storage + (start * BDRV_SECTOR_SIZE), - nb_sectors * BDRV_SECTOR_SIZE); - blk_aio_writev(s->blk, start, &iov, nb_sectors, blk_sync_complete, NULL); -} - -static void flash_erase(Flash *s, int offset, FlashCMD cmd) -{ - uint32_t len; - uint8_t capa_to_assert = 0; - - switch (cmd) { - case ERASE_4K: - case ERASE4_4K: - len = 4 << 10; - capa_to_assert = ER_4K; - break; - case ERASE_32K: - len = 32 << 10; - capa_to_assert = ER_32K; - break; - case ERASE_SECTOR: - case ERASE4_SECTOR: - len = s->pi->sector_size; - break; - case BULK_ERASE: - len = s->size; - break; - default: - abort(); - } - - DB_PRINT_L(0, "offset = %#x, len = %d\n", offset, len); - if ((s->pi->flags & capa_to_assert) != capa_to_assert) { - qemu_log_mask(LOG_GUEST_ERROR, "M25P80: %d erase size not supported by" - " device\n", len); - } - - if (!s->write_enable) { - qemu_log_mask(LOG_GUEST_ERROR, "M25P80: erase with write protect!\n"); - return; - } - memset(s->storage + offset, 0xff, len); - flash_sync_area(s, offset, len); -} - -static inline void flash_sync_dirty(Flash *s, int64_t newpage) -{ - if (s->dirty_page >= 0 && s->dirty_page != newpage) { - flash_sync_page(s, s->dirty_page); - s->dirty_page = newpage; - } -} - -static inline -void flash_write8(Flash *s, uint64_t addr, uint8_t data) -{ - int64_t page = addr / s->pi->page_size; - uint8_t prev = s->storage[s->cur_addr]; - - if (!s->write_enable) { - qemu_log_mask(LOG_GUEST_ERROR, "M25P80: write with write protect!\n"); - } - - if ((prev ^ data) & data) { - DB_PRINT_L(1, "programming zero to one! addr=%" PRIx64 " %" PRIx8 - " -> %" PRIx8 "\n", addr, prev, data); - } - - if (s->pi->flags & EEPROM) { - s->storage[s->cur_addr] = data; - } else { - s->storage[s->cur_addr] &= data; - } - - flash_sync_dirty(s, page); - 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) -{ - 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; - - switch (s->cmd_in_progress) { - 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: - if (s->write_enable) { - 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: - 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: - 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; - default: - 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; - s->state = STATE_COLLECTING_DATA; - break; - - case QIOR: - case QIOR4: - switch ((s->pi->jedec >> 16) & 0xFF) { - case JEDEC_WINBOND: - case JEDEC_SPANSION: - s->needed_bytes = 6; - break; - default: - 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; - s->state = STATE_COLLECTING_DATA; - break; - - case WRSR: - if (s->write_enable) { - s->needed_bytes = 1; - s->pos = 0; - s->len = 0; - s->state = STATE_COLLECTING_DATA; - } - break; - - case WRDI: - s->write_enable = false; - break; - case WREN: - s->write_enable = true; - break; - - case RDSR: - s->data[0] = (!!s->write_enable) << 1; - s->pos = 0; - s->len = 1; - 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; - s->data[1] = (s->pi->jedec >> 8) & 0xff; - s->data[2] = s->pi->jedec & 0xff; - if (s->pi->ext_jedec) { - s->data[3] = (s->pi->ext_jedec >> 8) & 0xff; - s->data[4] = s->pi->ext_jedec & 0xff; - s->len = 5; - } else { - s->len = 3; - } - s->pos = 0; - s->state = STATE_READING_DATA; - break; - - case BULK_ERASE: - if (s->write_enable) { - DB_PRINT_L(0, "chip erase\n"); - flash_erase(s, 0, BULK_ERASE); - } else { - qemu_log_mask(LOG_GUEST_ERROR, "M25P80: chip erase with write " - "protect!\n"); - } - 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; - } -} - -static int m25p80_cs(SSISlave *ss, bool select) -{ - Flash *s = M25P80(ss); - - if (select) { - s->len = 0; - s->pos = 0; - s->state = STATE_IDLE; - flash_sync_dirty(s, -1); - } - - DB_PRINT_L(0, "%sselect\n", select ? "de" : ""); - - return 0; -} - -static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) -{ - Flash *s = M25P80(ss); - uint32_t r = 0; - - switch (s->state) { - - case STATE_PAGE_PROGRAM: - DB_PRINT_L(1, "page program cur_addr=%#" PRIx64 " data=%" PRIx8 "\n", - s->cur_addr, (uint8_t)tx); - flash_write8(s, s->cur_addr, (uint8_t)tx); - s->cur_addr++; - break; - - case STATE_READ: - r = s->storage[s->cur_addr]; - DB_PRINT_L(1, "READ 0x%" PRIx64 "=%" PRIx8 "\n", s->cur_addr, - (uint8_t)r); - s->cur_addr = (s->cur_addr + 1) % s->size; - break; - - case STATE_COLLECTING_DATA: - s->data[s->len] = (uint8_t)tx; - s->len++; - - if (s->len == s->needed_bytes) { - complete_collecting_data(s); - } - break; - - case STATE_READING_DATA: - r = s->data[s->pos]; - s->pos++; - if (s->pos == s->len) { - s->pos = 0; - s->state = STATE_IDLE; - } - break; - - default: - case STATE_IDLE: - decode_new_cmd(s, (uint8_t)tx); - break; - } - - return r; -} - -static int m25p80_init(SSISlave *ss) -{ - DriveInfo *dinfo; - Flash *s = M25P80(ss); - M25P80Class *mc = M25P80_GET_CLASS(s); - - s->pi = mc->pi; - - s->size = s->pi->sector_size * s->pi->n_sectors; - s->dirty_page = -1; - - /* FIXME use a qdev drive property instead of drive_get_next() */ - dinfo = drive_get_next(IF_MTD); - - if (dinfo) { - DB_PRINT_L(0, "Binding to IF_MTD drive\n"); - s->blk = blk_by_legacy_dinfo(dinfo); - blk_attach_dev_nofail(s->blk, s); - - s->storage = blk_blockalign(s->blk, s->size); - - /* FIXME: Move to late init */ - if (blk_read(s->blk, 0, s->storage, - DIV_ROUND_UP(s->size, BDRV_SECTOR_SIZE))) { - fprintf(stderr, "Failed to initialize SPI flash!\n"); - return 1; - } - } else { - DB_PRINT_L(0, "No BDRV - binding to RAM\n"); - s->storage = blk_blockalign(NULL, s->size); - memset(s->storage, 0xFF, s->size); - } - - 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 = 2, - .minimum_version_id = 1, - .pre_save = m25p80_pre_save, - .fields = (VMStateField[]) { - VMSTATE_UINT8(state, Flash), - VMSTATE_UINT8_ARRAY(data, Flash, 16), - VMSTATE_UINT32(len, Flash), - VMSTATE_UINT32(pos, Flash), - VMSTATE_UINT8(needed_bytes, Flash), - 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() - } -}; - -static void m25p80_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - M25P80Class *mc = M25P80_CLASS(klass); - - k->init = m25p80_init; - k->transfer = m25p80_transfer8; - 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; -} - -static const TypeInfo m25p80_info = { - .name = TYPE_M25P80, - .parent = TYPE_SSI_SLAVE, - .instance_size = sizeof(Flash), - .class_size = sizeof(M25P80Class), - .abstract = true, -}; - -static void m25p80_register_types(void) -{ - int i; - - type_register_static(&m25p80_info); - for (i = 0; i < ARRAY_SIZE(known_devices); ++i) { - TypeInfo ti = { - .name = known_devices[i].part_name, - .parent = TYPE_M25P80, - .class_init = m25p80_class_init, - .class_data = (void *)&known_devices[i], - }; - type_register(&ti); - } -} - -type_init(m25p80_register_types) diff --git a/qemu/hw/block/nand.c b/qemu/hw/block/nand.c deleted file mode 100644 index 29c659681..000000000 --- a/qemu/hw/block/nand.c +++ /dev/null @@ -1,801 +0,0 @@ -/* - * Flash NAND memory emulation. Based on "16M x 8 Bit NAND Flash - * Memory" datasheet for the KM29U128AT / K9F2808U0A chips from - * Samsung Electronic. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * Support for additional features based on "MT29F2G16ABCWP 2Gx16" - * datasheet from Micron Technology and "NAND02G-B2C" datasheet - * from ST Microelectronics. - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#ifndef NAND_IO - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/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 -# define NAND_CMD_READ1 0x01 -# define NAND_CMD_READ2 0x50 -# define NAND_CMD_LPREAD2 0x30 -# define NAND_CMD_NOSERIALREAD2 0x35 -# define NAND_CMD_RANDOMREAD1 0x05 -# define NAND_CMD_RANDOMREAD2 0xe0 -# define NAND_CMD_READID 0x90 -# define NAND_CMD_RESET 0xff -# define NAND_CMD_PAGEPROGRAM1 0x80 -# define NAND_CMD_PAGEPROGRAM2 0x10 -# define NAND_CMD_CACHEPROGRAM2 0x15 -# define NAND_CMD_BLOCKERASE1 0x60 -# define NAND_CMD_BLOCKERASE2 0xd0 -# define NAND_CMD_READSTATUS 0x70 -# define NAND_CMD_COPYBACKPRG1 0x85 - -# define NAND_IOSTATUS_ERROR (1 << 0) -# define NAND_IOSTATUS_PLANE0 (1 << 1) -# define NAND_IOSTATUS_PLANE1 (1 << 2) -# define NAND_IOSTATUS_PLANE2 (1 << 3) -# define NAND_IOSTATUS_PLANE3 (1 << 4) -# define NAND_IOSTATUS_READY (1 << 6) -# define NAND_IOSTATUS_UNPROTCT (1 << 7) - -# define MAX_PAGE 0x800 -# define MAX_OOB 0x40 - -typedef struct NANDFlashState NANDFlashState; -struct NANDFlashState { - DeviceState parent_obj; - - uint8_t manf_id, chip_id; - uint8_t buswidth; /* in BYTES */ - int size, pages; - int page_shift, oob_shift, erase_shift, addr_shift; - uint8_t *storage; - BlockBackend *blk; - int mem_oob; - - uint8_t cle, ale, ce, wp, gnd; - - uint8_t io[MAX_PAGE + MAX_OOB + 0x400]; - uint8_t *ioaddr; - int iolen; - - uint32_t cmd; - uint64_t addr; - int addrlen; - int status; - int offset; - - void (*blk_write)(NANDFlashState *s); - void (*blk_erase)(NANDFlashState *s); - void (*blk_load)(NANDFlashState *s, uint64_t addr, int offset); - - uint32_t ioaddr_vmstate; -}; - -#define TYPE_NAND "nand" - -#define NAND(obj) \ - OBJECT_CHECK(NANDFlashState, (obj), TYPE_NAND) - -static void mem_and(uint8_t *dest, const uint8_t *src, size_t n) -{ - /* Like memcpy() but we logical-AND the data into the destination */ - int i; - for (i = 0; i < n; i++) { - dest[i] &= src[i]; - } -} - -# define NAND_NO_AUTOINCR 0x00000001 -# define NAND_BUSWIDTH_16 0x00000002 -# define NAND_NO_PADDING 0x00000004 -# define NAND_CACHEPRG 0x00000008 -# define NAND_COPYBACK 0x00000010 -# define NAND_IS_AND 0x00000020 -# define NAND_4PAGE_ARRAY 0x00000040 -# define NAND_NO_READRDY 0x00000100 -# define NAND_SAMSUNG_LP (NAND_NO_PADDING | NAND_COPYBACK) - -# define NAND_IO - -# define PAGE(addr) ((addr) >> ADDR_SHIFT) -# define PAGE_START(page) (PAGE(page) * (PAGE_SIZE + OOB_SIZE)) -# define PAGE_MASK ((1 << ADDR_SHIFT) - 1) -# define OOB_SHIFT (PAGE_SHIFT - 5) -# define OOB_SIZE (1 << OOB_SHIFT) -# define SECTOR(addr) ((addr) >> (9 + ADDR_SHIFT - PAGE_SHIFT)) -# define SECTOR_OFFSET(addr) ((addr) & ((511 >> PAGE_SHIFT) << 8)) - -# define PAGE_SIZE 256 -# define PAGE_SHIFT 8 -# define PAGE_SECTORS 1 -# define ADDR_SHIFT 8 -# include "nand.c" -# define PAGE_SIZE 512 -# define PAGE_SHIFT 9 -# define PAGE_SECTORS 1 -# define ADDR_SHIFT 8 -# include "nand.c" -# define PAGE_SIZE 2048 -# define PAGE_SHIFT 11 -# define PAGE_SECTORS 4 -# define ADDR_SHIFT 16 -# include "nand.c" - -/* Information based on Linux drivers/mtd/nand/nand_ids.c */ -static const struct { - int size; - int width; - int page_shift; - int erase_shift; - uint32_t options; -} nand_flash_ids[0x100] = { - [0 ... 0xff] = { 0 }, - - [0x6e] = { 1, 8, 8, 4, 0 }, - [0x64] = { 2, 8, 8, 4, 0 }, - [0x6b] = { 4, 8, 9, 4, 0 }, - [0xe8] = { 1, 8, 8, 4, 0 }, - [0xec] = { 1, 8, 8, 4, 0 }, - [0xea] = { 2, 8, 8, 4, 0 }, - [0xd5] = { 4, 8, 9, 4, 0 }, - [0xe3] = { 4, 8, 9, 4, 0 }, - [0xe5] = { 4, 8, 9, 4, 0 }, - [0xd6] = { 8, 8, 9, 4, 0 }, - - [0x39] = { 8, 8, 9, 4, 0 }, - [0xe6] = { 8, 8, 9, 4, 0 }, - [0x49] = { 8, 16, 9, 4, NAND_BUSWIDTH_16 }, - [0x59] = { 8, 16, 9, 4, NAND_BUSWIDTH_16 }, - - [0x33] = { 16, 8, 9, 5, 0 }, - [0x73] = { 16, 8, 9, 5, 0 }, - [0x43] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x53] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, - - [0x35] = { 32, 8, 9, 5, 0 }, - [0x75] = { 32, 8, 9, 5, 0 }, - [0x45] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x55] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, - - [0x36] = { 64, 8, 9, 5, 0 }, - [0x76] = { 64, 8, 9, 5, 0 }, - [0x46] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x56] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, - - [0x78] = { 128, 8, 9, 5, 0 }, - [0x39] = { 128, 8, 9, 5, 0 }, - [0x79] = { 128, 8, 9, 5, 0 }, - [0x72] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x49] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x74] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x59] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, - - [0x71] = { 256, 8, 9, 5, 0 }, - - /* - * These are the new chips with large page size. The pagesize and the - * erasesize is determined from the extended id bytes - */ -# define LP_OPTIONS (NAND_SAMSUNG_LP | NAND_NO_READRDY | NAND_NO_AUTOINCR) -# define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) - - /* 512 Megabit */ - [0xa2] = { 64, 8, 0, 0, LP_OPTIONS }, - [0xf2] = { 64, 8, 0, 0, LP_OPTIONS }, - [0xb2] = { 64, 16, 0, 0, LP_OPTIONS16 }, - [0xc2] = { 64, 16, 0, 0, LP_OPTIONS16 }, - - /* 1 Gigabit */ - [0xa1] = { 128, 8, 0, 0, LP_OPTIONS }, - [0xf1] = { 128, 8, 0, 0, LP_OPTIONS }, - [0xb1] = { 128, 16, 0, 0, LP_OPTIONS16 }, - [0xc1] = { 128, 16, 0, 0, LP_OPTIONS16 }, - - /* 2 Gigabit */ - [0xaa] = { 256, 8, 0, 0, LP_OPTIONS }, - [0xda] = { 256, 8, 0, 0, LP_OPTIONS }, - [0xba] = { 256, 16, 0, 0, LP_OPTIONS16 }, - [0xca] = { 256, 16, 0, 0, LP_OPTIONS16 }, - - /* 4 Gigabit */ - [0xac] = { 512, 8, 0, 0, LP_OPTIONS }, - [0xdc] = { 512, 8, 0, 0, LP_OPTIONS }, - [0xbc] = { 512, 16, 0, 0, LP_OPTIONS16 }, - [0xcc] = { 512, 16, 0, 0, LP_OPTIONS16 }, - - /* 8 Gigabit */ - [0xa3] = { 1024, 8, 0, 0, LP_OPTIONS }, - [0xd3] = { 1024, 8, 0, 0, LP_OPTIONS }, - [0xb3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, - [0xc3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, - - /* 16 Gigabit */ - [0xa5] = { 2048, 8, 0, 0, LP_OPTIONS }, - [0xd5] = { 2048, 8, 0, 0, LP_OPTIONS }, - [0xb5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, - [0xc5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, -}; - -static void nand_reset(DeviceState *dev) -{ - NANDFlashState *s = NAND(dev); - s->cmd = NAND_CMD_READ0; - s->addr = 0; - s->addrlen = 0; - s->iolen = 0; - s->offset = 0; - s->status &= NAND_IOSTATUS_UNPROTCT; - s->status |= NAND_IOSTATUS_READY; -} - -static inline void nand_pushio_byte(NANDFlashState *s, uint8_t value) -{ - s->ioaddr[s->iolen++] = value; - for (value = s->buswidth; --value;) { - s->ioaddr[s->iolen++] = 0; - } -} - -static void nand_command(NANDFlashState *s) -{ - unsigned int offset; - switch (s->cmd) { - case NAND_CMD_READ0: - s->iolen = 0; - break; - - case NAND_CMD_READID: - s->ioaddr = s->io; - s->iolen = 0; - nand_pushio_byte(s, s->manf_id); - nand_pushio_byte(s, s->chip_id); - nand_pushio_byte(s, 'Q'); /* Don't-care byte (often 0xa5) */ - if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { - /* Page Size, Block Size, Spare Size; bit 6 indicates - * 8 vs 16 bit width NAND. - */ - nand_pushio_byte(s, (s->buswidth == 2) ? 0x55 : 0x15); - } else { - nand_pushio_byte(s, 0xc0); /* Multi-plane */ - } - break; - - case NAND_CMD_RANDOMREAD2: - case NAND_CMD_NOSERIALREAD2: - if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)) - break; - offset = s->addr & ((1 << s->addr_shift) - 1); - s->blk_load(s, s->addr, offset); - if (s->gnd) - s->iolen = (1 << s->page_shift) - offset; - else - s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset; - break; - - case NAND_CMD_RESET: - nand_reset(DEVICE(s)); - break; - - case NAND_CMD_PAGEPROGRAM1: - s->ioaddr = s->io; - s->iolen = 0; - break; - - case NAND_CMD_PAGEPROGRAM2: - if (s->wp) { - s->blk_write(s); - } - break; - - case NAND_CMD_BLOCKERASE1: - break; - - case NAND_CMD_BLOCKERASE2: - s->addr &= (1ull << s->addrlen * 8) - 1; - s->addr <<= nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP ? - 16 : 8; - - if (s->wp) { - s->blk_erase(s); - } - break; - - case NAND_CMD_READSTATUS: - s->ioaddr = s->io; - s->iolen = 0; - nand_pushio_byte(s, s->status); - break; - - default: - printf("%s: Unknown NAND command 0x%02x\n", __FUNCTION__, s->cmd); - } -} - -static void nand_pre_save(void *opaque) -{ - NANDFlashState *s = NAND(opaque); - - s->ioaddr_vmstate = s->ioaddr - s->io; -} - -static int nand_post_load(void *opaque, int version_id) -{ - NANDFlashState *s = NAND(opaque); - - if (s->ioaddr_vmstate > sizeof(s->io)) { - return -EINVAL; - } - s->ioaddr = s->io + s->ioaddr_vmstate; - - return 0; -} - -static const VMStateDescription vmstate_nand = { - .name = "nand", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = nand_pre_save, - .post_load = nand_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(cle, NANDFlashState), - VMSTATE_UINT8(ale, NANDFlashState), - VMSTATE_UINT8(ce, NANDFlashState), - VMSTATE_UINT8(wp, NANDFlashState), - VMSTATE_UINT8(gnd, NANDFlashState), - VMSTATE_BUFFER(io, NANDFlashState), - VMSTATE_UINT32(ioaddr_vmstate, NANDFlashState), - VMSTATE_INT32(iolen, NANDFlashState), - VMSTATE_UINT32(cmd, NANDFlashState), - VMSTATE_UINT64(addr, NANDFlashState), - VMSTATE_INT32(addrlen, NANDFlashState), - VMSTATE_INT32(status, NANDFlashState), - VMSTATE_INT32(offset, NANDFlashState), - /* XXX: do we want to save s->storage too? */ - VMSTATE_END_OF_LIST() - } -}; - -static void nand_realize(DeviceState *dev, Error **errp) -{ - int pagesize; - NANDFlashState *s = NAND(dev); - - s->buswidth = nand_flash_ids[s->chip_id].width >> 3; - s->size = nand_flash_ids[s->chip_id].size << 20; - if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { - s->page_shift = 11; - s->erase_shift = 6; - } else { - s->page_shift = nand_flash_ids[s->chip_id].page_shift; - s->erase_shift = nand_flash_ids[s->chip_id].erase_shift; - } - - switch (1 << s->page_shift) { - case 256: - nand_init_256(s); - break; - case 512: - nand_init_512(s); - break; - case 2048: - nand_init_2048(s); - break; - default: - error_setg(errp, "Unsupported NAND block size %#x", - 1 << s->page_shift); - return; - } - - pagesize = 1 << s->oob_shift; - s->mem_oob = 1; - if (s->blk) { - if (blk_is_read_only(s->blk)) { - error_setg(errp, "Can't use a read-only drive"); - return; - } - if (blk_getlength(s->blk) >= - (s->pages << s->page_shift) + (s->pages << s->oob_shift)) { - pagesize = 0; - s->mem_oob = 0; - } - } else { - pagesize += 1 << s->page_shift; - } - if (pagesize) { - s->storage = (uint8_t *) memset(g_malloc(s->pages * pagesize), - 0xff, s->pages * pagesize); - } - /* Give s->ioaddr a sane value in case we save state before it is used. */ - s->ioaddr = s->io; -} - -static Property nand_properties[] = { - DEFINE_PROP_UINT8("manufacturer_id", NANDFlashState, manf_id, 0), - DEFINE_PROP_UINT8("chip_id", NANDFlashState, chip_id, 0), - DEFINE_PROP_DRIVE("drive", NANDFlashState, blk), - DEFINE_PROP_END_OF_LIST(), -}; - -static void nand_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = nand_realize; - dc->reset = nand_reset; - dc->vmsd = &vmstate_nand; - dc->props = nand_properties; -} - -static const TypeInfo nand_info = { - .name = TYPE_NAND, - .parent = TYPE_DEVICE, - .instance_size = sizeof(NANDFlashState), - .class_init = nand_class_init, -}; - -static void nand_register_types(void) -{ - type_register_static(&nand_info); -} - -/* - * Chip inputs are CLE, ALE, CE, WP, GND and eight I/O pins. Chip - * outputs are R/B and eight I/O pins. - * - * CE, WP and R/B are active low. - */ -void nand_setpins(DeviceState *dev, uint8_t cle, uint8_t ale, - uint8_t ce, uint8_t wp, uint8_t gnd) -{ - NANDFlashState *s = NAND(dev); - - s->cle = cle; - s->ale = ale; - s->ce = ce; - s->wp = wp; - s->gnd = gnd; - if (wp) { - s->status |= NAND_IOSTATUS_UNPROTCT; - } else { - s->status &= ~NAND_IOSTATUS_UNPROTCT; - } -} - -void nand_getpins(DeviceState *dev, int *rb) -{ - *rb = 1; -} - -void nand_setio(DeviceState *dev, uint32_t value) -{ - int i; - NANDFlashState *s = NAND(dev); - - if (!s->ce && s->cle) { - if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { - if (s->cmd == NAND_CMD_READ0 && value == NAND_CMD_LPREAD2) - return; - if (value == NAND_CMD_RANDOMREAD1) { - s->addr &= ~((1 << s->addr_shift) - 1); - s->addrlen = 0; - return; - } - } - if (value == NAND_CMD_READ0) { - s->offset = 0; - } else if (value == NAND_CMD_READ1) { - s->offset = 0x100; - value = NAND_CMD_READ0; - } else if (value == NAND_CMD_READ2) { - s->offset = 1 << s->page_shift; - value = NAND_CMD_READ0; - } - - s->cmd = value; - - if (s->cmd == NAND_CMD_READSTATUS || - s->cmd == NAND_CMD_PAGEPROGRAM2 || - s->cmd == NAND_CMD_BLOCKERASE1 || - s->cmd == NAND_CMD_BLOCKERASE2 || - s->cmd == NAND_CMD_NOSERIALREAD2 || - s->cmd == NAND_CMD_RANDOMREAD2 || - s->cmd == NAND_CMD_RESET) { - nand_command(s); - } - - if (s->cmd != NAND_CMD_RANDOMREAD2) { - s->addrlen = 0; - } - } - - if (s->ale) { - unsigned int shift = s->addrlen * 8; - uint64_t mask = ~(0xffull << shift); - uint64_t v = (uint64_t)value << shift; - - s->addr = (s->addr & mask) | v; - s->addrlen ++; - - switch (s->addrlen) { - case 1: - if (s->cmd == NAND_CMD_READID) { - nand_command(s); - } - break; - case 2: /* fix cache address as a byte address */ - s->addr <<= (s->buswidth - 1); - break; - case 3: - if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && - (s->cmd == NAND_CMD_READ0 || - s->cmd == NAND_CMD_PAGEPROGRAM1)) { - nand_command(s); - } - break; - case 4: - if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && - nand_flash_ids[s->chip_id].size < 256 && /* 1Gb or less */ - (s->cmd == NAND_CMD_READ0 || - s->cmd == NAND_CMD_PAGEPROGRAM1)) { - nand_command(s); - } - break; - case 5: - if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && - nand_flash_ids[s->chip_id].size >= 256 && /* 2Gb or more */ - (s->cmd == NAND_CMD_READ0 || - s->cmd == NAND_CMD_PAGEPROGRAM1)) { - nand_command(s); - } - break; - default: - break; - } - } - - if (!s->cle && !s->ale && s->cmd == NAND_CMD_PAGEPROGRAM1) { - if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift)) { - for (i = s->buswidth; i--; value >>= 8) { - s->io[s->iolen ++] = (uint8_t) (value & 0xff); - } - } - } else if (!s->cle && !s->ale && s->cmd == NAND_CMD_COPYBACKPRG1) { - if ((s->addr & ((1 << s->addr_shift) - 1)) < - (1 << s->page_shift) + (1 << s->oob_shift)) { - for (i = s->buswidth; i--; s->addr++, value >>= 8) { - s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] = - (uint8_t) (value & 0xff); - } - } - } -} - -uint32_t nand_getio(DeviceState *dev) -{ - int offset; - uint32_t x = 0; - NANDFlashState *s = NAND(dev); - - /* Allow sequential reading */ - if (!s->iolen && s->cmd == NAND_CMD_READ0) { - offset = (int) (s->addr & ((1 << s->addr_shift) - 1)) + s->offset; - s->offset = 0; - - s->blk_load(s, s->addr, offset); - if (s->gnd) - s->iolen = (1 << s->page_shift) - offset; - else - s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset; - } - - if (s->ce || s->iolen <= 0) { - return 0; - } - - for (offset = s->buswidth; offset--;) { - x |= s->ioaddr[offset] << (offset << 3); - } - /* after receiving READ STATUS command all subsequent reads will - * return the status register value until another command is issued - */ - if (s->cmd != NAND_CMD_READSTATUS) { - s->addr += s->buswidth; - s->ioaddr += s->buswidth; - s->iolen -= s->buswidth; - } - return x; -} - -uint32_t nand_getbuswidth(DeviceState *dev) -{ - NANDFlashState *s = (NANDFlashState *) dev; - return s->buswidth << 3; -} - -DeviceState *nand_init(BlockBackend *blk, int manf_id, int chip_id) -{ - DeviceState *dev; - - if (nand_flash_ids[chip_id].size == 0) { - hw_error("%s: Unsupported NAND chip ID.\n", __FUNCTION__); - } - dev = DEVICE(object_new(TYPE_NAND)); - qdev_prop_set_uint8(dev, "manufacturer_id", manf_id); - qdev_prop_set_uint8(dev, "chip_id", chip_id); - if (blk) { - qdev_prop_set_drive(dev, "drive", blk, &error_fatal); - } - - qdev_init_nofail(dev); - return dev; -} - -type_init(nand_register_types) - -#else - -/* Program a single page */ -static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s) -{ - uint64_t off, page, sector, soff; - uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200]; - if (PAGE(s->addr) >= s->pages) - return; - - if (!s->blk) { - mem_and(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) + - s->offset, s->io, s->iolen); - } else if (s->mem_oob) { - sector = SECTOR(s->addr); - off = (s->addr & PAGE_MASK) + s->offset; - soff = SECTOR_OFFSET(s->addr); - if (blk_read(s->blk, sector, iobuf, PAGE_SECTORS) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); - return; - } - - mem_and(iobuf + (soff | off), s->io, MIN(s->iolen, PAGE_SIZE - off)); - if (off + s->iolen > PAGE_SIZE) { - page = PAGE(s->addr); - mem_and(s->storage + (page << OOB_SHIFT), s->io + PAGE_SIZE - off, - MIN(OOB_SIZE, off + s->iolen - PAGE_SIZE)); - } - - if (blk_write(s->blk, sector, iobuf, PAGE_SECTORS) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); - } - } else { - off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset; - sector = off >> 9; - soff = off & 0x1ff; - if (blk_read(s->blk, sector, iobuf, PAGE_SECTORS + 2) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); - return; - } - - mem_and(iobuf + soff, s->io, s->iolen); - - if (blk_write(s->blk, sector, iobuf, PAGE_SECTORS + 2) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); - } - } - s->offset = 0; -} - -/* Erase a single block */ -static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s) -{ - uint64_t i, page, addr; - uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, }; - addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1); - - if (PAGE(addr) >= s->pages) { - return; - } - - if (!s->blk) { - memset(s->storage + PAGE_START(addr), - 0xff, (PAGE_SIZE + OOB_SIZE) << s->erase_shift); - } else if (s->mem_oob) { - memset(s->storage + (PAGE(addr) << OOB_SHIFT), - 0xff, OOB_SIZE << s->erase_shift); - i = SECTOR(addr); - page = SECTOR(addr + (1 << (ADDR_SHIFT + s->erase_shift))); - for (; i < page; i ++) - if (blk_write(s->blk, i, iobuf, 1) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, i); - } - } else { - addr = PAGE_START(addr); - page = addr >> 9; - if (blk_read(s->blk, page, iobuf, 1) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", __func__, page); - } - memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1); - if (blk_write(s->blk, page, iobuf, 1) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, page); - } - - memset(iobuf, 0xff, 0x200); - i = (addr & ~0x1ff) + 0x200; - for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200; - i < addr; i += 0x200) { - if (blk_write(s->blk, i >> 9, iobuf, 1) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", - __func__, i >> 9); - } - } - - page = i >> 9; - if (blk_read(s->blk, page, iobuf, 1) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", __func__, page); - } - memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1); - if (blk_write(s->blk, page, iobuf, 1) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, page); - } - } -} - -static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s, - uint64_t addr, int offset) -{ - if (PAGE(addr) >= s->pages) { - return; - } - - if (s->blk) { - if (s->mem_oob) { - if (blk_read(s->blk, SECTOR(addr), s->io, PAGE_SECTORS) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", - __func__, SECTOR(addr)); - } - memcpy(s->io + SECTOR_OFFSET(s->addr) + PAGE_SIZE, - s->storage + (PAGE(s->addr) << OOB_SHIFT), - OOB_SIZE); - s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset; - } else { - if (blk_read(s->blk, PAGE_START(addr) >> 9, - s->io, (PAGE_SECTORS + 2)) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", - __func__, PAGE_START(addr) >> 9); - } - s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset; - } - } else { - memcpy(s->io, s->storage + PAGE_START(s->addr) + - offset, PAGE_SIZE + OOB_SIZE - offset); - s->ioaddr = s->io; - } -} - -static void glue(nand_init_, PAGE_SIZE)(NANDFlashState *s) -{ - s->oob_shift = PAGE_SHIFT - 5; - s->pages = s->size >> PAGE_SHIFT; - s->addr_shift = ADDR_SHIFT; - - s->blk_erase = glue(nand_blk_erase_, PAGE_SIZE); - s->blk_write = glue(nand_blk_write_, PAGE_SIZE); - s->blk_load = glue(nand_blk_load_, PAGE_SIZE); -} - -# undef PAGE_SIZE -# undef PAGE_SHIFT -# undef PAGE_SECTORS -# undef ADDR_SHIFT -#endif /* NAND_IO */ diff --git a/qemu/hw/block/nvme.c b/qemu/hw/block/nvme.c deleted file mode 100644 index 173988ee8..000000000 --- a/qemu/hw/block/nvme.c +++ /dev/null @@ -1,942 +0,0 @@ -/* - * QEMU NVM Express Controller - * - * Copyright (c) 2012, Intel Corporation - * - * Written by Keith Busch - * - * This code is licensed under the GNU GPL v2 or later. - */ - -/** - * Reference Specs: http://www.nvmexpress.org, 1.1, 1.0e - * - * http://www.nvmexpress.org/resources/ - */ - -/** - * Usage: add options: - * -drive file=,if=none,id= - * -device nvme,drive=,serial=,id= - */ - -#include "qemu/osdep.h" -#include -#include -#include -#include -#include "sysemu/sysemu.h" -#include "qapi/error.h" -#include "qapi/visitor.h" -#include "sysemu/block-backend.h" - -#include "nvme.h" - -static void nvme_process_sq(void *opaque); - -static int nvme_check_sqid(NvmeCtrl *n, uint16_t sqid) -{ - return sqid < n->num_queues && n->sq[sqid] != NULL ? 0 : -1; -} - -static int nvme_check_cqid(NvmeCtrl *n, uint16_t cqid) -{ - return cqid < n->num_queues && n->cq[cqid] != NULL ? 0 : -1; -} - -static void nvme_inc_cq_tail(NvmeCQueue *cq) -{ - cq->tail++; - if (cq->tail >= cq->size) { - cq->tail = 0; - cq->phase = !cq->phase; - } -} - -static void nvme_inc_sq_head(NvmeSQueue *sq) -{ - sq->head = (sq->head + 1) % sq->size; -} - -static uint8_t nvme_cq_full(NvmeCQueue *cq) -{ - return (cq->tail + 1) % cq->size == cq->head; -} - -static uint8_t nvme_sq_empty(NvmeSQueue *sq) -{ - return sq->head == sq->tail; -} - -static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq) -{ - if (cq->irq_enabled) { - if (msix_enabled(&(n->parent_obj))) { - msix_notify(&(n->parent_obj), cq->vector); - } else { - pci_irq_pulse(&n->parent_obj); - } - } -} - -static uint16_t nvme_map_prp(QEMUSGList *qsg, uint64_t prp1, uint64_t prp2, - uint32_t len, NvmeCtrl *n) -{ - hwaddr trans_len = n->page_size - (prp1 % n->page_size); - trans_len = MIN(len, trans_len); - int num_prps = (len >> n->page_bits) + 1; - - if (!prp1) { - return NVME_INVALID_FIELD | NVME_DNR; - } - - pci_dma_sglist_init(qsg, &n->parent_obj, num_prps); - qemu_sglist_add(qsg, prp1, trans_len); - len -= trans_len; - if (len) { - if (!prp2) { - goto unmap; - } - if (len > n->page_size) { - uint64_t prp_list[n->max_prp_ents]; - uint32_t nents, prp_trans; - int i = 0; - - nents = (len + n->page_size - 1) >> n->page_bits; - prp_trans = MIN(n->max_prp_ents, nents) * sizeof(uint64_t); - pci_dma_read(&n->parent_obj, prp2, (void *)prp_list, prp_trans); - while (len != 0) { - uint64_t prp_ent = le64_to_cpu(prp_list[i]); - - if (i == n->max_prp_ents - 1 && len > n->page_size) { - if (!prp_ent || prp_ent & (n->page_size - 1)) { - goto unmap; - } - - i = 0; - nents = (len + n->page_size - 1) >> n->page_bits; - prp_trans = MIN(n->max_prp_ents, nents) * sizeof(uint64_t); - pci_dma_read(&n->parent_obj, prp_ent, (void *)prp_list, - prp_trans); - prp_ent = le64_to_cpu(prp_list[i]); - } - - if (!prp_ent || prp_ent & (n->page_size - 1)) { - goto unmap; - } - - trans_len = MIN(len, n->page_size); - qemu_sglist_add(qsg, prp_ent, trans_len); - len -= trans_len; - i++; - } - } else { - if (prp2 & (n->page_size - 1)) { - goto unmap; - } - qemu_sglist_add(qsg, prp2, len); - } - } - return NVME_SUCCESS; - - unmap: - qemu_sglist_destroy(qsg); - return NVME_INVALID_FIELD | NVME_DNR; -} - -static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len, - uint64_t prp1, uint64_t prp2) -{ - QEMUSGList qsg; - - if (nvme_map_prp(&qsg, prp1, prp2, len, n)) { - return NVME_INVALID_FIELD | NVME_DNR; - } - if (dma_buf_read(ptr, len, &qsg)) { - qemu_sglist_destroy(&qsg); - return NVME_INVALID_FIELD | NVME_DNR; - } - qemu_sglist_destroy(&qsg); - return NVME_SUCCESS; -} - -static void nvme_post_cqes(void *opaque) -{ - NvmeCQueue *cq = opaque; - NvmeCtrl *n = cq->ctrl; - NvmeRequest *req, *next; - - QTAILQ_FOREACH_SAFE(req, &cq->req_list, entry, next) { - NvmeSQueue *sq; - hwaddr addr; - - if (nvme_cq_full(cq)) { - break; - } - - QTAILQ_REMOVE(&cq->req_list, req, entry); - sq = req->sq; - req->cqe.status = cpu_to_le16((req->status << 1) | cq->phase); - req->cqe.sq_id = cpu_to_le16(sq->sqid); - req->cqe.sq_head = cpu_to_le16(sq->head); - addr = cq->dma_addr + cq->tail * n->cqe_size; - nvme_inc_cq_tail(cq); - pci_dma_write(&n->parent_obj, addr, (void *)&req->cqe, - sizeof(req->cqe)); - QTAILQ_INSERT_TAIL(&sq->req_list, req, entry); - } - nvme_isr_notify(n, cq); -} - -static void nvme_enqueue_req_completion(NvmeCQueue *cq, NvmeRequest *req) -{ - assert(cq->cqid == req->sq->cqid); - QTAILQ_REMOVE(&req->sq->out_req_list, req, entry); - QTAILQ_INSERT_TAIL(&cq->req_list, req, entry); - timer_mod(cq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); -} - -static void nvme_rw_cb(void *opaque, int ret) -{ - NvmeRequest *req = opaque; - NvmeSQueue *sq = req->sq; - NvmeCtrl *n = sq->ctrl; - NvmeCQueue *cq = n->cq[sq->cqid]; - - 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) { - qemu_sglist_destroy(&req->qsg); - } - nvme_enqueue_req_completion(cq, req); -} - -static uint16_t nvme_flush(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, - NvmeRequest *req) -{ - req->has_sg = false; - block_acct_start(blk_get_stats(n->conf.blk), &req->acct, 0, - BLOCK_ACCT_FLUSH); - req->aiocb = blk_aio_flush(n->conf.blk, nvme_rw_cb, req); - - return NVME_NO_COMPLETE; -} - -static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, - NvmeRequest *req) -{ - NvmeRwCmd *rw = (NvmeRwCmd *)cmd; - uint32_t nlb = le32_to_cpu(rw->nlb) + 1; - uint64_t slba = le64_to_cpu(rw->slba); - uint64_t prp1 = le64_to_cpu(rw->prp1); - uint64_t prp2 = le64_to_cpu(rw->prp2); - - uint8_t lba_index = NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas); - uint8_t data_shift = ns->id_ns.lbaf[lba_index].ds; - 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, 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); - - return NVME_NO_COMPLETE; -} - -static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) -{ - NvmeNamespace *ns; - uint32_t nsid = le32_to_cpu(cmd->nsid); - - if (nsid == 0 || nsid > n->num_namespaces) { - return NVME_INVALID_NSID | NVME_DNR; - } - - ns = &n->namespaces[nsid - 1]; - switch (cmd->opcode) { - case NVME_CMD_FLUSH: - return nvme_flush(n, ns, cmd, req); - case NVME_CMD_WRITE: - case NVME_CMD_READ: - return nvme_rw(n, ns, cmd, req); - default: - return NVME_INVALID_OPCODE | NVME_DNR; - } -} - -static void nvme_free_sq(NvmeSQueue *sq, NvmeCtrl *n) -{ - n->sq[sq->sqid] = NULL; - timer_del(sq->timer); - timer_free(sq->timer); - g_free(sq->io_req); - if (sq->sqid) { - g_free(sq); - } -} - -static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeCmd *cmd) -{ - NvmeDeleteQ *c = (NvmeDeleteQ *)cmd; - NvmeRequest *req, *next; - NvmeSQueue *sq; - NvmeCQueue *cq; - uint16_t qid = le16_to_cpu(c->qid); - - if (!qid || nvme_check_sqid(n, qid)) { - return NVME_INVALID_QID | NVME_DNR; - } - - sq = n->sq[qid]; - while (!QTAILQ_EMPTY(&sq->out_req_list)) { - req = QTAILQ_FIRST(&sq->out_req_list); - assert(req->aiocb); - blk_aio_cancel(req->aiocb); - } - if (!nvme_check_cqid(n, sq->cqid)) { - cq = n->cq[sq->cqid]; - QTAILQ_REMOVE(&cq->sq_list, sq, entry); - - nvme_post_cqes(cq); - QTAILQ_FOREACH_SAFE(req, &cq->req_list, entry, next) { - if (req->sq == sq) { - QTAILQ_REMOVE(&cq->req_list, req, entry); - QTAILQ_INSERT_TAIL(&sq->req_list, req, entry); - } - } - } - - nvme_free_sq(sq, n); - return NVME_SUCCESS; -} - -static void nvme_init_sq(NvmeSQueue *sq, NvmeCtrl *n, uint64_t dma_addr, - uint16_t sqid, uint16_t cqid, uint16_t size) -{ - int i; - NvmeCQueue *cq; - - sq->ctrl = n; - sq->dma_addr = dma_addr; - sq->sqid = sqid; - sq->size = size; - sq->cqid = cqid; - sq->head = sq->tail = 0; - sq->io_req = g_new(NvmeRequest, sq->size); - - QTAILQ_INIT(&sq->req_list); - QTAILQ_INIT(&sq->out_req_list); - for (i = 0; i < sq->size; i++) { - sq->io_req[i].sq = sq; - QTAILQ_INSERT_TAIL(&(sq->req_list), &sq->io_req[i], entry); - } - sq->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, nvme_process_sq, sq); - - assert(n->cq[cqid]); - cq = n->cq[cqid]; - QTAILQ_INSERT_TAIL(&(cq->sq_list), sq, entry); - n->sq[sqid] = sq; -} - -static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd *cmd) -{ - NvmeSQueue *sq; - NvmeCreateSq *c = (NvmeCreateSq *)cmd; - - uint16_t cqid = le16_to_cpu(c->cqid); - uint16_t sqid = le16_to_cpu(c->sqid); - uint16_t qsize = le16_to_cpu(c->qsize); - uint16_t qflags = le16_to_cpu(c->sq_flags); - uint64_t prp1 = le64_to_cpu(c->prp1); - - if (!cqid || nvme_check_cqid(n, cqid)) { - return NVME_INVALID_CQID | NVME_DNR; - } - if (!sqid || (sqid && !nvme_check_sqid(n, sqid))) { - return NVME_INVALID_QID | NVME_DNR; - } - if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) { - return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR; - } - if (!prp1 || prp1 & (n->page_size - 1)) { - return NVME_INVALID_FIELD | NVME_DNR; - } - if (!(NVME_SQ_FLAGS_PC(qflags))) { - return NVME_INVALID_FIELD | NVME_DNR; - } - sq = g_malloc0(sizeof(*sq)); - nvme_init_sq(sq, n, prp1, sqid, cqid, qsize + 1); - return NVME_SUCCESS; -} - -static void nvme_free_cq(NvmeCQueue *cq, NvmeCtrl *n) -{ - n->cq[cq->cqid] = NULL; - timer_del(cq->timer); - timer_free(cq->timer); - msix_vector_unuse(&n->parent_obj, cq->vector); - if (cq->cqid) { - g_free(cq); - } -} - -static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd) -{ - NvmeDeleteQ *c = (NvmeDeleteQ *)cmd; - NvmeCQueue *cq; - uint16_t qid = le16_to_cpu(c->qid); - - if (!qid || nvme_check_cqid(n, qid)) { - return NVME_INVALID_CQID | NVME_DNR; - } - - cq = n->cq[qid]; - if (!QTAILQ_EMPTY(&cq->sq_list)) { - return NVME_INVALID_QUEUE_DEL; - } - nvme_free_cq(cq, n); - return NVME_SUCCESS; -} - -static void nvme_init_cq(NvmeCQueue *cq, NvmeCtrl *n, uint64_t dma_addr, - uint16_t cqid, uint16_t vector, uint16_t size, uint16_t irq_enabled) -{ - cq->ctrl = n; - cq->cqid = cqid; - cq->size = size; - cq->dma_addr = dma_addr; - cq->phase = 1; - cq->irq_enabled = irq_enabled; - cq->vector = vector; - cq->head = cq->tail = 0; - QTAILQ_INIT(&cq->req_list); - QTAILQ_INIT(&cq->sq_list); - msix_vector_use(&n->parent_obj, cq->vector); - n->cq[cqid] = cq; - cq->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, nvme_post_cqes, cq); -} - -static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeCmd *cmd) -{ - NvmeCQueue *cq; - NvmeCreateCq *c = (NvmeCreateCq *)cmd; - uint16_t cqid = le16_to_cpu(c->cqid); - uint16_t vector = le16_to_cpu(c->irq_vector); - uint16_t qsize = le16_to_cpu(c->qsize); - uint16_t qflags = le16_to_cpu(c->cq_flags); - uint64_t prp1 = le64_to_cpu(c->prp1); - - if (!cqid || (cqid && !nvme_check_cqid(n, cqid))) { - return NVME_INVALID_CQID | NVME_DNR; - } - if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) { - return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR; - } - if (!prp1) { - return NVME_INVALID_FIELD | NVME_DNR; - } - if (vector > n->num_queues) { - return NVME_INVALID_IRQ_VECTOR | NVME_DNR; - } - if (!(NVME_CQ_FLAGS_PC(qflags))) { - return NVME_INVALID_FIELD | NVME_DNR; - } - - cq = g_malloc0(sizeof(*cq)); - nvme_init_cq(cq, n, prp1, cqid, vector, qsize + 1, - NVME_CQ_FLAGS_IEN(qflags)); - return NVME_SUCCESS; -} - -static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd) -{ - NvmeNamespace *ns; - NvmeIdentify *c = (NvmeIdentify *)cmd; - uint32_t cns = le32_to_cpu(c->cns); - uint32_t nsid = le32_to_cpu(c->nsid); - uint64_t prp1 = le64_to_cpu(c->prp1); - uint64_t prp2 = le64_to_cpu(c->prp2); - - if (cns) { - return nvme_dma_read_prp(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl), - prp1, prp2); - } - if (nsid == 0 || nsid > n->num_namespaces) { - return NVME_INVALID_NSID | NVME_DNR; - } - - ns = &n->namespaces[nsid - 1]; - return nvme_dma_read_prp(n, (uint8_t *)&ns->id_ns, sizeof(ns->id_ns), - prp1, prp2); -} - -static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) -{ - uint32_t dw10 = le32_to_cpu(cmd->cdw10); - uint32_t result; - - switch (dw10) { - case NVME_VOLATILE_WRITE_CACHE: - result = blk_enable_write_cache(n->conf.blk); - break; - case NVME_NUMBER_OF_QUEUES: - result = cpu_to_le32((n->num_queues - 1) | ((n->num_queues - 1) << 16)); - break; - default: - return NVME_INVALID_FIELD | NVME_DNR; - } - - req->cqe.result = result; - return NVME_SUCCESS; -} - -static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) -{ - uint32_t dw10 = le32_to_cpu(cmd->cdw10); - uint32_t dw11 = le32_to_cpu(cmd->cdw11); - - switch (dw10) { - case NVME_VOLATILE_WRITE_CACHE: - blk_set_enable_write_cache(n->conf.blk, dw11 & 1); - break; - case NVME_NUMBER_OF_QUEUES: - req->cqe.result = - cpu_to_le32((n->num_queues - 1) | ((n->num_queues - 1) << 16)); - break; - default: - return NVME_INVALID_FIELD | NVME_DNR; - } - return NVME_SUCCESS; -} - -static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) -{ - switch (cmd->opcode) { - case NVME_ADM_CMD_DELETE_SQ: - return nvme_del_sq(n, cmd); - case NVME_ADM_CMD_CREATE_SQ: - return nvme_create_sq(n, cmd); - case NVME_ADM_CMD_DELETE_CQ: - return nvme_del_cq(n, cmd); - case NVME_ADM_CMD_CREATE_CQ: - return nvme_create_cq(n, cmd); - case NVME_ADM_CMD_IDENTIFY: - return nvme_identify(n, cmd); - case NVME_ADM_CMD_SET_FEATURES: - return nvme_set_feature(n, cmd, req); - case NVME_ADM_CMD_GET_FEATURES: - return nvme_get_feature(n, cmd, req); - default: - return NVME_INVALID_OPCODE | NVME_DNR; - } -} - -static void nvme_process_sq(void *opaque) -{ - NvmeSQueue *sq = opaque; - NvmeCtrl *n = sq->ctrl; - NvmeCQueue *cq = n->cq[sq->cqid]; - - uint16_t status; - hwaddr addr; - NvmeCmd cmd; - NvmeRequest *req; - - while (!(nvme_sq_empty(sq) || QTAILQ_EMPTY(&sq->req_list))) { - addr = sq->dma_addr + sq->head * n->sqe_size; - pci_dma_read(&n->parent_obj, addr, (void *)&cmd, sizeof(cmd)); - nvme_inc_sq_head(sq); - - req = QTAILQ_FIRST(&sq->req_list); - QTAILQ_REMOVE(&sq->req_list, req, entry); - QTAILQ_INSERT_TAIL(&sq->out_req_list, req, entry); - memset(&req->cqe, 0, sizeof(req->cqe)); - req->cqe.cid = cmd.cid; - - status = sq->sqid ? nvme_io_cmd(n, &cmd, req) : - nvme_admin_cmd(n, &cmd, req); - if (status != NVME_NO_COMPLETE) { - req->status = status; - nvme_enqueue_req_completion(cq, req); - } - } -} - -static void nvme_clear_ctrl(NvmeCtrl *n) -{ - int i; - - for (i = 0; i < n->num_queues; i++) { - if (n->sq[i] != NULL) { - nvme_free_sq(n->sq[i], n); - } - } - for (i = 0; i < n->num_queues; i++) { - if (n->cq[i] != NULL) { - nvme_free_cq(n->cq[i], n); - } - } - - blk_flush(n->conf.blk); - n->bar.cc = 0; -} - -static int nvme_start_ctrl(NvmeCtrl *n) -{ - uint32_t page_bits = NVME_CC_MPS(n->bar.cc) + 12; - uint32_t page_size = 1 << page_bits; - - if (n->cq[0] || n->sq[0] || !n->bar.asq || !n->bar.acq || - n->bar.asq & (page_size - 1) || n->bar.acq & (page_size - 1) || - NVME_CC_MPS(n->bar.cc) < NVME_CAP_MPSMIN(n->bar.cap) || - NVME_CC_MPS(n->bar.cc) > NVME_CAP_MPSMAX(n->bar.cap) || - NVME_CC_IOCQES(n->bar.cc) < NVME_CTRL_CQES_MIN(n->id_ctrl.cqes) || - NVME_CC_IOCQES(n->bar.cc) > NVME_CTRL_CQES_MAX(n->id_ctrl.cqes) || - NVME_CC_IOSQES(n->bar.cc) < NVME_CTRL_SQES_MIN(n->id_ctrl.sqes) || - NVME_CC_IOSQES(n->bar.cc) > NVME_CTRL_SQES_MAX(n->id_ctrl.sqes) || - !NVME_AQA_ASQS(n->bar.aqa) || !NVME_AQA_ACQS(n->bar.aqa)) { - return -1; - } - - n->page_bits = page_bits; - n->page_size = page_size; - n->max_prp_ents = n->page_size / sizeof(uint64_t); - n->cqe_size = 1 << NVME_CC_IOCQES(n->bar.cc); - n->sqe_size = 1 << NVME_CC_IOSQES(n->bar.cc); - nvme_init_cq(&n->admin_cq, n, n->bar.acq, 0, 0, - NVME_AQA_ACQS(n->bar.aqa) + 1, 1); - nvme_init_sq(&n->admin_sq, n, n->bar.asq, 0, 0, - NVME_AQA_ASQS(n->bar.aqa) + 1); - - return 0; -} - -static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data, - unsigned size) -{ - switch (offset) { - case 0xc: - n->bar.intms |= data & 0xffffffff; - n->bar.intmc = n->bar.intms; - break; - case 0x10: - n->bar.intms &= ~(data & 0xffffffff); - n->bar.intmc = n->bar.intms; - break; - case 0x14: - /* Windows first sends data, then sends enable bit */ - if (!NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc) && - !NVME_CC_SHN(data) && !NVME_CC_SHN(n->bar.cc)) - { - n->bar.cc = data; - } - - if (NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc)) { - n->bar.cc = data; - if (nvme_start_ctrl(n)) { - n->bar.csts = NVME_CSTS_FAILED; - } else { - n->bar.csts = NVME_CSTS_READY; - } - } else if (!NVME_CC_EN(data) && NVME_CC_EN(n->bar.cc)) { - nvme_clear_ctrl(n); - n->bar.csts &= ~NVME_CSTS_READY; - } - if (NVME_CC_SHN(data) && !(NVME_CC_SHN(n->bar.cc))) { - nvme_clear_ctrl(n); - n->bar.cc = data; - n->bar.csts |= NVME_CSTS_SHST_COMPLETE; - } else if (!NVME_CC_SHN(data) && NVME_CC_SHN(n->bar.cc)) { - n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE; - n->bar.cc = data; - } - break; - case 0x24: - n->bar.aqa = data & 0xffffffff; - break; - case 0x28: - n->bar.asq = data; - break; - case 0x2c: - n->bar.asq |= data << 32; - break; - case 0x30: - n->bar.acq = data; - break; - case 0x34: - n->bar.acq |= data << 32; - break; - default: - break; - } -} - -static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size) -{ - NvmeCtrl *n = (NvmeCtrl *)opaque; - uint8_t *ptr = (uint8_t *)&n->bar; - uint64_t val = 0; - - if (addr < sizeof(n->bar)) { - memcpy(&val, ptr + addr, size); - } - return val; -} - -static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val) -{ - uint32_t qid; - - if (addr & ((1 << 2) - 1)) { - return; - } - - if (((addr - 0x1000) >> 2) & 1) { - uint16_t new_head = val & 0xffff; - int start_sqs; - NvmeCQueue *cq; - - qid = (addr - (0x1000 + (1 << 2))) >> 3; - if (nvme_check_cqid(n, qid)) { - return; - } - - cq = n->cq[qid]; - if (new_head >= cq->size) { - return; - } - - start_sqs = nvme_cq_full(cq) ? 1 : 0; - cq->head = new_head; - if (start_sqs) { - NvmeSQueue *sq; - QTAILQ_FOREACH(sq, &cq->sq_list, entry) { - timer_mod(sq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); - } - timer_mod(cq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); - } - - if (cq->tail != cq->head) { - nvme_isr_notify(n, cq); - } - } else { - uint16_t new_tail = val & 0xffff; - NvmeSQueue *sq; - - qid = (addr - 0x1000) >> 3; - if (nvme_check_sqid(n, qid)) { - return; - } - - sq = n->sq[qid]; - if (new_tail >= sq->size) { - return; - } - - sq->tail = new_tail; - timer_mod(sq->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 500); - } -} - -static void nvme_mmio_write(void *opaque, hwaddr addr, uint64_t data, - unsigned size) -{ - NvmeCtrl *n = (NvmeCtrl *)opaque; - if (addr < sizeof(n->bar)) { - nvme_write_bar(n, addr, data, size); - } else if (addr >= 0x1000) { - nvme_process_db(n, addr, data); - } -} - -static const MemoryRegionOps nvme_mmio_ops = { - .read = nvme_mmio_read, - .write = nvme_mmio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 2, - .max_access_size = 8, - }, -}; - -static int nvme_init(PCIDevice *pci_dev) -{ - NvmeCtrl *n = NVME(pci_dev); - NvmeIdCtrl *id = &n->id_ctrl; - - int i; - int64_t bs_size; - uint8_t *pci_conf; - - if (!n->conf.blk) { - return -1; - } - - bs_size = blk_getlength(n->conf.blk); - if (bs_size < 0) { - return -1; - } - - blkconf_serial(&n->conf, &n->serial); - if (!n->serial) { - return -1; - } - blkconf_blocksizes(&n->conf); - - pci_conf = pci_dev->config; - pci_conf[PCI_INTERRUPT_PIN] = 1; - pci_config_set_prog_interface(pci_dev->config, 0x2); - pci_config_set_class(pci_dev->config, PCI_CLASS_STORAGE_EXPRESS); - pcie_endpoint_cap_init(&n->parent_obj, 0x80); - - n->num_namespaces = 1; - n->num_queues = 64; - 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); - n->sq = g_new0(NvmeSQueue *, n->num_queues); - n->cq = g_new0(NvmeCQueue *, n->num_queues); - - memory_region_init_io(&n->iomem, OBJECT(n), &nvme_mmio_ops, n, - "nvme", n->reg_size); - pci_register_bar(&n->parent_obj, 0, - PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, - &n->iomem); - msix_init_exclusive_bar(&n->parent_obj, n->num_queues, 4); - - id->vid = cpu_to_le16(pci_get_word(pci_conf + PCI_VENDOR_ID)); - id->ssvid = cpu_to_le16(pci_get_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID)); - strpadcpy((char *)id->mn, sizeof(id->mn), "QEMU NVMe Ctrl", ' '); - strpadcpy((char *)id->fr, sizeof(id->fr), "1.0", ' '); - strpadcpy((char *)id->sn, sizeof(id->sn), n->serial, ' '); - id->rab = 6; - id->ieee[0] = 0x00; - id->ieee[1] = 0x02; - id->ieee[2] = 0xb3; - id->oacs = cpu_to_le16(0); - id->frmw = 7 << 1; - id->lpa = 1 << 0; - id->sqes = (0x6 << 4) | 0x6; - id->cqes = (0x4 << 4) | 0x4; - id->nn = cpu_to_le32(n->num_namespaces); - id->psd[0].mp = cpu_to_le16(0x9c4); - id->psd[0].enlat = cpu_to_le32(0x10); - id->psd[0].exlat = cpu_to_le32(0x4); - if (blk_enable_write_cache(n->conf.blk)) { - id->vwc = 1; - } - - n->bar.cap = 0; - NVME_CAP_SET_MQES(n->bar.cap, 0x7ff); - NVME_CAP_SET_CQR(n->bar.cap, 1); - NVME_CAP_SET_AMS(n->bar.cap, 1); - NVME_CAP_SET_TO(n->bar.cap, 0xf); - NVME_CAP_SET_CSS(n->bar.cap, 1); - NVME_CAP_SET_MPSMAX(n->bar.cap, 4); - - n->bar.vs = 0x00010100; - n->bar.intmc = n->bar.intms = 0; - - for (i = 0; i < n->num_namespaces; i++) { - NvmeNamespace *ns = &n->namespaces[i]; - NvmeIdNs *id_ns = &ns->id_ns; - id_ns->nsfeat = 0; - id_ns->nlbaf = 0; - id_ns->flbas = 0; - id_ns->mc = 0; - id_ns->dpc = 0; - id_ns->dps = 0; - id_ns->lbaf[0].ds = BDRV_SECTOR_BITS; - id_ns->ncap = id_ns->nuse = id_ns->nsze = - cpu_to_le64(n->ns_size >> - id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas)].ds); - } - return 0; -} - -static void nvme_exit(PCIDevice *pci_dev) -{ - NvmeCtrl *n = NVME(pci_dev); - - nvme_clear_ctrl(n); - g_free(n->namespaces); - g_free(n->cq); - g_free(n->sq); - msix_uninit_exclusive_bar(pci_dev); -} - -static Property nvme_props[] = { - DEFINE_BLOCK_PROPERTIES(NvmeCtrl, conf), - DEFINE_PROP_STRING("serial", NvmeCtrl, serial), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription nvme_vmstate = { - .name = "nvme", - .unmigratable = 1, -}; - -static void nvme_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); - - pc->init = nvme_init; - pc->exit = nvme_exit; - pc->class_id = PCI_CLASS_STORAGE_EXPRESS; - pc->vendor_id = PCI_VENDOR_ID_INTEL; - pc->device_id = 0x5845; - pc->revision = 1; - pc->is_express = 1; - - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->desc = "Non-Volatile Memory Express"; - dc->props = nvme_props; - dc->vmsd = &nvme_vmstate; -} - -static void nvme_instance_init(Object *obj) -{ - NvmeCtrl *s = NVME(obj); - - device_add_bootindex_property(obj, &s->conf.bootindex, - "bootindex", "/namespace@1,0", - DEVICE(obj), &error_abort); -} - -static const TypeInfo nvme_info = { - .name = "nvme", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(NvmeCtrl), - .class_init = nvme_class_init, - .instance_init = nvme_instance_init, -}; - -static void nvme_register_types(void) -{ - type_register_static(&nvme_info); -} - -type_init(nvme_register_types) diff --git a/qemu/hw/block/nvme.h b/qemu/hw/block/nvme.h deleted file mode 100644 index 8fb0c1075..000000000 --- a/qemu/hw/block/nvme.h +++ /dev/null @@ -1,713 +0,0 @@ -#ifndef HW_NVME_H -#define HW_NVME_H -#include "qemu/cutils.h" - -typedef struct NvmeBar { - uint64_t cap; - uint32_t vs; - uint32_t intms; - uint32_t intmc; - uint32_t cc; - uint32_t rsvd1; - uint32_t csts; - uint32_t nssrc; - uint32_t aqa; - uint64_t asq; - uint64_t acq; -} NvmeBar; - -enum NvmeCapShift { - CAP_MQES_SHIFT = 0, - CAP_CQR_SHIFT = 16, - CAP_AMS_SHIFT = 17, - CAP_TO_SHIFT = 24, - CAP_DSTRD_SHIFT = 32, - CAP_NSSRS_SHIFT = 33, - CAP_CSS_SHIFT = 37, - CAP_MPSMIN_SHIFT = 48, - CAP_MPSMAX_SHIFT = 52, -}; - -enum NvmeCapMask { - CAP_MQES_MASK = 0xffff, - CAP_CQR_MASK = 0x1, - CAP_AMS_MASK = 0x3, - CAP_TO_MASK = 0xff, - CAP_DSTRD_MASK = 0xf, - CAP_NSSRS_MASK = 0x1, - CAP_CSS_MASK = 0xff, - CAP_MPSMIN_MASK = 0xf, - CAP_MPSMAX_MASK = 0xf, -}; - -#define NVME_CAP_MQES(cap) (((cap) >> CAP_MQES_SHIFT) & CAP_MQES_MASK) -#define NVME_CAP_CQR(cap) (((cap) >> CAP_CQR_SHIFT) & CAP_CQR_MASK) -#define NVME_CAP_AMS(cap) (((cap) >> CAP_AMS_SHIFT) & CAP_AMS_MASK) -#define NVME_CAP_TO(cap) (((cap) >> CAP_TO_SHIFT) & CAP_TO_MASK) -#define NVME_CAP_DSTRD(cap) (((cap) >> CAP_DSTRD_SHIFT) & CAP_DSTRD_MASK) -#define NVME_CAP_NSSRS(cap) (((cap) >> CAP_NSSRS_SHIFT) & CAP_NSSRS_MASK) -#define NVME_CAP_CSS(cap) (((cap) >> CAP_CSS_SHIFT) & CAP_CSS_MASK) -#define NVME_CAP_MPSMIN(cap)(((cap) >> CAP_MPSMIN_SHIFT) & CAP_MPSMIN_MASK) -#define NVME_CAP_MPSMAX(cap)(((cap) >> CAP_MPSMAX_SHIFT) & CAP_MPSMAX_MASK) - -#define NVME_CAP_SET_MQES(cap, val) (cap |= (uint64_t)(val & CAP_MQES_MASK) \ - << CAP_MQES_SHIFT) -#define NVME_CAP_SET_CQR(cap, val) (cap |= (uint64_t)(val & CAP_CQR_MASK) \ - << CAP_CQR_SHIFT) -#define NVME_CAP_SET_AMS(cap, val) (cap |= (uint64_t)(val & CAP_AMS_MASK) \ - << CAP_AMS_SHIFT) -#define NVME_CAP_SET_TO(cap, val) (cap |= (uint64_t)(val & CAP_TO_MASK) \ - << CAP_TO_SHIFT) -#define NVME_CAP_SET_DSTRD(cap, val) (cap |= (uint64_t)(val & CAP_DSTRD_MASK) \ - << CAP_DSTRD_SHIFT) -#define NVME_CAP_SET_NSSRS(cap, val) (cap |= (uint64_t)(val & CAP_NSSRS_MASK) \ - << CAP_NSSRS_SHIFT) -#define NVME_CAP_SET_CSS(cap, val) (cap |= (uint64_t)(val & CAP_CSS_MASK) \ - << CAP_CSS_SHIFT) -#define NVME_CAP_SET_MPSMIN(cap, val) (cap |= (uint64_t)(val & CAP_MPSMIN_MASK)\ - << CAP_MPSMIN_SHIFT) -#define NVME_CAP_SET_MPSMAX(cap, val) (cap |= (uint64_t)(val & CAP_MPSMAX_MASK)\ - << CAP_MPSMAX_SHIFT) - -enum NvmeCcShift { - CC_EN_SHIFT = 0, - CC_CSS_SHIFT = 4, - CC_MPS_SHIFT = 7, - CC_AMS_SHIFT = 11, - CC_SHN_SHIFT = 14, - CC_IOSQES_SHIFT = 16, - CC_IOCQES_SHIFT = 20, -}; - -enum NvmeCcMask { - CC_EN_MASK = 0x1, - CC_CSS_MASK = 0x7, - CC_MPS_MASK = 0xf, - CC_AMS_MASK = 0x7, - CC_SHN_MASK = 0x3, - CC_IOSQES_MASK = 0xf, - CC_IOCQES_MASK = 0xf, -}; - -#define NVME_CC_EN(cc) ((cc >> CC_EN_SHIFT) & CC_EN_MASK) -#define NVME_CC_CSS(cc) ((cc >> CC_CSS_SHIFT) & CC_CSS_MASK) -#define NVME_CC_MPS(cc) ((cc >> CC_MPS_SHIFT) & CC_MPS_MASK) -#define NVME_CC_AMS(cc) ((cc >> CC_AMS_SHIFT) & CC_AMS_MASK) -#define NVME_CC_SHN(cc) ((cc >> CC_SHN_SHIFT) & CC_SHN_MASK) -#define NVME_CC_IOSQES(cc) ((cc >> CC_IOSQES_SHIFT) & CC_IOSQES_MASK) -#define NVME_CC_IOCQES(cc) ((cc >> CC_IOCQES_SHIFT) & CC_IOCQES_MASK) - -enum NvmeCstsShift { - CSTS_RDY_SHIFT = 0, - CSTS_CFS_SHIFT = 1, - CSTS_SHST_SHIFT = 2, - CSTS_NSSRO_SHIFT = 4, -}; - -enum NvmeCstsMask { - CSTS_RDY_MASK = 0x1, - CSTS_CFS_MASK = 0x1, - CSTS_SHST_MASK = 0x3, - CSTS_NSSRO_MASK = 0x1, -}; - -enum NvmeCsts { - NVME_CSTS_READY = 1 << CSTS_RDY_SHIFT, - NVME_CSTS_FAILED = 1 << CSTS_CFS_SHIFT, - NVME_CSTS_SHST_NORMAL = 0 << CSTS_SHST_SHIFT, - NVME_CSTS_SHST_PROGRESS = 1 << CSTS_SHST_SHIFT, - NVME_CSTS_SHST_COMPLETE = 2 << CSTS_SHST_SHIFT, - NVME_CSTS_NSSRO = 1 << CSTS_NSSRO_SHIFT, -}; - -#define NVME_CSTS_RDY(csts) ((csts >> CSTS_RDY_SHIFT) & CSTS_RDY_MASK) -#define NVME_CSTS_CFS(csts) ((csts >> CSTS_CFS_SHIFT) & CSTS_CFS_MASK) -#define NVME_CSTS_SHST(csts) ((csts >> CSTS_SHST_SHIFT) & CSTS_SHST_MASK) -#define NVME_CSTS_NSSRO(csts) ((csts >> CSTS_NSSRO_SHIFT) & CSTS_NSSRO_MASK) - -enum NvmeAqaShift { - AQA_ASQS_SHIFT = 0, - AQA_ACQS_SHIFT = 16, -}; - -enum NvmeAqaMask { - AQA_ASQS_MASK = 0xfff, - AQA_ACQS_MASK = 0xfff, -}; - -#define NVME_AQA_ASQS(aqa) ((aqa >> AQA_ASQS_SHIFT) & AQA_ASQS_MASK) -#define NVME_AQA_ACQS(aqa) ((aqa >> AQA_ACQS_SHIFT) & AQA_ACQS_MASK) - -typedef struct NvmeCmd { - uint8_t opcode; - uint8_t fuse; - uint16_t cid; - uint32_t nsid; - uint64_t res1; - uint64_t mptr; - uint64_t prp1; - uint64_t prp2; - uint32_t cdw10; - uint32_t cdw11; - uint32_t cdw12; - uint32_t cdw13; - uint32_t cdw14; - uint32_t cdw15; -} NvmeCmd; - -enum NvmeAdminCommands { - NVME_ADM_CMD_DELETE_SQ = 0x00, - NVME_ADM_CMD_CREATE_SQ = 0x01, - NVME_ADM_CMD_GET_LOG_PAGE = 0x02, - NVME_ADM_CMD_DELETE_CQ = 0x04, - NVME_ADM_CMD_CREATE_CQ = 0x05, - NVME_ADM_CMD_IDENTIFY = 0x06, - NVME_ADM_CMD_ABORT = 0x08, - NVME_ADM_CMD_SET_FEATURES = 0x09, - NVME_ADM_CMD_GET_FEATURES = 0x0a, - NVME_ADM_CMD_ASYNC_EV_REQ = 0x0c, - NVME_ADM_CMD_ACTIVATE_FW = 0x10, - NVME_ADM_CMD_DOWNLOAD_FW = 0x11, - NVME_ADM_CMD_FORMAT_NVM = 0x80, - NVME_ADM_CMD_SECURITY_SEND = 0x81, - NVME_ADM_CMD_SECURITY_RECV = 0x82, -}; - -enum NvmeIoCommands { - NVME_CMD_FLUSH = 0x00, - NVME_CMD_WRITE = 0x01, - NVME_CMD_READ = 0x02, - NVME_CMD_WRITE_UNCOR = 0x04, - NVME_CMD_COMPARE = 0x05, - NVME_CMD_DSM = 0x09, -}; - -typedef struct NvmeDeleteQ { - uint8_t opcode; - uint8_t flags; - uint16_t cid; - uint32_t rsvd1[9]; - uint16_t qid; - uint16_t rsvd10; - uint32_t rsvd11[5]; -} NvmeDeleteQ; - -typedef struct NvmeCreateCq { - uint8_t opcode; - uint8_t flags; - uint16_t cid; - uint32_t rsvd1[5]; - uint64_t prp1; - uint64_t rsvd8; - uint16_t cqid; - uint16_t qsize; - uint16_t cq_flags; - uint16_t irq_vector; - uint32_t rsvd12[4]; -} NvmeCreateCq; - -#define NVME_CQ_FLAGS_PC(cq_flags) (cq_flags & 0x1) -#define NVME_CQ_FLAGS_IEN(cq_flags) ((cq_flags >> 1) & 0x1) - -typedef struct NvmeCreateSq { - uint8_t opcode; - uint8_t flags; - uint16_t cid; - uint32_t rsvd1[5]; - uint64_t prp1; - uint64_t rsvd8; - uint16_t sqid; - uint16_t qsize; - uint16_t sq_flags; - uint16_t cqid; - uint32_t rsvd12[4]; -} NvmeCreateSq; - -#define NVME_SQ_FLAGS_PC(sq_flags) (sq_flags & 0x1) -#define NVME_SQ_FLAGS_QPRIO(sq_flags) ((sq_flags >> 1) & 0x3) - -enum NvmeQueueFlags { - NVME_Q_PC = 1, - NVME_Q_PRIO_URGENT = 0, - NVME_Q_PRIO_HIGH = 1, - NVME_Q_PRIO_NORMAL = 2, - NVME_Q_PRIO_LOW = 3, -}; - -typedef struct NvmeIdentify { - uint8_t opcode; - uint8_t flags; - uint16_t cid; - uint32_t nsid; - uint64_t rsvd2[2]; - uint64_t prp1; - uint64_t prp2; - uint32_t cns; - uint32_t rsvd11[5]; -} NvmeIdentify; - -typedef struct NvmeRwCmd { - uint8_t opcode; - uint8_t flags; - uint16_t cid; - uint32_t nsid; - uint64_t rsvd2; - uint64_t mptr; - uint64_t prp1; - uint64_t prp2; - uint64_t slba; - uint16_t nlb; - uint16_t control; - uint32_t dsmgmt; - uint32_t reftag; - uint16_t apptag; - uint16_t appmask; -} NvmeRwCmd; - -enum { - NVME_RW_LR = 1 << 15, - NVME_RW_FUA = 1 << 14, - NVME_RW_DSM_FREQ_UNSPEC = 0, - NVME_RW_DSM_FREQ_TYPICAL = 1, - NVME_RW_DSM_FREQ_RARE = 2, - NVME_RW_DSM_FREQ_READS = 3, - NVME_RW_DSM_FREQ_WRITES = 4, - NVME_RW_DSM_FREQ_RW = 5, - NVME_RW_DSM_FREQ_ONCE = 6, - NVME_RW_DSM_FREQ_PREFETCH = 7, - NVME_RW_DSM_FREQ_TEMP = 8, - NVME_RW_DSM_LATENCY_NONE = 0 << 4, - NVME_RW_DSM_LATENCY_IDLE = 1 << 4, - NVME_RW_DSM_LATENCY_NORM = 2 << 4, - NVME_RW_DSM_LATENCY_LOW = 3 << 4, - NVME_RW_DSM_SEQ_REQ = 1 << 6, - NVME_RW_DSM_COMPRESSED = 1 << 7, - NVME_RW_PRINFO_PRACT = 1 << 13, - NVME_RW_PRINFO_PRCHK_GUARD = 1 << 12, - NVME_RW_PRINFO_PRCHK_APP = 1 << 11, - NVME_RW_PRINFO_PRCHK_REF = 1 << 10, -}; - -typedef struct NvmeDsmCmd { - uint8_t opcode; - uint8_t flags; - uint16_t cid; - uint32_t nsid; - uint64_t rsvd2[2]; - uint64_t prp1; - uint64_t prp2; - uint32_t nr; - uint32_t attributes; - uint32_t rsvd12[4]; -} NvmeDsmCmd; - -enum { - NVME_DSMGMT_IDR = 1 << 0, - NVME_DSMGMT_IDW = 1 << 1, - NVME_DSMGMT_AD = 1 << 2, -}; - -typedef struct NvmeDsmRange { - uint32_t cattr; - uint32_t nlb; - uint64_t slba; -} NvmeDsmRange; - -enum NvmeAsyncEventRequest { - NVME_AER_TYPE_ERROR = 0, - NVME_AER_TYPE_SMART = 1, - NVME_AER_TYPE_IO_SPECIFIC = 6, - NVME_AER_TYPE_VENDOR_SPECIFIC = 7, - NVME_AER_INFO_ERR_INVALID_SQ = 0, - NVME_AER_INFO_ERR_INVALID_DB = 1, - NVME_AER_INFO_ERR_DIAG_FAIL = 2, - NVME_AER_INFO_ERR_PERS_INTERNAL_ERR = 3, - NVME_AER_INFO_ERR_TRANS_INTERNAL_ERR = 4, - NVME_AER_INFO_ERR_FW_IMG_LOAD_ERR = 5, - NVME_AER_INFO_SMART_RELIABILITY = 0, - NVME_AER_INFO_SMART_TEMP_THRESH = 1, - NVME_AER_INFO_SMART_SPARE_THRESH = 2, -}; - -typedef struct NvmeAerResult { - uint8_t event_type; - uint8_t event_info; - uint8_t log_page; - uint8_t resv; -} NvmeAerResult; - -typedef struct NvmeCqe { - uint32_t result; - uint32_t rsvd; - uint16_t sq_head; - uint16_t sq_id; - uint16_t cid; - uint16_t status; -} NvmeCqe; - -enum NvmeStatusCodes { - NVME_SUCCESS = 0x0000, - NVME_INVALID_OPCODE = 0x0001, - NVME_INVALID_FIELD = 0x0002, - NVME_CID_CONFLICT = 0x0003, - NVME_DATA_TRAS_ERROR = 0x0004, - NVME_POWER_LOSS_ABORT = 0x0005, - NVME_INTERNAL_DEV_ERROR = 0x0006, - NVME_CMD_ABORT_REQ = 0x0007, - NVME_CMD_ABORT_SQ_DEL = 0x0008, - NVME_CMD_ABORT_FAILED_FUSE = 0x0009, - NVME_CMD_ABORT_MISSING_FUSE = 0x000a, - NVME_INVALID_NSID = 0x000b, - NVME_CMD_SEQ_ERROR = 0x000c, - NVME_LBA_RANGE = 0x0080, - NVME_CAP_EXCEEDED = 0x0081, - NVME_NS_NOT_READY = 0x0082, - NVME_NS_RESV_CONFLICT = 0x0083, - NVME_INVALID_CQID = 0x0100, - NVME_INVALID_QID = 0x0101, - NVME_MAX_QSIZE_EXCEEDED = 0x0102, - NVME_ACL_EXCEEDED = 0x0103, - NVME_RESERVED = 0x0104, - NVME_AER_LIMIT_EXCEEDED = 0x0105, - NVME_INVALID_FW_SLOT = 0x0106, - NVME_INVALID_FW_IMAGE = 0x0107, - NVME_INVALID_IRQ_VECTOR = 0x0108, - NVME_INVALID_LOG_ID = 0x0109, - NVME_INVALID_FORMAT = 0x010a, - NVME_FW_REQ_RESET = 0x010b, - NVME_INVALID_QUEUE_DEL = 0x010c, - NVME_FID_NOT_SAVEABLE = 0x010d, - NVME_FID_NOT_NSID_SPEC = 0x010f, - NVME_FW_REQ_SUSYSTEM_RESET = 0x0110, - NVME_CONFLICTING_ATTRS = 0x0180, - NVME_INVALID_PROT_INFO = 0x0181, - NVME_WRITE_TO_RO = 0x0182, - NVME_WRITE_FAULT = 0x0280, - NVME_UNRECOVERED_READ = 0x0281, - NVME_E2E_GUARD_ERROR = 0x0282, - NVME_E2E_APP_ERROR = 0x0283, - NVME_E2E_REF_ERROR = 0x0284, - NVME_CMP_FAILURE = 0x0285, - NVME_ACCESS_DENIED = 0x0286, - NVME_MORE = 0x2000, - NVME_DNR = 0x4000, - NVME_NO_COMPLETE = 0xffff, -}; - -typedef struct NvmeFwSlotInfoLog { - uint8_t afi; - uint8_t reserved1[7]; - uint8_t frs1[8]; - uint8_t frs2[8]; - uint8_t frs3[8]; - uint8_t frs4[8]; - uint8_t frs5[8]; - uint8_t frs6[8]; - uint8_t frs7[8]; - uint8_t reserved2[448]; -} NvmeFwSlotInfoLog; - -typedef struct NvmeErrorLog { - uint64_t error_count; - uint16_t sqid; - uint16_t cid; - uint16_t status_field; - uint16_t param_error_location; - uint64_t lba; - uint32_t nsid; - uint8_t vs; - uint8_t resv[35]; -} NvmeErrorLog; - -typedef struct NvmeSmartLog { - uint8_t critical_warning; - uint8_t temperature[2]; - uint8_t available_spare; - uint8_t available_spare_threshold; - uint8_t percentage_used; - uint8_t reserved1[26]; - uint64_t data_units_read[2]; - uint64_t data_units_written[2]; - uint64_t host_read_commands[2]; - uint64_t host_write_commands[2]; - uint64_t controller_busy_time[2]; - uint64_t power_cycles[2]; - uint64_t power_on_hours[2]; - uint64_t unsafe_shutdowns[2]; - uint64_t media_errors[2]; - uint64_t number_of_error_log_entries[2]; - uint8_t reserved2[320]; -} NvmeSmartLog; - -enum NvmeSmartWarn { - NVME_SMART_SPARE = 1 << 0, - NVME_SMART_TEMPERATURE = 1 << 1, - NVME_SMART_RELIABILITY = 1 << 2, - NVME_SMART_MEDIA_READ_ONLY = 1 << 3, - NVME_SMART_FAILED_VOLATILE_MEDIA = 1 << 4, -}; - -enum LogIdentifier { - NVME_LOG_ERROR_INFO = 0x01, - NVME_LOG_SMART_INFO = 0x02, - NVME_LOG_FW_SLOT_INFO = 0x03, -}; - -typedef struct NvmePSD { - uint16_t mp; - uint16_t reserved; - uint32_t enlat; - uint32_t exlat; - uint8_t rrt; - uint8_t rrl; - uint8_t rwt; - uint8_t rwl; - uint8_t resv[16]; -} NvmePSD; - -typedef struct NvmeIdCtrl { - uint16_t vid; - uint16_t ssvid; - uint8_t sn[20]; - uint8_t mn[40]; - uint8_t fr[8]; - uint8_t rab; - uint8_t ieee[3]; - uint8_t cmic; - uint8_t mdts; - uint8_t rsvd255[178]; - uint16_t oacs; - uint8_t acl; - uint8_t aerl; - uint8_t frmw; - uint8_t lpa; - uint8_t elpe; - uint8_t npss; - uint8_t rsvd511[248]; - uint8_t sqes; - uint8_t cqes; - uint16_t rsvd515; - uint32_t nn; - uint16_t oncs; - uint16_t fuses; - uint8_t fna; - uint8_t vwc; - uint16_t awun; - uint16_t awupf; - uint8_t rsvd703[174]; - uint8_t rsvd2047[1344]; - NvmePSD psd[32]; - uint8_t vs[1024]; -} NvmeIdCtrl; - -enum NvmeIdCtrlOacs { - NVME_OACS_SECURITY = 1 << 0, - NVME_OACS_FORMAT = 1 << 1, - NVME_OACS_FW = 1 << 2, -}; - -enum NvmeIdCtrlOncs { - NVME_ONCS_COMPARE = 1 << 0, - NVME_ONCS_WRITE_UNCORR = 1 << 1, - NVME_ONCS_DSM = 1 << 2, - NVME_ONCS_WRITE_ZEROS = 1 << 3, - NVME_ONCS_FEATURES = 1 << 4, - NVME_ONCS_RESRVATIONS = 1 << 5, -}; - -#define NVME_CTRL_SQES_MIN(sqes) ((sqes) & 0xf) -#define NVME_CTRL_SQES_MAX(sqes) (((sqes) >> 4) & 0xf) -#define NVME_CTRL_CQES_MIN(cqes) ((cqes) & 0xf) -#define NVME_CTRL_CQES_MAX(cqes) (((cqes) >> 4) & 0xf) - -typedef struct NvmeFeatureVal { - uint32_t arbitration; - uint32_t power_mgmt; - uint32_t temp_thresh; - uint32_t err_rec; - uint32_t volatile_wc; - uint32_t num_queues; - uint32_t int_coalescing; - uint32_t *int_vector_config; - uint32_t write_atomicity; - uint32_t async_config; - uint32_t sw_prog_marker; -} NvmeFeatureVal; - -#define NVME_ARB_AB(arb) (arb & 0x7) -#define NVME_ARB_LPW(arb) ((arb >> 8) & 0xff) -#define NVME_ARB_MPW(arb) ((arb >> 16) & 0xff) -#define NVME_ARB_HPW(arb) ((arb >> 24) & 0xff) - -#define NVME_INTC_THR(intc) (intc & 0xff) -#define NVME_INTC_TIME(intc) ((intc >> 8) & 0xff) - -enum NvmeFeatureIds { - NVME_ARBITRATION = 0x1, - NVME_POWER_MANAGEMENT = 0x2, - NVME_LBA_RANGE_TYPE = 0x3, - NVME_TEMPERATURE_THRESHOLD = 0x4, - NVME_ERROR_RECOVERY = 0x5, - NVME_VOLATILE_WRITE_CACHE = 0x6, - NVME_NUMBER_OF_QUEUES = 0x7, - NVME_INTERRUPT_COALESCING = 0x8, - NVME_INTERRUPT_VECTOR_CONF = 0x9, - NVME_WRITE_ATOMICITY = 0xa, - NVME_ASYNCHRONOUS_EVENT_CONF = 0xb, - NVME_SOFTWARE_PROGRESS_MARKER = 0x80 -}; - -typedef struct NvmeRangeType { - uint8_t type; - uint8_t attributes; - uint8_t rsvd2[14]; - uint64_t slba; - uint64_t nlb; - uint8_t guid[16]; - uint8_t rsvd48[16]; -} NvmeRangeType; - -typedef struct NvmeLBAF { - uint16_t ms; - uint8_t ds; - uint8_t rp; -} NvmeLBAF; - -typedef struct NvmeIdNs { - uint64_t nsze; - uint64_t ncap; - uint64_t nuse; - uint8_t nsfeat; - uint8_t nlbaf; - uint8_t flbas; - uint8_t mc; - uint8_t dpc; - uint8_t dps; - uint8_t res30[98]; - NvmeLBAF lbaf[16]; - uint8_t res192[192]; - uint8_t vs[3712]; -} NvmeIdNs; - -#define NVME_ID_NS_NSFEAT_THIN(nsfeat) ((nsfeat & 0x1)) -#define NVME_ID_NS_FLBAS_EXTENDED(flbas) ((flbas >> 4) & 0x1) -#define NVME_ID_NS_FLBAS_INDEX(flbas) ((flbas & 0xf)) -#define NVME_ID_NS_MC_SEPARATE(mc) ((mc >> 1) & 0x1) -#define NVME_ID_NS_MC_EXTENDED(mc) ((mc & 0x1)) -#define NVME_ID_NS_DPC_LAST_EIGHT(dpc) ((dpc >> 4) & 0x1) -#define NVME_ID_NS_DPC_FIRST_EIGHT(dpc) ((dpc >> 3) & 0x1) -#define NVME_ID_NS_DPC_TYPE_3(dpc) ((dpc >> 2) & 0x1) -#define NVME_ID_NS_DPC_TYPE_2(dpc) ((dpc >> 1) & 0x1) -#define NVME_ID_NS_DPC_TYPE_1(dpc) ((dpc & 0x1)) -#define NVME_ID_NS_DPC_TYPE_MASK 0x7 - -enum NvmeIdNsDps { - DPS_TYPE_NONE = 0, - DPS_TYPE_1 = 1, - DPS_TYPE_2 = 2, - DPS_TYPE_3 = 3, - DPS_TYPE_MASK = 0x7, - DPS_FIRST_EIGHT = 8, -}; - -static inline void _nvme_check_size(void) -{ - QEMU_BUILD_BUG_ON(sizeof(NvmeAerResult) != 4); - QEMU_BUILD_BUG_ON(sizeof(NvmeCqe) != 16); - QEMU_BUILD_BUG_ON(sizeof(NvmeDsmRange) != 16); - QEMU_BUILD_BUG_ON(sizeof(NvmeCmd) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeDeleteQ) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeCreateCq) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeCreateSq) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeIdentify) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeRwCmd) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeDsmCmd) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeRangeType) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeErrorLog) != 64); - QEMU_BUILD_BUG_ON(sizeof(NvmeFwSlotInfoLog) != 512); - QEMU_BUILD_BUG_ON(sizeof(NvmeSmartLog) != 512); - QEMU_BUILD_BUG_ON(sizeof(NvmeIdCtrl) != 4096); - QEMU_BUILD_BUG_ON(sizeof(NvmeIdNs) != 4096); -} - -typedef struct NvmeAsyncEvent { - QSIMPLEQ_ENTRY(NvmeAsyncEvent) entry; - NvmeAerResult result; -} NvmeAsyncEvent; - -typedef struct NvmeRequest { - struct NvmeSQueue *sq; - BlockAIOCB *aiocb; - uint16_t status; - bool has_sg; - NvmeCqe cqe; - BlockAcctCookie acct; - QEMUSGList qsg; - QTAILQ_ENTRY(NvmeRequest)entry; -} NvmeRequest; - -typedef struct NvmeSQueue { - struct NvmeCtrl *ctrl; - uint16_t sqid; - uint16_t cqid; - uint32_t head; - uint32_t tail; - uint32_t size; - uint64_t dma_addr; - QEMUTimer *timer; - NvmeRequest *io_req; - QTAILQ_HEAD(sq_req_list, NvmeRequest) req_list; - QTAILQ_HEAD(out_req_list, NvmeRequest) out_req_list; - QTAILQ_ENTRY(NvmeSQueue) entry; -} NvmeSQueue; - -typedef struct NvmeCQueue { - struct NvmeCtrl *ctrl; - uint8_t phase; - uint16_t cqid; - uint16_t irq_enabled; - uint32_t head; - uint32_t tail; - uint32_t vector; - uint32_t size; - uint64_t dma_addr; - QEMUTimer *timer; - QTAILQ_HEAD(sq_list, NvmeSQueue) sq_list; - QTAILQ_HEAD(cq_req_list, NvmeRequest) req_list; -} NvmeCQueue; - -typedef struct NvmeNamespace { - NvmeIdNs id_ns; -} NvmeNamespace; - -#define TYPE_NVME "nvme" -#define NVME(obj) \ - OBJECT_CHECK(NvmeCtrl, (obj), TYPE_NVME) - -typedef struct NvmeCtrl { - PCIDevice parent_obj; - MemoryRegion iomem; - NvmeBar bar; - BlockConf conf; - - uint32_t page_size; - uint16_t page_bits; - uint16_t max_prp_ents; - uint16_t cqe_size; - uint16_t sqe_size; - uint32_t reg_size; - uint32_t num_namespaces; - uint32_t num_queues; - uint32_t max_q_ents; - uint64_t ns_size; - - char *serial; - NvmeNamespace *namespaces; - NvmeSQueue **sq; - NvmeCQueue **cq; - NvmeSQueue admin_sq; - NvmeCQueue admin_cq; - NvmeIdCtrl id_ctrl; -} NvmeCtrl; - -#endif /* HW_NVME_H */ diff --git a/qemu/hw/block/onenand.c b/qemu/hw/block/onenand.c deleted file mode 100644 index 883f4b1fa..000000000 --- a/qemu/hw/block/onenand.c +++ /dev/null @@ -1,850 +0,0 @@ -/* - * OneNAND flash memories emulation. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "hw/hw.h" -#include "hw/block/flash.h" -#include "hw/irq.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" -#include "hw/sysbus.h" -#include "qemu/error-report.h" - -/* 11 for 2kB-page OneNAND ("2nd generation") and 10 for 1kB-page chips */ -#define PAGE_SHIFT 11 - -/* Fixed */ -#define BLOCK_SHIFT (PAGE_SHIFT + 6) - -#define TYPE_ONE_NAND "onenand" -#define ONE_NAND(obj) OBJECT_CHECK(OneNANDState, (obj), TYPE_ONE_NAND) - -typedef struct OneNANDState { - SysBusDevice parent_obj; - - struct { - uint16_t man; - uint16_t dev; - uint16_t ver; - } id; - int shift; - hwaddr base; - qemu_irq intr; - qemu_irq rdy; - BlockBackend *blk; - BlockBackend *blk_cur; - uint8_t *image; - uint8_t *otp; - uint8_t *current; - MemoryRegion ram; - MemoryRegion mapped_ram; - uint8_t current_direction; - uint8_t *boot[2]; - uint8_t *data[2][2]; - MemoryRegion iomem; - MemoryRegion container; - int cycle; - int otpmode; - - uint16_t addr[8]; - uint16_t unladdr[8]; - int bufaddr; - int count; - uint16_t command; - uint16_t config[2]; - uint16_t status; - uint16_t intstatus; - uint16_t wpstatus; - - ECCState ecc; - - int density_mask; - int secs; - int secs_cur; - int blocks; - uint8_t *blockwp; -} OneNANDState; - -enum { - ONEN_BUF_BLOCK = 0, - ONEN_BUF_BLOCK2 = 1, - ONEN_BUF_DEST_BLOCK = 2, - ONEN_BUF_DEST_PAGE = 3, - ONEN_BUF_PAGE = 7, -}; - -enum { - ONEN_ERR_CMD = 1 << 10, - ONEN_ERR_ERASE = 1 << 11, - ONEN_ERR_PROG = 1 << 12, - ONEN_ERR_LOAD = 1 << 13, -}; - -enum { - ONEN_INT_RESET = 1 << 4, - ONEN_INT_ERASE = 1 << 5, - ONEN_INT_PROG = 1 << 6, - ONEN_INT_LOAD = 1 << 7, - ONEN_INT = 1 << 15, -}; - -enum { - ONEN_LOCK_LOCKTIGHTEN = 1 << 0, - ONEN_LOCK_LOCKED = 1 << 1, - ONEN_LOCK_UNLOCKED = 1 << 2, -}; - -static void onenand_mem_setup(OneNANDState *s) -{ - /* XXX: We should use IO_MEM_ROMD but we broke it earlier... - * Both 0x0000 ... 0x01ff and 0x8000 ... 0x800f can be used to - * write boot commands. Also take note of the BWPS bit. */ - memory_region_init(&s->container, OBJECT(s), "onenand", - 0x10000 << s->shift); - memory_region_add_subregion(&s->container, 0, &s->iomem); - memory_region_init_alias(&s->mapped_ram, OBJECT(s), "onenand-mapped-ram", - &s->ram, 0x0200 << s->shift, - 0xbe00 << s->shift); - memory_region_add_subregion_overlap(&s->container, - 0x0200 << s->shift, - &s->mapped_ram, - 1); -} - -static void onenand_intr_update(OneNANDState *s) -{ - qemu_set_irq(s->intr, ((s->intstatus >> 15) ^ (~s->config[0] >> 6)) & 1); -} - -static void onenand_pre_save(void *opaque) -{ - OneNANDState *s = opaque; - if (s->current == s->otp) { - s->current_direction = 1; - } else if (s->current == s->image) { - s->current_direction = 2; - } else { - s->current_direction = 0; - } -} - -static int onenand_post_load(void *opaque, int version_id) -{ - OneNANDState *s = opaque; - switch (s->current_direction) { - case 0: - break; - case 1: - s->current = s->otp; - break; - case 2: - s->current = s->image; - break; - default: - return -1; - } - onenand_intr_update(s); - return 0; -} - -static const VMStateDescription vmstate_onenand = { - .name = "onenand", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = onenand_pre_save, - .post_load = onenand_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(current_direction, OneNANDState), - VMSTATE_INT32(cycle, OneNANDState), - VMSTATE_INT32(otpmode, OneNANDState), - VMSTATE_UINT16_ARRAY(addr, OneNANDState, 8), - VMSTATE_UINT16_ARRAY(unladdr, OneNANDState, 8), - VMSTATE_INT32(bufaddr, OneNANDState), - VMSTATE_INT32(count, OneNANDState), - VMSTATE_UINT16(command, OneNANDState), - VMSTATE_UINT16_ARRAY(config, OneNANDState, 2), - VMSTATE_UINT16(status, OneNANDState), - VMSTATE_UINT16(intstatus, OneNANDState), - VMSTATE_UINT16(wpstatus, OneNANDState), - VMSTATE_INT32(secs_cur, OneNANDState), - VMSTATE_PARTIAL_VBUFFER(blockwp, OneNANDState, blocks), - VMSTATE_UINT8(ecc.cp, OneNANDState), - VMSTATE_UINT16_ARRAY(ecc.lp, OneNANDState, 2), - VMSTATE_UINT16(ecc.count, OneNANDState), - VMSTATE_BUFFER_POINTER_UNSAFE(otp, OneNANDState, 0, - ((64 + 2) << PAGE_SHIFT)), - VMSTATE_END_OF_LIST() - } -}; - -/* Hot reset (Reset OneNAND command) or warm reset (RP pin low) */ -static void onenand_reset(OneNANDState *s, int cold) -{ - memset(&s->addr, 0, sizeof(s->addr)); - s->command = 0; - s->count = 1; - s->bufaddr = 0; - s->config[0] = 0x40c0; - s->config[1] = 0x0000; - onenand_intr_update(s); - qemu_irq_raise(s->rdy); - s->status = 0x0000; - s->intstatus = cold ? 0x8080 : 0x8010; - s->unladdr[0] = 0; - s->unladdr[1] = 0; - s->wpstatus = 0x0002; - s->cycle = 0; - s->otpmode = 0; - s->blk_cur = s->blk; - s->current = s->image; - s->secs_cur = s->secs; - - if (cold) { - /* Lock the whole flash */ - memset(s->blockwp, ONEN_LOCK_LOCKED, s->blocks); - - if (s->blk_cur && blk_read(s->blk_cur, 0, s->boot[0], 8) < 0) { - hw_error("%s: Loading the BootRAM failed.\n", __func__); - } - } -} - -static void onenand_system_reset(DeviceState *dev) -{ - OneNANDState *s = ONE_NAND(dev); - - onenand_reset(s, 1); -} - -static inline int onenand_load_main(OneNANDState *s, int sec, int secn, - void *dest) -{ - if (s->blk_cur) { - return blk_read(s->blk_cur, sec, dest, secn) < 0; - } else if (sec + secn > s->secs_cur) { - return 1; - } - - memcpy(dest, s->current + (sec << 9), secn << 9); - - return 0; -} - -static inline int onenand_prog_main(OneNANDState *s, int sec, int secn, - void *src) -{ - int result = 0; - - if (secn > 0) { - uint32_t size = (uint32_t)secn * 512; - const uint8_t *sp = (const uint8_t *)src; - uint8_t *dp = 0; - if (s->blk_cur) { - dp = g_malloc(size); - if (!dp || blk_read(s->blk_cur, sec, dp, secn) < 0) { - result = 1; - } - } else { - if (sec + secn > s->secs_cur) { - result = 1; - } else { - dp = (uint8_t *)s->current + (sec << 9); - } - } - if (!result) { - uint32_t i; - for (i = 0; i < size; i++) { - dp[i] &= sp[i]; - } - if (s->blk_cur) { - result = blk_write(s->blk_cur, sec, dp, secn) < 0; - } - } - if (dp && s->blk_cur) { - g_free(dp); - } - } - - return result; -} - -static inline int onenand_load_spare(OneNANDState *s, int sec, int secn, - void *dest) -{ - uint8_t buf[512]; - - if (s->blk_cur) { - if (blk_read(s->blk_cur, s->secs_cur + (sec >> 5), buf, 1) < 0) { - return 1; - } - memcpy(dest, buf + ((sec & 31) << 4), secn << 4); - } else if (sec + secn > s->secs_cur) { - return 1; - } else { - memcpy(dest, s->current + (s->secs_cur << 9) + (sec << 4), secn << 4); - } - - return 0; -} - -static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn, - void *src) -{ - int result = 0; - if (secn > 0) { - const uint8_t *sp = (const uint8_t *)src; - uint8_t *dp = 0, *dpp = 0; - if (s->blk_cur) { - dp = g_malloc(512); - if (!dp - || blk_read(s->blk_cur, s->secs_cur + (sec >> 5), dp, 1) < 0) { - result = 1; - } else { - dpp = dp + ((sec & 31) << 4); - } - } else { - if (sec + secn > s->secs_cur) { - result = 1; - } else { - dpp = s->current + (s->secs_cur << 9) + (sec << 4); - } - } - if (!result) { - uint32_t i; - for (i = 0; i < (secn << 4); i++) { - dpp[i] &= sp[i]; - } - if (s->blk_cur) { - result = blk_write(s->blk_cur, s->secs_cur + (sec >> 5), - dp, 1) < 0; - } - } - g_free(dp); - } - return result; -} - -static inline int onenand_erase(OneNANDState *s, int sec, int num) -{ - uint8_t *blankbuf, *tmpbuf; - - blankbuf = g_malloc(512); - tmpbuf = g_malloc(512); - memset(blankbuf, 0xff, 512); - for (; num > 0; num--, sec++) { - if (s->blk_cur) { - int erasesec = s->secs_cur + (sec >> 5); - if (blk_write(s->blk_cur, sec, blankbuf, 1) < 0) { - goto fail; - } - if (blk_read(s->blk_cur, erasesec, tmpbuf, 1) < 0) { - goto fail; - } - memcpy(tmpbuf + ((sec & 31) << 4), blankbuf, 1 << 4); - if (blk_write(s->blk_cur, erasesec, tmpbuf, 1) < 0) { - goto fail; - } - } else { - if (sec + 1 > s->secs_cur) { - goto fail; - } - memcpy(s->current + (sec << 9), blankbuf, 512); - memcpy(s->current + (s->secs_cur << 9) + (sec << 4), - blankbuf, 1 << 4); - } - } - - g_free(tmpbuf); - g_free(blankbuf); - return 0; - -fail: - g_free(tmpbuf); - g_free(blankbuf); - return 1; -} - -static void onenand_command(OneNANDState *s) -{ - int b; - int sec; - void *buf; -#define SETADDR(block, page) \ - sec = (s->addr[page] & 3) + \ - ((((s->addr[page] >> 2) & 0x3f) + \ - (((s->addr[block] & 0xfff) | \ - (s->addr[block] >> 15 ? \ - s->density_mask : 0)) << 6)) << (PAGE_SHIFT - 9)); -#define SETBUF_M() \ - buf = (s->bufaddr & 8) ? \ - s->data[(s->bufaddr >> 2) & 1][0] : s->boot[0]; \ - buf += (s->bufaddr & 3) << 9; -#define SETBUF_S() \ - buf = (s->bufaddr & 8) ? \ - s->data[(s->bufaddr >> 2) & 1][1] : s->boot[1]; \ - buf += (s->bufaddr & 3) << 4; - - switch (s->command) { - case 0x00: /* Load single/multiple sector data unit into buffer */ - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - - SETBUF_M() - if (onenand_load_main(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD; - -#if 0 - SETBUF_S() - if (onenand_load_spare(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD; -#endif - - /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) - * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) - * then we need two split the read/write into two chunks. - */ - s->intstatus |= ONEN_INT | ONEN_INT_LOAD; - break; - case 0x13: /* Load single/multiple spare sector into buffer */ - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - - SETBUF_S() - if (onenand_load_spare(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD; - - /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) - * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) - * then we need two split the read/write into two chunks. - */ - s->intstatus |= ONEN_INT | ONEN_INT_LOAD; - break; - case 0x80: /* Program single/multiple sector data unit from buffer */ - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - - SETBUF_M() - if (onenand_prog_main(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; - -#if 0 - SETBUF_S() - if (onenand_prog_spare(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; -#endif - - /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) - * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) - * then we need two split the read/write into two chunks. - */ - s->intstatus |= ONEN_INT | ONEN_INT_PROG; - break; - case 0x1a: /* Program single/multiple spare area sector from buffer */ - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - - SETBUF_S() - if (onenand_prog_spare(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; - - /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) - * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) - * then we need two split the read/write into two chunks. - */ - s->intstatus |= ONEN_INT | ONEN_INT_PROG; - break; - case 0x1b: /* Copy-back program */ - SETBUF_S() - - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - if (onenand_load_main(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; - - SETADDR(ONEN_BUF_DEST_BLOCK, ONEN_BUF_DEST_PAGE) - if (onenand_prog_main(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; - - /* TODO: spare areas */ - - s->intstatus |= ONEN_INT | ONEN_INT_PROG; - break; - - case 0x23: /* Unlock NAND array block(s) */ - s->intstatus |= ONEN_INT; - - /* XXX the previous (?) area should be locked automatically */ - for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) { - if (b >= s->blocks) { - s->status |= ONEN_ERR_CMD; - break; - } - if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN) - break; - - s->wpstatus = s->blockwp[b] = ONEN_LOCK_UNLOCKED; - } - break; - case 0x27: /* Unlock All NAND array blocks */ - s->intstatus |= ONEN_INT; - - for (b = 0; b < s->blocks; b ++) { - if (b >= s->blocks) { - s->status |= ONEN_ERR_CMD; - break; - } - if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN) - break; - - s->wpstatus = s->blockwp[b] = ONEN_LOCK_UNLOCKED; - } - break; - - case 0x2a: /* Lock NAND array block(s) */ - s->intstatus |= ONEN_INT; - - for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) { - if (b >= s->blocks) { - s->status |= ONEN_ERR_CMD; - break; - } - if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN) - break; - - s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKED; - } - break; - case 0x2c: /* Lock-tight NAND array block(s) */ - s->intstatus |= ONEN_INT; - - for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) { - if (b >= s->blocks) { - s->status |= ONEN_ERR_CMD; - break; - } - if (s->blockwp[b] == ONEN_LOCK_UNLOCKED) - continue; - - s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKTIGHTEN; - } - break; - - case 0x71: /* Erase-Verify-Read */ - s->intstatus |= ONEN_INT; - break; - case 0x95: /* Multi-block erase */ - qemu_irq_pulse(s->intr); - /* Fall through. */ - case 0x94: /* Block erase */ - sec = ((s->addr[ONEN_BUF_BLOCK] & 0xfff) | - (s->addr[ONEN_BUF_BLOCK] >> 15 ? s->density_mask : 0)) - << (BLOCK_SHIFT - 9); - if (onenand_erase(s, sec, 1 << (BLOCK_SHIFT - 9))) - s->status |= ONEN_ERR_CMD | ONEN_ERR_ERASE; - - s->intstatus |= ONEN_INT | ONEN_INT_ERASE; - break; - case 0xb0: /* Erase suspend */ - break; - case 0x30: /* Erase resume */ - s->intstatus |= ONEN_INT | ONEN_INT_ERASE; - break; - - case 0xf0: /* Reset NAND Flash core */ - onenand_reset(s, 0); - break; - case 0xf3: /* Reset OneNAND */ - onenand_reset(s, 0); - break; - - case 0x65: /* OTP Access */ - s->intstatus |= ONEN_INT; - s->blk_cur = NULL; - s->current = s->otp; - s->secs_cur = 1 << (BLOCK_SHIFT - 9); - s->addr[ONEN_BUF_BLOCK] = 0; - s->otpmode = 1; - break; - - default: - s->status |= ONEN_ERR_CMD; - s->intstatus |= ONEN_INT; - fprintf(stderr, "%s: unknown OneNAND command %x\n", - __func__, s->command); - } - - onenand_intr_update(s); -} - -static uint64_t onenand_read(void *opaque, hwaddr addr, - unsigned size) -{ - OneNANDState *s = (OneNANDState *) opaque; - int offset = addr >> s->shift; - - switch (offset) { - case 0x0000 ... 0xc000: - return lduw_le_p(s->boot[0] + addr); - - case 0xf000: /* Manufacturer ID */ - return s->id.man; - case 0xf001: /* Device ID */ - return s->id.dev; - case 0xf002: /* Version ID */ - return s->id.ver; - /* TODO: get the following values from a real chip! */ - case 0xf003: /* Data Buffer size */ - return 1 << PAGE_SHIFT; - case 0xf004: /* Boot Buffer size */ - return 0x200; - case 0xf005: /* Amount of buffers */ - return 1 | (2 << 8); - case 0xf006: /* Technology */ - return 0; - - case 0xf100 ... 0xf107: /* Start addresses */ - return s->addr[offset - 0xf100]; - - case 0xf200: /* Start buffer */ - return (s->bufaddr << 8) | ((s->count - 1) & (1 << (PAGE_SHIFT - 10))); - - case 0xf220: /* Command */ - return s->command; - case 0xf221: /* System Configuration 1 */ - return s->config[0] & 0xffe0; - case 0xf222: /* System Configuration 2 */ - return s->config[1]; - - case 0xf240: /* Controller Status */ - return s->status; - case 0xf241: /* Interrupt */ - return s->intstatus; - case 0xf24c: /* Unlock Start Block Address */ - return s->unladdr[0]; - case 0xf24d: /* Unlock End Block Address */ - return s->unladdr[1]; - case 0xf24e: /* Write Protection Status */ - return s->wpstatus; - - case 0xff00: /* ECC Status */ - return 0x00; - case 0xff01: /* ECC Result of main area data */ - case 0xff02: /* ECC Result of spare area data */ - case 0xff03: /* ECC Result of main area data */ - case 0xff04: /* ECC Result of spare area data */ - hw_error("%s: imeplement ECC\n", __FUNCTION__); - return 0x0000; - } - - fprintf(stderr, "%s: unknown OneNAND register %x\n", - __FUNCTION__, offset); - return 0; -} - -static void onenand_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - OneNANDState *s = (OneNANDState *) opaque; - int offset = addr >> s->shift; - int sec; - - switch (offset) { - case 0x0000 ... 0x01ff: - case 0x8000 ... 0x800f: - if (s->cycle) { - s->cycle = 0; - - if (value == 0x0000) { - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - onenand_load_main(s, sec, - 1 << (PAGE_SHIFT - 9), s->data[0][0]); - s->addr[ONEN_BUF_PAGE] += 4; - s->addr[ONEN_BUF_PAGE] &= 0xff; - } - break; - } - - switch (value) { - case 0x00f0: /* Reset OneNAND */ - onenand_reset(s, 0); - break; - - case 0x00e0: /* Load Data into Buffer */ - s->cycle = 1; - break; - - case 0x0090: /* Read Identification Data */ - memset(s->boot[0], 0, 3 << s->shift); - s->boot[0][0 << s->shift] = s->id.man & 0xff; - s->boot[0][1 << s->shift] = s->id.dev & 0xff; - s->boot[0][2 << s->shift] = s->wpstatus & 0xff; - break; - - default: - fprintf(stderr, "%s: unknown OneNAND boot command %"PRIx64"\n", - __FUNCTION__, value); - } - break; - - case 0xf100 ... 0xf107: /* Start addresses */ - s->addr[offset - 0xf100] = value; - break; - - case 0xf200: /* Start buffer */ - s->bufaddr = (value >> 8) & 0xf; - if (PAGE_SHIFT == 11) - s->count = (value & 3) ?: 4; - else if (PAGE_SHIFT == 10) - s->count = (value & 1) ?: 2; - break; - - case 0xf220: /* Command */ - if (s->intstatus & (1 << 15)) - break; - s->command = value; - onenand_command(s); - break; - case 0xf221: /* System Configuration 1 */ - s->config[0] = value; - onenand_intr_update(s); - qemu_set_irq(s->rdy, (s->config[0] >> 7) & 1); - break; - case 0xf222: /* System Configuration 2 */ - s->config[1] = value; - break; - - case 0xf241: /* Interrupt */ - s->intstatus &= value; - if ((1 << 15) & ~s->intstatus) - s->status &= ~(ONEN_ERR_CMD | ONEN_ERR_ERASE | - ONEN_ERR_PROG | ONEN_ERR_LOAD); - onenand_intr_update(s); - break; - case 0xf24c: /* Unlock Start Block Address */ - s->unladdr[0] = value & (s->blocks - 1); - /* For some reason we have to set the end address to by default - * be same as start because the software forgets to write anything - * in there. */ - s->unladdr[1] = value & (s->blocks - 1); - break; - case 0xf24d: /* Unlock End Block Address */ - s->unladdr[1] = value & (s->blocks - 1); - break; - - default: - fprintf(stderr, "%s: unknown OneNAND register %x\n", - __FUNCTION__, offset); - } -} - -static const MemoryRegionOps onenand_ops = { - .read = onenand_read, - .write = onenand_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int onenand_initfn(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - OneNANDState *s = ONE_NAND(dev); - uint32_t size = 1 << (24 + ((s->id.dev >> 4) & 7)); - void *ram; - - s->base = (hwaddr)-1; - s->rdy = NULL; - s->blocks = size >> BLOCK_SHIFT; - s->secs = size >> 9; - s->blockwp = g_malloc(s->blocks); - s->density_mask = (s->id.dev & 0x08) - ? (1 << (6 + ((s->id.dev >> 4) & 7))) : 0; - memory_region_init_io(&s->iomem, OBJECT(s), &onenand_ops, s, "onenand", - 0x10000 << s->shift); - if (!s->blk) { - s->image = memset(g_malloc(size + (size >> 5)), - 0xff, size + (size >> 5)); - } else { - if (blk_is_read_only(s->blk)) { - error_report("Can't use a read-only drive"); - return -1; - } - s->blk_cur = s->blk; - } - 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_fatal); - vmstate_register_ram_global(&s->ram); - ram = memory_region_get_ram_ptr(&s->ram); - s->boot[0] = ram + (0x0000 << s->shift); - s->boot[1] = ram + (0x8000 << s->shift); - s->data[0][0] = ram + ((0x0200 + (0 << (PAGE_SHIFT - 1))) << s->shift); - s->data[0][1] = ram + ((0x8010 + (0 << (PAGE_SHIFT - 6))) << s->shift); - s->data[1][0] = ram + ((0x0200 + (1 << (PAGE_SHIFT - 1))) << s->shift); - s->data[1][1] = ram + ((0x8010 + (1 << (PAGE_SHIFT - 6))) << s->shift); - onenand_mem_setup(s); - sysbus_init_irq(sbd, &s->intr); - sysbus_init_mmio(sbd, &s->container); - vmstate_register(dev, - ((s->shift & 0x7f) << 24) - | ((s->id.man & 0xff) << 16) - | ((s->id.dev & 0xff) << 8) - | (s->id.ver & 0xff), - &vmstate_onenand, s); - return 0; -} - -static Property onenand_properties[] = { - DEFINE_PROP_UINT16("manufacturer_id", OneNANDState, id.man, 0), - DEFINE_PROP_UINT16("device_id", OneNANDState, id.dev, 0), - DEFINE_PROP_UINT16("version_id", OneNANDState, id.ver, 0), - DEFINE_PROP_INT32("shift", OneNANDState, shift, 0), - DEFINE_PROP_DRIVE("drive", OneNANDState, blk), - DEFINE_PROP_END_OF_LIST(), -}; - -static void onenand_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = onenand_initfn; - dc->reset = onenand_system_reset; - dc->props = onenand_properties; -} - -static const TypeInfo onenand_info = { - .name = TYPE_ONE_NAND, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(OneNANDState), - .class_init = onenand_class_init, -}; - -static void onenand_register_types(void) -{ - type_register_static(&onenand_info); -} - -void *onenand_raw_otp(DeviceState *onenand_device) -{ - OneNANDState *s = ONE_NAND(onenand_device); - - return s->otp; -} - -type_init(onenand_register_types) diff --git a/qemu/hw/block/pflash_cfi01.c b/qemu/hw/block/pflash_cfi01.c deleted file mode 100644 index 106a77523..000000000 --- a/qemu/hw/block/pflash_cfi01.c +++ /dev/null @@ -1,970 +0,0 @@ -/* - * CFI parallel flash with Intel command set emulation - * - * Copyright (c) 2006 Thorsten Zitterell - * Copyright (c) 2005 Jocelyn Mayer - * - * 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 . - */ - -/* - * For now, this code can emulate flashes of 1, 2 or 4 bytes width. - * Supported commands/modes are: - * - flash read - * - flash write - * - flash ID read - * - sector erase - * - CFI queries - * - * It does not support timings - * It does not support flash interleaving - * It does not implement software data protection as found in many real chips - * It does not implement erase suspend/resume commands - * It does not implement multiple sectors erase - * - * 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 { \ - fprintf(stderr, "PFLASH: Possible BUG - " fmt, ## __VA_ARGS__); \ - exit(1); \ -} while(0) - -/* #define PFLASH_DEBUG */ -#ifdef PFLASH_DEBUG -#define DPRINTF(fmt, ...) \ -do { \ - fprintf(stderr, "PFLASH: " fmt , ## __VA_ARGS__); \ -} while (0) -#else -#define DPRINTF(fmt, ...) do { } while (0) -#endif - -#define TYPE_CFI_PFLASH01 "cfi.pflash01" -#define CFI_PFLASH01(obj) OBJECT_CHECK(pflash_t, (obj), TYPE_CFI_PFLASH01) - -#define PFLASH_BE 0 -#define PFLASH_SECURE 1 - -struct pflash_t { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - BlockBackend *blk; - uint32_t nb_blocs; - uint64_t sector_len; - uint8_t bank_width; - uint8_t device_width; /* If 0, device width not specified. */ - uint8_t max_device_width; /* max device width in bytes */ - uint32_t features; - uint8_t wcycle; /* if 0, the flash is read normally */ - int ro; - uint8_t cmd; - uint8_t status; - uint16_t ident0; - uint16_t ident1; - uint16_t ident2; - uint16_t ident3; - uint8_t cfi_len; - uint8_t cfi_table[0x52]; - uint64_t counter; - unsigned int writeblock_size; - QEMUTimer *timer; - MemoryRegion mem; - char *name; - void *storage; - VMChangeStateEntry *vmstate; -}; - -static int pflash_post_load(void *opaque, int version_id); - -static const VMStateDescription vmstate_pflash = { - .name = "pflash_cfi01", - .version_id = 1, - .minimum_version_id = 1, - .post_load = pflash_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(wcycle, pflash_t), - VMSTATE_UINT8(cmd, pflash_t), - VMSTATE_UINT8(status, pflash_t), - VMSTATE_UINT64(counter, pflash_t), - VMSTATE_END_OF_LIST() - } -}; - -static void pflash_timer (void *opaque) -{ - pflash_t *pfl = opaque; - - DPRINTF("%s: command %02x done\n", __func__, pfl->cmd); - /* Reset flash */ - pfl->status ^= 0x80; - memory_region_rom_device_set_romd(&pfl->mem, true); - pfl->wcycle = 0; - pfl->cmd = 0; -} - -/* Perform a CFI query based on the bank width of the flash. - * If this code is called we know we have a device_width set for - * this flash. - */ -static uint32_t pflash_cfi_query(pflash_t *pfl, hwaddr offset) -{ - int i; - uint32_t resp = 0; - hwaddr boff; - - /* Adjust incoming offset to match expected device-width - * addressing. CFI query addresses are always specified in terms of - * the maximum supported width of the device. This means that x8 - * devices and x8/x16 devices in x8 mode behave differently. For - * devices that are not used at their max width, we will be - * provided with addresses that use higher address bits than - * expected (based on the max width), so we will shift them lower - * so that they will match the addresses used when - * device_width==max_device_width. - */ - boff = offset >> (ctz32(pfl->bank_width) + - ctz32(pfl->max_device_width) - ctz32(pfl->device_width)); - - if (boff > pfl->cfi_len) { - return 0; - } - /* Now we will construct the CFI response generated by a single - * device, then replicate that for all devices that make up the - * bus. For wide parts used in x8 mode, CFI query responses - * are different than native byte-wide parts. - */ - resp = pfl->cfi_table[boff]; - if (pfl->device_width != pfl->max_device_width) { - /* The only case currently supported is x8 mode for a - * wider part. - */ - if (pfl->device_width != 1 || pfl->bank_width > 4) { - DPRINTF("%s: Unsupported device configuration: " - "device_width=%d, max_device_width=%d\n", - __func__, pfl->device_width, - pfl->max_device_width); - return 0; - } - /* CFI query data is repeated, rather than zero padded for - * wide devices used in x8 mode. - */ - for (i = 1; i < pfl->max_device_width; i++) { - resp = deposit32(resp, 8 * i, 8, pfl->cfi_table[boff]); - } - } - /* Replicate responses for each device in bank. */ - if (pfl->device_width < pfl->bank_width) { - for (i = pfl->device_width; - i < pfl->bank_width; i += pfl->device_width) { - resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp); - } - } - - return resp; -} - - - -/* Perform a device id query based on the bank width of the flash. */ -static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset) -{ - int i; - uint32_t resp; - hwaddr boff; - - /* Adjust incoming offset to match expected device-width - * addressing. Device ID read addresses are always specified in - * terms of the maximum supported width of the device. This means - * that x8 devices and x8/x16 devices in x8 mode behave - * differently. For devices that are not used at their max width, - * we will be provided with addresses that use higher address bits - * than expected (based on the max width), so we will shift them - * lower so that they will match the addresses used when - * device_width==max_device_width. - */ - boff = offset >> (ctz32(pfl->bank_width) + - ctz32(pfl->max_device_width) - ctz32(pfl->device_width)); - - /* Mask off upper bits which may be used in to query block - * or sector lock status at other addresses. - * Offsets 2/3 are block lock status, is not emulated. - */ - switch (boff & 0xFF) { - case 0: - resp = pfl->ident0; - DPRINTF("%s: Manufacturer Code %04x\n", __func__, resp); - break; - case 1: - resp = pfl->ident1; - DPRINTF("%s: Device ID Code %04x\n", __func__, resp); - break; - default: - DPRINTF("%s: Read Device Information offset=%x\n", __func__, - (unsigned)offset); - return 0; - break; - } - /* Replicate responses for each device in bank. */ - if (pfl->device_width < pfl->bank_width) { - for (i = pfl->device_width; - i < pfl->bank_width; i += pfl->device_width) { - resp = deposit32(resp, 8 * i, 8 * pfl->device_width, resp); - } - } - - return resp; -} - -static uint32_t pflash_data_read(pflash_t *pfl, hwaddr offset, - int width, int be) -{ - uint8_t *p; - uint32_t ret; - - p = pfl->storage; - switch (width) { - case 1: - ret = p[offset]; - DPRINTF("%s: data offset " TARGET_FMT_plx " %02x\n", - __func__, offset, ret); - break; - case 2: - if (be) { - ret = p[offset] << 8; - ret |= p[offset + 1]; - } else { - ret = p[offset]; - ret |= p[offset + 1] << 8; - } - DPRINTF("%s: data offset " TARGET_FMT_plx " %04x\n", - __func__, offset, ret); - break; - case 4: - if (be) { - ret = p[offset] << 24; - ret |= p[offset + 1] << 16; - ret |= p[offset + 2] << 8; - ret |= p[offset + 3]; - } else { - ret = p[offset]; - ret |= p[offset + 1] << 8; - ret |= p[offset + 2] << 16; - ret |= p[offset + 3] << 24; - } - DPRINTF("%s: data offset " TARGET_FMT_plx " %08x\n", - __func__, offset, ret); - break; - default: - DPRINTF("BUG in %s\n", __func__); - abort(); - } - return ret; -} - -static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, - int width, int be) -{ - hwaddr boff; - uint32_t ret; - - ret = -1; - -#if 0 - DPRINTF("%s: reading offset " TARGET_FMT_plx " under cmd %02x width %d\n", - __func__, offset, pfl->cmd, width); -#endif - switch (pfl->cmd) { - default: - /* This should never happen : reset state & treat it as a read */ - DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd); - pfl->wcycle = 0; - pfl->cmd = 0; - /* fall through to read code */ - case 0x00: - /* Flash area read */ - ret = pflash_data_read(pfl, offset, width, be); - break; - case 0x10: /* Single byte program */ - case 0x20: /* Block erase */ - case 0x28: /* Block erase */ - case 0x40: /* single byte program */ - case 0x50: /* Clear status register */ - case 0x60: /* Block /un)lock */ - case 0x70: /* Status Register */ - case 0xe8: /* Write block */ - /* Status register read. Return status from each device in - * bank. - */ - ret = pfl->status; - if (pfl->device_width && width > pfl->device_width) { - int shift = pfl->device_width * 8; - while (shift + pfl->device_width * 8 <= width * 8) { - ret |= pfl->status << shift; - shift += pfl->device_width * 8; - } - } else if (!pfl->device_width && width > 2) { - /* Handle 32 bit flash cases where device width is not - * set. (Existing behavior before device width added.) - */ - ret |= pfl->status << 16; - } - DPRINTF("%s: status %x\n", __func__, ret); - break; - case 0x90: - if (!pfl->device_width) { - /* Preserve old behavior if device width not specified */ - boff = offset & 0xFF; - if (pfl->bank_width == 2) { - boff = boff >> 1; - } else if (pfl->bank_width == 4) { - boff = boff >> 2; - } - - switch (boff) { - case 0: - ret = pfl->ident0 << 8 | pfl->ident1; - DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret); - break; - case 1: - ret = pfl->ident2 << 8 | pfl->ident3; - DPRINTF("%s: Device ID Code %04x\n", __func__, ret); - break; - default: - DPRINTF("%s: Read Device Information boff=%x\n", __func__, - (unsigned)boff); - ret = 0; - break; - } - } else { - /* If we have a read larger than the bank_width, combine multiple - * manufacturer/device ID queries into a single response. - */ - int i; - for (i = 0; i < width; i += pfl->bank_width) { - ret = deposit32(ret, i * 8, pfl->bank_width * 8, - pflash_devid_query(pfl, - offset + i * pfl->bank_width)); - } - } - break; - case 0x98: /* Query mode */ - if (!pfl->device_width) { - /* Preserve old behavior if device width not specified */ - boff = offset & 0xFF; - if (pfl->bank_width == 2) { - boff = boff >> 1; - } else if (pfl->bank_width == 4) { - boff = boff >> 2; - } - - if (boff > pfl->cfi_len) { - ret = 0; - } else { - ret = pfl->cfi_table[boff]; - } - } else { - /* If we have a read larger than the bank_width, combine multiple - * CFI queries into a single response. - */ - int i; - for (i = 0; i < width; i += pfl->bank_width) { - ret = deposit32(ret, i * 8, pfl->bank_width * 8, - pflash_cfi_query(pfl, - offset + i * pfl->bank_width)); - } - } - - break; - } - return ret; -} - -/* update flash content on disk */ -static void pflash_update(pflash_t *pfl, int offset, - int size) -{ - int offset_end; - if (pfl->blk) { - offset_end = offset + size; - /* round to sectors */ - offset = offset >> 9; - offset_end = (offset_end + 511) >> 9; - blk_write(pfl->blk, offset, pfl->storage + (offset << 9), - offset_end - offset); - } -} - -static inline void pflash_data_write(pflash_t *pfl, hwaddr offset, - uint32_t value, int width, int be) -{ - uint8_t *p = pfl->storage; - - DPRINTF("%s: block write offset " TARGET_FMT_plx - " value %x counter %016" PRIx64 "\n", - __func__, offset, value, pfl->counter); - switch (width) { - case 1: - p[offset] = value; - break; - case 2: - if (be) { - p[offset] = value >> 8; - p[offset + 1] = value; - } else { - p[offset] = value; - p[offset + 1] = value >> 8; - } - break; - case 4: - if (be) { - p[offset] = value >> 24; - p[offset + 1] = value >> 16; - p[offset + 2] = value >> 8; - p[offset + 3] = value; - } else { - p[offset] = value; - p[offset + 1] = value >> 8; - p[offset + 2] = value >> 16; - p[offset + 3] = value >> 24; - } - break; - } - -} - -static void pflash_write(pflash_t *pfl, hwaddr offset, - uint32_t value, int width, int be) -{ - uint8_t *p; - uint8_t cmd; - - cmd = value; - - DPRINTF("%s: writing offset " TARGET_FMT_plx " value %08x width %d wcycle 0x%x\n", - __func__, offset, value, width, pfl->wcycle); - - if (!pfl->wcycle) { - /* Set the device in I/O access mode */ - memory_region_rom_device_set_romd(&pfl->mem, false); - } - - switch (pfl->wcycle) { - case 0: - /* read mode */ - switch (cmd) { - case 0x00: /* ??? */ - goto reset_flash; - case 0x10: /* Single Byte Program */ - case 0x40: /* Single Byte Program */ - DPRINTF("%s: Single Byte Program\n", __func__); - break; - case 0x20: /* Block erase */ - p = pfl->storage; - offset &= ~(pfl->sector_len - 1); - - DPRINTF("%s: block erase at " TARGET_FMT_plx " bytes %x\n", - __func__, offset, (unsigned)pfl->sector_len); - - if (!pfl->ro) { - memset(p + offset, 0xff, pfl->sector_len); - pflash_update(pfl, offset, pfl->sector_len); - } else { - pfl->status |= 0x20; /* Block erase error */ - } - pfl->status |= 0x80; /* Ready! */ - break; - case 0x50: /* Clear status bits */ - DPRINTF("%s: Clear status bits\n", __func__); - pfl->status = 0x0; - goto reset_flash; - case 0x60: /* Block (un)lock */ - DPRINTF("%s: Block unlock\n", __func__); - break; - case 0x70: /* Status Register */ - DPRINTF("%s: Read status register\n", __func__); - pfl->cmd = cmd; - return; - case 0x90: /* Read Device ID */ - DPRINTF("%s: Read Device information\n", __func__); - pfl->cmd = cmd; - return; - case 0x98: /* CFI query */ - DPRINTF("%s: CFI query\n", __func__); - break; - case 0xe8: /* Write to buffer */ - DPRINTF("%s: Write to buffer\n", __func__); - pfl->status |= 0x80; /* Ready! */ - break; - case 0xf0: /* Probe for AMD flash */ - DPRINTF("%s: Probe for AMD flash\n", __func__); - goto reset_flash; - case 0xff: /* Read array mode */ - DPRINTF("%s: Read array mode\n", __func__); - goto reset_flash; - default: - goto error_flash; - } - pfl->wcycle++; - pfl->cmd = cmd; - break; - case 1: - switch (pfl->cmd) { - case 0x10: /* Single Byte Program */ - case 0x40: /* Single Byte Program */ - DPRINTF("%s: Single Byte Program\n", __func__); - if (!pfl->ro) { - pflash_data_write(pfl, offset, value, width, be); - pflash_update(pfl, offset, width); - } else { - pfl->status |= 0x10; /* Programming error */ - } - pfl->status |= 0x80; /* Ready! */ - pfl->wcycle = 0; - break; - case 0x20: /* Block erase */ - case 0x28: - if (cmd == 0xd0) { /* confirm */ - pfl->wcycle = 0; - pfl->status |= 0x80; - } else if (cmd == 0xff) { /* read array mode */ - goto reset_flash; - } else - goto error_flash; - - break; - case 0xe8: - /* Mask writeblock size based on device width, or bank width if - * device width not specified. - */ - if (pfl->device_width) { - value = extract32(value, 0, pfl->device_width * 8); - } else { - value = extract32(value, 0, pfl->bank_width * 8); - } - DPRINTF("%s: block write of %x bytes\n", __func__, value); - pfl->counter = value; - pfl->wcycle++; - break; - case 0x60: - if (cmd == 0xd0) { - pfl->wcycle = 0; - pfl->status |= 0x80; - } else if (cmd == 0x01) { - pfl->wcycle = 0; - pfl->status |= 0x80; - } else if (cmd == 0xff) { - goto reset_flash; - } else { - DPRINTF("%s: Unknown (un)locking command\n", __func__); - goto reset_flash; - } - break; - case 0x98: - if (cmd == 0xff) { - goto reset_flash; - } else { - DPRINTF("%s: leaving query mode\n", __func__); - } - break; - default: - goto error_flash; - } - break; - case 2: - switch (pfl->cmd) { - case 0xe8: /* Block write */ - if (!pfl->ro) { - pflash_data_write(pfl, offset, value, width, be); - } else { - pfl->status |= 0x10; /* Programming error */ - } - - pfl->status |= 0x80; - - if (!pfl->counter) { - hwaddr mask = pfl->writeblock_size - 1; - mask = ~mask; - - DPRINTF("%s: block write finished\n", __func__); - pfl->wcycle++; - if (!pfl->ro) { - /* Flush the entire write buffer onto backing storage. */ - pflash_update(pfl, offset & mask, pfl->writeblock_size); - } else { - pfl->status |= 0x10; /* Programming error */ - } - } - - pfl->counter--; - break; - default: - goto error_flash; - } - break; - case 3: /* Confirm mode */ - switch (pfl->cmd) { - case 0xe8: /* Block write */ - if (cmd == 0xd0) { - pfl->wcycle = 0; - pfl->status |= 0x80; - } else { - DPRINTF("%s: unknown command for \"write block\"\n", __func__); - PFLASH_BUG("Write block confirm"); - goto reset_flash; - } - break; - default: - goto error_flash; - } - break; - default: - /* Should never happen */ - DPRINTF("%s: invalid write state\n", __func__); - goto reset_flash; - } - return; - - error_flash: - qemu_log_mask(LOG_UNIMP, "%s: Unimplemented flash cmd sequence " - "(offset " TARGET_FMT_plx ", wcycle 0x%x cmd 0x%x value 0x%x)" - "\n", __func__, offset, pfl->wcycle, pfl->cmd, value); - - reset_flash: - memory_region_rom_device_set_romd(&pfl->mem, true); - - pfl->wcycle = 0; - pfl->cmd = 0; -} - - -static MemTxResult pflash_mem_read_with_attrs(void *opaque, hwaddr addr, uint64_t *value, - unsigned len, MemTxAttrs attrs) -{ - pflash_t *pfl = opaque; - bool be = !!(pfl->features & (1 << PFLASH_BE)); - - if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) { - *value = pflash_data_read(opaque, addr, len, be); - } else { - *value = pflash_read(opaque, addr, len, be); - } - return MEMTX_OK; -} - -static MemTxResult pflash_mem_write_with_attrs(void *opaque, hwaddr addr, uint64_t value, - unsigned len, MemTxAttrs attrs) -{ - pflash_t *pfl = opaque; - bool be = !!(pfl->features & (1 << PFLASH_BE)); - - if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) { - return MEMTX_ERROR; - } else { - pflash_write(opaque, addr, value, len, be); - return MEMTX_OK; - } -} - -static const MemoryRegionOps pflash_cfi01_ops = { - .read_with_attrs = pflash_mem_read_with_attrs, - .write_with_attrs = pflash_mem_write_with_attrs, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pflash_cfi01_realize(DeviceState *dev, Error **errp) -{ - pflash_t *pfl = CFI_PFLASH01(dev); - uint64_t total_len; - int ret; - uint64_t blocks_per_device, device_len; - int num_devices; - Error *local_err = NULL; - - total_len = pfl->sector_len * pfl->nb_blocs; - - /* These are only used to expose the parameters of each device - * in the cfi_table[]. - */ - num_devices = pfl->device_width ? (pfl->bank_width / pfl->device_width) : 1; - blocks_per_device = pfl->nb_blocs / num_devices; - device_len = pfl->sector_len * blocks_per_device; - - /* XXX: to be fixed */ -#if 0 - if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) && - total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024)) - return NULL; -#endif - - memory_region_init_rom_device( - &pfl->mem, OBJECT(dev), - &pflash_cfi01_ops, - pfl, - pfl->name, total_len, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - vmstate_register_ram(&pfl->mem, DEVICE(pfl)); - pfl->storage = memory_region_get_ram_ptr(&pfl->mem); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem); - - if (pfl->blk) { - /* read the initial flash content */ - ret = blk_read(pfl->blk, 0, pfl->storage, total_len >> 9); - - if (ret < 0) { - vmstate_unregister_ram(&pfl->mem, DEVICE(pfl)); - error_setg(errp, "failed to read the initial flash content"); - return; - } - } - - if (pfl->blk) { - pfl->ro = blk_is_read_only(pfl->blk); - } else { - pfl->ro = 0; - } - - /* Default to devices being used at their maximum device width. This was - * assumed before the device_width support was added. - */ - if (!pfl->max_device_width) { - pfl->max_device_width = pfl->device_width; - } - - pfl->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pflash_timer, pfl); - pfl->wcycle = 0; - pfl->cmd = 0; - pfl->status = 0; - /* Hardcoded CFI table */ - pfl->cfi_len = 0x52; - /* Standard "QRY" string */ - pfl->cfi_table[0x10] = 'Q'; - pfl->cfi_table[0x11] = 'R'; - pfl->cfi_table[0x12] = 'Y'; - /* Command set (Intel) */ - pfl->cfi_table[0x13] = 0x01; - pfl->cfi_table[0x14] = 0x00; - /* Primary extended table address (none) */ - pfl->cfi_table[0x15] = 0x31; - pfl->cfi_table[0x16] = 0x00; - /* Alternate command set (none) */ - pfl->cfi_table[0x17] = 0x00; - pfl->cfi_table[0x18] = 0x00; - /* Alternate extended table (none) */ - pfl->cfi_table[0x19] = 0x00; - pfl->cfi_table[0x1A] = 0x00; - /* Vcc min */ - pfl->cfi_table[0x1B] = 0x45; - /* Vcc max */ - pfl->cfi_table[0x1C] = 0x55; - /* Vpp min (no Vpp pin) */ - pfl->cfi_table[0x1D] = 0x00; - /* Vpp max (no Vpp pin) */ - pfl->cfi_table[0x1E] = 0x00; - /* Reserved */ - pfl->cfi_table[0x1F] = 0x07; - /* Timeout for min size buffer write */ - pfl->cfi_table[0x20] = 0x07; - /* Typical timeout for block erase */ - pfl->cfi_table[0x21] = 0x0a; - /* Typical timeout for full chip erase (4096 ms) */ - pfl->cfi_table[0x22] = 0x00; - /* Reserved */ - pfl->cfi_table[0x23] = 0x04; - /* Max timeout for buffer write */ - pfl->cfi_table[0x24] = 0x04; - /* Max timeout for block erase */ - pfl->cfi_table[0x25] = 0x04; - /* Max timeout for chip erase */ - pfl->cfi_table[0x26] = 0x00; - /* Device size */ - pfl->cfi_table[0x27] = ctz32(device_len); /* + 1; */ - /* Flash device interface (8 & 16 bits) */ - pfl->cfi_table[0x28] = 0x02; - pfl->cfi_table[0x29] = 0x00; - /* Max number of bytes in multi-bytes write */ - if (pfl->bank_width == 1) { - pfl->cfi_table[0x2A] = 0x08; - } else { - pfl->cfi_table[0x2A] = 0x0B; - } - pfl->writeblock_size = 1 << pfl->cfi_table[0x2A]; - - pfl->cfi_table[0x2B] = 0x00; - /* Number of erase block regions (uniform) */ - pfl->cfi_table[0x2C] = 0x01; - /* Erase block region 1 */ - pfl->cfi_table[0x2D] = blocks_per_device - 1; - pfl->cfi_table[0x2E] = (blocks_per_device - 1) >> 8; - pfl->cfi_table[0x2F] = pfl->sector_len >> 8; - pfl->cfi_table[0x30] = pfl->sector_len >> 16; - - /* Extended */ - pfl->cfi_table[0x31] = 'P'; - pfl->cfi_table[0x32] = 'R'; - pfl->cfi_table[0x33] = 'I'; - - pfl->cfi_table[0x34] = '1'; - pfl->cfi_table[0x35] = '0'; - - pfl->cfi_table[0x36] = 0x00; - pfl->cfi_table[0x37] = 0x00; - pfl->cfi_table[0x38] = 0x00; - pfl->cfi_table[0x39] = 0x00; - - pfl->cfi_table[0x3a] = 0x00; - - pfl->cfi_table[0x3b] = 0x00; - pfl->cfi_table[0x3c] = 0x00; - - pfl->cfi_table[0x3f] = 0x01; /* Number of protection fields */ -} - -static Property pflash_cfi01_properties[] = { - DEFINE_PROP_DRIVE("drive", struct pflash_t, blk), - /* num-blocks is the number of blocks actually visible to the guest, - * ie the total size of the device divided by the sector length. - * If we're emulating flash devices wired in parallel the actual - * number of blocks per indvidual device will differ. - */ - DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0), - DEFINE_PROP_UINT64("sector-length", struct pflash_t, sector_len, 0), - /* width here is the overall width of this QEMU device in bytes. - * The QEMU device may be emulating a number of flash devices - * wired up in parallel; the width of each individual flash - * device should be specified via device-width. If the individual - * devices have a maximum width which is greater than the width - * they are being used for, this maximum width should be set via - * max-device-width (which otherwise defaults to device-width). - * So for instance a 32-bit wide QEMU flash device made from four - * 16-bit flash devices used in 8-bit wide mode would be configured - * with width = 4, device-width = 1, max-device-width = 2. - * - * If device-width is not specified we default to backwards - * compatible behaviour which is a bad emulation of two - * 16 bit devices making up a 32 bit wide QEMU device. This - * is deprecated for new uses of this device. - */ - DEFINE_PROP_UINT8("width", struct pflash_t, bank_width, 0), - DEFINE_PROP_UINT8("device-width", struct pflash_t, device_width, 0), - DEFINE_PROP_UINT8("max-device-width", struct pflash_t, max_device_width, 0), - DEFINE_PROP_BIT("big-endian", struct pflash_t, features, PFLASH_BE, 0), - DEFINE_PROP_BIT("secure", struct pflash_t, features, PFLASH_SECURE, 0), - DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0), - DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0), - DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0), - DEFINE_PROP_UINT16("id3", struct pflash_t, ident3, 0), - DEFINE_PROP_STRING("name", struct pflash_t, name), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pflash_cfi01_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = pflash_cfi01_realize; - dc->props = pflash_cfi01_properties; - dc->vmsd = &vmstate_pflash; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - - -static const TypeInfo pflash_cfi01_info = { - .name = TYPE_CFI_PFLASH01, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct pflash_t), - .class_init = pflash_cfi01_class_init, -}; - -static void pflash_cfi01_register_types(void) -{ - type_register_static(&pflash_cfi01_info); -} - -type_init(pflash_cfi01_register_types) - -pflash_t *pflash_cfi01_register(hwaddr base, - DeviceState *qdev, const char *name, - hwaddr size, - BlockBackend *blk, - uint32_t sector_len, int nb_blocs, - int bank_width, uint16_t id0, uint16_t id1, - uint16_t id2, uint16_t id3, int be) -{ - DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH01); - - if (blk) { - qdev_prop_set_drive(dev, "drive", blk, &error_abort); - } - qdev_prop_set_uint32(dev, "num-blocks", nb_blocs); - qdev_prop_set_uint64(dev, "sector-length", sector_len); - qdev_prop_set_uint8(dev, "width", bank_width); - qdev_prop_set_bit(dev, "big-endian", !!be); - qdev_prop_set_uint16(dev, "id0", id0); - qdev_prop_set_uint16(dev, "id1", id1); - qdev_prop_set_uint16(dev, "id2", id2); - qdev_prop_set_uint16(dev, "id3", id3); - qdev_prop_set_string(dev, "name", name); - qdev_init_nofail(dev); - - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - return CFI_PFLASH01(dev); -} - -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) { - 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 deleted file mode 100644 index b13172c6e..000000000 --- a/qemu/hw/block/pflash_cfi02.c +++ /dev/null @@ -1,797 +0,0 @@ -/* - * CFI parallel flash with AMD command set emulation - * - * Copyright (c) 2005 Jocelyn Mayer - * - * 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 . - */ - -/* - * For now, this code can emulate flashes of 1, 2 or 4 bytes width. - * Supported commands/modes are: - * - flash read - * - flash write - * - flash ID read - * - sector erase - * - chip erase - * - unlock bypass command - * - CFI queries - * - * It does not support flash interleaving. - * It does not implement boot blocs with reduced size - * It does not implement software data protection as found in many real chips - * It does not implement erase suspend/resume commands - * 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" -#include "qemu/host-utils.h" -#include "hw/sysbus.h" - -//#define PFLASH_DEBUG -#ifdef PFLASH_DEBUG -#define DPRINTF(fmt, ...) \ -do { \ - fprintf(stderr, "PFLASH: " fmt , ## __VA_ARGS__); \ -} while (0) -#else -#define DPRINTF(fmt, ...) do { } while (0) -#endif - -#define PFLASH_LAZY_ROMD_THRESHOLD 42 - -#define TYPE_CFI_PFLASH02 "cfi.pflash02" -#define CFI_PFLASH02(obj) OBJECT_CHECK(pflash_t, (obj), TYPE_CFI_PFLASH02) - -struct pflash_t { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - BlockBackend *blk; - uint32_t sector_len; - uint32_t nb_blocs; - uint32_t chip_len; - uint8_t mappings; - uint8_t width; - uint8_t be; - int wcycle; /* if 0, the flash is read normally */ - int bypass; - int ro; - uint8_t cmd; - uint8_t status; - /* FIXME: implement array device properties */ - uint16_t ident0; - uint16_t ident1; - uint16_t ident2; - uint16_t ident3; - uint16_t unlock_addr0; - uint16_t unlock_addr1; - uint8_t cfi_len; - uint8_t cfi_table[0x52]; - QEMUTimer *timer; - /* The device replicates the flash memory across its memory space. Emulate - * that by having a container (.mem) filled with an array of aliases - * (.mem_mappings) pointing to the flash memory (.orig_mem). - */ - MemoryRegion mem; - MemoryRegion *mem_mappings; /* array; one per mapping */ - MemoryRegion orig_mem; - int rom_mode; - int read_counter; /* used for lazy switch-back to rom mode */ - char *name; - void *storage; -}; - -/* - * Set up replicated mappings of the same region. - */ -static void pflash_setup_mappings(pflash_t *pfl) -{ - unsigned i; - hwaddr size = memory_region_size(&pfl->orig_mem); - - memory_region_init(&pfl->mem, OBJECT(pfl), "pflash", pfl->mappings * size); - pfl->mem_mappings = g_new(MemoryRegion, pfl->mappings); - for (i = 0; i < pfl->mappings; ++i) { - memory_region_init_alias(&pfl->mem_mappings[i], OBJECT(pfl), - "pflash-alias", &pfl->orig_mem, 0, size); - memory_region_add_subregion(&pfl->mem, i * size, &pfl->mem_mappings[i]); - } -} - -static void pflash_register_memory(pflash_t *pfl, int rom_mode) -{ - memory_region_rom_device_set_romd(&pfl->orig_mem, rom_mode); - pfl->rom_mode = rom_mode; -} - -static void pflash_timer (void *opaque) -{ - pflash_t *pfl = opaque; - - DPRINTF("%s: command %02x done\n", __func__, pfl->cmd); - /* Reset flash */ - pfl->status ^= 0x80; - if (pfl->bypass) { - pfl->wcycle = 2; - } else { - pflash_register_memory(pfl, 1); - pfl->wcycle = 0; - } - pfl->cmd = 0; -} - -static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, - int width, int be) -{ - hwaddr boff; - uint32_t ret; - uint8_t *p; - - DPRINTF("%s: offset " TARGET_FMT_plx "\n", __func__, offset); - ret = -1; - /* Lazy reset to ROMD mode after a certain amount of read accesses */ - if (!pfl->rom_mode && pfl->wcycle == 0 && - ++pfl->read_counter > PFLASH_LAZY_ROMD_THRESHOLD) { - pflash_register_memory(pfl, 1); - } - offset &= pfl->chip_len - 1; - boff = offset & 0xFF; - if (pfl->width == 2) - boff = boff >> 1; - else if (pfl->width == 4) - boff = boff >> 2; - switch (pfl->cmd) { - default: - /* This should never happen : reset state & treat it as a read*/ - DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd); - pfl->wcycle = 0; - pfl->cmd = 0; - /* fall through to the read code */ - case 0x80: - /* We accept reads during second unlock sequence... */ - case 0x00: - flash_read: - /* Flash area read */ - p = pfl->storage; - switch (width) { - case 1: - ret = p[offset]; -// DPRINTF("%s: data offset %08x %02x\n", __func__, offset, ret); - break; - case 2: - if (be) { - ret = p[offset] << 8; - ret |= p[offset + 1]; - } else { - ret = p[offset]; - ret |= p[offset + 1] << 8; - } -// DPRINTF("%s: data offset %08x %04x\n", __func__, offset, ret); - break; - case 4: - if (be) { - ret = p[offset] << 24; - ret |= p[offset + 1] << 16; - ret |= p[offset + 2] << 8; - ret |= p[offset + 3]; - } else { - ret = p[offset]; - ret |= p[offset + 1] << 8; - ret |= p[offset + 2] << 16; - ret |= p[offset + 3] << 24; - } -// DPRINTF("%s: data offset %08x %08x\n", __func__, offset, ret); - break; - } - break; - case 0x90: - /* flash ID read */ - switch (boff) { - case 0x00: - case 0x01: - ret = boff & 0x01 ? pfl->ident1 : pfl->ident0; - break; - case 0x02: - ret = 0x00; /* Pretend all sectors are unprotected */ - break; - case 0x0E: - case 0x0F: - ret = boff & 0x01 ? pfl->ident3 : pfl->ident2; - if (ret == (uint8_t)-1) { - goto flash_read; - } - break; - default: - goto flash_read; - } - DPRINTF("%s: ID " TARGET_FMT_plx " %x\n", __func__, boff, ret); - break; - case 0xA0: - case 0x10: - case 0x30: - /* Status register read */ - ret = pfl->status; - DPRINTF("%s: status %x\n", __func__, ret); - /* Toggle bit 6 */ - pfl->status ^= 0x40; - break; - case 0x98: - /* CFI query mode */ - if (boff > pfl->cfi_len) - ret = 0; - else - ret = pfl->cfi_table[boff]; - break; - } - - return ret; -} - -/* update flash content on disk */ -static void pflash_update(pflash_t *pfl, int offset, - int size) -{ - int offset_end; - if (pfl->blk) { - offset_end = offset + size; - /* round to sectors */ - offset = offset >> 9; - offset_end = (offset_end + 511) >> 9; - blk_write(pfl->blk, offset, pfl->storage + (offset << 9), - offset_end - offset); - } -} - -static void pflash_write (pflash_t *pfl, hwaddr offset, - uint32_t value, int width, int be) -{ - hwaddr boff; - uint8_t *p; - uint8_t cmd; - - cmd = value; - if (pfl->cmd != 0xA0 && cmd == 0xF0) { -#if 0 - DPRINTF("%s: flash reset asked (%02x %02x)\n", - __func__, pfl->cmd, cmd); -#endif - goto reset_flash; - } - DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d %d\n", __func__, - offset, value, width, pfl->wcycle); - offset &= pfl->chip_len - 1; - - DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d\n", __func__, - offset, value, width); - boff = offset & (pfl->sector_len - 1); - if (pfl->width == 2) - boff = boff >> 1; - else if (pfl->width == 4) - boff = boff >> 2; - switch (pfl->wcycle) { - case 0: - /* Set the device in I/O access mode if required */ - if (pfl->rom_mode) - pflash_register_memory(pfl, 0); - pfl->read_counter = 0; - /* We're in read mode */ - check_unlock0: - if (boff == 0x55 && cmd == 0x98) { - enter_CFI_mode: - /* Enter CFI query mode */ - pfl->wcycle = 7; - pfl->cmd = 0x98; - return; - } - if (boff != pfl->unlock_addr0 || cmd != 0xAA) { - DPRINTF("%s: unlock0 failed " TARGET_FMT_plx " %02x %04x\n", - __func__, boff, cmd, pfl->unlock_addr0); - goto reset_flash; - } - DPRINTF("%s: unlock sequence started\n", __func__); - break; - case 1: - /* We started an unlock sequence */ - check_unlock1: - if (boff != pfl->unlock_addr1 || cmd != 0x55) { - DPRINTF("%s: unlock1 failed " TARGET_FMT_plx " %02x\n", __func__, - boff, cmd); - goto reset_flash; - } - DPRINTF("%s: unlock sequence done\n", __func__); - break; - case 2: - /* We finished an unlock sequence */ - if (!pfl->bypass && boff != pfl->unlock_addr0) { - DPRINTF("%s: command failed " TARGET_FMT_plx " %02x\n", __func__, - boff, cmd); - goto reset_flash; - } - switch (cmd) { - case 0x20: - pfl->bypass = 1; - goto do_bypass; - case 0x80: - case 0x90: - case 0xA0: - pfl->cmd = cmd; - DPRINTF("%s: starting command %02x\n", __func__, cmd); - break; - default: - DPRINTF("%s: unknown command %02x\n", __func__, cmd); - goto reset_flash; - } - break; - case 3: - switch (pfl->cmd) { - case 0x80: - /* We need another unlock sequence */ - goto check_unlock0; - case 0xA0: - DPRINTF("%s: write data offset " TARGET_FMT_plx " %08x %d\n", - __func__, offset, value, width); - p = pfl->storage; - if (!pfl->ro) { - switch (width) { - case 1: - p[offset] &= value; - pflash_update(pfl, offset, 1); - break; - case 2: - if (be) { - p[offset] &= value >> 8; - p[offset + 1] &= value; - } else { - p[offset] &= value; - p[offset + 1] &= value >> 8; - } - pflash_update(pfl, offset, 2); - break; - case 4: - if (be) { - p[offset] &= value >> 24; - p[offset + 1] &= value >> 16; - p[offset + 2] &= value >> 8; - p[offset + 3] &= value; - } else { - p[offset] &= value; - p[offset + 1] &= value >> 8; - p[offset + 2] &= value >> 16; - p[offset + 3] &= value >> 24; - } - pflash_update(pfl, offset, 4); - break; - } - } - pfl->status = 0x00 | ~(value & 0x80); - /* Let's pretend write is immediate */ - if (pfl->bypass) - goto do_bypass; - goto reset_flash; - case 0x90: - if (pfl->bypass && cmd == 0x00) { - /* Unlock bypass reset */ - goto reset_flash; - } - /* We can enter CFI query mode from autoselect mode */ - if (boff == 0x55 && cmd == 0x98) - goto enter_CFI_mode; - /* No break here */ - default: - DPRINTF("%s: invalid write for command %02x\n", - __func__, pfl->cmd); - goto reset_flash; - } - case 4: - switch (pfl->cmd) { - case 0xA0: - /* Ignore writes while flash data write is occurring */ - /* As we suppose write is immediate, this should never happen */ - return; - case 0x80: - goto check_unlock1; - default: - /* Should never happen */ - DPRINTF("%s: invalid command state %02x (wc 4)\n", - __func__, pfl->cmd); - goto reset_flash; - } - break; - case 5: - switch (cmd) { - case 0x10: - if (boff != pfl->unlock_addr0) { - DPRINTF("%s: chip erase: invalid address " TARGET_FMT_plx "\n", - __func__, offset); - goto reset_flash; - } - /* Chip erase */ - DPRINTF("%s: start chip erase\n", __func__); - if (!pfl->ro) { - memset(pfl->storage, 0xFF, pfl->chip_len); - pflash_update(pfl, 0, pfl->chip_len); - } - pfl->status = 0x00; - /* Let's wait 5 seconds before chip erase is done */ - timer_mod(pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND * 5)); - break; - case 0x30: - /* Sector erase */ - p = pfl->storage; - offset &= ~(pfl->sector_len - 1); - DPRINTF("%s: start sector erase at " TARGET_FMT_plx "\n", __func__, - offset); - if (!pfl->ro) { - memset(p + offset, 0xFF, pfl->sector_len); - pflash_update(pfl, offset, pfl->sector_len); - } - 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) + - (NANOSECONDS_PER_SECOND / 2)); - break; - default: - DPRINTF("%s: invalid command %02x (wc 5)\n", __func__, cmd); - goto reset_flash; - } - pfl->cmd = cmd; - break; - case 6: - switch (pfl->cmd) { - case 0x10: - /* Ignore writes during chip erase */ - return; - case 0x30: - /* Ignore writes during sector erase */ - return; - default: - /* Should never happen */ - DPRINTF("%s: invalid command state %02x (wc 6)\n", - __func__, pfl->cmd); - goto reset_flash; - } - break; - case 7: /* Special value for CFI queries */ - DPRINTF("%s: invalid write in CFI query mode\n", __func__); - goto reset_flash; - default: - /* Should never happen */ - DPRINTF("%s: invalid write state (wc 7)\n", __func__); - goto reset_flash; - } - pfl->wcycle++; - - return; - - /* Reset flash */ - reset_flash: - pfl->bypass = 0; - pfl->wcycle = 0; - pfl->cmd = 0; - return; - - do_bypass: - pfl->wcycle = 2; - pfl->cmd = 0; -} - - -static uint32_t pflash_readb_be(void *opaque, hwaddr addr) -{ - return pflash_read(opaque, addr, 1, 1); -} - -static uint32_t pflash_readb_le(void *opaque, hwaddr addr) -{ - return pflash_read(opaque, addr, 1, 0); -} - -static uint32_t pflash_readw_be(void *opaque, hwaddr addr) -{ - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 2, 1); -} - -static uint32_t pflash_readw_le(void *opaque, hwaddr addr) -{ - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 2, 0); -} - -static uint32_t pflash_readl_be(void *opaque, hwaddr addr) -{ - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 4, 1); -} - -static uint32_t pflash_readl_le(void *opaque, hwaddr addr) -{ - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 4, 0); -} - -static void pflash_writeb_be(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_write(opaque, addr, value, 1, 1); -} - -static void pflash_writeb_le(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_write(opaque, addr, value, 1, 0); -} - -static void pflash_writew_be(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 2, 1); -} - -static void pflash_writew_le(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 2, 0); -} - -static void pflash_writel_be(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 4, 1); -} - -static void pflash_writel_le(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 4, 0); -} - -static const MemoryRegionOps pflash_cfi02_ops_be = { - .old_mmio = { - .read = { pflash_readb_be, pflash_readw_be, pflash_readl_be, }, - .write = { pflash_writeb_be, pflash_writew_be, pflash_writel_be, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const MemoryRegionOps pflash_cfi02_ops_le = { - .old_mmio = { - .read = { pflash_readb_le, pflash_readw_le, pflash_readl_le, }, - .write = { pflash_writeb_le, pflash_writew_le, pflash_writel_le, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pflash_cfi02_realize(DeviceState *dev, Error **errp) -{ - pflash_t *pfl = CFI_PFLASH02(dev); - uint32_t chip_len; - int ret; - Error *local_err = NULL; - - chip_len = pfl->sector_len * pfl->nb_blocs; - /* XXX: to be fixed */ -#if 0 - if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) && - total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024)) - return NULL; -#endif - - memory_region_init_rom_device(&pfl->orig_mem, OBJECT(pfl), pfl->be ? - &pflash_cfi02_ops_be : &pflash_cfi02_ops_le, - pfl, pfl->name, chip_len, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - vmstate_register_ram(&pfl->orig_mem, DEVICE(pfl)); - pfl->storage = memory_region_get_ram_ptr(&pfl->orig_mem); - pfl->chip_len = chip_len; - if (pfl->blk) { - /* read the initial flash content */ - ret = blk_read(pfl->blk, 0, pfl->storage, chip_len >> 9); - if (ret < 0) { - vmstate_unregister_ram(&pfl->orig_mem, DEVICE(pfl)); - error_setg(errp, "failed to read the initial flash content"); - return; - } - } - - pflash_setup_mappings(pfl); - pfl->rom_mode = 1; - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem); - - if (pfl->blk) { - pfl->ro = blk_is_read_only(pfl->blk); - } else { - pfl->ro = 0; - } - - pfl->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pflash_timer, pfl); - pfl->wcycle = 0; - pfl->cmd = 0; - pfl->status = 0; - /* Hardcoded CFI table (mostly from SG29 Spansion flash) */ - pfl->cfi_len = 0x52; - /* Standard "QRY" string */ - pfl->cfi_table[0x10] = 'Q'; - pfl->cfi_table[0x11] = 'R'; - pfl->cfi_table[0x12] = 'Y'; - /* Command set (AMD/Fujitsu) */ - pfl->cfi_table[0x13] = 0x02; - pfl->cfi_table[0x14] = 0x00; - /* Primary extended table address */ - pfl->cfi_table[0x15] = 0x31; - pfl->cfi_table[0x16] = 0x00; - /* Alternate command set (none) */ - pfl->cfi_table[0x17] = 0x00; - pfl->cfi_table[0x18] = 0x00; - /* Alternate extended table (none) */ - pfl->cfi_table[0x19] = 0x00; - pfl->cfi_table[0x1A] = 0x00; - /* Vcc min */ - pfl->cfi_table[0x1B] = 0x27; - /* Vcc max */ - pfl->cfi_table[0x1C] = 0x36; - /* Vpp min (no Vpp pin) */ - pfl->cfi_table[0x1D] = 0x00; - /* Vpp max (no Vpp pin) */ - pfl->cfi_table[0x1E] = 0x00; - /* Reserved */ - pfl->cfi_table[0x1F] = 0x07; - /* Timeout for min size buffer write (NA) */ - pfl->cfi_table[0x20] = 0x00; - /* Typical timeout for block erase (512 ms) */ - pfl->cfi_table[0x21] = 0x09; - /* Typical timeout for full chip erase (4096 ms) */ - pfl->cfi_table[0x22] = 0x0C; - /* Reserved */ - pfl->cfi_table[0x23] = 0x01; - /* Max timeout for buffer write (NA) */ - pfl->cfi_table[0x24] = 0x00; - /* Max timeout for block erase */ - pfl->cfi_table[0x25] = 0x0A; - /* Max timeout for chip erase */ - pfl->cfi_table[0x26] = 0x0D; - /* Device size */ - pfl->cfi_table[0x27] = ctz32(chip_len); - /* Flash device interface (8 & 16 bits) */ - pfl->cfi_table[0x28] = 0x02; - pfl->cfi_table[0x29] = 0x00; - /* Max number of bytes in multi-bytes write */ - /* XXX: disable buffered write as it's not supported */ - // pfl->cfi_table[0x2A] = 0x05; - pfl->cfi_table[0x2A] = 0x00; - pfl->cfi_table[0x2B] = 0x00; - /* Number of erase block regions (uniform) */ - pfl->cfi_table[0x2C] = 0x01; - /* Erase block region 1 */ - pfl->cfi_table[0x2D] = pfl->nb_blocs - 1; - pfl->cfi_table[0x2E] = (pfl->nb_blocs - 1) >> 8; - pfl->cfi_table[0x2F] = pfl->sector_len >> 8; - pfl->cfi_table[0x30] = pfl->sector_len >> 16; - - /* Extended */ - pfl->cfi_table[0x31] = 'P'; - pfl->cfi_table[0x32] = 'R'; - pfl->cfi_table[0x33] = 'I'; - - pfl->cfi_table[0x34] = '1'; - pfl->cfi_table[0x35] = '0'; - - pfl->cfi_table[0x36] = 0x00; - pfl->cfi_table[0x37] = 0x00; - pfl->cfi_table[0x38] = 0x00; - pfl->cfi_table[0x39] = 0x00; - - pfl->cfi_table[0x3a] = 0x00; - - pfl->cfi_table[0x3b] = 0x00; - pfl->cfi_table[0x3c] = 0x00; -} - -static Property pflash_cfi02_properties[] = { - DEFINE_PROP_DRIVE("drive", struct pflash_t, blk), - DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0), - DEFINE_PROP_UINT32("sector-length", struct pflash_t, sector_len, 0), - DEFINE_PROP_UINT8("width", struct pflash_t, width, 0), - DEFINE_PROP_UINT8("mappings", struct pflash_t, mappings, 0), - DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0), - DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0), - DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0), - DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0), - DEFINE_PROP_UINT16("id3", struct pflash_t, ident3, 0), - DEFINE_PROP_UINT16("unlock-addr0", struct pflash_t, unlock_addr0, 0), - DEFINE_PROP_UINT16("unlock-addr1", struct pflash_t, unlock_addr1, 0), - DEFINE_PROP_STRING("name", struct pflash_t, name), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pflash_cfi02_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = pflash_cfi02_realize; - dc->props = pflash_cfi02_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo pflash_cfi02_info = { - .name = TYPE_CFI_PFLASH02, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct pflash_t), - .class_init = pflash_cfi02_class_init, -}; - -static void pflash_cfi02_register_types(void) -{ - type_register_static(&pflash_cfi02_info); -} - -type_init(pflash_cfi02_register_types) - -pflash_t *pflash_cfi02_register(hwaddr base, - DeviceState *qdev, const char *name, - hwaddr size, - BlockBackend *blk, uint32_t sector_len, - int nb_blocs, int nb_mappings, int width, - uint16_t id0, uint16_t id1, - uint16_t id2, uint16_t id3, - uint16_t unlock_addr0, uint16_t unlock_addr1, - int be) -{ - DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH02); - - if (blk) { - qdev_prop_set_drive(dev, "drive", blk, &error_abort); - } - qdev_prop_set_uint32(dev, "num-blocks", nb_blocs); - qdev_prop_set_uint32(dev, "sector-length", sector_len); - qdev_prop_set_uint8(dev, "width", width); - qdev_prop_set_uint8(dev, "mappings", nb_mappings); - qdev_prop_set_uint8(dev, "big-endian", !!be); - qdev_prop_set_uint16(dev, "id0", id0); - qdev_prop_set_uint16(dev, "id1", id1); - qdev_prop_set_uint16(dev, "id2", id2); - qdev_prop_set_uint16(dev, "id3", id3); - qdev_prop_set_uint16(dev, "unlock-addr0", unlock_addr0); - qdev_prop_set_uint16(dev, "unlock-addr1", unlock_addr1); - qdev_prop_set_string(dev, "name", name); - qdev_init_nofail(dev); - - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - return CFI_PFLASH02(dev); -} diff --git a/qemu/hw/block/tc58128.c b/qemu/hw/block/tc58128.c deleted file mode 100644 index 7909d5041..000000000 --- a/qemu/hw/block/tc58128.c +++ /dev/null @@ -1,181 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sh4/sh.h" -#include "hw/loader.h" -#include "sysemu/qtest.h" -#include "qemu/error-report.h" - -#define CE1 0x0100 -#define CE2 0x0200 -#define RE 0x0400 -#define WE 0x0800 -#define ALE 0x1000 -#define CLE 0x2000 -#define RDY1 0x4000 -#define RDY2 0x8000 -#define RDY(n) ((n) == 0 ? RDY1 : RDY2) - -typedef enum { WAIT, READ1, READ2, READ3 } state_t; - -typedef struct { - uint8_t *flash_contents; - state_t state; - uint32_t address; - uint8_t address_cycle; -} tc58128_dev; - -static tc58128_dev tc58128_devs[2]; - -#define FLASH_SIZE (16*1024*1024) - -static void init_dev(tc58128_dev * dev, const char *filename) -{ - int ret, blocks; - - dev->state = WAIT; - dev->flash_contents = g_malloc(FLASH_SIZE); - memset(dev->flash_contents, 0xff, FLASH_SIZE); - if (filename) { - /* Load flash image skipping the first block */ - ret = load_image(filename, dev->flash_contents + 528 * 32); - if (ret < 0) { - if (!qtest_enabled()) { - error_report("Could not load flash image %s", filename); - exit(1); - } - } else { - /* Build first block with number of blocks */ - blocks = (ret + 528 * 32 - 1) / (528 * 32); - dev->flash_contents[0] = blocks & 0xff; - dev->flash_contents[1] = (blocks >> 8) & 0xff; - dev->flash_contents[2] = (blocks >> 16) & 0xff; - dev->flash_contents[3] = (blocks >> 24) & 0xff; - fprintf(stderr, "loaded %d bytes for %s into flash\n", ret, - filename); - } - } -} - -static void handle_command(tc58128_dev * dev, uint8_t command) -{ - switch (command) { - case 0xff: - fprintf(stderr, "reset flash device\n"); - dev->state = WAIT; - break; - case 0x00: - fprintf(stderr, "read mode 1\n"); - dev->state = READ1; - dev->address_cycle = 0; - break; - case 0x01: - fprintf(stderr, "read mode 2\n"); - dev->state = READ2; - dev->address_cycle = 0; - break; - case 0x50: - fprintf(stderr, "read mode 3\n"); - dev->state = READ3; - dev->address_cycle = 0; - break; - default: - fprintf(stderr, "unknown flash command 0x%02x\n", command); - abort(); - } -} - -static void handle_address(tc58128_dev * dev, uint8_t data) -{ - switch (dev->state) { - case READ1: - case READ2: - case READ3: - switch (dev->address_cycle) { - case 0: - dev->address = data; - if (dev->state == READ2) - dev->address |= 0x100; - else if (dev->state == READ3) - dev->address |= 0x200; - break; - case 1: - dev->address += data * 528 * 0x100; - break; - case 2: - dev->address += data * 528; - fprintf(stderr, "address pointer in flash: 0x%08x\n", - dev->address); - break; - default: - /* Invalid data */ - abort(); - } - dev->address_cycle++; - break; - default: - abort(); - } -} - -static uint8_t handle_read(tc58128_dev * dev) -{ -#if 0 - if (dev->address % 0x100000 == 0) - fprintf(stderr, "reading flash at address 0x%08x\n", dev->address); -#endif - return dev->flash_contents[dev->address++]; -} - -/* We never mark the device as busy, so interrupts cannot be triggered - XXXXX */ - -static int tc58128_cb(uint16_t porta, uint16_t portb, - uint16_t * periph_pdtra, uint16_t * periph_portadir, - uint16_t * periph_pdtrb, uint16_t * periph_portbdir) -{ - int dev; - - if ((porta & CE1) == 0) - dev = 0; - else if ((porta & CE2) == 0) - dev = 1; - else - return 0; /* No device selected */ - - if ((porta & RE) && (porta & WE)) { - /* Nothing to do, assert ready and return to input state */ - *periph_portadir &= 0xff00; - *periph_portadir |= RDY(dev); - *periph_pdtra |= RDY(dev); - return 1; - } - - if (porta & CLE) { - /* Command */ - assert((porta & WE) == 0); - handle_command(&tc58128_devs[dev], porta & 0x00ff); - } else if (porta & ALE) { - assert((porta & WE) == 0); - handle_address(&tc58128_devs[dev], porta & 0x00ff); - } else if ((porta & RE) == 0) { - *periph_portadir |= 0x00ff; - *periph_pdtra &= 0xff00; - *periph_pdtra |= handle_read(&tc58128_devs[dev]); - } else { - abort(); - } - return 1; -} - -static sh7750_io_device tc58128 = { - RE | WE, /* Port A triggers */ - 0, /* Port B triggers */ - tc58128_cb /* Callback */ -}; - -int tc58128_init(struct SH7750State *s, const char *zone1, const char *zone2) -{ - init_dev(&tc58128_devs[0], zone1); - init_dev(&tc58128_devs[1], zone2); - return sh7750_register_io_device(s, &tc58128); -} diff --git a/qemu/hw/block/virtio-blk.c b/qemu/hw/block/virtio-blk.c deleted file mode 100644 index 3f88f8cf5..000000000 --- a/qemu/hw/block/virtio-blk.c +++ /dev/null @@ -1,980 +0,0 @@ -/* - * Virtio Block Device - * - * Copyright IBM, Corp. 2007 - * - * Authors: - * Anthony Liguori - * - * 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 "qapi/error.h" -#include "qemu-common.h" -#include "qemu/iov.h" -#include "qemu/error-report.h" -#include "trace.h" -#include "hw/block/block.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "hw/virtio/virtio-blk.h" -#include "dataplane/virtio-blk.h" -#include "block/scsi.h" -#ifdef __linux__ -# include -#endif -#include "hw/virtio/virtio-bus.h" -#include "hw/virtio/virtio-access.h" - -void virtio_blk_init_request(VirtIOBlock *s, VirtIOBlockReq *req) -{ - req->dev = s; - req->qiov.size = 0; - req->in_len = 0; - req->next = NULL; - req->mr_next = NULL; -} - -void virtio_blk_free_request(VirtIOBlockReq *req) -{ - if (req) { - g_free(req); - } -} - -static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) -{ - VirtIOBlock *s = req->dev; - VirtIODevice *vdev = VIRTIO_DEVICE(s); - - trace_virtio_blk_req_complete(req, status); - - stb_p(&req->in->status, status); - virtqueue_push(s->vq, &req->elem, req->in_len); - 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, - bool is_read) -{ - BlockErrorAction action = blk_get_error_action(req->dev->blk, - is_read, 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_failed(blk_get_stats(s->blk), &req->acct); - virtio_blk_free_request(req); - } - - blk_error_action(s->blk, action, is_read, error); - return action != BLOCK_ERROR_ACTION_IGNORE; -} - -static void virtio_blk_rw_complete(void *opaque, int ret) -{ - VirtIOBlockReq *next = opaque; - - while (next) { - VirtIOBlockReq *req = next; - next = req->mr_next; - trace_virtio_blk_rw_complete(req, ret); - - if (req->qiov.nalloc != -1) { - /* If nalloc is != 1 req->qiov is a local copy of the original - * external iovec. It was allocated in submit_merged_requests - * to be able to merge requests. */ - qemu_iovec_destroy(&req->qiov); - } - - if (ret) { - int p = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); - bool is_read = !(p & VIRTIO_BLK_T_OUT); - /* Note that memory may be dirtied on read failure. If the - * virtio request is not completed here, as is the case for - * BLOCK_ERROR_ACTION_STOP, the memory may not be copied - * correctly during live migration. While this is ugly, - * it is acceptable because the device is free to write to - * the memory until the request is completed (which will - * happen on the other side of the migration). - */ - if (virtio_blk_handle_rw_error(req, -ret, is_read)) { - continue; - } - } - - virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); - block_acct_done(blk_get_stats(req->dev->blk), &req->acct); - virtio_blk_free_request(req); - } -} - -static void virtio_blk_flush_complete(void *opaque, int ret) -{ - VirtIOBlockReq *req = opaque; - - if (ret) { - if (virtio_blk_handle_rw_error(req, -ret, 0)) { - return; - } - } - - virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); - block_acct_done(blk_get_stats(req->dev->blk), &req->acct); - virtio_blk_free_request(req); -} - -#ifdef __linux__ - -typedef struct { - VirtIOBlockReq *req; - struct sg_io_hdr hdr; -} VirtIOBlockIoctlReq; - -static void virtio_blk_ioctl_complete(void *opaque, int status) -{ - VirtIOBlockIoctlReq *ioctl_req = opaque; - VirtIOBlockReq *req = ioctl_req->req; - VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); - struct virtio_scsi_inhdr *scsi; - struct sg_io_hdr *hdr; - - scsi = (void *)req->elem.in_sg[req->elem.in_num - 2].iov_base; - - if (status) { - status = VIRTIO_BLK_S_UNSUPP; - virtio_stl_p(vdev, &scsi->errors, 255); - goto out; - } - - hdr = &ioctl_req->hdr; - /* - * From SCSI-Generic-HOWTO: "Some lower level drivers (e.g. ide-scsi) - * clear the masked_status field [hence status gets cleared too, see - * block/scsi_ioctl.c] even when a CHECK_CONDITION or COMMAND_TERMINATED - * status has occurred. However they do set DRIVER_SENSE in driver_status - * field. Also a (sb_len_wr > 0) indicates there is a sense buffer. - */ - if (hdr->status == 0 && hdr->sb_len_wr > 0) { - hdr->status = CHECK_CONDITION; - } - - virtio_stl_p(vdev, &scsi->errors, - hdr->status | (hdr->msg_status << 8) | - (hdr->host_status << 16) | (hdr->driver_status << 24)); - virtio_stl_p(vdev, &scsi->residual, hdr->resid); - virtio_stl_p(vdev, &scsi->sense_len, hdr->sb_len_wr); - virtio_stl_p(vdev, &scsi->data_len, hdr->dxfer_len); - -out: - virtio_blk_req_complete(req, status); - virtio_blk_free_request(req); - g_free(ioctl_req); -} - -#endif - -static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s) -{ - VirtIOBlockReq *req = virtqueue_pop(s->vq, sizeof(VirtIOBlockReq)); - - if (req) { - virtio_blk_init_request(s, req); - } - return req; -} - -static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req) -{ - int status = VIRTIO_BLK_S_OK; - struct virtio_scsi_inhdr *scsi = NULL; - VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); - VirtQueueElement *elem = &req->elem; - VirtIOBlock *blk = req->dev; - -#ifdef __linux__ - int i; - VirtIOBlockIoctlReq *ioctl_req; - BlockAIOCB *acb; -#endif - - /* - * We require at least one output segment each for the virtio_blk_outhdr - * and the SCSI command block. - * - * We also at least require the virtio_blk_inhdr, the virtio_scsi_inhdr - * and the sense buffer pointer in the input segments. - */ - if (elem->out_num < 2 || elem->in_num < 3) { - status = VIRTIO_BLK_S_IOERR; - goto fail; - } - - /* - * The scsi inhdr is placed in the second-to-last input segment, just - * before the regular inhdr. - */ - scsi = (void *)elem->in_sg[elem->in_num - 2].iov_base; - - if (!blk->conf.scsi) { - status = VIRTIO_BLK_S_UNSUPP; - goto fail; - } - - /* - * No support for bidirection commands yet. - */ - if (elem->out_num > 2 && elem->in_num > 3) { - status = VIRTIO_BLK_S_UNSUPP; - goto fail; - } - -#ifdef __linux__ - ioctl_req = g_new0(VirtIOBlockIoctlReq, 1); - ioctl_req->req = req; - ioctl_req->hdr.interface_id = 'S'; - ioctl_req->hdr.cmd_len = elem->out_sg[1].iov_len; - ioctl_req->hdr.cmdp = elem->out_sg[1].iov_base; - ioctl_req->hdr.dxfer_len = 0; - - if (elem->out_num > 2) { - /* - * If there are more than the minimally required 2 output segments - * there is write payload starting from the third iovec. - */ - ioctl_req->hdr.dxfer_direction = SG_DXFER_TO_DEV; - ioctl_req->hdr.iovec_count = elem->out_num - 2; - - for (i = 0; i < ioctl_req->hdr.iovec_count; i++) { - ioctl_req->hdr.dxfer_len += elem->out_sg[i + 2].iov_len; - } - - ioctl_req->hdr.dxferp = elem->out_sg + 2; - - } else if (elem->in_num > 3) { - /* - * If we have more than 3 input segments the guest wants to actually - * read data. - */ - ioctl_req->hdr.dxfer_direction = SG_DXFER_FROM_DEV; - ioctl_req->hdr.iovec_count = elem->in_num - 3; - for (i = 0; i < ioctl_req->hdr.iovec_count; i++) { - ioctl_req->hdr.dxfer_len += elem->in_sg[i].iov_len; - } - - ioctl_req->hdr.dxferp = elem->in_sg; - } else { - /* - * Some SCSI commands don't actually transfer any data. - */ - ioctl_req->hdr.dxfer_direction = SG_DXFER_NONE; - } - - ioctl_req->hdr.sbp = elem->in_sg[elem->in_num - 3].iov_base; - ioctl_req->hdr.mx_sb_len = elem->in_sg[elem->in_num - 3].iov_len; - - acb = blk_aio_ioctl(blk->blk, SG_IO, &ioctl_req->hdr, - virtio_blk_ioctl_complete, ioctl_req); - if (!acb) { - g_free(ioctl_req); - status = VIRTIO_BLK_S_UNSUPP; - goto fail; - } - return -EINPROGRESS; -#else - abort(); -#endif - -fail: - /* Just put anything nonzero so that the ioctl fails in the guest. */ - if (scsi) { - virtio_stl_p(vdev, &scsi->errors, 255); - } - return status; -} - -static void virtio_blk_handle_scsi(VirtIOBlockReq *req) -{ - int status; - - status = virtio_blk_handle_scsi_req(req); - if (status != -EINPROGRESS) { - virtio_blk_req_complete(req, status); - virtio_blk_free_request(req); - } -} - -static inline void submit_requests(BlockBackend *blk, MultiReqBuffer *mrb, - int start, int num_reqs, int niov) -{ - QEMUIOVector *qiov = &mrb->reqs[start]->qiov; - int64_t sector_num = mrb->reqs[start]->sector_num; - int nb_sectors = mrb->reqs[start]->qiov.size / BDRV_SECTOR_SIZE; - bool is_write = mrb->is_write; - - if (num_reqs > 1) { - int i; - struct iovec *tmp_iov = qiov->iov; - int tmp_niov = qiov->niov; - - /* mrb->reqs[start]->qiov was initialized from external so we can't - * modifiy it here. We need to initialize it locally and then add the - * external iovecs. */ - qemu_iovec_init(qiov, niov); - - for (i = 0; i < tmp_niov; i++) { - qemu_iovec_add(qiov, tmp_iov[i].iov_base, tmp_iov[i].iov_len); - } - - for (i = start + 1; i < start + num_reqs; i++) { - qemu_iovec_concat(qiov, &mrb->reqs[i]->qiov, 0, - mrb->reqs[i]->qiov.size); - mrb->reqs[i - 1]->mr_next = mrb->reqs[i]; - nb_sectors += mrb->reqs[i]->qiov.size / BDRV_SECTOR_SIZE; - } - assert(nb_sectors == qiov->size / BDRV_SECTOR_SIZE); - - trace_virtio_blk_submit_multireq(mrb, start, num_reqs, sector_num, - nb_sectors, is_write); - block_acct_merge_done(blk_get_stats(blk), - is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ, - num_reqs - 1); - } - - if (is_write) { - blk_aio_writev(blk, sector_num, qiov, nb_sectors, - virtio_blk_rw_complete, mrb->reqs[start]); - } else { - blk_aio_readv(blk, sector_num, qiov, nb_sectors, - virtio_blk_rw_complete, mrb->reqs[start]); - } -} - -static int multireq_compare(const void *a, const void *b) -{ - const VirtIOBlockReq *req1 = *(VirtIOBlockReq **)a, - *req2 = *(VirtIOBlockReq **)b; - - /* - * Note that we can't simply subtract sector_num1 from sector_num2 - * here as that could overflow the return value. - */ - if (req1->sector_num > req2->sector_num) { - return 1; - } else if (req1->sector_num < req2->sector_num) { - return -1; - } else { - return 0; - } -} - -void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb) -{ - int i = 0, start = 0, num_reqs = 0, niov = 0, nb_sectors = 0; - int max_xfer_len = 0; - int64_t sector_num = 0; - - if (mrb->num_reqs == 1) { - submit_requests(blk, mrb, 0, 1, -1); - mrb->num_reqs = 0; - return; - } - - max_xfer_len = blk_get_max_transfer_length(mrb->reqs[0]->dev->blk); - max_xfer_len = MIN_NON_ZERO(max_xfer_len, BDRV_REQUEST_MAX_SECTORS); - - qsort(mrb->reqs, mrb->num_reqs, sizeof(*mrb->reqs), - &multireq_compare); - - for (i = 0; i < mrb->num_reqs; i++) { - VirtIOBlockReq *req = mrb->reqs[i]; - if (num_reqs > 0) { - /* - * 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; - } - } - - if (num_reqs == 0) { - sector_num = req->sector_num; - nb_sectors = niov = 0; - start = i; - } - - nb_sectors += req->qiov.size / BDRV_SECTOR_SIZE; - niov += req->qiov.niov; - num_reqs++; - } - - submit_requests(blk, mrb, start, num_reqs, niov); - mrb->num_reqs = 0; -} - -static void virtio_blk_handle_flush(VirtIOBlockReq *req, MultiReqBuffer *mrb) -{ - block_acct_start(blk_get_stats(req->dev->blk), &req->acct, 0, - BLOCK_ACCT_FLUSH); - - /* - * Make sure all outstanding writes are posted to the backing device. - */ - if (mrb->is_write && mrb->num_reqs > 0) { - virtio_blk_submit_multireq(req->dev->blk, mrb); - } - blk_aio_flush(req->dev->blk, virtio_blk_flush_complete, req); -} - -static bool virtio_blk_sect_range_ok(VirtIOBlock *dev, - uint64_t sector, size_t size) -{ - uint64_t nb_sectors = size >> BDRV_SECTOR_BITS; - uint64_t total_sectors; - - if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) { - return false; - } - if (sector & dev->sector_mask) { - return false; - } - if (size % dev->conf.conf.logical_block_size) { - return false; - } - blk_get_geometry(dev->blk, &total_sectors); - if (sector > total_sectors || nb_sectors > total_sectors - sector) { - return false; - } - return true; -} - -void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) -{ - uint32_t type; - struct iovec *in_iov = req->elem.in_sg; - struct iovec *iov = req->elem.out_sg; - unsigned in_num = req->elem.in_num; - unsigned out_num = req->elem.out_num; - - if (req->elem.out_num < 1 || req->elem.in_num < 1) { - error_report("virtio-blk missing headers"); - exit(1); - } - - if (unlikely(iov_to_buf(iov, out_num, 0, &req->out, - sizeof(req->out)) != sizeof(req->out))) { - error_report("virtio-blk request outhdr too short"); - exit(1); - } - - iov_discard_front(&iov, &out_num, sizeof(req->out)); - - if (in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) { - error_report("virtio-blk request inhdr too short"); - exit(1); - } - - /* We always touch the last byte, so just see how big in_iov is. */ - req->in_len = iov_size(in_iov, in_num); - req->in = (void *)in_iov[in_num - 1].iov_base - + in_iov[in_num - 1].iov_len - - sizeof(struct virtio_blk_inhdr); - iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr)); - - type = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); - - /* VIRTIO_BLK_T_OUT defines the command direction. VIRTIO_BLK_T_BARRIER - * is an optional flag. Although a guest should not send this flag if - * not negotiated we ignored it in the past. So keep ignoring it. */ - switch (type & ~(VIRTIO_BLK_T_OUT | VIRTIO_BLK_T_BARRIER)) { - case VIRTIO_BLK_T_IN: - { - bool is_write = type & VIRTIO_BLK_T_OUT; - req->sector_num = virtio_ldq_p(VIRTIO_DEVICE(req->dev), - &req->out.sector); - - if (is_write) { - qemu_iovec_init_external(&req->qiov, iov, out_num); - trace_virtio_blk_handle_write(req, req->sector_num, - req->qiov.size / BDRV_SECTOR_SIZE); - } else { - qemu_iovec_init_external(&req->qiov, in_iov, in_num); - trace_virtio_blk_handle_read(req, req->sector_num, - req->qiov.size / BDRV_SECTOR_SIZE); - } - - 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; - } - - block_acct_start(blk_get_stats(req->dev->blk), - &req->acct, req->qiov.size, - is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ); - - /* merge would exceed maximum number of requests or IO direction - * changes */ - if (mrb->num_reqs > 0 && (mrb->num_reqs == VIRTIO_BLK_MAX_MERGE_REQS || - is_write != mrb->is_write || - !req->dev->conf.request_merging)) { - virtio_blk_submit_multireq(req->dev->blk, mrb); - } - - assert(mrb->num_reqs < VIRTIO_BLK_MAX_MERGE_REQS); - mrb->reqs[mrb->num_reqs++] = req; - mrb->is_write = is_write; - break; - } - case VIRTIO_BLK_T_FLUSH: - virtio_blk_handle_flush(req, mrb); - break; - case VIRTIO_BLK_T_SCSI_CMD: - virtio_blk_handle_scsi(req); - break; - case VIRTIO_BLK_T_GET_ID: - { - VirtIOBlock *s = req->dev; - - /* - * NB: per existing s/n string convention the string is - * terminated by '\0' only when shorter than buffer. - */ - const char *serial = s->conf.serial ? s->conf.serial : ""; - size_t size = MIN(strlen(serial) + 1, - MIN(iov_size(in_iov, in_num), - VIRTIO_BLK_ID_BYTES)); - iov_from_buf(in_iov, in_num, 0, serial, size); - virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); - virtio_blk_free_request(req); - break; - } - default: - virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); - virtio_blk_free_request(req); - } -} - -void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq) -{ - VirtIOBlockReq *req; - MultiReqBuffer mrb = {}; - - blk_io_plug(s->blk); - - while ((req = virtio_blk_get_request(s))) { - virtio_blk_handle_request(req, &mrb); - } - - 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) -{ - VirtIOBlock *s = opaque; - VirtIOBlockReq *req = s->rq; - MultiReqBuffer mrb = {}; - - qemu_bh_delete(s->bh); - s->bh = NULL; - - s->rq = NULL; - - while (req) { - VirtIOBlockReq *next = req->next; - virtio_blk_handle_request(req, &mrb); - req = next; - } - - if (mrb.num_reqs) { - virtio_blk_submit_multireq(s->blk, &mrb); - } -} - -static void virtio_blk_dma_restart_cb(void *opaque, int running, - RunState state) -{ - VirtIOBlock *s = opaque; - - if (!running) { - return; - } - - if (!s->bh) { - s->bh = aio_bh_new(blk_get_aio_context(s->conf.conf.blk), - virtio_blk_dma_restart_bh, s); - qemu_bh_schedule(s->bh); - } -} - -static void virtio_blk_reset(VirtIODevice *vdev) -{ - VirtIOBlock *s = VIRTIO_BLK(vdev); - AioContext *ctx; - - /* - * This should cancel pending requests, but can't do nicely until there - * are per-device request lists. - */ - ctx = blk_get_aio_context(s->blk); - aio_context_acquire(ctx); - blk_drain(s->blk); - - if (s->dataplane) { - virtio_blk_data_plane_stop(s->dataplane); - } - aio_context_release(ctx); - - blk_set_enable_write_cache(s->blk, s->original_wce); -} - -/* coalesce internal state, copy to pci i/o region 0 - */ -static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) -{ - VirtIOBlock *s = VIRTIO_BLK(vdev); - BlockConf *conf = &s->conf.conf; - struct virtio_blk_config blkcfg; - uint64_t capacity; - int blk_size = conf->logical_block_size; - - blk_get_geometry(s->blk, &capacity); - memset(&blkcfg, 0, sizeof(blkcfg)); - virtio_stq_p(vdev, &blkcfg.capacity, capacity); - virtio_stl_p(vdev, &blkcfg.seg_max, 128 - 2); - virtio_stw_p(vdev, &blkcfg.geometry.cylinders, conf->cyls); - virtio_stl_p(vdev, &blkcfg.blk_size, blk_size); - virtio_stw_p(vdev, &blkcfg.min_io_size, conf->min_io_size / blk_size); - virtio_stw_p(vdev, &blkcfg.opt_io_size, conf->opt_io_size / blk_size); - blkcfg.geometry.heads = conf->heads; - /* - * We must ensure that the block device capacity is a multiple of - * the logical block size. If that is not the case, let's use - * sector_mask to adopt the geometry to have a correct picture. - * For those devices where the capacity is ok for the given geometry - * we don't touch the sector value of the geometry, since some devices - * (like s390 dasd) need a specific value. Here the capacity is already - * cyls*heads*secs*blk_size and the sector value is not block size - * divided by 512 - instead it is the amount of blk_size blocks - * per track (cylinder). - */ - if (blk_getlength(s->blk) / conf->heads / conf->secs % blk_size) { - blkcfg.geometry.sectors = conf->secs & ~s->sector_mask; - } else { - blkcfg.geometry.sectors = conf->secs; - } - blkcfg.size_max = 0; - blkcfg.physical_block_exp = get_physical_block_exp(conf); - blkcfg.alignment_offset = 0; - blkcfg.wce = blk_enable_write_cache(s->blk); - memcpy(config, &blkcfg, sizeof(struct virtio_blk_config)); -} - -static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config) -{ - VirtIOBlock *s = VIRTIO_BLK(vdev); - struct virtio_blk_config blkcfg; - - memcpy(&blkcfg, config, sizeof(blkcfg)); - - aio_context_acquire(blk_get_aio_context(s->blk)); - blk_set_enable_write_cache(s->blk, blkcfg.wce != 0); - aio_context_release(blk_get_aio_context(s->blk)); -} - -static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features, - Error **errp) -{ - VirtIOBlock *s = VIRTIO_BLK(vdev); - - virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX); - 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 (s->conf.scsi) { - error_setg(errp, "Please set scsi=off for virtio-blk devices in order to use virtio 1.0"); - return 0; - } - } else { - virtio_clear_feature(&features, VIRTIO_F_ANY_LAYOUT); - virtio_add_feature(&features, VIRTIO_BLK_F_SCSI); - } - - if (s->conf.config_wce) { - virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE); - } - if (blk_enable_write_cache(s->blk)) { - virtio_add_feature(&features, VIRTIO_BLK_F_WCE); - } - if (blk_is_read_only(s->blk)) { - virtio_add_feature(&features, VIRTIO_BLK_F_RO); - } - - return features; -} - -static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) -{ - VirtIOBlock *s = VIRTIO_BLK(vdev); - - if (s->dataplane && !(status & (VIRTIO_CONFIG_S_DRIVER | - VIRTIO_CONFIG_S_DRIVER_OK))) { - virtio_blk_data_plane_stop(s->dataplane); - } - - if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) { - return; - } - - /* A guest that supports VIRTIO_BLK_F_CONFIG_WCE must be able to send - * cache flushes. Thus, the "auto writethrough" behavior is never - * necessary for guests that support the VIRTIO_BLK_F_CONFIG_WCE feature. - * Leaving it enabled would break the following sequence: - * - * Guest started with "-drive cache=writethrough" - * Guest sets status to 0 - * Guest sets DRIVER bit in status field - * Guest reads host features (WCE=0, CONFIG_WCE=1) - * Guest writes guest features (WCE=0, CONFIG_WCE=1) - * Guest writes 1 to the WCE configuration field (writeback mode) - * Guest sets DRIVER_OK bit in status field - * - * s->blk would erroneously be placed in writethrough mode. - */ - 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_vdev_has_feature(vdev, - VIRTIO_BLK_F_WCE)); - aio_context_release(blk_get_aio_context(s->blk)); - } -} - -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); -} - -static void virtio_blk_save_device(VirtIODevice *vdev, QEMUFile *f) -{ - VirtIOBlock *s = VIRTIO_BLK(vdev); - VirtIOBlockReq *req = s->rq; - - while (req) { - qemu_put_sbyte(f, 1); - qemu_put_virtqueue_element(f, &req->elem); - req = req->next; - } - qemu_put_sbyte(f, 0); -} - -static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id) -{ - VirtIOBlock *s = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(s); - - if (version_id != 2) - return -EINVAL; - - return virtio_load(vdev, f, version_id); -} - -static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f, - int version_id) -{ - VirtIOBlock *s = VIRTIO_BLK(vdev); - - while (qemu_get_sbyte(f)) { - VirtIOBlockReq *req; - req = qemu_get_virtqueue_element(f, sizeof(VirtIOBlockReq)); - virtio_blk_init_request(s, req); - req->next = s->rq; - s->rq = req; - } - - return 0; -} - -static void virtio_blk_resize(void *opaque) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(opaque); - - virtio_notify_config(vdev); -} - -static const BlockDevOps virtio_block_ops = { - .resize_cb = virtio_blk_resize, -}; - -static void virtio_blk_device_realize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIOBlock *s = VIRTIO_BLK(dev); - VirtIOBlkConf *conf = &s->conf; - Error *err = NULL; - static int virtio_blk_id; - - if (!conf->conf.blk) { - error_setg(errp, "drive property not set"); - return; - } - if (!blk_is_inserted(conf->conf.blk)) { - error_setg(errp, "Device needs media, but drive is empty"); - return; - } - - blkconf_serial(&conf->conf, &conf->serial); - s->original_wce = blk_enable_write_cache(conf->conf.blk); - blkconf_geometry(&conf->conf, NULL, 65535, 255, 255, &err); - if (err) { - error_propagate(errp, err); - return; - } - blkconf_blocksizes(&conf->conf); - - virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, - sizeof(struct virtio_blk_config)); - - s->blk = conf->conf.blk; - s->rq = NULL; - s->sector_mask = (s->conf.conf.logical_block_size / BDRV_SECTOR_SIZE) - 1; - - s->vq = virtio_add_queue(vdev, 128, virtio_blk_handle_output); - virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err); - if (err != NULL) { - error_propagate(errp, err); - virtio_cleanup(vdev); - return; - } - - s->change = qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s); - register_savevm(dev, "virtio-blk", virtio_blk_id++, 2, - virtio_blk_save, virtio_blk_load, s); - blk_set_dev_ops(s->blk, &virtio_block_ops, s); - blk_set_guest_block_size(s->blk, s->conf.conf.logical_block_size); - - blk_iostatus_enable(s->blk); -} - -static void virtio_blk_device_unrealize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIOBlock *s = VIRTIO_BLK(dev); - - virtio_blk_data_plane_destroy(s->dataplane); - s->dataplane = NULL; - qemu_del_vm_change_state_handler(s->change); - unregister_savevm(dev, "virtio-blk", s); - blockdev_mark_auto_del(s->blk); - virtio_cleanup(vdev); -} - -static void virtio_blk_instance_init(Object *obj) -{ - VirtIOBlock *s = VIRTIO_BLK(obj); - - object_property_add_link(obj, "iothread", TYPE_IOTHREAD, - (Object **)&s->conf.iothread, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); - device_add_bootindex_property(obj, &s->conf.conf.bootindex, - "bootindex", "/disk@0,0", - DEVICE(obj), NULL); -} - -static Property virtio_blk_properties[] = { - DEFINE_BLOCK_PROPERTIES(VirtIOBlock, conf.conf), - DEFINE_BLOCK_CHS_PROPERTIES(VirtIOBlock, conf.conf), - 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, false), -#endif - DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, - true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_blk_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - - dc->props = virtio_blk_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - vdc->realize = virtio_blk_device_realize; - vdc->unrealize = virtio_blk_device_unrealize; - vdc->get_config = virtio_blk_update_config; - vdc->set_config = virtio_blk_set_config; - vdc->get_features = virtio_blk_get_features; - vdc->set_status = virtio_blk_set_status; - vdc->reset = virtio_blk_reset; - vdc->save = virtio_blk_save_device; - vdc->load = virtio_blk_load_device; -} - -static const TypeInfo virtio_device_info = { - .name = TYPE_VIRTIO_BLK, - .parent = TYPE_VIRTIO_DEVICE, - .instance_size = sizeof(VirtIOBlock), - .instance_init = virtio_blk_instance_init, - .class_init = virtio_blk_class_init, -}; - -static void virtio_register_types(void) -{ - type_register_static(&virtio_device_info); -} - -type_init(virtio_register_types) diff --git a/qemu/hw/block/xen_blkif.h b/qemu/hw/block/xen_blkif.h deleted file mode 100644 index c68487cb3..000000000 --- a/qemu/hw/block/xen_blkif.h +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef __XEN_BLKIF_H__ -#define __XEN_BLKIF_H__ - -#include -#include -#include - -/* Not a real protocol. Used to generate ring structs which contain - * the elements common to all protocols only. This way we get a - * compiler-checkable way to use common struct elements, so we can - * avoid using switch(protocol) in a number of places. */ -struct blkif_common_request { - char dummy; -}; -struct blkif_common_response { - char dummy; -}; - -/* i386 protocol version */ -#pragma pack(push, 4) -struct blkif_x86_32_request { - uint8_t operation; /* BLKIF_OP_??? */ - uint8_t nr_segments; /* number of segments */ - blkif_vdev_t handle; /* only for read/write requests */ - uint64_t id; /* private guest value, echoed in resp */ - blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ - struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; -}; -struct blkif_x86_32_response { - uint64_t id; /* copied from request */ - uint8_t operation; /* copied from request */ - int16_t status; /* BLKIF_RSP_??? */ -}; -typedef struct blkif_x86_32_request blkif_x86_32_request_t; -typedef struct blkif_x86_32_response blkif_x86_32_response_t; -#pragma pack(pop) - -/* x86_64 protocol version */ -struct blkif_x86_64_request { - uint8_t operation; /* BLKIF_OP_??? */ - uint8_t nr_segments; /* number of segments */ - blkif_vdev_t handle; /* only for read/write requests */ - uint64_t __attribute__((__aligned__(8))) id; - blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ - struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; -}; -struct blkif_x86_64_response { - uint64_t __attribute__((__aligned__(8))) id; - uint8_t operation; /* copied from request */ - int16_t status; /* BLKIF_RSP_??? */ -}; -typedef struct blkif_x86_64_request blkif_x86_64_request_t; -typedef struct blkif_x86_64_response blkif_x86_64_response_t; - -DEFINE_RING_TYPES(blkif_common, struct blkif_common_request, struct blkif_common_response); -DEFINE_RING_TYPES(blkif_x86_32, struct blkif_x86_32_request, struct blkif_x86_32_response); -DEFINE_RING_TYPES(blkif_x86_64, struct blkif_x86_64_request, struct blkif_x86_64_response); - -union blkif_back_rings { - blkif_back_ring_t native; - blkif_common_back_ring_t common; - blkif_x86_32_back_ring_t x86_32_part; - blkif_x86_64_back_ring_t x86_64_part; -}; -typedef union blkif_back_rings blkif_back_rings_t; - -enum blkif_protocol { - BLKIF_PROTOCOL_NATIVE = 1, - BLKIF_PROTOCOL_X86_32 = 2, - BLKIF_PROTOCOL_X86_64 = 3, -}; - -static inline void blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_request_t *src) -{ - int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; - - dst->operation = src->operation; - dst->nr_segments = src->nr_segments; - dst->handle = src->handle; - dst->id = src->id; - dst->sector_number = src->sector_number; - if (src->operation == BLKIF_OP_DISCARD) { - struct blkif_request_discard *s = (void *)src; - struct blkif_request_discard *d = (void *)dst; - d->nr_sectors = s->nr_sectors; - return; - } - /* 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]; -} - -static inline void blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_request_t *src) -{ - int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; - - dst->operation = src->operation; - dst->nr_segments = src->nr_segments; - dst->handle = src->handle; - dst->id = src->id; - dst->sector_number = src->sector_number; - if (src->operation == BLKIF_OP_DISCARD) { - struct blkif_request_discard *s = (void *)src; - struct blkif_request_discard *d = (void *)dst; - d->nr_sectors = s->nr_sectors; - return; - } - /* 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]; -} - -#endif /* __XEN_BLKIF_H__ */ diff --git a/qemu/hw/block/xen_disk.c b/qemu/hw/block/xen_disk.c deleted file mode 100644 index d4ce380fe..000000000 --- a/qemu/hw/block/xen_disk.c +++ /dev/null @@ -1,1121 +0,0 @@ -/* - * xen paravirt block device backend - * - * (c) Gerd Hoffmann - * - * 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; under version 2 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 . - * - * 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 -#include -#include - -#include "hw/hw.h" -#include "hw/xen/xen_backend.h" -#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" - -/* ------------------------------------------------------------- */ - -static int batch_maps = 0; - -static int max_requests = 32; - -/* ------------------------------------------------------------- */ - -#define BLOCK_SIZE 512 -#define IOCB_COUNT (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2) - -struct PersistentGrant { - void *page; - struct XenBlkDev *blkdev; -}; - -typedef struct PersistentGrant PersistentGrant; - -struct PersistentRegion { - void *addr; - int num; -}; - -typedef struct PersistentRegion PersistentRegion; - -struct ioreq { - blkif_request_t req; - int16_t status; - - /* parsed request */ - off_t start; - QEMUIOVector v; - int presync; - uint8_t mapped; - - /* grant mapping */ - uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - int prot; - void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - void *pages; - int num_unmap; - - /* aio status */ - int aio_inflight; - int aio_errors; - - struct XenBlkDev *blkdev; - QLIST_ENTRY(ioreq) list; - BlockAcctCookie acct; -}; - -struct XenBlkDev { - struct XenDevice xendev; /* must be first */ - char *params; - char *mode; - char *type; - char *dev; - char *devtype; - bool directiosafe; - const char *fileproto; - const char *filename; - int ring_ref; - void *sring; - int64_t file_blk; - int64_t file_size; - int protocol; - blkif_back_rings_t rings; - int more_work; - int cnt_map; - - /* request lists */ - QLIST_HEAD(inflight_head, ioreq) inflight; - QLIST_HEAD(finished_head, ioreq) finished; - QLIST_HEAD(freelist_head, ioreq) freelist; - int requests_total; - int requests_inflight; - int requests_finished; - - /* Persistent grants extension */ - gboolean feature_discard; - gboolean feature_persistent; - GTree *persistent_gnts; - GSList *persistent_regions; - unsigned int persistent_gnt_count; - unsigned int max_grants; - - /* qemu block driver */ - DriveInfo *dinfo; - BlockBackend *blk; - QEMUBH *bh; -}; - -/* ------------------------------------------------------------- */ - -static void ioreq_reset(struct ioreq *ioreq) -{ - memset(&ioreq->req, 0, sizeof(ioreq->req)); - ioreq->status = 0; - ioreq->start = 0; - ioreq->presync = 0; - ioreq->mapped = 0; - - memset(ioreq->domids, 0, sizeof(ioreq->domids)); - memset(ioreq->refs, 0, sizeof(ioreq->refs)); - ioreq->prot = 0; - memset(ioreq->page, 0, sizeof(ioreq->page)); - ioreq->pages = NULL; - - ioreq->aio_inflight = 0; - ioreq->aio_errors = 0; - - ioreq->blkdev = NULL; - memset(&ioreq->list, 0, sizeof(ioreq->list)); - memset(&ioreq->acct, 0, sizeof(ioreq->acct)); - - qemu_iovec_reset(&ioreq->v); -} - -static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data) -{ - uint ua = GPOINTER_TO_UINT(a); - uint ub = GPOINTER_TO_UINT(b); - return (ua > ub) - (ua < ub); -} - -static void destroy_grant(gpointer pgnt) -{ - PersistentGrant *grant = pgnt; - xengnttab_handle *gnt = grant->blkdev->xendev.gnttabdev; - - if (xengnttab_unmap(gnt, grant->page, 1) != 0) { - xen_be_printf(&grant->blkdev->xendev, 0, - "xengnttab_unmap failed: %s\n", - strerror(errno)); - } - grant->blkdev->persistent_gnt_count--; - xen_be_printf(&grant->blkdev->xendev, 3, - "unmapped grant %p\n", grant->page); - g_free(grant); -} - -static void remove_persistent_region(gpointer data, gpointer dev) -{ - PersistentRegion *region = data; - struct XenBlkDev *blkdev = dev; - xengnttab_handle *gnt = blkdev->xendev.gnttabdev; - - if (xengnttab_unmap(gnt, region->addr, region->num) != 0) { - xen_be_printf(&blkdev->xendev, 0, - "xengnttab_unmap region %p failed: %s\n", - region->addr, strerror(errno)); - } - xen_be_printf(&blkdev->xendev, 3, - "unmapped grant region %p with %d pages\n", - region->addr, region->num); - g_free(region); -} - -static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) -{ - struct ioreq *ioreq = NULL; - - if (QLIST_EMPTY(&blkdev->freelist)) { - if (blkdev->requests_total >= max_requests) { - goto out; - } - /* allocate new struct */ - ioreq = g_malloc0(sizeof(*ioreq)); - ioreq->blkdev = blkdev; - blkdev->requests_total++; - qemu_iovec_init(&ioreq->v, BLKIF_MAX_SEGMENTS_PER_REQUEST); - } else { - /* get one from freelist */ - ioreq = QLIST_FIRST(&blkdev->freelist); - QLIST_REMOVE(ioreq, list); - } - QLIST_INSERT_HEAD(&blkdev->inflight, ioreq, list); - blkdev->requests_inflight++; - -out: - return ioreq; -} - -static void ioreq_finish(struct ioreq *ioreq) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - - QLIST_REMOVE(ioreq, list); - QLIST_INSERT_HEAD(&blkdev->finished, ioreq, list); - blkdev->requests_inflight--; - blkdev->requests_finished++; -} - -static void ioreq_release(struct ioreq *ioreq, bool finish) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - - QLIST_REMOVE(ioreq, list); - ioreq_reset(ioreq); - ioreq->blkdev = blkdev; - QLIST_INSERT_HEAD(&blkdev->freelist, ioreq, list); - if (finish) { - blkdev->requests_finished--; - } else { - blkdev->requests_inflight--; - } -} - -/* - * translate request into iovec + start offset - * do sanity checks along the way - */ -static int ioreq_parse(struct ioreq *ioreq) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - uintptr_t mem; - size_t len; - int i; - - xen_be_printf(&blkdev->xendev, 3, - "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", - ioreq->req.operation, ioreq->req.nr_segments, - ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); - switch (ioreq->req.operation) { - case BLKIF_OP_READ: - ioreq->prot = PROT_WRITE; /* to memory */ - break; - case BLKIF_OP_FLUSH_DISKCACHE: - ioreq->presync = 1; - if (!ioreq->req.nr_segments) { - return 0; - } - /* fall through */ - case BLKIF_OP_WRITE: - ioreq->prot = PROT_READ; /* from memory */ - break; - case BLKIF_OP_DISCARD: - return 0; - default: - xen_be_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", - ioreq->req.operation); - goto err; - }; - - if (ioreq->req.operation != BLKIF_OP_READ && blkdev->mode[0] != 'w') { - xen_be_printf(&blkdev->xendev, 0, "error: write req for ro device\n"); - goto err; - } - - ioreq->start = ioreq->req.sector_number * blkdev->file_blk; - for (i = 0; i < ioreq->req.nr_segments; i++) { - if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { - xen_be_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); - goto err; - } - if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { - xen_be_printf(&blkdev->xendev, 0, "error: first > last sector\n"); - goto err; - } - if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { - xen_be_printf(&blkdev->xendev, 0, "error: page crossing\n"); - goto err; - } - - ioreq->domids[i] = blkdev->xendev.dom; - ioreq->refs[i] = ioreq->req.seg[i].gref; - - mem = ioreq->req.seg[i].first_sect * blkdev->file_blk; - len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk; - qemu_iovec_add(&ioreq->v, (void*)mem, len); - } - if (ioreq->start + ioreq->v.size > blkdev->file_size) { - xen_be_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); - goto err; - } - return 0; - -err: - ioreq->status = BLKIF_RSP_ERROR; - return -1; -} - -static void ioreq_unmap(struct ioreq *ioreq) -{ - xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; - int i; - - if (ioreq->num_unmap == 0 || ioreq->mapped == 0) { - return; - } - if (batch_maps) { - if (!ioreq->pages) { - return; - } - 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; - ioreq->pages = NULL; - } else { - for (i = 0; i < ioreq->num_unmap; i++) { - if (!ioreq->page[i]) { - continue; - } - 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--; - ioreq->page[i] = NULL; - } - } - ioreq->mapped = 0; -} - -static int ioreq_map(struct ioreq *ioreq) -{ - 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]; - int i, j, new_maps = 0; - PersistentGrant *grant; - PersistentRegion *region; - /* domids and refs variables will contain the information necessary - * to map the grants that are needed to fulfill this request. - * - * After mapping the needed grants, the page array will contain the - * memory address of each granted page in the order specified in ioreq - * (disregarding if it's a persistent grant or not). - */ - - if (ioreq->v.niov == 0 || ioreq->mapped == 1) { - return 0; - } - if (ioreq->blkdev->feature_persistent) { - for (i = 0; i < ioreq->v.niov; i++) { - grant = g_tree_lookup(ioreq->blkdev->persistent_gnts, - GUINT_TO_POINTER(ioreq->refs[i])); - - if (grant != NULL) { - page[i] = grant->page; - xen_be_printf(&ioreq->blkdev->xendev, 3, - "using persistent-grant %" PRIu32 "\n", - ioreq->refs[i]); - } else { - /* Add the grant to the list of grants that - * should be mapped - */ - domids[new_maps] = ioreq->domids[i]; - refs[new_maps] = ioreq->refs[i]; - page[i] = NULL; - new_maps++; - } - } - /* Set the protection to RW, since grants may be reused later - * with a different protection than the one needed for this request - */ - ioreq->prot = PROT_WRITE | PROT_READ; - } else { - /* All grants in the request should be mapped */ - memcpy(refs, ioreq->refs, sizeof(refs)); - memcpy(domids, ioreq->domids, sizeof(domids)); - memset(page, 0, sizeof(page)); - new_maps = ioreq->v.niov; - } - - if (batch_maps && new_maps) { - ioreq->pages = xengnttab_map_grant_refs - (gnt, new_maps, domids, refs, ioreq->prot); - if (ioreq->pages == NULL) { - xen_be_printf(&ioreq->blkdev->xendev, 0, - "can't map %d grant refs (%s, %d maps)\n", - new_maps, strerror(errno), ioreq->blkdev->cnt_map); - return -1; - } - for (i = 0, j = 0; i < ioreq->v.niov; i++) { - if (page[i] == NULL) { - page[i] = ioreq->pages + (j++) * XC_PAGE_SIZE; - } - } - ioreq->blkdev->cnt_map += new_maps; - } else if (new_maps) { - for (i = 0; i < new_maps; i++) { - 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, - "can't map grant ref %d (%s, %d maps)\n", - refs[i], strerror(errno), ioreq->blkdev->cnt_map); - ioreq->mapped = 1; - ioreq_unmap(ioreq); - return -1; - } - ioreq->blkdev->cnt_map++; - } - for (i = 0, j = 0; i < ioreq->v.niov; i++) { - if (page[i] == NULL) { - page[i] = ioreq->page[j++]; - } - } - } - if (ioreq->blkdev->feature_persistent && new_maps != 0 && - (!batch_maps || (ioreq->blkdev->persistent_gnt_count + new_maps <= - ioreq->blkdev->max_grants))) { - /* - * If we are using persistent grants and batch mappings only - * add the new maps to the list of persistent grants if the whole - * area can be persistently mapped. - */ - if (batch_maps) { - region = g_malloc0(sizeof(*region)); - region->addr = ioreq->pages; - region->num = new_maps; - ioreq->blkdev->persistent_regions = g_slist_append( - ioreq->blkdev->persistent_regions, - region); - } - while ((ioreq->blkdev->persistent_gnt_count < ioreq->blkdev->max_grants) - && new_maps) { - /* Go through the list of newly mapped grants and add as many - * as possible to the list of persistently mapped grants. - * - * Since we start at the end of ioreq->page(s), we only need - * to decrease new_maps to prevent this granted pages from - * being unmapped in ioreq_unmap. - */ - grant = g_malloc0(sizeof(*grant)); - new_maps--; - if (batch_maps) { - grant->page = ioreq->pages + (new_maps) * XC_PAGE_SIZE; - } else { - grant->page = ioreq->page[new_maps]; - } - grant->blkdev = ioreq->blkdev; - xen_be_printf(&ioreq->blkdev->xendev, 3, - "adding grant %" PRIu32 " page: %p\n", - refs[new_maps], grant->page); - g_tree_insert(ioreq->blkdev->persistent_gnts, - GUINT_TO_POINTER(refs[new_maps]), - grant); - ioreq->blkdev->persistent_gnt_count++; - } - assert(!batch_maps || new_maps == 0); - } - for (i = 0; i < ioreq->v.niov; i++) { - ioreq->v.iov[i].iov_base += (uintptr_t)page[i]; - } - ioreq->mapped = 1; - ioreq->num_unmap = new_maps; - return 0; -} - -static int ioreq_runio_qemu_aio(struct ioreq *ioreq); - -static void qemu_aio_complete(void *opaque, int ret) -{ - struct ioreq *ioreq = opaque; - - if (ret != 0) { - xen_be_printf(&ioreq->blkdev->xendev, 0, "%s I/O error\n", - ioreq->req.operation == BLKIF_OP_READ ? "read" : "write"); - ioreq->aio_errors++; - } - - ioreq->aio_inflight--; - if (ioreq->presync) { - ioreq->presync = 0; - ioreq_runio_qemu_aio(ioreq); - return; - } - if (ioreq->aio_inflight > 0) { - return; - } - - ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; - ioreq_unmap(ioreq); - ioreq_finish(ioreq); - switch (ioreq->req.operation) { - case BLKIF_OP_WRITE: - case BLKIF_OP_FLUSH_DISKCACHE: - if (!ioreq->req.nr_segments) { - break; - } - case BLKIF_OP_READ: - 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: - break; - } - qemu_bh_schedule(ioreq->blkdev->bh); -} - -static int ioreq_runio_qemu_aio(struct ioreq *ioreq) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - - if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) { - goto err_no_map; - } - - ioreq->aio_inflight++; - if (ioreq->presync) { - blk_aio_flush(ioreq->blkdev->blk, qemu_aio_complete, ioreq); - return 0; - } - - switch (ioreq->req.operation) { - case BLKIF_OP_READ: - block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct, - ioreq->v.size, BLOCK_ACCT_READ); - ioreq->aio_inflight++; - blk_aio_readv(blkdev->blk, ioreq->start / BLOCK_SIZE, - &ioreq->v, ioreq->v.size / BLOCK_SIZE, - qemu_aio_complete, ioreq); - break; - case BLKIF_OP_WRITE: - case BLKIF_OP_FLUSH_DISKCACHE: - if (!ioreq->req.nr_segments) { - break; - } - - block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct, - 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, - qemu_aio_complete, ioreq); - break; - case BLKIF_OP_DISCARD: - { - struct blkif_request_discard *discard_req = (void *)&ioreq->req; - ioreq->aio_inflight++; - blk_aio_discard(blkdev->blk, - discard_req->sector_number, discard_req->nr_sectors, - qemu_aio_complete, ioreq); - break; - } - default: - /* unknown operation (shouldn't happen -- parse catches this) */ - goto err; - } - - qemu_aio_complete(ioreq, 0); - - return 0; - -err: - ioreq_unmap(ioreq); -err_no_map: - ioreq_finish(ioreq); - ioreq->status = BLKIF_RSP_ERROR; - return -1; -} - -static int blk_send_response_one(struct ioreq *ioreq) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - int send_notify = 0; - int have_requests = 0; - blkif_response_t resp; - void *dst; - - resp.id = ioreq->req.id; - resp.operation = ioreq->req.operation; - resp.status = ioreq->status; - - /* Place on the response ring for the relevant domain. */ - switch (blkdev->protocol) { - case BLKIF_PROTOCOL_NATIVE: - dst = RING_GET_RESPONSE(&blkdev->rings.native, blkdev->rings.native.rsp_prod_pvt); - break; - case BLKIF_PROTOCOL_X86_32: - dst = RING_GET_RESPONSE(&blkdev->rings.x86_32_part, - blkdev->rings.x86_32_part.rsp_prod_pvt); - break; - case BLKIF_PROTOCOL_X86_64: - dst = RING_GET_RESPONSE(&blkdev->rings.x86_64_part, - blkdev->rings.x86_64_part.rsp_prod_pvt); - break; - default: - dst = NULL; - return 0; - } - memcpy(dst, &resp, sizeof(resp)); - blkdev->rings.common.rsp_prod_pvt++; - - RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify); - if (blkdev->rings.common.rsp_prod_pvt == blkdev->rings.common.req_cons) { - /* - * Tail check for pending requests. Allows frontend to avoid - * notifications if requests are already in flight (lower - * overheads and promotes batching). - */ - RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests); - } else if (RING_HAS_UNCONSUMED_REQUESTS(&blkdev->rings.common)) { - have_requests = 1; - } - - if (have_requests) { - blkdev->more_work++; - } - return send_notify; -} - -/* walk finished list, send outstanding responses, free requests */ -static void blk_send_response_all(struct XenBlkDev *blkdev) -{ - struct ioreq *ioreq; - int send_notify = 0; - - while (!QLIST_EMPTY(&blkdev->finished)) { - ioreq = QLIST_FIRST(&blkdev->finished); - send_notify += blk_send_response_one(ioreq); - ioreq_release(ioreq, true); - } - if (send_notify) { - xen_be_send_notify(&blkdev->xendev); - } -} - -static int blk_get_request(struct XenBlkDev *blkdev, struct ioreq *ioreq, RING_IDX rc) -{ - switch (blkdev->protocol) { - case BLKIF_PROTOCOL_NATIVE: - memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc), - sizeof(ioreq->req)); - break; - case BLKIF_PROTOCOL_X86_32: - blkif_get_x86_32_req(&ioreq->req, - RING_GET_REQUEST(&blkdev->rings.x86_32_part, rc)); - break; - case BLKIF_PROTOCOL_X86_64: - blkif_get_x86_64_req(&ioreq->req, - RING_GET_REQUEST(&blkdev->rings.x86_64_part, rc)); - break; - } - return 0; -} - -static void blk_handle_requests(struct XenBlkDev *blkdev) -{ - RING_IDX rc, rp; - struct ioreq *ioreq; - - blkdev->more_work = 0; - - rc = blkdev->rings.common.req_cons; - rp = blkdev->rings.common.sring->req_prod; - xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ - - blk_send_response_all(blkdev); - while (rc != rp) { - /* pull request from ring */ - if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) { - break; - } - ioreq = ioreq_start(blkdev); - if (ioreq == NULL) { - blkdev->more_work++; - break; - } - blk_get_request(blkdev, ioreq, rc); - blkdev->rings.common.req_cons = ++rc; - - /* 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); - } - ioreq_release(ioreq, false); - continue; - } - - ioreq_runio_qemu_aio(ioreq); - } - - if (blkdev->more_work && blkdev->requests_inflight < max_requests) { - qemu_bh_schedule(blkdev->bh); - } -} - -/* ------------------------------------------------------------- */ - -static void blk_bh(void *opaque) -{ - struct XenBlkDev *blkdev = opaque; - blk_handle_requests(blkdev); -} - -/* - * We need to account for the grant allocations requiring contiguous - * chunks; the worst case number would be - * max_req * max_seg + (max_req - 1) * (max_seg - 1) + 1, - * but in order to keep things simple just use - * 2 * max_req * max_seg. - */ -#define MAX_GRANTS(max_req, max_seg) (2 * (max_req) * (max_seg)) - -static void blk_alloc(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - - QLIST_INIT(&blkdev->inflight); - QLIST_INIT(&blkdev->finished); - QLIST_INIT(&blkdev->freelist); - blkdev->bh = qemu_bh_new(blk_bh, blkdev); - if (xen_mode != XEN_EMULATE) { - batch_maps = 1; - } - if (xengnttab_set_max_grants(xendev->gnttabdev, - MAX_GRANTS(max_requests, BLKIF_MAX_SEGMENTS_PER_REQUEST)) < 0) { - xen_be_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", - strerror(errno)); - } -} - -static void blk_parse_discard(struct XenBlkDev *blkdev) -{ - int enable; - - blkdev->feature_discard = true; - - if (xenstore_read_be_int(&blkdev->xendev, "discard-enable", &enable) == 0) { - blkdev->feature_discard = !!enable; - } - - if (blkdev->feature_discard) { - xenstore_write_be_int(&blkdev->xendev, "feature-discard", 1); - } -} - -static int blk_init(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - int info = 0; - char *directiosafe = NULL; - - /* read xenstore entries */ - if (blkdev->params == NULL) { - char *h = NULL; - blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); - if (blkdev->params != NULL) { - h = strchr(blkdev->params, ':'); - } - if (h != NULL) { - blkdev->fileproto = blkdev->params; - blkdev->filename = h+1; - *h = 0; - } else { - blkdev->fileproto = ""; - blkdev->filename = blkdev->params; - } - } - 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"); - } - if (blkdev->type == NULL) { - blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type"); - } - if (blkdev->dev == NULL) { - blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev"); - } - if (blkdev->devtype == NULL) { - blkdev->devtype = xenstore_read_be_str(&blkdev->xendev, "device-type"); - } - directiosafe = xenstore_read_be_str(&blkdev->xendev, "direct-io-safe"); - blkdev->directiosafe = (directiosafe && atoi(directiosafe)); - - /* do we have all we need? */ - if (blkdev->params == NULL || - blkdev->mode == NULL || - blkdev->type == NULL || - blkdev->dev == NULL) { - goto out_error; - } - - /* read-only ? */ - if (strcmp(blkdev->mode, "w")) { - info |= VDISK_READONLY; - } - - /* cdrom ? */ - if (blkdev->devtype && !strcmp(blkdev->devtype, "cdrom")) { - info |= VDISK_CDROM; - } - - blkdev->file_blk = BLOCK_SIZE; - - /* fill info - * blk_connect supplies sector-size and sectors - */ - xenstore_write_be_int(&blkdev->xendev, "feature-flush-cache", 1); - xenstore_write_be_int(&blkdev->xendev, "feature-persistent", 1); - xenstore_write_be_int(&blkdev->xendev, "info", info); - - blk_parse_discard(blkdev); - - g_free(directiosafe); - return 0; - -out_error: - g_free(blkdev->params); - blkdev->params = NULL; - g_free(blkdev->mode); - blkdev->mode = NULL; - g_free(blkdev->type); - blkdev->type = NULL; - g_free(blkdev->dev); - blkdev->dev = NULL; - g_free(blkdev->devtype); - blkdev->devtype = NULL; - g_free(directiosafe); - blkdev->directiosafe = false; - return -1; -} - -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 = 0; - writethrough = false; - } - if (strcmp(blkdev->mode, "w") == 0) { - qflags |= BDRV_O_RDWR; - readonly = false; - } - if (blkdev->feature_discard) { - qflags |= BDRV_O_UNMAP; - } - - /* init qemu block driver */ - index = (blkdev->xendev.dev - 202 * 256) / 16; - blkdev->dinfo = drive_get(IF_XEN, 0, index); - if (!blkdev->dinfo) { - Error *local_err = NULL; - QDict *options = NULL; - - if (strcmp(blkdev->fileproto, "")) { - options = qdict_new(); - qdict_put(options, "driver", qstring_from_str(blkdev->fileproto)); - } - - /* 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->filename, NULL, options, - qflags, &local_err); - if (!blkdev->blk) { - xen_be_printf(&blkdev->xendev, 0, "error: %s\n", - error_get_pretty(local_err)); - 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"); - blkdev->blk = blk_by_legacy_dinfo(blkdev->dinfo); - if (blk_is_read_only(blkdev->blk) && !readonly) { - xen_be_printf(&blkdev->xendev, 0, "Unexpected read-only drive"); - blkdev->blk = NULL; - return -1; - } - /* blkdev->blk is not create by us, we get a reference - * so we can blk_unref() unconditionally */ - blk_ref(blkdev->blk); - } - 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), - drv_name ?: "-"); - blkdev->file_size = 0; - } - - xen_be_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\"," - " size %" PRId64 " (%" PRId64 " MB)\n", - blkdev->type, blkdev->fileproto, blkdev->filename, - blkdev->file_size, blkdev->file_size >> 20); - - /* Fill in number of sector size and number of sectors */ - xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); - xenstore_write_be_int64(&blkdev->xendev, "sectors", - blkdev->file_size / blkdev->file_blk); - - if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref) == -1) { - return -1; - } - if (xenstore_read_fe_int(&blkdev->xendev, "event-channel", - &blkdev->xendev.remote_port) == -1) { - return -1; - } - if (xenstore_read_fe_int(&blkdev->xendev, "feature-persistent", &pers)) { - blkdev->feature_persistent = FALSE; - } else { - blkdev->feature_persistent = !!pers; - } - - blkdev->protocol = BLKIF_PROTOCOL_NATIVE; - if (blkdev->xendev.protocol) { - if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32) == 0) { - blkdev->protocol = BLKIF_PROTOCOL_X86_32; - } - if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64) == 0) { - blkdev->protocol = BLKIF_PROTOCOL_X86_64; - } - } - - blkdev->sring = xengnttab_map_grant_ref(blkdev->xendev.gnttabdev, - blkdev->xendev.dom, - blkdev->ring_ref, - PROT_READ | PROT_WRITE); - if (!blkdev->sring) { - return -1; - } - blkdev->cnt_map++; - - switch (blkdev->protocol) { - case BLKIF_PROTOCOL_NATIVE: - { - blkif_sring_t *sring_native = blkdev->sring; - BACK_RING_INIT(&blkdev->rings.native, sring_native, XC_PAGE_SIZE); - break; - } - case BLKIF_PROTOCOL_X86_32: - { - blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring; - - BACK_RING_INIT(&blkdev->rings.x86_32_part, sring_x86_32, XC_PAGE_SIZE); - break; - } - case BLKIF_PROTOCOL_X86_64: - { - blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring; - - BACK_RING_INIT(&blkdev->rings.x86_64_part, sring_x86_64, XC_PAGE_SIZE); - break; - } - } - - if (blkdev->feature_persistent) { - /* Init persistent grants */ - blkdev->max_grants = max_requests * BLKIF_MAX_SEGMENTS_PER_REQUEST; - blkdev->persistent_gnts = g_tree_new_full((GCompareDataFunc)int_cmp, - NULL, NULL, - batch_maps ? - (GDestroyNotify)g_free : - (GDestroyNotify)destroy_grant); - blkdev->persistent_regions = NULL; - blkdev->persistent_gnt_count = 0; - } - - xen_be_bind_evtchn(&blkdev->xendev); - - xen_be_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, " - "remote port %d, local port %d\n", - blkdev->xendev.protocol, blkdev->ring_ref, - blkdev->xendev.remote_port, blkdev->xendev.local_port); - return 0; -} - -static void blk_disconnect(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - - if (blkdev->blk) { - blk_detach_dev(blkdev->blk, blkdev); - blk_unref(blkdev->blk); - blkdev->blk = NULL; - } - xen_be_unbind_evtchn(&blkdev->xendev); - - if (blkdev->sring) { - xengnttab_unmap(blkdev->xendev.gnttabdev, blkdev->sring, 1); - blkdev->cnt_map--; - blkdev->sring = NULL; - } - - /* - * Unmap persistent grants before switching to the closed state - * so the frontend can free them. - * - * In the !batch_maps case g_tree_destroy will take care of unmapping - * the grant, but in the batch_maps case we need to iterate over every - * region in persistent_regions and unmap it. - */ - if (blkdev->feature_persistent) { - g_tree_destroy(blkdev->persistent_gnts); - assert(batch_maps || blkdev->persistent_gnt_count == 0); - if (batch_maps) { - blkdev->persistent_gnt_count = 0; - g_slist_foreach(blkdev->persistent_regions, - (GFunc)remove_persistent_region, blkdev); - g_slist_free(blkdev->persistent_regions); - } - blkdev->feature_persistent = false; - } -} - -static int blk_free(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - struct ioreq *ioreq; - - if (blkdev->blk || blkdev->sring) { - blk_disconnect(xendev); - } - - while (!QLIST_EMPTY(&blkdev->freelist)) { - ioreq = QLIST_FIRST(&blkdev->freelist); - QLIST_REMOVE(ioreq, list); - qemu_iovec_destroy(&ioreq->v); - g_free(ioreq); - } - - g_free(blkdev->params); - g_free(blkdev->mode); - g_free(blkdev->type); - g_free(blkdev->dev); - g_free(blkdev->devtype); - qemu_bh_delete(blkdev->bh); - return 0; -} - -static void blk_event(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - - qemu_bh_schedule(blkdev->bh); -} - -struct XenDevOps xen_blkdev_ops = { - .size = sizeof(struct XenBlkDev), - .flags = DEVOPS_FLAG_NEED_GNTDEV, - .alloc = blk_alloc, - .init = blk_init, - .initialise = blk_connect, - .disconnect = blk_disconnect, - .event = blk_event, - .free = blk_free, -}; diff --git a/qemu/hw/bt/Makefile.objs b/qemu/hw/bt/Makefile.objs deleted file mode 100644 index 867a7d2e8..000000000 --- a/qemu/hw/bt/Makefile.objs +++ /dev/null @@ -1,3 +0,0 @@ -common-obj-y += core.o l2cap.o sdp.o hci.o hid.o -common-obj-y += hci-csr.o - diff --git a/qemu/hw/bt/core.c b/qemu/hw/bt/core.c deleted file mode 100644 index 615f0af07..000000000 --- a/qemu/hw/bt/core.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Convenience functions for bluetooth. - * - * Copyright (C) 2008 Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "sysemu/bt.h" -#include "hw/bt.h" - -/* Slave implementations can ignore this */ -static void bt_dummy_lmp_mode_change(struct bt_link_s *link) -{ -} - -/* Slaves should never receive these PDUs */ -static void bt_dummy_lmp_connection_complete(struct bt_link_s *link) -{ - if (link->slave->reject_reason) - fprintf(stderr, "%s: stray LMP_not_accepted received, fixme\n", - __FUNCTION__); - else - fprintf(stderr, "%s: stray LMP_accepted received, fixme\n", - __FUNCTION__); - exit(-1); -} - -static void bt_dummy_lmp_disconnect_master(struct bt_link_s *link) -{ - fprintf(stderr, "%s: stray LMP_detach received, fixme\n", __FUNCTION__); - exit(-1); -} - -static void bt_dummy_lmp_acl_resp(struct bt_link_s *link, - const uint8_t *data, int start, int len) -{ - fprintf(stderr, "%s: stray ACL response PDU, fixme\n", __FUNCTION__); - exit(-1); -} - -/* Slaves that don't hold any additional per link state can use these */ -static void bt_dummy_lmp_connection_request(struct bt_link_s *req) -{ - struct bt_link_s *link = g_malloc0(sizeof(struct bt_link_s)); - - link->slave = req->slave; - link->host = req->host; - - req->host->reject_reason = 0; - req->host->lmp_connection_complete(link); -} - -static void bt_dummy_lmp_disconnect_slave(struct bt_link_s *link) -{ - g_free(link); -} - -static void bt_dummy_destroy(struct bt_device_s *device) -{ - bt_device_done(device); - g_free(device); -} - -static int bt_dev_idx = 0; - -void bt_device_init(struct bt_device_s *dev, struct bt_scatternet_s *net) -{ - memset(dev, 0, sizeof(*dev)); - dev->inquiry_scan = 1; - dev->page_scan = 1; - - dev->bd_addr.b[0] = bt_dev_idx & 0xff; - dev->bd_addr.b[1] = bt_dev_idx >> 8; - dev->bd_addr.b[2] = 0xd0; - dev->bd_addr.b[3] = 0xba; - dev->bd_addr.b[4] = 0xbe; - dev->bd_addr.b[5] = 0xba; - bt_dev_idx ++; - - /* Simple slave-only devices need to implement only .lmp_acl_data */ - dev->lmp_connection_complete = bt_dummy_lmp_connection_complete; - dev->lmp_disconnect_master = bt_dummy_lmp_disconnect_master; - dev->lmp_acl_resp = bt_dummy_lmp_acl_resp; - dev->lmp_mode_change = bt_dummy_lmp_mode_change; - dev->lmp_connection_request = bt_dummy_lmp_connection_request; - dev->lmp_disconnect_slave = bt_dummy_lmp_disconnect_slave; - - dev->handle_destroy = bt_dummy_destroy; - - dev->net = net; - dev->next = net->slave; - net->slave = dev; -} - -void bt_device_done(struct bt_device_s *dev) -{ - struct bt_device_s **p = &dev->net->slave; - - while (*p && *p != dev) - p = &(*p)->next; - if (*p != dev) { - fprintf(stderr, "%s: bad bt device \"%s\"\n", __FUNCTION__, - dev->lmp_name ?: "(null)"); - exit(-1); - } - - *p = dev->next; -} - -static struct bt_vlan_s { - struct bt_scatternet_s net; - int id; - struct bt_vlan_s *next; -} *first_bt_vlan; - -/* find or alloc a new bluetooth "VLAN" */ -struct bt_scatternet_s *qemu_find_bt_vlan(int id) -{ - struct bt_vlan_s **pvlan, *vlan; - for (vlan = first_bt_vlan; vlan != NULL; vlan = vlan->next) { - if (vlan->id == id) - return &vlan->net; - } - vlan = g_malloc0(sizeof(struct bt_vlan_s)); - vlan->id = id; - pvlan = &first_bt_vlan; - while (*pvlan != NULL) - pvlan = &(*pvlan)->next; - *pvlan = vlan; - return &vlan->net; -} diff --git a/qemu/hw/bt/hci-csr.c b/qemu/hw/bt/hci-csr.c deleted file mode 100644 index 2e970b656..000000000 --- a/qemu/hw/bt/hci-csr.c +++ /dev/null @@ -1,455 +0,0 @@ -/* - * Bluetooth serial HCI transport. - * CSR41814 HCI with H4p vendor extensions. - * - * Copyright (C) 2008 Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "sysemu/char.h" -#include "qemu/timer.h" -#include "hw/irq.h" -#include "sysemu/bt.h" -#include "hw/bt.h" - -struct csrhci_s { - int enable; - qemu_irq *pins; - int pin_state; - int modem_state; - CharDriverState chr; -#define FIFO_LEN 4096 - int out_start; - int out_len; - int out_size; - uint8_t outfifo[FIFO_LEN * 2]; - uint8_t inpkt[FIFO_LEN]; - int in_len; - int in_hdr; - int in_data; - QEMUTimer *out_tm; - int64_t baud_delay; - - bdaddr_t bd_addr; - struct HCIInfo *hci; -}; - -/* H4+ packet types */ -enum { - H4_CMD_PKT = 1, - H4_ACL_PKT = 2, - H4_SCO_PKT = 3, - H4_EVT_PKT = 4, - H4_NEG_PKT = 6, - H4_ALIVE_PKT = 7, -}; - -/* CSR41814 negotiation start magic packet */ -static const uint8_t csrhci_neg_packet[] = { - H4_NEG_PKT, 10, - 0x00, 0xa0, 0x01, 0x00, 0x00, - 0x4c, 0x00, 0x96, 0x00, 0x00, -}; - -/* CSR41814 vendor-specific command OCFs */ -enum { - OCF_CSR_SEND_FIRMWARE = 0x000, -}; - -static inline void csrhci_fifo_wake(struct csrhci_s *s) -{ - if (!s->enable || !s->out_len) - return; - - /* XXX: Should wait for s->modem_state & CHR_TIOCM_RTS? */ - if (s->chr.chr_can_read && s->chr.chr_can_read(s->chr.handler_opaque) && - s->chr.chr_read) { - s->chr.chr_read(s->chr.handler_opaque, - s->outfifo + s->out_start ++, 1); - s->out_len --; - if (s->out_start >= s->out_size) { - s->out_start = 0; - s->out_size = FIFO_LEN; - } - } - - if (s->out_len) - timer_mod(s->out_tm, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->baud_delay); -} - -#define csrhci_out_packetz(s, len) memset(csrhci_out_packet(s, len), 0, len) -static uint8_t *csrhci_out_packet(struct csrhci_s *s, int len) -{ - int off = s->out_start + s->out_len; - - /* TODO: do the padding here, i.e. align len */ - s->out_len += len; - - if (off < FIFO_LEN) { - if (off + len > FIFO_LEN && (s->out_size = off + len) > FIFO_LEN * 2) { - fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len); - exit(-1); - } - return s->outfifo + off; - } - - if (s->out_len > s->out_size) { - fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len); - exit(-1); - } - - return s->outfifo + off - s->out_size; -} - -static inline uint8_t *csrhci_out_packet_csr(struct csrhci_s *s, - int type, int len) -{ - uint8_t *ret = csrhci_out_packetz(s, len + 2); - - *ret ++ = type; - *ret ++ = len; - - return ret; -} - -static inline uint8_t *csrhci_out_packet_event(struct csrhci_s *s, - int evt, int len) -{ - uint8_t *ret = csrhci_out_packetz(s, - len + 1 + sizeof(struct hci_event_hdr)); - - *ret ++ = H4_EVT_PKT; - ((struct hci_event_hdr *) ret)->evt = evt; - ((struct hci_event_hdr *) ret)->plen = len; - - return ret + sizeof(struct hci_event_hdr); -} - -static void csrhci_in_packet_vendor(struct csrhci_s *s, int ocf, - uint8_t *data, int len) -{ - int offset; - uint8_t *rpkt; - - switch (ocf) { - case OCF_CSR_SEND_FIRMWARE: - /* Check if this is the bd_address packet */ - if (len >= 18 + 8 && data[12] == 0x01 && data[13] == 0x00) { - offset = 18; - s->bd_addr.b[0] = data[offset + 7]; /* Beyond cmd packet end(!?) */ - s->bd_addr.b[1] = data[offset + 6]; - s->bd_addr.b[2] = data[offset + 4]; - s->bd_addr.b[3] = data[offset + 0]; - s->bd_addr.b[4] = data[offset + 3]; - s->bd_addr.b[5] = data[offset + 2]; - - s->hci->bdaddr_set(s->hci, s->bd_addr.b); - fprintf(stderr, "%s: bd_address loaded from firmware: " - "%02x:%02x:%02x:%02x:%02x:%02x\n", __FUNCTION__, - s->bd_addr.b[0], s->bd_addr.b[1], s->bd_addr.b[2], - s->bd_addr.b[3], s->bd_addr.b[4], s->bd_addr.b[5]); - } - - rpkt = csrhci_out_packet_event(s, EVT_VENDOR, 11); - /* Status bytes: no error */ - rpkt[9] = 0x00; - rpkt[10] = 0x00; - break; - - default: - fprintf(stderr, "%s: got a bad CMD packet\n", __FUNCTION__); - return; - } - - csrhci_fifo_wake(s); -} - -static void csrhci_in_packet(struct csrhci_s *s, uint8_t *pkt) -{ - uint8_t *rpkt; - int opc; - - switch (*pkt ++) { - case H4_CMD_PKT: - opc = le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode); - if (cmd_opcode_ogf(opc) == OGF_VENDOR_CMD) { - csrhci_in_packet_vendor(s, cmd_opcode_ocf(opc), - pkt + sizeof(struct hci_command_hdr), - s->in_len - sizeof(struct hci_command_hdr) - 1); - return; - } - - /* TODO: if the command is OCF_READ_LOCAL_COMMANDS or the likes, - * we need to send it to the HCI layer and then add our supported - * commands to the returned mask (such as OGF_VENDOR_CMD). With - * bt-hci.c we could just have hooks for this kind of commands but - * we can't with bt-host.c. */ - - s->hci->cmd_send(s->hci, pkt, s->in_len - 1); - break; - - case H4_EVT_PKT: - goto bad_pkt; - - case H4_ACL_PKT: - s->hci->acl_send(s->hci, pkt, s->in_len - 1); - break; - - case H4_SCO_PKT: - s->hci->sco_send(s->hci, pkt, s->in_len - 1); - break; - - case H4_NEG_PKT: - if (s->in_hdr != sizeof(csrhci_neg_packet) || - memcmp(pkt - 1, csrhci_neg_packet, s->in_hdr)) { - fprintf(stderr, "%s: got a bad NEG packet\n", __FUNCTION__); - return; - } - pkt += 2; - - rpkt = csrhci_out_packet_csr(s, H4_NEG_PKT, 10); - - *rpkt ++ = 0x20; /* Operational settings negotiation Ok */ - memcpy(rpkt, pkt, 7); rpkt += 7; - *rpkt ++ = 0xff; - *rpkt = 0xff; - break; - - case H4_ALIVE_PKT: - if (s->in_hdr != 4 || pkt[1] != 0x55 || pkt[2] != 0x00) { - fprintf(stderr, "%s: got a bad ALIVE packet\n", __FUNCTION__); - return; - } - - rpkt = csrhci_out_packet_csr(s, H4_ALIVE_PKT, 2); - - *rpkt ++ = 0xcc; - *rpkt = 0x00; - break; - - default: - bad_pkt: - /* TODO: error out */ - fprintf(stderr, "%s: got a bad packet\n", __FUNCTION__); - break; - } - - csrhci_fifo_wake(s); -} - -static int csrhci_header_len(const uint8_t *pkt) -{ - switch (pkt[0]) { - case H4_CMD_PKT: - return HCI_COMMAND_HDR_SIZE; - case H4_EVT_PKT: - return HCI_EVENT_HDR_SIZE; - case H4_ACL_PKT: - return HCI_ACL_HDR_SIZE; - case H4_SCO_PKT: - return HCI_SCO_HDR_SIZE; - case H4_NEG_PKT: - return pkt[1] + 1; - case H4_ALIVE_PKT: - return 3; - } - - exit(-1); -} - -static int csrhci_data_len(const uint8_t *pkt) -{ - switch (*pkt ++) { - case H4_CMD_PKT: - /* It seems that vendor-specific command packets for H4+ are all - * one byte longer than indicated in the standard header. */ - if (le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode) == 0xfc00) - return (((struct hci_command_hdr *) pkt)->plen + 1) & ~1; - - return ((struct hci_command_hdr *) pkt)->plen; - case H4_EVT_PKT: - return ((struct hci_event_hdr *) pkt)->plen; - case H4_ACL_PKT: - return le16_to_cpu(((struct hci_acl_hdr *) pkt)->dlen); - case H4_SCO_PKT: - return ((struct hci_sco_hdr *) pkt)->dlen; - case H4_NEG_PKT: - case H4_ALIVE_PKT: - return 0; - } - - exit(-1); -} - -static int csrhci_write(struct CharDriverState *chr, - const uint8_t *buf, int len) -{ - struct csrhci_s *s = (struct csrhci_s *) chr->opaque; - int plen = s->in_len; - - if (!s->enable) - return 0; - - s->in_len += len; - memcpy(s->inpkt + plen, buf, len); - - while (1) { - if (s->in_len >= 2 && plen < 2) - s->in_hdr = csrhci_header_len(s->inpkt) + 1; - - if (s->in_len >= s->in_hdr && plen < s->in_hdr) - s->in_data = csrhci_data_len(s->inpkt) + s->in_hdr; - - if (s->in_len >= s->in_data) { - csrhci_in_packet(s, s->inpkt); - - memmove(s->inpkt, s->inpkt + s->in_len, s->in_len - s->in_data); - s->in_len -= s->in_data; - s->in_hdr = INT_MAX; - s->in_data = INT_MAX; - plen = 0; - } else - break; - } - - return len; -} - -static void csrhci_out_hci_packet_event(void *opaque, - const uint8_t *data, int len) -{ - struct csrhci_s *s = (struct csrhci_s *) opaque; - uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1); /* Align */ - - *pkt ++ = H4_EVT_PKT; - memcpy(pkt, data, len); - - csrhci_fifo_wake(s); -} - -static void csrhci_out_hci_packet_acl(void *opaque, - const uint8_t *data, int len) -{ - struct csrhci_s *s = (struct csrhci_s *) opaque; - uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1); /* Align */ - - *pkt ++ = H4_ACL_PKT; - pkt[len & ~1] = 0; - memcpy(pkt, data, len); - - csrhci_fifo_wake(s); -} - -static int csrhci_ioctl(struct CharDriverState *chr, int cmd, void *arg) -{ - QEMUSerialSetParams *ssp; - struct csrhci_s *s = (struct csrhci_s *) chr->opaque; - int prev_state = s->modem_state; - - switch (cmd) { - case CHR_IOCTL_SERIAL_SET_PARAMS: - ssp = (QEMUSerialSetParams *) arg; - s->baud_delay = NANOSECONDS_PER_SECOND / ssp->speed; - /* Moments later... (but shorter than 100ms) */ - s->modem_state |= CHR_TIOCM_CTS; - break; - - case CHR_IOCTL_SERIAL_GET_TIOCM: - *(int *) arg = s->modem_state; - break; - - case CHR_IOCTL_SERIAL_SET_TIOCM: - s->modem_state = *(int *) arg; - if (~s->modem_state & prev_state & CHR_TIOCM_RTS) - s->modem_state &= ~CHR_TIOCM_CTS; - break; - - default: - return -ENOTSUP; - } - return 0; -} - -static void csrhci_reset(struct csrhci_s *s) -{ - s->out_len = 0; - s->out_size = FIFO_LEN; - s->in_len = 0; - s->baud_delay = NANOSECONDS_PER_SECOND; - s->enable = 0; - s->in_hdr = INT_MAX; - s->in_data = INT_MAX; - - s->modem_state = 0; - /* After a while... (but sooner than 10ms) */ - s->modem_state |= CHR_TIOCM_CTS; - - memset(&s->bd_addr, 0, sizeof(bdaddr_t)); -} - -static void csrhci_out_tick(void *opaque) -{ - csrhci_fifo_wake((struct csrhci_s *) opaque); -} - -static void csrhci_pins(void *opaque, int line, int level) -{ - struct csrhci_s *s = (struct csrhci_s *) opaque; - int state = s->pin_state; - - s->pin_state &= ~(1 << line); - s->pin_state |= (!!level) << line; - - if ((state & ~s->pin_state) & (1 << csrhci_pin_reset)) { - /* TODO: Disappear from lower layers */ - csrhci_reset(s); - } - - if (s->pin_state == 3 && state != 3) { - s->enable = 1; - /* TODO: Wake lower layers up */ - } -} - -qemu_irq *csrhci_pins_get(CharDriverState *chr) -{ - struct csrhci_s *s = (struct csrhci_s *) chr->opaque; - - return s->pins; -} - -CharDriverState *uart_hci_init(qemu_irq wakeup) -{ - struct csrhci_s *s = (struct csrhci_s *) - g_malloc0(sizeof(struct csrhci_s)); - - s->chr.opaque = s; - s->chr.chr_write = csrhci_write; - s->chr.chr_ioctl = csrhci_ioctl; - s->chr.avail_connections = 1; - - s->hci = qemu_next_hci(); - s->hci->opaque = s; - s->hci->evt_recv = csrhci_out_hci_packet_event; - s->hci->acl_recv = csrhci_out_hci_packet_acl; - - s->out_tm = timer_new_ns(QEMU_CLOCK_VIRTUAL, csrhci_out_tick, s); - s->pins = qemu_allocate_irqs(csrhci_pins, s, __csrhci_pins); - csrhci_reset(s); - - return &s->chr; -} diff --git a/qemu/hw/bt/hci.c b/qemu/hw/bt/hci.c deleted file mode 100644 index 7d5220509..000000000 --- a/qemu/hw/bt/hci.c +++ /dev/null @@ -1,2272 +0,0 @@ -/* - * QEMU Bluetooth HCI logic. - * - * Copyright (C) 2007 OpenMoko, Inc. - * Copyright (C) 2008 Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "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); - void (*evt_submit)(void *opaque, int len); - void *opaque; - uint8_t evt_buf[256]; - - uint8_t acl_buf[4096]; - int acl_len; - - uint16_t asb_handle; - uint16_t psb_handle; - - int last_cmd; /* Note: Always little-endian */ - - struct bt_device_s *conn_req_host; - - struct { - int inquire; - int periodic; - int responses_left; - int responses; - QEMUTimer *inquiry_done; - QEMUTimer *inquiry_next; - int inquiry_length; - int inquiry_period; - int inquiry_mode; - -#define HCI_HANDLE_OFFSET 0x20 -#define HCI_HANDLES_MAX 0x10 - struct bt_hci_master_link_s { - struct bt_link_s *link; - void (*lmp_acl_data)(struct bt_link_s *link, - const uint8_t *data, int start, int len); - QEMUTimer *acl_mode_timer; - } handle[HCI_HANDLES_MAX]; - uint32_t role_bmp; - int last_handle; - int connecting; - bdaddr_t awaiting_bdaddr[HCI_HANDLES_MAX]; - } lm; - - uint8_t event_mask[8]; - uint16_t voice_setting; /* Notw: Always little-endian */ - uint16_t conn_accept_tout; - QEMUTimer *conn_accept_timer; - - struct HCIInfo info; - struct bt_device_s device; - - Error *replay_blocker; -}; - -#define DEFAULT_RSSI_DBM 20 - -#define hci_from_info(ptr) container_of((ptr), struct bt_hci_s, info) -#define hci_from_device(ptr) container_of((ptr), struct bt_hci_s, device) - -struct bt_hci_link_s { - struct bt_link_s btlink; - uint16_t handle; /* Local */ -}; - -/* LMP layer emulation */ -#if 0 -static void bt_submit_lmp(struct bt_device_s *bt, int length, uint8_t *data) -{ - int resp, resplen, error, op, tr; - uint8_t respdata[17]; - - if (length < 1) - return; - - tr = *data & 1; - op = *(data ++) >> 1; - resp = LMP_ACCEPTED; - resplen = 2; - respdata[1] = op; - error = 0; - length --; - - if (op >= 0x7c) { /* Extended opcode */ - op |= *(data ++) << 8; - resp = LMP_ACCEPTED_EXT; - resplen = 4; - respdata[0] = op >> 8; - respdata[1] = op & 0xff; - length --; - } - - switch (op) { - case LMP_ACCEPTED: - /* data[0] Op code - */ - if (length < 1) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - case LMP_ACCEPTED_EXT: - /* data[0] Escape op code - * data[1] Extended op code - */ - if (length < 2) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - case LMP_NOT_ACCEPTED: - /* data[0] Op code - * data[1] Error code - */ - if (length < 2) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - case LMP_NOT_ACCEPTED_EXT: - /* data[0] Op code - * data[1] Extended op code - * data[2] Error code - */ - if (length < 3) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - case LMP_HOST_CONNECTION_REQ: - break; - - case LMP_SETUP_COMPLETE: - resp = LMP_SETUP_COMPLETE; - resplen = 1; - bt->setup = 1; - break; - - case LMP_DETACH: - /* data[0] Error code - */ - if (length < 1) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - bt->setup = 0; - resp = 0; - break; - - case LMP_SUPERVISION_TIMEOUT: - /* data[0,1] Supervision timeout - */ - if (length < 2) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - case LMP_QUALITY_OF_SERVICE: - resp = 0; - /* Fall through */ - case LMP_QOS_REQ: - /* data[0,1] Poll interval - * data[2] N(BC) - */ - if (length < 3) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - break; - - case LMP_MAX_SLOT: - resp = 0; - /* Fall through */ - case LMP_MAX_SLOT_REQ: - /* data[0] Max slots - */ - if (length < 1) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - break; - - case LMP_AU_RAND: - case LMP_IN_RAND: - case LMP_COMB_KEY: - /* data[0-15] Random number - */ - if (length < 16) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - if (op == LMP_AU_RAND) { - if (bt->key_present) { - resp = LMP_SRES; - resplen = 5; - /* XXX: [Part H] Section 6.1 on page 801 */ - } else { - error = HCI_PIN_OR_KEY_MISSING; - goto not_accepted; - } - } else if (op == LMP_IN_RAND) { - error = HCI_PAIRING_NOT_ALLOWED; - goto not_accepted; - } else { - /* XXX: [Part H] Section 3.2 on page 779 */ - resp = LMP_UNIT_KEY; - resplen = 17; - memcpy(respdata + 1, bt->key, 16); - - error = HCI_UNIT_LINK_KEY_USED; - goto not_accepted; - } - break; - - case LMP_UNIT_KEY: - /* data[0-15] Key - */ - if (length < 16) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - memcpy(bt->key, data, 16); - bt->key_present = 1; - break; - - case LMP_SRES: - /* data[0-3] Authentication response - */ - if (length < 4) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - break; - - case LMP_CLKOFFSET_REQ: - resp = LMP_CLKOFFSET_RES; - resplen = 3; - respdata[1] = 0x33; - respdata[2] = 0x33; - break; - - case LMP_CLKOFFSET_RES: - /* data[0,1] Clock offset - * (Slave to master only) - */ - if (length < 2) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - break; - - case LMP_VERSION_REQ: - case LMP_VERSION_RES: - /* data[0] VersNr - * data[1,2] CompId - * data[3,4] SubVersNr - */ - if (length < 5) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - if (op == LMP_VERSION_REQ) { - resp = LMP_VERSION_RES; - resplen = 6; - respdata[1] = 0x20; - respdata[2] = 0xff; - respdata[3] = 0xff; - respdata[4] = 0xff; - respdata[5] = 0xff; - } else - resp = 0; - break; - - case LMP_FEATURES_REQ: - case LMP_FEATURES_RES: - /* data[0-7] Features - */ - if (length < 8) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - if (op == LMP_FEATURES_REQ) { - resp = LMP_FEATURES_RES; - resplen = 9; - respdata[1] = (bt->lmp_caps >> 0) & 0xff; - respdata[2] = (bt->lmp_caps >> 8) & 0xff; - respdata[3] = (bt->lmp_caps >> 16) & 0xff; - respdata[4] = (bt->lmp_caps >> 24) & 0xff; - respdata[5] = (bt->lmp_caps >> 32) & 0xff; - respdata[6] = (bt->lmp_caps >> 40) & 0xff; - respdata[7] = (bt->lmp_caps >> 48) & 0xff; - respdata[8] = (bt->lmp_caps >> 56) & 0xff; - } else - resp = 0; - break; - - case LMP_NAME_REQ: - /* data[0] Name offset - */ - if (length < 1) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = LMP_NAME_RES; - resplen = 17; - respdata[1] = data[0]; - respdata[2] = strlen(bt->lmp_name); - memset(respdata + 3, 0x00, 14); - if (respdata[2] > respdata[1]) - memcpy(respdata + 3, bt->lmp_name + respdata[1], - respdata[2] - respdata[1]); - break; - - case LMP_NAME_RES: - /* data[0] Name offset - * data[1] Name length - * data[2-15] Name fragment - */ - if (length < 16) { - error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE; - goto not_accepted; - } - resp = 0; - break; - - default: - error = HCI_UNKNOWN_LMP_PDU; - /* Fall through */ - not_accepted: - if (op >> 8) { - resp = LMP_NOT_ACCEPTED_EXT; - resplen = 5; - respdata[0] = op >> 8; - respdata[1] = op & 0xff; - respdata[2] = error; - } else { - resp = LMP_NOT_ACCEPTED; - resplen = 3; - respdata[0] = op & 0xff; - respdata[1] = error; - } - } - - if (resp == 0) - return; - - if (resp >> 8) { - respdata[0] = resp >> 8; - respdata[1] = resp & 0xff; - } else - respdata[0] = resp & 0xff; - - respdata[0] <<= 1; - respdata[0] |= tr; -} - -static void bt_submit_raw_acl(struct bt_piconet_s *net, int length, uint8_t *data) -{ - struct bt_device_s *slave; - if (length < 1) - return; - - slave = 0; -#if 0 - slave = net->slave; -#endif - - switch (data[0] & 3) { - case LLID_ACLC: - bt_submit_lmp(slave, length - 1, data + 1); - break; - case LLID_ACLU_START: -#if 0 - bt_sumbit_l2cap(slave, length - 1, data + 1, (data[0] >> 2) & 1); - breka; -#endif - default: - case LLID_ACLU_CONT: - break; - } -} -#endif - -/* HCI layer emulation */ - -/* Note: we could ignore endiannes because unswapped handles will still - * be valid as connection identifiers for the guest - they don't have to - * be continuously allocated. We do it though, to preserve similar - * behaviour between hosts. Some things, like the BD_ADDR cannot be - * preserved though (for example if a real hci is used). */ -#ifdef HOST_WORDS_BIGENDIAN -# define HNDL(raw) bswap16(raw) -#else -# define HNDL(raw) (raw) -#endif - -static const uint8_t bt_event_reserved_mask[8] = { - 0xff, 0x9f, 0xfb, 0xff, 0x07, 0x18, 0x00, 0x00, -}; - - -static void null_hci_send(struct HCIInfo *hci, const uint8_t *data, int len) -{ -} - -static int null_hci_addr_set(struct HCIInfo *hci, const uint8_t *bd_addr) -{ - return -ENOTSUP; -} - -struct HCIInfo null_hci = { - .cmd_send = null_hci_send, - .sco_send = null_hci_send, - .acl_send = null_hci_send, - .bdaddr_set = null_hci_addr_set, -}; - - -static inline uint8_t *bt_hci_event_start(struct bt_hci_s *hci, - int evt, int len) -{ - uint8_t *packet, mask; - int mask_byte; - - if (len > 255) { - fprintf(stderr, "%s: HCI event params too long (%ib)\n", - __FUNCTION__, len); - exit(-1); - } - - mask_byte = (evt - 1) >> 3; - mask = 1 << ((evt - 1) & 3); - if (mask & bt_event_reserved_mask[mask_byte] & ~hci->event_mask[mask_byte]) - return NULL; - - packet = hci->evt_packet(hci->opaque); - packet[0] = evt; - packet[1] = len; - - return &packet[2]; -} - -static inline void bt_hci_event(struct bt_hci_s *hci, int evt, - void *params, int len) -{ - uint8_t *packet = bt_hci_event_start(hci, evt, len); - - if (!packet) - return; - - if (len) - memcpy(packet, params, len); - - hci->evt_submit(hci->opaque, len + 2); -} - -static inline void bt_hci_event_status(struct bt_hci_s *hci, int status) -{ - evt_cmd_status params = { - .status = status, - .ncmd = 1, - .opcode = hci->last_cmd, - }; - - bt_hci_event(hci, EVT_CMD_STATUS, ¶ms, EVT_CMD_STATUS_SIZE); -} - -static inline void bt_hci_event_complete(struct bt_hci_s *hci, - void *ret, int len) -{ - uint8_t *packet = bt_hci_event_start(hci, EVT_CMD_COMPLETE, - len + EVT_CMD_COMPLETE_SIZE); - evt_cmd_complete *params = (evt_cmd_complete *) packet; - - if (!packet) - return; - - params->ncmd = 1; - params->opcode = hci->last_cmd; - if (len) - memcpy(&packet[EVT_CMD_COMPLETE_SIZE], ret, len); - - hci->evt_submit(hci->opaque, len + EVT_CMD_COMPLETE_SIZE + 2); -} - -static void bt_hci_inquiry_done(void *opaque) -{ - struct bt_hci_s *hci = (struct bt_hci_s *) opaque; - uint8_t status = HCI_SUCCESS; - - if (!hci->lm.periodic) - hci->lm.inquire = 0; - - /* The specification is inconsistent about this one. Page 565 reads - * "The event parameters of Inquiry Complete event will have a summary - * of the result from the Inquiry process, which reports the number of - * nearby Bluetooth devices that responded [so hci->responses].", but - * Event Parameters (see page 729) has only Status. */ - bt_hci_event(hci, EVT_INQUIRY_COMPLETE, &status, 1); -} - -static void bt_hci_inquiry_result_standard(struct bt_hci_s *hci, - struct bt_device_s *slave) -{ - inquiry_info params = { - .num_responses = 1, - .bdaddr = BAINIT(&slave->bd_addr), - .pscan_rep_mode = 0x00, /* R0 */ - .pscan_period_mode = 0x00, /* P0 - deprecated */ - .pscan_mode = 0x00, /* Standard scan - deprecated */ - .dev_class[0] = slave->class[0], - .dev_class[1] = slave->class[1], - .dev_class[2] = slave->class[2], - /* TODO: return the clkoff *differenece* */ - .clock_offset = slave->clkoff, /* Note: no swapping */ - }; - - bt_hci_event(hci, EVT_INQUIRY_RESULT, ¶ms, INQUIRY_INFO_SIZE); -} - -static void bt_hci_inquiry_result_with_rssi(struct bt_hci_s *hci, - struct bt_device_s *slave) -{ - inquiry_info_with_rssi params = { - .num_responses = 1, - .bdaddr = BAINIT(&slave->bd_addr), - .pscan_rep_mode = 0x00, /* R0 */ - .pscan_period_mode = 0x00, /* P0 - deprecated */ - .dev_class[0] = slave->class[0], - .dev_class[1] = slave->class[1], - .dev_class[2] = slave->class[2], - /* TODO: return the clkoff *differenece* */ - .clock_offset = slave->clkoff, /* Note: no swapping */ - .rssi = DEFAULT_RSSI_DBM, - }; - - bt_hci_event(hci, EVT_INQUIRY_RESULT_WITH_RSSI, - ¶ms, INQUIRY_INFO_WITH_RSSI_SIZE); -} - -static void bt_hci_inquiry_result(struct bt_hci_s *hci, - struct bt_device_s *slave) -{ - if (!slave->inquiry_scan || !hci->lm.responses_left) - return; - - hci->lm.responses_left --; - hci->lm.responses ++; - - switch (hci->lm.inquiry_mode) { - case 0x00: - bt_hci_inquiry_result_standard(hci, slave); - return; - case 0x01: - bt_hci_inquiry_result_with_rssi(hci, slave); - return; - default: - fprintf(stderr, "%s: bad inquiry mode %02x\n", __FUNCTION__, - hci->lm.inquiry_mode); - exit(-1); - } -} - -static void bt_hci_mod_timer_1280ms(QEMUTimer *timer, int period) -{ - timer_mod(timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (uint64_t)(period << 7) * 10000000); -} - -static void bt_hci_inquiry_start(struct bt_hci_s *hci, int length) -{ - struct bt_device_s *slave; - - hci->lm.inquiry_length = length; - for (slave = hci->device.net->slave; slave; slave = slave->next) - /* Don't uncover ourselves. */ - if (slave != &hci->device) - bt_hci_inquiry_result(hci, slave); - - /* TODO: register for a callback on a new device's addition to the - * scatternet so that if it's added before inquiry_length expires, - * an Inquiry Result is generated immediately. Alternatively re-loop - * through the devices on the inquiry_length expiration and report - * devices not seen before. */ - if (hci->lm.responses_left) - bt_hci_mod_timer_1280ms(hci->lm.inquiry_done, hci->lm.inquiry_length); - else - bt_hci_inquiry_done(hci); - - if (hci->lm.periodic) - bt_hci_mod_timer_1280ms(hci->lm.inquiry_next, hci->lm.inquiry_period); -} - -static void bt_hci_inquiry_next(void *opaque) -{ - struct bt_hci_s *hci = (struct bt_hci_s *) opaque; - - hci->lm.responses_left += hci->lm.responses; - hci->lm.responses = 0; - bt_hci_inquiry_start(hci, hci->lm.inquiry_length); -} - -static inline int bt_hci_handle_bad(struct bt_hci_s *hci, uint16_t handle) -{ - return !(handle & HCI_HANDLE_OFFSET) || - handle >= (HCI_HANDLE_OFFSET | HCI_HANDLES_MAX) || - !hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link; -} - -static inline int bt_hci_role_master(struct bt_hci_s *hci, uint16_t handle) -{ - return !!(hci->lm.role_bmp & (1 << (handle & ~HCI_HANDLE_OFFSET))); -} - -static inline struct bt_device_s *bt_hci_remote_dev(struct bt_hci_s *hci, - uint16_t handle) -{ - struct bt_link_s *link = hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link; - - return bt_hci_role_master(hci, handle) ? link->slave : link->host; -} - -static void bt_hci_mode_tick(void *opaque); -static void bt_hci_lmp_link_establish(struct bt_hci_s *hci, - struct bt_link_s *link, int master) -{ - hci->lm.handle[hci->lm.last_handle].link = link; - - if (master) { - /* We are the master side of an ACL link */ - hci->lm.role_bmp |= 1 << hci->lm.last_handle; - - hci->lm.handle[hci->lm.last_handle].lmp_acl_data = - link->slave->lmp_acl_data; - } else { - /* We are the slave side of an ACL link */ - hci->lm.role_bmp &= ~(1 << hci->lm.last_handle); - - hci->lm.handle[hci->lm.last_handle].lmp_acl_data = - link->host->lmp_acl_resp; - } - - /* Mode */ - if (master) { - link->acl_mode = acl_active; - hci->lm.handle[hci->lm.last_handle].acl_mode_timer = - timer_new_ns(QEMU_CLOCK_VIRTUAL, bt_hci_mode_tick, link); - } -} - -static void bt_hci_lmp_link_teardown(struct bt_hci_s *hci, uint16_t handle) -{ - handle &= ~HCI_HANDLE_OFFSET; - hci->lm.handle[handle].link = NULL; - - if (bt_hci_role_master(hci, handle)) { - timer_del(hci->lm.handle[handle].acl_mode_timer); - timer_free(hci->lm.handle[handle].acl_mode_timer); - } -} - -static int bt_hci_connect(struct bt_hci_s *hci, bdaddr_t *bdaddr) -{ - struct bt_device_s *slave; - struct bt_link_s link; - - for (slave = hci->device.net->slave; slave; slave = slave->next) - if (slave->page_scan && !bacmp(&slave->bd_addr, bdaddr)) - break; - if (!slave || slave == &hci->device) - return -ENODEV; - - bacpy(&hci->lm.awaiting_bdaddr[hci->lm.connecting ++], &slave->bd_addr); - - link.slave = slave; - link.host = &hci->device; - link.slave->lmp_connection_request(&link); /* Always last */ - - return 0; -} - -static void bt_hci_connection_reject(struct bt_hci_s *hci, - struct bt_device_s *host, uint8_t because) -{ - struct bt_link_s link = { - .slave = &hci->device, - .host = host, - /* Rest uninitialised */ - }; - - host->reject_reason = because; - host->lmp_connection_complete(&link); -} - -static void bt_hci_connection_reject_event(struct bt_hci_s *hci, - bdaddr_t *bdaddr) -{ - evt_conn_complete params; - - params.status = HCI_NO_CONNECTION; - params.handle = 0; - bacpy(¶ms.bdaddr, bdaddr); - params.link_type = ACL_LINK; - params.encr_mode = 0x00; /* Encryption not required */ - bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, EVT_CONN_COMPLETE_SIZE); -} - -static void bt_hci_connection_accept(struct bt_hci_s *hci, - struct bt_device_s *host) -{ - struct bt_hci_link_s *link = g_malloc0(sizeof(struct bt_hci_link_s)); - evt_conn_complete params; - uint16_t handle; - uint8_t status = HCI_SUCCESS; - int tries = HCI_HANDLES_MAX; - - /* Make a connection handle */ - do { - while (hci->lm.handle[++ hci->lm.last_handle].link && -- tries) - hci->lm.last_handle &= HCI_HANDLES_MAX - 1; - handle = hci->lm.last_handle | HCI_HANDLE_OFFSET; - } while ((handle == hci->asb_handle || handle == hci->psb_handle) && - tries); - - if (!tries) { - g_free(link); - bt_hci_connection_reject(hci, host, HCI_REJECTED_LIMITED_RESOURCES); - status = HCI_NO_CONNECTION; - goto complete; - } - - link->btlink.slave = &hci->device; - link->btlink.host = host; - link->handle = handle; - - /* Link established */ - bt_hci_lmp_link_establish(hci, &link->btlink, 0); - -complete: - params.status = status; - params.handle = HNDL(handle); - bacpy(¶ms.bdaddr, &host->bd_addr); - params.link_type = ACL_LINK; - params.encr_mode = 0x00; /* Encryption not required */ - bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, EVT_CONN_COMPLETE_SIZE); - - /* Neets to be done at the very end because it can trigger a (nested) - * disconnected, in case the other and had cancelled the request - * locally. */ - if (status == HCI_SUCCESS) { - host->reject_reason = 0; - host->lmp_connection_complete(&link->btlink); - } -} - -static void bt_hci_lmp_connection_request(struct bt_link_s *link) -{ - struct bt_hci_s *hci = hci_from_device(link->slave); - evt_conn_request params; - - if (hci->conn_req_host) { - bt_hci_connection_reject(hci, link->host, - HCI_REJECTED_LIMITED_RESOURCES); - return; - } - hci->conn_req_host = link->host; - /* TODO: if masked and auto-accept, then auto-accept, - * if masked and not auto-accept, then auto-reject */ - /* TODO: kick the hci->conn_accept_timer, timeout after - * hci->conn_accept_tout * 0.625 msec */ - - bacpy(¶ms.bdaddr, &link->host->bd_addr); - memcpy(¶ms.dev_class, &link->host->class, sizeof(params.dev_class)); - params.link_type = ACL_LINK; - bt_hci_event(hci, EVT_CONN_REQUEST, ¶ms, EVT_CONN_REQUEST_SIZE); -} - -static void bt_hci_conn_accept_timeout(void *opaque) -{ - struct bt_hci_s *hci = (struct bt_hci_s *) opaque; - - if (!hci->conn_req_host) - /* Already accepted or rejected. If the other end cancelled the - * connection request then we still have to reject or accept it - * and then we'll get a disconnect. */ - return; - - /* TODO */ -} - -/* Remove from the list of devices which we wanted to connect to and - * are awaiting a response from. If the callback sees a response from - * a device which is not on the list it will assume it's a connection - * that's been cancelled by the host in the meantime and immediately - * try to detach the link and send a Connection Complete. */ -static int bt_hci_lmp_connection_ready(struct bt_hci_s *hci, - bdaddr_t *bdaddr) -{ - int i; - - for (i = 0; i < hci->lm.connecting; i ++) - if (!bacmp(&hci->lm.awaiting_bdaddr[i], bdaddr)) { - if (i < -- hci->lm.connecting) - bacpy(&hci->lm.awaiting_bdaddr[i], - &hci->lm.awaiting_bdaddr[hci->lm.connecting]); - return 0; - } - - return 1; -} - -static void bt_hci_lmp_connection_complete(struct bt_link_s *link) -{ - struct bt_hci_s *hci = hci_from_device(link->host); - evt_conn_complete params; - uint16_t handle; - uint8_t status = HCI_SUCCESS; - int tries = HCI_HANDLES_MAX; - - if (bt_hci_lmp_connection_ready(hci, &link->slave->bd_addr)) { - if (!hci->device.reject_reason) - link->slave->lmp_disconnect_slave(link); - handle = 0; - status = HCI_NO_CONNECTION; - goto complete; - } - - if (hci->device.reject_reason) { - handle = 0; - status = hci->device.reject_reason; - goto complete; - } - - /* Make a connection handle */ - do { - while (hci->lm.handle[++ hci->lm.last_handle].link && -- tries) - hci->lm.last_handle &= HCI_HANDLES_MAX - 1; - handle = hci->lm.last_handle | HCI_HANDLE_OFFSET; - } while ((handle == hci->asb_handle || handle == hci->psb_handle) && - tries); - - if (!tries) { - link->slave->lmp_disconnect_slave(link); - status = HCI_NO_CONNECTION; - goto complete; - } - - /* Link established */ - link->handle = handle; - bt_hci_lmp_link_establish(hci, link, 1); - -complete: - params.status = status; - params.handle = HNDL(handle); - params.link_type = ACL_LINK; - bacpy(¶ms.bdaddr, &link->slave->bd_addr); - params.encr_mode = 0x00; /* Encryption not required */ - bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, EVT_CONN_COMPLETE_SIZE); -} - -static void bt_hci_disconnect(struct bt_hci_s *hci, - uint16_t handle, int reason) -{ - struct bt_link_s *btlink = - hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link; - struct bt_hci_link_s *link; - evt_disconn_complete params; - - if (bt_hci_role_master(hci, handle)) { - btlink->slave->reject_reason = reason; - btlink->slave->lmp_disconnect_slave(btlink); - /* The link pointer is invalid from now on */ - - goto complete; - } - - btlink->host->reject_reason = reason; - btlink->host->lmp_disconnect_master(btlink); - - /* We are the slave, we get to clean this burden */ - link = (struct bt_hci_link_s *) btlink; - g_free(link); - -complete: - bt_hci_lmp_link_teardown(hci, handle); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - params.reason = HCI_CONNECTION_TERMINATED; - bt_hci_event(hci, EVT_DISCONN_COMPLETE, - ¶ms, EVT_DISCONN_COMPLETE_SIZE); -} - -/* TODO: use only one function */ -static void bt_hci_lmp_disconnect_host(struct bt_link_s *link) -{ - struct bt_hci_s *hci = hci_from_device(link->host); - uint16_t handle = link->handle; - evt_disconn_complete params; - - bt_hci_lmp_link_teardown(hci, handle); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - params.reason = hci->device.reject_reason; - bt_hci_event(hci, EVT_DISCONN_COMPLETE, - ¶ms, EVT_DISCONN_COMPLETE_SIZE); -} - -static void bt_hci_lmp_disconnect_slave(struct bt_link_s *btlink) -{ - struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink; - struct bt_hci_s *hci = hci_from_device(btlink->slave); - uint16_t handle = link->handle; - evt_disconn_complete params; - - g_free(link); - - bt_hci_lmp_link_teardown(hci, handle); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - params.reason = hci->device.reject_reason; - bt_hci_event(hci, EVT_DISCONN_COMPLETE, - ¶ms, EVT_DISCONN_COMPLETE_SIZE); -} - -static int bt_hci_name_req(struct bt_hci_s *hci, bdaddr_t *bdaddr) -{ - struct bt_device_s *slave; - evt_remote_name_req_complete params; - - for (slave = hci->device.net->slave; slave; slave = slave->next) - if (slave->page_scan && !bacmp(&slave->bd_addr, bdaddr)) - break; - if (!slave) - return -ENODEV; - - bt_hci_event_status(hci, HCI_SUCCESS); - - params.status = HCI_SUCCESS; - bacpy(¶ms.bdaddr, &slave->bd_addr); - pstrcpy(params.name, sizeof(params.name), slave->lmp_name ?: ""); - bt_hci_event(hci, EVT_REMOTE_NAME_REQ_COMPLETE, - ¶ms, EVT_REMOTE_NAME_REQ_COMPLETE_SIZE); - - return 0; -} - -static int bt_hci_features_req(struct bt_hci_s *hci, uint16_t handle) -{ - struct bt_device_s *slave; - evt_read_remote_features_complete params; - - if (bt_hci_handle_bad(hci, handle)) - return -ENODEV; - - slave = bt_hci_remote_dev(hci, handle); - - bt_hci_event_status(hci, HCI_SUCCESS); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - params.features[0] = (slave->lmp_caps >> 0) & 0xff; - params.features[1] = (slave->lmp_caps >> 8) & 0xff; - params.features[2] = (slave->lmp_caps >> 16) & 0xff; - params.features[3] = (slave->lmp_caps >> 24) & 0xff; - params.features[4] = (slave->lmp_caps >> 32) & 0xff; - params.features[5] = (slave->lmp_caps >> 40) & 0xff; - params.features[6] = (slave->lmp_caps >> 48) & 0xff; - params.features[7] = (slave->lmp_caps >> 56) & 0xff; - bt_hci_event(hci, EVT_READ_REMOTE_FEATURES_COMPLETE, - ¶ms, EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE); - - return 0; -} - -static int bt_hci_version_req(struct bt_hci_s *hci, uint16_t handle) -{ - evt_read_remote_version_complete params; - - if (bt_hci_handle_bad(hci, handle)) - return -ENODEV; - - bt_hci_remote_dev(hci, handle); - - bt_hci_event_status(hci, HCI_SUCCESS); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - params.lmp_ver = 0x03; - params.manufacturer = cpu_to_le16(0xa000); - params.lmp_subver = cpu_to_le16(0xa607); - bt_hci_event(hci, EVT_READ_REMOTE_VERSION_COMPLETE, - ¶ms, EVT_READ_REMOTE_VERSION_COMPLETE_SIZE); - - return 0; -} - -static int bt_hci_clkoffset_req(struct bt_hci_s *hci, uint16_t handle) -{ - struct bt_device_s *slave; - evt_read_clock_offset_complete params; - - if (bt_hci_handle_bad(hci, handle)) - return -ENODEV; - - slave = bt_hci_remote_dev(hci, handle); - - bt_hci_event_status(hci, HCI_SUCCESS); - - params.status = HCI_SUCCESS; - params.handle = HNDL(handle); - /* TODO: return the clkoff *differenece* */ - params.clock_offset = slave->clkoff; /* Note: no swapping */ - bt_hci_event(hci, EVT_READ_CLOCK_OFFSET_COMPLETE, - ¶ms, EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE); - - return 0; -} - -static void bt_hci_event_mode(struct bt_hci_s *hci, struct bt_link_s *link, - uint16_t handle) -{ - evt_mode_change params = { - .status = HCI_SUCCESS, - .handle = HNDL(handle), - .mode = link->acl_mode, - .interval = cpu_to_le16(link->acl_interval), - }; - - bt_hci_event(hci, EVT_MODE_CHANGE, ¶ms, EVT_MODE_CHANGE_SIZE); -} - -static void bt_hci_lmp_mode_change_master(struct bt_hci_s *hci, - struct bt_link_s *link, int mode, uint16_t interval) -{ - link->acl_mode = mode; - link->acl_interval = interval; - - bt_hci_event_mode(hci, link, link->handle); - - link->slave->lmp_mode_change(link); -} - -static void bt_hci_lmp_mode_change_slave(struct bt_link_s *btlink) -{ - struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink; - struct bt_hci_s *hci = hci_from_device(btlink->slave); - - bt_hci_event_mode(hci, btlink, link->handle); -} - -static int bt_hci_mode_change(struct bt_hci_s *hci, uint16_t handle, - int interval, int mode) -{ - struct bt_hci_master_link_s *link; - - if (bt_hci_handle_bad(hci, handle) || !bt_hci_role_master(hci, handle)) - return -ENODEV; - - link = &hci->lm.handle[handle & ~HCI_HANDLE_OFFSET]; - if (link->link->acl_mode != acl_active) { - bt_hci_event_status(hci, HCI_COMMAND_DISALLOWED); - return 0; - } - - bt_hci_event_status(hci, HCI_SUCCESS); - - timer_mod(link->acl_mode_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - ((uint64_t)interval * 625) * 1000); - bt_hci_lmp_mode_change_master(hci, link->link, mode, interval); - - return 0; -} - -static int bt_hci_mode_cancel(struct bt_hci_s *hci, uint16_t handle, int mode) -{ - struct bt_hci_master_link_s *link; - - if (bt_hci_handle_bad(hci, handle) || !bt_hci_role_master(hci, handle)) - return -ENODEV; - - link = &hci->lm.handle[handle & ~HCI_HANDLE_OFFSET]; - if (link->link->acl_mode != mode) { - bt_hci_event_status(hci, HCI_COMMAND_DISALLOWED); - - return 0; - } - - bt_hci_event_status(hci, HCI_SUCCESS); - - timer_del(link->acl_mode_timer); - bt_hci_lmp_mode_change_master(hci, link->link, acl_active, 0); - - return 0; -} - -static void bt_hci_mode_tick(void *opaque) -{ - struct bt_link_s *link = opaque; - struct bt_hci_s *hci = hci_from_device(link->host); - - bt_hci_lmp_mode_change_master(hci, link, acl_active, 0); -} - -static void bt_hci_reset(struct bt_hci_s *hci) -{ - hci->acl_len = 0; - hci->last_cmd = 0; - hci->lm.connecting = 0; - - hci->event_mask[0] = 0xff; - hci->event_mask[1] = 0xff; - hci->event_mask[2] = 0xff; - hci->event_mask[3] = 0xff; - hci->event_mask[4] = 0xff; - hci->event_mask[5] = 0x1f; - hci->event_mask[6] = 0x00; - hci->event_mask[7] = 0x00; - hci->device.inquiry_scan = 0; - hci->device.page_scan = 0; - g_free((void *) hci->device.lmp_name); - hci->device.lmp_name = NULL; - hci->device.class[0] = 0x00; - hci->device.class[1] = 0x00; - hci->device.class[2] = 0x00; - hci->voice_setting = 0x0000; - hci->conn_accept_tout = 0x1f40; - hci->lm.inquiry_mode = 0x00; - - hci->psb_handle = 0x000; - hci->asb_handle = 0x000; - - /* XXX: timer_del(sl->acl_mode_timer); for all links */ - timer_del(hci->lm.inquiry_done); - timer_del(hci->lm.inquiry_next); - timer_del(hci->conn_accept_timer); -} - -static void bt_hci_read_local_version_rp(struct bt_hci_s *hci) -{ - read_local_version_rp lv = { - .status = HCI_SUCCESS, - .hci_ver = 0x03, - .hci_rev = cpu_to_le16(0xa607), - .lmp_ver = 0x03, - .manufacturer = cpu_to_le16(0xa000), - .lmp_subver = cpu_to_le16(0xa607), - }; - - bt_hci_event_complete(hci, &lv, READ_LOCAL_VERSION_RP_SIZE); -} - -static void bt_hci_read_local_commands_rp(struct bt_hci_s *hci) -{ - read_local_commands_rp lc = { - .status = HCI_SUCCESS, - .commands = { - /* Keep updated! */ - /* Also, keep in sync with hci->device.lmp_caps in bt_new_hci */ - 0xbf, 0x80, 0xf9, 0x03, 0xb2, 0xc0, 0x03, 0xc3, - 0x00, 0x0f, 0x80, 0x00, 0xc0, 0x00, 0xe8, 0x13, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }, - }; - - bt_hci_event_complete(hci, &lc, READ_LOCAL_COMMANDS_RP_SIZE); -} - -static void bt_hci_read_local_features_rp(struct bt_hci_s *hci) -{ - read_local_features_rp lf = { - .status = HCI_SUCCESS, - .features = { - (hci->device.lmp_caps >> 0) & 0xff, - (hci->device.lmp_caps >> 8) & 0xff, - (hci->device.lmp_caps >> 16) & 0xff, - (hci->device.lmp_caps >> 24) & 0xff, - (hci->device.lmp_caps >> 32) & 0xff, - (hci->device.lmp_caps >> 40) & 0xff, - (hci->device.lmp_caps >> 48) & 0xff, - (hci->device.lmp_caps >> 56) & 0xff, - }, - }; - - bt_hci_event_complete(hci, &lf, READ_LOCAL_FEATURES_RP_SIZE); -} - -static void bt_hci_read_local_ext_features_rp(struct bt_hci_s *hci, int page) -{ - read_local_ext_features_rp lef = { - .status = HCI_SUCCESS, - .page_num = page, - .max_page_num = 0x00, - .features = { - /* Keep updated! */ - 0x5f, 0x35, 0x85, 0x7e, 0x9b, 0x19, 0x00, 0x80, - }, - }; - if (page) - memset(lef.features, 0, sizeof(lef.features)); - - bt_hci_event_complete(hci, &lef, READ_LOCAL_EXT_FEATURES_RP_SIZE); -} - -static void bt_hci_read_buffer_size_rp(struct bt_hci_s *hci) -{ - read_buffer_size_rp bs = { - /* This can be made configurable, for one standard USB dongle HCI - * the four values are cpu_to_le16(0x0180), 0x40, - * cpu_to_le16(0x0008), cpu_to_le16(0x0008). */ - .status = HCI_SUCCESS, - .acl_mtu = cpu_to_le16(0x0200), - .sco_mtu = 0, - .acl_max_pkt = cpu_to_le16(0x0001), - .sco_max_pkt = cpu_to_le16(0x0000), - }; - - bt_hci_event_complete(hci, &bs, READ_BUFFER_SIZE_RP_SIZE); -} - -/* Deprecated in V2.0 (page 661) */ -static void bt_hci_read_country_code_rp(struct bt_hci_s *hci) -{ - read_country_code_rp cc ={ - .status = HCI_SUCCESS, - .country_code = 0x00, /* North America & Europe^1 and Japan */ - }; - - bt_hci_event_complete(hci, &cc, READ_COUNTRY_CODE_RP_SIZE); - - /* ^1. Except France, sorry */ -} - -static void bt_hci_read_bd_addr_rp(struct bt_hci_s *hci) -{ - read_bd_addr_rp ba = { - .status = HCI_SUCCESS, - .bdaddr = BAINIT(&hci->device.bd_addr), - }; - - bt_hci_event_complete(hci, &ba, READ_BD_ADDR_RP_SIZE); -} - -static int bt_hci_link_quality_rp(struct bt_hci_s *hci, uint16_t handle) -{ - read_link_quality_rp lq = { - .status = HCI_SUCCESS, - .handle = HNDL(handle), - .link_quality = 0xff, - }; - - if (bt_hci_handle_bad(hci, handle)) - lq.status = HCI_NO_CONNECTION; - - bt_hci_event_complete(hci, &lq, READ_LINK_QUALITY_RP_SIZE); - return 0; -} - -/* Generate a Command Complete event with only the Status parameter */ -static inline void bt_hci_event_complete_status(struct bt_hci_s *hci, - uint8_t status) -{ - bt_hci_event_complete(hci, &status, 1); -} - -static inline void bt_hci_event_complete_conn_cancel(struct bt_hci_s *hci, - uint8_t status, bdaddr_t *bd_addr) -{ - create_conn_cancel_rp params = { - .status = status, - .bdaddr = BAINIT(bd_addr), - }; - - bt_hci_event_complete(hci, ¶ms, CREATE_CONN_CANCEL_RP_SIZE); -} - -static inline void bt_hci_event_auth_complete(struct bt_hci_s *hci, - uint16_t handle) -{ - evt_auth_complete params = { - .status = HCI_SUCCESS, - .handle = HNDL(handle), - }; - - bt_hci_event(hci, EVT_AUTH_COMPLETE, ¶ms, EVT_AUTH_COMPLETE_SIZE); -} - -static inline void bt_hci_event_encrypt_change(struct bt_hci_s *hci, - uint16_t handle, uint8_t mode) -{ - evt_encrypt_change params = { - .status = HCI_SUCCESS, - .handle = HNDL(handle), - .encrypt = mode, - }; - - bt_hci_event(hci, EVT_ENCRYPT_CHANGE, ¶ms, EVT_ENCRYPT_CHANGE_SIZE); -} - -static inline void bt_hci_event_complete_name_cancel(struct bt_hci_s *hci, - bdaddr_t *bd_addr) -{ - remote_name_req_cancel_rp params = { - .status = HCI_INVALID_PARAMETERS, - .bdaddr = BAINIT(bd_addr), - }; - - bt_hci_event_complete(hci, ¶ms, REMOTE_NAME_REQ_CANCEL_RP_SIZE); -} - -static inline void bt_hci_event_read_remote_ext_features(struct bt_hci_s *hci, - uint16_t handle) -{ - evt_read_remote_ext_features_complete params = { - .status = HCI_UNSUPPORTED_FEATURE, - .handle = HNDL(handle), - /* Rest uninitialised */ - }; - - bt_hci_event(hci, EVT_READ_REMOTE_EXT_FEATURES_COMPLETE, - ¶ms, EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE); -} - -static inline void bt_hci_event_complete_lmp_handle(struct bt_hci_s *hci, - uint16_t handle) -{ - read_lmp_handle_rp params = { - .status = HCI_NO_CONNECTION, - .handle = HNDL(handle), - .reserved = 0, - /* Rest uninitialised */ - }; - - bt_hci_event_complete(hci, ¶ms, READ_LMP_HANDLE_RP_SIZE); -} - -static inline void bt_hci_event_complete_role_discovery(struct bt_hci_s *hci, - int status, uint16_t handle, int master) -{ - role_discovery_rp params = { - .status = status, - .handle = HNDL(handle), - .role = master ? 0x00 : 0x01, - }; - - bt_hci_event_complete(hci, ¶ms, ROLE_DISCOVERY_RP_SIZE); -} - -static inline void bt_hci_event_complete_flush(struct bt_hci_s *hci, - int status, uint16_t handle) -{ - flush_rp params = { - .status = status, - .handle = HNDL(handle), - }; - - bt_hci_event_complete(hci, ¶ms, FLUSH_RP_SIZE); -} - -static inline void bt_hci_event_complete_read_local_name(struct bt_hci_s *hci) -{ - read_local_name_rp params; - params.status = HCI_SUCCESS; - memset(params.name, 0, sizeof(params.name)); - if (hci->device.lmp_name) - pstrcpy(params.name, sizeof(params.name), hci->device.lmp_name); - - bt_hci_event_complete(hci, ¶ms, READ_LOCAL_NAME_RP_SIZE); -} - -static inline void bt_hci_event_complete_read_conn_accept_timeout( - struct bt_hci_s *hci) -{ - read_conn_accept_timeout_rp params = { - .status = HCI_SUCCESS, - .timeout = cpu_to_le16(hci->conn_accept_tout), - }; - - bt_hci_event_complete(hci, ¶ms, READ_CONN_ACCEPT_TIMEOUT_RP_SIZE); -} - -static inline void bt_hci_event_complete_read_scan_enable(struct bt_hci_s *hci) -{ - read_scan_enable_rp params = { - .status = HCI_SUCCESS, - .enable = - (hci->device.inquiry_scan ? SCAN_INQUIRY : 0) | - (hci->device.page_scan ? SCAN_PAGE : 0), - }; - - bt_hci_event_complete(hci, ¶ms, READ_SCAN_ENABLE_RP_SIZE); -} - -static inline void bt_hci_event_complete_read_local_class(struct bt_hci_s *hci) -{ - read_class_of_dev_rp params; - - params.status = HCI_SUCCESS; - memcpy(params.dev_class, hci->device.class, sizeof(params.dev_class)); - - bt_hci_event_complete(hci, ¶ms, READ_CLASS_OF_DEV_RP_SIZE); -} - -static inline void bt_hci_event_complete_voice_setting(struct bt_hci_s *hci) -{ - read_voice_setting_rp params = { - .status = HCI_SUCCESS, - .voice_setting = hci->voice_setting, /* Note: no swapping */ - }; - - bt_hci_event_complete(hci, ¶ms, READ_VOICE_SETTING_RP_SIZE); -} - -static inline void bt_hci_event_complete_read_inquiry_mode( - struct bt_hci_s *hci) -{ - read_inquiry_mode_rp params = { - .status = HCI_SUCCESS, - .mode = hci->lm.inquiry_mode, - }; - - bt_hci_event_complete(hci, ¶ms, READ_INQUIRY_MODE_RP_SIZE); -} - -static inline void bt_hci_event_num_comp_pkts(struct bt_hci_s *hci, - uint16_t handle, int packets) -{ - uint16_t buf[EVT_NUM_COMP_PKTS_SIZE(1) / 2 + 1]; - evt_num_comp_pkts *params = (void *) ((uint8_t *) buf + 1); - - params->num_hndl = 1; - params->connection->handle = HNDL(handle); - params->connection->num_packets = cpu_to_le16(packets); - - bt_hci_event(hci, EVT_NUM_COMP_PKTS, params, EVT_NUM_COMP_PKTS_SIZE(1)); -} - -static void bt_submit_hci(struct HCIInfo *info, - const uint8_t *data, int length) -{ - struct bt_hci_s *hci = hci_from_info(info); - uint16_t cmd; - int paramlen, i; - - if (length < HCI_COMMAND_HDR_SIZE) - goto short_hci; - - memcpy(&hci->last_cmd, data, 2); - - cmd = (data[1] << 8) | data[0]; - paramlen = data[2]; - if (cmd_opcode_ogf(cmd) == 0 || cmd_opcode_ocf(cmd) == 0) /* NOP */ - return; - - data += HCI_COMMAND_HDR_SIZE; - length -= HCI_COMMAND_HDR_SIZE; - - if (paramlen > length) - return; - -#define PARAM(cmd, param) (((cmd##_cp *) data)->param) -#define PARAM16(cmd, param) le16_to_cpup(&PARAM(cmd, param)) -#define PARAMHANDLE(cmd) HNDL(PARAM(cmd, handle)) -#define LENGTH_CHECK(cmd) if (length < sizeof(cmd##_cp)) goto short_hci - /* Note: the supported commands bitmask in bt_hci_read_local_commands_rp - * needs to be updated every time a command is implemented here! */ - switch (cmd) { - case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY): - LENGTH_CHECK(inquiry); - - if (PARAM(inquiry, length) < 1) { - bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - hci->lm.inquire = 1; - hci->lm.periodic = 0; - hci->lm.responses_left = PARAM(inquiry, num_rsp) ?: INT_MAX; - hci->lm.responses = 0; - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_inquiry_start(hci, PARAM(inquiry, length)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY_CANCEL): - if (!hci->lm.inquire || hci->lm.periodic) { - fprintf(stderr, "%s: Inquiry Cancel should only be issued after " - "the Inquiry command has been issued, a Command " - "Status event has been received for the Inquiry " - "command, and before the Inquiry Complete event " - "occurs", __FUNCTION__); - bt_hci_event_complete_status(hci, HCI_COMMAND_DISALLOWED); - break; - } - - hci->lm.inquire = 0; - timer_del(hci->lm.inquiry_done); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_PERIODIC_INQUIRY): - LENGTH_CHECK(periodic_inquiry); - - if (!(PARAM(periodic_inquiry, length) < - PARAM16(periodic_inquiry, min_period) && - PARAM16(periodic_inquiry, min_period) < - PARAM16(periodic_inquiry, max_period)) || - PARAM(periodic_inquiry, length) < 1 || - PARAM16(periodic_inquiry, min_period) < 2 || - PARAM16(periodic_inquiry, max_period) < 3) { - bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - hci->lm.inquire = 1; - hci->lm.periodic = 1; - hci->lm.responses_left = PARAM(periodic_inquiry, num_rsp); - hci->lm.responses = 0; - hci->lm.inquiry_period = PARAM16(periodic_inquiry, max_period); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - bt_hci_inquiry_start(hci, PARAM(periodic_inquiry, length)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_EXIT_PERIODIC_INQUIRY): - if (!hci->lm.inquire || !hci->lm.periodic) { - fprintf(stderr, "%s: Inquiry Cancel should only be issued after " - "the Inquiry command has been issued, a Command " - "Status event has been received for the Inquiry " - "command, and before the Inquiry Complete event " - "occurs", __FUNCTION__); - bt_hci_event_complete_status(hci, HCI_COMMAND_DISALLOWED); - break; - } - hci->lm.inquire = 0; - timer_del(hci->lm.inquiry_done); - timer_del(hci->lm.inquiry_next); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_CREATE_CONN): - LENGTH_CHECK(create_conn); - - if (hci->lm.connecting >= HCI_HANDLES_MAX) { - bt_hci_event_status(hci, HCI_REJECTED_LIMITED_RESOURCES); - break; - } - bt_hci_event_status(hci, HCI_SUCCESS); - - if (bt_hci_connect(hci, &PARAM(create_conn, bdaddr))) - bt_hci_connection_reject_event(hci, &PARAM(create_conn, bdaddr)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_DISCONNECT): - LENGTH_CHECK(disconnect); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(disconnect))) { - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - } - - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_disconnect(hci, PARAMHANDLE(disconnect), - PARAM(disconnect, reason)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_CREATE_CONN_CANCEL): - LENGTH_CHECK(create_conn_cancel); - - if (bt_hci_lmp_connection_ready(hci, - &PARAM(create_conn_cancel, bdaddr))) { - for (i = 0; i < HCI_HANDLES_MAX; i ++) - if (bt_hci_role_master(hci, i) && hci->lm.handle[i].link && - !bacmp(&hci->lm.handle[i].link->slave->bd_addr, - &PARAM(create_conn_cancel, bdaddr))) - break; - - bt_hci_event_complete_conn_cancel(hci, i < HCI_HANDLES_MAX ? - HCI_ACL_CONNECTION_EXISTS : HCI_NO_CONNECTION, - &PARAM(create_conn_cancel, bdaddr)); - } else - bt_hci_event_complete_conn_cancel(hci, HCI_SUCCESS, - &PARAM(create_conn_cancel, bdaddr)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ): - LENGTH_CHECK(accept_conn_req); - - if (!hci->conn_req_host || - bacmp(&PARAM(accept_conn_req, bdaddr), - &hci->conn_req_host->bd_addr)) { - bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_connection_accept(hci, hci->conn_req_host); - hci->conn_req_host = NULL; - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_REJECT_CONN_REQ): - LENGTH_CHECK(reject_conn_req); - - if (!hci->conn_req_host || - bacmp(&PARAM(reject_conn_req, bdaddr), - &hci->conn_req_host->bd_addr)) { - bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_connection_reject(hci, hci->conn_req_host, - PARAM(reject_conn_req, reason)); - bt_hci_connection_reject_event(hci, &hci->conn_req_host->bd_addr); - hci->conn_req_host = NULL; - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_AUTH_REQUESTED): - LENGTH_CHECK(auth_requested); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(auth_requested))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - else { - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_event_auth_complete(hci, PARAMHANDLE(auth_requested)); - } - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT): - LENGTH_CHECK(set_conn_encrypt); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(set_conn_encrypt))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - else { - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_event_encrypt_change(hci, - PARAMHANDLE(set_conn_encrypt), - PARAM(set_conn_encrypt, encrypt)); - } - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_REMOTE_NAME_REQ): - LENGTH_CHECK(remote_name_req); - - if (bt_hci_name_req(hci, &PARAM(remote_name_req, bdaddr))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_REMOTE_NAME_REQ_CANCEL): - LENGTH_CHECK(remote_name_req_cancel); - - bt_hci_event_complete_name_cancel(hci, - &PARAM(remote_name_req_cancel, bdaddr)); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_FEATURES): - LENGTH_CHECK(read_remote_features); - - if (bt_hci_features_req(hci, PARAMHANDLE(read_remote_features))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_EXT_FEATURES): - LENGTH_CHECK(read_remote_ext_features); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(read_remote_ext_features))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - else { - bt_hci_event_status(hci, HCI_SUCCESS); - bt_hci_event_read_remote_ext_features(hci, - PARAMHANDLE(read_remote_ext_features)); - } - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_VERSION): - LENGTH_CHECK(read_remote_version); - - if (bt_hci_version_req(hci, PARAMHANDLE(read_remote_version))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_CLOCK_OFFSET): - LENGTH_CHECK(read_clock_offset); - - if (bt_hci_clkoffset_req(hci, PARAMHANDLE(read_clock_offset))) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_LMP_HANDLE): - LENGTH_CHECK(read_lmp_handle); - - /* TODO: */ - bt_hci_event_complete_lmp_handle(hci, PARAMHANDLE(read_lmp_handle)); - break; - - case cmd_opcode_pack(OGF_LINK_POLICY, OCF_HOLD_MODE): - LENGTH_CHECK(hold_mode); - - if (PARAM16(hold_mode, min_interval) > - PARAM16(hold_mode, max_interval) || - PARAM16(hold_mode, min_interval) < 0x0002 || - PARAM16(hold_mode, max_interval) > 0xff00 || - (PARAM16(hold_mode, min_interval) & 1) || - (PARAM16(hold_mode, max_interval) & 1)) { - bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - if (bt_hci_mode_change(hci, PARAMHANDLE(hold_mode), - PARAM16(hold_mode, max_interval), - acl_hold)) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_POLICY, OCF_PARK_MODE): - LENGTH_CHECK(park_mode); - - if (PARAM16(park_mode, min_interval) > - PARAM16(park_mode, max_interval) || - PARAM16(park_mode, min_interval) < 0x000e || - (PARAM16(park_mode, min_interval) & 1) || - (PARAM16(park_mode, max_interval) & 1)) { - bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - if (bt_hci_mode_change(hci, PARAMHANDLE(park_mode), - PARAM16(park_mode, max_interval), - acl_parked)) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_POLICY, OCF_EXIT_PARK_MODE): - LENGTH_CHECK(exit_park_mode); - - if (bt_hci_mode_cancel(hci, PARAMHANDLE(exit_park_mode), - acl_parked)) - bt_hci_event_status(hci, HCI_NO_CONNECTION); - break; - - case cmd_opcode_pack(OGF_LINK_POLICY, OCF_ROLE_DISCOVERY): - LENGTH_CHECK(role_discovery); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(role_discovery))) - bt_hci_event_complete_role_discovery(hci, - HCI_NO_CONNECTION, PARAMHANDLE(role_discovery), 0); - else - bt_hci_event_complete_role_discovery(hci, - HCI_SUCCESS, PARAMHANDLE(role_discovery), - bt_hci_role_master(hci, - PARAMHANDLE(role_discovery))); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_SET_EVENT_MASK): - LENGTH_CHECK(set_event_mask); - - memcpy(hci->event_mask, PARAM(set_event_mask, mask), 8); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_RESET): - bt_hci_reset(hci); - bt_hci_event_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_SET_EVENT_FLT): - if (length >= 1 && PARAM(set_event_flt, flt_type) == FLT_CLEAR_ALL) - /* No length check */; - else - LENGTH_CHECK(set_event_flt); - - /* Filters are not implemented */ - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_FLUSH): - LENGTH_CHECK(flush); - - if (bt_hci_handle_bad(hci, PARAMHANDLE(flush))) - bt_hci_event_complete_flush(hci, - HCI_NO_CONNECTION, PARAMHANDLE(flush)); - else { - /* TODO: ordering? */ - bt_hci_event(hci, EVT_FLUSH_OCCURRED, - &PARAM(flush, handle), - EVT_FLUSH_OCCURRED_SIZE); - bt_hci_event_complete_flush(hci, - HCI_SUCCESS, PARAMHANDLE(flush)); - } - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME): - LENGTH_CHECK(change_local_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); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_LOCAL_NAME): - bt_hci_event_complete_read_local_name(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_CONN_ACCEPT_TIMEOUT): - bt_hci_event_complete_read_conn_accept_timeout(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CONN_ACCEPT_TIMEOUT): - /* TODO */ - LENGTH_CHECK(write_conn_accept_timeout); - - if (PARAM16(write_conn_accept_timeout, timeout) < 0x0001 || - PARAM16(write_conn_accept_timeout, timeout) > 0xb540) { - bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - hci->conn_accept_tout = PARAM16(write_conn_accept_timeout, timeout); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_SCAN_ENABLE): - bt_hci_event_complete_read_scan_enable(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE): - LENGTH_CHECK(write_scan_enable); - - /* TODO: check that the remaining bits are all 0 */ - hci->device.inquiry_scan = - !!(PARAM(write_scan_enable, scan_enable) & SCAN_INQUIRY); - hci->device.page_scan = - !!(PARAM(write_scan_enable, scan_enable) & SCAN_PAGE); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_CLASS_OF_DEV): - bt_hci_event_complete_read_local_class(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV): - LENGTH_CHECK(write_class_of_dev); - - memcpy(hci->device.class, PARAM(write_class_of_dev, dev_class), - sizeof(PARAM(write_class_of_dev, dev_class))); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_VOICE_SETTING): - bt_hci_event_complete_voice_setting(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_VOICE_SETTING): - LENGTH_CHECK(write_voice_setting); - - hci->voice_setting = PARAM(write_voice_setting, voice_setting); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_HOST_NUMBER_OF_COMPLETED_PACKETS): - if (length < data[0] * 2 + 1) - goto short_hci; - - for (i = 0; i < data[0]; i ++) - if (bt_hci_handle_bad(hci, - data[i * 2 + 1] | (data[i * 2 + 2] << 8))) - bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_INQUIRY_MODE): - /* Only if (local_features[3] & 0x40) && (local_commands[12] & 0x40) - * else - * goto unknown_command */ - bt_hci_event_complete_read_inquiry_mode(hci); - break; - - case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_INQUIRY_MODE): - /* Only if (local_features[3] & 0x40) && (local_commands[12] & 0x80) - * else - * goto unknown_command */ - LENGTH_CHECK(write_inquiry_mode); - - if (PARAM(write_inquiry_mode, mode) > 0x01) { - bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS); - break; - } - - hci->lm.inquiry_mode = PARAM(write_inquiry_mode, mode); - bt_hci_event_complete_status(hci, HCI_SUCCESS); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_VERSION): - bt_hci_read_local_version_rp(hci); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_COMMANDS): - bt_hci_read_local_commands_rp(hci); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES): - bt_hci_read_local_features_rp(hci); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_EXT_FEATURES): - LENGTH_CHECK(read_local_ext_features); - - bt_hci_read_local_ext_features_rp(hci, - PARAM(read_local_ext_features, page_num)); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_BUFFER_SIZE): - bt_hci_read_buffer_size_rp(hci); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_COUNTRY_CODE): - bt_hci_read_country_code_rp(hci); - break; - - case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_BD_ADDR): - bt_hci_read_bd_addr_rp(hci); - break; - - case cmd_opcode_pack(OGF_STATUS_PARAM, OCF_READ_LINK_QUALITY): - LENGTH_CHECK(read_link_quality); - - bt_hci_link_quality_rp(hci, PARAMHANDLE(read_link_quality)); - break; - - default: - bt_hci_event_status(hci, HCI_UNKNOWN_COMMAND); - break; - - short_hci: - fprintf(stderr, "%s: HCI packet too short (%iB)\n", - __FUNCTION__, length); - bt_hci_event_status(hci, HCI_INVALID_PARAMETERS); - break; - } -} - -/* We could perform fragmentation here, we can't do "recombination" because - * at this layer the length of the payload is not know ahead, so we only - * know that a packet contained the last fragment of the SDU when the next - * SDU starts. */ -static inline void bt_hci_lmp_acl_data(struct bt_hci_s *hci, uint16_t handle, - const uint8_t *data, int start, int len) -{ - struct hci_acl_hdr *pkt = (void *) hci->acl_buf; - - /* TODO: packet flags */ - /* TODO: avoid memcpy'ing */ - - if (len + HCI_ACL_HDR_SIZE > sizeof(hci->acl_buf)) { - fprintf(stderr, "%s: can't take ACL packets %i bytes long\n", - __FUNCTION__, len); - return; - } - memcpy(hci->acl_buf + HCI_ACL_HDR_SIZE, data, len); - - pkt->handle = cpu_to_le16( - acl_handle_pack(handle, start ? ACL_START : ACL_CONT)); - pkt->dlen = cpu_to_le16(len); - hci->info.acl_recv(hci->info.opaque, - hci->acl_buf, len + HCI_ACL_HDR_SIZE); -} - -static void bt_hci_lmp_acl_data_slave(struct bt_link_s *btlink, - const uint8_t *data, int start, int len) -{ - struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink; - - bt_hci_lmp_acl_data(hci_from_device(btlink->slave), - link->handle, data, start, len); -} - -static void bt_hci_lmp_acl_data_host(struct bt_link_s *link, - const uint8_t *data, int start, int len) -{ - bt_hci_lmp_acl_data(hci_from_device(link->host), - link->handle, data, start, len); -} - -static void bt_submit_acl(struct HCIInfo *info, - const uint8_t *data, int length) -{ - struct bt_hci_s *hci = hci_from_info(info); - uint16_t handle; - int datalen, flags; - struct bt_link_s *link; - - if (length < HCI_ACL_HDR_SIZE) { - fprintf(stderr, "%s: ACL packet too short (%iB)\n", - __FUNCTION__, length); - return; - } - - handle = acl_handle((data[1] << 8) | data[0]); - flags = acl_flags((data[1] << 8) | data[0]); - datalen = (data[3] << 8) | data[2]; - data += HCI_ACL_HDR_SIZE; - length -= HCI_ACL_HDR_SIZE; - - if (bt_hci_handle_bad(hci, handle)) { - fprintf(stderr, "%s: invalid ACL handle %03x\n", - __FUNCTION__, handle); - /* TODO: signal an error */ - return; - } - handle &= ~HCI_HANDLE_OFFSET; - - if (datalen > length) { - fprintf(stderr, "%s: ACL packet too short (%iB < %iB)\n", - __FUNCTION__, length, datalen); - return; - } - - link = hci->lm.handle[handle].link; - - if ((flags & ~3) == ACL_ACTIVE_BCAST) { - if (!hci->asb_handle) - hci->asb_handle = handle; - else if (handle != hci->asb_handle) { - fprintf(stderr, "%s: Bad handle %03x in Active Slave Broadcast\n", - __FUNCTION__, handle); - /* TODO: signal an error */ - return; - } - - /* TODO */ - } - - if ((flags & ~3) == ACL_PICO_BCAST) { - if (!hci->psb_handle) - hci->psb_handle = handle; - else if (handle != hci->psb_handle) { - fprintf(stderr, "%s: Bad handle %03x in Parked Slave Broadcast\n", - __FUNCTION__, handle); - /* TODO: signal an error */ - return; - } - - /* TODO */ - } - - /* TODO: increase counter and send EVT_NUM_COMP_PKTS */ - bt_hci_event_num_comp_pkts(hci, handle | HCI_HANDLE_OFFSET, 1); - - /* Do this last as it can trigger further events even in this HCI */ - hci->lm.handle[handle].lmp_acl_data(link, data, - (flags & 3) == ACL_START, length); -} - -static void bt_submit_sco(struct HCIInfo *info, - const uint8_t *data, int length) -{ - struct bt_hci_s *hci = hci_from_info(info); - uint16_t handle; - int datalen; - - if (length < 3) - return; - - handle = acl_handle((data[1] << 8) | data[0]); - datalen = data[2]; - length -= 3; - - if (bt_hci_handle_bad(hci, handle)) { - fprintf(stderr, "%s: invalid SCO handle %03x\n", - __FUNCTION__, handle); - return; - } - - if (datalen > length) { - fprintf(stderr, "%s: SCO packet too short (%iB < %iB)\n", - __FUNCTION__, length, datalen); - return; - } - - /* TODO */ - - /* TODO: increase counter and send EVT_NUM_COMP_PKTS if synchronous - * Flow Control is enabled. - * (See Read/Write_Synchronous_Flow_Control_Enable on page 513 and - * page 514.) */ -} - -static uint8_t *bt_hci_evt_packet(void *opaque) -{ - /* TODO: allocate a packet from upper layer */ - struct bt_hci_s *s = opaque; - - return s->evt_buf; -} - -static void bt_hci_evt_submit(void *opaque, int len) -{ - /* TODO: notify upper layer */ - struct bt_hci_s *s = opaque; - - s->info.evt_recv(s->info.opaque, s->evt_buf, len); -} - -static int bt_hci_bdaddr_set(struct HCIInfo *info, const uint8_t *bd_addr) -{ - struct bt_hci_s *hci = hci_from_info(info); - - bacpy(&hci->device.bd_addr, (const bdaddr_t *) bd_addr); - return 0; -} - -static void bt_hci_done(struct HCIInfo *info); -static void bt_hci_destroy(struct bt_device_s *dev) -{ - struct bt_hci_s *hci = hci_from_device(dev); - - bt_hci_done(&hci->info); -} - -struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net) -{ - struct bt_hci_s *s = g_malloc0(sizeof(struct bt_hci_s)); - - s->lm.inquiry_done = timer_new_ns(QEMU_CLOCK_VIRTUAL, bt_hci_inquiry_done, s); - s->lm.inquiry_next = timer_new_ns(QEMU_CLOCK_VIRTUAL, bt_hci_inquiry_next, s); - s->conn_accept_timer = - timer_new_ns(QEMU_CLOCK_VIRTUAL, bt_hci_conn_accept_timeout, s); - - s->evt_packet = bt_hci_evt_packet; - s->evt_submit = bt_hci_evt_submit; - s->opaque = s; - - bt_device_init(&s->device, net); - s->device.lmp_connection_request = bt_hci_lmp_connection_request; - s->device.lmp_connection_complete = bt_hci_lmp_connection_complete; - s->device.lmp_disconnect_master = bt_hci_lmp_disconnect_host; - s->device.lmp_disconnect_slave = bt_hci_lmp_disconnect_slave; - s->device.lmp_acl_data = bt_hci_lmp_acl_data_slave; - s->device.lmp_acl_resp = bt_hci_lmp_acl_data_host; - s->device.lmp_mode_change = bt_hci_lmp_mode_change_slave; - - /* Keep updated! */ - /* Also keep in sync with supported commands bitmask in - * bt_hci_read_local_commands_rp */ - s->device.lmp_caps = 0x8000199b7e85355fll; - - bt_hci_reset(s); - - s->info.cmd_send = bt_submit_hci; - s->info.sco_send = bt_submit_sco; - s->info.acl_send = bt_submit_acl; - s->info.bdaddr_set = bt_hci_bdaddr_set; - - 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; -} - -struct HCIInfo *hci_init(const char *str) -{ - char *endp; - struct bt_scatternet_s *vlan = 0; - - if (!strcmp(str, "null")) - /* null */ - return &null_hci; - else if (!strncmp(str, "host", 4) && (str[4] == '\0' || str[4] == ':')) - /* host[:hciN] */ - return bt_host_hci(str[4] ? str + 5 : "hci0"); - else if (!strncmp(str, "hci", 3)) { - /* hci[,vlan=n] */ - if (str[3]) { - if (!strncmp(str + 3, ",vlan=", 6)) { - vlan = qemu_find_bt_vlan(strtol(str + 9, &endp, 0)); - if (*endp) - vlan = 0; - } - } else - vlan = qemu_find_bt_vlan(0); - if (vlan) - return bt_new_hci(vlan); - } - - fprintf(stderr, "qemu: Unknown bluetooth HCI `%s'.\n", str); - - return 0; -} - -static void bt_hci_done(struct HCIInfo *info) -{ - struct bt_hci_s *hci = hci_from_info(info); - int handle; - - bt_device_done(&hci->device); - - 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. - * This frees the links. */ - if (hci->conn_req_host) { - bt_hci_connection_reject(hci, - hci->conn_req_host, HCI_OE_POWER_OFF); - return; - } - - for (handle = HCI_HANDLE_OFFSET; - handle < (HCI_HANDLE_OFFSET | HCI_HANDLES_MAX); handle ++) - if (!bt_hci_handle_bad(hci, handle)) - bt_hci_disconnect(hci, handle, HCI_OE_POWER_OFF); - - /* TODO: this is not enough actually, there may be slaves from whom - * we have requested a connection who will soon (or not) respond with - * an accept or a reject, so we should also check if hci->lm.connecting - * is non-zero and if so, avoid freeing the hci but otherwise disappear - * from all qemu social life (e.g. stop scanning and request to be - * removed from s->device.net) and arrange for - * s->device.lmp_connection_complete to free the remaining bits once - * hci->lm.awaiting_bdaddr[] is empty. */ - - timer_free(hci->lm.inquiry_done); - timer_free(hci->lm.inquiry_next); - timer_free(hci->conn_accept_timer); - - g_free(hci); -} diff --git a/qemu/hw/bt/hid.c b/qemu/hw/bt/hid.c deleted file mode 100644 index f6affbbb4..000000000 --- a/qemu/hw/bt/hid.c +++ /dev/null @@ -1,554 +0,0 @@ -/* - * QEMU Bluetooth HID Profile wrapper for USB HID. - * - * Copyright (C) 2007-2008 OpenMoko, Inc. - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/timer.h" -#include "ui/console.h" -#include "hw/input/hid.h" -#include "hw/bt.h" - -enum hid_transaction_req { - BT_HANDSHAKE = 0x0, - BT_HID_CONTROL = 0x1, - BT_GET_REPORT = 0x4, - BT_SET_REPORT = 0x5, - BT_GET_PROTOCOL = 0x6, - BT_SET_PROTOCOL = 0x7, - BT_GET_IDLE = 0x8, - BT_SET_IDLE = 0x9, - BT_DATA = 0xa, - BT_DATC = 0xb, -}; - -enum hid_transaction_handshake { - BT_HS_SUCCESSFUL = 0x0, - BT_HS_NOT_READY = 0x1, - BT_HS_ERR_INVALID_REPORT_ID = 0x2, - BT_HS_ERR_UNSUPPORTED_REQUEST = 0x3, - BT_HS_ERR_INVALID_PARAMETER = 0x4, - BT_HS_ERR_UNKNOWN = 0xe, - BT_HS_ERR_FATAL = 0xf, -}; - -enum hid_transaction_control { - BT_HC_NOP = 0x0, - BT_HC_HARD_RESET = 0x1, - BT_HC_SOFT_RESET = 0x2, - BT_HC_SUSPEND = 0x3, - BT_HC_EXIT_SUSPEND = 0x4, - BT_HC_VIRTUAL_CABLE_UNPLUG = 0x5, -}; - -enum hid_protocol { - BT_HID_PROTO_BOOT = 0, - BT_HID_PROTO_REPORT = 1, -}; - -enum hid_boot_reportid { - BT_HID_BOOT_INVALID = 0, - BT_HID_BOOT_KEYBOARD, - BT_HID_BOOT_MOUSE, -}; - -enum hid_data_pkt { - BT_DATA_OTHER = 0, - BT_DATA_INPUT, - BT_DATA_OUTPUT, - BT_DATA_FEATURE, -}; - -#define BT_HID_MTU 48 - -/* HID interface requests */ -#define GET_REPORT 0xa101 -#define GET_IDLE 0xa102 -#define GET_PROTOCOL 0xa103 -#define SET_REPORT 0x2109 -#define SET_IDLE 0x210a -#define SET_PROTOCOL 0x210b - -struct bt_hid_device_s { - struct bt_l2cap_device_s btdev; - struct bt_l2cap_conn_params_s *control; - struct bt_l2cap_conn_params_s *interrupt; - HIDState hid; - - int proto; - int connected; - int data_type; - int intr_state; - struct { - int len; - uint8_t buffer[1024]; - } dataother, datain, dataout, feature, intrdataout; - enum { - bt_state_ready, - bt_state_transaction, - bt_state_suspend, - } state; -}; - -static void bt_hid_reset(struct bt_hid_device_s *s) -{ - struct bt_scatternet_s *net = s->btdev.device.net; - - /* Go as far as... */ - bt_l2cap_device_done(&s->btdev); - bt_l2cap_device_init(&s->btdev, net); - - hid_reset(&s->hid); - s->proto = BT_HID_PROTO_REPORT; - s->state = bt_state_ready; - s->dataother.len = 0; - s->datain.len = 0; - s->dataout.len = 0; - s->feature.len = 0; - s->intrdataout.len = 0; - s->intr_state = 0; -} - -static int bt_hid_out(struct bt_hid_device_s *s) -{ - if (s->data_type == BT_DATA_OUTPUT) { - /* nothing */ - ; - } - - if (s->data_type == BT_DATA_FEATURE) { - /* XXX: - * does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE - * or a SET_REPORT? */ - ; - } - - return -1; -} - -static int bt_hid_in(struct bt_hid_device_s *s) -{ - s->datain.len = hid_keyboard_poll(&s->hid, s->datain.buffer, - sizeof(s->datain.buffer)); - return s->datain.len; -} - -static void bt_hid_send_handshake(struct bt_hid_device_s *s, int result) -{ - *s->control->sdu_out(s->control, 1) = - (BT_HANDSHAKE << 4) | result; - s->control->sdu_submit(s->control); -} - -static void bt_hid_send_control(struct bt_hid_device_s *s, int operation) -{ - *s->control->sdu_out(s->control, 1) = - (BT_HID_CONTROL << 4) | operation; - s->control->sdu_submit(s->control); -} - -static void bt_hid_disconnect(struct bt_hid_device_s *s) -{ - /* Disconnect s->control and s->interrupt */ -} - -static void bt_hid_send_data(struct bt_l2cap_conn_params_s *ch, int type, - const uint8_t *data, int len) -{ - uint8_t *pkt, hdr = (BT_DATA << 4) | type; - int plen; - - do { - plen = MIN(len, ch->remote_mtu - 1); - pkt = ch->sdu_out(ch, plen + 1); - - pkt[0] = hdr; - if (plen) - memcpy(pkt + 1, data, plen); - ch->sdu_submit(ch); - - len -= plen; - data += plen; - hdr = (BT_DATC << 4) | type; - } while (plen == ch->remote_mtu - 1); -} - -static void bt_hid_control_transaction(struct bt_hid_device_s *s, - const uint8_t *data, int len) -{ - uint8_t type, parameter; - int rlen, ret = -1; - if (len < 1) - return; - - type = data[0] >> 4; - parameter = data[0] & 0xf; - - switch (type) { - case BT_HANDSHAKE: - case BT_DATA: - switch (parameter) { - default: - /* These are not expected to be sent this direction. */ - ret = BT_HS_ERR_INVALID_PARAMETER; - } - break; - - case BT_HID_CONTROL: - if (len != 1 || (parameter != BT_HC_VIRTUAL_CABLE_UNPLUG && - s->state == bt_state_transaction)) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - switch (parameter) { - case BT_HC_NOP: - break; - case BT_HC_HARD_RESET: - case BT_HC_SOFT_RESET: - bt_hid_reset(s); - break; - case BT_HC_SUSPEND: - if (s->state == bt_state_ready) - s->state = bt_state_suspend; - else - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - case BT_HC_EXIT_SUSPEND: - if (s->state == bt_state_suspend) - s->state = bt_state_ready; - else - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - case BT_HC_VIRTUAL_CABLE_UNPLUG: - bt_hid_disconnect(s); - break; - default: - ret = BT_HS_ERR_INVALID_PARAMETER; - } - break; - - case BT_GET_REPORT: - /* No ReportIDs declared. */ - if (((parameter & 8) && len != 3) || - (!(parameter & 8) && len != 1) || - s->state != bt_state_ready) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - if (parameter & 8) - rlen = data[2] | (data[3] << 8); - else - rlen = INT_MAX; - switch (parameter & 3) { - case BT_DATA_OTHER: - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - case BT_DATA_INPUT: - /* Here we can as well poll s->usbdev */ - bt_hid_send_data(s->control, BT_DATA_INPUT, - s->datain.buffer, MIN(rlen, s->datain.len)); - break; - case BT_DATA_OUTPUT: - bt_hid_send_data(s->control, BT_DATA_OUTPUT, - s->dataout.buffer, MIN(rlen, s->dataout.len)); - break; - case BT_DATA_FEATURE: - bt_hid_send_data(s->control, BT_DATA_FEATURE, - s->feature.buffer, MIN(rlen, s->feature.len)); - break; - } - break; - - case BT_SET_REPORT: - if (len < 2 || len > BT_HID_MTU || s->state != bt_state_ready || - (parameter & 3) == BT_DATA_OTHER || - (parameter & 3) == BT_DATA_INPUT) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - s->data_type = parameter & 3; - if (s->data_type == BT_DATA_OUTPUT) { - s->dataout.len = len - 1; - memcpy(s->dataout.buffer, data + 1, s->dataout.len); - } else { - s->feature.len = len - 1; - memcpy(s->feature.buffer, data + 1, s->feature.len); - } - if (len == BT_HID_MTU) - s->state = bt_state_transaction; - else - bt_hid_out(s); - break; - - case BT_GET_PROTOCOL: - if (len != 1 || s->state == bt_state_transaction) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - *s->control->sdu_out(s->control, 1) = s->proto; - s->control->sdu_submit(s->control); - break; - - case BT_SET_PROTOCOL: - if (len != 1 || s->state == bt_state_transaction || - (parameter != BT_HID_PROTO_BOOT && - parameter != BT_HID_PROTO_REPORT)) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - s->proto = parameter; - s->hid.protocol = parameter; - ret = BT_HS_SUCCESSFUL; - break; - - case BT_GET_IDLE: - if (len != 1 || s->state == bt_state_transaction) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - *s->control->sdu_out(s->control, 1) = s->hid.idle; - s->control->sdu_submit(s->control); - break; - - case BT_SET_IDLE: - if (len != 2 || s->state == bt_state_transaction) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - - s->hid.idle = data[1]; - /* XXX: Does this generate a handshake? */ - break; - - case BT_DATC: - if (len > BT_HID_MTU || s->state != bt_state_transaction) { - ret = BT_HS_ERR_INVALID_PARAMETER; - break; - } - if (s->data_type == BT_DATA_OUTPUT) { - memcpy(s->dataout.buffer + s->dataout.len, data + 1, len - 1); - s->dataout.len += len - 1; - } else { - memcpy(s->feature.buffer + s->feature.len, data + 1, len - 1); - s->feature.len += len - 1; - } - if (len < BT_HID_MTU) { - bt_hid_out(s); - s->state = bt_state_ready; - } - break; - - default: - ret = BT_HS_ERR_UNSUPPORTED_REQUEST; - } - - if (ret != -1) - bt_hid_send_handshake(s, ret); -} - -static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len) -{ - struct bt_hid_device_s *hid = opaque; - - bt_hid_control_transaction(hid, data, len); -} - -static void bt_hid_datain(HIDState *hs) -{ - struct bt_hid_device_s *hid = - container_of(hs, struct bt_hid_device_s, hid); - - /* If suspended, wake-up and send a wake-up event first. We might - * want to also inspect the input report and ignore event like - * mouse movements until a button event occurs. */ - if (hid->state == bt_state_suspend) { - hid->state = bt_state_ready; - } - - if (bt_hid_in(hid) > 0) - /* TODO: when in boot-mode precede any Input reports with the ReportID - * byte, here and in GetReport/SetReport on the Control channel. */ - bt_hid_send_data(hid->interrupt, BT_DATA_INPUT, - hid->datain.buffer, hid->datain.len); -} - -static void bt_hid_interrupt_sdu(void *opaque, const uint8_t *data, int len) -{ - struct bt_hid_device_s *hid = opaque; - - if (len > BT_HID_MTU || len < 1) - goto bad; - if ((data[0] & 3) != BT_DATA_OUTPUT) - goto bad; - if ((data[0] >> 4) == BT_DATA) { - if (hid->intr_state) - goto bad; - - hid->data_type = BT_DATA_OUTPUT; - hid->intrdataout.len = 0; - } else if ((data[0] >> 4) == BT_DATC) { - if (!hid->intr_state) - goto bad; - } else - goto bad; - - memcpy(hid->intrdataout.buffer + hid->intrdataout.len, data + 1, len - 1); - hid->intrdataout.len += len - 1; - hid->intr_state = (len == BT_HID_MTU); - if (!hid->intr_state) { - memcpy(hid->dataout.buffer, hid->intrdataout.buffer, - hid->dataout.len = hid->intrdataout.len); - bt_hid_out(hid); - } - - return; -bad: - fprintf(stderr, "%s: bad transaction on Interrupt channel.\n", - __FUNCTION__); -} - -/* "Virtual cable" plug/unplug event. */ -static void bt_hid_connected_update(struct bt_hid_device_s *hid) -{ - int prev = hid->connected; - - hid->connected = hid->control && hid->interrupt; - - /* Stop page-/inquiry-scanning when a host is connected. */ - hid->btdev.device.page_scan = !hid->connected; - hid->btdev.device.inquiry_scan = !hid->connected; - - if (hid->connected && !prev) { - hid_reset(&hid->hid); - hid->proto = BT_HID_PROTO_REPORT; - } - - /* Should set HIDVirtualCable in SDP (possibly need to check that SDP - * isn't destroyed yet, in case we're being called from handle_destroy) */ -} - -static void bt_hid_close_control(void *opaque) -{ - struct bt_hid_device_s *hid = opaque; - - hid->control = NULL; - bt_hid_connected_update(hid); -} - -static void bt_hid_close_interrupt(void *opaque) -{ - struct bt_hid_device_s *hid = opaque; - - hid->interrupt = NULL; - bt_hid_connected_update(hid); -} - -static int bt_hid_new_control_ch(struct bt_l2cap_device_s *dev, - struct bt_l2cap_conn_params_s *params) -{ - struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; - - if (hid->control) - return 1; - - hid->control = params; - hid->control->opaque = hid; - hid->control->close = bt_hid_close_control; - hid->control->sdu_in = bt_hid_control_sdu; - - bt_hid_connected_update(hid); - - return 0; -} - -static int bt_hid_new_interrupt_ch(struct bt_l2cap_device_s *dev, - struct bt_l2cap_conn_params_s *params) -{ - struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; - - if (hid->interrupt) - return 1; - - hid->interrupt = params; - hid->interrupt->opaque = hid; - hid->interrupt->close = bt_hid_close_interrupt; - hid->interrupt->sdu_in = bt_hid_interrupt_sdu; - - bt_hid_connected_update(hid); - - return 0; -} - -static void bt_hid_destroy(struct bt_device_s *dev) -{ - struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; - - if (hid->connected) - bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG); - bt_l2cap_device_done(&hid->btdev); - - hid_free(&hid->hid); - - g_free(hid); -} - -enum peripheral_minor_class { - class_other = 0 << 4, - class_keyboard = 1 << 4, - class_pointing = 2 << 4, - class_combo = 3 << 4, -}; - -static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net, - enum peripheral_minor_class minor) -{ - struct bt_hid_device_s *s = g_malloc0(sizeof(*s)); - uint32_t class = - /* Format type */ - (0 << 0) | - /* Device class */ - (minor << 2) | - (5 << 8) | /* "Peripheral" */ - /* Service classes */ - (1 << 13) | /* Limited discoverable mode */ - (1 << 19); /* Capturing device (?) */ - - bt_l2cap_device_init(&s->btdev, net); - bt_l2cap_sdp_init(&s->btdev); - bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_CTRL, - BT_HID_MTU, bt_hid_new_control_ch); - bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR, - BT_HID_MTU, bt_hid_new_interrupt_ch); - - hid_init(&s->hid, HID_KEYBOARD, bt_hid_datain); - s->btdev.device.lmp_name = "BT Keyboard"; - - s->btdev.device.handle_destroy = bt_hid_destroy; - - s->btdev.device.class[0] = (class >> 0) & 0xff; - s->btdev.device.class[1] = (class >> 8) & 0xff; - s->btdev.device.class[2] = (class >> 16) & 0xff; - - return &s->btdev.device; -} - -struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net) -{ - return bt_hid_init(net, class_keyboard); -} diff --git a/qemu/hw/bt/l2cap.c b/qemu/hw/bt/l2cap.c deleted file mode 100644 index 806525194..000000000 --- a/qemu/hw/bt/l2cap.c +++ /dev/null @@ -1,1366 +0,0 @@ -/* - * QEMU Bluetooth L2CAP logic. - * - * Copyright (C) 2008 Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/timer.h" -#include "hw/bt.h" - -#define L2CAP_CID_MAX 0x100 /* Between 0x40 and 0x10000 */ - -struct l2cap_instance_s { - struct bt_link_s *link; - struct bt_l2cap_device_s *dev; - int role; - - uint8_t frame_in[65535 + L2CAP_HDR_SIZE] __attribute__ ((aligned (4))); - int frame_in_len; - - uint8_t frame_out[65535 + L2CAP_HDR_SIZE] __attribute__ ((aligned (4))); - int frame_out_len; - - /* Signalling channel timers. They exist per-request but we can make - * sure we have no more than one outstanding request at any time. */ - QEMUTimer *rtx; - QEMUTimer *ertx; - - int last_id; - int next_id; - - struct l2cap_chan_s { - struct bt_l2cap_conn_params_s params; - - void (*frame_in)(struct l2cap_chan_s *chan, uint16_t cid, - const l2cap_hdr *hdr, int len); - int mps; - int min_mtu; - - struct l2cap_instance_s *l2cap; - - /* Only allocated channels */ - uint16_t remote_cid; -#define L2CAP_CFG_INIT 2 -#define L2CAP_CFG_ACC 1 - int config_req_id; /* TODO: handle outgoing requests generically */ - int config; - - /* Only connection-oriented channels. Note: if we allow the tx and - * rx traffic to be in different modes at any time, we need two. */ - int mode; - - /* Only flow-controlled, connection-oriented channels */ - uint8_t sdu[65536]; /* TODO: dynamically allocate */ - int len_cur, len_total; - int rexmit; - int monitor_timeout; - QEMUTimer *monitor_timer; - QEMUTimer *retransmission_timer; - } *cid[L2CAP_CID_MAX]; - /* The channel state machine states map as following: - * CLOSED -> !cid[N] - * WAIT_CONNECT -> never occurs - * WAIT_CONNECT_RSP -> never occurs - * CONFIG -> cid[N] && config < 3 - * WAIT_CONFIG -> never occurs, cid[N] && config == 0 && !config_r - * WAIT_SEND_CONFIG -> never occurs, cid[N] && config == 1 && !config_r - * WAIT_CONFIG_REQ_RSP -> cid[N] && config == 0 && config_req_id - * WAIT_CONFIG_RSP -> cid[N] && config == 1 && config_req_id - * WAIT_CONFIG_REQ -> cid[N] && config == 2 - * OPEN -> cid[N] && config == 3 - * WAIT_DISCONNECT -> never occurs - */ - - struct l2cap_chan_s signalling_ch; - struct l2cap_chan_s group_ch; -}; - -struct slave_l2cap_instance_s { - struct bt_link_s link; /* Underlying logical link (ACL) */ - struct l2cap_instance_s l2cap; -}; - -struct bt_l2cap_psm_s { - int psm; - int min_mtu; - int (*new_channel)(struct bt_l2cap_device_s *device, - struct bt_l2cap_conn_params_s *params); - struct bt_l2cap_psm_s *next; -}; - -static const uint16_t l2cap_fcs16_table[256] = { - 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, - 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, - 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, - 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, - 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, - 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, - 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, - 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, - 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, - 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, - 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, - 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, - 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, - 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, - 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, - 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, - 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, - 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, - 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, - 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, - 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, - 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, - 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, - 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, - 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, - 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, - 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, - 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, - 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, - 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, - 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, - 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040, -}; - -static uint16_t l2cap_fcs16(const uint8_t *message, int len) -{ - uint16_t fcs = 0x0000; - - while (len --) -#if 0 - { - int i; - - fcs ^= *message ++; - for (i = 8; i; -- i) - if (fcs & 1) - fcs = (fcs >> 1) ^ 0xa001; - else - fcs = (fcs >> 1); - } -#else - fcs = (fcs >> 8) ^ l2cap_fcs16_table[(fcs ^ *message ++) & 0xff]; -#endif - - return fcs; -} - -/* L2CAP layer logic (protocol) */ - -static void l2cap_retransmission_timer_update(struct l2cap_chan_s *ch) -{ -#if 0 - if (ch->mode != L2CAP_MODE_BASIC && ch->rexmit) - timer_mod(ch->retransmission_timer); - else - timer_del(ch->retransmission_timer); -#endif -} - -static void l2cap_monitor_timer_update(struct l2cap_chan_s *ch) -{ -#if 0 - if (ch->mode != L2CAP_MODE_BASIC && !ch->rexmit) - timer_mod(ch->monitor_timer); - else - timer_del(ch->monitor_timer); -#endif -} - -static void l2cap_command_reject(struct l2cap_instance_s *l2cap, int id, - uint16_t reason, const void *data, int plen) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_cmd_rej *params; - uint16_t len; - - reason = cpu_to_le16(reason); - len = cpu_to_le16(L2CAP_CMD_REJ_SIZE + plen); - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_CMD_REJ_SIZE + plen); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_COMMAND_REJ; - hdr->ident = id; - memcpy(&hdr->len, &len, sizeof(hdr->len)); - memcpy(¶ms->reason, &reason, sizeof(reason)); - if (plen) - memcpy(pkt + L2CAP_CMD_HDR_SIZE + L2CAP_CMD_REJ_SIZE, data, plen); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_command_reject_cid(struct l2cap_instance_s *l2cap, int id, - uint16_t reason, uint16_t dcid, uint16_t scid) -{ - l2cap_cmd_rej_cid params = { - .dcid = dcid, - .scid = scid, - }; - - l2cap_command_reject(l2cap, id, reason, ¶ms, L2CAP_CMD_REJ_CID_SIZE); -} - -static void l2cap_connection_response(struct l2cap_instance_s *l2cap, - int dcid, int scid, int result, int status) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_conn_rsp *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_CONN_RSP_SIZE); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_CONN_RSP; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(L2CAP_CONN_RSP_SIZE); - - params->dcid = cpu_to_le16(dcid); - params->scid = cpu_to_le16(scid); - params->result = cpu_to_le16(result); - params->status = cpu_to_le16(status); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_configuration_request(struct l2cap_instance_s *l2cap, - int dcid, int flag, const uint8_t *data, int len) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_conf_req *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_CONF_REQ_SIZE(len)); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - /* TODO: unify the id sequencing */ - l2cap->last_id = l2cap->next_id; - l2cap->next_id = l2cap->next_id == 255 ? 1 : l2cap->next_id + 1; - - hdr->code = L2CAP_CONF_REQ; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(L2CAP_CONF_REQ_SIZE(len)); - - params->dcid = cpu_to_le16(dcid); - params->flags = cpu_to_le16(flag); - if (len) - memcpy(params->data, data, len); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_configuration_response(struct l2cap_instance_s *l2cap, - int scid, int flag, int result, const uint8_t *data, int len) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_conf_rsp *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_CONF_RSP_SIZE(len)); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_CONF_RSP; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(L2CAP_CONF_RSP_SIZE(len)); - - params->scid = cpu_to_le16(scid); - params->flags = cpu_to_le16(flag); - params->result = cpu_to_le16(result); - if (len) - memcpy(params->data, data, len); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_disconnection_response(struct l2cap_instance_s *l2cap, - int dcid, int scid) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_disconn_rsp *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_DISCONN_RSP_SIZE); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_DISCONN_RSP; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(L2CAP_DISCONN_RSP_SIZE); - - params->dcid = cpu_to_le16(dcid); - params->scid = cpu_to_le16(scid); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_echo_response(struct l2cap_instance_s *l2cap, - const uint8_t *data, int len) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - uint8_t *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + len); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_ECHO_RSP; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(len); - - memcpy(params, data, len); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static void l2cap_info_response(struct l2cap_instance_s *l2cap, int type, - int result, const uint8_t *data, int len) -{ - uint8_t *pkt; - l2cap_cmd_hdr *hdr; - l2cap_info_rsp *params; - - pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params, - L2CAP_CMD_HDR_SIZE + L2CAP_INFO_RSP_SIZE + len); - hdr = (void *) (pkt + 0); - params = (void *) (pkt + L2CAP_CMD_HDR_SIZE); - - hdr->code = L2CAP_INFO_RSP; - hdr->ident = l2cap->last_id; - hdr->len = cpu_to_le16(L2CAP_INFO_RSP_SIZE + len); - - params->type = cpu_to_le16(type); - params->result = cpu_to_le16(result); - if (len) - memcpy(params->data, data, len); - - l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params); -} - -static uint8_t *l2cap_bframe_out(struct bt_l2cap_conn_params_s *parm, int len); -static void l2cap_bframe_submit(struct bt_l2cap_conn_params_s *parms); -#if 0 -static uint8_t *l2cap_iframe_out(struct bt_l2cap_conn_params_s *parm, int len); -static void l2cap_iframe_submit(struct bt_l2cap_conn_params_s *parm); -#endif -static void l2cap_bframe_in(struct l2cap_chan_s *ch, uint16_t cid, - const l2cap_hdr *hdr, int len); -static void l2cap_iframe_in(struct l2cap_chan_s *ch, uint16_t cid, - const l2cap_hdr *hdr, int len); - -static int l2cap_cid_new(struct l2cap_instance_s *l2cap) -{ - int i; - - for (i = L2CAP_CID_ALLOC; i < L2CAP_CID_MAX; i ++) - if (!l2cap->cid[i]) - return i; - - return L2CAP_CID_INVALID; -} - -static inline struct bt_l2cap_psm_s *l2cap_psm( - struct bt_l2cap_device_s *device, int psm) -{ - struct bt_l2cap_psm_s *ret = device->first_psm; - - while (ret && ret->psm != psm) - ret = ret->next; - - return ret; -} - -static struct l2cap_chan_s *l2cap_channel_open(struct l2cap_instance_s *l2cap, - int psm, int source_cid) -{ - struct l2cap_chan_s *ch = NULL; - struct bt_l2cap_psm_s *psm_info; - int result, status; - int cid = l2cap_cid_new(l2cap); - - if (cid) { - /* See what the channel is to be used for.. */ - psm_info = l2cap_psm(l2cap->dev, psm); - - if (psm_info) { - /* Device supports this use-case. */ - ch = g_malloc0(sizeof(*ch)); - ch->params.sdu_out = l2cap_bframe_out; - ch->params.sdu_submit = l2cap_bframe_submit; - ch->frame_in = l2cap_bframe_in; - ch->mps = 65536; - ch->min_mtu = MAX(48, psm_info->min_mtu); - ch->params.remote_mtu = MAX(672, ch->min_mtu); - ch->remote_cid = source_cid; - ch->mode = L2CAP_MODE_BASIC; - ch->l2cap = l2cap; - - /* Does it feel like opening yet another channel though? */ - if (!psm_info->new_channel(l2cap->dev, &ch->params)) { - l2cap->cid[cid] = ch; - - result = L2CAP_CR_SUCCESS; - status = L2CAP_CS_NO_INFO; - } else { - g_free(ch); - ch = NULL; - result = L2CAP_CR_NO_MEM; - status = L2CAP_CS_NO_INFO; - } - } else { - result = L2CAP_CR_BAD_PSM; - status = L2CAP_CS_NO_INFO; - } - } else { - result = L2CAP_CR_NO_MEM; - status = L2CAP_CS_NO_INFO; - } - - l2cap_connection_response(l2cap, cid, source_cid, result, status); - - return ch; -} - -static void l2cap_channel_close(struct l2cap_instance_s *l2cap, - int cid, int source_cid) -{ - struct l2cap_chan_s *ch = NULL; - - /* According to Volume 3, section 6.1.1, pg 1048 of BT Core V2.0, a - * connection in CLOSED state still responds with a L2CAP_DisconnectRsp - * message on an L2CAP_DisconnectReq event. */ - if (unlikely(cid < L2CAP_CID_ALLOC)) { - l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL, - cid, source_cid); - return; - } - if (likely(cid >= L2CAP_CID_ALLOC && cid < L2CAP_CID_MAX)) - ch = l2cap->cid[cid]; - - if (likely(ch)) { - if (ch->remote_cid != source_cid) { - fprintf(stderr, "%s: Ignoring a Disconnection Request with the " - "invalid SCID %04x.\n", __FUNCTION__, source_cid); - return; - } - - l2cap->cid[cid] = NULL; - - ch->params.close(ch->params.opaque); - g_free(ch); - } - - l2cap_disconnection_response(l2cap, cid, source_cid); -} - -static void l2cap_channel_config_null(struct l2cap_instance_s *l2cap, - struct l2cap_chan_s *ch) -{ - l2cap_configuration_request(l2cap, ch->remote_cid, 0, NULL, 0); - ch->config_req_id = l2cap->last_id; - ch->config &= ~L2CAP_CFG_INIT; -} - -static void l2cap_channel_config_req_event(struct l2cap_instance_s *l2cap, - struct l2cap_chan_s *ch) -{ - /* Use all default channel options and terminate negotiation. */ - l2cap_channel_config_null(l2cap, ch); -} - -static int l2cap_channel_config(struct l2cap_instance_s *l2cap, - struct l2cap_chan_s *ch, int flag, - const uint8_t *data, int len) -{ - l2cap_conf_opt *opt; - l2cap_conf_opt_qos *qos; - uint32_t val; - uint8_t rsp[len]; - int result = L2CAP_CONF_SUCCESS; - - data = memcpy(rsp, data, len); - while (len) { - opt = (void *) data; - - if (len < L2CAP_CONF_OPT_SIZE || - len < L2CAP_CONF_OPT_SIZE + opt->len) { - result = L2CAP_CONF_REJECT; - break; - } - data += L2CAP_CONF_OPT_SIZE + opt->len; - len -= L2CAP_CONF_OPT_SIZE + opt->len; - - switch (opt->type & 0x7f) { - case L2CAP_CONF_MTU: - if (opt->len != 2) { - result = L2CAP_CONF_REJECT; - break; - } - - /* MTU */ - val = le16_to_cpup((void *) opt->val); - if (val < ch->min_mtu) { - cpu_to_le16w((void *) opt->val, ch->min_mtu); - result = L2CAP_CONF_UNACCEPT; - break; - } - - ch->params.remote_mtu = val; - break; - - case L2CAP_CONF_FLUSH_TO: - if (opt->len != 2) { - result = L2CAP_CONF_REJECT; - break; - } - - /* Flush Timeout */ - val = le16_to_cpup((void *) opt->val); - if (val < 0x0001) { - opt->val[0] = 0xff; - opt->val[1] = 0xff; - result = L2CAP_CONF_UNACCEPT; - break; - } - break; - - case L2CAP_CONF_QOS: - if (opt->len != L2CAP_CONF_OPT_QOS_SIZE) { - result = L2CAP_CONF_REJECT; - break; - } - qos = (void *) opt->val; - - /* Flags */ - val = qos->flags; - if (val) { - qos->flags = 0; - result = L2CAP_CONF_UNACCEPT; - } - - /* Service type */ - val = qos->service_type; - if (val != L2CAP_CONF_QOS_BEST_EFFORT && - val != L2CAP_CONF_QOS_NO_TRAFFIC) { - qos->service_type = L2CAP_CONF_QOS_BEST_EFFORT; - result = L2CAP_CONF_UNACCEPT; - } - - if (val != L2CAP_CONF_QOS_NO_TRAFFIC) { - /* XXX: These values should possibly be calculated - * based on LM / baseband properties also. */ - - /* Token rate */ - val = le32_to_cpu(qos->token_rate); - if (val == L2CAP_CONF_QOS_WILDCARD) - qos->token_rate = cpu_to_le32(0x100000); - - /* Token bucket size */ - val = le32_to_cpu(qos->token_bucket_size); - if (val == L2CAP_CONF_QOS_WILDCARD) - qos->token_bucket_size = cpu_to_le32(65500); - - /* Any Peak bandwidth value is correct to return as-is */ - /* Any Access latency value is correct to return as-is */ - /* Any Delay variation value is correct to return as-is */ - } - break; - - case L2CAP_CONF_RFC: - if (opt->len != 9) { - result = L2CAP_CONF_REJECT; - break; - } - - /* Mode */ - val = opt->val[0]; - switch (val) { - case L2CAP_MODE_BASIC: - ch->mode = val; - ch->frame_in = l2cap_bframe_in; - - /* All other parameters shall be ignored */ - break; - - case L2CAP_MODE_RETRANS: - case L2CAP_MODE_FLOWCTL: - ch->mode = val; - ch->frame_in = l2cap_iframe_in; - /* Note: most of these parameters refer to incoming traffic - * so we don't need to save them as long as we can accept - * incoming PDUs at any values of the parameters. */ - - /* TxWindow size */ - val = opt->val[1]; - if (val < 1 || val > 32) { - opt->val[1] = 32; - result = L2CAP_CONF_UNACCEPT; - break; - } - - /* MaxTransmit */ - val = opt->val[2]; - if (val < 1) { - opt->val[2] = 1; - result = L2CAP_CONF_UNACCEPT; - break; - } - - /* Remote Retransmission time-out shouldn't affect local - * operation (?) */ - - /* The Monitor time-out drives the local Monitor timer (?), - * so save the value. */ - val = (opt->val[6] << 8) | opt->val[5]; - if (val < 30) { - opt->val[5] = 100 & 0xff; - opt->val[6] = 100 >> 8; - result = L2CAP_CONF_UNACCEPT; - break; - } - ch->monitor_timeout = val; - l2cap_monitor_timer_update(ch); - - /* MPS */ - val = (opt->val[8] << 8) | opt->val[7]; - if (val < ch->min_mtu) { - opt->val[7] = ch->min_mtu & 0xff; - opt->val[8] = ch->min_mtu >> 8; - result = L2CAP_CONF_UNACCEPT; - break; - } - ch->mps = val; - break; - - default: - result = L2CAP_CONF_UNACCEPT; - break; - } - break; - - default: - if (!(opt->type >> 7)) - result = L2CAP_CONF_UNKNOWN; - break; - } - - if (result != L2CAP_CONF_SUCCESS) - break; /* XXX: should continue? */ - } - - l2cap_configuration_response(l2cap, ch->remote_cid, - flag, result, rsp, len); - - return result == L2CAP_CONF_SUCCESS && !flag; -} - -static void l2cap_channel_config_req_msg(struct l2cap_instance_s *l2cap, - int flag, int cid, const uint8_t *data, int len) -{ - struct l2cap_chan_s *ch; - - if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) { - l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL, - cid, 0x0000); - return; - } - ch = l2cap->cid[cid]; - - /* From OPEN go to WAIT_CONFIG_REQ and from WAIT_CONFIG_REQ_RSP to - * WAIT_CONFIG_REQ_RSP. This is assuming the transition chart for OPEN - * on pg 1053, section 6.1.5, volume 3 of BT Core V2.0 has a mistake - * and on options-acceptable we go back to OPEN and otherwise to - * WAIT_CONFIG_REQ and not the other way. */ - ch->config &= ~L2CAP_CFG_ACC; - - if (l2cap_channel_config(l2cap, ch, flag, data, len)) - /* Go to OPEN or WAIT_CONFIG_RSP */ - ch->config |= L2CAP_CFG_ACC; - - /* TODO: if the incoming traffic flow control or retransmission mode - * changed then we probably need to also generate the - * ConfigureChannel_Req event and set the outgoing traffic to the same - * mode. */ - if (!(ch->config & L2CAP_CFG_INIT) && (ch->config & L2CAP_CFG_ACC) && - !ch->config_req_id) - l2cap_channel_config_req_event(l2cap, ch); -} - -static int l2cap_channel_config_rsp_msg(struct l2cap_instance_s *l2cap, - int result, int flag, int cid, const uint8_t *data, int len) -{ - struct l2cap_chan_s *ch; - - if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) { - l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL, - cid, 0x0000); - return 0; - } - ch = l2cap->cid[cid]; - - if (ch->config_req_id != l2cap->last_id) - return 1; - ch->config_req_id = 0; - - if (result == L2CAP_CONF_SUCCESS) { - if (!flag) - ch->config |= L2CAP_CFG_INIT; - else - l2cap_channel_config_null(l2cap, ch); - } else - /* Retry until we succeed */ - l2cap_channel_config_req_event(l2cap, ch); - - return 0; -} - -static void l2cap_channel_open_req_msg(struct l2cap_instance_s *l2cap, - int psm, int source_cid) -{ - struct l2cap_chan_s *ch = l2cap_channel_open(l2cap, psm, source_cid); - - if (!ch) - return; - - /* Optional */ - if (!(ch->config & L2CAP_CFG_INIT) && !ch->config_req_id) - l2cap_channel_config_req_event(l2cap, ch); -} - -static void l2cap_info(struct l2cap_instance_s *l2cap, int type) -{ - uint8_t data[4]; - int len = 0; - int result = L2CAP_IR_SUCCESS; - - switch (type) { - case L2CAP_IT_CL_MTU: - data[len ++] = l2cap->group_ch.mps & 0xff; - data[len ++] = l2cap->group_ch.mps >> 8; - break; - - case L2CAP_IT_FEAT_MASK: - /* (Prematurely) report Flow control and Retransmission modes. */ - data[len ++] = 0x03; - data[len ++] = 0x00; - data[len ++] = 0x00; - data[len ++] = 0x00; - break; - - default: - result = L2CAP_IR_NOTSUPP; - } - - l2cap_info_response(l2cap, type, result, data, len); -} - -static void l2cap_command(struct l2cap_instance_s *l2cap, int code, int id, - const uint8_t *params, int len) -{ - int err; - -#if 0 - /* TODO: do the IDs really have to be in sequence? */ - if (!id || (id != l2cap->last_id && id != l2cap->next_id)) { - fprintf(stderr, "%s: out of sequence command packet ignored.\n", - __FUNCTION__); - return; - } -#else - l2cap->next_id = id; -#endif - if (id == l2cap->next_id) { - l2cap->last_id = l2cap->next_id; - l2cap->next_id = l2cap->next_id == 255 ? 1 : l2cap->next_id + 1; - } else { - /* TODO: Need to re-send the same response, without re-executing - * the corresponding command! */ - } - - switch (code) { - case L2CAP_COMMAND_REJ: - if (unlikely(len != 2 && len != 4 && len != 6)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - /* We never issue commands other than Command Reject currently. */ - fprintf(stderr, "%s: stray Command Reject (%02x, %04x) " - "packet, ignoring.\n", __FUNCTION__, id, - le16_to_cpu(((l2cap_cmd_rej *) params)->reason)); - break; - - case L2CAP_CONN_REQ: - if (unlikely(len != L2CAP_CONN_REQ_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - l2cap_channel_open_req_msg(l2cap, - le16_to_cpu(((l2cap_conn_req *) params)->psm), - le16_to_cpu(((l2cap_conn_req *) params)->scid)); - break; - - case L2CAP_CONN_RSP: - if (unlikely(len != L2CAP_CONN_RSP_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - /* We never issue Connection Requests currently. TODO */ - fprintf(stderr, "%s: unexpected Connection Response (%02x) " - "packet, ignoring.\n", __FUNCTION__, id); - break; - - case L2CAP_CONF_REQ: - if (unlikely(len < L2CAP_CONF_REQ_SIZE(0))) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - l2cap_channel_config_req_msg(l2cap, - le16_to_cpu(((l2cap_conf_req *) params)->flags) & 1, - le16_to_cpu(((l2cap_conf_req *) params)->dcid), - ((l2cap_conf_req *) params)->data, - len - L2CAP_CONF_REQ_SIZE(0)); - break; - - case L2CAP_CONF_RSP: - if (unlikely(len < L2CAP_CONF_RSP_SIZE(0))) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - if (l2cap_channel_config_rsp_msg(l2cap, - le16_to_cpu(((l2cap_conf_rsp *) params)->result), - le16_to_cpu(((l2cap_conf_rsp *) params)->flags) & 1, - le16_to_cpu(((l2cap_conf_rsp *) params)->scid), - ((l2cap_conf_rsp *) params)->data, - len - L2CAP_CONF_RSP_SIZE(0))) - fprintf(stderr, "%s: unexpected Configure Response (%02x) " - "packet, ignoring.\n", __FUNCTION__, id); - break; - - case L2CAP_DISCONN_REQ: - if (unlikely(len != L2CAP_DISCONN_REQ_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - l2cap_channel_close(l2cap, - le16_to_cpu(((l2cap_disconn_req *) params)->dcid), - le16_to_cpu(((l2cap_disconn_req *) params)->scid)); - break; - - case L2CAP_DISCONN_RSP: - if (unlikely(len != L2CAP_DISCONN_RSP_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - /* We never issue Disconnection Requests currently. TODO */ - fprintf(stderr, "%s: unexpected Disconnection Response (%02x) " - "packet, ignoring.\n", __FUNCTION__, id); - break; - - case L2CAP_ECHO_REQ: - l2cap_echo_response(l2cap, params, len); - break; - - case L2CAP_ECHO_RSP: - /* We never issue Echo Requests currently. TODO */ - fprintf(stderr, "%s: unexpected Echo Response (%02x) " - "packet, ignoring.\n", __FUNCTION__, id); - break; - - case L2CAP_INFO_REQ: - if (unlikely(len != L2CAP_INFO_REQ_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - l2cap_info(l2cap, le16_to_cpu(((l2cap_info_req *) params)->type)); - break; - - case L2CAP_INFO_RSP: - if (unlikely(len != L2CAP_INFO_RSP_SIZE)) { - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - goto reject; - } - - /* We never issue Information Requests currently. TODO */ - fprintf(stderr, "%s: unexpected Information Response (%02x) " - "packet, ignoring.\n", __FUNCTION__, id); - break; - - default: - err = L2CAP_REJ_CMD_NOT_UNDERSTOOD; - reject: - l2cap_command_reject(l2cap, id, err, 0, 0); - break; - } -} - -static void l2cap_rexmit_enable(struct l2cap_chan_s *ch, int enable) -{ - ch->rexmit = enable; - - l2cap_retransmission_timer_update(ch); - l2cap_monitor_timer_update(ch); -} - -/* Command frame SDU */ -static void l2cap_cframe_in(void *opaque, const uint8_t *data, int len) -{ - struct l2cap_instance_s *l2cap = opaque; - const l2cap_cmd_hdr *hdr; - int clen; - - while (len) { - hdr = (void *) data; - if (len < L2CAP_CMD_HDR_SIZE) - /* TODO: signal an error */ - return; - len -= L2CAP_CMD_HDR_SIZE; - data += L2CAP_CMD_HDR_SIZE; - - clen = le16_to_cpu(hdr->len); - if (len < clen) { - l2cap_command_reject(l2cap, hdr->ident, - L2CAP_REJ_CMD_NOT_UNDERSTOOD, 0, 0); - break; - } - - l2cap_command(l2cap, hdr->code, hdr->ident, data, clen); - len -= clen; - data += clen; - } -} - -/* Group frame SDU */ -static void l2cap_gframe_in(void *opaque, const uint8_t *data, int len) -{ -} - -/* Supervisory frame */ -static void l2cap_sframe_in(struct l2cap_chan_s *ch, uint16_t ctrl) -{ -} - -/* Basic L2CAP mode Information frame */ -static void l2cap_bframe_in(struct l2cap_chan_s *ch, uint16_t cid, - const l2cap_hdr *hdr, int len) -{ - /* We have a full SDU, no further processing */ - ch->params.sdu_in(ch->params.opaque, hdr->data, len); -} - -/* Flow Control and Retransmission mode frame */ -static void l2cap_iframe_in(struct l2cap_chan_s *ch, uint16_t cid, - const l2cap_hdr *hdr, int len) -{ - uint16_t fcs = le16_to_cpup((void *) (hdr->data + len - 2)); - - if (len < 4) - goto len_error; - if (l2cap_fcs16((const uint8_t *) hdr, L2CAP_HDR_SIZE + len - 2) != fcs) - goto fcs_error; - - if ((hdr->data[0] >> 7) == ch->rexmit) - l2cap_rexmit_enable(ch, !(hdr->data[0] >> 7)); - - if (hdr->data[0] & 1) { - if (len != 4) { - /* TODO: Signal an error? */ - return; - } - l2cap_sframe_in(ch, le16_to_cpup((void *) hdr->data)); - return; - } - - switch (hdr->data[1] >> 6) { /* SAR */ - case L2CAP_SAR_NO_SEG: - if (ch->len_total) - goto seg_error; - if (len - 4 > ch->mps) - goto len_error; - - ch->params.sdu_in(ch->params.opaque, hdr->data + 2, len - 4); - break; - - case L2CAP_SAR_START: - if (ch->len_total || len < 6) - goto seg_error; - if (len - 6 > ch->mps) - goto len_error; - - ch->len_total = le16_to_cpup((void *) (hdr->data + 2)); - if (len >= 6 + ch->len_total) - goto seg_error; - - ch->len_cur = len - 6; - memcpy(ch->sdu, hdr->data + 4, ch->len_cur); - break; - - case L2CAP_SAR_END: - if (!ch->len_total || ch->len_cur + len - 4 < ch->len_total) - goto seg_error; - if (len - 4 > ch->mps) - goto len_error; - - memcpy(ch->sdu + ch->len_cur, hdr->data + 2, len - 4); - ch->params.sdu_in(ch->params.opaque, ch->sdu, ch->len_total); - break; - - case L2CAP_SAR_CONT: - if (!ch->len_total || ch->len_cur + len - 4 >= ch->len_total) - goto seg_error; - if (len - 4 > ch->mps) - goto len_error; - - memcpy(ch->sdu + ch->len_cur, hdr->data + 2, len - 4); - ch->len_cur += len - 4; - break; - - seg_error: - len_error: /* TODO */ - fcs_error: /* TODO */ - ch->len_cur = 0; - ch->len_total = 0; - break; - } -} - -static void l2cap_frame_in(struct l2cap_instance_s *l2cap, - const l2cap_hdr *frame) -{ - uint16_t cid = le16_to_cpu(frame->cid); - uint16_t len = le16_to_cpu(frame->len); - - if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) { - fprintf(stderr, "%s: frame addressed to a non-existent L2CAP " - "channel %04x received.\n", __FUNCTION__, cid); - return; - } - - l2cap->cid[cid]->frame_in(l2cap->cid[cid], cid, frame, len); -} - -/* "Recombination" */ -static void l2cap_pdu_in(struct l2cap_instance_s *l2cap, - const uint8_t *data, int len) -{ - const l2cap_hdr *hdr = (void *) l2cap->frame_in; - - if (unlikely(len + l2cap->frame_in_len > sizeof(l2cap->frame_in))) { - if (l2cap->frame_in_len < sizeof(l2cap->frame_in)) { - memcpy(l2cap->frame_in + l2cap->frame_in_len, data, - sizeof(l2cap->frame_in) - l2cap->frame_in_len); - l2cap->frame_in_len = sizeof(l2cap->frame_in); - /* TODO: truncate */ - l2cap_frame_in(l2cap, hdr); - } - - return; - } - - memcpy(l2cap->frame_in + l2cap->frame_in_len, data, len); - l2cap->frame_in_len += len; - - if (len >= L2CAP_HDR_SIZE) - if (len >= L2CAP_HDR_SIZE + le16_to_cpu(hdr->len)) - l2cap_frame_in(l2cap, hdr); - /* There is never a start of a new PDU in the same ACL packet, so - * no need to memmove the remaining payload and loop. */ -} - -static inline uint8_t *l2cap_pdu_out(struct l2cap_instance_s *l2cap, - uint16_t cid, uint16_t len) -{ - l2cap_hdr *hdr = (void *) l2cap->frame_out; - - l2cap->frame_out_len = len + L2CAP_HDR_SIZE; - - hdr->cid = cpu_to_le16(cid); - hdr->len = cpu_to_le16(len); - - return l2cap->frame_out + L2CAP_HDR_SIZE; -} - -static inline void l2cap_pdu_submit(struct l2cap_instance_s *l2cap) -{ - /* TODO: Fragmentation */ - (l2cap->role ? - l2cap->link->slave->lmp_acl_data : l2cap->link->host->lmp_acl_resp) - (l2cap->link, l2cap->frame_out, 1, l2cap->frame_out_len); -} - -static uint8_t *l2cap_bframe_out(struct bt_l2cap_conn_params_s *parm, int len) -{ - struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parm; - - if (len > chan->params.remote_mtu) { - fprintf(stderr, "%s: B-Frame for CID %04x longer than %i octets.\n", - __FUNCTION__, - chan->remote_cid, chan->params.remote_mtu); - exit(-1); - } - - return l2cap_pdu_out(chan->l2cap, chan->remote_cid, len); -} - -static void l2cap_bframe_submit(struct bt_l2cap_conn_params_s *parms) -{ - struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parms; - - l2cap_pdu_submit(chan->l2cap); -} - -#if 0 -/* Stub: Only used if an emulated device requests outgoing flow control */ -static uint8_t *l2cap_iframe_out(struct bt_l2cap_conn_params_s *parm, int len) -{ - struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parm; - - if (len > chan->params.remote_mtu) { - /* TODO: slice into segments and queue each segment as a separate - * I-Frame in a FIFO of I-Frames, local to the CID. */ - } else { - /* TODO: add to the FIFO of I-Frames, local to the CID. */ - /* Possibly we need to return a pointer to a contiguous buffer - * for now and then memcpy from it into FIFOs in l2cap_iframe_submit - * while segmenting at the same time. */ - } - return 0; -} - -static void l2cap_iframe_submit(struct bt_l2cap_conn_params_s *parm) -{ - /* TODO: If flow control indicates clear to send, start submitting the - * invidual I-Frames from the FIFO, but don't remove them from there. - * Kick the appropriate timer until we get an S-Frame, and only then - * remove from FIFO or resubmit and re-kick the timer if the timer - * expired. */ -} -#endif - -static void l2cap_init(struct l2cap_instance_s *l2cap, - struct bt_link_s *link, int role) -{ - l2cap->link = link; - l2cap->role = role; - l2cap->dev = (struct bt_l2cap_device_s *) - (role ? link->host : link->slave); - - l2cap->next_id = 1; - - /* Establish the signalling channel */ - l2cap->signalling_ch.params.sdu_in = l2cap_cframe_in; - l2cap->signalling_ch.params.sdu_out = l2cap_bframe_out; - l2cap->signalling_ch.params.sdu_submit = l2cap_bframe_submit; - l2cap->signalling_ch.params.opaque = l2cap; - l2cap->signalling_ch.params.remote_mtu = 48; - l2cap->signalling_ch.remote_cid = L2CAP_CID_SIGNALLING; - l2cap->signalling_ch.frame_in = l2cap_bframe_in; - l2cap->signalling_ch.mps = 65536; - l2cap->signalling_ch.min_mtu = 48; - l2cap->signalling_ch.mode = L2CAP_MODE_BASIC; - l2cap->signalling_ch.l2cap = l2cap; - l2cap->cid[L2CAP_CID_SIGNALLING] = &l2cap->signalling_ch; - - /* Establish the connection-less data channel */ - l2cap->group_ch.params.sdu_in = l2cap_gframe_in; - l2cap->group_ch.params.opaque = l2cap; - l2cap->group_ch.frame_in = l2cap_bframe_in; - l2cap->group_ch.mps = 65533; - l2cap->group_ch.l2cap = l2cap; - l2cap->group_ch.remote_cid = L2CAP_CID_INVALID; - l2cap->cid[L2CAP_CID_GROUP] = &l2cap->group_ch; -} - -static void l2cap_teardown(struct l2cap_instance_s *l2cap, int send_disconnect) -{ - int cid; - - /* Don't send DISCONNECT if we are currently handling a DISCONNECT - * sent from the other side. */ - if (send_disconnect) { - if (l2cap->role) - l2cap->dev->device.lmp_disconnect_slave(l2cap->link); - /* l2cap->link is invalid from now on. */ - else - l2cap->dev->device.lmp_disconnect_master(l2cap->link); - } - - for (cid = L2CAP_CID_ALLOC; cid < L2CAP_CID_MAX; cid ++) - if (l2cap->cid[cid]) { - l2cap->cid[cid]->params.close(l2cap->cid[cid]->params.opaque); - g_free(l2cap->cid[cid]); - } - - if (l2cap->role) - g_free(l2cap); - else - g_free(l2cap->link); -} - -/* L2CAP glue to lower layers in bluetooth stack (LMP) */ - -static void l2cap_lmp_connection_request(struct bt_link_s *link) -{ - struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->slave; - struct slave_l2cap_instance_s *l2cap; - - /* Always accept - we only get called if (dev->device->page_scan). */ - - l2cap = g_malloc0(sizeof(struct slave_l2cap_instance_s)); - l2cap->link.slave = &dev->device; - l2cap->link.host = link->host; - l2cap_init(&l2cap->l2cap, &l2cap->link, 0); - - /* Always at the end */ - link->host->reject_reason = 0; - link->host->lmp_connection_complete(&l2cap->link); -} - -/* Stub */ -static void l2cap_lmp_connection_complete(struct bt_link_s *link) -{ - struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host; - struct l2cap_instance_s *l2cap; - - if (dev->device.reject_reason) { - /* Signal to upper layer */ - return; - } - - l2cap = g_malloc0(sizeof(struct l2cap_instance_s)); - l2cap_init(l2cap, link, 1); - - link->acl_mode = acl_active; - - /* Signal to upper layer */ -} - -/* Stub */ -static void l2cap_lmp_disconnect_host(struct bt_link_s *link) -{ - struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host; - struct l2cap_instance_s *l2cap = - /* TODO: Retrieve from upper layer */ (void *) dev; - - /* Signal to upper layer */ - - l2cap_teardown(l2cap, 0); -} - -static void l2cap_lmp_disconnect_slave(struct bt_link_s *link) -{ - struct slave_l2cap_instance_s *l2cap = - (struct slave_l2cap_instance_s *) link; - - l2cap_teardown(&l2cap->l2cap, 0); -} - -static void l2cap_lmp_acl_data_slave(struct bt_link_s *link, - const uint8_t *data, int start, int len) -{ - struct slave_l2cap_instance_s *l2cap = - (struct slave_l2cap_instance_s *) link; - - if (start) - l2cap->l2cap.frame_in_len = 0; - - l2cap_pdu_in(&l2cap->l2cap, data, len); -} - -/* Stub */ -static void l2cap_lmp_acl_data_host(struct bt_link_s *link, - const uint8_t *data, int start, int len) -{ - struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host; - struct l2cap_instance_s *l2cap = - /* TODO: Retrieve from upper layer */ (void *) dev; - - if (start) - l2cap->frame_in_len = 0; - - l2cap_pdu_in(l2cap, data, len); -} - -static void l2cap_dummy_destroy(struct bt_device_s *dev) -{ - struct bt_l2cap_device_s *l2cap_dev = (struct bt_l2cap_device_s *) dev; - - bt_l2cap_device_done(l2cap_dev); -} - -void bt_l2cap_device_init(struct bt_l2cap_device_s *dev, - struct bt_scatternet_s *net) -{ - bt_device_init(&dev->device, net); - - dev->device.lmp_connection_request = l2cap_lmp_connection_request; - dev->device.lmp_connection_complete = l2cap_lmp_connection_complete; - dev->device.lmp_disconnect_master = l2cap_lmp_disconnect_host; - dev->device.lmp_disconnect_slave = l2cap_lmp_disconnect_slave; - dev->device.lmp_acl_data = l2cap_lmp_acl_data_slave; - dev->device.lmp_acl_resp = l2cap_lmp_acl_data_host; - - dev->device.handle_destroy = l2cap_dummy_destroy; -} - -void bt_l2cap_device_done(struct bt_l2cap_device_s *dev) -{ - bt_device_done(&dev->device); - - /* Should keep a list of all instances and go through it and - * invoke l2cap_teardown() for each. */ -} - -void bt_l2cap_psm_register(struct bt_l2cap_device_s *dev, int psm, int min_mtu, - int (*new_channel)(struct bt_l2cap_device_s *dev, - struct bt_l2cap_conn_params_s *params)) -{ - struct bt_l2cap_psm_s *new_psm = l2cap_psm(dev, psm); - - if (new_psm) { - fprintf(stderr, "%s: PSM %04x already registered for device `%s'.\n", - __FUNCTION__, psm, dev->device.lmp_name); - exit(-1); - } - - new_psm = g_malloc0(sizeof(*new_psm)); - new_psm->psm = psm; - new_psm->min_mtu = min_mtu; - new_psm->new_channel = new_channel; - new_psm->next = dev->first_psm; - dev->first_psm = new_psm; -} diff --git a/qemu/hw/bt/sdp.c b/qemu/hw/bt/sdp.c deleted file mode 100644 index be26009b0..000000000 --- a/qemu/hw/bt/sdp.c +++ /dev/null @@ -1,979 +0,0 @@ -/* - * Service Discover Protocol server for QEMU L2CAP devices - * - * Copyright (C) 2008 Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "hw/bt.h" - -struct bt_l2cap_sdp_state_s { - struct bt_l2cap_conn_params_s *channel; - - struct sdp_service_record_s { - int match; - - int *uuid; - int uuids; - struct sdp_service_attribute_s { - int match; - - int attribute_id; - int len; - void *pair; - } *attribute_list; - int attributes; - } *service_list; - int services; -}; - -static ssize_t sdp_datalen(const uint8_t **element, ssize_t *left) -{ - uint32_t len = *(*element) ++ & SDP_DSIZE_MASK; - - if (!*left) - return -1; - (*left) --; - - if (len < SDP_DSIZE_NEXT1) - return 1 << len; - else if (len == SDP_DSIZE_NEXT1) { - if (*left < 1) - return -1; - (*left) --; - - return *(*element) ++; - } else if (len == SDP_DSIZE_NEXT2) { - if (*left < 2) - return -1; - (*left) -= 2; - - len = (*(*element) ++) << 8; - return len | (*(*element) ++); - } else { - if (*left < 4) - return -1; - (*left) -= 4; - - len = (*(*element) ++) << 24; - len |= (*(*element) ++) << 16; - len |= (*(*element) ++) << 8; - return len | (*(*element) ++); - } -} - -static const uint8_t bt_base_uuid[12] = { - 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb, -}; - -static int sdp_uuid_match(struct sdp_service_record_s *record, - const uint8_t *uuid, ssize_t datalen) -{ - int *lo, hi, val; - - if (datalen == 16 || datalen == 4) { - if (datalen == 16 && memcmp(uuid + 4, bt_base_uuid, 12)) - return 0; - - if (uuid[0] | uuid[1]) - return 0; - uuid += 2; - } - - val = (uuid[0] << 8) | uuid[1]; - lo = record->uuid; - hi = record->uuids; - while (hi >>= 1) - if (lo[hi] <= val) - lo += hi; - - return *lo == val; -} - -#define CONTINUATION_PARAM_SIZE (1 + sizeof(int)) -#define MAX_PDU_OUT_SIZE 96 /* Arbitrary */ -#define PDU_HEADER_SIZE 5 -#define MAX_RSP_PARAM_SIZE (MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE - \ - CONTINUATION_PARAM_SIZE) - -static int sdp_svc_match(struct bt_l2cap_sdp_state_s *sdp, - const uint8_t **req, ssize_t *len) -{ - size_t datalen; - int i; - - if ((**req & ~SDP_DSIZE_MASK) != SDP_DTYPE_UUID) - return 1; - - datalen = sdp_datalen(req, len); - if (datalen != 2 && datalen != 4 && datalen != 16) - return 1; - - for (i = 0; i < sdp->services; i ++) - if (sdp_uuid_match(&sdp->service_list[i], *req, datalen)) - sdp->service_list[i].match = 1; - - (*req) += datalen; - (*len) -= datalen; - - return 0; -} - -static ssize_t sdp_svc_search(struct bt_l2cap_sdp_state_s *sdp, - uint8_t *rsp, const uint8_t *req, ssize_t len) -{ - ssize_t seqlen; - int i, count, start, end, max; - int32_t handle; - - /* Perform the search */ - for (i = 0; i < sdp->services; i ++) - sdp->service_list[i].match = 0; - - if (len < 1) - return -SDP_INVALID_SYNTAX; - if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { - seqlen = sdp_datalen(&req, &len); - 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, &len)) { - return -SDP_INVALID_SYNTAX; - } - } - - if (len < 3) - return -SDP_INVALID_SYNTAX; - max = (req[0] << 8) | req[1]; - req += 2; - len -= 2; - - if (*req) { - if (len <= sizeof(int)) - return -SDP_INVALID_SYNTAX; - len -= sizeof(int); - memcpy(&start, req + 1, sizeof(int)); - } else - start = 0; - - if (len > 1) - return -SDP_INVALID_SYNTAX; - - /* Output the results */ - len = 4; - count = 0; - end = start; - for (i = 0; i < sdp->services; i ++) - if (sdp->service_list[i].match) { - if (count >= start && count < max && len + 4 < MAX_RSP_PARAM_SIZE) { - handle = i; - memcpy(rsp + len, &handle, 4); - len += 4; - end = count + 1; - } - - count ++; - } - - rsp[0] = count >> 8; - rsp[1] = count & 0xff; - rsp[2] = (end - start) >> 8; - rsp[3] = (end - start) & 0xff; - - if (end < count) { - rsp[len ++] = sizeof(int); - memcpy(rsp + len, &end, sizeof(int)); - len += 4; - } else - rsp[len ++] = 0; - - return len; -} - -static int sdp_attr_match(struct sdp_service_record_s *record, - const uint8_t **req, ssize_t *len) -{ - int i, start, end; - - if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) { - (*req) ++; - if (*len < 3) - return 1; - - start = (*(*req) ++) << 8; - start |= *(*req) ++; - end = start; - *len -= 3; - } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) { - (*req) ++; - if (*len < 5) - return 1; - - start = (*(*req) ++) << 8; - start |= *(*req) ++; - end = (*(*req) ++) << 8; - end |= *(*req) ++; - *len -= 5; - } else - return 1; - - for (i = 0; i < record->attributes; i ++) - if (record->attribute_list[i].attribute_id >= start && - record->attribute_list[i].attribute_id <= end) - record->attribute_list[i].match = 1; - - return 0; -} - -static ssize_t sdp_attr_get(struct bt_l2cap_sdp_state_s *sdp, - uint8_t *rsp, const uint8_t *req, ssize_t len) -{ - ssize_t seqlen; - int i, start, end, max; - int32_t handle; - struct sdp_service_record_s *record; - uint8_t *lst; - - /* Perform the search */ - if (len < 7) - return -SDP_INVALID_SYNTAX; - memcpy(&handle, req, 4); - req += 4; - len -= 4; - - if (handle < 0 || handle > sdp->services) - return -SDP_INVALID_RECORD_HANDLE; - record = &sdp->service_list[handle]; - - for (i = 0; i < record->attributes; i ++) - record->attribute_list[i].match = 0; - - max = (req[0] << 8) | req[1]; - req += 2; - len -= 2; - if (max < 0x0007) - return -SDP_INVALID_SYNTAX; - - if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { - seqlen = sdp_datalen(&req, &len); - if (seqlen < 3 || len < seqlen) - return -SDP_INVALID_SYNTAX; - len -= seqlen; - - while (seqlen) - 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; - - if (*req) { - if (len <= sizeof(int)) - return -SDP_INVALID_SYNTAX; - len -= sizeof(int); - memcpy(&start, req + 1, sizeof(int)); - } else - start = 0; - - if (len > 1) - return -SDP_INVALID_SYNTAX; - - /* Output the results */ - lst = rsp + 2; - max = MIN(max, MAX_RSP_PARAM_SIZE); - len = 3 - start; - end = 0; - for (i = 0; i < record->attributes; i ++) - if (record->attribute_list[i].match) { - if (len >= 0 && len + record->attribute_list[i].len < max) { - memcpy(lst + len, record->attribute_list[i].pair, - record->attribute_list[i].len); - end = len + record->attribute_list[i].len; - } - len += record->attribute_list[i].len; - } - if (0 >= start) { - lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2; - lst[1] = (len + start - 3) >> 8; - lst[2] = (len + start - 3) & 0xff; - } - - rsp[0] = end >> 8; - rsp[1] = end & 0xff; - - if (end < len) { - len = end + start; - lst[end ++] = sizeof(int); - memcpy(lst + end, &len, sizeof(int)); - end += sizeof(int); - } else - lst[end ++] = 0; - - return end + 2; -} - -static int sdp_svc_attr_match(struct bt_l2cap_sdp_state_s *sdp, - const uint8_t **req, ssize_t *len) -{ - int i, j, start, end; - struct sdp_service_record_s *record; - - if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) { - (*req) ++; - if (*len < 3) - return 1; - - start = (*(*req) ++) << 8; - start |= *(*req) ++; - end = start; - *len -= 3; - } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) { - (*req) ++; - if (*len < 5) - return 1; - - start = (*(*req) ++) << 8; - start |= *(*req) ++; - end = (*(*req) ++) << 8; - end |= *(*req) ++; - *len -= 5; - } else - return 1; - - for (i = 0; i < sdp->services; i ++) - if ((record = &sdp->service_list[i])->match) - for (j = 0; j < record->attributes; j ++) - if (record->attribute_list[j].attribute_id >= start && - record->attribute_list[j].attribute_id <= end) - record->attribute_list[j].match = 1; - - return 0; -} - -static ssize_t sdp_svc_search_attr_get(struct bt_l2cap_sdp_state_s *sdp, - uint8_t *rsp, const uint8_t *req, ssize_t len) -{ - ssize_t seqlen; - int i, j, start, end, max; - struct sdp_service_record_s *record; - uint8_t *lst; - - /* Perform the search */ - for (i = 0; i < sdp->services; i ++) { - sdp->service_list[i].match = 0; - for (j = 0; j < sdp->service_list[i].attributes; j ++) - sdp->service_list[i].attribute_list[j].match = 0; - } - - if (len < 1) - return -SDP_INVALID_SYNTAX; - if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { - seqlen = sdp_datalen(&req, &len); - 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, &len)) { - return -SDP_INVALID_SYNTAX; - } - } - - if (len < 3) - return -SDP_INVALID_SYNTAX; - max = (req[0] << 8) | req[1]; - req += 2; - len -= 2; - if (max < 0x0007) - return -SDP_INVALID_SYNTAX; - - if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) { - seqlen = sdp_datalen(&req, &len); - if (seqlen < 3 || len < seqlen) - return -SDP_INVALID_SYNTAX; - len -= seqlen; - - while (seqlen) - 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; - - if (*req) { - if (len <= sizeof(int)) - return -SDP_INVALID_SYNTAX; - len -= sizeof(int); - memcpy(&start, req + 1, sizeof(int)); - } else - start = 0; - - if (len > 1) - return -SDP_INVALID_SYNTAX; - - /* Output the results */ - /* This assumes empty attribute lists are never to be returned even - * for matching Service Records. In practice this shouldn't happen - * as the requestor will usually include the always present - * ServiceRecordHandle AttributeID in AttributeIDList. */ - lst = rsp + 2; - max = MIN(max, MAX_RSP_PARAM_SIZE); - len = 3 - start; - end = 0; - for (i = 0; i < sdp->services; i ++) - if ((record = &sdp->service_list[i])->match) { - len += 3; - seqlen = len; - for (j = 0; j < record->attributes; j ++) - if (record->attribute_list[j].match) { - if (len >= 0) - if (len + record->attribute_list[j].len < max) { - memcpy(lst + len, record->attribute_list[j].pair, - record->attribute_list[j].len); - end = len + record->attribute_list[j].len; - } - len += record->attribute_list[j].len; - } - if (seqlen == len) - len -= 3; - else if (seqlen >= 3 && seqlen < max) { - lst[seqlen - 3] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2; - lst[seqlen - 2] = (len - seqlen) >> 8; - lst[seqlen - 1] = (len - seqlen) & 0xff; - } - } - if (len == 3 - start) - len -= 3; - else if (0 >= start) { - lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2; - lst[1] = (len + start - 3) >> 8; - lst[2] = (len + start - 3) & 0xff; - } - - rsp[0] = end >> 8; - rsp[1] = end & 0xff; - - if (end < len) { - len = end + start; - lst[end ++] = sizeof(int); - memcpy(lst + end, &len, sizeof(int)); - end += sizeof(int); - } else - lst[end ++] = 0; - - return end + 2; -} - -static void bt_l2cap_sdp_sdu_in(void *opaque, const uint8_t *data, int len) -{ - struct bt_l2cap_sdp_state_s *sdp = opaque; - enum bt_sdp_cmd pdu_id; - uint8_t rsp[MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE], *sdu_out; - int transaction_id, plen; - int err = 0; - int rsp_len = 0; - - if (len < 5) { - fprintf(stderr, "%s: short SDP PDU (%iB).\n", __FUNCTION__, len); - return; - } - - pdu_id = *data ++; - transaction_id = (data[0] << 8) | data[1]; - plen = (data[2] << 8) | data[3]; - data += 4; - len -= 5; - - if (len != plen) { - fprintf(stderr, "%s: wrong SDP PDU length (%iB != %iB).\n", - __FUNCTION__, plen, len); - err = SDP_INVALID_PDU_SIZE; - goto respond; - } - - switch (pdu_id) { - case SDP_SVC_SEARCH_REQ: - rsp_len = sdp_svc_search(sdp, rsp, data, len); - pdu_id = SDP_SVC_SEARCH_RSP; - break; - - case SDP_SVC_ATTR_REQ: - rsp_len = sdp_attr_get(sdp, rsp, data, len); - pdu_id = SDP_SVC_ATTR_RSP; - break; - - case SDP_SVC_SEARCH_ATTR_REQ: - rsp_len = sdp_svc_search_attr_get(sdp, rsp, data, len); - pdu_id = SDP_SVC_SEARCH_ATTR_RSP; - break; - - case SDP_ERROR_RSP: - case SDP_SVC_ATTR_RSP: - case SDP_SVC_SEARCH_RSP: - case SDP_SVC_SEARCH_ATTR_RSP: - default: - fprintf(stderr, "%s: unexpected SDP PDU ID %02x.\n", - __FUNCTION__, pdu_id); - err = SDP_INVALID_SYNTAX; - break; - } - - if (rsp_len < 0) { - err = -rsp_len; - rsp_len = 0; - } - -respond: - if (err) { - pdu_id = SDP_ERROR_RSP; - rsp[rsp_len ++] = err >> 8; - rsp[rsp_len ++] = err & 0xff; - } - - sdu_out = sdp->channel->sdu_out(sdp->channel, rsp_len + PDU_HEADER_SIZE); - - sdu_out[0] = pdu_id; - sdu_out[1] = transaction_id >> 8; - sdu_out[2] = transaction_id & 0xff; - sdu_out[3] = rsp_len >> 8; - sdu_out[4] = rsp_len & 0xff; - memcpy(sdu_out + PDU_HEADER_SIZE, rsp, rsp_len); - - sdp->channel->sdu_submit(sdp->channel); -} - -static void bt_l2cap_sdp_close_ch(void *opaque) -{ - struct bt_l2cap_sdp_state_s *sdp = opaque; - int i; - - for (i = 0; i < sdp->services; i ++) { - g_free(sdp->service_list[i].attribute_list->pair); - g_free(sdp->service_list[i].attribute_list); - g_free(sdp->service_list[i].uuid); - } - g_free(sdp->service_list); - g_free(sdp); -} - -struct sdp_def_service_s { - uint16_t class_uuid; - struct sdp_def_attribute_s { - uint16_t id; - struct sdp_def_data_element_s { - uint8_t type; - union { - uint32_t uint; - const char *str; - struct sdp_def_data_element_s *list; - } value; - } data; - } attributes[]; -}; - -/* Calculate a safe byte count to allocate that will store the given - * element, at the same time count elements of a UUID type. */ -static int sdp_attr_max_size(struct sdp_def_data_element_s *element, - int *uuids) -{ - int type = element->type & ~SDP_DSIZE_MASK; - int len; - - if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_UUID || - type == SDP_DTYPE_BOOL) { - if (type == SDP_DTYPE_UUID) - (*uuids) ++; - return 1 + (1 << (element->type & SDP_DSIZE_MASK)); - } - - if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) { - if (element->type & SDP_DSIZE_MASK) { - for (len = 0; element->value.str[len] | - element->value.str[len + 1]; len ++); - return len; - } else - return 2 + strlen(element->value.str); - } - - if (type != SDP_DTYPE_SEQ) - exit(-1); - len = 2; - element = element->value.list; - while (element->type) - len += sdp_attr_max_size(element ++, uuids); - if (len > 255) - exit (-1); - - return len; -} - -static int sdp_attr_write(uint8_t *data, - struct sdp_def_data_element_s *element, int **uuid) -{ - int type = element->type & ~SDP_DSIZE_MASK; - int len = 0; - - if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_BOOL) { - data[len ++] = element->type; - if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_1) - data[len ++] = (element->value.uint >> 0) & 0xff; - else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_2) { - data[len ++] = (element->value.uint >> 8) & 0xff; - data[len ++] = (element->value.uint >> 0) & 0xff; - } else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_4) { - data[len ++] = (element->value.uint >> 24) & 0xff; - data[len ++] = (element->value.uint >> 16) & 0xff; - data[len ++] = (element->value.uint >> 8) & 0xff; - data[len ++] = (element->value.uint >> 0) & 0xff; - } - - return len; - } - - if (type == SDP_DTYPE_UUID) { - *(*uuid) ++ = element->value.uint; - - data[len ++] = element->type; - data[len ++] = (element->value.uint >> 24) & 0xff; - data[len ++] = (element->value.uint >> 16) & 0xff; - data[len ++] = (element->value.uint >> 8) & 0xff; - data[len ++] = (element->value.uint >> 0) & 0xff; - memcpy(data + len, bt_base_uuid, 12); - - return len + 12; - } - - data[0] = type | SDP_DSIZE_NEXT1; - if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) { - if (element->type & SDP_DSIZE_MASK) - for (len = 0; element->value.str[len] | - element->value.str[len + 1]; len ++); - else - len = strlen(element->value.str); - memcpy(data + 2, element->value.str, data[1] = len); - - return len + 2; - } - - len = 2; - element = element->value.list; - while (element->type) - len += sdp_attr_write(data + len, element ++, uuid); - data[1] = len - 2; - - return len; -} - -static int sdp_attributeid_compare(const struct sdp_service_attribute_s *a, - const struct sdp_service_attribute_s *b) -{ - return (int) b->attribute_id - a->attribute_id; -} - -static int sdp_uuid_compare(const int *a, const int *b) -{ - return *a - *b; -} - -static void sdp_service_record_build(struct sdp_service_record_s *record, - struct sdp_def_service_s *def, int handle) -{ - int len = 0; - uint8_t *data; - int *uuid; - - record->uuids = 0; - while (def->attributes[record->attributes].data.type) { - len += 3; - len += sdp_attr_max_size(&def->attributes[record->attributes ++].data, - &record->uuids); - } - record->uuids = pow2ceil(record->uuids); - record->attribute_list = - g_malloc0(record->attributes * sizeof(*record->attribute_list)); - record->uuid = - g_malloc0(record->uuids * sizeof(*record->uuid)); - data = g_malloc(len); - - record->attributes = 0; - uuid = record->uuid; - while (def->attributes[record->attributes].data.type) { - record->attribute_list[record->attributes].pair = data; - - len = 0; - data[len ++] = SDP_DTYPE_UINT | SDP_DSIZE_2; - data[len ++] = def->attributes[record->attributes].id >> 8; - data[len ++] = def->attributes[record->attributes].id & 0xff; - len += sdp_attr_write(data + len, - &def->attributes[record->attributes].data, &uuid); - - /* Special case: assign a ServiceRecordHandle in sequence */ - if (def->attributes[record->attributes].id == SDP_ATTR_RECORD_HANDLE) - def->attributes[record->attributes].data.value.uint = handle; - /* Note: we could also assign a ServiceDescription based on - * sdp->device.device->lmp_name. */ - - record->attribute_list[record->attributes ++].len = len; - data += len; - } - - /* Sort the attribute list by the AttributeID */ - qsort(record->attribute_list, record->attributes, - sizeof(*record->attribute_list), - (void *) sdp_attributeid_compare); - /* Sort the searchable UUIDs list for bisection */ - qsort(record->uuid, record->uuids, - sizeof(*record->uuid), - (void *) sdp_uuid_compare); -} - -static void sdp_service_db_build(struct bt_l2cap_sdp_state_s *sdp, - struct sdp_def_service_s **service) -{ - sdp->services = 0; - while (service[sdp->services]) - sdp->services ++; - sdp->service_list = - g_malloc0(sdp->services * sizeof(*sdp->service_list)); - - sdp->services = 0; - while (*service) { - sdp_service_record_build(&sdp->service_list[sdp->services], - *service, sdp->services); - service ++; - sdp->services ++; - } -} - -#define LAST { .type = 0 } -#define SERVICE(name, attrs) \ - static struct sdp_def_service_s glue(glue(sdp_service_, name), _s) = { \ - .attributes = { attrs { .data = LAST } }, \ - }; -#define ATTRIBUTE(attrid, val) { .id = glue(SDP_ATTR_, attrid), .data = val }, -#define UINT8(val) { \ - .type = SDP_DTYPE_UINT | SDP_DSIZE_1, \ - .value.uint = val, \ - }, -#define UINT16(val) { \ - .type = SDP_DTYPE_UINT | SDP_DSIZE_2, \ - .value.uint = val, \ - }, -#define UINT32(val) { \ - .type = SDP_DTYPE_UINT | SDP_DSIZE_4, \ - .value.uint = val, \ - }, -#define UUID128(val) { \ - .type = SDP_DTYPE_UUID | SDP_DSIZE_16, \ - .value.uint = val, \ - }, -#define SDP_TRUE { \ - .type = SDP_DTYPE_BOOL | SDP_DSIZE_1, \ - .value.uint = 1, \ - }, -#define SDP_FALSE { \ - .type = SDP_DTYPE_BOOL | SDP_DSIZE_1, \ - .value.uint = 0, \ - }, -#define STRING(val) { \ - .type = SDP_DTYPE_STRING, \ - .value.str = val, \ - }, -#define ARRAY(...) { \ - .type = SDP_DTYPE_STRING | SDP_DSIZE_2, \ - .value.str = (char []) { __VA_ARGS__, 0, 0 }, \ - }, -#define URL(val) { \ - .type = SDP_DTYPE_URL, \ - .value.str = val, \ - }, -#if 1 -#define LIST(val) { \ - .type = SDP_DTYPE_SEQ, \ - .value.list = (struct sdp_def_data_element_s []) { val LAST }, \ - }, -#endif - -/* Try to keep each single attribute below MAX_PDU_OUT_SIZE bytes - * in resulting SDP data representation size. */ - -SERVICE(hid, - ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */ - ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(HID_SVCLASS_ID))) - ATTRIBUTE(RECORD_STATE, UINT32(1)) - ATTRIBUTE(PROTO_DESC_LIST, LIST( - LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_HID_CTRL)) - LIST(UUID128(HIDP_UUID)) - )) - ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002))) - ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST( - UINT16(0x656e) UINT16(0x006a) UINT16(0x0100) - )) - ATTRIBUTE(PFILE_DESC_LIST, LIST( - LIST(UUID128(HID_PROFILE_ID) UINT16(0x0100)) - )) - ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html")) - ATTRIBUTE(SVCNAME_PRIMARY, STRING("QEMU Bluetooth HID")) - ATTRIBUTE(SVCDESC_PRIMARY, STRING("QEMU Keyboard/Mouse")) - ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU")) - - /* Profile specific */ - ATTRIBUTE(DEVICE_RELEASE_NUMBER, UINT16(0x0091)) /* Deprecated, remove */ - ATTRIBUTE(PARSER_VERSION, UINT16(0x0111)) - /* TODO: extract from l2cap_device->device.class[0] */ - ATTRIBUTE(DEVICE_SUBCLASS, UINT8(0x40)) - ATTRIBUTE(COUNTRY_CODE, UINT8(0x15)) - ATTRIBUTE(VIRTUAL_CABLE, SDP_TRUE) - ATTRIBUTE(RECONNECT_INITIATE, SDP_FALSE) - /* TODO: extract from hid->usbdev->report_desc */ - ATTRIBUTE(DESCRIPTOR_LIST, LIST( - LIST(UINT8(0x22) ARRAY( - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x06, /* Usage (Keyboard) */ - 0xa1, 0x01, /* Collection (Application) */ - 0x75, 0x01, /* Report Size (1) */ - 0x95, 0x08, /* Report Count (8) */ - 0x05, 0x07, /* Usage Page (Key Codes) */ - 0x19, 0xe0, /* Usage Minimum (224) */ - 0x29, 0xe7, /* Usage Maximum (231) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0x81, 0x02, /* Input (Data, Variable, Absolute) */ - 0x95, 0x01, /* Report Count (1) */ - 0x75, 0x08, /* Report Size (8) */ - 0x81, 0x01, /* Input (Constant) */ - 0x95, 0x05, /* Report Count (5) */ - 0x75, 0x01, /* Report Size (1) */ - 0x05, 0x08, /* Usage Page (LEDs) */ - 0x19, 0x01, /* Usage Minimum (1) */ - 0x29, 0x05, /* Usage Maximum (5) */ - 0x91, 0x02, /* Output (Data, Variable, Absolute) */ - 0x95, 0x01, /* Report Count (1) */ - 0x75, 0x03, /* Report Size (3) */ - 0x91, 0x01, /* Output (Constant) */ - 0x95, 0x06, /* Report Count (6) */ - 0x75, 0x08, /* Report Size (8) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0xff, /* Logical Maximum (255) */ - 0x05, 0x07, /* Usage Page (Key Codes) */ - 0x19, 0x00, /* Usage Minimum (0) */ - 0x29, 0xff, /* Usage Maximum (255) */ - 0x81, 0x00, /* Input (Data, Array) */ - 0xc0 /* End Collection */ - )))) - ATTRIBUTE(LANG_ID_BASE_LIST, LIST( - LIST(UINT16(0x0409) UINT16(0x0100)) - )) - ATTRIBUTE(SDP_DISABLE, SDP_FALSE) - ATTRIBUTE(BATTERY_POWER, SDP_TRUE) - ATTRIBUTE(REMOTE_WAKEUP, SDP_TRUE) - ATTRIBUTE(BOOT_DEVICE, SDP_TRUE) /* XXX: untested */ - ATTRIBUTE(SUPERVISION_TIMEOUT, UINT16(0x0c80)) - ATTRIBUTE(NORMALLY_CONNECTABLE, SDP_TRUE) - ATTRIBUTE(PROFILE_VERSION, UINT16(0x0100)) -) - -SERVICE(sdp, - ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */ - ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(SDP_SERVER_SVCLASS_ID))) - ATTRIBUTE(RECORD_STATE, UINT32(1)) - ATTRIBUTE(PROTO_DESC_LIST, LIST( - LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP)) - LIST(UUID128(SDP_UUID)) - )) - ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002))) - ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST( - UINT16(0x656e) UINT16(0x006a) UINT16(0x0100) - )) - ATTRIBUTE(PFILE_DESC_LIST, LIST( - LIST(UUID128(SDP_SERVER_PROFILE_ID) UINT16(0x0100)) - )) - ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html")) - ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU")) - - /* Profile specific */ - ATTRIBUTE(VERSION_NUM_LIST, LIST(UINT16(0x0100))) - ATTRIBUTE(SVCDB_STATE , UINT32(1)) -) - -SERVICE(pnp, - ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */ - ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(PNP_INFO_SVCLASS_ID))) - ATTRIBUTE(RECORD_STATE, UINT32(1)) - ATTRIBUTE(PROTO_DESC_LIST, LIST( - LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP)) - LIST(UUID128(SDP_UUID)) - )) - ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002))) - ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST( - UINT16(0x656e) UINT16(0x006a) UINT16(0x0100) - )) - ATTRIBUTE(PFILE_DESC_LIST, LIST( - LIST(UUID128(PNP_INFO_PROFILE_ID) UINT16(0x0100)) - )) - ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html")) - ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU")) - - /* Profile specific */ - ATTRIBUTE(SPECIFICATION_ID, UINT16(0x0100)) - ATTRIBUTE(VERSION, UINT16(0x0100)) - ATTRIBUTE(PRIMARY_RECORD, SDP_TRUE) -) - -static int bt_l2cap_sdp_new_ch(struct bt_l2cap_device_s *dev, - struct bt_l2cap_conn_params_s *params) -{ - struct bt_l2cap_sdp_state_s *sdp = g_malloc0(sizeof(*sdp)); - struct sdp_def_service_s *services[] = { - &sdp_service_sdp_s, - &sdp_service_hid_s, - &sdp_service_pnp_s, - NULL, - }; - - sdp->channel = params; - sdp->channel->opaque = sdp; - sdp->channel->close = bt_l2cap_sdp_close_ch; - sdp->channel->sdu_in = bt_l2cap_sdp_sdu_in; - - sdp_service_db_build(sdp, services); - - return 0; -} - -void bt_l2cap_sdp_init(struct bt_l2cap_device_s *dev) -{ - bt_l2cap_psm_register(dev, BT_PSM_SDP, - MAX_PDU_OUT_SIZE, bt_l2cap_sdp_new_ch); -} diff --git a/qemu/hw/char/Makefile.objs b/qemu/hw/char/Makefile.objs deleted file mode 100644 index 69a553cd8..000000000 --- a/qemu/hw/char/Makefile.objs +++ /dev/null @@ -1,30 +0,0 @@ -common-obj-$(CONFIG_IPACK) += ipoctal232.o -common-obj-$(CONFIG_ESCC) += escc.o -common-obj-$(CONFIG_PARALLEL) += parallel.o -common-obj-$(CONFIG_PL011) += pl011.o -common-obj-$(CONFIG_SERIAL) += serial.o serial-isa.o -common-obj-$(CONFIG_SERIAL_PCI) += serial-pci.o -common-obj-$(CONFIG_VIRTIO) += virtio-console.o -common-obj-$(CONFIG_XILINX) += xilinx_uartlite.o -common-obj-$(CONFIG_XEN_BACKEND) += xen_console.o -common-obj-$(CONFIG_CADENCE) += cadence_uart.o - -obj-$(CONFIG_EXYNOS4) += exynos4210_uart.o -obj-$(CONFIG_COLDFIRE) += mcf_uart.o -obj-$(CONFIG_OMAP) += omap_uart.o -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 -common-obj-$(CONFIG_GRLIB) += grlib_apbuart.o -common-obj-$(CONFIG_IMX) += imx_serial.o -common-obj-$(CONFIG_LM32) += lm32_juart.o -common-obj-$(CONFIG_LM32) += lm32_uart.o -common-obj-$(CONFIG_MILKYMIST) += milkymist-uart.o -common-obj-$(CONFIG_SCLPCONSOLE) += sclpconsole.o sclpconsole-lm.o - -obj-$(CONFIG_VIRTIO) += virtio-serial-bus.o diff --git a/qemu/hw/char/bcm2835_aux.c b/qemu/hw/char/bcm2835_aux.c deleted file mode 100644 index 0394d11a8..000000000 --- a/qemu/hw/char/bcm2835_aux.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * 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 deleted file mode 100644 index 797787823..000000000 --- a/qemu/hw/char/cadence_uart.c +++ /dev/null @@ -1,540 +0,0 @@ -/* - * Device model for Cadence UART - * - * Copyright (c) 2010 Xilinx Inc. - * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com) - * Copyright (c) 2012 PetaLogix Pty Ltd. - * Written by Haibing Ma - * M.Habib - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "hw/char/cadence_uart.h" - -#ifdef CADENCE_UART_ERR_DEBUG -#define DB_PRINT(...) do { \ - fprintf(stderr, ": %s: ", __func__); \ - fprintf(stderr, ## __VA_ARGS__); \ - } while (0); -#else - #define DB_PRINT(...) -#endif - -#define UART_SR_INTR_RTRIG 0x00000001 -#define UART_SR_INTR_REMPTY 0x00000002 -#define UART_SR_INTR_RFUL 0x00000004 -#define UART_SR_INTR_TEMPTY 0x00000008 -#define UART_SR_INTR_TFUL 0x00000010 -/* somewhat awkwardly, TTRIG is misaligned between SR and ISR */ -#define UART_SR_TTRIG 0x00002000 -#define UART_INTR_TTRIG 0x00000400 -/* bits fields in CSR that correlate to CISR. If any of these bits are set in - * SR, then the same bit in CISR is set high too */ -#define UART_SR_TO_CISR_MASK 0x0000001F - -#define UART_INTR_ROVR 0x00000020 -#define UART_INTR_FRAME 0x00000040 -#define UART_INTR_PARE 0x00000080 -#define UART_INTR_TIMEOUT 0x00000100 -#define UART_INTR_DMSI 0x00000200 -#define UART_INTR_TOVR 0x00001000 - -#define UART_SR_RACTIVE 0x00000400 -#define UART_SR_TACTIVE 0x00000800 -#define UART_SR_FDELT 0x00001000 - -#define UART_CR_RXRST 0x00000001 -#define UART_CR_TXRST 0x00000002 -#define UART_CR_RX_EN 0x00000004 -#define UART_CR_RX_DIS 0x00000008 -#define UART_CR_TX_EN 0x00000010 -#define UART_CR_TX_DIS 0x00000020 -#define UART_CR_RST_TO 0x00000040 -#define UART_CR_STARTBRK 0x00000080 -#define UART_CR_STOPBRK 0x00000100 - -#define UART_MR_CLKS 0x00000001 -#define UART_MR_CHRL 0x00000006 -#define UART_MR_CHRL_SH 1 -#define UART_MR_PAR 0x00000038 -#define UART_MR_PAR_SH 3 -#define UART_MR_NBSTOP 0x000000C0 -#define UART_MR_NBSTOP_SH 6 -#define UART_MR_CHMODE 0x00000300 -#define UART_MR_CHMODE_SH 8 -#define UART_MR_UCLKEN 0x00000400 -#define UART_MR_IRMODE 0x00000800 - -#define UART_DATA_BITS_6 (0x3 << UART_MR_CHRL_SH) -#define UART_DATA_BITS_7 (0x2 << UART_MR_CHRL_SH) -#define UART_PARITY_ODD (0x1 << UART_MR_PAR_SH) -#define UART_PARITY_EVEN (0x0 << UART_MR_PAR_SH) -#define UART_STOP_BITS_1 (0x3 << UART_MR_NBSTOP_SH) -#define UART_STOP_BITS_2 (0x2 << UART_MR_NBSTOP_SH) -#define NORMAL_MODE (0x0 << UART_MR_CHMODE_SH) -#define ECHO_MODE (0x1 << UART_MR_CHMODE_SH) -#define LOCAL_LOOPBACK (0x2 << UART_MR_CHMODE_SH) -#define REMOTE_LOOPBACK (0x3 << UART_MR_CHMODE_SH) - -#define UART_INPUT_CLK 50000000 - -#define R_CR (0x00/4) -#define R_MR (0x04/4) -#define R_IER (0x08/4) -#define R_IDR (0x0C/4) -#define R_IMR (0x10/4) -#define R_CISR (0x14/4) -#define R_BRGR (0x18/4) -#define R_RTOR (0x1C/4) -#define R_RTRIG (0x20/4) -#define R_MCR (0x24/4) -#define R_MSR (0x28/4) -#define R_SR (0x2C/4) -#define R_TX_RX (0x30/4) -#define R_BDIV (0x34/4) -#define R_FDEL (0x38/4) -#define R_PMIN (0x3C/4) -#define R_PWID (0x40/4) -#define R_TTRIG (0x44/4) - - -static void uart_update_status(CadenceUARTState *s) -{ - s->r[R_SR] = 0; - - s->r[R_SR] |= s->rx_count == CADENCE_UART_RX_FIFO_SIZE ? UART_SR_INTR_RFUL - : 0; - s->r[R_SR] |= !s->rx_count ? UART_SR_INTR_REMPTY : 0; - s->r[R_SR] |= s->rx_count >= s->r[R_RTRIG] ? UART_SR_INTR_RTRIG : 0; - - s->r[R_SR] |= s->tx_count == CADENCE_UART_TX_FIFO_SIZE ? UART_SR_INTR_TFUL - : 0; - s->r[R_SR] |= !s->tx_count ? UART_SR_INTR_TEMPTY : 0; - s->r[R_SR] |= s->tx_count >= s->r[R_TTRIG] ? UART_SR_TTRIG : 0; - - s->r[R_CISR] |= s->r[R_SR] & UART_SR_TO_CISR_MASK; - s->r[R_CISR] |= s->r[R_SR] & UART_SR_TTRIG ? UART_INTR_TTRIG : 0; - qemu_set_irq(s->irq, !!(s->r[R_IMR] & s->r[R_CISR])); -} - -static void fifo_trigger_update(void *opaque) -{ - CadenceUARTState *s = opaque; - - s->r[R_CISR] |= UART_INTR_TIMEOUT; - - uart_update_status(s); -} - -static void uart_rx_reset(CadenceUARTState *s) -{ - s->rx_wpos = 0; - s->rx_count = 0; - if (s->chr) { - qemu_chr_accept_input(s->chr); - } -} - -static void uart_tx_reset(CadenceUARTState *s) -{ - s->tx_count = 0; -} - -static void uart_send_breaks(CadenceUARTState *s) -{ - int break_enabled = 1; - - if (s->chr) { - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK, - &break_enabled); - } -} - -static void uart_parameters_setup(CadenceUARTState *s) -{ - QEMUSerialSetParams ssp; - unsigned int baud_rate, packet_size; - - baud_rate = (s->r[R_MR] & UART_MR_CLKS) ? - UART_INPUT_CLK / 8 : UART_INPUT_CLK; - - ssp.speed = baud_rate / (s->r[R_BRGR] * (s->r[R_BDIV] + 1)); - packet_size = 1; - - switch (s->r[R_MR] & UART_MR_PAR) { - case UART_PARITY_EVEN: - ssp.parity = 'E'; - packet_size++; - break; - case UART_PARITY_ODD: - ssp.parity = 'O'; - packet_size++; - break; - default: - ssp.parity = 'N'; - break; - } - - switch (s->r[R_MR] & UART_MR_CHRL) { - case UART_DATA_BITS_6: - ssp.data_bits = 6; - break; - case UART_DATA_BITS_7: - ssp.data_bits = 7; - break; - default: - ssp.data_bits = 8; - break; - } - - switch (s->r[R_MR] & UART_MR_NBSTOP) { - case UART_STOP_BITS_1: - ssp.stop_bits = 1; - break; - default: - ssp.stop_bits = 2; - break; - } - - packet_size += ssp.data_bits + ssp.stop_bits; - 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); - } -} - -static int uart_can_receive(void *opaque) -{ - CadenceUARTState *s = opaque; - int ret = MAX(CADENCE_UART_RX_FIFO_SIZE, CADENCE_UART_TX_FIFO_SIZE); - uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE; - - if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) { - ret = MIN(ret, CADENCE_UART_RX_FIFO_SIZE - s->rx_count); - } - if (ch_mode == REMOTE_LOOPBACK || ch_mode == ECHO_MODE) { - ret = MIN(ret, CADENCE_UART_TX_FIFO_SIZE - s->tx_count); - } - return ret; -} - -static void uart_ctrl_update(CadenceUARTState *s) -{ - if (s->r[R_CR] & UART_CR_TXRST) { - uart_tx_reset(s); - } - - if (s->r[R_CR] & UART_CR_RXRST) { - uart_rx_reset(s); - } - - s->r[R_CR] &= ~(UART_CR_TXRST | UART_CR_RXRST); - - if (s->r[R_CR] & UART_CR_STARTBRK && !(s->r[R_CR] & UART_CR_STOPBRK)) { - uart_send_breaks(s); - } -} - -static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size) -{ - CadenceUARTState *s = opaque; - uint64_t new_rx_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - int i; - - if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) { - return; - } - - if (s->rx_count == CADENCE_UART_RX_FIFO_SIZE) { - s->r[R_CISR] |= UART_INTR_ROVR; - } else { - for (i = 0; i < size; i++) { - s->rx_fifo[s->rx_wpos] = buf[i]; - s->rx_wpos = (s->rx_wpos + 1) % CADENCE_UART_RX_FIFO_SIZE; - s->rx_count++; - } - timer_mod(s->fifo_trigger_handle, new_rx_time + - (s->char_tx_time * 4)); - } - uart_update_status(s); -} - -static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond, - void *opaque) -{ - CadenceUARTState *s = opaque; - int ret; - - /* instant drain the fifo when there's no back-end */ - if (!s->chr) { - s->tx_count = 0; - return FALSE; - } - - if (!s->tx_count) { - return FALSE; - } - - ret = qemu_chr_fe_write(s->chr, s->tx_fifo, s->tx_count); - s->tx_count -= ret; - memmove(s->tx_fifo, s->tx_fifo + ret, s->tx_count); - - if (s->tx_count) { - int r = qemu_chr_fe_add_watch(s->chr, G_IO_OUT|G_IO_HUP, - cadence_uart_xmit, s); - assert(r); - } - - uart_update_status(s); - return FALSE; -} - -static void uart_write_tx_fifo(CadenceUARTState *s, const uint8_t *buf, - int size) -{ - if ((s->r[R_CR] & UART_CR_TX_DIS) || !(s->r[R_CR] & UART_CR_TX_EN)) { - return; - } - - if (size > CADENCE_UART_TX_FIFO_SIZE - s->tx_count) { - size = CADENCE_UART_TX_FIFO_SIZE - s->tx_count; - /* - * This can only be a guest error via a bad tx fifo register push, - * as can_receive() should stop remote loop and echo modes ever getting - * us to here. - */ - qemu_log_mask(LOG_GUEST_ERROR, "cadence_uart: TxFIFO overflow"); - s->r[R_CISR] |= UART_INTR_ROVR; - } - - memcpy(s->tx_fifo + s->tx_count, buf, size); - s->tx_count += size; - - cadence_uart_xmit(NULL, G_IO_OUT, s); -} - -static void uart_receive(void *opaque, const uint8_t *buf, int size) -{ - CadenceUARTState *s = opaque; - uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE; - - if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) { - uart_write_rx_fifo(opaque, buf, size); - } - if (ch_mode == REMOTE_LOOPBACK || ch_mode == ECHO_MODE) { - uart_write_tx_fifo(s, buf, size); - } -} - -static void uart_event(void *opaque, int event) -{ - CadenceUARTState *s = opaque; - uint8_t buf = '\0'; - - if (event == CHR_EVENT_BREAK) { - uart_write_rx_fifo(opaque, &buf, 1); - } - - uart_update_status(s); -} - -static void uart_read_rx_fifo(CadenceUARTState *s, uint32_t *c) -{ - if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) { - return; - } - - if (s->rx_count) { - uint32_t rx_rpos = (CADENCE_UART_RX_FIFO_SIZE + s->rx_wpos - - s->rx_count) % CADENCE_UART_RX_FIFO_SIZE; - *c = s->rx_fifo[rx_rpos]; - s->rx_count--; - - if (s->chr) { - qemu_chr_accept_input(s->chr); - } - } else { - *c = 0; - } - - uart_update_status(s); -} - -static void uart_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - CadenceUARTState *s = opaque; - - 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; - break; - case R_IDR: /* idr (wtc imr) */ - s->r[R_IMR] &= ~value; - break; - case R_IMR: /* imr (read only) */ - break; - case R_CISR: /* cisr (wtc) */ - s->r[R_CISR] &= ~value; - break; - case R_TX_RX: /* UARTDR */ - switch (s->r[R_MR] & UART_MR_CHMODE) { - case NORMAL_MODE: - uart_write_tx_fifo(s, (uint8_t *) &value, 1); - break; - case LOCAL_LOOPBACK: - uart_write_rx_fifo(opaque, (uint8_t *) &value, 1); - break; - } - break; - default: - s->r[offset] = value; - } - - switch (offset) { - case R_CR: - uart_ctrl_update(s); - break; - case R_MR: - uart_parameters_setup(s); - break; - } - uart_update_status(s); -} - -static uint64_t uart_read(void *opaque, hwaddr offset, - unsigned size) -{ - CadenceUARTState *s = opaque; - uint32_t c = 0; - - offset >>= 2; - if (offset >= CADENCE_UART_R_MAX) { - c = 0; - } else if (offset == R_TX_RX) { - uart_read_rx_fifo(s, &c); - } else { - c = s->r[offset]; - } - - DB_PRINT(" offset:%x data:%08x\n", (unsigned)(offset << 2), (unsigned)c); - return c; -} - -static const MemoryRegionOps uart_ops = { - .read = uart_read, - .write = uart_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void cadence_uart_reset(DeviceState *dev) -{ - CadenceUARTState *s = CADENCE_UART(dev); - - s->r[R_CR] = 0x00000128; - s->r[R_IMR] = 0; - s->r[R_CISR] = 0; - s->r[R_RTRIG] = 0x00000020; - s->r[R_BRGR] = 0x0000000F; - s->r[R_TTRIG] = 0x00000020; - - uart_rx_reset(s); - uart_tx_reset(s); - - uart_update_status(s); -} - -static void cadence_uart_realize(DeviceState *dev, Error **errp) -{ - CadenceUARTState *s = CADENCE_UART(dev); - - s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL, - fifo_trigger_update, s); - - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); - - if (s->chr) { - qemu_chr_add_handlers(s->chr, uart_can_receive, uart_receive, - uart_event, s); - } -} - -static void cadence_uart_init(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - CadenceUARTState *s = CADENCE_UART(obj); - - memory_region_init_io(&s->iomem, obj, &uart_ops, s, "uart", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - - s->char_tx_time = (NANOSECONDS_PER_SECOND / 9600) * 10; -} - -static int cadence_uart_post_load(void *opaque, int version_id) -{ - CadenceUARTState *s = opaque; - - uart_parameters_setup(s); - uart_update_status(s); - return 0; -} - -static const VMStateDescription vmstate_cadence_uart = { - .name = "cadence_uart", - .version_id = 2, - .minimum_version_id = 2, - .post_load = cadence_uart_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(r, CadenceUARTState, CADENCE_UART_R_MAX), - VMSTATE_UINT8_ARRAY(rx_fifo, CadenceUARTState, - CADENCE_UART_RX_FIFO_SIZE), - VMSTATE_UINT8_ARRAY(tx_fifo, CadenceUARTState, - CADENCE_UART_TX_FIFO_SIZE), - VMSTATE_UINT32(rx_count, CadenceUARTState), - VMSTATE_UINT32(tx_count, CadenceUARTState), - VMSTATE_UINT32(rx_wpos, CadenceUARTState), - VMSTATE_TIMER_PTR(fifo_trigger_handle, CadenceUARTState), - VMSTATE_END_OF_LIST() - } -}; - -static void cadence_uart_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = cadence_uart_realize; - dc->vmsd = &vmstate_cadence_uart; - dc->reset = cadence_uart_reset; - /* Reason: realize() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo cadence_uart_info = { - .name = TYPE_CADENCE_UART, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(CadenceUARTState), - .instance_init = cadence_uart_init, - .class_init = cadence_uart_class_init, -}; - -static void cadence_uart_register_types(void) -{ - type_register_static(&cadence_uart_info); -} - -type_init(cadence_uart_register_types) diff --git a/qemu/hw/char/debugcon.c b/qemu/hw/char/debugcon.c deleted file mode 100644 index e7f025ec6..000000000 --- a/qemu/hw/char/debugcon.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * QEMU Bochs-style debug console ("port E9") emulation - * - * Copyright (c) 2003-2004 Fabrice Bellard - * Copyright (c) 2008 Citrix Systems, Inc. - * Copyright (c) Intel Corporation; author: H. Peter Anvin - * - * 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 "sysemu/char.h" -#include "hw/isa/isa.h" -#include "hw/i386/pc.h" - -#define TYPE_ISA_DEBUGCON_DEVICE "isa-debugcon" -#define ISA_DEBUGCON_DEVICE(obj) \ - OBJECT_CHECK(ISADebugconState, (obj), TYPE_ISA_DEBUGCON_DEVICE) - -//#define DEBUG_DEBUGCON - -typedef struct DebugconState { - MemoryRegion io; - CharDriverState *chr; - uint32_t readback; -} DebugconState; - -typedef struct ISADebugconState { - ISADevice parent_obj; - - uint32_t iobase; - DebugconState state; -} ISADebugconState; - -static void debugcon_ioport_write(void *opaque, hwaddr addr, uint64_t val, - unsigned width) -{ - DebugconState *s = opaque; - unsigned char ch = val; - -#ifdef DEBUG_DEBUGCON - printf(" [debugcon: write addr=0x%04" HWADDR_PRIx " val=0x%02" PRIx64 "]\n", addr, val); -#endif - - qemu_chr_fe_write(s->chr, &ch, 1); -} - - -static uint64_t debugcon_ioport_read(void *opaque, hwaddr addr, unsigned width) -{ - DebugconState *s = opaque; - -#ifdef DEBUG_DEBUGCON - printf("debugcon: read addr=0x%04" HWADDR_PRIx "\n", addr); -#endif - - return s->readback; -} - -static const MemoryRegionOps debugcon_ops = { - .read = debugcon_ioport_read, - .write = debugcon_ioport_write, - .valid.min_access_size = 1, - .valid.max_access_size = 1, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void debugcon_realize_core(DebugconState *s, Error **errp) -{ - if (!s->chr) { - error_setg(errp, "Can't create debugcon device, empty char device"); - return; - } - - qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, s); -} - -static void debugcon_isa_realizefn(DeviceState *dev, Error **errp) -{ - ISADevice *d = ISA_DEVICE(dev); - ISADebugconState *isa = ISA_DEBUGCON_DEVICE(dev); - DebugconState *s = &isa->state; - Error *err = NULL; - - debugcon_realize_core(s, &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - memory_region_init_io(&s->io, OBJECT(dev), &debugcon_ops, s, - TYPE_ISA_DEBUGCON_DEVICE, 1); - memory_region_add_subregion(isa_address_space_io(d), - isa->iobase, &s->io); -} - -static Property debugcon_isa_properties[] = { - DEFINE_PROP_UINT32("iobase", ISADebugconState, iobase, 0xe9), - DEFINE_PROP_CHR("chardev", ISADebugconState, state.chr), - DEFINE_PROP_UINT32("readback", ISADebugconState, state.readback, 0xe9), - DEFINE_PROP_END_OF_LIST(), -}; - -static void debugcon_isa_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = debugcon_isa_realizefn; - dc->props = debugcon_isa_properties; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo debugcon_isa_info = { - .name = TYPE_ISA_DEBUGCON_DEVICE, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(ISADebugconState), - .class_init = debugcon_isa_class_initfn, -}; - -static void debugcon_register_types(void) -{ - type_register_static(&debugcon_isa_info); -} - -type_init(debugcon_register_types) diff --git a/qemu/hw/char/digic-uart.c b/qemu/hw/char/digic-uart.c deleted file mode 100644 index d3bc533d7..000000000 --- a/qemu/hw/char/digic-uart.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * QEMU model of the Canon DIGIC UART block. - * - * Copyright (C) 2013 Antony Pavlov - * - * This model is based on reverse engineering efforts - * made by CHDK (http://chdk.wikia.com) and - * Magic Lantern (http://www.magiclantern.fm) projects - * contributors. - * - * See "Serial terminal" docs here: - * http://magiclantern.wikia.com/wiki/Register_Map#Misc_Registers - * - * The QEMU model of the Milkymist UART block by Michael Walle - * is used as a template. - * - * 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. - * - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "sysemu/char.h" - -#include "hw/char/digic-uart.h" - -enum { - ST_RX_RDY = (1 << 0), - ST_TX_RDY = (1 << 1), -}; - -static uint64_t digic_uart_read(void *opaque, hwaddr addr, - unsigned size) -{ - DigicUartState *s = opaque; - uint64_t ret = 0; - - addr >>= 2; - - switch (addr) { - case R_RX: - s->reg_st &= ~(ST_RX_RDY); - ret = s->reg_rx; - break; - - case R_ST: - ret = s->reg_st; - break; - - default: - qemu_log_mask(LOG_UNIMP, - "digic-uart: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - } - - return ret; -} - -static void digic_uart_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - DigicUartState *s = opaque; - unsigned char ch = value; - - addr >>= 2; - - switch (addr) { - case R_TX: - if (s->chr) { - qemu_chr_fe_write_all(s->chr, &ch, 1); - } - break; - - case R_ST: - /* - * Ignore write to R_ST. - * - * The point is that this register is actively used - * during receiving and transmitting symbols, - * but we don't know the function of most of bits. - * - * Ignoring writes to R_ST is only a simplification - * of the model. It has no perceptible side effects - * for existing guests. - */ - break; - - default: - qemu_log_mask(LOG_UNIMP, - "digic-uart: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - } -} - -static const MemoryRegionOps uart_mmio_ops = { - .read = digic_uart_read, - .write = digic_uart_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int uart_can_rx(void *opaque) -{ - DigicUartState *s = opaque; - - return !(s->reg_st & ST_RX_RDY); -} - -static void uart_rx(void *opaque, const uint8_t *buf, int size) -{ - DigicUartState *s = opaque; - - assert(uart_can_rx(opaque)); - - s->reg_st |= ST_RX_RDY; - s->reg_rx = *buf; -} - -static void uart_event(void *opaque, int event) -{ -} - -static void digic_uart_reset(DeviceState *d) -{ - DigicUartState *s = DIGIC_UART(d); - - s->reg_rx = 0; - s->reg_st = ST_TX_RDY; -} - -static void digic_uart_realize(DeviceState *dev, Error **errp) -{ - DigicUartState *s = DIGIC_UART(dev); - - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); - if (s->chr) { - qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); - } -} - -static void digic_uart_init(Object *obj) -{ - DigicUartState *s = DIGIC_UART(obj); - - memory_region_init_io(&s->regs_region, OBJECT(s), &uart_mmio_ops, s, - TYPE_DIGIC_UART, 0x18); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->regs_region); -} - -static const VMStateDescription vmstate_digic_uart = { - .name = "digic-uart", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(reg_rx, DigicUartState), - VMSTATE_UINT32(reg_st, DigicUartState), - VMSTATE_END_OF_LIST() - } -}; - -static void digic_uart_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = digic_uart_realize; - dc->reset = digic_uart_reset; - dc->vmsd = &vmstate_digic_uart; - /* Reason: realize() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo digic_uart_info = { - .name = TYPE_DIGIC_UART, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(DigicUartState), - .instance_init = digic_uart_init, - .class_init = digic_uart_class_init, -}; - -static void digic_uart_register_types(void) -{ - type_register_static(&digic_uart_info); -} - -type_init(digic_uart_register_types) diff --git a/qemu/hw/char/escc.c b/qemu/hw/char/escc.c deleted file mode 100644 index 7bf09a007..000000000 --- a/qemu/hw/char/escc.c +++ /dev/null @@ -1,1056 +0,0 @@ -/* - * QEMU ESCC (Z8030/Z8530/Z85C30/SCC/ESCC) serial port emulation - * - * Copyright (c) 2003-2005 Fabrice Bellard - * - * 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/sysbus.h" -#include "hw/char/escc.h" -#include "sysemu/char.h" -#include "ui/console.h" -#include "ui/input.h" -#include "trace.h" - -/* - * Chipset docs: - * "Z80C30/Z85C30/Z80230/Z85230/Z85233 SCC/ESCC User Manual", - * http://www.zilog.com/docs/serial/scc_escc_um.pdf - * - * On Sparc32 this is the serial port, mouse and keyboard part of chip STP2001 - * (Slave I/O), also produced as NCR89C105. See - * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt - * - * The serial ports implement full AMD AM8530 or Zilog Z8530 chips, - * mouse and keyboard ports don't implement all functions and they are - * only asynchronous. There is no DMA. - * - * Z85C30 is also used on PowerMacs. There are some small differences - * between Sparc version (sunzilog) and PowerMac (pmac): - * Offset between control and data registers - * There is some kind of lockup bug, but we can ignore it - * CTS is inverted - * DMA on pmac using DBDMA chip - * pmac can do IRDA and faster rates, sunzilog can only do 38400 - * pmac baud rate generator clock is 3.6864 MHz, sunzilog 4.9152 MHz - */ - -/* - * Modifications: - * 2006-Aug-10 Igor Kovalenko : Renamed KBDQueue to SERIOQueue, implemented - * serial mouse queue. - * Implemented serial mouse protocol. - * - * 2010-May-23 Artyom Tarasenko: Reworked IUS logic - */ - -typedef enum { - chn_a, chn_b, -} ChnID; - -#define CHN_C(s) ((s)->chn == chn_b? 'b' : 'a') - -typedef enum { - ser, kbd, mouse, -} ChnType; - -#define SERIO_QUEUE_SIZE 256 - -typedef struct { - uint8_t data[SERIO_QUEUE_SIZE]; - int rptr, wptr, count; -} SERIOQueue; - -#define SERIAL_REGS 16 -typedef struct ChannelState { - qemu_irq irq; - uint32_t rxint, txint, rxint_under_svc, txint_under_svc; - struct ChannelState *otherchn; - uint32_t reg; - uint8_t wregs[SERIAL_REGS], rregs[SERIAL_REGS]; - SERIOQueue queue; - CharDriverState *chr; - int e0_mode, led_mode, caps_lock_mode, num_lock_mode; - int disabled; - int clock; - uint32_t vmstate_dummy; - ChnID chn; // this channel, A (base+4) or B (base+0) - ChnType type; - uint8_t rx, tx; - QemuInputHandlerState *hs; -} ChannelState; - -#define ESCC(obj) OBJECT_CHECK(ESCCState, (obj), TYPE_ESCC) - -typedef struct ESCCState { - SysBusDevice parent_obj; - - struct ChannelState chn[2]; - uint32_t it_shift; - MemoryRegion mmio; - uint32_t disabled; - uint32_t frequency; -} ESCCState; - -#define SERIAL_CTRL 0 -#define SERIAL_DATA 1 - -#define W_CMD 0 -#define CMD_PTR_MASK 0x07 -#define CMD_CMD_MASK 0x38 -#define CMD_HI 0x08 -#define CMD_CLR_TXINT 0x28 -#define CMD_CLR_IUS 0x38 -#define W_INTR 1 -#define INTR_INTALL 0x01 -#define INTR_TXINT 0x02 -#define INTR_RXMODEMSK 0x18 -#define INTR_RXINT1ST 0x08 -#define INTR_RXINTALL 0x10 -#define W_IVEC 2 -#define W_RXCTRL 3 -#define RXCTRL_RXEN 0x01 -#define W_TXCTRL1 4 -#define TXCTRL1_PAREN 0x01 -#define TXCTRL1_PAREV 0x02 -#define TXCTRL1_1STOP 0x04 -#define TXCTRL1_1HSTOP 0x08 -#define TXCTRL1_2STOP 0x0c -#define TXCTRL1_STPMSK 0x0c -#define TXCTRL1_CLK1X 0x00 -#define TXCTRL1_CLK16X 0x40 -#define TXCTRL1_CLK32X 0x80 -#define TXCTRL1_CLK64X 0xc0 -#define TXCTRL1_CLKMSK 0xc0 -#define W_TXCTRL2 5 -#define TXCTRL2_TXEN 0x08 -#define TXCTRL2_BITMSK 0x60 -#define TXCTRL2_5BITS 0x00 -#define TXCTRL2_7BITS 0x20 -#define TXCTRL2_6BITS 0x40 -#define TXCTRL2_8BITS 0x60 -#define W_SYNC1 6 -#define W_SYNC2 7 -#define W_TXBUF 8 -#define W_MINTR 9 -#define MINTR_STATUSHI 0x10 -#define MINTR_RST_MASK 0xc0 -#define MINTR_RST_B 0x40 -#define MINTR_RST_A 0x80 -#define MINTR_RST_ALL 0xc0 -#define W_MISC1 10 -#define W_CLOCK 11 -#define CLOCK_TRXC 0x08 -#define W_BRGLO 12 -#define W_BRGHI 13 -#define W_MISC2 14 -#define MISC2_PLLDIS 0x30 -#define W_EXTINT 15 -#define EXTINT_DCD 0x08 -#define EXTINT_SYNCINT 0x10 -#define EXTINT_CTSINT 0x20 -#define EXTINT_TXUNDRN 0x40 -#define EXTINT_BRKINT 0x80 - -#define R_STATUS 0 -#define STATUS_RXAV 0x01 -#define STATUS_ZERO 0x02 -#define STATUS_TXEMPTY 0x04 -#define STATUS_DCD 0x08 -#define STATUS_SYNC 0x10 -#define STATUS_CTS 0x20 -#define STATUS_TXUNDRN 0x40 -#define STATUS_BRK 0x80 -#define R_SPEC 1 -#define SPEC_ALLSENT 0x01 -#define SPEC_BITS8 0x06 -#define R_IVEC 2 -#define IVEC_TXINTB 0x00 -#define IVEC_LONOINT 0x06 -#define IVEC_LORXINTA 0x0c -#define IVEC_LORXINTB 0x04 -#define IVEC_LOTXINTA 0x08 -#define IVEC_HINOINT 0x60 -#define IVEC_HIRXINTA 0x30 -#define IVEC_HIRXINTB 0x20 -#define IVEC_HITXINTA 0x10 -#define R_INTR 3 -#define INTR_EXTINTB 0x01 -#define INTR_TXINTB 0x02 -#define INTR_RXINTB 0x04 -#define INTR_EXTINTA 0x08 -#define INTR_TXINTA 0x10 -#define INTR_RXINTA 0x20 -#define R_IPEN 4 -#define R_TXCTRL1 5 -#define R_TXCTRL2 6 -#define R_BC 7 -#define R_RXBUF 8 -#define R_RXCTRL 9 -#define R_MISC 10 -#define R_MISC1 11 -#define R_BRGLO 12 -#define R_BRGHI 13 -#define R_MISC1I 14 -#define R_EXTINT 15 - -static void handle_kbd_command(ChannelState *s, int val); -static int serial_can_receive(void *opaque); -static void serial_receive_byte(ChannelState *s, int ch); - -static void clear_queue(void *opaque) -{ - ChannelState *s = opaque; - SERIOQueue *q = &s->queue; - q->rptr = q->wptr = q->count = 0; -} - -static void put_queue(void *opaque, int b) -{ - ChannelState *s = opaque; - SERIOQueue *q = &s->queue; - - trace_escc_put_queue(CHN_C(s), b); - if (q->count >= SERIO_QUEUE_SIZE) - return; - q->data[q->wptr] = b; - if (++q->wptr == SERIO_QUEUE_SIZE) - q->wptr = 0; - q->count++; - serial_receive_byte(s, 0); -} - -static uint32_t get_queue(void *opaque) -{ - ChannelState *s = opaque; - SERIOQueue *q = &s->queue; - int val; - - if (q->count == 0) { - return 0; - } else { - val = q->data[q->rptr]; - if (++q->rptr == SERIO_QUEUE_SIZE) - q->rptr = 0; - q->count--; - } - trace_escc_get_queue(CHN_C(s), val); - if (q->count > 0) - serial_receive_byte(s, 0); - return val; -} - -static int escc_update_irq_chn(ChannelState *s) -{ - if ((((s->wregs[W_INTR] & INTR_TXINT) && (s->txint == 1)) || - // tx ints enabled, pending - ((((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINT1ST) || - ((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINTALL)) && - s->rxint == 1) || // rx ints enabled, pending - ((s->wregs[W_EXTINT] & EXTINT_BRKINT) && - (s->rregs[R_STATUS] & STATUS_BRK)))) { // break int e&p - return 1; - } - return 0; -} - -static void escc_update_irq(ChannelState *s) -{ - int irq; - - irq = escc_update_irq_chn(s); - irq |= escc_update_irq_chn(s->otherchn); - - trace_escc_update_irq(irq); - qemu_set_irq(s->irq, irq); -} - -static void escc_reset_chn(ChannelState *s) -{ - int i; - - s->reg = 0; - for (i = 0; i < SERIAL_REGS; i++) { - s->rregs[i] = 0; - s->wregs[i] = 0; - } - s->wregs[W_TXCTRL1] = TXCTRL1_1STOP; // 1X divisor, 1 stop bit, no parity - s->wregs[W_MINTR] = MINTR_RST_ALL; - s->wregs[W_CLOCK] = CLOCK_TRXC; // Synch mode tx clock = TRxC - s->wregs[W_MISC2] = MISC2_PLLDIS; // PLL disabled - s->wregs[W_EXTINT] = EXTINT_DCD | EXTINT_SYNCINT | EXTINT_CTSINT | - EXTINT_TXUNDRN | EXTINT_BRKINT; // Enable most interrupts - if (s->disabled) - s->rregs[R_STATUS] = STATUS_TXEMPTY | STATUS_DCD | STATUS_SYNC | - STATUS_CTS | STATUS_TXUNDRN; - else - s->rregs[R_STATUS] = STATUS_TXEMPTY | STATUS_TXUNDRN; - s->rregs[R_SPEC] = SPEC_BITS8 | SPEC_ALLSENT; - - s->rx = s->tx = 0; - s->rxint = s->txint = 0; - s->rxint_under_svc = s->txint_under_svc = 0; - s->e0_mode = s->led_mode = s->caps_lock_mode = s->num_lock_mode = 0; - clear_queue(s); -} - -static void escc_reset(DeviceState *d) -{ - ESCCState *s = ESCC(d); - - escc_reset_chn(&s->chn[0]); - escc_reset_chn(&s->chn[1]); -} - -static inline void set_rxint(ChannelState *s) -{ - s->rxint = 1; - /* XXX: missing daisy chainnig: chn_b rx should have a lower priority - than chn_a rx/tx/special_condition service*/ - s->rxint_under_svc = 1; - if (s->chn == chn_a) { - s->rregs[R_INTR] |= INTR_RXINTA; - if (s->wregs[W_MINTR] & MINTR_STATUSHI) - s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA; - else - s->otherchn->rregs[R_IVEC] = IVEC_LORXINTA; - } else { - s->otherchn->rregs[R_INTR] |= INTR_RXINTB; - if (s->wregs[W_MINTR] & MINTR_STATUSHI) - s->rregs[R_IVEC] = IVEC_HIRXINTB; - else - s->rregs[R_IVEC] = IVEC_LORXINTB; - } - escc_update_irq(s); -} - -static inline void set_txint(ChannelState *s) -{ - s->txint = 1; - if (!s->rxint_under_svc) { - s->txint_under_svc = 1; - if (s->chn == chn_a) { - if (s->wregs[W_INTR] & INTR_TXINT) { - s->rregs[R_INTR] |= INTR_TXINTA; - } - if (s->wregs[W_MINTR] & MINTR_STATUSHI) - s->otherchn->rregs[R_IVEC] = IVEC_HITXINTA; - else - s->otherchn->rregs[R_IVEC] = IVEC_LOTXINTA; - } else { - s->rregs[R_IVEC] = IVEC_TXINTB; - if (s->wregs[W_INTR] & INTR_TXINT) { - s->otherchn->rregs[R_INTR] |= INTR_TXINTB; - } - } - escc_update_irq(s); - } -} - -static inline void clr_rxint(ChannelState *s) -{ - s->rxint = 0; - s->rxint_under_svc = 0; - if (s->chn == chn_a) { - if (s->wregs[W_MINTR] & MINTR_STATUSHI) - s->otherchn->rregs[R_IVEC] = IVEC_HINOINT; - else - s->otherchn->rregs[R_IVEC] = IVEC_LONOINT; - s->rregs[R_INTR] &= ~INTR_RXINTA; - } else { - if (s->wregs[W_MINTR] & MINTR_STATUSHI) - s->rregs[R_IVEC] = IVEC_HINOINT; - else - s->rregs[R_IVEC] = IVEC_LONOINT; - s->otherchn->rregs[R_INTR] &= ~INTR_RXINTB; - } - if (s->txint) - set_txint(s); - escc_update_irq(s); -} - -static inline void clr_txint(ChannelState *s) -{ - s->txint = 0; - s->txint_under_svc = 0; - if (s->chn == chn_a) { - if (s->wregs[W_MINTR] & MINTR_STATUSHI) - s->otherchn->rregs[R_IVEC] = IVEC_HINOINT; - else - s->otherchn->rregs[R_IVEC] = IVEC_LONOINT; - s->rregs[R_INTR] &= ~INTR_TXINTA; - } else { - s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB; - if (s->wregs[W_MINTR] & MINTR_STATUSHI) - s->rregs[R_IVEC] = IVEC_HINOINT; - else - s->rregs[R_IVEC] = IVEC_LONOINT; - s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB; - } - if (s->rxint) - set_rxint(s); - escc_update_irq(s); -} - -static void escc_update_parameters(ChannelState *s) -{ - int speed, parity, data_bits, stop_bits; - QEMUSerialSetParams ssp; - - if (!s->chr || s->type != ser) - return; - - if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREN) { - if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREV) - parity = 'E'; - else - parity = 'O'; - } else { - parity = 'N'; - } - if ((s->wregs[W_TXCTRL1] & TXCTRL1_STPMSK) == TXCTRL1_2STOP) - stop_bits = 2; - else - stop_bits = 1; - switch (s->wregs[W_TXCTRL2] & TXCTRL2_BITMSK) { - case TXCTRL2_5BITS: - data_bits = 5; - break; - case TXCTRL2_7BITS: - data_bits = 7; - break; - case TXCTRL2_6BITS: - data_bits = 6; - break; - default: - case TXCTRL2_8BITS: - data_bits = 8; - break; - } - speed = s->clock / ((s->wregs[W_BRGLO] | (s->wregs[W_BRGHI] << 8)) + 2); - switch (s->wregs[W_TXCTRL1] & TXCTRL1_CLKMSK) { - case TXCTRL1_CLK1X: - break; - case TXCTRL1_CLK16X: - speed /= 16; - break; - case TXCTRL1_CLK32X: - speed /= 32; - break; - default: - case TXCTRL1_CLK64X: - speed /= 64; - break; - } - ssp.speed = speed; - ssp.parity = parity; - ssp.data_bits = data_bits; - ssp.stop_bits = stop_bits; - trace_escc_update_parameters(CHN_C(s), speed, parity, data_bits, stop_bits); - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); -} - -static void escc_mem_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - ESCCState *serial = opaque; - ChannelState *s; - uint32_t saddr; - int newreg, channel; - - val &= 0xff; - saddr = (addr >> serial->it_shift) & 1; - channel = (addr >> (serial->it_shift + 1)) & 1; - s = &serial->chn[channel]; - switch (saddr) { - case SERIAL_CTRL: - trace_escc_mem_writeb_ctrl(CHN_C(s), s->reg, val & 0xff); - newreg = 0; - switch (s->reg) { - case W_CMD: - newreg = val & CMD_PTR_MASK; - val &= CMD_CMD_MASK; - switch (val) { - case CMD_HI: - newreg |= CMD_HI; - break; - case CMD_CLR_TXINT: - clr_txint(s); - break; - case CMD_CLR_IUS: - if (s->rxint_under_svc) { - s->rxint_under_svc = 0; - if (s->txint) { - set_txint(s); - } - } else if (s->txint_under_svc) { - s->txint_under_svc = 0; - } - escc_update_irq(s); - break; - default: - break; - } - break; - case W_INTR ... W_RXCTRL: - case W_SYNC1 ... W_TXBUF: - case W_MISC1 ... W_CLOCK: - case W_MISC2 ... W_EXTINT: - s->wregs[s->reg] = val; - break; - case W_TXCTRL1: - case W_TXCTRL2: - s->wregs[s->reg] = val; - escc_update_parameters(s); - break; - case W_BRGLO: - case W_BRGHI: - s->wregs[s->reg] = val; - s->rregs[s->reg] = val; - escc_update_parameters(s); - break; - case W_MINTR: - switch (val & MINTR_RST_MASK) { - case 0: - default: - break; - case MINTR_RST_B: - escc_reset_chn(&serial->chn[0]); - return; - case MINTR_RST_A: - escc_reset_chn(&serial->chn[1]); - return; - case MINTR_RST_ALL: - escc_reset(DEVICE(serial)); - return; - } - break; - default: - break; - } - if (s->reg == 0) - s->reg = newreg; - else - s->reg = 0; - break; - case SERIAL_DATA: - trace_escc_mem_writeb_data(CHN_C(s), val); - s->tx = val; - if (s->wregs[W_TXCTRL2] & TXCTRL2_TXEN) { // tx enabled - if (s->chr) - qemu_chr_fe_write(s->chr, &s->tx, 1); - else if (s->type == kbd && !s->disabled) { - handle_kbd_command(s, val); - } - } - s->rregs[R_STATUS] |= STATUS_TXEMPTY; // Tx buffer empty - s->rregs[R_SPEC] |= SPEC_ALLSENT; // All sent - set_txint(s); - break; - default: - break; - } -} - -static uint64_t escc_mem_read(void *opaque, hwaddr addr, - unsigned size) -{ - ESCCState *serial = opaque; - ChannelState *s; - uint32_t saddr; - uint32_t ret; - int channel; - - saddr = (addr >> serial->it_shift) & 1; - channel = (addr >> (serial->it_shift + 1)) & 1; - s = &serial->chn[channel]; - switch (saddr) { - case SERIAL_CTRL: - trace_escc_mem_readb_ctrl(CHN_C(s), s->reg, s->rregs[s->reg]); - ret = s->rregs[s->reg]; - s->reg = 0; - return ret; - case SERIAL_DATA: - s->rregs[R_STATUS] &= ~STATUS_RXAV; - clr_rxint(s); - if (s->type == kbd || s->type == mouse) - ret = get_queue(s); - else - ret = s->rx; - trace_escc_mem_readb_data(CHN_C(s), ret); - if (s->chr) - qemu_chr_accept_input(s->chr); - return ret; - default: - break; - } - return 0; -} - -static const MemoryRegionOps escc_mem_ops = { - .read = escc_mem_read, - .write = escc_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static int serial_can_receive(void *opaque) -{ - ChannelState *s = opaque; - int ret; - - if (((s->wregs[W_RXCTRL] & RXCTRL_RXEN) == 0) // Rx not enabled - || ((s->rregs[R_STATUS] & STATUS_RXAV) == STATUS_RXAV)) - // char already available - ret = 0; - else - ret = 1; - return ret; -} - -static void serial_receive_byte(ChannelState *s, int ch) -{ - trace_escc_serial_receive_byte(CHN_C(s), ch); - s->rregs[R_STATUS] |= STATUS_RXAV; - s->rx = ch; - set_rxint(s); -} - -static void serial_receive_break(ChannelState *s) -{ - s->rregs[R_STATUS] |= STATUS_BRK; - escc_update_irq(s); -} - -static void serial_receive1(void *opaque, const uint8_t *buf, int size) -{ - ChannelState *s = opaque; - serial_receive_byte(s, buf[0]); -} - -static void serial_event(void *opaque, int event) -{ - ChannelState *s = opaque; - if (event == CHR_EVENT_BREAK) - serial_receive_break(s); -} - -static const VMStateDescription vmstate_escc_chn = { - .name ="escc_chn", - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(vmstate_dummy, ChannelState), - VMSTATE_UINT32(reg, ChannelState), - VMSTATE_UINT32(rxint, ChannelState), - VMSTATE_UINT32(txint, ChannelState), - VMSTATE_UINT32(rxint_under_svc, ChannelState), - VMSTATE_UINT32(txint_under_svc, ChannelState), - VMSTATE_UINT8(rx, ChannelState), - VMSTATE_UINT8(tx, ChannelState), - VMSTATE_BUFFER(wregs, ChannelState), - VMSTATE_BUFFER(rregs, ChannelState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_escc = { - .name ="escc", - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(chn, ESCCState, 2, 2, vmstate_escc_chn, - ChannelState), - VMSTATE_END_OF_LIST() - } -}; - -MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB, - CharDriverState *chrA, CharDriverState *chrB, - int clock, int it_shift) -{ - DeviceState *dev; - SysBusDevice *s; - ESCCState *d; - - dev = qdev_create(NULL, TYPE_ESCC); - qdev_prop_set_uint32(dev, "disabled", 0); - qdev_prop_set_uint32(dev, "frequency", clock); - qdev_prop_set_uint32(dev, "it_shift", it_shift); - qdev_prop_set_chr(dev, "chrB", chrB); - qdev_prop_set_chr(dev, "chrA", chrA); - qdev_prop_set_uint32(dev, "chnBtype", ser); - qdev_prop_set_uint32(dev, "chnAtype", ser); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, irqB); - sysbus_connect_irq(s, 1, irqA); - if (base) { - sysbus_mmio_map(s, 0, base); - } - - d = ESCC(s); - return &d->mmio; -} - -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, - [Q_KEY_CODE_ALT_R] = 13, - [Q_KEY_CODE_ALTGR] = 13, - [Q_KEY_CODE_CTRL] = 76, - [Q_KEY_CODE_CTRL_R] = 76, - [Q_KEY_CODE_ESC] = 29, - [Q_KEY_CODE_1] = 30, - [Q_KEY_CODE_2] = 31, - [Q_KEY_CODE_3] = 32, - [Q_KEY_CODE_4] = 33, - [Q_KEY_CODE_5] = 34, - [Q_KEY_CODE_6] = 35, - [Q_KEY_CODE_7] = 36, - [Q_KEY_CODE_8] = 37, - [Q_KEY_CODE_9] = 38, - [Q_KEY_CODE_0] = 39, - [Q_KEY_CODE_MINUS] = 40, - [Q_KEY_CODE_EQUAL] = 41, - [Q_KEY_CODE_BACKSPACE] = 43, - [Q_KEY_CODE_TAB] = 53, - [Q_KEY_CODE_Q] = 54, - [Q_KEY_CODE_W] = 55, - [Q_KEY_CODE_E] = 56, - [Q_KEY_CODE_R] = 57, - [Q_KEY_CODE_T] = 58, - [Q_KEY_CODE_Y] = 59, - [Q_KEY_CODE_U] = 60, - [Q_KEY_CODE_I] = 61, - [Q_KEY_CODE_O] = 62, - [Q_KEY_CODE_P] = 63, - [Q_KEY_CODE_BRACKET_LEFT] = 64, - [Q_KEY_CODE_BRACKET_RIGHT] = 65, - [Q_KEY_CODE_RET] = 89, - [Q_KEY_CODE_A] = 77, - [Q_KEY_CODE_S] = 78, - [Q_KEY_CODE_D] = 79, - [Q_KEY_CODE_F] = 80, - [Q_KEY_CODE_G] = 81, - [Q_KEY_CODE_H] = 82, - [Q_KEY_CODE_J] = 83, - [Q_KEY_CODE_K] = 84, - [Q_KEY_CODE_L] = 85, - [Q_KEY_CODE_SEMICOLON] = 86, - [Q_KEY_CODE_APOSTROPHE] = 87, - [Q_KEY_CODE_GRAVE_ACCENT] = 42, - [Q_KEY_CODE_BACKSLASH] = 88, - [Q_KEY_CODE_Z] = 100, - [Q_KEY_CODE_X] = 101, - [Q_KEY_CODE_C] = 102, - [Q_KEY_CODE_V] = 103, - [Q_KEY_CODE_B] = 104, - [Q_KEY_CODE_N] = 105, - [Q_KEY_CODE_M] = 106, - [Q_KEY_CODE_COMMA] = 107, - [Q_KEY_CODE_DOT] = 108, - [Q_KEY_CODE_SLASH] = 109, - [Q_KEY_CODE_ASTERISK] = 47, - [Q_KEY_CODE_SPC] = 121, - [Q_KEY_CODE_CAPS_LOCK] = 119, - [Q_KEY_CODE_F1] = 5, - [Q_KEY_CODE_F2] = 6, - [Q_KEY_CODE_F3] = 8, - [Q_KEY_CODE_F4] = 10, - [Q_KEY_CODE_F5] = 12, - [Q_KEY_CODE_F6] = 14, - [Q_KEY_CODE_F7] = 16, - [Q_KEY_CODE_F8] = 17, - [Q_KEY_CODE_F9] = 18, - [Q_KEY_CODE_F10] = 7, - [Q_KEY_CODE_NUM_LOCK] = 98, - [Q_KEY_CODE_SCROLL_LOCK] = 23, - [Q_KEY_CODE_KP_DIVIDE] = 46, - [Q_KEY_CODE_KP_MULTIPLY] = 47, - [Q_KEY_CODE_KP_SUBTRACT] = 71, - [Q_KEY_CODE_KP_ADD] = 125, - [Q_KEY_CODE_KP_ENTER] = 90, - [Q_KEY_CODE_KP_DECIMAL] = 50, - [Q_KEY_CODE_KP_0] = 94, - [Q_KEY_CODE_KP_1] = 112, - [Q_KEY_CODE_KP_2] = 113, - [Q_KEY_CODE_KP_3] = 114, - [Q_KEY_CODE_KP_4] = 91, - [Q_KEY_CODE_KP_5] = 92, - [Q_KEY_CODE_KP_6] = 93, - [Q_KEY_CODE_KP_7] = 68, - [Q_KEY_CODE_KP_8] = 69, - [Q_KEY_CODE_KP_9] = 70, - [Q_KEY_CODE_LESS] = 124, - [Q_KEY_CODE_F11] = 9, - [Q_KEY_CODE_F12] = 11, - [Q_KEY_CODE_HOME] = 52, - [Q_KEY_CODE_PGUP] = 96, - [Q_KEY_CODE_PGDN] = 123, - [Q_KEY_CODE_END] = 74, - [Q_KEY_CODE_LEFT] = 24, - [Q_KEY_CODE_UP] = 20, - [Q_KEY_CODE_DOWN] = 27, - [Q_KEY_CODE_RIGHT] = 28, - [Q_KEY_CODE_INSERT] = 44, - [Q_KEY_CODE_DELETE] = 66, - [Q_KEY_CODE_STOP] = 1, - [Q_KEY_CODE_AGAIN] = 3, - [Q_KEY_CODE_PROPS] = 25, - [Q_KEY_CODE_UNDO] = 26, - [Q_KEY_CODE_FRONT] = 49, - [Q_KEY_CODE_COPY] = 51, - [Q_KEY_CODE_OPEN] = 72, - [Q_KEY_CODE_PASTE] = 73, - [Q_KEY_CODE_FIND] = 95, - [Q_KEY_CODE_CUT] = 97, - [Q_KEY_CODE_LF] = 111, - [Q_KEY_CODE_HELP] = 118, - [Q_KEY_CODE_META_L] = 120, - [Q_KEY_CODE_META_R] = 122, - [Q_KEY_CODE_COMPOSE] = 67, - [Q_KEY_CODE_PRINT] = 22, - [Q_KEY_CODE_SYSRQ] = 21, -}; - -static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) -{ - ChannelState *s = (ChannelState *)dev; - int qcode, keycode; - InputKeyEvent *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], - key->down); - - if (qcode == Q_KEY_CODE_CAPS_LOCK) { - if (key->down) { - s->caps_lock_mode ^= 1; - if (s->caps_lock_mode == 2) { - return; /* Drop second press */ - } - } else { - s->caps_lock_mode ^= 2; - if (s->caps_lock_mode == 3) { - return; /* Drop first release */ - } - } - } - - if (qcode == Q_KEY_CODE_NUM_LOCK) { - if (key->down) { - s->num_lock_mode ^= 1; - if (s->num_lock_mode == 2) { - return; /* Drop second press */ - } - } else { - s->num_lock_mode ^= 2; - if (s->num_lock_mode == 3) { - return; /* Drop first release */ - } - } - } - - keycode = qcode_to_keycode[qcode]; - if (!key->down) { - keycode |= 0x80; - } - trace_escc_sunkbd_event_out(keycode); - put_queue(s, keycode); -} - -static QemuInputHandler sunkbd_handler = { - .name = "sun keyboard", - .mask = INPUT_EVENT_MASK_KEY, - .event = sunkbd_handle_event, -}; - -static void handle_kbd_command(ChannelState *s, int val) -{ - trace_escc_kbd_command(val); - if (s->led_mode) { // Ignore led byte - s->led_mode = 0; - return; - } - switch (val) { - case 1: // Reset, return type code - clear_queue(s); - put_queue(s, 0xff); - put_queue(s, 4); // Type 4 - put_queue(s, 0x7f); - break; - case 0xe: // Set leds - s->led_mode = 1; - break; - case 7: // Query layout - case 0xf: - clear_queue(s); - put_queue(s, 0xfe); - put_queue(s, 0x21); /* en-us layout */ - break; - default: - break; - } -} - -static void sunmouse_event(void *opaque, - int dx, int dy, int dz, int buttons_state) -{ - ChannelState *s = opaque; - int ch; - - trace_escc_sunmouse_event(dx, dy, buttons_state); - ch = 0x80 | 0x7; /* protocol start byte, no buttons pressed */ - - if (buttons_state & MOUSE_EVENT_LBUTTON) - ch ^= 0x4; - if (buttons_state & MOUSE_EVENT_MBUTTON) - ch ^= 0x2; - if (buttons_state & MOUSE_EVENT_RBUTTON) - ch ^= 0x1; - - put_queue(s, ch); - - ch = dx; - - if (ch > 127) - ch = 127; - else if (ch < -127) - ch = -127; - - put_queue(s, ch & 0xff); - - ch = -dy; - - if (ch > 127) - ch = 127; - else if (ch < -127) - ch = -127; - - put_queue(s, ch & 0xff); - - // MSC protocol specify two extra motion bytes - - put_queue(s, 0); - put_queue(s, 0); -} - -void slavio_serial_ms_kbd_init(hwaddr base, qemu_irq irq, - int disabled, int clock, int it_shift) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_create(NULL, TYPE_ESCC); - qdev_prop_set_uint32(dev, "disabled", disabled); - qdev_prop_set_uint32(dev, "frequency", clock); - qdev_prop_set_uint32(dev, "it_shift", it_shift); - qdev_prop_set_chr(dev, "chrB", NULL); - qdev_prop_set_chr(dev, "chrA", NULL); - qdev_prop_set_uint32(dev, "chnBtype", mouse); - qdev_prop_set_uint32(dev, "chnAtype", kbd); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, irq); - sysbus_connect_irq(s, 1, irq); - sysbus_mmio_map(s, 0, base); -} - -static int escc_init1(SysBusDevice *dev) -{ - ESCCState *s = ESCC(dev); - unsigned int i; - - s->chn[0].disabled = s->disabled; - s->chn[1].disabled = s->disabled; - for (i = 0; i < 2; i++) { - sysbus_init_irq(dev, &s->chn[i].irq); - s->chn[i].chn = 1 - i; - s->chn[i].clock = s->frequency / 2; - if (s->chn[i].chr) { - qemu_chr_add_handlers(s->chn[i].chr, serial_can_receive, - serial_receive1, serial_event, &s->chn[i]); - } - } - s->chn[0].otherchn = &s->chn[1]; - s->chn[1].otherchn = &s->chn[0]; - - memory_region_init_io(&s->mmio, OBJECT(s), &escc_mem_ops, s, "escc", - ESCC_SIZE << s->it_shift); - sysbus_init_mmio(dev, &s->mmio); - - if (s->chn[0].type == mouse) { - qemu_add_mouse_event_handler(sunmouse_event, &s->chn[0], 0, - "QEMU Sun Mouse"); - } - if (s->chn[1].type == kbd) { - s->chn[1].hs = qemu_input_handler_register((DeviceState *)(&s->chn[1]), - &sunkbd_handler); - } - - return 0; -} - -static Property escc_properties[] = { - DEFINE_PROP_UINT32("frequency", ESCCState, frequency, 0), - DEFINE_PROP_UINT32("it_shift", ESCCState, it_shift, 0), - DEFINE_PROP_UINT32("disabled", ESCCState, disabled, 0), - DEFINE_PROP_UINT32("chnBtype", ESCCState, chn[0].type, 0), - DEFINE_PROP_UINT32("chnAtype", ESCCState, chn[1].type, 0), - DEFINE_PROP_CHR("chrB", ESCCState, chn[0].chr), - DEFINE_PROP_CHR("chrA", ESCCState, chn[1].chr), - DEFINE_PROP_END_OF_LIST(), -}; - -static void escc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = escc_init1; - dc->reset = escc_reset; - dc->vmsd = &vmstate_escc; - dc->props = escc_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo escc_info = { - .name = TYPE_ESCC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ESCCState), - .class_init = escc_class_init, -}; - -static void escc_register_types(void) -{ - type_register_static(&escc_info); -} - -type_init(escc_register_types) diff --git a/qemu/hw/char/etraxfs_ser.c b/qemu/hw/char/etraxfs_ser.c deleted file mode 100644 index 146b387e7..000000000 --- a/qemu/hw/char/etraxfs_ser.c +++ /dev/null @@ -1,254 +0,0 @@ -/* - * QEMU ETRAX System Emulator - * - * Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB. - * - * 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/sysbus.h" -#include "sysemu/char.h" -#include "qemu/log.h" - -#define D(x) - -#define RW_TR_CTRL (0x00 / 4) -#define RW_TR_DMA_EN (0x04 / 4) -#define RW_REC_CTRL (0x08 / 4) -#define RW_DOUT (0x1c / 4) -#define RS_STAT_DIN (0x20 / 4) -#define R_STAT_DIN (0x24 / 4) -#define RW_INTR_MASK (0x2c / 4) -#define RW_ACK_INTR (0x30 / 4) -#define R_INTR (0x34 / 4) -#define R_MASKED_INTR (0x38 / 4) -#define R_MAX (0x3c / 4) - -#define STAT_DAV 16 -#define STAT_TR_IDLE 22 -#define STAT_TR_RDY 24 - -#define TYPE_ETRAX_FS_SERIAL "etraxfs,serial" -#define ETRAX_SERIAL(obj) \ - OBJECT_CHECK(ETRAXSerial, (obj), TYPE_ETRAX_FS_SERIAL) - -typedef struct ETRAXSerial { - SysBusDevice parent_obj; - - MemoryRegion mmio; - CharDriverState *chr; - qemu_irq irq; - - int pending_tx; - - uint8_t rx_fifo[16]; - unsigned int rx_fifo_pos; - unsigned int rx_fifo_len; - - /* Control registers. */ - uint32_t regs[R_MAX]; -} ETRAXSerial; - -static void ser_update_irq(ETRAXSerial *s) -{ - - if (s->rx_fifo_len) { - s->regs[R_INTR] |= 8; - } else { - s->regs[R_INTR] &= ~8; - } - - s->regs[R_MASKED_INTR] = s->regs[R_INTR] & s->regs[RW_INTR_MASK]; - qemu_set_irq(s->irq, !!s->regs[R_MASKED_INTR]); -} - -static uint64_t -ser_read(void *opaque, hwaddr addr, unsigned int size) -{ - ETRAXSerial *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) - { - case R_STAT_DIN: - r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 15]; - if (s->rx_fifo_len) { - r |= 1 << STAT_DAV; - } - r |= 1 << STAT_TR_RDY; - r |= 1 << STAT_TR_IDLE; - break; - case RS_STAT_DIN: - r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 15]; - if (s->rx_fifo_len) { - r |= 1 << STAT_DAV; - s->rx_fifo_len--; - } - r |= 1 << STAT_TR_RDY; - r |= 1 << STAT_TR_IDLE; - break; - default: - r = s->regs[addr]; - D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr, r)); - break; - } - return r; -} - -static void -ser_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - ETRAXSerial *s = opaque; - uint32_t value = val64; - unsigned char ch = val64; - - D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr, value)); - addr >>= 2; - switch (addr) - { - case RW_DOUT: - qemu_chr_fe_write(s->chr, &ch, 1); - s->regs[R_INTR] |= 3; - s->pending_tx = 1; - s->regs[addr] = value; - break; - case RW_ACK_INTR: - if (s->pending_tx) { - value &= ~1; - s->pending_tx = 0; - D(qemu_log("fixedup value=%x r_intr=%x\n", - value, s->regs[R_INTR])); - } - s->regs[addr] = value; - s->regs[R_INTR] &= ~value; - D(printf("r_intr=%x\n", s->regs[R_INTR])); - break; - default: - s->regs[addr] = value; - break; - } - ser_update_irq(s); -} - -static const MemoryRegionOps ser_ops = { - .read = ser_read, - .write = ser_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static void serial_receive(void *opaque, const uint8_t *buf, int size) -{ - ETRAXSerial *s = opaque; - int i; - - /* Got a byte. */ - if (s->rx_fifo_len >= 16) { - D(qemu_log("WARNING: UART dropped char.\n")); - return; - } - - for (i = 0; i < size; i++) { - s->rx_fifo[s->rx_fifo_pos] = buf[i]; - s->rx_fifo_pos++; - s->rx_fifo_pos &= 15; - s->rx_fifo_len++; - } - - ser_update_irq(s); -} - -static int serial_can_receive(void *opaque) -{ - ETRAXSerial *s = opaque; - - /* Is the receiver enabled? */ - if (!(s->regs[RW_REC_CTRL] & (1 << 3))) { - return 0; - } - - return sizeof(s->rx_fifo) - s->rx_fifo_len; -} - -static void serial_event(void *opaque, int event) -{ - -} - -static void etraxfs_ser_reset(DeviceState *d) -{ - ETRAXSerial *s = ETRAX_SERIAL(d); - - /* transmitter begins ready and idle. */ - s->regs[RS_STAT_DIN] |= (1 << STAT_TR_RDY); - s->regs[RS_STAT_DIN] |= (1 << STAT_TR_IDLE); - - s->regs[RW_REC_CTRL] = 0x10000; - -} - -static int etraxfs_ser_init(SysBusDevice *dev) -{ - ETRAXSerial *s = ETRAX_SERIAL(dev); - - sysbus_init_irq(dev, &s->irq); - memory_region_init_io(&s->mmio, OBJECT(s), &ser_ops, s, - "etraxfs-serial", R_MAX * 4); - sysbus_init_mmio(dev, &s->mmio); - - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); - if (s->chr) { - qemu_chr_add_handlers(s->chr, - serial_can_receive, serial_receive, - serial_event, s); - } - return 0; -} - -static void etraxfs_ser_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = etraxfs_ser_init; - dc->reset = etraxfs_ser_reset; - /* Reason: init() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo etraxfs_ser_info = { - .name = TYPE_ETRAX_FS_SERIAL, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ETRAXSerial), - .class_init = etraxfs_ser_class_init, -}; - -static void etraxfs_serial_register_types(void) -{ - type_register_static(&etraxfs_ser_info); -} - -type_init(etraxfs_serial_register_types) diff --git a/qemu/hw/char/exynos4210_uart.c b/qemu/hw/char/exynos4210_uart.c deleted file mode 100644 index 885ecc027..000000000 --- a/qemu/hw/char/exynos4210_uart.c +++ /dev/null @@ -1,678 +0,0 @@ -/* - * Exynos4210 UART Emulation - * - * Copyright (C) 2011 Samsung Electronics Co Ltd. - * Maksim Kozlov, - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "qemu/error-report.h" -#include "sysemu/sysemu.h" -#include "sysemu/char.h" - -#include "hw/arm/exynos4210.h" - -#undef DEBUG_UART -#undef DEBUG_UART_EXTEND -#undef DEBUG_IRQ -#undef DEBUG_Rx_DATA -#undef DEBUG_Tx_DATA - -#define DEBUG_UART 0 -#define DEBUG_UART_EXTEND 0 -#define DEBUG_IRQ 0 -#define DEBUG_Rx_DATA 0 -#define DEBUG_Tx_DATA 0 - -#if DEBUG_UART -#define PRINT_DEBUG(fmt, args...) \ - do { \ - fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ - } while (0) - -#if DEBUG_UART_EXTEND -#define PRINT_DEBUG_EXTEND(fmt, args...) \ - do { \ - fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ - } while (0) -#else -#define PRINT_DEBUG_EXTEND(fmt, args...) \ - do {} while (0) -#endif /* EXTEND */ - -#else -#define PRINT_DEBUG(fmt, args...) \ - do {} while (0) -#define PRINT_DEBUG_EXTEND(fmt, args...) \ - do {} while (0) -#endif - -#define PRINT_ERROR(fmt, args...) \ - do { \ - fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ - } while (0) - -/* - * Offsets for UART registers relative to SFR base address - * for UARTn - * - */ -#define ULCON 0x0000 /* Line Control */ -#define UCON 0x0004 /* Control */ -#define UFCON 0x0008 /* FIFO Control */ -#define UMCON 0x000C /* Modem Control */ -#define UTRSTAT 0x0010 /* Tx/Rx Status */ -#define UERSTAT 0x0014 /* UART Error Status */ -#define UFSTAT 0x0018 /* FIFO Status */ -#define UMSTAT 0x001C /* Modem Status */ -#define UTXH 0x0020 /* Transmit Buffer */ -#define URXH 0x0024 /* Receive Buffer */ -#define UBRDIV 0x0028 /* Baud Rate Divisor */ -#define UFRACVAL 0x002C /* Divisor Fractional Value */ -#define UINTP 0x0030 /* Interrupt Pending */ -#define UINTSP 0x0034 /* Interrupt Source Pending */ -#define UINTM 0x0038 /* Interrupt Mask */ - -/* - * for indexing register in the uint32_t array - * - * 'reg' - register offset (see offsets definitions above) - * - */ -#define I_(reg) (reg / sizeof(uint32_t)) - -typedef struct Exynos4210UartReg { - const char *name; /* the only reason is the debug output */ - hwaddr offset; - uint32_t reset_value; -} Exynos4210UartReg; - -static Exynos4210UartReg exynos4210_uart_regs[] = { - {"ULCON", ULCON, 0x00000000}, - {"UCON", UCON, 0x00003000}, - {"UFCON", UFCON, 0x00000000}, - {"UMCON", UMCON, 0x00000000}, - {"UTRSTAT", UTRSTAT, 0x00000006}, /* RO */ - {"UERSTAT", UERSTAT, 0x00000000}, /* RO */ - {"UFSTAT", UFSTAT, 0x00000000}, /* RO */ - {"UMSTAT", UMSTAT, 0x00000000}, /* RO */ - {"UTXH", UTXH, 0x5c5c5c5c}, /* WO, undefined reset value*/ - {"URXH", URXH, 0x00000000}, /* RO */ - {"UBRDIV", UBRDIV, 0x00000000}, - {"UFRACVAL", UFRACVAL, 0x00000000}, - {"UINTP", UINTP, 0x00000000}, - {"UINTSP", UINTSP, 0x00000000}, - {"UINTM", UINTM, 0x00000000}, -}; - -#define EXYNOS4210_UART_REGS_MEM_SIZE 0x3C - -/* UART FIFO Control */ -#define UFCON_FIFO_ENABLE 0x1 -#define UFCON_Rx_FIFO_RESET 0x2 -#define UFCON_Tx_FIFO_RESET 0x4 -#define UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT 8 -#define UFCON_Tx_FIFO_TRIGGER_LEVEL (7 << UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT) -#define UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT 4 -#define UFCON_Rx_FIFO_TRIGGER_LEVEL (7 << UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT) - -/* Uart FIFO Status */ -#define UFSTAT_Rx_FIFO_COUNT 0xff -#define UFSTAT_Rx_FIFO_FULL 0x100 -#define UFSTAT_Rx_FIFO_ERROR 0x200 -#define UFSTAT_Tx_FIFO_COUNT_SHIFT 16 -#define UFSTAT_Tx_FIFO_COUNT (0xff << UFSTAT_Tx_FIFO_COUNT_SHIFT) -#define UFSTAT_Tx_FIFO_FULL_SHIFT 24 -#define UFSTAT_Tx_FIFO_FULL (1 << UFSTAT_Tx_FIFO_FULL_SHIFT) - -/* UART Interrupt Source Pending */ -#define UINTSP_RXD 0x1 /* Receive interrupt */ -#define UINTSP_ERROR 0x2 /* Error interrupt */ -#define UINTSP_TXD 0x4 /* Transmit interrupt */ -#define UINTSP_MODEM 0x8 /* Modem interrupt */ - -/* UART Line Control */ -#define ULCON_IR_MODE_SHIFT 6 -#define ULCON_PARITY_SHIFT 3 -#define ULCON_STOP_BIT_SHIFT 1 - -/* UART Tx/Rx Status */ -#define UTRSTAT_TRANSMITTER_EMPTY 0x4 -#define UTRSTAT_Tx_BUFFER_EMPTY 0x2 -#define UTRSTAT_Rx_BUFFER_DATA_READY 0x1 - -/* UART Error Status */ -#define UERSTAT_OVERRUN 0x1 -#define UERSTAT_PARITY 0x2 -#define UERSTAT_FRAME 0x4 -#define UERSTAT_BREAK 0x8 - -typedef struct { - uint8_t *data; - uint32_t sp, rp; /* store and retrieve pointers */ - uint32_t size; -} Exynos4210UartFIFO; - -#define TYPE_EXYNOS4210_UART "exynos4210.uart" -#define EXYNOS4210_UART(obj) \ - OBJECT_CHECK(Exynos4210UartState, (obj), TYPE_EXYNOS4210_UART) - -typedef struct Exynos4210UartState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - - uint32_t reg[EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)]; - Exynos4210UartFIFO rx; - Exynos4210UartFIFO tx; - - CharDriverState *chr; - qemu_irq irq; - - uint32_t channel; - -} Exynos4210UartState; - - -#if DEBUG_UART -/* Used only for debugging inside PRINT_DEBUG_... macros */ -static const char *exynos4210_uart_regname(hwaddr offset) -{ - - int i; - - for (i = 0; i < ARRAY_SIZE(exynos4210_uart_regs); i++) { - if (offset == exynos4210_uart_regs[i].offset) { - return exynos4210_uart_regs[i].name; - } - } - - return NULL; -} -#endif - - -static void fifo_store(Exynos4210UartFIFO *q, uint8_t ch) -{ - q->data[q->sp] = ch; - q->sp = (q->sp + 1) % q->size; -} - -static uint8_t fifo_retrieve(Exynos4210UartFIFO *q) -{ - uint8_t ret = q->data[q->rp]; - q->rp = (q->rp + 1) % q->size; - return ret; -} - -static int fifo_elements_number(Exynos4210UartFIFO *q) -{ - if (q->sp < q->rp) { - return q->size - q->rp + q->sp; - } - - return q->sp - q->rp; -} - -static int fifo_empty_elements_number(Exynos4210UartFIFO *q) -{ - return q->size - fifo_elements_number(q); -} - -static void fifo_reset(Exynos4210UartFIFO *q) -{ - g_free(q->data); - q->data = NULL; - - q->data = (uint8_t *)g_malloc0(q->size); - - q->sp = 0; - q->rp = 0; -} - -static uint32_t exynos4210_uart_Tx_FIFO_trigger_level(Exynos4210UartState *s) -{ - uint32_t level = 0; - uint32_t reg; - - reg = (s->reg[I_(UFCON)] & UFCON_Tx_FIFO_TRIGGER_LEVEL) >> - UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT; - - switch (s->channel) { - case 0: - level = reg * 32; - break; - case 1: - case 4: - level = reg * 8; - break; - case 2: - case 3: - level = reg * 2; - break; - default: - level = 0; - PRINT_ERROR("Wrong UART channel number: %d\n", s->channel); - } - - return level; -} - -static void exynos4210_uart_update_irq(Exynos4210UartState *s) -{ - /* - * The Tx interrupt is always requested if the number of data in the - * transmit FIFO is smaller than the trigger level. - */ - if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) { - - uint32_t count = (s->reg[I_(UFSTAT)] & UFSTAT_Tx_FIFO_COUNT) >> - UFSTAT_Tx_FIFO_COUNT_SHIFT; - - if (count <= exynos4210_uart_Tx_FIFO_trigger_level(s)) { - s->reg[I_(UINTSP)] |= UINTSP_TXD; - } - } - - s->reg[I_(UINTP)] = s->reg[I_(UINTSP)] & ~s->reg[I_(UINTM)]; - - if (s->reg[I_(UINTP)]) { - qemu_irq_raise(s->irq); - -#if DEBUG_IRQ - fprintf(stderr, "UART%d: IRQ has been raised: %08x\n", - s->channel, s->reg[I_(UINTP)]); -#endif - - } else { - qemu_irq_lower(s->irq); - } -} - -static void exynos4210_uart_update_parameters(Exynos4210UartState *s) -{ - int speed, parity, data_bits, stop_bits, frame_size; - QEMUSerialSetParams ssp; - uint64_t uclk_rate; - - if (s->reg[I_(UBRDIV)] == 0) { - return; - } - - frame_size = 1; /* start bit */ - if (s->reg[I_(ULCON)] & 0x20) { - frame_size++; /* parity bit */ - if (s->reg[I_(ULCON)] & 0x28) { - parity = 'E'; - } else { - parity = 'O'; - } - } else { - parity = 'N'; - } - - if (s->reg[I_(ULCON)] & 0x4) { - stop_bits = 2; - } else { - stop_bits = 1; - } - - data_bits = (s->reg[I_(ULCON)] & 0x3) + 5; - - frame_size += data_bits + stop_bits; - - uclk_rate = 24000000; - - speed = uclk_rate / ((16 * (s->reg[I_(UBRDIV)]) & 0xffff) + - (s->reg[I_(UFRACVAL)] & 0x7) + 16); - - ssp.speed = speed; - ssp.parity = parity; - ssp.data_bits = data_bits; - ssp.stop_bits = stop_bits; - - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); - - PRINT_DEBUG("UART%d: speed: %d, parity: %c, data: %d, stop: %d\n", - s->channel, speed, parity, data_bits, stop_bits); -} - -static void exynos4210_uart_write(void *opaque, hwaddr offset, - uint64_t val, unsigned size) -{ - Exynos4210UartState *s = (Exynos4210UartState *)opaque; - uint8_t ch; - - PRINT_DEBUG_EXTEND("UART%d: <0x%04x> %s <- 0x%08llx\n", s->channel, - offset, exynos4210_uart_regname(offset), (long long unsigned int)val); - - switch (offset) { - case ULCON: - case UBRDIV: - case UFRACVAL: - s->reg[I_(offset)] = val; - exynos4210_uart_update_parameters(s); - break; - case UFCON: - s->reg[I_(UFCON)] = val; - if (val & UFCON_Rx_FIFO_RESET) { - fifo_reset(&s->rx); - s->reg[I_(UFCON)] &= ~UFCON_Rx_FIFO_RESET; - PRINT_DEBUG("UART%d: Rx FIFO Reset\n", s->channel); - } - if (val & UFCON_Tx_FIFO_RESET) { - fifo_reset(&s->tx); - s->reg[I_(UFCON)] &= ~UFCON_Tx_FIFO_RESET; - PRINT_DEBUG("UART%d: Tx FIFO Reset\n", s->channel); - } - break; - - case UTXH: - if (s->chr) { - s->reg[I_(UTRSTAT)] &= ~(UTRSTAT_TRANSMITTER_EMPTY | - UTRSTAT_Tx_BUFFER_EMPTY); - ch = (uint8_t)val; - qemu_chr_fe_write(s->chr, &ch, 1); -#if DEBUG_Tx_DATA - fprintf(stderr, "%c", ch); -#endif - s->reg[I_(UTRSTAT)] |= UTRSTAT_TRANSMITTER_EMPTY | - UTRSTAT_Tx_BUFFER_EMPTY; - s->reg[I_(UINTSP)] |= UINTSP_TXD; - exynos4210_uart_update_irq(s); - } - break; - - case UINTP: - s->reg[I_(UINTP)] &= ~val; - s->reg[I_(UINTSP)] &= ~val; - PRINT_DEBUG("UART%d: UINTP [%04x] have been cleared: %08x\n", - s->channel, offset, s->reg[I_(UINTP)]); - exynos4210_uart_update_irq(s); - break; - case UTRSTAT: - case UERSTAT: - case UFSTAT: - case UMSTAT: - case URXH: - PRINT_DEBUG("UART%d: Trying to write into RO register: %s [%04x]\n", - s->channel, exynos4210_uart_regname(offset), offset); - break; - case UINTSP: - s->reg[I_(UINTSP)] &= ~val; - break; - case UINTM: - s->reg[I_(UINTM)] = val; - exynos4210_uart_update_irq(s); - break; - case UCON: - case UMCON: - default: - s->reg[I_(offset)] = val; - break; - } -} -static uint64_t exynos4210_uart_read(void *opaque, hwaddr offset, - unsigned size) -{ - Exynos4210UartState *s = (Exynos4210UartState *)opaque; - uint32_t res; - - switch (offset) { - case UERSTAT: /* Read Only */ - res = s->reg[I_(UERSTAT)]; - s->reg[I_(UERSTAT)] = 0; - return res; - case UFSTAT: /* Read Only */ - s->reg[I_(UFSTAT)] = fifo_elements_number(&s->rx) & 0xff; - if (fifo_empty_elements_number(&s->rx) == 0) { - s->reg[I_(UFSTAT)] |= UFSTAT_Rx_FIFO_FULL; - s->reg[I_(UFSTAT)] &= ~0xff; - } - return s->reg[I_(UFSTAT)]; - case URXH: - if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) { - if (fifo_elements_number(&s->rx)) { - res = fifo_retrieve(&s->rx); -#if DEBUG_Rx_DATA - fprintf(stderr, "%c", res); -#endif - if (!fifo_elements_number(&s->rx)) { - s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY; - } else { - s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; - } - } else { - s->reg[I_(UINTSP)] |= UINTSP_ERROR; - exynos4210_uart_update_irq(s); - res = 0; - } - } else { - s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY; - res = s->reg[I_(URXH)]; - } - return res; - case UTXH: - PRINT_DEBUG("UART%d: Trying to read from WO register: %s [%04x]\n", - s->channel, exynos4210_uart_regname(offset), offset); - break; - default: - return s->reg[I_(offset)]; - } - - return 0; -} - -static const MemoryRegionOps exynos4210_uart_ops = { - .read = exynos4210_uart_read, - .write = exynos4210_uart_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .max_access_size = 4, - .unaligned = false - }, -}; - -static int exynos4210_uart_can_receive(void *opaque) -{ - Exynos4210UartState *s = (Exynos4210UartState *)opaque; - - return fifo_empty_elements_number(&s->rx); -} - - -static void exynos4210_uart_receive(void *opaque, const uint8_t *buf, int size) -{ - Exynos4210UartState *s = (Exynos4210UartState *)opaque; - int i; - - if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) { - if (fifo_empty_elements_number(&s->rx) < size) { - for (i = 0; i < fifo_empty_elements_number(&s->rx); i++) { - fifo_store(&s->rx, buf[i]); - } - s->reg[I_(UINTSP)] |= UINTSP_ERROR; - s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; - } else { - for (i = 0; i < size; i++) { - fifo_store(&s->rx, buf[i]); - } - s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; - } - /* XXX: Around here we maybe should check Rx trigger level */ - s->reg[I_(UINTSP)] |= UINTSP_RXD; - } else { - s->reg[I_(URXH)] = buf[0]; - s->reg[I_(UINTSP)] |= UINTSP_RXD; - s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; - } - - exynos4210_uart_update_irq(s); -} - - -static void exynos4210_uart_event(void *opaque, int event) -{ - Exynos4210UartState *s = (Exynos4210UartState *)opaque; - - if (event == CHR_EVENT_BREAK) { - /* When the RxDn is held in logic 0, then a null byte is pushed into the - * fifo */ - fifo_store(&s->rx, '\0'); - s->reg[I_(UERSTAT)] |= UERSTAT_BREAK; - exynos4210_uart_update_irq(s); - } -} - - -static void exynos4210_uart_reset(DeviceState *dev) -{ - Exynos4210UartState *s = EXYNOS4210_UART(dev); - int i; - - for (i = 0; i < ARRAY_SIZE(exynos4210_uart_regs); i++) { - s->reg[I_(exynos4210_uart_regs[i].offset)] = - exynos4210_uart_regs[i].reset_value; - } - - fifo_reset(&s->rx); - fifo_reset(&s->tx); - - PRINT_DEBUG("UART%d: Rx FIFO size: %d\n", s->channel, s->rx.size); -} - -static const VMStateDescription vmstate_exynos4210_uart_fifo = { - .name = "exynos4210.uart.fifo", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(sp, Exynos4210UartFIFO), - VMSTATE_UINT32(rp, Exynos4210UartFIFO), - VMSTATE_VBUFFER_UINT32(data, Exynos4210UartFIFO, 1, NULL, 0, size), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_exynos4210_uart = { - .name = "exynos4210.uart", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(rx, Exynos4210UartState, 1, - vmstate_exynos4210_uart_fifo, Exynos4210UartFIFO), - VMSTATE_UINT32_ARRAY(reg, Exynos4210UartState, - EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)), - VMSTATE_END_OF_LIST() - } -}; - -DeviceState *exynos4210_uart_create(hwaddr addr, - int fifo_size, - int channel, - CharDriverState *chr, - qemu_irq irq) -{ - DeviceState *dev; - SysBusDevice *bus; - - const char chr_name[] = "serial"; - char label[ARRAY_SIZE(chr_name) + 1]; - - dev = qdev_create(NULL, TYPE_EXYNOS4210_UART); - - if (!chr) { - if (channel >= 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)) { - error_report("Can't assign serial port to UART%d", channel); - exit(1); - } - } - } - - qdev_prop_set_chr(dev, "chardev", chr); - qdev_prop_set_uint32(dev, "channel", channel); - qdev_prop_set_uint32(dev, "rx-size", fifo_size); - qdev_prop_set_uint32(dev, "tx-size", fifo_size); - - 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); - - return dev; -} - -static int exynos4210_uart_init(SysBusDevice *dev) -{ - Exynos4210UartState *s = EXYNOS4210_UART(dev); - - /* memory mapping */ - memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_uart_ops, s, - "exynos4210.uart", EXYNOS4210_UART_REGS_MEM_SIZE); - sysbus_init_mmio(dev, &s->iomem); - - sysbus_init_irq(dev, &s->irq); - - qemu_chr_add_handlers(s->chr, exynos4210_uart_can_receive, - exynos4210_uart_receive, exynos4210_uart_event, s); - - return 0; -} - -static Property exynos4210_uart_properties[] = { - DEFINE_PROP_CHR("chardev", Exynos4210UartState, chr), - DEFINE_PROP_UINT32("channel", Exynos4210UartState, channel, 0), - DEFINE_PROP_UINT32("rx-size", Exynos4210UartState, rx.size, 16), - DEFINE_PROP_UINT32("tx-size", Exynos4210UartState, tx.size, 16), - DEFINE_PROP_END_OF_LIST(), -}; - -static void exynos4210_uart_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = exynos4210_uart_init; - dc->reset = exynos4210_uart_reset; - dc->props = exynos4210_uart_properties; - dc->vmsd = &vmstate_exynos4210_uart; -} - -static const TypeInfo exynos4210_uart_info = { - .name = TYPE_EXYNOS4210_UART, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Exynos4210UartState), - .class_init = exynos4210_uart_class_init, -}; - -static void exynos4210_uart_register(void) -{ - type_register_static(&exynos4210_uart_info); -} - -type_init(exynos4210_uart_register) diff --git a/qemu/hw/char/grlib_apbuart.c b/qemu/hw/char/grlib_apbuart.c deleted file mode 100644 index 871524c82..000000000 --- a/qemu/hw/char/grlib_apbuart.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * QEMU GRLIB APB UART Emulator - * - * Copyright (c) 2010-2011 AdaCore - * - * 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/sysbus.h" -#include "sysemu/char.h" - -#include "trace.h" - -#define UART_REG_SIZE 20 /* Size of memory mapped registers */ - -/* UART status register fields */ -#define UART_DATA_READY (1 << 0) -#define UART_TRANSMIT_SHIFT_EMPTY (1 << 1) -#define UART_TRANSMIT_FIFO_EMPTY (1 << 2) -#define UART_BREAK_RECEIVED (1 << 3) -#define UART_OVERRUN (1 << 4) -#define UART_PARITY_ERROR (1 << 5) -#define UART_FRAMING_ERROR (1 << 6) -#define UART_TRANSMIT_FIFO_HALF (1 << 7) -#define UART_RECEIVE_FIFO_HALF (1 << 8) -#define UART_TRANSMIT_FIFO_FULL (1 << 9) -#define UART_RECEIVE_FIFO_FULL (1 << 10) - -/* UART control register fields */ -#define UART_RECEIVE_ENABLE (1 << 0) -#define UART_TRANSMIT_ENABLE (1 << 1) -#define UART_RECEIVE_INTERRUPT (1 << 2) -#define UART_TRANSMIT_INTERRUPT (1 << 3) -#define UART_PARITY_SELECT (1 << 4) -#define UART_PARITY_ENABLE (1 << 5) -#define UART_FLOW_CONTROL (1 << 6) -#define UART_LOOPBACK (1 << 7) -#define UART_EXTERNAL_CLOCK (1 << 8) -#define UART_RECEIVE_FIFO_INTERRUPT (1 << 9) -#define UART_TRANSMIT_FIFO_INTERRUPT (1 << 10) -#define UART_FIFO_DEBUG_MODE (1 << 11) -#define UART_OUTPUT_ENABLE (1 << 12) -#define UART_FIFO_AVAILABLE (1 << 31) - -/* Memory mapped register offsets */ -#define DATA_OFFSET 0x00 -#define STATUS_OFFSET 0x04 -#define CONTROL_OFFSET 0x08 -#define SCALER_OFFSET 0x0C /* not supported */ -#define FIFO_DEBUG_OFFSET 0x10 /* not supported */ - -#define FIFO_LENGTH 1024 - -#define TYPE_GRLIB_APB_UART "grlib,apbuart" -#define GRLIB_APB_UART(obj) \ - OBJECT_CHECK(UART, (obj), TYPE_GRLIB_APB_UART) - -typedef struct UART { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq irq; - - CharDriverState *chr; - - /* registers */ - uint32_t status; - uint32_t control; - - /* FIFO */ - char buffer[FIFO_LENGTH]; - int len; - int current; -} UART; - -static int uart_data_to_read(UART *uart) -{ - return uart->current < uart->len; -} - -static char uart_pop(UART *uart) -{ - char ret; - - if (uart->len == 0) { - uart->status &= ~UART_DATA_READY; - return 0; - } - - ret = uart->buffer[uart->current++]; - - if (uart->current >= uart->len) { - /* Flush */ - uart->len = 0; - uart->current = 0; - } - - if (!uart_data_to_read(uart)) { - uart->status &= ~UART_DATA_READY; - } - - return ret; -} - -static void uart_add_to_fifo(UART *uart, - const uint8_t *buffer, - int length) -{ - if (uart->len + length > FIFO_LENGTH) { - abort(); - } - memcpy(uart->buffer + uart->len, buffer, length); - uart->len += length; -} - -static int grlib_apbuart_can_receive(void *opaque) -{ - UART *uart = opaque; - - return FIFO_LENGTH - uart->len; -} - -static void grlib_apbuart_receive(void *opaque, const uint8_t *buf, int size) -{ - UART *uart = opaque; - - if (uart->control & UART_RECEIVE_ENABLE) { - uart_add_to_fifo(uart, buf, size); - - uart->status |= UART_DATA_READY; - - if (uart->control & UART_RECEIVE_INTERRUPT) { - qemu_irq_pulse(uart->irq); - } - } -} - -static void grlib_apbuart_event(void *opaque, int event) -{ - trace_grlib_apbuart_event(event); -} - - -static uint64_t grlib_apbuart_read(void *opaque, hwaddr addr, - unsigned size) -{ - UART *uart = opaque; - - addr &= 0xff; - - /* Unit registers */ - switch (addr) { - case DATA_OFFSET: - case DATA_OFFSET + 3: /* when only one byte read */ - return uart_pop(uart); - - case STATUS_OFFSET: - /* Read Only */ - return uart->status; - - case CONTROL_OFFSET: - return uart->control; - - case SCALER_OFFSET: - /* Not supported */ - return 0; - - default: - trace_grlib_apbuart_readl_unknown(addr); - return 0; - } -} - -static void grlib_apbuart_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - UART *uart = opaque; - unsigned char c = 0; - - addr &= 0xff; - - /* Unit registers */ - switch (addr) { - case DATA_OFFSET: - case DATA_OFFSET + 3: /* When only one byte write */ - /* Transmit when character device available and transmitter enabled */ - if ((uart->chr) && (uart->control & UART_TRANSMIT_ENABLE)) { - c = value & 0xFF; - qemu_chr_fe_write(uart->chr, &c, 1); - /* Generate interrupt */ - if (uart->control & UART_TRANSMIT_INTERRUPT) { - qemu_irq_pulse(uart->irq); - } - } - return; - - case STATUS_OFFSET: - /* Read Only */ - return; - - case CONTROL_OFFSET: - uart->control = value; - return; - - case SCALER_OFFSET: - /* Not supported */ - return; - - default: - break; - } - - trace_grlib_apbuart_writel_unknown(addr, value); -} - -static const MemoryRegionOps grlib_apbuart_ops = { - .write = grlib_apbuart_write, - .read = grlib_apbuart_read, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int grlib_apbuart_init(SysBusDevice *dev) -{ - UART *uart = GRLIB_APB_UART(dev); - - qemu_chr_add_handlers(uart->chr, - grlib_apbuart_can_receive, - grlib_apbuart_receive, - grlib_apbuart_event, - uart); - - sysbus_init_irq(dev, &uart->irq); - - memory_region_init_io(&uart->iomem, OBJECT(uart), &grlib_apbuart_ops, uart, - "uart", UART_REG_SIZE); - - sysbus_init_mmio(dev, &uart->iomem); - - return 0; -} - -static void grlib_apbuart_reset(DeviceState *d) -{ - UART *uart = GRLIB_APB_UART(d); - - /* Transmitter FIFO and shift registers are always empty in QEMU */ - uart->status = UART_TRANSMIT_FIFO_EMPTY | UART_TRANSMIT_SHIFT_EMPTY; - /* Everything is off */ - uart->control = 0; - /* Flush receive FIFO */ - uart->len = 0; - uart->current = 0; -} - -static Property grlib_apbuart_properties[] = { - DEFINE_PROP_CHR("chrdev", UART, chr), - DEFINE_PROP_END_OF_LIST(), -}; - -static void grlib_apbuart_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = grlib_apbuart_init; - dc->reset = grlib_apbuart_reset; - dc->props = grlib_apbuart_properties; -} - -static const TypeInfo grlib_apbuart_info = { - .name = TYPE_GRLIB_APB_UART, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(UART), - .class_init = grlib_apbuart_class_init, -}; - -static void grlib_apbuart_register_types(void) -{ - type_register_static(&grlib_apbuart_info); -} - -type_init(grlib_apbuart_register_types) diff --git a/qemu/hw/char/imx_serial.c b/qemu/hw/char/imx_serial.c deleted file mode 100644 index 6df74ac7c..000000000 --- a/qemu/hw/char/imx_serial.c +++ /dev/null @@ -1,367 +0,0 @@ -/* - * IMX31 UARTS - * - * Copyright (c) 2008 OKL - * Originally Written by Hans Jiang - * Copyright (c) 2011 NICTA Pty Ltd. - * Updated by Jean-Christophe Dubois - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - * This is a `bare-bones' implementation of the IMX series serial ports. - * TODO: - * -- implement FIFOs. The real hardware has 32 word transmit - * and receive FIFOs; we currently use a 1-char buffer - * -- implement DMA - * -- implement BAUD-rate and modem lines, for when the backend - * is a real serial device. - */ - -#include "qemu/osdep.h" -#include "hw/char/imx_serial.h" -#include "sysemu/sysemu.h" -#include "sysemu/char.h" - -#ifndef DEBUG_IMX_UART -#define DEBUG_IMX_UART 0 -#endif - -#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 = TYPE_IMX_SERIAL, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32(readbuff, IMXSerialState), - VMSTATE_UINT32(usr1, IMXSerialState), - VMSTATE_UINT32(usr2, IMXSerialState), - VMSTATE_UINT32(ucr1, IMXSerialState), - VMSTATE_UINT32(uts1, IMXSerialState), - VMSTATE_UINT32(onems, IMXSerialState), - VMSTATE_UINT32(ufcr, IMXSerialState), - VMSTATE_UINT32(ubmr, IMXSerialState), - VMSTATE_UINT32(ubrc, IMXSerialState), - VMSTATE_UINT32(ucr3, IMXSerialState), - VMSTATE_END_OF_LIST() - }, -}; - -static void imx_update(IMXSerialState *s) -{ - uint32_t flags; - - flags = (s->usr1 & s->ucr1) & (USR1_TRDY|USR1_RRDY); - if (s->ucr1 & UCR1_TXMPTYEN) { - flags |= (s->uts1 & UTS1_TXEMPTY); - } else { - flags &= ~USR1_TRDY; - } - - qemu_set_irq(s->irq, !!flags); -} - -static void imx_serial_reset(IMXSerialState *s) -{ - - s->usr1 = USR1_TRDY | USR1_RXDS; - /* - * Fake attachment of a terminal: assert RTS. - */ - s->usr1 |= USR1_RTSS; - s->usr2 = USR2_TXFE | USR2_TXDC | USR2_DCDIN; - s->uts1 = UTS1_RXEMPTY | UTS1_TXEMPTY; - s->ucr1 = 0; - s->ucr2 = UCR2_SRST; - s->ucr3 = 0x700; - s->ubmr = 0; - s->ubrc = 4; - s->readbuff = URXD_ERR; -} - -static void imx_serial_reset_at_boot(DeviceState *dev) -{ - IMXSerialState *s = IMX_SERIAL(dev); - - imx_serial_reset(s); - - /* - * enable the uart on boot, so messages from the linux decompresser - * are visible. On real hardware this is done by the boot rom - * before anything else is loaded. - */ - s->ucr1 = UCR1_UARTEN; - s->ucr2 = UCR2_TXEN; - -} - -static uint64_t imx_serial_read(void *opaque, hwaddr offset, - unsigned size) -{ - IMXSerialState *s = (IMXSerialState *)opaque; - uint32_t c; - - DPRINTF("read(offset=0x%" HWADDR_PRIx ")\n", offset); - - switch (offset >> 2) { - case 0x0: /* URXD */ - c = s->readbuff; - if (!(s->uts1 & UTS1_RXEMPTY)) { - /* Character is valid */ - c |= URXD_CHARRDY; - s->usr1 &= ~USR1_RRDY; - s->usr2 &= ~USR2_RDR; - s->uts1 |= UTS1_RXEMPTY; - imx_update(s); - if (s->chr) { - qemu_chr_accept_input(s->chr); - } - } - return c; - - case 0x20: /* UCR1 */ - return s->ucr1; - - case 0x21: /* UCR2 */ - return s->ucr2; - - case 0x25: /* USR1 */ - return s->usr1; - - case 0x26: /* USR2 */ - return s->usr2; - - case 0x2A: /* BRM Modulator */ - return s->ubmr; - - case 0x2B: /* Baud Rate Count */ - return s->ubrc; - - case 0x2d: /* Test register */ - return s->uts1; - - case 0x24: /* UFCR */ - return s->ufcr; - - case 0x2c: - return s->onems; - - case 0x22: /* UCR3 */ - return s->ucr3; - - case 0x23: /* UCR4 */ - case 0x29: /* BRM Incremental */ - return 0x0; /* TODO */ - - default: - 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) -{ - IMXSerialState *s = (IMXSerialState *)opaque; - unsigned char ch; - - 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 */ - ch = value; - if (s->ucr2 & UCR2_TXEN) { - if (s->chr) { - qemu_chr_fe_write(s->chr, &ch, 1); - } - s->usr1 &= ~USR1_TRDY; - imx_update(s); - s->usr1 |= USR1_TRDY; - imx_update(s); - } - break; - - case 0x20: /* UCR1 */ - s->ucr1 = value & 0xffff; - - DPRINTF("write(ucr1=%x)\n", (unsigned int)value); - - imx_update(s); - break; - - case 0x21: /* UCR2 */ - /* - * Only a few bits in control register 2 are implemented as yet. - * If it's intended to use a real serial device as a back-end, this - * register will have to be implemented more fully. - */ - if (!(value & UCR2_SRST)) { - imx_serial_reset(s); - imx_update(s); - value |= UCR2_SRST; - } - if (value & UCR2_RXEN) { - if (!(s->ucr2 & UCR2_RXEN)) { - if (s->chr) { - qemu_chr_accept_input(s->chr); - } - } - } - s->ucr2 = value & 0xffff; - break; - - case 0x25: /* USR1 */ - value &= USR1_AWAKE | USR1_AIRINT | USR1_DTRD | USR1_AGTIM | - 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 - */ - value &= USR2_ADET | USR2_DTRF | USR2_IDLE | USR2_ACST | - 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 - */ - case 0x29: /* UBIR */ - s->ubrc = value & 0xffff; - break; - - case 0x2a: /* UBMR */ - s->ubmr = value & 0xffff; - break; - - case 0x2c: /* One ms reg */ - s->onems = value & 0xffff; - break; - - case 0x24: /* FIFO control register */ - s->ufcr = value & 0xffff; - break; - - case 0x22: /* UCR3 */ - s->ucr3 = value & 0xffff; - break; - - case 0x2d: /* UTS1 */ - case 0x23: /* UCR4 */ - qemu_log_mask(LOG_UNIMP, "[%s]%s: Unimplemented reg 0x%" - HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset); - /* TODO */ - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" - HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset); - } -} - -static int imx_can_receive(void *opaque) -{ - IMXSerialState *s = (IMXSerialState *)opaque; - return !(s->usr1 & USR1_RRDY); -} - -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; - s->readbuff = value; - imx_update(s); -} - -static void imx_receive(void *opaque, const uint8_t *buf, int size) -{ - imx_put_data(opaque, *buf); -} - -static void imx_event(void *opaque, int event) -{ - if (event == CHR_EVENT_BREAK) { - imx_put_data(opaque, URXD_BRK); - } -} - - -static const struct MemoryRegionOps imx_serial_ops = { - .read = imx_serial_read, - .write = imx_serial_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void imx_serial_realize(DeviceState *dev, Error **errp) -{ - IMXSerialState *s = IMX_SERIAL(dev); - - if (s->chr) { - qemu_chr_add_handlers(s->chr, imx_can_receive, imx_receive, - imx_event, s); - } else { - DPRINTF("No char dev for uart\n"); - } -} - -static void imx_serial_init(Object *obj) -{ - 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 imx_serial_properties[] = { - DEFINE_PROP_CHR("chardev", IMXSerialState, chr), - DEFINE_PROP_END_OF_LIST(), -}; - -static void imx_serial_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - 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 = imx_serial_properties; -} - -static const TypeInfo imx_serial_info = { - .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) -{ - type_register_static(&imx_serial_info); -} - -type_init(imx_serial_register_types) diff --git a/qemu/hw/char/ipoctal232.c b/qemu/hw/char/ipoctal232.c deleted file mode 100644 index bc0ae4980..000000000 --- a/qemu/hw/char/ipoctal232.c +++ /dev/null @@ -1,604 +0,0 @@ -/* - * QEMU GE IP-Octal 232 IndustryPack emulation - * - * Copyright (C) 2012 Igalia, S.L. - * Author: Alberto Garcia - * - * This code is licensed under the GNU GPL v2 or (at your option) any - * later version. - */ - -#include "qemu/osdep.h" -#include "hw/ipack/ipack.h" -#include "qemu/bitops.h" -#include "sysemu/char.h" - -/* #define DEBUG_IPOCTAL */ - -#ifdef DEBUG_IPOCTAL -#define DPRINTF2(fmt, ...) \ - do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF2(fmt, ...) do { } while (0) -#endif - -#define DPRINTF(fmt, ...) DPRINTF2("IP-Octal: " fmt, ## __VA_ARGS__) - -#define RX_FIFO_SIZE 3 - -/* The IP-Octal has 8 channels (a-h) - divided into 4 blocks (A-D) */ -#define N_CHANNELS 8 -#define N_BLOCKS 4 - -#define REG_MRa 0x01 -#define REG_MRb 0x11 -#define REG_SRa 0x03 -#define REG_SRb 0x13 -#define REG_CSRa 0x03 -#define REG_CSRb 0x13 -#define REG_CRa 0x05 -#define REG_CRb 0x15 -#define REG_RHRa 0x07 -#define REG_RHRb 0x17 -#define REG_THRa 0x07 -#define REG_THRb 0x17 -#define REG_ACR 0x09 -#define REG_ISR 0x0B -#define REG_IMR 0x0B -#define REG_OPCR 0x1B - -#define CR_ENABLE_RX BIT(0) -#define CR_DISABLE_RX BIT(1) -#define CR_ENABLE_TX BIT(2) -#define CR_DISABLE_TX BIT(3) -#define CR_CMD(cr) ((cr) >> 4) -#define CR_NO_OP 0 -#define CR_RESET_MR 1 -#define CR_RESET_RX 2 -#define CR_RESET_TX 3 -#define CR_RESET_ERR 4 -#define CR_RESET_BRKINT 5 -#define CR_START_BRK 6 -#define CR_STOP_BRK 7 -#define CR_ASSERT_RTSN 8 -#define CR_NEGATE_RTSN 9 -#define CR_TIMEOUT_ON 10 -#define CR_TIMEOUT_OFF 12 - -#define SR_RXRDY BIT(0) -#define SR_FFULL BIT(1) -#define SR_TXRDY BIT(2) -#define SR_TXEMT BIT(3) -#define SR_OVERRUN BIT(4) -#define SR_PARITY BIT(5) -#define SR_FRAMING BIT(6) -#define SR_BREAK BIT(7) - -#define ISR_TXRDYA BIT(0) -#define ISR_RXRDYA BIT(1) -#define ISR_BREAKA BIT(2) -#define ISR_CNTRDY BIT(3) -#define ISR_TXRDYB BIT(4) -#define ISR_RXRDYB BIT(5) -#define ISR_BREAKB BIT(6) -#define ISR_MPICHG BIT(7) -#define ISR_TXRDY(CH) (((CH) & 1) ? BIT(4) : BIT(0)) -#define ISR_RXRDY(CH) (((CH) & 1) ? BIT(5) : BIT(1)) -#define ISR_BREAK(CH) (((CH) & 1) ? BIT(6) : BIT(2)) - -typedef struct IPOctalState IPOctalState; -typedef struct SCC2698Channel SCC2698Channel; -typedef struct SCC2698Block SCC2698Block; - -struct SCC2698Channel { - IPOctalState *ipoctal; - CharDriverState *dev; - bool rx_enabled; - uint8_t mr[2]; - uint8_t mr_idx; - uint8_t sr; - uint8_t rhr[RX_FIFO_SIZE]; - uint8_t rhr_idx; - uint8_t rx_pending; -}; - -struct SCC2698Block { - uint8_t imr; - uint8_t isr; -}; - -struct IPOctalState { - IPackDevice parent_obj; - - SCC2698Channel ch[N_CHANNELS]; - SCC2698Block blk[N_BLOCKS]; - uint8_t irq_vector; -}; - -#define TYPE_IPOCTAL "ipoctal232" - -#define IPOCTAL(obj) \ - OBJECT_CHECK(IPOctalState, (obj), TYPE_IPOCTAL) - -static const VMStateDescription vmstate_scc2698_channel = { - .name = "scc2698_channel", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_BOOL(rx_enabled, SCC2698Channel), - VMSTATE_UINT8_ARRAY(mr, SCC2698Channel, 2), - VMSTATE_UINT8(mr_idx, SCC2698Channel), - VMSTATE_UINT8(sr, SCC2698Channel), - VMSTATE_UINT8_ARRAY(rhr, SCC2698Channel, RX_FIFO_SIZE), - VMSTATE_UINT8(rhr_idx, SCC2698Channel), - VMSTATE_UINT8(rx_pending, SCC2698Channel), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_scc2698_block = { - .name = "scc2698_block", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(imr, SCC2698Block), - VMSTATE_UINT8(isr, SCC2698Block), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_ipoctal = { - .name = "ipoctal232", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_IPACK_DEVICE(parent_obj, IPOctalState), - VMSTATE_STRUCT_ARRAY(ch, IPOctalState, N_CHANNELS, 1, - vmstate_scc2698_channel, SCC2698Channel), - VMSTATE_STRUCT_ARRAY(blk, IPOctalState, N_BLOCKS, 1, - vmstate_scc2698_block, SCC2698Block), - VMSTATE_UINT8(irq_vector, IPOctalState), - VMSTATE_END_OF_LIST() - } -}; - -/* data[10] is 0x0C, not 0x0B as the doc says */ -static const uint8_t id_prom_data[] = { - 0x49, 0x50, 0x41, 0x43, 0xF0, 0x22, - 0xA1, 0x00, 0x00, 0x00, 0x0C, 0xCC -}; - -static void update_irq(IPOctalState *dev, unsigned block) -{ - IPackDevice *idev = IPACK_DEVICE(dev); - /* Blocks A and B interrupt on INT0#, C and D on INT1#. - Thus, to get the status we have to check two blocks. */ - SCC2698Block *blk0 = &dev->blk[block]; - SCC2698Block *blk1 = &dev->blk[block^1]; - unsigned intno = block / 2; - - if ((blk0->isr & blk0->imr) || (blk1->isr & blk1->imr)) { - qemu_irq_raise(idev->irq[intno]); - } else { - qemu_irq_lower(idev->irq[intno]); - } -} - -static void write_cr(IPOctalState *dev, unsigned channel, uint8_t val) -{ - SCC2698Channel *ch = &dev->ch[channel]; - SCC2698Block *blk = &dev->blk[channel / 2]; - - DPRINTF("Write CR%c %u: ", channel + 'a', val); - - /* The lower 4 bits are used to enable and disable Tx and Rx */ - if (val & CR_ENABLE_RX) { - DPRINTF2("Rx on, "); - ch->rx_enabled = true; - } - if (val & CR_DISABLE_RX) { - DPRINTF2("Rx off, "); - ch->rx_enabled = false; - } - if (val & CR_ENABLE_TX) { - DPRINTF2("Tx on, "); - ch->sr |= SR_TXRDY | SR_TXEMT; - blk->isr |= ISR_TXRDY(channel); - } - if (val & CR_DISABLE_TX) { - DPRINTF2("Tx off, "); - ch->sr &= ~(SR_TXRDY | SR_TXEMT); - blk->isr &= ~ISR_TXRDY(channel); - } - - DPRINTF2("cmd: "); - - /* The rest of the bits implement different commands */ - switch (CR_CMD(val)) { - case CR_NO_OP: - DPRINTF2("none"); - break; - case CR_RESET_MR: - DPRINTF2("reset MR"); - ch->mr_idx = 0; - break; - case CR_RESET_RX: - DPRINTF2("reset Rx"); - ch->rx_enabled = false; - ch->rx_pending = 0; - ch->sr &= ~SR_RXRDY; - blk->isr &= ~ISR_RXRDY(channel); - break; - case CR_RESET_TX: - DPRINTF2("reset Tx"); - ch->sr &= ~(SR_TXRDY | SR_TXEMT); - blk->isr &= ~ISR_TXRDY(channel); - break; - case CR_RESET_ERR: - DPRINTF2("reset err"); - ch->sr &= ~(SR_OVERRUN | SR_PARITY | SR_FRAMING | SR_BREAK); - break; - case CR_RESET_BRKINT: - DPRINTF2("reset brk ch int"); - blk->isr &= ~(ISR_BREAKA | ISR_BREAKB); - break; - default: - DPRINTF2("unsupported 0x%x", CR_CMD(val)); - } - - DPRINTF2("\n"); -} - -static uint16_t io_read(IPackDevice *ip, uint8_t addr) -{ - IPOctalState *dev = IPOCTAL(ip); - uint16_t ret = 0; - /* addr[7:6]: block (A-D) - addr[7:5]: channel (a-h) - addr[5:0]: register */ - unsigned block = addr >> 5; - unsigned channel = addr >> 4; - /* Big endian, accessed using 8-bit bytes at odd locations */ - unsigned offset = (addr & 0x1F) ^ 1; - SCC2698Channel *ch = &dev->ch[channel]; - SCC2698Block *blk = &dev->blk[block]; - uint8_t old_isr = blk->isr; - - switch (offset) { - - case REG_MRa: - case REG_MRb: - ret = ch->mr[ch->mr_idx]; - DPRINTF("Read MR%u%c: 0x%x\n", ch->mr_idx + 1, channel + 'a', ret); - ch->mr_idx = 1; - break; - - case REG_SRa: - case REG_SRb: - ret = ch->sr; - DPRINTF("Read SR%c: 0x%x\n", channel + 'a', ret); - break; - - case REG_RHRa: - case REG_RHRb: - ret = ch->rhr[ch->rhr_idx]; - if (ch->rx_pending > 0) { - ch->rx_pending--; - if (ch->rx_pending == 0) { - ch->sr &= ~SR_RXRDY; - blk->isr &= ~ISR_RXRDY(channel); - if (ch->dev) { - qemu_chr_accept_input(ch->dev); - } - } else { - ch->rhr_idx = (ch->rhr_idx + 1) % RX_FIFO_SIZE; - } - if (ch->sr & SR_BREAK) { - ch->sr &= ~SR_BREAK; - blk->isr |= ISR_BREAK(channel); - } - } - DPRINTF("Read RHR%c (0x%x)\n", channel + 'a', ret); - break; - - case REG_ISR: - ret = blk->isr; - DPRINTF("Read ISR%c: 0x%x\n", block + 'A', ret); - break; - - default: - DPRINTF("Read unknown/unsupported register 0x%02x\n", offset); - } - - if (old_isr != blk->isr) { - update_irq(dev, block); - } - - return ret; -} - -static void io_write(IPackDevice *ip, uint8_t addr, uint16_t val) -{ - IPOctalState *dev = IPOCTAL(ip); - unsigned reg = val & 0xFF; - /* addr[7:6]: block (A-D) - addr[7:5]: channel (a-h) - addr[5:0]: register */ - unsigned block = addr >> 5; - unsigned channel = addr >> 4; - /* Big endian, accessed using 8-bit bytes at odd locations */ - unsigned offset = (addr & 0x1F) ^ 1; - SCC2698Channel *ch = &dev->ch[channel]; - SCC2698Block *blk = &dev->blk[block]; - uint8_t old_isr = blk->isr; - uint8_t old_imr = blk->imr; - - switch (offset) { - - case REG_MRa: - case REG_MRb: - ch->mr[ch->mr_idx] = reg; - DPRINTF("Write MR%u%c 0x%x\n", ch->mr_idx + 1, channel + 'a', reg); - ch->mr_idx = 1; - break; - - /* Not implemented */ - case REG_CSRa: - case REG_CSRb: - DPRINTF("Write CSR%c: 0x%x\n", channel + 'a', reg); - break; - - case REG_CRa: - case REG_CRb: - write_cr(dev, channel, reg); - break; - - case REG_THRa: - case REG_THRb: - if (ch->sr & SR_TXRDY) { - DPRINTF("Write THR%c (0x%x)\n", channel + 'a', reg); - if (ch->dev) { - uint8_t thr = reg; - qemu_chr_fe_write(ch->dev, &thr, 1); - } - } else { - DPRINTF("Write THR%c (0x%x), Tx disabled\n", channel + 'a', reg); - } - break; - - /* Not implemented */ - case REG_ACR: - DPRINTF("Write ACR%c 0x%x\n", block + 'A', val); - break; - - case REG_IMR: - DPRINTF("Write IMR%c 0x%x\n", block + 'A', val); - blk->imr = reg; - break; - - /* Not implemented */ - case REG_OPCR: - DPRINTF("Write OPCR%c 0x%x\n", block + 'A', val); - break; - - default: - DPRINTF("Write unknown/unsupported register 0x%02x %u\n", offset, val); - } - - if (old_isr != blk->isr || old_imr != blk->imr) { - update_irq(dev, block); - } -} - -static uint16_t id_read(IPackDevice *ip, uint8_t addr) -{ - uint16_t ret = 0; - unsigned pos = addr / 2; /* The ID PROM data is stored every other byte */ - - if (pos < ARRAY_SIZE(id_prom_data)) { - ret = id_prom_data[pos]; - } else { - DPRINTF("Attempt to read unavailable PROM data at 0x%x\n", addr); - } - - return ret; -} - -static void id_write(IPackDevice *ip, uint8_t addr, uint16_t val) -{ - IPOctalState *dev = IPOCTAL(ip); - if (addr == 1) { - DPRINTF("Write IRQ vector: %u\n", (unsigned) val); - dev->irq_vector = val; /* Undocumented, but the hw works like that */ - } else { - DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr); - } -} - -static uint16_t int_read(IPackDevice *ip, uint8_t addr) -{ - IPOctalState *dev = IPOCTAL(ip); - /* Read address 0 to ACK INT0# and address 2 to ACK INT1# */ - if (addr != 0 && addr != 2) { - DPRINTF("Attempt to read from 0x%x\n", addr); - return 0; - } else { - /* Update interrupts if necessary */ - update_irq(dev, addr); - return dev->irq_vector; - } -} - -static void int_write(IPackDevice *ip, uint8_t addr, uint16_t val) -{ - DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr); -} - -static uint16_t mem_read16(IPackDevice *ip, uint32_t addr) -{ - DPRINTF("Attempt to read from 0x%x\n", addr); - return 0; -} - -static void mem_write16(IPackDevice *ip, uint32_t addr, uint16_t val) -{ - DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr); -} - -static uint8_t mem_read8(IPackDevice *ip, uint32_t addr) -{ - DPRINTF("Attempt to read from 0x%x\n", addr); - return 0; -} - -static void mem_write8(IPackDevice *ip, uint32_t addr, uint8_t val) -{ - IPOctalState *dev = IPOCTAL(ip); - if (addr == 1) { - DPRINTF("Write IRQ vector: %u\n", (unsigned) val); - dev->irq_vector = val; - } else { - DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr); - } -} - -static int hostdev_can_receive(void *opaque) -{ - SCC2698Channel *ch = opaque; - int available_bytes = RX_FIFO_SIZE - ch->rx_pending; - return ch->rx_enabled ? available_bytes : 0; -} - -static void hostdev_receive(void *opaque, const uint8_t *buf, int size) -{ - SCC2698Channel *ch = opaque; - IPOctalState *dev = ch->ipoctal; - unsigned pos = ch->rhr_idx + ch->rx_pending; - int i; - - assert(size + ch->rx_pending <= RX_FIFO_SIZE); - - /* Copy data to the RxFIFO */ - for (i = 0; i < size; i++) { - pos %= RX_FIFO_SIZE; - ch->rhr[pos++] = buf[i]; - } - - ch->rx_pending += size; - - /* If the RxFIFO was empty raise an interrupt */ - if (!(ch->sr & SR_RXRDY)) { - unsigned block, channel = 0; - /* Find channel number to update the ISR register */ - while (&dev->ch[channel] != ch) { - channel++; - } - block = channel / 2; - dev->blk[block].isr |= ISR_RXRDY(channel); - ch->sr |= SR_RXRDY; - update_irq(dev, block); - } -} - -static void hostdev_event(void *opaque, int event) -{ - SCC2698Channel *ch = opaque; - switch (event) { - case CHR_EVENT_OPENED: - DPRINTF("Device %s opened\n", ch->dev->label); - break; - case CHR_EVENT_BREAK: { - uint8_t zero = 0; - DPRINTF("Device %s received break\n", ch->dev->label); - - if (!(ch->sr & SR_BREAK)) { - IPOctalState *dev = ch->ipoctal; - unsigned block, channel = 0; - - while (&dev->ch[channel] != ch) { - channel++; - } - block = channel / 2; - - ch->sr |= SR_BREAK; - dev->blk[block].isr |= ISR_BREAK(channel); - } - - /* Put a zero character in the buffer */ - hostdev_receive(ch, &zero, 1); - } - break; - default: - DPRINTF("Device %s received event %d\n", ch->dev->label, event); - } -} - -static void ipoctal_realize(DeviceState *dev, Error **errp) -{ - IPOctalState *s = IPOCTAL(dev); - unsigned i; - - for (i = 0; i < N_CHANNELS; i++) { - SCC2698Channel *ch = &s->ch[i]; - ch->ipoctal = s; - - /* Redirect IP-Octal channels to host character devices */ - if (ch->dev) { - qemu_chr_add_handlers(ch->dev, hostdev_can_receive, - hostdev_receive, hostdev_event, ch); - DPRINTF("Redirecting channel %u to %s\n", i, ch->dev->label); - } else { - DPRINTF("Could not redirect channel %u, no chardev set\n", i); - } - } -} - -static Property ipoctal_properties[] = { - DEFINE_PROP_CHR("chardev0", IPOctalState, ch[0].dev), - DEFINE_PROP_CHR("chardev1", IPOctalState, ch[1].dev), - DEFINE_PROP_CHR("chardev2", IPOctalState, ch[2].dev), - DEFINE_PROP_CHR("chardev3", IPOctalState, ch[3].dev), - DEFINE_PROP_CHR("chardev4", IPOctalState, ch[4].dev), - DEFINE_PROP_CHR("chardev5", IPOctalState, ch[5].dev), - DEFINE_PROP_CHR("chardev6", IPOctalState, ch[6].dev), - DEFINE_PROP_CHR("chardev7", IPOctalState, ch[7].dev), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ipoctal_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - IPackDeviceClass *ic = IPACK_DEVICE_CLASS(klass); - - ic->realize = ipoctal_realize; - ic->io_read = io_read; - ic->io_write = io_write; - ic->id_read = id_read; - ic->id_write = id_write; - ic->int_read = int_read; - ic->int_write = int_write; - ic->mem_read16 = mem_read16; - ic->mem_write16 = mem_write16; - ic->mem_read8 = mem_read8; - ic->mem_write8 = mem_write8; - - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - dc->desc = "GE IP-Octal 232 8-channel RS-232 IndustryPack"; - dc->props = ipoctal_properties; - dc->vmsd = &vmstate_ipoctal; -} - -static const TypeInfo ipoctal_info = { - .name = TYPE_IPOCTAL, - .parent = TYPE_IPACK_DEVICE, - .instance_size = sizeof(IPOctalState), - .class_init = ipoctal_class_init, -}; - -static void ipoctal_register_types(void) -{ - type_register_static(&ipoctal_info); -} - -type_init(ipoctal_register_types) diff --git a/qemu/hw/char/lm32_juart.c b/qemu/hw/char/lm32_juart.c deleted file mode 100644 index 5bf8acfe8..000000000 --- a/qemu/hw/char/lm32_juart.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * LatticeMico32 JTAG UART model. - * - * Copyright (c) 2010 Michael Walle - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "sysemu/char.h" - -#include "hw/char/lm32_juart.h" - -enum { - LM32_JUART_MIN_SAVE_VERSION = 0, - LM32_JUART_CURRENT_SAVE_VERSION = 0, - LM32_JUART_MAX_SAVE_VERSION = 0, -}; - -enum { - JTX_FULL = (1<<8), -}; - -enum { - JRX_FULL = (1<<8), -}; - -#define LM32_JUART(obj) OBJECT_CHECK(LM32JuartState, (obj), TYPE_LM32_JUART) - -struct LM32JuartState { - SysBusDevice parent_obj; - - CharDriverState *chr; - - uint32_t jtx; - uint32_t jrx; -}; -typedef struct LM32JuartState LM32JuartState; - -uint32_t lm32_juart_get_jtx(DeviceState *d) -{ - LM32JuartState *s = LM32_JUART(d); - - trace_lm32_juart_get_jtx(s->jtx); - return s->jtx; -} - -uint32_t lm32_juart_get_jrx(DeviceState *d) -{ - LM32JuartState *s = LM32_JUART(d); - - trace_lm32_juart_get_jrx(s->jrx); - return s->jrx; -} - -void lm32_juart_set_jtx(DeviceState *d, uint32_t jtx) -{ - LM32JuartState *s = LM32_JUART(d); - unsigned char ch = jtx & 0xff; - - trace_lm32_juart_set_jtx(s->jtx); - - s->jtx = jtx; - if (s->chr) { - qemu_chr_fe_write_all(s->chr, &ch, 1); - } -} - -void lm32_juart_set_jrx(DeviceState *d, uint32_t jtx) -{ - LM32JuartState *s = LM32_JUART(d); - - trace_lm32_juart_set_jrx(s->jrx); - s->jrx &= ~JRX_FULL; -} - -static void juart_rx(void *opaque, const uint8_t *buf, int size) -{ - LM32JuartState *s = opaque; - - s->jrx = *buf | JRX_FULL; -} - -static int juart_can_rx(void *opaque) -{ - LM32JuartState *s = opaque; - - return !(s->jrx & JRX_FULL); -} - -static void juart_event(void *opaque, int event) -{ -} - -static void juart_reset(DeviceState *d) -{ - LM32JuartState *s = LM32_JUART(d); - - s->jtx = 0; - s->jrx = 0; -} - -static int lm32_juart_init(SysBusDevice *dev) -{ - LM32JuartState *s = LM32_JUART(dev); - - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); - if (s->chr) { - qemu_chr_add_handlers(s->chr, juart_can_rx, juart_rx, juart_event, s); - } - - return 0; -} - -static const VMStateDescription vmstate_lm32_juart = { - .name = "lm32-juart", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(jtx, LM32JuartState), - VMSTATE_UINT32(jrx, LM32JuartState), - VMSTATE_END_OF_LIST() - } -}; - -static void lm32_juart_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = lm32_juart_init; - dc->reset = juart_reset; - dc->vmsd = &vmstate_lm32_juart; - /* Reason: init() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo lm32_juart_info = { - .name = TYPE_LM32_JUART, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(LM32JuartState), - .class_init = lm32_juart_class_init, -}; - -static void lm32_juart_register_types(void) -{ - type_register_static(&lm32_juart_info); -} - -type_init(lm32_juart_register_types) diff --git a/qemu/hw/char/lm32_uart.c b/qemu/hw/char/lm32_uart.c deleted file mode 100644 index 036813d0f..000000000 --- a/qemu/hw/char/lm32_uart.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * QEMU model of the LatticeMico32 UART block. - * - * Copyright (c) 2010 Michael Walle - * - * 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 . - * - * - * Specification available at: - * http://www.latticesemi.com/documents/mico32uart.pdf - */ - - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "sysemu/char.h" -#include "qemu/error-report.h" - -enum { - R_RXTX = 0, - R_IER, - R_IIR, - R_LCR, - R_MCR, - R_LSR, - R_MSR, - R_DIV, - R_MAX -}; - -enum { - IER_RBRI = (1<<0), - IER_THRI = (1<<1), - IER_RLSI = (1<<2), - IER_MSI = (1<<3), -}; - -enum { - IIR_STAT = (1<<0), - IIR_ID0 = (1<<1), - IIR_ID1 = (1<<2), -}; - -enum { - LCR_WLS0 = (1<<0), - LCR_WLS1 = (1<<1), - LCR_STB = (1<<2), - LCR_PEN = (1<<3), - LCR_EPS = (1<<4), - LCR_SP = (1<<5), - LCR_SB = (1<<6), -}; - -enum { - MCR_DTR = (1<<0), - MCR_RTS = (1<<1), -}; - -enum { - LSR_DR = (1<<0), - LSR_OE = (1<<1), - LSR_PE = (1<<2), - LSR_FE = (1<<3), - LSR_BI = (1<<4), - LSR_THRE = (1<<5), - LSR_TEMT = (1<<6), -}; - -enum { - MSR_DCTS = (1<<0), - MSR_DDSR = (1<<1), - MSR_TERI = (1<<2), - MSR_DDCD = (1<<3), - MSR_CTS = (1<<4), - MSR_DSR = (1<<5), - MSR_RI = (1<<6), - MSR_DCD = (1<<7), -}; - -#define TYPE_LM32_UART "lm32-uart" -#define LM32_UART(obj) OBJECT_CHECK(LM32UartState, (obj), TYPE_LM32_UART) - -struct LM32UartState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - CharDriverState *chr; - qemu_irq irq; - - uint32_t regs[R_MAX]; -}; -typedef struct LM32UartState LM32UartState; - -static void uart_update_irq(LM32UartState *s) -{ - unsigned int irq; - - if ((s->regs[R_LSR] & (LSR_OE | LSR_PE | LSR_FE | LSR_BI)) - && (s->regs[R_IER] & IER_RLSI)) { - irq = 1; - s->regs[R_IIR] = IIR_ID1 | IIR_ID0; - } else if ((s->regs[R_LSR] & LSR_DR) && (s->regs[R_IER] & IER_RBRI)) { - irq = 1; - s->regs[R_IIR] = IIR_ID1; - } else if ((s->regs[R_LSR] & LSR_THRE) && (s->regs[R_IER] & IER_THRI)) { - irq = 1; - s->regs[R_IIR] = IIR_ID0; - } else if ((s->regs[R_MSR] & 0x0f) && (s->regs[R_IER] & IER_MSI)) { - irq = 1; - s->regs[R_IIR] = 0; - } else { - irq = 0; - s->regs[R_IIR] = IIR_STAT; - } - - trace_lm32_uart_irq_state(irq); - qemu_set_irq(s->irq, irq); -} - -static uint64_t uart_read(void *opaque, hwaddr addr, - unsigned size) -{ - LM32UartState *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_RXTX: - r = s->regs[R_RXTX]; - s->regs[R_LSR] &= ~LSR_DR; - uart_update_irq(s); - qemu_chr_accept_input(s->chr); - break; - case R_IIR: - case R_LSR: - case R_MSR: - r = s->regs[addr]; - break; - case R_IER: - case R_LCR: - case R_MCR: - case R_DIV: - error_report("lm32_uart: read access to write only register 0x" - TARGET_FMT_plx, addr << 2); - break; - default: - error_report("lm32_uart: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_lm32_uart_memory_read(addr << 2, r); - return r; -} - -static void uart_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - LM32UartState *s = opaque; - unsigned char ch = value; - - trace_lm32_uart_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_RXTX: - if (s->chr) { - qemu_chr_fe_write_all(s->chr, &ch, 1); - } - break; - case R_IER: - case R_LCR: - case R_MCR: - case R_DIV: - s->regs[addr] = value; - break; - case R_IIR: - case R_LSR: - case R_MSR: - error_report("lm32_uart: write access to read only register 0x" - TARGET_FMT_plx, addr << 2); - break; - default: - error_report("lm32_uart: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - uart_update_irq(s); -} - -static const MemoryRegionOps uart_ops = { - .read = uart_read, - .write = uart_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void uart_rx(void *opaque, const uint8_t *buf, int size) -{ - LM32UartState *s = opaque; - - if (s->regs[R_LSR] & LSR_DR) { - s->regs[R_LSR] |= LSR_OE; - } - - s->regs[R_LSR] |= LSR_DR; - s->regs[R_RXTX] = *buf; - - uart_update_irq(s); -} - -static int uart_can_rx(void *opaque) -{ - LM32UartState *s = opaque; - - return !(s->regs[R_LSR] & LSR_DR); -} - -static void uart_event(void *opaque, int event) -{ -} - -static void uart_reset(DeviceState *d) -{ - LM32UartState *s = LM32_UART(d); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } - - /* defaults */ - s->regs[R_LSR] = LSR_THRE | LSR_TEMT; -} - -static int lm32_uart_init(SysBusDevice *dev) -{ - LM32UartState *s = LM32_UART(dev); - - sysbus_init_irq(dev, &s->irq); - - memory_region_init_io(&s->iomem, OBJECT(s), &uart_ops, s, - "uart", R_MAX * 4); - sysbus_init_mmio(dev, &s->iomem); - - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); - if (s->chr) { - qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); - } - - return 0; -} - -static const VMStateDescription vmstate_lm32_uart = { - .name = "lm32-uart", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, LM32UartState, R_MAX), - VMSTATE_END_OF_LIST() - } -}; - -static void lm32_uart_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = lm32_uart_init; - dc->reset = uart_reset; - dc->vmsd = &vmstate_lm32_uart; - /* Reason: init() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo lm32_uart_info = { - .name = TYPE_LM32_UART, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(LM32UartState), - .class_init = lm32_uart_class_init, -}; - -static void lm32_uart_register_types(void) -{ - type_register_static(&lm32_uart_info); -} - -type_init(lm32_uart_register_types) diff --git a/qemu/hw/char/mcf_uart.c b/qemu/hw/char/mcf_uart.c deleted file mode 100644 index 3c0438fd7..000000000 --- a/qemu/hw/char/mcf_uart.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * ColdFire UART emulation. - * - * Copyright (c) 2007 CodeSourcery. - * - * This code is licensed under the GPL - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/m68k/mcf.h" -#include "sysemu/char.h" -#include "exec/address-spaces.h" - -typedef struct { - MemoryRegion iomem; - uint8_t mr[2]; - uint8_t sr; - uint8_t isr; - uint8_t imr; - uint8_t bg1; - uint8_t bg2; - uint8_t fifo[4]; - uint8_t tb; - int current_mr; - int fifo_len; - int tx_enabled; - int rx_enabled; - qemu_irq irq; - CharDriverState *chr; -} mcf_uart_state; - -/* UART Status Register bits. */ -#define MCF_UART_RxRDY 0x01 -#define MCF_UART_FFULL 0x02 -#define MCF_UART_TxRDY 0x04 -#define MCF_UART_TxEMP 0x08 -#define MCF_UART_OE 0x10 -#define MCF_UART_PE 0x20 -#define MCF_UART_FE 0x40 -#define MCF_UART_RB 0x80 - -/* Interrupt flags. */ -#define MCF_UART_TxINT 0x01 -#define MCF_UART_RxINT 0x02 -#define MCF_UART_DBINT 0x04 -#define MCF_UART_COSINT 0x80 - -/* UMR1 flags. */ -#define MCF_UART_BC0 0x01 -#define MCF_UART_BC1 0x02 -#define MCF_UART_PT 0x04 -#define MCF_UART_PM0 0x08 -#define MCF_UART_PM1 0x10 -#define MCF_UART_ERR 0x20 -#define MCF_UART_RxIRQ 0x40 -#define MCF_UART_RxRTS 0x80 - -static void mcf_uart_update(mcf_uart_state *s) -{ - s->isr &= ~(MCF_UART_TxINT | MCF_UART_RxINT); - if (s->sr & MCF_UART_TxRDY) - s->isr |= MCF_UART_TxINT; - if ((s->sr & ((s->mr[0] & MCF_UART_RxIRQ) - ? MCF_UART_FFULL : MCF_UART_RxRDY)) != 0) - s->isr |= MCF_UART_RxINT; - - qemu_set_irq(s->irq, (s->isr & s->imr) != 0); -} - -uint64_t mcf_uart_read(void *opaque, hwaddr addr, - unsigned size) -{ - mcf_uart_state *s = (mcf_uart_state *)opaque; - switch (addr & 0x3f) { - case 0x00: - return s->mr[s->current_mr]; - case 0x04: - return s->sr; - case 0x0c: - { - uint8_t val; - int i; - - if (s->fifo_len == 0) - return 0; - - val = s->fifo[0]; - s->fifo_len--; - for (i = 0; i < s->fifo_len; i++) - s->fifo[i] = s->fifo[i + 1]; - s->sr &= ~MCF_UART_FFULL; - if (s->fifo_len == 0) - s->sr &= ~MCF_UART_RxRDY; - mcf_uart_update(s); - qemu_chr_accept_input(s->chr); - return val; - } - case 0x10: - /* TODO: Implement IPCR. */ - return 0; - case 0x14: - return s->isr; - case 0x18: - return s->bg1; - case 0x1c: - return s->bg2; - default: - return 0; - } -} - -/* Update TxRDY flag and set data if present and enabled. */ -static void mcf_uart_do_tx(mcf_uart_state *s) -{ - if (s->tx_enabled && (s->sr & MCF_UART_TxEMP) == 0) { - if (s->chr) - qemu_chr_fe_write(s->chr, (unsigned char *)&s->tb, 1); - s->sr |= MCF_UART_TxEMP; - } - if (s->tx_enabled) { - s->sr |= MCF_UART_TxRDY; - } else { - s->sr &= ~MCF_UART_TxRDY; - } -} - -static void mcf_do_command(mcf_uart_state *s, uint8_t cmd) -{ - /* Misc command. */ - switch ((cmd >> 4) & 7) { - case 0: /* No-op. */ - break; - case 1: /* Reset mode register pointer. */ - s->current_mr = 0; - break; - case 2: /* Reset receiver. */ - s->rx_enabled = 0; - s->fifo_len = 0; - s->sr &= ~(MCF_UART_RxRDY | MCF_UART_FFULL); - break; - case 3: /* Reset transmitter. */ - s->tx_enabled = 0; - s->sr |= MCF_UART_TxEMP; - s->sr &= ~MCF_UART_TxRDY; - break; - case 4: /* Reset error status. */ - break; - case 5: /* Reset break-change interrupt. */ - s->isr &= ~MCF_UART_DBINT; - break; - case 6: /* Start break. */ - case 7: /* Stop break. */ - break; - } - - /* Transmitter command. */ - switch ((cmd >> 2) & 3) { - case 0: /* No-op. */ - break; - case 1: /* Enable. */ - s->tx_enabled = 1; - mcf_uart_do_tx(s); - break; - case 2: /* Disable. */ - s->tx_enabled = 0; - mcf_uart_do_tx(s); - break; - case 3: /* Reserved. */ - fprintf(stderr, "mcf_uart: Bad TX command\n"); - break; - } - - /* Receiver command. */ - switch (cmd & 3) { - case 0: /* No-op. */ - break; - case 1: /* Enable. */ - s->rx_enabled = 1; - break; - case 2: - s->rx_enabled = 0; - break; - case 3: /* Reserved. */ - fprintf(stderr, "mcf_uart: Bad RX command\n"); - break; - } -} - -void mcf_uart_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - mcf_uart_state *s = (mcf_uart_state *)opaque; - switch (addr & 0x3f) { - case 0x00: - s->mr[s->current_mr] = val; - s->current_mr = 1; - break; - case 0x04: - /* CSR is ignored. */ - break; - case 0x08: /* Command Register. */ - mcf_do_command(s, val); - break; - case 0x0c: /* Transmit Buffer. */ - s->sr &= ~MCF_UART_TxEMP; - s->tb = val; - mcf_uart_do_tx(s); - break; - case 0x10: - /* ACR is ignored. */ - break; - case 0x14: - s->imr = val; - break; - default: - break; - } - mcf_uart_update(s); -} - -static void mcf_uart_reset(mcf_uart_state *s) -{ - s->fifo_len = 0; - s->mr[0] = 0; - s->mr[1] = 0; - s->sr = MCF_UART_TxEMP; - s->tx_enabled = 0; - s->rx_enabled = 0; - s->isr = 0; - s->imr = 0; -} - -static void mcf_uart_push_byte(mcf_uart_state *s, uint8_t data) -{ - /* Break events overwrite the last byte if the fifo is full. */ - if (s->fifo_len == 4) - s->fifo_len--; - - s->fifo[s->fifo_len] = data; - s->fifo_len++; - s->sr |= MCF_UART_RxRDY; - if (s->fifo_len == 4) - s->sr |= MCF_UART_FFULL; - - mcf_uart_update(s); -} - -static void mcf_uart_event(void *opaque, int event) -{ - mcf_uart_state *s = (mcf_uart_state *)opaque; - - switch (event) { - case CHR_EVENT_BREAK: - s->isr |= MCF_UART_DBINT; - mcf_uart_push_byte(s, 0); - break; - default: - break; - } -} - -static int mcf_uart_can_receive(void *opaque) -{ - mcf_uart_state *s = (mcf_uart_state *)opaque; - - return s->rx_enabled && (s->sr & MCF_UART_FFULL) == 0; -} - -static void mcf_uart_receive(void *opaque, const uint8_t *buf, int size) -{ - mcf_uart_state *s = (mcf_uart_state *)opaque; - - mcf_uart_push_byte(s, buf[0]); -} - -void *mcf_uart_init(qemu_irq irq, CharDriverState *chr) -{ - mcf_uart_state *s; - - s = g_malloc0(sizeof(mcf_uart_state)); - s->chr = chr; - s->irq = irq; - if (chr) { - qemu_chr_fe_claim_no_fail(chr); - qemu_chr_add_handlers(chr, mcf_uart_can_receive, mcf_uart_receive, - mcf_uart_event, s); - } - mcf_uart_reset(s); - return s; -} - -static const MemoryRegionOps mcf_uart_ops = { - .read = mcf_uart_read, - .write = mcf_uart_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -void mcf_uart_mm_init(MemoryRegion *sysmem, - hwaddr base, - qemu_irq irq, - CharDriverState *chr) -{ - mcf_uart_state *s; - - s = mcf_uart_init(irq, chr); - memory_region_init_io(&s->iomem, NULL, &mcf_uart_ops, s, "uart", 0x40); - memory_region_add_subregion(sysmem, base, &s->iomem); -} diff --git a/qemu/hw/char/milkymist-uart.c b/qemu/hw/char/milkymist-uart.c deleted file mode 100644 index 03b36b223..000000000 --- a/qemu/hw/char/milkymist-uart.c +++ /dev/null @@ -1,256 +0,0 @@ -/* - * QEMU model of the Milkymist UART block. - * - * Copyright (c) 2010 Michael Walle - * - * 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 . - * - * - * Specification available at: - * http://www.milkymist.org/socdoc/uart.pdf - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "sysemu/char.h" -#include "qemu/error-report.h" - -enum { - R_RXTX = 0, - R_DIV, - R_STAT, - R_CTRL, - R_DBG, - R_MAX -}; - -enum { - STAT_THRE = (1<<0), - STAT_RX_EVT = (1<<1), - STAT_TX_EVT = (1<<2), -}; - -enum { - CTRL_RX_IRQ_EN = (1<<0), - CTRL_TX_IRQ_EN = (1<<1), - CTRL_THRU_EN = (1<<2), -}; - -enum { - DBG_BREAK_EN = (1<<0), -}; - -#define TYPE_MILKYMIST_UART "milkymist-uart" -#define MILKYMIST_UART(obj) \ - OBJECT_CHECK(MilkymistUartState, (obj), TYPE_MILKYMIST_UART) - -struct MilkymistUartState { - SysBusDevice parent_obj; - - MemoryRegion regs_region; - CharDriverState *chr; - qemu_irq irq; - - uint32_t regs[R_MAX]; -}; -typedef struct MilkymistUartState MilkymistUartState; - -static void uart_update_irq(MilkymistUartState *s) -{ - int rx_event = s->regs[R_STAT] & STAT_RX_EVT; - int tx_event = s->regs[R_STAT] & STAT_TX_EVT; - int rx_irq_en = s->regs[R_CTRL] & CTRL_RX_IRQ_EN; - int tx_irq_en = s->regs[R_CTRL] & CTRL_TX_IRQ_EN; - - if ((rx_irq_en && rx_event) || (tx_irq_en && tx_event)) { - trace_milkymist_uart_raise_irq(); - qemu_irq_raise(s->irq); - } else { - trace_milkymist_uart_lower_irq(); - qemu_irq_lower(s->irq); - } -} - -static uint64_t uart_read(void *opaque, hwaddr addr, - unsigned size) -{ - MilkymistUartState *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_RXTX: - r = s->regs[addr]; - break; - case R_DIV: - case R_STAT: - case R_CTRL: - case R_DBG: - r = s->regs[addr]; - break; - - default: - error_report("milkymist_uart: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_milkymist_uart_memory_read(addr << 2, r); - - return r; -} - -static void uart_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - MilkymistUartState *s = opaque; - unsigned char ch = value; - - trace_milkymist_uart_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_RXTX: - if (s->chr) { - qemu_chr_fe_write_all(s->chr, &ch, 1); - } - s->regs[R_STAT] |= STAT_TX_EVT; - break; - case R_DIV: - case R_CTRL: - case R_DBG: - s->regs[addr] = value; - break; - - case R_STAT: - /* write one to clear bits */ - s->regs[addr] &= ~(value & (STAT_RX_EVT | STAT_TX_EVT)); - qemu_chr_accept_input(s->chr); - break; - - default: - error_report("milkymist_uart: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - uart_update_irq(s); -} - -static const MemoryRegionOps uart_mmio_ops = { - .read = uart_read, - .write = uart_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void uart_rx(void *opaque, const uint8_t *buf, int size) -{ - MilkymistUartState *s = opaque; - - assert(!(s->regs[R_STAT] & STAT_RX_EVT)); - - s->regs[R_STAT] |= STAT_RX_EVT; - s->regs[R_RXTX] = *buf; - - uart_update_irq(s); -} - -static int uart_can_rx(void *opaque) -{ - MilkymistUartState *s = opaque; - - return !(s->regs[R_STAT] & STAT_RX_EVT); -} - -static void uart_event(void *opaque, int event) -{ -} - -static void milkymist_uart_reset(DeviceState *d) -{ - MilkymistUartState *s = MILKYMIST_UART(d); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } - - /* THRE is always set */ - s->regs[R_STAT] = STAT_THRE; -} - -static void milkymist_uart_realize(DeviceState *dev, Error **errp) -{ - MilkymistUartState *s = MILKYMIST_UART(dev); - - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); - if (s->chr) { - qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); - } -} - -static void milkymist_uart_init(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - MilkymistUartState *s = MILKYMIST_UART(obj); - - sysbus_init_irq(sbd, &s->irq); - - memory_region_init_io(&s->regs_region, OBJECT(s), &uart_mmio_ops, s, - "milkymist-uart", R_MAX * 4); - sysbus_init_mmio(sbd, &s->regs_region); -} - -static const VMStateDescription vmstate_milkymist_uart = { - .name = "milkymist-uart", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, MilkymistUartState, R_MAX), - VMSTATE_END_OF_LIST() - } -}; - -static void milkymist_uart_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = milkymist_uart_realize; - dc->reset = milkymist_uart_reset; - dc->vmsd = &vmstate_milkymist_uart; - /* Reason: realize() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo milkymist_uart_info = { - .name = TYPE_MILKYMIST_UART, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MilkymistUartState), - .instance_init = milkymist_uart_init, - .class_init = milkymist_uart_class_init, -}; - -static void milkymist_uart_register_types(void) -{ - type_register_static(&milkymist_uart_info); -} - -type_init(milkymist_uart_register_types) diff --git a/qemu/hw/char/omap_uart.c b/qemu/hw/char/omap_uart.c deleted file mode 100644 index 415bec5fa..000000000 --- a/qemu/hw/char/omap_uart.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * TI OMAP processors UART emulation. - * - * Copyright (C) 2006-2008 Andrzej Zaborowski - * Copyright (C) 2007-2009 Nokia Corporation - * - * 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 . - */ -#include "qemu/osdep.h" -#include "sysemu/char.h" -#include "hw/hw.h" -#include "hw/arm/omap.h" -#include "hw/char/serial.h" -#include "exec/address-spaces.h" - -/* UARTs */ -struct omap_uart_s { - MemoryRegion iomem; - hwaddr base; - SerialState *serial; /* TODO */ - struct omap_target_agent_s *ta; - omap_clk fclk; - qemu_irq irq; - - uint8_t eblr; - uint8_t syscontrol; - uint8_t wkup; - uint8_t cfps; - uint8_t mdr[2]; - uint8_t scr; - uint8_t clksel; -}; - -void omap_uart_reset(struct omap_uart_s *s) -{ - s->eblr = 0x00; - s->syscontrol = 0; - s->wkup = 0x3f; - s->cfps = 0x69; - s->clksel = 0; -} - -struct omap_uart_s *omap_uart_init(hwaddr base, - qemu_irq irq, omap_clk fclk, omap_clk iclk, - qemu_irq txdma, qemu_irq rxdma, - const char *label, CharDriverState *chr) -{ - struct omap_uart_s *s = g_new0(struct omap_uart_s, 1); - - s->base = base; - s->fclk = fclk; - s->irq = irq; - s->serial = serial_mm_init(get_system_memory(), base, 2, irq, - omap_clk_getrate(fclk)/16, - chr ?: qemu_chr_new(label, "null", NULL), - DEVICE_NATIVE_ENDIAN); - return s; -} - -static uint64_t omap_uart_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_uart_s *s = (struct omap_uart_s *) opaque; - - if (size == 4) { - return omap_badwidth_read8(opaque, addr); - } - - switch (addr) { - case 0x20: /* MDR1 */ - return s->mdr[0]; - case 0x24: /* MDR2 */ - return s->mdr[1]; - case 0x40: /* SCR */ - return s->scr; - case 0x44: /* SSR */ - return 0x0; - case 0x48: /* EBLR (OMAP2) */ - return s->eblr; - case 0x4C: /* OSC_12M_SEL (OMAP1) */ - return s->clksel; - case 0x50: /* MVR */ - return 0x30; - case 0x54: /* SYSC (OMAP2) */ - return s->syscontrol; - case 0x58: /* SYSS (OMAP2) */ - return 1; - case 0x5c: /* WER (OMAP2) */ - return s->wkup; - case 0x60: /* CFPS (OMAP2) */ - return s->cfps; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_uart_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_uart_s *s = (struct omap_uart_s *) opaque; - - if (size == 4) { - omap_badwidth_write8(opaque, addr, value); - return; - } - - switch (addr) { - case 0x20: /* MDR1 */ - s->mdr[0] = value & 0x7f; - break; - case 0x24: /* MDR2 */ - s->mdr[1] = value & 0xff; - break; - case 0x40: /* SCR */ - s->scr = value & 0xff; - break; - case 0x48: /* EBLR (OMAP2) */ - s->eblr = value & 0xff; - break; - case 0x4C: /* OSC_12M_SEL (OMAP1) */ - s->clksel = value & 1; - break; - case 0x44: /* SSR */ - case 0x50: /* MVR */ - case 0x58: /* SYSS (OMAP2) */ - OMAP_RO_REG(addr); - break; - case 0x54: /* SYSC (OMAP2) */ - s->syscontrol = value & 0x1d; - if (value & 2) - omap_uart_reset(s); - break; - case 0x5c: /* WER (OMAP2) */ - s->wkup = value & 0x7f; - break; - case 0x60: /* CFPS (OMAP2) */ - s->cfps = value & 0xff; - break; - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_uart_ops = { - .read = omap_uart_read, - .write = omap_uart_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem, - struct omap_target_agent_s *ta, - qemu_irq irq, omap_clk fclk, omap_clk iclk, - qemu_irq txdma, qemu_irq rxdma, - const char *label, CharDriverState *chr) -{ - hwaddr base = omap_l4_attach(ta, 0, NULL); - struct omap_uart_s *s = omap_uart_init(base, irq, - fclk, iclk, txdma, rxdma, label, chr); - - memory_region_init_io(&s->iomem, NULL, &omap_uart_ops, s, "omap.uart", 0x100); - - s->ta = ta; - - memory_region_add_subregion(sysmem, base + 0x20, &s->iomem); - - return s; -} - -void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr) -{ - /* TODO: Should reuse or destroy current s->serial */ - s->serial = serial_mm_init(get_system_memory(), s->base, 2, s->irq, - omap_clk_getrate(s->fclk) / 16, - chr ?: qemu_chr_new("null", "null", NULL), - DEVICE_NATIVE_ENDIAN); -} diff --git a/qemu/hw/char/parallel.c b/qemu/hw/char/parallel.c deleted file mode 100644 index 11c78fed8..000000000 --- a/qemu/hw/char/parallel.c +++ /dev/null @@ -1,645 +0,0 @@ -/* - * QEMU Parallel PORT emulation - * - * Copyright (c) 2003-2005 Fabrice Bellard - * Copyright (c) 2007 Marko Kohtala - * - * 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 "sysemu/char.h" -#include "hw/isa/isa.h" -#include "hw/i386/pc.h" -#include "sysemu/sysemu.h" - -//#define DEBUG_PARALLEL - -#ifdef DEBUG_PARALLEL -#define pdebug(fmt, ...) printf("pp: " fmt, ## __VA_ARGS__) -#else -#define pdebug(fmt, ...) ((void)0) -#endif - -#define PARA_REG_DATA 0 -#define PARA_REG_STS 1 -#define PARA_REG_CTR 2 -#define PARA_REG_EPP_ADDR 3 -#define PARA_REG_EPP_DATA 4 - -/* - * These are the definitions for the Printer Status Register - */ -#define PARA_STS_BUSY 0x80 /* Busy complement */ -#define PARA_STS_ACK 0x40 /* Acknowledge */ -#define PARA_STS_PAPER 0x20 /* Out of paper */ -#define PARA_STS_ONLINE 0x10 /* Online */ -#define PARA_STS_ERROR 0x08 /* Error complement */ -#define PARA_STS_TMOUT 0x01 /* EPP timeout */ - -/* - * These are the definitions for the Printer Control Register - */ -#define PARA_CTR_DIR 0x20 /* Direction (1=read, 0=write) */ -#define PARA_CTR_INTEN 0x10 /* IRQ Enable */ -#define PARA_CTR_SELECT 0x08 /* Select In complement */ -#define PARA_CTR_INIT 0x04 /* Initialize Printer complement */ -#define PARA_CTR_AUTOLF 0x02 /* Auto linefeed complement */ -#define PARA_CTR_STROBE 0x01 /* Strobe complement */ - -#define PARA_CTR_SIGNAL (PARA_CTR_SELECT|PARA_CTR_INIT|PARA_CTR_AUTOLF|PARA_CTR_STROBE) - -typedef struct ParallelState { - MemoryRegion iomem; - uint8_t dataw; - uint8_t datar; - uint8_t status; - uint8_t control; - qemu_irq irq; - int irq_pending; - CharDriverState *chr; - int hw_driver; - int epp_timeout; - uint32_t last_read_offset; /* For debugging */ - /* Memory-mapped interface */ - int it_shift; -} ParallelState; - -#define TYPE_ISA_PARALLEL "isa-parallel" -#define ISA_PARALLEL(obj) \ - OBJECT_CHECK(ISAParallelState, (obj), TYPE_ISA_PARALLEL) - -typedef struct ISAParallelState { - ISADevice parent_obj; - - uint32_t index; - uint32_t iobase; - uint32_t isairq; - ParallelState state; -} ISAParallelState; - -static void parallel_update_irq(ParallelState *s) -{ - if (s->irq_pending) - qemu_irq_raise(s->irq); - else - qemu_irq_lower(s->irq); -} - -static void -parallel_ioport_write_sw(void *opaque, uint32_t addr, uint32_t val) -{ - ParallelState *s = opaque; - - pdebug("write addr=0x%02x val=0x%02x\n", addr, val); - - addr &= 7; - switch(addr) { - case PARA_REG_DATA: - s->dataw = val; - parallel_update_irq(s); - break; - case PARA_REG_CTR: - val |= 0xc0; - if ((val & PARA_CTR_INIT) == 0 ) { - s->status = PARA_STS_BUSY; - s->status |= PARA_STS_ACK; - s->status |= PARA_STS_ONLINE; - s->status |= PARA_STS_ERROR; - } - else if (val & PARA_CTR_SELECT) { - if (val & PARA_CTR_STROBE) { - s->status &= ~PARA_STS_BUSY; - if ((s->control & PARA_CTR_STROBE) == 0) - qemu_chr_fe_write(s->chr, &s->dataw, 1); - } else { - if (s->control & PARA_CTR_INTEN) { - s->irq_pending = 1; - } - } - } - parallel_update_irq(s); - s->control = val; - break; - } -} - -static void parallel_ioport_write_hw(void *opaque, uint32_t addr, uint32_t val) -{ - ParallelState *s = opaque; - uint8_t parm = val; - int dir; - - /* Sometimes programs do several writes for timing purposes on old - HW. Take care not to waste time on writes that do nothing. */ - - s->last_read_offset = ~0U; - - addr &= 7; - switch(addr) { - case PARA_REG_DATA: - if (s->dataw == val) - return; - pdebug("wd%02x\n", val); - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_WRITE_DATA, &parm); - s->dataw = val; - break; - case PARA_REG_STS: - pdebug("ws%02x\n", val); - if (val & PARA_STS_TMOUT) - s->epp_timeout = 0; - break; - case PARA_REG_CTR: - val |= 0xc0; - if (s->control == val) - return; - pdebug("wc%02x\n", val); - - if ((val & PARA_CTR_DIR) != (s->control & PARA_CTR_DIR)) { - if (val & PARA_CTR_DIR) { - dir = 1; - } else { - dir = 0; - } - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_DATA_DIR, &dir); - parm &= ~PARA_CTR_DIR; - } - - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_WRITE_CONTROL, &parm); - s->control = val; - break; - case PARA_REG_EPP_ADDR: - if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) - /* Controls not correct for EPP address cycle, so do nothing */ - pdebug("wa%02x s\n", val); - else { - struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 }; - if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE_ADDR, &ioarg)) { - s->epp_timeout = 1; - pdebug("wa%02x t\n", val); - } - else - pdebug("wa%02x\n", val); - } - break; - case PARA_REG_EPP_DATA: - if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) - /* Controls not correct for EPP data cycle, so do nothing */ - pdebug("we%02x s\n", val); - else { - struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 }; - if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg)) { - s->epp_timeout = 1; - pdebug("we%02x t\n", val); - } - else - pdebug("we%02x\n", val); - } - break; - } -} - -static void -parallel_ioport_eppdata_write_hw2(void *opaque, uint32_t addr, uint32_t val) -{ - ParallelState *s = opaque; - uint16_t eppdata = cpu_to_le16(val); - int err; - struct ParallelIOArg ioarg = { - .buffer = &eppdata, .count = sizeof(eppdata) - }; - if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) { - /* Controls not correct for EPP data cycle, so do nothing */ - pdebug("we%04x s\n", val); - return; - } - err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg); - if (err) { - s->epp_timeout = 1; - pdebug("we%04x t\n", val); - } - else - pdebug("we%04x\n", val); -} - -static void -parallel_ioport_eppdata_write_hw4(void *opaque, uint32_t addr, uint32_t val) -{ - ParallelState *s = opaque; - uint32_t eppdata = cpu_to_le32(val); - int err; - struct ParallelIOArg ioarg = { - .buffer = &eppdata, .count = sizeof(eppdata) - }; - if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) { - /* Controls not correct for EPP data cycle, so do nothing */ - pdebug("we%08x s\n", val); - return; - } - err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg); - if (err) { - s->epp_timeout = 1; - pdebug("we%08x t\n", val); - } - else - pdebug("we%08x\n", val); -} - -static uint32_t parallel_ioport_read_sw(void *opaque, uint32_t addr) -{ - ParallelState *s = opaque; - uint32_t ret = 0xff; - - addr &= 7; - switch(addr) { - case PARA_REG_DATA: - if (s->control & PARA_CTR_DIR) - ret = s->datar; - else - ret = s->dataw; - break; - case PARA_REG_STS: - ret = s->status; - s->irq_pending = 0; - if ((s->status & PARA_STS_BUSY) == 0 && (s->control & PARA_CTR_STROBE) == 0) { - /* XXX Fixme: wait 5 microseconds */ - if (s->status & PARA_STS_ACK) - s->status &= ~PARA_STS_ACK; - else { - /* XXX Fixme: wait 5 microseconds */ - s->status |= PARA_STS_ACK; - s->status |= PARA_STS_BUSY; - } - } - parallel_update_irq(s); - break; - case PARA_REG_CTR: - ret = s->control; - break; - } - pdebug("read addr=0x%02x val=0x%02x\n", addr, ret); - return ret; -} - -static uint32_t parallel_ioport_read_hw(void *opaque, uint32_t addr) -{ - ParallelState *s = opaque; - uint8_t ret = 0xff; - addr &= 7; - switch(addr) { - case PARA_REG_DATA: - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_DATA, &ret); - if (s->last_read_offset != addr || s->datar != ret) - pdebug("rd%02x\n", ret); - s->datar = ret; - break; - case PARA_REG_STS: - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &ret); - ret &= ~PARA_STS_TMOUT; - if (s->epp_timeout) - ret |= PARA_STS_TMOUT; - if (s->last_read_offset != addr || s->status != ret) - pdebug("rs%02x\n", ret); - s->status = ret; - break; - case PARA_REG_CTR: - /* s->control has some bits fixed to 1. It is zero only when - it has not been yet written to. */ - if (s->control == 0) { - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_CONTROL, &ret); - if (s->last_read_offset != addr) - pdebug("rc%02x\n", ret); - s->control = ret; - } - else { - ret = s->control; - if (s->last_read_offset != addr) - pdebug("rc%02x\n", ret); - } - break; - case PARA_REG_EPP_ADDR: - if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) - /* Controls not correct for EPP addr cycle, so do nothing */ - pdebug("ra%02x s\n", ret); - else { - struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 }; - if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ_ADDR, &ioarg)) { - s->epp_timeout = 1; - pdebug("ra%02x t\n", ret); - } - else - pdebug("ra%02x\n", ret); - } - break; - case PARA_REG_EPP_DATA: - if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) - /* Controls not correct for EPP data cycle, so do nothing */ - pdebug("re%02x s\n", ret); - else { - struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 }; - if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg)) { - s->epp_timeout = 1; - pdebug("re%02x t\n", ret); - } - else - pdebug("re%02x\n", ret); - } - break; - } - s->last_read_offset = addr; - return ret; -} - -static uint32_t -parallel_ioport_eppdata_read_hw2(void *opaque, uint32_t addr) -{ - ParallelState *s = opaque; - uint32_t ret; - uint16_t eppdata = ~0; - int err; - struct ParallelIOArg ioarg = { - .buffer = &eppdata, .count = sizeof(eppdata) - }; - if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) { - /* Controls not correct for EPP data cycle, so do nothing */ - pdebug("re%04x s\n", eppdata); - return eppdata; - } - err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg); - ret = le16_to_cpu(eppdata); - - if (err) { - s->epp_timeout = 1; - pdebug("re%04x t\n", ret); - } - else - pdebug("re%04x\n", ret); - return ret; -} - -static uint32_t -parallel_ioport_eppdata_read_hw4(void *opaque, uint32_t addr) -{ - ParallelState *s = opaque; - uint32_t ret; - uint32_t eppdata = ~0U; - int err; - struct ParallelIOArg ioarg = { - .buffer = &eppdata, .count = sizeof(eppdata) - }; - if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) { - /* Controls not correct for EPP data cycle, so do nothing */ - pdebug("re%08x s\n", eppdata); - return eppdata; - } - err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg); - ret = le32_to_cpu(eppdata); - - if (err) { - s->epp_timeout = 1; - pdebug("re%08x t\n", ret); - } - else - pdebug("re%08x\n", ret); - return ret; -} - -static void parallel_ioport_ecp_write(void *opaque, uint32_t addr, uint32_t val) -{ - pdebug("wecp%d=%02x\n", addr & 7, val); -} - -static uint32_t parallel_ioport_ecp_read(void *opaque, uint32_t addr) -{ - uint8_t ret = 0xff; - - pdebug("recp%d:%02x\n", addr & 7, ret); - return ret; -} - -static void parallel_reset(void *opaque) -{ - ParallelState *s = opaque; - - s->datar = ~0; - s->dataw = ~0; - s->status = PARA_STS_BUSY; - s->status |= PARA_STS_ACK; - s->status |= PARA_STS_ONLINE; - s->status |= PARA_STS_ERROR; - s->status |= PARA_STS_TMOUT; - s->control = PARA_CTR_SELECT; - s->control |= PARA_CTR_INIT; - s->control |= 0xc0; - s->irq_pending = 0; - s->hw_driver = 0; - s->epp_timeout = 0; - s->last_read_offset = ~0U; -} - -static const int isa_parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc }; - -static const MemoryRegionPortio isa_parallel_portio_hw_list[] = { - { 0, 8, 1, - .read = parallel_ioport_read_hw, - .write = parallel_ioport_write_hw }, - { 4, 1, 2, - .read = parallel_ioport_eppdata_read_hw2, - .write = parallel_ioport_eppdata_write_hw2 }, - { 4, 1, 4, - .read = parallel_ioport_eppdata_read_hw4, - .write = parallel_ioport_eppdata_write_hw4 }, - { 0x400, 8, 1, - .read = parallel_ioport_ecp_read, - .write = parallel_ioport_ecp_write }, - PORTIO_END_OF_LIST(), -}; - -static const MemoryRegionPortio isa_parallel_portio_sw_list[] = { - { 0, 8, 1, - .read = parallel_ioport_read_sw, - .write = parallel_ioport_write_sw }, - PORTIO_END_OF_LIST(), -}; - - -static const VMStateDescription vmstate_parallel_isa = { - .name = "parallel_isa", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(state.dataw, ISAParallelState), - VMSTATE_UINT8(state.datar, ISAParallelState), - VMSTATE_UINT8(state.status, ISAParallelState), - VMSTATE_UINT8(state.control, ISAParallelState), - VMSTATE_INT32(state.irq_pending, ISAParallelState), - VMSTATE_INT32(state.epp_timeout, ISAParallelState), - VMSTATE_END_OF_LIST() - } -}; - - -static void parallel_isa_realizefn(DeviceState *dev, Error **errp) -{ - static int index; - ISADevice *isadev = ISA_DEVICE(dev); - ISAParallelState *isa = ISA_PARALLEL(dev); - ParallelState *s = &isa->state; - int base; - uint8_t dummy; - - if (!s->chr) { - error_setg(errp, "Can't create parallel device, empty char device"); - return; - } - - if (isa->index == -1) { - isa->index = index; - } - if (isa->index >= MAX_PARALLEL_PORTS) { - error_setg(errp, "Max. supported number of parallel ports is %d.", - MAX_PARALLEL_PORTS); - return; - } - if (isa->iobase == -1) { - isa->iobase = isa_parallel_io[isa->index]; - } - index++; - - base = isa->iobase; - isa_init_irq(isadev, &s->irq, isa->isairq); - qemu_register_reset(parallel_reset, s); - - if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) { - s->hw_driver = 1; - s->status = dummy; - } - - isa_register_portio_list(isadev, base, - (s->hw_driver - ? &isa_parallel_portio_hw_list[0] - : &isa_parallel_portio_sw_list[0]), - s, "parallel"); -} - -/* Memory mapped interface */ -static uint32_t parallel_mm_readb (void *opaque, hwaddr addr) -{ - ParallelState *s = opaque; - - return parallel_ioport_read_sw(s, addr >> s->it_shift) & 0xFF; -} - -static void parallel_mm_writeb (void *opaque, - hwaddr addr, uint32_t value) -{ - ParallelState *s = opaque; - - parallel_ioport_write_sw(s, addr >> s->it_shift, value & 0xFF); -} - -static uint32_t parallel_mm_readw (void *opaque, hwaddr addr) -{ - ParallelState *s = opaque; - - return parallel_ioport_read_sw(s, addr >> s->it_shift) & 0xFFFF; -} - -static void parallel_mm_writew (void *opaque, - hwaddr addr, uint32_t value) -{ - ParallelState *s = opaque; - - parallel_ioport_write_sw(s, addr >> s->it_shift, value & 0xFFFF); -} - -static uint32_t parallel_mm_readl (void *opaque, hwaddr addr) -{ - ParallelState *s = opaque; - - return parallel_ioport_read_sw(s, addr >> s->it_shift); -} - -static void parallel_mm_writel (void *opaque, - hwaddr addr, uint32_t value) -{ - ParallelState *s = opaque; - - parallel_ioport_write_sw(s, addr >> s->it_shift, value); -} - -static const MemoryRegionOps parallel_mm_ops = { - .old_mmio = { - .read = { parallel_mm_readb, parallel_mm_readw, parallel_mm_readl }, - .write = { parallel_mm_writeb, parallel_mm_writew, parallel_mm_writel }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* If fd is zero, it means that the parallel device uses the console */ -bool parallel_mm_init(MemoryRegion *address_space, - hwaddr base, int it_shift, qemu_irq irq, - CharDriverState *chr) -{ - ParallelState *s; - - s = g_malloc0(sizeof(ParallelState)); - s->irq = irq; - s->chr = chr; - s->it_shift = it_shift; - qemu_register_reset(parallel_reset, s); - - memory_region_init_io(&s->iomem, NULL, ¶llel_mm_ops, s, - "parallel", 8 << it_shift); - memory_region_add_subregion(address_space, base, &s->iomem); - return true; -} - -static Property parallel_isa_properties[] = { - DEFINE_PROP_UINT32("index", ISAParallelState, index, -1), - DEFINE_PROP_UINT32("iobase", ISAParallelState, iobase, -1), - DEFINE_PROP_UINT32("irq", ISAParallelState, isairq, 7), - DEFINE_PROP_CHR("chardev", ISAParallelState, state.chr), - DEFINE_PROP_END_OF_LIST(), -}; - -static void parallel_isa_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = parallel_isa_realizefn; - dc->vmsd = &vmstate_parallel_isa; - dc->props = parallel_isa_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo parallel_isa_info = { - .name = TYPE_ISA_PARALLEL, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(ISAParallelState), - .class_init = parallel_isa_class_initfn, -}; - -static void parallel_register_types(void) -{ - type_register_static(¶llel_isa_info); -} - -type_init(parallel_register_types) diff --git a/qemu/hw/char/pl011.c b/qemu/hw/char/pl011.c deleted file mode 100644 index 210c87b4c..000000000 --- a/qemu/hw/char/pl011.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Arm PrimeCell PL011 UART - * - * Copyright (c) 2006 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "sysemu/char.h" - -#define TYPE_PL011 "pl011" -#define PL011(obj) OBJECT_CHECK(PL011State, (obj), TYPE_PL011) - -typedef struct PL011State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t readbuff; - uint32_t flags; - uint32_t lcr; - uint32_t rsr; - uint32_t cr; - uint32_t dmacr; - uint32_t int_enabled; - uint32_t int_level; - uint32_t read_fifo[16]; - uint32_t ilpr; - uint32_t ibrd; - uint32_t fbrd; - uint32_t ifl; - int read_pos; - int read_count; - int read_trigger; - CharDriverState *chr; - qemu_irq irq; - const unsigned char *id; -} PL011State; - -#define PL011_INT_TX 0x20 -#define PL011_INT_RX 0x10 - -#define PL011_FLAG_TXFE 0x80 -#define PL011_FLAG_RXFF 0x40 -#define PL011_FLAG_TXFF 0x20 -#define PL011_FLAG_RXFE 0x10 - -static const unsigned char pl011_id_arm[8] = - { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; -static const unsigned char pl011_id_luminary[8] = - { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }; - -static void pl011_update(PL011State *s) -{ - uint32_t flags; - - flags = s->int_level & s->int_enabled; - qemu_set_irq(s->irq, flags != 0); -} - -static uint64_t pl011_read(void *opaque, hwaddr offset, - unsigned size) -{ - PL011State *s = (PL011State *)opaque; - uint32_t c; - - if (offset >= 0xfe0 && offset < 0x1000) { - return s->id[(offset - 0xfe0) >> 2]; - } - switch (offset >> 2) { - case 0: /* UARTDR */ - s->flags &= ~PL011_FLAG_RXFF; - c = s->read_fifo[s->read_pos]; - if (s->read_count > 0) { - s->read_count--; - if (++s->read_pos == 16) - s->read_pos = 0; - } - if (s->read_count == 0) { - s->flags |= PL011_FLAG_RXFE; - } - if (s->read_count == s->read_trigger - 1) - s->int_level &= ~ PL011_INT_RX; - s->rsr = c >> 8; - pl011_update(s); - if (s->chr) { - qemu_chr_accept_input(s->chr); - } - return c; - case 1: /* UARTRSR */ - return s->rsr; - case 6: /* UARTFR */ - return s->flags; - case 8: /* UARTILPR */ - return s->ilpr; - case 9: /* UARTIBRD */ - return s->ibrd; - case 10: /* UARTFBRD */ - return s->fbrd; - case 11: /* UARTLCR_H */ - return s->lcr; - case 12: /* UARTCR */ - return s->cr; - case 13: /* UARTIFLS */ - return s->ifl; - case 14: /* UARTIMSC */ - return s->int_enabled; - case 15: /* UARTRIS */ - return s->int_level; - case 16: /* UARTMIS */ - return s->int_level & s->int_enabled; - case 18: /* UARTDMACR */ - return s->dmacr; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pl011_read: Bad offset %x\n", (int)offset); - return 0; - } -} - -static void pl011_set_read_trigger(PL011State *s) -{ -#if 0 - /* The docs say the RX interrupt is triggered when the FIFO exceeds - the threshold. However linux only reads the FIFO in response to an - interrupt. Triggering the interrupt when the FIFO is non-empty seems - to make things work. */ - if (s->lcr & 0x10) - s->read_trigger = (s->ifl >> 1) & 0x1c; - else -#endif - s->read_trigger = 1; -} - -static void pl011_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PL011State *s = (PL011State *)opaque; - unsigned char ch; - - switch (offset >> 2) { - case 0: /* UARTDR */ - /* ??? Check if transmitter is enabled. */ - ch = value; - if (s->chr) - qemu_chr_fe_write(s->chr, &ch, 1); - s->int_level |= PL011_INT_TX; - pl011_update(s); - break; - case 1: /* UARTRSR/UARTECR */ - s->rsr = 0; - break; - case 6: /* UARTFR */ - /* Writes to Flag register are ignored. */ - break; - case 8: /* UARTUARTILPR */ - s->ilpr = value; - break; - case 9: /* UARTIBRD */ - s->ibrd = value; - break; - case 10: /* UARTFBRD */ - s->fbrd = value; - break; - case 11: /* UARTLCR_H */ - /* Reset the FIFO state on FIFO enable or disable */ - if ((s->lcr ^ value) & 0x10) { - s->read_count = 0; - s->read_pos = 0; - } - s->lcr = value; - pl011_set_read_trigger(s); - break; - case 12: /* UARTCR */ - /* ??? Need to implement the enable and loopback bits. */ - s->cr = value; - break; - case 13: /* UARTIFS */ - s->ifl = value; - pl011_set_read_trigger(s); - break; - case 14: /* UARTIMSC */ - s->int_enabled = value; - pl011_update(s); - break; - case 17: /* UARTICR */ - s->int_level &= ~value; - pl011_update(s); - break; - case 18: /* UARTDMACR */ - s->dmacr = value; - if (value & 3) { - qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n"); - } - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pl011_write: Bad offset %x\n", (int)offset); - } -} - -static int pl011_can_receive(void *opaque) -{ - PL011State *s = (PL011State *)opaque; - - if (s->lcr & 0x10) - return s->read_count < 16; - else - return s->read_count < 1; -} - -static void pl011_put_fifo(void *opaque, uint32_t value) -{ - PL011State *s = (PL011State *)opaque; - int slot; - - slot = s->read_pos + s->read_count; - if (slot >= 16) - slot -= 16; - s->read_fifo[slot] = value; - s->read_count++; - s->flags &= ~PL011_FLAG_RXFE; - if (!(s->lcr & 0x10) || s->read_count == 16) { - s->flags |= PL011_FLAG_RXFF; - } - if (s->read_count == s->read_trigger) { - s->int_level |= PL011_INT_RX; - pl011_update(s); - } -} - -static void pl011_receive(void *opaque, const uint8_t *buf, int size) -{ - pl011_put_fifo(opaque, *buf); -} - -static void pl011_event(void *opaque, int event) -{ - if (event == CHR_EVENT_BREAK) - pl011_put_fifo(opaque, 0x400); -} - -static const MemoryRegionOps pl011_ops = { - .read = pl011_read, - .write = pl011_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_pl011 = { - .name = "pl011", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32(readbuff, PL011State), - VMSTATE_UINT32(flags, PL011State), - VMSTATE_UINT32(lcr, PL011State), - VMSTATE_UINT32(rsr, PL011State), - VMSTATE_UINT32(cr, PL011State), - VMSTATE_UINT32(dmacr, PL011State), - VMSTATE_UINT32(int_enabled, PL011State), - VMSTATE_UINT32(int_level, PL011State), - VMSTATE_UINT32_ARRAY(read_fifo, PL011State, 16), - VMSTATE_UINT32(ilpr, PL011State), - VMSTATE_UINT32(ibrd, PL011State), - VMSTATE_UINT32(fbrd, PL011State), - VMSTATE_UINT32(ifl, PL011State), - VMSTATE_INT32(read_pos, PL011State), - VMSTATE_INT32(read_count, PL011State), - VMSTATE_INT32(read_trigger, PL011State), - VMSTATE_END_OF_LIST() - } -}; - -static void pl011_init(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - PL011State *s = PL011(obj); - - memory_region_init_io(&s->iomem, OBJECT(s), &pl011_ops, s, "pl011", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - - s->read_trigger = 1; - s->ifl = 0x12; - s->cr = 0x300; - s->flags = 0x90; - - s->id = pl011_id_arm; -} - -static void pl011_realize(DeviceState *dev, Error **errp) -{ - PL011State *s = PL011(dev); - - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); - - if (s->chr) { - qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive, - pl011_event, s); - } -} - -static void pl011_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = pl011_realize; - dc->vmsd = &vmstate_pl011; - /* Reason: realize() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo pl011_arm_info = { - .name = TYPE_PL011, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PL011State), - .instance_init = pl011_init, - .class_init = pl011_class_init, -}; - -static void pl011_luminary_init(Object *obj) -{ - PL011State *s = PL011(obj); - - s->id = pl011_id_luminary; -} - -static const TypeInfo pl011_luminary_info = { - .name = "pl011_luminary", - .parent = TYPE_PL011, - .instance_init = pl011_luminary_init, -}; - -static void pl011_register_types(void) -{ - type_register_static(&pl011_arm_info); - type_register_static(&pl011_luminary_info); -} - -type_init(pl011_register_types) diff --git a/qemu/hw/char/sclpconsole-lm.c b/qemu/hw/char/sclpconsole-lm.c deleted file mode 100644 index 7d4ff8120..000000000 --- a/qemu/hw/char/sclpconsole-lm.c +++ /dev/null @@ -1,384 +0,0 @@ -/* - * SCLP event types - * Operations Command - Line Mode input - * Message - Line Mode output - * - * Copyright IBM, Corp. 2013 - * - * Authors: - * Heinz Graalfs - * - * 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/qdev.h" -#include "qemu/thread.h" -#include "qemu/error-report.h" -#include "sysemu/char.h" - -#include "hw/s390x/sclp.h" -#include "hw/s390x/event-facility.h" -#include "hw/s390x/ebcdic.h" - -#define SIZE_BUFFER 4096 -#define NEWLINE "\n" - -typedef struct OprtnsCommand { - EventBufferHeader header; - MDMSU message_unit; - char data[0]; -} QEMU_PACKED OprtnsCommand; - -/* max size for line-mode data in 4K SCCB page */ -#define SIZE_CONSOLE_BUFFER (SCCB_DATA_LEN - sizeof(OprtnsCommand)) - -typedef struct SCLPConsoleLM { - SCLPEvent event; - CharDriverState *chr; - bool echo; /* immediate echo of input if true */ - uint32_t write_errors; /* errors writing to char layer */ - uint32_t length; /* length of byte stream in buffer */ - uint8_t buf[SIZE_CONSOLE_BUFFER]; -} SCLPConsoleLM; - -/* -* Character layer call-back functions - * - * Allow 1 character at a time - * - * Accumulate bytes from character layer in console buffer, - * event_pending is set when a newline character is encountered - * - * The maximum command line length is limited by the maximum - * space available in an SCCB. Line mode console input is sent - * truncated to the guest in case it doesn't fit into the SCCB. - */ - -static int chr_can_read(void *opaque) -{ - SCLPConsoleLM *scon = opaque; - - if (scon->event.event_pending) { - return 0; - } - return 1; -} - -static void chr_read(void *opaque, const uint8_t *buf, int size) -{ - SCLPConsoleLM *scon = opaque; - - assert(size == 1); - - if (*buf == '\r' || *buf == '\n') { - scon->event.event_pending = true; - sclp_service_interrupt(0); - return; - } - if (scon->length == SIZE_CONSOLE_BUFFER) { - /* Eat the character, but still process CR and LF. */ - return; - } - scon->buf[scon->length] = *buf; - scon->length += 1; - if (scon->echo) { - qemu_chr_fe_write(scon->chr, buf, size); - } -} - -/* functions to be called by event facility */ - -static bool can_handle_event(uint8_t type) -{ - return type == SCLP_EVENT_MESSAGE || type == SCLP_EVENT_PMSGCMD; -} - -static unsigned int send_mask(void) -{ - return SCLP_EVENT_MASK_OP_CMD | SCLP_EVENT_MASK_PMSGCMD; -} - -static unsigned int receive_mask(void) -{ - return SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_PMSGCMD; -} - -/* - * Triggered by SCLP's read_event_data - * - convert ASCII byte stream to EBCDIC and - * - copy converted data into provided (SCLP) buffer - */ -static int get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, - int avail) -{ - int len; - - SCLPConsoleLM *cons = DO_UPCAST(SCLPConsoleLM, event, event); - - len = cons->length; - /* data need to fit into provided SCLP buffer */ - if (len > avail) { - return 1; - } - - ebcdic_put(buf, (char *)&cons->buf, len); - *size = len; - cons->length = 0; - /* data provided and no more data pending */ - event->event_pending = false; - qemu_notify_event(); - return 0; -} - -static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, - int *slen) -{ - int avail, rc; - size_t src_len; - uint8_t *to; - OprtnsCommand *oc = (OprtnsCommand *) evt_buf_hdr; - - if (!event->event_pending) { - /* no data pending */ - return 0; - } - - to = (uint8_t *)&oc->data; - avail = *slen - sizeof(OprtnsCommand); - rc = get_console_data(event, to, &src_len, avail); - if (rc) { - /* data didn't fit, try next SCCB */ - return 1; - } - - oc->message_unit.mdmsu.gds_id = GDS_ID_MDSMU; - oc->message_unit.mdmsu.length = cpu_to_be16(sizeof(struct MDMSU)); - - oc->message_unit.cpmsu.gds_id = GDS_ID_CPMSU; - oc->message_unit.cpmsu.length = - cpu_to_be16(sizeof(struct MDMSU) - sizeof(GdsVector)); - - oc->message_unit.text_command.gds_id = GDS_ID_TEXTCMD; - oc->message_unit.text_command.length = - cpu_to_be16(sizeof(struct MDMSU) - (2 * sizeof(GdsVector))); - - oc->message_unit.self_def_text_message.key = GDS_KEY_SELFDEFTEXTMSG; - oc->message_unit.self_def_text_message.length = - cpu_to_be16(sizeof(struct MDMSU) - (3 * sizeof(GdsVector))); - - oc->message_unit.text_message.key = GDS_KEY_TEXTMSG; - oc->message_unit.text_message.length = - cpu_to_be16(sizeof(GdsSubvector) + src_len); - - oc->header.length = cpu_to_be16(sizeof(OprtnsCommand) + src_len); - oc->header.type = SCLP_EVENT_OPRTNS_COMMAND; - *slen = avail - src_len; - - return 1; -} - -/* - * Triggered by SCLP's write_event_data - * - write console data to character layer - * returns < 0 if an error occurred - */ -static int write_console_data(SCLPEvent *event, const uint8_t *buf, int len) -{ - int ret = 0; - const uint8_t *buf_offset; - - SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); - - if (!scon->chr) { - /* If there's no backend, we can just say we consumed all data. */ - return len; - } - - buf_offset = buf; - while (len > 0) { - ret = qemu_chr_fe_write(scon->chr, buf, len); - if (ret == 0) { - /* a pty doesn't seem to be connected - no error */ - len = 0; - } else if (ret == -EAGAIN || (ret > 0 && ret < len)) { - len -= ret; - buf_offset += ret; - } else { - len = 0; - } - } - - return ret; -} - -static int process_mdb(SCLPEvent *event, MDBO *mdbo) -{ - int rc; - int len; - uint8_t buffer[SIZE_BUFFER]; - - len = be16_to_cpu(mdbo->length); - len -= sizeof(mdbo->length) + sizeof(mdbo->type) - + sizeof(mdbo->mto.line_type_flags) - + sizeof(mdbo->mto.alarm_control) - + sizeof(mdbo->mto._reserved); - - assert(len <= SIZE_BUFFER); - - /* convert EBCDIC SCLP contents to ASCII console message */ - ascii_put(buffer, mdbo->mto.message, len); - rc = write_console_data(event, (uint8_t *)NEWLINE, 1); - if (rc < 0) { - return rc; - } - return write_console_data(event, buffer, len); -} - -static int write_event_data(SCLPEvent *event, EventBufferHeader *ebh) -{ - int len; - int written; - int errors = 0; - MDBO *mdbo; - SclpMsg *data = (SclpMsg *) ebh; - SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); - - len = be16_to_cpu(data->mdb.header.length); - if (len < sizeof(data->mdb.header)) { - return SCLP_RC_INCONSISTENT_LENGTHS; - } - len -= sizeof(data->mdb.header); - - /* first check message buffers */ - mdbo = data->mdb.mdbo; - while (len > 0) { - if (be16_to_cpu(mdbo->length) > len - || be16_to_cpu(mdbo->length) == 0) { - return SCLP_RC_INCONSISTENT_LENGTHS; - } - len -= be16_to_cpu(mdbo->length); - mdbo = (void *) mdbo + be16_to_cpu(mdbo->length); - } - - /* then execute */ - len = be16_to_cpu(data->mdb.header.length) - sizeof(data->mdb.header); - mdbo = data->mdb.mdbo; - while (len > 0) { - switch (be16_to_cpu(mdbo->type)) { - case MESSAGE_TEXT: - /* message text object */ - written = process_mdb(event, mdbo); - if (written < 0) { - /* character layer error */ - errors++; - } - break; - default: /* ignore */ - break; - } - len -= be16_to_cpu(mdbo->length); - mdbo = (void *) mdbo + be16_to_cpu(mdbo->length); - } - if (errors) { - scon->write_errors += errors; - } - data->header.flags = SCLP_EVENT_BUFFER_ACCEPTED; - - return SCLP_RC_NORMAL_COMPLETION; -} - -/* functions for live migration */ - -static const VMStateDescription vmstate_sclplmconsole = { - .name = "sclplmconsole", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_BOOL(event.event_pending, SCLPConsoleLM), - VMSTATE_UINT32(write_errors, SCLPConsoleLM), - VMSTATE_UINT32(length, SCLPConsoleLM), - VMSTATE_UINT8_ARRAY(buf, SCLPConsoleLM, SIZE_CONSOLE_BUFFER), - VMSTATE_END_OF_LIST() - } -}; - -/* qemu object creation and initialization functions */ - -/* tell character layer our call-back functions */ - -static int console_init(SCLPEvent *event) -{ - static bool console_available; - - SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); - - if (console_available) { - error_report("Multiple line-mode operator consoles are not supported"); - return -1; - } - console_available = true; - - if (scon->chr) { - qemu_chr_add_handlers(scon->chr, chr_can_read, chr_read, NULL, scon); - } - - return 0; -} - -static int console_exit(SCLPEvent *event) -{ - return 0; -} - -static void console_reset(DeviceState *dev) -{ - SCLPEvent *event = SCLP_EVENT(dev); - SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); - - event->event_pending = false; - scon->length = 0; - scon->write_errors = 0; -} - -static Property console_properties[] = { - DEFINE_PROP_CHR("chardev", SCLPConsoleLM, chr), - DEFINE_PROP_UINT32("write_errors", SCLPConsoleLM, write_errors, 0), - DEFINE_PROP_BOOL("echo", SCLPConsoleLM, echo, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void console_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); - - dc->props = console_properties; - dc->reset = console_reset; - dc->vmsd = &vmstate_sclplmconsole; - ec->init = console_init; - ec->exit = console_exit; - ec->get_send_mask = send_mask; - ec->get_receive_mask = receive_mask; - ec->can_handle_event = can_handle_event; - ec->read_event_data = read_event_data; - ec->write_event_data = write_event_data; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo sclp_console_info = { - .name = "sclplmconsole", - .parent = TYPE_SCLP_EVENT, - .instance_size = sizeof(SCLPConsoleLM), - .class_init = console_class_init, - .class_size = sizeof(SCLPEventClass), -}; - -static void register_types(void) -{ - type_register_static(&sclp_console_info); -} - -type_init(register_types) diff --git a/qemu/hw/char/sclpconsole.c b/qemu/hw/char/sclpconsole.c deleted file mode 100644 index 45997ff4a..000000000 --- a/qemu/hw/char/sclpconsole.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * SCLP event type - * Ascii Console Data (VT220 Console) - * - * Copyright IBM, Corp. 2012 - * - * Authors: - * Heinz Graalfs - * - * 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 -#include "qemu/thread.h" -#include "qemu/error-report.h" - -#include "hw/s390x/sclp.h" -#include "hw/s390x/event-facility.h" -#include "sysemu/char.h" - -typedef struct ASCIIConsoleData { - EventBufferHeader ebh; - char data[0]; -} QEMU_PACKED ASCIIConsoleData; - -/* max size for ASCII data in 4K SCCB page */ -#define SIZE_BUFFER_VT220 4080 - -typedef struct SCLPConsole { - SCLPEvent event; - CharDriverState *chr; - uint8_t iov[SIZE_BUFFER_VT220]; - uint32_t iov_sclp; /* offset in buf for SCLP read operation */ - uint32_t iov_bs; /* offset in buf for char layer read operation */ - uint32_t iov_data_len; /* length of byte stream in buffer */ - uint32_t iov_sclp_rest; /* length of byte stream not read via SCLP */ - bool notify; /* qemu_notify_event() req'd if true */ -} SCLPConsole; - -/* character layer call-back functions */ - -/* Return number of bytes that fit into iov buffer */ -static int chr_can_read(void *opaque) -{ - SCLPConsole *scon = opaque; - int avail = SIZE_BUFFER_VT220 - scon->iov_data_len; - - if (avail == 0) { - scon->notify = true; - } - return avail; -} - -/* Send data from a char device over to the guest */ -static void chr_read(void *opaque, const uint8_t *buf, int size) -{ - SCLPConsole *scon = opaque; - - assert(scon); - /* read data must fit into current buffer */ - assert(size <= SIZE_BUFFER_VT220 - scon->iov_data_len); - - /* put byte-stream from character layer into buffer */ - memcpy(&scon->iov[scon->iov_bs], buf, size); - scon->iov_data_len += size; - scon->iov_sclp_rest += size; - scon->iov_bs += size; - scon->event.event_pending = true; - sclp_service_interrupt(0); -} - -/* functions to be called by event facility */ - -static bool can_handle_event(uint8_t type) -{ - return type == SCLP_EVENT_ASCII_CONSOLE_DATA; -} - -static unsigned int send_mask(void) -{ - return SCLP_EVENT_MASK_MSG_ASCII; -} - -static unsigned int receive_mask(void) -{ - return SCLP_EVENT_MASK_MSG_ASCII; -} - -/* triggered by SCLP's read_event_data - - * copy console data byte-stream into provided (SCLP) buffer - */ -static void get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, - int avail) -{ - SCLPConsole *cons = DO_UPCAST(SCLPConsole, event, event); - - /* first byte is hex 0 saying an ascii string follows */ - *buf++ = '\0'; - avail--; - /* if all data fit into provided SCLP buffer */ - if (avail >= cons->iov_sclp_rest) { - /* copy character byte-stream to SCLP buffer */ - memcpy(buf, &cons->iov[cons->iov_sclp], cons->iov_sclp_rest); - *size = cons->iov_sclp_rest + 1; - cons->iov_sclp = 0; - cons->iov_bs = 0; - cons->iov_data_len = 0; - cons->iov_sclp_rest = 0; - event->event_pending = false; - /* data provided and no more data pending */ - } else { - /* if provided buffer is too small, just copy part */ - memcpy(buf, &cons->iov[cons->iov_sclp], avail); - *size = avail + 1; - cons->iov_sclp_rest -= avail; - cons->iov_sclp += avail; - /* more data pending */ - } - if (cons->notify) { - cons->notify = false; - qemu_notify_event(); - } -} - -static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, - int *slen) -{ - int avail; - size_t src_len; - uint8_t *to; - ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr; - - if (!event->event_pending) { - /* no data pending */ - return 0; - } - - to = (uint8_t *)&acd->data; - avail = *slen - sizeof(ASCIIConsoleData); - get_console_data(event, to, &src_len, avail); - - acd->ebh.length = cpu_to_be16(sizeof(ASCIIConsoleData) + src_len); - acd->ebh.type = SCLP_EVENT_ASCII_CONSOLE_DATA; - acd->ebh.flags |= SCLP_EVENT_BUFFER_ACCEPTED; - *slen = avail - src_len; - - return 1; -} - -/* triggered by SCLP's write_event_data - * - write console data to character layer - * returns < 0 if an error occurred - */ -static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf, - size_t len) -{ - SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); - - if (!scon->chr) { - /* If there's no backend, we can just say we consumed all data. */ - return len; - } - - return qemu_chr_fe_write_all(scon->chr, buf, len); -} - -static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr) -{ - int rc; - int length; - ssize_t written; - ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr; - - length = be16_to_cpu(evt_buf_hdr->length) - sizeof(EventBufferHeader); - written = write_console_data(event, (uint8_t *)acd->data, length); - - rc = SCLP_RC_NORMAL_COMPLETION; - /* set event buffer accepted flag */ - evt_buf_hdr->flags |= SCLP_EVENT_BUFFER_ACCEPTED; - - /* written will be zero if a pty is not connected - don't treat as error */ - if (written < 0) { - /* event buffer not accepted due to error in character layer */ - evt_buf_hdr->flags &= ~(SCLP_EVENT_BUFFER_ACCEPTED); - rc = SCLP_RC_CONTAINED_EQUIPMENT_CHECK; - } - - return rc; -} - -static const VMStateDescription vmstate_sclpconsole = { - .name = "sclpconsole", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_BOOL(event.event_pending, SCLPConsole), - VMSTATE_UINT8_ARRAY(iov, SCLPConsole, SIZE_BUFFER_VT220), - VMSTATE_UINT32(iov_sclp, SCLPConsole), - VMSTATE_UINT32(iov_bs, SCLPConsole), - VMSTATE_UINT32(iov_data_len, SCLPConsole), - VMSTATE_UINT32(iov_sclp_rest, SCLPConsole), - VMSTATE_END_OF_LIST() - } -}; - -/* qemu object creation and initialization functions */ - -/* tell character layer our call-back functions */ - -static int console_init(SCLPEvent *event) -{ - static bool console_available; - - SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); - - if (console_available) { - error_report("Multiple VT220 operator consoles are not supported"); - return -1; - } - console_available = true; - if (scon->chr) { - qemu_chr_add_handlers(scon->chr, chr_can_read, - chr_read, NULL, scon); - } - - return 0; -} - -static void console_reset(DeviceState *dev) -{ - SCLPEvent *event = SCLP_EVENT(dev); - SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); - - event->event_pending = false; - scon->iov_sclp = 0; - scon->iov_bs = 0; - scon->iov_data_len = 0; - scon->iov_sclp_rest = 0; - scon->notify = false; -} - -static int console_exit(SCLPEvent *event) -{ - return 0; -} - -static Property console_properties[] = { - DEFINE_PROP_CHR("chardev", SCLPConsole, chr), - DEFINE_PROP_END_OF_LIST(), -}; - -static void console_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); - - dc->props = console_properties; - dc->reset = console_reset; - dc->vmsd = &vmstate_sclpconsole; - ec->init = console_init; - ec->exit = console_exit; - ec->get_send_mask = send_mask; - ec->get_receive_mask = receive_mask; - ec->can_handle_event = can_handle_event; - ec->read_event_data = read_event_data; - ec->write_event_data = write_event_data; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo sclp_console_info = { - .name = "sclpconsole", - .parent = TYPE_SCLP_EVENT, - .instance_size = sizeof(SCLPConsole), - .class_init = console_class_init, - .class_size = sizeof(SCLPEventClass), -}; - -static void register_types(void) -{ - type_register_static(&sclp_console_info); -} - -type_init(register_types) diff --git a/qemu/hw/char/serial-isa.c b/qemu/hw/char/serial-isa.c deleted file mode 100644 index 1594ec4db..000000000 --- a/qemu/hw/char/serial-isa.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * QEMU 16550A UART emulation - * - * Copyright (c) 2003-2004 Fabrice Bellard - * Copyright (c) 2008 Citrix Systems, Inc. - * - * 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/char/serial.h" -#include "hw/isa/isa.h" - -#define ISA_SERIAL(obj) OBJECT_CHECK(ISASerialState, (obj), TYPE_ISA_SERIAL) - -typedef struct ISASerialState { - ISADevice parent_obj; - - uint32_t index; - uint32_t iobase; - uint32_t isairq; - SerialState state; -} ISASerialState; - -static const int isa_serial_io[MAX_SERIAL_PORTS] = { - 0x3f8, 0x2f8, 0x3e8, 0x2e8 -}; -static const int isa_serial_irq[MAX_SERIAL_PORTS] = { - 4, 3, 4, 3 -}; - -static void serial_isa_realizefn(DeviceState *dev, Error **errp) -{ - static int index; - ISADevice *isadev = ISA_DEVICE(dev); - ISASerialState *isa = ISA_SERIAL(dev); - SerialState *s = &isa->state; - - if (isa->index == -1) { - isa->index = index; - } - if (isa->index >= MAX_SERIAL_PORTS) { - error_setg(errp, "Max. supported number of ISA serial ports is %d.", - MAX_SERIAL_PORTS); - return; - } - if (isa->iobase == -1) { - isa->iobase = isa_serial_io[isa->index]; - } - if (isa->isairq == -1) { - isa->isairq = isa_serial_irq[isa->index]; - } - index++; - - s->baudbase = 115200; - isa_init_irq(isadev, &s->irq, isa->isairq); - serial_realize_core(s, errp); - qdev_set_legacy_instance_id(dev, isa->iobase, 3); - - memory_region_init_io(&s->io, OBJECT(isa), &serial_io_ops, s, "serial", 8); - isa_register_ioport(isadev, &s->io, isa->iobase); -} - -static const VMStateDescription vmstate_isa_serial = { - .name = "serial", - .version_id = 3, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(state, ISASerialState, 0, vmstate_serial, SerialState), - VMSTATE_END_OF_LIST() - } -}; - -static Property serial_isa_properties[] = { - DEFINE_PROP_UINT32("index", ISASerialState, index, -1), - DEFINE_PROP_UINT32("iobase", ISASerialState, iobase, -1), - DEFINE_PROP_UINT32("irq", ISASerialState, isairq, -1), - DEFINE_PROP_CHR("chardev", ISASerialState, state.chr), - DEFINE_PROP_UINT32("wakeup", ISASerialState, state.wakeup, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void serial_isa_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = serial_isa_realizefn; - dc->vmsd = &vmstate_isa_serial; - dc->props = serial_isa_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo serial_isa_info = { - .name = TYPE_ISA_SERIAL, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(ISASerialState), - .class_init = serial_isa_class_initfn, -}; - -static void serial_register_types(void) -{ - type_register_static(&serial_isa_info); -} - -type_init(serial_register_types) - -static void serial_isa_init(ISABus *bus, int index, CharDriverState *chr) -{ - DeviceState *dev; - ISADevice *isadev; - - isadev = isa_create(bus, TYPE_ISA_SERIAL); - dev = DEVICE(isadev); - qdev_prop_set_uint32(dev, "index", index); - qdev_prop_set_chr(dev, "chardev", chr); - qdev_init_nofail(dev); -} - -void serial_hds_isa_init(ISABus *bus, int n) -{ - int i; - - assert(n <= MAX_SERIAL_PORTS); - - for (i = 0; i < n; ++i) { - if (serial_hds[i]) { - serial_isa_init(bus, i, serial_hds[i]); - } - } -} diff --git a/qemu/hw/char/serial-pci.c b/qemu/hw/char/serial-pci.c deleted file mode 100644 index 303104dd1..000000000 --- a/qemu/hw/char/serial-pci.c +++ /dev/null @@ -1,276 +0,0 @@ -/* - * QEMU 16550A UART emulation - * - * Copyright (c) 2003-2004 Fabrice Bellard - * Copyright (c) 2008 Citrix Systems, Inc. - * - * 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. - */ - -/* see docs/specs/pci-serial.txt */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/char/serial.h" -#include "hw/pci/pci.h" - -#define PCI_SERIAL_MAX_PORTS 4 - -typedef struct PCISerialState { - PCIDevice dev; - SerialState state; - uint8_t prog_if; -} PCISerialState; - -typedef struct PCIMultiSerialState { - PCIDevice dev; - MemoryRegion iobar; - uint32_t ports; - char *name[PCI_SERIAL_MAX_PORTS]; - SerialState state[PCI_SERIAL_MAX_PORTS]; - uint32_t level[PCI_SERIAL_MAX_PORTS]; - qemu_irq *irqs; - uint8_t prog_if; -} PCIMultiSerialState; - -static void multi_serial_pci_exit(PCIDevice *dev); - -static void serial_pci_realize(PCIDevice *dev, Error **errp) -{ - PCISerialState *pci = DO_UPCAST(PCISerialState, dev, dev); - SerialState *s = &pci->state; - Error *err = NULL; - - s->baudbase = 115200; - serial_realize_core(s, &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - - pci->dev.config[PCI_CLASS_PROG] = pci->prog_if; - pci->dev.config[PCI_INTERRUPT_PIN] = 0x01; - s->irq = pci_allocate_irq(&pci->dev); - - memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s, "serial", 8); - pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io); -} - -static void multi_serial_irq_mux(void *opaque, int n, int level) -{ - PCIMultiSerialState *pci = opaque; - int i, pending = 0; - - pci->level[n] = level; - for (i = 0; i < pci->ports; i++) { - if (pci->level[i]) { - pending = 1; - } - } - pci_set_irq(&pci->dev, pending); -} - -static void multi_serial_pci_realize(PCIDevice *dev, Error **errp) -{ - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); - PCIMultiSerialState *pci = DO_UPCAST(PCIMultiSerialState, dev, dev); - SerialState *s; - Error *err = NULL; - int i, nr_ports = 0; - - switch (pc->device_id) { - case 0x0003: - nr_ports = 2; - break; - case 0x0004: - nr_ports = 4; - break; - } - assert(nr_ports > 0); - assert(nr_ports <= PCI_SERIAL_MAX_PORTS); - - pci->dev.config[PCI_CLASS_PROG] = pci->prog_if; - pci->dev.config[PCI_INTERRUPT_PIN] = 0x01; - memory_region_init(&pci->iobar, OBJECT(pci), "multiserial", 8 * nr_ports); - pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->iobar); - pci->irqs = qemu_allocate_irqs(multi_serial_irq_mux, pci, - nr_ports); - - for (i = 0; i < nr_ports; i++) { - s = pci->state + i; - s->baudbase = 115200; - serial_realize_core(s, &err); - if (err != NULL) { - error_propagate(errp, err); - multi_serial_pci_exit(dev); - return; - } - s->irq = pci->irqs[i]; - pci->name[i] = g_strdup_printf("uart #%d", i+1); - memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s, - pci->name[i], 8); - memory_region_add_subregion(&pci->iobar, 8 * i, &s->io); - pci->ports++; - } -} - -static void serial_pci_exit(PCIDevice *dev) -{ - PCISerialState *pci = DO_UPCAST(PCISerialState, dev, dev); - SerialState *s = &pci->state; - - serial_exit_core(s); - qemu_free_irq(s->irq); -} - -static void multi_serial_pci_exit(PCIDevice *dev) -{ - PCIMultiSerialState *pci = DO_UPCAST(PCIMultiSerialState, dev, dev); - SerialState *s; - int i; - - for (i = 0; i < pci->ports; i++) { - s = pci->state + i; - serial_exit_core(s); - memory_region_del_subregion(&pci->iobar, &s->io); - g_free(pci->name[i]); - } - qemu_free_irqs(pci->irqs, pci->ports); -} - -static const VMStateDescription vmstate_pci_serial = { - .name = "pci-serial", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, PCISerialState), - VMSTATE_STRUCT(state, PCISerialState, 0, vmstate_serial, SerialState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_pci_multi_serial = { - .name = "pci-serial-multi", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, PCIMultiSerialState), - VMSTATE_STRUCT_ARRAY(state, PCIMultiSerialState, PCI_SERIAL_MAX_PORTS, - 0, vmstate_serial, SerialState), - VMSTATE_UINT32_ARRAY(level, PCIMultiSerialState, PCI_SERIAL_MAX_PORTS), - VMSTATE_END_OF_LIST() - } -}; - -static Property serial_pci_properties[] = { - DEFINE_PROP_CHR("chardev", PCISerialState, state.chr), - DEFINE_PROP_UINT8("prog_if", PCISerialState, prog_if, 0x02), - DEFINE_PROP_END_OF_LIST(), -}; - -static Property multi_2x_serial_pci_properties[] = { - DEFINE_PROP_CHR("chardev1", PCIMultiSerialState, state[0].chr), - DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr), - DEFINE_PROP_UINT8("prog_if", PCIMultiSerialState, prog_if, 0x02), - DEFINE_PROP_END_OF_LIST(), -}; - -static Property multi_4x_serial_pci_properties[] = { - DEFINE_PROP_CHR("chardev1", PCIMultiSerialState, state[0].chr), - DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr), - DEFINE_PROP_CHR("chardev3", PCIMultiSerialState, state[2].chr), - DEFINE_PROP_CHR("chardev4", PCIMultiSerialState, state[3].chr), - DEFINE_PROP_UINT8("prog_if", PCIMultiSerialState, prog_if, 0x02), - DEFINE_PROP_END_OF_LIST(), -}; - -static void serial_pci_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); - pc->realize = serial_pci_realize; - pc->exit = serial_pci_exit; - pc->vendor_id = PCI_VENDOR_ID_REDHAT; - pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL; - pc->revision = 1; - pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL; - dc->vmsd = &vmstate_pci_serial; - dc->props = serial_pci_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); - pc->realize = multi_serial_pci_realize; - pc->exit = multi_serial_pci_exit; - pc->vendor_id = PCI_VENDOR_ID_REDHAT; - pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL2; - pc->revision = 1; - pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL; - dc->vmsd = &vmstate_pci_multi_serial; - dc->props = multi_2x_serial_pci_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static void multi_4x_serial_pci_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); - pc->realize = multi_serial_pci_realize; - pc->exit = multi_serial_pci_exit; - pc->vendor_id = PCI_VENDOR_ID_REDHAT; - pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL4; - pc->revision = 1; - pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL; - dc->vmsd = &vmstate_pci_multi_serial; - dc->props = multi_4x_serial_pci_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo serial_pci_info = { - .name = "pci-serial", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCISerialState), - .class_init = serial_pci_class_initfn, -}; - -static const TypeInfo multi_2x_serial_pci_info = { - .name = "pci-serial-2x", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIMultiSerialState), - .class_init = multi_2x_serial_pci_class_initfn, -}; - -static const TypeInfo multi_4x_serial_pci_info = { - .name = "pci-serial-4x", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIMultiSerialState), - .class_init = multi_4x_serial_pci_class_initfn, -}; - -static void serial_pci_register_types(void) -{ - type_register_static(&serial_pci_info); - type_register_static(&multi_2x_serial_pci_info); - type_register_static(&multi_4x_serial_pci_info); -} - -type_init(serial_pci_register_types) diff --git a/qemu/hw/char/serial.c b/qemu/hw/char/serial.c deleted file mode 100644 index 6d815b5c6..000000000 --- a/qemu/hw/char/serial.c +++ /dev/null @@ -1,966 +0,0 @@ -/* - * QEMU 16550A UART emulation - * - * Copyright (c) 2003-2004 Fabrice Bellard - * Copyright (c) 2008 Citrix Systems, Inc. - * - * 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/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" - -//#define DEBUG_SERIAL - -#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ - -#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */ -#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */ -#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */ -#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */ - -#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ -#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ - -#define UART_IIR_MSI 0x00 /* Modem status interrupt */ -#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ -#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ -#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ -#define UART_IIR_CTI 0x0C /* Character Timeout Indication */ - -#define UART_IIR_FENF 0x80 /* Fifo enabled, but not functionning */ -#define UART_IIR_FE 0xC0 /* Fifo enabled */ - -/* - * These are the definitions for the Modem Control Register - */ -#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ -#define UART_MCR_OUT2 0x08 /* Out2 complement */ -#define UART_MCR_OUT1 0x04 /* Out1 complement */ -#define UART_MCR_RTS 0x02 /* RTS complement */ -#define UART_MCR_DTR 0x01 /* DTR complement */ - -/* - * These are the definitions for the Modem Status Register - */ -#define UART_MSR_DCD 0x80 /* Data Carrier Detect */ -#define UART_MSR_RI 0x40 /* Ring Indicator */ -#define UART_MSR_DSR 0x20 /* Data Set Ready */ -#define UART_MSR_CTS 0x10 /* Clear to Send */ -#define UART_MSR_DDCD 0x08 /* Delta DCD */ -#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */ -#define UART_MSR_DDSR 0x02 /* Delta DSR */ -#define UART_MSR_DCTS 0x01 /* Delta CTS */ -#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */ - -#define UART_LSR_TEMT 0x40 /* Transmitter empty */ -#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ -#define UART_LSR_BI 0x10 /* Break interrupt indicator */ -#define UART_LSR_FE 0x08 /* Frame error indicator */ -#define UART_LSR_PE 0x04 /* Parity error indicator */ -#define UART_LSR_OE 0x02 /* Overrun error indicator */ -#define UART_LSR_DR 0x01 /* Receiver data ready */ -#define UART_LSR_INT_ANY 0x1E /* Any of the lsr-interrupt-triggering status bits */ - -/* Interrupt trigger levels. The byte-counts are for 16550A - in newer UARTs the byte-count for each ITL is higher. */ - -#define UART_FCR_ITL_1 0x00 /* 1 byte ITL */ -#define UART_FCR_ITL_2 0x40 /* 4 bytes ITL */ -#define UART_FCR_ITL_3 0x80 /* 8 bytes ITL */ -#define UART_FCR_ITL_4 0xC0 /* 14 bytes ITL */ - -#define UART_FCR_DMS 0x08 /* DMA Mode Select */ -#define UART_FCR_XFR 0x04 /* XMIT Fifo Reset */ -#define UART_FCR_RFR 0x02 /* RCVR Fifo Reset */ -#define UART_FCR_FE 0x01 /* FIFO Enable */ - -#define MAX_XMIT_RETRY 4 - -#ifdef DEBUG_SERIAL -#define DPRINTF(fmt, ...) \ -do { fprintf(stderr, "serial: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ -do {} while (0) -#endif - -static void serial_receive1(void *opaque, const uint8_t *buf, int size); - -static inline void recv_fifo_put(SerialState *s, uint8_t chr) -{ - /* Receive overruns do not overwrite FIFO contents. */ - if (!fifo8_is_full(&s->recv_fifo)) { - fifo8_push(&s->recv_fifo, chr); - } else { - s->lsr |= UART_LSR_OE; - } -} - -static void serial_update_irq(SerialState *s) -{ - uint8_t tmp_iir = UART_IIR_NO_INT; - - if ((s->ier & UART_IER_RLSI) && (s->lsr & UART_LSR_INT_ANY)) { - tmp_iir = UART_IIR_RLSI; - } else if ((s->ier & UART_IER_RDI) && s->timeout_ipending) { - /* Note that(s->ier & UART_IER_RDI) can mask this interrupt, - * this is not in the specification but is observed on existing - * hardware. */ - tmp_iir = UART_IIR_CTI; - } else if ((s->ier & UART_IER_RDI) && (s->lsr & UART_LSR_DR) && - (!(s->fcr & UART_FCR_FE) || - s->recv_fifo.num >= s->recv_fifo_itl)) { - tmp_iir = UART_IIR_RDI; - } else if ((s->ier & UART_IER_THRI) && s->thr_ipending) { - tmp_iir = UART_IIR_THRI; - } else if ((s->ier & UART_IER_MSI) && (s->msr & UART_MSR_ANY_DELTA)) { - tmp_iir = UART_IIR_MSI; - } - - s->iir = tmp_iir | (s->iir & 0xF0); - - if (tmp_iir != UART_IIR_NO_INT) { - qemu_irq_raise(s->irq); - } else { - qemu_irq_lower(s->irq); - } -} - -static void serial_update_parameters(SerialState *s) -{ - int speed, parity, data_bits, stop_bits, frame_size; - QEMUSerialSetParams ssp; - - if (s->divider == 0) - return; - - /* Start bit. */ - frame_size = 1; - if (s->lcr & 0x08) { - /* Parity bit. */ - frame_size++; - if (s->lcr & 0x10) - parity = 'E'; - else - parity = 'O'; - } else { - parity = 'N'; - } - if (s->lcr & 0x04) - stop_bits = 2; - else - stop_bits = 1; - - data_bits = (s->lcr & 0x03) + 5; - frame_size += data_bits + stop_bits; - speed = s->baudbase / s->divider; - ssp.speed = speed; - ssp.parity = parity; - ssp.data_bits = data_bits; - ssp.stop_bits = stop_bits; - 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", - speed, parity, data_bits, stop_bits); -} - -static void serial_update_msl(SerialState *s) -{ - uint8_t omsr; - int flags; - - timer_del(s->modem_status_poll); - - if (qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) { - s->poll_msl = -1; - return; - } - - omsr = s->msr; - - s->msr = (flags & CHR_TIOCM_CTS) ? s->msr | UART_MSR_CTS : s->msr & ~UART_MSR_CTS; - s->msr = (flags & CHR_TIOCM_DSR) ? s->msr | UART_MSR_DSR : s->msr & ~UART_MSR_DSR; - s->msr = (flags & CHR_TIOCM_CAR) ? s->msr | UART_MSR_DCD : s->msr & ~UART_MSR_DCD; - s->msr = (flags & CHR_TIOCM_RI) ? s->msr | UART_MSR_RI : s->msr & ~UART_MSR_RI; - - if (s->msr != omsr) { - /* Set delta bits */ - s->msr = s->msr | ((s->msr >> 4) ^ (omsr >> 4)); - /* UART_MSR_TERI only if change was from 1 -> 0 */ - if ((s->msr & UART_MSR_TERI) && !(omsr & UART_MSR_RI)) - s->msr &= ~UART_MSR_TERI; - serial_update_irq(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) + - NANOSECONDS_PER_SECOND / 100); - } -} - -static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque) -{ - SerialState *s = opaque; - - do { - assert(!(s->lsr & UART_LSR_TEMT)); - if (s->tsr_retry <= 0) { - assert(!(s->lsr & UART_LSR_THRE)); - - if (s->fcr & UART_FCR_FE) { - assert(!fifo8_is_empty(&s->xmit_fifo)); - s->tsr = fifo8_pop(&s->xmit_fifo); - if (!s->xmit_fifo.num) { - s->lsr |= UART_LSR_THRE; - } - } else { - s->tsr = s->thr; - s->lsr |= UART_LSR_THRE; - } - if ((s->lsr & UART_LSR_THRE) && !s->thr_ipending) { - s->thr_ipending = 1; - serial_update_irq(s); - } - } - - if (s->mcr & UART_MCR_LOOP) { - /* in loopback mode, say that we just received a char */ - serial_receive1(s, &s->tsr, 1); - } else if (qemu_chr_fe_write(s->chr, &s->tsr, 1) != 1) { - if (s->tsr_retry >= 0 && s->tsr_retry < MAX_XMIT_RETRY && - qemu_chr_fe_add_watch(s->chr, G_IO_OUT|G_IO_HUP, - serial_xmit, s) > 0) { - s->tsr_retry++; - return FALSE; - } - s->tsr_retry = 0; - } else { - s->tsr_retry = 0; - } - - /* Transmit another byte if it is already available. It is only - possible when FIFO is enabled and not empty. */ - } while (!(s->lsr & UART_LSR_THRE)); - - s->last_xmit_ts = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - s->lsr |= UART_LSR_TEMT; - - return FALSE; -} - - -/* Setter for FCR. - is_load flag means, that value is set while loading VM state - and interrupt should not be invoked */ -static void serial_write_fcr(SerialState *s, uint8_t val) -{ - /* Set fcr - val only has the bits that are supposed to "stick" */ - s->fcr = val; - - if (val & UART_FCR_FE) { - s->iir |= UART_IIR_FE; - /* Set recv_fifo trigger Level */ - switch (val & 0xC0) { - case UART_FCR_ITL_1: - s->recv_fifo_itl = 1; - break; - case UART_FCR_ITL_2: - s->recv_fifo_itl = 4; - break; - case UART_FCR_ITL_3: - s->recv_fifo_itl = 8; - break; - case UART_FCR_ITL_4: - s->recv_fifo_itl = 14; - break; - } - } else { - s->iir &= ~UART_IIR_FE; - } -} - -static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - SerialState *s = opaque; - - addr &= 7; - DPRINTF("write addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 "\n", addr, val); - switch(addr) { - default: - case 0: - if (s->lcr & UART_LCR_DLAB) { - s->divider = (s->divider & 0xff00) | val; - serial_update_parameters(s); - } else { - s->thr = (uint8_t) val; - if(s->fcr & UART_FCR_FE) { - /* xmit overruns overwrite data, so make space if needed */ - if (fifo8_is_full(&s->xmit_fifo)) { - fifo8_pop(&s->xmit_fifo); - } - fifo8_push(&s->xmit_fifo, s->thr); - } - s->thr_ipending = 0; - s->lsr &= ~UART_LSR_THRE; - s->lsr &= ~UART_LSR_TEMT; - serial_update_irq(s); - if (s->tsr_retry <= 0) { - serial_xmit(NULL, G_IO_OUT, s); - } - } - break; - case 1: - if (s->lcr & UART_LCR_DLAB) { - s->divider = (s->divider & 0x00ff) | (val << 8); - serial_update_parameters(s); - } else { - uint8_t changed = (s->ier ^ val) & 0x0f; - s->ier = val & 0x0f; - /* If the backend device is a real serial port, turn polling of the modem - * status lines on physical port on or off depending on UART_IER_MSI state. - */ - if ((changed & UART_IER_MSI) && s->poll_msl >= 0) { - if (s->ier & UART_IER_MSI) { - s->poll_msl = 1; - serial_update_msl(s); - } else { - timer_del(s->modem_status_poll); - s->poll_msl = 0; - } - } - - /* Turning on the THRE interrupt on IER can trigger the interrupt - * if LSR.THRE=1, even if it had been masked before by reading IIR. - * This is not in the datasheet, but Windows relies on it. It is - * unclear if THRE has to be resampled every time THRI becomes - * 1, or only on the rising edge. Bochs does the latter, and Windows - * always toggles IER to all zeroes and back to all ones, so do the - * same. - * - * If IER.THRI is zero, thr_ipending is not used. Set it to zero - * so that the thr_ipending subsection is not migrated. - */ - if (changed & UART_IER_THRI) { - if ((s->ier & UART_IER_THRI) && (s->lsr & UART_LSR_THRE)) { - s->thr_ipending = 1; - } else { - s->thr_ipending = 0; - } - } - - if (changed) { - serial_update_irq(s); - } - } - break; - case 2: - /* Did the enable/disable flag change? If so, make sure FIFOs get flushed */ - if ((val ^ s->fcr) & UART_FCR_FE) { - val |= UART_FCR_XFR | UART_FCR_RFR; - } - - /* FIFO clear */ - - if (val & UART_FCR_RFR) { - s->lsr &= ~(UART_LSR_DR | UART_LSR_BI); - timer_del(s->fifo_timeout_timer); - s->timeout_ipending = 0; - fifo8_reset(&s->recv_fifo); - } - - if (val & UART_FCR_XFR) { - s->lsr |= UART_LSR_THRE; - s->thr_ipending = 1; - fifo8_reset(&s->xmit_fifo); - } - - serial_write_fcr(s, val & 0xC9); - serial_update_irq(s); - break; - case 3: - { - int break_enable; - s->lcr = val; - serial_update_parameters(s); - break_enable = (val >> 6) & 1; - if (break_enable != s->last_break_enable) { - s->last_break_enable = break_enable; - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK, - &break_enable); - } - } - break; - case 4: - { - int flags; - int old_mcr = s->mcr; - s->mcr = val & 0x1f; - if (val & UART_MCR_LOOP) - break; - - if (s->poll_msl >= 0 && old_mcr != s->mcr) { - - qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags); - - flags &= ~(CHR_TIOCM_RTS | CHR_TIOCM_DTR); - - if (val & UART_MCR_RTS) - flags |= CHR_TIOCM_RTS; - if (val & UART_MCR_DTR) - flags |= CHR_TIOCM_DTR; - - qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_SET_TIOCM, &flags); - /* Update the modem status after a one-character-send wait-time, since there may be a response - from the device/computer at the other end of the serial line */ - timer_mod(s->modem_status_poll, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->char_transmit_time); - } - } - break; - case 5: - break; - case 6: - break; - case 7: - s->scr = val; - break; - } -} - -static uint64_t serial_ioport_read(void *opaque, hwaddr addr, unsigned size) -{ - SerialState *s = opaque; - uint32_t ret; - - addr &= 7; - switch(addr) { - default: - case 0: - if (s->lcr & UART_LCR_DLAB) { - ret = s->divider & 0xff; - } else { - if(s->fcr & UART_FCR_FE) { - ret = fifo8_is_empty(&s->recv_fifo) ? - 0 : fifo8_pop(&s->recv_fifo); - if (s->recv_fifo.num == 0) { - s->lsr &= ~(UART_LSR_DR | UART_LSR_BI); - } else { - timer_mod(s->fifo_timeout_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->char_transmit_time * 4); - } - s->timeout_ipending = 0; - } else { - ret = s->rbr; - s->lsr &= ~(UART_LSR_DR | UART_LSR_BI); - } - serial_update_irq(s); - if (!(s->mcr & UART_MCR_LOOP)) { - /* in loopback mode, don't receive any data */ - qemu_chr_accept_input(s->chr); - } - } - break; - case 1: - if (s->lcr & UART_LCR_DLAB) { - ret = (s->divider >> 8) & 0xff; - } else { - ret = s->ier; - } - break; - case 2: - ret = s->iir; - if ((ret & UART_IIR_ID) == UART_IIR_THRI) { - s->thr_ipending = 0; - serial_update_irq(s); - } - break; - case 3: - ret = s->lcr; - break; - case 4: - ret = s->mcr; - break; - case 5: - ret = s->lsr; - /* Clear break and overrun interrupts */ - if (s->lsr & (UART_LSR_BI|UART_LSR_OE)) { - s->lsr &= ~(UART_LSR_BI|UART_LSR_OE); - serial_update_irq(s); - } - break; - case 6: - if (s->mcr & UART_MCR_LOOP) { - /* in loopback, the modem output pins are connected to the - inputs */ - ret = (s->mcr & 0x0c) << 4; - ret |= (s->mcr & 0x02) << 3; - ret |= (s->mcr & 0x01) << 5; - } else { - if (s->poll_msl >= 0) - serial_update_msl(s); - ret = s->msr; - /* Clear delta bits & msr int after read, if they were set */ - if (s->msr & UART_MSR_ANY_DELTA) { - s->msr &= 0xF0; - serial_update_irq(s); - } - } - break; - case 7: - ret = s->scr; - break; - } - DPRINTF("read addr=0x%" HWADDR_PRIx " val=0x%02x\n", addr, ret); - return ret; -} - -static int serial_can_receive(SerialState *s) -{ - if(s->fcr & UART_FCR_FE) { - if (s->recv_fifo.num < UART_FIFO_LENGTH) { - /* - * Advertise (fifo.itl - fifo.count) bytes when count < ITL, and 1 - * if above. If UART_FIFO_LENGTH - fifo.count is advertised the - * effect will be to almost always fill the fifo completely before - * the guest has a chance to respond, effectively overriding the ITL - * that the guest has set. - */ - return (s->recv_fifo.num <= s->recv_fifo_itl) ? - s->recv_fifo_itl - s->recv_fifo.num : 1; - } else { - return 0; - } - } else { - return !(s->lsr & UART_LSR_DR); - } -} - -static void serial_receive_break(SerialState *s) -{ - s->rbr = 0; - /* When the LSR_DR is set a null byte is pushed into the fifo */ - recv_fifo_put(s, '\0'); - s->lsr |= UART_LSR_BI | UART_LSR_DR; - serial_update_irq(s); -} - -/* There's data in recv_fifo and s->rbr has not been read for 4 char transmit times */ -static void fifo_timeout_int (void *opaque) { - SerialState *s = opaque; - if (s->recv_fifo.num) { - s->timeout_ipending = 1; - serial_update_irq(s); - } -} - -static int serial_can_receive1(void *opaque) -{ - SerialState *s = opaque; - return serial_can_receive(s); -} - -static void serial_receive1(void *opaque, const uint8_t *buf, int size) -{ - SerialState *s = opaque; - - if (s->wakeup) { - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); - } - if(s->fcr & UART_FCR_FE) { - int i; - for (i = 0; i < size; i++) { - recv_fifo_put(s, buf[i]); - } - s->lsr |= UART_LSR_DR; - /* call the timeout receive callback in 4 char transmit time */ - timer_mod(s->fifo_timeout_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->char_transmit_time * 4); - } else { - if (s->lsr & UART_LSR_DR) - s->lsr |= UART_LSR_OE; - s->rbr = buf[0]; - s->lsr |= UART_LSR_DR; - } - serial_update_irq(s); -} - -static void serial_event(void *opaque, int event) -{ - SerialState *s = opaque; - DPRINTF("event %x\n", event); - if (event == CHR_EVENT_BREAK) - serial_receive_break(s); -} - -static void serial_pre_save(void *opaque) -{ - SerialState *s = opaque; - s->fcr_vmstate = s->fcr; -} - -static int serial_pre_load(void *opaque) -{ - SerialState *s = opaque; - s->thr_ipending = -1; - s->poll_msl = -1; - return 0; -} - -static int serial_post_load(void *opaque, int version_id) -{ - SerialState *s = opaque; - - if (version_id < 3) { - s->fcr_vmstate = 0; - } - if (s->thr_ipending == -1) { - s->thr_ipending = ((s->iir & UART_IIR_ID) == UART_IIR_THRI); - } - s->last_break_enable = (s->lcr >> 6) & 1; - /* Initialize fcr via setter to perform essential side-effects */ - serial_write_fcr(s, s->fcr_vmstate); - serial_update_parameters(s); - return 0; -} - -static bool serial_thr_ipending_needed(void *opaque) -{ - SerialState *s = opaque; - - if (s->ier & UART_IER_THRI) { - bool expected_value = ((s->iir & UART_IIR_ID) == UART_IIR_THRI); - return s->thr_ipending != expected_value; - } else { - /* LSR.THRE will be sampled again when the interrupt is - * enabled. thr_ipending is not used in this case, do - * not migrate it. - */ - return false; - } -} - -static const VMStateDescription vmstate_serial_thr_ipending = { - .name = "serial/thr_ipending", - .version_id = 1, - .minimum_version_id = 1, - .needed = serial_thr_ipending_needed, - .fields = (VMStateField[]) { - VMSTATE_INT32(thr_ipending, SerialState), - VMSTATE_END_OF_LIST() - } -}; - -static bool serial_tsr_needed(void *opaque) -{ - SerialState *s = (SerialState *)opaque; - return s->tsr_retry != 0; -} - -static const VMStateDescription vmstate_serial_tsr = { - .name = "serial/tsr", - .version_id = 1, - .minimum_version_id = 1, - .needed = serial_tsr_needed, - .fields = (VMStateField[]) { - VMSTATE_INT32(tsr_retry, SerialState), - VMSTATE_UINT8(thr, SerialState), - VMSTATE_UINT8(tsr, SerialState), - VMSTATE_END_OF_LIST() - } -}; - -static bool serial_recv_fifo_needed(void *opaque) -{ - SerialState *s = (SerialState *)opaque; - return !fifo8_is_empty(&s->recv_fifo); - -} - -static const VMStateDescription vmstate_serial_recv_fifo = { - .name = "serial/recv_fifo", - .version_id = 1, - .minimum_version_id = 1, - .needed = serial_recv_fifo_needed, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(recv_fifo, SerialState, 1, vmstate_fifo8, Fifo8), - VMSTATE_END_OF_LIST() - } -}; - -static bool serial_xmit_fifo_needed(void *opaque) -{ - SerialState *s = (SerialState *)opaque; - return !fifo8_is_empty(&s->xmit_fifo); -} - -static const VMStateDescription vmstate_serial_xmit_fifo = { - .name = "serial/xmit_fifo", - .version_id = 1, - .minimum_version_id = 1, - .needed = serial_xmit_fifo_needed, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(xmit_fifo, SerialState, 1, vmstate_fifo8, Fifo8), - VMSTATE_END_OF_LIST() - } -}; - -static bool serial_fifo_timeout_timer_needed(void *opaque) -{ - SerialState *s = (SerialState *)opaque; - return timer_pending(s->fifo_timeout_timer); -} - -static const VMStateDescription vmstate_serial_fifo_timeout_timer = { - .name = "serial/fifo_timeout_timer", - .version_id = 1, - .minimum_version_id = 1, - .needed = serial_fifo_timeout_timer_needed, - .fields = (VMStateField[]) { - VMSTATE_TIMER_PTR(fifo_timeout_timer, SerialState), - VMSTATE_END_OF_LIST() - } -}; - -static bool serial_timeout_ipending_needed(void *opaque) -{ - SerialState *s = (SerialState *)opaque; - return s->timeout_ipending != 0; -} - -static const VMStateDescription vmstate_serial_timeout_ipending = { - .name = "serial/timeout_ipending", - .version_id = 1, - .minimum_version_id = 1, - .needed = serial_timeout_ipending_needed, - .fields = (VMStateField[]) { - VMSTATE_INT32(timeout_ipending, SerialState), - VMSTATE_END_OF_LIST() - } -}; - -static bool serial_poll_needed(void *opaque) -{ - SerialState *s = (SerialState *)opaque; - return s->poll_msl >= 0; -} - -static const VMStateDescription vmstate_serial_poll = { - .name = "serial/poll", - .version_id = 1, - .needed = serial_poll_needed, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32(poll_msl, SerialState), - VMSTATE_TIMER_PTR(modem_status_poll, SerialState), - VMSTATE_END_OF_LIST() - } -}; - -const VMStateDescription vmstate_serial = { - .name = "serial", - .version_id = 3, - .minimum_version_id = 2, - .pre_save = serial_pre_save, - .pre_load = serial_pre_load, - .post_load = serial_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT16_V(divider, SerialState, 2), - VMSTATE_UINT8(rbr, SerialState), - VMSTATE_UINT8(ier, SerialState), - VMSTATE_UINT8(iir, SerialState), - VMSTATE_UINT8(lcr, SerialState), - VMSTATE_UINT8(mcr, SerialState), - VMSTATE_UINT8(lsr, SerialState), - VMSTATE_UINT8(msr, SerialState), - VMSTATE_UINT8(scr, SerialState), - VMSTATE_UINT8_V(fcr_vmstate, SerialState, 3), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_serial_thr_ipending, - &vmstate_serial_tsr, - &vmstate_serial_recv_fifo, - &vmstate_serial_xmit_fifo, - &vmstate_serial_fifo_timeout_timer, - &vmstate_serial_timeout_ipending, - &vmstate_serial_poll, - NULL - } -}; - -static void serial_reset(void *opaque) -{ - SerialState *s = opaque; - - s->rbr = 0; - s->ier = 0; - s->iir = UART_IIR_NO_INT; - s->lcr = 0; - s->lsr = UART_LSR_TEMT | UART_LSR_THRE; - s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS; - /* Default to 9600 baud, 1 start bit, 8 data bits, 1 stop bit, no parity. */ - s->divider = 0x0C; - s->mcr = UART_MCR_OUT2; - s->scr = 0; - s->tsr_retry = 0; - s->char_transmit_time = (NANOSECONDS_PER_SECOND / 9600) * 10; - s->poll_msl = 0; - - s->timeout_ipending = 0; - timer_del(s->fifo_timeout_timer); - timer_del(s->modem_status_poll); - - fifo8_reset(&s->recv_fifo); - fifo8_reset(&s->xmit_fifo); - - s->last_xmit_ts = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - s->thr_ipending = 0; - s->last_break_enable = 0; - qemu_irq_lower(s->irq); - - serial_update_msl(s); - s->msr &= ~UART_MSR_ANY_DELTA; -} - -void serial_realize_core(SerialState *s, Error **errp) -{ - if (!s->chr) { - error_setg(errp, "Can't create serial device, empty char device"); - return; - } - - s->modem_status_poll = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) serial_update_msl, s); - - s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) fifo_timeout_int, s); - qemu_register_reset(serial_reset, s); - - qemu_chr_add_handlers(s->chr, serial_can_receive1, serial_receive1, - serial_event, s); - fifo8_create(&s->recv_fifo, UART_FIFO_LENGTH); - fifo8_create(&s->xmit_fifo, UART_FIFO_LENGTH); - serial_reset(s); -} - -void serial_exit_core(SerialState *s) -{ - qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL); - qemu_unregister_reset(serial_reset, s); -} - -/* Change the main reference oscillator frequency. */ -void serial_set_frequency(SerialState *s, uint32_t frequency) -{ - s->baudbase = frequency; - serial_update_parameters(s); -} - -const MemoryRegionOps serial_io_ops = { - .read = serial_ioport_read, - .write = serial_ioport_write, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -SerialState *serial_init(int base, qemu_irq irq, int baudbase, - CharDriverState *chr, MemoryRegion *system_io) -{ - SerialState *s; - - s = g_malloc0(sizeof(SerialState)); - - s->irq = irq; - s->baudbase = baudbase; - s->chr = chr; - serial_realize_core(s, &error_fatal); - - vmstate_register(NULL, base, &vmstate_serial, s); - - memory_region_init_io(&s->io, NULL, &serial_io_ops, s, "serial", 8); - memory_region_add_subregion(system_io, base, &s->io); - - return s; -} - -/* Memory mapped interface */ -static uint64_t serial_mm_read(void *opaque, hwaddr addr, - unsigned size) -{ - SerialState *s = opaque; - return serial_ioport_read(s, addr >> s->it_shift, 1); -} - -static void serial_mm_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - SerialState *s = opaque; - value &= ~0u >> (32 - (size * 8)); - serial_ioport_write(s, addr >> s->it_shift, value, 1); -} - -static const MemoryRegionOps serial_mm_ops[3] = { - [DEVICE_NATIVE_ENDIAN] = { - .read = serial_mm_read, - .write = serial_mm_write, - .endianness = DEVICE_NATIVE_ENDIAN, - }, - [DEVICE_LITTLE_ENDIAN] = { - .read = serial_mm_read, - .write = serial_mm_write, - .endianness = DEVICE_LITTLE_ENDIAN, - }, - [DEVICE_BIG_ENDIAN] = { - .read = serial_mm_read, - .write = serial_mm_write, - .endianness = DEVICE_BIG_ENDIAN, - }, -}; - -SerialState *serial_mm_init(MemoryRegion *address_space, - hwaddr base, int it_shift, - qemu_irq irq, int baudbase, - CharDriverState *chr, enum device_endian end) -{ - SerialState *s; - - s = g_malloc0(sizeof(SerialState)); - - s->it_shift = it_shift; - s->irq = irq; - s->baudbase = baudbase; - s->chr = chr; - - 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, - "serial", 8 << it_shift); - memory_region_add_subregion(address_space, base, &s->io); - return s; -} diff --git a/qemu/hw/char/sh_serial.c b/qemu/hw/char/sh_serial.c deleted file mode 100644 index 4c55dcb7d..000000000 --- a/qemu/hw/char/sh_serial.c +++ /dev/null @@ -1,409 +0,0 @@ -/* - * QEMU SCI/SCIF serial port emulation - * - * Copyright (c) 2007 Magnus Damm - * - * Based on serial.c - QEMU 16450 UART emulation - * Copyright (c) 2003-2004 Fabrice Bellard - * - * 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/sh4/sh.h" -#include "sysemu/char.h" -#include "exec/address-spaces.h" - -//#define DEBUG_SERIAL - -#define SH_SERIAL_FLAG_TEND (1 << 0) -#define SH_SERIAL_FLAG_TDE (1 << 1) -#define SH_SERIAL_FLAG_RDF (1 << 2) -#define SH_SERIAL_FLAG_BRK (1 << 3) -#define SH_SERIAL_FLAG_DR (1 << 4) - -#define SH_RX_FIFO_LENGTH (16) - -typedef struct { - MemoryRegion iomem; - MemoryRegion iomem_p4; - MemoryRegion iomem_a7; - uint8_t smr; - uint8_t brr; - uint8_t scr; - uint8_t dr; /* ftdr / tdr */ - uint8_t sr; /* fsr / ssr */ - uint16_t fcr; - uint8_t sptr; - - uint8_t rx_fifo[SH_RX_FIFO_LENGTH]; /* frdr / rdr */ - uint8_t rx_cnt; - uint8_t rx_tail; - uint8_t rx_head; - - int freq; - int feat; - int flags; - int rtrg; - - CharDriverState *chr; - - qemu_irq eri; - qemu_irq rxi; - qemu_irq txi; - qemu_irq tei; - qemu_irq bri; -} sh_serial_state; - -static void sh_serial_clear_fifo(sh_serial_state * s) -{ - memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH); - s->rx_cnt = 0; - s->rx_head = 0; - s->rx_tail = 0; -} - -static void sh_serial_write(void *opaque, hwaddr offs, - uint64_t val, unsigned size) -{ - sh_serial_state *s = opaque; - unsigned char ch; - -#ifdef DEBUG_SERIAL - printf("sh_serial: write offs=0x%02x val=0x%02x\n", - offs, val); -#endif - switch(offs) { - case 0x00: /* SMR */ - s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff); - return; - case 0x04: /* BRR */ - s->brr = val; - return; - case 0x08: /* SCR */ - /* TODO : For SH7751, SCIF mask should be 0xfb. */ - s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff); - if (!(val & (1 << 5))) - s->flags |= SH_SERIAL_FLAG_TEND; - if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) { - qemu_set_irq(s->txi, val & (1 << 7)); - } - if (!(val & (1 << 6))) { - qemu_set_irq(s->rxi, 0); - } - return; - case 0x0c: /* FTDR / TDR */ - if (s->chr) { - ch = val; - qemu_chr_fe_write(s->chr, &ch, 1); - } - s->dr = val; - s->flags &= ~SH_SERIAL_FLAG_TDE; - return; -#if 0 - case 0x14: /* FRDR / RDR */ - ret = 0; - break; -#endif - } - if (s->feat & SH_SERIAL_FEAT_SCIF) { - switch(offs) { - case 0x10: /* FSR */ - if (!(val & (1 << 6))) - s->flags &= ~SH_SERIAL_FLAG_TEND; - if (!(val & (1 << 5))) - s->flags &= ~SH_SERIAL_FLAG_TDE; - if (!(val & (1 << 4))) - s->flags &= ~SH_SERIAL_FLAG_BRK; - if (!(val & (1 << 1))) - s->flags &= ~SH_SERIAL_FLAG_RDF; - if (!(val & (1 << 0))) - s->flags &= ~SH_SERIAL_FLAG_DR; - - if (!(val & (1 << 1)) || !(val & (1 << 0))) { - if (s->rxi) { - qemu_set_irq(s->rxi, 0); - } - } - return; - case 0x18: /* FCR */ - s->fcr = val; - switch ((val >> 6) & 3) { - case 0: - s->rtrg = 1; - break; - case 1: - s->rtrg = 4; - break; - case 2: - s->rtrg = 8; - break; - case 3: - s->rtrg = 14; - break; - } - if (val & (1 << 1)) { - sh_serial_clear_fifo(s); - s->sr &= ~(1 << 1); - } - - return; - case 0x20: /* SPTR */ - s->sptr = val & 0xf3; - return; - case 0x24: /* LSR */ - return; - } - } - else { - switch(offs) { -#if 0 - case 0x0c: - ret = s->dr; - break; - case 0x10: - ret = 0; - break; -#endif - case 0x1c: - s->sptr = val & 0x8f; - return; - } - } - - fprintf(stderr, "sh_serial: unsupported write to 0x%02" - HWADDR_PRIx "\n", offs); - abort(); -} - -static uint64_t sh_serial_read(void *opaque, hwaddr offs, - unsigned size) -{ - sh_serial_state *s = opaque; - uint32_t ret = ~0; - -#if 0 - switch(offs) { - case 0x00: - ret = s->smr; - break; - case 0x04: - ret = s->brr; - break; - case 0x08: - ret = s->scr; - break; - case 0x14: - ret = 0; - break; - } -#endif - if (s->feat & SH_SERIAL_FEAT_SCIF) { - switch(offs) { - case 0x00: /* SMR */ - ret = s->smr; - break; - case 0x08: /* SCR */ - ret = s->scr; - break; - case 0x10: /* FSR */ - ret = 0; - if (s->flags & SH_SERIAL_FLAG_TEND) - ret |= (1 << 6); - if (s->flags & SH_SERIAL_FLAG_TDE) - ret |= (1 << 5); - if (s->flags & SH_SERIAL_FLAG_BRK) - ret |= (1 << 4); - if (s->flags & SH_SERIAL_FLAG_RDF) - ret |= (1 << 1); - if (s->flags & SH_SERIAL_FLAG_DR) - ret |= (1 << 0); - - if (s->scr & (1 << 5)) - s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND; - - break; - case 0x14: - if (s->rx_cnt > 0) { - ret = s->rx_fifo[s->rx_tail++]; - s->rx_cnt--; - if (s->rx_tail == SH_RX_FIFO_LENGTH) - s->rx_tail = 0; - if (s->rx_cnt < s->rtrg) - s->flags &= ~SH_SERIAL_FLAG_RDF; - } - break; - case 0x18: - ret = s->fcr; - break; - case 0x1c: - ret = s->rx_cnt; - break; - case 0x20: - ret = s->sptr; - break; - case 0x24: - ret = 0; - break; - } - } - else { - switch(offs) { -#if 0 - case 0x0c: - ret = s->dr; - break; - case 0x10: - ret = 0; - break; - case 0x14: - ret = s->rx_fifo[0]; - break; -#endif - case 0x1c: - ret = s->sptr; - break; - } - } -#ifdef DEBUG_SERIAL - printf("sh_serial: read offs=0x%02x val=0x%x\n", - offs, ret); -#endif - - if (ret & ~((1 << 16) - 1)) { - fprintf(stderr, "sh_serial: unsupported read from 0x%02" - HWADDR_PRIx "\n", offs); - abort(); - } - - return ret; -} - -static int sh_serial_can_receive(sh_serial_state *s) -{ - return s->scr & (1 << 4); -} - -static void sh_serial_receive_break(sh_serial_state *s) -{ - if (s->feat & SH_SERIAL_FEAT_SCIF) - s->sr |= (1 << 4); -} - -static int sh_serial_can_receive1(void *opaque) -{ - sh_serial_state *s = opaque; - return sh_serial_can_receive(s); -} - -static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size) -{ - sh_serial_state *s = opaque; - - if (s->feat & SH_SERIAL_FEAT_SCIF) { - int i; - for (i = 0; i < size; i++) { - if (s->rx_cnt < SH_RX_FIFO_LENGTH) { - s->rx_fifo[s->rx_head++] = buf[i]; - if (s->rx_head == SH_RX_FIFO_LENGTH) { - s->rx_head = 0; - } - s->rx_cnt++; - if (s->rx_cnt >= s->rtrg) { - s->flags |= SH_SERIAL_FLAG_RDF; - if (s->scr & (1 << 6) && s->rxi) { - qemu_set_irq(s->rxi, 1); - } - } - } - } - } else { - s->rx_fifo[0] = buf[0]; - } -} - -static void sh_serial_event(void *opaque, int event) -{ - sh_serial_state *s = opaque; - if (event == CHR_EVENT_BREAK) - sh_serial_receive_break(s); -} - -static const MemoryRegionOps sh_serial_ops = { - .read = sh_serial_read, - .write = sh_serial_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -void sh_serial_init(MemoryRegion *sysmem, - hwaddr base, int feat, - uint32_t freq, CharDriverState *chr, - qemu_irq eri_source, - qemu_irq rxi_source, - qemu_irq txi_source, - qemu_irq tei_source, - qemu_irq bri_source) -{ - sh_serial_state *s; - - s = g_malloc0(sizeof(sh_serial_state)); - - s->feat = feat; - s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE; - s->rtrg = 1; - - s->smr = 0; - s->brr = 0xff; - s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */ - s->sptr = 0; - - if (feat & SH_SERIAL_FEAT_SCIF) { - s->fcr = 0; - } - else { - s->dr = 0xff; - } - - sh_serial_clear_fifo(s); - - memory_region_init_io(&s->iomem, NULL, &sh_serial_ops, s, - "serial", 0x100000000ULL); - - memory_region_init_alias(&s->iomem_p4, NULL, "serial-p4", &s->iomem, - 0, 0x28); - memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4); - - memory_region_init_alias(&s->iomem_a7, NULL, "serial-a7", &s->iomem, - 0, 0x28); - memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7); - - s->chr = chr; - - if (chr) { - qemu_chr_fe_claim_no_fail(chr); - qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1, - sh_serial_event, s); - } - - s->eri = eri_source; - s->rxi = rxi_source; - s->txi = txi_source; - s->tei = tei_source; - s->bri = bri_source; -} diff --git a/qemu/hw/char/spapr_vty.c b/qemu/hw/char/spapr_vty.c deleted file mode 100644 index 3498d7b05..000000000 --- a/qemu/hw/char/spapr_vty.c +++ /dev/null @@ -1,249 +0,0 @@ -#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" -#include "hw/ppc/spapr_vio.h" - -#define VTERM_BUFSIZE 16 - -typedef struct VIOsPAPRVTYDevice { - VIOsPAPRDevice sdev; - CharDriverState *chardev; - uint32_t in, out; - uint8_t buf[VTERM_BUFSIZE]; -} VIOsPAPRVTYDevice; - -#define TYPE_VIO_SPAPR_VTY_DEVICE "spapr-vty" -#define VIO_SPAPR_VTY_DEVICE(obj) \ - OBJECT_CHECK(VIOsPAPRVTYDevice, (obj), TYPE_VIO_SPAPR_VTY_DEVICE) - -static int vty_can_receive(void *opaque) -{ - VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(opaque); - - return (dev->in - dev->out) < VTERM_BUFSIZE; -} - -static void vty_receive(void *opaque, const uint8_t *buf, int size) -{ - VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(opaque); - int i; - - if ((dev->in == dev->out) && size) { - /* toggle line to simulate edge interrupt */ - qemu_irq_pulse(spapr_vio_qirq(&dev->sdev)); - } - for (i = 0; i < size; i++) { - assert((dev->in - dev->out) < VTERM_BUFSIZE); - dev->buf[dev->in++ % VTERM_BUFSIZE] = buf[i]; - } -} - -static int vty_getchars(VIOsPAPRDevice *sdev, uint8_t *buf, int max) -{ - VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(sdev); - int n = 0; - - while ((n < max) && (dev->out != dev->in)) { - buf[n++] = dev->buf[dev->out++ % VTERM_BUFSIZE]; - } - - qemu_chr_accept_input(dev->chardev); - - return n; -} - -void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len) -{ - VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(sdev); - - /* FIXME: should check the qemu_chr_fe_write() return value */ - qemu_chr_fe_write(dev->chardev, buf, len); -} - -static void spapr_vty_realize(VIOsPAPRDevice *sdev, Error **errp) -{ - VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(sdev); - - if (!dev->chardev) { - error_setg(errp, "chardev property not set"); - return; - } - - qemu_chr_add_handlers(dev->chardev, vty_can_receive, - vty_receive, NULL, dev); -} - -/* Forward declaration */ -static target_ulong h_put_term_char(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong reg = args[0]; - target_ulong len = args[1]; - target_ulong char0_7 = args[2]; - target_ulong char8_15 = args[3]; - VIOsPAPRDevice *sdev; - uint8_t buf[16]; - - sdev = vty_lookup(spapr, reg); - if (!sdev) { - return H_PARAMETER; - } - - if (len > 16) { - return H_PARAMETER; - } - - *((uint64_t *)buf) = cpu_to_be64(char0_7); - *((uint64_t *)buf + 1) = cpu_to_be64(char8_15); - - vty_putchars(sdev, buf, len); - - return H_SUCCESS; -} - -static target_ulong h_get_term_char(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong reg = args[0]; - target_ulong *len = args + 0; - target_ulong *char0_7 = args + 1; - target_ulong *char8_15 = args + 2; - VIOsPAPRDevice *sdev; - uint8_t buf[16]; - - sdev = vty_lookup(spapr, reg); - if (!sdev) { - return H_PARAMETER; - } - - *len = vty_getchars(sdev, buf, sizeof(buf)); - if (*len < 16) { - memset(buf + *len, 0, 16 - *len); - } - - *char0_7 = be64_to_cpu(*((uint64_t *)buf)); - *char8_15 = be64_to_cpu(*((uint64_t *)buf + 1)); - - return H_SUCCESS; -} - -void spapr_vty_create(VIOsPAPRBus *bus, CharDriverState *chardev) -{ - DeviceState *dev; - - dev = qdev_create(&bus->bus, "spapr-vty"); - qdev_prop_set_chr(dev, "chardev", chardev); - qdev_init_nofail(dev); -} - -static Property spapr_vty_properties[] = { - DEFINE_SPAPR_PROPERTIES(VIOsPAPRVTYDevice, sdev), - DEFINE_PROP_CHR("chardev", VIOsPAPRVTYDevice, chardev), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription vmstate_spapr_vty = { - .name = "spapr_vty", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_SPAPR_VIO(sdev, VIOsPAPRVTYDevice), - - VMSTATE_UINT32(in, VIOsPAPRVTYDevice), - VMSTATE_UINT32(out, VIOsPAPRVTYDevice), - VMSTATE_BUFFER(buf, VIOsPAPRVTYDevice), - VMSTATE_END_OF_LIST() - }, -}; - -static void spapr_vty_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); - - k->realize = spapr_vty_realize; - k->dt_name = "vty"; - k->dt_type = "serial"; - k->dt_compatible = "hvterm1"; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - dc->props = spapr_vty_properties; - dc->vmsd = &vmstate_spapr_vty; -} - -static const TypeInfo spapr_vty_info = { - .name = TYPE_VIO_SPAPR_VTY_DEVICE, - .parent = TYPE_VIO_SPAPR_DEVICE, - .instance_size = sizeof(VIOsPAPRVTYDevice), - .class_init = spapr_vty_class_init, -}; - -VIOsPAPRDevice *spapr_vty_get_default(VIOsPAPRBus *bus) -{ - VIOsPAPRDevice *sdev, *selected; - BusChild *kid; - - /* - * To avoid the console bouncing around we want one VTY to be - * the "default". We haven't really got anything to go on, so - * arbitrarily choose the one with the lowest reg value. - */ - - selected = NULL; - QTAILQ_FOREACH(kid, &bus->bus.children, sibling) { - DeviceState *iter = kid->child; - - /* Only look at VTY devices */ - if (!object_dynamic_cast(OBJECT(iter), TYPE_VIO_SPAPR_VTY_DEVICE)) { - continue; - } - - sdev = VIO_SPAPR_DEVICE(iter); - - /* First VTY we've found, so it is selected for now */ - if (!selected) { - selected = sdev; - continue; - } - - /* Choose VTY with lowest reg value */ - if (sdev->reg < selected->reg) { - selected = sdev; - } - } - - return selected; -} - -VIOsPAPRDevice *vty_lookup(sPAPRMachineState *spapr, target_ulong reg) -{ - VIOsPAPRDevice *sdev; - - sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - if (!sdev && reg == 0) { - /* Hack for kernel early debug, which always specifies reg==0. - * We search all VIO devices, and grab the vty with the lowest - * reg. This attempts to mimic existing PowerVM behaviour - * (early debug does work there, despite having no vty with - * reg==0. */ - return spapr_vty_get_default(spapr->vio_bus); - } - - if (!object_dynamic_cast(OBJECT(sdev), TYPE_VIO_SPAPR_VTY_DEVICE)) { - return NULL; - } - - return sdev; -} - -static void spapr_vty_register_types(void) -{ - spapr_register_hypercall(H_PUT_TERM_CHAR, h_put_term_char); - spapr_register_hypercall(H_GET_TERM_CHAR, h_get_term_char); - type_register_static(&spapr_vty_info); -} - -type_init(spapr_vty_register_types) diff --git a/qemu/hw/char/stm32f2xx_usart.c b/qemu/hw/char/stm32f2xx_usart.c deleted file mode 100644 index a94d61ceb..000000000 --- a/qemu/hw/char/stm32f2xx_usart.c +++ /dev/null @@ -1,233 +0,0 @@ -/* - * STM32F2XX USART - * - * Copyright (c) 2014 Alistair Francis - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/char/stm32f2xx_usart.h" - -#ifndef STM_USART_ERR_DEBUG -#define STM_USART_ERR_DEBUG 0 -#endif - -#define DB_PRINT_L(lvl, fmt, args...) do { \ - if (STM_USART_ERR_DEBUG >= lvl) { \ - qemu_log("%s: " fmt, __func__, ## args); \ - } \ -} while (0); - -#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) - -static int stm32f2xx_usart_can_receive(void *opaque) -{ - STM32F2XXUsartState *s = opaque; - - if (!(s->usart_sr & USART_SR_RXNE)) { - return 1; - } - - return 0; -} - -static void stm32f2xx_usart_receive(void *opaque, const uint8_t *buf, int size) -{ - STM32F2XXUsartState *s = opaque; - - s->usart_dr = *buf; - - if (!(s->usart_cr1 & USART_CR1_UE && s->usart_cr1 & USART_CR1_RE)) { - /* USART not enabled - drop the chars */ - DB_PRINT("Dropping the chars\n"); - return; - } - - s->usart_sr |= USART_SR_RXNE; - - if (s->usart_cr1 & USART_CR1_RXNEIE) { - qemu_set_irq(s->irq, 1); - } - - DB_PRINT("Receiving: %c\n", s->usart_dr); -} - -static void stm32f2xx_usart_reset(DeviceState *dev) -{ - STM32F2XXUsartState *s = STM32F2XX_USART(dev); - - s->usart_sr = USART_SR_RESET; - s->usart_dr = 0x00000000; - s->usart_brr = 0x00000000; - s->usart_cr1 = 0x00000000; - s->usart_cr2 = 0x00000000; - s->usart_cr3 = 0x00000000; - s->usart_gtpr = 0x00000000; - - qemu_set_irq(s->irq, 0); -} - -static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr, - unsigned int size) -{ - STM32F2XXUsartState *s = opaque; - uint64_t retvalue; - - DB_PRINT("Read 0x%"HWADDR_PRIx"\n", addr); - - switch (addr) { - case USART_SR: - retvalue = s->usart_sr; - s->usart_sr &= ~USART_SR_TC; - if (s->chr) { - qemu_chr_accept_input(s->chr); - } - return retvalue; - case USART_DR: - DB_PRINT("Value: 0x%" PRIx32 ", %c\n", s->usart_dr, (char) s->usart_dr); - s->usart_sr |= USART_SR_TXE; - s->usart_sr &= ~USART_SR_RXNE; - if (s->chr) { - qemu_chr_accept_input(s->chr); - } - qemu_set_irq(s->irq, 0); - return s->usart_dr & 0x3FF; - case USART_BRR: - return s->usart_brr; - case USART_CR1: - return s->usart_cr1; - case USART_CR2: - return s->usart_cr2; - case USART_CR3: - return s->usart_cr3; - case USART_GTPR: - return s->usart_gtpr; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); - return 0; - } - - return 0; -} - -static void stm32f2xx_usart_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - STM32F2XXUsartState *s = opaque; - uint32_t value = val64; - unsigned char ch; - - DB_PRINT("Write 0x%" PRIx32 ", 0x%"HWADDR_PRIx"\n", value, addr); - - switch (addr) { - case USART_SR: - if (value <= 0x3FF) { - s->usart_sr = value; - } else { - s->usart_sr &= value; - } - if (!(s->usart_sr & USART_SR_RXNE)) { - qemu_set_irq(s->irq, 0); - } - return; - case USART_DR: - if (value < 0xF000) { - ch = value; - if (s->chr) { - qemu_chr_fe_write_all(s->chr, &ch, 1); - } - s->usart_sr |= USART_SR_TC; - s->usart_sr &= ~USART_SR_TXE; - } - return; - case USART_BRR: - s->usart_brr = value; - return; - case USART_CR1: - s->usart_cr1 = value; - if (s->usart_cr1 & USART_CR1_RXNEIE && - s->usart_sr & USART_SR_RXNE) { - qemu_set_irq(s->irq, 1); - } - return; - case USART_CR2: - s->usart_cr2 = value; - return; - case USART_CR3: - s->usart_cr3 = value; - return; - case USART_GTPR: - s->usart_gtpr = value; - return; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); - } -} - -static const MemoryRegionOps stm32f2xx_usart_ops = { - .read = stm32f2xx_usart_read, - .write = stm32f2xx_usart_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void stm32f2xx_usart_init(Object *obj) -{ - STM32F2XXUsartState *s = STM32F2XX_USART(obj); - - sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); - - memory_region_init_io(&s->mmio, obj, &stm32f2xx_usart_ops, s, - TYPE_STM32F2XX_USART, 0x2000); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); - - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); - - if (s->chr) { - qemu_chr_add_handlers(s->chr, stm32f2xx_usart_can_receive, - stm32f2xx_usart_receive, NULL, s); - } -} - -static void stm32f2xx_usart_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = stm32f2xx_usart_reset; - /* Reason: instance_init() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo stm32f2xx_usart_info = { - .name = TYPE_STM32F2XX_USART, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(STM32F2XXUsartState), - .instance_init = stm32f2xx_usart_init, - .class_init = stm32f2xx_usart_class_init, -}; - -static void stm32f2xx_usart_register_types(void) -{ - type_register_static(&stm32f2xx_usart_info); -} - -type_init(stm32f2xx_usart_register_types) diff --git a/qemu/hw/char/virtio-console.c b/qemu/hw/char/virtio-console.c deleted file mode 100644 index 2e36481a7..000000000 --- a/qemu/hw/char/virtio-console.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Virtio Console and Generic Serial Port Devices - * - * Copyright Red Hat, Inc. 2009, 2010 - * - * Authors: - * Amit Shah - * - * 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 "sysemu/char.h" -#include "qemu/error-report.h" -#include "trace.h" -#include "hw/virtio/virtio-serial.h" -#include "qapi-event.h" - -#define TYPE_VIRTIO_CONSOLE_SERIAL_PORT "virtserialport" -#define VIRTIO_CONSOLE(obj) \ - OBJECT_CHECK(VirtConsole, (obj), TYPE_VIRTIO_CONSOLE_SERIAL_PORT) - -typedef struct VirtConsole { - VirtIOSerialPort parent_obj; - - CharDriverState *chr; - guint watch; -} VirtConsole; - -/* - * Callback function that's called from chardevs when backend becomes - * writable. - */ -static gboolean chr_write_unblocked(GIOChannel *chan, GIOCondition cond, - void *opaque) -{ - VirtConsole *vcon = opaque; - - vcon->watch = 0; - virtio_serial_throttle_port(VIRTIO_SERIAL_PORT(vcon), false); - return FALSE; -} - -/* Callback function that's called when the guest sends us data */ -static ssize_t flush_buf(VirtIOSerialPort *port, - const uint8_t *buf, ssize_t len) -{ - VirtConsole *vcon = VIRTIO_CONSOLE(port); - ssize_t ret; - - if (!vcon->chr) { - /* If there's no backend, we can just say we consumed all data. */ - return len; - } - - ret = qemu_chr_fe_write(vcon->chr, buf, len); - trace_virtio_console_flush_buf(port->id, len, ret); - - if (ret < len) { - VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port); - - /* - * Ideally we'd get a better error code than just -1, but - * that's what the chardev interface gives us right now. If - * we had a finer-grained message, like -EPIPE, we could close - * this connection. - */ - if (ret < 0) - ret = 0; - if (!k->is_console) { - virtio_serial_throttle_port(port, true); - if (!vcon->watch) { - vcon->watch = qemu_chr_fe_add_watch(vcon->chr, - G_IO_OUT|G_IO_HUP, - chr_write_unblocked, vcon); - } - } - } - return ret; -} - -/* Callback function that's called when the guest opens/closes the port */ -static void set_guest_connected(VirtIOSerialPort *port, int guest_connected) -{ - VirtConsole *vcon = VIRTIO_CONSOLE(port); - DeviceState *dev = DEVICE(port); - - if (vcon->chr) { - qemu_chr_fe_set_open(vcon->chr, guest_connected); - } - - if (dev->id) { - qapi_event_send_vserport_change(dev->id, guest_connected, - &error_abort); - } -} - -static void guest_writable(VirtIOSerialPort *port) -{ - VirtConsole *vcon = VIRTIO_CONSOLE(port); - - if (vcon->chr) { - qemu_chr_accept_input(vcon->chr); - } -} - -/* Readiness of the guest to accept data on a port */ -static int chr_can_read(void *opaque) -{ - VirtConsole *vcon = opaque; - - return virtio_serial_guest_ready(VIRTIO_SERIAL_PORT(vcon)); -} - -/* Send data from a char device over to the guest */ -static void chr_read(void *opaque, const uint8_t *buf, int size) -{ - VirtConsole *vcon = opaque; - VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(vcon); - - trace_virtio_console_chr_read(port->id, size); - virtio_serial_write(port, buf, size); -} - -static void chr_event(void *opaque, int event) -{ - VirtConsole *vcon = opaque; - VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(vcon); - - trace_virtio_console_chr_event(port->id, event); - switch (event) { - case CHR_EVENT_OPENED: - virtio_serial_open(port); - break; - case CHR_EVENT_CLOSED: - if (vcon->watch) { - g_source_remove(vcon->watch); - vcon->watch = 0; - } - virtio_serial_close(port); - break; - } -} - -static void virtconsole_realize(DeviceState *dev, Error **errp) -{ - VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); - VirtConsole *vcon = VIRTIO_CONSOLE(dev); - VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(dev); - - if (port->id == 0 && !k->is_console) { - error_setg(errp, "Port number 0 on virtio-serial devices reserved " - "for virtconsole devices for backward compatibility."); - return; - } - - if (vcon->chr) { - vcon->chr->explicit_fe_open = 1; - qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event, - vcon); - } -} - -static void virtconsole_unrealize(DeviceState *dev, Error **errp) -{ - VirtConsole *vcon = VIRTIO_CONSOLE(dev); - - if (vcon->watch) { - g_source_remove(vcon->watch); - } -} - -static void virtconsole_class_init(ObjectClass *klass, void *data) -{ - VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass); - - k->is_console = true; -} - -static const TypeInfo virtconsole_info = { - .name = "virtconsole", - .parent = TYPE_VIRTIO_CONSOLE_SERIAL_PORT, - .class_init = virtconsole_class_init, -}; - -static Property virtserialport_properties[] = { - DEFINE_PROP_CHR("chardev", VirtConsole, chr), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtserialport_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass); - - k->realize = virtconsole_realize; - k->unrealize = virtconsole_unrealize; - k->have_data = flush_buf; - k->set_guest_connected = set_guest_connected; - k->guest_writable = guest_writable; - dc->props = virtserialport_properties; -} - -static const TypeInfo virtserialport_info = { - .name = TYPE_VIRTIO_CONSOLE_SERIAL_PORT, - .parent = TYPE_VIRTIO_SERIAL_PORT, - .instance_size = sizeof(VirtConsole), - .class_init = virtserialport_class_init, -}; - -static void virtconsole_register_types(void) -{ - type_register_static(&virtserialport_info); - type_register_static(&virtconsole_info); -} - -type_init(virtconsole_register_types) diff --git a/qemu/hw/char/virtio-serial-bus.c b/qemu/hw/char/virtio-serial-bus.c deleted file mode 100644 index 6e5de6dec..000000000 --- a/qemu/hw/char/virtio-serial-bus.c +++ /dev/null @@ -1,1149 +0,0 @@ -/* - * A bus for connecting virtio serial and console ports - * - * Copyright (C) 2009, 2010 Red Hat, Inc. - * - * Author(s): - * Amit Shah - * - * Some earlier parts are: - * Copyright IBM, Corp. 2008 - * authored by - * Christian Ehrhardt - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * 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/iov.h" -#include "monitor/monitor.h" -#include "qemu/error-report.h" -#include "qemu/queue.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "hw/virtio/virtio-serial.h" -#include "hw/virtio/virtio-access.h" - -static struct VirtIOSerialDevices { - QLIST_HEAD(, VirtIOSerial) devices; -} vserdevices; - -static VirtIOSerialPort *find_port_by_id(VirtIOSerial *vser, uint32_t id) -{ - VirtIOSerialPort *port; - - if (id == VIRTIO_CONSOLE_BAD_ID) { - return NULL; - } - - QTAILQ_FOREACH(port, &vser->ports, next) { - if (port->id == id) - return port; - } - return NULL; -} - -static VirtIOSerialPort *find_port_by_vq(VirtIOSerial *vser, VirtQueue *vq) -{ - VirtIOSerialPort *port; - - QTAILQ_FOREACH(port, &vser->ports, next) { - if (port->ivq == vq || port->ovq == vq) - return port; - } - return NULL; -} - -static VirtIOSerialPort *find_port_by_name(char *name) -{ - VirtIOSerial *vser; - - QLIST_FOREACH(vser, &vserdevices.devices, next) { - VirtIOSerialPort *port; - - QTAILQ_FOREACH(port, &vser->ports, next) { - if (port->name && !strcmp(port->name, name)) { - return port; - } - } - } - return NULL; -} - -static bool use_multiport(VirtIOSerial *vser) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(vser); - 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; - VirtQueue *vq; - size_t offset; - - vq = port->ivq; - if (!virtio_queue_ready(vq)) { - return 0; - } - - offset = 0; - while (offset < size) { - size_t len; - - elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); - if (!elem) { - break; - } - - len = iov_from_buf(elem->in_sg, elem->in_num, 0, - buf + offset, size - offset); - offset += len; - - virtqueue_push(vq, elem, len); - g_free(elem); - } - - virtio_notify(VIRTIO_DEVICE(port->vser), vq); - return offset; -} - -static void discard_vq_data(VirtQueue *vq, VirtIODevice *vdev) -{ - VirtQueueElement *elem; - - if (!virtio_queue_ready(vq)) { - return; - } - for (;;) { - elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); - if (!elem) { - break; - } - virtqueue_push(vq, elem, 0); - g_free(elem); - } - virtio_notify(vdev, vq); -} - -static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, - VirtIODevice *vdev) -{ - VirtIOSerialPortClass *vsc; - - assert(port); - assert(virtio_queue_ready(vq)); - - vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); - - while (!port->throttled) { - unsigned int i; - - /* Pop an elem only if we haven't left off a previous one mid-way */ - 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++) { - size_t buf_size; - ssize_t ret; - - 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->iov_offset, - buf_size); - if (port->throttled) { - port->iov_idx = i; - if (ret > 0) { - port->iov_offset += ret; - } - break; - } - port->iov_offset = 0; - } - if (port->throttled) { - break; - } - virtqueue_push(vq, port->elem, 0); - g_free(port->elem); - port->elem = NULL; - } - virtio_notify(vdev, vq); -} - -static void flush_queued_data(VirtIOSerialPort *port) -{ - assert(port); - - if (!virtio_queue_ready(port->ovq)) { - return; - } - do_flush_queued_data(port, port->ovq, VIRTIO_DEVICE(port->vser)); -} - -static size_t send_control_msg(VirtIOSerial *vser, void *buf, size_t len) -{ - VirtQueueElement *elem; - VirtQueue *vq; - - vq = vser->c_ivq; - if (!virtio_queue_ready(vq)) { - return 0; - } - - 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); - - virtqueue_push(vq, elem, len); - virtio_notify(VIRTIO_DEVICE(vser), vq); - g_free(elem); - - return len; -} - -static size_t send_control_event(VirtIOSerial *vser, uint32_t port_id, - uint16_t event, uint16_t value) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(vser); - struct virtio_console_control cpkt; - - virtio_stl_p(vdev, &cpkt.id, port_id); - virtio_stw_p(vdev, &cpkt.event, event); - virtio_stw_p(vdev, &cpkt.value, value); - - trace_virtio_serial_send_control_event(port_id, event, value); - return send_control_msg(vser, &cpkt, sizeof(cpkt)); -} - -/* Functions for use inside qemu to open and read from/write to ports */ -int virtio_serial_open(VirtIOSerialPort *port) -{ - /* Don't allow opening an already-open port */ - if (port->host_connected) { - return 0; - } - /* Send port open notification to the guest */ - port->host_connected = true; - send_control_event(port->vser, port->id, VIRTIO_CONSOLE_PORT_OPEN, 1); - - return 0; -} - -int virtio_serial_close(VirtIOSerialPort *port) -{ - port->host_connected = false; - /* - * If there's any data the guest sent which the app didn't - * consume, reset the throttling flag and discard the data. - */ - port->throttled = false; - discard_vq_data(port->ovq, VIRTIO_DEVICE(port->vser)); - - send_control_event(port->vser, port->id, VIRTIO_CONSOLE_PORT_OPEN, 0); - - return 0; -} - -/* Individual ports/apps call this function to write to the guest. */ -ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf, - size_t size) -{ - if (!port || !port->host_connected || !port->guest_connected) { - return 0; - } - return write_to_port(port, buf, size); -} - -/* - * Readiness of the guest to accept data on a port. - * Returns max. data the guest can receive - */ -size_t virtio_serial_guest_ready(VirtIOSerialPort *port) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(port->vser); - VirtQueue *vq = port->ivq; - unsigned int bytes; - - if (!virtio_queue_ready(vq) || - !(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) || - virtio_queue_empty(vq)) { - return 0; - } - if (use_multiport(port->vser) && !port->guest_connected) { - return 0; - } - virtqueue_get_avail_bytes(vq, &bytes, NULL, 4096, 0); - return bytes; -} - -static void flush_queued_data_bh(void *opaque) -{ - VirtIOSerialPort *port = opaque; - - flush_queued_data(port); -} - -void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) -{ - if (!port) { - return; - } - - trace_virtio_serial_throttle_port(port->id, throttle); - port->throttled = throttle; - if (throttle) { - return; - } - qemu_bh_schedule(port->bh); -} - -/* Guest wants to notify us of some event */ -static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(vser); - struct VirtIOSerialPort *port; - VirtIOSerialPortClass *vsc; - struct virtio_console_control cpkt, *gcpkt; - uint8_t *buffer; - size_t buffer_len; - - gcpkt = buf; - - if (len < sizeof(cpkt)) { - /* The guest sent an invalid control packet */ - return; - } - - cpkt.event = virtio_lduw_p(vdev, &gcpkt->event); - cpkt.value = virtio_lduw_p(vdev, &gcpkt->value); - - trace_virtio_serial_handle_control_message(cpkt.event, cpkt.value); - - if (cpkt.event == VIRTIO_CONSOLE_DEVICE_READY) { - if (!cpkt.value) { - error_report("virtio-serial-bus: Guest failure in adding device %s", - vser->bus.qbus.name); - return; - } - /* - * The device is up, we can now tell the device about all the - * ports we have here. - */ - QTAILQ_FOREACH(port, &vser->ports, next) { - send_control_event(vser, port->id, VIRTIO_CONSOLE_PORT_ADD, 1); - } - return; - } - - port = find_port_by_id(vser, virtio_ldl_p(vdev, &gcpkt->id)); - if (!port) { - error_report("virtio-serial-bus: Unexpected port id %u for device %s", - virtio_ldl_p(vdev, &gcpkt->id), vser->bus.qbus.name); - return; - } - - trace_virtio_serial_handle_control_message_port(port->id); - - vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); - - switch(cpkt.event) { - case VIRTIO_CONSOLE_PORT_READY: - if (!cpkt.value) { - error_report("virtio-serial-bus: Guest failure in adding port %u for device %s", - port->id, vser->bus.qbus.name); - break; - } - /* - * Now that we know the guest asked for the port name, we're - * sure the guest has initialised whatever state is necessary - * for this port. Now's a good time to let the guest know if - * this port is a console port so that the guest can hook it - * up to hvc. - */ - if (vsc->is_console) { - send_control_event(vser, port->id, VIRTIO_CONSOLE_CONSOLE_PORT, 1); - } - - if (port->name) { - virtio_stl_p(vdev, &cpkt.id, port->id); - virtio_stw_p(vdev, &cpkt.event, VIRTIO_CONSOLE_PORT_NAME); - virtio_stw_p(vdev, &cpkt.value, 1); - - buffer_len = sizeof(cpkt) + strlen(port->name) + 1; - buffer = g_malloc(buffer_len); - - memcpy(buffer, &cpkt, sizeof(cpkt)); - memcpy(buffer + sizeof(cpkt), port->name, strlen(port->name)); - buffer[buffer_len - 1] = 0; - - send_control_msg(vser, buffer, buffer_len); - g_free(buffer); - } - - if (port->host_connected) { - send_control_event(vser, port->id, VIRTIO_CONSOLE_PORT_OPEN, 1); - } - - /* - * When the guest has asked us for this information it means - * the guest is all setup and has its virtqueues - * initialised. If some app is interested in knowing about - * this event, let it know. - */ - if (vsc->guest_ready) { - vsc->guest_ready(port); - } - break; - - case VIRTIO_CONSOLE_PORT_OPEN: - port->guest_connected = cpkt.value; - if (vsc->set_guest_connected) { - /* Send the guest opened notification if an app is interested */ - vsc->set_guest_connected(port, cpkt.value); - } - break; - } -} - -static void control_in(VirtIODevice *vdev, VirtQueue *vq) -{ -} - -static void control_out(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtQueueElement *elem; - VirtIOSerial *vser; - uint8_t *buf; - size_t len; - - vser = VIRTIO_SERIAL(vdev); - - len = 0; - buf = NULL; - for (;;) { - size_t cur_len; - - 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 - */ - if (cur_len > len) { - g_free(buf); - - buf = g_malloc(cur_len); - len = 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); - g_free(elem); - } - g_free(buf); - virtio_notify(vdev, vq); -} - -/* Guest wrote something to some port. */ -static void handle_output(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIOSerial *vser; - VirtIOSerialPort *port; - - vser = VIRTIO_SERIAL(vdev); - port = find_port_by_vq(vser, vq); - - if (!port || !port->host_connected) { - discard_vq_data(vq, vdev); - return; - } - - if (!port->throttled) { - do_flush_queued_data(port, vq, vdev); - return; - } -} - -static void handle_input(VirtIODevice *vdev, VirtQueue *vq) -{ - /* - * Users of virtio-serial would like to know when guest becomes - * writable again -- i.e. if a vq had stuff queued up and the - * guest wasn't reading at all, the host would not be able to - * write to the vq anymore. Once the guest reads off something, - * we can start queueing things up again. However, this call is - * made for each buffer addition by the guest -- even though free - * buffers existed prior to the current buffer addition. This is - * done so as not to maintain previous state, which will need - * additional live-migration-related changes. - */ - VirtIOSerial *vser; - VirtIOSerialPort *port; - VirtIOSerialPortClass *vsc; - - vser = VIRTIO_SERIAL(vdev); - port = find_port_by_vq(vser, vq); - - if (!port) { - return; - } - vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); - - /* - * If guest_connected is false, this call is being made by the - * early-boot queueing up of descriptors, which is just noise for - * the host apps -- don't disturb them in that case. - */ - if (port->guest_connected && port->host_connected && vsc->guest_writable) { - vsc->guest_writable(port); - } -} - -static uint64_t get_features(VirtIODevice *vdev, uint64_t features, - Error **errp) -{ - VirtIOSerial *vser; - - vser = VIRTIO_SERIAL(vdev); - - if (vser->bus.max_nr_ports > 1) { - virtio_add_feature(&features, VIRTIO_CONSOLE_F_MULTIPORT); - } - return features; -} - -/* Guest requested config info */ -static void get_config(VirtIODevice *vdev, uint8_t *config_data) -{ - VirtIOSerial *vser = VIRTIO_SERIAL(vdev); - struct virtio_console_config *config = - (struct virtio_console_config *)config_data; - - config->cols = 0; - config->rows = 0; - config->max_nr_ports = virtio_tswap32(vdev, - vser->serial.max_virtserial_ports); -} - -static void guest_reset(VirtIOSerial *vser) -{ - VirtIOSerialPort *port; - VirtIOSerialPortClass *vsc; - - QTAILQ_FOREACH(port, &vser->ports, next) { - vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); - if (port->guest_connected) { - port->guest_connected = false; - if (vsc->set_guest_connected) { - vsc->set_guest_connected(port, false); - } - } - } -} - -static void set_status(VirtIODevice *vdev, uint8_t status) -{ - VirtIOSerial *vser; - VirtIOSerialPort *port; - - vser = VIRTIO_SERIAL(vdev); - port = find_port_by_id(vser, 0); - - if (port && !use_multiport(port->vser) - && (status & VIRTIO_CONFIG_S_DRIVER_OK)) { - /* - * Non-multiport guests won't be able to tell us guest - * open/close status. Such guests can only have a port at id - * 0, so set guest_connected for such ports as soon as guest - * is up. - */ - port->guest_connected = true; - } - if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) { - guest_reset(vser); - } -} - -static void vser_reset(VirtIODevice *vdev) -{ - VirtIOSerial *vser; - - vser = VIRTIO_SERIAL(vdev); - guest_reset(vser); -} - -static void virtio_serial_save(QEMUFile *f, void *opaque) -{ - /* The virtio device */ - virtio_save(VIRTIO_DEVICE(opaque), f); -} - -static void virtio_serial_save_device(VirtIODevice *vdev, QEMUFile *f) -{ - VirtIOSerial *s = VIRTIO_SERIAL(vdev); - VirtIOSerialPort *port; - uint32_t nr_active_ports; - unsigned int i, max_nr_ports; - struct virtio_console_config config; - - /* The config space (ignored on the far end in current versions) */ - get_config(vdev, (uint8_t *)&config); - qemu_put_be16s(f, &config.cols); - qemu_put_be16s(f, &config.rows); - qemu_put_be32s(f, &config.max_nr_ports); - - /* The ports map */ - max_nr_ports = s->serial.max_virtserial_ports; - for (i = 0; i < (max_nr_ports + 31) / 32; i++) { - qemu_put_be32s(f, &s->ports_map[i]); - } - - /* Ports */ - - nr_active_ports = 0; - QTAILQ_FOREACH(port, &s->ports, next) { - nr_active_ports++; - } - - qemu_put_be32s(f, &nr_active_ports); - - /* - * Items in struct VirtIOSerialPort. - */ - QTAILQ_FOREACH(port, &s->ports, next) { - uint32_t elem_popped; - - qemu_put_be32s(f, &port->id); - qemu_put_byte(f, port->guest_connected); - qemu_put_byte(f, port->host_connected); - - elem_popped = 0; - 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_virtqueue_element(f, port->elem); - } - } -} - -static void virtio_serial_post_load_timer_cb(void *opaque) -{ - uint32_t i; - VirtIOSerial *s = VIRTIO_SERIAL(opaque); - VirtIOSerialPort *port; - uint8_t host_connected; - VirtIOSerialPortClass *vsc; - - if (!s->post_load) { - return; - } - for (i = 0 ; i < s->post_load->nr_active_ports; ++i) { - port = s->post_load->connected[i].port; - host_connected = s->post_load->connected[i].host_connected; - if (host_connected != port->host_connected) { - /* - * We have to let the guest know of the host connection - * status change - */ - send_control_event(s, port->id, VIRTIO_CONSOLE_PORT_OPEN, - port->host_connected); - } - vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); - if (vsc->set_guest_connected) { - vsc->set_guest_connected(port, port->guest_connected); - } - } - g_free(s->post_load->connected); - timer_free(s->post_load->timer); - g_free(s->post_load); - s->post_load = NULL; -} - -static int fetch_active_ports_list(QEMUFile *f, int version_id, - VirtIOSerial *s, uint32_t nr_active_ports) -{ - uint32_t i; - - s->post_load = g_malloc0(sizeof(*s->post_load)); - s->post_load->nr_active_ports = nr_active_ports; - s->post_load->connected = - g_malloc0(sizeof(*s->post_load->connected) * nr_active_ports); - - s->post_load->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - virtio_serial_post_load_timer_cb, - s); - - /* Items in struct VirtIOSerialPort */ - for (i = 0; i < nr_active_ports; i++) { - VirtIOSerialPort *port; - uint32_t id; - - id = qemu_get_be32(f); - port = find_port_by_id(s, id); - if (!port) { - return -EINVAL; - } - - port->guest_connected = qemu_get_byte(f); - s->post_load->connected[i].port = port; - s->post_load->connected[i].host_connected = qemu_get_byte(f); - - if (version_id > 2) { - uint32_t elem_popped; - - qemu_get_be32s(f, &elem_popped); - if (elem_popped) { - qemu_get_be32s(f, &port->iov_idx); - qemu_get_be64s(f, &port->iov_offset); - - port->elem = - qemu_get_virtqueue_element(f, sizeof(VirtQueueElement)); - - /* - * Port was throttled on source machine. Let's - * unthrottle it here so data starts flowing again. - */ - virtio_serial_throttle_port(port, false); - } - } - } - timer_mod(s->post_load->timer, 1); - return 0; -} - -static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) -{ - if (version_id > 3) { - return -EINVAL; - } - - /* The virtio device */ - return virtio_load(VIRTIO_DEVICE(opaque), f, version_id); -} - -static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f, - int version_id) -{ - VirtIOSerial *s = VIRTIO_SERIAL(vdev); - uint32_t max_nr_ports, nr_active_ports, ports_map; - unsigned int i; - int ret; - uint32_t tmp; - - if (version_id < 2) { - return 0; - } - - /* Unused */ - qemu_get_be16s(f, (uint16_t *) &tmp); - qemu_get_be16s(f, (uint16_t *) &tmp); - qemu_get_be32s(f, &tmp); - - max_nr_ports = s->serial.max_virtserial_ports; - for (i = 0; i < (max_nr_ports + 31) / 32; i++) { - qemu_get_be32s(f, &ports_map); - - if (ports_map != s->ports_map[i]) { - /* - * Ports active on source and destination don't - * match. Fail migration. - */ - return -EINVAL; - } - } - - qemu_get_be32s(f, &nr_active_ports); - - if (nr_active_ports) { - ret = fetch_active_ports_list(f, version_id, s, nr_active_ports); - if (ret) { - return ret; - } - } - return 0; -} - -static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); - -static Property virtser_props[] = { - DEFINE_PROP_UINT32("nr", VirtIOSerialPort, id, VIRTIO_CONSOLE_BAD_ID), - DEFINE_PROP_STRING("name", VirtIOSerialPort, name), - DEFINE_PROP_END_OF_LIST() -}; - -#define TYPE_VIRTIO_SERIAL_BUS "virtio-serial-bus" -#define VIRTIO_SERIAL_BUS(obj) \ - OBJECT_CHECK(VirtIOSerialBus, (obj), TYPE_VIRTIO_SERIAL_BUS) - -static void virtser_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - k->print_dev = virtser_bus_dev_print; -} - -static const TypeInfo virtser_bus_info = { - .name = TYPE_VIRTIO_SERIAL_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(VirtIOSerialBus), - .class_init = virtser_bus_class_init, -}; - -static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) -{ - VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(qdev); - - monitor_printf(mon, "%*sport %d, guest %s, host %s, throttle %s\n", - indent, "", port->id, - port->guest_connected ? "on" : "off", - port->host_connected ? "on" : "off", - port->throttled ? "on" : "off"); -} - -/* This function is only used if a port id is not provided by the user */ -static uint32_t find_free_port_id(VirtIOSerial *vser) -{ - unsigned int i, max_nr_ports; - - max_nr_ports = vser->serial.max_virtserial_ports; - for (i = 0; i < (max_nr_ports + 31) / 32; i++) { - uint32_t map, zeroes; - - map = vser->ports_map[i]; - zeroes = ctz32(~map); - if (zeroes != 32) { - return zeroes + i * 32; - } - } - return VIRTIO_CONSOLE_BAD_ID; -} - -static void mark_port_added(VirtIOSerial *vser, uint32_t port_id) -{ - unsigned int i; - - i = port_id / 32; - vser->ports_map[i] |= 1U << (port_id % 32); -} - -static void add_port(VirtIOSerial *vser, uint32_t port_id) -{ - mark_port_added(vser, port_id); - send_control_event(vser, port_id, VIRTIO_CONSOLE_PORT_ADD, 1); -} - -static void remove_port(VirtIOSerial *vser, uint32_t port_id) -{ - VirtIOSerialPort *port; - - /* - * Don't mark port 0 removed -- we explicitly reserve it for - * backward compat with older guests, ensure a virtconsole device - * unplug retains the reservation. - */ - if (port_id) { - unsigned int i; - - i = port_id / 32; - vser->ports_map[i] &= ~(1U << (port_id % 32)); - } - - port = find_port_by_id(vser, port_id); - /* - * This function is only called from qdev's unplug callback; if we - * get a NULL port here, we're in trouble. - */ - assert(port); - - /* Flush out any unconsumed buffers first */ - discard_vq_data(port->ovq, VIRTIO_DEVICE(port->vser)); - - send_control_event(vser, port->id, VIRTIO_CONSOLE_PORT_REMOVE, 1); -} - -static void virtser_port_device_realize(DeviceState *dev, Error **errp) -{ - VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); - VirtIOSerialPortClass *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); - VirtIOSerialBus *bus = VIRTIO_SERIAL_BUS(qdev_get_parent_bus(dev)); - int max_nr_ports; - bool plugging_port0; - Error *err = NULL; - - port->vser = bus->vser; - port->bh = qemu_bh_new(flush_queued_data_bh, port); - - assert(vsc->have_data); - - /* - * Is the first console port we're seeing? If so, put it up at - * location 0. This is done for backward compatibility (old - * kernel, new qemu). - */ - plugging_port0 = vsc->is_console && !find_port_by_id(port->vser, 0); - - if (find_port_by_id(port->vser, port->id)) { - error_setg(errp, "virtio-serial-bus: A port already exists at id %u", - port->id); - return; - } - - if (port->name != NULL && find_port_by_name(port->name)) { - error_setg(errp, "virtio-serial-bus: A port already exists by name %s", - port->name); - return; - } - - if (port->id == VIRTIO_CONSOLE_BAD_ID) { - if (plugging_port0) { - port->id = 0; - } else { - port->id = find_free_port_id(port->vser); - if (port->id == VIRTIO_CONSOLE_BAD_ID) { - error_setg(errp, "virtio-serial-bus: Maximum port limit for " - "this device reached"); - return; - } - } - } - - max_nr_ports = port->vser->serial.max_virtserial_ports; - if (port->id >= max_nr_ports) { - error_setg(errp, "virtio-serial-bus: Out-of-range port id specified, " - "max. allowed: %u", max_nr_ports - 1); - return; - } - - vsc->realize(dev, &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - - port->elem = NULL; -} - -static void virtser_port_device_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); - - QTAILQ_INSERT_TAIL(&port->vser->ports, port, next); - port->ivq = port->vser->ivqs[port->id]; - port->ovq = port->vser->ovqs[port->id]; - - add_port(port->vser, port->id); - - /* Send an update to the guest about this new port added */ - virtio_notify_config(VIRTIO_DEVICE(hotplug_dev)); -} - -static void virtser_port_device_unrealize(DeviceState *dev, Error **errp) -{ - VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); - VirtIOSerialPortClass *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(dev); - VirtIOSerial *vser = port->vser; - - qemu_bh_delete(port->bh); - remove_port(port->vser, port->id); - - QTAILQ_REMOVE(&vser->ports, port, next); - - if (vsc->unrealize) { - vsc->unrealize(dev, errp); - } -} - -static void virtio_serial_device_realize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIOSerial *vser = VIRTIO_SERIAL(dev); - uint32_t i, max_supported_ports; - - if (!vser->serial.max_virtserial_ports) { - error_setg(errp, "Maximum number of serial ports not specified"); - return; - } - - /* Each port takes 2 queues, and one pair is for the control queue */ - max_supported_ports = VIRTIO_QUEUE_MAX / 2 - 1; - - if (vser->serial.max_virtserial_ports > max_supported_ports) { - error_setg(errp, "maximum ports supported: %u", max_supported_ports); - return; - } - - /* We don't support emergency write, skip it for now. */ - /* TODO: cleaner fix, depending on host features. */ - virtio_init(vdev, "virtio-serial", VIRTIO_ID_CONSOLE, - offsetof(struct virtio_console_config, emerg_wr)); - - /* Spawn a new virtio-serial bus on which the ports will ride as devices */ - qbus_create_inplace(&vser->bus, sizeof(vser->bus), TYPE_VIRTIO_SERIAL_BUS, - dev, vdev->bus_name); - qbus_set_hotplug_handler(BUS(&vser->bus), DEVICE(vser), errp); - vser->bus.vser = vser; - QTAILQ_INIT(&vser->ports); - - vser->bus.max_nr_ports = vser->serial.max_virtserial_ports; - vser->ivqs = g_malloc(vser->serial.max_virtserial_ports - * sizeof(VirtQueue *)); - vser->ovqs = g_malloc(vser->serial.max_virtserial_ports - * sizeof(VirtQueue *)); - - /* Add a queue for host to guest transfers for port 0 (backward compat) */ - vser->ivqs[0] = virtio_add_queue(vdev, 128, handle_input); - /* Add a queue for guest to host transfers for port 0 (backward compat) */ - vser->ovqs[0] = virtio_add_queue(vdev, 128, handle_output); - - /* TODO: host to guest notifications can get dropped - * if the queue fills up. Implement queueing in host, - * this might also make it possible to reduce the control - * queue size: as guest preposts buffers there, - * this will save 4Kbyte of guest memory per entry. */ - - /* control queue: host to guest */ - vser->c_ivq = virtio_add_queue(vdev, 32, control_in); - /* control queue: guest to host */ - vser->c_ovq = virtio_add_queue(vdev, 32, control_out); - - for (i = 1; i < vser->bus.max_nr_ports; i++) { - /* Add a per-port queue for host to guest transfers */ - vser->ivqs[i] = virtio_add_queue(vdev, 128, handle_input); - /* Add a per-per queue for guest to host transfers */ - vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output); - } - - vser->ports_map = g_malloc0(((vser->serial.max_virtserial_ports + 31) / 32) - * sizeof(vser->ports_map[0])); - /* - * Reserve location 0 for a console port for backward compat - * (old kernel, new qemu) - */ - mark_port_added(vser, 0); - - vser->post_load = NULL; - - /* - * Register for the savevm section with the virtio-console name - * to preserve backward compat - */ - register_savevm(dev, "virtio-console", -1, 3, virtio_serial_save, - virtio_serial_load, vser); - - QLIST_INSERT_HEAD(&vserdevices.devices, vser, next); -} - -static void virtio_serial_port_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_INPUT, k->categories); - k->bus_type = TYPE_VIRTIO_SERIAL_BUS; - k->realize = virtser_port_device_realize; - k->unrealize = virtser_port_device_unrealize; - k->props = virtser_props; -} - -static const TypeInfo virtio_serial_port_type_info = { - .name = TYPE_VIRTIO_SERIAL_PORT, - .parent = TYPE_DEVICE, - .instance_size = sizeof(VirtIOSerialPort), - .abstract = true, - .class_size = sizeof(VirtIOSerialPortClass), - .class_init = virtio_serial_port_class_init, -}; - -static void virtio_serial_device_unrealize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIOSerial *vser = VIRTIO_SERIAL(dev); - - QLIST_REMOVE(vser, next); - - unregister_savevm(dev, "virtio-console", vser); - - g_free(vser->ivqs); - g_free(vser->ovqs); - g_free(vser->ports_map); - if (vser->post_load) { - g_free(vser->post_load->connected); - timer_del(vser->post_load->timer); - timer_free(vser->post_load->timer); - g_free(vser->post_load); - } - virtio_cleanup(vdev); -} - -static Property virtio_serial_properties[] = { - DEFINE_PROP_UINT32("max_ports", VirtIOSerial, serial.max_virtserial_ports, - 31), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_serial_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - - QLIST_INIT(&vserdevices.devices); - - dc->props = virtio_serial_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - vdc->realize = virtio_serial_device_realize; - vdc->unrealize = virtio_serial_device_unrealize; - vdc->get_features = get_features; - vdc->get_config = get_config; - vdc->set_status = set_status; - vdc->reset = vser_reset; - vdc->save = virtio_serial_save_device; - vdc->load = virtio_serial_load_device; - hc->plug = virtser_port_device_plug; - hc->unplug = qdev_simple_device_unplug_cb; -} - -static const TypeInfo virtio_device_info = { - .name = TYPE_VIRTIO_SERIAL, - .parent = TYPE_VIRTIO_DEVICE, - .instance_size = sizeof(VirtIOSerial), - .class_init = virtio_serial_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - -static void virtio_serial_register_types(void) -{ - type_register_static(&virtser_bus_info); - type_register_static(&virtio_serial_port_type_info); - type_register_static(&virtio_device_info); -} - -type_init(virtio_serial_register_types) diff --git a/qemu/hw/char/xen_console.c b/qemu/hw/char/xen_console.c deleted file mode 100644 index cbf1dccbb..000000000 --- a/qemu/hw/char/xen_console.c +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright (C) International Business Machines Corp., 2005 - * Author(s): Anthony Liguori - * - * Copyright (C) Red Hat 2007 - * - * Xen Console - * - * 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; under version 2 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 . - */ - -#include "qemu/osdep.h" -#include -#include -#include - -#include "hw/hw.h" -#include "sysemu/char.h" -#include "hw/xen/xen_backend.h" - -#include - -struct buffer { - uint8_t *data; - size_t consumed; - size_t size; - size_t capacity; - size_t max_capacity; -}; - -struct XenConsole { - struct XenDevice xendev; /* must be first */ - struct buffer buffer; - char console[XEN_BUFSIZE]; - int ring_ref; - void *sring; - CharDriverState *chr; - int backlog; -}; - -static void buffer_append(struct XenConsole *con) -{ - struct buffer *buffer = &con->buffer; - XENCONS_RING_IDX cons, prod, size; - struct xencons_interface *intf = con->sring; - - cons = intf->out_cons; - prod = intf->out_prod; - xen_mb(); - - size = prod - cons; - if ((size == 0) || (size > sizeof(intf->out))) - return; - - if ((buffer->capacity - buffer->size) < size) { - buffer->capacity += (size + 1024); - buffer->data = g_realloc(buffer->data, buffer->capacity); - } - - while (cons != prod) - buffer->data[buffer->size++] = intf->out[ - MASK_XENCONS_IDX(cons++, intf->out)]; - - xen_mb(); - intf->out_cons = cons; - xen_be_send_notify(&con->xendev); - - if (buffer->max_capacity && - buffer->size > buffer->max_capacity) { - /* Discard the middle of the data. */ - - size_t over = buffer->size - buffer->max_capacity; - uint8_t *maxpos = buffer->data + buffer->max_capacity; - - memmove(maxpos - over, maxpos, over); - buffer->data = g_realloc(buffer->data, buffer->max_capacity); - buffer->size = buffer->capacity = buffer->max_capacity; - - if (buffer->consumed > buffer->max_capacity - over) - buffer->consumed = buffer->max_capacity - over; - } -} - -static void buffer_advance(struct buffer *buffer, size_t len) -{ - buffer->consumed += len; - if (buffer->consumed == buffer->size) { - buffer->consumed = 0; - buffer->size = 0; - } -} - -static int ring_free_bytes(struct XenConsole *con) -{ - struct xencons_interface *intf = con->sring; - XENCONS_RING_IDX cons, prod, space; - - cons = intf->in_cons; - prod = intf->in_prod; - xen_mb(); - - space = prod - cons; - if (space > sizeof(intf->in)) - return 0; /* ring is screwed: ignore it */ - - return (sizeof(intf->in) - space); -} - -static int xencons_can_receive(void *opaque) -{ - struct XenConsole *con = opaque; - return ring_free_bytes(con); -} - -static void xencons_receive(void *opaque, const uint8_t *buf, int len) -{ - struct XenConsole *con = opaque; - struct xencons_interface *intf = con->sring; - XENCONS_RING_IDX prod; - int i, max; - - max = ring_free_bytes(con); - /* The can_receive() func limits this, but check again anyway */ - if (max < len) - len = max; - - prod = intf->in_prod; - for (i = 0; i < len; i++) { - intf->in[MASK_XENCONS_IDX(prod++, intf->in)] = - buf[i]; - } - xen_wmb(); - intf->in_prod = prod; - xen_be_send_notify(&con->xendev); -} - -static void xencons_send(struct XenConsole *con) -{ - ssize_t len, size; - - size = con->buffer.size - con->buffer.consumed; - if (con->chr) - len = qemu_chr_fe_write(con->chr, con->buffer.data + con->buffer.consumed, - size); - else - len = size; - if (len < 1) { - if (!con->backlog) { - con->backlog = 1; - xen_be_printf(&con->xendev, 1, "backlog piling up, nobody listening?\n"); - } - } else { - buffer_advance(&con->buffer, len); - if (con->backlog && len == size) { - con->backlog = 0; - xen_be_printf(&con->xendev, 1, "backlog is gone\n"); - } - } -} - -/* -------------------------------------------------------------------- */ - -static int con_init(struct XenDevice *xendev) -{ - struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); - char *type, *dom, label[32]; - int ret = 0; - const char *output; - - /* setup */ - dom = xs_get_domain_path(xenstore, con->xendev.dom); - if (!xendev->dev) { - snprintf(con->console, sizeof(con->console), "%s/console", dom); - } else { - snprintf(con->console, sizeof(con->console), "%s/device/console/%d", dom, xendev->dev); - } - free(dom); - - type = xenstore_read_str(con->console, "type"); - if (!type || strcmp(type, "ioemu") != 0) { - xen_be_printf(xendev, 1, "not for me (type=%s)\n", type); - ret = -1; - goto out; - } - - output = xenstore_read_str(con->console, "output"); - - /* no Xen override, use qemu output device */ - if (output == NULL) { - con->chr = serial_hds[con->xendev.dev]; - } else { - snprintf(label, sizeof(label), "xencons%d", con->xendev.dev); - con->chr = qemu_chr_new(label, output, NULL); - } - - xenstore_store_pv_console_info(con->xendev.dev, con->chr); - -out: - g_free(type); - return ret; -} - -static int con_initialise(struct XenDevice *xendev) -{ - struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); - int limit; - - if (xenstore_read_int(con->console, "ring-ref", &con->ring_ref) == -1) - return -1; - if (xenstore_read_int(con->console, "port", &con->xendev.remote_port) == -1) - return -1; - if (xenstore_read_int(con->console, "limit", &limit) == 0) - con->buffer.max_capacity = limit; - - if (!xendev->dev) { - xen_pfn_t mfn = con->ring_ref; - con->sring = xenforeignmemory_map(xen_fmem, con->xendev.dom, - PROT_READ|PROT_WRITE, - 1, &mfn, NULL); - } else { - con->sring = xengnttab_map_grant_ref(xendev->gnttabdev, con->xendev.dom, - con->ring_ref, - PROT_READ|PROT_WRITE); - } - if (!con->sring) - return -1; - - xen_be_bind_evtchn(&con->xendev); - if (con->chr) { - if (qemu_chr_fe_claim(con->chr) == 0) { - qemu_chr_add_handlers(con->chr, xencons_can_receive, - xencons_receive, NULL, con); - } else { - xen_be_printf(xendev, 0, - "xen_console_init error chardev %s already used\n", - con->chr->label); - con->chr = NULL; - } - } - - xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", - con->ring_ref, - con->xendev.remote_port, - con->xendev.local_port, - con->buffer.max_capacity); - return 0; -} - -static void con_disconnect(struct XenDevice *xendev) -{ - struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); - - if (con->chr) { - qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL); - qemu_chr_fe_release(con->chr); - } - xen_be_unbind_evtchn(&con->xendev); - - if (con->sring) { - if (!xendev->dev) { - xenforeignmemory_unmap(xen_fmem, con->sring, 1); - } else { - xengnttab_unmap(xendev->gnttabdev, con->sring, 1); - } - con->sring = NULL; - } -} - -static void con_event(struct XenDevice *xendev) -{ - struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); - - buffer_append(con); - if (con->buffer.size - con->buffer.consumed) - xencons_send(con); -} - -/* -------------------------------------------------------------------- */ - -struct XenDevOps xen_console_ops = { - .size = sizeof(struct XenConsole), - .flags = DEVOPS_FLAG_IGNORE_STATE|DEVOPS_FLAG_NEED_GNTDEV, - .init = con_init, - .initialise = con_initialise, - .event = con_event, - .disconnect = con_disconnect, -}; diff --git a/qemu/hw/char/xilinx_uartlite.c b/qemu/hw/char/xilinx_uartlite.c deleted file mode 100644 index 911af4a0d..000000000 --- a/qemu/hw/char/xilinx_uartlite.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * QEMU model of Xilinx uartlite. - * - * Copyright (c) 2009 Edgar E. Iglesias. - * - * 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/sysbus.h" -#include "sysemu/char.h" - -#define DUART(x) - -#define R_RX 0 -#define R_TX 1 -#define R_STATUS 2 -#define R_CTRL 3 -#define R_MAX 4 - -#define STATUS_RXVALID 0x01 -#define STATUS_RXFULL 0x02 -#define STATUS_TXEMPTY 0x04 -#define STATUS_TXFULL 0x08 -#define STATUS_IE 0x10 -#define STATUS_OVERRUN 0x20 -#define STATUS_FRAME 0x40 -#define STATUS_PARITY 0x80 - -#define CONTROL_RST_TX 0x01 -#define CONTROL_RST_RX 0x02 -#define CONTROL_IE 0x10 - -#define TYPE_XILINX_UARTLITE "xlnx.xps-uartlite" -#define XILINX_UARTLITE(obj) \ - OBJECT_CHECK(XilinxUARTLite, (obj), TYPE_XILINX_UARTLITE) - -typedef struct XilinxUARTLite { - SysBusDevice parent_obj; - - MemoryRegion mmio; - CharDriverState *chr; - qemu_irq irq; - - uint8_t rx_fifo[8]; - unsigned int rx_fifo_pos; - unsigned int rx_fifo_len; - - uint32_t regs[R_MAX]; -} XilinxUARTLite; - -static void uart_update_irq(XilinxUARTLite *s) -{ - unsigned int irq; - - if (s->rx_fifo_len) - s->regs[R_STATUS] |= STATUS_IE; - - irq = (s->regs[R_STATUS] & STATUS_IE) && (s->regs[R_CTRL] & CONTROL_IE); - qemu_set_irq(s->irq, irq); -} - -static void uart_update_status(XilinxUARTLite *s) -{ - uint32_t r; - - r = s->regs[R_STATUS]; - r &= ~7; - r |= 1 << 2; /* Tx fifo is always empty. We are fast :) */ - r |= (s->rx_fifo_len == sizeof (s->rx_fifo)) << 1; - r |= (!!s->rx_fifo_len); - s->regs[R_STATUS] = r; -} - -static void xilinx_uartlite_reset(DeviceState *dev) -{ - uart_update_status(XILINX_UARTLITE(dev)); -} - -static uint64_t -uart_read(void *opaque, hwaddr addr, unsigned int size) -{ - XilinxUARTLite *s = opaque; - uint32_t r = 0; - addr >>= 2; - switch (addr) - { - case R_RX: - r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 7]; - if (s->rx_fifo_len) - s->rx_fifo_len--; - uart_update_status(s); - uart_update_irq(s); - qemu_chr_accept_input(s->chr); - break; - - default: - if (addr < ARRAY_SIZE(s->regs)) - r = s->regs[addr]; - DUART(qemu_log("%s addr=%x v=%x\n", __func__, addr, r)); - break; - } - return r; -} - -static void -uart_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - XilinxUARTLite *s = opaque; - uint32_t value = val64; - unsigned char ch = value; - - addr >>= 2; - switch (addr) - { - case R_STATUS: - hw_error("write to UART STATUS?\n"); - break; - - case R_CTRL: - if (value & CONTROL_RST_RX) { - s->rx_fifo_pos = 0; - s->rx_fifo_len = 0; - } - s->regs[addr] = value; - break; - - case R_TX: - if (s->chr) - qemu_chr_fe_write(s->chr, &ch, 1); - - s->regs[addr] = value; - - /* hax. */ - s->regs[R_STATUS] |= STATUS_IE; - break; - - default: - DUART(printf("%s addr=%x v=%x\n", __func__, addr, value)); - if (addr < ARRAY_SIZE(s->regs)) - s->regs[addr] = value; - break; - } - uart_update_status(s); - uart_update_irq(s); -} - -static const MemoryRegionOps uart_ops = { - .read = uart_read, - .write = uart_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 4 - } -}; - -static void uart_rx(void *opaque, const uint8_t *buf, int size) -{ - XilinxUARTLite *s = opaque; - - /* Got a byte. */ - if (s->rx_fifo_len >= 8) { - printf("WARNING: UART dropped char.\n"); - return; - } - s->rx_fifo[s->rx_fifo_pos] = *buf; - s->rx_fifo_pos++; - s->rx_fifo_pos &= 0x7; - s->rx_fifo_len++; - - uart_update_status(s); - uart_update_irq(s); -} - -static int uart_can_rx(void *opaque) -{ - XilinxUARTLite *s = opaque; - - return s->rx_fifo_len < sizeof(s->rx_fifo); -} - -static void uart_event(void *opaque, int event) -{ - -} - -static void xilinx_uartlite_realize(DeviceState *dev, Error **errp) -{ - XilinxUARTLite *s = XILINX_UARTLITE(dev); - - /* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */ - s->chr = qemu_char_get_next_serial(); - if (s->chr) - qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); -} - -static void xilinx_uartlite_init(Object *obj) -{ - XilinxUARTLite *s = XILINX_UARTLITE(obj); - - sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); - - memory_region_init_io(&s->mmio, obj, &uart_ops, s, - "xlnx.xps-uartlite", R_MAX * 4); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); -} - -static void xilinx_uartlite_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = xilinx_uartlite_reset; - dc->realize = xilinx_uartlite_realize; - /* Reason: realize() method uses qemu_char_get_next_serial() */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo xilinx_uartlite_info = { - .name = TYPE_XILINX_UARTLITE, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(XilinxUARTLite), - .instance_init = xilinx_uartlite_init, - .class_init = xilinx_uartlite_class_init, -}; - -static void xilinx_uart_register_types(void) -{ - type_register_static(&xilinx_uartlite_info); -} - -type_init(xilinx_uart_register_types) diff --git a/qemu/hw/core/Makefile.objs b/qemu/hw/core/Makefile.objs deleted file mode 100644 index abb3560be..000000000 --- a/qemu/hw/core/Makefile.objs +++ /dev/null @@ -1,17 +0,0 @@ -# core qdev-related obj files, also used by *-user: -common-obj-y += qdev.o qdev-properties.o -common-obj-y += fw-path-provider.o -# irq.o needed for qdev GPIO handling: -common-obj-y += irq.o -common-obj-y += hotplug.o -common-obj-y += nmi.o - -common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o -common-obj-$(CONFIG_XILINX_AXI) += stream.o -common-obj-$(CONFIG_PTIMER) += ptimer.o -common-obj-$(CONFIG_SOFTMMU) += sysbus.o -common-obj-$(CONFIG_SOFTMMU) += machine.o -common-obj-$(CONFIG_SOFTMMU) += null-machine.o -common-obj-$(CONFIG_SOFTMMU) += loader.o -common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o -common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o diff --git a/qemu/hw/core/empty_slot.c b/qemu/hw/core/empty_slot.c deleted file mode 100644 index c1b9c2b10..000000000 --- a/qemu/hw/core/empty_slot.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * QEMU Empty Slot - * - * The empty_slot device emulates known to a bus but not connected devices. - * - * Copyright (c) 2010 Artyom Tarasenko - * - * This code is licensed under the GNU GPL v2 or (at your option) any later - * version. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "hw/empty_slot.h" - -//#define DEBUG_EMPTY_SLOT - -#ifdef DEBUG_EMPTY_SLOT -#define DPRINTF(fmt, ...) \ - do { printf("empty_slot: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while (0) -#endif - -#define TYPE_EMPTY_SLOT "empty_slot" -#define EMPTY_SLOT(obj) OBJECT_CHECK(EmptySlot, (obj), TYPE_EMPTY_SLOT) - -typedef struct EmptySlot { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint64_t size; -} EmptySlot; - -static uint64_t empty_slot_read(void *opaque, hwaddr addr, - unsigned size) -{ - DPRINTF("read from " TARGET_FMT_plx "\n", addr); - return 0; -} - -static void empty_slot_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - DPRINTF("write 0x%x to " TARGET_FMT_plx "\n", (unsigned)val, addr); -} - -static const MemoryRegionOps empty_slot_ops = { - .read = empty_slot_read, - .write = empty_slot_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -void empty_slot_init(hwaddr addr, uint64_t slot_size) -{ - if (slot_size > 0) { - /* Only empty slots larger than 0 byte need handling. */ - DeviceState *dev; - SysBusDevice *s; - EmptySlot *e; - - dev = qdev_create(NULL, TYPE_EMPTY_SLOT); - s = SYS_BUS_DEVICE(dev); - e = EMPTY_SLOT(dev); - e->size = slot_size; - - qdev_init_nofail(dev); - - sysbus_mmio_map(s, 0, addr); - } -} - -static int empty_slot_init1(SysBusDevice *dev) -{ - EmptySlot *s = EMPTY_SLOT(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &empty_slot_ops, s, - "empty-slot", s->size); - sysbus_init_mmio(dev, &s->iomem); - return 0; -} - -static void empty_slot_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = empty_slot_init1; -} - -static const TypeInfo empty_slot_info = { - .name = TYPE_EMPTY_SLOT, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(EmptySlot), - .class_init = empty_slot_class_init, -}; - -static void empty_slot_register_types(void) -{ - type_register_static(&empty_slot_info); -} - -type_init(empty_slot_register_types) diff --git a/qemu/hw/core/fw-path-provider.c b/qemu/hw/core/fw-path-provider.c deleted file mode 100644 index 33b99830e..000000000 --- a/qemu/hw/core/fw-path-provider.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Firmware path provider class and helpers. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/fw-path-provider.h" - -char *fw_path_provider_get_dev_path(FWPathProvider *p, BusState *bus, - DeviceState *dev) -{ - FWPathProviderClass *k = FW_PATH_PROVIDER_GET_CLASS(p); - - return k->get_dev_path(p, bus, dev); -} - -char *fw_path_provider_try_get_dev_path(Object *o, BusState *bus, - DeviceState *dev) -{ - FWPathProvider *p = (FWPathProvider *) - object_dynamic_cast(o, TYPE_FW_PATH_PROVIDER); - - if (p) { - return fw_path_provider_get_dev_path(p, bus, dev); - } - - return NULL; -} - -static const TypeInfo fw_path_provider_info = { - .name = TYPE_FW_PATH_PROVIDER, - .parent = TYPE_INTERFACE, - .class_size = sizeof(FWPathProviderClass), -}; - -static void fw_path_provider_register_types(void) -{ - type_register_static(&fw_path_provider_info); -} - -type_init(fw_path_provider_register_types) diff --git a/qemu/hw/core/hotplug.c b/qemu/hw/core/hotplug.c deleted file mode 100644 index 645cfca1b..000000000 --- a/qemu/hw/core/hotplug.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Hotplug handler interface. - * - * Copyright (c) 2014 Red Hat Inc. - * - * Authors: - * Igor Mammedov , - * - * 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" - -void hotplug_handler_plug(HotplugHandler *plug_handler, - DeviceState *plugged_dev, - Error **errp) -{ - HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); - - if (hdc->plug) { - hdc->plug(plug_handler, plugged_dev, errp); - } -} - -void hotplug_handler_unplug_request(HotplugHandler *plug_handler, - DeviceState *plugged_dev, - Error **errp) -{ - HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); - - if (hdc->unplug_request) { - hdc->unplug_request(plug_handler, plugged_dev, errp); - } -} - -void hotplug_handler_unplug(HotplugHandler *plug_handler, - DeviceState *plugged_dev, - Error **errp) -{ - HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); - - if (hdc->unplug) { - hdc->unplug(plug_handler, plugged_dev, errp); - } -} - -static const TypeInfo hotplug_handler_info = { - .name = TYPE_HOTPLUG_HANDLER, - .parent = TYPE_INTERFACE, - .class_size = sizeof(HotplugHandlerClass), -}; - -static void hotplug_handler_register_types(void) -{ - type_register_static(&hotplug_handler_info); -} - -type_init(hotplug_handler_register_types) diff --git a/qemu/hw/core/irq.c b/qemu/hw/core/irq.c deleted file mode 100644 index 49ff2e64f..000000000 --- a/qemu/hw/core/irq.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * QEMU IRQ/GPIO common code. - * - * Copyright (c) 2007 CodeSourcery. - * - * 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 "qemu-common.h" -#include "hw/irq.h" -#include "qom/object.h" - -#define IRQ(obj) OBJECT_CHECK(struct IRQState, (obj), TYPE_IRQ) - -struct IRQState { - Object parent_obj; - - qemu_irq_handler handler; - void *opaque; - int n; -}; - -void qemu_set_irq(qemu_irq irq, int level) -{ - if (!irq) - return; - - irq->handler(irq->opaque, irq->n, level); -} - -qemu_irq *qemu_extend_irqs(qemu_irq *old, int n_old, qemu_irq_handler handler, - void *opaque, int n) -{ - qemu_irq *s; - int i; - - if (!old) { - n_old = 0; - } - s = old ? g_renew(qemu_irq, old, n + n_old) : g_new(qemu_irq, n); - for (i = n_old; i < n + n_old; i++) { - s[i] = qemu_allocate_irq(handler, opaque, i); - } - return s; -} - -qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n) -{ - return qemu_extend_irqs(NULL, 0, handler, opaque, n); -} - -qemu_irq qemu_allocate_irq(qemu_irq_handler handler, void *opaque, int n) -{ - struct IRQState *irq; - - irq = IRQ(object_new(TYPE_IRQ)); - irq->handler = handler; - irq->opaque = opaque; - irq->n = n; - - return irq; -} - -void qemu_free_irqs(qemu_irq *s, int n) -{ - int i; - for (i = 0; i < n; i++) { - qemu_free_irq(s[i]); - } - g_free(s); -} - -void qemu_free_irq(qemu_irq irq) -{ - object_unref(OBJECT(irq)); -} - -static void qemu_notirq(void *opaque, int line, int level) -{ - struct IRQState *irq = opaque; - - irq->handler(irq->opaque, irq->n, !level); -} - -qemu_irq qemu_irq_invert(qemu_irq irq) -{ - /* The default state for IRQs is low, so raise the output now. */ - qemu_irq_raise(irq); - return qemu_allocate_irq(qemu_notirq, irq, 0); -} - -static void qemu_splitirq(void *opaque, int line, int level) -{ - struct IRQState **irq = opaque; - irq[0]->handler(irq[0]->opaque, irq[0]->n, level); - irq[1]->handler(irq[1]->opaque, irq[1]->n, level); -} - -qemu_irq qemu_irq_split(qemu_irq irq1, qemu_irq irq2) -{ - qemu_irq *s = g_malloc0(2 * sizeof(qemu_irq)); - s[0] = irq1; - s[1] = irq2; - return qemu_allocate_irq(qemu_splitirq, s, 0); -} - -static void proxy_irq_handler(void *opaque, int n, int level) -{ - qemu_irq **target = opaque; - - if (*target) { - qemu_set_irq((*target)[n], level); - } -} - -qemu_irq *qemu_irq_proxy(qemu_irq **target, int n) -{ - return qemu_allocate_irqs(proxy_irq_handler, target, n); -} - -void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n) -{ - int i; - qemu_irq *old_irqs = qemu_allocate_irqs(NULL, NULL, n); - for (i = 0; i < n; i++) { - *old_irqs[i] = *gpio_in[i]; - gpio_in[i]->handler = handler; - gpio_in[i]->opaque = &old_irqs[i]; - } -} - -static const TypeInfo irq_type_info = { - .name = TYPE_IRQ, - .parent = TYPE_OBJECT, - .instance_size = sizeof(struct IRQState), -}; - -static void irq_register_types(void) -{ - type_register_static(&irq_type_info); -} - -type_init(irq_register_types) diff --git a/qemu/hw/core/loader.c b/qemu/hw/core/loader.c deleted file mode 100644 index c0499571c..000000000 --- a/qemu/hw/core/loader.c +++ /dev/null @@ -1,1190 +0,0 @@ -/* - * QEMU Executable loader - * - * Copyright (c) 2006 Fabrice Bellard - * - * 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. - * - * Gunzip functionality in this file is derived from u-boot: - * - * (C) Copyright 2008 Semihalf - * - * (C) Copyright 2000-2005 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/hw.h" -#include "disas/disas.h" -#include "monitor/monitor.h" -#include "sysemu/sysemu.h" -#include "uboot_image.h" -#include "hw/loader.h" -#include "hw/nvram/fw_cfg.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" -#include "hw/boards.h" -#include "qemu/cutils.h" - -#include - -static int roms_loaded; - -/* return the size or -1 if error */ -int get_image_size(const char *filename) -{ - int fd, size; - fd = open(filename, O_RDONLY | O_BINARY); - if (fd < 0) - return -1; - size = lseek(fd, 0, SEEK_END); - close(fd); - return size; -} - -/* return the size or -1 if error */ -/* deprecated, because caller does not specify buffer size! */ -int load_image(const char *filename, uint8_t *addr) -{ - int fd, size; - fd = open(filename, O_RDONLY | O_BINARY); - if (fd < 0) - return -1; - size = lseek(fd, 0, SEEK_END); - if (size == -1) { - fprintf(stderr, "file %-20s: get size error: %s\n", - filename, strerror(errno)); - close(fd); - return -1; - } - - lseek(fd, 0, SEEK_SET); - if (read(fd, addr, size) != size) { - close(fd); - return -1; - } - close(fd); - return size; -} - -/* return the size or -1 if error */ -ssize_t load_image_size(const char *filename, void *addr, size_t size) -{ - int fd; - ssize_t actsize; - - fd = open(filename, O_RDONLY | O_BINARY); - if (fd < 0) { - return -1; - } - - actsize = read(fd, addr, size); - if (actsize < 0) { - close(fd); - return -1; - } - close(fd); - - return actsize; -} - -/* read()-like version */ -ssize_t read_targphys(const char *name, - int fd, hwaddr dst_addr, size_t nbytes) -{ - uint8_t *buf; - ssize_t did; - - buf = g_malloc(nbytes); - did = read(fd, buf, nbytes); - if (did > 0) - rom_add_blob_fixed("read", buf, did, dst_addr); - g_free(buf); - return did; -} - -/* return the size or -1 if error */ -int load_image_targphys(const char *filename, - hwaddr addr, uint64_t max_sz) -{ - int size; - - size = get_image_size(filename); - if (size > max_sz) { - return -1; - } - if (size > 0) { - rom_add_file_fixed(filename, addr, -1); - } - 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) -{ - const char *nulp; - char *ptr; - - if (buf_size <= 0) return; - nulp = memchr(source, 0, buf_size); - if (nulp) { - rom_add_blob_fixed(name, source, (nulp - source) + 1, dest); - } else { - rom_add_blob_fixed(name, source, buf_size, dest); - ptr = rom_ptr(dest + buf_size - 1); - *ptr = 0; - } -} - -/* A.OUT loader */ - -struct exec -{ - uint32_t a_info; /* Use macros N_MAGIC, etc for access */ - uint32_t a_text; /* length of text, in bytes */ - uint32_t a_data; /* length of data, in bytes */ - uint32_t a_bss; /* length of uninitialized data area, in bytes */ - uint32_t a_syms; /* length of symbol table data in file, in bytes */ - uint32_t a_entry; /* start address */ - uint32_t a_trsize; /* length of relocation info for text, in bytes */ - uint32_t a_drsize; /* length of relocation info for data, in bytes */ -}; - -static void bswap_ahdr(struct exec *e) -{ - bswap32s(&e->a_info); - bswap32s(&e->a_text); - bswap32s(&e->a_data); - bswap32s(&e->a_bss); - bswap32s(&e->a_syms); - bswap32s(&e->a_entry); - bswap32s(&e->a_trsize); - bswap32s(&e->a_drsize); -} - -#define N_MAGIC(exec) ((exec).a_info & 0xffff) -#define OMAGIC 0407 -#define NMAGIC 0410 -#define ZMAGIC 0413 -#define QMAGIC 0314 -#define _N_HDROFF(x) (1024 - sizeof (struct exec)) -#define N_TXTOFF(x) \ - (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : \ - (N_MAGIC(x) == QMAGIC ? 0 : sizeof (struct exec))) -#define N_TXTADDR(x, target_page_size) (N_MAGIC(x) == QMAGIC ? target_page_size : 0) -#define _N_SEGMENT_ROUND(x, target_page_size) (((x) + target_page_size - 1) & ~(target_page_size - 1)) - -#define _N_TXTENDADDR(x, target_page_size) (N_TXTADDR(x, target_page_size)+(x).a_text) - -#define N_DATADDR(x, target_page_size) \ - (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x, target_page_size)) \ - : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x, target_page_size), target_page_size))) - - -int load_aout(const char *filename, hwaddr addr, int max_sz, - int bswap_needed, hwaddr target_page_size) -{ - int fd; - ssize_t size, ret; - struct exec e; - uint32_t magic; - - fd = open(filename, O_RDONLY | O_BINARY); - if (fd < 0) - return -1; - - size = read(fd, &e, sizeof(e)); - if (size < 0) - goto fail; - - if (bswap_needed) { - bswap_ahdr(&e); - } - - magic = N_MAGIC(e); - switch (magic) { - case ZMAGIC: - case QMAGIC: - case OMAGIC: - if (e.a_text + e.a_data > max_sz) - goto fail; - lseek(fd, N_TXTOFF(e), SEEK_SET); - size = read_targphys(filename, fd, addr, e.a_text + e.a_data); - if (size < 0) - goto fail; - break; - case NMAGIC: - if (N_DATADDR(e, target_page_size) + e.a_data > max_sz) - goto fail; - lseek(fd, N_TXTOFF(e), SEEK_SET); - size = read_targphys(filename, fd, addr, e.a_text); - if (size < 0) - goto fail; - ret = read_targphys(filename, fd, addr + N_DATADDR(e, target_page_size), - e.a_data); - if (ret < 0) - goto fail; - size += ret; - break; - default: - goto fail; - } - close(fd); - return size; - fail: - close(fd); - return -1; -} - -/* ELF loader */ - -static void *load_at(int fd, off_t offset, size_t size) -{ - void *ptr; - if (lseek(fd, offset, SEEK_SET) < 0) - return NULL; - ptr = g_malloc(size); - if (read(fd, ptr, size) != size) { - g_free(ptr); - return NULL; - } - return ptr; -} - -#ifdef ELF_CLASS -#undef ELF_CLASS -#endif - -#define ELF_CLASS ELFCLASS32 -#include "elf.h" - -#define SZ 32 -#define elf_word uint32_t -#define elf_sword int32_t -#define bswapSZs bswap32s -#include "hw/elf_ops.h" - -#undef elfhdr -#undef elf_phdr -#undef elf_shdr -#undef elf_sym -#undef elf_rela -#undef elf_note -#undef elf_word -#undef elf_sword -#undef bswapSZs -#undef SZ -#define elfhdr elf64_hdr -#define elf_phdr elf64_phdr -#define elf_note elf64_note -#define elf_shdr elf64_shdr -#define elf_sym elf64_sym -#define elf_rela elf64_rela -#define elf_word uint64_t -#define elf_sword int64_t -#define bswapSZs bswap64s -#define SZ 64 -#include "hw/elf_ops.h" - -const char *load_elf_strerror(int error) -{ - switch (error) { - case 0: - return "No error"; - case ELF_LOAD_FAILED: - return "Failed to load ELF"; - case ELF_LOAD_NOT_ELF: - return "The image is not ELF"; - case ELF_LOAD_WRONG_ARCH: - return "The image is from incompatible architecture"; - case ELF_LOAD_WRONG_ENDIAN: - return "The image has incorrect endianness"; - default: - return "Unknown 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, int data_swab) -{ - int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED; - uint8_t e_ident[EI_NIDENT]; - - fd = open(filename, O_RDONLY | O_BINARY); - if (fd < 0) { - perror(filename); - return -1; - } - if (read(fd, e_ident, sizeof(e_ident)) != sizeof(e_ident)) - goto fail; - if (e_ident[0] != ELFMAG0 || - e_ident[1] != ELFMAG1 || - e_ident[2] != ELFMAG2 || - e_ident[3] != ELFMAG3) { - ret = ELF_LOAD_NOT_ELF; - goto fail; - } -#ifdef HOST_WORDS_BIGENDIAN - data_order = ELFDATA2MSB; -#else - data_order = ELFDATA2LSB; -#endif - must_swab = data_order != e_ident[EI_DATA]; - if (big_endian) { - target_data_order = ELFDATA2MSB; - } else { - target_data_order = ELFDATA2LSB; - } - - if (target_data_order != e_ident[EI_DATA]) { - ret = ELF_LOAD_WRONG_ENDIAN; - goto fail; - } - - 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, - data_swab); - } else { - ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab, - pentry, lowaddr, highaddr, elf_machine, clear_lsb, - data_swab); - } - - fail: - close(fd); - return ret; -} - -static void bswap_uboot_header(uboot_image_header_t *hdr) -{ -#ifndef HOST_WORDS_BIGENDIAN - bswap32s(&hdr->ih_magic); - bswap32s(&hdr->ih_hcrc); - bswap32s(&hdr->ih_time); - bswap32s(&hdr->ih_size); - bswap32s(&hdr->ih_load); - bswap32s(&hdr->ih_ep); - bswap32s(&hdr->ih_dcrc); -#endif -} - - -#define ZALLOC_ALIGNMENT 16 - -static void *zalloc(void *x, unsigned items, unsigned size) -{ - void *p; - - size *= items; - size = (size + ZALLOC_ALIGNMENT - 1) & ~(ZALLOC_ALIGNMENT - 1); - - p = g_malloc(size); - - return (p); -} - -static void zfree(void *x, void *addr) -{ - g_free(addr); -} - - -#define HEAD_CRC 2 -#define EXTRA_FIELD 4 -#define ORIG_NAME 8 -#define COMMENT 0x10 -#define RESERVED 0xe0 - -#define DEFLATED 8 - -/* This is the usual maximum in uboot, so if a uImage overflows this, it would - * overflow on real hardware too. */ -#define UBOOT_MAX_GUNZIP_BYTES (64 << 20) - -static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, - size_t srclen) -{ - z_stream s; - ssize_t dstbytes; - int r, i, flags; - - /* skip header */ - i = 10; - flags = src[3]; - if (src[2] != DEFLATED || (flags & RESERVED) != 0) { - puts ("Error: Bad gzipped data\n"); - return -1; - } - if ((flags & EXTRA_FIELD) != 0) - i = 12 + src[10] + (src[11] << 8); - if ((flags & ORIG_NAME) != 0) - while (src[i++] != 0) - ; - if ((flags & COMMENT) != 0) - while (src[i++] != 0) - ; - if ((flags & HEAD_CRC) != 0) - i += 2; - if (i >= srclen) { - puts ("Error: gunzip out of data in header\n"); - return -1; - } - - s.zalloc = zalloc; - s.zfree = zfree; - - r = inflateInit2(&s, -MAX_WBITS); - if (r != Z_OK) { - printf ("Error: inflateInit2() returned %d\n", r); - return (-1); - } - s.next_in = src + i; - s.avail_in = srclen - i; - s.next_out = dst; - s.avail_out = dstlen; - r = inflate(&s, Z_FINISH); - if (r != Z_OK && r != Z_STREAM_END) { - printf ("Error: inflate() returned %d\n", r); - return -1; - } - dstbytes = s.next_out - (unsigned char *) dst; - inflateEnd(&s); - - return dstbytes; -} - -/* Load a U-Boot image. */ -static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr, - int *is_linux, uint8_t image_type, - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque) -{ - int fd; - int size; - hwaddr address; - uboot_image_header_t h; - uboot_image_header_t *hdr = &h; - uint8_t *data = NULL; - int ret = -1; - int do_uncompress = 0; - - fd = open(filename, O_RDONLY | O_BINARY); - if (fd < 0) - return -1; - - size = read(fd, hdr, sizeof(uboot_image_header_t)); - if (size < 0) - goto out; - - bswap_uboot_header(hdr); - - if (hdr->ih_magic != IH_MAGIC) - goto out; - - if (hdr->ih_type != image_type) { - fprintf(stderr, "Wrong image type %d, expected %d\n", hdr->ih_type, - image_type); - goto out; - } - - /* TODO: Implement other image types. */ - switch (hdr->ih_type) { - case IH_TYPE_KERNEL: - address = hdr->ih_load; - if (translate_fn) { - address = translate_fn(translate_opaque, address); - } - if (loadaddr) { - *loadaddr = hdr->ih_load; - } - - switch (hdr->ih_comp) { - case IH_COMP_NONE: - break; - case IH_COMP_GZIP: - do_uncompress = 1; - break; - default: - fprintf(stderr, - "Unable to load u-boot images with compression type %d\n", - hdr->ih_comp); - goto out; - } - - if (ep) { - *ep = hdr->ih_ep; - } - - /* TODO: Check CPU type. */ - if (is_linux) { - if (hdr->ih_os == IH_OS_LINUX) { - *is_linux = 1; - } else { - *is_linux = 0; - } - } - - break; - case IH_TYPE_RAMDISK: - address = *loadaddr; - break; - default: - fprintf(stderr, "Unsupported u-boot image type %d\n", hdr->ih_type); - goto out; - } - - data = g_malloc(hdr->ih_size); - - if (read(fd, data, hdr->ih_size) != hdr->ih_size) { - fprintf(stderr, "Error reading file\n"); - goto out; - } - - if (do_uncompress) { - uint8_t *compressed_data; - size_t max_bytes; - ssize_t bytes; - - compressed_data = data; - max_bytes = UBOOT_MAX_GUNZIP_BYTES; - data = g_malloc(max_bytes); - - bytes = gunzip(data, max_bytes, compressed_data, hdr->ih_size); - g_free(compressed_data); - if (bytes < 0) { - fprintf(stderr, "Unable to decompress gzipped image!\n"); - goto out; - } - hdr->ih_size = bytes; - } - - rom_add_blob_fixed(filename, data, hdr->ih_size, address); - - ret = hdr->ih_size; - -out: - g_free(data); - close(fd); - return ret; -} - -int load_uimage(const char *filename, hwaddr *ep, hwaddr *loadaddr, - int *is_linux, - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque) -{ - return load_uboot_image(filename, ep, loadaddr, is_linux, IH_TYPE_KERNEL, - translate_fn, translate_opaque); -} - -/* Load a ramdisk. */ -int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz) -{ - return load_uboot_image(filename, NULL, &addr, NULL, IH_TYPE_RAMDISK, - NULL, NULL); -} - -/* Load a gzip-compressed kernel to a dynamically allocated buffer. */ -int load_image_gzipped_buffer(const char *filename, uint64_t max_sz, - uint8_t **buffer) -{ - uint8_t *compressed_data = NULL; - uint8_t *data = NULL; - gsize len; - ssize_t bytes; - int ret = -1; - - if (!g_file_get_contents(filename, (char **) &compressed_data, &len, - NULL)) { - goto out; - } - - /* Is it a gzip-compressed file? */ - if (len < 2 || - compressed_data[0] != 0x1f || - compressed_data[1] != 0x8b) { - goto out; - } - - if (max_sz > LOAD_IMAGE_MAX_GUNZIP_BYTES) { - max_sz = LOAD_IMAGE_MAX_GUNZIP_BYTES; - } - - data = g_malloc(max_sz); - bytes = gunzip(data, max_sz, compressed_data, len); - if (bytes < 0) { - fprintf(stderr, "%s: unable to decompress gzipped kernel file\n", - filename); - goto out; - } - - /* trim to actual size and return to caller */ - *buffer = g_realloc(data, bytes); - ret = bytes; - /* ownership has been transferred to caller */ - data = NULL; - - out: - g_free(compressed_data); - g_free(data); - return ret; -} - -/* Load a gzip-compressed kernel. */ -int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz) -{ - int bytes; - uint8_t *data; - - bytes = load_image_gzipped_buffer(filename, max_sz, &data); - if (bytes != -1) { - rom_add_blob_fixed(filename, data, bytes, addr); - g_free(data); - } - return bytes; -} - -/* - * Functions for reboot-persistent memory regions. - * - used for vga bios and option roms. - * - also linux kernel (-kernel / -initrd). - */ - -typedef struct Rom Rom; - -struct Rom { - char *name; - char *path; - - /* datasize is the amount of memory allocated in "data". If datasize is less - * than romsize, it means that the area from datasize to romsize is filled - * with zeros. - */ - size_t romsize; - size_t datasize; - - uint8_t *data; - MemoryRegion *mr; - int isrom; - char *fw_dir; - char *fw_file; - - hwaddr addr; - QTAILQ_ENTRY(Rom) next; -}; - -static FWCfgState *fw_cfg; -static QTAILQ_HEAD(, Rom) roms = QTAILQ_HEAD_INITIALIZER(roms); - -static void rom_insert(Rom *rom) -{ - Rom *item; - - if (roms_loaded) { - hw_error ("ROM images must be loaded at startup\n"); - } - - /* list is ordered by load address */ - QTAILQ_FOREACH(item, &roms, next) { - if (rom->addr >= item->addr) - continue; - QTAILQ_INSERT_BEFORE(item, rom, next); - return; - } - QTAILQ_INSERT_TAIL(&roms, rom, next); -} - -static void fw_cfg_resized(const char *id, uint64_t length, void *host) -{ - if (fw_cfg) { - fw_cfg_modify_file(fw_cfg, id + strlen("/rom@"), host, length); - } -} - -static void *rom_set_mr(Rom *rom, Object *owner, const char *name) -{ - void *data; - - rom->mr = g_malloc(sizeof(*rom->mr)); - memory_region_init_resizeable_ram(rom->mr, owner, name, - rom->datasize, rom->romsize, - fw_cfg_resized, - &error_fatal); - memory_region_set_readonly(rom->mr, true); - vmstate_register_ram_global(rom->mr); - - data = memory_region_get_ram_ptr(rom->mr); - memcpy(data, rom->data, rom->datasize); - - return data; -} - -int rom_add_file(const char *file, const char *fw_dir, - hwaddr addr, int32_t bootindex, - bool option_rom, MemoryRegion *mr) -{ - MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); - Rom *rom; - int rc, fd = -1; - char devpath[100]; - - rom = g_malloc0(sizeof(*rom)); - rom->name = g_strdup(file); - rom->path = qemu_find_file(QEMU_FILE_TYPE_BIOS, rom->name); - if (rom->path == NULL) { - rom->path = g_strdup(file); - } - - fd = open(rom->path, O_RDONLY | O_BINARY); - if (fd == -1) { - fprintf(stderr, "Could not open option rom '%s': %s\n", - rom->path, strerror(errno)); - goto err; - } - - if (fw_dir) { - rom->fw_dir = g_strdup(fw_dir); - rom->fw_file = g_strdup(file); - } - rom->addr = addr; - rom->romsize = lseek(fd, 0, SEEK_END); - if (rom->romsize == -1) { - fprintf(stderr, "rom: file %-20s: get size error: %s\n", - rom->name, strerror(errno)); - goto err; - } - - rom->datasize = rom->romsize; - rom->data = g_malloc0(rom->datasize); - lseek(fd, 0, SEEK_SET); - rc = read(fd, rom->data, rom->datasize); - if (rc != rom->datasize) { - fprintf(stderr, "rom: file %-20s: read error: rc=%d (expected %zd)\n", - rom->name, rc, rom->datasize); - goto err; - } - close(fd); - rom_insert(rom); - if (rom->fw_file && fw_cfg) { - const char *basename; - char fw_file_name[FW_CFG_MAX_FILE_PATH]; - void *data; - - basename = strrchr(rom->fw_file, '/'); - if (basename) { - basename++; - } else { - basename = rom->fw_file; - } - snprintf(fw_file_name, sizeof(fw_file_name), "%s/%s", rom->fw_dir, - basename); - snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); - - 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; - } - - fw_cfg_add_file(fw_cfg, fw_file_name, data, rom->romsize); - } else { - 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); - return 0; - -err: - if (fd != -1) - close(fd); - g_free(rom->data); - g_free(rom->path); - g_free(rom->name); - g_free(rom); - return -1; -} - -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; - - rom = g_malloc0(sizeof(*rom)); - rom->name = g_strdup(name); - rom->addr = addr; - rom->romsize = max_len ? max_len : len; - rom->datasize = len; - rom->data = g_malloc0(rom->datasize); - memcpy(rom->data, blob, len); - rom_insert(rom); - if (fw_file_name && fw_cfg) { - char devpath[100]; - void *data; - - snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); - - if (mc->rom_file_has_mr) { - data = rom_set_mr(rom, OBJECT(fw_cfg), devpath); - mr = rom->mr; - } else { - data = rom->data; - } - - fw_cfg_add_file_callback(fw_cfg, fw_file_name, - fw_callback, callback_opaque, - data, rom->datasize); - } - return mr; -} - -/* This function is specific for elf program because we don't need to allocate - * all the rom. We just allocate the first part and the rest is just zeros. This - * is why romsize and datasize are different. Also, this function seize the - * memory ownership of "data", so we don't have to allocate and copy the buffer. - */ -int rom_add_elf_program(const char *name, void *data, size_t datasize, - size_t romsize, hwaddr addr) -{ - Rom *rom; - - rom = g_malloc0(sizeof(*rom)); - rom->name = g_strdup(name); - rom->addr = addr; - rom->datasize = datasize; - rom->romsize = romsize; - rom->data = data; - rom_insert(rom); - return 0; -} - -int rom_add_vga(const char *file) -{ - 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, NULL); -} - -static void rom_reset(void *unused) -{ - Rom *rom; - - QTAILQ_FOREACH(rom, &roms, next) { - if (rom->fw_file) { - continue; - } - if (rom->data == NULL) { - continue; - } - if (rom->mr) { - void *host = memory_region_get_ram_ptr(rom->mr); - memcpy(host, rom->data, rom->datasize); - } else { - cpu_physical_memory_write_rom(&address_space_memory, - rom->addr, rom->data, rom->datasize); - } - if (rom->isrom) { - /* rom needs to be written only once */ - g_free(rom->data); - rom->data = NULL; - } - /* - * The rom loader is really on the same level as firmware in the guest - * shadowing a ROM into RAM. Such a shadowing mechanism needs to ensure - * that the instruction cache for that new region is clear, so that the - * CPU definitely fetches its instructions from the just written data. - */ - cpu_flush_icache_range(rom->addr, rom->datasize); - } -} - -int rom_check_and_register_reset(void) -{ - hwaddr addr = 0; - MemoryRegionSection section; - Rom *rom; - - QTAILQ_FOREACH(rom, &roms, next) { - if (rom->fw_file) { - continue; - } - if (addr > rom->addr) { - fprintf(stderr, "rom: requested regions overlap " - "(rom %s. free=0x" TARGET_FMT_plx - ", addr=0x" TARGET_FMT_plx ")\n", - rom->name, addr, rom->addr); - return -1; - } - addr = rom->addr; - addr += rom->romsize; - section = memory_region_find(get_system_memory(), rom->addr, 1); - rom->isrom = int128_nz(section.size) && memory_region_is_rom(section.mr); - memory_region_unref(section.mr); - } - qemu_register_reset(rom_reset, NULL); - roms_loaded = 1; - return 0; -} - -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; - - QTAILQ_FOREACH(rom, &roms, next) { - if (rom->fw_file) { - continue; - } - if (rom->mr) { - continue; - } - if (rom->addr > addr) { - continue; - } - if (rom->addr + rom->romsize < addr) { - continue; - } - return rom; - } - return NULL; -} - -/* - * Copies memory from registered ROMs to dest. Any memory that is contained in - * a ROM between addr and addr + size is copied. Note that this can involve - * multiple ROMs, which need not start at addr and need not end at addr + size. - */ -int rom_copy(uint8_t *dest, hwaddr addr, size_t size) -{ - hwaddr end = addr + size; - uint8_t *s, *d = dest; - size_t l = 0; - Rom *rom; - - QTAILQ_FOREACH(rom, &roms, next) { - if (rom->fw_file) { - continue; - } - if (rom->mr) { - continue; - } - if (rom->addr + rom->romsize < addr) { - continue; - } - if (rom->addr > end) { - break; - } - - d = dest + (rom->addr - addr); - s = rom->data; - l = rom->datasize; - - if ((d + l) > (dest + size)) { - l = dest - d; - } - - if (l > 0) { - memcpy(d, s, l); - } - - if (rom->romsize > rom->datasize) { - /* If datasize is less than romsize, it means that we didn't - * allocate all the ROM because the trailing data are only zeros. - */ - - d += l; - l = rom->romsize - rom->datasize; - - if ((d + l) > (dest + size)) { - /* Rom size doesn't fit in the destination area. Adjust to avoid - * overflow. - */ - l = dest - d; - } - - if (l > 0) { - memset(d, 0x0, l); - } - } - } - - return (d + l) - dest; -} - -void *rom_ptr(hwaddr addr) -{ - Rom *rom; - - rom = find_rom(addr); - if (!rom || !rom->data) - return NULL; - return rom->data + (addr - rom->addr); -} - -void hmp_info_roms(Monitor *mon, const QDict *qdict) -{ - Rom *rom; - - QTAILQ_FOREACH(rom, &roms, next) { - if (rom->mr) { - monitor_printf(mon, "%s" - " size=0x%06zx name=\"%s\"\n", - memory_region_name(rom->mr), - rom->romsize, - rom->name); - } else if (!rom->fw_file) { - monitor_printf(mon, "addr=" TARGET_FMT_plx - " size=0x%06zx mem=%s name=\"%s\"\n", - rom->addr, rom->romsize, - rom->isrom ? "rom" : "ram", - rom->name); - } else { - monitor_printf(mon, "fw=%s/%s" - " size=0x%06zx name=\"%s\"\n", - rom->fw_dir, - rom->fw_file, - rom->romsize, - rom->name); - } - } -} diff --git a/qemu/hw/core/machine.c b/qemu/hw/core/machine.c deleted file mode 100644 index 6dbbc85b9..000000000 --- a/qemu/hw/core/machine.c +++ /dev/null @@ -1,570 +0,0 @@ -/* - * QEMU Machine - * - * Copyright (C) 2014 Red Hat Inc - * - * Authors: - * Marcel Apfelbaum - * - * 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/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) -{ - MachineState *ms = MACHINE(obj); - - return g_strdup(ms->accel); -} - -static void machine_set_accel(Object *obj, const char *value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - g_free(ms->accel); - ms->accel = g_strdup(value); -} - -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; - - 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, - const char *name, void *opaque, - Error **errp) -{ - MachineState *ms = MACHINE(obj); - int64_t value = ms->kvm_shadow_mem; - - visit_type_int(v, name, &value, errp); -} - -static void machine_set_kvm_shadow_mem(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - MachineState *ms = MACHINE(obj); - Error *error = NULL; - int64_t value; - - visit_type_int(v, name, &value, &error); - if (error) { - error_propagate(errp, error); - return; - } - - ms->kvm_shadow_mem = value; -} - -static char *machine_get_kernel(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - return g_strdup(ms->kernel_filename); -} - -static void machine_set_kernel(Object *obj, const char *value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - g_free(ms->kernel_filename); - ms->kernel_filename = g_strdup(value); -} - -static char *machine_get_initrd(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - return g_strdup(ms->initrd_filename); -} - -static void machine_set_initrd(Object *obj, const char *value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - g_free(ms->initrd_filename); - ms->initrd_filename = g_strdup(value); -} - -static char *machine_get_append(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - return g_strdup(ms->kernel_cmdline); -} - -static void machine_set_append(Object *obj, const char *value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - g_free(ms->kernel_cmdline); - ms->kernel_cmdline = g_strdup(value); -} - -static char *machine_get_dtb(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - return g_strdup(ms->dtb); -} - -static void machine_set_dtb(Object *obj, const char *value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - g_free(ms->dtb); - ms->dtb = g_strdup(value); -} - -static char *machine_get_dumpdtb(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - return g_strdup(ms->dumpdtb); -} - -static void machine_set_dumpdtb(Object *obj, const char *value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - g_free(ms->dumpdtb); - ms->dumpdtb = g_strdup(value); -} - -static void machine_get_phandle_start(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - MachineState *ms = MACHINE(obj); - int64_t value = ms->phandle_start; - - visit_type_int(v, name, &value, errp); -} - -static void machine_set_phandle_start(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - MachineState *ms = MACHINE(obj); - Error *error = NULL; - int64_t value; - - visit_type_int(v, name, &value, &error); - if (error) { - error_propagate(errp, error); - return; - } - - ms->phandle_start = value; -} - -static char *machine_get_dt_compatible(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - return g_strdup(ms->dt_compatible); -} - -static void machine_set_dt_compatible(Object *obj, const char *value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - g_free(ms->dt_compatible); - ms->dt_compatible = g_strdup(value); -} - -static bool machine_get_dump_guest_core(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - return ms->dump_guest_core; -} - -static void machine_set_dump_guest_core(Object *obj, bool value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - ms->dump_guest_core = value; -} - -static bool machine_get_mem_merge(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - return ms->mem_merge; -} - -static void machine_set_mem_merge(Object *obj, bool value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - ms->mem_merge = value; -} - -static bool machine_get_usb(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - return ms->usb; -} - -static void machine_set_usb(Object *obj, bool value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - ms->usb = value; - 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); - - return g_strdup(ms->firmware); -} - -static void machine_set_firmware(Object *obj, const char *value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - g_free(ms->firmware); - ms->firmware = g_strdup(value); -} - -static bool machine_get_iommu(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - return ms->iommu; -} - -static void machine_set_iommu(Object *obj, bool value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - ms->iommu = value; -} - -static void machine_set_suppress_vmdesc(Object *obj, bool value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - ms->suppress_vmdesc = value; -} - -static bool machine_get_suppress_vmdesc(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - 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", - object_class_get_name(object_get_class(OBJECT(sbdev)))); - exit(1); -} - -static void machine_init_notify(Notifier *notifier, void *data) -{ - Object *machine = qdev_get_machine(); - ObjectClass *oc = object_get_class(machine); - MachineClass *mc = MACHINE_CLASS(oc); - - if (mc->has_dynamic_sysbus) { - /* Our machine can handle dynamic sysbus devices, we're all good */ - return; - } - - /* - * Loop through all dynamically created devices and check whether there - * are sysbus devices among them. If there are, error out. - */ - foreach_dynamic_sysbus_device(error_on_sysbus_device, NULL); -} - -static void machine_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - /* 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) -{ - MachineState *ms = MACHINE(obj); - - ms->kernel_irqchip_allowed = true; - ms->kvm_shadow_mem = -1; - ms->dump_guest_core = true; - ms->mem_merge = true; - - object_property_add_str(obj, "accel", - machine_get_accel, machine_set_accel, NULL); - object_property_set_description(obj, "accel", - "Accelerator list", - NULL); - object_property_add(obj, "kernel-irqchip", "OnOffSplit", - NULL, - machine_set_kernel_irqchip, - NULL, NULL, NULL); - object_property_set_description(obj, "kernel-irqchip", - "Configure KVM in-kernel irqchip", - NULL); - object_property_add(obj, "kvm-shadow-mem", "int", - machine_get_kvm_shadow_mem, - machine_set_kvm_shadow_mem, - NULL, NULL, NULL); - object_property_set_description(obj, "kvm-shadow-mem", - "KVM shadow MMU size", - NULL); - object_property_add_str(obj, "kernel", - machine_get_kernel, machine_set_kernel, NULL); - object_property_set_description(obj, "kernel", - "Linux kernel image file", - NULL); - object_property_add_str(obj, "initrd", - machine_get_initrd, machine_set_initrd, NULL); - object_property_set_description(obj, "initrd", - "Linux initial ramdisk file", - NULL); - object_property_add_str(obj, "append", - machine_get_append, machine_set_append, NULL); - object_property_set_description(obj, "append", - "Linux kernel command line", - NULL); - object_property_add_str(obj, "dtb", - machine_get_dtb, machine_set_dtb, NULL); - object_property_set_description(obj, "dtb", - "Linux kernel device tree file", - NULL); - object_property_add_str(obj, "dumpdtb", - machine_get_dumpdtb, machine_set_dumpdtb, NULL); - object_property_set_description(obj, "dumpdtb", - "Dump current dtb to a file and quit", - NULL); - object_property_add(obj, "phandle-start", "int", - machine_get_phandle_start, - machine_set_phandle_start, - NULL, NULL, NULL); - object_property_set_description(obj, "phandle-start", - "The first phandle ID we may generate dynamically", - NULL); - object_property_add_str(obj, "dt-compatible", - machine_get_dt_compatible, - machine_set_dt_compatible, - NULL); - object_property_set_description(obj, "dt-compatible", - "Overrides the \"compatible\" property of the dt root node", - NULL); - object_property_add_bool(obj, "dump-guest-core", - machine_get_dump_guest_core, - machine_set_dump_guest_core, - NULL); - object_property_set_description(obj, "dump-guest-core", - "Include guest memory in a core dump", - NULL); - object_property_add_bool(obj, "mem-merge", - machine_get_mem_merge, - machine_set_mem_merge, NULL); - object_property_set_description(obj, "mem-merge", - "Enable/disable memory merge support", - NULL); - object_property_add_bool(obj, "usb", - machine_get_usb, - machine_set_usb, NULL); - 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); - object_property_set_description(obj, "firmware", - "Firmware image", - NULL); - object_property_add_bool(obj, "iommu", - machine_get_iommu, - machine_set_iommu, NULL); - object_property_set_description(obj, "iommu", - "Set on/off to enable/disable Intel IOMMU (VT-d)", - NULL); - object_property_add_bool(obj, "suppress-vmdesc", - machine_get_suppress_vmdesc, - machine_set_suppress_vmdesc, NULL); - 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; - qemu_add_machine_init_done_notifier(&ms->sysbus_notifier); -} - -static void machine_finalize(Object *obj) -{ - MachineState *ms = MACHINE(obj); - - g_free(ms->accel); - g_free(ms->kernel_filename); - g_free(ms->initrd_filename); - g_free(ms->kernel_cmdline); - g_free(ms->dtb); - g_free(ms->dumpdtb); - g_free(ms->dt_compatible); - g_free(ms->firmware); -} - -bool machine_usb(MachineState *machine) -{ - return machine->usb; -} - -bool machine_kernel_irqchip_allowed(MachineState *machine) -{ - return machine->kernel_irqchip_allowed; -} - -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; -} - -int machine_phandle_start(MachineState *machine) -{ - return machine->phandle_start; -} - -bool machine_dump_guest_core(MachineState *machine) -{ - return machine->dump_guest_core; -} - -bool machine_mem_merge(MachineState *machine) -{ - return machine->mem_merge; -} - -static const TypeInfo machine_info = { - .name = TYPE_MACHINE, - .parent = TYPE_OBJECT, - .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, -}; - -static void machine_register_types(void) -{ - type_register_static(&machine_info); -} - -type_init(machine_register_types) diff --git a/qemu/hw/core/nmi.c b/qemu/hw/core/nmi.c deleted file mode 100644 index e8bcc4177..000000000 --- a/qemu/hw/core/nmi.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * NMI monitor handler class and helpers. - * - * Copyright IBM Corp., 2014 - * - * Author: Alexey Kardashevskiy - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/nmi.h" -#include "qapi/error.h" -#include "qapi/qmp/qerror.h" -#include "monitor/monitor.h" - -struct do_nmi_s { - int cpu_index; - Error *err; - bool handled; -}; - -static void nmi_children(Object *o, struct do_nmi_s *ns); - -static int do_nmi(Object *o, void *opaque) -{ - struct do_nmi_s *ns = opaque; - NMIState *n = (NMIState *) object_dynamic_cast(o, TYPE_NMI); - - if (n) { - NMIClass *nc = NMI_GET_CLASS(n); - - ns->handled = true; - nc->nmi_monitor_handler(n, ns->cpu_index, &ns->err); - if (ns->err) { - return -1; - } - } - nmi_children(o, ns); - - return 0; -} - -static void nmi_children(Object *o, struct do_nmi_s *ns) -{ - object_child_foreach(o, do_nmi, ns); -} - -void nmi_monitor_handle(int cpu_index, Error **errp) -{ - struct do_nmi_s ns = { - .cpu_index = cpu_index, - .err = NULL, - .handled = false - }; - - nmi_children(object_get_root(), &ns); - if (ns.handled) { - error_propagate(errp, ns.err); - } else { - error_setg(errp, QERR_UNSUPPORTED); - } -} - -void inject_nmi(void) -{ -#if defined(TARGET_I386) - CPUState *cs; - - CPU_FOREACH(cs) { - X86CPU *cpu = X86_CPU(cs); - - if (!cpu->apic_state) { - cpu_interrupt(cs, CPU_INTERRUPT_NMI); - } else { - apic_deliver_nmi(cpu->apic_state); - } - } -#else - nmi_monitor_handle(0, NULL); -#endif -} - -static const TypeInfo nmi_info = { - .name = TYPE_NMI, - .parent = TYPE_INTERFACE, - .class_size = sizeof(NMIClass), -}; - -static void nmi_register_types(void) -{ - type_register_static(&nmi_info); -} - -type_init(nmi_register_types) diff --git a/qemu/hw/core/null-machine.c b/qemu/hw/core/null-machine.c deleted file mode 100644 index 0351ba782..000000000 --- a/qemu/hw/core/null-machine.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Empty machine - * - * Copyright IBM, Corp. 2012 - * - * Authors: - * Anthony Liguori - * - * 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 "hw/hw.h" -#include "hw/boards.h" - -static void machine_none_init(MachineState *machine) -{ -} - -static void machine_none_machine_init(MachineClass *mc) -{ - mc->desc = "empty machine"; - mc->init = machine_none_init; - mc->max_cpus = 0; -} - -DEFINE_MACHINE("none", machine_none_machine_init) diff --git a/qemu/hw/core/platform-bus.c b/qemu/hw/core/platform-bus.c deleted file mode 100644 index 36f84ab72..000000000 --- a/qemu/hw/core/platform-bus.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Platform Bus device to support dynamic Sysbus devices - * - * Copyright (C) 2014 Freescale Semiconductor, Inc. All rights reserved. - * - * Author: Alexander Graf, - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/platform-bus.h" -#include "exec/address-spaces.h" -#include "qemu/error-report.h" -#include "sysemu/sysemu.h" - - -/* - * Returns the PlatformBus IRQ number for a SysBusDevice irq number or -1 if - * the IRQ is not mapped on this Platform bus. - */ -int platform_bus_get_irqn(PlatformBusDevice *pbus, SysBusDevice *sbdev, - int n) -{ - qemu_irq sbirq = sysbus_get_connected_irq(sbdev, n); - int i; - - for (i = 0; i < pbus->num_irqs; i++) { - if (pbus->irqs[i] == sbirq) { - return i; - } - } - - /* IRQ not mapped on platform bus */ - return -1; -} - -/* - * Returns the PlatformBus MMIO region offset for Region n of a SysBusDevice or - * -1 if the region is not mapped on this Platform bus. - */ -hwaddr platform_bus_get_mmio_addr(PlatformBusDevice *pbus, SysBusDevice *sbdev, - int n) -{ - MemoryRegion *pbus_mr = &pbus->mmio; - MemoryRegion *sbdev_mr = sysbus_mmio_get_region(sbdev, n); - Object *pbus_mr_obj = OBJECT(pbus_mr); - Object *parent_mr; - - if (!memory_region_is_mapped(sbdev_mr)) { - /* Region is not mapped? */ - return -1; - } - - parent_mr = object_property_get_link(OBJECT(sbdev_mr), "container", NULL); - - assert(parent_mr); - if (parent_mr != pbus_mr_obj) { - /* MMIO region is not mapped on platform bus */ - return -1; - } - - return object_property_get_int(OBJECT(sbdev_mr), "addr", NULL); -} - -static int platform_bus_count_irqs(SysBusDevice *sbdev, void *opaque) -{ - PlatformBusDevice *pbus = opaque; - qemu_irq sbirq; - int n, i; - - for (n = 0; ; n++) { - if (!sysbus_has_irq(sbdev, n)) { - break; - } - - sbirq = sysbus_get_connected_irq(sbdev, n); - for (i = 0; i < pbus->num_irqs; i++) { - if (pbus->irqs[i] == sbirq) { - bitmap_set(pbus->used_irqs, i, 1); - break; - } - } - } - - return 0; -} - -/* - * Loop through all sysbus devices and look for unassigned IRQ lines as well as - * unassociated MMIO regions. Connect them to the platform bus if available. - */ -static void plaform_bus_refresh_irqs(PlatformBusDevice *pbus) -{ - bitmap_zero(pbus->used_irqs, pbus->num_irqs); - foreach_dynamic_sysbus_device(platform_bus_count_irqs, pbus); - pbus->done_gathering = true; -} - -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; - } - - irqn = find_first_zero_bit(pbus->used_irqs, max_irqs); - if (irqn >= max_irqs) { - 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]); -} - -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); - uint64_t alignment = (1ULL << (63 - clz64(size + size - 1))); - uint64_t off; - bool found_region = false; - - if (memory_region_is_mapped(sbdev_mr)) { - /* Region is already mapped, nothing to do */ - return; - } - - /* - * Look for empty space in the MMIO space that is naturally aligned with - * the target device's memory region - */ - for (off = 0; off < pbus->mmio_size; off += alignment) { - if (!memory_region_find(&pbus->mmio, off, size).mr) { - found_region = true; - break; - } - } - - if (!found_region) { - 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); -} - -/* - * For each sysbus device, look for unassigned IRQ lines as well as - * unassociated MMIO regions. Connect them to the platform bus if available. - */ -static int link_sysbus_device(SysBusDevice *sbdev, void *opaque) -{ - PlatformBusDevice *pbus = opaque; - int i; - - for (i = 0; sysbus_has_irq(sbdev, i); i++) { - platform_bus_map_irq(pbus, sbdev, i); - } - - for (i = 0; sysbus_has_mmio(sbdev, i); i++) { - platform_bus_map_mmio(pbus, sbdev, i); - } - - return 0; -} - -static void platform_bus_init_notify(Notifier *notifier, void *data) -{ - PlatformBusDevice *pb = container_of(notifier, PlatformBusDevice, notifier); - - /* - * Generate a bitmap of used IRQ lines, as the user might have specified - * them on the command line. - */ - plaform_bus_refresh_irqs(pb); - - foreach_dynamic_sysbus_device(link_sysbus_device, pb); -} - -static void platform_bus_realize(DeviceState *dev, Error **errp) -{ - PlatformBusDevice *pbus; - SysBusDevice *d; - int i; - - d = SYS_BUS_DEVICE(dev); - pbus = PLATFORM_BUS_DEVICE(dev); - - memory_region_init(&pbus->mmio, NULL, "platform bus", pbus->mmio_size); - sysbus_init_mmio(d, &pbus->mmio); - - pbus->used_irqs = bitmap_new(pbus->num_irqs); - pbus->irqs = g_new0(qemu_irq, pbus->num_irqs); - for (i = 0; i < pbus->num_irqs; i++) { - sysbus_init_irq(d, &pbus->irqs[i]); - } - - /* - * Register notifier that allows us to gather dangling devices once the - * machine is completely assembled - */ - pbus->notifier.notify = platform_bus_init_notify; - qemu_add_machine_init_done_notifier(&pbus->notifier); -} - -static Property platform_bus_properties[] = { - DEFINE_PROP_UINT32("num_irqs", PlatformBusDevice, num_irqs, 0), - DEFINE_PROP_UINT32("mmio_size", PlatformBusDevice, mmio_size, 0), - DEFINE_PROP_END_OF_LIST() -}; - -static void platform_bus_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = platform_bus_realize; - dc->props = platform_bus_properties; -} - -static const TypeInfo platform_bus_info = { - .name = TYPE_PLATFORM_BUS_DEVICE, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PlatformBusDevice), - .class_init = platform_bus_class_init, -}; - -static void platform_bus_register_types(void) -{ - type_register_static(&platform_bus_info); -} - -type_init(platform_bus_register_types) diff --git a/qemu/hw/core/ptimer.c b/qemu/hw/core/ptimer.c deleted file mode 100644 index 153c83513..000000000 --- a/qemu/hw/core/ptimer.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * General purpose implementation of a simple periodic countdown timer. - * - * Copyright (c) 2007 CodeSourcery. - * - * 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 -{ - uint8_t enabled; /* 0 = disabled, 1 = periodic, 2 = oneshot. */ - uint64_t limit; - uint64_t delta; - uint32_t period_frac; - int64_t period; - int64_t last_event; - int64_t next_event; - QEMUBH *bh; - QEMUTimer *timer; -}; - -/* Use a bottom-half routine to avoid reentrancy issues. */ -static void ptimer_trigger(ptimer_state *s) -{ - if (s->bh) { - replay_bh_schedule_event(s->bh); - } -} - -static void ptimer_reload(ptimer_state *s) -{ - if (s->delta == 0) { - ptimer_trigger(s); - s->delta = s->limit; - } - if (s->delta == 0 || s->period == 0) { - fprintf(stderr, "Timer with period zero, disabling\n"); - s->enabled = 0; - return; - } - - s->last_event = s->next_event; - s->next_event = s->last_event + s->delta * s->period; - if (s->period_frac) { - s->next_event += ((int64_t)s->period_frac * s->delta) >> 32; - } - timer_mod(s->timer, s->next_event); -} - -static void ptimer_tick(void *opaque) -{ - ptimer_state *s = (ptimer_state *)opaque; - ptimer_trigger(s); - s->delta = 0; - if (s->enabled == 2) { - s->enabled = 0; - } else { - ptimer_reload(s); - } -} - -uint64_t ptimer_get_count(ptimer_state *s) -{ - int64_t now; - uint64_t counter; - - if (s->enabled) { - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - /* Figure out the current counter value. */ - if (now - s->next_event > 0 - || s->period == 0) { - /* Prevent timer underflowing if it should already have - triggered. */ - counter = 0; - } else { - uint64_t rem; - uint64_t div; - int clz1, clz2; - int shift; - - /* We need to divide time by period, where time is stored in - rem (64-bit integer) and period is stored in period/period_frac - (64.32 fixed point). - - Doing full precision division is hard, so scale values and - do a 64-bit division. The result should be rounded down, - so that the rounding error never causes the timer to go - backwards. - */ - - rem = s->next_event - now; - div = s->period; - - clz1 = clz64(rem); - clz2 = clz64(div); - shift = clz1 < clz2 ? clz1 : clz2; - - rem <<= shift; - div <<= shift; - if (shift >= 32) { - div |= ((uint64_t)s->period_frac << (shift - 32)); - } else { - if (shift != 0) - div |= (s->period_frac >> (32 - shift)); - /* Look at remaining bits of period_frac and round div up if - necessary. */ - if ((uint32_t)(s->period_frac << shift)) - div += 1; - } - counter = rem / div; - } - } else { - counter = s->delta; - } - return counter; -} - -void ptimer_set_count(ptimer_state *s, uint64_t count) -{ - s->delta = count; - if (s->enabled) { - s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s); - } -} - -void ptimer_run(ptimer_state *s, int oneshot) -{ - if (s->enabled) { - return; - } - if (s->period == 0) { - fprintf(stderr, "Timer with period zero, disabling\n"); - return; - } - s->enabled = oneshot ? 2 : 1; - s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s); -} - -/* Pause a timer. Note that this may cause it to "lose" time, even if it - is immediately restarted. */ -void ptimer_stop(ptimer_state *s) -{ - if (!s->enabled) - return; - - s->delta = ptimer_get_count(s); - timer_del(s->timer); - s->enabled = 0; -} - -/* Set counter increment interval in nanoseconds. */ -void ptimer_set_period(ptimer_state *s, int64_t period) -{ - s->period = period; - s->period_frac = 0; - if (s->enabled) { - s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s); - } -} - -/* Set counter frequency in Hz. */ -void ptimer_set_freq(ptimer_state *s, uint32_t freq) -{ - s->period = 1000000000ll / freq; - s->period_frac = (1000000000ll << 32) / freq; - if (s->enabled) { - s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s); - } -} - -/* Set the initial countdown value. If reload is nonzero then also set - count = limit. */ -void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload) -{ - /* - * Artificially limit timeout rate to something - * achievable under QEMU. Otherwise, QEMU spends all - * its time generating timer interrupts, and there - * is no forward progress. - * About ten microseconds is the fastest that really works - * on the current generation of host machines. - */ - - if (!use_icount && limit * s->period < 10000 && s->period) { - limit = 10000 / s->period; - } - - s->limit = limit; - if (reload) - s->delta = limit; - if (s->enabled && reload) { - s->next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ptimer_reload(s); - } -} - -const VMStateDescription vmstate_ptimer = { - .name = "ptimer", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(enabled, ptimer_state), - VMSTATE_UINT64(limit, ptimer_state), - VMSTATE_UINT64(delta, ptimer_state), - VMSTATE_UINT32(period_frac, ptimer_state), - VMSTATE_INT64(period, ptimer_state), - VMSTATE_INT64(last_event, ptimer_state), - VMSTATE_INT64(next_event, ptimer_state), - VMSTATE_TIMER_PTR(timer, ptimer_state), - VMSTATE_END_OF_LIST() - } -}; - -ptimer_state *ptimer_init(QEMUBH *bh) -{ - ptimer_state *s; - - s = (ptimer_state *)g_malloc0(sizeof(ptimer_state)); - s->bh = bh; - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ptimer_tick, s); - return s; -} diff --git a/qemu/hw/core/qdev-properties-system.c b/qemu/hw/core/qdev-properties-system.c deleted file mode 100644 index 891219ae0..000000000 --- a/qemu/hw/core/qdev-properties-system.c +++ /dev/null @@ -1,415 +0,0 @@ -/* - * qdev property parsing and global properties - * (parts specific for qemu-system-*) - * - * This file is based on code from hw/qdev-properties.c from - * commit 074a86fccd185616469dfcdc0e157f438aebba18, - * Copyright (c) Gerd Hoffmann and other contributors. - * - * 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 "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" -#include "hw/block/block.h" -#include "net/hub.h" -#include "qapi/visitor.h" -#include "sysemu/char.h" -#include "sysemu/iothread.h" - -static void get_pointer(Object *obj, Visitor *v, Property *prop, - char *(*print)(void *ptr), - const char *name, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - void **ptr = qdev_get_prop_ptr(dev, prop); - char *p; - - p = *ptr ? print(*ptr) : g_strdup(""); - visit_type_str(v, name, &p, errp); - g_free(p); -} - -static void set_pointer(Object *obj, Visitor *v, Property *prop, - void (*parse)(DeviceState *dev, const char *str, - void **ptr, const char *propname, - Error **errp), - const char *name, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Error *local_err = NULL; - void **ptr = qdev_get_prop_ptr(dev, prop); - char *str; - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_str(v, name, &str, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - if (!*str) { - g_free(str); - *ptr = NULL; - return; - } - parse(dev, str, ptr, prop->name, errp); - g_free(str); -} - -/* --- drive --- */ - -static void parse_drive(DeviceState *dev, const char *str, void **ptr, - const char *propname, Error **errp) -{ - BlockBackend *blk; - - blk = blk_by_name(str); - if (!blk) { - error_setg(errp, "Property '%s.%s' can't find value '%s'", - object_get_typename(OBJECT(dev)), propname, str); - return; - } - if (blk_attach_dev(blk, dev) < 0) { - DriveInfo *dinfo = blk_legacy_dinfo(blk); - - if (dinfo->type != IF_NONE) { - error_setg(errp, "Drive '%s' is already in use because " - "it has been automatically connected to another " - "device (did you need 'if=none' in the drive options?)", - str); - } else { - error_setg(errp, "Drive '%s' is already in use by another device", - str); - } - return; - } - *ptr = blk; -} - -static void release_drive(Object *obj, const char *name, void *opaque) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - BlockBackend **ptr = qdev_get_prop_ptr(dev, prop); - - if (*ptr) { - blk_detach_dev(*ptr, dev); - blockdev_auto_del(*ptr); - } -} - -static char *print_drive(void *ptr) -{ - return g_strdup(blk_name(ptr)); -} - -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, const char *name, void *opaque, - Error **errp) -{ - set_pointer(obj, v, opaque, parse_drive, name, errp); -} - -PropertyInfo qdev_prop_drive = { - .name = "str", - .description = "ID of a drive to use as a backend", - .get = get_drive, - .set = set_drive, - .release = release_drive, -}; - -/* --- character device --- */ - -static void parse_chr(DeviceState *dev, const char *str, void **ptr, - const char *propname, Error **errp) -{ - CharDriverState *chr = qemu_chr_find(str); - if (chr == NULL) { - error_setg(errp, "Property '%s.%s' can't find value '%s'", - object_get_typename(OBJECT(dev)), propname, str); - return; - } - if (qemu_chr_fe_claim(chr) != 0) { - error_setg(errp, "Property '%s.%s' can't take value '%s', it's in use", - object_get_typename(OBJECT(dev)), propname, str); - return; - } - *ptr = chr; -} - -static void release_chr(Object *obj, const char *name, void *opaque) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - CharDriverState **ptr = qdev_get_prop_ptr(dev, prop); - CharDriverState *chr = *ptr; - - if (chr) { - qemu_chr_add_handlers(chr, NULL, NULL, NULL, NULL); - qemu_chr_fe_release(chr); - } -} - - -static char *print_chr(void *ptr) -{ - CharDriverState *chr = ptr; - const char *val = chr->label ? chr->label : ""; - - return g_strdup(val); -} - -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, const char *name, void *opaque, - Error **errp) -{ - set_pointer(obj, v, opaque, parse_chr, name, errp); -} - -PropertyInfo qdev_prop_chr = { - .name = "str", - .description = "ID of a chardev to use as a backend", - .get = get_chr, - .set = set_chr, - .release = release_chr, -}; - -/* --- netdev device --- */ -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, name, &p, errp); - g_free(p); -} - -static void set_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); - NetClientState **ncs = peers_ptr->ncs; - NetClientState *peers[MAX_QUEUE_NUM]; - Error *local_err = NULL; - int queues, err = 0, i = 0; - char *str; - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_str(v, name, &str, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - queues = qemu_find_net_clients_except(str, peers, - NET_CLIENT_OPTIONS_KIND_NIC, - MAX_QUEUE_NUM); - if (queues == 0) { - err = -ENOENT; - goto out; - } - - if (queues > MAX_QUEUE_NUM) { - error_setg(errp, "queues of backend '%s'(%d) exceeds QEMU limitation(%d)", - str, queues, MAX_QUEUE_NUM); - goto out; - } - - for (i = 0; i < queues; i++) { - if (peers[i] == NULL) { - err = -ENOENT; - goto out; - } - - if (peers[i]->peer) { - err = -EEXIST; - goto out; - } - - if (ncs[i]) { - err = -EINVAL; - goto out; - } - - ncs[i] = peers[i]; - ncs[i]->queue_index = i; - } - - peers_ptr->queues = queues; - -out: - error_set_from_qdev_prop_error(errp, err, dev, prop, str); - g_free(str); -} - -PropertyInfo qdev_prop_netdev = { - .name = "str", - .description = "ID of a netdev to use as a backend", - .get = get_netdev, - .set = set_netdev, -}; - -/* --- vlan --- */ - -static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len) -{ - NetClientState **ptr = qdev_get_prop_ptr(dev, prop); - - if (*ptr) { - int id; - if (!net_hub_id_for_client(*ptr, &id)) { - return snprintf(dest, len, "%d", id); - } - } - - return snprintf(dest, len, ""); -} - -static void get_vlan(Object *obj, Visitor *v, const char *name, void *opaque, - Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - NetClientState **ptr = qdev_get_prop_ptr(dev, prop); - int32_t id = -1; - - if (*ptr) { - int hub_id; - if (!net_hub_id_for_client(*ptr, &hub_id)) { - id = hub_id; - } - } - - visit_type_int32(v, name, &id, errp); -} - -static void set_vlan(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); - NetClientState **ptr = &peers_ptr->ncs[0]; - Error *local_err = NULL; - int32_t id; - NetClientState *hubport; - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_int32(v, name, &id, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - if (id == -1) { - *ptr = NULL; - return; - } - if (*ptr) { - error_set_from_qdev_prop_error(errp, -EINVAL, dev, prop, name); - return; - } - - hubport = net_hub_port_find(id); - if (!hubport) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - name, prop->info->name); - return; - } - *ptr = hubport; -} - -PropertyInfo qdev_prop_vlan = { - .name = "int32", - .description = "Integer VLAN id to connect to", - .print = print_vlan, - .get = get_vlan, - .set = set_vlan, -}; - -void qdev_prop_set_drive(DeviceState *dev, const char *name, - BlockBackend *value, Error **errp) -{ - object_property_set_str(OBJECT(dev), value ? blk_name(value) : "", - name, errp); -} - -void qdev_prop_set_chr(DeviceState *dev, const char *name, - CharDriverState *value) -{ - assert(!value || value->label); - object_property_set_str(OBJECT(dev), - value ? value->label : "", name, &error_abort); -} - -void qdev_prop_set_netdev(DeviceState *dev, const char *name, - NetClientState *value) -{ - assert(!value || value->name); - object_property_set_str(OBJECT(dev), - value ? value->name : "", name, &error_abort); -} - -void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) -{ - qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a); - if (nd->netdev) { - qdev_prop_set_netdev(dev, "netdev", nd->netdev); - } - if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED && - object_property_find(OBJECT(dev), "vectors", NULL)) { - qdev_prop_set_uint32(dev, "vectors", nd->nvectors); - } - nd->instantiated = 1; -} - -static int qdev_add_one_global(void *opaque, QemuOpts *opts, Error **errp) -{ - GlobalProperty *g; - - g = g_malloc0(sizeof(*g)); - g->driver = qemu_opt_get(opts, "driver"); - g->property = qemu_opt_get(opts, "property"); - g->value = qemu_opt_get(opts, "value"); - g->user_provided = true; - qdev_prop_register_global(g); - return 0; -} - -void qemu_add_globals(void) -{ - qemu_opts_foreach(qemu_find_opts("global"), - qdev_add_one_global, NULL, NULL); -} diff --git a/qemu/hw/core/qdev-properties.c b/qemu/hw/core/qdev-properties.c deleted file mode 100644 index 737d29c63..000000000 --- a/qemu/hw/core/qdev-properties.c +++ /dev/null @@ -1,1131 +0,0 @@ -#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" -#include "hw/block/block.h" -#include "net/hub.h" -#include "qapi/visitor.h" -#include "sysemu/char.h" - -void qdev_prop_set_after_realize(DeviceState *dev, const char *name, - Error **errp) -{ - if (dev->id) { - error_setg(errp, "Attempt to set property '%s' on device '%s' " - "(type '%s') after it was realized", name, dev->id, - object_get_typename(OBJECT(dev))); - } else { - error_setg(errp, "Attempt to set property '%s' on anonymous device " - "(type '%s') after it was realized", name, - object_get_typename(OBJECT(dev))); - } -} - -void qdev_prop_allow_set_link_before_realize(Object *obj, const char *name, - Object *val, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - - if (dev->realized) { - error_setg(errp, "Attempt to set link property '%s' on device '%s' " - "(type '%s') after it was realized", - name, dev->id, object_get_typename(obj)); - } -} - -void *qdev_get_prop_ptr(DeviceState *dev, Property *prop) -{ - void *ptr = dev; - ptr += prop->offset; - return ptr; -} - -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, prop->name, ptr, prop->info->enum_table, errp); -} - -static void set_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); - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_enum(v, prop->name, ptr, prop->info->enum_table, errp); -} - -/* Bit */ - -static uint32_t qdev_get_prop_mask(Property *prop) -{ - assert(prop->info == &qdev_prop_bit); - return 0x1 << prop->bitnr; -} - -static void bit_prop_set(DeviceState *dev, Property *props, bool val) -{ - uint32_t *p = qdev_get_prop_ptr(dev, props); - uint32_t mask = qdev_get_prop_mask(props); - if (val) { - *p |= mask; - } else { - *p &= ~mask; - } -} - -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, name, &value, errp); -} - -static void prop_set_bit(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - Error *local_err = NULL; - bool value; - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_bool(v, name, &value, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - bit_prop_set(dev, prop, value); -} - -PropertyInfo qdev_prop_bit = { - .name = "bool", - .description = "on/off", - .get = prop_get_bit, - .set = prop_set_bit, -}; - -/* Bit64 */ - -static uint64_t qdev_get_prop_mask64(Property *prop) -{ - assert(prop->info == &qdev_prop_bit64); - return 0x1ull << prop->bitnr; -} - -static void bit64_prop_set(DeviceState *dev, Property *props, bool val) -{ - uint64_t *p = qdev_get_prop_ptr(dev, props); - uint64_t mask = qdev_get_prop_mask64(props); - if (val) { - *p |= mask; - } else { - *p &= ~mask; - } -} - -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, name, &value, errp); -} - -static void prop_set_bit64(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - Error *local_err = NULL; - bool value; - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_bool(v, name, &value, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - bit64_prop_set(dev, prop, value); -} - -PropertyInfo qdev_prop_bit64 = { - .name = "bool", - .description = "on/off", - .get = prop_get_bit64, - .set = prop_set_bit64, -}; - -/* --- bool --- */ - -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, name, ptr, errp); -} - -static void set_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); - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_bool(v, name, ptr, errp); -} - -PropertyInfo qdev_prop_bool = { - .name = "bool", - .get = get_bool, - .set = set_bool, -}; - -/* --- 8bit integer --- */ - -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, name, ptr, errp); -} - -static void set_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); - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_uint8(v, name, ptr, errp); -} - -PropertyInfo qdev_prop_uint8 = { - .name = "uint8", - .get = get_uint8, - .set = set_uint8, -}; - -/* --- 16bit integer --- */ - -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, name, ptr, errp); -} - -static void set_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); - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_uint16(v, name, ptr, errp); -} - -PropertyInfo qdev_prop_uint16 = { - .name = "uint16", - .get = get_uint16, - .set = set_uint16, -}; - -/* --- 32bit integer --- */ - -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, name, ptr, errp); -} - -static void set_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); - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_uint32(v, name, ptr, 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, name, ptr, errp); -} - -static void set_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); - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_int32(v, name, ptr, errp); -} - -PropertyInfo qdev_prop_uint32 = { - .name = "uint32", - .get = get_uint32, - .set = set_uint32, -}; - -PropertyInfo qdev_prop_int32 = { - .name = "int32", - .get = get_int32, - .set = set_int32, -}; - -/* --- 64bit integer --- */ - -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, name, ptr, errp); -} - -static void set_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); - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_uint64(v, name, ptr, errp); -} - -PropertyInfo qdev_prop_uint64 = { - .name = "uint64", - .get = get_uint64, - .set = set_uint64, -}; - -/* --- string --- */ - -static void release_string(Object *obj, const char *name, void *opaque) -{ - Property *prop = opaque; - g_free(*(char **)qdev_get_prop_ptr(DEVICE(obj), prop)); -} - -static void get_string(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - char **ptr = qdev_get_prop_ptr(dev, prop); - - if (!*ptr) { - char *str = (char *)""; - visit_type_str(v, name, &str, errp); - } else { - visit_type_str(v, name, ptr, errp); - } -} - -static void set_string(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - char **ptr = qdev_get_prop_ptr(dev, prop); - Error *local_err = NULL; - char *str; - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_str(v, name, &str, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - g_free(*ptr); - *ptr = str; -} - -PropertyInfo qdev_prop_string = { - .name = "str", - .release = release_string, - .get = get_string, - .set = set_string, -}; - -/* --- pointer --- */ - -/* Not a proper property, just for dirty hacks. TODO Remove it! */ -PropertyInfo qdev_prop_ptr = { - .name = "ptr", -}; - -/* --- mac address --- */ - -/* - * accepted syntax versions: - * 01:02:03:04:05:06 - * 01-02-03-04-05-06 - */ -static void get_mac(Object *obj, Visitor *v, const char *name, void *opaque, - Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - MACAddr *mac = qdev_get_prop_ptr(dev, prop); - char buffer[2 * 6 + 5 + 1]; - char *p = buffer; - - snprintf(buffer, sizeof(buffer), "%02x:%02x:%02x:%02x:%02x:%02x", - mac->a[0], mac->a[1], mac->a[2], - mac->a[3], mac->a[4], mac->a[5]); - - visit_type_str(v, name, &p, errp); -} - -static void set_mac(Object *obj, Visitor *v, const char *name, void *opaque, - Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - MACAddr *mac = qdev_get_prop_ptr(dev, prop); - Error *local_err = NULL; - int i, pos; - char *str, *p; - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_str(v, name, &str, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - for (i = 0, pos = 0; i < 6; i++, pos += 3) { - if (!qemu_isxdigit(str[pos])) { - goto inval; - } - if (!qemu_isxdigit(str[pos+1])) { - goto inval; - } - if (i == 5) { - if (str[pos+2] != '\0') { - goto inval; - } - } else { - if (str[pos+2] != ':' && str[pos+2] != '-') { - goto inval; - } - } - mac->a[i] = strtol(str+pos, &p, 16); - } - g_free(str); - return; - -inval: - error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str); - g_free(str); -} - -PropertyInfo qdev_prop_macaddr = { - .name = "str", - .description = "Ethernet 6-byte MAC Address, example: 52:54:00:12:34:56", - .get = get_mac, - .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)); - -PropertyInfo qdev_prop_losttickpolicy = { - .name = "LostTickPolicy", - .enum_table = LostTickPolicy_lookup, - .get = get_enum, - .set = set_enum, -}; - -/* --- BIOS CHS translation */ - -QEMU_BUILD_BUG_ON(sizeof(BiosAtaTranslation) != sizeof(int)); - -PropertyInfo qdev_prop_bios_chs_trans = { - .name = "BiosAtaTranslation", - .description = "Logical CHS translation algorithm, " - "auto/none/lba/large/rechs", - .enum_table = BiosAtaTranslation_lookup, - .get = get_enum, - .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, const char *name, - void *opaque, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - int32_t value, *ptr = qdev_get_prop_ptr(dev, prop); - unsigned int slot, fn, n; - Error *local_err = NULL; - char *str; - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_str(v, name, &str, &local_err); - if (local_err) { - error_free(local_err); - local_err = NULL; - visit_type_int32(v, name, &value, &local_err); - if (local_err) { - error_propagate(errp, local_err); - } else if (value < -1 || value > 255) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - name ? name : "null", "pci_devfn"); - } else { - *ptr = value; - } - return; - } - - if (sscanf(str, "%x.%x%n", &slot, &fn, &n) != 2) { - fn = 0; - if (sscanf(str, "%x%n", &slot, &n) != 1) { - goto invalid; - } - } - if (str[n] != '\0' || fn > 7 || slot > 31) { - goto invalid; - } - *ptr = slot << 3 | fn; - g_free(str); - return; - -invalid: - error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str); - g_free(str); -} - -static int print_pci_devfn(DeviceState *dev, Property *prop, char *dest, - size_t len) -{ - int32_t *ptr = qdev_get_prop_ptr(dev, prop); - - if (*ptr == -1) { - return snprintf(dest, len, ""); - } else { - return snprintf(dest, len, "%02x.%x", *ptr >> 3, *ptr & 7); - } -} - -PropertyInfo qdev_prop_pci_devfn = { - .name = "int32", - .description = "Slot and optional function number, example: 06.0 or 06", - .print = print_pci_devfn, - .get = get_int32, - .set = set_pci_devfn, -}; - -/* --- blocksize --- */ - -static void set_blocksize(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - uint16_t value, *ptr = qdev_get_prop_ptr(dev, prop); - Error *local_err = NULL; - const int64_t min = 512; - const int64_t max = 32768; - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_uint16(v, name, &value, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - /* value of 0 means "unset" */ - if (value && (value < min || value > max)) { - error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, - dev->id ? : "", name, (int64_t)value, min, max); - return; - } - - /* We rely on power-of-2 blocksizes for bitmasks */ - if ((value & (value - 1)) != 0) { - error_setg(errp, - "Property %s.%s doesn't take value '%" PRId64 "', it's not a power of 2", - dev->id ?: "", name, (int64_t)value); - return; - } - - *ptr = value; -} - -PropertyInfo qdev_prop_blocksize = { - .name = "uint16", - .description = "A power of two between 512 and 32768", - .get = get_uint16, - .set = set_blocksize, -}; - -/* --- pci host address --- */ - -static void get_pci_host_devaddr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - PCIHostDeviceAddress *addr = qdev_get_prop_ptr(dev, prop); - char buffer[] = "xxxx:xx:xx.x"; - char *p = buffer; - int rc = 0; - - rc = snprintf(buffer, sizeof(buffer), "%04x:%02x:%02x.%d", - addr->domain, addr->bus, addr->slot, addr->function); - assert(rc == sizeof(buffer) - 1); - - visit_type_str(v, name, &p, errp); -} - -/* - * Parse [:]:. - * if is not supplied, it's assumed to be 0. - */ -static void set_pci_host_devaddr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - PCIHostDeviceAddress *addr = qdev_get_prop_ptr(dev, prop); - Error *local_err = NULL; - char *str, *p; - char *e; - unsigned long val; - unsigned long dom = 0, bus = 0; - unsigned int slot = 0, func = 0; - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_str(v, name, &str, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - p = str; - val = strtoul(p, &e, 16); - if (e == p || *e != ':') { - goto inval; - } - bus = val; - - p = e + 1; - val = strtoul(p, &e, 16); - if (e == p) { - goto inval; - } - if (*e == ':') { - dom = bus; - bus = val; - p = e + 1; - val = strtoul(p, &e, 16); - if (e == p) { - goto inval; - } - } - slot = val; - - if (*e != '.') { - goto inval; - } - p = e + 1; - val = strtoul(p, &e, 10); - if (e == p) { - goto inval; - } - func = val; - - if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7) { - goto inval; - } - - if (*e) { - goto inval; - } - - addr->domain = dom; - addr->bus = bus; - addr->slot = slot; - addr->function = func; - - g_free(str); - return; - -inval: - error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str); - g_free(str); -} - -PropertyInfo qdev_prop_pci_host_devaddr = { - .name = "str", - .description = "Address (bus/device/function) of " - "the host device, example: 04:10.0", - .get = get_pci_host_devaddr, - .set = set_pci_host_devaddr, -}; - -/* --- support for array properties --- */ - -/* Used as an opaque for the object properties we add for each - * array element. Note that the struct Property must be first - * in the struct so that a pointer to this works as the opaque - * for the underlying element's property hooks as well as for - * our own release callback. - */ -typedef struct { - struct Property prop; - char *propname; - ObjectPropertyRelease *release; -} ArrayElementProperty; - -/* object property release callback for array element properties: - * we call the underlying element's property release hook, and - * then free the memory we allocated when we added the property. - */ -static void array_element_release(Object *obj, const char *name, void *opaque) -{ - ArrayElementProperty *p = opaque; - if (p->release) { - p->release(obj, name, opaque); - } - g_free(p->propname); - g_free(p); -} - -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 - * array-length field in the device struct, we have to create the - * array itself and dynamically add the corresponding properties. - */ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - uint32_t *alenptr = qdev_get_prop_ptr(dev, prop); - void **arrayptr = (void *)dev + prop->arrayoffset; - Error *local_err = NULL; - void *eltptr; - const char *arrayname; - int i; - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - if (*alenptr) { - error_setg(errp, "array size property %s may not be set more than once", - name); - return; - } - visit_type_uint32(v, name, alenptr, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - if (!*alenptr) { - return; - } - - /* DEFINE_PROP_ARRAY guarantees that name should start with this prefix; - * strip it off so we can get the name of the array itself. - */ - assert(strncmp(name, PROP_ARRAY_LEN_PREFIX, - strlen(PROP_ARRAY_LEN_PREFIX)) == 0); - arrayname = name + strlen(PROP_ARRAY_LEN_PREFIX); - - /* Note that it is the responsibility of the individual device's deinit - * to free the array proper. - */ - *arrayptr = eltptr = g_malloc0(*alenptr * prop->arrayfieldsize); - for (i = 0; i < *alenptr; i++, eltptr += prop->arrayfieldsize) { - char *propname = g_strdup_printf("%s[%d]", arrayname, i); - ArrayElementProperty *arrayprop = g_new0(ArrayElementProperty, 1); - arrayprop->release = prop->arrayinfo->release; - arrayprop->propname = propname; - arrayprop->prop.info = prop->arrayinfo; - arrayprop->prop.name = propname; - /* This ugly piece of pointer arithmetic sets up the offset so - * that when the underlying get/set hooks call qdev_get_prop_ptr - * they get the right answer despite the array element not actually - * being inside the device struct. - */ - arrayprop->prop.offset = eltptr - (void *)dev; - assert(qdev_get_prop_ptr(dev, &arrayprop->prop) == eltptr); - object_property_add(obj, propname, - arrayprop->prop.info->name, - arrayprop->prop.info->get, - arrayprop->prop.info->set, - array_element_release, - arrayprop, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - } -} - -PropertyInfo qdev_prop_arraylen = { - .name = "uint32", - .get = get_uint32, - .set = set_prop_arraylen, -}; - -/* --- public helpers --- */ - -static Property *qdev_prop_walk(Property *props, const char *name) -{ - if (!props) { - return NULL; - } - while (props->name) { - if (strcmp(props->name, name) == 0) { - return props; - } - props++; - } - return NULL; -} - -static Property *qdev_prop_find(DeviceState *dev, const char *name) -{ - ObjectClass *class; - Property *prop; - - /* device properties */ - class = object_get_class(OBJECT(dev)); - do { - prop = qdev_prop_walk(DEVICE_CLASS(class)->props, name); - if (prop) { - return prop; - } - class = object_class_get_parent(class); - } while (class != object_class_by_name(TYPE_DEVICE)); - - return NULL; -} - -void error_set_from_qdev_prop_error(Error **errp, int ret, DeviceState *dev, - Property *prop, const char *value) -{ - switch (ret) { - case -EEXIST: - error_setg(errp, "Property '%s.%s' can't take value '%s', it's in use", - object_get_typename(OBJECT(dev)), prop->name, value); - break; - default: - case -EINVAL: - error_setg(errp, QERR_PROPERTY_VALUE_BAD, - object_get_typename(OBJECT(dev)), prop->name, value); - break; - case -ENOENT: - error_setg(errp, "Property '%s.%s' can't find value '%s'", - object_get_typename(OBJECT(dev)), prop->name, value); - break; - case 0: - break; - } -} - -void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value) -{ - object_property_set_bool(OBJECT(dev), value, name, &error_abort); -} - -void qdev_prop_set_uint8(DeviceState *dev, const char *name, uint8_t value) -{ - object_property_set_int(OBJECT(dev), value, name, &error_abort); -} - -void qdev_prop_set_uint16(DeviceState *dev, const char *name, uint16_t value) -{ - object_property_set_int(OBJECT(dev), value, name, &error_abort); -} - -void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value) -{ - object_property_set_int(OBJECT(dev), value, name, &error_abort); -} - -void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value) -{ - object_property_set_int(OBJECT(dev), value, name, &error_abort); -} - -void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value) -{ - object_property_set_int(OBJECT(dev), value, name, &error_abort); -} - -void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value) -{ - object_property_set_str(OBJECT(dev), value, name, &error_abort); -} - -void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value) -{ - char str[2 * 6 + 5 + 1]; - snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x", - value[0], value[1], value[2], value[3], value[4], value[5]); - - object_property_set_str(OBJECT(dev), str, name, &error_abort); -} - -void qdev_prop_set_enum(DeviceState *dev, const char *name, int value) -{ - Property *prop; - - prop = qdev_prop_find(dev, name); - object_property_set_str(OBJECT(dev), prop->info->enum_table[value], - name, &error_abort); -} - -void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value) -{ - Property *prop; - void **ptr; - - prop = qdev_prop_find(dev, name); - assert(prop && prop->info == &qdev_prop_ptr); - ptr = qdev_get_prop_ptr(dev, prop); - *ptr = value; -} - -static QTAILQ_HEAD(, GlobalProperty) global_props = - QTAILQ_HEAD_INITIALIZER(global_props); - -void qdev_prop_register_global(GlobalProperty *prop) -{ - QTAILQ_INSERT_TAIL(&global_props, prop, next); -} - -void qdev_prop_register_global_list(GlobalProperty *props) -{ - int i; - - for (i = 0; props[i].driver != NULL; i++) { - qdev_prop_register_global(props+i); - } -} - -int qdev_prop_check_globals(void) -{ - GlobalProperty *prop; - int ret = 0; - - QTAILQ_FOREACH(prop, &global_props, next) { - ObjectClass *oc; - DeviceClass *dc; - if (prop->used) { - continue; - } - if (!prop->user_provided) { - continue; - } - oc = object_class_by_name(prop->driver); - oc = object_class_dynamic_cast(oc, TYPE_DEVICE); - if (!oc) { - error_report("Warning: global %s.%s has invalid class name", - prop->driver, prop->property); - ret = 1; - continue; - } - dc = DEVICE_CLASS(oc); - if (!dc->hotpluggable && !prop->used) { - error_report("Warning: global %s.%s=%s not used", - prop->driver, prop->property, prop->value); - ret = 1; - continue; - } - } - return ret; -} - -static void qdev_prop_set_globals_for_type(DeviceState *dev, - const char *typename) -{ - GlobalProperty *prop; - - QTAILQ_FOREACH(prop, &global_props, next) { - Error *err = NULL; - - if (strcmp(typename, prop->driver) != 0) { - continue; - } - prop->used = true; - object_property_parse(OBJECT(dev), prop->value, prop->property, &err); - if (err != NULL) { - assert(prop->user_provided); - error_reportf_err(err, "Warning: global %s.%s=%s ignored: ", - prop->driver, prop->property, prop->value); - return; - } - } -} - -void qdev_prop_set_globals(DeviceState *dev) -{ - ObjectClass *class = object_get_class(OBJECT(dev)); - - do { - qdev_prop_set_globals_for_type(dev, object_class_get_name(class)); - class = object_class_get_parent(class); - } while (class); -} - -/* --- 64bit unsigned int 'size' type --- */ - -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, name, ptr, 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, name, ptr, errp); -} - -PropertyInfo qdev_prop_size = { - .name = "size", - .get = get_size, - .set = set_size, -}; diff --git a/qemu/hw/core/qdev.c b/qemu/hw/core/qdev.c deleted file mode 100644 index db41aa1f2..000000000 --- a/qemu/hw/core/qdev.c +++ /dev/null @@ -1,1370 +0,0 @@ -/* - * Dynamic device configuration and creation. - * - * Copyright (c) 2009 CodeSourcery - * - * 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 . - */ - -/* The theory here is that it should be possible to create a machine without - knowledge of specific devices. Historically board init routines have - passed a bunch of arguments to each device, requiring the board know - exactly which device it is dealing with. This file provides an abstract - API for device configuration and initialization. Devices will generally - 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/qmp/qerror.h" -#include "qapi/visitor.h" -#include "qapi/qmp/qjson.h" -#include "qemu/error-report.h" -#include "hw/hotplug.h" -#include "hw/boards.h" -#include "qapi-event.h" - -int qdev_hotplug = 0; -static bool qdev_hot_added = false; -static bool qdev_hot_removed = false; - -const VMStateDescription *qdev_get_vmsd(DeviceState *dev) -{ - DeviceClass *dc = DEVICE_GET_CLASS(dev); - return dc->vmsd; -} - -const char *qdev_fw_name(DeviceState *dev) -{ - DeviceClass *dc = DEVICE_GET_CLASS(dev); - - if (dc->fw_name) { - return dc->fw_name; - } - - return object_get_typename(OBJECT(dev)); -} - -static void qdev_property_add_legacy(DeviceState *dev, Property *prop, - Error **errp); - -static void bus_remove_child(BusState *bus, DeviceState *child) -{ - BusChild *kid; - - QTAILQ_FOREACH(kid, &bus->children, sibling) { - if (kid->child == child) { - char name[32]; - - snprintf(name, sizeof(name), "child[%d]", kid->index); - QTAILQ_REMOVE(&bus->children, kid, sibling); - - /* This gives back ownership of kid->child back to us. */ - object_property_del(OBJECT(bus), name, NULL); - object_unref(OBJECT(kid->child)); - g_free(kid); - return; - } - } -} - -static void bus_add_child(BusState *bus, DeviceState *child) -{ - char name[32]; - BusChild *kid = g_malloc0(sizeof(*kid)); - - kid->index = bus->max_index++; - kid->child = child; - object_ref(OBJECT(kid->child)); - - QTAILQ_INSERT_HEAD(&bus->children, kid, sibling); - - /* This transfers ownership of kid->child to the property. */ - snprintf(name, sizeof(name), "child[%d]", kid->index); - object_property_add_link(OBJECT(bus), name, - object_get_typename(OBJECT(child)), - (Object **)&kid->child, - NULL, /* read-only property */ - 0, /* return ownership on prop deletion */ - NULL); -} - -void qdev_set_parent_bus(DeviceState *dev, BusState *bus) -{ - dev->parent_bus = bus; - object_ref(OBJECT(bus)); - bus_add_child(bus, dev); -} - -static void qbus_set_hotplug_handler_internal(BusState *bus, Object *handler, - Error **errp) -{ - - object_property_set_link(OBJECT(bus), OBJECT(handler), - QDEV_HOTPLUG_HANDLER_PROPERTY, errp); -} - -void qbus_set_hotplug_handler(BusState *bus, DeviceState *handler, Error **errp) -{ - qbus_set_hotplug_handler_internal(bus, OBJECT(handler), errp); -} - -void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp) -{ - qbus_set_hotplug_handler_internal(bus, OBJECT(bus), errp); -} - -/* Create a new device. This only initializes the device state - structure and allows properties to be set. The device still needs - to be realized. See qdev-core.h. */ -DeviceState *qdev_create(BusState *bus, const char *name) -{ - DeviceState *dev; - - dev = qdev_try_create(bus, name); - if (!dev) { - if (bus) { - error_report("Unknown device '%s' for bus '%s'", name, - object_get_typename(OBJECT(bus))); - } else { - error_report("Unknown device '%s' for default sysbus", name); - } - abort(); - } - - return dev; -} - -DeviceState *qdev_try_create(BusState *bus, const char *type) -{ - DeviceState *dev; - - if (object_class_by_name(type) == NULL) { - return NULL; - } - dev = DEVICE(object_new(type)); - if (!dev) { - return NULL; - } - - if (!bus) { - bus = sysbus_get_default(); - } - - qdev_set_parent_bus(dev, bus); - object_unref(OBJECT(dev)); - return dev; -} - -static QTAILQ_HEAD(device_listeners, DeviceListener) device_listeners - = QTAILQ_HEAD_INITIALIZER(device_listeners); - -enum ListenerDirection { Forward, Reverse }; - -#define DEVICE_LISTENER_CALL(_callback, _direction, _args...) \ - do { \ - DeviceListener *_listener; \ - \ - switch (_direction) { \ - case Forward: \ - QTAILQ_FOREACH(_listener, &device_listeners, link) { \ - if (_listener->_callback) { \ - _listener->_callback(_listener, ##_args); \ - } \ - } \ - break; \ - case Reverse: \ - QTAILQ_FOREACH_REVERSE(_listener, &device_listeners, \ - device_listeners, link) { \ - if (_listener->_callback) { \ - _listener->_callback(_listener, ##_args); \ - } \ - } \ - break; \ - default: \ - abort(); \ - } \ - } while (0) - -static int device_listener_add(DeviceState *dev, void *opaque) -{ - DEVICE_LISTENER_CALL(realize, Forward, dev); - - return 0; -} - -void device_listener_register(DeviceListener *listener) -{ - QTAILQ_INSERT_TAIL(&device_listeners, listener, link); - - qbus_walk_children(sysbus_get_default(), NULL, NULL, device_listener_add, - NULL, NULL); -} - -void device_listener_unregister(DeviceListener *listener) -{ - QTAILQ_REMOVE(&device_listeners, listener, link); -} - -static void device_realize(DeviceState *dev, Error **errp) -{ - DeviceClass *dc = DEVICE_GET_CLASS(dev); - - if (dc->init) { - int rc = dc->init(dev); - if (rc < 0) { - error_setg(errp, "Device initialization failed."); - return; - } - } -} - -static void device_unrealize(DeviceState *dev, Error **errp) -{ - DeviceClass *dc = DEVICE_GET_CLASS(dev); - - if (dc->exit) { - int rc = dc->exit(dev); - if (rc < 0) { - error_setg(errp, "Device exit failed."); - return; - } - } -} - -void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, - int required_for_version) -{ - assert(!dev->realized); - dev->instance_id_alias = alias_id; - dev->alias_required_for_version = required_for_version; -} - -HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) -{ - HotplugHandler *hotplug_ctrl = NULL; - - if (dev->parent_bus && dev->parent_bus->hotplug_handler) { - hotplug_ctrl = dev->parent_bus->hotplug_handler; - } else if (object_dynamic_cast(qdev_get_machine(), TYPE_MACHINE)) { - MachineState *machine = MACHINE(qdev_get_machine()); - MachineClass *mc = MACHINE_GET_CLASS(machine); - - if (mc->get_hotplug_handler) { - hotplug_ctrl = mc->get_hotplug_handler(machine, dev); - } - } - return hotplug_ctrl; -} - -void qdev_unplug(DeviceState *dev, Error **errp) -{ - DeviceClass *dc = DEVICE_GET_CLASS(dev); - HotplugHandler *hotplug_ctrl; - HotplugHandlerClass *hdc; - - if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name); - return; - } - - if (!dc->hotpluggable) { - error_setg(errp, QERR_DEVICE_NO_HOTPLUG, - object_get_typename(OBJECT(dev))); - return; - } - - qdev_hot_removed = true; - - hotplug_ctrl = qdev_get_hotplug_handler(dev); - /* hotpluggable device MUST have HotplugHandler, if it doesn't - * then something is very wrong with it */ - g_assert(hotplug_ctrl); - - /* If device supports async unplug just request it to be done, - * otherwise just remove it synchronously */ - hdc = HOTPLUG_HANDLER_GET_CLASS(hotplug_ctrl); - if (hdc->unplug_request) { - hotplug_handler_unplug_request(hotplug_ctrl, dev, errp); - } else { - hotplug_handler_unplug(hotplug_ctrl, dev, errp); - } -} - -static int qdev_reset_one(DeviceState *dev, void *opaque) -{ - device_reset(dev); - - return 0; -} - -static int qbus_reset_one(BusState *bus, void *opaque) -{ - BusClass *bc = BUS_GET_CLASS(bus); - if (bc->reset) { - bc->reset(bus); - } - return 0; -} - -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); -} - -void qbus_reset_all_fn(void *opaque) -{ - BusState *bus = opaque; - qbus_reset_all(bus); -} - -/* can be used as ->unplug() callback for the simple cases */ -void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - /* just zap it */ - object_unparent(OBJECT(dev)); -} - -/* - * Realize @dev. - * Device properties should be set before calling this function. IRQs - * and MMIO regions should be connected/mapped after calling this - * function. - * On failure, report an error with error_report() and terminate the - * program. This is okay during machine creation. Don't use for - * hotplug, because there callers need to recover from failure. - * Exception: if you know the device's init() callback can't fail, - * then qdev_init_nofail() can't fail either, and is therefore usable - * even then. But relying on the device implementation that way is - * somewhat unclean, and best avoided. - */ -void qdev_init_nofail(DeviceState *dev) -{ - Error *err = NULL; - - assert(!dev->realized); - - object_property_set_bool(OBJECT(dev), true, "realized", &err); - if (err) { - error_reportf_err(err, "Initialization of device %s failed: ", - object_get_typename(OBJECT(dev))); - exit(1); - } -} - -void qdev_machine_creation_done(void) -{ - /* - * ok, initial machine setup is done, starting from now we can - * only create hotpluggable devices - */ - qdev_hotplug = 1; -} - -bool qdev_machine_modified(void) -{ - return qdev_hot_added || qdev_hot_removed; -} - -BusState *qdev_get_parent_bus(DeviceState *dev) -{ - return dev->parent_bus; -} - -static NamedGPIOList *qdev_get_named_gpio_list(DeviceState *dev, - const char *name) -{ - NamedGPIOList *ngl; - - QLIST_FOREACH(ngl, &dev->gpios, node) { - /* NULL is a valid and matchable name, otherwise do a normal - * strcmp match. - */ - if ((!ngl->name && !name) || - (name && ngl->name && strcmp(name, ngl->name) == 0)) { - return ngl; - } - } - - ngl = g_malloc0(sizeof(*ngl)); - ngl->name = g_strdup(name); - QLIST_INSERT_HEAD(&dev->gpios, ngl, node); - return ngl; -} - -void qdev_init_gpio_in_named(DeviceState *dev, qemu_irq_handler handler, - const char *name, int n) -{ - int i; - NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); - - 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); - } - - gpio_list->num_in += n; -} - -void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n) -{ - qdev_init_gpio_in_named(dev, handler, NULL, n); -} - -void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins, - const char *name, int n) -{ - int i; - NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); - - assert(gpio_list->num_in == 0 || !name); - - if (!name) { - name = "unnamed-gpio-out"; - } - memset(pins, 0, sizeof(*pins) * n); - for (i = 0; i < n; ++i) { - 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); - } - gpio_list->num_out += n; -} - -void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n) -{ - qdev_init_gpio_out_named(dev, pins, NULL, n); -} - -qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n) -{ - NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name); - - assert(n >= 0 && n < gpio_list->num_in); - return gpio_list->in[n]; -} - -qemu_irq qdev_get_gpio_in(DeviceState *dev, int n) -{ - return qdev_get_gpio_in_named(dev, NULL, n); -} - -void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n, - qemu_irq pin) -{ - char *propname = g_strdup_printf("%s[%d]", - name ? name : "unnamed-gpio-out", n); - if (pin) { - /* We need a name for object_property_set_link to work. If the - * object has a parent, object_property_add_child will come back - * with an error without doing anything. If it has none, it will - * never fail. So we can just call it with a NULL Error pointer. - */ - object_property_add_child(container_get(qdev_get_machine(), - "/unattached"), - "non-qdev-gpio[*]", OBJECT(pin), NULL); - } - object_property_set_link(OBJECT(dev), OBJECT(pin), propname, &error_abort); - g_free(propname); -} - -qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n) -{ - char *propname = g_strdup_printf("%s[%d]", - name ? name : "unnamed-gpio-out", n); - - qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname, - NULL); - - return ret; -} - -/* 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) -{ - char *propname = g_strdup_printf("%s[%d]", - name ? name : "unnamed-gpio-out", n); - - qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname, - NULL); - if (ret) { - object_property_set_link(OBJECT(dev), NULL, propname, NULL); - } - g_free(propname); - return ret; -} - -qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt, - const char *name, int n) -{ - qemu_irq disconnected = qdev_disconnect_gpio_out_named(dev, name, n); - qdev_connect_gpio_out_named(dev, name, n, icpt); - return disconnected; -} - -void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin) -{ - qdev_connect_gpio_out_named(dev, NULL, n, pin); -} - -void qdev_pass_gpios(DeviceState *dev, DeviceState *container, - const char *name) -{ - int i; - NamedGPIOList *ngl = qdev_get_named_gpio_list(dev, name); - - for (i = 0; i < ngl->num_in; i++) { - const char *nm = ngl->name ? ngl->name : "unnamed-gpio-in"; - char *propname = g_strdup_printf("%s[%d]", nm, i); - - object_property_add_alias(OBJECT(container), propname, - OBJECT(dev), propname, - &error_abort); - g_free(propname); - } - for (i = 0; i < ngl->num_out; i++) { - const char *nm = ngl->name ? ngl->name : "unnamed-gpio-out"; - char *propname = g_strdup_printf("%s[%d]", nm, i); - - object_property_add_alias(OBJECT(container), propname, - OBJECT(dev), propname, - &error_abort); - g_free(propname); - } - QLIST_REMOVE(ngl, node); - QLIST_INSERT_HEAD(&container->gpios, ngl, node); -} - -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) { - return bus; - } - } - return NULL; -} - -int qbus_walk_children(BusState *bus, - qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn, - qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn, - void *opaque) -{ - BusChild *kid; - int err; - - if (pre_busfn) { - err = pre_busfn(bus, opaque); - if (err) { - return err; - } - } - - QTAILQ_FOREACH(kid, &bus->children, sibling) { - err = qdev_walk_children(kid->child, - pre_devfn, pre_busfn, - post_devfn, post_busfn, opaque); - if (err < 0) { - return err; - } - } - - if (post_busfn) { - err = post_busfn(bus, opaque); - if (err) { - return err; - } - } - - return 0; -} - -int qdev_walk_children(DeviceState *dev, - qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn, - qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn, - void *opaque) -{ - BusState *bus; - int err; - - if (pre_devfn) { - err = pre_devfn(dev, opaque); - if (err) { - return err; - } - } - - QLIST_FOREACH(bus, &dev->child_bus, sibling) { - err = qbus_walk_children(bus, pre_devfn, pre_busfn, - post_devfn, post_busfn, opaque); - if (err < 0) { - return err; - } - } - - if (post_devfn) { - err = post_devfn(dev, opaque); - if (err) { - return err; - } - } - - return 0; -} - -DeviceState *qdev_find_recursive(BusState *bus, const char *id) -{ - BusChild *kid; - DeviceState *ret; - BusState *child; - - QTAILQ_FOREACH(kid, &bus->children, sibling) { - DeviceState *dev = kid->child; - - if (dev->id && strcmp(dev->id, id) == 0) { - return dev; - } - - QLIST_FOREACH(child, &dev->child_bus, sibling) { - ret = qdev_find_recursive(child, id); - if (ret) { - return ret; - } - } - } - return NULL; -} - -static void qbus_realize(BusState *bus, DeviceState *parent, const char *name) -{ - const char *typename = object_get_typename(OBJECT(bus)); - BusClass *bc; - char *buf; - int i, len, bus_id; - - bus->parent = parent; - - if (name) { - bus->name = g_strdup(name); - } else if (bus->parent && bus->parent->id) { - /* parent device has id -> use it plus parent-bus-id for bus name */ - bus_id = bus->parent->num_child_bus; - - len = strlen(bus->parent->id) + 16; - buf = g_malloc(len); - snprintf(buf, len, "%s.%d", bus->parent->id, bus_id); - bus->name = buf; - } else { - /* no id -> use lowercase bus type plus global bus-id for bus name */ - bc = BUS_GET_CLASS(bus); - bus_id = bc->automatic_ids++; - - len = strlen(typename) + 16; - buf = g_malloc(len); - len = snprintf(buf, len, "%s.%d", typename, bus_id); - for (i = 0; i < len; i++) { - buf[i] = qemu_tolower(buf[i]); - } - bus->name = buf; - } - - if (bus->parent) { - QLIST_INSERT_HEAD(&bus->parent->child_bus, bus, sibling); - bus->parent->num_child_bus++; - object_property_add_child(OBJECT(bus->parent), bus->name, OBJECT(bus), NULL); - object_unref(OBJECT(bus)); - } else if (bus != sysbus_get_default()) { - /* TODO: once all bus devices are qdevified, - only reset handler for main_system_bus should be registered here. */ - qemu_register_reset(qbus_reset_all_fn, bus); - } -} - -static void bus_unparent(Object *obj) -{ - BusState *bus = BUS(obj); - BusChild *kid; - - while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) { - DeviceState *dev = kid->child; - object_unparent(OBJECT(dev)); - } - if (bus->parent) { - QLIST_REMOVE(bus, sibling); - bus->parent->num_child_bus--; - bus->parent = NULL; - } else { - assert(bus != sysbus_get_default()); /* main_system_bus is never freed */ - qemu_unregister_reset(qbus_reset_all_fn, bus); - } -} - -static bool bus_get_realized(Object *obj, Error **errp) -{ - BusState *bus = BUS(obj); - - return bus->realized; -} - -static void bus_set_realized(Object *obj, bool value, Error **errp) -{ - BusState *bus = BUS(obj); - BusClass *bc = BUS_GET_CLASS(bus); - BusChild *kid; - Error *local_err = NULL; - - if (value && !bus->realized) { - if (bc->realize) { - bc->realize(bus, &local_err); - } - - /* TODO: recursive realization */ - } else if (!value && bus->realized) { - QTAILQ_FOREACH(kid, &bus->children, sibling) { - DeviceState *dev = kid->child; - object_property_set_bool(OBJECT(dev), false, "realized", - &local_err); - if (local_err != NULL) { - break; - } - } - if (bc->unrealize && local_err == NULL) { - bc->unrealize(bus, &local_err); - } - } - - if (local_err != NULL) { - error_propagate(errp, local_err); - return; - } - - bus->realized = value; -} - -void qbus_create_inplace(void *bus, size_t size, const char *typename, - DeviceState *parent, const char *name) -{ - object_initialize(bus, size, typename); - qbus_realize(bus, parent, name); -} - -BusState *qbus_create(const char *typename, DeviceState *parent, const char *name) -{ - BusState *bus; - - bus = BUS(object_new(typename)); - qbus_realize(bus, parent, name); - - return bus; -} - -static char *bus_get_fw_dev_path(BusState *bus, DeviceState *dev) -{ - BusClass *bc = BUS_GET_CLASS(bus); - - if (bc->get_fw_dev_path) { - return bc->get_fw_dev_path(dev); - } - - return NULL; -} - -static char *qdev_get_fw_dev_path_from_handler(BusState *bus, DeviceState *dev) -{ - Object *obj = OBJECT(dev); - char *d = NULL; - - while (!d && obj->parent) { - obj = obj->parent; - d = fw_path_provider_try_get_dev_path(obj, bus, dev); - } - return d; -} - -char *qdev_get_own_fw_dev_path_from_handler(BusState *bus, DeviceState *dev) -{ - Object *obj = OBJECT(dev); - - return fw_path_provider_try_get_dev_path(obj, bus, dev); -} - -static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size) -{ - int l = 0; - - if (dev && dev->parent_bus) { - char *d; - l = qdev_get_fw_dev_path_helper(dev->parent_bus->parent, p, size); - d = qdev_get_fw_dev_path_from_handler(dev->parent_bus, dev); - if (!d) { - d = bus_get_fw_dev_path(dev->parent_bus, dev); - } - if (d) { - l += snprintf(p + l, size - l, "%s", d); - g_free(d); - } else { - return l; - } - } - l += snprintf(p + l , size - l, "/"); - - return l; -} - -char* qdev_get_fw_dev_path(DeviceState *dev) -{ - char path[128]; - int l; - - l = qdev_get_fw_dev_path_helper(dev, path, 128); - - path[l-1] = '\0'; - - return g_strdup(path); -} - -char *qdev_get_dev_path(DeviceState *dev) -{ - BusClass *bc; - - if (!dev || !dev->parent_bus) { - return NULL; - } - - bc = BUS_GET_CLASS(dev->parent_bus); - if (bc->get_dev_path) { - return bc->get_dev_path(dev); - } - - return NULL; -} - -/** - * Legacy property handling - */ - -static void qdev_get_legacy_property(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - - char buffer[1024]; - char *ptr = buffer; - - prop->info->print(dev, prop, buffer, sizeof(buffer)); - visit_type_str(v, name, &ptr, errp); -} - -/** - * @qdev_add_legacy_property - adds a legacy property - * - * Do not use this is new code! Properties added through this interface will - * be given names and types in the "legacy" namespace. - * - * Legacy properties are string versions of other OOM properties. The format - * of the string depends on the property type. - */ -static void qdev_property_add_legacy(DeviceState *dev, Property *prop, - Error **errp) -{ - gchar *name; - - /* Register pointer properties as legacy properties */ - if (!prop->info->print && prop->info->get) { - return; - } - - name = g_strdup_printf("legacy-%s", prop->name); - object_property_add(OBJECT(dev), name, "str", - prop->info->print ? qdev_get_legacy_property : prop->info->get, - NULL, - NULL, - prop, errp); - - g_free(name); -} - -/** - * @qdev_property_add_static - add a @Property to a device. - * - * Static properties access data in a struct. The actual type of the - * property and the field depends on the property type. - */ -void qdev_property_add_static(DeviceState *dev, Property *prop, - Error **errp) -{ - Error *local_err = NULL; - Object *obj = OBJECT(dev); - - /* - * TODO qdev_prop_ptr does not have getters or setters. It must - * go now that it can be replaced with links. The test should be - * removed along with it: all static properties are read/write. - */ - if (!prop->info->get && !prop->info->set) { - return; - } - - object_property_add(obj, prop->name, prop->info->name, - prop->info->get, prop->info->set, - prop->info->release, - prop, &local_err); - - if (local_err) { - error_propagate(errp, local_err); - return; - } - - object_property_set_description(obj, prop->name, - prop->info->description, - &error_abort); - - if (prop->qtype == QTYPE_NONE) { - return; - } - - if (prop->qtype == QTYPE_QBOOL) { - object_property_set_bool(obj, prop->defval, prop->name, &error_abort); - } else if (prop->info->enum_table) { - object_property_set_str(obj, prop->info->enum_table[prop->defval], - prop->name, &error_abort); - } else if (prop->qtype == QTYPE_QINT) { - object_property_set_int(obj, prop->defval, prop->name, &error_abort); - } -} - -/* @qdev_alias_all_properties - Add alias properties to the source object for - * all qdev properties on the target DeviceState. - */ -void qdev_alias_all_properties(DeviceState *target, Object *source) -{ - ObjectClass *class; - Property *prop; - - class = object_get_class(OBJECT(target)); - do { - DeviceClass *dc = DEVICE_CLASS(class); - - for (prop = dc->props; prop && prop->name; prop++) { - object_property_add_alias(source, prop->name, - OBJECT(target), prop->name, - &error_abort); - } - class = object_class_get_parent(class); - } while (class != object_class_by_name(TYPE_DEVICE)); -} - -static int qdev_add_hotpluggable_device(Object *obj, void *opaque) -{ - GSList **list = opaque; - DeviceState *dev = (DeviceState *)object_dynamic_cast(OBJECT(obj), - TYPE_DEVICE); - - if (dev == NULL) { - return 0; - } - - if (dev->realized && object_property_get_bool(obj, "hotpluggable", NULL)) { - *list = g_slist_append(*list, dev); - } - - return 0; -} - -GSList *qdev_build_hotpluggable_device_list(Object *peripheral) -{ - GSList *list = NULL; - - object_child_foreach(peripheral, qdev_add_hotpluggable_device, &list); - - return list; -} - -static bool device_get_realized(Object *obj, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - return dev->realized; -} - -static void device_set_realized(Object *obj, bool value, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - DeviceClass *dc = DEVICE_GET_CLASS(dev); - HotplugHandler *hotplug_ctrl; - BusState *bus; - Error *local_err = NULL; - - if (dev->hotplugged && !dc->hotpluggable) { - error_setg(errp, QERR_DEVICE_NO_HOTPLUG, object_get_typename(obj)); - return; - } - - if (value && !dev->realized) { - if (!obj->parent) { - static int unattached_count; - gchar *name = g_strdup_printf("device[%d]", unattached_count++); - - object_property_add_child(container_get(qdev_get_machine(), - "/unattached"), - name, obj, &error_abort); - g_free(name); - } - - if (dc->realize) { - dc->realize(dev, &local_err); - } - - if (local_err != NULL) { - goto fail; - } - - DEVICE_LISTENER_CALL(realize, Forward, dev); - - hotplug_ctrl = qdev_get_hotplug_handler(dev); - if (hotplug_ctrl) { - hotplug_handler_plug(hotplug_ctrl, dev, &local_err); - } - - if (local_err != NULL) { - goto post_realize_fail; - } - - if (qdev_get_vmsd(dev)) { - vmstate_register_with_alias_id(dev, -1, qdev_get_vmsd(dev), dev, - dev->instance_id_alias, - dev->alias_required_for_version); - } - - QLIST_FOREACH(bus, &dev->child_bus, sibling) { - object_property_set_bool(OBJECT(bus), true, "realized", - &local_err); - if (local_err != NULL) { - goto child_realize_fail; - } - } - if (dev->hotplugged) { - device_reset(dev); - } - dev->pending_deleted_event = false; - } else if (!value && dev->realized) { - Error **local_errp = NULL; - QLIST_FOREACH(bus, &dev->child_bus, sibling) { - local_errp = local_err ? NULL : &local_err; - object_property_set_bool(OBJECT(bus), false, "realized", - local_errp); - } - if (qdev_get_vmsd(dev)) { - vmstate_unregister(dev, qdev_get_vmsd(dev), dev); - } - if (dc->unrealize) { - local_errp = local_err ? NULL : &local_err; - dc->unrealize(dev, local_errp); - } - dev->pending_deleted_event = true; - DEVICE_LISTENER_CALL(unrealize, Reverse, dev); - } - - if (local_err != NULL) { - goto fail; - } - - dev->realized = value; - return; - -child_realize_fail: - QLIST_FOREACH(bus, &dev->child_bus, sibling) { - object_property_set_bool(OBJECT(bus), false, "realized", - NULL); - } - - if (qdev_get_vmsd(dev)) { - vmstate_unregister(dev, qdev_get_vmsd(dev), dev); - } - -post_realize_fail: - if (dc->unrealize) { - dc->unrealize(dev, NULL); - } - -fail: - error_propagate(errp, local_err); -} - -static bool device_get_hotpluggable(Object *obj, Error **errp) -{ - DeviceClass *dc = DEVICE_GET_CLASS(obj); - DeviceState *dev = DEVICE(obj); - - return dc->hotpluggable && (dev->parent_bus == NULL || - qbus_is_hotpluggable(dev->parent_bus)); -} - -static bool device_get_hotplugged(Object *obj, Error **err) -{ - DeviceState *dev = DEVICE(obj); - - return dev->hotplugged; -} - -static void device_set_hotplugged(Object *obj, bool value, Error **err) -{ - DeviceState *dev = DEVICE(obj); - - dev->hotplugged = value; -} - -static void device_initfn(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - ObjectClass *class; - Property *prop; - - if (qdev_hotplug) { - dev->hotplugged = 1; - qdev_hot_added = true; - } - - dev->instance_id_alias = -1; - dev->realized = false; - - object_property_add_bool(obj, "realized", - device_get_realized, device_set_realized, NULL); - object_property_add_bool(obj, "hotpluggable", - device_get_hotpluggable, NULL, NULL); - object_property_add_bool(obj, "hotplugged", - device_get_hotplugged, device_set_hotplugged, - &error_abort); - - class = object_get_class(OBJECT(dev)); - do { - for (prop = DEVICE_CLASS(class)->props; prop && prop->name; prop++) { - qdev_property_add_legacy(dev, prop, &error_abort); - qdev_property_add_static(dev, prop, &error_abort); - } - class = object_class_get_parent(class); - } while (class != object_class_by_name(TYPE_DEVICE)); - - object_property_add_link(OBJECT(dev), "parent_bus", TYPE_BUS, - (Object **)&dev->parent_bus, NULL, 0, - &error_abort); - QLIST_INIT(&dev->gpios); -} - -static void device_post_init(Object *obj) -{ - qdev_prop_set_globals(DEVICE(obj)); -} - -/* Unlink device from bus and free the structure. */ -static void device_finalize(Object *obj) -{ - NamedGPIOList *ngl, *next; - - DeviceState *dev = DEVICE(obj); - - QLIST_FOREACH_SAFE(ngl, &dev->gpios, node, next) { - QLIST_REMOVE(ngl, node); - qemu_free_irqs(ngl->in, ngl->num_in); - g_free(ngl->name); - g_free(ngl); - /* ngl->out irqs are owned by the other end and should not be freed - * here - */ - } -} - -static void device_class_base_init(ObjectClass *class, void *data) -{ - DeviceClass *klass = DEVICE_CLASS(class); - - /* We explicitly look up properties in the superclasses, - * so do not propagate them to the subclasses. - */ - klass->props = NULL; -} - -static void device_unparent(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - BusState *bus; - - if (dev->realized) { - object_property_set_bool(obj, false, "realized", NULL); - } - while (dev->num_child_bus) { - bus = QLIST_FIRST(&dev->child_bus); - object_unparent(OBJECT(bus)); - } - if (dev->parent_bus) { - bus_remove_child(dev->parent_bus, dev); - object_unref(OBJECT(dev->parent_bus)); - dev->parent_bus = NULL; - } - - /* Only send event if the device had been completely realized */ - if (dev->pending_deleted_event) { - gchar *path = object_get_canonical_path(OBJECT(dev)); - - 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) -{ - DeviceClass *dc = DEVICE_CLASS(class); - - class->unparent = device_unparent; - dc->realize = device_realize; - dc->unrealize = device_unrealize; - - /* by default all devices were considered as hotpluggable, - * so with intent to check it in generic qdev_unplug() / - * device_set_realized() functions make every device - * hotpluggable. Devices that shouldn't be hotpluggable, - * should override it in their class_init() - */ - dc->hotpluggable = true; -} - -void device_reset(DeviceState *dev) -{ - DeviceClass *klass = DEVICE_GET_CLASS(dev); - - if (klass->reset) { - klass->reset(dev); - } -} - -Object *qdev_get_machine(void) -{ - static Object *dev; - - if (dev == NULL) { - dev = container_get(object_get_root(), "/machine"); - } - - return dev; -} - -static const TypeInfo device_type_info = { - .name = TYPE_DEVICE, - .parent = TYPE_OBJECT, - .instance_size = sizeof(DeviceState), - .instance_init = device_initfn, - .instance_post_init = device_post_init, - .instance_finalize = device_finalize, - .class_base_init = device_class_base_init, - .class_init = device_class_init, - .abstract = true, - .class_size = sizeof(DeviceClass), -}; - -static void qbus_initfn(Object *obj) -{ - BusState *bus = BUS(obj); - - QTAILQ_INIT(&bus->children); - object_property_add_link(obj, QDEV_HOTPLUG_HANDLER_PROPERTY, - TYPE_HOTPLUG_HANDLER, - (Object **)&bus->hotplug_handler, - object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - NULL); - object_property_add_bool(obj, "realized", - bus_get_realized, bus_set_realized, NULL); -} - -static char *default_bus_get_fw_dev_path(DeviceState *dev) -{ - return g_strdup(object_get_typename(OBJECT(dev))); -} - -static void bus_class_init(ObjectClass *class, void *data) -{ - BusClass *bc = BUS_CLASS(class); - - class->unparent = bus_unparent; - bc->get_fw_dev_path = default_bus_get_fw_dev_path; -} - -static void qbus_finalize(Object *obj) -{ - BusState *bus = BUS(obj); - - g_free((char *)bus->name); -} - -static const TypeInfo bus_info = { - .name = TYPE_BUS, - .parent = TYPE_OBJECT, - .instance_size = sizeof(BusState), - .abstract = true, - .class_size = sizeof(BusClass), - .instance_init = qbus_initfn, - .instance_finalize = qbus_finalize, - .class_init = bus_class_init, -}; - -static void qdev_register_types(void) -{ - type_register_static(&bus_info); - type_register_static(&device_type_info); -} - -type_init(qdev_register_types) diff --git a/qemu/hw/core/stream.c b/qemu/hw/core/stream.c deleted file mode 100644 index 4439ecdf0..000000000 --- a/qemu/hw/core/stream.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/stream.h" - -size_t -stream_push(StreamSlave *sink, uint8_t *buf, size_t len) -{ - StreamSlaveClass *k = STREAM_SLAVE_GET_CLASS(sink); - - return k->push(sink, buf, len); -} - -bool -stream_can_push(StreamSlave *sink, StreamCanPushNotifyFn notify, - void *notify_opaque) -{ - StreamSlaveClass *k = STREAM_SLAVE_GET_CLASS(sink); - - return k->can_push ? k->can_push(sink, notify, notify_opaque) : true; -} - -static const TypeInfo stream_slave_info = { - .name = TYPE_STREAM_SLAVE, - .parent = TYPE_INTERFACE, - .class_size = sizeof(StreamSlaveClass), -}; - - -static void stream_slave_register_types(void) -{ - type_register_static(&stream_slave_info); -} - -type_init(stream_slave_register_types) diff --git a/qemu/hw/core/sysbus.c b/qemu/hw/core/sysbus.c deleted file mode 100644 index a7dbe2b32..000000000 --- a/qemu/hw/core/sysbus.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * System (CPU) Bus device support code - * - * Copyright (c) 2009 CodeSourcery - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "monitor/monitor.h" -#include "exec/address-spaces.h" - -static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent); -static char *sysbus_get_fw_dev_path(DeviceState *dev); - -typedef struct SysBusFind { - void *opaque; - FindSysbusDeviceFunc *func; -} SysBusFind; - -/* Run func() for every sysbus device, traverse the tree for everything else */ -static int find_sysbus_device(Object *obj, void *opaque) -{ - SysBusFind *find = opaque; - Object *dev; - SysBusDevice *sbdev; - - dev = object_dynamic_cast(obj, TYPE_SYS_BUS_DEVICE); - sbdev = (SysBusDevice *)dev; - - if (!sbdev) { - /* Container, traverse it for children */ - return object_child_foreach(obj, find_sysbus_device, opaque); - } - - find->func(sbdev, find->opaque); - - return 0; -} - -/* - * Loop through all dynamically created sysbus devices and call - * func() for each instance. - */ -void foreach_dynamic_sysbus_device(FindSysbusDeviceFunc *func, void *opaque) -{ - Object *container; - SysBusFind find = { - .func = func, - .opaque = opaque, - }; - - /* Loop through all sysbus devices that were spawened outside the machine */ - container = container_get(qdev_get_machine(), "/peripheral"); - find_sysbus_device(container, &find); - container = container_get(qdev_get_machine(), "/peripheral-anon"); - find_sysbus_device(container, &find); -} - - -static void system_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - - k->print_dev = sysbus_dev_print; - k->get_fw_dev_path = sysbus_get_fw_dev_path; -} - -static const TypeInfo system_bus_info = { - .name = TYPE_SYSTEM_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(BusState), - .class_init = system_bus_class_init, -}; - -/* Check whether an IRQ source exists */ -bool sysbus_has_irq(SysBusDevice *dev, int n) -{ - char *prop = g_strdup_printf("%s[%d]", SYSBUS_DEVICE_GPIO_IRQ, n); - ObjectProperty *r; - - r = object_property_find(OBJECT(dev), prop, NULL); - g_free(prop); - - return (r != NULL); -} - -bool sysbus_is_irq_connected(SysBusDevice *dev, int n) -{ - return !!sysbus_get_connected_irq(dev, n); -} - -qemu_irq sysbus_get_connected_irq(SysBusDevice *dev, int n) -{ - DeviceState *d = DEVICE(dev); - return qdev_get_gpio_out_connector(d, SYSBUS_DEVICE_GPIO_IRQ, n); -} - -void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq) -{ - SysBusDeviceClass *sbd = SYS_BUS_DEVICE_GET_CLASS(dev); - - qdev_connect_gpio_out_named(DEVICE(dev), SYSBUS_DEVICE_GPIO_IRQ, n, irq); - - if (sbd->connect_irq_notifier) { - sbd->connect_irq_notifier(dev, irq); - } -} - -/* Check whether an MMIO region exists */ -bool sysbus_has_mmio(SysBusDevice *dev, unsigned int n) -{ - return (n < dev->num_mmio); -} - -static void sysbus_mmio_map_common(SysBusDevice *dev, int n, hwaddr addr, - bool may_overlap, int priority) -{ - assert(n >= 0 && n < dev->num_mmio); - - if (dev->mmio[n].addr == addr) { - /* ??? region already mapped here. */ - return; - } - if (dev->mmio[n].addr != (hwaddr)-1) { - /* Unregister previous mapping. */ - memory_region_del_subregion(get_system_memory(), dev->mmio[n].memory); - } - dev->mmio[n].addr = addr; - if (may_overlap) { - memory_region_add_subregion_overlap(get_system_memory(), - addr, - dev->mmio[n].memory, - priority); - } - else { - memory_region_add_subregion(get_system_memory(), - addr, - dev->mmio[n].memory); - } -} - -void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr) -{ - sysbus_mmio_map_common(dev, n, addr, false, 0); -} - -void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr, - int priority) -{ - sysbus_mmio_map_common(dev, n, addr, true, priority); -} - -/* Request an IRQ source. The actual IRQ object may be populated later. */ -void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p) -{ - qdev_init_gpio_out_named(DEVICE(dev), p, SYSBUS_DEVICE_GPIO_IRQ, 1); -} - -/* Pass IRQs from a target device. */ -void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target) -{ - qdev_pass_gpios(DEVICE(target), DEVICE(dev), SYSBUS_DEVICE_GPIO_IRQ); -} - -void sysbus_init_mmio(SysBusDevice *dev, MemoryRegion *memory) -{ - int n; - - assert(dev->num_mmio < QDEV_MAX_MMIO); - n = dev->num_mmio++; - dev->mmio[n].addr = -1; - dev->mmio[n].memory = memory; -} - -MemoryRegion *sysbus_mmio_get_region(SysBusDevice *dev, int n) -{ - return dev->mmio[n].memory; -} - -void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size) -{ - pio_addr_t i; - - for (i = 0; i < size; i++) { - assert(dev->num_pio < QDEV_MAX_PIO); - dev->pio[dev->num_pio++] = ioport++; - } -} - -static int sysbus_device_init(DeviceState *dev) -{ - SysBusDevice *sd = SYS_BUS_DEVICE(dev); - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_GET_CLASS(sd); - - if (!sbc->init) { - return 0; - } - return sbc->init(sd); -} - -DeviceState *sysbus_create_varargs(const char *name, - hwaddr addr, ...) -{ - DeviceState *dev; - SysBusDevice *s; - va_list va; - qemu_irq irq; - int n; - - dev = qdev_create(NULL, name); - s = SYS_BUS_DEVICE(dev); - qdev_init_nofail(dev); - if (addr != (hwaddr)-1) { - sysbus_mmio_map(s, 0, addr); - } - va_start(va, addr); - n = 0; - while (1) { - irq = va_arg(va, qemu_irq); - if (!irq) { - break; - } - sysbus_connect_irq(s, n, irq); - n++; - } - va_end(va); - return dev; -} - -DeviceState *sysbus_try_create_varargs(const char *name, - hwaddr addr, ...) -{ - DeviceState *dev; - SysBusDevice *s; - va_list va; - qemu_irq irq; - int n; - - dev = qdev_try_create(NULL, name); - if (!dev) { - return NULL; - } - s = SYS_BUS_DEVICE(dev); - qdev_init_nofail(dev); - if (addr != (hwaddr)-1) { - sysbus_mmio_map(s, 0, addr); - } - va_start(va, addr); - n = 0; - while (1) { - irq = va_arg(va, qemu_irq); - if (!irq) { - break; - } - sysbus_connect_irq(s, n, irq); - n++; - } - va_end(va); - return dev; -} - -static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent) -{ - SysBusDevice *s = SYS_BUS_DEVICE(dev); - hwaddr size; - int i; - - for (i = 0; i < s->num_mmio; i++) { - size = memory_region_size(s->mmio[i].memory); - monitor_printf(mon, "%*smmio " TARGET_FMT_plx "/" TARGET_FMT_plx "\n", - indent, "", s->mmio[i].addr, size); - } -} - -static char *sysbus_get_fw_dev_path(DeviceState *dev) -{ - SysBusDevice *s = SYS_BUS_DEVICE(dev); - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_GET_CLASS(s); - /* for the explicit unit address fallback case: */ - char *addr, *fw_dev_path; - - if (s->num_mmio) { - return g_strdup_printf("%s@" TARGET_FMT_plx, qdev_fw_name(dev), - s->mmio[0].addr); - } - if (s->num_pio) { - return g_strdup_printf("%s@i%04x", qdev_fw_name(dev), s->pio[0]); - } - if (sbc->explicit_ofw_unit_address) { - addr = sbc->explicit_ofw_unit_address(s); - if (addr) { - fw_dev_path = g_strdup_printf("%s@%s", qdev_fw_name(dev), addr); - g_free(addr); - return fw_dev_path; - } - } - return g_strdup(qdev_fw_name(dev)); -} - -void sysbus_add_io(SysBusDevice *dev, hwaddr addr, - MemoryRegion *mem) -{ - memory_region_add_subregion(get_system_io(), addr, mem); -} - -MemoryRegion *sysbus_address_space(SysBusDevice *dev) -{ - return get_system_memory(); -} - -static void sysbus_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - k->init = sysbus_device_init; - k->bus_type = TYPE_SYSTEM_BUS; -} - -static const TypeInfo sysbus_device_type_info = { - .name = TYPE_SYS_BUS_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(SysBusDevice), - .abstract = true, - .class_size = sizeof(SysBusDeviceClass), - .class_init = sysbus_device_class_init, -}; - -/* This is a nasty hack to allow passing a NULL bus to qdev_create. */ -static BusState *main_system_bus; - -static void main_system_bus_create(void) -{ - /* assign main_system_bus before qbus_create_inplace() - * in order to make "if (bus != sysbus_get_default())" work */ - main_system_bus = g_malloc0(system_bus_info.instance_size); - qbus_create_inplace(main_system_bus, system_bus_info.instance_size, - TYPE_SYSTEM_BUS, NULL, "main-system-bus"); - OBJECT(main_system_bus)->free = g_free; - object_property_add_child(container_get(qdev_get_machine(), - "/unattached"), - "sysbus", OBJECT(main_system_bus), NULL); -} - -BusState *sysbus_get_default(void) -{ - if (!main_system_bus) { - main_system_bus_create(); - } - return main_system_bus; -} - -static void sysbus_register_types(void) -{ - type_register_static(&system_bus_info); - type_register_static(&sysbus_device_type_info); -} - -type_init(sysbus_register_types) diff --git a/qemu/hw/core/uboot_image.h b/qemu/hw/core/uboot_image.h deleted file mode 100644 index 9fc2760b5..000000000 --- a/qemu/hw/core/uboot_image.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * (C) Copyright 2000-2005 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * See file CREDITS for list of people who contributed to this - * project. - * - * 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 . - * - ******************************************************************** - * NOTE: This header file defines an interface to U-Boot. Including - * this (unmodified) header file in another file is considered normal - * use of U-Boot, and does *not* fall under the heading of "derived - * work". - ******************************************************************** - */ - -#ifndef __UBOOT_IMAGE_H__ -#define __UBOOT_IMAGE_H__ - -/* - * Operating System Codes - */ -#define IH_OS_INVALID 0 /* Invalid OS */ -#define IH_OS_OPENBSD 1 /* OpenBSD */ -#define IH_OS_NETBSD 2 /* NetBSD */ -#define IH_OS_FREEBSD 3 /* FreeBSD */ -#define IH_OS_4_4BSD 4 /* 4.4BSD */ -#define IH_OS_LINUX 5 /* Linux */ -#define IH_OS_SVR4 6 /* SVR4 */ -#define IH_OS_ESIX 7 /* Esix */ -#define IH_OS_SOLARIS 8 /* Solaris */ -#define IH_OS_IRIX 9 /* Irix */ -#define IH_OS_SCO 10 /* SCO */ -#define IH_OS_DELL 11 /* Dell */ -#define IH_OS_NCR 12 /* NCR */ -#define IH_OS_LYNXOS 13 /* LynxOS */ -#define IH_OS_VXWORKS 14 /* VxWorks */ -#define IH_OS_PSOS 15 /* pSOS */ -#define IH_OS_QNX 16 /* QNX */ -#define IH_OS_U_BOOT 17 /* Firmware */ -#define IH_OS_RTEMS 18 /* RTEMS */ -#define IH_OS_ARTOS 19 /* ARTOS */ -#define IH_OS_UNITY 20 /* Unity OS */ - -/* - * CPU Architecture Codes (supported by Linux) - */ -#define IH_CPU_INVALID 0 /* Invalid CPU */ -#define IH_CPU_ALPHA 1 /* Alpha */ -#define IH_CPU_ARM 2 /* ARM */ -#define IH_CPU_I386 3 /* Intel x86 */ -#define IH_CPU_IA64 4 /* IA64 */ -#define IH_CPU_MIPS 5 /* MIPS */ -#define IH_CPU_MIPS64 6 /* MIPS 64 Bit */ -#define IH_CPU_PPC 7 /* PowerPC */ -#define IH_CPU_S390 8 /* IBM S390 */ -#define IH_CPU_SH 9 /* SuperH */ -#define IH_CPU_SPARC 10 /* Sparc */ -#define IH_CPU_SPARC64 11 /* Sparc 64 Bit */ -#define IH_CPU_M68K 12 /* M68K */ -#define IH_CPU_NIOS 13 /* Nios-32 */ -#define IH_CPU_MICROBLAZE 14 /* MicroBlaze */ -#define IH_CPU_NIOS2 15 /* Nios-II */ -#define IH_CPU_BLACKFIN 16 /* Blackfin */ -#define IH_CPU_AVR32 17 /* AVR32 */ - -/* - * Image Types - * - * "Standalone Programs" are directly runnable in the environment - * provided by U-Boot; it is expected that (if they behave - * well) you can continue to work in U-Boot after return from - * the Standalone Program. - * "OS Kernel Images" are usually images of some Embedded OS which - * will take over control completely. Usually these programs - * will install their own set of exception handlers, device - * drivers, set up the MMU, etc. - this means, that you cannot - * expect to re-enter U-Boot except by resetting the CPU. - * "RAMDisk Images" are more or less just data blocks, and their - * parameters (address, size) are passed to an OS kernel that is - * being started. - * "Multi-File Images" contain several images, typically an OS - * (Linux) kernel image and one or more data images like - * RAMDisks. This construct is useful for instance when you want - * to boot over the network using BOOTP etc., where the boot - * server provides just a single image file, but you want to get - * for instance an OS kernel and a RAMDisk image. - * - * "Multi-File Images" start with a list of image sizes, each - * image size (in bytes) specified by an "uint32_t" in network - * byte order. This list is terminated by an "(uint32_t)0". - * Immediately after the terminating 0 follow the images, one by - * one, all aligned on "uint32_t" boundaries (size rounded up to - * a multiple of 4 bytes - except for the last file). - * - * "Firmware Images" are binary images containing firmware (like - * U-Boot or FPGA images) which usually will be programmed to - * flash memory. - * - * "Script files" are command sequences that will be executed by - * U-Boot's command interpreter; this feature is especially - * useful when you configure U-Boot to use a real shell (hush) - * as command interpreter (=> Shell Scripts). - */ - -#define IH_TYPE_INVALID 0 /* Invalid Image */ -#define IH_TYPE_STANDALONE 1 /* Standalone Program */ -#define IH_TYPE_KERNEL 2 /* OS Kernel Image */ -#define IH_TYPE_RAMDISK 3 /* RAMDisk Image */ -#define IH_TYPE_MULTI 4 /* Multi-File Image */ -#define IH_TYPE_FIRMWARE 5 /* Firmware Image */ -#define IH_TYPE_SCRIPT 6 /* Script file */ -#define IH_TYPE_FILESYSTEM 7 /* Filesystem Image (any type) */ -#define IH_TYPE_FLATDT 8 /* Binary Flat Device Tree Blob */ - -/* - * Compression Types - */ -#define IH_COMP_NONE 0 /* No Compression Used */ -#define IH_COMP_GZIP 1 /* gzip Compression Used */ -#define IH_COMP_BZIP2 2 /* bzip2 Compression Used */ - -#define IH_MAGIC 0x27051956 /* Image Magic Number */ -#define IH_NMLEN 32 /* Image Name Length */ - -/* - * all data in network byte order (aka natural aka bigendian) - */ - -typedef struct uboot_image_header { - uint32_t ih_magic; /* Image Header Magic Number */ - uint32_t ih_hcrc; /* Image Header CRC Checksum */ - uint32_t ih_time; /* Image Creation Timestamp */ - uint32_t ih_size; /* Image Data Size */ - uint32_t ih_load; /* Data Load Address */ - uint32_t ih_ep; /* Entry Point Address */ - uint32_t ih_dcrc; /* Image Data CRC Checksum */ - uint8_t ih_os; /* Operating System */ - uint8_t ih_arch; /* CPU architecture */ - uint8_t ih_type; /* Image Type */ - uint8_t ih_comp; /* Compression Type */ - uint8_t ih_name[IH_NMLEN]; /* Image Name */ -} uboot_image_header_t; - - -#endif /* __IMAGE_H__ */ diff --git a/qemu/hw/cpu/Makefile.objs b/qemu/hw/cpu/Makefile.objs deleted file mode 100644 index 0954a1872..000000000 --- a/qemu/hw/cpu/Makefile.objs +++ /dev/null @@ -1,5 +0,0 @@ -obj-$(CONFIG_ARM11MPCORE) += arm11mpcore.o -obj-$(CONFIG_REALVIEW) += realview_mpcore.o -obj-$(CONFIG_A9MPCORE) += a9mpcore.o -obj-$(CONFIG_A15MPCORE) += a15mpcore.o - diff --git a/qemu/hw/cpu/a15mpcore.c b/qemu/hw/cpu/a15mpcore.c deleted file mode 100644 index bc05152fd..000000000 --- a/qemu/hw/cpu/a15mpcore.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Cortex-A15MPCore internal peripheral emulation. - * - * Copyright (c) 2012 Linaro Limited. - * Written 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 . - */ - -#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) -{ - A15MPPrivState *s = (A15MPPrivState *)opaque; - - qemu_set_irq(qdev_get_gpio_in(DEVICE(&s->gic), irq), level); -} - -static void a15mp_priv_initfn(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - A15MPPrivState *s = A15MPCORE_PRIV(obj); - DeviceState *gicdev; - - memory_region_init(&s->container, obj, "a15mp-priv-container", 0x8000); - sysbus_init_mmio(sbd, &s->container); - - 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); -} - -static void a15mp_priv_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - A15MPPrivState *s = A15MPCORE_PRIV(dev); - DeviceState *gicdev; - 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); - return; - } - busdev = SYS_BUS_DEVICE(&s->gic); - - /* Pass through outbound IRQ lines from the GIC */ - sysbus_pass_irq(sbd, busdev); - - /* Pass through inbound GPIO lines to the GIC */ - qdev_init_gpio_in(dev, a15mp_priv_set_irq, s->num_irq - 32); - - /* Wire the outputs from each CPU's generic timer to the - * appropriate GIC PPI inputs - */ - for (i = 0; i < s->num_cpu; i++) { - DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); - int ppibase = s->num_irq - 32 + i * 32; - int irq; - /* Mapping from the output timer irq lines from the CPU to the - * GIC PPI inputs used on the A15: - */ - 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-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) - */ - memory_region_add_subregion(&s->container, 0x1000, - sysbus_mmio_get_region(busdev, 0)); - memory_region_add_subregion(&s->container, 0x2000, - sysbus_mmio_get_region(busdev, 1)); -} - -static Property a15mp_priv_properties[] = { - DEFINE_PROP_UINT32("num-cpu", A15MPPrivState, num_cpu, 1), - /* The Cortex-A15MP may have anything from 0 to 224 external interrupt - * IRQ lines (with another 32 internal). We default to 128+32, which - * is the number provided by the Cortex-A15MP test chip in the - * Versatile Express A15 development board. - * Other boards may differ and should set this property appropriately. - */ - DEFINE_PROP_UINT32("num-irq", A15MPPrivState, num_irq, 160), - DEFINE_PROP_END_OF_LIST(), -}; - -static void a15mp_priv_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = a15mp_priv_realize; - dc->props = a15mp_priv_properties; - /* We currently have no savable state */ -} - -static const TypeInfo a15mp_priv_info = { - .name = TYPE_A15MPCORE_PRIV, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(A15MPPrivState), - .instance_init = a15mp_priv_initfn, - .class_init = a15mp_priv_class_init, -}; - -static void a15mp_register_types(void) -{ - type_register_static(&a15mp_priv_info); -} - -type_init(a15mp_register_types) diff --git a/qemu/hw/cpu/a9mpcore.c b/qemu/hw/cpu/a9mpcore.c deleted file mode 100644 index 5459ae8c1..000000000 --- a/qemu/hw/cpu/a9mpcore.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Cortex-A9MPCore internal peripheral emulation. - * - * Copyright (c) 2009 CodeSourcery. - * Copyright (c) 2011 Linaro Limited. - * Written by Paul Brook, Peter Maydell. - * - * 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) -{ - A9MPPrivState *s = (A9MPPrivState *)opaque; - - qemu_set_irq(qdev_get_gpio_in(DEVICE(&s->gic), irq), level); -} - -static void a9mp_priv_initfn(Object *obj) -{ - A9MPPrivState *s = A9MPCORE_PRIV(obj); - - memory_region_init(&s->container, obj, "a9mp-priv-container", 0x2000); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->container); - - object_initialize(&s->scu, sizeof(s->scu), TYPE_A9_SCU); - qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default()); - - object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC); - qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default()); - - object_initialize(&s->gtimer, sizeof(s->gtimer), TYPE_A9_GTIMER); - qdev_set_parent_bus(DEVICE(&s->gtimer), sysbus_get_default()); - - object_initialize(&s->mptimer, sizeof(s->mptimer), TYPE_ARM_MPTIMER); - qdev_set_parent_bus(DEVICE(&s->mptimer), sysbus_get_default()); - - object_initialize(&s->wdt, sizeof(s->wdt), TYPE_ARM_MPTIMER); - qdev_set_parent_bus(DEVICE(&s->wdt), sysbus_get_default()); -} - -static void a9mp_priv_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - A9MPPrivState *s = A9MPCORE_PRIV(dev); - DeviceState *scudev, *gicdev, *gtimerdev, *mptimerdev, *wdtdev; - SysBusDevice *scubusdev, *gicbusdev, *gtimerbusdev, *mptimerbusdev, - *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); - object_property_set_bool(OBJECT(&s->scu), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - scubusdev = SYS_BUS_DEVICE(&s->scu); - - 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); - return; - } - gicbusdev = SYS_BUS_DEVICE(&s->gic); - - /* Pass through outbound IRQ lines from the GIC */ - sysbus_pass_irq(sbd, gicbusdev); - - /* Pass through inbound GPIO lines to the GIC */ - qdev_init_gpio_in(dev, a9mp_priv_set_irq, s->num_irq - 32); - - gtimerdev = DEVICE(&s->gtimer); - qdev_prop_set_uint32(gtimerdev, "num-cpu", s->num_cpu); - object_property_set_bool(OBJECT(&s->gtimer), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - gtimerbusdev = SYS_BUS_DEVICE(&s->gtimer); - - mptimerdev = DEVICE(&s->mptimer); - qdev_prop_set_uint32(mptimerdev, "num-cpu", s->num_cpu); - object_property_set_bool(OBJECT(&s->mptimer), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - mptimerbusdev = SYS_BUS_DEVICE(&s->mptimer); - - wdtdev = DEVICE(&s->wdt); - qdev_prop_set_uint32(wdtdev, "num-cpu", s->num_cpu); - object_property_set_bool(OBJECT(&s->wdt), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - wdtbusdev = SYS_BUS_DEVICE(&s->wdt); - - /* Memory map (addresses are offsets from PERIPHBASE): - * 0x0000-0x00ff -- Snoop Control Unit - * 0x0100-0x01ff -- GIC CPU interface - * 0x0200-0x02ff -- Global Timer - * 0x0300-0x05ff -- nothing - * 0x0600-0x06ff -- private timers and watchdogs - * 0x0700-0x0fff -- nothing - * 0x1000-0x1fff -- GIC Distributor - */ - memory_region_add_subregion(&s->container, 0, - sysbus_mmio_get_region(scubusdev, 0)); - /* GIC CPU interface */ - memory_region_add_subregion(&s->container, 0x100, - sysbus_mmio_get_region(gicbusdev, 1)); - memory_region_add_subregion(&s->container, 0x200, - sysbus_mmio_get_region(gtimerbusdev, 0)); - /* Note that the A9 exposes only the "timer/watchdog for this core" - * memory region, not the "timer/watchdog for core X" ones 11MPcore has. - */ - memory_region_add_subregion(&s->container, 0x600, - sysbus_mmio_get_region(mptimerbusdev, 0)); - memory_region_add_subregion(&s->container, 0x620, - sysbus_mmio_get_region(wdtbusdev, 0)); - memory_region_add_subregion(&s->container, 0x1000, - sysbus_mmio_get_region(gicbusdev, 0)); - - /* Wire up the interrupt from each watchdog and timer. - * For each core the global timer is PPI 27, the private - * timer is PPI 29 and the watchdog PPI 30. - */ - for (i = 0; i < s->num_cpu; i++) { - int ppibase = (s->num_irq - 32) + i * 32; - sysbus_connect_irq(gtimerbusdev, i, - qdev_get_gpio_in(gicdev, ppibase + 27)); - sysbus_connect_irq(mptimerbusdev, i, - qdev_get_gpio_in(gicdev, ppibase + 29)); - sysbus_connect_irq(wdtbusdev, i, - qdev_get_gpio_in(gicdev, ppibase + 30)); - } -} - -static Property a9mp_priv_properties[] = { - DEFINE_PROP_UINT32("num-cpu", A9MPPrivState, num_cpu, 1), - /* The Cortex-A9MP may have anything from 0 to 224 external interrupt - * IRQ lines (with another 32 internal). We default to 64+32, which - * is the number provided by the Cortex-A9MP test chip in the - * Realview PBX-A9 and Versatile Express A9 development boards. - * Other boards may differ and should set this property appropriately. - */ - DEFINE_PROP_UINT32("num-irq", A9MPPrivState, num_irq, 96), - DEFINE_PROP_END_OF_LIST(), -}; - -static void a9mp_priv_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = a9mp_priv_realize; - dc->props = a9mp_priv_properties; -} - -static const TypeInfo a9mp_priv_info = { - .name = TYPE_A9MPCORE_PRIV, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(A9MPPrivState), - .instance_init = a9mp_priv_initfn, - .class_init = a9mp_priv_class_init, -}; - -static void a9mp_register_types(void) -{ - type_register_static(&a9mp_priv_info); -} - -type_init(a9mp_register_types) diff --git a/qemu/hw/cpu/arm11mpcore.c b/qemu/hw/cpu/arm11mpcore.c deleted file mode 100644 index eb244658b..000000000 --- a/qemu/hw/cpu/arm11mpcore.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * ARM11MPCore internal peripheral emulation. - * - * Copyright (c) 2006-2007 CodeSourcery. - * Written by Paul Brook - * - * 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" - - -static void mpcore_priv_set_irq(void *opaque, int irq, int level) -{ - ARM11MPCorePriveState *s = (ARM11MPCorePriveState *)opaque; - - qemu_set_irq(qdev_get_gpio_in(DEVICE(&s->gic), irq), level); -} - -static void mpcore_priv_map_setup(ARM11MPCorePriveState *s) -{ - int i; - SysBusDevice *scubusdev = SYS_BUS_DEVICE(&s->scu); - DeviceState *gicdev = DEVICE(&s->gic); - SysBusDevice *gicbusdev = SYS_BUS_DEVICE(&s->gic); - SysBusDevice *timerbusdev = SYS_BUS_DEVICE(&s->mptimer); - SysBusDevice *wdtbusdev = SYS_BUS_DEVICE(&s->wdtimer); - - memory_region_add_subregion(&s->container, 0, - sysbus_mmio_get_region(scubusdev, 0)); - /* GIC CPU interfaces: "current CPU" at 0x100, then specific CPUs - * at 0x200, 0x300... - */ - for (i = 0; i < (s->num_cpu + 1); i++) { - hwaddr offset = 0x100 + (i * 0x100); - memory_region_add_subregion(&s->container, offset, - sysbus_mmio_get_region(gicbusdev, i + 1)); - } - /* Add the regions for timer and watchdog for "current CPU" and - * for each specific CPU. - */ - for (i = 0; i < (s->num_cpu + 1); i++) { - /* Timers at 0x600, 0x700, ...; watchdogs at 0x620, 0x720, ... */ - hwaddr offset = 0x600 + i * 0x100; - memory_region_add_subregion(&s->container, offset, - sysbus_mmio_get_region(timerbusdev, i)); - memory_region_add_subregion(&s->container, offset + 0x20, - sysbus_mmio_get_region(wdtbusdev, i)); - } - memory_region_add_subregion(&s->container, 0x1000, - sysbus_mmio_get_region(gicbusdev, 0)); - /* Wire up the interrupt from each watchdog and timer. - * For each core the timer is PPI 29 and the watchdog PPI 30. - */ - for (i = 0; i < s->num_cpu; i++) { - int ppibase = (s->num_irq - 32) + i * 32; - sysbus_connect_irq(timerbusdev, i, - qdev_get_gpio_in(gicdev, ppibase + 29)); - sysbus_connect_irq(wdtbusdev, i, - qdev_get_gpio_in(gicdev, ppibase + 30)); - } -} - -static void mpcore_priv_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - ARM11MPCorePriveState *s = ARM11MPCORE_PRIV(dev); - DeviceState *scudev = DEVICE(&s->scu); - DeviceState *gicdev = DEVICE(&s->gic); - DeviceState *mptimerdev = DEVICE(&s->mptimer); - DeviceState *wdtimerdev = DEVICE(&s->wdtimer); - Error *err = NULL; - - qdev_prop_set_uint32(scudev, "num-cpu", s->num_cpu); - object_property_set_bool(OBJECT(&s->scu), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - - qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu); - qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq); - object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - - /* Pass through outbound IRQ lines from the GIC */ - sysbus_pass_irq(sbd, SYS_BUS_DEVICE(&s->gic)); - - /* Pass through inbound GPIO lines to the GIC */ - qdev_init_gpio_in(dev, mpcore_priv_set_irq, s->num_irq - 32); - - qdev_prop_set_uint32(mptimerdev, "num-cpu", s->num_cpu); - object_property_set_bool(OBJECT(&s->mptimer), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - - qdev_prop_set_uint32(wdtimerdev, "num-cpu", s->num_cpu); - object_property_set_bool(OBJECT(&s->wdtimer), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - - mpcore_priv_map_setup(s); -} - -static void mpcore_priv_initfn(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - ARM11MPCorePriveState *s = ARM11MPCORE_PRIV(obj); - - memory_region_init(&s->container, OBJECT(s), - "mpcore-priv-container", 0x2000); - sysbus_init_mmio(sbd, &s->container); - - object_initialize(&s->scu, sizeof(s->scu), TYPE_ARM11_SCU); - qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default()); - - object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC); - qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default()); - /* Request the legacy 11MPCore GIC behaviour: */ - qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 0); - - object_initialize(&s->mptimer, sizeof(s->mptimer), TYPE_ARM_MPTIMER); - qdev_set_parent_bus(DEVICE(&s->mptimer), sysbus_get_default()); - - object_initialize(&s->wdtimer, sizeof(s->wdtimer), TYPE_ARM_MPTIMER); - qdev_set_parent_bus(DEVICE(&s->wdtimer), sysbus_get_default()); -} - -static Property mpcore_priv_properties[] = { - DEFINE_PROP_UINT32("num-cpu", ARM11MPCorePriveState, num_cpu, 1), - /* The ARM11 MPCORE TRM says the on-chip controller may have - * anything from 0 to 224 external interrupt IRQ lines (with another - * 32 internal). We default to 32+32, which is the number provided by - * the ARM11 MPCore test chip in the Realview Versatile Express - * coretile. Other boards may differ and should set this property - * appropriately. Some Linux kernels may not boot if the hardware - * has more IRQ lines than the kernel expects. - */ - DEFINE_PROP_UINT32("num-irq", ARM11MPCorePriveState, num_irq, 64), - DEFINE_PROP_END_OF_LIST(), -}; - -static void mpcore_priv_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = mpcore_priv_realize; - dc->props = mpcore_priv_properties; -} - -static const TypeInfo mpcore_priv_info = { - .name = TYPE_ARM11MPCORE_PRIV, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ARM11MPCorePriveState), - .instance_init = mpcore_priv_initfn, - .class_init = mpcore_priv_class_init, -}; - -static void arm11mpcore_register_types(void) -{ - type_register_static(&mpcore_priv_info); -} - -type_init(arm11mpcore_register_types) diff --git a/qemu/hw/cpu/realview_mpcore.c b/qemu/hw/cpu/realview_mpcore.c deleted file mode 100644 index 39d4ebeb1..000000000 --- a/qemu/hw/cpu/realview_mpcore.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * RealView ARM11MPCore internal peripheral emulation - * - * Copyright (c) 2006-2007 CodeSourcery. - * Copyright (c) 2013 SUSE LINUX Products GmbH - * Written by Paul Brook and Andreas Färber - * - * 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" - -#define TYPE_REALVIEW_MPCORE_RIRQ "realview_mpcore" -#define REALVIEW_MPCORE_RIRQ(obj) \ - OBJECT_CHECK(mpcore_rirq_state, (obj), TYPE_REALVIEW_MPCORE_RIRQ) - -/* Dummy PIC to route IRQ lines. The baseboard has 4 independent IRQ - controllers. The output of these, plus some of the raw input lines - are fed into a single SMP-aware interrupt controller on the CPU. */ -typedef struct { - SysBusDevice parent_obj; - - qemu_irq cpuic[32]; - qemu_irq rvic[4][64]; - uint32_t num_cpu; - - ARM11MPCorePriveState priv; - RealViewGICState gic[4]; -} mpcore_rirq_state; - -/* Map baseboard IRQs onto CPU IRQ lines. */ -static const int mpcore_irq_map[32] = { - -1, -1, -1, -1, 1, 2, -1, -1, - -1, -1, 6, -1, 4, 5, -1, -1, - -1, 14, 15, 0, 7, 8, -1, -1, - -1, -1, -1, -1, 9, 3, -1, -1, -}; - -static void mpcore_rirq_set_irq(void *opaque, int irq, int level) -{ - mpcore_rirq_state *s = (mpcore_rirq_state *)opaque; - int i; - - for (i = 0; i < 4; i++) { - qemu_set_irq(s->rvic[i][irq], level); - } - if (irq < 32) { - irq = mpcore_irq_map[irq]; - if (irq >= 0) { - qemu_set_irq(s->cpuic[irq], level); - } - } -} - -static void realview_mpcore_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - mpcore_rirq_state *s = REALVIEW_MPCORE_RIRQ(dev); - DeviceState *priv = DEVICE(&s->priv); - DeviceState *gic; - SysBusDevice *gicbusdev; - Error *err = NULL; - int n; - int i; - - qdev_prop_set_uint32(priv, "num-cpu", s->num_cpu); - object_property_set_bool(OBJECT(&s->priv), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - sysbus_pass_irq(sbd, SYS_BUS_DEVICE(&s->priv)); - for (i = 0; i < 32; i++) { - s->cpuic[i] = qdev_get_gpio_in(priv, i); - } - /* ??? IRQ routing is hardcoded to "normal" mode. */ - for (n = 0; n < 4; n++) { - object_property_set_bool(OBJECT(&s->gic[n]), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - gic = DEVICE(&s->gic[n]); - gicbusdev = SYS_BUS_DEVICE(&s->gic[n]); - sysbus_mmio_map(gicbusdev, 0, 0x10040000 + n * 0x10000); - sysbus_connect_irq(gicbusdev, 0, s->cpuic[10 + n]); - for (i = 0; i < 64; i++) { - s->rvic[n][i] = qdev_get_gpio_in(gic, i); - } - } - qdev_init_gpio_in(dev, mpcore_rirq_set_irq, 64); -} - -static void mpcore_rirq_init(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - mpcore_rirq_state *s = REALVIEW_MPCORE_RIRQ(obj); - SysBusDevice *privbusdev; - int i; - - object_initialize(&s->priv, sizeof(s->priv), TYPE_ARM11MPCORE_PRIV); - qdev_set_parent_bus(DEVICE(&s->priv), sysbus_get_default()); - privbusdev = SYS_BUS_DEVICE(&s->priv); - sysbus_init_mmio(sbd, sysbus_mmio_get_region(privbusdev, 0)); - - for (i = 0; i < 4; i++) { - object_initialize(&s->gic[i], sizeof(s->gic[i]), TYPE_REALVIEW_GIC); - qdev_set_parent_bus(DEVICE(&s->gic[i]), sysbus_get_default()); - } -} - -static Property mpcore_rirq_properties[] = { - DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void mpcore_rirq_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = realview_mpcore_realize; - dc->props = mpcore_rirq_properties; -} - -static const TypeInfo mpcore_rirq_info = { - .name = TYPE_REALVIEW_MPCORE_RIRQ, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(mpcore_rirq_state), - .instance_init = mpcore_rirq_init, - .class_init = mpcore_rirq_class_init, -}; - -static void realview_mpcore_register_types(void) -{ - type_register_static(&mpcore_rirq_info); -} - -type_init(realview_mpcore_register_types) diff --git a/qemu/hw/cris/Makefile.objs b/qemu/hw/cris/Makefile.objs deleted file mode 100644 index 7624173f7..000000000 --- a/qemu/hw/cris/Makefile.objs +++ /dev/null @@ -1,2 +0,0 @@ -obj-y += boot.o -obj-y += axis_dev88.o diff --git a/qemu/hw/cris/axis_dev88.c b/qemu/hw/cris/axis_dev88.c deleted file mode 100644 index 9f5865874..000000000 --- a/qemu/hw/cris/axis_dev88.c +++ /dev/null @@ -1,365 +0,0 @@ -/* - * QEMU model for the AXIS devboard 88. - * - * Copyright (c) 2009 Edgar E. Iglesias, Axis Communications AB. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/sysbus.h" -#include "net/net.h" -#include "hw/block/flash.h" -#include "hw/boards.h" -#include "hw/cris/etraxfs.h" -#include "hw/loader.h" -#include "elf.h" -#include "boot.h" -#include "sysemu/block-backend.h" -#include "exec/address-spaces.h" -#include "sysemu/qtest.h" - -#define D(x) -#define DNAND(x) - -struct nand_state_t -{ - DeviceState *nand; - MemoryRegion iomem; - unsigned int rdy:1; - unsigned int ale:1; - unsigned int cle:1; - unsigned int ce:1; -}; - -static struct nand_state_t nand_state; -static uint64_t nand_read(void *opaque, hwaddr addr, unsigned size) -{ - struct nand_state_t *s = opaque; - uint32_t r; - int rdy; - - r = nand_getio(s->nand); - nand_getpins(s->nand, &rdy); - s->rdy = rdy; - - DNAND(printf("%s addr=%x r=%x\n", __func__, addr, r)); - return r; -} - -static void -nand_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - struct nand_state_t *s = opaque; - int rdy; - - DNAND(printf("%s addr=%x v=%x\n", __func__, addr, (unsigned)value)); - nand_setpins(s->nand, s->cle, s->ale, s->ce, 1, 0); - nand_setio(s->nand, value); - nand_getpins(s->nand, &rdy); - s->rdy = rdy; -} - -static const MemoryRegionOps nand_ops = { - .read = nand_read, - .write = nand_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct tempsensor_t -{ - unsigned int shiftreg; - unsigned int count; - enum { - ST_OUT, ST_IN, ST_Z - } state; - - uint16_t regs[3]; -}; - -static void tempsensor_clkedge(struct tempsensor_t *s, - unsigned int clk, unsigned int data_in) -{ - D(printf("%s clk=%d state=%d sr=%x\n", __func__, - clk, s->state, s->shiftreg)); - if (s->count == 0) { - s->count = 16; - s->state = ST_OUT; - } - switch (s->state) { - case ST_OUT: - /* Output reg is clocked at negedge. */ - if (!clk) { - s->count--; - s->shiftreg <<= 1; - if (s->count == 0) { - s->shiftreg = 0; - s->state = ST_IN; - s->count = 16; - } - } - break; - case ST_Z: - if (clk) { - s->count--; - if (s->count == 0) { - s->shiftreg = 0; - s->state = ST_OUT; - s->count = 16; - } - } - break; - case ST_IN: - /* Indata is sampled at posedge. */ - if (clk) { - s->count--; - s->shiftreg <<= 1; - s->shiftreg |= data_in & 1; - if (s->count == 0) { - D(printf("%s cfgreg=%x\n", __func__, s->shiftreg)); - s->regs[0] = s->shiftreg; - s->state = ST_OUT; - s->count = 16; - - if ((s->regs[0] & 0xff) == 0) { - /* 25 degrees celsius. */ - s->shiftreg = 0x0b9f; - } else if ((s->regs[0] & 0xff) == 0xff) { - /* Sensor ID, 0x8100 LM70. */ - s->shiftreg = 0x8100; - } else - printf("Invalid tempsens state %x\n", s->regs[0]); - } - } - break; - } -} - - -#define RW_PA_DOUT 0x00 -#define R_PA_DIN 0x01 -#define RW_PA_OE 0x02 -#define RW_PD_DOUT 0x10 -#define R_PD_DIN 0x11 -#define RW_PD_OE 0x12 - -static struct gpio_state_t -{ - MemoryRegion iomem; - struct nand_state_t *nand; - struct tempsensor_t tempsensor; - uint32_t regs[0x5c / 4]; -} gpio_state; - -static uint64_t gpio_read(void *opaque, hwaddr addr, unsigned size) -{ - struct gpio_state_t *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) - { - case R_PA_DIN: - r = s->regs[RW_PA_DOUT] & s->regs[RW_PA_OE]; - - /* Encode pins from the nand. */ - r |= s->nand->rdy << 7; - break; - case R_PD_DIN: - r = s->regs[RW_PD_DOUT] & s->regs[RW_PD_OE]; - - /* Encode temp sensor pins. */ - r |= (!!(s->tempsensor.shiftreg & 0x10000)) << 4; - break; - - default: - r = s->regs[addr]; - break; - } - return r; - D(printf("%s %x=%x\n", __func__, addr, r)); -} - -static void gpio_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - struct gpio_state_t *s = opaque; - D(printf("%s %x=%x\n", __func__, addr, (unsigned)value)); - - addr >>= 2; - switch (addr) - { - case RW_PA_DOUT: - /* Decode nand pins. */ - s->nand->ale = !!(value & (1 << 6)); - s->nand->cle = !!(value & (1 << 5)); - s->nand->ce = !!(value & (1 << 4)); - - s->regs[addr] = value; - break; - - case RW_PD_DOUT: - /* Temp sensor clk. */ - if ((s->regs[addr] ^ value) & 2) - tempsensor_clkedge(&s->tempsensor, !!(value & 2), - !!(value & 16)); - s->regs[addr] = value; - break; - - default: - s->regs[addr] = value; - break; - } -} - -static const MemoryRegionOps gpio_ops = { - .read = gpio_read, - .write = gpio_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -#define INTMEM_SIZE (128 * 1024) - -static struct cris_load_info li; - -static -void axisdev88_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; - CRISCPU *cpu; - CPUCRISState *env; - DeviceState *dev; - SysBusDevice *s; - DriveInfo *nand; - qemu_irq irq[30], nmi[2]; - void *etraxfs_dmac; - struct etraxfs_dma_client *dma_eth; - int i; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *phys_ram = g_new(MemoryRegion, 1); - MemoryRegion *phys_intmem = g_new(MemoryRegion, 1); - - /* init CPUs */ - if (cpu_model == NULL) { - cpu_model = "crisv32"; - } - cpu = cpu_cris_init(cpu_model); - env = &cpu->env; - - /* allocate RAM */ - memory_region_allocate_system_memory(phys_ram, NULL, "axisdev88.ram", - ram_size); - memory_region_add_subregion(address_space_mem, 0x40000000, phys_ram); - - /* 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_fatal); - vmstate_register_ram_global(phys_intmem); - memory_region_add_subregion(address_space_mem, 0x38000000, phys_intmem); - - /* Attach a NAND flash to CS1. */ - nand = drive_get(IF_MTD, 0, 0); - nand_state.nand = nand_init(nand ? blk_by_legacy_dinfo(nand) : NULL, - NAND_MFR_STMICRO, 0x39); - memory_region_init_io(&nand_state.iomem, NULL, &nand_ops, &nand_state, - "nand", 0x05000000); - memory_region_add_subregion(address_space_mem, 0x10000000, - &nand_state.iomem); - - gpio_state.nand = &nand_state; - memory_region_init_io(&gpio_state.iomem, NULL, &gpio_ops, &gpio_state, - "gpio", 0x5c); - memory_region_add_subregion(address_space_mem, 0x3001a000, - &gpio_state.iomem); - - - dev = qdev_create(NULL, "etraxfs,pic"); - /* FIXME: Is there a proper way to signal vectors to the CPU core? */ - qdev_prop_set_ptr(dev, "interrupt_vector", &env->interrupt_vector); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(s, 0, 0x3001c000); - sysbus_connect_irq(s, 0, qdev_get_gpio_in(DEVICE(cpu), CRIS_CPU_IRQ)); - sysbus_connect_irq(s, 1, qdev_get_gpio_in(DEVICE(cpu), CRIS_CPU_NMI)); - for (i = 0; i < 30; i++) { - irq[i] = qdev_get_gpio_in(dev, i); - } - nmi[0] = qdev_get_gpio_in(dev, 30); - nmi[1] = qdev_get_gpio_in(dev, 31); - - etraxfs_dmac = etraxfs_dmac_init(0x30000000, 10); - for (i = 0; i < 10; i++) { - /* On ETRAX, odd numbered channels are inputs. */ - etraxfs_dmac_connect(etraxfs_dmac, i, irq + 7 + i, i & 1); - } - - /* Add the two ethernet blocks. */ - dma_eth = g_malloc0(sizeof dma_eth[0] * 4); /* Allocate 4 channels. */ - etraxfs_eth_init(&nd_table[0], 0x30034000, 1, &dma_eth[0], &dma_eth[1]); - if (nb_nics > 1) { - etraxfs_eth_init(&nd_table[1], 0x30036000, 2, &dma_eth[2], &dma_eth[3]); - } - - /* The DMA Connector block is missing, hardwire things for now. */ - etraxfs_dmac_connect_client(etraxfs_dmac, 0, &dma_eth[0]); - etraxfs_dmac_connect_client(etraxfs_dmac, 1, &dma_eth[1]); - if (nb_nics > 1) { - etraxfs_dmac_connect_client(etraxfs_dmac, 6, &dma_eth[2]); - etraxfs_dmac_connect_client(etraxfs_dmac, 7, &dma_eth[3]); - } - - /* 2 timers. */ - sysbus_create_varargs("etraxfs,timer", 0x3001e000, irq[0x1b], nmi[1], NULL); - sysbus_create_varargs("etraxfs,timer", 0x3005e000, irq[0x1b], nmi[1], NULL); - - for (i = 0; i < 4; i++) { - sysbus_create_simple("etraxfs,serial", 0x30026000 + i * 0x2000, - irq[0x14 + i]); - } - - if (kernel_filename) { - li.image_filename = kernel_filename; - li.cmdline = kernel_cmdline; - cris_load_image(cpu, &li); - } else if (!qtest_enabled()) { - fprintf(stderr, "Kernel image must be specified\n"); - exit(1); - } -} - -static void axisdev88_machine_init(MachineClass *mc) -{ - mc->desc = "AXIS devboard 88"; - mc->init = axisdev88_init; - mc->is_default = 1; -} - -DEFINE_MACHINE("axis-dev88", axisdev88_machine_init) diff --git a/qemu/hw/cris/boot.c b/qemu/hw/cris/boot.c deleted file mode 100644 index f896ed7f8..000000000 --- a/qemu/hw/cris/boot.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * CRIS image loading. - * - * Copyright (c) 2010 Edgar E. Iglesias, Axis Communications AB. - * - * 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 "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) -{ - CRISCPU *cpu = opaque; - CPUCRISState *env = &cpu->env; - struct cris_load_info *li; - - li = env->load_info; - - cpu_reset(CPU(cpu)); - - if (!li) { - /* nothing more to do. */ - return; - } - - env->pc = li->entry; - - if (li->image_filename) { - env->regs[8] = 0x56902387; /* RAM boot magic. */ - env->regs[9] = 0x40004000 + li->image_size; - } - - if (li->cmdline) { - /* Let the kernel know we are modifying the cmdline. */ - env->regs[10] = 0x87109563; - env->regs[11] = 0x40000000; - } -} - -static uint64_t translate_kernel_address(void *opaque, uint64_t addr) -{ - return addr - 0x80000000LL; -} - -void cris_load_image(CRISCPU *cpu, struct cris_load_info *li) -{ - CPUCRISState *env = &cpu->env; - uint64_t entry, high; - int kcmdline_len; - int image_size; - - env->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, EM_CRIS, 0, 0); - li->entry = entry; - if (image_size < 0) { - /* Takes a kimage from the axis devboard SDK. */ - image_size = load_image_targphys(li->image_filename, 0x40004000, - ram_size); - li->entry = 0x40004000; - } - - if (image_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - li->image_filename); - exit(1); - } - - if (li->cmdline && (kcmdline_len = strlen(li->cmdline))) { - if (kcmdline_len > 256) { - fprintf(stderr, "Too long CRIS kernel cmdline (max 256)\n"); - exit(1); - } - pstrcpy_targphys("cmdline", 0x40000000, 256, li->cmdline); - } - qemu_register_reset(main_cpu_reset, cpu); -} diff --git a/qemu/hw/cris/boot.h b/qemu/hw/cris/boot.h deleted file mode 100644 index c4d3fa6f6..000000000 --- a/qemu/hw/cris/boot.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _CRIS_BOOT_H -#define HW_CRIS_BOOT_H 1 - -struct cris_load_info -{ - const char *image_filename; - const char *cmdline; - int image_size; - - hwaddr entry; -}; - -void cris_load_image(CRISCPU *cpu, struct cris_load_info *li); - -#endif diff --git a/qemu/hw/display/Makefile.objs b/qemu/hw/display/Makefile.objs deleted file mode 100644 index d99780eeb..000000000 --- a/qemu/hw/display/Makefile.objs +++ /dev/null @@ -1,45 +0,0 @@ -common-obj-$(CONFIG_ADS7846) += ads7846.o -common-obj-$(CONFIG_VGA_CIRRUS) += cirrus_vga.o -common-obj-$(CONFIG_G364FB) += g364fb.o -common-obj-$(CONFIG_JAZZ_LED) += jazz_led.o -common-obj-$(CONFIG_PL110) += pl110.o -common-obj-$(CONFIG_SSD0303) += ssd0303.o -common-obj-$(CONFIG_SSD0323) += ssd0323.o -common-obj-$(CONFIG_XEN_BACKEND) += xenfb.o - -common-obj-$(CONFIG_VGA_PCI) += vga-pci.o -common-obj-$(CONFIG_VGA_ISA) += vga-isa.o -common-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o -common-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o - -common-obj-$(CONFIG_BLIZZARD) += blizzard.o -common-obj-$(CONFIG_EXYNOS4) += exynos4210_fimd.o -common-obj-$(CONFIG_FRAMEBUFFER) += framebuffer.o -common-obj-$(CONFIG_MILKYMIST) += milkymist-vgafb.o -common-obj-$(CONFIG_ZAURUS) += tc6393xb.o - -ifeq ($(CONFIG_MILKYMIST_TMU2),y) -common-obj-y += milkymist-tmu2.o -milkymist-tmu2.o-cflags := $(OPENGL_CFLAGS) -milkymist-tmu2.o-libs += $(OPENGL_LIBS) -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 - -obj-$(CONFIG_VGA) += vga.o - -common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.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 deleted file mode 100644 index 05aa2d1e6..000000000 --- a/qemu/hw/display/ads7846.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * TI ADS7846 / TSC2046 chip emulation. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include "qemu/osdep.h" -#include "hw/ssi/ssi.h" -#include "ui/console.h" - -typedef struct { - SSISlave ssidev; - qemu_irq interrupt; - - int input[8]; - int pressure; - int noise; - - int cycle; - int output; -} ADS7846State; - -/* Control-byte bitfields */ -#define CB_PD0 (1 << 0) -#define CB_PD1 (1 << 1) -#define CB_SER (1 << 2) -#define CB_MODE (1 << 3) -#define CB_A0 (1 << 4) -#define CB_A1 (1 << 5) -#define CB_A2 (1 << 6) -#define CB_START (1 << 7) - -#define X_AXIS_DMAX 3470 -#define X_AXIS_MIN 290 -#define Y_AXIS_DMAX 3450 -#define Y_AXIS_MIN 200 - -#define ADS_VBAT 2000 -#define ADS_VAUX 2000 -#define ADS_TEMP0 2000 -#define ADS_TEMP1 3000 -#define ADS_XPOS(x, y) (X_AXIS_MIN + ((X_AXIS_DMAX * (x)) >> 15)) -#define ADS_YPOS(x, y) (Y_AXIS_MIN + ((Y_AXIS_DMAX * (y)) >> 15)) -#define ADS_Z1POS(x, y) 600 -#define ADS_Z2POS(x, y) (600 + 6000 / ADS_XPOS(x, y)) - -static void ads7846_int_update(ADS7846State *s) -{ - if (s->interrupt) - qemu_set_irq(s->interrupt, s->pressure == 0); -} - -static uint32_t ads7846_transfer(SSISlave *dev, uint32_t value) -{ - ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, dev); - - switch (s->cycle ++) { - case 0: - if (!(value & CB_START)) { - s->cycle = 0; - break; - } - - s->output = s->input[(value >> 4) & 7]; - - /* Imitate the ADC noise, some drivers expect this. */ - s->noise = (s->noise + 3) & 7; - switch ((value >> 4) & 7) { - case 1: s->output += s->noise ^ 2; break; - case 3: s->output += s->noise ^ 0; break; - case 4: s->output += s->noise ^ 7; break; - case 5: s->output += s->noise ^ 5; break; - } - - if (value & CB_MODE) - s->output >>= 4; /* 8 bits instead of 12 */ - - break; - case 1: - s->cycle = 0; - break; - } - return s->output; -} - -static void ads7846_ts_event(void *opaque, - int x, int y, int z, int buttons_state) -{ - ADS7846State *s = opaque; - - if (buttons_state) { - x = 0x7fff - x; - s->input[1] = ADS_XPOS(x, y); - s->input[3] = ADS_Z1POS(x, y); - s->input[4] = ADS_Z2POS(x, y); - s->input[5] = ADS_YPOS(x, y); - } - - if (s->pressure == !buttons_state) { - s->pressure = !!buttons_state; - - ads7846_int_update(s); - } -} - -static int ads7856_post_load(void *opaque, int version_id) -{ - ADS7846State *s = opaque; - - s->pressure = 0; - ads7846_int_update(s); - return 0; -} - -static const VMStateDescription vmstate_ads7846 = { - .name = "ads7846", - .version_id = 1, - .minimum_version_id = 1, - .post_load = ads7856_post_load, - .fields = (VMStateField[]) { - VMSTATE_SSI_SLAVE(ssidev, ADS7846State), - VMSTATE_INT32_ARRAY(input, ADS7846State, 8), - VMSTATE_INT32(noise, ADS7846State), - VMSTATE_INT32(cycle, ADS7846State), - VMSTATE_INT32(output, ADS7846State), - VMSTATE_END_OF_LIST() - } -}; - -static int ads7846_init(SSISlave *d) -{ - DeviceState *dev = DEVICE(d); - ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, d); - - qdev_init_gpio_out(dev, &s->interrupt, 1); - - s->input[0] = ADS_TEMP0; /* TEMP0 */ - s->input[2] = ADS_VBAT; /* VBAT */ - s->input[6] = ADS_VAUX; /* VAUX */ - s->input[7] = ADS_TEMP1; /* TEMP1 */ - - /* We want absolute coordinates */ - qemu_add_mouse_event_handler(ads7846_ts_event, s, 1, - "QEMU ADS7846-driven Touchscreen"); - - ads7846_int_update(s); - - vmstate_register(NULL, -1, &vmstate_ads7846, s); - return 0; -} - -static void ads7846_class_init(ObjectClass *klass, void *data) -{ - SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - - k->init = ads7846_init; - k->transfer = ads7846_transfer; -} - -static const TypeInfo ads7846_info = { - .name = "ads7846", - .parent = TYPE_SSI_SLAVE, - .instance_size = sizeof(ADS7846State), - .class_init = ads7846_class_init, -}; - -static void ads7846_register_types(void) -{ - type_register_static(&ads7846_info); -} - -type_init(ads7846_register_types) diff --git a/qemu/hw/display/bcm2835_fb.c b/qemu/hw/display/bcm2835_fb.c deleted file mode 100644 index 506f1d3d9..000000000 --- a/qemu/hw/display/bcm2835_fb.c +++ /dev/null @@ -1,425 +0,0 @@ -/* - * 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 - * - * 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 . - * - */ - -#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 deleted file mode 100644 index c231960d9..000000000 --- a/qemu/hw/display/blizzard.c +++ /dev/null @@ -1,987 +0,0 @@ -/* - * Epson S1D13744/S1D13745 (Blizzard/Hailstorm/Tornado) LCD/TV controller. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "ui/console.h" -#include "hw/devices.h" -#include "ui/pixel_ops.h" - -typedef void (*blizzard_fn_t)(uint8_t *, const uint8_t *, unsigned int); - -typedef struct { - uint8_t reg; - uint32_t addr; - int swallow; - - int pll; - int pll_range; - int pll_ctrl; - uint8_t pll_mode; - uint8_t clksel; - int memenable; - int memrefresh; - uint8_t timing[3]; - int priority; - - uint8_t lcd_config; - int x; - int y; - int skipx; - int skipy; - uint8_t hndp; - uint8_t vndp; - uint8_t hsync; - uint8_t vsync; - uint8_t pclk; - uint8_t u; - uint8_t v; - uint8_t yrc[2]; - int ix[2]; - int iy[2]; - int ox[2]; - int oy[2]; - - int enable; - int blank; - int bpp; - int invalidate; - int mx[2]; - int my[2]; - uint8_t mode; - uint8_t effect; - uint8_t iformat; - uint8_t source; - QemuConsole *con; - blizzard_fn_t *line_fn_tab[2]; - void *fb; - - uint8_t hssi_config[3]; - uint8_t tv_config; - uint8_t tv_timing[4]; - uint8_t vbi; - uint8_t tv_x; - uint8_t tv_y; - uint8_t tv_test; - uint8_t tv_filter_config; - uint8_t tv_filter_idx; - uint8_t tv_filter_coeff[0x20]; - uint8_t border_r; - uint8_t border_g; - uint8_t border_b; - uint8_t gamma_config; - uint8_t gamma_idx; - uint8_t gamma_lut[0x100]; - uint8_t matrix_ena; - uint8_t matrix_coeff[0x12]; - uint8_t matrix_r; - uint8_t matrix_g; - uint8_t matrix_b; - uint8_t pm; - uint8_t status; - uint8_t rgbgpio_dir; - uint8_t rgbgpio; - uint8_t gpio_dir; - uint8_t gpio; - uint8_t gpio_edge[2]; - uint8_t gpio_irq; - uint8_t gpio_pdown; - - struct { - int x; - int y; - int dx; - int dy; - int len; - int buflen; - void *buf; - void *data; - uint16_t *ptr; - int angle; - int pitch; - blizzard_fn_t line_fn; - } data; -} BlizzardState; - -/* Bytes(!) per pixel */ -static const int blizzard_iformat_bpp[0x10] = { - 0, - 2, /* RGB 5:6:5*/ - 3, /* RGB 6:6:6 mode 1 */ - 3, /* RGB 8:8:8 mode 1 */ - 0, 0, - 4, /* RGB 6:6:6 mode 2 */ - 4, /* RGB 8:8:8 mode 2 */ - 0, /* YUV 4:2:2 */ - 0, /* YUV 4:2:0 */ - 0, 0, 0, 0, 0, 0, -}; - -static void blizzard_window(BlizzardState *s) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - uint8_t *src, *dst; - int bypp[2]; - int bypl[3]; - int y; - blizzard_fn_t fn = s->data.line_fn; - - if (!fn) - return; - if (s->mx[0] > s->data.x) - s->mx[0] = s->data.x; - if (s->my[0] > s->data.y) - s->my[0] = s->data.y; - if (s->mx[1] < s->data.x + s->data.dx) - s->mx[1] = s->data.x + s->data.dx; - if (s->my[1] < s->data.y + s->data.dy) - s->my[1] = s->data.y + s->data.dy; - - bypp[0] = s->bpp; - bypp[1] = surface_bytes_per_pixel(surface); - bypl[0] = bypp[0] * s->data.pitch; - bypl[1] = bypp[1] * s->x; - bypl[2] = bypp[0] * s->data.dx; - - src = s->data.data; - dst = s->fb + bypl[1] * s->data.y + bypp[1] * s->data.x; - for (y = s->data.dy; y > 0; y --, src += bypl[0], dst += bypl[1]) - fn(dst, src, bypl[2]); -} - -static int blizzard_transfer_setup(BlizzardState *s) -{ - if (s->source > 3 || !s->bpp || - s->ix[1] < s->ix[0] || s->iy[1] < s->iy[0]) - return 0; - - s->data.angle = s->effect & 3; - s->data.line_fn = s->line_fn_tab[!!s->data.angle][s->iformat]; - s->data.x = s->ix[0]; - s->data.y = s->iy[0]; - s->data.dx = s->ix[1] - s->ix[0] + 1; - s->data.dy = s->iy[1] - s->iy[0] + 1; - s->data.len = s->bpp * s->data.dx * s->data.dy; - s->data.pitch = s->data.dx; - if (s->data.len > s->data.buflen) { - s->data.buf = g_realloc(s->data.buf, s->data.len); - s->data.buflen = s->data.len; - } - s->data.ptr = s->data.buf; - s->data.data = s->data.buf; - s->data.len /= 2; - return 1; -} - -static void blizzard_reset(BlizzardState *s) -{ - s->reg = 0; - s->swallow = 0; - - s->pll = 9; - s->pll_range = 1; - s->pll_ctrl = 0x14; - s->pll_mode = 0x32; - s->clksel = 0x00; - s->memenable = 0; - s->memrefresh = 0x25c; - s->timing[0] = 0x3f; - s->timing[1] = 0x13; - s->timing[2] = 0x21; - s->priority = 0; - - s->lcd_config = 0x74; - s->x = 8; - s->y = 1; - s->skipx = 0; - s->skipy = 0; - s->hndp = 3; - s->vndp = 2; - s->hsync = 1; - s->vsync = 1; - s->pclk = 0x80; - - s->ix[0] = 0; - s->ix[1] = 0; - s->iy[0] = 0; - s->iy[1] = 0; - s->ox[0] = 0; - s->ox[1] = 0; - s->oy[0] = 0; - s->oy[1] = 0; - - s->yrc[0] = 0x00; - s->yrc[1] = 0x30; - s->u = 0; - s->v = 0; - - s->iformat = 3; - s->source = 0; - s->bpp = blizzard_iformat_bpp[s->iformat]; - - s->hssi_config[0] = 0x00; - s->hssi_config[1] = 0x00; - s->hssi_config[2] = 0x01; - s->tv_config = 0x00; - s->tv_timing[0] = 0x00; - s->tv_timing[1] = 0x00; - s->tv_timing[2] = 0x00; - s->tv_timing[3] = 0x00; - s->vbi = 0x10; - s->tv_x = 0x14; - s->tv_y = 0x03; - s->tv_test = 0x00; - s->tv_filter_config = 0x80; - s->tv_filter_idx = 0x00; - s->border_r = 0x10; - s->border_g = 0x80; - s->border_b = 0x80; - s->gamma_config = 0x00; - s->gamma_idx = 0x00; - s->matrix_ena = 0x00; - memset(&s->matrix_coeff, 0, sizeof(s->matrix_coeff)); - s->matrix_r = 0x00; - s->matrix_g = 0x00; - s->matrix_b = 0x00; - s->pm = 0x02; - s->status = 0x00; - s->rgbgpio_dir = 0x00; - s->gpio_dir = 0x00; - s->gpio_edge[0] = 0x00; - s->gpio_edge[1] = 0x00; - s->gpio_irq = 0x00; - s->gpio_pdown = 0xff; -} - -static inline void blizzard_invalidate_display(void *opaque) { - BlizzardState *s = (BlizzardState *) opaque; - - s->invalidate = 1; -} - -static uint16_t blizzard_reg_read(void *opaque, uint8_t reg) -{ - BlizzardState *s = (BlizzardState *) opaque; - - switch (reg) { - case 0x00: /* Revision Code */ - return 0xa5; - - case 0x02: /* Configuration Readback */ - return 0x83; /* Macrovision OK, CNF[2:0] = 3 */ - - case 0x04: /* PLL M-Divider */ - return (s->pll - 1) | (1 << 7); - case 0x06: /* PLL Lock Range Control */ - return s->pll_range; - case 0x08: /* PLL Lock Synthesis Control 0 */ - return s->pll_ctrl & 0xff; - case 0x0a: /* PLL Lock Synthesis Control 1 */ - return s->pll_ctrl >> 8; - case 0x0c: /* PLL Mode Control 0 */ - return s->pll_mode; - - case 0x0e: /* Clock-Source Select */ - return s->clksel; - - case 0x10: /* Memory Controller Activate */ - case 0x14: /* Memory Controller Bank 0 Status Flag */ - return s->memenable; - - case 0x18: /* Auto-Refresh Interval Setting 0 */ - return s->memrefresh & 0xff; - case 0x1a: /* Auto-Refresh Interval Setting 1 */ - return s->memrefresh >> 8; - - case 0x1c: /* Power-On Sequence Timing Control */ - return s->timing[0]; - case 0x1e: /* Timing Control 0 */ - return s->timing[1]; - case 0x20: /* Timing Control 1 */ - return s->timing[2]; - - case 0x24: /* Arbitration Priority Control */ - return s->priority; - - case 0x28: /* LCD Panel Configuration */ - return s->lcd_config; - - case 0x2a: /* LCD Horizontal Display Width */ - return s->x >> 3; - case 0x2c: /* LCD Horizontal Non-display Period */ - return s->hndp; - case 0x2e: /* LCD Vertical Display Height 0 */ - return s->y & 0xff; - case 0x30: /* LCD Vertical Display Height 1 */ - return s->y >> 8; - case 0x32: /* LCD Vertical Non-display Period */ - return s->vndp; - case 0x34: /* LCD HS Pulse-width */ - return s->hsync; - case 0x36: /* LCd HS Pulse Start Position */ - return s->skipx >> 3; - case 0x38: /* LCD VS Pulse-width */ - return s->vsync; - case 0x3a: /* LCD VS Pulse Start Position */ - return s->skipy; - - case 0x3c: /* PCLK Polarity */ - return s->pclk; - - case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */ - return s->hssi_config[0]; - case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */ - return s->hssi_config[1]; - case 0x42: /* High-speed Serial Interface Tx Mode */ - return s->hssi_config[2]; - case 0x44: /* TV Display Configuration */ - return s->tv_config; - case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits */ - return s->tv_timing[(reg - 0x46) >> 1]; - case 0x4e: /* VBI: Closed Caption / XDS Control / Status */ - return s->vbi; - case 0x50: /* TV Horizontal Start Position */ - return s->tv_x; - case 0x52: /* TV Vertical Start Position */ - return s->tv_y; - case 0x54: /* TV Test Pattern Setting */ - return s->tv_test; - case 0x56: /* TV Filter Setting */ - return s->tv_filter_config; - case 0x58: /* TV Filter Coefficient Index */ - return s->tv_filter_idx; - case 0x5a: /* TV Filter Coefficient Data */ - if (s->tv_filter_idx < 0x20) - return s->tv_filter_coeff[s->tv_filter_idx ++]; - return 0; - - case 0x60: /* Input YUV/RGB Translate Mode 0 */ - return s->yrc[0]; - case 0x62: /* Input YUV/RGB Translate Mode 1 */ - return s->yrc[1]; - case 0x64: /* U Data Fix */ - return s->u; - case 0x66: /* V Data Fix */ - return s->v; - - case 0x68: /* Display Mode */ - return s->mode; - - case 0x6a: /* Special Effects */ - return s->effect; - - case 0x6c: /* Input Window X Start Position 0 */ - return s->ix[0] & 0xff; - case 0x6e: /* Input Window X Start Position 1 */ - return s->ix[0] >> 3; - case 0x70: /* Input Window Y Start Position 0 */ - return s->ix[0] & 0xff; - case 0x72: /* Input Window Y Start Position 1 */ - return s->ix[0] >> 3; - case 0x74: /* Input Window X End Position 0 */ - return s->ix[1] & 0xff; - case 0x76: /* Input Window X End Position 1 */ - return s->ix[1] >> 3; - case 0x78: /* Input Window Y End Position 0 */ - return s->ix[1] & 0xff; - case 0x7a: /* Input Window Y End Position 1 */ - return s->ix[1] >> 3; - case 0x7c: /* Output Window X Start Position 0 */ - return s->ox[0] & 0xff; - case 0x7e: /* Output Window X Start Position 1 */ - return s->ox[0] >> 3; - case 0x80: /* Output Window Y Start Position 0 */ - return s->oy[0] & 0xff; - case 0x82: /* Output Window Y Start Position 1 */ - return s->oy[0] >> 3; - case 0x84: /* Output Window X End Position 0 */ - return s->ox[1] & 0xff; - case 0x86: /* Output Window X End Position 1 */ - return s->ox[1] >> 3; - case 0x88: /* Output Window Y End Position 0 */ - return s->oy[1] & 0xff; - case 0x8a: /* Output Window Y End Position 1 */ - return s->oy[1] >> 3; - - case 0x8c: /* Input Data Format */ - return s->iformat; - case 0x8e: /* Data Source Select */ - return s->source; - case 0x90: /* Display Memory Data Port */ - return 0; - - case 0xa8: /* Border Color 0 */ - return s->border_r; - case 0xaa: /* Border Color 1 */ - return s->border_g; - case 0xac: /* Border Color 2 */ - return s->border_b; - - case 0xb4: /* Gamma Correction Enable */ - return s->gamma_config; - case 0xb6: /* Gamma Correction Table Index */ - return s->gamma_idx; - case 0xb8: /* Gamma Correction Table Data */ - return s->gamma_lut[s->gamma_idx ++]; - - case 0xba: /* 3x3 Matrix Enable */ - return s->matrix_ena; - case 0xbc ... 0xde: /* Coefficient Registers */ - return s->matrix_coeff[(reg - 0xbc) >> 1]; - case 0xe0: /* 3x3 Matrix Red Offset */ - return s->matrix_r; - case 0xe2: /* 3x3 Matrix Green Offset */ - return s->matrix_g; - case 0xe4: /* 3x3 Matrix Blue Offset */ - return s->matrix_b; - - case 0xe6: /* Power-save */ - return s->pm; - case 0xe8: /* Non-display Period Control / Status */ - return s->status | (1 << 5); - case 0xea: /* RGB Interface Control */ - return s->rgbgpio_dir; - case 0xec: /* RGB Interface Status */ - return s->rgbgpio; - case 0xee: /* General-purpose IO Pins Configuration */ - return s->gpio_dir; - case 0xf0: /* General-purpose IO Pins Status / Control */ - return s->gpio; - case 0xf2: /* GPIO Positive Edge Interrupt Trigger */ - return s->gpio_edge[0]; - case 0xf4: /* GPIO Negative Edge Interrupt Trigger */ - return s->gpio_edge[1]; - case 0xf6: /* GPIO Interrupt Status */ - return s->gpio_irq; - case 0xf8: /* GPIO Pull-down Control */ - return s->gpio_pdown; - - default: - fprintf(stderr, "%s: unknown register %02x\n", __FUNCTION__, reg); - return 0; - } -} - -static void blizzard_reg_write(void *opaque, uint8_t reg, uint16_t value) -{ - BlizzardState *s = (BlizzardState *) opaque; - - switch (reg) { - case 0x04: /* PLL M-Divider */ - s->pll = (value & 0x3f) + 1; - break; - case 0x06: /* PLL Lock Range Control */ - s->pll_range = value & 3; - break; - case 0x08: /* PLL Lock Synthesis Control 0 */ - s->pll_ctrl &= 0xf00; - s->pll_ctrl |= (value << 0) & 0x0ff; - break; - case 0x0a: /* PLL Lock Synthesis Control 1 */ - s->pll_ctrl &= 0x0ff; - s->pll_ctrl |= (value << 8) & 0xf00; - break; - case 0x0c: /* PLL Mode Control 0 */ - s->pll_mode = value & 0x77; - if ((value & 3) == 0 || (value & 3) == 3) - fprintf(stderr, "%s: wrong PLL Control bits (%i)\n", - __FUNCTION__, value & 3); - break; - - case 0x0e: /* Clock-Source Select */ - s->clksel = value & 0xff; - break; - - case 0x10: /* Memory Controller Activate */ - s->memenable = value & 1; - break; - case 0x14: /* Memory Controller Bank 0 Status Flag */ - break; - - case 0x18: /* Auto-Refresh Interval Setting 0 */ - s->memrefresh &= 0xf00; - s->memrefresh |= (value << 0) & 0x0ff; - break; - case 0x1a: /* Auto-Refresh Interval Setting 1 */ - s->memrefresh &= 0x0ff; - s->memrefresh |= (value << 8) & 0xf00; - break; - - case 0x1c: /* Power-On Sequence Timing Control */ - s->timing[0] = value & 0x7f; - break; - case 0x1e: /* Timing Control 0 */ - s->timing[1] = value & 0x17; - break; - case 0x20: /* Timing Control 1 */ - s->timing[2] = value & 0x35; - break; - - case 0x24: /* Arbitration Priority Control */ - s->priority = value & 1; - break; - - case 0x28: /* LCD Panel Configuration */ - s->lcd_config = value & 0xff; - if (value & (1 << 7)) - fprintf(stderr, "%s: data swap not supported!\n", __FUNCTION__); - break; - - case 0x2a: /* LCD Horizontal Display Width */ - s->x = value << 3; - break; - case 0x2c: /* LCD Horizontal Non-display Period */ - s->hndp = value & 0xff; - break; - case 0x2e: /* LCD Vertical Display Height 0 */ - s->y &= 0x300; - s->y |= (value << 0) & 0x0ff; - break; - case 0x30: /* LCD Vertical Display Height 1 */ - s->y &= 0x0ff; - s->y |= (value << 8) & 0x300; - break; - case 0x32: /* LCD Vertical Non-display Period */ - s->vndp = value & 0xff; - break; - case 0x34: /* LCD HS Pulse-width */ - s->hsync = value & 0xff; - break; - case 0x36: /* LCD HS Pulse Start Position */ - s->skipx = value & 0xff; - break; - case 0x38: /* LCD VS Pulse-width */ - s->vsync = value & 0xbf; - break; - case 0x3a: /* LCD VS Pulse Start Position */ - s->skipy = value & 0xff; - break; - - case 0x3c: /* PCLK Polarity */ - s->pclk = value & 0x82; - /* Affects calculation of s->hndp, s->hsync and s->skipx. */ - break; - - case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */ - s->hssi_config[0] = value; - break; - case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */ - s->hssi_config[1] = value; - if (((value >> 4) & 3) == 3) - fprintf(stderr, "%s: Illegal active-data-links value\n", - __FUNCTION__); - break; - case 0x42: /* High-speed Serial Interface Tx Mode */ - s->hssi_config[2] = value & 0xbd; - break; - - case 0x44: /* TV Display Configuration */ - s->tv_config = value & 0xfe; - break; - case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits 0 */ - s->tv_timing[(reg - 0x46) >> 1] = value; - break; - case 0x4e: /* VBI: Closed Caption / XDS Control / Status */ - s->vbi = value; - break; - case 0x50: /* TV Horizontal Start Position */ - s->tv_x = value; - break; - case 0x52: /* TV Vertical Start Position */ - s->tv_y = value & 0x7f; - break; - case 0x54: /* TV Test Pattern Setting */ - s->tv_test = value; - break; - case 0x56: /* TV Filter Setting */ - s->tv_filter_config = value & 0xbf; - break; - case 0x58: /* TV Filter Coefficient Index */ - s->tv_filter_idx = value & 0x1f; - break; - case 0x5a: /* TV Filter Coefficient Data */ - if (s->tv_filter_idx < 0x20) - s->tv_filter_coeff[s->tv_filter_idx ++] = value; - break; - - case 0x60: /* Input YUV/RGB Translate Mode 0 */ - s->yrc[0] = value & 0xb0; - break; - case 0x62: /* Input YUV/RGB Translate Mode 1 */ - s->yrc[1] = value & 0x30; - break; - case 0x64: /* U Data Fix */ - s->u = value & 0xff; - break; - case 0x66: /* V Data Fix */ - s->v = value & 0xff; - break; - - case 0x68: /* Display Mode */ - if ((s->mode ^ value) & 3) - s->invalidate = 1; - s->mode = value & 0xb7; - s->enable = value & 1; - s->blank = (value >> 1) & 1; - if (value & (1 << 4)) - fprintf(stderr, "%s: Macrovision enable attempt!\n", __FUNCTION__); - break; - - case 0x6a: /* Special Effects */ - s->effect = value & 0xfb; - break; - - case 0x6c: /* Input Window X Start Position 0 */ - s->ix[0] &= 0x300; - s->ix[0] |= (value << 0) & 0x0ff; - break; - case 0x6e: /* Input Window X Start Position 1 */ - s->ix[0] &= 0x0ff; - s->ix[0] |= (value << 8) & 0x300; - break; - case 0x70: /* Input Window Y Start Position 0 */ - s->iy[0] &= 0x300; - s->iy[0] |= (value << 0) & 0x0ff; - break; - case 0x72: /* Input Window Y Start Position 1 */ - s->iy[0] &= 0x0ff; - s->iy[0] |= (value << 8) & 0x300; - break; - case 0x74: /* Input Window X End Position 0 */ - s->ix[1] &= 0x300; - s->ix[1] |= (value << 0) & 0x0ff; - break; - case 0x76: /* Input Window X End Position 1 */ - s->ix[1] &= 0x0ff; - s->ix[1] |= (value << 8) & 0x300; - break; - case 0x78: /* Input Window Y End Position 0 */ - s->iy[1] &= 0x300; - s->iy[1] |= (value << 0) & 0x0ff; - break; - case 0x7a: /* Input Window Y End Position 1 */ - s->iy[1] &= 0x0ff; - s->iy[1] |= (value << 8) & 0x300; - break; - case 0x7c: /* Output Window X Start Position 0 */ - s->ox[0] &= 0x300; - s->ox[0] |= (value << 0) & 0x0ff; - break; - case 0x7e: /* Output Window X Start Position 1 */ - s->ox[0] &= 0x0ff; - s->ox[0] |= (value << 8) & 0x300; - break; - case 0x80: /* Output Window Y Start Position 0 */ - s->oy[0] &= 0x300; - s->oy[0] |= (value << 0) & 0x0ff; - break; - case 0x82: /* Output Window Y Start Position 1 */ - s->oy[0] &= 0x0ff; - s->oy[0] |= (value << 8) & 0x300; - break; - case 0x84: /* Output Window X End Position 0 */ - s->ox[1] &= 0x300; - s->ox[1] |= (value << 0) & 0x0ff; - break; - case 0x86: /* Output Window X End Position 1 */ - s->ox[1] &= 0x0ff; - s->ox[1] |= (value << 8) & 0x300; - break; - case 0x88: /* Output Window Y End Position 0 */ - s->oy[1] &= 0x300; - s->oy[1] |= (value << 0) & 0x0ff; - break; - case 0x8a: /* Output Window Y End Position 1 */ - s->oy[1] &= 0x0ff; - s->oy[1] |= (value << 8) & 0x300; - break; - - case 0x8c: /* Input Data Format */ - s->iformat = value & 0xf; - s->bpp = blizzard_iformat_bpp[s->iformat]; - if (!s->bpp) - fprintf(stderr, "%s: Illegal or unsupported input format %x\n", - __FUNCTION__, s->iformat); - break; - case 0x8e: /* Data Source Select */ - s->source = value & 7; - /* Currently all windows will be "destructive overlays". */ - if ((!(s->effect & (1 << 3)) && (s->ix[0] != s->ox[0] || - s->iy[0] != s->oy[0] || - s->ix[1] != s->ox[1] || - s->iy[1] != s->oy[1])) || - !((s->ix[1] - s->ix[0]) & (s->iy[1] - s->iy[0]) & - (s->ox[1] - s->ox[0]) & (s->oy[1] - s->oy[0]) & 1)) - fprintf(stderr, "%s: Illegal input/output window positions\n", - __FUNCTION__); - - blizzard_transfer_setup(s); - break; - - case 0x90: /* Display Memory Data Port */ - if (!s->data.len && !blizzard_transfer_setup(s)) - break; - - *s->data.ptr ++ = value; - if (-- s->data.len == 0) - blizzard_window(s); - break; - - case 0xa8: /* Border Color 0 */ - s->border_r = value; - break; - case 0xaa: /* Border Color 1 */ - s->border_g = value; - break; - case 0xac: /* Border Color 2 */ - s->border_b = value; - break; - - case 0xb4: /* Gamma Correction Enable */ - s->gamma_config = value & 0x87; - break; - case 0xb6: /* Gamma Correction Table Index */ - s->gamma_idx = value; - break; - case 0xb8: /* Gamma Correction Table Data */ - s->gamma_lut[s->gamma_idx ++] = value; - break; - - case 0xba: /* 3x3 Matrix Enable */ - s->matrix_ena = value & 1; - break; - case 0xbc ... 0xde: /* Coefficient Registers */ - s->matrix_coeff[(reg - 0xbc) >> 1] = value & ((reg & 2) ? 0x80 : 0xff); - break; - case 0xe0: /* 3x3 Matrix Red Offset */ - s->matrix_r = value; - break; - case 0xe2: /* 3x3 Matrix Green Offset */ - s->matrix_g = value; - break; - case 0xe4: /* 3x3 Matrix Blue Offset */ - s->matrix_b = value; - break; - - case 0xe6: /* Power-save */ - s->pm = value & 0x83; - if (value & s->mode & 1) - fprintf(stderr, "%s: The display must be disabled before entering " - "Standby Mode\n", __FUNCTION__); - break; - case 0xe8: /* Non-display Period Control / Status */ - s->status = value & 0x1b; - break; - case 0xea: /* RGB Interface Control */ - s->rgbgpio_dir = value & 0x8f; - break; - case 0xec: /* RGB Interface Status */ - s->rgbgpio = value & 0xcf; - break; - case 0xee: /* General-purpose IO Pins Configuration */ - s->gpio_dir = value; - break; - case 0xf0: /* General-purpose IO Pins Status / Control */ - s->gpio = value; - break; - case 0xf2: /* GPIO Positive Edge Interrupt Trigger */ - s->gpio_edge[0] = value; - break; - case 0xf4: /* GPIO Negative Edge Interrupt Trigger */ - s->gpio_edge[1] = value; - break; - case 0xf6: /* GPIO Interrupt Status */ - s->gpio_irq &= value; - break; - case 0xf8: /* GPIO Pull-down Control */ - s->gpio_pdown = value; - break; - - default: - fprintf(stderr, "%s: unknown register %02x\n", __FUNCTION__, reg); - break; - } -} - -uint16_t s1d13745_read(void *opaque, int dc) -{ - BlizzardState *s = (BlizzardState *) opaque; - uint16_t value = blizzard_reg_read(s, s->reg); - - if (s->swallow -- > 0) - return 0; - if (dc) - s->reg ++; - - return value; -} - -void s1d13745_write(void *opaque, int dc, uint16_t value) -{ - BlizzardState *s = (BlizzardState *) opaque; - - if (s->swallow -- > 0) - return; - if (dc) { - blizzard_reg_write(s, s->reg, value); - - if (s->reg != 0x90 && s->reg != 0x5a && s->reg != 0xb8) - s->reg += 2; - } else - s->reg = value & 0xff; -} - -void s1d13745_write_block(void *opaque, int dc, - void *buf, size_t len, int pitch) -{ - BlizzardState *s = (BlizzardState *) opaque; - - while (len > 0) { - if (s->reg == 0x90 && dc && - (s->data.len || blizzard_transfer_setup(s)) && - len >= (s->data.len << 1)) { - len -= s->data.len << 1; - s->data.len = 0; - s->data.data = buf; - if (pitch) - s->data.pitch = pitch; - blizzard_window(s); - s->data.data = s->data.buf; - continue; - } - - s1d13745_write(opaque, dc, *(uint16_t *) buf); - len -= 2; - buf += 2; - } -} - -static void blizzard_update_display(void *opaque) -{ - BlizzardState *s = (BlizzardState *) opaque; - DisplaySurface *surface = qemu_console_surface(s->con); - int y, bypp, bypl, bwidth; - uint8_t *src, *dst; - - if (!s->enable) - return; - - if (s->x != surface_width(surface) || s->y != surface_height(surface)) { - s->invalidate = 1; - qemu_console_resize(s->con, s->x, s->y); - surface = qemu_console_surface(s->con); - } - - if (s->invalidate) { - s->invalidate = 0; - - if (s->blank) { - bypp = surface_bytes_per_pixel(surface); - memset(surface_data(surface), 0, bypp * s->x * s->y); - return; - } - - s->mx[0] = 0; - s->mx[1] = s->x; - s->my[0] = 0; - s->my[1] = s->y; - } - - if (s->mx[1] <= s->mx[0]) - return; - - bypp = surface_bytes_per_pixel(surface); - bypl = bypp * s->x; - bwidth = bypp * (s->mx[1] - s->mx[0]); - y = s->my[0]; - src = s->fb + bypl * y + bypp * s->mx[0]; - dst = surface_data(surface) + bypl * y + bypp * s->mx[0]; - for (; y < s->my[1]; y ++, src += bypl, dst += bypl) - memcpy(dst, src, bwidth); - - dpy_gfx_update(s->con, s->mx[0], s->my[0], - s->mx[1] - s->mx[0], y - s->my[0]); - - s->mx[0] = s->x; - s->mx[1] = 0; - s->my[0] = s->y; - s->my[1] = 0; -} - -#define DEPTH 8 -#include "blizzard_template.h" -#define DEPTH 15 -#include "blizzard_template.h" -#define DEPTH 16 -#include "blizzard_template.h" -#define DEPTH 24 -#include "blizzard_template.h" -#define DEPTH 32 -#include "blizzard_template.h" - -static const GraphicHwOps blizzard_ops = { - .invalidate = blizzard_invalidate_display, - .gfx_update = blizzard_update_display, -}; - -void *s1d13745_init(qemu_irq gpio_int) -{ - BlizzardState *s = (BlizzardState *) g_malloc0(sizeof(*s)); - DisplaySurface *surface; - - s->fb = g_malloc(0x180000); - - s->con = graphic_console_init(NULL, 0, &blizzard_ops, s); - surface = qemu_console_surface(s->con); - - switch (surface_bits_per_pixel(surface)) { - case 0: - s->line_fn_tab[0] = s->line_fn_tab[1] = - g_malloc0(sizeof(blizzard_fn_t) * 0x10); - break; - case 8: - s->line_fn_tab[0] = blizzard_draw_fn_8; - s->line_fn_tab[1] = blizzard_draw_fn_r_8; - break; - case 15: - s->line_fn_tab[0] = blizzard_draw_fn_15; - s->line_fn_tab[1] = blizzard_draw_fn_r_15; - break; - case 16: - s->line_fn_tab[0] = blizzard_draw_fn_16; - s->line_fn_tab[1] = blizzard_draw_fn_r_16; - break; - case 24: - s->line_fn_tab[0] = blizzard_draw_fn_24; - s->line_fn_tab[1] = blizzard_draw_fn_r_24; - break; - case 32: - s->line_fn_tab[0] = blizzard_draw_fn_32; - s->line_fn_tab[1] = blizzard_draw_fn_r_32; - break; - default: - fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__); - exit(1); - } - - blizzard_reset(s); - - return s; -} diff --git a/qemu/hw/display/blizzard_template.h b/qemu/hw/display/blizzard_template.h deleted file mode 100644 index b7ef27c80..000000000 --- a/qemu/hw/display/blizzard_template.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * QEMU Epson S1D13744/S1D13745 templates - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#define SKIP_PIXEL(to) (to += deststep) -#if DEPTH == 8 -# define PIXEL_TYPE uint8_t -# define COPY_PIXEL(to, from) do { *to = from; SKIP_PIXEL(to); } while (0) -# define COPY_PIXEL1(to, from) (*to++ = from) -#elif DEPTH == 15 || DEPTH == 16 -# define PIXEL_TYPE uint16_t -# define COPY_PIXEL(to, from) do { *to = from; SKIP_PIXEL(to); } while (0) -# define COPY_PIXEL1(to, from) (*to++ = from) -#elif DEPTH == 24 -# define PIXEL_TYPE uint8_t -# define COPY_PIXEL(to, from) \ - do { \ - to[0] = from; \ - to[1] = (from) >> 8; \ - to[2] = (from) >> 16; \ - SKIP_PIXEL(to); \ - } while (0) - -# define COPY_PIXEL1(to, from) \ - do { \ - *to++ = from; \ - *to++ = (from) >> 8; \ - *to++ = (from) >> 16; \ - } while (0) -#elif DEPTH == 32 -# define PIXEL_TYPE uint32_t -# define COPY_PIXEL(to, from) do { *to = from; SKIP_PIXEL(to); } while (0) -# define COPY_PIXEL1(to, from) (*to++ = from) -#else -# error unknown bit depth -#endif - -#ifdef HOST_WORDS_BIGENDIAN -# define SWAP_WORDS 1 -#endif - -static void glue(blizzard_draw_line16_, DEPTH)(PIXEL_TYPE *dest, - const uint16_t *src, unsigned int width) -{ -#if !defined(SWAP_WORDS) && DEPTH == 16 - memcpy(dest, src, width); -#else - uint16_t data; - unsigned int r, g, b; - const uint16_t *end = (const void *) src + width; - while (src < end) { - data = *src ++; - b = (data & 0x1f) << 3; - data >>= 5; - g = (data & 0x3f) << 2; - data >>= 6; - r = (data & 0x1f) << 3; - data >>= 5; - COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b)); - } -#endif -} - -static void glue(blizzard_draw_line24mode1_, DEPTH)(PIXEL_TYPE *dest, - const uint8_t *src, unsigned int width) -{ - /* TODO: check if SDL 24-bit planes are not in the same format and - * if so, use memcpy */ - unsigned int r[2], g[2], b[2]; - const uint8_t *end = src + width; - while (src < end) { - g[0] = *src ++; - r[0] = *src ++; - r[1] = *src ++; - b[0] = *src ++; - COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[0], g[0], b[0])); - b[1] = *src ++; - g[1] = *src ++; - COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[1], g[1], b[1])); - } -} - -static void glue(blizzard_draw_line24mode2_, DEPTH)(PIXEL_TYPE *dest, - const uint8_t *src, unsigned int width) -{ - unsigned int r, g, b; - const uint8_t *end = src + width; - while (src < end) { - r = *src ++; - src ++; - b = *src ++; - g = *src ++; - COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b)); - } -} - -/* No rotation */ -static blizzard_fn_t glue(blizzard_draw_fn_, DEPTH)[0x10] = { - NULL, - /* RGB 5:6:5*/ - (blizzard_fn_t) glue(blizzard_draw_line16_, DEPTH), - /* RGB 6:6:6 mode 1 */ - (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH), - /* RGB 8:8:8 mode 1 */ - (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH), - NULL, NULL, - /* RGB 6:6:6 mode 2 */ - (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH), - /* RGB 8:8:8 mode 2 */ - (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH), - /* YUV 4:2:2 */ - NULL, - /* YUV 4:2:0 */ - NULL, - NULL, NULL, NULL, NULL, NULL, NULL, -}; - -/* 90deg, 180deg and 270deg rotation */ -static blizzard_fn_t glue(blizzard_draw_fn_r_, DEPTH)[0x10] = { - /* TODO */ - [0 ... 0xf] = NULL, -}; - -#undef DEPTH -#undef SKIP_PIXEL -#undef COPY_PIXEL -#undef COPY_PIXEL1 -#undef PIXEL_TYPE - -#undef SWAP_WORDS diff --git a/qemu/hw/display/cg3.c b/qemu/hw/display/cg3.c deleted file mode 100644 index fc0d97fa4..000000000 --- a/qemu/hw/display/cg3.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - * QEMU CG3 Frame buffer - * - * Copyright (c) 2012 Bob Breuer - * Copyright (c) 2013 Mark Cave-Ayland - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "qemu/error-report.h" -#include "ui/console.h" -#include "hw/sysbus.h" -#include "hw/loader.h" - -/* Change to 1 to enable debugging */ -#define DEBUG_CG3 0 - -#define CG3_ROM_FILE "QEMU,cgthree.bin" -#define FCODE_MAX_ROM_SIZE 0x10000 - -#define CG3_REG_SIZE 0x20 - -#define CG3_REG_BT458_ADDR 0x0 -#define CG3_REG_BT458_COLMAP 0x4 -#define CG3_REG_FBC_CTRL 0x10 -#define CG3_REG_FBC_STATUS 0x11 -#define CG3_REG_FBC_CURSTART 0x12 -#define CG3_REG_FBC_CUREND 0x13 -#define CG3_REG_FBC_VCTRL 0x14 - -/* Control register flags */ -#define CG3_CR_ENABLE_INTS 0x80 - -/* Status register flags */ -#define CG3_SR_PENDING_INT 0x80 -#define CG3_SR_1152_900_76_B 0x60 -#define CG3_SR_ID_COLOR 0x01 - -#define CG3_VRAM_SIZE 0x100000 -#define CG3_VRAM_OFFSET 0x800000 - -#define DPRINTF(fmt, ...) do { \ - if (DEBUG_CG3) { \ - printf("CG3: " fmt , ## __VA_ARGS__); \ - } \ -} while (0); - -#define TYPE_CG3 "cgthree" -#define CG3(obj) OBJECT_CHECK(CG3State, (obj), TYPE_CG3) - -typedef struct CG3State { - SysBusDevice parent_obj; - - QemuConsole *con; - qemu_irq irq; - hwaddr prom_addr; - MemoryRegion vram_mem; - MemoryRegion rom; - MemoryRegion reg; - uint32_t vram_size; - int full_update; - uint8_t regs[16]; - uint8_t r[256], g[256], b[256]; - uint16_t width, height, depth; - uint8_t dac_index, dac_state; -} CG3State; - -static void cg3_update_display(void *opaque) -{ - CG3State *s = opaque; - DisplaySurface *surface = qemu_console_surface(s->con); - const uint8_t *pix; - uint32_t *data; - uint32_t dval; - int x, y, y_start; - unsigned int width, height; - ram_addr_t page, page_min, page_max; - - if (surface_bits_per_pixel(surface) != 32) { - return; - } - width = s->width; - height = s->height; - - y_start = -1; - page_min = -1; - page_max = 0; - page = 0; - pix = memory_region_get_ram_ptr(&s->vram_mem); - data = (uint32_t *)surface_data(surface); - - memory_region_sync_dirty_bitmap(&s->vram_mem); - for (y = 0; y < height; y++) { - int update = s->full_update; - - page = (y * width) & TARGET_PAGE_MASK; - update |= memory_region_get_dirty(&s->vram_mem, page, page + width, - DIRTY_MEMORY_VGA); - if (update) { - if (y_start < 0) { - y_start = y; - } - if (page < page_min) { - page_min = page; - } - if (page > page_max) { - page_max = page; - } - - for (x = 0; x < width; x++) { - dval = *pix++; - dval = (s->r[dval] << 16) | (s->g[dval] << 8) | s->b[dval]; - *data++ = dval; - } - } else { - if (y_start >= 0) { - dpy_gfx_update(s->con, 0, y_start, s->width, y - y_start); - y_start = -1; - } - pix += width; - data += width; - } - } - s->full_update = 0; - if (y_start >= 0) { - dpy_gfx_update(s->con, 0, y_start, s->width, y - y_start); - } - if (page_max >= page_min) { - memory_region_reset_dirty(&s->vram_mem, - page_min, page_max - page_min + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); - } - /* vsync interrupt? */ - if (s->regs[0] & CG3_CR_ENABLE_INTS) { - s->regs[1] |= CG3_SR_PENDING_INT; - qemu_irq_raise(s->irq); - } -} - -static void cg3_invalidate_display(void *opaque) -{ - CG3State *s = opaque; - - memory_region_set_dirty(&s->vram_mem, 0, CG3_VRAM_SIZE); -} - -static uint64_t cg3_reg_read(void *opaque, hwaddr addr, unsigned size) -{ - CG3State *s = opaque; - int val; - - switch (addr) { - case CG3_REG_BT458_ADDR: - case CG3_REG_BT458_COLMAP: - val = 0; - break; - case CG3_REG_FBC_CTRL: - val = s->regs[0]; - break; - case CG3_REG_FBC_STATUS: - /* monitor ID 6, board type = 1 (color) */ - val = s->regs[1] | CG3_SR_1152_900_76_B | CG3_SR_ID_COLOR; - break; - case CG3_REG_FBC_CURSTART ... CG3_REG_SIZE - 1: - val = s->regs[addr - 0x10]; - break; - default: - qemu_log_mask(LOG_UNIMP, - "cg3: Unimplemented register read " - "reg 0x%" HWADDR_PRIx " size 0x%x\n", - addr, size); - val = 0; - break; - } - DPRINTF("read %02x from reg %" HWADDR_PRIx "\n", val, addr); - return val; -} - -static void cg3_reg_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - CG3State *s = opaque; - uint8_t regval; - int i; - - DPRINTF("write %" PRIx64 " to reg %" HWADDR_PRIx " size %d\n", - val, addr, size); - - switch (addr) { - case CG3_REG_BT458_ADDR: - s->dac_index = val; - s->dac_state = 0; - break; - case CG3_REG_BT458_COLMAP: - /* This register can be written to as either a long word or a byte */ - if (size == 1) { - val <<= 24; - } - - for (i = 0; i < size; i++) { - regval = val >> 24; - - switch (s->dac_state) { - case 0: - s->r[s->dac_index] = regval; - s->dac_state++; - break; - case 1: - s->g[s->dac_index] = regval; - s->dac_state++; - break; - case 2: - s->b[s->dac_index] = regval; - /* Index autoincrement */ - s->dac_index = (s->dac_index + 1) & 0xff; - default: - s->dac_state = 0; - break; - } - val <<= 8; - } - s->full_update = 1; - break; - case CG3_REG_FBC_CTRL: - s->regs[0] = val; - break; - case CG3_REG_FBC_STATUS: - if (s->regs[1] & CG3_SR_PENDING_INT) { - /* clear interrupt */ - s->regs[1] &= ~CG3_SR_PENDING_INT; - qemu_irq_lower(s->irq); - } - break; - case CG3_REG_FBC_CURSTART ... CG3_REG_SIZE - 1: - s->regs[addr - 0x10] = val; - break; - default: - qemu_log_mask(LOG_UNIMP, - "cg3: Unimplemented register write " - "reg 0x%" HWADDR_PRIx " size 0x%x value 0x%" PRIx64 "\n", - addr, size, val); - break; - } -} - -static const MemoryRegionOps cg3_reg_ops = { - .read = cg3_reg_read, - .write = cg3_reg_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 4, - }, -}; - -static const GraphicHwOps cg3_ops = { - .invalidate = cg3_invalidate_display, - .gfx_update = cg3_update_display, -}; - -static void cg3_initfn(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - CG3State *s = CG3(obj); - - 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, obj, &cg3_reg_ops, s, "cg3.reg", - CG3_REG_SIZE); - sysbus_init_mmio(sbd, &s->reg); -} - -static void cg3_realizefn(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - CG3State *s = CG3(dev); - int ret; - char *fcode_filename; - - /* FCode ROM */ - vmstate_register_ram_global(&s->rom); - fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, CG3_ROM_FILE); - if (fcode_filename) { - ret = load_image_targphys(fcode_filename, s->prom_addr, - FCODE_MAX_ROM_SIZE); - g_free(fcode_filename); - if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { - error_report("cg3: could not load prom '%s'", CG3_ROM_FILE); - } - } - - memory_region_init_ram(&s->vram_mem, NULL, "cg3.vram", s->vram_size, - &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); - - sysbus_init_irq(sbd, &s->irq); - - s->con = graphic_console_init(DEVICE(dev), 0, &cg3_ops, s); - qemu_console_resize(s->con, s->width, s->height); -} - -static int vmstate_cg3_post_load(void *opaque, int version_id) -{ - CG3State *s = opaque; - - cg3_invalidate_display(s); - - return 0; -} - -static const VMStateDescription vmstate_cg3 = { - .name = "cg3", - .version_id = 1, - .minimum_version_id = 1, - .post_load = vmstate_cg3_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT16(height, CG3State), - VMSTATE_UINT16(width, CG3State), - VMSTATE_UINT16(depth, CG3State), - VMSTATE_BUFFER(r, CG3State), - VMSTATE_BUFFER(g, CG3State), - VMSTATE_BUFFER(b, CG3State), - VMSTATE_UINT8(dac_index, CG3State), - VMSTATE_UINT8(dac_state, CG3State), - VMSTATE_END_OF_LIST() - } -}; - -static void cg3_reset(DeviceState *d) -{ - CG3State *s = CG3(d); - - /* Initialize palette */ - memset(s->r, 0, 256); - memset(s->g, 0, 256); - memset(s->b, 0, 256); - - s->dac_state = 0; - s->full_update = 1; - qemu_irq_lower(s->irq); -} - -static Property cg3_properties[] = { - DEFINE_PROP_UINT32("vram-size", CG3State, vram_size, -1), - DEFINE_PROP_UINT16("width", CG3State, width, -1), - DEFINE_PROP_UINT16("height", CG3State, height, -1), - DEFINE_PROP_UINT16("depth", CG3State, depth, -1), - DEFINE_PROP_UINT64("prom-addr", CG3State, prom_addr, -1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void cg3_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = cg3_realizefn; - dc->reset = cg3_reset; - dc->vmsd = &vmstate_cg3; - dc->props = cg3_properties; -} - -static const TypeInfo cg3_info = { - .name = TYPE_CG3, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(CG3State), - .instance_init = cg3_initfn, - .class_init = cg3_class_init, -}; - -static void cg3_register_types(void) -{ - type_register_static(&cg3_info); -} - -type_init(cg3_register_types) diff --git a/qemu/hw/display/cirrus_vga.c b/qemu/hw/display/cirrus_vga.c deleted file mode 100644 index 3d712d592..000000000 --- a/qemu/hw/display/cirrus_vga.c +++ /dev/null @@ -1,3091 +0,0 @@ -/* - * QEMU Cirrus CLGD 54xx VGA Emulator. - * - * Copyright (c) 2004 Fabrice Bellard - * Copyright (c) 2004 Makoto Suzuki (suzu) - * - * 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. - */ -/* - * 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" -#include "ui/pixel_ops.h" -#include "vga_int.h" -#include "hw/loader.h" - -/* - * TODO: - * - destination write mask support not complete (bits 5..7) - * - optimize linear mappings - * - optimize bitblt functions - */ - -//#define DEBUG_CIRRUS -//#define DEBUG_BITBLT - -/*************************************** - * - * definitions - * - ***************************************/ - -// ID -#define CIRRUS_ID_CLGD5422 (0x23<<2) -#define CIRRUS_ID_CLGD5426 (0x24<<2) -#define CIRRUS_ID_CLGD5424 (0x25<<2) -#define CIRRUS_ID_CLGD5428 (0x26<<2) -#define CIRRUS_ID_CLGD5430 (0x28<<2) -#define CIRRUS_ID_CLGD5434 (0x2A<<2) -#define CIRRUS_ID_CLGD5436 (0x2B<<2) -#define CIRRUS_ID_CLGD5446 (0x2E<<2) - -// sequencer 0x07 -#define CIRRUS_SR7_BPP_VGA 0x00 -#define CIRRUS_SR7_BPP_SVGA 0x01 -#define CIRRUS_SR7_BPP_MASK 0x0e -#define CIRRUS_SR7_BPP_8 0x00 -#define CIRRUS_SR7_BPP_16_DOUBLEVCLK 0x02 -#define CIRRUS_SR7_BPP_24 0x04 -#define CIRRUS_SR7_BPP_16 0x06 -#define CIRRUS_SR7_BPP_32 0x08 -#define CIRRUS_SR7_ISAADDR_MASK 0xe0 - -// sequencer 0x0f -#define CIRRUS_MEMSIZE_512k 0x08 -#define CIRRUS_MEMSIZE_1M 0x10 -#define CIRRUS_MEMSIZE_2M 0x18 -#define CIRRUS_MEMFLAGS_BANKSWITCH 0x80 // bank switching is enabled. - -// sequencer 0x12 -#define CIRRUS_CURSOR_SHOW 0x01 -#define CIRRUS_CURSOR_HIDDENPEL 0x02 -#define CIRRUS_CURSOR_LARGE 0x04 // 64x64 if set, 32x32 if clear - -// sequencer 0x17 -#define CIRRUS_BUSTYPE_VLBFAST 0x10 -#define CIRRUS_BUSTYPE_PCI 0x20 -#define CIRRUS_BUSTYPE_VLBSLOW 0x30 -#define CIRRUS_BUSTYPE_ISA 0x38 -#define CIRRUS_MMIO_ENABLE 0x04 -#define CIRRUS_MMIO_USE_PCIADDR 0x40 // 0xb8000 if cleared. -#define CIRRUS_MEMSIZEEXT_DOUBLE 0x80 - -// control 0x0b -#define CIRRUS_BANKING_DUAL 0x01 -#define CIRRUS_BANKING_GRANULARITY_16K 0x20 // set:16k, clear:4k - -// control 0x30 -#define CIRRUS_BLTMODE_BACKWARDS 0x01 -#define CIRRUS_BLTMODE_MEMSYSDEST 0x02 -#define CIRRUS_BLTMODE_MEMSYSSRC 0x04 -#define CIRRUS_BLTMODE_TRANSPARENTCOMP 0x08 -#define CIRRUS_BLTMODE_PATTERNCOPY 0x40 -#define CIRRUS_BLTMODE_COLOREXPAND 0x80 -#define CIRRUS_BLTMODE_PIXELWIDTHMASK 0x30 -#define CIRRUS_BLTMODE_PIXELWIDTH8 0x00 -#define CIRRUS_BLTMODE_PIXELWIDTH16 0x10 -#define CIRRUS_BLTMODE_PIXELWIDTH24 0x20 -#define CIRRUS_BLTMODE_PIXELWIDTH32 0x30 - -// control 0x31 -#define CIRRUS_BLT_BUSY 0x01 -#define CIRRUS_BLT_START 0x02 -#define CIRRUS_BLT_RESET 0x04 -#define CIRRUS_BLT_FIFOUSED 0x10 -#define CIRRUS_BLT_AUTOSTART 0x80 - -// control 0x32 -#define CIRRUS_ROP_0 0x00 -#define CIRRUS_ROP_SRC_AND_DST 0x05 -#define CIRRUS_ROP_NOP 0x06 -#define CIRRUS_ROP_SRC_AND_NOTDST 0x09 -#define CIRRUS_ROP_NOTDST 0x0b -#define CIRRUS_ROP_SRC 0x0d -#define CIRRUS_ROP_1 0x0e -#define CIRRUS_ROP_NOTSRC_AND_DST 0x50 -#define CIRRUS_ROP_SRC_XOR_DST 0x59 -#define CIRRUS_ROP_SRC_OR_DST 0x6d -#define CIRRUS_ROP_NOTSRC_OR_NOTDST 0x90 -#define CIRRUS_ROP_SRC_NOTXOR_DST 0x95 -#define CIRRUS_ROP_SRC_OR_NOTDST 0xad -#define CIRRUS_ROP_NOTSRC 0xd0 -#define CIRRUS_ROP_NOTSRC_OR_DST 0xd6 -#define CIRRUS_ROP_NOTSRC_AND_NOTDST 0xda - -#define CIRRUS_ROP_NOP_INDEX 2 -#define CIRRUS_ROP_SRC_INDEX 5 - -// control 0x33 -#define CIRRUS_BLTMODEEXT_SOLIDFILL 0x04 -#define CIRRUS_BLTMODEEXT_COLOREXPINV 0x02 -#define CIRRUS_BLTMODEEXT_DWORDGRANULARITY 0x01 - -// memory-mapped IO -#define CIRRUS_MMIO_BLTBGCOLOR 0x00 // dword -#define CIRRUS_MMIO_BLTFGCOLOR 0x04 // dword -#define CIRRUS_MMIO_BLTWIDTH 0x08 // word -#define CIRRUS_MMIO_BLTHEIGHT 0x0a // word -#define CIRRUS_MMIO_BLTDESTPITCH 0x0c // word -#define CIRRUS_MMIO_BLTSRCPITCH 0x0e // word -#define CIRRUS_MMIO_BLTDESTADDR 0x10 // dword -#define CIRRUS_MMIO_BLTSRCADDR 0x14 // dword -#define CIRRUS_MMIO_BLTWRITEMASK 0x17 // byte -#define CIRRUS_MMIO_BLTMODE 0x18 // byte -#define CIRRUS_MMIO_BLTROP 0x1a // byte -#define CIRRUS_MMIO_BLTMODEEXT 0x1b // byte -#define CIRRUS_MMIO_BLTTRANSPARENTCOLOR 0x1c // word? -#define CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK 0x20 // word? -#define CIRRUS_MMIO_LINEARDRAW_START_X 0x24 // word -#define CIRRUS_MMIO_LINEARDRAW_START_Y 0x26 // word -#define CIRRUS_MMIO_LINEARDRAW_END_X 0x28 // word -#define CIRRUS_MMIO_LINEARDRAW_END_Y 0x2a // word -#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_INC 0x2c // byte -#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ROLLOVER 0x2d // byte -#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_MASK 0x2e // byte -#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ACCUM 0x2f // byte -#define CIRRUS_MMIO_BRESENHAM_K1 0x30 // word -#define CIRRUS_MMIO_BRESENHAM_K3 0x32 // word -#define CIRRUS_MMIO_BRESENHAM_ERROR 0x34 // word -#define CIRRUS_MMIO_BRESENHAM_DELTA_MAJOR 0x36 // word -#define CIRRUS_MMIO_BRESENHAM_DIRECTION 0x38 // byte -#define CIRRUS_MMIO_LINEDRAW_MODE 0x39 // byte -#define CIRRUS_MMIO_BLTSTATUS 0x40 // byte - -#define CIRRUS_PNPMMIO_SIZE 0x1000 - -struct CirrusVGAState; -typedef void (*cirrus_bitblt_rop_t) (struct CirrusVGAState *s, - uint8_t * dst, const uint8_t * src, - int dstpitch, int srcpitch, - int bltwidth, int bltheight); -typedef void (*cirrus_fill_t)(struct CirrusVGAState *s, - uint8_t *dst, int dst_pitch, int width, int height); - -typedef struct CirrusVGAState { - VGACommonState vga; - - MemoryRegion cirrus_vga_io; - MemoryRegion cirrus_linear_io; - MemoryRegion cirrus_linear_bitblt_io; - MemoryRegion cirrus_mmio_io; - MemoryRegion pci_bar; - bool linear_vram; /* vga.vram mapped over cirrus_linear_io */ - MemoryRegion low_mem_container; /* container for 0xa0000-0xc0000 */ - MemoryRegion low_mem; /* always mapped, overridden by: */ - MemoryRegion cirrus_bank[2]; /* aliases at 0xa0000-0xb0000 */ - uint32_t cirrus_addr_mask; - uint32_t linear_mmio_mask; - uint8_t cirrus_shadow_gr0; - uint8_t cirrus_shadow_gr1; - uint8_t cirrus_hidden_dac_lockindex; - uint8_t cirrus_hidden_dac_data; - uint32_t cirrus_bank_base[2]; - uint32_t cirrus_bank_limit[2]; - uint8_t cirrus_hidden_palette[48]; - int cirrus_blt_pixelwidth; - int cirrus_blt_width; - int cirrus_blt_height; - int cirrus_blt_dstpitch; - int cirrus_blt_srcpitch; - uint32_t cirrus_blt_fgcol; - uint32_t cirrus_blt_bgcol; - uint32_t cirrus_blt_dstaddr; - uint32_t cirrus_blt_srcaddr; - uint8_t cirrus_blt_mode; - uint8_t cirrus_blt_modeext; - cirrus_bitblt_rop_t cirrus_rop; -#define CIRRUS_BLTBUFSIZE (2048 * 4) /* one line width */ - uint8_t cirrus_bltbuf[CIRRUS_BLTBUFSIZE]; - uint8_t *cirrus_srcptr; - uint8_t *cirrus_srcptr_end; - uint32_t cirrus_srccounter; - /* hwcursor display state */ - int last_hw_cursor_size; - int last_hw_cursor_x; - int last_hw_cursor_y; - int last_hw_cursor_y_start; - int last_hw_cursor_y_end; - int real_vram_size; /* XXX: suppress that */ - int device_id; - int bustype; -} CirrusVGAState; - -typedef struct PCICirrusVGAState { - PCIDevice dev; - CirrusVGAState cirrus_vga; -} PCICirrusVGAState; - -#define TYPE_PCI_CIRRUS_VGA "cirrus-vga" -#define PCI_CIRRUS_VGA(obj) \ - OBJECT_CHECK(PCICirrusVGAState, (obj), TYPE_PCI_CIRRUS_VGA) - -#define TYPE_ISA_CIRRUS_VGA "isa-cirrus-vga" -#define ISA_CIRRUS_VGA(obj) \ - OBJECT_CHECK(ISACirrusVGAState, (obj), TYPE_ISA_CIRRUS_VGA) - -typedef struct ISACirrusVGAState { - ISADevice parent_obj; - - CirrusVGAState cirrus_vga; -} ISACirrusVGAState; - -static uint8_t rop_to_index[256]; - -/*************************************** - * - * prototypes. - * - ***************************************/ - - -static void cirrus_bitblt_reset(CirrusVGAState *s); -static void cirrus_update_memory_access(CirrusVGAState *s); - -/*************************************** - * - * raster operations - * - ***************************************/ - -static bool blit_region_is_unsafe(struct CirrusVGAState *s, - int32_t pitch, int32_t addr) -{ - if (pitch < 0) { - int64_t min = addr - + ((int64_t)s->cirrus_blt_height-1) * pitch; - int32_t max = addr - + s->cirrus_blt_width; - 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) { - return true; - } - } - return false; -} - -static bool blit_is_unsafe(struct CirrusVGAState *s) -{ - /* should be the case, see cirrus_bitblt_start */ - assert(s->cirrus_blt_width > 0); - assert(s->cirrus_blt_height > 0); - - if (s->cirrus_blt_width > CIRRUS_BLTBUFSIZE) { - return true; - } - - if (blit_region_is_unsafe(s, s->cirrus_blt_dstpitch, - s->cirrus_blt_dstaddr & s->cirrus_addr_mask)) { - return true; - } - if (blit_region_is_unsafe(s, s->cirrus_blt_srcpitch, - s->cirrus_blt_srcaddr & s->cirrus_addr_mask)) { - return true; - } - - return false; -} - -static void cirrus_bitblt_rop_nop(CirrusVGAState *s, - uint8_t *dst,const uint8_t *src, - int dstpitch,int srcpitch, - int bltwidth,int bltheight) -{ -} - -static void cirrus_bitblt_fill_nop(CirrusVGAState *s, - uint8_t *dst, - int dstpitch, int bltwidth,int bltheight) -{ -} - -#define ROP_NAME 0 -#define ROP_FN(d, s) 0 -#include "cirrus_vga_rop.h" - -#define ROP_NAME src_and_dst -#define ROP_FN(d, s) (s) & (d) -#include "cirrus_vga_rop.h" - -#define ROP_NAME src_and_notdst -#define ROP_FN(d, s) (s) & (~(d)) -#include "cirrus_vga_rop.h" - -#define ROP_NAME notdst -#define ROP_FN(d, s) ~(d) -#include "cirrus_vga_rop.h" - -#define ROP_NAME src -#define ROP_FN(d, s) s -#include "cirrus_vga_rop.h" - -#define ROP_NAME 1 -#define ROP_FN(d, s) ~0 -#include "cirrus_vga_rop.h" - -#define ROP_NAME notsrc_and_dst -#define ROP_FN(d, s) (~(s)) & (d) -#include "cirrus_vga_rop.h" - -#define ROP_NAME src_xor_dst -#define ROP_FN(d, s) (s) ^ (d) -#include "cirrus_vga_rop.h" - -#define ROP_NAME src_or_dst -#define ROP_FN(d, s) (s) | (d) -#include "cirrus_vga_rop.h" - -#define ROP_NAME notsrc_or_notdst -#define ROP_FN(d, s) (~(s)) | (~(d)) -#include "cirrus_vga_rop.h" - -#define ROP_NAME src_notxor_dst -#define ROP_FN(d, s) ~((s) ^ (d)) -#include "cirrus_vga_rop.h" - -#define ROP_NAME src_or_notdst -#define ROP_FN(d, s) (s) | (~(d)) -#include "cirrus_vga_rop.h" - -#define ROP_NAME notsrc -#define ROP_FN(d, s) (~(s)) -#include "cirrus_vga_rop.h" - -#define ROP_NAME notsrc_or_dst -#define ROP_FN(d, s) (~(s)) | (d) -#include "cirrus_vga_rop.h" - -#define ROP_NAME notsrc_and_notdst -#define ROP_FN(d, s) (~(s)) & (~(d)) -#include "cirrus_vga_rop.h" - -static const cirrus_bitblt_rop_t cirrus_fwd_rop[16] = { - cirrus_bitblt_rop_fwd_0, - cirrus_bitblt_rop_fwd_src_and_dst, - cirrus_bitblt_rop_nop, - cirrus_bitblt_rop_fwd_src_and_notdst, - cirrus_bitblt_rop_fwd_notdst, - cirrus_bitblt_rop_fwd_src, - cirrus_bitblt_rop_fwd_1, - cirrus_bitblt_rop_fwd_notsrc_and_dst, - cirrus_bitblt_rop_fwd_src_xor_dst, - cirrus_bitblt_rop_fwd_src_or_dst, - cirrus_bitblt_rop_fwd_notsrc_or_notdst, - cirrus_bitblt_rop_fwd_src_notxor_dst, - cirrus_bitblt_rop_fwd_src_or_notdst, - cirrus_bitblt_rop_fwd_notsrc, - cirrus_bitblt_rop_fwd_notsrc_or_dst, - cirrus_bitblt_rop_fwd_notsrc_and_notdst, -}; - -static const cirrus_bitblt_rop_t cirrus_bkwd_rop[16] = { - cirrus_bitblt_rop_bkwd_0, - cirrus_bitblt_rop_bkwd_src_and_dst, - cirrus_bitblt_rop_nop, - cirrus_bitblt_rop_bkwd_src_and_notdst, - cirrus_bitblt_rop_bkwd_notdst, - cirrus_bitblt_rop_bkwd_src, - cirrus_bitblt_rop_bkwd_1, - cirrus_bitblt_rop_bkwd_notsrc_and_dst, - cirrus_bitblt_rop_bkwd_src_xor_dst, - cirrus_bitblt_rop_bkwd_src_or_dst, - cirrus_bitblt_rop_bkwd_notsrc_or_notdst, - cirrus_bitblt_rop_bkwd_src_notxor_dst, - cirrus_bitblt_rop_bkwd_src_or_notdst, - cirrus_bitblt_rop_bkwd_notsrc, - cirrus_bitblt_rop_bkwd_notsrc_or_dst, - cirrus_bitblt_rop_bkwd_notsrc_and_notdst, -}; - -#define TRANSP_ROP(name) {\ - name ## _8,\ - name ## _16,\ - } -#define TRANSP_NOP(func) {\ - func,\ - func,\ - } - -static const cirrus_bitblt_rop_t cirrus_fwd_transp_rop[16][2] = { - TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_0), - TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_and_dst), - TRANSP_NOP(cirrus_bitblt_rop_nop), - TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_and_notdst), - TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notdst), - TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src), - TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_1), - TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_and_dst), - TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_xor_dst), - TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_or_dst), - TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_or_notdst), - TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_notxor_dst), - TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_or_notdst), - TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc), - TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_or_dst), - TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_and_notdst), -}; - -static const cirrus_bitblt_rop_t cirrus_bkwd_transp_rop[16][2] = { - TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_0), - TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_and_dst), - TRANSP_NOP(cirrus_bitblt_rop_nop), - TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_and_notdst), - TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notdst), - TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src), - TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_1), - TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_and_dst), - TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_xor_dst), - TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_or_dst), - TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_or_notdst), - TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_notxor_dst), - TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_or_notdst), - TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc), - TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_or_dst), - TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_and_notdst), -}; - -#define ROP2(name) {\ - name ## _8,\ - name ## _16,\ - name ## _24,\ - name ## _32,\ - } - -#define ROP_NOP2(func) {\ - func,\ - func,\ - func,\ - func,\ - } - -static const cirrus_bitblt_rop_t cirrus_patternfill[16][4] = { - ROP2(cirrus_patternfill_0), - ROP2(cirrus_patternfill_src_and_dst), - ROP_NOP2(cirrus_bitblt_rop_nop), - ROP2(cirrus_patternfill_src_and_notdst), - ROP2(cirrus_patternfill_notdst), - ROP2(cirrus_patternfill_src), - ROP2(cirrus_patternfill_1), - ROP2(cirrus_patternfill_notsrc_and_dst), - ROP2(cirrus_patternfill_src_xor_dst), - ROP2(cirrus_patternfill_src_or_dst), - ROP2(cirrus_patternfill_notsrc_or_notdst), - ROP2(cirrus_patternfill_src_notxor_dst), - ROP2(cirrus_patternfill_src_or_notdst), - ROP2(cirrus_patternfill_notsrc), - ROP2(cirrus_patternfill_notsrc_or_dst), - ROP2(cirrus_patternfill_notsrc_and_notdst), -}; - -static const cirrus_bitblt_rop_t cirrus_colorexpand_transp[16][4] = { - ROP2(cirrus_colorexpand_transp_0), - ROP2(cirrus_colorexpand_transp_src_and_dst), - ROP_NOP2(cirrus_bitblt_rop_nop), - ROP2(cirrus_colorexpand_transp_src_and_notdst), - ROP2(cirrus_colorexpand_transp_notdst), - ROP2(cirrus_colorexpand_transp_src), - ROP2(cirrus_colorexpand_transp_1), - ROP2(cirrus_colorexpand_transp_notsrc_and_dst), - ROP2(cirrus_colorexpand_transp_src_xor_dst), - ROP2(cirrus_colorexpand_transp_src_or_dst), - ROP2(cirrus_colorexpand_transp_notsrc_or_notdst), - ROP2(cirrus_colorexpand_transp_src_notxor_dst), - ROP2(cirrus_colorexpand_transp_src_or_notdst), - ROP2(cirrus_colorexpand_transp_notsrc), - ROP2(cirrus_colorexpand_transp_notsrc_or_dst), - ROP2(cirrus_colorexpand_transp_notsrc_and_notdst), -}; - -static const cirrus_bitblt_rop_t cirrus_colorexpand[16][4] = { - ROP2(cirrus_colorexpand_0), - ROP2(cirrus_colorexpand_src_and_dst), - ROP_NOP2(cirrus_bitblt_rop_nop), - ROP2(cirrus_colorexpand_src_and_notdst), - ROP2(cirrus_colorexpand_notdst), - ROP2(cirrus_colorexpand_src), - ROP2(cirrus_colorexpand_1), - ROP2(cirrus_colorexpand_notsrc_and_dst), - ROP2(cirrus_colorexpand_src_xor_dst), - ROP2(cirrus_colorexpand_src_or_dst), - ROP2(cirrus_colorexpand_notsrc_or_notdst), - ROP2(cirrus_colorexpand_src_notxor_dst), - ROP2(cirrus_colorexpand_src_or_notdst), - ROP2(cirrus_colorexpand_notsrc), - ROP2(cirrus_colorexpand_notsrc_or_dst), - ROP2(cirrus_colorexpand_notsrc_and_notdst), -}; - -static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern_transp[16][4] = { - ROP2(cirrus_colorexpand_pattern_transp_0), - ROP2(cirrus_colorexpand_pattern_transp_src_and_dst), - ROP_NOP2(cirrus_bitblt_rop_nop), - ROP2(cirrus_colorexpand_pattern_transp_src_and_notdst), - ROP2(cirrus_colorexpand_pattern_transp_notdst), - ROP2(cirrus_colorexpand_pattern_transp_src), - ROP2(cirrus_colorexpand_pattern_transp_1), - ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_dst), - ROP2(cirrus_colorexpand_pattern_transp_src_xor_dst), - ROP2(cirrus_colorexpand_pattern_transp_src_or_dst), - ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_notdst), - ROP2(cirrus_colorexpand_pattern_transp_src_notxor_dst), - ROP2(cirrus_colorexpand_pattern_transp_src_or_notdst), - ROP2(cirrus_colorexpand_pattern_transp_notsrc), - ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_dst), - ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_notdst), -}; - -static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern[16][4] = { - ROP2(cirrus_colorexpand_pattern_0), - ROP2(cirrus_colorexpand_pattern_src_and_dst), - ROP_NOP2(cirrus_bitblt_rop_nop), - ROP2(cirrus_colorexpand_pattern_src_and_notdst), - ROP2(cirrus_colorexpand_pattern_notdst), - ROP2(cirrus_colorexpand_pattern_src), - ROP2(cirrus_colorexpand_pattern_1), - ROP2(cirrus_colorexpand_pattern_notsrc_and_dst), - ROP2(cirrus_colorexpand_pattern_src_xor_dst), - ROP2(cirrus_colorexpand_pattern_src_or_dst), - ROP2(cirrus_colorexpand_pattern_notsrc_or_notdst), - ROP2(cirrus_colorexpand_pattern_src_notxor_dst), - ROP2(cirrus_colorexpand_pattern_src_or_notdst), - ROP2(cirrus_colorexpand_pattern_notsrc), - ROP2(cirrus_colorexpand_pattern_notsrc_or_dst), - ROP2(cirrus_colorexpand_pattern_notsrc_and_notdst), -}; - -static const cirrus_fill_t cirrus_fill[16][4] = { - ROP2(cirrus_fill_0), - ROP2(cirrus_fill_src_and_dst), - ROP_NOP2(cirrus_bitblt_fill_nop), - ROP2(cirrus_fill_src_and_notdst), - ROP2(cirrus_fill_notdst), - ROP2(cirrus_fill_src), - ROP2(cirrus_fill_1), - ROP2(cirrus_fill_notsrc_and_dst), - ROP2(cirrus_fill_src_xor_dst), - ROP2(cirrus_fill_src_or_dst), - ROP2(cirrus_fill_notsrc_or_notdst), - ROP2(cirrus_fill_src_notxor_dst), - ROP2(cirrus_fill_src_or_notdst), - ROP2(cirrus_fill_notsrc), - ROP2(cirrus_fill_notsrc_or_dst), - ROP2(cirrus_fill_notsrc_and_notdst), -}; - -static inline void cirrus_bitblt_fgcol(CirrusVGAState *s) -{ - unsigned int color; - switch (s->cirrus_blt_pixelwidth) { - case 1: - s->cirrus_blt_fgcol = s->cirrus_shadow_gr1; - break; - case 2: - color = s->cirrus_shadow_gr1 | (s->vga.gr[0x11] << 8); - s->cirrus_blt_fgcol = le16_to_cpu(color); - break; - case 3: - s->cirrus_blt_fgcol = s->cirrus_shadow_gr1 | - (s->vga.gr[0x11] << 8) | (s->vga.gr[0x13] << 16); - break; - default: - case 4: - color = s->cirrus_shadow_gr1 | (s->vga.gr[0x11] << 8) | - (s->vga.gr[0x13] << 16) | (s->vga.gr[0x15] << 24); - s->cirrus_blt_fgcol = le32_to_cpu(color); - break; - } -} - -static inline void cirrus_bitblt_bgcol(CirrusVGAState *s) -{ - unsigned int color; - switch (s->cirrus_blt_pixelwidth) { - case 1: - s->cirrus_blt_bgcol = s->cirrus_shadow_gr0; - break; - case 2: - color = s->cirrus_shadow_gr0 | (s->vga.gr[0x10] << 8); - s->cirrus_blt_bgcol = le16_to_cpu(color); - break; - case 3: - s->cirrus_blt_bgcol = s->cirrus_shadow_gr0 | - (s->vga.gr[0x10] << 8) | (s->vga.gr[0x12] << 16); - break; - default: - case 4: - color = s->cirrus_shadow_gr0 | (s->vga.gr[0x10] << 8) | - (s->vga.gr[0x12] << 16) | (s->vga.gr[0x14] << 24); - s->cirrus_blt_bgcol = le32_to_cpu(color); - break; - } -} - -static void cirrus_invalidate_region(CirrusVGAState * s, int off_begin, - int off_pitch, int bytesperline, - int lines) -{ - int y; - int off_cur; - int off_cur_end; - - for (y = 0; y < lines; y++) { - off_cur = off_begin; - off_cur_end = (off_cur + bytesperline) & s->cirrus_addr_mask; - memory_region_set_dirty(&s->vga.vram, off_cur, off_cur_end - off_cur); - off_begin += off_pitch; - } -} - -static int cirrus_bitblt_common_patterncopy(CirrusVGAState * s, - const uint8_t * src) -{ - uint8_t *dst; - - dst = s->vga.vram_ptr + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask); - - if (blit_is_unsafe(s)) - return 0; - - (*s->cirrus_rop) (s, dst, src, - s->cirrus_blt_dstpitch, 0, - s->cirrus_blt_width, s->cirrus_blt_height); - cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, - s->cirrus_blt_dstpitch, s->cirrus_blt_width, - s->cirrus_blt_height); - return 1; -} - -/* fill */ - -static int cirrus_bitblt_solidfill(CirrusVGAState *s, int blt_rop) -{ - cirrus_fill_t rop_func; - - if (blit_is_unsafe(s)) { - return 0; - } - rop_func = cirrus_fill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; - rop_func(s, s->vga.vram_ptr + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask), - s->cirrus_blt_dstpitch, - s->cirrus_blt_width, s->cirrus_blt_height); - cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, - s->cirrus_blt_dstpitch, s->cirrus_blt_width, - s->cirrus_blt_height); - cirrus_bitblt_reset(s); - return 1; -} - -/*************************************** - * - * bitblt (video-to-video) - * - ***************************************/ - -static int cirrus_bitblt_videotovideo_patterncopy(CirrusVGAState * s) -{ - return cirrus_bitblt_common_patterncopy(s, - s->vga.vram_ptr + ((s->cirrus_blt_srcaddr & ~7) & - s->cirrus_addr_mask)); -} - -static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h) -{ - int sx = 0, sy = 0; - int dx = 0, dy = 0; - int depth = 0; - int notify = 0; - - /* make sure to only copy if it's a plain copy ROP */ - if (*s->cirrus_rop == cirrus_bitblt_rop_fwd_src || - *s->cirrus_rop == cirrus_bitblt_rop_bkwd_src) { - - int width, height; - - depth = s->vga.get_bpp(&s->vga) / 8; - s->vga.get_resolution(&s->vga, &width, &height); - - /* extra x, y */ - sx = (src % ABS(s->cirrus_blt_srcpitch)) / depth; - sy = (src / ABS(s->cirrus_blt_srcpitch)); - dx = (dst % ABS(s->cirrus_blt_dstpitch)) / depth; - dy = (dst / ABS(s->cirrus_blt_dstpitch)); - - /* normalize width */ - w /= depth; - - /* if we're doing a backward copy, we have to adjust - our x/y to be the upper left corner (instead of the lower - right corner) */ - if (s->cirrus_blt_dstpitch < 0) { - sx -= (s->cirrus_blt_width / depth) - 1; - dx -= (s->cirrus_blt_width / depth) - 1; - sy -= s->cirrus_blt_height - 1; - dy -= s->cirrus_blt_height - 1; - } - - /* are we in the visible portion of memory? */ - if (sx >= 0 && sy >= 0 && dx >= 0 && dy >= 0 && - (sx + w) <= width && (sy + h) <= height && - (dx + w) <= width && (dy + h) <= height) { - notify = 1; - } - } - - /* we have to flush all pending changes so that the copy - is generated at the appropriate moment in time */ - if (notify) - graphic_hw_update(s->vga.con); - - (*s->cirrus_rop) (s, s->vga.vram_ptr + - (s->cirrus_blt_dstaddr & s->cirrus_addr_mask), - s->vga.vram_ptr + - (s->cirrus_blt_srcaddr & s->cirrus_addr_mask), - s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch, - s->cirrus_blt_width, s->cirrus_blt_height); - - if (notify) { - qemu_console_copy(s->vga.con, - sx, sy, dx, dy, - s->cirrus_blt_width / depth, - s->cirrus_blt_height); - } - - /* we don't have to notify the display that this portion has - changed since qemu_console_copy implies this */ - - cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, - s->cirrus_blt_dstpitch, s->cirrus_blt_width, - s->cirrus_blt_height); -} - -static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s) -{ - if (blit_is_unsafe(s)) - return 0; - - cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->vga.start_addr, - s->cirrus_blt_srcaddr - s->vga.start_addr, - s->cirrus_blt_width, s->cirrus_blt_height); - - return 1; -} - -/*************************************** - * - * bitblt (cpu-to-video) - * - ***************************************/ - -static void cirrus_bitblt_cputovideo_next(CirrusVGAState * s) -{ - int copy_count; - uint8_t *end_ptr; - - if (s->cirrus_srccounter > 0) { - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) { - cirrus_bitblt_common_patterncopy(s, s->cirrus_bltbuf); - the_end: - s->cirrus_srccounter = 0; - cirrus_bitblt_reset(s); - } else { - /* at least one scan line */ - do { - (*s->cirrus_rop)(s, s->vga.vram_ptr + - (s->cirrus_blt_dstaddr & s->cirrus_addr_mask), - s->cirrus_bltbuf, 0, 0, s->cirrus_blt_width, 1); - cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, 0, - s->cirrus_blt_width, 1); - s->cirrus_blt_dstaddr += s->cirrus_blt_dstpitch; - s->cirrus_srccounter -= s->cirrus_blt_srcpitch; - if (s->cirrus_srccounter <= 0) - goto the_end; - /* more bytes than needed can be transferred because of - word alignment, so we keep them for the next line */ - /* XXX: keep alignment to speed up transfer */ - end_ptr = s->cirrus_bltbuf + s->cirrus_blt_srcpitch; - copy_count = s->cirrus_srcptr_end - end_ptr; - memmove(s->cirrus_bltbuf, end_ptr, copy_count); - s->cirrus_srcptr = s->cirrus_bltbuf + copy_count; - s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch; - } while (s->cirrus_srcptr >= s->cirrus_srcptr_end); - } - } -} - -/*************************************** - * - * bitblt wrapper - * - ***************************************/ - -static void cirrus_bitblt_reset(CirrusVGAState * s) -{ - int need_update; - - s->vga.gr[0x31] &= - ~(CIRRUS_BLT_START | CIRRUS_BLT_BUSY | CIRRUS_BLT_FIFOUSED); - need_update = s->cirrus_srcptr != &s->cirrus_bltbuf[0] - || s->cirrus_srcptr_end != &s->cirrus_bltbuf[0]; - s->cirrus_srcptr = &s->cirrus_bltbuf[0]; - s->cirrus_srcptr_end = &s->cirrus_bltbuf[0]; - s->cirrus_srccounter = 0; - if (!need_update) - return; - cirrus_update_memory_access(s); -} - -static int cirrus_bitblt_cputovideo(CirrusVGAState * s) -{ - int w; - - s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_MEMSYSSRC; - s->cirrus_srcptr = &s->cirrus_bltbuf[0]; - s->cirrus_srcptr_end = &s->cirrus_bltbuf[0]; - - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) { - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) { - s->cirrus_blt_srcpitch = 8; - } else { - /* XXX: check for 24 bpp */ - s->cirrus_blt_srcpitch = 8 * 8 * s->cirrus_blt_pixelwidth; - } - s->cirrus_srccounter = s->cirrus_blt_srcpitch; - } else { - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) { - w = s->cirrus_blt_width / s->cirrus_blt_pixelwidth; - if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_DWORDGRANULARITY) - s->cirrus_blt_srcpitch = ((w + 31) >> 5); - else - s->cirrus_blt_srcpitch = ((w + 7) >> 3); - } else { - /* always align input size to 32 bits */ - s->cirrus_blt_srcpitch = (s->cirrus_blt_width + 3) & ~3; - } - s->cirrus_srccounter = s->cirrus_blt_srcpitch * s->cirrus_blt_height; - } - s->cirrus_srcptr = s->cirrus_bltbuf; - s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch; - cirrus_update_memory_access(s); - return 1; -} - -static int cirrus_bitblt_videotocpu(CirrusVGAState * s) -{ - /* XXX */ -#ifdef DEBUG_BITBLT - printf("cirrus: bitblt (video to cpu) is not implemented yet\n"); -#endif - return 0; -} - -static int cirrus_bitblt_videotovideo(CirrusVGAState * s) -{ - int ret; - - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) { - ret = cirrus_bitblt_videotovideo_patterncopy(s); - } else { - ret = cirrus_bitblt_videotovideo_copy(s); - } - if (ret) - cirrus_bitblt_reset(s); - return ret; -} - -static void cirrus_bitblt_start(CirrusVGAState * s) -{ - uint8_t blt_rop; - - s->vga.gr[0x31] |= CIRRUS_BLT_BUSY; - - s->cirrus_blt_width = (s->vga.gr[0x20] | (s->vga.gr[0x21] << 8)) + 1; - s->cirrus_blt_height = (s->vga.gr[0x22] | (s->vga.gr[0x23] << 8)) + 1; - s->cirrus_blt_dstpitch = (s->vga.gr[0x24] | (s->vga.gr[0x25] << 8)); - s->cirrus_blt_srcpitch = (s->vga.gr[0x26] | (s->vga.gr[0x27] << 8)); - s->cirrus_blt_dstaddr = - (s->vga.gr[0x28] | (s->vga.gr[0x29] << 8) | (s->vga.gr[0x2a] << 16)); - s->cirrus_blt_srcaddr = - (s->vga.gr[0x2c] | (s->vga.gr[0x2d] << 8) | (s->vga.gr[0x2e] << 16)); - s->cirrus_blt_mode = s->vga.gr[0x30]; - s->cirrus_blt_modeext = s->vga.gr[0x33]; - blt_rop = s->vga.gr[0x32]; - -#ifdef DEBUG_BITBLT - printf("rop=0x%02x mode=0x%02x modeext=0x%02x w=%d h=%d dpitch=%d spitch=%d daddr=0x%08x saddr=0x%08x writemask=0x%02x\n", - blt_rop, - s->cirrus_blt_mode, - s->cirrus_blt_modeext, - s->cirrus_blt_width, - s->cirrus_blt_height, - s->cirrus_blt_dstpitch, - s->cirrus_blt_srcpitch, - s->cirrus_blt_dstaddr, - s->cirrus_blt_srcaddr, - s->vga.gr[0x2f]); -#endif - - switch (s->cirrus_blt_mode & CIRRUS_BLTMODE_PIXELWIDTHMASK) { - case CIRRUS_BLTMODE_PIXELWIDTH8: - s->cirrus_blt_pixelwidth = 1; - break; - case CIRRUS_BLTMODE_PIXELWIDTH16: - s->cirrus_blt_pixelwidth = 2; - break; - case CIRRUS_BLTMODE_PIXELWIDTH24: - s->cirrus_blt_pixelwidth = 3; - break; - case CIRRUS_BLTMODE_PIXELWIDTH32: - s->cirrus_blt_pixelwidth = 4; - break; - default: -#ifdef DEBUG_BITBLT - printf("cirrus: bitblt - pixel width is unknown\n"); -#endif - goto bitblt_ignore; - } - s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_PIXELWIDTHMASK; - - if ((s-> - cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSSRC | - CIRRUS_BLTMODE_MEMSYSDEST)) - == (CIRRUS_BLTMODE_MEMSYSSRC | CIRRUS_BLTMODE_MEMSYSDEST)) { -#ifdef DEBUG_BITBLT - printf("cirrus: bitblt - memory-to-memory copy is requested\n"); -#endif - goto bitblt_ignore; - } - - if ((s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_SOLIDFILL) && - (s->cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSDEST | - CIRRUS_BLTMODE_TRANSPARENTCOMP | - CIRRUS_BLTMODE_PATTERNCOPY | - CIRRUS_BLTMODE_COLOREXPAND)) == - (CIRRUS_BLTMODE_PATTERNCOPY | CIRRUS_BLTMODE_COLOREXPAND)) { - cirrus_bitblt_fgcol(s); - cirrus_bitblt_solidfill(s, blt_rop); - } else { - if ((s->cirrus_blt_mode & (CIRRUS_BLTMODE_COLOREXPAND | - CIRRUS_BLTMODE_PATTERNCOPY)) == - CIRRUS_BLTMODE_COLOREXPAND) { - - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) { - if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) - cirrus_bitblt_bgcol(s); - else - cirrus_bitblt_fgcol(s); - s->cirrus_rop = cirrus_colorexpand_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; - } else { - cirrus_bitblt_fgcol(s); - cirrus_bitblt_bgcol(s); - s->cirrus_rop = cirrus_colorexpand[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; - } - } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) { - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) { - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) { - if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) - cirrus_bitblt_bgcol(s); - else - cirrus_bitblt_fgcol(s); - s->cirrus_rop = cirrus_colorexpand_pattern_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; - } else { - cirrus_bitblt_fgcol(s); - cirrus_bitblt_bgcol(s); - s->cirrus_rop = cirrus_colorexpand_pattern[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; - } - } else { - s->cirrus_rop = cirrus_patternfill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; - } - } else { - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) { - if (s->cirrus_blt_pixelwidth > 2) { - printf("src transparent without colorexpand must be 8bpp or 16bpp\n"); - goto bitblt_ignore; - } - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) { - s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch; - s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch; - s->cirrus_rop = cirrus_bkwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; - } else { - s->cirrus_rop = cirrus_fwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; - } - } else { - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) { - s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch; - s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch; - s->cirrus_rop = cirrus_bkwd_rop[rop_to_index[blt_rop]]; - } else { - s->cirrus_rop = cirrus_fwd_rop[rop_to_index[blt_rop]]; - } - } - } - // setup bitblt engine. - if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSSRC) { - if (!cirrus_bitblt_cputovideo(s)) - goto bitblt_ignore; - } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSDEST) { - if (!cirrus_bitblt_videotocpu(s)) - goto bitblt_ignore; - } else { - if (!cirrus_bitblt_videotovideo(s)) - goto bitblt_ignore; - } - } - return; - bitblt_ignore:; - cirrus_bitblt_reset(s); -} - -static void cirrus_write_bitblt(CirrusVGAState * s, unsigned reg_value) -{ - unsigned old_value; - - old_value = s->vga.gr[0x31]; - s->vga.gr[0x31] = reg_value; - - if (((old_value & CIRRUS_BLT_RESET) != 0) && - ((reg_value & CIRRUS_BLT_RESET) == 0)) { - cirrus_bitblt_reset(s); - } else if (((old_value & CIRRUS_BLT_START) == 0) && - ((reg_value & CIRRUS_BLT_START) != 0)) { - cirrus_bitblt_start(s); - } -} - - -/*************************************** - * - * basic parameters - * - ***************************************/ - -static void cirrus_get_offsets(VGACommonState *s1, - uint32_t *pline_offset, - uint32_t *pstart_addr, - uint32_t *pline_compare) -{ - CirrusVGAState * s = container_of(s1, CirrusVGAState, vga); - uint32_t start_addr, line_offset, line_compare; - - line_offset = s->vga.cr[0x13] - | ((s->vga.cr[0x1b] & 0x10) << 4); - line_offset <<= 3; - *pline_offset = line_offset; - - start_addr = (s->vga.cr[0x0c] << 8) - | s->vga.cr[0x0d] - | ((s->vga.cr[0x1b] & 0x01) << 16) - | ((s->vga.cr[0x1b] & 0x0c) << 15) - | ((s->vga.cr[0x1d] & 0x80) << 12); - *pstart_addr = start_addr; - - line_compare = s->vga.cr[0x18] | - ((s->vga.cr[0x07] & 0x10) << 4) | - ((s->vga.cr[0x09] & 0x40) << 3); - *pline_compare = line_compare; -} - -static uint32_t cirrus_get_bpp16_depth(CirrusVGAState * s) -{ - uint32_t ret = 16; - - switch (s->cirrus_hidden_dac_data & 0xf) { - case 0: - ret = 15; - break; /* Sierra HiColor */ - case 1: - ret = 16; - break; /* XGA HiColor */ - default: -#ifdef DEBUG_CIRRUS - printf("cirrus: invalid DAC value %x in 16bpp\n", - (s->cirrus_hidden_dac_data & 0xf)); -#endif - ret = 15; /* XXX */ - break; - } - return ret; -} - -static int cirrus_get_bpp(VGACommonState *s1) -{ - CirrusVGAState * s = container_of(s1, CirrusVGAState, vga); - uint32_t ret = 8; - - if ((s->vga.sr[0x07] & 0x01) != 0) { - /* Cirrus SVGA */ - switch (s->vga.sr[0x07] & CIRRUS_SR7_BPP_MASK) { - case CIRRUS_SR7_BPP_8: - ret = 8; - break; - case CIRRUS_SR7_BPP_16_DOUBLEVCLK: - ret = cirrus_get_bpp16_depth(s); - break; - case CIRRUS_SR7_BPP_24: - ret = 24; - break; - case CIRRUS_SR7_BPP_16: - ret = cirrus_get_bpp16_depth(s); - break; - case CIRRUS_SR7_BPP_32: - ret = 32; - break; - default: -#ifdef DEBUG_CIRRUS - printf("cirrus: unknown bpp - sr7=%x\n", s->vga.sr[0x7]); -#endif - ret = 8; - break; - } - } else { - /* VGA */ - ret = 0; - } - - return ret; -} - -static void cirrus_get_resolution(VGACommonState *s, int *pwidth, int *pheight) -{ - int width, height; - - width = (s->cr[0x01] + 1) * 8; - height = s->cr[0x12] | - ((s->cr[0x07] & 0x02) << 7) | - ((s->cr[0x07] & 0x40) << 3); - height = (height + 1); - /* interlace support */ - if (s->cr[0x1a] & 0x01) - height = height * 2; - *pwidth = width; - *pheight = height; -} - -/*************************************** - * - * bank memory - * - ***************************************/ - -static void cirrus_update_bank_ptr(CirrusVGAState * s, unsigned bank_index) -{ - unsigned offset; - unsigned limit; - - if ((s->vga.gr[0x0b] & 0x01) != 0) /* dual bank */ - offset = s->vga.gr[0x09 + bank_index]; - else /* single bank */ - offset = s->vga.gr[0x09]; - - if ((s->vga.gr[0x0b] & 0x20) != 0) - offset <<= 14; - else - offset <<= 12; - - if (s->real_vram_size <= offset) - limit = 0; - else - limit = s->real_vram_size - offset; - - if (((s->vga.gr[0x0b] & 0x01) == 0) && (bank_index != 0)) { - if (limit > 0x8000) { - offset += 0x8000; - limit -= 0x8000; - } else { - limit = 0; - } - } - - if (limit > 0) { - s->cirrus_bank_base[bank_index] = offset; - s->cirrus_bank_limit[bank_index] = limit; - } else { - s->cirrus_bank_base[bank_index] = 0; - s->cirrus_bank_limit[bank_index] = 0; - } -} - -/*************************************** - * - * I/O access between 0x3c4-0x3c5 - * - ***************************************/ - -static int cirrus_vga_read_sr(CirrusVGAState * s) -{ - switch (s->vga.sr_index) { - case 0x00: // Standard VGA - case 0x01: // Standard VGA - case 0x02: // Standard VGA - case 0x03: // Standard VGA - case 0x04: // Standard VGA - return s->vga.sr[s->vga.sr_index]; - case 0x06: // Unlock Cirrus extensions - return s->vga.sr[s->vga.sr_index]; - case 0x10: - case 0x30: - case 0x50: - case 0x70: // Graphics Cursor X - case 0x90: - case 0xb0: - case 0xd0: - case 0xf0: // Graphics Cursor X - return s->vga.sr[0x10]; - case 0x11: - case 0x31: - case 0x51: - case 0x71: // Graphics Cursor Y - case 0x91: - case 0xb1: - case 0xd1: - case 0xf1: // Graphics Cursor Y - return s->vga.sr[0x11]; - case 0x05: // ??? - case 0x07: // Extended Sequencer Mode - case 0x08: // EEPROM Control - case 0x09: // Scratch Register 0 - case 0x0a: // Scratch Register 1 - case 0x0b: // VCLK 0 - case 0x0c: // VCLK 1 - case 0x0d: // VCLK 2 - case 0x0e: // VCLK 3 - case 0x0f: // DRAM Control - case 0x12: // Graphics Cursor Attribute - case 0x13: // Graphics Cursor Pattern Address - case 0x14: // Scratch Register 2 - case 0x15: // Scratch Register 3 - case 0x16: // Performance Tuning Register - case 0x17: // Configuration Readback and Extended Control - case 0x18: // Signature Generator Control - case 0x19: // Signal Generator Result - case 0x1a: // Signal Generator Result - case 0x1b: // VCLK 0 Denominator & Post - case 0x1c: // VCLK 1 Denominator & Post - case 0x1d: // VCLK 2 Denominator & Post - case 0x1e: // VCLK 3 Denominator & Post - case 0x1f: // BIOS Write Enable and MCLK select -#ifdef DEBUG_CIRRUS - printf("cirrus: handled inport sr_index %02x\n", s->vga.sr_index); -#endif - return s->vga.sr[s->vga.sr_index]; - default: -#ifdef DEBUG_CIRRUS - printf("cirrus: inport sr_index %02x\n", s->vga.sr_index); -#endif - return 0xff; - break; - } -} - -static void cirrus_vga_write_sr(CirrusVGAState * s, uint32_t val) -{ - switch (s->vga.sr_index) { - case 0x00: // Standard VGA - case 0x01: // Standard VGA - case 0x02: // Standard VGA - case 0x03: // Standard VGA - case 0x04: // Standard VGA - s->vga.sr[s->vga.sr_index] = val & sr_mask[s->vga.sr_index]; - if (s->vga.sr_index == 1) - s->vga.update_retrace_info(&s->vga); - break; - case 0x06: // Unlock Cirrus extensions - val &= 0x17; - if (val == 0x12) { - s->vga.sr[s->vga.sr_index] = 0x12; - } else { - s->vga.sr[s->vga.sr_index] = 0x0f; - } - break; - case 0x10: - case 0x30: - case 0x50: - case 0x70: // Graphics Cursor X - case 0x90: - case 0xb0: - case 0xd0: - case 0xf0: // Graphics Cursor X - s->vga.sr[0x10] = val; - s->vga.hw_cursor_x = (val << 3) | (s->vga.sr_index >> 5); - break; - case 0x11: - case 0x31: - case 0x51: - case 0x71: // Graphics Cursor Y - case 0x91: - case 0xb1: - case 0xd1: - case 0xf1: // Graphics Cursor Y - s->vga.sr[0x11] = val; - s->vga.hw_cursor_y = (val << 3) | (s->vga.sr_index >> 5); - break; - case 0x07: // Extended Sequencer Mode - cirrus_update_memory_access(s); - case 0x08: // EEPROM Control - case 0x09: // Scratch Register 0 - case 0x0a: // Scratch Register 1 - case 0x0b: // VCLK 0 - case 0x0c: // VCLK 1 - case 0x0d: // VCLK 2 - case 0x0e: // VCLK 3 - case 0x0f: // DRAM Control - case 0x13: // Graphics Cursor Pattern Address - case 0x14: // Scratch Register 2 - case 0x15: // Scratch Register 3 - case 0x16: // Performance Tuning Register - case 0x18: // Signature Generator Control - case 0x19: // Signature Generator Result - case 0x1a: // Signature Generator Result - case 0x1b: // VCLK 0 Denominator & Post - case 0x1c: // VCLK 1 Denominator & Post - case 0x1d: // VCLK 2 Denominator & Post - case 0x1e: // VCLK 3 Denominator & Post - case 0x1f: // BIOS Write Enable and MCLK select - s->vga.sr[s->vga.sr_index] = val; -#ifdef DEBUG_CIRRUS - printf("cirrus: handled outport sr_index %02x, sr_value %02x\n", - s->vga.sr_index, val); -#endif - break; - case 0x12: // Graphics Cursor Attribute - s->vga.sr[0x12] = val; - s->vga.force_shadow = !!(val & CIRRUS_CURSOR_SHOW); -#ifdef DEBUG_CIRRUS - printf("cirrus: cursor ctl SR12=%02x (force shadow: %d)\n", - val, s->vga.force_shadow); -#endif - break; - case 0x17: // Configuration Readback and Extended Control - s->vga.sr[s->vga.sr_index] = (s->vga.sr[s->vga.sr_index] & 0x38) - | (val & 0xc7); - cirrus_update_memory_access(s); - break; - default: -#ifdef DEBUG_CIRRUS - printf("cirrus: outport sr_index %02x, sr_value %02x\n", - s->vga.sr_index, val); -#endif - break; - } -} - -/*************************************** - * - * I/O access at 0x3c6 - * - ***************************************/ - -static int cirrus_read_hidden_dac(CirrusVGAState * s) -{ - if (++s->cirrus_hidden_dac_lockindex == 5) { - s->cirrus_hidden_dac_lockindex = 0; - return s->cirrus_hidden_dac_data; - } - return 0xff; -} - -static void cirrus_write_hidden_dac(CirrusVGAState * s, int reg_value) -{ - if (s->cirrus_hidden_dac_lockindex == 4) { - s->cirrus_hidden_dac_data = reg_value; -#if defined(DEBUG_CIRRUS) - printf("cirrus: outport hidden DAC, value %02x\n", reg_value); -#endif - } - s->cirrus_hidden_dac_lockindex = 0; -} - -/*************************************** - * - * I/O access at 0x3c9 - * - ***************************************/ - -static int cirrus_vga_read_palette(CirrusVGAState * s) -{ - int val; - - if ((s->vga.sr[0x12] & CIRRUS_CURSOR_HIDDENPEL)) { - val = s->cirrus_hidden_palette[(s->vga.dac_read_index & 0x0f) * 3 + - s->vga.dac_sub_index]; - } else { - val = s->vga.palette[s->vga.dac_read_index * 3 + s->vga.dac_sub_index]; - } - if (++s->vga.dac_sub_index == 3) { - s->vga.dac_sub_index = 0; - s->vga.dac_read_index++; - } - return val; -} - -static void cirrus_vga_write_palette(CirrusVGAState * s, int reg_value) -{ - s->vga.dac_cache[s->vga.dac_sub_index] = reg_value; - if (++s->vga.dac_sub_index == 3) { - if ((s->vga.sr[0x12] & CIRRUS_CURSOR_HIDDENPEL)) { - memcpy(&s->cirrus_hidden_palette[(s->vga.dac_write_index & 0x0f) * 3], - s->vga.dac_cache, 3); - } else { - memcpy(&s->vga.palette[s->vga.dac_write_index * 3], s->vga.dac_cache, 3); - } - /* XXX update cursor */ - s->vga.dac_sub_index = 0; - s->vga.dac_write_index++; - } -} - -/*************************************** - * - * I/O access between 0x3ce-0x3cf - * - ***************************************/ - -static int cirrus_vga_read_gr(CirrusVGAState * s, unsigned reg_index) -{ - switch (reg_index) { - case 0x00: // Standard VGA, BGCOLOR 0x000000ff - return s->cirrus_shadow_gr0; - case 0x01: // Standard VGA, FGCOLOR 0x000000ff - return s->cirrus_shadow_gr1; - case 0x02: // Standard VGA - case 0x03: // Standard VGA - case 0x04: // Standard VGA - case 0x06: // Standard VGA - case 0x07: // Standard VGA - case 0x08: // Standard VGA - return s->vga.gr[s->vga.gr_index]; - case 0x05: // Standard VGA, Cirrus extended mode - default: - break; - } - - if (reg_index < 0x3a) { - return s->vga.gr[reg_index]; - } else { -#ifdef DEBUG_CIRRUS - printf("cirrus: inport gr_index %02x\n", reg_index); -#endif - return 0xff; - } -} - -static void -cirrus_vga_write_gr(CirrusVGAState * s, unsigned reg_index, int reg_value) -{ -#if defined(DEBUG_BITBLT) && 0 - printf("gr%02x: %02x\n", reg_index, reg_value); -#endif - switch (reg_index) { - case 0x00: // Standard VGA, BGCOLOR 0x000000ff - s->vga.gr[reg_index] = reg_value & gr_mask[reg_index]; - s->cirrus_shadow_gr0 = reg_value; - break; - case 0x01: // Standard VGA, FGCOLOR 0x000000ff - s->vga.gr[reg_index] = reg_value & gr_mask[reg_index]; - s->cirrus_shadow_gr1 = reg_value; - break; - case 0x02: // Standard VGA - case 0x03: // Standard VGA - case 0x04: // Standard VGA - case 0x06: // Standard VGA - case 0x07: // Standard VGA - case 0x08: // Standard VGA - s->vga.gr[reg_index] = reg_value & gr_mask[reg_index]; - break; - case 0x05: // Standard VGA, Cirrus extended mode - s->vga.gr[reg_index] = reg_value & 0x7f; - cirrus_update_memory_access(s); - break; - case 0x09: // bank offset #0 - case 0x0A: // bank offset #1 - s->vga.gr[reg_index] = reg_value; - cirrus_update_bank_ptr(s, 0); - cirrus_update_bank_ptr(s, 1); - cirrus_update_memory_access(s); - break; - case 0x0B: - s->vga.gr[reg_index] = reg_value; - cirrus_update_bank_ptr(s, 0); - cirrus_update_bank_ptr(s, 1); - cirrus_update_memory_access(s); - break; - case 0x10: // BGCOLOR 0x0000ff00 - case 0x11: // FGCOLOR 0x0000ff00 - case 0x12: // BGCOLOR 0x00ff0000 - case 0x13: // FGCOLOR 0x00ff0000 - case 0x14: // BGCOLOR 0xff000000 - case 0x15: // FGCOLOR 0xff000000 - case 0x20: // BLT WIDTH 0x0000ff - case 0x22: // BLT HEIGHT 0x0000ff - case 0x24: // BLT DEST PITCH 0x0000ff - case 0x26: // BLT SRC PITCH 0x0000ff - case 0x28: // BLT DEST ADDR 0x0000ff - case 0x29: // BLT DEST ADDR 0x00ff00 - case 0x2c: // BLT SRC ADDR 0x0000ff - case 0x2d: // BLT SRC ADDR 0x00ff00 - case 0x2f: // BLT WRITEMASK - case 0x30: // BLT MODE - case 0x32: // RASTER OP - case 0x33: // BLT MODEEXT - case 0x34: // BLT TRANSPARENT COLOR 0x00ff - case 0x35: // BLT TRANSPARENT COLOR 0xff00 - case 0x38: // BLT TRANSPARENT COLOR MASK 0x00ff - case 0x39: // BLT TRANSPARENT COLOR MASK 0xff00 - s->vga.gr[reg_index] = reg_value; - break; - case 0x21: // BLT WIDTH 0x001f00 - case 0x23: // BLT HEIGHT 0x001f00 - case 0x25: // BLT DEST PITCH 0x001f00 - case 0x27: // BLT SRC PITCH 0x001f00 - s->vga.gr[reg_index] = reg_value & 0x1f; - break; - case 0x2a: // BLT DEST ADDR 0x3f0000 - s->vga.gr[reg_index] = reg_value & 0x3f; - /* if auto start mode, starts bit blt now */ - if (s->vga.gr[0x31] & CIRRUS_BLT_AUTOSTART) { - cirrus_bitblt_start(s); - } - break; - case 0x2e: // BLT SRC ADDR 0x3f0000 - s->vga.gr[reg_index] = reg_value & 0x3f; - break; - case 0x31: // BLT STATUS/START - cirrus_write_bitblt(s, reg_value); - break; - default: -#ifdef DEBUG_CIRRUS - printf("cirrus: outport gr_index %02x, gr_value %02x\n", reg_index, - reg_value); -#endif - break; - } -} - -/*************************************** - * - * I/O access between 0x3d4-0x3d5 - * - ***************************************/ - -static int cirrus_vga_read_cr(CirrusVGAState * s, unsigned reg_index) -{ - switch (reg_index) { - case 0x00: // Standard VGA - case 0x01: // Standard VGA - case 0x02: // Standard VGA - case 0x03: // Standard VGA - case 0x04: // Standard VGA - case 0x05: // Standard VGA - case 0x06: // Standard VGA - case 0x07: // Standard VGA - case 0x08: // Standard VGA - case 0x09: // Standard VGA - case 0x0a: // Standard VGA - case 0x0b: // Standard VGA - case 0x0c: // Standard VGA - case 0x0d: // Standard VGA - case 0x0e: // Standard VGA - case 0x0f: // Standard VGA - case 0x10: // Standard VGA - case 0x11: // Standard VGA - case 0x12: // Standard VGA - case 0x13: // Standard VGA - case 0x14: // Standard VGA - case 0x15: // Standard VGA - case 0x16: // Standard VGA - case 0x17: // Standard VGA - case 0x18: // Standard VGA - return s->vga.cr[s->vga.cr_index]; - case 0x24: // Attribute Controller Toggle Readback (R) - return (s->vga.ar_flip_flop << 7); - case 0x19: // Interlace End - case 0x1a: // Miscellaneous Control - case 0x1b: // Extended Display Control - case 0x1c: // Sync Adjust and Genlock - case 0x1d: // Overlay Extended Control - case 0x22: // Graphics Data Latches Readback (R) - case 0x25: // Part Status - case 0x27: // Part ID (R) - return s->vga.cr[s->vga.cr_index]; - case 0x26: // Attribute Controller Index Readback (R) - return s->vga.ar_index & 0x3f; - break; - default: -#ifdef DEBUG_CIRRUS - printf("cirrus: inport cr_index %02x\n", reg_index); -#endif - return 0xff; - } -} - -static void cirrus_vga_write_cr(CirrusVGAState * s, int reg_value) -{ - switch (s->vga.cr_index) { - case 0x00: // Standard VGA - case 0x01: // Standard VGA - case 0x02: // Standard VGA - case 0x03: // Standard VGA - case 0x04: // Standard VGA - case 0x05: // Standard VGA - case 0x06: // Standard VGA - case 0x07: // Standard VGA - case 0x08: // Standard VGA - case 0x09: // Standard VGA - case 0x0a: // Standard VGA - case 0x0b: // Standard VGA - case 0x0c: // Standard VGA - case 0x0d: // Standard VGA - case 0x0e: // Standard VGA - case 0x0f: // Standard VGA - case 0x10: // Standard VGA - case 0x11: // Standard VGA - case 0x12: // Standard VGA - case 0x13: // Standard VGA - case 0x14: // Standard VGA - case 0x15: // Standard VGA - case 0x16: // Standard VGA - case 0x17: // Standard VGA - case 0x18: // Standard VGA - /* handle CR0-7 protection */ - if ((s->vga.cr[0x11] & 0x80) && s->vga.cr_index <= 7) { - /* can always write bit 4 of CR7 */ - if (s->vga.cr_index == 7) - s->vga.cr[7] = (s->vga.cr[7] & ~0x10) | (reg_value & 0x10); - return; - } - s->vga.cr[s->vga.cr_index] = reg_value; - switch(s->vga.cr_index) { - case 0x00: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x11: - case 0x17: - s->vga.update_retrace_info(&s->vga); - break; - } - break; - case 0x19: // Interlace End - case 0x1a: // Miscellaneous Control - case 0x1b: // Extended Display Control - case 0x1c: // Sync Adjust and Genlock - case 0x1d: // Overlay Extended Control - s->vga.cr[s->vga.cr_index] = reg_value; -#ifdef DEBUG_CIRRUS - printf("cirrus: handled outport cr_index %02x, cr_value %02x\n", - s->vga.cr_index, reg_value); -#endif - break; - case 0x22: // Graphics Data Latches Readback (R) - case 0x24: // Attribute Controller Toggle Readback (R) - case 0x26: // Attribute Controller Index Readback (R) - case 0x27: // Part ID (R) - break; - case 0x25: // Part Status - default: -#ifdef DEBUG_CIRRUS - printf("cirrus: outport cr_index %02x, cr_value %02x\n", - s->vga.cr_index, reg_value); -#endif - break; - } -} - -/*************************************** - * - * memory-mapped I/O (bitblt) - * - ***************************************/ - -static uint8_t cirrus_mmio_blt_read(CirrusVGAState * s, unsigned address) -{ - int value = 0xff; - - switch (address) { - case (CIRRUS_MMIO_BLTBGCOLOR + 0): - value = cirrus_vga_read_gr(s, 0x00); - break; - case (CIRRUS_MMIO_BLTBGCOLOR + 1): - value = cirrus_vga_read_gr(s, 0x10); - break; - case (CIRRUS_MMIO_BLTBGCOLOR + 2): - value = cirrus_vga_read_gr(s, 0x12); - break; - case (CIRRUS_MMIO_BLTBGCOLOR + 3): - value = cirrus_vga_read_gr(s, 0x14); - break; - case (CIRRUS_MMIO_BLTFGCOLOR + 0): - value = cirrus_vga_read_gr(s, 0x01); - break; - case (CIRRUS_MMIO_BLTFGCOLOR + 1): - value = cirrus_vga_read_gr(s, 0x11); - break; - case (CIRRUS_MMIO_BLTFGCOLOR + 2): - value = cirrus_vga_read_gr(s, 0x13); - break; - case (CIRRUS_MMIO_BLTFGCOLOR + 3): - value = cirrus_vga_read_gr(s, 0x15); - break; - case (CIRRUS_MMIO_BLTWIDTH + 0): - value = cirrus_vga_read_gr(s, 0x20); - break; - case (CIRRUS_MMIO_BLTWIDTH + 1): - value = cirrus_vga_read_gr(s, 0x21); - break; - case (CIRRUS_MMIO_BLTHEIGHT + 0): - value = cirrus_vga_read_gr(s, 0x22); - break; - case (CIRRUS_MMIO_BLTHEIGHT + 1): - value = cirrus_vga_read_gr(s, 0x23); - break; - case (CIRRUS_MMIO_BLTDESTPITCH + 0): - value = cirrus_vga_read_gr(s, 0x24); - break; - case (CIRRUS_MMIO_BLTDESTPITCH + 1): - value = cirrus_vga_read_gr(s, 0x25); - break; - case (CIRRUS_MMIO_BLTSRCPITCH + 0): - value = cirrus_vga_read_gr(s, 0x26); - break; - case (CIRRUS_MMIO_BLTSRCPITCH + 1): - value = cirrus_vga_read_gr(s, 0x27); - break; - case (CIRRUS_MMIO_BLTDESTADDR + 0): - value = cirrus_vga_read_gr(s, 0x28); - break; - case (CIRRUS_MMIO_BLTDESTADDR + 1): - value = cirrus_vga_read_gr(s, 0x29); - break; - case (CIRRUS_MMIO_BLTDESTADDR + 2): - value = cirrus_vga_read_gr(s, 0x2a); - break; - case (CIRRUS_MMIO_BLTSRCADDR + 0): - value = cirrus_vga_read_gr(s, 0x2c); - break; - case (CIRRUS_MMIO_BLTSRCADDR + 1): - value = cirrus_vga_read_gr(s, 0x2d); - break; - case (CIRRUS_MMIO_BLTSRCADDR + 2): - value = cirrus_vga_read_gr(s, 0x2e); - break; - case CIRRUS_MMIO_BLTWRITEMASK: - value = cirrus_vga_read_gr(s, 0x2f); - break; - case CIRRUS_MMIO_BLTMODE: - value = cirrus_vga_read_gr(s, 0x30); - break; - case CIRRUS_MMIO_BLTROP: - value = cirrus_vga_read_gr(s, 0x32); - break; - case CIRRUS_MMIO_BLTMODEEXT: - value = cirrus_vga_read_gr(s, 0x33); - break; - case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0): - value = cirrus_vga_read_gr(s, 0x34); - break; - case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1): - value = cirrus_vga_read_gr(s, 0x35); - break; - case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0): - value = cirrus_vga_read_gr(s, 0x38); - break; - case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1): - value = cirrus_vga_read_gr(s, 0x39); - break; - case CIRRUS_MMIO_BLTSTATUS: - value = cirrus_vga_read_gr(s, 0x31); - break; - default: -#ifdef DEBUG_CIRRUS - printf("cirrus: mmio read - address 0x%04x\n", address); -#endif - break; - } - - return (uint8_t) value; -} - -static void cirrus_mmio_blt_write(CirrusVGAState * s, unsigned address, - uint8_t value) -{ - switch (address) { - case (CIRRUS_MMIO_BLTBGCOLOR + 0): - cirrus_vga_write_gr(s, 0x00, value); - break; - case (CIRRUS_MMIO_BLTBGCOLOR + 1): - cirrus_vga_write_gr(s, 0x10, value); - break; - case (CIRRUS_MMIO_BLTBGCOLOR + 2): - cirrus_vga_write_gr(s, 0x12, value); - break; - case (CIRRUS_MMIO_BLTBGCOLOR + 3): - cirrus_vga_write_gr(s, 0x14, value); - break; - case (CIRRUS_MMIO_BLTFGCOLOR + 0): - cirrus_vga_write_gr(s, 0x01, value); - break; - case (CIRRUS_MMIO_BLTFGCOLOR + 1): - cirrus_vga_write_gr(s, 0x11, value); - break; - case (CIRRUS_MMIO_BLTFGCOLOR + 2): - cirrus_vga_write_gr(s, 0x13, value); - break; - case (CIRRUS_MMIO_BLTFGCOLOR + 3): - cirrus_vga_write_gr(s, 0x15, value); - break; - case (CIRRUS_MMIO_BLTWIDTH + 0): - cirrus_vga_write_gr(s, 0x20, value); - break; - case (CIRRUS_MMIO_BLTWIDTH + 1): - cirrus_vga_write_gr(s, 0x21, value); - break; - case (CIRRUS_MMIO_BLTHEIGHT + 0): - cirrus_vga_write_gr(s, 0x22, value); - break; - case (CIRRUS_MMIO_BLTHEIGHT + 1): - cirrus_vga_write_gr(s, 0x23, value); - break; - case (CIRRUS_MMIO_BLTDESTPITCH + 0): - cirrus_vga_write_gr(s, 0x24, value); - break; - case (CIRRUS_MMIO_BLTDESTPITCH + 1): - cirrus_vga_write_gr(s, 0x25, value); - break; - case (CIRRUS_MMIO_BLTSRCPITCH + 0): - cirrus_vga_write_gr(s, 0x26, value); - break; - case (CIRRUS_MMIO_BLTSRCPITCH + 1): - cirrus_vga_write_gr(s, 0x27, value); - break; - case (CIRRUS_MMIO_BLTDESTADDR + 0): - cirrus_vga_write_gr(s, 0x28, value); - break; - case (CIRRUS_MMIO_BLTDESTADDR + 1): - cirrus_vga_write_gr(s, 0x29, value); - break; - case (CIRRUS_MMIO_BLTDESTADDR + 2): - cirrus_vga_write_gr(s, 0x2a, value); - break; - case (CIRRUS_MMIO_BLTDESTADDR + 3): - /* ignored */ - break; - case (CIRRUS_MMIO_BLTSRCADDR + 0): - cirrus_vga_write_gr(s, 0x2c, value); - break; - case (CIRRUS_MMIO_BLTSRCADDR + 1): - cirrus_vga_write_gr(s, 0x2d, value); - break; - case (CIRRUS_MMIO_BLTSRCADDR + 2): - cirrus_vga_write_gr(s, 0x2e, value); - break; - case CIRRUS_MMIO_BLTWRITEMASK: - cirrus_vga_write_gr(s, 0x2f, value); - break; - case CIRRUS_MMIO_BLTMODE: - cirrus_vga_write_gr(s, 0x30, value); - break; - case CIRRUS_MMIO_BLTROP: - cirrus_vga_write_gr(s, 0x32, value); - break; - case CIRRUS_MMIO_BLTMODEEXT: - cirrus_vga_write_gr(s, 0x33, value); - break; - case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0): - cirrus_vga_write_gr(s, 0x34, value); - break; - case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1): - cirrus_vga_write_gr(s, 0x35, value); - break; - case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0): - cirrus_vga_write_gr(s, 0x38, value); - break; - case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1): - cirrus_vga_write_gr(s, 0x39, value); - break; - case CIRRUS_MMIO_BLTSTATUS: - cirrus_vga_write_gr(s, 0x31, value); - break; - default: -#ifdef DEBUG_CIRRUS - printf("cirrus: mmio write - addr 0x%04x val 0x%02x (ignored)\n", - address, value); -#endif - break; - } -} - -/*************************************** - * - * write mode 4/5 - * - ***************************************/ - -static void cirrus_mem_writeb_mode4and5_8bpp(CirrusVGAState * s, - unsigned mode, - unsigned offset, - uint32_t mem_value) -{ - int x; - unsigned val = mem_value; - uint8_t *dst; - - dst = s->vga.vram_ptr + (offset &= s->cirrus_addr_mask); - for (x = 0; x < 8; x++) { - if (val & 0x80) { - *dst = s->cirrus_shadow_gr1; - } else if (mode == 5) { - *dst = s->cirrus_shadow_gr0; - } - val <<= 1; - dst++; - } - memory_region_set_dirty(&s->vga.vram, offset, 8); -} - -static void cirrus_mem_writeb_mode4and5_16bpp(CirrusVGAState * s, - unsigned mode, - unsigned offset, - uint32_t mem_value) -{ - int x; - unsigned val = mem_value; - uint8_t *dst; - - dst = s->vga.vram_ptr + (offset &= s->cirrus_addr_mask); - for (x = 0; x < 8; x++) { - if (val & 0x80) { - *dst = s->cirrus_shadow_gr1; - *(dst + 1) = s->vga.gr[0x11]; - } else if (mode == 5) { - *dst = s->cirrus_shadow_gr0; - *(dst + 1) = s->vga.gr[0x10]; - } - val <<= 1; - dst += 2; - } - memory_region_set_dirty(&s->vga.vram, offset, 16); -} - -/*************************************** - * - * memory access between 0xa0000-0xbffff - * - ***************************************/ - -static uint64_t cirrus_vga_mem_read(void *opaque, - hwaddr addr, - uint32_t size) -{ - CirrusVGAState *s = opaque; - unsigned bank_index; - unsigned bank_offset; - uint32_t val; - - if ((s->vga.sr[0x07] & 0x01) == 0) { - return vga_mem_readb(&s->vga, addr); - } - - if (addr < 0x10000) { - /* XXX handle bitblt */ - /* video memory */ - bank_index = addr >> 15; - bank_offset = addr & 0x7fff; - if (bank_offset < s->cirrus_bank_limit[bank_index]) { - bank_offset += s->cirrus_bank_base[bank_index]; - if ((s->vga.gr[0x0B] & 0x14) == 0x14) { - bank_offset <<= 4; - } else if (s->vga.gr[0x0B] & 0x02) { - bank_offset <<= 3; - } - bank_offset &= s->cirrus_addr_mask; - val = *(s->vga.vram_ptr + bank_offset); - } else - val = 0xff; - } else if (addr >= 0x18000 && addr < 0x18100) { - /* memory-mapped I/O */ - val = 0xff; - if ((s->vga.sr[0x17] & 0x44) == 0x04) { - val = cirrus_mmio_blt_read(s, addr & 0xff); - } - } else { - val = 0xff; -#ifdef DEBUG_CIRRUS - printf("cirrus: mem_readb " TARGET_FMT_plx "\n", addr); -#endif - } - return val; -} - -static void cirrus_vga_mem_write(void *opaque, - hwaddr addr, - uint64_t mem_value, - uint32_t size) -{ - CirrusVGAState *s = opaque; - unsigned bank_index; - unsigned bank_offset; - unsigned mode; - - if ((s->vga.sr[0x07] & 0x01) == 0) { - vga_mem_writeb(&s->vga, addr, mem_value); - return; - } - - if (addr < 0x10000) { - if (s->cirrus_srcptr != s->cirrus_srcptr_end) { - /* bitblt */ - *s->cirrus_srcptr++ = (uint8_t) mem_value; - if (s->cirrus_srcptr >= s->cirrus_srcptr_end) { - cirrus_bitblt_cputovideo_next(s); - } - } else { - /* video memory */ - bank_index = addr >> 15; - bank_offset = addr & 0x7fff; - if (bank_offset < s->cirrus_bank_limit[bank_index]) { - bank_offset += s->cirrus_bank_base[bank_index]; - if ((s->vga.gr[0x0B] & 0x14) == 0x14) { - bank_offset <<= 4; - } else if (s->vga.gr[0x0B] & 0x02) { - bank_offset <<= 3; - } - bank_offset &= s->cirrus_addr_mask; - mode = s->vga.gr[0x05] & 0x7; - if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) { - *(s->vga.vram_ptr + bank_offset) = mem_value; - memory_region_set_dirty(&s->vga.vram, bank_offset, - sizeof(mem_value)); - } else { - if ((s->vga.gr[0x0B] & 0x14) != 0x14) { - cirrus_mem_writeb_mode4and5_8bpp(s, mode, - bank_offset, - mem_value); - } else { - cirrus_mem_writeb_mode4and5_16bpp(s, mode, - bank_offset, - mem_value); - } - } - } - } - } else if (addr >= 0x18000 && addr < 0x18100) { - /* memory-mapped I/O */ - if ((s->vga.sr[0x17] & 0x44) == 0x04) { - cirrus_mmio_blt_write(s, addr & 0xff, mem_value); - } - } else { -#ifdef DEBUG_CIRRUS - printf("cirrus: mem_writeb " TARGET_FMT_plx " value 0x%02" PRIu64 "\n", addr, - mem_value); -#endif - } -} - -static const MemoryRegionOps cirrus_vga_mem_ops = { - .read = cirrus_vga_mem_read, - .write = cirrus_vga_mem_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -/*************************************** - * - * hardware cursor - * - ***************************************/ - -static inline void invalidate_cursor1(CirrusVGAState *s) -{ - if (s->last_hw_cursor_size) { - vga_invalidate_scanlines(&s->vga, - s->last_hw_cursor_y + s->last_hw_cursor_y_start, - s->last_hw_cursor_y + s->last_hw_cursor_y_end); - } -} - -static inline void cirrus_cursor_compute_yrange(CirrusVGAState *s) -{ - const uint8_t *src; - uint32_t content; - int y, y_min, y_max; - - src = s->vga.vram_ptr + s->real_vram_size - 16 * 1024; - if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) { - src += (s->vga.sr[0x13] & 0x3c) * 256; - y_min = 64; - y_max = -1; - for(y = 0; y < 64; y++) { - content = ((uint32_t *)src)[0] | - ((uint32_t *)src)[1] | - ((uint32_t *)src)[2] | - ((uint32_t *)src)[3]; - if (content) { - if (y < y_min) - y_min = y; - if (y > y_max) - y_max = y; - } - src += 16; - } - } else { - src += (s->vga.sr[0x13] & 0x3f) * 256; - y_min = 32; - y_max = -1; - for(y = 0; y < 32; y++) { - content = ((uint32_t *)src)[0] | - ((uint32_t *)(src + 128))[0]; - if (content) { - if (y < y_min) - y_min = y; - if (y > y_max) - y_max = y; - } - src += 4; - } - } - if (y_min > y_max) { - s->last_hw_cursor_y_start = 0; - s->last_hw_cursor_y_end = 0; - } else { - s->last_hw_cursor_y_start = y_min; - s->last_hw_cursor_y_end = y_max + 1; - } -} - -/* NOTE: we do not currently handle the cursor bitmap change, so we - update the cursor only if it moves. */ -static void cirrus_cursor_invalidate(VGACommonState *s1) -{ - CirrusVGAState *s = container_of(s1, CirrusVGAState, vga); - int size; - - if (!(s->vga.sr[0x12] & CIRRUS_CURSOR_SHOW)) { - size = 0; - } else { - if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) - size = 64; - else - size = 32; - } - /* invalidate last cursor and new cursor if any change */ - if (s->last_hw_cursor_size != size || - s->last_hw_cursor_x != s->vga.hw_cursor_x || - s->last_hw_cursor_y != s->vga.hw_cursor_y) { - - invalidate_cursor1(s); - - s->last_hw_cursor_size = size; - s->last_hw_cursor_x = s->vga.hw_cursor_x; - s->last_hw_cursor_y = s->vga.hw_cursor_y; - /* compute the real cursor min and max y */ - cirrus_cursor_compute_yrange(s); - invalidate_cursor1(s); - } -} - -static void vga_draw_cursor_line(uint8_t *d1, - const uint8_t *src1, - int poffset, int w, - unsigned int color0, - unsigned int color1, - unsigned int color_xor) -{ - const uint8_t *plane0, *plane1; - int x, b0, b1; - uint8_t *d; - - d = d1; - plane0 = src1; - plane1 = src1 + poffset; - for (x = 0; x < w; x++) { - b0 = (plane0[x >> 3] >> (7 - (x & 7))) & 1; - b1 = (plane1[x >> 3] >> (7 - (x & 7))) & 1; - switch (b0 | (b1 << 1)) { - case 0: - break; - case 1: - ((uint32_t *)d)[0] ^= color_xor; - break; - case 2: - ((uint32_t *)d)[0] = color0; - break; - case 3: - ((uint32_t *)d)[0] = color1; - break; - } - d += 4; - } -} - -static void cirrus_cursor_draw_line(VGACommonState *s1, uint8_t *d1, int scr_y) -{ - CirrusVGAState *s = container_of(s1, CirrusVGAState, vga); - int w, h, x1, x2, poffset; - unsigned int color0, color1; - const uint8_t *palette, *src; - uint32_t content; - - if (!(s->vga.sr[0x12] & CIRRUS_CURSOR_SHOW)) - return; - /* fast test to see if the cursor intersects with the scan line */ - if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) { - h = 64; - } else { - h = 32; - } - if (scr_y < s->vga.hw_cursor_y || - scr_y >= (s->vga.hw_cursor_y + h)) { - return; - } - - src = s->vga.vram_ptr + s->real_vram_size - 16 * 1024; - if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) { - src += (s->vga.sr[0x13] & 0x3c) * 256; - src += (scr_y - s->vga.hw_cursor_y) * 16; - poffset = 8; - content = ((uint32_t *)src)[0] | - ((uint32_t *)src)[1] | - ((uint32_t *)src)[2] | - ((uint32_t *)src)[3]; - } else { - src += (s->vga.sr[0x13] & 0x3f) * 256; - src += (scr_y - s->vga.hw_cursor_y) * 4; - - - poffset = 128; - content = ((uint32_t *)src)[0] | - ((uint32_t *)(src + 128))[0]; - } - /* if nothing to draw, no need to continue */ - if (!content) - return; - w = h; - - x1 = s->vga.hw_cursor_x; - if (x1 >= s->vga.last_scr_width) - return; - x2 = s->vga.hw_cursor_x + w; - if (x2 > s->vga.last_scr_width) - x2 = s->vga.last_scr_width; - w = x2 - x1; - palette = s->cirrus_hidden_palette; - color0 = rgb_to_pixel32(c6_to_8(palette[0x0 * 3]), - c6_to_8(palette[0x0 * 3 + 1]), - c6_to_8(palette[0x0 * 3 + 2])); - color1 = rgb_to_pixel32(c6_to_8(palette[0xf * 3]), - c6_to_8(palette[0xf * 3 + 1]), - c6_to_8(palette[0xf * 3 + 2])); - d1 += x1 * 4; - vga_draw_cursor_line(d1, src, poffset, w, color0, color1, 0xffffff); -} - -/*************************************** - * - * LFB memory access - * - ***************************************/ - -static uint64_t cirrus_linear_read(void *opaque, hwaddr addr, - unsigned size) -{ - CirrusVGAState *s = opaque; - uint32_t ret; - - addr &= s->cirrus_addr_mask; - - if (((s->vga.sr[0x17] & 0x44) == 0x44) && - ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) { - /* memory-mapped I/O */ - ret = cirrus_mmio_blt_read(s, addr & 0xff); - } else if (0) { - /* XXX handle bitblt */ - ret = 0xff; - } else { - /* video memory */ - if ((s->vga.gr[0x0B] & 0x14) == 0x14) { - addr <<= 4; - } else if (s->vga.gr[0x0B] & 0x02) { - addr <<= 3; - } - addr &= s->cirrus_addr_mask; - ret = *(s->vga.vram_ptr + addr); - } - - return ret; -} - -static void cirrus_linear_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - CirrusVGAState *s = opaque; - unsigned mode; - - addr &= s->cirrus_addr_mask; - - if (((s->vga.sr[0x17] & 0x44) == 0x44) && - ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) { - /* memory-mapped I/O */ - cirrus_mmio_blt_write(s, addr & 0xff, val); - } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) { - /* bitblt */ - *s->cirrus_srcptr++ = (uint8_t) val; - if (s->cirrus_srcptr >= s->cirrus_srcptr_end) { - cirrus_bitblt_cputovideo_next(s); - } - } else { - /* video memory */ - if ((s->vga.gr[0x0B] & 0x14) == 0x14) { - addr <<= 4; - } else if (s->vga.gr[0x0B] & 0x02) { - addr <<= 3; - } - addr &= s->cirrus_addr_mask; - - mode = s->vga.gr[0x05] & 0x7; - if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) { - *(s->vga.vram_ptr + addr) = (uint8_t) val; - memory_region_set_dirty(&s->vga.vram, addr, 1); - } else { - if ((s->vga.gr[0x0B] & 0x14) != 0x14) { - cirrus_mem_writeb_mode4and5_8bpp(s, mode, addr, val); - } else { - cirrus_mem_writeb_mode4and5_16bpp(s, mode, addr, val); - } - } - } -} - -/*************************************** - * - * system to screen memory access - * - ***************************************/ - - -static uint64_t cirrus_linear_bitblt_read(void *opaque, - hwaddr addr, - unsigned size) -{ - CirrusVGAState *s = opaque; - uint32_t ret; - - /* XXX handle bitblt */ - (void)s; - ret = 0xff; - return ret; -} - -static void cirrus_linear_bitblt_write(void *opaque, - hwaddr addr, - uint64_t val, - unsigned size) -{ - CirrusVGAState *s = opaque; - - if (s->cirrus_srcptr != s->cirrus_srcptr_end) { - /* bitblt */ - *s->cirrus_srcptr++ = (uint8_t) val; - if (s->cirrus_srcptr >= s->cirrus_srcptr_end) { - cirrus_bitblt_cputovideo_next(s); - } - } -} - -static const MemoryRegionOps cirrus_linear_bitblt_io_ops = { - .read = cirrus_linear_bitblt_read, - .write = cirrus_linear_bitblt_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void map_linear_vram_bank(CirrusVGAState *s, unsigned bank) -{ - MemoryRegion *mr = &s->cirrus_bank[bank]; - bool enabled = !(s->cirrus_srcptr != s->cirrus_srcptr_end) - && !((s->vga.sr[0x07] & 0x01) == 0) - && !((s->vga.gr[0x0B] & 0x14) == 0x14) - && !(s->vga.gr[0x0B] & 0x02); - - memory_region_set_enabled(mr, enabled); - memory_region_set_alias_offset(mr, s->cirrus_bank_base[bank]); -} - -static void map_linear_vram(CirrusVGAState *s) -{ - if (s->bustype == CIRRUS_BUSTYPE_PCI && !s->linear_vram) { - s->linear_vram = true; - memory_region_add_subregion_overlap(&s->pci_bar, 0, &s->vga.vram, 1); - } - map_linear_vram_bank(s, 0); - map_linear_vram_bank(s, 1); -} - -static void unmap_linear_vram(CirrusVGAState *s) -{ - if (s->bustype == CIRRUS_BUSTYPE_PCI && s->linear_vram) { - s->linear_vram = false; - memory_region_del_subregion(&s->pci_bar, &s->vga.vram); - } - memory_region_set_enabled(&s->cirrus_bank[0], false); - memory_region_set_enabled(&s->cirrus_bank[1], false); -} - -/* Compute the memory access functions */ -static void cirrus_update_memory_access(CirrusVGAState *s) -{ - unsigned mode; - - memory_region_transaction_begin(); - if ((s->vga.sr[0x17] & 0x44) == 0x44) { - goto generic_io; - } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) { - goto generic_io; - } else { - if ((s->vga.gr[0x0B] & 0x14) == 0x14) { - goto generic_io; - } else if (s->vga.gr[0x0B] & 0x02) { - goto generic_io; - } - - mode = s->vga.gr[0x05] & 0x7; - if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) { - map_linear_vram(s); - } else { - generic_io: - unmap_linear_vram(s); - } - } - memory_region_transaction_commit(); -} - - -/* I/O ports */ - -static uint64_t cirrus_vga_ioport_read(void *opaque, hwaddr addr, - unsigned size) -{ - CirrusVGAState *c = opaque; - VGACommonState *s = &c->vga; - int val, index; - - addr += 0x3b0; - - if (vga_ioport_invalid(s, addr)) { - val = 0xff; - } else { - switch (addr) { - case 0x3c0: - if (s->ar_flip_flop == 0) { - val = s->ar_index; - } else { - val = 0; - } - break; - case 0x3c1: - index = s->ar_index & 0x1f; - if (index < 21) - val = s->ar[index]; - else - val = 0; - break; - case 0x3c2: - val = s->st00; - break; - case 0x3c4: - val = s->sr_index; - break; - case 0x3c5: - val = cirrus_vga_read_sr(c); - break; -#ifdef DEBUG_VGA_REG - printf("vga: read SR%x = 0x%02x\n", s->sr_index, val); -#endif - break; - case 0x3c6: - val = cirrus_read_hidden_dac(c); - break; - case 0x3c7: - val = s->dac_state; - break; - case 0x3c8: - val = s->dac_write_index; - c->cirrus_hidden_dac_lockindex = 0; - break; - case 0x3c9: - val = cirrus_vga_read_palette(c); - break; - case 0x3ca: - val = s->fcr; - break; - case 0x3cc: - val = s->msr; - break; - case 0x3ce: - val = s->gr_index; - break; - case 0x3cf: - val = cirrus_vga_read_gr(c, s->gr_index); -#ifdef DEBUG_VGA_REG - printf("vga: read GR%x = 0x%02x\n", s->gr_index, val); -#endif - break; - case 0x3b4: - case 0x3d4: - val = s->cr_index; - break; - case 0x3b5: - case 0x3d5: - val = cirrus_vga_read_cr(c, s->cr_index); -#ifdef DEBUG_VGA_REG - printf("vga: read CR%x = 0x%02x\n", s->cr_index, val); -#endif - break; - case 0x3ba: - case 0x3da: - /* just toggle to fool polling */ - val = s->st01 = s->retrace(s); - s->ar_flip_flop = 0; - break; - default: - val = 0x00; - break; - } - } -#if defined(DEBUG_VGA) - printf("VGA: read addr=0x%04x data=0x%02x\n", addr, val); -#endif - return val; -} - -static void cirrus_vga_ioport_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - CirrusVGAState *c = opaque; - VGACommonState *s = &c->vga; - int index; - - addr += 0x3b0; - - /* check port range access depending on color/monochrome mode */ - if (vga_ioport_invalid(s, addr)) { - return; - } -#ifdef DEBUG_VGA - printf("VGA: write addr=0x%04x data=0x%02x\n", addr, val); -#endif - - switch (addr) { - case 0x3c0: - if (s->ar_flip_flop == 0) { - val &= 0x3f; - s->ar_index = val; - } else { - index = s->ar_index & 0x1f; - switch (index) { - case 0x00 ... 0x0f: - s->ar[index] = val & 0x3f; - break; - case 0x10: - s->ar[index] = val & ~0x10; - break; - case 0x11: - s->ar[index] = val; - break; - case 0x12: - s->ar[index] = val & ~0xc0; - break; - case 0x13: - s->ar[index] = val & ~0xf0; - break; - case 0x14: - s->ar[index] = val & ~0xf0; - break; - default: - break; - } - } - s->ar_flip_flop ^= 1; - break; - case 0x3c2: - s->msr = val & ~0x10; - s->update_retrace_info(s); - break; - case 0x3c4: - s->sr_index = val; - break; - case 0x3c5: -#ifdef DEBUG_VGA_REG - printf("vga: write SR%x = 0x%02" PRIu64 "\n", s->sr_index, val); -#endif - cirrus_vga_write_sr(c, val); - break; - case 0x3c6: - cirrus_write_hidden_dac(c, val); - break; - case 0x3c7: - s->dac_read_index = val; - s->dac_sub_index = 0; - s->dac_state = 3; - break; - case 0x3c8: - s->dac_write_index = val; - s->dac_sub_index = 0; - s->dac_state = 0; - break; - case 0x3c9: - cirrus_vga_write_palette(c, val); - break; - case 0x3ce: - s->gr_index = val; - break; - case 0x3cf: -#ifdef DEBUG_VGA_REG - printf("vga: write GR%x = 0x%02" PRIu64 "\n", s->gr_index, val); -#endif - cirrus_vga_write_gr(c, s->gr_index, val); - break; - case 0x3b4: - case 0x3d4: - s->cr_index = val; - break; - case 0x3b5: - case 0x3d5: -#ifdef DEBUG_VGA_REG - printf("vga: write CR%x = 0x%02"PRIu64"\n", s->cr_index, val); -#endif - cirrus_vga_write_cr(c, val); - break; - case 0x3ba: - case 0x3da: - s->fcr = val & 0x10; - break; - } -} - -/*************************************** - * - * memory-mapped I/O access - * - ***************************************/ - -static uint64_t cirrus_mmio_read(void *opaque, hwaddr addr, - unsigned size) -{ - CirrusVGAState *s = opaque; - - if (addr >= 0x100) { - return cirrus_mmio_blt_read(s, addr - 0x100); - } else { - return cirrus_vga_ioport_read(s, addr + 0x10, size); - } -} - -static void cirrus_mmio_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - CirrusVGAState *s = opaque; - - if (addr >= 0x100) { - cirrus_mmio_blt_write(s, addr - 0x100, val); - } else { - cirrus_vga_ioport_write(s, addr + 0x10, val, size); - } -} - -static const MemoryRegionOps cirrus_mmio_io_ops = { - .read = cirrus_mmio_read, - .write = cirrus_mmio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -/* load/save state */ - -static int cirrus_post_load(void *opaque, int version_id) -{ - CirrusVGAState *s = opaque; - - s->vga.gr[0x00] = s->cirrus_shadow_gr0 & 0x0f; - s->vga.gr[0x01] = s->cirrus_shadow_gr1 & 0x0f; - - cirrus_update_memory_access(s); - /* force refresh */ - s->vga.graphic_mode = -1; - cirrus_update_bank_ptr(s, 0); - cirrus_update_bank_ptr(s, 1); - return 0; -} - -static const VMStateDescription vmstate_cirrus_vga = { - .name = "cirrus_vga", - .version_id = 2, - .minimum_version_id = 1, - .post_load = cirrus_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(vga.latch, CirrusVGAState), - VMSTATE_UINT8(vga.sr_index, CirrusVGAState), - VMSTATE_BUFFER(vga.sr, CirrusVGAState), - VMSTATE_UINT8(vga.gr_index, CirrusVGAState), - VMSTATE_UINT8(cirrus_shadow_gr0, CirrusVGAState), - VMSTATE_UINT8(cirrus_shadow_gr1, CirrusVGAState), - VMSTATE_BUFFER_START_MIDDLE(vga.gr, CirrusVGAState, 2), - VMSTATE_UINT8(vga.ar_index, CirrusVGAState), - VMSTATE_BUFFER(vga.ar, CirrusVGAState), - VMSTATE_INT32(vga.ar_flip_flop, CirrusVGAState), - VMSTATE_UINT8(vga.cr_index, CirrusVGAState), - VMSTATE_BUFFER(vga.cr, CirrusVGAState), - VMSTATE_UINT8(vga.msr, CirrusVGAState), - VMSTATE_UINT8(vga.fcr, CirrusVGAState), - VMSTATE_UINT8(vga.st00, CirrusVGAState), - VMSTATE_UINT8(vga.st01, CirrusVGAState), - VMSTATE_UINT8(vga.dac_state, CirrusVGAState), - VMSTATE_UINT8(vga.dac_sub_index, CirrusVGAState), - VMSTATE_UINT8(vga.dac_read_index, CirrusVGAState), - VMSTATE_UINT8(vga.dac_write_index, CirrusVGAState), - VMSTATE_BUFFER(vga.dac_cache, CirrusVGAState), - VMSTATE_BUFFER(vga.palette, CirrusVGAState), - VMSTATE_INT32(vga.bank_offset, CirrusVGAState), - VMSTATE_UINT8(cirrus_hidden_dac_lockindex, CirrusVGAState), - VMSTATE_UINT8(cirrus_hidden_dac_data, CirrusVGAState), - VMSTATE_UINT32(vga.hw_cursor_x, CirrusVGAState), - VMSTATE_UINT32(vga.hw_cursor_y, CirrusVGAState), - /* XXX: we do not save the bitblt state - we assume we do not save - the state when the blitter is active */ - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_pci_cirrus_vga = { - .name = "cirrus_vga", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, PCICirrusVGAState), - VMSTATE_STRUCT(cirrus_vga, PCICirrusVGAState, 0, - vmstate_cirrus_vga, CirrusVGAState), - VMSTATE_END_OF_LIST() - } -}; - -/*************************************** - * - * initialize - * - ***************************************/ - -static void cirrus_reset(void *opaque) -{ - CirrusVGAState *s = opaque; - - vga_common_reset(&s->vga); - unmap_linear_vram(s); - s->vga.sr[0x06] = 0x0f; - if (s->device_id == CIRRUS_ID_CLGD5446) { - /* 4MB 64 bit memory config, always PCI */ - s->vga.sr[0x1F] = 0x2d; // MemClock - s->vga.gr[0x18] = 0x0f; // fastest memory configuration - s->vga.sr[0x0f] = 0x98; - s->vga.sr[0x17] = 0x20; - s->vga.sr[0x15] = 0x04; /* memory size, 3=2MB, 4=4MB */ - } else { - s->vga.sr[0x1F] = 0x22; // MemClock - s->vga.sr[0x0F] = CIRRUS_MEMSIZE_2M; - s->vga.sr[0x17] = s->bustype; - s->vga.sr[0x15] = 0x03; /* memory size, 3=2MB, 4=4MB */ - } - s->vga.cr[0x27] = s->device_id; - - s->cirrus_hidden_dac_lockindex = 5; - s->cirrus_hidden_dac_data = 0; -} - -static const MemoryRegionOps cirrus_linear_io_ops = { - .read = cirrus_linear_read, - .write = cirrus_linear_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static const MemoryRegionOps cirrus_vga_io_ops = { - .read = cirrus_vga_ioport_read, - .write = cirrus_vga_ioport_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void cirrus_init_common(CirrusVGAState *s, Object *owner, - int device_id, int is_pci, - MemoryRegion *system_memory, - MemoryRegion *system_io) -{ - int i; - static int inited; - - if (!inited) { - inited = 1; - for(i = 0;i < 256; i++) - rop_to_index[i] = CIRRUS_ROP_NOP_INDEX; /* nop rop */ - rop_to_index[CIRRUS_ROP_0] = 0; - rop_to_index[CIRRUS_ROP_SRC_AND_DST] = 1; - rop_to_index[CIRRUS_ROP_NOP] = 2; - rop_to_index[CIRRUS_ROP_SRC_AND_NOTDST] = 3; - rop_to_index[CIRRUS_ROP_NOTDST] = 4; - rop_to_index[CIRRUS_ROP_SRC] = 5; - rop_to_index[CIRRUS_ROP_1] = 6; - rop_to_index[CIRRUS_ROP_NOTSRC_AND_DST] = 7; - rop_to_index[CIRRUS_ROP_SRC_XOR_DST] = 8; - rop_to_index[CIRRUS_ROP_SRC_OR_DST] = 9; - rop_to_index[CIRRUS_ROP_NOTSRC_OR_NOTDST] = 10; - rop_to_index[CIRRUS_ROP_SRC_NOTXOR_DST] = 11; - rop_to_index[CIRRUS_ROP_SRC_OR_NOTDST] = 12; - rop_to_index[CIRRUS_ROP_NOTSRC] = 13; - rop_to_index[CIRRUS_ROP_NOTSRC_OR_DST] = 14; - rop_to_index[CIRRUS_ROP_NOTSRC_AND_NOTDST] = 15; - s->device_id = device_id; - if (is_pci) - s->bustype = CIRRUS_BUSTYPE_PCI; - else - s->bustype = CIRRUS_BUSTYPE_ISA; - } - - /* Register ioport 0x3b0 - 0x3df */ - memory_region_init_io(&s->cirrus_vga_io, owner, &cirrus_vga_io_ops, s, - "cirrus-io", 0x30); - memory_region_set_flush_coalesced(&s->cirrus_vga_io); - memory_region_add_subregion(system_io, 0x3b0, &s->cirrus_vga_io); - - memory_region_init(&s->low_mem_container, owner, - "cirrus-lowmem-container", - 0x20000); - - memory_region_init_io(&s->low_mem, owner, &cirrus_vga_mem_ops, s, - "cirrus-low-memory", 0x20000); - memory_region_add_subregion(&s->low_mem_container, 0, &s->low_mem); - for (i = 0; i < 2; ++i) { - static const char *names[] = { "vga.bank0", "vga.bank1" }; - MemoryRegion *bank = &s->cirrus_bank[i]; - memory_region_init_alias(bank, owner, names[i], &s->vga.vram, - 0, 0x8000); - memory_region_set_enabled(bank, false); - memory_region_add_subregion_overlap(&s->low_mem_container, i * 0x8000, - bank, 1); - } - memory_region_add_subregion_overlap(system_memory, - 0x000a0000, - &s->low_mem_container, - 1); - memory_region_set_coalescing(&s->low_mem); - - /* I/O handler for LFB */ - memory_region_init_io(&s->cirrus_linear_io, owner, &cirrus_linear_io_ops, s, - "cirrus-linear-io", s->vga.vram_size_mb - * 1024 * 1024); - memory_region_set_flush_coalesced(&s->cirrus_linear_io); - - /* I/O handler for LFB */ - memory_region_init_io(&s->cirrus_linear_bitblt_io, owner, - &cirrus_linear_bitblt_io_ops, - s, - "cirrus-bitblt-mmio", - 0x400000); - memory_region_set_flush_coalesced(&s->cirrus_linear_bitblt_io); - - /* I/O handler for memory-mapped I/O */ - memory_region_init_io(&s->cirrus_mmio_io, owner, &cirrus_mmio_io_ops, s, - "cirrus-mmio", CIRRUS_PNPMMIO_SIZE); - memory_region_set_flush_coalesced(&s->cirrus_mmio_io); - - s->real_vram_size = - (s->device_id == CIRRUS_ID_CLGD5446) ? 4096 * 1024 : 2048 * 1024; - - /* XXX: s->vga.vram_size must be a power of two */ - s->cirrus_addr_mask = s->real_vram_size - 1; - s->linear_mmio_mask = s->real_vram_size - 256; - - s->vga.get_bpp = cirrus_get_bpp; - s->vga.get_offsets = cirrus_get_offsets; - s->vga.get_resolution = cirrus_get_resolution; - s->vga.cursor_invalidate = cirrus_cursor_invalidate; - s->vga.cursor_draw_line = cirrus_cursor_draw_line; - - qemu_register_reset(cirrus_reset, s); -} - -/*************************************** - * - * ISA bus support - * - ***************************************/ - -static void isa_cirrus_vga_realizefn(DeviceState *dev, Error **errp) -{ - ISADevice *isadev = ISA_DEVICE(dev); - ISACirrusVGAState *d = ISA_CIRRUS_VGA(dev); - VGACommonState *s = &d->cirrus_vga.vga; - - /* follow real hardware, cirrus card emulated has 4 MB video memory. - Also accept 8 MB/16 MB for backward compatibility. */ - if (s->vram_size_mb != 4 && s->vram_size_mb != 8 && - s->vram_size_mb != 16) { - error_setg(errp, "Invalid cirrus_vga ram size '%u'", - s->vram_size_mb); - return; - } - vga_common_init(s, OBJECT(dev), true); - cirrus_init_common(&d->cirrus_vga, OBJECT(dev), CIRRUS_ID_CLGD5430, 0, - isa_address_space(isadev), - isa_address_space_io(isadev)); - s->con = graphic_console_init(dev, 0, s->hw_ops, s); - rom_add_vga(VGABIOS_CIRRUS_FILENAME); - /* XXX ISA-LFB support */ - /* FIXME not qdev yet */ -} - -static Property isa_cirrus_vga_properties[] = { - DEFINE_PROP_UINT32("vgamem_mb", struct ISACirrusVGAState, - cirrus_vga.vga.vram_size_mb, 8), - DEFINE_PROP_END_OF_LIST(), -}; - -static void isa_cirrus_vga_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_cirrus_vga; - dc->realize = isa_cirrus_vga_realizefn; - dc->props = isa_cirrus_vga_properties; - set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); -} - -static const TypeInfo isa_cirrus_vga_info = { - .name = TYPE_ISA_CIRRUS_VGA, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(ISACirrusVGAState), - .class_init = isa_cirrus_vga_class_init, -}; - -/*************************************** - * - * PCI bus support - * - ***************************************/ - -static void pci_cirrus_vga_realize(PCIDevice *dev, Error **errp) -{ - PCICirrusVGAState *d = PCI_CIRRUS_VGA(dev); - CirrusVGAState *s = &d->cirrus_vga; - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); - int16_t device_id = pc->device_id; - - /* follow real hardware, cirrus card emulated has 4 MB video memory. - Also accept 8 MB/16 MB for backward compatibility. */ - if (s->vga.vram_size_mb != 4 && s->vga.vram_size_mb != 8 && - s->vga.vram_size_mb != 16) { - error_setg(errp, "Invalid cirrus_vga ram size '%u'", - s->vga.vram_size_mb); - return; - } - /* setup VGA */ - vga_common_init(&s->vga, OBJECT(dev), true); - cirrus_init_common(s, OBJECT(dev), device_id, 1, pci_address_space(dev), - pci_address_space_io(dev)); - s->vga.con = graphic_console_init(DEVICE(dev), 0, s->vga.hw_ops, &s->vga); - - /* setup PCI */ - - memory_region_init(&s->pci_bar, OBJECT(dev), "cirrus-pci-bar0", 0x2000000); - - /* XXX: add byte swapping apertures */ - memory_region_add_subregion(&s->pci_bar, 0, &s->cirrus_linear_io); - memory_region_add_subregion(&s->pci_bar, 0x1000000, - &s->cirrus_linear_bitblt_io); - - /* setup memory space */ - /* memory #0 LFB */ - /* memory #1 memory-mapped I/O */ - /* XXX: s->vga.vram_size must be a power of two */ - pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->pci_bar); - if (device_id == CIRRUS_ID_CLGD5446) { - pci_register_bar(&d->dev, 1, 0, &s->cirrus_mmio_io); - } -} - -static Property pci_vga_cirrus_properties[] = { - DEFINE_PROP_UINT32("vgamem_mb", struct PCICirrusVGAState, - cirrus_vga.vga.vram_size_mb, 8), - DEFINE_PROP_END_OF_LIST(), -}; - -static void cirrus_vga_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = pci_cirrus_vga_realize; - k->romfile = VGABIOS_CIRRUS_FILENAME; - k->vendor_id = PCI_VENDOR_ID_CIRRUS; - k->device_id = CIRRUS_ID_CLGD5446; - k->class_id = PCI_CLASS_DISPLAY_VGA; - set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); - dc->desc = "Cirrus CLGD 54xx VGA"; - dc->vmsd = &vmstate_pci_cirrus_vga; - dc->props = pci_vga_cirrus_properties; - dc->hotpluggable = false; -} - -static const TypeInfo cirrus_vga_info = { - .name = TYPE_PCI_CIRRUS_VGA, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCICirrusVGAState), - .class_init = cirrus_vga_class_init, -}; - -static void cirrus_vga_register_types(void) -{ - type_register_static(&isa_cirrus_vga_info); - type_register_static(&cirrus_vga_info); -} - -type_init(cirrus_vga_register_types) diff --git a/qemu/hw/display/cirrus_vga_rop.h b/qemu/hw/display/cirrus_vga_rop.h deleted file mode 100644 index 0925a009f..000000000 --- a/qemu/hw/display/cirrus_vga_rop.h +++ /dev/null @@ -1,207 +0,0 @@ -/* - * QEMU Cirrus CLGD 54xx VGA Emulator. - * - * Copyright (c) 2004 Fabrice Bellard - * - * 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. - */ - -static inline void glue(rop_8_,ROP_NAME)(uint8_t *dst, uint8_t src) -{ - *dst = ROP_FN(*dst, src); -} - -static inline void glue(rop_16_,ROP_NAME)(uint16_t *dst, uint16_t src) -{ - *dst = ROP_FN(*dst, src); -} - -static inline void glue(rop_32_,ROP_NAME)(uint32_t *dst, uint32_t src) -{ - *dst = ROP_FN(*dst, src); -} - -#define ROP_OP(d, s) glue(rop_8_,ROP_NAME)(d, s) -#define ROP_OP_16(d, s) glue(rop_16_,ROP_NAME)(d, s) -#define ROP_OP_32(d, s) glue(rop_32_,ROP_NAME)(d, s) -#undef ROP_FN - -static void -glue(cirrus_bitblt_rop_fwd_, ROP_NAME)(CirrusVGAState *s, - uint8_t *dst,const uint8_t *src, - int dstpitch,int srcpitch, - int bltwidth,int bltheight) -{ - int x,y; - dstpitch -= bltwidth; - srcpitch -= bltwidth; - - if (bltheight > 1 && (dstpitch < 0 || srcpitch < 0)) { - return; - } - - for (y = 0; y < bltheight; y++) { - for (x = 0; x < bltwidth; x++) { - ROP_OP(dst, *src); - dst++; - src++; - } - dst += dstpitch; - src += srcpitch; - } -} - -static void -glue(cirrus_bitblt_rop_bkwd_, ROP_NAME)(CirrusVGAState *s, - uint8_t *dst,const uint8_t *src, - int dstpitch,int srcpitch, - int bltwidth,int bltheight) -{ - int x,y; - dstpitch += bltwidth; - srcpitch += bltwidth; - for (y = 0; y < bltheight; y++) { - for (x = 0; x < bltwidth; x++) { - ROP_OP(dst, *src); - dst--; - src--; - } - dst += dstpitch; - src += srcpitch; - } -} - -static void -glue(glue(cirrus_bitblt_rop_fwd_transp_, ROP_NAME),_8)(CirrusVGAState *s, - uint8_t *dst,const uint8_t *src, - int dstpitch,int srcpitch, - int bltwidth,int bltheight) -{ - int x,y; - uint8_t p; - dstpitch -= bltwidth; - srcpitch -= bltwidth; - for (y = 0; y < bltheight; y++) { - for (x = 0; x < bltwidth; x++) { - p = *dst; - ROP_OP(&p, *src); - if (p != s->vga.gr[0x34]) *dst = p; - dst++; - src++; - } - dst += dstpitch; - src += srcpitch; - } -} - -static void -glue(glue(cirrus_bitblt_rop_bkwd_transp_, ROP_NAME),_8)(CirrusVGAState *s, - uint8_t *dst,const uint8_t *src, - int dstpitch,int srcpitch, - int bltwidth,int bltheight) -{ - int x,y; - uint8_t p; - dstpitch += bltwidth; - srcpitch += bltwidth; - for (y = 0; y < bltheight; y++) { - for (x = 0; x < bltwidth; x++) { - p = *dst; - ROP_OP(&p, *src); - if (p != s->vga.gr[0x34]) *dst = p; - dst--; - src--; - } - dst += dstpitch; - src += srcpitch; - } -} - -static void -glue(glue(cirrus_bitblt_rop_fwd_transp_, ROP_NAME),_16)(CirrusVGAState *s, - uint8_t *dst,const uint8_t *src, - int dstpitch,int srcpitch, - int bltwidth,int bltheight) -{ - int x,y; - uint8_t p1, p2; - dstpitch -= bltwidth; - srcpitch -= bltwidth; - for (y = 0; y < bltheight; y++) { - for (x = 0; x < bltwidth; x+=2) { - p1 = *dst; - p2 = *(dst+1); - ROP_OP(&p1, *src); - ROP_OP(&p2, *(src + 1)); - if ((p1 != s->vga.gr[0x34]) || (p2 != s->vga.gr[0x35])) { - *dst = p1; - *(dst+1) = p2; - } - dst+=2; - src+=2; - } - dst += dstpitch; - src += srcpitch; - } -} - -static void -glue(glue(cirrus_bitblt_rop_bkwd_transp_, ROP_NAME),_16)(CirrusVGAState *s, - uint8_t *dst,const uint8_t *src, - int dstpitch,int srcpitch, - int bltwidth,int bltheight) -{ - int x,y; - uint8_t p1, p2; - dstpitch += bltwidth; - srcpitch += bltwidth; - for (y = 0; y < bltheight; y++) { - for (x = 0; x < bltwidth; x+=2) { - p1 = *(dst-1); - p2 = *dst; - ROP_OP(&p1, *(src - 1)); - ROP_OP(&p2, *src); - if ((p1 != s->vga.gr[0x34]) || (p2 != s->vga.gr[0x35])) { - *(dst-1) = p1; - *dst = p2; - } - dst-=2; - src-=2; - } - dst += dstpitch; - src += srcpitch; - } -} - -#define DEPTH 8 -#include "cirrus_vga_rop2.h" - -#define DEPTH 16 -#include "cirrus_vga_rop2.h" - -#define DEPTH 24 -#include "cirrus_vga_rop2.h" - -#define DEPTH 32 -#include "cirrus_vga_rop2.h" - -#undef ROP_NAME -#undef ROP_OP -#undef ROP_OP_16 -#undef ROP_OP_32 diff --git a/qemu/hw/display/cirrus_vga_rop2.h b/qemu/hw/display/cirrus_vga_rop2.h deleted file mode 100644 index d28bcc6f2..000000000 --- a/qemu/hw/display/cirrus_vga_rop2.h +++ /dev/null @@ -1,281 +0,0 @@ -/* - * QEMU Cirrus CLGD 54xx VGA Emulator. - * - * Copyright (c) 2004 Fabrice Bellard - * - * 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. - */ - -#if DEPTH == 8 -#define PUTPIXEL() ROP_OP(&d[0], col) -#elif DEPTH == 16 -#define PUTPIXEL() ROP_OP_16((uint16_t *)&d[0], col) -#elif DEPTH == 24 -#define PUTPIXEL() ROP_OP(&d[0], col); \ - ROP_OP(&d[1], (col >> 8)); \ - ROP_OP(&d[2], (col >> 16)) -#elif DEPTH == 32 -#define PUTPIXEL() ROP_OP_32(((uint32_t *)&d[0]), col) -#else -#error unsupported DEPTH -#endif - -static void -glue(glue(glue(cirrus_patternfill_, ROP_NAME), _),DEPTH) - (CirrusVGAState * s, uint8_t * dst, - const uint8_t * src, - int dstpitch, int srcpitch, - int bltwidth, int bltheight) -{ - uint8_t *d; - int x, y, pattern_y, pattern_pitch, pattern_x; - unsigned int col; - const uint8_t *src1; -#if DEPTH == 24 - int skipleft = s->vga.gr[0x2f] & 0x1f; -#else - int skipleft = (s->vga.gr[0x2f] & 0x07) * (DEPTH / 8); -#endif - -#if DEPTH == 8 - pattern_pitch = 8; -#elif DEPTH == 16 - pattern_pitch = 16; -#else - pattern_pitch = 32; -#endif - pattern_y = s->cirrus_blt_srcaddr & 7; - for(y = 0; y < bltheight; y++) { - pattern_x = skipleft; - d = dst + skipleft; - src1 = src + pattern_y * pattern_pitch; - for (x = skipleft; x < bltwidth; x += (DEPTH / 8)) { -#if DEPTH == 8 - col = src1[pattern_x]; - pattern_x = (pattern_x + 1) & 7; -#elif DEPTH == 16 - col = ((uint16_t *)(src1 + pattern_x))[0]; - pattern_x = (pattern_x + 2) & 15; -#elif DEPTH == 24 - { - const uint8_t *src2 = src1 + pattern_x * 3; - col = src2[0] | (src2[1] << 8) | (src2[2] << 16); - pattern_x = (pattern_x + 1) & 7; - } -#else - col = ((uint32_t *)(src1 + pattern_x))[0]; - pattern_x = (pattern_x + 4) & 31; -#endif - PUTPIXEL(); - d += (DEPTH / 8); - } - pattern_y = (pattern_y + 1) & 7; - dst += dstpitch; - } -} - -/* NOTE: srcpitch is ignored */ -static void -glue(glue(glue(cirrus_colorexpand_transp_, ROP_NAME), _),DEPTH) - (CirrusVGAState * s, uint8_t * dst, - const uint8_t * src, - int dstpitch, int srcpitch, - int bltwidth, int bltheight) -{ - uint8_t *d; - int x, y; - unsigned bits, bits_xor; - unsigned int col; - unsigned bitmask; - unsigned index; -#if DEPTH == 24 - int dstskipleft = s->vga.gr[0x2f] & 0x1f; - int srcskipleft = dstskipleft / 3; -#else - int srcskipleft = s->vga.gr[0x2f] & 0x07; - int dstskipleft = srcskipleft * (DEPTH / 8); -#endif - - if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) { - bits_xor = 0xff; - col = s->cirrus_blt_bgcol; - } else { - bits_xor = 0x00; - col = s->cirrus_blt_fgcol; - } - - for(y = 0; y < bltheight; y++) { - bitmask = 0x80 >> srcskipleft; - bits = *src++ ^ bits_xor; - d = dst + dstskipleft; - for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) { - if ((bitmask & 0xff) == 0) { - bitmask = 0x80; - bits = *src++ ^ bits_xor; - } - index = (bits & bitmask); - if (index) { - PUTPIXEL(); - } - d += (DEPTH / 8); - bitmask >>= 1; - } - dst += dstpitch; - } -} - -static void -glue(glue(glue(cirrus_colorexpand_, ROP_NAME), _),DEPTH) - (CirrusVGAState * s, uint8_t * dst, - const uint8_t * src, - int dstpitch, int srcpitch, - int bltwidth, int bltheight) -{ - uint32_t colors[2]; - uint8_t *d; - int x, y; - unsigned bits; - unsigned int col; - unsigned bitmask; - int srcskipleft = s->vga.gr[0x2f] & 0x07; - int dstskipleft = srcskipleft * (DEPTH / 8); - - colors[0] = s->cirrus_blt_bgcol; - colors[1] = s->cirrus_blt_fgcol; - for(y = 0; y < bltheight; y++) { - bitmask = 0x80 >> srcskipleft; - bits = *src++; - d = dst + dstskipleft; - for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) { - if ((bitmask & 0xff) == 0) { - bitmask = 0x80; - bits = *src++; - } - col = colors[!!(bits & bitmask)]; - PUTPIXEL(); - d += (DEPTH / 8); - bitmask >>= 1; - } - dst += dstpitch; - } -} - -static void -glue(glue(glue(cirrus_colorexpand_pattern_transp_, ROP_NAME), _),DEPTH) - (CirrusVGAState * s, uint8_t * dst, - const uint8_t * src, - int dstpitch, int srcpitch, - int bltwidth, int bltheight) -{ - uint8_t *d; - int x, y, bitpos, pattern_y; - unsigned int bits, bits_xor; - unsigned int col; -#if DEPTH == 24 - int dstskipleft = s->vga.gr[0x2f] & 0x1f; - int srcskipleft = dstskipleft / 3; -#else - int srcskipleft = s->vga.gr[0x2f] & 0x07; - int dstskipleft = srcskipleft * (DEPTH / 8); -#endif - - if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) { - bits_xor = 0xff; - col = s->cirrus_blt_bgcol; - } else { - bits_xor = 0x00; - col = s->cirrus_blt_fgcol; - } - pattern_y = s->cirrus_blt_srcaddr & 7; - - for(y = 0; y < bltheight; y++) { - bits = src[pattern_y] ^ bits_xor; - bitpos = 7 - srcskipleft; - d = dst + dstskipleft; - for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) { - if ((bits >> bitpos) & 1) { - PUTPIXEL(); - } - d += (DEPTH / 8); - bitpos = (bitpos - 1) & 7; - } - pattern_y = (pattern_y + 1) & 7; - dst += dstpitch; - } -} - -static void -glue(glue(glue(cirrus_colorexpand_pattern_, ROP_NAME), _),DEPTH) - (CirrusVGAState * s, uint8_t * dst, - const uint8_t * src, - int dstpitch, int srcpitch, - int bltwidth, int bltheight) -{ - uint32_t colors[2]; - uint8_t *d; - int x, y, bitpos, pattern_y; - unsigned int bits; - unsigned int col; - int srcskipleft = s->vga.gr[0x2f] & 0x07; - int dstskipleft = srcskipleft * (DEPTH / 8); - - colors[0] = s->cirrus_blt_bgcol; - colors[1] = s->cirrus_blt_fgcol; - pattern_y = s->cirrus_blt_srcaddr & 7; - - for(y = 0; y < bltheight; y++) { - bits = src[pattern_y]; - bitpos = 7 - srcskipleft; - d = dst + dstskipleft; - for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) { - col = colors[(bits >> bitpos) & 1]; - PUTPIXEL(); - d += (DEPTH / 8); - bitpos = (bitpos - 1) & 7; - } - pattern_y = (pattern_y + 1) & 7; - dst += dstpitch; - } -} - -static void -glue(glue(glue(cirrus_fill_, ROP_NAME), _),DEPTH) - (CirrusVGAState *s, - uint8_t *dst, int dst_pitch, - int width, int height) -{ - uint8_t *d, *d1; - uint32_t col; - int x, y; - - col = s->cirrus_blt_fgcol; - - d1 = dst; - for(y = 0; y < height; y++) { - d = d1; - for(x = 0; x < width; x += (DEPTH / 8)) { - PUTPIXEL(); - d += (DEPTH / 8); - } - d1 += dst_pitch; - } -} - -#undef DEPTH -#undef PUTPIXEL diff --git a/qemu/hw/display/exynos4210_fimd.c b/qemu/hw/display/exynos4210_fimd.c deleted file mode 100644 index 728eb214a..000000000 --- a/qemu/hw/display/exynos4210_fimd.c +++ /dev/null @@ -1,1952 +0,0 @@ -/* - * Samsung exynos4210 Display Controller (FIMD) - * - * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. - * All rights reserved. - * Based on LCD controller for Samsung S5PC1xx-based board emulation - * by Kirill Batuzov - * - * Contributed by Mitsyanko Igor - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "hw/sysbus.h" -#include "ui/console.h" -#include "ui/pixel_ops.h" -#include "qemu/bswap.h" - -/* Debug messages configuration */ -#define EXYNOS4210_FIMD_DEBUG 0 -#define EXYNOS4210_FIMD_MODE_TRACE 0 - -#if EXYNOS4210_FIMD_DEBUG == 0 - #define DPRINT_L1(fmt, args...) do { } while (0) - #define DPRINT_L2(fmt, args...) do { } while (0) - #define DPRINT_ERROR(fmt, args...) do { } while (0) -#elif EXYNOS4210_FIMD_DEBUG == 1 - #define DPRINT_L1(fmt, args...) \ - do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0) - #define DPRINT_L2(fmt, args...) do { } while (0) - #define DPRINT_ERROR(fmt, args...) \ - do {fprintf(stderr, "QEMU FIMD ERROR: "fmt, ## args); } while (0) -#else - #define DPRINT_L1(fmt, args...) \ - do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0) - #define DPRINT_L2(fmt, args...) \ - do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0) - #define DPRINT_ERROR(fmt, args...) \ - do {fprintf(stderr, "QEMU FIMD ERROR: "fmt, ## args); } while (0) -#endif - -#if EXYNOS4210_FIMD_MODE_TRACE == 0 - #define DPRINT_TRACE(fmt, args...) do { } while (0) -#else - #define DPRINT_TRACE(fmt, args...) \ - do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0) -#endif - -#define NUM_OF_WINDOWS 5 -#define FIMD_REGS_SIZE 0x4114 - -/* Video main control registers */ -#define FIMD_VIDCON0 0x0000 -#define FIMD_VIDCON1 0x0004 -#define FIMD_VIDCON2 0x0008 -#define FIMD_VIDCON3 0x000C -#define FIMD_VIDCON0_ENVID_F (1 << 0) -#define FIMD_VIDCON0_ENVID (1 << 1) -#define FIMD_VIDCON0_ENVID_MASK ((1 << 0) | (1 << 1)) -#define FIMD_VIDCON1_ROMASK 0x07FFE000 - -/* Video time control registers */ -#define FIMD_VIDTCON_START 0x10 -#define FIMD_VIDTCON_END 0x1C -#define FIMD_VIDTCON2_SIZE_MASK 0x07FF -#define FIMD_VIDTCON2_HOR_SHIFT 0 -#define FIMD_VIDTCON2_VER_SHIFT 11 - -/* Window control registers */ -#define FIMD_WINCON_START 0x0020 -#define FIMD_WINCON_END 0x0030 -#define FIMD_WINCON_ROMASK 0x82200000 -#define FIMD_WINCON_ENWIN (1 << 0) -#define FIMD_WINCON_BLD_PIX (1 << 6) -#define FIMD_WINCON_ALPHA_MUL (1 << 7) -#define FIMD_WINCON_ALPHA_SEL (1 << 1) -#define FIMD_WINCON_SWAP 0x078000 -#define FIMD_WINCON_SWAP_SHIFT 15 -#define FIMD_WINCON_SWAP_WORD 0x1 -#define FIMD_WINCON_SWAP_HWORD 0x2 -#define FIMD_WINCON_SWAP_BYTE 0x4 -#define FIMD_WINCON_SWAP_BITS 0x8 -#define FIMD_WINCON_BUFSTAT_L (1 << 21) -#define FIMD_WINCON_BUFSTAT_H (1 << 31) -#define FIMD_WINCON_BUFSTATUS ((1 << 21) | (1 << 31)) -#define FIMD_WINCON_BUF0_STAT ((0 << 21) | (0 << 31)) -#define FIMD_WINCON_BUF1_STAT ((1 << 21) | (0 << 31)) -#define FIMD_WINCON_BUF2_STAT ((0 << 21) | (1 << 31)) -#define FIMD_WINCON_BUFSELECT ((1 << 20) | (1 << 30)) -#define FIMD_WINCON_BUF0_SEL ((0 << 20) | (0 << 30)) -#define FIMD_WINCON_BUF1_SEL ((1 << 20) | (0 << 30)) -#define FIMD_WINCON_BUF2_SEL ((0 << 20) | (1 << 30)) -#define FIMD_WINCON_BUFMODE (1 << 14) -#define IS_PALETTIZED_MODE(w) (w->wincon & 0xC) -#define PAL_MODE_WITH_ALPHA(x) ((x) == 7) -#define WIN_BPP_MODE(w) ((w->wincon >> 2) & 0xF) -#define WIN_BPP_MODE_WITH_ALPHA(w) \ - (WIN_BPP_MODE(w) == 0xD || WIN_BPP_MODE(w) == 0xE) - -/* Shadow control register */ -#define FIMD_SHADOWCON 0x0034 -#define FIMD_WINDOW_PROTECTED(s, w) ((s) & (1 << (10 + (w)))) -/* Channel mapping control register */ -#define FIMD_WINCHMAP 0x003C - -/* Window position control registers */ -#define FIMD_VIDOSD_START 0x0040 -#define FIMD_VIDOSD_END 0x0088 -#define FIMD_VIDOSD_COORD_MASK 0x07FF -#define FIMD_VIDOSD_HOR_SHIFT 11 -#define FIMD_VIDOSD_VER_SHIFT 0 -#define FIMD_VIDOSD_ALPHA_AEN0 0xFFF000 -#define FIMD_VIDOSD_AEN0_SHIFT 12 -#define FIMD_VIDOSD_ALPHA_AEN1 0x000FFF - -/* Frame buffer address registers */ -#define FIMD_VIDWADD0_START 0x00A0 -#define FIMD_VIDWADD0_END 0x00C4 -#define FIMD_VIDWADD0_END 0x00C4 -#define FIMD_VIDWADD1_START 0x00D0 -#define FIMD_VIDWADD1_END 0x00F4 -#define FIMD_VIDWADD2_START 0x0100 -#define FIMD_VIDWADD2_END 0x0110 -#define FIMD_VIDWADD2_PAGEWIDTH 0x1FFF -#define FIMD_VIDWADD2_OFFSIZE 0x1FFF -#define FIMD_VIDWADD2_OFFSIZE_SHIFT 13 -#define FIMD_VIDW0ADD0_B2 0x20A0 -#define FIMD_VIDW4ADD0_B2 0x20C0 - -/* Video interrupt control registers */ -#define FIMD_VIDINTCON0 0x130 -#define FIMD_VIDINTCON1 0x134 - -/* Window color key registers */ -#define FIMD_WKEYCON_START 0x140 -#define FIMD_WKEYCON_END 0x15C -#define FIMD_WKEYCON0_COMPKEY 0x00FFFFFF -#define FIMD_WKEYCON0_CTL_SHIFT 24 -#define FIMD_WKEYCON0_DIRCON (1 << 24) -#define FIMD_WKEYCON0_KEYEN (1 << 25) -#define FIMD_WKEYCON0_KEYBLEN (1 << 26) -/* Window color key alpha control register */ -#define FIMD_WKEYALPHA_START 0x160 -#define FIMD_WKEYALPHA_END 0x16C - -/* Dithering control register */ -#define FIMD_DITHMODE 0x170 - -/* Window alpha control registers */ -#define FIMD_VIDALPHA_ALPHA_LOWER 0x000F0F0F -#define FIMD_VIDALPHA_ALPHA_UPPER 0x00F0F0F0 -#define FIMD_VIDWALPHA_START 0x21C -#define FIMD_VIDWALPHA_END 0x240 - -/* Window color map registers */ -#define FIMD_WINMAP_START 0x180 -#define FIMD_WINMAP_END 0x190 -#define FIMD_WINMAP_EN (1 << 24) -#define FIMD_WINMAP_COLOR_MASK 0x00FFFFFF - -/* Window palette control registers */ -#define FIMD_WPALCON_HIGH 0x019C -#define FIMD_WPALCON_LOW 0x01A0 -#define FIMD_WPALCON_UPDATEEN (1 << 9) -#define FIMD_WPAL_W0PAL_L 0x07 -#define FIMD_WPAL_W0PAL_L_SHT 0 -#define FIMD_WPAL_W1PAL_L 0x07 -#define FIMD_WPAL_W1PAL_L_SHT 3 -#define FIMD_WPAL_W2PAL_L 0x01 -#define FIMD_WPAL_W2PAL_L_SHT 6 -#define FIMD_WPAL_W2PAL_H 0x06 -#define FIMD_WPAL_W2PAL_H_SHT 8 -#define FIMD_WPAL_W3PAL_L 0x01 -#define FIMD_WPAL_W3PAL_L_SHT 7 -#define FIMD_WPAL_W3PAL_H 0x06 -#define FIMD_WPAL_W3PAL_H_SHT 12 -#define FIMD_WPAL_W4PAL_L 0x01 -#define FIMD_WPAL_W4PAL_L_SHT 8 -#define FIMD_WPAL_W4PAL_H 0x06 -#define FIMD_WPAL_W4PAL_H_SHT 16 - -/* Trigger control registers */ -#define FIMD_TRIGCON 0x01A4 -#define FIMD_TRIGCON_ROMASK 0x00000004 - -/* LCD I80 Interface Control */ -#define FIMD_I80IFCON_START 0x01B0 -#define FIMD_I80IFCON_END 0x01BC -/* Color gain control register */ -#define FIMD_COLORGAINCON 0x01C0 -/* LCD i80 Interface Command Control */ -#define FIMD_LDI_CMDCON0 0x01D0 -#define FIMD_LDI_CMDCON1 0x01D4 -/* I80 System Interface Manual Command Control */ -#define FIMD_SIFCCON0 0x01E0 -#define FIMD_SIFCCON2 0x01E8 - -/* Hue Control Registers */ -#define FIMD_HUECOEFCR_START 0x01EC -#define FIMD_HUECOEFCR_END 0x01F4 -#define FIMD_HUECOEFCB_START 0x01FC -#define FIMD_HUECOEFCB_END 0x0208 -#define FIMD_HUEOFFSET 0x020C - -/* Video interrupt control registers */ -#define FIMD_VIDINT_INTFIFOPEND (1 << 0) -#define FIMD_VIDINT_INTFRMPEND (1 << 1) -#define FIMD_VIDINT_INTI80PEND (1 << 2) -#define FIMD_VIDINT_INTEN (1 << 0) -#define FIMD_VIDINT_INTFIFOEN (1 << 1) -#define FIMD_VIDINT_INTFRMEN (1 << 12) -#define FIMD_VIDINT_I80IFDONE (1 << 17) - -/* Window blend equation control registers */ -#define FIMD_BLENDEQ_START 0x0244 -#define FIMD_BLENDEQ_END 0x0250 -#define FIMD_BLENDCON 0x0260 -#define FIMD_ALPHA_8BIT (1 << 0) -#define FIMD_BLENDEQ_COEF_MASK 0xF - -/* Window RTQOS Control Registers */ -#define FIMD_WRTQOSCON_START 0x0264 -#define FIMD_WRTQOSCON_END 0x0274 - -/* LCD I80 Interface Command */ -#define FIMD_I80IFCMD_START 0x0280 -#define FIMD_I80IFCMD_END 0x02AC - -/* Shadow windows control registers */ -#define FIMD_SHD_ADD0_START 0x40A0 -#define FIMD_SHD_ADD0_END 0x40C0 -#define FIMD_SHD_ADD1_START 0x40D0 -#define FIMD_SHD_ADD1_END 0x40F0 -#define FIMD_SHD_ADD2_START 0x4100 -#define FIMD_SHD_ADD2_END 0x4110 - -/* Palette memory */ -#define FIMD_PAL_MEM_START 0x2400 -#define FIMD_PAL_MEM_END 0x37FC -/* Palette memory aliases for windows 0 and 1 */ -#define FIMD_PALMEM_AL_START 0x0400 -#define FIMD_PALMEM_AL_END 0x0BFC - -typedef struct { - uint8_t r, g, b; - /* D[31..24]dummy, D[23..16]rAlpha, D[15..8]gAlpha, D[7..0]bAlpha */ - uint32_t a; -} rgba; -#define RGBA_SIZE 7 - -typedef void pixel_to_rgb_func(uint32_t pixel, rgba *p); -typedef struct Exynos4210fimdWindow Exynos4210fimdWindow; - -struct Exynos4210fimdWindow { - uint32_t wincon; /* Window control register */ - uint32_t buf_start[3]; /* Start address for video frame buffer */ - uint32_t buf_end[3]; /* End address for video frame buffer */ - uint32_t keycon[2]; /* Window color key registers */ - uint32_t keyalpha; /* Color key alpha control register */ - uint32_t winmap; /* Window color map register */ - uint32_t blendeq; /* Window blending equation control register */ - uint32_t rtqoscon; /* Window RTQOS Control Registers */ - uint32_t palette[256]; /* Palette RAM */ - uint32_t shadow_buf_start; /* Start address of shadow frame buffer */ - uint32_t shadow_buf_end; /* End address of shadow frame buffer */ - uint32_t shadow_buf_size; /* Virtual shadow screen width */ - - pixel_to_rgb_func *pixel_to_rgb; - void (*draw_line)(Exynos4210fimdWindow *w, uint8_t *src, uint8_t *dst, - bool blend); - uint32_t (*get_alpha)(Exynos4210fimdWindow *w, uint32_t pix_a); - uint16_t lefttop_x, lefttop_y; /* VIDOSD0 register */ - uint16_t rightbot_x, rightbot_y; /* VIDOSD1 register */ - uint32_t osdsize; /* VIDOSD2&3 register */ - uint32_t alpha_val[2]; /* VIDOSD2&3, VIDWALPHA registers */ - uint16_t virtpage_width; /* VIDWADD2 register */ - uint16_t virtpage_offsize; /* VIDWADD2 register */ - MemoryRegionSection mem_section; /* RAM fragment containing framebuffer */ - uint8_t *host_fb_addr; /* Host pointer to window's framebuffer */ - hwaddr fb_len; /* Framebuffer length */ -}; - -#define TYPE_EXYNOS4210_FIMD "exynos4210.fimd" -#define EXYNOS4210_FIMD(obj) \ - OBJECT_CHECK(Exynos4210fimdState, (obj), TYPE_EXYNOS4210_FIMD) - -typedef struct { - SysBusDevice parent_obj; - - MemoryRegion iomem; - QemuConsole *console; - qemu_irq irq[3]; - - uint32_t vidcon[4]; /* Video main control registers 0-3 */ - uint32_t vidtcon[4]; /* Video time control registers 0-3 */ - uint32_t shadowcon; /* Window shadow control register */ - uint32_t winchmap; /* Channel mapping control register */ - uint32_t vidintcon[2]; /* Video interrupt control registers */ - uint32_t dithmode; /* Dithering control register */ - uint32_t wpalcon[2]; /* Window palette control registers */ - uint32_t trigcon; /* Trigger control register */ - uint32_t i80ifcon[4]; /* I80 interface control registers */ - uint32_t colorgaincon; /* Color gain control register */ - uint32_t ldi_cmdcon[2]; /* LCD I80 interface command control */ - uint32_t sifccon[3]; /* I80 System Interface Manual Command Control */ - uint32_t huecoef_cr[4]; /* Hue control registers */ - uint32_t huecoef_cb[4]; /* Hue control registers */ - uint32_t hueoffset; /* Hue offset control register */ - uint32_t blendcon; /* Blending control register */ - uint32_t i80ifcmd[12]; /* LCD I80 Interface Command */ - - Exynos4210fimdWindow window[5]; /* Window-specific registers */ - uint8_t *ifb; /* Internal frame buffer */ - bool invalidate; /* Image needs to be redrawn */ - bool enabled; /* Display controller is enabled */ -} Exynos4210fimdState; - -/* Perform byte/halfword/word swap of data according to WINCON */ -static inline void fimd_swap_data(unsigned int swap_ctl, uint64_t *data) -{ - int i; - uint64_t res; - uint64_t x = *data; - - if (swap_ctl & FIMD_WINCON_SWAP_BITS) { - res = 0; - for (i = 0; i < 64; i++) { - if (x & (1ULL << (63 - i))) { - res |= (1ULL << i); - } - } - x = res; - } - - if (swap_ctl & FIMD_WINCON_SWAP_BYTE) { - x = bswap64(x); - } - - if (swap_ctl & FIMD_WINCON_SWAP_HWORD) { - x = ((x & 0x000000000000FFFFULL) << 48) | - ((x & 0x00000000FFFF0000ULL) << 16) | - ((x & 0x0000FFFF00000000ULL) >> 16) | - ((x & 0xFFFF000000000000ULL) >> 48); - } - - if (swap_ctl & FIMD_WINCON_SWAP_WORD) { - x = ((x & 0x00000000FFFFFFFFULL) << 32) | - ((x & 0xFFFFFFFF00000000ULL) >> 32); - } - - *data = x; -} - -/* Conversion routines of Pixel data from frame buffer area to internal RGBA - * pixel representation. - * Every color component internally represented as 8-bit value. If original - * data has less than 8 bit for component, data is extended to 8 bit. For - * example, if blue component has only two possible values 0 and 1 it will be - * extended to 0 and 0xFF */ - -/* One bit for alpha representation */ -#define DEF_PIXEL_TO_RGB_A1(N, R, G, B) \ -static void N(uint32_t pixel, rgba *p) \ -{ \ - p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \ - ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \ - pixel >>= (B); \ - p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \ - ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \ - pixel >>= (G); \ - p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \ - ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \ - pixel >>= (R); \ - p->a = (pixel & 0x1); \ -} - -DEF_PIXEL_TO_RGB_A1(pixel_a444_to_rgb, 4, 4, 4) -DEF_PIXEL_TO_RGB_A1(pixel_a555_to_rgb, 5, 5, 5) -DEF_PIXEL_TO_RGB_A1(pixel_a666_to_rgb, 6, 6, 6) -DEF_PIXEL_TO_RGB_A1(pixel_a665_to_rgb, 6, 6, 5) -DEF_PIXEL_TO_RGB_A1(pixel_a888_to_rgb, 8, 8, 8) -DEF_PIXEL_TO_RGB_A1(pixel_a887_to_rgb, 8, 8, 7) - -/* Alpha component is always zero */ -#define DEF_PIXEL_TO_RGB_A0(N, R, G, B) \ -static void N(uint32_t pixel, rgba *p) \ -{ \ - p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \ - ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \ - pixel >>= (B); \ - p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \ - ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \ - pixel >>= (G); \ - p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \ - ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \ - p->a = 0x0; \ -} - -DEF_PIXEL_TO_RGB_A0(pixel_565_to_rgb, 5, 6, 5) -DEF_PIXEL_TO_RGB_A0(pixel_555_to_rgb, 5, 5, 5) -DEF_PIXEL_TO_RGB_A0(pixel_666_to_rgb, 6, 6, 6) -DEF_PIXEL_TO_RGB_A0(pixel_888_to_rgb, 8, 8, 8) - -/* Alpha component has some meaningful value */ -#define DEF_PIXEL_TO_RGB_A(N, R, G, B, A) \ -static void N(uint32_t pixel, rgba *p) \ -{ \ - p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \ - ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \ - pixel >>= (B); \ - p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \ - ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \ - pixel >>= (G); \ - p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \ - ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \ - pixel >>= (R); \ - p->a = (pixel & ((1 << (A)) - 1)) << (8 - (A)) | \ - ((pixel >> (2 * (A) - 8)) & ((1 << (8 - (A))) - 1)); \ - p->a = p->a | (p->a << 8) | (p->a << 16); \ -} - -DEF_PIXEL_TO_RGB_A(pixel_4444_to_rgb, 4, 4, 4, 4) -DEF_PIXEL_TO_RGB_A(pixel_8888_to_rgb, 8, 8, 8, 8) - -/* Lookup table to extent 2-bit color component to 8 bit */ -static const uint8_t pixel_lutable_2b[4] = { - 0x0, 0x55, 0xAA, 0xFF -}; -/* Lookup table to extent 3-bit color component to 8 bit */ -static const uint8_t pixel_lutable_3b[8] = { - 0x0, 0x24, 0x49, 0x6D, 0x92, 0xB6, 0xDB, 0xFF -}; -/* Special case for a232 bpp mode */ -static void pixel_a232_to_rgb(uint32_t pixel, rgba *p) -{ - p->b = pixel_lutable_2b[(pixel & 0x3)]; - pixel >>= 2; - p->g = pixel_lutable_3b[(pixel & 0x7)]; - pixel >>= 3; - p->r = pixel_lutable_2b[(pixel & 0x3)]; - pixel >>= 2; - p->a = (pixel & 0x1); -} - -/* Special case for (5+1, 5+1, 5+1) mode. Data bit 15 is common LSB - * for all three color components */ -static void pixel_1555_to_rgb(uint32_t pixel, rgba *p) -{ - uint8_t comm = (pixel >> 15) & 1; - p->b = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3); - pixel >>= 5; - p->g = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3); - pixel >>= 5; - p->r = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3); - p->a = 0x0; -} - -/* Put/get pixel to/from internal LCD Controller framebuffer */ - -static int put_pixel_ifb(const rgba p, uint8_t *d) -{ - *(uint8_t *)d++ = p.r; - *(uint8_t *)d++ = p.g; - *(uint8_t *)d++ = p.b; - *(uint32_t *)d = p.a; - return RGBA_SIZE; -} - -static int get_pixel_ifb(const uint8_t *s, rgba *p) -{ - p->r = *(uint8_t *)s++; - p->g = *(uint8_t *)s++; - p->b = *(uint8_t *)s++; - p->a = (*(uint32_t *)s) & 0x00FFFFFF; - return RGBA_SIZE; -} - -static pixel_to_rgb_func *palette_data_format[8] = { - [0] = pixel_565_to_rgb, - [1] = pixel_a555_to_rgb, - [2] = pixel_666_to_rgb, - [3] = pixel_a665_to_rgb, - [4] = pixel_a666_to_rgb, - [5] = pixel_888_to_rgb, - [6] = pixel_a888_to_rgb, - [7] = pixel_8888_to_rgb -}; - -/* Returns Index in palette data formats table for given window number WINDOW */ -static uint32_t -exynos4210_fimd_palette_format(Exynos4210fimdState *s, int window) -{ - uint32_t ret; - - switch (window) { - case 0: - ret = (s->wpalcon[1] >> FIMD_WPAL_W0PAL_L_SHT) & FIMD_WPAL_W0PAL_L; - if (ret != 7) { - ret = 6 - ret; - } - break; - case 1: - ret = (s->wpalcon[1] >> FIMD_WPAL_W1PAL_L_SHT) & FIMD_WPAL_W1PAL_L; - if (ret != 7) { - ret = 6 - ret; - } - break; - case 2: - ret = ((s->wpalcon[0] >> FIMD_WPAL_W2PAL_H_SHT) & FIMD_WPAL_W2PAL_H) | - ((s->wpalcon[1] >> FIMD_WPAL_W2PAL_L_SHT) & FIMD_WPAL_W2PAL_L); - break; - case 3: - ret = ((s->wpalcon[0] >> FIMD_WPAL_W3PAL_H_SHT) & FIMD_WPAL_W3PAL_H) | - ((s->wpalcon[1] >> FIMD_WPAL_W3PAL_L_SHT) & FIMD_WPAL_W3PAL_L); - break; - case 4: - ret = ((s->wpalcon[0] >> FIMD_WPAL_W4PAL_H_SHT) & FIMD_WPAL_W4PAL_H) | - ((s->wpalcon[1] >> FIMD_WPAL_W4PAL_L_SHT) & FIMD_WPAL_W4PAL_L); - break; - default: - hw_error("exynos4210.fimd: incorrect window number %d\n", window); - ret = 0; - break; - } - return ret; -} - -#define FIMD_1_MINUS_COLOR(x) \ - ((0xFF - ((x) & 0xFF)) | (0xFF00 - ((x) & 0xFF00)) | \ - (0xFF0000 - ((x) & 0xFF0000))) -#define EXTEND_LOWER_HALFBYTE(x) (((x) & 0xF0F0F) | (((x) << 4) & 0xF0F0F0)) -#define EXTEND_UPPER_HALFBYTE(x) (((x) & 0xF0F0F0) | (((x) >> 4) & 0xF0F0F)) - -/* Multiply three lower bytes of two 32-bit words with each other. - * Each byte with values 0-255 is considered as a number with possible values - * in a range [0 - 1] */ -static inline uint32_t fimd_mult_each_byte(uint32_t a, uint32_t b) -{ - uint32_t tmp; - uint32_t ret; - - ret = ((tmp = (((a & 0xFF) * (b & 0xFF)) / 0xFF)) > 0xFF) ? 0xFF : tmp; - ret |= ((tmp = ((((a >> 8) & 0xFF) * ((b >> 8) & 0xFF)) / 0xFF)) > 0xFF) ? - 0xFF00 : tmp << 8; - ret |= ((tmp = ((((a >> 16) & 0xFF) * ((b >> 16) & 0xFF)) / 0xFF)) > 0xFF) ? - 0xFF0000 : tmp << 16; - return ret; -} - -/* For each corresponding bytes of two 32-bit words: (a*b + c*d) - * Byte values 0-255 are mapped to a range [0 .. 1] */ -static inline uint32_t -fimd_mult_and_sum_each_byte(uint32_t a, uint32_t b, uint32_t c, uint32_t d) -{ - uint32_t tmp; - uint32_t ret; - - ret = ((tmp = (((a & 0xFF) * (b & 0xFF) + (c & 0xFF) * (d & 0xFF)) / 0xFF)) - > 0xFF) ? 0xFF : tmp; - ret |= ((tmp = ((((a >> 8) & 0xFF) * ((b >> 8) & 0xFF) + ((c >> 8) & 0xFF) * - ((d >> 8) & 0xFF)) / 0xFF)) > 0xFF) ? 0xFF00 : tmp << 8; - ret |= ((tmp = ((((a >> 16) & 0xFF) * ((b >> 16) & 0xFF) + - ((c >> 16) & 0xFF) * ((d >> 16) & 0xFF)) / 0xFF)) > 0xFF) ? - 0xFF0000 : tmp << 16; - return ret; -} - -/* These routines cover all possible sources of window's transparent factor - * used in blending equation. Choice of routine is affected by WPALCON - * registers, BLENDCON register and window's WINCON register */ - -static uint32_t fimd_get_alpha_pix(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return pix_a; -} - -static uint32_t -fimd_get_alpha_pix_extlow(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return EXTEND_LOWER_HALFBYTE(pix_a); -} - -static uint32_t -fimd_get_alpha_pix_exthigh(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return EXTEND_UPPER_HALFBYTE(pix_a); -} - -static uint32_t fimd_get_alpha_mult(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return fimd_mult_each_byte(pix_a, w->alpha_val[0]); -} - -static uint32_t fimd_get_alpha_mult_ext(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return fimd_mult_each_byte(EXTEND_LOWER_HALFBYTE(pix_a), - EXTEND_UPPER_HALFBYTE(w->alpha_val[0])); -} - -static uint32_t fimd_get_alpha_aen(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return w->alpha_val[pix_a]; -} - -static uint32_t fimd_get_alpha_aen_ext(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return EXTEND_UPPER_HALFBYTE(w->alpha_val[pix_a]); -} - -static uint32_t fimd_get_alpha_sel(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return w->alpha_val[(w->wincon & FIMD_WINCON_ALPHA_SEL) ? 1 : 0]; -} - -static uint32_t fimd_get_alpha_sel_ext(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return EXTEND_UPPER_HALFBYTE(w->alpha_val[(w->wincon & - FIMD_WINCON_ALPHA_SEL) ? 1 : 0]); -} - -/* Updates currently active alpha value get function for specified window */ -static void fimd_update_get_alpha(Exynos4210fimdState *s, int win) -{ - Exynos4210fimdWindow *w = &s->window[win]; - const bool alpha_is_8bit = s->blendcon & FIMD_ALPHA_8BIT; - - if (w->wincon & FIMD_WINCON_BLD_PIX) { - if ((w->wincon & FIMD_WINCON_ALPHA_SEL) && WIN_BPP_MODE_WITH_ALPHA(w)) { - /* In this case, alpha component contains meaningful value */ - if (w->wincon & FIMD_WINCON_ALPHA_MUL) { - w->get_alpha = alpha_is_8bit ? - fimd_get_alpha_mult : fimd_get_alpha_mult_ext; - } else { - w->get_alpha = alpha_is_8bit ? - fimd_get_alpha_pix : fimd_get_alpha_pix_extlow; - } - } else { - if (IS_PALETTIZED_MODE(w) && - PAL_MODE_WITH_ALPHA(exynos4210_fimd_palette_format(s, win))) { - /* Alpha component has 8-bit numeric value */ - w->get_alpha = alpha_is_8bit ? - fimd_get_alpha_pix : fimd_get_alpha_pix_exthigh; - } else { - /* Alpha has only two possible values (AEN) */ - w->get_alpha = alpha_is_8bit ? - fimd_get_alpha_aen : fimd_get_alpha_aen_ext; - } - } - } else { - w->get_alpha = alpha_is_8bit ? fimd_get_alpha_sel : - fimd_get_alpha_sel_ext; - } -} - -/* Blends current window's (w) pixel (foreground pixel *ret) with background - * window (w_blend) pixel p_bg according to formula: - * NEW_COLOR = a_coef x FG_PIXEL_COLOR + b_coef x BG_PIXEL_COLOR - * NEW_ALPHA = p_coef x FG_ALPHA + q_coef x BG_ALPHA - */ -static void -exynos4210_fimd_blend_pixel(Exynos4210fimdWindow *w, rgba p_bg, rgba *ret) -{ - rgba p_fg = *ret; - uint32_t bg_color = ((p_bg.r & 0xFF) << 16) | ((p_bg.g & 0xFF) << 8) | - (p_bg.b & 0xFF); - uint32_t fg_color = ((p_fg.r & 0xFF) << 16) | ((p_fg.g & 0xFF) << 8) | - (p_fg.b & 0xFF); - uint32_t alpha_fg = p_fg.a; - int i; - /* It is possible that blending equation parameters a and b do not - * depend on window BLENEQ register. Account for this with first_coef */ - enum { A_COEF = 0, B_COEF = 1, P_COEF = 2, Q_COEF = 3, COEF_NUM = 4}; - uint32_t first_coef = A_COEF; - uint32_t blend_param[COEF_NUM]; - - if (w->keycon[0] & FIMD_WKEYCON0_KEYEN) { - uint32_t colorkey = (w->keycon[1] & - ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) & FIMD_WKEYCON0_COMPKEY; - - if ((w->keycon[0] & FIMD_WKEYCON0_DIRCON) && - (bg_color & ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) == colorkey) { - /* Foreground pixel is displayed */ - if (w->keycon[0] & FIMD_WKEYCON0_KEYBLEN) { - alpha_fg = w->keyalpha; - blend_param[A_COEF] = alpha_fg; - blend_param[B_COEF] = FIMD_1_MINUS_COLOR(alpha_fg); - } else { - alpha_fg = 0; - blend_param[A_COEF] = 0xFFFFFF; - blend_param[B_COEF] = 0x0; - } - first_coef = P_COEF; - } else if ((w->keycon[0] & FIMD_WKEYCON0_DIRCON) == 0 && - (fg_color & ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) == colorkey) { - /* Background pixel is displayed */ - if (w->keycon[0] & FIMD_WKEYCON0_KEYBLEN) { - alpha_fg = w->keyalpha; - blend_param[A_COEF] = alpha_fg; - blend_param[B_COEF] = FIMD_1_MINUS_COLOR(alpha_fg); - } else { - alpha_fg = 0; - blend_param[A_COEF] = 0x0; - blend_param[B_COEF] = 0xFFFFFF; - } - first_coef = P_COEF; - } - } - - for (i = first_coef; i < COEF_NUM; i++) { - switch ((w->blendeq >> i * 6) & FIMD_BLENDEQ_COEF_MASK) { - case 0: - blend_param[i] = 0; - break; - case 1: - blend_param[i] = 0xFFFFFF; - break; - case 2: - blend_param[i] = alpha_fg; - break; - case 3: - blend_param[i] = FIMD_1_MINUS_COLOR(alpha_fg); - break; - case 4: - blend_param[i] = p_bg.a; - break; - case 5: - blend_param[i] = FIMD_1_MINUS_COLOR(p_bg.a); - break; - case 6: - blend_param[i] = w->alpha_val[0]; - break; - case 10: - blend_param[i] = fg_color; - break; - case 11: - blend_param[i] = FIMD_1_MINUS_COLOR(fg_color); - break; - case 12: - blend_param[i] = bg_color; - break; - case 13: - blend_param[i] = FIMD_1_MINUS_COLOR(bg_color); - break; - default: - hw_error("exynos4210.fimd: blend equation coef illegal value\n"); - break; - } - } - - fg_color = fimd_mult_and_sum_each_byte(bg_color, blend_param[B_COEF], - fg_color, blend_param[A_COEF]); - ret->b = fg_color & 0xFF; - fg_color >>= 8; - ret->g = fg_color & 0xFF; - fg_color >>= 8; - ret->r = fg_color & 0xFF; - ret->a = fimd_mult_and_sum_each_byte(alpha_fg, blend_param[P_COEF], - p_bg.a, blend_param[Q_COEF]); -} - -/* These routines read data from video frame buffer in system RAM, convert - * this data to display controller internal representation, if necessary, - * perform pixel blending with data, currently presented in internal buffer. - * Result is stored in display controller internal frame buffer. */ - -/* Draw line with index in palette table in RAM frame buffer data */ -#define DEF_DRAW_LINE_PALETTE(N) \ -static void glue(draw_line_palette_, N)(Exynos4210fimdWindow *w, uint8_t *src, \ - uint8_t *dst, bool blend) \ -{ \ - int width = w->rightbot_x - w->lefttop_x + 1; \ - uint8_t *ifb = dst; \ - uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \ - uint64_t data; \ - rgba p, p_old; \ - int i; \ - do { \ - memcpy(&data, src, sizeof(data)); \ - src += 8; \ - fimd_swap_data(swap, &data); \ - for (i = (64 / (N) - 1); i >= 0; i--) { \ - w->pixel_to_rgb(w->palette[(data >> ((N) * i)) & \ - ((1ULL << (N)) - 1)], &p); \ - p.a = w->get_alpha(w, p.a); \ - if (blend) { \ - ifb += get_pixel_ifb(ifb, &p_old); \ - exynos4210_fimd_blend_pixel(w, p_old, &p); \ - } \ - dst += put_pixel_ifb(p, dst); \ - } \ - width -= (64 / (N)); \ - } while (width > 0); \ -} - -/* Draw line with direct color value in RAM frame buffer data */ -#define DEF_DRAW_LINE_NOPALETTE(N) \ -static void glue(draw_line_, N)(Exynos4210fimdWindow *w, uint8_t *src, \ - uint8_t *dst, bool blend) \ -{ \ - int width = w->rightbot_x - w->lefttop_x + 1; \ - uint8_t *ifb = dst; \ - uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \ - uint64_t data; \ - rgba p, p_old; \ - int i; \ - do { \ - memcpy(&data, src, sizeof(data)); \ - src += 8; \ - fimd_swap_data(swap, &data); \ - for (i = (64 / (N) - 1); i >= 0; i--) { \ - w->pixel_to_rgb((data >> ((N) * i)) & ((1ULL << (N)) - 1), &p); \ - p.a = w->get_alpha(w, p.a); \ - if (blend) { \ - ifb += get_pixel_ifb(ifb, &p_old); \ - exynos4210_fimd_blend_pixel(w, p_old, &p); \ - } \ - dst += put_pixel_ifb(p, dst); \ - } \ - width -= (64 / (N)); \ - } while (width > 0); \ -} - -DEF_DRAW_LINE_PALETTE(1) -DEF_DRAW_LINE_PALETTE(2) -DEF_DRAW_LINE_PALETTE(4) -DEF_DRAW_LINE_PALETTE(8) -DEF_DRAW_LINE_NOPALETTE(8) /* 8bpp mode has palette and non-palette versions */ -DEF_DRAW_LINE_NOPALETTE(16) -DEF_DRAW_LINE_NOPALETTE(32) - -/* Special draw line routine for window color map case */ -static void draw_line_mapcolor(Exynos4210fimdWindow *w, uint8_t *src, - uint8_t *dst, bool blend) -{ - rgba p, p_old; - uint8_t *ifb = dst; - int width = w->rightbot_x - w->lefttop_x + 1; - uint32_t map_color = w->winmap & FIMD_WINMAP_COLOR_MASK; - - do { - pixel_888_to_rgb(map_color, &p); - p.a = w->get_alpha(w, p.a); - if (blend) { - ifb += get_pixel_ifb(ifb, &p_old); - exynos4210_fimd_blend_pixel(w, p_old, &p); - } - dst += put_pixel_ifb(p, dst); - } while (--width); -} - -/* Write RGB to QEMU's GraphicConsole framebuffer */ - -static int put_to_qemufb_pixel8(const rgba p, uint8_t *d) -{ - uint32_t pixel = rgb_to_pixel8(p.r, p.g, p.b); - *(uint8_t *)d = pixel; - return 1; -} - -static int put_to_qemufb_pixel15(const rgba p, uint8_t *d) -{ - uint32_t pixel = rgb_to_pixel15(p.r, p.g, p.b); - *(uint16_t *)d = pixel; - return 2; -} - -static int put_to_qemufb_pixel16(const rgba p, uint8_t *d) -{ - uint32_t pixel = rgb_to_pixel16(p.r, p.g, p.b); - *(uint16_t *)d = pixel; - return 2; -} - -static int put_to_qemufb_pixel24(const rgba p, uint8_t *d) -{ - uint32_t pixel = rgb_to_pixel24(p.r, p.g, p.b); - *(uint8_t *)d++ = (pixel >> 0) & 0xFF; - *(uint8_t *)d++ = (pixel >> 8) & 0xFF; - *(uint8_t *)d++ = (pixel >> 16) & 0xFF; - return 3; -} - -static int put_to_qemufb_pixel32(const rgba p, uint8_t *d) -{ - uint32_t pixel = rgb_to_pixel24(p.r, p.g, p.b); - *(uint32_t *)d = pixel; - return 4; -} - -/* Routine to copy pixel from internal buffer to QEMU buffer */ -static int (*put_pixel_toqemu)(const rgba p, uint8_t *pixel); -static inline void fimd_update_putpix_qemu(int bpp) -{ - switch (bpp) { - case 8: - put_pixel_toqemu = put_to_qemufb_pixel8; - break; - case 15: - put_pixel_toqemu = put_to_qemufb_pixel15; - break; - case 16: - put_pixel_toqemu = put_to_qemufb_pixel16; - break; - case 24: - put_pixel_toqemu = put_to_qemufb_pixel24; - break; - case 32: - put_pixel_toqemu = put_to_qemufb_pixel32; - break; - default: - hw_error("exynos4210.fimd: unsupported BPP (%d)", bpp); - break; - } -} - -/* Routine to copy a line from internal frame buffer to QEMU display */ -static void fimd_copy_line_toqemu(int width, uint8_t *src, uint8_t *dst) -{ - rgba p; - - do { - src += get_pixel_ifb(src, &p); - dst += put_pixel_toqemu(p, dst); - } while (--width); -} - -/* Parse BPPMODE_F = WINCON1[5:2] bits */ -static void exynos4210_fimd_update_win_bppmode(Exynos4210fimdState *s, int win) -{ - Exynos4210fimdWindow *w = &s->window[win]; - - if (w->winmap & FIMD_WINMAP_EN) { - w->draw_line = draw_line_mapcolor; - return; - } - - switch (WIN_BPP_MODE(w)) { - case 0: - w->draw_line = draw_line_palette_1; - w->pixel_to_rgb = - palette_data_format[exynos4210_fimd_palette_format(s, win)]; - break; - case 1: - w->draw_line = draw_line_palette_2; - w->pixel_to_rgb = - palette_data_format[exynos4210_fimd_palette_format(s, win)]; - break; - case 2: - w->draw_line = draw_line_palette_4; - w->pixel_to_rgb = - palette_data_format[exynos4210_fimd_palette_format(s, win)]; - break; - case 3: - w->draw_line = draw_line_palette_8; - w->pixel_to_rgb = - palette_data_format[exynos4210_fimd_palette_format(s, win)]; - break; - case 4: - w->draw_line = draw_line_8; - w->pixel_to_rgb = pixel_a232_to_rgb; - break; - case 5: - w->draw_line = draw_line_16; - w->pixel_to_rgb = pixel_565_to_rgb; - break; - case 6: - w->draw_line = draw_line_16; - w->pixel_to_rgb = pixel_a555_to_rgb; - break; - case 7: - w->draw_line = draw_line_16; - w->pixel_to_rgb = pixel_1555_to_rgb; - break; - case 8: - w->draw_line = draw_line_32; - w->pixel_to_rgb = pixel_666_to_rgb; - break; - case 9: - w->draw_line = draw_line_32; - w->pixel_to_rgb = pixel_a665_to_rgb; - break; - case 10: - w->draw_line = draw_line_32; - w->pixel_to_rgb = pixel_a666_to_rgb; - break; - case 11: - w->draw_line = draw_line_32; - w->pixel_to_rgb = pixel_888_to_rgb; - break; - case 12: - w->draw_line = draw_line_32; - w->pixel_to_rgb = pixel_a887_to_rgb; - break; - case 13: - w->draw_line = draw_line_32; - if ((w->wincon & FIMD_WINCON_BLD_PIX) && (w->wincon & - FIMD_WINCON_ALPHA_SEL)) { - w->pixel_to_rgb = pixel_8888_to_rgb; - } else { - w->pixel_to_rgb = pixel_a888_to_rgb; - } - break; - case 14: - w->draw_line = draw_line_16; - if ((w->wincon & FIMD_WINCON_BLD_PIX) && (w->wincon & - FIMD_WINCON_ALPHA_SEL)) { - w->pixel_to_rgb = pixel_4444_to_rgb; - } else { - w->pixel_to_rgb = pixel_a444_to_rgb; - } - break; - case 15: - w->draw_line = draw_line_16; - w->pixel_to_rgb = pixel_555_to_rgb; - break; - } -} - -#if EXYNOS4210_FIMD_MODE_TRACE > 0 -static const char *exynos4210_fimd_get_bppmode(int mode_code) -{ - switch (mode_code) { - case 0: - return "1 bpp"; - case 1: - return "2 bpp"; - case 2: - return "4 bpp"; - case 3: - return "8 bpp (palettized)"; - case 4: - return "8 bpp (non-palettized, A: 1-R:2-G:3-B:2)"; - case 5: - return "16 bpp (non-palettized, R:5-G:6-B:5)"; - case 6: - return "16 bpp (non-palettized, A:1-R:5-G:5-B:5)"; - case 7: - return "16 bpp (non-palettized, I :1-R:5-G:5-B:5)"; - case 8: - return "Unpacked 18 bpp (non-palettized, R:6-G:6-B:6)"; - case 9: - return "Unpacked 18bpp (non-palettized,A:1-R:6-G:6-B:5)"; - case 10: - return "Unpacked 19bpp (non-palettized,A:1-R:6-G:6-B:6)"; - case 11: - return "Unpacked 24 bpp (non-palettized R:8-G:8-B:8)"; - case 12: - return "Unpacked 24 bpp (non-palettized A:1-R:8-G:8-B:7)"; - case 13: - return "Unpacked 25 bpp (non-palettized A:1-R:8-G:8-B:8)"; - case 14: - return "Unpacked 13 bpp (non-palettized A:1-R:4-G:4-B:4)"; - case 15: - return "Unpacked 15 bpp (non-palettized R:5-G:5-B:5)"; - default: - return "Non-existing bpp mode"; - } -} - -static inline void exynos4210_fimd_trace_bppmode(Exynos4210fimdState *s, - int win_num, uint32_t val) -{ - Exynos4210fimdWindow *w = &s->window[win_num]; - - if (w->winmap & FIMD_WINMAP_EN) { - printf("QEMU FIMD: Window %d is mapped with MAPCOLOR=0x%x\n", - win_num, w->winmap & 0xFFFFFF); - return; - } - - if ((val != 0xFFFFFFFF) && ((w->wincon >> 2) & 0xF) == ((val >> 2) & 0xF)) { - return; - } - printf("QEMU FIMD: Window %d BPP mode set to %s\n", win_num, - exynos4210_fimd_get_bppmode((val >> 2) & 0xF)); -} -#else -static inline void exynos4210_fimd_trace_bppmode(Exynos4210fimdState *s, - int win_num, uint32_t val) -{ - -} -#endif - -static inline int fimd_get_buffer_id(Exynos4210fimdWindow *w) -{ - switch (w->wincon & FIMD_WINCON_BUFSTATUS) { - case FIMD_WINCON_BUF0_STAT: - return 0; - case FIMD_WINCON_BUF1_STAT: - return 1; - case FIMD_WINCON_BUF2_STAT: - return 2; - default: - DPRINT_ERROR("Non-existent buffer index\n"); - return 0; - } -} - -static void exynos4210_fimd_invalidate(void *opaque) -{ - Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; - s->invalidate = true; -} - -/* Updates specified window's MemorySection based on values of WINCON, - * VIDOSDA, VIDOSDB, VIDWADDx and SHADOWCON registers */ -static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(s); - Exynos4210fimdWindow *w = &s->window[win]; - hwaddr fb_start_addr, fb_mapped_len; - - if (!s->enabled || !(w->wincon & FIMD_WINCON_ENWIN) || - FIMD_WINDOW_PROTECTED(s->shadowcon, win)) { - return; - } - - if (w->host_fb_addr) { - cpu_physical_memory_unmap(w->host_fb_addr, w->fb_len, 0, 0); - w->host_fb_addr = NULL; - w->fb_len = 0; - } - - fb_start_addr = w->buf_start[fimd_get_buffer_id(w)]; - /* Total number of bytes of virtual screen used by current window */ - w->fb_len = fb_mapped_len = (w->virtpage_width + w->virtpage_offsize) * - (w->rightbot_y - w->lefttop_y + 1); - - /* TODO: add .exit and unref the region there. Not needed yet since sysbus - * does not support hot-unplug. - */ - if (w->mem_section.mr) { - memory_region_set_log(w->mem_section.mr, false, DIRTY_MEMORY_VGA); - memory_region_unref(w->mem_section.mr); - } - - w->mem_section = memory_region_find(sysbus_address_space(sbd), - fb_start_addr, w->fb_len); - assert(w->mem_section.mr); - assert(w->mem_section.offset_within_address_space == fb_start_addr); - DPRINT_TRACE("Window %u framebuffer changed: address=0x%08x, len=0x%x\n", - win, fb_start_addr, w->fb_len); - - if (int128_get64(w->mem_section.size) != w->fb_len || - !memory_region_is_ram(w->mem_section.mr)) { - DPRINT_ERROR("Failed to find window %u framebuffer region\n", win); - goto error_return; - } - - w->host_fb_addr = cpu_physical_memory_map(fb_start_addr, &fb_mapped_len, 0); - if (!w->host_fb_addr) { - DPRINT_ERROR("Failed to map window %u framebuffer\n", win); - goto error_return; - } - - if (fb_mapped_len != w->fb_len) { - DPRINT_ERROR("Window %u mapped framebuffer length is less then " - "expected\n", win); - cpu_physical_memory_unmap(w->host_fb_addr, fb_mapped_len, 0, 0); - goto error_return; - } - memory_region_set_log(w->mem_section.mr, true, DIRTY_MEMORY_VGA); - exynos4210_fimd_invalidate(s); - return; - -error_return: - memory_region_unref(w->mem_section.mr); - w->mem_section.mr = NULL; - w->mem_section.size = int128_zero(); - w->host_fb_addr = NULL; - w->fb_len = 0; -} - -static void exynos4210_fimd_enable(Exynos4210fimdState *s, bool enabled) -{ - if (enabled && !s->enabled) { - unsigned w; - s->enabled = true; - for (w = 0; w < NUM_OF_WINDOWS; w++) { - fimd_update_memory_section(s, w); - } - } - s->enabled = enabled; - DPRINT_TRACE("display controller %s\n", enabled ? "enabled" : "disabled"); -} - -static inline uint32_t unpack_upper_4(uint32_t x) -{ - return ((x & 0xF00) << 12) | ((x & 0xF0) << 8) | ((x & 0xF) << 4); -} - -static inline uint32_t pack_upper_4(uint32_t x) -{ - return (((x & 0xF00000) >> 12) | ((x & 0xF000) >> 8) | - ((x & 0xF0) >> 4)) & 0xFFF; -} - -static void exynos4210_fimd_update_irq(Exynos4210fimdState *s) -{ - if (!(s->vidintcon[0] & FIMD_VIDINT_INTEN)) { - qemu_irq_lower(s->irq[0]); - qemu_irq_lower(s->irq[1]); - qemu_irq_lower(s->irq[2]); - return; - } - if ((s->vidintcon[0] & FIMD_VIDINT_INTFIFOEN) && - (s->vidintcon[1] & FIMD_VIDINT_INTFIFOPEND)) { - qemu_irq_raise(s->irq[0]); - } else { - qemu_irq_lower(s->irq[0]); - } - if ((s->vidintcon[0] & FIMD_VIDINT_INTFRMEN) && - (s->vidintcon[1] & FIMD_VIDINT_INTFRMPEND)) { - qemu_irq_raise(s->irq[1]); - } else { - qemu_irq_lower(s->irq[1]); - } - if ((s->vidintcon[0] & FIMD_VIDINT_I80IFDONE) && - (s->vidintcon[1] & FIMD_VIDINT_INTI80PEND)) { - qemu_irq_raise(s->irq[2]); - } else { - qemu_irq_lower(s->irq[2]); - } -} - -static void exynos4210_update_resolution(Exynos4210fimdState *s) -{ - DisplaySurface *surface = qemu_console_surface(s->console); - - /* LCD resolution is stored in VIDEO TIME CONTROL REGISTER 2 */ - uint32_t width = ((s->vidtcon[2] >> FIMD_VIDTCON2_HOR_SHIFT) & - FIMD_VIDTCON2_SIZE_MASK) + 1; - uint32_t height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) & - FIMD_VIDTCON2_SIZE_MASK) + 1; - - if (s->ifb == NULL || surface_width(surface) != width || - surface_height(surface) != height) { - DPRINT_L1("Resolution changed from %ux%u to %ux%u\n", - surface_width(surface), surface_height(surface), width, height); - qemu_console_resize(s->console, width, height); - s->ifb = g_realloc(s->ifb, width * height * RGBA_SIZE + 1); - memset(s->ifb, 0, width * height * RGBA_SIZE + 1); - exynos4210_fimd_invalidate(s); - } -} - -static void exynos4210_fimd_update(void *opaque) -{ - Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; - DisplaySurface *surface; - Exynos4210fimdWindow *w; - int i, line; - hwaddr fb_line_addr, inc_size; - int scrn_height; - int first_line = -1, last_line = -1, scrn_width; - bool blend = false; - uint8_t *host_fb_addr; - bool is_dirty = false; - const int global_width = (s->vidtcon[2] & FIMD_VIDTCON2_SIZE_MASK) + 1; - const int global_height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) & - FIMD_VIDTCON2_SIZE_MASK) + 1; - - if (!s || !s->console || !s->enabled || - surface_bits_per_pixel(qemu_console_surface(s->console)) == 0) { - return; - } - exynos4210_update_resolution(s); - surface = qemu_console_surface(s->console); - - for (i = 0; i < NUM_OF_WINDOWS; i++) { - w = &s->window[i]; - if ((w->wincon & FIMD_WINCON_ENWIN) && w->host_fb_addr) { - scrn_height = w->rightbot_y - w->lefttop_y + 1; - scrn_width = w->virtpage_width; - /* Total width of virtual screen page in bytes */ - inc_size = scrn_width + w->virtpage_offsize; - memory_region_sync_dirty_bitmap(w->mem_section.mr); - host_fb_addr = w->host_fb_addr; - fb_line_addr = w->mem_section.offset_within_region; - - for (line = 0; line < scrn_height; line++) { - is_dirty = memory_region_get_dirty(w->mem_section.mr, - fb_line_addr, scrn_width, DIRTY_MEMORY_VGA); - - if (s->invalidate || is_dirty) { - if (first_line == -1) { - first_line = line; - } - last_line = line; - w->draw_line(w, host_fb_addr, s->ifb + - w->lefttop_x * RGBA_SIZE + (w->lefttop_y + line) * - global_width * RGBA_SIZE, blend); - } - host_fb_addr += inc_size; - fb_line_addr += inc_size; - is_dirty = false; - } - memory_region_reset_dirty(w->mem_section.mr, - w->mem_section.offset_within_region, - w->fb_len, DIRTY_MEMORY_VGA); - blend = true; - } - } - - /* Copy resulting image to QEMU_CONSOLE. */ - if (first_line >= 0) { - uint8_t *d; - int bpp; - - bpp = surface_bits_per_pixel(surface); - fimd_update_putpix_qemu(bpp); - bpp = (bpp + 1) >> 3; - d = surface_data(surface); - for (line = first_line; line <= last_line; line++) { - fimd_copy_line_toqemu(global_width, s->ifb + global_width * line * - RGBA_SIZE, d + global_width * line * bpp); - } - dpy_gfx_update(s->console, 0, 0, global_width, global_height); - } - s->invalidate = false; - s->vidintcon[1] |= FIMD_VIDINT_INTFRMPEND; - if ((s->vidcon[0] & FIMD_VIDCON0_ENVID_F) == 0) { - exynos4210_fimd_enable(s, false); - } - exynos4210_fimd_update_irq(s); -} - -static void exynos4210_fimd_reset(DeviceState *d) -{ - Exynos4210fimdState *s = EXYNOS4210_FIMD(d); - unsigned w; - - DPRINT_TRACE("Display controller reset\n"); - /* Set all display controller registers to 0 */ - memset(&s->vidcon, 0, (uint8_t *)&s->window - (uint8_t *)&s->vidcon); - for (w = 0; w < NUM_OF_WINDOWS; w++) { - memset(&s->window[w], 0, sizeof(Exynos4210fimdWindow)); - s->window[w].blendeq = 0xC2; - exynos4210_fimd_update_win_bppmode(s, w); - exynos4210_fimd_trace_bppmode(s, w, 0xFFFFFFFF); - fimd_update_get_alpha(s, w); - } - - g_free(s->ifb); - s->ifb = NULL; - - exynos4210_fimd_invalidate(s); - exynos4210_fimd_enable(s, false); - /* Some registers have non-zero initial values */ - s->winchmap = 0x7D517D51; - s->colorgaincon = 0x10040100; - s->huecoef_cr[0] = s->huecoef_cr[3] = 0x01000100; - s->huecoef_cb[0] = s->huecoef_cb[3] = 0x01000100; - s->hueoffset = 0x01800080; -} - -static void exynos4210_fimd_write(void *opaque, hwaddr offset, - uint64_t val, unsigned size) -{ - Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; - unsigned w, i; - uint32_t old_value; - - DPRINT_L2("write offset 0x%08x, value=%llu(0x%08llx)\n", offset, - (long long unsigned int)val, (long long unsigned int)val); - - switch (offset) { - case FIMD_VIDCON0: - if ((val & FIMD_VIDCON0_ENVID_MASK) == FIMD_VIDCON0_ENVID_MASK) { - exynos4210_fimd_enable(s, true); - } else { - if ((val & FIMD_VIDCON0_ENVID) == 0) { - exynos4210_fimd_enable(s, false); - } - } - s->vidcon[0] = val; - break; - case FIMD_VIDCON1: - /* Leave read-only bits as is */ - val = (val & (~FIMD_VIDCON1_ROMASK)) | - (s->vidcon[1] & FIMD_VIDCON1_ROMASK); - s->vidcon[1] = val; - break; - case FIMD_VIDCON2 ... FIMD_VIDCON3: - s->vidcon[(offset) >> 2] = val; - break; - case FIMD_VIDTCON_START ... FIMD_VIDTCON_END: - s->vidtcon[(offset - FIMD_VIDTCON_START) >> 2] = val; - break; - case FIMD_WINCON_START ... FIMD_WINCON_END: - w = (offset - FIMD_WINCON_START) >> 2; - /* Window's current buffer ID */ - i = fimd_get_buffer_id(&s->window[w]); - old_value = s->window[w].wincon; - val = (val & ~FIMD_WINCON_ROMASK) | - (s->window[w].wincon & FIMD_WINCON_ROMASK); - if (w == 0) { - /* Window 0 wincon ALPHA_MUL bit must always be 0 */ - val &= ~FIMD_WINCON_ALPHA_MUL; - } - exynos4210_fimd_trace_bppmode(s, w, val); - switch (val & FIMD_WINCON_BUFSELECT) { - case FIMD_WINCON_BUF0_SEL: - val &= ~FIMD_WINCON_BUFSTATUS; - break; - case FIMD_WINCON_BUF1_SEL: - val = (val & ~FIMD_WINCON_BUFSTAT_H) | FIMD_WINCON_BUFSTAT_L; - break; - case FIMD_WINCON_BUF2_SEL: - if (val & FIMD_WINCON_BUFMODE) { - val = (val & ~FIMD_WINCON_BUFSTAT_L) | FIMD_WINCON_BUFSTAT_H; - } - break; - default: - break; - } - s->window[w].wincon = val; - exynos4210_fimd_update_win_bppmode(s, w); - fimd_update_get_alpha(s, w); - if ((i != fimd_get_buffer_id(&s->window[w])) || - (!(old_value & FIMD_WINCON_ENWIN) && (s->window[w].wincon & - FIMD_WINCON_ENWIN))) { - fimd_update_memory_section(s, w); - } - break; - case FIMD_SHADOWCON: - old_value = s->shadowcon; - s->shadowcon = val; - for (w = 0; w < NUM_OF_WINDOWS; w++) { - if (FIMD_WINDOW_PROTECTED(old_value, w) && - !FIMD_WINDOW_PROTECTED(s->shadowcon, w)) { - fimd_update_memory_section(s, w); - } - } - break; - case FIMD_WINCHMAP: - s->winchmap = val; - break; - case FIMD_VIDOSD_START ... FIMD_VIDOSD_END: - w = (offset - FIMD_VIDOSD_START) >> 4; - i = ((offset - FIMD_VIDOSD_START) & 0xF) >> 2; - switch (i) { - case 0: - old_value = s->window[w].lefttop_y; - s->window[w].lefttop_x = (val >> FIMD_VIDOSD_HOR_SHIFT) & - FIMD_VIDOSD_COORD_MASK; - s->window[w].lefttop_y = (val >> FIMD_VIDOSD_VER_SHIFT) & - FIMD_VIDOSD_COORD_MASK; - if (s->window[w].lefttop_y != old_value) { - fimd_update_memory_section(s, w); - } - break; - case 1: - old_value = s->window[w].rightbot_y; - s->window[w].rightbot_x = (val >> FIMD_VIDOSD_HOR_SHIFT) & - FIMD_VIDOSD_COORD_MASK; - s->window[w].rightbot_y = (val >> FIMD_VIDOSD_VER_SHIFT) & - FIMD_VIDOSD_COORD_MASK; - if (s->window[w].rightbot_y != old_value) { - fimd_update_memory_section(s, w); - } - break; - case 2: - if (w == 0) { - s->window[w].osdsize = val; - } else { - s->window[w].alpha_val[0] = - unpack_upper_4((val & FIMD_VIDOSD_ALPHA_AEN0) >> - FIMD_VIDOSD_AEN0_SHIFT) | - (s->window[w].alpha_val[0] & FIMD_VIDALPHA_ALPHA_LOWER); - s->window[w].alpha_val[1] = - unpack_upper_4(val & FIMD_VIDOSD_ALPHA_AEN1) | - (s->window[w].alpha_val[1] & FIMD_VIDALPHA_ALPHA_LOWER); - } - break; - case 3: - if (w != 1 && w != 2) { - DPRINT_ERROR("Bad write offset 0x%08x\n", offset); - return; - } - s->window[w].osdsize = val; - break; - } - break; - case FIMD_VIDWADD0_START ... FIMD_VIDWADD0_END: - w = (offset - FIMD_VIDWADD0_START) >> 3; - i = ((offset - FIMD_VIDWADD0_START) >> 2) & 1; - if (i == fimd_get_buffer_id(&s->window[w]) && - s->window[w].buf_start[i] != val) { - s->window[w].buf_start[i] = val; - fimd_update_memory_section(s, w); - break; - } - s->window[w].buf_start[i] = val; - break; - case FIMD_VIDWADD1_START ... FIMD_VIDWADD1_END: - w = (offset - FIMD_VIDWADD1_START) >> 3; - i = ((offset - FIMD_VIDWADD1_START) >> 2) & 1; - s->window[w].buf_end[i] = val; - break; - case FIMD_VIDWADD2_START ... FIMD_VIDWADD2_END: - w = (offset - FIMD_VIDWADD2_START) >> 2; - if (((val & FIMD_VIDWADD2_PAGEWIDTH) != s->window[w].virtpage_width) || - (((val >> FIMD_VIDWADD2_OFFSIZE_SHIFT) & FIMD_VIDWADD2_OFFSIZE) != - s->window[w].virtpage_offsize)) { - s->window[w].virtpage_width = val & FIMD_VIDWADD2_PAGEWIDTH; - s->window[w].virtpage_offsize = - (val >> FIMD_VIDWADD2_OFFSIZE_SHIFT) & FIMD_VIDWADD2_OFFSIZE; - fimd_update_memory_section(s, w); - } - break; - case FIMD_VIDINTCON0: - s->vidintcon[0] = val; - break; - case FIMD_VIDINTCON1: - s->vidintcon[1] &= ~(val & 7); - exynos4210_fimd_update_irq(s); - break; - case FIMD_WKEYCON_START ... FIMD_WKEYCON_END: - w = ((offset - FIMD_WKEYCON_START) >> 3) + 1; - i = ((offset - FIMD_WKEYCON_START) >> 2) & 1; - s->window[w].keycon[i] = val; - break; - case FIMD_WKEYALPHA_START ... FIMD_WKEYALPHA_END: - w = ((offset - FIMD_WKEYALPHA_START) >> 2) + 1; - s->window[w].keyalpha = val; - break; - case FIMD_DITHMODE: - s->dithmode = val; - break; - case FIMD_WINMAP_START ... FIMD_WINMAP_END: - w = (offset - FIMD_WINMAP_START) >> 2; - old_value = s->window[w].winmap; - s->window[w].winmap = val; - if ((val & FIMD_WINMAP_EN) ^ (old_value & FIMD_WINMAP_EN)) { - exynos4210_fimd_invalidate(s); - exynos4210_fimd_update_win_bppmode(s, w); - exynos4210_fimd_trace_bppmode(s, w, 0xFFFFFFFF); - exynos4210_fimd_update(s); - } - break; - case FIMD_WPALCON_HIGH ... FIMD_WPALCON_LOW: - i = (offset - FIMD_WPALCON_HIGH) >> 2; - s->wpalcon[i] = val; - if (s->wpalcon[1] & FIMD_WPALCON_UPDATEEN) { - for (w = 0; w < NUM_OF_WINDOWS; w++) { - exynos4210_fimd_update_win_bppmode(s, w); - fimd_update_get_alpha(s, w); - } - } - break; - case FIMD_TRIGCON: - val = (val & ~FIMD_TRIGCON_ROMASK) | (s->trigcon & FIMD_TRIGCON_ROMASK); - s->trigcon = val; - break; - case FIMD_I80IFCON_START ... FIMD_I80IFCON_END: - s->i80ifcon[(offset - FIMD_I80IFCON_START) >> 2] = val; - break; - case FIMD_COLORGAINCON: - s->colorgaincon = val; - break; - case FIMD_LDI_CMDCON0 ... FIMD_LDI_CMDCON1: - s->ldi_cmdcon[(offset - FIMD_LDI_CMDCON0) >> 2] = val; - break; - case FIMD_SIFCCON0 ... FIMD_SIFCCON2: - i = (offset - FIMD_SIFCCON0) >> 2; - if (i != 2) { - s->sifccon[i] = val; - } - break; - case FIMD_HUECOEFCR_START ... FIMD_HUECOEFCR_END: - i = (offset - FIMD_HUECOEFCR_START) >> 2; - s->huecoef_cr[i] = val; - break; - case FIMD_HUECOEFCB_START ... FIMD_HUECOEFCB_END: - i = (offset - FIMD_HUECOEFCB_START) >> 2; - s->huecoef_cb[i] = val; - break; - case FIMD_HUEOFFSET: - s->hueoffset = val; - break; - case FIMD_VIDWALPHA_START ... FIMD_VIDWALPHA_END: - w = ((offset - FIMD_VIDWALPHA_START) >> 3); - i = ((offset - FIMD_VIDWALPHA_START) >> 2) & 1; - if (w == 0) { - s->window[w].alpha_val[i] = val; - } else { - s->window[w].alpha_val[i] = (val & FIMD_VIDALPHA_ALPHA_LOWER) | - (s->window[w].alpha_val[i] & FIMD_VIDALPHA_ALPHA_UPPER); - } - break; - case FIMD_BLENDEQ_START ... FIMD_BLENDEQ_END: - s->window[(offset - FIMD_BLENDEQ_START) >> 2].blendeq = val; - break; - case FIMD_BLENDCON: - old_value = s->blendcon; - s->blendcon = val; - if ((s->blendcon & FIMD_ALPHA_8BIT) != (old_value & FIMD_ALPHA_8BIT)) { - for (w = 0; w < NUM_OF_WINDOWS; w++) { - fimd_update_get_alpha(s, w); - } - } - break; - case FIMD_WRTQOSCON_START ... FIMD_WRTQOSCON_END: - s->window[(offset - FIMD_WRTQOSCON_START) >> 2].rtqoscon = val; - break; - case FIMD_I80IFCMD_START ... FIMD_I80IFCMD_END: - s->i80ifcmd[(offset - FIMD_I80IFCMD_START) >> 2] = val; - break; - case FIMD_VIDW0ADD0_B2 ... FIMD_VIDW4ADD0_B2: - if (offset & 0x0004) { - DPRINT_ERROR("bad write offset 0x%08x\n", offset); - break; - } - w = (offset - FIMD_VIDW0ADD0_B2) >> 3; - if (fimd_get_buffer_id(&s->window[w]) == 2 && - s->window[w].buf_start[2] != val) { - s->window[w].buf_start[2] = val; - fimd_update_memory_section(s, w); - break; - } - s->window[w].buf_start[2] = val; - break; - case FIMD_SHD_ADD0_START ... FIMD_SHD_ADD0_END: - if (offset & 0x0004) { - DPRINT_ERROR("bad write offset 0x%08x\n", offset); - break; - } - s->window[(offset - FIMD_SHD_ADD0_START) >> 3].shadow_buf_start = val; - break; - case FIMD_SHD_ADD1_START ... FIMD_SHD_ADD1_END: - if (offset & 0x0004) { - DPRINT_ERROR("bad write offset 0x%08x\n", offset); - break; - } - s->window[(offset - FIMD_SHD_ADD1_START) >> 3].shadow_buf_end = val; - break; - case FIMD_SHD_ADD2_START ... FIMD_SHD_ADD2_END: - s->window[(offset - FIMD_SHD_ADD2_START) >> 2].shadow_buf_size = val; - break; - case FIMD_PAL_MEM_START ... FIMD_PAL_MEM_END: - w = (offset - FIMD_PAL_MEM_START) >> 10; - i = ((offset - FIMD_PAL_MEM_START) >> 2) & 0xFF; - s->window[w].palette[i] = val; - break; - case FIMD_PALMEM_AL_START ... FIMD_PALMEM_AL_END: - /* Palette memory aliases for windows 0 and 1 */ - w = (offset - FIMD_PALMEM_AL_START) >> 10; - i = ((offset - FIMD_PALMEM_AL_START) >> 2) & 0xFF; - s->window[w].palette[i] = val; - break; - default: - DPRINT_ERROR("bad write offset 0x%08x\n", offset); - break; - } -} - -static uint64_t exynos4210_fimd_read(void *opaque, hwaddr offset, - unsigned size) -{ - Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; - int w, i; - uint32_t ret = 0; - - DPRINT_L2("read offset 0x%08x\n", offset); - - switch (offset) { - case FIMD_VIDCON0 ... FIMD_VIDCON3: - return s->vidcon[(offset - FIMD_VIDCON0) >> 2]; - case FIMD_VIDTCON_START ... FIMD_VIDTCON_END: - return s->vidtcon[(offset - FIMD_VIDTCON_START) >> 2]; - case FIMD_WINCON_START ... FIMD_WINCON_END: - return s->window[(offset - FIMD_WINCON_START) >> 2].wincon; - case FIMD_SHADOWCON: - return s->shadowcon; - case FIMD_WINCHMAP: - return s->winchmap; - case FIMD_VIDOSD_START ... FIMD_VIDOSD_END: - w = (offset - FIMD_VIDOSD_START) >> 4; - i = ((offset - FIMD_VIDOSD_START) & 0xF) >> 2; - switch (i) { - case 0: - ret = ((s->window[w].lefttop_x & FIMD_VIDOSD_COORD_MASK) << - FIMD_VIDOSD_HOR_SHIFT) | - (s->window[w].lefttop_y & FIMD_VIDOSD_COORD_MASK); - break; - case 1: - ret = ((s->window[w].rightbot_x & FIMD_VIDOSD_COORD_MASK) << - FIMD_VIDOSD_HOR_SHIFT) | - (s->window[w].rightbot_y & FIMD_VIDOSD_COORD_MASK); - break; - case 2: - if (w == 0) { - ret = s->window[w].osdsize; - } else { - ret = (pack_upper_4(s->window[w].alpha_val[0]) << - FIMD_VIDOSD_AEN0_SHIFT) | - pack_upper_4(s->window[w].alpha_val[1]); - } - break; - case 3: - if (w != 1 && w != 2) { - DPRINT_ERROR("bad read offset 0x%08x\n", offset); - return 0xBAADBAAD; - } - ret = s->window[w].osdsize; - break; - } - return ret; - case FIMD_VIDWADD0_START ... FIMD_VIDWADD0_END: - w = (offset - FIMD_VIDWADD0_START) >> 3; - i = ((offset - FIMD_VIDWADD0_START) >> 2) & 1; - return s->window[w].buf_start[i]; - case FIMD_VIDWADD1_START ... FIMD_VIDWADD1_END: - w = (offset - FIMD_VIDWADD1_START) >> 3; - i = ((offset - FIMD_VIDWADD1_START) >> 2) & 1; - return s->window[w].buf_end[i]; - case FIMD_VIDWADD2_START ... FIMD_VIDWADD2_END: - w = (offset - FIMD_VIDWADD2_START) >> 2; - return s->window[w].virtpage_width | (s->window[w].virtpage_offsize << - FIMD_VIDWADD2_OFFSIZE_SHIFT); - case FIMD_VIDINTCON0 ... FIMD_VIDINTCON1: - return s->vidintcon[(offset - FIMD_VIDINTCON0) >> 2]; - case FIMD_WKEYCON_START ... FIMD_WKEYCON_END: - w = ((offset - FIMD_WKEYCON_START) >> 3) + 1; - i = ((offset - FIMD_WKEYCON_START) >> 2) & 1; - return s->window[w].keycon[i]; - case FIMD_WKEYALPHA_START ... FIMD_WKEYALPHA_END: - w = ((offset - FIMD_WKEYALPHA_START) >> 2) + 1; - return s->window[w].keyalpha; - case FIMD_DITHMODE: - return s->dithmode; - case FIMD_WINMAP_START ... FIMD_WINMAP_END: - return s->window[(offset - FIMD_WINMAP_START) >> 2].winmap; - case FIMD_WPALCON_HIGH ... FIMD_WPALCON_LOW: - return s->wpalcon[(offset - FIMD_WPALCON_HIGH) >> 2]; - case FIMD_TRIGCON: - return s->trigcon; - case FIMD_I80IFCON_START ... FIMD_I80IFCON_END: - return s->i80ifcon[(offset - FIMD_I80IFCON_START) >> 2]; - case FIMD_COLORGAINCON: - return s->colorgaincon; - case FIMD_LDI_CMDCON0 ... FIMD_LDI_CMDCON1: - return s->ldi_cmdcon[(offset - FIMD_LDI_CMDCON0) >> 2]; - case FIMD_SIFCCON0 ... FIMD_SIFCCON2: - i = (offset - FIMD_SIFCCON0) >> 2; - return s->sifccon[i]; - case FIMD_HUECOEFCR_START ... FIMD_HUECOEFCR_END: - i = (offset - FIMD_HUECOEFCR_START) >> 2; - return s->huecoef_cr[i]; - case FIMD_HUECOEFCB_START ... FIMD_HUECOEFCB_END: - i = (offset - FIMD_HUECOEFCB_START) >> 2; - return s->huecoef_cb[i]; - case FIMD_HUEOFFSET: - return s->hueoffset; - case FIMD_VIDWALPHA_START ... FIMD_VIDWALPHA_END: - w = ((offset - FIMD_VIDWALPHA_START) >> 3); - i = ((offset - FIMD_VIDWALPHA_START) >> 2) & 1; - return s->window[w].alpha_val[i] & - (w == 0 ? 0xFFFFFF : FIMD_VIDALPHA_ALPHA_LOWER); - case FIMD_BLENDEQ_START ... FIMD_BLENDEQ_END: - return s->window[(offset - FIMD_BLENDEQ_START) >> 2].blendeq; - case FIMD_BLENDCON: - return s->blendcon; - case FIMD_WRTQOSCON_START ... FIMD_WRTQOSCON_END: - return s->window[(offset - FIMD_WRTQOSCON_START) >> 2].rtqoscon; - case FIMD_I80IFCMD_START ... FIMD_I80IFCMD_END: - return s->i80ifcmd[(offset - FIMD_I80IFCMD_START) >> 2]; - case FIMD_VIDW0ADD0_B2 ... FIMD_VIDW4ADD0_B2: - if (offset & 0x0004) { - break; - } - return s->window[(offset - FIMD_VIDW0ADD0_B2) >> 3].buf_start[2]; - case FIMD_SHD_ADD0_START ... FIMD_SHD_ADD0_END: - if (offset & 0x0004) { - break; - } - return s->window[(offset - FIMD_SHD_ADD0_START) >> 3].shadow_buf_start; - case FIMD_SHD_ADD1_START ... FIMD_SHD_ADD1_END: - if (offset & 0x0004) { - break; - } - return s->window[(offset - FIMD_SHD_ADD1_START) >> 3].shadow_buf_end; - case FIMD_SHD_ADD2_START ... FIMD_SHD_ADD2_END: - return s->window[(offset - FIMD_SHD_ADD2_START) >> 2].shadow_buf_size; - case FIMD_PAL_MEM_START ... FIMD_PAL_MEM_END: - w = (offset - FIMD_PAL_MEM_START) >> 10; - i = ((offset - FIMD_PAL_MEM_START) >> 2) & 0xFF; - return s->window[w].palette[i]; - case FIMD_PALMEM_AL_START ... FIMD_PALMEM_AL_END: - /* Palette aliases for win 0,1 */ - w = (offset - FIMD_PALMEM_AL_START) >> 10; - i = ((offset - FIMD_PALMEM_AL_START) >> 2) & 0xFF; - return s->window[w].palette[i]; - } - - DPRINT_ERROR("bad read offset 0x%08x\n", offset); - return 0xBAADBAAD; -} - -static const MemoryRegionOps exynos4210_fimd_mmio_ops = { - .read = exynos4210_fimd_read, - .write = exynos4210_fimd_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int exynos4210_fimd_load(void *opaque, int version_id) -{ - Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; - int w; - - if (version_id != 1) { - return -EINVAL; - } - - for (w = 0; w < NUM_OF_WINDOWS; w++) { - exynos4210_fimd_update_win_bppmode(s, w); - fimd_update_get_alpha(s, w); - fimd_update_memory_section(s, w); - } - - /* Redraw the whole screen */ - exynos4210_update_resolution(s); - exynos4210_fimd_invalidate(s); - exynos4210_fimd_enable(s, (s->vidcon[0] & FIMD_VIDCON0_ENVID_MASK) == - FIMD_VIDCON0_ENVID_MASK); - return 0; -} - -static const VMStateDescription exynos4210_fimd_window_vmstate = { - .name = "exynos4210.fimd_window", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(wincon, Exynos4210fimdWindow), - VMSTATE_UINT32_ARRAY(buf_start, Exynos4210fimdWindow, 3), - VMSTATE_UINT32_ARRAY(buf_end, Exynos4210fimdWindow, 3), - VMSTATE_UINT32_ARRAY(keycon, Exynos4210fimdWindow, 2), - VMSTATE_UINT32(keyalpha, Exynos4210fimdWindow), - VMSTATE_UINT32(winmap, Exynos4210fimdWindow), - VMSTATE_UINT32(blendeq, Exynos4210fimdWindow), - VMSTATE_UINT32(rtqoscon, Exynos4210fimdWindow), - VMSTATE_UINT32_ARRAY(palette, Exynos4210fimdWindow, 256), - VMSTATE_UINT32(shadow_buf_start, Exynos4210fimdWindow), - VMSTATE_UINT32(shadow_buf_end, Exynos4210fimdWindow), - VMSTATE_UINT32(shadow_buf_size, Exynos4210fimdWindow), - VMSTATE_UINT16(lefttop_x, Exynos4210fimdWindow), - VMSTATE_UINT16(lefttop_y, Exynos4210fimdWindow), - VMSTATE_UINT16(rightbot_x, Exynos4210fimdWindow), - VMSTATE_UINT16(rightbot_y, Exynos4210fimdWindow), - VMSTATE_UINT32(osdsize, Exynos4210fimdWindow), - VMSTATE_UINT32_ARRAY(alpha_val, Exynos4210fimdWindow, 2), - VMSTATE_UINT16(virtpage_width, Exynos4210fimdWindow), - VMSTATE_UINT16(virtpage_offsize, Exynos4210fimdWindow), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription exynos4210_fimd_vmstate = { - .name = "exynos4210.fimd", - .version_id = 1, - .minimum_version_id = 1, - .post_load = exynos4210_fimd_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(vidcon, Exynos4210fimdState, 4), - VMSTATE_UINT32_ARRAY(vidtcon, Exynos4210fimdState, 4), - VMSTATE_UINT32(shadowcon, Exynos4210fimdState), - VMSTATE_UINT32(winchmap, Exynos4210fimdState), - VMSTATE_UINT32_ARRAY(vidintcon, Exynos4210fimdState, 2), - VMSTATE_UINT32(dithmode, Exynos4210fimdState), - VMSTATE_UINT32_ARRAY(wpalcon, Exynos4210fimdState, 2), - VMSTATE_UINT32(trigcon, Exynos4210fimdState), - VMSTATE_UINT32_ARRAY(i80ifcon, Exynos4210fimdState, 4), - VMSTATE_UINT32(colorgaincon, Exynos4210fimdState), - VMSTATE_UINT32_ARRAY(ldi_cmdcon, Exynos4210fimdState, 2), - VMSTATE_UINT32_ARRAY(sifccon, Exynos4210fimdState, 3), - VMSTATE_UINT32_ARRAY(huecoef_cr, Exynos4210fimdState, 4), - VMSTATE_UINT32_ARRAY(huecoef_cb, Exynos4210fimdState, 4), - VMSTATE_UINT32(hueoffset, Exynos4210fimdState), - VMSTATE_UINT32_ARRAY(i80ifcmd, Exynos4210fimdState, 12), - VMSTATE_UINT32(blendcon, Exynos4210fimdState), - VMSTATE_STRUCT_ARRAY(window, Exynos4210fimdState, 5, 1, - exynos4210_fimd_window_vmstate, Exynos4210fimdWindow), - VMSTATE_END_OF_LIST() - } -}; - -static const GraphicHwOps exynos4210_fimd_ops = { - .invalidate = exynos4210_fimd_invalidate, - .gfx_update = exynos4210_fimd_update, -}; - -static int exynos4210_fimd_init(SysBusDevice *dev) -{ - Exynos4210fimdState *s = EXYNOS4210_FIMD(dev); - - s->ifb = NULL; - - sysbus_init_irq(dev, &s->irq[0]); - sysbus_init_irq(dev, &s->irq[1]); - sysbus_init_irq(dev, &s->irq[2]); - - memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_fimd_mmio_ops, s, - "exynos4210.fimd", FIMD_REGS_SIZE); - sysbus_init_mmio(dev, &s->iomem); - s->console = graphic_console_init(DEVICE(dev), 0, &exynos4210_fimd_ops, s); - - return 0; -} - -static void exynos4210_fimd_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - dc->vmsd = &exynos4210_fimd_vmstate; - dc->reset = exynos4210_fimd_reset; - k->init = exynos4210_fimd_init; -} - -static const TypeInfo exynos4210_fimd_info = { - .name = TYPE_EXYNOS4210_FIMD, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Exynos4210fimdState), - .class_init = exynos4210_fimd_class_init, -}; - -static void exynos4210_fimd_register_types(void) -{ - type_register_static(&exynos4210_fimd_info); -} - -type_init(exynos4210_fimd_register_types) diff --git a/qemu/hw/display/framebuffer.c b/qemu/hw/display/framebuffer.c deleted file mode 100644 index df51358e7..000000000 --- a/qemu/hw/display/framebuffer.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Framebuffer device helper routines - * - * Copyright (c) 2009 CodeSourcery - * Written by Paul Brook - * - * This code is licensed under the GNU GPLv2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -/* TODO: - - Do something similar for framebuffers with local ram - - Handle rotation here instead of hacking dest_pitch - - Use common pixel conversion routines instead of per-device drawfn - - Remove all DisplayState knowledge from devices. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "ui/console.h" -#include "framebuffer.h" - -void framebuffer_update_memory_section( - MemoryRegionSection *mem_section, - MemoryRegion *root, - hwaddr base, - unsigned rows, - unsigned src_width) -{ - hwaddr src_len = (hwaddr)rows * src_width; - - if (mem_section->mr) { - memory_region_set_log(mem_section->mr, false, DIRTY_MEMORY_VGA); - memory_region_unref(mem_section->mr); - mem_section->mr = NULL; - } - - *mem_section = memory_region_find(root, base, src_len); - if (!mem_section->mr) { - return; - } - - if (int128_get64(mem_section->size) < src_len || - !memory_region_is_ram(mem_section->mr)) { - memory_region_unref(mem_section->mr); - mem_section->mr = NULL; - return; - } - - memory_region_set_log(mem_section->mr, true, DIRTY_MEMORY_VGA); -} - -/* Render an image from a shared memory framebuffer. */ -void framebuffer_update_display( - DisplaySurface *ds, - MemoryRegionSection *mem_section, - int cols, /* Width in pixels. */ - int rows, /* Height in pixels. */ - int src_width, /* Length of source line, in bytes. */ - int dest_row_pitch, /* Bytes between adjacent horizontal output pixels. */ - int dest_col_pitch, /* Bytes between adjacent vertical output pixels. */ - int invalidate, /* nonzero to redraw the whole image. */ - drawfn fn, - void *opaque, - int *first_row, /* Input and output. */ - int *last_row /* Output only */) -{ - hwaddr src_len; - uint8_t *dest; - uint8_t *src; - int first, last = 0; - int dirty; - int i; - ram_addr_t addr; - MemoryRegion *mem; - - i = *first_row; - *first_row = -1; - src_len = src_width * rows; - - mem = mem_section->mr; - if (!mem) { - return; - } - memory_region_sync_dirty_bitmap(mem); - - addr = mem_section->offset_within_region; - src = memory_region_get_ram_ptr(mem) + addr; - - dest = surface_data(ds); - if (dest_col_pitch < 0) { - dest -= dest_col_pitch * (cols - 1); - } - if (dest_row_pitch < 0) { - dest -= dest_row_pitch * (rows - 1); - } - first = -1; - - addr += i * src_width; - src += i * src_width; - dest += i * dest_row_pitch; - - for (; i < rows; i++) { - dirty = memory_region_get_dirty(mem, addr, src_width, - DIRTY_MEMORY_VGA); - if (dirty || invalidate) { - fn(opaque, dest, src, cols, dest_col_pitch); - if (first == -1) - first = i; - last = i; - } - addr += src_width; - src += src_width; - dest += dest_row_pitch; - } - if (first < 0) { - return; - } - memory_region_reset_dirty(mem, mem_section->offset_within_region, src_len, - DIRTY_MEMORY_VGA); - *first_row = first; - *last_row = last; -} diff --git a/qemu/hw/display/framebuffer.h b/qemu/hw/display/framebuffer.h deleted file mode 100644 index 38fa0dcec..000000000 --- a/qemu/hw/display/framebuffer.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef QEMU_FRAMEBUFFER_H -#define QEMU_FRAMEBUFFER_H - -#include "exec/memory.h" - -/* Framebuffer device helper routines. */ - -typedef void (*drawfn)(void *, uint8_t *, const uint8_t *, int, int); - -/* framebuffer_update_memory_section: Update framebuffer - * #MemoryRegionSection, for example if the framebuffer is switched to - * a different memory area. - * - * @mem_section: Output #MemoryRegionSection, to be passed to - * framebuffer_update_display(). - * @root: #MemoryRegion within which the framebuffer lies - * @base: Base address of the framebuffer within @root. - * @rows: Height of the screen. - * @src_width: Number of bytes in framebuffer memory between two rows. - */ -void framebuffer_update_memory_section( - MemoryRegionSection *mem_section, - MemoryRegion *root, - hwaddr base, - unsigned rows, - unsigned src_width); - -/* framebuffer_update_display: Draw the framebuffer on a surface. - * - * @ds: #DisplaySurface to draw to. - * @mem_section: #MemoryRegionSection provided by - * framebuffer_update_memory_section(). - * @cols: Width the screen. - * @rows: Height of the screen. - * @src_width: Number of bytes in framebuffer memory between two rows. - * @dest_row_pitch: Number of bytes in the surface data between two rows. - * Negative if the framebuffer is stored in the opposite order (e.g. - * bottom-to-top) compared to the framebuffer. - * @dest_col_pitch: Number of bytes in the surface data between two pixels. - * Negative if the framebuffer is stored in the opposite order (e.g. - * right-to-left) compared to the framebuffer. - * @invalidate: True if the function should redraw the whole screen - * without checking the DIRTY_MEMORY_VGA dirty bitmap. - * @fn: Drawing function to be called for each row that has to be drawn. - * @opaque: Opaque pointer passed to @fn. - * @first_row: Pointer to an integer, receives the number of the first row - * that was drawn (either the first dirty row, or 0 if @invalidate is true). - * @last_row: Pointer to an integer, receives the number of the last row that - * was drawn (either the last dirty row, or @rows-1 if @invalidate is true). - */ -void framebuffer_update_display( - DisplaySurface *ds, - MemoryRegionSection *mem_section, - int cols, - int rows, - int src_width, - int dest_row_pitch, - int dest_col_pitch, - int invalidate, - drawfn fn, - void *opaque, - int *first_row, - int *last_row); - -#endif diff --git a/qemu/hw/display/g364fb.c b/qemu/hw/display/g364fb.c deleted file mode 100644 index 70ef2c745..000000000 --- a/qemu/hw/display/g364fb.c +++ /dev/null @@ -1,559 +0,0 @@ -/* - * QEMU G364 framebuffer Emulator. - * - * Copyright (c) 2007-2011 Herve Poussineau - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "qemu/error-report.h" -#include "ui/console.h" -#include "ui/pixel_ops.h" -#include "trace.h" -#include "hw/sysbus.h" - -typedef struct G364State { - /* hardware */ - uint8_t *vram; - uint32_t vram_size; - qemu_irq irq; - MemoryRegion mem_vram; - MemoryRegion mem_ctrl; - /* registers */ - uint8_t color_palette[256][3]; - uint8_t cursor_palette[3][3]; - uint16_t cursor[512]; - uint32_t cursor_position; - uint32_t ctla; - uint32_t top_of_screen; - uint32_t width, height; /* in pixels */ - /* display refresh support */ - QemuConsole *con; - int depth; - int blanked; -} G364State; - -#define REG_BOOT 0x000000 -#define REG_DISPLAY 0x000118 -#define REG_VDISPLAY 0x000150 -#define REG_CTLA 0x000300 -#define REG_TOP 0x000400 -#define REG_CURS_PAL 0x000508 -#define REG_CURS_POS 0x000638 -#define REG_CLR_PAL 0x000800 -#define REG_CURS_PAT 0x001000 -#define REG_RESET 0x100000 - -#define CTLA_FORCE_BLANK 0x00000400 -#define CTLA_NO_CURSOR 0x00800000 - -#define G364_PAGE_SIZE 4096 - -static inline int check_dirty(G364State *s, ram_addr_t page) -{ - return memory_region_get_dirty(&s->mem_vram, page, G364_PAGE_SIZE, - DIRTY_MEMORY_VGA); -} - -static inline void reset_dirty(G364State *s, - ram_addr_t page_min, ram_addr_t page_max) -{ - memory_region_reset_dirty(&s->mem_vram, - page_min, - page_max + G364_PAGE_SIZE - page_min - 1, - DIRTY_MEMORY_VGA); -} - -static void g364fb_draw_graphic8(G364State *s) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int i, w; - uint8_t *vram; - uint8_t *data_display, *dd; - ram_addr_t page, page_min, page_max; - int x, y; - int xmin, xmax; - int ymin, ymax; - int xcursor, ycursor; - unsigned int (*rgb_to_pixel)(unsigned int r, unsigned int g, unsigned int b); - - switch (surface_bits_per_pixel(surface)) { - case 8: - rgb_to_pixel = rgb_to_pixel8; - w = 1; - break; - case 15: - rgb_to_pixel = rgb_to_pixel15; - w = 2; - break; - case 16: - rgb_to_pixel = rgb_to_pixel16; - w = 2; - break; - case 32: - rgb_to_pixel = rgb_to_pixel32; - w = 4; - break; - default: - hw_error("g364: unknown host depth %d", - surface_bits_per_pixel(surface)); - return; - } - - page = 0; - page_min = (ram_addr_t)-1; - page_max = 0; - - x = y = 0; - xmin = s->width; - xmax = 0; - ymin = s->height; - ymax = 0; - - if (!(s->ctla & CTLA_NO_CURSOR)) { - xcursor = s->cursor_position >> 12; - ycursor = s->cursor_position & 0xfff; - } else { - xcursor = ycursor = -65; - } - - vram = s->vram + s->top_of_screen; - /* XXX: out of range in vram? */ - data_display = dd = surface_data(surface); - while (y < s->height) { - if (check_dirty(s, page)) { - if (y < ymin) - ymin = ymax = y; - if (page_min == (ram_addr_t)-1) - page_min = page; - page_max = page; - if (x < xmin) - xmin = x; - for (i = 0; i < G364_PAGE_SIZE; i++) { - uint8_t index; - unsigned int color; - if (unlikely((y >= ycursor && y < ycursor + 64) && - (x >= xcursor && x < xcursor + 64))) { - /* pointer area */ - int xdiff = x - xcursor; - uint16_t curs = s->cursor[(y - ycursor) * 8 + xdiff / 8]; - int op = (curs >> ((xdiff & 7) * 2)) & 3; - if (likely(op == 0)) { - /* transparent */ - index = *vram; - color = (*rgb_to_pixel)( - s->color_palette[index][0], - s->color_palette[index][1], - s->color_palette[index][2]); - } else { - /* get cursor color */ - index = op - 1; - color = (*rgb_to_pixel)( - s->cursor_palette[index][0], - s->cursor_palette[index][1], - s->cursor_palette[index][2]); - } - } else { - /* normal area */ - index = *vram; - color = (*rgb_to_pixel)( - s->color_palette[index][0], - s->color_palette[index][1], - s->color_palette[index][2]); - } - memcpy(dd, &color, w); - dd += w; - x++; - vram++; - if (x == s->width) { - xmax = s->width - 1; - y++; - if (y == s->height) { - ymax = s->height - 1; - goto done; - } - data_display = dd = data_display + surface_stride(surface); - xmin = 0; - x = 0; - } - } - if (x > xmax) - xmax = x; - if (y > ymax) - ymax = y; - } else { - int dy; - if (page_min != (ram_addr_t)-1) { - reset_dirty(s, page_min, page_max); - page_min = (ram_addr_t)-1; - page_max = 0; - dpy_gfx_update(s->con, xmin, ymin, - xmax - xmin + 1, ymax - ymin + 1); - xmin = s->width; - xmax = 0; - ymin = s->height; - ymax = 0; - } - x += G364_PAGE_SIZE; - dy = x / s->width; - x = x % s->width; - y += dy; - vram += G364_PAGE_SIZE; - data_display += dy * surface_stride(surface); - dd = data_display + x * w; - } - page += G364_PAGE_SIZE; - } - -done: - if (page_min != (ram_addr_t)-1) { - dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); - reset_dirty(s, page_min, page_max); - } -} - -static void g364fb_draw_blank(G364State *s) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int i, w; - uint8_t *d; - - if (s->blanked) { - /* Screen is already blank. No need to redraw it */ - return; - } - - w = s->width * surface_bytes_per_pixel(surface); - d = surface_data(surface); - for (i = 0; i < s->height; i++) { - memset(d, 0, w); - d += surface_stride(surface); - } - - dpy_gfx_update(s->con, 0, 0, s->width, s->height); - s->blanked = 1; -} - -static void g364fb_update_display(void *opaque) -{ - G364State *s = opaque; - DisplaySurface *surface = qemu_console_surface(s->con); - - qemu_flush_coalesced_mmio_buffer(); - - if (s->width == 0 || s->height == 0) - return; - - if (s->width != surface_width(surface) || - s->height != surface_height(surface)) { - qemu_console_resize(s->con, s->width, s->height); - } - - memory_region_sync_dirty_bitmap(&s->mem_vram); - if (s->ctla & CTLA_FORCE_BLANK) { - g364fb_draw_blank(s); - } else if (s->depth == 8) { - g364fb_draw_graphic8(s); - } else { - error_report("g364: unknown guest depth %d", s->depth); - } - - qemu_irq_raise(s->irq); -} - -static inline void g364fb_invalidate_display(void *opaque) -{ - G364State *s = opaque; - - s->blanked = 0; - memory_region_set_dirty(&s->mem_vram, 0, s->vram_size); -} - -static void g364fb_reset(G364State *s) -{ - qemu_irq_lower(s->irq); - - memset(s->color_palette, 0, sizeof(s->color_palette)); - memset(s->cursor_palette, 0, sizeof(s->cursor_palette)); - memset(s->cursor, 0, sizeof(s->cursor)); - s->cursor_position = 0; - s->ctla = 0; - s->top_of_screen = 0; - s->width = s->height = 0; - memset(s->vram, 0, s->vram_size); - g364fb_invalidate_display(s); -} - -/* called for accesses to io ports */ -static uint64_t g364fb_ctrl_read(void *opaque, - hwaddr addr, - unsigned int size) -{ - G364State *s = opaque; - uint32_t val; - - if (addr >= REG_CURS_PAT && addr < REG_CURS_PAT + 0x1000) { - /* cursor pattern */ - int idx = (addr - REG_CURS_PAT) >> 3; - val = s->cursor[idx]; - } else if (addr >= REG_CURS_PAL && addr < REG_CURS_PAL + 0x18) { - /* cursor palette */ - int idx = (addr - REG_CURS_PAL) >> 3; - val = ((uint32_t)s->cursor_palette[idx][0] << 16); - val |= ((uint32_t)s->cursor_palette[idx][1] << 8); - val |= ((uint32_t)s->cursor_palette[idx][2] << 0); - } else { - switch (addr) { - case REG_DISPLAY: - val = s->width / 4; - break; - case REG_VDISPLAY: - val = s->height * 2; - break; - case REG_CTLA: - val = s->ctla; - break; - default: - { - error_report("g364: invalid read at [" TARGET_FMT_plx "]", - addr); - val = 0; - break; - } - } - } - - trace_g364fb_read(addr, val); - - return val; -} - -static void g364fb_update_depth(G364State *s) -{ - static const int depths[8] = { 1, 2, 4, 8, 15, 16, 0 }; - s->depth = depths[(s->ctla & 0x00700000) >> 20]; -} - -static void g364_invalidate_cursor_position(G364State *s) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int ymin, ymax, start, end; - - /* invalidate only near the cursor */ - ymin = s->cursor_position & 0xfff; - ymax = MIN(s->height, ymin + 64); - start = ymin * surface_stride(surface); - end = (ymax + 1) * surface_stride(surface); - - memory_region_set_dirty(&s->mem_vram, start, end - start); -} - -static void g364fb_ctrl_write(void *opaque, - hwaddr addr, - uint64_t val, - unsigned int size) -{ - G364State *s = opaque; - - trace_g364fb_write(addr, val); - - if (addr >= REG_CLR_PAL && addr < REG_CLR_PAL + 0x800) { - /* color palette */ - int idx = (addr - REG_CLR_PAL) >> 3; - s->color_palette[idx][0] = (val >> 16) & 0xff; - s->color_palette[idx][1] = (val >> 8) & 0xff; - s->color_palette[idx][2] = val & 0xff; - g364fb_invalidate_display(s); - } else if (addr >= REG_CURS_PAT && addr < REG_CURS_PAT + 0x1000) { - /* cursor pattern */ - int idx = (addr - REG_CURS_PAT) >> 3; - s->cursor[idx] = val; - g364fb_invalidate_display(s); - } else if (addr >= REG_CURS_PAL && addr < REG_CURS_PAL + 0x18) { - /* cursor palette */ - int idx = (addr - REG_CURS_PAL) >> 3; - s->cursor_palette[idx][0] = (val >> 16) & 0xff; - s->cursor_palette[idx][1] = (val >> 8) & 0xff; - s->cursor_palette[idx][2] = val & 0xff; - g364fb_invalidate_display(s); - } else { - switch (addr) { - case REG_BOOT: /* Boot timing */ - case 0x00108: /* Line timing: half sync */ - case 0x00110: /* Line timing: back porch */ - case 0x00120: /* Line timing: short display */ - case 0x00128: /* Frame timing: broad pulse */ - case 0x00130: /* Frame timing: v sync */ - case 0x00138: /* Frame timing: v preequalise */ - case 0x00140: /* Frame timing: v postequalise */ - case 0x00148: /* Frame timing: v blank */ - case 0x00158: /* Line timing: line time */ - case 0x00160: /* Frame store: line start */ - case 0x00168: /* vram cycle: mem init */ - case 0x00170: /* vram cycle: transfer delay */ - case 0x00200: /* vram cycle: mask register */ - /* ignore */ - break; - case REG_TOP: - s->top_of_screen = val; - g364fb_invalidate_display(s); - break; - case REG_DISPLAY: - s->width = val * 4; - break; - case REG_VDISPLAY: - s->height = val / 2; - break; - case REG_CTLA: - s->ctla = val; - g364fb_update_depth(s); - g364fb_invalidate_display(s); - break; - case REG_CURS_POS: - g364_invalidate_cursor_position(s); - s->cursor_position = val; - g364_invalidate_cursor_position(s); - break; - case REG_RESET: - g364fb_reset(s); - break; - default: - error_report("g364: invalid write of 0x%" PRIx64 - " at [" TARGET_FMT_plx "]", val, addr); - break; - } - } - qemu_irq_lower(s->irq); -} - -static const MemoryRegionOps g364fb_ctrl_ops = { - .read = g364fb_ctrl_read, - .write = g364fb_ctrl_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl.min_access_size = 4, - .impl.max_access_size = 4, -}; - -static int g364fb_post_load(void *opaque, int version_id) -{ - G364State *s = opaque; - - /* force refresh */ - g364fb_update_depth(s); - g364fb_invalidate_display(s); - - return 0; -} - -static const VMStateDescription vmstate_g364fb = { - .name = "g364fb", - .version_id = 1, - .minimum_version_id = 1, - .post_load = g364fb_post_load, - .fields = (VMStateField[]) { - VMSTATE_VBUFFER_UINT32(vram, G364State, 1, NULL, 0, vram_size), - VMSTATE_BUFFER_UNSAFE(color_palette, G364State, 0, 256 * 3), - VMSTATE_BUFFER_UNSAFE(cursor_palette, G364State, 0, 9), - VMSTATE_UINT16_ARRAY(cursor, G364State, 512), - VMSTATE_UINT32(cursor_position, G364State), - VMSTATE_UINT32(ctla, G364State), - VMSTATE_UINT32(top_of_screen, G364State), - VMSTATE_UINT32(width, G364State), - VMSTATE_UINT32(height, G364State), - VMSTATE_END_OF_LIST() - } -}; - -static const GraphicHwOps g364fb_ops = { - .invalidate = g364fb_invalidate_display, - .gfx_update = g364fb_update_display, -}; - -static void g364fb_init(DeviceState *dev, G364State *s) -{ - s->vram = g_malloc0(s->vram_size); - - s->con = graphic_console_init(dev, 0, &g364fb_ops, s); - - memory_region_init_io(&s->mem_ctrl, NULL, &g364fb_ctrl_ops, s, "ctrl", 0x180000); - memory_region_init_ram_ptr(&s->mem_vram, NULL, "vram", - s->vram_size, s->vram); - vmstate_register_ram(&s->mem_vram, dev); - memory_region_set_log(&s->mem_vram, true, DIRTY_MEMORY_VGA); -} - -#define TYPE_G364 "sysbus-g364" -#define G364(obj) OBJECT_CHECK(G364SysBusState, (obj), TYPE_G364) - -typedef struct { - SysBusDevice parent_obj; - - G364State g364; -} G364SysBusState; - -static int g364fb_sysbus_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - G364SysBusState *sbs = G364(dev); - G364State *s = &sbs->g364; - - g364fb_init(dev, s); - sysbus_init_irq(sbd, &s->irq); - sysbus_init_mmio(sbd, &s->mem_ctrl); - sysbus_init_mmio(sbd, &s->mem_vram); - - return 0; -} - -static void g364fb_sysbus_reset(DeviceState *d) -{ - G364SysBusState *s = G364(d); - - g364fb_reset(&s->g364); -} - -static Property g364fb_sysbus_properties[] = { - DEFINE_PROP_UINT32("vram_size", G364SysBusState, g364.vram_size, - 8 * 1024 * 1024), - DEFINE_PROP_END_OF_LIST(), -}; - -static void g364fb_sysbus_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = g364fb_sysbus_init; - set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); - dc->desc = "G364 framebuffer"; - dc->reset = g364fb_sysbus_reset; - dc->vmsd = &vmstate_g364fb; - dc->props = g364fb_sysbus_properties; -} - -static const TypeInfo g364fb_sysbus_info = { - .name = TYPE_G364, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(G364SysBusState), - .class_init = g364fb_sysbus_class_init, -}; - -static void g364fb_register_types(void) -{ - type_register_static(&g364fb_sysbus_info); -} - -type_init(g364fb_register_types) diff --git a/qemu/hw/display/jazz_led.c b/qemu/hw/display/jazz_led.c deleted file mode 100644 index 09dcdb46a..000000000 --- a/qemu/hw/display/jazz_led.c +++ /dev/null @@ -1,314 +0,0 @@ -/* - * QEMU JAZZ LED emulator. - * - * Copyright (c) 2007-2012 Herve Poussineau - * - * 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 "qemu-common.h" -#include "ui/console.h" -#include "ui/pixel_ops.h" -#include "trace.h" -#include "hw/sysbus.h" - -typedef enum { - REDRAW_NONE = 0, REDRAW_SEGMENTS = 1, REDRAW_BACKGROUND = 2, -} screen_state_t; - -#define TYPE_JAZZ_LED "jazz-led" -#define JAZZ_LED(obj) OBJECT_CHECK(LedState, (obj), TYPE_JAZZ_LED) - -typedef struct LedState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint8_t segments; - QemuConsole *con; - screen_state_t state; -} LedState; - -static uint64_t jazz_led_read(void *opaque, hwaddr addr, - unsigned int size) -{ - LedState *s = opaque; - uint8_t val; - - val = s->segments; - trace_jazz_led_read(addr, val); - - return val; -} - -static void jazz_led_write(void *opaque, hwaddr addr, - uint64_t val, unsigned int size) -{ - LedState *s = opaque; - uint8_t new_val = val & 0xff; - - trace_jazz_led_write(addr, new_val); - - s->segments = new_val; - s->state |= REDRAW_SEGMENTS; -} - -static const MemoryRegionOps led_ops = { - .read = jazz_led_read, - .write = jazz_led_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl.min_access_size = 1, - .impl.max_access_size = 1, -}; - -/***********************************************************/ -/* jazz_led display */ - -static void draw_horizontal_line(DisplaySurface *ds, - int posy, int posx1, int posx2, - uint32_t color) -{ - uint8_t *d; - int x, bpp; - - bpp = (surface_bits_per_pixel(ds) + 7) >> 3; - d = surface_data(ds) + surface_stride(ds) * posy + bpp * posx1; - switch(bpp) { - case 1: - for (x = posx1; x <= posx2; x++) { - *((uint8_t *)d) = color; - d++; - } - break; - case 2: - for (x = posx1; x <= posx2; x++) { - *((uint16_t *)d) = color; - d += 2; - } - break; - case 4: - for (x = posx1; x <= posx2; x++) { - *((uint32_t *)d) = color; - d += 4; - } - break; - } -} - -static void draw_vertical_line(DisplaySurface *ds, - int posx, int posy1, int posy2, - uint32_t color) -{ - uint8_t *d; - int y, bpp; - - bpp = (surface_bits_per_pixel(ds) + 7) >> 3; - d = surface_data(ds) + surface_stride(ds) * posy1 + bpp * posx; - switch(bpp) { - case 1: - for (y = posy1; y <= posy2; y++) { - *((uint8_t *)d) = color; - d += surface_stride(ds); - } - break; - case 2: - for (y = posy1; y <= posy2; y++) { - *((uint16_t *)d) = color; - d += surface_stride(ds); - } - break; - case 4: - for (y = posy1; y <= posy2; y++) { - *((uint32_t *)d) = color; - d += surface_stride(ds); - } - break; - } -} - -static void jazz_led_update_display(void *opaque) -{ - LedState *s = opaque; - DisplaySurface *surface = qemu_console_surface(s->con); - uint8_t *d1; - uint32_t color_segment, color_led; - int y, bpp; - - if (s->state & REDRAW_BACKGROUND) { - /* clear screen */ - bpp = (surface_bits_per_pixel(surface) + 7) >> 3; - d1 = surface_data(surface); - for (y = 0; y < surface_height(surface); y++) { - memset(d1, 0x00, surface_width(surface) * bpp); - d1 += surface_stride(surface); - } - } - - if (s->state & REDRAW_SEGMENTS) { - /* set colors according to bpp */ - switch (surface_bits_per_pixel(surface)) { - case 8: - color_segment = rgb_to_pixel8(0xaa, 0xaa, 0xaa); - color_led = rgb_to_pixel8(0x00, 0xff, 0x00); - break; - case 15: - color_segment = rgb_to_pixel15(0xaa, 0xaa, 0xaa); - color_led = rgb_to_pixel15(0x00, 0xff, 0x00); - break; - case 16: - color_segment = rgb_to_pixel16(0xaa, 0xaa, 0xaa); - color_led = rgb_to_pixel16(0x00, 0xff, 0x00); - break; - case 24: - color_segment = rgb_to_pixel24(0xaa, 0xaa, 0xaa); - color_led = rgb_to_pixel24(0x00, 0xff, 0x00); - break; - case 32: - color_segment = rgb_to_pixel32(0xaa, 0xaa, 0xaa); - color_led = rgb_to_pixel32(0x00, 0xff, 0x00); - break; - default: - return; - } - - /* display segments */ - draw_horizontal_line(surface, 40, 10, 40, - (s->segments & 0x02) ? color_segment : 0); - draw_vertical_line(surface, 10, 10, 40, - (s->segments & 0x04) ? color_segment : 0); - draw_vertical_line(surface, 10, 40, 70, - (s->segments & 0x08) ? color_segment : 0); - draw_horizontal_line(surface, 70, 10, 40, - (s->segments & 0x10) ? color_segment : 0); - draw_vertical_line(surface, 40, 40, 70, - (s->segments & 0x20) ? color_segment : 0); - draw_vertical_line(surface, 40, 10, 40, - (s->segments & 0x40) ? color_segment : 0); - draw_horizontal_line(surface, 10, 10, 40, - (s->segments & 0x80) ? color_segment : 0); - - /* display led */ - if (!(s->segments & 0x01)) - color_led = 0; /* black */ - draw_horizontal_line(surface, 68, 50, 50, color_led); - draw_horizontal_line(surface, 69, 49, 51, color_led); - draw_horizontal_line(surface, 70, 48, 52, color_led); - draw_horizontal_line(surface, 71, 49, 51, color_led); - draw_horizontal_line(surface, 72, 50, 50, color_led); - } - - s->state = REDRAW_NONE; - dpy_gfx_update(s->con, 0, 0, - surface_width(surface), surface_height(surface)); -} - -static void jazz_led_invalidate_display(void *opaque) -{ - LedState *s = opaque; - s->state |= REDRAW_SEGMENTS | REDRAW_BACKGROUND; -} - -static void jazz_led_text_update(void *opaque, console_ch_t *chardata) -{ - LedState *s = opaque; - char buf[2]; - - dpy_text_cursor(s->con, -1, -1); - qemu_console_resize(s->con, 2, 1); - - /* TODO: draw the segments */ - snprintf(buf, 2, "%02hhx\n", s->segments); - 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); -} - -static int jazz_led_post_load(void *opaque, int version_id) -{ - /* force refresh */ - jazz_led_invalidate_display(opaque); - - return 0; -} - -static const VMStateDescription vmstate_jazz_led = { - .name = "jazz-led", - .version_id = 0, - .minimum_version_id = 0, - .post_load = jazz_led_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(segments, LedState), - VMSTATE_END_OF_LIST() - } -}; - -static const GraphicHwOps jazz_led_ops = { - .invalidate = jazz_led_invalidate_display, - .gfx_update = jazz_led_update_display, - .text_update = jazz_led_text_update, -}; - -static int jazz_led_init(SysBusDevice *dev) -{ - LedState *s = JAZZ_LED(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &led_ops, s, "led", 1); - sysbus_init_mmio(dev, &s->iomem); - - s->con = graphic_console_init(DEVICE(dev), 0, &jazz_led_ops, s); - - return 0; -} - -static void jazz_led_reset(DeviceState *d) -{ - LedState *s = JAZZ_LED(d); - - s->segments = 0; - s->state = REDRAW_SEGMENTS | REDRAW_BACKGROUND; - qemu_console_resize(s->con, 60, 80); -} - -static void jazz_led_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = jazz_led_init; - dc->desc = "Jazz LED display", - dc->vmsd = &vmstate_jazz_led; - dc->reset = jazz_led_reset; -} - -static const TypeInfo jazz_led_info = { - .name = TYPE_JAZZ_LED, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(LedState), - .class_init = jazz_led_class_init, -}; - -static void jazz_led_register(void) -{ - type_register_static(&jazz_led_info); -} - -type_init(jazz_led_register); diff --git a/qemu/hw/display/milkymist-tmu2.c b/qemu/hw/display/milkymist-tmu2.c deleted file mode 100644 index 9bc88f93b..000000000 --- a/qemu/hw/display/milkymist-tmu2.c +++ /dev/null @@ -1,495 +0,0 @@ -/* - * QEMU model of the Milkymist texture mapping unit. - * - * Copyright (c) 2010 Michael Walle - * Copyright (c) 2010 Sebastien Bourdeauducq - * - * - * 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 . - * - * - * Specification available at: - * http://www.milkymist.org/socdoc/tmu2.pdf - * - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "qemu/error-report.h" - -#include -#include -#include - -enum { - R_CTL = 0, - R_HMESHLAST, - R_VMESHLAST, - R_BRIGHTNESS, - R_CHROMAKEY, - R_VERTICESADDR, - R_TEXFBUF, - R_TEXHRES, - R_TEXVRES, - R_TEXHMASK, - R_TEXVMASK, - R_DSTFBUF, - R_DSTHRES, - R_DSTVRES, - R_DSTHOFFSET, - R_DSTVOFFSET, - R_DSTSQUAREW, - R_DSTSQUAREH, - R_ALPHA, - R_MAX -}; - -enum { - CTL_START_BUSY = (1<<0), - CTL_CHROMAKEY = (1<<1), -}; - -enum { - MAX_BRIGHTNESS = 63, - MAX_ALPHA = 63, -}; - -enum { - MESH_MAXSIZE = 128, -}; - -struct vertex { - int x; - int y; -} QEMU_PACKED; - -#define TYPE_MILKYMIST_TMU2 "milkymist-tmu2" -#define MILKYMIST_TMU2(obj) \ - OBJECT_CHECK(MilkymistTMU2State, (obj), TYPE_MILKYMIST_TMU2) - -struct MilkymistTMU2State { - SysBusDevice parent_obj; - - MemoryRegion regs_region; - CharDriverState *chr; - qemu_irq irq; - - uint32_t regs[R_MAX]; - - Display *dpy; - GLXFBConfig glx_fb_config; - GLXContext glx_context; -}; -typedef struct MilkymistTMU2State MilkymistTMU2State; - -static const int glx_fbconfig_attr[] = { - GLX_GREEN_SIZE, 5, - GLX_GREEN_SIZE, 6, - GLX_BLUE_SIZE, 5, - None -}; - -static int tmu2_glx_init(MilkymistTMU2State *s) -{ - GLXFBConfig *configs; - int nelements; - - s->dpy = XOpenDisplay(NULL); /* FIXME: call XCloseDisplay() */ - if (s->dpy == NULL) { - return 1; - } - - configs = glXChooseFBConfig(s->dpy, 0, glx_fbconfig_attr, &nelements); - if (configs == NULL) { - return 1; - } - - s->glx_fb_config = *configs; - XFree(configs); - - /* FIXME: call glXDestroyContext() */ - s->glx_context = glXCreateNewContext(s->dpy, s->glx_fb_config, - GLX_RGBA_TYPE, NULL, 1); - if (s->glx_context == NULL) { - return 1; - } - - return 0; -} - -static void tmu2_gl_map(struct vertex *mesh, int texhres, int texvres, - int hmeshlast, int vmeshlast, int ho, int vo, int sw, int sh) -{ - int x, y; - int x0, y0, x1, y1; - int u0, v0, u1, v1, u2, v2, u3, v3; - double xscale = 1.0 / ((double)(64 * texhres)); - double yscale = 1.0 / ((double)(64 * texvres)); - - glLoadIdentity(); - glTranslatef(ho, vo, 0); - glEnable(GL_TEXTURE_2D); - glBegin(GL_QUADS); - - for (y = 0; y < vmeshlast; y++) { - y0 = y * sh; - y1 = y0 + sh; - for (x = 0; x < hmeshlast; x++) { - x0 = x * sw; - x1 = x0 + sw; - - u0 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x].x); - v0 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x].y); - u1 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x + 1].x); - v1 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x + 1].y); - u2 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x + 1].x); - v2 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x + 1].y); - u3 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x].x); - v3 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x].y); - - glTexCoord2d(((double)u0) * xscale, ((double)v0) * yscale); - glVertex3i(x0, y0, 0); - glTexCoord2d(((double)u1) * xscale, ((double)v1) * yscale); - glVertex3i(x1, y0, 0); - glTexCoord2d(((double)u2) * xscale, ((double)v2) * yscale); - glVertex3i(x1, y1, 0); - glTexCoord2d(((double)u3) * xscale, ((double)v3) * yscale); - glVertex3i(x0, y1, 0); - } - } - - glEnd(); -} - -static void tmu2_start(MilkymistTMU2State *s) -{ - int pbuffer_attrib[6] = { - GLX_PBUFFER_WIDTH, - 0, - GLX_PBUFFER_HEIGHT, - 0, - GLX_PRESERVED_CONTENTS, - True - }; - - GLXPbuffer pbuffer; - GLuint texture; - void *fb; - hwaddr fb_len; - void *mesh; - hwaddr mesh_len; - float m; - - trace_milkymist_tmu2_start(); - - /* Create and set up a suitable OpenGL context */ - pbuffer_attrib[1] = s->regs[R_DSTHRES]; - pbuffer_attrib[3] = s->regs[R_DSTVRES]; - pbuffer = glXCreatePbuffer(s->dpy, s->glx_fb_config, pbuffer_attrib); - glXMakeContextCurrent(s->dpy, pbuffer, pbuffer, s->glx_context); - - /* Fixup endianness. TODO: would it work on BE hosts? */ - glPixelStorei(GL_UNPACK_SWAP_BYTES, 1); - glPixelStorei(GL_PACK_SWAP_BYTES, 1); - - /* Row alignment */ - glPixelStorei(GL_UNPACK_ALIGNMENT, 2); - glPixelStorei(GL_PACK_ALIGNMENT, 2); - - /* Read the QEMU source framebuffer into an OpenGL texture */ - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - fb_len = 2*s->regs[R_TEXHRES]*s->regs[R_TEXVRES]; - fb = cpu_physical_memory_map(s->regs[R_TEXFBUF], &fb_len, 0); - if (fb == NULL) { - glDeleteTextures(1, &texture); - glXMakeContextCurrent(s->dpy, None, None, NULL); - glXDestroyPbuffer(s->dpy, pbuffer); - return; - } - glTexImage2D(GL_TEXTURE_2D, 0, 3, s->regs[R_TEXHRES], s->regs[R_TEXVRES], - 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, fb); - cpu_physical_memory_unmap(fb, fb_len, 0, fb_len); - - /* Set up texturing options */ - /* WARNING: - * Many cases of TMU2 masking are not supported by OpenGL. - * We only implement the most common ones: - * - full bilinear filtering vs. nearest texel - * - texture clamping vs. texture wrapping - */ - if ((s->regs[R_TEXHMASK] & 0x3f) > 0x20) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - } - if ((s->regs[R_TEXHMASK] >> 6) & s->regs[R_TEXHRES]) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - } - if ((s->regs[R_TEXVMASK] >> 6) & s->regs[R_TEXVRES]) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - } - - /* Translucency and decay */ - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - m = (float)(s->regs[R_BRIGHTNESS] + 1) / 64.0f; - glColor4f(m, m, m, (float)(s->regs[R_ALPHA] + 1) / 64.0f); - - /* Read the QEMU dest. framebuffer into the OpenGL framebuffer */ - fb_len = 2 * s->regs[R_DSTHRES] * s->regs[R_DSTVRES]; - fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, 0); - if (fb == NULL) { - glDeleteTextures(1, &texture); - glXMakeContextCurrent(s->dpy, None, None, NULL); - glXDestroyPbuffer(s->dpy, pbuffer); - return; - } - - glDrawPixels(s->regs[R_DSTHRES], s->regs[R_DSTVRES], GL_RGB, - GL_UNSIGNED_SHORT_5_6_5, fb); - cpu_physical_memory_unmap(fb, fb_len, 0, fb_len); - glViewport(0, 0, s->regs[R_DSTHRES], s->regs[R_DSTVRES]); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0, s->regs[R_DSTHRES], 0.0, s->regs[R_DSTVRES], -1.0, 1.0); - glMatrixMode(GL_MODELVIEW); - - /* Map the texture */ - mesh_len = MESH_MAXSIZE*MESH_MAXSIZE*sizeof(struct vertex); - mesh = cpu_physical_memory_map(s->regs[R_VERTICESADDR], &mesh_len, 0); - if (mesh == NULL) { - glDeleteTextures(1, &texture); - glXMakeContextCurrent(s->dpy, None, None, NULL); - glXDestroyPbuffer(s->dpy, pbuffer); - return; - } - - tmu2_gl_map((struct vertex *)mesh, - s->regs[R_TEXHRES], s->regs[R_TEXVRES], - s->regs[R_HMESHLAST], s->regs[R_VMESHLAST], - s->regs[R_DSTHOFFSET], s->regs[R_DSTVOFFSET], - s->regs[R_DSTSQUAREW], s->regs[R_DSTSQUAREH]); - cpu_physical_memory_unmap(mesh, mesh_len, 0, mesh_len); - - /* Write back the OpenGL framebuffer to the QEMU framebuffer */ - fb_len = 2 * s->regs[R_DSTHRES] * s->regs[R_DSTVRES]; - fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, 1); - if (fb == NULL) { - glDeleteTextures(1, &texture); - glXMakeContextCurrent(s->dpy, None, None, NULL); - glXDestroyPbuffer(s->dpy, pbuffer); - return; - } - - glReadPixels(0, 0, s->regs[R_DSTHRES], s->regs[R_DSTVRES], GL_RGB, - GL_UNSIGNED_SHORT_5_6_5, fb); - cpu_physical_memory_unmap(fb, fb_len, 1, fb_len); - - /* Free OpenGL allocs */ - glDeleteTextures(1, &texture); - glXMakeContextCurrent(s->dpy, None, None, NULL); - glXDestroyPbuffer(s->dpy, pbuffer); - - s->regs[R_CTL] &= ~CTL_START_BUSY; - - trace_milkymist_tmu2_pulse_irq(); - qemu_irq_pulse(s->irq); -} - -static uint64_t tmu2_read(void *opaque, hwaddr addr, - unsigned size) -{ - MilkymistTMU2State *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_CTL: - case R_HMESHLAST: - case R_VMESHLAST: - case R_BRIGHTNESS: - case R_CHROMAKEY: - case R_VERTICESADDR: - case R_TEXFBUF: - case R_TEXHRES: - case R_TEXVRES: - case R_TEXHMASK: - case R_TEXVMASK: - case R_DSTFBUF: - case R_DSTHRES: - case R_DSTVRES: - case R_DSTHOFFSET: - case R_DSTVOFFSET: - case R_DSTSQUAREW: - case R_DSTSQUAREH: - case R_ALPHA: - r = s->regs[addr]; - break; - - default: - error_report("milkymist_tmu2: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_milkymist_tmu2_memory_read(addr << 2, r); - - return r; -} - -static void tmu2_check_registers(MilkymistTMU2State *s) -{ - if (s->regs[R_BRIGHTNESS] > MAX_BRIGHTNESS) { - error_report("milkymist_tmu2: max brightness is %d", MAX_BRIGHTNESS); - } - - if (s->regs[R_ALPHA] > MAX_ALPHA) { - error_report("milkymist_tmu2: max alpha is %d", MAX_ALPHA); - } - - if (s->regs[R_VERTICESADDR] & 0x07) { - error_report("milkymist_tmu2: vertex mesh address has to be 64-bit " - "aligned"); - } - - if (s->regs[R_TEXFBUF] & 0x01) { - error_report("milkymist_tmu2: texture buffer address has to be " - "16-bit aligned"); - } -} - -static void tmu2_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - MilkymistTMU2State *s = opaque; - - trace_milkymist_tmu2_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_CTL: - s->regs[addr] = value; - if (value & CTL_START_BUSY) { - tmu2_start(s); - } - break; - case R_BRIGHTNESS: - case R_HMESHLAST: - case R_VMESHLAST: - case R_CHROMAKEY: - case R_VERTICESADDR: - case R_TEXFBUF: - case R_TEXHRES: - case R_TEXVRES: - case R_TEXHMASK: - case R_TEXVMASK: - case R_DSTFBUF: - case R_DSTHRES: - case R_DSTVRES: - case R_DSTHOFFSET: - case R_DSTVOFFSET: - case R_DSTSQUAREW: - case R_DSTSQUAREH: - case R_ALPHA: - s->regs[addr] = value; - break; - - default: - error_report("milkymist_tmu2: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - tmu2_check_registers(s); -} - -static const MemoryRegionOps tmu2_mmio_ops = { - .read = tmu2_read, - .write = tmu2_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void milkymist_tmu2_reset(DeviceState *d) -{ - MilkymistTMU2State *s = MILKYMIST_TMU2(d); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } -} - -static int milkymist_tmu2_init(SysBusDevice *dev) -{ - MilkymistTMU2State *s = MILKYMIST_TMU2(dev); - - if (tmu2_glx_init(s)) { - return 1; - } - - sysbus_init_irq(dev, &s->irq); - - memory_region_init_io(&s->regs_region, OBJECT(s), &tmu2_mmio_ops, s, - "milkymist-tmu2", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); - - return 0; -} - -static const VMStateDescription vmstate_milkymist_tmu2 = { - .name = "milkymist-tmu2", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, MilkymistTMU2State, R_MAX), - VMSTATE_END_OF_LIST() - } -}; - -static void milkymist_tmu2_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = milkymist_tmu2_init; - dc->reset = milkymist_tmu2_reset; - dc->vmsd = &vmstate_milkymist_tmu2; -} - -static const TypeInfo milkymist_tmu2_info = { - .name = TYPE_MILKYMIST_TMU2, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MilkymistTMU2State), - .class_init = milkymist_tmu2_class_init, -}; - -static void milkymist_tmu2_register_types(void) -{ - type_register_static(&milkymist_tmu2_info); -} - -type_init(milkymist_tmu2_register_types) diff --git a/qemu/hw/display/milkymist-vgafb.c b/qemu/hw/display/milkymist-vgafb.c deleted file mode 100644 index 19ca25647..000000000 --- a/qemu/hw/display/milkymist-vgafb.c +++ /dev/null @@ -1,354 +0,0 @@ - -/* - * QEMU model of the Milkymist VGA framebuffer. - * - * Copyright (c) 2010-2012 Michael Walle - * - * 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 . - * - * - * Specification available at: - * http://www.milkymist.org/socdoc/vgafb.pdf - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "ui/console.h" -#include "framebuffer.h" -#include "ui/pixel_ops.h" -#include "qemu/error-report.h" - -#define BITS 8 -#include "milkymist-vgafb_template.h" -#define BITS 15 -#include "milkymist-vgafb_template.h" -#define BITS 16 -#include "milkymist-vgafb_template.h" -#define BITS 24 -#include "milkymist-vgafb_template.h" -#define BITS 32 -#include "milkymist-vgafb_template.h" - -enum { - R_CTRL = 0, - R_HRES, - R_HSYNC_START, - R_HSYNC_END, - R_HSCAN, - R_VRES, - R_VSYNC_START, - R_VSYNC_END, - R_VSCAN, - R_BASEADDRESS, - R_BASEADDRESS_ACT, - R_BURST_COUNT, - R_DDC, - R_SOURCE_CLOCK, - R_MAX -}; - -enum { - CTRL_RESET = (1<<0), -}; - -#define TYPE_MILKYMIST_VGAFB "milkymist-vgafb" -#define MILKYMIST_VGAFB(obj) \ - OBJECT_CHECK(MilkymistVgafbState, (obj), TYPE_MILKYMIST_VGAFB) - -struct MilkymistVgafbState { - SysBusDevice parent_obj; - - MemoryRegion regs_region; - MemoryRegionSection fbsection; - QemuConsole *con; - - int invalidate; - uint32_t fb_offset; - uint32_t fb_mask; - - uint32_t regs[R_MAX]; -}; -typedef struct MilkymistVgafbState MilkymistVgafbState; - -static int vgafb_enabled(MilkymistVgafbState *s) -{ - return !(s->regs[R_CTRL] & CTRL_RESET); -} - -static void vgafb_update_display(void *opaque) -{ - MilkymistVgafbState *s = opaque; - SysBusDevice *sbd; - DisplaySurface *surface = qemu_console_surface(s->con); - int src_width; - int first = 0; - int last = 0; - drawfn fn; - - if (!vgafb_enabled(s)) { - return; - } - - sbd = SYS_BUS_DEVICE(s); - int dest_width = s->regs[R_HRES]; - - switch (surface_bits_per_pixel(surface)) { - case 0: - return; - case 8: - fn = draw_line_8; - break; - case 15: - fn = draw_line_15; - dest_width *= 2; - break; - case 16: - fn = draw_line_16; - dest_width *= 2; - break; - case 24: - fn = draw_line_24; - dest_width *= 3; - break; - case 32: - fn = draw_line_32; - dest_width *= 4; - break; - default: - hw_error("milkymist_vgafb: bad color depth\n"); - break; - } - - src_width = s->regs[R_HRES] * 2; - if (s->invalidate) { - framebuffer_update_memory_section(&s->fbsection, - sysbus_address_space(sbd), - s->regs[R_BASEADDRESS] + s->fb_offset, - s->regs[R_VRES], src_width); - } - - framebuffer_update_display(surface, &s->fbsection, - s->regs[R_HRES], - s->regs[R_VRES], - src_width, - dest_width, - 0, - s->invalidate, - fn, - NULL, - &first, &last); - - if (first >= 0) { - dpy_gfx_update(s->con, 0, first, s->regs[R_HRES], last - first + 1); - } - s->invalidate = 0; -} - -static void vgafb_invalidate_display(void *opaque) -{ - MilkymistVgafbState *s = opaque; - s->invalidate = 1; -} - -static void vgafb_resize(MilkymistVgafbState *s) -{ - if (!vgafb_enabled(s)) { - return; - } - - qemu_console_resize(s->con, s->regs[R_HRES], s->regs[R_VRES]); - s->invalidate = 1; -} - -static uint64_t vgafb_read(void *opaque, hwaddr addr, - unsigned size) -{ - MilkymistVgafbState *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_CTRL: - case R_HRES: - case R_HSYNC_START: - case R_HSYNC_END: - case R_HSCAN: - case R_VRES: - case R_VSYNC_START: - case R_VSYNC_END: - case R_VSCAN: - case R_BASEADDRESS: - case R_BURST_COUNT: - case R_DDC: - case R_SOURCE_CLOCK: - r = s->regs[addr]; - break; - case R_BASEADDRESS_ACT: - r = s->regs[R_BASEADDRESS]; - break; - - default: - error_report("milkymist_vgafb: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_milkymist_vgafb_memory_read(addr << 2, r); - - return r; -} - -static void vgafb_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - MilkymistVgafbState *s = opaque; - - trace_milkymist_vgafb_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_CTRL: - s->regs[addr] = value; - vgafb_resize(s); - break; - case R_HSYNC_START: - case R_HSYNC_END: - case R_HSCAN: - case R_VSYNC_START: - case R_VSYNC_END: - case R_VSCAN: - case R_BURST_COUNT: - case R_DDC: - case R_SOURCE_CLOCK: - s->regs[addr] = value; - break; - case R_BASEADDRESS: - if (value & 0x1f) { - error_report("milkymist_vgafb: framebuffer base address have to " - "be 32 byte aligned"); - break; - } - s->regs[addr] = value & s->fb_mask; - s->invalidate = 1; - break; - case R_HRES: - case R_VRES: - s->regs[addr] = value; - vgafb_resize(s); - break; - case R_BASEADDRESS_ACT: - error_report("milkymist_vgafb: write to read-only register 0x" - TARGET_FMT_plx, addr << 2); - break; - - default: - error_report("milkymist_vgafb: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } -} - -static const MemoryRegionOps vgafb_mmio_ops = { - .read = vgafb_read, - .write = vgafb_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void milkymist_vgafb_reset(DeviceState *d) -{ - MilkymistVgafbState *s = MILKYMIST_VGAFB(d); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } - - /* defaults */ - s->regs[R_CTRL] = CTRL_RESET; - s->regs[R_HRES] = 640; - s->regs[R_VRES] = 480; - s->regs[R_BASEADDRESS] = 0; -} - -static const GraphicHwOps vgafb_ops = { - .invalidate = vgafb_invalidate_display, - .gfx_update = vgafb_update_display, -}; - -static int milkymist_vgafb_init(SysBusDevice *dev) -{ - MilkymistVgafbState *s = MILKYMIST_VGAFB(dev); - - memory_region_init_io(&s->regs_region, OBJECT(s), &vgafb_mmio_ops, s, - "milkymist-vgafb", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); - - s->con = graphic_console_init(DEVICE(dev), 0, &vgafb_ops, s); - - return 0; -} - -static int vgafb_post_load(void *opaque, int version_id) -{ - vgafb_invalidate_display(opaque); - return 0; -} - -static const VMStateDescription vmstate_milkymist_vgafb = { - .name = "milkymist-vgafb", - .version_id = 1, - .minimum_version_id = 1, - .post_load = vgafb_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, MilkymistVgafbState, R_MAX), - VMSTATE_END_OF_LIST() - } -}; - -static Property milkymist_vgafb_properties[] = { - DEFINE_PROP_UINT32("fb_offset", MilkymistVgafbState, fb_offset, 0x0), - DEFINE_PROP_UINT32("fb_mask", MilkymistVgafbState, fb_mask, 0xffffffff), - DEFINE_PROP_END_OF_LIST(), -}; - -static void milkymist_vgafb_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = milkymist_vgafb_init; - dc->reset = milkymist_vgafb_reset; - dc->vmsd = &vmstate_milkymist_vgafb; - dc->props = milkymist_vgafb_properties; -} - -static const TypeInfo milkymist_vgafb_info = { - .name = TYPE_MILKYMIST_VGAFB, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MilkymistVgafbState), - .class_init = milkymist_vgafb_class_init, -}; - -static void milkymist_vgafb_register_types(void) -{ - type_register_static(&milkymist_vgafb_info); -} - -type_init(milkymist_vgafb_register_types) diff --git a/qemu/hw/display/milkymist-vgafb_template.h b/qemu/hw/display/milkymist-vgafb_template.h deleted file mode 100644 index 48837809e..000000000 --- a/qemu/hw/display/milkymist-vgafb_template.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * QEMU model of the Milkymist VGA framebuffer. - * - * Copyright (c) 2010 Michael Walle - * - * 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 . - * - */ - -#if BITS == 8 -#define COPY_PIXEL(to, r, g, b) \ - do { \ - *to = rgb_to_pixel8(r, g, b); \ - to += 1; \ - } while (0) -#elif BITS == 15 -#define COPY_PIXEL(to, r, g, b) \ - do { \ - *(uint16_t *)to = rgb_to_pixel15(r, g, b); \ - to += 2; \ - } while (0) -#elif BITS == 16 -#define COPY_PIXEL(to, r, g, b) \ - do { \ - *(uint16_t *)to = rgb_to_pixel16(r, g, b); \ - to += 2; \ - } while (0) -#elif BITS == 24 -#define COPY_PIXEL(to, r, g, b) \ - do { \ - uint32_t tmp = rgb_to_pixel24(r, g, b); \ - *(to++) = tmp & 0xff; \ - *(to++) = (tmp >> 8) & 0xff; \ - *(to++) = (tmp >> 16) & 0xff; \ - } while (0) -#elif BITS == 32 -#define COPY_PIXEL(to, r, g, b) \ - do { \ - *(uint32_t *)to = rgb_to_pixel32(r, g, b); \ - to += 4; \ - } while (0) -#else -#error unknown bit depth -#endif - -static void glue(draw_line_, BITS)(void *opaque, uint8_t *d, const uint8_t *s, - int width, int deststep) -{ - uint16_t rgb565; - uint8_t r, g, b; - - while (width--) { - rgb565 = lduw_be_p(s); - r = ((rgb565 >> 11) & 0x1f) << 3; - g = ((rgb565 >> 5) & 0x3f) << 2; - b = ((rgb565 >> 0) & 0x1f) << 3; - COPY_PIXEL(d, r, g, b); - s += 2; - } -} - -#undef BITS -#undef COPY_PIXEL diff --git a/qemu/hw/display/omap_dss.c b/qemu/hw/display/omap_dss.c deleted file mode 100644 index 783e9e131..000000000 --- a/qemu/hw/display/omap_dss.c +++ /dev/null @@ -1,1091 +0,0 @@ -/* - * OMAP2 Display Subsystem. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "ui/console.h" -#include "hw/arm/omap.h" - -struct omap_dss_s { - qemu_irq irq; - qemu_irq drq; - DisplayState *state; - MemoryRegion iomem_diss1, iomem_disc1, iomem_rfbi1, iomem_venc1, iomem_im3; - - int autoidle; - int control; - int enable; - - struct omap_dss_panel_s { - int enable; - int nx; - int ny; - - int x; - int y; - } dig, lcd; - - struct { - uint32_t idlemode; - uint32_t irqst; - uint32_t irqen; - uint32_t control; - uint32_t config; - uint32_t capable; - uint32_t timing[4]; - int line; - uint32_t bg[2]; - uint32_t trans[2]; - - struct omap_dss_plane_s { - int enable; - int bpp; - int posx; - int posy; - int nx; - int ny; - - hwaddr addr[3]; - - uint32_t attr; - uint32_t tresh; - int rowinc; - int colinc; - int wininc; - } l[3]; - - int invalidate; - uint16_t palette[256]; - } dispc; - - struct { - int idlemode; - uint32_t control; - int enable; - int pixels; - int busy; - int skiplines; - uint16_t rxbuf; - uint32_t config[2]; - uint32_t time[4]; - uint32_t data[6]; - uint16_t vsync; - uint16_t hsync; - struct rfbi_chip_s *chip[2]; - } rfbi; -}; - -static void omap_dispc_interrupt_update(struct omap_dss_s *s) -{ - qemu_set_irq(s->irq, s->dispc.irqst & s->dispc.irqen); -} - -static void omap_rfbi_reset(struct omap_dss_s *s) -{ - s->rfbi.idlemode = 0; - s->rfbi.control = 2; - s->rfbi.enable = 0; - s->rfbi.pixels = 0; - s->rfbi.skiplines = 0; - s->rfbi.busy = 0; - s->rfbi.config[0] = 0x00310000; - s->rfbi.config[1] = 0x00310000; - s->rfbi.time[0] = 0; - s->rfbi.time[1] = 0; - s->rfbi.time[2] = 0; - s->rfbi.time[3] = 0; - s->rfbi.data[0] = 0; - s->rfbi.data[1] = 0; - s->rfbi.data[2] = 0; - s->rfbi.data[3] = 0; - s->rfbi.data[4] = 0; - s->rfbi.data[5] = 0; - s->rfbi.vsync = 0; - s->rfbi.hsync = 0; -} - -void omap_dss_reset(struct omap_dss_s *s) -{ - s->autoidle = 0; - s->control = 0; - s->enable = 0; - - s->dig.enable = 0; - s->dig.nx = 1; - s->dig.ny = 1; - - s->lcd.enable = 0; - s->lcd.nx = 1; - s->lcd.ny = 1; - - s->dispc.idlemode = 0; - s->dispc.irqst = 0; - s->dispc.irqen = 0; - s->dispc.control = 0; - s->dispc.config = 0; - s->dispc.capable = 0x161; - s->dispc.timing[0] = 0; - s->dispc.timing[1] = 0; - s->dispc.timing[2] = 0; - s->dispc.timing[3] = 0; - s->dispc.line = 0; - s->dispc.bg[0] = 0; - s->dispc.bg[1] = 0; - s->dispc.trans[0] = 0; - s->dispc.trans[1] = 0; - - s->dispc.l[0].enable = 0; - s->dispc.l[0].bpp = 0; - s->dispc.l[0].addr[0] = 0; - s->dispc.l[0].addr[1] = 0; - s->dispc.l[0].addr[2] = 0; - s->dispc.l[0].posx = 0; - s->dispc.l[0].posy = 0; - s->dispc.l[0].nx = 1; - s->dispc.l[0].ny = 1; - s->dispc.l[0].attr = 0; - s->dispc.l[0].tresh = 0; - s->dispc.l[0].rowinc = 1; - s->dispc.l[0].colinc = 1; - s->dispc.l[0].wininc = 0; - - omap_rfbi_reset(s); - omap_dispc_interrupt_update(s); -} - -static uint64_t omap_diss_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* DSS_REVISIONNUMBER */ - return 0x20; - - case 0x10: /* DSS_SYSCONFIG */ - return s->autoidle; - - case 0x14: /* DSS_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x40: /* DSS_CONTROL */ - return s->control; - - case 0x50: /* DSS_PSA_LCD_REG_1 */ - case 0x54: /* DSS_PSA_LCD_REG_2 */ - case 0x58: /* DSS_PSA_VIDEO_REG */ - /* TODO: fake some values when appropriate s->control bits are set */ - return 0; - - case 0x5c: /* DSS_STATUS */ - return 1 + (s->control & 1); - - default: - break; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_diss_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x00: /* DSS_REVISIONNUMBER */ - case 0x14: /* DSS_SYSSTATUS */ - case 0x50: /* DSS_PSA_LCD_REG_1 */ - case 0x54: /* DSS_PSA_LCD_REG_2 */ - case 0x58: /* DSS_PSA_VIDEO_REG */ - case 0x5c: /* DSS_STATUS */ - OMAP_RO_REG(addr); - break; - - case 0x10: /* DSS_SYSCONFIG */ - if (value & 2) /* SOFTRESET */ - omap_dss_reset(s); - s->autoidle = value & 1; - break; - - case 0x40: /* DSS_CONTROL */ - s->control = value & 0x3dd; - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_diss_ops = { - .read = omap_diss_read, - .write = omap_diss_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t omap_disc_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x000: /* DISPC_REVISION */ - return 0x20; - - case 0x010: /* DISPC_SYSCONFIG */ - return s->dispc.idlemode; - - case 0x014: /* DISPC_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x018: /* DISPC_IRQSTATUS */ - return s->dispc.irqst; - - case 0x01c: /* DISPC_IRQENABLE */ - return s->dispc.irqen; - - case 0x040: /* DISPC_CONTROL */ - return s->dispc.control; - - case 0x044: /* DISPC_CONFIG */ - return s->dispc.config; - - case 0x048: /* DISPC_CAPABLE */ - return s->dispc.capable; - - case 0x04c: /* DISPC_DEFAULT_COLOR0 */ - return s->dispc.bg[0]; - case 0x050: /* DISPC_DEFAULT_COLOR1 */ - return s->dispc.bg[1]; - case 0x054: /* DISPC_TRANS_COLOR0 */ - return s->dispc.trans[0]; - case 0x058: /* DISPC_TRANS_COLOR1 */ - return s->dispc.trans[1]; - - case 0x05c: /* DISPC_LINE_STATUS */ - return 0x7ff; - case 0x060: /* DISPC_LINE_NUMBER */ - return s->dispc.line; - - case 0x064: /* DISPC_TIMING_H */ - return s->dispc.timing[0]; - case 0x068: /* DISPC_TIMING_V */ - return s->dispc.timing[1]; - case 0x06c: /* DISPC_POL_FREQ */ - return s->dispc.timing[2]; - case 0x070: /* DISPC_DIVISOR */ - return s->dispc.timing[3]; - - case 0x078: /* DISPC_SIZE_DIG */ - return ((s->dig.ny - 1) << 16) | (s->dig.nx - 1); - case 0x07c: /* DISPC_SIZE_LCD */ - return ((s->lcd.ny - 1) << 16) | (s->lcd.nx - 1); - - case 0x080: /* DISPC_GFX_BA0 */ - return s->dispc.l[0].addr[0]; - case 0x084: /* DISPC_GFX_BA1 */ - return s->dispc.l[0].addr[1]; - case 0x088: /* DISPC_GFX_POSITION */ - return (s->dispc.l[0].posy << 16) | s->dispc.l[0].posx; - case 0x08c: /* DISPC_GFX_SIZE */ - return ((s->dispc.l[0].ny - 1) << 16) | (s->dispc.l[0].nx - 1); - case 0x0a0: /* DISPC_GFX_ATTRIBUTES */ - return s->dispc.l[0].attr; - case 0x0a4: /* DISPC_GFX_FIFO_TRESHOLD */ - return s->dispc.l[0].tresh; - case 0x0a8: /* DISPC_GFX_FIFO_SIZE_STATUS */ - return 256; - case 0x0ac: /* DISPC_GFX_ROW_INC */ - return s->dispc.l[0].rowinc; - case 0x0b0: /* DISPC_GFX_PIXEL_INC */ - return s->dispc.l[0].colinc; - case 0x0b4: /* DISPC_GFX_WINDOW_SKIP */ - return s->dispc.l[0].wininc; - case 0x0b8: /* DISPC_GFX_TABLE_BA */ - return s->dispc.l[0].addr[2]; - - case 0x0bc: /* DISPC_VID1_BA0 */ - case 0x0c0: /* DISPC_VID1_BA1 */ - case 0x0c4: /* DISPC_VID1_POSITION */ - case 0x0c8: /* DISPC_VID1_SIZE */ - case 0x0cc: /* DISPC_VID1_ATTRIBUTES */ - case 0x0d0: /* DISPC_VID1_FIFO_TRESHOLD */ - case 0x0d4: /* DISPC_VID1_FIFO_SIZE_STATUS */ - case 0x0d8: /* DISPC_VID1_ROW_INC */ - case 0x0dc: /* DISPC_VID1_PIXEL_INC */ - case 0x0e0: /* DISPC_VID1_FIR */ - case 0x0e4: /* DISPC_VID1_PICTURE_SIZE */ - case 0x0e8: /* DISPC_VID1_ACCU0 */ - case 0x0ec: /* DISPC_VID1_ACCU1 */ - case 0x0f0 ... 0x140: /* DISPC_VID1_FIR_COEF, DISPC_VID1_CONV_COEF */ - case 0x14c: /* DISPC_VID2_BA0 */ - case 0x150: /* DISPC_VID2_BA1 */ - case 0x154: /* DISPC_VID2_POSITION */ - case 0x158: /* DISPC_VID2_SIZE */ - case 0x15c: /* DISPC_VID2_ATTRIBUTES */ - case 0x160: /* DISPC_VID2_FIFO_TRESHOLD */ - case 0x164: /* DISPC_VID2_FIFO_SIZE_STATUS */ - case 0x168: /* DISPC_VID2_ROW_INC */ - case 0x16c: /* DISPC_VID2_PIXEL_INC */ - case 0x170: /* DISPC_VID2_FIR */ - case 0x174: /* DISPC_VID2_PICTURE_SIZE */ - case 0x178: /* DISPC_VID2_ACCU0 */ - case 0x17c: /* DISPC_VID2_ACCU1 */ - case 0x180 ... 0x1d0: /* DISPC_VID2_FIR_COEF, DISPC_VID2_CONV_COEF */ - case 0x1d4: /* DISPC_DATA_CYCLE1 */ - case 0x1d8: /* DISPC_DATA_CYCLE2 */ - case 0x1dc: /* DISPC_DATA_CYCLE3 */ - return 0; - - default: - break; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_disc_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x010: /* DISPC_SYSCONFIG */ - if (value & 2) /* SOFTRESET */ - omap_dss_reset(s); - s->dispc.idlemode = value & 0x301b; - break; - - case 0x018: /* DISPC_IRQSTATUS */ - s->dispc.irqst &= ~value; - omap_dispc_interrupt_update(s); - break; - - case 0x01c: /* DISPC_IRQENABLE */ - s->dispc.irqen = value & 0xffff; - omap_dispc_interrupt_update(s); - break; - - case 0x040: /* DISPC_CONTROL */ - s->dispc.control = value & 0x07ff9fff; - s->dig.enable = (value >> 1) & 1; - s->lcd.enable = (value >> 0) & 1; - if (value & (1 << 12)) /* OVERLAY_OPTIMIZATION */ - if (!((s->dispc.l[1].attr | s->dispc.l[2].attr) & 1)) { - fprintf(stderr, "%s: Overlay Optimization when no overlay " - "region effectively exists leads to " - "unpredictable behaviour!\n", __func__); - } - if (value & (1 << 6)) { /* GODIGITAL */ - /* XXX: Shadowed fields are: - * s->dispc.config - * s->dispc.capable - * s->dispc.bg[0] - * s->dispc.bg[1] - * s->dispc.trans[0] - * s->dispc.trans[1] - * s->dispc.line - * s->dispc.timing[0] - * s->dispc.timing[1] - * s->dispc.timing[2] - * s->dispc.timing[3] - * s->lcd.nx - * s->lcd.ny - * s->dig.nx - * s->dig.ny - * s->dispc.l[0].addr[0] - * s->dispc.l[0].addr[1] - * s->dispc.l[0].addr[2] - * s->dispc.l[0].posx - * s->dispc.l[0].posy - * s->dispc.l[0].nx - * s->dispc.l[0].ny - * s->dispc.l[0].tresh - * s->dispc.l[0].rowinc - * s->dispc.l[0].colinc - * s->dispc.l[0].wininc - * All they need to be loaded here from their shadow registers. - */ - } - if (value & (1 << 5)) { /* GOLCD */ - /* XXX: Likewise for LCD here. */ - } - s->dispc.invalidate = 1; - break; - - case 0x044: /* DISPC_CONFIG */ - s->dispc.config = value & 0x3fff; - /* XXX: - * bits 2:1 (LOADMODE) reset to 0 after set to 1 and palette loaded - * bits 2:1 (LOADMODE) reset to 2 after set to 3 and palette loaded - */ - s->dispc.invalidate = 1; - break; - - case 0x048: /* DISPC_CAPABLE */ - s->dispc.capable = value & 0x3ff; - break; - - case 0x04c: /* DISPC_DEFAULT_COLOR0 */ - s->dispc.bg[0] = value & 0xffffff; - s->dispc.invalidate = 1; - break; - case 0x050: /* DISPC_DEFAULT_COLOR1 */ - s->dispc.bg[1] = value & 0xffffff; - s->dispc.invalidate = 1; - break; - case 0x054: /* DISPC_TRANS_COLOR0 */ - s->dispc.trans[0] = value & 0xffffff; - s->dispc.invalidate = 1; - break; - case 0x058: /* DISPC_TRANS_COLOR1 */ - s->dispc.trans[1] = value & 0xffffff; - s->dispc.invalidate = 1; - break; - - case 0x060: /* DISPC_LINE_NUMBER */ - s->dispc.line = value & 0x7ff; - break; - - case 0x064: /* DISPC_TIMING_H */ - s->dispc.timing[0] = value & 0x0ff0ff3f; - break; - case 0x068: /* DISPC_TIMING_V */ - s->dispc.timing[1] = value & 0x0ff0ff3f; - break; - case 0x06c: /* DISPC_POL_FREQ */ - s->dispc.timing[2] = value & 0x0003ffff; - break; - case 0x070: /* DISPC_DIVISOR */ - s->dispc.timing[3] = value & 0x00ff00ff; - break; - - case 0x078: /* DISPC_SIZE_DIG */ - s->dig.nx = ((value >> 0) & 0x7ff) + 1; /* PPL */ - s->dig.ny = ((value >> 16) & 0x7ff) + 1; /* LPP */ - s->dispc.invalidate = 1; - break; - case 0x07c: /* DISPC_SIZE_LCD */ - s->lcd.nx = ((value >> 0) & 0x7ff) + 1; /* PPL */ - s->lcd.ny = ((value >> 16) & 0x7ff) + 1; /* LPP */ - s->dispc.invalidate = 1; - break; - case 0x080: /* DISPC_GFX_BA0 */ - s->dispc.l[0].addr[0] = (hwaddr) value; - s->dispc.invalidate = 1; - break; - case 0x084: /* DISPC_GFX_BA1 */ - s->dispc.l[0].addr[1] = (hwaddr) value; - s->dispc.invalidate = 1; - break; - case 0x088: /* DISPC_GFX_POSITION */ - s->dispc.l[0].posx = ((value >> 0) & 0x7ff); /* GFXPOSX */ - s->dispc.l[0].posy = ((value >> 16) & 0x7ff); /* GFXPOSY */ - s->dispc.invalidate = 1; - break; - case 0x08c: /* DISPC_GFX_SIZE */ - s->dispc.l[0].nx = ((value >> 0) & 0x7ff) + 1; /* GFXSIZEX */ - s->dispc.l[0].ny = ((value >> 16) & 0x7ff) + 1; /* GFXSIZEY */ - s->dispc.invalidate = 1; - break; - case 0x0a0: /* DISPC_GFX_ATTRIBUTES */ - s->dispc.l[0].attr = value & 0x7ff; - if (value & (3 << 9)) - fprintf(stderr, "%s: Big-endian pixel format not supported\n", - __FUNCTION__); - s->dispc.l[0].enable = value & 1; - s->dispc.l[0].bpp = (value >> 1) & 0xf; - s->dispc.invalidate = 1; - break; - case 0x0a4: /* DISPC_GFX_FIFO_TRESHOLD */ - s->dispc.l[0].tresh = value & 0x01ff01ff; - break; - case 0x0ac: /* DISPC_GFX_ROW_INC */ - s->dispc.l[0].rowinc = value; - s->dispc.invalidate = 1; - break; - case 0x0b0: /* DISPC_GFX_PIXEL_INC */ - s->dispc.l[0].colinc = value; - s->dispc.invalidate = 1; - break; - case 0x0b4: /* DISPC_GFX_WINDOW_SKIP */ - s->dispc.l[0].wininc = value; - break; - case 0x0b8: /* DISPC_GFX_TABLE_BA */ - s->dispc.l[0].addr[2] = (hwaddr) value; - s->dispc.invalidate = 1; - break; - - case 0x0bc: /* DISPC_VID1_BA0 */ - case 0x0c0: /* DISPC_VID1_BA1 */ - case 0x0c4: /* DISPC_VID1_POSITION */ - case 0x0c8: /* DISPC_VID1_SIZE */ - case 0x0cc: /* DISPC_VID1_ATTRIBUTES */ - case 0x0d0: /* DISPC_VID1_FIFO_TRESHOLD */ - case 0x0d8: /* DISPC_VID1_ROW_INC */ - case 0x0dc: /* DISPC_VID1_PIXEL_INC */ - case 0x0e0: /* DISPC_VID1_FIR */ - case 0x0e4: /* DISPC_VID1_PICTURE_SIZE */ - case 0x0e8: /* DISPC_VID1_ACCU0 */ - case 0x0ec: /* DISPC_VID1_ACCU1 */ - case 0x0f0 ... 0x140: /* DISPC_VID1_FIR_COEF, DISPC_VID1_CONV_COEF */ - case 0x14c: /* DISPC_VID2_BA0 */ - case 0x150: /* DISPC_VID2_BA1 */ - case 0x154: /* DISPC_VID2_POSITION */ - case 0x158: /* DISPC_VID2_SIZE */ - case 0x15c: /* DISPC_VID2_ATTRIBUTES */ - case 0x160: /* DISPC_VID2_FIFO_TRESHOLD */ - case 0x168: /* DISPC_VID2_ROW_INC */ - case 0x16c: /* DISPC_VID2_PIXEL_INC */ - case 0x170: /* DISPC_VID2_FIR */ - case 0x174: /* DISPC_VID2_PICTURE_SIZE */ - case 0x178: /* DISPC_VID2_ACCU0 */ - case 0x17c: /* DISPC_VID2_ACCU1 */ - case 0x180 ... 0x1d0: /* DISPC_VID2_FIR_COEF, DISPC_VID2_CONV_COEF */ - case 0x1d4: /* DISPC_DATA_CYCLE1 */ - case 0x1d8: /* DISPC_DATA_CYCLE2 */ - case 0x1dc: /* DISPC_DATA_CYCLE3 */ - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_disc_ops = { - .read = omap_disc_read, - .write = omap_disc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_rfbi_transfer_stop(struct omap_dss_s *s) -{ - if (!s->rfbi.busy) - return; - - /* TODO: in non-Bypass mode we probably need to just deassert the DRQ. */ - - s->rfbi.busy = 0; -} - -static void omap_rfbi_transfer_start(struct omap_dss_s *s) -{ - void *data; - hwaddr len; - hwaddr data_addr; - int pitch; - static void *bounce_buffer; - static hwaddr bounce_len; - - if (!s->rfbi.enable || s->rfbi.busy) - return; - - if (s->rfbi.control & (1 << 1)) { /* BYPASS */ - /* TODO: in non-Bypass mode we probably need to just assert the - * DRQ and wait for DMA to write the pixels. */ - fprintf(stderr, "%s: Bypass mode unimplemented\n", __FUNCTION__); - return; - } - - if (!(s->dispc.control & (1 << 11))) /* RFBIMODE */ - return; - /* TODO: check that LCD output is enabled in DISPC. */ - - s->rfbi.busy = 1; - - len = s->rfbi.pixels * 2; - - data_addr = s->dispc.l[0].addr[0]; - data = cpu_physical_memory_map(data_addr, &len, 0); - if (data && len != s->rfbi.pixels * 2) { - cpu_physical_memory_unmap(data, len, 0, 0); - data = NULL; - len = s->rfbi.pixels * 2; - } - if (!data) { - if (len > bounce_len) { - bounce_buffer = g_realloc(bounce_buffer, len); - } - data = bounce_buffer; - cpu_physical_memory_read(data_addr, data, len); - } - - /* TODO bpp */ - s->rfbi.pixels = 0; - - /* TODO: negative values */ - pitch = s->dispc.l[0].nx + (s->dispc.l[0].rowinc - 1) / 2; - - if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) - s->rfbi.chip[0]->block(s->rfbi.chip[0]->opaque, 1, data, len, pitch); - if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) - s->rfbi.chip[1]->block(s->rfbi.chip[1]->opaque, 1, data, len, pitch); - - if (data != bounce_buffer) { - cpu_physical_memory_unmap(data, len, 0, len); - } - - omap_rfbi_transfer_stop(s); - - /* TODO */ - s->dispc.irqst |= 1; /* FRAMEDONE */ - omap_dispc_interrupt_update(s); -} - -static uint64_t omap_rfbi_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* RFBI_REVISION */ - return 0x10; - - case 0x10: /* RFBI_SYSCONFIG */ - return s->rfbi.idlemode; - - case 0x14: /* RFBI_SYSSTATUS */ - return 1 | (s->rfbi.busy << 8); /* RESETDONE */ - - case 0x40: /* RFBI_CONTROL */ - return s->rfbi.control; - - case 0x44: /* RFBI_PIXELCNT */ - return s->rfbi.pixels; - - case 0x48: /* RFBI_LINE_NUMBER */ - return s->rfbi.skiplines; - - case 0x58: /* RFBI_READ */ - case 0x5c: /* RFBI_STATUS */ - return s->rfbi.rxbuf; - - case 0x60: /* RFBI_CONFIG0 */ - return s->rfbi.config[0]; - case 0x64: /* RFBI_ONOFF_TIME0 */ - return s->rfbi.time[0]; - case 0x68: /* RFBI_CYCLE_TIME0 */ - return s->rfbi.time[1]; - case 0x6c: /* RFBI_DATA_CYCLE1_0 */ - return s->rfbi.data[0]; - case 0x70: /* RFBI_DATA_CYCLE2_0 */ - return s->rfbi.data[1]; - case 0x74: /* RFBI_DATA_CYCLE3_0 */ - return s->rfbi.data[2]; - - case 0x78: /* RFBI_CONFIG1 */ - return s->rfbi.config[1]; - case 0x7c: /* RFBI_ONOFF_TIME1 */ - return s->rfbi.time[2]; - case 0x80: /* RFBI_CYCLE_TIME1 */ - return s->rfbi.time[3]; - case 0x84: /* RFBI_DATA_CYCLE1_1 */ - return s->rfbi.data[3]; - case 0x88: /* RFBI_DATA_CYCLE2_1 */ - return s->rfbi.data[4]; - case 0x8c: /* RFBI_DATA_CYCLE3_1 */ - return s->rfbi.data[5]; - - case 0x90: /* RFBI_VSYNC_WIDTH */ - return s->rfbi.vsync; - case 0x94: /* RFBI_HSYNC_WIDTH */ - return s->rfbi.hsync; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_rfbi_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x10: /* RFBI_SYSCONFIG */ - if (value & 2) /* SOFTRESET */ - omap_rfbi_reset(s); - s->rfbi.idlemode = value & 0x19; - break; - - case 0x40: /* RFBI_CONTROL */ - s->rfbi.control = value & 0xf; - s->rfbi.enable = value & 1; - if (value & (1 << 4) && /* ITE */ - !(s->rfbi.config[0] & s->rfbi.config[1] & 0xc)) - omap_rfbi_transfer_start(s); - break; - - case 0x44: /* RFBI_PIXELCNT */ - s->rfbi.pixels = value; - break; - - case 0x48: /* RFBI_LINE_NUMBER */ - s->rfbi.skiplines = value & 0x7ff; - break; - - case 0x4c: /* RFBI_CMD */ - if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) - s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 0, value & 0xffff); - if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) - s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 0, value & 0xffff); - break; - case 0x50: /* RFBI_PARAM */ - if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) - s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value & 0xffff); - if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) - s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value & 0xffff); - break; - case 0x54: /* RFBI_DATA */ - /* TODO: take into account the format set up in s->rfbi.config[?] and - * s->rfbi.data[?], but special-case the most usual scenario so that - * speed doesn't suffer. */ - if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) { - s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value & 0xffff); - s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value >> 16); - } - if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) { - s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value & 0xffff); - s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value >> 16); - } - if (!-- s->rfbi.pixels) - omap_rfbi_transfer_stop(s); - break; - case 0x58: /* RFBI_READ */ - if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) - s->rfbi.rxbuf = s->rfbi.chip[0]->read(s->rfbi.chip[0]->opaque, 1); - else if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) - s->rfbi.rxbuf = s->rfbi.chip[1]->read(s->rfbi.chip[1]->opaque, 1); - if (!-- s->rfbi.pixels) - omap_rfbi_transfer_stop(s); - break; - - case 0x5c: /* RFBI_STATUS */ - if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) - s->rfbi.rxbuf = s->rfbi.chip[0]->read(s->rfbi.chip[0]->opaque, 0); - else if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) - s->rfbi.rxbuf = s->rfbi.chip[1]->read(s->rfbi.chip[1]->opaque, 0); - if (!-- s->rfbi.pixels) - omap_rfbi_transfer_stop(s); - break; - - case 0x60: /* RFBI_CONFIG0 */ - s->rfbi.config[0] = value & 0x003f1fff; - break; - - case 0x64: /* RFBI_ONOFF_TIME0 */ - s->rfbi.time[0] = value & 0x3fffffff; - break; - case 0x68: /* RFBI_CYCLE_TIME0 */ - s->rfbi.time[1] = value & 0x0fffffff; - break; - case 0x6c: /* RFBI_DATA_CYCLE1_0 */ - s->rfbi.data[0] = value & 0x0f1f0f1f; - break; - case 0x70: /* RFBI_DATA_CYCLE2_0 */ - s->rfbi.data[1] = value & 0x0f1f0f1f; - break; - case 0x74: /* RFBI_DATA_CYCLE3_0 */ - s->rfbi.data[2] = value & 0x0f1f0f1f; - break; - case 0x78: /* RFBI_CONFIG1 */ - s->rfbi.config[1] = value & 0x003f1fff; - break; - - case 0x7c: /* RFBI_ONOFF_TIME1 */ - s->rfbi.time[2] = value & 0x3fffffff; - break; - case 0x80: /* RFBI_CYCLE_TIME1 */ - s->rfbi.time[3] = value & 0x0fffffff; - break; - case 0x84: /* RFBI_DATA_CYCLE1_1 */ - s->rfbi.data[3] = value & 0x0f1f0f1f; - break; - case 0x88: /* RFBI_DATA_CYCLE2_1 */ - s->rfbi.data[4] = value & 0x0f1f0f1f; - break; - case 0x8c: /* RFBI_DATA_CYCLE3_1 */ - s->rfbi.data[5] = value & 0x0f1f0f1f; - break; - - case 0x90: /* RFBI_VSYNC_WIDTH */ - s->rfbi.vsync = value & 0xffff; - break; - case 0x94: /* RFBI_HSYNC_WIDTH */ - s->rfbi.hsync = value & 0xffff; - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_rfbi_ops = { - .read = omap_rfbi_read, - .write = omap_rfbi_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t omap_venc_read(void *opaque, hwaddr addr, - unsigned size) -{ - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* REV_ID */ - case 0x04: /* STATUS */ - case 0x08: /* F_CONTROL */ - case 0x10: /* VIDOUT_CTRL */ - case 0x14: /* SYNC_CTRL */ - case 0x1c: /* LLEN */ - case 0x20: /* FLENS */ - case 0x24: /* HFLTR_CTRL */ - case 0x28: /* CC_CARR_WSS_CARR */ - case 0x2c: /* C_PHASE */ - case 0x30: /* GAIN_U */ - case 0x34: /* GAIN_V */ - case 0x38: /* GAIN_Y */ - case 0x3c: /* BLACK_LEVEL */ - case 0x40: /* BLANK_LEVEL */ - case 0x44: /* X_COLOR */ - case 0x48: /* M_CONTROL */ - case 0x4c: /* BSTAMP_WSS_DATA */ - case 0x50: /* S_CARR */ - case 0x54: /* LINE21 */ - case 0x58: /* LN_SEL */ - case 0x5c: /* L21__WC_CTL */ - case 0x60: /* HTRIGGER_VTRIGGER */ - case 0x64: /* SAVID__EAVID */ - case 0x68: /* FLEN__FAL */ - case 0x6c: /* LAL__PHASE_RESET */ - case 0x70: /* HS_INT_START_STOP_X */ - case 0x74: /* HS_EXT_START_STOP_X */ - case 0x78: /* VS_INT_START_X */ - case 0x7c: /* VS_INT_STOP_X__VS_INT_START_Y */ - case 0x80: /* VS_INT_STOP_Y__VS_INT_START_X */ - case 0x84: /* VS_EXT_STOP_X__VS_EXT_START_Y */ - case 0x88: /* VS_EXT_STOP_Y */ - case 0x90: /* AVID_START_STOP_X */ - case 0x94: /* AVID_START_STOP_Y */ - case 0xa0: /* FID_INT_START_X__FID_INT_START_Y */ - case 0xa4: /* FID_INT_OFFSET_Y__FID_EXT_START_X */ - case 0xa8: /* FID_EXT_START_Y__FID_EXT_OFFSET_Y */ - case 0xb0: /* TVDETGP_INT_START_STOP_X */ - case 0xb4: /* TVDETGP_INT_START_STOP_Y */ - case 0xb8: /* GEN_CTRL */ - case 0xc4: /* DAC_TST__DAC_A */ - case 0xc8: /* DAC_B__DAC_C */ - return 0; - - default: - break; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_venc_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - if (size != 4) { - omap_badwidth_write32(opaque, addr, size); - return; - } - - switch (addr) { - case 0x08: /* F_CONTROL */ - case 0x10: /* VIDOUT_CTRL */ - case 0x14: /* SYNC_CTRL */ - case 0x1c: /* LLEN */ - case 0x20: /* FLENS */ - case 0x24: /* HFLTR_CTRL */ - case 0x28: /* CC_CARR_WSS_CARR */ - case 0x2c: /* C_PHASE */ - case 0x30: /* GAIN_U */ - case 0x34: /* GAIN_V */ - case 0x38: /* GAIN_Y */ - case 0x3c: /* BLACK_LEVEL */ - case 0x40: /* BLANK_LEVEL */ - case 0x44: /* X_COLOR */ - case 0x48: /* M_CONTROL */ - case 0x4c: /* BSTAMP_WSS_DATA */ - case 0x50: /* S_CARR */ - case 0x54: /* LINE21 */ - case 0x58: /* LN_SEL */ - case 0x5c: /* L21__WC_CTL */ - case 0x60: /* HTRIGGER_VTRIGGER */ - case 0x64: /* SAVID__EAVID */ - case 0x68: /* FLEN__FAL */ - case 0x6c: /* LAL__PHASE_RESET */ - case 0x70: /* HS_INT_START_STOP_X */ - case 0x74: /* HS_EXT_START_STOP_X */ - case 0x78: /* VS_INT_START_X */ - case 0x7c: /* VS_INT_STOP_X__VS_INT_START_Y */ - case 0x80: /* VS_INT_STOP_Y__VS_INT_START_X */ - case 0x84: /* VS_EXT_STOP_X__VS_EXT_START_Y */ - case 0x88: /* VS_EXT_STOP_Y */ - case 0x90: /* AVID_START_STOP_X */ - case 0x94: /* AVID_START_STOP_Y */ - case 0xa0: /* FID_INT_START_X__FID_INT_START_Y */ - case 0xa4: /* FID_INT_OFFSET_Y__FID_EXT_START_X */ - case 0xa8: /* FID_EXT_START_Y__FID_EXT_OFFSET_Y */ - case 0xb0: /* TVDETGP_INT_START_STOP_X */ - case 0xb4: /* TVDETGP_INT_START_STOP_Y */ - case 0xb8: /* GEN_CTRL */ - case 0xc4: /* DAC_TST__DAC_A */ - case 0xc8: /* DAC_B__DAC_C */ - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_venc_ops = { - .read = omap_venc_read, - .write = omap_venc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t omap_im3_read(void *opaque, hwaddr addr, - unsigned size) -{ - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x0a8: /* SBIMERRLOGA */ - case 0x0b0: /* SBIMERRLOG */ - case 0x190: /* SBIMSTATE */ - case 0x198: /* SBTMSTATE_L */ - case 0x19c: /* SBTMSTATE_H */ - case 0x1a8: /* SBIMCONFIG_L */ - case 0x1ac: /* SBIMCONFIG_H */ - case 0x1f8: /* SBID_L */ - case 0x1fc: /* SBID_H */ - return 0; - - default: - break; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_im3_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x0b0: /* SBIMERRLOG */ - case 0x190: /* SBIMSTATE */ - case 0x198: /* SBTMSTATE_L */ - case 0x19c: /* SBTMSTATE_H */ - case 0x1a8: /* SBIMCONFIG_L */ - case 0x1ac: /* SBIMCONFIG_H */ - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_im3_ops = { - .read = omap_im3_read, - .write = omap_im3_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_dss_s *omap_dss_init(struct omap_target_agent_s *ta, - MemoryRegion *sysmem, - hwaddr l3_base, - qemu_irq irq, qemu_irq drq, - omap_clk fck1, omap_clk fck2, omap_clk ck54m, - omap_clk ick1, omap_clk ick2) -{ - struct omap_dss_s *s = g_new0(struct omap_dss_s, 1); - - s->irq = irq; - s->drq = drq; - omap_dss_reset(s); - - memory_region_init_io(&s->iomem_diss1, NULL, &omap_diss_ops, s, "omap.diss1", - omap_l4_region_size(ta, 0)); - memory_region_init_io(&s->iomem_disc1, NULL, &omap_disc_ops, s, "omap.disc1", - omap_l4_region_size(ta, 1)); - memory_region_init_io(&s->iomem_rfbi1, NULL, &omap_rfbi_ops, s, "omap.rfbi1", - omap_l4_region_size(ta, 2)); - memory_region_init_io(&s->iomem_venc1, NULL, &omap_venc_ops, s, "omap.venc1", - omap_l4_region_size(ta, 3)); - memory_region_init_io(&s->iomem_im3, NULL, &omap_im3_ops, s, - "omap.im3", 0x1000); - - omap_l4_attach(ta, 0, &s->iomem_diss1); - omap_l4_attach(ta, 1, &s->iomem_disc1); - omap_l4_attach(ta, 2, &s->iomem_rfbi1); - omap_l4_attach(ta, 3, &s->iomem_venc1); - memory_region_add_subregion(sysmem, l3_base, &s->iomem_im3); - -#if 0 - s->state = graphic_console_init(omap_update_display, - omap_invalidate_display, omap_screen_dump, s); -#endif - - return s; -} - -void omap_rfbi_attach(struct omap_dss_s *s, int cs, struct rfbi_chip_s *chip) -{ - if (cs < 0 || cs > 1) - hw_error("%s: wrong CS %i\n", __FUNCTION__, cs); - s->rfbi.chip[cs] = chip; -} diff --git a/qemu/hw/display/omap_lcd_template.h b/qemu/hw/display/omap_lcd_template.h deleted file mode 100644 index f0ce71fd6..000000000 --- a/qemu/hw/display/omap_lcd_template.h +++ /dev/null @@ -1,175 +0,0 @@ -/* - * QEMU OMAP LCD Emulator templates - * - * Copyright (c) 2006 Andrzej Zaborowski - * - * 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 the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if DEPTH == 8 -# define BPP 1 -# define PIXEL_TYPE uint8_t -#elif DEPTH == 15 || DEPTH == 16 -# define BPP 2 -# define PIXEL_TYPE uint16_t -#elif DEPTH == 32 -# define BPP 4 -# define PIXEL_TYPE uint32_t -#else -# error unsupport depth -#endif - -/* - * 2-bit colour - */ -static void glue(draw_line2_, DEPTH)(void *opaque, - uint8_t *d, const uint8_t *s, int width, int deststep) -{ - uint16_t *pal = opaque; - uint8_t v, r, g, b; - - do { - v = ldub_p((void *) s); - r = (pal[v & 3] >> 4) & 0xf0; - g = pal[v & 3] & 0xf0; - b = (pal[v & 3] << 4) & 0xf0; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - d += BPP; - v >>= 2; - r = (pal[v & 3] >> 4) & 0xf0; - g = pal[v & 3] & 0xf0; - b = (pal[v & 3] << 4) & 0xf0; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - d += BPP; - v >>= 2; - r = (pal[v & 3] >> 4) & 0xf0; - g = pal[v & 3] & 0xf0; - b = (pal[v & 3] << 4) & 0xf0; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - d += BPP; - v >>= 2; - r = (pal[v & 3] >> 4) & 0xf0; - g = pal[v & 3] & 0xf0; - b = (pal[v & 3] << 4) & 0xf0; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - d += BPP; - s ++; - width -= 4; - } while (width > 0); -} - -/* - * 4-bit colour - */ -static void glue(draw_line4_, DEPTH)(void *opaque, - uint8_t *d, const uint8_t *s, int width, int deststep) -{ - uint16_t *pal = opaque; - uint8_t v, r, g, b; - - do { - v = ldub_p((void *) s); - r = (pal[v & 0xf] >> 4) & 0xf0; - g = pal[v & 0xf] & 0xf0; - b = (pal[v & 0xf] << 4) & 0xf0; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - d += BPP; - v >>= 4; - r = (pal[v & 0xf] >> 4) & 0xf0; - g = pal[v & 0xf] & 0xf0; - b = (pal[v & 0xf] << 4) & 0xf0; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - d += BPP; - s ++; - width -= 2; - } while (width > 0); -} - -/* - * 8-bit colour - */ -static void glue(draw_line8_, DEPTH)(void *opaque, - uint8_t *d, const uint8_t *s, int width, int deststep) -{ - uint16_t *pal = opaque; - uint8_t v, r, g, b; - - do { - v = ldub_p((void *) s); - r = (pal[v] >> 4) & 0xf0; - g = pal[v] & 0xf0; - b = (pal[v] << 4) & 0xf0; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - s ++; - d += BPP; - } while (-- width != 0); -} - -/* - * 12-bit colour - */ -static void glue(draw_line12_, DEPTH)(void *opaque, - uint8_t *d, const uint8_t *s, int width, int deststep) -{ - uint16_t v; - uint8_t r, g, b; - - do { - v = lduw_le_p((void *) s); - r = (v >> 4) & 0xf0; - g = v & 0xf0; - b = (v << 4) & 0xf0; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - s += 2; - d += BPP; - } while (-- width != 0); -} - -/* - * 16-bit colour - */ -static void glue(draw_line16_, DEPTH)(void *opaque, - uint8_t *d, const uint8_t *s, int width, int deststep) -{ -#if DEPTH == 16 && defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) - memcpy(d, s, width * 2); -#else - uint16_t v; - uint8_t r, g, b; - - do { - v = lduw_le_p((void *) s); - r = (v >> 8) & 0xf8; - g = (v >> 3) & 0xfc; - b = (v << 3) & 0xf8; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b); - s += 2; - d += BPP; - } while (-- width != 0); -#endif -} - -#undef DEPTH -#undef BPP -#undef PIXEL_TYPE diff --git a/qemu/hw/display/omap_lcdc.c b/qemu/hw/display/omap_lcdc.c deleted file mode 100644 index ce1058bf8..000000000 --- a/qemu/hw/display/omap_lcdc.c +++ /dev/null @@ -1,420 +0,0 @@ -/* - * OMAP LCD controller. - * - * Copyright (C) 2006-2007 Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "ui/console.h" -#include "hw/arm/omap.h" -#include "framebuffer.h" -#include "ui/pixel_ops.h" - -struct omap_lcd_panel_s { - MemoryRegion *sysmem; - MemoryRegion iomem; - MemoryRegionSection fbsection; - qemu_irq irq; - QemuConsole *con; - - int plm; - int tft; - int mono; - int enable; - int width; - int height; - int interrupts; - uint32_t timing[3]; - uint32_t subpanel; - uint32_t ctrl; - - struct omap_dma_lcd_channel_s *dma; - uint16_t palette[256]; - int palette_done; - int frame_done; - int invalidate; - int sync_error; -}; - -static void omap_lcd_interrupts(struct omap_lcd_panel_s *s) -{ - if (s->frame_done && (s->interrupts & 1)) { - qemu_irq_raise(s->irq); - return; - } - - if (s->palette_done && (s->interrupts & 2)) { - qemu_irq_raise(s->irq); - return; - } - - if (s->sync_error) { - qemu_irq_raise(s->irq); - return; - } - - qemu_irq_lower(s->irq); -} - -#define draw_line_func drawfn - -#define DEPTH 8 -#include "omap_lcd_template.h" -#define DEPTH 15 -#include "omap_lcd_template.h" -#define DEPTH 16 -#include "omap_lcd_template.h" -#define DEPTH 32 -#include "omap_lcd_template.h" - -static draw_line_func draw_line_table2[33] = { - [0 ... 32] = NULL, - [8] = draw_line2_8, - [15] = draw_line2_15, - [16] = draw_line2_16, - [32] = draw_line2_32, -}, draw_line_table4[33] = { - [0 ... 32] = NULL, - [8] = draw_line4_8, - [15] = draw_line4_15, - [16] = draw_line4_16, - [32] = draw_line4_32, -}, draw_line_table8[33] = { - [0 ... 32] = NULL, - [8] = draw_line8_8, - [15] = draw_line8_15, - [16] = draw_line8_16, - [32] = draw_line8_32, -}, draw_line_table12[33] = { - [0 ... 32] = NULL, - [8] = draw_line12_8, - [15] = draw_line12_15, - [16] = draw_line12_16, - [32] = draw_line12_32, -}, draw_line_table16[33] = { - [0 ... 32] = NULL, - [8] = draw_line16_8, - [15] = draw_line16_15, - [16] = draw_line16_16, - [32] = draw_line16_32, -}; - -static void omap_update_display(void *opaque) -{ - struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque; - DisplaySurface *surface = qemu_console_surface(omap_lcd->con); - draw_line_func draw_line; - int size, height, first, last; - int width, linesize, step, bpp, frame_offset; - hwaddr frame_base; - - if (!omap_lcd || omap_lcd->plm == 1 || !omap_lcd->enable || - !surface_bits_per_pixel(surface)) { - return; - } - - frame_offset = 0; - if (omap_lcd->plm != 2) { - cpu_physical_memory_read(omap_lcd->dma->phys_framebuffer[ - omap_lcd->dma->current_frame], - (void *)omap_lcd->palette, 0x200); - switch (omap_lcd->palette[0] >> 12 & 7) { - case 3 ... 7: - frame_offset += 0x200; - break; - default: - frame_offset += 0x20; - } - } - - /* Colour depth */ - switch ((omap_lcd->palette[0] >> 12) & 7) { - case 1: - draw_line = draw_line_table2[surface_bits_per_pixel(surface)]; - bpp = 2; - break; - - case 2: - draw_line = draw_line_table4[surface_bits_per_pixel(surface)]; - bpp = 4; - break; - - case 3: - draw_line = draw_line_table8[surface_bits_per_pixel(surface)]; - bpp = 8; - break; - - case 4 ... 7: - if (!omap_lcd->tft) - draw_line = draw_line_table12[surface_bits_per_pixel(surface)]; - else - draw_line = draw_line_table16[surface_bits_per_pixel(surface)]; - bpp = 16; - break; - - default: - /* Unsupported at the moment. */ - return; - } - - /* Resolution */ - width = omap_lcd->width; - if (width != surface_width(surface) || - omap_lcd->height != surface_height(surface)) { - qemu_console_resize(omap_lcd->con, - omap_lcd->width, omap_lcd->height); - surface = qemu_console_surface(omap_lcd->con); - omap_lcd->invalidate = 1; - } - - if (omap_lcd->dma->current_frame == 0) - size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top; - else - size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top; - - if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) { - omap_lcd->sync_error = 1; - omap_lcd_interrupts(omap_lcd); - omap_lcd->enable = 0; - return; - } - - /* Content */ - frame_base = omap_lcd->dma->phys_framebuffer[ - omap_lcd->dma->current_frame] + frame_offset; - omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame; - if (omap_lcd->dma->interrupts & 1) - qemu_irq_raise(omap_lcd->dma->irq); - if (omap_lcd->dma->dual) - omap_lcd->dma->current_frame ^= 1; - - if (!surface_bits_per_pixel(surface)) { - return; - } - - first = 0; - height = omap_lcd->height; - if (omap_lcd->subpanel & (1 << 31)) { - if (omap_lcd->subpanel & (1 << 29)) - first = (omap_lcd->subpanel >> 16) & 0x3ff; - else - height = (omap_lcd->subpanel >> 16) & 0x3ff; - /* TODO: fill the rest of the panel with DPD */ - } - - step = width * bpp >> 3; - linesize = surface_stride(surface); - if (omap_lcd->invalidate) { - framebuffer_update_memory_section(&omap_lcd->fbsection, - omap_lcd->sysmem, frame_base, - height, step); - } - - framebuffer_update_display(surface, &omap_lcd->fbsection, - width, height, - step, linesize, 0, - omap_lcd->invalidate, - draw_line, omap_lcd->palette, - &first, &last); - - if (first >= 0) { - dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1); - } - omap_lcd->invalidate = 0; -} - -static void omap_invalidate_display(void *opaque) { - struct omap_lcd_panel_s *omap_lcd = opaque; - omap_lcd->invalidate = 1; -} - -static void omap_lcd_update(struct omap_lcd_panel_s *s) { - if (!s->enable) { - s->dma->current_frame = -1; - s->sync_error = 0; - if (s->plm != 1) - s->frame_done = 1; - omap_lcd_interrupts(s); - return; - } - - if (s->dma->current_frame == -1) { - s->frame_done = 0; - s->palette_done = 0; - s->dma->current_frame = 0; - } - - if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu, - s->dma->src_f1_top) || - !s->dma->mpu->port[ - s->dma->src].addr_valid(s->dma->mpu, - s->dma->src_f1_bottom) || - (s->dma->dual && - (!s->dma->mpu->port[ - s->dma->src].addr_valid(s->dma->mpu, - s->dma->src_f2_top) || - !s->dma->mpu->port[ - s->dma->src].addr_valid(s->dma->mpu, - s->dma->src_f2_bottom)))) { - s->dma->condition |= 1 << 2; - if (s->dma->interrupts & (1 << 1)) - qemu_irq_raise(s->dma->irq); - s->enable = 0; - return; - } - - s->dma->phys_framebuffer[0] = s->dma->src_f1_top; - s->dma->phys_framebuffer[1] = s->dma->src_f2_top; - - if (s->plm != 2 && !s->palette_done) { - cpu_physical_memory_read( - s->dma->phys_framebuffer[s->dma->current_frame], - (void *)s->palette, 0x200); - s->palette_done = 1; - omap_lcd_interrupts(s); - } -} - -static uint64_t omap_lcdc_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque; - - switch (addr) { - case 0x00: /* LCD_CONTROL */ - return (s->tft << 23) | (s->plm << 20) | - (s->tft << 7) | (s->interrupts << 3) | - (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34; - - case 0x04: /* LCD_TIMING0 */ - return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f; - - case 0x08: /* LCD_TIMING1 */ - return (s->timing[1] << 10) | (s->height - 1); - - case 0x0c: /* LCD_TIMING2 */ - return s->timing[2] | 0xfc000000; - - case 0x10: /* LCD_STATUS */ - return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done; - - case 0x14: /* LCD_SUBPANEL */ - return s->subpanel; - - default: - break; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_lcdc_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque; - - switch (addr) { - case 0x00: /* LCD_CONTROL */ - s->plm = (value >> 20) & 3; - s->tft = (value >> 7) & 1; - s->interrupts = (value >> 3) & 3; - s->mono = (value >> 1) & 1; - s->ctrl = value & 0x01cff300; - if (s->enable != (value & 1)) { - s->enable = value & 1; - omap_lcd_update(s); - } - break; - - case 0x04: /* LCD_TIMING0 */ - s->timing[0] = value >> 10; - s->width = (value & 0x3ff) + 1; - break; - - case 0x08: /* LCD_TIMING1 */ - s->timing[1] = value >> 10; - s->height = (value & 0x3ff) + 1; - break; - - case 0x0c: /* LCD_TIMING2 */ - s->timing[2] = value; - break; - - case 0x10: /* LCD_STATUS */ - break; - - case 0x14: /* LCD_SUBPANEL */ - s->subpanel = value & 0xa1ffffff; - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_lcdc_ops = { - .read = omap_lcdc_read, - .write = omap_lcdc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -void omap_lcdc_reset(struct omap_lcd_panel_s *s) -{ - s->dma->current_frame = -1; - s->plm = 0; - s->tft = 0; - s->mono = 0; - s->enable = 0; - s->width = 0; - s->height = 0; - s->interrupts = 0; - s->timing[0] = 0; - s->timing[1] = 0; - s->timing[2] = 0; - s->subpanel = 0; - s->palette_done = 0; - s->frame_done = 0; - s->sync_error = 0; - s->invalidate = 1; - s->subpanel = 0; - s->ctrl = 0; -} - -static const GraphicHwOps omap_ops = { - .invalidate = omap_invalidate_display, - .gfx_update = omap_update_display, -}; - -struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem, - hwaddr base, - qemu_irq irq, - struct omap_dma_lcd_channel_s *dma, - omap_clk clk) -{ - struct omap_lcd_panel_s *s = g_new0(struct omap_lcd_panel_s, 1); - - s->irq = irq; - s->dma = dma; - s->sysmem = sysmem; - omap_lcdc_reset(s); - - memory_region_init_io(&s->iomem, NULL, &omap_lcdc_ops, s, "omap.lcdc", 0x100); - memory_region_add_subregion(sysmem, base, &s->iomem); - - s->con = graphic_console_init(NULL, 0, &omap_ops, s); - - return s; -} diff --git a/qemu/hw/display/pl110.c b/qemu/hw/display/pl110.c deleted file mode 100644 index d589959f1..000000000 --- a/qemu/hw/display/pl110.c +++ /dev/null @@ -1,539 +0,0 @@ -/* - * Arm PrimeCell PL110 Color LCD Controller - * - * Copyright (c) 2005-2009 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GNU LGPL - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "ui/console.h" -#include "framebuffer.h" -#include "ui/pixel_ops.h" - -#define PL110_CR_EN 0x001 -#define PL110_CR_BGR 0x100 -#define PL110_CR_BEBO 0x200 -#define PL110_CR_BEPO 0x400 -#define PL110_CR_PWR 0x800 - -enum pl110_bppmode -{ - BPP_1, - BPP_2, - BPP_4, - BPP_8, - BPP_16, - BPP_32, - BPP_16_565, /* PL111 only */ - BPP_12 /* PL111 only */ -}; - - -/* The Versatile/PB uses a slightly modified PL110 controller. */ -enum pl110_version -{ - PL110, - PL110_VERSATILE, - PL111 -}; - -#define TYPE_PL110 "pl110" -#define PL110(obj) OBJECT_CHECK(PL110State, (obj), TYPE_PL110) - -typedef struct PL110State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - MemoryRegionSection fbsection; - QemuConsole *con; - - int version; - uint32_t timing[4]; - uint32_t cr; - uint32_t upbase; - uint32_t lpbase; - uint32_t int_status; - uint32_t int_mask; - int cols; - int rows; - enum pl110_bppmode bpp; - int invalidate; - uint32_t mux_ctrl; - uint32_t palette[256]; - uint32_t raw_palette[128]; - qemu_irq irq; -} PL110State; - -static int vmstate_pl110_post_load(void *opaque, int version_id); - -static const VMStateDescription vmstate_pl110 = { - .name = "pl110", - .version_id = 2, - .minimum_version_id = 1, - .post_load = vmstate_pl110_post_load, - .fields = (VMStateField[]) { - VMSTATE_INT32(version, PL110State), - VMSTATE_UINT32_ARRAY(timing, PL110State, 4), - VMSTATE_UINT32(cr, PL110State), - VMSTATE_UINT32(upbase, PL110State), - VMSTATE_UINT32(lpbase, PL110State), - VMSTATE_UINT32(int_status, PL110State), - VMSTATE_UINT32(int_mask, PL110State), - VMSTATE_INT32(cols, PL110State), - VMSTATE_INT32(rows, PL110State), - VMSTATE_UINT32(bpp, PL110State), - VMSTATE_INT32(invalidate, PL110State), - VMSTATE_UINT32_ARRAY(palette, PL110State, 256), - VMSTATE_UINT32_ARRAY(raw_palette, PL110State, 128), - VMSTATE_UINT32_V(mux_ctrl, PL110State, 2), - VMSTATE_END_OF_LIST() - } -}; - -static const unsigned char pl110_id[] = -{ 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; - -static const unsigned char pl111_id[] = { - 0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1 -}; - - -/* Indexed by pl110_version */ -static const unsigned char *idregs[] = { - pl110_id, - /* The ARM documentation (DDI0224C) says the CLCDC on the Versatile board - * has a different ID (0x93, 0x10, 0x04, 0x00, ...). However the hardware - * itself has the same ID values as a stock PL110, and guests (in - * particular Linux) rely on this. We emulate what the hardware does, - * rather than what the docs claim it ought to do. - */ - pl110_id, - pl111_id -}; - -#define BITS 8 -#include "pl110_template.h" -#define BITS 15 -#include "pl110_template.h" -#define BITS 16 -#include "pl110_template.h" -#define BITS 24 -#include "pl110_template.h" -#define BITS 32 -#include "pl110_template.h" - -static int pl110_enabled(PL110State *s) -{ - return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR); -} - -static void pl110_update_display(void *opaque) -{ - PL110State *s = (PL110State *)opaque; - SysBusDevice *sbd; - DisplaySurface *surface = qemu_console_surface(s->con); - drawfn* fntable; - drawfn fn; - int dest_width; - int src_width; - int bpp_offset; - int first; - int last; - - if (!pl110_enabled(s)) { - return; - } - - sbd = SYS_BUS_DEVICE(s); - - switch (surface_bits_per_pixel(surface)) { - case 0: - return; - case 8: - fntable = pl110_draw_fn_8; - dest_width = 1; - break; - case 15: - fntable = pl110_draw_fn_15; - dest_width = 2; - break; - case 16: - fntable = pl110_draw_fn_16; - dest_width = 2; - break; - case 24: - fntable = pl110_draw_fn_24; - dest_width = 3; - break; - case 32: - fntable = pl110_draw_fn_32; - dest_width = 4; - break; - default: - fprintf(stderr, "pl110: Bad color depth\n"); - exit(1); - } - if (s->cr & PL110_CR_BGR) - bpp_offset = 0; - else - bpp_offset = 24; - - if ((s->version != PL111) && (s->bpp == BPP_16)) { - /* The PL110's native 16 bit mode is 5551; however - * most boards with a PL110 implement an external - * mux which allows bits to be reshuffled to give - * 565 format. The mux is typically controlled by - * an external system register. - * This is controlled by a GPIO input pin - * so boards can wire it up to their register. - * - * The PL111 straightforwardly implements both - * 5551 and 565 under control of the bpp field - * in the LCDControl register. - */ - switch (s->mux_ctrl) { - case 3: /* 565 BGR */ - bpp_offset = (BPP_16_565 - BPP_16); - break; - case 1: /* 5551 */ - break; - case 0: /* 888; also if we have loaded vmstate from an old version */ - case 2: /* 565 RGB */ - default: - /* treat as 565 but honour BGR bit */ - bpp_offset += (BPP_16_565 - BPP_16); - break; - } - } - - if (s->cr & PL110_CR_BEBO) - fn = fntable[s->bpp + 8 + bpp_offset]; - else if (s->cr & PL110_CR_BEPO) - fn = fntable[s->bpp + 16 + bpp_offset]; - else - fn = fntable[s->bpp + bpp_offset]; - - src_width = s->cols; - switch (s->bpp) { - case BPP_1: - src_width >>= 3; - break; - case BPP_2: - src_width >>= 2; - break; - case BPP_4: - src_width >>= 1; - break; - case BPP_8: - break; - case BPP_16: - case BPP_16_565: - case BPP_12: - src_width <<= 1; - break; - case BPP_32: - src_width <<= 2; - break; - } - dest_width *= s->cols; - first = 0; - if (s->invalidate) { - framebuffer_update_memory_section(&s->fbsection, - sysbus_address_space(sbd), - s->upbase, - s->rows, src_width); - } - - framebuffer_update_display(surface, &s->fbsection, - s->cols, s->rows, - src_width, dest_width, 0, - s->invalidate, - fn, s->palette, - &first, &last); - - if (first >= 0) { - dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1); - } - s->invalidate = 0; -} - -static void pl110_invalidate_display(void * opaque) -{ - PL110State *s = (PL110State *)opaque; - s->invalidate = 1; - if (pl110_enabled(s)) { - qemu_console_resize(s->con, s->cols, s->rows); - } -} - -static void pl110_update_palette(PL110State *s, int n) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int i; - uint32_t raw; - unsigned int r, g, b; - - raw = s->raw_palette[n]; - n <<= 1; - for (i = 0; i < 2; i++) { - r = (raw & 0x1f) << 3; - raw >>= 5; - g = (raw & 0x1f) << 3; - raw >>= 5; - b = (raw & 0x1f) << 3; - /* The I bit is ignored. */ - raw >>= 6; - switch (surface_bits_per_pixel(surface)) { - case 8: - s->palette[n] = rgb_to_pixel8(r, g, b); - break; - case 15: - s->palette[n] = rgb_to_pixel15(r, g, b); - break; - case 16: - s->palette[n] = rgb_to_pixel16(r, g, b); - break; - case 24: - case 32: - s->palette[n] = rgb_to_pixel32(r, g, b); - break; - } - n++; - } -} - -static void pl110_resize(PL110State *s, int width, int height) -{ - if (width != s->cols || height != s->rows) { - if (pl110_enabled(s)) { - qemu_console_resize(s->con, width, height); - } - } - s->cols = width; - s->rows = height; -} - -/* Update interrupts. */ -static void pl110_update(PL110State *s) -{ - /* TODO: Implement interrupts. */ -} - -static uint64_t pl110_read(void *opaque, hwaddr offset, - unsigned size) -{ - PL110State *s = (PL110State *)opaque; - - if (offset >= 0xfe0 && offset < 0x1000) { - return idregs[s->version][(offset - 0xfe0) >> 2]; - } - if (offset >= 0x200 && offset < 0x400) { - return s->raw_palette[(offset - 0x200) >> 2]; - } - switch (offset >> 2) { - case 0: /* LCDTiming0 */ - return s->timing[0]; - case 1: /* LCDTiming1 */ - return s->timing[1]; - case 2: /* LCDTiming2 */ - return s->timing[2]; - case 3: /* LCDTiming3 */ - return s->timing[3]; - case 4: /* LCDUPBASE */ - return s->upbase; - case 5: /* LCDLPBASE */ - return s->lpbase; - case 6: /* LCDIMSC */ - if (s->version != PL110) { - return s->cr; - } - return s->int_mask; - case 7: /* LCDControl */ - if (s->version != PL110) { - return s->int_mask; - } - return s->cr; - case 8: /* LCDRIS */ - return s->int_status; - case 9: /* LCDMIS */ - return s->int_status & s->int_mask; - case 11: /* LCDUPCURR */ - /* TODO: Implement vertical refresh. */ - return s->upbase; - case 12: /* LCDLPCURR */ - return s->lpbase; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pl110_read: Bad offset %x\n", (int)offset); - return 0; - } -} - -static void pl110_write(void *opaque, hwaddr offset, - uint64_t val, unsigned size) -{ - PL110State *s = (PL110State *)opaque; - int n; - - /* For simplicity invalidate the display whenever a control register - is written to. */ - s->invalidate = 1; - if (offset >= 0x200 && offset < 0x400) { - /* Palette. */ - n = (offset - 0x200) >> 2; - s->raw_palette[(offset - 0x200) >> 2] = val; - pl110_update_palette(s, n); - return; - } - switch (offset >> 2) { - case 0: /* LCDTiming0 */ - s->timing[0] = val; - n = ((val & 0xfc) + 4) * 4; - pl110_resize(s, n, s->rows); - break; - case 1: /* LCDTiming1 */ - s->timing[1] = val; - n = (val & 0x3ff) + 1; - pl110_resize(s, s->cols, n); - break; - case 2: /* LCDTiming2 */ - s->timing[2] = val; - break; - case 3: /* LCDTiming3 */ - s->timing[3] = val; - break; - case 4: /* LCDUPBASE */ - s->upbase = val; - break; - case 5: /* LCDLPBASE */ - s->lpbase = val; - break; - case 6: /* LCDIMSC */ - if (s->version != PL110) { - goto control; - } - imsc: - s->int_mask = val; - pl110_update(s); - break; - case 7: /* LCDControl */ - if (s->version != PL110) { - goto imsc; - } - control: - s->cr = val; - s->bpp = (val >> 1) & 7; - if (pl110_enabled(s)) { - qemu_console_resize(s->con, s->cols, s->rows); - } - break; - case 10: /* LCDICR */ - s->int_status &= ~val; - pl110_update(s); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pl110_write: Bad offset %x\n", (int)offset); - } -} - -static const MemoryRegionOps pl110_ops = { - .read = pl110_read, - .write = pl110_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pl110_mux_ctrl_set(void *opaque, int line, int level) -{ - PL110State *s = (PL110State *)opaque; - s->mux_ctrl = level; -} - -static int vmstate_pl110_post_load(void *opaque, int version_id) -{ - PL110State *s = opaque; - /* Make sure we redraw, and at the right size */ - pl110_invalidate_display(s); - return 0; -} - -static const GraphicHwOps pl110_gfx_ops = { - .invalidate = pl110_invalidate_display, - .gfx_update = pl110_update_display, -}; - -static int pl110_initfn(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - PL110State *s = PL110(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1); - s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s); - return 0; -} - -static void pl110_init(Object *obj) -{ - PL110State *s = PL110(obj); - - s->version = PL110; -} - -static void pl110_versatile_init(Object *obj) -{ - PL110State *s = PL110(obj); - - s->version = PL110_VERSATILE; -} - -static void pl111_init(Object *obj) -{ - PL110State *s = PL110(obj); - - s->version = PL111; -} - -static void pl110_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = pl110_initfn; - set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); - dc->vmsd = &vmstate_pl110; -} - -static const TypeInfo pl110_info = { - .name = TYPE_PL110, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PL110State), - .instance_init = pl110_init, - .class_init = pl110_class_init, -}; - -static const TypeInfo pl110_versatile_info = { - .name = "pl110_versatile", - .parent = TYPE_PL110, - .instance_init = pl110_versatile_init, -}; - -static const TypeInfo pl111_info = { - .name = "pl111", - .parent = TYPE_PL110, - .instance_init = pl111_init, -}; - -static void pl110_register_types(void) -{ - type_register_static(&pl110_info); - type_register_static(&pl110_versatile_info); - type_register_static(&pl111_info); -} - -type_init(pl110_register_types) diff --git a/qemu/hw/display/pl110_template.h b/qemu/hw/display/pl110_template.h deleted file mode 100644 index 36ba791c6..000000000 --- a/qemu/hw/display/pl110_template.h +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Arm PrimeCell PL110 Color LCD Controller - * - * Copyright (c) 2005 CodeSourcery, LLC. - * Written by Paul Brook - * - * This code is licensed under the GNU LGPL - * - * Framebuffer format conversion routines. - */ - -#ifndef ORDER - -#if BITS == 8 -#define COPY_PIXEL(to, from) *(to++) = from -#elif BITS == 15 || BITS == 16 -#define COPY_PIXEL(to, from) do { *(uint16_t *)to = from; to += 2; } while (0) -#elif BITS == 24 -#define COPY_PIXEL(to, from) \ - do { \ - *(to++) = from; \ - *(to++) = (from) >> 8; \ - *(to++) = (from) >> 16; \ - } while (0) -#elif BITS == 32 -#define COPY_PIXEL(to, from) do { *(uint32_t *)to = from; to += 4; } while (0) -#else -#error unknown bit depth -#endif - -#undef RGB -#define BORDER bgr -#define ORDER 0 -#include "pl110_template.h" -#define ORDER 1 -#include "pl110_template.h" -#define ORDER 2 -#include "pl110_template.h" -#undef BORDER -#define RGB -#define BORDER rgb -#define ORDER 0 -#include "pl110_template.h" -#define ORDER 1 -#include "pl110_template.h" -#define ORDER 2 -#include "pl110_template.h" -#undef BORDER - -static drawfn glue(pl110_draw_fn_,BITS)[48] = -{ - glue(pl110_draw_line1_lblp_bgr,BITS), - glue(pl110_draw_line2_lblp_bgr,BITS), - glue(pl110_draw_line4_lblp_bgr,BITS), - glue(pl110_draw_line8_lblp_bgr,BITS), - glue(pl110_draw_line16_555_lblp_bgr,BITS), - glue(pl110_draw_line32_lblp_bgr,BITS), - glue(pl110_draw_line16_lblp_bgr,BITS), - glue(pl110_draw_line12_lblp_bgr,BITS), - - glue(pl110_draw_line1_bbbp_bgr,BITS), - glue(pl110_draw_line2_bbbp_bgr,BITS), - glue(pl110_draw_line4_bbbp_bgr,BITS), - glue(pl110_draw_line8_bbbp_bgr,BITS), - glue(pl110_draw_line16_555_bbbp_bgr,BITS), - glue(pl110_draw_line32_bbbp_bgr,BITS), - glue(pl110_draw_line16_bbbp_bgr,BITS), - glue(pl110_draw_line12_bbbp_bgr,BITS), - - glue(pl110_draw_line1_lbbp_bgr,BITS), - glue(pl110_draw_line2_lbbp_bgr,BITS), - glue(pl110_draw_line4_lbbp_bgr,BITS), - glue(pl110_draw_line8_lbbp_bgr,BITS), - glue(pl110_draw_line16_555_lbbp_bgr,BITS), - glue(pl110_draw_line32_lbbp_bgr,BITS), - glue(pl110_draw_line16_lbbp_bgr,BITS), - glue(pl110_draw_line12_lbbp_bgr,BITS), - - glue(pl110_draw_line1_lblp_rgb,BITS), - glue(pl110_draw_line2_lblp_rgb,BITS), - glue(pl110_draw_line4_lblp_rgb,BITS), - glue(pl110_draw_line8_lblp_rgb,BITS), - glue(pl110_draw_line16_555_lblp_rgb,BITS), - glue(pl110_draw_line32_lblp_rgb,BITS), - glue(pl110_draw_line16_lblp_rgb,BITS), - glue(pl110_draw_line12_lblp_rgb,BITS), - - glue(pl110_draw_line1_bbbp_rgb,BITS), - glue(pl110_draw_line2_bbbp_rgb,BITS), - glue(pl110_draw_line4_bbbp_rgb,BITS), - glue(pl110_draw_line8_bbbp_rgb,BITS), - glue(pl110_draw_line16_555_bbbp_rgb,BITS), - glue(pl110_draw_line32_bbbp_rgb,BITS), - glue(pl110_draw_line16_bbbp_rgb,BITS), - glue(pl110_draw_line12_bbbp_rgb,BITS), - - glue(pl110_draw_line1_lbbp_rgb,BITS), - glue(pl110_draw_line2_lbbp_rgb,BITS), - glue(pl110_draw_line4_lbbp_rgb,BITS), - glue(pl110_draw_line8_lbbp_rgb,BITS), - glue(pl110_draw_line16_555_lbbp_rgb,BITS), - glue(pl110_draw_line32_lbbp_rgb,BITS), - glue(pl110_draw_line16_lbbp_rgb,BITS), - glue(pl110_draw_line12_lbbp_rgb,BITS), -}; - -#undef BITS -#undef COPY_PIXEL - -#else - -#if ORDER == 0 -#define NAME glue(glue(lblp_, BORDER), BITS) -#ifdef HOST_WORDS_BIGENDIAN -#define SWAP_WORDS 1 -#endif -#elif ORDER == 1 -#define NAME glue(glue(bbbp_, BORDER), BITS) -#ifndef HOST_WORDS_BIGENDIAN -#define SWAP_WORDS 1 -#endif -#else -#define SWAP_PIXELS 1 -#define NAME glue(glue(lbbp_, BORDER), BITS) -#ifdef HOST_WORDS_BIGENDIAN -#define SWAP_WORDS 1 -#endif -#endif - -#define FN_2(x, y) FN(x, y) FN(x+1, y) -#define FN_4(x, y) FN_2(x, y) FN_2(x+2, y) -#define FN_8(y) FN_4(0, y) FN_4(4, y) - -static void glue(pl110_draw_line1_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep) -{ - uint32_t *palette = opaque; - uint32_t data; - while (width > 0) { - data = *(uint32_t *)src; -#ifdef SWAP_PIXELS -#define FN(x, y) COPY_PIXEL(d, palette[(data >> (y + 7 - (x))) & 1]); -#else -#define FN(x, y) COPY_PIXEL(d, palette[(data >> ((x) + y)) & 1]); -#endif -#ifdef SWAP_WORDS - FN_8(24) - FN_8(16) - FN_8(8) - FN_8(0) -#else - FN_8(0) - FN_8(8) - FN_8(16) - FN_8(24) -#endif -#undef FN - width -= 32; - src += 4; - } -} - -static void glue(pl110_draw_line2_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep) -{ - uint32_t *palette = opaque; - uint32_t data; - while (width > 0) { - data = *(uint32_t *)src; -#ifdef SWAP_PIXELS -#define FN(x, y) COPY_PIXEL(d, palette[(data >> (y + 6 - (x)*2)) & 3]); -#else -#define FN(x, y) COPY_PIXEL(d, palette[(data >> ((x)*2 + y)) & 3]); -#endif -#ifdef SWAP_WORDS - FN_4(0, 24) - FN_4(0, 16) - FN_4(0, 8) - FN_4(0, 0) -#else - FN_4(0, 0) - FN_4(0, 8) - FN_4(0, 16) - FN_4(0, 24) -#endif -#undef FN - width -= 16; - src += 4; - } -} - -static void glue(pl110_draw_line4_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep) -{ - uint32_t *palette = opaque; - uint32_t data; - while (width > 0) { - data = *(uint32_t *)src; -#ifdef SWAP_PIXELS -#define FN(x, y) COPY_PIXEL(d, palette[(data >> (y + 4 - (x)*4)) & 0xf]); -#else -#define FN(x, y) COPY_PIXEL(d, palette[(data >> ((x)*4 + y)) & 0xf]); -#endif -#ifdef SWAP_WORDS - FN_2(0, 24) - FN_2(0, 16) - FN_2(0, 8) - FN_2(0, 0) -#else - FN_2(0, 0) - FN_2(0, 8) - FN_2(0, 16) - FN_2(0, 24) -#endif -#undef FN - width -= 8; - src += 4; - } -} - -static void glue(pl110_draw_line8_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep) -{ - uint32_t *palette = opaque; - uint32_t data; - while (width > 0) { - data = *(uint32_t *)src; -#define FN(x) COPY_PIXEL(d, palette[(data >> (x)) & 0xff]); -#ifdef SWAP_WORDS - FN(24) - FN(16) - FN(8) - FN(0) -#else - FN(0) - FN(8) - FN(16) - FN(24) -#endif -#undef FN - width -= 4; - src += 4; - } -} - -static void glue(pl110_draw_line16_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep) -{ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *)src; -#ifdef SWAP_WORDS - data = bswap32(data); -#endif -#ifdef RGB -#define LSB r -#define MSB b -#else -#define LSB b -#define MSB r -#endif -#if 0 - LSB = data & 0x1f; - data >>= 5; - g = data & 0x3f; - data >>= 6; - MSB = data & 0x1f; - data >>= 5; -#else - LSB = (data & 0x1f) << 3; - data >>= 5; - g = (data & 0x3f) << 2; - data >>= 6; - MSB = (data & 0x1f) << 3; - data >>= 5; -#endif - COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b)); - LSB = (data & 0x1f) << 3; - data >>= 5; - g = (data & 0x3f) << 2; - data >>= 6; - MSB = (data & 0x1f) << 3; - data >>= 5; - COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b)); -#undef MSB -#undef LSB - width -= 2; - src += 4; - } -} - -static void glue(pl110_draw_line32_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep) -{ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *)src; -#ifdef RGB -#define LSB r -#define MSB b -#else -#define LSB b -#define MSB r -#endif -#ifndef SWAP_WORDS - LSB = data & 0xff; - g = (data >> 8) & 0xff; - MSB = (data >> 16) & 0xff; -#else - LSB = (data >> 24) & 0xff; - g = (data >> 16) & 0xff; - MSB = (data >> 8) & 0xff; -#endif - COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b)); -#undef MSB -#undef LSB - width--; - src += 4; - } -} - -static void glue(pl110_draw_line16_555_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep) -{ - /* RGB 555 plus an intensity bit (which we ignore) */ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *)src; -#ifdef SWAP_WORDS - data = bswap32(data); -#endif -#ifdef RGB -#define LSB r -#define MSB b -#else -#define LSB b -#define MSB r -#endif - LSB = (data & 0x1f) << 3; - data >>= 5; - g = (data & 0x1f) << 3; - data >>= 5; - MSB = (data & 0x1f) << 3; - data >>= 5; - COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b)); - LSB = (data & 0x1f) << 3; - data >>= 5; - g = (data & 0x1f) << 3; - data >>= 5; - MSB = (data & 0x1f) << 3; - data >>= 6; - COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b)); -#undef MSB -#undef LSB - width -= 2; - src += 4; - } -} - -static void glue(pl110_draw_line12_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep) -{ - /* RGB 444 with 4 bits of zeroes at the top of each halfword */ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *)src; -#ifdef SWAP_WORDS - data = bswap32(data); -#endif -#ifdef RGB -#define LSB r -#define MSB b -#else -#define LSB b -#define MSB r -#endif - LSB = (data & 0xf) << 4; - data >>= 4; - g = (data & 0xf) << 4; - data >>= 4; - MSB = (data & 0xf) << 4; - data >>= 8; - COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b)); - LSB = (data & 0xf) << 4; - data >>= 4; - g = (data & 0xf) << 4; - data >>= 4; - MSB = (data & 0xf) << 4; - data >>= 8; - COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b)); -#undef MSB -#undef LSB - width -= 2; - src += 4; - } -} - -#undef SWAP_PIXELS -#undef NAME -#undef SWAP_WORDS -#undef ORDER - -#endif diff --git a/qemu/hw/display/pxa2xx_lcd.c b/qemu/hw/display/pxa2xx_lcd.c deleted file mode 100644 index 845521c5b..000000000 --- a/qemu/hw/display/pxa2xx_lcd.c +++ /dev/null @@ -1,1065 +0,0 @@ -/* - * Intel XScale PXA255/270 LCDC emulation. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GPLv2. - * - * 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 "ui/console.h" -#include "hw/arm/pxa.h" -#include "ui/pixel_ops.h" -/* FIXME: For graphic_rotate. Should probably be done in common code. */ -#include "sysemu/sysemu.h" -#include "framebuffer.h" - -struct DMAChannel { - uint32_t branch; - uint8_t up; - uint8_t palette[1024]; - uint8_t pbuffer[1024]; - void (*redraw)(PXA2xxLCDState *s, hwaddr addr, - int *miny, int *maxy); - - uint32_t descriptor; - uint32_t source; - uint32_t id; - uint32_t command; -}; - -struct PXA2xxLCDState { - MemoryRegion *sysmem; - MemoryRegion iomem; - MemoryRegionSection fbsection; - qemu_irq irq; - int irqlevel; - - int invalidated; - QemuConsole *con; - drawfn *line_fn[2]; - int dest_width; - int xres, yres; - int pal_for; - int transp; - enum { - pxa_lcdc_2bpp = 1, - pxa_lcdc_4bpp = 2, - pxa_lcdc_8bpp = 3, - pxa_lcdc_16bpp = 4, - pxa_lcdc_18bpp = 5, - pxa_lcdc_18pbpp = 6, - pxa_lcdc_19bpp = 7, - pxa_lcdc_19pbpp = 8, - pxa_lcdc_24bpp = 9, - pxa_lcdc_25bpp = 10, - } bpp; - - uint32_t control[6]; - uint32_t status[2]; - uint32_t ovl1c[2]; - uint32_t ovl2c[2]; - uint32_t ccr; - uint32_t cmdcr; - uint32_t trgbr; - uint32_t tcr; - uint32_t liidr; - uint8_t bscntr; - - struct DMAChannel dma_ch[7]; - - qemu_irq vsync_cb; - int orientation; -}; - -typedef struct QEMU_PACKED { - uint32_t fdaddr; - uint32_t fsaddr; - uint32_t fidr; - uint32_t ldcmd; -} PXAFrameDescriptor; - -#define LCCR0 0x000 /* LCD Controller Control register 0 */ -#define LCCR1 0x004 /* LCD Controller Control register 1 */ -#define LCCR2 0x008 /* LCD Controller Control register 2 */ -#define LCCR3 0x00c /* LCD Controller Control register 3 */ -#define LCCR4 0x010 /* LCD Controller Control register 4 */ -#define LCCR5 0x014 /* LCD Controller Control register 5 */ - -#define FBR0 0x020 /* DMA Channel 0 Frame Branch register */ -#define FBR1 0x024 /* DMA Channel 1 Frame Branch register */ -#define FBR2 0x028 /* DMA Channel 2 Frame Branch register */ -#define FBR3 0x02c /* DMA Channel 3 Frame Branch register */ -#define FBR4 0x030 /* DMA Channel 4 Frame Branch register */ -#define FBR5 0x110 /* DMA Channel 5 Frame Branch register */ -#define FBR6 0x114 /* DMA Channel 6 Frame Branch register */ - -#define LCSR1 0x034 /* LCD Controller Status register 1 */ -#define LCSR0 0x038 /* LCD Controller Status register 0 */ -#define LIIDR 0x03c /* LCD Controller Interrupt ID register */ - -#define TRGBR 0x040 /* TMED RGB Seed register */ -#define TCR 0x044 /* TMED Control register */ - -#define OVL1C1 0x050 /* Overlay 1 Control register 1 */ -#define OVL1C2 0x060 /* Overlay 1 Control register 2 */ -#define OVL2C1 0x070 /* Overlay 2 Control register 1 */ -#define OVL2C2 0x080 /* Overlay 2 Control register 2 */ -#define CCR 0x090 /* Cursor Control register */ - -#define CMDCR 0x100 /* Command Control register */ -#define PRSR 0x104 /* Panel Read Status register */ - -#define PXA_LCDDMA_CHANS 7 -#define DMA_FDADR 0x00 /* Frame Descriptor Address register */ -#define DMA_FSADR 0x04 /* Frame Source Address register */ -#define DMA_FIDR 0x08 /* Frame ID register */ -#define DMA_LDCMD 0x0c /* Command register */ - -/* LCD Buffer Strength Control register */ -#define BSCNTR 0x04000054 - -/* Bitfield masks */ -#define LCCR0_ENB (1 << 0) -#define LCCR0_CMS (1 << 1) -#define LCCR0_SDS (1 << 2) -#define LCCR0_LDM (1 << 3) -#define LCCR0_SOFM0 (1 << 4) -#define LCCR0_IUM (1 << 5) -#define LCCR0_EOFM0 (1 << 6) -#define LCCR0_PAS (1 << 7) -#define LCCR0_DPD (1 << 9) -#define LCCR0_DIS (1 << 10) -#define LCCR0_QDM (1 << 11) -#define LCCR0_PDD (0xff << 12) -#define LCCR0_BSM0 (1 << 20) -#define LCCR0_OUM (1 << 21) -#define LCCR0_LCDT (1 << 22) -#define LCCR0_RDSTM (1 << 23) -#define LCCR0_CMDIM (1 << 24) -#define LCCR0_OUC (1 << 25) -#define LCCR0_LDDALT (1 << 26) -#define LCCR1_PPL(x) ((x) & 0x3ff) -#define LCCR2_LPP(x) ((x) & 0x3ff) -#define LCCR3_API (15 << 16) -#define LCCR3_BPP(x) ((((x) >> 24) & 7) | (((x) >> 26) & 8)) -#define LCCR3_PDFOR(x) (((x) >> 30) & 3) -#define LCCR4_K1(x) (((x) >> 0) & 7) -#define LCCR4_K2(x) (((x) >> 3) & 7) -#define LCCR4_K3(x) (((x) >> 6) & 7) -#define LCCR4_PALFOR(x) (((x) >> 15) & 3) -#define LCCR5_SOFM(ch) (1 << (ch - 1)) -#define LCCR5_EOFM(ch) (1 << (ch + 7)) -#define LCCR5_BSM(ch) (1 << (ch + 15)) -#define LCCR5_IUM(ch) (1 << (ch + 23)) -#define OVLC1_EN (1 << 31) -#define CCR_CEN (1 << 31) -#define FBR_BRA (1 << 0) -#define FBR_BINT (1 << 1) -#define FBR_SRCADDR (0xfffffff << 4) -#define LCSR0_LDD (1 << 0) -#define LCSR0_SOF0 (1 << 1) -#define LCSR0_BER (1 << 2) -#define LCSR0_ABC (1 << 3) -#define LCSR0_IU0 (1 << 4) -#define LCSR0_IU1 (1 << 5) -#define LCSR0_OU (1 << 6) -#define LCSR0_QD (1 << 7) -#define LCSR0_EOF0 (1 << 8) -#define LCSR0_BS0 (1 << 9) -#define LCSR0_SINT (1 << 10) -#define LCSR0_RDST (1 << 11) -#define LCSR0_CMDINT (1 << 12) -#define LCSR0_BERCH(x) (((x) & 7) << 28) -#define LCSR1_SOF(ch) (1 << (ch - 1)) -#define LCSR1_EOF(ch) (1 << (ch + 7)) -#define LCSR1_BS(ch) (1 << (ch + 15)) -#define LCSR1_IU(ch) (1 << (ch + 23)) -#define LDCMD_LENGTH(x) ((x) & 0x001ffffc) -#define LDCMD_EOFINT (1 << 21) -#define LDCMD_SOFINT (1 << 22) -#define LDCMD_PAL (1 << 26) - -/* Route internal interrupt lines to the global IC */ -static void pxa2xx_lcdc_int_update(PXA2xxLCDState *s) -{ - int level = 0; - level |= (s->status[0] & LCSR0_LDD) && !(s->control[0] & LCCR0_LDM); - level |= (s->status[0] & LCSR0_SOF0) && !(s->control[0] & LCCR0_SOFM0); - level |= (s->status[0] & LCSR0_IU0) && !(s->control[0] & LCCR0_IUM); - level |= (s->status[0] & LCSR0_IU1) && !(s->control[5] & LCCR5_IUM(1)); - level |= (s->status[0] & LCSR0_OU) && !(s->control[0] & LCCR0_OUM); - level |= (s->status[0] & LCSR0_QD) && !(s->control[0] & LCCR0_QDM); - level |= (s->status[0] & LCSR0_EOF0) && !(s->control[0] & LCCR0_EOFM0); - level |= (s->status[0] & LCSR0_BS0) && !(s->control[0] & LCCR0_BSM0); - level |= (s->status[0] & LCSR0_RDST) && !(s->control[0] & LCCR0_RDSTM); - level |= (s->status[0] & LCSR0_CMDINT) && !(s->control[0] & LCCR0_CMDIM); - level |= (s->status[1] & ~s->control[5]); - - qemu_set_irq(s->irq, !!level); - s->irqlevel = level; -} - -/* Set Branch Status interrupt high and poke associated registers */ -static inline void pxa2xx_dma_bs_set(PXA2xxLCDState *s, int ch) -{ - int unmasked; - if (ch == 0) { - s->status[0] |= LCSR0_BS0; - unmasked = !(s->control[0] & LCCR0_BSM0); - } else { - s->status[1] |= LCSR1_BS(ch); - unmasked = !(s->control[5] & LCCR5_BSM(ch)); - } - - if (unmasked) { - if (s->irqlevel) - s->status[0] |= LCSR0_SINT; - else - s->liidr = s->dma_ch[ch].id; - } -} - -/* Set Start Of Frame Status interrupt high and poke associated registers */ -static inline void pxa2xx_dma_sof_set(PXA2xxLCDState *s, int ch) -{ - int unmasked; - if (!(s->dma_ch[ch].command & LDCMD_SOFINT)) - return; - - if (ch == 0) { - s->status[0] |= LCSR0_SOF0; - unmasked = !(s->control[0] & LCCR0_SOFM0); - } else { - s->status[1] |= LCSR1_SOF(ch); - unmasked = !(s->control[5] & LCCR5_SOFM(ch)); - } - - if (unmasked) { - if (s->irqlevel) - s->status[0] |= LCSR0_SINT; - else - s->liidr = s->dma_ch[ch].id; - } -} - -/* Set End Of Frame Status interrupt high and poke associated registers */ -static inline void pxa2xx_dma_eof_set(PXA2xxLCDState *s, int ch) -{ - int unmasked; - if (!(s->dma_ch[ch].command & LDCMD_EOFINT)) - return; - - if (ch == 0) { - s->status[0] |= LCSR0_EOF0; - unmasked = !(s->control[0] & LCCR0_EOFM0); - } else { - s->status[1] |= LCSR1_EOF(ch); - unmasked = !(s->control[5] & LCCR5_EOFM(ch)); - } - - if (unmasked) { - if (s->irqlevel) - s->status[0] |= LCSR0_SINT; - else - s->liidr = s->dma_ch[ch].id; - } -} - -/* Set Bus Error Status interrupt high and poke associated registers */ -static inline void pxa2xx_dma_ber_set(PXA2xxLCDState *s, int ch) -{ - s->status[0] |= LCSR0_BERCH(ch) | LCSR0_BER; - if (s->irqlevel) - s->status[0] |= LCSR0_SINT; - else - s->liidr = s->dma_ch[ch].id; -} - -/* Load new Frame Descriptors from DMA */ -static void pxa2xx_descriptor_load(PXA2xxLCDState *s) -{ - PXAFrameDescriptor desc; - hwaddr descptr; - int i; - - for (i = 0; i < PXA_LCDDMA_CHANS; i ++) { - s->dma_ch[i].source = 0; - - if (!s->dma_ch[i].up) - continue; - - if (s->dma_ch[i].branch & FBR_BRA) { - descptr = s->dma_ch[i].branch & FBR_SRCADDR; - if (s->dma_ch[i].branch & FBR_BINT) - pxa2xx_dma_bs_set(s, i); - s->dma_ch[i].branch &= ~FBR_BRA; - } else - descptr = s->dma_ch[i].descriptor; - - if (!((descptr >= PXA2XX_SDRAM_BASE && descptr + - sizeof(desc) <= PXA2XX_SDRAM_BASE + ram_size) || - (descptr >= PXA2XX_INTERNAL_BASE && descptr + sizeof(desc) <= - PXA2XX_INTERNAL_BASE + PXA2XX_INTERNAL_SIZE))) { - continue; - } - - cpu_physical_memory_read(descptr, &desc, sizeof(desc)); - 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); - } -} - -static uint64_t pxa2xx_lcdc_read(void *opaque, hwaddr offset, - unsigned size) -{ - PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - int ch; - - switch (offset) { - case LCCR0: - return s->control[0]; - case LCCR1: - return s->control[1]; - case LCCR2: - return s->control[2]; - case LCCR3: - return s->control[3]; - case LCCR4: - return s->control[4]; - case LCCR5: - return s->control[5]; - - case OVL1C1: - return s->ovl1c[0]; - case OVL1C2: - return s->ovl1c[1]; - case OVL2C1: - return s->ovl2c[0]; - case OVL2C2: - return s->ovl2c[1]; - - case CCR: - return s->ccr; - - case CMDCR: - return s->cmdcr; - - case TRGBR: - return s->trgbr; - case TCR: - return s->tcr; - - case 0x200 ... 0x1000: /* DMA per-channel registers */ - ch = (offset - 0x200) >> 4; - if (!(ch >= 0 && ch < PXA_LCDDMA_CHANS)) - goto fail; - - switch (offset & 0xf) { - case DMA_FDADR: - return s->dma_ch[ch].descriptor; - case DMA_FSADR: - return s->dma_ch[ch].source; - case DMA_FIDR: - return s->dma_ch[ch].id; - case DMA_LDCMD: - return s->dma_ch[ch].command; - default: - goto fail; - } - - case FBR0: - return s->dma_ch[0].branch; - case FBR1: - return s->dma_ch[1].branch; - case FBR2: - return s->dma_ch[2].branch; - case FBR3: - return s->dma_ch[3].branch; - case FBR4: - return s->dma_ch[4].branch; - case FBR5: - return s->dma_ch[5].branch; - case FBR6: - return s->dma_ch[6].branch; - - case BSCNTR: - return s->bscntr; - - case PRSR: - return 0; - - case LCSR0: - return s->status[0]; - case LCSR1: - return s->status[1]; - case LIIDR: - return s->liidr; - - default: - fail: - hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset); - } - - return 0; -} - -static void pxa2xx_lcdc_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - int ch; - - switch (offset) { - case LCCR0: - /* ACK Quick Disable done */ - if ((s->control[0] & LCCR0_ENB) && !(value & LCCR0_ENB)) - s->status[0] |= LCSR0_QD; - - if (!(s->control[0] & LCCR0_LCDT) && (value & LCCR0_LCDT)) - printf("%s: internal frame buffer unsupported\n", __FUNCTION__); - - if ((s->control[3] & LCCR3_API) && - (value & LCCR0_ENB) && !(value & LCCR0_LCDT)) - s->status[0] |= LCSR0_ABC; - - s->control[0] = value & 0x07ffffff; - pxa2xx_lcdc_int_update(s); - - s->dma_ch[0].up = !!(value & LCCR0_ENB); - s->dma_ch[1].up = (s->ovl1c[0] & OVLC1_EN) || (value & LCCR0_SDS); - break; - - case LCCR1: - s->control[1] = value; - break; - - case LCCR2: - s->control[2] = value; - break; - - case LCCR3: - s->control[3] = value & 0xefffffff; - s->bpp = LCCR3_BPP(value); - break; - - case LCCR4: - s->control[4] = value & 0x83ff81ff; - break; - - case LCCR5: - s->control[5] = value & 0x3f3f3f3f; - break; - - case OVL1C1: - if (!(s->ovl1c[0] & OVLC1_EN) && (value & OVLC1_EN)) - printf("%s: Overlay 1 not supported\n", __FUNCTION__); - - s->ovl1c[0] = value & 0x80ffffff; - s->dma_ch[1].up = (value & OVLC1_EN) || (s->control[0] & LCCR0_SDS); - break; - - case OVL1C2: - s->ovl1c[1] = value & 0x000fffff; - break; - - case OVL2C1: - if (!(s->ovl2c[0] & OVLC1_EN) && (value & OVLC1_EN)) - printf("%s: Overlay 2 not supported\n", __FUNCTION__); - - s->ovl2c[0] = value & 0x80ffffff; - s->dma_ch[2].up = !!(value & OVLC1_EN); - s->dma_ch[3].up = !!(value & OVLC1_EN); - s->dma_ch[4].up = !!(value & OVLC1_EN); - break; - - case OVL2C2: - s->ovl2c[1] = value & 0x007fffff; - break; - - case CCR: - if (!(s->ccr & CCR_CEN) && (value & CCR_CEN)) - printf("%s: Hardware cursor unimplemented\n", __FUNCTION__); - - s->ccr = value & 0x81ffffe7; - s->dma_ch[5].up = !!(value & CCR_CEN); - break; - - case CMDCR: - s->cmdcr = value & 0xff; - break; - - case TRGBR: - s->trgbr = value & 0x00ffffff; - break; - - case TCR: - s->tcr = value & 0x7fff; - break; - - case 0x200 ... 0x1000: /* DMA per-channel registers */ - ch = (offset - 0x200) >> 4; - if (!(ch >= 0 && ch < PXA_LCDDMA_CHANS)) - goto fail; - - switch (offset & 0xf) { - case DMA_FDADR: - s->dma_ch[ch].descriptor = value & 0xfffffff0; - break; - - default: - goto fail; - } - break; - - case FBR0: - s->dma_ch[0].branch = value & 0xfffffff3; - break; - case FBR1: - s->dma_ch[1].branch = value & 0xfffffff3; - break; - case FBR2: - s->dma_ch[2].branch = value & 0xfffffff3; - break; - case FBR3: - s->dma_ch[3].branch = value & 0xfffffff3; - break; - case FBR4: - s->dma_ch[4].branch = value & 0xfffffff3; - break; - case FBR5: - s->dma_ch[5].branch = value & 0xfffffff3; - break; - case FBR6: - s->dma_ch[6].branch = value & 0xfffffff3; - break; - - case BSCNTR: - s->bscntr = value & 0xf; - break; - - case PRSR: - break; - - case LCSR0: - s->status[0] &= ~(value & 0xfff); - if (value & LCSR0_BER) - s->status[0] &= ~LCSR0_BERCH(7); - break; - - case LCSR1: - s->status[1] &= ~(value & 0x3e3f3f); - break; - - default: - fail: - hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset); - } -} - -static const MemoryRegionOps pxa2xx_lcdc_ops = { - .read = pxa2xx_lcdc_read, - .write = pxa2xx_lcdc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* Load new palette for a given DMA channel, convert to internal format */ -static void pxa2xx_palette_parse(PXA2xxLCDState *s, int ch, int bpp) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int i, n, format, r, g, b, alpha; - uint32_t *dest; - uint8_t *src; - s->pal_for = LCCR4_PALFOR(s->control[4]); - format = s->pal_for; - - switch (bpp) { - case pxa_lcdc_2bpp: - n = 4; - break; - case pxa_lcdc_4bpp: - n = 16; - break; - case pxa_lcdc_8bpp: - n = 256; - break; - default: - format = 0; - return; - } - - src = (uint8_t *) s->dma_ch[ch].pbuffer; - dest = (uint32_t *) s->dma_ch[ch].palette; - alpha = r = g = b = 0; - - for (i = 0; i < n; i ++) { - switch (format) { - case 0: /* 16 bpp, no transparency */ - alpha = 0; - if (s->control[0] & LCCR0_CMS) { - r = g = b = *(uint16_t *) src & 0xff; - } - else { - r = (*(uint16_t *) src & 0xf800) >> 8; - g = (*(uint16_t *) src & 0x07e0) >> 3; - b = (*(uint16_t *) src & 0x001f) << 3; - } - src += 2; - break; - case 1: /* 16 bpp plus transparency */ - alpha = *(uint32_t *) src & (1 << 24); - if (s->control[0] & LCCR0_CMS) - r = g = b = *(uint32_t *) src & 0xff; - else { - r = (*(uint32_t *) src & 0xf80000) >> 16; - g = (*(uint32_t *) src & 0x00fc00) >> 8; - b = (*(uint32_t *) src & 0x0000f8); - } - src += 4; - break; - case 2: /* 18 bpp plus transparency */ - alpha = *(uint32_t *) src & (1 << 24); - if (s->control[0] & LCCR0_CMS) - r = g = b = *(uint32_t *) src & 0xff; - else { - r = (*(uint32_t *) src & 0xfc0000) >> 16; - g = (*(uint32_t *) src & 0x00fc00) >> 8; - b = (*(uint32_t *) src & 0x0000fc); - } - src += 4; - break; - case 3: /* 24 bpp plus transparency */ - alpha = *(uint32_t *) src & (1 << 24); - if (s->control[0] & LCCR0_CMS) - r = g = b = *(uint32_t *) src & 0xff; - else { - r = (*(uint32_t *) src & 0xff0000) >> 16; - g = (*(uint32_t *) src & 0x00ff00) >> 8; - b = (*(uint32_t *) src & 0x0000ff); - } - src += 4; - break; - } - switch (surface_bits_per_pixel(surface)) { - case 8: - *dest = rgb_to_pixel8(r, g, b) | alpha; - break; - case 15: - *dest = rgb_to_pixel15(r, g, b) | alpha; - break; - case 16: - *dest = rgb_to_pixel16(r, g, b) | alpha; - break; - case 24: - *dest = rgb_to_pixel24(r, g, b) | alpha; - break; - case 32: - *dest = rgb_to_pixel32(r, g, b) | alpha; - break; - } - dest ++; - } -} - -static void pxa2xx_lcdc_dma0_redraw_rot0(PXA2xxLCDState *s, - hwaddr addr, int *miny, int *maxy) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int src_width, dest_width; - drawfn fn = NULL; - if (s->dest_width) - fn = s->line_fn[s->transp][s->bpp]; - if (!fn) - return; - - src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ - if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) - src_width *= 3; - else if (s->bpp > pxa_lcdc_16bpp) - src_width *= 4; - else if (s->bpp > pxa_lcdc_8bpp) - src_width *= 2; - - dest_width = s->xres * s->dest_width; - *miny = 0; - if (s->invalidated) { - framebuffer_update_memory_section(&s->fbsection, s->sysmem, - addr, s->yres, src_width); - } - framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, - src_width, dest_width, s->dest_width, - s->invalidated, - fn, s->dma_ch[0].palette, miny, maxy); -} - -static void pxa2xx_lcdc_dma0_redraw_rot90(PXA2xxLCDState *s, - hwaddr addr, int *miny, int *maxy) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int src_width, dest_width; - drawfn fn = NULL; - if (s->dest_width) - fn = s->line_fn[s->transp][s->bpp]; - if (!fn) - return; - - src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ - if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) - src_width *= 3; - else if (s->bpp > pxa_lcdc_16bpp) - src_width *= 4; - else if (s->bpp > pxa_lcdc_8bpp) - src_width *= 2; - - dest_width = s->yres * s->dest_width; - *miny = 0; - if (s->invalidated) { - framebuffer_update_memory_section(&s->fbsection, s->sysmem, - addr, s->yres, src_width); - } - framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, - src_width, s->dest_width, -dest_width, - s->invalidated, - fn, s->dma_ch[0].palette, - miny, maxy); -} - -static void pxa2xx_lcdc_dma0_redraw_rot180(PXA2xxLCDState *s, - hwaddr addr, int *miny, int *maxy) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int src_width, dest_width; - drawfn fn = NULL; - if (s->dest_width) { - fn = s->line_fn[s->transp][s->bpp]; - } - if (!fn) { - return; - } - - src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ - if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) { - src_width *= 3; - } else if (s->bpp > pxa_lcdc_16bpp) { - src_width *= 4; - } else if (s->bpp > pxa_lcdc_8bpp) { - src_width *= 2; - } - - dest_width = s->xres * s->dest_width; - *miny = 0; - if (s->invalidated) { - framebuffer_update_memory_section(&s->fbsection, s->sysmem, - addr, s->yres, src_width); - } - framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, - src_width, -dest_width, -s->dest_width, - s->invalidated, - fn, s->dma_ch[0].palette, miny, maxy); -} - -static void pxa2xx_lcdc_dma0_redraw_rot270(PXA2xxLCDState *s, - hwaddr addr, int *miny, int *maxy) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int src_width, dest_width; - drawfn fn = NULL; - if (s->dest_width) { - fn = s->line_fn[s->transp][s->bpp]; - } - if (!fn) { - return; - } - - src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ - if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) { - src_width *= 3; - } else if (s->bpp > pxa_lcdc_16bpp) { - src_width *= 4; - } else if (s->bpp > pxa_lcdc_8bpp) { - src_width *= 2; - } - - dest_width = s->yres * s->dest_width; - *miny = 0; - if (s->invalidated) { - framebuffer_update_memory_section(&s->fbsection, s->sysmem, - addr, s->yres, src_width); - } - framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, - src_width, -s->dest_width, dest_width, - s->invalidated, - fn, s->dma_ch[0].palette, - miny, maxy); -} - -static void pxa2xx_lcdc_resize(PXA2xxLCDState *s) -{ - int width, height; - if (!(s->control[0] & LCCR0_ENB)) - return; - - width = LCCR1_PPL(s->control[1]) + 1; - height = LCCR2_LPP(s->control[2]) + 1; - - if (width != s->xres || height != s->yres) { - if (s->orientation == 90 || s->orientation == 270) { - qemu_console_resize(s->con, height, width); - } else { - qemu_console_resize(s->con, width, height); - } - s->invalidated = 1; - s->xres = width; - s->yres = height; - } -} - -static void pxa2xx_update_display(void *opaque) -{ - PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - hwaddr fbptr; - int miny, maxy; - int ch; - if (!(s->control[0] & LCCR0_ENB)) - return; - - pxa2xx_descriptor_load(s); - - pxa2xx_lcdc_resize(s); - miny = s->yres; - maxy = 0; - s->transp = s->dma_ch[2].up || s->dma_ch[3].up; - /* Note: With overlay planes the order depends on LCCR0 bit 25. */ - for (ch = 0; ch < PXA_LCDDMA_CHANS; ch ++) - if (s->dma_ch[ch].up) { - if (!s->dma_ch[ch].source) { - pxa2xx_dma_ber_set(s, ch); - continue; - } - fbptr = s->dma_ch[ch].source; - if (!((fbptr >= PXA2XX_SDRAM_BASE && - fbptr <= PXA2XX_SDRAM_BASE + ram_size) || - (fbptr >= PXA2XX_INTERNAL_BASE && - fbptr <= PXA2XX_INTERNAL_BASE + PXA2XX_INTERNAL_SIZE))) { - pxa2xx_dma_ber_set(s, ch); - continue; - } - - if (s->dma_ch[ch].command & LDCMD_PAL) { - cpu_physical_memory_read(fbptr, s->dma_ch[ch].pbuffer, - MAX(LDCMD_LENGTH(s->dma_ch[ch].command), - sizeof(s->dma_ch[ch].pbuffer))); - pxa2xx_palette_parse(s, ch, s->bpp); - } else { - /* Do we need to reparse palette */ - if (LCCR4_PALFOR(s->control[4]) != s->pal_for) - pxa2xx_palette_parse(s, ch, s->bpp); - - /* ACK frame start */ - pxa2xx_dma_sof_set(s, ch); - - s->dma_ch[ch].redraw(s, fbptr, &miny, &maxy); - s->invalidated = 0; - - /* ACK frame completed */ - pxa2xx_dma_eof_set(s, ch); - } - } - - if (s->control[0] & LCCR0_DIS) { - /* ACK last frame completed */ - s->control[0] &= ~LCCR0_ENB; - s->status[0] |= LCSR0_LDD; - } - - if (miny >= 0) { - switch (s->orientation) { - case 0: - dpy_gfx_update(s->con, 0, miny, s->xres, maxy - miny + 1); - break; - case 90: - dpy_gfx_update(s->con, miny, 0, maxy - miny + 1, s->xres); - break; - case 180: - maxy = s->yres - maxy - 1; - miny = s->yres - miny - 1; - dpy_gfx_update(s->con, 0, maxy, s->xres, miny - maxy + 1); - break; - case 270: - maxy = s->yres - maxy - 1; - miny = s->yres - miny - 1; - dpy_gfx_update(s->con, maxy, 0, miny - maxy + 1, s->xres); - break; - } - } - pxa2xx_lcdc_int_update(s); - - qemu_irq_raise(s->vsync_cb); -} - -static void pxa2xx_invalidate_display(void *opaque) -{ - PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - s->invalidated = 1; -} - -static void pxa2xx_lcdc_orientation(void *opaque, int angle) -{ - PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - - switch (angle) { - case 0: - s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot0; - break; - case 90: - s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot90; - break; - case 180: - s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot180; - break; - case 270: - s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot270; - break; - } - - s->orientation = angle; - s->xres = s->yres = -1; - pxa2xx_lcdc_resize(s); -} - -static const VMStateDescription vmstate_dma_channel = { - .name = "dma_channel", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(branch, struct DMAChannel), - VMSTATE_UINT8(up, struct DMAChannel), - VMSTATE_BUFFER(pbuffer, struct DMAChannel), - VMSTATE_UINT32(descriptor, struct DMAChannel), - VMSTATE_UINT32(source, struct DMAChannel), - VMSTATE_UINT32(id, struct DMAChannel), - VMSTATE_UINT32(command, struct DMAChannel), - VMSTATE_END_OF_LIST() - } -}; - -static int pxa2xx_lcdc_post_load(void *opaque, int version_id) -{ - PXA2xxLCDState *s = opaque; - - s->bpp = LCCR3_BPP(s->control[3]); - s->xres = s->yres = s->pal_for = -1; - - return 0; -} - -static const VMStateDescription vmstate_pxa2xx_lcdc = { - .name = "pxa2xx_lcdc", - .version_id = 0, - .minimum_version_id = 0, - .post_load = pxa2xx_lcdc_post_load, - .fields = (VMStateField[]) { - VMSTATE_INT32(irqlevel, PXA2xxLCDState), - VMSTATE_INT32(transp, PXA2xxLCDState), - VMSTATE_UINT32_ARRAY(control, PXA2xxLCDState, 6), - VMSTATE_UINT32_ARRAY(status, PXA2xxLCDState, 2), - VMSTATE_UINT32_ARRAY(ovl1c, PXA2xxLCDState, 2), - VMSTATE_UINT32_ARRAY(ovl2c, PXA2xxLCDState, 2), - VMSTATE_UINT32(ccr, PXA2xxLCDState), - VMSTATE_UINT32(cmdcr, PXA2xxLCDState), - VMSTATE_UINT32(trgbr, PXA2xxLCDState), - VMSTATE_UINT32(tcr, PXA2xxLCDState), - VMSTATE_UINT32(liidr, PXA2xxLCDState), - VMSTATE_UINT8(bscntr, PXA2xxLCDState), - VMSTATE_STRUCT_ARRAY(dma_ch, PXA2xxLCDState, 7, 0, - vmstate_dma_channel, struct DMAChannel), - VMSTATE_END_OF_LIST() - } -}; - -#define BITS 8 -#include "pxa2xx_template.h" -#define BITS 15 -#include "pxa2xx_template.h" -#define BITS 16 -#include "pxa2xx_template.h" -#define BITS 24 -#include "pxa2xx_template.h" -#define BITS 32 -#include "pxa2xx_template.h" - -static const GraphicHwOps pxa2xx_ops = { - .invalidate = pxa2xx_invalidate_display, - .gfx_update = pxa2xx_update_display, -}; - -PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem, - hwaddr base, qemu_irq irq) -{ - PXA2xxLCDState *s; - DisplaySurface *surface; - - s = (PXA2xxLCDState *) g_malloc0(sizeof(PXA2xxLCDState)); - s->invalidated = 1; - s->irq = irq; - s->sysmem = sysmem; - - pxa2xx_lcdc_orientation(s, graphic_rotate); - - memory_region_init_io(&s->iomem, NULL, &pxa2xx_lcdc_ops, s, - "pxa2xx-lcd-controller", 0x00100000); - memory_region_add_subregion(sysmem, base, &s->iomem); - - s->con = graphic_console_init(NULL, 0, &pxa2xx_ops, s); - surface = qemu_console_surface(s->con); - - switch (surface_bits_per_pixel(surface)) { - case 0: - s->dest_width = 0; - break; - case 8: - s->line_fn[0] = pxa2xx_draw_fn_8; - s->line_fn[1] = pxa2xx_draw_fn_8t; - s->dest_width = 1; - break; - case 15: - s->line_fn[0] = pxa2xx_draw_fn_15; - s->line_fn[1] = pxa2xx_draw_fn_15t; - s->dest_width = 2; - break; - case 16: - s->line_fn[0] = pxa2xx_draw_fn_16; - s->line_fn[1] = pxa2xx_draw_fn_16t; - s->dest_width = 2; - break; - case 24: - s->line_fn[0] = pxa2xx_draw_fn_24; - s->line_fn[1] = pxa2xx_draw_fn_24t; - s->dest_width = 3; - break; - case 32: - s->line_fn[0] = pxa2xx_draw_fn_32; - s->line_fn[1] = pxa2xx_draw_fn_32t; - s->dest_width = 4; - break; - default: - fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__); - exit(1); - } - - vmstate_register(NULL, 0, &vmstate_pxa2xx_lcdc, s); - - return s; -} - -void pxa2xx_lcd_vsync_notifier(PXA2xxLCDState *s, qemu_irq handler) -{ - s->vsync_cb = handler; -} diff --git a/qemu/hw/display/pxa2xx_template.h b/qemu/hw/display/pxa2xx_template.h deleted file mode 100644 index c64eebc4b..000000000 --- a/qemu/hw/display/pxa2xx_template.h +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Intel XScale PXA255/270 LCDC emulation. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GPLv2. - * - * Framebuffer format conversion routines. - */ - -# define SKIP_PIXEL(to) to += deststep -#if BITS == 8 -# define COPY_PIXEL(to, from) do { *to = from; SKIP_PIXEL(to); } while (0) -#elif BITS == 15 || BITS == 16 -# define COPY_PIXEL(to, from) \ - do { \ - *(uint16_t *) to = from; \ - SKIP_PIXEL(to); \ - } while (0) -#elif BITS == 24 -# define COPY_PIXEL(to, from) \ - do { \ - *(uint16_t *) to = from; \ - *(to + 2) = (from) >> 16; \ - SKIP_PIXEL(to); \ - } while (0) -#elif BITS == 32 -# define COPY_PIXEL(to, from) \ - do { \ - *(uint32_t *) to = from; \ - SKIP_PIXEL(to); \ - } while (0) -#else -# error unknown bit depth -#endif - -#ifdef HOST_WORDS_BIGENDIAN -# define SWAP_WORDS 1 -#endif - -#define FN_2(x) FN(x + 1) FN(x) -#define FN_4(x) FN_2(x + 2) FN_2(x) - -static void glue(pxa2xx_draw_line2_, BITS)(void *opaque, - uint8_t *dest, const uint8_t *src, int width, int deststep) -{ - uint32_t *palette = opaque; - uint32_t data; - while (width > 0) { - data = *(uint32_t *) src; -#define FN(x) COPY_PIXEL(dest, palette[(data >> ((x) * 2)) & 3]); -#ifdef SWAP_WORDS - FN_4(12) - FN_4(8) - FN_4(4) - FN_4(0) -#else - FN_4(0) - FN_4(4) - FN_4(8) - FN_4(12) -#endif -#undef FN - width -= 16; - src += 4; - } -} - -static void glue(pxa2xx_draw_line4_, BITS)(void *opaque, - uint8_t *dest, const uint8_t *src, int width, int deststep) -{ - uint32_t *palette = opaque; - uint32_t data; - while (width > 0) { - data = *(uint32_t *) src; -#define FN(x) COPY_PIXEL(dest, palette[(data >> ((x) * 4)) & 0xf]); -#ifdef SWAP_WORDS - FN_2(6) - FN_2(4) - FN_2(2) - FN_2(0) -#else - FN_2(0) - FN_2(2) - FN_2(4) - FN_2(6) -#endif -#undef FN - width -= 8; - src += 4; - } -} - -static void glue(pxa2xx_draw_line8_, BITS)(void *opaque, - uint8_t *dest, const uint8_t *src, int width, int deststep) -{ - uint32_t *palette = opaque; - uint32_t data; - while (width > 0) { - data = *(uint32_t *) src; -#define FN(x) COPY_PIXEL(dest, palette[(data >> (x)) & 0xff]); -#ifdef SWAP_WORDS - FN(24) - FN(16) - FN(8) - FN(0) -#else - FN(0) - FN(8) - FN(16) - FN(24) -#endif -#undef FN - width -= 4; - src += 4; - } -} - -static void glue(pxa2xx_draw_line16_, BITS)(void *opaque, - uint8_t *dest, const uint8_t *src, int width, int deststep) -{ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *) src; -#ifdef SWAP_WORDS - data = bswap32(data); -#endif - b = (data & 0x1f) << 3; - data >>= 5; - g = (data & 0x3f) << 2; - data >>= 6; - r = (data & 0x1f) << 3; - data >>= 5; - COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); - b = (data & 0x1f) << 3; - data >>= 5; - g = (data & 0x3f) << 2; - data >>= 6; - r = (data & 0x1f) << 3; - COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); - width -= 2; - src += 4; - } -} - -static void glue(pxa2xx_draw_line16t_, BITS)(void *opaque, - uint8_t *dest, const uint8_t *src, int width, int deststep) -{ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *) src; -#ifdef SWAP_WORDS - data = bswap32(data); -#endif - b = (data & 0x1f) << 3; - data >>= 5; - g = (data & 0x1f) << 3; - data >>= 5; - r = (data & 0x1f) << 3; - data >>= 5; - if (data & 1) - SKIP_PIXEL(dest); - else - COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); - data >>= 1; - b = (data & 0x1f) << 3; - data >>= 5; - g = (data & 0x1f) << 3; - data >>= 5; - r = (data & 0x1f) << 3; - data >>= 5; - if (data & 1) - SKIP_PIXEL(dest); - else - COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); - width -= 2; - src += 4; - } -} - -static void glue(pxa2xx_draw_line18_, BITS)(void *opaque, - uint8_t *dest, const uint8_t *src, int width, int deststep) -{ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *) src; -#ifdef SWAP_WORDS - data = bswap32(data); -#endif - b = (data & 0x3f) << 2; - data >>= 6; - g = (data & 0x3f) << 2; - data >>= 6; - r = (data & 0x3f) << 2; - COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); - width -= 1; - src += 4; - } -} - -/* The wicked packed format */ -static void glue(pxa2xx_draw_line18p_, BITS)(void *opaque, - uint8_t *dest, const uint8_t *src, int width, int deststep) -{ - uint32_t data[3]; - unsigned int r, g, b; - while (width > 0) { - data[0] = *(uint32_t *) src; - src += 4; - data[1] = *(uint32_t *) src; - src += 4; - data[2] = *(uint32_t *) src; - src += 4; -#ifdef SWAP_WORDS - data[0] = bswap32(data[0]); - data[1] = bswap32(data[1]); - data[2] = bswap32(data[2]); -#endif - b = (data[0] & 0x3f) << 2; - data[0] >>= 6; - g = (data[0] & 0x3f) << 2; - data[0] >>= 6; - r = (data[0] & 0x3f) << 2; - data[0] >>= 12; - COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); - b = (data[0] & 0x3f) << 2; - data[0] >>= 6; - g = ((data[1] & 0xf) << 4) | (data[0] << 2); - data[1] >>= 4; - r = (data[1] & 0x3f) << 2; - data[1] >>= 12; - COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); - b = (data[1] & 0x3f) << 2; - data[1] >>= 6; - g = (data[1] & 0x3f) << 2; - data[1] >>= 6; - r = ((data[2] & 0x3) << 6) | (data[1] << 2); - data[2] >>= 8; - COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); - b = (data[2] & 0x3f) << 2; - data[2] >>= 6; - g = (data[2] & 0x3f) << 2; - data[2] >>= 6; - r = data[2] << 2; - COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); - width -= 4; - } -} - -static void glue(pxa2xx_draw_line19_, BITS)(void *opaque, - uint8_t *dest, const uint8_t *src, int width, int deststep) -{ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *) src; -#ifdef SWAP_WORDS - data = bswap32(data); -#endif - b = (data & 0x3f) << 2; - data >>= 6; - g = (data & 0x3f) << 2; - data >>= 6; - r = (data & 0x3f) << 2; - data >>= 6; - if (data & 1) - SKIP_PIXEL(dest); - else - COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); - width -= 1; - src += 4; - } -} - -/* The wicked packed format */ -static void glue(pxa2xx_draw_line19p_, BITS)(void *opaque, - uint8_t *dest, const uint8_t *src, int width, int deststep) -{ - uint32_t data[3]; - unsigned int r, g, b; - while (width > 0) { - data[0] = *(uint32_t *) src; - src += 4; - data[1] = *(uint32_t *) src; - src += 4; - data[2] = *(uint32_t *) src; - src += 4; -# ifdef SWAP_WORDS - data[0] = bswap32(data[0]); - data[1] = bswap32(data[1]); - data[2] = bswap32(data[2]); -# endif - b = (data[0] & 0x3f) << 2; - data[0] >>= 6; - g = (data[0] & 0x3f) << 2; - data[0] >>= 6; - r = (data[0] & 0x3f) << 2; - data[0] >>= 6; - if (data[0] & 1) - SKIP_PIXEL(dest); - else - COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); - data[0] >>= 6; - b = (data[0] & 0x3f) << 2; - data[0] >>= 6; - g = ((data[1] & 0xf) << 4) | (data[0] << 2); - data[1] >>= 4; - r = (data[1] & 0x3f) << 2; - data[1] >>= 6; - if (data[1] & 1) - SKIP_PIXEL(dest); - else - COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); - data[1] >>= 6; - b = (data[1] & 0x3f) << 2; - data[1] >>= 6; - g = (data[1] & 0x3f) << 2; - data[1] >>= 6; - r = ((data[2] & 0x3) << 6) | (data[1] << 2); - data[2] >>= 2; - if (data[2] & 1) - SKIP_PIXEL(dest); - else - COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); - data[2] >>= 6; - b = (data[2] & 0x3f) << 2; - data[2] >>= 6; - g = (data[2] & 0x3f) << 2; - data[2] >>= 6; - r = data[2] << 2; - data[2] >>= 6; - if (data[2] & 1) - SKIP_PIXEL(dest); - else - COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); - width -= 4; - } -} - -static void glue(pxa2xx_draw_line24_, BITS)(void *opaque, - uint8_t *dest, const uint8_t *src, int width, int deststep) -{ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *) src; -#ifdef SWAP_WORDS - data = bswap32(data); -#endif - b = data & 0xff; - data >>= 8; - g = data & 0xff; - data >>= 8; - r = data & 0xff; - COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); - width -= 1; - src += 4; - } -} - -static void glue(pxa2xx_draw_line24t_, BITS)(void *opaque, - uint8_t *dest, const uint8_t *src, int width, int deststep) -{ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *) src; -#ifdef SWAP_WORDS - data = bswap32(data); -#endif - b = (data & 0x7f) << 1; - data >>= 7; - g = data & 0xff; - data >>= 8; - r = data & 0xff; - data >>= 8; - if (data & 1) - SKIP_PIXEL(dest); - else - COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); - width -= 1; - src += 4; - } -} - -static void glue(pxa2xx_draw_line25_, BITS)(void *opaque, - uint8_t *dest, const uint8_t *src, int width, int deststep) -{ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *) src; -#ifdef SWAP_WORDS - data = bswap32(data); -#endif - b = data & 0xff; - data >>= 8; - g = data & 0xff; - data >>= 8; - r = data & 0xff; - data >>= 8; - if (data & 1) - SKIP_PIXEL(dest); - else - COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b)); - width -= 1; - src += 4; - } -} - -/* Overlay planes disabled, no transparency */ -static drawfn glue(pxa2xx_draw_fn_, BITS)[16] = -{ - [0 ... 0xf] = NULL, - [pxa_lcdc_2bpp] = glue(pxa2xx_draw_line2_, BITS), - [pxa_lcdc_4bpp] = glue(pxa2xx_draw_line4_, BITS), - [pxa_lcdc_8bpp] = glue(pxa2xx_draw_line8_, BITS), - [pxa_lcdc_16bpp] = glue(pxa2xx_draw_line16_, BITS), - [pxa_lcdc_18bpp] = glue(pxa2xx_draw_line18_, BITS), - [pxa_lcdc_18pbpp] = glue(pxa2xx_draw_line18p_, BITS), - [pxa_lcdc_24bpp] = glue(pxa2xx_draw_line24_, BITS), -}; - -/* Overlay planes enabled, transparency used */ -static drawfn glue(glue(pxa2xx_draw_fn_, BITS), t)[16] = -{ - [0 ... 0xf] = NULL, - [pxa_lcdc_4bpp] = glue(pxa2xx_draw_line4_, BITS), - [pxa_lcdc_8bpp] = glue(pxa2xx_draw_line8_, BITS), - [pxa_lcdc_16bpp] = glue(pxa2xx_draw_line16t_, BITS), - [pxa_lcdc_19bpp] = glue(pxa2xx_draw_line19_, BITS), - [pxa_lcdc_19pbpp] = glue(pxa2xx_draw_line19p_, BITS), - [pxa_lcdc_24bpp] = glue(pxa2xx_draw_line24t_, BITS), - [pxa_lcdc_25bpp] = glue(pxa2xx_draw_line25_, BITS), -}; - -#undef BITS -#undef COPY_PIXEL -#undef SKIP_PIXEL - -#ifdef SWAP_WORDS -# undef SWAP_WORDS -#endif diff --git a/qemu/hw/display/qxl-logger.c b/qemu/hw/display/qxl-logger.c deleted file mode 100644 index 2ec6d8fa3..000000000 --- a/qemu/hw/display/qxl-logger.c +++ /dev/null @@ -1,276 +0,0 @@ -/* - * qxl command logging -- for debug purposes - * - * Copyright (C) 2010 Red Hat, Inc. - * - * maintained by Gerd Hoffmann - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "qemu/timer.h" -#include "qxl.h" - -static const char *const qxl_type[] = { - [ QXL_CMD_NOP ] = "nop", - [ QXL_CMD_DRAW ] = "draw", - [ QXL_CMD_UPDATE ] = "update", - [ QXL_CMD_CURSOR ] = "cursor", - [ QXL_CMD_MESSAGE ] = "message", - [ QXL_CMD_SURFACE ] = "surface", -}; - -static const char *const qxl_draw_type[] = { - [ QXL_DRAW_NOP ] = "nop", - [ QXL_DRAW_FILL ] = "fill", - [ QXL_DRAW_OPAQUE ] = "opaque", - [ QXL_DRAW_COPY ] = "copy", - [ QXL_COPY_BITS ] = "copy-bits", - [ QXL_DRAW_BLEND ] = "blend", - [ QXL_DRAW_BLACKNESS ] = "blackness", - [ QXL_DRAW_WHITENESS ] = "whitemess", - [ QXL_DRAW_INVERS ] = "invers", - [ QXL_DRAW_ROP3 ] = "rop3", - [ QXL_DRAW_STROKE ] = "stroke", - [ QXL_DRAW_TEXT ] = "text", - [ QXL_DRAW_TRANSPARENT ] = "transparent", - [ QXL_DRAW_ALPHA_BLEND ] = "alpha-blend", -}; - -static const char *const qxl_draw_effect[] = { - [ QXL_EFFECT_BLEND ] = "blend", - [ QXL_EFFECT_OPAQUE ] = "opaque", - [ QXL_EFFECT_REVERT_ON_DUP ] = "revert-on-dup", - [ QXL_EFFECT_BLACKNESS_ON_DUP ] = "blackness-on-dup", - [ QXL_EFFECT_WHITENESS_ON_DUP ] = "whiteness-on-dup", - [ QXL_EFFECT_NOP_ON_DUP ] = "nop-on-dup", - [ QXL_EFFECT_NOP ] = "nop", - [ QXL_EFFECT_OPAQUE_BRUSH ] = "opaque-brush", -}; - -static const char *const qxl_surface_cmd[] = { - [ QXL_SURFACE_CMD_CREATE ] = "create", - [ QXL_SURFACE_CMD_DESTROY ] = "destroy", -}; - -static const char *const spice_surface_fmt[] = { - [ SPICE_SURFACE_FMT_INVALID ] = "invalid", - [ SPICE_SURFACE_FMT_1_A ] = "alpha/1", - [ SPICE_SURFACE_FMT_8_A ] = "alpha/8", - [ SPICE_SURFACE_FMT_16_555 ] = "555/16", - [ SPICE_SURFACE_FMT_16_565 ] = "565/16", - [ SPICE_SURFACE_FMT_32_xRGB ] = "xRGB/32", - [ SPICE_SURFACE_FMT_32_ARGB ] = "ARGB/32", -}; - -static const char *const qxl_cursor_cmd[] = { - [ QXL_CURSOR_SET ] = "set", - [ QXL_CURSOR_MOVE ] = "move", - [ QXL_CURSOR_HIDE ] = "hide", - [ QXL_CURSOR_TRAIL ] = "trail", -}; - -static const char *const spice_cursor_type[] = { - [ SPICE_CURSOR_TYPE_ALPHA ] = "alpha", - [ SPICE_CURSOR_TYPE_MONO ] = "mono", - [ SPICE_CURSOR_TYPE_COLOR4 ] = "color4", - [ SPICE_CURSOR_TYPE_COLOR8 ] = "color8", - [ SPICE_CURSOR_TYPE_COLOR16 ] = "color16", - [ SPICE_CURSOR_TYPE_COLOR24 ] = "color24", - [ SPICE_CURSOR_TYPE_COLOR32 ] = "color32", -}; - -static const char *qxl_v2n(const char *const n[], size_t l, int v) -{ - if (v >= l || !n[v]) { - return "???"; - } - return n[v]; -} -#define qxl_name(_list, _value) qxl_v2n(_list, ARRAY_SIZE(_list), _value) - -static int qxl_log_image(PCIQXLDevice *qxl, QXLPHYSICAL addr, int group_id) -{ - QXLImage *image; - QXLImageDescriptor *desc; - - image = qxl_phys2virt(qxl, addr, group_id); - if (!image) { - return 1; - } - desc = &image->descriptor; - fprintf(stderr, " (id %" PRIx64 " type %d flags %d width %d height %d", - desc->id, desc->type, desc->flags, desc->width, desc->height); - switch (desc->type) { - case SPICE_IMAGE_TYPE_BITMAP: - fprintf(stderr, ", fmt %d flags %d x %d y %d stride %d" - " palette %" PRIx64 " data %" PRIx64, - image->bitmap.format, image->bitmap.flags, - image->bitmap.x, image->bitmap.y, - image->bitmap.stride, - image->bitmap.palette, image->bitmap.data); - break; - } - fprintf(stderr, ")"); - return 0; -} - -static void qxl_log_rect(QXLRect *rect) -{ - fprintf(stderr, " %dx%d+%d+%d", - rect->right - rect->left, - rect->bottom - rect->top, - rect->left, rect->top); -} - -static int qxl_log_cmd_draw_copy(PCIQXLDevice *qxl, QXLCopy *copy, - int group_id) -{ - int ret; - - fprintf(stderr, " src %" PRIx64, - copy->src_bitmap); - ret = qxl_log_image(qxl, copy->src_bitmap, group_id); - if (ret != 0) { - return ret; - } - fprintf(stderr, " area"); - qxl_log_rect(©->src_area); - fprintf(stderr, " rop %d", copy->rop_descriptor); - return 0; -} - -static int qxl_log_cmd_draw(PCIQXLDevice *qxl, QXLDrawable *draw, int group_id) -{ - fprintf(stderr, ": surface_id %d type %s effect %s", - draw->surface_id, - qxl_name(qxl_draw_type, draw->type), - qxl_name(qxl_draw_effect, draw->effect)); - switch (draw->type) { - case QXL_DRAW_COPY: - return qxl_log_cmd_draw_copy(qxl, &draw->u.copy, group_id); - break; - } - return 0; -} - -static int qxl_log_cmd_draw_compat(PCIQXLDevice *qxl, QXLCompatDrawable *draw, - int group_id) -{ - fprintf(stderr, ": type %s effect %s", - qxl_name(qxl_draw_type, draw->type), - qxl_name(qxl_draw_effect, draw->effect)); - if (draw->bitmap_offset) { - fprintf(stderr, ": bitmap %d", - draw->bitmap_offset); - qxl_log_rect(&draw->bitmap_area); - } - switch (draw->type) { - case QXL_DRAW_COPY: - return qxl_log_cmd_draw_copy(qxl, &draw->u.copy, group_id); - break; - } - return 0; -} - -static void qxl_log_cmd_surface(PCIQXLDevice *qxl, QXLSurfaceCmd *cmd) -{ - fprintf(stderr, ": %s id %d", - qxl_name(qxl_surface_cmd, cmd->type), - cmd->surface_id); - if (cmd->type == QXL_SURFACE_CMD_CREATE) { - fprintf(stderr, " size %dx%d stride %d format %s (count %d, max %d)", - cmd->u.surface_create.width, - cmd->u.surface_create.height, - cmd->u.surface_create.stride, - qxl_name(spice_surface_fmt, cmd->u.surface_create.format), - qxl->guest_surfaces.count, qxl->guest_surfaces.max); - } - if (cmd->type == QXL_SURFACE_CMD_DESTROY) { - fprintf(stderr, " (count %d)", qxl->guest_surfaces.count); - } -} - -int qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id) -{ - QXLCursor *cursor; - - fprintf(stderr, ": %s", - qxl_name(qxl_cursor_cmd, cmd->type)); - switch (cmd->type) { - case QXL_CURSOR_SET: - fprintf(stderr, " +%d+%d visible %s, shape @ 0x%" PRIx64, - cmd->u.set.position.x, - cmd->u.set.position.y, - cmd->u.set.visible ? "yes" : "no", - cmd->u.set.shape); - cursor = qxl_phys2virt(qxl, cmd->u.set.shape, group_id); - if (!cursor) { - return 1; - } - fprintf(stderr, " type %s size %dx%d hot-spot +%d+%d" - " unique 0x%" PRIx64 " data-size %d", - qxl_name(spice_cursor_type, cursor->header.type), - cursor->header.width, cursor->header.height, - cursor->header.hot_spot_x, cursor->header.hot_spot_y, - cursor->header.unique, cursor->data_size); - break; - case QXL_CURSOR_MOVE: - fprintf(stderr, " +%d+%d", cmd->u.position.x, cmd->u.position.y); - break; - } - return 0; -} - -int qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext) -{ - bool compat = ext->flags & QXL_COMMAND_FLAG_COMPAT; - void *data; - int ret; - - if (!qxl->cmdlog) { - return 0; - } - fprintf(stderr, "%" PRId64 " qxl-%d/%s:", qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - qxl->id, ring); - fprintf(stderr, " cmd @ 0x%" PRIx64 " %s%s", ext->cmd.data, - qxl_name(qxl_type, ext->cmd.type), - compat ? "(compat)" : ""); - - data = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); - if (!data) { - return 1; - } - switch (ext->cmd.type) { - case QXL_CMD_DRAW: - if (!compat) { - ret = qxl_log_cmd_draw(qxl, data, ext->group_id); - } else { - ret = qxl_log_cmd_draw_compat(qxl, data, ext->group_id); - } - if (ret) { - return ret; - } - break; - case QXL_CMD_SURFACE: - qxl_log_cmd_surface(qxl, data); - break; - case QXL_CMD_CURSOR: - qxl_log_cmd_cursor(qxl, data, ext->group_id); - break; - } - fprintf(stderr, "\n"); - return 0; -} diff --git a/qemu/hw/display/qxl-render.c b/qemu/hw/display/qxl-render.c deleted file mode 100644 index 9ad9d9e0f..000000000 --- a/qemu/hw/display/qxl-render.c +++ /dev/null @@ -1,298 +0,0 @@ -/* - * qxl local rendering (aka display on sdl/vnc) - * - * Copyright (C) 2010 Red Hat, Inc. - * - * maintained by Gerd Hoffmann - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "qxl.h" -#include "trace.h" - -static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) -{ - DisplaySurface *surface = qemu_console_surface(qxl->vga.con); - uint8_t *dst = surface_data(surface); - uint8_t *src; - int len, i; - - if (is_buffer_shared(surface)) { - return; - } - trace_qxl_render_blit(qxl->guest_primary.qxl_stride, - rect->left, rect->right, rect->top, rect->bottom); - src = qxl->guest_primary.data; - if (qxl->guest_primary.qxl_stride < 0) { - /* qxl surface is upside down, walk src scanlines - * in reverse order to flip it */ - src += (qxl->guest_primary.surface.height - rect->top - 1) * - qxl->guest_primary.abs_stride; - } else { - src += rect->top * qxl->guest_primary.abs_stride; - } - dst += rect->top * qxl->guest_primary.abs_stride; - src += rect->left * qxl->guest_primary.bytes_pp; - dst += rect->left * qxl->guest_primary.bytes_pp; - len = (rect->right - rect->left) * qxl->guest_primary.bytes_pp; - - for (i = rect->top; i < rect->bottom; i++) { - memcpy(dst, src, len); - dst += qxl->guest_primary.abs_stride; - src += qxl->guest_primary.qxl_stride; - } -} - -void qxl_render_resize(PCIQXLDevice *qxl) -{ - QXLSurfaceCreate *sc = &qxl->guest_primary.surface; - - qxl->guest_primary.qxl_stride = sc->stride; - qxl->guest_primary.abs_stride = abs(sc->stride); - qxl->guest_primary.resized++; - switch (sc->format) { - case SPICE_SURFACE_FMT_16_555: - qxl->guest_primary.bytes_pp = 2; - qxl->guest_primary.bits_pp = 15; - break; - case SPICE_SURFACE_FMT_16_565: - qxl->guest_primary.bytes_pp = 2; - qxl->guest_primary.bits_pp = 16; - break; - case SPICE_SURFACE_FMT_32_xRGB: - case SPICE_SURFACE_FMT_32_ARGB: - qxl->guest_primary.bytes_pp = 4; - qxl->guest_primary.bits_pp = 32; - break; - default: - fprintf(stderr, "%s: unhandled format: %x\n", __FUNCTION__, - qxl->guest_primary.surface.format); - qxl->guest_primary.bytes_pp = 4; - qxl->guest_primary.bits_pp = 32; - break; - } -} - -static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area) -{ - area->left = 0; - area->right = qxl->guest_primary.surface.width; - area->top = 0; - area->bottom = qxl->guest_primary.surface.height; -} - -static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) -{ - VGACommonState *vga = &qxl->vga; - DisplaySurface *surface; - int i; - - if (qxl->guest_primary.resized) { - qxl->guest_primary.resized = 0; - qxl->guest_primary.data = qxl_phys2virt(qxl, - qxl->guest_primary.surface.mem, - MEMSLOT_GROUP_GUEST); - if (!qxl->guest_primary.data) { - return; - } - qxl_set_rect_to_surface(qxl, &qxl->dirty[0]); - qxl->num_dirty_rects = 1; - trace_qxl_render_guest_primary_resized( - qxl->guest_primary.surface.width, - qxl->guest_primary.surface.height, - qxl->guest_primary.qxl_stride, - qxl->guest_primary.bytes_pp, - qxl->guest_primary.bits_pp); - if (qxl->guest_primary.qxl_stride > 0) { - pixman_format_code_t format = - qemu_default_pixman_format(qxl->guest_primary.bits_pp, true); - surface = qemu_create_displaysurface_from - (qxl->guest_primary.surface.width, - qxl->guest_primary.surface.height, - format, - qxl->guest_primary.abs_stride, - qxl->guest_primary.data); - } else { - surface = qemu_create_displaysurface - (qxl->guest_primary.surface.width, - qxl->guest_primary.surface.height); - } - dpy_gfx_replace_surface(vga->con, surface); - } - - if (!qxl->guest_primary.data) { - return; - } - for (i = 0; i < qxl->num_dirty_rects; i++) { - if (qemu_spice_rect_is_empty(qxl->dirty+i)) { - break; - } - if (qxl->dirty[i].left < 0 || - qxl->dirty[i].top < 0 || - qxl->dirty[i].left > qxl->dirty[i].right || - qxl->dirty[i].top > qxl->dirty[i].bottom || - qxl->dirty[i].right > qxl->guest_primary.surface.width || - qxl->dirty[i].bottom > qxl->guest_primary.surface.height) { - continue; - } - qxl_blit(qxl, qxl->dirty+i); - dpy_gfx_update(vga->con, - qxl->dirty[i].left, qxl->dirty[i].top, - qxl->dirty[i].right - qxl->dirty[i].left, - qxl->dirty[i].bottom - qxl->dirty[i].top); - } - qxl->num_dirty_rects = 0; -} - -/* - * 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, deferring to bh called from the - * io thread. - */ -void qxl_render_update(PCIQXLDevice *qxl) -{ - QXLCookie *cookie; - - qemu_mutex_lock(&qxl->ssd.lock); - - if (!runstate_is_running() || !qxl->guest_primary.commands) { - qxl_render_update_area_unlocked(qxl); - qemu_mutex_unlock(&qxl->ssd.lock); - return; - } - - qxl->guest_primary.commands = 0; - qxl->render_update_cookie_num++; - qemu_mutex_unlock(&qxl->ssd.lock); - cookie = qxl_cookie_new(QXL_COOKIE_TYPE_RENDER_UPDATE_AREA, - 0); - qxl_set_rect_to_surface(qxl, &cookie->u.render.area); - qxl_spice_update_area(qxl, 0, &cookie->u.render.area, NULL, - 0, 1 /* clear_dirty_region */, QXL_ASYNC, cookie); -} - -void qxl_render_update_area_bh(void *opaque) -{ - PCIQXLDevice *qxl = opaque; - - qemu_mutex_lock(&qxl->ssd.lock); - qxl_render_update_area_unlocked(qxl); - qemu_mutex_unlock(&qxl->ssd.lock); -} - -void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie) -{ - qemu_mutex_lock(&qxl->ssd.lock); - trace_qxl_render_update_area_done(cookie); - qemu_bh_schedule(qxl->update_area_bh); - qxl->render_update_cookie_num--; - qemu_mutex_unlock(&qxl->ssd.lock); - g_free(cookie); -} - -static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor) -{ - QEMUCursor *c; - uint8_t *image, *mask; - size_t size; - - c = cursor_alloc(cursor->header.width, cursor->header.height); - c->hot_x = cursor->header.hot_spot_x; - c->hot_y = cursor->header.hot_spot_y; - switch (cursor->header.type) { - case SPICE_CURSOR_TYPE_ALPHA: - size = sizeof(uint32_t) * cursor->header.width * cursor->header.height; - memcpy(c->data, cursor->chunk.data, size); - if (qxl->debug > 2) { - cursor_print_ascii_art(c, "qxl/alpha"); - } - break; - case SPICE_CURSOR_TYPE_MONO: - mask = cursor->chunk.data; - image = mask + cursor_get_mono_bpl(c) * c->width; - cursor_set_mono(c, 0xffffff, 0x000000, image, 1, mask); - if (qxl->debug > 2) { - cursor_print_ascii_art(c, "qxl/mono"); - } - break; - default: - fprintf(stderr, "%s: not implemented: type %d\n", - __FUNCTION__, cursor->header.type); - goto fail; - } - return c; - -fail: - cursor_put(c); - return NULL; -} - - -/* called from spice server thread context only */ -int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) -{ - QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); - QXLCursor *cursor; - QEMUCursor *c; - - if (!cmd) { - return 1; - } - - if (!dpy_cursor_define_supported(qxl->vga.con)) { - return 0; - } - - if (qxl->debug > 1 && cmd->type != QXL_CURSOR_MOVE) { - fprintf(stderr, "%s", __FUNCTION__); - qxl_log_cmd_cursor(qxl, cmd, ext->group_id); - fprintf(stderr, "\n"); - } - switch (cmd->type) { - case QXL_CURSOR_SET: - cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id); - if (!cursor) { - return 1; - } - if (cursor->chunk.data_size != cursor->data_size) { - fprintf(stderr, "%s: multiple chunks\n", __FUNCTION__); - return 1; - } - c = qxl_cursor(qxl, cursor); - if (c == NULL) { - c = cursor_builtin_left_ptr(); - } - qemu_mutex_lock(&qxl->ssd.lock); - if (qxl->ssd.cursor) { - cursor_put(qxl->ssd.cursor); - } - qxl->ssd.cursor = c; - qxl->ssd.mouse_x = cmd->u.set.position.x; - qxl->ssd.mouse_y = cmd->u.set.position.y; - qemu_mutex_unlock(&qxl->ssd.lock); - qemu_bh_schedule(qxl->ssd.cursor_bh); - break; - case QXL_CURSOR_MOVE: - qemu_mutex_lock(&qxl->ssd.lock); - qxl->ssd.mouse_x = cmd->u.position.x; - qxl->ssd.mouse_y = cmd->u.position.y; - qemu_mutex_unlock(&qxl->ssd.lock); - qemu_bh_schedule(qxl->ssd.cursor_bh); - break; - } - return 0; -} diff --git a/qemu/hw/display/qxl.c b/qemu/hw/display/qxl.c deleted file mode 100644 index 919dc5cd3..000000000 --- a/qemu/hw/display/qxl.c +++ /dev/null @@ -1,2359 +0,0 @@ -/* - * Copyright (C) 2010 Red Hat, Inc. - * - * written by Yaniv Kamay, Izik Eidus, Gerd Hoffmann - * maintained by Gerd Hoffmann - * - * 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 . - */ - -#include "qemu/osdep.h" -#include - -#include "qemu-common.h" -#include "qemu/timer.h" -#include "qemu/queue.h" -#include "qemu/atomic.h" -#include "sysemu/sysemu.h" -#include "trace.h" - -#include "qxl.h" - -/* - * NOTE: SPICE_RING_PROD_ITEM accesses memory on the pci bar and as - * such can be changed by the guest, so to avoid a guest trigerrable - * abort we just qxl_set_guest_bug and set the return to NULL. Still - * it may happen as a result of emulator bug as well. - */ -#undef SPICE_RING_PROD_ITEM -#define SPICE_RING_PROD_ITEM(qxl, r, ret) { \ - uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r); \ - if (prod >= ARRAY_SIZE((r)->items)) { \ - qxl_set_guest_bug(qxl, "SPICE_RING_PROD_ITEM indices mismatch " \ - "%u >= %zu", prod, ARRAY_SIZE((r)->items)); \ - ret = NULL; \ - } else { \ - ret = &(r)->items[prod].el; \ - } \ - } - -#undef SPICE_RING_CONS_ITEM -#define SPICE_RING_CONS_ITEM(qxl, r, ret) { \ - uint32_t cons = (r)->cons & SPICE_RING_INDEX_MASK(r); \ - if (cons >= ARRAY_SIZE((r)->items)) { \ - qxl_set_guest_bug(qxl, "SPICE_RING_CONS_ITEM indices mismatch " \ - "%u >= %zu", cons, ARRAY_SIZE((r)->items)); \ - ret = NULL; \ - } else { \ - ret = &(r)->items[cons].el; \ - } \ - } - -#undef ALIGN -#define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1)) - -#define PIXEL_SIZE 0.2936875 //1280x1024 is 14.8" x 11.9" - -#define QXL_MODE(_x, _y, _b, _o) \ - { .x_res = _x, \ - .y_res = _y, \ - .bits = _b, \ - .stride = (_x) * (_b) / 8, \ - .x_mili = PIXEL_SIZE * (_x), \ - .y_mili = PIXEL_SIZE * (_y), \ - .orientation = _o, \ - } - -#define QXL_MODE_16_32(x_res, y_res, orientation) \ - QXL_MODE(x_res, y_res, 16, orientation), \ - QXL_MODE(x_res, y_res, 32, orientation) - -#define QXL_MODE_EX(x_res, y_res) \ - QXL_MODE_16_32(x_res, y_res, 0), \ - QXL_MODE_16_32(x_res, y_res, 1) - -static QXLMode qxl_modes[] = { - QXL_MODE_EX(640, 480), - QXL_MODE_EX(800, 480), - QXL_MODE_EX(800, 600), - QXL_MODE_EX(832, 624), - QXL_MODE_EX(960, 640), - QXL_MODE_EX(1024, 600), - QXL_MODE_EX(1024, 768), - QXL_MODE_EX(1152, 864), - QXL_MODE_EX(1152, 870), - QXL_MODE_EX(1280, 720), - QXL_MODE_EX(1280, 760), - QXL_MODE_EX(1280, 768), - QXL_MODE_EX(1280, 800), - QXL_MODE_EX(1280, 960), - QXL_MODE_EX(1280, 1024), - QXL_MODE_EX(1360, 768), - QXL_MODE_EX(1366, 768), - QXL_MODE_EX(1400, 1050), - QXL_MODE_EX(1440, 900), - QXL_MODE_EX(1600, 900), - QXL_MODE_EX(1600, 1200), - QXL_MODE_EX(1680, 1050), - QXL_MODE_EX(1920, 1080), - /* these modes need more than 8 MB video memory */ - QXL_MODE_EX(1920, 1200), - QXL_MODE_EX(1920, 1440), - QXL_MODE_EX(2000, 2000), - QXL_MODE_EX(2048, 1536), - QXL_MODE_EX(2048, 2048), - QXL_MODE_EX(2560, 1440), - QXL_MODE_EX(2560, 1600), - /* these modes need more than 16 MB video memory */ - QXL_MODE_EX(2560, 2048), - QXL_MODE_EX(2800, 2100), - QXL_MODE_EX(3200, 2400), - /* these modes need more than 32 MB video memory */ - QXL_MODE_EX(3840, 2160), /* 4k mainstream */ - QXL_MODE_EX(4096, 2160), /* 4k */ - /* these modes need more than 64 MB video memory */ - QXL_MODE_EX(7680, 4320), /* 8k mainstream */ - /* these modes need more than 128 MB video memory */ - QXL_MODE_EX(8192, 4320), /* 8k */ -}; - -static void qxl_send_events(PCIQXLDevice *d, uint32_t events); -static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async); -static void qxl_reset_memslots(PCIQXLDevice *d); -static void qxl_reset_surfaces(PCIQXLDevice *d); -static void qxl_ring_set_dirty(PCIQXLDevice *qxl); - -static void qxl_hw_update(void *opaque); - -void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...) -{ - trace_qxl_set_guest_bug(qxl->id); - qxl_send_events(qxl, QXL_INTERRUPT_ERROR); - qxl->guest_bug = 1; - if (qxl->guestdebug) { - va_list ap; - va_start(ap, msg); - fprintf(stderr, "qxl-%d: guest bug: ", qxl->id); - vfprintf(stderr, msg, ap); - fprintf(stderr, "\n"); - va_end(ap); - } -} - -static void qxl_clear_guest_bug(PCIQXLDevice *qxl) -{ - qxl->guest_bug = 0; -} - -void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, - struct QXLRect *area, struct QXLRect *dirty_rects, - uint32_t num_dirty_rects, - uint32_t clear_dirty_region, - qxl_async_io async, struct QXLCookie *cookie) -{ - trace_qxl_spice_update_area(qxl->id, surface_id, area->left, area->right, - area->top, area->bottom); - trace_qxl_spice_update_area_rest(qxl->id, num_dirty_rects, - clear_dirty_region); - if (async == QXL_SYNC) { - spice_qxl_update_area(&qxl->ssd.qxl, surface_id, area, - dirty_rects, num_dirty_rects, clear_dirty_region); - } else { - assert(cookie != NULL); - spice_qxl_update_area_async(&qxl->ssd.qxl, surface_id, area, - clear_dirty_region, (uintptr_t)cookie); - } -} - -static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl, - uint32_t id) -{ - trace_qxl_spice_destroy_surface_wait_complete(qxl->id, id); - qemu_mutex_lock(&qxl->track_lock); - qxl->guest_surfaces.cmds[id] = 0; - qxl->guest_surfaces.count--; - qemu_mutex_unlock(&qxl->track_lock); -} - -static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id, - qxl_async_io async) -{ - QXLCookie *cookie; - - trace_qxl_spice_destroy_surface_wait(qxl->id, id, async); - if (async) { - cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO, - QXL_IO_DESTROY_SURFACE_ASYNC); - cookie->u.surface_id = id; - spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, (uintptr_t)cookie); - } else { - spice_qxl_destroy_surface_wait(&qxl->ssd.qxl, id); - qxl_spice_destroy_surface_wait_complete(qxl, id); - } -} - -static void qxl_spice_flush_surfaces_async(PCIQXLDevice *qxl) -{ - trace_qxl_spice_flush_surfaces_async(qxl->id, qxl->guest_surfaces.count, - qxl->num_free_res); - spice_qxl_flush_surfaces_async(&qxl->ssd.qxl, - (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, - QXL_IO_FLUSH_SURFACES_ASYNC)); -} - -void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, - uint32_t count) -{ - trace_qxl_spice_loadvm_commands(qxl->id, ext, count); - spice_qxl_loadvm_commands(&qxl->ssd.qxl, ext, count); -} - -void qxl_spice_oom(PCIQXLDevice *qxl) -{ - trace_qxl_spice_oom(qxl->id); - spice_qxl_oom(&qxl->ssd.qxl); -} - -void qxl_spice_reset_memslots(PCIQXLDevice *qxl) -{ - trace_qxl_spice_reset_memslots(qxl->id); - spice_qxl_reset_memslots(&qxl->ssd.qxl); -} - -static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl) -{ - trace_qxl_spice_destroy_surfaces_complete(qxl->id); - qemu_mutex_lock(&qxl->track_lock); - memset(qxl->guest_surfaces.cmds, 0, - sizeof(qxl->guest_surfaces.cmds[0]) * qxl->ssd.num_surfaces); - qxl->guest_surfaces.count = 0; - qemu_mutex_unlock(&qxl->track_lock); -} - -static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async) -{ - trace_qxl_spice_destroy_surfaces(qxl->id, async); - if (async) { - spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl, - (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, - QXL_IO_DESTROY_ALL_SURFACES_ASYNC)); - } else { - spice_qxl_destroy_surfaces(&qxl->ssd.qxl); - qxl_spice_destroy_surfaces_complete(qxl); - } -} - -static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay) -{ - trace_qxl_spice_monitors_config(qxl->id); - if (replay) { - /* - * don't use QXL_COOKIE_TYPE_IO: - * - we are not running yet (post_load), we will assert - * in send_events - * - this is not a guest io, but a reply, so async_io isn't set. - */ - spice_qxl_monitors_config_async(&qxl->ssd.qxl, - qxl->guest_monitors_config, - MEMSLOT_GROUP_GUEST, - (uintptr_t)qxl_cookie_new( - QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG, - 0)); - } else { -#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */ - if (qxl->max_outputs) { - spice_qxl_set_max_monitors(&qxl->ssd.qxl, qxl->max_outputs); - } -#endif - qxl->guest_monitors_config = qxl->ram->monitors_config; - spice_qxl_monitors_config_async(&qxl->ssd.qxl, - qxl->ram->monitors_config, - MEMSLOT_GROUP_GUEST, - (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, - QXL_IO_MONITORS_CONFIG_ASYNC)); - } -} - -void qxl_spice_reset_image_cache(PCIQXLDevice *qxl) -{ - trace_qxl_spice_reset_image_cache(qxl->id); - spice_qxl_reset_image_cache(&qxl->ssd.qxl); -} - -void qxl_spice_reset_cursor(PCIQXLDevice *qxl) -{ - trace_qxl_spice_reset_cursor(qxl->id); - spice_qxl_reset_cursor(&qxl->ssd.qxl); - qemu_mutex_lock(&qxl->track_lock); - qxl->guest_cursor = 0; - qemu_mutex_unlock(&qxl->track_lock); - if (qxl->ssd.cursor) { - cursor_put(qxl->ssd.cursor); - } - qxl->ssd.cursor = cursor_builtin_hidden(); -} - -static ram_addr_t qxl_rom_size(void) -{ - uint32_t required_rom_size = sizeof(QXLRom) + sizeof(QXLModes) + - sizeof(qxl_modes); - uint32_t rom_size = 8192; /* two pages */ - - QEMU_BUILD_BUG_ON(required_rom_size > rom_size); - return rom_size; -} - -static void init_qxl_rom(PCIQXLDevice *d) -{ - QXLRom *rom = memory_region_get_ram_ptr(&d->rom_bar); - QXLModes *modes = (QXLModes *)(rom + 1); - uint32_t ram_header_size; - uint32_t surface0_area_size; - uint32_t num_pages; - uint32_t fb; - int i, n; - - memset(rom, 0, d->rom_size); - - rom->magic = cpu_to_le32(QXL_ROM_MAGIC); - rom->id = cpu_to_le32(d->id); - rom->log_level = cpu_to_le32(d->guestdebug); - rom->modes_offset = cpu_to_le32(sizeof(QXLRom)); - - rom->slot_gen_bits = MEMSLOT_GENERATION_BITS; - rom->slot_id_bits = MEMSLOT_SLOT_BITS; - rom->slots_start = 1; - rom->slots_end = NUM_MEMSLOTS - 1; - rom->n_surfaces = cpu_to_le32(d->ssd.num_surfaces); - - for (i = 0, n = 0; i < ARRAY_SIZE(qxl_modes); i++) { - fb = qxl_modes[i].y_res * qxl_modes[i].stride; - if (fb > d->vgamem_size) { - continue; - } - modes->modes[n].id = cpu_to_le32(i); - modes->modes[n].x_res = cpu_to_le32(qxl_modes[i].x_res); - modes->modes[n].y_res = cpu_to_le32(qxl_modes[i].y_res); - modes->modes[n].bits = cpu_to_le32(qxl_modes[i].bits); - modes->modes[n].stride = cpu_to_le32(qxl_modes[i].stride); - modes->modes[n].x_mili = cpu_to_le32(qxl_modes[i].x_mili); - modes->modes[n].y_mili = cpu_to_le32(qxl_modes[i].y_mili); - modes->modes[n].orientation = cpu_to_le32(qxl_modes[i].orientation); - n++; - } - modes->n_modes = cpu_to_le32(n); - - ram_header_size = ALIGN(sizeof(QXLRam), 4096); - surface0_area_size = ALIGN(d->vgamem_size, 4096); - num_pages = d->vga.vram_size; - num_pages -= ram_header_size; - num_pages -= surface0_area_size; - num_pages = num_pages / QXL_PAGE_SIZE; - - assert(ram_header_size + surface0_area_size <= d->vga.vram_size); - - rom->draw_area_offset = cpu_to_le32(0); - rom->surface0_area_size = cpu_to_le32(surface0_area_size); - rom->pages_offset = cpu_to_le32(surface0_area_size); - rom->num_pages = cpu_to_le32(num_pages); - rom->ram_header_offset = cpu_to_le32(d->vga.vram_size - ram_header_size); - - d->shadow_rom = *rom; - d->rom = rom; - d->modes = modes; -} - -static void init_qxl_ram(PCIQXLDevice *d) -{ - uint8_t *buf; - uint64_t *item; - - buf = d->vga.vram_ptr; - d->ram = (QXLRam *)(buf + le32_to_cpu(d->shadow_rom.ram_header_offset)); - d->ram->magic = cpu_to_le32(QXL_RAM_MAGIC); - d->ram->int_pending = cpu_to_le32(0); - d->ram->int_mask = cpu_to_le32(0); - d->ram->update_surface = 0; - d->ram->monitors_config = 0; - SPICE_RING_INIT(&d->ram->cmd_ring); - SPICE_RING_INIT(&d->ram->cursor_ring); - SPICE_RING_INIT(&d->ram->release_ring); - SPICE_RING_PROD_ITEM(d, &d->ram->release_ring, item); - assert(item); - *item = 0; - qxl_ring_set_dirty(d); -} - -/* can be called from spice server thread context */ -static void qxl_set_dirty(MemoryRegion *mr, ram_addr_t addr, ram_addr_t end) -{ - memory_region_set_dirty(mr, addr, end - addr); -} - -static void qxl_rom_set_dirty(PCIQXLDevice *qxl) -{ - qxl_set_dirty(&qxl->rom_bar, 0, qxl->rom_size); -} - -/* called from spice server thread context only */ -static void qxl_ram_set_dirty(PCIQXLDevice *qxl, void *ptr) -{ - void *base = qxl->vga.vram_ptr; - intptr_t offset; - - offset = ptr - base; - assert(offset < qxl->vga.vram_size); - qxl_set_dirty(&qxl->vga.vram, offset, offset + 3); -} - -/* can be called from spice server thread context */ -static void qxl_ring_set_dirty(PCIQXLDevice *qxl) -{ - ram_addr_t addr = qxl->shadow_rom.ram_header_offset; - ram_addr_t end = qxl->vga.vram_size; - qxl_set_dirty(&qxl->vga.vram, addr, end); -} - -/* - * keep track of some command state, for savevm/loadvm. - * called from spice server thread context only - */ -static int qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext) -{ - switch (le32_to_cpu(ext->cmd.type)) { - case QXL_CMD_SURFACE: - { - QXLSurfaceCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); - - if (!cmd) { - return 1; - } - uint32_t id = le32_to_cpu(cmd->surface_id); - - if (id >= qxl->ssd.num_surfaces) { - qxl_set_guest_bug(qxl, "QXL_CMD_SURFACE id %d >= %d", id, - qxl->ssd.num_surfaces); - return 1; - } - if (cmd->type == QXL_SURFACE_CMD_CREATE && - (cmd->u.surface_create.stride & 0x03) != 0) { - qxl_set_guest_bug(qxl, "QXL_CMD_SURFACE stride = %d %% 4 != 0\n", - cmd->u.surface_create.stride); - return 1; - } - qemu_mutex_lock(&qxl->track_lock); - if (cmd->type == QXL_SURFACE_CMD_CREATE) { - qxl->guest_surfaces.cmds[id] = ext->cmd.data; - qxl->guest_surfaces.count++; - if (qxl->guest_surfaces.max < qxl->guest_surfaces.count) - qxl->guest_surfaces.max = qxl->guest_surfaces.count; - } - if (cmd->type == QXL_SURFACE_CMD_DESTROY) { - qxl->guest_surfaces.cmds[id] = 0; - qxl->guest_surfaces.count--; - } - qemu_mutex_unlock(&qxl->track_lock); - break; - } - case QXL_CMD_CURSOR: - { - QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); - - if (!cmd) { - return 1; - } - if (cmd->type == QXL_CURSOR_SET) { - qemu_mutex_lock(&qxl->track_lock); - qxl->guest_cursor = ext->cmd.data; - qemu_mutex_unlock(&qxl->track_lock); - } - break; - } - } - return 0; -} - -/* spice display interface callbacks */ - -static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - - trace_qxl_interface_attach_worker(qxl->id); - qxl->ssd.worker = qxl_worker; -} - -static void interface_set_compression_level(QXLInstance *sin, int level) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - - trace_qxl_interface_set_compression_level(qxl->id, level); - qxl->shadow_rom.compression_level = cpu_to_le32(level); - qxl->rom->compression_level = cpu_to_le32(level); - qxl_rom_set_dirty(qxl); -} - -static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - - if (!qemu_spice_display_is_running(&qxl->ssd)) { - return; - } - - trace_qxl_interface_set_mm_time(qxl->id, mm_time); - qxl->shadow_rom.mm_clock = cpu_to_le32(mm_time); - qxl->rom->mm_clock = cpu_to_le32(mm_time); - qxl_rom_set_dirty(qxl); -} - -static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - - trace_qxl_interface_get_init_info(qxl->id); - info->memslot_gen_bits = MEMSLOT_GENERATION_BITS; - info->memslot_id_bits = MEMSLOT_SLOT_BITS; - info->num_memslots = NUM_MEMSLOTS; - info->num_memslots_groups = NUM_MEMSLOTS_GROUPS; - info->internal_groupslot_id = 0; - info->qxl_ram_size = - le32_to_cpu(qxl->shadow_rom.num_pages) << QXL_PAGE_BITS; - info->n_surfaces = qxl->ssd.num_surfaces; -} - -static const char *qxl_mode_to_string(int mode) -{ - switch (mode) { - case QXL_MODE_COMPAT: - return "compat"; - case QXL_MODE_NATIVE: - return "native"; - case QXL_MODE_UNDEFINED: - return "undefined"; - case QXL_MODE_VGA: - return "vga"; - } - return "INVALID"; -} - -static const char *io_port_to_string(uint32_t io_port) -{ - if (io_port >= QXL_IO_RANGE_SIZE) { - return "out of range"; - } - static const char *io_port_to_string[QXL_IO_RANGE_SIZE + 1] = { - [QXL_IO_NOTIFY_CMD] = "QXL_IO_NOTIFY_CMD", - [QXL_IO_NOTIFY_CURSOR] = "QXL_IO_NOTIFY_CURSOR", - [QXL_IO_UPDATE_AREA] = "QXL_IO_UPDATE_AREA", - [QXL_IO_UPDATE_IRQ] = "QXL_IO_UPDATE_IRQ", - [QXL_IO_NOTIFY_OOM] = "QXL_IO_NOTIFY_OOM", - [QXL_IO_RESET] = "QXL_IO_RESET", - [QXL_IO_SET_MODE] = "QXL_IO_SET_MODE", - [QXL_IO_LOG] = "QXL_IO_LOG", - [QXL_IO_MEMSLOT_ADD] = "QXL_IO_MEMSLOT_ADD", - [QXL_IO_MEMSLOT_DEL] = "QXL_IO_MEMSLOT_DEL", - [QXL_IO_DETACH_PRIMARY] = "QXL_IO_DETACH_PRIMARY", - [QXL_IO_ATTACH_PRIMARY] = "QXL_IO_ATTACH_PRIMARY", - [QXL_IO_CREATE_PRIMARY] = "QXL_IO_CREATE_PRIMARY", - [QXL_IO_DESTROY_PRIMARY] = "QXL_IO_DESTROY_PRIMARY", - [QXL_IO_DESTROY_SURFACE_WAIT] = "QXL_IO_DESTROY_SURFACE_WAIT", - [QXL_IO_DESTROY_ALL_SURFACES] = "QXL_IO_DESTROY_ALL_SURFACES", - [QXL_IO_UPDATE_AREA_ASYNC] = "QXL_IO_UPDATE_AREA_ASYNC", - [QXL_IO_MEMSLOT_ADD_ASYNC] = "QXL_IO_MEMSLOT_ADD_ASYNC", - [QXL_IO_CREATE_PRIMARY_ASYNC] = "QXL_IO_CREATE_PRIMARY_ASYNC", - [QXL_IO_DESTROY_PRIMARY_ASYNC] = "QXL_IO_DESTROY_PRIMARY_ASYNC", - [QXL_IO_DESTROY_SURFACE_ASYNC] = "QXL_IO_DESTROY_SURFACE_ASYNC", - [QXL_IO_DESTROY_ALL_SURFACES_ASYNC] - = "QXL_IO_DESTROY_ALL_SURFACES_ASYNC", - [QXL_IO_FLUSH_SURFACES_ASYNC] = "QXL_IO_FLUSH_SURFACES_ASYNC", - [QXL_IO_FLUSH_RELEASE] = "QXL_IO_FLUSH_RELEASE", - [QXL_IO_MONITORS_CONFIG_ASYNC] = "QXL_IO_MONITORS_CONFIG_ASYNC", - }; - return io_port_to_string[io_port]; -} - -/* called from spice server thread context only */ -static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - SimpleSpiceUpdate *update; - QXLCommandRing *ring; - QXLCommand *cmd; - int notify, ret; - - trace_qxl_ring_command_check(qxl->id, qxl_mode_to_string(qxl->mode)); - - switch (qxl->mode) { - case QXL_MODE_VGA: - ret = false; - qemu_mutex_lock(&qxl->ssd.lock); - update = QTAILQ_FIRST(&qxl->ssd.updates); - if (update != NULL) { - QTAILQ_REMOVE(&qxl->ssd.updates, update, next); - *ext = update->ext; - ret = true; - } - qemu_mutex_unlock(&qxl->ssd.lock); - if (ret) { - trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode)); - qxl_log_command(qxl, "vga", ext); - } - return ret; - case QXL_MODE_COMPAT: - case QXL_MODE_NATIVE: - case QXL_MODE_UNDEFINED: - ring = &qxl->ram->cmd_ring; - if (qxl->guest_bug || SPICE_RING_IS_EMPTY(ring)) { - return false; - } - SPICE_RING_CONS_ITEM(qxl, ring, cmd); - if (!cmd) { - return false; - } - ext->cmd = *cmd; - ext->group_id = MEMSLOT_GROUP_GUEST; - ext->flags = qxl->cmdflags; - SPICE_RING_POP(ring, notify); - qxl_ring_set_dirty(qxl); - if (notify) { - qxl_send_events(qxl, QXL_INTERRUPT_DISPLAY); - } - qxl->guest_primary.commands++; - qxl_track_command(qxl, ext); - qxl_log_command(qxl, "cmd", ext); - trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode)); - return true; - default: - return false; - } -} - -/* called from spice server thread context only */ -static int interface_req_cmd_notification(QXLInstance *sin) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - int wait = 1; - - trace_qxl_ring_command_req_notification(qxl->id); - switch (qxl->mode) { - case QXL_MODE_COMPAT: - case QXL_MODE_NATIVE: - case QXL_MODE_UNDEFINED: - SPICE_RING_CONS_WAIT(&qxl->ram->cmd_ring, wait); - qxl_ring_set_dirty(qxl); - break; - default: - /* nothing */ - break; - } - return wait; -} - -/* called from spice server thread context only */ -static inline void qxl_push_free_res(PCIQXLDevice *d, int flush) -{ - QXLReleaseRing *ring = &d->ram->release_ring; - uint64_t *item; - int notify; - -#define QXL_FREE_BUNCH_SIZE 32 - - if (ring->prod - ring->cons + 1 == ring->num_items) { - /* ring full -- can't push */ - return; - } - if (!flush && d->oom_running) { - /* collect everything from oom handler before pushing */ - return; - } - if (!flush && d->num_free_res < QXL_FREE_BUNCH_SIZE) { - /* collect a bit more before pushing */ - return; - } - - SPICE_RING_PUSH(ring, notify); - trace_qxl_ring_res_push(d->id, qxl_mode_to_string(d->mode), - d->guest_surfaces.count, d->num_free_res, - d->last_release, notify ? "yes" : "no"); - trace_qxl_ring_res_push_rest(d->id, ring->prod - ring->cons, - ring->num_items, ring->prod, ring->cons); - if (notify) { - qxl_send_events(d, QXL_INTERRUPT_DISPLAY); - } - SPICE_RING_PROD_ITEM(d, ring, item); - if (!item) { - return; - } - *item = 0; - d->num_free_res = 0; - d->last_release = NULL; - qxl_ring_set_dirty(d); -} - -/* called from spice server thread context only */ -static void interface_release_resource(QXLInstance *sin, - QXLReleaseInfoExt ext) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - QXLReleaseRing *ring; - uint64_t *item, id; - - if (ext.group_id == MEMSLOT_GROUP_HOST) { - /* host group -> vga mode update request */ - QXLCommandExt *cmdext = (void *)(intptr_t)(ext.info->id); - SimpleSpiceUpdate *update; - g_assert(cmdext->cmd.type == QXL_CMD_DRAW); - update = container_of(cmdext, SimpleSpiceUpdate, ext); - qemu_spice_destroy_update(&qxl->ssd, update); - return; - } - - /* - * ext->info points into guest-visible memory - * pci bar 0, $command.release_info - */ - ring = &qxl->ram->release_ring; - SPICE_RING_PROD_ITEM(qxl, ring, item); - if (!item) { - return; - } - if (*item == 0) { - /* stick head into the ring */ - id = ext.info->id; - ext.info->next = 0; - qxl_ram_set_dirty(qxl, &ext.info->next); - *item = id; - qxl_ring_set_dirty(qxl); - } else { - /* append item to the list */ - qxl->last_release->next = ext.info->id; - qxl_ram_set_dirty(qxl, &qxl->last_release->next); - ext.info->next = 0; - qxl_ram_set_dirty(qxl, &ext.info->next); - } - qxl->last_release = ext.info; - qxl->num_free_res++; - trace_qxl_ring_res_put(qxl->id, qxl->num_free_res); - qxl_push_free_res(qxl, 0); -} - -/* called from spice server thread context only */ -static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - QXLCursorRing *ring; - QXLCommand *cmd; - int notify; - - trace_qxl_ring_cursor_check(qxl->id, qxl_mode_to_string(qxl->mode)); - - switch (qxl->mode) { - case QXL_MODE_COMPAT: - case QXL_MODE_NATIVE: - case QXL_MODE_UNDEFINED: - ring = &qxl->ram->cursor_ring; - if (SPICE_RING_IS_EMPTY(ring)) { - return false; - } - SPICE_RING_CONS_ITEM(qxl, ring, cmd); - if (!cmd) { - return false; - } - ext->cmd = *cmd; - ext->group_id = MEMSLOT_GROUP_GUEST; - ext->flags = qxl->cmdflags; - SPICE_RING_POP(ring, notify); - qxl_ring_set_dirty(qxl); - if (notify) { - qxl_send_events(qxl, QXL_INTERRUPT_CURSOR); - } - qxl->guest_primary.commands++; - qxl_track_command(qxl, ext); - qxl_log_command(qxl, "csr", ext); - if (qxl->id == 0) { - qxl_render_cursor(qxl, ext); - } - trace_qxl_ring_cursor_get(qxl->id, qxl_mode_to_string(qxl->mode)); - return true; - default: - return false; - } -} - -/* called from spice server thread context only */ -static int interface_req_cursor_notification(QXLInstance *sin) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - int wait = 1; - - trace_qxl_ring_cursor_req_notification(qxl->id); - switch (qxl->mode) { - case QXL_MODE_COMPAT: - case QXL_MODE_NATIVE: - case QXL_MODE_UNDEFINED: - SPICE_RING_CONS_WAIT(&qxl->ram->cursor_ring, wait); - qxl_ring_set_dirty(qxl); - break; - default: - /* nothing */ - break; - } - return wait; -} - -/* called from spice server thread context */ -static void interface_notify_update(QXLInstance *sin, uint32_t update_id) -{ - /* - * Called by spice-server as a result of a QXL_CMD_UPDATE which is not in - * use by xf86-video-qxl and is defined out in the qxl windows driver. - * Probably was at some earlier version that is prior to git start (2009), - * and is still guest trigerrable. - */ - fprintf(stderr, "%s: deprecated\n", __func__); -} - -/* called from spice server thread context only */ -static int interface_flush_resources(QXLInstance *sin) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - int ret; - - ret = qxl->num_free_res; - if (ret) { - qxl_push_free_res(qxl, 1); - } - return ret; -} - -static void qxl_create_guest_primary_complete(PCIQXLDevice *d); - -/* called from spice server thread context only */ -static void interface_async_complete_io(PCIQXLDevice *qxl, QXLCookie *cookie) -{ - uint32_t current_async; - - qemu_mutex_lock(&qxl->async_lock); - current_async = qxl->current_async; - qxl->current_async = QXL_UNDEFINED_IO; - qemu_mutex_unlock(&qxl->async_lock); - - trace_qxl_interface_async_complete_io(qxl->id, current_async, cookie); - if (!cookie) { - fprintf(stderr, "qxl: %s: error, cookie is NULL\n", __func__); - return; - } - if (cookie && current_async != cookie->io) { - fprintf(stderr, - "qxl: %s: error: current_async = %d != %" - PRId64 " = cookie->io\n", __func__, current_async, cookie->io); - } - switch (current_async) { - case QXL_IO_MEMSLOT_ADD_ASYNC: - case QXL_IO_DESTROY_PRIMARY_ASYNC: - case QXL_IO_UPDATE_AREA_ASYNC: - case QXL_IO_FLUSH_SURFACES_ASYNC: - case QXL_IO_MONITORS_CONFIG_ASYNC: - break; - case QXL_IO_CREATE_PRIMARY_ASYNC: - qxl_create_guest_primary_complete(qxl); - break; - case QXL_IO_DESTROY_ALL_SURFACES_ASYNC: - qxl_spice_destroy_surfaces_complete(qxl); - break; - case QXL_IO_DESTROY_SURFACE_ASYNC: - qxl_spice_destroy_surface_wait_complete(qxl, cookie->u.surface_id); - break; - default: - fprintf(stderr, "qxl: %s: unexpected current_async %d\n", __func__, - current_async); - } - qxl_send_events(qxl, QXL_INTERRUPT_IO_CMD); -} - -/* called from spice server thread context only */ -static void interface_update_area_complete(QXLInstance *sin, - uint32_t surface_id, - QXLRect *dirty, uint32_t num_updated_rects) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - int i; - int qxl_i; - - qemu_mutex_lock(&qxl->ssd.lock); - if (surface_id != 0 || !qxl->render_update_cookie_num) { - qemu_mutex_unlock(&qxl->ssd.lock); - return; - } - trace_qxl_interface_update_area_complete(qxl->id, surface_id, dirty->left, - dirty->right, dirty->top, dirty->bottom); - trace_qxl_interface_update_area_complete_rest(qxl->id, num_updated_rects); - if (qxl->num_dirty_rects + num_updated_rects > QXL_NUM_DIRTY_RECTS) { - /* - * overflow - treat this as a full update. Not expected to be common. - */ - trace_qxl_interface_update_area_complete_overflow(qxl->id, - QXL_NUM_DIRTY_RECTS); - qxl->guest_primary.resized = 1; - } - if (qxl->guest_primary.resized) { - /* - * Don't bother copying or scheduling the bh since we will flip - * the whole area anyway on completion of the update_area async call - */ - qemu_mutex_unlock(&qxl->ssd.lock); - return; - } - qxl_i = qxl->num_dirty_rects; - for (i = 0; i < num_updated_rects; i++) { - qxl->dirty[qxl_i++] = dirty[i]; - } - qxl->num_dirty_rects += num_updated_rects; - trace_qxl_interface_update_area_complete_schedule_bh(qxl->id, - qxl->num_dirty_rects); - qemu_bh_schedule(qxl->update_area_bh); - qemu_mutex_unlock(&qxl->ssd.lock); -} - -/* called from spice server thread context only */ -static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - QXLCookie *cookie = (QXLCookie *)(uintptr_t)cookie_token; - - switch (cookie->type) { - case QXL_COOKIE_TYPE_IO: - interface_async_complete_io(qxl, cookie); - g_free(cookie); - break; - case QXL_COOKIE_TYPE_RENDER_UPDATE_AREA: - qxl_render_update_area_done(qxl, cookie); - break; - case QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG: - break; - default: - fprintf(stderr, "qxl: %s: unexpected cookie type %d\n", - __func__, cookie->type); - g_free(cookie); - } -} - -/* called from spice server thread context only */ -static void interface_set_client_capabilities(QXLInstance *sin, - uint8_t client_present, - uint8_t caps[58]) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - - if (qxl->revision < 4) { - trace_qxl_set_client_capabilities_unsupported_by_revision(qxl->id, - qxl->revision); - return; - } - - if (runstate_check(RUN_STATE_INMIGRATE) || - runstate_check(RUN_STATE_POSTMIGRATE)) { - return; - } - - qxl->shadow_rom.client_present = client_present; - memcpy(qxl->shadow_rom.client_capabilities, caps, - sizeof(qxl->shadow_rom.client_capabilities)); - qxl->rom->client_present = client_present; - memcpy(qxl->rom->client_capabilities, caps, - sizeof(qxl->rom->client_capabilities)); - qxl_rom_set_dirty(qxl); - - qxl_send_events(qxl, QXL_INTERRUPT_CLIENT); -} - -static uint32_t qxl_crc32(const uint8_t *p, unsigned len) -{ - /* - * zlib xors the seed with 0xffffffff, and xors the result - * again with 0xffffffff; Both are not done with linux's crc32, - * which we want to be compatible with, so undo that. - */ - return crc32(0xffffffff, p, len) ^ 0xffffffff; -} - -/* called from main context only */ -static int interface_client_monitors_config(QXLInstance *sin, - VDAgentMonitorsConfig *monitors_config) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - QXLRom *rom = memory_region_get_ram_ptr(&qxl->rom_bar); - int i; - unsigned max_outputs = ARRAY_SIZE(rom->client_monitors_config.heads); - - if (qxl->revision < 4) { - trace_qxl_client_monitors_config_unsupported_by_device(qxl->id, - qxl->revision); - return 0; - } - /* - * Older windows drivers set int_mask to 0 when their ISR is called, - * then later set it to ~0. So it doesn't relate to the actual interrupts - * handled. However, they are old, so clearly they don't support this - * interrupt - */ - if (qxl->ram->int_mask == 0 || qxl->ram->int_mask == ~0 || - !(qxl->ram->int_mask & QXL_INTERRUPT_CLIENT_MONITORS_CONFIG)) { - trace_qxl_client_monitors_config_unsupported_by_guest(qxl->id, - qxl->ram->int_mask, - monitors_config); - return 0; - } - if (!monitors_config) { - return 1; - } - -#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */ - /* limit number of outputs based on setting limit */ - if (qxl->max_outputs && qxl->max_outputs <= max_outputs) { - max_outputs = qxl->max_outputs; - } -#endif - - memset(&rom->client_monitors_config, 0, - sizeof(rom->client_monitors_config)); - rom->client_monitors_config.count = monitors_config->num_of_monitors; - /* monitors_config->flags ignored */ - if (rom->client_monitors_config.count >= max_outputs) { - trace_qxl_client_monitors_config_capped(qxl->id, - monitors_config->num_of_monitors, - max_outputs); - rom->client_monitors_config.count = max_outputs; - } - for (i = 0 ; i < rom->client_monitors_config.count ; ++i) { - VDAgentMonConfig *monitor = &monitors_config->monitors[i]; - QXLURect *rect = &rom->client_monitors_config.heads[i]; - /* monitor->depth ignored */ - rect->left = monitor->x; - rect->top = monitor->y; - rect->right = monitor->x + monitor->width; - rect->bottom = monitor->y + monitor->height; - } - rom->client_monitors_config_crc = qxl_crc32( - (const uint8_t *)&rom->client_monitors_config, - sizeof(rom->client_monitors_config)); - trace_qxl_client_monitors_config_crc(qxl->id, - sizeof(rom->client_monitors_config), - rom->client_monitors_config_crc); - - trace_qxl_interrupt_client_monitors_config(qxl->id, - rom->client_monitors_config.count, - rom->client_monitors_config.heads); - qxl_send_events(qxl, QXL_INTERRUPT_CLIENT_MONITORS_CONFIG); - return 1; -} - -static const QXLInterface qxl_interface = { - .base.type = SPICE_INTERFACE_QXL, - .base.description = "qxl gpu", - .base.major_version = SPICE_INTERFACE_QXL_MAJOR, - .base.minor_version = SPICE_INTERFACE_QXL_MINOR, - - .attache_worker = interface_attach_worker, - .set_compression_level = interface_set_compression_level, - .set_mm_time = interface_set_mm_time, - .get_init_info = interface_get_init_info, - - /* the callbacks below are called from spice server thread context */ - .get_command = interface_get_command, - .req_cmd_notification = interface_req_cmd_notification, - .release_resource = interface_release_resource, - .get_cursor_command = interface_get_cursor_command, - .req_cursor_notification = interface_req_cursor_notification, - .notify_update = interface_notify_update, - .flush_resources = interface_flush_resources, - .async_complete = interface_async_complete, - .update_area_complete = interface_update_area_complete, - .set_client_capabilities = interface_set_client_capabilities, - .client_monitors_config = interface_client_monitors_config, -}; - -static const GraphicHwOps qxl_ops = { - .gfx_update = qxl_hw_update, -}; - -static void qxl_enter_vga_mode(PCIQXLDevice *d) -{ - if (d->mode == QXL_MODE_VGA) { - return; - } - trace_qxl_enter_vga_mode(d->id); -#if SPICE_SERVER_VERSION >= 0x000c03 /* release 0.12.3 */ - spice_qxl_driver_unload(&d->ssd.qxl); -#endif - graphic_console_set_hwops(d->ssd.dcl.con, d->vga.hw_ops, &d->vga); - update_displaychangelistener(&d->ssd.dcl, GUI_REFRESH_INTERVAL_DEFAULT); - qemu_spice_create_host_primary(&d->ssd); - d->mode = QXL_MODE_VGA; - vga_dirty_log_start(&d->vga); - graphic_hw_update(d->vga.con); -} - -static void qxl_exit_vga_mode(PCIQXLDevice *d) -{ - if (d->mode != QXL_MODE_VGA) { - return; - } - trace_qxl_exit_vga_mode(d->id); - graphic_console_set_hwops(d->ssd.dcl.con, &qxl_ops, d); - update_displaychangelistener(&d->ssd.dcl, GUI_REFRESH_INTERVAL_IDLE); - vga_dirty_log_stop(&d->vga); - qxl_destroy_primary(d, QXL_SYNC); -} - -static void qxl_update_irq(PCIQXLDevice *d) -{ - uint32_t pending = le32_to_cpu(d->ram->int_pending); - uint32_t mask = le32_to_cpu(d->ram->int_mask); - int level = !!(pending & mask); - pci_set_irq(&d->pci, level); - qxl_ring_set_dirty(d); -} - -static void qxl_check_state(PCIQXLDevice *d) -{ - QXLRam *ram = d->ram; - int spice_display_running = qemu_spice_display_is_running(&d->ssd); - - assert(!spice_display_running || SPICE_RING_IS_EMPTY(&ram->cmd_ring)); - assert(!spice_display_running || SPICE_RING_IS_EMPTY(&ram->cursor_ring)); -} - -static void qxl_reset_state(PCIQXLDevice *d) -{ - QXLRom *rom = d->rom; - - qxl_check_state(d); - d->shadow_rom.update_id = cpu_to_le32(0); - *rom = d->shadow_rom; - qxl_rom_set_dirty(d); - init_qxl_ram(d); - d->num_free_res = 0; - d->last_release = NULL; - memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty)); - qxl_update_irq(d); -} - -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); - } else { - d->mode = QXL_MODE_UNDEFINED; - } -} - -static void qxl_hard_reset(PCIQXLDevice *d, int loadvm) -{ - bool startstop = qemu_spice_display_is_running(&d->ssd); - - trace_qxl_hard_reset(d->id, loadvm); - - if (startstop) { - qemu_spice_display_stop(); - } - - qxl_spice_reset_cursor(d); - qxl_spice_reset_image_cache(d); - qxl_reset_surfaces(d); - qxl_reset_memslots(d); - - /* pre loadvm reset must not touch QXLRam. This lives in - * device memory, is migrated together with RAM and thus - * already loaded at this point */ - if (!loadvm) { - qxl_reset_state(d); - } - qemu_spice_create_host_memslot(&d->ssd); - qxl_soft_reset(d); - - if (startstop) { - qemu_spice_display_start(); - } -} - -static void qxl_reset_handler(DeviceState *dev) -{ - PCIQXLDevice *d = PCI_QXL(PCI_DEVICE(dev)); - - qxl_hard_reset(d, 0); -} - -static void qxl_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) -{ - VGACommonState *vga = opaque; - PCIQXLDevice *qxl = container_of(vga, PCIQXLDevice, vga); - - trace_qxl_io_write_vga(qxl->id, qxl_mode_to_string(qxl->mode), addr, val); - if (qxl->mode != QXL_MODE_VGA) { - qxl_destroy_primary(qxl, QXL_SYNC); - qxl_soft_reset(qxl); - } - vga_ioport_write(opaque, addr, val); -} - -static const MemoryRegionPortio qxl_vga_portio_list[] = { - { 0x04, 2, 1, .read = vga_ioport_read, - .write = qxl_vga_ioport_write }, /* 3b4 */ - { 0x0a, 1, 1, .read = vga_ioport_read, - .write = qxl_vga_ioport_write }, /* 3ba */ - { 0x10, 16, 1, .read = vga_ioport_read, - .write = qxl_vga_ioport_write }, /* 3c0 */ - { 0x24, 2, 1, .read = vga_ioport_read, - .write = qxl_vga_ioport_write }, /* 3d4 */ - { 0x2a, 1, 1, .read = vga_ioport_read, - .write = qxl_vga_ioport_write }, /* 3da */ - PORTIO_END_OF_LIST(), -}; - -static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, - qxl_async_io async) -{ - static const int regions[] = { - QXL_RAM_RANGE_INDEX, - QXL_VRAM_RANGE_INDEX, - QXL_VRAM64_RANGE_INDEX, - }; - uint64_t guest_start; - uint64_t guest_end; - int pci_region; - pcibus_t pci_start; - pcibus_t pci_end; - intptr_t virt_start; - QXLDevMemSlot memslot; - int i; - - guest_start = le64_to_cpu(d->guest_slots[slot_id].slot.mem_start); - guest_end = le64_to_cpu(d->guest_slots[slot_id].slot.mem_end); - - trace_qxl_memslot_add_guest(d->id, slot_id, guest_start, guest_end); - - if (slot_id >= NUM_MEMSLOTS) { - qxl_set_guest_bug(d, "%s: slot_id >= NUM_MEMSLOTS %d >= %d", __func__, - slot_id, NUM_MEMSLOTS); - return 1; - } - if (guest_start > guest_end) { - qxl_set_guest_bug(d, "%s: guest_start > guest_end 0x%" PRIx64 - " > 0x%" PRIx64, __func__, guest_start, guest_end); - return 1; - } - - for (i = 0; i < ARRAY_SIZE(regions); i++) { - pci_region = regions[i]; - pci_start = d->pci.io_regions[pci_region].addr; - pci_end = pci_start + d->pci.io_regions[pci_region].size; - /* mapped? */ - if (pci_start == -1) { - continue; - } - /* start address in range ? */ - if (guest_start < pci_start || guest_start > pci_end) { - continue; - } - /* end address in range ? */ - if (guest_end > pci_end) { - continue; - } - /* passed */ - break; - } - if (i == ARRAY_SIZE(regions)) { - qxl_set_guest_bug(d, "%s: finished loop without match", __func__); - return 1; - } - - switch (pci_region) { - case QXL_RAM_RANGE_INDEX: - virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vga.vram); - break; - case QXL_VRAM_RANGE_INDEX: - case 4 /* vram 64bit */: - virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vram_bar); - break; - default: - /* should not happen */ - qxl_set_guest_bug(d, "%s: pci_region = %d", __func__, pci_region); - return 1; - } - - memslot.slot_id = slot_id; - memslot.slot_group_id = MEMSLOT_GROUP_GUEST; /* guest group */ - memslot.virt_start = virt_start + (guest_start - pci_start); - memslot.virt_end = virt_start + (guest_end - pci_start); - memslot.addr_delta = memslot.virt_start - delta; - memslot.generation = d->rom->slot_generation = 0; - qxl_rom_set_dirty(d); - - qemu_spice_add_memslot(&d->ssd, &memslot, async); - d->guest_slots[slot_id].ptr = (void*)memslot.virt_start; - d->guest_slots[slot_id].size = memslot.virt_end - memslot.virt_start; - d->guest_slots[slot_id].delta = delta; - d->guest_slots[slot_id].active = 1; - return 0; -} - -static void qxl_del_memslot(PCIQXLDevice *d, uint32_t slot_id) -{ - qemu_spice_del_memslot(&d->ssd, MEMSLOT_GROUP_HOST, slot_id); - d->guest_slots[slot_id].active = 0; -} - -static void qxl_reset_memslots(PCIQXLDevice *d) -{ - qxl_spice_reset_memslots(d); - memset(&d->guest_slots, 0, sizeof(d->guest_slots)); -} - -static void qxl_reset_surfaces(PCIQXLDevice *d) -{ - trace_qxl_reset_surfaces(d->id); - d->mode = QXL_MODE_UNDEFINED; - qxl_spice_destroy_surfaces(d, QXL_SYNC); -} - -/* can be also called from spice server thread context */ -void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id) -{ - uint64_t phys = le64_to_cpu(pqxl); - uint32_t slot = (phys >> (64 - 8)) & 0xff; - uint64_t offset = phys & 0xffffffffffff; - - switch (group_id) { - case MEMSLOT_GROUP_HOST: - return (void *)(intptr_t)offset; - case MEMSLOT_GROUP_GUEST: - if (slot >= NUM_MEMSLOTS) { - qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot, - NUM_MEMSLOTS); - return NULL; - } - if (!qxl->guest_slots[slot].active) { - qxl_set_guest_bug(qxl, "inactive slot %d\n", slot); - return NULL; - } - if (offset < qxl->guest_slots[slot].delta) { - qxl_set_guest_bug(qxl, - "slot %d offset %"PRIu64" < delta %"PRIu64"\n", - slot, offset, qxl->guest_slots[slot].delta); - return NULL; - } - offset -= qxl->guest_slots[slot].delta; - if (offset > qxl->guest_slots[slot].size) { - qxl_set_guest_bug(qxl, - "slot %d offset %"PRIu64" > size %"PRIu64"\n", - slot, offset, qxl->guest_slots[slot].size); - return NULL; - } - return qxl->guest_slots[slot].ptr + offset; - } - return NULL; -} - -static void qxl_create_guest_primary_complete(PCIQXLDevice *qxl) -{ - /* for local rendering */ - qxl_render_resize(qxl); -} - -static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm, - qxl_async_io async) -{ - QXLDevSurfaceCreate surface; - QXLSurfaceCreate *sc = &qxl->guest_primary.surface; - uint32_t requested_height = le32_to_cpu(sc->height); - int requested_stride = le32_to_cpu(sc->stride); - - if (requested_stride == INT32_MIN || - abs(requested_stride) * (uint64_t)requested_height - > qxl->vgamem_size) { - qxl_set_guest_bug(qxl, "%s: requested primary larger than framebuffer" - " stride %d x height %" PRIu32 " > %" PRIu32, - __func__, requested_stride, requested_height, - qxl->vgamem_size); - return; - } - - if (qxl->mode == QXL_MODE_NATIVE) { - qxl_set_guest_bug(qxl, "%s: nop since already in QXL_MODE_NATIVE", - __func__); - } - qxl_exit_vga_mode(qxl); - - surface.format = le32_to_cpu(sc->format); - surface.height = le32_to_cpu(sc->height); - surface.mem = le64_to_cpu(sc->mem); - surface.position = le32_to_cpu(sc->position); - surface.stride = le32_to_cpu(sc->stride); - surface.width = le32_to_cpu(sc->width); - surface.type = le32_to_cpu(sc->type); - surface.flags = le32_to_cpu(sc->flags); - trace_qxl_create_guest_primary(qxl->id, sc->width, sc->height, sc->mem, - sc->format, sc->position); - trace_qxl_create_guest_primary_rest(qxl->id, sc->stride, sc->type, - sc->flags); - - if ((surface.stride & 0x3) != 0) { - qxl_set_guest_bug(qxl, "primary surface stride = %d %% 4 != 0", - surface.stride); - return; - } - - surface.mouse_mode = true; - surface.group_id = MEMSLOT_GROUP_GUEST; - if (loadvm) { - surface.flags |= QXL_SURF_FLAG_KEEP_DATA; - } - - qxl->mode = QXL_MODE_NATIVE; - qxl->cmdflags = 0; - qemu_spice_create_primary_surface(&qxl->ssd, 0, &surface, async); - - if (async == QXL_SYNC) { - qxl_create_guest_primary_complete(qxl); - } -} - -/* return 1 if surface destoy was initiated (in QXL_ASYNC case) or - * done (in QXL_SYNC case), 0 otherwise. */ -static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async) -{ - if (d->mode == QXL_MODE_UNDEFINED) { - return 0; - } - trace_qxl_destroy_primary(d->id); - d->mode = QXL_MODE_UNDEFINED; - qemu_spice_destroy_primary_surface(&d->ssd, 0, async); - qxl_spice_reset_cursor(d); - return 1; -} - -static void qxl_set_mode(PCIQXLDevice *d, unsigned int modenr, int loadvm) -{ - pcibus_t start = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr; - pcibus_t end = d->pci.io_regions[QXL_RAM_RANGE_INDEX].size + start; - QXLMode *mode = d->modes->modes + modenr; - uint64_t devmem = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr; - QXLMemSlot slot = { - .mem_start = start, - .mem_end = end - }; - - if (modenr >= d->modes->n_modes) { - qxl_set_guest_bug(d, "mode number out of range"); - return; - } - - QXLSurfaceCreate surface = { - .width = mode->x_res, - .height = mode->y_res, - .stride = -mode->x_res * 4, - .format = SPICE_SURFACE_FMT_32_xRGB, - .flags = loadvm ? QXL_SURF_FLAG_KEEP_DATA : 0, - .mouse_mode = true, - .mem = devmem + d->shadow_rom.draw_area_offset, - }; - - trace_qxl_set_mode(d->id, modenr, mode->x_res, mode->y_res, mode->bits, - devmem); - if (!loadvm) { - qxl_hard_reset(d, 0); - } - - d->guest_slots[0].slot = slot; - assert(qxl_add_memslot(d, 0, devmem, QXL_SYNC) == 0); - - d->guest_primary.surface = surface; - qxl_create_guest_primary(d, 0, QXL_SYNC); - - d->mode = QXL_MODE_COMPAT; - d->cmdflags = QXL_COMMAND_FLAG_COMPAT; - if (mode->bits == 16) { - d->cmdflags |= QXL_COMMAND_FLAG_COMPAT_16BPP; - } - d->shadow_rom.mode = cpu_to_le32(modenr); - d->rom->mode = cpu_to_le32(modenr); - qxl_rom_set_dirty(d); -} - -static void ioport_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - PCIQXLDevice *d = opaque; - uint32_t io_port = addr; - qxl_async_io async = QXL_SYNC; - uint32_t orig_io_port = io_port; - - if (d->guest_bug && io_port != QXL_IO_RESET) { - return; - } - - if (d->revision <= QXL_REVISION_STABLE_V10 && - io_port > QXL_IO_FLUSH_RELEASE) { - qxl_set_guest_bug(d, "unsupported io %d for revision %d\n", - io_port, d->revision); - return; - } - - switch (io_port) { - case QXL_IO_RESET: - case QXL_IO_SET_MODE: - case QXL_IO_MEMSLOT_ADD: - case QXL_IO_MEMSLOT_DEL: - case QXL_IO_CREATE_PRIMARY: - case QXL_IO_UPDATE_IRQ: - case QXL_IO_LOG: - case QXL_IO_MEMSLOT_ADD_ASYNC: - case QXL_IO_CREATE_PRIMARY_ASYNC: - break; - default: - if (d->mode != QXL_MODE_VGA) { - break; - } - trace_qxl_io_unexpected_vga_mode(d->id, - addr, val, io_port_to_string(io_port)); - /* be nice to buggy guest drivers */ - if (io_port >= QXL_IO_UPDATE_AREA_ASYNC && - io_port < QXL_IO_RANGE_SIZE) { - qxl_send_events(d, QXL_INTERRUPT_IO_CMD); - } - return; - } - - /* we change the io_port to avoid ifdeffery in the main switch */ - orig_io_port = io_port; - switch (io_port) { - case QXL_IO_UPDATE_AREA_ASYNC: - io_port = QXL_IO_UPDATE_AREA; - goto async_common; - case QXL_IO_MEMSLOT_ADD_ASYNC: - io_port = QXL_IO_MEMSLOT_ADD; - goto async_common; - case QXL_IO_CREATE_PRIMARY_ASYNC: - io_port = QXL_IO_CREATE_PRIMARY; - goto async_common; - case QXL_IO_DESTROY_PRIMARY_ASYNC: - io_port = QXL_IO_DESTROY_PRIMARY; - goto async_common; - case QXL_IO_DESTROY_SURFACE_ASYNC: - io_port = QXL_IO_DESTROY_SURFACE_WAIT; - goto async_common; - case QXL_IO_DESTROY_ALL_SURFACES_ASYNC: - io_port = QXL_IO_DESTROY_ALL_SURFACES; - goto async_common; - case QXL_IO_FLUSH_SURFACES_ASYNC: - case QXL_IO_MONITORS_CONFIG_ASYNC: -async_common: - async = QXL_ASYNC; - qemu_mutex_lock(&d->async_lock); - if (d->current_async != QXL_UNDEFINED_IO) { - qxl_set_guest_bug(d, "%d async started before last (%d) complete", - io_port, d->current_async); - qemu_mutex_unlock(&d->async_lock); - return; - } - d->current_async = orig_io_port; - qemu_mutex_unlock(&d->async_lock); - break; - default: - break; - } - trace_qxl_io_write(d->id, qxl_mode_to_string(d->mode), - addr, io_port_to_string(addr), - val, size, async); - - switch (io_port) { - case QXL_IO_UPDATE_AREA: - { - QXLCookie *cookie = NULL; - QXLRect update = d->ram->update_area; - - if (d->ram->update_surface > d->ssd.num_surfaces) { - qxl_set_guest_bug(d, "QXL_IO_UPDATE_AREA: invalid surface id %d\n", - d->ram->update_surface); - break; - } - if (update.left >= update.right || update.top >= update.bottom || - update.left < 0 || update.top < 0) { - qxl_set_guest_bug(d, - "QXL_IO_UPDATE_AREA: invalid area (%ux%u)x(%ux%u)\n", - update.left, update.top, update.right, update.bottom); - if (update.left == update.right || update.top == update.bottom) { - /* old drivers may provide empty area, keep going */ - qxl_clear_guest_bug(d); - goto cancel_async; - } - break; - } - if (async == QXL_ASYNC) { - cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO, - QXL_IO_UPDATE_AREA_ASYNC); - cookie->u.area = update; - } - qxl_spice_update_area(d, d->ram->update_surface, - cookie ? &cookie->u.area : &update, - NULL, 0, 0, async, cookie); - break; - } - case QXL_IO_NOTIFY_CMD: - qemu_spice_wakeup(&d->ssd); - break; - case QXL_IO_NOTIFY_CURSOR: - qemu_spice_wakeup(&d->ssd); - break; - case QXL_IO_UPDATE_IRQ: - qxl_update_irq(d); - break; - case QXL_IO_NOTIFY_OOM: - if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) { - break; - } - d->oom_running = 1; - qxl_spice_oom(d); - d->oom_running = 0; - break; - case QXL_IO_SET_MODE: - qxl_set_mode(d, val, 0); - break; - case QXL_IO_LOG: - trace_qxl_io_log(d->id, d->ram->log_buf); - if (d->guestdebug) { - fprintf(stderr, "qxl/guest-%d: %" PRId64 ": %s", d->id, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), d->ram->log_buf); - } - break; - case QXL_IO_RESET: - qxl_hard_reset(d, 0); - break; - case QXL_IO_MEMSLOT_ADD: - if (val >= NUM_MEMSLOTS) { - qxl_set_guest_bug(d, "QXL_IO_MEMSLOT_ADD: val out of range"); - break; - } - if (d->guest_slots[val].active) { - qxl_set_guest_bug(d, - "QXL_IO_MEMSLOT_ADD: memory slot already active"); - break; - } - d->guest_slots[val].slot = d->ram->mem_slot; - qxl_add_memslot(d, val, 0, async); - break; - case QXL_IO_MEMSLOT_DEL: - if (val >= NUM_MEMSLOTS) { - qxl_set_guest_bug(d, "QXL_IO_MEMSLOT_DEL: val out of range"); - break; - } - qxl_del_memslot(d, val); - break; - case QXL_IO_CREATE_PRIMARY: - if (val != 0) { - qxl_set_guest_bug(d, "QXL_IO_CREATE_PRIMARY (async=%d): val != 0", - async); - goto cancel_async; - } - d->guest_primary.surface = d->ram->create_surface; - qxl_create_guest_primary(d, 0, async); - break; - case QXL_IO_DESTROY_PRIMARY: - if (val != 0) { - qxl_set_guest_bug(d, "QXL_IO_DESTROY_PRIMARY (async=%d): val != 0", - async); - goto cancel_async; - } - if (!qxl_destroy_primary(d, async)) { - trace_qxl_io_destroy_primary_ignored(d->id, - qxl_mode_to_string(d->mode)); - goto cancel_async; - } - break; - case QXL_IO_DESTROY_SURFACE_WAIT: - if (val >= d->ssd.num_surfaces) { - qxl_set_guest_bug(d, "QXL_IO_DESTROY_SURFACE (async=%d):" - "%" PRIu64 " >= NUM_SURFACES", async, val); - goto cancel_async; - } - qxl_spice_destroy_surface_wait(d, val, async); - break; - case QXL_IO_FLUSH_RELEASE: { - QXLReleaseRing *ring = &d->ram->release_ring; - if (ring->prod - ring->cons + 1 == ring->num_items) { - fprintf(stderr, - "ERROR: no flush, full release ring [p%d,%dc]\n", - ring->prod, ring->cons); - } - qxl_push_free_res(d, 1 /* flush */); - break; - } - case QXL_IO_FLUSH_SURFACES_ASYNC: - qxl_spice_flush_surfaces_async(d); - break; - case QXL_IO_DESTROY_ALL_SURFACES: - d->mode = QXL_MODE_UNDEFINED; - qxl_spice_destroy_surfaces(d, async); - break; - case QXL_IO_MONITORS_CONFIG_ASYNC: - qxl_spice_monitors_config_async(d, 0); - break; - default: - qxl_set_guest_bug(d, "%s: unexpected ioport=0x%x\n", __func__, io_port); - } - return; -cancel_async: - if (async) { - qxl_send_events(d, QXL_INTERRUPT_IO_CMD); - qemu_mutex_lock(&d->async_lock); - d->current_async = QXL_UNDEFINED_IO; - qemu_mutex_unlock(&d->async_lock); - } -} - -static uint64_t ioport_read(void *opaque, hwaddr addr, - unsigned size) -{ - PCIQXLDevice *qxl = opaque; - - trace_qxl_io_read_unexpected(qxl->id); - return 0xff; -} - -static const MemoryRegionOps qxl_io_ops = { - .read = ioport_read, - .write = ioport_write, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void qxl_update_irq_bh(void *opaque) -{ - PCIQXLDevice *d = opaque; - qxl_update_irq(d); -} - -static void qxl_send_events(PCIQXLDevice *d, uint32_t events) -{ - uint32_t old_pending; - uint32_t le_events = cpu_to_le32(events); - - trace_qxl_send_events(d->id, events); - if (!qemu_spice_display_is_running(&d->ssd)) { - /* spice-server tracks guest running state and should not do this */ - fprintf(stderr, "%s: spice-server bug: guest stopped, ignoring\n", - __func__); - trace_qxl_send_events_vm_stopped(d->id, events); - return; - } - old_pending = atomic_fetch_or(&d->ram->int_pending, le_events); - if ((old_pending & le_events) == le_events) { - return; - } - qemu_bh_schedule(d->update_irq); -} - -/* graphics console */ - -static void qxl_hw_update(void *opaque) -{ - PCIQXLDevice *qxl = opaque; - - qxl_render_update(qxl); -} - -static void qxl_dirty_surfaces(PCIQXLDevice *qxl) -{ - uintptr_t vram_start; - int i; - - if (qxl->mode != QXL_MODE_NATIVE && qxl->mode != QXL_MODE_COMPAT) { - return; - } - - /* dirty the primary surface */ - qxl_set_dirty(&qxl->vga.vram, qxl->shadow_rom.draw_area_offset, - qxl->shadow_rom.surface0_area_size); - - vram_start = (uintptr_t)memory_region_get_ram_ptr(&qxl->vram_bar); - - /* dirty the off-screen surfaces */ - for (i = 0; i < qxl->ssd.num_surfaces; i++) { - QXLSurfaceCmd *cmd; - intptr_t surface_offset; - int surface_size; - - if (qxl->guest_surfaces.cmds[i] == 0) { - continue; - } - - cmd = qxl_phys2virt(qxl, qxl->guest_surfaces.cmds[i], - MEMSLOT_GROUP_GUEST); - assert(cmd); - assert(cmd->type == QXL_SURFACE_CMD_CREATE); - surface_offset = (intptr_t)qxl_phys2virt(qxl, - cmd->u.surface_create.data, - MEMSLOT_GROUP_GUEST); - assert(surface_offset); - surface_offset -= vram_start; - surface_size = cmd->u.surface_create.height * - abs(cmd->u.surface_create.stride); - trace_qxl_surfaces_dirty(qxl->id, i, (int)surface_offset, surface_size); - qxl_set_dirty(&qxl->vram_bar, surface_offset, surface_size); - } -} - -static void qxl_vm_change_state_handler(void *opaque, int running, - RunState state) -{ - PCIQXLDevice *qxl = opaque; - - if (running) { - /* - * if qxl_send_events was called from spice server context before - * migration ended, qxl_update_irq for these events might not have been - * called - */ - qxl_update_irq(qxl); - } else { - /* make sure surfaces are saved before migration */ - qxl_dirty_surfaces(qxl); - } -} - -/* display change listener */ - -static void display_update(DisplayChangeListener *dcl, - int x, int y, int w, int h) -{ - PCIQXLDevice *qxl = container_of(dcl, PCIQXLDevice, ssd.dcl); - - if (qxl->mode == QXL_MODE_VGA) { - qemu_spice_display_update(&qxl->ssd, x, y, w, h); - } -} - -static void display_switch(DisplayChangeListener *dcl, - struct DisplaySurface *surface) -{ - PCIQXLDevice *qxl = container_of(dcl, PCIQXLDevice, ssd.dcl); - - qxl->ssd.ds = surface; - if (qxl->mode == QXL_MODE_VGA) { - qemu_spice_display_switch(&qxl->ssd, surface); - } -} - -static void display_refresh(DisplayChangeListener *dcl) -{ - PCIQXLDevice *qxl = container_of(dcl, PCIQXLDevice, ssd.dcl); - - if (qxl->mode == QXL_MODE_VGA) { - qemu_spice_display_refresh(&qxl->ssd); - } -} - -static DisplayChangeListenerOps display_listener_ops = { - .dpy_name = "spice/qxl", - .dpy_gfx_update = display_update, - .dpy_gfx_switch = display_switch, - .dpy_refresh = display_refresh, -}; - -static void qxl_init_ramsize(PCIQXLDevice *qxl) -{ - /* vga mode framebuffer / primary surface (bar 0, first part) */ - if (qxl->vgamem_size_mb < 8) { - qxl->vgamem_size_mb = 8; - } - /* XXX: we round vgamem_size_mb up to a nearest power of two and it must be - * less than vga_common_init()'s maximum on qxl->vga.vram_size (512 now). - */ - if (qxl->vgamem_size_mb > 256) { - qxl->vgamem_size_mb = 256; - } - qxl->vgamem_size = qxl->vgamem_size_mb * 1024 * 1024; - - /* vga ram (bar 0, total) */ - if (qxl->ram_size_mb != -1) { - qxl->vga.vram_size = qxl->ram_size_mb * 1024 * 1024; - } - if (qxl->vga.vram_size < qxl->vgamem_size * 2) { - qxl->vga.vram_size = qxl->vgamem_size * 2; - } - - /* vram32 (surfaces, 32bit, bar 1) */ - if (qxl->vram32_size_mb != -1) { - qxl->vram32_size = qxl->vram32_size_mb * 1024 * 1024; - } - if (qxl->vram32_size < 4096) { - qxl->vram32_size = 4096; - } - - /* vram (surfaces, 64bit, bar 4+5) */ - if (qxl->vram_size_mb != -1) { - qxl->vram_size = qxl->vram_size_mb * 1024 * 1024; - } - if (qxl->vram_size < qxl->vram32_size) { - qxl->vram_size = qxl->vram32_size; - } - - if (qxl->revision == 1) { - qxl->vram32_size = 4096; - qxl->vram_size = 4096; - } - qxl->vgamem_size = pow2ceil(qxl->vgamem_size); - qxl->vga.vram_size = pow2ceil(qxl->vga.vram_size); - qxl->vram32_size = pow2ceil(qxl->vram32_size); - qxl->vram_size = pow2ceil(qxl->vram_size); -} - -static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) -{ - uint8_t* config = qxl->pci.config; - uint32_t pci_device_rev; - uint32_t io_size; - - qxl->mode = QXL_MODE_UNDEFINED; - qxl->generation = 1; - qxl->num_memslots = NUM_MEMSLOTS; - qemu_mutex_init(&qxl->track_lock); - qemu_mutex_init(&qxl->async_lock); - qxl->current_async = QXL_UNDEFINED_IO; - qxl->guest_bug = 0; - - switch (qxl->revision) { - case 1: /* spice 0.4 -- qxl-1 */ - pci_device_rev = QXL_REVISION_STABLE_V04; - io_size = 8; - break; - case 2: /* spice 0.6 -- qxl-2 */ - pci_device_rev = QXL_REVISION_STABLE_V06; - io_size = 16; - break; - case 3: /* qxl-3 */ - pci_device_rev = QXL_REVISION_STABLE_V10; - io_size = 32; /* PCI region size must be pow2 */ - break; - case 4: /* qxl-4 */ - pci_device_rev = QXL_REVISION_STABLE_V12; - io_size = pow2ceil(QXL_IO_RANGE_SIZE); - break; - default: - error_setg(errp, "Invalid revision %d for qxl device (max %d)", - qxl->revision, QXL_DEFAULT_REVISION); - return; - } - - pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev); - pci_set_byte(&config[PCI_INTERRUPT_PIN], 1); - - qxl->rom_size = qxl_rom_size(); - memory_region_init_ram(&qxl->rom_bar, OBJECT(qxl), "qxl.vrom", - 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_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); - - memory_region_init_io(&qxl->io_bar, OBJECT(qxl), &qxl_io_ops, qxl, - "qxl-ioports", io_size); - if (qxl->id == 0) { - vga_dirty_log_start(&qxl->vga); - } - memory_region_set_flush_coalesced(&qxl->io_bar); - - - pci_register_bar(&qxl->pci, QXL_IO_RANGE_INDEX, - PCI_BASE_ADDRESS_SPACE_IO, &qxl->io_bar); - - pci_register_bar(&qxl->pci, QXL_ROM_RANGE_INDEX, - PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->rom_bar); - - pci_register_bar(&qxl->pci, QXL_RAM_RANGE_INDEX, - PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vga.vram); - - pci_register_bar(&qxl->pci, QXL_VRAM_RANGE_INDEX, - PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vram32_bar); - - if (qxl->vram32_size < qxl->vram_size) { - /* - * Make the 64bit vram bar show up only in case it is - * configured to be larger than the 32bit vram bar. - */ - pci_register_bar(&qxl->pci, QXL_VRAM64_RANGE_INDEX, - PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_TYPE_64 | - PCI_BASE_ADDRESS_MEM_PREFETCH, - &qxl->vram_bar); - } - - /* print pci bar details */ - dprint(qxl, 1, "ram/%s: %d MB [region 0]\n", - qxl->id == 0 ? "pri" : "sec", - qxl->vga.vram_size / (1024*1024)); - dprint(qxl, 1, "vram/32: %d MB [region 1]\n", - qxl->vram32_size / (1024*1024)); - dprint(qxl, 1, "vram/64: %d MB %s\n", - qxl->vram_size / (1024*1024), - qxl->vram32_size < qxl->vram_size ? "[region 4]" : "[unmapped]"); - - qxl->ssd.qxl.base.sif = &qxl_interface.base; - if (qemu_spice_add_display_interface(&qxl->ssd.qxl, qxl->vga.con) != 0) { - error_setg(errp, "qxl interface %d.%d not supported by spice-server", - SPICE_INTERFACE_QXL_MAJOR, SPICE_INTERFACE_QXL_MINOR); - return; - } - qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl); - - qxl->update_irq = qemu_bh_new(qxl_update_irq_bh, qxl); - qxl_reset_state(qxl); - - qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl); - qxl->ssd.cursor_bh = qemu_bh_new(qemu_spice_cursor_refresh_bh, &qxl->ssd); -} - -static void qxl_realize_primary(PCIDevice *dev, Error **errp) -{ - PCIQXLDevice *qxl = PCI_QXL(dev); - VGACommonState *vga = &qxl->vga; - Error *local_err = NULL; - - qxl->id = 0; - qxl_init_ramsize(qxl); - vga->vbe_size = qxl->vgamem_size; - vga->vram_size_mb = qxl->vga.vram_size >> 20; - vga_common_init(vga, OBJECT(dev), true); - vga_init(vga, OBJECT(dev), - pci_address_space(dev), pci_address_space_io(dev), false); - portio_list_init(&qxl->vga_port_list, OBJECT(dev), qxl_vga_portio_list, - vga, "vga"); - portio_list_set_flush_coalesced(&qxl->vga_port_list); - portio_list_add(&qxl->vga_port_list, pci_address_space_io(dev), 0x3b0); - - vga->con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl); - qemu_spice_display_init_common(&qxl->ssd); - - qxl_realize_common(qxl, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - qxl->ssd.dcl.ops = &display_listener_ops; - qxl->ssd.dcl.con = vga->con; - register_displaychangelistener(&qxl->ssd.dcl); -} - -static void qxl_realize_secondary(PCIDevice *dev, Error **errp) -{ - static int device_id = 1; - PCIQXLDevice *qxl = PCI_QXL(dev); - - qxl->id = device_id++; - qxl_init_ramsize(qxl); - memory_region_init_ram(&qxl->vga.vram, OBJECT(dev), "qxl.vgavram", - 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); - - qxl_realize_common(qxl, errp); -} - -static void qxl_pre_save(void *opaque) -{ - PCIQXLDevice* d = opaque; - uint8_t *ram_start = d->vga.vram_ptr; - - trace_qxl_pre_save(d->id); - if (d->last_release == NULL) { - d->last_release_offset = 0; - } else { - d->last_release_offset = (uint8_t *)d->last_release - ram_start; - } - assert(d->last_release_offset < d->vga.vram_size); -} - -static int qxl_pre_load(void *opaque) -{ - PCIQXLDevice* d = opaque; - - trace_qxl_pre_load(d->id); - qxl_hard_reset(d, 1); - qxl_exit_vga_mode(d); - return 0; -} - -static void qxl_create_memslots(PCIQXLDevice *d) -{ - int i; - - for (i = 0; i < NUM_MEMSLOTS; i++) { - if (!d->guest_slots[i].active) { - continue; - } - qxl_add_memslot(d, i, 0, QXL_SYNC); - } -} - -static int qxl_post_load(void *opaque, int version) -{ - PCIQXLDevice* d = opaque; - uint8_t *ram_start = d->vga.vram_ptr; - QXLCommandExt *cmds; - int in, out, newmode; - - assert(d->last_release_offset < d->vga.vram_size); - if (d->last_release_offset == 0) { - d->last_release = NULL; - } else { - d->last_release = (QXLReleaseInfo *)(ram_start + d->last_release_offset); - } - - d->modes = (QXLModes*)((uint8_t*)d->rom + d->rom->modes_offset); - - trace_qxl_post_load(d->id, qxl_mode_to_string(d->mode)); - newmode = d->mode; - d->mode = QXL_MODE_UNDEFINED; - - switch (newmode) { - case QXL_MODE_UNDEFINED: - qxl_create_memslots(d); - break; - case QXL_MODE_VGA: - qxl_create_memslots(d); - qxl_enter_vga_mode(d); - break; - case QXL_MODE_NATIVE: - qxl_create_memslots(d); - qxl_create_guest_primary(d, 1, QXL_SYNC); - - /* replay surface-create and cursor-set commands */ - 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; - } - cmds[out].cmd.data = d->guest_surfaces.cmds[in]; - cmds[out].cmd.type = QXL_CMD_SURFACE; - cmds[out].group_id = MEMSLOT_GROUP_GUEST; - out++; - } - if (d->guest_cursor) { - cmds[out].cmd.data = d->guest_cursor; - cmds[out].cmd.type = QXL_CMD_CURSOR; - cmds[out].group_id = MEMSLOT_GROUP_GUEST; - out++; - } - qxl_spice_loadvm_commands(d, cmds, out); - g_free(cmds); - if (d->guest_monitors_config) { - qxl_spice_monitors_config_async(d, 1); - } - break; - case QXL_MODE_COMPAT: - /* note: no need to call qxl_create_memslots, qxl_set_mode - * creates the mem slot. */ - qxl_set_mode(d, d->shadow_rom.mode, 1); - break; - } - return 0; -} - -#define QXL_SAVE_VERSION 21 - -static bool qxl_monitors_config_needed(void *opaque) -{ - PCIQXLDevice *qxl = opaque; - - return qxl->guest_monitors_config != 0; -} - - -static VMStateDescription qxl_memslot = { - .name = "qxl-memslot", - .version_id = QXL_SAVE_VERSION, - .minimum_version_id = QXL_SAVE_VERSION, - .fields = (VMStateField[]) { - VMSTATE_UINT64(slot.mem_start, struct guest_slots), - VMSTATE_UINT64(slot.mem_end, struct guest_slots), - VMSTATE_UINT32(active, struct guest_slots), - VMSTATE_END_OF_LIST() - } -}; - -static VMStateDescription qxl_surface = { - .name = "qxl-surface", - .version_id = QXL_SAVE_VERSION, - .minimum_version_id = QXL_SAVE_VERSION, - .fields = (VMStateField[]) { - VMSTATE_UINT32(width, QXLSurfaceCreate), - VMSTATE_UINT32(height, QXLSurfaceCreate), - VMSTATE_INT32(stride, QXLSurfaceCreate), - VMSTATE_UINT32(format, QXLSurfaceCreate), - VMSTATE_UINT32(position, QXLSurfaceCreate), - VMSTATE_UINT32(mouse_mode, QXLSurfaceCreate), - VMSTATE_UINT32(flags, QXLSurfaceCreate), - VMSTATE_UINT32(type, QXLSurfaceCreate), - VMSTATE_UINT64(mem, QXLSurfaceCreate), - VMSTATE_END_OF_LIST() - } -}; - -static VMStateDescription qxl_vmstate_monitors_config = { - .name = "qxl/monitors-config", - .version_id = 1, - .minimum_version_id = 1, - .needed = qxl_monitors_config_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT64(guest_monitors_config, PCIQXLDevice), - VMSTATE_END_OF_LIST() - }, -}; - -static VMStateDescription qxl_vmstate = { - .name = "qxl", - .version_id = QXL_SAVE_VERSION, - .minimum_version_id = QXL_SAVE_VERSION, - .pre_save = qxl_pre_save, - .pre_load = qxl_pre_load, - .post_load = qxl_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(pci, PCIQXLDevice), - VMSTATE_STRUCT(vga, PCIQXLDevice, 0, vmstate_vga_common, VGACommonState), - VMSTATE_UINT32(shadow_rom.mode, PCIQXLDevice), - VMSTATE_UINT32(num_free_res, PCIQXLDevice), - VMSTATE_UINT32(last_release_offset, PCIQXLDevice), - VMSTATE_UINT32(mode, PCIQXLDevice), - VMSTATE_UINT32(ssd.unique, PCIQXLDevice), - VMSTATE_INT32_EQUAL(num_memslots, PCIQXLDevice), - VMSTATE_STRUCT_ARRAY(guest_slots, PCIQXLDevice, NUM_MEMSLOTS, 0, - qxl_memslot, struct guest_slots), - VMSTATE_STRUCT(guest_primary.surface, PCIQXLDevice, 0, - qxl_surface, QXLSurfaceCreate), - VMSTATE_INT32_EQUAL(ssd.num_surfaces, PCIQXLDevice), - VMSTATE_VARRAY_INT32(guest_surfaces.cmds, PCIQXLDevice, - ssd.num_surfaces, 0, - vmstate_info_uint64, uint64_t), - VMSTATE_UINT64(guest_cursor, PCIQXLDevice), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &qxl_vmstate_monitors_config, - NULL - } -}; - -static Property qxl_properties[] = { - DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, - 64 * 1024 * 1024), - DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram32_size, - 64 * 1024 * 1024), - DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, - QXL_DEFAULT_REVISION), - DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0), - DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0), - DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0), - DEFINE_PROP_UINT32("ram_size_mb", PCIQXLDevice, ram_size_mb, -1), - DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, -1), - DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1), - DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 16), - DEFINE_PROP_INT32("surfaces", PCIQXLDevice, ssd.num_surfaces, 1024), -#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */ - DEFINE_PROP_UINT16("max_outputs", PCIQXLDevice, max_outputs, 0), -#endif - DEFINE_PROP_END_OF_LIST(), -}; - -static void qxl_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->vendor_id = REDHAT_PCI_VENDOR_ID; - k->device_id = QXL_DEVICE_ID_STABLE; - set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); - dc->reset = qxl_reset_handler; - dc->vmsd = &qxl_vmstate; - dc->props = qxl_properties; -} - -static const TypeInfo qxl_pci_type_info = { - .name = TYPE_PCI_QXL, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIQXLDevice), - .abstract = true, - .class_init = qxl_pci_class_init, -}; - -static void qxl_primary_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = qxl_realize_primary; - k->romfile = "vgabios-qxl.bin"; - k->class_id = PCI_CLASS_DISPLAY_VGA; - dc->desc = "Spice QXL GPU (primary, vga compatible)"; - dc->hotpluggable = false; -} - -static const TypeInfo qxl_primary_info = { - .name = "qxl-vga", - .parent = TYPE_PCI_QXL, - .class_init = qxl_primary_class_init, -}; - -static void qxl_secondary_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = qxl_realize_secondary; - k->class_id = PCI_CLASS_DISPLAY_OTHER; - dc->desc = "Spice QXL GPU (secondary)"; -} - -static const TypeInfo qxl_secondary_info = { - .name = "qxl", - .parent = TYPE_PCI_QXL, - .class_init = qxl_secondary_class_init, -}; - -static void qxl_register_types(void) -{ - type_register_static(&qxl_pci_type_info); - type_register_static(&qxl_primary_info); - type_register_static(&qxl_secondary_info); -} - -type_init(qxl_register_types) diff --git a/qemu/hw/display/qxl.h b/qemu/hw/display/qxl.h deleted file mode 100644 index 2ddf065e1..000000000 --- a/qemu/hw/display/qxl.h +++ /dev/null @@ -1,174 +0,0 @@ -#ifndef HW_QXL_H -#define HW_QXL_H 1 - -#include "qemu-common.h" - -#include "ui/console.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "vga_int.h" -#include "qemu/thread.h" - -#include "ui/qemu-spice.h" -#include "ui/spice-display.h" - -enum qxl_mode { - QXL_MODE_UNDEFINED, - QXL_MODE_VGA, - QXL_MODE_COMPAT, /* spice 0.4.x */ - QXL_MODE_NATIVE, -}; - -#ifndef QXL_VRAM64_RANGE_INDEX -#define QXL_VRAM64_RANGE_INDEX 4 -#endif - -#define QXL_UNDEFINED_IO UINT32_MAX - -#define QXL_NUM_DIRTY_RECTS 64 - -#define QXL_PAGE_BITS 12 -#define QXL_PAGE_SIZE (1 << QXL_PAGE_BITS); - -typedef struct PCIQXLDevice { - PCIDevice pci; - PortioList vga_port_list; - SimpleSpiceDisplay ssd; - int id; - uint32_t debug; - uint32_t guestdebug; - uint32_t cmdlog; - - uint32_t guest_bug; - - enum qxl_mode mode; - uint32_t cmdflags; - int generation; - uint32_t revision; - - int32_t num_memslots; - - uint32_t current_async; - QemuMutex async_lock; - - struct guest_slots { - QXLMemSlot slot; - void *ptr; - uint64_t size; - uint64_t delta; - uint32_t active; - } guest_slots[NUM_MEMSLOTS]; - - struct guest_primary { - QXLSurfaceCreate surface; - uint32_t commands; - uint32_t resized; - int32_t qxl_stride; - uint32_t abs_stride; - uint32_t bits_pp; - uint32_t bytes_pp; - uint8_t *data; - } guest_primary; - - struct surfaces { - QXLPHYSICAL *cmds; - uint32_t count; - uint32_t max; - } guest_surfaces; - QXLPHYSICAL guest_cursor; - - QXLPHYSICAL guest_monitors_config; - - QemuMutex track_lock; - - /* thread signaling */ - QEMUBH *update_irq; - - /* ram pci bar */ - QXLRam *ram; - VGACommonState vga; - uint32_t num_free_res; - QXLReleaseInfo *last_release; - uint32_t last_release_offset; - uint32_t oom_running; - uint32_t vgamem_size; - - /* rom pci bar */ - QXLRom shadow_rom; - QXLRom *rom; - QXLModes *modes; - uint32_t rom_size; - MemoryRegion rom_bar; -#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */ - uint16_t max_outputs; -#endif - - /* vram pci bar */ - uint32_t vram_size; - MemoryRegion vram_bar; - uint32_t vram32_size; - MemoryRegion vram32_bar; - - /* io bar */ - MemoryRegion io_bar; - - /* user-friendly properties (in megabytes) */ - uint32_t ram_size_mb; - uint32_t vram_size_mb; - uint32_t vram32_size_mb; - uint32_t vgamem_size_mb; - - /* qxl_render_update state */ - int render_update_cookie_num; - int num_dirty_rects; - QXLRect dirty[QXL_NUM_DIRTY_RECTS]; - QEMUBH *update_area_bh; -} PCIQXLDevice; - -#define TYPE_PCI_QXL "pci-qxl" -#define PCI_QXL(obj) OBJECT_CHECK(PCIQXLDevice, (obj), TYPE_PCI_QXL) - -#define PANIC_ON(x) if ((x)) { \ - printf("%s: PANIC %s failed\n", __FUNCTION__, #x); \ - abort(); \ -} - -#define dprint(_qxl, _level, _fmt, ...) \ - do { \ - if (_qxl->debug >= _level) { \ - fprintf(stderr, "qxl-%d: ", _qxl->id); \ - fprintf(stderr, _fmt, ## __VA_ARGS__); \ - } \ - } while (0) - -#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V12 - -/* qxl.c */ -void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id); -void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...) - GCC_FMT_ATTR(2, 3); - -void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, - struct QXLRect *area, struct QXLRect *dirty_rects, - uint32_t num_dirty_rects, - uint32_t clear_dirty_region, - qxl_async_io async, QXLCookie *cookie); -void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, - uint32_t count); -void qxl_spice_oom(PCIQXLDevice *qxl); -void qxl_spice_reset_memslots(PCIQXLDevice *qxl); -void qxl_spice_reset_image_cache(PCIQXLDevice *qxl); -void qxl_spice_reset_cursor(PCIQXLDevice *qxl); - -/* qxl-logger.c */ -int qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id); -int qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext); - -/* qxl-render.c */ -void qxl_render_resize(PCIQXLDevice *qxl); -void qxl_render_update(PCIQXLDevice *qxl); -int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext); -void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie); -void qxl_render_update_area_bh(void *opaque); - -#endif diff --git a/qemu/hw/display/sm501.c b/qemu/hw/display/sm501.c deleted file mode 100644 index 5f7101210..000000000 --- a/qemu/hw/display/sm501.c +++ /dev/null @@ -1,1458 +0,0 @@ -/* - * QEMU SM501 Device - * - * Copyright (c) 2008 Shin-ichiro KAWASAKI - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/hw.h" -#include "hw/char/serial.h" -#include "ui/console.h" -#include "hw/devices.h" -#include "hw/sysbus.h" -#include "qemu/range.h" -#include "ui/pixel_ops.h" -#include "exec/address-spaces.h" - -/* - * Status: 2010/05/07 - * - Minimum implementation for Linux console : mmio regs and CRT layer. - * - 2D grapihcs acceleration partially supported : only fill rectangle. - * - * TODO: - * - Panel support - * - Touch panel support - * - USB support - * - UART support - * - More 2D graphics engine support - * - Performance tuning - */ - -//#define DEBUG_SM501 -//#define DEBUG_BITBLT - -#ifdef DEBUG_SM501 -#define SM501_DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) -#else -#define SM501_DPRINTF(fmt, ...) do {} while(0) -#endif - - -#define MMIO_BASE_OFFSET 0x3e00000 - -/* SM501 register definitions taken from "linux/include/linux/sm501-regs.h" */ - -/* System Configuration area */ -/* System config base */ -#define SM501_SYS_CONFIG (0x000000) - -/* config 1 */ -#define SM501_SYSTEM_CONTROL (0x000000) - -#define SM501_SYSCTRL_PANEL_TRISTATE (1<<0) -#define SM501_SYSCTRL_MEM_TRISTATE (1<<1) -#define SM501_SYSCTRL_CRT_TRISTATE (1<<2) - -#define SM501_SYSCTRL_PCI_SLAVE_BURST_MASK (3<<4) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_1 (0<<4) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_2 (1<<4) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_4 (2<<4) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_8 (3<<4) - -#define SM501_SYSCTRL_PCI_CLOCK_RUN_EN (1<<6) -#define SM501_SYSCTRL_PCI_RETRY_DISABLE (1<<7) -#define SM501_SYSCTRL_PCI_SUBSYS_LOCK (1<<11) -#define SM501_SYSCTRL_PCI_BURST_READ_EN (1<<15) - -/* miscellaneous control */ - -#define SM501_MISC_CONTROL (0x000004) - -#define SM501_MISC_BUS_SH (0x0) -#define SM501_MISC_BUS_PCI (0x1) -#define SM501_MISC_BUS_XSCALE (0x2) -#define SM501_MISC_BUS_NEC (0x6) -#define SM501_MISC_BUS_MASK (0x7) - -#define SM501_MISC_VR_62MB (1<<3) -#define SM501_MISC_CDR_RESET (1<<7) -#define SM501_MISC_USB_LB (1<<8) -#define SM501_MISC_USB_SLAVE (1<<9) -#define SM501_MISC_BL_1 (1<<10) -#define SM501_MISC_MC (1<<11) -#define SM501_MISC_DAC_POWER (1<<12) -#define SM501_MISC_IRQ_INVERT (1<<16) -#define SM501_MISC_SH (1<<17) - -#define SM501_MISC_HOLD_EMPTY (0<<18) -#define SM501_MISC_HOLD_8 (1<<18) -#define SM501_MISC_HOLD_16 (2<<18) -#define SM501_MISC_HOLD_24 (3<<18) -#define SM501_MISC_HOLD_32 (4<<18) -#define SM501_MISC_HOLD_MASK (7<<18) - -#define SM501_MISC_FREQ_12 (1<<24) -#define SM501_MISC_PNL_24BIT (1<<25) -#define SM501_MISC_8051_LE (1<<26) - - - -#define SM501_GPIO31_0_CONTROL (0x000008) -#define SM501_GPIO63_32_CONTROL (0x00000C) -#define SM501_DRAM_CONTROL (0x000010) - -/* command list */ -#define SM501_ARBTRTN_CONTROL (0x000014) - -/* command list */ -#define SM501_COMMAND_LIST_STATUS (0x000024) - -/* interrupt debug */ -#define SM501_RAW_IRQ_STATUS (0x000028) -#define SM501_RAW_IRQ_CLEAR (0x000028) -#define SM501_IRQ_STATUS (0x00002C) -#define SM501_IRQ_MASK (0x000030) -#define SM501_DEBUG_CONTROL (0x000034) - -/* power management */ -#define SM501_POWERMODE_P2X_SRC (1<<29) -#define SM501_POWERMODE_V2X_SRC (1<<20) -#define SM501_POWERMODE_M_SRC (1<<12) -#define SM501_POWERMODE_M1_SRC (1<<4) - -#define SM501_CURRENT_GATE (0x000038) -#define SM501_CURRENT_CLOCK (0x00003C) -#define SM501_POWER_MODE_0_GATE (0x000040) -#define SM501_POWER_MODE_0_CLOCK (0x000044) -#define SM501_POWER_MODE_1_GATE (0x000048) -#define SM501_POWER_MODE_1_CLOCK (0x00004C) -#define SM501_SLEEP_MODE_GATE (0x000050) -#define SM501_POWER_MODE_CONTROL (0x000054) - -/* power gates for units within the 501 */ -#define SM501_GATE_HOST (0) -#define SM501_GATE_MEMORY (1) -#define SM501_GATE_DISPLAY (2) -#define SM501_GATE_2D_ENGINE (3) -#define SM501_GATE_CSC (4) -#define SM501_GATE_ZVPORT (5) -#define SM501_GATE_GPIO (6) -#define SM501_GATE_UART0 (7) -#define SM501_GATE_UART1 (8) -#define SM501_GATE_SSP (10) -#define SM501_GATE_USB_HOST (11) -#define SM501_GATE_USB_GADGET (12) -#define SM501_GATE_UCONTROLLER (17) -#define SM501_GATE_AC97 (18) - -/* panel clock */ -#define SM501_CLOCK_P2XCLK (24) -/* crt clock */ -#define SM501_CLOCK_V2XCLK (16) -/* main clock */ -#define SM501_CLOCK_MCLK (8) -/* SDRAM controller clock */ -#define SM501_CLOCK_M1XCLK (0) - -/* config 2 */ -#define SM501_PCI_MASTER_BASE (0x000058) -#define SM501_ENDIAN_CONTROL (0x00005C) -#define SM501_DEVICEID (0x000060) -/* 0x050100A0 */ - -#define SM501_DEVICEID_SM501 (0x05010000) -#define SM501_DEVICEID_IDMASK (0xffff0000) -#define SM501_DEVICEID_REVMASK (0x000000ff) - -#define SM501_PLLCLOCK_COUNT (0x000064) -#define SM501_MISC_TIMING (0x000068) -#define SM501_CURRENT_SDRAM_CLOCK (0x00006C) - -#define SM501_PROGRAMMABLE_PLL_CONTROL (0x000074) - -/* GPIO base */ -#define SM501_GPIO (0x010000) -#define SM501_GPIO_DATA_LOW (0x00) -#define SM501_GPIO_DATA_HIGH (0x04) -#define SM501_GPIO_DDR_LOW (0x08) -#define SM501_GPIO_DDR_HIGH (0x0C) -#define SM501_GPIO_IRQ_SETUP (0x10) -#define SM501_GPIO_IRQ_STATUS (0x14) -#define SM501_GPIO_IRQ_RESET (0x14) - -/* I2C controller base */ -#define SM501_I2C (0x010040) -#define SM501_I2C_BYTE_COUNT (0x00) -#define SM501_I2C_CONTROL (0x01) -#define SM501_I2C_STATUS (0x02) -#define SM501_I2C_RESET (0x02) -#define SM501_I2C_SLAVE_ADDRESS (0x03) -#define SM501_I2C_DATA (0x04) - -/* SSP base */ -#define SM501_SSP (0x020000) - -/* Uart 0 base */ -#define SM501_UART0 (0x030000) - -/* Uart 1 base */ -#define SM501_UART1 (0x030020) - -/* USB host port base */ -#define SM501_USB_HOST (0x040000) - -/* USB slave/gadget base */ -#define SM501_USB_GADGET (0x060000) - -/* USB slave/gadget data port base */ -#define SM501_USB_GADGET_DATA (0x070000) - -/* Display controller/video engine base */ -#define SM501_DC (0x080000) - -/* common defines for the SM501 address registers */ -#define SM501_ADDR_FLIP (1<<31) -#define SM501_ADDR_EXT (1<<27) -#define SM501_ADDR_CS1 (1<<26) -#define SM501_ADDR_MASK (0x3f << 26) - -#define SM501_FIFO_MASK (0x3 << 16) -#define SM501_FIFO_1 (0x0 << 16) -#define SM501_FIFO_3 (0x1 << 16) -#define SM501_FIFO_7 (0x2 << 16) -#define SM501_FIFO_11 (0x3 << 16) - -/* common registers for panel and the crt */ -#define SM501_OFF_DC_H_TOT (0x000) -#define SM501_OFF_DC_V_TOT (0x008) -#define SM501_OFF_DC_H_SYNC (0x004) -#define SM501_OFF_DC_V_SYNC (0x00C) - -#define SM501_DC_PANEL_CONTROL (0x000) - -#define SM501_DC_PANEL_CONTROL_FPEN (1<<27) -#define SM501_DC_PANEL_CONTROL_BIAS (1<<26) -#define SM501_DC_PANEL_CONTROL_DATA (1<<25) -#define SM501_DC_PANEL_CONTROL_VDD (1<<24) -#define SM501_DC_PANEL_CONTROL_DP (1<<23) - -#define SM501_DC_PANEL_CONTROL_TFT_888 (0<<21) -#define SM501_DC_PANEL_CONTROL_TFT_333 (1<<21) -#define SM501_DC_PANEL_CONTROL_TFT_444 (2<<21) - -#define SM501_DC_PANEL_CONTROL_DE (1<<20) - -#define SM501_DC_PANEL_CONTROL_LCD_TFT (0<<18) -#define SM501_DC_PANEL_CONTROL_LCD_STN8 (1<<18) -#define SM501_DC_PANEL_CONTROL_LCD_STN12 (2<<18) - -#define SM501_DC_PANEL_CONTROL_CP (1<<14) -#define SM501_DC_PANEL_CONTROL_VSP (1<<13) -#define SM501_DC_PANEL_CONTROL_HSP (1<<12) -#define SM501_DC_PANEL_CONTROL_CK (1<<9) -#define SM501_DC_PANEL_CONTROL_TE (1<<8) -#define SM501_DC_PANEL_CONTROL_VPD (1<<7) -#define SM501_DC_PANEL_CONTROL_VP (1<<6) -#define SM501_DC_PANEL_CONTROL_HPD (1<<5) -#define SM501_DC_PANEL_CONTROL_HP (1<<4) -#define SM501_DC_PANEL_CONTROL_GAMMA (1<<3) -#define SM501_DC_PANEL_CONTROL_EN (1<<2) - -#define SM501_DC_PANEL_CONTROL_8BPP (0<<0) -#define SM501_DC_PANEL_CONTROL_16BPP (1<<0) -#define SM501_DC_PANEL_CONTROL_32BPP (2<<0) - - -#define SM501_DC_PANEL_PANNING_CONTROL (0x004) -#define SM501_DC_PANEL_COLOR_KEY (0x008) -#define SM501_DC_PANEL_FB_ADDR (0x00C) -#define SM501_DC_PANEL_FB_OFFSET (0x010) -#define SM501_DC_PANEL_FB_WIDTH (0x014) -#define SM501_DC_PANEL_FB_HEIGHT (0x018) -#define SM501_DC_PANEL_TL_LOC (0x01C) -#define SM501_DC_PANEL_BR_LOC (0x020) -#define SM501_DC_PANEL_H_TOT (0x024) -#define SM501_DC_PANEL_H_SYNC (0x028) -#define SM501_DC_PANEL_V_TOT (0x02C) -#define SM501_DC_PANEL_V_SYNC (0x030) -#define SM501_DC_PANEL_CUR_LINE (0x034) - -#define SM501_DC_VIDEO_CONTROL (0x040) -#define SM501_DC_VIDEO_FB0_ADDR (0x044) -#define SM501_DC_VIDEO_FB_WIDTH (0x048) -#define SM501_DC_VIDEO_FB0_LAST_ADDR (0x04C) -#define SM501_DC_VIDEO_TL_LOC (0x050) -#define SM501_DC_VIDEO_BR_LOC (0x054) -#define SM501_DC_VIDEO_SCALE (0x058) -#define SM501_DC_VIDEO_INIT_SCALE (0x05C) -#define SM501_DC_VIDEO_YUV_CONSTANTS (0x060) -#define SM501_DC_VIDEO_FB1_ADDR (0x064) -#define SM501_DC_VIDEO_FB1_LAST_ADDR (0x068) - -#define SM501_DC_VIDEO_ALPHA_CONTROL (0x080) -#define SM501_DC_VIDEO_ALPHA_FB_ADDR (0x084) -#define SM501_DC_VIDEO_ALPHA_FB_OFFSET (0x088) -#define SM501_DC_VIDEO_ALPHA_FB_LAST_ADDR (0x08C) -#define SM501_DC_VIDEO_ALPHA_TL_LOC (0x090) -#define SM501_DC_VIDEO_ALPHA_BR_LOC (0x094) -#define SM501_DC_VIDEO_ALPHA_SCALE (0x098) -#define SM501_DC_VIDEO_ALPHA_INIT_SCALE (0x09C) -#define SM501_DC_VIDEO_ALPHA_CHROMA_KEY (0x0A0) -#define SM501_DC_VIDEO_ALPHA_COLOR_LOOKUP (0x0A4) - -#define SM501_DC_PANEL_HWC_BASE (0x0F0) -#define SM501_DC_PANEL_HWC_ADDR (0x0F0) -#define SM501_DC_PANEL_HWC_LOC (0x0F4) -#define SM501_DC_PANEL_HWC_COLOR_1_2 (0x0F8) -#define SM501_DC_PANEL_HWC_COLOR_3 (0x0FC) - -#define SM501_HWC_EN (1<<31) - -#define SM501_OFF_HWC_ADDR (0x00) -#define SM501_OFF_HWC_LOC (0x04) -#define SM501_OFF_HWC_COLOR_1_2 (0x08) -#define SM501_OFF_HWC_COLOR_3 (0x0C) - -#define SM501_DC_ALPHA_CONTROL (0x100) -#define SM501_DC_ALPHA_FB_ADDR (0x104) -#define SM501_DC_ALPHA_FB_OFFSET (0x108) -#define SM501_DC_ALPHA_TL_LOC (0x10C) -#define SM501_DC_ALPHA_BR_LOC (0x110) -#define SM501_DC_ALPHA_CHROMA_KEY (0x114) -#define SM501_DC_ALPHA_COLOR_LOOKUP (0x118) - -#define SM501_DC_CRT_CONTROL (0x200) - -#define SM501_DC_CRT_CONTROL_TVP (1<<15) -#define SM501_DC_CRT_CONTROL_CP (1<<14) -#define SM501_DC_CRT_CONTROL_VSP (1<<13) -#define SM501_DC_CRT_CONTROL_HSP (1<<12) -#define SM501_DC_CRT_CONTROL_VS (1<<11) -#define SM501_DC_CRT_CONTROL_BLANK (1<<10) -#define SM501_DC_CRT_CONTROL_SEL (1<<9) -#define SM501_DC_CRT_CONTROL_TE (1<<8) -#define SM501_DC_CRT_CONTROL_PIXEL_MASK (0xF << 4) -#define SM501_DC_CRT_CONTROL_GAMMA (1<<3) -#define SM501_DC_CRT_CONTROL_ENABLE (1<<2) - -#define SM501_DC_CRT_CONTROL_8BPP (0<<0) -#define SM501_DC_CRT_CONTROL_16BPP (1<<0) -#define SM501_DC_CRT_CONTROL_32BPP (2<<0) - -#define SM501_DC_CRT_FB_ADDR (0x204) -#define SM501_DC_CRT_FB_OFFSET (0x208) -#define SM501_DC_CRT_H_TOT (0x20C) -#define SM501_DC_CRT_H_SYNC (0x210) -#define SM501_DC_CRT_V_TOT (0x214) -#define SM501_DC_CRT_V_SYNC (0x218) -#define SM501_DC_CRT_SIGNATURE_ANALYZER (0x21C) -#define SM501_DC_CRT_CUR_LINE (0x220) -#define SM501_DC_CRT_MONITOR_DETECT (0x224) - -#define SM501_DC_CRT_HWC_BASE (0x230) -#define SM501_DC_CRT_HWC_ADDR (0x230) -#define SM501_DC_CRT_HWC_LOC (0x234) -#define SM501_DC_CRT_HWC_COLOR_1_2 (0x238) -#define SM501_DC_CRT_HWC_COLOR_3 (0x23C) - -#define SM501_DC_PANEL_PALETTE (0x400) - -#define SM501_DC_VIDEO_PALETTE (0x800) - -#define SM501_DC_CRT_PALETTE (0xC00) - -/* Zoom Video port base */ -#define SM501_ZVPORT (0x090000) - -/* AC97/I2S base */ -#define SM501_AC97 (0x0A0000) - -/* 8051 micro controller base */ -#define SM501_UCONTROLLER (0x0B0000) - -/* 8051 micro controller SRAM base */ -#define SM501_UCONTROLLER_SRAM (0x0C0000) - -/* DMA base */ -#define SM501_DMA (0x0D0000) - -/* 2d engine base */ -#define SM501_2D_ENGINE (0x100000) -#define SM501_2D_SOURCE (0x00) -#define SM501_2D_DESTINATION (0x04) -#define SM501_2D_DIMENSION (0x08) -#define SM501_2D_CONTROL (0x0C) -#define SM501_2D_PITCH (0x10) -#define SM501_2D_FOREGROUND (0x14) -#define SM501_2D_BACKGROUND (0x18) -#define SM501_2D_STRETCH (0x1C) -#define SM501_2D_COLOR_COMPARE (0x20) -#define SM501_2D_COLOR_COMPARE_MASK (0x24) -#define SM501_2D_MASK (0x28) -#define SM501_2D_CLIP_TL (0x2C) -#define SM501_2D_CLIP_BR (0x30) -#define SM501_2D_MONO_PATTERN_LOW (0x34) -#define SM501_2D_MONO_PATTERN_HIGH (0x38) -#define SM501_2D_WINDOW_WIDTH (0x3C) -#define SM501_2D_SOURCE_BASE (0x40) -#define SM501_2D_DESTINATION_BASE (0x44) -#define SM501_2D_ALPHA (0x48) -#define SM501_2D_WRAP (0x4C) -#define SM501_2D_STATUS (0x50) - -#define SM501_CSC_Y_SOURCE_BASE (0xC8) -#define SM501_CSC_CONSTANTS (0xCC) -#define SM501_CSC_Y_SOURCE_X (0xD0) -#define SM501_CSC_Y_SOURCE_Y (0xD4) -#define SM501_CSC_U_SOURCE_BASE (0xD8) -#define SM501_CSC_V_SOURCE_BASE (0xDC) -#define SM501_CSC_SOURCE_DIMENSION (0xE0) -#define SM501_CSC_SOURCE_PITCH (0xE4) -#define SM501_CSC_DESTINATION (0xE8) -#define SM501_CSC_DESTINATION_DIMENSION (0xEC) -#define SM501_CSC_DESTINATION_PITCH (0xF0) -#define SM501_CSC_SCALE_FACTOR (0xF4) -#define SM501_CSC_DESTINATION_BASE (0xF8) -#define SM501_CSC_CONTROL (0xFC) - -/* 2d engine data port base */ -#define SM501_2D_ENGINE_DATA (0x110000) - -/* end of register definitions */ - -#define SM501_HWC_WIDTH (64) -#define SM501_HWC_HEIGHT (64) - -/* SM501 local memory size taken from "linux/drivers/mfd/sm501.c" */ -static const uint32_t sm501_mem_local_size[] = { - [0] = 4*1024*1024, - [1] = 8*1024*1024, - [2] = 16*1024*1024, - [3] = 32*1024*1024, - [4] = 64*1024*1024, - [5] = 2*1024*1024, -}; -#define get_local_mem_size(s) sm501_mem_local_size[(s)->local_mem_size_index] - -typedef struct SM501State { - /* graphic console status */ - QemuConsole *con; - - /* status & internal resources */ - hwaddr base; - uint32_t local_mem_size_index; - uint8_t * local_mem; - MemoryRegion local_mem_region; - uint32_t last_width; - uint32_t last_height; - - /* mmio registers */ - uint32_t system_control; - uint32_t misc_control; - uint32_t gpio_31_0_control; - uint32_t gpio_63_32_control; - uint32_t dram_control; - uint32_t irq_mask; - uint32_t misc_timing; - uint32_t power_mode_control; - - uint32_t uart0_ier; - uint32_t uart0_lcr; - uint32_t uart0_mcr; - uint32_t uart0_scr; - - uint8_t dc_palette[0x400 * 3]; - - uint32_t dc_panel_control; - uint32_t dc_panel_panning_control; - uint32_t dc_panel_fb_addr; - uint32_t dc_panel_fb_offset; - uint32_t dc_panel_fb_width; - uint32_t dc_panel_fb_height; - uint32_t dc_panel_tl_location; - uint32_t dc_panel_br_location; - uint32_t dc_panel_h_total; - uint32_t dc_panel_h_sync; - uint32_t dc_panel_v_total; - uint32_t dc_panel_v_sync; - - uint32_t dc_panel_hwc_addr; - uint32_t dc_panel_hwc_location; - uint32_t dc_panel_hwc_color_1_2; - uint32_t dc_panel_hwc_color_3; - - uint32_t dc_crt_control; - uint32_t dc_crt_fb_addr; - uint32_t dc_crt_fb_offset; - uint32_t dc_crt_h_total; - uint32_t dc_crt_h_sync; - uint32_t dc_crt_v_total; - uint32_t dc_crt_v_sync; - - uint32_t dc_crt_hwc_addr; - uint32_t dc_crt_hwc_location; - uint32_t dc_crt_hwc_color_1_2; - uint32_t dc_crt_hwc_color_3; - - uint32_t twoD_source; - uint32_t twoD_destination; - uint32_t twoD_dimension; - uint32_t twoD_control; - uint32_t twoD_pitch; - uint32_t twoD_foreground; - uint32_t twoD_stretch; - uint32_t twoD_color_compare_mask; - uint32_t twoD_mask; - uint32_t twoD_window_width; - uint32_t twoD_source_base; - uint32_t twoD_destination_base; - -} SM501State; - -static uint32_t get_local_mem_size_index(uint32_t size) -{ - uint32_t norm_size = 0; - int i, index = 0; - - for (i = 0; i < ARRAY_SIZE(sm501_mem_local_size); i++) { - uint32_t new_size = sm501_mem_local_size[i]; - if (new_size >= size) { - if (norm_size == 0 || norm_size > new_size) { - norm_size = new_size; - index = i; - } - } - } - - return index; -} - -/** - * Check the availability of hardware cursor. - * @param crt 0 for PANEL, 1 for CRT. - */ -static inline int is_hwc_enabled(SM501State *state, int crt) -{ - uint32_t addr = crt ? state->dc_crt_hwc_addr : state->dc_panel_hwc_addr; - return addr & 0x80000000; -} - -/** - * Get the address which holds cursor pattern data. - * @param crt 0 for PANEL, 1 for CRT. - */ -static inline uint32_t get_hwc_address(SM501State *state, int crt) -{ - uint32_t addr = crt ? state->dc_crt_hwc_addr : state->dc_panel_hwc_addr; - return (addr & 0x03FFFFF0)/* >> 4*/; -} - -/** - * Get the cursor position in y coordinate. - * @param crt 0 for PANEL, 1 for CRT. - */ -static inline uint32_t get_hwc_y(SM501State *state, int crt) -{ - uint32_t location = crt ? state->dc_crt_hwc_location - : state->dc_panel_hwc_location; - return (location & 0x07FF0000) >> 16; -} - -/** - * Get the cursor position in x coordinate. - * @param crt 0 for PANEL, 1 for CRT. - */ -static inline uint32_t get_hwc_x(SM501State *state, int crt) -{ - uint32_t location = crt ? state->dc_crt_hwc_location - : state->dc_panel_hwc_location; - return location & 0x000007FF; -} - -/** - * Get the cursor position in x coordinate. - * @param crt 0 for PANEL, 1 for CRT. - * @param index 0, 1, 2 or 3 which specifies color of corsor dot. - */ -static inline uint16_t get_hwc_color(SM501State *state, int crt, int index) -{ - uint32_t color_reg = 0; - uint16_t color_565 = 0; - - if (index == 0) { - return 0; - } - - switch (index) { - case 1: - case 2: - color_reg = crt ? state->dc_crt_hwc_color_1_2 - : state->dc_panel_hwc_color_1_2; - break; - case 3: - color_reg = crt ? state->dc_crt_hwc_color_3 - : state->dc_panel_hwc_color_3; - break; - default: - printf("invalid hw cursor color.\n"); - abort(); - } - - switch (index) { - case 1: - case 3: - color_565 = (uint16_t)(color_reg & 0xFFFF); - break; - case 2: - color_565 = (uint16_t)((color_reg >> 16) & 0xFFFF); - break; - } - return color_565; -} - -static int within_hwc_y_range(SM501State *state, int y, int crt) -{ - int hwc_y = get_hwc_y(state, crt); - return (hwc_y <= y && y < hwc_y + SM501_HWC_HEIGHT); -} - -static void sm501_2d_operation(SM501State * s) -{ - /* obtain operation parameters */ - int operation = (s->twoD_control >> 16) & 0x1f; - int rtl = s->twoD_control & 0x8000000; - int src_x = (s->twoD_source >> 16) & 0x01FFF; - int src_y = s->twoD_source & 0xFFFF; - int dst_x = (s->twoD_destination >> 16) & 0x01FFF; - int dst_y = s->twoD_destination & 0xFFFF; - int operation_width = (s->twoD_dimension >> 16) & 0x1FFF; - int operation_height = s->twoD_dimension & 0xFFFF; - uint32_t color = s->twoD_foreground; - int format_flags = (s->twoD_stretch >> 20) & 0x3; - int addressing = (s->twoD_stretch >> 16) & 0xF; - - /* get frame buffer info */ - uint8_t * src = s->local_mem + (s->twoD_source_base & 0x03FFFFFF); - uint8_t * dst = s->local_mem + (s->twoD_destination_base & 0x03FFFFFF); - int src_width = (s->dc_crt_h_total & 0x00000FFF) + 1; - int dst_width = (s->dc_crt_h_total & 0x00000FFF) + 1; - - if (addressing != 0x0) { - printf("%s: only XY addressing is supported.\n", __func__); - abort(); - } - - if ((s->twoD_source_base & 0x08000000) || - (s->twoD_destination_base & 0x08000000)) { - printf("%s: only local memory is supported.\n", __func__); - abort(); - } - - switch (operation) { - case 0x00: /* copy area */ -#define COPY_AREA(_bpp, _pixel_type, rtl) { \ - int y, x, index_d, index_s; \ - for (y = 0; y < operation_height; y++) { \ - for (x = 0; x < operation_width; x++) { \ - if (rtl) { \ - index_s = ((src_y - y) * src_width + src_x - x) * _bpp; \ - index_d = ((dst_y - y) * dst_width + dst_x - x) * _bpp; \ - } else { \ - index_s = ((src_y + y) * src_width + src_x + x) * _bpp; \ - index_d = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ - } \ - *(_pixel_type*)&dst[index_d] = *(_pixel_type*)&src[index_s];\ - } \ - } \ - } - switch (format_flags) { - case 0: - COPY_AREA(1, uint8_t, rtl); - break; - case 1: - COPY_AREA(2, uint16_t, rtl); - break; - case 2: - COPY_AREA(4, uint32_t, rtl); - break; - } - break; - - case 0x01: /* fill rectangle */ -#define FILL_RECT(_bpp, _pixel_type) { \ - int y, x; \ - for (y = 0; y < operation_height; y++) { \ - for (x = 0; x < operation_width; x++) { \ - int index = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ - *(_pixel_type*)&dst[index] = (_pixel_type)color; \ - } \ - } \ - } - - switch (format_flags) { - case 0: - FILL_RECT(1, uint8_t); - break; - case 1: - FILL_RECT(2, uint16_t); - break; - case 2: - FILL_RECT(4, uint32_t); - break; - } - break; - - default: - printf("non-implemented SM501 2D operation. %d\n", operation); - abort(); - break; - } -} - -static uint64_t sm501_system_config_read(void *opaque, hwaddr addr, - unsigned size) -{ - SM501State * s = (SM501State *)opaque; - uint32_t ret = 0; - SM501_DPRINTF("sm501 system config regs : read addr=%x\n", (int)addr); - - switch(addr) { - case SM501_SYSTEM_CONTROL: - ret = s->system_control; - break; - case SM501_MISC_CONTROL: - ret = s->misc_control; - break; - case SM501_GPIO31_0_CONTROL: - ret = s->gpio_31_0_control; - break; - case SM501_GPIO63_32_CONTROL: - ret = s->gpio_63_32_control; - break; - case SM501_DEVICEID: - ret = 0x050100A0; - break; - case SM501_DRAM_CONTROL: - ret = (s->dram_control & 0x07F107C0) | s->local_mem_size_index << 13; - break; - case SM501_IRQ_MASK: - ret = s->irq_mask; - break; - case SM501_MISC_TIMING: - /* TODO : simulate gate control */ - ret = s->misc_timing; - break; - case SM501_CURRENT_GATE: - /* TODO : simulate gate control */ - ret = 0x00021807; - break; - case SM501_CURRENT_CLOCK: - ret = 0x2A1A0A09; - break; - case SM501_POWER_MODE_CONTROL: - ret = s->power_mode_control; - break; - - default: - printf("sm501 system config : not implemented register read." - " addr=%x\n", (int)addr); - abort(); - } - - return ret; -} - -static void sm501_system_config_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - SM501State * s = (SM501State *)opaque; - SM501_DPRINTF("sm501 system config regs : write addr=%x, val=%x\n", - (uint32_t)addr, (uint32_t)value); - - switch(addr) { - case SM501_SYSTEM_CONTROL: - s->system_control = value & 0xE300B8F7; - break; - case SM501_MISC_CONTROL: - s->misc_control = value & 0xFF7FFF20; - break; - case SM501_GPIO31_0_CONTROL: - s->gpio_31_0_control = value; - break; - case SM501_GPIO63_32_CONTROL: - s->gpio_63_32_control = value; - break; - case SM501_DRAM_CONTROL: - s->local_mem_size_index = (value >> 13) & 0x7; - /* rODO : check validity of size change */ - s->dram_control |= value & 0x7FFFFFC3; - break; - case SM501_IRQ_MASK: - s->irq_mask = value; - break; - case SM501_MISC_TIMING: - s->misc_timing = value & 0xF31F1FFF; - break; - case SM501_POWER_MODE_0_GATE: - case SM501_POWER_MODE_1_GATE: - case SM501_POWER_MODE_0_CLOCK: - case SM501_POWER_MODE_1_CLOCK: - /* TODO : simulate gate & clock control */ - break; - case SM501_POWER_MODE_CONTROL: - s->power_mode_control = value & 0x00000003; - break; - - default: - printf("sm501 system config : not implemented register write." - " addr=%x, val=%x\n", (int)addr, (uint32_t)value); - abort(); - } -} - -static const MemoryRegionOps sm501_system_config_ops = { - .read = sm501_system_config_read, - .write = sm501_system_config_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint32_t sm501_palette_read(void *opaque, hwaddr addr) -{ - SM501State * s = (SM501State *)opaque; - SM501_DPRINTF("sm501 palette read addr=%x\n", (int)addr); - - /* TODO : consider BYTE/WORD access */ - /* TODO : consider endian */ - - assert(range_covers_byte(0, 0x400 * 3, addr)); - return *(uint32_t*)&s->dc_palette[addr]; -} - -static void sm501_palette_write(void *opaque, - hwaddr addr, uint32_t value) -{ - SM501State * s = (SM501State *)opaque; - SM501_DPRINTF("sm501 palette write addr=%x, val=%x\n", - (int)addr, value); - - /* TODO : consider BYTE/WORD access */ - /* TODO : consider endian */ - - assert(range_covers_byte(0, 0x400 * 3, addr)); - *(uint32_t*)&s->dc_palette[addr] = value; -} - -static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr, - unsigned size) -{ - SM501State * s = (SM501State *)opaque; - uint32_t ret = 0; - SM501_DPRINTF("sm501 disp ctrl regs : read addr=%x\n", (int)addr); - - switch(addr) { - - case SM501_DC_PANEL_CONTROL: - ret = s->dc_panel_control; - break; - case SM501_DC_PANEL_PANNING_CONTROL: - ret = s->dc_panel_panning_control; - break; - case SM501_DC_PANEL_FB_ADDR: - ret = s->dc_panel_fb_addr; - break; - case SM501_DC_PANEL_FB_OFFSET: - ret = s->dc_panel_fb_offset; - break; - case SM501_DC_PANEL_FB_WIDTH: - ret = s->dc_panel_fb_width; - break; - case SM501_DC_PANEL_FB_HEIGHT: - ret = s->dc_panel_fb_height; - break; - case SM501_DC_PANEL_TL_LOC: - ret = s->dc_panel_tl_location; - break; - case SM501_DC_PANEL_BR_LOC: - ret = s->dc_panel_br_location; - break; - - case SM501_DC_PANEL_H_TOT: - ret = s->dc_panel_h_total; - break; - case SM501_DC_PANEL_H_SYNC: - ret = s->dc_panel_h_sync; - break; - case SM501_DC_PANEL_V_TOT: - ret = s->dc_panel_v_total; - break; - case SM501_DC_PANEL_V_SYNC: - ret = s->dc_panel_v_sync; - break; - - case SM501_DC_CRT_CONTROL: - ret = s->dc_crt_control; - break; - case SM501_DC_CRT_FB_ADDR: - ret = s->dc_crt_fb_addr; - break; - case SM501_DC_CRT_FB_OFFSET: - ret = s->dc_crt_fb_offset; - break; - case SM501_DC_CRT_H_TOT: - ret = s->dc_crt_h_total; - break; - case SM501_DC_CRT_H_SYNC: - ret = s->dc_crt_h_sync; - break; - case SM501_DC_CRT_V_TOT: - ret = s->dc_crt_v_total; - break; - case SM501_DC_CRT_V_SYNC: - ret = s->dc_crt_v_sync; - break; - - case SM501_DC_CRT_HWC_ADDR: - ret = s->dc_crt_hwc_addr; - break; - case SM501_DC_CRT_HWC_LOC: - ret = s->dc_crt_hwc_location; - break; - case SM501_DC_CRT_HWC_COLOR_1_2: - ret = s->dc_crt_hwc_color_1_2; - break; - case SM501_DC_CRT_HWC_COLOR_3: - ret = s->dc_crt_hwc_color_3; - break; - - case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400*3 - 4: - ret = sm501_palette_read(opaque, addr - SM501_DC_PANEL_PALETTE); - break; - - default: - printf("sm501 disp ctrl : not implemented register read." - " addr=%x\n", (int)addr); - abort(); - } - - return ret; -} - -static void sm501_disp_ctrl_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - SM501State * s = (SM501State *)opaque; - SM501_DPRINTF("sm501 disp ctrl regs : write addr=%x, val=%x\n", - (unsigned)addr, (unsigned)value); - - switch(addr) { - case SM501_DC_PANEL_CONTROL: - s->dc_panel_control = value & 0x0FFF73FF; - break; - case SM501_DC_PANEL_PANNING_CONTROL: - s->dc_panel_panning_control = value & 0xFF3FFF3F; - break; - case SM501_DC_PANEL_FB_ADDR: - s->dc_panel_fb_addr = value & 0x8FFFFFF0; - break; - case SM501_DC_PANEL_FB_OFFSET: - s->dc_panel_fb_offset = value & 0x3FF03FF0; - break; - case SM501_DC_PANEL_FB_WIDTH: - s->dc_panel_fb_width = value & 0x0FFF0FFF; - break; - case SM501_DC_PANEL_FB_HEIGHT: - s->dc_panel_fb_height = value & 0x0FFF0FFF; - break; - case SM501_DC_PANEL_TL_LOC: - s->dc_panel_tl_location = value & 0x07FF07FF; - break; - case SM501_DC_PANEL_BR_LOC: - s->dc_panel_br_location = value & 0x07FF07FF; - break; - - case SM501_DC_PANEL_H_TOT: - s->dc_panel_h_total = value & 0x0FFF0FFF; - break; - case SM501_DC_PANEL_H_SYNC: - s->dc_panel_h_sync = value & 0x00FF0FFF; - break; - case SM501_DC_PANEL_V_TOT: - s->dc_panel_v_total = value & 0x0FFF0FFF; - break; - case SM501_DC_PANEL_V_SYNC: - s->dc_panel_v_sync = value & 0x003F0FFF; - break; - - case SM501_DC_PANEL_HWC_ADDR: - s->dc_panel_hwc_addr = value & 0x8FFFFFF0; - break; - case SM501_DC_PANEL_HWC_LOC: - s->dc_panel_hwc_location = value & 0x0FFF0FFF; - break; - case SM501_DC_PANEL_HWC_COLOR_1_2: - s->dc_panel_hwc_color_1_2 = value; - break; - case SM501_DC_PANEL_HWC_COLOR_3: - s->dc_panel_hwc_color_3 = value & 0x0000FFFF; - break; - - case SM501_DC_CRT_CONTROL: - s->dc_crt_control = value & 0x0003FFFF; - break; - case SM501_DC_CRT_FB_ADDR: - s->dc_crt_fb_addr = value & 0x8FFFFFF0; - break; - case SM501_DC_CRT_FB_OFFSET: - s->dc_crt_fb_offset = value & 0x3FF03FF0; - break; - case SM501_DC_CRT_H_TOT: - s->dc_crt_h_total = value & 0x0FFF0FFF; - break; - case SM501_DC_CRT_H_SYNC: - s->dc_crt_h_sync = value & 0x00FF0FFF; - break; - case SM501_DC_CRT_V_TOT: - s->dc_crt_v_total = value & 0x0FFF0FFF; - break; - case SM501_DC_CRT_V_SYNC: - s->dc_crt_v_sync = value & 0x003F0FFF; - break; - - case SM501_DC_CRT_HWC_ADDR: - s->dc_crt_hwc_addr = value & 0x8FFFFFF0; - break; - case SM501_DC_CRT_HWC_LOC: - s->dc_crt_hwc_location = value & 0x0FFF0FFF; - break; - case SM501_DC_CRT_HWC_COLOR_1_2: - s->dc_crt_hwc_color_1_2 = value; - break; - case SM501_DC_CRT_HWC_COLOR_3: - s->dc_crt_hwc_color_3 = value & 0x0000FFFF; - break; - - case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400*3 - 4: - sm501_palette_write(opaque, addr - SM501_DC_PANEL_PALETTE, value); - break; - - default: - printf("sm501 disp ctrl : not implemented register write." - " addr=%x, val=%x\n", (int)addr, (unsigned)value); - abort(); - } -} - -static const MemoryRegionOps sm501_disp_ctrl_ops = { - .read = sm501_disp_ctrl_read, - .write = sm501_disp_ctrl_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t sm501_2d_engine_read(void *opaque, hwaddr addr, - unsigned size) -{ - SM501State * s = (SM501State *)opaque; - uint32_t ret = 0; - SM501_DPRINTF("sm501 2d engine regs : read addr=%x\n", (int)addr); - - switch(addr) { - case SM501_2D_SOURCE_BASE: - ret = s->twoD_source_base; - break; - default: - printf("sm501 disp ctrl : not implemented register read." - " addr=%x\n", (int)addr); - abort(); - } - - return ret; -} - -static void sm501_2d_engine_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - SM501State * s = (SM501State *)opaque; - SM501_DPRINTF("sm501 2d engine regs : write addr=%x, val=%x\n", - (unsigned)addr, (unsigned)value); - - switch(addr) { - case SM501_2D_SOURCE: - s->twoD_source = value; - break; - case SM501_2D_DESTINATION: - s->twoD_destination = value; - break; - case SM501_2D_DIMENSION: - s->twoD_dimension = value; - break; - case SM501_2D_CONTROL: - s->twoD_control = value; - - /* do 2d operation if start flag is set. */ - if (value & 0x80000000) { - sm501_2d_operation(s); - s->twoD_control &= ~0x80000000; /* start flag down */ - } - - break; - case SM501_2D_PITCH: - s->twoD_pitch = value; - break; - case SM501_2D_FOREGROUND: - s->twoD_foreground = value; - break; - case SM501_2D_STRETCH: - s->twoD_stretch = value; - break; - case SM501_2D_COLOR_COMPARE_MASK: - s->twoD_color_compare_mask = value; - break; - case SM501_2D_MASK: - s->twoD_mask = value; - break; - case SM501_2D_WINDOW_WIDTH: - s->twoD_window_width = value; - break; - case SM501_2D_SOURCE_BASE: - s->twoD_source_base = value; - break; - case SM501_2D_DESTINATION_BASE: - s->twoD_destination_base = value; - break; - default: - printf("sm501 2d engine : not implemented register write." - " addr=%x, val=%x\n", (int)addr, (unsigned)value); - abort(); - } -} - -static const MemoryRegionOps sm501_2d_engine_ops = { - .read = sm501_2d_engine_read, - .write = sm501_2d_engine_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* draw line functions for all console modes */ - -typedef void draw_line_func(uint8_t *d, const uint8_t *s, - int width, const uint32_t *pal); - -typedef void draw_hwc_line_func(SM501State * s, int crt, uint8_t * palette, - int c_y, uint8_t *d, int width); - -#define DEPTH 8 -#include "sm501_template.h" - -#define DEPTH 15 -#include "sm501_template.h" - -#define BGR_FORMAT -#define DEPTH 15 -#include "sm501_template.h" - -#define DEPTH 16 -#include "sm501_template.h" - -#define BGR_FORMAT -#define DEPTH 16 -#include "sm501_template.h" - -#define DEPTH 32 -#include "sm501_template.h" - -#define BGR_FORMAT -#define DEPTH 32 -#include "sm501_template.h" - -static draw_line_func * draw_line8_funcs[] = { - draw_line8_8, - draw_line8_15, - draw_line8_16, - draw_line8_32, - draw_line8_32bgr, - draw_line8_15bgr, - draw_line8_16bgr, -}; - -static draw_line_func * draw_line16_funcs[] = { - draw_line16_8, - draw_line16_15, - draw_line16_16, - draw_line16_32, - draw_line16_32bgr, - draw_line16_15bgr, - draw_line16_16bgr, -}; - -static draw_line_func * draw_line32_funcs[] = { - draw_line32_8, - draw_line32_15, - draw_line32_16, - draw_line32_32, - draw_line32_32bgr, - draw_line32_15bgr, - draw_line32_16bgr, -}; - -static draw_hwc_line_func * draw_hwc_line_funcs[] = { - draw_hwc_line_8, - draw_hwc_line_15, - draw_hwc_line_16, - draw_hwc_line_32, - draw_hwc_line_32bgr, - draw_hwc_line_15bgr, - draw_hwc_line_16bgr, -}; - -static inline int get_depth_index(DisplaySurface *surface) -{ - switch (surface_bits_per_pixel(surface)) { - default: - case 8: - return 0; - case 15: - return 1; - case 16: - return 2; - case 32: - if (is_surface_bgr(surface)) { - return 4; - } else { - return 3; - } - } -} - -static void sm501_draw_crt(SM501State * s) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int y; - int width = (s->dc_crt_h_total & 0x00000FFF) + 1; - int height = (s->dc_crt_v_total & 0x00000FFF) + 1; - - uint8_t * src = s->local_mem; - int src_bpp = 0; - int dst_bpp = surface_bytes_per_pixel(surface); - uint32_t * palette = (uint32_t *)&s->dc_palette[SM501_DC_CRT_PALETTE - - SM501_DC_PANEL_PALETTE]; - uint8_t hwc_palette[3 * 3]; - int ds_depth_index = get_depth_index(surface); - draw_line_func * draw_line = NULL; - draw_hwc_line_func * draw_hwc_line = NULL; - int full_update = 0; - int y_start = -1; - ram_addr_t page_min = ~0l; - ram_addr_t page_max = 0l; - ram_addr_t offset = 0; - - /* choose draw_line function */ - switch (s->dc_crt_control & 3) { - case SM501_DC_CRT_CONTROL_8BPP: - src_bpp = 1; - draw_line = draw_line8_funcs[ds_depth_index]; - break; - case SM501_DC_CRT_CONTROL_16BPP: - src_bpp = 2; - draw_line = draw_line16_funcs[ds_depth_index]; - break; - case SM501_DC_CRT_CONTROL_32BPP: - src_bpp = 4; - draw_line = draw_line32_funcs[ds_depth_index]; - break; - default: - printf("sm501 draw crt : invalid DC_CRT_CONTROL=%x.\n", - s->dc_crt_control); - abort(); - break; - } - - /* set up to draw hardware cursor */ - if (is_hwc_enabled(s, 1)) { - int i; - - /* get cursor palette */ - for (i = 0; i < 3; i++) { - uint16_t rgb565 = get_hwc_color(s, 1, i + 1); - hwc_palette[i * 3 + 0] = (rgb565 & 0xf800) >> 8; /* red */ - hwc_palette[i * 3 + 1] = (rgb565 & 0x07e0) >> 3; /* green */ - hwc_palette[i * 3 + 2] = (rgb565 & 0x001f) << 3; /* blue */ - } - - /* choose cursor draw line function */ - draw_hwc_line = draw_hwc_line_funcs[ds_depth_index]; - } - - /* adjust console size */ - if (s->last_width != width || s->last_height != height) { - qemu_console_resize(s->con, width, height); - surface = qemu_console_surface(s->con); - s->last_width = width; - s->last_height = height; - full_update = 1; - } - - /* draw each line according to conditions */ - memory_region_sync_dirty_bitmap(&s->local_mem_region); - for (y = 0; y < height; y++) { - int update_hwc = draw_hwc_line ? within_hwc_y_range(s, y, 1) : 0; - int update = full_update || update_hwc; - ram_addr_t page0 = offset; - ram_addr_t page1 = offset + width * src_bpp - 1; - - /* check dirty flags for each line */ - update = memory_region_get_dirty(&s->local_mem_region, page0, - page1 - page0, DIRTY_MEMORY_VGA); - - /* draw line and change status */ - if (update) { - uint8_t *d = surface_data(surface); - d += y * width * dst_bpp; - - /* draw graphics layer */ - draw_line(d, src, width, palette); - - /* draw haredware cursor */ - if (update_hwc) { - draw_hwc_line(s, 1, hwc_palette, y - get_hwc_y(s, 1), d, width); - } - - if (y_start < 0) - y_start = y; - if (page0 < page_min) - page_min = page0; - if (page1 > page_max) - page_max = page1; - } else { - if (y_start >= 0) { - /* flush to display */ - dpy_gfx_update(s->con, 0, y_start, width, y - y_start); - y_start = -1; - } - } - - src += width * src_bpp; - offset += width * src_bpp; - } - - /* complete flush to display */ - if (y_start >= 0) - dpy_gfx_update(s->con, 0, y_start, width, y - y_start); - - /* clear dirty flags */ - if (page_min != ~0l) { - memory_region_reset_dirty(&s->local_mem_region, - page_min, page_max + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); - } -} - -static void sm501_update_display(void *opaque) -{ - SM501State * s = (SM501State *)opaque; - - if (s->dc_crt_control & SM501_DC_CRT_CONTROL_ENABLE) - sm501_draw_crt(s); -} - -static const GraphicHwOps sm501_ops = { - .gfx_update = sm501_update_display, -}; - -void sm501_init(MemoryRegion *address_space_mem, uint32_t base, - uint32_t local_mem_bytes, qemu_irq irq, CharDriverState *chr) -{ - SM501State * s; - DeviceState *dev; - MemoryRegion *sm501_system_config = g_new(MemoryRegion, 1); - MemoryRegion *sm501_disp_ctrl = g_new(MemoryRegion, 1); - MemoryRegion *sm501_2d_engine = g_new(MemoryRegion, 1); - - /* allocate management data region */ - s = (SM501State *)g_malloc0(sizeof(SM501State)); - s->base = base; - s->local_mem_size_index - = get_local_mem_size_index(local_mem_bytes); - SM501_DPRINTF("local mem size=%x. index=%d\n", get_local_mem_size(s), - s->local_mem_size_index); - s->system_control = 0x00100000; - s->misc_control = 0x00001000; /* assumes SH, active=low */ - s->dc_panel_control = 0x00010000; - s->dc_crt_control = 0x00010000; - - /* allocate local memory */ - memory_region_init_ram(&s->local_mem_region, NULL, "sm501.local", - 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); - memory_region_add_subregion(address_space_mem, base, &s->local_mem_region); - - /* map mmio */ - memory_region_init_io(sm501_system_config, NULL, &sm501_system_config_ops, s, - "sm501-system-config", 0x6c); - memory_region_add_subregion(address_space_mem, base + MMIO_BASE_OFFSET, - sm501_system_config); - memory_region_init_io(sm501_disp_ctrl, NULL, &sm501_disp_ctrl_ops, s, - "sm501-disp-ctrl", 0x1000); - memory_region_add_subregion(address_space_mem, - base + MMIO_BASE_OFFSET + SM501_DC, - sm501_disp_ctrl); - memory_region_init_io(sm501_2d_engine, NULL, &sm501_2d_engine_ops, s, - "sm501-2d-engine", 0x54); - memory_region_add_subregion(address_space_mem, - base + MMIO_BASE_OFFSET + SM501_2D_ENGINE, - sm501_2d_engine); - - /* bridge to usb host emulation module */ - dev = qdev_create(NULL, "sysbus-ohci"); - qdev_prop_set_uint32(dev, "num-ports", 2); - qdev_prop_set_uint64(dev, "dma-offset", base); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, - base + MMIO_BASE_OFFSET + SM501_USB_HOST); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); - - /* bridge to serial emulation module */ - if (chr) { - serial_mm_init(address_space_mem, - base + MMIO_BASE_OFFSET + SM501_UART0, 2, - NULL, /* TODO : chain irq to IRL */ - 115200, chr, DEVICE_NATIVE_ENDIAN); - } - - /* create qemu graphic console */ - s->con = graphic_console_init(DEVICE(dev), 0, &sm501_ops, s); -} diff --git a/qemu/hw/display/sm501_template.h b/qemu/hw/display/sm501_template.h deleted file mode 100644 index f33e499be..000000000 --- a/qemu/hw/display/sm501_template.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Pixel drawing function templates for QEMU SM501 Device - * - * Copyright (c) 2008 Shin-ichiro KAWASAKI - * - * 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. - */ - -#if DEPTH == 8 -#define BPP 1 -#define PIXEL_TYPE uint8_t -#elif DEPTH == 15 || DEPTH == 16 -#define BPP 2 -#define PIXEL_TYPE uint16_t -#elif DEPTH == 32 -#define BPP 4 -#define PIXEL_TYPE uint32_t -#else -#error unsupport depth -#endif - -#ifdef BGR_FORMAT -#define PIXEL_NAME glue(DEPTH, bgr) -#else -#define PIXEL_NAME DEPTH -#endif /* BGR_FORMAT */ - - -static void glue(draw_line8_, PIXEL_NAME)( - uint8_t *d, const uint8_t *s, int width, const uint32_t *pal) -{ - uint8_t v, r, g, b; - do { - v = ldub_p(s); - r = (pal[v] >> 16) & 0xff; - g = (pal[v] >> 8) & 0xff; - b = (pal[v] >> 0) & 0xff; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); - s ++; - d += BPP; - } while (-- width != 0); -} - -static void glue(draw_line16_, PIXEL_NAME)( - uint8_t *d, const uint8_t *s, int width, const uint32_t *pal) -{ - uint16_t rgb565; - uint8_t r, g, b; - - do { - rgb565 = lduw_p(s); - r = ((rgb565 >> 11) & 0x1f) << 3; - g = ((rgb565 >> 5) & 0x3f) << 2; - b = ((rgb565 >> 0) & 0x1f) << 3; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); - s += 2; - d += BPP; - } while (-- width != 0); -} - -static void glue(draw_line32_, PIXEL_NAME)( - uint8_t *d, const uint8_t *s, int width, const uint32_t *pal) -{ - uint8_t r, g, b; - - do { - ldub_p(s); -#if defined(TARGET_WORDS_BIGENDIAN) - r = s[1]; - g = s[2]; - b = s[3]; -#else - b = s[0]; - g = s[1]; - r = s[2]; -#endif - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); - s += 4; - d += BPP; - } while (-- width != 0); -} - -/** - * Draw hardware cursor image on the given line. - */ -static void glue(draw_hwc_line_, PIXEL_NAME)(SM501State * s, int crt, - uint8_t * palette, int c_y, uint8_t *d, int width) -{ - int x, i; - uint8_t bitset = 0; - - /* get hardware cursor pattern */ - uint32_t cursor_addr = get_hwc_address(s, crt); - assert(0 <= c_y && c_y < SM501_HWC_HEIGHT); - cursor_addr += 64 * c_y / 4; /* 4 pixels per byte */ - cursor_addr += s->base; - - /* get cursor position */ - x = get_hwc_x(s, crt); - d += x * BPP; - - for (i = 0; i < SM501_HWC_WIDTH && x + i < width; i++) { - uint8_t v; - - /* get pixel value */ - if (i % 4 == 0) { - bitset = ldub_phys(&address_space_memory, cursor_addr); - cursor_addr++; - } - v = bitset & 3; - bitset >>= 2; - - /* write pixel */ - if (v) { - v--; - uint8_t r = palette[v * 3 + 0]; - uint8_t g = palette[v * 3 + 1]; - uint8_t b = palette[v * 3 + 2]; - ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); - } - d += BPP; - } -} - -#undef DEPTH -#undef BPP -#undef PIXEL_TYPE -#undef PIXEL_NAME -#undef BGR_FORMAT diff --git a/qemu/hw/display/ssd0303.c b/qemu/hw/display/ssd0303.c deleted file mode 100644 index d3017563f..000000000 --- a/qemu/hw/display/ssd0303.c +++ /dev/null @@ -1,331 +0,0 @@ -/* - * SSD0303 OLED controller with OSRAM Pictiva 96x16 display. - * - * Copyright (c) 2006-2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ - -/* 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" - -//#define DEBUG_SSD0303 1 - -#ifdef DEBUG_SSD0303 -#define DPRINTF(fmt, ...) \ -do { printf("ssd0303: " fmt , ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "ssd0303: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "ssd0303: error: " fmt , ## __VA_ARGS__);} while (0) -#endif - -/* Scaling factor for pixels. */ -#define MAGNIFY 4 - -enum ssd0303_mode -{ - SSD0303_IDLE, - SSD0303_DATA, - SSD0303_CMD -}; - -enum ssd0303_cmd { - SSD0303_CMD_NONE, - SSD0303_CMD_SKIP1 -}; - -#define TYPE_SSD0303 "ssd0303" -#define SSD0303(obj) OBJECT_CHECK(ssd0303_state, (obj), TYPE_SSD0303) - -typedef struct { - I2CSlave parent_obj; - - QemuConsole *con; - int row; - int col; - int start_line; - int mirror; - int flash; - int enabled; - int inverse; - int redraw; - enum ssd0303_mode mode; - enum ssd0303_cmd cmd_state; - uint8_t framebuffer[132*8]; -} ssd0303_state; - -static int ssd0303_recv(I2CSlave *i2c) -{ - BADF("Reads not implemented\n"); - return -1; -} - -static int ssd0303_send(I2CSlave *i2c, uint8_t data) -{ - ssd0303_state *s = SSD0303(i2c); - enum ssd0303_cmd old_cmd_state; - - switch (s->mode) { - case SSD0303_IDLE: - DPRINTF("byte 0x%02x\n", data); - if (data == 0x80) - s->mode = SSD0303_CMD; - else if (data == 0x40) - s->mode = SSD0303_DATA; - else - BADF("Unexpected byte 0x%x\n", data); - break; - case SSD0303_DATA: - DPRINTF("data 0x%02x\n", data); - if (s->col < 132) { - s->framebuffer[s->col + s->row * 132] = data; - s->col++; - s->redraw = 1; - } - break; - case SSD0303_CMD: - old_cmd_state = s->cmd_state; - s->cmd_state = SSD0303_CMD_NONE; - switch (old_cmd_state) { - case SSD0303_CMD_NONE: - DPRINTF("cmd 0x%02x\n", data); - s->mode = SSD0303_IDLE; - switch (data) { - case 0x00 ... 0x0f: /* Set lower column address. */ - s->col = (s->col & 0xf0) | (data & 0xf); - break; - case 0x10 ... 0x20: /* Set higher column address. */ - s->col = (s->col & 0x0f) | ((data & 0xf) << 4); - break; - case 0x40 ... 0x7f: /* Set start line. */ - s->start_line = 0; - break; - case 0x81: /* Set contrast (Ignored). */ - s->cmd_state = SSD0303_CMD_SKIP1; - break; - case 0xa0: /* Mirror off. */ - s->mirror = 0; - break; - case 0xa1: /* Mirror off. */ - s->mirror = 1; - break; - case 0xa4: /* Entire display off. */ - s->flash = 0; - break; - case 0xa5: /* Entire display on. */ - s->flash = 1; - break; - case 0xa6: /* Inverse off. */ - s->inverse = 0; - break; - case 0xa7: /* Inverse on. */ - s->inverse = 1; - break; - case 0xa8: /* Set multiplied ratio (Ignored). */ - s->cmd_state = SSD0303_CMD_SKIP1; - break; - case 0xad: /* DC-DC power control. */ - s->cmd_state = SSD0303_CMD_SKIP1; - break; - case 0xae: /* Display off. */ - s->enabled = 0; - break; - case 0xaf: /* Display on. */ - s->enabled = 1; - break; - case 0xb0 ... 0xbf: /* Set Page address. */ - s->row = data & 7; - break; - case 0xc0 ... 0xc8: /* Set COM output direction (Ignored). */ - break; - case 0xd3: /* Set display offset (Ignored). */ - s->cmd_state = SSD0303_CMD_SKIP1; - break; - case 0xd5: /* Set display clock (Ignored). */ - s->cmd_state = SSD0303_CMD_SKIP1; - break; - case 0xd8: /* Set color and power mode (Ignored). */ - s->cmd_state = SSD0303_CMD_SKIP1; - break; - case 0xd9: /* Set pre-charge period (Ignored). */ - s->cmd_state = SSD0303_CMD_SKIP1; - break; - case 0xda: /* Set COM pin configuration (Ignored). */ - s->cmd_state = SSD0303_CMD_SKIP1; - break; - case 0xdb: /* Set VCOM dselect level (Ignored). */ - s->cmd_state = SSD0303_CMD_SKIP1; - break; - case 0xe3: /* no-op. */ - break; - default: - BADF("Unknown command: 0x%x\n", data); - } - break; - case SSD0303_CMD_SKIP1: - DPRINTF("skip 0x%02x\n", data); - break; - } - break; - } - return 0; -} - -static void ssd0303_event(I2CSlave *i2c, enum i2c_event event) -{ - ssd0303_state *s = SSD0303(i2c); - - switch (event) { - case I2C_FINISH: - s->mode = SSD0303_IDLE; - break; - case I2C_START_RECV: - case I2C_START_SEND: - case I2C_NACK: - /* Nothing to do. */ - break; - } -} - -static void ssd0303_update_display(void *opaque) -{ - ssd0303_state *s = (ssd0303_state *)opaque; - DisplaySurface *surface = qemu_console_surface(s->con); - uint8_t *dest; - uint8_t *src; - int x; - int y; - int line; - char *colors[2]; - char colortab[MAGNIFY * 8]; - int dest_width; - uint8_t mask; - - if (!s->redraw) - return; - - switch (surface_bits_per_pixel(surface)) { - case 0: - return; - 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: - BADF("Bad color depth\n"); - return; - } - dest_width *= MAGNIFY; - memset(colortab, 0xff, dest_width); - memset(colortab + dest_width, 0, dest_width); - if (s->flash) { - colors[0] = colortab; - colors[1] = colortab; - } else if (s->inverse) { - colors[0] = colortab; - colors[1] = colortab + dest_width; - } else { - colors[0] = colortab + dest_width; - colors[1] = colortab; - } - dest = surface_data(surface); - for (y = 0; y < 16; y++) { - line = (y + s->start_line) & 63; - src = s->framebuffer + 132 * (line >> 3) + 36; - mask = 1 << (line & 7); - for (x = 0; x < 96; x++) { - memcpy(dest, colors[(*src & mask) != 0], dest_width); - dest += dest_width; - src++; - } - for (x = 1; x < MAGNIFY; x++) { - memcpy(dest, dest - dest_width * 96, dest_width * 96); - dest += dest_width * 96; - } - } - s->redraw = 0; - dpy_gfx_update(s->con, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY); -} - -static void ssd0303_invalidate_display(void * opaque) -{ - ssd0303_state *s = (ssd0303_state *)opaque; - s->redraw = 1; -} - -static const VMStateDescription vmstate_ssd0303 = { - .name = "ssd0303_oled", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32(row, ssd0303_state), - VMSTATE_INT32(col, ssd0303_state), - VMSTATE_INT32(start_line, ssd0303_state), - VMSTATE_INT32(mirror, ssd0303_state), - VMSTATE_INT32(flash, ssd0303_state), - VMSTATE_INT32(enabled, ssd0303_state), - VMSTATE_INT32(inverse, ssd0303_state), - VMSTATE_INT32(redraw, ssd0303_state), - VMSTATE_UINT32(mode, ssd0303_state), - VMSTATE_UINT32(cmd_state, ssd0303_state), - VMSTATE_BUFFER(framebuffer, ssd0303_state), - VMSTATE_I2C_SLAVE(parent_obj, ssd0303_state), - VMSTATE_END_OF_LIST() - } -}; - -static const GraphicHwOps ssd0303_ops = { - .invalidate = ssd0303_invalidate_display, - .gfx_update = ssd0303_update_display, -}; - -static int ssd0303_init(I2CSlave *i2c) -{ - ssd0303_state *s = SSD0303(i2c); - - s->con = graphic_console_init(DEVICE(i2c), 0, &ssd0303_ops, s); - qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY); - return 0; -} - -static void ssd0303_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - - k->init = ssd0303_init; - k->event = ssd0303_event; - k->recv = ssd0303_recv; - k->send = ssd0303_send; - dc->vmsd = &vmstate_ssd0303; -} - -static const TypeInfo ssd0303_info = { - .name = TYPE_SSD0303, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(ssd0303_state), - .class_init = ssd0303_class_init, -}; - -static void ssd0303_register_types(void) -{ - type_register_static(&ssd0303_info); -} - -type_init(ssd0303_register_types) diff --git a/qemu/hw/display/ssd0323.c b/qemu/hw/display/ssd0323.c deleted file mode 100644 index 14c1bf339..000000000 --- a/qemu/hw/display/ssd0323.c +++ /dev/null @@ -1,402 +0,0 @@ -/* - * SSD0323 OLED controller with OSRAM Pictiva 128x64 display. - * - * Copyright (c) 2006-2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ - -/* 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/ssi/ssi.h" -#include "ui/console.h" - -//#define DEBUG_SSD0323 1 - -#ifdef DEBUG_SSD0323 -#define DPRINTF(fmt, ...) \ -do { printf("ssd0323: " fmt , ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { \ - fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__); abort(); \ -} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__);} while (0) -#endif - -/* Scaling factor for pixels. */ -#define MAGNIFY 4 - -#define REMAP_SWAP_COLUMN 0x01 -#define REMAP_SWAP_NYBBLE 0x02 -#define REMAP_VERTICAL 0x04 -#define REMAP_SWAP_COM 0x10 -#define REMAP_SPLIT_COM 0x40 - -enum ssd0323_mode -{ - SSD0323_CMD, - SSD0323_DATA -}; - -typedef struct { - SSISlave ssidev; - QemuConsole *con; - - int cmd_len; - int cmd; - int cmd_data[8]; - int row; - int row_start; - int row_end; - int col; - int col_start; - int col_end; - int redraw; - int remap; - enum ssd0323_mode mode; - uint8_t framebuffer[128 * 80 / 2]; -} ssd0323_state; - -static uint32_t ssd0323_transfer(SSISlave *dev, uint32_t data) -{ - ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, dev); - - switch (s->mode) { - case SSD0323_DATA: - DPRINTF("data 0x%02x\n", data); - s->framebuffer[s->col + s->row * 64] = data; - if (s->remap & REMAP_VERTICAL) { - s->row++; - if (s->row > s->row_end) { - s->row = s->row_start; - s->col++; - } - if (s->col > s->col_end) { - s->col = s->col_start; - } - } else { - s->col++; - if (s->col > s->col_end) { - s->row++; - s->col = s->col_start; - } - if (s->row > s->row_end) { - s->row = s->row_start; - } - } - s->redraw = 1; - break; - case SSD0323_CMD: - DPRINTF("cmd 0x%02x\n", data); - if (s->cmd_len == 0) { - s->cmd = data; - } else { - s->cmd_data[s->cmd_len - 1] = data; - } - s->cmd_len++; - switch (s->cmd) { -#define DATA(x) if (s->cmd_len <= (x)) return 0 - case 0x15: /* Set column. */ - DATA(2); - s->col = s->col_start = s->cmd_data[0] % 64; - s->col_end = s->cmd_data[1] % 64; - break; - case 0x75: /* Set row. */ - DATA(2); - s->row = s->row_start = s->cmd_data[0] % 80; - s->row_end = s->cmd_data[1] % 80; - break; - case 0x81: /* Set contrast */ - DATA(1); - break; - case 0x84: case 0x85: case 0x86: /* Max current. */ - DATA(0); - break; - case 0xa0: /* Set remapping. */ - /* FIXME: Implement this. */ - DATA(1); - s->remap = s->cmd_data[0]; - break; - case 0xa1: /* Set display start line. */ - case 0xa2: /* Set display offset. */ - /* FIXME: Implement these. */ - DATA(1); - break; - case 0xa4: /* Normal mode. */ - case 0xa5: /* All on. */ - case 0xa6: /* All off. */ - case 0xa7: /* Inverse. */ - /* FIXME: Implement these. */ - DATA(0); - break; - case 0xa8: /* Set multiplex ratio. */ - case 0xad: /* Set DC-DC converter. */ - DATA(1); - /* Ignored. Don't care. */ - break; - case 0xae: /* Display off. */ - case 0xaf: /* Display on. */ - DATA(0); - /* TODO: Implement power control. */ - break; - case 0xb1: /* Set phase length. */ - case 0xb2: /* Set row period. */ - case 0xb3: /* Set clock rate. */ - case 0xbc: /* Set precharge. */ - case 0xbe: /* Set VCOMH. */ - case 0xbf: /* Set segment low. */ - DATA(1); - /* Ignored. Don't care. */ - break; - case 0xb8: /* Set grey scale table. */ - /* FIXME: Implement this. */ - DATA(8); - break; - case 0xe3: /* NOP. */ - DATA(0); - break; - case 0xff: /* Nasty hack because we don't handle chip selects - properly. */ - break; - default: - BADF("Unknown command: 0x%x\n", data); - } - s->cmd_len = 0; - return 0; - } - return 0; -} - -static void ssd0323_update_display(void *opaque) -{ - ssd0323_state *s = (ssd0323_state *)opaque; - DisplaySurface *surface = qemu_console_surface(s->con); - uint8_t *dest; - uint8_t *src; - int x; - int y; - int i; - int line; - char *colors[16]; - char colortab[MAGNIFY * 64]; - char *p; - int dest_width; - - if (!s->redraw) - return; - - switch (surface_bits_per_pixel(surface)) { - case 0: - return; - 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: - BADF("Bad color depth\n"); - return; - } - p = colortab; - for (i = 0; i < 16; i++) { - int n; - colors[i] = p; - switch (surface_bits_per_pixel(surface)) { - case 15: - n = i * 2 + (i >> 3); - p[0] = n | (n << 5); - p[1] = (n << 2) | (n >> 3); - break; - case 16: - n = i * 2 + (i >> 3); - p[0] = n | (n << 6) | ((n << 1) & 0x20); - p[1] = (n << 3) | (n >> 2); - break; - case 24: - case 32: - n = (i << 4) | i; - p[0] = p[1] = p[2] = n; - break; - default: - BADF("Bad color depth\n"); - return; - } - p += dest_width; - } - /* TODO: Implement row/column remapping. */ - dest = surface_data(surface); - for (y = 0; y < 64; y++) { - line = y; - src = s->framebuffer + 64 * line; - for (x = 0; x < 64; x++) { - int val; - val = *src >> 4; - for (i = 0; i < MAGNIFY; i++) { - memcpy(dest, colors[val], dest_width); - dest += dest_width; - } - val = *src & 0xf; - for (i = 0; i < MAGNIFY; i++) { - memcpy(dest, colors[val], dest_width); - dest += dest_width; - } - src++; - } - for (i = 1; i < MAGNIFY; i++) { - memcpy(dest, dest - dest_width * MAGNIFY * 128, - dest_width * 128 * MAGNIFY); - dest += dest_width * 128 * MAGNIFY; - } - } - s->redraw = 0; - dpy_gfx_update(s->con, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY); -} - -static void ssd0323_invalidate_display(void * opaque) -{ - ssd0323_state *s = (ssd0323_state *)opaque; - s->redraw = 1; -} - -/* Command/data input. */ -static void ssd0323_cd(void *opaque, int n, int level) -{ - ssd0323_state *s = (ssd0323_state *)opaque; - DPRINTF("%s mode\n", level ? "Data" : "Command"); - s->mode = level ? SSD0323_DATA : SSD0323_CMD; -} - -static void ssd0323_save(QEMUFile *f, void *opaque) -{ - SSISlave *ss = SSI_SLAVE(opaque); - ssd0323_state *s = (ssd0323_state *)opaque; - int i; - - qemu_put_be32(f, s->cmd_len); - qemu_put_be32(f, s->cmd); - for (i = 0; i < 8; i++) - qemu_put_be32(f, s->cmd_data[i]); - qemu_put_be32(f, s->row); - qemu_put_be32(f, s->row_start); - qemu_put_be32(f, s->row_end); - qemu_put_be32(f, s->col); - qemu_put_be32(f, s->col_start); - qemu_put_be32(f, s->col_end); - qemu_put_be32(f, s->redraw); - qemu_put_be32(f, s->remap); - qemu_put_be32(f, s->mode); - qemu_put_buffer(f, s->framebuffer, sizeof(s->framebuffer)); - - qemu_put_be32(f, ss->cs); -} - -static int ssd0323_load(QEMUFile *f, void *opaque, int version_id) -{ - SSISlave *ss = SSI_SLAVE(opaque); - ssd0323_state *s = (ssd0323_state *)opaque; - int i; - - if (version_id != 1) - return -EINVAL; - - s->cmd_len = qemu_get_be32(f); - if (s->cmd_len < 0 || s->cmd_len > ARRAY_SIZE(s->cmd_data)) { - return -EINVAL; - } - s->cmd = qemu_get_be32(f); - for (i = 0; i < 8; i++) - s->cmd_data[i] = qemu_get_be32(f); - s->row = qemu_get_be32(f); - if (s->row < 0 || s->row >= 80) { - return -EINVAL; - } - s->row_start = qemu_get_be32(f); - if (s->row_start < 0 || s->row_start >= 80) { - return -EINVAL; - } - s->row_end = qemu_get_be32(f); - if (s->row_end < 0 || s->row_end >= 80) { - return -EINVAL; - } - s->col = qemu_get_be32(f); - if (s->col < 0 || s->col >= 64) { - return -EINVAL; - } - s->col_start = qemu_get_be32(f); - if (s->col_start < 0 || s->col_start >= 64) { - return -EINVAL; - } - s->col_end = qemu_get_be32(f); - if (s->col_end < 0 || s->col_end >= 64) { - return -EINVAL; - } - s->redraw = qemu_get_be32(f); - s->remap = qemu_get_be32(f); - s->mode = qemu_get_be32(f); - if (s->mode != SSD0323_CMD && s->mode != SSD0323_DATA) { - return -EINVAL; - } - qemu_get_buffer(f, s->framebuffer, sizeof(s->framebuffer)); - - ss->cs = qemu_get_be32(f); - - return 0; -} - -static const GraphicHwOps ssd0323_ops = { - .invalidate = ssd0323_invalidate_display, - .gfx_update = ssd0323_update_display, -}; - -static int ssd0323_init(SSISlave *d) -{ - DeviceState *dev = DEVICE(d); - ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, d); - - s->col_end = 63; - s->row_end = 79; - s->con = graphic_console_init(dev, 0, &ssd0323_ops, s); - qemu_console_resize(s->con, 128 * MAGNIFY, 64 * MAGNIFY); - - qdev_init_gpio_in(dev, ssd0323_cd, 1); - - register_savevm(dev, "ssd0323_oled", -1, 1, - ssd0323_save, ssd0323_load, s); - return 0; -} - -static void ssd0323_class_init(ObjectClass *klass, void *data) -{ - SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - - k->init = ssd0323_init; - k->transfer = ssd0323_transfer; - k->cs_polarity = SSI_CS_HIGH; -} - -static const TypeInfo ssd0323_info = { - .name = "ssd0323", - .parent = TYPE_SSI_SLAVE, - .instance_size = sizeof(ssd0323_state), - .class_init = ssd0323_class_init, -}; - -static void ssd03232_register_types(void) -{ - type_register_static(&ssd0323_info); -} - -type_init(ssd03232_register_types) diff --git a/qemu/hw/display/tc6393xb.c b/qemu/hw/display/tc6393xb.c deleted file mode 100644 index da3ceceb0..000000000 --- a/qemu/hw/display/tc6393xb.c +++ /dev/null @@ -1,598 +0,0 @@ -/* - * Toshiba TC6393XB I/O Controller. - * Found in Sharp Zaurus SL-6000 (tosa) or some - * Toshiba e-Series PDAs. - * - * Most features are currently unsupported!!! - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/hw.h" -#include "hw/devices.h" -#include "hw/block/flash.h" -#include "ui/console.h" -#include "ui/pixel_ops.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" - -#define IRQ_TC6393_NAND 0 -#define IRQ_TC6393_MMC 1 -#define IRQ_TC6393_OHCI 2 -#define IRQ_TC6393_SERIAL 3 -#define IRQ_TC6393_FB 4 - -#define TC6393XB_NR_IRQS 8 - -#define TC6393XB_GPIOS 16 - -#define SCR_REVID 0x08 /* b Revision ID */ -#define SCR_ISR 0x50 /* b Interrupt Status */ -#define SCR_IMR 0x52 /* b Interrupt Mask */ -#define SCR_IRR 0x54 /* b Interrupt Routing */ -#define SCR_GPER 0x60 /* w GP Enable */ -#define SCR_GPI_SR(i) (0x64 + (i)) /* b3 GPI Status */ -#define SCR_GPI_IMR(i) (0x68 + (i)) /* b3 GPI INT Mask */ -#define SCR_GPI_EDER(i) (0x6c + (i)) /* b3 GPI Edge Detect Enable */ -#define SCR_GPI_LIR(i) (0x70 + (i)) /* b3 GPI Level Invert */ -#define SCR_GPO_DSR(i) (0x78 + (i)) /* b3 GPO Data Set */ -#define SCR_GPO_DOECR(i) (0x7c + (i)) /* b3 GPO Data OE Control */ -#define SCR_GP_IARCR(i) (0x80 + (i)) /* b3 GP Internal Active Register Control */ -#define SCR_GP_IARLCR(i) (0x84 + (i)) /* b3 GP INTERNAL Active Register Level Control */ -#define SCR_GPI_BCR(i) (0x88 + (i)) /* b3 GPI Buffer Control */ -#define SCR_GPA_IARCR 0x8c /* w GPa Internal Active Register Control */ -#define SCR_GPA_IARLCR 0x90 /* w GPa Internal Active Register Level Control */ -#define SCR_GPA_BCR 0x94 /* w GPa Buffer Control */ -#define SCR_CCR 0x98 /* w Clock Control */ -#define SCR_PLL2CR 0x9a /* w PLL2 Control */ -#define SCR_PLL1CR 0x9c /* l PLL1 Control */ -#define SCR_DIARCR 0xa0 /* b Device Internal Active Register Control */ -#define SCR_DBOCR 0xa1 /* b Device Buffer Off Control */ -#define SCR_FER 0xe0 /* b Function Enable */ -#define SCR_MCR 0xe4 /* w Mode Control */ -#define SCR_CONFIG 0xfc /* b Configuration Control */ -#define SCR_DEBUG 0xff /* b Debug */ - -#define NAND_CFG_COMMAND 0x04 /* w Command */ -#define NAND_CFG_BASE 0x10 /* l Control Base Address */ -#define NAND_CFG_INTP 0x3d /* b Interrupt Pin */ -#define NAND_CFG_INTE 0x48 /* b Int Enable */ -#define NAND_CFG_EC 0x4a /* b Event Control */ -#define NAND_CFG_ICC 0x4c /* b Internal Clock Control */ -#define NAND_CFG_ECCC 0x5b /* b ECC Control */ -#define NAND_CFG_NFTC 0x60 /* b NAND Flash Transaction Control */ -#define NAND_CFG_NFM 0x61 /* b NAND Flash Monitor */ -#define NAND_CFG_NFPSC 0x62 /* b NAND Flash Power Supply Control */ -#define NAND_CFG_NFDC 0x63 /* b NAND Flash Detect Control */ - -#define NAND_DATA 0x00 /* l Data */ -#define NAND_MODE 0x04 /* b Mode */ -#define NAND_STATUS 0x05 /* b Status */ -#define NAND_ISR 0x06 /* b Interrupt Status */ -#define NAND_IMR 0x07 /* b Interrupt Mask */ - -#define NAND_MODE_WP 0x80 -#define NAND_MODE_CE 0x10 -#define NAND_MODE_ALE 0x02 -#define NAND_MODE_CLE 0x01 -#define NAND_MODE_ECC_MASK 0x60 -#define NAND_MODE_ECC_EN 0x20 -#define NAND_MODE_ECC_READ 0x40 -#define NAND_MODE_ECC_RST 0x60 - -struct TC6393xbState { - MemoryRegion iomem; - qemu_irq irq; - qemu_irq *sub_irqs; - struct { - uint8_t ISR; - uint8_t IMR; - uint8_t IRR; - uint16_t GPER; - uint8_t GPI_SR[3]; - uint8_t GPI_IMR[3]; - uint8_t GPI_EDER[3]; - uint8_t GPI_LIR[3]; - uint8_t GP_IARCR[3]; - uint8_t GP_IARLCR[3]; - uint8_t GPI_BCR[3]; - uint16_t GPA_IARCR; - uint16_t GPA_IARLCR; - uint16_t CCR; - uint16_t PLL2CR; - uint32_t PLL1CR; - uint8_t DIARCR; - uint8_t DBOCR; - uint8_t FER; - uint16_t MCR; - uint8_t CONFIG; - uint8_t DEBUG; - } scr; - uint32_t gpio_dir; - uint32_t gpio_level; - uint32_t prev_level; - qemu_irq handler[TC6393XB_GPIOS]; - qemu_irq *gpio_in; - - struct { - uint8_t mode; - uint8_t isr; - uint8_t imr; - } nand; - int nand_enable; - uint32_t nand_phys; - DeviceState *flash; - ECCState ecc; - - QemuConsole *con; - MemoryRegion vram; - uint16_t *vram_ptr; - uint32_t scr_width, scr_height; /* in pixels */ - qemu_irq l3v; - unsigned blank : 1, - blanked : 1; -}; - -qemu_irq *tc6393xb_gpio_in_get(TC6393xbState *s) -{ - return s->gpio_in; -} - -static void tc6393xb_gpio_set(void *opaque, int line, int level) -{ -// TC6393xbState *s = opaque; - - if (line > TC6393XB_GPIOS) { - printf("%s: No GPIO pin %i\n", __FUNCTION__, line); - return; - } - - // FIXME: how does the chip reflect the GPIO input level change? -} - -void tc6393xb_gpio_out_set(TC6393xbState *s, int line, - qemu_irq handler) -{ - if (line >= TC6393XB_GPIOS) { - fprintf(stderr, "TC6393xb: no GPIO pin %d\n", line); - return; - } - - s->handler[line] = handler; -} - -static void tc6393xb_gpio_handler_update(TC6393xbState *s) -{ - uint32_t level, diff; - int bit; - - level = s->gpio_level & s->gpio_dir; - - for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) { - bit = ctz32(diff); - qemu_set_irq(s->handler[bit], (level >> bit) & 1); - } - - s->prev_level = level; -} - -qemu_irq tc6393xb_l3v_get(TC6393xbState *s) -{ - return s->l3v; -} - -static void tc6393xb_l3v(void *opaque, int line, int level) -{ - TC6393xbState *s = opaque; - s->blank = !level; - fprintf(stderr, "L3V: %d\n", level); -} - -static void tc6393xb_sub_irq(void *opaque, int line, int level) { - TC6393xbState *s = opaque; - uint8_t isr = s->scr.ISR; - if (level) - isr |= 1 << line; - else - isr &= ~(1 << line); - s->scr.ISR = isr; - qemu_set_irq(s->irq, isr & s->scr.IMR); -} - -#define SCR_REG_B(N) \ - case SCR_ ##N: return s->scr.N -#define SCR_REG_W(N) \ - case SCR_ ##N: return s->scr.N; \ - case SCR_ ##N + 1: return s->scr.N >> 8; -#define SCR_REG_L(N) \ - case SCR_ ##N: return s->scr.N; \ - case SCR_ ##N + 1: return s->scr.N >> 8; \ - case SCR_ ##N + 2: return s->scr.N >> 16; \ - case SCR_ ##N + 3: return s->scr.N >> 24; -#define SCR_REG_A(N) \ - case SCR_ ##N(0): return s->scr.N[0]; \ - case SCR_ ##N(1): return s->scr.N[1]; \ - case SCR_ ##N(2): return s->scr.N[2] - -static uint32_t tc6393xb_scr_readb(TC6393xbState *s, hwaddr addr) -{ - switch (addr) { - case SCR_REVID: - return 3; - case SCR_REVID+1: - return 0; - SCR_REG_B(ISR); - SCR_REG_B(IMR); - SCR_REG_B(IRR); - SCR_REG_W(GPER); - SCR_REG_A(GPI_SR); - SCR_REG_A(GPI_IMR); - SCR_REG_A(GPI_EDER); - SCR_REG_A(GPI_LIR); - case SCR_GPO_DSR(0): - case SCR_GPO_DSR(1): - case SCR_GPO_DSR(2): - return (s->gpio_level >> ((addr - SCR_GPO_DSR(0)) * 8)) & 0xff; - case SCR_GPO_DOECR(0): - case SCR_GPO_DOECR(1): - case SCR_GPO_DOECR(2): - return (s->gpio_dir >> ((addr - SCR_GPO_DOECR(0)) * 8)) & 0xff; - SCR_REG_A(GP_IARCR); - SCR_REG_A(GP_IARLCR); - SCR_REG_A(GPI_BCR); - SCR_REG_W(GPA_IARCR); - SCR_REG_W(GPA_IARLCR); - SCR_REG_W(CCR); - SCR_REG_W(PLL2CR); - SCR_REG_L(PLL1CR); - SCR_REG_B(DIARCR); - SCR_REG_B(DBOCR); - SCR_REG_B(FER); - SCR_REG_W(MCR); - SCR_REG_B(CONFIG); - SCR_REG_B(DEBUG); - } - fprintf(stderr, "tc6393xb_scr: unhandled read at %08x\n", (uint32_t) addr); - return 0; -} -#undef SCR_REG_B -#undef SCR_REG_W -#undef SCR_REG_L -#undef SCR_REG_A - -#define SCR_REG_B(N) \ - case SCR_ ##N: s->scr.N = value; return; -#define SCR_REG_W(N) \ - case SCR_ ##N: s->scr.N = (s->scr.N & ~0xff) | (value & 0xff); return; \ - case SCR_ ##N + 1: s->scr.N = (s->scr.N & 0xff) | (value << 8); return -#define SCR_REG_L(N) \ - case SCR_ ##N: s->scr.N = (s->scr.N & ~0xff) | (value & 0xff); return; \ - case SCR_ ##N + 1: s->scr.N = (s->scr.N & ~(0xff << 8)) | (value & (0xff << 8)); return; \ - case SCR_ ##N + 2: s->scr.N = (s->scr.N & ~(0xff << 16)) | (value & (0xff << 16)); return; \ - case SCR_ ##N + 3: s->scr.N = (s->scr.N & ~(0xff << 24)) | (value & (0xff << 24)); return; -#define SCR_REG_A(N) \ - case SCR_ ##N(0): s->scr.N[0] = value; return; \ - case SCR_ ##N(1): s->scr.N[1] = value; return; \ - case SCR_ ##N(2): s->scr.N[2] = value; return - -static void tc6393xb_scr_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) -{ - switch (addr) { - SCR_REG_B(ISR); - SCR_REG_B(IMR); - SCR_REG_B(IRR); - SCR_REG_W(GPER); - SCR_REG_A(GPI_SR); - SCR_REG_A(GPI_IMR); - SCR_REG_A(GPI_EDER); - SCR_REG_A(GPI_LIR); - case SCR_GPO_DSR(0): - case SCR_GPO_DSR(1): - case SCR_GPO_DSR(2): - s->gpio_level = (s->gpio_level & ~(0xff << ((addr - SCR_GPO_DSR(0))*8))) | ((value & 0xff) << ((addr - SCR_GPO_DSR(0))*8)); - tc6393xb_gpio_handler_update(s); - return; - case SCR_GPO_DOECR(0): - case SCR_GPO_DOECR(1): - case SCR_GPO_DOECR(2): - s->gpio_dir = (s->gpio_dir & ~(0xff << ((addr - SCR_GPO_DOECR(0))*8))) | ((value & 0xff) << ((addr - SCR_GPO_DOECR(0))*8)); - tc6393xb_gpio_handler_update(s); - return; - SCR_REG_A(GP_IARCR); - SCR_REG_A(GP_IARLCR); - SCR_REG_A(GPI_BCR); - SCR_REG_W(GPA_IARCR); - SCR_REG_W(GPA_IARLCR); - SCR_REG_W(CCR); - SCR_REG_W(PLL2CR); - SCR_REG_L(PLL1CR); - SCR_REG_B(DIARCR); - SCR_REG_B(DBOCR); - SCR_REG_B(FER); - SCR_REG_W(MCR); - SCR_REG_B(CONFIG); - SCR_REG_B(DEBUG); - } - fprintf(stderr, "tc6393xb_scr: unhandled write at %08x: %02x\n", - (uint32_t) addr, value & 0xff); -} -#undef SCR_REG_B -#undef SCR_REG_W -#undef SCR_REG_L -#undef SCR_REG_A - -static void tc6393xb_nand_irq(TC6393xbState *s) { - qemu_set_irq(s->sub_irqs[IRQ_TC6393_NAND], - (s->nand.imr & 0x80) && (s->nand.imr & s->nand.isr)); -} - -static uint32_t tc6393xb_nand_cfg_readb(TC6393xbState *s, hwaddr addr) { - switch (addr) { - case NAND_CFG_COMMAND: - return s->nand_enable ? 2 : 0; - case NAND_CFG_BASE: - case NAND_CFG_BASE + 1: - case NAND_CFG_BASE + 2: - case NAND_CFG_BASE + 3: - return s->nand_phys >> (addr - NAND_CFG_BASE); - } - fprintf(stderr, "tc6393xb_nand_cfg: unhandled read at %08x\n", (uint32_t) addr); - return 0; -} -static void tc6393xb_nand_cfg_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) { - switch (addr) { - case NAND_CFG_COMMAND: - s->nand_enable = (value & 0x2); - return; - case NAND_CFG_BASE: - case NAND_CFG_BASE + 1: - case NAND_CFG_BASE + 2: - case NAND_CFG_BASE + 3: - s->nand_phys &= ~(0xff << ((addr - NAND_CFG_BASE) * 8)); - s->nand_phys |= (value & 0xff) << ((addr - NAND_CFG_BASE) * 8); - return; - } - fprintf(stderr, "tc6393xb_nand_cfg: unhandled write at %08x: %02x\n", - (uint32_t) addr, value & 0xff); -} - -static uint32_t tc6393xb_nand_readb(TC6393xbState *s, hwaddr addr) { - switch (addr) { - case NAND_DATA + 0: - case NAND_DATA + 1: - case NAND_DATA + 2: - case NAND_DATA + 3: - return nand_getio(s->flash); - case NAND_MODE: - return s->nand.mode; - case NAND_STATUS: - return 0x14; - case NAND_ISR: - return s->nand.isr; - case NAND_IMR: - return s->nand.imr; - } - fprintf(stderr, "tc6393xb_nand: unhandled read at %08x\n", (uint32_t) addr); - return 0; -} -static void tc6393xb_nand_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) { -// fprintf(stderr, "tc6393xb_nand: write at %08x: %02x\n", -// (uint32_t) addr, value & 0xff); - switch (addr) { - case NAND_DATA + 0: - case NAND_DATA + 1: - case NAND_DATA + 2: - case NAND_DATA + 3: - nand_setio(s->flash, value); - s->nand.isr |= 1; - tc6393xb_nand_irq(s); - return; - case NAND_MODE: - s->nand.mode = value; - nand_setpins(s->flash, - value & NAND_MODE_CLE, - value & NAND_MODE_ALE, - !(value & NAND_MODE_CE), - value & NAND_MODE_WP, - 0); // FIXME: gnd - switch (value & NAND_MODE_ECC_MASK) { - case NAND_MODE_ECC_RST: - ecc_reset(&s->ecc); - break; - case NAND_MODE_ECC_READ: - // FIXME - break; - case NAND_MODE_ECC_EN: - ecc_reset(&s->ecc); - } - return; - case NAND_ISR: - s->nand.isr = value; - tc6393xb_nand_irq(s); - return; - case NAND_IMR: - s->nand.imr = value; - tc6393xb_nand_irq(s); - return; - } - fprintf(stderr, "tc6393xb_nand: unhandled write at %08x: %02x\n", - (uint32_t) addr, value & 0xff); -} - -#define BITS 8 -#include "tc6393xb_template.h" -#define BITS 15 -#include "tc6393xb_template.h" -#define BITS 16 -#include "tc6393xb_template.h" -#define BITS 24 -#include "tc6393xb_template.h" -#define BITS 32 -#include "tc6393xb_template.h" - -static void tc6393xb_draw_graphic(TC6393xbState *s, int full_update) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - - switch (surface_bits_per_pixel(surface)) { - case 8: - tc6393xb_draw_graphic8(s); - break; - case 15: - tc6393xb_draw_graphic15(s); - break; - case 16: - tc6393xb_draw_graphic16(s); - break; - case 24: - tc6393xb_draw_graphic24(s); - break; - case 32: - tc6393xb_draw_graphic32(s); - break; - default: - printf("tc6393xb: unknown depth %d\n", - surface_bits_per_pixel(surface)); - return; - } - - dpy_gfx_update(s->con, 0, 0, s->scr_width, s->scr_height); -} - -static void tc6393xb_draw_blank(TC6393xbState *s, int full_update) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int i, w; - uint8_t *d; - - if (!full_update) - return; - - w = s->scr_width * surface_bytes_per_pixel(surface); - d = surface_data(surface); - for(i = 0; i < s->scr_height; i++) { - memset(d, 0, w); - d += surface_stride(surface); - } - - dpy_gfx_update(s->con, 0, 0, s->scr_width, s->scr_height); -} - -static void tc6393xb_update_display(void *opaque) -{ - TC6393xbState *s = opaque; - DisplaySurface *surface = qemu_console_surface(s->con); - int full_update; - - if (s->scr_width == 0 || s->scr_height == 0) - return; - - full_update = 0; - if (s->blanked != s->blank) { - s->blanked = s->blank; - full_update = 1; - } - if (s->scr_width != surface_width(surface) || - s->scr_height != surface_height(surface)) { - qemu_console_resize(s->con, s->scr_width, s->scr_height); - full_update = 1; - } - if (s->blanked) - tc6393xb_draw_blank(s, full_update); - else - tc6393xb_draw_graphic(s, full_update); -} - - -static uint64_t tc6393xb_readb(void *opaque, hwaddr addr, - unsigned size) -{ - TC6393xbState *s = opaque; - - switch (addr >> 8) { - case 0: - return tc6393xb_scr_readb(s, addr & 0xff); - case 1: - return tc6393xb_nand_cfg_readb(s, addr & 0xff); - }; - - if ((addr &~0xff) == s->nand_phys && s->nand_enable) { -// return tc6393xb_nand_readb(s, addr & 0xff); - uint8_t d = tc6393xb_nand_readb(s, addr & 0xff); -// fprintf(stderr, "tc6393xb_nand: read at %08x: %02hhx\n", (uint32_t) addr, d); - return d; - } - -// fprintf(stderr, "tc6393xb: unhandled read at %08x\n", (uint32_t) addr); - return 0; -} - -static void tc6393xb_writeb(void *opaque, hwaddr addr, - uint64_t value, unsigned size) { - TC6393xbState *s = opaque; - - switch (addr >> 8) { - case 0: - tc6393xb_scr_writeb(s, addr & 0xff, value); - return; - case 1: - tc6393xb_nand_cfg_writeb(s, addr & 0xff, value); - return; - }; - - if ((addr &~0xff) == s->nand_phys && s->nand_enable) - tc6393xb_nand_writeb(s, addr & 0xff, value); - else - fprintf(stderr, "tc6393xb: unhandled write at %08x: %02x\n", - (uint32_t) addr, (int)value & 0xff); -} - -static const GraphicHwOps tc6393xb_gfx_ops = { - .gfx_update = tc6393xb_update_display, -}; - -TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq) -{ - TC6393xbState *s; - DriveInfo *nand; - static const MemoryRegionOps tc6393xb_ops = { - .read = tc6393xb_readb, - .write = tc6393xb_writeb, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, - }; - - s = (TC6393xbState *) g_malloc0(sizeof(TC6393xbState)); - s->irq = irq; - s->gpio_in = qemu_allocate_irqs(tc6393xb_gpio_set, s, TC6393XB_GPIOS); - - s->l3v = qemu_allocate_irq(tc6393xb_l3v, s, 0); - s->blanked = 1; - - s->sub_irqs = qemu_allocate_irqs(tc6393xb_sub_irq, s, TC6393XB_NR_IRQS); - - nand = drive_get(IF_MTD, 0, 0); - s->flash = nand_init(nand ? blk_by_legacy_dinfo(nand) : NULL, - NAND_MFR_TOSHIBA, 0x76); - - memory_region_init_io(&s->iomem, NULL, &tc6393xb_ops, s, "tc6393xb", 0x10000); - memory_region_add_subregion(sysmem, base, &s->iomem); - - memory_region_init_ram(&s->vram, NULL, "tc6393xb.vram", 0x100000, - &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); - s->scr_width = 480; - s->scr_height = 640; - s->con = graphic_console_init(NULL, 0, &tc6393xb_gfx_ops, s); - - return s; -} diff --git a/qemu/hw/display/tc6393xb_template.h b/qemu/hw/display/tc6393xb_template.h deleted file mode 100644 index 78629c07f..000000000 --- a/qemu/hw/display/tc6393xb_template.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Toshiba TC6393XB I/O Controller. - * Found in Sharp Zaurus SL-6000 (tosa) or some - * Toshiba e-Series PDAs. - * - * FB support code. Based on G364 fb emulator - * - * Copyright (c) 2007 Hervé Poussineau - * - * 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 . - */ - -#if BITS == 8 -# define SET_PIXEL(addr, color) (*(uint8_t *)addr = color) -#elif BITS == 15 || BITS == 16 -# define SET_PIXEL(addr, color) (*(uint16_t *)addr = color) -#elif BITS == 24 -# define SET_PIXEL(addr, color) \ - do { \ - addr[0] = color; \ - addr[1] = (color) >> 8; \ - addr[2] = (color) >> 16; \ - } while (0) -#elif BITS == 32 -# define SET_PIXEL(addr, color) (*(uint32_t *)addr = color) -#else -# error unknown bit depth -#endif - - -static void glue(tc6393xb_draw_graphic, BITS)(TC6393xbState *s) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int i; - uint16_t *data_buffer; - uint8_t *data_display; - - data_buffer = s->vram_ptr; - data_display = surface_data(surface); - for(i = 0; i < s->scr_height; i++) { -#if (BITS == 16) - memcpy(data_display, data_buffer, s->scr_width * 2); - data_buffer += s->scr_width; - data_display += surface_stride(surface); -#else - int j; - for (j = 0; j < s->scr_width; j++, data_display += BITS / 8, data_buffer++) { - uint16_t color = *data_buffer; - uint32_t dest_color = glue(rgb_to_pixel, BITS)( - ((color & 0xf800) * 0x108) >> 11, - ((color & 0x7e0) * 0x41) >> 9, - ((color & 0x1f) * 0x21) >> 2 - ); - SET_PIXEL(data_display, dest_color); - } -#endif - } -} - -#undef BITS -#undef SET_PIXEL diff --git a/qemu/hw/display/tcx.c b/qemu/hw/display/tcx.c deleted file mode 100644 index 8e26aae80..000000000 --- a/qemu/hw/display/tcx.c +++ /dev/null @@ -1,1106 +0,0 @@ -/* - * QEMU TCX Frame buffer - * - * Copyright (c) 2003-2005 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-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" -#include "hw/sysbus.h" -#include "qemu/error-report.h" - -#define TCX_ROM_FILE "QEMU,tcx.bin" -#define FCODE_MAX_ROM_SIZE 0x10000 - -#define MAXX 1024 -#define MAXY 768 -#define TCX_DAC_NREGS 16 -#define TCX_THC_NREGS 0x1000 -#define TCX_DHC_NREGS 0x4000 -#define TCX_TEC_NREGS 0x1000 -#define TCX_ALT_NREGS 0x8000 -#define TCX_STIP_NREGS 0x800000 -#define TCX_BLIT_NREGS 0x800000 -#define TCX_RSTIP_NREGS 0x800000 -#define TCX_RBLIT_NREGS 0x800000 - -#define TCX_THC_MISC 0x818 -#define TCX_THC_CURSXY 0x8fc -#define TCX_THC_CURSMASK 0x900 -#define TCX_THC_CURSBITS 0x980 - -#define TYPE_TCX "SUNW,tcx" -#define TCX(obj) OBJECT_CHECK(TCXState, (obj), TYPE_TCX) - -typedef struct TCXState { - SysBusDevice parent_obj; - - QemuConsole *con; - qemu_irq irq; - uint8_t *vram; - uint32_t *vram24, *cplane; - hwaddr prom_addr; - MemoryRegion rom; - MemoryRegion vram_mem; - MemoryRegion vram_8bit; - MemoryRegion vram_24bit; - MemoryRegion stip; - MemoryRegion blit; - MemoryRegion vram_cplane; - MemoryRegion rstip; - MemoryRegion rblit; - MemoryRegion tec; - MemoryRegion dac; - MemoryRegion thc; - MemoryRegion dhc; - MemoryRegion alt; - MemoryRegion thc24; - - ram_addr_t vram24_offset, cplane_offset; - uint32_t tmpblit; - uint32_t vram_size; - uint32_t palette[260]; - uint8_t r[260], g[260], b[260]; - uint16_t width, height, depth; - uint8_t dac_index, dac_state; - uint32_t thcmisc; - uint32_t cursmask[32]; - uint32_t cursbits[32]; - uint16_t cursx; - uint16_t cursy; -} TCXState; - -static void tcx_set_dirty(TCXState *s) -{ - memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY); -} - -static inline int tcx24_check_dirty(TCXState *s, ram_addr_t page, - ram_addr_t page24, ram_addr_t cpage) -{ - int ret; - - ret = memory_region_get_dirty(&s->vram_mem, page, TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); - ret |= memory_region_get_dirty(&s->vram_mem, page24, TARGET_PAGE_SIZE * 4, - DIRTY_MEMORY_VGA); - ret |= memory_region_get_dirty(&s->vram_mem, cpage, TARGET_PAGE_SIZE * 4, - DIRTY_MEMORY_VGA); - return ret; -} - -static inline void tcx24_reset_dirty(TCXState *ts, ram_addr_t page_min, - ram_addr_t page_max, ram_addr_t page24, - ram_addr_t cpage) -{ - memory_region_reset_dirty(&ts->vram_mem, - page_min, - (page_max - page_min) + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); - memory_region_reset_dirty(&ts->vram_mem, - page24 + page_min * 4, - (page_max - page_min) * 4 + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); - memory_region_reset_dirty(&ts->vram_mem, - cpage + page_min * 4, - (page_max - page_min) * 4 + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); -} - -static void update_palette_entries(TCXState *s, int start, int end) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int i; - - for (i = start; i < end; i++) { - switch (surface_bits_per_pixel(surface)) { - default: - case 8: - s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]); - break; - case 15: - s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]); - break; - case 16: - s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]); - break; - case 32: - if (is_surface_bgr(surface)) { - s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]); - } else { - s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]); - } - break; - } - } - tcx_set_dirty(s); -} - -static void tcx_draw_line32(TCXState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - int x; - uint8_t val; - uint32_t *p = (uint32_t *)d; - - for (x = 0; x < width; x++) { - val = *s++; - *p++ = s1->palette[val]; - } -} - -static void tcx_draw_line16(TCXState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - int x; - uint8_t val; - uint16_t *p = (uint16_t *)d; - - for (x = 0; x < width; x++) { - val = *s++; - *p++ = s1->palette[val]; - } -} - -static void tcx_draw_line8(TCXState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - int x; - uint8_t val; - - for(x = 0; x < width; x++) { - val = *s++; - *d++ = s1->palette[val]; - } -} - -static void tcx_draw_cursor32(TCXState *s1, uint8_t *d, - int y, int width) -{ - int x, len; - uint32_t mask, bits; - uint32_t *p = (uint32_t *)d; - - y = y - s1->cursy; - mask = s1->cursmask[y]; - bits = s1->cursbits[y]; - len = MIN(width - s1->cursx, 32); - p = &p[s1->cursx]; - for (x = 0; x < len; x++) { - if (mask & 0x80000000) { - if (bits & 0x80000000) { - *p = s1->palette[259]; - } else { - *p = s1->palette[258]; - } - } - p++; - mask <<= 1; - bits <<= 1; - } -} - -static void tcx_draw_cursor16(TCXState *s1, uint8_t *d, - int y, int width) -{ - int x, len; - uint32_t mask, bits; - uint16_t *p = (uint16_t *)d; - - y = y - s1->cursy; - mask = s1->cursmask[y]; - bits = s1->cursbits[y]; - len = MIN(width - s1->cursx, 32); - p = &p[s1->cursx]; - for (x = 0; x < len; x++) { - if (mask & 0x80000000) { - if (bits & 0x80000000) { - *p = s1->palette[259]; - } else { - *p = s1->palette[258]; - } - } - p++; - mask <<= 1; - bits <<= 1; - } -} - -static void tcx_draw_cursor8(TCXState *s1, uint8_t *d, - int y, int width) -{ - int x, len; - uint32_t mask, bits; - - y = y - s1->cursy; - mask = s1->cursmask[y]; - bits = s1->cursbits[y]; - len = MIN(width - s1->cursx, 32); - d = &d[s1->cursx]; - for (x = 0; x < len; x++) { - if (mask & 0x80000000) { - if (bits & 0x80000000) { - *d = s1->palette[259]; - } else { - *d = s1->palette[258]; - } - } - d++; - mask <<= 1; - bits <<= 1; - } -} - -/* - XXX Could be much more optimal: - * detect if line/page/whole screen is in 24 bit mode - * if destination is also BGR, use memcpy - */ -static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d, - const uint8_t *s, int width, - const uint32_t *cplane, - const uint32_t *s24) -{ - DisplaySurface *surface = qemu_console_surface(s1->con); - int x, bgr, r, g, b; - uint8_t val, *p8; - uint32_t *p = (uint32_t *)d; - uint32_t dval; - bgr = is_surface_bgr(surface); - for(x = 0; x < width; x++, s++, s24++) { - if (be32_to_cpu(*cplane) & 0x03000000) { - /* 24-bit direct, BGR order */ - p8 = (uint8_t *)s24; - p8++; - b = *p8++; - g = *p8++; - r = *p8; - if (bgr) - dval = rgb_to_pixel32bgr(r, g, b); - else - dval = rgb_to_pixel32(r, g, b); - } else { - /* 8-bit pseudocolor */ - val = *s; - dval = s1->palette[val]; - } - *p++ = dval; - cplane++; - } -} - -/* Fixed line length 1024 allows us to do nice tricks not possible on - VGA... */ - -static void tcx_update_display(void *opaque) -{ - TCXState *ts = opaque; - DisplaySurface *surface = qemu_console_surface(ts->con); - ram_addr_t page, page_min, page_max; - int y, y_start, dd, ds; - uint8_t *d, *s; - void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width); - void (*fc)(TCXState *s1, uint8_t *dst, int y, int width); - - if (surface_bits_per_pixel(surface) == 0) { - return; - } - - page = 0; - y_start = -1; - page_min = -1; - page_max = 0; - d = surface_data(surface); - s = ts->vram; - dd = surface_stride(surface); - ds = 1024; - - switch (surface_bits_per_pixel(surface)) { - case 32: - f = tcx_draw_line32; - fc = tcx_draw_cursor32; - break; - case 15: - case 16: - f = tcx_draw_line16; - fc = tcx_draw_cursor16; - break; - default: - case 8: - f = tcx_draw_line8; - fc = tcx_draw_cursor8; - break; - case 0: - return; - } - - memory_region_sync_dirty_bitmap(&ts->vram_mem); - for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE) { - if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA)) { - if (y_start < 0) - y_start = y; - if (page < page_min) - page_min = page; - if (page > page_max) - page_max = page; - - f(ts, d, s, ts->width); - if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { - fc(ts, d, y, ts->width); - } - d += dd; - s += ds; - y++; - - f(ts, d, s, ts->width); - if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { - fc(ts, d, y, ts->width); - } - d += dd; - s += ds; - y++; - - f(ts, d, s, ts->width); - if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { - fc(ts, d, y, ts->width); - } - d += dd; - s += ds; - y++; - - f(ts, d, s, ts->width); - if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { - fc(ts, d, y, ts->width); - } - d += dd; - s += ds; - y++; - } else { - if (y_start >= 0) { - /* flush to display */ - dpy_gfx_update(ts->con, 0, y_start, - ts->width, y - y_start); - y_start = -1; - } - d += dd * 4; - s += ds * 4; - y += 4; - } - } - if (y_start >= 0) { - /* flush to display */ - dpy_gfx_update(ts->con, 0, y_start, - ts->width, y - y_start); - } - /* reset modified pages */ - if (page_max >= page_min) { - memory_region_reset_dirty(&ts->vram_mem, - page_min, - (page_max - page_min) + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); - } -} - -static void tcx24_update_display(void *opaque) -{ - TCXState *ts = opaque; - DisplaySurface *surface = qemu_console_surface(ts->con); - ram_addr_t page, page_min, page_max, cpage, page24; - int y, y_start, dd, ds; - uint8_t *d, *s; - uint32_t *cptr, *s24; - - if (surface_bits_per_pixel(surface) != 32) { - return; - } - - page = 0; - page24 = ts->vram24_offset; - cpage = ts->cplane_offset; - y_start = -1; - page_min = -1; - page_max = 0; - d = surface_data(surface); - s = ts->vram; - s24 = ts->vram24; - cptr = ts->cplane; - dd = surface_stride(surface); - ds = 1024; - - memory_region_sync_dirty_bitmap(&ts->vram_mem); - for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE, - page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) { - if (tcx24_check_dirty(ts, page, page24, cpage)) { - if (y_start < 0) - y_start = y; - if (page < page_min) - page_min = page; - if (page > page_max) - page_max = page; - tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); - if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { - tcx_draw_cursor32(ts, d, y, ts->width); - } - d += dd; - s += ds; - cptr += ds; - s24 += ds; - y++; - tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); - if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { - tcx_draw_cursor32(ts, d, y, ts->width); - } - d += dd; - s += ds; - cptr += ds; - s24 += ds; - y++; - tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); - if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { - tcx_draw_cursor32(ts, d, y, ts->width); - } - d += dd; - s += ds; - cptr += ds; - s24 += ds; - y++; - tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); - if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { - tcx_draw_cursor32(ts, d, y, ts->width); - } - d += dd; - s += ds; - cptr += ds; - s24 += ds; - y++; - } else { - if (y_start >= 0) { - /* flush to display */ - dpy_gfx_update(ts->con, 0, y_start, - ts->width, y - y_start); - y_start = -1; - } - d += dd * 4; - s += ds * 4; - cptr += ds * 4; - s24 += ds * 4; - y += 4; - } - } - if (y_start >= 0) { - /* flush to display */ - dpy_gfx_update(ts->con, 0, y_start, - ts->width, y - y_start); - } - /* reset modified pages */ - if (page_max >= page_min) { - tcx24_reset_dirty(ts, page_min, page_max, page24, cpage); - } -} - -static void tcx_invalidate_display(void *opaque) -{ - TCXState *s = opaque; - - tcx_set_dirty(s); - qemu_console_resize(s->con, s->width, s->height); -} - -static void tcx24_invalidate_display(void *opaque) -{ - TCXState *s = opaque; - - tcx_set_dirty(s); - qemu_console_resize(s->con, s->width, s->height); -} - -static int vmstate_tcx_post_load(void *opaque, int version_id) -{ - TCXState *s = opaque; - - update_palette_entries(s, 0, 256); - tcx_set_dirty(s); - return 0; -} - -static const VMStateDescription vmstate_tcx = { - .name ="tcx", - .version_id = 4, - .minimum_version_id = 4, - .post_load = vmstate_tcx_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT16(height, TCXState), - VMSTATE_UINT16(width, TCXState), - VMSTATE_UINT16(depth, TCXState), - VMSTATE_BUFFER(r, TCXState), - VMSTATE_BUFFER(g, TCXState), - VMSTATE_BUFFER(b, TCXState), - VMSTATE_UINT8(dac_index, TCXState), - VMSTATE_UINT8(dac_state, TCXState), - VMSTATE_END_OF_LIST() - } -}; - -static void tcx_reset(DeviceState *d) -{ - TCXState *s = TCX(d); - - /* Initialize palette */ - memset(s->r, 0, 260); - memset(s->g, 0, 260); - memset(s->b, 0, 260); - s->r[255] = s->g[255] = s->b[255] = 255; - s->r[256] = s->g[256] = s->b[256] = 255; - s->r[258] = s->g[258] = s->b[258] = 255; - update_palette_entries(s, 0, 260); - memset(s->vram, 0, MAXX*MAXY); - memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4), - DIRTY_MEMORY_VGA); - s->dac_index = 0; - s->dac_state = 0; - s->cursx = 0xf000; /* Put cursor off screen */ - s->cursy = 0xf000; -} - -static uint64_t tcx_dac_readl(void *opaque, hwaddr addr, - unsigned size) -{ - TCXState *s = opaque; - uint32_t val = 0; - - switch (s->dac_state) { - case 0: - val = s->r[s->dac_index] << 24; - s->dac_state++; - break; - case 1: - val = s->g[s->dac_index] << 24; - s->dac_state++; - break; - case 2: - val = s->b[s->dac_index] << 24; - s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */ - default: - s->dac_state = 0; - break; - } - - return val; -} - -static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - TCXState *s = opaque; - unsigned index; - - switch (addr) { - case 0: /* Address */ - s->dac_index = val >> 24; - s->dac_state = 0; - break; - case 4: /* Pixel colours */ - case 12: /* Overlay (cursor) colours */ - if (addr & 8) { - index = (s->dac_index & 3) + 256; - } else { - index = s->dac_index; - } - switch (s->dac_state) { - case 0: - s->r[index] = val >> 24; - update_palette_entries(s, index, index + 1); - s->dac_state++; - break; - case 1: - s->g[index] = val >> 24; - update_palette_entries(s, index, index + 1); - s->dac_state++; - break; - case 2: - s->b[index] = val >> 24; - update_palette_entries(s, index, index + 1); - s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */ - default: - s->dac_state = 0; - break; - } - break; - default: /* Control registers */ - break; - } -} - -static const MemoryRegionOps tcx_dac_ops = { - .read = tcx_dac_readl, - .write = tcx_dac_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static uint64_t tcx_stip_readl(void *opaque, hwaddr addr, - unsigned size) -{ - return 0; -} - -static void tcx_stip_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - TCXState *s = opaque; - int i; - uint32_t col; - - if (!(addr & 4)) { - s->tmpblit = val; - } else { - addr = (addr >> 3) & 0xfffff; - col = cpu_to_be32(s->tmpblit); - if (s->depth == 24) { - for (i = 0; i < 32; i++) { - if (val & 0x80000000) { - s->vram[addr + i] = s->tmpblit; - s->vram24[addr + i] = col; - } - val <<= 1; - } - } else { - for (i = 0; i < 32; i++) { - if (val & 0x80000000) { - s->vram[addr + i] = s->tmpblit; - } - val <<= 1; - } - } - memory_region_set_dirty(&s->vram_mem, addr, 32); - } -} - -static void tcx_rstip_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - TCXState *s = opaque; - int i; - uint32_t col; - - if (!(addr & 4)) { - s->tmpblit = val; - } else { - addr = (addr >> 3) & 0xfffff; - col = cpu_to_be32(s->tmpblit); - if (s->depth == 24) { - for (i = 0; i < 32; i++) { - if (val & 0x80000000) { - s->vram[addr + i] = s->tmpblit; - s->vram24[addr + i] = col; - s->cplane[addr + i] = col; - } - val <<= 1; - } - } else { - for (i = 0; i < 32; i++) { - if (val & 0x80000000) { - s->vram[addr + i] = s->tmpblit; - } - val <<= 1; - } - } - memory_region_set_dirty(&s->vram_mem, addr, 32); - } -} - -static const MemoryRegionOps tcx_stip_ops = { - .read = tcx_stip_readl, - .write = tcx_stip_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const MemoryRegionOps tcx_rstip_ops = { - .read = tcx_stip_readl, - .write = tcx_rstip_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static uint64_t tcx_blit_readl(void *opaque, hwaddr addr, - unsigned size) -{ - return 0; -} - -static void tcx_blit_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - TCXState *s = opaque; - uint32_t adsr, len; - int i; - - if (!(addr & 4)) { - s->tmpblit = val; - } else { - addr = (addr >> 3) & 0xfffff; - adsr = val & 0xffffff; - len = ((val >> 24) & 0x1f) + 1; - if (adsr == 0xffffff) { - memset(&s->vram[addr], s->tmpblit, len); - if (s->depth == 24) { - val = s->tmpblit & 0xffffff; - val = cpu_to_be32(val); - for (i = 0; i < len; i++) { - s->vram24[addr + i] = val; - } - } - } else { - memcpy(&s->vram[addr], &s->vram[adsr], len); - if (s->depth == 24) { - memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4); - } - } - memory_region_set_dirty(&s->vram_mem, addr, len); - } -} - -static void tcx_rblit_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - TCXState *s = opaque; - uint32_t adsr, len; - int i; - - if (!(addr & 4)) { - s->tmpblit = val; - } else { - addr = (addr >> 3) & 0xfffff; - adsr = val & 0xffffff; - len = ((val >> 24) & 0x1f) + 1; - if (adsr == 0xffffff) { - memset(&s->vram[addr], s->tmpblit, len); - if (s->depth == 24) { - val = s->tmpblit & 0xffffff; - val = cpu_to_be32(val); - for (i = 0; i < len; i++) { - s->vram24[addr + i] = val; - s->cplane[addr + i] = val; - } - } - } else { - memcpy(&s->vram[addr], &s->vram[adsr], len); - if (s->depth == 24) { - memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4); - memcpy(&s->cplane[addr], &s->cplane[adsr], len * 4); - } - } - memory_region_set_dirty(&s->vram_mem, addr, len); - } -} - -static const MemoryRegionOps tcx_blit_ops = { - .read = tcx_blit_readl, - .write = tcx_blit_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const MemoryRegionOps tcx_rblit_ops = { - .read = tcx_blit_readl, - .write = tcx_rblit_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void tcx_invalidate_cursor_position(TCXState *s) -{ - int ymin, ymax, start, end; - - /* invalidate only near the cursor */ - ymin = s->cursy; - if (ymin >= s->height) { - return; - } - ymax = MIN(s->height, ymin + 32); - start = ymin * 1024; - end = ymax * 1024; - - memory_region_set_dirty(&s->vram_mem, start, end-start); -} - -static uint64_t tcx_thc_readl(void *opaque, hwaddr addr, - unsigned size) -{ - TCXState *s = opaque; - uint64_t val; - - if (addr == TCX_THC_MISC) { - val = s->thcmisc | 0x02000000; - } else { - val = 0; - } - return val; -} - -static void tcx_thc_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - TCXState *s = opaque; - - if (addr == TCX_THC_CURSXY) { - tcx_invalidate_cursor_position(s); - s->cursx = val >> 16; - s->cursy = val; - tcx_invalidate_cursor_position(s); - } else if (addr >= TCX_THC_CURSMASK && addr < TCX_THC_CURSMASK + 128) { - s->cursmask[(addr - TCX_THC_CURSMASK) >> 2] = val; - tcx_invalidate_cursor_position(s); - } else if (addr >= TCX_THC_CURSBITS && addr < TCX_THC_CURSBITS + 128) { - s->cursbits[(addr - TCX_THC_CURSBITS) >> 2] = val; - tcx_invalidate_cursor_position(s); - } else if (addr == TCX_THC_MISC) { - s->thcmisc = val; - } - -} - -static const MemoryRegionOps tcx_thc_ops = { - .read = tcx_thc_readl, - .write = tcx_thc_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static uint64_t tcx_dummy_readl(void *opaque, hwaddr addr, - unsigned size) -{ - return 0; -} - -static void tcx_dummy_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - return; -} - -static const MemoryRegionOps tcx_dummy_ops = { - .read = tcx_dummy_readl, - .write = tcx_dummy_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const GraphicHwOps tcx_ops = { - .invalidate = tcx_invalidate_display, - .gfx_update = tcx_update_display, -}; - -static const GraphicHwOps tcx24_ops = { - .invalidate = tcx24_invalidate_display, - .gfx_update = tcx24_update_display, -}; - -static void tcx_initfn(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - TCXState *s = TCX(obj); - - 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, 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, 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, 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, obj, &tcx_rblit_ops, s, "tcx.rblit", - TCX_RBLIT_NREGS); - sysbus_init_mmio(sbd, &s->rblit); - - /* 7/TEC : ??? */ - 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, 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, obj, &tcx_thc_ops, s, "tcx.thc", - TCX_THC_NREGS); - sysbus_init_mmio(sbd, &s->thc); - - /* 11/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, obj, &tcx_dummy_ops, s, "tcx.alt", - TCX_ALT_NREGS); - sysbus_init_mmio(sbd, &s->alt); -} - -static void tcx_realizefn(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - TCXState *s = TCX(dev); - ram_addr_t vram_offset = 0; - int size, ret; - uint8_t *vram_base; - char *fcode_filename; - - memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram", - 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); - - /* 10/ROM : FCode ROM */ - vmstate_register_ram_global(&s->rom); - fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE); - if (fcode_filename) { - ret = load_image_targphys(fcode_filename, s->prom_addr, - FCODE_MAX_ROM_SIZE); - g_free(fcode_filename); - if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { - error_report("tcx: could not load prom '%s'", TCX_ROM_FILE); - } - } - - /* 0/DFB8 : 8-bit plane */ - s->vram = vram_base; - size = s->vram_size; - memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit", - &s->vram_mem, vram_offset, size); - sysbus_init_mmio(sbd, &s->vram_8bit); - vram_offset += size; - vram_base += size; - - /* 1/DFB24 : 24bit plane */ - size = s->vram_size * 4; - s->vram24 = (uint32_t *)vram_base; - s->vram24_offset = vram_offset; - memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit", - &s->vram_mem, vram_offset, size); - sysbus_init_mmio(sbd, &s->vram_24bit); - vram_offset += size; - vram_base += size; - - /* 4/RDFB32 : Raw Framebuffer */ - size = s->vram_size * 4; - s->cplane = (uint32_t *)vram_base; - s->cplane_offset = vram_offset; - memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane", - &s->vram_mem, vram_offset, size); - sysbus_init_mmio(sbd, &s->vram_cplane); - - /* 9/THC24bits : NetBSD writes here even with 8-bit display: dummy */ - if (s->depth == 8) { - memory_region_init_io(&s->thc24, OBJECT(s), &tcx_dummy_ops, s, - "tcx.thc24", TCX_THC_NREGS); - sysbus_init_mmio(sbd, &s->thc24); - } - - sysbus_init_irq(sbd, &s->irq); - - if (s->depth == 8) { - s->con = graphic_console_init(DEVICE(dev), 0, &tcx_ops, s); - } else { - s->con = graphic_console_init(DEVICE(dev), 0, &tcx24_ops, s); - } - s->thcmisc = 0; - - qemu_console_resize(s->con, s->width, s->height); -} - -static Property tcx_properties[] = { - DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1), - DEFINE_PROP_UINT16("width", TCXState, width, -1), - DEFINE_PROP_UINT16("height", TCXState, height, -1), - DEFINE_PROP_UINT16("depth", TCXState, depth, -1), - DEFINE_PROP_UINT64("prom_addr", TCXState, prom_addr, -1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void tcx_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = tcx_realizefn; - dc->reset = tcx_reset; - dc->vmsd = &vmstate_tcx; - dc->props = tcx_properties; -} - -static const TypeInfo tcx_info = { - .name = TYPE_TCX, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(TCXState), - .instance_init = tcx_initfn, - .class_init = tcx_class_init, -}; - -static void tcx_register_types(void) -{ - type_register_static(&tcx_info); -} - -type_init(tcx_register_types) diff --git a/qemu/hw/display/vga-helpers.h b/qemu/hw/display/vga-helpers.h deleted file mode 100644 index 94f6de204..000000000 --- a/qemu/hw/display/vga-helpers.h +++ /dev/null @@ -1,439 +0,0 @@ -/* - * QEMU VGA Emulator templates - * - * Copyright (c) 2003 Fabrice Bellard - * - * 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. - */ - -static inline void vga_draw_glyph_line(uint8_t *d, uint32_t font_data, - uint32_t xorcol, uint32_t bgcol) -{ - ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol; - ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol; - ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol; - ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol; - ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol; - ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol; - ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol; - ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol; -} - -static void vga_draw_glyph8(uint8_t *d, int linesize, - const uint8_t *font_ptr, int h, - uint32_t fgcol, uint32_t bgcol) -{ - uint32_t font_data, xorcol; - - xorcol = bgcol ^ fgcol; - do { - font_data = font_ptr[0]; - vga_draw_glyph_line(d, font_data, xorcol, bgcol); - font_ptr += 4; - d += linesize; - } while (--h); -} - -static void vga_draw_glyph16(uint8_t *d, int linesize, - const uint8_t *font_ptr, int h, - uint32_t fgcol, uint32_t bgcol) -{ - uint32_t font_data, xorcol; - - xorcol = bgcol ^ fgcol; - do { - font_data = font_ptr[0]; - vga_draw_glyph_line(d, expand4to8[font_data >> 4], - xorcol, bgcol); - vga_draw_glyph_line(d + 32, expand4to8[font_data & 0x0f], - xorcol, bgcol); - font_ptr += 4; - d += linesize; - } while (--h); -} - -static void vga_draw_glyph9(uint8_t *d, int linesize, - const uint8_t *font_ptr, int h, - uint32_t fgcol, uint32_t bgcol, int dup9) -{ - uint32_t font_data, xorcol, v; - - xorcol = bgcol ^ fgcol; - do { - font_data = font_ptr[0]; - ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol; - ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol; - ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol; - ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol; - ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol; - ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol; - ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol; - v = (-((font_data >> 0) & 1) & xorcol) ^ bgcol; - ((uint32_t *)d)[7] = v; - if (dup9) - ((uint32_t *)d)[8] = v; - else - ((uint32_t *)d)[8] = bgcol; - font_ptr += 4; - d += linesize; - } while (--h); -} - -/* - * 4 color mode - */ -static void vga_draw_line2(VGACommonState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - uint32_t plane_mask, *palette, data, v; - int x; - - palette = s1->last_palette; - plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; - width >>= 3; - for(x = 0; x < width; x++) { - data = ((uint32_t *)s)[0]; - data &= plane_mask; - v = expand2[GET_PLANE(data, 0)]; - v |= expand2[GET_PLANE(data, 2)] << 2; - ((uint32_t *)d)[0] = palette[v >> 12]; - ((uint32_t *)d)[1] = palette[(v >> 8) & 0xf]; - ((uint32_t *)d)[2] = palette[(v >> 4) & 0xf]; - ((uint32_t *)d)[3] = palette[(v >> 0) & 0xf]; - - v = expand2[GET_PLANE(data, 1)]; - v |= expand2[GET_PLANE(data, 3)] << 2; - ((uint32_t *)d)[4] = palette[v >> 12]; - ((uint32_t *)d)[5] = palette[(v >> 8) & 0xf]; - ((uint32_t *)d)[6] = palette[(v >> 4) & 0xf]; - ((uint32_t *)d)[7] = palette[(v >> 0) & 0xf]; - d += 32; - s += 4; - } -} - -#define PUT_PIXEL2(d, n, v) \ -((uint32_t *)d)[2*(n)] = ((uint32_t *)d)[2*(n)+1] = (v) - -/* - * 4 color mode, dup2 horizontal - */ -static void vga_draw_line2d2(VGACommonState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - uint32_t plane_mask, *palette, data, v; - int x; - - palette = s1->last_palette; - plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; - width >>= 3; - for(x = 0; x < width; x++) { - data = ((uint32_t *)s)[0]; - data &= plane_mask; - v = expand2[GET_PLANE(data, 0)]; - v |= expand2[GET_PLANE(data, 2)] << 2; - PUT_PIXEL2(d, 0, palette[v >> 12]); - PUT_PIXEL2(d, 1, palette[(v >> 8) & 0xf]); - PUT_PIXEL2(d, 2, palette[(v >> 4) & 0xf]); - PUT_PIXEL2(d, 3, palette[(v >> 0) & 0xf]); - - v = expand2[GET_PLANE(data, 1)]; - v |= expand2[GET_PLANE(data, 3)] << 2; - PUT_PIXEL2(d, 4, palette[v >> 12]); - PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]); - PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]); - PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]); - d += 64; - s += 4; - } -} - -/* - * 16 color mode - */ -static void vga_draw_line4(VGACommonState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - uint32_t plane_mask, data, v, *palette; - int x; - - palette = s1->last_palette; - plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; - width >>= 3; - for(x = 0; x < width; x++) { - data = ((uint32_t *)s)[0]; - data &= plane_mask; - v = expand4[GET_PLANE(data, 0)]; - v |= expand4[GET_PLANE(data, 1)] << 1; - v |= expand4[GET_PLANE(data, 2)] << 2; - v |= expand4[GET_PLANE(data, 3)] << 3; - ((uint32_t *)d)[0] = palette[v >> 28]; - ((uint32_t *)d)[1] = palette[(v >> 24) & 0xf]; - ((uint32_t *)d)[2] = palette[(v >> 20) & 0xf]; - ((uint32_t *)d)[3] = palette[(v >> 16) & 0xf]; - ((uint32_t *)d)[4] = palette[(v >> 12) & 0xf]; - ((uint32_t *)d)[5] = palette[(v >> 8) & 0xf]; - ((uint32_t *)d)[6] = palette[(v >> 4) & 0xf]; - ((uint32_t *)d)[7] = palette[(v >> 0) & 0xf]; - d += 32; - s += 4; - } -} - -/* - * 16 color mode, dup2 horizontal - */ -static void vga_draw_line4d2(VGACommonState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - uint32_t plane_mask, data, v, *palette; - int x; - - palette = s1->last_palette; - plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; - width >>= 3; - for(x = 0; x < width; x++) { - data = ((uint32_t *)s)[0]; - data &= plane_mask; - v = expand4[GET_PLANE(data, 0)]; - v |= expand4[GET_PLANE(data, 1)] << 1; - v |= expand4[GET_PLANE(data, 2)] << 2; - v |= expand4[GET_PLANE(data, 3)] << 3; - PUT_PIXEL2(d, 0, palette[v >> 28]); - PUT_PIXEL2(d, 1, palette[(v >> 24) & 0xf]); - PUT_PIXEL2(d, 2, palette[(v >> 20) & 0xf]); - PUT_PIXEL2(d, 3, palette[(v >> 16) & 0xf]); - PUT_PIXEL2(d, 4, palette[(v >> 12) & 0xf]); - PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]); - PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]); - PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]); - d += 64; - s += 4; - } -} - -/* - * 256 color mode, double pixels - * - * XXX: add plane_mask support (never used in standard VGA modes) - */ -static void vga_draw_line8d2(VGACommonState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - uint32_t *palette; - int x; - - palette = s1->last_palette; - width >>= 3; - for(x = 0; x < width; x++) { - PUT_PIXEL2(d, 0, palette[s[0]]); - PUT_PIXEL2(d, 1, palette[s[1]]); - PUT_PIXEL2(d, 2, palette[s[2]]); - PUT_PIXEL2(d, 3, palette[s[3]]); - d += 32; - s += 4; - } -} - -/* - * standard 256 color mode - * - * XXX: add plane_mask support (never used in standard VGA modes) - */ -static void vga_draw_line8(VGACommonState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - uint32_t *palette; - int x; - - palette = s1->last_palette; - width >>= 3; - for(x = 0; x < width; x++) { - ((uint32_t *)d)[0] = palette[s[0]]; - ((uint32_t *)d)[1] = palette[s[1]]; - ((uint32_t *)d)[2] = palette[s[2]]; - ((uint32_t *)d)[3] = palette[s[3]]; - ((uint32_t *)d)[4] = palette[s[4]]; - ((uint32_t *)d)[5] = palette[s[5]]; - ((uint32_t *)d)[6] = palette[s[6]]; - ((uint32_t *)d)[7] = palette[s[7]]; - d += 32; - s += 8; - } -} - -/* - * 15 bit color - */ -static void vga_draw_line15_le(VGACommonState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - int w; - uint32_t v, r, g, b; - - w = width; - do { - v = lduw_le_p((void *)s); - r = (v >> 7) & 0xf8; - g = (v >> 2) & 0xf8; - b = (v << 3) & 0xf8; - ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b); - s += 2; - d += 4; - } while (--w != 0); -} - -static void vga_draw_line15_be(VGACommonState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - int w; - uint32_t v, r, g, b; - - w = width; - do { - v = lduw_be_p((void *)s); - r = (v >> 7) & 0xf8; - g = (v >> 2) & 0xf8; - b = (v << 3) & 0xf8; - ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b); - s += 2; - d += 4; - } while (--w != 0); -} - -/* - * 16 bit color - */ -static void vga_draw_line16_le(VGACommonState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - int w; - uint32_t v, r, g, b; - - w = width; - do { - v = lduw_le_p((void *)s); - r = (v >> 8) & 0xf8; - g = (v >> 3) & 0xfc; - b = (v << 3) & 0xf8; - ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b); - s += 2; - d += 4; - } while (--w != 0); -} - -static void vga_draw_line16_be(VGACommonState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - int w; - uint32_t v, r, g, b; - - w = width; - do { - v = lduw_be_p((void *)s); - r = (v >> 8) & 0xf8; - g = (v >> 3) & 0xfc; - b = (v << 3) & 0xf8; - ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b); - s += 2; - d += 4; - } while (--w != 0); -} - -/* - * 24 bit color - */ -static void vga_draw_line24_le(VGACommonState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - int w; - uint32_t r, g, b; - - w = width; - do { - b = s[0]; - g = s[1]; - r = s[2]; - ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b); - s += 3; - d += 4; - } while (--w != 0); -} - -static void vga_draw_line24_be(VGACommonState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - int w; - uint32_t r, g, b; - - w = width; - do { - r = s[0]; - g = s[1]; - b = s[2]; - ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b); - s += 3; - d += 4; - } while (--w != 0); -} - -/* - * 32 bit color - */ -static void vga_draw_line32_le(VGACommonState *s1, uint8_t *d, - const uint8_t *s, int width) -{ -#ifndef HOST_WORDS_BIGENDIAN - memcpy(d, s, width * 4); -#else - int w; - uint32_t r, g, b; - - w = width; - do { - b = s[0]; - g = s[1]; - r = s[2]; - ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b); - s += 4; - d += 4; - } while (--w != 0); -#endif -} - -static void vga_draw_line32_be(VGACommonState *s1, uint8_t *d, - const uint8_t *s, int width) -{ -#ifdef HOST_WORDS_BIGENDIAN - memcpy(d, s, width * 4); -#else - int w; - uint32_t r, g, b; - - w = width; - do { - r = s[1]; - g = s[2]; - b = s[3]; - ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b); - s += 4; - d += 4; - } while (--w != 0); -#endif -} diff --git a/qemu/hw/display/vga-isa-mm.c b/qemu/hw/display/vga-isa-mm.c deleted file mode 100644 index 51ccbccc4..000000000 --- a/qemu/hw/display/vga-isa-mm.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * QEMU ISA MM VGA Emulator. - * - * Copyright (c) 2003 Fabrice Bellard - * - * 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 "ui/console.h" -#include "hw/i386/pc.h" -#include "vga_int.h" -#include "ui/pixel_ops.h" -#include "qemu/timer.h" - -#define VGA_RAM_SIZE (8192 * 1024) - -typedef struct ISAVGAMMState { - VGACommonState vga; - int it_shift; -} ISAVGAMMState; - -/* Memory mapped interface */ -static uint32_t vga_mm_readb (void *opaque, hwaddr addr) -{ - ISAVGAMMState *s = opaque; - - return vga_ioport_read(&s->vga, addr >> s->it_shift) & 0xff; -} - -static void vga_mm_writeb (void *opaque, - hwaddr addr, uint32_t value) -{ - ISAVGAMMState *s = opaque; - - vga_ioport_write(&s->vga, addr >> s->it_shift, value & 0xff); -} - -static uint32_t vga_mm_readw (void *opaque, hwaddr addr) -{ - ISAVGAMMState *s = opaque; - - return vga_ioport_read(&s->vga, addr >> s->it_shift) & 0xffff; -} - -static void vga_mm_writew (void *opaque, - hwaddr addr, uint32_t value) -{ - ISAVGAMMState *s = opaque; - - vga_ioport_write(&s->vga, addr >> s->it_shift, value & 0xffff); -} - -static uint32_t vga_mm_readl (void *opaque, hwaddr addr) -{ - ISAVGAMMState *s = opaque; - - return vga_ioport_read(&s->vga, addr >> s->it_shift); -} - -static void vga_mm_writel (void *opaque, - hwaddr addr, uint32_t value) -{ - ISAVGAMMState *s = opaque; - - vga_ioport_write(&s->vga, addr >> s->it_shift, value); -} - -static const MemoryRegionOps vga_mm_ctrl_ops = { - .old_mmio = { - .read = { - vga_mm_readb, - vga_mm_readw, - vga_mm_readl, - }, - .write = { - vga_mm_writeb, - vga_mm_writew, - vga_mm_writel, - }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void vga_mm_init(ISAVGAMMState *s, hwaddr vram_base, - hwaddr ctrl_base, int it_shift, - MemoryRegion *address_space) -{ - MemoryRegion *s_ioport_ctrl, *vga_io_memory; - - s->it_shift = it_shift; - s_ioport_ctrl = g_malloc(sizeof(*s_ioport_ctrl)); - memory_region_init_io(s_ioport_ctrl, NULL, &vga_mm_ctrl_ops, s, - "vga-mm-ctrl", 0x100000); - memory_region_set_flush_coalesced(s_ioport_ctrl); - - vga_io_memory = g_malloc(sizeof(*vga_io_memory)); - /* XXX: endianness? */ - memory_region_init_io(vga_io_memory, NULL, &vga_mem_ops, &s->vga, - "vga-mem", 0x20000); - - vmstate_register(NULL, 0, &vmstate_vga_common, s); - - memory_region_add_subregion(address_space, ctrl_base, s_ioport_ctrl); - s->vga.bank_offset = 0; - memory_region_add_subregion(address_space, - vram_base + 0x000a0000, vga_io_memory); - memory_region_set_coalescing(vga_io_memory); -} - -int isa_vga_mm_init(hwaddr vram_base, - hwaddr ctrl_base, int it_shift, - MemoryRegion *address_space) -{ - ISAVGAMMState *s; - - s = g_malloc0(sizeof(*s)); - - s->vga.vram_size_mb = VGA_RAM_SIZE >> 20; - vga_common_init(&s->vga, NULL, true); - vga_mm_init(s, vram_base, ctrl_base, it_shift, address_space); - - s->vga.con = graphic_console_init(NULL, 0, s->vga.hw_ops, s); - - vga_init_vbe(&s->vga, NULL, address_space); - return 0; -} diff --git a/qemu/hw/display/vga-isa.c b/qemu/hw/display/vga-isa.c deleted file mode 100644 index f5aff1cbe..000000000 --- a/qemu/hw/display/vga-isa.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * QEMU ISA VGA Emulator. - * - * see docs/specs/standard-vga.txt for virtual hardware specs. - * - * Copyright (c) 2003 Fabrice Bellard - * - * 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 "ui/console.h" -#include "hw/i386/pc.h" -#include "vga_int.h" -#include "ui/pixel_ops.h" -#include "qemu/timer.h" -#include "hw/loader.h" - -#define TYPE_ISA_VGA "isa-vga" -#define ISA_VGA(obj) OBJECT_CHECK(ISAVGAState, (obj), TYPE_ISA_VGA) - -typedef struct ISAVGAState { - ISADevice parent_obj; - - struct VGACommonState state; -} ISAVGAState; - -static void vga_isa_reset(DeviceState *dev) -{ - ISAVGAState *d = ISA_VGA(dev); - VGACommonState *s = &d->state; - - vga_common_reset(s); -} - -static void vga_isa_realizefn(DeviceState *dev, Error **errp) -{ - ISADevice *isadev = ISA_DEVICE(dev); - ISAVGAState *d = ISA_VGA(dev); - VGACommonState *s = &d->state; - MemoryRegion *vga_io_memory; - const MemoryRegionPortio *vga_ports, *vbe_ports; - - vga_common_init(s, OBJECT(dev), true); - s->legacy_address_space = isa_address_space(isadev); - vga_io_memory = vga_init_io(s, OBJECT(dev), &vga_ports, &vbe_ports); - isa_register_portio_list(isadev, 0x3b0, vga_ports, s, "vga"); - if (vbe_ports) { - isa_register_portio_list(isadev, 0x1ce, vbe_ports, s, "vbe"); - } - memory_region_add_subregion_overlap(isa_address_space(isadev), - 0x000a0000, - vga_io_memory, 1); - memory_region_set_coalescing(vga_io_memory); - s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s); - - vga_init_vbe(s, OBJECT(dev), isa_address_space(isadev)); - /* ROM BIOS */ - rom_add_vga(VGABIOS_FILENAME); -} - -static Property vga_isa_properties[] = { - DEFINE_PROP_UINT32("vgamem_mb", ISAVGAState, state.vram_size_mb, 8), - DEFINE_PROP_END_OF_LIST(), -}; - -static void vga_isa_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = vga_isa_realizefn; - dc->reset = vga_isa_reset; - dc->vmsd = &vmstate_vga_common; - dc->props = vga_isa_properties; - set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); -} - -static const TypeInfo vga_isa_info = { - .name = TYPE_ISA_VGA, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(ISAVGAState), - .class_init = vga_isa_class_initfn, -}; - -static void vga_isa_register_types(void) -{ - type_register_static(&vga_isa_info); -} - -type_init(vga_isa_register_types) diff --git a/qemu/hw/display/vga-pci.c b/qemu/hw/display/vga-pci.c deleted file mode 100644 index ac9a76499..000000000 --- a/qemu/hw/display/vga-pci.c +++ /dev/null @@ -1,387 +0,0 @@ -/* - * QEMU PCI VGA Emulator. - * - * see docs/specs/standard-vga.txt for virtual hardware specs. - * - * Copyright (c) 2003 Fabrice Bellard - * - * 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 "ui/console.h" -#include "hw/pci/pci.h" -#include "vga_int.h" -#include "ui/pixel_ops.h" -#include "qemu/timer.h" -#include "hw/loader.h" - -#define PCI_VGA_IOPORT_OFFSET 0x400 -#define PCI_VGA_IOPORT_SIZE (0x3e0 - 0x3c0) -#define PCI_VGA_BOCHS_OFFSET 0x500 -#define PCI_VGA_BOCHS_SIZE (0x0b * 2) -#define PCI_VGA_QEXT_OFFSET 0x600 -#define PCI_VGA_QEXT_SIZE (2 * 4) -#define PCI_VGA_MMIO_SIZE 0x1000 - -#define PCI_VGA_QEXT_REG_SIZE (0 * 4) -#define PCI_VGA_QEXT_REG_BYTEORDER (1 * 4) -#define PCI_VGA_QEXT_LITTLE_ENDIAN 0x1e1e1e1e -#define PCI_VGA_QEXT_BIG_ENDIAN 0xbebebebe - -enum vga_pci_flags { - PCI_VGA_FLAG_ENABLE_MMIO = 1, - PCI_VGA_FLAG_ENABLE_QEXT = 2, -}; - -typedef struct PCIVGAState { - PCIDevice dev; - VGACommonState vga; - uint32_t flags; - MemoryRegion mmio; - MemoryRegion mrs[3]; -} PCIVGAState; - -#define TYPE_PCI_VGA "pci-vga" -#define PCI_VGA(obj) OBJECT_CHECK(PCIVGAState, (obj), TYPE_PCI_VGA) - -static const VMStateDescription vmstate_vga_pci = { - .name = "vga", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, PCIVGAState), - VMSTATE_STRUCT(vga, PCIVGAState, 0, vmstate_vga_common, VGACommonState), - VMSTATE_END_OF_LIST() - } -}; - -static uint64_t pci_vga_ioport_read(void *ptr, hwaddr addr, - unsigned size) -{ - VGACommonState *s = ptr; - uint64_t ret = 0; - - switch (size) { - case 1: - ret = vga_ioport_read(s, addr + 0x3c0); - break; - case 2: - ret = vga_ioport_read(s, addr + 0x3c0); - ret |= vga_ioport_read(s, addr + 0x3c1) << 8; - break; - } - return ret; -} - -static void pci_vga_ioport_write(void *ptr, hwaddr addr, - uint64_t val, unsigned size) -{ - VGACommonState *s = ptr; - - switch (size) { - case 1: - vga_ioport_write(s, addr + 0x3c0, val); - break; - case 2: - /* - * Update bytes in little endian order. Allows to update - * indexed registers with a single word write because the - * index byte is updated first. - */ - vga_ioport_write(s, addr + 0x3c0, val & 0xff); - vga_ioport_write(s, addr + 0x3c1, (val >> 8) & 0xff); - break; - } -} - -static const MemoryRegionOps pci_vga_ioport_ops = { - .read = pci_vga_ioport_read, - .write = pci_vga_ioport_write, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .impl.min_access_size = 1, - .impl.max_access_size = 2, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static uint64_t pci_vga_bochs_read(void *ptr, hwaddr addr, - unsigned size) -{ - VGACommonState *s = ptr; - int index = addr >> 1; - - vbe_ioport_write_index(s, 0, index); - return vbe_ioport_read_data(s, 0); -} - -static void pci_vga_bochs_write(void *ptr, hwaddr addr, - uint64_t val, unsigned size) -{ - VGACommonState *s = ptr; - int index = addr >> 1; - - vbe_ioport_write_index(s, 0, index); - vbe_ioport_write_data(s, 0, val); -} - -static const MemoryRegionOps pci_vga_bochs_ops = { - .read = pci_vga_bochs_read, - .write = pci_vga_bochs_write, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .impl.min_access_size = 2, - .impl.max_access_size = 2, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static uint64_t pci_vga_qext_read(void *ptr, hwaddr addr, unsigned size) -{ - VGACommonState *s = ptr; - - switch (addr) { - case PCI_VGA_QEXT_REG_SIZE: - return PCI_VGA_QEXT_SIZE; - case PCI_VGA_QEXT_REG_BYTEORDER: - return s->big_endian_fb ? - PCI_VGA_QEXT_BIG_ENDIAN : PCI_VGA_QEXT_LITTLE_ENDIAN; - default: - return 0; - } -} - -static void pci_vga_qext_write(void *ptr, hwaddr addr, - uint64_t val, unsigned size) -{ - VGACommonState *s = ptr; - - switch (addr) { - case PCI_VGA_QEXT_REG_BYTEORDER: - if (val == PCI_VGA_QEXT_BIG_ENDIAN) { - s->big_endian_fb = true; - } - if (val == PCI_VGA_QEXT_LITTLE_ENDIAN) { - s->big_endian_fb = false; - } - break; - } -} - -static bool vga_get_big_endian_fb(Object *obj, Error **errp) -{ - PCIVGAState *d = PCI_VGA(PCI_DEVICE(obj)); - - return d->vga.big_endian_fb; -} - -static void vga_set_big_endian_fb(Object *obj, bool value, Error **errp) -{ - PCIVGAState *d = PCI_VGA(PCI_DEVICE(obj)); - - d->vga.big_endian_fb = value; -} - -static const MemoryRegionOps pci_vga_qext_ops = { - .read = pci_vga_qext_read, - .write = pci_vga_qext_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -void pci_std_vga_mmio_region_init(VGACommonState *s, - MemoryRegion *parent, - MemoryRegion *subs, - bool qext) -{ - memory_region_init_io(&subs[0], NULL, &pci_vga_ioport_ops, s, - "vga ioports remapped", PCI_VGA_IOPORT_SIZE); - memory_region_add_subregion(parent, PCI_VGA_IOPORT_OFFSET, - &subs[0]); - - memory_region_init_io(&subs[1], NULL, &pci_vga_bochs_ops, s, - "bochs dispi interface", PCI_VGA_BOCHS_SIZE); - memory_region_add_subregion(parent, PCI_VGA_BOCHS_OFFSET, - &subs[1]); - - if (qext) { - memory_region_init_io(&subs[2], NULL, &pci_vga_qext_ops, s, - "qemu extended regs", PCI_VGA_QEXT_SIZE); - memory_region_add_subregion(parent, PCI_VGA_QEXT_OFFSET, - &subs[2]); - } -} - -static void pci_std_vga_realize(PCIDevice *dev, Error **errp) -{ - PCIVGAState *d = PCI_VGA(dev); - VGACommonState *s = &d->vga; - bool qext = false; - - /* vga + console init */ - vga_common_init(s, OBJECT(dev), true); - vga_init(s, OBJECT(dev), pci_address_space(dev), pci_address_space_io(dev), - true); - - s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s); - - /* XXX: VGA_RAM_SIZE must be a power of two */ - pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); - - /* mmio bar for vga register access */ - if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_MMIO)) { - memory_region_init(&d->mmio, NULL, "vga.mmio", 4096); - - if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) { - qext = true; - pci_set_byte(&d->dev.config[PCI_REVISION_ID], 2); - } - pci_std_vga_mmio_region_init(s, &d->mmio, d->mrs, qext); - - pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); - } - - if (!dev->rom_bar) { - /* compatibility with pc-0.13 and older */ - vga_init_vbe(s, OBJECT(dev), pci_address_space(dev)); - } -} - -static void pci_std_vga_init(Object *obj) -{ - /* Expose framebuffer byteorder via QOM */ - object_property_add_bool(obj, "big-endian-framebuffer", - vga_get_big_endian_fb, vga_set_big_endian_fb, NULL); -} - -static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp) -{ - PCIVGAState *d = PCI_VGA(dev); - VGACommonState *s = &d->vga; - bool qext = false; - - /* vga + console init */ - vga_common_init(s, OBJECT(dev), false); - s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s); - - /* mmio bar */ - memory_region_init(&d->mmio, OBJECT(dev), "vga.mmio", 4096); - - if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) { - qext = true; - pci_set_byte(&d->dev.config[PCI_REVISION_ID], 2); - } - pci_std_vga_mmio_region_init(s, &d->mmio, d->mrs, qext); - - pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); - pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); -} - -static void pci_secondary_vga_init(Object *obj) -{ - /* Expose framebuffer byteorder via QOM */ - object_property_add_bool(obj, "big-endian-framebuffer", - vga_get_big_endian_fb, vga_set_big_endian_fb, NULL); -} - -static void pci_secondary_vga_reset(DeviceState *dev) -{ - PCIVGAState *d = PCI_VGA(PCI_DEVICE(dev)); - vga_common_reset(&d->vga); -} - -static Property vga_pci_properties[] = { - DEFINE_PROP_UINT32("vgamem_mb", PCIVGAState, vga.vram_size_mb, 16), - DEFINE_PROP_BIT("mmio", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_MMIO, true), - DEFINE_PROP_BIT("qemu-extended-regs", - PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_QEXT, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static Property secondary_pci_properties[] = { - DEFINE_PROP_UINT32("vgamem_mb", PCIVGAState, vga.vram_size_mb, 16), - DEFINE_PROP_BIT("qemu-extended-regs", - PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_QEXT, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void vga_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->vendor_id = PCI_VENDOR_ID_QEMU; - k->device_id = PCI_DEVICE_ID_QEMU_VGA; - dc->vmsd = &vmstate_vga_pci; - set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); -} - -static const TypeInfo vga_pci_type_info = { - .name = TYPE_PCI_VGA, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIVGAState), - .abstract = true, - .class_init = vga_pci_class_init, -}; - -static void vga_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = pci_std_vga_realize; - k->romfile = "vgabios-stdvga.bin"; - k->class_id = PCI_CLASS_DISPLAY_VGA; - dc->props = vga_pci_properties; - dc->hotpluggable = false; -} - -static void secondary_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = pci_secondary_vga_realize; - k->class_id = PCI_CLASS_DISPLAY_OTHER; - dc->props = secondary_pci_properties; - dc->reset = pci_secondary_vga_reset; -} - -static const TypeInfo vga_info = { - .name = "VGA", - .parent = TYPE_PCI_VGA, - .instance_init = pci_std_vga_init, - .class_init = vga_class_init, -}; - -static const TypeInfo secondary_info = { - .name = "secondary-vga", - .parent = TYPE_PCI_VGA, - .instance_init = pci_secondary_vga_init, - .class_init = secondary_class_init, -}; - -static void vga_register_types(void) -{ - type_register_static(&vga_pci_type_info); - type_register_static(&vga_info); - type_register_static(&secondary_info); -} - -type_init(vga_register_types) diff --git a/qemu/hw/display/vga.c b/qemu/hw/display/vga.c deleted file mode 100644 index 4a55ec6db..000000000 --- a/qemu/hw/display/vga.c +++ /dev/null @@ -1,2289 +0,0 @@ -/* - * QEMU VGA Emulator. - * - * Copyright (c) 2003 Fabrice Bellard - * - * 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 "vga.h" -#include "ui/console.h" -#include "hw/i386/pc.h" -#include "hw/pci/pci.h" -#include "vga_int.h" -#include "ui/pixel_ops.h" -#include "qemu/timer.h" -#include "hw/xen/xen.h" -#include "trace.h" - -//#define DEBUG_VGA -//#define DEBUG_VGA_MEM -//#define DEBUG_VGA_REG - -//#define DEBUG_BOCHS_VBE - -/* 16 state changes per vertical frame @60 Hz */ -#define VGA_TEXT_CURSOR_PERIOD_MS (1000 * 2 * 16 / 60) - -/* - * Video Graphics Array (VGA) - * - * Chipset docs for original IBM VGA: - * http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf - * - * FreeVGA site: - * http://www.osdever.net/FreeVGA/home.htm - * - * Standard VGA features and Bochs VBE extensions are implemented. - */ - -/* force some bits to zero */ -const uint8_t sr_mask[8] = { - 0x03, - 0x3d, - 0x0f, - 0x3f, - 0x0e, - 0x00, - 0x00, - 0xff, -}; - -const uint8_t gr_mask[16] = { - 0x0f, /* 0x00 */ - 0x0f, /* 0x01 */ - 0x0f, /* 0x02 */ - 0x1f, /* 0x03 */ - 0x03, /* 0x04 */ - 0x7b, /* 0x05 */ - 0x0f, /* 0x06 */ - 0x0f, /* 0x07 */ - 0xff, /* 0x08 */ - 0x00, /* 0x09 */ - 0x00, /* 0x0a */ - 0x00, /* 0x0b */ - 0x00, /* 0x0c */ - 0x00, /* 0x0d */ - 0x00, /* 0x0e */ - 0x00, /* 0x0f */ -}; - -#define cbswap_32(__x) \ -((uint32_t)( \ - (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ - (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ - (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ - (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )) - -#ifdef HOST_WORDS_BIGENDIAN -#define PAT(x) cbswap_32(x) -#else -#define PAT(x) (x) -#endif - -#ifdef HOST_WORDS_BIGENDIAN -#define BIG 1 -#else -#define BIG 0 -#endif - -#ifdef HOST_WORDS_BIGENDIAN -#define GET_PLANE(data, p) (((data) >> (24 - (p) * 8)) & 0xff) -#else -#define GET_PLANE(data, p) (((data) >> ((p) * 8)) & 0xff) -#endif - -static const uint32_t mask16[16] = { - PAT(0x00000000), - PAT(0x000000ff), - PAT(0x0000ff00), - PAT(0x0000ffff), - PAT(0x00ff0000), - PAT(0x00ff00ff), - PAT(0x00ffff00), - PAT(0x00ffffff), - PAT(0xff000000), - PAT(0xff0000ff), - PAT(0xff00ff00), - PAT(0xff00ffff), - PAT(0xffff0000), - PAT(0xffff00ff), - PAT(0xffffff00), - PAT(0xffffffff), -}; - -#undef PAT - -#ifdef HOST_WORDS_BIGENDIAN -#define PAT(x) (x) -#else -#define PAT(x) cbswap_32(x) -#endif - -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; - - if (s->legacy_address_space == NULL) { - return; - } - - if (s->has_chain4_alias) { - memory_region_del_subregion(s->legacy_address_space, &s->chain4_alias); - object_unparent(OBJECT(&s->chain4_alias)); - s->has_chain4_alias = false; - s->plane_updated = 0xf; - } - if ((s->sr[VGA_SEQ_PLANE_WRITE] & VGA_SR02_ALL_PLANES) == - VGA_SR02_ALL_PLANES && s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) { - offset = 0; - switch ((s->gr[VGA_GFX_MISC] >> 2) & 3) { - case 0: - base = 0xa0000; - size = 0x20000; - break; - case 1: - base = 0xa0000; - size = 0x10000; - offset = s->bank_offset; - break; - case 2: - base = 0xb0000; - size = 0x8000; - break; - case 3: - default: - base = 0xb8000; - 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, - &s->chain4_alias, 2); - s->has_chain4_alias = true; - } -} - -static void vga_dumb_update_retrace_info(VGACommonState *s) -{ - (void) s; -} - -static void vga_precise_update_retrace_info(VGACommonState *s) -{ - int htotal_chars; - int hretr_start_char; - int hretr_skew_chars; - int hretr_end_char; - - int vtotal_lines; - int vretr_start_line; - int vretr_end_line; - - int dots; -#if 0 - int div2, sldiv2; -#endif - int clocking_mode; - int clock_sel; - const int clk_hz[] = {25175000, 28322000, 25175000, 25175000}; - int64_t chars_per_sec; - struct vga_precise_retrace *r = &s->retrace_info.precise; - - htotal_chars = s->cr[VGA_CRTC_H_TOTAL] + 5; - hretr_start_char = s->cr[VGA_CRTC_H_SYNC_START]; - hretr_skew_chars = (s->cr[VGA_CRTC_H_SYNC_END] >> 5) & 3; - hretr_end_char = s->cr[VGA_CRTC_H_SYNC_END] & 0x1f; - - vtotal_lines = (s->cr[VGA_CRTC_V_TOTAL] | - (((s->cr[VGA_CRTC_OVERFLOW] & 1) | - ((s->cr[VGA_CRTC_OVERFLOW] >> 4) & 2)) << 8)) + 2; - vretr_start_line = s->cr[VGA_CRTC_V_SYNC_START] | - ((((s->cr[VGA_CRTC_OVERFLOW] >> 2) & 1) | - ((s->cr[VGA_CRTC_OVERFLOW] >> 6) & 2)) << 8); - vretr_end_line = s->cr[VGA_CRTC_V_SYNC_END] & 0xf; - - clocking_mode = (s->sr[VGA_SEQ_CLOCK_MODE] >> 3) & 1; - clock_sel = (s->msr >> 2) & 3; - dots = (s->msr & 1) ? 8 : 9; - - chars_per_sec = clk_hz[clock_sel] / dots; - - htotal_chars <<= clocking_mode; - - r->total_chars = vtotal_lines * htotal_chars; - if (r->freq) { - r->ticks_per_char = NANOSECONDS_PER_SECOND / (r->total_chars * r->freq); - } else { - r->ticks_per_char = NANOSECONDS_PER_SECOND / chars_per_sec; - } - - r->vstart = vretr_start_line; - r->vend = r->vstart + vretr_end_line + 1; - - r->hstart = hretr_start_char + hretr_skew_chars; - r->hend = r->hstart + hretr_end_char + 1; - r->htotal = htotal_chars; - -#if 0 - div2 = (s->cr[VGA_CRTC_MODE] >> 2) & 1; - sldiv2 = (s->cr[VGA_CRTC_MODE] >> 3) & 1; - printf ( - "hz=%f\n" - "htotal = %d\n" - "hretr_start = %d\n" - "hretr_skew = %d\n" - "hretr_end = %d\n" - "vtotal = %d\n" - "vretr_start = %d\n" - "vretr_end = %d\n" - "div2 = %d sldiv2 = %d\n" - "clocking_mode = %d\n" - "clock_sel = %d %d\n" - "dots = %d\n" - "ticks/char = %" PRId64 "\n" - "\n", - (double) NANOSECONDS_PER_SECOND / (r->ticks_per_char * r->total_chars), - htotal_chars, - hretr_start_char, - hretr_skew_chars, - hretr_end_char, - vtotal_lines, - vretr_start_line, - vretr_end_line, - div2, sldiv2, - clocking_mode, - clock_sel, - clk_hz[clock_sel], - dots, - r->ticks_per_char - ); -#endif -} - -static uint8_t vga_precise_retrace(VGACommonState *s) -{ - struct vga_precise_retrace *r = &s->retrace_info.precise; - uint8_t val = s->st01 & ~(ST01_V_RETRACE | ST01_DISP_ENABLE); - - if (r->total_chars) { - int cur_line, cur_line_char, cur_char; - int64_t cur_tick; - - cur_tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - cur_char = (cur_tick / r->ticks_per_char) % r->total_chars; - cur_line = cur_char / r->htotal; - - if (cur_line >= r->vstart && cur_line <= r->vend) { - val |= ST01_V_RETRACE | ST01_DISP_ENABLE; - } else { - cur_line_char = cur_char % r->htotal; - if (cur_line_char >= r->hstart && cur_line_char <= r->hend) { - val |= ST01_DISP_ENABLE; - } - } - - return val; - } else { - return s->st01 ^ (ST01_V_RETRACE | ST01_DISP_ENABLE); - } -} - -static uint8_t vga_dumb_retrace(VGACommonState *s) -{ - return s->st01 ^ (ST01_V_RETRACE | ST01_DISP_ENABLE); -} - -int vga_ioport_invalid(VGACommonState *s, uint32_t addr) -{ - if (s->msr & VGA_MIS_COLOR) { - /* Color */ - return (addr >= 0x3b0 && addr <= 0x3bf); - } else { - /* Monochrome */ - return (addr >= 0x3d0 && addr <= 0x3df); - } -} - -uint32_t vga_ioport_read(void *opaque, uint32_t addr) -{ - VGACommonState *s = opaque; - int val, index; - - if (vga_ioport_invalid(s, addr)) { - val = 0xff; - } else { - switch(addr) { - case VGA_ATT_W: - if (s->ar_flip_flop == 0) { - val = s->ar_index; - } else { - val = 0; - } - break; - case VGA_ATT_R: - index = s->ar_index & 0x1f; - if (index < VGA_ATT_C) { - val = s->ar[index]; - } else { - val = 0; - } - break; - case VGA_MIS_W: - val = s->st00; - break; - case VGA_SEQ_I: - val = s->sr_index; - break; - case VGA_SEQ_D: - val = s->sr[s->sr_index]; -#ifdef DEBUG_VGA_REG - printf("vga: read SR%x = 0x%02x\n", s->sr_index, val); -#endif - break; - case VGA_PEL_IR: - val = s->dac_state; - break; - case VGA_PEL_IW: - val = s->dac_write_index; - break; - case VGA_PEL_D: - val = s->palette[s->dac_read_index * 3 + s->dac_sub_index]; - if (++s->dac_sub_index == 3) { - s->dac_sub_index = 0; - s->dac_read_index++; - } - break; - case VGA_FTC_R: - val = s->fcr; - break; - case VGA_MIS_R: - val = s->msr; - break; - case VGA_GFX_I: - val = s->gr_index; - break; - case VGA_GFX_D: - val = s->gr[s->gr_index]; -#ifdef DEBUG_VGA_REG - printf("vga: read GR%x = 0x%02x\n", s->gr_index, val); -#endif - break; - case VGA_CRT_IM: - case VGA_CRT_IC: - val = s->cr_index; - break; - case VGA_CRT_DM: - case VGA_CRT_DC: - val = s->cr[s->cr_index]; -#ifdef DEBUG_VGA_REG - printf("vga: read CR%x = 0x%02x\n", s->cr_index, val); -#endif - break; - case VGA_IS1_RM: - case VGA_IS1_RC: - /* just toggle to fool polling */ - val = s->st01 = s->retrace(s); - s->ar_flip_flop = 0; - break; - default: - val = 0x00; - break; - } - } -#if defined(DEBUG_VGA) - printf("VGA: read addr=0x%04x data=0x%02x\n", addr, val); -#endif - return val; -} - -void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) -{ - VGACommonState *s = opaque; - int index; - - /* check port range access depending on color/monochrome mode */ - if (vga_ioport_invalid(s, addr)) { - return; - } -#ifdef DEBUG_VGA - printf("VGA: write addr=0x%04x data=0x%02x\n", addr, val); -#endif - - switch(addr) { - case VGA_ATT_W: - if (s->ar_flip_flop == 0) { - val &= 0x3f; - s->ar_index = val; - } else { - index = s->ar_index & 0x1f; - switch(index) { - case VGA_ATC_PALETTE0 ... VGA_ATC_PALETTEF: - s->ar[index] = val & 0x3f; - break; - case VGA_ATC_MODE: - s->ar[index] = val & ~0x10; - break; - case VGA_ATC_OVERSCAN: - s->ar[index] = val; - break; - case VGA_ATC_PLANE_ENABLE: - s->ar[index] = val & ~0xc0; - break; - case VGA_ATC_PEL: - s->ar[index] = val & ~0xf0; - break; - case VGA_ATC_COLOR_PAGE: - s->ar[index] = val & ~0xf0; - break; - default: - break; - } - } - s->ar_flip_flop ^= 1; - break; - case VGA_MIS_W: - s->msr = val & ~0x10; - s->update_retrace_info(s); - break; - case VGA_SEQ_I: - s->sr_index = val & 7; - break; - case VGA_SEQ_D: -#ifdef DEBUG_VGA_REG - 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); - } - vga_update_memory_access(s); - break; - case VGA_PEL_IR: - s->dac_read_index = val; - s->dac_sub_index = 0; - s->dac_state = 3; - break; - case VGA_PEL_IW: - s->dac_write_index = val; - s->dac_sub_index = 0; - s->dac_state = 0; - break; - case VGA_PEL_D: - s->dac_cache[s->dac_sub_index] = val; - if (++s->dac_sub_index == 3) { - memcpy(&s->palette[s->dac_write_index * 3], s->dac_cache, 3); - s->dac_sub_index = 0; - s->dac_write_index++; - } - break; - case VGA_GFX_I: - s->gr_index = val & 0x0f; - break; - case VGA_GFX_D: -#ifdef DEBUG_VGA_REG - 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: - case VGA_CRT_IC: - s->cr_index = val; - break; - case VGA_CRT_DM: - case VGA_CRT_DC: -#ifdef DEBUG_VGA_REG - printf("vga: write CR%x = 0x%02x\n", s->cr_index, val); -#endif - /* handle CR0-7 protection */ - if ((s->cr[VGA_CRTC_V_SYNC_END] & VGA_CR11_LOCK_CR0_CR7) && - s->cr_index <= VGA_CRTC_OVERFLOW) { - /* can always write bit 4 of CR7 */ - 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: - case VGA_CRTC_H_SYNC_START: - case VGA_CRTC_H_SYNC_END: - case VGA_CRTC_V_TOTAL: - case VGA_CRTC_OVERFLOW: - case VGA_CRTC_V_SYNC_END: - case VGA_CRTC_MODE: - s->update_retrace_info(s); - break; - } - break; - case VGA_IS1_RM: - case VGA_IS1_RC: - s->fcr = val & 0x10; - break; - } -} - -/* - * Sanity check vbe register writes. - * - * As we don't have a way to signal errors to the guest in the bochs - * dispi interface we'll go adjust the registers to the closest valid - * value. - */ -static void vbe_fixup_regs(VGACommonState *s) -{ - uint16_t *r = s->vbe_regs; - uint32_t bits, linelength, maxy, offset; - - if (!vbe_enabled(s)) { - /* vbe is turned off -- nothing to do */ - return; - } - - /* check depth */ - switch (r[VBE_DISPI_INDEX_BPP]) { - case 4: - case 8: - case 16: - case 24: - case 32: - bits = r[VBE_DISPI_INDEX_BPP]; - break; - case 15: - bits = 16; - break; - default: - bits = r[VBE_DISPI_INDEX_BPP] = 8; - break; - } - - /* check width */ - r[VBE_DISPI_INDEX_XRES] &= ~7u; - if (r[VBE_DISPI_INDEX_XRES] == 0) { - r[VBE_DISPI_INDEX_XRES] = 8; - } - if (r[VBE_DISPI_INDEX_XRES] > VBE_DISPI_MAX_XRES) { - r[VBE_DISPI_INDEX_XRES] = VBE_DISPI_MAX_XRES; - } - r[VBE_DISPI_INDEX_VIRT_WIDTH] &= ~7u; - if (r[VBE_DISPI_INDEX_VIRT_WIDTH] > VBE_DISPI_MAX_XRES) { - r[VBE_DISPI_INDEX_VIRT_WIDTH] = VBE_DISPI_MAX_XRES; - } - if (r[VBE_DISPI_INDEX_VIRT_WIDTH] < r[VBE_DISPI_INDEX_XRES]) { - r[VBE_DISPI_INDEX_VIRT_WIDTH] = r[VBE_DISPI_INDEX_XRES]; - } - - /* check height */ - linelength = r[VBE_DISPI_INDEX_VIRT_WIDTH] * bits / 8; - maxy = s->vbe_size / linelength; - if (r[VBE_DISPI_INDEX_YRES] == 0) { - r[VBE_DISPI_INDEX_YRES] = 1; - } - if (r[VBE_DISPI_INDEX_YRES] > VBE_DISPI_MAX_YRES) { - r[VBE_DISPI_INDEX_YRES] = VBE_DISPI_MAX_YRES; - } - if (r[VBE_DISPI_INDEX_YRES] > maxy) { - r[VBE_DISPI_INDEX_YRES] = maxy; - } - - /* check offset */ - if (r[VBE_DISPI_INDEX_X_OFFSET] > VBE_DISPI_MAX_XRES) { - r[VBE_DISPI_INDEX_X_OFFSET] = VBE_DISPI_MAX_XRES; - } - if (r[VBE_DISPI_INDEX_Y_OFFSET] > VBE_DISPI_MAX_YRES) { - r[VBE_DISPI_INDEX_Y_OFFSET] = VBE_DISPI_MAX_YRES; - } - offset = r[VBE_DISPI_INDEX_X_OFFSET] * bits / 8; - offset += r[VBE_DISPI_INDEX_Y_OFFSET] * linelength; - if (offset + r[VBE_DISPI_INDEX_YRES] * linelength > s->vbe_size) { - r[VBE_DISPI_INDEX_Y_OFFSET] = 0; - offset = r[VBE_DISPI_INDEX_X_OFFSET] * bits / 8; - if (offset + r[VBE_DISPI_INDEX_YRES] * linelength > s->vbe_size) { - r[VBE_DISPI_INDEX_X_OFFSET] = 0; - offset = 0; - } - } - - /* update vga state */ - r[VBE_DISPI_INDEX_VIRT_HEIGHT] = maxy; - s->vbe_line_offset = linelength; - 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; - uint32_t val; - val = s->vbe_index; - return val; -} - -uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr) -{ - VGACommonState *s = opaque; - uint32_t val; - - if (s->vbe_index < VBE_DISPI_INDEX_NB) { - if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_GETCAPS) { - switch(s->vbe_index) { - /* XXX: do not hardcode ? */ - case VBE_DISPI_INDEX_XRES: - val = VBE_DISPI_MAX_XRES; - break; - case VBE_DISPI_INDEX_YRES: - val = VBE_DISPI_MAX_YRES; - break; - case VBE_DISPI_INDEX_BPP: - val = VBE_DISPI_MAX_BPP; - break; - default: - val = s->vbe_regs[s->vbe_index]; - break; - } - } else { - val = s->vbe_regs[s->vbe_index]; - } - } else if (s->vbe_index == VBE_DISPI_INDEX_VIDEO_MEMORY_64K) { - val = s->vbe_size / (64 * 1024); - } else { - val = 0; - } -#ifdef DEBUG_BOCHS_VBE - printf("VBE: read index=0x%x val=0x%x\n", s->vbe_index, val); -#endif - return val; -} - -void vbe_ioport_write_index(void *opaque, uint32_t addr, uint32_t val) -{ - VGACommonState *s = opaque; - s->vbe_index = val; -} - -void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val) -{ - VGACommonState *s = opaque; - - if (s->vbe_index <= VBE_DISPI_INDEX_NB) { -#ifdef DEBUG_BOCHS_VBE - printf("VBE: write index=0x%x val=0x%x\n", s->vbe_index, val); -#endif - switch(s->vbe_index) { - case VBE_DISPI_INDEX_ID: - if (val == VBE_DISPI_ID0 || - val == VBE_DISPI_ID1 || - val == VBE_DISPI_ID2 || - val == VBE_DISPI_ID3 || - val == VBE_DISPI_ID4) { - s->vbe_regs[s->vbe_index] = val; - } - break; - case VBE_DISPI_INDEX_XRES: - case VBE_DISPI_INDEX_YRES: - case VBE_DISPI_INDEX_BPP: - case VBE_DISPI_INDEX_VIRT_WIDTH: - case VBE_DISPI_INDEX_X_OFFSET: - 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: - val &= s->vbe_bank_mask; - s->vbe_regs[s->vbe_index] = val; - s->bank_offset = (val << 16); - vga_update_memory_access(s); - break; - case VBE_DISPI_INDEX_ENABLE: - if ((val & VBE_DISPI_ENABLED) && - !(s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) { - - 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); - } - } else { - s->bank_offset = 0; - } - s->dac_8bit = (val & VBE_DISPI_8BIT_DAC) > 0; - s->vbe_regs[s->vbe_index] = val; - vga_update_memory_access(s); - break; - default: - break; - } - } -} - -/* called for accesses between 0xa0000 and 0xc0000 */ -uint32_t vga_mem_readb(VGACommonState *s, hwaddr addr) -{ - int memory_map_mode, plane; - uint32_t ret; - - /* convert to VGA memory offset */ - memory_map_mode = (s->gr[VGA_GFX_MISC] >> 2) & 3; - addr &= 0x1ffff; - switch(memory_map_mode) { - case 0: - break; - case 1: - if (addr >= 0x10000) - return 0xff; - addr += s->bank_offset; - break; - case 2: - addr -= 0x10000; - if (addr >= 0x8000) - return 0xff; - break; - default: - case 3: - addr -= 0x18000; - if (addr >= 0x8000) - return 0xff; - break; - } - - 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); - 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)) { - /* read mode 0 */ - plane = s->gr[VGA_GFX_PLANE_READ]; - ret = GET_PLANE(s->latch, plane); - } else { - /* read mode 1 */ - ret = (s->latch ^ mask16[s->gr[VGA_GFX_COMPARE_VALUE]]) & - mask16[s->gr[VGA_GFX_COMPARE_MASK]]; - ret |= ret >> 16; - ret |= ret >> 8; - ret = (~ret) & 0xff; - } - } - return ret; -} - -/* called for accesses between 0xa0000 and 0xc0000 */ -void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val) -{ - int memory_map_mode, plane, write_mode, b, func_select, mask; - uint32_t write_mask, bit_mask, set_mask; - -#ifdef DEBUG_VGA_MEM - printf("vga: [0x" TARGET_FMT_plx "] = 0x%02x\n", addr, val); -#endif - /* convert to VGA memory offset */ - memory_map_mode = (s->gr[VGA_GFX_MISC] >> 2) & 3; - addr &= 0x1ffff; - switch(memory_map_mode) { - case 0: - break; - case 1: - if (addr >= 0x10000) - return; - addr += s->bank_offset; - break; - case 2: - addr -= 0x10000; - if (addr >= 0x8000) - return; - break; - default: - case 3: - addr -= 0x18000; - if (addr >= 0x8000) - return; - break; - } - - if (s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) { - /* chain 4 mode : simplest access */ - 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); -#endif - s->plane_updated |= mask; /* only used to detect font change */ - memory_region_set_dirty(&s->vram, addr, 1); - } - } 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); - 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); -#endif - s->plane_updated |= mask; /* only used to detect font change */ - memory_region_set_dirty(&s->vram, addr, 1); - } - } else { - /* standard VGA latched access */ - write_mode = s->gr[VGA_GFX_MODE] & 3; - switch(write_mode) { - default: - case 0: - /* rotate */ - b = s->gr[VGA_GFX_DATA_ROTATE] & 7; - val = ((val >> b) | (val << (8 - b))) & 0xff; - val |= val << 8; - val |= val << 16; - - /* apply set/reset mask */ - set_mask = mask16[s->gr[VGA_GFX_SR_ENABLE]]; - val = (val & ~set_mask) | - (mask16[s->gr[VGA_GFX_SR_VALUE]] & set_mask); - bit_mask = s->gr[VGA_GFX_BIT_MASK]; - break; - case 1: - val = s->latch; - goto do_write; - case 2: - val = mask16[val & 0x0f]; - bit_mask = s->gr[VGA_GFX_BIT_MASK]; - break; - case 3: - /* rotate */ - b = s->gr[VGA_GFX_DATA_ROTATE] & 7; - val = (val >> b) | (val << (8 - b)); - - bit_mask = s->gr[VGA_GFX_BIT_MASK] & val; - val = mask16[s->gr[VGA_GFX_SR_VALUE]]; - break; - } - - /* apply logical operation */ - func_select = s->gr[VGA_GFX_DATA_ROTATE] >> 3; - switch(func_select) { - case 0: - default: - /* nothing to do */ - break; - case 1: - /* and */ - val &= s->latch; - break; - case 2: - /* or */ - val |= s->latch; - break; - case 3: - /* xor */ - val ^= s->latch; - break; - } - - /* apply bit mask */ - bit_mask |= bit_mask << 8; - bit_mask |= bit_mask << 16; - val = (val & bit_mask) | (s->latch & ~bit_mask); - - do_write: - /* mask data according to sr[2] */ - 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); -#ifdef DEBUG_VGA_MEM - printf("vga: latch: [0x" TARGET_FMT_plx "] mask=0x%08x val=0x%08x\n", - addr * 4, write_mask, val); -#endif - memory_region_set_dirty(&s->vram, addr << 2, sizeof(uint32_t)); - } -} - -typedef void vga_draw_line_func(VGACommonState *s1, uint8_t *d, - const uint8_t *s, int width); - -#include "vga-helpers.h" - -/* return true if the palette was modified */ -static int update_palette16(VGACommonState *s) -{ - int full_update, i; - uint32_t v, col, *palette; - - full_update = 0; - palette = s->last_palette; - for(i = 0; i < 16; i++) { - v = s->ar[i]; - if (s->ar[VGA_ATC_MODE] & 0x80) { - v = ((s->ar[VGA_ATC_COLOR_PAGE] & 0xf) << 4) | (v & 0xf); - } else { - v = ((s->ar[VGA_ATC_COLOR_PAGE] & 0xc) << 4) | (v & 0x3f); - } - v = v * 3; - col = rgb_to_pixel32(c6_to_8(s->palette[v]), - c6_to_8(s->palette[v + 1]), - c6_to_8(s->palette[v + 2])); - if (col != palette[i]) { - full_update = 1; - palette[i] = col; - } - } - return full_update; -} - -/* return true if the palette was modified */ -static int update_palette256(VGACommonState *s) -{ - int full_update, i; - uint32_t v, col, *palette; - - full_update = 0; - palette = s->last_palette; - v = 0; - for(i = 0; i < 256; i++) { - if (s->dac_8bit) { - col = rgb_to_pixel32(s->palette[v], - s->palette[v + 1], - s->palette[v + 2]); - } else { - col = rgb_to_pixel32(c6_to_8(s->palette[v]), - c6_to_8(s->palette[v + 1]), - c6_to_8(s->palette[v + 2])); - } - if (col != palette[i]) { - full_update = 1; - palette[i] = col; - } - v += 3; - } - return full_update; -} - -static void vga_get_offsets(VGACommonState *s, - uint32_t *pline_offset, - uint32_t *pstart_addr, - uint32_t *pline_compare) -{ - uint32_t start_addr, line_offset, line_compare; - - if (vbe_enabled(s)) { - line_offset = s->vbe_line_offset; - start_addr = s->vbe_start_addr; - line_compare = 65535; - } else { - /* compute line_offset in bytes */ - line_offset = s->cr[VGA_CRTC_OFFSET]; - line_offset <<= 3; - - /* starting address */ - start_addr = s->cr[VGA_CRTC_START_LO] | - (s->cr[VGA_CRTC_START_HI] << 8); - - /* line compare */ - line_compare = s->cr[VGA_CRTC_LINE_COMPARE] | - ((s->cr[VGA_CRTC_OVERFLOW] & 0x10) << 4) | - ((s->cr[VGA_CRTC_MAX_SCAN] & 0x40) << 3); - } - *pline_offset = line_offset; - *pstart_addr = start_addr; - *pline_compare = line_compare; -} - -/* update start_addr and line_offset. Return TRUE if modified */ -static int update_basic_params(VGACommonState *s) -{ - int full_update; - uint32_t start_addr, line_offset, line_compare; - - full_update = 0; - - s->get_offsets(s, &line_offset, &start_addr, &line_compare); - - if (line_offset != s->line_offset || - start_addr != s->start_addr || - line_compare != s->line_compare) { - s->line_offset = line_offset; - s->start_addr = start_addr; - s->line_compare = line_compare; - full_update = 1; - } - return full_update; -} - - -static const uint8_t cursor_glyph[32 * 4] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -}; - -static void vga_get_text_resolution(VGACommonState *s, int *pwidth, int *pheight, - int *pcwidth, int *pcheight) -{ - int width, cwidth, height, cheight; - - /* total width & height */ - cheight = (s->cr[VGA_CRTC_MAX_SCAN] & 0x1f) + 1; - cwidth = 8; - if (!(s->sr[VGA_SEQ_CLOCK_MODE] & VGA_SR01_CHAR_CLK_8DOTS)) { - cwidth = 9; - } - if (s->sr[VGA_SEQ_CLOCK_MODE] & 0x08) { - cwidth = 16; /* NOTE: no 18 pixel wide */ - } - width = (s->cr[VGA_CRTC_H_DISP] + 1); - if (s->cr[VGA_CRTC_V_TOTAL] == 100) { - /* ugly hack for CGA 160x100x16 - explain me the logic */ - height = 100; - } else { - height = s->cr[VGA_CRTC_V_DISP_END] | - ((s->cr[VGA_CRTC_OVERFLOW] & 0x02) << 7) | - ((s->cr[VGA_CRTC_OVERFLOW] & 0x40) << 3); - height = (height + 1) / cheight; - } - - *pwidth = width; - *pheight = height; - *pcwidth = cwidth; - *pcheight = cheight; -} - -/* - * Text mode update - * Missing: - * - double scan - * - double width - * - underline - * - flashing - */ -static void vga_draw_text(VGACommonState *s, int full_update) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int cx, cy, cheight, cw, ch, cattr, height, width, ch_attr; - int cx_min, cx_max, linesize, x_incr, line, line1; - uint32_t offset, fgcol, bgcol, v, cursor_offset; - uint8_t *d1, *d, *src, *dest, *cursor_ptr; - const uint8_t *font_ptr, *font_base[2]; - int dup9, line_offset; - uint32_t *palette; - uint32_t *ch_attr_ptr; - int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); - - /* compute font data address (in plane 2) */ - v = s->sr[VGA_SEQ_CHARACTER_MAP]; - offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2; - if (offset != s->font_offsets[0]) { - s->font_offsets[0] = offset; - full_update = 1; - } - font_base[0] = s->vram_ptr + offset; - - offset = (((v >> 5) & 1) | ((v >> 1) & 6)) * 8192 * 4 + 2; - font_base[1] = s->vram_ptr + offset; - if (offset != s->font_offsets[1]) { - s->font_offsets[1] = offset; - full_update = 1; - } - if (s->plane_updated & (1 << 2) || s->has_chain4_alias) { - /* if the plane 2 was modified since the last display, it - indicates the font may have been modified */ - s->plane_updated = 0; - full_update = 1; - } - full_update |= update_basic_params(s); - - line_offset = s->line_offset; - - vga_get_text_resolution(s, &width, &height, &cw, &cheight); - if ((height * width) <= 1) { - /* better than nothing: exit if transient size is too small */ - return; - } - if ((height * width) > CH_ATTR_SIZE) { - /* better than nothing: exit if transient size is too big */ - return; - } - - if (width != s->last_width || height != s->last_height || - cw != s->last_cw || cheight != s->last_ch || s->last_depth) { - s->last_scr_width = width * cw; - s->last_scr_height = height * cheight; - qemu_console_resize(s->con, s->last_scr_width, s->last_scr_height); - surface = qemu_console_surface(s->con); - dpy_text_resize(s->con, width, height); - s->last_depth = 0; - s->last_width = width; - s->last_height = height; - s->last_ch = cheight; - s->last_cw = cw; - full_update = 1; - } - full_update |= update_palette16(s); - palette = s->last_palette; - x_incr = cw * surface_bytes_per_pixel(surface); - - if (full_update) { - s->full_update_text = 1; - } - if (s->full_update_gfx) { - s->full_update_gfx = 0; - full_update |= 1; - } - - cursor_offset = ((s->cr[VGA_CRTC_CURSOR_HI] << 8) | - s->cr[VGA_CRTC_CURSOR_LO]) - s->start_addr; - if (cursor_offset != s->cursor_offset || - s->cr[VGA_CRTC_CURSOR_START] != s->cursor_start || - s->cr[VGA_CRTC_CURSOR_END] != s->cursor_end) { - /* if the cursor position changed, we update the old and new - chars */ - if (s->cursor_offset < CH_ATTR_SIZE) - s->last_ch_attr[s->cursor_offset] = -1; - if (cursor_offset < CH_ATTR_SIZE) - s->last_ch_attr[cursor_offset] = -1; - s->cursor_offset = cursor_offset; - s->cursor_start = s->cr[VGA_CRTC_CURSOR_START]; - s->cursor_end = s->cr[VGA_CRTC_CURSOR_END]; - } - cursor_ptr = s->vram_ptr + (s->start_addr + cursor_offset) * 4; - if (now >= s->cursor_blink_time) { - s->cursor_blink_time = now + VGA_TEXT_CURSOR_PERIOD_MS / 2; - s->cursor_visible_phase = !s->cursor_visible_phase; - } - - dest = surface_data(surface); - linesize = surface_stride(surface); - ch_attr_ptr = s->last_ch_attr; - line = 0; - offset = s->start_addr * 4; - for(cy = 0; cy < height; cy++) { - d1 = dest; - src = s->vram_ptr + offset; - cx_min = width; - cx_max = -1; - for(cx = 0; cx < width; cx++) { - ch_attr = *(uint16_t *)src; - if (full_update || ch_attr != *ch_attr_ptr || src == cursor_ptr) { - if (cx < cx_min) - cx_min = cx; - if (cx > cx_max) - cx_max = cx; - *ch_attr_ptr = ch_attr; -#ifdef HOST_WORDS_BIGENDIAN - ch = ch_attr >> 8; - cattr = ch_attr & 0xff; -#else - ch = ch_attr & 0xff; - cattr = ch_attr >> 8; -#endif - font_ptr = font_base[(cattr >> 3) & 1]; - font_ptr += 32 * 4 * ch; - bgcol = palette[cattr >> 4]; - fgcol = palette[cattr & 0x0f]; - if (cw == 16) { - vga_draw_glyph16(d1, linesize, - font_ptr, cheight, fgcol, bgcol); - } else if (cw != 9) { - vga_draw_glyph8(d1, linesize, - font_ptr, cheight, fgcol, bgcol); - } else { - dup9 = 0; - if (ch >= 0xb0 && ch <= 0xdf && - (s->ar[VGA_ATC_MODE] & 0x04)) { - dup9 = 1; - } - vga_draw_glyph9(d1, linesize, - font_ptr, cheight, fgcol, bgcol, dup9); - } - if (src == cursor_ptr && - !(s->cr[VGA_CRTC_CURSOR_START] & 0x20) && - s->cursor_visible_phase) { - int line_start, line_last, h; - /* draw the cursor */ - line_start = s->cr[VGA_CRTC_CURSOR_START] & 0x1f; - line_last = s->cr[VGA_CRTC_CURSOR_END] & 0x1f; - /* XXX: check that */ - if (line_last > cheight - 1) - line_last = cheight - 1; - if (line_last >= line_start && line_start < cheight) { - h = line_last - line_start + 1; - d = d1 + linesize * line_start; - if (cw == 16) { - vga_draw_glyph16(d, linesize, - cursor_glyph, h, fgcol, bgcol); - } else if (cw != 9) { - vga_draw_glyph8(d, linesize, - cursor_glyph, h, fgcol, bgcol); - } else { - vga_draw_glyph9(d, linesize, - cursor_glyph, h, fgcol, bgcol, 1); - } - } - } - } - d1 += x_incr; - src += 4; - ch_attr_ptr++; - } - if (cx_max != -1) { - dpy_gfx_update(s->con, cx_min * cw, cy * cheight, - (cx_max - cx_min + 1) * cw, cheight); - } - dest += linesize * cheight; - line1 = line + cheight; - offset += line_offset; - if (line < s->line_compare && line1 >= s->line_compare) { - offset = 0; - } - line = line1; - } -} - -enum { - VGA_DRAW_LINE2, - VGA_DRAW_LINE2D2, - VGA_DRAW_LINE4, - VGA_DRAW_LINE4D2, - VGA_DRAW_LINE8D2, - VGA_DRAW_LINE8, - VGA_DRAW_LINE15_LE, - VGA_DRAW_LINE16_LE, - VGA_DRAW_LINE24_LE, - VGA_DRAW_LINE32_LE, - VGA_DRAW_LINE15_BE, - VGA_DRAW_LINE16_BE, - VGA_DRAW_LINE24_BE, - VGA_DRAW_LINE32_BE, - VGA_DRAW_LINE_NB, -}; - -static vga_draw_line_func * const vga_draw_line_table[VGA_DRAW_LINE_NB] = { - vga_draw_line2, - vga_draw_line2d2, - vga_draw_line4, - vga_draw_line4d2, - vga_draw_line8d2, - vga_draw_line8, - vga_draw_line15_le, - vga_draw_line16_le, - vga_draw_line24_le, - vga_draw_line32_le, - vga_draw_line15_be, - vga_draw_line16_be, - vga_draw_line24_be, - vga_draw_line32_be, -}; - -static int vga_get_bpp(VGACommonState *s) -{ - int ret; - - if (vbe_enabled(s)) { - ret = s->vbe_regs[VBE_DISPI_INDEX_BPP]; - } else { - ret = 0; - } - return ret; -} - -static void vga_get_resolution(VGACommonState *s, int *pwidth, int *pheight) -{ - int width, height; - - if (vbe_enabled(s)) { - width = s->vbe_regs[VBE_DISPI_INDEX_XRES]; - height = s->vbe_regs[VBE_DISPI_INDEX_YRES]; - } else { - width = (s->cr[VGA_CRTC_H_DISP] + 1) * 8; - height = s->cr[VGA_CRTC_V_DISP_END] | - ((s->cr[VGA_CRTC_OVERFLOW] & 0x02) << 7) | - ((s->cr[VGA_CRTC_OVERFLOW] & 0x40) << 3); - height = (height + 1); - } - *pwidth = width; - *pheight = height; -} - -void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2) -{ - int y; - if (y1 >= VGA_MAX_HEIGHT) - return; - if (y2 >= VGA_MAX_HEIGHT) - y2 = VGA_MAX_HEIGHT; - for(y = y1; y < y2; y++) { - s->invalidated_y_table[y >> 5] |= 1 << (y & 0x1f); - } -} - -void vga_sync_dirty_bitmap(VGACommonState *s) -{ - memory_region_sync_dirty_bitmap(&s->vram); -} - -void vga_dirty_log_start(VGACommonState *s) -{ - memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA); -} - -void vga_dirty_log_stop(VGACommonState *s) -{ - memory_region_set_log(&s->vram, false, DIRTY_MEMORY_VGA); -} - -/* - * graphic modes - */ -static void vga_draw_graphic(VGACommonState *s, int full_update) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int y1, y, update, linesize, y_start, double_scan, mask, depth; - int width, height, shift_control, line_offset, bwidth, bits; - ram_addr_t page0, page1, page_min, page_max; - int disp_width, multi_scan, multi_run; - uint8_t *d; - uint32_t v, addr1, addr; - vga_draw_line_func *vga_draw_line = NULL; - bool share_surface; - pixman_format_code_t format; -#ifdef HOST_WORDS_BIGENDIAN - bool byteswap = !s->big_endian_fb; -#else - bool byteswap = s->big_endian_fb; -#endif - - full_update |= update_basic_params(s); - - if (!full_update) - vga_sync_dirty_bitmap(s); - - s->get_resolution(s, &width, &height); - disp_width = width; - - shift_control = (s->gr[VGA_GFX_MODE] >> 5) & 3; - double_scan = (s->cr[VGA_CRTC_MAX_SCAN] >> 7); - if (shift_control != 1) { - multi_scan = (((s->cr[VGA_CRTC_MAX_SCAN] & 0x1f) + 1) << double_scan) - - 1; - } else { - /* in CGA modes, multi_scan is ignored */ - /* XXX: is it correct ? */ - multi_scan = double_scan; - } - multi_run = multi_scan; - if (shift_control != s->shift_control || - double_scan != s->double_scan) { - full_update = 1; - s->shift_control = shift_control; - s->double_scan = double_scan; - } - - if (shift_control == 0) { - if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { - disp_width <<= 1; - } - } else if (shift_control == 1) { - if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { - disp_width <<= 1; - } - } - - depth = s->get_bpp(s); - - /* - * Check whether we can share the surface with the backend - * or whether we need a shadow surface. We share native - * endian surfaces for 15bpp and above and byteswapped - * surfaces for 24bpp and above. - */ - format = qemu_default_pixman_format(depth, !byteswap); - if (format) { - share_surface = dpy_gfx_check_format(s->con, format) - && !s->force_shadow; - } else { - share_surface = false; - } - if (s->line_offset != s->last_line_offset || - disp_width != s->last_width || - height != s->last_height || - s->last_depth != depth || - s->last_byteswap != byteswap || - share_surface != is_buffer_shared(surface)) { - if (share_surface) { - surface = qemu_create_displaysurface_from(disp_width, - height, format, s->line_offset, - s->vram_ptr + (s->start_addr * 4)); - dpy_gfx_replace_surface(s->con, surface); -#ifdef DEBUG_VGA - printf("VGA: Using shared surface for depth=%d swap=%d\n", - depth, byteswap); -#endif - } else { - qemu_console_resize(s->con, disp_width, height); - surface = qemu_console_surface(s->con); -#ifdef DEBUG_VGA - printf("VGA: Using shadow surface for depth=%d swap=%d\n", - depth, byteswap); -#endif - } - s->last_scr_width = disp_width; - s->last_scr_height = height; - s->last_width = disp_width; - s->last_height = height; - s->last_line_offset = s->line_offset; - s->last_depth = depth; - s->last_byteswap = byteswap; - full_update = 1; - } else if (is_buffer_shared(surface) && - (full_update || surface_data(surface) != s->vram_ptr - + (s->start_addr * 4))) { - pixman_format_code_t format = - qemu_default_pixman_format(depth, !byteswap); - surface = qemu_create_displaysurface_from(disp_width, - height, format, s->line_offset, - s->vram_ptr + (s->start_addr * 4)); - dpy_gfx_replace_surface(s->con, surface); - } - - if (shift_control == 0) { - full_update |= update_palette16(s); - if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { - v = VGA_DRAW_LINE4D2; - } else { - v = VGA_DRAW_LINE4; - } - bits = 4; - } else if (shift_control == 1) { - full_update |= update_palette16(s); - if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { - v = VGA_DRAW_LINE2D2; - } else { - v = VGA_DRAW_LINE2; - } - bits = 4; - } else { - switch(s->get_bpp(s)) { - default: - case 0: - full_update |= update_palette256(s); - v = VGA_DRAW_LINE8D2; - bits = 4; - break; - case 8: - full_update |= update_palette256(s); - v = VGA_DRAW_LINE8; - bits = 8; - break; - case 15: - v = s->big_endian_fb ? VGA_DRAW_LINE15_BE : VGA_DRAW_LINE15_LE; - bits = 16; - break; - case 16: - v = s->big_endian_fb ? VGA_DRAW_LINE16_BE : VGA_DRAW_LINE16_LE; - bits = 16; - break; - case 24: - v = s->big_endian_fb ? VGA_DRAW_LINE24_BE : VGA_DRAW_LINE24_LE; - bits = 24; - break; - case 32: - v = s->big_endian_fb ? VGA_DRAW_LINE32_BE : VGA_DRAW_LINE32_LE; - bits = 32; - break; - } - } - vga_draw_line = vga_draw_line_table[v]; - - if (!is_buffer_shared(surface) && s->cursor_invalidate) { - s->cursor_invalidate(s); - } - - line_offset = s->line_offset; -#if 0 - printf("w=%d h=%d v=%d line_offset=%d cr[0x09]=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=0x%02x\n", - width, height, v, line_offset, s->cr[9], s->cr[VGA_CRTC_MODE], - s->line_compare, s->sr[VGA_SEQ_CLOCK_MODE]); -#endif - addr1 = (s->start_addr * 4); - bwidth = (width * bits + 7) / 8; - y_start = -1; - page_min = -1; - page_max = 0; - d = surface_data(surface); - linesize = surface_stride(surface); - y1 = 0; - for(y = 0; y < height; y++) { - addr = addr1; - if (!(s->cr[VGA_CRTC_MODE] & 1)) { - int shift; - /* CGA compatibility handling */ - shift = 14 + ((s->cr[VGA_CRTC_MODE] >> 6) & 1); - addr = (addr & ~(1 << shift)) | ((y1 & 1) << shift); - } - if (!(s->cr[VGA_CRTC_MODE] & 2)) { - addr = (addr & ~0x8000) | ((y1 & 2) << 14); - } - update = full_update; - page0 = addr; - page1 = addr + bwidth - 1; - update |= memory_region_get_dirty(&s->vram, page0, page1 - page0, - DIRTY_MEMORY_VGA); - /* explicit invalidation for the hardware cursor */ - update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1; - if (update) { - if (y_start < 0) - y_start = y; - if (page0 < page_min) - page_min = page0; - if (page1 > page_max) - page_max = page1; - if (!(is_buffer_shared(surface))) { - vga_draw_line(s, d, s->vram_ptr + addr, width); - if (s->cursor_draw_line) - s->cursor_draw_line(s, d, y); - } - } else { - if (y_start >= 0) { - /* flush to display */ - dpy_gfx_update(s->con, 0, y_start, - disp_width, y - y_start); - y_start = -1; - } - } - if (!multi_run) { - mask = (s->cr[VGA_CRTC_MODE] & 3) ^ 3; - if ((y1 & mask) == mask) - addr1 += line_offset; - y1++; - multi_run = multi_scan; - } else { - multi_run--; - } - /* line compare acts on the displayed lines */ - if (y == s->line_compare) - addr1 = 0; - d += linesize; - } - if (y_start >= 0) { - /* flush to display */ - dpy_gfx_update(s->con, 0, y_start, - disp_width, y - y_start); - } - /* reset modified pages */ - if (page_max >= page_min) { - memory_region_reset_dirty(&s->vram, - page_min, - page_max - page_min, - DIRTY_MEMORY_VGA); - } - memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4); -} - -static void vga_draw_blank(VGACommonState *s, int full_update) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int i, w; - uint8_t *d; - - if (!full_update) - return; - if (s->last_scr_width <= 0 || s->last_scr_height <= 0) - return; - - w = s->last_scr_width * surface_bytes_per_pixel(surface); - d = surface_data(surface); - for(i = 0; i < s->last_scr_height; i++) { - memset(d, 0, w); - d += surface_stride(surface); - } - dpy_gfx_update(s->con, 0, 0, - s->last_scr_width, s->last_scr_height); -} - -#define GMODE_TEXT 0 -#define GMODE_GRAPH 1 -#define GMODE_BLANK 2 - -static void vga_update_display(void *opaque) -{ - VGACommonState *s = opaque; - DisplaySurface *surface = qemu_console_surface(s->con); - int full_update, graphic_mode; - - qemu_flush_coalesced_mmio_buffer(); - - if (surface_bits_per_pixel(surface) == 0) { - /* nothing to do */ - } else { - full_update = 0; - if (!(s->ar_index & 0x20)) { - graphic_mode = GMODE_BLANK; - } else { - graphic_mode = s->gr[VGA_GFX_MISC] & VGA_GR06_GRAPHICS_MODE; - } - if (graphic_mode != s->graphic_mode) { - s->graphic_mode = graphic_mode; - s->cursor_blink_time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); - full_update = 1; - } - switch(graphic_mode) { - case GMODE_TEXT: - vga_draw_text(s, full_update); - break; - case GMODE_GRAPH: - vga_draw_graphic(s, full_update); - break; - case GMODE_BLANK: - default: - vga_draw_blank(s, full_update); - break; - } - } -} - -/* force a full display refresh */ -static void vga_invalidate_display(void *opaque) -{ - VGACommonState *s = opaque; - - s->last_width = -1; - s->last_height = -1; -} - -void vga_common_reset(VGACommonState *s) -{ - s->sr_index = 0; - memset(s->sr, '\0', sizeof(s->sr)); - s->gr_index = 0; - memset(s->gr, '\0', sizeof(s->gr)); - s->ar_index = 0; - memset(s->ar, '\0', sizeof(s->ar)); - s->ar_flip_flop = 0; - s->cr_index = 0; - memset(s->cr, '\0', sizeof(s->cr)); - s->msr = 0; - s->fcr = 0; - s->st00 = 0; - s->st01 = 0; - s->dac_state = 0; - s->dac_sub_index = 0; - s->dac_read_index = 0; - s->dac_write_index = 0; - memset(s->dac_cache, '\0', sizeof(s->dac_cache)); - s->dac_8bit = 0; - memset(s->palette, '\0', sizeof(s->palette)); - s->bank_offset = 0; - s->vbe_index = 0; - memset(s->vbe_regs, '\0', sizeof(s->vbe_regs)); - s->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID5; - s->vbe_start_addr = 0; - s->vbe_line_offset = 0; - s->vbe_bank_mask = (s->vram_size >> 16) - 1; - memset(s->font_offsets, '\0', sizeof(s->font_offsets)); - s->graphic_mode = -1; /* force full update */ - s->shift_control = 0; - s->double_scan = 0; - s->line_offset = 0; - s->line_compare = 0; - s->start_addr = 0; - s->plane_updated = 0; - s->last_cw = 0; - s->last_ch = 0; - s->last_width = 0; - s->last_height = 0; - s->last_scr_width = 0; - s->last_scr_height = 0; - s->cursor_start = 0; - s->cursor_end = 0; - s->cursor_offset = 0; - s->big_endian_fb = s->default_endian_fb; - memset(s->invalidated_y_table, '\0', sizeof(s->invalidated_y_table)); - memset(s->last_palette, '\0', sizeof(s->last_palette)); - memset(s->last_ch_attr, '\0', sizeof(s->last_ch_attr)); - switch (vga_retrace_method) { - case VGA_RETRACE_DUMB: - break; - case VGA_RETRACE_PRECISE: - memset(&s->retrace_info, 0, sizeof (s->retrace_info)); - break; - } - vga_update_memory_access(s); -} - -static void vga_reset(void *opaque) -{ - VGACommonState *s = opaque; - vga_common_reset(s); -} - -#define TEXTMODE_X(x) ((x) % width) -#define TEXTMODE_Y(x) ((x) / width) -#define VMEM2CHTYPE(v) ((v & 0xff0007ff) | \ - ((v & 0x00000800) << 10) | ((v & 0x00007000) >> 1)) -/* relay text rendering to the display driver - * instead of doing a full vga_update_display() */ -static void vga_update_text(void *opaque, console_ch_t *chardata) -{ - VGACommonState *s = opaque; - int graphic_mode, i, cursor_offset, cursor_visible; - int cw, cheight, width, height, size, c_min, c_max; - uint32_t *src; - console_ch_t *dst, val; - char msg_buffer[80]; - int full_update = 0; - - qemu_flush_coalesced_mmio_buffer(); - - if (!(s->ar_index & 0x20)) { - graphic_mode = GMODE_BLANK; - } else { - graphic_mode = s->gr[VGA_GFX_MISC] & VGA_GR06_GRAPHICS_MODE; - } - if (graphic_mode != s->graphic_mode) { - s->graphic_mode = graphic_mode; - full_update = 1; - } - if (s->last_width == -1) { - s->last_width = 0; - full_update = 1; - } - - switch (graphic_mode) { - case GMODE_TEXT: - /* TODO: update palette */ - full_update |= update_basic_params(s); - - /* total width & height */ - cheight = (s->cr[VGA_CRTC_MAX_SCAN] & 0x1f) + 1; - cw = 8; - if (!(s->sr[VGA_SEQ_CLOCK_MODE] & VGA_SR01_CHAR_CLK_8DOTS)) { - cw = 9; - } - if (s->sr[VGA_SEQ_CLOCK_MODE] & 0x08) { - cw = 16; /* NOTE: no 18 pixel wide */ - } - width = (s->cr[VGA_CRTC_H_DISP] + 1); - if (s->cr[VGA_CRTC_V_TOTAL] == 100) { - /* ugly hack for CGA 160x100x16 - explain me the logic */ - height = 100; - } else { - height = s->cr[VGA_CRTC_V_DISP_END] | - ((s->cr[VGA_CRTC_OVERFLOW] & 0x02) << 7) | - ((s->cr[VGA_CRTC_OVERFLOW] & 0x40) << 3); - height = (height + 1) / cheight; - } - - size = (height * width); - if (size > CH_ATTR_SIZE) { - if (!full_update) - return; - - snprintf(msg_buffer, sizeof(msg_buffer), "%i x %i Text mode", - width, height); - break; - } - - if (width != s->last_width || height != s->last_height || - cw != s->last_cw || cheight != s->last_ch) { - s->last_scr_width = width * cw; - s->last_scr_height = height * cheight; - qemu_console_resize(s->con, s->last_scr_width, s->last_scr_height); - dpy_text_resize(s->con, width, height); - s->last_depth = 0; - s->last_width = width; - s->last_height = height; - s->last_ch = cheight; - s->last_cw = cw; - full_update = 1; - } - - if (full_update) { - s->full_update_gfx = 1; - } - if (s->full_update_text) { - s->full_update_text = 0; - full_update |= 1; - } - - /* Update "hardware" cursor */ - cursor_offset = ((s->cr[VGA_CRTC_CURSOR_HI] << 8) | - s->cr[VGA_CRTC_CURSOR_LO]) - s->start_addr; - if (cursor_offset != s->cursor_offset || - s->cr[VGA_CRTC_CURSOR_START] != s->cursor_start || - s->cr[VGA_CRTC_CURSOR_END] != s->cursor_end || full_update) { - cursor_visible = !(s->cr[VGA_CRTC_CURSOR_START] & 0x20); - if (cursor_visible && cursor_offset < size && cursor_offset >= 0) - dpy_text_cursor(s->con, - TEXTMODE_X(cursor_offset), - TEXTMODE_Y(cursor_offset)); - else - dpy_text_cursor(s->con, -1, -1); - s->cursor_offset = cursor_offset; - s->cursor_start = s->cr[VGA_CRTC_CURSOR_START]; - s->cursor_end = s->cr[VGA_CRTC_CURSOR_END]; - } - - src = (uint32_t *) s->vram_ptr + s->start_addr; - dst = chardata; - - if (full_update) { - for (i = 0; i < size; src ++, dst ++, i ++) - console_write_ch(dst, VMEM2CHTYPE(le32_to_cpu(*src))); - - dpy_text_update(s->con, 0, 0, width, height); - } else { - c_max = 0; - - for (i = 0; i < size; src ++, dst ++, i ++) { - console_write_ch(&val, VMEM2CHTYPE(le32_to_cpu(*src))); - if (*dst != val) { - *dst = val; - c_max = i; - break; - } - } - c_min = i; - for (; i < size; src ++, dst ++, i ++) { - console_write_ch(&val, VMEM2CHTYPE(le32_to_cpu(*src))); - if (*dst != val) { - *dst = val; - c_max = i; - } - } - - if (c_min <= c_max) { - i = TEXTMODE_Y(c_min); - dpy_text_update(s->con, 0, i, width, TEXTMODE_Y(c_max) - i + 1); - } - } - - return; - case GMODE_GRAPH: - if (!full_update) - return; - - s->get_resolution(s, &width, &height); - snprintf(msg_buffer, sizeof(msg_buffer), "%i x %i Graphic mode", - width, height); - break; - case GMODE_BLANK: - default: - if (!full_update) - return; - - snprintf(msg_buffer, sizeof(msg_buffer), "VGA Blank mode"); - break; - } - - /* Display a message */ - s->last_width = 60; - s->last_height = height = 3; - dpy_text_cursor(s->con, -1, -1); - dpy_text_resize(s->con, s->last_width, height); - - for (dst = chardata, i = 0; i < s->last_width * height; i ++) - console_write_ch(dst ++, ' '); - - size = strlen(msg_buffer); - width = (s->last_width - size) / 2; - dst = chardata + s->last_width + width; - for (i = 0; i < size; 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); -} - -static uint64_t vga_mem_read(void *opaque, hwaddr addr, - unsigned size) -{ - VGACommonState *s = opaque; - - return vga_mem_readb(s, addr); -} - -static void vga_mem_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VGACommonState *s = opaque; - - vga_mem_writeb(s, addr, data); -} - -const MemoryRegionOps vga_mem_ops = { - .read = vga_mem_read, - .write = vga_mem_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static int vga_common_post_load(void *opaque, int version_id) -{ - VGACommonState *s = opaque; - - /* force refresh */ - s->graphic_mode = -1; - return 0; -} - -static bool vga_endian_state_needed(void *opaque) -{ - VGACommonState *s = opaque; - - /* - * Only send the endian state if it's different from the - * default one, thus ensuring backward compatibility for - * migration of the common case - */ - return s->default_endian_fb != s->big_endian_fb; -} - -static const VMStateDescription vmstate_vga_endian = { - .name = "vga.endian", - .version_id = 1, - .minimum_version_id = 1, - .needed = vga_endian_state_needed, - .fields = (VMStateField[]) { - VMSTATE_BOOL(big_endian_fb, VGACommonState), - VMSTATE_END_OF_LIST() - } -}; - -const VMStateDescription vmstate_vga_common = { - .name = "vga", - .version_id = 2, - .minimum_version_id = 2, - .post_load = vga_common_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(latch, VGACommonState), - VMSTATE_UINT8(sr_index, VGACommonState), - VMSTATE_PARTIAL_BUFFER(sr, VGACommonState, 8), - VMSTATE_UINT8(gr_index, VGACommonState), - VMSTATE_PARTIAL_BUFFER(gr, VGACommonState, 16), - VMSTATE_UINT8(ar_index, VGACommonState), - VMSTATE_BUFFER(ar, VGACommonState), - VMSTATE_INT32(ar_flip_flop, VGACommonState), - VMSTATE_UINT8(cr_index, VGACommonState), - VMSTATE_BUFFER(cr, VGACommonState), - VMSTATE_UINT8(msr, VGACommonState), - VMSTATE_UINT8(fcr, VGACommonState), - VMSTATE_UINT8(st00, VGACommonState), - VMSTATE_UINT8(st01, VGACommonState), - - VMSTATE_UINT8(dac_state, VGACommonState), - VMSTATE_UINT8(dac_sub_index, VGACommonState), - VMSTATE_UINT8(dac_read_index, VGACommonState), - VMSTATE_UINT8(dac_write_index, VGACommonState), - VMSTATE_BUFFER(dac_cache, VGACommonState), - VMSTATE_BUFFER(palette, VGACommonState), - - VMSTATE_INT32(bank_offset, VGACommonState), - VMSTATE_UINT8_EQUAL(is_vbe_vmstate, VGACommonState), - VMSTATE_UINT16(vbe_index, VGACommonState), - VMSTATE_UINT16_ARRAY(vbe_regs, VGACommonState, VBE_DISPI_INDEX_NB), - VMSTATE_UINT32(vbe_start_addr, VGACommonState), - VMSTATE_UINT32(vbe_line_offset, VGACommonState), - VMSTATE_UINT32(vbe_bank_mask, VGACommonState), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_vga_endian, - NULL - } -}; - -static const GraphicHwOps vga_ops = { - .invalidate = vga_invalidate_display, - .gfx_update = vga_update_display, - .text_update = vga_update_text, -}; - -static inline uint32_t uint_clamp(uint32_t val, uint32_t vmin, uint32_t vmax) -{ - if (val < vmin) { - return vmin; - } - if (val > vmax) { - return vmax; - } - return val; -} - -void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate) -{ - int i, j, v, b; - - for(i = 0;i < 256; i++) { - v = 0; - for(j = 0; j < 8; j++) { - v |= ((i >> j) & 1) << (j * 4); - } - expand4[i] = v; - - v = 0; - for(j = 0; j < 4; j++) { - v |= ((i >> (2 * j)) & 3) << (j * 4); - } - expand2[i] = v; - } - for(i = 0; i < 16; i++) { - v = 0; - for(j = 0; j < 4; j++) { - b = ((i >> j) & 1); - v |= b << (2 * j); - v |= b << (2 * j + 1); - } - expand4to8[i] = v; - } - - s->vram_size_mb = uint_clamp(s->vram_size_mb, 1, 512); - s->vram_size_mb = pow2ceil(s->vram_size_mb); - s->vram_size = s->vram_size_mb << 20; - - if (!s->vbe_size) { - s->vbe_size = s->vram_size; - } - - s->is_vbe_vmstate = 1; - memory_region_init_ram(&s->vram, obj, "vga.vram", s->vram_size, - &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); - s->get_bpp = vga_get_bpp; - s->get_offsets = vga_get_offsets; - s->get_resolution = vga_get_resolution; - s->hw_ops = &vga_ops; - switch (vga_retrace_method) { - case VGA_RETRACE_DUMB: - s->retrace = vga_dumb_retrace; - s->update_retrace_info = vga_dumb_update_retrace_info; - break; - - case VGA_RETRACE_PRECISE: - s->retrace = vga_precise_retrace; - s->update_retrace_info = vga_precise_update_retrace_info; - break; - } - - /* - * Set default fb endian based on target, could probably be turned - * into a device attribute set by the machine/platform to remove - * all target endian dependencies from this file. - */ -#ifdef TARGET_WORDS_BIGENDIAN - s->default_endian_fb = true; -#else - s->default_endian_fb = false; -#endif - vga_dirty_log_start(s); -} - -static const MemoryRegionPortio vga_portio_list[] = { - { 0x04, 2, 1, .read = vga_ioport_read, .write = vga_ioport_write }, /* 3b4 */ - { 0x0a, 1, 1, .read = vga_ioport_read, .write = vga_ioport_write }, /* 3ba */ - { 0x10, 16, 1, .read = vga_ioport_read, .write = vga_ioport_write }, /* 3c0 */ - { 0x24, 2, 1, .read = vga_ioport_read, .write = vga_ioport_write }, /* 3d4 */ - { 0x2a, 1, 1, .read = vga_ioport_read, .write = vga_ioport_write }, /* 3da */ - PORTIO_END_OF_LIST(), -}; - -static const MemoryRegionPortio vbe_portio_list[] = { - { 0, 1, 2, .read = vbe_ioport_read_index, .write = vbe_ioport_write_index }, -# ifdef TARGET_I386 - { 1, 1, 2, .read = vbe_ioport_read_data, .write = vbe_ioport_write_data }, -# endif - { 2, 1, 2, .read = vbe_ioport_read_data, .write = vbe_ioport_write_data }, - PORTIO_END_OF_LIST(), -}; - -/* Used by both ISA and PCI */ -MemoryRegion *vga_init_io(VGACommonState *s, Object *obj, - const MemoryRegionPortio **vga_ports, - const MemoryRegionPortio **vbe_ports) -{ - MemoryRegion *vga_mem; - - *vga_ports = vga_portio_list; - *vbe_ports = vbe_portio_list; - - vga_mem = g_malloc(sizeof(*vga_mem)); - memory_region_init_io(vga_mem, obj, &vga_mem_ops, s, - "vga-lowmem", 0x20000); - memory_region_set_flush_coalesced(vga_mem); - - return vga_mem; -} - -void vga_init(VGACommonState *s, Object *obj, MemoryRegion *address_space, - MemoryRegion *address_space_io, bool init_vga_ports) -{ - MemoryRegion *vga_io_memory; - const MemoryRegionPortio *vga_ports, *vbe_ports; - - qemu_register_reset(vga_reset, s); - - s->bank_offset = 0; - - s->legacy_address_space = address_space; - - vga_io_memory = vga_init_io(s, obj, &vga_ports, &vbe_ports); - memory_region_add_subregion_overlap(address_space, - 0x000a0000, - vga_io_memory, - 1); - memory_region_set_coalescing(vga_io_memory); - if (init_vga_ports) { - portio_list_init(&s->vga_port_list, obj, vga_ports, s, "vga"); - portio_list_set_flush_coalesced(&s->vga_port_list); - portio_list_add(&s->vga_port_list, address_space_io, 0x3b0); - } - if (vbe_ports) { - portio_list_init(&s->vbe_port_list, obj, vbe_ports, s, "vbe"); - portio_list_add(&s->vbe_port_list, address_space_io, 0x1ce); - } -} - -void vga_init_vbe(VGACommonState *s, Object *obj, MemoryRegion *system_memory) -{ - /* With pc-0.12 and below we map both the PCI BAR and the fixed VBE region, - * so use an alias to avoid double-mapping the same region. - */ - memory_region_init_alias(&s->vram_vbe, obj, "vram.vbe", - &s->vram, 0, memory_region_size(&s->vram)); - /* XXX: use optimized standard vga accesses */ - memory_region_add_subregion(system_memory, - VBE_DISPI_LFB_PHYSICAL_ADDRESS, - &s->vram_vbe); - s->vbe_mapped = 1; -} diff --git a/qemu/hw/display/vga.h b/qemu/hw/display/vga.h deleted file mode 100644 index d917046da..000000000 --- a/qemu/hw/display/vga.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * linux/include/video/vga.h -- standard VGA chipset interaction - * - * Copyright 1999 Jeff Garzik - * - * Copyright history from vga16fb.c: - * Copyright 1999 Ben Pfaff and Petr Vandrovec - * Based on VGA info at http://www.osdever.net/FreeVGA/home.htm - * Based on VESA framebuffer (c) 1998 Gerd Knorr - * - * 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. - * - */ - -#ifndef __linux_video_vga_h__ -#define __linux_video_vga_h__ - -/* Some of the code below is taken from SVGAlib. The original, - unmodified copyright notice for that code is below. */ -/* VGAlib version 1.2 - (c) 1993 Tommy Frandsen */ -/* */ -/* This library is free software; you can redistribute it and/or */ -/* modify it without any restrictions. This library is distributed */ -/* in the hope that it will be useful, but without any warranty. */ - -/* Multi-chipset support Copyright 1993 Harm Hanemaayer */ -/* partially copyrighted (C) 1993 by Hartmut Schirmer */ - -/* VGA data register ports */ -#define VGA_CRT_DC 0x3D5 /* CRT Controller Data Register - color emulation */ -#define VGA_CRT_DM 0x3B5 /* CRT Controller Data Register - mono emulation */ -#define VGA_ATT_R 0x3C1 /* Attribute Controller Data Read Register */ -#define VGA_ATT_W 0x3C0 /* Attribute Controller Data Write Register */ -#define VGA_GFX_D 0x3CF /* Graphics Controller Data Register */ -#define VGA_SEQ_D 0x3C5 /* Sequencer Data Register */ -#define VGA_MIS_R 0x3CC /* Misc Output Read Register */ -#define VGA_MIS_W 0x3C2 /* Misc Output Write Register */ -#define VGA_FTC_R 0x3CA /* Feature Control Read Register */ -#define VGA_IS1_RC 0x3DA /* Input Status Register 1 - color emulation */ -#define VGA_IS1_RM 0x3BA /* Input Status Register 1 - mono emulation */ -#define VGA_PEL_D 0x3C9 /* PEL Data Register */ -#define VGA_PEL_MSK 0x3C6 /* PEL mask register */ - -/* EGA-specific registers */ -#define EGA_GFX_E0 0x3CC /* Graphics enable processor 0 */ -#define EGA_GFX_E1 0x3CA /* Graphics enable processor 1 */ - -/* VGA index register ports */ -#define VGA_CRT_IC 0x3D4 /* CRT Controller Index - color emulation */ -#define VGA_CRT_IM 0x3B4 /* CRT Controller Index - mono emulation */ -#define VGA_ATT_IW 0x3C0 /* Attribute Controller Index & Data Write Register */ -#define VGA_GFX_I 0x3CE /* Graphics Controller Index */ -#define VGA_SEQ_I 0x3C4 /* Sequencer Index */ -#define VGA_PEL_IW 0x3C8 /* PEL Write Index */ -#define VGA_PEL_IR 0x3C7 /* PEL Read Index */ - -/* standard VGA indexes max counts */ -#define VGA_CRT_C 0x19 /* Number of CRT Controller Registers */ -#define VGA_ATT_C 0x15 /* Number of Attribute Controller Registers */ -#define VGA_GFX_C 0x09 /* Number of Graphics Controller Registers */ -#define VGA_SEQ_C 0x05 /* Number of Sequencer Registers */ -#define VGA_MIS_C 0x01 /* Number of Misc Output Register */ - -/* VGA misc register bit masks */ -#define VGA_MIS_COLOR 0x01 -#define VGA_MIS_ENB_MEM_ACCESS 0x02 -#define VGA_MIS_DCLK_28322_720 0x04 -#define VGA_MIS_ENB_PLL_LOAD (0x04 | 0x08) -#define VGA_MIS_SEL_HIGH_PAGE 0x20 - -/* VGA CRT controller register indices */ -#define VGA_CRTC_H_TOTAL 0 -#define VGA_CRTC_H_DISP 1 -#define VGA_CRTC_H_BLANK_START 2 -#define VGA_CRTC_H_BLANK_END 3 -#define VGA_CRTC_H_SYNC_START 4 -#define VGA_CRTC_H_SYNC_END 5 -#define VGA_CRTC_V_TOTAL 6 -#define VGA_CRTC_OVERFLOW 7 -#define VGA_CRTC_PRESET_ROW 8 -#define VGA_CRTC_MAX_SCAN 9 -#define VGA_CRTC_CURSOR_START 0x0A -#define VGA_CRTC_CURSOR_END 0x0B -#define VGA_CRTC_START_HI 0x0C -#define VGA_CRTC_START_LO 0x0D -#define VGA_CRTC_CURSOR_HI 0x0E -#define VGA_CRTC_CURSOR_LO 0x0F -#define VGA_CRTC_V_SYNC_START 0x10 -#define VGA_CRTC_V_SYNC_END 0x11 -#define VGA_CRTC_V_DISP_END 0x12 -#define VGA_CRTC_OFFSET 0x13 -#define VGA_CRTC_UNDERLINE 0x14 -#define VGA_CRTC_V_BLANK_START 0x15 -#define VGA_CRTC_V_BLANK_END 0x16 -#define VGA_CRTC_MODE 0x17 -#define VGA_CRTC_LINE_COMPARE 0x18 -#define VGA_CRTC_REGS VGA_CRT_C - -/* VGA CRT controller bit masks */ -#define VGA_CR11_LOCK_CR0_CR7 0x80 /* lock writes to CR0 - CR7 */ -#define VGA_CR17_H_V_SIGNALS_ENABLED 0x80 - -/* VGA attribute controller register indices */ -#define VGA_ATC_PALETTE0 0x00 -#define VGA_ATC_PALETTE1 0x01 -#define VGA_ATC_PALETTE2 0x02 -#define VGA_ATC_PALETTE3 0x03 -#define VGA_ATC_PALETTE4 0x04 -#define VGA_ATC_PALETTE5 0x05 -#define VGA_ATC_PALETTE6 0x06 -#define VGA_ATC_PALETTE7 0x07 -#define VGA_ATC_PALETTE8 0x08 -#define VGA_ATC_PALETTE9 0x09 -#define VGA_ATC_PALETTEA 0x0A -#define VGA_ATC_PALETTEB 0x0B -#define VGA_ATC_PALETTEC 0x0C -#define VGA_ATC_PALETTED 0x0D -#define VGA_ATC_PALETTEE 0x0E -#define VGA_ATC_PALETTEF 0x0F -#define VGA_ATC_MODE 0x10 -#define VGA_ATC_OVERSCAN 0x11 -#define VGA_ATC_PLANE_ENABLE 0x12 -#define VGA_ATC_PEL 0x13 -#define VGA_ATC_COLOR_PAGE 0x14 - -#define VGA_AR_ENABLE_DISPLAY 0x20 - -/* VGA sequencer register indices */ -#define VGA_SEQ_RESET 0x00 -#define VGA_SEQ_CLOCK_MODE 0x01 -#define VGA_SEQ_PLANE_WRITE 0x02 -#define VGA_SEQ_CHARACTER_MAP 0x03 -#define VGA_SEQ_MEMORY_MODE 0x04 - -/* VGA sequencer register bit masks */ -#define VGA_SR01_CHAR_CLK_8DOTS 0x01 /* bit 0: character clocks 8 dots wide are generated */ -#define VGA_SR01_SCREEN_OFF 0x20 /* bit 5: Screen is off */ -#define VGA_SR02_ALL_PLANES 0x0F /* bits 3-0: enable access to all planes */ -#define VGA_SR04_EXT_MEM 0x02 /* bit 1: allows complete mem access to 256K */ -#define VGA_SR04_SEQ_MODE 0x04 /* bit 2: directs system to use a sequential addressing mode */ -#define VGA_SR04_CHN_4M 0x08 /* bit 3: selects modulo 4 addressing for CPU access to display memory */ - -/* VGA graphics controller register indices */ -#define VGA_GFX_SR_VALUE 0x00 -#define VGA_GFX_SR_ENABLE 0x01 -#define VGA_GFX_COMPARE_VALUE 0x02 -#define VGA_GFX_DATA_ROTATE 0x03 -#define VGA_GFX_PLANE_READ 0x04 -#define VGA_GFX_MODE 0x05 -#define VGA_GFX_MISC 0x06 -#define VGA_GFX_COMPARE_MASK 0x07 -#define VGA_GFX_BIT_MASK 0x08 - -/* VGA graphics controller bit masks */ -#define VGA_GR06_GRAPHICS_MODE 0x01 - -#endif /* __linux_video_vga_h__ */ diff --git a/qemu/hw/display/vga_int.h b/qemu/hw/display/vga_int.h deleted file mode 100644 index bdb43a5a3..000000000 --- a/qemu/hw/display/vga_int.h +++ /dev/null @@ -1,227 +0,0 @@ -/* - * QEMU internal VGA defines. - * - * Copyright (c) 2003-2004 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef HW_VGA_INT_H -#define HW_VGA_INT_H 1 - -#include -#include "exec/memory.h" - -#define ST01_V_RETRACE 0x08 -#define ST01_DISP_ENABLE 0x01 - -#define VBE_DISPI_MAX_XRES 16000 -#define VBE_DISPI_MAX_YRES 12000 -#define VBE_DISPI_MAX_BPP 32 - -#define VBE_DISPI_INDEX_ID 0x0 -#define VBE_DISPI_INDEX_XRES 0x1 -#define VBE_DISPI_INDEX_YRES 0x2 -#define VBE_DISPI_INDEX_BPP 0x3 -#define VBE_DISPI_INDEX_ENABLE 0x4 -#define VBE_DISPI_INDEX_BANK 0x5 -#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 -#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 -#define VBE_DISPI_INDEX_X_OFFSET 0x8 -#define VBE_DISPI_INDEX_Y_OFFSET 0x9 -#define VBE_DISPI_INDEX_NB 0xa /* size of vbe_regs[] */ -#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa /* read-only, not in vbe_regs */ - -#define VBE_DISPI_ID0 0xB0C0 -#define VBE_DISPI_ID1 0xB0C1 -#define VBE_DISPI_ID2 0xB0C2 -#define VBE_DISPI_ID3 0xB0C3 -#define VBE_DISPI_ID4 0xB0C4 -#define VBE_DISPI_ID5 0xB0C5 - -#define VBE_DISPI_DISABLED 0x00 -#define VBE_DISPI_ENABLED 0x01 -#define VBE_DISPI_GETCAPS 0x02 -#define VBE_DISPI_8BIT_DAC 0x20 -#define VBE_DISPI_LFB_ENABLED 0x40 -#define VBE_DISPI_NOCLEARMEM 0x80 - -#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 - -#define CH_ATTR_SIZE (160 * 100) -#define VGA_MAX_HEIGHT 2048 - -struct vga_precise_retrace { - int64_t ticks_per_char; - int64_t total_chars; - int htotal; - int hstart; - int hend; - int vstart; - int vend; - int freq; -}; - -union vga_retrace { - struct vga_precise_retrace precise; -}; - -struct VGACommonState; -typedef uint8_t (* vga_retrace_fn)(struct VGACommonState *s); -typedef void (* vga_update_retrace_info_fn)(struct VGACommonState *s); - -typedef struct VGACommonState { - MemoryRegion *legacy_address_space; - uint8_t *vram_ptr; - MemoryRegion vram; - MemoryRegion vram_vbe; - uint32_t vram_size; - uint32_t vram_size_mb; /* property */ - uint32_t vbe_size; - uint32_t latch; - bool has_chain4_alias; - MemoryRegion chain4_alias; - uint8_t sr_index; - uint8_t sr[256]; - uint8_t gr_index; - uint8_t gr[256]; - uint8_t ar_index; - uint8_t ar[21]; - int ar_flip_flop; - uint8_t cr_index; - uint8_t cr[256]; /* CRT registers */ - uint8_t msr; /* Misc Output Register */ - uint8_t fcr; /* Feature Control Register */ - uint8_t st00; /* status 0 */ - uint8_t st01; /* status 1 */ - uint8_t dac_state; - uint8_t dac_sub_index; - uint8_t dac_read_index; - uint8_t dac_write_index; - uint8_t dac_cache[3]; /* used when writing */ - int dac_8bit; - uint8_t palette[768]; - int32_t bank_offset; - int (*get_bpp)(struct VGACommonState *s); - void (*get_offsets)(struct VGACommonState *s, - uint32_t *pline_offset, - uint32_t *pstart_addr, - uint32_t *pline_compare); - void (*get_resolution)(struct VGACommonState *s, - int *pwidth, - int *pheight); - PortioList vga_port_list; - PortioList vbe_port_list; - /* bochs vbe state */ - uint16_t vbe_index; - uint16_t vbe_regs[VBE_DISPI_INDEX_NB]; - uint32_t vbe_start_addr; - uint32_t vbe_line_offset; - uint32_t vbe_bank_mask; - int vbe_mapped; - /* display refresh support */ - QemuConsole *con; - uint32_t font_offsets[2]; - int graphic_mode; - uint8_t shift_control; - uint8_t double_scan; - uint32_t line_offset; - uint32_t line_compare; - uint32_t start_addr; - uint32_t plane_updated; - uint32_t last_line_offset; - uint8_t last_cw, last_ch; - uint32_t last_width, last_height; /* in chars or pixels */ - uint32_t last_scr_width, last_scr_height; /* in pixels */ - uint32_t last_depth; /* in bits */ - bool last_byteswap; - bool force_shadow; - uint8_t cursor_start, cursor_end; - bool cursor_visible_phase; - int64_t cursor_blink_time; - uint32_t cursor_offset; - const GraphicHwOps *hw_ops; - bool full_update_text; - bool full_update_gfx; - bool big_endian_fb; - bool default_endian_fb; - /* hardware mouse cursor support */ - uint32_t invalidated_y_table[VGA_MAX_HEIGHT / 32]; - uint32_t hw_cursor_x; - uint32_t hw_cursor_y; - void (*cursor_invalidate)(struct VGACommonState *s); - void (*cursor_draw_line)(struct VGACommonState *s, uint8_t *d, int y); - /* tell for each page if it has been updated since the last time */ - uint32_t last_palette[256]; - uint32_t last_ch_attr[CH_ATTR_SIZE]; /* XXX: make it dynamic */ - /* retrace */ - vga_retrace_fn retrace; - vga_update_retrace_info_fn update_retrace_info; - union vga_retrace retrace_info; - uint8_t is_vbe_vmstate; -} VGACommonState; - -static inline int c6_to_8(int v) -{ - int b; - v &= 0x3f; - b = v & 1; - return (v << 2) | (b << 1) | b; -} - -void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate); -void vga_init(VGACommonState *s, Object *obj, MemoryRegion *address_space, - MemoryRegion *address_space_io, bool init_vga_ports); -MemoryRegion *vga_init_io(VGACommonState *s, Object *obj, - const MemoryRegionPortio **vga_ports, - const MemoryRegionPortio **vbe_ports); -void vga_common_reset(VGACommonState *s); - -void vga_sync_dirty_bitmap(VGACommonState *s); -void vga_dirty_log_start(VGACommonState *s); -void vga_dirty_log_stop(VGACommonState *s); - -extern const VMStateDescription vmstate_vga_common; -uint32_t vga_ioport_read(void *opaque, uint32_t addr); -void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val); -uint32_t vga_mem_readb(VGACommonState *s, hwaddr addr); -void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val); -void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2); - -int vga_ioport_invalid(VGACommonState *s, uint32_t addr); - -void vga_init_vbe(VGACommonState *s, Object *obj, MemoryRegion *address_space); -uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr); -void vbe_ioport_write_index(void *opaque, uint32_t addr, uint32_t val); -void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val); - -extern const uint8_t sr_mask[8]; -extern const uint8_t gr_mask[16]; - -#define VGABIOS_FILENAME "vgabios.bin" -#define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin" - -extern const MemoryRegionOps vga_mem_ops; - -/* vga-pci.c */ -void pci_std_vga_mmio_region_init(VGACommonState *s, - MemoryRegion *parent, - MemoryRegion *subs, - bool qext); - -#endif diff --git a/qemu/hw/display/virtio-gpu-3d.c b/qemu/hw/display/virtio-gpu-3d.c deleted file mode 100644 index fa192946a..000000000 --- a/qemu/hw/display/virtio-gpu-3d.c +++ /dev/null @@ -1,606 +0,0 @@ -/* - * Virtio GPU Device - * - * Copyright Red Hat, Inc. 2013-2014 - * - * Authors: - * Dave Airlie - * Gerd Hoffmann - * - * 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 deleted file mode 100644 index a71b230d3..000000000 --- a/qemu/hw/display/virtio-gpu-pci.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Virtio video device - * - * Copyright Red Hat - * - * Authors: - * Dave Airlie - * - * 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" -#include "hw/virtio/virtio-pci.h" -#include "hw/virtio/virtio-gpu.h" - -static Property virtio_gpu_pci_properties[] = { - DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_gpu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - VirtIOGPUPCI *vgpu = VIRTIO_GPU_PCI(vpci_dev); - VirtIOGPU *g = &vgpu->vdev; - DeviceState *vdev = DEVICE(&vgpu->vdev); - int i; - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - /* force virtio-1.0 */ - vpci_dev->flags &= ~VIRTIO_PCI_FLAG_DISABLE_MODERN; - vpci_dev->flags |= VIRTIO_PCI_FLAG_DISABLE_LEGACY; - object_property_set_bool(OBJECT(vdev), true, "realized", errp); - - for (i = 0; i < g->conf.max_outputs; i++) { - object_property_set_link(OBJECT(g->scanout[i].con), - OBJECT(vpci_dev), - "device", errp); - } -} - -static void virtio_gpu_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); - dc->props = virtio_gpu_pci_properties; - k->realize = virtio_gpu_pci_realize; - pcidev_k->class_id = PCI_CLASS_DISPLAY_OTHER; -} - -static void virtio_gpu_initfn(Object *obj) -{ - VirtIOGPUPCI *dev = VIRTIO_GPU_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_GPU); -} - -static const TypeInfo virtio_gpu_pci_info = { - .name = TYPE_VIRTIO_GPU_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VirtIOGPUPCI), - .instance_init = virtio_gpu_initfn, - .class_init = virtio_gpu_pci_class_init, -}; - -static void virtio_gpu_pci_register_types(void) -{ - type_register_static(&virtio_gpu_pci_info); -} -type_init(virtio_gpu_pci_register_types) diff --git a/qemu/hw/display/virtio-gpu.c b/qemu/hw/display/virtio-gpu.c deleted file mode 100644 index c181fb364..000000000 --- a/qemu/hw/display/virtio-gpu.c +++ /dev/null @@ -1,1087 +0,0 @@ -/* - * Virtio GPU Device - * - * Copyright Red Hat, Inc. 2013-2014 - * - * Authors: - * Dave Airlie - * Gerd Hoffmann - * - * 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" -#include "trace.h" -#include "hw/virtio/virtio.h" -#include "hw/virtio/virtio-gpu.h" -#include "hw/virtio/virtio-bus.h" - -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) -{ - struct virtio_gpu_simple_resource *res; - uint32_t pixels; - - res = virtio_gpu_find_resource(g, resource_id); - if (!res) { - return; - } - - if (pixman_image_get_width(res->image) != s->current_cursor->width || - pixman_image_get_height(res->image) != s->current_cursor->height) { - return; - } - - pixels = s->current_cursor->width * s->current_cursor->height; - memcpy(s->current_cursor->data, - pixman_image_get_data(res->image), - 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]; - - 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); - } - - s->current_cursor->hot_x = cursor->hot_x; - s->current_cursor->hot_y = cursor->hot_y; - - if (cursor->resource_id > 0) { - VIRGL(g, update_cursor_data_virgl, update_cursor_data_simple, - g, s, cursor->resource_id); - } - dpy_cursor_define(s->con, s->current_cursor); - } - dpy_mouse_set(s->con, cursor->pos.x, cursor->pos.y, - cursor->resource_id ? 1 : 0); -} - -static void virtio_gpu_get_config(VirtIODevice *vdev, uint8_t *config) -{ - VirtIOGPU *g = VIRTIO_GPU(vdev); - memcpy(config, &g->virtio_config, sizeof(g->virtio_config)); -} - -static void virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *config) -{ - VirtIOGPU *g = VIRTIO_GPU(vdev); - struct virtio_gpu_config vgconfig; - - memcpy(&vgconfig, config, sizeof(g->virtio_config)); - - if (vgconfig.events_clear) { - g->virtio_config.events_read &= ~vgconfig.events_clear; - } -} - -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; - virtio_notify_config(&g->parent_obj); -} - -static struct virtio_gpu_simple_resource * -virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id) -{ - struct virtio_gpu_simple_resource *res; - - QTAILQ_FOREACH(res, &g->reslist, next) { - if (res->resource_id == resource_id) { - return res; - } - } - return NULL; -} - -void virtio_gpu_ctrl_response(VirtIOGPU *g, - struct virtio_gpu_ctrl_command *cmd, - struct virtio_gpu_ctrl_hdr *resp, - size_t resp_len) -{ - size_t s; - - if (cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE) { - resp->flags |= VIRTIO_GPU_FLAG_FENCE; - resp->fence_id = cmd->cmd_hdr.fence_id; - resp->ctx_id = cmd->cmd_hdr.ctx_id; - } - s = iov_from_buf(cmd->elem.in_sg, cmd->elem.in_num, 0, resp, resp_len); - if (s != resp_len) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: response size incorrect %zu vs %zu\n", - __func__, s, resp_len); - } - virtqueue_push(cmd->vq, &cmd->elem, s); - virtio_notify(VIRTIO_DEVICE(g), cmd->vq); - cmd->finished = true; -} - -void virtio_gpu_ctrl_response_nodata(VirtIOGPU *g, - struct virtio_gpu_ctrl_command *cmd, - enum virtio_gpu_ctrl_type type) -{ - struct virtio_gpu_ctrl_hdr resp; - - memset(&resp, 0, sizeof(resp)); - resp.type = type; - virtio_gpu_ctrl_response(g, cmd, &resp, sizeof(resp)); -} - -static void -virtio_gpu_fill_display_info(VirtIOGPU *g, - struct virtio_gpu_resp_display_info *dpy_info) -{ - int i; - - for (i = 0; i < g->conf.max_outputs; i++) { - if (g->enabled_output_bitmask & (1 << i)) { - dpy_info->pmodes[i].enabled = 1; - dpy_info->pmodes[i].r.width = g->req_state[i].width; - dpy_info->pmodes[i].r.height = g->req_state[i].height; - } - } -} - -void virtio_gpu_get_display_info(VirtIOGPU *g, - struct virtio_gpu_ctrl_command *cmd) -{ - struct virtio_gpu_resp_display_info display_info; - - trace_virtio_gpu_cmd_get_display_info(); - memset(&display_info, 0, sizeof(display_info)); - display_info.hdr.type = VIRTIO_GPU_RESP_OK_DISPLAY_INFO; - virtio_gpu_fill_display_info(g, &display_info); - virtio_gpu_ctrl_response(g, cmd, &display_info.hdr, - sizeof(display_info)); -} - -static pixman_format_code_t get_pixman_format(uint32_t virtio_gpu_format) -{ - switch (virtio_gpu_format) { -#ifdef HOST_WORDS_BIGENDIAN - case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: - return PIXMAN_b8g8r8x8; - case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: - return PIXMAN_b8g8r8a8; - case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: - return PIXMAN_x8r8g8b8; - case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: - return PIXMAN_a8r8g8b8; - case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: - return PIXMAN_r8g8b8x8; - case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: - return PIXMAN_r8g8b8a8; - case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: - return PIXMAN_x8b8g8r8; - case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: - return PIXMAN_a8b8g8r8; -#else - case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: - return PIXMAN_x8r8g8b8; - case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: - return PIXMAN_a8r8g8b8; - case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: - return PIXMAN_b8g8r8x8; - case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: - return PIXMAN_b8g8r8a8; - case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: - return PIXMAN_x8b8g8r8; - case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: - return PIXMAN_a8b8g8r8; - case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: - return PIXMAN_r8g8b8x8; - case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: - return PIXMAN_r8g8b8a8; -#endif - default: - return 0; - } -} - -static void virtio_gpu_resource_create_2d(VirtIOGPU *g, - struct virtio_gpu_ctrl_command *cmd) -{ - pixman_format_code_t pformat; - struct virtio_gpu_simple_resource *res; - struct virtio_gpu_resource_create_2d c2d; - - VIRTIO_GPU_FILL_CMD(c2d); - trace_virtio_gpu_cmd_res_create_2d(c2d.resource_id, c2d.format, - c2d.width, c2d.height); - - if (c2d.resource_id == 0) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not allowed\n", - __func__); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; - return; - } - - res = virtio_gpu_find_resource(g, c2d.resource_id); - if (res) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already exists %d\n", - __func__, c2d.resource_id); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; - return; - } - - res = g_new0(struct virtio_gpu_simple_resource, 1); - - res->width = c2d.width; - res->height = c2d.height; - res->format = c2d.format; - res->resource_id = c2d.resource_id; - - pformat = get_pixman_format(c2d.format); - if (!pformat) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: host couldn't handle guest format %d\n", - __func__, c2d.format); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; - return; - } - res->image = pixman_image_create_bits(pformat, - c2d.width, - c2d.height, - NULL, 0); - - if (!res->image) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: resource creation failed %d %d %d\n", - __func__, c2d.resource_id, c2d.width, c2d.height); - g_free(res); - cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; - return; - } - - QTAILQ_INSERT_HEAD(&g->reslist, res, next); -} - -static void virtio_gpu_resource_destroy(VirtIOGPU *g, - struct virtio_gpu_simple_resource *res) -{ - pixman_image_unref(res->image); - QTAILQ_REMOVE(&g->reslist, res, next); - g_free(res); -} - -static void virtio_gpu_resource_unref(VirtIOGPU *g, - struct virtio_gpu_ctrl_command *cmd) -{ - struct virtio_gpu_simple_resource *res; - struct virtio_gpu_resource_unref unref; - - VIRTIO_GPU_FILL_CMD(unref); - trace_virtio_gpu_cmd_res_unref(unref.resource_id); - - res = virtio_gpu_find_resource(g, unref.resource_id); - if (!res) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n", - __func__, unref.resource_id); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; - return; - } - virtio_gpu_resource_destroy(g, res); -} - -static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g, - struct virtio_gpu_ctrl_command *cmd) -{ - struct virtio_gpu_simple_resource *res; - int h; - uint32_t src_offset, dst_offset, stride; - int bpp; - pixman_format_code_t format; - struct virtio_gpu_transfer_to_host_2d t2d; - - VIRTIO_GPU_FILL_CMD(t2d); - trace_virtio_gpu_cmd_res_xfer_toh_2d(t2d.resource_id); - - res = virtio_gpu_find_resource(g, t2d.resource_id); - if (!res || !res->iov) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n", - __func__, t2d.resource_id); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; - return; - } - - if (t2d.r.x > res->width || - t2d.r.y > res->height || - t2d.r.width > res->width || - t2d.r.height > res->height || - t2d.r.x + t2d.r.width > res->width || - t2d.r.y + t2d.r.height > res->height) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: transfer bounds outside resource" - " bounds for resource %d: %d %d %d %d vs %d %d\n", - __func__, t2d.resource_id, t2d.r.x, t2d.r.y, - t2d.r.width, t2d.r.height, res->width, res->height); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; - return; - } - - format = pixman_image_get_format(res->image); - bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8; - stride = pixman_image_get_stride(res->image); - - if (t2d.offset || t2d.r.x || t2d.r.y || - t2d.r.width != pixman_image_get_width(res->image)) { - void *img_data = pixman_image_get_data(res->image); - for (h = 0; h < t2d.r.height; h++) { - src_offset = t2d.offset + stride * h; - dst_offset = (t2d.r.y + h) * stride + (t2d.r.x * bpp); - - iov_to_buf(res->iov, res->iov_cnt, src_offset, - (uint8_t *)img_data - + dst_offset, t2d.r.width * bpp); - } - } else { - iov_to_buf(res->iov, res->iov_cnt, 0, - pixman_image_get_data(res->image), - pixman_image_get_stride(res->image) - * pixman_image_get_height(res->image)); - } -} - -static void virtio_gpu_resource_flush(VirtIOGPU *g, - struct virtio_gpu_ctrl_command *cmd) -{ - struct virtio_gpu_simple_resource *res; - struct virtio_gpu_resource_flush rf; - pixman_region16_t flush_region; - 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); - - res = virtio_gpu_find_resource(g, rf.resource_id); - if (!res) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n", - __func__, rf.resource_id); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; - return; - } - - if (rf.r.x > res->width || - rf.r.y > res->height || - rf.r.width > res->width || - rf.r.height > res->height || - rf.r.x + rf.r.width > res->width || - rf.r.y + rf.r.height > res->height) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: flush bounds outside resource" - " bounds for resource %d: %d %d %d %d vs %d %d\n", - __func__, rf.resource_id, rf.r.x, rf.r.y, - rf.r.width, rf.r.height, res->width, res->height); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; - return; - } - - pixman_region_init_rect(&flush_region, - rf.r.x, rf.r.y, rf.r.width, rf.r.height); - for (i = 0; i < VIRTIO_GPU_MAX_SCANOUT; i++) { - struct virtio_gpu_scanout *scanout; - pixman_region16_t region, finalregion; - pixman_box16_t *extents; - - if (!(res->scanout_bitmask & (1 << i))) { - continue; - } - scanout = &g->scanout[i]; - - pixman_region_init(&finalregion); - pixman_region_init_rect(®ion, scanout->x, scanout->y, - scanout->width, scanout->height); - - pixman_region_intersect(&finalregion, &flush_region, ®ion); - pixman_region_translate(&finalregion, -scanout->x, -scanout->y); - extents = pixman_region_extents(&finalregion); - /* work out the area we need to update for each console */ - dpy_gfx_update(g->scanout[i].con, - extents->x1, extents->y1, - extents->x2 - extents->x1, - extents->y2 - extents->y1); - - pixman_region_fini(®ion); - pixman_region_fini(&finalregion); - } - pixman_region_fini(&flush_region); -} - -static void virtio_gpu_set_scanout(VirtIOGPU *g, - struct virtio_gpu_ctrl_command *cmd) -{ - struct virtio_gpu_simple_resource *res; - struct virtio_gpu_scanout *scanout; - pixman_format_code_t format; - uint32_t offset; - int bpp; - struct virtio_gpu_set_scanout ss; - - 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); - - g->enable = 1; - if (ss.resource_id == 0) { - scanout = &g->scanout[ss.scanout_id]; - if (scanout->resource_id) { - res = virtio_gpu_find_resource(g, scanout->resource_id); - if (res) { - res->scanout_bitmask &= ~(1 << ss.scanout_id); - } - } - if (ss.scanout_id == 0 || - ss.scanout_id >= g->conf.max_outputs) { - 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; - } - dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL); - scanout->ds = NULL; - scanout->width = 0; - scanout->height = 0; - return; - } - - /* create a surface for this scanout */ - if (ss.scanout_id >= VIRTIO_GPU_MAX_SCANOUT || - ss.scanout_id >= g->conf.max_outputs) { - 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; - } - - res = virtio_gpu_find_resource(g, ss.resource_id); - if (!res) { - 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; - } - - if (ss.r.x > res->width || - ss.r.y > res->height || - ss.r.width > res->width || - ss.r.height > res->height || - ss.r.x + ss.r.width > res->width || - ss.r.y + ss.r.height > res->height) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for" - " resource %d, (%d,%d)+%d,%d vs %d %d\n", - __func__, ss.scanout_id, ss.resource_id, ss.r.x, ss.r.y, - ss.r.width, ss.r.height, res->width, res->height); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; - return; - } - - scanout = &g->scanout[ss.scanout_id]; - - format = pixman_image_get_format(res->image); - bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8; - offset = (ss.r.x * bpp) + ss.r.y * pixman_image_get_stride(res->image); - if (!scanout->ds || surface_data(scanout->ds) - != ((uint8_t *)pixman_image_get_data(res->image) + offset) || - scanout->width != ss.r.width || - scanout->height != ss.r.height) { - /* realloc the surface ptr */ - scanout->ds = qemu_create_displaysurface_pixman(res->image); - if (!scanout->ds) { - cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; - return; - } - dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, scanout->ds); - } - - res->scanout_bitmask |= (1 << ss.scanout_id); - scanout->resource_id = ss.resource_id; - scanout->x = ss.r.x; - scanout->y = ss.r.y; - scanout->width = ss.r.width; - scanout->height = ss.r.height; -} - -int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab, - struct virtio_gpu_ctrl_command *cmd, - struct iovec **iov) -{ - struct virtio_gpu_mem_entry *ents; - size_t esize, s; - int i; - - if (ab->nr_entries > 16384) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: nr_entries is too big (%d > 16384)\n", - __func__, ab->nr_entries); - return -1; - } - - esize = sizeof(*ents) * ab->nr_entries; - ents = g_malloc(esize); - s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, - sizeof(*ab), ents, esize); - if (s != esize) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: command data size incorrect %zu vs %zu\n", - __func__, s, esize); - g_free(ents); - return -1; - } - - *iov = g_malloc0(sizeof(struct iovec) * ab->nr_entries); - for (i = 0; i < ab->nr_entries; i++) { - hwaddr len = ents[i].length; - (*iov)[i].iov_len = ents[i].length; - (*iov)[i].iov_base = cpu_physical_memory_map(ents[i].addr, &len, 1); - if (!(*iov)[i].iov_base || len != ents[i].length) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to map MMIO memory for" - " resource %d element %d\n", - __func__, ab->resource_id, i); - virtio_gpu_cleanup_mapping_iov(*iov, i); - g_free(ents); - *iov = NULL; - return -1; - } - } - g_free(ents); - return 0; -} - -void virtio_gpu_cleanup_mapping_iov(struct iovec *iov, uint32_t count) -{ - int i; - - for (i = 0; i < count; i++) { - 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); - res->iov = NULL; - res->iov_cnt = 0; -} - -static void -virtio_gpu_resource_attach_backing(VirtIOGPU *g, - struct virtio_gpu_ctrl_command *cmd) -{ - struct virtio_gpu_simple_resource *res; - struct virtio_gpu_resource_attach_backing ab; - int ret; - - VIRTIO_GPU_FILL_CMD(ab); - trace_virtio_gpu_cmd_res_back_attach(ab.resource_id); - - res = virtio_gpu_find_resource(g, ab.resource_id); - if (!res) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n", - __func__, ab.resource_id); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; - return; - } - - ret = virtio_gpu_create_mapping_iov(&ab, cmd, &res->iov); - if (ret != 0) { - cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; - return; - } - - res->iov_cnt = ab.nr_entries; -} - -static void -virtio_gpu_resource_detach_backing(VirtIOGPU *g, - struct virtio_gpu_ctrl_command *cmd) -{ - struct virtio_gpu_simple_resource *res; - struct virtio_gpu_resource_detach_backing detach; - - VIRTIO_GPU_FILL_CMD(detach); - trace_virtio_gpu_cmd_res_back_detach(detach.resource_id); - - res = virtio_gpu_find_resource(g, detach.resource_id); - if (!res || !res->iov) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n", - __func__, detach.resource_id); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; - return; - } - virtio_gpu_cleanup_mapping(res); -} - -static void virtio_gpu_simple_process_cmd(VirtIOGPU *g, - struct virtio_gpu_ctrl_command *cmd) -{ - VIRTIO_GPU_FILL_CMD(cmd->cmd_hdr); - - switch (cmd->cmd_hdr.type) { - case VIRTIO_GPU_CMD_GET_DISPLAY_INFO: - virtio_gpu_get_display_info(g, cmd); - break; - case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: - virtio_gpu_resource_create_2d(g, cmd); - break; - case VIRTIO_GPU_CMD_RESOURCE_UNREF: - virtio_gpu_resource_unref(g, cmd); - break; - case VIRTIO_GPU_CMD_RESOURCE_FLUSH: - virtio_gpu_resource_flush(g, cmd); - break; - case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: - virtio_gpu_transfer_to_host_2d(g, cmd); - break; - case VIRTIO_GPU_CMD_SET_SCANOUT: - virtio_gpu_set_scanout(g, cmd); - break; - case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: - virtio_gpu_resource_attach_backing(g, cmd); - break; - case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: - virtio_gpu_resource_detach_backing(g, cmd); - break; - default: - cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; - break; - } - if (!cmd->finished) { - virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error ? cmd->error : - VIRTIO_GPU_RESP_OK_NODATA); - } -} - -static void virtio_gpu_handle_ctrl_cb(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIOGPU *g = VIRTIO_GPU(vdev); - qemu_bh_schedule(g->ctrl_bh); -} - -static void virtio_gpu_handle_cursor_cb(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIOGPU *g = VIRTIO_GPU(vdev); - 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); - struct virtio_gpu_ctrl_command *cmd; - - if (!virtio_queue_ready(vq)) { - return; - } - -#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; - cmd->waiting = false; - QTAILQ_INSERT_TAIL(&g->cmdq, cmd, next); - cmd = virtqueue_pop(vq, sizeof(struct virtio_gpu_ctrl_command)); - } - - virtio_gpu_process_cmdq(g); - -#ifdef CONFIG_VIRGL - if (g->use_virgl_renderer) { - virtio_gpu_virgl_fence_poll(g); - } -#endif -} - -static void virtio_gpu_ctrl_bh(void *opaque) -{ - VirtIOGPU *g = opaque; - virtio_gpu_handle_ctrl(&g->parent_obj, g->ctrl_vq); -} - -static void virtio_gpu_handle_cursor(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIOGPU *g = VIRTIO_GPU(vdev); - VirtQueueElement *elem; - size_t s; - struct virtio_gpu_update_cursor cursor_info; - - if (!virtio_queue_ready(vq)) { - return; - } - 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, - "%s: cursor size incorrect %zu vs %zu\n", - __func__, s, sizeof(cursor_info)); - } else { - update_cursor(g, &cursor_info); - } - virtqueue_push(vq, elem, 0); - virtio_notify(vdev, vq); - g_free(elem); - } -} - -static void virtio_gpu_cursor_bh(void *opaque) -{ - VirtIOGPU *g = opaque; - virtio_gpu_handle_cursor(&g->parent_obj, g->cursor_vq); -} - -static void virtio_gpu_invalidate_display(void *opaque) -{ -} - -static void virtio_gpu_update_display(void *opaque) -{ -} - -static void virtio_gpu_text_update(void *opaque, console_ch_t *chardata) -{ -} - -static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) -{ - VirtIOGPU *g = opaque; - - if (idx > g->conf.max_outputs) { - return -1; - } - - g->req_state[idx].x = info->xoff; - g->req_state[idx].y = info->yoff; - g->req_state[idx].width = info->width; - g->req_state[idx].height = info->height; - - if (info->width && info->height) { - g->enabled_output_bitmask |= (1 << idx); - } else { - g->enabled_output_bitmask &= ~(1 << idx); - } - - /* send event to guest */ - virtio_gpu_notify_event(g, VIRTIO_GPU_EVENT_DISPLAY); - 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); - g->virtio_config.num_scanouts = g->conf.max_outputs; - virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU, - g->config_size); - - g->req_state[0].width = 1024; - g->req_state[0].height = 768; - - 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; - g->qdev = qdev; - - for (i = 0; i < g->conf.max_outputs; i++) { - g->scanout[i].con = - graphic_console_init(DEVICE(g), i, &virtio_gpu_ops, g); - if (i > 0) { - 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) -{ -} - -static void virtio_gpu_reset(VirtIODevice *vdev) -{ - VirtIOGPU *g = VIRTIO_GPU(vdev); - struct virtio_gpu_simple_resource *res, *tmp; - int i; - - g->enable = 0; - - QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) { - virtio_gpu_resource_destroy(g, res); - } - for (i = 0; i < g->conf.max_outputs; i++) { -#if 0 - g->req_state[i].x = 0; - g->req_state[i].y = 0; - if (i == 0) { - g->req_state[0].width = 1024; - g->req_state[0].height = 768; - } else { - g->req_state[i].width = 0; - g->req_state[i].height = 0; - } -#endif - g->scanout[i].resource_id = 0; - g->scanout[i].width = 0; - g->scanout[i].height = 0; - g->scanout[i].x = 0; - g->scanout[i].y = 0; - 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(), -}; - -static void virtio_gpu_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - - vdc->realize = virtio_gpu_device_realize; - 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; - - dc->props = virtio_gpu_properties; -} - -static const TypeInfo virtio_gpu_info = { - .name = TYPE_VIRTIO_GPU, - .parent = TYPE_VIRTIO_DEVICE, - .instance_size = sizeof(VirtIOGPU), - .instance_init = virtio_gpu_instance_init, - .class_init = virtio_gpu_class_init, -}; - -static void virtio_register_types(void) -{ - type_register_static(&virtio_gpu_info); -} - -type_init(virtio_register_types) - -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctrl_hdr) != 24); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_update_cursor) != 56); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_unref) != 32); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_2d) != 40); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_set_scanout) != 48); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_flush) != 48); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_to_host_2d) != 56); -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 deleted file mode 100644 index e58b165ae..000000000 --- a/qemu/hw/display/virtio-vga.c +++ /dev/null @@ -1,193 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "ui/console.h" -#include "vga_int.h" -#include "hw/virtio/virtio-pci.h" - -/* - * virtio-vga: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_VGA "virtio-vga" -#define VIRTIO_VGA(obj) \ - OBJECT_CHECK(VirtIOVGA, (obj), TYPE_VIRTIO_VGA) - -typedef struct VirtIOVGA { - VirtIOPCIProxy parent_obj; - VirtIOGPU vdev; - VGACommonState vga; - MemoryRegion vga_mrs[3]; -} VirtIOVGA; - -static void virtio_vga_invalidate_display(void *opaque) -{ - VirtIOVGA *vvga = opaque; - - if (vvga->vdev.enable) { - virtio_gpu_ops.invalidate(&vvga->vdev); - } else { - vvga->vga.hw_ops->invalidate(&vvga->vga); - } -} - -static void virtio_vga_update_display(void *opaque) -{ - VirtIOVGA *vvga = opaque; - - if (vvga->vdev.enable) { - virtio_gpu_ops.gfx_update(&vvga->vdev); - } else { - vvga->vga.hw_ops->gfx_update(&vvga->vga); - } -} - -static void virtio_vga_text_update(void *opaque, console_ch_t *chardata) -{ - VirtIOVGA *vvga = opaque; - - if (vvga->vdev.enable) { - if (virtio_gpu_ops.text_update) { - virtio_gpu_ops.text_update(&vvga->vdev, chardata); - } - } else { - if (vvga->vga.hw_ops->text_update) { - vvga->vga.hw_ops->text_update(&vvga->vga, chardata); - } - } -} - -static int virtio_vga_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) -{ - VirtIOVGA *vvga = opaque; - - if (virtio_gpu_ops.ui_info) { - return virtio_gpu_ops.ui_info(&vvga->vdev, idx, 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 */ -static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - VirtIOVGA *vvga = VIRTIO_VGA(vpci_dev); - VirtIOGPU *g = &vvga->vdev; - VGACommonState *vga = &vvga->vga; - uint32_t offset; - int i; - - /* init vga compat bits */ - vga->vram_size_mb = 8; - vga_common_init(vga, OBJECT(vpci_dev), false); - vga_init(vga, OBJECT(vpci_dev), pci_address_space(&vpci_dev->pci_dev), - pci_address_space_io(&vpci_dev->pci_dev), true); - pci_register_bar(&vpci_dev->pci_dev, 0, - PCI_BASE_ADDRESS_MEM_PREFETCH, &vga->vram); - - /* - * Configure virtio bar and regions - * - * We use bar #2 for the mmio regions, to be compatible with stdvga. - * virtio regions are moved to the end of bar #2, to make room for - * the stdvga mmio registers at the start of bar #2. - */ - vpci_dev->modern_mem_bar = 2; - vpci_dev->msix_bar = 4; - offset = memory_region_size(&vpci_dev->modern_bar); - offset -= vpci_dev->notify.size; - vpci_dev->notify.offset = offset; - offset -= vpci_dev->device.size; - vpci_dev->device.offset = offset; - offset -= vpci_dev->isr.size; - vpci_dev->isr.offset = offset; - offset -= vpci_dev->common.size; - vpci_dev->common.offset = offset; - - /* init virtio bits */ - qdev_set_parent_bus(DEVICE(g), BUS(&vpci_dev->bus)); - /* force virtio-1.0 */ - vpci_dev->flags &= ~VIRTIO_PCI_FLAG_DISABLE_MODERN; - vpci_dev->flags |= VIRTIO_PCI_FLAG_DISABLE_LEGACY; - object_property_set_bool(OBJECT(g), true, "realized", errp); - - /* add stdvga mmio regions */ - pci_std_vga_mmio_region_init(vga, &vpci_dev->modern_bar, - vvga->vga_mrs, true); - - vga->con = g->scanout[0].con; - graphic_console_set_hwops(vga->con, &virtio_vga_ops, vvga); - - for (i = 0; i < g->conf.max_outputs; i++) { - object_property_set_link(OBJECT(g->scanout[i].con), - OBJECT(vpci_dev), - "device", errp); - } -} - -static void virtio_vga_reset(DeviceState *dev) -{ - VirtIOVGA *vvga = VIRTIO_VGA(dev); - vvga->vdev.enable = 0; - - vga_dirty_log_start(&vvga->vga); -} - -static Property virtio_vga_properties[] = { - DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_vga_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); - dc->props = virtio_vga_properties; - dc->reset = virtio_vga_reset; - dc->hotpluggable = false; - - k->realize = virtio_vga_realize; - pcidev_k->romfile = "vgabios-virtio.bin"; - pcidev_k->class_id = PCI_CLASS_DISPLAY_VGA; -} - -static void virtio_vga_inst_initfn(Object *obj) -{ - VirtIOVGA *dev = VIRTIO_VGA(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_GPU); -} - -static TypeInfo virtio_vga_info = { - .name = TYPE_VIRTIO_VGA, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(struct VirtIOVGA), - .instance_init = virtio_vga_inst_initfn, - .class_init = virtio_vga_class_init, -}; - -static void virtio_vga_register_types(void) -{ - type_register_static(&virtio_vga_info); -} - -type_init(virtio_vga_register_types) diff --git a/qemu/hw/display/vmware_vga.c b/qemu/hw/display/vmware_vga.c deleted file mode 100644 index 0c63fa851..000000000 --- a/qemu/hw/display/vmware_vga.c +++ /dev/null @@ -1,1370 +0,0 @@ -/* - * QEMU VMware-SVGA "chipset". - * - * Copyright (c) 2007 Andrzej Zaborowski - * - * 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/loader.h" -#include "trace.h" -#include "ui/console.h" -#include "ui/vnc.h" -#include "hw/pci/pci.h" - -#undef VERBOSE -#define HW_RECT_ACCEL -#define HW_FILL_ACCEL -#define HW_MOUSE_ACCEL - -#include "vga_int.h" - -/* See http://vmware-svga.sf.net/ for some documentation on VMWare SVGA */ - -struct vmsvga_state_s { - VGACommonState vga; - - int invalidated; - int enable; - int config; - struct { - int id; - int x; - int y; - int on; - } cursor; - - int index; - int scratch_size; - uint32_t *scratch; - int new_width; - int new_height; - int new_depth; - uint32_t guest; - uint32_t svgaid; - int syncing; - - MemoryRegion fifo_ram; - uint8_t *fifo_ptr; - unsigned int fifo_size; - - union { - uint32_t *fifo; - struct QEMU_PACKED { - uint32_t min; - uint32_t max; - uint32_t next_cmd; - uint32_t stop; - /* Add registers here when adding capabilities. */ - uint32_t fifo[0]; - } *cmd; - }; - -#define REDRAW_FIFO_LEN 512 - struct vmsvga_rect_s { - int x, y, w, h; - } redraw_fifo[REDRAW_FIFO_LEN]; - int redraw_fifo_first, redraw_fifo_last; -}; - -#define TYPE_VMWARE_SVGA "vmware-svga" - -#define VMWARE_SVGA(obj) \ - OBJECT_CHECK(struct pci_vmsvga_state_s, (obj), TYPE_VMWARE_SVGA) - -struct pci_vmsvga_state_s { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - struct vmsvga_state_s chip; - MemoryRegion io_bar; -}; - -#define SVGA_MAGIC 0x900000UL -#define SVGA_MAKE_ID(ver) (SVGA_MAGIC << 8 | (ver)) -#define SVGA_ID_0 SVGA_MAKE_ID(0) -#define SVGA_ID_1 SVGA_MAKE_ID(1) -#define SVGA_ID_2 SVGA_MAKE_ID(2) - -#define SVGA_LEGACY_BASE_PORT 0x4560 -#define SVGA_INDEX_PORT 0x0 -#define SVGA_VALUE_PORT 0x1 -#define SVGA_BIOS_PORT 0x2 - -#define SVGA_VERSION_2 - -#ifdef SVGA_VERSION_2 -# define SVGA_ID SVGA_ID_2 -# define SVGA_IO_BASE SVGA_LEGACY_BASE_PORT -# define SVGA_IO_MUL 1 -# define SVGA_FIFO_SIZE 0x10000 -# define SVGA_PCI_DEVICE_ID PCI_DEVICE_ID_VMWARE_SVGA2 -#else -# define SVGA_ID SVGA_ID_1 -# define SVGA_IO_BASE SVGA_LEGACY_BASE_PORT -# define SVGA_IO_MUL 4 -# define SVGA_FIFO_SIZE 0x10000 -# define SVGA_PCI_DEVICE_ID PCI_DEVICE_ID_VMWARE_SVGA -#endif - -enum { - /* ID 0, 1 and 2 registers */ - SVGA_REG_ID = 0, - SVGA_REG_ENABLE = 1, - SVGA_REG_WIDTH = 2, - SVGA_REG_HEIGHT = 3, - SVGA_REG_MAX_WIDTH = 4, - SVGA_REG_MAX_HEIGHT = 5, - SVGA_REG_DEPTH = 6, - SVGA_REG_BITS_PER_PIXEL = 7, /* Current bpp in the guest */ - SVGA_REG_PSEUDOCOLOR = 8, - SVGA_REG_RED_MASK = 9, - SVGA_REG_GREEN_MASK = 10, - SVGA_REG_BLUE_MASK = 11, - SVGA_REG_BYTES_PER_LINE = 12, - SVGA_REG_FB_START = 13, - SVGA_REG_FB_OFFSET = 14, - SVGA_REG_VRAM_SIZE = 15, - SVGA_REG_FB_SIZE = 16, - - /* ID 1 and 2 registers */ - SVGA_REG_CAPABILITIES = 17, - SVGA_REG_MEM_START = 18, /* Memory for command FIFO */ - SVGA_REG_MEM_SIZE = 19, - SVGA_REG_CONFIG_DONE = 20, /* Set when memory area configured */ - SVGA_REG_SYNC = 21, /* Write to force synchronization */ - SVGA_REG_BUSY = 22, /* Read to check if sync is done */ - SVGA_REG_GUEST_ID = 23, /* Set guest OS identifier */ - SVGA_REG_CURSOR_ID = 24, /* ID of cursor */ - SVGA_REG_CURSOR_X = 25, /* Set cursor X position */ - SVGA_REG_CURSOR_Y = 26, /* Set cursor Y position */ - SVGA_REG_CURSOR_ON = 27, /* Turn cursor on/off */ - SVGA_REG_HOST_BITS_PER_PIXEL = 28, /* Current bpp in the host */ - SVGA_REG_SCRATCH_SIZE = 29, /* Number of scratch registers */ - SVGA_REG_MEM_REGS = 30, /* Number of FIFO registers */ - SVGA_REG_NUM_DISPLAYS = 31, /* Number of guest displays */ - SVGA_REG_PITCHLOCK = 32, /* Fixed pitch for all modes */ - - SVGA_PALETTE_BASE = 1024, /* Base of SVGA color map */ - SVGA_PALETTE_END = SVGA_PALETTE_BASE + 767, - SVGA_SCRATCH_BASE = SVGA_PALETTE_BASE + 768, -}; - -#define SVGA_CAP_NONE 0 -#define SVGA_CAP_RECT_FILL (1 << 0) -#define SVGA_CAP_RECT_COPY (1 << 1) -#define SVGA_CAP_RECT_PAT_FILL (1 << 2) -#define SVGA_CAP_LEGACY_OFFSCREEN (1 << 3) -#define SVGA_CAP_RASTER_OP (1 << 4) -#define SVGA_CAP_CURSOR (1 << 5) -#define SVGA_CAP_CURSOR_BYPASS (1 << 6) -#define SVGA_CAP_CURSOR_BYPASS_2 (1 << 7) -#define SVGA_CAP_8BIT_EMULATION (1 << 8) -#define SVGA_CAP_ALPHA_CURSOR (1 << 9) -#define SVGA_CAP_GLYPH (1 << 10) -#define SVGA_CAP_GLYPH_CLIPPING (1 << 11) -#define SVGA_CAP_OFFSCREEN_1 (1 << 12) -#define SVGA_CAP_ALPHA_BLEND (1 << 13) -#define SVGA_CAP_3D (1 << 14) -#define SVGA_CAP_EXTENDED_FIFO (1 << 15) -#define SVGA_CAP_MULTIMON (1 << 16) -#define SVGA_CAP_PITCHLOCK (1 << 17) - -/* - * FIFO offsets (seen as an array of 32-bit words) - */ -enum { - /* - * The original defined FIFO offsets - */ - SVGA_FIFO_MIN = 0, - SVGA_FIFO_MAX, /* The distance from MIN to MAX must be at least 10K */ - SVGA_FIFO_NEXT_CMD, - SVGA_FIFO_STOP, - - /* - * Additional offsets added as of SVGA_CAP_EXTENDED_FIFO - */ - SVGA_FIFO_CAPABILITIES = 4, - SVGA_FIFO_FLAGS, - SVGA_FIFO_FENCE, - SVGA_FIFO_3D_HWVERSION, - SVGA_FIFO_PITCHLOCK, -}; - -#define SVGA_FIFO_CAP_NONE 0 -#define SVGA_FIFO_CAP_FENCE (1 << 0) -#define SVGA_FIFO_CAP_ACCELFRONT (1 << 1) -#define SVGA_FIFO_CAP_PITCHLOCK (1 << 2) - -#define SVGA_FIFO_FLAG_NONE 0 -#define SVGA_FIFO_FLAG_ACCELFRONT (1 << 0) - -/* These values can probably be changed arbitrarily. */ -#define SVGA_SCRATCH_SIZE 0x8000 -#define SVGA_MAX_WIDTH ROUND_UP(2360, VNC_DIRTY_PIXELS_PER_BIT) -#define SVGA_MAX_HEIGHT 1770 - -#ifdef VERBOSE -# define GUEST_OS_BASE 0x5001 -static const char *vmsvga_guest_id[] = { - [0x00] = "Dos", - [0x01] = "Windows 3.1", - [0x02] = "Windows 95", - [0x03] = "Windows 98", - [0x04] = "Windows ME", - [0x05] = "Windows NT", - [0x06] = "Windows 2000", - [0x07] = "Linux", - [0x08] = "OS/2", - [0x09] = "an unknown OS", - [0x0a] = "BSD", - [0x0b] = "Whistler", - [0x0c] = "an unknown OS", - [0x0d] = "an unknown OS", - [0x0e] = "an unknown OS", - [0x0f] = "an unknown OS", - [0x10] = "an unknown OS", - [0x11] = "an unknown OS", - [0x12] = "an unknown OS", - [0x13] = "an unknown OS", - [0x14] = "an unknown OS", - [0x15] = "Windows 2003", -}; -#endif - -enum { - SVGA_CMD_INVALID_CMD = 0, - SVGA_CMD_UPDATE = 1, - SVGA_CMD_RECT_FILL = 2, - SVGA_CMD_RECT_COPY = 3, - SVGA_CMD_DEFINE_BITMAP = 4, - SVGA_CMD_DEFINE_BITMAP_SCANLINE = 5, - SVGA_CMD_DEFINE_PIXMAP = 6, - SVGA_CMD_DEFINE_PIXMAP_SCANLINE = 7, - SVGA_CMD_RECT_BITMAP_FILL = 8, - SVGA_CMD_RECT_PIXMAP_FILL = 9, - SVGA_CMD_RECT_BITMAP_COPY = 10, - SVGA_CMD_RECT_PIXMAP_COPY = 11, - SVGA_CMD_FREE_OBJECT = 12, - SVGA_CMD_RECT_ROP_FILL = 13, - SVGA_CMD_RECT_ROP_COPY = 14, - SVGA_CMD_RECT_ROP_BITMAP_FILL = 15, - SVGA_CMD_RECT_ROP_PIXMAP_FILL = 16, - SVGA_CMD_RECT_ROP_BITMAP_COPY = 17, - SVGA_CMD_RECT_ROP_PIXMAP_COPY = 18, - SVGA_CMD_DEFINE_CURSOR = 19, - SVGA_CMD_DISPLAY_CURSOR = 20, - SVGA_CMD_MOVE_CURSOR = 21, - SVGA_CMD_DEFINE_ALPHA_CURSOR = 22, - SVGA_CMD_DRAW_GLYPH = 23, - SVGA_CMD_DRAW_GLYPH_CLIPPED = 24, - SVGA_CMD_UPDATE_VERBOSE = 25, - SVGA_CMD_SURFACE_FILL = 26, - SVGA_CMD_SURFACE_COPY = 27, - SVGA_CMD_SURFACE_ALPHA_BLEND = 28, - SVGA_CMD_FRONT_ROP_FILL = 29, - SVGA_CMD_FENCE = 30, -}; - -/* Legal values for the SVGA_REG_CURSOR_ON register in cursor bypass mode */ -enum { - SVGA_CURSOR_ON_HIDE = 0, - SVGA_CURSOR_ON_SHOW = 1, - SVGA_CURSOR_ON_REMOVE_FROM_FB = 2, - SVGA_CURSOR_ON_RESTORE_TO_FB = 3, -}; - -static inline bool vmsvga_verify_rect(DisplaySurface *surface, - const char *name, - int x, int y, int w, int h) -{ - if (x < 0) { - fprintf(stderr, "%s: x was < 0 (%d)\n", name, x); - return false; - } - if (x > SVGA_MAX_WIDTH) { - fprintf(stderr, "%s: x was > %d (%d)\n", name, SVGA_MAX_WIDTH, x); - return false; - } - if (w < 0) { - fprintf(stderr, "%s: w was < 0 (%d)\n", name, w); - return false; - } - if (w > SVGA_MAX_WIDTH) { - fprintf(stderr, "%s: w was > %d (%d)\n", name, SVGA_MAX_WIDTH, w); - return false; - } - if (x + w > surface_width(surface)) { - fprintf(stderr, "%s: width was > %d (x: %d, w: %d)\n", - name, surface_width(surface), x, w); - return false; - } - - if (y < 0) { - fprintf(stderr, "%s: y was < 0 (%d)\n", name, y); - return false; - } - if (y > SVGA_MAX_HEIGHT) { - fprintf(stderr, "%s: y was > %d (%d)\n", name, SVGA_MAX_HEIGHT, y); - return false; - } - if (h < 0) { - fprintf(stderr, "%s: h was < 0 (%d)\n", name, h); - return false; - } - if (h > SVGA_MAX_HEIGHT) { - fprintf(stderr, "%s: h was > %d (%d)\n", name, SVGA_MAX_HEIGHT, h); - return false; - } - if (y + h > surface_height(surface)) { - fprintf(stderr, "%s: update height > %d (y: %d, h: %d)\n", - name, surface_height(surface), y, h); - return false; - } - - return true; -} - -static inline void vmsvga_update_rect(struct vmsvga_state_s *s, - int x, int y, int w, int h) -{ - DisplaySurface *surface = qemu_console_surface(s->vga.con); - int line; - int bypl; - int width; - int start; - uint8_t *src; - uint8_t *dst; - - if (!vmsvga_verify_rect(surface, __func__, x, y, w, h)) { - /* go for a fullscreen update as fallback */ - x = 0; - y = 0; - w = surface_width(surface); - h = surface_height(surface); - } - - bypl = surface_stride(surface); - width = surface_bytes_per_pixel(surface) * w; - start = surface_bytes_per_pixel(surface) * x + bypl * y; - src = s->vga.vram_ptr + start; - dst = surface_data(surface) + start; - - for (line = h; line > 0; line--, src += bypl, dst += bypl) { - memcpy(dst, src, width); - } - dpy_gfx_update(s->vga.con, x, y, w, h); -} - -static inline void vmsvga_update_rect_delayed(struct vmsvga_state_s *s, - int x, int y, int w, int h) -{ - struct vmsvga_rect_s *rect = &s->redraw_fifo[s->redraw_fifo_last++]; - - s->redraw_fifo_last &= REDRAW_FIFO_LEN - 1; - rect->x = x; - rect->y = y; - rect->w = w; - rect->h = h; -} - -static inline void vmsvga_update_rect_flush(struct vmsvga_state_s *s) -{ - struct vmsvga_rect_s *rect; - - if (s->invalidated) { - s->redraw_fifo_first = s->redraw_fifo_last; - return; - } - /* Overlapping region updates can be optimised out here - if someone - * knows a smart algorithm to do that, please share. */ - while (s->redraw_fifo_first != s->redraw_fifo_last) { - rect = &s->redraw_fifo[s->redraw_fifo_first++]; - s->redraw_fifo_first &= REDRAW_FIFO_LEN - 1; - vmsvga_update_rect(s, rect->x, rect->y, rect->w, rect->h); - } -} - -#ifdef HW_RECT_ACCEL -static inline int vmsvga_copy_rect(struct vmsvga_state_s *s, - int x0, int y0, int x1, int y1, int w, int h) -{ - DisplaySurface *surface = qemu_console_surface(s->vga.con); - uint8_t *vram = s->vga.vram_ptr; - int bypl = surface_stride(surface); - int bypp = surface_bytes_per_pixel(surface); - int width = bypp * w; - int line = h; - uint8_t *ptr[2]; - - if (!vmsvga_verify_rect(surface, "vmsvga_copy_rect/src", x0, y0, w, h)) { - return -1; - } - if (!vmsvga_verify_rect(surface, "vmsvga_copy_rect/dst", x1, y1, w, h)) { - return -1; - } - - if (y1 > y0) { - ptr[0] = vram + bypp * x0 + bypl * (y0 + h - 1); - ptr[1] = vram + bypp * x1 + bypl * (y1 + h - 1); - for (; line > 0; line --, ptr[0] -= bypl, ptr[1] -= bypl) { - memmove(ptr[1], ptr[0], width); - } - } else { - ptr[0] = vram + bypp * x0 + bypl * y0; - ptr[1] = vram + bypp * x1 + bypl * y1; - for (; line > 0; line --, ptr[0] += bypl, ptr[1] += bypl) { - memmove(ptr[1], ptr[0], width); - } - } - - vmsvga_update_rect_delayed(s, x1, y1, w, h); - return 0; -} -#endif - -#ifdef HW_FILL_ACCEL -static inline int vmsvga_fill_rect(struct vmsvga_state_s *s, - uint32_t c, int x, int y, int w, int h) -{ - DisplaySurface *surface = qemu_console_surface(s->vga.con); - int bypl = surface_stride(surface); - int width = surface_bytes_per_pixel(surface) * w; - int line = h; - int column; - uint8_t *fst; - uint8_t *dst; - uint8_t *src; - uint8_t col[4]; - - if (!vmsvga_verify_rect(surface, __func__, x, y, w, h)) { - return -1; - } - - col[0] = c; - col[1] = c >> 8; - col[2] = c >> 16; - col[3] = c >> 24; - - fst = s->vga.vram_ptr + surface_bytes_per_pixel(surface) * x + bypl * y; - - if (line--) { - dst = fst; - src = col; - for (column = width; column > 0; column--) { - *(dst++) = *(src++); - if (src - col == surface_bytes_per_pixel(surface)) { - src = col; - } - } - dst = fst; - for (; line > 0; line--) { - dst += bypl; - memcpy(dst, fst, width); - } - } - - vmsvga_update_rect_delayed(s, x, y, w, h); - return 0; -} -#endif - -struct vmsvga_cursor_definition_s { - uint32_t width; - uint32_t height; - int id; - uint32_t bpp; - int hot_x; - int hot_y; - uint32_t mask[1024]; - uint32_t image[4096]; -}; - -#define SVGA_BITMAP_SIZE(w, h) ((((w) + 31) >> 5) * (h)) -#define SVGA_PIXMAP_SIZE(w, h, bpp) (((((w) * (bpp)) + 31) >> 5) * (h)) - -#ifdef HW_MOUSE_ACCEL -static inline void vmsvga_cursor_define(struct vmsvga_state_s *s, - struct vmsvga_cursor_definition_s *c) -{ - QEMUCursor *qc; - int i, pixels; - - qc = cursor_alloc(c->width, c->height); - qc->hot_x = c->hot_x; - qc->hot_y = c->hot_y; - switch (c->bpp) { - case 1: - cursor_set_mono(qc, 0xffffff, 0x000000, (void *)c->image, - 1, (void *)c->mask); -#ifdef DEBUG - cursor_print_ascii_art(qc, "vmware/mono"); -#endif - break; - case 32: - /* fill alpha channel from mask, set color to zero */ - cursor_set_mono(qc, 0x000000, 0x000000, (void *)c->mask, - 1, (void *)c->mask); - /* add in rgb values */ - pixels = c->width * c->height; - for (i = 0; i < pixels; i++) { - qc->data[i] |= c->image[i] & 0xffffff; - } -#ifdef DEBUG - cursor_print_ascii_art(qc, "vmware/32bit"); -#endif - break; - default: - fprintf(stderr, "%s: unhandled bpp %d, using fallback cursor\n", - __func__, c->bpp); - cursor_put(qc); - qc = cursor_builtin_left_ptr(); - } - - dpy_cursor_define(s->vga.con, qc); - cursor_put(qc); -} -#endif - -#define CMD(f) le32_to_cpu(s->cmd->f) - -static inline int vmsvga_fifo_length(struct vmsvga_state_s *s) -{ - int num; - - if (!s->config || !s->enable) { - return 0; - } - num = CMD(next_cmd) - CMD(stop); - if (num < 0) { - num += CMD(max) - CMD(min); - } - return num >> 2; -} - -static inline uint32_t vmsvga_fifo_read_raw(struct vmsvga_state_s *s) -{ - uint32_t cmd = s->fifo[CMD(stop) >> 2]; - - s->cmd->stop = cpu_to_le32(CMD(stop) + 4); - if (CMD(stop) >= CMD(max)) { - s->cmd->stop = s->cmd->min; - } - return cmd; -} - -static inline uint32_t vmsvga_fifo_read(struct vmsvga_state_s *s) -{ - return le32_to_cpu(vmsvga_fifo_read_raw(s)); -} - -static void vmsvga_fifo_run(struct vmsvga_state_s *s) -{ - uint32_t cmd, colour; - int args, len; - int x, y, dx, dy, width, height; - struct vmsvga_cursor_definition_s cursor; - uint32_t cmd_start; - - len = vmsvga_fifo_length(s); - while (len > 0) { - /* May need to go back to the start of the command if incomplete */ - cmd_start = s->cmd->stop; - - switch (cmd = vmsvga_fifo_read(s)) { - case SVGA_CMD_UPDATE: - case SVGA_CMD_UPDATE_VERBOSE: - len -= 5; - if (len < 0) { - goto rewind; - } - - x = vmsvga_fifo_read(s); - y = vmsvga_fifo_read(s); - width = vmsvga_fifo_read(s); - height = vmsvga_fifo_read(s); - vmsvga_update_rect_delayed(s, x, y, width, height); - break; - - case SVGA_CMD_RECT_FILL: - len -= 6; - if (len < 0) { - goto rewind; - } - - colour = vmsvga_fifo_read(s); - x = vmsvga_fifo_read(s); - y = vmsvga_fifo_read(s); - width = vmsvga_fifo_read(s); - height = vmsvga_fifo_read(s); -#ifdef HW_FILL_ACCEL - if (vmsvga_fill_rect(s, colour, x, y, width, height) == 0) { - break; - } -#endif - args = 0; - goto badcmd; - - case SVGA_CMD_RECT_COPY: - len -= 7; - if (len < 0) { - goto rewind; - } - - x = vmsvga_fifo_read(s); - y = vmsvga_fifo_read(s); - dx = vmsvga_fifo_read(s); - dy = vmsvga_fifo_read(s); - width = vmsvga_fifo_read(s); - height = vmsvga_fifo_read(s); -#ifdef HW_RECT_ACCEL - if (vmsvga_copy_rect(s, x, y, dx, dy, width, height) == 0) { - break; - } -#endif - args = 0; - goto badcmd; - - case SVGA_CMD_DEFINE_CURSOR: - len -= 8; - if (len < 0) { - goto rewind; - } - - cursor.id = vmsvga_fifo_read(s); - cursor.hot_x = vmsvga_fifo_read(s); - cursor.hot_y = vmsvga_fifo_read(s); - cursor.width = x = vmsvga_fifo_read(s); - cursor.height = y = vmsvga_fifo_read(s); - vmsvga_fifo_read(s); - cursor.bpp = vmsvga_fifo_read(s); - - args = SVGA_BITMAP_SIZE(x, y) + SVGA_PIXMAP_SIZE(x, y, cursor.bpp); - 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; - } - - len -= args; - if (len < 0) { - goto rewind; - } - - for (args = 0; args < SVGA_BITMAP_SIZE(x, y); args++) { - cursor.mask[args] = vmsvga_fifo_read_raw(s); - } - for (args = 0; args < SVGA_PIXMAP_SIZE(x, y, cursor.bpp); args++) { - cursor.image[args] = vmsvga_fifo_read_raw(s); - } -#ifdef HW_MOUSE_ACCEL - vmsvga_cursor_define(s, &cursor); - break; -#else - args = 0; - goto badcmd; -#endif - - /* - * Other commands that we at least know the number of arguments - * for so we can avoid FIFO desync if driver uses them illegally. - */ - case SVGA_CMD_DEFINE_ALPHA_CURSOR: - len -= 6; - if (len < 0) { - goto rewind; - } - vmsvga_fifo_read(s); - vmsvga_fifo_read(s); - vmsvga_fifo_read(s); - x = vmsvga_fifo_read(s); - y = vmsvga_fifo_read(s); - args = x * y; - goto badcmd; - case SVGA_CMD_RECT_ROP_FILL: - args = 6; - goto badcmd; - case SVGA_CMD_RECT_ROP_COPY: - args = 7; - goto badcmd; - case SVGA_CMD_DRAW_GLYPH_CLIPPED: - len -= 4; - if (len < 0) { - goto rewind; - } - vmsvga_fifo_read(s); - vmsvga_fifo_read(s); - args = 7 + (vmsvga_fifo_read(s) >> 2); - goto badcmd; - case SVGA_CMD_SURFACE_ALPHA_BLEND: - args = 12; - goto badcmd; - - /* - * Other commands that are not listed as depending on any - * CAPABILITIES bits, but are not described in the README either. - */ - case SVGA_CMD_SURFACE_FILL: - case SVGA_CMD_SURFACE_COPY: - case SVGA_CMD_FRONT_ROP_FILL: - case SVGA_CMD_FENCE: - case SVGA_CMD_INVALID_CMD: - break; /* Nop */ - - default: - args = 0; - badcmd: - len -= args; - if (len < 0) { - goto rewind; - } - while (args--) { - vmsvga_fifo_read(s); - } - printf("%s: Unknown command 0x%02x in SVGA command FIFO\n", - __func__, cmd); - break; - - rewind: - s->cmd->stop = cmd_start; - break; - } - } - - s->syncing = 0; -} - -static uint32_t vmsvga_index_read(void *opaque, uint32_t address) -{ - struct vmsvga_state_s *s = opaque; - - return s->index; -} - -static void vmsvga_index_write(void *opaque, uint32_t address, uint32_t index) -{ - struct vmsvga_state_s *s = opaque; - - s->index = index; -} - -static uint32_t vmsvga_value_read(void *opaque, uint32_t address) -{ - uint32_t caps; - struct vmsvga_state_s *s = opaque; - DisplaySurface *surface = qemu_console_surface(s->vga.con); - PixelFormat pf; - uint32_t ret; - - switch (s->index) { - case SVGA_REG_ID: - ret = s->svgaid; - break; - - case SVGA_REG_ENABLE: - ret = s->enable; - break; - - case SVGA_REG_WIDTH: - ret = s->new_width ? s->new_width : surface_width(surface); - break; - - case SVGA_REG_HEIGHT: - ret = s->new_height ? s->new_height : surface_height(surface); - break; - - case SVGA_REG_MAX_WIDTH: - ret = SVGA_MAX_WIDTH; - break; - - case SVGA_REG_MAX_HEIGHT: - ret = SVGA_MAX_HEIGHT; - break; - - case SVGA_REG_DEPTH: - ret = (s->new_depth == 32) ? 24 : s->new_depth; - break; - - case SVGA_REG_BITS_PER_PIXEL: - case SVGA_REG_HOST_BITS_PER_PIXEL: - ret = s->new_depth; - break; - - case SVGA_REG_PSEUDOCOLOR: - ret = 0x0; - break; - - case SVGA_REG_RED_MASK: - pf = qemu_default_pixelformat(s->new_depth); - ret = pf.rmask; - break; - - case SVGA_REG_GREEN_MASK: - pf = qemu_default_pixelformat(s->new_depth); - ret = pf.gmask; - break; - - case SVGA_REG_BLUE_MASK: - pf = qemu_default_pixelformat(s->new_depth); - ret = pf.bmask; - break; - - case SVGA_REG_BYTES_PER_LINE: - if (s->new_width) { - ret = (s->new_depth * s->new_width) / 8; - } else { - ret = surface_stride(surface); - } - break; - - case SVGA_REG_FB_START: { - struct pci_vmsvga_state_s *pci_vmsvga - = container_of(s, struct pci_vmsvga_state_s, chip); - ret = pci_get_bar_addr(PCI_DEVICE(pci_vmsvga), 1); - break; - } - - case SVGA_REG_FB_OFFSET: - ret = 0x0; - break; - - case SVGA_REG_VRAM_SIZE: - ret = s->vga.vram_size; /* No physical VRAM besides the framebuffer */ - break; - - case SVGA_REG_FB_SIZE: - ret = s->vga.vram_size; - break; - - case SVGA_REG_CAPABILITIES: - caps = SVGA_CAP_NONE; -#ifdef HW_RECT_ACCEL - caps |= SVGA_CAP_RECT_COPY; -#endif -#ifdef HW_FILL_ACCEL - caps |= SVGA_CAP_RECT_FILL; -#endif -#ifdef HW_MOUSE_ACCEL - if (dpy_cursor_define_supported(s->vga.con)) { - caps |= SVGA_CAP_CURSOR | SVGA_CAP_CURSOR_BYPASS_2 | - SVGA_CAP_CURSOR_BYPASS; - } -#endif - ret = caps; - break; - - case SVGA_REG_MEM_START: { - struct pci_vmsvga_state_s *pci_vmsvga - = container_of(s, struct pci_vmsvga_state_s, chip); - ret = pci_get_bar_addr(PCI_DEVICE(pci_vmsvga), 2); - break; - } - - case SVGA_REG_MEM_SIZE: - ret = s->fifo_size; - break; - - case SVGA_REG_CONFIG_DONE: - ret = s->config; - break; - - case SVGA_REG_SYNC: - case SVGA_REG_BUSY: - ret = s->syncing; - break; - - case SVGA_REG_GUEST_ID: - ret = s->guest; - break; - - case SVGA_REG_CURSOR_ID: - ret = s->cursor.id; - break; - - case SVGA_REG_CURSOR_X: - ret = s->cursor.x; - break; - - case SVGA_REG_CURSOR_Y: - ret = s->cursor.y; - break; - - case SVGA_REG_CURSOR_ON: - ret = s->cursor.on; - break; - - case SVGA_REG_SCRATCH_SIZE: - ret = s->scratch_size; - break; - - case SVGA_REG_MEM_REGS: - case SVGA_REG_NUM_DISPLAYS: - case SVGA_REG_PITCHLOCK: - case SVGA_PALETTE_BASE ... SVGA_PALETTE_END: - ret = 0; - break; - - default: - if (s->index >= SVGA_SCRATCH_BASE && - s->index < SVGA_SCRATCH_BASE + s->scratch_size) { - ret = s->scratch[s->index - SVGA_SCRATCH_BASE]; - break; - } - printf("%s: Bad register %02x\n", __func__, s->index); - ret = 0; - break; - } - - if (s->index >= SVGA_SCRATCH_BASE) { - trace_vmware_scratch_read(s->index, ret); - } else if (s->index >= SVGA_PALETTE_BASE) { - trace_vmware_palette_read(s->index, ret); - } else { - trace_vmware_value_read(s->index, ret); - } - return ret; -} - -static void vmsvga_value_write(void *opaque, uint32_t address, uint32_t value) -{ - struct vmsvga_state_s *s = opaque; - - if (s->index >= SVGA_SCRATCH_BASE) { - trace_vmware_scratch_write(s->index, value); - } else if (s->index >= SVGA_PALETTE_BASE) { - trace_vmware_palette_write(s->index, value); - } else { - trace_vmware_value_write(s->index, value); - } - switch (s->index) { - case SVGA_REG_ID: - if (value == SVGA_ID_2 || value == SVGA_ID_1 || value == SVGA_ID_0) { - s->svgaid = value; - } - break; - - case SVGA_REG_ENABLE: - s->enable = !!value; - s->invalidated = 1; - s->vga.hw_ops->invalidate(&s->vga); - if (s->enable && s->config) { - vga_dirty_log_stop(&s->vga); - } else { - vga_dirty_log_start(&s->vga); - } - break; - - case SVGA_REG_WIDTH: - if (value <= SVGA_MAX_WIDTH) { - s->new_width = value; - s->invalidated = 1; - } else { - printf("%s: Bad width: %i\n", __func__, value); - } - break; - - case SVGA_REG_HEIGHT: - if (value <= SVGA_MAX_HEIGHT) { - s->new_height = value; - s->invalidated = 1; - } else { - printf("%s: Bad height: %i\n", __func__, value); - } - break; - - case SVGA_REG_BITS_PER_PIXEL: - if (value != 32) { - printf("%s: Bad bits per pixel: %i bits\n", __func__, value); - s->config = 0; - s->invalidated = 1; - } - break; - - case SVGA_REG_CONFIG_DONE: - if (value) { - s->fifo = (uint32_t *) s->fifo_ptr; - /* Check range and alignment. */ - if ((CMD(min) | CMD(max) | CMD(next_cmd) | CMD(stop)) & 3) { - break; - } - if (CMD(min) < (uint8_t *) s->cmd->fifo - (uint8_t *) s->fifo) { - break; - } - if (CMD(max) > SVGA_FIFO_SIZE) { - break; - } - if (CMD(max) < CMD(min) + 10 * 1024) { - break; - } - vga_dirty_log_stop(&s->vga); - } - s->config = !!value; - break; - - case SVGA_REG_SYNC: - s->syncing = 1; - vmsvga_fifo_run(s); /* Or should we just wait for update_display? */ - break; - - case SVGA_REG_GUEST_ID: - s->guest = value; -#ifdef VERBOSE - if (value >= GUEST_OS_BASE && value < GUEST_OS_BASE + - ARRAY_SIZE(vmsvga_guest_id)) { - printf("%s: guest runs %s.\n", __func__, - vmsvga_guest_id[value - GUEST_OS_BASE]); - } -#endif - break; - - case SVGA_REG_CURSOR_ID: - s->cursor.id = value; - break; - - case SVGA_REG_CURSOR_X: - s->cursor.x = value; - break; - - case SVGA_REG_CURSOR_Y: - s->cursor.y = value; - break; - - case SVGA_REG_CURSOR_ON: - s->cursor.on |= (value == SVGA_CURSOR_ON_SHOW); - s->cursor.on &= (value != SVGA_CURSOR_ON_HIDE); -#ifdef HW_MOUSE_ACCEL - if (value <= SVGA_CURSOR_ON_SHOW) { - dpy_mouse_set(s->vga.con, s->cursor.x, s->cursor.y, s->cursor.on); - } -#endif - break; - - case SVGA_REG_DEPTH: - case SVGA_REG_MEM_REGS: - case SVGA_REG_NUM_DISPLAYS: - case SVGA_REG_PITCHLOCK: - case SVGA_PALETTE_BASE ... SVGA_PALETTE_END: - break; - - default: - if (s->index >= SVGA_SCRATCH_BASE && - s->index < SVGA_SCRATCH_BASE + s->scratch_size) { - s->scratch[s->index - SVGA_SCRATCH_BASE] = value; - break; - } - printf("%s: Bad register %02x\n", __func__, s->index); - } -} - -static uint32_t vmsvga_bios_read(void *opaque, uint32_t address) -{ - printf("%s: what are we supposed to return?\n", __func__); - return 0xcafe; -} - -static void vmsvga_bios_write(void *opaque, uint32_t address, uint32_t data) -{ - printf("%s: what are we supposed to do with (%08x)?\n", __func__, data); -} - -static inline void vmsvga_check_size(struct vmsvga_state_s *s) -{ - DisplaySurface *surface = qemu_console_surface(s->vga.con); - - if (s->new_width != surface_width(surface) || - s->new_height != surface_height(surface) || - s->new_depth != surface_bits_per_pixel(surface)) { - int stride = (s->new_depth * s->new_width) / 8; - pixman_format_code_t format = - qemu_default_pixman_format(s->new_depth, true); - trace_vmware_setmode(s->new_width, s->new_height, s->new_depth); - surface = qemu_create_displaysurface_from(s->new_width, s->new_height, - format, stride, - s->vga.vram_ptr); - dpy_gfx_replace_surface(s->vga.con, surface); - s->invalidated = 1; - } -} - -static void vmsvga_update_display(void *opaque) -{ - struct vmsvga_state_s *s = opaque; - DisplaySurface *surface; - bool dirty = false; - - if (!s->enable) { - s->vga.hw_ops->gfx_update(&s->vga); - return; - } - - vmsvga_check_size(s); - surface = qemu_console_surface(s->vga.con); - - vmsvga_fifo_run(s); - vmsvga_update_rect_flush(s); - - /* - * Is it more efficient to look at vram VGA-dirty bits or wait - * for the driver to issue SVGA_CMD_UPDATE? - */ - if (memory_region_is_logging(&s->vga.vram, DIRTY_MEMORY_VGA)) { - vga_sync_dirty_bitmap(&s->vga); - dirty = memory_region_get_dirty(&s->vga.vram, 0, - surface_stride(surface) * surface_height(surface), - DIRTY_MEMORY_VGA); - } - if (s->invalidated || dirty) { - s->invalidated = 0; - dpy_gfx_update(s->vga.con, 0, 0, - surface_width(surface), surface_height(surface)); - } - if (dirty) { - memory_region_reset_dirty(&s->vga.vram, 0, - surface_stride(surface) * surface_height(surface), - DIRTY_MEMORY_VGA); - } -} - -static void vmsvga_reset(DeviceState *dev) -{ - struct pci_vmsvga_state_s *pci = VMWARE_SVGA(dev); - struct vmsvga_state_s *s = &pci->chip; - - s->index = 0; - s->enable = 0; - s->config = 0; - s->svgaid = SVGA_ID; - s->cursor.on = 0; - s->redraw_fifo_first = 0; - s->redraw_fifo_last = 0; - s->syncing = 0; - - vga_dirty_log_start(&s->vga); -} - -static void vmsvga_invalidate_display(void *opaque) -{ - struct vmsvga_state_s *s = opaque; - if (!s->enable) { - s->vga.hw_ops->invalidate(&s->vga); - return; - } - - s->invalidated = 1; -} - -static void vmsvga_text_update(void *opaque, console_ch_t *chardata) -{ - struct vmsvga_state_s *s = opaque; - - if (s->vga.hw_ops->text_update) { - s->vga.hw_ops->text_update(&s->vga, chardata); - } -} - -static int vmsvga_post_load(void *opaque, int version_id) -{ - struct vmsvga_state_s *s = opaque; - - s->invalidated = 1; - if (s->config) { - s->fifo = (uint32_t *) s->fifo_ptr; - } - return 0; -} - -static const VMStateDescription vmstate_vmware_vga_internal = { - .name = "vmware_vga_internal", - .version_id = 0, - .minimum_version_id = 0, - .post_load = vmsvga_post_load, - .fields = (VMStateField[]) { - VMSTATE_INT32_EQUAL(new_depth, struct vmsvga_state_s), - VMSTATE_INT32(enable, struct vmsvga_state_s), - VMSTATE_INT32(config, struct vmsvga_state_s), - VMSTATE_INT32(cursor.id, struct vmsvga_state_s), - VMSTATE_INT32(cursor.x, struct vmsvga_state_s), - VMSTATE_INT32(cursor.y, struct vmsvga_state_s), - VMSTATE_INT32(cursor.on, struct vmsvga_state_s), - VMSTATE_INT32(index, struct vmsvga_state_s), - VMSTATE_VARRAY_INT32(scratch, struct vmsvga_state_s, - scratch_size, 0, vmstate_info_uint32, uint32_t), - VMSTATE_INT32(new_width, struct vmsvga_state_s), - VMSTATE_INT32(new_height, struct vmsvga_state_s), - VMSTATE_UINT32(guest, struct vmsvga_state_s), - VMSTATE_UINT32(svgaid, struct vmsvga_state_s), - VMSTATE_INT32(syncing, struct vmsvga_state_s), - VMSTATE_UNUSED(4), /* was fb_size */ - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_vmware_vga = { - .name = "vmware_vga", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, struct pci_vmsvga_state_s), - VMSTATE_STRUCT(chip, struct pci_vmsvga_state_s, 0, - vmstate_vmware_vga_internal, struct vmsvga_state_s), - VMSTATE_END_OF_LIST() - } -}; - -static const GraphicHwOps vmsvga_ops = { - .invalidate = vmsvga_invalidate_display, - .gfx_update = vmsvga_update_display, - .text_update = vmsvga_text_update, -}; - -static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s, - MemoryRegion *address_space, MemoryRegion *io) -{ - s->scratch_size = SVGA_SCRATCH_SIZE; - s->scratch = g_malloc(s->scratch_size * 4); - - s->vga.con = graphic_console_init(dev, 0, &vmsvga_ops, s); - - s->fifo_size = SVGA_FIFO_SIZE; - memory_region_init_ram(&s->fifo_ram, NULL, "vmsvga.fifo", s->fifo_size, - &error_fatal); - vmstate_register_ram_global(&s->fifo_ram); - s->fifo_ptr = memory_region_get_ram_ptr(&s->fifo_ram); - - vga_common_init(&s->vga, OBJECT(dev), true); - vga_init(&s->vga, OBJECT(dev), address_space, io, true); - vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga); - s->new_depth = 32; -} - -static uint64_t vmsvga_io_read(void *opaque, hwaddr addr, unsigned size) -{ - struct vmsvga_state_s *s = opaque; - - switch (addr) { - case SVGA_IO_MUL * SVGA_INDEX_PORT: return vmsvga_index_read(s, addr); - case SVGA_IO_MUL * SVGA_VALUE_PORT: return vmsvga_value_read(s, addr); - case SVGA_IO_MUL * SVGA_BIOS_PORT: return vmsvga_bios_read(s, addr); - default: return -1u; - } -} - -static void vmsvga_io_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - struct vmsvga_state_s *s = opaque; - - switch (addr) { - case SVGA_IO_MUL * SVGA_INDEX_PORT: - vmsvga_index_write(s, addr, data); - break; - case SVGA_IO_MUL * SVGA_VALUE_PORT: - vmsvga_value_write(s, addr, data); - break; - case SVGA_IO_MUL * SVGA_BIOS_PORT: - vmsvga_bios_write(s, addr, data); - break; - } -} - -static const MemoryRegionOps vmsvga_io_ops = { - .read = vmsvga_io_read, - .write = vmsvga_io_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - .unaligned = true, - }, - .impl = { - .unaligned = true, - }, -}; - -static void pci_vmsvga_realize(PCIDevice *dev, Error **errp) -{ - struct pci_vmsvga_state_s *s = VMWARE_SVGA(dev); - - dev->config[PCI_CACHE_LINE_SIZE] = 0x08; - dev->config[PCI_LATENCY_TIMER] = 0x40; - dev->config[PCI_INTERRUPT_LINE] = 0xff; /* End */ - - memory_region_init_io(&s->io_bar, NULL, &vmsvga_io_ops, &s->chip, - "vmsvga-io", 0x10); - memory_region_set_flush_coalesced(&s->io_bar); - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar); - - vmsvga_init(DEVICE(dev), &s->chip, - pci_address_space(dev), pci_address_space_io(dev)); - - pci_register_bar(dev, 1, PCI_BASE_ADDRESS_MEM_PREFETCH, - &s->chip.vga.vram); - pci_register_bar(dev, 2, PCI_BASE_ADDRESS_MEM_PREFETCH, - &s->chip.fifo_ram); - - if (!dev->rom_bar) { - /* compatibility with pc-0.13 and older */ - vga_init_vbe(&s->chip.vga, OBJECT(dev), pci_address_space(dev)); - } -} - -static Property vga_vmware_properties[] = { - DEFINE_PROP_UINT32("vgamem_mb", struct pci_vmsvga_state_s, - chip.vga.vram_size_mb, 16), - DEFINE_PROP_END_OF_LIST(), -}; - -static void vmsvga_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = pci_vmsvga_realize; - k->romfile = "vgabios-vmware.bin"; - k->vendor_id = PCI_VENDOR_ID_VMWARE; - k->device_id = SVGA_PCI_DEVICE_ID; - k->class_id = PCI_CLASS_DISPLAY_VGA; - k->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE; - k->subsystem_id = SVGA_PCI_DEVICE_ID; - dc->reset = vmsvga_reset; - dc->vmsd = &vmstate_vmware_vga; - dc->props = vga_vmware_properties; - dc->hotpluggable = false; - set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); -} - -static const TypeInfo vmsvga_info = { - .name = TYPE_VMWARE_SVGA, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(struct pci_vmsvga_state_s), - .class_init = vmsvga_class_init, -}; - -static void vmsvga_register_types(void) -{ - type_register_static(&vmsvga_info); -} - -type_init(vmsvga_register_types) diff --git a/qemu/hw/display/xenfb.c b/qemu/hw/display/xenfb.c deleted file mode 100644 index 9866dfda5..000000000 --- a/qemu/hw/display/xenfb.c +++ /dev/null @@ -1,1004 +0,0 @@ -/* - * xen paravirt framebuffer backend - * - * Copyright IBM, Corp. 2005-2006 - * Copyright Red Hat, Inc. 2006-2008 - * - * Authors: - * Anthony Liguori , - * Markus Armbruster , - * Daniel P. Berrange , - * Pat Campbell , - * Gerd Hoffmann - * - * 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; under version 2 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 . - */ - -#include "qemu/osdep.h" -#include - -#include "hw/hw.h" -#include "ui/console.h" -#include "sysemu/char.h" -#include "hw/xen/xen_backend.h" - -#include -#include -#include -#include - -#include "trace.h" - -#ifndef BTN_LEFT -#define BTN_LEFT 0x110 /* from */ -#endif - -/* -------------------------------------------------------------------- */ - -struct common { - struct XenDevice xendev; /* must be first */ - void *page; - QemuConsole *con; -}; - -struct XenInput { - struct common c; - int abs_pointer_wanted; /* Whether guest supports absolute pointer */ - int button_state; /* Last seen pointer button state */ - int extended; - QEMUPutMouseEntry *qmouse; -}; - -#define UP_QUEUE 8 - -struct XenFB { - struct common c; - size_t fb_len; - int row_stride; - int depth; - int width; - int height; - int offset; - void *pixels; - int fbpages; - int feature_update; - int bug_trigger; - int have_console; - int do_resize; - - struct { - int x,y,w,h; - } up_rects[UP_QUEUE]; - int up_count; - int up_fullscreen; -}; - -/* -------------------------------------------------------------------- */ - -static int common_bind(struct common *c) -{ - uint64_t val; - xen_pfn_t mfn; - - if (xenstore_read_fe_uint64(&c->xendev, "page-ref", &val) == -1) - return -1; - 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 = 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 %"PRI_xen_pfn", remote-port %d, local-port %d\n", - mfn, c->xendev.remote_port, c->xendev.local_port); - - return 0; -} - -static void common_unbind(struct common *c) -{ - xen_be_unbind_evtchn(&c->xendev); - if (c->page) { - xenforeignmemory_unmap(xen_fmem, c->page, 1); - c->page = NULL; - } -} - -/* -------------------------------------------------------------------- */ - -#if 0 -/* - * These two tables are not needed any more, but left in here - * intentionally as documentation, to show how scancode2linux[] - * was generated. - * - * Tables to map from scancode to Linux input layer keycode. - * Scancodes are hardware-specific. These maps assumes a - * standard AT or PS/2 keyboard which is what QEMU feeds us. - */ -const unsigned char atkbd_set2_keycode[512] = { - - 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117, - 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0, - 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183, - 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185, - 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, - 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85, - 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0, - 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125, - 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127, - 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142, - 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0, - 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112, - 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0, - -}; - -const unsigned char atkbd_unxlate_table[128] = { - - 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, - 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, - 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, - 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, - 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, - 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, - 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, - 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 - -}; -#endif - -/* - * for (i = 0; i < 128; i++) { - * scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]]; - * scancode2linux[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80]; - * } - */ -static const unsigned char scancode2linux[512] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 99, 0, 86, 87, 88,117, 0, 0, 95,183,184,185, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 93, 0, 0, 89, 0, 0, 85, 91, 90, 92, 0, 94, 0,124,121, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 165, 0, 0, 0, 0, 0, 0, 0, 0,163, 0, 0, 96, 97, 0, 0, - 113,140,164, 0,166, 0, 0, 0, 0, 0,255, 0, 0, 0,114, 0, - 115, 0,150, 0, 0, 98,255, 99,100, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,119,119,102,103,104, 0,105,112,106,118,107, - 108,109,110,111, 0, 0, 0, 0, 0, 0, 0,125,126,127,116,142, - 0, 0, 0,143, 0,217,156,173,128,159,158,157,155,226, 0,112, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -/* Send an event to the keyboard frontend driver */ -static int xenfb_kbd_event(struct XenInput *xenfb, - union xenkbd_in_event *event) -{ - struct xenkbd_page *page = xenfb->c.page; - uint32_t prod; - - if (xenfb->c.xendev.be_state != XenbusStateConnected) - return 0; - if (!page) - return 0; - - prod = page->in_prod; - if (prod - page->in_cons == XENKBD_IN_RING_LEN) { - errno = EAGAIN; - return -1; - } - - xen_mb(); /* ensure ring space available */ - XENKBD_IN_RING_REF(page, prod) = *event; - xen_wmb(); /* ensure ring contents visible */ - page->in_prod = prod + 1; - return xen_be_send_notify(&xenfb->c.xendev); -} - -/* Send a keyboard (or mouse button) event */ -static int xenfb_send_key(struct XenInput *xenfb, bool down, int keycode) -{ - union xenkbd_in_event event; - - memset(&event, 0, XENKBD_IN_EVENT_SIZE); - event.type = XENKBD_TYPE_KEY; - event.key.pressed = down ? 1 : 0; - event.key.keycode = keycode; - - return xenfb_kbd_event(xenfb, &event); -} - -/* Send a relative mouse movement event */ -static int xenfb_send_motion(struct XenInput *xenfb, - int rel_x, int rel_y, int rel_z) -{ - union xenkbd_in_event event; - - memset(&event, 0, XENKBD_IN_EVENT_SIZE); - event.type = XENKBD_TYPE_MOTION; - event.motion.rel_x = rel_x; - event.motion.rel_y = rel_y; - event.motion.rel_z = rel_z; - - return xenfb_kbd_event(xenfb, &event); -} - -/* Send an absolute mouse movement event */ -static int xenfb_send_position(struct XenInput *xenfb, - int abs_x, int abs_y, int z) -{ - union xenkbd_in_event event; - - memset(&event, 0, XENKBD_IN_EVENT_SIZE); - event.type = XENKBD_TYPE_POS; - event.pos.abs_x = abs_x; - event.pos.abs_y = abs_y; - event.pos.rel_z = z; - - return xenfb_kbd_event(xenfb, &event); -} - -/* - * Send a key event from the client to the guest OS - * QEMU gives us a raw scancode from an AT / PS/2 style keyboard. - * We have to turn this into a Linux Input layer keycode. - * - * Extra complexity from the fact that with extended scancodes - * (like those produced by arrow keys) this method gets called - * twice, but we only want to send a single event. So we have to - * track the '0xe0' scancode state & collapse the extended keys - * as needed. - * - * Wish we could just send scancodes straight to the guest which - * already has code for dealing with this... - */ -static void xenfb_key_event(void *opaque, int scancode) -{ - struct XenInput *xenfb = opaque; - int down = 1; - - if (scancode == 0xe0) { - xenfb->extended = 1; - return; - } else if (scancode & 0x80) { - scancode &= 0x7f; - down = 0; - } - if (xenfb->extended) { - scancode |= 0x80; - xenfb->extended = 0; - } - xenfb_send_key(xenfb, down, scancode2linux[scancode]); -} - -/* - * Send a mouse event from the client to the guest OS - * - * The QEMU mouse can be in either relative, or absolute mode. - * Movement is sent separately from button state, which has to - * be encoded as virtual key events. We also don't actually get - * given any button up/down events, so have to track changes in - * the button state. - */ -static void xenfb_mouse_event(void *opaque, - int dx, int dy, int dz, int button_state) -{ - struct XenInput *xenfb = opaque; - DisplaySurface *surface = qemu_console_surface(xenfb->c.con); - int dw = surface_width(surface); - int dh = surface_height(surface); - int i; - - trace_xenfb_mouse_event(opaque, dx, dy, dz, button_state, - xenfb->abs_pointer_wanted); - if (xenfb->abs_pointer_wanted) - xenfb_send_position(xenfb, - dx * (dw - 1) / 0x7fff, - dy * (dh - 1) / 0x7fff, - dz); - else - xenfb_send_motion(xenfb, dx, dy, dz); - - for (i = 0 ; i < 8 ; i++) { - int lastDown = xenfb->button_state & (1 << i); - int down = button_state & (1 << i); - if (down == lastDown) - continue; - - if (xenfb_send_key(xenfb, down, BTN_LEFT+i) < 0) - return; - } - xenfb->button_state = button_state; -} - -static int input_init(struct XenDevice *xendev) -{ - xenstore_write_be_int(xendev, "feature-abs-pointer", 1); - return 0; -} - -static int input_initialise(struct XenDevice *xendev) -{ - struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); - int rc; - - if (!in->c.con) { - xen_be_printf(xendev, 1, "ds not set (yet)\n"); - return -1; - } - - rc = common_bind(&in->c); - if (rc != 0) - return rc; - - qemu_add_kbd_event_handler(xenfb_key_event, in); - return 0; -} - -static void input_connected(struct XenDevice *xendev) -{ - struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); - - if (xenstore_read_fe_int(xendev, "request-abs-pointer", - &in->abs_pointer_wanted) == -1) { - in->abs_pointer_wanted = 0; - } - - if (in->qmouse) { - qemu_remove_mouse_event_handler(in->qmouse); - } - trace_xenfb_input_connected(xendev, in->abs_pointer_wanted); - in->qmouse = qemu_add_mouse_event_handler(xenfb_mouse_event, in, - in->abs_pointer_wanted, - "Xen PVFB Mouse"); -} - -static void input_disconnect(struct XenDevice *xendev) -{ - struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); - - if (in->qmouse) { - qemu_remove_mouse_event_handler(in->qmouse); - in->qmouse = NULL; - } - qemu_add_kbd_event_handler(NULL, NULL); - common_unbind(&in->c); -} - -static void input_event(struct XenDevice *xendev) -{ - struct XenInput *xenfb = container_of(xendev, struct XenInput, c.xendev); - struct xenkbd_page *page = xenfb->c.page; - - /* We don't understand any keyboard events, so just ignore them. */ - if (page->out_prod == page->out_cons) - return; - page->out_cons = page->out_prod; - xen_be_send_notify(&xenfb->c.xendev); -} - -/* -------------------------------------------------------------------- */ - -static void xenfb_copy_mfns(int mode, int count, xen_pfn_t *dst, void *src) -{ - uint32_t *src32 = src; - uint64_t *src64 = src; - int i; - - for (i = 0; i < count; i++) - dst[i] = (mode == 32) ? src32[i] : src64[i]; -} - -static int xenfb_map_fb(struct XenFB *xenfb) -{ - struct xenfb_page *page = xenfb->c.page; - char *protocol = xenfb->c.xendev.protocol; - int n_fbdirs; - xen_pfn_t *pgmfns = NULL; - xen_pfn_t *fbmfns = NULL; - void *map, *pd; - int mode, ret = -1; - - /* default to native */ - pd = page->pd; - mode = sizeof(unsigned long) * 8; - - if (!protocol) { - /* - * Undefined protocol, some guesswork needed. - * - * Old frontends which don't set the protocol use - * one page directory only, thus pd[1] must be zero. - * pd[1] of the 32bit struct layout and the lower - * 32 bits of pd[0] of the 64bit struct layout have - * the same location, so we can check that ... - */ - uint32_t *ptr32 = NULL; - uint32_t *ptr64 = NULL; -#if defined(__i386__) - ptr32 = (void*)page->pd; - ptr64 = ((void*)page->pd) + 4; -#elif defined(__x86_64__) - ptr32 = ((void*)page->pd) - 4; - ptr64 = (void*)page->pd; -#endif - if (ptr32) { - if (ptr32[1] == 0) { - mode = 32; - pd = ptr32; - } else { - mode = 64; - pd = ptr64; - } - } -#if defined(__x86_64__) - } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_32) == 0) { - /* 64bit dom0, 32bit domU */ - mode = 32; - pd = ((void*)page->pd) - 4; -#elif defined(__i386__) - } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_64) == 0) { - /* 32bit dom0, 64bit domU */ - mode = 64; - pd = ((void*)page->pd) + 4; -#endif - } - - if (xenfb->pixels) { - munmap(xenfb->pixels, xenfb->fbpages * XC_PAGE_SIZE); - xenfb->pixels = NULL; - } - - xenfb->fbpages = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; - n_fbdirs = xenfb->fbpages * mode / 8; - n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; - - pgmfns = g_malloc0(sizeof(xen_pfn_t) * n_fbdirs); - fbmfns = g_malloc0(sizeof(xen_pfn_t) * xenfb->fbpages); - - xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd); - 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); - xenforeignmemory_unmap(xen_fmem, map, n_fbdirs); - - xenfb->pixels = xenforeignmemory_map(xen_fmem, xenfb->c.xendev.dom, - PROT_READ, xenfb->fbpages, fbmfns, NULL); - if (xenfb->pixels == NULL) - goto out; - - ret = 0; /* all is fine */ - -out: - g_free(pgmfns); - g_free(fbmfns); - return ret; -} - -static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim, - int width, int height, int depth, - size_t fb_len, int offset, int row_stride) -{ - size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd); - size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz; - size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz; - size_t fb_len_max = fb_pages * XC_PAGE_SIZE; - int max_width, max_height; - - if (fb_len_lim > fb_len_max) { - xen_be_printf(&xenfb->c.xendev, 0, "fb size limit %zu exceeds %zu, corrected\n", - fb_len_lim, fb_len_max); - fb_len_lim = fb_len_max; - } - if (fb_len_lim && fb_len > fb_len_lim) { - xen_be_printf(&xenfb->c.xendev, 0, "frontend fb size %zu limited to %zu\n", - fb_len, fb_len_lim); - fb_len = fb_len_lim; - } - if (depth != 8 && depth != 16 && depth != 24 && depth != 32) { - xen_be_printf(&xenfb->c.xendev, 0, "can't handle frontend fb depth %d\n", - depth); - return -1; - } - if (row_stride <= 0 || row_stride > fb_len) { - xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend stride %d\n", row_stride); - return -1; - } - max_width = row_stride / (depth / 8); - if (width < 0 || width > max_width) { - xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend width %d limited to %d\n", - width, max_width); - width = max_width; - } - if (offset < 0 || offset >= fb_len) { - xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend offset %d (max %zu)\n", - offset, fb_len - 1); - return -1; - } - max_height = (fb_len - offset) / row_stride; - if (height < 0 || height > max_height) { - xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend height %d limited to %d\n", - height, max_height); - height = max_height; - } - xenfb->fb_len = fb_len; - xenfb->row_stride = row_stride; - xenfb->depth = depth; - xenfb->width = width; - xenfb->height = height; - xenfb->offset = offset; - xenfb->up_fullscreen = 1; - xenfb->do_resize = 1; - xen_be_printf(&xenfb->c.xendev, 1, "framebuffer %dx%dx%d offset %d stride %d\n", - width, height, depth, offset, row_stride); - return 0; -} - -/* A convenient function for munging pixels between different depths */ -#define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \ - for (line = y ; line < (y+h) ; line++) { \ - SRC_T *src = (SRC_T *)(xenfb->pixels \ - + xenfb->offset \ - + (line * xenfb->row_stride) \ - + (x * xenfb->depth / 8)); \ - DST_T *dst = (DST_T *)(data \ - + (line * linesize) \ - + (x * bpp / 8)); \ - int col; \ - const int RSS = 32 - (RSB + GSB + BSB); \ - const int GSS = 32 - (GSB + BSB); \ - const int BSS = 32 - (BSB); \ - const uint32_t RSM = (~0U) << (32 - RSB); \ - const uint32_t GSM = (~0U) << (32 - GSB); \ - const uint32_t BSM = (~0U) << (32 - BSB); \ - const int RDS = 32 - (RDB + GDB + BDB); \ - const int GDS = 32 - (GDB + BDB); \ - const int BDS = 32 - (BDB); \ - const uint32_t RDM = (~0U) << (32 - RDB); \ - const uint32_t GDM = (~0U) << (32 - GDB); \ - const uint32_t BDM = (~0U) << (32 - BDB); \ - for (col = x ; col < (x+w) ; col++) { \ - uint32_t spix = *src; \ - *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \ - (((spix << GSS) & GSM & GDM) >> GDS) | \ - (((spix << BSS) & BSM & BDM) >> BDS); \ - src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \ - dst = (DST_T *) ((unsigned long) dst + bpp / 8); \ - } \ - } - - -/* - * This copies data from the guest framebuffer region, into QEMU's - * displaysurface. qemu uses 16 or 32 bpp. In case the pv framebuffer - * uses something else we must convert and copy, otherwise we can - * supply the buffer directly and no thing here. - */ -static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h) -{ - DisplaySurface *surface = qemu_console_surface(xenfb->c.con); - int line, oops = 0; - int bpp = surface_bits_per_pixel(surface); - int linesize = surface_stride(surface); - uint8_t *data = surface_data(surface); - - if (!is_buffer_shared(surface)) { - switch (xenfb->depth) { - case 8: - if (bpp == 16) { - BLT(uint8_t, uint16_t, 3, 3, 2, 5, 6, 5); - } else if (bpp == 32) { - BLT(uint8_t, uint32_t, 3, 3, 2, 8, 8, 8); - } else { - oops = 1; - } - break; - case 24: - if (bpp == 16) { - BLT(uint32_t, uint16_t, 8, 8, 8, 5, 6, 5); - } else if (bpp == 32) { - BLT(uint32_t, uint32_t, 8, 8, 8, 8, 8, 8); - } else { - oops = 1; - } - break; - default: - oops = 1; - } - } - if (oops) /* should not happen */ - xen_be_printf(&xenfb->c.xendev, 0, "%s: oops: convert %d -> %d bpp?\n", - __FUNCTION__, xenfb->depth, bpp); - - dpy_gfx_update(xenfb->c.con, x, y, w, h); -} - -#ifdef XENFB_TYPE_REFRESH_PERIOD -static int xenfb_queue_full(struct XenFB *xenfb) -{ - struct xenfb_page *page = xenfb->c.page; - uint32_t cons, prod; - - if (!page) - return 1; - - prod = page->in_prod; - cons = page->in_cons; - return prod - cons == XENFB_IN_RING_LEN; -} - -static void xenfb_send_event(struct XenFB *xenfb, union xenfb_in_event *event) -{ - uint32_t prod; - struct xenfb_page *page = xenfb->c.page; - - prod = page->in_prod; - /* caller ensures !xenfb_queue_full() */ - xen_mb(); /* ensure ring space available */ - XENFB_IN_RING_REF(page, prod) = *event; - xen_wmb(); /* ensure ring contents visible */ - page->in_prod = prod + 1; - - xen_be_send_notify(&xenfb->c.xendev); -} - -static void xenfb_send_refresh_period(struct XenFB *xenfb, int period) -{ - union xenfb_in_event event; - - memset(&event, 0, sizeof(event)); - event.type = XENFB_TYPE_REFRESH_PERIOD; - event.refresh_period.period = period; - xenfb_send_event(xenfb, &event); -} -#endif - -/* - * Periodic update of display. - * Also transmit the refresh interval to the frontend. - * - * Never ever do any qemu display operations - * (resize, screen update) outside this function. - * Our screen might be inactive. When asked for - * an update we know it is active. - */ -static void xenfb_update(void *opaque) -{ - struct XenFB *xenfb = opaque; - DisplaySurface *surface; - int i; - - if (xenfb->c.xendev.be_state != XenbusStateConnected) - return; - - if (!xenfb->feature_update) { - /* we don't get update notifications, thus use the - * sledge hammer approach ... */ - xenfb->up_fullscreen = 1; - } - - /* resize if needed */ - if (xenfb->do_resize) { - pixman_format_code_t format; - - xenfb->do_resize = 0; - switch (xenfb->depth) { - case 16: - case 32: - /* console.c supported depth -> buffer can be used directly */ - format = qemu_default_pixman_format(xenfb->depth, true); - surface = qemu_create_displaysurface_from - (xenfb->width, xenfb->height, format, - xenfb->row_stride, xenfb->pixels + xenfb->offset); - break; - default: - /* we must convert stuff */ - surface = qemu_create_displaysurface(xenfb->width, xenfb->height); - break; - } - dpy_gfx_replace_surface(xenfb->c.con, surface); - xen_be_printf(&xenfb->c.xendev, 1, "update: resizing: %dx%d @ %d bpp%s\n", - xenfb->width, xenfb->height, xenfb->depth, - is_buffer_shared(surface) ? " (shared)" : ""); - xenfb->up_fullscreen = 1; - } - - /* run queued updates */ - if (xenfb->up_fullscreen) { - xen_be_printf(&xenfb->c.xendev, 3, "update: fullscreen\n"); - xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height); - } else if (xenfb->up_count) { - xen_be_printf(&xenfb->c.xendev, 3, "update: %d rects\n", xenfb->up_count); - for (i = 0; i < xenfb->up_count; i++) - xenfb_guest_copy(xenfb, - xenfb->up_rects[i].x, - xenfb->up_rects[i].y, - xenfb->up_rects[i].w, - xenfb->up_rects[i].h); - } else { - xen_be_printf(&xenfb->c.xendev, 3, "update: nothing\n"); - } - xenfb->up_count = 0; - xenfb->up_fullscreen = 0; -} - -static void xenfb_update_interval(void *opaque, uint64_t interval) -{ - struct XenFB *xenfb = opaque; - - if (xenfb->feature_update) { -#ifdef XENFB_TYPE_REFRESH_PERIOD - if (xenfb_queue_full(xenfb)) { - return; - } - xenfb_send_refresh_period(xenfb, interval); -#endif - } -} - -/* QEMU display state changed, so refresh the framebuffer copy */ -static void xenfb_invalidate(void *opaque) -{ - struct XenFB *xenfb = opaque; - xenfb->up_fullscreen = 1; -} - -static void xenfb_handle_events(struct XenFB *xenfb) -{ - uint32_t prod, cons, out_cons; - struct xenfb_page *page = xenfb->c.page; - - prod = page->out_prod; - 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 = 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 (type) { - case XENFB_TYPE_UPDATE: - if (xenfb->up_count == UP_QUEUE) - xenfb->up_fullscreen = 1; - if (xenfb->up_fullscreen) - break; - x = MAX(event->update.x, 0); - y = MAX(event->update.y, 0); - w = MIN(event->update.width, xenfb->width - x); - h = MIN(event->update.height, xenfb->height - y); - if (w < 0 || h < 0) { - xen_be_printf(&xenfb->c.xendev, 1, "bogus update ignored\n"); - break; - } - if (x != event->update.x || - y != event->update.y || - w != event->update.width || - h != event->update.height) { - xen_be_printf(&xenfb->c.xendev, 1, "bogus update clipped\n"); - } - if (w == xenfb->width && h > xenfb->height / 2) { - /* scroll detector: updated more than 50% of the lines, - * don't bother keeping track of the rectangles then */ - xenfb->up_fullscreen = 1; - } else { - xenfb->up_rects[xenfb->up_count].x = x; - xenfb->up_rects[xenfb->up_count].y = y; - xenfb->up_rects[xenfb->up_count].w = w; - xenfb->up_rects[xenfb->up_count].h = h; - xenfb->up_count++; - } - break; -#ifdef XENFB_TYPE_RESIZE - case XENFB_TYPE_RESIZE: - if (xenfb_configure_fb(xenfb, xenfb->fb_len, - event->resize.width, - event->resize.height, - event->resize.depth, - xenfb->fb_len, - event->resize.offset, - event->resize.stride) < 0) - break; - xenfb_invalidate(xenfb); - break; -#endif - } - } - xen_mb(); /* ensure we're done with ring contents */ - page->out_cons = cons; -} - -static int fb_init(struct XenDevice *xendev) -{ -#ifdef XENFB_TYPE_RESIZE - xenstore_write_be_int(xendev, "feature-resize", 1); -#endif - return 0; -} - -static int fb_initialise(struct XenDevice *xendev) -{ - struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); - struct xenfb_page *fb_page; - int videoram; - int rc; - - if (xenstore_read_fe_int(xendev, "videoram", &videoram) == -1) - videoram = 0; - - rc = common_bind(&fb->c); - if (rc != 0) - return rc; - - fb_page = fb->c.page; - rc = xenfb_configure_fb(fb, videoram * 1024 * 1024U, - fb_page->width, fb_page->height, fb_page->depth, - fb_page->mem_length, 0, fb_page->line_length); - if (rc != 0) - return rc; - - rc = xenfb_map_fb(fb); - if (rc != 0) - return rc; - -#if 0 /* handled in xen_init_display() for now */ - if (!fb->have_console) { - fb->c.ds = graphic_console_init(xenfb_update, - xenfb_invalidate, - NULL, - NULL, - fb); - fb->have_console = 1; - } -#endif - - if (xenstore_read_fe_int(xendev, "feature-update", &fb->feature_update) == -1) - fb->feature_update = 0; - if (fb->feature_update) - xenstore_write_be_int(xendev, "request-update", 1); - - xen_be_printf(xendev, 1, "feature-update=%d, videoram=%d\n", - fb->feature_update, videoram); - return 0; -} - -static void fb_disconnect(struct XenDevice *xendev) -{ - struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); - - /* - * FIXME: qemu can't un-init gfx display (yet?). - * 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); - if (fb->pixels == MAP_FAILED) { - xen_be_printf(xendev, 0, - "Couldn't replace the framebuffer with anonymous memory errno=%d\n", - errno); - } - common_unbind(&fb->c); - fb->feature_update = 0; - fb->bug_trigger = 0; -} - -static void fb_frontend_changed(struct XenDevice *xendev, const char *node) -{ - struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); - - /* - * Set state to Connected *again* once the frontend switched - * to connected. We must trigger the watch a second time to - * workaround a frontend bug. - */ - if (fb->bug_trigger == 0 && strcmp(node, "state") == 0 && - xendev->fe_state == XenbusStateConnected && - xendev->be_state == XenbusStateConnected) { - xen_be_printf(xendev, 2, "re-trigger connected (frontend bug)\n"); - xen_be_set_state(xendev, XenbusStateConnected); - fb->bug_trigger = 1; /* only once */ - } -} - -static void fb_event(struct XenDevice *xendev) -{ - struct XenFB *xenfb = container_of(xendev, struct XenFB, c.xendev); - - xenfb_handle_events(xenfb); - xen_be_send_notify(&xenfb->c.xendev); -} - -/* -------------------------------------------------------------------- */ - -struct XenDevOps xen_kbdmouse_ops = { - .size = sizeof(struct XenInput), - .init = input_init, - .initialise = input_initialise, - .connected = input_connected, - .disconnect = input_disconnect, - .event = input_event, -}; - -struct XenDevOps xen_framebuffer_ops = { - .size = sizeof(struct XenFB), - .init = fb_init, - .initialise = fb_initialise, - .disconnect = fb_disconnect, - .event = fb_event, - .frontend_changed = fb_frontend_changed, -}; - -static const GraphicHwOps xenfb_ops = { - .invalidate = xenfb_invalidate, - .gfx_update = xenfb_update, - .update_interval = xenfb_update_interval, -}; - -/* - * FIXME/TODO: Kill this. - * Temporary needed while DisplayState reorganization is in flight. - */ -void xen_init_display(int domid) -{ - struct XenDevice *xfb, *xin; - struct XenFB *fb; - struct XenInput *in; - int i = 0; - -wait_more: - i++; - main_loop_wait(true); - xfb = xen_be_find_xendev("vfb", domid, 0); - xin = xen_be_find_xendev("vkbd", domid, 0); - if (!xfb || !xin) { - if (i < 256) { - usleep(10000); - goto wait_more; - } - xen_be_printf(NULL, 1, "displaystate setup failed\n"); - return; - } - - /* vfb */ - fb = container_of(xfb, struct XenFB, c.xendev); - fb->c.con = graphic_console_init(NULL, 0, &xenfb_ops, fb); - fb->have_console = 1; - - /* vkbd */ - in = container_of(xin, struct XenInput, c.xendev); - in->c.con = fb->c.con; - - /* retry ->init() */ - xen_be_check_state(xin); - xen_be_check_state(xfb); -} diff --git a/qemu/hw/dma/Makefile.objs b/qemu/hw/dma/Makefile.objs deleted file mode 100644 index a1abbcf74..000000000 --- a/qemu/hw/dma/Makefile.objs +++ /dev/null @@ -1,14 +0,0 @@ -common-obj-$(CONFIG_PUV3) += puv3_dma.o -common-obj-$(CONFIG_RC4030) += rc4030.o -common-obj-$(CONFIG_PL080) += pl080.o -common-obj-$(CONFIG_PL330) += pl330.o -common-obj-$(CONFIG_I82374) += i82374.o -common-obj-$(CONFIG_I8257) += i8257.o -common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o -common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o -common-obj-$(CONFIG_STP2000) += sparc32_dma.o -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 deleted file mode 100644 index 542117599..000000000 --- a/qemu/hw/dma/bcm2835_dma.c +++ /dev/null @@ -1,409 +0,0 @@ -/* - * 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 deleted file mode 100644 index d5650eb88..000000000 --- a/qemu/hw/dma/etraxfs_dma.c +++ /dev/null @@ -1,783 +0,0 @@ -/* - * QEMU ETRAX DMA Controller. - * - * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB. - * - * 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 "exec/address-spaces.h" -#include "qemu-common.h" -#include "sysemu/sysemu.h" - -#include "hw/cris/etraxfs_dma.h" - -#define D(x) - -#define RW_DATA (0x0 / 4) -#define RW_SAVED_DATA (0x58 / 4) -#define RW_SAVED_DATA_BUF (0x5c / 4) -#define RW_GROUP (0x60 / 4) -#define RW_GROUP_DOWN (0x7c / 4) -#define RW_CMD (0x80 / 4) -#define RW_CFG (0x84 / 4) -#define RW_STAT (0x88 / 4) -#define RW_INTR_MASK (0x8c / 4) -#define RW_ACK_INTR (0x90 / 4) -#define R_INTR (0x94 / 4) -#define R_MASKED_INTR (0x98 / 4) -#define RW_STREAM_CMD (0x9c / 4) - -#define DMA_REG_MAX (0x100 / 4) - -/* descriptors */ - -// ------------------------------------------------------------ dma_descr_group -typedef struct dma_descr_group { - uint32_t next; - unsigned eol : 1; - unsigned tol : 1; - unsigned bol : 1; - unsigned : 1; - unsigned intr : 1; - unsigned : 2; - unsigned en : 1; - unsigned : 7; - unsigned dis : 1; - unsigned md : 16; - struct dma_descr_group *up; - union { - struct dma_descr_context *context; - struct dma_descr_group *group; - } down; -} dma_descr_group; - -// ---------------------------------------------------------- dma_descr_context -typedef struct dma_descr_context { - uint32_t next; - unsigned eol : 1; - unsigned : 3; - unsigned intr : 1; - unsigned : 1; - unsigned store_mode : 1; - unsigned en : 1; - unsigned : 7; - unsigned dis : 1; - unsigned md0 : 16; - unsigned md1; - unsigned md2; - unsigned md3; - unsigned md4; - uint32_t saved_data; - uint32_t saved_data_buf; -} dma_descr_context; - -// ------------------------------------------------------------- dma_descr_data -typedef struct dma_descr_data { - uint32_t next; - uint32_t buf; - unsigned eol : 1; - unsigned : 2; - unsigned out_eop : 1; - unsigned intr : 1; - unsigned wait : 1; - unsigned : 2; - unsigned : 3; - unsigned in_eop : 1; - unsigned : 4; - unsigned md : 16; - uint32_t after; -} dma_descr_data; - -/* Constants */ -enum { - regk_dma_ack_pkt = 0x00000100, - regk_dma_anytime = 0x00000001, - regk_dma_array = 0x00000008, - regk_dma_burst = 0x00000020, - regk_dma_client = 0x00000002, - regk_dma_copy_next = 0x00000010, - regk_dma_copy_up = 0x00000020, - regk_dma_data_at_eol = 0x00000001, - regk_dma_dis_c = 0x00000010, - regk_dma_dis_g = 0x00000020, - regk_dma_idle = 0x00000001, - regk_dma_intern = 0x00000004, - regk_dma_load_c = 0x00000200, - regk_dma_load_c_n = 0x00000280, - regk_dma_load_c_next = 0x00000240, - regk_dma_load_d = 0x00000140, - regk_dma_load_g = 0x00000300, - regk_dma_load_g_down = 0x000003c0, - regk_dma_load_g_next = 0x00000340, - regk_dma_load_g_up = 0x00000380, - regk_dma_next_en = 0x00000010, - regk_dma_next_pkt = 0x00000010, - regk_dma_no = 0x00000000, - regk_dma_only_at_wait = 0x00000000, - regk_dma_restore = 0x00000020, - regk_dma_rst = 0x00000001, - regk_dma_running = 0x00000004, - regk_dma_rw_cfg_default = 0x00000000, - regk_dma_rw_cmd_default = 0x00000000, - regk_dma_rw_intr_mask_default = 0x00000000, - regk_dma_rw_stat_default = 0x00000101, - regk_dma_rw_stream_cmd_default = 0x00000000, - regk_dma_save_down = 0x00000020, - regk_dma_save_up = 0x00000020, - regk_dma_set_reg = 0x00000050, - regk_dma_set_w_size1 = 0x00000190, - regk_dma_set_w_size2 = 0x000001a0, - regk_dma_set_w_size4 = 0x000001c0, - regk_dma_stopped = 0x00000002, - regk_dma_store_c = 0x00000002, - regk_dma_store_descr = 0x00000000, - regk_dma_store_g = 0x00000004, - regk_dma_store_md = 0x00000001, - regk_dma_sw = 0x00000008, - regk_dma_update_down = 0x00000020, - regk_dma_yes = 0x00000001 -}; - -enum dma_ch_state -{ - RST = 1, - STOPPED = 2, - RUNNING = 4 -}; - -struct fs_dma_channel -{ - qemu_irq irq; - struct etraxfs_dma_client *client; - - /* Internal status. */ - int stream_cmd_src; - enum dma_ch_state state; - - unsigned int input : 1; - unsigned int eol : 1; - - struct dma_descr_group current_g; - struct dma_descr_context current_c; - struct dma_descr_data current_d; - - /* Control registers. */ - uint32_t regs[DMA_REG_MAX]; -}; - -struct fs_dma_ctrl -{ - MemoryRegion mmio; - int nr_channels; - struct fs_dma_channel *channels; - - QEMUBH *bh; -}; - -static void DMA_run(void *opaque); -static int channel_out_run(struct fs_dma_ctrl *ctrl, int c); - -static inline uint32_t channel_reg(struct fs_dma_ctrl *ctrl, int c, int reg) -{ - return ctrl->channels[c].regs[reg]; -} - -static inline int channel_stopped(struct fs_dma_ctrl *ctrl, int c) -{ - return channel_reg(ctrl, c, RW_CFG) & 2; -} - -static inline int channel_en(struct fs_dma_ctrl *ctrl, int c) -{ - return (channel_reg(ctrl, c, RW_CFG) & 1) - && ctrl->channels[c].client; -} - -static inline int fs_channel(hwaddr addr) -{ - /* Every channel has a 0x2000 ctrl register map. */ - return addr >> 13; -} - -#ifdef USE_THIS_DEAD_CODE -static void channel_load_g(struct fs_dma_ctrl *ctrl, int c) -{ - hwaddr addr = channel_reg(ctrl, c, RW_GROUP); - - /* Load and decode. FIXME: handle endianness. */ - cpu_physical_memory_read (addr, - (void *) &ctrl->channels[c].current_g, - sizeof ctrl->channels[c].current_g); -} - -static void dump_c(int ch, struct dma_descr_context *c) -{ - printf("%s ch=%d\n", __func__, ch); - printf("next=%x\n", c->next); - printf("saved_data=%x\n", c->saved_data); - printf("saved_data_buf=%x\n", c->saved_data_buf); - printf("eol=%x\n", (uint32_t) c->eol); -} - -static void dump_d(int ch, struct dma_descr_data *d) -{ - printf("%s ch=%d\n", __func__, ch); - printf("next=%x\n", d->next); - printf("buf=%x\n", d->buf); - printf("after=%x\n", d->after); - printf("intr=%x\n", (uint32_t) d->intr); - printf("out_eop=%x\n", (uint32_t) d->out_eop); - printf("in_eop=%x\n", (uint32_t) d->in_eop); - printf("eol=%x\n", (uint32_t) d->eol); -} -#endif - -static void channel_load_c(struct fs_dma_ctrl *ctrl, int c) -{ - hwaddr addr = channel_reg(ctrl, c, RW_GROUP_DOWN); - - /* Load and decode. FIXME: handle endianness. */ - cpu_physical_memory_read (addr, - (void *) &ctrl->channels[c].current_c, - sizeof ctrl->channels[c].current_c); - - D(dump_c(c, &ctrl->channels[c].current_c)); - /* I guess this should update the current pos. */ - ctrl->channels[c].regs[RW_SAVED_DATA] = - (uint32_t)(unsigned long)ctrl->channels[c].current_c.saved_data; - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = - (uint32_t)(unsigned long)ctrl->channels[c].current_c.saved_data_buf; -} - -static void channel_load_d(struct fs_dma_ctrl *ctrl, int c) -{ - hwaddr addr = channel_reg(ctrl, c, RW_SAVED_DATA); - - /* Load and decode. FIXME: handle endianness. */ - D(printf("%s ch=%d addr=" TARGET_FMT_plx "\n", __func__, c, addr)); - cpu_physical_memory_read (addr, - (void *) &ctrl->channels[c].current_d, - sizeof ctrl->channels[c].current_d); - - D(dump_d(c, &ctrl->channels[c].current_d)); - ctrl->channels[c].regs[RW_DATA] = addr; -} - -static void channel_store_c(struct fs_dma_ctrl *ctrl, int c) -{ - hwaddr addr = channel_reg(ctrl, c, RW_GROUP_DOWN); - - /* Encode and store. FIXME: handle endianness. */ - D(printf("%s ch=%d addr=" TARGET_FMT_plx "\n", __func__, c, addr)); - D(dump_d(c, &ctrl->channels[c].current_d)); - cpu_physical_memory_write (addr, - (void *) &ctrl->channels[c].current_c, - sizeof ctrl->channels[c].current_c); -} - -static void channel_store_d(struct fs_dma_ctrl *ctrl, int c) -{ - hwaddr addr = channel_reg(ctrl, c, RW_SAVED_DATA); - - /* Encode and store. FIXME: handle endianness. */ - D(printf("%s ch=%d addr=" TARGET_FMT_plx "\n", __func__, c, addr)); - cpu_physical_memory_write (addr, - (void *) &ctrl->channels[c].current_d, - sizeof ctrl->channels[c].current_d); -} - -static inline void channel_stop(struct fs_dma_ctrl *ctrl, int c) -{ - /* FIXME: */ -} - -static inline void channel_start(struct fs_dma_ctrl *ctrl, int c) -{ - if (ctrl->channels[c].client) - { - ctrl->channels[c].eol = 0; - ctrl->channels[c].state = RUNNING; - if (!ctrl->channels[c].input) - channel_out_run(ctrl, c); - } else - printf("WARNING: starting DMA ch %d with no client\n", c); - - qemu_bh_schedule_idle(ctrl->bh); -} - -static void channel_continue(struct fs_dma_ctrl *ctrl, int c) -{ - if (!channel_en(ctrl, c) - || channel_stopped(ctrl, c) - || ctrl->channels[c].state != RUNNING - /* Only reload the current data descriptor if it has eol set. */ - || !ctrl->channels[c].current_d.eol) { - D(printf("continue failed ch=%d state=%d stopped=%d en=%d eol=%d\n", - c, ctrl->channels[c].state, - channel_stopped(ctrl, c), - channel_en(ctrl,c), - ctrl->channels[c].eol)); - D(dump_d(c, &ctrl->channels[c].current_d)); - return; - } - - /* Reload the current descriptor. */ - channel_load_d(ctrl, c); - - /* If the current descriptor cleared the eol flag and we had already - reached eol state, do the continue. */ - if (!ctrl->channels[c].current_d.eol && ctrl->channels[c].eol) { - D(printf("continue %d ok %x\n", c, - ctrl->channels[c].current_d.next)); - ctrl->channels[c].regs[RW_SAVED_DATA] = - (uint32_t)(unsigned long)ctrl->channels[c].current_d.next; - channel_load_d(ctrl, c); - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = - (uint32_t)(unsigned long)ctrl->channels[c].current_d.buf; - - channel_start(ctrl, c); - } - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = - (uint32_t)(unsigned long)ctrl->channels[c].current_d.buf; -} - -static void channel_stream_cmd(struct fs_dma_ctrl *ctrl, int c, uint32_t v) -{ - unsigned int cmd = v & ((1 << 10) - 1); - - D(printf("%s ch=%d cmd=%x\n", - __func__, c, cmd)); - if (cmd & regk_dma_load_d) { - channel_load_d(ctrl, c); - if (cmd & regk_dma_burst) - channel_start(ctrl, c); - } - - if (cmd & regk_dma_load_c) { - channel_load_c(ctrl, c); - } -} - -static void channel_update_irq(struct fs_dma_ctrl *ctrl, int c) -{ - D(printf("%s %d\n", __func__, c)); - ctrl->channels[c].regs[R_INTR] &= - ~(ctrl->channels[c].regs[RW_ACK_INTR]); - - ctrl->channels[c].regs[R_MASKED_INTR] = - ctrl->channels[c].regs[R_INTR] - & ctrl->channels[c].regs[RW_INTR_MASK]; - - D(printf("%s: chan=%d masked_intr=%x\n", __func__, - c, - ctrl->channels[c].regs[R_MASKED_INTR])); - - qemu_set_irq(ctrl->channels[c].irq, - !!ctrl->channels[c].regs[R_MASKED_INTR]); -} - -static int channel_out_run(struct fs_dma_ctrl *ctrl, int c) -{ - uint32_t len; - uint32_t saved_data_buf; - unsigned char buf[2 * 1024]; - - struct dma_context_metadata meta; - bool send_context = true; - - if (ctrl->channels[c].eol) - return 0; - - do { - bool out_eop; - D(printf("ch=%d buf=%x after=%x\n", - c, - (uint32_t)ctrl->channels[c].current_d.buf, - (uint32_t)ctrl->channels[c].current_d.after)); - - if (send_context) { - if (ctrl->channels[c].client->client.metadata_push) { - meta.metadata = ctrl->channels[c].current_d.md; - ctrl->channels[c].client->client.metadata_push( - ctrl->channels[c].client->client.opaque, - &meta); - } - send_context = false; - } - - channel_load_d(ctrl, c); - saved_data_buf = channel_reg(ctrl, c, RW_SAVED_DATA_BUF); - len = (uint32_t)(unsigned long) - ctrl->channels[c].current_d.after; - len -= saved_data_buf; - - if (len > sizeof buf) - len = sizeof buf; - cpu_physical_memory_read (saved_data_buf, buf, len); - - out_eop = ((saved_data_buf + len) == - ctrl->channels[c].current_d.after) && - ctrl->channels[c].current_d.out_eop; - - 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) { - 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; - - if (saved_data_buf == (uint32_t)(unsigned long) - ctrl->channels[c].current_d.after) { - /* Done. Step to next. */ - if (ctrl->channels[c].current_d.out_eop) { - send_context = true; - } - if (ctrl->channels[c].current_d.intr) { - /* data intr. */ - D(printf("signal intr %d eol=%d\n", - len, ctrl->channels[c].current_d.eol)); - ctrl->channels[c].regs[R_INTR] |= (1 << 2); - channel_update_irq(ctrl, c); - } - channel_store_d(ctrl, c); - if (ctrl->channels[c].current_d.eol) { - D(printf("channel %d EOL\n", c)); - ctrl->channels[c].eol = 1; - - /* Mark the context as disabled. */ - ctrl->channels[c].current_c.dis = 1; - channel_store_c(ctrl, c); - - channel_stop(ctrl, c); - } else { - ctrl->channels[c].regs[RW_SAVED_DATA] = - (uint32_t)(unsigned long)ctrl-> - channels[c].current_d.next; - /* Load new descriptor. */ - channel_load_d(ctrl, c); - saved_data_buf = (uint32_t)(unsigned long) - ctrl->channels[c].current_d.buf; - } - - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = - saved_data_buf; - D(dump_d(c, &ctrl->channels[c].current_d)); - } - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = saved_data_buf; - } while (!ctrl->channels[c].eol); - return 1; -} - -static int channel_in_process(struct fs_dma_ctrl *ctrl, int c, - unsigned char *buf, int buflen, int eop) -{ - uint32_t len; - uint32_t saved_data_buf; - - if (ctrl->channels[c].eol == 1) - return 0; - - channel_load_d(ctrl, c); - saved_data_buf = channel_reg(ctrl, c, RW_SAVED_DATA_BUF); - len = (uint32_t)(unsigned long)ctrl->channels[c].current_d.after; - len -= saved_data_buf; - - if (len > buflen) - len = buflen; - - cpu_physical_memory_write (saved_data_buf, buf, len); - saved_data_buf += len; - - if (saved_data_buf == - (uint32_t)(unsigned long)ctrl->channels[c].current_d.after - || eop) { - uint32_t r_intr = ctrl->channels[c].regs[R_INTR]; - - D(printf("in dscr end len=%d\n", - ctrl->channels[c].current_d.after - - ctrl->channels[c].current_d.buf)); - ctrl->channels[c].current_d.after = saved_data_buf; - - /* Done. Step to next. */ - if (ctrl->channels[c].current_d.intr) { - /* TODO: signal eop to the client. */ - /* data intr. */ - ctrl->channels[c].regs[R_INTR] |= 3; - } - if (eop) { - ctrl->channels[c].current_d.in_eop = 1; - ctrl->channels[c].regs[R_INTR] |= 8; - } - if (r_intr != ctrl->channels[c].regs[R_INTR]) - channel_update_irq(ctrl, c); - - channel_store_d(ctrl, c); - D(dump_d(c, &ctrl->channels[c].current_d)); - - if (ctrl->channels[c].current_d.eol) { - D(printf("channel %d EOL\n", c)); - ctrl->channels[c].eol = 1; - - /* Mark the context as disabled. */ - ctrl->channels[c].current_c.dis = 1; - channel_store_c(ctrl, c); - - channel_stop(ctrl, c); - } else { - ctrl->channels[c].regs[RW_SAVED_DATA] = - (uint32_t)(unsigned long)ctrl-> - channels[c].current_d.next; - /* Load new descriptor. */ - channel_load_d(ctrl, c); - saved_data_buf = (uint32_t)(unsigned long) - ctrl->channels[c].current_d.buf; - } - } - - ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = saved_data_buf; - return len; -} - -static inline int channel_in_run(struct fs_dma_ctrl *ctrl, int c) -{ - if (ctrl->channels[c].client->client.pull) { - ctrl->channels[c].client->client.pull( - ctrl->channels[c].client->client.opaque); - return 1; - } else - return 0; -} - -static uint32_t dma_rinvalid (void *opaque, hwaddr addr) -{ - hw_error("Unsupported short raccess. reg=" TARGET_FMT_plx "\n", addr); - return 0; -} - -static uint64_t -dma_read(void *opaque, hwaddr addr, unsigned int size) -{ - struct fs_dma_ctrl *ctrl = opaque; - int c; - uint32_t r = 0; - - if (size != 4) { - dma_rinvalid(opaque, addr); - } - - /* Make addr relative to this channel and bounded to nr regs. */ - c = fs_channel(addr); - addr &= 0xff; - addr >>= 2; - switch (addr) - { - case RW_STAT: - r = ctrl->channels[c].state & 7; - r |= ctrl->channels[c].eol << 5; - r |= ctrl->channels[c].stream_cmd_src << 8; - break; - - default: - r = ctrl->channels[c].regs[addr]; - D(printf ("%s c=%d addr=" TARGET_FMT_plx "\n", - __func__, c, addr)); - break; - } - return r; -} - -static void -dma_winvalid (void *opaque, hwaddr addr, uint32_t value) -{ - hw_error("Unsupported short waccess. reg=" TARGET_FMT_plx "\n", addr); -} - -static void -dma_update_state(struct fs_dma_ctrl *ctrl, int c) -{ - if (ctrl->channels[c].regs[RW_CFG] & 2) - ctrl->channels[c].state = STOPPED; - if (!(ctrl->channels[c].regs[RW_CFG] & 1)) - ctrl->channels[c].state = RST; -} - -static void -dma_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - struct fs_dma_ctrl *ctrl = opaque; - uint32_t value = val64; - int c; - - if (size != 4) { - dma_winvalid(opaque, addr, value); - } - - /* Make addr relative to this channel and bounded to nr regs. */ - c = fs_channel(addr); - addr &= 0xff; - addr >>= 2; - switch (addr) - { - case RW_DATA: - ctrl->channels[c].regs[addr] = value; - break; - - case RW_CFG: - ctrl->channels[c].regs[addr] = value; - dma_update_state(ctrl, c); - break; - case RW_CMD: - /* continue. */ - if (value & ~1) - printf("Invalid store to ch=%d RW_CMD %x\n", - c, value); - ctrl->channels[c].regs[addr] = value; - channel_continue(ctrl, c); - break; - - case RW_SAVED_DATA: - case RW_SAVED_DATA_BUF: - case RW_GROUP: - case RW_GROUP_DOWN: - ctrl->channels[c].regs[addr] = value; - break; - - case RW_ACK_INTR: - case RW_INTR_MASK: - ctrl->channels[c].regs[addr] = value; - channel_update_irq(ctrl, c); - if (addr == RW_ACK_INTR) - ctrl->channels[c].regs[RW_ACK_INTR] = 0; - break; - - case RW_STREAM_CMD: - if (value & ~1023) - printf("Invalid store to ch=%d " - "RW_STREAMCMD %x\n", - c, value); - ctrl->channels[c].regs[addr] = value; - D(printf("stream_cmd ch=%d\n", c)); - channel_stream_cmd(ctrl, c, value); - break; - - default: - D(printf ("%s c=%d " TARGET_FMT_plx "\n", - __func__, c, addr)); - break; - } -} - -static const MemoryRegionOps dma_ops = { - .read = dma_read, - .write = dma_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 4 - } -}; - -static int etraxfs_dmac_run(void *opaque) -{ - struct fs_dma_ctrl *ctrl = opaque; - int i; - int p = 0; - - for (i = 0; - i < ctrl->nr_channels; - i++) - { - if (ctrl->channels[i].state == RUNNING) - { - if (ctrl->channels[i].input) { - p += channel_in_run(ctrl, i); - } else { - p += channel_out_run(ctrl, i); - } - } - } - return p; -} - -int etraxfs_dmac_input(struct etraxfs_dma_client *client, - void *buf, int len, int eop) -{ - return channel_in_process(client->ctrl, client->channel, - buf, len, eop); -} - -/* Connect an IRQ line with a channel. */ -void etraxfs_dmac_connect(void *opaque, int c, qemu_irq *line, int input) -{ - struct fs_dma_ctrl *ctrl = opaque; - ctrl->channels[c].irq = *line; - ctrl->channels[c].input = input; -} - -void etraxfs_dmac_connect_client(void *opaque, int c, - struct etraxfs_dma_client *cl) -{ - struct fs_dma_ctrl *ctrl = opaque; - cl->ctrl = ctrl; - cl->channel = c; - ctrl->channels[c].client = cl; -} - - -static void DMA_run(void *opaque) -{ - struct fs_dma_ctrl *etraxfs_dmac = opaque; - int p = 1; - - if (runstate_is_running()) - p = etraxfs_dmac_run(etraxfs_dmac); - - if (p) - qemu_bh_schedule_idle(etraxfs_dmac->bh); -} - -void *etraxfs_dmac_init(hwaddr base, int nr_channels) -{ - struct fs_dma_ctrl *ctrl = NULL; - - ctrl = g_malloc0(sizeof *ctrl); - - ctrl->bh = qemu_bh_new(DMA_run, ctrl); - - ctrl->nr_channels = nr_channels; - ctrl->channels = g_malloc0(sizeof ctrl->channels[0] * nr_channels); - - memory_region_init_io(&ctrl->mmio, NULL, &dma_ops, ctrl, "etraxfs-dma", - nr_channels * 0x2000); - memory_region_add_subregion(get_system_memory(), base, &ctrl->mmio); - - return ctrl; -} diff --git a/qemu/hw/dma/i82374.c b/qemu/hw/dma/i82374.c deleted file mode 100644 index 6c0f975df..000000000 --- a/qemu/hw/dma/i82374.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * QEMU Intel 82374 emulation (Enhanced DMA controller) - * - * Copyright (c) 2010 Hervé Poussineau - * - * 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/isa/isa.h" - -#define TYPE_I82374 "i82374" -#define I82374(obj) OBJECT_CHECK(I82374State, (obj), TYPE_I82374) - -//#define DEBUG_I82374 - -#ifdef DEBUG_I82374 -#define DPRINTF(fmt, ...) \ -do { fprintf(stderr, "i82374: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ -do {} while (0) -#endif -#define BADF(fmt, ...) \ -do { fprintf(stderr, "i82374 ERROR: " fmt , ## __VA_ARGS__); } while (0) - -typedef struct I82374State { - ISADevice parent_obj; - - uint32_t iobase; - uint8_t commands[8]; - PortioList port_list; -} I82374State; - -static const VMStateDescription vmstate_i82374 = { - .name = "i82374", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT8_ARRAY(commands, I82374State, 8), - VMSTATE_END_OF_LIST() - }, -}; - -static uint32_t i82374_read_isr(void *opaque, uint32_t nport) -{ - uint32_t val = 0; - - BADF("%s: %08x\n", __func__, nport); - - DPRINTF("%s: %08x=%08x\n", __func__, nport, val); - return val; -} - -static void i82374_write_command(void *opaque, uint32_t nport, uint32_t data) -{ - DPRINTF("%s: %08x=%08x\n", __func__, nport, data); - - if (data != 0x42) { - /* Not Stop S/G command */ - BADF("%s: %08x=%08x\n", __func__, nport, data); - } -} - -static uint32_t i82374_read_status(void *opaque, uint32_t nport) -{ - uint32_t val = 0; - - BADF("%s: %08x\n", __func__, nport); - - DPRINTF("%s: %08x=%08x\n", __func__, nport, val); - return val; -} - -static void i82374_write_descriptor(void *opaque, uint32_t nport, uint32_t data) -{ - DPRINTF("%s: %08x=%08x\n", __func__, nport, data); - - BADF("%s: %08x=%08x\n", __func__, nport, data); -} - -static uint32_t i82374_read_descriptor(void *opaque, uint32_t nport) -{ - uint32_t val = 0; - - BADF("%s: %08x\n", __func__, nport); - - DPRINTF("%s: %08x=%08x\n", __func__, nport, val); - return val; -} - -static const MemoryRegionPortio i82374_portio_list[] = { - { 0x0A, 1, 1, .read = i82374_read_isr, }, - { 0x10, 8, 1, .write = i82374_write_command, }, - { 0x18, 8, 1, .read = i82374_read_status, }, - { 0x20, 0x20, 1, - .write = i82374_write_descriptor, .read = i82374_read_descriptor, }, - PORTIO_END_OF_LIST(), -}; - -static void i82374_realize(DeviceState *dev, Error **errp) -{ - I82374State *s = I82374(dev); - - portio_list_init(&s->port_list, OBJECT(s), i82374_portio_list, s, - "i82374"); - portio_list_add(&s->port_list, isa_address_space_io(&s->parent_obj), - s->iobase); - - 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", I82374State, iobase, 0x400), - DEFINE_PROP_END_OF_LIST() -}; - -static void i82374_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = i82374_realize; - dc->vmsd = &vmstate_i82374; - dc->props = i82374_properties; -} - -static const TypeInfo i82374_info = { - .name = TYPE_I82374, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(I82374State), - .class_init = i82374_class_init, -}; - -static void i82374_register_types(void) -{ - type_register_static(&i82374_info); -} - -type_init(i82374_register_types) diff --git a/qemu/hw/dma/i8257.c b/qemu/hw/dma/i8257.c deleted file mode 100644 index f345c5476..000000000 --- a/qemu/hw/dma/i8257.c +++ /dev/null @@ -1,643 +0,0 @@ -/* - * QEMU DMA emulation - * - * Copyright (c) 2003-2004 Vassili Karpov (malc) - * - * 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/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__) -#ifdef DEBUG_DMA -#define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__) -#define ldebug(...) fprintf (stderr, "dma: " __VA_ARGS__) -#else -#define linfo(...) -#define ldebug(...) -#endif - -#define ADDR 0 -#define COUNT 1 - -enum { - CMD_MEMORY_TO_MEMORY = 0x01, - CMD_FIXED_ADDRESS = 0x02, - CMD_BLOCK_CONTROLLER = 0x04, - CMD_COMPRESSED_TIME = 0x08, - CMD_CYCLIC_PRIORITY = 0x10, - CMD_EXTENDED_WRITE = 0x20, - CMD_LOW_DREQ = 0x40, - CMD_LOW_DACK = 0x80, - CMD_NOT_SUPPORTED = CMD_MEMORY_TO_MEMORY | CMD_FIXED_ADDRESS - | CMD_COMPRESSED_TIME | CMD_CYCLIC_PRIORITY | CMD_EXTENDED_WRITE - | CMD_LOW_DREQ | CMD_LOW_DACK - -}; - -static void i8257_dma_run(void *opaque); - -static const int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0}; - -static void i8257_write_page(void *opaque, uint32_t nport, uint32_t data) -{ - I8257State *d = opaque; - int ichan; - - ichan = channels[nport & 7]; - if (-1 == ichan) { - dolog ("invalid channel %#x %#x\n", nport, data); - return; - } - d->regs[ichan].page = data; -} - -static void i8257_write_pageh(void *opaque, uint32_t nport, uint32_t data) -{ - I8257State *d = opaque; - int ichan; - - ichan = channels[nport & 7]; - if (-1 == ichan) { - dolog ("invalid channel %#x %#x\n", nport, data); - return; - } - d->regs[ichan].pageh = data; -} - -static uint32_t i8257_read_page(void *opaque, uint32_t nport) -{ - I8257State *d = opaque; - int ichan; - - ichan = channels[nport & 7]; - if (-1 == ichan) { - dolog ("invalid channel read %#x\n", nport); - return 0; - } - return d->regs[ichan].page; -} - -static uint32_t i8257_read_pageh(void *opaque, uint32_t nport) -{ - I8257State *d = opaque; - int ichan; - - ichan = channels[nport & 7]; - if (-1 == ichan) { - dolog ("invalid channel read %#x\n", nport); - return 0; - } - return d->regs[ichan].pageh; -} - -static inline void i8257_init_chan(I8257State *d, int ichan) -{ - I8257Regs *r; - - r = d->regs + ichan; - r->now[ADDR] = r->base[ADDR] << d->dshift; - r->now[COUNT] = 0; -} - -static inline int i8257_getff(I8257State *d) -{ - int ff; - - ff = d->flip_flop; - d->flip_flop = !ff; - return ff; -} - -static uint64_t i8257_read_chan(void *opaque, hwaddr nport, unsigned size) -{ - I8257State *d = opaque; - int ichan, nreg, iport, ff, val, dir; - I8257Regs *r; - - iport = (nport >> d->dshift) & 0x0f; - ichan = iport >> 1; - nreg = iport & 1; - r = d->regs + ichan; - - dir = ((r->mode >> 5) & 1) ? -1 : 1; - ff = i8257_getff(d); - if (nreg) - val = (r->base[COUNT] << d->dshift) - r->now[COUNT]; - else - val = r->now[ADDR] + r->now[COUNT] * dir; - - ldebug ("read_chan %#x -> %d\n", iport, val); - return (val >> (d->dshift + (ff << 3))) & 0xff; -} - -static void i8257_write_chan(void *opaque, hwaddr nport, uint64_t data, - unsigned int size) -{ - I8257State *d = opaque; - int iport, ichan, nreg; - I8257Regs *r; - - iport = (nport >> d->dshift) & 0x0f; - ichan = iport >> 1; - nreg = iport & 1; - r = d->regs + ichan; - if (i8257_getff(d)) { - r->base[nreg] = (r->base[nreg] & 0xff) | ((data << 8) & 0xff00); - i8257_init_chan(d, ichan); - } else { - r->base[nreg] = (r->base[nreg] & 0xff00) | (data & 0xff); - } -} - -static void i8257_write_cont(void *opaque, hwaddr nport, uint64_t data, - unsigned int size) -{ - I8257State *d = opaque; - int iport, ichan = 0; - - iport = (nport >> d->dshift) & 0x0f; - switch (iport) { - case 0x00: /* command */ - if ((data != 0) && (data & CMD_NOT_SUPPORTED)) { - dolog("command %"PRIx64" not supported\n", data); - return; - } - d->command = data; - break; - - case 0x01: - ichan = data & 3; - if (data & 4) { - d->status |= 1 << (ichan + 4); - } - else { - d->status &= ~(1 << (ichan + 4)); - } - d->status &= ~(1 << ichan); - i8257_dma_run(d); - break; - - case 0x02: /* single mask */ - if (data & 4) - d->mask |= 1 << (data & 3); - else - d->mask &= ~(1 << (data & 3)); - i8257_dma_run(d); - break; - - case 0x03: /* mode */ - { - ichan = data & 3; -#ifdef DEBUG_DMA - { - int op, ai, dir, opmode; - op = (data >> 2) & 3; - ai = (data >> 4) & 1; - dir = (data >> 5) & 1; - opmode = (data >> 6) & 3; - - linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n", - ichan, op, ai, dir, opmode); - } -#endif - d->regs[ichan].mode = data; - break; - } - - case 0x04: /* clear flip flop */ - d->flip_flop = 0; - break; - - case 0x05: /* reset */ - d->flip_flop = 0; - d->mask = ~0; - d->status = 0; - d->command = 0; - break; - - case 0x06: /* clear mask for all channels */ - d->mask = 0; - i8257_dma_run(d); - break; - - case 0x07: /* write mask for all channels */ - d->mask = data; - i8257_dma_run(d); - break; - - default: - dolog ("unknown iport %#x\n", iport); - break; - } - -#ifdef DEBUG_DMA - if (0xc != iport) { - linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n", - nport, ichan, data); - } -#endif -} - -static uint64_t i8257_read_cont(void *opaque, hwaddr nport, unsigned size) -{ - I8257State *d = opaque; - int iport, val; - - iport = (nport >> d->dshift) & 0x0f; - switch (iport) { - case 0x00: /* status */ - val = d->status; - d->status &= 0xf0; - break; - case 0x01: /* mask */ - val = d->mask; - break; - default: - val = 0; - break; - } - - ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val); - return val; -} - -static IsaDmaTransferMode i8257_dma_get_transfer_mode(IsaDma *obj, int nchan) -{ - I8257State *d = I8257(obj); - return (d->regs[nchan & 3].mode >> 2) & 3; -} - -static bool i8257_dma_has_autoinitialization(IsaDma *obj, int nchan) -{ - 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; - - ichan = nchan & 3; - d->status |= 1 << (ichan + 4); - i8257_dma_run(d); -} - -static void i8257_dma_release_DREQ(IsaDma *obj, int nchan) -{ - I8257State *d = I8257(obj); - int ichan; - - ichan = nchan & 3; - d->status &= ~(1 << (ichan + 4)); - i8257_dma_run(d); -} - -static void i8257_channel_run(I8257State *d, int ichan) -{ - int ncont = d->dshift; - int n; - I8257Regs *r = &d->regs[ichan]; -#ifdef DEBUG_DMA - int dir, opmode; - - dir = (r->mode >> 5) & 1; - opmode = (r->mode >> 6) & 3; - - if (dir) { - dolog ("DMA in address decrement mode\n"); - } - if (opmode != 1) { - dolog ("DMA not in single mode select %#x\n", opmode); - } -#endif - - n = r->transfer_handler (r->opaque, ichan + (ncont << 2), - 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 void i8257_dma_run(void *opaque) -{ - I8257State *d = opaque; - int ichan; - int rearm = 0; - - if (d->running) { - rearm = 1; - goto out; - } else { - d->running = 1; - } - - for (ichan = 0; ichan < 4; ichan++) { - int mask; - - mask = 1 << ichan; - - if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) { - i8257_channel_run(d, ichan); - rearm = 1; - } - } - - d->running = 0; -out: - if (rearm) { - qemu_bh_schedule_idle(d->dma_bh); - d->dma_bh_scheduled = true; - } -} - -static void i8257_dma_register_channel(IsaDma *obj, int nchan, - IsaDmaTransferHandler transfer_handler, - void *opaque) -{ - I8257State *d = I8257(obj); - I8257Regs *r; - int ichan; - - ichan = nchan & 3; - - r = d->regs + ichan; - r->transfer_handler = transfer_handler; - r->opaque = opaque; -} - -static int i8257_dma_read_memory(IsaDma *obj, int nchan, void *buf, int pos, - int len) -{ - 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) { - int i; - uint8_t *p = buf; - - cpu_physical_memory_read (addr - pos - len, buf, len); - /* What about 16bit transfers? */ - for (i = 0; i < len >> 1; i++) { - uint8_t b = p[len - i - 1]; - p[i] = b; - } - } - else - cpu_physical_memory_read (addr + pos, buf, len); - - return len; -} - -static int i8257_dma_write_memory(IsaDma *obj, int nchan, void *buf, int pos, - int len) -{ - 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) { - int i; - uint8_t *p = buf; - - cpu_physical_memory_write (addr - pos - len, buf, len); - /* What about 16bit transfers? */ - for (i = 0; i < len; i++) { - uint8_t b = p[len - i - 1]; - p[i] = b; - } - } - else - cpu_physical_memory_write (addr + pos, buf, len); - - return len; -} - -/* 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) -{ - I8257State *d = I8257(obj); - if (d->dma_bh_scheduled) { - qemu_notify_event(); - } -} - -static void i8257_reset(DeviceState *dev) -{ - I8257State *d = I8257(dev); - i8257_write_cont(d, (0x05 << d->dshift), 0, 1); -} - -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; -} - - -static const MemoryRegionOps channel_io_ops = { - .read = i8257_read_chan, - .write = i8257_write_chan, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -/* IOport from page_base */ -static const MemoryRegionPortio page_portio_list[] = { - { 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 = 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 = i8257_read_cont, - .write = i8257_write_cont, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -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) -{ - I8257State *d = opaque; - i8257_dma_run(d); - - 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(isa), - d->base, &d->channel_io); - - isa_register_portio_list(isa, d->page_base, page_portio_list, d, - "dma-page"); - 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, 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); - - 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 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 void i8257_class_init(ObjectClass *klass, void *data) -{ - 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 TypeInfo i8257_info = { - .name = TYPE_I8257, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(I8257State), - .class_init = i8257_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_ISADMA }, - { } - } -}; - -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) -{ - 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 deleted file mode 100644 index 700cd6b43..000000000 --- a/qemu/hw/dma/omap_dma.c +++ /dev/null @@ -1,2103 +0,0 @@ -/* - * TI OMAP DMA gigacell. - * - * Copyright (C) 2006-2008 Andrzej Zaborowski - * Copyright (C) 2007-2008 Lauro Ramos Venancio - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/timer.h" -#include "hw/arm/omap.h" -#include "hw/irq.h" -#include "hw/arm/soc_dma.h" - -struct omap_dma_channel_s { - /* transfer data */ - int burst[2]; - int pack[2]; - int endian[2]; - int endian_lock[2]; - int translate[2]; - enum omap_dma_port port[2]; - hwaddr addr[2]; - omap_dma_addressing_t mode[2]; - uint32_t elements; - uint16_t frames; - int32_t frame_index[2]; - int16_t element_index[2]; - int data_type; - - /* transfer type */ - int transparent_copy; - int constant_fill; - uint32_t color; - int prefetch; - - /* auto init and linked channel data */ - int end_prog; - int repeat; - int auto_init; - int link_enabled; - int link_next_ch; - - /* interruption data */ - int interrupts; - int status; - int cstatus; - - /* state data */ - int active; - int enable; - int sync; - int src_sync; - int pending_request; - int waiting_end_prog; - uint16_t cpc; - int set_update; - - /* sync type */ - int fs; - int bs; - - /* compatibility */ - int omap_3_1_compatible_disable; - - qemu_irq irq; - struct omap_dma_channel_s *sibling; - - struct omap_dma_reg_set_s { - hwaddr src, dest; - int frame; - int element; - int pck_element; - int frame_delta[2]; - int elem_delta[2]; - int frames; - int elements; - int pck_elements; - } active_set; - - struct soc_dma_ch_s *dma; - - /* unused parameters */ - int write_mode; - int priority; - int interleave_disabled; - int type; - int suspend; - int buf_disable; -}; - -struct omap_dma_s { - struct soc_dma_s *dma; - MemoryRegion iomem; - - struct omap_mpu_state_s *mpu; - omap_clk clk; - qemu_irq irq[4]; - void (*intr_update)(struct omap_dma_s *s); - enum omap_dma_model model; - int omap_3_1_mapping_disabled; - - uint32_t gcr; - uint32_t ocp; - uint32_t caps[5]; - uint32_t irqen[4]; - uint32_t irqstat[4]; - - int chans; - struct omap_dma_channel_s ch[32]; - struct omap_dma_lcd_channel_s lcd_ch; -}; - -/* Interrupts */ -#define TIMEOUT_INTR (1 << 0) -#define EVENT_DROP_INTR (1 << 1) -#define HALF_FRAME_INTR (1 << 2) -#define END_FRAME_INTR (1 << 3) -#define LAST_FRAME_INTR (1 << 4) -#define END_BLOCK_INTR (1 << 5) -#define SYNC (1 << 6) -#define END_PKT_INTR (1 << 7) -#define TRANS_ERR_INTR (1 << 8) -#define MISALIGN_INTR (1 << 11) - -static inline void omap_dma_interrupts_update(struct omap_dma_s *s) -{ - s->intr_update(s); -} - -static void omap_dma_channel_load(struct omap_dma_channel_s *ch) -{ - struct omap_dma_reg_set_s *a = &ch->active_set; - int i, normal; - int omap_3_1 = !ch->omap_3_1_compatible_disable; - - /* - * TODO: verify address ranges and alignment - * TODO: port endianness - */ - - a->src = ch->addr[0]; - a->dest = ch->addr[1]; - a->frames = ch->frames; - a->elements = ch->elements; - a->pck_elements = ch->frame_index[!ch->src_sync]; - a->frame = 0; - a->element = 0; - a->pck_element = 0; - - if (unlikely(!ch->elements || !ch->frames)) { - printf("%s: bad DMA request\n", __FUNCTION__); - return; - } - - for (i = 0; i < 2; i ++) - switch (ch->mode[i]) { - case constant: - a->elem_delta[i] = 0; - a->frame_delta[i] = 0; - break; - case post_incremented: - a->elem_delta[i] = ch->data_type; - a->frame_delta[i] = 0; - break; - case single_index: - a->elem_delta[i] = ch->data_type + - ch->element_index[omap_3_1 ? 0 : i] - 1; - a->frame_delta[i] = 0; - break; - case double_index: - a->elem_delta[i] = ch->data_type + - ch->element_index[omap_3_1 ? 0 : i] - 1; - a->frame_delta[i] = ch->frame_index[omap_3_1 ? 0 : i] - - ch->element_index[omap_3_1 ? 0 : i]; - break; - default: - break; - } - - normal = !ch->transparent_copy && !ch->constant_fill && - /* FIFO is big-endian so either (ch->endian[n] == 1) OR - * (ch->endian_lock[n] == 1) mean no endianism conversion. */ - (ch->endian[0] | ch->endian_lock[0]) == - (ch->endian[1] | ch->endian_lock[1]); - for (i = 0; i < 2; i ++) { - /* TODO: for a->frame_delta[i] > 0 still use the fast path, just - * limit min_elems in omap_dma_transfer_setup to the nearest frame - * end. */ - if (!a->elem_delta[i] && normal && - (a->frames == 1 || !a->frame_delta[i])) - ch->dma->type[i] = soc_dma_access_const; - else if (a->elem_delta[i] == ch->data_type && normal && - (a->frames == 1 || !a->frame_delta[i])) - ch->dma->type[i] = soc_dma_access_linear; - else - ch->dma->type[i] = soc_dma_access_other; - - ch->dma->vaddr[i] = ch->addr[i]; - } - soc_dma_ch_update(ch->dma); -} - -static void omap_dma_activate_channel(struct omap_dma_s *s, - struct omap_dma_channel_s *ch) -{ - if (!ch->active) { - if (ch->set_update) { - /* It's not clear when the active set is supposed to be - * loaded from registers. We're already loading it when the - * channel is enabled, and for some guests this is not enough - * but that may be also because of a race condition (no - * delays in qemu) in the guest code, which we're just - * working around here. */ - omap_dma_channel_load(ch); - ch->set_update = 0; - } - - ch->active = 1; - soc_dma_set_request(ch->dma, 1); - if (ch->sync) - ch->status |= SYNC; - } -} - -static void omap_dma_deactivate_channel(struct omap_dma_s *s, - struct omap_dma_channel_s *ch) -{ - /* Update cpc */ - ch->cpc = ch->active_set.dest & 0xffff; - - if (ch->pending_request && !ch->waiting_end_prog && ch->enable) { - /* Don't deactivate the channel */ - ch->pending_request = 0; - return; - } - - /* Don't deactive the channel if it is synchronized and the DMA request is - active */ - if (ch->sync && ch->enable && (s->dma->drqbmp & (1ULL << ch->sync))) - return; - - if (ch->active) { - ch->active = 0; - ch->status &= ~SYNC; - soc_dma_set_request(ch->dma, 0); - } -} - -static void omap_dma_enable_channel(struct omap_dma_s *s, - struct omap_dma_channel_s *ch) -{ - if (!ch->enable) { - ch->enable = 1; - ch->waiting_end_prog = 0; - omap_dma_channel_load(ch); - /* TODO: theoretically if ch->sync && ch->prefetch && - * !s->dma->drqbmp[ch->sync], we should also activate and fetch - * from source and then stall until signalled. */ - if ((!ch->sync) || (s->dma->drqbmp & (1ULL << ch->sync))) { - omap_dma_activate_channel(s, ch); - } - } -} - -static void omap_dma_disable_channel(struct omap_dma_s *s, - struct omap_dma_channel_s *ch) -{ - if (ch->enable) { - ch->enable = 0; - /* Discard any pending request */ - ch->pending_request = 0; - omap_dma_deactivate_channel(s, ch); - } -} - -static void omap_dma_channel_end_prog(struct omap_dma_s *s, - struct omap_dma_channel_s *ch) -{ - if (ch->waiting_end_prog) { - ch->waiting_end_prog = 0; - if (!ch->sync || ch->pending_request) { - ch->pending_request = 0; - omap_dma_activate_channel(s, ch); - } - } -} - -static void omap_dma_interrupts_3_1_update(struct omap_dma_s *s) -{ - struct omap_dma_channel_s *ch = s->ch; - - /* First three interrupts are shared between two channels each. */ - if (ch[0].status | ch[6].status) - qemu_irq_raise(ch[0].irq); - if (ch[1].status | ch[7].status) - qemu_irq_raise(ch[1].irq); - if (ch[2].status | ch[8].status) - qemu_irq_raise(ch[2].irq); - if (ch[3].status) - qemu_irq_raise(ch[3].irq); - if (ch[4].status) - qemu_irq_raise(ch[4].irq); - if (ch[5].status) - qemu_irq_raise(ch[5].irq); -} - -static void omap_dma_interrupts_3_2_update(struct omap_dma_s *s) -{ - struct omap_dma_channel_s *ch = s->ch; - int i; - - for (i = s->chans; i; ch ++, i --) - if (ch->status) - qemu_irq_raise(ch->irq); -} - -static void omap_dma_enable_3_1_mapping(struct omap_dma_s *s) -{ - s->omap_3_1_mapping_disabled = 0; - s->chans = 9; - s->intr_update = omap_dma_interrupts_3_1_update; -} - -static void omap_dma_disable_3_1_mapping(struct omap_dma_s *s) -{ - s->omap_3_1_mapping_disabled = 1; - s->chans = 16; - s->intr_update = omap_dma_interrupts_3_2_update; -} - -static void omap_dma_process_request(struct omap_dma_s *s, int request) -{ - int channel; - int drop_event = 0; - struct omap_dma_channel_s *ch = s->ch; - - for (channel = 0; channel < s->chans; channel ++, ch ++) { - if (ch->enable && ch->sync == request) { - if (!ch->active) - omap_dma_activate_channel(s, ch); - else if (!ch->pending_request) - ch->pending_request = 1; - else { - /* Request collision */ - /* Second request received while processing other request */ - ch->status |= EVENT_DROP_INTR; - drop_event = 1; - } - } - } - - if (drop_event) - omap_dma_interrupts_update(s); -} - -static void omap_dma_transfer_generic(struct soc_dma_ch_s *dma) -{ - uint8_t value[4]; - struct omap_dma_channel_s *ch = dma->opaque; - struct omap_dma_reg_set_s *a = &ch->active_set; - int bytes = dma->bytes; -#ifdef MULTI_REQ - uint16_t status = ch->status; -#endif - - do { - /* Transfer a single element */ - /* FIXME: check the endianness */ - if (!ch->constant_fill) - cpu_physical_memory_read(a->src, value, ch->data_type); - else - *(uint32_t *) value = ch->color; - - if (!ch->transparent_copy || *(uint32_t *) value != ch->color) - cpu_physical_memory_write(a->dest, value, ch->data_type); - - a->src += a->elem_delta[0]; - a->dest += a->elem_delta[1]; - a->element ++; - -#ifndef MULTI_REQ - if (a->element == a->elements) { - /* End of Frame */ - a->element = 0; - a->src += a->frame_delta[0]; - a->dest += a->frame_delta[1]; - a->frame ++; - - /* If the channel is async, update cpc */ - if (!ch->sync) - ch->cpc = a->dest & 0xffff; - } - } while ((bytes -= ch->data_type)); -#else - /* If the channel is element synchronized, deactivate it */ - if (ch->sync && !ch->fs && !ch->bs) - omap_dma_deactivate_channel(s, ch); - - /* If it is the last frame, set the LAST_FRAME interrupt */ - if (a->element == 1 && a->frame == a->frames - 1) - if (ch->interrupts & LAST_FRAME_INTR) - ch->status |= LAST_FRAME_INTR; - - /* If the half of the frame was reached, set the HALF_FRAME - interrupt */ - if (a->element == (a->elements >> 1)) - if (ch->interrupts & HALF_FRAME_INTR) - ch->status |= HALF_FRAME_INTR; - - if (ch->fs && ch->bs) { - a->pck_element ++; - /* Check if a full packet has beed transferred. */ - if (a->pck_element == a->pck_elements) { - a->pck_element = 0; - - /* Set the END_PKT interrupt */ - if ((ch->interrupts & END_PKT_INTR) && !ch->src_sync) - ch->status |= END_PKT_INTR; - - /* If the channel is packet-synchronized, deactivate it */ - if (ch->sync) - omap_dma_deactivate_channel(s, ch); - } - } - - if (a->element == a->elements) { - /* End of Frame */ - a->element = 0; - a->src += a->frame_delta[0]; - a->dest += a->frame_delta[1]; - a->frame ++; - - /* If the channel is frame synchronized, deactivate it */ - if (ch->sync && ch->fs && !ch->bs) - omap_dma_deactivate_channel(s, ch); - - /* If the channel is async, update cpc */ - if (!ch->sync) - ch->cpc = a->dest & 0xffff; - - /* Set the END_FRAME interrupt */ - if (ch->interrupts & END_FRAME_INTR) - ch->status |= END_FRAME_INTR; - - if (a->frame == a->frames) { - /* End of Block */ - /* Disable the channel */ - - if (ch->omap_3_1_compatible_disable) { - omap_dma_disable_channel(s, ch); - if (ch->link_enabled) - omap_dma_enable_channel(s, - &s->ch[ch->link_next_ch]); - } else { - if (!ch->auto_init) - omap_dma_disable_channel(s, ch); - else if (ch->repeat || ch->end_prog) - omap_dma_channel_load(ch); - else { - ch->waiting_end_prog = 1; - omap_dma_deactivate_channel(s, ch); - } - } - - if (ch->interrupts & END_BLOCK_INTR) - ch->status |= END_BLOCK_INTR; - } - } - } while (status == ch->status && ch->active); - - omap_dma_interrupts_update(s); -#endif -} - -enum { - omap_dma_intr_element_sync, - omap_dma_intr_last_frame, - omap_dma_intr_half_frame, - omap_dma_intr_frame, - omap_dma_intr_frame_sync, - omap_dma_intr_packet, - omap_dma_intr_packet_sync, - omap_dma_intr_block, - __omap_dma_intr_last, -}; - -static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) -{ - struct omap_dma_port_if_s *src_p, *dest_p; - struct omap_dma_reg_set_s *a; - struct omap_dma_channel_s *ch = dma->opaque; - struct omap_dma_s *s = dma->dma->opaque; - int frames, min_elems, elements[__omap_dma_intr_last]; - - a = &ch->active_set; - - src_p = &s->mpu->port[ch->port[0]]; - dest_p = &s->mpu->port[ch->port[1]]; - if ((!ch->constant_fill && !src_p->addr_valid(s->mpu, a->src)) || - (!dest_p->addr_valid(s->mpu, a->dest))) { -#if 0 - /* Bus time-out */ - if (ch->interrupts & TIMEOUT_INTR) - ch->status |= TIMEOUT_INTR; - omap_dma_deactivate_channel(s, ch); - continue; -#endif - printf("%s: Bus time-out in DMA%i operation\n", - __FUNCTION__, dma->num); - } - - min_elems = INT_MAX; - - /* Check all the conditions that terminate the transfer starting - * with those that can occur the soonest. */ -#define INTR_CHECK(cond, id, nelements) \ - if (cond) { \ - elements[id] = nelements; \ - if (elements[id] < min_elems) \ - min_elems = elements[id]; \ - } else \ - elements[id] = INT_MAX; - - /* Elements */ - INTR_CHECK( - ch->sync && !ch->fs && !ch->bs, - omap_dma_intr_element_sync, - 1) - - /* Frames */ - /* TODO: for transfers where entire frames can be read and written - * using memcpy() but a->frame_delta is non-zero, try to still do - * transfers using soc_dma but limit min_elems to a->elements - ... - * See also the TODO in omap_dma_channel_load. */ - INTR_CHECK( - (ch->interrupts & LAST_FRAME_INTR) && - ((a->frame < a->frames - 1) || !a->element), - omap_dma_intr_last_frame, - (a->frames - a->frame - 2) * a->elements + - (a->elements - a->element + 1)) - INTR_CHECK( - ch->interrupts & HALF_FRAME_INTR, - omap_dma_intr_half_frame, - (a->elements >> 1) + - (a->element >= (a->elements >> 1) ? a->elements : 0) - - a->element) - INTR_CHECK( - ch->sync && ch->fs && (ch->interrupts & END_FRAME_INTR), - omap_dma_intr_frame, - a->elements - a->element) - INTR_CHECK( - ch->sync && ch->fs && !ch->bs, - omap_dma_intr_frame_sync, - a->elements - a->element) - - /* Packets */ - INTR_CHECK( - ch->fs && ch->bs && - (ch->interrupts & END_PKT_INTR) && !ch->src_sync, - omap_dma_intr_packet, - a->pck_elements - a->pck_element) - INTR_CHECK( - ch->fs && ch->bs && ch->sync, - omap_dma_intr_packet_sync, - a->pck_elements - a->pck_element) - - /* Blocks */ - INTR_CHECK( - 1, - omap_dma_intr_block, - (a->frames - a->frame - 1) * a->elements + - (a->elements - a->element)) - - dma->bytes = min_elems * ch->data_type; - - /* Set appropriate interrupts and/or deactivate channels */ - -#ifdef MULTI_REQ - /* TODO: should all of this only be done if dma->update, and otherwise - * inside omap_dma_transfer_generic below - check what's faster. */ - if (dma->update) { -#endif - - /* If the channel is element synchronized, deactivate it */ - if (min_elems == elements[omap_dma_intr_element_sync]) - omap_dma_deactivate_channel(s, ch); - - /* If it is the last frame, set the LAST_FRAME interrupt */ - if (min_elems == elements[omap_dma_intr_last_frame]) - ch->status |= LAST_FRAME_INTR; - - /* If exactly half of the frame was reached, set the HALF_FRAME - interrupt */ - if (min_elems == elements[omap_dma_intr_half_frame]) - ch->status |= HALF_FRAME_INTR; - - /* If a full packet has been transferred, set the END_PKT interrupt */ - if (min_elems == elements[omap_dma_intr_packet]) - ch->status |= END_PKT_INTR; - - /* If the channel is packet-synchronized, deactivate it */ - if (min_elems == elements[omap_dma_intr_packet_sync]) - omap_dma_deactivate_channel(s, ch); - - /* If the channel is frame synchronized, deactivate it */ - if (min_elems == elements[omap_dma_intr_frame_sync]) - omap_dma_deactivate_channel(s, ch); - - /* Set the END_FRAME interrupt */ - if (min_elems == elements[omap_dma_intr_frame]) - ch->status |= END_FRAME_INTR; - - if (min_elems == elements[omap_dma_intr_block]) { - /* End of Block */ - /* Disable the channel */ - - if (ch->omap_3_1_compatible_disable) { - omap_dma_disable_channel(s, ch); - if (ch->link_enabled) - omap_dma_enable_channel(s, &s->ch[ch->link_next_ch]); - } else { - if (!ch->auto_init) - omap_dma_disable_channel(s, ch); - else if (ch->repeat || ch->end_prog) - omap_dma_channel_load(ch); - else { - ch->waiting_end_prog = 1; - omap_dma_deactivate_channel(s, ch); - } - } - - if (ch->interrupts & END_BLOCK_INTR) - ch->status |= END_BLOCK_INTR; - } - - /* Update packet number */ - if (ch->fs && ch->bs) { - a->pck_element += min_elems; - a->pck_element %= a->pck_elements; - } - - /* TODO: check if we really need to update anything here or perhaps we - * can skip part of this. */ -#ifndef MULTI_REQ - if (dma->update) { -#endif - a->element += min_elems; - - frames = a->element / a->elements; - a->element = a->element % a->elements; - a->frame += frames; - a->src += min_elems * a->elem_delta[0] + frames * a->frame_delta[0]; - a->dest += min_elems * a->elem_delta[1] + frames * a->frame_delta[1]; - - /* If the channel is async, update cpc */ - if (!ch->sync && frames) - ch->cpc = a->dest & 0xffff; - - /* TODO: if the destination port is IMIF or EMIFF, set the dirty - * bits on it. */ -#ifndef MULTI_REQ - } -#else - } -#endif - - omap_dma_interrupts_update(s); -} - -void omap_dma_reset(struct soc_dma_s *dma) -{ - int i; - struct omap_dma_s *s = dma->opaque; - - soc_dma_reset(s->dma); - if (s->model < omap_dma_4) - s->gcr = 0x0004; - else - s->gcr = 0x00010010; - s->ocp = 0x00000000; - memset(&s->irqstat, 0, sizeof(s->irqstat)); - memset(&s->irqen, 0, sizeof(s->irqen)); - s->lcd_ch.src = emiff; - s->lcd_ch.condition = 0; - s->lcd_ch.interrupts = 0; - s->lcd_ch.dual = 0; - if (s->model < omap_dma_4) - omap_dma_enable_3_1_mapping(s); - for (i = 0; i < s->chans; i ++) { - s->ch[i].suspend = 0; - s->ch[i].prefetch = 0; - s->ch[i].buf_disable = 0; - s->ch[i].src_sync = 0; - memset(&s->ch[i].burst, 0, sizeof(s->ch[i].burst)); - memset(&s->ch[i].port, 0, sizeof(s->ch[i].port)); - memset(&s->ch[i].mode, 0, sizeof(s->ch[i].mode)); - memset(&s->ch[i].frame_index, 0, sizeof(s->ch[i].frame_index)); - memset(&s->ch[i].element_index, 0, sizeof(s->ch[i].element_index)); - memset(&s->ch[i].endian, 0, sizeof(s->ch[i].endian)); - memset(&s->ch[i].endian_lock, 0, sizeof(s->ch[i].endian_lock)); - memset(&s->ch[i].translate, 0, sizeof(s->ch[i].translate)); - s->ch[i].write_mode = 0; - s->ch[i].data_type = 0; - s->ch[i].transparent_copy = 0; - s->ch[i].constant_fill = 0; - s->ch[i].color = 0x00000000; - s->ch[i].end_prog = 0; - s->ch[i].repeat = 0; - s->ch[i].auto_init = 0; - s->ch[i].link_enabled = 0; - if (s->model < omap_dma_4) - s->ch[i].interrupts = 0x0003; - else - s->ch[i].interrupts = 0x0000; - s->ch[i].status = 0; - s->ch[i].cstatus = 0; - s->ch[i].active = 0; - s->ch[i].enable = 0; - s->ch[i].sync = 0; - s->ch[i].pending_request = 0; - s->ch[i].waiting_end_prog = 0; - s->ch[i].cpc = 0x0000; - s->ch[i].fs = 0; - s->ch[i].bs = 0; - s->ch[i].omap_3_1_compatible_disable = 0; - memset(&s->ch[i].active_set, 0, sizeof(s->ch[i].active_set)); - s->ch[i].priority = 0; - s->ch[i].interleave_disabled = 0; - s->ch[i].type = 0; - } -} - -static int omap_dma_ch_reg_read(struct omap_dma_s *s, - struct omap_dma_channel_s *ch, int reg, uint16_t *value) -{ - switch (reg) { - case 0x00: /* SYS_DMA_CSDP_CH0 */ - *value = (ch->burst[1] << 14) | - (ch->pack[1] << 13) | - (ch->port[1] << 9) | - (ch->burst[0] << 7) | - (ch->pack[0] << 6) | - (ch->port[0] << 2) | - (ch->data_type >> 1); - break; - - case 0x02: /* SYS_DMA_CCR_CH0 */ - if (s->model <= omap_dma_3_1) - *value = 0 << 10; /* FIFO_FLUSH reads as 0 */ - else - *value = ch->omap_3_1_compatible_disable << 10; - *value |= (ch->mode[1] << 14) | - (ch->mode[0] << 12) | - (ch->end_prog << 11) | - (ch->repeat << 9) | - (ch->auto_init << 8) | - (ch->enable << 7) | - (ch->priority << 6) | - (ch->fs << 5) | ch->sync; - break; - - case 0x04: /* SYS_DMA_CICR_CH0 */ - *value = ch->interrupts; - break; - - case 0x06: /* SYS_DMA_CSR_CH0 */ - *value = ch->status; - ch->status &= SYNC; - if (!ch->omap_3_1_compatible_disable && ch->sibling) { - *value |= (ch->sibling->status & 0x3f) << 6; - ch->sibling->status &= SYNC; - } - qemu_irq_lower(ch->irq); - break; - - case 0x08: /* SYS_DMA_CSSA_L_CH0 */ - *value = ch->addr[0] & 0x0000ffff; - break; - - case 0x0a: /* SYS_DMA_CSSA_U_CH0 */ - *value = ch->addr[0] >> 16; - break; - - case 0x0c: /* SYS_DMA_CDSA_L_CH0 */ - *value = ch->addr[1] & 0x0000ffff; - break; - - case 0x0e: /* SYS_DMA_CDSA_U_CH0 */ - *value = ch->addr[1] >> 16; - break; - - case 0x10: /* SYS_DMA_CEN_CH0 */ - *value = ch->elements; - break; - - case 0x12: /* SYS_DMA_CFN_CH0 */ - *value = ch->frames; - break; - - case 0x14: /* SYS_DMA_CFI_CH0 */ - *value = ch->frame_index[0]; - break; - - case 0x16: /* SYS_DMA_CEI_CH0 */ - *value = ch->element_index[0]; - break; - - case 0x18: /* SYS_DMA_CPC_CH0 or DMA_CSAC */ - if (ch->omap_3_1_compatible_disable) - *value = ch->active_set.src & 0xffff; /* CSAC */ - else - *value = ch->cpc; - break; - - case 0x1a: /* DMA_CDAC */ - *value = ch->active_set.dest & 0xffff; /* CDAC */ - break; - - case 0x1c: /* DMA_CDEI */ - *value = ch->element_index[1]; - break; - - case 0x1e: /* DMA_CDFI */ - *value = ch->frame_index[1]; - break; - - case 0x20: /* DMA_COLOR_L */ - *value = ch->color & 0xffff; - break; - - case 0x22: /* DMA_COLOR_U */ - *value = ch->color >> 16; - break; - - case 0x24: /* DMA_CCR2 */ - *value = (ch->bs << 2) | - (ch->transparent_copy << 1) | - ch->constant_fill; - break; - - case 0x28: /* DMA_CLNK_CTRL */ - *value = (ch->link_enabled << 15) | - (ch->link_next_ch & 0xf); - break; - - case 0x2a: /* DMA_LCH_CTRL */ - *value = (ch->interleave_disabled << 15) | - ch->type; - break; - - default: - return 1; - } - return 0; -} - -static int omap_dma_ch_reg_write(struct omap_dma_s *s, - struct omap_dma_channel_s *ch, int reg, uint16_t value) -{ - switch (reg) { - case 0x00: /* SYS_DMA_CSDP_CH0 */ - ch->burst[1] = (value & 0xc000) >> 14; - ch->pack[1] = (value & 0x2000) >> 13; - ch->port[1] = (enum omap_dma_port) ((value & 0x1e00) >> 9); - ch->burst[0] = (value & 0x0180) >> 7; - ch->pack[0] = (value & 0x0040) >> 6; - ch->port[0] = (enum omap_dma_port) ((value & 0x003c) >> 2); - ch->data_type = 1 << (value & 3); - if (ch->port[0] >= __omap_dma_port_last) - printf("%s: invalid DMA port %i\n", __FUNCTION__, - ch->port[0]); - if (ch->port[1] >= __omap_dma_port_last) - printf("%s: invalid DMA port %i\n", __FUNCTION__, - ch->port[1]); - if ((value & 3) == 3) - printf("%s: bad data_type for DMA channel\n", __FUNCTION__); - break; - - case 0x02: /* SYS_DMA_CCR_CH0 */ - ch->mode[1] = (omap_dma_addressing_t) ((value & 0xc000) >> 14); - ch->mode[0] = (omap_dma_addressing_t) ((value & 0x3000) >> 12); - ch->end_prog = (value & 0x0800) >> 11; - if (s->model >= omap_dma_3_2) - ch->omap_3_1_compatible_disable = (value >> 10) & 0x1; - ch->repeat = (value & 0x0200) >> 9; - ch->auto_init = (value & 0x0100) >> 8; - ch->priority = (value & 0x0040) >> 6; - ch->fs = (value & 0x0020) >> 5; - ch->sync = value & 0x001f; - - if (value & 0x0080) - omap_dma_enable_channel(s, ch); - else - omap_dma_disable_channel(s, ch); - - if (ch->end_prog) - omap_dma_channel_end_prog(s, ch); - - break; - - case 0x04: /* SYS_DMA_CICR_CH0 */ - ch->interrupts = value & 0x3f; - break; - - case 0x06: /* SYS_DMA_CSR_CH0 */ - OMAP_RO_REG((hwaddr) reg); - break; - - case 0x08: /* SYS_DMA_CSSA_L_CH0 */ - ch->addr[0] &= 0xffff0000; - ch->addr[0] |= value; - break; - - case 0x0a: /* SYS_DMA_CSSA_U_CH0 */ - ch->addr[0] &= 0x0000ffff; - ch->addr[0] |= (uint32_t) value << 16; - break; - - case 0x0c: /* SYS_DMA_CDSA_L_CH0 */ - ch->addr[1] &= 0xffff0000; - ch->addr[1] |= value; - break; - - case 0x0e: /* SYS_DMA_CDSA_U_CH0 */ - ch->addr[1] &= 0x0000ffff; - ch->addr[1] |= (uint32_t) value << 16; - break; - - case 0x10: /* SYS_DMA_CEN_CH0 */ - ch->elements = value; - break; - - case 0x12: /* SYS_DMA_CFN_CH0 */ - ch->frames = value; - break; - - case 0x14: /* SYS_DMA_CFI_CH0 */ - ch->frame_index[0] = (int16_t) value; - break; - - case 0x16: /* SYS_DMA_CEI_CH0 */ - ch->element_index[0] = (int16_t) value; - break; - - case 0x18: /* SYS_DMA_CPC_CH0 or DMA_CSAC */ - OMAP_RO_REG((hwaddr) reg); - break; - - case 0x1c: /* DMA_CDEI */ - ch->element_index[1] = (int16_t) value; - break; - - case 0x1e: /* DMA_CDFI */ - ch->frame_index[1] = (int16_t) value; - break; - - case 0x20: /* DMA_COLOR_L */ - ch->color &= 0xffff0000; - ch->color |= value; - break; - - case 0x22: /* DMA_COLOR_U */ - ch->color &= 0xffff; - ch->color |= (uint32_t)value << 16; - break; - - case 0x24: /* DMA_CCR2 */ - ch->bs = (value >> 2) & 0x1; - ch->transparent_copy = (value >> 1) & 0x1; - ch->constant_fill = value & 0x1; - break; - - case 0x28: /* DMA_CLNK_CTRL */ - ch->link_enabled = (value >> 15) & 0x1; - if (value & (1 << 14)) { /* Stop_Lnk */ - ch->link_enabled = 0; - omap_dma_disable_channel(s, ch); - } - ch->link_next_ch = value & 0x1f; - break; - - case 0x2a: /* DMA_LCH_CTRL */ - ch->interleave_disabled = (value >> 15) & 0x1; - ch->type = value & 0xf; - break; - - default: - return 1; - } - return 0; -} - -static int omap_dma_3_2_lcd_write(struct omap_dma_lcd_channel_s *s, int offset, - uint16_t value) -{ - switch (offset) { - case 0xbc0: /* DMA_LCD_CSDP */ - s->brust_f2 = (value >> 14) & 0x3; - s->pack_f2 = (value >> 13) & 0x1; - s->data_type_f2 = (1 << ((value >> 11) & 0x3)); - s->brust_f1 = (value >> 7) & 0x3; - s->pack_f1 = (value >> 6) & 0x1; - s->data_type_f1 = (1 << ((value >> 0) & 0x3)); - break; - - case 0xbc2: /* DMA_LCD_CCR */ - s->mode_f2 = (value >> 14) & 0x3; - s->mode_f1 = (value >> 12) & 0x3; - s->end_prog = (value >> 11) & 0x1; - s->omap_3_1_compatible_disable = (value >> 10) & 0x1; - s->repeat = (value >> 9) & 0x1; - s->auto_init = (value >> 8) & 0x1; - s->running = (value >> 7) & 0x1; - s->priority = (value >> 6) & 0x1; - s->bs = (value >> 4) & 0x1; - break; - - case 0xbc4: /* DMA_LCD_CTRL */ - s->dst = (value >> 8) & 0x1; - s->src = ((value >> 6) & 0x3) << 1; - s->condition = 0; - /* Assume no bus errors and thus no BUS_ERROR irq bits. */ - s->interrupts = (value >> 1) & 1; - s->dual = value & 1; - break; - - case 0xbc8: /* TOP_B1_L */ - s->src_f1_top &= 0xffff0000; - s->src_f1_top |= 0x0000ffff & value; - break; - - case 0xbca: /* TOP_B1_U */ - s->src_f1_top &= 0x0000ffff; - s->src_f1_top |= (uint32_t)value << 16; - break; - - case 0xbcc: /* BOT_B1_L */ - s->src_f1_bottom &= 0xffff0000; - s->src_f1_bottom |= 0x0000ffff & value; - break; - - case 0xbce: /* BOT_B1_U */ - s->src_f1_bottom &= 0x0000ffff; - s->src_f1_bottom |= (uint32_t) value << 16; - break; - - case 0xbd0: /* TOP_B2_L */ - s->src_f2_top &= 0xffff0000; - s->src_f2_top |= 0x0000ffff & value; - break; - - case 0xbd2: /* TOP_B2_U */ - s->src_f2_top &= 0x0000ffff; - s->src_f2_top |= (uint32_t) value << 16; - break; - - case 0xbd4: /* BOT_B2_L */ - s->src_f2_bottom &= 0xffff0000; - s->src_f2_bottom |= 0x0000ffff & value; - break; - - case 0xbd6: /* BOT_B2_U */ - s->src_f2_bottom &= 0x0000ffff; - s->src_f2_bottom |= (uint32_t) value << 16; - break; - - case 0xbd8: /* DMA_LCD_SRC_EI_B1 */ - s->element_index_f1 = value; - break; - - case 0xbda: /* DMA_LCD_SRC_FI_B1_L */ - s->frame_index_f1 &= 0xffff0000; - s->frame_index_f1 |= 0x0000ffff & value; - break; - - case 0xbf4: /* DMA_LCD_SRC_FI_B1_U */ - s->frame_index_f1 &= 0x0000ffff; - s->frame_index_f1 |= (uint32_t) value << 16; - break; - - case 0xbdc: /* DMA_LCD_SRC_EI_B2 */ - s->element_index_f2 = value; - break; - - case 0xbde: /* DMA_LCD_SRC_FI_B2_L */ - s->frame_index_f2 &= 0xffff0000; - s->frame_index_f2 |= 0x0000ffff & value; - break; - - case 0xbf6: /* DMA_LCD_SRC_FI_B2_U */ - s->frame_index_f2 &= 0x0000ffff; - s->frame_index_f2 |= (uint32_t) value << 16; - break; - - case 0xbe0: /* DMA_LCD_SRC_EN_B1 */ - s->elements_f1 = value; - break; - - case 0xbe4: /* DMA_LCD_SRC_FN_B1 */ - s->frames_f1 = value; - break; - - case 0xbe2: /* DMA_LCD_SRC_EN_B2 */ - s->elements_f2 = value; - break; - - case 0xbe6: /* DMA_LCD_SRC_FN_B2 */ - s->frames_f2 = value; - break; - - case 0xbea: /* DMA_LCD_LCH_CTRL */ - s->lch_type = value & 0xf; - break; - - default: - return 1; - } - return 0; -} - -static int omap_dma_3_2_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, - uint16_t *ret) -{ - switch (offset) { - case 0xbc0: /* DMA_LCD_CSDP */ - *ret = (s->brust_f2 << 14) | - (s->pack_f2 << 13) | - ((s->data_type_f2 >> 1) << 11) | - (s->brust_f1 << 7) | - (s->pack_f1 << 6) | - ((s->data_type_f1 >> 1) << 0); - break; - - case 0xbc2: /* DMA_LCD_CCR */ - *ret = (s->mode_f2 << 14) | - (s->mode_f1 << 12) | - (s->end_prog << 11) | - (s->omap_3_1_compatible_disable << 10) | - (s->repeat << 9) | - (s->auto_init << 8) | - (s->running << 7) | - (s->priority << 6) | - (s->bs << 4); - break; - - case 0xbc4: /* DMA_LCD_CTRL */ - qemu_irq_lower(s->irq); - *ret = (s->dst << 8) | - ((s->src & 0x6) << 5) | - (s->condition << 3) | - (s->interrupts << 1) | - s->dual; - break; - - case 0xbc8: /* TOP_B1_L */ - *ret = s->src_f1_top & 0xffff; - break; - - case 0xbca: /* TOP_B1_U */ - *ret = s->src_f1_top >> 16; - break; - - case 0xbcc: /* BOT_B1_L */ - *ret = s->src_f1_bottom & 0xffff; - break; - - case 0xbce: /* BOT_B1_U */ - *ret = s->src_f1_bottom >> 16; - break; - - case 0xbd0: /* TOP_B2_L */ - *ret = s->src_f2_top & 0xffff; - break; - - case 0xbd2: /* TOP_B2_U */ - *ret = s->src_f2_top >> 16; - break; - - case 0xbd4: /* BOT_B2_L */ - *ret = s->src_f2_bottom & 0xffff; - break; - - case 0xbd6: /* BOT_B2_U */ - *ret = s->src_f2_bottom >> 16; - break; - - case 0xbd8: /* DMA_LCD_SRC_EI_B1 */ - *ret = s->element_index_f1; - break; - - case 0xbda: /* DMA_LCD_SRC_FI_B1_L */ - *ret = s->frame_index_f1 & 0xffff; - break; - - case 0xbf4: /* DMA_LCD_SRC_FI_B1_U */ - *ret = s->frame_index_f1 >> 16; - break; - - case 0xbdc: /* DMA_LCD_SRC_EI_B2 */ - *ret = s->element_index_f2; - break; - - case 0xbde: /* DMA_LCD_SRC_FI_B2_L */ - *ret = s->frame_index_f2 & 0xffff; - break; - - case 0xbf6: /* DMA_LCD_SRC_FI_B2_U */ - *ret = s->frame_index_f2 >> 16; - break; - - case 0xbe0: /* DMA_LCD_SRC_EN_B1 */ - *ret = s->elements_f1; - break; - - case 0xbe4: /* DMA_LCD_SRC_FN_B1 */ - *ret = s->frames_f1; - break; - - case 0xbe2: /* DMA_LCD_SRC_EN_B2 */ - *ret = s->elements_f2; - break; - - case 0xbe6: /* DMA_LCD_SRC_FN_B2 */ - *ret = s->frames_f2; - break; - - case 0xbea: /* DMA_LCD_LCH_CTRL */ - *ret = s->lch_type; - break; - - default: - return 1; - } - return 0; -} - -static int omap_dma_3_1_lcd_write(struct omap_dma_lcd_channel_s *s, int offset, - uint16_t value) -{ - switch (offset) { - case 0x300: /* SYS_DMA_LCD_CTRL */ - s->src = (value & 0x40) ? imif : emiff; - s->condition = 0; - /* Assume no bus errors and thus no BUS_ERROR irq bits. */ - s->interrupts = (value >> 1) & 1; - s->dual = value & 1; - break; - - case 0x302: /* SYS_DMA_LCD_TOP_F1_L */ - s->src_f1_top &= 0xffff0000; - s->src_f1_top |= 0x0000ffff & value; - break; - - case 0x304: /* SYS_DMA_LCD_TOP_F1_U */ - s->src_f1_top &= 0x0000ffff; - s->src_f1_top |= (uint32_t)value << 16; - break; - - case 0x306: /* SYS_DMA_LCD_BOT_F1_L */ - s->src_f1_bottom &= 0xffff0000; - s->src_f1_bottom |= 0x0000ffff & value; - break; - - case 0x308: /* SYS_DMA_LCD_BOT_F1_U */ - s->src_f1_bottom &= 0x0000ffff; - s->src_f1_bottom |= (uint32_t)value << 16; - break; - - case 0x30a: /* SYS_DMA_LCD_TOP_F2_L */ - s->src_f2_top &= 0xffff0000; - s->src_f2_top |= 0x0000ffff & value; - break; - - case 0x30c: /* SYS_DMA_LCD_TOP_F2_U */ - s->src_f2_top &= 0x0000ffff; - s->src_f2_top |= (uint32_t)value << 16; - break; - - case 0x30e: /* SYS_DMA_LCD_BOT_F2_L */ - s->src_f2_bottom &= 0xffff0000; - s->src_f2_bottom |= 0x0000ffff & value; - break; - - case 0x310: /* SYS_DMA_LCD_BOT_F2_U */ - s->src_f2_bottom &= 0x0000ffff; - s->src_f2_bottom |= (uint32_t)value << 16; - break; - - default: - return 1; - } - return 0; -} - -static int omap_dma_3_1_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, - uint16_t *ret) -{ - int i; - - switch (offset) { - case 0x300: /* SYS_DMA_LCD_CTRL */ - i = s->condition; - s->condition = 0; - qemu_irq_lower(s->irq); - *ret = ((s->src == imif) << 6) | (i << 3) | - (s->interrupts << 1) | s->dual; - break; - - case 0x302: /* SYS_DMA_LCD_TOP_F1_L */ - *ret = s->src_f1_top & 0xffff; - break; - - case 0x304: /* SYS_DMA_LCD_TOP_F1_U */ - *ret = s->src_f1_top >> 16; - break; - - case 0x306: /* SYS_DMA_LCD_BOT_F1_L */ - *ret = s->src_f1_bottom & 0xffff; - break; - - case 0x308: /* SYS_DMA_LCD_BOT_F1_U */ - *ret = s->src_f1_bottom >> 16; - break; - - case 0x30a: /* SYS_DMA_LCD_TOP_F2_L */ - *ret = s->src_f2_top & 0xffff; - break; - - case 0x30c: /* SYS_DMA_LCD_TOP_F2_U */ - *ret = s->src_f2_top >> 16; - break; - - case 0x30e: /* SYS_DMA_LCD_BOT_F2_L */ - *ret = s->src_f2_bottom & 0xffff; - break; - - case 0x310: /* SYS_DMA_LCD_BOT_F2_U */ - *ret = s->src_f2_bottom >> 16; - break; - - default: - return 1; - } - return 0; -} - -static int omap_dma_sys_write(struct omap_dma_s *s, int offset, uint16_t value) -{ - switch (offset) { - case 0x400: /* SYS_DMA_GCR */ - s->gcr = value; - break; - - case 0x404: /* DMA_GSCR */ - if (value & 0x8) - omap_dma_disable_3_1_mapping(s); - else - omap_dma_enable_3_1_mapping(s); - break; - - case 0x408: /* DMA_GRST */ - if (value & 0x1) - omap_dma_reset(s->dma); - break; - - default: - return 1; - } - return 0; -} - -static int omap_dma_sys_read(struct omap_dma_s *s, int offset, - uint16_t *ret) -{ - switch (offset) { - case 0x400: /* SYS_DMA_GCR */ - *ret = s->gcr; - break; - - case 0x404: /* DMA_GSCR */ - *ret = s->omap_3_1_mapping_disabled << 3; - break; - - case 0x408: /* DMA_GRST */ - *ret = 0; - break; - - case 0x442: /* DMA_HW_ID */ - case 0x444: /* DMA_PCh2_ID */ - case 0x446: /* DMA_PCh0_ID */ - case 0x448: /* DMA_PCh1_ID */ - case 0x44a: /* DMA_PChG_ID */ - case 0x44c: /* DMA_PChD_ID */ - *ret = 1; - break; - - case 0x44e: /* DMA_CAPS_0_U */ - *ret = (s->caps[0] >> 16) & 0xffff; - break; - case 0x450: /* DMA_CAPS_0_L */ - *ret = (s->caps[0] >> 0) & 0xffff; - break; - - case 0x452: /* DMA_CAPS_1_U */ - *ret = (s->caps[1] >> 16) & 0xffff; - break; - case 0x454: /* DMA_CAPS_1_L */ - *ret = (s->caps[1] >> 0) & 0xffff; - break; - - case 0x456: /* DMA_CAPS_2 */ - *ret = s->caps[2]; - break; - - case 0x458: /* DMA_CAPS_3 */ - *ret = s->caps[3]; - break; - - case 0x45a: /* DMA_CAPS_4 */ - *ret = s->caps[4]; - break; - - case 0x460: /* DMA_PCh2_SR */ - case 0x480: /* DMA_PCh0_SR */ - case 0x482: /* DMA_PCh1_SR */ - case 0x4c0: /* DMA_PChD_SR_0 */ - printf("%s: Physical Channel Status Registers not implemented.\n", - __FUNCTION__); - *ret = 0xff; - break; - - default: - return 1; - } - return 0; -} - -static uint64_t omap_dma_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_dma_s *s = (struct omap_dma_s *) opaque; - int reg, ch; - uint16_t ret; - - if (size != 2) { - return omap_badwidth_read16(opaque, addr); - } - - switch (addr) { - case 0x300 ... 0x3fe: - if (s->model <= omap_dma_3_1 || !s->omap_3_1_mapping_disabled) { - if (omap_dma_3_1_lcd_read(&s->lcd_ch, addr, &ret)) - break; - return ret; - } - /* Fall through. */ - case 0x000 ... 0x2fe: - reg = addr & 0x3f; - ch = (addr >> 6) & 0x0f; - if (omap_dma_ch_reg_read(s, &s->ch[ch], reg, &ret)) - break; - return ret; - - case 0x404 ... 0x4fe: - if (s->model <= omap_dma_3_1) - break; - /* Fall through. */ - case 0x400: - if (omap_dma_sys_read(s, addr, &ret)) - break; - return ret; - - case 0xb00 ... 0xbfe: - if (s->model == omap_dma_3_2 && s->omap_3_1_mapping_disabled) { - if (omap_dma_3_2_lcd_read(&s->lcd_ch, addr, &ret)) - break; - return ret; - } - break; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_dma_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_dma_s *s = (struct omap_dma_s *) opaque; - int reg, ch; - - if (size != 2) { - omap_badwidth_write16(opaque, addr, value); - return; - } - - switch (addr) { - case 0x300 ... 0x3fe: - if (s->model <= omap_dma_3_1 || !s->omap_3_1_mapping_disabled) { - if (omap_dma_3_1_lcd_write(&s->lcd_ch, addr, value)) - break; - return; - } - /* Fall through. */ - case 0x000 ... 0x2fe: - reg = addr & 0x3f; - ch = (addr >> 6) & 0x0f; - if (omap_dma_ch_reg_write(s, &s->ch[ch], reg, value)) - break; - return; - - case 0x404 ... 0x4fe: - if (s->model <= omap_dma_3_1) - break; - case 0x400: - /* Fall through. */ - if (omap_dma_sys_write(s, addr, value)) - break; - return; - - case 0xb00 ... 0xbfe: - if (s->model == omap_dma_3_2 && s->omap_3_1_mapping_disabled) { - if (omap_dma_3_2_lcd_write(&s->lcd_ch, addr, value)) - break; - return; - } - break; - } - - OMAP_BAD_REG(addr); -} - -static const MemoryRegionOps omap_dma_ops = { - .read = omap_dma_read, - .write = omap_dma_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_dma_request(void *opaque, int drq, int req) -{ - struct omap_dma_s *s = (struct omap_dma_s *) opaque; - /* The request pins are level triggered in QEMU. */ - if (req) { - if (~s->dma->drqbmp & (1ULL << drq)) { - s->dma->drqbmp |= 1ULL << drq; - omap_dma_process_request(s, drq); - } - } else - s->dma->drqbmp &= ~(1ULL << drq); -} - -/* XXX: this won't be needed once soc_dma knows about clocks. */ -static void omap_dma_clk_update(void *opaque, int line, int on) -{ - struct omap_dma_s *s = (struct omap_dma_s *) opaque; - int i; - - s->dma->freq = omap_clk_getrate(s->clk); - - for (i = 0; i < s->chans; i ++) - if (s->ch[i].active) - soc_dma_set_request(s->ch[i].dma, on); -} - -static void omap_dma_setcaps(struct omap_dma_s *s) -{ - switch (s->model) { - default: - case omap_dma_3_1: - break; - case omap_dma_3_2: - case omap_dma_4: - /* XXX Only available for sDMA */ - s->caps[0] = - (1 << 19) | /* Constant Fill Capability */ - (1 << 18); /* Transparent BLT Capability */ - s->caps[1] = - (1 << 1); /* 1-bit palettized capability (DMA 3.2 only) */ - s->caps[2] = - (1 << 8) | /* SEPARATE_SRC_AND_DST_INDEX_CPBLTY */ - (1 << 7) | /* DST_DOUBLE_INDEX_ADRS_CPBLTY */ - (1 << 6) | /* DST_SINGLE_INDEX_ADRS_CPBLTY */ - (1 << 5) | /* DST_POST_INCRMNT_ADRS_CPBLTY */ - (1 << 4) | /* DST_CONST_ADRS_CPBLTY */ - (1 << 3) | /* SRC_DOUBLE_INDEX_ADRS_CPBLTY */ - (1 << 2) | /* SRC_SINGLE_INDEX_ADRS_CPBLTY */ - (1 << 1) | /* SRC_POST_INCRMNT_ADRS_CPBLTY */ - (1 << 0); /* SRC_CONST_ADRS_CPBLTY */ - s->caps[3] = - (1 << 6) | /* BLOCK_SYNCHR_CPBLTY (DMA 4 only) */ - (1 << 7) | /* PKT_SYNCHR_CPBLTY (DMA 4 only) */ - (1 << 5) | /* CHANNEL_CHAINING_CPBLTY */ - (1 << 4) | /* LCh_INTERLEAVE_CPBLTY */ - (1 << 3) | /* AUTOINIT_REPEAT_CPBLTY (DMA 3.2 only) */ - (1 << 2) | /* AUTOINIT_ENDPROG_CPBLTY (DMA 3.2 only) */ - (1 << 1) | /* FRAME_SYNCHR_CPBLTY */ - (1 << 0); /* ELMNT_SYNCHR_CPBLTY */ - s->caps[4] = - (1 << 7) | /* PKT_INTERRUPT_CPBLTY (DMA 4 only) */ - (1 << 6) | /* SYNC_STATUS_CPBLTY */ - (1 << 5) | /* BLOCK_INTERRUPT_CPBLTY */ - (1 << 4) | /* LAST_FRAME_INTERRUPT_CPBLTY */ - (1 << 3) | /* FRAME_INTERRUPT_CPBLTY */ - (1 << 2) | /* HALF_FRAME_INTERRUPT_CPBLTY */ - (1 << 1) | /* EVENT_DROP_INTERRUPT_CPBLTY */ - (1 << 0); /* TIMEOUT_INTERRUPT_CPBLTY (DMA 3.2 only) */ - break; - } -} - -struct soc_dma_s *omap_dma_init(hwaddr base, qemu_irq *irqs, - MemoryRegion *sysmem, - qemu_irq lcd_irq, struct omap_mpu_state_s *mpu, omap_clk clk, - enum omap_dma_model model) -{ - int num_irqs, memsize, i; - struct omap_dma_s *s = g_new0(struct omap_dma_s, 1); - - if (model <= omap_dma_3_1) { - num_irqs = 6; - memsize = 0x800; - } else { - num_irqs = 16; - memsize = 0xc00; - } - s->model = model; - s->mpu = mpu; - s->clk = clk; - s->lcd_ch.irq = lcd_irq; - s->lcd_ch.mpu = mpu; - - s->dma = soc_dma_init((model <= omap_dma_3_1) ? 9 : 16); - s->dma->freq = omap_clk_getrate(clk); - s->dma->transfer_fn = omap_dma_transfer_generic; - s->dma->setup_fn = omap_dma_transfer_setup; - s->dma->drq = qemu_allocate_irqs(omap_dma_request, s, 32); - s->dma->opaque = s; - - while (num_irqs --) - s->ch[num_irqs].irq = irqs[num_irqs]; - for (i = 0; i < 3; i ++) { - s->ch[i].sibling = &s->ch[i + 6]; - s->ch[i + 6].sibling = &s->ch[i]; - } - for (i = (model <= omap_dma_3_1) ? 8 : 15; i >= 0; i --) { - s->ch[i].dma = &s->dma->ch[i]; - s->dma->ch[i].opaque = &s->ch[i]; - } - - omap_dma_setcaps(s); - omap_clk_adduser(s->clk, qemu_allocate_irq(omap_dma_clk_update, s, 0)); - omap_dma_reset(s->dma); - omap_dma_clk_update(s, 0, 1); - - memory_region_init_io(&s->iomem, NULL, &omap_dma_ops, s, "omap.dma", memsize); - memory_region_add_subregion(sysmem, base, &s->iomem); - - mpu->drq = s->dma->drq; - - return s->dma; -} - -static void omap_dma_interrupts_4_update(struct omap_dma_s *s) -{ - struct omap_dma_channel_s *ch = s->ch; - uint32_t bmp, bit; - - for (bmp = 0, bit = 1; bit; ch ++, bit <<= 1) - if (ch->status) { - bmp |= bit; - ch->cstatus |= ch->status; - ch->status = 0; - } - if ((s->irqstat[0] |= s->irqen[0] & bmp)) - qemu_irq_raise(s->irq[0]); - if ((s->irqstat[1] |= s->irqen[1] & bmp)) - qemu_irq_raise(s->irq[1]); - if ((s->irqstat[2] |= s->irqen[2] & bmp)) - qemu_irq_raise(s->irq[2]); - if ((s->irqstat[3] |= s->irqen[3] & bmp)) - qemu_irq_raise(s->irq[3]); -} - -static uint64_t omap_dma4_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_dma_s *s = (struct omap_dma_s *) opaque; - int irqn = 0, chnum; - struct omap_dma_channel_s *ch; - - if (size == 1) { - return omap_badwidth_read16(opaque, addr); - } - - switch (addr) { - case 0x00: /* DMA4_REVISION */ - return 0x40; - - case 0x14: /* DMA4_IRQSTATUS_L3 */ - irqn ++; - /* fall through */ - case 0x10: /* DMA4_IRQSTATUS_L2 */ - irqn ++; - /* fall through */ - case 0x0c: /* DMA4_IRQSTATUS_L1 */ - irqn ++; - /* fall through */ - case 0x08: /* DMA4_IRQSTATUS_L0 */ - return s->irqstat[irqn]; - - case 0x24: /* DMA4_IRQENABLE_L3 */ - irqn ++; - /* fall through */ - case 0x20: /* DMA4_IRQENABLE_L2 */ - irqn ++; - /* fall through */ - case 0x1c: /* DMA4_IRQENABLE_L1 */ - irqn ++; - /* fall through */ - case 0x18: /* DMA4_IRQENABLE_L0 */ - return s->irqen[irqn]; - - case 0x28: /* DMA4_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x2c: /* DMA4_OCP_SYSCONFIG */ - return s->ocp; - - case 0x64: /* DMA4_CAPS_0 */ - return s->caps[0]; - case 0x6c: /* DMA4_CAPS_2 */ - return s->caps[2]; - case 0x70: /* DMA4_CAPS_3 */ - return s->caps[3]; - case 0x74: /* DMA4_CAPS_4 */ - return s->caps[4]; - - case 0x78: /* DMA4_GCR */ - return s->gcr; - - case 0x80 ... 0xfff: - addr -= 0x80; - chnum = addr / 0x60; - ch = s->ch + chnum; - addr -= chnum * 0x60; - break; - - default: - OMAP_BAD_REG(addr); - return 0; - } - - /* Per-channel registers */ - switch (addr) { - case 0x00: /* DMA4_CCR */ - return (ch->buf_disable << 25) | - (ch->src_sync << 24) | - (ch->prefetch << 23) | - ((ch->sync & 0x60) << 14) | - (ch->bs << 18) | - (ch->transparent_copy << 17) | - (ch->constant_fill << 16) | - (ch->mode[1] << 14) | - (ch->mode[0] << 12) | - (0 << 10) | (0 << 9) | - (ch->suspend << 8) | - (ch->enable << 7) | - (ch->priority << 6) | - (ch->fs << 5) | (ch->sync & 0x1f); - - case 0x04: /* DMA4_CLNK_CTRL */ - return (ch->link_enabled << 15) | ch->link_next_ch; - - case 0x08: /* DMA4_CICR */ - return ch->interrupts; - - case 0x0c: /* DMA4_CSR */ - return ch->cstatus; - - case 0x10: /* DMA4_CSDP */ - return (ch->endian[0] << 21) | - (ch->endian_lock[0] << 20) | - (ch->endian[1] << 19) | - (ch->endian_lock[1] << 18) | - (ch->write_mode << 16) | - (ch->burst[1] << 14) | - (ch->pack[1] << 13) | - (ch->translate[1] << 9) | - (ch->burst[0] << 7) | - (ch->pack[0] << 6) | - (ch->translate[0] << 2) | - (ch->data_type >> 1); - - case 0x14: /* DMA4_CEN */ - return ch->elements; - - case 0x18: /* DMA4_CFN */ - return ch->frames; - - case 0x1c: /* DMA4_CSSA */ - return ch->addr[0]; - - case 0x20: /* DMA4_CDSA */ - return ch->addr[1]; - - case 0x24: /* DMA4_CSEI */ - return ch->element_index[0]; - - case 0x28: /* DMA4_CSFI */ - return ch->frame_index[0]; - - case 0x2c: /* DMA4_CDEI */ - return ch->element_index[1]; - - case 0x30: /* DMA4_CDFI */ - return ch->frame_index[1]; - - case 0x34: /* DMA4_CSAC */ - return ch->active_set.src & 0xffff; - - case 0x38: /* DMA4_CDAC */ - return ch->active_set.dest & 0xffff; - - case 0x3c: /* DMA4_CCEN */ - return ch->active_set.element; - - case 0x40: /* DMA4_CCFN */ - return ch->active_set.frame; - - case 0x44: /* DMA4_COLOR */ - /* XXX only in sDMA */ - return ch->color; - - default: - OMAP_BAD_REG(addr); - return 0; - } -} - -static void omap_dma4_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_dma_s *s = (struct omap_dma_s *) opaque; - int chnum, irqn = 0; - struct omap_dma_channel_s *ch; - - if (size == 1) { - omap_badwidth_write16(opaque, addr, value); - return; - } - - switch (addr) { - case 0x14: /* DMA4_IRQSTATUS_L3 */ - irqn ++; - /* fall through */ - case 0x10: /* DMA4_IRQSTATUS_L2 */ - irqn ++; - /* fall through */ - case 0x0c: /* DMA4_IRQSTATUS_L1 */ - irqn ++; - /* fall through */ - case 0x08: /* DMA4_IRQSTATUS_L0 */ - s->irqstat[irqn] &= ~value; - if (!s->irqstat[irqn]) - qemu_irq_lower(s->irq[irqn]); - return; - - case 0x24: /* DMA4_IRQENABLE_L3 */ - irqn ++; - /* fall through */ - case 0x20: /* DMA4_IRQENABLE_L2 */ - irqn ++; - /* fall through */ - case 0x1c: /* DMA4_IRQENABLE_L1 */ - irqn ++; - /* fall through */ - case 0x18: /* DMA4_IRQENABLE_L0 */ - s->irqen[irqn] = value; - return; - - case 0x2c: /* DMA4_OCP_SYSCONFIG */ - if (value & 2) /* SOFTRESET */ - omap_dma_reset(s->dma); - s->ocp = value & 0x3321; - if (((s->ocp >> 12) & 3) == 3) /* MIDLEMODE */ - fprintf(stderr, "%s: invalid DMA power mode\n", __FUNCTION__); - return; - - case 0x78: /* DMA4_GCR */ - s->gcr = value & 0x00ff00ff; - if ((value & 0xff) == 0x00) /* MAX_CHANNEL_FIFO_DEPTH */ - fprintf(stderr, "%s: wrong FIFO depth in GCR\n", __FUNCTION__); - return; - - case 0x80 ... 0xfff: - addr -= 0x80; - chnum = addr / 0x60; - ch = s->ch + chnum; - addr -= chnum * 0x60; - break; - - case 0x00: /* DMA4_REVISION */ - case 0x28: /* DMA4_SYSSTATUS */ - case 0x64: /* DMA4_CAPS_0 */ - case 0x6c: /* DMA4_CAPS_2 */ - case 0x70: /* DMA4_CAPS_3 */ - case 0x74: /* DMA4_CAPS_4 */ - OMAP_RO_REG(addr); - return; - - default: - OMAP_BAD_REG(addr); - return; - } - - /* Per-channel registers */ - switch (addr) { - case 0x00: /* DMA4_CCR */ - ch->buf_disable = (value >> 25) & 1; - ch->src_sync = (value >> 24) & 1; /* XXX For CamDMA must be 1 */ - if (ch->buf_disable && !ch->src_sync) - fprintf(stderr, "%s: Buffering disable is not allowed in " - "destination synchronised mode\n", __FUNCTION__); - ch->prefetch = (value >> 23) & 1; - ch->bs = (value >> 18) & 1; - ch->transparent_copy = (value >> 17) & 1; - ch->constant_fill = (value >> 16) & 1; - ch->mode[1] = (omap_dma_addressing_t) ((value & 0xc000) >> 14); - ch->mode[0] = (omap_dma_addressing_t) ((value & 0x3000) >> 12); - ch->suspend = (value & 0x0100) >> 8; - ch->priority = (value & 0x0040) >> 6; - ch->fs = (value & 0x0020) >> 5; - if (ch->fs && ch->bs && ch->mode[0] && ch->mode[1]) - fprintf(stderr, "%s: For a packet transfer at least one port " - "must be constant-addressed\n", __FUNCTION__); - ch->sync = (value & 0x001f) | ((value >> 14) & 0x0060); - /* XXX must be 0x01 for CamDMA */ - - if (value & 0x0080) - omap_dma_enable_channel(s, ch); - else - omap_dma_disable_channel(s, ch); - - break; - - case 0x04: /* DMA4_CLNK_CTRL */ - ch->link_enabled = (value >> 15) & 0x1; - ch->link_next_ch = value & 0x1f; - break; - - case 0x08: /* DMA4_CICR */ - ch->interrupts = value & 0x09be; - break; - - case 0x0c: /* DMA4_CSR */ - ch->cstatus &= ~value; - break; - - case 0x10: /* DMA4_CSDP */ - ch->endian[0] =(value >> 21) & 1; - ch->endian_lock[0] =(value >> 20) & 1; - ch->endian[1] =(value >> 19) & 1; - ch->endian_lock[1] =(value >> 18) & 1; - if (ch->endian[0] != ch->endian[1]) - fprintf(stderr, "%s: DMA endiannes conversion enable attempt\n", - __FUNCTION__); - ch->write_mode = (value >> 16) & 3; - ch->burst[1] = (value & 0xc000) >> 14; - ch->pack[1] = (value & 0x2000) >> 13; - ch->translate[1] = (value & 0x1e00) >> 9; - ch->burst[0] = (value & 0x0180) >> 7; - ch->pack[0] = (value & 0x0040) >> 6; - ch->translate[0] = (value & 0x003c) >> 2; - if (ch->translate[0] | ch->translate[1]) - fprintf(stderr, "%s: bad MReqAddressTranslate sideband signal\n", - __FUNCTION__); - ch->data_type = 1 << (value & 3); - if ((value & 3) == 3) - printf("%s: bad data_type for DMA channel\n", __FUNCTION__); - break; - - case 0x14: /* DMA4_CEN */ - ch->set_update = 1; - ch->elements = value & 0xffffff; - break; - - case 0x18: /* DMA4_CFN */ - ch->frames = value & 0xffff; - ch->set_update = 1; - break; - - case 0x1c: /* DMA4_CSSA */ - ch->addr[0] = (hwaddr) (uint32_t) value; - ch->set_update = 1; - break; - - case 0x20: /* DMA4_CDSA */ - ch->addr[1] = (hwaddr) (uint32_t) value; - ch->set_update = 1; - break; - - case 0x24: /* DMA4_CSEI */ - ch->element_index[0] = (int16_t) value; - ch->set_update = 1; - break; - - case 0x28: /* DMA4_CSFI */ - ch->frame_index[0] = (int32_t) value; - ch->set_update = 1; - break; - - case 0x2c: /* DMA4_CDEI */ - ch->element_index[1] = (int16_t) value; - ch->set_update = 1; - break; - - case 0x30: /* DMA4_CDFI */ - ch->frame_index[1] = (int32_t) value; - ch->set_update = 1; - break; - - case 0x44: /* DMA4_COLOR */ - /* XXX only in sDMA */ - ch->color = value; - break; - - case 0x34: /* DMA4_CSAC */ - case 0x38: /* DMA4_CDAC */ - case 0x3c: /* DMA4_CCEN */ - case 0x40: /* DMA4_CCFN */ - OMAP_RO_REG(addr); - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_dma4_ops = { - .read = omap_dma4_read, - .write = omap_dma4_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct soc_dma_s *omap_dma4_init(hwaddr base, qemu_irq *irqs, - MemoryRegion *sysmem, - struct omap_mpu_state_s *mpu, int fifo, - int chans, omap_clk iclk, omap_clk fclk) -{ - int i; - struct omap_dma_s *s = g_new0(struct omap_dma_s, 1); - - s->model = omap_dma_4; - s->chans = chans; - s->mpu = mpu; - s->clk = fclk; - - s->dma = soc_dma_init(s->chans); - s->dma->freq = omap_clk_getrate(fclk); - s->dma->transfer_fn = omap_dma_transfer_generic; - s->dma->setup_fn = omap_dma_transfer_setup; - s->dma->drq = qemu_allocate_irqs(omap_dma_request, s, 64); - s->dma->opaque = s; - for (i = 0; i < s->chans; i ++) { - s->ch[i].dma = &s->dma->ch[i]; - s->dma->ch[i].opaque = &s->ch[i]; - } - - memcpy(&s->irq, irqs, sizeof(s->irq)); - s->intr_update = omap_dma_interrupts_4_update; - - omap_dma_setcaps(s); - omap_clk_adduser(s->clk, qemu_allocate_irq(omap_dma_clk_update, s, 0)); - omap_dma_reset(s->dma); - omap_dma_clk_update(s, 0, !!s->dma->freq); - - memory_region_init_io(&s->iomem, NULL, &omap_dma4_ops, s, "omap.dma4", 0x1000); - memory_region_add_subregion(sysmem, base, &s->iomem); - - mpu->drq = s->dma->drq; - - return s->dma; -} - -struct omap_dma_lcd_channel_s *omap_dma_get_lcdch(struct soc_dma_s *dma) -{ - struct omap_dma_s *s = dma->opaque; - - return &s->lcd_ch; -} diff --git a/qemu/hw/dma/pl080.c b/qemu/hw/dma/pl080.c deleted file mode 100644 index 9318108b8..000000000 --- a/qemu/hw/dma/pl080.c +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Arm PrimeCell PL080/PL081 DMA controller - * - * Copyright (c) 2006 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "exec/address-spaces.h" - -#define PL080_MAX_CHANNELS 8 -#define PL080_CONF_E 0x1 -#define PL080_CONF_M1 0x2 -#define PL080_CONF_M2 0x4 - -#define PL080_CCONF_H 0x40000 -#define PL080_CCONF_A 0x20000 -#define PL080_CCONF_L 0x10000 -#define PL080_CCONF_ITC 0x08000 -#define PL080_CCONF_IE 0x04000 -#define PL080_CCONF_E 0x00001 - -#define PL080_CCTRL_I 0x80000000 -#define PL080_CCTRL_DI 0x08000000 -#define PL080_CCTRL_SI 0x04000000 -#define PL080_CCTRL_D 0x02000000 -#define PL080_CCTRL_S 0x01000000 - -typedef struct { - uint32_t src; - uint32_t dest; - uint32_t lli; - uint32_t ctrl; - uint32_t conf; -} pl080_channel; - -#define TYPE_PL080 "pl080" -#define PL080(obj) OBJECT_CHECK(PL080State, (obj), TYPE_PL080) - -typedef struct PL080State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint8_t tc_int; - uint8_t tc_mask; - uint8_t err_int; - uint8_t err_mask; - uint32_t conf; - uint32_t sync; - uint32_t req_single; - uint32_t req_burst; - pl080_channel chan[PL080_MAX_CHANNELS]; - int nchannels; - /* Flag to avoid recursive DMA invocations. */ - int running; - qemu_irq irq; -} PL080State; - -static const VMStateDescription vmstate_pl080_channel = { - .name = "pl080_channel", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(src, pl080_channel), - VMSTATE_UINT32(dest, pl080_channel), - VMSTATE_UINT32(lli, pl080_channel), - VMSTATE_UINT32(ctrl, pl080_channel), - VMSTATE_UINT32(conf, pl080_channel), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_pl080 = { - .name = "pl080", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(tc_int, PL080State), - VMSTATE_UINT8(tc_mask, PL080State), - VMSTATE_UINT8(err_int, PL080State), - VMSTATE_UINT8(err_mask, PL080State), - VMSTATE_UINT32(conf, PL080State), - VMSTATE_UINT32(sync, PL080State), - VMSTATE_UINT32(req_single, PL080State), - VMSTATE_UINT32(req_burst, PL080State), - VMSTATE_UINT8(tc_int, PL080State), - VMSTATE_UINT8(tc_int, PL080State), - VMSTATE_UINT8(tc_int, PL080State), - VMSTATE_STRUCT_ARRAY(chan, PL080State, PL080_MAX_CHANNELS, - 1, vmstate_pl080_channel, pl080_channel), - VMSTATE_INT32(running, PL080State), - VMSTATE_END_OF_LIST() - } -}; - -static const unsigned char pl080_id[] = -{ 0x80, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 }; - -static const unsigned char pl081_id[] = -{ 0x81, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 }; - -static void pl080_update(PL080State *s) -{ - if ((s->tc_int & s->tc_mask) - || (s->err_int & s->err_mask)) - qemu_irq_raise(s->irq); - else - qemu_irq_lower(s->irq); -} - -static void pl080_run(PL080State *s) -{ - int c; - int flow; - pl080_channel *ch; - int swidth; - int dwidth; - int xsize; - int n; - int src_id; - int dest_id; - int size; - uint8_t buff[4]; - uint32_t req; - - s->tc_mask = 0; - for (c = 0; c < s->nchannels; c++) { - if (s->chan[c].conf & PL080_CCONF_ITC) - s->tc_mask |= 1 << c; - if (s->chan[c].conf & PL080_CCONF_IE) - s->err_mask |= 1 << c; - } - - if ((s->conf & PL080_CONF_E) == 0) - return; - -hw_error("DMA active\n"); - /* If we are already in the middle of a DMA operation then indicate that - there may be new DMA requests and return immediately. */ - if (s->running) { - s->running++; - return; - } - s->running = 1; - while (s->running) { - for (c = 0; c < s->nchannels; c++) { - ch = &s->chan[c]; -again: - /* Test if thiws channel has any pending DMA requests. */ - if ((ch->conf & (PL080_CCONF_H | PL080_CCONF_E)) - != PL080_CCONF_E) - continue; - flow = (ch->conf >> 11) & 7; - if (flow >= 4) { - hw_error( - "pl080_run: Peripheral flow control not implemented\n"); - } - src_id = (ch->conf >> 1) & 0x1f; - dest_id = (ch->conf >> 6) & 0x1f; - size = ch->ctrl & 0xfff; - req = s->req_single | s->req_burst; - switch (flow) { - case 0: - break; - case 1: - if ((req & (1u << dest_id)) == 0) - size = 0; - break; - case 2: - if ((req & (1u << src_id)) == 0) - size = 0; - break; - case 3: - if ((req & (1u << src_id)) == 0 - || (req & (1u << dest_id)) == 0) - size = 0; - break; - } - if (!size) - continue; - - /* Transfer one element. */ - /* ??? Should transfer multiple elements for a burst request. */ - /* ??? Unclear what the proper behavior is when source and - destination widths are different. */ - swidth = 1 << ((ch->ctrl >> 18) & 7); - dwidth = 1 << ((ch->ctrl >> 21) & 7); - for (n = 0; n < dwidth; n+= swidth) { - cpu_physical_memory_read(ch->src, buff + n, swidth); - if (ch->ctrl & PL080_CCTRL_SI) - ch->src += swidth; - } - xsize = (dwidth < swidth) ? swidth : dwidth; - /* ??? This may pad the value incorrectly for dwidth < 32. */ - for (n = 0; n < xsize; n += dwidth) { - cpu_physical_memory_write(ch->dest + n, buff + n, dwidth); - if (ch->ctrl & PL080_CCTRL_DI) - ch->dest += swidth; - } - - size--; - ch->ctrl = (ch->ctrl & 0xfffff000) | size; - if (size == 0) { - /* Transfer complete. */ - if (ch->lli) { - ch->src = address_space_ldl_le(&address_space_memory, - ch->lli, - MEMTXATTRS_UNSPECIFIED, - NULL); - ch->dest = address_space_ldl_le(&address_space_memory, - ch->lli + 4, - MEMTXATTRS_UNSPECIFIED, - NULL); - ch->ctrl = address_space_ldl_le(&address_space_memory, - ch->lli + 12, - MEMTXATTRS_UNSPECIFIED, - NULL); - ch->lli = address_space_ldl_le(&address_space_memory, - ch->lli + 8, - MEMTXATTRS_UNSPECIFIED, - NULL); - } else { - ch->conf &= ~PL080_CCONF_E; - } - if (ch->ctrl & PL080_CCTRL_I) { - s->tc_int |= 1 << c; - } - } - goto again; - } - if (--s->running) - s->running = 1; - } -} - -static uint64_t pl080_read(void *opaque, hwaddr offset, - unsigned size) -{ - PL080State *s = (PL080State *)opaque; - uint32_t i; - uint32_t mask; - - if (offset >= 0xfe0 && offset < 0x1000) { - if (s->nchannels == 8) { - return pl080_id[(offset - 0xfe0) >> 2]; - } else { - return pl081_id[(offset - 0xfe0) >> 2]; - } - } - if (offset >= 0x100 && offset < 0x200) { - i = (offset & 0xe0) >> 5; - if (i >= s->nchannels) - goto bad_offset; - switch (offset >> 2) { - case 0: /* SrcAddr */ - return s->chan[i].src; - case 1: /* DestAddr */ - return s->chan[i].dest; - case 2: /* LLI */ - return s->chan[i].lli; - case 3: /* Control */ - return s->chan[i].ctrl; - case 4: /* Configuration */ - return s->chan[i].conf; - default: - goto bad_offset; - } - } - switch (offset >> 2) { - case 0: /* IntStatus */ - return (s->tc_int & s->tc_mask) | (s->err_int & s->err_mask); - case 1: /* IntTCStatus */ - return (s->tc_int & s->tc_mask); - case 3: /* IntErrorStatus */ - return (s->err_int & s->err_mask); - case 5: /* RawIntTCStatus */ - return s->tc_int; - case 6: /* RawIntErrorStatus */ - return s->err_int; - case 7: /* EnbldChns */ - mask = 0; - for (i = 0; i < s->nchannels; i++) { - if (s->chan[i].conf & PL080_CCONF_E) - mask |= 1 << i; - } - return mask; - case 8: /* SoftBReq */ - case 9: /* SoftSReq */ - case 10: /* SoftLBReq */ - case 11: /* SoftLSReq */ - /* ??? Implement these. */ - return 0; - case 12: /* Configuration */ - return s->conf; - case 13: /* Sync */ - return s->sync; - default: - bad_offset: - qemu_log_mask(LOG_GUEST_ERROR, - "pl080_read: Bad offset %x\n", (int)offset); - return 0; - } -} - -static void pl080_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PL080State *s = (PL080State *)opaque; - int i; - - if (offset >= 0x100 && offset < 0x200) { - i = (offset & 0xe0) >> 5; - if (i >= s->nchannels) - goto bad_offset; - switch (offset >> 2) { - case 0: /* SrcAddr */ - s->chan[i].src = value; - break; - case 1: /* DestAddr */ - s->chan[i].dest = value; - break; - case 2: /* LLI */ - s->chan[i].lli = value; - break; - case 3: /* Control */ - s->chan[i].ctrl = value; - break; - case 4: /* Configuration */ - s->chan[i].conf = value; - pl080_run(s); - break; - } - } - switch (offset >> 2) { - case 2: /* IntTCClear */ - s->tc_int &= ~value; - break; - case 4: /* IntErrorClear */ - s->err_int &= ~value; - break; - case 8: /* SoftBReq */ - case 9: /* SoftSReq */ - case 10: /* SoftLBReq */ - case 11: /* SoftLSReq */ - /* ??? Implement these. */ - qemu_log_mask(LOG_UNIMP, "pl080_write: Soft DMA not implemented\n"); - break; - case 12: /* Configuration */ - s->conf = value; - if (s->conf & (PL080_CONF_M1 | PL080_CONF_M1)) { - qemu_log_mask(LOG_UNIMP, - "pl080_write: Big-endian DMA not implemented\n"); - } - pl080_run(s); - break; - case 13: /* Sync */ - s->sync = value; - break; - default: - bad_offset: - qemu_log_mask(LOG_GUEST_ERROR, - "pl080_write: Bad offset %x\n", (int)offset); - } - pl080_update(s); -} - -static const MemoryRegionOps pl080_ops = { - .read = pl080_read, - .write = pl080_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pl080_init(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - PL080State *s = PL080(obj); - - memory_region_init_io(&s->iomem, OBJECT(s), &pl080_ops, s, "pl080", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - s->nchannels = 8; -} - -static void pl081_init(Object *obj) -{ - PL080State *s = PL080(obj); - - s->nchannels = 2; -} - -static void pl080_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->vmsd = &vmstate_pl080; -} - -static const TypeInfo pl080_info = { - .name = TYPE_PL080, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PL080State), - .instance_init = pl080_init, - .class_init = pl080_class_init, -}; - -static const TypeInfo pl081_info = { - .name = "pl081", - .parent = TYPE_PL080, - .instance_init = pl081_init, -}; - -/* The PL080 and PL081 are the same except for the number of channels - they implement (8 and 2 respectively). */ -static void pl080_register_types(void) -{ - type_register_static(&pl080_info); - type_register_static(&pl081_info); -} - -type_init(pl080_register_types) diff --git a/qemu/hw/dma/pl330.c b/qemu/hw/dma/pl330.c deleted file mode 100644 index ea89ecb00..000000000 --- a/qemu/hw/dma/pl330.c +++ /dev/null @@ -1,1668 +0,0 @@ -/* - * ARM PrimeCell PL330 DMA Controller - * - * Copyright (c) 2009 Samsung Electronics. - * Contributed by Kirill Batuzov - * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com) - * Copyright (c) 2012 PetaLogix Pty Ltd. - * - * 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; version 2 or later. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include "sysemu/dma.h" - -#ifndef PL330_ERR_DEBUG -#define PL330_ERR_DEBUG 0 -#endif - -#define DB_PRINT_L(lvl, fmt, args...) do {\ - if (PL330_ERR_DEBUG >= lvl) {\ - fprintf(stderr, "PL330: %s:" fmt, __func__, ## args);\ - } \ -} while (0); - -#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) - -#define PL330_PERIPH_NUM 32 -#define PL330_MAX_BURST_LEN 128 -#define PL330_INSN_MAXSIZE 6 - -#define PL330_FIFO_OK 0 -#define PL330_FIFO_STALL 1 -#define PL330_FIFO_ERR (-1) - -#define PL330_FAULT_UNDEF_INSTR (1 << 0) -#define PL330_FAULT_OPERAND_INVALID (1 << 1) -#define PL330_FAULT_DMAGO_ERR (1 << 4) -#define PL330_FAULT_EVENT_ERR (1 << 5) -#define PL330_FAULT_CH_PERIPH_ERR (1 << 6) -#define PL330_FAULT_CH_RDWR_ERR (1 << 7) -#define PL330_FAULT_ST_DATA_UNAVAILABLE (1 << 12) -#define PL330_FAULT_FIFOEMPTY_ERR (1 << 13) -#define PL330_FAULT_INSTR_FETCH_ERR (1 << 16) -#define PL330_FAULT_DATA_WRITE_ERR (1 << 17) -#define PL330_FAULT_DATA_READ_ERR (1 << 18) -#define PL330_FAULT_DBG_INSTR (1 << 30) -#define PL330_FAULT_LOCKUP_ERR (1 << 31) - -#define PL330_UNTAGGED 0xff - -#define PL330_SINGLE 0x0 -#define PL330_BURST 0x1 - -#define PL330_WATCHDOG_LIMIT 1024 - -/* IOMEM mapped registers */ -#define PL330_REG_DSR 0x000 -#define PL330_REG_DPC 0x004 -#define PL330_REG_INTEN 0x020 -#define PL330_REG_INT_EVENT_RIS 0x024 -#define PL330_REG_INTMIS 0x028 -#define PL330_REG_INTCLR 0x02C -#define PL330_REG_FSRD 0x030 -#define PL330_REG_FSRC 0x034 -#define PL330_REG_FTRD 0x038 -#define PL330_REG_FTR_BASE 0x040 -#define PL330_REG_CSR_BASE 0x100 -#define PL330_REG_CPC_BASE 0x104 -#define PL330_REG_CHANCTRL 0x400 -#define PL330_REG_DBGSTATUS 0xD00 -#define PL330_REG_DBGCMD 0xD04 -#define PL330_REG_DBGINST0 0xD08 -#define PL330_REG_DBGINST1 0xD0C -#define PL330_REG_CR0_BASE 0xE00 -#define PL330_REG_PERIPH_ID 0xFE0 - -#define PL330_IOMEM_SIZE 0x1000 - -#define CFG_BOOT_ADDR 2 -#define CFG_INS 3 -#define CFG_PNS 4 -#define CFG_CRD 5 - -static const uint32_t pl330_id[] = { - 0x30, 0x13, 0x24, 0x00, 0x0D, 0xF0, 0x05, 0xB1 -}; - -/* DMA channel states as they are described in PL330 Technical Reference Manual - * Most of them will not be used in emulation. - */ -typedef enum { - pl330_chan_stopped = 0, - pl330_chan_executing = 1, - pl330_chan_cache_miss = 2, - pl330_chan_updating_pc = 3, - pl330_chan_waiting_event = 4, - pl330_chan_at_barrier = 5, - pl330_chan_queue_busy = 6, - pl330_chan_waiting_periph = 7, - pl330_chan_killing = 8, - pl330_chan_completing = 9, - pl330_chan_fault_completing = 14, - pl330_chan_fault = 15, -} PL330ChanState; - -typedef struct PL330State PL330State; - -typedef struct PL330Chan { - uint32_t src; - uint32_t dst; - uint32_t pc; - uint32_t control; - uint32_t status; - uint32_t lc[2]; - uint32_t fault_type; - uint32_t watchdog_timer; - - bool ns; - uint8_t request_flag; - uint8_t wakeup; - uint8_t wfp_sbp; - - uint8_t state; - uint8_t stall; - - bool is_manager; - PL330State *parent; - uint8_t tag; -} PL330Chan; - -static const VMStateDescription vmstate_pl330_chan = { - .name = "pl330_chan", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(src, PL330Chan), - VMSTATE_UINT32(dst, PL330Chan), - VMSTATE_UINT32(pc, PL330Chan), - VMSTATE_UINT32(control, PL330Chan), - VMSTATE_UINT32(status, PL330Chan), - VMSTATE_UINT32_ARRAY(lc, PL330Chan, 2), - VMSTATE_UINT32(fault_type, PL330Chan), - VMSTATE_UINT32(watchdog_timer, PL330Chan), - VMSTATE_BOOL(ns, PL330Chan), - VMSTATE_UINT8(request_flag, PL330Chan), - VMSTATE_UINT8(wakeup, PL330Chan), - VMSTATE_UINT8(wfp_sbp, PL330Chan), - VMSTATE_UINT8(state, PL330Chan), - VMSTATE_UINT8(stall, PL330Chan), - VMSTATE_END_OF_LIST() - } -}; - -typedef struct PL330Fifo { - uint8_t *buf; - uint8_t *tag; - uint32_t head; - uint32_t num; - uint32_t buf_size; -} PL330Fifo; - -static const VMStateDescription vmstate_pl330_fifo = { - .name = "pl330_chan", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_VBUFFER_UINT32(buf, PL330Fifo, 1, NULL, 0, buf_size), - VMSTATE_VBUFFER_UINT32(tag, PL330Fifo, 1, NULL, 0, buf_size), - VMSTATE_UINT32(head, PL330Fifo), - VMSTATE_UINT32(num, PL330Fifo), - VMSTATE_UINT32(buf_size, PL330Fifo), - VMSTATE_END_OF_LIST() - } -}; - -typedef struct PL330QueueEntry { - uint32_t addr; - uint32_t len; - uint8_t n; - bool inc; - bool z; - uint8_t tag; - uint8_t seqn; -} PL330QueueEntry; - -static const VMStateDescription vmstate_pl330_queue_entry = { - .name = "pl330_queue_entry", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(addr, PL330QueueEntry), - VMSTATE_UINT32(len, PL330QueueEntry), - VMSTATE_UINT8(n, PL330QueueEntry), - VMSTATE_BOOL(inc, PL330QueueEntry), - VMSTATE_BOOL(z, PL330QueueEntry), - VMSTATE_UINT8(tag, PL330QueueEntry), - VMSTATE_UINT8(seqn, PL330QueueEntry), - VMSTATE_END_OF_LIST() - } -}; - -typedef struct PL330Queue { - PL330State *parent; - PL330QueueEntry *queue; - uint32_t queue_size; -} PL330Queue; - -static const VMStateDescription vmstate_pl330_queue = { - .name = "pl330_queue", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_VARRAY_UINT32(queue, PL330Queue, queue_size, 1, - vmstate_pl330_queue_entry, PL330QueueEntry), - VMSTATE_END_OF_LIST() - } -}; - -struct PL330State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq irq_abort; - qemu_irq *irq; - - /* Config registers. cfg[5] = CfgDn. */ - uint32_t cfg[6]; -#define EVENT_SEC_STATE 3 -#define PERIPH_SEC_STATE 4 - /* cfg 0 bits and pieces */ - uint32_t num_chnls; - uint8_t num_periph_req; - uint8_t num_events; - uint8_t mgr_ns_at_rst; - /* cfg 1 bits and pieces */ - uint8_t i_cache_len; - uint8_t num_i_cache_lines; - /* CRD bits and pieces */ - uint8_t data_width; - uint8_t wr_cap; - uint8_t wr_q_dep; - uint8_t rd_cap; - uint8_t rd_q_dep; - uint16_t data_buffer_dep; - - PL330Chan manager; - PL330Chan *chan; - PL330Fifo fifo; - PL330Queue read_queue; - PL330Queue write_queue; - uint8_t *lo_seqn; - uint8_t *hi_seqn; - QEMUTimer *timer; /* is used for restore dma. */ - - uint32_t inten; - uint32_t int_status; - uint32_t ev_status; - uint32_t dbg[2]; - uint8_t debug_status; - uint8_t num_faulting; - uint8_t periph_busy[PL330_PERIPH_NUM]; - -}; - -#define TYPE_PL330 "pl330" -#define PL330(obj) OBJECT_CHECK(PL330State, (obj), TYPE_PL330) - -static const VMStateDescription vmstate_pl330 = { - .name = "pl330", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(manager, PL330State, 0, vmstate_pl330_chan, PL330Chan), - VMSTATE_STRUCT_VARRAY_UINT32(chan, PL330State, num_chnls, 0, - vmstate_pl330_chan, PL330Chan), - VMSTATE_VBUFFER_UINT32(lo_seqn, PL330State, 1, NULL, 0, num_chnls), - VMSTATE_VBUFFER_UINT32(hi_seqn, PL330State, 1, NULL, 0, num_chnls), - VMSTATE_STRUCT(fifo, PL330State, 0, vmstate_pl330_fifo, PL330Fifo), - VMSTATE_STRUCT(read_queue, PL330State, 0, vmstate_pl330_queue, - PL330Queue), - VMSTATE_STRUCT(write_queue, PL330State, 0, vmstate_pl330_queue, - PL330Queue), - VMSTATE_TIMER_PTR(timer, PL330State), - VMSTATE_UINT32(inten, PL330State), - VMSTATE_UINT32(int_status, PL330State), - VMSTATE_UINT32(ev_status, PL330State), - VMSTATE_UINT32_ARRAY(dbg, PL330State, 2), - VMSTATE_UINT8(debug_status, PL330State), - VMSTATE_UINT8(num_faulting, PL330State), - VMSTATE_UINT8_ARRAY(periph_busy, PL330State, PL330_PERIPH_NUM), - VMSTATE_END_OF_LIST() - } -}; - -typedef struct PL330InsnDesc { - /* OPCODE of the instruction */ - uint8_t opcode; - /* Mask so we can select several sibling instructions, such as - DMALD, DMALDS and DMALDB */ - uint8_t opmask; - /* Size of instruction in bytes */ - uint8_t size; - /* Interpreter */ - void (*exec)(PL330Chan *, uint8_t opcode, uint8_t *args, int len); -} PL330InsnDesc; - - -/* MFIFO Implementation - * - * MFIFO is implemented as a cyclic buffer of BUF_SIZE size. Tagged bytes are - * stored in this buffer. Data is stored in BUF field, tags - in the - * corresponding array elements of TAG field. - */ - -/* Initialize queue. */ - -static void pl330_fifo_init(PL330Fifo *s, uint32_t size) -{ - s->buf = g_malloc0(size); - s->tag = g_malloc0(size); - s->buf_size = size; -} - -/* Cyclic increment */ - -static inline int pl330_fifo_inc(PL330Fifo *s, int x) -{ - return (x + 1) % s->buf_size; -} - -/* Number of empty bytes in MFIFO */ - -static inline int pl330_fifo_num_free(PL330Fifo *s) -{ - return s->buf_size - s->num; -} - -/* Push LEN bytes of data stored in BUF to MFIFO and tag it with TAG. - * Zero returned on success, PL330_FIFO_STALL if there is no enough free - * space in MFIFO to store requested amount of data. If push was unsuccessful - * no data is stored to MFIFO. - */ - -static int pl330_fifo_push(PL330Fifo *s, uint8_t *buf, int len, uint8_t tag) -{ - int i; - - if (s->buf_size - s->num < len) { - return PL330_FIFO_STALL; - } - for (i = 0; i < len; i++) { - int push_idx = (s->head + s->num + i) % s->buf_size; - s->buf[push_idx] = buf[i]; - s->tag[push_idx] = tag; - } - s->num += len; - return PL330_FIFO_OK; -} - -/* Get LEN bytes of data from MFIFO and store it to BUF. Tag value of each - * byte is verified. Zero returned on success, PL330_FIFO_ERR on tag mismatch - * and PL330_FIFO_STALL if there is no enough data in MFIFO. If get was - * unsuccessful no data is removed from MFIFO. - */ - -static int pl330_fifo_get(PL330Fifo *s, uint8_t *buf, int len, uint8_t tag) -{ - int i; - - if (s->num < len) { - return PL330_FIFO_STALL; - } - for (i = 0; i < len; i++) { - if (s->tag[s->head] == tag) { - int get_idx = (s->head + i) % s->buf_size; - buf[i] = s->buf[get_idx]; - } else { /* Tag mismatch - Rollback transaction */ - return PL330_FIFO_ERR; - } - } - s->head = (s->head + len) % s->buf_size; - s->num -= len; - return PL330_FIFO_OK; -} - -/* Reset MFIFO. This completely erases all data in it. */ - -static inline void pl330_fifo_reset(PL330Fifo *s) -{ - s->head = 0; - s->num = 0; -} - -/* Return tag of the first byte stored in MFIFO. If MFIFO is empty - * PL330_UNTAGGED is returned. - */ - -static inline uint8_t pl330_fifo_tag(PL330Fifo *s) -{ - return (!s->num) ? PL330_UNTAGGED : s->tag[s->head]; -} - -/* Returns non-zero if tag TAG is present in fifo or zero otherwise */ - -static int pl330_fifo_has_tag(PL330Fifo *s, uint8_t tag) -{ - int i, n; - - i = s->head; - for (n = 0; n < s->num; n++) { - if (s->tag[i] == tag) { - return 1; - } - i = pl330_fifo_inc(s, i); - } - return 0; -} - -/* Remove all entry tagged with TAG from MFIFO */ - -static void pl330_fifo_tagged_remove(PL330Fifo *s, uint8_t tag) -{ - int i, t, n; - - t = i = s->head; - for (n = 0; n < s->num; n++) { - if (s->tag[i] != tag) { - s->buf[t] = s->buf[i]; - s->tag[t] = s->tag[i]; - t = pl330_fifo_inc(s, t); - } else { - s->num = s->num - 1; - } - i = pl330_fifo_inc(s, i); - } -} - -/* Read-Write Queue implementation - * - * A Read-Write Queue stores up to QUEUE_SIZE instructions (loads or stores). - * Each instruction is described by source (for loads) or destination (for - * stores) address ADDR, width of data to be loaded/stored LEN, number of - * stores/loads to be performed N, INC bit, Z bit and TAG to identify channel - * this instruction belongs to. Queue does not store any information about - * nature of the instruction: is it load or store. PL330 has different queues - * for loads and stores so this is already known at the top level where it - * matters. - * - * Queue works as FIFO for instructions with equivalent tags, but can issue - * instructions with different tags in arbitrary order. SEQN field attached to - * each instruction helps to achieve this. For each TAG queue contains - * instructions with consecutive SEQN values ranging from LO_SEQN[TAG] to - * HI_SEQN[TAG]-1 inclusive. SEQN is 8-bit unsigned integer, so SEQN=255 is - * followed by SEQN=0. - * - * Z bit indicates that zeroes should be stored. No MFIFO fetches are performed - * in this case. - */ - -static void pl330_queue_reset(PL330Queue *s) -{ - int i; - - for (i = 0; i < s->queue_size; i++) { - s->queue[i].tag = PL330_UNTAGGED; - } -} - -/* Initialize queue */ -static void pl330_queue_init(PL330Queue *s, int size, PL330State *parent) -{ - s->parent = parent; - s->queue = g_new0(PL330QueueEntry, size); - s->queue_size = size; -} - -/* Returns pointer to an empty slot or NULL if queue is full */ -static PL330QueueEntry *pl330_queue_find_empty(PL330Queue *s) -{ - int i; - - for (i = 0; i < s->queue_size; i++) { - if (s->queue[i].tag == PL330_UNTAGGED) { - return &s->queue[i]; - } - } - return NULL; -} - -/* Put instruction in queue. - * Return value: - * - zero - OK - * - non-zero - queue is full - */ - -static int pl330_queue_put_insn(PL330Queue *s, uint32_t addr, - int len, int n, bool inc, bool z, uint8_t tag) -{ - PL330QueueEntry *entry = pl330_queue_find_empty(s); - - if (!entry) { - return 1; - } - entry->tag = tag; - entry->addr = addr; - entry->len = len; - entry->n = n; - entry->z = z; - entry->inc = inc; - entry->seqn = s->parent->hi_seqn[tag]; - s->parent->hi_seqn[tag]++; - return 0; -} - -/* Returns a pointer to queue slot containing instruction which satisfies - * following conditions: - * - it has valid tag value (not PL330_UNTAGGED) - * - if enforce_seq is set it has to be issuable without violating queue - * logic (see above) - * - if TAG argument is not PL330_UNTAGGED this instruction has tag value - * equivalent to the argument TAG value. - * If such instruction cannot be found NULL is returned. - */ - -static PL330QueueEntry *pl330_queue_find_insn(PL330Queue *s, uint8_t tag, - bool enforce_seq) -{ - int i; - - for (i = 0; i < s->queue_size; i++) { - if (s->queue[i].tag != PL330_UNTAGGED) { - if ((!enforce_seq || - s->queue[i].seqn == s->parent->lo_seqn[s->queue[i].tag]) && - (s->queue[i].tag == tag || tag == PL330_UNTAGGED || - s->queue[i].z)) { - return &s->queue[i]; - } - } - } - return NULL; -} - -/* Removes instruction from queue. */ - -static inline void pl330_queue_remove_insn(PL330Queue *s, PL330QueueEntry *e) -{ - s->parent->lo_seqn[e->tag]++; - e->tag = PL330_UNTAGGED; -} - -/* Removes all instructions tagged with TAG from queue. */ - -static inline void pl330_queue_remove_tagged(PL330Queue *s, uint8_t tag) -{ - int i; - - for (i = 0; i < s->queue_size; i++) { - if (s->queue[i].tag == tag) { - s->queue[i].tag = PL330_UNTAGGED; - } - } -} - -/* DMA instruction execution engine */ - -/* Moves DMA channel to the FAULT state and updates it's status. */ - -static inline void pl330_fault(PL330Chan *ch, uint32_t flags) -{ - DB_PRINT("ch: %p, flags: %" PRIx32 "\n", ch, flags); - ch->fault_type |= flags; - if (ch->state == pl330_chan_fault) { - return; - } - ch->state = pl330_chan_fault; - ch->parent->num_faulting++; - if (ch->parent->num_faulting == 1) { - DB_PRINT("abort interrupt raised\n"); - qemu_irq_raise(ch->parent->irq_abort); - } -} - -/* - * For information about instructions see PL330 Technical Reference Manual. - * - * Arguments: - * CH - channel executing the instruction - * OPCODE - opcode - * ARGS - array of 8-bit arguments - * LEN - number of elements in ARGS array - */ - -static void pl330_dmaadxh(PL330Chan *ch, uint8_t *args, bool ra, bool neg) -{ - uint32_t im = (args[1] << 8) | args[0]; - if (neg) { - im |= 0xffffu << 16; - } - - if (ch->is_manager) { - pl330_fault(ch, PL330_FAULT_UNDEF_INSTR); - return; - } - if (ra) { - ch->dst += im; - } else { - ch->src += im; - } -} - -static void pl330_dmaaddh(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - pl330_dmaadxh(ch, args, extract32(opcode, 1, 1), false); -} - -static void pl330_dmaadnh(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - pl330_dmaadxh(ch, args, extract32(opcode, 1, 1), true); -} - -static void pl330_dmaend(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - PL330State *s = ch->parent; - - if (ch->state == pl330_chan_executing && !ch->is_manager) { - /* Wait for all transfers to complete */ - if (pl330_fifo_has_tag(&s->fifo, ch->tag) || - pl330_queue_find_insn(&s->read_queue, ch->tag, false) != NULL || - pl330_queue_find_insn(&s->write_queue, ch->tag, false) != NULL) { - - ch->stall = 1; - return; - } - } - DB_PRINT("DMA ending!\n"); - pl330_fifo_tagged_remove(&s->fifo, ch->tag); - pl330_queue_remove_tagged(&s->read_queue, ch->tag); - pl330_queue_remove_tagged(&s->write_queue, ch->tag); - ch->state = pl330_chan_stopped; -} - -static void pl330_dmaflushp(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - uint8_t periph_id; - - if (args[0] & 7) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - periph_id = (args[0] >> 3) & 0x1f; - if (periph_id >= ch->parent->num_periph_req) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) { - pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR); - return; - } - /* Do nothing */ -} - -static void pl330_dmago(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - uint8_t chan_id; - uint8_t ns; - uint32_t pc; - PL330Chan *s; - - DB_PRINT("\n"); - - if (!ch->is_manager) { - pl330_fault(ch, PL330_FAULT_UNDEF_INSTR); - return; - } - ns = !!(opcode & 2); - chan_id = args[0] & 7; - if ((args[0] >> 3)) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if (chan_id >= ch->parent->num_chnls) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - pc = (((uint32_t)args[4]) << 24) | (((uint32_t)args[3]) << 16) | - (((uint32_t)args[2]) << 8) | (((uint32_t)args[1])); - if (ch->parent->chan[chan_id].state != pl330_chan_stopped) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if (ch->ns && !ns) { - pl330_fault(ch, PL330_FAULT_DMAGO_ERR); - return; - } - s = &ch->parent->chan[chan_id]; - s->ns = ns; - s->pc = pc; - s->state = pl330_chan_executing; -} - -static void pl330_dmald(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - uint8_t bs = opcode & 3; - uint32_t size, num; - bool inc; - - if (bs == 2) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if ((bs == 1 && ch->request_flag == PL330_BURST) || - (bs == 3 && ch->request_flag == PL330_SINGLE)) { - /* Perform NOP */ - return; - } - if (bs == 1 && ch->request_flag == PL330_SINGLE) { - num = 1; - } else { - num = ((ch->control >> 4) & 0xf) + 1; - } - size = (uint32_t)1 << ((ch->control >> 1) & 0x7); - inc = !!(ch->control & 1); - ch->stall = pl330_queue_put_insn(&ch->parent->read_queue, ch->src, - size, num, inc, 0, ch->tag); - if (!ch->stall) { - DB_PRINT("channel:%" PRId8 " address:%08" PRIx32 " size:%" PRIx32 - " num:%" PRId32 " %c\n", - ch->tag, ch->src, size, num, inc ? 'Y' : 'N'); - ch->src += inc ? size * num - (ch->src & (size - 1)) : 0; - } -} - -static void pl330_dmaldp(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - uint8_t periph_id; - - if (args[0] & 7) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - periph_id = (args[0] >> 3) & 0x1f; - if (periph_id >= ch->parent->num_periph_req) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) { - pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR); - return; - } - pl330_dmald(ch, opcode, args, len); -} - -static void pl330_dmalp(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - uint8_t lc = (opcode & 2) >> 1; - - ch->lc[lc] = args[0]; -} - -static void pl330_dmakill(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - if (ch->state == pl330_chan_fault || - ch->state == pl330_chan_fault_completing) { - /* This is the only way for a channel to leave the faulting state */ - ch->fault_type = 0; - ch->parent->num_faulting--; - if (ch->parent->num_faulting == 0) { - DB_PRINT("abort interrupt lowered\n"); - qemu_irq_lower(ch->parent->irq_abort); - } - } - ch->state = pl330_chan_killing; - pl330_fifo_tagged_remove(&ch->parent->fifo, ch->tag); - pl330_queue_remove_tagged(&ch->parent->read_queue, ch->tag); - pl330_queue_remove_tagged(&ch->parent->write_queue, ch->tag); - ch->state = pl330_chan_stopped; -} - -static void pl330_dmalpend(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - uint8_t nf = (opcode & 0x10) >> 4; - uint8_t bs = opcode & 3; - uint8_t lc = (opcode & 4) >> 2; - - if (bs == 2) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if ((bs == 1 && ch->request_flag == PL330_BURST) || - (bs == 3 && ch->request_flag == PL330_SINGLE)) { - /* Perform NOP */ - return; - } - if (!nf || ch->lc[lc]) { - if (nf) { - ch->lc[lc]--; - } - DB_PRINT("loop reiteration\n"); - ch->pc -= args[0]; - ch->pc -= len + 1; - /* "ch->pc -= args[0] + len + 1" is incorrect when args[0] == 256 */ - } else { - DB_PRINT("loop fallthrough\n"); - } -} - - -static void pl330_dmamov(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - uint8_t rd = args[0] & 7; - uint32_t im; - - if ((args[0] >> 3)) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - im = (((uint32_t)args[4]) << 24) | (((uint32_t)args[3]) << 16) | - (((uint32_t)args[2]) << 8) | (((uint32_t)args[1])); - switch (rd) { - case 0: - ch->src = im; - break; - case 1: - ch->control = im; - break; - case 2: - ch->dst = im; - break; - default: - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } -} - -static void pl330_dmanop(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - /* NOP is NOP. */ -} - -static void pl330_dmarmb(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - if (pl330_queue_find_insn(&ch->parent->read_queue, ch->tag, false)) { - ch->state = pl330_chan_at_barrier; - ch->stall = 1; - return; - } else { - ch->state = pl330_chan_executing; - } -} - -static void pl330_dmasev(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - uint8_t ev_id; - - if (args[0] & 7) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - ev_id = (args[0] >> 3) & 0x1f; - if (ev_id >= ch->parent->num_events) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if (ch->ns && !(ch->parent->cfg[CFG_INS] & (1 << ev_id))) { - pl330_fault(ch, PL330_FAULT_EVENT_ERR); - return; - } - if (ch->parent->inten & (1 << ev_id)) { - ch->parent->int_status |= (1 << ev_id); - DB_PRINT("event interrupt raised %" PRId8 "\n", ev_id); - qemu_irq_raise(ch->parent->irq[ev_id]); - } - DB_PRINT("event raised %" PRId8 "\n", ev_id); - ch->parent->ev_status |= (1 << ev_id); -} - -static void pl330_dmast(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len) -{ - uint8_t bs = opcode & 3; - uint32_t size, num; - bool inc; - - if (bs == 2) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if ((bs == 1 && ch->request_flag == PL330_BURST) || - (bs == 3 && ch->request_flag == PL330_SINGLE)) { - /* Perform NOP */ - return; - } - num = ((ch->control >> 18) & 0xf) + 1; - size = (uint32_t)1 << ((ch->control >> 15) & 0x7); - inc = !!((ch->control >> 14) & 1); - ch->stall = pl330_queue_put_insn(&ch->parent->write_queue, ch->dst, - size, num, inc, 0, ch->tag); - if (!ch->stall) { - DB_PRINT("channel:%" PRId8 " address:%08" PRIx32 " size:%" PRIx32 - " num:%" PRId32 " %c\n", - ch->tag, ch->dst, size, num, inc ? 'Y' : 'N'); - ch->dst += inc ? size * num - (ch->dst & (size - 1)) : 0; - } -} - -static void pl330_dmastp(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - uint8_t periph_id; - - if (args[0] & 7) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - periph_id = (args[0] >> 3) & 0x1f; - if (periph_id >= ch->parent->num_periph_req) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) { - pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR); - return; - } - pl330_dmast(ch, opcode, args, len); -} - -static void pl330_dmastz(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - uint32_t size, num; - bool inc; - - num = ((ch->control >> 18) & 0xf) + 1; - size = (uint32_t)1 << ((ch->control >> 15) & 0x7); - inc = !!((ch->control >> 14) & 1); - ch->stall = pl330_queue_put_insn(&ch->parent->write_queue, ch->dst, - size, num, inc, 1, ch->tag); - if (inc) { - ch->dst += size * num; - } -} - -static void pl330_dmawfe(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - uint8_t ev_id; - int i; - - if (args[0] & 5) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - ev_id = (args[0] >> 3) & 0x1f; - if (ev_id >= ch->parent->num_events) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if (ch->ns && !(ch->parent->cfg[CFG_INS] & (1 << ev_id))) { - pl330_fault(ch, PL330_FAULT_EVENT_ERR); - return; - } - ch->wakeup = ev_id; - ch->state = pl330_chan_waiting_event; - if (~ch->parent->inten & ch->parent->ev_status & 1 << ev_id) { - ch->state = pl330_chan_executing; - /* If anyone else is currently waiting on the same event, let them - * clear the ev_status so they pick up event as well - */ - for (i = 0; i < ch->parent->num_chnls; ++i) { - PL330Chan *peer = &ch->parent->chan[i]; - if (peer->state == pl330_chan_waiting_event && - peer->wakeup == ev_id) { - return; - } - } - ch->parent->ev_status &= ~(1 << ev_id); - DB_PRINT("event lowered %" PRIx8 "\n", ev_id); - } else { - ch->stall = 1; - } -} - -static void pl330_dmawfp(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - uint8_t bs = opcode & 3; - uint8_t periph_id; - - if (args[0] & 7) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - periph_id = (args[0] >> 3) & 0x1f; - if (periph_id >= ch->parent->num_periph_req) { - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) { - pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR); - return; - } - switch (bs) { - case 0: /* S */ - ch->request_flag = PL330_SINGLE; - ch->wfp_sbp = 0; - break; - case 1: /* P */ - ch->request_flag = PL330_BURST; - ch->wfp_sbp = 2; - break; - case 2: /* B */ - ch->request_flag = PL330_BURST; - ch->wfp_sbp = 1; - break; - default: - pl330_fault(ch, PL330_FAULT_OPERAND_INVALID); - return; - } - - if (ch->parent->periph_busy[periph_id]) { - ch->state = pl330_chan_waiting_periph; - ch->stall = 1; - } else if (ch->state == pl330_chan_waiting_periph) { - ch->state = pl330_chan_executing; - } -} - -static void pl330_dmawmb(PL330Chan *ch, uint8_t opcode, - uint8_t *args, int len) -{ - if (pl330_queue_find_insn(&ch->parent->write_queue, ch->tag, false)) { - ch->state = pl330_chan_at_barrier; - ch->stall = 1; - return; - } else { - ch->state = pl330_chan_executing; - } -} - -/* NULL terminated array of the instruction descriptions. */ -static const PL330InsnDesc insn_desc[] = { - { .opcode = 0x54, .opmask = 0xFD, .size = 3, .exec = pl330_dmaaddh, }, - { .opcode = 0x5c, .opmask = 0xFD, .size = 3, .exec = pl330_dmaadnh, }, - { .opcode = 0x00, .opmask = 0xFF, .size = 1, .exec = pl330_dmaend, }, - { .opcode = 0x35, .opmask = 0xFF, .size = 2, .exec = pl330_dmaflushp, }, - { .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago, }, - { .opcode = 0x04, .opmask = 0xFC, .size = 1, .exec = pl330_dmald, }, - { .opcode = 0x25, .opmask = 0xFD, .size = 2, .exec = pl330_dmaldp, }, - { .opcode = 0x20, .opmask = 0xFD, .size = 2, .exec = pl330_dmalp, }, - /* dmastp must be before dmalpend in this list, because their maps - * are overlapping - */ - { .opcode = 0x29, .opmask = 0xFD, .size = 2, .exec = pl330_dmastp, }, - { .opcode = 0x28, .opmask = 0xE8, .size = 2, .exec = pl330_dmalpend, }, - { .opcode = 0x01, .opmask = 0xFF, .size = 1, .exec = pl330_dmakill, }, - { .opcode = 0xBC, .opmask = 0xFF, .size = 6, .exec = pl330_dmamov, }, - { .opcode = 0x18, .opmask = 0xFF, .size = 1, .exec = pl330_dmanop, }, - { .opcode = 0x12, .opmask = 0xFF, .size = 1, .exec = pl330_dmarmb, }, - { .opcode = 0x34, .opmask = 0xFF, .size = 2, .exec = pl330_dmasev, }, - { .opcode = 0x08, .opmask = 0xFC, .size = 1, .exec = pl330_dmast, }, - { .opcode = 0x0C, .opmask = 0xFF, .size = 1, .exec = pl330_dmastz, }, - { .opcode = 0x36, .opmask = 0xFF, .size = 2, .exec = pl330_dmawfe, }, - { .opcode = 0x30, .opmask = 0xFC, .size = 2, .exec = pl330_dmawfp, }, - { .opcode = 0x13, .opmask = 0xFF, .size = 1, .exec = pl330_dmawmb, }, - { .opcode = 0x00, .opmask = 0x00, .size = 0, .exec = NULL, } -}; - -/* Instructions which can be issued via debug registers. */ -static const PL330InsnDesc debug_insn_desc[] = { - { .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago, }, - { .opcode = 0x01, .opmask = 0xFF, .size = 1, .exec = pl330_dmakill, }, - { .opcode = 0x34, .opmask = 0xFF, .size = 2, .exec = pl330_dmasev, }, - { .opcode = 0x00, .opmask = 0x00, .size = 0, .exec = NULL, } -}; - -static inline const PL330InsnDesc *pl330_fetch_insn(PL330Chan *ch) -{ - uint8_t opcode; - int i; - - dma_memory_read(&address_space_memory, ch->pc, &opcode, 1); - for (i = 0; insn_desc[i].size; i++) { - if ((opcode & insn_desc[i].opmask) == insn_desc[i].opcode) { - return &insn_desc[i]; - } - } - return NULL; -} - -static inline void pl330_exec_insn(PL330Chan *ch, const PL330InsnDesc *insn) -{ - uint8_t buf[PL330_INSN_MAXSIZE]; - - assert(insn->size <= PL330_INSN_MAXSIZE); - dma_memory_read(&address_space_memory, ch->pc, buf, insn->size); - insn->exec(ch, buf[0], &buf[1], insn->size - 1); -} - -static inline void pl330_update_pc(PL330Chan *ch, - const PL330InsnDesc *insn) -{ - ch->pc += insn->size; -} - -/* Try to execute current instruction in channel CH. Number of executed - instructions is returned (0 or 1). */ -static int pl330_chan_exec(PL330Chan *ch) -{ - const PL330InsnDesc *insn; - - if (ch->state != pl330_chan_executing && - ch->state != pl330_chan_waiting_periph && - ch->state != pl330_chan_at_barrier && - ch->state != pl330_chan_waiting_event) { - return 0; - } - ch->stall = 0; - insn = pl330_fetch_insn(ch); - if (!insn) { - DB_PRINT("pl330 undefined instruction\n"); - pl330_fault(ch, PL330_FAULT_UNDEF_INSTR); - return 0; - } - pl330_exec_insn(ch, insn); - if (!ch->stall) { - pl330_update_pc(ch, insn); - ch->watchdog_timer = 0; - return 1; - /* WDT only active in exec state */ - } else if (ch->state == pl330_chan_executing) { - ch->watchdog_timer++; - if (ch->watchdog_timer >= PL330_WATCHDOG_LIMIT) { - pl330_fault(ch, PL330_FAULT_LOCKUP_ERR); - } - } - return 0; -} - -/* Try to execute 1 instruction in each channel, one instruction from read - queue and one instruction from write queue. Number of successfully executed - instructions is returned. */ -static int pl330_exec_cycle(PL330Chan *channel) -{ - PL330State *s = channel->parent; - PL330QueueEntry *q; - int i; - int num_exec = 0; - int fifo_res = 0; - uint8_t buf[PL330_MAX_BURST_LEN]; - - /* Execute one instruction in each channel */ - num_exec += pl330_chan_exec(channel); - - /* Execute one instruction from read queue */ - q = pl330_queue_find_insn(&s->read_queue, PL330_UNTAGGED, true); - if (q != NULL && q->len <= pl330_fifo_num_free(&s->fifo)) { - int len = q->len - (q->addr & (q->len - 1)); - - dma_memory_read(&address_space_memory, q->addr, buf, len); - if (PL330_ERR_DEBUG > 1) { - DB_PRINT("PL330 read from memory @%08" PRIx32 " (size = %08x):\n", - q->addr, len); - qemu_hexdump((char *)buf, stderr, "", len); - } - fifo_res = pl330_fifo_push(&s->fifo, buf, len, q->tag); - if (fifo_res == PL330_FIFO_OK) { - if (q->inc) { - q->addr += len; - } - q->n--; - if (!q->n) { - pl330_queue_remove_insn(&s->read_queue, q); - } - num_exec++; - } - } - - /* Execute one instruction from write queue. */ - q = pl330_queue_find_insn(&s->write_queue, pl330_fifo_tag(&s->fifo), true); - if (q != NULL) { - int len = q->len - (q->addr & (q->len - 1)); - - if (q->z) { - for (i = 0; i < len; i++) { - buf[i] = 0; - } - } else { - fifo_res = pl330_fifo_get(&s->fifo, buf, len, q->tag); - } - if (fifo_res == PL330_FIFO_OK || q->z) { - dma_memory_write(&address_space_memory, q->addr, buf, len); - if (PL330_ERR_DEBUG > 1) { - DB_PRINT("PL330 read from memory @%08" PRIx32 - " (size = %08x):\n", q->addr, len); - qemu_hexdump((char *)buf, stderr, "", len); - } - if (q->inc) { - q->addr += len; - } - num_exec++; - } else if (fifo_res == PL330_FIFO_STALL) { - pl330_fault(&channel->parent->chan[q->tag], - PL330_FAULT_FIFOEMPTY_ERR); - } - q->n--; - if (!q->n) { - pl330_queue_remove_insn(&s->write_queue, q); - } - } - - return num_exec; -} - -static int pl330_exec_channel(PL330Chan *channel) -{ - int insr_exec = 0; - - /* TODO: Is it all right to execute everything or should we do per-cycle - simulation? */ - while (pl330_exec_cycle(channel)) { - insr_exec++; - } - - /* Detect deadlock */ - if (channel->state == pl330_chan_executing) { - pl330_fault(channel, PL330_FAULT_LOCKUP_ERR); - } - /* Situation when one of the queues has deadlocked but all channels - * have finished their programs should be impossible. - */ - - return insr_exec; -} - -static inline void pl330_exec(PL330State *s) -{ - DB_PRINT("\n"); - int i, insr_exec; - do { - insr_exec = pl330_exec_channel(&s->manager); - - for (i = 0; i < s->num_chnls; i++) { - insr_exec += pl330_exec_channel(&s->chan[i]); - } - } while (insr_exec); -} - -static void pl330_exec_cycle_timer(void *opaque) -{ - PL330State *s = (PL330State *)opaque; - pl330_exec(s); -} - -/* Stop or restore dma operations */ - -static void pl330_dma_stop_irq(void *opaque, int irq, int level) -{ - PL330State *s = (PL330State *)opaque; - - if (s->periph_busy[irq] != level) { - s->periph_busy[irq] = level; - timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - } -} - -static void pl330_debug_exec(PL330State *s) -{ - uint8_t args[5]; - uint8_t opcode; - uint8_t chan_id; - int i; - PL330Chan *ch; - const PL330InsnDesc *insn; - - s->debug_status = 1; - chan_id = (s->dbg[0] >> 8) & 0x07; - opcode = (s->dbg[0] >> 16) & 0xff; - args[0] = (s->dbg[0] >> 24) & 0xff; - args[1] = (s->dbg[1] >> 0) & 0xff; - args[2] = (s->dbg[1] >> 8) & 0xff; - args[3] = (s->dbg[1] >> 16) & 0xff; - args[4] = (s->dbg[1] >> 24) & 0xff; - DB_PRINT("chan id: %" PRIx8 "\n", chan_id); - if (s->dbg[0] & 1) { - ch = &s->chan[chan_id]; - } else { - ch = &s->manager; - } - insn = NULL; - for (i = 0; debug_insn_desc[i].size; i++) { - if ((opcode & debug_insn_desc[i].opmask) == debug_insn_desc[i].opcode) { - insn = &debug_insn_desc[i]; - } - } - if (!insn) { - pl330_fault(ch, PL330_FAULT_UNDEF_INSTR | PL330_FAULT_DBG_INSTR); - return ; - } - ch->stall = 0; - insn->exec(ch, opcode, args, insn->size - 1); - if (ch->fault_type) { - ch->fault_type |= PL330_FAULT_DBG_INSTR; - } - if (ch->stall) { - qemu_log_mask(LOG_UNIMP, "pl330: stall of debug instruction not " - "implemented\n"); - } - s->debug_status = 0; -} - -/* IOMEM mapped registers */ - -static void pl330_iomem_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PL330State *s = (PL330State *) opaque; - int i; - - DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)value); - - switch (offset) { - case PL330_REG_INTEN: - s->inten = value; - break; - case PL330_REG_INTCLR: - for (i = 0; i < s->num_events; i++) { - if (s->int_status & s->inten & value & (1 << i)) { - DB_PRINT("event interrupt lowered %d\n", i); - qemu_irq_lower(s->irq[i]); - } - } - s->ev_status &= ~(value & s->inten); - s->int_status &= ~(value & s->inten); - break; - case PL330_REG_DBGCMD: - if ((value & 3) == 0) { - pl330_debug_exec(s); - pl330_exec(s); - } else { - qemu_log_mask(LOG_GUEST_ERROR, "pl330: write of illegal value %u " - "for offset " TARGET_FMT_plx "\n", (unsigned)value, - offset); - } - break; - case PL330_REG_DBGINST0: - DB_PRINT("s->dbg[0] = %08x\n", (unsigned)value); - s->dbg[0] = value; - break; - case PL330_REG_DBGINST1: - DB_PRINT("s->dbg[1] = %08x\n", (unsigned)value); - s->dbg[1] = value; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad write offset " TARGET_FMT_plx - "\n", offset); - break; - } -} - -static inline uint32_t pl330_iomem_read_imp(void *opaque, - hwaddr offset) -{ - PL330State *s = (PL330State *)opaque; - int chan_id; - int i; - uint32_t res; - - if (offset >= PL330_REG_PERIPH_ID && offset < PL330_REG_PERIPH_ID + 32) { - return pl330_id[(offset - PL330_REG_PERIPH_ID) >> 2]; - } - if (offset >= PL330_REG_CR0_BASE && offset < PL330_REG_CR0_BASE + 24) { - return s->cfg[(offset - PL330_REG_CR0_BASE) >> 2]; - } - if (offset >= PL330_REG_CHANCTRL && offset < PL330_REG_DBGSTATUS) { - offset -= PL330_REG_CHANCTRL; - chan_id = offset >> 5; - if (chan_id >= s->num_chnls) { - qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " - TARGET_FMT_plx "\n", offset); - return 0; - } - switch (offset & 0x1f) { - case 0x00: - return s->chan[chan_id].src; - case 0x04: - return s->chan[chan_id].dst; - case 0x08: - return s->chan[chan_id].control; - case 0x0C: - return s->chan[chan_id].lc[0]; - case 0x10: - return s->chan[chan_id].lc[1]; - default: - qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " - TARGET_FMT_plx "\n", offset); - return 0; - } - } - if (offset >= PL330_REG_CSR_BASE && offset < 0x400) { - offset -= PL330_REG_CSR_BASE; - chan_id = offset >> 3; - if (chan_id >= s->num_chnls) { - qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " - TARGET_FMT_plx "\n", offset); - return 0; - } - switch ((offset >> 2) & 1) { - case 0x0: - res = (s->chan[chan_id].ns << 21) | - (s->chan[chan_id].wakeup << 4) | - (s->chan[chan_id].state) | - (s->chan[chan_id].wfp_sbp << 14); - return res; - case 0x1: - return s->chan[chan_id].pc; - default: - qemu_log_mask(LOG_GUEST_ERROR, "pl330: read error\n"); - return 0; - } - } - if (offset >= PL330_REG_FTR_BASE && offset < 0x100) { - offset -= PL330_REG_FTR_BASE; - chan_id = offset >> 2; - if (chan_id >= s->num_chnls) { - qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " - TARGET_FMT_plx "\n", offset); - return 0; - } - return s->chan[chan_id].fault_type; - } - switch (offset) { - case PL330_REG_DSR: - return (s->manager.ns << 9) | (s->manager.wakeup << 4) | - (s->manager.state & 0xf); - case PL330_REG_DPC: - return s->manager.pc; - case PL330_REG_INTEN: - return s->inten; - case PL330_REG_INT_EVENT_RIS: - return s->ev_status; - case PL330_REG_INTMIS: - return s->int_status; - case PL330_REG_INTCLR: - /* Documentation says that we can't read this register - * but linux kernel does it - */ - return 0; - case PL330_REG_FSRD: - return s->manager.state ? 1 : 0; - case PL330_REG_FSRC: - res = 0; - for (i = 0; i < s->num_chnls; i++) { - if (s->chan[i].state == pl330_chan_fault || - s->chan[i].state == pl330_chan_fault_completing) { - res |= 1 << i; - } - } - return res; - case PL330_REG_FTRD: - return s->manager.fault_type; - case PL330_REG_DBGSTATUS: - return s->debug_status; - default: - qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset " - TARGET_FMT_plx "\n", offset); - } - return 0; -} - -static uint64_t pl330_iomem_read(void *opaque, hwaddr offset, - unsigned size) -{ - uint32_t ret = pl330_iomem_read_imp(opaque, offset); - DB_PRINT("addr: %08" HWADDR_PRIx " data: %08" PRIx32 "\n", offset, ret); - return ret; -} - -static const MemoryRegionOps pl330_ops = { - .read = pl330_iomem_read, - .write = pl330_iomem_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - } -}; - -/* Controller logic and initialization */ - -static void pl330_chan_reset(PL330Chan *ch) -{ - ch->src = 0; - ch->dst = 0; - ch->pc = 0; - ch->state = pl330_chan_stopped; - ch->watchdog_timer = 0; - ch->stall = 0; - ch->control = 0; - ch->status = 0; - ch->fault_type = 0; -} - -static void pl330_reset(DeviceState *d) -{ - int i; - PL330State *s = PL330(d); - - s->inten = 0; - s->int_status = 0; - s->ev_status = 0; - s->debug_status = 0; - s->num_faulting = 0; - s->manager.ns = s->mgr_ns_at_rst; - pl330_fifo_reset(&s->fifo); - pl330_queue_reset(&s->read_queue); - pl330_queue_reset(&s->write_queue); - - for (i = 0; i < s->num_chnls; i++) { - pl330_chan_reset(&s->chan[i]); - } - for (i = 0; i < s->num_periph_req; i++) { - s->periph_busy[i] = 0; - } - - timer_del(s->timer); -} - -static void pl330_realize(DeviceState *dev, Error **errp) -{ - int i; - PL330State *s = PL330(dev); - - sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq_abort); - memory_region_init_io(&s->iomem, OBJECT(s), &pl330_ops, s, - "dma", PL330_IOMEM_SIZE); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); - - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pl330_exec_cycle_timer, s); - - s->cfg[0] = (s->mgr_ns_at_rst ? 0x4 : 0) | - (s->num_periph_req > 0 ? 1 : 0) | - ((s->num_chnls - 1) & 0x7) << 4 | - ((s->num_periph_req - 1) & 0x1f) << 12 | - ((s->num_events - 1) & 0x1f) << 17; - - switch (s->i_cache_len) { - case (4): - s->cfg[1] |= 2; - break; - case (8): - s->cfg[1] |= 3; - break; - case (16): - s->cfg[1] |= 4; - break; - case (32): - s->cfg[1] |= 5; - break; - default: - error_setg(errp, "Bad value for i-cache_len property: %" PRIx8, - s->i_cache_len); - return; - } - s->cfg[1] |= ((s->num_i_cache_lines - 1) & 0xf) << 4; - - s->chan = g_new0(PL330Chan, s->num_chnls); - s->hi_seqn = g_new0(uint8_t, s->num_chnls); - s->lo_seqn = g_new0(uint8_t, s->num_chnls); - for (i = 0; i < s->num_chnls; i++) { - s->chan[i].parent = s; - s->chan[i].tag = (uint8_t)i; - } - s->manager.parent = s; - s->manager.tag = s->num_chnls; - s->manager.is_manager = true; - - s->irq = g_new0(qemu_irq, s->num_events); - for (i = 0; i < s->num_events; i++) { - sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]); - } - - qdev_init_gpio_in(dev, pl330_dma_stop_irq, PL330_PERIPH_NUM); - - switch (s->data_width) { - case (32): - s->cfg[CFG_CRD] |= 0x2; - break; - case (64): - s->cfg[CFG_CRD] |= 0x3; - break; - case (128): - s->cfg[CFG_CRD] |= 0x4; - break; - default: - error_setg(errp, "Bad value for data_width property: %" PRIx8, - s->data_width); - return; - } - - s->cfg[CFG_CRD] |= ((s->wr_cap - 1) & 0x7) << 4 | - ((s->wr_q_dep - 1) & 0xf) << 8 | - ((s->rd_cap - 1) & 0x7) << 12 | - ((s->rd_q_dep - 1) & 0xf) << 16 | - ((s->data_buffer_dep - 1) & 0x1ff) << 20; - - pl330_queue_init(&s->read_queue, s->rd_q_dep, s); - pl330_queue_init(&s->write_queue, s->wr_q_dep, s); - pl330_fifo_init(&s->fifo, s->data_width / 4 * s->data_buffer_dep); -} - -static Property pl330_properties[] = { - /* CR0 */ - DEFINE_PROP_UINT32("num_chnls", PL330State, num_chnls, 8), - DEFINE_PROP_UINT8("num_periph_req", PL330State, num_periph_req, 4), - DEFINE_PROP_UINT8("num_events", PL330State, num_events, 16), - DEFINE_PROP_UINT8("mgr_ns_at_rst", PL330State, mgr_ns_at_rst, 0), - /* CR1 */ - DEFINE_PROP_UINT8("i-cache_len", PL330State, i_cache_len, 4), - DEFINE_PROP_UINT8("num_i-cache_lines", PL330State, num_i_cache_lines, 8), - /* CR2-4 */ - DEFINE_PROP_UINT32("boot_addr", PL330State, cfg[CFG_BOOT_ADDR], 0), - DEFINE_PROP_UINT32("INS", PL330State, cfg[CFG_INS], 0), - DEFINE_PROP_UINT32("PNS", PL330State, cfg[CFG_PNS], 0), - /* CRD */ - DEFINE_PROP_UINT8("data_width", PL330State, data_width, 64), - DEFINE_PROP_UINT8("wr_cap", PL330State, wr_cap, 8), - DEFINE_PROP_UINT8("wr_q_dep", PL330State, wr_q_dep, 16), - DEFINE_PROP_UINT8("rd_cap", PL330State, rd_cap, 8), - DEFINE_PROP_UINT8("rd_q_dep", PL330State, rd_q_dep, 16), - DEFINE_PROP_UINT16("data_buffer_dep", PL330State, data_buffer_dep, 256), - - DEFINE_PROP_END_OF_LIST(), -}; - -static void pl330_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = pl330_realize; - dc->reset = pl330_reset; - dc->props = pl330_properties; - dc->vmsd = &vmstate_pl330; -} - -static const TypeInfo pl330_type_info = { - .name = TYPE_PL330, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PL330State), - .class_init = pl330_class_init, -}; - -static void pl330_register_types(void) -{ - type_register_static(&pl330_type_info); -} - -type_init(pl330_register_types) diff --git a/qemu/hw/dma/puv3_dma.c b/qemu/hw/dma/puv3_dma.c deleted file mode 100644 index b97a6c176..000000000 --- a/qemu/hw/dma/puv3_dma.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * DMA device simulation in PKUnity SoC - * - * Copyright (C) 2010-2012 Guan Xuetao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation, 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" - -#undef DEBUG_PUV3 -#include "hw/unicore32/puv3.h" - -#define PUV3_DMA_CH_NR (6) -#define PUV3_DMA_CH_MASK (0xff) -#define PUV3_DMA_CH(offset) ((offset) >> 8) - -#define TYPE_PUV3_DMA "puv3_dma" -#define PUV3_DMA(obj) OBJECT_CHECK(PUV3DMAState, (obj), TYPE_PUV3_DMA) - -typedef struct PUV3DMAState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t reg_CFG[PUV3_DMA_CH_NR]; -} PUV3DMAState; - -static uint64_t puv3_dma_read(void *opaque, hwaddr offset, - unsigned size) -{ - PUV3DMAState *s = opaque; - uint32_t ret = 0; - - assert(PUV3_DMA_CH(offset) < PUV3_DMA_CH_NR); - - switch (offset & PUV3_DMA_CH_MASK) { - case 0x10: - ret = s->reg_CFG[PUV3_DMA_CH(offset)]; - break; - default: - DPRINTF("Bad offset 0x%x\n", offset); - } - DPRINTF("offset 0x%x, value 0x%x\n", offset, ret); - - return ret; -} - -static void puv3_dma_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PUV3DMAState *s = opaque; - - assert(PUV3_DMA_CH(offset) < PUV3_DMA_CH_NR); - - switch (offset & PUV3_DMA_CH_MASK) { - case 0x10: - s->reg_CFG[PUV3_DMA_CH(offset)] = value; - break; - default: - DPRINTF("Bad offset 0x%x\n", offset); - } - DPRINTF("offset 0x%x, value 0x%x\n", offset, value); -} - -static const MemoryRegionOps puv3_dma_ops = { - .read = puv3_dma_read, - .write = puv3_dma_write, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int puv3_dma_init(SysBusDevice *dev) -{ - PUV3DMAState *s = PUV3_DMA(dev); - int i; - - for (i = 0; i < PUV3_DMA_CH_NR; i++) { - s->reg_CFG[i] = 0x0; - } - - memory_region_init_io(&s->iomem, OBJECT(s), &puv3_dma_ops, s, "puv3_dma", - PUV3_REGS_OFFSET); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static void puv3_dma_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = puv3_dma_init; -} - -static const TypeInfo puv3_dma_info = { - .name = TYPE_PUV3_DMA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PUV3DMAState), - .class_init = puv3_dma_class_init, -}; - -static void puv3_dma_register_type(void) -{ - type_register_static(&puv3_dma_info); -} - -type_init(puv3_dma_register_type) diff --git a/qemu/hw/dma/pxa2xx_dma.c b/qemu/hw/dma/pxa2xx_dma.c deleted file mode 100644 index 2306abc35..000000000 --- a/qemu/hw/dma/pxa2xx_dma.c +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Intel XScale PXA255/270 DMA controller. - * - * Copyright (c) 2006 Openedhand Ltd. - * Copyright (c) 2006 Thorsten Zitterell - * Written by Andrzej Zaborowski - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/arm/pxa.h" -#include "hw/sysbus.h" - -#define PXA255_DMA_NUM_CHANNELS 16 -#define PXA27X_DMA_NUM_CHANNELS 32 - -#define PXA2XX_DMA_NUM_REQUESTS 75 - -typedef struct { - uint32_t descr; - uint32_t src; - uint32_t dest; - uint32_t cmd; - uint32_t state; - int request; -} PXA2xxDMAChannel; - -#define TYPE_PXA2XX_DMA "pxa2xx-dma" -#define PXA2XX_DMA(obj) OBJECT_CHECK(PXA2xxDMAState, (obj), TYPE_PXA2XX_DMA) - -typedef struct PXA2xxDMAState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq irq; - - uint32_t stopintr; - uint32_t eorintr; - uint32_t rasintr; - uint32_t startintr; - uint32_t endintr; - - uint32_t align; - uint32_t pio; - - int channels; - PXA2xxDMAChannel *chan; - - uint8_t req[PXA2XX_DMA_NUM_REQUESTS]; - - /* Flag to avoid recursive DMA invocations. */ - int running; -} PXA2xxDMAState; - -#define DCSR0 0x0000 /* DMA Control / Status register for Channel 0 */ -#define DCSR31 0x007c /* DMA Control / Status register for Channel 31 */ -#define DALGN 0x00a0 /* DMA Alignment register */ -#define DPCSR 0x00a4 /* DMA Programmed I/O Control Status register */ -#define DRQSR0 0x00e0 /* DMA DREQ<0> Status register */ -#define DRQSR1 0x00e4 /* DMA DREQ<1> Status register */ -#define DRQSR2 0x00e8 /* DMA DREQ<2> Status register */ -#define DINT 0x00f0 /* DMA Interrupt register */ -#define DRCMR0 0x0100 /* Request to Channel Map register 0 */ -#define DRCMR63 0x01fc /* Request to Channel Map register 63 */ -#define D_CH0 0x0200 /* Channel 0 Descriptor start */ -#define DRCMR64 0x1100 /* Request to Channel Map register 64 */ -#define DRCMR74 0x1128 /* Request to Channel Map register 74 */ - -/* Per-channel register */ -#define DDADR 0x00 -#define DSADR 0x01 -#define DTADR 0x02 -#define DCMD 0x03 - -/* Bit-field masks */ -#define DRCMR_CHLNUM 0x1f -#define DRCMR_MAPVLD (1 << 7) -#define DDADR_STOP (1 << 0) -#define DDADR_BREN (1 << 1) -#define DCMD_LEN 0x1fff -#define DCMD_WIDTH(x) (1 << ((((x) >> 14) & 3) - 1)) -#define DCMD_SIZE(x) (4 << (((x) >> 16) & 3)) -#define DCMD_FLYBYT (1 << 19) -#define DCMD_FLYBYS (1 << 20) -#define DCMD_ENDIRQEN (1 << 21) -#define DCMD_STARTIRQEN (1 << 22) -#define DCMD_CMPEN (1 << 25) -#define DCMD_FLOWTRG (1 << 28) -#define DCMD_FLOWSRC (1 << 29) -#define DCMD_INCTRGADDR (1 << 30) -#define DCMD_INCSRCADDR (1 << 31) -#define DCSR_BUSERRINTR (1 << 0) -#define DCSR_STARTINTR (1 << 1) -#define DCSR_ENDINTR (1 << 2) -#define DCSR_STOPINTR (1 << 3) -#define DCSR_RASINTR (1 << 4) -#define DCSR_REQPEND (1 << 8) -#define DCSR_EORINT (1 << 9) -#define DCSR_CMPST (1 << 10) -#define DCSR_MASKRUN (1 << 22) -#define DCSR_RASIRQEN (1 << 23) -#define DCSR_CLRCMPST (1 << 24) -#define DCSR_SETCMPST (1 << 25) -#define DCSR_EORSTOPEN (1 << 26) -#define DCSR_EORJMPEN (1 << 27) -#define DCSR_EORIRQEN (1 << 28) -#define DCSR_STOPIRQEN (1 << 29) -#define DCSR_NODESCFETCH (1 << 30) -#define DCSR_RUN (1 << 31) - -static inline void pxa2xx_dma_update(PXA2xxDMAState *s, int ch) -{ - if (ch >= 0) { - if ((s->chan[ch].state & DCSR_STOPIRQEN) && - (s->chan[ch].state & DCSR_STOPINTR)) - s->stopintr |= 1 << ch; - else - s->stopintr &= ~(1 << ch); - - if ((s->chan[ch].state & DCSR_EORIRQEN) && - (s->chan[ch].state & DCSR_EORINT)) - s->eorintr |= 1 << ch; - else - s->eorintr &= ~(1 << ch); - - if ((s->chan[ch].state & DCSR_RASIRQEN) && - (s->chan[ch].state & DCSR_RASINTR)) - s->rasintr |= 1 << ch; - else - s->rasintr &= ~(1 << ch); - - if (s->chan[ch].state & DCSR_STARTINTR) - s->startintr |= 1 << ch; - else - s->startintr &= ~(1 << ch); - - if (s->chan[ch].state & DCSR_ENDINTR) - s->endintr |= 1 << ch; - else - s->endintr &= ~(1 << ch); - } - - if (s->stopintr | s->eorintr | s->rasintr | s->startintr | s->endintr) - qemu_irq_raise(s->irq); - else - qemu_irq_lower(s->irq); -} - -static inline void pxa2xx_dma_descriptor_fetch( - PXA2xxDMAState *s, int ch) -{ - uint32_t desc[4]; - hwaddr daddr = s->chan[ch].descr & ~0xf; - if ((s->chan[ch].descr & DDADR_BREN) && (s->chan[ch].state & DCSR_CMPST)) - daddr += 32; - - cpu_physical_memory_read(daddr, desc, 16); - s->chan[ch].descr = desc[DDADR]; - s->chan[ch].src = desc[DSADR]; - s->chan[ch].dest = desc[DTADR]; - s->chan[ch].cmd = desc[DCMD]; - - if (s->chan[ch].cmd & DCMD_FLOWSRC) - s->chan[ch].src &= ~3; - if (s->chan[ch].cmd & DCMD_FLOWTRG) - s->chan[ch].dest &= ~3; - - if (s->chan[ch].cmd & (DCMD_CMPEN | DCMD_FLYBYS | DCMD_FLYBYT)) - printf("%s: unsupported mode in channel %i\n", __FUNCTION__, ch); - - if (s->chan[ch].cmd & DCMD_STARTIRQEN) - s->chan[ch].state |= DCSR_STARTINTR; -} - -static void pxa2xx_dma_run(PXA2xxDMAState *s) -{ - int c, srcinc, destinc; - uint32_t n, size; - uint32_t width; - uint32_t length; - uint8_t buffer[32]; - PXA2xxDMAChannel *ch; - - if (s->running ++) - return; - - while (s->running) { - s->running = 1; - for (c = 0; c < s->channels; c ++) { - ch = &s->chan[c]; - - while ((ch->state & DCSR_RUN) && !(ch->state & DCSR_STOPINTR)) { - /* Test for pending requests */ - if ((ch->cmd & (DCMD_FLOWSRC | DCMD_FLOWTRG)) && !ch->request) - break; - - length = ch->cmd & DCMD_LEN; - size = DCMD_SIZE(ch->cmd); - width = DCMD_WIDTH(ch->cmd); - - srcinc = (ch->cmd & DCMD_INCSRCADDR) ? width : 0; - destinc = (ch->cmd & DCMD_INCTRGADDR) ? width : 0; - - while (length) { - size = MIN(length, size); - - for (n = 0; n < size; n += width) { - cpu_physical_memory_read(ch->src, buffer + n, width); - ch->src += srcinc; - } - - for (n = 0; n < size; n += width) { - cpu_physical_memory_write(ch->dest, buffer + n, width); - ch->dest += destinc; - } - - length -= size; - - if ((ch->cmd & (DCMD_FLOWSRC | DCMD_FLOWTRG)) && - !ch->request) { - ch->state |= DCSR_EORINT; - if (ch->state & DCSR_EORSTOPEN) - ch->state |= DCSR_STOPINTR; - if ((ch->state & DCSR_EORJMPEN) && - !(ch->state & DCSR_NODESCFETCH)) - pxa2xx_dma_descriptor_fetch(s, c); - break; - } - } - - ch->cmd = (ch->cmd & ~DCMD_LEN) | length; - - /* Is the transfer complete now? */ - if (!length) { - if (ch->cmd & DCMD_ENDIRQEN) - ch->state |= DCSR_ENDINTR; - - if ((ch->state & DCSR_NODESCFETCH) || - (ch->descr & DDADR_STOP) || - (ch->state & DCSR_EORSTOPEN)) { - ch->state |= DCSR_STOPINTR; - ch->state &= ~DCSR_RUN; - - break; - } - - ch->state |= DCSR_STOPINTR; - break; - } - } - } - - s->running --; - } -} - -static uint64_t pxa2xx_dma_read(void *opaque, hwaddr offset, - unsigned size) -{ - PXA2xxDMAState *s = (PXA2xxDMAState *) opaque; - unsigned int channel; - - if (size != 4) { - hw_error("%s: Bad access width\n", __FUNCTION__); - return 5; - } - - switch (offset) { - case DRCMR64 ... DRCMR74: - offset -= DRCMR64 - DRCMR0 - (64 << 2); - /* Fall through */ - case DRCMR0 ... DRCMR63: - channel = (offset - DRCMR0) >> 2; - return s->req[channel]; - - case DRQSR0: - case DRQSR1: - case DRQSR2: - return 0; - - case DCSR0 ... DCSR31: - channel = offset >> 2; - if (s->chan[channel].request) - return s->chan[channel].state | DCSR_REQPEND; - return s->chan[channel].state; - - case DINT: - return s->stopintr | s->eorintr | s->rasintr | - s->startintr | s->endintr; - - case DALGN: - return s->align; - - case DPCSR: - return s->pio; - } - - if (offset >= D_CH0 && offset < D_CH0 + (s->channels << 4)) { - channel = (offset - D_CH0) >> 4; - switch ((offset & 0x0f) >> 2) { - case DDADR: - return s->chan[channel].descr; - case DSADR: - return s->chan[channel].src; - case DTADR: - return s->chan[channel].dest; - case DCMD: - return s->chan[channel].cmd; - } - } - - hw_error("%s: Bad offset 0x" TARGET_FMT_plx "\n", __FUNCTION__, offset); - return 7; -} - -static void pxa2xx_dma_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxDMAState *s = (PXA2xxDMAState *) opaque; - unsigned int channel; - - if (size != 4) { - hw_error("%s: Bad access width\n", __FUNCTION__); - return; - } - - switch (offset) { - case DRCMR64 ... DRCMR74: - offset -= DRCMR64 - DRCMR0 - (64 << 2); - /* Fall through */ - case DRCMR0 ... DRCMR63: - channel = (offset - DRCMR0) >> 2; - - if (value & DRCMR_MAPVLD) - if ((value & DRCMR_CHLNUM) > s->channels) - hw_error("%s: Bad DMA channel %i\n", - __FUNCTION__, (unsigned)value & DRCMR_CHLNUM); - - s->req[channel] = value; - break; - - case DRQSR0: - case DRQSR1: - case DRQSR2: - /* Nothing to do */ - break; - - case DCSR0 ... DCSR31: - channel = offset >> 2; - s->chan[channel].state &= 0x0000071f & ~(value & - (DCSR_EORINT | DCSR_ENDINTR | - DCSR_STARTINTR | DCSR_BUSERRINTR)); - s->chan[channel].state |= value & 0xfc800000; - - if (s->chan[channel].state & DCSR_STOPIRQEN) - s->chan[channel].state &= ~DCSR_STOPINTR; - - if (value & DCSR_NODESCFETCH) { - /* No-descriptor-fetch mode */ - if (value & DCSR_RUN) { - s->chan[channel].state &= ~DCSR_STOPINTR; - pxa2xx_dma_run(s); - } - } else { - /* Descriptor-fetch mode */ - if (value & DCSR_RUN) { - s->chan[channel].state &= ~DCSR_STOPINTR; - pxa2xx_dma_descriptor_fetch(s, channel); - pxa2xx_dma_run(s); - } - } - - /* Shouldn't matter as our DMA is synchronous. */ - if (!(value & (DCSR_RUN | DCSR_MASKRUN))) - s->chan[channel].state |= DCSR_STOPINTR; - - if (value & DCSR_CLRCMPST) - s->chan[channel].state &= ~DCSR_CMPST; - if (value & DCSR_SETCMPST) - s->chan[channel].state |= DCSR_CMPST; - - pxa2xx_dma_update(s, channel); - break; - - case DALGN: - s->align = value; - break; - - case DPCSR: - s->pio = value & 0x80000001; - break; - - default: - if (offset >= D_CH0 && offset < D_CH0 + (s->channels << 4)) { - channel = (offset - D_CH0) >> 4; - switch ((offset & 0x0f) >> 2) { - case DDADR: - s->chan[channel].descr = value; - break; - case DSADR: - s->chan[channel].src = value; - break; - case DTADR: - s->chan[channel].dest = value; - break; - case DCMD: - s->chan[channel].cmd = value; - break; - default: - goto fail; - } - - break; - } - fail: - hw_error("%s: Bad offset " TARGET_FMT_plx "\n", __FUNCTION__, offset); - } -} - -static const MemoryRegionOps pxa2xx_dma_ops = { - .read = pxa2xx_dma_read, - .write = pxa2xx_dma_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pxa2xx_dma_request(void *opaque, int req_num, int on) -{ - PXA2xxDMAState *s = opaque; - int ch; - if (req_num < 0 || req_num >= PXA2XX_DMA_NUM_REQUESTS) - hw_error("%s: Bad DMA request %i\n", __FUNCTION__, req_num); - - if (!(s->req[req_num] & DRCMR_MAPVLD)) - return; - ch = s->req[req_num] & DRCMR_CHLNUM; - - if (!s->chan[ch].request && on) - s->chan[ch].state |= DCSR_RASINTR; - else - s->chan[ch].state &= ~DCSR_RASINTR; - if (s->chan[ch].request && !on) - s->chan[ch].state |= DCSR_EORINT; - - s->chan[ch].request = on; - if (on) { - pxa2xx_dma_run(s); - pxa2xx_dma_update(s, ch); - } -} - -static int pxa2xx_dma_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - PXA2xxDMAState *s = PXA2XX_DMA(dev); - int i; - - if (s->channels <= 0) { - return -1; - } - - s->chan = g_new0(PXA2xxDMAChannel, s->channels); - - for (i = 0; i < s->channels; i ++) - s->chan[i].state = DCSR_STOPINTR; - - memset(s->req, 0, sizeof(uint8_t) * PXA2XX_DMA_NUM_REQUESTS); - - qdev_init_gpio_in(dev, pxa2xx_dma_request, PXA2XX_DMA_NUM_REQUESTS); - - memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_dma_ops, s, - "pxa2xx.dma", 0x00010000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - - return 0; -} - -DeviceState *pxa27x_dma_init(hwaddr base, qemu_irq irq) -{ - DeviceState *dev; - - dev = qdev_create(NULL, "pxa2xx-dma"); - qdev_prop_set_int32(dev, "channels", PXA27X_DMA_NUM_CHANNELS); - qdev_init_nofail(dev); - - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); - - return dev; -} - -DeviceState *pxa255_dma_init(hwaddr base, qemu_irq irq) -{ - DeviceState *dev; - - dev = qdev_create(NULL, "pxa2xx-dma"); - qdev_prop_set_int32(dev, "channels", PXA27X_DMA_NUM_CHANNELS); - qdev_init_nofail(dev); - - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); - - return dev; -} - -static bool is_version_0(void *opaque, int version_id) -{ - return version_id == 0; -} - -static VMStateDescription vmstate_pxa2xx_dma_chan = { - .name = "pxa2xx_dma_chan", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(descr, PXA2xxDMAChannel), - VMSTATE_UINT32(src, PXA2xxDMAChannel), - VMSTATE_UINT32(dest, PXA2xxDMAChannel), - VMSTATE_UINT32(cmd, PXA2xxDMAChannel), - VMSTATE_UINT32(state, PXA2xxDMAChannel), - VMSTATE_INT32(request, PXA2xxDMAChannel), - VMSTATE_END_OF_LIST(), - }, -}; - -static VMStateDescription vmstate_pxa2xx_dma = { - .name = "pxa2xx_dma", - .version_id = 1, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UNUSED_TEST(is_version_0, 4), - VMSTATE_UINT32(stopintr, PXA2xxDMAState), - VMSTATE_UINT32(eorintr, PXA2xxDMAState), - VMSTATE_UINT32(rasintr, PXA2xxDMAState), - VMSTATE_UINT32(startintr, PXA2xxDMAState), - VMSTATE_UINT32(endintr, PXA2xxDMAState), - VMSTATE_UINT32(align, PXA2xxDMAState), - VMSTATE_UINT32(pio, PXA2xxDMAState), - VMSTATE_BUFFER(req, PXA2xxDMAState), - VMSTATE_STRUCT_VARRAY_POINTER_INT32(chan, PXA2xxDMAState, channels, - vmstate_pxa2xx_dma_chan, PXA2xxDMAChannel), - VMSTATE_END_OF_LIST(), - }, -}; - -static Property pxa2xx_dma_properties[] = { - DEFINE_PROP_INT32("channels", PXA2xxDMAState, channels, -1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pxa2xx_dma_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = pxa2xx_dma_init; - dc->desc = "PXA2xx DMA controller"; - dc->vmsd = &vmstate_pxa2xx_dma; - dc->props = pxa2xx_dma_properties; -} - -static const TypeInfo pxa2xx_dma_info = { - .name = TYPE_PXA2XX_DMA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxDMAState), - .class_init = pxa2xx_dma_class_init, -}; - -static void pxa2xx_dma_register_types(void) -{ - type_register_static(&pxa2xx_dma_info); -} - -type_init(pxa2xx_dma_register_types) diff --git a/qemu/hw/dma/rc4030.c b/qemu/hw/dma/rc4030.c deleted file mode 100644 index a06c2359a..000000000 --- a/qemu/hw/dma/rc4030.c +++ /dev/null @@ -1,842 +0,0 @@ -/* - * QEMU JAZZ RC4030 chipset - * - * Copyright (c) 2007-2013 Hervé Poussineau - * - * 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/mips/mips.h" -#include "hw/sysbus.h" -#include "qemu/timer.h" -#include "exec/address-spaces.h" -#include "trace.h" - -/********************************************************/ -/* rc4030 emulation */ - -#define MAX_TL_ENTRIES 512 - -typedef struct dma_pagetable_entry { - int32_t frame; - int32_t owner; -} QEMU_PACKED dma_pagetable_entry; - -#define DMA_PAGESIZE 4096 -#define DMA_REG_ENABLE 1 -#define DMA_REG_COUNT 2 -#define DMA_REG_ADDRESS 3 - -#define DMA_FLAG_ENABLE 0x0001 -#define DMA_FLAG_MEM_TO_DEV 0x0002 -#define DMA_FLAG_TC_INTR 0x0100 -#define DMA_FLAG_MEM_INTR 0x0200 -#define DMA_FLAG_ADDR_INTR 0x0400 - -#define TYPE_RC4030 "rc4030" -#define RC4030(obj) \ - OBJECT_CHECK(rc4030State, (obj), TYPE_RC4030) - -typedef struct rc4030State -{ - SysBusDevice parent; - - uint32_t config; /* 0x0000: RC4030 config register */ - uint32_t revision; /* 0x0008: RC4030 Revision register */ - uint32_t invalid_address_register; /* 0x0010: Invalid Address register */ - - /* DMA */ - uint32_t dma_regs[8][4]; - uint32_t dma_tl_base; /* 0x0018: DMA transl. table base */ - uint32_t dma_tl_limit; /* 0x0020: DMA transl. table limit */ - - /* cache */ - uint32_t cache_maint; /* 0x0030: Cache Maintenance */ - uint32_t remote_failed_address; /* 0x0038: Remote Failed Address */ - uint32_t memory_failed_address; /* 0x0040: Memory Failed Address */ - uint32_t cache_ptag; /* 0x0048: I/O Cache Physical Tag */ - uint32_t cache_ltag; /* 0x0050: I/O Cache Logical Tag */ - uint32_t cache_bmask; /* 0x0058: I/O Cache Byte Mask */ - - uint32_t nmi_interrupt; /* 0x0200: interrupt source */ - uint32_t memory_refresh_rate; /* 0x0210: memory refresh rate */ - uint32_t nvram_protect; /* 0x0220: NV ram protect register */ - uint32_t rem_speed[16]; - uint32_t imr_jazz; /* Local bus int enable mask */ - uint32_t isr_jazz; /* Local bus int source */ - - /* timer */ - QEMUTimer *periodic_timer; - uint32_t itr; /* Interval timer reload */ - - qemu_irq timer_irq; - qemu_irq jazz_bus_irq; - - /* biggest translation table */ - MemoryRegion dma_tt; - /* translation table memory region alias, added to system RAM */ - MemoryRegion dma_tt_alias; - /* whole DMA memory region, root of DMA address space */ - MemoryRegion dma_mr; - /* translation table entry aliases, added to DMA memory region */ - MemoryRegion dma_mrs[MAX_TL_ENTRIES]; - AddressSpace dma_as; - - MemoryRegion iomem_chipset; - MemoryRegion iomem_jazzio; -} rc4030State; - -static void set_next_tick(rc4030State *s) -{ - qemu_irq_lower(s->timer_irq); - uint32_t tm_hz; - - tm_hz = 1000 / (s->itr + 1); - - timer_mod(s->periodic_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - NANOSECONDS_PER_SECOND / tm_hz); -} - -/* called for accesses to rc4030 */ -static uint64_t rc4030_read(void *opaque, hwaddr addr, unsigned int size) -{ - rc4030State *s = opaque; - uint32_t val; - - addr &= 0x3fff; - switch (addr & ~0x3) { - /* Global config register */ - case 0x0000: - val = s->config; - break; - /* Revision register */ - case 0x0008: - val = s->revision; - break; - /* Invalid Address register */ - case 0x0010: - val = s->invalid_address_register; - break; - /* DMA transl. table base */ - case 0x0018: - val = s->dma_tl_base; - break; - /* DMA transl. table limit */ - case 0x0020: - val = s->dma_tl_limit; - break; - /* Remote Failed Address */ - case 0x0038: - val = s->remote_failed_address; - break; - /* Memory Failed Address */ - case 0x0040: - val = s->memory_failed_address; - break; - /* I/O Cache Byte Mask */ - case 0x0058: - val = s->cache_bmask; - /* HACK */ - if (s->cache_bmask == (uint32_t)-1) - s->cache_bmask = 0; - break; - /* Remote Speed Registers */ - case 0x0070: - case 0x0078: - case 0x0080: - case 0x0088: - case 0x0090: - case 0x0098: - case 0x00a0: - case 0x00a8: - case 0x00b0: - case 0x00b8: - case 0x00c0: - case 0x00c8: - case 0x00d0: - case 0x00d8: - case 0x00e0: - case 0x00e8: - val = s->rem_speed[(addr - 0x0070) >> 3]; - break; - /* DMA channel base address */ - case 0x0100: - case 0x0108: - case 0x0110: - case 0x0118: - case 0x0120: - case 0x0128: - case 0x0130: - case 0x0138: - case 0x0140: - case 0x0148: - case 0x0150: - case 0x0158: - case 0x0160: - case 0x0168: - case 0x0170: - case 0x0178: - case 0x0180: - case 0x0188: - case 0x0190: - case 0x0198: - case 0x01a0: - case 0x01a8: - case 0x01b0: - case 0x01b8: - case 0x01c0: - case 0x01c8: - case 0x01d0: - case 0x01d8: - case 0x01e0: - case 0x01e8: - case 0x01f0: - case 0x01f8: - { - int entry = (addr - 0x0100) >> 5; - int idx = (addr & 0x1f) >> 3; - val = s->dma_regs[entry][idx]; - } - break; - /* Interrupt source */ - case 0x0200: - val = s->nmi_interrupt; - break; - /* Error type */ - case 0x0208: - val = 0; - break; - /* Memory refresh rate */ - case 0x0210: - val = s->memory_refresh_rate; - break; - /* NV ram protect register */ - case 0x0220: - val = s->nvram_protect; - break; - /* Interval timer count */ - case 0x0230: - val = 0; - qemu_irq_lower(s->timer_irq); - break; - /* EISA interrupt */ - case 0x0238: - val = 7; /* FIXME: should be read from EISA controller */ - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "rc4030: invalid read at 0x%x", (int)addr); - val = 0; - break; - } - - if ((addr & ~3) != 0x230) { - trace_rc4030_read(addr, val); - } - - return val; -} - -static void rc4030_dma_as_update_one(rc4030State *s, int index, uint32_t frame) -{ - if (index < MAX_TL_ENTRIES) { - memory_region_set_enabled(&s->dma_mrs[index], false); - } - - if (!frame) { - return; - } - - if (index >= MAX_TL_ENTRIES) { - qemu_log_mask(LOG_UNIMP, - "rc4030: trying to use too high " - "translation table entry %d (max allowed=%d)", - index, MAX_TL_ENTRIES); - return; - } - memory_region_set_alias_offset(&s->dma_mrs[index], frame); - memory_region_set_enabled(&s->dma_mrs[index], true); -} - -static void rc4030_dma_tt_write(void *opaque, hwaddr addr, uint64_t data, - unsigned int size) -{ - rc4030State *s = opaque; - - /* write memory */ - memcpy(memory_region_get_ram_ptr(&s->dma_tt) + addr, &data, size); - - /* update dma address space (only if frame field has been written) */ - if (addr % sizeof(dma_pagetable_entry) == 0) { - int index = addr / sizeof(dma_pagetable_entry); - memory_region_transaction_begin(); - rc4030_dma_as_update_one(s, index, (uint32_t)data); - memory_region_transaction_commit(); - } -} - -static const MemoryRegionOps rc4030_dma_tt_ops = { - .write = rc4030_dma_tt_write, - .impl.min_access_size = 4, - .impl.max_access_size = 4, -}; - -static void rc4030_dma_tt_update(rc4030State *s, uint32_t new_tl_base, - uint32_t new_tl_limit) -{ - int entries, i; - dma_pagetable_entry *dma_tl_contents; - - if (s->dma_tl_limit) { - /* write old dma tl table to physical memory */ - memory_region_del_subregion(get_system_memory(), &s->dma_tt_alias); - cpu_physical_memory_write(s->dma_tl_limit & 0x7fffffff, - memory_region_get_ram_ptr(&s->dma_tt), - memory_region_size(&s->dma_tt_alias)); - } - object_unparent(OBJECT(&s->dma_tt_alias)); - - s->dma_tl_base = new_tl_base; - s->dma_tl_limit = new_tl_limit; - new_tl_base &= 0x7fffffff; - - if (s->dma_tl_limit) { - uint64_t dma_tt_size; - if (s->dma_tl_limit <= memory_region_size(&s->dma_tt)) { - dma_tt_size = s->dma_tl_limit; - } else { - dma_tt_size = memory_region_size(&s->dma_tt); - } - memory_region_init_alias(&s->dma_tt_alias, OBJECT(s), - "dma-table-alias", - &s->dma_tt, 0, dma_tt_size); - dma_tl_contents = memory_region_get_ram_ptr(&s->dma_tt); - cpu_physical_memory_read(new_tl_base, dma_tl_contents, dma_tt_size); - - memory_region_transaction_begin(); - entries = dma_tt_size / sizeof(dma_pagetable_entry); - for (i = 0; i < entries; i++) { - rc4030_dma_as_update_one(s, i, dma_tl_contents[i].frame); - } - memory_region_add_subregion(get_system_memory(), new_tl_base, - &s->dma_tt_alias); - memory_region_transaction_commit(); - } else { - memory_region_init(&s->dma_tt_alias, OBJECT(s), - "dma-table-alias", 0); - } -} - -static void rc4030_write(void *opaque, hwaddr addr, uint64_t data, - unsigned int size) -{ - rc4030State *s = opaque; - uint32_t val = data; - addr &= 0x3fff; - - trace_rc4030_write(addr, val); - - switch (addr & ~0x3) { - /* Global config register */ - case 0x0000: - s->config = val; - break; - /* DMA transl. table base */ - case 0x0018: - rc4030_dma_tt_update(s, val, s->dma_tl_limit); - break; - /* DMA transl. table limit */ - case 0x0020: - rc4030_dma_tt_update(s, s->dma_tl_base, val); - break; - /* DMA transl. table invalidated */ - case 0x0028: - break; - /* Cache Maintenance */ - case 0x0030: - s->cache_maint = val; - break; - /* I/O Cache Physical Tag */ - case 0x0048: - s->cache_ptag = val; - break; - /* I/O Cache Logical Tag */ - case 0x0050: - s->cache_ltag = val; - break; - /* I/O Cache Byte Mask */ - case 0x0058: - s->cache_bmask |= val; /* HACK */ - break; - /* I/O Cache Buffer Window */ - case 0x0060: - /* HACK */ - if (s->cache_ltag == 0x80000001 && s->cache_bmask == 0xf0f0f0f) { - hwaddr dest = s->cache_ptag & ~0x1; - dest += (s->cache_maint & 0x3) << 3; - cpu_physical_memory_write(dest, &val, 4); - } - break; - /* Remote Speed Registers */ - case 0x0070: - case 0x0078: - case 0x0080: - case 0x0088: - case 0x0090: - case 0x0098: - case 0x00a0: - case 0x00a8: - case 0x00b0: - case 0x00b8: - case 0x00c0: - case 0x00c8: - case 0x00d0: - case 0x00d8: - case 0x00e0: - case 0x00e8: - s->rem_speed[(addr - 0x0070) >> 3] = val; - break; - /* DMA channel base address */ - case 0x0100: - case 0x0108: - case 0x0110: - case 0x0118: - case 0x0120: - case 0x0128: - case 0x0130: - case 0x0138: - case 0x0140: - case 0x0148: - case 0x0150: - case 0x0158: - case 0x0160: - case 0x0168: - case 0x0170: - case 0x0178: - case 0x0180: - case 0x0188: - case 0x0190: - case 0x0198: - case 0x01a0: - case 0x01a8: - case 0x01b0: - case 0x01b8: - case 0x01c0: - case 0x01c8: - case 0x01d0: - case 0x01d8: - case 0x01e0: - case 0x01e8: - case 0x01f0: - case 0x01f8: - { - int entry = (addr - 0x0100) >> 5; - int idx = (addr & 0x1f) >> 3; - s->dma_regs[entry][idx] = val; - } - break; - /* Memory refresh rate */ - case 0x0210: - s->memory_refresh_rate = val; - break; - /* Interval timer reload */ - case 0x0228: - s->itr = val; - qemu_irq_lower(s->timer_irq); - set_next_tick(s); - break; - /* EISA interrupt */ - case 0x0238: - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "rc4030: invalid write of 0x%02x at 0x%x", - val, (int)addr); - break; - } -} - -static const MemoryRegionOps rc4030_ops = { - .read = rc4030_read, - .write = rc4030_write, - .impl.min_access_size = 4, - .impl.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void update_jazz_irq(rc4030State *s) -{ - uint16_t pending; - - pending = s->isr_jazz & s->imr_jazz; - - if (pending != 0) - qemu_irq_raise(s->jazz_bus_irq); - else - qemu_irq_lower(s->jazz_bus_irq); -} - -static void rc4030_irq_jazz_request(void *opaque, int irq, int level) -{ - rc4030State *s = opaque; - - if (level) { - s->isr_jazz |= 1 << irq; - } else { - s->isr_jazz &= ~(1 << irq); - } - - update_jazz_irq(s); -} - -static void rc4030_periodic_timer(void *opaque) -{ - rc4030State *s = opaque; - - set_next_tick(s); - qemu_irq_raise(s->timer_irq); -} - -static uint64_t jazzio_read(void *opaque, hwaddr addr, unsigned int size) -{ - rc4030State *s = opaque; - uint32_t val; - uint32_t irq; - addr &= 0xfff; - - switch (addr) { - /* Local bus int source */ - case 0x00: { - uint32_t pending = s->isr_jazz & s->imr_jazz; - val = 0; - irq = 0; - while (pending) { - if (pending & 1) { - val = (irq + 1) << 2; - break; - } - irq++; - pending >>= 1; - } - break; - } - /* Local bus int enable mask */ - case 0x02: - val = s->imr_jazz; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "rc4030/jazzio: invalid read at 0x%x", (int)addr); - val = 0; - break; - } - - trace_jazzio_read(addr, val); - - return val; -} - -static void jazzio_write(void *opaque, hwaddr addr, uint64_t data, - unsigned int size) -{ - rc4030State *s = opaque; - uint32_t val = data; - addr &= 0xfff; - - trace_jazzio_write(addr, val); - - switch (addr) { - /* Local bus int enable mask */ - case 0x02: - s->imr_jazz = val; - update_jazz_irq(s); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "rc4030/jazzio: invalid write of 0x%02x at 0x%x", - val, (int)addr); - break; - } -} - -static const MemoryRegionOps jazzio_ops = { - .read = jazzio_read, - .write = jazzio_write, - .impl.min_access_size = 2, - .impl.max_access_size = 2, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void rc4030_reset(DeviceState *dev) -{ - rc4030State *s = RC4030(dev); - int i; - - s->config = 0x410; /* some boards seem to accept 0x104 too */ - s->revision = 1; - s->invalid_address_register = 0; - - memset(s->dma_regs, 0, sizeof(s->dma_regs)); - rc4030_dma_tt_update(s, 0, 0); - - s->remote_failed_address = s->memory_failed_address = 0; - s->cache_maint = 0; - s->cache_ptag = s->cache_ltag = 0; - s->cache_bmask = 0; - - s->memory_refresh_rate = 0x18186; - s->nvram_protect = 7; - for (i = 0; i < 15; i++) - s->rem_speed[i] = 7; - s->imr_jazz = 0x10; /* XXX: required by firmware, but why? */ - s->isr_jazz = 0; - - s->itr = 0; - - qemu_irq_lower(s->timer_irq); - qemu_irq_lower(s->jazz_bus_irq); -} - -static int rc4030_load(QEMUFile *f, void *opaque, int version_id) -{ - rc4030State* s = opaque; - int i, j; - - if (version_id != 2) - return -EINVAL; - - s->config = qemu_get_be32(f); - s->invalid_address_register = qemu_get_be32(f); - for (i = 0; i < 8; i++) - for (j = 0; j < 4; j++) - s->dma_regs[i][j] = qemu_get_be32(f); - s->dma_tl_base = qemu_get_be32(f); - s->dma_tl_limit = qemu_get_be32(f); - s->cache_maint = qemu_get_be32(f); - s->remote_failed_address = qemu_get_be32(f); - s->memory_failed_address = qemu_get_be32(f); - s->cache_ptag = qemu_get_be32(f); - s->cache_ltag = qemu_get_be32(f); - s->cache_bmask = qemu_get_be32(f); - s->memory_refresh_rate = qemu_get_be32(f); - s->nvram_protect = qemu_get_be32(f); - for (i = 0; i < 15; i++) - s->rem_speed[i] = qemu_get_be32(f); - s->imr_jazz = qemu_get_be32(f); - s->isr_jazz = qemu_get_be32(f); - s->itr = qemu_get_be32(f); - - set_next_tick(s); - update_jazz_irq(s); - - return 0; -} - -static void rc4030_save(QEMUFile *f, void *opaque) -{ - rc4030State* s = opaque; - int i, j; - - qemu_put_be32(f, s->config); - qemu_put_be32(f, s->invalid_address_register); - for (i = 0; i < 8; i++) - for (j = 0; j < 4; j++) - qemu_put_be32(f, s->dma_regs[i][j]); - qemu_put_be32(f, s->dma_tl_base); - qemu_put_be32(f, s->dma_tl_limit); - qemu_put_be32(f, s->cache_maint); - qemu_put_be32(f, s->remote_failed_address); - qemu_put_be32(f, s->memory_failed_address); - qemu_put_be32(f, s->cache_ptag); - qemu_put_be32(f, s->cache_ltag); - qemu_put_be32(f, s->cache_bmask); - qemu_put_be32(f, s->memory_refresh_rate); - qemu_put_be32(f, s->nvram_protect); - for (i = 0; i < 15; i++) - qemu_put_be32(f, s->rem_speed[i]); - qemu_put_be32(f, s->imr_jazz); - qemu_put_be32(f, s->isr_jazz); - qemu_put_be32(f, s->itr); -} - -static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_write) -{ - rc4030State *s = opaque; - hwaddr dma_addr; - int dev_to_mem; - - s->dma_regs[n][DMA_REG_ENABLE] &= ~(DMA_FLAG_TC_INTR | DMA_FLAG_MEM_INTR | DMA_FLAG_ADDR_INTR); - - /* Check DMA channel consistency */ - dev_to_mem = (s->dma_regs[n][DMA_REG_ENABLE] & DMA_FLAG_MEM_TO_DEV) ? 0 : 1; - if (!(s->dma_regs[n][DMA_REG_ENABLE] & DMA_FLAG_ENABLE) || - (is_write != dev_to_mem)) { - s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_MEM_INTR; - s->nmi_interrupt |= 1 << n; - return; - } - - /* Get start address and len */ - if (len > s->dma_regs[n][DMA_REG_COUNT]) - len = s->dma_regs[n][DMA_REG_COUNT]; - dma_addr = s->dma_regs[n][DMA_REG_ADDRESS]; - - /* Read/write data at right place */ - address_space_rw(&s->dma_as, dma_addr, MEMTXATTRS_UNSPECIFIED, - buf, len, is_write); - - s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_TC_INTR; - s->dma_regs[n][DMA_REG_COUNT] -= len; -} - -struct rc4030DMAState { - void *opaque; - int n; -}; - -void rc4030_dma_read(void *dma, uint8_t *buf, int len) -{ - rc4030_dma s = dma; - rc4030_do_dma(s->opaque, s->n, buf, len, 0); -} - -void rc4030_dma_write(void *dma, uint8_t *buf, int len) -{ - rc4030_dma s = dma; - rc4030_do_dma(s->opaque, s->n, buf, len, 1); -} - -static rc4030_dma *rc4030_allocate_dmas(void *opaque, int n) -{ - rc4030_dma *s; - struct rc4030DMAState *p; - int i; - - s = (rc4030_dma *)g_malloc0(sizeof(rc4030_dma) * n); - p = (struct rc4030DMAState *)g_malloc0(sizeof(struct rc4030DMAState) * n); - for (i = 0; i < n; i++) { - p->opaque = opaque; - p->n = i; - s[i] = p; - p++; - } - return s; -} - -static void rc4030_initfn(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - rc4030State *s = RC4030(obj); - SysBusDevice *sysbus = SYS_BUS_DEVICE(obj); - - qdev_init_gpio_in(dev, rc4030_irq_jazz_request, 16); - - sysbus_init_irq(sysbus, &s->timer_irq); - sysbus_init_irq(sysbus, &s->jazz_bus_irq); - - register_savevm(NULL, "rc4030", 0, 2, rc4030_save, rc4030_load, s); - - sysbus_init_mmio(sysbus, &s->iomem_chipset); - sysbus_init_mmio(sysbus, &s->iomem_jazzio); -} - -static void rc4030_realize(DeviceState *dev, Error **errp) -{ - rc4030State *s = RC4030(dev); - Object *o = OBJECT(dev); - int i; - - s->periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - rc4030_periodic_timer, s); - - memory_region_init_io(&s->iomem_chipset, NULL, &rc4030_ops, s, - "rc4030.chipset", 0x300); - memory_region_init_io(&s->iomem_jazzio, NULL, &jazzio_ops, s, - "rc4030.jazzio", 0x00001000); - - memory_region_init_rom_device(&s->dma_tt, o, - &rc4030_dma_tt_ops, s, "dma-table", - MAX_TL_ENTRIES * sizeof(dma_pagetable_entry), - NULL); - memory_region_init(&s->dma_tt_alias, o, "dma-table-alias", 0); - memory_region_init(&s->dma_mr, o, "dma", INT32_MAX); - for (i = 0; i < MAX_TL_ENTRIES; ++i) { - memory_region_init_alias(&s->dma_mrs[i], o, "dma-alias", - get_system_memory(), 0, DMA_PAGESIZE); - memory_region_set_enabled(&s->dma_mrs[i], false); - memory_region_add_subregion(&s->dma_mr, i * DMA_PAGESIZE, - &s->dma_mrs[i]); - } - address_space_init(&s->dma_as, &s->dma_mr, "rc4030-dma"); -} - -static void rc4030_unrealize(DeviceState *dev, Error **errp) -{ - rc4030State *s = RC4030(dev); - int i; - - timer_free(s->periodic_timer); - - address_space_destroy(&s->dma_as); - object_unparent(OBJECT(&s->dma_tt)); - object_unparent(OBJECT(&s->dma_tt_alias)); - object_unparent(OBJECT(&s->dma_mr)); - for (i = 0; i < MAX_TL_ENTRIES; ++i) { - memory_region_del_subregion(&s->dma_mr, &s->dma_mrs[i]); - object_unparent(OBJECT(&s->dma_mrs[i])); - } -} - -static void rc4030_class_init(ObjectClass *klass, void *class_data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = rc4030_realize; - dc->unrealize = rc4030_unrealize; - dc->reset = rc4030_reset; -} - -static const TypeInfo rc4030_info = { - .name = TYPE_RC4030, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(rc4030State), - .instance_init = rc4030_initfn, - .class_init = rc4030_class_init, -}; - -static void rc4030_register_types(void) -{ - type_register_static(&rc4030_info); -} - -type_init(rc4030_register_types) - -DeviceState *rc4030_init(rc4030_dma **dmas, MemoryRegion **dma_mr) -{ - DeviceState *dev; - - dev = qdev_create(NULL, TYPE_RC4030); - qdev_init_nofail(dev); - - *dmas = rc4030_allocate_dmas(dev, 4); - *dma_mr = &RC4030(dev)->dma_mr; - return dev; -} diff --git a/qemu/hw/dma/soc_dma.c b/qemu/hw/dma/soc_dma.c deleted file mode 100644 index 9bb499bf9..000000000 --- a/qemu/hw/dma/soc_dma.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * On-chip DMA controller framework. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/timer.h" -#include "hw/arm/soc_dma.h" - -static void transfer_mem2mem(struct soc_dma_ch_s *ch) -{ - memcpy(ch->paddr[0], ch->paddr[1], ch->bytes); - ch->paddr[0] += ch->bytes; - ch->paddr[1] += ch->bytes; -} - -static void transfer_mem2fifo(struct soc_dma_ch_s *ch) -{ - ch->io_fn[1](ch->io_opaque[1], ch->paddr[0], ch->bytes); - ch->paddr[0] += ch->bytes; -} - -static void transfer_fifo2mem(struct soc_dma_ch_s *ch) -{ - ch->io_fn[0](ch->io_opaque[0], ch->paddr[1], ch->bytes); - ch->paddr[1] += ch->bytes; -} - -/* This is further optimisable but isn't very important because often - * DMA peripherals forbid this kind of transfers and even when they don't, - * oprating systems may not need to use them. */ -static void *fifo_buf; -static int fifo_size; -static void transfer_fifo2fifo(struct soc_dma_ch_s *ch) -{ - if (ch->bytes > fifo_size) - fifo_buf = g_realloc(fifo_buf, fifo_size = ch->bytes); - - /* Implement as transfer_fifo2linear + transfer_linear2fifo. */ - ch->io_fn[0](ch->io_opaque[0], fifo_buf, ch->bytes); - ch->io_fn[1](ch->io_opaque[1], fifo_buf, ch->bytes); -} - -struct dma_s { - struct soc_dma_s soc; - int chnum; - uint64_t ch_enable_mask; - int64_t channel_freq; - int enabled_count; - - struct memmap_entry_s { - enum soc_dma_port_type type; - hwaddr addr; - union { - struct { - void *opaque; - soc_dma_io_t fn; - int out; - } fifo; - struct { - void *base; - size_t size; - } mem; - } u; - } *memmap; - int memmap_size; - - struct soc_dma_ch_s ch[0]; -}; - -static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, int delay_bytes) -{ - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - struct dma_s *dma = (struct dma_s *) ch->dma; - - timer_mod(ch->timer, now + delay_bytes / dma->channel_freq); -} - -static void soc_dma_ch_run(void *opaque) -{ - struct soc_dma_ch_s *ch = (struct soc_dma_ch_s *) opaque; - - ch->running = 1; - ch->dma->setup_fn(ch); - ch->transfer_fn(ch); - ch->running = 0; - - if (ch->enable) - soc_dma_ch_schedule(ch, ch->bytes); - ch->bytes = 0; -} - -static inline struct memmap_entry_s *soc_dma_lookup(struct dma_s *dma, - hwaddr addr) -{ - struct memmap_entry_s *lo; - int hi; - - lo = dma->memmap; - hi = dma->memmap_size; - - while (hi > 1) { - hi /= 2; - if (lo[hi].addr <= addr) - lo += hi; - } - - return lo; -} - -static inline enum soc_dma_port_type soc_dma_ch_update_type( - struct soc_dma_ch_s *ch, int port) -{ - struct dma_s *dma = (struct dma_s *) ch->dma; - struct memmap_entry_s *entry = soc_dma_lookup(dma, ch->vaddr[port]); - - if (entry->type == soc_dma_port_fifo) { - while (entry < dma->memmap + dma->memmap_size && - entry->u.fifo.out != port) - entry ++; - if (entry->addr != ch->vaddr[port] || entry->u.fifo.out != port) - return soc_dma_port_other; - - if (ch->type[port] != soc_dma_access_const) - return soc_dma_port_other; - - ch->io_fn[port] = entry->u.fifo.fn; - ch->io_opaque[port] = entry->u.fifo.opaque; - return soc_dma_port_fifo; - } else if (entry->type == soc_dma_port_mem) { - if (entry->addr > ch->vaddr[port] || - entry->addr + entry->u.mem.size <= ch->vaddr[port]) - return soc_dma_port_other; - - /* TODO: support constant memory address for source port as used for - * drawing solid rectangles by PalmOS(R). */ - if (ch->type[port] != soc_dma_access_const) - return soc_dma_port_other; - - ch->paddr[port] = (uint8_t *) entry->u.mem.base + - (ch->vaddr[port] - entry->addr); - /* TODO: save bytes left to the end of the mapping somewhere so we - * can check we're not reading beyond it. */ - return soc_dma_port_mem; - } else - return soc_dma_port_other; -} - -void soc_dma_ch_update(struct soc_dma_ch_s *ch) -{ - enum soc_dma_port_type src, dst; - - src = soc_dma_ch_update_type(ch, 0); - if (src == soc_dma_port_other) { - ch->update = 0; - ch->transfer_fn = ch->dma->transfer_fn; - return; - } - dst = soc_dma_ch_update_type(ch, 1); - - /* TODO: use src and dst as array indices. */ - if (src == soc_dma_port_mem && dst == soc_dma_port_mem) - ch->transfer_fn = transfer_mem2mem; - else if (src == soc_dma_port_mem && dst == soc_dma_port_fifo) - ch->transfer_fn = transfer_mem2fifo; - else if (src == soc_dma_port_fifo && dst == soc_dma_port_mem) - ch->transfer_fn = transfer_fifo2mem; - else if (src == soc_dma_port_fifo && dst == soc_dma_port_fifo) - ch->transfer_fn = transfer_fifo2fifo; - else - ch->transfer_fn = ch->dma->transfer_fn; - - ch->update = (dst != soc_dma_port_other); -} - -static void soc_dma_ch_freq_update(struct dma_s *s) -{ - if (s->enabled_count) - /* We completely ignore channel priorities and stuff */ - s->channel_freq = s->soc.freq / s->enabled_count; - else { - /* TODO: Signal that we want to disable the functional clock and let - * the platform code decide what to do with it, i.e. check that - * auto-idle is enabled in the clock controller and if we are stopping - * the clock, do the same with any parent clocks that had only one - * user keeping them on and auto-idle enabled. */ - } -} - -void soc_dma_set_request(struct soc_dma_ch_s *ch, int level) -{ - struct dma_s *dma = (struct dma_s *) ch->dma; - - dma->enabled_count += level - ch->enable; - - if (level) - dma->ch_enable_mask |= 1 << ch->num; - else - dma->ch_enable_mask &= ~(1 << ch->num); - - if (level != ch->enable) { - soc_dma_ch_freq_update(dma); - ch->enable = level; - - if (!ch->enable) - timer_del(ch->timer); - else if (!ch->running) - soc_dma_ch_run(ch); - else - soc_dma_ch_schedule(ch, 1); - } -} - -void soc_dma_reset(struct soc_dma_s *soc) -{ - struct dma_s *s = (struct dma_s *) soc; - - s->soc.drqbmp = 0; - s->ch_enable_mask = 0; - s->enabled_count = 0; - soc_dma_ch_freq_update(s); -} - -/* TODO: take a functional-clock argument */ -struct soc_dma_s *soc_dma_init(int n) -{ - int i; - struct dma_s *s = g_malloc0(sizeof(*s) + n * sizeof(*s->ch)); - - s->chnum = n; - s->soc.ch = s->ch; - for (i = 0; i < n; i ++) { - s->ch[i].dma = &s->soc; - s->ch[i].num = i; - s->ch[i].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, soc_dma_ch_run, &s->ch[i]); - } - - soc_dma_reset(&s->soc); - fifo_size = 0; - - return &s->soc; -} - -void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base, - soc_dma_io_t fn, void *opaque, int out) -{ - struct memmap_entry_s *entry; - struct dma_s *dma = (struct dma_s *) soc; - - dma->memmap = g_realloc(dma->memmap, sizeof(*entry) * - (dma->memmap_size + 1)); - entry = soc_dma_lookup(dma, virt_base); - - if (dma->memmap_size) { - 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 %"PRIx64 - " collides with RAM region at %"PRIx64 - "-%"PRIx64 "\n", __func__, - virt_base, entry->addr, - (entry->addr + entry->u.mem.size)); - exit(-1); - } - - if (entry->addr <= virt_base) - entry ++; - } else - 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 %"PRIx64 - " collides FIFO at %"PRIx64 "\n", - __func__, virt_base, entry->addr); - exit(-1); - } - - entry ++; - } - - memmove(entry + 1, entry, - (uint8_t *) (dma->memmap + dma->memmap_size ++) - - (uint8_t *) entry); - } else - dma->memmap_size ++; - - entry->addr = virt_base; - entry->type = soc_dma_port_fifo; - entry->u.fifo.fn = fn; - entry->u.fifo.opaque = opaque; - entry->u.fifo.out = out; -} - -void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, - hwaddr virt_base, size_t size) -{ - struct memmap_entry_s *entry; - struct dma_s *dma = (struct dma_s *) soc; - - dma->memmap = g_realloc(dma->memmap, sizeof(*entry) * - (dma->memmap_size + 1)); - entry = soc_dma_lookup(dma, virt_base); - - if (dma->memmap_size) { - if (entry->type == soc_dma_port_mem) { - 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 %"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); - } - - if (entry->addr <= virt_base) - entry ++; - } else { - if (entry->addr >= virt_base && - entry->addr < virt_base + size) { - fprintf(stderr, "%s: RAM at %"PRIx64 "-%"PRIx64 - " collides with FIFO at %"PRIx64 - "\n", __func__, - virt_base, virt_base + size, - entry->addr); - exit(-1); - } - - while (entry < dma->memmap + dma->memmap_size && - entry->addr <= virt_base) - entry ++; - } - - memmove(entry + 1, entry, - (uint8_t *) (dma->memmap + dma->memmap_size ++) - - (uint8_t *) entry); - } else - dma->memmap_size ++; - - entry->addr = virt_base; - entry->type = soc_dma_port_mem; - entry->u.mem.base = phys_base; - entry->u.mem.size = size; -} - -/* TODO: port removal for ports like PCMCIA memory */ diff --git a/qemu/hw/dma/sparc32_dma.c b/qemu/hw/dma/sparc32_dma.c deleted file mode 100644 index 9d545e412..000000000 --- a/qemu/hw/dma/sparc32_dma.c +++ /dev/null @@ -1,323 +0,0 @@ -/* - * QEMU Sparc32 DMA controller emulation - * - * Copyright (c) 2006 Fabrice Bellard - * - * Modifications: - * 2010-Feb-14 Artyom Tarasenko : reworked irq generation - * - * 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/sparc/sparc32_dma.h" -#include "hw/sparc/sun4m.h" -#include "hw/sysbus.h" -#include "trace.h" - -/* - * This is the DMA controller part of chip STP2000 (Master I/O), also - * produced as NCR89C100. See - * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt - * and - * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/DMA2.txt - */ - -#define DMA_REGS 4 -#define DMA_SIZE (4 * sizeof(uint32_t)) -/* We need the mask, because one instance of the device is not page - aligned (ledma, start address 0x0010) */ -#define DMA_MASK (DMA_SIZE - 1) -/* OBP says 0x20 bytes for ledma, the extras are aliased to espdma */ -#define DMA_ETH_SIZE (8 * sizeof(uint32_t)) -#define DMA_MAX_REG_OFFSET (2 * DMA_SIZE - 1) - -#define DMA_VER 0xa0000000 -#define DMA_INTR 1 -#define DMA_INTREN 0x10 -#define DMA_WRITE_MEM 0x100 -#define DMA_EN 0x200 -#define DMA_LOADED 0x04000000 -#define DMA_DRAIN_FIFO 0x40 -#define DMA_RESET 0x80 - -/* XXX SCSI and ethernet should have different read-only bit masks */ -#define DMA_CSR_RO_MASK 0xfe000007 - -#define TYPE_SPARC32_DMA "sparc32_dma" -#define SPARC32_DMA(obj) OBJECT_CHECK(DMAState, (obj), TYPE_SPARC32_DMA) - -typedef struct DMAState DMAState; - -struct DMAState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t dmaregs[DMA_REGS]; - qemu_irq irq; - void *iommu; - qemu_irq gpio[2]; - uint32_t is_ledma; -}; - -enum { - GPIO_RESET = 0, - GPIO_DMA, -}; - -/* Note: on sparc, the lance 16 bit bus is swapped */ -void ledma_memory_read(void *opaque, hwaddr addr, - uint8_t *buf, int len, int do_bswap) -{ - DMAState *s = opaque; - int i; - - addr |= s->dmaregs[3]; - trace_ledma_memory_read(addr); - if (do_bswap) { - sparc_iommu_memory_read(s->iommu, addr, buf, len); - } else { - addr &= ~1; - len &= ~1; - sparc_iommu_memory_read(s->iommu, addr, buf, len); - for(i = 0; i < len; i += 2) { - bswap16s((uint16_t *)(buf + i)); - } - } -} - -void ledma_memory_write(void *opaque, hwaddr addr, - uint8_t *buf, int len, int do_bswap) -{ - DMAState *s = opaque; - int l, i; - uint16_t tmp_buf[32]; - - addr |= s->dmaregs[3]; - trace_ledma_memory_write(addr); - if (do_bswap) { - sparc_iommu_memory_write(s->iommu, addr, buf, len); - } else { - addr &= ~1; - len &= ~1; - while (len > 0) { - l = len; - if (l > sizeof(tmp_buf)) - l = sizeof(tmp_buf); - for(i = 0; i < l; i += 2) { - tmp_buf[i >> 1] = bswap16(*(uint16_t *)(buf + i)); - } - sparc_iommu_memory_write(s->iommu, addr, (uint8_t *)tmp_buf, l); - len -= l; - buf += l; - addr += l; - } - } -} - -static void dma_set_irq(void *opaque, int irq, int level) -{ - DMAState *s = opaque; - if (level) { - s->dmaregs[0] |= DMA_INTR; - if (s->dmaregs[0] & DMA_INTREN) { - trace_sparc32_dma_set_irq_raise(); - qemu_irq_raise(s->irq); - } - } else { - if (s->dmaregs[0] & DMA_INTR) { - s->dmaregs[0] &= ~DMA_INTR; - if (s->dmaregs[0] & DMA_INTREN) { - trace_sparc32_dma_set_irq_lower(); - qemu_irq_lower(s->irq); - } - } - } -} - -void espdma_memory_read(void *opaque, uint8_t *buf, int len) -{ - DMAState *s = opaque; - - trace_espdma_memory_read(s->dmaregs[1]); - sparc_iommu_memory_read(s->iommu, s->dmaregs[1], buf, len); - s->dmaregs[1] += len; -} - -void espdma_memory_write(void *opaque, uint8_t *buf, int len) -{ - DMAState *s = opaque; - - trace_espdma_memory_write(s->dmaregs[1]); - sparc_iommu_memory_write(s->iommu, s->dmaregs[1], buf, len); - s->dmaregs[1] += len; -} - -static uint64_t dma_mem_read(void *opaque, hwaddr addr, - unsigned size) -{ - DMAState *s = opaque; - uint32_t saddr; - - if (s->is_ledma && (addr > DMA_MAX_REG_OFFSET)) { - /* aliased to espdma, but we can't get there from here */ - /* buggy driver if using undocumented behavior, just return 0 */ - trace_sparc32_dma_mem_readl(addr, 0); - return 0; - } - saddr = (addr & DMA_MASK) >> 2; - trace_sparc32_dma_mem_readl(addr, s->dmaregs[saddr]); - return s->dmaregs[saddr]; -} - -static void dma_mem_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - DMAState *s = opaque; - uint32_t saddr; - - if (s->is_ledma && (addr > DMA_MAX_REG_OFFSET)) { - /* aliased to espdma, but we can't get there from here */ - trace_sparc32_dma_mem_writel(addr, 0, val); - return; - } - saddr = (addr & DMA_MASK) >> 2; - trace_sparc32_dma_mem_writel(addr, s->dmaregs[saddr], val); - switch (saddr) { - case 0: - if (val & DMA_INTREN) { - if (s->dmaregs[0] & DMA_INTR) { - trace_sparc32_dma_set_irq_raise(); - qemu_irq_raise(s->irq); - } - } else { - if (s->dmaregs[0] & (DMA_INTR | DMA_INTREN)) { - trace_sparc32_dma_set_irq_lower(); - qemu_irq_lower(s->irq); - } - } - if (val & DMA_RESET) { - qemu_irq_raise(s->gpio[GPIO_RESET]); - qemu_irq_lower(s->gpio[GPIO_RESET]); - } else if (val & DMA_DRAIN_FIFO) { - val &= ~DMA_DRAIN_FIFO; - } else if (val == 0) - val = DMA_DRAIN_FIFO; - - if (val & DMA_EN && !(s->dmaregs[0] & DMA_EN)) { - trace_sparc32_dma_enable_raise(); - qemu_irq_raise(s->gpio[GPIO_DMA]); - } else if (!(val & DMA_EN) && !!(s->dmaregs[0] & DMA_EN)) { - trace_sparc32_dma_enable_lower(); - qemu_irq_lower(s->gpio[GPIO_DMA]); - } - - val &= ~DMA_CSR_RO_MASK; - val |= DMA_VER; - s->dmaregs[0] = (s->dmaregs[0] & DMA_CSR_RO_MASK) | val; - break; - case 1: - s->dmaregs[0] |= DMA_LOADED; - /* fall through */ - default: - s->dmaregs[saddr] = val; - break; - } -} - -static const MemoryRegionOps dma_mem_ops = { - .read = dma_mem_read, - .write = dma_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void dma_reset(DeviceState *d) -{ - DMAState *s = SPARC32_DMA(d); - - memset(s->dmaregs, 0, DMA_SIZE); - s->dmaregs[0] = DMA_VER; -} - -static const VMStateDescription vmstate_dma = { - .name ="sparc32_dma", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(dmaregs, DMAState, DMA_REGS), - VMSTATE_END_OF_LIST() - } -}; - -static int sparc32_dma_init1(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - DMAState *s = SPARC32_DMA(dev); - int reg_size; - - sysbus_init_irq(sbd, &s->irq); - - reg_size = s->is_ledma ? DMA_ETH_SIZE : DMA_SIZE; - memory_region_init_io(&s->iomem, OBJECT(s), &dma_mem_ops, s, - "dma", reg_size); - sysbus_init_mmio(sbd, &s->iomem); - - qdev_init_gpio_in(dev, dma_set_irq, 1); - qdev_init_gpio_out(dev, s->gpio, 2); - - return 0; -} - -static Property sparc32_dma_properties[] = { - DEFINE_PROP_PTR("iommu_opaque", DMAState, iommu), - DEFINE_PROP_UINT32("is_ledma", DMAState, is_ledma, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void sparc32_dma_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = sparc32_dma_init1; - dc->reset = dma_reset; - dc->vmsd = &vmstate_dma; - dc->props = sparc32_dma_properties; - /* Reason: pointer property "iommu_opaque" */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo sparc32_dma_info = { - .name = TYPE_SPARC32_DMA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(DMAState), - .class_init = sparc32_dma_class_init, -}; - -static void sparc32_dma_register_types(void) -{ - type_register_static(&sparc32_dma_info); -} - -type_init(sparc32_dma_register_types) diff --git a/qemu/hw/dma/sun4m_iommu.c b/qemu/hw/dma/sun4m_iommu.c deleted file mode 100644 index b3cbc54c2..000000000 --- a/qemu/hw/dma/sun4m_iommu.c +++ /dev/null @@ -1,393 +0,0 @@ -/* - * QEMU Sun4m iommu emulation - * - * Copyright (c) 2003-2005 Fabrice Bellard - * - * 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/sparc/sun4m.h" -#include "hw/sysbus.h" -#include "exec/address-spaces.h" -#include "trace.h" - -/* - * I/O MMU used by Sun4m systems - * - * Chipset docs: - * "Sun-4M System Architecture (revision 2.0) by Chuck Narad", 950-1373-01, - * http://mediacast.sun.com/users/Barton808/media/Sun4M_SystemArchitecture_edited2.pdf - */ - -#define IOMMU_NREGS (4*4096/4) -#define IOMMU_CTRL (0x0000 >> 2) -#define IOMMU_CTRL_IMPL 0xf0000000 /* Implementation */ -#define IOMMU_CTRL_VERS 0x0f000000 /* Version */ -#define IOMMU_CTRL_RNGE 0x0000001c /* Mapping RANGE */ -#define IOMMU_RNGE_16MB 0x00000000 /* 0xff000000 -> 0xffffffff */ -#define IOMMU_RNGE_32MB 0x00000004 /* 0xfe000000 -> 0xffffffff */ -#define IOMMU_RNGE_64MB 0x00000008 /* 0xfc000000 -> 0xffffffff */ -#define IOMMU_RNGE_128MB 0x0000000c /* 0xf8000000 -> 0xffffffff */ -#define IOMMU_RNGE_256MB 0x00000010 /* 0xf0000000 -> 0xffffffff */ -#define IOMMU_RNGE_512MB 0x00000014 /* 0xe0000000 -> 0xffffffff */ -#define IOMMU_RNGE_1GB 0x00000018 /* 0xc0000000 -> 0xffffffff */ -#define IOMMU_RNGE_2GB 0x0000001c /* 0x80000000 -> 0xffffffff */ -#define IOMMU_CTRL_ENAB 0x00000001 /* IOMMU Enable */ -#define IOMMU_CTRL_MASK 0x0000001d - -#define IOMMU_BASE (0x0004 >> 2) -#define IOMMU_BASE_MASK 0x07fffc00 - -#define IOMMU_TLBFLUSH (0x0014 >> 2) -#define IOMMU_TLBFLUSH_MASK 0xffffffff - -#define IOMMU_PGFLUSH (0x0018 >> 2) -#define IOMMU_PGFLUSH_MASK 0xffffffff - -#define IOMMU_AFSR (0x1000 >> 2) -#define IOMMU_AFSR_ERR 0x80000000 /* LE, TO, or BE asserted */ -#define IOMMU_AFSR_LE 0x40000000 /* SBUS reports error after - transaction */ -#define IOMMU_AFSR_TO 0x20000000 /* Write access took more than - 12.8 us. */ -#define IOMMU_AFSR_BE 0x10000000 /* Write access received error - acknowledge */ -#define IOMMU_AFSR_SIZE 0x0e000000 /* Size of transaction causing error */ -#define IOMMU_AFSR_S 0x01000000 /* Sparc was in supervisor mode */ -#define IOMMU_AFSR_RESV 0x00800000 /* Reserved, forced to 0x8 by - hardware */ -#define IOMMU_AFSR_ME 0x00080000 /* Multiple errors occurred */ -#define IOMMU_AFSR_RD 0x00040000 /* A read operation was in progress */ -#define IOMMU_AFSR_FAV 0x00020000 /* IOMMU afar has valid contents */ -#define IOMMU_AFSR_MASK 0xff0fffff - -#define IOMMU_AFAR (0x1004 >> 2) - -#define IOMMU_AER (0x1008 >> 2) /* Arbiter Enable Register */ -#define IOMMU_AER_EN_P0_ARB 0x00000001 /* MBus master 0x8 (Always 1) */ -#define IOMMU_AER_EN_P1_ARB 0x00000002 /* MBus master 0x9 */ -#define IOMMU_AER_EN_P2_ARB 0x00000004 /* MBus master 0xa */ -#define IOMMU_AER_EN_P3_ARB 0x00000008 /* MBus master 0xb */ -#define IOMMU_AER_EN_0 0x00010000 /* SBus slot 0 */ -#define IOMMU_AER_EN_1 0x00020000 /* SBus slot 1 */ -#define IOMMU_AER_EN_2 0x00040000 /* SBus slot 2 */ -#define IOMMU_AER_EN_3 0x00080000 /* SBus slot 3 */ -#define IOMMU_AER_EN_F 0x00100000 /* SBus on-board */ -#define IOMMU_AER_SBW 0x80000000 /* S-to-M asynchronous writes */ -#define IOMMU_AER_MASK 0x801f000f - -#define IOMMU_SBCFG0 (0x1010 >> 2) /* SBUS configration per-slot */ -#define IOMMU_SBCFG1 (0x1014 >> 2) /* SBUS configration per-slot */ -#define IOMMU_SBCFG2 (0x1018 >> 2) /* SBUS configration per-slot */ -#define IOMMU_SBCFG3 (0x101c >> 2) /* SBUS configration per-slot */ -#define IOMMU_SBCFG_SAB30 0x00010000 /* Phys-address bit 30 when - bypass enabled */ -#define IOMMU_SBCFG_BA16 0x00000004 /* Slave supports 16 byte bursts */ -#define IOMMU_SBCFG_BA8 0x00000002 /* Slave supports 8 byte bursts */ -#define IOMMU_SBCFG_BYPASS 0x00000001 /* Bypass IOMMU, treat all addresses - produced by this device as pure - physical. */ -#define IOMMU_SBCFG_MASK 0x00010003 - -#define IOMMU_ARBEN (0x2000 >> 2) /* SBUS arbitration enable */ -#define IOMMU_ARBEN_MASK 0x001f0000 -#define IOMMU_MID 0x00000008 - -#define IOMMU_MASK_ID (0x3018 >> 2) /* Mask ID */ -#define IOMMU_MASK_ID_MASK 0x00ffffff - -#define IOMMU_MSII_MASK 0x26000000 /* microSPARC II mask number */ -#define IOMMU_TS_MASK 0x23000000 /* turboSPARC mask number */ - -/* The format of an iopte in the page tables */ -#define IOPTE_PAGE 0xffffff00 /* Physical page number (PA[35:12]) */ -#define IOPTE_CACHE 0x00000080 /* Cached (in vme IOCACHE or - Viking/MXCC) */ -#define IOPTE_WRITE 0x00000004 /* Writable */ -#define IOPTE_VALID 0x00000002 /* IOPTE is valid */ -#define IOPTE_WAZ 0x00000001 /* Write as zeros */ - -#define IOMMU_PAGE_SHIFT 12 -#define IOMMU_PAGE_SIZE (1 << IOMMU_PAGE_SHIFT) -#define IOMMU_PAGE_MASK ~(IOMMU_PAGE_SIZE - 1) - -#define TYPE_SUN4M_IOMMU "iommu" -#define SUN4M_IOMMU(obj) OBJECT_CHECK(IOMMUState, (obj), TYPE_SUN4M_IOMMU) - -typedef struct IOMMUState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t regs[IOMMU_NREGS]; - hwaddr iostart; - qemu_irq irq; - uint32_t version; -} IOMMUState; - -static uint64_t iommu_mem_read(void *opaque, hwaddr addr, - unsigned size) -{ - IOMMUState *s = opaque; - hwaddr saddr; - uint32_t ret; - - saddr = addr >> 2; - switch (saddr) { - default: - ret = s->regs[saddr]; - break; - case IOMMU_AFAR: - case IOMMU_AFSR: - ret = s->regs[saddr]; - qemu_irq_lower(s->irq); - break; - } - trace_sun4m_iommu_mem_readl(saddr, ret); - return ret; -} - -static void iommu_mem_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - IOMMUState *s = opaque; - hwaddr saddr; - - saddr = addr >> 2; - trace_sun4m_iommu_mem_writel(saddr, val); - switch (saddr) { - case IOMMU_CTRL: - switch (val & IOMMU_CTRL_RNGE) { - case IOMMU_RNGE_16MB: - s->iostart = 0xffffffffff000000ULL; - break; - case IOMMU_RNGE_32MB: - s->iostart = 0xfffffffffe000000ULL; - break; - case IOMMU_RNGE_64MB: - s->iostart = 0xfffffffffc000000ULL; - break; - case IOMMU_RNGE_128MB: - s->iostart = 0xfffffffff8000000ULL; - break; - case IOMMU_RNGE_256MB: - s->iostart = 0xfffffffff0000000ULL; - break; - case IOMMU_RNGE_512MB: - s->iostart = 0xffffffffe0000000ULL; - break; - case IOMMU_RNGE_1GB: - s->iostart = 0xffffffffc0000000ULL; - break; - default: - case IOMMU_RNGE_2GB: - s->iostart = 0xffffffff80000000ULL; - break; - } - trace_sun4m_iommu_mem_writel_ctrl(s->iostart); - s->regs[saddr] = ((val & IOMMU_CTRL_MASK) | s->version); - break; - case IOMMU_BASE: - s->regs[saddr] = val & IOMMU_BASE_MASK; - break; - case IOMMU_TLBFLUSH: - trace_sun4m_iommu_mem_writel_tlbflush(val); - s->regs[saddr] = val & IOMMU_TLBFLUSH_MASK; - break; - case IOMMU_PGFLUSH: - trace_sun4m_iommu_mem_writel_pgflush(val); - s->regs[saddr] = val & IOMMU_PGFLUSH_MASK; - break; - case IOMMU_AFAR: - s->regs[saddr] = val; - qemu_irq_lower(s->irq); - break; - case IOMMU_AER: - s->regs[saddr] = (val & IOMMU_AER_MASK) | IOMMU_AER_EN_P0_ARB; - break; - case IOMMU_AFSR: - s->regs[saddr] = (val & IOMMU_AFSR_MASK) | IOMMU_AFSR_RESV; - qemu_irq_lower(s->irq); - break; - case IOMMU_SBCFG0: - case IOMMU_SBCFG1: - case IOMMU_SBCFG2: - case IOMMU_SBCFG3: - s->regs[saddr] = val & IOMMU_SBCFG_MASK; - break; - case IOMMU_ARBEN: - // XXX implement SBus probing: fault when reading unmapped - // addresses, fault cause and address stored to MMU/IOMMU - s->regs[saddr] = (val & IOMMU_ARBEN_MASK) | IOMMU_MID; - break; - case IOMMU_MASK_ID: - s->regs[saddr] |= val & IOMMU_MASK_ID_MASK; - break; - default: - s->regs[saddr] = val; - break; - } -} - -static const MemoryRegionOps iommu_mem_ops = { - .read = iommu_mem_read, - .write = iommu_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static uint32_t iommu_page_get_flags(IOMMUState *s, hwaddr addr) -{ - uint32_t ret; - hwaddr iopte; - hwaddr pa = addr; - - iopte = s->regs[IOMMU_BASE] << 4; - addr &= ~s->iostart; - iopte += (addr >> (IOMMU_PAGE_SHIFT - 2)) & ~3; - ret = address_space_ldl_be(&address_space_memory, iopte, - MEMTXATTRS_UNSPECIFIED, NULL); - trace_sun4m_iommu_page_get_flags(pa, iopte, ret); - return ret; -} - -static hwaddr iommu_translate_pa(hwaddr addr, - uint32_t pte) -{ - hwaddr pa; - - pa = ((pte & IOPTE_PAGE) << 4) + (addr & ~IOMMU_PAGE_MASK); - trace_sun4m_iommu_translate_pa(addr, pa, pte); - return pa; -} - -static void iommu_bad_addr(IOMMUState *s, hwaddr addr, - int is_write) -{ - trace_sun4m_iommu_bad_addr(addr); - s->regs[IOMMU_AFSR] = IOMMU_AFSR_ERR | IOMMU_AFSR_LE | IOMMU_AFSR_RESV | - IOMMU_AFSR_FAV; - if (!is_write) - s->regs[IOMMU_AFSR] |= IOMMU_AFSR_RD; - s->regs[IOMMU_AFAR] = addr; - qemu_irq_raise(s->irq); -} - -void sparc_iommu_memory_rw(void *opaque, hwaddr addr, - uint8_t *buf, int len, int is_write) -{ - int l; - uint32_t flags; - hwaddr page, phys_addr; - - while (len > 0) { - page = addr & IOMMU_PAGE_MASK; - l = (page + IOMMU_PAGE_SIZE) - addr; - if (l > len) - l = len; - flags = iommu_page_get_flags(opaque, page); - if (!(flags & IOPTE_VALID)) { - iommu_bad_addr(opaque, page, is_write); - return; - } - phys_addr = iommu_translate_pa(addr, flags); - if (is_write) { - if (!(flags & IOPTE_WRITE)) { - iommu_bad_addr(opaque, page, is_write); - return; - } - cpu_physical_memory_write(phys_addr, buf, l); - } else { - cpu_physical_memory_read(phys_addr, buf, l); - } - len -= l; - buf += l; - addr += l; - } -} - -static const VMStateDescription vmstate_iommu = { - .name ="iommu", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, IOMMUState, IOMMU_NREGS), - VMSTATE_UINT64(iostart, IOMMUState), - VMSTATE_END_OF_LIST() - } -}; - -static void iommu_reset(DeviceState *d) -{ - IOMMUState *s = SUN4M_IOMMU(d); - - memset(s->regs, 0, IOMMU_NREGS * 4); - s->iostart = 0; - s->regs[IOMMU_CTRL] = s->version; - s->regs[IOMMU_ARBEN] = IOMMU_MID; - s->regs[IOMMU_AFSR] = IOMMU_AFSR_RESV; - s->regs[IOMMU_AER] = IOMMU_AER_EN_P0_ARB | IOMMU_AER_EN_P1_ARB; - s->regs[IOMMU_MASK_ID] = IOMMU_TS_MASK; -} - -static int iommu_init1(SysBusDevice *dev) -{ - IOMMUState *s = SUN4M_IOMMU(dev); - - sysbus_init_irq(dev, &s->irq); - - memory_region_init_io(&s->iomem, OBJECT(s), &iommu_mem_ops, s, "iommu", - IOMMU_NREGS * sizeof(uint32_t)); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static Property iommu_properties[] = { - DEFINE_PROP_UINT32("version", IOMMUState, version, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void iommu_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = iommu_init1; - dc->reset = iommu_reset; - dc->vmsd = &vmstate_iommu; - dc->props = iommu_properties; -} - -static const TypeInfo iommu_info = { - .name = TYPE_SUN4M_IOMMU, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IOMMUState), - .class_init = iommu_class_init, -}; - -static void iommu_register_types(void) -{ - type_register_static(&iommu_info); -} - -type_init(iommu_register_types) diff --git a/qemu/hw/dma/xilinx_axidma.c b/qemu/hw/dma/xilinx_axidma.c deleted file mode 100644 index a4753e55a..000000000 --- a/qemu/hw/dma/xilinx_axidma.c +++ /dev/null @@ -1,666 +0,0 @@ -/* - * QEMU model of Xilinx AXI-DMA block. - * - * Copyright (c) 2011 Edgar E. Iglesias. - * - * 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/sysbus.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include "hw/ptimer.h" -#include "qemu/log.h" -#include "qemu/main-loop.h" - -#include "hw/stream.h" - -#define D(x) - -#define TYPE_XILINX_AXI_DMA "xlnx.axi-dma" -#define TYPE_XILINX_AXI_DMA_DATA_STREAM "xilinx-axi-dma-data-stream" -#define TYPE_XILINX_AXI_DMA_CONTROL_STREAM "xilinx-axi-dma-control-stream" - -#define XILINX_AXI_DMA(obj) \ - OBJECT_CHECK(XilinxAXIDMA, (obj), TYPE_XILINX_AXI_DMA) - -#define XILINX_AXI_DMA_DATA_STREAM(obj) \ - OBJECT_CHECK(XilinxAXIDMAStreamSlave, (obj),\ - TYPE_XILINX_AXI_DMA_DATA_STREAM) - -#define XILINX_AXI_DMA_CONTROL_STREAM(obj) \ - OBJECT_CHECK(XilinxAXIDMAStreamSlave, (obj),\ - TYPE_XILINX_AXI_DMA_CONTROL_STREAM) - -#define R_DMACR (0x00 / 4) -#define R_DMASR (0x04 / 4) -#define R_CURDESC (0x08 / 4) -#define R_TAILDESC (0x10 / 4) -#define R_MAX (0x30 / 4) - -#define CONTROL_PAYLOAD_WORDS 5 -#define CONTROL_PAYLOAD_SIZE (CONTROL_PAYLOAD_WORDS * (sizeof(uint32_t))) - -typedef struct XilinxAXIDMA XilinxAXIDMA; -typedef struct XilinxAXIDMAStreamSlave XilinxAXIDMAStreamSlave; - -enum { - DMACR_RUNSTOP = 1, - DMACR_TAILPTR_MODE = 2, - DMACR_RESET = 4 -}; - -enum { - DMASR_HALTED = 1, - DMASR_IDLE = 2, - DMASR_IOC_IRQ = 1 << 12, - DMASR_DLY_IRQ = 1 << 13, - - DMASR_IRQ_MASK = 7 << 12 -}; - -struct SDesc { - uint64_t nxtdesc; - uint64_t buffer_address; - uint64_t reserved; - uint32_t control; - uint32_t status; - uint8_t app[CONTROL_PAYLOAD_SIZE]; -}; - -enum { - SDESC_CTRL_EOF = (1 << 26), - SDESC_CTRL_SOF = (1 << 27), - - SDESC_CTRL_LEN_MASK = (1 << 23) - 1 -}; - -enum { - SDESC_STATUS_EOF = (1 << 26), - SDESC_STATUS_SOF_BIT = 27, - SDESC_STATUS_SOF = (1 << SDESC_STATUS_SOF_BIT), - SDESC_STATUS_COMPLETE = (1 << 31) -}; - -struct Stream { - QEMUBH *bh; - ptimer_state *ptimer; - qemu_irq irq; - - int nr; - - struct SDesc desc; - int pos; - unsigned int complete_cnt; - uint32_t regs[R_MAX]; - uint8_t app[20]; -}; - -struct XilinxAXIDMAStreamSlave { - Object parent; - - struct XilinxAXIDMA *dma; -}; - -struct XilinxAXIDMA { - SysBusDevice busdev; - MemoryRegion iomem; - uint32_t freqhz; - StreamSlave *tx_data_dev; - StreamSlave *tx_control_dev; - XilinxAXIDMAStreamSlave rx_data_dev; - XilinxAXIDMAStreamSlave rx_control_dev; - - struct Stream streams[2]; - - StreamCanPushNotifyFn notify; - void *notify_opaque; -}; - -/* - * Helper calls to extract info from descriptors and other trivial - * state from regs. - */ -static inline int stream_desc_sof(struct SDesc *d) -{ - return d->control & SDESC_CTRL_SOF; -} - -static inline int stream_desc_eof(struct SDesc *d) -{ - return d->control & SDESC_CTRL_EOF; -} - -static inline int stream_resetting(struct Stream *s) -{ - return !!(s->regs[R_DMACR] & DMACR_RESET); -} - -static inline int stream_running(struct Stream *s) -{ - return s->regs[R_DMACR] & DMACR_RUNSTOP; -} - -static inline int stream_idle(struct Stream *s) -{ - return !!(s->regs[R_DMASR] & DMASR_IDLE); -} - -static void stream_reset(struct Stream *s) -{ - s->regs[R_DMASR] = DMASR_HALTED; /* starts up halted. */ - s->regs[R_DMACR] = 1 << 16; /* Starts with one in compl threshold. */ -} - -/* Map an offset addr into a channel index. */ -static inline int streamid_from_addr(hwaddr addr) -{ - int sid; - - sid = addr / (0x30); - sid &= 1; - return sid; -} - -static void stream_desc_load(struct Stream *s, hwaddr addr) -{ - struct SDesc *d = &s->desc; - - cpu_physical_memory_read(addr, d, sizeof *d); - - /* Convert from LE into host endianness. */ - d->buffer_address = le64_to_cpu(d->buffer_address); - d->nxtdesc = le64_to_cpu(d->nxtdesc); - d->control = le32_to_cpu(d->control); - d->status = le32_to_cpu(d->status); -} - -static void stream_desc_store(struct Stream *s, hwaddr addr) -{ - struct SDesc *d = &s->desc; - - /* Convert from host endianness into LE. */ - d->buffer_address = cpu_to_le64(d->buffer_address); - d->nxtdesc = cpu_to_le64(d->nxtdesc); - d->control = cpu_to_le32(d->control); - d->status = cpu_to_le32(d->status); - cpu_physical_memory_write(addr, d, sizeof *d); -} - -static void stream_update_irq(struct Stream *s) -{ - unsigned int pending, mask, irq; - - pending = s->regs[R_DMASR] & DMASR_IRQ_MASK; - mask = s->regs[R_DMACR] & DMASR_IRQ_MASK; - - irq = pending & mask; - - qemu_set_irq(s->irq, !!irq); -} - -static void stream_reload_complete_cnt(struct Stream *s) -{ - unsigned int comp_th; - comp_th = (s->regs[R_DMACR] >> 16) & 0xff; - s->complete_cnt = comp_th; -} - -static void timer_hit(void *opaque) -{ - struct Stream *s = opaque; - - stream_reload_complete_cnt(s); - s->regs[R_DMASR] |= DMASR_DLY_IRQ; - stream_update_irq(s); -} - -static void stream_complete(struct Stream *s) -{ - unsigned int comp_delay; - - /* Start the delayed timer. */ - comp_delay = s->regs[R_DMACR] >> 24; - if (comp_delay) { - ptimer_stop(s->ptimer); - ptimer_set_count(s->ptimer, comp_delay); - ptimer_run(s->ptimer, 1); - } - - s->complete_cnt--; - if (s->complete_cnt == 0) { - /* Raise the IOC irq. */ - s->regs[R_DMASR] |= DMASR_IOC_IRQ; - stream_reload_complete_cnt(s); - } -} - -static void stream_process_mem2s(struct Stream *s, StreamSlave *tx_data_dev, - StreamSlave *tx_control_dev) -{ - uint32_t prev_d; - unsigned char txbuf[16 * 1024]; - unsigned int txlen; - - if (!stream_running(s) || stream_idle(s)) { - return; - } - - while (1) { - stream_desc_load(s, s->regs[R_CURDESC]); - - if (s->desc.status & SDESC_STATUS_COMPLETE) { - s->regs[R_DMASR] |= DMASR_HALTED; - break; - } - - if (stream_desc_sof(&s->desc)) { - s->pos = 0; - stream_push(tx_control_dev, s->desc.app, sizeof(s->desc.app)); - } - - txlen = s->desc.control & SDESC_CTRL_LEN_MASK; - if ((txlen + s->pos) > sizeof txbuf) { - hw_error("%s: too small internal txbuf! %d\n", __func__, - txlen + s->pos); - } - - cpu_physical_memory_read(s->desc.buffer_address, - txbuf + s->pos, txlen); - s->pos += txlen; - - if (stream_desc_eof(&s->desc)) { - stream_push(tx_data_dev, txbuf, s->pos); - s->pos = 0; - stream_complete(s); - } - - /* Update the descriptor. */ - s->desc.status = txlen | SDESC_STATUS_COMPLETE; - stream_desc_store(s, s->regs[R_CURDESC]); - - /* Advance. */ - prev_d = s->regs[R_CURDESC]; - s->regs[R_CURDESC] = s->desc.nxtdesc; - if (prev_d == s->regs[R_TAILDESC]) { - s->regs[R_DMASR] |= DMASR_IDLE; - break; - } - } -} - -static size_t stream_process_s2mem(struct Stream *s, unsigned char *buf, - size_t len) -{ - uint32_t prev_d; - unsigned int rxlen; - size_t pos = 0; - int sof = 1; - - if (!stream_running(s) || stream_idle(s)) { - return 0; - } - - while (len) { - stream_desc_load(s, s->regs[R_CURDESC]); - - if (s->desc.status & SDESC_STATUS_COMPLETE) { - s->regs[R_DMASR] |= DMASR_HALTED; - break; - } - - rxlen = s->desc.control & SDESC_CTRL_LEN_MASK; - if (rxlen > len) { - /* It fits. */ - rxlen = len; - } - - cpu_physical_memory_write(s->desc.buffer_address, buf + pos, rxlen); - len -= rxlen; - pos += rxlen; - - /* Update the descriptor. */ - if (!len) { - stream_complete(s); - memcpy(s->desc.app, s->app, sizeof(s->desc.app)); - s->desc.status |= SDESC_STATUS_EOF; - } - - s->desc.status |= sof << SDESC_STATUS_SOF_BIT; - s->desc.status |= SDESC_STATUS_COMPLETE; - stream_desc_store(s, s->regs[R_CURDESC]); - sof = 0; - - /* Advance. */ - prev_d = s->regs[R_CURDESC]; - s->regs[R_CURDESC] = s->desc.nxtdesc; - if (prev_d == s->regs[R_TAILDESC]) { - s->regs[R_DMASR] |= DMASR_IDLE; - break; - } - } - - return pos; -} - -static void xilinx_axidma_reset(DeviceState *dev) -{ - int i; - XilinxAXIDMA *s = XILINX_AXI_DMA(dev); - - for (i = 0; i < 2; i++) { - stream_reset(&s->streams[i]); - } -} - -static size_t -xilinx_axidma_control_stream_push(StreamSlave *obj, unsigned char *buf, - size_t len) -{ - XilinxAXIDMAStreamSlave *cs = XILINX_AXI_DMA_CONTROL_STREAM(obj); - struct Stream *s = &cs->dma->streams[1]; - - if (len != CONTROL_PAYLOAD_SIZE) { - hw_error("AXI DMA requires %d byte control stream payload\n", - (int)CONTROL_PAYLOAD_SIZE); - } - - memcpy(s->app, buf, len); - return len; -} - -static bool -xilinx_axidma_data_stream_can_push(StreamSlave *obj, - StreamCanPushNotifyFn notify, - void *notify_opaque) -{ - XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(obj); - struct Stream *s = &ds->dma->streams[1]; - - if (!stream_running(s) || stream_idle(s)) { - ds->dma->notify = notify; - ds->dma->notify_opaque = notify_opaque; - return false; - } - - return true; -} - -static size_t -xilinx_axidma_data_stream_push(StreamSlave *obj, unsigned char *buf, size_t len) -{ - XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(obj); - struct Stream *s = &ds->dma->streams[1]; - size_t ret; - - ret = stream_process_s2mem(s, buf, len); - stream_update_irq(s); - return ret; -} - -static uint64_t axidma_read(void *opaque, hwaddr addr, - unsigned size) -{ - XilinxAXIDMA *d = opaque; - struct Stream *s; - uint32_t r = 0; - int sid; - - sid = streamid_from_addr(addr); - s = &d->streams[sid]; - - addr = addr % 0x30; - addr >>= 2; - switch (addr) { - case R_DMACR: - /* Simulate one cycles reset delay. */ - s->regs[addr] &= ~DMACR_RESET; - r = s->regs[addr]; - break; - case R_DMASR: - s->regs[addr] &= 0xffff; - s->regs[addr] |= (s->complete_cnt & 0xff) << 16; - s->regs[addr] |= (ptimer_get_count(s->ptimer) & 0xff) << 24; - r = s->regs[addr]; - break; - default: - r = s->regs[addr]; - D(qemu_log("%s ch=%d addr=" TARGET_FMT_plx " v=%x\n", - __func__, sid, addr * 4, r)); - break; - } - return r; - -} - -static void axidma_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - XilinxAXIDMA *d = opaque; - struct Stream *s; - int sid; - - sid = streamid_from_addr(addr); - s = &d->streams[sid]; - - addr = addr % 0x30; - addr >>= 2; - switch (addr) { - case R_DMACR: - /* Tailptr mode is always on. */ - value |= DMACR_TAILPTR_MODE; - /* Remember our previous reset state. */ - value |= (s->regs[addr] & DMACR_RESET); - s->regs[addr] = value; - - if (value & DMACR_RESET) { - stream_reset(s); - } - - if ((value & 1) && !stream_resetting(s)) { - /* Start processing. */ - s->regs[R_DMASR] &= ~(DMASR_HALTED | DMASR_IDLE); - } - stream_reload_complete_cnt(s); - break; - - case R_DMASR: - /* Mask away write to clear irq lines. */ - value &= ~(value & DMASR_IRQ_MASK); - s->regs[addr] = value; - break; - - case R_TAILDESC: - s->regs[addr] = value; - s->regs[R_DMASR] &= ~DMASR_IDLE; /* Not idle. */ - if (!sid) { - stream_process_mem2s(s, d->tx_data_dev, d->tx_control_dev); - } - break; - default: - D(qemu_log("%s: ch=%d addr=" TARGET_FMT_plx " v=%x\n", - __func__, sid, addr * 4, (unsigned)value)); - s->regs[addr] = value; - break; - } - if (sid == 1 && d->notify) { - StreamCanPushNotifyFn notifytmp = d->notify; - d->notify = NULL; - notifytmp(d->notify_opaque); - } - stream_update_irq(s); -} - -static const MemoryRegionOps axidma_ops = { - .read = axidma_read, - .write = axidma_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void xilinx_axidma_realize(DeviceState *dev, Error **errp) -{ - XilinxAXIDMA *s = XILINX_AXI_DMA(dev); - XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(&s->rx_data_dev); - XilinxAXIDMAStreamSlave *cs = XILINX_AXI_DMA_CONTROL_STREAM( - &s->rx_control_dev); - Error *local_err = NULL; - - object_property_add_link(OBJECT(ds), "dma", TYPE_XILINX_AXI_DMA, - (Object **)&ds->dma, - object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &local_err); - object_property_add_link(OBJECT(cs), "dma", TYPE_XILINX_AXI_DMA, - (Object **)&cs->dma, - object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &local_err); - if (local_err) { - goto xilinx_axidma_realize_fail; - } - object_property_set_link(OBJECT(ds), OBJECT(s), "dma", &local_err); - object_property_set_link(OBJECT(cs), OBJECT(s), "dma", &local_err); - if (local_err) { - goto xilinx_axidma_realize_fail; - } - - int i; - - for (i = 0; i < 2; i++) { - struct Stream *st = &s->streams[i]; - - st->nr = i; - st->bh = qemu_bh_new(timer_hit, st); - st->ptimer = ptimer_init(st->bh); - ptimer_set_freq(st->ptimer, s->freqhz); - } - return; - -xilinx_axidma_realize_fail: - if (!*errp) { - *errp = local_err; - } -} - -static void xilinx_axidma_init(Object *obj) -{ - XilinxAXIDMA *s = XILINX_AXI_DMA(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE, - (Object **)&s->tx_data_dev, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); - object_property_add_link(obj, "axistream-control-connected", - TYPE_STREAM_SLAVE, - (Object **)&s->tx_control_dev, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); - - object_initialize(&s->rx_data_dev, sizeof(s->rx_data_dev), - TYPE_XILINX_AXI_DMA_DATA_STREAM); - object_initialize(&s->rx_control_dev, sizeof(s->rx_control_dev), - TYPE_XILINX_AXI_DMA_CONTROL_STREAM); - object_property_add_child(OBJECT(s), "axistream-connected-target", - (Object *)&s->rx_data_dev, &error_abort); - object_property_add_child(OBJECT(s), "axistream-control-connected-target", - (Object *)&s->rx_control_dev, &error_abort); - - sysbus_init_irq(sbd, &s->streams[0].irq); - sysbus_init_irq(sbd, &s->streams[1].irq); - - memory_region_init_io(&s->iomem, obj, &axidma_ops, s, - "xlnx.axi-dma", R_MAX * 4 * 2); - sysbus_init_mmio(sbd, &s->iomem); -} - -static Property axidma_properties[] = { - DEFINE_PROP_UINT32("freqhz", XilinxAXIDMA, freqhz, 50000000), - DEFINE_PROP_END_OF_LIST(), -}; - -static void axidma_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = xilinx_axidma_realize, - dc->reset = xilinx_axidma_reset; - dc->props = axidma_properties; -} - -static StreamSlaveClass xilinx_axidma_data_stream_class = { - .push = xilinx_axidma_data_stream_push, - .can_push = xilinx_axidma_data_stream_can_push, -}; - -static StreamSlaveClass xilinx_axidma_control_stream_class = { - .push = xilinx_axidma_control_stream_push, -}; - -static void xilinx_axidma_stream_class_init(ObjectClass *klass, void *data) -{ - StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass); - - ssc->push = ((StreamSlaveClass *)data)->push; - ssc->can_push = ((StreamSlaveClass *)data)->can_push; -} - -static const TypeInfo axidma_info = { - .name = TYPE_XILINX_AXI_DMA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(XilinxAXIDMA), - .class_init = axidma_class_init, - .instance_init = xilinx_axidma_init, -}; - -static const TypeInfo xilinx_axidma_data_stream_info = { - .name = TYPE_XILINX_AXI_DMA_DATA_STREAM, - .parent = TYPE_OBJECT, - .instance_size = sizeof(struct XilinxAXIDMAStreamSlave), - .class_init = xilinx_axidma_stream_class_init, - .class_data = &xilinx_axidma_data_stream_class, - .interfaces = (InterfaceInfo[]) { - { TYPE_STREAM_SLAVE }, - { } - } -}; - -static const TypeInfo xilinx_axidma_control_stream_info = { - .name = TYPE_XILINX_AXI_DMA_CONTROL_STREAM, - .parent = TYPE_OBJECT, - .instance_size = sizeof(struct XilinxAXIDMAStreamSlave), - .class_init = xilinx_axidma_stream_class_init, - .class_data = &xilinx_axidma_control_stream_class, - .interfaces = (InterfaceInfo[]) { - { TYPE_STREAM_SLAVE }, - { } - } -}; - -static void xilinx_axidma_register_types(void) -{ - type_register_static(&axidma_info); - type_register_static(&xilinx_axidma_data_stream_info); - type_register_static(&xilinx_axidma_control_stream_info); -} - -type_init(xilinx_axidma_register_types) diff --git a/qemu/hw/gpio/Makefile.objs b/qemu/hw/gpio/Makefile.objs deleted file mode 100644 index a43c7cf44..000000000 --- a/qemu/hw/gpio/Makefile.objs +++ /dev/null @@ -1,9 +0,0 @@ -common-obj-$(CONFIG_MAX7310) += max7310.o -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 deleted file mode 100644 index ef287727b..000000000 --- a/qemu/hw/gpio/gpio_key.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * GPIO key - * - * Copyright (c) 2016 Linaro Limited - * - * Author: Shannon Zhao - * - * 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 . - */ - -#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 deleted file mode 100644 index ed7e247f5..000000000 --- a/qemu/hw/gpio/imx_gpio.c +++ /dev/null @@ -1,350 +0,0 @@ -/* - * i.MX processors GPIO emulation. - * - * Copyright (C) 2015 Jean-Christophe Dubois - * - * 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 . - */ - -#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 deleted file mode 100644 index 1bd5eaf91..000000000 --- a/qemu/hw/gpio/max7310.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * MAX7310 8-port GPIO expansion chip. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This file is licensed under GNU GPL. - */ - -#include "qemu/osdep.h" -#include "hw/i2c/i2c.h" - -#define TYPE_MAX7310 "max7310" -#define MAX7310(obj) OBJECT_CHECK(MAX7310State, (obj), TYPE_MAX7310) - -typedef struct MAX7310State { - I2CSlave parent_obj; - - int i2c_command_byte; - int len; - - uint8_t level; - uint8_t direction; - uint8_t polarity; - uint8_t status; - uint8_t command; - qemu_irq handler[8]; - qemu_irq *gpio_in; -} MAX7310State; - -static void max7310_reset(DeviceState *dev) -{ - MAX7310State *s = MAX7310(dev); - - s->level &= s->direction; - s->direction = 0xff; - s->polarity = 0xf0; - s->status = 0x01; - s->command = 0x00; -} - -static int max7310_rx(I2CSlave *i2c) -{ - MAX7310State *s = MAX7310(i2c); - - switch (s->command) { - case 0x00: /* Input port */ - return s->level ^ s->polarity; - break; - - case 0x01: /* Output port */ - return s->level & ~s->direction; - break; - - case 0x02: /* Polarity inversion */ - return s->polarity; - - case 0x03: /* Configuration */ - return s->direction; - - case 0x04: /* Timeout */ - return s->status; - break; - - case 0xff: /* Reserved */ - return 0xff; - - default: -#ifdef VERBOSE - printf("%s: unknown register %02x\n", __FUNCTION__, s->command); -#endif - break; - } - return 0xff; -} - -static int max7310_tx(I2CSlave *i2c, uint8_t data) -{ - MAX7310State *s = MAX7310(i2c); - uint8_t diff; - int line; - - if (s->len ++ > 1) { -#ifdef VERBOSE - printf("%s: message too long (%i bytes)\n", __FUNCTION__, s->len); -#endif - return 1; - } - - if (s->i2c_command_byte) { - s->command = data; - s->i2c_command_byte = 0; - return 0; - } - - switch (s->command) { - case 0x01: /* Output port */ - for (diff = (data ^ s->level) & ~s->direction; diff; - diff &= ~(1 << line)) { - line = ctz32(diff); - if (s->handler[line]) - qemu_set_irq(s->handler[line], (data >> line) & 1); - } - s->level = (s->level & s->direction) | (data & ~s->direction); - break; - - case 0x02: /* Polarity inversion */ - s->polarity = data; - break; - - case 0x03: /* Configuration */ - s->level &= ~(s->direction ^ data); - s->direction = data; - break; - - case 0x04: /* Timeout */ - s->status = data; - break; - - case 0x00: /* Input port - ignore writes */ - break; - default: -#ifdef VERBOSE - printf("%s: unknown register %02x\n", __FUNCTION__, s->command); -#endif - return 1; - } - - return 0; -} - -static void max7310_event(I2CSlave *i2c, enum i2c_event event) -{ - MAX7310State *s = MAX7310(i2c); - s->len = 0; - - switch (event) { - case I2C_START_SEND: - s->i2c_command_byte = 1; - break; - case I2C_FINISH: -#ifdef VERBOSE - if (s->len == 1) - printf("%s: message too short (%i bytes)\n", __FUNCTION__, s->len); -#endif - break; - default: - break; - } -} - -static const VMStateDescription vmstate_max7310 = { - .name = "max7310", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_INT32(i2c_command_byte, MAX7310State), - VMSTATE_INT32(len, MAX7310State), - VMSTATE_UINT8(level, MAX7310State), - VMSTATE_UINT8(direction, MAX7310State), - VMSTATE_UINT8(polarity, MAX7310State), - VMSTATE_UINT8(status, MAX7310State), - VMSTATE_UINT8(command, MAX7310State), - VMSTATE_I2C_SLAVE(parent_obj, MAX7310State), - VMSTATE_END_OF_LIST() - } -}; - -static void max7310_gpio_set(void *opaque, int line, int level) -{ - MAX7310State *s = (MAX7310State *) opaque; - if (line >= ARRAY_SIZE(s->handler) || line < 0) - hw_error("bad GPIO line"); - - if (level) - s->level |= s->direction & (1 << line); - else - s->level &= ~(s->direction & (1 << line)); -} - -/* MAX7310 is SMBus-compatible (can be used with only SMBus protocols), - * but also accepts sequences that are not SMBus so return an I2C device. */ -static int max7310_init(I2CSlave *i2c) -{ - MAX7310State *s = MAX7310(i2c); - - qdev_init_gpio_in(&i2c->qdev, max7310_gpio_set, 8); - qdev_init_gpio_out(&i2c->qdev, s->handler, 8); - - return 0; -} - -static void max7310_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - - k->init = max7310_init; - k->event = max7310_event; - k->recv = max7310_rx; - k->send = max7310_tx; - dc->reset = max7310_reset; - dc->vmsd = &vmstate_max7310; -} - -static const TypeInfo max7310_info = { - .name = TYPE_MAX7310, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(MAX7310State), - .class_init = max7310_class_init, -}; - -static void max7310_register_types(void) -{ - type_register_static(&max7310_info); -} - -type_init(max7310_register_types) diff --git a/qemu/hw/gpio/mpc8xxx.c b/qemu/hw/gpio/mpc8xxx.c deleted file mode 100644 index d14971946..000000000 --- a/qemu/hw/gpio/mpc8xxx.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * GPIO Controller for a lot of Freescale SoCs - * - * Copyright (C) 2014 Freescale Semiconductor, Inc. All rights reserved. - * - * Author: Alexander Graf, - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" - -#define TYPE_MPC8XXX_GPIO "mpc8xxx_gpio" -#define MPC8XXX_GPIO(obj) OBJECT_CHECK(MPC8XXXGPIOState, (obj), TYPE_MPC8XXX_GPIO) - -typedef struct MPC8XXXGPIOState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq irq; - qemu_irq out[32]; - - uint32_t dir; - uint32_t odr; - uint32_t dat; - uint32_t ier; - uint32_t imr; - uint32_t icr; -} MPC8XXXGPIOState; - -static const VMStateDescription vmstate_mpc8xxx_gpio = { - .name = "mpc8xxx_gpio", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(dir, MPC8XXXGPIOState), - VMSTATE_UINT32(odr, MPC8XXXGPIOState), - VMSTATE_UINT32(dat, MPC8XXXGPIOState), - VMSTATE_UINT32(ier, MPC8XXXGPIOState), - VMSTATE_UINT32(imr, MPC8XXXGPIOState), - VMSTATE_UINT32(icr, MPC8XXXGPIOState), - VMSTATE_END_OF_LIST() - } -}; - -static void mpc8xxx_gpio_update(MPC8XXXGPIOState *s) -{ - qemu_set_irq(s->irq, !!(s->ier & s->imr)); -} - -static uint64_t mpc8xxx_gpio_read(void *opaque, hwaddr offset, - unsigned size) -{ - MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque; - - if (size != 4) { - /* All registers are 32bit */ - return 0; - } - - switch (offset) { - case 0x0: /* Direction */ - return s->dir; - case 0x4: /* Open Drain */ - return s->odr; - case 0x8: /* Data */ - return s->dat; - case 0xC: /* Interrupt Event */ - return s->ier; - case 0x10: /* Interrupt Mask */ - return s->imr; - case 0x14: /* Interrupt Control */ - return s->icr; - default: - return 0; - } -} - -static void mpc8xxx_write_data(MPC8XXXGPIOState *s, uint32_t new_data) -{ - uint32_t old_data = s->dat; - uint32_t diff = old_data ^ new_data; - int i; - - for (i = 0; i < 32; i++) { - uint32_t mask = 0x80000000 >> i; - if (!(diff & mask)) { - continue; - } - - if (s->dir & mask) { - /* Output */ - qemu_set_irq(s->out[i], (new_data & mask) != 0); - } - } - - s->dat = new_data; -} - -static void mpc8xxx_gpio_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque; - - if (size != 4) { - /* All registers are 32bit */ - return; - } - - switch (offset) { - case 0x0: /* Direction */ - s->dir = value; - break; - case 0x4: /* Open Drain */ - s->odr = value; - break; - case 0x8: /* Data */ - mpc8xxx_write_data(s, value); - break; - case 0xC: /* Interrupt Event */ - s->ier &= ~value; - break; - case 0x10: /* Interrupt Mask */ - s->imr = value; - break; - case 0x14: /* Interrupt Control */ - s->icr = value; - break; - } - - mpc8xxx_gpio_update(s); -} - -static void mpc8xxx_gpio_reset(MPC8XXXGPIOState *s) -{ - s->dir = 0; - s->odr = 0; - s->dat = 0; - s->ier = 0; - s->imr = 0; - s->icr = 0; -} - -static void mpc8xxx_gpio_set_irq(void * opaque, int irq, int level) -{ - MPC8XXXGPIOState *s = (MPC8XXXGPIOState *)opaque; - uint32_t mask; - - mask = 0x80000000 >> irq; - if ((s->dir & mask) == 0) { - uint32_t old_value = s->dat & mask; - - s->dat &= ~mask; - if (level) - s->dat |= mask; - - if (!(s->icr & irq) || (old_value && !level)) { - s->ier |= mask; - } - - mpc8xxx_gpio_update(s); - } -} - -static const MemoryRegionOps mpc8xxx_gpio_ops = { - .read = mpc8xxx_gpio_read, - .write = mpc8xxx_gpio_write, - .endianness = DEVICE_BIG_ENDIAN, -}; - -static int mpc8xxx_gpio_initfn(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - MPC8XXXGPIOState *s = MPC8XXX_GPIO(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &mpc8xxx_gpio_ops, s, "mpc8xxx_gpio", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - qdev_init_gpio_in(dev, mpc8xxx_gpio_set_irq, 32); - qdev_init_gpio_out(dev, s->out, 32); - mpc8xxx_gpio_reset(s); - return 0; -} - -static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = mpc8xxx_gpio_initfn; - dc->vmsd = &vmstate_mpc8xxx_gpio; -} - -static const TypeInfo mpc8xxx_gpio_info = { - .name = TYPE_MPC8XXX_GPIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MPC8XXXGPIOState), - .class_init = mpc8xxx_gpio_class_init, -}; - -static void mpc8xxx_gpio_register_types(void) -{ - type_register_static(&mpc8xxx_gpio_info); -} - -type_init(mpc8xxx_gpio_register_types) diff --git a/qemu/hw/gpio/omap_gpio.c b/qemu/hw/gpio/omap_gpio.c deleted file mode 100644 index 9b1b004fc..000000000 --- a/qemu/hw/gpio/omap_gpio.c +++ /dev/null @@ -1,822 +0,0 @@ -/* - * TI OMAP processors GPIO emulation. - * - * Copyright (C) 2006-2008 Andrzej Zaborowski - * Copyright (C) 2007-2009 Nokia Corporation - * - * 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 . - */ - -#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; - qemu_irq handler[16]; - - uint16_t inputs; - uint16_t outputs; - uint16_t dir; - uint16_t edge; - uint16_t mask; - uint16_t ints; - uint16_t pins; -}; - -#define TYPE_OMAP1_GPIO "omap-gpio" -#define OMAP1_GPIO(obj) \ - OBJECT_CHECK(struct omap_gpif_s, (obj), TYPE_OMAP1_GPIO) - -struct omap_gpif_s { - SysBusDevice parent_obj; - - MemoryRegion iomem; - int mpu_model; - void *clk; - struct omap_gpio_s omap1; -}; - -/* General-Purpose I/O of OMAP1 */ -static void omap_gpio_set(void *opaque, int line, int level) -{ - struct omap_gpio_s *s = &((struct omap_gpif_s *) opaque)->omap1; - uint16_t prev = s->inputs; - - if (level) - s->inputs |= 1 << line; - else - s->inputs &= ~(1 << line); - - if (((s->edge & s->inputs & ~prev) | (~s->edge & ~s->inputs & prev)) & - (1 << line) & s->dir & ~s->mask) { - s->ints |= 1 << line; - qemu_irq_raise(s->irq); - } -} - -static uint64_t omap_gpio_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - - if (size != 2) { - return omap_badwidth_read16(opaque, addr); - } - - switch (offset) { - case 0x00: /* DATA_INPUT */ - return s->inputs & s->pins; - - case 0x04: /* DATA_OUTPUT */ - return s->outputs; - - case 0x08: /* DIRECTION_CONTROL */ - return s->dir; - - case 0x0c: /* INTERRUPT_CONTROL */ - return s->edge; - - case 0x10: /* INTERRUPT_MASK */ - return s->mask; - - case 0x14: /* INTERRUPT_STATUS */ - return s->ints; - - case 0x18: /* PIN_CONTROL (not in OMAP310) */ - OMAP_BAD_REG(addr); - return s->pins; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_gpio_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - uint16_t diff; - int ln; - - if (size != 2) { - omap_badwidth_write16(opaque, addr, value); - return; - } - - switch (offset) { - case 0x00: /* DATA_INPUT */ - OMAP_RO_REG(addr); - return; - - case 0x04: /* DATA_OUTPUT */ - diff = (s->outputs ^ value) & ~s->dir; - s->outputs = value; - while ((ln = ctz32(diff)) != 32) { - if (s->handler[ln]) - qemu_set_irq(s->handler[ln], (value >> ln) & 1); - diff &= ~(1 << ln); - } - break; - - case 0x08: /* DIRECTION_CONTROL */ - diff = s->outputs & (s->dir ^ value); - s->dir = value; - - value = s->outputs & ~s->dir; - while ((ln = ctz32(diff)) != 32) { - if (s->handler[ln]) - qemu_set_irq(s->handler[ln], (value >> ln) & 1); - diff &= ~(1 << ln); - } - break; - - case 0x0c: /* INTERRUPT_CONTROL */ - s->edge = value; - break; - - case 0x10: /* INTERRUPT_MASK */ - s->mask = value; - break; - - case 0x14: /* INTERRUPT_STATUS */ - s->ints &= ~value; - if (!s->ints) - qemu_irq_lower(s->irq); - break; - - case 0x18: /* PIN_CONTROL (not in OMAP310 TRM) */ - OMAP_BAD_REG(addr); - s->pins = value; - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -/* *Some* sources say the memory region is 32-bit. */ -static const MemoryRegionOps omap_gpio_ops = { - .read = omap_gpio_read, - .write = omap_gpio_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_gpio_reset(struct omap_gpio_s *s) -{ - s->inputs = 0; - s->outputs = ~0; - s->dir = ~0; - s->edge = ~0; - s->mask = ~0; - s->ints = 0; - s->pins = ~0; -} - -struct omap2_gpio_s { - qemu_irq irq[2]; - qemu_irq wkup; - qemu_irq *handler; - MemoryRegion iomem; - - uint8_t revision; - uint8_t config[2]; - uint32_t inputs; - uint32_t outputs; - uint32_t dir; - uint32_t level[2]; - uint32_t edge[2]; - uint32_t mask[2]; - uint32_t wumask; - uint32_t ints[2]; - uint32_t debounce; - uint8_t delay; -}; - -#define TYPE_OMAP2_GPIO "omap2-gpio" -#define OMAP2_GPIO(obj) \ - OBJECT_CHECK(struct omap2_gpif_s, (obj), TYPE_OMAP2_GPIO) - -struct omap2_gpif_s { - SysBusDevice parent_obj; - - MemoryRegion iomem; - int mpu_model; - void *iclk; - void *fclk[6]; - int modulecount; - struct omap2_gpio_s *modules; - qemu_irq *handler; - int autoidle; - int gpo; -}; - -/* General-Purpose Interface of OMAP2/3 */ -static inline void omap2_gpio_module_int_update(struct omap2_gpio_s *s, - int line) -{ - qemu_set_irq(s->irq[line], s->ints[line] & s->mask[line]); -} - -static void omap2_gpio_module_wake(struct omap2_gpio_s *s, int line) -{ - if (!(s->config[0] & (1 << 2))) /* ENAWAKEUP */ - return; - if (!(s->config[0] & (3 << 3))) /* Force Idle */ - return; - if (!(s->wumask & (1 << line))) - return; - - qemu_irq_raise(s->wkup); -} - -static inline void omap2_gpio_module_out_update(struct omap2_gpio_s *s, - uint32_t diff) -{ - int ln; - - s->outputs ^= diff; - diff &= ~s->dir; - while ((ln = ctz32(diff)) != 32) { - qemu_set_irq(s->handler[ln], (s->outputs >> ln) & 1); - diff &= ~(1 << ln); - } -} - -static void omap2_gpio_module_level_update(struct omap2_gpio_s *s, int line) -{ - s->ints[line] |= s->dir & - ((s->inputs & s->level[1]) | (~s->inputs & s->level[0])); - omap2_gpio_module_int_update(s, line); -} - -static inline void omap2_gpio_module_int(struct omap2_gpio_s *s, int line) -{ - s->ints[0] |= 1 << line; - omap2_gpio_module_int_update(s, 0); - s->ints[1] |= 1 << line; - omap2_gpio_module_int_update(s, 1); - omap2_gpio_module_wake(s, line); -} - -static void omap2_gpio_set(void *opaque, int line, int level) -{ - struct omap2_gpif_s *p = opaque; - struct omap2_gpio_s *s = &p->modules[line >> 5]; - - line &= 31; - if (level) { - if (s->dir & (1 << line) & ((~s->inputs & s->edge[0]) | s->level[1])) - omap2_gpio_module_int(s, line); - s->inputs |= 1 << line; - } else { - if (s->dir & (1 << line) & ((s->inputs & s->edge[1]) | s->level[0])) - omap2_gpio_module_int(s, line); - s->inputs &= ~(1 << line); - } -} - -static void omap2_gpio_module_reset(struct omap2_gpio_s *s) -{ - s->config[0] = 0; - s->config[1] = 2; - s->ints[0] = 0; - s->ints[1] = 0; - s->mask[0] = 0; - s->mask[1] = 0; - s->wumask = 0; - s->dir = ~0; - s->level[0] = 0; - s->level[1] = 0; - s->edge[0] = 0; - s->edge[1] = 0; - s->debounce = 0; - s->delay = 0; -} - -static uint32_t omap2_gpio_module_read(void *opaque, hwaddr addr) -{ - struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; - - switch (addr) { - case 0x00: /* GPIO_REVISION */ - return s->revision; - - case 0x10: /* GPIO_SYSCONFIG */ - return s->config[0]; - - case 0x14: /* GPIO_SYSSTATUS */ - return 0x01; - - case 0x18: /* GPIO_IRQSTATUS1 */ - return s->ints[0]; - - case 0x1c: /* GPIO_IRQENABLE1 */ - case 0x60: /* GPIO_CLEARIRQENABLE1 */ - case 0x64: /* GPIO_SETIRQENABLE1 */ - return s->mask[0]; - - case 0x20: /* GPIO_WAKEUPENABLE */ - case 0x80: /* GPIO_CLEARWKUENA */ - case 0x84: /* GPIO_SETWKUENA */ - return s->wumask; - - case 0x28: /* GPIO_IRQSTATUS2 */ - return s->ints[1]; - - case 0x2c: /* GPIO_IRQENABLE2 */ - case 0x70: /* GPIO_CLEARIRQENABLE2 */ - case 0x74: /* GPIO_SETIREQNEABLE2 */ - return s->mask[1]; - - case 0x30: /* GPIO_CTRL */ - return s->config[1]; - - case 0x34: /* GPIO_OE */ - return s->dir; - - case 0x38: /* GPIO_DATAIN */ - return s->inputs; - - case 0x3c: /* GPIO_DATAOUT */ - case 0x90: /* GPIO_CLEARDATAOUT */ - case 0x94: /* GPIO_SETDATAOUT */ - return s->outputs; - - case 0x40: /* GPIO_LEVELDETECT0 */ - return s->level[0]; - - case 0x44: /* GPIO_LEVELDETECT1 */ - return s->level[1]; - - case 0x48: /* GPIO_RISINGDETECT */ - return s->edge[0]; - - case 0x4c: /* GPIO_FALLINGDETECT */ - return s->edge[1]; - - case 0x50: /* GPIO_DEBOUNCENABLE */ - return s->debounce; - - case 0x54: /* GPIO_DEBOUNCINGTIME */ - return s->delay; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap2_gpio_module_write(void *opaque, hwaddr addr, - uint32_t value) -{ - struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; - uint32_t diff; - int ln; - - switch (addr) { - case 0x00: /* GPIO_REVISION */ - case 0x14: /* GPIO_SYSSTATUS */ - case 0x38: /* GPIO_DATAIN */ - OMAP_RO_REG(addr); - break; - - case 0x10: /* GPIO_SYSCONFIG */ - if (((value >> 3) & 3) == 3) - fprintf(stderr, "%s: bad IDLEMODE value\n", __FUNCTION__); - if (value & 2) - omap2_gpio_module_reset(s); - s->config[0] = value & 0x1d; - break; - - case 0x18: /* GPIO_IRQSTATUS1 */ - if (s->ints[0] & value) { - s->ints[0] &= ~value; - omap2_gpio_module_level_update(s, 0); - } - break; - - case 0x1c: /* GPIO_IRQENABLE1 */ - s->mask[0] = value; - omap2_gpio_module_int_update(s, 0); - break; - - case 0x20: /* GPIO_WAKEUPENABLE */ - s->wumask = value; - break; - - case 0x28: /* GPIO_IRQSTATUS2 */ - if (s->ints[1] & value) { - s->ints[1] &= ~value; - omap2_gpio_module_level_update(s, 1); - } - break; - - case 0x2c: /* GPIO_IRQENABLE2 */ - s->mask[1] = value; - omap2_gpio_module_int_update(s, 1); - break; - - case 0x30: /* GPIO_CTRL */ - s->config[1] = value & 7; - break; - - case 0x34: /* GPIO_OE */ - diff = s->outputs & (s->dir ^ value); - s->dir = value; - - value = s->outputs & ~s->dir; - while ((ln = ctz32(diff)) != 32) { - diff &= ~(1 << ln); - qemu_set_irq(s->handler[ln], (value >> ln) & 1); - } - - omap2_gpio_module_level_update(s, 0); - omap2_gpio_module_level_update(s, 1); - break; - - case 0x3c: /* GPIO_DATAOUT */ - omap2_gpio_module_out_update(s, s->outputs ^ value); - break; - - case 0x40: /* GPIO_LEVELDETECT0 */ - s->level[0] = value; - omap2_gpio_module_level_update(s, 0); - omap2_gpio_module_level_update(s, 1); - break; - - case 0x44: /* GPIO_LEVELDETECT1 */ - s->level[1] = value; - omap2_gpio_module_level_update(s, 0); - omap2_gpio_module_level_update(s, 1); - break; - - case 0x48: /* GPIO_RISINGDETECT */ - s->edge[0] = value; - break; - - case 0x4c: /* GPIO_FALLINGDETECT */ - s->edge[1] = value; - break; - - case 0x50: /* GPIO_DEBOUNCENABLE */ - s->debounce = value; - break; - - case 0x54: /* GPIO_DEBOUNCINGTIME */ - s->delay = value; - break; - - case 0x60: /* GPIO_CLEARIRQENABLE1 */ - s->mask[0] &= ~value; - omap2_gpio_module_int_update(s, 0); - break; - - case 0x64: /* GPIO_SETIRQENABLE1 */ - s->mask[0] |= value; - omap2_gpio_module_int_update(s, 0); - break; - - case 0x70: /* GPIO_CLEARIRQENABLE2 */ - s->mask[1] &= ~value; - omap2_gpio_module_int_update(s, 1); - break; - - case 0x74: /* GPIO_SETIREQNEABLE2 */ - s->mask[1] |= value; - omap2_gpio_module_int_update(s, 1); - break; - - case 0x80: /* GPIO_CLEARWKUENA */ - s->wumask &= ~value; - break; - - case 0x84: /* GPIO_SETWKUENA */ - s->wumask |= value; - break; - - case 0x90: /* GPIO_CLEARDATAOUT */ - omap2_gpio_module_out_update(s, s->outputs & value); - break; - - case 0x94: /* GPIO_SETDATAOUT */ - omap2_gpio_module_out_update(s, ~s->outputs & value); - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static uint32_t omap2_gpio_module_readp(void *opaque, hwaddr addr) -{ - return omap2_gpio_module_read(opaque, addr & ~3) >> ((addr & 3) << 3); -} - -static void omap2_gpio_module_writep(void *opaque, hwaddr addr, - uint32_t value) -{ - uint32_t cur = 0; - uint32_t mask = 0xffff; - - switch (addr & ~3) { - case 0x00: /* GPIO_REVISION */ - case 0x14: /* GPIO_SYSSTATUS */ - case 0x38: /* GPIO_DATAIN */ - OMAP_RO_REG(addr); - break; - - case 0x10: /* GPIO_SYSCONFIG */ - case 0x1c: /* GPIO_IRQENABLE1 */ - case 0x20: /* GPIO_WAKEUPENABLE */ - case 0x2c: /* GPIO_IRQENABLE2 */ - case 0x30: /* GPIO_CTRL */ - case 0x34: /* GPIO_OE */ - case 0x3c: /* GPIO_DATAOUT */ - case 0x40: /* GPIO_LEVELDETECT0 */ - case 0x44: /* GPIO_LEVELDETECT1 */ - case 0x48: /* GPIO_RISINGDETECT */ - case 0x4c: /* GPIO_FALLINGDETECT */ - case 0x50: /* GPIO_DEBOUNCENABLE */ - case 0x54: /* GPIO_DEBOUNCINGTIME */ - cur = omap2_gpio_module_read(opaque, addr & ~3) & - ~(mask << ((addr & 3) << 3)); - - /* Fall through. */ - case 0x18: /* GPIO_IRQSTATUS1 */ - case 0x28: /* GPIO_IRQSTATUS2 */ - case 0x60: /* GPIO_CLEARIRQENABLE1 */ - case 0x64: /* GPIO_SETIRQENABLE1 */ - case 0x70: /* GPIO_CLEARIRQENABLE2 */ - case 0x74: /* GPIO_SETIREQNEABLE2 */ - case 0x80: /* GPIO_CLEARWKUENA */ - case 0x84: /* GPIO_SETWKUENA */ - case 0x90: /* GPIO_CLEARDATAOUT */ - case 0x94: /* GPIO_SETDATAOUT */ - value <<= (addr & 3) << 3; - omap2_gpio_module_write(opaque, addr, cur | value); - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap2_gpio_module_ops = { - .old_mmio = { - .read = { - omap2_gpio_module_readp, - omap2_gpio_module_readp, - omap2_gpio_module_read, - }, - .write = { - omap2_gpio_module_writep, - omap2_gpio_module_writep, - omap2_gpio_module_write, - }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_gpif_reset(DeviceState *dev) -{ - struct omap_gpif_s *s = OMAP1_GPIO(dev); - - omap_gpio_reset(&s->omap1); -} - -static void omap2_gpif_reset(DeviceState *dev) -{ - struct omap2_gpif_s *s = OMAP2_GPIO(dev); - int i; - - for (i = 0; i < s->modulecount; i++) { - omap2_gpio_module_reset(&s->modules[i]); - } - s->autoidle = 0; - s->gpo = 0; -} - -static uint64_t omap2_gpif_top_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap2_gpif_s *s = (struct omap2_gpif_s *) opaque; - - switch (addr) { - case 0x00: /* IPGENERICOCPSPL_REVISION */ - return 0x18; - - case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */ - return s->autoidle; - - case 0x14: /* IPGENERICOCPSPL_SYSSTATUS */ - return 0x01; - - case 0x18: /* IPGENERICOCPSPL_IRQSTATUS */ - return 0x00; - - case 0x40: /* IPGENERICOCPSPL_GPO */ - return s->gpo; - - case 0x50: /* IPGENERICOCPSPL_GPI */ - return 0x00; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap2_gpif_top_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap2_gpif_s *s = (struct omap2_gpif_s *) opaque; - - switch (addr) { - case 0x00: /* IPGENERICOCPSPL_REVISION */ - case 0x14: /* IPGENERICOCPSPL_SYSSTATUS */ - case 0x18: /* IPGENERICOCPSPL_IRQSTATUS */ - case 0x50: /* IPGENERICOCPSPL_GPI */ - OMAP_RO_REG(addr); - break; - - case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */ - if (value & (1 << 1)) /* SOFTRESET */ - omap2_gpif_reset(DEVICE(s)); - s->autoidle = value & 1; - break; - - case 0x40: /* IPGENERICOCPSPL_GPO */ - s->gpo = value & 1; - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap2_gpif_top_ops = { - .read = omap2_gpif_top_read, - .write = omap2_gpif_top_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int omap_gpio_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - struct omap_gpif_s *s = OMAP1_GPIO(dev); - - if (!s->clk) { - 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); - sysbus_init_irq(sbd, &s->omap1.irq); - memory_region_init_io(&s->iomem, OBJECT(s), &omap_gpio_ops, &s->omap1, - "omap.gpio", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - return 0; -} - -static int omap2_gpio_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - struct omap2_gpif_s *s = OMAP2_GPIO(dev); - int i; - - if (!s->iclk) { - 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) { - memory_region_init_io(&s->iomem, OBJECT(s), &omap2_gpif_top_ops, s, - "omap2.gpio", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - } - - 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]; - - m->revision = (s->mpu_model < omap3430) ? 0x18 : 0x25; - m->handler = &s->handler[i * 32]; - sysbus_init_irq(sbd, &m->irq[0]); /* mpu irq */ - sysbus_init_irq(sbd, &m->irq[1]); /* dsp irq */ - sysbus_init_irq(sbd, &m->wkup); - memory_region_init_io(&m->iomem, OBJECT(s), &omap2_gpio_module_ops, m, - "omap.gpio-module", 0x1000); - sysbus_init_mmio(sbd, &m->iomem); - } - - return 0; -} - -/* Using qdev pointer properties for the clocks is not ideal. - * qdev should support a generic means of defining a 'port' with - * an arbitrary interface for connecting two devices. Then we - * could reframe the omap clock API in terms of clock ports, - * and get some type safety. For now the best qdev provides is - * passing an arbitrary pointer. - * (It's not possible to pass in the string which is the clock - * name, because this device does not have the necessary information - * (ie the struct omap_mpu_state_s*) to do the clockname to pointer - * translation.) - */ - -static Property omap_gpio_properties[] = { - DEFINE_PROP_INT32("mpu_model", struct omap_gpif_s, mpu_model, 0), - DEFINE_PROP_PTR("clk", struct omap_gpif_s, clk), - DEFINE_PROP_END_OF_LIST(), -}; - -static void omap_gpio_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = omap_gpio_init; - dc->reset = omap_gpif_reset; - dc->props = omap_gpio_properties; - /* Reason: pointer property "clk" */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo omap_gpio_info = { - .name = TYPE_OMAP1_GPIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct omap_gpif_s), - .class_init = omap_gpio_class_init, -}; - -static Property omap2_gpio_properties[] = { - DEFINE_PROP_INT32("mpu_model", struct omap2_gpif_s, mpu_model, 0), - DEFINE_PROP_PTR("iclk", struct omap2_gpif_s, iclk), - DEFINE_PROP_PTR("fclk0", struct omap2_gpif_s, fclk[0]), - DEFINE_PROP_PTR("fclk1", struct omap2_gpif_s, fclk[1]), - DEFINE_PROP_PTR("fclk2", struct omap2_gpif_s, fclk[2]), - DEFINE_PROP_PTR("fclk3", struct omap2_gpif_s, fclk[3]), - DEFINE_PROP_PTR("fclk4", struct omap2_gpif_s, fclk[4]), - DEFINE_PROP_PTR("fclk5", struct omap2_gpif_s, fclk[5]), - DEFINE_PROP_END_OF_LIST(), -}; - -static void omap2_gpio_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = omap2_gpio_init; - dc->reset = omap2_gpif_reset; - dc->props = omap2_gpio_properties; - /* Reason: pointer properties "iclk", "fclk0", ..., "fclk5" */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo omap2_gpio_info = { - .name = TYPE_OMAP2_GPIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct omap2_gpif_s), - .class_init = omap2_gpio_class_init, -}; - -static void omap_gpio_register_types(void) -{ - type_register_static(&omap_gpio_info); - type_register_static(&omap2_gpio_info); -} - -type_init(omap_gpio_register_types) diff --git a/qemu/hw/gpio/pl061.c b/qemu/hw/gpio/pl061.c deleted file mode 100644 index 29dc7fc38..000000000 --- a/qemu/hw/gpio/pl061.c +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Arm PrimeCell PL061 General Purpose IO with additional - * Luminary Micro Stellaris bits. - * - * Copyright (c) 2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" - -//#define DEBUG_PL061 1 - -#ifdef DEBUG_PL061 -#define DPRINTF(fmt, ...) \ -do { printf("pl061: " fmt , ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "pl061: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "pl061: error: " fmt , ## __VA_ARGS__);} while (0) -#endif - -static const uint8_t pl061_id[12] = - { 0x00, 0x00, 0x00, 0x00, 0x61, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; -static const uint8_t pl061_id_luminary[12] = - { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }; - -#define TYPE_PL061 "pl061" -#define PL061(obj) OBJECT_CHECK(PL061State, (obj), TYPE_PL061) - -typedef struct PL061State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t locked; - uint32_t data; - uint32_t old_out_data; - uint32_t old_in_data; - uint32_t dir; - uint32_t isense; - uint32_t ibe; - uint32_t iev; - uint32_t im; - uint32_t istate; - uint32_t afsel; - uint32_t dr2r; - uint32_t dr4r; - uint32_t dr8r; - uint32_t odr; - uint32_t pur; - uint32_t pdr; - uint32_t slr; - uint32_t den; - uint32_t cr; - 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 = 4, - .minimum_version_id = 4, - .fields = (VMStateField[]) { - VMSTATE_UINT32(locked, PL061State), - VMSTATE_UINT32(data, PL061State), - VMSTATE_UINT32(old_out_data, PL061State), - VMSTATE_UINT32(old_in_data, PL061State), - VMSTATE_UINT32(dir, PL061State), - VMSTATE_UINT32(isense, PL061State), - VMSTATE_UINT32(ibe, PL061State), - VMSTATE_UINT32(iev, PL061State), - VMSTATE_UINT32(im, PL061State), - VMSTATE_UINT32(istate, PL061State), - VMSTATE_UINT32(afsel, PL061State), - VMSTATE_UINT32(dr2r, PL061State), - VMSTATE_UINT32(dr4r, PL061State), - VMSTATE_UINT32(dr8r, PL061State), - VMSTATE_UINT32(odr, PL061State), - VMSTATE_UINT32(pur, PL061State), - VMSTATE_UINT32(pdr, PL061State), - VMSTATE_UINT32(slr, PL061State), - VMSTATE_UINT32(den, PL061State), - VMSTATE_UINT32(cr, PL061State), - VMSTATE_UINT32_V(amsel, PL061State, 2), - VMSTATE_END_OF_LIST() - } -}; - -static void pl061_update(PL061State *s) -{ - uint8_t changed; - uint8_t mask; - uint8_t out; - int i; - - DPRINTF("dir = %d, data = %d\n", s->dir, s->data); - - /* Outputs float high. */ - /* FIXME: This is board dependent. */ - out = (s->data & s->dir) | ~s->dir; - changed = s->old_out_data ^ out; - if (changed) { - s->old_out_data = out; - for (i = 0; i < 8; i++) { - mask = 1 << i; - if (changed & mask) { - DPRINTF("Set output %d = %d\n", i, (out & mask) != 0); - qemu_set_irq(s->out[i], (out & mask) != 0); - } - } - } - - /* Inputs */ - changed = (s->old_in_data ^ s->data) & ~s->dir; - if (changed) { - s->old_in_data = s->data; - for (i = 0; i < 8; i++) { - mask = 1 << i; - if (changed & mask) { - DPRINTF("Changed input %d = %d\n", i, (s->data & mask) != 0); - - if (!(s->isense & mask)) { - /* Edge interrupt */ - if (s->ibe & mask) { - /* Any edge triggers the interrupt */ - s->istate |= mask; - } else { - /* Edge is selected by IEV */ - s->istate |= ~(s->data ^ s->iev) & mask; - } - } - } - } - } - - /* Level interrupt */ - s->istate |= ~(s->data ^ s->iev) & s->isense; - - DPRINTF("istate = %02X\n", s->istate); - - qemu_set_irq(s->irq, (s->istate & s->im) != 0); -} - -static uint64_t pl061_read(void *opaque, hwaddr offset, - unsigned size) -{ - PL061State *s = (PL061State *)opaque; - - if (offset < 0x400) { - return s->data & (offset >> 2); - } - if (offset >= s->rsvd_start && offset <= 0xfcc) { - goto err_out; - } - if (offset >= 0xfd0 && offset < 0x1000) { - return s->id[(offset - 0xfd0) >> 2]; - } - switch (offset) { - case 0x400: /* Direction */ - return s->dir; - case 0x404: /* Interrupt sense */ - return s->isense; - case 0x408: /* Interrupt both edges */ - return s->ibe; - case 0x40c: /* Interrupt event */ - return s->iev; - case 0x410: /* Interrupt mask */ - return s->im; - case 0x414: /* Raw interrupt status */ - return s->istate; - case 0x418: /* Masked interrupt status */ - return s->istate & s->im; - case 0x420: /* Alternate function select */ - return s->afsel; - case 0x500: /* 2mA drive */ - return s->dr2r; - case 0x504: /* 4mA drive */ - return s->dr4r; - case 0x508: /* 8mA drive */ - return s->dr8r; - case 0x50c: /* Open drain */ - return s->odr; - case 0x510: /* Pull-up */ - return s->pur; - case 0x514: /* Pull-down */ - return s->pdr; - case 0x518: /* Slew rate control */ - return s->slr; - case 0x51c: /* Digital enable */ - return s->den; - case 0x520: /* Lock */ - return s->locked; - case 0x524: /* Commit */ - return s->cr; - case 0x528: /* Analog mode select */ - return s->amsel; - default: - 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, - uint64_t value, unsigned size) -{ - PL061State *s = (PL061State *)opaque; - uint8_t mask; - - if (offset < 0x400) { - mask = (offset >> 2) & s->dir; - s->data = (s->data & ~mask) | (value & mask); - pl061_update(s); - return; - } - if (offset >= s->rsvd_start) { - goto err_out; - } - switch (offset) { - case 0x400: /* Direction */ - s->dir = value & 0xff; - break; - case 0x404: /* Interrupt sense */ - s->isense = value & 0xff; - break; - case 0x408: /* Interrupt both edges */ - s->ibe = value & 0xff; - break; - case 0x40c: /* Interrupt event */ - s->iev = value & 0xff; - break; - case 0x410: /* Interrupt mask */ - s->im = value & 0xff; - break; - case 0x41c: /* Interrupt clear */ - s->istate &= ~value; - break; - case 0x420: /* Alternate function select */ - mask = s->cr; - s->afsel = (s->afsel & ~mask) | (value & mask); - break; - case 0x500: /* 2mA drive */ - s->dr2r = value & 0xff; - break; - case 0x504: /* 4mA drive */ - s->dr4r = value & 0xff; - break; - case 0x508: /* 8mA drive */ - s->dr8r = value & 0xff; - break; - case 0x50c: /* Open drain */ - s->odr = value & 0xff; - break; - case 0x510: /* Pull-up */ - s->pur = value & 0xff; - break; - case 0x514: /* Pull-down */ - s->pdr = value & 0xff; - break; - case 0x518: /* Slew rate control */ - s->slr = value & 0xff; - break; - case 0x51c: /* Digital enable */ - s->den = value & 0xff; - break; - case 0x520: /* Lock */ - s->locked = (value != 0xacce551); - break; - case 0x524: /* Commit */ - if (!s->locked) - s->cr = value & 0xff; - break; - case 0x528: - s->amsel = value & 0xff; - break; - default: - 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(DeviceState *dev) -{ - 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) -{ - PL061State *s = (PL061State *)opaque; - uint8_t mask; - - mask = 1 << irq; - if ((s->dir & mask) == 0) { - s->data &= ~mask; - if (level) - s->data |= mask; - pl061_update(s); - } -} - -static const MemoryRegionOps pl061_ops = { - .read = pl061_read, - .write = pl061_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int pl061_initfn(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - PL061State *s = PL061(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &pl061_ops, s, "pl061", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - qdev_init_gpio_in(dev, pl061_set_irq, 8); - qdev_init_gpio_out(dev, s->out, 8); - - return 0; -} - -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) -{ - PL061State *s = PL061(obj); - - s->id = pl061_id; - s->rsvd_start = 0x424; -} - -static void pl061_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = pl061_initfn; - dc->vmsd = &vmstate_pl061; - dc->reset = &pl061_reset; -} - -static const TypeInfo pl061_info = { - .name = TYPE_PL061, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PL061State), - .instance_init = pl061_init, - .class_init = pl061_class_init, -}; - -static const TypeInfo pl061_luminary_info = { - .name = "pl061_luminary", - .parent = TYPE_PL061, - .instance_init = pl061_luminary_init, -}; - -static void pl061_register_types(void) -{ - type_register_static(&pl061_info); - type_register_static(&pl061_luminary_info); -} - -type_init(pl061_register_types) diff --git a/qemu/hw/gpio/puv3_gpio.c b/qemu/hw/gpio/puv3_gpio.c deleted file mode 100644 index 445afccf9..000000000 --- a/qemu/hw/gpio/puv3_gpio.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * GPIO device simulation in PKUnity SoC - * - * Copyright (C) 2010-2012 Guan Xuetao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation, 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" - -#undef DEBUG_PUV3 -#include "hw/unicore32/puv3.h" - -#define TYPE_PUV3_GPIO "puv3_gpio" -#define PUV3_GPIO(obj) OBJECT_CHECK(PUV3GPIOState, (obj), TYPE_PUV3_GPIO) - -typedef struct PUV3GPIOState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq irq[9]; - - uint32_t reg_GPLR; - uint32_t reg_GPDR; - uint32_t reg_GPIR; -} PUV3GPIOState; - -static uint64_t puv3_gpio_read(void *opaque, hwaddr offset, - unsigned size) -{ - PUV3GPIOState *s = opaque; - uint32_t ret = 0; - - switch (offset) { - case 0x00: - ret = s->reg_GPLR; - break; - case 0x04: - ret = s->reg_GPDR; - break; - case 0x20: - ret = s->reg_GPIR; - break; - default: - DPRINTF("Bad offset 0x%x\n", offset); - } - DPRINTF("offset 0x%x, value 0x%x\n", offset, ret); - - return ret; -} - -static void puv3_gpio_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PUV3GPIOState *s = opaque; - - DPRINTF("offset 0x%x, value 0x%x\n", offset, value); - switch (offset) { - case 0x04: - s->reg_GPDR = value; - break; - case 0x08: - if (s->reg_GPDR & value) { - s->reg_GPLR |= value; - } else { - DPRINTF("Write gpio input port error!"); - } - break; - case 0x0c: - if (s->reg_GPDR & value) { - s->reg_GPLR &= ~value; - } else { - DPRINTF("Write gpio input port error!"); - } - break; - case 0x10: /* GRER */ - case 0x14: /* GFER */ - case 0x18: /* GEDR */ - break; - case 0x20: /* GPIR */ - s->reg_GPIR = value; - break; - default: - DPRINTF("Bad offset 0x%x\n", offset); - } -} - -static const MemoryRegionOps puv3_gpio_ops = { - .read = puv3_gpio_read, - .write = puv3_gpio_write, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int puv3_gpio_init(SysBusDevice *dev) -{ - PUV3GPIOState *s = PUV3_GPIO(dev); - - s->reg_GPLR = 0; - s->reg_GPDR = 0; - - /* FIXME: these irqs not handled yet */ - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW0]); - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW1]); - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW2]); - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW3]); - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW4]); - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW5]); - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW6]); - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW7]); - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOHIGH]); - - memory_region_init_io(&s->iomem, OBJECT(s), &puv3_gpio_ops, s, "puv3_gpio", - PUV3_REGS_OFFSET); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static void puv3_gpio_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = puv3_gpio_init; -} - -static const TypeInfo puv3_gpio_info = { - .name = TYPE_PUV3_GPIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PUV3GPIOState), - .class_init = puv3_gpio_class_init, -}; - -static void puv3_gpio_register_type(void) -{ - type_register_static(&puv3_gpio_info); -} - -type_init(puv3_gpio_register_type) diff --git a/qemu/hw/gpio/zaurus.c b/qemu/hw/gpio/zaurus.c deleted file mode 100644 index 555da281c..000000000 --- a/qemu/hw/gpio/zaurus.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (c) 2006-2008 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/arm/sharpsl.h" -#include "hw/sysbus.h" - -#undef REG_FMT -#define REG_FMT "0x%02lx" - -/* SCOOP devices */ - -#define TYPE_SCOOP "scoop" -#define SCOOP(obj) OBJECT_CHECK(ScoopInfo, (obj), TYPE_SCOOP) - -typedef struct ScoopInfo ScoopInfo; -struct ScoopInfo { - SysBusDevice parent_obj; - - qemu_irq handler[16]; - MemoryRegion iomem; - uint16_t status; - uint16_t power; - uint32_t gpio_level; - uint32_t gpio_dir; - uint32_t prev_level; - - uint16_t mcr; - uint16_t cdr; - uint16_t ccr; - uint16_t irr; - uint16_t imr; - uint16_t isr; -}; - -#define SCOOP_MCR 0x00 -#define SCOOP_CDR 0x04 -#define SCOOP_CSR 0x08 -#define SCOOP_CPR 0x0c -#define SCOOP_CCR 0x10 -#define SCOOP_IRR_IRM 0x14 -#define SCOOP_IMR 0x18 -#define SCOOP_ISR 0x1c -#define SCOOP_GPCR 0x20 -#define SCOOP_GPWR 0x24 -#define SCOOP_GPRR 0x28 - -static inline void scoop_gpio_handler_update(ScoopInfo *s) { - uint32_t level, diff; - int bit; - level = s->gpio_level & s->gpio_dir; - - for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) { - bit = ctz32(diff); - qemu_set_irq(s->handler[bit], (level >> bit) & 1); - } - - s->prev_level = level; -} - -static uint64_t scoop_read(void *opaque, hwaddr addr, - unsigned size) -{ - ScoopInfo *s = (ScoopInfo *) opaque; - - switch (addr & 0x3f) { - case SCOOP_MCR: - return s->mcr; - case SCOOP_CDR: - return s->cdr; - case SCOOP_CSR: - return s->status; - case SCOOP_CPR: - return s->power; - case SCOOP_CCR: - return s->ccr; - case SCOOP_IRR_IRM: - return s->irr; - case SCOOP_IMR: - return s->imr; - case SCOOP_ISR: - return s->isr; - case SCOOP_GPCR: - return s->gpio_dir; - case SCOOP_GPWR: - case SCOOP_GPRR: - return s->gpio_level; - default: - zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr); - } - - return 0; -} - -static void scoop_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - ScoopInfo *s = (ScoopInfo *) opaque; - value &= 0xffff; - - switch (addr & 0x3f) { - case SCOOP_MCR: - s->mcr = value; - break; - case SCOOP_CDR: - s->cdr = value; - break; - case SCOOP_CPR: - s->power = value; - if (value & 0x80) - s->power |= 0x8040; - break; - case SCOOP_CCR: - s->ccr = value; - break; - case SCOOP_IRR_IRM: - s->irr = value; - break; - case SCOOP_IMR: - s->imr = value; - break; - case SCOOP_ISR: - s->isr = value; - break; - case SCOOP_GPCR: - s->gpio_dir = value; - scoop_gpio_handler_update(s); - break; - case SCOOP_GPWR: - case SCOOP_GPRR: /* GPRR is probably R/O in real HW */ - s->gpio_level = value & s->gpio_dir; - scoop_gpio_handler_update(s); - break; - default: - zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr); - } -} - -static const MemoryRegionOps scoop_ops = { - .read = scoop_read, - .write = scoop_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void scoop_gpio_set(void *opaque, int line, int level) -{ - ScoopInfo *s = (ScoopInfo *) opaque; - - if (level) - s->gpio_level |= (1 << line); - else - s->gpio_level &= ~(1 << line); -} - -static int scoop_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - ScoopInfo *s = SCOOP(dev); - - s->status = 0x02; - qdev_init_gpio_out(dev, s->handler, 16); - qdev_init_gpio_in(dev, scoop_gpio_set, 16); - memory_region_init_io(&s->iomem, OBJECT(s), &scoop_ops, s, "scoop", 0x1000); - - sysbus_init_mmio(sbd, &s->iomem); - - return 0; -} - -static int scoop_post_load(void *opaque, int version_id) -{ - ScoopInfo *s = (ScoopInfo *) opaque; - int i; - uint32_t level; - - level = s->gpio_level & s->gpio_dir; - - for (i = 0; i < 16; i++) { - qemu_set_irq(s->handler[i], (level >> i) & 1); - } - - s->prev_level = level; - - return 0; -} - -static bool is_version_0 (void *opaque, int version_id) -{ - return version_id == 0; -} - -static bool vmstate_scoop_validate(void *opaque, int version_id) -{ - ScoopInfo *s = opaque; - - return !(s->prev_level & 0xffff0000) && - !(s->gpio_level & 0xffff0000) && - !(s->gpio_dir & 0xffff0000); -} - -static const VMStateDescription vmstate_scoop_regs = { - .name = "scoop", - .version_id = 1, - .minimum_version_id = 0, - .post_load = scoop_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT16(status, ScoopInfo), - VMSTATE_UINT16(power, ScoopInfo), - VMSTATE_UINT32(gpio_level, ScoopInfo), - VMSTATE_UINT32(gpio_dir, ScoopInfo), - VMSTATE_UINT32(prev_level, ScoopInfo), - VMSTATE_VALIDATE("irq levels are 16 bit", vmstate_scoop_validate), - VMSTATE_UINT16(mcr, ScoopInfo), - VMSTATE_UINT16(cdr, ScoopInfo), - VMSTATE_UINT16(ccr, ScoopInfo), - VMSTATE_UINT16(irr, ScoopInfo), - VMSTATE_UINT16(imr, ScoopInfo), - VMSTATE_UINT16(isr, ScoopInfo), - VMSTATE_UNUSED_TEST(is_version_0, 2), - VMSTATE_END_OF_LIST(), - }, -}; - -static void scoop_sysbus_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = scoop_init; - dc->desc = "Scoop2 Sharp custom ASIC"; - dc->vmsd = &vmstate_scoop_regs; -} - -static const TypeInfo scoop_sysbus_info = { - .name = TYPE_SCOOP, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ScoopInfo), - .class_init = scoop_sysbus_class_init, -}; - -static void scoop_register_types(void) -{ - type_register_static(&scoop_sysbus_info); -} - -type_init(scoop_register_types) - -/* Write the bootloader parameters memory area. */ - -#define MAGIC_CHG(a, b, c, d) ((d << 24) | (c << 16) | (b << 8) | a) - -static struct QEMU_PACKED sl_param_info { - uint32_t comadj_keyword; - int32_t comadj; - - uint32_t uuid_keyword; - char uuid[16]; - - uint32_t touch_keyword; - int32_t touch_xp; - int32_t touch_yp; - int32_t touch_xd; - int32_t touch_yd; - - uint32_t adadj_keyword; - int32_t adadj; - - uint32_t phad_keyword; - int32_t phadadj; -} zaurus_bootparam = { - .comadj_keyword = MAGIC_CHG('C', 'M', 'A', 'D'), - .comadj = 125, - .uuid_keyword = MAGIC_CHG('U', 'U', 'I', 'D'), - .uuid = { -1 }, - .touch_keyword = MAGIC_CHG('T', 'U', 'C', 'H'), - .touch_xp = -1, - .adadj_keyword = MAGIC_CHG('B', 'V', 'A', 'D'), - .adadj = -1, - .phad_keyword = MAGIC_CHG('P', 'H', 'A', 'D'), - .phadadj = 0x01, -}; - -void sl_bootparam_write(hwaddr ptr) -{ - cpu_physical_memory_write(ptr, &zaurus_bootparam, - sizeof(struct sl_param_info)); -} diff --git a/qemu/hw/i2c/Makefile.objs b/qemu/hw/i2c/Makefile.objs deleted file mode 100644 index aeb8f38d7..000000000 --- a/qemu/hw/i2c/Makefile.objs +++ /dev/null @@ -1,8 +0,0 @@ -common-obj-y += core.o smbus.o smbus_eeprom.o -common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o -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 deleted file mode 100644 index 6ed206020..000000000 --- a/qemu/hw/i2c/bitbang_i2c.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Bit-Bang i2c emulation extracted from - * Marvell MV88W8618 / Freecom MusicPal emulation. - * - * Copyright (c) 2008 Jan Kiszka - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "bitbang_i2c.h" -#include "hw/sysbus.h" - -//#define DEBUG_BITBANG_I2C - -#ifdef DEBUG_BITBANG_I2C -#define DPRINTF(fmt, ...) \ -do { printf("bitbang_i2c: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - -typedef enum bitbang_i2c_state { - STOPPED = 0, - SENDING_BIT7, - SENDING_BIT6, - SENDING_BIT5, - SENDING_BIT4, - SENDING_BIT3, - SENDING_BIT2, - SENDING_BIT1, - SENDING_BIT0, - WAITING_FOR_ACK, - RECEIVING_BIT7, - RECEIVING_BIT6, - RECEIVING_BIT5, - RECEIVING_BIT4, - RECEIVING_BIT3, - RECEIVING_BIT2, - RECEIVING_BIT1, - RECEIVING_BIT0, - SENDING_ACK, - SENT_NACK -} bitbang_i2c_state; - -struct bitbang_i2c_interface { - I2CBus *bus; - bitbang_i2c_state state; - int last_data; - int last_clock; - int device_out; - uint8_t buffer; - int current_addr; -}; - -static void bitbang_i2c_enter_stop(bitbang_i2c_interface *i2c) -{ - DPRINTF("STOP\n"); - if (i2c->current_addr >= 0) - i2c_end_transfer(i2c->bus); - i2c->current_addr = -1; - i2c->state = STOPPED; -} - -/* Set device data pin. */ -static int bitbang_i2c_ret(bitbang_i2c_interface *i2c, int level) -{ - i2c->device_out = level; - //DPRINTF("%d %d %d\n", i2c->last_clock, i2c->last_data, i2c->device_out); - return level & i2c->last_data; -} - -/* Leave device data pin unodified. */ -static int bitbang_i2c_nop(bitbang_i2c_interface *i2c) -{ - return bitbang_i2c_ret(i2c, i2c->device_out); -} - -/* Returns data line level. */ -int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level) -{ - int data; - - if (level != 0 && level != 1) { - abort(); - } - - if (line == BITBANG_I2C_SDA) { - if (level == i2c->last_data) { - return bitbang_i2c_nop(i2c); - } - i2c->last_data = level; - if (i2c->last_clock == 0) { - return bitbang_i2c_nop(i2c); - } - if (level == 0) { - DPRINTF("START\n"); - /* START condition. */ - i2c->state = SENDING_BIT7; - i2c->current_addr = -1; - } else { - /* STOP condition. */ - bitbang_i2c_enter_stop(i2c); - } - return bitbang_i2c_ret(i2c, 1); - } - - data = i2c->last_data; - if (i2c->last_clock == level) { - return bitbang_i2c_nop(i2c); - } - i2c->last_clock = level; - if (level == 0) { - /* State is set/read at the start of the clock pulse. - release the data line at the end. */ - return bitbang_i2c_ret(i2c, 1); - } - switch (i2c->state) { - case STOPPED: - case SENT_NACK: - return bitbang_i2c_ret(i2c, 1); - - case SENDING_BIT7 ... SENDING_BIT0: - i2c->buffer = (i2c->buffer << 1) | data; - /* will end up in WAITING_FOR_ACK */ - i2c->state++; - return bitbang_i2c_ret(i2c, 1); - - case WAITING_FOR_ACK: - if (i2c->current_addr < 0) { - i2c->current_addr = i2c->buffer; - DPRINTF("Address 0x%02x\n", i2c->current_addr); - i2c_start_transfer(i2c->bus, i2c->current_addr >> 1, - i2c->current_addr & 1); - } else { - DPRINTF("Sent 0x%02x\n", i2c->buffer); - i2c_send(i2c->bus, i2c->buffer); - } - if (i2c->current_addr & 1) { - i2c->state = RECEIVING_BIT7; - } else { - i2c->state = SENDING_BIT7; - } - return bitbang_i2c_ret(i2c, 0); - - case RECEIVING_BIT7: - i2c->buffer = i2c_recv(i2c->bus); - DPRINTF("RX byte 0x%02x\n", i2c->buffer); - /* Fall through... */ - case RECEIVING_BIT6 ... RECEIVING_BIT0: - data = i2c->buffer >> 7; - /* will end up in SENDING_ACK */ - i2c->state++; - i2c->buffer <<= 1; - return bitbang_i2c_ret(i2c, data); - - case SENDING_ACK: - i2c->state = RECEIVING_BIT7; - if (data != 0) { - DPRINTF("NACKED\n"); - i2c->state = SENT_NACK; - i2c_nack(i2c->bus); - } else { - DPRINTF("ACKED\n"); - } - return bitbang_i2c_ret(i2c, 1); - } - abort(); -} - -bitbang_i2c_interface *bitbang_i2c_init(I2CBus *bus) -{ - bitbang_i2c_interface *s; - - s = g_malloc0(sizeof(bitbang_i2c_interface)); - - s->bus = bus; - s->last_data = 1; - s->last_clock = 1; - s->device_out = 1; - - return s; -} - -/* GPIO interface. */ - -#define TYPE_GPIO_I2C "gpio_i2c" -#define GPIO_I2C(obj) OBJECT_CHECK(GPIOI2CState, (obj), TYPE_GPIO_I2C) - -typedef struct GPIOI2CState { - SysBusDevice parent_obj; - - MemoryRegion dummy_iomem; - bitbang_i2c_interface *bitbang; - int last_level; - qemu_irq out; -} GPIOI2CState; - -static void bitbang_i2c_gpio_set(void *opaque, int irq, int level) -{ - GPIOI2CState *s = opaque; - - level = bitbang_i2c_set(s->bitbang, irq, level); - if (level != s->last_level) { - s->last_level = level; - qemu_set_irq(s->out, level); - } -} - -static int gpio_i2c_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - GPIOI2CState *s = GPIO_I2C(dev); - I2CBus *bus; - - memory_region_init(&s->dummy_iomem, OBJECT(s), "gpio_i2c", 0); - sysbus_init_mmio(sbd, &s->dummy_iomem); - - bus = i2c_init_bus(dev, "i2c"); - s->bitbang = bitbang_i2c_init(bus); - - qdev_init_gpio_in(dev, bitbang_i2c_gpio_set, 2); - qdev_init_gpio_out(dev, &s->out, 1); - - return 0; -} - -static void gpio_i2c_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = gpio_i2c_init; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->desc = "Virtual GPIO to I2C bridge"; -} - -static const TypeInfo gpio_i2c_info = { - .name = TYPE_GPIO_I2C, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(GPIOI2CState), - .class_init = gpio_i2c_class_init, -}; - -static void bitbang_i2c_register_types(void) -{ - type_register_static(&gpio_i2c_info); -} - -type_init(bitbang_i2c_register_types) diff --git a/qemu/hw/i2c/bitbang_i2c.h b/qemu/hw/i2c/bitbang_i2c.h deleted file mode 100644 index 3a7126d5d..000000000 --- a/qemu/hw/i2c/bitbang_i2c.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef BITBANG_I2C_H -#define BITBANG_I2C_H - -#include "hw/i2c/i2c.h" - -typedef struct bitbang_i2c_interface bitbang_i2c_interface; - -#define BITBANG_I2C_SDA 0 -#define BITBANG_I2C_SCL 1 - -bitbang_i2c_interface *bitbang_i2c_init(I2CBus *bus); -int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level); - -#endif diff --git a/qemu/hw/i2c/core.c b/qemu/hw/i2c/core.c deleted file mode 100644 index ba22104af..000000000 --- a/qemu/hw/i2c/core.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * QEMU I2C bus interface. - * - * Copyright (c) 2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the LGPL. - */ - -#include "qemu/osdep.h" -#include "hw/i2c/i2c.h" - -struct I2CBus -{ - BusState qbus; - I2CSlave *current_dev; - I2CSlave *dev; - uint8_t saved_address; -}; - -static Property i2c_props[] = { - DEFINE_PROP_UINT8("address", struct I2CSlave, address, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -#define TYPE_I2C_BUS "i2c-bus" -#define I2C_BUS(obj) OBJECT_CHECK(I2CBus, (obj), TYPE_I2C_BUS) - -static const TypeInfo i2c_bus_info = { - .name = TYPE_I2C_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(I2CBus), -}; - -static void i2c_bus_pre_save(void *opaque) -{ - I2CBus *bus = opaque; - - bus->saved_address = bus->current_dev ? bus->current_dev->address : -1; -} - -static int i2c_bus_post_load(void *opaque, int version_id) -{ - I2CBus *bus = opaque; - - /* The bus is loaded before attached devices, so load and save the - current device id. Devices will check themselves as loaded. */ - bus->current_dev = NULL; - return 0; -} - -static const VMStateDescription vmstate_i2c_bus = { - .name = "i2c_bus", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = i2c_bus_pre_save, - .post_load = i2c_bus_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(saved_address, I2CBus), - VMSTATE_END_OF_LIST() - } -}; - -/* Create a new I2C bus. */ -I2CBus *i2c_init_bus(DeviceState *parent, const char *name) -{ - I2CBus *bus; - - bus = I2C_BUS(qbus_create(TYPE_I2C_BUS, parent, name)); - vmstate_register(NULL, -1, &vmstate_i2c_bus, bus); - return bus; -} - -void i2c_set_slave_address(I2CSlave *dev, uint8_t address) -{ - dev->address = address; -} - -/* Return nonzero if bus is busy. */ -int i2c_bus_busy(I2CBus *bus) -{ - return bus->current_dev != NULL; -} - -/* Returns non-zero if the address is not valid. */ -/* TODO: Make this handle multiple masters. */ -int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) -{ - BusChild *kid; - I2CSlave *slave = NULL; - I2CSlaveClass *sc; - - QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { - DeviceState *qdev = kid->child; - I2CSlave *candidate = I2C_SLAVE(qdev); - if (candidate->address == address) { - slave = candidate; - break; - } - } - - if (!slave) { - return 1; - } - - sc = I2C_SLAVE_GET_CLASS(slave); - /* If the bus is already busy, assume this is a repeated - start condition. */ - bus->current_dev = slave; - if (sc->event) { - sc->event(slave, recv ? I2C_START_RECV : I2C_START_SEND); - } - return 0; -} - -void i2c_end_transfer(I2CBus *bus) -{ - I2CSlave *dev = bus->current_dev; - I2CSlaveClass *sc; - - if (!dev) { - return; - } - - sc = I2C_SLAVE_GET_CLASS(dev); - if (sc->event) { - sc->event(dev, I2C_FINISH); - } - - bus->current_dev = NULL; -} - -int i2c_send(I2CBus *bus, uint8_t data) -{ - I2CSlave *dev = bus->current_dev; - I2CSlaveClass *sc; - - if (!dev) { - return -1; - } - - sc = I2C_SLAVE_GET_CLASS(dev); - if (sc->send) { - return sc->send(dev, data); - } - - return -1; -} - -int i2c_recv(I2CBus *bus) -{ - I2CSlave *dev = bus->current_dev; - I2CSlaveClass *sc; - - if (!dev) { - return -1; - } - - sc = I2C_SLAVE_GET_CLASS(dev); - if (sc->recv) { - return sc->recv(dev); - } - - return -1; -} - -void i2c_nack(I2CBus *bus) -{ - I2CSlave *dev = bus->current_dev; - I2CSlaveClass *sc; - - if (!dev) { - return; - } - - sc = I2C_SLAVE_GET_CLASS(dev); - if (sc->event) { - sc->event(dev, I2C_NACK); - } -} - -static int i2c_slave_post_load(void *opaque, int version_id) -{ - I2CSlave *dev = opaque; - I2CBus *bus; - bus = I2C_BUS(qdev_get_parent_bus(DEVICE(dev))); - if (bus->saved_address == dev->address) { - bus->current_dev = dev; - } - return 0; -} - -const VMStateDescription vmstate_i2c_slave = { - .name = "I2CSlave", - .version_id = 1, - .minimum_version_id = 1, - .post_load = i2c_slave_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(address, I2CSlave), - VMSTATE_END_OF_LIST() - } -}; - -static int i2c_slave_qdev_init(DeviceState *dev) -{ - I2CSlave *s = I2C_SLAVE(dev); - I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(s); - - return sc->init(s); -} - -DeviceState *i2c_create_slave(I2CBus *bus, const char *name, uint8_t addr) -{ - DeviceState *dev; - - dev = qdev_create(&bus->qbus, name); - qdev_prop_set_uint8(dev, "address", addr); - qdev_init_nofail(dev); - return dev; -} - -static void i2c_slave_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - k->init = i2c_slave_qdev_init; - set_bit(DEVICE_CATEGORY_MISC, k->categories); - k->bus_type = TYPE_I2C_BUS; - k->props = i2c_props; -} - -static const TypeInfo i2c_slave_type_info = { - .name = TYPE_I2C_SLAVE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(I2CSlave), - .abstract = true, - .class_size = sizeof(I2CSlaveClass), - .class_init = i2c_slave_class_init, -}; - -static void i2c_slave_register_types(void) -{ - type_register_static(&i2c_bus_info); - type_register_static(&i2c_slave_type_info); -} - -type_init(i2c_slave_register_types) diff --git a/qemu/hw/i2c/exynos4210_i2c.c b/qemu/hw/i2c/exynos4210_i2c.c deleted file mode 100644 index 8c2a2c163..000000000 --- a/qemu/hw/i2c/exynos4210_i2c.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Exynos4210 I2C Bus Serial Interface Emulation - * - * Copyright (C) 2012 Samsung Electronics Co Ltd. - * Maksim Kozlov, - * Igor Mitsyanko, - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "qemu/timer.h" -#include "hw/sysbus.h" -#include "hw/i2c/i2c.h" - -#ifndef EXYNOS4_I2C_DEBUG -#define EXYNOS4_I2C_DEBUG 0 -#endif - -#define TYPE_EXYNOS4_I2C "exynos4210.i2c" -#define EXYNOS4_I2C(obj) \ - OBJECT_CHECK(Exynos4210I2CState, (obj), TYPE_EXYNOS4_I2C) - -/* Exynos4210 I2C memory map */ -#define EXYNOS4_I2C_MEM_SIZE 0x14 -#define I2CCON_ADDR 0x00 /* control register */ -#define I2CSTAT_ADDR 0x04 /* control/status register */ -#define I2CADD_ADDR 0x08 /* address register */ -#define I2CDS_ADDR 0x0c /* data shift register */ -#define I2CLC_ADDR 0x10 /* line control register */ - -#define I2CCON_ACK_GEN (1 << 7) -#define I2CCON_INTRS_EN (1 << 5) -#define I2CCON_INT_PEND (1 << 4) - -#define EXYNOS4_I2C_MODE(reg) (((reg) >> 6) & 3) -#define I2C_IN_MASTER_MODE(reg) (((reg) >> 6) & 2) -#define I2CMODE_MASTER_Rx 0x2 -#define I2CMODE_MASTER_Tx 0x3 -#define I2CSTAT_LAST_BIT (1 << 0) -#define I2CSTAT_OUTPUT_EN (1 << 4) -#define I2CSTAT_START_BUSY (1 << 5) - - -#if EXYNOS4_I2C_DEBUG -#define DPRINT(fmt, args...) \ - do { fprintf(stderr, "QEMU I2C: "fmt, ## args); } while (0) - -static const char *exynos4_i2c_get_regname(unsigned offset) -{ - switch (offset) { - case I2CCON_ADDR: - return "I2CCON"; - case I2CSTAT_ADDR: - return "I2CSTAT"; - case I2CADD_ADDR: - return "I2CADD"; - case I2CDS_ADDR: - return "I2CDS"; - case I2CLC_ADDR: - return "I2CLC"; - default: - return "[?]"; - } -} - -#else -#define DPRINT(fmt, args...) do { } while (0) -#endif - -typedef struct Exynos4210I2CState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - I2CBus *bus; - qemu_irq irq; - - uint8_t i2ccon; - uint8_t i2cstat; - uint8_t i2cadd; - uint8_t i2cds; - uint8_t i2clc; - bool scl_free; -} Exynos4210I2CState; - -static inline void exynos4210_i2c_raise_interrupt(Exynos4210I2CState *s) -{ - if (s->i2ccon & I2CCON_INTRS_EN) { - s->i2ccon |= I2CCON_INT_PEND; - qemu_irq_raise(s->irq); - } -} - -static void exynos4210_i2c_data_receive(void *opaque) -{ - Exynos4210I2CState *s = (Exynos4210I2CState *)opaque; - int ret; - - s->i2cstat &= ~I2CSTAT_LAST_BIT; - s->scl_free = false; - ret = i2c_recv(s->bus); - if (ret < 0 && (s->i2ccon & I2CCON_ACK_GEN)) { - s->i2cstat |= I2CSTAT_LAST_BIT; /* Data is not acknowledged */ - } else { - s->i2cds = ret; - } - exynos4210_i2c_raise_interrupt(s); -} - -static void exynos4210_i2c_data_send(void *opaque) -{ - Exynos4210I2CState *s = (Exynos4210I2CState *)opaque; - - s->i2cstat &= ~I2CSTAT_LAST_BIT; - s->scl_free = false; - if (i2c_send(s->bus, s->i2cds) < 0 && (s->i2ccon & I2CCON_ACK_GEN)) { - s->i2cstat |= I2CSTAT_LAST_BIT; - } - exynos4210_i2c_raise_interrupt(s); -} - -static uint64_t exynos4210_i2c_read(void *opaque, hwaddr offset, - unsigned size) -{ - Exynos4210I2CState *s = (Exynos4210I2CState *)opaque; - uint8_t value; - - switch (offset) { - case I2CCON_ADDR: - value = s->i2ccon; - break; - case I2CSTAT_ADDR: - value = s->i2cstat; - break; - case I2CADD_ADDR: - value = s->i2cadd; - break; - case I2CDS_ADDR: - value = s->i2cds; - s->scl_free = true; - if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Rx && - (s->i2cstat & I2CSTAT_START_BUSY) && - !(s->i2ccon & I2CCON_INT_PEND)) { - exynos4210_i2c_data_receive(s); - } - break; - case I2CLC_ADDR: - value = s->i2clc; - break; - default: - value = 0; - DPRINT("ERROR: Bad read offset 0x%x\n", (unsigned int)offset); - break; - } - - DPRINT("read %s [0x%02x] -> 0x%02x\n", exynos4_i2c_get_regname(offset), - (unsigned int)offset, value); - return value; -} - -static void exynos4210_i2c_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - Exynos4210I2CState *s = (Exynos4210I2CState *)opaque; - uint8_t v = value & 0xff; - - DPRINT("write %s [0x%02x] <- 0x%02x\n", exynos4_i2c_get_regname(offset), - (unsigned int)offset, v); - - switch (offset) { - case I2CCON_ADDR: - s->i2ccon = (v & ~I2CCON_INT_PEND) | (s->i2ccon & I2CCON_INT_PEND); - if ((s->i2ccon & I2CCON_INT_PEND) && !(v & I2CCON_INT_PEND)) { - s->i2ccon &= ~I2CCON_INT_PEND; - qemu_irq_lower(s->irq); - if (!(s->i2ccon & I2CCON_INTRS_EN)) { - s->i2cstat &= ~I2CSTAT_START_BUSY; - } - - if (s->i2cstat & I2CSTAT_START_BUSY) { - if (s->scl_free) { - if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Tx) { - exynos4210_i2c_data_send(s); - } else if (EXYNOS4_I2C_MODE(s->i2cstat) == - I2CMODE_MASTER_Rx) { - exynos4210_i2c_data_receive(s); - } - } else { - s->i2ccon |= I2CCON_INT_PEND; - qemu_irq_raise(s->irq); - } - } - } - break; - case I2CSTAT_ADDR: - s->i2cstat = - (s->i2cstat & I2CSTAT_START_BUSY) | (v & ~I2CSTAT_START_BUSY); - - if (!(s->i2cstat & I2CSTAT_OUTPUT_EN)) { - s->i2cstat &= ~I2CSTAT_START_BUSY; - s->scl_free = true; - qemu_irq_lower(s->irq); - break; - } - - /* Nothing to do if in i2c slave mode */ - if (!I2C_IN_MASTER_MODE(s->i2cstat)) { - break; - } - - if (v & I2CSTAT_START_BUSY) { - s->i2cstat &= ~I2CSTAT_LAST_BIT; - s->i2cstat |= I2CSTAT_START_BUSY; /* Line is busy */ - s->scl_free = false; - - /* Generate start bit and send slave address */ - if (i2c_start_transfer(s->bus, s->i2cds >> 1, s->i2cds & 0x1) && - (s->i2ccon & I2CCON_ACK_GEN)) { - s->i2cstat |= I2CSTAT_LAST_BIT; - } else if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Rx) { - exynos4210_i2c_data_receive(s); - } - exynos4210_i2c_raise_interrupt(s); - } else { - i2c_end_transfer(s->bus); - if (!(s->i2ccon & I2CCON_INT_PEND)) { - s->i2cstat &= ~I2CSTAT_START_BUSY; - } - s->scl_free = true; - } - break; - case I2CADD_ADDR: - if ((s->i2cstat & I2CSTAT_OUTPUT_EN) == 0) { - s->i2cadd = v; - } - break; - case I2CDS_ADDR: - if (s->i2cstat & I2CSTAT_OUTPUT_EN) { - s->i2cds = v; - s->scl_free = true; - if (EXYNOS4_I2C_MODE(s->i2cstat) == I2CMODE_MASTER_Tx && - (s->i2cstat & I2CSTAT_START_BUSY) && - !(s->i2ccon & I2CCON_INT_PEND)) { - exynos4210_i2c_data_send(s); - } - } - break; - case I2CLC_ADDR: - s->i2clc = v; - break; - default: - DPRINT("ERROR: Bad write offset 0x%x\n", (unsigned int)offset); - break; - } -} - -static const MemoryRegionOps exynos4210_i2c_ops = { - .read = exynos4210_i2c_read, - .write = exynos4210_i2c_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription exynos4210_i2c_vmstate = { - .name = "exynos4210.i2c", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(i2ccon, Exynos4210I2CState), - VMSTATE_UINT8(i2cstat, Exynos4210I2CState), - VMSTATE_UINT8(i2cds, Exynos4210I2CState), - VMSTATE_UINT8(i2cadd, Exynos4210I2CState), - VMSTATE_UINT8(i2clc, Exynos4210I2CState), - VMSTATE_BOOL(scl_free, Exynos4210I2CState), - VMSTATE_END_OF_LIST() - } -}; - -static void exynos4210_i2c_reset(DeviceState *d) -{ - Exynos4210I2CState *s = EXYNOS4_I2C(d); - - s->i2ccon = 0x00; - s->i2cstat = 0x00; - s->i2cds = 0xFF; - s->i2clc = 0x00; - s->i2cadd = 0xFF; - s->scl_free = true; -} - -static int exynos4210_i2c_realize(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - Exynos4210I2CState *s = EXYNOS4_I2C(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_i2c_ops, s, - TYPE_EXYNOS4_I2C, EXYNOS4_I2C_MEM_SIZE); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - s->bus = i2c_init_bus(dev, "i2c"); - return 0; -} - -static void exynos4210_i2c_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sbdc = SYS_BUS_DEVICE_CLASS(klass); - - dc->vmsd = &exynos4210_i2c_vmstate; - dc->reset = exynos4210_i2c_reset; - sbdc->init = exynos4210_i2c_realize; -} - -static const TypeInfo exynos4210_i2c_type_info = { - .name = TYPE_EXYNOS4_I2C, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Exynos4210I2CState), - .class_init = exynos4210_i2c_class_init, -}; - -static void exynos4210_i2c_register_types(void) -{ - type_register_static(&exynos4210_i2c_type_info); -} - -type_init(exynos4210_i2c_register_types) diff --git a/qemu/hw/i2c/imx_i2c.c b/qemu/hw/i2c/imx_i2c.c deleted file mode 100644 index a01e43ebe..000000000 --- a/qemu/hw/i2c/imx_i2c.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * i.MX I2C Bus Serial Interface Emulation - * - * Copyright (C) 2013 Jean-Christophe Dubois. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "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 deleted file mode 100644 index 67fbbff8e..000000000 --- a/qemu/hw/i2c/omap_i2c.c +++ /dev/null @@ -1,509 +0,0 @@ -/* - * TI OMAP on-chip I2C controller. Only "new I2C" mode supported. - * - * Copyright (C) 2007 Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "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) - -typedef struct OMAPI2CState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq irq; - qemu_irq drq[2]; - I2CBus *bus; - - uint8_t revision; - void *iclk; - void *fclk; - - uint8_t mask; - uint16_t stat; - uint16_t dma; - uint16_t count; - int count_cur; - uint32_t fifo; - int rxlen; - int txlen; - uint16_t control; - uint16_t addr[2]; - uint8_t divider; - uint8_t times[2]; - uint16_t test; -} OMAPI2CState; - -#define OMAP2_INTR_REV 0x34 -#define OMAP2_GC_REV 0x34 - -static void omap_i2c_interrupts_update(OMAPI2CState *s) -{ - qemu_set_irq(s->irq, s->stat & s->mask); - if ((s->dma >> 15) & 1) /* RDMA_EN */ - qemu_set_irq(s->drq[0], (s->stat >> 3) & 1); /* RRDY */ - if ((s->dma >> 7) & 1) /* XDMA_EN */ - qemu_set_irq(s->drq[1], (s->stat >> 4) & 1); /* XRDY */ -} - -static void omap_i2c_fifo_run(OMAPI2CState *s) -{ - int ack = 1; - - if (!i2c_bus_busy(s->bus)) - return; - - if ((s->control >> 2) & 1) { /* RM */ - if ((s->control >> 1) & 1) { /* STP */ - i2c_end_transfer(s->bus); - s->control &= ~(1 << 1); /* STP */ - s->count_cur = s->count; - s->txlen = 0; - } else if ((s->control >> 9) & 1) { /* TRX */ - while (ack && s->txlen) - ack = (i2c_send(s->bus, - (s->fifo >> ((-- s->txlen) << 3)) & - 0xff) >= 0); - s->stat |= 1 << 4; /* XRDY */ - } else { - while (s->rxlen < 4) - s->fifo |= i2c_recv(s->bus) << ((s->rxlen ++) << 3); - s->stat |= 1 << 3; /* RRDY */ - } - } else { - if ((s->control >> 9) & 1) { /* TRX */ - while (ack && s->count_cur && s->txlen) { - ack = (i2c_send(s->bus, - (s->fifo >> ((-- s->txlen) << 3)) & - 0xff) >= 0); - s->count_cur --; - } - if (ack && s->count_cur) - s->stat |= 1 << 4; /* XRDY */ - else - s->stat &= ~(1 << 4); /* XRDY */ - if (!s->count_cur) { - s->stat |= 1 << 2; /* ARDY */ - s->control &= ~(1 << 10); /* MST */ - } - } else { - while (s->count_cur && s->rxlen < 4) { - s->fifo |= i2c_recv(s->bus) << ((s->rxlen ++) << 3); - s->count_cur --; - } - if (s->rxlen) - s->stat |= 1 << 3; /* RRDY */ - else - s->stat &= ~(1 << 3); /* RRDY */ - } - if (!s->count_cur) { - if ((s->control >> 1) & 1) { /* STP */ - i2c_end_transfer(s->bus); - s->control &= ~(1 << 1); /* STP */ - s->count_cur = s->count; - s->txlen = 0; - } else { - s->stat |= 1 << 2; /* ARDY */ - s->control &= ~(1 << 10); /* MST */ - } - } - } - - s->stat |= (!ack) << 1; /* NACK */ - if (!ack) - s->control &= ~(1 << 1); /* STP */ -} - -static void omap_i2c_reset(DeviceState *dev) -{ - OMAPI2CState *s = OMAP_I2C(dev); - - s->mask = 0; - s->stat = 0; - s->dma = 0; - s->count = 0; - s->count_cur = 0; - s->fifo = 0; - s->rxlen = 0; - s->txlen = 0; - s->control = 0; - s->addr[0] = 0; - s->addr[1] = 0; - s->divider = 0; - s->times[0] = 0; - s->times[1] = 0; - s->test = 0; -} - -static uint32_t omap_i2c_read(void *opaque, hwaddr addr) -{ - OMAPI2CState *s = opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - uint16_t ret; - - switch (offset) { - case 0x00: /* I2C_REV */ - return s->revision; /* REV */ - - case 0x04: /* I2C_IE */ - return s->mask; - - case 0x08: /* I2C_STAT */ - return s->stat | (i2c_bus_busy(s->bus) << 12); - - case 0x0c: /* I2C_IV */ - if (s->revision >= OMAP2_INTR_REV) - break; - ret = ctz32(s->stat & s->mask); - if (ret != 32) { - s->stat ^= 1 << ret; - ret++; - } else { - ret = 0; - } - omap_i2c_interrupts_update(s); - return ret; - - case 0x10: /* I2C_SYSS */ - return (s->control >> 15) & 1; /* I2C_EN */ - - case 0x14: /* I2C_BUF */ - return s->dma; - - case 0x18: /* I2C_CNT */ - return s->count_cur; /* DCOUNT */ - - case 0x1c: /* I2C_DATA */ - ret = 0; - if (s->control & (1 << 14)) { /* BE */ - ret |= ((s->fifo >> 0) & 0xff) << 8; - ret |= ((s->fifo >> 8) & 0xff) << 0; - } else { - ret |= ((s->fifo >> 8) & 0xff) << 8; - ret |= ((s->fifo >> 0) & 0xff) << 0; - } - if (s->rxlen == 1) { - s->stat |= 1 << 15; /* SBD */ - s->rxlen = 0; - } else if (s->rxlen > 1) { - if (s->rxlen > 2) - s->fifo >>= 16; - s->rxlen -= 2; - } else { - /* XXX: remote access (qualifier) error - what's that? */ - } - if (!s->rxlen) { - s->stat &= ~(1 << 3); /* RRDY */ - if (((s->control >> 10) & 1) && /* MST */ - ((~s->control >> 9) & 1)) { /* TRX */ - s->stat |= 1 << 2; /* ARDY */ - s->control &= ~(1 << 10); /* MST */ - } - } - s->stat &= ~(1 << 11); /* ROVR */ - omap_i2c_fifo_run(s); - omap_i2c_interrupts_update(s); - return ret; - - case 0x20: /* I2C_SYSC */ - return 0; - - case 0x24: /* I2C_CON */ - return s->control; - - case 0x28: /* I2C_OA */ - return s->addr[0]; - - case 0x2c: /* I2C_SA */ - return s->addr[1]; - - case 0x30: /* I2C_PSC */ - return s->divider; - - case 0x34: /* I2C_SCLL */ - return s->times[0]; - - case 0x38: /* I2C_SCLH */ - return s->times[1]; - - case 0x3c: /* I2C_SYSTEST */ - if (s->test & (1 << 15)) { /* ST_EN */ - s->test ^= 0xa; - return s->test; - } else - return s->test & ~0x300f; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_i2c_write(void *opaque, hwaddr addr, - uint32_t value) -{ - OMAPI2CState *s = opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - int nack; - - switch (offset) { - case 0x00: /* I2C_REV */ - case 0x0c: /* I2C_IV */ - case 0x10: /* I2C_SYSS */ - OMAP_RO_REG(addr); - return; - - case 0x04: /* I2C_IE */ - s->mask = value & (s->revision < OMAP2_GC_REV ? 0x1f : 0x3f); - break; - - case 0x08: /* I2C_STAT */ - if (s->revision < OMAP2_INTR_REV) { - OMAP_RO_REG(addr); - return; - } - - /* RRDY and XRDY are reset by hardware. (in all versions???) */ - s->stat &= ~(value & 0x27); - omap_i2c_interrupts_update(s); - break; - - case 0x14: /* I2C_BUF */ - s->dma = value & 0x8080; - if (value & (1 << 15)) /* RDMA_EN */ - s->mask &= ~(1 << 3); /* RRDY_IE */ - if (value & (1 << 7)) /* XDMA_EN */ - s->mask &= ~(1 << 4); /* XRDY_IE */ - break; - - case 0x18: /* I2C_CNT */ - s->count = value; /* DCOUNT */ - break; - - case 0x1c: /* I2C_DATA */ - if (s->txlen > 2) { - /* XXX: remote access (qualifier) error - what's that? */ - break; - } - s->fifo <<= 16; - s->txlen += 2; - if (s->control & (1 << 14)) { /* BE */ - s->fifo |= ((value >> 8) & 0xff) << 8; - s->fifo |= ((value >> 0) & 0xff) << 0; - } else { - s->fifo |= ((value >> 0) & 0xff) << 8; - s->fifo |= ((value >> 8) & 0xff) << 0; - } - s->stat &= ~(1 << 10); /* XUDF */ - if (s->txlen > 2) - s->stat &= ~(1 << 4); /* XRDY */ - omap_i2c_fifo_run(s); - omap_i2c_interrupts_update(s); - break; - - case 0x20: /* I2C_SYSC */ - if (s->revision < OMAP2_INTR_REV) { - OMAP_BAD_REG(addr); - return; - } - - if (value & 2) { - omap_i2c_reset(DEVICE(s)); - } - break; - - case 0x24: /* I2C_CON */ - s->control = value & 0xcf87; - if (~value & (1 << 15)) { /* I2C_EN */ - if (s->revision < OMAP2_INTR_REV) { - omap_i2c_reset(DEVICE(s)); - } - break; - } - if ((value & (1 << 15)) && !(value & (1 << 10))) { /* MST */ - fprintf(stderr, "%s: I^2C slave mode not supported\n", - __FUNCTION__); - break; - } - if ((value & (1 << 15)) && value & (1 << 8)) { /* XA */ - fprintf(stderr, "%s: 10-bit addressing mode not supported\n", - __FUNCTION__); - break; - } - if ((value & (1 << 15)) && value & (1 << 0)) { /* STT */ - nack = !!i2c_start_transfer(s->bus, s->addr[1], /* SA */ - (~value >> 9) & 1); /* TRX */ - s->stat |= nack << 1; /* NACK */ - s->control &= ~(1 << 0); /* STT */ - s->fifo = 0; - if (nack) - s->control &= ~(1 << 1); /* STP */ - else { - s->count_cur = s->count; - omap_i2c_fifo_run(s); - } - omap_i2c_interrupts_update(s); - } - break; - - case 0x28: /* I2C_OA */ - s->addr[0] = value & 0x3ff; - break; - - case 0x2c: /* I2C_SA */ - s->addr[1] = value & 0x3ff; - break; - - case 0x30: /* I2C_PSC */ - s->divider = value; - break; - - case 0x34: /* I2C_SCLL */ - s->times[0] = value; - break; - - case 0x38: /* I2C_SCLH */ - s->times[1] = value; - break; - - case 0x3c: /* I2C_SYSTEST */ - s->test = value & 0xf80f; - if (value & (1 << 11)) /* SBB */ - if (s->revision >= OMAP2_INTR_REV) { - s->stat |= 0x3f; - omap_i2c_interrupts_update(s); - } - if (value & (1 << 15)) /* ST_EN */ - fprintf(stderr, "%s: System Test not supported\n", __FUNCTION__); - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static void omap_i2c_writeb(void *opaque, hwaddr addr, - uint32_t value) -{ - OMAPI2CState *s = opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - - switch (offset) { - case 0x1c: /* I2C_DATA */ - if (s->txlen > 2) { - /* XXX: remote access (qualifier) error - what's that? */ - break; - } - s->fifo <<= 8; - s->txlen += 1; - s->fifo |= value & 0xff; - s->stat &= ~(1 << 10); /* XUDF */ - if (s->txlen > 2) - s->stat &= ~(1 << 4); /* XRDY */ - omap_i2c_fifo_run(s); - omap_i2c_interrupts_update(s); - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_i2c_ops = { - .old_mmio = { - .read = { - omap_badwidth_read16, - omap_i2c_read, - omap_badwidth_read16, - }, - .write = { - omap_i2c_writeb, /* Only the last fifo write can be 8 bit. */ - omap_i2c_write, - omap_badwidth_write16, - }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int omap_i2c_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - OMAPI2CState *s = OMAP_I2C(dev); - - if (!s->fclk) { - 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 */ - 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]); - memory_region_init_io(&s->iomem, OBJECT(s), &omap_i2c_ops, s, "omap.i2c", - (s->revision < OMAP2_INTR_REV) ? 0x800 : 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - s->bus = i2c_init_bus(dev, NULL); - return 0; -} - -static Property omap_i2c_properties[] = { - DEFINE_PROP_UINT8("revision", OMAPI2CState, revision, 0), - DEFINE_PROP_PTR("iclk", OMAPI2CState, iclk), - DEFINE_PROP_PTR("fclk", OMAPI2CState, fclk), - DEFINE_PROP_END_OF_LIST(), -}; - -static void omap_i2c_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = omap_i2c_init; - dc->props = omap_i2c_properties; - dc->reset = omap_i2c_reset; - /* Reason: pointer properties "iclk", "fclk" */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo omap_i2c_info = { - .name = TYPE_OMAP_I2C, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(OMAPI2CState), - .class_init = omap_i2c_class_init, -}; - -static void omap_i2c_register_types(void) -{ - type_register_static(&omap_i2c_info); -} - -I2CBus *omap_i2c_bus(DeviceState *omap_i2c) -{ - OMAPI2CState *s = OMAP_I2C(omap_i2c); - return s->bus; -} - -type_init(omap_i2c_register_types) diff --git a/qemu/hw/i2c/pm_smbus.c b/qemu/hw/i2c/pm_smbus.c deleted file mode 100644 index 6fc3923f5..000000000 --- a/qemu/hw/i2c/pm_smbus.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - * PC SMBus implementation - * splitted from acpi.c - * - * 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, see - * . - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/i386/pc.h" -#include "hw/i2c/pm_smbus.h" -#include "hw/i2c/smbus.h" - -/* no save/load? */ - -#define SMBHSTSTS 0x00 -#define SMBHSTCNT 0x02 -#define SMBHSTCMD 0x03 -#define SMBHSTADD 0x04 -#define SMBHSTDAT0 0x05 -#define SMBHSTDAT1 0x06 -#define SMBBLKDAT 0x07 - -#define STS_HOST_BUSY (1) -#define STS_INTR (1<<1) -#define STS_DEV_ERR (1<<2) -#define STS_BUS_ERR (1<<3) -#define STS_FAILED (1<<4) -#define STS_SMBALERT (1<<5) -#define STS_INUSE_STS (1<<6) -#define STS_BYTE_DONE (1<<7) -/* Signs of successfully transaction end : -* ByteDoneStatus = 1 (STS_BYTE_DONE) and INTR = 1 (STS_INTR ) -*/ - -//#define DEBUG - -#ifdef DEBUG -# define SMBUS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) -#else -# define SMBUS_DPRINTF(format, ...) do { } while (0) -#endif - - -static void smb_transaction(PMSMBus *s) -{ - uint8_t prot = (s->smb_ctl >> 2) & 0x07; - uint8_t read = s->smb_addr & 0x01; - uint8_t cmd = s->smb_cmd; - uint8_t addr = s->smb_addr >> 1; - I2CBus *bus = s->smbus; - int ret; - - SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot); - /* Transaction isn't exec if STS_DEV_ERR bit set */ - if ((s->smb_stat & STS_DEV_ERR) != 0) { - goto error; - } - switch(prot) { - case 0x0: - ret = smbus_quick_command(bus, addr, read); - goto done; - case 0x1: - if (read) { - ret = smbus_receive_byte(bus, addr); - goto data8; - } else { - ret = smbus_send_byte(bus, addr, cmd); - goto done; - } - case 0x2: - if (read) { - ret = smbus_read_byte(bus, addr, cmd); - goto data8; - } else { - ret = smbus_write_byte(bus, addr, cmd, s->smb_data0); - goto done; - } - break; - case 0x3: - if (read) { - ret = smbus_read_word(bus, addr, cmd); - goto data16; - } else { - ret = smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0); - goto done; - } - break; - case 0x5: - if (read) { - ret = smbus_read_block(bus, addr, cmd, s->smb_data); - goto data8; - } else { - ret = smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0); - goto done; - } - break; - default: - goto error; - } - abort(); - -data16: - if (ret < 0) { - goto error; - } - s->smb_data1 = ret >> 8; -data8: - if (ret < 0) { - goto error; - } - s->smb_data0 = ret; -done: - if (ret < 0) { - goto error; - } - s->smb_stat |= STS_BYTE_DONE | STS_INTR; - return; - -error: - s->smb_stat |= STS_DEV_ERR; - return; - -} - -static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, - unsigned width) -{ - PMSMBus *s = opaque; - - SMBUS_DPRINTF("SMB writeb port=0x%04" HWADDR_PRIx - " val=0x%02" PRIx64 "\n", addr, val); - switch(addr) { - case SMBHSTSTS: - s->smb_stat = (~(val & 0xff)) & s->smb_stat; - s->smb_index = 0; - break; - case SMBHSTCNT: - s->smb_ctl = val; - if (val & 0x40) - smb_transaction(s); - break; - case SMBHSTCMD: - s->smb_cmd = val; - break; - case SMBHSTADD: - s->smb_addr = val; - break; - case SMBHSTDAT0: - s->smb_data0 = val; - break; - case SMBHSTDAT1: - s->smb_data1 = val; - break; - case SMBBLKDAT: - s->smb_data[s->smb_index++] = val; - if (s->smb_index > 31) - s->smb_index = 0; - break; - default: - break; - } -} - -static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) -{ - PMSMBus *s = opaque; - uint32_t val; - - switch(addr) { - case SMBHSTSTS: - val = s->smb_stat; - break; - case SMBHSTCNT: - s->smb_index = 0; - val = s->smb_ctl & 0x1f; - break; - case SMBHSTCMD: - val = s->smb_cmd; - break; - case SMBHSTADD: - val = s->smb_addr; - break; - case SMBHSTDAT0: - val = s->smb_data0; - break; - case SMBHSTDAT1: - val = s->smb_data1; - break; - case SMBBLKDAT: - val = s->smb_data[s->smb_index++]; - if (s->smb_index > 31) - s->smb_index = 0; - break; - default: - val = 0; - break; - } - SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n", addr, val); - return val; -} - -static const MemoryRegionOps pm_smbus_ops = { - .read = smb_ioport_readb, - .write = smb_ioport_writeb, - .valid.min_access_size = 1, - .valid.max_access_size = 1, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -void pm_smbus_init(DeviceState *parent, PMSMBus *smb) -{ - smb->smbus = i2c_init_bus(parent, "i2c"); - memory_region_init_io(&smb->io, OBJECT(parent), &pm_smbus_ops, smb, - "pm-smbus", 64); -} diff --git a/qemu/hw/i2c/smbus.c b/qemu/hw/i2c/smbus.c deleted file mode 100644 index 3979b3dad..000000000 --- a/qemu/hw/i2c/smbus.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * QEMU SMBus device emulation. - * - * Copyright (c) 2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the LGPL. - */ - -/* TODO: Implement PEC. */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/i2c/i2c.h" -#include "hw/i2c/smbus.h" - -//#define DEBUG_SMBUS 1 - -#ifdef DEBUG_SMBUS -#define DPRINTF(fmt, ...) \ -do { printf("smbus(%02x): " fmt , dev->i2c.address, ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0) -#endif - -enum { - SMBUS_IDLE, - SMBUS_WRITE_DATA, - SMBUS_RECV_BYTE, - SMBUS_READ_DATA, - SMBUS_DONE, - SMBUS_CONFUSED = -1 -}; - -static void smbus_do_quick_cmd(SMBusDevice *dev, int recv) -{ - SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); - - DPRINTF("Quick Command %d\n", recv); - if (sc->quick_cmd) { - sc->quick_cmd(dev, recv); - } -} - -static void smbus_do_write(SMBusDevice *dev) -{ - SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); - - if (dev->data_len == 0) { - smbus_do_quick_cmd(dev, 0); - } else if (dev->data_len == 1) { - DPRINTF("Send Byte\n"); - if (sc->send_byte) { - sc->send_byte(dev, dev->data_buf[0]); - } - } else { - dev->command = dev->data_buf[0]; - DPRINTF("Command %d len %d\n", dev->command, dev->data_len - 1); - if (sc->write_data) { - sc->write_data(dev, dev->command, dev->data_buf + 1, - dev->data_len - 1); - } - } -} - -static void smbus_i2c_event(I2CSlave *s, enum i2c_event event) -{ - SMBusDevice *dev = SMBUS_DEVICE(s); - - switch (event) { - case I2C_START_SEND: - switch (dev->mode) { - case SMBUS_IDLE: - DPRINTF("Incoming data\n"); - dev->mode = SMBUS_WRITE_DATA; - break; - default: - BADF("Unexpected send start condition in state %d\n", dev->mode); - dev->mode = SMBUS_CONFUSED; - break; - } - break; - - case I2C_START_RECV: - switch (dev->mode) { - case SMBUS_IDLE: - DPRINTF("Read mode\n"); - dev->mode = SMBUS_RECV_BYTE; - break; - case SMBUS_WRITE_DATA: - if (dev->data_len == 0) { - BADF("Read after write with no data\n"); - dev->mode = SMBUS_CONFUSED; - } else { - if (dev->data_len > 1) { - smbus_do_write(dev); - } else { - dev->command = dev->data_buf[0]; - DPRINTF("%02x: Command %d\n", dev->i2c.address, - dev->command); - } - DPRINTF("Read mode\n"); - dev->data_len = 0; - dev->mode = SMBUS_READ_DATA; - } - break; - default: - BADF("Unexpected recv start condition in state %d\n", dev->mode); - dev->mode = SMBUS_CONFUSED; - break; - } - break; - - case I2C_FINISH: - switch (dev->mode) { - case SMBUS_WRITE_DATA: - smbus_do_write(dev); - break; - case SMBUS_RECV_BYTE: - smbus_do_quick_cmd(dev, 1); - break; - case SMBUS_READ_DATA: - BADF("Unexpected stop during receive\n"); - break; - default: - /* Nothing to do. */ - break; - } - dev->mode = SMBUS_IDLE; - dev->data_len = 0; - break; - - case I2C_NACK: - switch (dev->mode) { - case SMBUS_DONE: - /* Nothing to do. */ - break; - case SMBUS_READ_DATA: - dev->mode = SMBUS_DONE; - break; - default: - BADF("Unexpected NACK in state %d\n", dev->mode); - dev->mode = SMBUS_CONFUSED; - break; - } - } -} - -static int smbus_i2c_recv(I2CSlave *s) -{ - SMBusDevice *dev = SMBUS_DEVICE(s); - SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); - int ret; - - switch (dev->mode) { - case SMBUS_RECV_BYTE: - if (sc->receive_byte) { - ret = sc->receive_byte(dev); - } else { - ret = 0; - } - DPRINTF("Receive Byte %02x\n", ret); - dev->mode = SMBUS_DONE; - break; - case SMBUS_READ_DATA: - if (sc->read_data) { - ret = sc->read_data(dev, dev->command, dev->data_len); - dev->data_len++; - } else { - ret = 0; - } - DPRINTF("Read data %02x\n", ret); - break; - default: - BADF("Unexpected read in state %d\n", dev->mode); - dev->mode = SMBUS_CONFUSED; - ret = 0; - break; - } - return ret; -} - -static int smbus_i2c_send(I2CSlave *s, uint8_t data) -{ - SMBusDevice *dev = SMBUS_DEVICE(s); - - switch (dev->mode) { - case SMBUS_WRITE_DATA: - DPRINTF("Write data %02x\n", data); - dev->data_buf[dev->data_len++] = data; - break; - default: - BADF("Unexpected write in state %d\n", dev->mode); - break; - } - return 0; -} - -static int smbus_device_init(I2CSlave *i2c) -{ - SMBusDevice *dev = SMBUS_DEVICE(i2c); - SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); - - return sc->init(dev); -} - -/* Master device commands. */ -int smbus_quick_command(I2CBus *bus, uint8_t addr, int read) -{ - if (i2c_start_transfer(bus, addr, read)) { - return -1; - } - i2c_end_transfer(bus); - return 0; -} - -int smbus_receive_byte(I2CBus *bus, uint8_t addr) -{ - uint8_t data; - - if (i2c_start_transfer(bus, addr, 1)) { - return -1; - } - data = i2c_recv(bus); - i2c_nack(bus); - i2c_end_transfer(bus); - return data; -} - -int smbus_send_byte(I2CBus *bus, uint8_t addr, uint8_t data) -{ - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, data); - i2c_end_transfer(bus); - return 0; -} - -int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command) -{ - uint8_t data; - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - i2c_start_transfer(bus, addr, 1); - data = i2c_recv(bus); - i2c_nack(bus); - i2c_end_transfer(bus); - return data; -} - -int smbus_write_byte(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t data) -{ - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - i2c_send(bus, data); - i2c_end_transfer(bus); - return 0; -} - -int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command) -{ - uint16_t data; - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - i2c_start_transfer(bus, addr, 1); - data = i2c_recv(bus); - data |= i2c_recv(bus) << 8; - i2c_nack(bus); - i2c_end_transfer(bus); - return data; -} - -int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data) -{ - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - i2c_send(bus, data & 0xff); - i2c_send(bus, data >> 8); - i2c_end_transfer(bus); - return 0; -} - -int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data) -{ - int len; - int i; - - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - i2c_start_transfer(bus, addr, 1); - len = i2c_recv(bus); - if (len > 32) { - len = 0; - } - for (i = 0; i < len; i++) { - data[i] = i2c_recv(bus); - } - i2c_nack(bus); - i2c_end_transfer(bus); - return len; -} - -int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, - int len) -{ - int i; - - if (len > 32) - len = 32; - - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - i2c_send(bus, len); - for (i = 0; i < len; i++) { - i2c_send(bus, data[i]); - } - i2c_end_transfer(bus); - return 0; -} - -static void smbus_device_class_init(ObjectClass *klass, void *data) -{ - I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); - - sc->init = smbus_device_init; - sc->event = smbus_i2c_event; - sc->recv = smbus_i2c_recv; - sc->send = smbus_i2c_send; -} - -static const TypeInfo smbus_device_type_info = { - .name = TYPE_SMBUS_DEVICE, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(SMBusDevice), - .abstract = true, - .class_size = sizeof(SMBusDeviceClass), - .class_init = smbus_device_class_init, -}; - -static void smbus_device_register_types(void) -{ - type_register_static(&smbus_device_type_info); -} - -type_init(smbus_device_register_types) diff --git a/qemu/hw/i2c/smbus_eeprom.c b/qemu/hw/i2c/smbus_eeprom.c deleted file mode 100644 index 5b7bd891b..000000000 --- a/qemu/hw/i2c/smbus_eeprom.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * QEMU SMBus EEPROM device - * - * Copyright (c) 2007 Arastra, Inc. - * - * 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/i2c/i2c.h" -#include "hw/i2c/smbus.h" - -//#define DEBUG - -typedef struct SMBusEEPROMDevice { - SMBusDevice smbusdev; - void *data; - uint8_t offset; -} SMBusEEPROMDevice; - -static void eeprom_quick_cmd(SMBusDevice *dev, uint8_t read) -{ -#ifdef DEBUG - printf("eeprom_quick_cmd: addr=0x%02x read=%d\n", dev->i2c.address, read); -#endif -} - -static void eeprom_send_byte(SMBusDevice *dev, uint8_t val) -{ - SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; -#ifdef DEBUG - printf("eeprom_send_byte: addr=0x%02x val=0x%02x\n", - dev->i2c.address, val); -#endif - eeprom->offset = val; -} - -static uint8_t eeprom_receive_byte(SMBusDevice *dev) -{ - SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; - uint8_t *data = eeprom->data; - uint8_t val = data[eeprom->offset++]; -#ifdef DEBUG - printf("eeprom_receive_byte: addr=0x%02x val=0x%02x\n", - dev->i2c.address, val); -#endif - return val; -} - -static void eeprom_write_data(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len) -{ - SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; - int n; -#ifdef DEBUG - printf("eeprom_write_byte: addr=0x%02x cmd=0x%02x val=0x%02x\n", - dev->i2c.address, cmd, buf[0]); -#endif - /* A page write operation is not a valid SMBus command. - It is a block write without a length byte. Fortunately we - get the full block anyway. */ - /* TODO: Should this set the current location? */ - if (cmd + len > 256) - n = 256 - cmd; - else - n = len; - memcpy(eeprom->data + cmd, buf, n); - len -= n; - if (len) - memcpy(eeprom->data, buf + n, len); -} - -static uint8_t eeprom_read_data(SMBusDevice *dev, uint8_t cmd, int n) -{ - SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; - /* If this is the first byte then set the current position. */ - if (n == 0) - eeprom->offset = cmd; - /* As with writes, we implement block reads without the - SMBus length byte. */ - return eeprom_receive_byte(dev); -} - -static int smbus_eeprom_initfn(SMBusDevice *dev) -{ - SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *)dev; - - eeprom->offset = 0; - return 0; -} - -static Property smbus_eeprom_properties[] = { - DEFINE_PROP_PTR("data", SMBusEEPROMDevice, data), - DEFINE_PROP_END_OF_LIST(), -}; - -static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(klass); - - sc->init = smbus_eeprom_initfn; - sc->quick_cmd = eeprom_quick_cmd; - sc->send_byte = eeprom_send_byte; - sc->receive_byte = eeprom_receive_byte; - sc->write_data = eeprom_write_data; - sc->read_data = eeprom_read_data; - dc->props = smbus_eeprom_properties; - /* Reason: pointer property "data" */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo smbus_eeprom_info = { - .name = "smbus-eeprom", - .parent = TYPE_SMBUS_DEVICE, - .instance_size = sizeof(SMBusEEPROMDevice), - .class_init = smbus_eeprom_class_initfn, -}; - -static void smbus_eeprom_register_types(void) -{ - type_register_static(&smbus_eeprom_info); -} - -type_init(smbus_eeprom_register_types) - -void smbus_eeprom_init(I2CBus *smbus, int nb_eeprom, - const uint8_t *eeprom_spd, int eeprom_spd_size) -{ - int i; - uint8_t *eeprom_buf = g_malloc0(8 * 256); /* XXX: make this persistent */ - if (eeprom_spd_size > 0) { - memcpy(eeprom_buf, eeprom_spd, eeprom_spd_size); - } - - for (i = 0; i < nb_eeprom; i++) { - DeviceState *eeprom; - eeprom = qdev_create((BusState *)smbus, "smbus-eeprom"); - qdev_prop_set_uint8(eeprom, "address", 0x50 + i); - qdev_prop_set_ptr(eeprom, "data", eeprom_buf + (i * 256)); - qdev_init_nofail(eeprom); - } -} diff --git a/qemu/hw/i2c/smbus_ich9.c b/qemu/hw/i2c/smbus_ich9.c deleted file mode 100644 index 498f03e83..000000000 --- a/qemu/hw/i2c/smbus_ich9.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * ACPI implementation - * - * Copyright (c) 2006 Fabrice Bellard - * Copyright (c) 2009 Isaku Yamahata - * VA Linux Systems Japan K.K. - * Copyright (C) 2012 Jason Baron - * - * This is based on acpi.c, but heavily rewritten. - * - * 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, see - * - * 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/i2c/pm_smbus.h" -#include "hw/pci/pci.h" -#include "sysemu/sysemu.h" -#include "hw/i2c/i2c.h" -#include "hw/i2c/smbus.h" - -#include "hw/i386/ich9.h" - -#define TYPE_ICH9_SMB_DEVICE "ICH9 SMB" -#define ICH9_SMB_DEVICE(obj) \ - OBJECT_CHECK(ICH9SMBState, (obj), TYPE_ICH9_SMB_DEVICE) - -typedef struct ICH9SMBState { - PCIDevice dev; - - PMSMBus smb; -} ICH9SMBState; - -static const VMStateDescription vmstate_ich9_smbus = { - .name = "ich9_smb", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, struct ICH9SMBState), - VMSTATE_END_OF_LIST() - } -}; - -static void ich9_smbus_write_config(PCIDevice *d, uint32_t address, - uint32_t val, int len) -{ - ICH9SMBState *s = ICH9_SMB_DEVICE(d); - - pci_default_write_config(d, address, val, len); - if (range_covers_byte(address, len, ICH9_SMB_HOSTC)) { - uint8_t hostc = s->dev.config[ICH9_SMB_HOSTC]; - if ((hostc & ICH9_SMB_HOSTC_HST_EN) && - !(hostc & ICH9_SMB_HOSTC_I2C_EN)) { - memory_region_set_enabled(&s->smb.io, true); - } else { - memory_region_set_enabled(&s->smb.io, false); - } - } -} - -static void ich9_smbus_realize(PCIDevice *d, Error **errp) -{ - ICH9SMBState *s = ICH9_SMB_DEVICE(d); - - /* TODO? D31IP.SMIP in chipset configuration space */ - pci_config_set_interrupt_pin(d->config, 0x01); /* interrupt pin 1 */ - - pci_set_byte(d->config + ICH9_SMB_HOSTC, 0); - /* TODO bar0, bar1: 64bit BAR support*/ - - pm_smbus_init(&d->qdev, &s->smb); - pci_register_bar(d, ICH9_SMB_SMB_BASE_BAR, PCI_BASE_ADDRESS_SPACE_IO, - &s->smb.io); -} - -static void ich9_smb_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_ICH9_6; - k->revision = ICH9_A2_SMB_REVISION; - k->class_id = PCI_CLASS_SERIAL_SMBUS; - dc->vmsd = &vmstate_ich9_smbus; - dc->desc = "ICH9 SMBUS Bridge"; - k->realize = ich9_smbus_realize; - k->config_write = ich9_smbus_write_config; - /* - * Reason: part of ICH9 southbridge, needs to be wired up by - * pc_q35_init() - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base) -{ - PCIDevice *d = - pci_create_simple_multifunction(bus, devfn, true, TYPE_ICH9_SMB_DEVICE); - ICH9SMBState *s = ICH9_SMB_DEVICE(d); - return s->smb.smbus; -} - -static const TypeInfo ich9_smb_info = { - .name = TYPE_ICH9_SMB_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(ICH9SMBState), - .class_init = ich9_smb_class_init, -}; - -static void ich9_smb_register(void) -{ - type_register_static(&ich9_smb_info); -} - -type_init(ich9_smb_register); diff --git a/qemu/hw/i2c/versatile_i2c.c b/qemu/hw/i2c/versatile_i2c.c deleted file mode 100644 index fee3bc761..000000000 --- a/qemu/hw/i2c/versatile_i2c.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * ARM Versatile I2C controller - * - * Copyright (c) 2006-2007 CodeSourcery. - * Copyright (c) 2012 Oskar Andero - * - * This file is derived from hw/realview.c by Paul Brook - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "bitbang_i2c.h" - -#define TYPE_VERSATILE_I2C "versatile_i2c" -#define VERSATILE_I2C(obj) \ - OBJECT_CHECK(VersatileI2CState, (obj), TYPE_VERSATILE_I2C) - -typedef struct VersatileI2CState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - bitbang_i2c_interface *bitbang; - int out; - int in; -} VersatileI2CState; - -static uint64_t versatile_i2c_read(void *opaque, hwaddr offset, - unsigned size) -{ - VersatileI2CState *s = (VersatileI2CState *)opaque; - - if (offset == 0) { - return (s->out & 1) | (s->in << 1); - } else { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset 0x%x\n", __func__, (int)offset); - return -1; - } -} - -static void versatile_i2c_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - VersatileI2CState *s = (VersatileI2CState *)opaque; - - switch (offset) { - case 0: - s->out |= value & 3; - break; - case 4: - s->out &= ~value; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset 0x%x\n", __func__, (int)offset); - } - bitbang_i2c_set(s->bitbang, BITBANG_I2C_SCL, (s->out & 1) != 0); - s->in = bitbang_i2c_set(s->bitbang, BITBANG_I2C_SDA, (s->out & 2) != 0); -} - -static const MemoryRegionOps versatile_i2c_ops = { - .read = versatile_i2c_read, - .write = versatile_i2c_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int versatile_i2c_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - VersatileI2CState *s = VERSATILE_I2C(dev); - I2CBus *bus; - - bus = i2c_init_bus(dev, "i2c"); - s->bitbang = bitbang_i2c_init(bus); - memory_region_init_io(&s->iomem, OBJECT(s), &versatile_i2c_ops, s, - "versatile_i2c", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - return 0; -} - -static void versatile_i2c_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = versatile_i2c_init; -} - -static const TypeInfo versatile_i2c_info = { - .name = TYPE_VERSATILE_I2C, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(VersatileI2CState), - .class_init = versatile_i2c_class_init, -}; - -static void versatile_i2c_register_types(void) -{ - type_register_static(&versatile_i2c_info); -} - -type_init(versatile_i2c_register_types) diff --git a/qemu/hw/i386/Makefile.objs b/qemu/hw/i386/Makefile.objs deleted file mode 100644 index b52d5b875..000000000 --- a/qemu/hw/i386/Makefile.objs +++ /dev/null @@ -1,10 +0,0 @@ -obj-$(CONFIG_KVM) += kvm/ -obj-y += multiboot.o -obj-y += pc.o pc_piix.o pc_q35.o -obj-y += pc_sysfw.o -obj-y += intel_iommu.o -obj-$(CONFIG_XEN) += ../xenpv/ xen/ - -obj-y += kvmvapic.o -obj-y += acpi-build.o -obj-y += pci-assign-load-rom.o diff --git a/qemu/hw/i386/acpi-build.c b/qemu/hw/i386/acpi-build.c deleted file mode 100644 index 64770034f..000000000 --- a/qemu/hw/i386/acpi-build.c +++ /dev/null @@ -1,2950 +0,0 @@ -/* Support for generating ACPI tables and passing them to Guests - * - * Copyright (C) 2008-2010 Kevin O'Connor - * Copyright (C) 2006 Fabrice Bellard - * Copyright (C) 2013 Red Hat Inc - * - * Author: Michael S. Tsirkin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "acpi-build.h" -#include -#include "qemu-common.h" -#include "qemu/bitmap.h" -#include "qemu/error-report.h" -#include "hw/pci/pci.h" -#include "qom/cpu.h" -#include "hw/i386/pc.h" -#include "target-i386/cpu.h" -#include "hw/timer/hpet.h" -#include "hw/acpi/acpi-defs.h" -#include "hw/acpi/acpi.h" -#include "hw/nvram/fw_cfg.h" -#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" -#include "hw/acpi/pcihp.h" -#include "hw/i386/ich9.h" -#include "hw/pci/pci_bus.h" -#include "hw/pci-host/q35.h" -#include "hw/i386/intel_iommu.h" -#include "hw/timer/hpet.h" - -#include "hw/acpi/aml-build.h" - -#include "qapi/qmp/qint.h" -#include "qom/qom-qobject.h" - -/* These are used to size the ACPI tables for -M pc-i440fx-1.7 and - * -M pc-i440fx-2.0. Even if the actual amount of AML generated grows - * a little bit, there should be plenty of free space since the DSDT - * shrunk by ~1.5k between QEMU 2.0 and QEMU 2.1. - */ -#define ACPI_BUILD_LEGACY_CPU_AML_SIZE 97 -#define ACPI_BUILD_ALIGN_SIZE 0x1000 - -#define ACPI_BUILD_TABLE_SIZE 0x20000 - -/* #define DEBUG_ACPI_BUILD */ -#ifdef DEBUG_ACPI_BUILD -#define ACPI_BUILD_DPRINTF(fmt, ...) \ - do {printf("ACPI_BUILD: " fmt, ## __VA_ARGS__); } while (0) -#else -#define ACPI_BUILD_DPRINTF(fmt, ...) -#endif - -typedef struct AcpiMcfgInfo { - uint64_t mcfg_base; - uint32_t mcfg_size; -} AcpiMcfgInfo; - -typedef struct AcpiPmInfo { - bool s3_disabled; - bool s4_disabled; - bool pcihp_bridge_en; - uint8_t s4_val; - uint16_t sci_int; - uint8_t acpi_enable_cmd; - uint8_t acpi_disable_cmd; - uint32_t gpe0_blk; - uint32_t gpe0_blk_len; - uint32_t io_base; - uint16_t cpu_hp_io_base; - uint16_t cpu_hp_io_len; - uint16_t mem_hp_io_base; - uint16_t mem_hp_io_len; - uint16_t pcihp_io_base; - uint16_t pcihp_io_len; -} AcpiPmInfo; - -typedef struct AcpiMiscInfo { - bool is_piix4; - bool has_hpet; - TPMVersion tpm_version; - const unsigned char *dsdt_code; - unsigned dsdt_size; - uint16_t pvpanic_port; - uint16_t applesmc_io_base; -} AcpiMiscInfo; - -typedef struct AcpiBuildPciBusHotplugState { - GArray *device_table; - GArray *notify_table; - struct AcpiBuildPciBusHotplugState *parent; - bool pcihp_bridge_en; -} AcpiBuildPciBusHotplugState; - -static void acpi_get_pm_info(AcpiPmInfo *pm) -{ - Object *piix = piix4_pm_find(); - Object *lpc = ich9_lpc_find(); - Object *obj = NULL; - QObject *o; - - pm->cpu_hp_io_base = 0; - pm->pcihp_io_base = 0; - pm->pcihp_io_len = 0; - if (piix) { - obj = piix; - pm->cpu_hp_io_base = PIIX4_CPU_HOTPLUG_IO_BASE; - pm->pcihp_io_base = - object_property_get_int(obj, ACPI_PCIHP_IO_BASE_PROP, NULL); - pm->pcihp_io_len = - object_property_get_int(obj, ACPI_PCIHP_IO_LEN_PROP, NULL); - } - if (lpc) { - obj = lpc; - pm->cpu_hp_io_base = ICH9_CPU_HOTPLUG_IO_BASE; - } - assert(obj); - - pm->cpu_hp_io_len = ACPI_GPE_PROC_LEN; - pm->mem_hp_io_base = ACPI_MEMORY_HOTPLUG_BASE; - pm->mem_hp_io_len = ACPI_MEMORY_HOTPLUG_IO_LEN; - - /* Fill in optional s3/s4 related properties */ - o = object_property_get_qobject(obj, ACPI_PM_PROP_S3_DISABLED, NULL); - if (o) { - pm->s3_disabled = qint_get_int(qobject_to_qint(o)); - } else { - pm->s3_disabled = false; - } - qobject_decref(o); - o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_DISABLED, NULL); - if (o) { - pm->s4_disabled = qint_get_int(qobject_to_qint(o)); - } else { - pm->s4_disabled = false; - } - qobject_decref(o); - o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_VAL, NULL); - if (o) { - pm->s4_val = qint_get_int(qobject_to_qint(o)); - } else { - pm->s4_val = false; - } - qobject_decref(o); - - /* Fill in mandatory properties */ - pm->sci_int = object_property_get_int(obj, ACPI_PM_PROP_SCI_INT, NULL); - - pm->acpi_enable_cmd = object_property_get_int(obj, - ACPI_PM_PROP_ACPI_ENABLE_CMD, - NULL); - pm->acpi_disable_cmd = object_property_get_int(obj, - ACPI_PM_PROP_ACPI_DISABLE_CMD, - NULL); - pm->io_base = object_property_get_int(obj, ACPI_PM_PROP_PM_IO_BASE, - NULL); - pm->gpe0_blk = object_property_get_int(obj, ACPI_PM_PROP_GPE0_BLK, - NULL); - pm->gpe0_blk_len = object_property_get_int(obj, ACPI_PM_PROP_GPE0_BLK_LEN, - NULL); - pm->pcihp_bridge_en = - object_property_get_bool(obj, "acpi-pci-hotplug-with-bridge-support", - NULL); -} - -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(); - info->applesmc_io_base = applesmc_port(); -} - -/* - * Because of the PXB hosts we cannot simply query TYPE_PCI_HOST_BRIDGE. - * On i386 arch we only have two pci hosts, so we can look only for them. - */ -static Object *acpi_get_i386_pci_host(void) -{ - PCIHostState *host; - - host = OBJECT_CHECK(PCIHostState, - object_resolve_path("/machine/i440fx", NULL), - TYPE_PCI_HOST_BRIDGE); - if (!host) { - host = OBJECT_CHECK(PCIHostState, - object_resolve_path("/machine/q35", NULL), - TYPE_PCI_HOST_BRIDGE); - } - - return OBJECT(host); -} - -static void acpi_get_pci_info(PcPciInfo *info) -{ - Object *pci_host; - - - pci_host = acpi_get_i386_pci_host(); - g_assert(pci_host); - - info->w32.begin = object_property_get_int(pci_host, - PCI_HOST_PROP_PCI_HOLE_START, - NULL); - info->w32.end = object_property_get_int(pci_host, - PCI_HOST_PROP_PCI_HOLE_END, - NULL); - info->w64.begin = object_property_get_int(pci_host, - PCI_HOST_PROP_PCI_HOLE64_START, - NULL); - info->w64.end = object_property_get_int(pci_host, - PCI_HOST_PROP_PCI_HOLE64_END, - NULL); -} - -#define ACPI_PORT_SMI_CMD 0x00b2 /* TODO: this is APM_CNT_IOPORT */ - -static void acpi_align_size(GArray *blob, unsigned align) -{ - /* Align size to multiple of given size. This reduces the chance - * we need to change size in the future (breaking cross version migration). - */ - g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align)); -} - -/* FACS */ -static void -build_facs(GArray *table_data, GArray *linker) -{ - AcpiFacsDescriptorRev1 *facs = acpi_data_push(table_data, sizeof *facs); - memcpy(&facs->signature, "FACS", 4); - facs->length = cpu_to_le32(sizeof(*facs)); -} - -/* Load chipset information in FADT */ -static void fadt_setup(AcpiFadtDescriptorRev1 *fadt, AcpiPmInfo *pm) -{ - fadt->model = 1; - fadt->reserved1 = 0; - fadt->sci_int = cpu_to_le16(pm->sci_int); - fadt->smi_cmd = cpu_to_le32(ACPI_PORT_SMI_CMD); - fadt->acpi_enable = pm->acpi_enable_cmd; - fadt->acpi_disable = pm->acpi_disable_cmd; - /* EVT, CNT, TMR offset matches hw/acpi/core.c */ - fadt->pm1a_evt_blk = cpu_to_le32(pm->io_base); - fadt->pm1a_cnt_blk = cpu_to_le32(pm->io_base + 0x04); - fadt->pm_tmr_blk = cpu_to_le32(pm->io_base + 0x08); - fadt->gpe0_blk = cpu_to_le32(pm->gpe0_blk); - /* EVT, CNT, TMR length matches hw/acpi/core.c */ - fadt->pm1_evt_len = 4; - fadt->pm1_cnt_len = 2; - fadt->pm_tmr_len = 4; - fadt->gpe0_blk_len = pm->gpe0_blk_len; - fadt->plvl2_lat = cpu_to_le16(0xfff); /* C2 state not supported */ - fadt->plvl3_lat = cpu_to_le16(0xfff); /* C3 state not supported */ - fadt->flags = cpu_to_le32((1 << ACPI_FADT_F_WBINVD) | - (1 << ACPI_FADT_F_PROC_C1) | - (1 << ACPI_FADT_F_SLP_BUTTON) | - (1 << ACPI_FADT_F_RTC_S4)); - fadt->flags |= cpu_to_le32(1 << ACPI_FADT_F_USE_PLATFORM_CLOCK); - /* APIC destination mode ("Flat Logical") has an upper limit of 8 CPUs - * For more than 8 CPUs, "Clustered Logical" mode has to be used - */ - 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, - const char *oem_id, const char *oem_table_id) -{ - AcpiFadtDescriptorRev1 *fadt = acpi_data_push(table_data, sizeof(*fadt)); - - fadt->firmware_ctrl = cpu_to_le32(facs); - /* FACS address to be filled by Guest linker */ - bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, - ACPI_BUILD_TABLE_FILE, - table_data, &fadt->firmware_ctrl, - sizeof fadt->firmware_ctrl); - - fadt->dsdt = cpu_to_le32(dsdt); - /* DSDT address to be filled by Guest linker */ - bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, - ACPI_BUILD_TABLE_FILE, - table_data, &fadt->dsdt, - sizeof fadt->dsdt); - - fadt_setup(fadt, pm); - - build_header(linker, table_data, - (void *)fadt, "FACP", sizeof(*fadt), 1, oem_id, oem_table_id); -} - -static void -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; - AcpiMadtIoApic *io_apic; - AcpiMadtIntsrcovr *intsrcovr; - AcpiMadtLocalNmi *local_nmi; - int i; - - madt = acpi_data_push(table_data, sizeof *madt); - madt->local_apic_address = cpu_to_le32(APIC_DEFAULT_ADDRESS); - madt->flags = cpu_to_le32(1); - - 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 = 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); -#define ACPI_BUILD_IOAPIC_ID 0x0 - io_apic->io_apic_id = ACPI_BUILD_IOAPIC_ID; - io_apic->address = cpu_to_le32(IO_APIC_DEFAULT_ADDRESS); - io_apic->interrupt = cpu_to_le32(0); - - if (pcms->apic_xrupt_override) { - intsrcovr = acpi_data_push(table_data, sizeof *intsrcovr); - intsrcovr->type = ACPI_APIC_XRUPT_OVERRIDE; - intsrcovr->length = sizeof(*intsrcovr); - intsrcovr->source = 0; - intsrcovr->gsi = cpu_to_le32(2); - intsrcovr->flags = cpu_to_le16(0); /* conforms to bus specifications */ - } - for (i = 1; i < 16; i++) { -#define ACPI_BUILD_PCI_IRQS ((1<<5) | (1<<9) | (1<<10) | (1<<11)) - if (!(ACPI_BUILD_PCI_IRQS & (1 << i))) { - /* No need for a INT source override structure. */ - continue; - } - intsrcovr = acpi_data_push(table_data, sizeof *intsrcovr); - intsrcovr->type = ACPI_APIC_XRUPT_OVERRIDE; - intsrcovr->length = sizeof(*intsrcovr); - intsrcovr->source = i; - intsrcovr->gsi = cpu_to_le32(i); - intsrcovr->flags = cpu_to_le16(0xd); /* active high, level triggered */ - } - - local_nmi = acpi_data_push(table_data, sizeof *local_nmi); - local_nmi->type = ACPI_APIC_LOCAL_NMI; - local_nmi->length = sizeof(*local_nmi); - local_nmi->processor_id = 0xff; /* all processors */ - local_nmi->flags = cpu_to_le16(0); - local_nmi->lint = 1; /* ACPI_LINT1 */ - - build_header(linker, table_data, - (void *)(table_data->data + madt_start), "APIC", - table_data->len - madt_start, 1, NULL, NULL); -} - -/* Assign BSEL property to all buses. In the future, this can be changed - * to only assign to buses that support hotplug. - */ -static void *acpi_set_bsel(PCIBus *bus, void *opaque) -{ - unsigned *bsel_alloc = opaque; - unsigned *bus_bsel; - - if (qbus_is_hotpluggable(BUS(bus))) { - bus_bsel = g_malloc(sizeof *bus_bsel); - - *bus_bsel = (*bsel_alloc)++; - object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, - bus_bsel, NULL); - } - - return bsel_alloc; -} - -static void acpi_set_pci_info(void) -{ - PCIBus *bus = find_i440fx(); /* TODO: Q35 support */ - unsigned bsel_alloc = 0; - - if (bus) { - /* Scan all PCI buses. Set property to enable acpi based hotplug. */ - pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &bsel_alloc); - } -} - -static void build_append_pcihp_notify_entry(Aml *method, int slot) -{ - Aml *if_ctx; - int32_t devfn = PCI_DEVFN(slot, 0); - - if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot), NULL)); - aml_append(if_ctx, aml_notify(aml_name("S%.02X", devfn), aml_arg(1))); - aml_append(method, if_ctx); -} - -static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, - bool pcihp_bridge_en) -{ - Aml *dev, *notify_method, *method; - QObject *bsel; - PCIBus *sec; - int i; - - bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL); - if (bsel) { - 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, AML_NOTSERIALIZED); - } - - for (i = 0; i < ARRAY_SIZE(bus->devices); i += PCI_FUNC_MAX) { - DeviceClass *dc; - PCIDeviceClass *pc; - PCIDevice *pdev = bus->devices[i]; - int slot = PCI_SLOT(i); - bool hotplug_enabled_dev; - bool bridge_in_acpi; - - if (!pdev) { - if (bsel) { /* add hotplug slots for non present devices */ - 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, AML_NOTSERIALIZED); - aml_append(method, - aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN")) - ); - aml_append(dev, method); - aml_append(parent_scope, dev); - - build_append_pcihp_notify_entry(notify_method, slot); - } - continue; - } - - pc = PCI_DEVICE_GET_CLASS(pdev); - dc = DEVICE_GET_CLASS(pdev); - - /* When hotplug for bridges is enabled, bridges are - * described in ACPI separately (see build_pci_bus_end). - * In this case they aren't themselves hot-pluggable. - * Hotplugged bridges *are* hot-pluggable. - */ - bridge_in_acpi = pc->is_bridge && pcihp_bridge_en && - !DEVICE(pdev)->hotplugged; - - hotplug_enabled_dev = bsel && dc->hotpluggable && !bridge_in_acpi; - - if (pc->class_id == PCI_CLASS_BRIDGE_ISA) { - continue; - } - - /* start to compose PCI slot descriptor */ - dev = aml_device("S%.02X", PCI_DEVFN(slot, 0)); - aml_append(dev, aml_name_decl("_ADR", aml_int(slot << 16))); - - if (pc->class_id == PCI_CLASS_DISPLAY_VGA) { - /* add VGA specific AML methods */ - int s3d; - - if (object_dynamic_cast(OBJECT(pdev), "qxl-vga")) { - s3d = 3; - } else { - s3d = 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, AML_NOTSERIALIZED); - aml_append(method, aml_return(aml_int(0))); - aml_append(dev, method); - - 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, AML_NOTSERIALIZED); - aml_append(method, - aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN")) - ); - aml_append(dev, method); - - if (bsel) { - build_append_pcihp_notify_entry(notify_method, slot); - } - } else if (bridge_in_acpi) { - /* - * device is coldplugged bridge, - * add child device descriptions into its scope - */ - PCIBus *sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev)); - - build_append_pci_bus_devices(dev, sec_bus, pcihp_bridge_en); - } - /* slot descriptor has been composed, add it into parent context */ - aml_append(parent_scope, dev); - } - - if (bsel) { - aml_append(parent_scope, notify_method); - } - - /* 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, AML_NOTSERIALIZED); - - /* If bus supports hotplug select it and notify about local events */ - if (bsel) { - int64_t bsel_val = qint_get_int(qobject_to_qint(bsel)); - aml_append(method, aml_store(aml_int(bsel_val), aml_name("BNUM"))); - aml_append(method, - aml_call2("DVNT", aml_name("PCIU"), aml_int(1) /* Device Check */) - ); - aml_append(method, - aml_call2("DVNT", aml_name("PCID"), aml_int(3)/* Eject Request */) - ); - } - - /* Notify about child bus events in any case */ - if (pcihp_bridge_en) { - QLIST_FOREACH(sec, &bus->child, sibling) { - int32_t devfn = sec->parent_dev->devfn; - - aml_append(method, aml_name("^S%.02X.PCNT", devfn)); - } - } - aml_append(parent_scope, method); - 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: - * if (lnk_idx == idx) - * route using link 'link_name' - */ -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 = build_prt_entry(link_name); - - aml_append(if_ctx, aml_store(pkg, route)); - - return if_ctx; -} - -/* - * build_prt - Define interrupt rounting rules - * - * Returns an array of 128 routes, one for each device, - * based on device location. - * The main goal is to equaly distribute the interrupts - * over the 4 existing ACPI links (works only for i440fx). - * The hash function is (slot + pin) & 3 -> "LNK[D|A|B|C]". - * - */ -static Aml *build_prt(bool is_pci0_prt) -{ - Aml *method, *while_ctx, *pin, *res; - - method = aml_method("_PRT", 0, AML_NOTSERIALIZED); - res = aml_local(0); - pin = aml_local(1); - aml_append(method, aml_store(aml_package(128), res)); - aml_append(method, aml_store(aml_int(0), pin)); - - /* while (pin < 128) */ - while_ctx = aml_while(aml_lless(pin, aml_int(128))); - { - Aml *slot = aml_local(2); - Aml *lnk_idx = aml_local(3); - Aml *route = aml_local(4); - - /* slot = pin >> 2 */ - aml_append(while_ctx, - 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, 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)); - 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), - NULL), - aml_index(route, aml_int(0)))); - /* route[1] = pin & 3 */ - aml_append(while_ctx, - 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++ */ - aml_append(while_ctx, aml_increment(pin)); - } - aml_append(method, while_ctx); - /* return res*/ - aml_append(method, aml_return(res)); - - return method; -} - -typedef struct CrsRangeEntry { - uint64_t base; - uint64_t limit; -} CrsRangeEntry; - -static void crs_range_insert(GPtrArray *ranges, uint64_t base, uint64_t limit) -{ - CrsRangeEntry *entry; - - entry = g_malloc(sizeof(*entry)); - entry->base = base; - entry->limit = limit; - - g_ptr_array_add(ranges, entry); -} - -static void crs_range_free(gpointer data) -{ - CrsRangeEntry *entry = (CrsRangeEntry *)data; - g_free(entry); -} - -static gint crs_range_compare(gconstpointer a, gconstpointer b) -{ - CrsRangeEntry *entry_a = *(CrsRangeEntry **)a; - CrsRangeEntry *entry_b = *(CrsRangeEntry **)b; - - return (int64_t)entry_a->base - (int64_t)entry_b->base; -} - -/* - * crs_replace_with_free_ranges - given the 'used' ranges within [start - end] - * interval, computes the 'free' ranges from the same interval. - * Example: If the input array is { [a1 - a2],[b1 - b2] }, the function - * will return { [base - a1], [a2 - b1], [b2 - limit] }. - */ -static void crs_replace_with_free_ranges(GPtrArray *ranges, - uint64_t start, uint64_t end) -{ - GPtrArray *free_ranges = g_ptr_array_new_with_free_func(crs_range_free); - uint64_t free_base = start; - int i; - - g_ptr_array_sort(ranges, crs_range_compare); - for (i = 0; i < ranges->len; i++) { - CrsRangeEntry *used = g_ptr_array_index(ranges, i); - - if (free_base < used->base) { - crs_range_insert(free_ranges, free_base, used->base - 1); - } - - free_base = used->limit + 1; - } - - if (free_base < end) { - crs_range_insert(free_ranges, free_base, end); - } - - g_ptr_array_set_size(ranges, 0); - for (i = 0; i < free_ranges->len; i++) { - g_ptr_array_add(ranges, g_ptr_array_index(free_ranges, i)); - } - - 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++) { - uint64_t range_base, range_limit; - PCIDevice *dev = host->bus->devices[devfn]; - - if (!dev) { - continue; - } - - for (i = 0; i < PCI_NUM_REGIONS; i++) { - PCIIORegion *r = &dev->io_regions[i]; - - range_base = r->addr; - range_limit = r->addr + r->size - 1; - - /* - * Work-around for old bioses - * that do not support multiple root buses - */ - if (!range_base || range_base > range_limit) { - continue; - } - - if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { - crs_range_insert(host_io_ranges, range_base, range_limit); - } else { /* "memory" */ - crs_range_insert(host_mem_ranges, range_base, range_limit); - } - } - - type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; - if (type == PCI_HEADER_TYPE_BRIDGE) { - uint8_t subordinate = dev->config[PCI_SUBORDINATE_BUS]; - if (subordinate > max_bus) { - max_bus = subordinate; - } - - range_base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO); - range_limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO); - - /* - * Work-around for old bioses - * that do not support multiple root buses - */ - if (range_base && range_base <= range_limit) { - crs_range_insert(host_io_ranges, range_base, range_limit); - } - - range_base = - pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); - range_limit = - pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); - - /* - * Work-around for old bioses - * that do not support multiple root buses - */ - if (range_base && range_base <= range_limit) { - crs_range_insert(host_mem_ranges, range_base, range_limit); - } - - range_base = - pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - range_limit = - pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - - /* - * Work-around for old bioses - * that do not support multiple root buses - */ - if (range_base && 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, - pci_bus_num(host->bus), - max_bus, - 0, - max_bus - pci_bus_num(host->bus) + 1)); - - 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 DIR */ - 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_dsdt(GArray *table_data, GArray *linker, - AcpiPmInfo *pm, AcpiMiscInfo *misc, - PcPciInfo *pci, MachineState *machine) -{ - 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; - - dsdt = init_aml_allocator(); - - /* Reserve space for header */ - 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); - } - - 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); - uint8_t numa_node = pci_bus_numa_node(bus); - - /* look only for expander root buses */ - if (!pci_bus_is_root(bus)) { - continue; - } - - if (bus_num < root_bus_limit) { - root_bus_limit = bus_num - 1; - } - - scope = aml_scope("\\_SB"); - dev = aml_device("PC%.02X", bus_num); - aml_append(dev, aml_name_decl("_UID", aml_int(bus_num))); - aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A03"))); - aml_append(dev, aml_name_decl("_BBN", aml_int(bus_num))); - - if (numa_node != NUMA_NODE_UNASSIGNED) { - aml_append(dev, aml_name_decl("_PXM", aml_int(numa_node))); - } - - 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(dsdt, scope); - } - } - - scope = aml_scope("\\_SB.PCI0"); - /* build PCI0._CRS */ - crs = aml_resource_template(); - aml_append(crs, - aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, - 0x0000, 0x0, root_bus_limit, - 0x0000, root_bus_limit + 1)); - aml_append(crs, aml_io(AML_DECODE16, 0x0CF8, 0x0CF8, 0x01, 0x08)); - - aml_append(crs, - aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, - AML_POS_DECODE, AML_ENTIRE_RANGE, - 0x0000, 0x0000, 0x0CF7, 0x0000, 0x0CF8)); - - crs_replace_with_free_ranges(io_ranges, 0x0D00, 0xFFFF); - for (i = 0; i < io_ranges->len; i++) { - entry = g_ptr_array_index(io_ranges, i); - aml_append(crs, - aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, - AML_POS_DECODE, AML_ENTIRE_RANGE, - 0x0000, entry->base, entry->limit, - 0x0000, entry->limit - entry->base + 1)); - } - - aml_append(crs, - aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, - AML_CACHEABLE, AML_READ_WRITE, - 0, 0x000A0000, 0x000BFFFF, 0, 0x00020000)); - - crs_replace_with_free_ranges(mem_ranges, pci->w32.begin, pci->w32.end - 1); - for (i = 0; i < mem_ranges->len; i++) { - entry = g_ptr_array_index(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)); - } - - if (pci->w64.begin) { - aml_append(crs, - aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, - AML_CACHEABLE, AML_READ_WRITE, - 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 */ - dev = aml_device("GPE0"); - aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A06"))); - aml_append(dev, aml_name_decl("_UID", aml_string("GPE0 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->gpe0_blk, pm->gpe0_blk, 1, pm->gpe0_blk_len) - ); - aml_append(dev, aml_name_decl("_CRS", crs)); - aml_append(scope, dev); - - g_ptr_array_free(io_ranges, true); - g_ptr_array_free(mem_ranges, true); - - /* reserve PCIHP resources */ - if (pm->pcihp_io_len) { - dev = aml_device("PHPR"); - aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A06"))); - aml_append(dev, - aml_name_decl("_UID", aml_string("PCI Hotplug resources"))); - /* device present, functioning, decoding, not shown in UI */ - aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); - crs = aml_resource_template(); - aml_append(crs, - aml_io(AML_DECODE16, pm->pcihp_io_base, pm->pcihp_io_base, 1, - pm->pcihp_io_len) - ); - aml_append(dev, aml_name_decl("_CRS", crs)); - aml_append(scope, dev); - } - aml_append(dsdt, scope); - - /* create S3_ / S4_ / S5_ packages if necessary */ - scope = aml_scope("\\"); - if (!pm->s3_disabled) { - pkg = aml_package(4); - aml_append(pkg, aml_int(1)); /* PM1a_CNT.SLP_TYP */ - aml_append(pkg, aml_int(1)); /* PM1b_CNT.SLP_TYP, FIXME: not impl. */ - aml_append(pkg, aml_int(0)); /* reserved */ - aml_append(pkg, aml_int(0)); /* reserved */ - aml_append(scope, aml_name_decl("_S3", pkg)); - } - - if (!pm->s4_disabled) { - pkg = aml_package(4); - aml_append(pkg, aml_int(pm->s4_val)); /* PM1a_CNT.SLP_TYP */ - /* PM1b_CNT.SLP_TYP, FIXME: not impl. */ - aml_append(pkg, aml_int(pm->s4_val)); - aml_append(pkg, aml_int(0)); /* reserved */ - aml_append(pkg, aml_int(0)); /* reserved */ - aml_append(scope, aml_name_decl("_S4", pkg)); - } - - pkg = aml_package(4); - aml_append(pkg, aml_int(0)); /* PM1a_CNT.SLP_TYP */ - aml_append(pkg, aml_int(0)); /* PM1b_CNT.SLP_TYP not impl. */ - aml_append(pkg, aml_int(0)); /* reserved */ - aml_append(pkg, aml_int(0)); /* reserved */ - aml_append(scope, aml_name_decl("_S5", pkg)); - 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"); - dev = aml_device("SMC"); - - aml_append(dev, aml_name_decl("_HID", aml_eisaid("APP0001"))); - /* 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, misc->applesmc_io_base, misc->applesmc_io_base, - 0x01, APPLESMC_MAX_DATA_LENGTH) - ); - aml_append(crs, aml_irq_no_flags(6)); - aml_append(dev, aml_name_decl("_CRS", crs)); - - aml_append(scope, dev); - aml_append(dsdt, scope); - } - - if (misc->pvpanic_port) { - scope = aml_scope("\\_SB.PCI0.ISA"); - - dev = aml_device("PEVT"); - aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0001"))); - - crs = aml_resource_template(); - aml_append(crs, - aml_io(AML_DECODE16, misc->pvpanic_port, misc->pvpanic_port, 1, 1) - ); - aml_append(dev, aml_name_decl("_CRS", crs)); - - aml_append(dev, aml_operation_region("PEOR", AML_SYSTEM_IO, - 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, 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, AML_NOTSERIALIZED); - aml_append(method, aml_store(aml_arg(0), aml_name("PEPT"))); - aml_append(dev, method); - - aml_append(scope, dev); - aml_append(dsdt, scope); - } - - sb_scope = aml_scope("\\_SB"); - { - build_processor_devices(sb_scope, machine, pm); - - build_memory_devices(sb_scope, nr_mem, pm->mem_hp_io_base, - pm->mem_hp_io_len); - - { - Object *pci_host; - PCIBus *bus = NULL; - - pci_host = acpi_get_i386_pci_host(); - if (pci_host) { - bus = PCI_HOST_BRIDGE(pci_host)->bus; - } - - if (bus) { - Aml *scope = aml_scope("PCI0"); - /* Scan all PCI buses. Generate tables to support hotplug. */ - build_append_pci_bus_devices(scope, bus, pm->pcihp_bridge_en); - - if (misc->tpm_version != TPM_VERSION_UNSPEC) { - dev = aml_device("ISA.TPM"); - aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C31"))); - aml_append(dev, aml_name_decl("_STA", aml_int(0xF))); - crs = aml_resource_template(); - aml_append(crs, aml_memory32_fixed(TPM_TIS_ADDR_BASE, - TPM_TIS_ADDR_SIZE, AML_READ_WRITE)); - /* - 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); - } - - aml_append(sb_scope, scope); - } - } - aml_append(dsdt, sb_scope); - } - - /* copy AML table into ACPI tables blob and patch header there */ - 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, 1, NULL, NULL); - free_aml_allocator(); -} - -static void -build_hpet(GArray *table_data, GArray *linker) -{ - Acpi20Hpet *hpet; - - hpet = acpi_data_push(table_data, sizeof(*hpet)); - /* Note timer_block_id value must be kept in sync with value advertised by - * emulated hpet - */ - 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, NULL, NULL); -} - -static void -build_tpm_tcpa(GArray *table_data, GArray *linker, GArray *tcpalog) -{ - Acpi20Tcpa *tcpa = acpi_data_push(table_data, sizeof *tcpa); - uint64_t log_area_start_address = acpi_data_len(tcpalog); - - tcpa->platform_class = cpu_to_le16(TPM_TCPA_ACPI_CLASS_CLIENT); - tcpa->log_area_minimum_length = cpu_to_le32(TPM_LOG_AREA_MINIMUM_SIZE); - tcpa->log_area_start_address = cpu_to_le64(log_area_start_address); - - bios_linker_loader_alloc(linker, ACPI_BUILD_TPMLOG_FILE, 1, - false /* high memory */); - - /* log area start address to be filled by Guest linker */ - bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, - ACPI_BUILD_TPMLOG_FILE, - table_data, &tcpa->log_area_start_address, - sizeof(tcpa->log_area_start_address)); - - build_header(linker, table_data, - (void *)tcpa, "TCPA", sizeof(*tcpa), 2, NULL, NULL); - - acpi_data_push(tcpalog, TPM_LOG_AREA_MINIMUM_SIZE); -} - -static void -build_tpm2(GArray *table_data, GArray *linker) -{ - Acpi20TPM2 *tpm2_ptr; - - tpm2_ptr = acpi_data_push(table_data, sizeof *tpm2_ptr); - - tpm2_ptr->platform_class = cpu_to_le16(TPM2_ACPI_CLASS_CLIENT); - tpm2_ptr->control_area_address = cpu_to_le64(0); - tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO); - - build_header(linker, table_data, - (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL); -} - -typedef enum { - MEM_AFFINITY_NOFLAGS = 0, - MEM_AFFINITY_ENABLED = (1 << 0), - MEM_AFFINITY_HOTPLUGGABLE = (1 << 1), - MEM_AFFINITY_NON_VOLATILE = (1 << 2), -} MemoryAffinityFlags; - -static void -acpi_build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base, - uint64_t len, int node, MemoryAffinityFlags flags) -{ - numamem->type = ACPI_SRAT_MEMORY; - numamem->length = sizeof(*numamem); - memset(numamem->proximity, 0, 4); - numamem->proximity[0] = node; - numamem->flags = cpu_to_le32(flags); - numamem->base_addr = cpu_to_le64(base); - numamem->range_length = cpu_to_le64(len); -} - -static void -build_srat(GArray *table_data, GArray *linker, MachineState *machine) -{ - AcpiSystemResourceAffinityTable *srat; - AcpiSratProcessorAffinity *core; - AcpiSratMemoryAffinity *numamem; - - int i; - uint64_t curnode; - int srat_start, numa_start, slots; - uint64_t mem_len, mem_base, next_base; - 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); - - srat_start = table_data->len; - - srat = acpi_data_push(table_data, sizeof *srat); - srat->reserved1 = cpu_to_le32(1); - - 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 = apic_id; - curnode = pcms->node_cpu[apic_id]; - core->proximity_lo = curnode; - memset(core->proximity_hi, 0, 3); - core->local_sapic_eid = 0; - core->flags = cpu_to_le32(1); - } - - - /* the memory map is a bit tricky, it contains at least one hole - * from 640k-1M and possibly another one from 3.5G-4G. - */ - next_base = 0; - numa_start = table_data->len; - - 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 < pcms->numa_nodes + 1; ++i) { - mem_base = next_base; - 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 <= 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 - 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 < pcms->numa_nodes + 2; slots++) { - numamem = acpi_data_push(table_data, sizeof *numamem); - acpi_build_srat_memory(numamem, 0, 0, 0, MEM_AFFINITY_NOFLAGS); - } - - /* - * Entry is required for Windows to enable memory hotplug in OS. - * Memory devices may override proximity set by this entry, - * providing _PXM method if necessary. - */ - if (hotplugabble_address_space_size) { - numamem = acpi_data_push(table_data, sizeof *numamem); - acpi_build_srat_memory(numamem, pcms->hotplug_memory.base, - hotplugabble_address_space_size, 0, - MEM_AFFINITY_HOTPLUGGABLE | - MEM_AFFINITY_ENABLED); - } - - build_header(linker, table_data, - (void *)(table_data->data + srat_start), - "SRAT", - table_data->len - srat_start, 1, NULL, NULL); - g_free(apic_ids); -} - -static void -build_mcfg_q35(GArray *table_data, GArray *linker, AcpiMcfgInfo *info) -{ - AcpiTableMcfg *mcfg; - const char *sig; - int len = sizeof(*mcfg) + 1 * sizeof(mcfg->allocation[0]); - - mcfg = acpi_data_push(table_data, len); - mcfg->allocation[0].address = cpu_to_le64(info->mcfg_base); - /* Only a single allocation so no need to play with segments */ - mcfg->allocation[0].pci_segment = cpu_to_le16(0); - mcfg->allocation[0].start_bus_number = 0; - mcfg->allocation[0].end_bus_number = PCIE_MMCFG_BUS(info->mcfg_size - 1); - - /* MCFG is used for ECAM which can be enabled or disabled by guest. - * To avoid table size changes (which create migration issues), - * always create the table even if there are no allocations, - * but set the signature to a reserved value in this case. - * ACPI spec requires OSPMs to ignore such tables. - */ - if (info->mcfg_base == PCIE_BASE_ADDR_UNMAPPED) { - /* Reserved signature: ignored by OSPM */ - sig = "QEMU"; - } else { - sig = "MCFG"; - } - build_header(linker, table_data, (void *)mcfg, sig, len, 1, NULL, NULL); -} - -static void -build_dmar_q35(GArray *table_data, GArray *linker) -{ - int dmar_start = table_data->len; - - AcpiTableDmar *dmar; - AcpiDmarHardwareUnit *drhd; - - dmar = acpi_data_push(table_data, sizeof(*dmar)); - dmar->host_address_width = VTD_HOST_ADDRESS_WIDTH - 1; - dmar->flags = 0; /* No intr_remap for now */ - - /* DMAR Remapping Hardware Unit Definition structure */ - drhd = acpi_data_push(table_data, sizeof(*drhd)); - drhd->type = cpu_to_le16(ACPI_DMAR_TYPE_HARDWARE_UNIT); - drhd->length = cpu_to_le16(sizeof(*drhd)); /* No device scope now */ - drhd->flags = ACPI_DMAR_INCLUDE_PCI_ALL; - drhd->pci_segment = cpu_to_le16(0); - 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, NULL, NULL); -} - -static GArray * -build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt) -{ - AcpiRsdpDescriptor *rsdp = acpi_data_push(rsdp_table, sizeof *rsdp); - - bios_linker_loader_alloc(linker, ACPI_BUILD_RSDP_FILE, 16, - true /* fseg memory */); - - memcpy(&rsdp->signature, "RSD PTR ", 8); - memcpy(rsdp->oem_id, ACPI_BUILD_APPNAME6, 6); - rsdp->rsdt_physical_address = cpu_to_le32(rsdt); - /* Address to be filled by Guest linker */ - bios_linker_loader_add_pointer(linker, ACPI_BUILD_RSDP_FILE, - ACPI_BUILD_TABLE_FILE, - rsdp_table, &rsdp->rsdt_physical_address, - sizeof rsdp->rsdt_physical_address); - rsdp->checksum = 0; - /* Checksum to be filled by Guest linker */ - bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE, - rsdp_table, rsdp, sizeof *rsdp, - &rsdp->checksum); - - return rsdp_table; -} - -typedef -struct AcpiBuildState { - /* Copy of table in RAM (for patching). */ - MemoryRegion *table_mr; - /* Is table patched? */ - uint8_t patched; - void *rsdp; - MemoryRegion *rsdp_mr; - MemoryRegion *linker_mr; -} AcpiBuildState; - -static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg) -{ - Object *pci_host; - QObject *o; - - pci_host = acpi_get_i386_pci_host(); - g_assert(pci_host); - - o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_BASE, NULL); - if (!o) { - return false; - } - mcfg->mcfg_base = qint_get_int(qobject_to_qint(o)); - qobject_decref(o); - - o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_SIZE, NULL); - assert(o); - mcfg->mcfg_size = qint_get_int(qobject_to_qint(o)); - qobject_decref(o); - return true; -} - -static bool acpi_has_iommu(void) -{ - bool ambiguous; - Object *intel_iommu; - - intel_iommu = object_resolve_path_type("", TYPE_INTEL_IOMMU_DEVICE, - &ambiguous); - return intel_iommu && !ambiguous; -} - -static -void acpi_build(AcpiBuildTables *tables, MachineState *machine) -{ - PCMachineState *pcms = PC_MACHINE(machine); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); - GArray *table_offsets; - unsigned facs, dsdt, rsdt, fadt; - AcpiPmInfo pm; - AcpiMiscInfo misc; - AcpiMcfgInfo mcfg; - PcPciInfo pci; - uint8_t *u; - size_t aml_len = 0; - GArray *tables_blob = tables->table_data; - AcpiSlicOem slic_oem = { .id = NULL, .table_id = NULL }; - - acpi_get_pm_info(&pm); - 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)); - ACPI_BUILD_DPRINTF("init ACPI tables\n"); - - bios_linker_loader_alloc(tables->linker, ACPI_BUILD_TABLE_FILE, - 64 /* Ensure FACS is aligned */, - false /* high memory */); - - /* - * FACS is pointed to by FADT. - * We place it first since it's the only table that has alignment - * requirements. - */ - facs = tables_blob->len; - build_facs(tables_blob, tables->linker); - - /* DSDT is pointed to by FADT */ - dsdt = tables_blob->len; - 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. - */ - 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, - 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, pcms); - - if (misc.has_hpet) { - acpi_add_table(table_offsets, tables_blob); - build_hpet(tables_blob, tables->linker); - } - if (misc.tpm_version != TPM_VERSION_UNSPEC) { - acpi_add_table(table_offsets, tables_blob); - build_tpm_tcpa(tables_blob, tables->linker, tables->tcpalog); - - if (misc.tpm_version == TPM_VERSION_2_0) { - acpi_add_table(table_offsets, tables_blob); - build_tpm2(tables_blob, tables->linker); - } - } - if (pcms->numa_nodes) { - acpi_add_table(table_offsets, tables_blob); - build_srat(tables_blob, tables->linker, machine); - } - if (acpi_get_mcfg(&mcfg)) { - acpi_add_table(table_offsets, tables_blob); - build_mcfg_q35(tables_blob, tables->linker, &mcfg); - } - if (acpi_has_iommu()) { - 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)) { - unsigned len = acpi_table_len(u); - - acpi_add_table(table_offsets, tables_blob); - g_array_append_vals(tables_blob, u, len); - } - - /* RSDT is pointed to by RSDP */ - rsdt = tables_blob->len; - 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); - - /* We'll expose it all to Guest so we want to reduce - * chance of size changes. - * - * We used to align the tables to 4k, but of course this would - * too simple to be enough. 4k turned out to be too small an - * alignment very soon, and in fact it is almost impossible to - * keep the table size stable for all (max_cpus, max_memory_slots) - * combinations. So the table size is always 64k for pc-i440fx-2.1 - * and we give an error if the table grows beyond that limit. - * - * We still have the problem of migrating from "-M pc-i440fx-2.0". For - * that, we exploit the fact that QEMU 2.1 generates _smaller_ tables - * than 2.0 and we can always pad the smaller tables with zeros. We can - * then use the exact size of the 2.0 tables. - * - * All this is for PIIX4, since QEMU 2.0 didn't support Q35 migration. - */ - 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 = - 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, - ACPI_BUILD_ALIGN_SIZE); - if (tables_blob->len > legacy_table_size) { - /* Should happen only with PCI bridges and -M pc-i440fx-2.0. */ - error_report("Warning: migration may not work."); - } - g_array_set_size(tables_blob, legacy_table_size); - } else { - /* Make sure we have a buffer in case we need to resize the tables. */ - if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) { - /* As of QEMU 2.1, this fires with 160 VCPUs and 255 memory slots. */ - error_report("Warning: ACPI tables are larger than 64k."); - error_report("Warning: migration may not work."); - error_report("Warning: please remove CPUs, NUMA nodes, " - "memory slots or PCI bridges."); - } - acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE); - } - - acpi_align_size(tables->linker, ACPI_BUILD_ALIGN_SIZE); - - /* Cleanup memory that's no longer used. */ - g_array_free(table_offsets, true); -} - -static void acpi_ram_update(MemoryRegion *mr, GArray *data) -{ - uint32_t size = acpi_data_len(data); - - /* Make sure RAM size is correct - in case it got changed e.g. by migration */ - memory_region_ram_resize(mr, size, &error_abort); - - memcpy(memory_region_get_ram_ptr(mr), data->data, size); - memory_region_set_dirty(mr, 0, size); -} - -static void acpi_build_update(void *build_opaque) -{ - AcpiBuildState *build_state = build_opaque; - AcpiBuildTables tables; - - /* No state to update or already patched? Nothing to do. */ - if (!build_state || build_state->patched) { - return; - } - build_state->patched = 1; - - acpi_build_tables_init(&tables); - - acpi_build(&tables, MACHINE(qdev_get_machine())); - - acpi_ram_update(build_state->table_mr, tables.table_data); - - if (build_state->rsdp) { - memcpy(build_state->rsdp, tables.rsdp->data, acpi_data_len(tables.rsdp)); - } else { - acpi_ram_update(build_state->rsdp_mr, tables.rsdp); - } - - acpi_ram_update(build_state->linker_mr, tables.linker); - acpi_build_tables_cleanup(&tables, true); -} - -static void acpi_build_reset(void *build_opaque) -{ - AcpiBuildState *build_state = build_opaque; - build_state->patched = 0; -} - -static MemoryRegion *acpi_add_rom_blob(AcpiBuildState *build_state, - GArray *blob, const char *name, - uint64_t max_size) -{ - return rom_add_blob(name, blob->data, acpi_data_len(blob), max_size, -1, - name, acpi_build_update, build_state); -} - -static const VMStateDescription vmstate_acpi_build = { - .name = "acpi_build", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(patched, AcpiBuildState), - VMSTATE_END_OF_LIST() - }, -}; - -void acpi_setup(void) -{ - PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); - AcpiBuildTables tables; - AcpiBuildState *build_state; - - if (!pcms->fw_cfg) { - ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n"); - return; - } - - if (!pcmc->has_acpi_build) { - ACPI_BUILD_DPRINTF("ACPI build disabled. Bailing out.\n"); - return; - } - - if (!acpi_enabled) { - ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n"); - return; - } - - build_state = g_malloc0(sizeof *build_state); - - acpi_set_pci_info(); - - acpi_build_tables_init(&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, - ACPI_BUILD_TABLE_FILE, - ACPI_BUILD_TABLE_MAX_SIZE); - assert(build_state->table_mr != NULL); - - build_state->linker_mr = - acpi_add_rom_blob(build_state, tables.linker, "etc/table-loader", 0); - - fw_cfg_add_file(pcms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, - tables.tcpalog->data, acpi_data_len(tables.tcpalog)); - - if (!pcmc->rsdp_in_ram) { - /* - * Keep for compatibility with old machine types. - * Though RSDP is small, its contents isn't immutable, so - * we'll update it along with the rest of tables on guest access. - */ - uint32_t rsdp_size = acpi_data_len(tables.rsdp); - - build_state->rsdp = g_memdup(tables.rsdp->data, rsdp_size); - 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; - } else { - build_state->rsdp = NULL; - build_state->rsdp_mr = acpi_add_rom_blob(build_state, tables.rsdp, - ACPI_BUILD_RSDP_FILE, 0); - } - - qemu_register_reset(acpi_build_reset, build_state); - acpi_build_reset(build_state); - vmstate_register(NULL, 0, &vmstate_acpi_build, build_state); - - /* Cleanup tables but don't free the memory: we track it - * in build_state. - */ - acpi_build_tables_cleanup(&tables, false); -} diff --git a/qemu/hw/i386/acpi-build.h b/qemu/hw/i386/acpi-build.h deleted file mode 100644 index 007332e51..000000000 --- a/qemu/hw/i386/acpi-build.h +++ /dev/null @@ -1,7 +0,0 @@ - -#ifndef HW_I386_ACPI_BUILD_H -#define HW_I386_ACPI_BUILD_H - -void acpi_setup(void); - -#endif diff --git a/qemu/hw/i386/intel_iommu.c b/qemu/hw/i386/intel_iommu.c deleted file mode 100644 index 347718f93..000000000 --- a/qemu/hw/i386/intel_iommu.c +++ /dev/null @@ -1,2057 +0,0 @@ -/* - * QEMU emulation of an Intel IOMMU (VT-d) - * (DMA Remapping device) - * - * Copyright (C) 2013 Knut Omang, Oracle - * Copyright (C) 2014 Le Tan, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "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 -enum { - DEBUG_GENERAL, DEBUG_CSR, DEBUG_INV, DEBUG_MMU, DEBUG_FLOG, - DEBUG_CACHE, -}; -#define VTD_DBGBIT(x) (1 << DEBUG_##x) -static int vtd_dbgflags = VTD_DBGBIT(GENERAL) | VTD_DBGBIT(CSR); - -#define VTD_DPRINTF(what, fmt, ...) do { \ - if (vtd_dbgflags & VTD_DBGBIT(what)) { \ - fprintf(stderr, "(vtd)%s: " fmt "\n", __func__, \ - ## __VA_ARGS__); } \ - } while (0) -#else -#define VTD_DPRINTF(what, fmt, ...) do {} while (0) -#endif - -static void vtd_define_quad(IntelIOMMUState *s, hwaddr addr, uint64_t val, - uint64_t wmask, uint64_t w1cmask) -{ - stq_le_p(&s->csr[addr], val); - stq_le_p(&s->wmask[addr], wmask); - stq_le_p(&s->w1cmask[addr], w1cmask); -} - -static void vtd_define_quad_wo(IntelIOMMUState *s, hwaddr addr, uint64_t mask) -{ - stq_le_p(&s->womask[addr], mask); -} - -static void vtd_define_long(IntelIOMMUState *s, hwaddr addr, uint32_t val, - uint32_t wmask, uint32_t w1cmask) -{ - stl_le_p(&s->csr[addr], val); - stl_le_p(&s->wmask[addr], wmask); - stl_le_p(&s->w1cmask[addr], w1cmask); -} - -static void vtd_define_long_wo(IntelIOMMUState *s, hwaddr addr, uint32_t mask) -{ - stl_le_p(&s->womask[addr], mask); -} - -/* "External" get/set operations */ -static void vtd_set_quad(IntelIOMMUState *s, hwaddr addr, uint64_t val) -{ - uint64_t oldval = ldq_le_p(&s->csr[addr]); - uint64_t wmask = ldq_le_p(&s->wmask[addr]); - uint64_t w1cmask = ldq_le_p(&s->w1cmask[addr]); - stq_le_p(&s->csr[addr], - ((oldval & ~wmask) | (val & wmask)) & ~(w1cmask & val)); -} - -static void vtd_set_long(IntelIOMMUState *s, hwaddr addr, uint32_t val) -{ - uint32_t oldval = ldl_le_p(&s->csr[addr]); - uint32_t wmask = ldl_le_p(&s->wmask[addr]); - uint32_t w1cmask = ldl_le_p(&s->w1cmask[addr]); - stl_le_p(&s->csr[addr], - ((oldval & ~wmask) | (val & wmask)) & ~(w1cmask & val)); -} - -static uint64_t vtd_get_quad(IntelIOMMUState *s, hwaddr addr) -{ - uint64_t val = ldq_le_p(&s->csr[addr]); - uint64_t womask = ldq_le_p(&s->womask[addr]); - return val & ~womask; -} - -static uint32_t vtd_get_long(IntelIOMMUState *s, hwaddr addr) -{ - uint32_t val = ldl_le_p(&s->csr[addr]); - uint32_t womask = ldl_le_p(&s->womask[addr]); - return val & ~womask; -} - -/* "Internal" get/set operations */ -static uint64_t vtd_get_quad_raw(IntelIOMMUState *s, hwaddr addr) -{ - return ldq_le_p(&s->csr[addr]); -} - -static uint32_t vtd_get_long_raw(IntelIOMMUState *s, hwaddr addr) -{ - return ldl_le_p(&s->csr[addr]); -} - -static void vtd_set_quad_raw(IntelIOMMUState *s, hwaddr addr, uint64_t val) -{ - stq_le_p(&s->csr[addr], val); -} - -static uint32_t vtd_set_clear_mask_long(IntelIOMMUState *s, hwaddr addr, - uint32_t clear, uint32_t mask) -{ - uint32_t new_val = (ldl_le_p(&s->csr[addr]) & ~clear) | mask; - stl_le_p(&s->csr[addr], new_val); - return new_val; -} - -static uint64_t vtd_set_clear_mask_quad(IntelIOMMUState *s, hwaddr addr, - uint64_t clear, uint64_t mask) -{ - uint64_t new_val = (ldq_le_p(&s->csr[addr]) & ~clear) | mask; - stq_le_p(&s->csr[addr], new_val); - return new_val; -} - -/* GHashTable functions */ -static gboolean vtd_uint64_equal(gconstpointer v1, gconstpointer v2) -{ - return *((const uint64_t *)v1) == *((const uint64_t *)v2); -} - -static guint vtd_uint64_hash(gconstpointer v) -{ - return (guint)*(const uint64_t *)v; -} - -static gboolean vtd_hash_remove_by_domain(gpointer key, gpointer value, - gpointer user_data) -{ - VTDIOTLBEntry *entry = (VTDIOTLBEntry *)value; - uint16_t domain_id = *(uint16_t *)user_data; - 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->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 == gfn_tlb)); -} - -/* Reset all the gen of VTDAddressSpace to zero and set the gen of - * IntelIOMMUState to 1. - */ -static void vtd_reset_context_cache(IntelIOMMUState *s) -{ - VTDAddressSpace *vtd_as; - 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"); - 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 = vtd_bus->dev_as[devfn_it]; - if (!vtd_as) { - continue; - } - vtd_as->context_cache_entry.context_cache_gen = 0; - } - } - s->context_cache_gen = 1; -} - -static void vtd_reset_iotlb(IntelIOMMUState *s) -{ - assert(s->iotlb); - 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; - } - } - -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, - uint32_t level) -{ - VTDIOTLBEntry *entry = g_malloc(sizeof(*entry)); - uint64_t *key = g_malloc(sizeof(*key)); - 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, - domain_id); - if (g_hash_table_size(s->iotlb) >= VTD_IOTLB_MAX_SIZE) { - VTD_DPRINTF(CACHE, "iotlb exceeds size limit, forced to reset"); - vtd_reset_iotlb(s); - } - - entry->gfn = gfn; - entry->domain_id = domain_id; - entry->slpte = slpte; - entry->read_flags = read_flags; - entry->write_flags = write_flags; - 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); -} - -/* Given the reg addr of both the message data and address, generate an - * interrupt via MSI. - */ -static void vtd_generate_interrupt(IntelIOMMUState *s, hwaddr mesg_addr_reg, - hwaddr mesg_data_reg) -{ - hwaddr addr; - uint32_t data; - - assert(mesg_data_reg < DMAR_REG_SIZE); - assert(mesg_addr_reg < DMAR_REG_SIZE); - - addr = vtd_get_long_raw(s, mesg_addr_reg); - data = vtd_get_long_raw(s, mesg_data_reg); - - VTD_DPRINTF(FLOG, "msi: addr 0x%"PRIx64 " data 0x%"PRIx32, addr, data); - address_space_stl_le(&address_space_memory, addr, data, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -/* Generate a fault event to software via MSI if conditions are met. - * Notice that the value of FSTS_REG being passed to it should be the one - * before any update. - */ -static void vtd_generate_fault_event(IntelIOMMUState *s, uint32_t pre_fsts) -{ - if (pre_fsts & VTD_FSTS_PPF || pre_fsts & VTD_FSTS_PFO || - pre_fsts & VTD_FSTS_IQE) { - VTD_DPRINTF(FLOG, "there are previous interrupt conditions " - "to be serviced by software, fault event is not generated " - "(FSTS_REG 0x%"PRIx32 ")", pre_fsts); - return; - } - vtd_set_clear_mask_long(s, DMAR_FECTL_REG, 0, VTD_FECTL_IP); - if (vtd_get_long_raw(s, DMAR_FECTL_REG) & VTD_FECTL_IM) { - VTD_DPRINTF(FLOG, "Interrupt Mask set, fault event is not generated"); - } else { - vtd_generate_interrupt(s, DMAR_FEADDR_REG, DMAR_FEDATA_REG); - vtd_set_clear_mask_long(s, DMAR_FECTL_REG, VTD_FECTL_IP, 0); - } -} - -/* Check if the Fault (F) field of the Fault Recording Register referenced by - * @index is Set. - */ -static bool vtd_is_frcd_set(IntelIOMMUState *s, uint16_t index) -{ - /* Each reg is 128-bit */ - hwaddr addr = DMAR_FRCD_REG_OFFSET + (((uint64_t)index) << 4); - addr += 8; /* Access the high 64-bit half */ - - assert(index < DMAR_FRCD_REG_NR); - - return vtd_get_quad_raw(s, addr) & VTD_FRCD_F; -} - -/* Update the PPF field of Fault Status Register. - * Should be called whenever change the F field of any fault recording - * registers. - */ -static void vtd_update_fsts_ppf(IntelIOMMUState *s) -{ - uint32_t i; - uint32_t ppf_mask = 0; - - for (i = 0; i < DMAR_FRCD_REG_NR; i++) { - if (vtd_is_frcd_set(s, i)) { - ppf_mask = VTD_FSTS_PPF; - break; - } - } - vtd_set_clear_mask_long(s, DMAR_FSTS_REG, VTD_FSTS_PPF, ppf_mask); - VTD_DPRINTF(FLOG, "set PPF of FSTS_REG to %d", ppf_mask ? 1 : 0); -} - -static void vtd_set_frcd_and_update_ppf(IntelIOMMUState *s, uint16_t index) -{ - /* Each reg is 128-bit */ - hwaddr addr = DMAR_FRCD_REG_OFFSET + (((uint64_t)index) << 4); - addr += 8; /* Access the high 64-bit half */ - - assert(index < DMAR_FRCD_REG_NR); - - vtd_set_clear_mask_quad(s, addr, 0, VTD_FRCD_F); - vtd_update_fsts_ppf(s); -} - -/* Must not update F field now, should be done later */ -static void vtd_record_frcd(IntelIOMMUState *s, uint16_t index, - uint16_t source_id, hwaddr addr, - VTDFaultReason fault, bool is_write) -{ - uint64_t hi = 0, lo; - hwaddr frcd_reg_addr = DMAR_FRCD_REG_OFFSET + (((uint64_t)index) << 4); - - assert(index < DMAR_FRCD_REG_NR); - - lo = VTD_FRCD_FI(addr); - hi = VTD_FRCD_SID(source_id) | VTD_FRCD_FR(fault); - if (!is_write) { - hi |= VTD_FRCD_T; - } - vtd_set_quad_raw(s, frcd_reg_addr, lo); - vtd_set_quad_raw(s, frcd_reg_addr + 8, hi); - VTD_DPRINTF(FLOG, "record to FRCD_REG #%"PRIu16 ": hi 0x%"PRIx64 - ", lo 0x%"PRIx64, index, hi, lo); -} - -/* Try to collapse multiple pending faults from the same requester */ -static bool vtd_try_collapse_fault(IntelIOMMUState *s, uint16_t source_id) -{ - uint32_t i; - uint64_t frcd_reg; - hwaddr addr = DMAR_FRCD_REG_OFFSET + 8; /* The high 64-bit half */ - - for (i = 0; i < DMAR_FRCD_REG_NR; i++) { - frcd_reg = vtd_get_quad_raw(s, addr); - VTD_DPRINTF(FLOG, "frcd_reg #%d 0x%"PRIx64, i, frcd_reg); - if ((frcd_reg & VTD_FRCD_F) && - ((frcd_reg & VTD_FRCD_SID_MASK) == source_id)) { - return true; - } - addr += 16; /* 128-bit for each */ - } - return false; -} - -/* Log and report an DMAR (address translation) fault to software */ -static void vtd_report_dmar_fault(IntelIOMMUState *s, uint16_t source_id, - hwaddr addr, VTDFaultReason fault, - bool is_write) -{ - uint32_t fsts_reg = vtd_get_long_raw(s, DMAR_FSTS_REG); - - assert(fault < VTD_FR_MAX); - - if (fault == VTD_FR_RESERVED_ERR) { - /* This is not a normal fault reason case. Drop it. */ - return; - } - VTD_DPRINTF(FLOG, "sid 0x%"PRIx16 ", fault %d, addr 0x%"PRIx64 - ", is_write %d", source_id, fault, addr, is_write); - if (fsts_reg & VTD_FSTS_PFO) { - VTD_DPRINTF(FLOG, "new fault is not recorded due to " - "Primary Fault Overflow"); - return; - } - if (vtd_try_collapse_fault(s, source_id)) { - VTD_DPRINTF(FLOG, "new fault is not recorded due to " - "compression of faults"); - return; - } - if (vtd_is_frcd_set(s, s->next_frcd_reg)) { - VTD_DPRINTF(FLOG, "Primary Fault Overflow and " - "new fault is not recorded, set PFO field"); - vtd_set_clear_mask_long(s, DMAR_FSTS_REG, 0, VTD_FSTS_PFO); - return; - } - - vtd_record_frcd(s, s->next_frcd_reg, source_id, addr, fault, is_write); - - if (fsts_reg & VTD_FSTS_PPF) { - VTD_DPRINTF(FLOG, "there are pending faults already, " - "fault event is not generated"); - vtd_set_frcd_and_update_ppf(s, s->next_frcd_reg); - s->next_frcd_reg++; - if (s->next_frcd_reg == DMAR_FRCD_REG_NR) { - s->next_frcd_reg = 0; - } - } else { - vtd_set_clear_mask_long(s, DMAR_FSTS_REG, VTD_FSTS_FRI_MASK, - VTD_FSTS_FRI(s->next_frcd_reg)); - vtd_set_frcd_and_update_ppf(s, s->next_frcd_reg); /* Will set PPF */ - s->next_frcd_reg++; - if (s->next_frcd_reg == DMAR_FRCD_REG_NR) { - s->next_frcd_reg = 0; - } - /* This case actually cause the PPF to be Set. - * So generate fault event (interrupt). - */ - vtd_generate_fault_event(s, fsts_reg); - } -} - -/* Handle Invalidation Queue Errors of queued invalidation interface error - * conditions. - */ -static void vtd_handle_inv_queue_error(IntelIOMMUState *s) -{ - uint32_t fsts_reg = vtd_get_long_raw(s, DMAR_FSTS_REG); - - vtd_set_clear_mask_long(s, DMAR_FSTS_REG, 0, VTD_FSTS_IQE); - vtd_generate_fault_event(s, fsts_reg); -} - -/* Set the IWC field and try to generate an invalidation completion interrupt */ -static void vtd_generate_completion_event(IntelIOMMUState *s) -{ - VTD_DPRINTF(INV, "completes an invalidation wait command with " - "Interrupt Flag"); - if (vtd_get_long_raw(s, DMAR_ICS_REG) & VTD_ICS_IWC) { - VTD_DPRINTF(INV, "there is a previous interrupt condition to be " - "serviced by software, " - "new invalidation event is not generated"); - return; - } - vtd_set_clear_mask_long(s, DMAR_ICS_REG, 0, VTD_ICS_IWC); - vtd_set_clear_mask_long(s, DMAR_IECTL_REG, 0, VTD_IECTL_IP); - if (vtd_get_long_raw(s, DMAR_IECTL_REG) & VTD_IECTL_IM) { - VTD_DPRINTF(INV, "IM filed in IECTL_REG is set, new invalidation " - "event is not generated"); - return; - } else { - /* Generate the interrupt event */ - vtd_generate_interrupt(s, DMAR_IEADDR_REG, DMAR_IEDATA_REG); - vtd_set_clear_mask_long(s, DMAR_IECTL_REG, VTD_IECTL_IP, 0); - } -} - -static inline bool vtd_root_entry_present(VTDRootEntry *root) -{ - return root->val & VTD_ROOT_ENTRY_P; -} - -static int vtd_get_root_entry(IntelIOMMUState *s, uint8_t index, - VTDRootEntry *re) -{ - dma_addr_t addr; - - addr = s->root + index * sizeof(*re); - if (dma_memory_read(&address_space_memory, addr, re, sizeof(*re))) { - VTD_DPRINTF(GENERAL, "error: fail to access root-entry at 0x%"PRIx64 - " + %"PRIu8, s->root, index); - re->val = 0; - return -VTD_FR_ROOT_TABLE_INV; - } - re->val = le64_to_cpu(re->val); - return 0; -} - -static inline bool vtd_context_entry_present(VTDContextEntry *context) -{ - return context->lo & VTD_CONTEXT_ENTRY_P; -} - -static int vtd_get_context_entry_from_root(VTDRootEntry *root, uint8_t index, - VTDContextEntry *ce) -{ - dma_addr_t addr; - - if (!vtd_root_entry_present(root)) { - VTD_DPRINTF(GENERAL, "error: root-entry is not present"); - return -VTD_FR_ROOT_ENTRY_P; - } - addr = (root->val & VTD_ROOT_ENTRY_CTP) + index * sizeof(*ce); - if (dma_memory_read(&address_space_memory, addr, ce, sizeof(*ce))) { - VTD_DPRINTF(GENERAL, "error: fail to access context-entry at 0x%"PRIx64 - " + %"PRIu8, - (uint64_t)(root->val & VTD_ROOT_ENTRY_CTP), index); - return -VTD_FR_CONTEXT_TABLE_INV; - } - ce->lo = le64_to_cpu(ce->lo); - ce->hi = le64_to_cpu(ce->hi); - return 0; -} - -static inline dma_addr_t vtd_get_slpt_base_from_context(VTDContextEntry *ce) -{ - return ce->lo & VTD_CONTEXT_ENTRY_SLPTPTR; -} - -static inline uint64_t vtd_get_slpte_addr(uint64_t slpte) -{ - return slpte & VTD_SL_PT_BASE_ADDR_MASK; -} - -/* Whether the pte indicates the address of the page frame */ -static inline bool vtd_is_last_slpte(uint64_t slpte, uint32_t level) -{ - return level == VTD_SL_PT_LEVEL || (slpte & VTD_SL_PT_PAGE_SIZE_MASK); -} - -/* Get the content of a spte located in @base_addr[@index] */ -static uint64_t vtd_get_slpte(dma_addr_t base_addr, uint32_t index) -{ - uint64_t slpte; - - assert(index < VTD_SL_PT_ENTRY_NR); - - if (dma_memory_read(&address_space_memory, - base_addr + index * sizeof(slpte), &slpte, - sizeof(slpte))) { - slpte = (uint64_t)-1; - return slpte; - } - slpte = le64_to_cpu(slpte); - return slpte; -} - -/* Given a gpa and the level of paging structure, return the offset of current - * level. - */ -static inline uint32_t vtd_gpa_level_offset(uint64_t gpa, uint32_t level) -{ - return (gpa >> vtd_slpt_level_shift(level)) & - ((1ULL << VTD_SL_LEVEL_BITS) - 1); -} - -/* Check Capability Register to see if the @level of page-table is supported */ -static inline bool vtd_is_level_supported(IntelIOMMUState *s, uint32_t level) -{ - return VTD_CAP_SAGAW_MASK & s->cap & - (1ULL << (level - 2 + VTD_CAP_SAGAW_SHIFT)); -} - -/* Get the page-table level that hardware should use for the second-level - * page-table walk from the Address Width field of context-entry. - */ -static inline uint32_t vtd_get_level_from_context_entry(VTDContextEntry *ce) -{ - return 2 + (ce->hi & VTD_CONTEXT_ENTRY_AW); -} - -static inline uint32_t vtd_get_agaw_from_context_entry(VTDContextEntry *ce) -{ - return 30 + (ce->hi & VTD_CONTEXT_ENTRY_AW) * 9; -} - -static const uint64_t vtd_paging_entry_rsvd_field[] = { - [0] = ~0ULL, - /* For not large page */ - [1] = 0x800ULL | ~(VTD_HAW_MASK | VTD_SL_IGN_COM), - [2] = 0x800ULL | ~(VTD_HAW_MASK | VTD_SL_IGN_COM), - [3] = 0x800ULL | ~(VTD_HAW_MASK | VTD_SL_IGN_COM), - [4] = 0x880ULL | ~(VTD_HAW_MASK | VTD_SL_IGN_COM), - /* For large page */ - [5] = 0x800ULL | ~(VTD_HAW_MASK | VTD_SL_IGN_COM), - [6] = 0x1ff800ULL | ~(VTD_HAW_MASK | VTD_SL_IGN_COM), - [7] = 0x3ffff800ULL | ~(VTD_HAW_MASK | VTD_SL_IGN_COM), - [8] = 0x880ULL | ~(VTD_HAW_MASK | VTD_SL_IGN_COM), -}; - -static bool vtd_slpte_nonzero_rsvd(uint64_t slpte, uint32_t level) -{ - if (slpte & VTD_SL_PT_PAGE_SIZE_MASK) { - /* Maybe large page */ - return slpte & vtd_paging_entry_rsvd_field[level + 4]; - } else { - return slpte & vtd_paging_entry_rsvd_field[level]; - } -} - -/* Given the @gpa, get relevant @slptep. @slpte_level will be the last level - * of the translation, can be used for deciding the size of large page. - */ -static int vtd_gpa_to_slpte(VTDContextEntry *ce, uint64_t gpa, bool is_write, - uint64_t *slptep, uint32_t *slpte_level, - bool *reads, bool *writes) -{ - dma_addr_t addr = vtd_get_slpt_base_from_context(ce); - uint32_t level = vtd_get_level_from_context_entry(ce); - uint32_t offset; - uint64_t slpte; - uint32_t ce_agaw = vtd_get_agaw_from_context_entry(ce); - uint64_t access_right_check; - - /* Check if @gpa is above 2^X-1, where X is the minimum of MGAW in CAP_REG - * and AW in context-entry. - */ - if (gpa & ~((1ULL << MIN(ce_agaw, VTD_MGAW)) - 1)) { - VTD_DPRINTF(GENERAL, "error: gpa 0x%"PRIx64 " exceeds limits", gpa); - return -VTD_FR_ADDR_BEYOND_MGAW; - } - - /* FIXME: what is the Atomics request here? */ - access_right_check = is_write ? VTD_SL_W : VTD_SL_R; - - while (true) { - offset = vtd_gpa_level_offset(gpa, level); - slpte = vtd_get_slpte(addr, offset); - - if (slpte == (uint64_t)-1) { - VTD_DPRINTF(GENERAL, "error: fail to access second-level paging " - "entry at level %"PRIu32 " for gpa 0x%"PRIx64, - level, gpa); - if (level == vtd_get_level_from_context_entry(ce)) { - /* Invalid programming of context-entry */ - return -VTD_FR_CONTEXT_ENTRY_INV; - } else { - return -VTD_FR_PAGING_ENTRY_INV; - } - } - *reads = (*reads) && (slpte & VTD_SL_R); - *writes = (*writes) && (slpte & VTD_SL_W); - if (!(slpte & access_right_check)) { - VTD_DPRINTF(GENERAL, "error: lack of %s permission for " - "gpa 0x%"PRIx64 " slpte 0x%"PRIx64, - (is_write ? "write" : "read"), gpa, slpte); - return is_write ? -VTD_FR_WRITE : -VTD_FR_READ; - } - if (vtd_slpte_nonzero_rsvd(slpte, level)) { - VTD_DPRINTF(GENERAL, "error: non-zero reserved field in second " - "level paging entry level %"PRIu32 " slpte 0x%"PRIx64, - level, slpte); - return -VTD_FR_PAGING_ENTRY_RSVD; - } - - if (vtd_is_last_slpte(slpte, level)) { - *slptep = slpte; - *slpte_level = level; - return 0; - } - addr = vtd_get_slpte_addr(slpte); - level--; - } -} - -/* Map a device to its corresponding domain (context-entry) */ -static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, - uint8_t devfn, VTDContextEntry *ce) -{ - VTDRootEntry re; - int ret_fr; - - ret_fr = vtd_get_root_entry(s, bus_num, &re); - if (ret_fr) { - return ret_fr; - } - - if (!vtd_root_entry_present(&re)) { - VTD_DPRINTF(GENERAL, "error: root-entry #%"PRIu8 " is not present", - bus_num); - return -VTD_FR_ROOT_ENTRY_P; - } else if (re.rsvd || (re.val & VTD_ROOT_ENTRY_RSVD)) { - VTD_DPRINTF(GENERAL, "error: non-zero reserved field in root-entry " - "hi 0x%"PRIx64 " lo 0x%"PRIx64, re.rsvd, re.val); - return -VTD_FR_ROOT_ENTRY_RSVD; - } - - ret_fr = vtd_get_context_entry_from_root(&re, devfn, ce); - if (ret_fr) { - return ret_fr; - } - - if (!vtd_context_entry_present(ce)) { - VTD_DPRINTF(GENERAL, - "error: context-entry #%"PRIu8 "(bus #%"PRIu8 ") " - "is not present", devfn, bus_num); - return -VTD_FR_CONTEXT_ENTRY_P; - } else if ((ce->hi & VTD_CONTEXT_ENTRY_RSVD_HI) || - (ce->lo & VTD_CONTEXT_ENTRY_RSVD_LO)) { - VTD_DPRINTF(GENERAL, - "error: non-zero reserved field in context-entry " - "hi 0x%"PRIx64 " lo 0x%"PRIx64, ce->hi, ce->lo); - return -VTD_FR_CONTEXT_ENTRY_RSVD; - } - /* Check if the programming of context-entry is valid */ - if (!vtd_is_level_supported(s, vtd_get_level_from_context_entry(ce))) { - VTD_DPRINTF(GENERAL, "error: unsupported Address Width value in " - "context-entry hi 0x%"PRIx64 " lo 0x%"PRIx64, - ce->hi, ce->lo); - return -VTD_FR_CONTEXT_ENTRY_INV; - } else if (ce->lo & VTD_CONTEXT_ENTRY_TT) { - VTD_DPRINTF(GENERAL, "error: unsupported Translation Type in " - "context-entry hi 0x%"PRIx64 " lo 0x%"PRIx64, - ce->hi, ce->lo); - return -VTD_FR_CONTEXT_ENTRY_INV; - } - return 0; -} - -static inline uint16_t vtd_make_source_id(uint8_t bus_num, uint8_t devfn) -{ - return ((bus_num & 0xffUL) << 8) | (devfn & 0xffUL); -} - -static const bool vtd_qualified_faults[] = { - [VTD_FR_RESERVED] = false, - [VTD_FR_ROOT_ENTRY_P] = false, - [VTD_FR_CONTEXT_ENTRY_P] = true, - [VTD_FR_CONTEXT_ENTRY_INV] = true, - [VTD_FR_ADDR_BEYOND_MGAW] = true, - [VTD_FR_WRITE] = true, - [VTD_FR_READ] = true, - [VTD_FR_PAGING_ENTRY_INV] = true, - [VTD_FR_ROOT_TABLE_INV] = false, - [VTD_FR_CONTEXT_TABLE_INV] = false, - [VTD_FR_ROOT_ENTRY_RSVD] = false, - [VTD_FR_PAGING_ENTRY_RSVD] = true, - [VTD_FR_CONTEXT_ENTRY_TT] = true, - [VTD_FR_RESERVED_ERR] = false, - [VTD_FR_MAX] = false, -}; - -/* To see if a fault condition is "qualified", which is reported to software - * only if the FPD field in the context-entry used to process the faulting - * request is 0. - */ -static inline bool vtd_is_qualified_fault(VTDFaultReason fault) -{ - return vtd_qualified_faults[fault]; -} - -static inline bool vtd_is_interrupt_addr(hwaddr addr) -{ - return VTD_INTERRUPT_ADDR_FIRST <= addr && addr <= VTD_INTERRUPT_ADDR_LAST; -} - -/* Map dev to context-entry then do a paging-structures walk to do a iommu - * translation. - * - * Called from RCU critical section. - * - * @bus_num: The bus number - * @devfn: The devfn, which is the combined of device and function number - * @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, 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, page_mask; - uint32_t level; - uint16_t source_id = vtd_make_source_id(bus_num, devfn); - int ret_fr; - bool is_fpd_set = false; - bool reads = true; - bool writes = true; - VTDIOTLBEntry *iotlb_entry; - - /* Check if the request is in interrupt address range */ - if (vtd_is_interrupt_addr(addr)) { - if (is_write) { - /* FIXME: since we don't know the length of the access here, we - * treat Non-DWORD length write requests without PASID as - * interrupt requests, too. Withoud interrupt remapping support, - * we just use 1:1 mapping. - */ - VTD_DPRINTF(MMU, "write request to interrupt address " - "gpa 0x%"PRIx64, addr); - entry->iova = addr & VTD_PAGE_MASK_4K; - entry->translated_addr = addr & VTD_PAGE_MASK_4K; - entry->addr_mask = ~VTD_PAGE_MASK_4K; - entry->perm = IOMMU_WO; - return; - } else { - VTD_DPRINTF(GENERAL, "error: read request from interrupt address " - "gpa 0x%"PRIx64, addr); - vtd_report_dmar_fault(s, source_id, addr, VTD_FR_READ, is_write); - return; - } - } - /* Try to fetch slpte form IOTLB */ - iotlb_entry = vtd_lookup_iotlb(s, source_id, addr); - if (iotlb_entry) { - VTD_DPRINTF(CACHE, "hit iotlb sid 0x%"PRIx16 " gpa 0x%"PRIx64 - " slpte 0x%"PRIx64 " did 0x%"PRIx16, source_id, addr, - iotlb_entry->slpte, iotlb_entry->domain_id); - 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 */ - if (cc_entry->context_cache_gen == s->context_cache_gen) { - VTD_DPRINTF(CACHE, "hit context-cache bus %d devfn %d " - "(hi %"PRIx64 " lo %"PRIx64 " gen %"PRIu32 ")", - bus_num, devfn, cc_entry->context_entry.hi, - cc_entry->context_entry.lo, cc_entry->context_cache_gen); - ce = cc_entry->context_entry; - is_fpd_set = ce.lo & VTD_CONTEXT_ENTRY_FPD; - } else { - ret_fr = vtd_dev_to_context_entry(s, bus_num, devfn, &ce); - is_fpd_set = ce.lo & VTD_CONTEXT_ENTRY_FPD; - if (ret_fr) { - ret_fr = -ret_fr; - if (is_fpd_set && vtd_is_qualified_fault(ret_fr)) { - VTD_DPRINTF(FLOG, "fault processing is disabled for DMA " - "requests through this context-entry " - "(with FPD Set)"); - } else { - vtd_report_dmar_fault(s, source_id, addr, ret_fr, is_write); - } - return; - } - /* Update context-cache */ - VTD_DPRINTF(CACHE, "update context-cache bus %d devfn %d " - "(hi %"PRIx64 " lo %"PRIx64 " gen %"PRIu32 "->%"PRIu32 ")", - bus_num, devfn, ce.hi, ce.lo, - cc_entry->context_cache_gen, s->context_cache_gen); - cc_entry->context_entry = ce; - cc_entry->context_cache_gen = s->context_cache_gen; - } - - ret_fr = vtd_gpa_to_slpte(&ce, addr, is_write, &slpte, &level, - &reads, &writes); - if (ret_fr) { - ret_fr = -ret_fr; - if (is_fpd_set && vtd_is_qualified_fault(ret_fr)) { - VTD_DPRINTF(FLOG, "fault processing is disabled for DMA requests " - "through this context-entry (with FPD Set)"); - } else { - vtd_report_dmar_fault(s, source_id, addr, ret_fr, is_write); - } - 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, level); -out: - 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); -} - -static void vtd_root_table_setup(IntelIOMMUState *s) -{ - s->root = vtd_get_quad_raw(s, DMAR_RTADDR_REG); - s->root_extended = s->root & VTD_RTADDR_RTT; - s->root &= VTD_RTADDR_ADDR_MASK; - - VTD_DPRINTF(CSR, "root_table addr 0x%"PRIx64 " %s", s->root, - (s->root_extended ? "(extended)" : "")); -} - -static void vtd_context_global_invalidate(IntelIOMMUState *s) -{ - s->context_cache_gen++; - if (s->context_cache_gen == VTD_CONTEXT_CACHE_GEN_MAX) { - vtd_reset_context_cache(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 - */ -static void vtd_context_device_invalidate(IntelIOMMUState *s, - uint16_t source_id, - uint16_t func_mask) -{ - uint16_t mask; - VTDBus *vtd_bus; - VTDAddressSpace *vtd_as; - uint16_t devfn; - uint16_t devfn_it; - - switch (func_mask & 3) { - case 0: - mask = 0; /* No bits in the SID field masked */ - break; - case 1: - mask = 4; /* Mask bit 2 in the SID field */ - break; - case 2: - mask = 6; /* Mask bit 2:1 in the SID field */ - break; - case 3: - mask = 7; /* Mask bit 2:0 in the SID field */ - break; - } - VTD_DPRINTF(INV, "device-selective invalidation source 0x%"PRIx16 - " mask %"PRIu16, source_id, mask); - 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 = 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); - vtd_as->context_cache_entry.context_cache_gen = 0; - } - } - } -} - -/* Context-cache invalidation - * Returns the Context Actual Invalidation Granularity. - * @val: the content of the CCMD_REG - */ -static uint64_t vtd_context_cache_invalidate(IntelIOMMUState *s, uint64_t val) -{ - uint64_t caig; - uint64_t type = val & VTD_CCMD_CIRG_MASK; - - switch (type) { - case VTD_CCMD_DOMAIN_INVL: - VTD_DPRINTF(INV, "domain-selective invalidation domain 0x%"PRIx16, - (uint16_t)VTD_CCMD_DID(val)); - /* Fall through */ - case VTD_CCMD_GLOBAL_INVL: - VTD_DPRINTF(INV, "global invalidation"); - caig = VTD_CCMD_GLOBAL_INVL_A; - vtd_context_global_invalidate(s); - break; - - case VTD_CCMD_DEVICE_INVL: - caig = VTD_CCMD_DEVICE_INVL_A; - vtd_context_device_invalidate(s, VTD_CCMD_SID(val), VTD_CCMD_FM(val)); - break; - - default: - VTD_DPRINTF(GENERAL, "error: invalid granularity"); - caig = 0; - } - return caig; -} - -static void vtd_iotlb_global_invalidate(IntelIOMMUState *s) -{ - vtd_reset_iotlb(s); -} - -static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) -{ - g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_domain, - &domain_id); -} - -static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, - hwaddr addr, uint8_t am) -{ - VTDIOTLBPageInvInfo info; - - assert(am <= VTD_MAMV); - info.domain_id = domain_id; - info.addr = addr; - info.mask = ~((1 << am) - 1); - g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info); -} - -/* Flush IOTLB - * Returns the IOTLB Actual Invalidation Granularity. - * @val: the content of the IOTLB_REG - */ -static uint64_t vtd_iotlb_flush(IntelIOMMUState *s, uint64_t val) -{ - uint64_t iaig; - uint64_t type = val & VTD_TLB_FLUSH_GRANU_MASK; - uint16_t domain_id; - hwaddr addr; - uint8_t am; - - switch (type) { - case VTD_TLB_GLOBAL_FLUSH: - VTD_DPRINTF(INV, "global invalidation"); - iaig = VTD_TLB_GLOBAL_FLUSH_A; - vtd_iotlb_global_invalidate(s); - break; - - case VTD_TLB_DSI_FLUSH: - domain_id = VTD_TLB_DID(val); - VTD_DPRINTF(INV, "domain-selective invalidation domain 0x%"PRIx16, - domain_id); - iaig = VTD_TLB_DSI_FLUSH_A; - vtd_iotlb_domain_invalidate(s, domain_id); - break; - - case VTD_TLB_PSI_FLUSH: - domain_id = VTD_TLB_DID(val); - addr = vtd_get_quad_raw(s, DMAR_IVA_REG); - am = VTD_IVA_AM(addr); - addr = VTD_IVA_ADDR(addr); - VTD_DPRINTF(INV, "page-selective invalidation domain 0x%"PRIx16 - " addr 0x%"PRIx64 " mask %"PRIu8, domain_id, addr, am); - if (am > VTD_MAMV) { - VTD_DPRINTF(GENERAL, "error: supported max address mask value is " - "%"PRIu8, (uint8_t)VTD_MAMV); - iaig = 0; - break; - } - iaig = VTD_TLB_PSI_FLUSH_A; - vtd_iotlb_page_invalidate(s, domain_id, addr, am); - break; - - default: - VTD_DPRINTF(GENERAL, "error: invalid granularity"); - iaig = 0; - } - return iaig; -} - -static inline bool vtd_queued_inv_enable_check(IntelIOMMUState *s) -{ - return s->iq_tail == 0; -} - -static inline bool vtd_queued_inv_disable_check(IntelIOMMUState *s) -{ - return s->qi_enabled && (s->iq_tail == s->iq_head) && - (s->iq_last_desc_type == VTD_INV_DESC_WAIT); -} - -static void vtd_handle_gcmd_qie(IntelIOMMUState *s, bool en) -{ - uint64_t iqa_val = vtd_get_quad_raw(s, DMAR_IQA_REG); - - VTD_DPRINTF(INV, "Queued Invalidation Enable %s", (en ? "on" : "off")); - if (en) { - if (vtd_queued_inv_enable_check(s)) { - s->iq = iqa_val & VTD_IQA_IQA_MASK; - /* 2^(x+8) entries */ - s->iq_size = 1UL << ((iqa_val & VTD_IQA_QS) + 8); - s->qi_enabled = true; - VTD_DPRINTF(INV, "DMAR_IQA_REG 0x%"PRIx64, iqa_val); - VTD_DPRINTF(INV, "Invalidation Queue addr 0x%"PRIx64 " size %d", - s->iq, s->iq_size); - /* Ok - report back to driver */ - vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_QIES); - } else { - VTD_DPRINTF(GENERAL, "error: can't enable Queued Invalidation: " - "tail %"PRIu16, s->iq_tail); - } - } else { - if (vtd_queued_inv_disable_check(s)) { - /* disable Queued Invalidation */ - vtd_set_quad_raw(s, DMAR_IQH_REG, 0); - s->iq_head = 0; - s->qi_enabled = false; - /* Ok - report back to driver */ - vtd_set_clear_mask_long(s, DMAR_GSTS_REG, VTD_GSTS_QIES, 0); - } else { - VTD_DPRINTF(GENERAL, "error: can't disable Queued Invalidation: " - "head %"PRIu16 ", tail %"PRIu16 - ", last_descriptor %"PRIu8, - s->iq_head, s->iq_tail, s->iq_last_desc_type); - } - } -} - -/* Set Root Table Pointer */ -static void vtd_handle_gcmd_srtp(IntelIOMMUState *s) -{ - VTD_DPRINTF(CSR, "set Root Table Pointer"); - - vtd_root_table_setup(s); - /* Ok - report back to driver */ - vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_RTPS); -} - -/* Handle Translation Enable/Disable */ -static void vtd_handle_gcmd_te(IntelIOMMUState *s, bool en) -{ - VTD_DPRINTF(CSR, "Translation Enable %s", (en ? "on" : "off")); - - if (en) { - s->dmar_enabled = true; - /* Ok - report back to driver */ - vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_TES); - } else { - s->dmar_enabled = false; - - /* Clear the index of Fault Recording Register */ - s->next_frcd_reg = 0; - /* Ok - report back to driver */ - vtd_set_clear_mask_long(s, DMAR_GSTS_REG, VTD_GSTS_TES, 0); - } -} - -/* Handle write to Global Command Register */ -static void vtd_handle_gcmd_write(IntelIOMMUState *s) -{ - uint32_t status = vtd_get_long_raw(s, DMAR_GSTS_REG); - uint32_t val = vtd_get_long_raw(s, DMAR_GCMD_REG); - uint32_t changed = status ^ val; - - VTD_DPRINTF(CSR, "value 0x%"PRIx32 " status 0x%"PRIx32, val, status); - if (changed & VTD_GCMD_TE) { - /* Translation enable/disable */ - vtd_handle_gcmd_te(s, val & VTD_GCMD_TE); - } - if (val & VTD_GCMD_SRTP) { - /* Set/update the root-table pointer */ - vtd_handle_gcmd_srtp(s); - } - if (changed & VTD_GCMD_QIE) { - /* Queued Invalidation Enable */ - vtd_handle_gcmd_qie(s, val & VTD_GCMD_QIE); - } -} - -/* Handle write to Context Command Register */ -static void vtd_handle_ccmd_write(IntelIOMMUState *s) -{ - uint64_t ret; - uint64_t val = vtd_get_quad_raw(s, DMAR_CCMD_REG); - - /* Context-cache invalidation request */ - if (val & VTD_CCMD_ICC) { - if (s->qi_enabled) { - VTD_DPRINTF(GENERAL, "error: Queued Invalidation enabled, " - "should not use register-based invalidation"); - return; - } - ret = vtd_context_cache_invalidate(s, val); - /* Invalidation completed. Change something to show */ - vtd_set_clear_mask_quad(s, DMAR_CCMD_REG, VTD_CCMD_ICC, 0ULL); - ret = vtd_set_clear_mask_quad(s, DMAR_CCMD_REG, VTD_CCMD_CAIG_MASK, - ret); - VTD_DPRINTF(INV, "CCMD_REG write-back val: 0x%"PRIx64, ret); - } -} - -/* Handle write to IOTLB Invalidation Register */ -static void vtd_handle_iotlb_write(IntelIOMMUState *s) -{ - uint64_t ret; - uint64_t val = vtd_get_quad_raw(s, DMAR_IOTLB_REG); - - /* IOTLB invalidation request */ - if (val & VTD_TLB_IVT) { - if (s->qi_enabled) { - VTD_DPRINTF(GENERAL, "error: Queued Invalidation enabled, " - "should not use register-based invalidation"); - return; - } - ret = vtd_iotlb_flush(s, val); - /* Invalidation completed. Change something to show */ - vtd_set_clear_mask_quad(s, DMAR_IOTLB_REG, VTD_TLB_IVT, 0ULL); - ret = vtd_set_clear_mask_quad(s, DMAR_IOTLB_REG, - VTD_TLB_FLUSH_GRANU_MASK_A, ret); - VTD_DPRINTF(INV, "IOTLB_REG write-back val: 0x%"PRIx64, ret); - } -} - -/* Fetch an Invalidation Descriptor from the Invalidation Queue */ -static bool vtd_get_inv_desc(dma_addr_t base_addr, uint32_t offset, - VTDInvDesc *inv_desc) -{ - dma_addr_t addr = base_addr + offset * sizeof(*inv_desc); - if (dma_memory_read(&address_space_memory, addr, inv_desc, - sizeof(*inv_desc))) { - VTD_DPRINTF(GENERAL, "error: fail to fetch Invalidation Descriptor " - "base_addr 0x%"PRIx64 " offset %"PRIu32, base_addr, offset); - inv_desc->lo = 0; - inv_desc->hi = 0; - - return false; - } - inv_desc->lo = le64_to_cpu(inv_desc->lo); - inv_desc->hi = le64_to_cpu(inv_desc->hi); - return true; -} - -static bool vtd_process_wait_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) -{ - if ((inv_desc->hi & VTD_INV_DESC_WAIT_RSVD_HI) || - (inv_desc->lo & VTD_INV_DESC_WAIT_RSVD_LO)) { - VTD_DPRINTF(GENERAL, "error: non-zero reserved field in Invalidation " - "Wait Descriptor hi 0x%"PRIx64 " lo 0x%"PRIx64, - inv_desc->hi, inv_desc->lo); - return false; - } - if (inv_desc->lo & VTD_INV_DESC_WAIT_SW) { - /* Status Write */ - uint32_t status_data = (uint32_t)(inv_desc->lo >> - VTD_INV_DESC_WAIT_DATA_SHIFT); - - assert(!(inv_desc->lo & VTD_INV_DESC_WAIT_IF)); - - /* FIXME: need to be masked with HAW? */ - dma_addr_t status_addr = inv_desc->hi; - VTD_DPRINTF(INV, "status data 0x%x, status addr 0x%"PRIx64, - status_data, status_addr); - status_data = cpu_to_le32(status_data); - if (dma_memory_write(&address_space_memory, status_addr, &status_data, - sizeof(status_data))) { - VTD_DPRINTF(GENERAL, "error: fail to perform a coherent write"); - return false; - } - } else if (inv_desc->lo & VTD_INV_DESC_WAIT_IF) { - /* Interrupt flag */ - VTD_DPRINTF(INV, "Invalidation Wait Descriptor interrupt completion"); - vtd_generate_completion_event(s); - } else { - VTD_DPRINTF(GENERAL, "error: invalid Invalidation Wait Descriptor: " - "hi 0x%"PRIx64 " lo 0x%"PRIx64, inv_desc->hi, inv_desc->lo); - return false; - } - return true; -} - -static bool vtd_process_context_cache_desc(IntelIOMMUState *s, - VTDInvDesc *inv_desc) -{ - if ((inv_desc->lo & VTD_INV_DESC_CC_RSVD) || inv_desc->hi) { - VTD_DPRINTF(GENERAL, "error: non-zero reserved field in Context-cache " - "Invalidate Descriptor"); - return false; - } - switch (inv_desc->lo & VTD_INV_DESC_CC_G) { - case VTD_INV_DESC_CC_DOMAIN: - VTD_DPRINTF(INV, "domain-selective invalidation domain 0x%"PRIx16, - (uint16_t)VTD_INV_DESC_CC_DID(inv_desc->lo)); - /* Fall through */ - case VTD_INV_DESC_CC_GLOBAL: - VTD_DPRINTF(INV, "global invalidation"); - vtd_context_global_invalidate(s); - break; - - case VTD_INV_DESC_CC_DEVICE: - vtd_context_device_invalidate(s, VTD_INV_DESC_CC_SID(inv_desc->lo), - VTD_INV_DESC_CC_FM(inv_desc->lo)); - break; - - default: - VTD_DPRINTF(GENERAL, "error: invalid granularity in Context-cache " - "Invalidate Descriptor hi 0x%"PRIx64 " lo 0x%"PRIx64, - inv_desc->hi, inv_desc->lo); - return false; - } - return true; -} - -static bool vtd_process_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) -{ - uint16_t domain_id; - uint8_t am; - hwaddr addr; - - if ((inv_desc->lo & VTD_INV_DESC_IOTLB_RSVD_LO) || - (inv_desc->hi & VTD_INV_DESC_IOTLB_RSVD_HI)) { - VTD_DPRINTF(GENERAL, "error: non-zero reserved field in IOTLB " - "Invalidate Descriptor hi 0x%"PRIx64 " lo 0x%"PRIx64, - inv_desc->hi, inv_desc->lo); - return false; - } - - switch (inv_desc->lo & VTD_INV_DESC_IOTLB_G) { - case VTD_INV_DESC_IOTLB_GLOBAL: - VTD_DPRINTF(INV, "global invalidation"); - vtd_iotlb_global_invalidate(s); - break; - - case VTD_INV_DESC_IOTLB_DOMAIN: - domain_id = VTD_INV_DESC_IOTLB_DID(inv_desc->lo); - VTD_DPRINTF(INV, "domain-selective invalidation domain 0x%"PRIx16, - domain_id); - vtd_iotlb_domain_invalidate(s, domain_id); - break; - - case VTD_INV_DESC_IOTLB_PAGE: - domain_id = VTD_INV_DESC_IOTLB_DID(inv_desc->lo); - addr = VTD_INV_DESC_IOTLB_ADDR(inv_desc->hi); - am = VTD_INV_DESC_IOTLB_AM(inv_desc->hi); - VTD_DPRINTF(INV, "page-selective invalidation domain 0x%"PRIx16 - " addr 0x%"PRIx64 " mask %"PRIu8, domain_id, addr, am); - if (am > VTD_MAMV) { - VTD_DPRINTF(GENERAL, "error: supported max address mask value is " - "%"PRIu8, (uint8_t)VTD_MAMV); - return false; - } - vtd_iotlb_page_invalidate(s, domain_id, addr, am); - break; - - default: - VTD_DPRINTF(GENERAL, "error: invalid granularity in IOTLB Invalidate " - "Descriptor hi 0x%"PRIx64 " lo 0x%"PRIx64, - inv_desc->hi, inv_desc->lo); - return false; - } - return true; -} - -static bool vtd_process_inv_desc(IntelIOMMUState *s) -{ - VTDInvDesc inv_desc; - uint8_t desc_type; - - VTD_DPRINTF(INV, "iq head %"PRIu16, s->iq_head); - if (!vtd_get_inv_desc(s->iq, s->iq_head, &inv_desc)) { - s->iq_last_desc_type = VTD_INV_DESC_NONE; - return false; - } - desc_type = inv_desc.lo & VTD_INV_DESC_TYPE; - /* FIXME: should update at first or at last? */ - s->iq_last_desc_type = desc_type; - - switch (desc_type) { - case VTD_INV_DESC_CC: - VTD_DPRINTF(INV, "Context-cache Invalidate Descriptor hi 0x%"PRIx64 - " lo 0x%"PRIx64, inv_desc.hi, inv_desc.lo); - if (!vtd_process_context_cache_desc(s, &inv_desc)) { - return false; - } - break; - - case VTD_INV_DESC_IOTLB: - VTD_DPRINTF(INV, "IOTLB Invalidate Descriptor hi 0x%"PRIx64 - " lo 0x%"PRIx64, inv_desc.hi, inv_desc.lo); - if (!vtd_process_iotlb_desc(s, &inv_desc)) { - return false; - } - break; - - case VTD_INV_DESC_WAIT: - VTD_DPRINTF(INV, "Invalidation Wait Descriptor hi 0x%"PRIx64 - " lo 0x%"PRIx64, inv_desc.hi, inv_desc.lo); - if (!vtd_process_wait_desc(s, &inv_desc)) { - return false; - } - break; - - default: - VTD_DPRINTF(GENERAL, "error: unkonw Invalidation Descriptor type " - "hi 0x%"PRIx64 " lo 0x%"PRIx64 " type %"PRIu8, - inv_desc.hi, inv_desc.lo, desc_type); - return false; - } - s->iq_head++; - if (s->iq_head == s->iq_size) { - s->iq_head = 0; - } - return true; -} - -/* Try to fetch and process more Invalidation Descriptors */ -static void vtd_fetch_inv_desc(IntelIOMMUState *s) -{ - VTD_DPRINTF(INV, "fetch Invalidation Descriptors"); - if (s->iq_tail >= s->iq_size) { - /* Detects an invalid Tail pointer */ - VTD_DPRINTF(GENERAL, "error: iq_tail is %"PRIu16 - " while iq_size is %"PRIu16, s->iq_tail, s->iq_size); - vtd_handle_inv_queue_error(s); - return; - } - while (s->iq_head != s->iq_tail) { - if (!vtd_process_inv_desc(s)) { - /* Invalidation Queue Errors */ - vtd_handle_inv_queue_error(s); - break; - } - /* Must update the IQH_REG in time */ - vtd_set_quad_raw(s, DMAR_IQH_REG, - (((uint64_t)(s->iq_head)) << VTD_IQH_QH_SHIFT) & - VTD_IQH_QH_MASK); - } -} - -/* Handle write to Invalidation Queue Tail Register */ -static void vtd_handle_iqt_write(IntelIOMMUState *s) -{ - uint64_t val = vtd_get_quad_raw(s, DMAR_IQT_REG); - - s->iq_tail = VTD_IQT_QT(val); - VTD_DPRINTF(INV, "set iq tail %"PRIu16, s->iq_tail); - if (s->qi_enabled && !(vtd_get_long_raw(s, DMAR_FSTS_REG) & VTD_FSTS_IQE)) { - /* Process Invalidation Queue here */ - vtd_fetch_inv_desc(s); - } -} - -static void vtd_handle_fsts_write(IntelIOMMUState *s) -{ - uint32_t fsts_reg = vtd_get_long_raw(s, DMAR_FSTS_REG); - uint32_t fectl_reg = vtd_get_long_raw(s, DMAR_FECTL_REG); - uint32_t status_fields = VTD_FSTS_PFO | VTD_FSTS_PPF | VTD_FSTS_IQE; - - if ((fectl_reg & VTD_FECTL_IP) && !(fsts_reg & status_fields)) { - vtd_set_clear_mask_long(s, DMAR_FECTL_REG, VTD_FECTL_IP, 0); - VTD_DPRINTF(FLOG, "all pending interrupt conditions serviced, clear " - "IP field of FECTL_REG"); - } - /* FIXME: when IQE is Clear, should we try to fetch some Invalidation - * Descriptors if there are any when Queued Invalidation is enabled? - */ -} - -static void vtd_handle_fectl_write(IntelIOMMUState *s) -{ - uint32_t fectl_reg; - /* FIXME: when software clears the IM field, check the IP field. But do we - * need to compare the old value and the new value to conclude that - * software clears the IM field? Or just check if the IM field is zero? - */ - fectl_reg = vtd_get_long_raw(s, DMAR_FECTL_REG); - if ((fectl_reg & VTD_FECTL_IP) && !(fectl_reg & VTD_FECTL_IM)) { - vtd_generate_interrupt(s, DMAR_FEADDR_REG, DMAR_FEDATA_REG); - vtd_set_clear_mask_long(s, DMAR_FECTL_REG, VTD_FECTL_IP, 0); - VTD_DPRINTF(FLOG, "IM field is cleared, generate " - "fault event interrupt"); - } -} - -static void vtd_handle_ics_write(IntelIOMMUState *s) -{ - uint32_t ics_reg = vtd_get_long_raw(s, DMAR_ICS_REG); - uint32_t iectl_reg = vtd_get_long_raw(s, DMAR_IECTL_REG); - - if ((iectl_reg & VTD_IECTL_IP) && !(ics_reg & VTD_ICS_IWC)) { - vtd_set_clear_mask_long(s, DMAR_IECTL_REG, VTD_IECTL_IP, 0); - VTD_DPRINTF(INV, "pending completion interrupt condition serviced, " - "clear IP field of IECTL_REG"); - } -} - -static void vtd_handle_iectl_write(IntelIOMMUState *s) -{ - uint32_t iectl_reg; - /* FIXME: when software clears the IM field, check the IP field. But do we - * need to compare the old value and the new value to conclude that - * software clears the IM field? Or just check if the IM field is zero? - */ - iectl_reg = vtd_get_long_raw(s, DMAR_IECTL_REG); - if ((iectl_reg & VTD_IECTL_IP) && !(iectl_reg & VTD_IECTL_IM)) { - vtd_generate_interrupt(s, DMAR_IEADDR_REG, DMAR_IEDATA_REG); - vtd_set_clear_mask_long(s, DMAR_IECTL_REG, VTD_IECTL_IP, 0); - VTD_DPRINTF(INV, "IM field is cleared, generate " - "invalidation event interrupt"); - } -} - -static uint64_t vtd_mem_read(void *opaque, hwaddr addr, unsigned size) -{ - IntelIOMMUState *s = opaque; - uint64_t val; - - if (addr + size > DMAR_REG_SIZE) { - VTD_DPRINTF(GENERAL, "error: addr outside region: max 0x%"PRIx64 - ", got 0x%"PRIx64 " %d", - (uint64_t)DMAR_REG_SIZE, addr, size); - return (uint64_t)-1; - } - - switch (addr) { - /* Root Table Address Register, 64-bit */ - case DMAR_RTADDR_REG: - if (size == 4) { - val = s->root & ((1ULL << 32) - 1); - } else { - val = s->root; - } - break; - - case DMAR_RTADDR_REG_HI: - assert(size == 4); - val = s->root >> 32; - break; - - /* Invalidation Queue Address Register, 64-bit */ - case DMAR_IQA_REG: - val = s->iq | (vtd_get_quad(s, DMAR_IQA_REG) & VTD_IQA_QS); - if (size == 4) { - val = val & ((1ULL << 32) - 1); - } - break; - - case DMAR_IQA_REG_HI: - assert(size == 4); - val = s->iq >> 32; - break; - - default: - if (size == 4) { - val = vtd_get_long(s, addr); - } else { - val = vtd_get_quad(s, addr); - } - } - VTD_DPRINTF(CSR, "addr 0x%"PRIx64 " size %d val 0x%"PRIx64, - addr, size, val); - return val; -} - -static void vtd_mem_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - IntelIOMMUState *s = opaque; - - if (addr + size > DMAR_REG_SIZE) { - VTD_DPRINTF(GENERAL, "error: addr outside region: max 0x%"PRIx64 - ", got 0x%"PRIx64 " %d", - (uint64_t)DMAR_REG_SIZE, addr, size); - return; - } - - switch (addr) { - /* Global Command Register, 32-bit */ - case DMAR_GCMD_REG: - VTD_DPRINTF(CSR, "DMAR_GCMD_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - vtd_set_long(s, addr, val); - vtd_handle_gcmd_write(s); - break; - - /* Context Command Register, 64-bit */ - case DMAR_CCMD_REG: - VTD_DPRINTF(CSR, "DMAR_CCMD_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - if (size == 4) { - vtd_set_long(s, addr, val); - } else { - vtd_set_quad(s, addr, val); - vtd_handle_ccmd_write(s); - } - break; - - case DMAR_CCMD_REG_HI: - VTD_DPRINTF(CSR, "DMAR_CCMD_REG_HI write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - vtd_handle_ccmd_write(s); - break; - - /* IOTLB Invalidation Register, 64-bit */ - case DMAR_IOTLB_REG: - VTD_DPRINTF(INV, "DMAR_IOTLB_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - if (size == 4) { - vtd_set_long(s, addr, val); - } else { - vtd_set_quad(s, addr, val); - vtd_handle_iotlb_write(s); - } - break; - - case DMAR_IOTLB_REG_HI: - VTD_DPRINTF(INV, "DMAR_IOTLB_REG_HI write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - vtd_handle_iotlb_write(s); - break; - - /* Invalidate Address Register, 64-bit */ - case DMAR_IVA_REG: - VTD_DPRINTF(INV, "DMAR_IVA_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - if (size == 4) { - vtd_set_long(s, addr, val); - } else { - vtd_set_quad(s, addr, val); - } - break; - - case DMAR_IVA_REG_HI: - VTD_DPRINTF(INV, "DMAR_IVA_REG_HI write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - break; - - /* Fault Status Register, 32-bit */ - case DMAR_FSTS_REG: - VTD_DPRINTF(FLOG, "DMAR_FSTS_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - vtd_handle_fsts_write(s); - break; - - /* Fault Event Control Register, 32-bit */ - case DMAR_FECTL_REG: - VTD_DPRINTF(FLOG, "DMAR_FECTL_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - vtd_handle_fectl_write(s); - break; - - /* Fault Event Data Register, 32-bit */ - case DMAR_FEDATA_REG: - VTD_DPRINTF(FLOG, "DMAR_FEDATA_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - break; - - /* Fault Event Address Register, 32-bit */ - case DMAR_FEADDR_REG: - VTD_DPRINTF(FLOG, "DMAR_FEADDR_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - break; - - /* Fault Event Upper Address Register, 32-bit */ - case DMAR_FEUADDR_REG: - VTD_DPRINTF(FLOG, "DMAR_FEUADDR_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - break; - - /* Protected Memory Enable Register, 32-bit */ - case DMAR_PMEN_REG: - VTD_DPRINTF(CSR, "DMAR_PMEN_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - break; - - /* Root Table Address Register, 64-bit */ - case DMAR_RTADDR_REG: - VTD_DPRINTF(CSR, "DMAR_RTADDR_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - if (size == 4) { - vtd_set_long(s, addr, val); - } else { - vtd_set_quad(s, addr, val); - } - break; - - case DMAR_RTADDR_REG_HI: - VTD_DPRINTF(CSR, "DMAR_RTADDR_REG_HI write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - break; - - /* Invalidation Queue Tail Register, 64-bit */ - case DMAR_IQT_REG: - VTD_DPRINTF(INV, "DMAR_IQT_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - if (size == 4) { - vtd_set_long(s, addr, val); - } else { - vtd_set_quad(s, addr, val); - } - vtd_handle_iqt_write(s); - break; - - case DMAR_IQT_REG_HI: - VTD_DPRINTF(INV, "DMAR_IQT_REG_HI write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - /* 19:63 of IQT_REG is RsvdZ, do nothing here */ - break; - - /* Invalidation Queue Address Register, 64-bit */ - case DMAR_IQA_REG: - VTD_DPRINTF(INV, "DMAR_IQA_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - if (size == 4) { - vtd_set_long(s, addr, val); - } else { - vtd_set_quad(s, addr, val); - } - break; - - case DMAR_IQA_REG_HI: - VTD_DPRINTF(INV, "DMAR_IQA_REG_HI write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - break; - - /* Invalidation Completion Status Register, 32-bit */ - case DMAR_ICS_REG: - VTD_DPRINTF(INV, "DMAR_ICS_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - vtd_handle_ics_write(s); - break; - - /* Invalidation Event Control Register, 32-bit */ - case DMAR_IECTL_REG: - VTD_DPRINTF(INV, "DMAR_IECTL_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - vtd_handle_iectl_write(s); - break; - - /* Invalidation Event Data Register, 32-bit */ - case DMAR_IEDATA_REG: - VTD_DPRINTF(INV, "DMAR_IEDATA_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - break; - - /* Invalidation Event Address Register, 32-bit */ - case DMAR_IEADDR_REG: - VTD_DPRINTF(INV, "DMAR_IEADDR_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - break; - - /* Invalidation Event Upper Address Register, 32-bit */ - case DMAR_IEUADDR_REG: - VTD_DPRINTF(INV, "DMAR_IEUADDR_REG write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - break; - - /* Fault Recording Registers, 128-bit */ - case DMAR_FRCD_REG_0_0: - VTD_DPRINTF(FLOG, "DMAR_FRCD_REG_0_0 write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - if (size == 4) { - vtd_set_long(s, addr, val); - } else { - vtd_set_quad(s, addr, val); - } - break; - - case DMAR_FRCD_REG_0_1: - VTD_DPRINTF(FLOG, "DMAR_FRCD_REG_0_1 write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - break; - - case DMAR_FRCD_REG_0_2: - VTD_DPRINTF(FLOG, "DMAR_FRCD_REG_0_2 write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - if (size == 4) { - vtd_set_long(s, addr, val); - } else { - vtd_set_quad(s, addr, val); - /* May clear bit 127 (Fault), update PPF */ - vtd_update_fsts_ppf(s); - } - break; - - case DMAR_FRCD_REG_0_3: - VTD_DPRINTF(FLOG, "DMAR_FRCD_REG_0_3 write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - assert(size == 4); - vtd_set_long(s, addr, val); - /* May clear bit 127 (Fault), update PPF */ - vtd_update_fsts_ppf(s); - break; - - default: - VTD_DPRINTF(GENERAL, "error: unhandled reg write addr 0x%"PRIx64 - ", size %d, val 0x%"PRIx64, addr, size, val); - if (size == 4) { - vtd_set_long(s, addr, val); - } else { - vtd_set_quad(s, addr, val); - } - } -} - -static IOMMUTLBEntry vtd_iommu_translate(MemoryRegion *iommu, hwaddr addr, - bool is_write) -{ - VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); - IntelIOMMUState *s = vtd_as->iommu_state; - IOMMUTLBEntry ret = { - .target_as = &address_space_memory, - .iova = addr, - .translated_addr = 0, - .addr_mask = ~(hwaddr)0, - .perm = IOMMU_NONE, - }; - - if (!s->dmar_enabled) { - /* DMAR disabled, passthrough, use 4k-page*/ - ret.iova = addr & VTD_PAGE_MASK_4K; - ret.translated_addr = addr & VTD_PAGE_MASK_4K; - ret.addr_mask = ~VTD_PAGE_MASK_4K; - ret.perm = IOMMU_RW; - return ret; - } - - 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, 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; -} - -static const VMStateDescription vtd_vmstate = { - .name = "iommu-intel", - .unmigratable = 1, -}; - -static const MemoryRegionOps vtd_mem_ops = { - .read = vtd_mem_read, - .write = vtd_mem_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 8, - }, - .valid = { - .min_access_size = 4, - .max_access_size = 8, - }, -}; - -static Property vtd_properties[] = { - DEFINE_PROP_UINT32("version", IntelIOMMUState, version, 0), - 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. - */ -static void vtd_init(IntelIOMMUState *s) -{ - memset(s->csr, 0, DMAR_REG_SIZE); - memset(s->wmask, 0, DMAR_REG_SIZE); - memset(s->w1cmask, 0, DMAR_REG_SIZE); - memset(s->womask, 0, DMAR_REG_SIZE); - - s->iommu_ops.translate = vtd_iommu_translate; - s->root = 0; - s->root_extended = false; - s->dmar_enabled = false; - s->iq_head = 0; - s->iq_tail = 0; - s->iq = 0; - s->iq_size = 0; - s->qi_enabled = false; - 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_SLLPS; - s->ecap = VTD_ECAP_QI | VTD_ECAP_IRO; - - vtd_reset_context_cache(s); - vtd_reset_iotlb(s); - - /* Define registers with default values and bit semantics */ - vtd_define_long(s, DMAR_VER_REG, 0x10UL, 0, 0); - vtd_define_quad(s, DMAR_CAP_REG, s->cap, 0, 0); - vtd_define_quad(s, DMAR_ECAP_REG, s->ecap, 0, 0); - vtd_define_long(s, DMAR_GCMD_REG, 0, 0xff800000UL, 0); - vtd_define_long_wo(s, DMAR_GCMD_REG, 0xff800000UL); - vtd_define_long(s, DMAR_GSTS_REG, 0, 0, 0); - vtd_define_quad(s, DMAR_RTADDR_REG, 0, 0xfffffffffffff000ULL, 0); - vtd_define_quad(s, DMAR_CCMD_REG, 0, 0xe0000003ffffffffULL, 0); - vtd_define_quad_wo(s, DMAR_CCMD_REG, 0x3ffff0000ULL); - - /* Advanced Fault Logging not supported */ - vtd_define_long(s, DMAR_FSTS_REG, 0, 0, 0x11UL); - vtd_define_long(s, DMAR_FECTL_REG, 0x80000000UL, 0x80000000UL, 0); - vtd_define_long(s, DMAR_FEDATA_REG, 0, 0x0000ffffUL, 0); - vtd_define_long(s, DMAR_FEADDR_REG, 0, 0xfffffffcUL, 0); - - /* Treated as RsvdZ when EIM in ECAP_REG is not supported - * vtd_define_long(s, DMAR_FEUADDR_REG, 0, 0xffffffffUL, 0); - */ - vtd_define_long(s, DMAR_FEUADDR_REG, 0, 0, 0); - - /* Treated as RO for implementations that PLMR and PHMR fields reported - * as Clear in the CAP_REG. - * vtd_define_long(s, DMAR_PMEN_REG, 0, 0x80000000UL, 0); - */ - vtd_define_long(s, DMAR_PMEN_REG, 0, 0, 0); - - vtd_define_quad(s, DMAR_IQH_REG, 0, 0, 0); - vtd_define_quad(s, DMAR_IQT_REG, 0, 0x7fff0ULL, 0); - vtd_define_quad(s, DMAR_IQA_REG, 0, 0xfffffffffffff007ULL, 0); - vtd_define_long(s, DMAR_ICS_REG, 0, 0, 0x1UL); - vtd_define_long(s, DMAR_IECTL_REG, 0x80000000UL, 0x80000000UL, 0); - vtd_define_long(s, DMAR_IEDATA_REG, 0, 0xffffffffUL, 0); - vtd_define_long(s, DMAR_IEADDR_REG, 0, 0xfffffffcUL, 0); - /* Treadted as RsvdZ when EIM in ECAP_REG is not supported */ - vtd_define_long(s, DMAR_IEUADDR_REG, 0, 0, 0); - - /* IOTLB registers */ - vtd_define_quad(s, DMAR_IOTLB_REG, 0, 0Xb003ffff00000000ULL, 0); - vtd_define_quad(s, DMAR_IVA_REG, 0, 0xfffffffffffff07fULL, 0); - vtd_define_quad_wo(s, DMAR_IVA_REG, 0xfffffffffffff07fULL); - - /* Fault Recording Registers, 128-bit */ - vtd_define_quad(s, DMAR_FRCD_REG_0_0, 0, 0, 0); - vtd_define_quad(s, DMAR_FRCD_REG_0_2, 0, 0, 0x8000000000000000ULL); -} - -/* Should not reset address_spaces when reset because devices will still use - * the address space they got at first (won't ask the bus again). - */ -static void vtd_reset(DeviceState *dev) -{ - IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev); - - VTD_DPRINTF(GENERAL, ""); - vtd_init(s); -} - -static void vtd_realize(DeviceState *dev, Error **errp) -{ - IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev); - - VTD_DPRINTF(GENERAL, ""); - 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); -} - -static void vtd_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = vtd_reset; - dc->realize = vtd_realize; - dc->vmsd = &vtd_vmstate; - dc->props = vtd_properties; -} - -static const TypeInfo vtd_info = { - .name = TYPE_INTEL_IOMMU_DEVICE, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IntelIOMMUState), - .class_init = vtd_class_init, -}; - -static void vtd_register_types(void) -{ - VTD_DPRINTF(GENERAL, ""); - type_register_static(&vtd_info); -} - -type_init(vtd_register_types) diff --git a/qemu/hw/i386/intel_iommu_internal.h b/qemu/hw/i386/intel_iommu_internal.h deleted file mode 100644 index e5f514c6e..000000000 --- a/qemu/hw/i386/intel_iommu_internal.h +++ /dev/null @@ -1,391 +0,0 @@ -/* - * QEMU emulation of an Intel IOMMU (VT-d) - * (DMA Remapping device) - * - * Copyright (C) 2013 Knut Omang, Oracle - * Copyright (C) 2014 Le Tan, - * - * 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 . - * - * Lots of defines copied from kernel/include/linux/intel-iommu.h: - * Copyright (C) 2006-2008 Intel Corporation - * Author: Ashok Raj - * Author: Anil S Keshavamurthy - * - */ - -#ifndef HW_I386_INTEL_IOMMU_INTERNAL_H -#define HW_I386_INTEL_IOMMU_INTERNAL_H -#include "hw/i386/intel_iommu.h" - -/* - * Intel IOMMU register specification - */ -#define DMAR_VER_REG 0x0 /* Arch version supported by this IOMMU */ -#define DMAR_CAP_REG 0x8 /* Hardware supported capabilities */ -#define DMAR_CAP_REG_HI 0xc /* High 32-bit of DMAR_CAP_REG */ -#define DMAR_ECAP_REG 0x10 /* Extended capabilities supported */ -#define DMAR_ECAP_REG_HI 0X14 -#define DMAR_GCMD_REG 0x18 /* Global command */ -#define DMAR_GSTS_REG 0x1c /* Global status */ -#define DMAR_RTADDR_REG 0x20 /* Root entry table */ -#define DMAR_RTADDR_REG_HI 0X24 -#define DMAR_CCMD_REG 0x28 /* Context command */ -#define DMAR_CCMD_REG_HI 0x2c -#define DMAR_FSTS_REG 0x34 /* Fault status */ -#define DMAR_FECTL_REG 0x38 /* Fault control */ -#define DMAR_FEDATA_REG 0x3c /* Fault event interrupt data */ -#define DMAR_FEADDR_REG 0x40 /* Fault event interrupt addr */ -#define DMAR_FEUADDR_REG 0x44 /* Upper address */ -#define DMAR_AFLOG_REG 0x58 /* Advanced fault control */ -#define DMAR_AFLOG_REG_HI 0X5c -#define DMAR_PMEN_REG 0x64 /* Enable protected memory region */ -#define DMAR_PLMBASE_REG 0x68 /* PMRR low addr */ -#define DMAR_PLMLIMIT_REG 0x6c /* PMRR low limit */ -#define DMAR_PHMBASE_REG 0x70 /* PMRR high base addr */ -#define DMAR_PHMBASE_REG_HI 0X74 -#define DMAR_PHMLIMIT_REG 0x78 /* PMRR high limit */ -#define DMAR_PHMLIMIT_REG_HI 0x7c -#define DMAR_IQH_REG 0x80 /* Invalidation queue head */ -#define DMAR_IQH_REG_HI 0X84 -#define DMAR_IQT_REG 0x88 /* Invalidation queue tail */ -#define DMAR_IQT_REG_HI 0X8c -#define DMAR_IQA_REG 0x90 /* Invalidation queue addr */ -#define DMAR_IQA_REG_HI 0x94 -#define DMAR_ICS_REG 0x9c /* Invalidation complete status */ -#define DMAR_IRTA_REG 0xb8 /* Interrupt remapping table addr */ -#define DMAR_IRTA_REG_HI 0xbc -#define DMAR_IECTL_REG 0xa0 /* Invalidation event control */ -#define DMAR_IEDATA_REG 0xa4 /* Invalidation event data */ -#define DMAR_IEADDR_REG 0xa8 /* Invalidation event address */ -#define DMAR_IEUADDR_REG 0xac /* Invalidation event address */ -#define DMAR_PQH_REG 0xc0 /* Page request queue head */ -#define DMAR_PQH_REG_HI 0xc4 -#define DMAR_PQT_REG 0xc8 /* Page request queue tail*/ -#define DMAR_PQT_REG_HI 0xcc -#define DMAR_PQA_REG 0xd0 /* Page request queue address */ -#define DMAR_PQA_REG_HI 0xd4 -#define DMAR_PRS_REG 0xdc /* Page request status */ -#define DMAR_PECTL_REG 0xe0 /* Page request event control */ -#define DMAR_PEDATA_REG 0xe4 /* Page request event data */ -#define DMAR_PEADDR_REG 0xe8 /* Page request event address */ -#define DMAR_PEUADDR_REG 0xec /* Page event upper address */ -#define DMAR_MTRRCAP_REG 0x100 /* MTRR capability */ -#define DMAR_MTRRCAP_REG_HI 0x104 -#define DMAR_MTRRDEF_REG 0x108 /* MTRR default type */ -#define DMAR_MTRRDEF_REG_HI 0x10c - -/* IOTLB registers */ -#define DMAR_IOTLB_REG_OFFSET 0xf0 /* Offset to the IOTLB registers */ -#define DMAR_IVA_REG DMAR_IOTLB_REG_OFFSET /* Invalidate address */ -#define DMAR_IVA_REG_HI (DMAR_IVA_REG + 4) -/* IOTLB invalidate register */ -#define DMAR_IOTLB_REG (DMAR_IOTLB_REG_OFFSET + 0x8) -#define DMAR_IOTLB_REG_HI (DMAR_IOTLB_REG + 4) - -/* FRCD */ -#define DMAR_FRCD_REG_OFFSET 0x220 /* Offset to the fault recording regs */ -/* NOTICE: If you change the DMAR_FRCD_REG_NR, please remember to change the - * DMAR_REG_SIZE in include/hw/i386/intel_iommu.h. - * #define DMAR_REG_SIZE (DMAR_FRCD_REG_OFFSET + 16 * DMAR_FRCD_REG_NR) - */ -#define DMAR_FRCD_REG_NR 1ULL /* Num of fault recording regs */ - -#define DMAR_FRCD_REG_0_0 0x220 /* The 0th fault recording regs */ -#define DMAR_FRCD_REG_0_1 0x224 -#define DMAR_FRCD_REG_0_2 0x228 -#define DMAR_FRCD_REG_0_3 0x22c - -/* Interrupt Address Range */ -#define VTD_INTERRUPT_ADDR_FIRST 0xfee00000ULL -#define VTD_INTERRUPT_ADDR_LAST 0xfeefffffULL - -/* 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 */ -#define VTD_TLB_GLOBAL_FLUSH (1ULL << 60) /* Global invalidation */ -#define VTD_TLB_DSI_FLUSH (2ULL << 60) /* Domain-selective */ -#define VTD_TLB_PSI_FLUSH (3ULL << 60) /* Page-selective */ -#define VTD_TLB_FLUSH_GRANU_MASK (3ULL << 60) -#define VTD_TLB_GLOBAL_FLUSH_A (1ULL << 57) -#define VTD_TLB_DSI_FLUSH_A (2ULL << 57) -#define VTD_TLB_PSI_FLUSH_A (3ULL << 57) -#define VTD_TLB_FLUSH_GRANU_MASK_A (3ULL << 57) -#define VTD_TLB_IVT (1ULL << 63) -#define VTD_TLB_DID(val) (((val) >> 32) & VTD_DOMAIN_ID_MASK) - -/* IVA_REG */ -#define VTD_IVA_ADDR(val) ((val) & ~0xfffULL & ((1ULL << VTD_MGAW) - 1)) -#define VTD_IVA_AM(val) ((val) & 0x3fULL) - -/* GCMD_REG */ -#define VTD_GCMD_TE (1UL << 31) -#define VTD_GCMD_SRTP (1UL << 30) -#define VTD_GCMD_SFL (1UL << 29) -#define VTD_GCMD_EAFL (1UL << 28) -#define VTD_GCMD_WBF (1UL << 27) -#define VTD_GCMD_QIE (1UL << 26) -#define VTD_GCMD_IRE (1UL << 25) -#define VTD_GCMD_SIRTP (1UL << 24) -#define VTD_GCMD_CFI (1UL << 23) - -/* GSTS_REG */ -#define VTD_GSTS_TES (1UL << 31) -#define VTD_GSTS_RTPS (1UL << 30) -#define VTD_GSTS_FLS (1UL << 29) -#define VTD_GSTS_AFLS (1UL << 28) -#define VTD_GSTS_WBFS (1UL << 27) -#define VTD_GSTS_QIES (1UL << 26) -#define VTD_GSTS_IRES (1UL << 25) -#define VTD_GSTS_IRTPS (1UL << 24) -#define VTD_GSTS_CFIS (1UL << 23) - -/* CCMD_REG */ -#define VTD_CCMD_ICC (1ULL << 63) -#define VTD_CCMD_GLOBAL_INVL (1ULL << 61) -#define VTD_CCMD_DOMAIN_INVL (2ULL << 61) -#define VTD_CCMD_DEVICE_INVL (3ULL << 61) -#define VTD_CCMD_CIRG_MASK (3ULL << 61) -#define VTD_CCMD_GLOBAL_INVL_A (1ULL << 59) -#define VTD_CCMD_DOMAIN_INVL_A (2ULL << 59) -#define VTD_CCMD_DEVICE_INVL_A (3ULL << 59) -#define VTD_CCMD_CAIG_MASK (3ULL << 59) -#define VTD_CCMD_DID(val) ((val) & VTD_DOMAIN_ID_MASK) -#define VTD_CCMD_SID(val) (((val) >> 16) & 0xffffULL) -#define VTD_CCMD_FM(val) (((val) >> 32) & 3ULL) - -/* RTADDR_REG */ -#define VTD_RTADDR_RTT (1ULL << 11) -#define VTD_RTADDR_ADDR_MASK (VTD_HAW_MASK ^ 0xfffULL) - -/* ECAP_REG */ -/* (offset >> 4) << 8 */ -#define VTD_ECAP_IRO (DMAR_IOTLB_REG_OFFSET << 4) -#define VTD_ECAP_QI (1ULL << 1) - -/* CAP_REG */ -/* (offset >> 4) << 24 */ -#define VTD_CAP_FRO (DMAR_FRCD_REG_OFFSET << 20) -#define VTD_CAP_NFR ((DMAR_FRCD_REG_NR - 1) << 40) -#define VTD_DOMAIN_ID_SHIFT 16 /* 16-bit domain id for 64K domains */ -#define VTD_DOMAIN_ID_MASK ((1UL << VTD_DOMAIN_ID_SHIFT) - 1) -#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 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 -#define VTD_CAP_SAGAW_MASK (0x1fULL << VTD_CAP_SAGAW_SHIFT) - /* 39-bit AGAW, 3-level page-table */ -#define VTD_CAP_SAGAW_39bit (0x2ULL << VTD_CAP_SAGAW_SHIFT) - /* 48-bit AGAW, 4-level page-table */ -#define VTD_CAP_SAGAW_48bit (0x4ULL << VTD_CAP_SAGAW_SHIFT) -#define VTD_CAP_SAGAW VTD_CAP_SAGAW_39bit - -/* IQT_REG */ -#define VTD_IQT_QT(val) (((val) >> 4) & 0x7fffULL) - -/* IQA_REG */ -#define VTD_IQA_IQA_MASK (VTD_HAW_MASK ^ 0xfffULL) -#define VTD_IQA_QS 0x7ULL - -/* IQH_REG */ -#define VTD_IQH_QH_SHIFT 4 -#define VTD_IQH_QH_MASK 0x7fff0ULL - -/* ICS_REG */ -#define VTD_ICS_IWC 1UL - -/* IECTL_REG */ -#define VTD_IECTL_IM (1UL << 31) -#define VTD_IECTL_IP (1UL << 30) - -/* FSTS_REG */ -#define VTD_FSTS_FRI_MASK 0xff00UL -#define VTD_FSTS_FRI(val) ((((uint32_t)(val)) << 8) & VTD_FSTS_FRI_MASK) -#define VTD_FSTS_IQE (1UL << 4) -#define VTD_FSTS_PPF (1UL << 1) -#define VTD_FSTS_PFO 1UL - -/* FECTL_REG */ -#define VTD_FECTL_IM (1UL << 31) -#define VTD_FECTL_IP (1UL << 30) - -/* Fault Recording Register */ -/* For the high 64-bit of 128-bit */ -#define VTD_FRCD_F (1ULL << 63) -#define VTD_FRCD_T (1ULL << 62) -#define VTD_FRCD_FR(val) (((val) & 0xffULL) << 32) -#define VTD_FRCD_SID_MASK 0xffffULL -#define VTD_FRCD_SID(val) ((val) & VTD_FRCD_SID_MASK) -/* For the low 64-bit of 128-bit */ -#define VTD_FRCD_FI(val) ((val) & (((1ULL << VTD_MGAW) - 1) ^ 0xfffULL)) - -/* DMA Remapping Fault Conditions */ -typedef enum VTDFaultReason { - VTD_FR_RESERVED = 0, /* Reserved for Advanced Fault logging */ - VTD_FR_ROOT_ENTRY_P = 1, /* The Present(P) field of root-entry is 0 */ - VTD_FR_CONTEXT_ENTRY_P, /* The Present(P) field of context-entry is 0 */ - VTD_FR_CONTEXT_ENTRY_INV, /* Invalid programming of a context-entry */ - VTD_FR_ADDR_BEYOND_MGAW, /* Input-address above (2^x-1) */ - VTD_FR_WRITE, /* No write permission */ - VTD_FR_READ, /* No read permission */ - /* Fail to access a second-level paging entry (not SL_PML4E) */ - VTD_FR_PAGING_ENTRY_INV, - VTD_FR_ROOT_TABLE_INV, /* Fail to access a root-entry */ - VTD_FR_CONTEXT_TABLE_INV, /* Fail to access a context-entry */ - /* Non-zero reserved field in a present root-entry */ - VTD_FR_ROOT_ENTRY_RSVD, - /* Non-zero reserved field in a present context-entry */ - VTD_FR_CONTEXT_ENTRY_RSVD, - /* Non-zero reserved field in a second-level paging entry with at lease one - * Read(R) and Write(W) or Execute(E) field is Set. - */ - VTD_FR_PAGING_ENTRY_RSVD, - /* Translation request or translated request explicitly blocked dut to the - * programming of the Translation Type (T) field in the present - * context-entry. - */ - VTD_FR_CONTEXT_ENTRY_TT, - /* This is not a normal fault reason. We use this to indicate some faults - * that are not referenced by the VT-d specification. - * Fault event with such reason should not be recorded. - */ - VTD_FR_RESERVED_ERR, - VTD_FR_MAX, /* Guard */ -} VTDFaultReason; - -#define VTD_CONTEXT_CACHE_GEN_MAX 0xffffffffUL - -/* Queued Invalidation Descriptor */ -struct VTDInvDesc { - uint64_t lo; - uint64_t hi; -}; -typedef struct VTDInvDesc VTDInvDesc; - -/* Masks for struct VTDInvDesc */ -#define VTD_INV_DESC_TYPE 0xf -#define VTD_INV_DESC_CC 0x1 /* Context-cache Invalidate Desc */ -#define VTD_INV_DESC_IOTLB 0x2 -#define VTD_INV_DESC_WAIT 0x5 /* Invalidation Wait Descriptor */ -#define VTD_INV_DESC_NONE 0 /* Not an Invalidate Descriptor */ - -/* Masks for Invalidation Wait Descriptor*/ -#define VTD_INV_DESC_WAIT_SW (1ULL << 5) -#define VTD_INV_DESC_WAIT_IF (1ULL << 4) -#define VTD_INV_DESC_WAIT_FN (1ULL << 6) -#define VTD_INV_DESC_WAIT_DATA_SHIFT 32 -#define VTD_INV_DESC_WAIT_RSVD_LO 0Xffffff80ULL -#define VTD_INV_DESC_WAIT_RSVD_HI 3ULL - -/* Masks for Context-cache Invalidation Descriptor */ -#define VTD_INV_DESC_CC_G (3ULL << 4) -#define VTD_INV_DESC_CC_GLOBAL (1ULL << 4) -#define VTD_INV_DESC_CC_DOMAIN (2ULL << 4) -#define VTD_INV_DESC_CC_DEVICE (3ULL << 4) -#define VTD_INV_DESC_CC_DID(val) (((val) >> 16) & VTD_DOMAIN_ID_MASK) -#define VTD_INV_DESC_CC_SID(val) (((val) >> 32) & 0xffffUL) -#define VTD_INV_DESC_CC_FM(val) (((val) >> 48) & 3UL) -#define VTD_INV_DESC_CC_RSVD 0xfffc00000000ffc0ULL - -/* Masks for IOTLB Invalidate Descriptor */ -#define VTD_INV_DESC_IOTLB_G (3ULL << 4) -#define VTD_INV_DESC_IOTLB_GLOBAL (1ULL << 4) -#define VTD_INV_DESC_IOTLB_DOMAIN (2ULL << 4) -#define VTD_INV_DESC_IOTLB_PAGE (3ULL << 4) -#define VTD_INV_DESC_IOTLB_DID(val) (((val) >> 16) & VTD_DOMAIN_ID_MASK) -#define VTD_INV_DESC_IOTLB_ADDR(val) ((val) & ~0xfffULL & \ - ((1ULL << VTD_MGAW) - 1)) -#define VTD_INV_DESC_IOTLB_AM(val) ((val) & 0x3fULL) -#define VTD_INV_DESC_IOTLB_RSVD_LO 0xffffffff0000ff00ULL -#define VTD_INV_DESC_IOTLB_RSVD_HI 0xf80ULL - -/* Information about page-selective IOTLB invalidate */ -struct VTDIOTLBPageInvInfo { - uint16_t domain_id; - uint64_t addr; - uint8_t mask; -}; -typedef struct VTDIOTLBPageInvInfo VTDIOTLBPageInvInfo; - -/* Pagesize of VTD paging structures, including root and context tables */ -#define VTD_PAGE_SHIFT 12 -#define VTD_PAGE_SIZE (1ULL << VTD_PAGE_SHIFT) - -#define VTD_PAGE_SHIFT_4K 12 -#define VTD_PAGE_MASK_4K (~((1ULL << VTD_PAGE_SHIFT_4K) - 1)) -#define VTD_PAGE_SHIFT_2M 21 -#define VTD_PAGE_MASK_2M (~((1ULL << VTD_PAGE_SHIFT_2M) - 1)) -#define VTD_PAGE_SHIFT_1G 30 -#define VTD_PAGE_MASK_1G (~((1ULL << VTD_PAGE_SHIFT_1G) - 1)) - -struct VTDRootEntry { - uint64_t val; - uint64_t rsvd; -}; -typedef struct VTDRootEntry VTDRootEntry; - -/* Masks for struct VTDRootEntry */ -#define VTD_ROOT_ENTRY_P 1ULL -#define VTD_ROOT_ENTRY_CTP (~0xfffULL) - -#define VTD_ROOT_ENTRY_NR (VTD_PAGE_SIZE / sizeof(VTDRootEntry)) -#define VTD_ROOT_ENTRY_RSVD (0xffeULL | ~VTD_HAW_MASK) - -/* Masks for struct VTDContextEntry */ -/* lo */ -#define VTD_CONTEXT_ENTRY_P (1ULL << 0) -#define VTD_CONTEXT_ENTRY_FPD (1ULL << 1) /* Fault Processing Disable */ -#define VTD_CONTEXT_ENTRY_TT (3ULL << 2) /* Translation Type */ -#define VTD_CONTEXT_TT_MULTI_LEVEL 0 -#define VTD_CONTEXT_TT_DEV_IOTLB 1 -#define VTD_CONTEXT_TT_PASS_THROUGH 2 -/* Second Level Page Translation Pointer*/ -#define VTD_CONTEXT_ENTRY_SLPTPTR (~0xfffULL) -#define VTD_CONTEXT_ENTRY_RSVD_LO (0xff0ULL | ~VTD_HAW_MASK) -/* hi */ -#define VTD_CONTEXT_ENTRY_AW 7ULL /* Adjusted guest-address-width */ -#define VTD_CONTEXT_ENTRY_DID(val) (((val) >> 8) & VTD_DOMAIN_ID_MASK) -#define VTD_CONTEXT_ENTRY_RSVD_HI 0xffffffffff000080ULL - -#define VTD_CONTEXT_ENTRY_NR (VTD_PAGE_SIZE / sizeof(VTDContextEntry)) - -/* Paging Structure common */ -#define VTD_SL_PT_PAGE_SIZE_MASK (1ULL << 7) -/* Bits to decide the offset for each level */ -#define VTD_SL_LEVEL_BITS 9 - -/* Second Level Paging Structure */ -#define VTD_SL_PML4_LEVEL 4 -#define VTD_SL_PDP_LEVEL 3 -#define VTD_SL_PD_LEVEL 2 -#define VTD_SL_PT_LEVEL 1 -#define VTD_SL_PT_ENTRY_NR 512 - -/* Masks for Second Level Paging Entry */ -#define VTD_SL_RW_MASK 3ULL -#define VTD_SL_R 1ULL -#define VTD_SL_W (1ULL << 1) -#define VTD_SL_PT_BASE_ADDR_MASK (~(VTD_PAGE_SIZE - 1) & VTD_HAW_MASK) -#define VTD_SL_IGN_COM 0xbff0000000000000ULL - -#endif diff --git a/qemu/hw/i386/kvm/Makefile.objs b/qemu/hw/i386/kvm/Makefile.objs deleted file mode 100644 index d8bce209b..000000000 --- a/qemu/hw/i386/kvm/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -obj-y += clock.o apic.o i8259.o ioapic.o i8254.o pci-assign.o diff --git a/qemu/hw/i386/kvm/apic.c b/qemu/hw/i386/kvm/apic.c deleted file mode 100644 index 3c7c8fa00..000000000 --- a/qemu/hw/i386/kvm/apic.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * KVM in-kernel APIC support - * - * Copyright (c) 2011 Siemens AG - * - * Authors: - * Jan Kiszka - * - * 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" - -static inline void kvm_apic_set_reg(struct kvm_lapic_state *kapic, - int reg_id, uint32_t val) -{ - *((uint32_t *)(kapic->regs + (reg_id << 4))) = val; -} - -static inline uint32_t kvm_apic_get_reg(struct kvm_lapic_state *kapic, - int reg_id) -{ - return *((uint32_t *)(kapic->regs + (reg_id << 4))); -} - -void kvm_put_apic_state(DeviceState *dev, struct kvm_lapic_state *kapic) -{ - APICCommonState *s = APIC_COMMON(dev); - int i; - - memset(kapic, 0, sizeof(*kapic)); - kvm_apic_set_reg(kapic, 0x2, s->id << 24); - kvm_apic_set_reg(kapic, 0x8, s->tpr); - kvm_apic_set_reg(kapic, 0xd, s->log_dest << 24); - kvm_apic_set_reg(kapic, 0xe, s->dest_mode << 28 | 0x0fffffff); - kvm_apic_set_reg(kapic, 0xf, s->spurious_vec); - for (i = 0; i < 8; i++) { - kvm_apic_set_reg(kapic, 0x10 + i, s->isr[i]); - kvm_apic_set_reg(kapic, 0x18 + i, s->tmr[i]); - kvm_apic_set_reg(kapic, 0x20 + i, s->irr[i]); - } - kvm_apic_set_reg(kapic, 0x28, s->esr); - kvm_apic_set_reg(kapic, 0x30, s->icr[0]); - kvm_apic_set_reg(kapic, 0x31, s->icr[1]); - for (i = 0; i < APIC_LVT_NB; i++) { - kvm_apic_set_reg(kapic, 0x32 + i, s->lvt[i]); - } - kvm_apic_set_reg(kapic, 0x38, s->initial_count); - kvm_apic_set_reg(kapic, 0x3e, s->divide_conf); -} - -void kvm_get_apic_state(DeviceState *dev, struct kvm_lapic_state *kapic) -{ - APICCommonState *s = APIC_COMMON(dev); - int i, v; - - s->id = kvm_apic_get_reg(kapic, 0x2) >> 24; - s->tpr = kvm_apic_get_reg(kapic, 0x8); - s->arb_id = kvm_apic_get_reg(kapic, 0x9); - s->log_dest = kvm_apic_get_reg(kapic, 0xd) >> 24; - s->dest_mode = kvm_apic_get_reg(kapic, 0xe) >> 28; - s->spurious_vec = kvm_apic_get_reg(kapic, 0xf); - for (i = 0; i < 8; i++) { - s->isr[i] = kvm_apic_get_reg(kapic, 0x10 + i); - s->tmr[i] = kvm_apic_get_reg(kapic, 0x18 + i); - s->irr[i] = kvm_apic_get_reg(kapic, 0x20 + i); - } - s->esr = kvm_apic_get_reg(kapic, 0x28); - s->icr[0] = kvm_apic_get_reg(kapic, 0x30); - s->icr[1] = kvm_apic_get_reg(kapic, 0x31); - for (i = 0; i < APIC_LVT_NB; i++) { - s->lvt[i] = kvm_apic_get_reg(kapic, 0x32 + i); - } - s->initial_count = kvm_apic_get_reg(kapic, 0x38); - s->divide_conf = kvm_apic_get_reg(kapic, 0x3e); - - v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4); - s->count_shift = (v + 1) & 7; - - s->initial_count_load_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - apic_next_timer(s, s->initial_count_load_time); -} - -static void kvm_apic_set_base(APICCommonState *s, uint64_t val) -{ - s->apicbase = val; -} - -static void kvm_apic_set_tpr(APICCommonState *s, uint8_t val) -{ - s->tpr = (val & 0x0f) << 4; -} - -static uint8_t kvm_apic_get_tpr(APICCommonState *s) -{ - return s->tpr >> 4; -} - -static void kvm_apic_enable_tpr_reporting(APICCommonState *s, bool enable) -{ - struct kvm_tpr_access_ctl ctl = { - .enabled = enable - }; - - kvm_vcpu_ioctl(CPU(s->cpu), KVM_TPR_ACCESS_REPORTING, &ctl); -} - -static void kvm_apic_vapic_base_update(APICCommonState *s) -{ - struct kvm_vapic_addr vapid_addr = { - .vapic_addr = s->vapic_paddr, - }; - int ret; - - ret = kvm_vcpu_ioctl(CPU(s->cpu), KVM_SET_VAPIC_ADDR, &vapid_addr); - if (ret < 0) { - fprintf(stderr, "KVM: setting VAPIC address failed (%s)\n", - strerror(-ret)); - abort(); - } -} - -static void do_inject_external_nmi(void *data) -{ - APICCommonState *s = data; - CPUState *cpu = CPU(s->cpu); - uint32_t lvt; - int ret; - - cpu_synchronize_state(cpu); - - lvt = s->lvt[APIC_LVT_LINT1]; - if (!(lvt & APIC_LVT_MASKED) && ((lvt >> 8) & 7) == APIC_DM_NMI) { - ret = kvm_vcpu_ioctl(cpu, KVM_NMI); - if (ret < 0) { - fprintf(stderr, "KVM: injection failed, NMI lost (%s)\n", - strerror(-ret)); - } - } -} - -static void kvm_apic_external_nmi(APICCommonState *s) -{ - run_on_cpu(CPU(s->cpu), do_inject_external_nmi, s); -} - -static uint64_t kvm_apic_mem_read(void *opaque, hwaddr addr, - unsigned size) -{ - return ~(uint64_t)0; -} - -static void kvm_apic_mem_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - MSIMessage msg = { .address = addr, .data = data }; - int ret; - - ret = kvm_irqchip_send_msi(kvm_state, msg); - if (ret < 0) { - fprintf(stderr, "KVM: injection failed, MSI lost (%s)\n", - strerror(-ret)); - } -} - -static const MemoryRegionOps kvm_apic_io_ops = { - .read = kvm_apic_mem_read, - .write = kvm_apic_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void kvm_apic_reset(APICCommonState *s) -{ - /* Not used by KVM, which uses the CPU mp_state instead. */ - s->wait_for_sipi = 0; -} - -static void kvm_apic_realize(DeviceState *dev, Error **errp) -{ - APICCommonState *s = APIC_COMMON(dev); - - memory_region_init_io(&s->io_memory, NULL, &kvm_apic_io_ops, s, "kvm-apic-msi", - APIC_SPACE_SIZE); - - if (kvm_has_gsi_routing()) { - msi_nonbroken = true; - } -} - -static void kvm_apic_class_init(ObjectClass *klass, void *data) -{ - APICCommonClass *k = APIC_COMMON_CLASS(klass); - - k->realize = kvm_apic_realize; - k->reset = kvm_apic_reset; - k->set_base = kvm_apic_set_base; - k->set_tpr = kvm_apic_set_tpr; - k->get_tpr = kvm_apic_get_tpr; - k->enable_tpr_reporting = kvm_apic_enable_tpr_reporting; - k->vapic_base_update = kvm_apic_vapic_base_update; - k->external_nmi = kvm_apic_external_nmi; -} - -static const TypeInfo kvm_apic_info = { - .name = "kvm-apic", - .parent = TYPE_APIC_COMMON, - .instance_size = sizeof(APICCommonState), - .class_init = kvm_apic_class_init, -}; - -static void kvm_apic_register_types(void) -{ - type_register_static(&kvm_apic_info); -} - -type_init(kvm_apic_register_types) diff --git a/qemu/hw/i386/kvm/clock.c b/qemu/hw/i386/kvm/clock.c deleted file mode 100644 index a3b300cad..000000000 --- a/qemu/hw/i386/kvm/clock.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * QEMU KVM support, paravirtual clock device - * - * Copyright (C) 2011 Siemens AG - * - * Authors: - * Jan Kiszka - * - * This work is licensed under the terms of the GNU GPL version 2. - * See the COPYING file in the top-level directory. - * - * 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 "qemu-common.h" -#include "qemu/host-utils.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" -#include "kvm_i386.h" -#include "hw/sysbus.h" -#include "hw/kvm/clock.h" - -#include -#include - -#define TYPE_KVM_CLOCK "kvmclock" -#define KVM_CLOCK(obj) OBJECT_CHECK(KVMClockState, (obj), TYPE_KVM_CLOCK) - -typedef struct KVMClockState { - /*< private >*/ - SysBusDevice busdev; - /*< public >*/ - - uint64_t clock; - bool clock_valid; -} KVMClockState; - -struct pvclock_vcpu_time_info { - uint32_t version; - uint32_t pad0; - uint64_t tsc_timestamp; - uint64_t system_time; - uint32_t tsc_to_system_mul; - int8_t tsc_shift; - uint8_t flags; - uint8_t pad[2]; -} __attribute__((__packed__)); /* 32 bytes */ - -static uint64_t kvmclock_current_nsec(KVMClockState *s) -{ - CPUState *cpu = first_cpu; - CPUX86State *env = cpu->env_ptr; - hwaddr kvmclock_struct_pa = env->system_time_msr & ~1ULL; - uint64_t migration_tsc = env->tsc; - struct pvclock_vcpu_time_info time; - uint64_t delta; - uint64_t nsec_lo; - uint64_t nsec_hi; - uint64_t nsec; - - if (!(env->system_time_msr & 1ULL)) { - /* KVM clock not active */ - return 0; - } - - cpu_physical_memory_read(kvmclock_struct_pa, &time, sizeof(time)); - - assert(time.tsc_timestamp <= migration_tsc); - delta = migration_tsc - time.tsc_timestamp; - if (time.tsc_shift < 0) { - delta >>= -time.tsc_shift; - } else { - delta <<= time.tsc_shift; - } - - mulu64(&nsec_lo, &nsec_hi, delta, time.tsc_to_system_mul); - nsec = (nsec_lo >> 32) | (nsec_hi << 32); - return nsec + time.system_time; -} - -static void kvmclock_vm_state_change(void *opaque, int running, - RunState state) -{ - KVMClockState *s = opaque; - CPUState *cpu; - int cap_clock_ctrl = kvm_check_extension(kvm_state, KVM_CAP_KVMCLOCK_CTRL); - int ret; - - if (running) { - struct kvm_clock_data data = {}; - uint64_t time_at_migration = kvmclock_current_nsec(s); - - s->clock_valid = false; - - /* We can't rely on the migrated clock value, just discard it */ - if (time_at_migration) { - s->clock = time_at_migration; - } - - data.clock = s->clock; - ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data); - if (ret < 0) { - fprintf(stderr, "KVM_SET_CLOCK failed: %s\n", strerror(ret)); - abort(); - } - - if (!cap_clock_ctrl) { - return; - } - CPU_FOREACH(cpu) { - ret = kvm_vcpu_ioctl(cpu, KVM_KVMCLOCK_CTRL, 0); - if (ret) { - if (ret != -EINVAL) { - fprintf(stderr, "%s: %s\n", __func__, strerror(-ret)); - } - return; - } - } - } else { - struct kvm_clock_data data; - int ret; - - if (s->clock_valid) { - return; - } - - kvm_synchronize_all_tsc(); - - ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data); - if (ret < 0) { - fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret)); - abort(); - } - s->clock = data.clock; - - /* - * If the VM is stopped, declare the clock state valid to - * avoid re-reading it on next vmsave (which would return - * a different value). Will be reset when the VM is continued. - */ - s->clock_valid = true; - } -} - -static void kvmclock_realize(DeviceState *dev, Error **errp) -{ - KVMClockState *s = KVM_CLOCK(dev); - - qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s); -} - -static const VMStateDescription kvmclock_vmsd = { - .name = "kvmclock", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT64(clock, KVMClockState), - VMSTATE_END_OF_LIST() - } -}; - -static void kvmclock_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = kvmclock_realize; - dc->vmsd = &kvmclock_vmsd; -} - -static const TypeInfo kvmclock_info = { - .name = TYPE_KVM_CLOCK, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(KVMClockState), - .class_init = kvmclock_class_init, -}; - -/* Note: Must be called after VCPU initialization. */ -void kvmclock_create(void) -{ - X86CPU *cpu = X86_CPU(first_cpu); - - if (kvm_enabled() && - cpu->env.features[FEAT_KVM] & ((1ULL << KVM_FEATURE_CLOCKSOURCE) | - (1ULL << KVM_FEATURE_CLOCKSOURCE2))) { - sysbus_create_simple(TYPE_KVM_CLOCK, -1, NULL); - } -} - -static void kvmclock_register_types(void) -{ - type_register_static(&kvmclock_info); -} - -type_init(kvmclock_register_types) diff --git a/qemu/hw/i386/kvm/i8254.c b/qemu/hw/i386/kvm/i8254.c deleted file mode 100644 index a4462e5ca..000000000 --- a/qemu/hw/i386/kvm/i8254.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * KVM in-kernel PIT (i8254) support - * - * Copyright (c) 2003-2004 Fabrice Bellard - * Copyright (c) 2012 Jan Kiszka, Siemens AG - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "hw/timer/i8254.h" -#include "hw/timer/i8254_internal.h" -#include "sysemu/kvm.h" - -#define KVM_PIT_REINJECT_BIT 0 - -#define CALIBRATION_ROUNDS 3 - -#define KVM_PIT(obj) OBJECT_CHECK(KVMPITState, (obj), TYPE_KVM_I8254) -#define KVM_PIT_CLASS(class) \ - OBJECT_CLASS_CHECK(KVMPITClass, (class), TYPE_KVM_I8254) -#define KVM_PIT_GET_CLASS(obj) \ - OBJECT_GET_CLASS(KVMPITClass, (obj), TYPE_KVM_I8254) - -typedef struct KVMPITState { - PITCommonState parent_obj; - - LostTickPolicy lost_tick_policy; - bool vm_stopped; - int64_t kernel_clock_offset; -} KVMPITState; - -typedef struct KVMPITClass { - PITCommonClass parent_class; - - DeviceRealize parent_realize; -} KVMPITClass; - -static int64_t abs64(int64_t v) -{ - return v < 0 ? -v : v; -} - -static void kvm_pit_update_clock_offset(KVMPITState *s) -{ - int64_t offset, clock_offset; - struct timespec ts; - int i; - - /* - * Measure the delta between CLOCK_MONOTONIC, the base used for - * kvm_pit_channel_state::count_load_time, and QEMU_CLOCK_VIRTUAL. Take the - * minimum of several samples to filter out scheduling noise. - */ - clock_offset = INT64_MAX; - for (i = 0; i < CALIBRATION_ROUNDS; i++) { - offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - clock_gettime(CLOCK_MONOTONIC, &ts); - offset -= ts.tv_nsec; - offset -= (int64_t)ts.tv_sec * 1000000000; - if (abs64(offset) < abs64(clock_offset)) { - clock_offset = offset; - } - } - s->kernel_clock_offset = clock_offset; -} - -static void kvm_pit_get(PITCommonState *pit) -{ - KVMPITState *s = KVM_PIT(pit); - struct kvm_pit_state2 kpit; - struct kvm_pit_channel_state *kchan; - struct PITChannelState *sc; - int i, ret; - - /* No need to re-read the state if VM is stopped. */ - if (s->vm_stopped) { - return; - } - - if (kvm_has_pit_state2()) { - ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, &kpit); - if (ret < 0) { - fprintf(stderr, "KVM_GET_PIT2 failed: %s\n", strerror(ret)); - abort(); - } - pit->channels[0].irq_disabled = kpit.flags & KVM_PIT_FLAGS_HPET_LEGACY; - } else { - /* - * kvm_pit_state2 is superset of kvm_pit_state struct, - * so we can use it for KVM_GET_PIT as well. - */ - ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT, &kpit); - if (ret < 0) { - fprintf(stderr, "KVM_GET_PIT failed: %s\n", strerror(ret)); - abort(); - } - } - for (i = 0; i < 3; i++) { - kchan = &kpit.channels[i]; - sc = &pit->channels[i]; - sc->count = kchan->count; - sc->latched_count = kchan->latched_count; - sc->count_latched = kchan->count_latched; - sc->status_latched = kchan->status_latched; - sc->status = kchan->status; - sc->read_state = kchan->read_state; - sc->write_state = kchan->write_state; - sc->write_latch = kchan->write_latch; - sc->rw_mode = kchan->rw_mode; - sc->mode = kchan->mode; - sc->bcd = kchan->bcd; - sc->gate = kchan->gate; - sc->count_load_time = kchan->count_load_time + s->kernel_clock_offset; - } - - sc = &pit->channels[0]; - sc->next_transition_time = - pit_get_next_transition_time(sc, sc->count_load_time); -} - -static void kvm_pit_put(PITCommonState *pit) -{ - KVMPITState *s = KVM_PIT(pit); - struct kvm_pit_state2 kpit = {}; - struct kvm_pit_channel_state *kchan; - struct PITChannelState *sc; - int i, ret; - - /* The offset keeps changing as long as the VM is stopped. */ - if (s->vm_stopped) { - kvm_pit_update_clock_offset(s); - } - - kpit.flags = pit->channels[0].irq_disabled ? KVM_PIT_FLAGS_HPET_LEGACY : 0; - for (i = 0; i < 3; i++) { - kchan = &kpit.channels[i]; - sc = &pit->channels[i]; - kchan->count = sc->count; - kchan->latched_count = sc->latched_count; - kchan->count_latched = sc->count_latched; - kchan->status_latched = sc->status_latched; - kchan->status = sc->status; - kchan->read_state = sc->read_state; - kchan->write_state = sc->write_state; - kchan->write_latch = sc->write_latch; - kchan->rw_mode = sc->rw_mode; - kchan->mode = sc->mode; - kchan->bcd = sc->bcd; - kchan->gate = sc->gate; - kchan->count_load_time = sc->count_load_time - s->kernel_clock_offset; - } - - ret = kvm_vm_ioctl(kvm_state, - kvm_has_pit_state2() ? KVM_SET_PIT2 : KVM_SET_PIT, - &kpit); - if (ret < 0) { - fprintf(stderr, "%s failed: %s\n", - kvm_has_pit_state2() ? "KVM_SET_PIT2" : "KVM_SET_PIT", - strerror(ret)); - abort(); - } -} - -static void kvm_pit_set_gate(PITCommonState *s, PITChannelState *sc, int val) -{ - kvm_pit_get(s); - - switch (sc->mode) { - default: - case 0: - case 4: - /* XXX: just disable/enable counting */ - break; - case 1: - case 2: - case 3: - case 5: - if (sc->gate < val) { - /* restart counting on rising edge */ - sc->count_load_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - } - break; - } - sc->gate = val; - - kvm_pit_put(s); -} - -static void kvm_pit_get_channel_info(PITCommonState *s, PITChannelState *sc, - PITChannelInfo *info) -{ - kvm_pit_get(s); - - pit_get_channel_info_common(s, sc, info); -} - -static void kvm_pit_reset(DeviceState *dev) -{ - PITCommonState *s = PIT_COMMON(dev); - - pit_reset_common(s); - - kvm_pit_put(s); -} - -static void kvm_pit_irq_control(void *opaque, int n, int enable) -{ - PITCommonState *pit = opaque; - PITChannelState *s = &pit->channels[0]; - - kvm_pit_get(pit); - - s->irq_disabled = !enable; - - kvm_pit_put(pit); -} - -static void kvm_pit_vm_state_change(void *opaque, int running, - RunState state) -{ - KVMPITState *s = opaque; - - if (running) { - kvm_pit_update_clock_offset(s); - kvm_pit_put(PIT_COMMON(s)); - s->vm_stopped = false; - } else { - kvm_pit_update_clock_offset(s); - kvm_pit_get(PIT_COMMON(s)); - s->vm_stopped = true; - } -} - -static void kvm_pit_realizefn(DeviceState *dev, Error **errp) -{ - PITCommonState *pit = PIT_COMMON(dev); - KVMPITClass *kpc = KVM_PIT_GET_CLASS(dev); - KVMPITState *s = KVM_PIT(pit); - struct kvm_pit_config config = { - .flags = 0, - }; - int ret; - - if (kvm_check_extension(kvm_state, KVM_CAP_PIT2)) { - ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT2, &config); - } else { - ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT); - } - if (ret < 0) { - error_setg(errp, "Create kernel PIC irqchip failed: %s", - strerror(ret)); - return; - } - switch (s->lost_tick_policy) { - case LOST_TICK_POLICY_DELAY: - break; /* enabled by default */ - case LOST_TICK_POLICY_DISCARD: - if (kvm_check_extension(kvm_state, KVM_CAP_REINJECT_CONTROL)) { - struct kvm_reinject_control control = { .pit_reinject = 0 }; - - ret = kvm_vm_ioctl(kvm_state, KVM_REINJECT_CONTROL, &control); - if (ret < 0) { - error_setg(errp, - "Can't disable in-kernel PIT reinjection: %s", - strerror(ret)); - return; - } - } - break; - default: - error_setg(errp, "Lost tick policy not supported."); - return; - } - - memory_region_init_reservation(&pit->ioports, NULL, "kvm-pit", 4); - - qdev_init_gpio_in(dev, kvm_pit_irq_control, 1); - - qemu_add_vm_change_state_handler(kvm_pit_vm_state_change, s); - - kpc->parent_realize(dev, errp); -} - -static Property kvm_pit_properties[] = { - DEFINE_PROP_UINT32("iobase", PITCommonState, iobase, -1), - DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", KVMPITState, - lost_tick_policy, LOST_TICK_POLICY_DELAY), - DEFINE_PROP_END_OF_LIST(), -}; - -static void kvm_pit_class_init(ObjectClass *klass, void *data) -{ - KVMPITClass *kpc = KVM_PIT_CLASS(klass); - PITCommonClass *k = PIT_COMMON_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - kpc->parent_realize = dc->realize; - dc->realize = kvm_pit_realizefn; - k->set_channel_gate = kvm_pit_set_gate; - k->get_channel_info = kvm_pit_get_channel_info; - dc->reset = kvm_pit_reset; - dc->props = kvm_pit_properties; -} - -static const TypeInfo kvm_pit_info = { - .name = TYPE_KVM_I8254, - .parent = TYPE_PIT_COMMON, - .instance_size = sizeof(KVMPITState), - .class_init = kvm_pit_class_init, - .class_size = sizeof(KVMPITClass), -}; - -static void kvm_pit_register(void) -{ - type_register_static(&kvm_pit_info); -} - -type_init(kvm_pit_register) diff --git a/qemu/hw/i386/kvm/i8259.c b/qemu/hw/i386/kvm/i8259.c deleted file mode 100644 index 2b207de01..000000000 --- a/qemu/hw/i386/kvm/i8259.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * KVM in-kernel PIC (i8259) support - * - * Copyright (c) 2011 Siemens AG - * - * Authors: - * Jan Kiszka - * - * 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" - -#define TYPE_KVM_I8259 "kvm-i8259" -#define KVM_PIC_CLASS(class) \ - OBJECT_CLASS_CHECK(KVMPICClass, (class), TYPE_KVM_I8259) -#define KVM_PIC_GET_CLASS(obj) \ - OBJECT_GET_CLASS(KVMPICClass, (obj), TYPE_KVM_I8259) - -/** - * KVMPICClass: - * @parent_realize: The parent's realizefn. - */ -typedef struct KVMPICClass { - PICCommonClass parent_class; - - DeviceRealize parent_realize; -} KVMPICClass; - -static void kvm_pic_get(PICCommonState *s) -{ - struct kvm_irqchip chip; - struct kvm_pic_state *kpic; - int ret; - - chip.chip_id = s->master ? KVM_IRQCHIP_PIC_MASTER : KVM_IRQCHIP_PIC_SLAVE; - ret = kvm_vm_ioctl(kvm_state, KVM_GET_IRQCHIP, &chip); - if (ret < 0) { - fprintf(stderr, "KVM_GET_IRQCHIP failed: %s\n", strerror(ret)); - abort(); - } - - kpic = &chip.chip.pic; - - s->last_irr = kpic->last_irr; - s->irr = kpic->irr; - s->imr = kpic->imr; - s->isr = kpic->isr; - s->priority_add = kpic->priority_add; - s->irq_base = kpic->irq_base; - s->read_reg_select = kpic->read_reg_select; - s->poll = kpic->poll; - s->special_mask = kpic->special_mask; - s->init_state = kpic->init_state; - s->auto_eoi = kpic->auto_eoi; - s->rotate_on_auto_eoi = kpic->rotate_on_auto_eoi; - s->special_fully_nested_mode = kpic->special_fully_nested_mode; - s->init4 = kpic->init4; - s->elcr = kpic->elcr; - s->elcr_mask = kpic->elcr_mask; -} - -static void kvm_pic_put(PICCommonState *s) -{ - struct kvm_irqchip chip; - struct kvm_pic_state *kpic; - int ret; - - chip.chip_id = s->master ? KVM_IRQCHIP_PIC_MASTER : KVM_IRQCHIP_PIC_SLAVE; - - kpic = &chip.chip.pic; - - kpic->last_irr = s->last_irr; - kpic->irr = s->irr; - kpic->imr = s->imr; - kpic->isr = s->isr; - kpic->priority_add = s->priority_add; - kpic->irq_base = s->irq_base; - kpic->read_reg_select = s->read_reg_select; - kpic->poll = s->poll; - kpic->special_mask = s->special_mask; - kpic->init_state = s->init_state; - kpic->auto_eoi = s->auto_eoi; - kpic->rotate_on_auto_eoi = s->rotate_on_auto_eoi; - kpic->special_fully_nested_mode = s->special_fully_nested_mode; - kpic->init4 = s->init4; - kpic->elcr = s->elcr; - kpic->elcr_mask = s->elcr_mask; - - ret = kvm_vm_ioctl(kvm_state, KVM_SET_IRQCHIP, &chip); - if (ret < 0) { - fprintf(stderr, "KVM_GET_IRQCHIP failed: %s\n", strerror(ret)); - abort(); - } -} - -static void kvm_pic_reset(DeviceState *dev) -{ - PICCommonState *s = PIC_COMMON(dev); - - s->elcr = 0; - pic_reset_common(s); - - kvm_pic_put(s); -} - -static void kvm_pic_set_irq(void *opaque, int irq, int level) -{ - int delivered; - - delivered = kvm_set_irq(kvm_state, irq, level); - apic_report_irq_delivered(delivered); -} - -static void kvm_pic_realize(DeviceState *dev, Error **errp) -{ - PICCommonState *s = PIC_COMMON(dev); - KVMPICClass *kpc = KVM_PIC_GET_CLASS(dev); - - memory_region_init_reservation(&s->base_io, NULL, "kvm-pic", 2); - memory_region_init_reservation(&s->elcr_io, NULL, "kvm-elcr", 1); - - kpc->parent_realize(dev, errp); -} - -qemu_irq *kvm_i8259_init(ISABus *bus) -{ - i8259_init_chip(TYPE_KVM_I8259, bus, true); - i8259_init_chip(TYPE_KVM_I8259, bus, false); - - return qemu_allocate_irqs(kvm_pic_set_irq, NULL, ISA_NUM_IRQS); -} - -static void kvm_i8259_class_init(ObjectClass *klass, void *data) -{ - KVMPICClass *kpc = KVM_PIC_CLASS(klass); - PICCommonClass *k = PIC_COMMON_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = kvm_pic_reset; - kpc->parent_realize = dc->realize; - dc->realize = kvm_pic_realize; - k->pre_save = kvm_pic_get; - k->post_load = kvm_pic_put; -} - -static const TypeInfo kvm_i8259_info = { - .name = TYPE_KVM_I8259, - .parent = TYPE_PIC_COMMON, - .instance_size = sizeof(PICCommonState), - .class_init = kvm_i8259_class_init, - .class_size = sizeof(KVMPICClass), -}; - -static void kvm_pic_register_types(void) -{ - type_register_static(&kvm_i8259_info); -} - -type_init(kvm_pic_register_types) diff --git a/qemu/hw/i386/kvm/ioapic.c b/qemu/hw/i386/kvm/ioapic.c deleted file mode 100644 index 8eb2c7a70..000000000 --- a/qemu/hw/i386/kvm/ioapic.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * KVM in-kernel IOPIC support - * - * Copyright (c) 2011 Siemens AG - * - * Authors: - * Jan Kiszka - * - * 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 "monitor/monitor.h" -#include "hw/i386/pc.h" -#include "hw/i386/ioapic_internal.h" -#include "hw/i386/apic_internal.h" -#include "sysemu/kvm.h" - -/* PC Utility function */ -void kvm_pc_setup_irq_routing(bool pci_enabled) -{ - KVMState *s = kvm_state; - int i; - - if (kvm_check_extension(s, KVM_CAP_IRQ_ROUTING)) { - for (i = 0; i < 8; ++i) { - if (i == 2) { - continue; - } - kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_PIC_MASTER, i); - } - for (i = 8; i < 16; ++i) { - kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_PIC_SLAVE, i - 8); - } - if (pci_enabled) { - for (i = 0; i < 24; ++i) { - if (i == 0) { - kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_IOAPIC, 2); - } else if (i != 2) { - kvm_irqchip_add_irq_route(s, i, KVM_IRQCHIP_IOAPIC, i); - } - } - } - kvm_irqchip_commit_routes(s); - } -} - -void kvm_pc_gsi_handler(void *opaque, int n, int level) -{ - GSIState *s = opaque; - - if (n < ISA_NUM_IRQS) { - /* Kernel will forward to both PIC and IOAPIC */ - qemu_set_irq(s->i8259_irq[n], level); - } else { - qemu_set_irq(s->ioapic_irq[n], level); - } -} - -typedef struct KVMIOAPICState KVMIOAPICState; - -struct KVMIOAPICState { - IOAPICCommonState ioapic; - uint32_t kvm_gsi_base; -}; - -static void kvm_ioapic_get(IOAPICCommonState *s) -{ - struct kvm_irqchip chip; - struct kvm_ioapic_state *kioapic; - int ret, i; - - chip.chip_id = KVM_IRQCHIP_IOAPIC; - ret = kvm_vm_ioctl(kvm_state, KVM_GET_IRQCHIP, &chip); - if (ret < 0) { - fprintf(stderr, "KVM_GET_IRQCHIP failed: %s\n", strerror(ret)); - abort(); - } - - kioapic = &chip.chip.ioapic; - - s->id = kioapic->id; - s->ioregsel = kioapic->ioregsel; - s->irr = kioapic->irr; - for (i = 0; i < IOAPIC_NUM_PINS; i++) { - s->ioredtbl[i] = kioapic->redirtbl[i].bits; - } -} - -static void kvm_ioapic_put(IOAPICCommonState *s) -{ - struct kvm_irqchip chip; - struct kvm_ioapic_state *kioapic; - int ret, i; - - chip.chip_id = KVM_IRQCHIP_IOAPIC; - kioapic = &chip.chip.ioapic; - - kioapic->id = s->id; - kioapic->ioregsel = s->ioregsel; - kioapic->base_address = s->busdev.mmio[0].addr; - kioapic->irr = s->irr; - for (i = 0; i < IOAPIC_NUM_PINS; i++) { - kioapic->redirtbl[i].bits = s->ioredtbl[i]; - } - - ret = kvm_vm_ioctl(kvm_state, KVM_SET_IRQCHIP, &chip); - if (ret < 0) { - fprintf(stderr, "KVM_GET_IRQCHIP failed: %s\n", strerror(ret)); - abort(); - } -} - -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); - - ioapic_reset_common(dev); - kvm_ioapic_put(s); -} - -static void kvm_ioapic_set_irq(void *opaque, int irq, int level) -{ - KVMIOAPICState *s = opaque; - int delivered; - - delivered = kvm_set_irq(kvm_state, s->kvm_gsi_base + irq, level); - apic_report_irq_delivered(delivered); -} - -static void kvm_ioapic_realize(DeviceState *dev, Error **errp) -{ - IOAPICCommonState *s = IOAPIC_COMMON(dev); - - memory_region_init_reservation(&s->io_memory, NULL, "kvm-ioapic", 0x1000); - - qdev_init_gpio_in(dev, kvm_ioapic_set_irq, IOAPIC_NUM_PINS); -} - -static Property kvm_ioapic_properties[] = { - DEFINE_PROP_UINT32("gsi_base", KVMIOAPICState, kvm_gsi_base, 0), - DEFINE_PROP_END_OF_LIST() -}; - -static void kvm_ioapic_class_init(ObjectClass *klass, void *data) -{ - IOAPICCommonClass *k = IOAPIC_COMMON_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = kvm_ioapic_realize; - k->pre_save = kvm_ioapic_get; - k->post_load = kvm_ioapic_put; - dc->reset = kvm_ioapic_reset; - dc->props = kvm_ioapic_properties; -} - -static const TypeInfo kvm_ioapic_info = { - .name = "kvm-ioapic", - .parent = TYPE_IOAPIC_COMMON, - .instance_size = sizeof(KVMIOAPICState), - .class_init = kvm_ioapic_class_init, -}; - -static void kvm_ioapic_register_types(void) -{ - type_register_static(&kvm_ioapic_info); -} - -type_init(kvm_ioapic_register_types) diff --git a/qemu/hw/i386/kvm/pci-assign.c b/qemu/hw/i386/kvm/pci-assign.c deleted file mode 100644 index bf425a2b9..000000000 --- a/qemu/hw/i386/kvm/pci-assign.c +++ /dev/null @@ -1,1898 +0,0 @@ -/* - * Copyright (c) 2007, Neocleus Corporation. - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * - * Assign a PCI device from the host to a guest VM. - * - * This implementation uses the classic device assignment interface of KVM - * and is only available on x86 hosts. It is expected to be obsoleted by VFIO - * based device assignment. - * - * Adapted for KVM (qemu-kvm) by Qumranet. QEMU version was based on qemu-kvm - * revision 4144fe9d48. See its repository for the history. - * - * Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com) - * Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com) - * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com) - * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com) - * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com) - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include -#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/msi.h" -#include "kvm_i386.h" -#include "hw/pci/pci-assign.h" - -#define MSIX_PAGE_SIZE 0x1000 - -/* From linux/ioport.h */ -#define IORESOURCE_IO 0x00000100 /* Resource type */ -#define IORESOURCE_MEM 0x00000200 -#define IORESOURCE_IRQ 0x00000400 -#define IORESOURCE_DMA 0x00000800 -#define IORESOURCE_PREFETCH 0x00002000 /* No side effects */ -#define IORESOURCE_MEM_64 0x00100000 - -typedef struct PCIRegion { - int type; /* Memory or port I/O */ - int valid; - uint64_t base_addr; - uint64_t size; /* size of the region */ - int resource_fd; -} PCIRegion; - -typedef struct PCIDevRegions { - uint8_t bus, dev, func; /* Bus inside domain, device and function */ - int irq; /* IRQ number */ - uint16_t region_number; /* number of active regions */ - - /* Port I/O or MMIO Regions */ - PCIRegion regions[PCI_NUM_REGIONS - 1]; - int config_fd; -} PCIDevRegions; - -typedef struct AssignedDevRegion { - MemoryRegion container; - MemoryRegion real_iomem; - union { - uint8_t *r_virtbase; /* mmapped access address for memory regions */ - uint32_t r_baseport; /* the base guest port for I/O regions */ - } u; - pcibus_t e_size; /* emulated size of region in bytes */ - pcibus_t r_size; /* real size of region in bytes */ - PCIRegion *region; -} AssignedDevRegion; - -#define ASSIGNED_DEVICE_PREFER_MSI_BIT 0 -#define ASSIGNED_DEVICE_SHARE_INTX_BIT 1 - -#define ASSIGNED_DEVICE_PREFER_MSI_MASK (1 << ASSIGNED_DEVICE_PREFER_MSI_BIT) -#define ASSIGNED_DEVICE_SHARE_INTX_MASK (1 << ASSIGNED_DEVICE_SHARE_INTX_BIT) - -typedef struct MSIXTableEntry { - uint32_t addr_lo; - uint32_t addr_hi; - uint32_t data; - uint32_t ctrl; -} MSIXTableEntry; - -typedef enum AssignedIRQType { - ASSIGNED_IRQ_NONE = 0, - ASSIGNED_IRQ_INTX_HOST_INTX, - ASSIGNED_IRQ_INTX_HOST_MSI, - ASSIGNED_IRQ_MSI, - ASSIGNED_IRQ_MSIX -} AssignedIRQType; - -typedef struct AssignedDevice { - PCIDevice dev; - PCIHostDeviceAddress host; - uint32_t dev_id; - uint32_t features; - int intpin; - AssignedDevRegion v_addrs[PCI_NUM_REGIONS - 1]; - PCIDevRegions real_device; - PCIINTxRoute intx_route; - AssignedIRQType assigned_irq_type; - struct { -#define ASSIGNED_DEVICE_CAP_MSI (1 << 0) -#define ASSIGNED_DEVICE_CAP_MSIX (1 << 1) - uint32_t available; -#define ASSIGNED_DEVICE_MSI_ENABLED (1 << 0) -#define ASSIGNED_DEVICE_MSIX_ENABLED (1 << 1) -#define ASSIGNED_DEVICE_MSIX_MASKED (1 << 2) - uint32_t state; - } cap; - uint8_t emulate_config_read[PCI_CONFIG_SPACE_SIZE]; - uint8_t emulate_config_write[PCI_CONFIG_SPACE_SIZE]; - int msi_virq_nr; - int *msi_virq; - MSIXTableEntry *msix_table; - hwaddr msix_table_addr; - uint16_t msix_max; - MemoryRegion mmio; - char *configfd_name; - int32_t bootindex; -} AssignedDevice; - -#define TYPE_PCI_ASSIGN "kvm-pci-assign" -#define PCI_ASSIGN(obj) OBJECT_CHECK(AssignedDevice, (obj), TYPE_PCI_ASSIGN) - -static void assigned_dev_update_irq_routing(PCIDevice *dev); - -static void assigned_dev_load_option_rom(AssignedDevice *dev); - -static void assigned_dev_unregister_msix_mmio(AssignedDevice *dev); - -static uint64_t assigned_dev_ioport_rw(AssignedDevRegion *dev_region, - hwaddr addr, int size, - uint64_t *data) -{ - uint64_t val = 0; - int fd = dev_region->region->resource_fd; - - if (data) { - DEBUG("pwrite data=%" PRIx64 ", size=%d, e_phys=" TARGET_FMT_plx - ", addr="TARGET_FMT_plx"\n", *data, size, addr, addr); - if (pwrite(fd, data, size, addr) != size) { - error_report("%s - pwrite failed %s", __func__, strerror(errno)); - } - } else { - if (pread(fd, &val, size, addr) != size) { - error_report("%s - pread failed %s", __func__, strerror(errno)); - val = (1UL << (size * 8)) - 1; - } - DEBUG("pread val=%" PRIx64 ", size=%d, e_phys=" TARGET_FMT_plx - ", addr=" TARGET_FMT_plx "\n", val, size, addr, addr); - } - return val; -} - -static void assigned_dev_ioport_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - assigned_dev_ioport_rw(opaque, addr, size, &data); -} - -static uint64_t assigned_dev_ioport_read(void *opaque, - hwaddr addr, unsigned size) -{ - return assigned_dev_ioport_rw(opaque, addr, size, NULL); -} - -static uint32_t slow_bar_readb(void *opaque, hwaddr addr) -{ - AssignedDevRegion *d = opaque; - uint8_t *in = d->u.r_virtbase + addr; - uint32_t r; - - r = *in; - DEBUG("addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, r); - - return r; -} - -static uint32_t slow_bar_readw(void *opaque, hwaddr addr) -{ - AssignedDevRegion *d = opaque; - uint16_t *in = (uint16_t *)(d->u.r_virtbase + addr); - uint32_t r; - - r = *in; - DEBUG("addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, r); - - return r; -} - -static uint32_t slow_bar_readl(void *opaque, hwaddr addr) -{ - AssignedDevRegion *d = opaque; - uint32_t *in = (uint32_t *)(d->u.r_virtbase + addr); - uint32_t r; - - r = *in; - DEBUG("addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, r); - - return r; -} - -static void slow_bar_writeb(void *opaque, hwaddr addr, uint32_t val) -{ - AssignedDevRegion *d = opaque; - uint8_t *out = d->u.r_virtbase + addr; - - DEBUG("addr=0x" TARGET_FMT_plx " val=0x%02x\n", addr, val); - *out = val; -} - -static void slow_bar_writew(void *opaque, hwaddr addr, uint32_t val) -{ - AssignedDevRegion *d = opaque; - uint16_t *out = (uint16_t *)(d->u.r_virtbase + addr); - - DEBUG("addr=0x" TARGET_FMT_plx " val=0x%04x\n", addr, val); - *out = val; -} - -static void slow_bar_writel(void *opaque, hwaddr addr, uint32_t val) -{ - AssignedDevRegion *d = opaque; - uint32_t *out = (uint32_t *)(d->u.r_virtbase + addr); - - DEBUG("addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, val); - *out = val; -} - -static const MemoryRegionOps slow_bar_ops = { - .old_mmio = { - .read = { slow_bar_readb, slow_bar_readw, slow_bar_readl, }, - .write = { slow_bar_writeb, slow_bar_writew, slow_bar_writel, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void assigned_dev_iomem_setup(PCIDevice *pci_dev, int region_num, - pcibus_t e_size) -{ - AssignedDevice *r_dev = PCI_ASSIGN(pci_dev); - AssignedDevRegion *region = &r_dev->v_addrs[region_num]; - PCIRegion *real_region = &r_dev->real_device.regions[region_num]; - - if (e_size > 0) { - memory_region_init(®ion->container, OBJECT(pci_dev), - "assigned-dev-container", e_size); - memory_region_add_subregion(®ion->container, 0, ®ion->real_iomem); - - /* deal with MSI-X MMIO page */ - if (real_region->base_addr <= r_dev->msix_table_addr && - real_region->base_addr + real_region->size > - r_dev->msix_table_addr) { - uint64_t offset = r_dev->msix_table_addr - real_region->base_addr; - - memory_region_add_subregion_overlap(®ion->container, - offset, - &r_dev->mmio, - 1); - } - } -} - -static const MemoryRegionOps assigned_dev_ioport_ops = { - .read = assigned_dev_ioport_read, - .write = assigned_dev_ioport_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void assigned_dev_ioport_setup(PCIDevice *pci_dev, int region_num, - pcibus_t size) -{ - AssignedDevice *r_dev = PCI_ASSIGN(pci_dev); - AssignedDevRegion *region = &r_dev->v_addrs[region_num]; - - region->e_size = size; - memory_region_init(®ion->container, OBJECT(pci_dev), - "assigned-dev-container", size); - memory_region_init_io(®ion->real_iomem, OBJECT(pci_dev), - &assigned_dev_ioport_ops, r_dev->v_addrs + region_num, - "assigned-dev-iomem", size); - memory_region_add_subregion(®ion->container, 0, ®ion->real_iomem); -} - -static uint32_t assigned_dev_pci_read(PCIDevice *d, int pos, int len) -{ - AssignedDevice *pci_dev = PCI_ASSIGN(d); - uint32_t val; - ssize_t ret; - int fd = pci_dev->real_device.config_fd; - -again: - ret = pread(fd, &val, len, pos); - if (ret != len) { - if ((ret < 0) && (errno == EINTR || errno == EAGAIN)) { - goto again; - } - - hw_error("pci read failed, ret = %zd errno = %d\n", ret, errno); - } - - return val; -} - -static uint8_t assigned_dev_pci_read_byte(PCIDevice *d, int pos) -{ - return (uint8_t)assigned_dev_pci_read(d, pos, 1); -} - -static void assigned_dev_pci_write(PCIDevice *d, int pos, uint32_t val, int len) -{ - AssignedDevice *pci_dev = PCI_ASSIGN(d); - ssize_t ret; - int fd = pci_dev->real_device.config_fd; - -again: - ret = pwrite(fd, &val, len, pos); - if (ret != len) { - if ((ret < 0) && (errno == EINTR || errno == EAGAIN)) { - goto again; - } - - hw_error("pci write failed, ret = %zd errno = %d\n", ret, errno); - } -} - -static void assigned_dev_emulate_config_read(AssignedDevice *dev, - uint32_t offset, uint32_t len) -{ - memset(dev->emulate_config_read + offset, 0xff, len); -} - -static void assigned_dev_direct_config_read(AssignedDevice *dev, - uint32_t offset, uint32_t len) -{ - memset(dev->emulate_config_read + offset, 0, len); -} - -static void assigned_dev_direct_config_write(AssignedDevice *dev, - uint32_t offset, uint32_t len) -{ - memset(dev->emulate_config_write + offset, 0, len); -} - -static uint8_t pci_find_cap_offset(PCIDevice *d, uint8_t cap, uint8_t start) -{ - int id; - int max_cap = 48; - int pos = start ? start : PCI_CAPABILITY_LIST; - int status; - - status = assigned_dev_pci_read_byte(d, PCI_STATUS); - if ((status & PCI_STATUS_CAP_LIST) == 0) { - return 0; - } - - while (max_cap--) { - pos = assigned_dev_pci_read_byte(d, pos); - if (pos < 0x40) { - break; - } - - pos &= ~3; - id = assigned_dev_pci_read_byte(d, pos + PCI_CAP_LIST_ID); - - if (id == 0xff) { - break; - } - if (id == cap) { - return pos; - } - - pos += PCI_CAP_LIST_NEXT; - } - return 0; -} - -static void assigned_dev_register_regions(PCIRegion *io_regions, - unsigned long regions_num, - AssignedDevice *pci_dev, - Error **errp) -{ - uint32_t i; - PCIRegion *cur_region = io_regions; - - for (i = 0; i < regions_num; i++, cur_region++) { - if (!cur_region->valid) { - continue; - } - - /* handle memory io regions */ - if (cur_region->type & IORESOURCE_MEM) { - int t = PCI_BASE_ADDRESS_SPACE_MEMORY; - if (cur_region->type & IORESOURCE_PREFETCH) { - t |= PCI_BASE_ADDRESS_MEM_PREFETCH; - } - if (cur_region->type & IORESOURCE_MEM_64) { - t |= PCI_BASE_ADDRESS_MEM_TYPE_64; - } - - /* map physical memory */ - pci_dev->v_addrs[i].u.r_virtbase = mmap(NULL, cur_region->size, - PROT_WRITE | PROT_READ, - MAP_SHARED, - cur_region->resource_fd, - (off_t)0); - - if (pci_dev->v_addrs[i].u.r_virtbase == MAP_FAILED) { - pci_dev->v_addrs[i].u.r_virtbase = NULL; - error_setg_errno(errp, errno, "Couldn't mmap 0x%" PRIx64 "!", - cur_region->base_addr); - return; - } - - pci_dev->v_addrs[i].r_size = cur_region->size; - pci_dev->v_addrs[i].e_size = 0; - - /* add offset */ - pci_dev->v_addrs[i].u.r_virtbase += - (cur_region->base_addr & 0xFFF); - - if (cur_region->size & 0xFFF) { - error_report("PCI region %d at address 0x%" PRIx64 " has " - "size 0x%" PRIx64 ", which is not a multiple of " - "4K. You might experience some performance hit " - "due to that.", - i, cur_region->base_addr, cur_region->size); - memory_region_init_io(&pci_dev->v_addrs[i].real_iomem, - OBJECT(pci_dev), &slow_bar_ops, - &pci_dev->v_addrs[i], - "assigned-dev-slow-bar", - cur_region->size); - } else { - void *virtbase = pci_dev->v_addrs[i].u.r_virtbase; - char name[32]; - snprintf(name, sizeof(name), "%s.bar%d", - object_get_typename(OBJECT(pci_dev)), i); - memory_region_init_ram_ptr(&pci_dev->v_addrs[i].real_iomem, - OBJECT(pci_dev), name, - cur_region->size, virtbase); - vmstate_register_ram(&pci_dev->v_addrs[i].real_iomem, - &pci_dev->dev.qdev); - } - - assigned_dev_iomem_setup(&pci_dev->dev, i, cur_region->size); - pci_register_bar((PCIDevice *) pci_dev, i, t, - &pci_dev->v_addrs[i].container); - continue; - } else { - /* handle port io regions */ - uint32_t val; - int ret; - - /* Test kernel support for ioport resource read/write. Old - * kernels return EIO. New kernels only allow 1/2/4 byte reads - * so should return EINVAL for a 3 byte read */ - ret = pread(pci_dev->v_addrs[i].region->resource_fd, &val, 3, 0); - if (ret >= 0) { - error_report("Unexpected return from I/O port read: %d", ret); - abort(); - } else if (errno != EINVAL) { - error_report("Kernel doesn't support ioport resource " - "access, hiding this region."); - close(pci_dev->v_addrs[i].region->resource_fd); - cur_region->valid = 0; - continue; - } - - pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr; - pci_dev->v_addrs[i].r_size = cur_region->size; - pci_dev->v_addrs[i].e_size = 0; - - assigned_dev_ioport_setup(&pci_dev->dev, i, cur_region->size); - pci_register_bar((PCIDevice *) pci_dev, i, - PCI_BASE_ADDRESS_SPACE_IO, - &pci_dev->v_addrs[i].container); - } - } - - /* success */ -} - -static void get_real_id(const char *devpath, const char *idname, uint16_t *val, - Error **errp) -{ - FILE *f; - char name[128]; - long id; - - snprintf(name, sizeof(name), "%s%s", devpath, idname); - f = fopen(name, "r"); - if (f == NULL) { - error_setg_file_open(errp, errno, name); - return; - } - if (fscanf(f, "%li\n", &id) == 1) { - *val = id; - } else { - error_setg(errp, "Failed to parse contents of '%s'", name); - } - fclose(f); -} - -static void get_real_vendor_id(const char *devpath, uint16_t *val, - Error **errp) -{ - get_real_id(devpath, "vendor", val, errp); -} - -static void get_real_device_id(const char *devpath, uint16_t *val, - Error **errp) -{ - get_real_id(devpath, "device", val, errp); -} - -static void get_real_device(AssignedDevice *pci_dev, Error **errp) -{ - char dir[128], name[128]; - int fd, r = 0; - FILE *f; - uint64_t start, end, size, flags; - uint16_t id; - PCIRegion *rp; - PCIDevRegions *dev = &pci_dev->real_device; - Error *local_err = NULL; - - dev->region_number = 0; - - snprintf(dir, sizeof(dir), "/sys/bus/pci/devices/%04x:%02x:%02x.%x/", - pci_dev->host.domain, pci_dev->host.bus, - pci_dev->host.slot, pci_dev->host.function); - - snprintf(name, sizeof(name), "%sconfig", dir); - - if (pci_dev->configfd_name && *pci_dev->configfd_name) { - dev->config_fd = monitor_fd_param(cur_mon, pci_dev->configfd_name, - &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - } else { - dev->config_fd = open(name, O_RDWR); - - if (dev->config_fd == -1) { - error_setg_file_open(errp, errno, name); - return; - } - } -again: - r = read(dev->config_fd, pci_dev->dev.config, - pci_config_size(&pci_dev->dev)); - if (r < 0) { - if (errno == EINTR || errno == EAGAIN) { - goto again; - } - error_setg_errno(errp, errno, "read(\"%s\")", - (pci_dev->configfd_name && *pci_dev->configfd_name) ? - pci_dev->configfd_name : name); - return; - } - - /* Restore or clear multifunction, this is always controlled by qemu */ - if (pci_dev->dev.cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { - pci_dev->dev.config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; - } else { - pci_dev->dev.config[PCI_HEADER_TYPE] &= ~PCI_HEADER_TYPE_MULTI_FUNCTION; - } - - /* Clear host resource mapping info. If we choose not to register a - * BAR, such as might be the case with the option ROM, we can get - * confusing, unwritable, residual addresses from the host here. */ - memset(&pci_dev->dev.config[PCI_BASE_ADDRESS_0], 0, 24); - memset(&pci_dev->dev.config[PCI_ROM_ADDRESS], 0, 4); - - snprintf(name, sizeof(name), "%sresource", dir); - - f = fopen(name, "r"); - if (f == NULL) { - error_setg_file_open(errp, errno, name); - return; - } - - for (r = 0; r < PCI_ROM_SLOT; r++) { - if (fscanf(f, "%" SCNi64 " %" SCNi64 " %" SCNi64 "\n", - &start, &end, &flags) != 3) { - break; - } - - rp = dev->regions + r; - rp->valid = 0; - rp->resource_fd = -1; - size = end - start + 1; - flags &= IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH - | IORESOURCE_MEM_64; - if (size == 0 || (flags & ~IORESOURCE_PREFETCH) == 0) { - continue; - } - if (flags & IORESOURCE_MEM) { - flags &= ~IORESOURCE_IO; - } else { - flags &= ~IORESOURCE_PREFETCH; - } - snprintf(name, sizeof(name), "%sresource%d", dir, r); - fd = open(name, O_RDWR); - if (fd == -1) { - continue; - } - rp->resource_fd = fd; - - rp->type = flags; - rp->valid = 1; - rp->base_addr = start; - rp->size = size; - pci_dev->v_addrs[r].region = rp; - DEBUG("region %d size %" PRIu64 " start 0x%" PRIx64 - " type %d resource_fd %d\n", - r, rp->size, start, rp->type, rp->resource_fd); - } - - fclose(f); - - /* read and fill vendor ID */ - get_real_vendor_id(dir, &id, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - pci_dev->dev.config[0] = id & 0xff; - pci_dev->dev.config[1] = (id & 0xff00) >> 8; - - /* read and fill device ID */ - get_real_device_id(dir, &id, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - pci_dev->dev.config[2] = id & 0xff; - pci_dev->dev.config[3] = (id & 0xff00) >> 8; - - pci_word_test_and_clear_mask(pci_dev->emulate_config_write + PCI_COMMAND, - PCI_COMMAND_MASTER | PCI_COMMAND_INTX_DISABLE); - - dev->region_number = r; -} - -static void free_msi_virqs(AssignedDevice *dev) -{ - int i; - - for (i = 0; i < dev->msi_virq_nr; i++) { - if (dev->msi_virq[i] >= 0) { - kvm_irqchip_release_virq(kvm_state, dev->msi_virq[i]); - dev->msi_virq[i] = -1; - } - } - g_free(dev->msi_virq); - dev->msi_virq = NULL; - dev->msi_virq_nr = 0; -} - -static void free_assigned_device(AssignedDevice *dev) -{ - int i; - - if (dev->cap.available & ASSIGNED_DEVICE_CAP_MSIX) { - assigned_dev_unregister_msix_mmio(dev); - } - for (i = 0; i < dev->real_device.region_number; i++) { - PCIRegion *pci_region = &dev->real_device.regions[i]; - AssignedDevRegion *region = &dev->v_addrs[i]; - - if (!pci_region->valid) { - continue; - } - if (pci_region->type & IORESOURCE_IO) { - if (region->u.r_baseport) { - memory_region_del_subregion(®ion->container, - ®ion->real_iomem); - } - } else if (pci_region->type & IORESOURCE_MEM) { - if (region->u.r_virtbase) { - memory_region_del_subregion(®ion->container, - ®ion->real_iomem); - - /* Remove MSI-X table subregion */ - if (pci_region->base_addr <= dev->msix_table_addr && - pci_region->base_addr + pci_region->size > - dev->msix_table_addr) { - memory_region_del_subregion(®ion->container, - &dev->mmio); - } - if (munmap(region->u.r_virtbase, - (pci_region->size + 0xFFF) & 0xFFFFF000)) { - error_report("Failed to unmap assigned device region: %s", - strerror(errno)); - } - } - } - if (pci_region->resource_fd >= 0) { - close(pci_region->resource_fd); - } - } - - if (dev->real_device.config_fd >= 0) { - close(dev->real_device.config_fd); - } - - free_msi_virqs(dev); -} - -/* This function tries to determine the cause of the PCI assignment failure. It - * always returns the cause as a dynamically allocated, human readable string. - * If the function fails to determine the cause for any internal reason, then - * the returned string will state that fact. - */ -static char *assign_failed_examine(const AssignedDevice *dev) -{ - char name[PATH_MAX], dir[PATH_MAX], driver[PATH_MAX] = {}, *ns; - uint16_t vendor_id, device_id; - int r; - Error *local_err = NULL; - - snprintf(dir, sizeof(dir), "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/", - dev->host.domain, dev->host.bus, dev->host.slot, - dev->host.function); - - snprintf(name, sizeof(name), "%sdriver", dir); - - r = readlink(name, driver, sizeof(driver)); - if ((r <= 0) || r >= sizeof(driver)) { - goto fail; - } - - driver[r] = 0; - ns = strrchr(driver, '/'); - if (!ns) { - goto fail; - } - - ns++; - - if ((get_real_vendor_id(dir, &vendor_id, &local_err), local_err) || - (get_real_device_id(dir, &device_id, &local_err), local_err)) { - /* We're already analyzing an assignment error, so we suppress this - * one just like the others above. - */ - error_free(local_err); - goto fail; - } - - return g_strdup_printf( - "*** The driver '%s' is occupying your device %04x:%02x:%02x.%x.\n" - "***\n" - "*** You can try the following commands to free it:\n" - "***\n" - "*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/pci-stub/new_id\n" - "*** $ echo \"%04x:%02x:%02x.%x\" > /sys/bus/pci/drivers/%s/unbind\n" - "*** $ 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, - ns, dev->host.domain, dev->host.bus, dev->host.slot, - dev->host.function, vendor_id, device_id); - -fail: - return g_strdup("Couldn't find out why.\n"); -} - -static void assign_device(AssignedDevice *dev, Error **errp) -{ - uint32_t flags = KVM_DEV_ASSIGN_ENABLE_IOMMU; - int r; - - /* Only pass non-zero PCI segment to capable module */ - if (!kvm_check_extension(kvm_state, KVM_CAP_PCI_SEGMENT) && - dev->host.domain) { - error_setg(errp, "Can't assign device inside non-zero PCI segment " - "as this KVM module doesn't support it."); - return; - } - - if (!kvm_check_extension(kvm_state, KVM_CAP_IOMMU)) { - error_setg(errp, "No IOMMU found. Unable to assign device \"%s\"", - dev->dev.qdev.id); - return; - } - - if (dev->features & ASSIGNED_DEVICE_SHARE_INTX_MASK && - kvm_has_intx_set_mask()) { - flags |= KVM_DEV_ASSIGN_PCI_2_3; - } - - r = kvm_device_pci_assign(kvm_state, &dev->host, flags, &dev->dev_id); - if (r < 0) { - switch (r) { - case -EBUSY: { - char *cause; - - cause = assign_failed_examine(dev); - error_setg_errno(errp, -r, "Failed to assign device \"%s\"", - dev->dev.qdev.id); - error_append_hint(errp, "%s", cause); - g_free(cause); - break; - } - default: - error_setg_errno(errp, -r, "Failed to assign device \"%s\"", - dev->dev.qdev.id); - break; - } - } -} - -static void verify_irqchip_in_kernel(Error **errp) -{ - if (kvm_irqchip_in_kernel()) { - return; - } - error_setg(errp, "pci-assign requires KVM with in-kernel irqchip enabled"); -} - -static int assign_intx(AssignedDevice *dev, Error **errp) -{ - AssignedIRQType new_type; - PCIINTxRoute intx_route; - bool intx_host_msi; - int r; - Error *local_err = NULL; - - /* Interrupt PIN 0 means don't use INTx */ - if (assigned_dev_pci_read_byte(&dev->dev, PCI_INTERRUPT_PIN) == 0) { - pci_device_set_intx_routing_notifier(&dev->dev, NULL); - return 0; - } - - verify_irqchip_in_kernel(&local_err); - if (local_err) { - error_propagate(errp, local_err); - return -ENOTSUP; - } - - pci_device_set_intx_routing_notifier(&dev->dev, - assigned_dev_update_irq_routing); - - intx_route = pci_device_route_intx_to_irq(&dev->dev, dev->intpin); - assert(intx_route.mode != PCI_INTX_INVERTED); - - if (!pci_intx_route_changed(&dev->intx_route, &intx_route)) { - return 0; - } - - switch (dev->assigned_irq_type) { - case ASSIGNED_IRQ_INTX_HOST_INTX: - case ASSIGNED_IRQ_INTX_HOST_MSI: - intx_host_msi = dev->assigned_irq_type == ASSIGNED_IRQ_INTX_HOST_MSI; - r = kvm_device_intx_deassign(kvm_state, dev->dev_id, intx_host_msi); - break; - case ASSIGNED_IRQ_MSI: - r = kvm_device_msi_deassign(kvm_state, dev->dev_id); - break; - case ASSIGNED_IRQ_MSIX: - r = kvm_device_msix_deassign(kvm_state, dev->dev_id); - break; - default: - r = 0; - break; - } - if (r) { - perror("assign_intx: deassignment of previous interrupt failed"); - } - dev->assigned_irq_type = ASSIGNED_IRQ_NONE; - - if (intx_route.mode == PCI_INTX_DISABLED) { - dev->intx_route = intx_route; - return 0; - } - -retry: - if (dev->features & ASSIGNED_DEVICE_PREFER_MSI_MASK && - dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) { - intx_host_msi = true; - new_type = ASSIGNED_IRQ_INTX_HOST_MSI; - } else { - intx_host_msi = false; - new_type = ASSIGNED_IRQ_INTX_HOST_INTX; - } - - r = kvm_device_intx_assign(kvm_state, dev->dev_id, intx_host_msi, - intx_route.irq); - if (r < 0) { - if (r == -EIO && !(dev->features & ASSIGNED_DEVICE_PREFER_MSI_MASK) && - dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) { - /* Retry with host-side MSI. There might be an IRQ conflict and - * either the kernel or the device doesn't support sharing. */ - error_report("Host-side INTx sharing not supported, " - "using MSI instead"); - error_printf("Some devices do not work properly in this mode.\n"); - dev->features |= ASSIGNED_DEVICE_PREFER_MSI_MASK; - goto retry; - } - 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; - } - - dev->intx_route = intx_route; - dev->assigned_irq_type = new_type; - return r; -} - -static void deassign_device(AssignedDevice *dev) -{ - int r; - - r = kvm_device_pci_deassign(kvm_state, dev->dev_id); - assert(r == 0); -} - -/* The pci config space got updated. Check if irq numbers have changed - * for our devices - */ -static void assigned_dev_update_irq_routing(PCIDevice *dev) -{ - AssignedDevice *assigned_dev = PCI_ASSIGN(dev); - Error *err = NULL; - int r; - - r = assign_intx(assigned_dev, &err); - if (r < 0) { - error_report_err(err); - err = NULL; - qdev_unplug(&dev->qdev, &err); - assert(!err); - } -} - -static void assigned_dev_update_msi(PCIDevice *pci_dev) -{ - AssignedDevice *assigned_dev = PCI_ASSIGN(pci_dev); - uint8_t ctrl_byte = pci_get_byte(pci_dev->config + pci_dev->msi_cap + - PCI_MSI_FLAGS); - int r; - - /* Some guests gratuitously disable MSI even if they're not using it, - * try to catch this by only deassigning irqs if the guest is using - * MSI or intends to start. */ - if (assigned_dev->assigned_irq_type == ASSIGNED_IRQ_MSI || - (ctrl_byte & PCI_MSI_FLAGS_ENABLE)) { - r = kvm_device_msi_deassign(kvm_state, assigned_dev->dev_id); - /* -ENXIO means no assigned irq */ - if (r && r != -ENXIO) { - perror("assigned_dev_update_msi: deassign irq"); - } - - free_msi_virqs(assigned_dev); - - assigned_dev->assigned_irq_type = ASSIGNED_IRQ_NONE; - pci_device_set_intx_routing_notifier(pci_dev, NULL); - } - - if (ctrl_byte & PCI_MSI_FLAGS_ENABLE) { - MSIMessage msg = msi_get_message(pci_dev, 0); - int virq; - - 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; - } - - assigned_dev->msi_virq = g_malloc(sizeof(*assigned_dev->msi_virq)); - assigned_dev->msi_virq_nr = 1; - assigned_dev->msi_virq[0] = virq; - if (kvm_device_msi_assign(kvm_state, assigned_dev->dev_id, virq) < 0) { - perror("assigned_dev_update_msi: kvm_device_msi_assign"); - } - - assigned_dev->intx_route.mode = PCI_INTX_DISABLED; - assigned_dev->intx_route.irq = -1; - assigned_dev->assigned_irq_type = ASSIGNED_IRQ_MSI; - } else { - Error *local_err = NULL; - - assign_intx(assigned_dev, &local_err); - if (local_err) { - error_report_err(local_err); - } - } -} - -static void assigned_dev_update_msi_msg(PCIDevice *pci_dev) -{ - AssignedDevice *assigned_dev = PCI_ASSIGN(pci_dev); - uint8_t ctrl_byte = pci_get_byte(pci_dev->config + pci_dev->msi_cap + - PCI_MSI_FLAGS); - - if (assigned_dev->assigned_irq_type != ASSIGNED_IRQ_MSI || - !(ctrl_byte & PCI_MSI_FLAGS_ENABLE)) { - return; - } - - kvm_irqchip_update_msi_route(kvm_state, assigned_dev->msi_virq[0], - msi_get_message(pci_dev, 0), pci_dev); -} - -static bool assigned_dev_msix_masked(MSIXTableEntry *entry) -{ - return (entry->ctrl & cpu_to_le32(0x1)) != 0; -} - -/* - * When MSI-X is first enabled the vector table typically has all the - * vectors masked, so we can't use that as the obvious test to figure out - * how many vectors to initially enable. Instead we look at the data field - * because this is what worked for pci-assign for a long time. This makes - * sure the physical MSI-X state tracks the guest's view, which is important - * for some VF/PF and PF/fw communication channels. - */ -static bool assigned_dev_msix_skipped(MSIXTableEntry *entry) -{ - return !entry->data; -} - -static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev) -{ - AssignedDevice *adev = PCI_ASSIGN(pci_dev); - uint16_t entries_nr = 0; - int i, r = 0; - MSIXTableEntry *entry = adev->msix_table; - MSIMessage msg; - - /* Get the usable entry number for allocating */ - for (i = 0; i < adev->msix_max; i++, entry++) { - if (assigned_dev_msix_skipped(entry)) { - continue; - } - entries_nr++; - } - - DEBUG("MSI-X entries: %d\n", entries_nr); - - /* It's valid to enable MSI-X with all entries masked */ - if (!entries_nr) { - return 0; - } - - r = kvm_device_msix_init_vectors(kvm_state, adev->dev_id, entries_nr); - if (r != 0) { - error_report("fail to set MSI-X entry number for MSIX! %s", - strerror(-r)); - return r; - } - - free_msi_virqs(adev); - - adev->msi_virq_nr = adev->msix_max; - adev->msi_virq = g_malloc(adev->msix_max * sizeof(*adev->msi_virq)); - - entry = adev->msix_table; - for (i = 0; i < adev->msix_max; i++, entry++) { - adev->msi_virq[i] = -1; - - if (assigned_dev_msix_skipped(entry)) { - continue; - } - - msg.address = entry->addr_lo | ((uint64_t)entry->addr_hi << 32); - msg.data = entry->data; - r = kvm_irqchip_add_msi_route(kvm_state, msg, pci_dev); - if (r < 0) { - return r; - } - adev->msi_virq[i] = r; - - DEBUG("MSI-X vector %d, gsi %d, addr %08x_%08x, data %08x\n", i, - r, entry->addr_hi, entry->addr_lo, entry->data); - - r = kvm_device_msix_set_vector(kvm_state, adev->dev_id, i, - adev->msi_virq[i]); - if (r) { - error_report("fail to set MSI-X entry! %s", strerror(-r)); - break; - } - } - - return r; -} - -static void assigned_dev_update_msix(PCIDevice *pci_dev) -{ - AssignedDevice *assigned_dev = PCI_ASSIGN(pci_dev); - uint16_t ctrl_word = pci_get_word(pci_dev->config + pci_dev->msix_cap + - PCI_MSIX_FLAGS); - int r; - - /* Some guests gratuitously disable MSIX even if they're not using it, - * try to catch this by only deassigning irqs if the guest is using - * MSIX or intends to start. */ - if ((assigned_dev->assigned_irq_type == ASSIGNED_IRQ_MSIX) || - (ctrl_word & PCI_MSIX_FLAGS_ENABLE)) { - r = kvm_device_msix_deassign(kvm_state, assigned_dev->dev_id); - /* -ENXIO means no assigned irq */ - if (r && r != -ENXIO) { - perror("assigned_dev_update_msix: deassign irq"); - } - - free_msi_virqs(assigned_dev); - - assigned_dev->assigned_irq_type = ASSIGNED_IRQ_NONE; - pci_device_set_intx_routing_notifier(pci_dev, NULL); - } - - if (ctrl_word & PCI_MSIX_FLAGS_ENABLE) { - if (assigned_dev_update_msix_mmio(pci_dev) < 0) { - perror("assigned_dev_update_msix_mmio"); - return; - } - - if (assigned_dev->msi_virq_nr > 0) { - if (kvm_device_msix_assign(kvm_state, assigned_dev->dev_id) < 0) { - perror("assigned_dev_enable_msix: assign irq"); - return; - } - } - assigned_dev->intx_route.mode = PCI_INTX_DISABLED; - assigned_dev->intx_route.irq = -1; - assigned_dev->assigned_irq_type = ASSIGNED_IRQ_MSIX; - } else { - Error *local_err = NULL; - - assign_intx(assigned_dev, &local_err); - if (local_err) { - error_report_err(local_err); - } - } -} - -static uint32_t assigned_dev_pci_read_config(PCIDevice *pci_dev, - uint32_t address, int len) -{ - AssignedDevice *assigned_dev = PCI_ASSIGN(pci_dev); - uint32_t virt_val = pci_default_read_config(pci_dev, address, len); - uint32_t real_val, emulate_mask, full_emulation_mask; - - emulate_mask = 0; - memcpy(&emulate_mask, assigned_dev->emulate_config_read + address, len); - emulate_mask = le32_to_cpu(emulate_mask); - - full_emulation_mask = 0xffffffff >> (32 - len * 8); - - if (emulate_mask != full_emulation_mask) { - real_val = assigned_dev_pci_read(pci_dev, address, len); - return (virt_val & emulate_mask) | (real_val & ~emulate_mask); - } else { - return virt_val; - } -} - -static void assigned_dev_pci_write_config(PCIDevice *pci_dev, uint32_t address, - uint32_t val, int len) -{ - AssignedDevice *assigned_dev = PCI_ASSIGN(pci_dev); - uint16_t old_cmd = pci_get_word(pci_dev->config + PCI_COMMAND); - uint32_t emulate_mask, full_emulation_mask; - int ret; - - pci_default_write_config(pci_dev, address, val, len); - - if (kvm_has_intx_set_mask() && - range_covers_byte(address, len, PCI_COMMAND + 1)) { - bool intx_masked = (pci_get_word(pci_dev->config + PCI_COMMAND) & - PCI_COMMAND_INTX_DISABLE); - - if (intx_masked != !!(old_cmd & PCI_COMMAND_INTX_DISABLE)) { - ret = kvm_device_intx_set_mask(kvm_state, assigned_dev->dev_id, - intx_masked); - if (ret) { - perror("assigned_dev_pci_write_config: set intx mask"); - } - } - } - if (assigned_dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) { - if (range_covers_byte(address, len, - pci_dev->msi_cap + PCI_MSI_FLAGS)) { - assigned_dev_update_msi(pci_dev); - } else if (ranges_overlap(address, len, /* 32bit MSI only */ - pci_dev->msi_cap + PCI_MSI_ADDRESS_LO, 6)) { - assigned_dev_update_msi_msg(pci_dev); - } - } - if (assigned_dev->cap.available & ASSIGNED_DEVICE_CAP_MSIX) { - if (range_covers_byte(address, len, - pci_dev->msix_cap + PCI_MSIX_FLAGS + 1)) { - assigned_dev_update_msix(pci_dev); - } - } - - emulate_mask = 0; - memcpy(&emulate_mask, assigned_dev->emulate_config_write + address, len); - emulate_mask = le32_to_cpu(emulate_mask); - - full_emulation_mask = 0xffffffff >> (32 - len * 8); - - if (emulate_mask != full_emulation_mask) { - if (emulate_mask) { - val &= ~emulate_mask; - val |= assigned_dev_pci_read(pci_dev, address, len) & emulate_mask; - } - assigned_dev_pci_write(pci_dev, address, val, len); - } -} - -static void assigned_dev_setup_cap_read(AssignedDevice *dev, uint32_t offset, - uint32_t len) -{ - assigned_dev_direct_config_read(dev, offset, len); - assigned_dev_emulate_config_read(dev, offset + PCI_CAP_LIST_NEXT, 1); -} - -static int assigned_device_pci_cap_init(PCIDevice *pci_dev, Error **errp) -{ - AssignedDevice *dev = PCI_ASSIGN(pci_dev); - PCIRegion *pci_region = dev->real_device.regions; - int ret, pos; - Error *local_err = NULL; - - /* Clear initial capabilities pointer and status copied from hw */ - pci_set_byte(pci_dev->config + PCI_CAPABILITY_LIST, 0); - pci_set_word(pci_dev->config + PCI_STATUS, - pci_get_word(pci_dev->config + PCI_STATUS) & - ~PCI_STATUS_CAP_LIST); - - /* Expose MSI capability - * MSI capability is the 1st capability in capability config */ - pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_MSI, 0); - if (pos != 0 && kvm_check_extension(kvm_state, KVM_CAP_ASSIGN_DEV_IRQ)) { - verify_irqchip_in_kernel(&local_err); - if (local_err) { - error_propagate(errp, local_err); - return -ENOTSUP; - } - dev->cap.available |= ASSIGNED_DEVICE_CAP_MSI; - /* Only 32-bit/no-mask currently supported */ - ret = pci_add_capability2(pci_dev, PCI_CAP_ID_MSI, pos, 10, - &local_err); - if (ret < 0) { - error_propagate(errp, local_err); - return ret; - } - pci_dev->msi_cap = pos; - - pci_set_word(pci_dev->config + pos + PCI_MSI_FLAGS, - pci_get_word(pci_dev->config + pos + PCI_MSI_FLAGS) & - PCI_MSI_FLAGS_QMASK); - pci_set_long(pci_dev->config + pos + PCI_MSI_ADDRESS_LO, 0); - pci_set_word(pci_dev->config + pos + PCI_MSI_DATA_32, 0); - - /* Set writable fields */ - pci_set_word(pci_dev->wmask + pos + PCI_MSI_FLAGS, - PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); - pci_set_long(pci_dev->wmask + pos + PCI_MSI_ADDRESS_LO, 0xfffffffc); - pci_set_word(pci_dev->wmask + pos + PCI_MSI_DATA_32, 0xffff); - } - /* Expose MSI-X capability */ - pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_MSIX, 0); - if (pos != 0 && kvm_device_msix_supported(kvm_state)) { - int bar_nr; - uint32_t msix_table_entry; - uint16_t msix_max; - - verify_irqchip_in_kernel(&local_err); - if (local_err) { - error_propagate(errp, local_err); - return -ENOTSUP; - } - dev->cap.available |= ASSIGNED_DEVICE_CAP_MSIX; - ret = pci_add_capability2(pci_dev, PCI_CAP_ID_MSIX, pos, 12, - &local_err); - if (ret < 0) { - error_propagate(errp, local_err); - return ret; - } - pci_dev->msix_cap = pos; - - msix_max = (pci_get_word(pci_dev->config + pos + PCI_MSIX_FLAGS) & - PCI_MSIX_FLAGS_QSIZE) + 1; - msix_max = MIN(msix_max, KVM_MAX_MSIX_PER_DEV); - pci_set_word(pci_dev->config + pos + PCI_MSIX_FLAGS, msix_max - 1); - - /* Only enable and function mask bits are writable */ - pci_set_word(pci_dev->wmask + pos + PCI_MSIX_FLAGS, - PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL); - - msix_table_entry = pci_get_long(pci_dev->config + pos + PCI_MSIX_TABLE); - bar_nr = msix_table_entry & PCI_MSIX_FLAGS_BIRMASK; - msix_table_entry &= ~PCI_MSIX_FLAGS_BIRMASK; - dev->msix_table_addr = pci_region[bar_nr].base_addr + msix_table_entry; - dev->msix_max = msix_max; - } - - /* Minimal PM support, nothing writable, device appears to NAK changes */ - pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_PM, 0); - if (pos) { - uint16_t pmc; - - ret = pci_add_capability2(pci_dev, PCI_CAP_ID_PM, pos, PCI_PM_SIZEOF, - &local_err); - if (ret < 0) { - error_propagate(errp, local_err); - return ret; - } - - assigned_dev_setup_cap_read(dev, pos, PCI_PM_SIZEOF); - - pmc = pci_get_word(pci_dev->config + pos + PCI_CAP_FLAGS); - pmc &= (PCI_PM_CAP_VER_MASK | PCI_PM_CAP_DSI); - pci_set_word(pci_dev->config + pos + PCI_CAP_FLAGS, pmc); - - /* assign_device will bring the device up to D0, so we don't need - * to worry about doing that ourselves here. */ - pci_set_word(pci_dev->config + pos + PCI_PM_CTRL, - PCI_PM_CTRL_NO_SOFT_RESET); - - pci_set_byte(pci_dev->config + pos + PCI_PM_PPB_EXTENSIONS, 0); - pci_set_byte(pci_dev->config + pos + PCI_PM_DATA_REGISTER, 0); - } - - pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_EXP, 0); - if (pos) { - uint8_t version, size = 0; - uint16_t type, devctl, lnksta; - uint32_t devcap, lnkcap; - - version = pci_get_byte(pci_dev->config + pos + PCI_EXP_FLAGS); - version &= PCI_EXP_FLAGS_VERS; - if (version == 1) { - size = 0x14; - } else if (version == 2) { - /* - * Check for non-std size, accept reduced size to 0x34, - * which is what bcm5761 implemented, violating the - * PCIe v3.0 spec that regs should exist and be read as 0, - * not optionally provided and shorten the struct size. - */ - size = MIN(0x3c, PCI_CONFIG_SPACE_SIZE - pos); - if (size < 0x34) { - error_setg(errp, "Invalid size PCIe cap-id 0x%x", - PCI_CAP_ID_EXP); - return -EINVAL; - } else if (size != 0x3c) { - error_report("WARNING, %s: PCIe cap-id 0x%x has " - "non-standard size 0x%x; std size should be 0x3c", - __func__, PCI_CAP_ID_EXP, size); - } - } else if (version == 0) { - uint16_t vid, did; - vid = pci_get_word(pci_dev->config + PCI_VENDOR_ID); - did = pci_get_word(pci_dev->config + PCI_DEVICE_ID); - if (vid == PCI_VENDOR_ID_INTEL && did == 0x10ed) { - /* - * quirk for Intel 82599 VF with invalid PCIe capability - * version, should really be version 2 (same as PF) - */ - size = 0x3c; - } - } - - if (size == 0) { - error_setg(errp, "Unsupported PCI express capability version %d", - version); - return -EINVAL; - } - - ret = pci_add_capability2(pci_dev, PCI_CAP_ID_EXP, pos, size, - &local_err); - if (ret < 0) { - error_propagate(errp, local_err); - return ret; - } - - assigned_dev_setup_cap_read(dev, pos, size); - - type = pci_get_word(pci_dev->config + pos + PCI_EXP_FLAGS); - type = (type & PCI_EXP_FLAGS_TYPE) >> 4; - if (type != PCI_EXP_TYPE_ENDPOINT && - type != PCI_EXP_TYPE_LEG_END && type != PCI_EXP_TYPE_RC_END) { - error_setg(errp, "Device assignment only supports endpoint " - "assignment, device type %d", type); - return -EINVAL; - } - - /* capabilities, pass existing read-only copy - * PCI_EXP_FLAGS_IRQ: updated by hardware, should be direct read */ - - /* device capabilities: hide FLR */ - devcap = pci_get_long(pci_dev->config + pos + PCI_EXP_DEVCAP); - devcap &= ~PCI_EXP_DEVCAP_FLR; - pci_set_long(pci_dev->config + pos + PCI_EXP_DEVCAP, devcap); - - /* device control: clear all error reporting enable bits, leaving - * only a few host values. Note, these are - * all writable, but not passed to hw. - */ - devctl = pci_get_word(pci_dev->config + pos + PCI_EXP_DEVCTL); - devctl = (devctl & (PCI_EXP_DEVCTL_READRQ | PCI_EXP_DEVCTL_PAYLOAD)) | - PCI_EXP_DEVCTL_RELAX_EN | PCI_EXP_DEVCTL_NOSNOOP_EN; - pci_set_word(pci_dev->config + pos + PCI_EXP_DEVCTL, devctl); - devctl = PCI_EXP_DEVCTL_BCR_FLR | PCI_EXP_DEVCTL_AUX_PME; - pci_set_word(pci_dev->wmask + pos + PCI_EXP_DEVCTL, ~devctl); - - /* Clear device status */ - pci_set_word(pci_dev->config + pos + PCI_EXP_DEVSTA, 0); - - /* Link capabilities, expose links and latencues, clear reporting */ - lnkcap = pci_get_long(pci_dev->config + pos + PCI_EXP_LNKCAP); - lnkcap &= (PCI_EXP_LNKCAP_SLS | PCI_EXP_LNKCAP_MLW | - PCI_EXP_LNKCAP_ASPMS | PCI_EXP_LNKCAP_L0SEL | - PCI_EXP_LNKCAP_L1EL); - pci_set_long(pci_dev->config + pos + PCI_EXP_LNKCAP, lnkcap); - - /* Link control, pass existing read-only copy. Should be writable? */ - - /* Link status, only expose current speed and width */ - lnksta = pci_get_word(pci_dev->config + pos + PCI_EXP_LNKSTA); - lnksta &= (PCI_EXP_LNKSTA_CLS | PCI_EXP_LNKSTA_NLW); - pci_set_word(pci_dev->config + pos + PCI_EXP_LNKSTA, lnksta); - - if (version >= 2) { - /* Slot capabilities, control, status - not needed for endpoints */ - pci_set_long(pci_dev->config + pos + PCI_EXP_SLTCAP, 0); - pci_set_word(pci_dev->config + pos + PCI_EXP_SLTCTL, 0); - pci_set_word(pci_dev->config + pos + PCI_EXP_SLTSTA, 0); - - /* Root control, capabilities, status - not needed for endpoints */ - pci_set_word(pci_dev->config + pos + PCI_EXP_RTCTL, 0); - pci_set_word(pci_dev->config + pos + PCI_EXP_RTCAP, 0); - pci_set_long(pci_dev->config + pos + PCI_EXP_RTSTA, 0); - - /* Device capabilities/control 2, pass existing read-only copy */ - /* Link control 2, pass existing read-only copy */ - } - } - - pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_PCIX, 0); - if (pos) { - uint16_t cmd; - uint32_t status; - - /* Only expose the minimum, 8 byte capability */ - ret = pci_add_capability2(pci_dev, PCI_CAP_ID_PCIX, pos, 8, - &local_err); - if (ret < 0) { - error_propagate(errp, local_err); - return ret; - } - - assigned_dev_setup_cap_read(dev, pos, 8); - - /* Command register, clear upper bits, including extended modes */ - cmd = pci_get_word(pci_dev->config + pos + PCI_X_CMD); - cmd &= (PCI_X_CMD_DPERR_E | PCI_X_CMD_ERO | PCI_X_CMD_MAX_READ | - PCI_X_CMD_MAX_SPLIT); - pci_set_word(pci_dev->config + pos + PCI_X_CMD, cmd); - - /* Status register, update with emulated PCI bus location, clear - * 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_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); - } - - pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_VPD, 0); - if (pos) { - /* Direct R/W passthrough */ - ret = pci_add_capability2(pci_dev, PCI_CAP_ID_VPD, pos, 8, - &local_err); - if (ret < 0) { - error_propagate(errp, local_err); - return ret; - } - - assigned_dev_setup_cap_read(dev, pos, 8); - - /* direct write for cap content */ - assigned_dev_direct_config_write(dev, pos + 2, 6); - } - - /* Devices can have multiple vendor capabilities, get them all */ - for (pos = 0; (pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_VNDR, pos)); - pos += PCI_CAP_LIST_NEXT) { - uint8_t len = pci_get_byte(pci_dev->config + pos + PCI_CAP_FLAGS); - /* Direct R/W passthrough */ - ret = pci_add_capability2(pci_dev, PCI_CAP_ID_VNDR, pos, len, - &local_err); - if (ret < 0) { - error_propagate(errp, local_err); - return ret; - } - - assigned_dev_setup_cap_read(dev, pos, len); - - /* direct write for cap content */ - assigned_dev_direct_config_write(dev, pos + 2, len - 2); - } - - /* If real and virtual capability list status bits differ, virtualize the - * access. */ - if ((pci_get_word(pci_dev->config + PCI_STATUS) & PCI_STATUS_CAP_LIST) != - (assigned_dev_pci_read_byte(pci_dev, PCI_STATUS) & - PCI_STATUS_CAP_LIST)) { - dev->emulate_config_read[PCI_STATUS] |= PCI_STATUS_CAP_LIST; - } - - return 0; -} - -static uint64_t -assigned_dev_msix_mmio_read(void *opaque, hwaddr addr, - unsigned size) -{ - AssignedDevice *adev = opaque; - uint64_t val; - - memcpy(&val, (void *)((uint8_t *)adev->msix_table + addr), size); - - return val; -} - -static void assigned_dev_msix_mmio_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - AssignedDevice *adev = opaque; - PCIDevice *pdev = &adev->dev; - uint16_t ctrl; - MSIXTableEntry orig; - int i = addr >> 4; - - if (i >= adev->msix_max) { - return; /* Drop write */ - } - - ctrl = pci_get_word(pdev->config + pdev->msix_cap + PCI_MSIX_FLAGS); - - DEBUG("write to MSI-X table offset 0x%lx, val 0x%lx\n", addr, val); - - if (ctrl & PCI_MSIX_FLAGS_ENABLE) { - orig = adev->msix_table[i]; - } - - memcpy((uint8_t *)adev->msix_table + addr, &val, size); - - if (ctrl & PCI_MSIX_FLAGS_ENABLE) { - MSIXTableEntry *entry = &adev->msix_table[i]; - - if (!assigned_dev_msix_masked(&orig) && - assigned_dev_msix_masked(entry)) { - /* - * Vector masked, disable it - * - * XXX It's not clear if we can or should actually attempt - * to mask or disable the interrupt. KVM doesn't have - * support for pending bits and kvm_assign_set_msix_entry - * doesn't modify the device hardware mask. Interrupts - * while masked are simply not injected to the guest, so - * are lost. Can we get away with always injecting an - * interrupt on unmask? - */ - } else if (assigned_dev_msix_masked(&orig) && - !assigned_dev_msix_masked(entry)) { - /* Vector unmasked */ - if (i >= adev->msi_virq_nr || adev->msi_virq[i] < 0) { - /* Previously unassigned vector, start from scratch */ - assigned_dev_update_msix(pdev); - return; - } else { - /* Update an existing, previously masked vector */ - MSIMessage msg; - int ret; - - msg.address = entry->addr_lo | - ((uint64_t)entry->addr_hi << 32); - msg.data = entry->data; - - ret = kvm_irqchip_update_msi_route(kvm_state, - adev->msi_virq[i], msg, - pdev); - if (ret) { - error_report("Error updating irq routing entry (%d)", ret); - } - } - } - } -} - -static const MemoryRegionOps assigned_dev_msix_mmio_ops = { - .read = assigned_dev_msix_mmio_read, - .write = assigned_dev_msix_mmio_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 8, - }, - .impl = { - .min_access_size = 4, - .max_access_size = 8, - }, -}; - -static void assigned_dev_msix_reset(AssignedDevice *dev) -{ - MSIXTableEntry *entry; - int i; - - if (!dev->msix_table) { - return; - } - - memset(dev->msix_table, 0, MSIX_PAGE_SIZE); - - for (i = 0, entry = dev->msix_table; i < dev->msix_max; i++, entry++) { - entry->ctrl = cpu_to_le32(0x1); /* Masked */ - } -} - -static void assigned_dev_register_msix_mmio(AssignedDevice *dev, Error **errp) -{ - dev->msix_table = mmap(NULL, MSIX_PAGE_SIZE, PROT_READ|PROT_WRITE, - MAP_ANONYMOUS|MAP_PRIVATE, 0, 0); - if (dev->msix_table == MAP_FAILED) { - error_setg_errno(errp, errno, "failed to allocate msix_table"); - dev->msix_table = NULL; - return; - } - - assigned_dev_msix_reset(dev); - - memory_region_init_io(&dev->mmio, OBJECT(dev), &assigned_dev_msix_mmio_ops, - dev, "assigned-dev-msix", MSIX_PAGE_SIZE); -} - -static void assigned_dev_unregister_msix_mmio(AssignedDevice *dev) -{ - if (!dev->msix_table) { - return; - } - - if (munmap(dev->msix_table, MSIX_PAGE_SIZE) == -1) { - error_report("error unmapping msix_table! %s", strerror(errno)); - } - dev->msix_table = NULL; -} - -static const VMStateDescription vmstate_assigned_device = { - .name = "pci-assign", - .unmigratable = 1, -}; - -static void reset_assigned_device(DeviceState *dev) -{ - PCIDevice *pci_dev = PCI_DEVICE(dev); - AssignedDevice *adev = PCI_ASSIGN(pci_dev); - char reset_file[64]; - const char reset[] = "1"; - int fd, ret; - - /* - * If a guest is reset without being shutdown, MSI/MSI-X can still - * be running. We want to return the device to a known state on - * reset, so disable those here. We especially do not want MSI-X - * enabled since it lives in MMIO space, which is about to get - * disabled. - */ - if (adev->assigned_irq_type == ASSIGNED_IRQ_MSIX) { - uint16_t ctrl = pci_get_word(pci_dev->config + - pci_dev->msix_cap + PCI_MSIX_FLAGS); - - pci_set_word(pci_dev->config + pci_dev->msix_cap + PCI_MSIX_FLAGS, - ctrl & ~PCI_MSIX_FLAGS_ENABLE); - assigned_dev_update_msix(pci_dev); - } else if (adev->assigned_irq_type == ASSIGNED_IRQ_MSI) { - uint8_t ctrl = pci_get_byte(pci_dev->config + - pci_dev->msi_cap + PCI_MSI_FLAGS); - - pci_set_byte(pci_dev->config + pci_dev->msi_cap + PCI_MSI_FLAGS, - ctrl & ~PCI_MSI_FLAGS_ENABLE); - assigned_dev_update_msi(pci_dev); - } - - snprintf(reset_file, sizeof(reset_file), - "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/reset", - adev->host.domain, adev->host.bus, adev->host.slot, - adev->host.function); - - /* - * Issue a device reset via pci-sysfs. Note that we use write(2) here - * and ignore the return value because some kernels have a bug that - * returns 0 rather than bytes written on success, sending us into an - * infinite retry loop using other write mechanisms. - */ - fd = open(reset_file, O_WRONLY); - if (fd != -1) { - ret = write(fd, reset, strlen(reset)); - (void)ret; - close(fd); - } - - /* - * When a 0 is written to the bus master register, the device is logically - * disconnected from the PCI bus. This avoids further DMA transfers. - */ - assigned_dev_pci_write_config(pci_dev, PCI_COMMAND, 0, 1); -} - -static void assigned_realize(struct PCIDevice *pci_dev, Error **errp) -{ - AssignedDevice *dev = PCI_ASSIGN(pci_dev); - uint8_t e_intx; - int r; - Error *local_err = NULL; - - if (!kvm_enabled()) { - error_setg(&local_err, "pci-assign requires KVM support"); - goto exit_with_error; - } - - if (!dev->host.domain && !dev->host.bus && !dev->host.slot && - !dev->host.function) { - error_setg(&local_err, "no host device specified"); - goto exit_with_error; - } - - /* - * Set up basic config space access control. Will be further refined during - * device initialization. - */ - assigned_dev_emulate_config_read(dev, 0, PCI_CONFIG_SPACE_SIZE); - assigned_dev_direct_config_read(dev, PCI_STATUS, 2); - assigned_dev_direct_config_read(dev, PCI_REVISION_ID, 1); - assigned_dev_direct_config_read(dev, PCI_CLASS_PROG, 3); - assigned_dev_direct_config_read(dev, PCI_CACHE_LINE_SIZE, 1); - assigned_dev_direct_config_read(dev, PCI_LATENCY_TIMER, 1); - assigned_dev_direct_config_read(dev, PCI_BIST, 1); - assigned_dev_direct_config_read(dev, PCI_CARDBUS_CIS, 4); - assigned_dev_direct_config_read(dev, PCI_SUBSYSTEM_VENDOR_ID, 2); - assigned_dev_direct_config_read(dev, PCI_SUBSYSTEM_ID, 2); - assigned_dev_direct_config_read(dev, PCI_CAPABILITY_LIST + 1, 7); - assigned_dev_direct_config_read(dev, PCI_MIN_GNT, 1); - assigned_dev_direct_config_read(dev, PCI_MAX_LAT, 1); - memcpy(dev->emulate_config_write, dev->emulate_config_read, - sizeof(dev->emulate_config_read)); - - get_real_device(dev, &local_err); - if (local_err) { - goto out; - } - - if (assigned_device_pci_cap_init(pci_dev, &local_err) < 0) { - goto out; - } - - /* intercept MSI-X entry page in the MMIO */ - if (dev->cap.available & ASSIGNED_DEVICE_CAP_MSIX) { - assigned_dev_register_msix_mmio(dev, &local_err); - if (local_err) { - goto out; - } - } - - /* handle real device's MMIO/PIO BARs */ - assigned_dev_register_regions(dev->real_device.regions, - dev->real_device.region_number, dev, - &local_err); - if (local_err) { - goto out; - } - - /* handle interrupt routing */ - e_intx = dev->dev.config[PCI_INTERRUPT_PIN] - 1; - dev->intpin = e_intx; - dev->intx_route.mode = PCI_INTX_DISABLED; - dev->intx_route.irq = -1; - - /* assign device to guest */ - assign_device(dev, &local_err); - if (local_err) { - goto out; - } - - /* assign legacy INTx to the device */ - r = assign_intx(dev, &local_err); - if (r < 0) { - goto assigned_out; - } - - assigned_dev_load_option_rom(dev); - - return; - -assigned_out: - deassign_device(dev); - -out: - free_assigned_device(dev); - -exit_with_error: - assert(local_err); - error_propagate(errp, local_err); -} - -static void assigned_exitfn(struct PCIDevice *pci_dev) -{ - AssignedDevice *dev = PCI_ASSIGN(pci_dev); - - deassign_device(dev); - free_assigned_device(dev); -} - -static void assigned_dev_instance_init(Object *obj) -{ - PCIDevice *pci_dev = PCI_DEVICE(obj); - AssignedDevice *d = PCI_ASSIGN(pci_dev); - - device_add_bootindex_property(obj, &d->bootindex, - "bootindex", NULL, - &pci_dev->qdev, NULL); -} - -static Property assigned_dev_properties[] = { - DEFINE_PROP_PCI_HOST_DEVADDR("host", AssignedDevice, host), - DEFINE_PROP_BIT("prefer_msi", AssignedDevice, features, - ASSIGNED_DEVICE_PREFER_MSI_BIT, false), - DEFINE_PROP_BIT("share_intx", AssignedDevice, features, - ASSIGNED_DEVICE_SHARE_INTX_BIT, true), - DEFINE_PROP_STRING("configfd", AssignedDevice, configfd_name), - DEFINE_PROP_END_OF_LIST(), -}; - -static void assign_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = assigned_realize; - k->exit = assigned_exitfn; - k->config_read = assigned_dev_pci_read_config; - k->config_write = assigned_dev_pci_write_config; - dc->props = assigned_dev_properties; - dc->vmsd = &vmstate_assigned_device; - dc->reset = reset_assigned_device; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->desc = "KVM-based PCI passthrough"; -} - -static const TypeInfo assign_info = { - .name = TYPE_PCI_ASSIGN, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(AssignedDevice), - .class_init = assign_class_init, - .instance_init = assigned_dev_instance_init, -}; - -static void assign_register_types(void) -{ - type_register_static(&assign_info); -} - -type_init(assign_register_types) - -static void assigned_dev_load_option_rom(AssignedDevice *dev) -{ - int size = 0; - - pci_assign_dev_load_option_rom(&dev->dev, OBJECT(dev), &size, - dev->host.domain, dev->host.bus, - dev->host.slot, dev->host.function); - - if (!size) { - error_report("pci-assign: Invalid ROM."); - } -} diff --git a/qemu/hw/i386/kvmvapic.c b/qemu/hw/i386/kvmvapic.c deleted file mode 100644 index c69f37404..000000000 --- a/qemu/hw/i386/kvmvapic.c +++ /dev/null @@ -1,866 +0,0 @@ -/* - * TPR optimization for 32-bit Windows guests (XP and Server 2003) - * - * Copyright (C) 2007-2008 Qumranet Technologies - * Copyright (C) 2012 Jan Kiszka, Siemens AG - * - * 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 "sysemu/sysemu.h" -#include "sysemu/cpus.h" -#include "sysemu/kvm.h" -#include "hw/i386/apic_internal.h" -#include "hw/sysbus.h" - -#define VAPIC_IO_PORT 0x7e - -#define VAPIC_CPU_SHIFT 7 - -#define ROM_BLOCK_SIZE 512 -#define ROM_BLOCK_MASK (~(ROM_BLOCK_SIZE - 1)) - -typedef enum VAPICMode { - VAPIC_INACTIVE = 0, - VAPIC_ACTIVE = 1, - VAPIC_STANDBY = 2, -} VAPICMode; - -typedef struct VAPICHandlers { - uint32_t set_tpr; - uint32_t set_tpr_eax; - uint32_t get_tpr[8]; - uint32_t get_tpr_stack; -} QEMU_PACKED VAPICHandlers; - -typedef struct GuestROMState { - char signature[8]; - uint32_t vaddr; - uint32_t fixup_start; - uint32_t fixup_end; - uint32_t vapic_vaddr; - uint32_t vapic_size; - uint32_t vcpu_shift; - uint32_t real_tpr_addr; - VAPICHandlers up; - VAPICHandlers mp; -} QEMU_PACKED GuestROMState; - -typedef struct VAPICROMState { - SysBusDevice busdev; - MemoryRegion io; - MemoryRegion rom; - uint32_t state; - uint32_t rom_state_paddr; - uint32_t rom_state_vaddr; - uint32_t vapic_paddr; - uint32_t real_tpr_addr; - GuestROMState rom_state; - size_t rom_size; - bool rom_mapped_writable; - VMChangeStateEntry *vmsentry; -} VAPICROMState; - -#define TYPE_VAPIC "kvmvapic" -#define VAPIC(obj) OBJECT_CHECK(VAPICROMState, (obj), TYPE_VAPIC) - -#define TPR_INSTR_ABS_MODRM 0x1 -#define TPR_INSTR_MATCH_MODRM_REG 0x2 - -typedef struct TPRInstruction { - uint8_t opcode; - uint8_t modrm_reg; - unsigned int flags; - TPRAccess access; - size_t length; - off_t addr_offset; -} TPRInstruction; - -/* must be sorted by length, shortest first */ -static const TPRInstruction tpr_instr[] = { - { /* mov abs to eax */ - .opcode = 0xa1, - .access = TPR_ACCESS_READ, - .length = 5, - .addr_offset = 1, - }, - { /* mov eax to abs */ - .opcode = 0xa3, - .access = TPR_ACCESS_WRITE, - .length = 5, - .addr_offset = 1, - }, - { /* mov r32 to r/m32 */ - .opcode = 0x89, - .flags = TPR_INSTR_ABS_MODRM, - .access = TPR_ACCESS_WRITE, - .length = 6, - .addr_offset = 2, - }, - { /* mov r/m32 to r32 */ - .opcode = 0x8b, - .flags = TPR_INSTR_ABS_MODRM, - .access = TPR_ACCESS_READ, - .length = 6, - .addr_offset = 2, - }, - { /* push r/m32 */ - .opcode = 0xff, - .modrm_reg = 6, - .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG, - .access = TPR_ACCESS_READ, - .length = 6, - .addr_offset = 2, - }, - { /* mov imm32, r/m32 (c7/0) */ - .opcode = 0xc7, - .modrm_reg = 0, - .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG, - .access = TPR_ACCESS_WRITE, - .length = 10, - .addr_offset = 2, - }, -}; - -static void read_guest_rom_state(VAPICROMState *s) -{ - cpu_physical_memory_read(s->rom_state_paddr, &s->rom_state, - sizeof(GuestROMState)); -} - -static void write_guest_rom_state(VAPICROMState *s) -{ - cpu_physical_memory_write(s->rom_state_paddr, &s->rom_state, - sizeof(GuestROMState)); -} - -static void update_guest_rom_state(VAPICROMState *s) -{ - read_guest_rom_state(s); - - s->rom_state.real_tpr_addr = cpu_to_le32(s->real_tpr_addr); - s->rom_state.vcpu_shift = cpu_to_le32(VAPIC_CPU_SHIFT); - - write_guest_rom_state(s); -} - -static int find_real_tpr_addr(VAPICROMState *s, CPUX86State *env) -{ - CPUState *cs = CPU(x86_env_get_cpu(env)); - hwaddr paddr; - target_ulong addr; - - if (s->state == VAPIC_ACTIVE) { - return 0; - } - /* - * If there is no prior TPR access instruction we could analyze (which is - * the case after resume from hibernation), we need to scan the possible - * virtual address space for the APIC mapping. - */ - for (addr = 0xfffff000; addr >= 0x80000000; addr -= TARGET_PAGE_SIZE) { - paddr = cpu_get_phys_page_debug(cs, addr); - if (paddr != APIC_DEFAULT_ADDRESS) { - continue; - } - s->real_tpr_addr = addr + 0x80; - update_guest_rom_state(s); - return 0; - } - return -1; -} - -static uint8_t modrm_reg(uint8_t modrm) -{ - return (modrm >> 3) & 7; -} - -static bool is_abs_modrm(uint8_t modrm) -{ - return (modrm & 0xc7) == 0x05; -} - -static bool opcode_matches(uint8_t *opcode, const TPRInstruction *instr) -{ - return opcode[0] == instr->opcode && - (!(instr->flags & TPR_INSTR_ABS_MODRM) || is_abs_modrm(opcode[1])) && - (!(instr->flags & TPR_INSTR_MATCH_MODRM_REG) || - modrm_reg(opcode[1]) == instr->modrm_reg); -} - -static int evaluate_tpr_instruction(VAPICROMState *s, X86CPU *cpu, - target_ulong *pip, TPRAccess access) -{ - CPUState *cs = CPU(cpu); - const TPRInstruction *instr; - target_ulong ip = *pip; - uint8_t opcode[2]; - uint32_t real_tpr_addr; - int i; - - if ((ip & 0xf0000000ULL) != 0x80000000ULL && - (ip & 0xf0000000ULL) != 0xe0000000ULL) { - return -1; - } - - /* - * Early Windows 2003 SMP initialization contains a - * - * mov imm32, r/m32 - * - * instruction that is patched by TPR optimization. The problem is that - * RSP, used by the patched instruction, is zero, so the guest gets a - * double fault and dies. - */ - if (cpu->env.regs[R_ESP] == 0) { - return -1; - } - - if (kvm_enabled() && !kvm_irqchip_in_kernel()) { - /* - * KVM without kernel-based TPR access reporting will pass an IP that - * points after the accessing instruction. So we need to look backward - * to find the reason. - */ - for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) { - instr = &tpr_instr[i]; - if (instr->access != access) { - continue; - } - if (cpu_memory_rw_debug(cs, ip - instr->length, opcode, - sizeof(opcode), 0) < 0) { - return -1; - } - if (opcode_matches(opcode, instr)) { - ip -= instr->length; - goto instruction_ok; - } - } - return -1; - } else { - if (cpu_memory_rw_debug(cs, ip, opcode, sizeof(opcode), 0) < 0) { - return -1; - } - for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) { - instr = &tpr_instr[i]; - if (opcode_matches(opcode, instr)) { - goto instruction_ok; - } - } - return -1; - } - -instruction_ok: - /* - * Grab the virtual TPR address from the instruction - * and update the cached values. - */ - if (cpu_memory_rw_debug(cs, ip + instr->addr_offset, - (void *)&real_tpr_addr, - sizeof(real_tpr_addr), 0) < 0) { - return -1; - } - real_tpr_addr = le32_to_cpu(real_tpr_addr); - if ((real_tpr_addr & 0xfff) != 0x80) { - return -1; - } - s->real_tpr_addr = real_tpr_addr; - update_guest_rom_state(s); - - *pip = ip; - return 0; -} - -static int update_rom_mapping(VAPICROMState *s, CPUX86State *env, target_ulong ip) -{ - CPUState *cs = CPU(x86_env_get_cpu(env)); - hwaddr paddr; - uint32_t rom_state_vaddr; - uint32_t pos, patch, offset; - - /* nothing to do if already activated */ - if (s->state == VAPIC_ACTIVE) { - return 0; - } - - /* bail out if ROM init code was not executed (missing ROM?) */ - if (s->state == VAPIC_INACTIVE) { - return -1; - } - - /* find out virtual address of the ROM */ - rom_state_vaddr = s->rom_state_paddr + (ip & 0xf0000000); - paddr = cpu_get_phys_page_debug(cs, rom_state_vaddr); - if (paddr == -1) { - return -1; - } - paddr += rom_state_vaddr & ~TARGET_PAGE_MASK; - if (paddr != s->rom_state_paddr) { - return -1; - } - read_guest_rom_state(s); - if (memcmp(s->rom_state.signature, "kvm aPiC", 8) != 0) { - return -1; - } - s->rom_state_vaddr = rom_state_vaddr; - - /* fixup addresses in ROM if needed */ - if (rom_state_vaddr == le32_to_cpu(s->rom_state.vaddr)) { - return 0; - } - for (pos = le32_to_cpu(s->rom_state.fixup_start); - pos < le32_to_cpu(s->rom_state.fixup_end); - pos += 4) { - cpu_physical_memory_read(paddr + pos - s->rom_state.vaddr, - &offset, sizeof(offset)); - offset = le32_to_cpu(offset); - cpu_physical_memory_read(paddr + offset, &patch, sizeof(patch)); - patch = le32_to_cpu(patch); - patch += rom_state_vaddr - le32_to_cpu(s->rom_state.vaddr); - patch = cpu_to_le32(patch); - cpu_physical_memory_write(paddr + offset, &patch, sizeof(patch)); - } - read_guest_rom_state(s); - s->vapic_paddr = paddr + le32_to_cpu(s->rom_state.vapic_vaddr) - - le32_to_cpu(s->rom_state.vaddr); - - return 0; -} - -/* - * Tries to read the unique processor number from the Kernel Processor Control - * Region (KPCR) of 32-bit Windows XP and Server 2003. Returns -1 if the KPCR - * cannot be accessed or is considered invalid. This also ensures that we are - * not patching the wrong guest. - */ -static int get_kpcr_number(X86CPU *cpu) -{ - CPUX86State *env = &cpu->env; - struct kpcr { - uint8_t fill1[0x1c]; - uint32_t self; - uint8_t fill2[0x31]; - uint8_t number; - } QEMU_PACKED kpcr; - - if (cpu_memory_rw_debug(CPU(cpu), env->segs[R_FS].base, - (void *)&kpcr, sizeof(kpcr), 0) < 0 || - kpcr.self != env->segs[R_FS].base) { - return -1; - } - return kpcr.number; -} - -static int vapic_enable(VAPICROMState *s, X86CPU *cpu) -{ - int cpu_number = get_kpcr_number(cpu); - hwaddr vapic_paddr; - static const uint8_t enabled = 1; - - if (cpu_number < 0) { - return -1; - } - vapic_paddr = s->vapic_paddr + - (((hwaddr)cpu_number) << VAPIC_CPU_SHIFT); - cpu_physical_memory_write(vapic_paddr + offsetof(VAPICState, enabled), - &enabled, sizeof(enabled)); - apic_enable_vapic(cpu->apic_state, vapic_paddr); - - s->state = VAPIC_ACTIVE; - - return 0; -} - -static void patch_byte(X86CPU *cpu, target_ulong addr, uint8_t byte) -{ - cpu_memory_rw_debug(CPU(cpu), addr, &byte, 1, 1); -} - -static void patch_call(VAPICROMState *s, X86CPU *cpu, target_ulong ip, - uint32_t target) -{ - uint32_t offset; - - offset = cpu_to_le32(target - ip - 5); - patch_byte(cpu, ip, 0xe8); /* call near */ - cpu_memory_rw_debug(CPU(cpu), ip + 1, (void *)&offset, sizeof(offset), 1); -} - -static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip) -{ - CPUState *cs = CPU(cpu); - CPUX86State *env = &cpu->env; - VAPICHandlers *handlers; - uint8_t opcode[2]; - uint32_t imm32; - target_ulong current_pc = 0; - target_ulong current_cs_base = 0; - int current_flags = 0; - - if (smp_cpus == 1) { - handlers = &s->rom_state.up; - } else { - handlers = &s->rom_state.mp; - } - - if (!kvm_enabled()) { - cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base, - ¤t_flags); - } - - pause_all_vcpus(); - - cpu_memory_rw_debug(cs, ip, opcode, sizeof(opcode), 0); - - switch (opcode[0]) { - case 0x89: /* mov r32 to r/m32 */ - patch_byte(cpu, ip, 0x50 + modrm_reg(opcode[1])); /* push reg */ - patch_call(s, cpu, ip + 1, handlers->set_tpr); - break; - case 0x8b: /* mov r/m32 to r32 */ - patch_byte(cpu, ip, 0x90); - patch_call(s, cpu, ip + 1, handlers->get_tpr[modrm_reg(opcode[1])]); - break; - case 0xa1: /* mov abs to eax */ - patch_call(s, cpu, ip, handlers->get_tpr[0]); - break; - case 0xa3: /* mov eax to abs */ - patch_call(s, cpu, ip, handlers->set_tpr_eax); - break; - case 0xc7: /* mov imm32, r/m32 (c7/0) */ - patch_byte(cpu, ip, 0x68); /* push imm32 */ - cpu_memory_rw_debug(cs, ip + 6, (void *)&imm32, sizeof(imm32), 0); - cpu_memory_rw_debug(cs, ip + 1, (void *)&imm32, sizeof(imm32), 1); - patch_call(s, cpu, ip + 5, handlers->set_tpr); - break; - case 0xff: /* push r/m32 */ - patch_byte(cpu, ip, 0x50); /* push eax */ - patch_call(s, cpu, ip + 1, handlers->get_tpr_stack); - break; - default: - abort(); - } - - resume_all_vcpus(); - - if (!kvm_enabled()) { - cs->current_tb = NULL; - tb_gen_code(cs, current_pc, current_cs_base, current_flags, 1); - cpu_resume_from_signal(cs, NULL); - } -} - -void vapic_report_tpr_access(DeviceState *dev, CPUState *cs, target_ulong ip, - TPRAccess access) -{ - VAPICROMState *s = VAPIC(dev); - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - - cpu_synchronize_state(cs); - - if (evaluate_tpr_instruction(s, cpu, &ip, access) < 0) { - if (s->state == VAPIC_ACTIVE) { - vapic_enable(s, cpu); - } - return; - } - if (update_rom_mapping(s, env, ip) < 0) { - return; - } - if (vapic_enable(s, cpu) < 0) { - return; - } - patch_instruction(s, cpu, ip); -} - -typedef struct VAPICEnableTPRReporting { - DeviceState *apic; - bool enable; -} VAPICEnableTPRReporting; - -static void vapic_do_enable_tpr_reporting(void *data) -{ - VAPICEnableTPRReporting *info = data; - - apic_enable_tpr_access_reporting(info->apic, info->enable); -} - -static void vapic_enable_tpr_reporting(bool enable) -{ - VAPICEnableTPRReporting info = { - .enable = enable, - }; - CPUState *cs; - X86CPU *cpu; - - CPU_FOREACH(cs) { - cpu = X86_CPU(cs); - info.apic = cpu->apic_state; - run_on_cpu(cs, vapic_do_enable_tpr_reporting, &info); - } -} - -static void vapic_reset(DeviceState *dev) -{ - VAPICROMState *s = VAPIC(dev); - - s->state = VAPIC_INACTIVE; - s->rom_state_paddr = 0; - vapic_enable_tpr_reporting(false); -} - -/* - * Set the IRQ polling hypercalls to the supported variant: - * - vmcall if using KVM in-kernel irqchip - * - 32-bit VAPIC port write otherwise - */ -static int patch_hypercalls(VAPICROMState *s) -{ - hwaddr rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK; - static const uint8_t vmcall_pattern[] = { /* vmcall */ - 0xb8, 0x1, 0, 0, 0, 0xf, 0x1, 0xc1 - }; - static const uint8_t outl_pattern[] = { /* nop; outl %eax,0x7e */ - 0xb8, 0x1, 0, 0, 0, 0x90, 0xe7, 0x7e - }; - uint8_t alternates[2]; - const uint8_t *pattern; - const uint8_t *patch; - int patches = 0; - off_t pos; - uint8_t *rom; - - rom = g_malloc(s->rom_size); - cpu_physical_memory_read(rom_paddr, rom, s->rom_size); - - for (pos = 0; pos < s->rom_size - sizeof(vmcall_pattern); pos++) { - if (kvm_irqchip_in_kernel()) { - pattern = outl_pattern; - alternates[0] = outl_pattern[7]; - alternates[1] = outl_pattern[7]; - patch = &vmcall_pattern[5]; - } else { - pattern = vmcall_pattern; - alternates[0] = vmcall_pattern[7]; - alternates[1] = 0xd9; /* AMD's VMMCALL */ - patch = &outl_pattern[5]; - } - if (memcmp(rom + pos, pattern, 7) == 0 && - (rom[pos + 7] == alternates[0] || rom[pos + 7] == alternates[1])) { - cpu_physical_memory_write(rom_paddr + pos + 5, patch, 3); - /* - * Don't flush the tb here. Under ordinary conditions, the patched - * calls are miles away from the current IP. Under malicious - * conditions, the guest could trick us to crash. - */ - } - } - - g_free(rom); - - if (patches != 0 && patches != 2) { - return -1; - } - - return 0; -} - -/* - * For TCG mode or the time KVM honors read-only memory regions, we need to - * enable write access to the option ROM so that variables can be updated by - * the guest. - */ -static int vapic_map_rom_writable(VAPICROMState *s) -{ - hwaddr rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK; - MemoryRegionSection section; - MemoryRegion *as; - size_t rom_size; - uint8_t *ram; - - as = sysbus_address_space(&s->busdev); - - if (s->rom_mapped_writable) { - memory_region_del_subregion(as, &s->rom); - object_unparent(OBJECT(&s->rom)); - } - - /* grab RAM memory region (region @rom_paddr may still be pc.rom) */ - section = memory_region_find(as, 0, 1); - - /* read ROM size from RAM region */ - if (rom_paddr + 2 >= memory_region_size(section.mr)) { - return -1; - } - ram = memory_region_get_ram_ptr(section.mr); - rom_size = ram[rom_paddr + 2] * ROM_BLOCK_SIZE; - if (rom_size == 0) { - return -1; - } - s->rom_size = rom_size; - - /* We need to round to avoid creating subpages - * from which we cannot run code. */ - rom_size += rom_paddr & ~TARGET_PAGE_MASK; - rom_paddr &= TARGET_PAGE_MASK; - rom_size = TARGET_PAGE_ALIGN(rom_size); - - memory_region_init_alias(&s->rom, OBJECT(s), "kvmvapic-rom", section.mr, - rom_paddr, rom_size); - memory_region_add_subregion_overlap(as, rom_paddr, &s->rom, 1000); - s->rom_mapped_writable = true; - memory_region_unref(section.mr); - - return 0; -} - -static int vapic_prepare(VAPICROMState *s) -{ - if (vapic_map_rom_writable(s) < 0) { - return -1; - } - - if (patch_hypercalls(s) < 0) { - return -1; - } - - vapic_enable_tpr_reporting(true); - - return 0; -} - -static void vapic_write(void *opaque, hwaddr addr, uint64_t data, - unsigned int size) -{ - VAPICROMState *s = opaque; - X86CPU *cpu; - CPUX86State *env; - hwaddr rom_paddr; - - 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. - * o 16-bit write access: - * Reports the option ROM initialization to the hypervisor. Written - * value is the offset of the state structure in the ROM. - * o 8-bit write access: - * Reactivates the VAPIC after a guest hibernation, i.e. after the - * option ROM content has been re-initialized by a guest power cycle. - * o 32-bit write access: - * Poll for pending IRQs, considering the current VAPIC state. - */ - switch (size) { - case 2: - if (s->state == VAPIC_INACTIVE) { - rom_paddr = (env->segs[R_CS].base + env->eip) & ROM_BLOCK_MASK; - s->rom_state_paddr = rom_paddr + data; - - s->state = VAPIC_STANDBY; - } - if (vapic_prepare(s) < 0) { - s->state = VAPIC_INACTIVE; - s->rom_state_paddr = 0; - break; - } - break; - case 1: - if (kvm_enabled()) { - /* - * Disable triggering instruction in ROM by writing a NOP. - * - * We cannot do this in TCG mode as the reported IP is not - * accurate. - */ - pause_all_vcpus(); - patch_byte(cpu, env->eip - 2, 0x66); - patch_byte(cpu, env->eip - 1, 0x90); - resume_all_vcpus(); - } - - if (s->state == VAPIC_ACTIVE) { - break; - } - if (update_rom_mapping(s, env, env->eip) < 0) { - break; - } - if (find_real_tpr_addr(s, env) < 0) { - break; - } - vapic_enable(s, cpu); - break; - default: - case 4: - if (!kvm_irqchip_in_kernel()) { - apic_poll_irq(cpu->apic_state); - } - break; - } -} - -static uint64_t vapic_read(void *opaque, hwaddr addr, unsigned size) -{ - return 0xffffffff; -} - -static const MemoryRegionOps vapic_ops = { - .write = vapic_write, - .read = vapic_read, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void vapic_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - VAPICROMState *s = VAPIC(dev); - - memory_region_init_io(&s->io, OBJECT(s), &vapic_ops, s, "kvmvapic", 2); - sysbus_add_io(sbd, VAPIC_IO_PORT, &s->io); - sysbus_init_ioports(sbd, VAPIC_IO_PORT, 2); - - option_rom[nb_option_roms].name = "kvmvapic.bin"; - option_rom[nb_option_roms].bootindex = -1; - nb_option_roms++; -} - -static void do_vapic_enable(void *data) -{ - VAPICROMState *s = data; - X86CPU *cpu = X86_CPU(first_cpu); - - static const uint8_t enabled = 1; - cpu_physical_memory_write(s->vapic_paddr + offsetof(VAPICState, enabled), - &enabled, sizeof(enabled)); - apic_enable_vapic(cpu->apic_state, s->vapic_paddr); - s->state = VAPIC_ACTIVE; -} - -static void kvmvapic_vm_state_change(void *opaque, int running, - RunState state) -{ - VAPICROMState *s = opaque; - uint8_t *zero; - - if (!running) { - return; - } - - if (s->state == VAPIC_ACTIVE) { - if (smp_cpus == 1) { - run_on_cpu(first_cpu, do_vapic_enable, s); - } else { - zero = g_malloc0(s->rom_state.vapic_size); - cpu_physical_memory_write(s->vapic_paddr, zero, - s->rom_state.vapic_size); - g_free(zero); - } - } - - qemu_del_vm_change_state_handler(s->vmsentry); -} - -static int vapic_post_load(void *opaque, int version_id) -{ - VAPICROMState *s = opaque; - - /* - * The old implementation of qemu-kvm did not provide the state - * VAPIC_STANDBY. Reconstruct it. - */ - if (s->state == VAPIC_INACTIVE && s->rom_state_paddr != 0) { - s->state = VAPIC_STANDBY; - } - - if (s->state != VAPIC_INACTIVE) { - if (vapic_prepare(s) < 0) { - return -1; - } - } - - if (!s->vmsentry) { - s->vmsentry = - qemu_add_vm_change_state_handler(kvmvapic_vm_state_change, s); - } - return 0; -} - -static const VMStateDescription vmstate_handlers = { - .name = "kvmvapic-handlers", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(set_tpr, VAPICHandlers), - VMSTATE_UINT32(set_tpr_eax, VAPICHandlers), - VMSTATE_UINT32_ARRAY(get_tpr, VAPICHandlers, 8), - VMSTATE_UINT32(get_tpr_stack, VAPICHandlers), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_guest_rom = { - .name = "kvmvapic-guest-rom", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UNUSED(8), /* signature */ - VMSTATE_UINT32(vaddr, GuestROMState), - VMSTATE_UINT32(fixup_start, GuestROMState), - VMSTATE_UINT32(fixup_end, GuestROMState), - VMSTATE_UINT32(vapic_vaddr, GuestROMState), - VMSTATE_UINT32(vapic_size, GuestROMState), - VMSTATE_UINT32(vcpu_shift, GuestROMState), - VMSTATE_UINT32(real_tpr_addr, GuestROMState), - VMSTATE_STRUCT(up, GuestROMState, 0, vmstate_handlers, VAPICHandlers), - VMSTATE_STRUCT(mp, GuestROMState, 0, vmstate_handlers, VAPICHandlers), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_vapic = { - .name = "kvm-tpr-opt", /* compatible with qemu-kvm VAPIC */ - .version_id = 1, - .minimum_version_id = 1, - .post_load = vapic_post_load, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(rom_state, VAPICROMState, 0, vmstate_guest_rom, - GuestROMState), - VMSTATE_UINT32(state, VAPICROMState), - VMSTATE_UINT32(real_tpr_addr, VAPICROMState), - VMSTATE_UINT32(rom_state_vaddr, VAPICROMState), - VMSTATE_UINT32(vapic_paddr, VAPICROMState), - VMSTATE_UINT32(rom_state_paddr, VAPICROMState), - VMSTATE_END_OF_LIST() - } -}; - -static void vapic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = vapic_reset; - dc->vmsd = &vmstate_vapic; - dc->realize = vapic_realize; -} - -static const TypeInfo vapic_type = { - .name = TYPE_VAPIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(VAPICROMState), - .class_init = vapic_class_init, -}; - -static void vapic_register(void) -{ - type_register_static(&vapic_type); -} - -type_init(vapic_register); diff --git a/qemu/hw/i386/multiboot.c b/qemu/hw/i386/multiboot.c deleted file mode 100644 index 387caa67d..000000000 --- a/qemu/hw/i386/multiboot.c +++ /dev/null @@ -1,375 +0,0 @@ -/* - * QEMU PC System Emulator - * - * Copyright (c) 2003-2004 Fabrice Bellard - * - * 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 "qemu-common.h" -#include "cpu.h" -#include "hw/hw.h" -#include "hw/nvram/fw_cfg.h" -#include "multiboot.h" -#include "hw/loader.h" -#include "elf.h" -#include "sysemu/sysemu.h" - -/* Show multiboot debug output */ -//#define DEBUG_MULTIBOOT - -#ifdef DEBUG_MULTIBOOT -#define mb_debug(a...) fprintf(stderr, ## a) -#else -#define mb_debug(a...) -#endif - -#define MULTIBOOT_STRUCT_ADDR 0x9000 - -#if MULTIBOOT_STRUCT_ADDR > 0xf0000 -#error multiboot struct needs to fit in 16 bit real mode -#endif - -enum { - /* Multiboot info */ - MBI_FLAGS = 0, - MBI_MEM_LOWER = 4, - MBI_MEM_UPPER = 8, - MBI_BOOT_DEVICE = 12, - MBI_CMDLINE = 16, - MBI_MODS_COUNT = 20, - MBI_MODS_ADDR = 24, - MBI_MMAP_ADDR = 48, - MBI_BOOTLOADER = 64, - - MBI_SIZE = 88, - - /* Multiboot modules */ - MB_MOD_START = 0, - MB_MOD_END = 4, - MB_MOD_CMDLINE = 8, - - MB_MOD_SIZE = 16, - - /* Region offsets */ - ADDR_E820_MAP = MULTIBOOT_STRUCT_ADDR + 0, - ADDR_MBI = ADDR_E820_MAP + 0x500, - - /* Multiboot flags */ - MULTIBOOT_FLAGS_MEMORY = 1 << 0, - MULTIBOOT_FLAGS_BOOT_DEVICE = 1 << 1, - MULTIBOOT_FLAGS_CMDLINE = 1 << 2, - MULTIBOOT_FLAGS_MODULES = 1 << 3, - MULTIBOOT_FLAGS_MMAP = 1 << 6, - MULTIBOOT_FLAGS_BOOTLOADER = 1 << 9, -}; - -typedef struct { - /* buffer holding kernel, cmdlines and mb_infos */ - void *mb_buf; - /* address in target */ - hwaddr mb_buf_phys; - /* size of mb_buf in bytes */ - unsigned mb_buf_size; - /* offset of mb-info's in bytes */ - hwaddr offset_mbinfo; - /* offset in buffer for cmdlines in bytes */ - hwaddr offset_cmdlines; - /* offset in buffer for bootloader name in bytes */ - hwaddr offset_bootloader; - /* offset of modules in bytes */ - hwaddr offset_mods; - /* available slots for mb modules infos */ - int mb_mods_avail; - /* currently used slots of mb modules */ - int mb_mods_count; -} MultibootState; - -const char *bootloader_name = "qemu"; - -static uint32_t mb_add_cmdline(MultibootState *s, const char *cmdline) -{ - hwaddr p = s->offset_cmdlines; - char *b = (char *)s->mb_buf + p; - - get_opt_value(b, strlen(cmdline) + 1, cmdline); - s->offset_cmdlines += strlen(b) + 1; - return s->mb_buf_phys + p; -} - -static uint32_t mb_add_bootloader(MultibootState *s, const char *bootloader) -{ - hwaddr p = s->offset_bootloader; - char *b = (char *)s->mb_buf + p; - - memcpy(b, bootloader, strlen(bootloader) + 1); - s->offset_bootloader += strlen(b) + 1; - return s->mb_buf_phys + p; -} - -static void mb_add_mod(MultibootState *s, - hwaddr start, hwaddr end, - hwaddr cmdline_phys) -{ - char *p; - assert(s->mb_mods_count < s->mb_mods_avail); - - p = (char *)s->mb_buf + s->offset_mbinfo + MB_MOD_SIZE * s->mb_mods_count; - - stl_p(p + MB_MOD_START, start); - stl_p(p + MB_MOD_END, end); - stl_p(p + MB_MOD_CMDLINE, cmdline_phys); - - mb_debug("mod%02d: "TARGET_FMT_plx" - "TARGET_FMT_plx"\n", - s->mb_mods_count, start, end); - - s->mb_mods_count++; -} - -int load_multiboot(FWCfgState *fw_cfg, - FILE *f, - const char *kernel_filename, - const char *initrd_filename, - const char *kernel_cmdline, - int kernel_file_size, - uint8_t *header) -{ - int i, is_multiboot = 0; - uint32_t flags = 0; - uint32_t mh_entry_addr; - uint32_t mh_load_addr; - uint32_t mb_kernel_size; - MultibootState mbs; - uint8_t bootinfo[MBI_SIZE]; - uint8_t *mb_bootinfo_data; - uint32_t cmdline_len; - - /* Ok, let's see if it is a multiboot image. - The header is 12x32bit long, so the latest entry may be 8192 - 48. */ - for (i = 0; i < (8192 - 48); i += 4) { - if (ldl_p(header+i) == 0x1BADB002) { - uint32_t checksum = ldl_p(header+i+8); - flags = ldl_p(header+i+4); - checksum += flags; - checksum += (uint32_t)0x1BADB002; - if (!checksum) { - is_multiboot = 1; - break; - } - } - } - - if (!is_multiboot) - return 0; /* no multiboot */ - - mb_debug("qemu: I believe we found a multiboot image!\n"); - memset(bootinfo, 0, sizeof(bootinfo)); - memset(&mbs, 0, sizeof(mbs)); - - if (flags & 0x00000004) { /* MULTIBOOT_HEADER_HAS_VBE */ - fprintf(stderr, "qemu: multiboot knows VBE. we don't.\n"); - } - if (!(flags & 0x00010000)) { /* MULTIBOOT_HEADER_HAS_ADDR */ - uint64_t elf_entry; - uint64_t elf_low, elf_high; - int kernel_size; - fclose(f); - - if (((struct elf64_hdr*)header)->e_machine == EM_X86_64) { - fprintf(stderr, "Cannot load x86-64 image, give a 32bit one.\n"); - exit(1); - } - - kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, - &elf_low, &elf_high, 0, I386_ELF_MACHINE, - 0, 0); - if (kernel_size < 0) { - fprintf(stderr, "Error while loading elf kernel\n"); - exit(1); - } - mh_load_addr = elf_low; - mb_kernel_size = elf_high - elf_low; - mh_entry_addr = elf_entry; - - mbs.mb_buf = g_malloc(mb_kernel_size); - if (rom_copy(mbs.mb_buf, mh_load_addr, mb_kernel_size) != mb_kernel_size) { - fprintf(stderr, "Error while fetching elf kernel from rom\n"); - exit(1); - } - - mb_debug("qemu: loading multiboot-elf kernel (%#x bytes) with entry %#zx\n", - mb_kernel_size, (size_t)mh_entry_addr); - } else { - /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */ - uint32_t mh_header_addr = ldl_p(header+i+12); - uint32_t mh_load_end_addr = ldl_p(header+i+20); - uint32_t mh_bss_end_addr = ldl_p(header+i+24); - mh_load_addr = ldl_p(header+i+16); - uint32_t mb_kernel_text_offset = i - (mh_header_addr - mh_load_addr); - uint32_t mb_load_size = 0; - mh_entry_addr = ldl_p(header+i+28); - - if (mh_load_end_addr) { - mb_kernel_size = mh_bss_end_addr - mh_load_addr; - mb_load_size = mh_load_end_addr - mh_load_addr; - } else { - mb_kernel_size = kernel_file_size - mb_kernel_text_offset; - mb_load_size = mb_kernel_size; - } - - /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_VBE. - uint32_t mh_mode_type = ldl_p(header+i+32); - uint32_t mh_width = ldl_p(header+i+36); - uint32_t mh_height = ldl_p(header+i+40); - uint32_t mh_depth = ldl_p(header+i+44); */ - - mb_debug("multiboot: mh_header_addr = %#x\n", mh_header_addr); - mb_debug("multiboot: mh_load_addr = %#x\n", mh_load_addr); - mb_debug("multiboot: mh_load_end_addr = %#x\n", mh_load_end_addr); - mb_debug("multiboot: mh_bss_end_addr = %#x\n", mh_bss_end_addr); - mb_debug("qemu: loading multiboot kernel (%#x bytes) at %#x\n", - mb_load_size, mh_load_addr); - - mbs.mb_buf = g_malloc(mb_kernel_size); - fseek(f, mb_kernel_text_offset, SEEK_SET); - if (fread(mbs.mb_buf, 1, mb_load_size, f) != mb_load_size) { - fprintf(stderr, "fread() failed\n"); - exit(1); - } - memset(mbs.mb_buf + mb_load_size, 0, mb_kernel_size - mb_load_size); - fclose(f); - } - - mbs.mb_buf_phys = mh_load_addr; - - mbs.mb_buf_size = TARGET_PAGE_ALIGN(mb_kernel_size); - mbs.offset_mbinfo = mbs.mb_buf_size; - - /* Calculate space for cmdlines, bootloader name, and mb_mods */ - cmdline_len = strlen(kernel_filename) + 1; - cmdline_len += strlen(kernel_cmdline) + 1; - if (initrd_filename) { - const char *r = initrd_filename; - cmdline_len += strlen(r) + 1; - mbs.mb_mods_avail = 1; - while (*(r = get_opt_value(NULL, 0, r))) { - mbs.mb_mods_avail++; - r++; - } - } - - mbs.mb_buf_size += cmdline_len; - mbs.mb_buf_size += MB_MOD_SIZE * mbs.mb_mods_avail; - mbs.mb_buf_size += strlen(bootloader_name) + 1; - - mbs.mb_buf_size = TARGET_PAGE_ALIGN(mbs.mb_buf_size); - - /* enlarge mb_buf to hold cmdlines, bootloader, mb-info structs */ - mbs.mb_buf = g_realloc(mbs.mb_buf, mbs.mb_buf_size); - mbs.offset_cmdlines = mbs.offset_mbinfo + mbs.mb_mods_avail * MB_MOD_SIZE; - mbs.offset_bootloader = mbs.offset_cmdlines + cmdline_len; - - if (initrd_filename) { - char *next_initrd, not_last; - - mbs.offset_mods = mbs.mb_buf_size; - - do { - char *next_space; - int mb_mod_length; - uint32_t offs = mbs.mb_buf_size; - - next_initrd = (char *)get_opt_value(NULL, 0, initrd_filename); - not_last = *next_initrd; - *next_initrd = '\0'; - /* if a space comes after the module filename, treat everything - after that as parameters */ - hwaddr c = mb_add_cmdline(&mbs, initrd_filename); - if ((next_space = strchr(initrd_filename, ' '))) - *next_space = '\0'; - mb_debug("multiboot loading module: %s\n", initrd_filename); - mb_mod_length = get_image_size(initrd_filename); - if (mb_mod_length < 0) { - fprintf(stderr, "Failed to open file '%s'\n", initrd_filename); - exit(1); - } - - mbs.mb_buf_size = TARGET_PAGE_ALIGN(mb_mod_length + mbs.mb_buf_size); - mbs.mb_buf = g_realloc(mbs.mb_buf, mbs.mb_buf_size); - - load_image(initrd_filename, (unsigned char *)mbs.mb_buf + offs); - mb_add_mod(&mbs, mbs.mb_buf_phys + offs, - mbs.mb_buf_phys + offs + mb_mod_length, c); - - mb_debug("mod_start: %p\nmod_end: %p\n cmdline: "TARGET_FMT_plx"\n", - (char *)mbs.mb_buf + offs, - (char *)mbs.mb_buf + offs + mb_mod_length, c); - initrd_filename = next_initrd+1; - } while (not_last); - } - - /* Commandline support */ - char kcmdline[strlen(kernel_filename) + strlen(kernel_cmdline) + 2]; - snprintf(kcmdline, sizeof(kcmdline), "%s %s", - kernel_filename, kernel_cmdline); - stl_p(bootinfo + MBI_CMDLINE, mb_add_cmdline(&mbs, kcmdline)); - - stl_p(bootinfo + MBI_BOOTLOADER, mb_add_bootloader(&mbs, bootloader_name)); - - stl_p(bootinfo + MBI_MODS_ADDR, mbs.mb_buf_phys + mbs.offset_mbinfo); - stl_p(bootinfo + MBI_MODS_COUNT, mbs.mb_mods_count); /* mods_count */ - - /* the kernel is where we want it to be now */ - stl_p(bootinfo + MBI_FLAGS, MULTIBOOT_FLAGS_MEMORY - | MULTIBOOT_FLAGS_BOOT_DEVICE - | MULTIBOOT_FLAGS_CMDLINE - | MULTIBOOT_FLAGS_MODULES - | MULTIBOOT_FLAGS_MMAP - | MULTIBOOT_FLAGS_BOOTLOADER); - stl_p(bootinfo + MBI_BOOT_DEVICE, 0x8000ffff); /* XXX: use the -boot switch? */ - stl_p(bootinfo + MBI_MMAP_ADDR, ADDR_E820_MAP); - - mb_debug("multiboot: mh_entry_addr = %#x\n", mh_entry_addr); - mb_debug(" mb_buf_phys = "TARGET_FMT_plx"\n", mbs.mb_buf_phys); - mb_debug(" mod_start = "TARGET_FMT_plx"\n", mbs.mb_buf_phys + mbs.offset_mods); - mb_debug(" mb_mods_count = %d\n", mbs.mb_mods_count); - - /* save bootinfo off the stack */ - mb_bootinfo_data = g_malloc(sizeof(bootinfo)); - memcpy(mb_bootinfo_data, bootinfo, sizeof(bootinfo)); - - /* Pass variables to option rom */ - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, mh_entry_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, mbs.mb_buf_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, - mbs.mb_buf, mbs.mb_buf_size); - - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, ADDR_MBI); - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, sizeof(bootinfo)); - fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, mb_bootinfo_data, - sizeof(bootinfo)); - - option_rom[nb_option_roms].name = "multiboot.bin"; - option_rom[nb_option_roms].bootindex = 0; - nb_option_roms++; - - return 1; /* yes, we are multiboot */ -} diff --git a/qemu/hw/i386/multiboot.h b/qemu/hw/i386/multiboot.h deleted file mode 100644 index 60de309cd..000000000 --- a/qemu/hw/i386/multiboot.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef QEMU_MULTIBOOT_H -#define QEMU_MULTIBOOT_H - -#include "hw/nvram/fw_cfg.h" - -int load_multiboot(FWCfgState *fw_cfg, - FILE *f, - const char *kernel_filename, - const char *initrd_filename, - const char *kernel_cmdline, - int kernel_file_size, - uint8_t *header); - -#endif diff --git a/qemu/hw/i386/pc.c b/qemu/hw/i386/pc.c deleted file mode 100644 index 99437e0b7..000000000 --- a/qemu/hw/i386/pc.c +++ /dev/null @@ -1,2017 +0,0 @@ -/* - * QEMU PC System Emulator - * - * Copyright (c) 2003-2004 Fabrice Bellard - * - * 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/i386/pc.h" -#include "hw/char/serial.h" -#include "hw/i386/apic.h" -#include "hw/i386/topology.h" -#include "sysemu/cpus.h" -#include "hw/block/fdc.h" -#include "hw/ide.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_bus.h" -#include "hw/nvram/fw_cfg.h" -#include "hw/timer/hpet.h" -#include "hw/smbios/smbios.h" -#include "hw/loader.h" -#include "elf.h" -#include "multiboot.h" -#include "hw/timer/mc146818rtc.h" -#include "hw/timer/i8254.h" -#include "hw/audio/pcspk.h" -#include "hw/pci/msi.h" -#include "hw/sysbus.h" -#include "sysemu/sysemu.h" -#include "sysemu/numa.h" -#include "sysemu/kvm.h" -#include "sysemu/qtest.h" -#include "kvm_i386.h" -#include "hw/xen/xen.h" -#include "sysemu/block-backend.h" -#include "hw/block/block.h" -#include "ui/qemu-spice.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" -#include "sysemu/arch_init.h" -#include "qemu/bitmap.h" -#include "qemu/config-file.h" -#include "qemu/error-report.h" -#include "hw/acpi/acpi.h" -#include "hw/acpi/cpu_hotplug.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 - -#ifdef DEBUG_IRQ -#define DPRINTF(fmt, ...) \ - do { printf("CPUIRQ: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) -#endif - -#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) -#define FW_CFG_E820_TABLE (FW_CFG_ARCH_LOCAL + 3) -#define FW_CFG_HPET (FW_CFG_ARCH_LOCAL + 4) - -#define E820_NR_ENTRIES 16 - -struct e820_entry { - uint64_t address; - uint64_t length; - uint32_t type; -} QEMU_PACKED __attribute((__aligned__(4))); - -struct e820_table { - uint32_t count; - struct e820_entry entry[E820_NR_ENTRIES]; -} QEMU_PACKED __attribute((__aligned__(4))); - -static struct e820_table e820_reserve; -static struct e820_entry *e820_table; -static unsigned e820_entries; -struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX}; - -void gsi_handler(void *opaque, int n, int level) -{ - GSIState *s = opaque; - - DPRINTF("pc: %s GSI %d\n", level ? "raising" : "lowering", n); - if (n < ISA_NUM_IRQS) { - qemu_set_irq(s->i8259_irq[n], level); - } - qemu_set_irq(s->ioapic_irq[n], level); -} - -static void ioport80_write(void *opaque, hwaddr addr, uint64_t data, - unsigned size) -{ -} - -static uint64_t ioport80_read(void *opaque, hwaddr addr, unsigned size) -{ - return 0xffffffffffffffffULL; -} - -/* MSDOS compatibility mode FPU exception support */ -static qemu_irq ferr_irq; - -void pc_register_ferr_irq(qemu_irq irq) -{ - ferr_irq = irq; -} - -/* XXX: add IGNNE support */ -void cpu_set_ferr(CPUX86State *s) -{ - qemu_irq_raise(ferr_irq); -} - -static void ioportF0_write(void *opaque, hwaddr addr, uint64_t data, - unsigned size) -{ - qemu_irq_lower(ferr_irq); -} - -static uint64_t ioportF0_read(void *opaque, hwaddr addr, unsigned size) -{ - return 0xffffffffffffffffULL; -} - -/* TSC handling */ -uint64_t cpu_get_tsc(CPUX86State *env) -{ - return cpu_get_ticks(); -} - -/* IRQ handling */ -int cpu_get_pic_interrupt(CPUX86State *env) -{ - X86CPU *cpu = x86_env_get_cpu(env); - int intno; - - intno = apic_get_interrupt(cpu->apic_state); - if (intno >= 0) { - return intno; - } - /* read the irq from the PIC */ - if (!apic_accept_pic_intr(cpu->apic_state)) { - return -1; - } - - intno = pic_read_irq(isa_pic); - return intno; -} - -static void pic_irq_request(void *opaque, int irq, int level) -{ - CPUState *cs = first_cpu; - X86CPU *cpu = X86_CPU(cs); - - DPRINTF("pic_irqs: %s irq %d\n", level? "raise" : "lower", irq); - if (cpu->apic_state) { - CPU_FOREACH(cs) { - cpu = X86_CPU(cs); - if (apic_accept_pic_intr(cpu->apic_state)) { - apic_deliver_pic_intr(cpu->apic_state, level); - } - } - } else { - if (level) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } - } -} - -/* PC cmos mappings */ - -#define REG_EQUIPMENT_BYTE 0x14 - -int cmos_get_fd_drive_type(FloppyDriveType fd0) -{ - int val; - - switch (fd0) { - case FLOPPY_DRIVE_TYPE_144: - /* 1.44 Mb 3"5 drive */ - val = 4; - break; - case FLOPPY_DRIVE_TYPE_288: - /* 2.88 Mb 3"5 drive */ - val = 5; - break; - case FLOPPY_DRIVE_TYPE_120: - /* 1.2 Mb 5"5 drive */ - val = 2; - break; - case FLOPPY_DRIVE_TYPE_NONE: - default: - val = 0; - break; - } - return val; -} - -static void cmos_init_hd(ISADevice *s, int type_ofs, int info_ofs, - int16_t cylinders, int8_t heads, int8_t sectors) -{ - rtc_set_memory(s, type_ofs, 47); - rtc_set_memory(s, info_ofs, cylinders); - rtc_set_memory(s, info_ofs + 1, cylinders >> 8); - rtc_set_memory(s, info_ofs + 2, heads); - rtc_set_memory(s, info_ofs + 3, 0xff); - rtc_set_memory(s, info_ofs + 4, 0xff); - rtc_set_memory(s, info_ofs + 5, 0xc0 | ((heads > 8) << 3)); - rtc_set_memory(s, info_ofs + 6, cylinders); - rtc_set_memory(s, info_ofs + 7, cylinders >> 8); - rtc_set_memory(s, info_ofs + 8, sectors); -} - -/* convert boot_device letter to something recognizable by the bios */ -static int boot_device2nibble(char boot_device) -{ - switch(boot_device) { - case 'a': - case 'b': - return 0x01; /* floppy boot */ - case 'c': - return 0x02; /* hard drive boot */ - case 'd': - return 0x03; /* CD-ROM boot */ - case 'n': - return 0x04; /* Network boot */ - } - return 0; -} - -static void set_boot_dev(ISADevice *s, const char *boot_device, Error **errp) -{ -#define PC_MAX_BOOT_DEVICES 3 - int nbds, bds[3] = { 0, }; - int i; - - nbds = strlen(boot_device); - if (nbds > PC_MAX_BOOT_DEVICES) { - error_setg(errp, "Too many boot devices for PC"); - return; - } - for (i = 0; i < nbds; i++) { - bds[i] = boot_device2nibble(boot_device[i]); - if (bds[i] == 0) { - error_setg(errp, "Invalid boot device for PC: '%c'", - boot_device[i]); - return; - } - } - rtc_set_memory(s, 0x3d, (bds[1] << 4) | bds[0]); - rtc_set_memory(s, 0x38, (bds[2] << 4) | (fd_bootchk ? 0x0 : 0x1)); -} - -static void pc_boot_set(void *opaque, const char *boot_device, Error **errp) -{ - set_boot_dev(opaque, boot_device, errp); -} - -static void pc_cmos_init_floppy(ISADevice *rtc_state, ISADevice *floppy) -{ - int val, nb, i; - FloppyDriveType fd_type[2] = { FLOPPY_DRIVE_TYPE_NONE, - FLOPPY_DRIVE_TYPE_NONE }; - - /* floppy type */ - if (floppy) { - for (i = 0; i < 2; i++) { - fd_type[i] = isa_fdc_get_drive_type(floppy, i); - } - } - val = (cmos_get_fd_drive_type(fd_type[0]) << 4) | - cmos_get_fd_drive_type(fd_type[1]); - rtc_set_memory(rtc_state, 0x10, val); - - val = rtc_get_memory(rtc_state, REG_EQUIPMENT_BYTE); - nb = 0; - if (fd_type[0] != FLOPPY_DRIVE_TYPE_NONE) { - nb++; - } - if (fd_type[1] != FLOPPY_DRIVE_TYPE_NONE) { - nb++; - } - switch (nb) { - case 0: - break; - case 1: - val |= 0x01; /* 1 drive, ready for boot */ - break; - case 2: - val |= 0x41; /* 2 drives, ready for boot */ - break; - } - rtc_set_memory(rtc_state, REG_EQUIPMENT_BYTE, val); -} - -typedef struct pc_cmos_init_late_arg { - ISADevice *rtc_state; - BusState *idebus[2]; -} pc_cmos_init_late_arg; - -typedef struct check_fdc_state { - ISADevice *floppy; - bool multiple; -} CheckFdcState; - -static int check_fdc(Object *obj, void *opaque) -{ - CheckFdcState *state = opaque; - Object *fdc; - uint32_t iobase; - Error *local_err = NULL; - - fdc = object_dynamic_cast(obj, TYPE_ISA_FDC); - if (!fdc) { - return 0; - } - - iobase = object_property_get_int(obj, "iobase", &local_err); - if (local_err || iobase != 0x3f0) { - error_free(local_err); - return 0; - } - - if (state->floppy) { - state->multiple = true; - } else { - state->floppy = ISA_DEVICE(obj); - } - return 0; -} - -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; - ISADevice *s = arg->rtc_state; - int16_t cylinders; - int8_t heads, sectors; - int val; - int i, trans; - - val = 0; - if (ide_get_geometry(arg->idebus[0], 0, - &cylinders, &heads, §ors) >= 0) { - cmos_init_hd(s, 0x19, 0x1b, cylinders, heads, sectors); - val |= 0xf0; - } - if (ide_get_geometry(arg->idebus[0], 1, - &cylinders, &heads, §ors) >= 0) { - cmos_init_hd(s, 0x1a, 0x24, cylinders, heads, sectors); - val |= 0x0f; - } - rtc_set_memory(s, 0x12, val); - - val = 0; - for (i = 0; i < 4; i++) { - /* NOTE: ide_get_geometry() returns the physical - geometry. It is always such that: 1 <= sects <= 63, 1 - <= heads <= 16, 1 <= cylinders <= 16383. The BIOS - geometry can be different if a translation is done. */ - if (ide_get_geometry(arg->idebus[i / 2], i % 2, - &cylinders, &heads, §ors) >= 0) { - trans = ide_get_bios_chs_trans(arg->idebus[i / 2], i % 2) - 1; - assert((trans & ~3) == 0); - val |= trans << (i * 2); - } - } - rtc_set_memory(s, 0x39, val); - - pc_cmos_init_floppy(s, pc_find_fdc0()); - - qemu_unregister_reset(pc_cmos_init_late, opaque); -} - -void pc_cmos_init(PCMachineState *pcms, - BusState *idebus0, BusState *idebus1, - ISADevice *s) -{ - int val; - static pc_cmos_init_late_arg arg; - - /* various important CMOS locations needed by PC/Bochs bios */ - - /* memory size */ - /* base memory (first MiB) */ - 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 (pcms->below_4g_mem_size > 1024 * 1024) { - val = (pcms->below_4g_mem_size - 1024 * 1024) / 1024; - } else { - val = 0; - } - if (val > 65535) - val = 65535; - rtc_set_memory(s, 0x17, val); - rtc_set_memory(s, 0x18, val >> 8); - rtc_set_memory(s, 0x30, val); - rtc_set_memory(s, 0x31, val >> 8); - /* memory between 16MiB and 4GiB */ - if (pcms->below_4g_mem_size > 16 * 1024 * 1024) { - val = (pcms->below_4g_mem_size - 16 * 1024 * 1024) / 65536; - } else { - val = 0; - } - if (val > 65535) - val = 65535; - rtc_set_memory(s, 0x34, val); - rtc_set_memory(s, 0x35, val >> 8); - /* memory above 4GiB */ - 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); - - /* set the number of CPU */ - rtc_set_memory(s, 0x5f, smp_cpus - 1); - - object_property_add_link(OBJECT(pcms), "rtc_state", - TYPE_ISA_DEVICE, - (Object **)&pcms->rtc, - object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); - object_property_set_link(OBJECT(pcms), OBJECT(s), - "rtc_state", &error_abort); - - set_boot_dev(s, MACHINE(pcms)->boot_order, &error_fatal); - - val = 0; - val |= 0x02; /* FPU is there */ - val |= 0x04; /* PS/2 mouse installed */ - rtc_set_memory(s, REG_EQUIPMENT_BYTE, val); - - /* hard drives and FDC */ - arg.rtc_state = s; - arg.idebus[0] = idebus0; - arg.idebus[1] = idebus1; - qemu_register_reset(pc_cmos_init_late, &arg); -} - -#define TYPE_PORT92 "port92" -#define PORT92(obj) OBJECT_CHECK(Port92State, (obj), TYPE_PORT92) - -/* port 92 stuff: could be split off */ -typedef struct Port92State { - ISADevice parent_obj; - - MemoryRegion io; - uint8_t outport; - qemu_irq *a20_out; -} Port92State; - -static void port92_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - Port92State *s = opaque; - int oldval = s->outport; - - DPRINTF("port92: write 0x%02" PRIx64 "\n", val); - s->outport = val; - qemu_set_irq(*s->a20_out, (val >> 1) & 1); - if ((val & 1) && !(oldval & 1)) { - qemu_system_reset_request(); - } -} - -static uint64_t port92_read(void *opaque, hwaddr addr, - unsigned size) -{ - Port92State *s = opaque; - uint32_t ret; - - ret = s->outport; - DPRINTF("port92: read 0x%02x\n", ret); - return ret; -} - -static void port92_init(ISADevice *dev, qemu_irq *a20_out) -{ - Port92State *s = PORT92(dev); - - s->a20_out = a20_out; -} - -static const VMStateDescription vmstate_port92_isa = { - .name = "port92", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(outport, Port92State), - VMSTATE_END_OF_LIST() - } -}; - -static void port92_reset(DeviceState *d) -{ - Port92State *s = PORT92(d); - - s->outport &= ~1; -} - -static const MemoryRegionOps port92_ops = { - .read = port92_read, - .write = port92_write, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void port92_initfn(Object *obj) -{ - Port92State *s = PORT92(obj); - - memory_region_init_io(&s->io, OBJECT(s), &port92_ops, s, "port92", 1); - - s->outport = 0; -} - -static void port92_realizefn(DeviceState *dev, Error **errp) -{ - ISADevice *isadev = ISA_DEVICE(dev); - Port92State *s = PORT92(dev); - - isa_register_ioport(isadev, &s->io, 0x92); -} - -static void port92_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = port92_realizefn; - dc->reset = port92_reset; - dc->vmsd = &vmstate_port92_isa; - /* - * Reason: unlike ordinary ISA devices, this one needs additional - * wiring: its A20 output line needs to be wired up by - * port92_init(). - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo port92_info = { - .name = TYPE_PORT92, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(Port92State), - .instance_init = port92_initfn, - .class_init = port92_class_initfn, -}; - -static void port92_register_types(void) -{ - type_register_static(&port92_info); -} - -type_init(port92_register_types) - -static void handle_a20_line_change(void *opaque, int irq, int level) -{ - X86CPU *cpu = opaque; - - /* XXX: send to all CPUs ? */ - /* XXX: add logic to handle multiple A20 line sources */ - x86_cpu_set_a20(cpu, level); -} - -int e820_add_entry(uint64_t address, uint64_t length, uint32_t type) -{ - int index = le32_to_cpu(e820_reserve.count); - struct e820_entry *entry; - - if (type != E820_RAM) { - /* old FW_CFG_E820_TABLE entry -- reservations only */ - if (index >= E820_NR_ENTRIES) { - return -EBUSY; - } - entry = &e820_reserve.entry[index++]; - - entry->address = cpu_to_le64(address); - entry->length = cpu_to_le64(length); - entry->type = cpu_to_le32(type); - - e820_reserve.count = cpu_to_le32(index); - } - - /* new "etc/e820" file -- include ram too */ - e820_table = g_renew(struct e820_entry, e820_table, e820_entries + 1); - e820_table[e820_entries].address = cpu_to_le64(address); - e820_table[e820_entries].length = cpu_to_le64(length); - e820_table[e820_entries].type = cpu_to_le32(type); - e820_entries++; - - return e820_entries; -} - -int e820_get_num_entries(void) -{ - return e820_entries; -} - -bool e820_get_entry(int idx, uint32_t type, uint64_t *address, uint64_t *length) -{ - if (idx < e820_entries && e820_table[idx].type == cpu_to_le32(type)) { - *address = le64_to_cpu(e820_table[idx].address); - *length = le64_to_cpu(e820_table[idx].length); - return true; - } - return false; -} - -/* Enables contiguous-apic-ID mode, for compatibility */ -static bool compat_apic_id_mode; - -void enable_compat_apic_id_mode(void) -{ - compat_apic_id_mode = true; -} - -/* Calculates initial APIC ID for a specific CPU index - * - * Currently we need to be able to calculate the APIC ID from the CPU index - * alone (without requiring a CPU object), as the QEMU<->Seabios interfaces have - * no concept of "CPU index", and the NUMA tables on fw_cfg need the APIC ID of - * all CPUs up to max_cpus. - */ -static uint32_t x86_cpu_apic_id_from_index(unsigned int cpu_index) -{ - uint32_t correct_id; - static bool warned; - - correct_id = x86_apicid_from_cpu_idx(smp_cores, smp_threads, cpu_index); - if (compat_apic_id_mode) { - if (cpu_index != correct_id && !warned && !qtest_enabled()) { - error_report("APIC IDs set in compatibility mode, " - "CPU topology won't match the configuration"); - warned = true; - } - return cpu_index; - } else { - return correct_id; - } -} - -static void pc_build_smbios(FWCfgState *fw_cfg) -{ - 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(AddressSpace *as, PCMachineState *pcms) -{ - FWCfgState *fw_cfg; - uint64_t *numa_fw_cfg; - int i, j; - - 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 - * QEMU<->SeaBIOS interface is not based on the "CPU index", but on the APIC - * ID of hotplugged CPUs[1]. This means that FW_CFG_MAX_CPUS is not the - * "maximum number of CPUs", but the "limit to the APIC ID values SeaBIOS - * may see". - * - * So, this means we must not use max_cpus, here, but the maximum possible - * APIC ID value, plus one. - * - * [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)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()); - - pc_build_smbios(fw_cfg); - - fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, - &e820_reserve, sizeof(e820_reserve)); - fw_cfg_add_file(fw_cfg, "etc/e820", e820_table, - sizeof(struct e820_entry) * e820_entries); - - fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, &hpet_cfg, sizeof(hpet_cfg)); - /* allocate memory for the NUMA channel: one (64bit) word for the number - * 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 + 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 < 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); - break; - } - } - } - for (i = 0; i < nb_numa_nodes; i++) { - 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 + pcms->apic_id_limit + nb_numa_nodes) * - sizeof(*numa_fw_cfg)); - - return fw_cfg; -} - -static long get_file_size(FILE *f) -{ - long where, size; - - /* XXX: on Unix systems, using fstat() probably makes more sense */ - - where = ftell(f); - fseek(f, 0, SEEK_END); - size = ftell(f); - fseek(f, where, SEEK_SET); - - return size; -} - -static void load_linux(PCMachineState *pcms, - FWCfgState *fw_cfg) -{ - uint16_t protocol; - int setup_size, kernel_size, initrd_size = 0, cmdline_size; - uint32_t initrd_max; - uint8_t header[8192], *setup, *kernel, *initrd_data; - 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; - - /* load the kernel header */ - f = fopen(kernel_filename, "rb"); - if (!f || !(kernel_size = get_file_size(f)) || - fread(header, 1, MIN(ARRAY_SIZE(header), kernel_size), f) != - MIN(ARRAY_SIZE(header), kernel_size)) { - fprintf(stderr, "qemu: could not load kernel '%s': %s\n", - kernel_filename, strerror(errno)); - exit(1); - } - - /* kernel protocol version */ -#if 0 - fprintf(stderr, "header magic: %#x\n", ldl_p(header+0x202)); -#endif - if (ldl_p(header+0x202) == 0x53726448) { - protocol = lduw_p(header+0x206); - } else { - /* This looks like a multiboot kernel. If it is, let's stop - treating it like a Linux kernel. */ - if (load_multiboot(fw_cfg, f, kernel_filename, initrd_filename, - kernel_cmdline, kernel_size, header)) { - return; - } - protocol = 0; - } - - if (protocol < 0x200 || !(header[0x211] & 0x01)) { - /* Low kernel */ - real_addr = 0x90000; - cmdline_addr = 0x9a000 - cmdline_size; - prot_addr = 0x10000; - } else if (protocol < 0x202) { - /* High but ancient kernel */ - real_addr = 0x90000; - cmdline_addr = 0x9a000 - cmdline_size; - prot_addr = 0x100000; - } else { - /* High and recent kernel */ - real_addr = 0x10000; - cmdline_addr = 0x20000; - prot_addr = 0x100000; - } - -#if 0 - fprintf(stderr, - "qemu: real_addr = 0x" TARGET_FMT_plx "\n" - "qemu: cmdline_addr = 0x" TARGET_FMT_plx "\n" - "qemu: prot_addr = 0x" TARGET_FMT_plx "\n", - real_addr, - cmdline_addr, - prot_addr); -#endif - - /* highest address for loading the initrd */ - if (protocol >= 0x203) { - initrd_max = ldl_p(header+0x22c); - } else { - initrd_max = 0x37ffffff; - } - - 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); - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline)+1); - fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); - - if (protocol >= 0x202) { - stl_p(header+0x228, cmdline_addr); - } else { - stw_p(header+0x20, 0xA33F); - stw_p(header+0x22, cmdline_addr-real_addr); - } - - /* handle vga= parameter */ - vmode = strstr(kernel_cmdline, "vga="); - if (vmode) { - unsigned int video_mode; - /* skip "vga=" */ - vmode += 4; - if (!strncmp(vmode, "normal", 6)) { - video_mode = 0xffff; - } else if (!strncmp(vmode, "ext", 3)) { - video_mode = 0xfffe; - } else if (!strncmp(vmode, "ask", 3)) { - video_mode = 0xfffd; - } else { - video_mode = strtol(vmode, NULL, 0); - } - stw_p(header+0x1fa, video_mode); - } - - /* loader type */ - /* High nybble = B reserved for QEMU; low nybble is revision number. - If this code is substantially changed, you may want to consider - incrementing the revision. */ - if (protocol >= 0x200) { - header[0x210] = 0xB0; - } - /* heap */ - if (protocol >= 0x201) { - header[0x211] |= 0x80; /* CAN_USE_HEAP */ - stw_p(header+0x224, cmdline_addr-real_addr-0x200); - } - - /* load initrd */ - if (initrd_filename) { - if (protocol < 0x200) { - fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n"); - exit(1); - } - - initrd_size = get_image_size(initrd_filename); - if (initrd_size < 0) { - fprintf(stderr, "qemu: error reading initrd %s: %s\n", - initrd_filename, strerror(errno)); - exit(1); - } - - initrd_addr = (initrd_max-initrd_size) & ~4095; - - initrd_data = g_malloc(initrd_size); - load_image(initrd_filename, initrd_data); - - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size); - - stl_p(header+0x218, initrd_addr); - stl_p(header+0x21c, initrd_size); - } - - /* load kernel and setup */ - setup_size = header[0x1f1]; - if (setup_size == 0) { - 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); - kernel = g_malloc(kernel_size); - fseek(f, 0, SEEK_SET); - if (fread(setup, 1, setup_size, f) != setup_size) { - fprintf(stderr, "fread() failed\n"); - exit(1); - } - if (fread(kernel, 1, kernel_size, f) != kernel_size) { - fprintf(stderr, "fread() failed\n"); - exit(1); - } - fclose(f); - memcpy(setup, header, MIN(sizeof(header), setup_size)); - - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size); - - fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size); - - option_rom[nb_option_roms].name = "linuxboot.bin"; - option_rom[nb_option_roms].bootindex = 0; - nb_option_roms++; -} - -#define NE2000_NB_MAX 6 - -static const int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, - 0x280, 0x380 }; -static const int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 }; - -void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd) -{ - static int nb_ne2k = 0; - - if (nb_ne2k == NE2000_NB_MAX) - return; - isa_ne2000_init(bus, ne2000_io[nb_ne2k], - ne2000_irq[nb_ne2k], nd); - nb_ne2k++; -} - -DeviceState *cpu_get_current_apic(void) -{ - if (current_cpu) { - X86CPU *cpu = X86_CPU(current_cpu); - return cpu->apic_state; - } else { - return NULL; - } -} - -void pc_acpi_smi_interrupt(void *opaque, int irq, int level) -{ - X86CPU *cpu = opaque; - - if (level) { - cpu_interrupt(CPU(cpu), CPU_INTERRUPT_SMI); - } -} - -static X86CPU *pc_new_cpu(const char *cpu_model, int64_t apic_id, - Error **errp) -{ - X86CPU *cpu = NULL; - Error *local_err = NULL; - - cpu = cpu_x86_create(cpu_model, &local_err); - if (local_err != NULL) { - goto out; - } - - object_property_set_int(OBJECT(cpu), apic_id, "apic-id", &local_err); - object_property_set_bool(OBJECT(cpu), true, "realized", &local_err); - -out: - if (local_err) { - error_propagate(errp, local_err); - object_unref(OBJECT(cpu)); - cpu = NULL; - } - return cpu; -} - -void pc_hot_add_cpu(const int64_t id, Error **errp) -{ - X86CPU *cpu; - MachineState *machine = MACHINE(qdev_get_machine()); - int64_t apic_id = x86_cpu_apic_id_from_index(id); - Error *local_err = NULL; - - if (id < 0) { - error_setg(errp, "Invalid CPU id: %" PRIi64, id); - return; - } - - if (cpu_exists(apic_id)) { - error_setg(errp, "Unable to add CPU: %" PRIi64 - ", it already exists", id); - return; - } - - if (id >= max_cpus) { - error_setg(errp, "Unable to add CPU: %" PRIi64 - ", max allowed: %d", id, max_cpus - 1); - return; - } - - if (apic_id >= ACPI_CPU_HOTPLUG_ID_LIMIT) { - error_setg(errp, "Unable to add CPU: %" PRIi64 - ", resulting APIC ID (%" PRIi64 ") is too large", - id, apic_id); - return; - } - - cpu = pc_new_cpu(machine->cpu_model, apic_id, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - object_unref(OBJECT(cpu)); -} - -void pc_cpus_init(PCMachineState *pcms) -{ - int i; - X86CPU *cpu = NULL; - MachineState *machine = MACHINE(pcms); - - /* init CPUs */ - if (machine->cpu_model == NULL) { -#ifdef TARGET_X86_64 - machine->cpu_model = "qemu64"; -#else - machine->cpu_model = "qemu32"; -#endif - } - - /* 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); - } - - 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)); - } - } - - /* tell smbios about cpuid version and features */ - smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]); -} - -/* pci-info ROM file. Little endian format */ -typedef struct PcRomPciInfo { - uint64_t w32_min; - uint64_t w32_max; - uint64_t w64_min; - uint64_t w64_max; -} PcRomPciInfo; - -static -void pc_machine_done(Notifier *notifier, void *data) -{ - PCMachineState *pcms = container_of(notifier, - PCMachineState, machine_done); - PCIBus *bus = pcms->bus; - - if (bus) { - int extra_hosts = 0; - - QLIST_FOREACH(bus, &bus->child, sibling) { - /* look for expander root buses */ - if (pci_bus_is_root(bus)) { - extra_hosts++; - } - } - if (extra_hosts && pcms->fw_cfg) { - uint64_t *val = g_malloc(sizeof(*val)); - *val = cpu_to_le64(extra_hosts); - fw_cfg_add_file(pcms->fw_cfg, - "etc/extra-pci-roots", val, sizeof(*val)); - } - } - - acpi_setup(); -} - -void pc_guest_info_init(PCMachineState *pcms) -{ - int i, j; - - 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++) { - pcms->node_mem[i] = numa_info[i].node_mem; - } - - 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 < pcms->apic_id_limit); - for (j = 0; j < nb_numa_nodes; j++) { - if (test_bit(i, numa_info[j].node_cpu)) { - pcms->node_cpu[apic_id] = j; - break; - } - } - } - - 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 */ -void pc_pci_as_mapping_init(Object *owner, MemoryRegion *system_memory, - MemoryRegion *pci_address_space) -{ - /* Set to lower priority than RAM */ - memory_region_add_subregion_overlap(system_memory, 0x0, - pci_address_space, -1); -} - -void pc_acpi_init(const char *default_dsdt) -{ - char *filename; - - if (acpi_tables != NULL) { - /* manually set via -acpitable, leave it alone */ - return; - } - - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, default_dsdt); - if (filename == NULL) { - fprintf(stderr, "WARNING: failed to find %s\n", default_dsdt); - } else { - QemuOpts *opts = qemu_opts_create(qemu_find_opts("acpi"), NULL, 0, - &error_abort); - Error *err = NULL; - - qemu_opt_set(opts, "file", filename, &error_abort); - - acpi_table_add_builtin(opts, &err); - if (err) { - error_reportf_err(err, "WARNING: failed to load %s: ", - filename); - } - g_free(filename); - } -} - -void xen_load_linux(PCMachineState *pcms) -{ - int i; - FWCfgState *fw_cfg; - - assert(MACHINE(pcms)->kernel_filename != NULL); - - fw_cfg = fw_cfg_init_io(FW_CFG_IO_BASE); - rom_set_fw(fw_cfg); - - 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); - } - pcms->fw_cfg = fw_cfg; -} - -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; - MachineState *machine = MACHINE(pcms); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); - - assert(machine->ram_size == pcms->below_4g_mem_size + - pcms->above_4g_mem_size); - - linux_boot = (machine->kernel_filename != NULL); - - /* Allocate RAM. We allocate it as a single memory region and use - * aliases to address portions of it, mostly for backwards compatibility - * with older qemus that used qemu_ram_alloc(). - */ - ram = g_malloc(sizeof(*ram)); - memory_region_allocate_system_memory(ram, NULL, "pc.ram", - machine->ram_size); - *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, pcms->below_4g_mem_size); - memory_region_add_subregion(system_memory, 0, ram_below_4g); - 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, - pcms->below_4g_mem_size, - pcms->above_4g_mem_size); - memory_region_add_subregion(system_memory, 0x100000000ULL, - ram_above_4g); - e820_add_entry(0x100000000ULL, pcms->above_4g_mem_size, E820_RAM); - } - - if (!pcmc->has_reserved_memory && - (machine->ram_slots || - (machine->maxram_size > machine->ram_size))) { - MachineClass *mc = MACHINE_GET_CLASS(machine); - - error_report("\"-memory 'slots|maxmem'\" is not supported by: %s", - mc->name); - exit(EXIT_FAILURE); - } - - /* initialize hotplug memory address space */ - if (pcmc->has_reserved_memory && - (machine->ram_size < machine->maxram_size)) { - ram_addr_t hotplug_mem_size = - machine->maxram_size - machine->ram_size; - - if (machine->ram_slots > ACPI_MAX_RAM_SLOTS) { - error_report("unsupported amount of memory slots: %"PRIu64, - machine->ram_slots); - exit(EXIT_FAILURE); - } - - if (QEMU_ALIGN_UP(machine->maxram_size, - TARGET_PAGE_SIZE) != machine->maxram_size) { - error_report("maximum memory size must by aligned to multiple of " - "%d bytes", TARGET_PAGE_SIZE); - exit(EXIT_FAILURE); - } - - pcms->hotplug_memory.base = - ROUND_UP(0x100000000ULL + pcms->above_4g_mem_size, 1ULL << 30); - - if (pcmc->enforce_aligned_dimm) { - /* size hotplug region assuming 1G page max alignment per slot */ - hotplug_mem_size += (1ULL << 30) * machine->ram_slots; - } - - if ((pcms->hotplug_memory.base + hotplug_mem_size) < - hotplug_mem_size) { - error_report("unsupported amount of maximum memory: " RAM_ADDR_FMT, - machine->maxram_size); - exit(EXIT_FAILURE); - } - - memory_region_init(&pcms->hotplug_memory.mr, OBJECT(pcms), - "hotplug-memory", hotplug_mem_size); - memory_region_add_subregion(system_memory, pcms->hotplug_memory.base, - &pcms->hotplug_memory.mr); - } - - /* Initialize PC system firmware */ - 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_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(&address_space_memory, pcms); - - rom_set_fw(fw_cfg); - - if (pcmc->has_reserved_memory && pcms->hotplug_memory.base) { - uint64_t *val = g_malloc(sizeof(*val)); - 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(pcms, fw_cfg); - } - - for (i = 0; i < nb_option_roms; i++) { - rom_add_option(option_rom[i].name, option_rom[i].bootindex); - } - pcms->fw_cfg = fw_cfg; -} - -qemu_irq pc_allocate_cpu_irq(void) -{ - return qemu_allocate_irq(pic_irq_request, NULL, 0); -} - -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; - } else if (isa_bus) { - ISADevice *isadev = isa_vga_init(isa_bus); - dev = isadev ? DEVICE(isadev) : NULL; - } - rom_reset_order_override(); - return dev; -} - -static const MemoryRegionOps ioport80_io_ops = { - .write = ioport80_write, - .read = ioport80_read, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static const MemoryRegionOps ioportF0_io_ops = { - .write = ioportF0_write, - .read = ioportF0_read, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, - ISADevice **rtc_state, - bool create_fdctrl, - bool no_vmport, - uint32_t hpet_irqs) -{ - int i; - DriveInfo *fd[MAX_FD]; - DeviceState *hpet = NULL; - int pit_isa_irq = 0; - qemu_irq pit_alt_irq = NULL; - qemu_irq rtc_irq = NULL; - qemu_irq *a20_line; - ISADevice *i8042, *port92, *vmmouse, *pit = NULL; - MemoryRegion *ioport80_io = g_new(MemoryRegion, 1); - MemoryRegion *ioportF0_io = g_new(MemoryRegion, 1); - - memory_region_init_io(ioport80_io, NULL, &ioport80_io_ops, NULL, "ioport80", 1); - memory_region_add_subregion(isa_bus->address_space_io, 0x80, ioport80_io); - - memory_region_init_io(ioportF0_io, NULL, &ioportF0_io_ops, NULL, "ioportF0", 1); - memory_region_add_subregion(isa_bus->address_space_io, 0xf0, ioportF0_io); - - /* - * Check if an HPET shall be created. - * - * Without KVM_CAP_PIT_STATE2, we cannot switch off the in-kernel PIT - * when the HPET wants to take over. Thus we have to disable the latter. - */ - if (!no_hpet && (!kvm_irqchip_in_kernel() || kvm_has_pit_state2())) { - /* In order to set property, here not using sysbus_try_create_simple */ - hpet = qdev_try_create(NULL, TYPE_HPET); - if (hpet) { - /* For pc-piix-*, hpet's intcap is always IRQ2. For pc-q35-1.7 - * and earlier, use IRQ2 for compat. Otherwise, use IRQ16~23, - * IRQ8 and IRQ2. - */ - uint8_t compat = object_property_get_int(OBJECT(hpet), - HPET_INTCAP, NULL); - if (!compat) { - qdev_prop_set_uint32(hpet, HPET_INTCAP, hpet_irqs); - } - qdev_init_nofail(hpet); - sysbus_mmio_map(SYS_BUS_DEVICE(hpet), 0, HPET_BASE); - - for (i = 0; i < GSI_NUM_PINS; i++) { - sysbus_connect_irq(SYS_BUS_DEVICE(hpet), i, gsi[i]); - } - pit_isa_irq = -1; - pit_alt_irq = qdev_get_gpio_in(hpet, HPET_LEGACY_PIT_INT); - rtc_irq = qdev_get_gpio_in(hpet, HPET_LEGACY_RTC_INT); - } - } - *rtc_state = rtc_init(isa_bus, 2000, rtc_irq); - - qemu_register_boot_set(pc_boot_set, *rtc_state); - - if (!xen_enabled()) { - 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); - } - if (hpet) { - /* connect PIT to output control line of the HPET */ - qdev_connect_gpio_out(hpet, 0, qdev_get_gpio_in(DEVICE(pit), 0)); - } - pcspk_init(isa_bus, pit); - } - - serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS); - parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS); - - a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2); - i8042 = isa_create_simple(isa_bus, "i8042"); - i8042_setup_a20_line(i8042, &a20_line[0]); - if (!no_vmport) { - vmport_init(isa_bus); - vmmouse = isa_try_create(isa_bus, "vmmouse"); - } else { - vmmouse = NULL; - } - if (vmmouse) { - DeviceState *dev = DEVICE(vmmouse); - qdev_prop_set_ptr(dev, "ps2_mouse", i8042); - qdev_init_nofail(dev); - } - port92 = isa_create_simple(isa_bus, "port92"); - port92_init(port92, &a20_line[1]); - - DMA_init(isa_bus, 0); - - for(i = 0; i < MAX_FD; i++) { - fd[i] = drive_get(IF_FLOPPY, 0, i); - create_fdctrl |= !!fd[i]; - } - if (create_fdctrl) { - fdctrl_init_isa(isa_bus, fd); - } -} - -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]; - - if (!pci_bus || (nd->model && strcmp(nd->model, "ne2k_isa") == 0)) { - pc_init_ne2k_isa(isa_bus, nd); - } else { - pci_nic_init_nofail(nd, pci_bus, "e1000", NULL); - } - } - rom_reset_order_override(); -} - -void pc_pci_device_init(PCIBus *pci_bus) -{ - int max_bus; - int bus; - - max_bus = drive_get_max_bus(IF_SCSI); - for (bus = 0; bus <= max_bus; bus++) { - pci_create_simple(pci_bus, -1, "lsi53c895a"); - } -} - -void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) -{ - DeviceState *dev; - SysBusDevice *d; - unsigned int i; - - if (kvm_ioapic_in_kernel()) { - dev = qdev_create(NULL, "kvm-ioapic"); - } else { - dev = qdev_create(NULL, "ioapic"); - } - if (parent_name) { - object_property_add_child(object_resolve_path(parent_name, NULL), - "ioapic", OBJECT(dev), NULL); - } - qdev_init_nofail(dev); - d = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(d, 0, IO_APIC_DEFAULT_ADDRESS); - - for (i = 0; i < IOAPIC_NUM_PINS; i++) { - gsi_state->ioapic_irq[i] = qdev_get_gpio_in(dev, i); - } -} - -static void pc_dimm_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - 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) && pcmc->enforce_aligned_dimm) { - align = memory_region_get_alignment(mr); - } - - if (!pcms->acpi_dev) { - error_setg(&local_err, - "memory hotplug is not enabled: missing acpi device"); - goto out; - } - - pc_dimm_memory_plug(dev, &pcms->hotplug_memory, mr, align, &local_err); - if (local_err) { - goto out; - } - - hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); - hhc->plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &error_abort); -out: - error_propagate(errp, local_err); -} - -static void pc_dimm_unplug_request(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - HotplugHandlerClass *hhc; - Error *local_err = NULL; - PCMachineState *pcms = PC_MACHINE(hotplug_dev); - - if (!pcms->acpi_dev) { - error_setg(&local_err, - "memory hotplug is not enabled: missing acpi device"); - goto out; - } - - hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); - hhc->unplug_request(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); - -out: - error_propagate(errp, local_err); -} - -static void pc_dimm_unplug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - PCMachineState *pcms = PC_MACHINE(hotplug_dev); - PCDIMMDevice *dimm = PC_DIMM(dev); - PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *mr = ddc->get_memory_region(dimm); - HotplugHandlerClass *hhc; - Error *local_err = NULL; - - hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); - hhc->unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); - - if (local_err) { - goto out; - } - - pc_dimm_memory_unplug(dev, &pcms->hotplug_memory, mr); - object_unparent(OBJECT(dev)); - - out: - 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); - - if (!dev->hotplugged) { - goto out; - } - - if (!pcms->acpi_dev) { - error_setg(&local_err, - "cpu hotplug is not enabled: missing acpi device"); - goto out; - } - - hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); - hhc->plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); - if (local_err) { - goto out; - } - - /* 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); -} - -static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - pc_dimm_plug(hotplug_dev, dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { - pc_cpu_plug(hotplug_dev, dev, errp); - } -} - -static void pc_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - pc_dimm_unplug_request(hotplug_dev, dev, errp); - } else { - error_setg(errp, "acpi: device unplug request for not supported device" - " type: %s", object_get_typename(OBJECT(dev))); - } -} - -static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - pc_dimm_unplug(hotplug_dev, dev, errp); - } else { - error_setg(errp, "acpi: device unplug for not supported device" - " type: %s", object_get_typename(OBJECT(dev))); - } -} - -static HotplugHandler *pc_get_hotpug_handler(MachineState *machine, - DeviceState *dev) -{ - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(machine); - - if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) || - object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { - return HOTPLUG_HANDLER(machine); - } - - return pcmc->get_hotplug_handler ? - pcmc->get_hotplug_handler(machine, dev) : NULL; -} - -static void -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, name, &value, errp); -} - -static void pc_machine_get_max_ram_below_4g(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - PCMachineState *pcms = PC_MACHINE(obj); - uint64_t value = pcms->max_ram_below_4g; - - visit_type_size(v, name, &value, errp); -} - -static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - PCMachineState *pcms = PC_MACHINE(obj); - Error *error = NULL; - uint64_t value; - - visit_type_size(v, name, &value, &error); - if (error) { - error_propagate(errp, error); - return; - } - if (value > (1ULL << 32)) { - error_setg(&error, - "Machine option 'max-ram-below-4g=%"PRIu64 - "' expects size less than or equal to 4G", value); - error_propagate(errp, error); - return; - } - - if (value < (1ULL << 20)) { - error_report("Warning: small max_ram_below_4g(%"PRIu64 - ") less than 1M. BIOS may not work..", - value); - } - - pcms->max_ram_below_4g = value; -} - -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, name, &vmport, 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, name, &pcms->vmport, errp); -} - -bool pc_machine_is_smm_enabled(PCMachineState *pcms) -{ - bool smm_available = false; - - if (pcms->smm == ON_OFF_AUTO_OFF) { - return false; - } - - if (tcg_enabled() || qtest_enabled()) { - smm_available = true; - } else if (kvm_enabled()) { - smm_available = kvm_has_smm(); - } - - if (smm_available) { - return true; - } - - if (pcms->smm == ON_OFF_AUTO_ON) { - error_report("System Management Mode not supported by this hypervisor."); - exit(1); - } - return false; -} - -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, 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 bool pc_machine_get_nvdimm(Object *obj, Error **errp) -{ - PCMachineState *pcms = PC_MACHINE(obj); - - return pcms->acpi_nvdimm_state.is_enabled; -} - -static void pc_machine_set_nvdimm(Object *obj, bool value, Error **errp) -{ - PCMachineState *pcms = PC_MACHINE(obj); - - pcms->acpi_nvdimm_state.is_enabled = value; -} - -static void pc_machine_initfn(Object *obj) -{ - PCMachineState *pcms = PC_MACHINE(obj); - - object_property_add(obj, PC_MACHINE_MEMHP_REGION_SIZE, "int", - pc_machine_get_hotplug_memory_region_size, - 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, &error_abort); - object_property_set_description(obj, PC_MACHINE_MAX_RAM_BELOW_4G, - "Maximum ram below the 4G boundary (32bit boundary)", - &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, &error_abort); - object_property_set_description(obj, PC_MACHINE_SMM, - "Enable SMM (pc & q35)", - &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, &error_abort); - object_property_set_description(obj, PC_MACHINE_VMPORT, - "Enable vmport (pc & q35)", - &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); - - if (cpu->apic_state) { - device_reset(cpu->apic_state); - } - } -} - -static unsigned pc_cpu_index_to_socket_id(unsigned cpu_index) -{ - X86CPUTopoInfo topo; - x86_topo_ids_from_idx(smp_cores, smp_threads, cpu_index, - &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) -{ - MachineClass *mc = MACHINE_CLASS(oc); - PCMachineClass *pcmc = PC_MACHINE_CLASS(oc); - 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; -} - -static const TypeInfo pc_machine_info = { - .name = TYPE_PC_MACHINE, - .parent = TYPE_MACHINE, - .abstract = true, - .instance_size = sizeof(PCMachineState), - .instance_init = pc_machine_initfn, - .class_size = sizeof(PCMachineClass), - .class_init = pc_machine_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - }, -}; - -static void pc_machine_register_types(void) -{ - type_register_static(&pc_machine_info); -} - -type_init(pc_machine_register_types) diff --git a/qemu/hw/i386/pc_piix.c b/qemu/hw/i386/pc_piix.c deleted file mode 100644 index 7f50116bc..000000000 --- a/qemu/hw/i386/pc_piix.c +++ /dev/null @@ -1,1062 +0,0 @@ -/* - * QEMU PC System Emulator - * - * Copyright (c) 2003-2004 Fabrice Bellard - * - * 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 - -#include "hw/hw.h" -#include "hw/loader.h" -#include "hw/i386/pc.h" -#include "hw/i386/apic.h" -#include "hw/smbios/smbios.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_ids.h" -#include "hw/usb.h" -#include "net/net.h" -#include "hw/boards.h" -#include "hw/ide.h" -#include "sysemu/kvm.h" -#include "hw/kvm/clock.h" -#include "sysemu/sysemu.h" -#include "hw/sysbus.h" -#include "sysemu/arch_init.h" -#include "sysemu/block-backend.h" -#include "hw/i2c/smbus.h" -#include "hw/xen/xen.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" -#include "hw/acpi/acpi.h" -#include "cpu.h" -#include "qemu/error-report.h" -#ifdef CONFIG_XEN -#include -#include "hw/xen/xen_pt.h" -#endif -#include "migration/migration.h" -#include "kvm_i386.h" - -#define MAX_IDE_BUS 2 - -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 }; - -/* PC hardware initialisation */ -static void pc_init1(MachineState *machine, - const char *host_type, const char *pci_type) -{ - 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; - PCIBus *pci_bus; - ISABus *isa_bus; - PCII440FXState *i440fx_state; - int piix3_devfn = -1; - qemu_irq *gsi; - qemu_irq *i8259; - qemu_irq smi_irq; - GSIState *gsi_state; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - BusState *idebus[MAX_IDE_BUS]; - ISADevice *rtc_state; - MemoryRegion *ram_memory; - MemoryRegion *pci_memory; - MemoryRegion *rom_memory; - ram_addr_t lowmem; - - /* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory). - * 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 >= 0xe0000000) { - lowmem = pcmc->gigabyte_align ? 0xc0000000 : 0xe0000000; - } else { - lowmem = 0xe0000000; - } - - /* Handle the machine opt max-ram-below-4g. It is basically doing - * min(qemu limit, user limit). - */ - 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.", - pcms->max_ram_below_4g); - } - } - - if (machine->ram_size >= lowmem) { - pcms->above_4g_mem_size = machine->ram_size - lowmem; - pcms->below_4g_mem_size = lowmem; - } else { - pcms->above_4g_mem_size = 0; - pcms->below_4g_mem_size = machine->ram_size; - } - - if (xen_enabled()) { - xen_hvm_init(pcms, &ram_memory); - } - - pc_cpus_init(pcms); - - if (kvm_enabled() && pcmc->kvmclock_enabled) { - kvmclock_create(); - } - - if (pcmc->pci_enabled) { - pci_memory = g_new(MemoryRegion, 1); - memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); - rom_memory = pci_memory; - } else { - pci_memory = NULL; - rom_memory = system_memory; - } - - pc_guest_info_init(pcms); - - 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, pcmc->smbios_legacy_mode, - pcmc->smbios_uuid_encoded, - SMBIOS_ENTRY_POINT_21); - } - - /* allocate ram and load rom/bios */ - if (!xen_enabled()) { - 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(pcms); - } - - gsi_state = g_malloc0(sizeof(*gsi_state)); - 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 (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, - 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, - &error_abort); - no_hpet = 1; - } - isa_bus_irqs(isa_bus, gsi); - - if (kvm_pic_in_kernel()) { - i8259 = kvm_i8259_init(isa_bus); - } else if (xen_enabled()) { - i8259 = xen_interrupt_controller_init(); - } else { - i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq()); - } - - for (i = 0; i < ISA_NUM_IRQS; i++) { - gsi_state->i8259_irq[i] = i8259[i]; - } - g_free(i8259); - if (pcmc->pci_enabled) { - ioapic_init_gsi(gsi_state, "i440fx"); - } - - pc_register_ferr_irq(gsi[13]); - - pc_vga_init(isa_bus, pcmc->pci_enabled ? pci_bus : NULL); - - 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, - (pcms->vmport != ON_OFF_AUTO_ON), 0x4); - - pc_nic_init(isa_bus, pci_bus); - - ide_drive_get(hd, ARRAY_SIZE(hd)); - if (pcmc->pci_enabled) { - PCIDevice *dev; - if (xen_enabled()) { - dev = pci_piix3_xen_ide_init(pci_bus, hd, piix3_devfn + 1); - } else { - dev = pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1); - } - idebus[0] = qdev_get_child_bus(&dev->qdev, "ide.0"); - idebus[1] = qdev_get_child_bus(&dev->qdev, "ide.1"); - } else { - for(i = 0; i < MAX_IDE_BUS; i++) { - ISADevice *dev; - char busname[] = "ide.0"; - dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], - ide_irq[i], - hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); - /* - * The ide bus name is ide.0 for the first bus and ide.1 for the - * second one. - */ - busname[4] = '0' + i; - idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); - } - } - - pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state); - - if (pcmc->pci_enabled && usb_enabled()) { - pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci"); - } - - if (pcmc->pci_enabled && acpi_enabled) { - DeviceState *piix4_pm; - I2CBus *smbus; - - smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0); - /* TODO: Populate SPD eeprom data. */ - smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100, - gsi[9], smi_irq, - 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 **)&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 (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); - savevm_skip_section_footers(); - if (kvm_enabled()) { - pcms->smm = ON_OFF_AUTO_OFF; - } - global_state_set_optional(); - savevm_skip_configuration(); -} - -static void pc_compat_2_2(MachineState *machine) -{ - pc_compat_2_3(machine); - machine->suppress_vmdesc = true; -} - -static void pc_compat_2_1(MachineState *machine) -{ - pc_compat_2_2(machine); - x86_cpu_change_kvm_default("svm", NULL); -} - -static void pc_compat_2_0(MachineState *machine) -{ - pc_compat_2_1(machine); -} - -static void pc_compat_1_7(MachineState *machine) -{ - pc_compat_2_0(machine); - x86_cpu_change_kvm_default("x2apic", NULL); -} - -static void pc_compat_1_6(MachineState *machine) -{ - pc_compat_1_7(machine); -} - -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); -} - -static void pc_compat_1_3(MachineState *machine) -{ - pc_compat_1_4(machine); - enable_compat_apic_id_mode(); -} - -/* PC compat function for pc-0.14 to pc-1.2 */ -static void pc_compat_1_2(MachineState *machine) -{ - pc_compat_1_3(machine); - 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); -} - -static void pc_init_isa(MachineState *machine) -{ - if (!machine->cpu_model) { - machine->cpu_model = "486"; - } - x86_cpu_change_kvm_default("kvm-pv-eoi", NULL); - enable_compat_apic_id_mode(); - 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; - - 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) { - pci_create_simple(bus, -1, "xen-platform"); - } -} -#endif - -#define DEFINE_I440FX_MACHINE(suffix, name, compatfn, optionfn) \ - static void pc_init_##suffix(MachineState *machine) \ - { \ - void (*compat)(MachineState *m) = (compatfn); \ - if (compat) { \ - compat(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) -{ - 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_6_machine_options(MachineClass *m) -{ - pc_i440fx_machine_options(m); - 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) - - -static void pc_i440fx_2_3_machine_options(MachineClass *m) -{ - pc_i440fx_2_4_machine_options(m); - m->hw_version = "2.3.0"; - SET_MACHINE_COMPAT(m, PC_COMPAT_2_3); -} - -DEFINE_I440FX_MACHINE(v2_3, "pc-i440fx-2.3", pc_compat_2_3, - pc_i440fx_2_3_machine_options); - - -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, - pc_i440fx_2_2_machine_options); - - -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, - pc_i440fx_2_1_machine_options); - - - -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, - pc_i440fx_2_0_machine_options); - - -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, - pc_i440fx_1_7_machine_options); - - -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, - pc_i440fx_1_6_machine_options); - - -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); -} - -DEFINE_I440FX_MACHINE(v1_5, "pc-i440fx-1.5", pc_compat_1_5, - pc_i440fx_1_5_machine_options); - - -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); -} - -DEFINE_I440FX_MACHINE(v1_4, "pc-i440fx-1.4", pc_compat_1_4, - pc_i440fx_1_4_machine_options); - - -#define PC_COMPAT_1_3 \ - PC_COMPAT_1_4 \ - {\ - .driver = "usb-tablet",\ - .property = "usb_version",\ - .value = stringify(1),\ - },{\ - .driver = "virtio-net-pci",\ - .property = "ctrl_mac_addr",\ - .value = "off", \ - },{ \ - .driver = "virtio-net-pci", \ - .property = "mq", \ - .value = "off", \ - }, {\ - .driver = "e1000",\ - .property = "autonegotiation",\ - .value = "off",\ - }, - - -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); -} - -DEFINE_I440FX_MACHINE(v1_3, "pc-1.3", pc_compat_1_3, - pc_i440fx_1_3_machine_options); - - -#define PC_COMPAT_1_2 \ - PC_COMPAT_1_3 \ - {\ - .driver = "nec-usb-xhci",\ - .property = "msi",\ - .value = "off",\ - },{\ - .driver = "nec-usb-xhci",\ - .property = "msix",\ - .value = "off",\ - },{\ - .driver = "ivshmem",\ - .property = "use64",\ - .value = "0",\ - },{\ - .driver = "qxl",\ - .property = "revision",\ - .value = stringify(3),\ - },{\ - .driver = "qxl-vga",\ - .property = "revision",\ - .value = stringify(3),\ - },{\ - .driver = "VGA",\ - .property = "mmio",\ - .value = "off",\ - }, - -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); -} - -DEFINE_I440FX_MACHINE(v1_2, "pc-1.2", pc_compat_1_2, - pc_i440fx_1_2_machine_options); - - -#define PC_COMPAT_1_1 \ - PC_COMPAT_1_2 \ - {\ - .driver = "virtio-scsi-pci",\ - .property = "hotplug",\ - .value = "off",\ - },{\ - .driver = "virtio-scsi-pci",\ - .property = "param_change",\ - .value = "off",\ - },{\ - .driver = "VGA",\ - .property = "vgamem_mb",\ - .value = stringify(8),\ - },{\ - .driver = "vmware-svga",\ - .property = "vgamem_mb",\ - .value = stringify(8),\ - },{\ - .driver = "qxl-vga",\ - .property = "vgamem_mb",\ - .value = stringify(8),\ - },{\ - .driver = "qxl",\ - .property = "vgamem_mb",\ - .value = stringify(8),\ - },{\ - .driver = "virtio-blk-pci",\ - .property = "config-wce",\ - .value = "off",\ - }, - -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); -} - -DEFINE_I440FX_MACHINE(v1_1, "pc-1.1", pc_compat_1_2, - pc_i440fx_1_1_machine_options); - - -#define PC_COMPAT_1_0 \ - PC_COMPAT_1_1 \ - {\ - .driver = TYPE_ISA_FDC,\ - .property = "check_media_rate",\ - .value = "off",\ - }, {\ - .driver = "virtio-balloon-pci",\ - .property = "class",\ - .value = stringify(PCI_CLASS_MEMORY_RAM),\ - },{\ - .driver = "apic-common",\ - .property = "vapic",\ - .value = "off",\ - },{\ - .driver = TYPE_USB_DEVICE,\ - .property = "full-path",\ - .value = "no",\ - }, - -static void pc_i440fx_1_0_machine_options(MachineClass *m) -{ - pc_i440fx_1_1_machine_options(m); - m->hw_version = "1.0"; - SET_MACHINE_COMPAT(m, PC_COMPAT_1_0); -} - -DEFINE_I440FX_MACHINE(v1_0, "pc-1.0", pc_compat_1_2, - pc_i440fx_1_0_machine_options); - - -#define PC_COMPAT_0_15 \ - PC_COMPAT_1_0 - -static void pc_i440fx_0_15_machine_options(MachineClass *m) -{ - pc_i440fx_1_0_machine_options(m); - m->hw_version = "0.15"; - SET_MACHINE_COMPAT(m, PC_COMPAT_0_15); -} - -DEFINE_I440FX_MACHINE(v0_15, "pc-0.15", pc_compat_1_2, - pc_i440fx_0_15_machine_options); - - -#define PC_COMPAT_0_14 \ - PC_COMPAT_0_15 \ - {\ - .driver = "virtio-blk-pci",\ - .property = "event_idx",\ - .value = "off",\ - },{\ - .driver = "virtio-serial-pci",\ - .property = "event_idx",\ - .value = "off",\ - },{\ - .driver = "virtio-net-pci",\ - .property = "event_idx",\ - .value = "off",\ - },{\ - .driver = "virtio-balloon-pci",\ - .property = "event_idx",\ - .value = "off",\ - },{\ - .driver = "qxl",\ - .property = "revision",\ - .value = stringify(2),\ - },{\ - .driver = "qxl-vga",\ - .property = "revision",\ - .value = stringify(2),\ - }, - -static void pc_i440fx_0_14_machine_options(MachineClass *m) -{ - pc_i440fx_0_15_machine_options(m); - m->hw_version = "0.14"; - SET_MACHINE_COMPAT(m, PC_COMPAT_0_14); -} - -DEFINE_I440FX_MACHINE(v0_14, "pc-0.14", pc_compat_1_2, - pc_i440fx_0_14_machine_options); - - -#define PC_COMPAT_0_13 \ - PC_COMPAT_0_14 \ - {\ - .driver = TYPE_PCI_DEVICE,\ - .property = "command_serr_enable",\ - .value = "off",\ - },{\ - .driver = "AC97",\ - .property = "use_broken_id",\ - .value = stringify(1),\ - },{\ - .driver = "virtio-9p-pci",\ - .property = "vectors",\ - .value = stringify(0),\ - },{\ - .driver = "VGA",\ - .property = "rombar",\ - .value = stringify(0),\ - },{\ - .driver = "vmware-svga",\ - .property = "rombar",\ - .value = stringify(0),\ - }, - -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, - pc_i440fx_0_13_machine_options); - - -#define PC_COMPAT_0_12 \ - PC_COMPAT_0_13 \ - {\ - .driver = "virtio-serial-pci",\ - .property = "max_ports",\ - .value = stringify(1),\ - },{\ - .driver = "virtio-serial-pci",\ - .property = "vectors",\ - .value = stringify(0),\ - },{\ - .driver = "usb-mouse",\ - .property = "serial",\ - .value = "1",\ - },{\ - .driver = "usb-tablet",\ - .property = "serial",\ - .value = "1",\ - },{\ - .driver = "usb-kbd",\ - .property = "serial",\ - .value = "1",\ - }, - -static void pc_i440fx_0_12_machine_options(MachineClass *m) -{ - pc_i440fx_0_13_machine_options(m); - m->hw_version = "0.12"; - SET_MACHINE_COMPAT(m, PC_COMPAT_0_12); -} - -DEFINE_I440FX_MACHINE(v0_12, "pc-0.12", pc_compat_0_13, - pc_i440fx_0_12_machine_options); - - -#define PC_COMPAT_0_11 \ - PC_COMPAT_0_12 \ - {\ - .driver = "virtio-blk-pci",\ - .property = "vectors",\ - .value = stringify(0),\ - },{\ - .driver = TYPE_PCI_DEVICE,\ - .property = "rombar",\ - .value = stringify(0),\ - },{\ - .driver = "ide-drive",\ - .property = "ver",\ - .value = "0.11",\ - },{\ - .driver = "scsi-disk",\ - .property = "ver",\ - .value = "0.11",\ - }, - -static void pc_i440fx_0_11_machine_options(MachineClass *m) -{ - pc_i440fx_0_12_machine_options(m); - m->hw_version = "0.11"; - SET_MACHINE_COMPAT(m, PC_COMPAT_0_11); -} - -DEFINE_I440FX_MACHINE(v0_11, "pc-0.11", pc_compat_0_13, - pc_i440fx_0_11_machine_options); - - -#define PC_COMPAT_0_10 \ - PC_COMPAT_0_11 \ - {\ - .driver = "virtio-blk-pci",\ - .property = "class",\ - .value = stringify(PCI_CLASS_STORAGE_OTHER),\ - },{\ - .driver = "virtio-serial-pci",\ - .property = "class",\ - .value = stringify(PCI_CLASS_DISPLAY_OTHER),\ - },{\ - .driver = "virtio-net-pci",\ - .property = "vectors",\ - .value = stringify(0),\ - },{\ - .driver = "ide-drive",\ - .property = "ver",\ - .value = "0.10",\ - },{\ - .driver = "scsi-disk",\ - .property = "ver",\ - .value = "0.10",\ - }, - -static void pc_i440fx_0_10_machine_options(MachineClass *m) -{ - pc_i440fx_0_11_machine_options(m); - m->hw_version = "0.10"; - SET_MACHINE_COMPAT(m, PC_COMPAT_0_10); -} - -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) -{ - 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, - isapc_machine_options); - - -#ifdef CONFIG_XEN -static void xenfv_machine_options(MachineClass *m) -{ - m->desc = "Xen Fully-virtualized PC"; - m->max_cpus = HVM_MAX_VCPUS; - m->default_machine_opts = "accel=xen"; - m->hot_add_cpu = pc_hot_add_cpu; -} - -DEFINE_PC_MACHINE(xenfv, "xenfv", pc_xen_hvm_init, - xenfv_machine_options); -#endif diff --git a/qemu/hw/i386/pc_q35.c b/qemu/hw/i386/pc_q35.c deleted file mode 100644 index 04aae8958..000000000 --- a/qemu/hw/i386/pc_q35.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Q35 chipset based pc system emulator - * - * Copyright (c) 2003-2004 Fabrice Bellard - * Copyright (c) 2009, 2010 - * Isaku Yamahata - * VA Linux Systems Japan K.K. - * Copyright (C) 2012 Jason Baron - * - * This is based on pc.c, but heavily modified. - * - * 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/loader.h" -#include "sysemu/arch_init.h" -#include "hw/i2c/smbus.h" -#include "hw/boards.h" -#include "hw/timer/mc146818rtc.h" -#include "hw/xen/xen.h" -#include "sysemu/kvm.h" -#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/smbios/smbios.h" -#include "hw/ide/pci.h" -#include "hw/ide/ahci.h" -#include "hw/usb.h" -#include "qemu/error-report.h" -#include "migration/migration.h" - -/* ICH9 AHCI has 6 ports */ -#define MAX_SATA_PORTS 6 - -/* PC hardware initialisation */ -static void pc_q35_init(MachineState *machine) -{ - 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; - qemu_irq *gsi; - qemu_irq *i8259; - int i; - ICH9LPCState *ich9_lpc; - PCIDevice *ahci; - ram_addr_t lowmem; - DriveInfo *hd[MAX_SATA_PORTS]; - MachineClass *mc = MACHINE_GET_CLASS(machine); - - /* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory - * and 256 Mbytes for PCI Express Enhanced Configuration Access Mapping - * also known as MMCFG). - * 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. - */ - if (machine->ram_size >= 0xb0000000) { - lowmem = 0x80000000; - } else { - lowmem = 0xb0000000; - } - - /* Handle the machine opt max-ram-below-4g. It is basically doing - * min(qemu limit, user limit). - */ - 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.", - pcms->max_ram_below_4g); - } - } - - if (machine->ram_size >= lowmem) { - pcms->above_4g_mem_size = machine->ram_size - lowmem; - pcms->below_4g_mem_size = lowmem; - } else { - pcms->above_4g_mem_size = 0; - pcms->below_4g_mem_size = machine->ram_size; - } - - if (xen_enabled()) { - xen_hvm_init(pcms, &ram_memory); - } - - pc_cpus_init(pcms); - - kvmclock_create(); - - /* 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; - } else { - pci_memory = NULL; - rom_memory = get_system_memory(); - } - - pc_guest_info_init(pcms); - - if (pcmc->smbios_defaults) { - /* These values are guest ABI, do not change */ - smbios_set_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)", - 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(pcms, get_system_memory(), - rom_memory, &ram_memory); - } - - /* irq lines */ - gsi_state = g_malloc0(sizeof(*gsi_state)); - 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); - } - - /* create pci host bus */ - q35_host = Q35_HOST_DEVICE(qdev_create(NULL, TYPE_Q35_HOST_DEVICE)); - - object_property_add_child(qdev_get_machine(), "q35", OBJECT(q35_host), NULL); - 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 = 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, - TYPE_ICH9_LPC_DEVICE); - - object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, - TYPE_HOTPLUG_HANDLER, - (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), - PC_MACHINE_ACPI_DEVICE_PROP, &error_abort); - - ich9_lpc = ICH9_LPC_DEVICE(lpc); - ich9_lpc->pic = gsi; - ich9_lpc->ioapic = gsi_state->ioapic_irq; - pci_bus_irqs(host_bus, ich9_lpc_set_irq, ich9_lpc_map_irq, ich9_lpc, - ICH9_LPC_NB_PIRQS); - pci_bus_set_route_irq_fn(host_bus, ich9_route_intx_pin_to_irq); - isa_bus = ich9_lpc->isa_bus; - - /*end early*/ - isa_bus_irqs(isa_bus, gsi); - - if (kvm_pic_in_kernel()) { - i8259 = kvm_i8259_init(isa_bus); - } else if (xen_enabled()) { - i8259 = xen_interrupt_controller_init(); - } else { - i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq()); - } - - for (i = 0; i < ISA_NUM_IRQS; i++) { - gsi_state->i8259_irq[i] = i8259[i]; - } - if (pcmc->pci_enabled) { - ioapic_init_gsi(gsi_state, "q35"); - } - - pc_register_ferr_irq(gsi[13]); - - 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, - (pcms->vmport != ON_OFF_AUTO_ON), 0xff0104); - - /* connect pm stuff to lpc */ - 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, - PCI_DEVFN(ICH9_SATA1_DEV, - ICH9_SATA1_FUNC), - true, "ich9-ahci"); - idebus[0] = qdev_get_child_bus(&ahci->qdev, "ide.0"); - idebus[1] = qdev_get_child_bus(&ahci->qdev, "ide.1"); - g_assert(MAX_SATA_PORTS == ICH_AHCI(ahci)->ahci.ports); - ide_drive_get(hd, ICH_AHCI(ahci)->ahci.ports); - ahci_ide_create_devs(ahci, hd); - - if (usb_enabled()) { - /* Should we create 6 UHCI according to ich9 spec? */ - ehci_create_ich9_with_companions(host_bus, 0x1d); - } - - /* TODO: Populate SPD eeprom data. */ - smbus_eeprom_init(ich9_smb_init(host_bus, - PCI_DEVFN(ICH9_SMB_DEV, ICH9_SMB_FUNC), - 0xb100), - 8, NULL, 0); - - 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 (pcmc->pci_enabled) { - pc_pci_device_init(host_bus); - } - - if (pcms->acpi_nvdimm_state.is_enabled) { - nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io, - pcms->fw_cfg, OBJECT(pcms)); - } -} - -#define DEFINE_Q35_MACHINE(suffix, name, compatfn, optionfn) \ - static void pc_init_##suffix(MachineState *machine) \ - { \ - void (*compat)(MachineState *m) = (compatfn); \ - if (compat) { \ - compat(machine); \ - } \ - pc_q35_init(machine); \ - } \ - DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn) - - -static void pc_q35_machine_options(MachineClass *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; - m->default_machine_opts = "firmware=bios-256k.bin"; - m->default_display = "std"; - m->no_floppy = 1; -} - -static void pc_q35_2_6_machine_options(MachineClass *m) -{ - pc_q35_machine_options(m); - m->alias = "q35"; -} - -DEFINE_Q35_MACHINE(v2_6, "pc-q35-2.6", NULL, - pc_q35_2_6_machine_options); - -static void pc_q35_2_5_machine_options(MachineClass *m) -{ - 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(v2_5, "pc-q35-2.5", NULL, - pc_q35_2_5_machine_options); - -static void pc_q35_2_4_machine_options(MachineClass *m) -{ - 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(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 deleted file mode 100644 index f915ad0a3..000000000 --- a/qemu/hw/i386/pc_sysfw.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - * QEMU PC System Firmware - * - * Copyright (c) 2003-2004 Fabrice Bellard - * Copyright (c) 2011-2012 Intel Corporation - * - * 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 "sysemu/block-backend.h" -#include "qemu/error-report.h" -#include "hw/sysbus.h" -#include "hw/hw.h" -#include "hw/i386/pc.h" -#include "hw/boards.h" -#include "hw/loader.h" -#include "sysemu/sysemu.h" -#include "hw/block/flash.h" -#include "sysemu/kvm.h" - -#define BIOS_FILENAME "bios.bin" - -typedef struct PcSysFwDevice { - SysBusDevice busdev; - uint8_t isapc_ram_fw; -} PcSysFwDevice; - -static void pc_isa_bios_init(MemoryRegion *rom_memory, - MemoryRegion *flash_mem, - int ram_size) -{ - int isa_bios_size; - MemoryRegion *isa_bios; - uint64_t flash_size; - void *flash_ptr, *isa_bios_ptr; - - flash_size = memory_region_size(flash_mem); - - /* map the last 128KB of the BIOS in ISA space */ - 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_fatal); - vmstate_register_ram_global(isa_bios); - memory_region_add_subregion_overlap(rom_memory, - 0x100000 - isa_bios_size, - isa_bios, - 1); - - /* copy ISA rom image from top of flash memory */ - flash_ptr = memory_region_get_ram_ptr(flash_mem); - isa_bios_ptr = memory_region_get_ram_ptr(isa_bios); - memcpy(isa_bios_ptr, - ((uint8_t*)flash_ptr) + (flash_size - isa_bios_size), - isa_bios_size); - - memory_region_set_readonly(isa_bios, true); -} - -#define FLASH_MAP_UNIT_MAX 2 - -/* We don't have a theoretically justifiable exact lower bound on the base - * address of any flash mapping. In practice, the IO-APIC MMIO range is - * [0xFEE00000..0xFEE01000[ -- see IO_APIC_DEFAULT_ADDRESS --, leaving free - * only 18MB-4KB below 4G. For now, restrict the cumulative mapping to 8MB in - * size. - */ -#define FLASH_MAP_BASE_MIN ((hwaddr)(0x100000000ULL - 8*1024*1024)) - -/* This function maps flash drives from 4G downward, in order of their unit - * numbers. The mapping starts at unit#0, with unit number increments of 1, and - * stops before the first missing flash drive, or before - * unit#FLASH_MAP_UNIT_MAX, whichever is reached first. - * - * Addressing within one flash drive is of course not reversed. - * - * An error message is printed and the process exits if: - * - the size of the backing file for a flash drive is non-positive, or not a - * multiple of the required sector size, or - * - the current mapping's base address would fall below FLASH_MAP_BASE_MIN. - * - * The drive with unit#0 (if available) is mapped at the highest address, and - * it is passed to pc_isa_bios_init(). Merging several drives for isa-bios is - * not supported. - */ -static void pc_system_flash_init(MemoryRegion *rom_memory) -{ - int unit; - DriveInfo *pflash_drv; - BlockBackend *blk; - int64_t size; - char *fatal_errmsg = NULL; - hwaddr phys_addr = 0x100000000ULL; - int sector_bits, sector_size; - pflash_t *system_flash; - MemoryRegion *flash_mem; - char name[64]; - - sector_bits = 12; - sector_size = 1 << sector_bits; - - for (unit = 0; - (unit < FLASH_MAP_UNIT_MAX && - (pflash_drv = drive_get(IF_PFLASH, 0, unit)) != NULL); - ++unit) { - blk = blk_by_legacy_dinfo(pflash_drv); - size = blk_getlength(blk); - if (size < 0) { - fatal_errmsg = g_strdup_printf("failed to get backing file size"); - } else if (size == 0) { - fatal_errmsg = g_strdup_printf("PC system firmware (pflash) " - "cannot have zero size"); - } else if ((size % sector_size) != 0) { - fatal_errmsg = g_strdup_printf("PC system firmware (pflash) " - "must be a multiple of 0x%x", sector_size); - } else if (phys_addr < size || phys_addr - size < FLASH_MAP_BASE_MIN) { - fatal_errmsg = g_strdup_printf("oversized backing file, pflash " - "segments cannot be mapped under " - TARGET_FMT_plx, FLASH_MAP_BASE_MIN); - } - if (fatal_errmsg != NULL) { - Location loc; - - /* push a new, "none" location on the location stack; overwrite its - * contents with the location saved in the option; print the error - * (includes location); pop the top - */ - loc_push_none(&loc); - if (pflash_drv->opts != NULL) { - qemu_opts_loc_restore(pflash_drv->opts); - } - error_report("%s", fatal_errmsg); - loc_pop(&loc); - g_free(fatal_errmsg); - exit(1); - } - - phys_addr -= size; - - /* pflash_cfi01_register() creates a deep copy of the name */ - snprintf(name, sizeof name, "system.flash%d", unit); - system_flash = pflash_cfi01_register(phys_addr, NULL /* qdev */, name, - size, blk, sector_size, - size >> sector_bits, - 1 /* width */, - 0x0000 /* id0 */, - 0x0000 /* id1 */, - 0x0000 /* id2 */, - 0x0000 /* id3 */, - 0 /* be */); - if (unit == 0) { - flash_mem = pflash_cfi01_get_memory(system_flash); - pc_isa_bios_init(rom_memory, flash_mem, size); - } - } -} - -static void old_pc_system_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw) -{ - char *filename; - MemoryRegion *bios, *isa_bios; - int bios_size, isa_bios_size; - int ret; - - /* BIOS load */ - if (bios_name == NULL) { - bios_name = BIOS_FILENAME; - } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - bios_size = get_image_size(filename); - } else { - bios_size = -1; - } - if (bios_size <= 0 || - (bios_size % 65536) != 0) { - goto bios_error; - } - bios = g_malloc(sizeof(*bios)); - 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); - } - ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); - if (ret != 0) { - bios_error: - fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name); - exit(1); - } - g_free(filename); - - /* map the last 128KB of the BIOS in ISA space */ - isa_bios_size = bios_size; - if (isa_bios_size > (128 * 1024)) { - isa_bios_size = 128 * 1024; - } - isa_bios = g_malloc(sizeof(*isa_bios)); - memory_region_init_alias(isa_bios, NULL, "isa-bios", bios, - bios_size - isa_bios_size, isa_bios_size); - memory_region_add_subregion_overlap(rom_memory, - 0x100000 - isa_bios_size, - isa_bios, - 1); - if (!isapc_ram_fw) { - memory_region_set_readonly(isa_bios, true); - } - - /* map all the bios at the top of memory */ - memory_region_add_subregion(rom_memory, - (uint32_t)(-bios_size), - bios); -} - -void pc_system_firmware_init(MemoryRegion *rom_memory, bool isapc_ram_fw) -{ - DriveInfo *pflash_drv; - - pflash_drv = drive_get(IF_PFLASH, 0, 0); - - if (isapc_ram_fw || pflash_drv == NULL) { - /* When a pflash drive is not found, use rom-mode */ - old_pc_system_rom_init(rom_memory, isapc_ram_fw); - return; - } - - if (kvm_enabled() && !kvm_readonly_mem_enabled()) { - /* Older KVM cannot execute from device memory. So, flash memory - * cannot be used unless the readonly memory kvm capability is present. */ - fprintf(stderr, "qemu: pflash with kvm requires KVM readonly memory support\n"); - exit(1); - } - - pc_system_flash_init(rom_memory); -} diff --git a/qemu/hw/i386/pci-assign-load-rom.c b/qemu/hw/i386/pci-assign-load-rom.c deleted file mode 100644 index 4bbb08c95..000000000 --- a/qemu/hw/i386/pci-assign-load-rom.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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/xen/Makefile.objs b/qemu/hw/i386/xen/Makefile.objs deleted file mode 100644 index 801a68d32..000000000 --- a/qemu/hw/i386/xen/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -obj-y += xen_platform.o xen_apic.o xen_pvdevice.o diff --git a/qemu/hw/i386/xen/xen_apic.c b/qemu/hw/i386/xen/xen_apic.c deleted file mode 100644 index 21d68ee04..000000000 --- a/qemu/hw/i386/xen/xen_apic.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Xen basic APIC support - * - * Copyright (c) 2012 Citrix - * - * Authors: - * Wei Liu - * - * 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" - -static uint64_t xen_apic_mem_read(void *opaque, hwaddr addr, - unsigned size) -{ - return ~(uint64_t)0; -} - -static void xen_apic_mem_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - if (size != sizeof(uint32_t)) { - fprintf(stderr, "Xen: APIC write data size = %d, invalid\n", size); - return; - } - - xen_hvm_inject_msi(addr, data); -} - -static const MemoryRegionOps xen_apic_io_ops = { - .read = xen_apic_mem_read, - .write = xen_apic_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void xen_apic_realize(DeviceState *dev, Error **errp) -{ - APICCommonState *s = APIC_COMMON(dev); - - s->vapic_control = 0; - memory_region_init_io(&s->io_memory, OBJECT(s), &xen_apic_io_ops, s, - "xen-apic-msi", APIC_SPACE_SIZE); - msi_nonbroken = true; -} - -static void xen_apic_set_base(APICCommonState *s, uint64_t val) -{ -} - -static void xen_apic_set_tpr(APICCommonState *s, uint8_t val) -{ -} - -static uint8_t xen_apic_get_tpr(APICCommonState *s) -{ - return 0; -} - -static void xen_apic_vapic_base_update(APICCommonState *s) -{ -} - -static void xen_apic_external_nmi(APICCommonState *s) -{ -} - -static void xen_apic_class_init(ObjectClass *klass, void *data) -{ - APICCommonClass *k = APIC_COMMON_CLASS(klass); - - k->realize = xen_apic_realize; - k->set_base = xen_apic_set_base; - k->set_tpr = xen_apic_set_tpr; - k->get_tpr = xen_apic_get_tpr; - k->vapic_base_update = xen_apic_vapic_base_update; - k->external_nmi = xen_apic_external_nmi; -} - -static const TypeInfo xen_apic_info = { - .name = "xen-apic", - .parent = TYPE_APIC_COMMON, - .instance_size = sizeof(APICCommonState), - .class_init = xen_apic_class_init, -}; - -static void xen_apic_register_types(void) -{ - type_register_static(&xen_apic_info); -} - -type_init(xen_apic_register_types) diff --git a/qemu/hw/i386/xen/xen_platform.c b/qemu/hw/i386/xen/xen_platform.c deleted file mode 100644 index aa7839324..000000000 --- a/qemu/hw/i386/xen/xen_platform.c +++ /dev/null @@ -1,455 +0,0 @@ -/* - * XEN platform pci device, formerly known as the event channel device - * - * Copyright (c) 2003-2004 Intel Corp. - * Copyright (c) 2006 XenSource - * - * 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/i386/pc.h" -#include "hw/ide.h" -#include "hw/pci/pci.h" -#include "hw/irq.h" -#include "hw/xen/xen_common.h" -#include "hw/xen/xen_backend.h" -#include "trace.h" -#include "exec/address-spaces.h" -#include "sysemu/block-backend.h" -#include "qemu/error-report.h" - -#include - -//#define DEBUG_PLATFORM - -#ifdef DEBUG_PLATFORM -#define DPRINTF(fmt, ...) do { \ - fprintf(stderr, "xen_platform: " fmt, ## __VA_ARGS__); \ -} while (0) -#else -#define DPRINTF(fmt, ...) do { } while (0) -#endif - -#define PFFLAG_ROM_LOCK 1 /* Sets whether ROM memory area is RW or RO */ - -typedef struct PCIXenPlatformState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - MemoryRegion fixed_io; - MemoryRegion bar; - MemoryRegion mmio_bar; - uint8_t flags; /* used only for version_id == 2 */ - int drivers_blacklisted; - uint16_t driver_product_version; - - /* Log from guest drivers */ - char log_buffer[4096]; - int log_buffer_off; -} PCIXenPlatformState; - -#define TYPE_XEN_PLATFORM "xen-platform" -#define XEN_PLATFORM(obj) \ - OBJECT_CHECK(PCIXenPlatformState, (obj), TYPE_XEN_PLATFORM) - -#define XEN_PLATFORM_IOPORT 0x10 - -/* Send bytes to syslog */ -static void log_writeb(PCIXenPlatformState *s, char val) -{ - if (val == '\n' || s->log_buffer_off == sizeof(s->log_buffer) - 1) { - /* Flush buffer */ - s->log_buffer[s->log_buffer_off] = 0; - trace_xen_platform_log(s->log_buffer); - s->log_buffer_off = 0; - } else { - s->log_buffer[s->log_buffer_off++] = val; - } -} - -/* Xen Platform, Fixed IOPort */ -#define UNPLUG_ALL_IDE_DISKS 1 -#define UNPLUG_ALL_NICS 2 -#define UNPLUG_AUX_IDE_DISKS 4 - -static void unplug_nic(PCIBus *b, PCIDevice *d, void *o) -{ - /* We have to ignore passthrough devices */ - if (pci_get_word(d->config + PCI_CLASS_DEVICE) == - PCI_CLASS_NETWORK_ETHERNET - && strcmp(d->name, "xen-pci-passthrough") != 0) { - object_unparent(OBJECT(d)); - } -} - -static void pci_unplug_nics(PCIBus *bus) -{ - pci_for_each_device(bus, 0, unplug_nic, NULL); -} - -static void unplug_disks(PCIBus *b, PCIDevice *d, void *o) -{ - /* We have to ignore passthrough devices */ - if (pci_get_word(d->config + PCI_CLASS_DEVICE) == - PCI_CLASS_STORAGE_IDE - && strcmp(d->name, "xen-pci-passthrough") != 0) { - pci_piix3_xen_ide_unplug(DEVICE(d)); - } -} - -static void pci_unplug_disks(PCIBus *bus) -{ - pci_for_each_device(bus, 0, unplug_disks, NULL); -} - -static void platform_fixed_ioport_writew(void *opaque, uint32_t addr, uint32_t val) -{ - PCIXenPlatformState *s = opaque; - - switch (addr) { - case 0: { - PCIDevice *pci_dev = PCI_DEVICE(s); - /* Unplug devices. Value is a bitmask of which devices to - unplug, with bit 0 the IDE devices, bit 1 the network - devices, and bit 2 the non-primary-master IDE devices. */ - if (val & UNPLUG_ALL_IDE_DISKS) { - DPRINTF("unplug disks\n"); - blk_drain_all(); - blk_flush_all(); - pci_unplug_disks(pci_dev->bus); - } - if (val & UNPLUG_ALL_NICS) { - DPRINTF("unplug nics\n"); - pci_unplug_nics(pci_dev->bus); - } - if (val & UNPLUG_AUX_IDE_DISKS) { - DPRINTF("unplug auxiliary disks not supported\n"); - } - break; - } - case 2: - switch (val) { - case 1: - DPRINTF("Citrix Windows PV drivers loaded in guest\n"); - break; - case 0: - DPRINTF("Guest claimed to be running PV product 0?\n"); - break; - default: - DPRINTF("Unknown PV product %d loaded in guest\n", val); - break; - } - s->driver_product_version = val; - break; - } -} - -static void platform_fixed_ioport_writel(void *opaque, uint32_t addr, - uint32_t val) -{ - switch (addr) { - case 0: - /* PV driver version */ - break; - } -} - -static void platform_fixed_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) -{ - PCIXenPlatformState *s = opaque; - - switch (addr) { - case 0: /* Platform flags */ { - hvmmem_type_t mem_type = (val & PFFLAG_ROM_LOCK) ? - HVMMEM_ram_ro : HVMMEM_ram_rw; - if (xc_hvm_set_mem_type(xen_xc, xen_domid, mem_type, 0xc0, 0x40)) { - DPRINTF("unable to change ro/rw state of ROM memory area!\n"); - } else { - s->flags = val & PFFLAG_ROM_LOCK; - DPRINTF("changed ro/rw state of ROM memory area. now is %s state.\n", - (mem_type == HVMMEM_ram_ro ? "ro":"rw")); - } - break; - } - case 2: - log_writeb(s, val); - break; - } -} - -static uint32_t platform_fixed_ioport_readw(void *opaque, uint32_t addr) -{ - PCIXenPlatformState *s = opaque; - - switch (addr) { - case 0: - if (s->drivers_blacklisted) { - /* The drivers will recognise this magic number and refuse - * to do anything. */ - return 0xd249; - } else { - /* Magic value so that you can identify the interface. */ - return 0x49d2; - } - default: - return 0xffff; - } -} - -static uint32_t platform_fixed_ioport_readb(void *opaque, uint32_t addr) -{ - PCIXenPlatformState *s = opaque; - - switch (addr) { - case 0: - /* Platform flags */ - return s->flags; - case 2: - /* Version number */ - return 1; - default: - return 0xff; - } -} - -static void platform_fixed_ioport_reset(void *opaque) -{ - PCIXenPlatformState *s = opaque; - - platform_fixed_ioport_writeb(s, 0, 0); -} - -static uint64_t platform_fixed_ioport_read(void *opaque, - hwaddr addr, - unsigned size) -{ - switch (size) { - case 1: - return platform_fixed_ioport_readb(opaque, addr); - case 2: - return platform_fixed_ioport_readw(opaque, addr); - default: - return -1; - } -} - -static void platform_fixed_ioport_write(void *opaque, hwaddr addr, - - uint64_t val, unsigned size) -{ - switch (size) { - case 1: - platform_fixed_ioport_writeb(opaque, addr, val); - break; - case 2: - platform_fixed_ioport_writew(opaque, addr, val); - break; - case 4: - platform_fixed_ioport_writel(opaque, addr, val); - break; - } -} - - -static const MemoryRegionOps platform_fixed_io_ops = { - .read = platform_fixed_ioport_read, - .write = platform_fixed_ioport_write, - .valid = { - .unaligned = true, - }, - .impl = { - .min_access_size = 1, - .max_access_size = 4, - .unaligned = true, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void platform_fixed_ioport_init(PCIXenPlatformState* s) -{ - memory_region_init_io(&s->fixed_io, OBJECT(s), &platform_fixed_io_ops, s, - "xen-fixed", 16); - memory_region_add_subregion(get_system_io(), XEN_PLATFORM_IOPORT, - &s->fixed_io); -} - -/* Xen Platform PCI Device */ - -static uint64_t xen_platform_ioport_readb(void *opaque, hwaddr addr, - unsigned int size) -{ - if (addr == 0) { - return platform_fixed_ioport_readb(opaque, 0); - } else { - return ~0u; - } -} - -static void xen_platform_ioport_writeb(void *opaque, hwaddr addr, - uint64_t val, unsigned int size) -{ - PCIXenPlatformState *s = opaque; - - switch (addr) { - case 0: /* Platform flags */ - platform_fixed_ioport_writeb(opaque, 0, (uint32_t)val); - break; - case 8: - log_writeb(s, (uint32_t)val); - break; - default: - break; - } -} - -static const MemoryRegionOps xen_pci_io_ops = { - .read = xen_platform_ioport_readb, - .write = xen_platform_ioport_writeb, - .impl.min_access_size = 1, - .impl.max_access_size = 1, -}; - -static void platform_ioport_bar_setup(PCIXenPlatformState *d) -{ - memory_region_init_io(&d->bar, OBJECT(d), &xen_pci_io_ops, d, - "xen-pci", 0x100); -} - -static uint64_t platform_mmio_read(void *opaque, hwaddr addr, - unsigned size) -{ - DPRINTF("Warning: attempted read from physical address " - "0x" TARGET_FMT_plx " in xen platform mmio space\n", addr); - - return 0; -} - -static void platform_mmio_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - DPRINTF("Warning: attempted write of 0x%"PRIx64" to physical " - "address 0x" TARGET_FMT_plx " in xen platform mmio space\n", - val, addr); -} - -static const MemoryRegionOps platform_mmio_handler = { - .read = &platform_mmio_read, - .write = &platform_mmio_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void platform_mmio_setup(PCIXenPlatformState *d) -{ - memory_region_init_io(&d->mmio_bar, OBJECT(d), &platform_mmio_handler, d, - "xen-mmio", 0x1000000); -} - -static int xen_platform_post_load(void *opaque, int version_id) -{ - PCIXenPlatformState *s = opaque; - - platform_fixed_ioport_writeb(s, 0, s->flags); - - return 0; -} - -static const VMStateDescription vmstate_xen_platform = { - .name = "platform", - .version_id = 4, - .minimum_version_id = 4, - .post_load = xen_platform_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, PCIXenPlatformState), - VMSTATE_UINT8(flags, PCIXenPlatformState), - VMSTATE_END_OF_LIST() - } -}; - -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); - - pci_config_set_prog_interface(pci_conf, 0); - - pci_conf[PCI_INTERRUPT_PIN] = 1; - - platform_ioport_bar_setup(d); - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->bar); - - /* reserve 16MB mmio address for share memory*/ - platform_mmio_setup(d); - pci_register_bar(dev, 1, PCI_BASE_ADDRESS_MEM_PREFETCH, - &d->mmio_bar); - - platform_fixed_ioport_init(d); -} - -static void platform_reset(DeviceState *dev) -{ - PCIXenPlatformState *s = XEN_PLATFORM(dev); - - platform_fixed_ioport_reset(s); -} - -static void xen_platform_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - 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; - k->subsystem_vendor_id = PCI_VENDOR_ID_XEN; - k->subsystem_id = PCI_DEVICE_ID_XEN_PLATFORM; - k->revision = 1; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->desc = "XEN platform pci device"; - dc->reset = platform_reset; - dc->vmsd = &vmstate_xen_platform; -} - -static const TypeInfo xen_platform_info = { - .name = TYPE_XEN_PLATFORM, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIXenPlatformState), - .class_init = xen_platform_class_init, -}; - -static void xen_platform_register_types(void) -{ - type_register_static(&xen_platform_info); -} - -type_init(xen_platform_register_types) diff --git a/qemu/hw/i386/xen/xen_pvdevice.c b/qemu/hw/i386/xen/xen_pvdevice.c deleted file mode 100644 index c093b3445..000000000 --- a/qemu/hw/i386/xen/xen_pvdevice.c +++ /dev/null @@ -1,137 +0,0 @@ -/* Copyright (c) Citrix Systems Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, - * with or without modification, are permitted provided - * that the following conditions are met: - * - * * Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the - * following disclaimer in the documentation and/or other - * materials provided with the distribution. - * - * 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 HOLDER 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 ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "trace.h" - -#define TYPE_XEN_PV_DEVICE "xen-pvdevice" - -#define XEN_PV_DEVICE(obj) \ - OBJECT_CHECK(XenPVDevice, (obj), TYPE_XEN_PV_DEVICE) - -typedef struct XenPVDevice { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - uint16_t vendor_id; - uint16_t device_id; - uint8_t revision; - uint32_t size; - MemoryRegion mmio; -} XenPVDevice; - -static uint64_t xen_pv_mmio_read(void *opaque, hwaddr addr, - unsigned size) -{ - trace_xen_pv_mmio_read(addr); - - return ~(uint64_t)0; -} - -static void xen_pv_mmio_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - trace_xen_pv_mmio_write(addr); -} - -static const MemoryRegionOps xen_pv_mmio_ops = { - .read = &xen_pv_mmio_read, - .write = &xen_pv_mmio_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -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) { - error_setg(errp, "Device ID invalid, it must always be supplied"); - return; - } - - pci_conf = pci_dev->config; - - pci_set_word(pci_conf + PCI_VENDOR_ID, d->vendor_id); - pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, d->vendor_id); - pci_set_word(pci_conf + PCI_DEVICE_ID, d->device_id); - pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, d->device_id); - pci_set_byte(pci_conf + PCI_REVISION_ID, d->revision); - - pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_MEMORY); - - pci_config_set_prog_interface(pci_conf, 0); - - pci_conf[PCI_INTERRUPT_PIN] = 1; - - memory_region_init_io(&d->mmio, NULL, &xen_pv_mmio_ops, d, - "mmio", d->size); - - pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_MEM_PREFETCH, - &d->mmio); -} - -static Property xen_pv_props[] = { - DEFINE_PROP_UINT16("vendor-id", XenPVDevice, vendor_id, PCI_VENDOR_ID_XEN), - DEFINE_PROP_UINT16("device-id", XenPVDevice, device_id, 0xffff), - DEFINE_PROP_UINT8("revision", XenPVDevice, revision, 0x01), - DEFINE_PROP_UINT32("size", XenPVDevice, size, 0x400000), - DEFINE_PROP_END_OF_LIST() -}; - -static void xen_pv_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = xen_pv_realize; - k->class_id = PCI_CLASS_SYSTEM_OTHER; - dc->desc = "Xen PV Device"; - dc->props = xen_pv_props; -} - -static const TypeInfo xen_pv_type_info = { - .name = TYPE_XEN_PV_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(XenPVDevice), - .class_init = xen_pv_class_init, -}; - -static void xen_pv_register_types(void) -{ - type_register_static(&xen_pv_type_info); -} - -type_init(xen_pv_register_types) diff --git a/qemu/hw/ide/Makefile.objs b/qemu/hw/ide/Makefile.objs deleted file mode 100644 index 729e9bd0d..000000000 --- a/qemu/hw/ide/Makefile.objs +++ /dev/null @@ -1,12 +0,0 @@ -common-obj-$(CONFIG_IDE_CORE) += core.o atapi.o -common-obj-$(CONFIG_IDE_QDEV) += qdev.o -common-obj-$(CONFIG_IDE_PCI) += pci.o -common-obj-$(CONFIG_IDE_ISA) += isa.o -common-obj-$(CONFIG_IDE_PIIX) += piix.o -common-obj-$(CONFIG_IDE_CMD646) += cmd646.o -common-obj-$(CONFIG_IDE_MACIO) += macio.o -common-obj-$(CONFIG_IDE_MMIO) += mmio.o -common-obj-$(CONFIG_IDE_VIA) += via.o -common-obj-$(CONFIG_MICRODRIVE) += microdrive.o -common-obj-$(CONFIG_AHCI) += ahci.o -common-obj-$(CONFIG_AHCI) += ich.o diff --git a/qemu/hw/ide/ahci.c b/qemu/hw/ide/ahci.c deleted file mode 100644 index f244bc01c..000000000 --- a/qemu/hw/ide/ahci.c +++ /dev/null @@ -1,1832 +0,0 @@ -/* - * QEMU AHCI Emulation - * - * Copyright (c) 2010 qiaochong@loongson.cn - * Copyright (c) 2010 Roland Elek - * Copyright (c) 2010 Sebastian Herbszt - * Copyright (c) 2010 Alexander Graf - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include -#include -#include -#include - -#include "qemu/error-report.h" -#include "sysemu/block-backend.h" -#include "sysemu/dma.h" -#include "internal.h" -#include -#include - -#define DEBUG_AHCI 0 - -#define DPRINTF(port, fmt, ...) \ -do { \ - if (DEBUG_AHCI) { \ - fprintf(stderr, "ahci: %s: [%d] ", __func__, port); \ - fprintf(stderr, fmt, ## __VA_ARGS__); \ - } \ -} while (0) - -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 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 bool ahci_map_clb_address(AHCIDevice *ad); -static bool ahci_map_fis_address(AHCIDevice *ad); -static void ahci_unmap_clb_address(AHCIDevice *ad); -static void ahci_unmap_fis_address(AHCIDevice *ad); - - -static uint32_t ahci_port_read(AHCIState *s, int port, int offset) -{ - uint32_t val; - AHCIPortRegs *pr; - pr = &s->dev[port].port_regs; - - switch (offset) { - case PORT_LST_ADDR: - val = pr->lst_addr; - break; - case PORT_LST_ADDR_HI: - val = pr->lst_addr_hi; - break; - case PORT_FIS_ADDR: - val = pr->fis_addr; - break; - case PORT_FIS_ADDR_HI: - val = pr->fis_addr_hi; - break; - case PORT_IRQ_STAT: - val = pr->irq_stat; - break; - case PORT_IRQ_MASK: - val = pr->irq_mask; - break; - case PORT_CMD: - val = pr->cmd; - break; - case PORT_TFDATA: - val = pr->tfdata; - break; - case PORT_SIG: - val = pr->sig; - break; - case PORT_SCR_STAT: - if (s->dev[port].port.ifs[0].blk) { - val = SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP | - SATA_SCR_SSTATUS_SPD_GEN1 | SATA_SCR_SSTATUS_IPM_ACTIVE; - } else { - val = SATA_SCR_SSTATUS_DET_NODEV; - } - break; - case PORT_SCR_CTL: - val = pr->scr_ctl; - break; - case PORT_SCR_ERR: - val = pr->scr_err; - break; - case PORT_SCR_ACT: - val = pr->scr_act; - break; - case PORT_CMD_ISSUE: - val = pr->cmd_issue; - break; - case PORT_RESERVED: - default: - val = 0; - } - DPRINTF(port, "offset: 0x%x val: 0x%x\n", offset, val); - return val; - -} - -static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev) -{ - DeviceState *dev_state = s->container; - PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), - TYPE_PCI_DEVICE); - - DPRINTF(0, "raise irq\n"); - - if (pci_dev && msi_enabled(pci_dev)) { - msi_notify(pci_dev, 0); - } else { - qemu_irq_raise(s->irq); - } -} - -static void ahci_irq_lower(AHCIState *s, AHCIDevice *dev) -{ - DeviceState *dev_state = s->container; - PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), - TYPE_PCI_DEVICE); - - DPRINTF(0, "lower irq\n"); - - if (!pci_dev || !msi_enabled(pci_dev)) { - qemu_irq_lower(s->irq); - } -} - -static void ahci_check_irq(AHCIState *s) -{ - int i; - - DPRINTF(-1, "check irq %#x\n", s->control_regs.irqstatus); - - s->control_regs.irqstatus = 0; - for (i = 0; i < s->ports; i++) { - AHCIPortRegs *pr = &s->dev[i].port_regs; - if (pr->irq_stat & pr->irq_mask) { - s->control_regs.irqstatus |= (1 << i); - } - } - - if (s->control_regs.irqstatus && - (s->control_regs.ghc & HOST_CTL_IRQ_EN)) { - ahci_irq_raise(s, NULL); - } else { - ahci_irq_lower(s, NULL); - } -} - -static void ahci_trigger_irq(AHCIState *s, AHCIDevice *d, - int irq_type) -{ - DPRINTF(d->port_no, "trigger irq %#x -> %x\n", - irq_type, d->port_regs.irq_mask & irq_type); - - d->port_regs.irq_stat |= irq_type; - ahci_check_irq(s); -} - -static void map_page(AddressSpace *as, uint8_t **ptr, uint64_t addr, - uint32_t wanted) -{ - hwaddr len = wanted; - - if (*ptr) { - dma_memory_unmap(as, *ptr, len, DMA_DIRECTION_FROM_DEVICE, len); - } - - *ptr = dma_memory_map(as, addr, &len, DMA_DIRECTION_FROM_DEVICE); - if (len < wanted) { - dma_memory_unmap(as, *ptr, len, DMA_DIRECTION_FROM_DEVICE, len); - *ptr = NULL; - } -} - -/** - * Check the cmd register to see if we should start or stop - * the DMA or FIS RX engines. - * - * @ad: Device to dis/engage. - * - * @return 0 on success, -1 on error. - */ -static int ahci_cond_start_engines(AHCIDevice *ad) -{ - AHCIPortRegs *pr = &ad->port_regs; - 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 (!cmd_start && cmd_on) { - ahci_unmap_clb_address(ad); - } - - 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 (!fis_start && fis_on) { - ahci_unmap_fis_address(ad); - } - - return 0; -} - -static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) -{ - AHCIPortRegs *pr = &s->dev[port].port_regs; - - DPRINTF(port, "offset: 0x%x val: 0x%x\n", offset, val); - switch (offset) { - case PORT_LST_ADDR: - pr->lst_addr = val; - break; - case PORT_LST_ADDR_HI: - pr->lst_addr_hi = val; - break; - case PORT_FIS_ADDR: - pr->fis_addr = val; - break; - case PORT_FIS_ADDR_HI: - pr->fis_addr_hi = val; - break; - case PORT_IRQ_STAT: - pr->irq_stat &= ~val; - ahci_check_irq(s); - break; - case PORT_IRQ_MASK: - pr->irq_mask = val & 0xfdc000ff; - ahci_check_irq(s); - break; - case PORT_CMD: - /* Block any Read-only fields from being set; - * including LIST_ON and FIS_ON. - * The spec requires to set ICC bits to zero after the ICC change - * is done. We don't support ICC state changes, therefore always - * force the ICC bits to zero. - */ - pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) | - (val & ~(PORT_CMD_RO_MASK|PORT_CMD_ICC_MASK)); - - /* 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. - Instead, we only submit it once - which works in most - cases, but is a hack. */ - if ((pr->cmd & PORT_CMD_FIS_ON) && - !s->dev[port].init_d2h_sent) { - ahci_init_d2h(&s->dev[port]); - } - - check_cmd(s, port); - break; - case PORT_TFDATA: - /* Read Only. */ - break; - case PORT_SIG: - /* Read Only */ - break; - case PORT_SCR_STAT: - /* Read Only */ - break; - case PORT_SCR_CTL: - if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) && - ((val & AHCI_SCR_SCTL_DET) == 0)) { - ahci_reset_port(s, port); - } - pr->scr_ctl = val; - break; - case PORT_SCR_ERR: - pr->scr_err &= ~val; - break; - case PORT_SCR_ACT: - /* RW1 */ - pr->scr_act |= val; - break; - case PORT_CMD_ISSUE: - pr->cmd_issue |= val; - check_cmd(s, port); - break; - default: - break; - } -} - -static uint64_t ahci_mem_read_32(void *opaque, hwaddr addr) -{ - AHCIState *s = opaque; - uint32_t val = 0; - - if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) { - switch (addr) { - case HOST_CAP: - val = s->control_regs.cap; - break; - case HOST_CTL: - val = s->control_regs.ghc; - break; - case HOST_IRQ_STAT: - val = s->control_regs.irqstatus; - break; - case HOST_PORTS_IMPL: - val = s->control_regs.impl; - break; - case HOST_VERSION: - val = s->control_regs.version; - break; - } - - DPRINTF(-1, "(addr 0x%08X), val 0x%08X\n", (unsigned) addr, val); - } else if ((addr >= AHCI_PORT_REGS_START_ADDR) && - (addr < (AHCI_PORT_REGS_START_ADDR + - (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) { - val = ahci_port_read(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7, - addr & AHCI_PORT_ADDR_OFFSET_MASK); - } - - return val; -} - - -/** - * AHCI 1.3 section 3 ("HBA Memory Registers") - * Support unaligned 8/16/32 bit reads, and 64 bit aligned reads. - * Caller is responsible for masking unwanted higher order bytes. - */ -static uint64_t ahci_mem_read(void *opaque, hwaddr addr, unsigned size) -{ - hwaddr aligned = addr & ~0x3; - 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) { - 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); - } - - DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n", - addr, val, size); - return val; -} - - -static void ahci_mem_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - 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" - TARGET_FMT_plx "\n", addr); - return; - } - - if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) { - DPRINTF(-1, "(addr 0x%08X), val 0x%08"PRIX64"\n", (unsigned) addr, val); - - switch (addr) { - case HOST_CAP: /* R/WO, RO */ - /* FIXME handle R/WO */ - break; - case HOST_CTL: /* R/W */ - if (val & HOST_CTL_RESET) { - DPRINTF(-1, "HBA Reset\n"); - ahci_reset(s); - } else { - s->control_regs.ghc = (val & 0x3) | HOST_CTL_AHCI_EN; - ahci_check_irq(s); - } - break; - case HOST_IRQ_STAT: /* R/WC, RO */ - s->control_regs.irqstatus &= ~val; - ahci_check_irq(s); - break; - case HOST_PORTS_IMPL: /* R/WO, RO */ - /* FIXME handle R/WO */ - break; - case HOST_VERSION: /* RO */ - /* FIXME report write? */ - break; - default: - DPRINTF(-1, "write to unknown register 0x%x\n", (unsigned)addr); - } - } else if ((addr >= AHCI_PORT_REGS_START_ADDR) && - (addr < (AHCI_PORT_REGS_START_ADDR + - (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) { - ahci_port_write(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7, - addr & AHCI_PORT_ADDR_OFFSET_MASK, val); - } - -} - -static const MemoryRegionOps ahci_mem_ops = { - .read = ahci_mem_read, - .write = ahci_mem_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static uint64_t ahci_idp_read(void *opaque, hwaddr addr, - unsigned size) -{ - AHCIState *s = opaque; - - if (addr == s->idp_offset) { - /* index register */ - return s->idp_index; - } else if (addr == s->idp_offset + 4) { - /* data register - do memory read at location selected by index */ - return ahci_mem_read(opaque, s->idp_index, size); - } else { - return 0; - } -} - -static void ahci_idp_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - AHCIState *s = opaque; - - if (addr == s->idp_offset) { - /* index register - mask off reserved bits */ - s->idp_index = (uint32_t)val & ((AHCI_MEM_BAR_SIZE - 1) & ~3); - } else if (addr == s->idp_offset + 4) { - /* data register - do memory write at location selected by index */ - ahci_mem_write(opaque, s->idp_index, val, size); - } -} - -static const MemoryRegionOps ahci_idp_ops = { - .read = ahci_idp_read, - .write = ahci_idp_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - - -static void ahci_reg_init(AHCIState *s) -{ - int i; - - s->control_regs.cap = (s->ports - 1) | - (AHCI_NUM_COMMAND_SLOTS << 8) | - (AHCI_SUPPORTED_SPEED_GEN1 << AHCI_SUPPORTED_SPEED) | - HOST_CAP_NCQ | HOST_CAP_AHCI; - - s->control_regs.impl = (1 << s->ports) - 1; - - s->control_regs.version = AHCI_VERSION_1_0; - - for (i = 0; i < s->ports; i++) { - s->dev[i].port_state = STATE_RUN; - } -} - -static void check_cmd(AHCIState *s, int port) -{ - AHCIPortRegs *pr = &s->dev[port].port_regs; - uint8_t slot; - - if ((pr->cmd & PORT_CMD_START) && pr->cmd_issue) { - for (slot = 0; (slot < 32) && pr->cmd_issue; slot++) { - if ((pr->cmd_issue & (1U << slot)) && - !handle_cmd(s, port, slot)) { - pr->cmd_issue &= ~(1U << slot); - } - } - } -} - -static void ahci_check_cmd_bh(void *opaque) -{ - AHCIDevice *ad = opaque; - - qemu_bh_delete(ad->check_bh); - ad->check_bh = NULL; - - if ((ad->busy_slot != -1) && - !(ad->port.ifs[0].status & (BUSY_STAT|DRQ_STAT))) { - /* no longer busy */ - ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot); - ad->busy_slot = -1; - } - - check_cmd(ad->hba, ad->port_no); -} - -static void ahci_init_d2h(AHCIDevice *ad) -{ - IDEState *ide_state = &ad->port.ifs[0]; - AHCIPortRegs *pr = &ad->port_regs; - - if (ad->init_d2h_sent) { - return; - } - - 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); - } -} - -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) -{ - AHCIDevice *d = &s->dev[port]; - AHCIPortRegs *pr = &d->port_regs; - IDEState *ide_state = &d->port.ifs[0]; - int i; - - DPRINTF(port, "reset port\n"); - - ide_bus_reset(&d->port); - ide_state->ncq_queues = AHCI_MAX_CMDS; - - pr->scr_stat = 0; - pr->scr_err = 0; - pr->scr_act = 0; - pr->tfdata = 0x7F; - pr->sig = 0xFFFFFFFF; - d->busy_slot = -1; - d->init_d2h_sent = false; - - ide_state = &s->dev[port].port.ifs[0]; - if (!ide_state->blk) { - return; - } - - /* reset ncq queue */ - for (i = 0; i < AHCI_MAX_CMDS; i++) { - NCQTransferState *ncq_tfs = &s->dev[port].ncq_tfs[i]; - ncq_tfs->halt = false; - if (!ncq_tfs->used) { - continue; - } - - if (ncq_tfs->aiocb) { - blk_aio_cancel(ncq_tfs->aiocb); - ncq_tfs->aiocb = NULL; - } - - /* Maybe we just finished the request thanks to blk_aio_cancel() */ - if (!ncq_tfs->used) { - continue; - } - - qemu_sglist_destroy(&ncq_tfs->sglist); - ncq_tfs->used = 0; - } - - s->dev[port].port_state = STATE_RUN; - if (ide_state->drive_kind == IDE_CD) { - ahci_set_signature(d, SATA_SIGNATURE_CDROM);\ - ide_state->status = SEEK_STAT | WRERR_STAT | READY_STAT; - } else { - ahci_set_signature(d, SATA_SIGNATURE_DISK); - ide_state->status = SEEK_STAT | WRERR_STAT; - } - - ide_state->error = 1; - ahci_init_d2h(d); -} - -static void debug_print_fis(uint8_t *fis, int cmd_len) -{ -#if DEBUG_AHCI - int i; - - fprintf(stderr, "fis:"); - for (i = 0; i < cmd_len; i++) { - if ((i & 0xf) == 0) { - fprintf(stderr, "\n%02x:",i); - } - fprintf(stderr, "%02x ",fis[i]); - } - fprintf(stderr, "\n"); -#endif -} - -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); - 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; -} - -static bool ahci_map_clb_address(AHCIDevice *ad) -{ - AHCIPortRegs *pr = &ad->port_regs; - ad->cur_cmd = NULL; - map_page(ad->hba->as, &ad->lst, - ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); - 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; -} - -static void ahci_write_fis_sdb(AHCIState *s, NCQTransferState *ncq_tfs) -{ - AHCIDevice *ad = ncq_tfs->drive; - AHCIPortRegs *pr = &ad->port_regs; - IDEState *ide_state; - SDBFIS *sdb_fis; - - if (!ad->res_fis || - !(pr->cmd & PORT_CMD_FIS_RX)) { - return; - } - - sdb_fis = (SDBFIS *)&ad->res_fis[RES_FIS_SDBFIS]; - ide_state = &ad->port.ifs[0]; - - sdb_fis->type = SATA_FIS_TYPE_SDB; - /* Interrupt pending & Notification bit */ - sdb_fis->flags = 0x40; /* Interrupt bit, always 1 for NCQ */ - sdb_fis->status = ide_state->status & 0x77; - sdb_fis->error = ide_state->error; - /* update SAct field in SDB_FIS */ - sdb_fis->payload = cpu_to_le32(ad->finished); - - /* Update shadow registers (except BSY 0x80 and DRQ 0x08) */ - pr->tfdata = (ad->port.ifs[0].error << 8) | - (ad->port.ifs[0].status & 0x77) | - (pr->tfdata & 0x88); - pr->scr_act &= ~ad->finished; - ad->finished = 0; - - /* Trigger IRQ if interrupt bit is set (which currently, it always is) */ - if (sdb_fis->flags & 0x40) { - ahci_trigger_irq(s, ad, PORT_IRQ_SDB_FIS); - } -} - -static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) -{ - AHCIPortRegs *pr = &ad->port_regs; - uint8_t *pio_fis; - IDEState *s = &ad->port.ifs[0]; - - if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) { - return; - } - - pio_fis = &ad->res_fis[RES_FIS_PSFIS]; - - pio_fis[0] = SATA_FIS_TYPE_PIO_SETUP; - pio_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0); - pio_fis[2] = s->status; - pio_fis[3] = s->error; - - pio_fis[4] = s->sector; - pio_fis[5] = s->lcyl; - pio_fis[6] = s->hcyl; - pio_fis[7] = s->select; - pio_fis[8] = s->hob_sector; - pio_fis[9] = s->hob_lcyl; - pio_fis[10] = s->hob_hcyl; - pio_fis[11] = 0; - pio_fis[12] = s->nsector & 0xFF; - pio_fis[13] = (s->nsector >> 8) & 0xFF; - pio_fis[14] = 0; - pio_fis[15] = s->status; - pio_fis[16] = len & 255; - pio_fis[17] = len >> 8; - pio_fis[18] = 0; - pio_fis[19] = 0; - - /* Update shadow registers: */ - pr->tfdata = (ad->port.ifs[0].error << 8) | - ad->port.ifs[0].status; - - if (pio_fis[2] & ERR_STAT) { - ahci_trigger_irq(ad->hba, ad, PORT_IRQ_TF_ERR); - } - - ahci_trigger_irq(ad->hba, ad, PORT_IRQ_PIOS_FIS); -} - -static bool ahci_write_fis_d2h(AHCIDevice *ad) -{ - AHCIPortRegs *pr = &ad->port_regs; - uint8_t *d2h_fis; - int i; - IDEState *s = &ad->port.ifs[0]; - - if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) { - return false; - } - - d2h_fis = &ad->res_fis[RES_FIS_RFIS]; - - d2h_fis[0] = SATA_FIS_TYPE_REGISTER_D2H; - d2h_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0); - d2h_fis[2] = s->status; - d2h_fis[3] = s->error; - - d2h_fis[4] = s->sector; - d2h_fis[5] = s->lcyl; - d2h_fis[6] = s->hcyl; - d2h_fis[7] = s->select; - d2h_fis[8] = s->hob_sector; - d2h_fis[9] = s->hob_lcyl; - d2h_fis[10] = s->hob_hcyl; - d2h_fis[11] = 0; - d2h_fis[12] = s->nsector & 0xFF; - d2h_fis[13] = (s->nsector >> 8) & 0xFF; - for (i = 14; i < 20; i++) { - d2h_fis[i] = 0; - } - - /* Update shadow registers: */ - pr->tfdata = (ad->port.ifs[0].error << 8) | - ad->port.ifs[0].status; - - if (d2h_fis[2] & ERR_STAT) { - ahci_trigger_irq(ad->hba, ad, PORT_IRQ_TF_ERR); - } - - ahci_trigger_irq(ad->hba, ad, PORT_IRQ_D2H_REG_FIS); - return true; -} - -static int prdt_tbl_entry_size(const AHCI_SG *tbl) -{ - /* flags_size is zero-based */ - 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, uint64_t offset) -{ - uint16_t opts = le16_to_cpu(cmd->opts); - uint16_t prdtl = le16_to_cpu(cmd->prdtl); - uint64_t cfis_addr = le64_to_cpu(cmd->tbl_addr); - uint64_t prdt_addr = cfis_addr + 0x80; - dma_addr_t prdt_len = (prdtl * sizeof(AHCI_SG)); - dma_addr_t real_prdt_len = prdt_len; - uint8_t *prdt; - int i; - int r = 0; - uint64_t sum = 0; - int off_idx = -1; - int64_t off_pos = -1; - int tbl_entry_size; - IDEBus *bus = &ad->port; - BusState *qbus = BUS(bus); - - if (!prdtl) { - DPRINTF(ad->port_no, "no sg list given by guest: 0x%08x\n", opts); - return -1; - } - - /* map PRDT */ - if (!(prdt = dma_memory_map(ad->hba->as, prdt_addr, &prdt_len, - DMA_DIRECTION_TO_DEVICE))){ - DPRINTF(ad->port_no, "map failed\n"); - return -1; - } - - if (prdt_len < real_prdt_len) { - DPRINTF(ad->port_no, "mapped less than expected\n"); - r = -1; - goto out; - } - - /* Get entries in the PRDT, init a qemu sglist accordingly */ - if (prdtl > 0) { - AHCI_SG *tbl = (AHCI_SG *)prdt; - sum = 0; - for (i = 0; i < prdtl; i++) { - tbl_entry_size = prdt_tbl_entry_size(&tbl[i]); - if (offset < (sum + tbl_entry_size)) { - off_idx = i; - off_pos = offset - sum; - break; - } - sum += tbl_entry_size; - } - if ((off_idx == -1) || (off_pos < 0) || (off_pos > tbl_entry_size)) { - DPRINTF(ad->port_no, "%s: Incorrect offset! " - "off_idx: %d, off_pos: %"PRId64"\n", - __func__, off_idx, off_pos); - r = -1; - goto out; - } - - qemu_sglist_init(sglist, qbus->parent, (prdtl - off_idx), - ad->hba->as); - qemu_sglist_add(sglist, le64_to_cpu(tbl[off_idx].addr) + off_pos, - MIN(prdt_tbl_entry_size(&tbl[off_idx]) - off_pos, - limit)); - - for (i = off_idx + 1; i < prdtl && sglist->size < limit; i++) { - qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr), - MIN(prdt_tbl_entry_size(&tbl[i]), - limit - sglist->size)); - } - } - -out: - dma_memory_unmap(ad->hba->as, prdt, prdt_len, - DMA_DIRECTION_TO_DEVICE, prdt_len); - return r; -} - -static void ncq_err(NCQTransferState *ncq_tfs) -{ - IDEState *ide_state = &ncq_tfs->drive->port.ifs[0]; - - 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) -{ - /* If we didn't error out, set our finished bit. Errored commands - * do not get a bit set for the SDB FIS ACT register, nor do they - * clear the outstanding bit in scr_act (PxSACT). */ - if (!(ncq_tfs->drive->port_regs.scr_err & (1 << ncq_tfs->tag))) { - ncq_tfs->drive->finished |= (1 << ncq_tfs->tag); - } - - ahci_write_fis_sdb(ncq_tfs->drive->hba, ncq_tfs); - - DPRINTF(ncq_tfs->drive->port_no, "NCQ transfer tag %d finished\n", - ncq_tfs->tag); - - block_acct_done(blk_get_stats(ncq_tfs->drive->port.ifs[0].blk), - &ncq_tfs->acct); - qemu_sglist_destroy(&ncq_tfs->sglist); - ncq_tfs->used = 0; -} - -static void ncq_cb(void *opaque, int ret) -{ - NCQTransferState *ncq_tfs = (NCQTransferState *)opaque; - IDEState *ide_state = &ncq_tfs->drive->port.ifs[0]; - - if (ret == -ECANCELED) { - return; - } - - if (ret < 0) { - bool is_read = ncq_tfs->cmd == READ_FPDMA_QUEUED; - BlockErrorAction action = blk_get_error_action(ide_state->blk, - is_read, -ret); - if (action == BLOCK_ERROR_ACTION_STOP) { - ncq_tfs->halt = true; - ide_state->bus->error_status = IDE_RETRY_HBA; - } else if (action == BLOCK_ERROR_ACTION_REPORT) { - ncq_err(ncq_tfs); - } - blk_error_action(ide_state->blk, action, is_read, -ret); - } else { - ide_state->status = READY_STAT | SEEK_STAT; - } - - if (!ncq_tfs->halt) { - ncq_finish(ncq_tfs); - } -} - -static int is_ncq(uint8_t ata_cmd) -{ - /* Based on SATA 3.2 section 13.6.3.2 */ - switch (ata_cmd) { - case READ_FPDMA_QUEUED: - case WRITE_FPDMA_QUEUED: - case NCQ_NON_DATA: - case RECEIVE_FPDMA_QUEUED: - case SEND_FPDMA_QUEUED: - return 1; - default: - return 0; - } -} - -static void execute_ncq_command(NCQTransferState *ncq_tfs) -{ - AHCIDevice *ad = ncq_tfs->drive; - IDEState *ide_state = &ad->port.ifs[0]; - int port = ad->port_no; - - g_assert(is_ncq(ncq_tfs->cmd)); - ncq_tfs->halt = false; - - switch (ncq_tfs->cmd) { - case READ_FPDMA_QUEUED: - DPRINTF(port, "NCQ reading %d sectors from LBA %"PRId64", tag %d\n", - ncq_tfs->sector_count, ncq_tfs->lba, ncq_tfs->tag); - - DPRINTF(port, "tag %d aio read %"PRId64"\n", - ncq_tfs->tag, ncq_tfs->lba); - - dma_acct_start(ide_state->blk, &ncq_tfs->acct, - &ncq_tfs->sglist, BLOCK_ACCT_READ); - ncq_tfs->aiocb = dma_blk_read(ide_state->blk, &ncq_tfs->sglist, - ncq_tfs->lba, ncq_cb, ncq_tfs); - break; - case WRITE_FPDMA_QUEUED: - DPRINTF(port, "NCQ writing %d sectors to LBA %"PRId64", tag %d\n", - ncq_tfs->sector_count, ncq_tfs->lba, ncq_tfs->tag); - - DPRINTF(port, "tag %d aio write %"PRId64"\n", - ncq_tfs->tag, ncq_tfs->lba); - - dma_acct_start(ide_state->blk, &ncq_tfs->acct, - &ncq_tfs->sglist, BLOCK_ACCT_WRITE); - ncq_tfs->aiocb = dma_blk_write(ide_state->blk, &ncq_tfs->sglist, - ncq_tfs->lba, ncq_cb, ncq_tfs); - break; - default: - DPRINTF(port, "error: unsupported NCQ command (0x%02x) received\n", - ncq_tfs->cmd); - qemu_sglist_destroy(&ncq_tfs->sglist); - ncq_err(ncq_tfs); - } -} - - -static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, - uint8_t slot) -{ - AHCIDevice *ad = &s->dev[port]; - IDEState *ide_state = &ad->port.ifs[0]; - NCQFrame *ncq_fis = (NCQFrame*)cmd_fis; - uint8_t tag = ncq_fis->tag >> 3; - NCQTransferState *ncq_tfs = &ad->ncq_tfs[tag]; - size_t size; - - g_assert(is_ncq(ncq_fis->command)); - if (ncq_tfs->used) { - /* error - already in use */ - fprintf(stderr, "%s: tag %d already used\n", __FUNCTION__, tag); - return; - } - - ncq_tfs->used = 1; - ncq_tfs->drive = ad; - ncq_tfs->slot = slot; - ncq_tfs->cmdh = &((AHCICmdHdr *)ad->lst)[slot]; - ncq_tfs->cmd = ncq_fis->command; - ncq_tfs->lba = ((uint64_t)ncq_fis->lba5 << 40) | - ((uint64_t)ncq_fis->lba4 << 32) | - ((uint64_t)ncq_fis->lba3 << 24) | - ((uint64_t)ncq_fis->lba2 << 16) | - ((uint64_t)ncq_fis->lba1 << 8) | - (uint64_t)ncq_fis->lba0; - ncq_tfs->tag = tag; - - /* Sanity-check the NCQ packet */ - if (tag != slot) { - DPRINTF(port, "Warn: NCQ slot (%d) did not match the given tag (%d)\n", - slot, tag); - } - - if (ncq_fis->aux0 || ncq_fis->aux1 || ncq_fis->aux2 || ncq_fis->aux3) { - DPRINTF(port, "Warn: Attempt to use NCQ auxiliary fields.\n"); - } - if (ncq_fis->prio || ncq_fis->icc) { - DPRINTF(port, "Warn: Unsupported attempt to use PRIO/ICC fields\n"); - } - if (ncq_fis->fua & NCQ_FIS_FUA_MASK) { - DPRINTF(port, "Warn: Unsupported attempt to use Force Unit Access\n"); - } - if (ncq_fis->tag & NCQ_FIS_RARC_MASK) { - DPRINTF(port, "Warn: Unsupported attempt to use Rebuild Assist\n"); - } - - ncq_tfs->sector_count = ((ncq_fis->sector_count_high << 8) | - ncq_fis->sector_count_low); - if (!ncq_tfs->sector_count) { - ncq_tfs->sector_count = 0x10000; - } - size = ncq_tfs->sector_count * 512; - ahci_populate_sglist(ad, &ncq_tfs->sglist, ncq_tfs->cmdh, size, 0); - - if (ncq_tfs->sglist.size < size) { - error_report("ahci: PRDT length for NCQ command (0x%zx) " - "is smaller than the requested size (0x%zx)", - ncq_tfs->sglist.size, size); - qemu_sglist_destroy(&ncq_tfs->sglist); - ncq_err(ncq_tfs); - ahci_trigger_irq(ad->hba, ad, PORT_IRQ_OVERFLOW); - return; - } else if (ncq_tfs->sglist.size != size) { - DPRINTF(port, "Warn: PRDTL (0x%zx)" - " does not match requested size (0x%zx)", - ncq_tfs->sglist.size, size); - } - - DPRINTF(port, "NCQ transfer LBA from %"PRId64" to %"PRId64", " - "drive max %"PRId64"\n", - ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 1, - ide_state->nb_sectors - 1); - - execute_ncq_command(ncq_tfs); -} - -static AHCICmdHdr *get_cmd_header(AHCIState *s, uint8_t port, uint8_t slot) -{ - if (port >= s->ports || slot >= AHCI_MAX_CMDS) { - return NULL; - } - - return s->dev[port].lst ? &((AHCICmdHdr *)s->dev[port].lst)[slot] : NULL; -} - -static void handle_reg_h2d_fis(AHCIState *s, int port, - uint8_t slot, uint8_t *cmd_fis) -{ - IDEState *ide_state = &s->dev[port].port.ifs[0]; - AHCICmdHdr *cmd = get_cmd_header(s, port, slot); - uint16_t opts = le16_to_cpu(cmd->opts); - - if (cmd_fis[1] & 0x0F) { - DPRINTF(port, "Port Multiplier not supported." - " cmd_fis[0]=%02x cmd_fis[1]=%02x cmd_fis[2]=%02x\n", - cmd_fis[0], cmd_fis[1], cmd_fis[2]); - return; - } - - if (cmd_fis[1] & 0x70) { - DPRINTF(port, "Reserved flags set in H2D Register FIS." - " cmd_fis[0]=%02x cmd_fis[1]=%02x cmd_fis[2]=%02x\n", - cmd_fis[0], cmd_fis[1], cmd_fis[2]); - return; - } - - if (!(cmd_fis[1] & SATA_FIS_REG_H2D_UPDATE_COMMAND_REGISTER)) { - switch (s->dev[port].port_state) { - case STATE_RUN: - if (cmd_fis[15] & ATA_SRST) { - s->dev[port].port_state = STATE_RESET; - } - break; - case STATE_RESET: - if (!(cmd_fis[15] & ATA_SRST)) { - ahci_reset_port(s, port); - } - break; - } - return; - } - - /* Check for NCQ command */ - if (is_ncq(cmd_fis[2])) { - process_ncq_command(s, port, cmd_fis, slot); - return; - } - - /* Decompose the FIS: - * AHCI does not interpret FIS packets, it only forwards them. - * SATA 1.0 describes how to decode LBA28 and CHS FIS packets. - * Later specifications, e.g, SATA 3.2, describe LBA48 FIS packets. - * - * ATA4 describes sector number for LBA28/CHS commands. - * ATA6 describes sector number for LBA48 commands. - * ATA8 deprecates CHS fully, describing only LBA28/48. - * - * We dutifully convert the FIS into IDE registers, and allow the - * core layer to interpret them as needed. */ - ide_state->feature = cmd_fis[3]; - ide_state->sector = cmd_fis[4]; /* LBA 7:0 */ - ide_state->lcyl = cmd_fis[5]; /* LBA 15:8 */ - ide_state->hcyl = cmd_fis[6]; /* LBA 23:16 */ - ide_state->select = cmd_fis[7]; /* LBA 27:24 (LBA28) */ - ide_state->hob_sector = cmd_fis[8]; /* LBA 31:24 */ - ide_state->hob_lcyl = cmd_fis[9]; /* LBA 39:32 */ - ide_state->hob_hcyl = cmd_fis[10]; /* LBA 47:40 */ - ide_state->hob_feature = cmd_fis[11]; - ide_state->nsector = (int64_t)((cmd_fis[13] << 8) | cmd_fis[12]); - /* 14, 16, 17, 18, 19: Reserved (SATA 1.0) */ - /* 15: Only valid when UPDATE_COMMAND not set. */ - - /* Copy the ACMD field (ATAPI packet, if any) from the AHCI command - * table to ide_state->io_buffer */ - if (opts & AHCI_CMD_ATAPI) { - memcpy(ide_state->io_buffer, &cmd_fis[AHCI_COMMAND_TABLE_ACMD], 0x10); - debug_print_fis(ide_state->io_buffer, 0x10); - s->dev[port].done_atapi_packet = false; - /* XXX send PIO setup FIS */ - } - - ide_state->error = 0; - - /* Reset transferred byte counter */ - cmd->status = 0; - - /* We're ready to process the command in FIS byte 2. */ - ide_exec_cmd(&s->dev[port].port, cmd_fis[2]); -} - -static int handle_cmd(AHCIState *s, int port, uint8_t slot) -{ - IDEState *ide_state; - uint64_t tbl_addr; - AHCICmdHdr *cmd; - uint8_t *cmd_fis; - dma_addr_t cmd_len; - - if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) { - /* Engine currently busy, try again later */ - DPRINTF(port, "engine busy\n"); - return -1; - } - - if (!s->dev[port].lst) { - DPRINTF(port, "error: lst not given but cmd handled"); - return -1; - } - cmd = get_cmd_header(s, port, slot); - /* remember current slot handle for later */ - s->dev[port].cur_cmd = cmd; - - /* The device we are working for */ - ide_state = &s->dev[port].port.ifs[0]; - if (!ide_state->blk) { - DPRINTF(port, "error: guest accessed unused port"); - return -1; - } - - tbl_addr = le64_to_cpu(cmd->tbl_addr); - cmd_len = 0x80; - cmd_fis = dma_memory_map(s->as, tbl_addr, &cmd_len, - DMA_DIRECTION_FROM_DEVICE); - if (!cmd_fis) { - DPRINTF(port, "error: guest passed us an invalid cmd fis\n"); - return -1; - } else if (cmd_len != 0x80) { - ahci_trigger_irq(s, &s->dev[port], PORT_IRQ_HBUS_ERR); - DPRINTF(port, "error: dma_memory_map failed: " - "(len(%02"PRIx64") != 0x80)\n", - cmd_len); - goto out; - } - debug_print_fis(cmd_fis, 0x80); - - switch (cmd_fis[0]) { - case SATA_FIS_TYPE_REGISTER_H2D: - handle_reg_h2d_fis(s, port, slot, cmd_fis); - break; - default: - DPRINTF(port, "unknown command cmd_fis[0]=%02x cmd_fis[1]=%02x " - "cmd_fis[2]=%02x\n", cmd_fis[0], cmd_fis[1], - cmd_fis[2]); - break; - } - -out: - dma_memory_unmap(s->as, cmd_fis, cmd_len, DMA_DIRECTION_FROM_DEVICE, - cmd_len); - - if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) { - /* async command, complete later */ - s->dev[port].busy_slot = slot; - return -1; - } - - /* done handling the command */ - return 0; -} - -/* DMA dev <-> ram */ -static void ahci_start_transfer(IDEDMA *dma) -{ - AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); - IDEState *s = &ad->port.ifs[0]; - uint32_t size = (uint32_t)(s->data_end - s->data_ptr); - /* write == ram -> device */ - uint16_t opts = le16_to_cpu(ad->cur_cmd->opts); - int is_write = opts & AHCI_CMD_WRITE; - int is_atapi = opts & AHCI_CMD_ATAPI; - int has_sglist = 0; - - if (is_atapi && !ad->done_atapi_packet) { - /* already prepopulated iobuffer */ - ad->done_atapi_packet = true; - size = 0; - goto out; - } - - if (ahci_dma_prepare_buf(dma, size)) { - has_sglist = 1; - } - - DPRINTF(ad->port_no, "%sing %d bytes on %s w/%s sglist\n", - is_write ? "writ" : "read", size, is_atapi ? "atapi" : "ata", - has_sglist ? "" : "o"); - - if (has_sglist && size) { - if (is_write) { - dma_buf_write(s->data_ptr, size, &s->sg); - } else { - dma_buf_read(s->data_ptr, size, &s->sg); - } - } - -out: - /* declare that we processed everything */ - s->data_ptr = s->data_end; - - /* Update number of transferred bytes, destroy sglist */ - dma_buf_commit(s, size); - - s->end_transfer_func(s); - - if (!(s->status & DRQ_STAT)) { - /* done with PIO send/receive */ - ahci_write_fis_pio(ad, le32_to_cpu(ad->cur_cmd->status)); - } -} - -static void ahci_start_dma(IDEDMA *dma, IDEState *s, - BlockCompletionFunc *dma_cb) -{ - AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); - DPRINTF(ad->port_no, "\n"); - s->io_buffer_offset = 0; - dma_cb(s, 0); -} - -static void ahci_restart_dma(IDEDMA *dma) -{ - /* Nothing to do, ahci_start_dma already resets s->io_buffer_offset. */ -} - -/** - * IDE/PIO restarts are handled by the core layer, but NCQ commands - * need an extra kick from the AHCI HBA. - */ -static void ahci_restart(IDEDMA *dma) -{ - AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); - int i; - - for (i = 0; i < AHCI_MAX_CMDS; i++) { - NCQTransferState *ncq_tfs = &ad->ncq_tfs[i]; - if (ncq_tfs->halt) { - execute_ncq_command(ncq_tfs); - } - } -} - -/** - * 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) -{ - AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); - IDEState *s = &ad->port.ifs[0]; - - if (ahci_populate_sglist(ad, &s->sg, ad->cur_cmd, - limit, s->io_buffer_offset) == -1) { - DPRINTF(ad->port_no, "ahci_dma_prepare_buf failed.\n"); - return -1; - } - s->io_buffer_size = s->sg.size; - - DPRINTF(ad->port_no, "len=%#x\n", s->io_buffer_size); - return s->io_buffer_size; -} - -/** - * 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); - - tx_bytes += le32_to_cpu(ad->cur_cmd->status); - ad->cur_cmd->status = cpu_to_le32(tx_bytes); -} - -static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) -{ - AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); - IDEState *s = &ad->port.ifs[0]; - uint8_t *p = s->io_buffer + s->io_buffer_index; - int l = s->io_buffer_size - s->io_buffer_index; - - if (ahci_populate_sglist(ad, &s->sg, ad->cur_cmd, l, s->io_buffer_offset)) { - return 0; - } - - if (is_write) { - dma_buf_read(p, l, &s->sg); - } else { - dma_buf_write(p, l, &s->sg); - } - - /* free sglist, update byte count */ - dma_buf_commit(s, l); - - s->io_buffer_index += l; - - DPRINTF(ad->port_no, "len=%#x\n", l); - - return 1; -} - -static void ahci_cmd_done(IDEDMA *dma) -{ - AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); - - DPRINTF(ad->port_no, "cmd done\n"); - - /* update d2h status */ - ahci_write_fis_d2h(ad); - - if (!ad->check_bh) { - /* maybe we still have something to process, check later */ - ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad); - qemu_bh_schedule(ad->check_bh); - } -} - -static void ahci_irq_set(void *opaque, int n, int level) -{ -} - -static const IDEDMAOps ahci_dma_ops = { - .start_dma = ahci_start_dma, - .restart = ahci_restart, - .restart_dma = ahci_restart_dma, - .start_transfer = ahci_start_transfer, - .prepare_buf = ahci_dma_prepare_buf, - .commit_buf = ahci_commit_buf, - .rw_buf = ahci_dma_rw_buf, - .cmd_done = ahci_cmd_done, -}; - -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; - - s->as = as; - s->ports = ports; - s->dev = g_new0(AHCIDevice, ports); - ahci_reg_init(s); - irqs = qemu_allocate_irqs(ahci_irq_set, s, s->ports); - for (i = 0; i < s->ports; i++) { - AHCIDevice *ad = &s->dev[i]; - - ide_bus_new(&ad->port, sizeof(ad->port), qdev, i, 1); - ide_init2(&ad->port, irqs[i]); - - ad->hba = s; - ad->port_no = i; - ad->port.dma = &ad->dma; - ad->port.dma->ops = &ahci_dma_ops; - ide_register_restart_cb(&ad->port); - } -} - -void ahci_uninit(AHCIState *s) -{ - g_free(s->dev); -} - -void ahci_reset(AHCIState *s) -{ - AHCIPortRegs *pr; - int i; - - s->control_regs.irqstatus = 0; - /* AHCI Enable (AE) - * The implementation of this bit is dependent upon the value of the - * CAP.SAM bit. If CAP.SAM is '0', then GHC.AE shall be read-write and - * shall have a reset value of '0'. If CAP.SAM is '1', then AE shall be - * read-only and shall have a reset value of '1'. - * - * We set HOST_CAP_AHCI so we must enable AHCI at reset. - */ - s->control_regs.ghc = HOST_CTL_AHCI_EN; - - for (i = 0; i < s->ports; i++) { - pr = &s->dev[i].port_regs; - pr->irq_stat = 0; - pr->irq_mask = 0; - pr->scr_ctl = 0; - pr->cmd = PORT_CMD_SPIN_UP | PORT_CMD_POWER_ON; - ahci_reset_port(s, i); - } -} - -static const VMStateDescription vmstate_ncq_tfs = { - .name = "ncq state", - .version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(sector_count, NCQTransferState), - VMSTATE_UINT64(lba, NCQTransferState), - VMSTATE_UINT8(tag, NCQTransferState), - VMSTATE_UINT8(cmd, NCQTransferState), - VMSTATE_UINT8(slot, NCQTransferState), - VMSTATE_BOOL(used, NCQTransferState), - VMSTATE_BOOL(halt, NCQTransferState), - VMSTATE_END_OF_LIST() - }, -}; - -static const VMStateDescription vmstate_ahci_device = { - .name = "ahci port", - .version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_IDE_BUS(port, AHCIDevice), - VMSTATE_IDE_DRIVE(port.ifs[0], AHCIDevice), - VMSTATE_UINT32(port_state, AHCIDevice), - VMSTATE_UINT32(finished, AHCIDevice), - VMSTATE_UINT32(port_regs.lst_addr, AHCIDevice), - VMSTATE_UINT32(port_regs.lst_addr_hi, AHCIDevice), - VMSTATE_UINT32(port_regs.fis_addr, AHCIDevice), - VMSTATE_UINT32(port_regs.fis_addr_hi, AHCIDevice), - VMSTATE_UINT32(port_regs.irq_stat, AHCIDevice), - VMSTATE_UINT32(port_regs.irq_mask, AHCIDevice), - VMSTATE_UINT32(port_regs.cmd, AHCIDevice), - VMSTATE_UINT32(port_regs.tfdata, AHCIDevice), - VMSTATE_UINT32(port_regs.sig, AHCIDevice), - VMSTATE_UINT32(port_regs.scr_stat, AHCIDevice), - VMSTATE_UINT32(port_regs.scr_ctl, AHCIDevice), - VMSTATE_UINT32(port_regs.scr_err, AHCIDevice), - VMSTATE_UINT32(port_regs.scr_act, AHCIDevice), - VMSTATE_UINT32(port_regs.cmd_issue, AHCIDevice), - VMSTATE_BOOL(done_atapi_packet, AHCIDevice), - VMSTATE_INT32(busy_slot, AHCIDevice), - VMSTATE_BOOL(init_d2h_sent, AHCIDevice), - VMSTATE_STRUCT_ARRAY(ncq_tfs, AHCIDevice, AHCI_MAX_CMDS, - 1, vmstate_ncq_tfs, NCQTransferState), - VMSTATE_END_OF_LIST() - }, -}; - -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; - } - - /* 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; - } - - for (j = 0; j < AHCI_MAX_CMDS; j++) { - ncq_tfs = &ad->ncq_tfs[j]; - ncq_tfs->drive = ad; - - if (ncq_tfs->used != ncq_tfs->halt) { - return -1; - } - if (!ncq_tfs->halt) { - continue; - } - if (!is_ncq(ncq_tfs->cmd)) { - return -1; - } - if (ncq_tfs->slot != ncq_tfs->tag) { - return -1; - } - /* If ncq_tfs->halt is justly set, the engine should be engaged, - * and the command list buffer should be mapped. */ - ncq_tfs->cmdh = get_cmd_header(s, i, ncq_tfs->slot); - if (!ncq_tfs->cmdh) { - return -1; - } - ahci_populate_sglist(ncq_tfs->drive, &ncq_tfs->sglist, - ncq_tfs->cmdh, ncq_tfs->sector_count * 512, - 0); - if (ncq_tfs->sector_count != ncq_tfs->sglist.size >> 9) { - return -1; - } - } - - - /* - * If an error is present, ad->busy_slot will be valid and not -1. - * In this case, an operation is waiting to resume and will re-check - * for additional AHCI commands to execute upon completion. - * - * In the case where no error was present, busy_slot will be -1, - * and we should check to see if there are additional commands waiting. - */ - if (ad->busy_slot == -1) { - check_cmd(s, i); - } else { - /* We are in the middle of a command, and may need to access - * the command header in guest memory again. */ - if (ad->busy_slot < 0 || ad->busy_slot >= AHCI_MAX_CMDS) { - return -1; - } - ad->cur_cmd = get_cmd_header(s, i, ad->busy_slot); - } - } - - return 0; -} - -const VMStateDescription vmstate_ahci = { - .name = "ahci", - .version_id = 1, - .post_load = ahci_state_post_load, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_VARRAY_POINTER_INT32(dev, AHCIState, ports, - vmstate_ahci_device, AHCIDevice), - VMSTATE_UINT32(control_regs.cap, AHCIState), - VMSTATE_UINT32(control_regs.ghc, AHCIState), - VMSTATE_UINT32(control_regs.irqstatus, AHCIState), - VMSTATE_UINT32(control_regs.impl, AHCIState), - VMSTATE_UINT32(control_regs.version, AHCIState), - VMSTATE_UINT32(idp_index, AHCIState), - VMSTATE_INT32_EQUAL(ports, AHCIState), - VMSTATE_END_OF_LIST() - }, -}; - -static const VMStateDescription vmstate_sysbus_ahci = { - .name = "sysbus-ahci", - .fields = (VMStateField[]) { - VMSTATE_AHCI(ahci, SysbusAHCIState), - VMSTATE_END_OF_LIST() - }, -}; - -static void sysbus_ahci_reset(DeviceState *dev) -{ - SysbusAHCIState *s = SYSBUS_AHCI(dev); - - ahci_reset(&s->ahci); -} - -static void sysbus_ahci_init(Object *obj) -{ - SysbusAHCIState *s = SYSBUS_AHCI(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - 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(), -}; - -static void sysbus_ahci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = sysbus_ahci_realize; - dc->vmsd = &vmstate_sysbus_ahci; - dc->props = sysbus_ahci_properties; - dc->reset = sysbus_ahci_reset; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -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) - -void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **hd) -{ - AHCIPCIState *d = ICH_AHCI(dev); - AHCIState *ahci = &d->ahci; - int i; - - for (i = 0; i < ahci->ports; i++) { - if (hd[i] == NULL) { - continue; - } - ide_create_drive(&ahci->dev[i].port, 0, hd[i]); - } - -} diff --git a/qemu/hw/ide/ahci.h b/qemu/hw/ide/ahci.h deleted file mode 100644 index bc777ed5c..000000000 --- a/qemu/hw/ide/ahci.h +++ /dev/null @@ -1,405 +0,0 @@ -/* - * QEMU AHCI Emulation - * - * Copyright (c) 2010 qiaochong@loongson.cn - * Copyright (c) 2010 Roland Elek - * Copyright (c) 2010 Sebastian Herbszt - * Copyright (c) 2010 Alexander Graf - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#ifndef HW_IDE_AHCI_H -#define HW_IDE_AHCI_H - -#include - -#define AHCI_MEM_BAR_SIZE 0x1000 -#define AHCI_MAX_PORTS 32 -#define AHCI_MAX_SG 168 /* hardware max is 64K */ -#define AHCI_DMA_BOUNDARY 0xffffffff -#define AHCI_USE_CLUSTERING 0 -#define AHCI_MAX_CMDS 32 -#define AHCI_CMD_SZ 32 -#define AHCI_CMD_SLOT_SZ (AHCI_MAX_CMDS * AHCI_CMD_SZ) -#define AHCI_RX_FIS_SZ 256 -#define AHCI_CMD_TBL_CDB 0x40 -#define AHCI_CMD_TBL_HDR_SZ 0x80 -#define AHCI_CMD_TBL_SZ (AHCI_CMD_TBL_HDR_SZ + (AHCI_MAX_SG * 16)) -#define AHCI_CMD_TBL_AR_SZ (AHCI_CMD_TBL_SZ * AHCI_MAX_CMDS) -#define AHCI_PORT_PRIV_DMA_SZ (AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_AR_SZ + \ - AHCI_RX_FIS_SZ) - -#define AHCI_IRQ_ON_SG (1U << 31) -#define AHCI_CMD_ATAPI (1 << 5) -#define AHCI_CMD_WRITE (1 << 6) -#define AHCI_CMD_PREFETCH (1 << 7) -#define AHCI_CMD_RESET (1 << 8) -#define AHCI_CMD_CLR_BUSY (1 << 10) - -#define RX_FIS_D2H_REG 0x40 /* offset of D2H Register FIS data */ -#define RX_FIS_SDB 0x58 /* offset of SDB FIS data */ -#define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */ - -/* global controller registers */ -#define HOST_CAP 0x00 /* host capabilities */ -#define HOST_CTL 0x04 /* global host control */ -#define HOST_IRQ_STAT 0x08 /* interrupt status */ -#define HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */ -#define HOST_VERSION 0x10 /* AHCI spec. version compliancy */ - -/* HOST_CTL bits */ -#define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */ -#define HOST_CTL_IRQ_EN (1 << 1) /* global IRQ enable */ -#define HOST_CTL_AHCI_EN (1U << 31) /* AHCI enabled */ - -/* HOST_CAP bits */ -#define HOST_CAP_SSC (1 << 14) /* Slumber capable */ -#define HOST_CAP_AHCI (1 << 18) /* AHCI only */ -#define HOST_CAP_CLO (1 << 24) /* Command List Override support */ -#define HOST_CAP_SSS (1 << 27) /* Staggered Spin-up */ -#define HOST_CAP_NCQ (1 << 30) /* Native Command Queueing */ -#define HOST_CAP_64 (1U << 31) /* PCI DAC (64-bit DMA) support */ - -/* registers for each SATA port */ -#define PORT_LST_ADDR 0x00 /* command list DMA addr */ -#define PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */ -#define PORT_FIS_ADDR 0x08 /* FIS rx buf addr */ -#define PORT_FIS_ADDR_HI 0x0c /* FIS rx buf addr hi */ -#define PORT_IRQ_STAT 0x10 /* interrupt status */ -#define PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */ -#define PORT_CMD 0x18 /* port command */ -#define PORT_TFDATA 0x20 /* taskfile data */ -#define PORT_SIG 0x24 /* device TF signature */ -#define PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */ -#define PORT_SCR_CTL 0x2c /* SATA phy register: SControl */ -#define PORT_SCR_ERR 0x30 /* SATA phy register: SError */ -#define PORT_SCR_ACT 0x34 /* SATA phy register: SActive */ -#define PORT_CMD_ISSUE 0x38 /* command issue */ -#define PORT_RESERVED 0x3c /* reserved */ - -/* PORT_IRQ_{STAT,MASK} bits */ -#define PORT_IRQ_COLD_PRES (1U << 31) /* cold presence detect */ -#define PORT_IRQ_TF_ERR (1 << 30) /* task file error */ -#define PORT_IRQ_HBUS_ERR (1 << 29) /* host bus fatal error */ -#define PORT_IRQ_HBUS_DATA_ERR (1 << 28) /* host bus data error */ -#define PORT_IRQ_IF_ERR (1 << 27) /* interface fatal error */ -#define PORT_IRQ_IF_NONFATAL (1 << 26) /* interface non-fatal error */ -#define PORT_IRQ_OVERFLOW (1 << 24) /* xfer exhausted available S/G */ -#define PORT_IRQ_BAD_PMP (1 << 23) /* incorrect port multiplier */ - -#define PORT_IRQ_PHYRDY (1 << 22) /* PhyRdy changed */ -#define PORT_IRQ_DEV_ILCK (1 << 7) /* device interlock */ -#define PORT_IRQ_CONNECT (1 << 6) /* port connect change status */ -#define PORT_IRQ_SG_DONE (1 << 5) /* descriptor processed */ -#define PORT_IRQ_UNK_FIS (1 << 4) /* unknown FIS rx'd */ -#define PORT_IRQ_SDB_FIS (1 << 3) /* Set Device Bits FIS rx'd */ -#define PORT_IRQ_DMAS_FIS (1 << 2) /* DMA Setup FIS rx'd */ -#define PORT_IRQ_PIOS_FIS (1 << 1) /* PIO Setup FIS rx'd */ -#define PORT_IRQ_D2H_REG_FIS (1 << 0) /* D2H Register FIS rx'd */ - -#define PORT_IRQ_FREEZE (PORT_IRQ_HBUS_ERR | PORT_IRQ_IF_ERR | \ - PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY | \ - PORT_IRQ_UNK_FIS) -#define PORT_IRQ_ERROR (PORT_IRQ_FREEZE | PORT_IRQ_TF_ERR | \ - PORT_IRQ_HBUS_DATA_ERR) -#define DEF_PORT_IRQ (PORT_IRQ_ERROR | PORT_IRQ_SG_DONE | \ - PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS | \ - PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS) - -/* PORT_CMD bits */ -#define PORT_CMD_ATAPI (1 << 24) /* Device is ATAPI */ -#define PORT_CMD_LIST_ON (1 << 15) /* cmd list DMA engine running */ -#define PORT_CMD_FIS_ON (1 << 14) /* FIS DMA engine running */ -#define PORT_CMD_FIS_RX (1 << 4) /* Enable FIS receive DMA engine */ -#define PORT_CMD_CLO (1 << 3) /* Command list override */ -#define PORT_CMD_POWER_ON (1 << 2) /* Power up device */ -#define PORT_CMD_SPIN_UP (1 << 1) /* Spin up device */ -#define PORT_CMD_START (1 << 0) /* Enable port DMA engine */ - -#define PORT_CMD_ICC_MASK (0xfU << 28) /* i/f ICC state mask */ -#define PORT_CMD_ICC_ACTIVE (0x1 << 28) /* Put i/f in active state */ -#define PORT_CMD_ICC_PARTIAL (0x2 << 28) /* Put i/f in partial state */ -#define PORT_CMD_ICC_SLUMBER (0x6 << 28) /* Put i/f in slumber state */ - -#define PORT_CMD_RO_MASK 0x007dffe0 /* Which CMD bits are read only? */ - -/* ap->flags bits */ -#define AHCI_FLAG_NO_NCQ (1 << 24) -#define AHCI_FLAG_IGN_IRQ_IF_ERR (1 << 25) /* ignore IRQ_IF_ERR */ -#define AHCI_FLAG_HONOR_PI (1 << 26) /* honor PORTS_IMPL */ -#define AHCI_FLAG_IGN_SERR_INTERNAL (1 << 27) /* ignore SERR_INTERNAL */ -#define AHCI_FLAG_32BIT_ONLY (1 << 28) /* force 32bit */ - -#define ATA_SRST (1 << 2) /* software reset */ - -#define STATE_RUN 0 -#define STATE_RESET 1 - -#define SATA_SCR_SSTATUS_DET_NODEV 0x0 -#define SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP 0x3 - -#define SATA_SCR_SSTATUS_SPD_NODEV 0x00 -#define SATA_SCR_SSTATUS_SPD_GEN1 0x10 - -#define SATA_SCR_SSTATUS_IPM_NODEV 0x000 -#define SATA_SCR_SSTATUS_IPM_ACTIVE 0X100 - -#define AHCI_SCR_SCTL_DET 0xf - -#define SATA_FIS_TYPE_REGISTER_H2D 0x27 -#define SATA_FIS_REG_H2D_UPDATE_COMMAND_REGISTER 0x80 -#define SATA_FIS_TYPE_REGISTER_D2H 0x34 -#define SATA_FIS_TYPE_PIO_SETUP 0x5f -#define SATA_FIS_TYPE_SDB 0xA1 - -#define AHCI_CMD_HDR_CMD_FIS_LEN 0x1f -#define AHCI_CMD_HDR_PRDT_LEN 16 - -#define SATA_SIGNATURE_CDROM 0xeb140101 -#define SATA_SIGNATURE_DISK 0x00000101 - -#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x20 - /* Shouldn't this be 0x2c? */ - -#define AHCI_PORT_REGS_START_ADDR 0x100 -#define AHCI_PORT_ADDR_OFFSET_MASK 0x7f -#define AHCI_PORT_ADDR_OFFSET_LEN 0x80 - -#define AHCI_NUM_COMMAND_SLOTS 31 -#define AHCI_SUPPORTED_SPEED 20 -#define AHCI_SUPPORTED_SPEED_GEN1 1 -#define AHCI_VERSION_1_0 0x10000 - -#define AHCI_PROGMODE_MAJOR_REV_1 1 - -#define AHCI_COMMAND_TABLE_ACMD 0x40 - -#define AHCI_PRDT_SIZE_MASK 0x3fffff - -#define IDE_FEATURE_DMA 1 - -#define READ_FPDMA_QUEUED 0x60 -#define WRITE_FPDMA_QUEUED 0x61 -#define NCQ_NON_DATA 0x63 -#define RECEIVE_FPDMA_QUEUED 0x65 -#define SEND_FPDMA_QUEUED 0x64 - -#define NCQ_FIS_FUA_MASK 0x80 -#define NCQ_FIS_RARC_MASK 0x01 - -#define RES_FIS_DSFIS 0x00 -#define RES_FIS_PSFIS 0x20 -#define RES_FIS_RFIS 0x40 -#define RES_FIS_SDBFIS 0x58 -#define RES_FIS_UFIS 0x60 - -#define SATA_CAP_SIZE 0x8 -#define SATA_CAP_REV 0x2 -#define SATA_CAP_BAR 0x4 - -typedef struct AHCIControlRegs { - uint32_t cap; - uint32_t ghc; - uint32_t irqstatus; - uint32_t impl; - uint32_t version; -} AHCIControlRegs; - -typedef struct AHCIPortRegs { - uint32_t lst_addr; - uint32_t lst_addr_hi; - uint32_t fis_addr; - uint32_t fis_addr_hi; - uint32_t irq_stat; - uint32_t irq_mask; - uint32_t cmd; - uint32_t unused0; - uint32_t tfdata; - uint32_t sig; - uint32_t scr_stat; - uint32_t scr_ctl; - uint32_t scr_err; - uint32_t scr_act; - uint32_t cmd_issue; - uint32_t reserved; -} AHCIPortRegs; - -typedef struct AHCICmdHdr { - uint16_t opts; - uint16_t prdtl; - uint32_t status; - uint64_t tbl_addr; - uint32_t reserved[4]; -} QEMU_PACKED AHCICmdHdr; - -typedef struct AHCI_SG { - uint64_t addr; - uint32_t reserved; - uint32_t flags_size; -} QEMU_PACKED AHCI_SG; - -typedef struct AHCIDevice AHCIDevice; - -typedef struct NCQTransferState { - AHCIDevice *drive; - BlockAIOCB *aiocb; - AHCICmdHdr *cmdh; - QEMUSGList sglist; - BlockAcctCookie acct; - uint32_t sector_count; - uint64_t lba; - uint8_t tag; - uint8_t cmd; - uint8_t slot; - bool used; - bool halt; -} NCQTransferState; - -struct AHCIDevice { - IDEDMA dma; - IDEBus port; - int port_no; - uint32_t port_state; - uint32_t finished; - AHCIPortRegs port_regs; - struct AHCIState *hba; - QEMUBH *check_bh; - uint8_t *lst; - uint8_t *res_fis; - bool done_atapi_packet; - int32_t busy_slot; - bool init_d2h_sent; - AHCICmdHdr *cur_cmd; - NCQTransferState ncq_tfs[AHCI_MAX_CMDS]; -}; - -typedef struct AHCIState { - DeviceState *container; - - AHCIDevice *dev; - AHCIControlRegs control_regs; - MemoryRegion mem; - MemoryRegion idp; /* Index-Data Pair I/O port space */ - unsigned idp_offset; /* Offset of index in I/O port space */ - uint32_t idp_index; /* Current IDP index */ - int32_t ports; - qemu_irq irq; - AddressSpace *as; -} AHCIState; - -typedef struct AHCIPCIState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - AHCIState ahci; -} AHCIPCIState; - -#define TYPE_ICH9_AHCI "ich9-ahci" - -#define ICH_AHCI(obj) \ - OBJECT_CHECK(AHCIPCIState, (obj), TYPE_ICH9_AHCI) - -extern const VMStateDescription vmstate_ahci; - -#define VMSTATE_AHCI(_field, _state) { \ - .name = (stringify(_field)), \ - .size = sizeof(AHCIState), \ - .vmsd = &vmstate_ahci, \ - .flags = VMS_STRUCT, \ - .offset = vmstate_offset_value(_state, _field, AHCIState), \ -} - -/** - * NCQFrame is the same as a Register H2D FIS (described in SATA 3.2), - * but some fields have been re-mapped and re-purposed, as seen in - * SATA 3.2 section 13.6.4.1 ("READ FPDMA QUEUED") - * - * cmd_fis[3], feature 7:0, becomes sector count 7:0. - * cmd_fis[7], device 7:0, uses bit 7 as the Force Unit Access bit. - * cmd_fis[11], feature 15:8, becomes sector count 15:8. - * cmd_fis[12], count 7:0, becomes the NCQ TAG (7:3) and RARC bit (0) - * cmd_fis[13], count 15:8, becomes the priority value (7:6) - * bytes 16-19 become an le32 "auxiliary" field. - */ -typedef struct NCQFrame { - uint8_t fis_type; - uint8_t c; - uint8_t command; - uint8_t sector_count_low; /* (feature 7:0) */ - uint8_t lba0; - uint8_t lba1; - uint8_t lba2; - uint8_t fua; /* (device 7:0) */ - uint8_t lba3; - uint8_t lba4; - uint8_t lba5; - uint8_t sector_count_high; /* (feature 15:8) */ - uint8_t tag; /* (count 0:7) */ - uint8_t prio; /* (count 15:8) */ - uint8_t icc; - uint8_t control; - uint8_t aux0; - uint8_t aux1; - uint8_t aux2; - uint8_t aux3; -} QEMU_PACKED NCQFrame; - -typedef struct SDBFIS { - uint8_t type; - uint8_t flags; - uint8_t status; - uint8_t error; - uint32_t payload; -} QEMU_PACKED SDBFIS; - -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 deleted file mode 100644 index 2bb606c1c..000000000 --- a/qemu/hw/ide/atapi.c +++ /dev/null @@ -1,1367 +0,0 @@ -/* - * QEMU ATAPI Emulation - * - * Copyright (c) 2003 Fabrice Bellard - * Copyright (c) 2006 Openedhand Ltd. - * - * 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/ide/internal.h" -#include "hw/scsi/scsi.h" -#include "sysemu/block-backend.h" - -static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret); - -static void padstr8(uint8_t *buf, int buf_size, const char *src) -{ - int i; - for(i = 0; i < buf_size; i++) { - if (*src) - buf[i] = *src++; - else - buf[i] = ' '; - } -} - -static inline void cpu_to_ube16(uint8_t *buf, int val) -{ - buf[0] = val >> 8; - buf[1] = val & 0xff; -} - -static inline void cpu_to_ube32(uint8_t *buf, unsigned int val) -{ - buf[0] = val >> 24; - buf[1] = val >> 16; - buf[2] = val >> 8; - buf[3] = val & 0xff; -} - -static inline int ube16_to_cpu(const uint8_t *buf) -{ - return (buf[0] << 8) | buf[1]; -} - -static inline int ube32_to_cpu(const uint8_t *buf) -{ - return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; -} - -static void lba_to_msf(uint8_t *buf, int lba) -{ - lba += 150; - buf[0] = (lba / 75) / 60; - buf[1] = (lba / 75) % 60; - buf[2] = lba % 75; -} - -static inline int media_present(IDEState *s) -{ - return !s->tray_open && s->nb_sectors > 0; -} - -/* XXX: DVDs that could fit on a CD will be reported as a CD */ -static inline int media_is_dvd(IDEState *s) -{ - return (media_present(s) && s->nb_sectors > CD_MAX_SECTORS); -} - -static inline int media_is_cd(IDEState *s) -{ - return (media_present(s) && s->nb_sectors <= CD_MAX_SECTORS); -} - -static void cd_data_to_raw(uint8_t *buf, int lba) -{ - /* sync bytes */ - buf[0] = 0x00; - memset(buf + 1, 0xff, 10); - buf[11] = 0x00; - buf += 12; - /* MSF */ - lba_to_msf(buf, lba); - buf[3] = 0x01; /* mode 1 data */ - buf += 4; - /* data */ - buf += 2048; - /* XXX: ECC not computed */ - memset(buf, 0, 288); -} - -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); - -#ifdef DEBUG_IDE_ATAPI - printf("cd_read_sector_sync: lba=%d\n", s->lba); -#endif - - switch (s->cd_sector_size) { - case 2048: - ret = blk_read(s->blk, (int64_t)s->lba << 2, - s->io_buffer, 4); - break; - case 2352: - 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: - 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; - s->status = READY_STAT | SEEK_STAT; - s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; - ide_transfer_stop(s); - ide_set_irq(s->bus); -} - -void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc) -{ -#ifdef DEBUG_IDE_ATAPI - printf("atapi_cmd_error: sense=0x%x asc=0x%x\n", sense_key, asc); -#endif - s->error = sense_key << 4; - s->status = READY_STAT | ERR_STAT; - s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; - s->sense_key = sense_key; - s->asc = asc; - ide_transfer_stop(s); - ide_set_irq(s->bus); -} - -void ide_atapi_io_error(IDEState *s, int ret) -{ - /* XXX: handle more errors */ - if (ret == -ENOMEDIUM) { - ide_atapi_cmd_error(s, NOT_READY, - ASC_MEDIUM_NOT_PRESENT); - } else { - ide_atapi_cmd_error(s, ILLEGAL_REQUEST, - ASC_LOGICAL_BLOCK_OOR); - } -} - -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) -{ - int byte_count_limit, size, ret; -#ifdef DEBUG_IDE_ATAPI - printf("reply: tx_size=%d elem_tx_size=%d index=%d\n", - s->packet_transfer_size, - s->elementary_transfer_size, - s->io_buffer_index); -#endif - if (s->packet_transfer_size <= 0) { - /* end of transfer */ - ide_atapi_cmd_ok(s); - ide_set_irq(s->bus); -#ifdef DEBUG_IDE_ATAPI - 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) { - 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; - } - } - } - if (s->elementary_transfer_size > 0) { - /* there are some data left to transmit in this elementary - transfer */ - size = s->cd_sector_size - s->io_buffer_index; - if (size > s->elementary_transfer_size) - size = s->elementary_transfer_size; - s->packet_transfer_size -= size; - s->elementary_transfer_size -= size; - s->io_buffer_index += size; - ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size, - size, ide_atapi_cmd_reply_end); - } else { - /* a new transfer is needed */ - s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; - byte_count_limit = atapi_byte_count_limit(s); -#ifdef DEBUG_IDE_ATAPI - printf("byte_count_limit=%d\n", byte_count_limit); -#endif - size = s->packet_transfer_size; - if (size > byte_count_limit) { - /* byte count limit must be even if this case */ - if (byte_count_limit & 1) - byte_count_limit--; - size = byte_count_limit; - } - s->lcyl = size; - s->hcyl = size >> 8; - s->elementary_transfer_size = size; - /* we cannot transmit more than one sector at a time */ - if (s->lba != -1) { - if (size > (s->cd_sector_size - s->io_buffer_index)) - size = (s->cd_sector_size - s->io_buffer_index); - } - s->packet_transfer_size -= size; - s->elementary_transfer_size -= size; - s->io_buffer_index += size; - ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size, - size, ide_atapi_cmd_reply_end); - ide_set_irq(s->bus); -#ifdef DEBUG_IDE_ATAPI - printf("status=0x%x\n", s->status); -#endif - } - } -} - -/* send a reply of 'size' bytes in s->io_buffer to an ATAPI command */ -static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size) -{ - if (size > max_size) - size = max_size; - s->lba = -1; /* no sector read */ - s->packet_transfer_size = size; - s->io_buffer_size = size; /* dma: send the reply data as one chunk */ - s->elementary_transfer_size = 0; - - if (s->atapi_dma) { - block_acct_start(blk_get_stats(s->blk), &s->acct, size, - BLOCK_ACCT_READ); - s->status = READY_STAT | SEEK_STAT | DRQ_STAT; - ide_start_dma(s, ide_atapi_cmd_read_dma_cb); - } else { - s->status = READY_STAT | SEEK_STAT; - s->io_buffer_index = 0; - ide_atapi_cmd_reply_end(s); - } -} - -/* start a CD-CDROM read command */ -static void ide_atapi_cmd_read_pio(IDEState *s, int lba, int nb_sectors, - int sector_size) -{ - s->lba = lba; - s->packet_transfer_size = nb_sectors * sector_size; - s->elementary_transfer_size = 0; - s->io_buffer_index = sector_size; - s->cd_sector_size = sector_size; - - ide_atapi_cmd_reply_end(s); -} - -static void ide_atapi_cmd_check_status(IDEState *s) -{ -#ifdef DEBUG_IDE_ATAPI - printf("atapi_cmd_check_status\n"); -#endif - s->error = MC_ERR | (UNIT_ATTENTION << 4); - s->status = ERR_STAT; - s->nsector = 0; - ide_set_irq(s->bus); -} -/* ATAPI DMA support */ - -static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) -{ - IDEState *s = opaque; - int data_offset, n; - - if (ret < 0) { - 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) { - /* - * For a cdrom read sector command (s->lba != -1), - * adjust the lba for the next s->io_buffer_size chunk - * and dma the current chunk. - * For a command != read (s->lba == -1), just transfer - * the reply data. - */ - if (s->lba != -1) { - if (s->cd_sector_size == 2352) { - n = 1; - cd_data_to_raw(s->io_buffer, s->lba); - } else { - n = s->io_buffer_size >> 11; - } - s->lba += n; - } - s->packet_transfer_size -= s->io_buffer_size; - if (s->bus->dma->ops->rw_buf(s->bus->dma, 1) == 0) - goto eot; - } - - if (s->packet_transfer_size <= 0) { - s->status = READY_STAT | SEEK_STAT; - s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; - ide_set_irq(s->bus); - goto eot; - } - - s->io_buffer_index = 0; - if (s->cd_sector_size == 2352) { - n = 1; - s->io_buffer_size = s->cd_sector_size; - data_offset = 16; - } else { - n = s->packet_transfer_size >> 11; - if (n > (IDE_DMA_BUF_SECTORS / 4)) - n = (IDE_DMA_BUF_SECTORS / 4); - s->io_buffer_size = n * 2048; - data_offset = 0; - } -#ifdef DEBUG_AIO - printf("aio_read_cd: lba=%u n=%d\n", s->lba, n); -#endif - - s->bus->dma->iov.iov_base = (void *)(s->io_buffer + data_offset); - 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 = ide_buffered_readv(s, (int64_t)s->lba << 2, - &s->bus->dma->qiov, n * 4, - ide_atapi_cmd_read_dma_cb, s); - return; - -eot: - 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); -} - -/* start a CD-CDROM read command with DMA */ -/* XXX: test if DMA is available */ -static void ide_atapi_cmd_read_dma(IDEState *s, int lba, int nb_sectors, - int sector_size) -{ - s->lba = lba; - s->packet_transfer_size = nb_sectors * sector_size; - s->io_buffer_size = 0; - s->cd_sector_size = sector_size; - - block_acct_start(blk_get_stats(s->blk), &s->acct, s->packet_transfer_size, - BLOCK_ACCT_READ); - - /* XXX: check if BUSY_STAT should be set */ - s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT; - ide_start_dma(s, ide_atapi_cmd_read_dma_cb); -} - -static void ide_atapi_cmd_read(IDEState *s, int lba, int nb_sectors, - int sector_size) -{ -#ifdef DEBUG_IDE_ATAPI - printf("read %s: LBA=%d nb_sectors=%d\n", s->atapi_dma ? "dma" : "pio", - lba, nb_sectors); -#endif - if (s->atapi_dma) { - ide_atapi_cmd_read_dma(s, lba, nb_sectors, sector_size); - } else { - ide_atapi_cmd_read_pio(s, lba, nb_sectors, sector_size); - } -} - -void ide_atapi_dma_restart(IDEState *s) -{ - /* - * 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. - */ - 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, - uint16_t profile) -{ - uint8_t *buf_profile = buf + 12; /* start of profiles */ - - buf_profile += ((*index) * 4); /* start of indexed profile */ - cpu_to_ube16 (buf_profile, profile); - buf_profile[2] = ((buf_profile[0] == buf[6]) && (buf_profile[1] == buf[7])); - - /* each profile adds 4 bytes to the response */ - (*index)++; - buf[11] += 4; /* Additional Length */ - - return 4; -} - -static int ide_dvd_read_structure(IDEState *s, int format, - const uint8_t *packet, uint8_t *buf) -{ - switch (format) { - case 0x0: /* Physical format information */ - { - int layer = packet[6]; - uint64_t total_sectors; - - if (layer != 0) - return -ASC_INV_FIELD_IN_CMD_PACKET; - - total_sectors = s->nb_sectors >> 2; - if (total_sectors == 0) { - return -ASC_MEDIUM_NOT_PRESENT; - } - - buf[4] = 1; /* DVD-ROM, part version 1 */ - buf[5] = 0xf; /* 120mm disc, minimum rate unspecified */ - buf[6] = 1; /* one layer, read-only (per MMC-2 spec) */ - buf[7] = 0; /* default densities */ - - /* FIXME: 0x30000 per spec? */ - cpu_to_ube32(buf + 8, 0); /* start sector */ - cpu_to_ube32(buf + 12, total_sectors - 1); /* end sector */ - cpu_to_ube32(buf + 16, total_sectors - 1); /* l0 end sector */ - - /* Size of buffer, not including 2 byte size field */ - stw_be_p(buf, 2048 + 2); - - /* 2k data + 4 byte header */ - return (2048 + 4); - } - - case 0x01: /* DVD copyright information */ - buf[4] = 0; /* no copyright data */ - buf[5] = 0; /* no region restrictions */ - - /* Size of buffer, not including 2 byte size field */ - stw_be_p(buf, 4 + 2); - - /* 4 byte header + 4 byte data */ - return (4 + 4); - - case 0x03: /* BCA information - invalid field for no BCA info */ - return -ASC_INV_FIELD_IN_CMD_PACKET; - - case 0x04: /* DVD disc manufacturing information */ - /* Size of buffer, not including 2 byte size field */ - stw_be_p(buf, 2048 + 2); - - /* 2k data + 4 byte header */ - return (2048 + 4); - - case 0xff: - /* - * This lists all the command capabilities above. Add new ones - * in order and update the length and buffer return values. - */ - - buf[4] = 0x00; /* Physical format */ - buf[5] = 0x40; /* Not writable, is readable */ - stw_be_p(buf + 6, 2048 + 4); - - buf[8] = 0x01; /* Copyright info */ - buf[9] = 0x40; /* Not writable, is readable */ - stw_be_p(buf + 10, 4 + 4); - - buf[12] = 0x03; /* BCA info */ - buf[13] = 0x40; /* Not writable, is readable */ - stw_be_p(buf + 14, 188 + 4); - - buf[16] = 0x04; /* Manufacturing info */ - buf[17] = 0x40; /* Not writable, is readable */ - stw_be_p(buf + 18, 2048 + 4); - - /* Size of buffer, not including 2 byte size field */ - stw_be_p(buf, 16 + 2); - - /* data written + 4 byte header */ - return (16 + 4); - - default: /* TODO: formats beyond DVD-ROM requires */ - return -ASC_INV_FIELD_IN_CMD_PACKET; - } -} - -static unsigned int event_status_media(IDEState *s, - uint8_t *buf) -{ - uint8_t event_code, media_status; - - media_status = 0; - if (s->tray_open) { - media_status = MS_TRAY_OPEN; - } else if (blk_is_inserted(s->blk)) { - media_status = MS_MEDIA_PRESENT; - } - - /* Event notification descriptor */ - event_code = MEC_NO_CHANGE; - if (media_status != MS_TRAY_OPEN) { - if (s->events.new_media) { - event_code = MEC_NEW_MEDIA; - s->events.new_media = false; - } else if (s->events.eject_request) { - event_code = MEC_EJECT_REQUESTED; - s->events.eject_request = false; - } - } - - buf[4] = event_code; - buf[5] = media_status; - - /* These fields are reserved, just clear them. */ - buf[6] = 0; - buf[7] = 0; - - return 8; /* We wrote to 4 extra bytes from the header */ -} - -static void cmd_get_event_status_notification(IDEState *s, - uint8_t *buf) -{ - const uint8_t *packet = buf; - - struct { - uint8_t opcode; - uint8_t polled; /* lsb bit is polled; others are reserved */ - uint8_t reserved2[2]; - uint8_t class; - uint8_t reserved3[2]; - uint16_t len; - uint8_t control; - } QEMU_PACKED *gesn_cdb; - - struct { - uint16_t len; - uint8_t notification_class; - uint8_t supported_events; - } QEMU_PACKED *gesn_event_header; - unsigned int max_len, used_len; - - gesn_cdb = (void *)packet; - gesn_event_header = (void *)buf; - - max_len = be16_to_cpu(gesn_cdb->len); - - /* It is fine by the MMC spec to not support async mode operations */ - if (!(gesn_cdb->polled & 0x01)) { /* asynchronous mode */ - /* Only polling is supported, asynchronous mode is not. */ - ide_atapi_cmd_error(s, ILLEGAL_REQUEST, - ASC_INV_FIELD_IN_CMD_PACKET); - return; - } - - /* polling mode operation */ - - /* - * These are the supported events. - * - * We currently only support requests of the 'media' type. - * Notification class requests and supported event classes are bitmasks, - * but they are build from the same values as the "notification class" - * field. - */ - gesn_event_header->supported_events = 1 << GESN_MEDIA; - - /* - * We use |= below to set the class field; other bits in this byte - * are reserved now but this is useful to do if we have to use the - * reserved fields later. - */ - gesn_event_header->notification_class = 0; - - /* - * Responses to requests are to be based on request priority. The - * notification_class_request_type enum above specifies the - * priority: upper elements are higher prio than lower ones. - */ - if (gesn_cdb->class & (1 << GESN_MEDIA)) { - gesn_event_header->notification_class |= GESN_MEDIA; - used_len = event_status_media(s, buf); - } else { - gesn_event_header->notification_class = 0x80; /* No event available */ - used_len = sizeof(*gesn_event_header); - } - gesn_event_header->len = cpu_to_be16(used_len - - sizeof(*gesn_event_header)); - ide_atapi_cmd_reply(s, used_len, max_len); -} - -static void cmd_request_sense(IDEState *s, uint8_t *buf) -{ - int max_len = buf[4]; - - memset(buf, 0, 18); - buf[0] = 0x70 | (1 << 7); - buf[2] = s->sense_key; - buf[7] = 10; - buf[12] = s->asc; - - if (s->sense_key == UNIT_ATTENTION) { - s->sense_key = NO_SENSE; - } - - ide_atapi_cmd_reply(s, 18, max_len); -} - -static void cmd_inquiry(IDEState *s, uint8_t *buf) -{ - uint8_t page_code = buf[2]; - int max_len = buf[4]; - - unsigned idx = 0; - unsigned size_idx; - unsigned preamble_len; - - /* If the EVPD (Enable Vital Product Data) bit is set in byte 1, - * we are being asked for a specific page of info indicated by byte 2. */ - if (buf[1] & 0x01) { - preamble_len = 4; - size_idx = 3; - - buf[idx++] = 0x05; /* CD-ROM */ - buf[idx++] = page_code; /* Page Code */ - buf[idx++] = 0x00; /* reserved */ - idx++; /* length (set later) */ - - switch (page_code) { - case 0x00: - /* Supported Pages: List of supported VPD responses. */ - buf[idx++] = 0x00; /* 0x00: Supported Pages, and: */ - buf[idx++] = 0x83; /* 0x83: Device Identification. */ - break; - - case 0x83: - /* Device Identification. Each entry is optional, but the entries - * included here are modeled after libata's VPD responses. - * If the response is given, at least one entry must be present. */ - - /* Entry 1: Serial */ - if (idx + 24 > max_len) { - /* Not enough room for even the first entry: */ - /* 4 byte header + 20 byte string */ - ide_atapi_cmd_error(s, ILLEGAL_REQUEST, - ASC_DATA_PHASE_ERROR); - return; - } - buf[idx++] = 0x02; /* Ascii */ - buf[idx++] = 0x00; /* Vendor Specific */ - buf[idx++] = 0x00; - buf[idx++] = 20; /* Remaining length */ - padstr8(buf + idx, 20, s->drive_serial_str); - idx += 20; - - /* Entry 2: Drive Model and Serial */ - if (idx + 72 > max_len) { - /* 4 (header) + 8 (vendor) + 60 (model & serial) */ - goto out; - } - buf[idx++] = 0x02; /* Ascii */ - buf[idx++] = 0x01; /* T10 Vendor */ - buf[idx++] = 0x00; - buf[idx++] = 68; - padstr8(buf + idx, 8, "ATA"); /* Generic T10 vendor */ - idx += 8; - padstr8(buf + idx, 40, s->drive_model_str); - idx += 40; - padstr8(buf + idx, 20, s->drive_serial_str); - idx += 20; - - /* Entry 3: WWN */ - if (s->wwn && (idx + 12 <= max_len)) { - /* 4 byte header + 8 byte wwn */ - buf[idx++] = 0x01; /* Binary */ - buf[idx++] = 0x03; /* NAA */ - buf[idx++] = 0x00; - buf[idx++] = 0x08; - stq_be_p(&buf[idx], s->wwn); - idx += 8; - } - break; - - default: - /* SPC-3, revision 23 sec. 6.4 */ - ide_atapi_cmd_error(s, ILLEGAL_REQUEST, - ASC_INV_FIELD_IN_CMD_PACKET); - return; - } - } else { - preamble_len = 5; - size_idx = 4; - - buf[0] = 0x05; /* CD-ROM */ - buf[1] = 0x80; /* removable */ - buf[2] = 0x00; /* ISO */ - buf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */ - /* buf[size_idx] set below. */ - buf[5] = 0; /* reserved */ - buf[6] = 0; /* reserved */ - buf[7] = 0; /* reserved */ - padstr8(buf + 8, 8, "QEMU"); - padstr8(buf + 16, 16, "QEMU DVD-ROM"); - padstr8(buf + 32, 4, s->version); - idx = 36; - } - - out: - buf[size_idx] = idx - preamble_len; - ide_atapi_cmd_reply(s, idx, max_len); -} - -static void cmd_get_configuration(IDEState *s, uint8_t *buf) -{ - uint32_t len; - uint8_t index = 0; - int max_len; - - /* only feature 0 is supported */ - if (buf[2] != 0 || buf[3] != 0) { - ide_atapi_cmd_error(s, ILLEGAL_REQUEST, - ASC_INV_FIELD_IN_CMD_PACKET); - return; - } - - /* XXX: could result in alignment problems in some architectures */ - max_len = ube16_to_cpu(buf + 7); - - /* - * XXX: avoid overflow for io_buffer if max_len is bigger than - * the size of that buffer (dimensioned to max number of - * sectors to transfer at once) - * - * Only a problem if the feature/profiles grow. - */ - if (max_len > 512) { - /* XXX: assume 1 sector */ - max_len = 512; - } - - memset(buf, 0, max_len); - /* - * the number of sectors from the media tells us which profile - * to use as current. 0 means there is no media - */ - if (media_is_dvd(s)) { - cpu_to_ube16(buf + 6, MMC_PROFILE_DVD_ROM); - } else if (media_is_cd(s)) { - cpu_to_ube16(buf + 6, MMC_PROFILE_CD_ROM); - } - - buf[10] = 0x02 | 0x01; /* persistent and current */ - len = 12; /* headers: 8 + 4 */ - len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_DVD_ROM); - len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_CD_ROM); - cpu_to_ube32(buf, len - 4); /* data length */ - - ide_atapi_cmd_reply(s, len, max_len); -} - -static void cmd_mode_sense(IDEState *s, uint8_t *buf) -{ - int action, code; - int max_len; - - max_len = ube16_to_cpu(buf + 7); - action = buf[2] >> 6; - code = buf[2] & 0x3f; - - switch(action) { - case 0: /* current values */ - switch(code) { - case MODE_PAGE_R_W_ERROR: /* error recovery */ - cpu_to_ube16(&buf[0], 16 - 2); - buf[2] = 0x70; - buf[3] = 0; - buf[4] = 0; - buf[5] = 0; - buf[6] = 0; - buf[7] = 0; - - buf[8] = MODE_PAGE_R_W_ERROR; - buf[9] = 16 - 10; - buf[10] = 0x00; - buf[11] = 0x05; - buf[12] = 0x00; - buf[13] = 0x00; - buf[14] = 0x00; - buf[15] = 0x00; - ide_atapi_cmd_reply(s, 16, max_len); - break; - case MODE_PAGE_AUDIO_CTL: - cpu_to_ube16(&buf[0], 24 - 2); - buf[2] = 0x70; - buf[3] = 0; - buf[4] = 0; - buf[5] = 0; - buf[6] = 0; - buf[7] = 0; - - buf[8] = MODE_PAGE_AUDIO_CTL; - buf[9] = 24 - 10; - /* Fill with CDROM audio volume */ - buf[17] = 0; - buf[19] = 0; - buf[21] = 0; - buf[23] = 0; - - ide_atapi_cmd_reply(s, 24, max_len); - break; - case MODE_PAGE_CAPABILITIES: - cpu_to_ube16(&buf[0], 30 - 2); - buf[2] = 0x70; - buf[3] = 0; - buf[4] = 0; - buf[5] = 0; - buf[6] = 0; - buf[7] = 0; - - buf[8] = MODE_PAGE_CAPABILITIES; - buf[9] = 30 - 10; - buf[10] = 0x3b; /* read CDR/CDRW/DVDROM/DVDR/DVDRAM */ - buf[11] = 0x00; - - /* Claim PLAY_AUDIO capability (0x01) since some Linux - code checks for this to automount media. */ - buf[12] = 0x71; - buf[13] = 3 << 5; - buf[14] = (1 << 0) | (1 << 3) | (1 << 5); - if (s->tray_locked) { - buf[14] |= 1 << 1; - } - buf[15] = 0x00; /* No volume & mute control, no changer */ - cpu_to_ube16(&buf[16], 704); /* 4x read speed */ - buf[18] = 0; /* Two volume levels */ - buf[19] = 2; - cpu_to_ube16(&buf[20], 512); /* 512k buffer */ - cpu_to_ube16(&buf[22], 704); /* 4x read speed current */ - buf[24] = 0; - buf[25] = 0; - buf[26] = 0; - buf[27] = 0; - buf[28] = 0; - buf[29] = 0; - ide_atapi_cmd_reply(s, 30, max_len); - break; - default: - goto error_cmd; - } - break; - case 1: /* changeable values */ - goto error_cmd; - case 2: /* default values */ - goto error_cmd; - default: - case 3: /* saved values */ - ide_atapi_cmd_error(s, ILLEGAL_REQUEST, - ASC_SAVING_PARAMETERS_NOT_SUPPORTED); - break; - } - return; - -error_cmd: - ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET); -} - -static void cmd_test_unit_ready(IDEState *s, uint8_t *buf) -{ - /* Not Ready Conditions are already handled in ide_atapi_cmd(), so if we - * come here, we know that it's ready. */ - ide_atapi_cmd_ok(s); -} - -static void cmd_prevent_allow_medium_removal(IDEState *s, uint8_t* buf) -{ - s->tray_locked = buf[4] & 1; - blk_lock_medium(s->blk, buf[4] & 1); - ide_atapi_cmd_ok(s); -} - -static void cmd_read(IDEState *s, uint8_t* buf) -{ - int nb_sectors, lba; - - if (buf[0] == GPCMD_READ_10) { - nb_sectors = ube16_to_cpu(buf + 7); - } else { - nb_sectors = ube32_to_cpu(buf + 6); - } - - lba = ube32_to_cpu(buf + 2); - if (nb_sectors == 0) { - ide_atapi_cmd_ok(s); - return; - } - - ide_atapi_cmd_read(s, lba, nb_sectors, 2048); -} - -static void cmd_read_cd(IDEState *s, uint8_t* buf) -{ - int nb_sectors, lba, transfer_request; - - nb_sectors = (buf[6] << 16) | (buf[7] << 8) | buf[8]; - lba = ube32_to_cpu(buf + 2); - - if (nb_sectors == 0) { - ide_atapi_cmd_ok(s); - return; - } - - transfer_request = buf[9]; - switch(transfer_request & 0xf8) { - case 0x00: - /* nothing */ - ide_atapi_cmd_ok(s); - break; - case 0x10: - /* normal read */ - ide_atapi_cmd_read(s, lba, nb_sectors, 2048); - break; - case 0xf8: - /* read all data */ - ide_atapi_cmd_read(s, lba, nb_sectors, 2352); - break; - default: - ide_atapi_cmd_error(s, ILLEGAL_REQUEST, - ASC_INV_FIELD_IN_CMD_PACKET); - break; - } -} - -static void cmd_seek(IDEState *s, uint8_t* buf) -{ - unsigned int lba; - uint64_t total_sectors = s->nb_sectors >> 2; - - lba = ube32_to_cpu(buf + 2); - if (lba >= total_sectors) { - ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR); - return; - } - - ide_atapi_cmd_ok(s); -} - -static void cmd_start_stop_unit(IDEState *s, uint8_t* buf) -{ - int sense; - bool start = buf[4] & 1; - bool loej = buf[4] & 2; /* load on start, eject on !start */ - int pwrcnd = buf[4] & 0xf0; - - if (pwrcnd) { - /* eject/load only happens for power condition == 0 */ - ide_atapi_cmd_ok(s); - return; - } - - if (loej) { - if (!start && !s->tray_open && s->tray_locked) { - sense = blk_is_inserted(s->blk) - ? NOT_READY : ILLEGAL_REQUEST; - ide_atapi_cmd_error(s, sense, ASC_MEDIA_REMOVAL_PREVENTED); - return; - } - - if (s->tray_open != !start) { - blk_eject(s->blk, !start); - s->tray_open = !start; - } - } - - ide_atapi_cmd_ok(s); -} - -static void cmd_mechanism_status(IDEState *s, uint8_t* buf) -{ - int max_len = ube16_to_cpu(buf + 8); - - cpu_to_ube16(buf, 0); - /* no current LBA */ - buf[2] = 0; - buf[3] = 0; - buf[4] = 0; - buf[5] = 1; - cpu_to_ube16(buf + 6, 0); - ide_atapi_cmd_reply(s, 8, max_len); -} - -static void cmd_read_toc_pma_atip(IDEState *s, uint8_t* buf) -{ - int format, msf, start_track, len; - int max_len; - uint64_t total_sectors = s->nb_sectors >> 2; - - max_len = ube16_to_cpu(buf + 7); - format = buf[9] >> 6; - msf = (buf[1] >> 1) & 1; - start_track = buf[6]; - - switch(format) { - case 0: - len = cdrom_read_toc(total_sectors, buf, msf, start_track); - if (len < 0) - goto error_cmd; - ide_atapi_cmd_reply(s, len, max_len); - break; - case 1: - /* multi session : only a single session defined */ - memset(buf, 0, 12); - buf[1] = 0x0a; - buf[2] = 0x01; - buf[3] = 0x01; - ide_atapi_cmd_reply(s, 12, max_len); - break; - case 2: - len = cdrom_read_toc_raw(total_sectors, buf, msf, start_track); - if (len < 0) - goto error_cmd; - ide_atapi_cmd_reply(s, len, max_len); - break; - default: - error_cmd: - ide_atapi_cmd_error(s, ILLEGAL_REQUEST, - ASC_INV_FIELD_IN_CMD_PACKET); - } -} - -static void cmd_read_cdvd_capacity(IDEState *s, uint8_t* buf) -{ - uint64_t total_sectors = s->nb_sectors >> 2; - - /* NOTE: it is really the number of sectors minus 1 */ - cpu_to_ube32(buf, total_sectors - 1); - cpu_to_ube32(buf + 4, 2048); - ide_atapi_cmd_reply(s, 8, 8); -} - -static void cmd_read_disc_information(IDEState *s, uint8_t* buf) -{ - uint8_t type = buf[1] & 7; - uint32_t max_len = ube16_to_cpu(buf + 7); - - /* Types 1/2 are only defined for Blu-Ray. */ - if (type != 0) { - ide_atapi_cmd_error(s, ILLEGAL_REQUEST, - ASC_INV_FIELD_IN_CMD_PACKET); - return; - } - - memset(buf, 0, 34); - buf[1] = 32; - buf[2] = 0xe; /* last session complete, disc finalized */ - buf[3] = 1; /* first track on disc */ - buf[4] = 1; /* # of sessions */ - buf[5] = 1; /* first track of last session */ - buf[6] = 1; /* last track of last session */ - buf[7] = 0x20; /* unrestricted use */ - buf[8] = 0x00; /* CD-ROM or DVD-ROM */ - /* 9-10-11: most significant byte corresponding bytes 4-5-6 */ - /* 12-23: not meaningful for CD-ROM or DVD-ROM */ - /* 24-31: disc bar code */ - /* 32: disc application code */ - /* 33: number of OPC tables */ - - ide_atapi_cmd_reply(s, 34, max_len); -} - -static void cmd_read_dvd_structure(IDEState *s, uint8_t* buf) -{ - int max_len; - int media = buf[1]; - int format = buf[7]; - int ret; - - max_len = ube16_to_cpu(buf + 8); - - if (format < 0xff) { - if (media_is_cd(s)) { - ide_atapi_cmd_error(s, ILLEGAL_REQUEST, - ASC_INCOMPATIBLE_FORMAT); - return; - } else if (!media_present(s)) { - ide_atapi_cmd_error(s, ILLEGAL_REQUEST, - ASC_INV_FIELD_IN_CMD_PACKET); - return; - } - } - - memset(buf, 0, max_len > IDE_DMA_BUF_SECTORS * 512 + 4 ? - IDE_DMA_BUF_SECTORS * 512 + 4 : max_len); - - switch (format) { - case 0x00 ... 0x7f: - case 0xff: - if (media == 0) { - ret = ide_dvd_read_structure(s, format, buf, buf); - - if (ret < 0) { - ide_atapi_cmd_error(s, ILLEGAL_REQUEST, -ret); - } else { - ide_atapi_cmd_reply(s, ret, max_len); - } - - break; - } - /* TODO: BD support, fall through for now */ - - /* Generic disk structures */ - case 0x80: /* TODO: AACS volume identifier */ - case 0x81: /* TODO: AACS media serial number */ - case 0x82: /* TODO: AACS media identifier */ - case 0x83: /* TODO: AACS media key block */ - case 0x90: /* TODO: List of recognized format layers */ - case 0xc0: /* TODO: Write protection status */ - default: - ide_atapi_cmd_error(s, ILLEGAL_REQUEST, - ASC_INV_FIELD_IN_CMD_PACKET); - break; - } -} - -static void cmd_set_speed(IDEState *s, uint8_t* buf) -{ - ide_atapi_cmd_ok(s); -} - -enum { - /* - * Only commands flagged as ALLOW_UA are allowed to run under a - * unit attention condition. (See MMC-5, section 4.1.6.1) - */ - ALLOW_UA = 0x01, - - /* - * Commands flagged with CHECK_READY can only execute if a medium is present. - * Otherwise they report the Not Ready Condition. (See MMC-5, section - * 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 AtapiCmd { - void (*handler)(IDEState *s, uint8_t *buf); - int flags; -} atapi_cmd_table[0x100] = { - [ 0x00 ] = { cmd_test_unit_ready, CHECK_READY | NONDATA }, - [ 0x03 ] = { cmd_request_sense, ALLOW_UA }, - [ 0x12 ] = { cmd_inquiry, ALLOW_UA }, - [ 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 | NONDATA }, - [ 0x43 ] = { cmd_read_toc_pma_atip, CHECK_READY }, - [ 0x46 ] = { cmd_get_configuration, ALLOW_UA }, - [ 0x4a ] = { cmd_get_event_status_notification, ALLOW_UA }, - [ 0x51 ] = { cmd_read_disc_information, CHECK_READY }, - [ 0x5a ] = { cmd_mode_sense, /* (10) */ 0 }, - [ 0xa8 ] = { cmd_read, /* (12) */ CHECK_READY }, - [ 0xad ] = { cmd_read_dvd_structure, CHECK_READY }, - [ 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 */ -}; - -void ide_atapi_cmd(IDEState *s) -{ - uint8_t *buf = s->io_buffer; - const struct AtapiCmd *cmd = &atapi_cmd_table[s->io_buffer[0]]; - -#ifdef DEBUG_IDE_ATAPI - { - int i; - printf("ATAPI limit=0x%x packet:", s->lcyl | (s->hcyl << 8)); - for(i = 0; i < ATAPI_PACKET_SIZE; i++) { - printf(" %02x", buf[i]); - } - 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 && !(cmd->flags & ALLOW_UA)) { - ide_atapi_cmd_check_status(s); - return; - } - /* - * When a CD gets changed, we have to report an ejected state and - * then a loaded state to guests so that they detect tray - * open/close and media change events. Guests that do not use - * GET_EVENT_STATUS_NOTIFICATION to detect such tray open/close - * states rely on this behavior. - */ - if (!(cmd->flags & ALLOW_UA) && - !s->tray_open && blk_is_inserted(s->blk) && s->cdrom_changed) { - - if (s->cdrom_changed == 1) { - ide_atapi_cmd_error(s, NOT_READY, ASC_MEDIUM_NOT_PRESENT); - s->cdrom_changed = 2; - } else { - ide_atapi_cmd_error(s, UNIT_ATTENTION, ASC_MEDIUM_MAY_HAVE_CHANGED); - s->cdrom_changed = 0; - } - - return; - } - - /* Report a Not Ready condition if appropriate for the command */ - 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 (cmd->handler) { - cmd->handler(s, buf); - return; - } - - ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_ILLEGAL_OPCODE); -} diff --git a/qemu/hw/ide/cmd646.c b/qemu/hw/ide/cmd646.c deleted file mode 100644 index 49294a531..000000000 --- a/qemu/hw/ide/cmd646.c +++ /dev/null @@ -1,435 +0,0 @@ -/* - * QEMU IDE Emulation: PCI cmd646 support. - * - * Copyright (c) 2003 Fabrice Bellard - * Copyright (c) 2006 Openedhand Ltd. - * - * 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 -#include -#include -#include -#include "sysemu/block-backend.h" -#include "sysemu/sysemu.h" -#include "sysemu/dma.h" - -#include - -/* CMD646 specific */ -#define CFR 0x50 -#define CFR_INTR_CH0 0x04 -#define CNTRL 0x51 -#define CNTRL_EN_CH0 0x04 -#define CNTRL_EN_CH1 0x08 -#define ARTTIM23 0x57 -#define ARTTIM23_INTR_CH1 0x10 -#define MRDMODE 0x71 -#define MRDMODE_INTR_CH0 0x04 -#define MRDMODE_INTR_CH1 0x08 -#define MRDMODE_BLK_CH0 0x10 -#define MRDMODE_BLK_CH1 0x20 -#define UDIDETCR0 0x73 -#define UDIDETCR1 0x7B - -static void cmd646_update_irq(PCIDevice *pd); - -static uint64_t cmd646_cmd_read(void *opaque, hwaddr addr, - unsigned size) -{ - CMD646BAR *cmd646bar = opaque; - - if (addr != 2 || size != 1) { - return ((uint64_t)1 << (size * 8)) - 1; - } - return ide_status_read(cmd646bar->bus, addr + 2); -} - -static void cmd646_cmd_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - CMD646BAR *cmd646bar = opaque; - - if (addr != 2 || size != 1) { - return; - } - ide_cmd_write(cmd646bar->bus, addr + 2, data); -} - -static const MemoryRegionOps cmd646_cmd_ops = { - .read = cmd646_cmd_read, - .write = cmd646_cmd_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static uint64_t cmd646_data_read(void *opaque, hwaddr addr, - unsigned size) -{ - CMD646BAR *cmd646bar = opaque; - - if (size == 1) { - return ide_ioport_read(cmd646bar->bus, addr); - } else if (addr == 0) { - if (size == 2) { - return ide_data_readw(cmd646bar->bus, addr); - } else { - return ide_data_readl(cmd646bar->bus, addr); - } - } - return ((uint64_t)1 << (size * 8)) - 1; -} - -static void cmd646_data_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - CMD646BAR *cmd646bar = opaque; - - if (size == 1) { - ide_ioport_write(cmd646bar->bus, addr, data); - } else if (addr == 0) { - if (size == 2) { - ide_data_writew(cmd646bar->bus, addr, data); - } else { - ide_data_writel(cmd646bar->bus, addr, data); - } - } -} - -static const MemoryRegionOps cmd646_data_ops = { - .read = cmd646_data_read, - .write = cmd646_data_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void setup_cmd646_bar(PCIIDEState *d, int bus_num) -{ - IDEBus *bus = &d->bus[bus_num]; - CMD646BAR *bar = &d->cmd646_bar[bus_num]; - - bar->bus = bus; - bar->pci_dev = d; - memory_region_init_io(&bar->cmd, OBJECT(d), &cmd646_cmd_ops, bar, - "cmd646-cmd", 4); - memory_region_init_io(&bar->data, OBJECT(d), &cmd646_data_ops, bar, - "cmd646-data", 8); -} - -static void cmd646_update_dma_interrupts(PCIDevice *pd) -{ - /* Sync DMA interrupt status from UDMA interrupt status */ - if (pd->config[MRDMODE] & MRDMODE_INTR_CH0) { - pd->config[CFR] |= CFR_INTR_CH0; - } else { - pd->config[CFR] &= ~CFR_INTR_CH0; - } - - if (pd->config[MRDMODE] & MRDMODE_INTR_CH1) { - pd->config[ARTTIM23] |= ARTTIM23_INTR_CH1; - } else { - pd->config[ARTTIM23] &= ~ARTTIM23_INTR_CH1; - } -} - -static void cmd646_update_udma_interrupts(PCIDevice *pd) -{ - /* Sync UDMA interrupt status from DMA interrupt status */ - if (pd->config[CFR] & CFR_INTR_CH0) { - pd->config[MRDMODE] |= MRDMODE_INTR_CH0; - } else { - pd->config[MRDMODE] &= ~MRDMODE_INTR_CH0; - } - - if (pd->config[ARTTIM23] & ARTTIM23_INTR_CH1) { - pd->config[MRDMODE] |= MRDMODE_INTR_CH1; - } else { - pd->config[MRDMODE] &= ~MRDMODE_INTR_CH1; - } -} - -static uint64_t bmdma_read(void *opaque, hwaddr addr, - unsigned size) -{ - BMDMAState *bm = opaque; - PCIDevice *pci_dev = PCI_DEVICE(bm->pci_dev); - uint32_t val; - - if (size != 1) { - return ((uint64_t)1 << (size * 8)) - 1; - } - - switch(addr & 3) { - case 0: - val = bm->cmd; - break; - case 1: - val = pci_dev->config[MRDMODE]; - break; - case 2: - val = bm->status; - break; - case 3: - if (bm == &bm->pci_dev->bmdma[0]) { - val = pci_dev->config[UDIDETCR0]; - } else { - val = pci_dev->config[UDIDETCR1]; - } - break; - default: - val = 0xff; - break; - } -#ifdef DEBUG_IDE - printf("bmdma: readb " TARGET_FMT_plx " : 0x%02x\n", addr, val); -#endif - return val; -} - -static void bmdma_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - BMDMAState *bm = opaque; - PCIDevice *pci_dev = PCI_DEVICE(bm->pci_dev); - - if (size != 1) { - return; - } - -#ifdef DEBUG_IDE - printf("bmdma: writeb " TARGET_FMT_plx " : 0x%" PRIx64 "\n", addr, val); -#endif - switch(addr & 3) { - case 0: - bmdma_cmd_writeb(bm, val); - break; - case 1: - pci_dev->config[MRDMODE] = - (pci_dev->config[MRDMODE] & ~0x30) | (val & 0x30); - cmd646_update_dma_interrupts(pci_dev); - cmd646_update_irq(pci_dev); - break; - case 2: - bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06); - break; - case 3: - if (bm == &bm->pci_dev->bmdma[0]) { - pci_dev->config[UDIDETCR0] = val; - } else { - pci_dev->config[UDIDETCR1] = val; - } - break; - } -} - -static const MemoryRegionOps cmd646_bmdma_ops = { - .read = bmdma_read, - .write = bmdma_write, -}; - -static void bmdma_setup_bar(PCIIDEState *d) -{ - BMDMAState *bm; - int i; - - memory_region_init(&d->bmdma_bar, OBJECT(d), "cmd646-bmdma", 16); - for(i = 0;i < 2; i++) { - bm = &d->bmdma[i]; - memory_region_init_io(&bm->extra_io, OBJECT(d), &cmd646_bmdma_ops, bm, - "cmd646-bmdma-bus", 4); - memory_region_add_subregion(&d->bmdma_bar, i * 8, &bm->extra_io); - memory_region_init_io(&bm->addr_ioport, OBJECT(d), - &bmdma_addr_ioport_ops, bm, - "cmd646-bmdma-ioport", 4); - memory_region_add_subregion(&d->bmdma_bar, i * 8 + 4, &bm->addr_ioport); - } -} - -static void cmd646_update_irq(PCIDevice *pd) -{ - int pci_level; - - pci_level = ((pd->config[MRDMODE] & MRDMODE_INTR_CH0) && - !(pd->config[MRDMODE] & MRDMODE_BLK_CH0)) || - ((pd->config[MRDMODE] & MRDMODE_INTR_CH1) && - !(pd->config[MRDMODE] & MRDMODE_BLK_CH1)); - pci_set_irq(pd, pci_level); -} - -/* the PCI irq level is the logical OR of the two channels */ -static void cmd646_set_irq(void *opaque, int channel, int level) -{ - PCIIDEState *d = opaque; - PCIDevice *pd = PCI_DEVICE(d); - int irq_mask; - - irq_mask = MRDMODE_INTR_CH0 << channel; - if (level) { - pd->config[MRDMODE] |= irq_mask; - } else { - pd->config[MRDMODE] &= ~irq_mask; - } - cmd646_update_dma_interrupts(pd); - cmd646_update_irq(pd); -} - -static void cmd646_reset(void *opaque) -{ - PCIIDEState *d = opaque; - unsigned int i; - - for (i = 0; i < 2; i++) { - ide_bus_reset(&d->bus[i]); - } -} - -static uint32_t cmd646_pci_config_read(PCIDevice *d, - uint32_t address, int len) -{ - return pci_default_read_config(d, address, len); -} - -static void cmd646_pci_config_write(PCIDevice *d, uint32_t addr, uint32_t val, - int l) -{ - uint32_t i; - - pci_default_write_config(d, addr, val, l); - - for (i = addr; i < addr + l; i++) { - switch (i) { - case CFR: - case ARTTIM23: - cmd646_update_udma_interrupts(d); - break; - case MRDMODE: - cmd646_update_dma_interrupts(d); - break; - } - } - - cmd646_update_irq(d); -} - -/* CMD646 PCI IDE controller */ -static void pci_cmd646_ide_realize(PCIDevice *dev, Error **errp) -{ - PCIIDEState *d = PCI_IDE(dev); - uint8_t *pci_conf = dev->config; - qemu_irq *irq; - int i; - - pci_conf[PCI_CLASS_PROG] = 0x8f; - - pci_conf[CNTRL] = CNTRL_EN_CH0; // enable IDE0 - if (d->secondary) { - /* XXX: if not enabled, really disable the seconday IDE controller */ - pci_conf[CNTRL] |= CNTRL_EN_CH1; /* enable IDE1 */ - } - - /* Set write-to-clear interrupt bits */ - dev->wmask[CFR] = 0x0; - dev->w1cmask[CFR] = CFR_INTR_CH0; - dev->wmask[ARTTIM23] = 0x0; - dev->w1cmask[ARTTIM23] = ARTTIM23_INTR_CH1; - dev->wmask[MRDMODE] = 0x0; - dev->w1cmask[MRDMODE] = MRDMODE_INTR_CH0 | MRDMODE_INTR_CH1; - - setup_cmd646_bar(d, 0); - setup_cmd646_bar(d, 1); - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[0].data); - pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[0].cmd); - pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[1].data); - pci_register_bar(dev, 3, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[1].cmd); - bmdma_setup_bar(d); - pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar); - - /* TODO: RST# value should be 0 */ - pci_conf[PCI_INTERRUPT_PIN] = 0x01; // interrupt on pin 1 - - irq = qemu_allocate_irqs(cmd646_set_irq, d, 2); - for (i = 0; i < 2; i++) { - ide_bus_new(&d->bus[i], sizeof(d->bus[i]), DEVICE(dev), i, 2); - ide_init2(&d->bus[i], irq[i]); - - bmdma_init(&d->bus[i], &d->bmdma[i], d); - d->bmdma[i].bus = &d->bus[i]; - ide_register_restart_cb(&d->bus[i]); - } - - vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d); - qemu_register_reset(cmd646_reset, d); -} - -static void pci_cmd646_ide_exitfn(PCIDevice *dev) -{ - PCIIDEState *d = PCI_IDE(dev); - unsigned i; - - for (i = 0; i < 2; ++i) { - memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].extra_io); - memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].addr_ioport); - } -} - -void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table, - int secondary_ide_enabled) -{ - PCIDevice *dev; - - dev = pci_create(bus, -1, "cmd646-ide"); - qdev_prop_set_uint32(&dev->qdev, "secondary", secondary_ide_enabled); - qdev_init_nofail(&dev->qdev); - - pci_ide_create_devs(dev, hd_table); -} - -static Property cmd646_ide_properties[] = { - DEFINE_PROP_UINT32("secondary", PCIIDEState, secondary, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void cmd646_ide_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = pci_cmd646_ide_realize; - k->exit = pci_cmd646_ide_exitfn; - k->vendor_id = PCI_VENDOR_ID_CMD; - k->device_id = PCI_DEVICE_ID_CMD_646; - k->revision = 0x07; - k->class_id = PCI_CLASS_STORAGE_IDE; - 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 = { - .name = "cmd646-ide", - .parent = TYPE_PCI_IDE, - .class_init = cmd646_ide_class_init, -}; - -static void cmd646_ide_register_types(void) -{ - type_register_static(&cmd646_ide_info); -} - -type_init(cmd646_ide_register_types) diff --git a/qemu/hw/ide/core.c b/qemu/hw/ide/core.c deleted file mode 100644 index 41e6a2dc4..000000000 --- a/qemu/hw/ide/core.c +++ /dev/null @@ -1,2842 +0,0 @@ -/* - * QEMU IDE disk and CD/DVD-ROM Emulator - * - * Copyright (c) 2003 Fabrice Bellard - * Copyright (c) 2006 Openedhand Ltd. - * - * 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 -#include -#include -#include -#include "qemu/error-report.h" -#include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "sysemu/dma.h" -#include "hw/block/block.h" -#include "sysemu/block-backend.h" -#include "qemu/cutils.h" - -#include - -/* These values were based on a Seagate ST3500418AS but have been modified - to make more sense in QEMU */ -static const int smart_attributes[][12] = { - /* id, flags, hflags, val, wrst, raw (6 bytes), threshold */ - /* raw read error rate*/ - { 0x01, 0x03, 0x00, 0x64, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, - /* spin up */ - { 0x03, 0x03, 0x00, 0x64, 0x64, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - /* start stop count */ - { 0x04, 0x02, 0x00, 0x64, 0x64, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14}, - /* remapped sectors */ - { 0x05, 0x03, 0x00, 0x64, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24}, - /* power on hours */ - { 0x09, 0x03, 0x00, 0x64, 0x64, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - /* power cycle count */ - { 0x0c, 0x03, 0x00, 0x64, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - /* airflow-temperature-celsius */ - { 190, 0x03, 0x00, 0x45, 0x45, 0x1f, 0x00, 0x1f, 0x1f, 0x00, 0x00, 0x32}, -}; - -static void ide_dummy_transfer_stop(IDEState *s); - -static void padstr(char *str, const char *src, int len) -{ - int i, v; - for(i = 0; i < len; i++) { - if (*src) - v = *src++; - else - v = ' '; - str[i^1] = v; - } -} - -static void put_le16(uint16_t *p, unsigned int v) -{ - *p = cpu_to_le16(v); -} - -static void ide_identify_size(IDEState *s) -{ - uint16_t *p = (uint16_t *)s->identify_data; - put_le16(p + 60, s->nb_sectors); - put_le16(p + 61, s->nb_sectors >> 16); - put_le16(p + 100, s->nb_sectors); - put_le16(p + 101, s->nb_sectors >> 16); - put_le16(p + 102, s->nb_sectors >> 32); - put_le16(p + 103, s->nb_sectors >> 48); -} - -static void ide_identify(IDEState *s) -{ - uint16_t *p; - unsigned int oldsize; - IDEDevice *dev = s->unit ? s->bus->slave : s->bus->master; - - p = (uint16_t *)s->identify_data; - if (s->identify_set) { - goto fill_buffer; - } - memset(p, 0, sizeof(s->identify_data)); - - put_le16(p + 0, 0x0040); - put_le16(p + 1, s->cylinders); - put_le16(p + 3, s->heads); - put_le16(p + 4, 512 * s->sectors); /* XXX: retired, remove ? */ - put_le16(p + 5, 512); /* XXX: retired, remove ? */ - put_le16(p + 6, s->sectors); - padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */ - put_le16(p + 20, 3); /* XXX: retired, remove ? */ - put_le16(p + 21, 512); /* cache size in sectors */ - put_le16(p + 22, 4); /* ecc bytes */ - padstr((char *)(p + 23), s->version, 8); /* firmware version */ - padstr((char *)(p + 27), s->drive_model_str, 40); /* model */ -#if MAX_MULT_SECTORS > 1 - put_le16(p + 47, 0x8000 | MAX_MULT_SECTORS); -#endif - put_le16(p + 48, 1); /* dword I/O */ - put_le16(p + 49, (1 << 11) | (1 << 9) | (1 << 8)); /* DMA and LBA supported */ - put_le16(p + 51, 0x200); /* PIO transfer cycle */ - put_le16(p + 52, 0x200); /* DMA transfer cycle */ - put_le16(p + 53, 1 | (1 << 1) | (1 << 2)); /* words 54-58,64-70,88 are valid */ - put_le16(p + 54, s->cylinders); - put_le16(p + 55, s->heads); - put_le16(p + 56, s->sectors); - oldsize = s->cylinders * s->heads * s->sectors; - put_le16(p + 57, oldsize); - put_le16(p + 58, oldsize >> 16); - if (s->mult_sectors) - put_le16(p + 59, 0x100 | s->mult_sectors); - /* *(p + 60) := nb_sectors -- see ide_identify_size */ - /* *(p + 61) := nb_sectors >> 16 -- see ide_identify_size */ - put_le16(p + 62, 0x07); /* single word dma0-2 supported */ - put_le16(p + 63, 0x07); /* mdma0-2 supported */ - put_le16(p + 64, 0x03); /* pio3-4 supported */ - put_le16(p + 65, 120); - put_le16(p + 66, 120); - put_le16(p + 67, 120); - put_le16(p + 68, 120); - if (dev && dev->conf.discard_granularity) { - put_le16(p + 69, (1 << 14)); /* determinate TRIM behavior */ - } - - if (s->ncq_queues) { - put_le16(p + 75, s->ncq_queues - 1); - /* NCQ supported */ - put_le16(p + 76, (1 << 8)); - } - - put_le16(p + 80, 0xf0); /* ata3 -> ata6 supported */ - put_le16(p + 81, 0x16); /* conforms to ata5 */ - /* 14=NOP supported, 5=WCACHE supported, 0=SMART supported */ - put_le16(p + 82, (1 << 14) | (1 << 5) | 1); - /* 13=flush_cache_ext,12=flush_cache,10=lba48 */ - put_le16(p + 83, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10)); - /* 14=set to 1, 8=has WWN, 1=SMART self test, 0=SMART error logging */ - if (s->wwn) { - put_le16(p + 84, (1 << 14) | (1 << 8) | 0); - } else { - put_le16(p + 84, (1 << 14) | 0); - } - /* 14 = NOP supported, 5=WCACHE enabled, 0=SMART feature set enabled */ - if (blk_enable_write_cache(s->blk)) { - put_le16(p + 85, (1 << 14) | (1 << 5) | 1); - } else { - put_le16(p + 85, (1 << 14) | 1); - } - /* 13=flush_cache_ext,12=flush_cache,10=lba48 */ - put_le16(p + 86, (1 << 13) | (1 <<12) | (1 << 10)); - /* 14=set to 1, 8=has WWN, 1=SMART self test, 0=SMART error logging */ - if (s->wwn) { - put_le16(p + 87, (1 << 14) | (1 << 8) | 0); - } else { - put_le16(p + 87, (1 << 14) | 0); - } - put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */ - put_le16(p + 93, 1 | (1 << 14) | 0x2000); - /* *(p + 100) := nb_sectors -- see ide_identify_size */ - /* *(p + 101) := nb_sectors >> 16 -- see ide_identify_size */ - /* *(p + 102) := nb_sectors >> 32 -- see ide_identify_size */ - /* *(p + 103) := nb_sectors >> 48 -- see ide_identify_size */ - - if (dev && dev->conf.physical_block_size) - put_le16(p + 106, 0x6000 | get_physical_block_exp(&dev->conf)); - if (s->wwn) { - /* LE 16-bit words 111-108 contain 64-bit World Wide Name */ - put_le16(p + 108, s->wwn >> 48); - put_le16(p + 109, s->wwn >> 32); - put_le16(p + 110, s->wwn >> 16); - put_le16(p + 111, s->wwn); - } - if (dev && dev->conf.discard_granularity) { - put_le16(p + 169, 1); /* TRIM support */ - } - - ide_identify_size(s); - s->identify_set = 1; - -fill_buffer: - memcpy(s->io_buffer, p, sizeof(s->identify_data)); -} - -static void ide_atapi_identify(IDEState *s) -{ - uint16_t *p; - - p = (uint16_t *)s->identify_data; - if (s->identify_set) { - goto fill_buffer; - } - memset(p, 0, sizeof(s->identify_data)); - - /* Removable CDROM, 50us response, 12 byte packets */ - put_le16(p + 0, (2 << 14) | (5 << 8) | (1 << 7) | (2 << 5) | (0 << 0)); - padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */ - put_le16(p + 20, 3); /* buffer type */ - put_le16(p + 21, 512); /* cache size in sectors */ - put_le16(p + 22, 4); /* ecc bytes */ - padstr((char *)(p + 23), s->version, 8); /* firmware version */ - padstr((char *)(p + 27), s->drive_model_str, 40); /* model */ - put_le16(p + 48, 1); /* dword I/O (XXX: should not be set on CDROM) */ -#ifdef USE_DMA_CDROM - put_le16(p + 49, 1 << 9 | 1 << 8); /* DMA and LBA supported */ - put_le16(p + 53, 7); /* words 64-70, 54-58, 88 valid */ - put_le16(p + 62, 7); /* single word dma0-2 supported */ - put_le16(p + 63, 7); /* mdma0-2 supported */ -#else - put_le16(p + 49, 1 << 9); /* LBA supported, no DMA */ - put_le16(p + 53, 3); /* words 64-70, 54-58 valid */ - put_le16(p + 63, 0x103); /* DMA modes XXX: may be incorrect */ -#endif - put_le16(p + 64, 3); /* pio3-4 supported */ - put_le16(p + 65, 0xb4); /* minimum DMA multiword tx cycle time */ - put_le16(p + 66, 0xb4); /* recommended DMA multiword tx cycle time */ - put_le16(p + 67, 0x12c); /* minimum PIO cycle time without flow control */ - put_le16(p + 68, 0xb4); /* minimum PIO cycle time with IORDY flow control */ - - put_le16(p + 71, 30); /* in ns */ - put_le16(p + 72, 30); /* in ns */ - - if (s->ncq_queues) { - put_le16(p + 75, s->ncq_queues - 1); - /* NCQ supported */ - put_le16(p + 76, (1 << 8)); - } - - put_le16(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */ - if (s->wwn) { - put_le16(p + 84, (1 << 8)); /* supports WWN for words 108-111 */ - put_le16(p + 87, (1 << 8)); /* WWN enabled */ - } - -#ifdef USE_DMA_CDROM - put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */ -#endif - - if (s->wwn) { - /* LE 16-bit words 111-108 contain 64-bit World Wide Name */ - put_le16(p + 108, s->wwn >> 48); - put_le16(p + 109, s->wwn >> 32); - put_le16(p + 110, s->wwn >> 16); - put_le16(p + 111, s->wwn); - } - - s->identify_set = 1; - -fill_buffer: - memcpy(s->io_buffer, p, sizeof(s->identify_data)); -} - -static void ide_cfata_identify_size(IDEState *s) -{ - uint16_t *p = (uint16_t *)s->identify_data; - put_le16(p + 7, s->nb_sectors >> 16); /* Sectors per card */ - put_le16(p + 8, s->nb_sectors); /* Sectors per card */ - put_le16(p + 60, s->nb_sectors); /* Total LBA sectors */ - put_le16(p + 61, s->nb_sectors >> 16); /* Total LBA sectors */ -} - -static void ide_cfata_identify(IDEState *s) -{ - uint16_t *p; - uint32_t cur_sec; - - p = (uint16_t *)s->identify_data; - if (s->identify_set) { - goto fill_buffer; - } - memset(p, 0, sizeof(s->identify_data)); - - cur_sec = s->cylinders * s->heads * s->sectors; - - put_le16(p + 0, 0x848a); /* CF Storage Card signature */ - put_le16(p + 1, s->cylinders); /* Default cylinders */ - put_le16(p + 3, s->heads); /* Default heads */ - put_le16(p + 6, s->sectors); /* Default sectors per track */ - /* *(p + 7) := nb_sectors >> 16 -- see ide_cfata_identify_size */ - /* *(p + 8) := nb_sectors -- see ide_cfata_identify_size */ - padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */ - put_le16(p + 22, 0x0004); /* ECC bytes */ - padstr((char *) (p + 23), s->version, 8); /* Firmware Revision */ - padstr((char *) (p + 27), s->drive_model_str, 40);/* Model number */ -#if MAX_MULT_SECTORS > 1 - put_le16(p + 47, 0x8000 | MAX_MULT_SECTORS); -#else - put_le16(p + 47, 0x0000); -#endif - put_le16(p + 49, 0x0f00); /* Capabilities */ - put_le16(p + 51, 0x0002); /* PIO cycle timing mode */ - put_le16(p + 52, 0x0001); /* DMA cycle timing mode */ - put_le16(p + 53, 0x0003); /* Translation params valid */ - put_le16(p + 54, s->cylinders); /* Current cylinders */ - put_le16(p + 55, s->heads); /* Current heads */ - put_le16(p + 56, s->sectors); /* Current sectors */ - put_le16(p + 57, cur_sec); /* Current capacity */ - put_le16(p + 58, cur_sec >> 16); /* Current capacity */ - if (s->mult_sectors) /* Multiple sector setting */ - put_le16(p + 59, 0x100 | s->mult_sectors); - /* *(p + 60) := nb_sectors -- see ide_cfata_identify_size */ - /* *(p + 61) := nb_sectors >> 16 -- see ide_cfata_identify_size */ - put_le16(p + 63, 0x0203); /* Multiword DMA capability */ - put_le16(p + 64, 0x0001); /* Flow Control PIO support */ - put_le16(p + 65, 0x0096); /* Min. Multiword DMA cycle */ - put_le16(p + 66, 0x0096); /* Rec. Multiword DMA cycle */ - put_le16(p + 68, 0x00b4); /* Min. PIO cycle time */ - put_le16(p + 82, 0x400c); /* Command Set supported */ - put_le16(p + 83, 0x7068); /* Command Set supported */ - put_le16(p + 84, 0x4000); /* Features supported */ - put_le16(p + 85, 0x000c); /* Command Set enabled */ - put_le16(p + 86, 0x7044); /* Command Set enabled */ - put_le16(p + 87, 0x4000); /* Features enabled */ - put_le16(p + 91, 0x4060); /* Current APM level */ - put_le16(p + 129, 0x0002); /* Current features option */ - put_le16(p + 130, 0x0005); /* Reassigned sectors */ - put_le16(p + 131, 0x0001); /* Initial power mode */ - put_le16(p + 132, 0x0000); /* User signature */ - put_le16(p + 160, 0x8100); /* Power requirement */ - put_le16(p + 161, 0x8001); /* CF command set */ - - ide_cfata_identify_size(s); - s->identify_set = 1; - -fill_buffer: - memcpy(s->io_buffer, p, sizeof(s->identify_data)); -} - -static void ide_set_signature(IDEState *s) -{ - s->select &= 0xf0; /* clear head */ - /* put signature */ - s->nsector = 1; - s->sector = 1; - if (s->drive_kind == IDE_CD) { - s->lcyl = 0x14; - s->hcyl = 0xeb; - } else if (s->blk) { - s->lcyl = 0; - s->hcyl = 0; - } else { - s->lcyl = 0xff; - s->hcyl = 0xff; - } -} - -typedef struct TrimAIOCB { - BlockAIOCB common; - BlockBackend *blk; - QEMUBH *bh; - int ret; - QEMUIOVector *qiov; - BlockAIOCB *aiocb; - int i, j; -} TrimAIOCB; - -static void trim_aio_cancel(BlockAIOCB *acb) -{ - TrimAIOCB *iocb = container_of(acb, TrimAIOCB, common); - - /* Exit the loop so ide_issue_trim_cb will not continue */ - iocb->j = iocb->qiov->niov - 1; - iocb->i = (iocb->qiov->iov[iocb->j].iov_len / 8) - 1; - - iocb->ret = -ECANCELED; - - if (iocb->aiocb) { - blk_aio_cancel_async(iocb->aiocb); - iocb->aiocb = NULL; - } -} - -static const AIOCBInfo trim_aiocb_info = { - .aiocb_size = sizeof(TrimAIOCB), - .cancel_async = trim_aio_cancel, -}; - -static void ide_trim_bh_cb(void *opaque) -{ - TrimAIOCB *iocb = opaque; - - iocb->common.cb(iocb->common.opaque, iocb->ret); - - qemu_bh_delete(iocb->bh); - iocb->bh = NULL; - qemu_aio_unref(iocb); -} - -static void ide_issue_trim_cb(void *opaque, int ret) -{ - TrimAIOCB *iocb = opaque; - if (ret >= 0) { - while (iocb->j < iocb->qiov->niov) { - int j = iocb->j; - while (++iocb->i < iocb->qiov->iov[j].iov_len / 8) { - int i = iocb->i; - uint64_t *buffer = iocb->qiov->iov[j].iov_base; - - /* 6-byte LBA + 2-byte range per entry */ - uint64_t entry = le64_to_cpu(buffer[i]); - uint64_t sector = entry & 0x0000ffffffffffffULL; - uint16_t count = entry >> 48; - - if (count == 0) { - continue; - } - - /* Got an entry! Submit and exit. */ - iocb->aiocb = blk_aio_discard(iocb->blk, sector, count, - ide_issue_trim_cb, opaque); - return; - } - - iocb->j++; - iocb->i = -1; - } - } else { - iocb->ret = ret; - } - - iocb->aiocb = NULL; - if (iocb->bh) { - qemu_bh_schedule(iocb->bh); - } -} - -BlockAIOCB *ide_issue_trim(BlockBackend *blk, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, - BlockCompletionFunc *cb, void *opaque) -{ - TrimAIOCB *iocb; - - iocb = blk_aio_get(&trim_aiocb_info, blk, cb, opaque); - iocb->blk = blk; - iocb->bh = qemu_bh_new(ide_trim_bh_cb, iocb); - iocb->ret = 0; - iocb->qiov = qiov; - iocb->i = -1; - iocb->j = 0; - ide_issue_trim_cb(iocb, 0); - return &iocb->common; -} - -void ide_abort_command(IDEState *s) -{ - ide_transfer_stop(s); - s->status = READY_STAT | ERR_STAT; - s->error = ABRT_ERR; -} - -/* prepare data transfer and tell what to do after */ -void ide_transfer_start(IDEState *s, uint8_t *buf, int size, - EndTransferFunc *end_transfer_func) -{ - s->end_transfer_func = end_transfer_func; - s->data_ptr = buf; - s->data_end = buf + size; - if (!(s->status & ERR_STAT)) { - s->status |= DRQ_STAT; - } - if (s->bus->dma->ops->start_transfer) { - s->bus->dma->ops->start_transfer(s->bus->dma); - } -} - -static void ide_cmd_done(IDEState *s) -{ - if (s->bus->dma->ops->cmd_done) { - s->bus->dma->ops->cmd_done(s->bus->dma); - } -} - -static void ide_transfer_halt(IDEState *s, - void(*end_transfer_func)(IDEState *), - bool notify) -{ - s->end_transfer_func = end_transfer_func; - s->data_ptr = s->io_buffer; - s->data_end = s->io_buffer; - s->status &= ~DRQ_STAT; - 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) -{ - int64_t sector_num; - if (s->select & 0x40) { - /* lba */ - if (!s->lba48) { - sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) | - (s->lcyl << 8) | s->sector; - } else { - sector_num = ((int64_t)s->hob_hcyl << 40) | - ((int64_t) s->hob_lcyl << 32) | - ((int64_t) s->hob_sector << 24) | - ((int64_t) s->hcyl << 16) | - ((int64_t) s->lcyl << 8) | s->sector; - } - } else { - sector_num = ((s->hcyl << 8) | s->lcyl) * s->heads * s->sectors + - (s->select & 0x0f) * s->sectors + (s->sector - 1); - } - return sector_num; -} - -void ide_set_sector(IDEState *s, int64_t sector_num) -{ - unsigned int cyl, r; - if (s->select & 0x40) { - if (!s->lba48) { - s->select = (s->select & 0xf0) | (sector_num >> 24); - s->hcyl = (sector_num >> 16); - s->lcyl = (sector_num >> 8); - s->sector = (sector_num); - } else { - s->sector = sector_num; - s->lcyl = sector_num >> 8; - s->hcyl = sector_num >> 16; - s->hob_sector = sector_num >> 24; - s->hob_lcyl = sector_num >> 32; - s->hob_hcyl = sector_num >> 40; - } - } else { - cyl = sector_num / (s->heads * s->sectors); - r = sector_num % (s->heads * s->sectors); - s->hcyl = cyl >> 8; - s->lcyl = cyl; - s->select = (s->select & 0xf0) | ((r / s->sectors) & 0x0f); - s->sector = (r % s->sectors) + 1; - } -} - -static void ide_rw_error(IDEState *s) { - ide_abort_command(s); - ide_set_irq(s->bus); -} - -static bool ide_sect_range_ok(IDEState *s, - uint64_t sector, uint64_t nb_sectors) -{ - uint64_t total_sectors; - - blk_get_geometry(s->blk, &total_sectors); - if (sector > total_sectors || nb_sectors > total_sectors - sector) { - return false; - } - 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) -{ - IDEState *s = opaque; - int n; - - s->pio_aiocb = NULL; - s->status &= ~BUSY_STAT; - - if (ret == -ECANCELED) { - return; - } - if (ret != 0) { - if (ide_handle_rw_error(s, -ret, IDE_RETRY_PIO | - IDE_RETRY_READ)) { - return; - } - } - - block_acct_done(blk_get_stats(s->blk), &s->acct); - - n = s->nsector; - if (n > s->req_nb_sectors) { - n = s->req_nb_sectors; - } - - ide_set_sector(s, ide_get_sector(s) + n); - 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); - ide_set_irq(s->bus); -} - -static void ide_sector_read(IDEState *s) -{ - int64_t sector_num; - int n; - - s->status = READY_STAT | SEEK_STAT; - s->error = 0; /* not needed by IDE spec, but needed by Windows */ - sector_num = ide_get_sector(s); - n = s->nsector; - - if (n == 0) { - ide_transfer_stop(s); - return; - } - - s->status |= BUSY_STAT; - - if (n > s->req_nb_sectors) { - n = s->req_nb_sectors; - } - -#if defined(DEBUG_IDE) - printf("sector=%" PRId64 "\n", sector_num); -#endif - - 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; - } - - s->iov.iov_base = s->io_buffer; - s->iov.iov_len = n * BDRV_SECTOR_SIZE; - 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); - s->pio_aiocb = ide_buffered_readv(s, sector_num, &s->qiov, n, - ide_sector_read_cb, s); -} - -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); -} - -void ide_set_inactive(IDEState *s, bool more) -{ - s->bus->dma->aiocb = NULL; - s->bus->retry_unit = -1; - s->bus->retry_sector_num = 0; - s->bus->retry_nsector = 0; - if (s->bus->dma->ops->set_inactive) { - s->bus->dma->ops->set_inactive(s->bus->dma, more); - } - ide_cmd_done(s); -} - -void ide_dma_error(IDEState *s) -{ - dma_buf_commit(s, 0); - ide_abort_command(s); - ide_set_inactive(s, false); - ide_set_irq(s->bus); -} - -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); - - if (action == BLOCK_ERROR_ACTION_STOP) { - assert(s->bus->retry_unit == s->unit); - s->bus->error_status = op; - } else if (action == BLOCK_ERROR_ACTION_REPORT) { - 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); - } - } - blk_error_action(s->blk, action, is_read, error); - return action != BLOCK_ERROR_ACTION_IGNORE; -} - -static void ide_dma_cb(void *opaque, int ret) -{ - IDEState *s = opaque; - int n; - int64_t sector_num; - bool stay_active = false; - - if (ret == -ECANCELED) { - return; - } - if (ret < 0) { - if (ide_handle_rw_error(s, -ret, ide_dma_cmd_to_retry(s->dma_cmd))) { - return; - } - } - - n = s->io_buffer_size >> 9; - if (n > s->nsector) { - /* The PRDs were longer than needed for this request. Shorten them so - * we don't get a negative remainder. The Active bit must remain set - * after the request completes. */ - n = s->nsector; - stay_active = true; - } - - sector_num = ide_get_sector(s); - if (n > 0) { - assert(n * 512 == s->sg.size); - dma_buf_commit(s, s->sg.size); - sector_num += n; - ide_set_sector(s, sector_num); - s->nsector -= n; - } - - /* end of transfer ? */ - if (s->nsector == 0) { - s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); - goto eot; - } - - /* launch next transfer */ - n = s->nsector; - s->io_buffer_index = 0; - s->io_buffer_size = n * 512; - if (s->bus->dma->ops->prepare_buf(s->bus->dma, s->io_buffer_size) < 512) { - /* The PRDs were too short. Reset the Active bit, but don't raise an - * interrupt. */ - s->status = READY_STAT | SEEK_STAT; - dma_buf_commit(s, 0); - goto eot; - } - -#ifdef DEBUG_AIO - printf("ide_dma_cb: sector_num=%" PRId64 " n=%d, cmd_cmd=%d\n", - sector_num, n, s->dma_cmd); -#endif - - 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; - } - - switch (s->dma_cmd) { - case IDE_DMA_READ: - s->bus->dma->aiocb = dma_blk_read(s->blk, &s->sg, sector_num, - ide_dma_cb, s); - break; - case IDE_DMA_WRITE: - s->bus->dma->aiocb = dma_blk_write(s->blk, &s->sg, sector_num, - ide_dma_cb, s); - break; - case IDE_DMA_TRIM: - s->bus->dma->aiocb = dma_blk_io(s->blk, &s->sg, sector_num, - ide_issue_trim, ide_dma_cb, s, - DMA_DIRECTION_TO_DEVICE); - break; - default: - abort(); - } - return; - -eot: - if (s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) { - block_acct_done(blk_get_stats(s->blk), &s->acct); - } - ide_set_inactive(s, stay_active); -} - -static void ide_sector_start_dma(IDEState *s, enum ide_dma_cmd dma_cmd) -{ - s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT; - s->io_buffer_size = 0; - s->dma_cmd = dma_cmd; - - switch (dma_cmd) { - case IDE_DMA_READ: - block_acct_start(blk_get_stats(s->blk), &s->acct, - s->nsector * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ); - break; - case IDE_DMA_WRITE: - block_acct_start(blk_get_stats(s->blk), &s->acct, - s->nsector * BDRV_SECTOR_SIZE, BLOCK_ACCT_WRITE); - break; - default: - break; - } - - ide_start_dma(s, ide_dma_cb); -} - -void ide_start_dma(IDEState *s, BlockCompletionFunc *cb) -{ - s->io_buffer_index = 0; - s->bus->retry_unit = s->unit; - s->bus->retry_sector_num = ide_get_sector(s); - s->bus->retry_nsector = s->nsector; - if (s->bus->dma->ops->start_dma) { - s->bus->dma->ops->start_dma(s->bus->dma, s, cb); - } -} - -static void ide_sector_write(IDEState *s); - -static void ide_sector_write_timer_cb(void *opaque) -{ - IDEState *s = opaque; - ide_set_irq(s->bus); -} - -static void ide_sector_write_cb(void *opaque, int ret) -{ - IDEState *s = opaque; - int n; - - if (ret == -ECANCELED) { - return; - } - - s->pio_aiocb = NULL; - s->status &= ~BUSY_STAT; - - if (ret != 0) { - if (ide_handle_rw_error(s, -ret, IDE_RETRY_PIO)) { - return; - } - } - - 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; - - ide_set_sector(s, ide_get_sector(s) + n); - if (s->nsector == 0) { - /* no more sectors to write */ - ide_transfer_stop(s); - } else { - int n1 = s->nsector; - if (n1 > s->req_nb_sectors) { - n1 = s->req_nb_sectors; - } - ide_transfer_start(s, s->io_buffer, n1 * BDRV_SECTOR_SIZE, - ide_sector_write); - } - - if (win2k_install_hack && ((++s->irq_count % 16) == 0)) { - /* It seems there is a bug in the Windows 2000 installer HDD - IDE driver which fills the disk with empty logs when the - IDE write IRQ comes too early. This hack tries to correct - 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) + - (NANOSECONDS_PER_SECOND / 1000)); - } else { - ide_set_irq(s->bus); - } -} - -static void ide_sector_write(IDEState *s) -{ - int64_t sector_num; - int n; - - s->status = READY_STAT | SEEK_STAT | BUSY_STAT; - sector_num = ide_get_sector(s); -#if defined(DEBUG_IDE) - printf("sector=%" PRId64 "\n", sector_num); -#endif - n = s->nsector; - if (n > s->req_nb_sectors) { - n = s->req_nb_sectors; - } - - 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; - } - - s->iov.iov_base = s->io_buffer; - s->iov.iov_len = n * BDRV_SECTOR_SIZE; - 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_WRITE); - s->pio_aiocb = blk_aio_writev(s->blk, sector_num, &s->qiov, n, - ide_sector_write_cb, s); -} - -static void ide_flush_cb(void *opaque, int ret) -{ - IDEState *s = opaque; - - s->pio_aiocb = NULL; - - if (ret == -ECANCELED) { - return; - } - if (ret < 0) { - /* XXX: What sector number to set here? */ - if (ide_handle_rw_error(s, -ret, IDE_RETRY_FLUSH)) { - return; - } - } - - if (s->blk) { - block_acct_done(blk_get_stats(s->blk), &s->acct); - } - s->status = READY_STAT | SEEK_STAT; - ide_cmd_done(s); - ide_set_irq(s->bus); -} - -static void ide_flush_cache(IDEState *s) -{ - if (s->blk == NULL) { - ide_flush_cb(s, 0); - return; - } - - s->status |= BUSY_STAT; - block_acct_start(blk_get_stats(s->blk), &s->acct, 0, BLOCK_ACCT_FLUSH); - s->pio_aiocb = blk_aio_flush(s->blk, ide_flush_cb, s); -} - -static void ide_cfata_metadata_inquiry(IDEState *s) -{ - uint16_t *p; - uint32_t spd; - - p = (uint16_t *) s->io_buffer; - memset(p, 0, 0x200); - spd = ((s->mdata_size - 1) >> 9) + 1; - - put_le16(p + 0, 0x0001); /* Data format revision */ - put_le16(p + 1, 0x0000); /* Media property: silicon */ - put_le16(p + 2, s->media_changed); /* Media status */ - put_le16(p + 3, s->mdata_size & 0xffff); /* Capacity in bytes (low) */ - put_le16(p + 4, s->mdata_size >> 16); /* Capacity in bytes (high) */ - put_le16(p + 5, spd & 0xffff); /* Sectors per device (low) */ - put_le16(p + 6, spd >> 16); /* Sectors per device (high) */ -} - -static void ide_cfata_metadata_read(IDEState *s) -{ - uint16_t *p; - - if (((s->hcyl << 16) | s->lcyl) << 9 > s->mdata_size + 2) { - s->status = ERR_STAT; - s->error = ABRT_ERR; - return; - } - - p = (uint16_t *) s->io_buffer; - memset(p, 0, 0x200); - - put_le16(p + 0, s->media_changed); /* Media status */ - memcpy(p + 1, s->mdata_storage + (((s->hcyl << 16) | s->lcyl) << 9), - MIN(MIN(s->mdata_size - (((s->hcyl << 16) | s->lcyl) << 9), - s->nsector << 9), 0x200 - 2)); -} - -static void ide_cfata_metadata_write(IDEState *s) -{ - if (((s->hcyl << 16) | s->lcyl) << 9 > s->mdata_size + 2) { - s->status = ERR_STAT; - s->error = ABRT_ERR; - return; - } - - s->media_changed = 0; - - memcpy(s->mdata_storage + (((s->hcyl << 16) | s->lcyl) << 9), - s->io_buffer + 2, - MIN(MIN(s->mdata_size - (((s->hcyl << 16) | s->lcyl) << 9), - s->nsector << 9), 0x200 - 2)); -} - -/* called when the inserted state of the media has changed */ -static void ide_cd_change_cb(void *opaque, bool load) -{ - IDEState *s = opaque; - uint64_t nb_sectors; - - s->tray_open = !load; - blk_get_geometry(s->blk, &nb_sectors); - s->nb_sectors = nb_sectors; - - /* - * First indicate to the guest that a CD has been removed. That's - * done on the next command the guest sends us. - * - * Then we set UNIT_ATTENTION, by which the guest will - * detect a new CD in the drive. See ide_atapi_cmd() for details. - */ - s->cdrom_changed = 1; - s->events.new_media = true; - s->events.eject_request = false; - ide_set_irq(s->bus); -} - -static void ide_cd_eject_request_cb(void *opaque, bool force) -{ - IDEState *s = opaque; - - s->events.eject_request = true; - if (force) { - s->tray_locked = false; - } - ide_set_irq(s->bus); -} - -static void ide_cmd_lba48_transform(IDEState *s, int lba48) -{ - s->lba48 = lba48; - - /* handle the 'magic' 0 nsector count conversion here. to avoid - * fiddling with the rest of the read logic, we just store the - * full sector count in ->nsector and ignore ->hob_nsector from now - */ - if (!s->lba48) { - if (!s->nsector) - s->nsector = 256; - } else { - if (!s->nsector && !s->hob_nsector) - s->nsector = 65536; - else { - int lo = s->nsector; - int hi = s->hob_nsector; - - s->nsector = (hi << 8) | lo; - } - } -} - -static void ide_clear_hob(IDEBus *bus) -{ - /* any write clears HOB high bit of device control register */ - bus->ifs[0].select &= ~(1 << 7); - bus->ifs[1].select &= ~(1 << 7); -} - -void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val) -{ - IDEBus *bus = opaque; - -#ifdef DEBUG_IDE - printf("IDE: write addr=0x%x val=0x%02x\n", addr, val); -#endif - - addr &= 7; - - /* ignore writes to command block while busy with previous command */ - if (addr != 7 && (idebus_active_if(bus)->status & (BUSY_STAT|DRQ_STAT))) - return; - - switch(addr) { - case 0: - break; - case 1: - ide_clear_hob(bus); - /* NOTE: data is written to the two drives */ - bus->ifs[0].hob_feature = bus->ifs[0].feature; - bus->ifs[1].hob_feature = bus->ifs[1].feature; - bus->ifs[0].feature = val; - bus->ifs[1].feature = val; - break; - case 2: - ide_clear_hob(bus); - bus->ifs[0].hob_nsector = bus->ifs[0].nsector; - bus->ifs[1].hob_nsector = bus->ifs[1].nsector; - bus->ifs[0].nsector = val; - bus->ifs[1].nsector = val; - break; - case 3: - ide_clear_hob(bus); - bus->ifs[0].hob_sector = bus->ifs[0].sector; - bus->ifs[1].hob_sector = bus->ifs[1].sector; - bus->ifs[0].sector = val; - bus->ifs[1].sector = val; - break; - case 4: - ide_clear_hob(bus); - bus->ifs[0].hob_lcyl = bus->ifs[0].lcyl; - bus->ifs[1].hob_lcyl = bus->ifs[1].lcyl; - bus->ifs[0].lcyl = val; - bus->ifs[1].lcyl = val; - break; - case 5: - ide_clear_hob(bus); - bus->ifs[0].hob_hcyl = bus->ifs[0].hcyl; - bus->ifs[1].hob_hcyl = bus->ifs[1].hcyl; - bus->ifs[0].hcyl = val; - bus->ifs[1].hcyl = val; - break; - case 6: - /* FIXME: HOB readback uses bit 7 */ - bus->ifs[0].select = (val & ~0x10) | 0xa0; - bus->ifs[1].select = (val | 0x10) | 0xa0; - /* select drive */ - bus->unit = (val >> 4) & 1; - break; - default: - case 7: - /* command */ - ide_exec_cmd(bus, val); - break; - } -} - -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) { - case DSM_TRIM: - if (s->blk) { - ide_sector_start_dma(s, IDE_DMA_TRIM); - return false; - } - break; - } - - ide_abort_command(s); - return true; -} - -static bool cmd_identify(IDEState *s, uint8_t cmd) -{ - if (s->blk && s->drive_kind != IDE_CD) { - if (s->drive_kind != IDE_CFATA) { - ide_identify(s); - } else { - ide_cfata_identify(s); - } - s->status = READY_STAT | SEEK_STAT; - ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop); - ide_set_irq(s->bus); - return false; - } else { - if (s->drive_kind == IDE_CD) { - ide_set_signature(s); - } - ide_abort_command(s); - } - - return true; -} - -static bool cmd_verify(IDEState *s, uint8_t cmd) -{ - bool lba48 = (cmd == WIN_VERIFY_EXT); - - /* do sector number check ? */ - ide_cmd_lba48_transform(s, lba48); - - return true; -} - -static bool cmd_set_multiple_mode(IDEState *s, uint8_t cmd) -{ - if (s->drive_kind == IDE_CFATA && s->nsector == 0) { - /* Disable Read and Write Multiple */ - s->mult_sectors = 0; - } else if ((s->nsector & 0xff) != 0 && - ((s->nsector & 0xff) > MAX_MULT_SECTORS || - (s->nsector & (s->nsector - 1)) != 0)) { - ide_abort_command(s); - } else { - s->mult_sectors = s->nsector & 0xff; - } - - return true; -} - -static bool cmd_read_multiple(IDEState *s, uint8_t cmd) -{ - bool lba48 = (cmd == WIN_MULTREAD_EXT); - - if (!s->blk || !s->mult_sectors) { - ide_abort_command(s); - return true; - } - - ide_cmd_lba48_transform(s, lba48); - s->req_nb_sectors = s->mult_sectors; - ide_sector_read(s); - return false; -} - -static bool cmd_write_multiple(IDEState *s, uint8_t cmd) -{ - bool lba48 = (cmd == WIN_MULTWRITE_EXT); - int n; - - if (!s->blk || !s->mult_sectors) { - ide_abort_command(s); - return true; - } - - ide_cmd_lba48_transform(s, lba48); - - s->req_nb_sectors = s->mult_sectors; - n = MIN(s->nsector, s->req_nb_sectors); - - s->status = SEEK_STAT | READY_STAT; - ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write); - - s->media_changed = 1; - - return false; -} - -static bool cmd_read_pio(IDEState *s, uint8_t cmd) -{ - bool lba48 = (cmd == WIN_READ_EXT); - - if (s->drive_kind == IDE_CD) { - ide_set_signature(s); /* odd, but ATA4 8.27.5.2 requires it */ - ide_abort_command(s); - return true; - } - - if (!s->blk) { - ide_abort_command(s); - return true; - } - - ide_cmd_lba48_transform(s, lba48); - s->req_nb_sectors = 1; - ide_sector_read(s); - - return false; -} - -static bool cmd_write_pio(IDEState *s, uint8_t cmd) -{ - bool lba48 = (cmd == WIN_WRITE_EXT); - - if (!s->blk) { - ide_abort_command(s); - return true; - } - - ide_cmd_lba48_transform(s, lba48); - - s->req_nb_sectors = 1; - s->status = SEEK_STAT | READY_STAT; - ide_transfer_start(s, s->io_buffer, 512, ide_sector_write); - - s->media_changed = 1; - - return false; -} - -static bool cmd_read_dma(IDEState *s, uint8_t cmd) -{ - bool lba48 = (cmd == WIN_READDMA_EXT); - - if (!s->blk) { - ide_abort_command(s); - return true; - } - - ide_cmd_lba48_transform(s, lba48); - ide_sector_start_dma(s, IDE_DMA_READ); - - return false; -} - -static bool cmd_write_dma(IDEState *s, uint8_t cmd) -{ - bool lba48 = (cmd == WIN_WRITEDMA_EXT); - - if (!s->blk) { - ide_abort_command(s); - return true; - } - - ide_cmd_lba48_transform(s, lba48); - ide_sector_start_dma(s, IDE_DMA_WRITE); - - s->media_changed = 1; - - return false; -} - -static bool cmd_flush_cache(IDEState *s, uint8_t cmd) -{ - ide_flush_cache(s); - return false; -} - -static bool cmd_seek(IDEState *s, uint8_t cmd) -{ - /* XXX: Check that seek is within bounds */ - return true; -} - -static bool cmd_read_native_max(IDEState *s, uint8_t cmd) -{ - bool lba48 = (cmd == WIN_READ_NATIVE_MAX_EXT); - - /* Refuse if no sectors are addressable (e.g. medium not inserted) */ - if (s->nb_sectors == 0) { - ide_abort_command(s); - return true; - } - - ide_cmd_lba48_transform(s, lba48); - ide_set_sector(s, s->nb_sectors - 1); - - return true; -} - -static bool cmd_check_power_mode(IDEState *s, uint8_t cmd) -{ - s->nsector = 0xff; /* device active or idle */ - return true; -} - -static bool cmd_set_features(IDEState *s, uint8_t cmd) -{ - uint16_t *identify_data; - - if (!s->blk) { - ide_abort_command(s); - return true; - } - - /* XXX: valid for CDROM ? */ - switch (s->feature) { - case 0x02: /* write cache enable */ - blk_set_enable_write_cache(s->blk, true); - identify_data = (uint16_t *)s->identify_data; - put_le16(identify_data + 85, (1 << 14) | (1 << 5) | 1); - return true; - case 0x82: /* write cache disable */ - blk_set_enable_write_cache(s->blk, false); - identify_data = (uint16_t *)s->identify_data; - put_le16(identify_data + 85, (1 << 14) | 1); - ide_flush_cache(s); - return false; - case 0xcc: /* reverting to power-on defaults enable */ - case 0x66: /* reverting to power-on defaults disable */ - case 0xaa: /* read look-ahead enable */ - case 0x55: /* read look-ahead disable */ - case 0x05: /* set advanced power management mode */ - case 0x85: /* disable advanced power management mode */ - case 0x69: /* NOP */ - case 0x67: /* NOP */ - case 0x96: /* NOP */ - case 0x9a: /* NOP */ - case 0x42: /* enable Automatic Acoustic Mode */ - case 0xc2: /* disable Automatic Acoustic Mode */ - return true; - case 0x03: /* set transfer mode */ - { - uint8_t val = s->nsector & 0x07; - identify_data = (uint16_t *)s->identify_data; - - switch (s->nsector >> 3) { - case 0x00: /* pio default */ - case 0x01: /* pio mode */ - put_le16(identify_data + 62, 0x07); - put_le16(identify_data + 63, 0x07); - put_le16(identify_data + 88, 0x3f); - break; - case 0x02: /* sigle word dma mode*/ - put_le16(identify_data + 62, 0x07 | (1 << (val + 8))); - put_le16(identify_data + 63, 0x07); - put_le16(identify_data + 88, 0x3f); - break; - case 0x04: /* mdma mode */ - put_le16(identify_data + 62, 0x07); - put_le16(identify_data + 63, 0x07 | (1 << (val + 8))); - put_le16(identify_data + 88, 0x3f); - break; - case 0x08: /* udma mode */ - put_le16(identify_data + 62, 0x07); - put_le16(identify_data + 63, 0x07); - put_le16(identify_data + 88, 0x3f | (1 << (val + 8))); - break; - default: - goto abort_cmd; - } - return true; - } - } - -abort_cmd: - ide_abort_command(s); - return true; -} - - -/*** ATAPI commands ***/ - -static bool cmd_identify_packet(IDEState *s, uint8_t cmd) -{ - ide_atapi_identify(s); - s->status = READY_STAT | SEEK_STAT; - ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop); - ide_set_irq(s->bus); - return false; -} - -static bool cmd_exec_dev_diagnostic(IDEState *s, uint8_t cmd) -{ - ide_set_signature(s); - - if (s->drive_kind == IDE_CD) { - s->status = 0; /* ATAPI spec (v6) section 9.10 defines packet - * devices to return a clear status register - * with READY_STAT *not* set. */ - s->error = 0x01; - } else { - s->status = READY_STAT | SEEK_STAT; - /* The bits of the error register are not as usual for this command! - * They are part of the regular output (this is why ERR_STAT isn't set) - * Device 0 passed, Device 1 passed or not present. */ - s->error = 0x01; - ide_set_irq(s->bus); - } - - return false; -} - -static bool cmd_packet(IDEState *s, uint8_t cmd) -{ - /* overlapping commands not supported */ - if (s->feature & 0x02) { - ide_abort_command(s); - return true; - } - - 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); - return false; -} - - -/*** CF-ATA commands ***/ - -static bool cmd_cfa_req_ext_error_code(IDEState *s, uint8_t cmd) -{ - s->error = 0x09; /* miscellaneous error */ - s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); - - return false; -} - -static bool cmd_cfa_erase_sectors(IDEState *s, uint8_t cmd) -{ - /* WIN_SECURITY_FREEZE_LOCK has the same ID as CFA_WEAR_LEVEL and is - * required for Windows 8 to work with AHCI */ - - if (cmd == CFA_WEAR_LEVEL) { - s->nsector = 0; - } - - if (cmd == CFA_ERASE_SECTORS) { - s->media_changed = 1; - } - - return true; -} - -static bool cmd_cfa_translate_sector(IDEState *s, uint8_t cmd) -{ - s->status = READY_STAT | SEEK_STAT; - - memset(s->io_buffer, 0, 0x200); - s->io_buffer[0x00] = s->hcyl; /* Cyl MSB */ - s->io_buffer[0x01] = s->lcyl; /* Cyl LSB */ - s->io_buffer[0x02] = s->select; /* Head */ - s->io_buffer[0x03] = s->sector; /* Sector */ - s->io_buffer[0x04] = ide_get_sector(s) >> 16; /* LBA MSB */ - s->io_buffer[0x05] = ide_get_sector(s) >> 8; /* LBA */ - s->io_buffer[0x06] = ide_get_sector(s) >> 0; /* LBA LSB */ - s->io_buffer[0x13] = 0x00; /* Erase flag */ - s->io_buffer[0x18] = 0x00; /* Hot count */ - s->io_buffer[0x19] = 0x00; /* Hot count */ - s->io_buffer[0x1a] = 0x01; /* Hot count */ - - ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); - ide_set_irq(s->bus); - - return false; -} - -static bool cmd_cfa_access_metadata_storage(IDEState *s, uint8_t cmd) -{ - switch (s->feature) { - case 0x02: /* Inquiry Metadata Storage */ - ide_cfata_metadata_inquiry(s); - break; - case 0x03: /* Read Metadata Storage */ - ide_cfata_metadata_read(s); - break; - case 0x04: /* Write Metadata Storage */ - ide_cfata_metadata_write(s); - break; - default: - ide_abort_command(s); - return true; - } - - ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); - s->status = 0x00; /* NOTE: READY is _not_ set */ - ide_set_irq(s->bus); - - return false; -} - -static bool cmd_ibm_sense_condition(IDEState *s, uint8_t cmd) -{ - switch (s->feature) { - case 0x01: /* sense temperature in device */ - s->nsector = 0x50; /* +20 C */ - break; - default: - ide_abort_command(s); - return true; - } - - return true; -} - - -/*** SMART commands ***/ - -static bool cmd_smart(IDEState *s, uint8_t cmd) -{ - int n; - - if (s->hcyl != 0xc2 || s->lcyl != 0x4f) { - goto abort_cmd; - } - - if (!s->smart_enabled && s->feature != SMART_ENABLE) { - goto abort_cmd; - } - - switch (s->feature) { - case SMART_DISABLE: - s->smart_enabled = 0; - return true; - - case SMART_ENABLE: - s->smart_enabled = 1; - return true; - - case SMART_ATTR_AUTOSAVE: - switch (s->sector) { - case 0x00: - s->smart_autosave = 0; - break; - case 0xf1: - s->smart_autosave = 1; - break; - default: - goto abort_cmd; - } - return true; - - case SMART_STATUS: - if (!s->smart_errors) { - s->hcyl = 0xc2; - s->lcyl = 0x4f; - } else { - s->hcyl = 0x2c; - s->lcyl = 0xf4; - } - return true; - - case SMART_READ_THRESH: - memset(s->io_buffer, 0, 0x200); - s->io_buffer[0] = 0x01; /* smart struct version */ - - for (n = 0; n < ARRAY_SIZE(smart_attributes); n++) { - s->io_buffer[2 + 0 + (n * 12)] = smart_attributes[n][0]; - s->io_buffer[2 + 1 + (n * 12)] = smart_attributes[n][11]; - } - - /* checksum */ - for (n = 0; n < 511; n++) { - s->io_buffer[511] += s->io_buffer[n]; - } - s->io_buffer[511] = 0x100 - s->io_buffer[511]; - - s->status = READY_STAT | SEEK_STAT; - ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); - ide_set_irq(s->bus); - return false; - - case SMART_READ_DATA: - memset(s->io_buffer, 0, 0x200); - s->io_buffer[0] = 0x01; /* smart struct version */ - - for (n = 0; n < ARRAY_SIZE(smart_attributes); n++) { - int i; - for (i = 0; i < 11; i++) { - s->io_buffer[2 + i + (n * 12)] = smart_attributes[n][i]; - } - } - - s->io_buffer[362] = 0x02 | (s->smart_autosave ? 0x80 : 0x00); - if (s->smart_selftest_count == 0) { - s->io_buffer[363] = 0; - } else { - s->io_buffer[363] = - s->smart_selftest_data[3 + - (s->smart_selftest_count - 1) * - 24]; - } - s->io_buffer[364] = 0x20; - s->io_buffer[365] = 0x01; - /* offline data collection capacity: execute + self-test*/ - s->io_buffer[367] = (1 << 4 | 1 << 3 | 1); - s->io_buffer[368] = 0x03; /* smart capability (1) */ - s->io_buffer[369] = 0x00; /* smart capability (2) */ - s->io_buffer[370] = 0x01; /* error logging supported */ - s->io_buffer[372] = 0x02; /* minutes for poll short test */ - s->io_buffer[373] = 0x36; /* minutes for poll ext test */ - s->io_buffer[374] = 0x01; /* minutes for poll conveyance */ - - for (n = 0; n < 511; n++) { - s->io_buffer[511] += s->io_buffer[n]; - } - s->io_buffer[511] = 0x100 - s->io_buffer[511]; - - s->status = READY_STAT | SEEK_STAT; - ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); - ide_set_irq(s->bus); - return false; - - case SMART_READ_LOG: - switch (s->sector) { - case 0x01: /* summary smart error log */ - memset(s->io_buffer, 0, 0x200); - s->io_buffer[0] = 0x01; - s->io_buffer[1] = 0x00; /* no error entries */ - s->io_buffer[452] = s->smart_errors & 0xff; - s->io_buffer[453] = (s->smart_errors & 0xff00) >> 8; - - for (n = 0; n < 511; n++) { - s->io_buffer[511] += s->io_buffer[n]; - } - s->io_buffer[511] = 0x100 - s->io_buffer[511]; - break; - case 0x06: /* smart self test log */ - memset(s->io_buffer, 0, 0x200); - s->io_buffer[0] = 0x01; - if (s->smart_selftest_count == 0) { - s->io_buffer[508] = 0; - } else { - s->io_buffer[508] = s->smart_selftest_count; - for (n = 2; n < 506; n++) { - s->io_buffer[n] = s->smart_selftest_data[n]; - } - } - - for (n = 0; n < 511; n++) { - s->io_buffer[511] += s->io_buffer[n]; - } - s->io_buffer[511] = 0x100 - s->io_buffer[511]; - break; - default: - goto abort_cmd; - } - s->status = READY_STAT | SEEK_STAT; - ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); - ide_set_irq(s->bus); - return false; - - case SMART_EXECUTE_OFFLINE: - switch (s->sector) { - case 0: /* off-line routine */ - case 1: /* short self test */ - case 2: /* extended self test */ - s->smart_selftest_count++; - if (s->smart_selftest_count > 21) { - s->smart_selftest_count = 1; - } - n = 2 + (s->smart_selftest_count - 1) * 24; - s->smart_selftest_data[n] = s->sector; - s->smart_selftest_data[n + 1] = 0x00; /* OK and finished */ - s->smart_selftest_data[n + 2] = 0x34; /* hour count lsb */ - s->smart_selftest_data[n + 3] = 0x12; /* hour count msb */ - break; - default: - goto abort_cmd; - } - return true; - } - -abort_cmd: - ide_abort_command(s); - return true; -} - -#define HD_OK (1u << IDE_HD) -#define CD_OK (1u << IDE_CD) -#define CFA_OK (1u << IDE_CFATA) -#define HD_CFA_OK (HD_OK | CFA_OK) -#define ALL_OK (HD_OK | CD_OK | CFA_OK) - -/* Set the Disk Seek Completed status bit during completion */ -#define SET_DSC (1u << 8) - -/* See ACS-2 T13/2015-D Table B.2 Command codes */ -static const struct { - /* Returns true if the completion code should be run */ - bool (*handler)(IDEState *s, uint8_t cmd); - int flags; -} 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, 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, 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 }, - [WIN_MULTREAD_EXT] = { cmd_read_multiple, HD_CFA_OK }, - [WIN_WRITE] = { cmd_write_pio, HD_CFA_OK }, - [WIN_WRITE_ONCE] = { cmd_write_pio, HD_CFA_OK }, - [WIN_WRITE_EXT] = { cmd_write_pio, HD_CFA_OK }, - [WIN_WRITEDMA_EXT] = { cmd_write_dma, HD_CFA_OK }, - [CFA_WRITE_SECT_WO_ERASE] = { cmd_write_pio, CFA_OK }, - [WIN_MULTWRITE_EXT] = { cmd_write_multiple, HD_CFA_OK }, - [WIN_WRITE_VERIFY] = { cmd_write_pio, HD_CFA_OK }, - [WIN_VERIFY] = { cmd_verify, HD_CFA_OK | SET_DSC }, - [WIN_VERIFY_ONCE] = { cmd_verify, HD_CFA_OK | SET_DSC }, - [WIN_VERIFY_EXT] = { cmd_verify, HD_CFA_OK | SET_DSC }, - [WIN_SEEK] = { cmd_seek, HD_CFA_OK | SET_DSC }, - [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, 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 }, - [CFA_ACCESS_METADATA_STORAGE] = { cmd_cfa_access_metadata_storage, CFA_OK }, - [CFA_ERASE_SECTORS] = { cmd_cfa_erase_sectors, CFA_OK | SET_DSC }, - [WIN_MULTREAD] = { cmd_read_multiple, HD_CFA_OK }, - [WIN_MULTWRITE] = { cmd_write_multiple, HD_CFA_OK }, - [WIN_SETMULT] = { cmd_set_multiple_mode, HD_CFA_OK | SET_DSC }, - [WIN_READDMA] = { cmd_read_dma, HD_CFA_OK }, - [WIN_READDMA_ONCE] = { cmd_read_dma, HD_CFA_OK }, - [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, 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, HD_CFA_OK | SET_DSC }, -}; - -static bool ide_cmd_permitted(IDEState *s, uint32_t cmd) -{ - return cmd < ARRAY_SIZE(ide_cmd_table) - && (ide_cmd_table[cmd].flags & (1u << s->drive_kind)); -} - -void ide_exec_cmd(IDEBus *bus, uint32_t val) -{ - IDEState *s; - bool complete; - -#if defined(DEBUG_IDE) - printf("ide: CMD=%02x\n", val); -#endif - s = idebus_active_if(bus); - /* ignore commands to non existent slave */ - if (s != bus->ifs && !s->blk) { - 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); - ide_set_irq(s->bus); - return; - } - - s->status = READY_STAT | BUSY_STAT; - s->error = 0; - s->io_buffer_offset = 0; - - complete = ide_cmd_table[val].handler(s, val); - if (complete) { - s->status &= ~BUSY_STAT; - assert(!!s->error == !!(s->status & ERR_STAT)); - - if ((ide_cmd_table[val].flags & SET_DSC) && !s->error) { - s->status |= SEEK_STAT; - } - - ide_cmd_done(s); - ide_set_irq(s->bus); - } -} - -uint32_t ide_ioport_read(void *opaque, uint32_t addr1) -{ - IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); - uint32_t addr; - int ret, hob; - - addr = addr1 & 7; - /* FIXME: HOB readback uses bit 7, but it's always set right now */ - //hob = s->select & (1 << 7); - hob = 0; - switch(addr) { - case 0: - ret = 0xff; - break; - case 1: - if ((!bus->ifs[0].blk && !bus->ifs[1].blk) || - (s != bus->ifs && !s->blk)) { - ret = 0; - } else if (!hob) { - ret = s->error; - } else { - ret = s->hob_feature; - } - break; - case 2: - if (!bus->ifs[0].blk && !bus->ifs[1].blk) { - ret = 0; - } else if (!hob) { - ret = s->nsector & 0xff; - } else { - ret = s->hob_nsector; - } - break; - case 3: - if (!bus->ifs[0].blk && !bus->ifs[1].blk) { - ret = 0; - } else if (!hob) { - ret = s->sector; - } else { - ret = s->hob_sector; - } - break; - case 4: - if (!bus->ifs[0].blk && !bus->ifs[1].blk) { - ret = 0; - } else if (!hob) { - ret = s->lcyl; - } else { - ret = s->hob_lcyl; - } - break; - case 5: - if (!bus->ifs[0].blk && !bus->ifs[1].blk) { - ret = 0; - } else if (!hob) { - ret = s->hcyl; - } else { - ret = s->hob_hcyl; - } - break; - case 6: - if (!bus->ifs[0].blk && !bus->ifs[1].blk) { - ret = 0; - } else { - ret = s->select; - } - break; - default: - case 7: - if ((!bus->ifs[0].blk && !bus->ifs[1].blk) || - (s != bus->ifs && !s->blk)) { - ret = 0; - } else { - ret = s->status; - } - qemu_irq_lower(bus->irq); - break; - } -#ifdef DEBUG_IDE - printf("ide: read addr=0x%x val=%02x\n", addr1, ret); -#endif - return ret; -} - -uint32_t ide_status_read(void *opaque, uint32_t addr) -{ - IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); - int ret; - - if ((!bus->ifs[0].blk && !bus->ifs[1].blk) || - (s != bus->ifs && !s->blk)) { - ret = 0; - } else { - ret = s->status; - } -#ifdef DEBUG_IDE - printf("ide: read status addr=0x%x val=%02x\n", addr, ret); -#endif - return ret; -} - -void ide_cmd_write(void *opaque, uint32_t addr, uint32_t val) -{ - IDEBus *bus = opaque; - IDEState *s; - int i; - -#ifdef DEBUG_IDE - printf("ide: write control addr=0x%x val=%02x\n", addr, val); -#endif - /* common for both drives */ - if (!(bus->cmd & IDE_CMD_RESET) && - (val & IDE_CMD_RESET)) { - /* reset low to high */ - for(i = 0;i < 2; i++) { - s = &bus->ifs[i]; - s->status = BUSY_STAT | SEEK_STAT; - s->error = 0x01; - } - } else if ((bus->cmd & IDE_CMD_RESET) && - !(val & IDE_CMD_RESET)) { - /* high to low */ - for(i = 0;i < 2; i++) { - s = &bus->ifs[i]; - if (s->drive_kind == IDE_CD) - s->status = 0x00; /* NOTE: READY is _not_ set */ - else - s->status = READY_STAT | SEEK_STAT; - ide_set_signature(s); - } - } - - bus->cmd = val; -} - -/* - * Returns true if the running PIO transfer is a PIO out (i.e. data is - * transferred from the device to the guest), false if it's a PIO in - */ -static bool ide_is_pio_out(IDEState *s) -{ - if (s->end_transfer_func == ide_sector_write || - s->end_transfer_func == ide_atapi_cmd) { - return false; - } else if (s->end_transfer_func == ide_sector_read || - s->end_transfer_func == ide_transfer_stop || - s->end_transfer_func == ide_atapi_cmd_reply_end || - s->end_transfer_func == ide_dummy_transfer_stop) { - return true; - } - - abort(); -} - -void ide_data_writew(void *opaque, uint32_t addr, uint32_t val) -{ - IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); - uint8_t *p; - - /* PIO data access allowed only when DRQ bit is set. The result of a write - * during PIO out is indeterminate, just ignore it. */ - if (!(s->status & DRQ_STAT) || ide_is_pio_out(s)) { - return; - } - - p = s->data_ptr; - if (p + 2 > s->data_end) { - return; - } - - *(uint16_t *)p = le16_to_cpu(val); - p += 2; - s->data_ptr = p; - if (p >= s->data_end) { - s->status &= ~DRQ_STAT; - s->end_transfer_func(s); - } -} - -uint32_t ide_data_readw(void *opaque, uint32_t addr) -{ - IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); - uint8_t *p; - int ret; - - /* PIO data access allowed only when DRQ bit is set. The result of a read - * during PIO in is indeterminate, return 0 and don't move forward. */ - if (!(s->status & DRQ_STAT) || !ide_is_pio_out(s)) { - return 0; - } - - p = s->data_ptr; - if (p + 2 > s->data_end) { - return 0; - } - - ret = cpu_to_le16(*(uint16_t *)p); - p += 2; - s->data_ptr = p; - if (p >= s->data_end) { - s->status &= ~DRQ_STAT; - s->end_transfer_func(s); - } - return ret; -} - -void ide_data_writel(void *opaque, uint32_t addr, uint32_t val) -{ - IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); - uint8_t *p; - - /* PIO data access allowed only when DRQ bit is set. The result of a write - * during PIO out is indeterminate, just ignore it. */ - if (!(s->status & DRQ_STAT) || ide_is_pio_out(s)) { - return; - } - - p = s->data_ptr; - if (p + 4 > s->data_end) { - return; - } - - *(uint32_t *)p = le32_to_cpu(val); - p += 4; - s->data_ptr = p; - if (p >= s->data_end) { - s->status &= ~DRQ_STAT; - s->end_transfer_func(s); - } -} - -uint32_t ide_data_readl(void *opaque, uint32_t addr) -{ - IDEBus *bus = opaque; - IDEState *s = idebus_active_if(bus); - uint8_t *p; - int ret; - - /* PIO data access allowed only when DRQ bit is set. The result of a read - * during PIO in is indeterminate, return 0 and don't move forward. */ - if (!(s->status & DRQ_STAT) || !ide_is_pio_out(s)) { - return 0; - } - - p = s->data_ptr; - if (p + 4 > s->data_end) { - return 0; - } - - ret = cpu_to_le32(*(uint32_t *)p); - p += 4; - s->data_ptr = p; - if (p >= s->data_end) { - s->status &= ~DRQ_STAT; - s->end_transfer_func(s); - } - return ret; -} - -static void ide_dummy_transfer_stop(IDEState *s) -{ - s->data_ptr = s->io_buffer; - s->data_end = s->io_buffer; - s->io_buffer[0] = 0xff; - s->io_buffer[1] = 0xff; - s->io_buffer[2] = 0xff; - s->io_buffer[3] = 0xff; -} - -void ide_bus_reset(IDEBus *bus) -{ - bus->unit = 0; - bus->cmd = 0; - ide_reset(&bus->ifs[0]); - ide_reset(&bus->ifs[1]); - ide_clear_hob(bus); - - /* pending async DMA */ - if (bus->dma->aiocb) { -#ifdef DEBUG_AIO - printf("aio_cancel\n"); -#endif - blk_aio_cancel(bus->dma->aiocb); - bus->dma->aiocb = NULL; - } - - /* reset dma provider too */ - if (bus->dma->ops->reset) { - bus->dma->ops->reset(bus->dma); - } -} - -static bool ide_cd_is_tray_open(void *opaque) -{ - return ((IDEState *)opaque)->tray_open; -} - -static bool ide_cd_is_medium_locked(void *opaque) -{ - return ((IDEState *)opaque)->tray_locked; -} - -static void ide_resize_cb(void *opaque) -{ - IDEState *s = opaque; - uint64_t nb_sectors; - - if (!s->identify_set) { - return; - } - - blk_get_geometry(s->blk, &nb_sectors); - s->nb_sectors = nb_sectors; - - /* Update the identify data buffer. */ - if (s->drive_kind == IDE_CFATA) { - ide_cfata_identify_size(s); - } else { - /* IDE_CD uses a different set of callbacks entirely. */ - assert(s->drive_kind != IDE_CD); - ide_identify_size(s); - } -} - -static const BlockDevOps ide_cd_block_ops = { - .change_media_cb = ide_cd_change_cb, - .eject_request_cb = ide_cd_eject_request_cb, - .is_tray_open = ide_cd_is_tray_open, - .is_medium_locked = ide_cd_is_medium_locked, -}; - -static const BlockDevOps ide_hd_block_ops = { - .resize_cb = ide_resize_cb, -}; - -int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind, - const char *version, const char *serial, const char *model, - uint64_t wwn, - uint32_t cylinders, uint32_t heads, uint32_t secs, - int chs_trans) -{ - uint64_t nb_sectors; - - s->blk = blk; - s->drive_kind = kind; - - blk_get_geometry(blk, &nb_sectors); - s->cylinders = cylinders; - s->heads = heads; - s->sectors = secs; - s->chs_trans = chs_trans; - s->nb_sectors = nb_sectors; - s->wwn = wwn; - /* The SMART values should be preserved across power cycles - but they aren't. */ - s->smart_enabled = 1; - s->smart_autosave = 1; - s->smart_errors = 0; - s->smart_selftest_count = 0; - if (kind == IDE_CD) { - blk_set_dev_ops(blk, &ide_cd_block_ops, s); - blk_set_guest_block_size(blk, 2048); - } else { - if (!blk_is_inserted(s->blk)) { - error_report("Device needs media, but drive is empty"); - return -1; - } - if (blk_is_read_only(blk)) { - error_report("Can't use a read-only drive"); - return -1; - } - blk_set_dev_ops(blk, &ide_hd_block_ops, s); - } - if (serial) { - pstrcpy(s->drive_serial_str, sizeof(s->drive_serial_str), serial); - } else { - snprintf(s->drive_serial_str, sizeof(s->drive_serial_str), - "QM%05d", s->drive_serial); - } - if (model) { - pstrcpy(s->drive_model_str, sizeof(s->drive_model_str), model); - } else { - switch (kind) { - case IDE_CD: - strcpy(s->drive_model_str, "QEMU DVD-ROM"); - break; - case IDE_CFATA: - strcpy(s->drive_model_str, "QEMU MICRODRIVE"); - break; - default: - strcpy(s->drive_model_str, "QEMU HARDDISK"); - break; - } - } - - if (version) { - pstrcpy(s->version, sizeof(s->version), version); - } else { - pstrcpy(s->version, sizeof(s->version), qemu_hw_version()); - } - - ide_reset(s); - blk_iostatus_enable(blk); - return 0; -} - -static void ide_init1(IDEBus *bus, int unit) -{ - static int drive_serial = 1; - IDEState *s = &bus->ifs[unit]; - - s->bus = bus; - s->unit = unit; - s->drive_serial = drive_serial++; - /* we need at least 2k alignment for accessing CDROMs using O_DIRECT */ - s->io_buffer_total_len = IDE_DMA_BUF_SECTORS*512 + 4; - s->io_buffer = qemu_memalign(2048, s->io_buffer_total_len); - memset(s->io_buffer, 0, s->io_buffer_total_len); - - s->smart_selftest_data = blk_blockalign(s->blk, 512); - memset(s->smart_selftest_data, 0, 512); - - s->sector_write_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - ide_sector_write_timer_cb, s); -} - -static int ide_nop_int(IDEDMA *dma, int x) -{ - return 0; -} - -static void ide_nop(IDEDMA *dma) -{ -} - -static int32_t ide_nop_int32(IDEDMA *dma, int32_t l) -{ - return 0; -} - -static const IDEDMAOps ide_dma_nop_ops = { - .prepare_buf = ide_nop_int32, - .restart_dma = ide_nop, - .rw_buf = ide_nop_int, -}; - -static void ide_restart_dma(IDEState *s, enum ide_dma_cmd dma_cmd) -{ - s->unit = s->bus->retry_unit; - ide_set_sector(s, s->bus->retry_sector_num); - s->nsector = s->bus->retry_nsector; - s->bus->dma->ops->restart_dma(s->bus->dma); - s->io_buffer_size = 0; - s->dma_cmd = dma_cmd; - ide_start_dma(s, ide_dma_cb); -} - -static void ide_restart_bh(void *opaque) -{ - IDEBus *bus = opaque; - IDEState *s; - bool is_read; - int error_status; - - qemu_bh_delete(bus->bh); - bus->bh = NULL; - - error_status = bus->error_status; - if (bus->error_status == 0) { - return; - } - - s = idebus_active_if(bus); - is_read = (bus->error_status & IDE_RETRY_READ) != 0; - - /* The error status must be cleared before resubmitting the request: The - * request may fail again, and this case can only be distinguished if the - * called function can set a new error status. */ - bus->error_status = 0; - - /* The HBA has generically asked to be kicked on retry */ - if (error_status & IDE_RETRY_HBA) { - if (s->bus->dma->ops->restart) { - s->bus->dma->ops->restart(s->bus->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 (IS_IDE_RETRY_PIO(error_status)) { - if (is_read) { - ide_sector_read(s); - } else { - ide_sector_write(s); - } - } 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 { - abort(); - } -} - -static void ide_restart_cb(void *opaque, int running, RunState state) -{ - IDEBus *bus = opaque; - - if (!running) - return; - - if (!bus->bh) { - bus->bh = qemu_bh_new(ide_restart_bh, bus); - qemu_bh_schedule(bus->bh); - } -} - -void ide_register_restart_cb(IDEBus *bus) -{ - if (bus->dma->ops->restart_dma) { - qemu_add_vm_change_state_handler(ide_restart_cb, bus); - } -} - -static IDEDMA ide_dma_nop = { - .ops = &ide_dma_nop_ops, - .aiocb = NULL, -}; - -void ide_init2(IDEBus *bus, qemu_irq irq) -{ - int i; - - for(i = 0; i < 2; i++) { - ide_init1(bus, i); - ide_reset(&bus->ifs[i]); - } - bus->irq = irq; - bus->dma = &ide_dma_nop; -} - -static const MemoryRegionPortio ide_portio_list[] = { - { 0, 8, 1, .read = ide_ioport_read, .write = ide_ioport_write }, - { 0, 1, 2, .read = ide_data_readw, .write = ide_data_writew }, - { 0, 1, 4, .read = ide_data_readl, .write = ide_data_writel }, - PORTIO_END_OF_LIST(), -}; - -static const MemoryRegionPortio ide_portio2_list[] = { - { 0, 1, 1, .read = ide_status_read, .write = ide_cmd_write }, - PORTIO_END_OF_LIST(), -}; - -void ide_init_ioport(IDEBus *bus, ISADevice *dev, int iobase, int iobase2) -{ - /* ??? Assume only ISA and PCI configurations, and that the PCI-ISA - bridge has been setup properly to always register with ISA. */ - isa_register_portio_list(dev, iobase, ide_portio_list, bus, "ide"); - - if (iobase2) { - isa_register_portio_list(dev, iobase2, ide_portio2_list, bus, "ide"); - } -} - -static bool is_identify_set(void *opaque, int version_id) -{ - IDEState *s = opaque; - - return s->identify_set != 0; -} - -static EndTransferFunc* transfer_end_table[] = { - ide_sector_read, - ide_sector_write, - ide_transfer_stop, - ide_atapi_cmd_reply_end, - ide_atapi_cmd, - ide_dummy_transfer_stop, -}; - -static int transfer_end_table_idx(EndTransferFunc *fn) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(transfer_end_table); i++) - if (transfer_end_table[i] == fn) - return i; - - return -1; -} - -static int ide_drive_post_load(void *opaque, int version_id) -{ - IDEState *s = opaque; - - if (s->blk && s->identify_set) { - blk_set_enable_write_cache(s->blk, !!(s->identify_data[85] & (1 << 5))); - } - return 0; -} - -static int ide_drive_pio_post_load(void *opaque, int version_id) -{ - IDEState *s = opaque; - - if (s->end_transfer_fn_idx >= ARRAY_SIZE(transfer_end_table)) { - return -EINVAL; - } - s->end_transfer_func = transfer_end_table[s->end_transfer_fn_idx]; - s->data_ptr = s->io_buffer + s->cur_io_buffer_offset; - s->data_end = s->data_ptr + s->cur_io_buffer_len; - s->atapi_dma = s->feature & 1; /* as per cmd_packet */ - - return 0; -} - -static void ide_drive_pio_pre_save(void *opaque) -{ - IDEState *s = opaque; - int idx; - - s->cur_io_buffer_offset = s->data_ptr - s->io_buffer; - s->cur_io_buffer_len = s->data_end - s->data_ptr; - - idx = transfer_end_table_idx(s->end_transfer_func); - if (idx == -1) { - fprintf(stderr, "%s: invalid end_transfer_func for DRQ_STAT\n", - __func__); - s->end_transfer_fn_idx = 2; - } else { - s->end_transfer_fn_idx = idx; - } -} - -static bool ide_drive_pio_state_needed(void *opaque) -{ - IDEState *s = opaque; - - return ((s->status & DRQ_STAT) != 0) - || (s->bus->error_status & IDE_RETRY_PIO); -} - -static bool ide_tray_state_needed(void *opaque) -{ - IDEState *s = opaque; - - return s->tray_open || s->tray_locked; -} - -static bool ide_atapi_gesn_needed(void *opaque) -{ - IDEState *s = opaque; - - return s->events.new_media || s->events.eject_request; -} - -static bool ide_error_needed(void *opaque) -{ - IDEBus *bus = opaque; - - return (bus->error_status != 0); -} - -/* Fields for GET_EVENT_STATUS_NOTIFICATION ATAPI command */ -static const VMStateDescription vmstate_ide_atapi_gesn_state = { - .name ="ide_drive/atapi/gesn_state", - .version_id = 1, - .minimum_version_id = 1, - .needed = ide_atapi_gesn_needed, - .fields = (VMStateField[]) { - VMSTATE_BOOL(events.new_media, IDEState), - VMSTATE_BOOL(events.eject_request, IDEState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_ide_tray_state = { - .name = "ide_drive/tray_state", - .version_id = 1, - .minimum_version_id = 1, - .needed = ide_tray_state_needed, - .fields = (VMStateField[]) { - VMSTATE_BOOL(tray_open, IDEState), - VMSTATE_BOOL(tray_locked, IDEState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_ide_drive_pio_state = { - .name = "ide_drive/pio_state", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = ide_drive_pio_pre_save, - .post_load = ide_drive_pio_post_load, - .needed = ide_drive_pio_state_needed, - .fields = (VMStateField[]) { - VMSTATE_INT32(req_nb_sectors, IDEState), - VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1, - vmstate_info_uint8, uint8_t), - VMSTATE_INT32(cur_io_buffer_offset, IDEState), - VMSTATE_INT32(cur_io_buffer_len, IDEState), - VMSTATE_UINT8(end_transfer_fn_idx, IDEState), - VMSTATE_INT32(elementary_transfer_size, IDEState), - VMSTATE_INT32(packet_transfer_size, IDEState), - VMSTATE_END_OF_LIST() - } -}; - -const VMStateDescription vmstate_ide_drive = { - .name = "ide_drive", - .version_id = 3, - .minimum_version_id = 0, - .post_load = ide_drive_post_load, - .fields = (VMStateField[]) { - VMSTATE_INT32(mult_sectors, IDEState), - VMSTATE_INT32(identify_set, IDEState), - VMSTATE_BUFFER_TEST(identify_data, IDEState, is_identify_set), - VMSTATE_UINT8(feature, IDEState), - VMSTATE_UINT8(error, IDEState), - VMSTATE_UINT32(nsector, IDEState), - VMSTATE_UINT8(sector, IDEState), - VMSTATE_UINT8(lcyl, IDEState), - VMSTATE_UINT8(hcyl, IDEState), - VMSTATE_UINT8(hob_feature, IDEState), - VMSTATE_UINT8(hob_sector, IDEState), - VMSTATE_UINT8(hob_nsector, IDEState), - VMSTATE_UINT8(hob_lcyl, IDEState), - VMSTATE_UINT8(hob_hcyl, IDEState), - VMSTATE_UINT8(select, IDEState), - VMSTATE_UINT8(status, IDEState), - VMSTATE_UINT8(lba48, IDEState), - VMSTATE_UINT8(sense_key, IDEState), - VMSTATE_UINT8(asc, IDEState), - VMSTATE_UINT8_V(cdrom_changed, IDEState, 3), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_ide_drive_pio_state, - &vmstate_ide_tray_state, - &vmstate_ide_atapi_gesn_state, - NULL - } -}; - -static const VMStateDescription vmstate_ide_error_status = { - .name ="ide_bus/error", - .version_id = 2, - .minimum_version_id = 1, - .needed = ide_error_needed, - .fields = (VMStateField[]) { - VMSTATE_INT32(error_status, IDEBus), - VMSTATE_INT64_V(retry_sector_num, IDEBus, 2), - VMSTATE_UINT32_V(retry_nsector, IDEBus, 2), - VMSTATE_UINT8_V(retry_unit, IDEBus, 2), - VMSTATE_END_OF_LIST() - } -}; - -const VMStateDescription vmstate_ide_bus = { - .name = "ide_bus", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(cmd, IDEBus), - VMSTATE_UINT8(unit, IDEBus), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_ide_error_status, - NULL - } -}; - -void ide_drive_get(DriveInfo **hd, int n) -{ - int i; - int highest_bus = drive_get_max_bus(IF_IDE) + 1; - int max_devs = drive_get_max_devs(IF_IDE); - int n_buses = max_devs ? (n / max_devs) : n; - - /* - * Note: The number of actual buses available is not known. - * We compute this based on the size of the DriveInfo* array, n. - * If it is less than max_devs * , - * We will stop looking for drives prematurely instead of overfilling - * the array. - */ - - if (highest_bus > n_buses) { - error_report("Too many IDE buses defined (%d > %d)", - highest_bus, n_buses); - exit(1); - } - - for (i = 0; i < n; i++) { - hd[i] = drive_get_by_index(IF_IDE, i); - } -} diff --git a/qemu/hw/ide/ich.c b/qemu/hw/ide/ich.c deleted file mode 100644 index 0a13334ba..000000000 --- a/qemu/hw/ide/ich.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * QEMU ICH Emulation - * - * Copyright (c) 2010 Sebastian Herbszt - * Copyright (c) 2010 Alexander Graf - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 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 . - * - * - * lspci dump of a ICH-9 real device - * - * 00:1f.2 SATA controller [0106]: Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA AHCI Controller [8086:2922] (rev 02) (prog-if 01 [AHCI 1.0]) - * Subsystem: Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA AHCI Controller [8086:2922] - * Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+ - * Status: Cap+ 66MHz+ UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- SERR- - * Capabilities: [b0] Vendor Specific Information - * Kernel driver in use: ahci - * Kernel modules: ahci - * 00: 86 80 22 29 07 04 b0 02 02 01 06 01 00 00 00 00 - * 10: 01 d0 00 00 01 cc 00 00 81 c8 00 00 01 c8 00 00 - * 20: 81 c4 00 00 00 90 bf fe 00 00 00 00 86 80 22 29 - * 30: 00 00 00 00 80 00 00 00 00 00 00 00 0f 02 00 00 - * 40: 00 80 00 80 00 00 00 00 00 00 00 00 00 00 00 00 - * 50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 70: 01 a8 03 40 08 00 00 00 00 00 00 00 00 00 00 00 - * 80: 05 70 09 00 0c f0 e0 fe d9 41 00 00 00 00 00 00 - * 90: 40 00 0f 82 93 01 00 00 00 00 00 00 00 00 00 00 - * a0: ac 00 00 00 0a 00 12 00 12 b0 10 00 48 00 00 00 - * b0: 09 00 06 20 00 00 00 00 00 00 00 00 00 00 00 00 - * c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * f0: 00 00 00 00 00 00 00 00 86 0f 02 00 00 00 00 00 - * - */ - -#include "qemu/osdep.h" -#include -#include -#include -#include -#include -#include "sysemu/block-backend.h" -#include "sysemu/dma.h" - -#include -#include - -#define ICH9_MSI_CAP_OFFSET 0x80 -#define ICH9_SATA_CAP_OFFSET 0xA8 - -#define ICH9_IDP_BAR 4 -#define ICH9_MEM_BAR 5 - -#define ICH9_IDP_INDEX 0x10 -#define ICH9_IDP_INDEX_LOG2 0x04 - -static const VMStateDescription vmstate_ich9_ahci = { - .name = "ich9_ahci", - .version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, AHCIPCIState), - VMSTATE_AHCI(ahci, AHCIPCIState), - VMSTATE_END_OF_LIST() - }, -}; - -static void pci_ich9_reset(DeviceState *dev) -{ - AHCIPCIState *d = ICH_AHCI(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; - int sata_cap_offset; - uint8_t *sata_cap; - d = ICH_AHCI(dev); - - ahci_realize(&d->ahci, DEVICE(dev), pci_get_address_space(dev), 6); - - pci_config_set_prog_interface(dev->config, AHCI_PROGMODE_MAJOR_REV_1); - - dev->config[PCI_CACHE_LINE_SIZE] = 0x08; /* Cache line size */ - dev->config[PCI_LATENCY_TIMER] = 0x00; /* Latency timer */ - pci_config_set_interrupt_pin(dev->config, 1); - - /* XXX Software should program this register */ - dev->config[0x90] = 1 << 6; /* Address Map Register - AHCI mode */ - - d->ahci.irq = pci_allocate_irq(dev); - - pci_register_bar(dev, ICH9_IDP_BAR, PCI_BASE_ADDRESS_SPACE_IO, - &d->ahci.idp); - pci_register_bar(dev, ICH9_MEM_BAR, PCI_BASE_ADDRESS_SPACE_MEMORY, - &d->ahci.mem); - - sata_cap_offset = pci_add_capability2(dev, PCI_CAP_ID_SATA, - ICH9_SATA_CAP_OFFSET, SATA_CAP_SIZE, - errp); - if (sata_cap_offset < 0) { - return; - } - - sata_cap = dev->config + sata_cap_offset; - pci_set_word(sata_cap + SATA_CAP_REV, 0x10); - pci_set_long(sata_cap + SATA_CAP_BAR, - (ICH9_IDP_BAR + 0x4) | (ICH9_IDP_INDEX_LOG2 << 4)); - d->ahci.idp_offset = ICH9_IDP_INDEX; - - /* Although the AHCI 1.3 specification states that the first capability - * should be PMCAP, the Intel ICH9 data sheet specifies that the ICH9 - * AHCI device puts the MSI capability first, pointing to 0x80. */ - msi_init(dev, ICH9_MSI_CAP_OFFSET, 1, true, false); -} - -static void pci_ich9_uninit(PCIDevice *dev) -{ - struct AHCIPCIState *d; - d = ICH_AHCI(dev); - - msi_uninit(dev); - ahci_uninit(&d->ahci); - qemu_free_irq(d->ahci.irq); -} - -static void ich_ahci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = pci_ich9_ahci_realize; - k->exit = pci_ich9_uninit; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82801IR; - k->revision = 0x02; - k->class_id = PCI_CLASS_STORAGE_SATA; - dc->vmsd = &vmstate_ich9_ahci; - dc->reset = pci_ich9_reset; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -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, -}; - -static void ich_ahci_register_types(void) -{ - type_register_static(&ich_ahci_info); -} - -type_init(ich_ahci_register_types) diff --git a/qemu/hw/ide/internal.h b/qemu/hw/ide/internal.h deleted file mode 100644 index d2c458f57..000000000 --- a/qemu/hw/ide/internal.h +++ /dev/null @@ -1,635 +0,0 @@ -#ifndef HW_IDE_INTERNAL_H -#define HW_IDE_INTERNAL_H - -/* - * QEMU IDE Emulation -- internal header file - * only files in hw/ide/ are supposed to include this file. - * non-internal declarations are in hw/ide.h - */ -#include -#include -#include "sysemu/dma.h" -#include "sysemu/sysemu.h" -#include "hw/block/block.h" -#include "block/scsi.h" - -/* debug IDE devices */ -//#define DEBUG_IDE -//#define DEBUG_IDE_ATAPI -//#define DEBUG_AIO -#define USE_DMA_CDROM - -typedef struct IDEBus IDEBus; -typedef struct IDEDevice IDEDevice; -typedef struct IDEState IDEState; -typedef struct IDEDMA IDEDMA; -typedef struct IDEDMAOps IDEDMAOps; - -#define TYPE_IDE_BUS "IDE" -#define IDE_BUS(obj) OBJECT_CHECK(IDEBus, (obj), TYPE_IDE_BUS) - -/* Bits of HD_STATUS */ -#define ERR_STAT 0x01 -#define INDEX_STAT 0x02 -#define ECC_STAT 0x04 /* Corrected error */ -#define DRQ_STAT 0x08 -#define SEEK_STAT 0x10 -#define SRV_STAT 0x10 -#define WRERR_STAT 0x20 -#define READY_STAT 0x40 -#define BUSY_STAT 0x80 - -/* Bits for HD_ERROR */ -#define MARK_ERR 0x01 /* Bad address mark */ -#define TRK0_ERR 0x02 /* couldn't find track 0 */ -#define ABRT_ERR 0x04 /* Command aborted */ -#define MCR_ERR 0x08 /* media change request */ -#define ID_ERR 0x10 /* ID field not found */ -#define MC_ERR 0x20 /* media changed */ -#define ECC_ERR 0x40 /* Uncorrectable ECC error */ -#define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */ -#define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */ - -/* Bits of HD_NSECTOR */ -#define CD 0x01 -#define IO 0x02 -#define REL 0x04 -#define TAG_MASK 0xf8 - -#define IDE_CMD_RESET 0x04 -#define IDE_CMD_DISABLE_IRQ 0x02 - -/* ACS-2 T13/2015-D Table B.2 Command codes */ -#define WIN_NOP 0x00 -/* reserved 0x01..0x02 */ -#define CFA_REQ_EXT_ERROR_CODE 0x03 /* CFA Request Extended Error Code */ -/* reserved 0x04..0x05 */ -#define WIN_DSM 0x06 -/* reserved 0x07 */ -#define WIN_DEVICE_RESET 0x08 -/* reserved 0x09..0x0a */ -/* REQUEST SENSE DATA EXT 0x0B */ -/* reserved 0x0C..0x0F */ -#define WIN_RECAL 0x10 /* obsolete since ATA4 */ -/* obsolete since ATA3, retired in ATA4 0x11..0x1F */ -#define WIN_READ 0x20 /* 28-Bit */ -#define WIN_READ_ONCE 0x21 /* 28-Bit w/o retries, obsolete since ATA5 */ -/* obsolete since ATA4 0x22..0x23 */ -#define WIN_READ_EXT 0x24 /* 48-Bit */ -#define WIN_READDMA_EXT 0x25 /* 48-Bit */ -#define WIN_READDMA_QUEUED_EXT 0x26 /* 48-Bit, obsolete since ACS2 */ -#define WIN_READ_NATIVE_MAX_EXT 0x27 /* 48-Bit */ -/* reserved 0x28 */ -#define WIN_MULTREAD_EXT 0x29 /* 48-Bit */ -/* READ STREAM DMA EXT 0x2A */ -/* READ STREAM EXT 0x2B */ -/* reserved 0x2C..0x2E */ -/* READ LOG EXT 0x2F */ -#define WIN_WRITE 0x30 /* 28-Bit */ -#define WIN_WRITE_ONCE 0x31 /* 28-Bit w/o retries, obsolete since ATA5 */ -/* obsolete since ATA4 0x32..0x33 */ -#define WIN_WRITE_EXT 0x34 /* 48-Bit */ -#define WIN_WRITEDMA_EXT 0x35 /* 48-Bit */ -#define WIN_WRITEDMA_QUEUED_EXT 0x36 /* 48-Bit */ -#define WIN_SET_MAX_EXT 0x37 /* 48-Bit, obsolete since ACS2 */ -#define WIN_SET_MAX_EXT 0x37 /* 48-Bit */ -#define CFA_WRITE_SECT_WO_ERASE 0x38 /* CFA Write Sectors without erase */ -#define WIN_MULTWRITE_EXT 0x39 /* 48-Bit */ -/* WRITE STREAM DMA EXT 0x3A */ -/* WRITE STREAM EXT 0x3B */ -#define WIN_WRITE_VERIFY 0x3C /* 28-Bit, obsolete since ATA4 */ -/* WRITE DMA FUA EXT 0x3D */ -/* obsolete since ACS2 0x3E */ -/* WRITE LOG EXT 0x3F */ -#define WIN_VERIFY 0x40 /* 28-Bit - Read Verify Sectors */ -#define WIN_VERIFY_ONCE 0x41 /* 28-Bit - w/o retries, obsolete since ATA5 */ -#define WIN_VERIFY_EXT 0x42 /* 48-Bit */ -/* reserved 0x43..0x44 */ -/* WRITE UNCORRECTABLE EXT 0x45 */ -/* reserved 0x46 */ -/* READ LOG DMA EXT 0x47 */ -/* reserved 0x48..0x4F */ -/* obsolete since ATA4 0x50 */ -/* CONFIGURE STREAM 0x51 */ -/* reserved 0x52..0x56 */ -/* WRITE LOG DMA EXT 0x57 */ -/* reserved 0x58..0x5A */ -/* TRUSTED NON DATA 0x5B */ -/* TRUSTED RECEIVE 0x5C */ -/* TRUSTED RECEIVE DMA 0x5D */ -/* TRUSTED SEND 0x5E */ -/* TRUSTED SEND DMA 0x5F */ -/* READ FPDMA QUEUED 0x60 */ -/* WRITE FPDMA QUEUED 0x61 */ -/* reserved 0x62->0x6F */ -#define WIN_SEEK 0x70 /* obsolete since ATA7 */ -/* reserved 0x71-0x7F */ -/* vendor specific 0x80-0x86 */ -#define CFA_TRANSLATE_SECTOR 0x87 /* CFA Translate Sector */ -/* vendor specific 0x88-0x8F */ -#define WIN_DIAGNOSE 0x90 -#define WIN_SPECIFY 0x91 /* set drive geometry translation, obsolete since ATA6 */ -#define WIN_DOWNLOAD_MICROCODE 0x92 -/* DOWNLOAD MICROCODE DMA 0x93 */ -#define WIN_STANDBYNOW2 0x94 /* retired in ATA4 */ -#define WIN_IDLEIMMEDIATE2 0x95 /* force drive to become "ready", retired in ATA4 */ -#define WIN_STANDBY2 0x96 /* retired in ATA4 */ -#define WIN_SETIDLE2 0x97 /* retired in ATA4 */ -#define WIN_CHECKPOWERMODE2 0x98 /* retired in ATA4 */ -#define WIN_SLEEPNOW2 0x99 /* retired in ATA4 */ -/* vendor specific 0x9A */ -/* reserved 0x9B..0x9F */ -#define WIN_PACKETCMD 0xA0 /* Send a packet command. */ -#define WIN_PIDENTIFY 0xA1 /* identify ATAPI device */ -#define WIN_QUEUED_SERVICE 0xA2 /* obsolete since ACS2 */ -/* reserved 0xA3..0xAF */ -#define WIN_SMART 0xB0 /* self-monitoring and reporting */ -/* Device Configuration Overlay 0xB1 */ -/* reserved 0xB2..0xB3 */ -/* Sanitize Device 0xB4 */ -/* reserved 0xB5 */ -/* NV Cache 0xB6 */ -/* reserved for CFA 0xB7..0xBB */ -#define CFA_ACCESS_METADATA_STORAGE 0xB8 -/* reserved 0xBC..0xBF */ -#define CFA_ERASE_SECTORS 0xC0 /* microdrives implement as NOP */ -/* vendor specific 0xC1..0xC3 */ -#define WIN_MULTREAD 0xC4 /* read sectors using multiple mode*/ -#define WIN_MULTWRITE 0xC5 /* write sectors using multiple mode */ -#define WIN_SETMULT 0xC6 /* enable/disable multiple mode */ -#define WIN_READDMA_QUEUED 0xC7 /* read sectors using Queued DMA transfers, obsolete since ACS2 */ -#define WIN_READDMA 0xC8 /* read sectors using DMA transfers */ -#define WIN_READDMA_ONCE 0xC9 /* 28-Bit - w/o retries, obsolete since ATA5 */ -#define WIN_WRITEDMA 0xCA /* write sectors using DMA transfers */ -#define WIN_WRITEDMA_ONCE 0xCB /* 28-Bit - w/o retries, obsolete since ATA5 */ -#define WIN_WRITEDMA_QUEUED 0xCC /* write sectors using Queued DMA transfers, obsolete since ACS2 */ -#define CFA_WRITE_MULTI_WO_ERASE 0xCD /* CFA Write multiple without erase */ -/* WRITE MULTIPLE FUA EXT 0xCE */ -/* reserved 0xCF..0xDO */ -/* CHECK MEDIA CARD TYPE 0xD1 */ -/* reserved for media card pass through 0xD2..0xD4 */ -/* reserved 0xD5..0xD9 */ -#define WIN_GETMEDIASTATUS 0xDA /* obsolete since ATA8 */ -/* obsolete since ATA3, retired in ATA4 0xDB..0xDD */ -#define WIN_DOORLOCK 0xDE /* lock door on removable drives, obsolete since ATA8 */ -#define WIN_DOORUNLOCK 0xDF /* unlock door on removable drives, obsolete since ATA8 */ -#define WIN_STANDBYNOW1 0xE0 -#define WIN_IDLEIMMEDIATE 0xE1 /* force drive to become "ready" */ -#define WIN_STANDBY 0xE2 /* Set device in Standby Mode */ -#define WIN_SETIDLE1 0xE3 -#define WIN_READ_BUFFER 0xE4 /* force read only 1 sector */ -#define WIN_CHECKPOWERMODE1 0xE5 -#define WIN_SLEEPNOW1 0xE6 -#define WIN_FLUSH_CACHE 0xE7 -#define WIN_WRITE_BUFFER 0xE8 /* force write only 1 sector */ -/* READ BUFFER DMA 0xE9 */ -#define WIN_FLUSH_CACHE_EXT 0xEA /* 48-Bit */ -/* WRITE BUFFER DMA 0xEB */ -#define WIN_IDENTIFY 0xEC /* ask drive to identify itself */ -#define WIN_MEDIAEJECT 0xED /* obsolete since ATA8 */ -/* obsolete since ATA4 0xEE */ -#define WIN_SETFEATURES 0xEF /* set special drive features */ -#define IBM_SENSE_CONDITION 0xF0 /* measure disk temperature, vendor specific */ -#define WIN_SECURITY_SET_PASS 0xF1 -#define WIN_SECURITY_UNLOCK 0xF2 -#define WIN_SECURITY_ERASE_PREPARE 0xF3 -#define WIN_SECURITY_ERASE_UNIT 0xF4 -#define WIN_SECURITY_FREEZE_LOCK 0xF5 -#define CFA_WEAR_LEVEL 0xF5 /* microdrives implement as NOP; not specified in T13! */ -#define WIN_SECURITY_DISABLE 0xF6 -/* vendor specific 0xF7 */ -#define WIN_READ_NATIVE_MAX 0xF8 /* return the native maximum address */ -#define WIN_SET_MAX 0xF9 -/* vendor specific 0xFA..0xFF */ - -/* set to 1 set disable mult support */ -#define MAX_MULT_SECTORS 16 - -#define IDE_DMA_BUF_SECTORS 256 - -/* feature values for Data Set Management */ -#define DSM_TRIM 0x01 - -#if (IDE_DMA_BUF_SECTORS < MAX_MULT_SECTORS) -#error "IDE_DMA_BUF_SECTORS must be bigger or equal to MAX_MULT_SECTORS" -#endif - -/* ATAPI defines */ - -#define ATAPI_PACKET_SIZE 12 - -/* The generic packet command opcodes for CD/DVD Logical Units, - * From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */ -#define GPCMD_BLANK 0xa1 -#define GPCMD_CLOSE_TRACK 0x5b -#define GPCMD_FLUSH_CACHE 0x35 -#define GPCMD_FORMAT_UNIT 0x04 -#define GPCMD_GET_CONFIGURATION 0x46 -#define GPCMD_GET_EVENT_STATUS_NOTIFICATION 0x4a -#define GPCMD_GET_PERFORMANCE 0xac -#define GPCMD_INQUIRY 0x12 -#define GPCMD_LOAD_UNLOAD 0xa6 -#define GPCMD_MECHANISM_STATUS 0xbd -#define GPCMD_MODE_SELECT_10 0x55 -#define GPCMD_MODE_SENSE_10 0x5a -#define GPCMD_PAUSE_RESUME 0x4b -#define GPCMD_PLAY_AUDIO_10 0x45 -#define GPCMD_PLAY_AUDIO_MSF 0x47 -#define GPCMD_PLAY_AUDIO_TI 0x48 -#define GPCMD_PLAY_CD 0xbc -#define GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e -#define GPCMD_READ_10 0x28 -#define GPCMD_READ_12 0xa8 -#define GPCMD_READ_CDVD_CAPACITY 0x25 -#define GPCMD_READ_CD 0xbe -#define GPCMD_READ_CD_MSF 0xb9 -#define GPCMD_READ_DISC_INFO 0x51 -#define GPCMD_READ_DVD_STRUCTURE 0xad -#define GPCMD_READ_FORMAT_CAPACITIES 0x23 -#define GPCMD_READ_HEADER 0x44 -#define GPCMD_READ_TRACK_RZONE_INFO 0x52 -#define GPCMD_READ_SUBCHANNEL 0x42 -#define GPCMD_READ_TOC_PMA_ATIP 0x43 -#define GPCMD_REPAIR_RZONE_TRACK 0x58 -#define GPCMD_REPORT_KEY 0xa4 -#define GPCMD_REQUEST_SENSE 0x03 -#define GPCMD_RESERVE_RZONE_TRACK 0x53 -#define GPCMD_SCAN 0xba -#define GPCMD_SEEK 0x2b -#define GPCMD_SEND_DVD_STRUCTURE 0xad -#define GPCMD_SEND_EVENT 0xa2 -#define GPCMD_SEND_KEY 0xa3 -#define GPCMD_SEND_OPC 0x54 -#define GPCMD_SET_READ_AHEAD 0xa7 -#define GPCMD_SET_STREAMING 0xb6 -#define GPCMD_START_STOP_UNIT 0x1b -#define GPCMD_STOP_PLAY_SCAN 0x4e -#define GPCMD_TEST_UNIT_READY 0x00 -#define GPCMD_VERIFY_10 0x2f -#define GPCMD_WRITE_10 0x2a -#define GPCMD_WRITE_AND_VERIFY_10 0x2e -/* This is listed as optional in ATAPI 2.6, but is (curiously) - * missing from Mt. Fuji, Table 57. It _is_ mentioned in Mt. Fuji - * Table 377 as an MMC command for SCSi devices though... Most ATAPI - * drives support it. */ -#define GPCMD_SET_SPEED 0xbb -/* This seems to be a SCSI specific CD-ROM opcode - * to play data at track/index */ -#define GPCMD_PLAYAUDIO_TI 0x48 -/* - * From MS Media Status Notification Support Specification. For - * older drives only. - */ -#define GPCMD_GET_MEDIA_STATUS 0xda -#define GPCMD_MODE_SENSE_6 0x1a - -#define ATAPI_INT_REASON_CD 0x01 /* 0 = data transfer */ -#define ATAPI_INT_REASON_IO 0x02 /* 1 = transfer to the host */ -#define ATAPI_INT_REASON_REL 0x04 -#define ATAPI_INT_REASON_TAG 0xf8 - -/* same constants as bochs */ -#define ASC_NO_SEEK_COMPLETE 0x02 -#define ASC_ILLEGAL_OPCODE 0x20 -#define ASC_LOGICAL_BLOCK_OOR 0x21 -#define ASC_INV_FIELD_IN_CMD_PACKET 0x24 -#define ASC_MEDIUM_MAY_HAVE_CHANGED 0x28 -#define ASC_INCOMPATIBLE_FORMAT 0x30 -#define ASC_MEDIUM_NOT_PRESENT 0x3a -#define ASC_SAVING_PARAMETERS_NOT_SUPPORTED 0x39 -#define ASC_DATA_PHASE_ERROR 0x4b -#define ASC_MEDIA_REMOVAL_PREVENTED 0x53 - -#define CFA_NO_ERROR 0x00 -#define CFA_MISC_ERROR 0x09 -#define CFA_INVALID_COMMAND 0x20 -#define CFA_INVALID_ADDRESS 0x21 -#define CFA_ADDRESS_OVERFLOW 0x2f - -#define SMART_READ_DATA 0xd0 -#define SMART_READ_THRESH 0xd1 -#define SMART_ATTR_AUTOSAVE 0xd2 -#define SMART_SAVE_ATTR 0xd3 -#define SMART_EXECUTE_OFFLINE 0xd4 -#define SMART_READ_LOG 0xd5 -#define SMART_WRITE_LOG 0xd6 -#define SMART_ENABLE 0xd8 -#define SMART_DISABLE 0xd9 -#define SMART_STATUS 0xda - -typedef enum { IDE_HD, IDE_CD, IDE_CFATA } IDEDriveKind; - -typedef void EndTransferFunc(IDEState *); - -typedef void DMAStartFunc(IDEDMA *, IDEState *, BlockCompletionFunc *); -typedef void DMAVoidFunc(IDEDMA *); -typedef int DMAIntFunc(IDEDMA *, int); -typedef int32_t DMAInt32Func(IDEDMA *, int32_t len); -typedef void DMAu32Func(IDEDMA *, uint32_t); -typedef void DMAStopFunc(IDEDMA *, bool); -typedef void DMARestartFunc(void *, int, RunState); - -struct unreported_events { - bool eject_request; - bool new_media; -}; - -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; - uint8_t unit; - /* ide config */ - IDEDriveKind drive_kind; - int cylinders, heads, sectors, chs_trans; - int64_t nb_sectors; - int mult_sectors; - int identify_set; - uint8_t identify_data[512]; - int drive_serial; - char drive_serial_str[21]; - char drive_model_str[41]; - uint64_t wwn; - /* ide regs */ - uint8_t feature; - uint8_t error; - uint32_t nsector; - uint8_t sector; - uint8_t lcyl; - uint8_t hcyl; - /* other part of tf for lba48 support */ - uint8_t hob_feature; - uint8_t hob_nsector; - uint8_t hob_sector; - uint8_t hob_lcyl; - uint8_t hob_hcyl; - - uint8_t select; - uint8_t status; - - /* set for lba48 access */ - uint8_t lba48; - BlockBackend *blk; - char version[9]; - /* ATAPI specific */ - struct unreported_events events; - uint8_t sense_key; - uint8_t asc; - bool tray_open; - bool tray_locked; - uint8_t cdrom_changed; - int packet_transfer_size; - int elementary_transfer_size; - int32_t io_buffer_index; - int lba; - int cd_sector_size; - int atapi_dma; /* true if dma is requested for the packet cmd */ - BlockAcctCookie acct; - BlockAIOCB *pio_aiocb; - struct iovec iov; - QEMUIOVector qiov; - QLIST_HEAD(, IDEBufferedRequest) buffered_requests; - /* ATA DMA state */ - uint64_t io_buffer_offset; - int32_t io_buffer_size; - QEMUSGList sg; - /* PIO transfer handling */ - int req_nb_sectors; /* number of sectors per interrupt */ - EndTransferFunc *end_transfer_func; - uint8_t *data_ptr; - uint8_t *data_end; - uint8_t *io_buffer; - /* PIO save/restore */ - int32_t io_buffer_total_len; - int32_t cur_io_buffer_offset; - int32_t cur_io_buffer_len; - uint8_t end_transfer_fn_idx; - QEMUTimer *sector_write_timer; /* only used for win2k install hack */ - uint32_t irq_count; /* counts IRQs when using win2k install hack */ - /* CF-ATA extended error */ - uint8_t ext_error; - /* CF-ATA metadata storage */ - uint32_t mdata_size; - uint8_t *mdata_storage; - int media_changed; - enum ide_dma_cmd dma_cmd; - /* SMART */ - uint8_t smart_enabled; - uint8_t smart_autosave; - int smart_errors; - uint8_t smart_selftest_count; - uint8_t *smart_selftest_data; - /* AHCI */ - int ncq_queues; -}; - -struct IDEDMAOps { - DMAStartFunc *start_dma; - DMAVoidFunc *start_transfer; - DMAInt32Func *prepare_buf; - DMAu32Func *commit_buf; - DMAIntFunc *rw_buf; - DMAVoidFunc *restart; - DMAVoidFunc *restart_dma; - DMAStopFunc *set_inactive; - DMAVoidFunc *cmd_done; - DMAVoidFunc *reset; -}; - -struct IDEDMA { - const struct IDEDMAOps *ops; - struct iovec iov; - QEMUIOVector qiov; - BlockAIOCB *aiocb; -}; - -struct IDEBus { - BusState qbus; - IDEDevice *master; - IDEDevice *slave; - IDEState ifs[2]; - QEMUBH *bh; - - int bus_id; - int max_units; - IDEDMA *dma; - uint8_t unit; - uint8_t cmd; - qemu_irq irq; - - int error_status; - uint8_t retry_unit; - int64_t retry_sector_num; - uint32_t retry_nsector; -}; - -#define TYPE_IDE_DEVICE "ide-device" -#define IDE_DEVICE(obj) \ - OBJECT_CHECK(IDEDevice, (obj), TYPE_IDE_DEVICE) -#define IDE_DEVICE_CLASS(klass) \ - OBJECT_CLASS_CHECK(IDEDeviceClass, (klass), TYPE_IDE_DEVICE) -#define IDE_DEVICE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(IDEDeviceClass, (obj), TYPE_IDE_DEVICE) - -typedef struct IDEDeviceClass { - DeviceClass parent_class; - int (*init)(IDEDevice *dev); -} IDEDeviceClass; - -struct IDEDevice { - DeviceState qdev; - uint32_t unit; - BlockConf conf; - int chs_trans; - char *version; - char *serial; - char *model; - uint64_t wwn; -}; - -/* 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; -} - -static inline void ide_set_irq(IDEBus *bus) -{ - if (!(bus->cmd & IDE_CMD_DISABLE_IRQ)) { - qemu_irq_raise(bus->irq); - } -} - -/* hw/ide/core.c */ -extern const VMStateDescription vmstate_ide_bus; - -#define VMSTATE_IDE_BUS(_field, _state) \ - VMSTATE_STRUCT(_field, _state, 1, vmstate_ide_bus, IDEBus) - -#define VMSTATE_IDE_BUS_ARRAY(_field, _state, _num) \ - VMSTATE_STRUCT_ARRAY(_field, _state, _num, 1, vmstate_ide_bus, IDEBus) - -extern const VMStateDescription vmstate_ide_drive; - -#define VMSTATE_IDE_DRIVES(_field, _state) \ - VMSTATE_STRUCT_ARRAY(_field, _state, 2, 3, vmstate_ide_drive, IDEState) - -#define VMSTATE_IDE_DRIVE(_field, _state) \ - VMSTATE_STRUCT(_field, _state, 1, vmstate_ide_drive, IDEState) - -void ide_bus_reset(IDEBus *bus); -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); -void ide_atapi_dma_restart(IDEState *s); -void ide_atapi_io_error(IDEState *s, int ret); - -void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val); -uint32_t ide_ioport_read(void *opaque, uint32_t addr1); -uint32_t ide_status_read(void *opaque, uint32_t addr); -void ide_cmd_write(void *opaque, uint32_t addr, uint32_t val); -void ide_data_writew(void *opaque, uint32_t addr, uint32_t val); -uint32_t ide_data_readw(void *opaque, uint32_t addr); -void ide_data_writel(void *opaque, uint32_t addr, uint32_t val); -uint32_t ide_data_readl(void *opaque, uint32_t addr); - -int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind, - const char *version, const char *serial, const char *model, - uint64_t wwn, - uint32_t cylinders, uint32_t heads, uint32_t secs, - int chs_trans); -void ide_init2(IDEBus *bus, qemu_irq irq); -void ide_init_ioport(IDEBus *bus, ISADevice *isa, int iobase, int iobase2); -void ide_register_restart_cb(IDEBus *bus); - -void ide_exec_cmd(IDEBus *bus, uint32_t val); - -void ide_transfer_start(IDEState *s, uint8_t *buf, int size, - EndTransferFunc *end_transfer_func); -void ide_transfer_stop(IDEState *s); -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); -void ide_atapi_cmd_reply_end(IDEState *s); - -/* hw/ide/qdev.c */ -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 deleted file mode 100644 index eba567c87..000000000 --- a/qemu/hw/ide/isa.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * QEMU IDE Emulation: ISA Bus support. - * - * Copyright (c) 2003 Fabrice Bellard - * Copyright (c) 2006 Openedhand Ltd. - * - * 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 -#include -#include -#include "sysemu/block-backend.h" -#include "sysemu/dma.h" - -#include - -/***********************************************************/ -/* ISA IDE definitions */ - -#define TYPE_ISA_IDE "isa-ide" -#define ISA_IDE(obj) OBJECT_CHECK(ISAIDEState, (obj), TYPE_ISA_IDE) - -typedef struct ISAIDEState { - ISADevice parent_obj; - - IDEBus bus; - uint32_t iobase; - uint32_t iobase2; - uint32_t isairq; - qemu_irq irq; -} ISAIDEState; - -static void isa_ide_reset(DeviceState *d) -{ - ISAIDEState *s = ISA_IDE(d); - - ide_bus_reset(&s->bus); -} - -static const VMStateDescription vmstate_ide_isa = { - .name = "isa-ide", - .version_id = 3, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_IDE_BUS(bus, ISAIDEState), - VMSTATE_IDE_DRIVES(bus.ifs, ISAIDEState), - VMSTATE_END_OF_LIST() - } -}; - -static void isa_ide_realizefn(DeviceState *dev, Error **errp) -{ - ISADevice *isadev = ISA_DEVICE(dev); - ISAIDEState *s = ISA_IDE(dev); - - ide_bus_new(&s->bus, sizeof(s->bus), dev, 0, 2); - ide_init_ioport(&s->bus, isadev, s->iobase, s->iobase2); - isa_init_irq(isadev, &s->irq, s->isairq); - ide_init2(&s->bus, s->irq); - vmstate_register(dev, 0, &vmstate_ide_isa, s); - ide_register_restart_cb(&s->bus); -} - -ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, - DriveInfo *hd0, DriveInfo *hd1) -{ - DeviceState *dev; - ISADevice *isadev; - ISAIDEState *s; - - isadev = isa_create(bus, TYPE_ISA_IDE); - dev = DEVICE(isadev); - qdev_prop_set_uint32(dev, "iobase", iobase); - qdev_prop_set_uint32(dev, "iobase2", iobase2); - qdev_prop_set_uint32(dev, "irq", isairq); - qdev_init_nofail(dev); - - s = ISA_IDE(dev); - if (hd0) { - ide_create_drive(&s->bus, 0, hd0); - } - if (hd1) { - ide_create_drive(&s->bus, 1, hd1); - } - return isadev; -} - -static Property isa_ide_properties[] = { - DEFINE_PROP_UINT32("iobase", ISAIDEState, iobase, 0x1f0), - DEFINE_PROP_UINT32("iobase2", ISAIDEState, iobase2, 0x3f6), - DEFINE_PROP_UINT32("irq", ISAIDEState, isairq, 14), - DEFINE_PROP_END_OF_LIST(), -}; - -static void isa_ide_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = isa_ide_realizefn; - dc->fw_name = "ide"; - dc->reset = isa_ide_reset; - dc->props = isa_ide_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo isa_ide_info = { - .name = TYPE_ISA_IDE, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(ISAIDEState), - .class_init = isa_ide_class_initfn, -}; - -static void isa_ide_register_types(void) -{ - type_register_static(&isa_ide_info); -} - -type_init(isa_ide_register_types) diff --git a/qemu/hw/ide/macio.c b/qemu/hw/ide/macio.c deleted file mode 100644 index 76256eb8a..000000000 --- a/qemu/hw/ide/macio.c +++ /dev/null @@ -1,642 +0,0 @@ -/* - * QEMU IDE Emulation: MacIO support. - * - * Copyright (c) 2003 Fabrice Bellard - * Copyright (c) 2006 Openedhand Ltd. - * - * 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/ppc/mac.h" -#include "hw/ppc/mac_dbdma.h" -#include "sysemu/block-backend.h" -#include "sysemu/dma.h" - -#include - -/* debug MACIO */ -// #define DEBUG_MACIO - -#ifdef DEBUG_MACIO -static const int debug_macio = 1; -#else -static const int debug_macio = 0; -#endif - -#define MACIO_DPRINTF(fmt, ...) do { \ - if (debug_macio) { \ - printf(fmt , ## __VA_ARGS__); \ - } \ - } while (0) - - -/***********************************************************/ -/* MacIO based PowerPC IDE */ - -#define MACIO_PAGE_SIZE 4096 - -/* - * Unaligned DMA read/write access functions required for OS X/Darwin which - * don't perform DMA transactions on sector boundaries. These functions are - * modelled on bdrv_co_do_preadv()/bdrv_co_do_pwritev() and so should be - * easy to remove if the unaligned block APIs are ever exposed. - */ - -static void pmac_dma_read(BlockBackend *blk, - int64_t offset, unsigned int bytes, - void (*cb)(void *opaque, int ret), void *opaque) -{ - DBDMA_io *io = opaque; - MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); - dma_addr_t dma_addr, dma_len; - void *mem; - int64_t sector_num; - int nsector; - uint64_t align = BDRV_SECTOR_SIZE; - size_t head_bytes, tail_bytes; - - qemu_iovec_destroy(&io->iov); - qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1); - - sector_num = (offset >> 9); - nsector = (io->len >> 9); - - MACIO_DPRINTF("--- DMA read transfer (0x%" HWADDR_PRIx ",0x%x): " - "sector_num: %" PRId64 ", nsector: %d\n", io->addr, io->len, - sector_num, nsector); - - dma_addr = io->addr; - dma_len = io->len; - mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len, - DMA_DIRECTION_FROM_DEVICE); - - if (offset & (align - 1)) { - head_bytes = offset & (align - 1); - - MACIO_DPRINTF("--- DMA unaligned head: sector %" PRId64 ", " - "discarding %zu bytes\n", sector_num, head_bytes); - - qemu_iovec_add(&io->iov, &io->head_remainder, head_bytes); - - bytes += offset & (align - 1); - offset = offset & ~(align - 1); - } - - qemu_iovec_add(&io->iov, mem, io->len); - - if ((offset + bytes) & (align - 1)) { - tail_bytes = (offset + bytes) & (align - 1); - - MACIO_DPRINTF("--- DMA unaligned tail: sector %" PRId64 ", " - "discarding bytes %zu\n", sector_num, tail_bytes); - - qemu_iovec_add(&io->iov, &io->tail_remainder, align - tail_bytes); - bytes = ROUND_UP(bytes, align); - } - - s->io_buffer_size -= io->len; - s->io_buffer_index += io->len; - - io->len = 0; - - MACIO_DPRINTF("--- Block read transfer - sector_num: %" PRIx64 " " - "nsector: %x\n", (offset >> 9), (bytes >> 9)); - - s->bus->dma->aiocb = blk_aio_readv(blk, (offset >> 9), &io->iov, - (bytes >> 9), cb, io); -} - -static void pmac_dma_write(BlockBackend *blk, - int64_t offset, int bytes, - void (*cb)(void *opaque, int ret), void *opaque) -{ - DBDMA_io *io = opaque; - MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); - dma_addr_t dma_addr, dma_len; - void *mem; - int64_t sector_num; - int nsector; - uint64_t align = BDRV_SECTOR_SIZE; - size_t head_bytes, tail_bytes; - bool unaligned_head = false, unaligned_tail = false; - - qemu_iovec_destroy(&io->iov); - qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1); - - sector_num = (offset >> 9); - nsector = (io->len >> 9); - - MACIO_DPRINTF("--- DMA write transfer (0x%" HWADDR_PRIx ",0x%x): " - "sector_num: %" PRId64 ", nsector: %d\n", io->addr, io->len, - sector_num, nsector); - - dma_addr = io->addr; - dma_len = io->len; - mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len, - DMA_DIRECTION_TO_DEVICE); - - if (offset & (align - 1)) { - head_bytes = offset & (align - 1); - sector_num = ((offset & ~(align - 1)) >> 9); - - MACIO_DPRINTF("--- DMA unaligned head: pre-reading head sector %" - PRId64 "\n", sector_num); - - blk_pread(s->blk, (sector_num << 9), &io->head_remainder, align); - - qemu_iovec_add(&io->iov, &io->head_remainder, head_bytes); - qemu_iovec_add(&io->iov, mem, io->len); - - bytes += offset & (align - 1); - offset = offset & ~(align - 1); - - unaligned_head = true; - } - - if ((offset + bytes) & (align - 1)) { - tail_bytes = (offset + bytes) & (align - 1); - sector_num = (((offset + bytes) & ~(align - 1)) >> 9); - - MACIO_DPRINTF("--- DMA unaligned tail: pre-reading tail sector %" - PRId64 "\n", sector_num); - - blk_pread(s->blk, (sector_num << 9), &io->tail_remainder, align); - - if (!unaligned_head) { - qemu_iovec_add(&io->iov, mem, io->len); - } - - qemu_iovec_add(&io->iov, &io->tail_remainder + tail_bytes, - align - tail_bytes); - - bytes = ROUND_UP(bytes, align); - - unaligned_tail = true; - } - - if (!unaligned_head && !unaligned_tail) { - qemu_iovec_add(&io->iov, mem, io->len); - } - - s->io_buffer_size -= io->len; - s->io_buffer_index += io->len; - - io->len = 0; - - MACIO_DPRINTF("--- Block write transfer - sector_num: %" PRIx64 " " - "nsector: %x\n", (offset >> 9), (bytes >> 9)); - - s->bus->dma->aiocb = blk_aio_writev(blk, (offset >> 9), &io->iov, - (bytes >> 9), cb, io); -} - -static void pmac_dma_trim(BlockBackend *blk, - int64_t offset, int bytes, - void (*cb)(void *opaque, int ret), void *opaque) -{ - DBDMA_io *io = opaque; - MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); - dma_addr_t dma_addr, dma_len; - void *mem; - - qemu_iovec_destroy(&io->iov); - qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1); - - dma_addr = io->addr; - dma_len = io->len; - mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len, - DMA_DIRECTION_TO_DEVICE); - - qemu_iovec_add(&io->iov, mem, io->len); - s->io_buffer_size -= io->len; - s->io_buffer_index += io->len; - io->len = 0; - - 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) -{ - DBDMA_io *io = opaque; - MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); - int64_t offset; - - MACIO_DPRINTF("pmac_ide_atapi_transfer_cb\n"); - - if (ret < 0) { - MACIO_DPRINTF("DMA error: %d\n", ret); - ide_atapi_io_error(s, ret); - goto done; - } - - if (!m->dma_active) { - MACIO_DPRINTF("waiting for data (%#x - %#x - %x)\n", - s->nsector, io->len, s->status); - /* data not ready yet, wait for the channel to get restarted */ - io->processing = false; - return; - } - - if (s->io_buffer_size <= 0) { - MACIO_DPRINTF("End of IDE transfer\n"); - ide_atapi_cmd_ok(s); - m->dma_active = false; - goto done; - } - - if (io->len == 0) { - MACIO_DPRINTF("End of DMA transfer\n"); - goto done; - } - - if (s->lba == -1) { - /* Non-block ATAPI transfer - just copy to RAM */ - s->io_buffer_size = MIN(s->io_buffer_size, io->len); - cpu_physical_memory_write(io->addr, s->io_buffer, s->io_buffer_size); - ide_atapi_cmd_ok(s); - m->dma_active = false; - goto done; - } - - /* Calculate current offset */ - 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: - 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); -} - -static void pmac_ide_transfer_cb(void *opaque, int ret) -{ - DBDMA_io *io = opaque; - MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); - int64_t offset; - - MACIO_DPRINTF("pmac_ide_transfer_cb\n"); - - if (ret < 0) { - MACIO_DPRINTF("DMA error: %d\n", ret); - ide_dma_error(s); - goto done; - } - - if (!m->dma_active) { - MACIO_DPRINTF("waiting for data (%#x - %#x - %x)\n", - s->nsector, io->len, s->status); - /* data not ready yet, wait for the channel to get restarted */ - io->processing = false; - return; - } - - if (s->io_buffer_size <= 0) { - MACIO_DPRINTF("End of IDE transfer\n"); - s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); - m->dma_active = false; - goto done; - } - - if (io->len == 0) { - MACIO_DPRINTF("End of DMA transfer\n"); - goto done; - } - - /* Calculate number of sectors */ - offset = (ide_get_sector(s) << 9) + s->io_buffer_index; - - switch (s->dma_cmd) { - case IDE_DMA_READ: - pmac_dma_read(s->blk, offset, io->len, pmac_ide_transfer_cb, io); - break; - case IDE_DMA_WRITE: - pmac_dma_write(s->blk, offset, io->len, pmac_ide_transfer_cb, io); - break; - 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) { - 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); -} - -static void pmac_ide_transfer(DBDMA_io *io) -{ - MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); - - MACIO_DPRINTF("\n"); - - if (s->drive_kind == IDE_CD) { - block_acct_start(blk_get_stats(s->blk), &s->acct, io->len, - BLOCK_ACCT_READ); - - pmac_ide_atapi_transfer_cb(io, 0); - return; - } - - switch (s->dma_cmd) { - case IDE_DMA_READ: - block_acct_start(blk_get_stats(s->blk), &s->acct, io->len, - BLOCK_ACCT_READ); - break; - case IDE_DMA_WRITE: - block_acct_start(blk_get_stats(s->blk), &s->acct, io->len, - BLOCK_ACCT_WRITE); - break; - default: - break; - } - - pmac_ide_transfer_cb(io, 0); -} - -static void pmac_ide_flush(DBDMA_io *io) -{ - MACIOIDEState *m = io->opaque; - IDEState *s = idebus_active_if(&m->bus); - - if (s->bus->dma->aiocb) { - blk_drain_all(); - } -} - -/* PowerMac IDE memory IO */ -static void pmac_ide_writeb (void *opaque, - hwaddr addr, uint32_t val) -{ - MACIOIDEState *d = opaque; - - addr = (addr & 0xFFF) >> 4; - switch (addr) { - case 1 ... 7: - ide_ioport_write(&d->bus, addr, val); - break; - case 8: - case 22: - ide_cmd_write(&d->bus, 0, val); - break; - default: - break; - } -} - -static uint32_t pmac_ide_readb (void *opaque,hwaddr addr) -{ - uint8_t retval; - MACIOIDEState *d = opaque; - - addr = (addr & 0xFFF) >> 4; - switch (addr) { - case 1 ... 7: - retval = ide_ioport_read(&d->bus, addr); - break; - case 8: - case 22: - retval = ide_status_read(&d->bus, 0); - break; - default: - retval = 0xFF; - break; - } - return retval; -} - -static void pmac_ide_writew (void *opaque, - hwaddr addr, uint32_t val) -{ - MACIOIDEState *d = opaque; - - addr = (addr & 0xFFF) >> 4; - val = bswap16(val); - if (addr == 0) { - ide_data_writew(&d->bus, 0, val); - } -} - -static uint32_t pmac_ide_readw (void *opaque,hwaddr addr) -{ - uint16_t retval; - MACIOIDEState *d = opaque; - - addr = (addr & 0xFFF) >> 4; - if (addr == 0) { - retval = ide_data_readw(&d->bus, 0); - } else { - retval = 0xFFFF; - } - retval = bswap16(retval); - return retval; -} - -static void pmac_ide_writel (void *opaque, - hwaddr addr, uint32_t val) -{ - MACIOIDEState *d = opaque; - - addr = (addr & 0xFFF) >> 4; - val = bswap32(val); - if (addr == 0) { - ide_data_writel(&d->bus, 0, val); - } -} - -static uint32_t pmac_ide_readl (void *opaque,hwaddr addr) -{ - uint32_t retval; - MACIOIDEState *d = opaque; - - addr = (addr & 0xFFF) >> 4; - if (addr == 0) { - retval = ide_data_readl(&d->bus, 0); - } else { - retval = 0xFFFFFFFF; - } - retval = bswap32(retval); - return retval; -} - -static const MemoryRegionOps pmac_ide_ops = { - .old_mmio = { - .write = { - pmac_ide_writeb, - pmac_ide_writew, - pmac_ide_writel, - }, - .read = { - pmac_ide_readb, - pmac_ide_readw, - pmac_ide_readl, - }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_pmac = { - .name = "ide", - .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() - } -}; - -static void macio_ide_reset(DeviceState *dev) -{ - MACIOIDEState *d = MACIO_IDE(dev); - - ide_bus_reset(&d->bus); -} - -static int ide_nop_int(IDEDMA *dma, int x) -{ - return 0; -} - -static int32_t ide_nop_int32(IDEDMA *dma, int32_t l) -{ - return 0; -} - -static void ide_dbdma_start(IDEDMA *dma, IDEState *s, - BlockCompletionFunc *cb) -{ - MACIOIDEState *m = container_of(dma, MACIOIDEState, dma); - - s->io_buffer_index = 0; - if (s->drive_kind == IDE_CD) { - s->io_buffer_size = s->packet_transfer_size; - } else { - s->io_buffer_size = s->nsector * BDRV_SECTOR_SIZE; - } - - MACIO_DPRINTF("\n\n------------ IDE transfer\n"); - MACIO_DPRINTF("buffer_size: %x buffer_index: %x\n", - s->io_buffer_size, s->io_buffer_index); - MACIO_DPRINTF("lba: %x size: %x\n", s->lba, s->io_buffer_size); - MACIO_DPRINTF("-------------------------\n"); - - m->dma_active = true; - DBDMA_kick(m->dbdma); -} - -static const IDEDMAOps dbdma_ops = { - .start_dma = ide_dbdma_start, - .prepare_buf = ide_nop_int32, - .rw_buf = ide_nop_int, -}; - -static void macio_ide_realizefn(DeviceState *dev, Error **errp) -{ - MACIOIDEState *s = MACIO_IDE(dev); - - ide_init2(&s->bus, s->irq); - - /* Register DMA callbacks */ - s->dma.ops = &dbdma_ops; - s->bus.dma = &s->dma; -} - -static void macio_ide_initfn(Object *obj) -{ - SysBusDevice *d = SYS_BUS_DEVICE(obj); - MACIOIDEState *s = MACIO_IDE(obj); - - ide_bus_new(&s->bus, sizeof(s->bus), DEVICE(obj), 0, 2); - memory_region_init_io(&s->mem, obj, &pmac_ide_ops, s, "pmac-ide", 0x1000); - sysbus_init_mmio(d, &s->mem); - sysbus_init_irq(d, &s->irq); - sysbus_init_irq(d, &s->dma_irq); -} - -static void macio_ide_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - 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 = { - .name = TYPE_MACIO_IDE, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MACIOIDEState), - .instance_init = macio_ide_initfn, - .class_init = macio_ide_class_init, -}; - -static void macio_ide_register_types(void) -{ - type_register_static(&macio_ide_type_info); -} - -/* hd_table must contain 2 block drivers */ -void macio_ide_init_drives(MACIOIDEState *s, DriveInfo **hd_table) -{ - int i; - - for (i = 0; i < 2; i++) { - if (hd_table[i]) { - ide_create_drive(&s->bus, i, hd_table[i]); - } - } -} - -void macio_ide_register_dma(MACIOIDEState *s, void *dbdma, int channel) -{ - s->dbdma = dbdma; - DBDMA_register_channel(dbdma, channel, s->dma_irq, - pmac_ide_transfer, pmac_ide_flush, s); -} - -type_init(macio_ide_register_types) diff --git a/qemu/hw/ide/microdrive.c b/qemu/hw/ide/microdrive.c deleted file mode 100644 index 5c9db8047..000000000 --- a/qemu/hw/ide/microdrive.c +++ /dev/null @@ -1,638 +0,0 @@ -/* - * QEMU IDE Emulation: microdrive (CF / PCMCIA) - * - * Copyright (c) 2003 Fabrice Bellard - * Copyright (c) 2006 Openedhand Ltd. - * - * 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 -#include -#include -#include "sysemu/block-backend.h" -#include "sysemu/dma.h" - -#include - -#define TYPE_MICRODRIVE "microdrive" -#define MICRODRIVE(obj) OBJECT_CHECK(MicroDriveState, (obj), TYPE_MICRODRIVE) - -/***********************************************************/ -/* CF-ATA Microdrive */ - -#define METADATA_SIZE 0x20 - -/* DSCM-1XXXX Microdrive hard disk with CF+ II / PCMCIA interface. */ - -typedef struct MicroDriveState { - /*< private >*/ - PCMCIACardState parent_obj; - /*< public >*/ - - IDEBus bus; - uint32_t attr_base; - uint32_t io_base; - - /* Card state */ - uint8_t opt; - uint8_t stat; - uint8_t pins; - - uint8_t ctrl; - uint16_t io; - uint8_t cycle; -} MicroDriveState; - -/* Register bitfields */ -enum md_opt { - OPT_MODE_MMAP = 0, - OPT_MODE_IOMAP16 = 1, - OPT_MODE_IOMAP1 = 2, - OPT_MODE_IOMAP2 = 3, - OPT_MODE = 0x3f, - OPT_LEVIREQ = 0x40, - OPT_SRESET = 0x80, -}; -enum md_cstat { - STAT_INT = 0x02, - STAT_PWRDWN = 0x04, - STAT_XE = 0x10, - STAT_IOIS8 = 0x20, - STAT_SIGCHG = 0x40, - STAT_CHANGED = 0x80, -}; -enum md_pins { - PINS_MRDY = 0x02, - PINS_CRDY = 0x20, -}; -enum md_ctrl { - CTRL_IEN = 0x02, - CTRL_SRST = 0x04, -}; - -static inline void md_interrupt_update(MicroDriveState *s) -{ - PCMCIACardState *card = PCMCIA_CARD(s); - - if (card->slot == NULL) { - return; - } - - qemu_set_irq(card->slot->irq, - !(s->stat & STAT_INT) && /* Inverted */ - !(s->ctrl & (CTRL_IEN | CTRL_SRST)) && - !(s->opt & OPT_SRESET)); -} - -static void md_set_irq(void *opaque, int irq, int level) -{ - MicroDriveState *s = opaque; - - if (level) { - s->stat |= STAT_INT; - } else { - s->stat &= ~STAT_INT; - } - - md_interrupt_update(s); -} - -static void md_reset(DeviceState *dev) -{ - MicroDriveState *s = MICRODRIVE(dev); - - s->opt = OPT_MODE_MMAP; - s->stat = 0; - s->pins = 0; - s->cycle = 0; - s->ctrl = 0; - ide_bus_reset(&s->bus); -} - -static uint8_t md_attr_read(PCMCIACardState *card, uint32_t at) -{ - MicroDriveState *s = MICRODRIVE(card); - PCMCIACardClass *pcc = PCMCIA_CARD_GET_CLASS(card); - - if (at < s->attr_base) { - if (at < pcc->cis_len) { - return pcc->cis[at]; - } else { - return 0x00; - } - } - - at -= s->attr_base; - - switch (at) { - case 0x00: /* Configuration Option Register */ - return s->opt; - case 0x02: /* Card Configuration Status Register */ - if (s->ctrl & CTRL_IEN) { - return s->stat & ~STAT_INT; - } else { - return s->stat; - } - case 0x04: /* Pin Replacement Register */ - return (s->pins & PINS_CRDY) | 0x0c; - case 0x06: /* Socket and Copy Register */ - return 0x00; -#ifdef VERBOSE - default: - printf("%s: Bad attribute space register %02x\n", __FUNCTION__, at); -#endif - } - - return 0; -} - -static void md_attr_write(PCMCIACardState *card, uint32_t at, uint8_t value) -{ - MicroDriveState *s = MICRODRIVE(card); - - at -= s->attr_base; - - switch (at) { - case 0x00: /* Configuration Option Register */ - s->opt = value & 0xcf; - if (value & OPT_SRESET) { - device_reset(DEVICE(s)); - } - md_interrupt_update(s); - break; - case 0x02: /* Card Configuration Status Register */ - if ((s->stat ^ value) & STAT_PWRDWN) { - s->pins |= PINS_CRDY; - } - s->stat &= 0x82; - s->stat |= value & 0x74; - md_interrupt_update(s); - /* Word 170 in Identify Device must be equal to STAT_XE */ - break; - case 0x04: /* Pin Replacement Register */ - s->pins &= PINS_CRDY; - s->pins |= value & PINS_MRDY; - break; - case 0x06: /* Socket and Copy Register */ - break; - default: - printf("%s: Bad attribute space register %02x\n", __FUNCTION__, at); - } -} - -static uint16_t md_common_read(PCMCIACardState *card, uint32_t at) -{ - MicroDriveState *s = MICRODRIVE(card); - IDEState *ifs; - uint16_t ret; - at -= s->io_base; - - switch (s->opt & OPT_MODE) { - case OPT_MODE_MMAP: - if ((at & ~0x3ff) == 0x400) { - at = 0; - } - break; - case OPT_MODE_IOMAP16: - at &= 0xf; - break; - case OPT_MODE_IOMAP1: - if ((at & ~0xf) == 0x3f0) { - at -= 0x3e8; - } else if ((at & ~0xf) == 0x1f0) { - at -= 0x1f0; - } - break; - case OPT_MODE_IOMAP2: - if ((at & ~0xf) == 0x370) { - at -= 0x368; - } else if ((at & ~0xf) == 0x170) { - at -= 0x170; - } - } - - switch (at) { - case 0x0: /* Even RD Data */ - case 0x8: - return ide_data_readw(&s->bus, 0); - - /* TODO: 8-bit accesses */ - if (s->cycle) { - ret = s->io >> 8; - } else { - s->io = ide_data_readw(&s->bus, 0); - ret = s->io & 0xff; - } - s->cycle = !s->cycle; - return ret; - case 0x9: /* Odd RD Data */ - return s->io >> 8; - case 0xd: /* Error */ - return ide_ioport_read(&s->bus, 0x1); - case 0xe: /* Alternate Status */ - ifs = idebus_active_if(&s->bus); - if (ifs->blk) { - return ifs->status; - } else { - return 0; - } - case 0xf: /* Device Address */ - ifs = idebus_active_if(&s->bus); - return 0xc2 | ((~ifs->select << 2) & 0x3c); - default: - return ide_ioport_read(&s->bus, at); - } - - return 0; -} - -static void md_common_write(PCMCIACardState *card, uint32_t at, uint16_t value) -{ - MicroDriveState *s = MICRODRIVE(card); - at -= s->io_base; - - switch (s->opt & OPT_MODE) { - case OPT_MODE_MMAP: - if ((at & ~0x3ff) == 0x400) { - at = 0; - } - break; - case OPT_MODE_IOMAP16: - at &= 0xf; - break; - case OPT_MODE_IOMAP1: - if ((at & ~0xf) == 0x3f0) { - at -= 0x3e8; - } else if ((at & ~0xf) == 0x1f0) { - at -= 0x1f0; - } - break; - case OPT_MODE_IOMAP2: - if ((at & ~0xf) == 0x370) { - at -= 0x368; - } else if ((at & ~0xf) == 0x170) { - at -= 0x170; - } - } - - switch (at) { - case 0x0: /* Even WR Data */ - case 0x8: - ide_data_writew(&s->bus, 0, value); - break; - - /* TODO: 8-bit accesses */ - if (s->cycle) { - ide_data_writew(&s->bus, 0, s->io | (value << 8)); - } else { - s->io = value & 0xff; - } - s->cycle = !s->cycle; - break; - case 0x9: - s->io = value & 0xff; - s->cycle = !s->cycle; - break; - case 0xd: /* Features */ - ide_ioport_write(&s->bus, 0x1, value); - break; - case 0xe: /* Device Control */ - s->ctrl = value; - if (value & CTRL_SRST) { - device_reset(DEVICE(s)); - } - md_interrupt_update(s); - break; - default: - if (s->stat & STAT_PWRDWN) { - s->pins |= PINS_CRDY; - s->stat &= ~STAT_PWRDWN; - } - ide_ioport_write(&s->bus, at, value); - } -} - -static const VMStateDescription vmstate_microdrive = { - .name = "microdrive", - .version_id = 3, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT8(opt, MicroDriveState), - VMSTATE_UINT8(stat, MicroDriveState), - VMSTATE_UINT8(pins, MicroDriveState), - VMSTATE_UINT8(ctrl, MicroDriveState), - VMSTATE_UINT16(io, MicroDriveState), - VMSTATE_UINT8(cycle, MicroDriveState), - VMSTATE_IDE_BUS(bus, MicroDriveState), - VMSTATE_IDE_DRIVES(bus.ifs, MicroDriveState), - VMSTATE_END_OF_LIST() - } -}; - -static const uint8_t dscm1xxxx_cis[0x14a] = { - [0x000] = CISTPL_DEVICE, /* 5V Device Information */ - [0x002] = 0x03, /* Tuple length = 4 bytes */ - [0x004] = 0xdb, /* ID: DTYPE_FUNCSPEC, non WP, DSPEED_150NS */ - [0x006] = 0x01, /* Size = 2K bytes */ - [0x008] = CISTPL_ENDMARK, - - [0x00a] = CISTPL_DEVICE_OC, /* Additional Device Information */ - [0x00c] = 0x04, /* Tuple length = 4 byest */ - [0x00e] = 0x03, /* Conditions: Ext = 0, Vcc 3.3V, MWAIT = 1 */ - [0x010] = 0xdb, /* ID: DTYPE_FUNCSPEC, non WP, DSPEED_150NS */ - [0x012] = 0x01, /* Size = 2K bytes */ - [0x014] = CISTPL_ENDMARK, - - [0x016] = CISTPL_JEDEC_C, /* JEDEC ID */ - [0x018] = 0x02, /* Tuple length = 2 bytes */ - [0x01a] = 0xdf, /* PC Card ATA with no Vpp required */ - [0x01c] = 0x01, - - [0x01e] = CISTPL_MANFID, /* Manufacture ID */ - [0x020] = 0x04, /* Tuple length = 4 bytes */ - [0x022] = 0xa4, /* TPLMID_MANF = 00a4 (IBM) */ - [0x024] = 0x00, - [0x026] = 0x00, /* PLMID_CARD = 0000 */ - [0x028] = 0x00, - - [0x02a] = CISTPL_VERS_1, /* Level 1 Version */ - [0x02c] = 0x12, /* Tuple length = 23 bytes */ - [0x02e] = 0x04, /* Major Version = JEIDA 4.2 / PCMCIA 2.1 */ - [0x030] = 0x01, /* Minor Version = 1 */ - [0x032] = 'I', - [0x034] = 'B', - [0x036] = 'M', - [0x038] = 0x00, - [0x03a] = 'm', - [0x03c] = 'i', - [0x03e] = 'c', - [0x040] = 'r', - [0x042] = 'o', - [0x044] = 'd', - [0x046] = 'r', - [0x048] = 'i', - [0x04a] = 'v', - [0x04c] = 'e', - [0x04e] = 0x00, - [0x050] = CISTPL_ENDMARK, - - [0x052] = CISTPL_FUNCID, /* Function ID */ - [0x054] = 0x02, /* Tuple length = 2 bytes */ - [0x056] = 0x04, /* TPLFID_FUNCTION = Fixed Disk */ - [0x058] = 0x01, /* TPLFID_SYSINIT: POST = 1, ROM = 0 */ - - [0x05a] = CISTPL_FUNCE, /* Function Extension */ - [0x05c] = 0x02, /* Tuple length = 2 bytes */ - [0x05e] = 0x01, /* TPLFE_TYPE = Disk Device Interface */ - [0x060] = 0x01, /* TPLFE_DATA = PC Card ATA Interface */ - - [0x062] = CISTPL_FUNCE, /* Function Extension */ - [0x064] = 0x03, /* Tuple length = 3 bytes */ - [0x066] = 0x02, /* TPLFE_TYPE = Basic PC Card ATA Interface */ - [0x068] = 0x08, /* TPLFE_DATA: Rotating, Unique, Single */ - [0x06a] = 0x0f, /* TPLFE_DATA: Sleep, Standby, Idle, Auto */ - - [0x06c] = CISTPL_CONFIG, /* Configuration */ - [0x06e] = 0x05, /* Tuple length = 5 bytes */ - [0x070] = 0x01, /* TPCC_RASZ = 2 bytes, TPCC_RMSZ = 1 byte */ - [0x072] = 0x07, /* TPCC_LAST = 7 */ - [0x074] = 0x00, /* TPCC_RADR = 0200 */ - [0x076] = 0x02, - [0x078] = 0x0f, /* TPCC_RMSK = 200, 202, 204, 206 */ - - [0x07a] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ - [0x07c] = 0x0b, /* Tuple length = 11 bytes */ - [0x07e] = 0xc0, /* TPCE_INDX = Memory Mode, Default, Iface */ - [0x080] = 0xc0, /* TPCE_IF = Memory, no BVDs, no WP, READY */ - [0x082] = 0xa1, /* TPCE_FS = Vcc only, no I/O, Memory, Misc */ - [0x084] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */ - [0x086] = 0x55, /* NomV: 5.0 V */ - [0x088] = 0x4d, /* MinV: 4.5 V */ - [0x08a] = 0x5d, /* MaxV: 5.5 V */ - [0x08c] = 0x4e, /* Peakl: 450 mA */ - [0x08e] = 0x08, /* TPCE_MS = 1 window, 1 byte, Host address */ - [0x090] = 0x00, /* Window descriptor: Window length = 0 */ - [0x092] = 0x20, /* TPCE_MI: support power down mode, RW */ - - [0x094] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ - [0x096] = 0x06, /* Tuple length = 6 bytes */ - [0x098] = 0x00, /* TPCE_INDX = Memory Mode, no Default */ - [0x09a] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */ - [0x09c] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */ - [0x09e] = 0xb5, /* NomV: 3.3 V */ - [0x0a0] = 0x1e, - [0x0a2] = 0x3e, /* Peakl: 350 mA */ - - [0x0a4] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ - [0x0a6] = 0x0d, /* Tuple length = 13 bytes */ - [0x0a8] = 0xc1, /* TPCE_INDX = I/O and Memory Mode, Default */ - [0x0aa] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */ - [0x0ac] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */ - [0x0ae] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */ - [0x0b0] = 0x55, /* NomV: 5.0 V */ - [0x0b2] = 0x4d, /* MinV: 4.5 V */ - [0x0b4] = 0x5d, /* MaxV: 5.5 V */ - [0x0b6] = 0x4e, /* Peakl: 450 mA */ - [0x0b8] = 0x64, /* TPCE_IO = 16-byte boundary, 16/8 accesses */ - [0x0ba] = 0xf0, /* TPCE_IR = MASK, Level, Pulse, Share */ - [0x0bc] = 0xff, /* IRQ0..IRQ7 supported */ - [0x0be] = 0xff, /* IRQ8..IRQ15 supported */ - [0x0c0] = 0x20, /* TPCE_MI = support power down mode */ - - [0x0c2] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ - [0x0c4] = 0x06, /* Tuple length = 6 bytes */ - [0x0c6] = 0x01, /* TPCE_INDX = I/O and Memory Mode */ - [0x0c8] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */ - [0x0ca] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */ - [0x0cc] = 0xb5, /* NomV: 3.3 V */ - [0x0ce] = 0x1e, - [0x0d0] = 0x3e, /* Peakl: 350 mA */ - - [0x0d2] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ - [0x0d4] = 0x12, /* Tuple length = 18 bytes */ - [0x0d6] = 0xc2, /* TPCE_INDX = I/O Primary Mode */ - [0x0d8] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */ - [0x0da] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */ - [0x0dc] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */ - [0x0de] = 0x55, /* NomV: 5.0 V */ - [0x0e0] = 0x4d, /* MinV: 4.5 V */ - [0x0e2] = 0x5d, /* MaxV: 5.5 V */ - [0x0e4] = 0x4e, /* Peakl: 450 mA */ - [0x0e6] = 0xea, /* TPCE_IO = 1K boundary, 16/8 access, Range */ - [0x0e8] = 0x61, /* Range: 2 fields, 2 bytes addr, 1 byte len */ - [0x0ea] = 0xf0, /* Field 1 address = 0x01f0 */ - [0x0ec] = 0x01, - [0x0ee] = 0x07, /* Address block length = 8 */ - [0x0f0] = 0xf6, /* Field 2 address = 0x03f6 */ - [0x0f2] = 0x03, - [0x0f4] = 0x01, /* Address block length = 2 */ - [0x0f6] = 0xee, /* TPCE_IR = IRQ E, Level, Pulse, Share */ - [0x0f8] = 0x20, /* TPCE_MI = support power down mode */ - - [0x0fa] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ - [0x0fc] = 0x06, /* Tuple length = 6 bytes */ - [0x0fe] = 0x02, /* TPCE_INDX = I/O Primary Mode, no Default */ - [0x100] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */ - [0x102] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */ - [0x104] = 0xb5, /* NomV: 3.3 V */ - [0x106] = 0x1e, - [0x108] = 0x3e, /* Peakl: 350 mA */ - - [0x10a] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ - [0x10c] = 0x12, /* Tuple length = 18 bytes */ - [0x10e] = 0xc3, /* TPCE_INDX = I/O Secondary Mode, Default */ - [0x110] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */ - [0x112] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */ - [0x114] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */ - [0x116] = 0x55, /* NomV: 5.0 V */ - [0x118] = 0x4d, /* MinV: 4.5 V */ - [0x11a] = 0x5d, /* MaxV: 5.5 V */ - [0x11c] = 0x4e, /* Peakl: 450 mA */ - [0x11e] = 0xea, /* TPCE_IO = 1K boundary, 16/8 access, Range */ - [0x120] = 0x61, /* Range: 2 fields, 2 byte addr, 1 byte len */ - [0x122] = 0x70, /* Field 1 address = 0x0170 */ - [0x124] = 0x01, - [0x126] = 0x07, /* Address block length = 8 */ - [0x128] = 0x76, /* Field 2 address = 0x0376 */ - [0x12a] = 0x03, - [0x12c] = 0x01, /* Address block length = 2 */ - [0x12e] = 0xee, /* TPCE_IR = IRQ E, Level, Pulse, Share */ - [0x130] = 0x20, /* TPCE_MI = support power down mode */ - - [0x132] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */ - [0x134] = 0x06, /* Tuple length = 6 bytes */ - [0x136] = 0x03, /* TPCE_INDX = I/O Secondary Mode */ - [0x138] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */ - [0x13a] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */ - [0x13c] = 0xb5, /* NomV: 3.3 V */ - [0x13e] = 0x1e, - [0x140] = 0x3e, /* Peakl: 350 mA */ - - [0x142] = CISTPL_NO_LINK, /* No Link */ - [0x144] = 0x00, /* Tuple length = 0 bytes */ - - [0x146] = CISTPL_END, /* Tuple End */ -}; - -#define TYPE_DSCM1XXXX "dscm1xxxx" - -static int dscm1xxxx_attach(PCMCIACardState *card) -{ - MicroDriveState *md = MICRODRIVE(card); - PCMCIACardClass *pcc = PCMCIA_CARD_GET_CLASS(card); - - md->attr_base = pcc->cis[0x74] | (pcc->cis[0x76] << 8); - md->io_base = 0x0; - - device_reset(DEVICE(md)); - md_interrupt_update(md); - - return 0; -} - -static int dscm1xxxx_detach(PCMCIACardState *card) -{ - MicroDriveState *md = MICRODRIVE(card); - - device_reset(DEVICE(md)); - return 0; -} - -PCMCIACardState *dscm1xxxx_init(DriveInfo *dinfo) -{ - MicroDriveState *md; - - md = MICRODRIVE(object_new(TYPE_DSCM1XXXX)); - qdev_init_nofail(DEVICE(md)); - - if (dinfo != NULL) { - ide_create_drive(&md->bus, 0, dinfo); - } - md->bus.ifs[0].drive_kind = IDE_CFATA; - md->bus.ifs[0].mdata_size = METADATA_SIZE; - md->bus.ifs[0].mdata_storage = g_malloc0(METADATA_SIZE); - - return PCMCIA_CARD(md); -} - -static void dscm1xxxx_class_init(ObjectClass *oc, void *data) -{ - PCMCIACardClass *pcc = PCMCIA_CARD_CLASS(oc); - - pcc->cis = dscm1xxxx_cis; - pcc->cis_len = sizeof(dscm1xxxx_cis); - - pcc->attach = dscm1xxxx_attach; - pcc->detach = dscm1xxxx_detach; -} - -static const TypeInfo dscm1xxxx_type_info = { - .name = TYPE_DSCM1XXXX, - .parent = TYPE_MICRODRIVE, - .class_init = dscm1xxxx_class_init, -}; - -static void microdrive_realize(DeviceState *dev, Error **errp) -{ - MicroDriveState *md = MICRODRIVE(dev); - - ide_init2(&md->bus, qemu_allocate_irq(md_set_irq, md, 0)); -} - -static void microdrive_init(Object *obj) -{ - MicroDriveState *md = MICRODRIVE(obj); - - ide_bus_new(&md->bus, sizeof(md->bus), DEVICE(obj), 0, 1); -} - -static void microdrive_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - PCMCIACardClass *pcc = PCMCIA_CARD_CLASS(oc); - - pcc->attr_read = md_attr_read; - pcc->attr_write = md_attr_write; - pcc->common_read = md_common_read; - pcc->common_write = md_common_write; - pcc->io_read = md_common_read; - pcc->io_write = md_common_write; - - dc->realize = microdrive_realize; - dc->reset = md_reset; - dc->vmsd = &vmstate_microdrive; -} - -static const TypeInfo microdrive_type_info = { - .name = TYPE_MICRODRIVE, - .parent = TYPE_PCMCIA_CARD, - .instance_size = sizeof(MicroDriveState), - .instance_init = microdrive_init, - .abstract = true, - .class_init = microdrive_class_init, -}; - -static void microdrive_register_types(void) -{ - type_register_static(µdrive_type_info); - type_register_static(&dscm1xxxx_type_info); -} - -type_init(microdrive_register_types) diff --git a/qemu/hw/ide/mmio.c b/qemu/hw/ide/mmio.c deleted file mode 100644 index 493f65a1d..000000000 --- a/qemu/hw/ide/mmio.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * QEMU IDE Emulation: mmio support (for embedded). - * - * Copyright (c) 2003 Fabrice Bellard - * Copyright (c) 2006 Openedhand Ltd. - * - * 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/sysbus.h" -#include "sysemu/block-backend.h" -#include "sysemu/dma.h" - -#include - -/***********************************************************/ -/* MMIO based ide port - * This emulates IDE device connected directly to the CPU bus without - * dedicated ide controller, which is often seen on embedded boards. - */ - -#define TYPE_MMIO_IDE "mmio-ide" -#define MMIO_IDE(obj) OBJECT_CHECK(MMIOState, (obj), TYPE_MMIO_IDE) - -typedef struct MMIOIDEState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - IDEBus bus; - - uint32_t shift; - qemu_irq irq; - MemoryRegion iomem1, iomem2; -} MMIOState; - -static void mmio_ide_reset(DeviceState *dev) -{ - MMIOState *s = MMIO_IDE(dev); - - ide_bus_reset(&s->bus); -} - -static uint64_t mmio_ide_read(void *opaque, hwaddr addr, - unsigned size) -{ - MMIOState *s = opaque; - addr >>= s->shift; - if (addr & 7) - return ide_ioport_read(&s->bus, addr); - else - return ide_data_readw(&s->bus, 0); -} - -static void mmio_ide_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - MMIOState *s = opaque; - addr >>= s->shift; - if (addr & 7) - ide_ioport_write(&s->bus, addr, val); - else - ide_data_writew(&s->bus, 0, val); -} - -static const MemoryRegionOps mmio_ide_ops = { - .read = mmio_ide_read, - .write = mmio_ide_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static uint64_t mmio_ide_status_read(void *opaque, hwaddr addr, - unsigned size) -{ - MMIOState *s= opaque; - return ide_status_read(&s->bus, 0); -} - -static void mmio_ide_cmd_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - MMIOState *s = opaque; - ide_cmd_write(&s->bus, 0, val); -} - -static const MemoryRegionOps mmio_ide_cs_ops = { - .read = mmio_ide_status_read, - .write = mmio_ide_cmd_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const VMStateDescription vmstate_ide_mmio = { - .name = "mmio-ide", - .version_id = 3, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_IDE_BUS(bus, MMIOState), - VMSTATE_IDE_DRIVES(bus.ifs, MMIOState), - VMSTATE_END_OF_LIST() - } -}; - -static void mmio_ide_realizefn(DeviceState *dev, Error **errp) -{ - SysBusDevice *d = SYS_BUS_DEVICE(dev); - MMIOState *s = MMIO_IDE(dev); - - ide_init2(&s->bus, s->irq); - - memory_region_init_io(&s->iomem1, OBJECT(s), &mmio_ide_ops, s, - "ide-mmio.1", 16 << s->shift); - memory_region_init_io(&s->iomem2, OBJECT(s), &mmio_ide_cs_ops, s, - "ide-mmio.2", 2 << s->shift); - sysbus_init_mmio(d, &s->iomem1); - sysbus_init_mmio(d, &s->iomem2); -} - -static void mmio_ide_initfn(Object *obj) -{ - SysBusDevice *d = SYS_BUS_DEVICE(obj); - MMIOState *s = MMIO_IDE(obj); - - ide_bus_new(&s->bus, sizeof(s->bus), DEVICE(obj), 0, 2); - sysbus_init_irq(d, &s->irq); -} - -static Property mmio_ide_properties[] = { - DEFINE_PROP_UINT32("shift", MMIOState, shift, 0), - DEFINE_PROP_END_OF_LIST() -}; - -static void mmio_ide_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = mmio_ide_realizefn; - dc->reset = mmio_ide_reset; - dc->props = mmio_ide_properties; - dc->vmsd = &vmstate_ide_mmio; -} - -static const TypeInfo mmio_ide_type_info = { - .name = TYPE_MMIO_IDE, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MMIOState), - .instance_init = mmio_ide_initfn, - .class_init = mmio_ide_class_init, -}; - -static void mmio_ide_register_types(void) -{ - type_register_static(&mmio_ide_type_info); -} - -void mmio_ide_init_drives(DeviceState *dev, DriveInfo *hd0, DriveInfo *hd1) -{ - MMIOState *s = MMIO_IDE(dev); - - if (hd0 != NULL) { - ide_create_drive(&s->bus, 0, hd0); - } - if (hd1 != NULL) { - ide_create_drive(&s->bus, 1, hd1); - } -} - -type_init(mmio_ide_register_types) diff --git a/qemu/hw/ide/pci.c b/qemu/hw/ide/pci.c deleted file mode 100644 index 8d56a00b1..000000000 --- a/qemu/hw/ide/pci.c +++ /dev/null @@ -1,468 +0,0 @@ -/* - * QEMU IDE Emulation: PCI Bus support. - * - * Copyright (c) 2003 Fabrice Bellard - * Copyright (c) 2006 Openedhand Ltd. - * - * 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 -#include -#include -#include -#include "sysemu/block-backend.h" -#include "sysemu/dma.h" -#include "qemu/error-report.h" -#include - -#define BMDMA_PAGE_SIZE 4096 - -#define BM_MIGRATION_COMPAT_STATUS_BITS \ - (IDE_RETRY_DMA | IDE_RETRY_PIO | \ - IDE_RETRY_READ | IDE_RETRY_FLUSH) - -static void bmdma_start_dma(IDEDMA *dma, IDEState *s, - BlockCompletionFunc *dma_cb) -{ - BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); - - bm->dma_cb = dma_cb; - bm->cur_prd_last = 0; - bm->cur_prd_addr = 0; - bm->cur_prd_len = 0; - - if (bm->status & BM_STATUS_DMAING) { - bm->dma_cb(bmdma_active_if(bm), 0); - } -} - -/** - * Prepare an sglist based on available PRDs. - * @limit: How many bytes to prepare total. - * - * Returns the number of bytes prepared, -1 on error. - * IDEState.io_buffer_size will contain the number of bytes described - * by the PRDs, whether or not we added them to the sglist. - */ -static int32_t bmdma_prepare_buf(IDEDMA *dma, int32_t limit) -{ - BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); - IDEState *s = bmdma_active_if(bm); - PCIDevice *pci_dev = PCI_DEVICE(bm->pci_dev); - struct { - uint32_t addr; - uint32_t size; - } prd; - int l, len; - - pci_dma_sglist_init(&s->sg, pci_dev, - s->nsector / (BMDMA_PAGE_SIZE / 512) + 1); - s->io_buffer_size = 0; - for(;;) { - if (bm->cur_prd_len == 0) { - /* end of table (with a fail safe of one page) */ - if (bm->cur_prd_last || - (bm->cur_addr - bm->addr) >= BMDMA_PAGE_SIZE) { - return s->sg.size; - } - pci_dma_read(pci_dev, bm->cur_addr, &prd, 8); - bm->cur_addr += 8; - prd.addr = le32_to_cpu(prd.addr); - prd.size = le32_to_cpu(prd.size); - len = prd.size & 0xfffe; - if (len == 0) - len = 0x10000; - bm->cur_prd_len = len; - bm->cur_prd_addr = prd.addr; - bm->cur_prd_last = (prd.size & 0x80000000); - } - l = bm->cur_prd_len; - if (l > 0) { - uint64_t sg_len; - - /* Don't add extra bytes to the SGList; consume any remaining - * PRDs from the guest, but ignore them. */ - sg_len = MIN(limit - s->sg.size, bm->cur_prd_len); - if (sg_len) { - qemu_sglist_add(&s->sg, bm->cur_prd_addr, sg_len); - } - - bm->cur_prd_addr += l; - bm->cur_prd_len -= l; - s->io_buffer_size += l; - } - } - - qemu_sglist_destroy(&s->sg); - s->io_buffer_size = 0; - return -1; -} - -/* return 0 if buffer completed */ -static int bmdma_rw_buf(IDEDMA *dma, int is_write) -{ - BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); - IDEState *s = bmdma_active_if(bm); - PCIDevice *pci_dev = PCI_DEVICE(bm->pci_dev); - struct { - uint32_t addr; - uint32_t size; - } prd; - int l, len; - - for(;;) { - l = s->io_buffer_size - s->io_buffer_index; - if (l <= 0) - break; - if (bm->cur_prd_len == 0) { - /* end of table (with a fail safe of one page) */ - if (bm->cur_prd_last || - (bm->cur_addr - bm->addr) >= BMDMA_PAGE_SIZE) - return 0; - pci_dma_read(pci_dev, bm->cur_addr, &prd, 8); - bm->cur_addr += 8; - prd.addr = le32_to_cpu(prd.addr); - prd.size = le32_to_cpu(prd.size); - len = prd.size & 0xfffe; - if (len == 0) - len = 0x10000; - bm->cur_prd_len = len; - bm->cur_prd_addr = prd.addr; - bm->cur_prd_last = (prd.size & 0x80000000); - } - if (l > bm->cur_prd_len) - l = bm->cur_prd_len; - if (l > 0) { - if (is_write) { - pci_dma_write(pci_dev, bm->cur_prd_addr, - s->io_buffer + s->io_buffer_index, l); - } else { - pci_dma_read(pci_dev, bm->cur_prd_addr, - s->io_buffer + s->io_buffer_index, l); - } - bm->cur_prd_addr += l; - bm->cur_prd_len -= l; - s->io_buffer_index += l; - } - } - return 1; -} - -static void bmdma_set_inactive(IDEDMA *dma, bool more) -{ - BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); - - bm->dma_cb = NULL; - if (more) { - bm->status |= BM_STATUS_DMAING; - } else { - bm->status &= ~BM_STATUS_DMAING; - } -} - -static void bmdma_restart_dma(IDEDMA *dma) -{ - BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); - - bm->cur_addr = bm->addr; -} - -static void bmdma_cancel(BMDMAState *bm) -{ - if (bm->status & BM_STATUS_DMAING) { - /* cancel DMA request */ - bmdma_set_inactive(&bm->dma, false); - } -} - -static void bmdma_reset(IDEDMA *dma) -{ - BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); - -#ifdef DEBUG_IDE - printf("ide: dma_reset\n"); -#endif - bmdma_cancel(bm); - bm->cmd = 0; - bm->status = 0; - bm->addr = 0; - bm->cur_addr = 0; - bm->cur_prd_last = 0; - bm->cur_prd_addr = 0; - bm->cur_prd_len = 0; -} - -static void bmdma_irq(void *opaque, int n, int level) -{ - BMDMAState *bm = opaque; - - if (!level) { - /* pass through lower */ - qemu_set_irq(bm->irq, level); - return; - } - - bm->status |= BM_STATUS_INT; - - /* trigger the real irq */ - qemu_set_irq(bm->irq, level); -} - -void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val) -{ -#ifdef DEBUG_IDE - printf("%s: 0x%08x\n", __func__, val); -#endif - - /* 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)) { - ide_cancel_dma_sync(idebus_active_if(bm->bus)); - bm->status &= ~BM_STATUS_DMAING; - } else { - bm->cur_addr = bm->addr; - if (!(bm->status & BM_STATUS_DMAING)) { - bm->status |= BM_STATUS_DMAING; - /* start dma transfer if possible */ - if (bm->dma_cb) - bm->dma_cb(bmdma_active_if(bm), 0); - } - } - } - - bm->cmd = val & 0x09; -} - -static uint64_t bmdma_addr_read(void *opaque, hwaddr addr, - unsigned width) -{ - BMDMAState *bm = opaque; - uint32_t mask = (1ULL << (width * 8)) - 1; - uint64_t data; - - data = (bm->addr >> (addr * 8)) & mask; -#ifdef DEBUG_IDE - printf("%s: 0x%08x\n", __func__, (unsigned)data); -#endif - return data; -} - -static void bmdma_addr_write(void *opaque, hwaddr addr, - uint64_t data, unsigned width) -{ - BMDMAState *bm = opaque; - int shift = addr * 8; - uint32_t mask = (1ULL << (width * 8)) - 1; - -#ifdef DEBUG_IDE - printf("%s: 0x%08x\n", __func__, (unsigned)data); -#endif - bm->addr &= ~(mask << shift); - bm->addr |= ((data & mask) << shift) & ~3; -} - -MemoryRegionOps bmdma_addr_ioport_ops = { - .read = bmdma_addr_read, - .write = bmdma_addr_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static bool ide_bmdma_current_needed(void *opaque) -{ - BMDMAState *bm = opaque; - - return (bm->cur_prd_len != 0); -} - -static bool ide_bmdma_status_needed(void *opaque) -{ - BMDMAState *bm = opaque; - - /* Older versions abused some bits in the status register for internal - * error state. If any of these bits are set, we must add a subsection to - * transfer the real status register */ - uint8_t abused_bits = BM_MIGRATION_COMPAT_STATUS_BITS; - - return ((bm->status & abused_bits) != 0); -} - -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; - bm->migration_compat_status = - (bm->status & ~abused_bits) | (bm->bus->error_status & abused_bits); -} - -/* This function accesses bm->bus->error_status which is loaded only after - * BMDMA itself. This is why the function is called from ide_pci_post_load - * instead of being registered with VMState where it would run too early. */ -static int ide_bmdma_post_load(void *opaque, int version_id) -{ - BMDMAState *bm = opaque; - uint8_t abused_bits = BM_MIGRATION_COMPAT_STATUS_BITS; - - if (bm->status == 0) { - bm->status = bm->migration_compat_status & ~abused_bits; - bm->bus->error_status |= bm->migration_compat_status & abused_bits; - } - if (bm->bus->error_status) { - bm->bus->retry_sector_num = bm->migration_retry_sector_num; - bm->bus->retry_nsector = bm->migration_retry_nsector; - bm->bus->retry_unit = bm->migration_retry_unit; - } - - return 0; -} - -static const VMStateDescription vmstate_bmdma_current = { - .name = "ide bmdma_current", - .version_id = 1, - .minimum_version_id = 1, - .needed = ide_bmdma_current_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT32(cur_addr, BMDMAState), - VMSTATE_UINT32(cur_prd_last, BMDMAState), - VMSTATE_UINT32(cur_prd_addr, BMDMAState), - VMSTATE_UINT32(cur_prd_len, BMDMAState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_bmdma_status = { - .name ="ide bmdma/status", - .version_id = 1, - .minimum_version_id = 1, - .needed = ide_bmdma_status_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(status, BMDMAState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_bmdma = { - .name = "ide bmdma", - .version_id = 3, - .minimum_version_id = 0, - .pre_save = ide_bmdma_pre_save, - .fields = (VMStateField[]) { - VMSTATE_UINT8(cmd, BMDMAState), - VMSTATE_UINT8(migration_compat_status, BMDMAState), - VMSTATE_UINT32(addr, BMDMAState), - VMSTATE_INT64(migration_retry_sector_num, BMDMAState), - VMSTATE_UINT32(migration_retry_nsector, BMDMAState), - VMSTATE_UINT8(migration_retry_unit, BMDMAState), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_bmdma_current, - &vmstate_bmdma_status, - NULL - } -}; - -static int ide_pci_post_load(void *opaque, int version_id) -{ - PCIIDEState *d = opaque; - int i; - - for(i = 0; i < 2; i++) { - /* current versions always store 0/1, but older version - stored bigger values. We only need last bit */ - d->bmdma[i].migration_retry_unit &= 1; - ide_bmdma_post_load(&d->bmdma[i], -1); - } - - return 0; -} - -const VMStateDescription vmstate_ide_pci = { - .name = "ide", - .version_id = 3, - .minimum_version_id = 0, - .post_load = ide_pci_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, PCIIDEState), - VMSTATE_STRUCT_ARRAY(bmdma, PCIIDEState, 2, 0, - vmstate_bmdma, BMDMAState), - VMSTATE_IDE_BUS_ARRAY(bus, PCIIDEState, 2), - VMSTATE_IDE_DRIVES(bus[0].ifs, PCIIDEState), - VMSTATE_IDE_DRIVES(bus[1].ifs, PCIIDEState), - VMSTATE_END_OF_LIST() - } -}; - -void pci_ide_create_devs(PCIDevice *dev, DriveInfo **hd_table) -{ - PCIIDEState *d = PCI_IDE(dev); - static const int bus[4] = { 0, 0, 1, 1 }; - static const int unit[4] = { 0, 1, 0, 1 }; - int i; - - for (i = 0; i < 4; i++) { - if (hd_table[i] == NULL) - continue; - ide_create_drive(d->bus+bus[i], unit[i], hd_table[i]); - } -} - -static const struct IDEDMAOps bmdma_ops = { - .start_dma = bmdma_start_dma, - .prepare_buf = bmdma_prepare_buf, - .rw_buf = bmdma_rw_buf, - .restart_dma = bmdma_restart_dma, - .set_inactive = bmdma_set_inactive, - .reset = bmdma_reset, -}; - -void bmdma_init(IDEBus *bus, BMDMAState *bm, PCIIDEState *d) -{ - if (bus->dma == &bm->dma) { - return; - } - - bm->dma.ops = &bmdma_ops; - bus->dma = &bm->dma; - bm->irq = bus->irq; - bus->irq = qemu_allocate_irq(bmdma_irq, bm, 0); - bm->pci_dev = d; -} - -static const TypeInfo pci_ide_type_info = { - .name = TYPE_PCI_IDE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIIDEState), - .abstract = true, -}; - -static void pci_ide_register_types(void) -{ - type_register_static(&pci_ide_type_info); -} - -type_init(pci_ide_register_types) diff --git a/qemu/hw/ide/pci.h b/qemu/hw/ide/pci.h deleted file mode 100644 index 0f2d4b91a..000000000 --- a/qemu/hw/ide/pci.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef HW_IDE_PCI_H -#define HW_IDE_PCI_H - -#include - -#define BM_STATUS_DMAING 0x01 -#define BM_STATUS_ERROR 0x02 -#define BM_STATUS_INT 0x04 - -#define BM_CMD_START 0x01 -#define BM_CMD_READ 0x08 - -typedef struct BMDMAState { - IDEDMA dma; - uint8_t cmd; - uint8_t status; - uint32_t addr; - - IDEBus *bus; - /* current transfer state */ - uint32_t cur_addr; - uint32_t cur_prd_last; - uint32_t cur_prd_addr; - uint32_t cur_prd_len; - BlockCompletionFunc *dma_cb; - MemoryRegion addr_ioport; - MemoryRegion extra_io; - qemu_irq irq; - - /* Bit 0-2 and 7: BM status register - * Bit 3-6: bus->error_status */ - uint8_t migration_compat_status; - uint8_t migration_retry_unit; - int64_t migration_retry_sector_num; - uint32_t migration_retry_nsector; - - struct PCIIDEState *pci_dev; -} BMDMAState; - -typedef struct CMD646BAR { - MemoryRegion cmd; - MemoryRegion data; - IDEBus *bus; - struct PCIIDEState *pci_dev; -} CMD646BAR; - -#define TYPE_PCI_IDE "pci-ide" -#define PCI_IDE(obj) OBJECT_CHECK(PCIIDEState, (obj), TYPE_PCI_IDE) - -typedef struct PCIIDEState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - IDEBus bus[2]; - BMDMAState bmdma[2]; - uint32_t secondary; /* used only for cmd646 */ - MemoryRegion bmdma_bar; - CMD646BAR cmd646_bar[2]; /* used only for cmd646 */ -} PCIIDEState; - - -static inline IDEState *bmdma_active_if(BMDMAState *bmdma) -{ - assert(bmdma->bus->retry_unit != (uint8_t)-1); - return bmdma->bus->ifs + bmdma->bus->retry_unit; -} - - -void bmdma_init(IDEBus *bus, BMDMAState *bm, PCIIDEState *d); -void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val); -extern MemoryRegionOps bmdma_addr_ioport_ops; -void pci_ide_create_devs(PCIDevice *dev, DriveInfo **hd_table); - -extern const VMStateDescription vmstate_ide_pci; -#endif diff --git a/qemu/hw/ide/piix.c b/qemu/hw/ide/piix.c deleted file mode 100644 index 6d76ce980..000000000 --- a/qemu/hw/ide/piix.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * QEMU IDE Emulation: PCI PIIX3/4 support. - * - * Copyright (c) 2003 Fabrice Bellard - * Copyright (c) 2006 Openedhand Ltd. - * - * 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 -#include -#include -#include -#include "sysemu/block-backend.h" -#include "sysemu/sysemu.h" -#include "sysemu/dma.h" - -#include - -static uint64_t bmdma_read(void *opaque, hwaddr addr, unsigned size) -{ - BMDMAState *bm = opaque; - uint32_t val; - - if (size != 1) { - return ((uint64_t)1 << (size * 8)) - 1; - } - - switch(addr & 3) { - case 0: - val = bm->cmd; - break; - case 2: - val = bm->status; - break; - default: - val = 0xff; - break; - } -#ifdef DEBUG_IDE - printf("bmdma: readb 0x%02x : 0x%02x\n", (uint8_t)addr, val); -#endif - return val; -} - -static void bmdma_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - BMDMAState *bm = opaque; - - if (size != 1) { - return; - } - -#ifdef DEBUG_IDE - printf("bmdma: writeb 0x%02x : 0x%02x\n", (uint8_t)addr, (uint8_t)val); -#endif - switch(addr & 3) { - case 0: - bmdma_cmd_writeb(bm, val); - break; - case 2: - bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06); - break; - } -} - -static const MemoryRegionOps piix_bmdma_ops = { - .read = bmdma_read, - .write = bmdma_write, -}; - -static void bmdma_setup_bar(PCIIDEState *d) -{ - int i; - - memory_region_init(&d->bmdma_bar, OBJECT(d), "piix-bmdma-container", 16); - for(i = 0;i < 2; i++) { - BMDMAState *bm = &d->bmdma[i]; - - memory_region_init_io(&bm->extra_io, OBJECT(d), &piix_bmdma_ops, bm, - "piix-bmdma", 4); - memory_region_add_subregion(&d->bmdma_bar, i * 8, &bm->extra_io); - memory_region_init_io(&bm->addr_ioport, OBJECT(d), - &bmdma_addr_ioport_ops, bm, "bmdma", 4); - memory_region_add_subregion(&d->bmdma_bar, i * 8 + 4, &bm->addr_ioport); - } -} - -static void piix3_reset(void *opaque) -{ - PCIIDEState *d = opaque; - PCIDevice *pd = PCI_DEVICE(d); - uint8_t *pci_conf = pd->config; - int i; - - for (i = 0; i < 2; i++) { - ide_bus_reset(&d->bus[i]); - } - - /* TODO: this is the default. do not override. */ - pci_conf[PCI_COMMAND] = 0x00; - /* TODO: this is the default. do not override. */ - pci_conf[PCI_COMMAND + 1] = 0x00; - /* TODO: use pci_set_word */ - pci_conf[PCI_STATUS] = PCI_STATUS_FAST_BACK; - pci_conf[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_MEDIUM >> 8; - pci_conf[0x20] = 0x01; /* BMIBA: 20-23h */ -} - -static void pci_piix_init_ports(PCIIDEState *d) { - static const struct { - int iobase; - int iobase2; - int isairq; - } port_info[] = { - {0x1f0, 0x3f6, 14}, - {0x170, 0x376, 15}, - }; - int i; - - for (i = 0; i < 2; i++) { - ide_bus_new(&d->bus[i], sizeof(d->bus[i]), DEVICE(d), i, 2); - ide_init_ioport(&d->bus[i], NULL, port_info[i].iobase, - port_info[i].iobase2); - ide_init2(&d->bus[i], isa_get_irq(NULL, port_info[i].isairq)); - - bmdma_init(&d->bus[i], &d->bmdma[i], d); - d->bmdma[i].bus = &d->bus[i]; - ide_register_restart_cb(&d->bus[i]); - } -} - -static void pci_piix_ide_realize(PCIDevice *dev, Error **errp) -{ - PCIIDEState *d = PCI_IDE(dev); - uint8_t *pci_conf = dev->config; - - pci_conf[PCI_CLASS_PROG] = 0x80; // legacy ATA mode - - qemu_register_reset(piix3_reset, d); - - bmdma_setup_bar(d); - pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar); - - vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d); - - pci_piix_init_ports(d); -} - -int pci_piix3_xen_ide_unplug(DeviceState *dev) -{ - PCIIDEState *pci_ide; - DriveInfo *di; - int i; - IDEDevice *idedev; - - pci_ide = PCI_IDE(dev); - - for (i = 0; i < 4; i++) { - di = drive_get_by_index(IF_IDE, i); - if (di != NULL && !di->media_cd) { - BlockBackend *blk = blk_by_legacy_dinfo(di); - DeviceState *ds = blk_get_attached_dev(blk); - if (ds) { - blk_detach_dev(blk, ds); - } - pci_ide->bus[di->bus].ifs[di->unit].blk = NULL; - if (!(i % 2)) { - idedev = pci_ide->bus[di->bus].master; - } else { - idedev = pci_ide->bus[di->bus].slave; - } - idedev->conf.blk = NULL; - monitor_remove_blk(blk); - blk_unref(blk); - } - } - qdev_reset_all(DEVICE(dev)); - return 0; -} - -PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -{ - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "piix3-ide-xen"); - pci_ide_create_devs(dev, hd_table); - return dev; -} - -static void pci_piix_ide_exitfn(PCIDevice *dev) -{ - PCIIDEState *d = PCI_IDE(dev); - unsigned i; - - for (i = 0; i < 2; ++i) { - memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].extra_io); - memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].addr_ioport); - } -} - -/* hd_table must contain 4 block drivers */ -/* NOTE: for the PIIX3, the IRQs and IOports are hardcoded */ -PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -{ - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "piix3-ide"); - pci_ide_create_devs(dev, hd_table); - return dev; -} - -/* hd_table must contain 4 block drivers */ -/* NOTE: for the PIIX4, the IRQs and IOports are hardcoded */ -PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -{ - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "piix4-ide"); - pci_ide_create_devs(dev, hd_table); - return dev; -} - -static void piix3_ide_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = pci_piix_ide_realize; - k->exit = pci_piix_ide_exitfn; - 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); - dc->hotpluggable = false; -} - -static const TypeInfo piix3_ide_info = { - .name = "piix3-ide", - .parent = TYPE_PCI_IDE, - .class_init = piix3_ide_class_init, -}; - -static const TypeInfo piix3_ide_xen_info = { - .name = "piix3-ide-xen", - .parent = TYPE_PCI_IDE, - .class_init = piix3_ide_class_init, -}; - -static void piix4_ide_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = pci_piix_ide_realize; - k->exit = pci_piix_ide_exitfn; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82371AB; - k->class_id = PCI_CLASS_STORAGE_IDE; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->hotpluggable = false; -} - -static const TypeInfo piix4_ide_info = { - .name = "piix4-ide", - .parent = TYPE_PCI_IDE, - .class_init = piix4_ide_class_init, -}; - -static void piix_ide_register_types(void) -{ - type_register_static(&piix3_ide_info); - type_register_static(&piix3_ide_xen_info); - type_register_static(&piix4_ide_info); -} - -type_init(piix_ide_register_types) diff --git a/qemu/hw/ide/qdev.c b/qemu/hw/ide/qdev.c deleted file mode 100644 index 4bc74a32d..000000000 --- a/qemu/hw/ide/qdev.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * ide bus support for qdev. - * - * Copyright (c) 2009 Gerd Hoffmann - * - * 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 . - */ -#include "qemu/osdep.h" -#include -#include "sysemu/dma.h" -#include "qapi/error.h" -#include "qemu/error-report.h" -#include -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "hw/block/block.h" -#include "sysemu/sysemu.h" -#include "qapi/visitor.h" - -/* --------------------------------- */ - -static char *idebus_get_fw_dev_path(DeviceState *dev); - -static Property ide_props[] = { - DEFINE_PROP_UINT32("unit", IDEDevice, unit, -1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ide_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - - k->get_fw_dev_path = idebus_get_fw_dev_path; -} - -static const TypeInfo ide_bus_info = { - .name = TYPE_IDE_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(IDEBus), - .class_init = ide_bus_class_init, -}; - -void ide_bus_new(IDEBus *idebus, size_t idebus_size, DeviceState *dev, - int bus_id, int max_units) -{ - qbus_create_inplace(idebus, idebus_size, TYPE_IDE_BUS, dev, NULL); - idebus->bus_id = bus_id; - idebus->max_units = max_units; -} - -static char *idebus_get_fw_dev_path(DeviceState *dev) -{ - char path[30]; - - snprintf(path, sizeof(path), "%s@%x", qdev_fw_name(dev), - ((IDEBus*)dev->parent_bus)->bus_id); - - return g_strdup(path); -} - -static int ide_qdev_init(DeviceState *qdev) -{ - IDEDevice *dev = IDE_DEVICE(qdev); - IDEDeviceClass *dc = IDE_DEVICE_GET_CLASS(dev); - IDEBus *bus = DO_UPCAST(IDEBus, qbus, qdev->parent_bus); - - if (!dev->conf.blk) { - error_report("No drive specified"); - goto err; - } - if (dev->unit == -1) { - dev->unit = bus->master ? 1 : 0; - } - - if (dev->unit >= bus->max_units) { - error_report("Can't create IDE unit %d, bus supports only %d units", - dev->unit, bus->max_units); - goto err; - } - - switch (dev->unit) { - case 0: - if (bus->master) { - error_report("IDE unit %d is in use", dev->unit); - goto err; - } - bus->master = dev; - break; - case 1: - if (bus->slave) { - error_report("IDE unit %d is in use", dev->unit); - goto err; - } - bus->slave = dev; - break; - default: - error_report("Invalid IDE unit %d", dev->unit); - goto err; - } - return dc->init(dev); - -err: - return -1; -} - -IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive) -{ - DeviceState *dev; - - dev = qdev_create(&bus->qbus, drive->media_cd ? "ide-cd" : "ide-hd"); - qdev_prop_set_uint32(dev, "unit", unit); - qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(drive), - &error_fatal); - qdev_init_nofail(dev); - return DO_UPCAST(IDEDevice, qdev, dev); -} - -int ide_get_geometry(BusState *bus, int unit, - int16_t *cyls, int8_t *heads, int8_t *secs) -{ - IDEState *s = &DO_UPCAST(IDEBus, qbus, bus)->ifs[unit]; - - if (s->drive_kind != IDE_HD || !s->blk) { - return -1; - } - - *cyls = s->cylinders; - *heads = s->heads; - *secs = s->sectors; - return 0; -} - -int ide_get_bios_chs_trans(BusState *bus, int unit) -{ - return DO_UPCAST(IDEBus, qbus, bus)->ifs[unit].chs_trans; -} - -/* --------------------------------- */ - -typedef struct IDEDrive { - IDEDevice dev; -} IDEDrive; - -static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) -{ - IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus); - IDEState *s = bus->ifs + dev->unit; - Error *err = NULL; - - if (dev->conf.discard_granularity == -1) { - dev->conf.discard_granularity = 512; - } else if (dev->conf.discard_granularity && - dev->conf.discard_granularity != 512) { - error_report("discard_granularity must be 512 for ide"); - return -1; - } - - blkconf_blocksizes(&dev->conf); - if (dev->conf.logical_block_size != 512) { - error_report("logical_block_size must be 512 for IDE"); - return -1; - } - - blkconf_serial(&dev->conf, &dev->serial); - if (kind != IDE_CD) { - blkconf_geometry(&dev->conf, &dev->chs_trans, 65535, 16, 255, &err); - if (err) { - error_report_err(err); - return -1; - } - } - - if (ide_init_drive(s, dev->conf.blk, kind, - dev->version, dev->serial, dev->model, dev->wwn, - dev->conf.cyls, dev->conf.heads, dev->conf.secs, - dev->chs_trans) < 0) { - return -1; - } - - if (!dev->version) { - dev->version = g_strdup(s->version); - } - if (!dev->serial) { - dev->serial = g_strdup(s->drive_serial_str); - } - - add_boot_device_path(dev->conf.bootindex, &dev->qdev, - dev->unit ? "/disk@1" : "/disk@0"); - - return 0; -} - -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, name, &d->conf.bootindex, 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, name, &boot_index, &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 */ - d->conf.bootindex = boot_index; - - if (d->unit != -1) { - add_boot_device_path(d->conf.bootindex, &d->qdev, - d->unit ? "/disk@1" : "/disk@0"); - } -out: - if (local_err) { - error_propagate(errp, local_err); - } -} - -static void ide_dev_instance_init(Object *obj) -{ - object_property_add(obj, "bootindex", "int32", - ide_dev_get_bootindex, - ide_dev_set_bootindex, NULL, NULL, NULL); - object_property_set_int(obj, -1, "bootindex", NULL); -} - -static int ide_hd_initfn(IDEDevice *dev) -{ - return ide_dev_initfn(dev, IDE_HD); -} - -static int ide_cd_initfn(IDEDevice *dev) -{ - return ide_dev_initfn(dev, IDE_CD); -} - -static int ide_drive_initfn(IDEDevice *dev) -{ - DriveInfo *dinfo = blk_legacy_dinfo(dev->conf.blk); - - return ide_dev_initfn(dev, dinfo && dinfo->media_cd ? IDE_CD : IDE_HD); -} - -#define DEFINE_IDE_DEV_PROPERTIES() \ - DEFINE_BLOCK_PROPERTIES(IDEDrive, dev.conf), \ - DEFINE_PROP_STRING("ver", IDEDrive, dev.version), \ - DEFINE_PROP_UINT64("wwn", IDEDrive, dev.wwn, 0), \ - DEFINE_PROP_STRING("serial", IDEDrive, dev.serial),\ - DEFINE_PROP_STRING("model", IDEDrive, dev.model) - -static Property ide_hd_properties[] = { - DEFINE_IDE_DEV_PROPERTIES(), - DEFINE_BLOCK_CHS_PROPERTIES(IDEDrive, dev.conf), - DEFINE_PROP_BIOS_CHS_TRANS("bios-chs-trans", - IDEDrive, dev.chs_trans, BIOS_ATA_TRANSLATION_AUTO), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ide_hd_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - IDEDeviceClass *k = IDE_DEVICE_CLASS(klass); - k->init = ide_hd_initfn; - dc->fw_name = "drive"; - dc->desc = "virtual IDE disk"; - dc->props = ide_hd_properties; -} - -static const TypeInfo ide_hd_info = { - .name = "ide-hd", - .parent = TYPE_IDE_DEVICE, - .instance_size = sizeof(IDEDrive), - .class_init = ide_hd_class_init, -}; - -static Property ide_cd_properties[] = { - DEFINE_IDE_DEV_PROPERTIES(), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ide_cd_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - IDEDeviceClass *k = IDE_DEVICE_CLASS(klass); - k->init = ide_cd_initfn; - dc->fw_name = "drive"; - dc->desc = "virtual IDE CD-ROM"; - dc->props = ide_cd_properties; -} - -static const TypeInfo ide_cd_info = { - .name = "ide-cd", - .parent = TYPE_IDE_DEVICE, - .instance_size = sizeof(IDEDrive), - .class_init = ide_cd_class_init, -}; - -static Property ide_drive_properties[] = { - DEFINE_IDE_DEV_PROPERTIES(), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ide_drive_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - IDEDeviceClass *k = IDE_DEVICE_CLASS(klass); - k->init = ide_drive_initfn; - dc->fw_name = "drive"; - dc->desc = "virtual IDE disk or CD-ROM (legacy)"; - dc->props = ide_drive_properties; -} - -static const TypeInfo ide_drive_info = { - .name = "ide-drive", - .parent = TYPE_IDE_DEVICE, - .instance_size = sizeof(IDEDrive), - .class_init = ide_drive_class_init, -}; - -static void ide_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - k->init = ide_qdev_init; - set_bit(DEVICE_CATEGORY_STORAGE, k->categories); - k->bus_type = TYPE_IDE_BUS; - k->props = ide_props; -} - -static const TypeInfo ide_device_type_info = { - .name = TYPE_IDE_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(IDEDevice), - .abstract = true, - .class_size = sizeof(IDEDeviceClass), - .class_init = ide_device_class_init, - .instance_init = ide_dev_instance_init, -}; - -static void ide_register_types(void) -{ - type_register_static(&ide_bus_info); - type_register_static(&ide_hd_info); - type_register_static(&ide_cd_info); - type_register_static(&ide_drive_info); - type_register_static(&ide_device_type_info); -} - -type_init(ide_register_types) diff --git a/qemu/hw/ide/via.c b/qemu/hw/ide/via.c deleted file mode 100644 index d3f72267a..000000000 --- a/qemu/hw/ide/via.c +++ /dev/null @@ -1,236 +0,0 @@ -/* - * QEMU IDE Emulation: PCI VIA82C686B support. - * - * Copyright (c) 2003 Fabrice Bellard - * Copyright (c) 2006 Openedhand Ltd. - * Copyright (c) 2010 Huacai Chen - * - * 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 -#include -#include -#include -#include "sysemu/block-backend.h" -#include "sysemu/sysemu.h" -#include "sysemu/dma.h" - -#include - -static uint64_t bmdma_read(void *opaque, hwaddr addr, - unsigned size) -{ - BMDMAState *bm = opaque; - uint32_t val; - - if (size != 1) { - return ((uint64_t)1 << (size * 8)) - 1; - } - - switch (addr & 3) { - case 0: - val = bm->cmd; - break; - case 2: - val = bm->status; - break; - default: - val = 0xff; - break; - } -#ifdef DEBUG_IDE - printf("bmdma: readb 0x%02x : 0x%02x\n", addr, val); -#endif - return val; -} - -static void bmdma_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - BMDMAState *bm = opaque; - - if (size != 1) { - return; - } - -#ifdef DEBUG_IDE - printf("bmdma: writeb 0x%02x : 0x%02x\n", addr, val); -#endif - switch (addr & 3) { - case 0: - bmdma_cmd_writeb(bm, val); - break; - case 2: - bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06); - break; - default:; - } -} - -static const MemoryRegionOps via_bmdma_ops = { - .read = bmdma_read, - .write = bmdma_write, -}; - -static void bmdma_setup_bar(PCIIDEState *d) -{ - int i; - - memory_region_init(&d->bmdma_bar, OBJECT(d), "via-bmdma-container", 16); - for(i = 0;i < 2; i++) { - BMDMAState *bm = &d->bmdma[i]; - - memory_region_init_io(&bm->extra_io, OBJECT(d), &via_bmdma_ops, bm, - "via-bmdma", 4); - memory_region_add_subregion(&d->bmdma_bar, i * 8, &bm->extra_io); - memory_region_init_io(&bm->addr_ioport, OBJECT(d), - &bmdma_addr_ioport_ops, bm, "bmdma", 4); - memory_region_add_subregion(&d->bmdma_bar, i * 8 + 4, &bm->addr_ioport); - } -} - -static void via_reset(void *opaque) -{ - PCIIDEState *d = opaque; - PCIDevice *pd = PCI_DEVICE(d); - uint8_t *pci_conf = pd->config; - int i; - - for (i = 0; i < 2; i++) { - ide_bus_reset(&d->bus[i]); - } - - pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_WAIT); - pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK | - PCI_STATUS_DEVSEL_MEDIUM); - - pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x000001f0); - pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x000003f4); - pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170); - pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374); - pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 20-23h */ - pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e); - - /* IDE chip enable, IDE configuration 1/2, IDE FIFO Configuration*/ - pci_set_long(pci_conf + 0x40, 0x0a090600); - /* IDE misc configuration 1/2/3 */ - pci_set_long(pci_conf + 0x44, 0x00c00068); - /* IDE Timing control */ - pci_set_long(pci_conf + 0x48, 0xa8a8a8a8); - /* IDE Address Setup Time */ - pci_set_long(pci_conf + 0x4c, 0x000000ff); - /* UltraDMA Extended Timing Control*/ - pci_set_long(pci_conf + 0x50, 0x07070707); - /* UltraDMA FIFO Control */ - pci_set_long(pci_conf + 0x54, 0x00000004); - /* IDE primary sector size */ - pci_set_long(pci_conf + 0x60, 0x00000200); - /* IDE secondary sector size */ - pci_set_long(pci_conf + 0x68, 0x00000200); - /* PCI PM Block */ - pci_set_long(pci_conf + 0xc0, 0x00020001); -} - -static void vt82c686b_init_ports(PCIIDEState *d) { - static const struct { - int iobase; - int iobase2; - int isairq; - } port_info[] = { - {0x1f0, 0x3f6, 14}, - {0x170, 0x376, 15}, - }; - int i; - - for (i = 0; i < 2; i++) { - ide_bus_new(&d->bus[i], sizeof(d->bus[i]), DEVICE(d), i, 2); - ide_init_ioport(&d->bus[i], NULL, port_info[i].iobase, - port_info[i].iobase2); - ide_init2(&d->bus[i], isa_get_irq(NULL, port_info[i].isairq)); - - bmdma_init(&d->bus[i], &d->bmdma[i], d); - d->bmdma[i].bus = &d->bus[i]; - ide_register_restart_cb(&d->bus[i]); - } -} - -/* via ide func */ -static void vt82c686b_ide_realize(PCIDevice *dev, Error **errp) -{ - PCIIDEState *d = PCI_IDE(dev); - uint8_t *pci_conf = dev->config; - - pci_config_set_prog_interface(pci_conf, 0x8a); /* legacy ATA mode */ - pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0); - - qemu_register_reset(via_reset, d); - bmdma_setup_bar(d); - pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar); - - vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d); - - vt82c686b_init_ports(d); -} - -static void vt82c686b_ide_exitfn(PCIDevice *dev) -{ - PCIIDEState *d = PCI_IDE(dev); - unsigned i; - - for (i = 0; i < 2; ++i) { - memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].extra_io); - memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].addr_ioport); - } -} - -void vt82c686b_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -{ - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "via-ide"); - pci_ide_create_devs(dev, hd_table); -} - -static void via_ide_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = vt82c686b_ide_realize; - k->exit = vt82c686b_ide_exitfn; - k->vendor_id = PCI_VENDOR_ID_VIA; - k->device_id = PCI_DEVICE_ID_VIA_IDE; - k->revision = 0x06; - k->class_id = PCI_CLASS_STORAGE_IDE; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo via_ide_info = { - .name = "via-ide", - .parent = TYPE_PCI_IDE, - .class_init = via_ide_class_init, -}; - -static void via_ide_register_types(void) -{ - type_register_static(&via_ide_info); -} - -type_init(via_ide_register_types) diff --git a/qemu/hw/input/Makefile.objs b/qemu/hw/input/Makefile.objs deleted file mode 100644 index 7715d7230..000000000 --- a/qemu/hw/input/Makefile.objs +++ /dev/null @@ -1,19 +0,0 @@ -common-obj-$(CONFIG_ADB) += adb.o -common-obj-y += hid.o -common-obj-$(CONFIG_LM832X) += lm832x.o -common-obj-$(CONFIG_PCKBD) += pckbd.o -common-obj-$(CONFIG_PL050) += pl050.o -common-obj-y += ps2.o -common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o -common-obj-$(CONFIG_TSC2005) += tsc2005.o -common-obj-$(CONFIG_VMMOUSE) += vmmouse.o - -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 - -obj-$(CONFIG_MILKYMIST) += milkymist-softusb.o -obj-$(CONFIG_PXA2XX) += pxa2xx_keypad.o -obj-$(CONFIG_TSC210X) += tsc210x.o diff --git a/qemu/hw/input/adb.c b/qemu/hw/input/adb.c deleted file mode 100644 index f0ad0d447..000000000 --- a/qemu/hw/input/adb.c +++ /dev/null @@ -1,598 +0,0 @@ -/* - * QEMU ADB support - * - * Copyright (c) 2004 Fabrice Bellard - * - * 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/input/adb.h" -#include "ui/console.h" - -/* debug ADB */ -//#define DEBUG_ADB - -#ifdef DEBUG_ADB -#define ADB_DPRINTF(fmt, ...) \ -do { printf("ADB: " fmt , ## __VA_ARGS__); } while (0) -#else -#define ADB_DPRINTF(fmt, ...) -#endif - -/* ADB commands */ -#define ADB_BUSRESET 0x00 -#define ADB_FLUSH 0x01 -#define ADB_WRITEREG 0x08 -#define ADB_READREG 0x0c - -/* ADB device commands */ -#define ADB_CMD_SELF_TEST 0xff -#define ADB_CMD_CHANGE_ID 0xfe -#define ADB_CMD_CHANGE_ID_AND_ACT 0xfd -#define ADB_CMD_CHANGE_ID_AND_ENABLE 0x00 - -/* ADB default device IDs (upper 4 bits of ADB command byte) */ -#define ADB_DEVID_DONGLE 1 -#define ADB_DEVID_KEYBOARD 2 -#define ADB_DEVID_MOUSE 3 -#define ADB_DEVID_TABLET 4 -#define ADB_DEVID_MODEM 5 -#define ADB_DEVID_MISC 7 - -/* error codes */ -#define ADB_RET_NOTPRESENT (-2) - -static void adb_device_reset(ADBDevice *d) -{ - qdev_reset_all(DEVICE(d)); -} - -int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) -{ - ADBDevice *d; - int devaddr, cmd, i; - - cmd = buf[0] & 0xf; - if (cmd == ADB_BUSRESET) { - for(i = 0; i < s->nb_devices; i++) { - d = s->devices[i]; - adb_device_reset(d); - } - return 0; - } - devaddr = buf[0] >> 4; - for(i = 0; i < s->nb_devices; i++) { - d = s->devices[i]; - if (d->devaddr == devaddr) { - ADBDeviceClass *adc = ADB_DEVICE_GET_CLASS(d); - return adc->devreq(d, obuf, buf, len); - } - } - return ADB_RET_NOTPRESENT; -} - -/* XXX: move that to cuda ? */ -int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask) -{ - ADBDevice *d; - int olen, i; - uint8_t buf[1]; - - olen = 0; - for(i = 0; i < s->nb_devices; i++) { - if (s->poll_index >= s->nb_devices) - s->poll_index = 0; - d = s->devices[s->poll_index]; - 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++; - } - return olen; -} - -static const TypeInfo adb_bus_type_info = { - .name = TYPE_ADB_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(ADBBusState), -}; - -static const VMStateDescription vmstate_adb_device = { - .name = "adb_device", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_INT32(devaddr, ADBDevice), - VMSTATE_INT32(handler, ADBDevice), - VMSTATE_END_OF_LIST() - } -}; - -static void adb_device_realizefn(DeviceState *dev, Error **errp) -{ - ADBDevice *d = ADB_DEVICE(dev); - ADBBusState *bus = ADB_BUS(qdev_get_parent_bus(dev)); - - if (bus->nb_devices >= MAX_ADB_DEVICES) { - return; - } - - bus->devices[bus->nb_devices++] = d; -} - -static void adb_device_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = adb_device_realizefn; - dc->bus_type = TYPE_ADB_BUS; -} - -static const TypeInfo adb_device_type_info = { - .name = TYPE_ADB_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(ADBDevice), - .abstract = true, - .class_init = adb_device_class_init, -}; - -/***************************************************************/ -/* Keyboard ADB device */ - -#define ADB_KEYBOARD(obj) OBJECT_CHECK(KBDState, (obj), TYPE_ADB_KEYBOARD) - -typedef struct KBDState { - /*< private >*/ - ADBDevice parent_obj; - /*< public >*/ - - uint8_t data[128]; - int rptr, wptr, count; -} KBDState; - -#define ADB_KEYBOARD_CLASS(class) \ - OBJECT_CLASS_CHECK(ADBKeyboardClass, (class), TYPE_ADB_KEYBOARD) -#define ADB_KEYBOARD_GET_CLASS(obj) \ - OBJECT_GET_CLASS(ADBKeyboardClass, (obj), TYPE_ADB_KEYBOARD) - -typedef struct ADBKeyboardClass { - /*< private >*/ - ADBDeviceClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; -} ADBKeyboardClass; - -static const uint8_t pc_to_adb_keycode[256] = { - 0, 53, 18, 19, 20, 21, 23, 22, 26, 28, 25, 29, 27, 24, 51, 48, - 12, 13, 14, 15, 17, 16, 32, 34, 31, 35, 33, 30, 36, 54, 0, 1, - 2, 3, 5, 4, 38, 40, 37, 41, 39, 50, 56, 42, 6, 7, 8, 9, - 11, 45, 46, 43, 47, 44,123, 67, 58, 49, 57,122,120, 99,118, 96, - 97, 98,100,101,109, 71,107, 89, 91, 92, 78, 86, 87, 88, 69, 83, - 84, 85, 82, 65, 0, 0, 10,103,111, 0, 0,110, 81, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 94, 0, 93, 0, 0, 0, 0, 0, 0,104,102, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76,125, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 75, 0, 0,124, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,115, 62,116, 0, 59, 0, 60, 0,119, - 61,121,114,117, 0, 0, 0, 0, 0, 0, 0, 55,126, 0,127, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -static void adb_kbd_put_keycode(void *opaque, int keycode) -{ - KBDState *s = opaque; - - if (s->count < sizeof(s->data)) { - s->data[s->wptr] = keycode; - if (++s->wptr == sizeof(s->data)) - s->wptr = 0; - s->count++; - } -} - -static int adb_kbd_poll(ADBDevice *d, uint8_t *obuf) -{ - static int ext_keycode; - KBDState *s = ADB_KEYBOARD(d); - int adb_keycode, keycode; - int olen; - - olen = 0; - for(;;) { - if (s->count == 0) - break; - keycode = s->data[s->rptr]; - if (++s->rptr == sizeof(s->data)) - s->rptr = 0; - s->count--; - - if (keycode == 0xe0) { - ext_keycode = 1; - } else { - if (ext_keycode) - adb_keycode = pc_to_adb_keycode[keycode | 0x80]; - else - adb_keycode = pc_to_adb_keycode[keycode & 0x7f]; - obuf[0] = adb_keycode | (keycode & 0x80); - /* NOTE: could put a second keycode if needed */ - obuf[1] = 0xff; - olen = 2; - ext_keycode = 0; - break; - } - } - return olen; -} - -static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, - const uint8_t *buf, int len) -{ - KBDState *s = ADB_KEYBOARD(d); - int cmd, reg, olen; - - if ((buf[0] & 0x0f) == ADB_FLUSH) { - /* flush keyboard fifo */ - s->wptr = s->rptr = s->count = 0; - return 0; - } - - cmd = buf[0] & 0xc; - reg = buf[0] & 0x3; - olen = 0; - switch(cmd) { - case ADB_WRITEREG: - switch(reg) { - case 2: - /* LED status */ - break; - case 3: - switch(buf[2]) { - case ADB_CMD_SELF_TEST: - break; - case ADB_CMD_CHANGE_ID: - case ADB_CMD_CHANGE_ID_AND_ACT: - case ADB_CMD_CHANGE_ID_AND_ENABLE: - d->devaddr = buf[1] & 0xf; - break; - default: - /* XXX: check this */ - d->devaddr = buf[1] & 0xf; - d->handler = buf[2]; - break; - } - } - break; - case ADB_READREG: - switch(reg) { - case 0: - olen = adb_kbd_poll(d, obuf); - break; - case 1: - break; - case 2: - obuf[0] = 0x00; /* XXX: check this */ - obuf[1] = 0x07; /* led status */ - olen = 2; - break; - case 3: - obuf[0] = d->handler; - obuf[1] = d->devaddr; - olen = 2; - break; - } - break; - } - return olen; -} - -static const VMStateDescription vmstate_adb_kbd = { - .name = "adb_kbd", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(parent_obj, KBDState, 0, vmstate_adb_device, ADBDevice), - VMSTATE_BUFFER(data, KBDState), - VMSTATE_INT32(rptr, KBDState), - VMSTATE_INT32(wptr, KBDState), - VMSTATE_INT32(count, KBDState), - VMSTATE_END_OF_LIST() - } -}; - -static void adb_kbd_reset(DeviceState *dev) -{ - ADBDevice *d = ADB_DEVICE(dev); - KBDState *s = ADB_KEYBOARD(dev); - - d->handler = 1; - d->devaddr = ADB_DEVID_KEYBOARD; - memset(s->data, 0, sizeof(s->data)); - s->rptr = 0; - s->wptr = 0; - s->count = 0; -} - -static void adb_kbd_realizefn(DeviceState *dev, Error **errp) -{ - ADBDevice *d = ADB_DEVICE(dev); - ADBKeyboardClass *akc = ADB_KEYBOARD_GET_CLASS(dev); - - akc->parent_realize(dev, errp); - - qemu_add_kbd_event_handler(adb_kbd_put_keycode, d); -} - -static void adb_kbd_initfn(Object *obj) -{ - ADBDevice *d = ADB_DEVICE(obj); - - d->devaddr = ADB_DEVID_KEYBOARD; -} - -static void adb_kbd_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc); - ADBKeyboardClass *akc = ADB_KEYBOARD_CLASS(oc); - - 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; - dc->vmsd = &vmstate_adb_kbd; -} - -static const TypeInfo adb_kbd_type_info = { - .name = TYPE_ADB_KEYBOARD, - .parent = TYPE_ADB_DEVICE, - .instance_size = sizeof(KBDState), - .instance_init = adb_kbd_initfn, - .class_init = adb_kbd_class_init, - .class_size = sizeof(ADBKeyboardClass), -}; - -/***************************************************************/ -/* Mouse ADB device */ - -#define ADB_MOUSE(obj) OBJECT_CHECK(MouseState, (obj), TYPE_ADB_MOUSE) - -typedef struct MouseState { - /*< public >*/ - ADBDevice parent_obj; - /*< private >*/ - - int buttons_state, last_buttons_state; - int dx, dy, dz; -} MouseState; - -#define ADB_MOUSE_CLASS(class) \ - OBJECT_CLASS_CHECK(ADBMouseClass, (class), TYPE_ADB_MOUSE) -#define ADB_MOUSE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(ADBMouseClass, (obj), TYPE_ADB_MOUSE) - -typedef struct ADBMouseClass { - /*< public >*/ - ADBDeviceClass parent_class; - /*< private >*/ - - DeviceRealize parent_realize; -} ADBMouseClass; - -static void adb_mouse_event(void *opaque, - int dx1, int dy1, int dz1, int buttons_state) -{ - MouseState *s = opaque; - - s->dx += dx1; - s->dy += dy1; - s->dz += dz1; - s->buttons_state = buttons_state; -} - - -static int adb_mouse_poll(ADBDevice *d, uint8_t *obuf) -{ - MouseState *s = ADB_MOUSE(d); - int dx, dy; - - if (s->last_buttons_state == s->buttons_state && - s->dx == 0 && s->dy == 0) - return 0; - - dx = s->dx; - if (dx < -63) - dx = -63; - else if (dx > 63) - dx = 63; - - dy = s->dy; - if (dy < -63) - dy = -63; - else if (dy > 63) - dy = 63; - - s->dx -= dx; - s->dy -= dy; - s->last_buttons_state = s->buttons_state; - - dx &= 0x7f; - dy &= 0x7f; - - if (!(s->buttons_state & MOUSE_EVENT_LBUTTON)) - dy |= 0x80; - if (!(s->buttons_state & MOUSE_EVENT_RBUTTON)) - dx |= 0x80; - - obuf[0] = dy; - obuf[1] = dx; - return 2; -} - -static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, - const uint8_t *buf, int len) -{ - MouseState *s = ADB_MOUSE(d); - int cmd, reg, olen; - - if ((buf[0] & 0x0f) == ADB_FLUSH) { - /* flush mouse fifo */ - s->buttons_state = s->last_buttons_state; - s->dx = 0; - s->dy = 0; - s->dz = 0; - return 0; - } - - cmd = buf[0] & 0xc; - reg = buf[0] & 0x3; - olen = 0; - switch(cmd) { - case ADB_WRITEREG: - ADB_DPRINTF("write reg %d val 0x%2.2x\n", reg, buf[1]); - switch(reg) { - case 2: - break; - case 3: - switch(buf[2]) { - case ADB_CMD_SELF_TEST: - break; - case ADB_CMD_CHANGE_ID: - case ADB_CMD_CHANGE_ID_AND_ACT: - case ADB_CMD_CHANGE_ID_AND_ENABLE: - d->devaddr = buf[1] & 0xf; - break; - default: - /* XXX: check this */ - d->devaddr = buf[1] & 0xf; - break; - } - } - break; - case ADB_READREG: - switch(reg) { - case 0: - olen = adb_mouse_poll(d, obuf); - break; - case 1: - break; - case 3: - obuf[0] = d->handler; - obuf[1] = d->devaddr; - olen = 2; - break; - } - ADB_DPRINTF("read reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x\n", reg, - obuf[0], obuf[1]); - break; - } - return olen; -} - -static void adb_mouse_reset(DeviceState *dev) -{ - ADBDevice *d = ADB_DEVICE(dev); - MouseState *s = ADB_MOUSE(dev); - - d->handler = 2; - d->devaddr = ADB_DEVID_MOUSE; - s->last_buttons_state = s->buttons_state = 0; - s->dx = s->dy = s->dz = 0; -} - -static const VMStateDescription vmstate_adb_mouse = { - .name = "adb_mouse", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(parent_obj, MouseState, 0, vmstate_adb_device, - ADBDevice), - VMSTATE_INT32(buttons_state, MouseState), - VMSTATE_INT32(last_buttons_state, MouseState), - VMSTATE_INT32(dx, MouseState), - VMSTATE_INT32(dy, MouseState), - VMSTATE_INT32(dz, MouseState), - VMSTATE_END_OF_LIST() - } -}; - -static void adb_mouse_realizefn(DeviceState *dev, Error **errp) -{ - MouseState *s = ADB_MOUSE(dev); - ADBMouseClass *amc = ADB_MOUSE_GET_CLASS(dev); - - amc->parent_realize(dev, errp); - - qemu_add_mouse_event_handler(adb_mouse_event, s, 0, "QEMU ADB Mouse"); -} - -static void adb_mouse_initfn(Object *obj) -{ - ADBDevice *d = ADB_DEVICE(obj); - - d->devaddr = ADB_DEVID_MOUSE; -} - -static void adb_mouse_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc); - ADBMouseClass *amc = ADB_MOUSE_CLASS(oc); - - 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; - dc->vmsd = &vmstate_adb_mouse; -} - -static const TypeInfo adb_mouse_type_info = { - .name = TYPE_ADB_MOUSE, - .parent = TYPE_ADB_DEVICE, - .instance_size = sizeof(MouseState), - .instance_init = adb_mouse_initfn, - .class_init = adb_mouse_class_init, - .class_size = sizeof(ADBMouseClass), -}; - - -static void adb_register_types(void) -{ - type_register_static(&adb_bus_type_info); - type_register_static(&adb_device_type_info); - type_register_static(&adb_kbd_type_info); - type_register_static(&adb_mouse_type_info); -} - -type_init(adb_register_types) diff --git a/qemu/hw/input/hid.c b/qemu/hw/input/hid.c deleted file mode 100644 index d92c7463b..000000000 --- a/qemu/hw/input/hid.c +++ /dev/null @@ -1,618 +0,0 @@ -/* - * QEMU HID devices - * - * Copyright (c) 2005 Fabrice Bellard - * Copyright (c) 2007 OpenMoko, Inc. (andrew@openedhand.com) - * - * 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 "ui/console.h" -#include "qemu/timer.h" -#include "hw/input/hid.h" - -#define HID_USAGE_ERROR_ROLLOVER 0x01 -#define HID_USAGE_POSTFAIL 0x02 -#define HID_USAGE_ERROR_UNDEFINED 0x03 - -/* Indices are QEMU keycodes, values are from HID Usage Table. Indices - * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d. */ -static const uint8_t hid_usage_keys[0x100] = { - 0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, - 0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b, - 0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c, - 0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16, - 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33, - 0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19, - 0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55, - 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, 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, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46, - 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a, - 0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d, - 0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -bool hid_has_events(HIDState *hs) -{ - return hs->n > 0 || hs->idle_pending; -} - -static void hid_idle_timer(void *opaque) -{ - HIDState *hs = opaque; - - hs->idle_pending = true; - hs->event(hs); -} - -static void hid_del_idle_timer(HIDState *hs) -{ - if (hs->idle_timer) { - timer_del(hs->idle_timer); - timer_free(hs->idle_timer); - hs->idle_timer = NULL; - } -} - -void hid_set_next_idle(HIDState *hs) -{ - if (hs->idle) { - uint64_t expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - NANOSECONDS_PER_SECOND * hs->idle * 4 / 1000; - if (!hs->idle_timer) { - hs->idle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hid_idle_timer, hs); - } - timer_mod_ns(hs->idle_timer, expire_time); - } else { - hid_del_idle_timer(hs); - } -} - -static void hid_pointer_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) -{ - 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->type) { - case INPUT_EVENT_KIND_REL: - 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: - 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: - 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 (btn->button == INPUT_BUTTON_WHEEL_DOWN) { - e->dz++; - } - } else { - e->buttons_state &= ~bmap[btn->button]; - } - break; - - default: - /* keep gcc happy */ - break; - } - -} - -static void hid_pointer_sync(DeviceState *dev) -{ - HIDState *hs = (HIDState *)dev; - HIDPointerEvent *prev, *curr, *next; - bool event_compression = false; - - if (hs->n == QUEUE_LENGTH-1) { - /* - * Queue full. We are losing information, but we at least - * keep track of most recent button state. - */ - return; - } - - prev = &hs->ptr.queue[(hs->head + hs->n - 1) & QUEUE_MASK]; - curr = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK]; - next = &hs->ptr.queue[(hs->head + hs->n + 1) & QUEUE_MASK]; - - if (hs->n > 0) { - /* - * No button state change between previous and current event - * (and previous wasn't seen by the guest yet), so there is - * motion information only and we can combine the two event - * into one. - */ - if (curr->buttons_state == prev->buttons_state) { - event_compression = true; - } - } - - if (event_compression) { - /* add current motion to previous, clear current */ - if (hs->kind == HID_MOUSE) { - prev->xdx += curr->xdx; - curr->xdx = 0; - prev->ydy += curr->ydy; - curr->ydy = 0; - } else { - prev->xdx = curr->xdx; - prev->ydy = curr->ydy; - } - prev->dz += curr->dz; - curr->dz = 0; - } else { - /* prepate next (clear rel, copy abs + btns) */ - if (hs->kind == HID_MOUSE) { - next->xdx = 0; - next->ydy = 0; - } else { - next->xdx = curr->xdx; - next->ydy = curr->ydy; - } - next->dz = 0; - next->buttons_state = curr->buttons_state; - /* make current guest visible, notify guest */ - hs->n++; - hs->event(hs); - } -} - -static void hid_keyboard_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) -{ - HIDState *hs = (HIDState *)dev; - int scancodes[3], i, count; - int slot; - InputKeyEvent *key = evt->u.key.data; - - 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"); - return; - } - for (i = 0; i < count; i++) { - slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++; - hs->kbd.keycodes[slot] = scancodes[i]; - } - hs->event(hs); -} - -static void hid_keyboard_process_keycode(HIDState *hs) -{ - uint8_t hid_code, index, key; - int i, keycode, slot; - - if (hs->n == 0) { - return; - } - slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--; - keycode = hs->kbd.keycodes[slot]; - - key = keycode & 0x7f; - index = key | ((hs->kbd.modifiers & (1 << 8)) >> 1); - hid_code = hid_usage_keys[index]; - hs->kbd.modifiers &= ~(1 << 8); - - switch (hid_code) { - case 0x00: - return; - - case 0xe0: - assert(key == 0x1d); - if (hs->kbd.modifiers & (1 << 9)) { - /* The hid_codes for the 0xe1/0x1d scancode sequence are 0xe9/0xe0. - * Here we're processing the second hid_code. By dropping bit 9 - * and setting bit 8, the scancode after 0x1d will access the - * second half of the table. - */ - hs->kbd.modifiers ^= (1 << 8) | (1 << 9); - return; - } - /* fall through to process Ctrl_L */ - case 0xe1 ... 0xe7: - /* Ctrl_L/Ctrl_R, Shift_L/Shift_R, Alt_L/Alt_R, Win_L/Win_R. - * Handle releases here, or fall through to process presses. - */ - if (keycode & (1 << 7)) { - hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f)); - return; - } - /* fall through */ - case 0xe8 ... 0xe9: - /* USB modifiers are just 1 byte long. Bits 8 and 9 of - * hs->kbd.modifiers implement a state machine that detects the - * 0xe0 and 0xe1/0x1d sequences. These bits do not follow the - * usual rules where bit 7 marks released keys; they are cleared - * elsewhere in the function as the state machine dictates. - */ - hs->kbd.modifiers |= 1 << (hid_code & 0x0f); - return; - - case 0xea ... 0xef: - abort(); - - default: - break; - } - - if (keycode & (1 << 7)) { - for (i = hs->kbd.keys - 1; i >= 0; i--) { - if (hs->kbd.key[i] == hid_code) { - hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys]; - hs->kbd.key[hs->kbd.keys] = 0x00; - break; - } - } - if (i < 0) { - return; - } - } else { - for (i = hs->kbd.keys - 1; i >= 0; i--) { - if (hs->kbd.key[i] == hid_code) { - break; - } - } - if (i < 0) { - if (hs->kbd.keys < sizeof(hs->kbd.key)) { - hs->kbd.key[hs->kbd.keys++] = hid_code; - } - } else { - return; - } - } -} - -static inline int int_clamp(int val, int vmin, int vmax) -{ - if (val < vmin) { - return vmin; - } else if (val > vmax) { - return vmax; - } else { - return val; - } -} - -void hid_pointer_activate(HIDState *hs) -{ - if (!hs->ptr.mouse_grabbed) { - qemu_input_handler_activate(hs->s); - hs->ptr.mouse_grabbed = 1; - } -} - -int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) -{ - int dx, dy, dz, l; - int index; - HIDPointerEvent *e; - - hs->idle_pending = false; - - hid_pointer_activate(hs); - - /* When the buffer is empty, return the last event. Relative - movements will all be zero. */ - index = (hs->n ? hs->head : hs->head - 1); - e = &hs->ptr.queue[index & QUEUE_MASK]; - - if (hs->kind == HID_MOUSE) { - dx = int_clamp(e->xdx, -127, 127); - dy = int_clamp(e->ydy, -127, 127); - e->xdx -= dx; - e->ydy -= dy; - } else { - dx = e->xdx; - dy = e->ydy; - } - dz = int_clamp(e->dz, -127, 127); - e->dz -= dz; - - if (hs->n && - !e->dz && - (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) { - /* that deals with this event */ - QUEUE_INCR(hs->head); - hs->n--; - } - - /* Appears we have to invert the wheel direction */ - dz = 0 - dz; - l = 0; - switch (hs->kind) { - case HID_MOUSE: - if (len > l) { - buf[l++] = e->buttons_state; - } - if (len > l) { - buf[l++] = dx; - } - if (len > l) { - buf[l++] = dy; - } - if (len > l) { - buf[l++] = dz; - } - break; - - case HID_TABLET: - if (len > l) { - buf[l++] = e->buttons_state; - } - if (len > l) { - buf[l++] = dx & 0xff; - } - if (len > l) { - buf[l++] = dx >> 8; - } - if (len > l) { - buf[l++] = dy & 0xff; - } - if (len > l) { - buf[l++] = dy >> 8; - } - if (len > l) { - buf[l++] = dz; - } - break; - - default: - abort(); - } - - return l; -} - -int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len) -{ - hs->idle_pending = false; - - if (len < 2) { - return 0; - } - - hid_keyboard_process_keycode(hs); - - buf[0] = hs->kbd.modifiers & 0xff; - buf[1] = 0; - if (hs->kbd.keys > 6) { - memset(buf + 2, HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2); - } else { - memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2); - } - - return MIN(8, len); -} - -int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len) -{ - if (len > 0) { - int ledstate = 0; - /* 0x01: Num Lock LED - * 0x02: Caps Lock LED - * 0x04: Scroll Lock LED - * 0x08: Compose LED - * 0x10: Kana LED */ - hs->kbd.leds = buf[0]; - if (hs->kbd.leds & 0x04) { - ledstate |= QEMU_SCROLL_LOCK_LED; - } - if (hs->kbd.leds & 0x01) { - ledstate |= QEMU_NUM_LOCK_LED; - } - if (hs->kbd.leds & 0x02) { - ledstate |= QEMU_CAPS_LOCK_LED; - } - kbd_put_ledstate(ledstate); - } - return 0; -} - -void hid_reset(HIDState *hs) -{ - switch (hs->kind) { - case HID_KEYBOARD: - memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes)); - memset(hs->kbd.key, 0, sizeof(hs->kbd.key)); - hs->kbd.keys = 0; - break; - case HID_MOUSE: - case HID_TABLET: - memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue)); - break; - } - hs->head = 0; - hs->n = 0; - hs->protocol = 1; - hs->idle = 0; - hs->idle_pending = false; - hid_del_idle_timer(hs); -} - -void hid_free(HIDState *hs) -{ - qemu_input_handler_unregister(hs->s); - hid_del_idle_timer(hs); -} - -static QemuInputHandler hid_keyboard_handler = { - .name = "QEMU HID Keyboard", - .mask = INPUT_EVENT_MASK_KEY, - .event = hid_keyboard_event, -}; - -static QemuInputHandler hid_mouse_handler = { - .name = "QEMU HID Mouse", - .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL, - .event = hid_pointer_event, - .sync = hid_pointer_sync, -}; - -static QemuInputHandler hid_tablet_handler = { - .name = "QEMU HID Tablet", - .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, - .event = hid_pointer_event, - .sync = hid_pointer_sync, -}; - -void hid_init(HIDState *hs, int kind, HIDEventFunc event) -{ - hs->kind = kind; - hs->event = event; - - if (hs->kind == HID_KEYBOARD) { - hs->s = qemu_input_handler_register((DeviceState *)hs, - &hid_keyboard_handler); - qemu_input_handler_activate(hs->s); - } else if (hs->kind == HID_MOUSE) { - hs->s = qemu_input_handler_register((DeviceState *)hs, - &hid_mouse_handler); - } else if (hs->kind == HID_TABLET) { - hs->s = qemu_input_handler_register((DeviceState *)hs, - &hid_tablet_handler); - } -} - -static int hid_post_load(void *opaque, int version_id) -{ - HIDState *s = opaque; - - hid_set_next_idle(s); - - if (s->n == QUEUE_LENGTH && (s->kind == HID_TABLET || - s->kind == HID_MOUSE)) { - /* - * Handle ptr device migration from old qemu with full queue. - * - * Throw away everything but the last event, so we propagate - * at least the current button state to the guest. Also keep - * current position for the tablet, signal "no motion" for the - * mouse. - */ - HIDPointerEvent evt; - evt = s->ptr.queue[(s->head+s->n) & QUEUE_MASK]; - if (s->kind == HID_MOUSE) { - evt.xdx = 0; - evt.ydy = 0; - } - s->ptr.queue[0] = evt; - s->head = 0; - s->n = 1; - } - return 0; -} - -static const VMStateDescription vmstate_hid_ptr_queue = { - .name = "HIDPointerEventQueue", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32(xdx, HIDPointerEvent), - VMSTATE_INT32(ydy, HIDPointerEvent), - VMSTATE_INT32(dz, HIDPointerEvent), - VMSTATE_INT32(buttons_state, HIDPointerEvent), - VMSTATE_END_OF_LIST() - } -}; - -const VMStateDescription vmstate_hid_ptr_device = { - .name = "HIDPointerDevice", - .version_id = 1, - .minimum_version_id = 1, - .post_load = hid_post_load, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(ptr.queue, HIDState, QUEUE_LENGTH, 0, - vmstate_hid_ptr_queue, HIDPointerEvent), - VMSTATE_UINT32(head, HIDState), - VMSTATE_UINT32(n, HIDState), - VMSTATE_INT32(protocol, HIDState), - VMSTATE_UINT8(idle, HIDState), - VMSTATE_END_OF_LIST(), - } -}; - -const VMStateDescription vmstate_hid_keyboard_device = { - .name = "HIDKeyboardDevice", - .version_id = 1, - .minimum_version_id = 1, - .post_load = hid_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(kbd.keycodes, HIDState, QUEUE_LENGTH), - VMSTATE_UINT32(head, HIDState), - VMSTATE_UINT32(n, HIDState), - VMSTATE_UINT16(kbd.modifiers, HIDState), - VMSTATE_UINT8(kbd.leds, HIDState), - VMSTATE_UINT8_ARRAY(kbd.key, HIDState, 16), - VMSTATE_INT32(kbd.keys, HIDState), - VMSTATE_INT32(protocol, HIDState), - VMSTATE_UINT8(idle, HIDState), - VMSTATE_END_OF_LIST(), - } -}; diff --git a/qemu/hw/input/lm832x.c b/qemu/hw/input/lm832x.c deleted file mode 100644 index 539682cac..000000000 --- a/qemu/hw/input/lm832x.c +++ /dev/null @@ -1,525 +0,0 @@ -/* - * National Semiconductor LM8322/8323 GPIO keyboard & PWM chips. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/i2c/i2c.h" -#include "qemu/timer.h" -#include "ui/console.h" - -#define TYPE_LM8323 "lm8323" -#define LM8323(obj) OBJECT_CHECK(LM823KbdState, (obj), TYPE_LM8323) - -typedef struct { - I2CSlave parent_obj; - - uint8_t i2c_dir; - uint8_t i2c_cycle; - uint8_t reg; - - qemu_irq nirq; - uint16_t model; - - struct { - qemu_irq out[2]; - int in[2][2]; - } mux; - - uint8_t config; - uint8_t status; - uint8_t acttime; - uint8_t error; - uint8_t clock; - - struct { - uint16_t pull; - uint16_t mask; - uint16_t dir; - uint16_t level; - qemu_irq out[16]; - } gpio; - - struct { - uint8_t dbnctime; - uint8_t size; - uint8_t start; - uint8_t len; - uint8_t fifo[16]; - } kbd; - - struct { - uint16_t file[256]; - uint8_t faddr; - uint8_t addr[3]; - QEMUTimer *tm[3]; - } pwm; -} LM823KbdState; - -#define INT_KEYPAD (1 << 0) -#define INT_ERROR (1 << 3) -#define INT_NOINIT (1 << 4) -#define INT_PWMEND(n) (1 << (5 + n)) - -#define ERR_BADPAR (1 << 0) -#define ERR_CMDUNK (1 << 1) -#define ERR_KEYOVR (1 << 2) -#define ERR_FIFOOVR (1 << 6) - -static void lm_kbd_irq_update(LM823KbdState *s) -{ - qemu_set_irq(s->nirq, !s->status); -} - -static void lm_kbd_gpio_update(LM823KbdState *s) -{ -} - -static void lm_kbd_reset(LM823KbdState *s) -{ - s->config = 0x80; - s->status = INT_NOINIT; - s->acttime = 125; - s->kbd.dbnctime = 3; - s->kbd.size = 0x33; - s->clock = 0x08; - - lm_kbd_irq_update(s); - lm_kbd_gpio_update(s); -} - -static void lm_kbd_error(LM823KbdState *s, int err) -{ - s->error |= err; - s->status |= INT_ERROR; - lm_kbd_irq_update(s); -} - -static void lm_kbd_pwm_tick(LM823KbdState *s, int line) -{ -} - -static void lm_kbd_pwm_start(LM823KbdState *s, int line) -{ - lm_kbd_pwm_tick(s, line); -} - -static void lm_kbd_pwm0_tick(void *opaque) -{ - lm_kbd_pwm_tick(opaque, 0); -} -static void lm_kbd_pwm1_tick(void *opaque) -{ - lm_kbd_pwm_tick(opaque, 1); -} -static void lm_kbd_pwm2_tick(void *opaque) -{ - lm_kbd_pwm_tick(opaque, 2); -} - -enum { - LM832x_CMD_READ_ID = 0x80, /* Read chip ID. */ - LM832x_CMD_WRITE_CFG = 0x81, /* Set configuration item. */ - LM832x_CMD_READ_INT = 0x82, /* Get interrupt status. */ - LM832x_CMD_RESET = 0x83, /* Reset, same as external one */ - LM823x_CMD_WRITE_PULL_DOWN = 0x84, /* Select GPIO pull-up/down. */ - LM832x_CMD_WRITE_PORT_SEL = 0x85, /* Select GPIO in/out. */ - LM832x_CMD_WRITE_PORT_STATE = 0x86, /* Set GPIO pull-up/down. */ - LM832x_CMD_READ_PORT_SEL = 0x87, /* Get GPIO in/out. */ - LM832x_CMD_READ_PORT_STATE = 0x88, /* Get GPIO pull-up/down. */ - LM832x_CMD_READ_FIFO = 0x89, /* Read byte from FIFO. */ - LM832x_CMD_RPT_READ_FIFO = 0x8a, /* Read FIFO (no increment). */ - LM832x_CMD_SET_ACTIVE = 0x8b, /* Set active time. */ - LM832x_CMD_READ_ERROR = 0x8c, /* Get error status. */ - LM832x_CMD_READ_ROTATOR = 0x8e, /* Read rotator status. */ - LM832x_CMD_SET_DEBOUNCE = 0x8f, /* Set debouncing time. */ - LM832x_CMD_SET_KEY_SIZE = 0x90, /* Set keypad size. */ - LM832x_CMD_READ_KEY_SIZE = 0x91, /* Get keypad size. */ - LM832x_CMD_READ_CFG = 0x92, /* Get configuration item. */ - LM832x_CMD_WRITE_CLOCK = 0x93, /* Set clock config. */ - LM832x_CMD_READ_CLOCK = 0x94, /* Get clock config. */ - LM832x_CMD_PWM_WRITE = 0x95, /* Write PWM script. */ - LM832x_CMD_PWM_START = 0x96, /* Start PWM engine. */ - LM832x_CMD_PWM_STOP = 0x97, /* Stop PWM engine. */ - LM832x_GENERAL_ERROR = 0xff, /* There was one error. - Previously was represented by -1 - This is not a command */ -}; - -#define LM832x_MAX_KPX 8 -#define LM832x_MAX_KPY 12 - -static uint8_t lm_kbd_read(LM823KbdState *s, int reg, int byte) -{ - int ret; - - switch (reg) { - case LM832x_CMD_READ_ID: - ret = 0x0400; - break; - - case LM832x_CMD_READ_INT: - ret = s->status; - if (!(s->status & INT_NOINIT)) { - s->status = 0; - lm_kbd_irq_update(s); - } - break; - - case LM832x_CMD_READ_PORT_SEL: - ret = s->gpio.dir; - break; - case LM832x_CMD_READ_PORT_STATE: - ret = s->gpio.mask; - break; - - case LM832x_CMD_READ_FIFO: - if (s->kbd.len <= 1) - return 0x00; - - /* Example response from the two commands after a INT_KEYPAD - * interrupt caused by the key 0x3c being pressed: - * RPT_READ_FIFO: 55 bc 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01 - * READ_FIFO: bc 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01 - * RPT_READ_FIFO: bc 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01 - * - * 55 is the code of the key release event serviced in the previous - * interrupt handling. - * - * TODO: find out whether the FIFO is advanced a single character - * before reading every byte or the whole size of the FIFO at the - * last LM832x_CMD_READ_FIFO. This affects LM832x_CMD_RPT_READ_FIFO - * output in cases where there are more than one event in the FIFO. - * Assume 0xbc and 0x3c events are in the FIFO: - * RPT_READ_FIFO: 55 bc 3c 00 4e ff 0a 50 08 00 29 d9 08 01 c9 - * READ_FIFO: bc 3c 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 - * Does RPT_READ_FIFO now return 0xbc and 0x3c or only 0x3c? - */ - s->kbd.start ++; - s->kbd.start &= sizeof(s->kbd.fifo) - 1; - s->kbd.len --; - - return s->kbd.fifo[s->kbd.start]; - case LM832x_CMD_RPT_READ_FIFO: - if (byte >= s->kbd.len) - return 0x00; - - return s->kbd.fifo[(s->kbd.start + byte) & (sizeof(s->kbd.fifo) - 1)]; - - case LM832x_CMD_READ_ERROR: - return s->error; - - case LM832x_CMD_READ_ROTATOR: - return 0; - - case LM832x_CMD_READ_KEY_SIZE: - return s->kbd.size; - - case LM832x_CMD_READ_CFG: - return s->config & 0xf; - - case LM832x_CMD_READ_CLOCK: - return (s->clock & 0xfc) | 2; - - default: - lm_kbd_error(s, ERR_CMDUNK); - fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, reg); - return 0x00; - } - - return ret >> (byte << 3); -} - -static void lm_kbd_write(LM823KbdState *s, int reg, int byte, uint8_t value) -{ - switch (reg) { - case LM832x_CMD_WRITE_CFG: - s->config = value; - /* This must be done whenever s->mux.in is updated (never). */ - if ((s->config >> 1) & 1) /* MUX1EN */ - qemu_set_irq(s->mux.out[0], s->mux.in[0][(s->config >> 0) & 1]); - if ((s->config >> 3) & 1) /* MUX2EN */ - qemu_set_irq(s->mux.out[0], s->mux.in[0][(s->config >> 2) & 1]); - /* TODO: check that this is issued only following the chip reset - * and not in the middle of operation and that it is followed by - * the GPIO ports re-resablishing through WRITE_PORT_SEL and - * WRITE_PORT_STATE (using a timer perhaps) and otherwise output - * warnings. */ - s->status = 0; - lm_kbd_irq_update(s); - s->kbd.len = 0; - s->kbd.start = 0; - s->reg = LM832x_GENERAL_ERROR; - break; - - case LM832x_CMD_RESET: - if (value == 0xaa) - lm_kbd_reset(s); - else - lm_kbd_error(s, ERR_BADPAR); - s->reg = LM832x_GENERAL_ERROR; - break; - - case LM823x_CMD_WRITE_PULL_DOWN: - if (!byte) - s->gpio.pull = value; - else { - s->gpio.pull |= value << 8; - lm_kbd_gpio_update(s); - s->reg = LM832x_GENERAL_ERROR; - } - break; - case LM832x_CMD_WRITE_PORT_SEL: - if (!byte) - s->gpio.dir = value; - else { - s->gpio.dir |= value << 8; - lm_kbd_gpio_update(s); - s->reg = LM832x_GENERAL_ERROR; - } - break; - case LM832x_CMD_WRITE_PORT_STATE: - if (!byte) - s->gpio.mask = value; - else { - s->gpio.mask |= value << 8; - lm_kbd_gpio_update(s); - s->reg = LM832x_GENERAL_ERROR; - } - break; - - case LM832x_CMD_SET_ACTIVE: - s->acttime = value; - s->reg = LM832x_GENERAL_ERROR; - break; - - case LM832x_CMD_SET_DEBOUNCE: - s->kbd.dbnctime = value; - s->reg = LM832x_GENERAL_ERROR; - if (!value) - lm_kbd_error(s, ERR_BADPAR); - break; - - case LM832x_CMD_SET_KEY_SIZE: - s->kbd.size = value; - s->reg = LM832x_GENERAL_ERROR; - if ( - (value & 0xf) < 3 || (value & 0xf) > LM832x_MAX_KPY || - (value >> 4) < 3 || (value >> 4) > LM832x_MAX_KPX) - lm_kbd_error(s, ERR_BADPAR); - break; - - case LM832x_CMD_WRITE_CLOCK: - s->clock = value; - s->reg = LM832x_GENERAL_ERROR; - if ((value & 3) && (value & 3) != 3) { - lm_kbd_error(s, ERR_BADPAR); - fprintf(stderr, "%s: invalid clock setting in RCPWM\n", - __FUNCTION__); - } - /* TODO: Validate that the command is only issued once */ - break; - - case LM832x_CMD_PWM_WRITE: - if (byte == 0) { - if (!(value & 3) || (value >> 2) > 59) { - lm_kbd_error(s, ERR_BADPAR); - s->reg = LM832x_GENERAL_ERROR; - break; - } - - s->pwm.faddr = value; - s->pwm.file[s->pwm.faddr] = 0; - } else if (byte == 1) { - s->pwm.file[s->pwm.faddr] |= value << 8; - } else if (byte == 2) { - s->pwm.file[s->pwm.faddr] |= value << 0; - s->reg = LM832x_GENERAL_ERROR; - } - break; - case LM832x_CMD_PWM_START: - s->reg = LM832x_GENERAL_ERROR; - if (!(value & 3) || (value >> 2) > 59) { - lm_kbd_error(s, ERR_BADPAR); - break; - } - - s->pwm.addr[(value & 3) - 1] = value >> 2; - lm_kbd_pwm_start(s, (value & 3) - 1); - break; - case LM832x_CMD_PWM_STOP: - s->reg = LM832x_GENERAL_ERROR; - if (!(value & 3)) { - lm_kbd_error(s, ERR_BADPAR); - break; - } - - timer_del(s->pwm.tm[(value & 3) - 1]); - break; - - case LM832x_GENERAL_ERROR: - lm_kbd_error(s, ERR_BADPAR); - break; - default: - lm_kbd_error(s, ERR_CMDUNK); - fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, reg); - break; - } -} - -static void lm_i2c_event(I2CSlave *i2c, enum i2c_event event) -{ - LM823KbdState *s = LM8323(i2c); - - switch (event) { - case I2C_START_RECV: - case I2C_START_SEND: - s->i2c_cycle = 0; - s->i2c_dir = (event == I2C_START_SEND); - break; - - default: - break; - } -} - -static int lm_i2c_rx(I2CSlave *i2c) -{ - LM823KbdState *s = LM8323(i2c); - - return lm_kbd_read(s, s->reg, s->i2c_cycle ++); -} - -static int lm_i2c_tx(I2CSlave *i2c, uint8_t data) -{ - LM823KbdState *s = LM8323(i2c); - - if (!s->i2c_cycle) - s->reg = data; - else - lm_kbd_write(s, s->reg, s->i2c_cycle - 1, data); - s->i2c_cycle ++; - - return 0; -} - -static int lm_kbd_post_load(void *opaque, int version_id) -{ - LM823KbdState *s = opaque; - - lm_kbd_irq_update(s); - lm_kbd_gpio_update(s); - - return 0; -} - -static const VMStateDescription vmstate_lm_kbd = { - .name = "LM8323", - .version_id = 0, - .minimum_version_id = 0, - .post_load = lm_kbd_post_load, - .fields = (VMStateField[]) { - VMSTATE_I2C_SLAVE(parent_obj, LM823KbdState), - VMSTATE_UINT8(i2c_dir, LM823KbdState), - VMSTATE_UINT8(i2c_cycle, LM823KbdState), - VMSTATE_UINT8(reg, LM823KbdState), - VMSTATE_UINT8(config, LM823KbdState), - VMSTATE_UINT8(status, LM823KbdState), - VMSTATE_UINT8(acttime, LM823KbdState), - VMSTATE_UINT8(error, LM823KbdState), - VMSTATE_UINT8(clock, LM823KbdState), - VMSTATE_UINT16(gpio.pull, LM823KbdState), - VMSTATE_UINT16(gpio.mask, LM823KbdState), - VMSTATE_UINT16(gpio.dir, LM823KbdState), - VMSTATE_UINT16(gpio.level, LM823KbdState), - VMSTATE_UINT8(kbd.dbnctime, LM823KbdState), - VMSTATE_UINT8(kbd.size, LM823KbdState), - VMSTATE_UINT8(kbd.start, LM823KbdState), - VMSTATE_UINT8(kbd.len, LM823KbdState), - VMSTATE_BUFFER(kbd.fifo, LM823KbdState), - VMSTATE_UINT16_ARRAY(pwm.file, LM823KbdState, 256), - VMSTATE_UINT8(pwm.faddr, LM823KbdState), - VMSTATE_BUFFER(pwm.addr, LM823KbdState), - VMSTATE_TIMER_PTR_ARRAY(pwm.tm, LM823KbdState, 3), - VMSTATE_END_OF_LIST() - } -}; - - -static int lm8323_init(I2CSlave *i2c) -{ - LM823KbdState *s = LM8323(i2c); - - s->model = 0x8323; - s->pwm.tm[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, lm_kbd_pwm0_tick, s); - s->pwm.tm[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, lm_kbd_pwm1_tick, s); - s->pwm.tm[2] = timer_new_ns(QEMU_CLOCK_VIRTUAL, lm_kbd_pwm2_tick, s); - qdev_init_gpio_out(DEVICE(i2c), &s->nirq, 1); - - lm_kbd_reset(s); - - qemu_register_reset((void *) lm_kbd_reset, s); - return 0; -} - -void lm832x_key_event(DeviceState *dev, int key, int state) -{ - LM823KbdState *s = LM8323(dev); - - if ((s->status & INT_ERROR) && (s->error & ERR_FIFOOVR)) - return; - - if (s->kbd.len >= sizeof(s->kbd.fifo)) { - lm_kbd_error(s, ERR_FIFOOVR); - return; - } - - s->kbd.fifo[(s->kbd.start + s->kbd.len ++) & (sizeof(s->kbd.fifo) - 1)] = - key | (state << 7); - - /* We never set ERR_KEYOVR because we support multiple keys fine. */ - s->status |= INT_KEYPAD; - lm_kbd_irq_update(s); -} - -static void lm8323_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - - k->init = lm8323_init; - k->event = lm_i2c_event; - k->recv = lm_i2c_rx; - k->send = lm_i2c_tx; - dc->vmsd = &vmstate_lm_kbd; -} - -static const TypeInfo lm8323_info = { - .name = TYPE_LM8323, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(LM823KbdState), - .class_init = lm8323_class_init, -}; - -static void lm832x_register_types(void) -{ - type_register_static(&lm8323_info); -} - -type_init(lm832x_register_types) diff --git a/qemu/hw/input/milkymist-softusb.c b/qemu/hw/input/milkymist-softusb.c deleted file mode 100644 index 40dfca157..000000000 --- a/qemu/hw/input/milkymist-softusb.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * QEMU model of the Milkymist SoftUSB block. - * - * Copyright (c) 2010 Michael Walle - * - * 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 . - * - * - * Specification available at: - * not available yet - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "ui/console.h" -#include "hw/input/hid.h" -#include "qemu/error-report.h" - -enum { - R_CTRL = 0, - R_MAX -}; - -enum { - CTRL_RESET = (1<<0), -}; - -#define COMLOC_DEBUG_PRODUCE 0x1000 -#define COMLOC_DEBUG_BASE 0x1001 -#define COMLOC_MEVT_PRODUCE 0x1101 -#define COMLOC_MEVT_BASE 0x1102 -#define COMLOC_KEVT_PRODUCE 0x1142 -#define COMLOC_KEVT_BASE 0x1143 - -#define TYPE_MILKYMIST_SOFTUSB "milkymist-softusb" -#define MILKYMIST_SOFTUSB(obj) \ - OBJECT_CHECK(MilkymistSoftUsbState, (obj), TYPE_MILKYMIST_SOFTUSB) - -struct MilkymistSoftUsbState { - SysBusDevice parent_obj; - - HIDState hid_kbd; - HIDState hid_mouse; - - MemoryRegion regs_region; - MemoryRegion pmem; - MemoryRegion dmem; - qemu_irq irq; - - void *pmem_ptr; - void *dmem_ptr; - - /* device properties */ - uint32_t pmem_size; - uint32_t dmem_size; - - /* device registers */ - uint32_t regs[R_MAX]; - - /* mouse state */ - uint8_t mouse_hid_buffer[4]; - - /* keyboard state */ - uint8_t kbd_hid_buffer[8]; -}; -typedef struct MilkymistSoftUsbState MilkymistSoftUsbState; - -static uint64_t softusb_read(void *opaque, hwaddr addr, - unsigned size) -{ - MilkymistSoftUsbState *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_CTRL: - r = s->regs[addr]; - break; - - default: - error_report("milkymist_softusb: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_milkymist_softusb_memory_read(addr << 2, r); - - return r; -} - -static void -softusb_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - MilkymistSoftUsbState *s = opaque; - - trace_milkymist_softusb_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_CTRL: - s->regs[addr] = value; - break; - - default: - error_report("milkymist_softusb: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } -} - -static const MemoryRegionOps softusb_mmio_ops = { - .read = softusb_read, - .write = softusb_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static inline void softusb_read_dmem(MilkymistSoftUsbState *s, - uint32_t offset, uint8_t *buf, uint32_t len) -{ - if (offset + len >= s->dmem_size) { - error_report("milkymist_softusb: read dmem out of bounds " - "at offset 0x%x, len %d", offset, len); - memset(buf, 0, len); - return; - } - - memcpy(buf, s->dmem_ptr + offset, len); -} - -static inline void softusb_write_dmem(MilkymistSoftUsbState *s, - uint32_t offset, uint8_t *buf, uint32_t len) -{ - if (offset + len >= s->dmem_size) { - error_report("milkymist_softusb: write dmem out of bounds " - "at offset 0x%x, len %d", offset, len); - return; - } - - memcpy(s->dmem_ptr + offset, buf, len); -} - -static void softusb_mouse_changed(MilkymistSoftUsbState *s) -{ - uint8_t m; - - softusb_read_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1); - trace_milkymist_softusb_mevt(m); - softusb_write_dmem(s, COMLOC_MEVT_BASE + 4 * m, s->mouse_hid_buffer, 4); - m = (m + 1) & 0xf; - softusb_write_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1); - - trace_milkymist_softusb_pulse_irq(); - qemu_irq_pulse(s->irq); -} - -static void softusb_kbd_changed(MilkymistSoftUsbState *s) -{ - uint8_t m; - - softusb_read_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1); - trace_milkymist_softusb_kevt(m); - softusb_write_dmem(s, COMLOC_KEVT_BASE + 8 * m, s->kbd_hid_buffer, 8); - m = (m + 1) & 0x7; - softusb_write_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1); - - trace_milkymist_softusb_pulse_irq(); - qemu_irq_pulse(s->irq); -} - -static void softusb_kbd_hid_datain(HIDState *hs) -{ - MilkymistSoftUsbState *s = container_of(hs, MilkymistSoftUsbState, hid_kbd); - int len; - - /* if device is in reset, do nothing */ - if (s->regs[R_CTRL] & CTRL_RESET) { - return; - } - - while (hid_has_events(hs)) { - len = hid_keyboard_poll(hs, s->kbd_hid_buffer, - sizeof(s->kbd_hid_buffer)); - - if (len == 8) { - softusb_kbd_changed(s); - } - } -} - -static void softusb_mouse_hid_datain(HIDState *hs) -{ - MilkymistSoftUsbState *s = - container_of(hs, MilkymistSoftUsbState, hid_mouse); - int len; - - /* if device is in reset, do nothing */ - if (s->regs[R_CTRL] & CTRL_RESET) { - return; - } - - while (hid_has_events(hs)) { - len = hid_pointer_poll(hs, s->mouse_hid_buffer, - sizeof(s->mouse_hid_buffer)); - - if (len == 4) { - softusb_mouse_changed(s); - } - } -} - -static void milkymist_softusb_reset(DeviceState *d) -{ - MilkymistSoftUsbState *s = MILKYMIST_SOFTUSB(d); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } - memset(s->kbd_hid_buffer, 0, sizeof(s->kbd_hid_buffer)); - memset(s->mouse_hid_buffer, 0, sizeof(s->mouse_hid_buffer)); - - hid_reset(&s->hid_kbd); - hid_reset(&s->hid_mouse); - - /* defaults */ - s->regs[R_CTRL] = CTRL_RESET; -} - -static int milkymist_softusb_init(SysBusDevice *dev) -{ - MilkymistSoftUsbState *s = MILKYMIST_SOFTUSB(dev); - - sysbus_init_irq(dev, &s->irq); - - memory_region_init_io(&s->regs_region, OBJECT(s), &softusb_mmio_ops, s, - "milkymist-softusb", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); - - /* register pmem and dmem */ - memory_region_init_ram(&s->pmem, OBJECT(s), "milkymist-softusb.pmem", - 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_fatal); - vmstate_register_ram_global(&s->dmem); - s->dmem_ptr = memory_region_get_ram_ptr(&s->dmem); - sysbus_init_mmio(dev, &s->dmem); - - hid_init(&s->hid_kbd, HID_KEYBOARD, softusb_kbd_hid_datain); - hid_init(&s->hid_mouse, HID_MOUSE, softusb_mouse_hid_datain); - - return 0; -} - -static const VMStateDescription vmstate_milkymist_softusb = { - .name = "milkymist-softusb", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, MilkymistSoftUsbState, R_MAX), - VMSTATE_HID_KEYBOARD_DEVICE(hid_kbd, MilkymistSoftUsbState), - VMSTATE_HID_POINTER_DEVICE(hid_mouse, MilkymistSoftUsbState), - VMSTATE_BUFFER(kbd_hid_buffer, MilkymistSoftUsbState), - VMSTATE_BUFFER(mouse_hid_buffer, MilkymistSoftUsbState), - VMSTATE_END_OF_LIST() - } -}; - -static Property milkymist_softusb_properties[] = { - DEFINE_PROP_UINT32("pmem_size", MilkymistSoftUsbState, pmem_size, 0x00001000), - DEFINE_PROP_UINT32("dmem_size", MilkymistSoftUsbState, dmem_size, 0x00002000), - DEFINE_PROP_END_OF_LIST(), -}; - -static void milkymist_softusb_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = milkymist_softusb_init; - dc->reset = milkymist_softusb_reset; - dc->vmsd = &vmstate_milkymist_softusb; - dc->props = milkymist_softusb_properties; -} - -static const TypeInfo milkymist_softusb_info = { - .name = TYPE_MILKYMIST_SOFTUSB, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MilkymistSoftUsbState), - .class_init = milkymist_softusb_class_init, -}; - -static void milkymist_softusb_register_types(void) -{ - type_register_static(&milkymist_softusb_info); -} - -type_init(milkymist_softusb_register_types) diff --git a/qemu/hw/input/pckbd.c b/qemu/hw/input/pckbd.c deleted file mode 100644 index 1d932ec19..000000000 --- a/qemu/hw/input/pckbd.c +++ /dev/null @@ -1,595 +0,0 @@ -/* - * QEMU PC keyboard emulation - * - * Copyright (c) 2003 Fabrice Bellard - * - * 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/isa/isa.h" -#include "hw/i386/pc.h" -#include "hw/input/ps2.h" -#include "sysemu/sysemu.h" - -/* debug PC keyboard */ -//#define DEBUG_KBD -#ifdef DEBUG_KBD -#define DPRINTF(fmt, ...) \ - do { printf("KBD: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) -#endif - -/* Keyboard Controller Commands */ -#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */ -#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */ -#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */ -#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */ -#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */ -#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */ -#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */ -#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */ -#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */ -#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */ -#define KBD_CCMD_READ_INPORT 0xC0 /* read input port */ -#define KBD_CCMD_READ_OUTPORT 0xD0 /* read output port */ -#define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */ -#define KBD_CCMD_WRITE_OBUF 0xD2 -#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if - initiated by the auxiliary device */ -#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */ -#define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */ -#define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */ -#define KBD_CCMD_PULSE_BITS_3_0 0xF0 /* Pulse bits 3-0 of the output port P2. */ -#define KBD_CCMD_RESET 0xFE /* Pulse bit 0 of the output port P2 = CPU reset. */ -#define KBD_CCMD_NO_OP 0xFF /* Pulse no bits of the output port P2. */ - -/* Keyboard Commands */ -#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */ -#define KBD_CMD_ECHO 0xEE -#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */ -#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */ -#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */ -#define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */ -#define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */ -#define KBD_CMD_RESET 0xFF /* Reset */ - -/* Keyboard Replies */ -#define KBD_REPLY_POR 0xAA /* Power on reset */ -#define KBD_REPLY_ACK 0xFA /* Command ACK */ -#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */ - -/* Status Register Bits */ -#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */ -#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */ -#define KBD_STAT_SELFTEST 0x04 /* Self test successful */ -#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */ -#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */ -#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */ -#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */ -#define KBD_STAT_PERR 0x80 /* Parity error */ - -/* Controller Mode Register Bits */ -#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */ -#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */ -#define KBD_MODE_SYS 0x04 /* The system flag (?) */ -#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */ -#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */ -#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */ -#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */ -#define KBD_MODE_RFU 0x80 - -/* Output Port Bits */ -#define KBD_OUT_RESET 0x01 /* 1=normal mode, 0=reset */ -#define KBD_OUT_A20 0x02 /* x86 only */ -#define KBD_OUT_OBF 0x10 /* Keyboard output buffer full */ -#define KBD_OUT_MOUSE_OBF 0x20 /* Mouse output buffer full */ - -/* OSes typically write 0xdd/0xdf to turn the A20 line off and on. - * We make the default value of the outport include these four bits, - * so that the subsection is rarely necessary. - */ -#define KBD_OUT_ONES 0xcc - -/* Mouse Commands */ -#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */ -#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */ -#define AUX_SET_RES 0xE8 /* Set resolution */ -#define AUX_GET_SCALE 0xE9 /* Get scaling factor */ -#define AUX_SET_STREAM 0xEA /* Set stream mode */ -#define AUX_POLL 0xEB /* Poll */ -#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */ -#define AUX_SET_WRAP 0xEE /* Set wrap mode */ -#define AUX_SET_REMOTE 0xF0 /* Set remote mode */ -#define AUX_GET_TYPE 0xF2 /* Get type */ -#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */ -#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */ -#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */ -#define AUX_SET_DEFAULT 0xF6 -#define AUX_RESET 0xFF /* Reset aux device */ -#define AUX_ACK 0xFA /* Command byte ACK. */ - -#define MOUSE_STATUS_REMOTE 0x40 -#define MOUSE_STATUS_ENABLED 0x20 -#define MOUSE_STATUS_SCALE21 0x10 - -#define KBD_PENDING_KBD 1 -#define KBD_PENDING_AUX 2 - -typedef struct KBDState { - uint8_t write_cmd; /* if non zero, write data to port 60 is expected */ - uint8_t status; - uint8_t mode; - uint8_t outport; - bool outport_present; - /* Bitmask of devices with data available. */ - uint8_t pending; - void *kbd; - void *mouse; - - qemu_irq irq_kbd; - qemu_irq irq_mouse; - qemu_irq *a20_out; - hwaddr mask; -} KBDState; - -/* update irq and KBD_STAT_[MOUSE_]OBF */ -/* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be - incorrect, but it avoids having to simulate exact delays */ -static void kbd_update_irq(KBDState *s) -{ - int irq_kbd_level, irq_mouse_level; - - irq_kbd_level = 0; - irq_mouse_level = 0; - s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF); - s->outport &= ~(KBD_OUT_OBF | KBD_OUT_MOUSE_OBF); - if (s->pending) { - s->status |= KBD_STAT_OBF; - s->outport |= KBD_OUT_OBF; - /* kbd data takes priority over aux data. */ - if (s->pending == KBD_PENDING_AUX) { - s->status |= KBD_STAT_MOUSE_OBF; - s->outport |= KBD_OUT_MOUSE_OBF; - if (s->mode & KBD_MODE_MOUSE_INT) - irq_mouse_level = 1; - } else { - if ((s->mode & KBD_MODE_KBD_INT) && - !(s->mode & KBD_MODE_DISABLE_KBD)) - irq_kbd_level = 1; - } - } - qemu_set_irq(s->irq_kbd, irq_kbd_level); - qemu_set_irq(s->irq_mouse, irq_mouse_level); -} - -static void kbd_update_kbd_irq(void *opaque, int level) -{ - KBDState *s = (KBDState *)opaque; - - if (level) - s->pending |= KBD_PENDING_KBD; - else - s->pending &= ~KBD_PENDING_KBD; - kbd_update_irq(s); -} - -static void kbd_update_aux_irq(void *opaque, int level) -{ - KBDState *s = (KBDState *)opaque; - - if (level) - s->pending |= KBD_PENDING_AUX; - else - s->pending &= ~KBD_PENDING_AUX; - kbd_update_irq(s); -} - -static uint64_t kbd_read_status(void *opaque, hwaddr addr, - unsigned size) -{ - KBDState *s = opaque; - int val; - val = s->status; - DPRINTF("kbd: read status=0x%02x\n", val); - return val; -} - -static void kbd_queue(KBDState *s, int b, int aux) -{ - if (aux) - ps2_queue(s->mouse, b); - else - ps2_queue(s->kbd, b); -} - -static void outport_write(KBDState *s, uint32_t val) -{ - DPRINTF("kbd: write outport=0x%02x\n", val); - s->outport = val; - if (s->a20_out) { - qemu_set_irq(*s->a20_out, (val >> 1) & 1); - } - if (!(val & 1)) { - qemu_system_reset_request(); - } -} - -static void kbd_write_command(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - KBDState *s = opaque; - - DPRINTF("kbd: write cmd=0x%02" PRIx64 "\n", val); - - /* Bits 3-0 of the output port P2 of the keyboard controller may be pulsed - * low for approximately 6 micro seconds. Bits 3-0 of the KBD_CCMD_PULSE - * command specify the output port bits to be pulsed. - * 0: Bit should be pulsed. 1: Bit should not be modified. - * The only useful version of this command is pulsing bit 0, - * which does a CPU reset. - */ - if((val & KBD_CCMD_PULSE_BITS_3_0) == KBD_CCMD_PULSE_BITS_3_0) { - if(!(val & 1)) - val = KBD_CCMD_RESET; - else - val = KBD_CCMD_NO_OP; - } - - switch(val) { - case KBD_CCMD_READ_MODE: - kbd_queue(s, s->mode, 0); - break; - case KBD_CCMD_WRITE_MODE: - case KBD_CCMD_WRITE_OBUF: - case KBD_CCMD_WRITE_AUX_OBUF: - case KBD_CCMD_WRITE_MOUSE: - case KBD_CCMD_WRITE_OUTPORT: - s->write_cmd = val; - break; - case KBD_CCMD_MOUSE_DISABLE: - s->mode |= KBD_MODE_DISABLE_MOUSE; - break; - case KBD_CCMD_MOUSE_ENABLE: - s->mode &= ~KBD_MODE_DISABLE_MOUSE; - break; - case KBD_CCMD_TEST_MOUSE: - kbd_queue(s, 0x00, 0); - break; - case KBD_CCMD_SELF_TEST: - s->status |= KBD_STAT_SELFTEST; - kbd_queue(s, 0x55, 0); - break; - case KBD_CCMD_KBD_TEST: - kbd_queue(s, 0x00, 0); - break; - case KBD_CCMD_KBD_DISABLE: - s->mode |= KBD_MODE_DISABLE_KBD; - kbd_update_irq(s); - break; - case KBD_CCMD_KBD_ENABLE: - s->mode &= ~KBD_MODE_DISABLE_KBD; - kbd_update_irq(s); - break; - case KBD_CCMD_READ_INPORT: - kbd_queue(s, 0x80, 0); - break; - case KBD_CCMD_READ_OUTPORT: - kbd_queue(s, s->outport, 0); - break; - case KBD_CCMD_ENABLE_A20: - if (s->a20_out) { - qemu_irq_raise(*s->a20_out); - } - s->outport |= KBD_OUT_A20; - break; - case KBD_CCMD_DISABLE_A20: - if (s->a20_out) { - qemu_irq_lower(*s->a20_out); - } - s->outport &= ~KBD_OUT_A20; - break; - case KBD_CCMD_RESET: - qemu_system_reset_request(); - break; - case KBD_CCMD_NO_OP: - /* ignore that */ - break; - default: - fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", (int)val); - break; - } -} - -static uint64_t kbd_read_data(void *opaque, hwaddr addr, - unsigned size) -{ - KBDState *s = opaque; - uint32_t val; - - if (s->pending == KBD_PENDING_AUX) - val = ps2_read_data(s->mouse); - else - val = ps2_read_data(s->kbd); - - DPRINTF("kbd: read data=0x%02x\n", val); - return val; -} - -static void kbd_write_data(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - KBDState *s = opaque; - - DPRINTF("kbd: write data=0x%02" PRIx64 "\n", val); - - switch(s->write_cmd) { - case 0: - ps2_write_keyboard(s->kbd, val); - break; - case KBD_CCMD_WRITE_MODE: - s->mode = val; - ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0); - /* ??? */ - kbd_update_irq(s); - break; - case KBD_CCMD_WRITE_OBUF: - kbd_queue(s, val, 0); - break; - case KBD_CCMD_WRITE_AUX_OBUF: - kbd_queue(s, val, 1); - break; - case KBD_CCMD_WRITE_OUTPORT: - outport_write(s, val); - break; - case KBD_CCMD_WRITE_MOUSE: - ps2_write_mouse(s->mouse, val); - break; - default: - break; - } - s->write_cmd = 0; -} - -static void kbd_reset(void *opaque) -{ - KBDState *s = opaque; - - s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT; - s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED; - s->outport = KBD_OUT_RESET | KBD_OUT_A20 | KBD_OUT_ONES; - s->outport_present = false; -} - -static uint8_t kbd_outport_default(KBDState *s) -{ - return KBD_OUT_RESET | KBD_OUT_A20 | KBD_OUT_ONES - | (s->status & KBD_STAT_OBF ? KBD_OUT_OBF : 0) - | (s->status & KBD_STAT_MOUSE_OBF ? KBD_OUT_MOUSE_OBF : 0); -} - -static int kbd_outport_post_load(void *opaque, int version_id) -{ - KBDState *s = opaque; - s->outport_present = true; - return 0; -} - -static bool kbd_outport_needed(void *opaque) -{ - KBDState *s = opaque; - return s->outport != kbd_outport_default(s); -} - -static const VMStateDescription vmstate_kbd_outport = { - .name = "pckbd_outport", - .version_id = 1, - .minimum_version_id = 1, - .post_load = kbd_outport_post_load, - .needed = kbd_outport_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(outport, KBDState), - VMSTATE_END_OF_LIST() - } -}; - -static int kbd_post_load(void *opaque, int version_id) -{ - KBDState *s = opaque; - if (!s->outport_present) { - s->outport = kbd_outport_default(s); - } - s->outport_present = false; - return 0; -} - -static const VMStateDescription vmstate_kbd = { - .name = "pckbd", - .version_id = 3, - .minimum_version_id = 3, - .post_load = kbd_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(write_cmd, KBDState), - VMSTATE_UINT8(status, KBDState), - VMSTATE_UINT8(mode, KBDState), - VMSTATE_UINT8(pending, KBDState), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_kbd_outport, - NULL - } -}; - -/* Memory mapped interface */ -static uint32_t kbd_mm_readb (void *opaque, hwaddr addr) -{ - KBDState *s = opaque; - - if (addr & s->mask) - return kbd_read_status(s, 0, 1) & 0xff; - else - return kbd_read_data(s, 0, 1) & 0xff; -} - -static void kbd_mm_writeb (void *opaque, hwaddr addr, uint32_t value) -{ - KBDState *s = opaque; - - if (addr & s->mask) - kbd_write_command(s, 0, value & 0xff, 1); - else - kbd_write_data(s, 0, value & 0xff, 1); -} - -static const MemoryRegionOps i8042_mmio_ops = { - .endianness = DEVICE_NATIVE_ENDIAN, - .old_mmio = { - .read = { kbd_mm_readb, kbd_mm_readb, kbd_mm_readb }, - .write = { kbd_mm_writeb, kbd_mm_writeb, kbd_mm_writeb }, - }, -}; - -void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, - MemoryRegion *region, ram_addr_t size, - hwaddr mask) -{ - KBDState *s = g_malloc0(sizeof(KBDState)); - - s->irq_kbd = kbd_irq; - s->irq_mouse = mouse_irq; - s->mask = mask; - - vmstate_register(NULL, 0, &vmstate_kbd, s); - - memory_region_init_io(region, NULL, &i8042_mmio_ops, s, "i8042", size); - - s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s); - s->mouse = ps2_mouse_init(kbd_update_aux_irq, s); - qemu_register_reset(kbd_reset, s); -} - -#define TYPE_I8042 "i8042" -#define I8042(obj) OBJECT_CHECK(ISAKBDState, (obj), TYPE_I8042) - -typedef struct ISAKBDState { - ISADevice parent_obj; - - KBDState kbd; - MemoryRegion io[2]; -} ISAKBDState; - -void i8042_isa_mouse_fake_event(void *opaque) -{ - ISADevice *dev = opaque; - ISAKBDState *isa = I8042(dev); - KBDState *s = &isa->kbd; - - ps2_mouse_fake_event(s->mouse); -} - -void i8042_setup_a20_line(ISADevice *dev, qemu_irq *a20_out) -{ - ISAKBDState *isa = I8042(dev); - KBDState *s = &isa->kbd; - - s->a20_out = a20_out; -} - -static const VMStateDescription vmstate_kbd_isa = { - .name = "pckbd", - .version_id = 3, - .minimum_version_id = 3, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(kbd, ISAKBDState, 0, vmstate_kbd, KBDState), - VMSTATE_END_OF_LIST() - } -}; - -static const MemoryRegionOps i8042_data_ops = { - .read = kbd_read_data, - .write = kbd_write_data, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps i8042_cmd_ops = { - .read = kbd_read_status, - .write = kbd_write_command, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void i8042_initfn(Object *obj) -{ - ISAKBDState *isa_s = I8042(obj); - KBDState *s = &isa_s->kbd; - - memory_region_init_io(isa_s->io + 0, obj, &i8042_data_ops, s, - "i8042-data", 1); - memory_region_init_io(isa_s->io + 1, obj, &i8042_cmd_ops, s, - "i8042-cmd", 1); -} - -static void i8042_realizefn(DeviceState *dev, Error **errp) -{ - ISADevice *isadev = ISA_DEVICE(dev); - ISAKBDState *isa_s = I8042(dev); - KBDState *s = &isa_s->kbd; - - isa_init_irq(isadev, &s->irq_kbd, 1); - isa_init_irq(isadev, &s->irq_mouse, 12); - - isa_register_ioport(isadev, isa_s->io + 0, 0x60); - isa_register_ioport(isadev, isa_s->io + 1, 0x64); - - s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s); - s->mouse = ps2_mouse_init(kbd_update_aux_irq, s); - qemu_register_reset(kbd_reset, s); -} - -static void i8042_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = i8042_realizefn; - dc->vmsd = &vmstate_kbd_isa; -} - -static const TypeInfo i8042_info = { - .name = TYPE_I8042, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(ISAKBDState), - .instance_init = i8042_initfn, - .class_init = i8042_class_initfn, -}; - -static void i8042_register_types(void) -{ - type_register_static(&i8042_info); -} - -type_init(i8042_register_types) diff --git a/qemu/hw/input/pl050.c b/qemu/hw/input/pl050.c deleted file mode 100644 index 3092b0fe3..000000000 --- a/qemu/hw/input/pl050.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Arm PrimeCell PL050 Keyboard / Mouse Interface - * - * Copyright (c) 2006-2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "hw/input/ps2.h" - -#define TYPE_PL050 "pl050" -#define PL050(obj) OBJECT_CHECK(PL050State, (obj), TYPE_PL050) - -typedef struct PL050State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - void *dev; - uint32_t cr; - uint32_t clk; - uint32_t last; - int pending; - qemu_irq irq; - bool is_mouse; -} PL050State; - -static const VMStateDescription vmstate_pl050 = { - .name = "pl050", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32(cr, PL050State), - VMSTATE_UINT32(clk, PL050State), - VMSTATE_UINT32(last, PL050State), - VMSTATE_INT32(pending, PL050State), - VMSTATE_END_OF_LIST() - } -}; - -#define PL050_TXEMPTY (1 << 6) -#define PL050_TXBUSY (1 << 5) -#define PL050_RXFULL (1 << 4) -#define PL050_RXBUSY (1 << 3) -#define PL050_RXPARITY (1 << 2) -#define PL050_KMIC (1 << 1) -#define PL050_KMID (1 << 0) - -static const unsigned char pl050_id[] = -{ 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; - -static void pl050_update(void *opaque, int level) -{ - PL050State *s = (PL050State *)opaque; - int raise; - - s->pending = level; - raise = (s->pending && (s->cr & 0x10) != 0) - || (s->cr & 0x08) != 0; - qemu_set_irq(s->irq, raise); -} - -static uint64_t pl050_read(void *opaque, hwaddr offset, - unsigned size) -{ - PL050State *s = (PL050State *)opaque; - if (offset >= 0xfe0 && offset < 0x1000) - return pl050_id[(offset - 0xfe0) >> 2]; - - switch (offset >> 2) { - case 0: /* KMICR */ - return s->cr; - case 1: /* KMISTAT */ - { - uint8_t val; - uint32_t stat; - - val = s->last; - val = val ^ (val >> 4); - val = val ^ (val >> 2); - val = (val ^ (val >> 1)) & 1; - - stat = PL050_TXEMPTY; - if (val) - stat |= PL050_RXPARITY; - if (s->pending) - stat |= PL050_RXFULL; - - return stat; - } - case 2: /* KMIDATA */ - if (s->pending) - s->last = ps2_read_data(s->dev); - return s->last; - case 3: /* KMICLKDIV */ - return s->clk; - case 4: /* KMIIR */ - return s->pending | 2; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pl050_read: Bad offset %x\n", (int)offset); - return 0; - } -} - -static void pl050_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PL050State *s = (PL050State *)opaque; - switch (offset >> 2) { - case 0: /* KMICR */ - s->cr = value; - pl050_update(s, s->pending); - /* ??? Need to implement the enable/disable bit. */ - break; - case 2: /* KMIDATA */ - /* ??? This should toggle the TX interrupt line. */ - /* ??? This means kbd/mouse can block each other. */ - if (s->is_mouse) { - ps2_write_mouse(s->dev, value); - } else { - ps2_write_keyboard(s->dev, value); - } - break; - case 3: /* KMICLKDIV */ - s->clk = value; - return; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pl050_write: Bad offset %x\n", (int)offset); - } -} -static const MemoryRegionOps pl050_ops = { - .read = pl050_read, - .write = pl050_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int pl050_initfn(SysBusDevice *dev) -{ - PL050State *s = PL050(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &pl050_ops, s, "pl050", 0x1000); - sysbus_init_mmio(dev, &s->iomem); - sysbus_init_irq(dev, &s->irq); - if (s->is_mouse) { - s->dev = ps2_mouse_init(pl050_update, s); - } else { - s->dev = ps2_kbd_init(pl050_update, s); - } - return 0; -} - -static void pl050_keyboard_init(Object *obj) -{ - PL050State *s = PL050(obj); - - s->is_mouse = false; -} - -static void pl050_mouse_init(Object *obj) -{ - PL050State *s = PL050(obj); - - s->is_mouse = true; -} - -static const TypeInfo pl050_kbd_info = { - .name = "pl050_keyboard", - .parent = TYPE_PL050, - .instance_init = pl050_keyboard_init, -}; - -static const TypeInfo pl050_mouse_info = { - .name = "pl050_mouse", - .parent = TYPE_PL050, - .instance_init = pl050_mouse_init, -}; - -static void pl050_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(oc); - - sdc->init = pl050_initfn; - dc->vmsd = &vmstate_pl050; -} - -static const TypeInfo pl050_type_info = { - .name = TYPE_PL050, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PL050State), - .abstract = true, - .class_init = pl050_class_init, -}; - -static void pl050_register_types(void) -{ - type_register_static(&pl050_type_info); - type_register_static(&pl050_kbd_info); - type_register_static(&pl050_mouse_info); -} - -type_init(pl050_register_types) diff --git a/qemu/hw/input/ps2.c b/qemu/hw/input/ps2.c deleted file mode 100644 index a8aa36f5c..000000000 --- a/qemu/hw/input/ps2.c +++ /dev/null @@ -1,813 +0,0 @@ -/* - * QEMU PS/2 keyboard/mouse emulation - * - * Copyright (c) 2003 Fabrice Bellard - * - * 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/input/ps2.h" -#include "ui/console.h" -#include "ui/input.h" -#include "sysemu/sysemu.h" - -#include "trace.h" - -/* debug PC keyboard */ -//#define DEBUG_KBD - -/* debug PC keyboard : only mouse */ -//#define DEBUG_MOUSE - -/* Keyboard Commands */ -#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */ -#define KBD_CMD_ECHO 0xEE -#define KBD_CMD_SCANCODE 0xF0 /* Get/set scancode set */ -#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */ -#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */ -#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */ -#define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */ -#define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */ -#define KBD_CMD_RESET 0xFF /* Reset */ - -/* Keyboard Replies */ -#define KBD_REPLY_POR 0xAA /* Power on reset */ -#define KBD_REPLY_ID 0xAB /* Keyboard ID */ -#define KBD_REPLY_ACK 0xFA /* Command ACK */ -#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */ - -/* Mouse Commands */ -#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */ -#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */ -#define AUX_SET_RES 0xE8 /* Set resolution */ -#define AUX_GET_SCALE 0xE9 /* Get scaling factor */ -#define AUX_SET_STREAM 0xEA /* Set stream mode */ -#define AUX_POLL 0xEB /* Poll */ -#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */ -#define AUX_SET_WRAP 0xEE /* Set wrap mode */ -#define AUX_SET_REMOTE 0xF0 /* Set remote mode */ -#define AUX_GET_TYPE 0xF2 /* Get type */ -#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */ -#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */ -#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */ -#define AUX_SET_DEFAULT 0xF6 -#define AUX_RESET 0xFF /* Reset aux device */ -#define AUX_ACK 0xFA /* Command byte ACK. */ - -#define MOUSE_STATUS_REMOTE 0x40 -#define MOUSE_STATUS_ENABLED 0x20 -#define MOUSE_STATUS_SCALE21 0x10 - -#define PS2_QUEUE_SIZE 16 /* Buffer size required by PS/2 protocol */ - -typedef struct { - /* Keep the data array 256 bytes long, which compatibility - with older qemu versions. */ - uint8_t data[256]; - int rptr, wptr, count; -} PS2Queue; - -typedef struct { - PS2Queue queue; - int32_t write_cmd; - void (*update_irq)(void *, int); - void *update_arg; -} PS2State; - -typedef struct { - PS2State common; - int scan_enabled; - /* QEMU uses translated PC scancodes internally. To avoid multiple - conversions we do the translation (if any) in the PS/2 emulation - not the keyboard controller. */ - int translate; - int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */ - int ledstate; -} PS2KbdState; - -typedef struct { - PS2State common; - uint8_t mouse_status; - uint8_t mouse_resolution; - uint8_t mouse_sample_rate; - uint8_t mouse_wrap; - uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */ - uint8_t mouse_detect_state; - int mouse_dx; /* current values, needed for 'poll' mode */ - int mouse_dy; - int mouse_dz; - uint8_t mouse_buttons; -} PS2MouseState; - -/* Table to convert from PC scancodes to raw scancodes. */ -static const unsigned char ps2_raw_keycode[128] = { - 0, 118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85, 102, 13, - 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, - 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, - 50, 49, 58, 65, 73, 74, 89, 124, 17, 41, 88, 5, 6, 4, 12, 3, - 11, 2, 10, 1, 9, 119, 126, 108, 117, 125, 123, 107, 115, 116, 121, 105, -114, 122, 112, 113, 127, 96, 97, 120, 7, 15, 23, 31, 39, 47, 55, 63, - 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87, 111, - 19, 25, 57, 81, 83, 92, 95, 98, 99, 100, 101, 103, 104, 106, 109, 110 -}; -static const unsigned char ps2_raw_keycode_set3[128] = { - 0, 8, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85, 102, 13, - 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 17, 28, 27, - 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 92, 26, 34, 33, 42, - 50, 49, 58, 65, 73, 74, 89, 126, 25, 41, 20, 7, 15, 23, 31, 39, - 47, 2, 63, 71, 79, 118, 95, 108, 117, 125, 132, 107, 115, 116, 124, 105, -114, 122, 112, 113, 127, 96, 97, 86, 94, 15, 23, 31, 39, 47, 55, 63, - 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87, 111, - 19, 25, 57, 81, 83, 92, 95, 98, 99, 100, 101, 103, 104, 106, 109, 110 -}; - -void ps2_queue(void *opaque, int b) -{ - PS2State *s = (PS2State *)opaque; - PS2Queue *q = &s->queue; - - if (q->count >= PS2_QUEUE_SIZE - 1) - return; - q->data[q->wptr] = b; - if (++q->wptr == PS2_QUEUE_SIZE) - q->wptr = 0; - q->count++; - s->update_irq(s->update_arg, 1); -} - -/* - keycode is expressed as follow: - bit 7 - 0 key pressed, 1 = key released - bits 6-0 - translated scancode set 2 - */ -static void ps2_put_keycode(void *opaque, int keycode) -{ - PS2KbdState *s = opaque; - - trace_ps2_put_keycode(opaque, keycode); - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); - /* XXX: add support for scancode set 1 */ - if (!s->translate && keycode < 0xe0 && s->scancode_set > 1) { - if (keycode & 0x80) { - ps2_queue(&s->common, 0xf0); - } - if (s->scancode_set == 2) { - keycode = ps2_raw_keycode[keycode & 0x7f]; - } else if (s->scancode_set == 3) { - keycode = ps2_raw_keycode_set3[keycode & 0x7f]; - } - } - ps2_queue(&s->common, keycode); -} - -static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) -{ - 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(key->key, - key->down, - scancodes); - for (i = 0; i < count; i++) { - ps2_put_keycode(s, scancodes[i]); - } -} - -uint32_t ps2_read_data(void *opaque) -{ - PS2State *s = (PS2State *)opaque; - PS2Queue *q; - int val, index; - - trace_ps2_read_data(opaque); - q = &s->queue; - if (q->count == 0) { - /* NOTE: if no data left, we return the last keyboard one - (needed for EMM386) */ - /* XXX: need a timer to do things correctly */ - index = q->rptr - 1; - if (index < 0) - index = PS2_QUEUE_SIZE - 1; - val = q->data[index]; - } else { - val = q->data[q->rptr]; - if (++q->rptr == PS2_QUEUE_SIZE) - q->rptr = 0; - q->count--; - /* reading deasserts IRQ */ - s->update_irq(s->update_arg, 0); - /* reassert IRQs if data left */ - s->update_irq(s->update_arg, q->count != 0); - } - return val; -} - -static void ps2_set_ledstate(PS2KbdState *s, int ledstate) -{ - trace_ps2_set_ledstate(s, ledstate); - s->ledstate = ledstate; - kbd_put_ledstate(ledstate); -} - -static void ps2_reset_keyboard(PS2KbdState *s) -{ - trace_ps2_reset_keyboard(s); - s->scan_enabled = 1; - s->scancode_set = 2; - ps2_set_ledstate(s, 0); -} - -void ps2_write_keyboard(void *opaque, int val) -{ - PS2KbdState *s = (PS2KbdState *)opaque; - - trace_ps2_write_keyboard(opaque, val); - switch(s->common.write_cmd) { - default: - case -1: - switch(val) { - case 0x00: - ps2_queue(&s->common, KBD_REPLY_ACK); - break; - case 0x05: - ps2_queue(&s->common, KBD_REPLY_RESEND); - break; - case KBD_CMD_GET_ID: - ps2_queue(&s->common, KBD_REPLY_ACK); - /* We emulate a MF2 AT keyboard here */ - ps2_queue(&s->common, KBD_REPLY_ID); - if (s->translate) - ps2_queue(&s->common, 0x41); - else - ps2_queue(&s->common, 0x83); - break; - case KBD_CMD_ECHO: - ps2_queue(&s->common, KBD_CMD_ECHO); - break; - case KBD_CMD_ENABLE: - s->scan_enabled = 1; - ps2_queue(&s->common, KBD_REPLY_ACK); - break; - case KBD_CMD_SCANCODE: - case KBD_CMD_SET_LEDS: - case KBD_CMD_SET_RATE: - s->common.write_cmd = val; - ps2_queue(&s->common, KBD_REPLY_ACK); - break; - case KBD_CMD_RESET_DISABLE: - ps2_reset_keyboard(s); - s->scan_enabled = 0; - ps2_queue(&s->common, KBD_REPLY_ACK); - break; - case KBD_CMD_RESET_ENABLE: - ps2_reset_keyboard(s); - s->scan_enabled = 1; - ps2_queue(&s->common, KBD_REPLY_ACK); - break; - case KBD_CMD_RESET: - ps2_reset_keyboard(s); - ps2_queue(&s->common, KBD_REPLY_ACK); - ps2_queue(&s->common, KBD_REPLY_POR); - break; - default: - ps2_queue(&s->common, KBD_REPLY_ACK); - break; - } - break; - case KBD_CMD_SCANCODE: - if (val == 0) { - if (s->scancode_set == 1) - ps2_put_keycode(s, 0x43); - else if (s->scancode_set == 2) - ps2_put_keycode(s, 0x41); - else if (s->scancode_set == 3) - ps2_put_keycode(s, 0x3f); - } else { - if (val >= 1 && val <= 3) - s->scancode_set = val; - ps2_queue(&s->common, KBD_REPLY_ACK); - } - s->common.write_cmd = -1; - break; - case KBD_CMD_SET_LEDS: - ps2_set_ledstate(s, val); - ps2_queue(&s->common, KBD_REPLY_ACK); - s->common.write_cmd = -1; - break; - case KBD_CMD_SET_RATE: - ps2_queue(&s->common, KBD_REPLY_ACK); - s->common.write_cmd = -1; - break; - } -} - -/* Set the scancode translation mode. - 0 = raw scancodes. - 1 = translated scancodes (used by qemu internally). */ - -void ps2_keyboard_set_translation(void *opaque, int mode) -{ - PS2KbdState *s = (PS2KbdState *)opaque; - trace_ps2_keyboard_set_translation(opaque, mode); - s->translate = mode; -} - -static void ps2_mouse_send_packet(PS2MouseState *s) -{ - unsigned int b; - int dx1, dy1, dz1; - - dx1 = s->mouse_dx; - dy1 = s->mouse_dy; - dz1 = s->mouse_dz; - /* XXX: increase range to 8 bits ? */ - if (dx1 > 127) - dx1 = 127; - else if (dx1 < -127) - dx1 = -127; - if (dy1 > 127) - dy1 = 127; - else if (dy1 < -127) - dy1 = -127; - b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07); - ps2_queue(&s->common, b); - ps2_queue(&s->common, dx1 & 0xff); - ps2_queue(&s->common, dy1 & 0xff); - /* extra byte for IMPS/2 or IMEX */ - switch(s->mouse_type) { - default: - break; - case 3: - if (dz1 > 127) - dz1 = 127; - else if (dz1 < -127) - dz1 = -127; - ps2_queue(&s->common, dz1 & 0xff); - break; - case 4: - if (dz1 > 7) - dz1 = 7; - else if (dz1 < -7) - dz1 = -7; - b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1); - ps2_queue(&s->common, b); - break; - } - - trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b); - /* update deltas */ - s->mouse_dx -= dx1; - s->mouse_dy -= dy1; - s->mouse_dz -= dz1; -} - -static void ps2_mouse_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) -{ - 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->type) { - case INPUT_EVENT_KIND_REL: - 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: - 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 (btn->button == INPUT_BUTTON_WHEEL_DOWN) { - s->mouse_dz++; - } - } else { - s->mouse_buttons &= ~bmap[btn->button]; - } - break; - - default: - /* keep gcc happy */ - break; - } -} - -static void ps2_mouse_sync(DeviceState *dev) -{ - PS2MouseState *s = (PS2MouseState *)dev; - - if (s->mouse_buttons) { - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); - } - if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) { - while (s->common.queue.count < PS2_QUEUE_SIZE - 4) { - /* if not remote, send event. Multiple events are sent if - too big deltas */ - ps2_mouse_send_packet(s); - if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0) - break; - } - } -} - -void ps2_mouse_fake_event(void *opaque) -{ - PS2MouseState *s = opaque; - trace_ps2_mouse_fake_event(opaque); - s->mouse_dx++; - ps2_mouse_sync(opaque); -} - -void ps2_write_mouse(void *opaque, int val) -{ - PS2MouseState *s = (PS2MouseState *)opaque; - - trace_ps2_write_mouse(opaque, val); -#ifdef DEBUG_MOUSE - printf("kbd: write mouse 0x%02x\n", val); -#endif - switch(s->common.write_cmd) { - default: - case -1: - /* mouse command */ - if (s->mouse_wrap) { - if (val == AUX_RESET_WRAP) { - s->mouse_wrap = 0; - ps2_queue(&s->common, AUX_ACK); - return; - } else if (val != AUX_RESET) { - ps2_queue(&s->common, val); - return; - } - } - switch(val) { - case AUX_SET_SCALE11: - s->mouse_status &= ~MOUSE_STATUS_SCALE21; - ps2_queue(&s->common, AUX_ACK); - break; - case AUX_SET_SCALE21: - s->mouse_status |= MOUSE_STATUS_SCALE21; - ps2_queue(&s->common, AUX_ACK); - break; - case AUX_SET_STREAM: - s->mouse_status &= ~MOUSE_STATUS_REMOTE; - ps2_queue(&s->common, AUX_ACK); - break; - case AUX_SET_WRAP: - s->mouse_wrap = 1; - ps2_queue(&s->common, AUX_ACK); - break; - case AUX_SET_REMOTE: - s->mouse_status |= MOUSE_STATUS_REMOTE; - ps2_queue(&s->common, AUX_ACK); - break; - case AUX_GET_TYPE: - ps2_queue(&s->common, AUX_ACK); - ps2_queue(&s->common, s->mouse_type); - break; - case AUX_SET_RES: - case AUX_SET_SAMPLE: - s->common.write_cmd = val; - ps2_queue(&s->common, AUX_ACK); - break; - case AUX_GET_SCALE: - ps2_queue(&s->common, AUX_ACK); - ps2_queue(&s->common, s->mouse_status); - ps2_queue(&s->common, s->mouse_resolution); - ps2_queue(&s->common, s->mouse_sample_rate); - break; - case AUX_POLL: - ps2_queue(&s->common, AUX_ACK); - ps2_mouse_send_packet(s); - break; - case AUX_ENABLE_DEV: - s->mouse_status |= MOUSE_STATUS_ENABLED; - ps2_queue(&s->common, AUX_ACK); - break; - case AUX_DISABLE_DEV: - s->mouse_status &= ~MOUSE_STATUS_ENABLED; - ps2_queue(&s->common, AUX_ACK); - break; - case AUX_SET_DEFAULT: - s->mouse_sample_rate = 100; - s->mouse_resolution = 2; - s->mouse_status = 0; - ps2_queue(&s->common, AUX_ACK); - break; - case AUX_RESET: - s->mouse_sample_rate = 100; - s->mouse_resolution = 2; - s->mouse_status = 0; - s->mouse_type = 0; - ps2_queue(&s->common, AUX_ACK); - ps2_queue(&s->common, 0xaa); - ps2_queue(&s->common, s->mouse_type); - break; - default: - break; - } - break; - case AUX_SET_SAMPLE: - s->mouse_sample_rate = val; - /* detect IMPS/2 or IMEX */ - switch(s->mouse_detect_state) { - default: - case 0: - if (val == 200) - s->mouse_detect_state = 1; - break; - case 1: - if (val == 100) - s->mouse_detect_state = 2; - else if (val == 200) - s->mouse_detect_state = 3; - else - s->mouse_detect_state = 0; - break; - case 2: - if (val == 80) - s->mouse_type = 3; /* IMPS/2 */ - s->mouse_detect_state = 0; - break; - case 3: - if (val == 80) - s->mouse_type = 4; /* IMEX */ - s->mouse_detect_state = 0; - break; - } - ps2_queue(&s->common, AUX_ACK); - s->common.write_cmd = -1; - break; - case AUX_SET_RES: - s->mouse_resolution = val; - ps2_queue(&s->common, AUX_ACK); - s->common.write_cmd = -1; - break; - } -} - -static void ps2_common_reset(PS2State *s) -{ - PS2Queue *q; - s->write_cmd = -1; - q = &s->queue; - q->rptr = 0; - q->wptr = 0; - q->count = 0; - s->update_irq(s->update_arg, 0); -} - -static void ps2_common_post_load(PS2State *s) -{ - PS2Queue *q = &s->queue; - int size; - int i; - int tmp_data[PS2_QUEUE_SIZE]; - - /* set the useful data buffer queue size, < PS2_QUEUE_SIZE */ - size = q->count > PS2_QUEUE_SIZE ? 0 : q->count; - - /* move the queue elements to the start of data array */ - if (size > 0) { - for (i = 0; i < size; i++) { - /* move the queue elements to the temporary buffer */ - tmp_data[i] = q->data[q->rptr]; - if (++q->rptr == 256) { - q->rptr = 0; - } - } - memcpy(q->data, tmp_data, size); - } - /* reset rptr/wptr/count */ - q->rptr = 0; - q->wptr = size; - q->count = size; - s->update_irq(s->update_arg, q->count != 0); -} - -static void ps2_kbd_reset(void *opaque) -{ - PS2KbdState *s = (PS2KbdState *) opaque; - - trace_ps2_kbd_reset(opaque); - ps2_common_reset(&s->common); - s->scan_enabled = 0; - s->translate = 0; - s->scancode_set = 2; -} - -static void ps2_mouse_reset(void *opaque) -{ - PS2MouseState *s = (PS2MouseState *) opaque; - - trace_ps2_mouse_reset(opaque); - ps2_common_reset(&s->common); - s->mouse_status = 0; - s->mouse_resolution = 0; - s->mouse_sample_rate = 0; - s->mouse_wrap = 0; - s->mouse_type = 0; - s->mouse_detect_state = 0; - s->mouse_dx = 0; - s->mouse_dy = 0; - s->mouse_dz = 0; - s->mouse_buttons = 0; -} - -static const VMStateDescription vmstate_ps2_common = { - .name = "PS2 Common State", - .version_id = 3, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_INT32(write_cmd, PS2State), - VMSTATE_INT32(queue.rptr, PS2State), - VMSTATE_INT32(queue.wptr, PS2State), - VMSTATE_INT32(queue.count, PS2State), - VMSTATE_BUFFER(queue.data, PS2State), - VMSTATE_END_OF_LIST() - } -}; - -static bool ps2_keyboard_ledstate_needed(void *opaque) -{ - PS2KbdState *s = opaque; - - return s->ledstate != 0; /* 0 is default state */ -} - -static int ps2_kbd_ledstate_post_load(void *opaque, int version_id) -{ - PS2KbdState *s = opaque; - - kbd_put_ledstate(s->ledstate); - return 0; -} - -static const VMStateDescription vmstate_ps2_keyboard_ledstate = { - .name = "ps2kbd/ledstate", - .version_id = 3, - .minimum_version_id = 2, - .post_load = ps2_kbd_ledstate_post_load, - .needed = ps2_keyboard_ledstate_needed, - .fields = (VMStateField[]) { - VMSTATE_INT32(ledstate, PS2KbdState), - VMSTATE_END_OF_LIST() - } -}; - -static int ps2_kbd_post_load(void* opaque, int version_id) -{ - PS2KbdState *s = (PS2KbdState*)opaque; - PS2State *ps2 = &s->common; - - if (version_id == 2) - s->scancode_set=2; - - ps2_common_post_load(ps2); - - return 0; -} - -static void ps2_kbd_pre_save(void *opaque) -{ - PS2KbdState *s = (PS2KbdState *)opaque; - PS2State *ps2 = &s->common; - - ps2_common_post_load(ps2); -} - -static const VMStateDescription vmstate_ps2_keyboard = { - .name = "ps2kbd", - .version_id = 3, - .minimum_version_id = 2, - .post_load = ps2_kbd_post_load, - .pre_save = ps2_kbd_pre_save, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(common, PS2KbdState, 0, vmstate_ps2_common, PS2State), - VMSTATE_INT32(scan_enabled, PS2KbdState), - VMSTATE_INT32(translate, PS2KbdState), - VMSTATE_INT32_V(scancode_set, PS2KbdState,3), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_ps2_keyboard_ledstate, - NULL - } -}; - -static int ps2_mouse_post_load(void *opaque, int version_id) -{ - PS2MouseState *s = (PS2MouseState *)opaque; - PS2State *ps2 = &s->common; - - ps2_common_post_load(ps2); - - return 0; -} - -static void ps2_mouse_pre_save(void *opaque) -{ - PS2MouseState *s = (PS2MouseState *)opaque; - PS2State *ps2 = &s->common; - - ps2_common_post_load(ps2); -} - -static const VMStateDescription vmstate_ps2_mouse = { - .name = "ps2mouse", - .version_id = 2, - .minimum_version_id = 2, - .post_load = ps2_mouse_post_load, - .pre_save = ps2_mouse_pre_save, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State), - VMSTATE_UINT8(mouse_status, PS2MouseState), - VMSTATE_UINT8(mouse_resolution, PS2MouseState), - VMSTATE_UINT8(mouse_sample_rate, PS2MouseState), - VMSTATE_UINT8(mouse_wrap, PS2MouseState), - VMSTATE_UINT8(mouse_type, PS2MouseState), - VMSTATE_UINT8(mouse_detect_state, PS2MouseState), - VMSTATE_INT32(mouse_dx, PS2MouseState), - VMSTATE_INT32(mouse_dy, PS2MouseState), - VMSTATE_INT32(mouse_dz, PS2MouseState), - VMSTATE_UINT8(mouse_buttons, PS2MouseState), - VMSTATE_END_OF_LIST() - } -}; - -static QemuInputHandler ps2_keyboard_handler = { - .name = "QEMU PS/2 Keyboard", - .mask = INPUT_EVENT_MASK_KEY, - .event = ps2_keyboard_event, -}; - -void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg) -{ - PS2KbdState *s = (PS2KbdState *)g_malloc0(sizeof(PS2KbdState)); - - trace_ps2_kbd_init(s); - s->common.update_irq = update_irq; - s->common.update_arg = update_arg; - s->scancode_set = 2; - vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s); - qemu_input_handler_register((DeviceState *)s, - &ps2_keyboard_handler); - qemu_register_reset(ps2_kbd_reset, s); - return s; -} - -static QemuInputHandler ps2_mouse_handler = { - .name = "QEMU PS/2 Mouse", - .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL, - .event = ps2_mouse_event, - .sync = ps2_mouse_sync, -}; - -void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg) -{ - PS2MouseState *s = (PS2MouseState *)g_malloc0(sizeof(PS2MouseState)); - - trace_ps2_mouse_init(s); - s->common.update_irq = update_irq; - s->common.update_arg = update_arg; - vmstate_register(NULL, 0, &vmstate_ps2_mouse, s); - qemu_input_handler_register((DeviceState *)s, - &ps2_mouse_handler); - qemu_register_reset(ps2_mouse_reset, s); - return s; -} diff --git a/qemu/hw/input/pxa2xx_keypad.c b/qemu/hw/input/pxa2xx_keypad.c deleted file mode 100644 index 2b70bbb95..000000000 --- a/qemu/hw/input/pxa2xx_keypad.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Intel PXA27X Keypad Controller emulation. - * - * Copyright (c) 2007 MontaVista Software, Inc - * Written by Armin Kuster - * or - * - * This code is licensed under the GPLv2. - * - * 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/arm/pxa.h" -#include "ui/console.h" - -/* - * Keypad - */ -#define KPC 0x00 /* Keypad Interface Control register */ -#define KPDK 0x08 /* Keypad Interface Direct Key register */ -#define KPREC 0x10 /* Keypad Interface Rotary Encoder register */ -#define KPMK 0x18 /* Keypad Interface Matrix Key register */ -#define KPAS 0x20 /* Keypad Interface Automatic Scan register */ -#define KPASMKP0 0x28 /* Keypad Interface Automatic Scan Multiple - Key Presser register 0 */ -#define KPASMKP1 0x30 /* Keypad Interface Automatic Scan Multiple - Key Presser register 1 */ -#define KPASMKP2 0x38 /* Keypad Interface Automatic Scan Multiple - Key Presser register 2 */ -#define KPASMKP3 0x40 /* Keypad Interface Automatic Scan Multiple - Key Presser register 3 */ -#define KPKDI 0x48 /* Keypad Interface Key Debounce Interval - register */ - -/* Keypad defines */ -#define KPC_AS (0x1 << 30) /* Automatic Scan bit */ -#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */ -#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */ -#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */ -#define KPC_MS7 (0x1 << 20) /* Matrix scan line 7 */ -#define KPC_MS6 (0x1 << 19) /* Matrix scan line 6 */ -#define KPC_MS5 (0x1 << 18) /* Matrix scan line 5 */ -#define KPC_MS4 (0x1 << 17) /* Matrix scan line 4 */ -#define KPC_MS3 (0x1 << 16) /* Matrix scan line 3 */ -#define KPC_MS2 (0x1 << 15) /* Matrix scan line 2 */ -#define KPC_MS1 (0x1 << 14) /* Matrix scan line 1 */ -#define KPC_MS0 (0x1 << 13) /* Matrix scan line 0 */ -#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */ -#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */ -#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Keypad Debounce Select */ -#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */ -#define KPC_RE_ZERO_DEB (0x1 << 4) /* Rotary Encoder Zero Debounce */ -#define KPC_REE1 (0x1 << 3) /* Rotary Encoder1 Enable */ -#define KPC_REE0 (0x1 << 2) /* Rotary Encoder0 Enable */ -#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */ -#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */ - -#define KPDK_DKP (0x1 << 31) -#define KPDK_DK7 (0x1 << 7) -#define KPDK_DK6 (0x1 << 6) -#define KPDK_DK5 (0x1 << 5) -#define KPDK_DK4 (0x1 << 4) -#define KPDK_DK3 (0x1 << 3) -#define KPDK_DK2 (0x1 << 2) -#define KPDK_DK1 (0x1 << 1) -#define KPDK_DK0 (0x1 << 0) - -#define KPREC_OF1 (0x1 << 31) -#define KPREC_UF1 (0x1 << 30) -#define KPREC_OF0 (0x1 << 15) -#define KPREC_UF0 (0x1 << 14) - -#define KPMK_MKP (0x1 << 31) -#define KPAS_SO (0x1 << 31) -#define KPASMKPx_SO (0x1 << 31) - - -#define KPASMKPx_MKC(row, col) (1 << (row + 16 * (col % 2))) - -#define PXAKBD_MAXROW 8 -#define PXAKBD_MAXCOL 8 - -struct PXA2xxKeyPadState { - MemoryRegion iomem; - qemu_irq irq; - const struct keymap *map; - int pressed_cnt; - int alt_code; - - uint32_t kpc; - uint32_t kpdk; - uint32_t kprec; - uint32_t kpmk; - uint32_t kpas; - uint32_t kpasmkp[4]; - uint32_t kpkdi; -}; - -static void pxa27x_keypad_find_pressed_key(PXA2xxKeyPadState *kp, int *row, int *col) -{ - int i; - for (i = 0; i < 4; i++) - { - *col = i * 2; - for (*row = 0; *row < 8; (*row)++) { - if (kp->kpasmkp[i] & (1 << *row)) - return; - } - *col = i * 2 + 1; - for (*row = 0; *row < 8; (*row)++) { - if (kp->kpasmkp[i] & (1 << (*row + 16))) - return; - } - } -} - -static void pxa27x_keyboard_event (PXA2xxKeyPadState *kp, int keycode) -{ - int row, col, rel, assert_irq = 0; - uint32_t val; - - if (keycode == 0xe0) { - kp->alt_code = 1; - return; - } - - if(!(kp->kpc & KPC_ME)) /* skip if not enabled */ - return; - - rel = (keycode & 0x80) ? 1 : 0; /* key release from qemu */ - keycode &= ~0x80; /* strip qemu key release bit */ - if (kp->alt_code) { - keycode |= 0x80; - kp->alt_code = 0; - } - - row = kp->map[keycode].row; - col = kp->map[keycode].column; - if (row == -1 || col == -1) { - return; - } - - val = KPASMKPx_MKC(row, col); - if (rel) { - if (kp->kpasmkp[col / 2] & val) { - kp->kpasmkp[col / 2] &= ~val; - kp->pressed_cnt--; - assert_irq = 1; - } - } else { - if (!(kp->kpasmkp[col / 2] & val)) { - kp->kpasmkp[col / 2] |= val; - kp->pressed_cnt++; - assert_irq = 1; - } - } - kp->kpas = ((kp->pressed_cnt & 0x1f) << 26) | (0xf << 4) | 0xf; - if (kp->pressed_cnt == 1) { - kp->kpas &= ~((0xf << 4) | 0xf); - if (rel) { - pxa27x_keypad_find_pressed_key(kp, &row, &col); - } - kp->kpas |= ((row & 0xf) << 4) | (col & 0xf); - } - - if (!(kp->kpc & (KPC_AS | KPC_ASACT))) - assert_irq = 0; - - if (assert_irq && (kp->kpc & KPC_MIE)) { - kp->kpc |= KPC_MI; - qemu_irq_raise(kp->irq); - } -} - -static uint64_t pxa2xx_keypad_read(void *opaque, hwaddr offset, - unsigned size) -{ - PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque; - uint32_t tmp; - - switch (offset) { - case KPC: - tmp = s->kpc; - if(tmp & KPC_MI) - s->kpc &= ~(KPC_MI); - if(tmp & KPC_DI) - s->kpc &= ~(KPC_DI); - qemu_irq_lower(s->irq); - return tmp; - break; - case KPDK: - return s->kpdk; - break; - case KPREC: - tmp = s->kprec; - if(tmp & KPREC_OF1) - s->kprec &= ~(KPREC_OF1); - if(tmp & KPREC_UF1) - s->kprec &= ~(KPREC_UF1); - if(tmp & KPREC_OF0) - s->kprec &= ~(KPREC_OF0); - if(tmp & KPREC_UF0) - s->kprec &= ~(KPREC_UF0); - return tmp; - break; - case KPMK: - tmp = s->kpmk; - if(tmp & KPMK_MKP) - s->kpmk &= ~(KPMK_MKP); - return tmp; - break; - case KPAS: - return s->kpas; - break; - case KPASMKP0: - return s->kpasmkp[0]; - break; - case KPASMKP1: - return s->kpasmkp[1]; - break; - case KPASMKP2: - return s->kpasmkp[2]; - break; - case KPASMKP3: - return s->kpasmkp[3]; - break; - case KPKDI: - return s->kpkdi; - break; - default: - hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset); - } - - return 0; -} - -static void pxa2xx_keypad_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque; - - switch (offset) { - case KPC: - s->kpc = value; - if (s->kpc & KPC_AS) { - s->kpc &= ~(KPC_AS); - } - break; - case KPDK: - s->kpdk = value; - break; - case KPREC: - s->kprec = value; - break; - case KPMK: - s->kpmk = value; - break; - case KPAS: - s->kpas = value; - break; - case KPASMKP0: - s->kpasmkp[0] = value; - break; - case KPASMKP1: - s->kpasmkp[1] = value; - break; - case KPASMKP2: - s->kpasmkp[2] = value; - break; - case KPASMKP3: - s->kpasmkp[3] = value; - break; - case KPKDI: - s->kpkdi = value; - break; - - default: - hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset); - } -} - -static const MemoryRegionOps pxa2xx_keypad_ops = { - .read = pxa2xx_keypad_read, - .write = pxa2xx_keypad_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_pxa2xx_keypad = { - .name = "pxa2xx_keypad", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(kpc, PXA2xxKeyPadState), - VMSTATE_UINT32(kpdk, PXA2xxKeyPadState), - VMSTATE_UINT32(kprec, PXA2xxKeyPadState), - VMSTATE_UINT32(kpmk, PXA2xxKeyPadState), - VMSTATE_UINT32(kpas, PXA2xxKeyPadState), - VMSTATE_UINT32_ARRAY(kpasmkp, PXA2xxKeyPadState, 4), - VMSTATE_UINT32(kpkdi, PXA2xxKeyPadState), - VMSTATE_END_OF_LIST() - } -}; - -PXA2xxKeyPadState *pxa27x_keypad_init(MemoryRegion *sysmem, - hwaddr base, - qemu_irq irq) -{ - PXA2xxKeyPadState *s; - - s = (PXA2xxKeyPadState *) g_malloc0(sizeof(PXA2xxKeyPadState)); - s->irq = irq; - - memory_region_init_io(&s->iomem, NULL, &pxa2xx_keypad_ops, s, - "pxa2xx-keypad", 0x00100000); - memory_region_add_subregion(sysmem, base, &s->iomem); - - vmstate_register(NULL, 0, &vmstate_pxa2xx_keypad, s); - - return s; -} - -void pxa27x_register_keypad(PXA2xxKeyPadState *kp, - const struct keymap *map, int size) -{ - if(!map || size < 0x80) { - fprintf(stderr, "%s - No PXA keypad map defined\n", __FUNCTION__); - exit(-1); - } - - kp->map = map; - qemu_add_kbd_event_handler((QEMUPutKBDEvent *) pxa27x_keyboard_event, kp); -} diff --git a/qemu/hw/input/stellaris_input.c b/qemu/hw/input/stellaris_input.c deleted file mode 100644 index 99168bfee..000000000 --- a/qemu/hw/input/stellaris_input.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Gamepad style buttons connected to IRQ/GPIO lines - * - * Copyright (c) 2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/devices.h" -#include "ui/console.h" - -typedef struct { - qemu_irq irq; - int keycode; - uint8_t pressed; -} gamepad_button; - -typedef struct { - gamepad_button *buttons; - int num_buttons; - int extension; -} gamepad_state; - -static void stellaris_gamepad_put_key(void * opaque, int keycode) -{ - gamepad_state *s = (gamepad_state *)opaque; - int i; - int down; - - if (keycode == 0xe0 && !s->extension) { - s->extension = 0x80; - return; - } - - down = (keycode & 0x80) == 0; - keycode = (keycode & 0x7f) | s->extension; - - for (i = 0; i < s->num_buttons; i++) { - if (s->buttons[i].keycode == keycode - && s->buttons[i].pressed != down) { - s->buttons[i].pressed = down; - qemu_set_irq(s->buttons[i].irq, down); - } - } - - s->extension = 0; -} - -static const VMStateDescription vmstate_stellaris_button = { - .name = "stellaris_button", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT8(pressed, gamepad_button), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_stellaris_gamepad = { - .name = "stellaris_gamepad", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32(extension, gamepad_state), - VMSTATE_STRUCT_VARRAY_INT32(buttons, gamepad_state, num_buttons, 0, - vmstate_stellaris_button, gamepad_button), - VMSTATE_END_OF_LIST() - } -}; - -/* 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 = 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]; - } - s->num_buttons = n; - qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s); - vmstate_register(NULL, -1, &vmstate_stellaris_gamepad, s); -} diff --git a/qemu/hw/input/tsc2005.c b/qemu/hw/input/tsc2005.c deleted file mode 100644 index 9b359aaec..000000000 --- a/qemu/hw/input/tsc2005.c +++ /dev/null @@ -1,595 +0,0 @@ -/* - * TI TSC2005 emulator. - * - * Copyright (c) 2006 Andrzej Zaborowski - * Copyright (C) 2008 Nokia Corporation - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "qemu/timer.h" -#include "ui/console.h" -#include "hw/devices.h" - -#define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - (p ? 12 : 10))) - -typedef struct { - qemu_irq pint; /* Combination of the nPENIRQ and DAV signals */ - QEMUTimer *timer; - uint16_t model; - - int x, y; - int pressure; - - int state, reg, irq, command; - uint16_t data, dav; - - int busy; - int enabled; - int host_mode; - int function; - int nextfunction; - int precision; - int nextprecision; - int filter; - int pin_func; - int timing[2]; - int noise; - int reset; - int pdst; - int pnd0; - uint16_t temp_thr[2]; - uint16_t aux_thr[2]; - - int tr[8]; -} TSC2005State; - -enum { - TSC_MODE_XYZ_SCAN = 0x0, - TSC_MODE_XY_SCAN, - TSC_MODE_X, - TSC_MODE_Y, - TSC_MODE_Z, - TSC_MODE_AUX, - TSC_MODE_TEMP1, - TSC_MODE_TEMP2, - TSC_MODE_AUX_SCAN, - TSC_MODE_X_TEST, - TSC_MODE_Y_TEST, - TSC_MODE_TS_TEST, - TSC_MODE_RESERVED, - TSC_MODE_XX_DRV, - TSC_MODE_YY_DRV, - TSC_MODE_YX_DRV, -}; - -static const uint16_t mode_regs[16] = { - 0xf000, /* X, Y, Z scan */ - 0xc000, /* X, Y scan */ - 0x8000, /* X */ - 0x4000, /* Y */ - 0x3000, /* Z */ - 0x0800, /* AUX */ - 0x0400, /* TEMP1 */ - 0x0200, /* TEMP2 */ - 0x0800, /* AUX scan */ - 0x0040, /* X test */ - 0x0020, /* Y test */ - 0x0080, /* Short-circuit test */ - 0x0000, /* Reserved */ - 0x0000, /* X+, X- drivers */ - 0x0000, /* Y+, Y- drivers */ - 0x0000, /* Y+, X- drivers */ -}; - -#define X_TRANSFORM(s) \ - ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3]) -#define Y_TRANSFORM(s) \ - ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7]) -#define Z1_TRANSFORM(s) \ - ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4) -#define Z2_TRANSFORM(s) \ - ((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4) - -#define AUX_VAL (700 << 4) /* +/- 3 at 12-bit */ -#define TEMP1_VAL (1264 << 4) /* +/- 5 at 12-bit */ -#define TEMP2_VAL (1531 << 4) /* +/- 5 at 12-bit */ - -static uint16_t tsc2005_read(TSC2005State *s, int reg) -{ - uint16_t ret; - - switch (reg) { - case 0x0: /* X */ - s->dav &= ~mode_regs[TSC_MODE_X]; - return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) + - (s->noise & 3); - case 0x1: /* Y */ - s->dav &= ~mode_regs[TSC_MODE_Y]; - s->noise ++; - return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^ - (s->noise & 3); - case 0x2: /* Z1 */ - s->dav &= 0xdfff; - return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) - - (s->noise & 3); - case 0x3: /* Z2 */ - s->dav &= 0xefff; - return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) | - (s->noise & 3); - - case 0x4: /* AUX */ - s->dav &= ~mode_regs[TSC_MODE_AUX]; - return TSC_CUT_RESOLUTION(AUX_VAL, s->precision); - - case 0x5: /* TEMP1 */ - s->dav &= ~mode_regs[TSC_MODE_TEMP1]; - return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) - - (s->noise & 5); - case 0x6: /* TEMP2 */ - s->dav &= 0xdfff; - s->dav &= ~mode_regs[TSC_MODE_TEMP2]; - return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^ - (s->noise & 3); - - case 0x7: /* Status */ - ret = s->dav | (s->reset << 7) | (s->pdst << 2) | 0x0; - s->dav &= ~(mode_regs[TSC_MODE_X_TEST] | mode_regs[TSC_MODE_Y_TEST] | - mode_regs[TSC_MODE_TS_TEST]); - s->reset = 1; - return ret; - - case 0x8: /* AUX high treshold */ - return s->aux_thr[1]; - case 0x9: /* AUX low treshold */ - return s->aux_thr[0]; - - case 0xa: /* TEMP high treshold */ - return s->temp_thr[1]; - case 0xb: /* TEMP low treshold */ - return s->temp_thr[0]; - - case 0xc: /* CFR0 */ - return (s->pressure << 15) | ((!s->busy) << 14) | - (s->nextprecision << 13) | s->timing[0]; - case 0xd: /* CFR1 */ - return s->timing[1]; - case 0xe: /* CFR2 */ - return (s->pin_func << 14) | s->filter; - - case 0xf: /* Function select status */ - return s->function >= 0 ? 1 << s->function : 0; - } - - /* Never gets here */ - return 0xffff; -} - -static void tsc2005_write(TSC2005State *s, int reg, uint16_t data) -{ - switch (reg) { - case 0x8: /* AUX high treshold */ - s->aux_thr[1] = data; - break; - case 0x9: /* AUX low treshold */ - s->aux_thr[0] = data; - break; - - case 0xa: /* TEMP high treshold */ - s->temp_thr[1] = data; - break; - case 0xb: /* TEMP low treshold */ - s->temp_thr[0] = data; - break; - - case 0xc: /* CFR0 */ - s->host_mode = data >> 15; - if (s->enabled != !(data & 0x4000)) { - s->enabled = !(data & 0x4000); - fprintf(stderr, "%s: touchscreen sense %sabled\n", - __FUNCTION__, s->enabled ? "en" : "dis"); - if (s->busy && !s->enabled) - timer_del(s->timer); - s->busy &= s->enabled; - } - s->nextprecision = (data >> 13) & 1; - s->timing[0] = data & 0x1fff; - if ((s->timing[0] >> 11) == 3) - fprintf(stderr, "%s: illegal conversion clock setting\n", - __FUNCTION__); - break; - case 0xd: /* CFR1 */ - s->timing[1] = data & 0xf07; - break; - case 0xe: /* CFR2 */ - s->pin_func = (data >> 14) & 3; - s->filter = data & 0x3fff; - break; - - default: - fprintf(stderr, "%s: write into read-only register %x\n", - __FUNCTION__, reg); - } -} - -/* This handles most of the chip's logic. */ -static void tsc2005_pin_update(TSC2005State *s) -{ - int64_t expires; - int pin_state; - - switch (s->pin_func) { - case 0: - pin_state = !s->pressure && !!s->dav; - break; - case 1: - case 3: - default: - pin_state = !s->dav; - break; - case 2: - pin_state = !s->pressure; - } - - if (pin_state != s->irq) { - s->irq = pin_state; - qemu_set_irq(s->pint, s->irq); - } - - switch (s->nextfunction) { - case TSC_MODE_XYZ_SCAN: - case TSC_MODE_XY_SCAN: - if (!s->host_mode && s->dav) - s->enabled = 0; - if (!s->pressure) - return; - /* Fall through */ - case TSC_MODE_AUX_SCAN: - break; - - case TSC_MODE_X: - case TSC_MODE_Y: - case TSC_MODE_Z: - if (!s->pressure) - return; - /* Fall through */ - case TSC_MODE_AUX: - case TSC_MODE_TEMP1: - case TSC_MODE_TEMP2: - case TSC_MODE_X_TEST: - case TSC_MODE_Y_TEST: - case TSC_MODE_TS_TEST: - if (s->dav) - s->enabled = 0; - break; - - case TSC_MODE_RESERVED: - case TSC_MODE_XX_DRV: - case TSC_MODE_YY_DRV: - case TSC_MODE_YX_DRV: - default: - return; - } - - if (!s->enabled || s->busy) - return; - - s->busy = 1; - s->precision = s->nextprecision; - s->function = s->nextfunction; - s->pdst = !s->pnd0; /* Synchronised on internal clock */ - expires = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND >> 7); - timer_mod(s->timer, expires); -} - -static void tsc2005_reset(TSC2005State *s) -{ - s->state = 0; - s->pin_func = 0; - s->enabled = 0; - s->busy = 0; - s->nextprecision = 0; - s->nextfunction = 0; - s->timing[0] = 0; - s->timing[1] = 0; - s->irq = 0; - s->dav = 0; - s->reset = 0; - s->pdst = 1; - s->pnd0 = 0; - s->function = -1; - s->temp_thr[0] = 0x000; - s->temp_thr[1] = 0xfff; - s->aux_thr[0] = 0x000; - s->aux_thr[1] = 0xfff; - - tsc2005_pin_update(s); -} - -static uint8_t tsc2005_txrx_word(void *opaque, uint8_t value) -{ - TSC2005State *s = opaque; - uint32_t ret = 0; - - switch (s->state ++) { - case 0: - if (value & 0x80) { - /* Command */ - if (value & (1 << 1)) - tsc2005_reset(s); - else { - s->nextfunction = (value >> 3) & 0xf; - s->nextprecision = (value >> 2) & 1; - if (s->enabled != !(value & 1)) { - s->enabled = !(value & 1); - fprintf(stderr, "%s: touchscreen sense %sabled\n", - __FUNCTION__, s->enabled ? "en" : "dis"); - if (s->busy && !s->enabled) - timer_del(s->timer); - s->busy &= s->enabled; - } - tsc2005_pin_update(s); - } - - s->state = 0; - } else if (value) { - /* Data transfer */ - s->reg = (value >> 3) & 0xf; - s->pnd0 = (value >> 1) & 1; - s->command = value & 1; - - if (s->command) { - /* Read */ - s->data = tsc2005_read(s, s->reg); - tsc2005_pin_update(s); - } else - s->data = 0; - } else - s->state = 0; - break; - - case 1: - if (s->command) - ret = (s->data >> 8) & 0xff; - else - s->data |= value << 8; - break; - - case 2: - if (s->command) - ret = s->data & 0xff; - else { - s->data |= value; - tsc2005_write(s, s->reg, s->data); - tsc2005_pin_update(s); - } - - s->state = 0; - break; - } - - return ret; -} - -uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len) -{ - uint32_t ret = 0; - - len &= ~7; - while (len > 0) { - len -= 8; - ret |= tsc2005_txrx_word(opaque, (value >> len) & 0xff) << len; - } - - return ret; -} - -static void tsc2005_timer_tick(void *opaque) -{ - TSC2005State *s = opaque; - - /* Timer ticked -- a set of conversions has been finished. */ - - if (!s->busy) - return; - - s->busy = 0; - s->dav |= mode_regs[s->function]; - s->function = -1; - tsc2005_pin_update(s); -} - -static void tsc2005_touchscreen_event(void *opaque, - int x, int y, int z, int buttons_state) -{ - TSC2005State *s = opaque; - int p = s->pressure; - - if (buttons_state) { - s->x = x; - s->y = y; - } - s->pressure = !!buttons_state; - - /* - * Note: We would get better responsiveness in the guest by - * signaling TS events immediately, but for now we simulate - * the first conversion delay for sake of correctness. - */ - if (p != s->pressure) - tsc2005_pin_update(s); -} - -static void tsc2005_save(QEMUFile *f, void *opaque) -{ - TSC2005State *s = (TSC2005State *) opaque; - int i; - - qemu_put_be16(f, s->x); - qemu_put_be16(f, s->y); - qemu_put_byte(f, s->pressure); - - qemu_put_byte(f, s->state); - qemu_put_byte(f, s->reg); - qemu_put_byte(f, s->command); - - qemu_put_byte(f, s->irq); - qemu_put_be16s(f, &s->dav); - qemu_put_be16s(f, &s->data); - - timer_put(f, s->timer); - qemu_put_byte(f, s->enabled); - qemu_put_byte(f, s->host_mode); - qemu_put_byte(f, s->function); - qemu_put_byte(f, s->nextfunction); - qemu_put_byte(f, s->precision); - qemu_put_byte(f, s->nextprecision); - qemu_put_be16(f, s->filter); - qemu_put_byte(f, s->pin_func); - qemu_put_be16(f, s->timing[0]); - qemu_put_be16(f, s->timing[1]); - qemu_put_be16s(f, &s->temp_thr[0]); - qemu_put_be16s(f, &s->temp_thr[1]); - qemu_put_be16s(f, &s->aux_thr[0]); - qemu_put_be16s(f, &s->aux_thr[1]); - qemu_put_be32(f, s->noise); - qemu_put_byte(f, s->reset); - qemu_put_byte(f, s->pdst); - qemu_put_byte(f, s->pnd0); - - for (i = 0; i < 8; i ++) - qemu_put_be32(f, s->tr[i]); -} - -static int tsc2005_load(QEMUFile *f, void *opaque, int version_id) -{ - TSC2005State *s = (TSC2005State *) opaque; - int i; - - s->x = qemu_get_be16(f); - s->y = qemu_get_be16(f); - s->pressure = qemu_get_byte(f); - - s->state = qemu_get_byte(f); - s->reg = qemu_get_byte(f); - s->command = qemu_get_byte(f); - - s->irq = qemu_get_byte(f); - qemu_get_be16s(f, &s->dav); - qemu_get_be16s(f, &s->data); - - timer_get(f, s->timer); - s->enabled = qemu_get_byte(f); - s->host_mode = qemu_get_byte(f); - s->function = qemu_get_byte(f); - s->nextfunction = qemu_get_byte(f); - s->precision = qemu_get_byte(f); - s->nextprecision = qemu_get_byte(f); - s->filter = qemu_get_be16(f); - s->pin_func = qemu_get_byte(f); - s->timing[0] = qemu_get_be16(f); - s->timing[1] = qemu_get_be16(f); - qemu_get_be16s(f, &s->temp_thr[0]); - qemu_get_be16s(f, &s->temp_thr[1]); - qemu_get_be16s(f, &s->aux_thr[0]); - qemu_get_be16s(f, &s->aux_thr[1]); - s->noise = qemu_get_be32(f); - s->reset = qemu_get_byte(f); - s->pdst = qemu_get_byte(f); - s->pnd0 = qemu_get_byte(f); - - for (i = 0; i < 8; i ++) - s->tr[i] = qemu_get_be32(f); - - s->busy = timer_pending(s->timer); - tsc2005_pin_update(s); - - return 0; -} - -void *tsc2005_init(qemu_irq pintdav) -{ - TSC2005State *s; - - s = (TSC2005State *) - g_malloc0(sizeof(TSC2005State)); - s->x = 400; - s->y = 240; - s->pressure = 0; - s->precision = s->nextprecision = 0; - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tsc2005_timer_tick, s); - s->pint = pintdav; - s->model = 0x2005; - - s->tr[0] = 0; - s->tr[1] = 1; - s->tr[2] = 1; - s->tr[3] = 0; - s->tr[4] = 1; - s->tr[5] = 0; - s->tr[6] = 1; - s->tr[7] = 0; - - tsc2005_reset(s); - - qemu_add_mouse_event_handler(tsc2005_touchscreen_event, s, 1, - "QEMU TSC2005-driven Touchscreen"); - - qemu_register_reset((void *) tsc2005_reset, s); - register_savevm(NULL, "tsc2005", -1, 0, tsc2005_save, tsc2005_load, s); - - return s; -} - -/* - * Use tslib generated calibration data to generate ADC input values - * from the touchscreen. Assuming 12-bit precision was used during - * tslib calibration. - */ -void tsc2005_set_transform(void *opaque, MouseTransformInfo *info) -{ - TSC2005State *s = (TSC2005State *) opaque; - - /* This version assumes touchscreen X & Y axis are parallel or - * perpendicular to LCD's X & Y axis in some way. */ - if (abs(info->a[0]) > abs(info->a[1])) { - s->tr[0] = 0; - s->tr[1] = -info->a[6] * info->x; - s->tr[2] = info->a[0]; - s->tr[3] = -info->a[2] / info->a[0]; - s->tr[4] = info->a[6] * info->y; - s->tr[5] = 0; - s->tr[6] = info->a[4]; - s->tr[7] = -info->a[5] / info->a[4]; - } else { - s->tr[0] = info->a[6] * info->y; - s->tr[1] = 0; - s->tr[2] = info->a[1]; - s->tr[3] = -info->a[2] / info->a[1]; - s->tr[4] = 0; - s->tr[5] = -info->a[6] * info->x; - s->tr[6] = info->a[3]; - s->tr[7] = -info->a[5] / info->a[3]; - } - - s->tr[0] >>= 11; - s->tr[1] >>= 11; - s->tr[3] <<= 4; - s->tr[4] >>= 11; - s->tr[5] >>= 11; - s->tr[7] <<= 4; -} diff --git a/qemu/hw/input/tsc210x.c b/qemu/hw/input/tsc210x.c deleted file mode 100644 index 93ca374fc..000000000 --- a/qemu/hw/input/tsc210x.c +++ /dev/null @@ -1,1273 +0,0 @@ -/* - * TI TSC2102 (touchscreen/sensors/audio controller) emulator. - * TI TSC2301 (touchscreen/sensors/keypad). - * - * Copyright (c) 2006 Andrzej Zaborowski - * Copyright (C) 2008 Nokia Corporation - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "audio/audio.h" -#include "qemu/timer.h" -#include "ui/console.h" -#include "hw/arm/omap.h" /* For I2SCodec and uWireSlave */ -#include "hw/devices.h" - -#define TSC_DATA_REGISTERS_PAGE 0x0 -#define TSC_CONTROL_REGISTERS_PAGE 0x1 -#define TSC_AUDIO_REGISTERS_PAGE 0x2 - -#define TSC_VERBOSE - -#define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - resolution[p])) - -typedef struct { - qemu_irq pint; - qemu_irq kbint; - qemu_irq davint; - QEMUTimer *timer; - QEMUSoundCard card; - uWireSlave chip; - I2SCodec codec; - uint8_t in_fifo[16384]; - uint8_t out_fifo[16384]; - uint16_t model; - - int x, y; - int pressure; - - int state, page, offset, irq; - uint16_t command, dav; - - int busy; - int enabled; - int host_mode; - int function; - int nextfunction; - int precision; - int nextprecision; - int filter; - int pin_func; - int ref; - int timing; - int noise; - - uint16_t audio_ctrl1; - uint16_t audio_ctrl2; - uint16_t audio_ctrl3; - uint16_t pll[3]; - uint16_t volume; - int64_t volume_change; - int softstep; - uint16_t dac_power; - int64_t powerdown; - uint16_t filter_data[0x14]; - - const char *name; - SWVoiceIn *adc_voice[1]; - SWVoiceOut *dac_voice[1]; - int i2s_rx_rate; - int i2s_tx_rate; - - int tr[8]; - - struct { - uint16_t down; - uint16_t mask; - int scan; - int debounce; - int mode; - int intr; - } kb; -} TSC210xState; - -static const int resolution[4] = { 12, 8, 10, 12 }; - -#define TSC_MODE_NO_SCAN 0x0 -#define TSC_MODE_XY_SCAN 0x1 -#define TSC_MODE_XYZ_SCAN 0x2 -#define TSC_MODE_X 0x3 -#define TSC_MODE_Y 0x4 -#define TSC_MODE_Z 0x5 -#define TSC_MODE_BAT1 0x6 -#define TSC_MODE_BAT2 0x7 -#define TSC_MODE_AUX 0x8 -#define TSC_MODE_AUX_SCAN 0x9 -#define TSC_MODE_TEMP1 0xa -#define TSC_MODE_PORT_SCAN 0xb -#define TSC_MODE_TEMP2 0xc -#define TSC_MODE_XX_DRV 0xd -#define TSC_MODE_YY_DRV 0xe -#define TSC_MODE_YX_DRV 0xf - -static const uint16_t mode_regs[16] = { - 0x0000, /* No scan */ - 0x0600, /* X, Y scan */ - 0x0780, /* X, Y, Z scan */ - 0x0400, /* X */ - 0x0200, /* Y */ - 0x0180, /* Z */ - 0x0040, /* BAT1 */ - 0x0030, /* BAT2 */ - 0x0010, /* AUX */ - 0x0010, /* AUX scan */ - 0x0004, /* TEMP1 */ - 0x0070, /* Port scan */ - 0x0002, /* TEMP2 */ - 0x0000, /* X+, X- drivers */ - 0x0000, /* Y+, Y- drivers */ - 0x0000, /* Y+, X- drivers */ -}; - -#define X_TRANSFORM(s) \ - ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3]) -#define Y_TRANSFORM(s) \ - ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7]) -#define Z1_TRANSFORM(s) \ - ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4) -#define Z2_TRANSFORM(s) \ - ((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4) - -#define BAT1_VAL 0x8660 -#define BAT2_VAL 0x0000 -#define AUX1_VAL 0x35c0 -#define AUX2_VAL 0xffff -#define TEMP1_VAL 0x8c70 -#define TEMP2_VAL 0xa5b0 - -#define TSC_POWEROFF_DELAY 50 -#define TSC_SOFTSTEP_DELAY 50 - -static void tsc210x_reset(TSC210xState *s) -{ - s->state = 0; - s->pin_func = 2; - s->enabled = 0; - s->busy = 0; - s->nextfunction = 0; - s->ref = 0; - s->timing = 0; - s->irq = 0; - s->dav = 0; - - s->audio_ctrl1 = 0x0000; - s->audio_ctrl2 = 0x4410; - s->audio_ctrl3 = 0x0000; - s->pll[0] = 0x1004; - s->pll[1] = 0x0000; - s->pll[2] = 0x1fff; - s->volume = 0xffff; - s->dac_power = 0x8540; - s->softstep = 1; - s->volume_change = 0; - s->powerdown = 0; - s->filter_data[0x00] = 0x6be3; - s->filter_data[0x01] = 0x9666; - s->filter_data[0x02] = 0x675d; - s->filter_data[0x03] = 0x6be3; - s->filter_data[0x04] = 0x9666; - s->filter_data[0x05] = 0x675d; - s->filter_data[0x06] = 0x7d83; - s->filter_data[0x07] = 0x84ee; - s->filter_data[0x08] = 0x7d83; - s->filter_data[0x09] = 0x84ee; - s->filter_data[0x0a] = 0x6be3; - s->filter_data[0x0b] = 0x9666; - s->filter_data[0x0c] = 0x675d; - s->filter_data[0x0d] = 0x6be3; - s->filter_data[0x0e] = 0x9666; - s->filter_data[0x0f] = 0x675d; - s->filter_data[0x10] = 0x7d83; - s->filter_data[0x11] = 0x84ee; - s->filter_data[0x12] = 0x7d83; - s->filter_data[0x13] = 0x84ee; - - s->i2s_tx_rate = 0; - s->i2s_rx_rate = 0; - - s->kb.scan = 1; - s->kb.debounce = 0; - s->kb.mask = 0x0000; - s->kb.mode = 3; - s->kb.intr = 0; - - qemu_set_irq(s->pint, !s->irq); - qemu_set_irq(s->davint, !s->dav); - qemu_irq_raise(s->kbint); -} - -typedef struct { - int rate; - int dsor; - int fsref; -} TSC210xRateInfo; - -/* { rate, dsor, fsref } */ -static const TSC210xRateInfo tsc2102_rates[] = { - /* Fsref / 6.0 */ - { 7350, 63, 1 }, - { 8000, 63, 0 }, - /* Fsref / 6.0 */ - { 7350, 54, 1 }, - { 8000, 54, 0 }, - /* Fsref / 5.0 */ - { 8820, 45, 1 }, - { 9600, 45, 0 }, - /* Fsref / 4.0 */ - { 11025, 36, 1 }, - { 12000, 36, 0 }, - /* Fsref / 3.0 */ - { 14700, 27, 1 }, - { 16000, 27, 0 }, - /* Fsref / 2.0 */ - { 22050, 18, 1 }, - { 24000, 18, 0 }, - /* Fsref / 1.5 */ - { 29400, 9, 1 }, - { 32000, 9, 0 }, - /* Fsref */ - { 44100, 0, 1 }, - { 48000, 0, 0 }, - - { 0, 0, 0 }, -}; - -static inline void tsc210x_out_flush(TSC210xState *s, int len) -{ - uint8_t *data = s->codec.out.fifo + s->codec.out.start; - uint8_t *end = data + len; - - while (data < end) - data += AUD_write(s->dac_voice[0], data, end - data) ?: (end - data); - - s->codec.out.len -= len; - if (s->codec.out.len) - memmove(s->codec.out.fifo, end, s->codec.out.len); - s->codec.out.start = 0; -} - -static void tsc210x_audio_out_cb(TSC210xState *s, int free_b) -{ - if (s->codec.out.len >= free_b) { - tsc210x_out_flush(s, free_b); - return; - } - - s->codec.out.size = MIN(free_b, 16384); - qemu_irq_raise(s->codec.tx_start); -} - -static void tsc2102_audio_rate_update(TSC210xState *s) -{ - const TSC210xRateInfo *rate; - - s->codec.tx_rate = 0; - s->codec.rx_rate = 0; - if (s->dac_power & (1 << 15)) /* PWDNC */ - return; - - for (rate = tsc2102_rates; rate->rate; rate ++) - if (rate->dsor == (s->audio_ctrl1 & 0x3f) && /* DACFS */ - rate->fsref == ((s->audio_ctrl3 >> 13) & 1))/* REFFS */ - break; - if (!rate->rate) { - printf("%s: unknown sampling rate configured\n", __FUNCTION__); - return; - } - - s->codec.tx_rate = rate->rate; -} - -static void tsc2102_audio_output_update(TSC210xState *s) -{ - int enable; - struct audsettings fmt; - - if (s->dac_voice[0]) { - tsc210x_out_flush(s, s->codec.out.len); - s->codec.out.size = 0; - AUD_set_active_out(s->dac_voice[0], 0); - AUD_close_out(&s->card, s->dac_voice[0]); - s->dac_voice[0] = NULL; - } - s->codec.cts = 0; - - enable = - (~s->dac_power & (1 << 15)) && /* PWDNC */ - (~s->dac_power & (1 << 10)); /* DAPWDN */ - if (!enable || !s->codec.tx_rate) - return; - - /* Force our own sampling rate even in slave DAC mode */ - fmt.endianness = 0; - fmt.nchannels = 2; - fmt.freq = s->codec.tx_rate; - fmt.fmt = AUD_FMT_S16; - - s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0], - "tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt); - if (s->dac_voice[0]) { - s->codec.cts = 1; - AUD_set_active_out(s->dac_voice[0], 1); - } -} - -static uint16_t tsc2102_data_register_read(TSC210xState *s, int reg) -{ - switch (reg) { - case 0x00: /* X */ - s->dav &= 0xfbff; - return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) + - (s->noise & 3); - - case 0x01: /* Y */ - s->noise ++; - s->dav &= 0xfdff; - return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^ - (s->noise & 3); - - case 0x02: /* Z1 */ - s->dav &= 0xfeff; - return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) - - (s->noise & 3); - - case 0x03: /* Z2 */ - s->dav &= 0xff7f; - return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) | - (s->noise & 3); - - case 0x04: /* KPData */ - if ((s->model & 0xff00) == 0x2300) { - if (s->kb.intr && (s->kb.mode & 2)) { - s->kb.intr = 0; - qemu_irq_raise(s->kbint); - } - return s->kb.down; - } - - return 0xffff; - - case 0x05: /* BAT1 */ - s->dav &= 0xffbf; - return TSC_CUT_RESOLUTION(BAT1_VAL, s->precision) + - (s->noise & 6); - - case 0x06: /* BAT2 */ - s->dav &= 0xffdf; - return TSC_CUT_RESOLUTION(BAT2_VAL, s->precision); - - case 0x07: /* AUX1 */ - s->dav &= 0xffef; - return TSC_CUT_RESOLUTION(AUX1_VAL, s->precision); - - case 0x08: /* AUX2 */ - s->dav &= 0xfff7; - return 0xffff; - - case 0x09: /* TEMP1 */ - s->dav &= 0xfffb; - return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) - - (s->noise & 5); - - case 0x0a: /* TEMP2 */ - s->dav &= 0xfffd; - return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^ - (s->noise & 3); - - case 0x0b: /* DAC */ - s->dav &= 0xfffe; - return 0xffff; - - default: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_data_register_read: " - "no such register: 0x%02x\n", reg); -#endif - return 0xffff; - } -} - -static uint16_t tsc2102_control_register_read( - TSC210xState *s, int reg) -{ - switch (reg) { - case 0x00: /* TSC ADC */ - return (s->pressure << 15) | ((!s->busy) << 14) | - (s->nextfunction << 10) | (s->nextprecision << 8) | s->filter; - - case 0x01: /* Status / Keypad Control */ - if ((s->model & 0xff00) == 0x2100) - return (s->pin_func << 14) | ((!s->enabled) << 13) | - (s->host_mode << 12) | ((!!s->dav) << 11) | s->dav; - else - return (s->kb.intr << 15) | ((s->kb.scan || !s->kb.down) << 14) | - (s->kb.debounce << 11); - - case 0x02: /* DAC Control */ - if ((s->model & 0xff00) == 0x2300) - return s->dac_power & 0x8000; - else - goto bad_reg; - - case 0x03: /* Reference */ - return s->ref; - - case 0x04: /* Reset */ - return 0xffff; - - case 0x05: /* Configuration */ - return s->timing; - - case 0x06: /* Secondary configuration */ - if ((s->model & 0xff00) == 0x2100) - goto bad_reg; - return ((!s->dav) << 15) | ((s->kb.mode & 1) << 14) | s->pll[2]; - - case 0x10: /* Keypad Mask */ - if ((s->model & 0xff00) == 0x2100) - goto bad_reg; - return s->kb.mask; - - default: - bad_reg: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_control_register_read: " - "no such register: 0x%02x\n", reg); -#endif - return 0xffff; - } -} - -static uint16_t tsc2102_audio_register_read(TSC210xState *s, int reg) -{ - int l_ch, r_ch; - uint16_t val; - - switch (reg) { - case 0x00: /* Audio Control 1 */ - return s->audio_ctrl1; - - case 0x01: - return 0xff00; - - case 0x02: /* DAC Volume Control */ - return s->volume; - - case 0x03: - return 0x8b00; - - case 0x04: /* Audio Control 2 */ - l_ch = 1; - r_ch = 1; - if (s->softstep && !(s->dac_power & (1 << 10))) { - l_ch = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) > - s->volume_change + TSC_SOFTSTEP_DELAY); - r_ch = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) > - s->volume_change + TSC_SOFTSTEP_DELAY); - } - - return s->audio_ctrl2 | (l_ch << 3) | (r_ch << 2); - - case 0x05: /* Stereo DAC Power Control */ - return 0x2aa0 | s->dac_power | - (((s->dac_power & (1 << 10)) && - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) > - s->powerdown + TSC_POWEROFF_DELAY)) << 6); - - case 0x06: /* Audio Control 3 */ - val = s->audio_ctrl3 | 0x0001; - s->audio_ctrl3 &= 0xff3f; - return val; - - case 0x07: /* LCH_BASS_BOOST_N0 */ - case 0x08: /* LCH_BASS_BOOST_N1 */ - case 0x09: /* LCH_BASS_BOOST_N2 */ - case 0x0a: /* LCH_BASS_BOOST_N3 */ - case 0x0b: /* LCH_BASS_BOOST_N4 */ - case 0x0c: /* LCH_BASS_BOOST_N5 */ - case 0x0d: /* LCH_BASS_BOOST_D1 */ - case 0x0e: /* LCH_BASS_BOOST_D2 */ - case 0x0f: /* LCH_BASS_BOOST_D4 */ - case 0x10: /* LCH_BASS_BOOST_D5 */ - case 0x11: /* RCH_BASS_BOOST_N0 */ - case 0x12: /* RCH_BASS_BOOST_N1 */ - case 0x13: /* RCH_BASS_BOOST_N2 */ - case 0x14: /* RCH_BASS_BOOST_N3 */ - case 0x15: /* RCH_BASS_BOOST_N4 */ - case 0x16: /* RCH_BASS_BOOST_N5 */ - case 0x17: /* RCH_BASS_BOOST_D1 */ - case 0x18: /* RCH_BASS_BOOST_D2 */ - case 0x19: /* RCH_BASS_BOOST_D4 */ - case 0x1a: /* RCH_BASS_BOOST_D5 */ - return s->filter_data[reg - 0x07]; - - case 0x1b: /* PLL Programmability 1 */ - return s->pll[0]; - - case 0x1c: /* PLL Programmability 2 */ - return s->pll[1]; - - case 0x1d: /* Audio Control 4 */ - return (!s->softstep) << 14; - - default: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_audio_register_read: " - "no such register: 0x%02x\n", reg); -#endif - return 0xffff; - } -} - -static void tsc2102_data_register_write( - TSC210xState *s, int reg, uint16_t value) -{ - switch (reg) { - case 0x00: /* X */ - case 0x01: /* Y */ - case 0x02: /* Z1 */ - case 0x03: /* Z2 */ - case 0x05: /* BAT1 */ - case 0x06: /* BAT2 */ - case 0x07: /* AUX1 */ - case 0x08: /* AUX2 */ - case 0x09: /* TEMP1 */ - case 0x0a: /* TEMP2 */ - return; - - default: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_data_register_write: " - "no such register: 0x%02x\n", reg); -#endif - } -} - -static void tsc2102_control_register_write( - TSC210xState *s, int reg, uint16_t value) -{ - switch (reg) { - case 0x00: /* TSC ADC */ - s->host_mode = value >> 15; - s->enabled = !(value & 0x4000); - if (s->busy && !s->enabled) - timer_del(s->timer); - s->busy &= s->enabled; - s->nextfunction = (value >> 10) & 0xf; - s->nextprecision = (value >> 8) & 3; - s->filter = value & 0xff; - return; - - case 0x01: /* Status / Keypad Control */ - if ((s->model & 0xff00) == 0x2100) - s->pin_func = value >> 14; - else { - s->kb.scan = (value >> 14) & 1; - s->kb.debounce = (value >> 11) & 7; - if (s->kb.intr && s->kb.scan) { - s->kb.intr = 0; - qemu_irq_raise(s->kbint); - } - } - return; - - case 0x02: /* DAC Control */ - if ((s->model & 0xff00) == 0x2300) { - s->dac_power &= 0x7fff; - s->dac_power |= 0x8000 & value; - } else - goto bad_reg; - break; - - case 0x03: /* Reference */ - s->ref = value & 0x1f; - return; - - case 0x04: /* Reset */ - if (value == 0xbb00) { - if (s->busy) - timer_del(s->timer); - tsc210x_reset(s); -#ifdef TSC_VERBOSE - } else { - fprintf(stderr, "tsc2102_control_register_write: " - "wrong value written into RESET\n"); -#endif - } - return; - - case 0x05: /* Configuration */ - s->timing = value & 0x3f; -#ifdef TSC_VERBOSE - if (value & ~0x3f) - fprintf(stderr, "tsc2102_control_register_write: " - "wrong value written into CONFIG\n"); -#endif - return; - - case 0x06: /* Secondary configuration */ - if ((s->model & 0xff00) == 0x2100) - goto bad_reg; - s->kb.mode = value >> 14; - s->pll[2] = value & 0x3ffff; - return; - - case 0x10: /* Keypad Mask */ - if ((s->model & 0xff00) == 0x2100) - goto bad_reg; - s->kb.mask = value; - return; - - default: - bad_reg: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_control_register_write: " - "no such register: 0x%02x\n", reg); -#endif - } -} - -static void tsc2102_audio_register_write( - TSC210xState *s, int reg, uint16_t value) -{ - switch (reg) { - case 0x00: /* Audio Control 1 */ - s->audio_ctrl1 = value & 0x0f3f; -#ifdef TSC_VERBOSE - if ((value & ~0x0f3f) || ((value & 7) != ((value >> 3) & 7))) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into Audio 1\n"); -#endif - tsc2102_audio_rate_update(s); - tsc2102_audio_output_update(s); - return; - - case 0x01: -#ifdef TSC_VERBOSE - if (value != 0xff00) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into reg 0x01\n"); -#endif - return; - - case 0x02: /* DAC Volume Control */ - s->volume = value; - s->volume_change = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - return; - - case 0x03: -#ifdef TSC_VERBOSE - if (value != 0x8b00) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into reg 0x03\n"); -#endif - return; - - case 0x04: /* Audio Control 2 */ - s->audio_ctrl2 = value & 0xf7f2; -#ifdef TSC_VERBOSE - if (value & ~0xf7fd) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into Audio 2\n"); -#endif - return; - - case 0x05: /* Stereo DAC Power Control */ - if ((value & ~s->dac_power) & (1 << 10)) - s->powerdown = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - s->dac_power = value & 0x9543; -#ifdef TSC_VERBOSE - if ((value & ~0x9543) != 0x2aa0) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into Power\n"); -#endif - tsc2102_audio_rate_update(s); - tsc2102_audio_output_update(s); - return; - - case 0x06: /* Audio Control 3 */ - s->audio_ctrl3 &= 0x00c0; - s->audio_ctrl3 |= value & 0xf800; -#ifdef TSC_VERBOSE - if (value & ~0xf8c7) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into Audio 3\n"); -#endif - tsc2102_audio_output_update(s); - return; - - case 0x07: /* LCH_BASS_BOOST_N0 */ - case 0x08: /* LCH_BASS_BOOST_N1 */ - case 0x09: /* LCH_BASS_BOOST_N2 */ - case 0x0a: /* LCH_BASS_BOOST_N3 */ - case 0x0b: /* LCH_BASS_BOOST_N4 */ - case 0x0c: /* LCH_BASS_BOOST_N5 */ - case 0x0d: /* LCH_BASS_BOOST_D1 */ - case 0x0e: /* LCH_BASS_BOOST_D2 */ - case 0x0f: /* LCH_BASS_BOOST_D4 */ - case 0x10: /* LCH_BASS_BOOST_D5 */ - case 0x11: /* RCH_BASS_BOOST_N0 */ - case 0x12: /* RCH_BASS_BOOST_N1 */ - case 0x13: /* RCH_BASS_BOOST_N2 */ - case 0x14: /* RCH_BASS_BOOST_N3 */ - case 0x15: /* RCH_BASS_BOOST_N4 */ - case 0x16: /* RCH_BASS_BOOST_N5 */ - case 0x17: /* RCH_BASS_BOOST_D1 */ - case 0x18: /* RCH_BASS_BOOST_D2 */ - case 0x19: /* RCH_BASS_BOOST_D4 */ - case 0x1a: /* RCH_BASS_BOOST_D5 */ - s->filter_data[reg - 0x07] = value; - return; - - case 0x1b: /* PLL Programmability 1 */ - s->pll[0] = value & 0xfffc; -#ifdef TSC_VERBOSE - if (value & ~0xfffc) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into PLL 1\n"); -#endif - return; - - case 0x1c: /* PLL Programmability 2 */ - s->pll[1] = value & 0xfffc; -#ifdef TSC_VERBOSE - if (value & ~0xfffc) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into PLL 2\n"); -#endif - return; - - case 0x1d: /* Audio Control 4 */ - s->softstep = !(value & 0x4000); -#ifdef TSC_VERBOSE - if (value & ~0x4000) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into Audio 4\n"); -#endif - return; - - default: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_audio_register_write: " - "no such register: 0x%02x\n", reg); -#endif - } -} - -/* This handles most of the chip logic. */ -static void tsc210x_pin_update(TSC210xState *s) -{ - int64_t expires; - int pin_state; - - switch (s->pin_func) { - case 0: - pin_state = s->pressure; - break; - case 1: - pin_state = !!s->dav; - break; - case 2: - default: - pin_state = s->pressure && !s->dav; - } - - if (!s->enabled) - pin_state = 0; - - if (pin_state != s->irq) { - s->irq = pin_state; - qemu_set_irq(s->pint, !s->irq); - } - - switch (s->nextfunction) { - case TSC_MODE_XY_SCAN: - case TSC_MODE_XYZ_SCAN: - if (!s->pressure) - return; - break; - - case TSC_MODE_X: - case TSC_MODE_Y: - case TSC_MODE_Z: - if (!s->pressure) - return; - /* Fall through */ - case TSC_MODE_BAT1: - case TSC_MODE_BAT2: - case TSC_MODE_AUX: - case TSC_MODE_TEMP1: - case TSC_MODE_TEMP2: - if (s->dav) - s->enabled = 0; - break; - - case TSC_MODE_AUX_SCAN: - case TSC_MODE_PORT_SCAN: - break; - - case TSC_MODE_NO_SCAN: - case TSC_MODE_XX_DRV: - case TSC_MODE_YY_DRV: - case TSC_MODE_YX_DRV: - default: - return; - } - - if (!s->enabled || s->busy || s->dav) - return; - - s->busy = 1; - s->precision = s->nextprecision; - s->function = s->nextfunction; - expires = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND >> 10); - timer_mod(s->timer, expires); -} - -static uint16_t tsc210x_read(TSC210xState *s) -{ - uint16_t ret = 0x0000; - - if (!s->command) - fprintf(stderr, "tsc210x_read: SPI underrun!\n"); - - switch (s->page) { - case TSC_DATA_REGISTERS_PAGE: - ret = tsc2102_data_register_read(s, s->offset); - if (!s->dav) - qemu_irq_raise(s->davint); - break; - case TSC_CONTROL_REGISTERS_PAGE: - ret = tsc2102_control_register_read(s, s->offset); - break; - case TSC_AUDIO_REGISTERS_PAGE: - ret = tsc2102_audio_register_read(s, s->offset); - break; - default: - hw_error("tsc210x_read: wrong memory page\n"); - } - - tsc210x_pin_update(s); - - /* Allow sequential reads. */ - s->offset ++; - s->state = 0; - return ret; -} - -static void tsc210x_write(TSC210xState *s, uint16_t value) -{ - /* - * This is a two-state state machine for reading - * command and data every second time. - */ - if (!s->state) { - s->command = value >> 15; - s->page = (value >> 11) & 0x0f; - s->offset = (value >> 5) & 0x3f; - s->state = 1; - } else { - if (s->command) - fprintf(stderr, "tsc210x_write: SPI overrun!\n"); - else - switch (s->page) { - case TSC_DATA_REGISTERS_PAGE: - tsc2102_data_register_write(s, s->offset, value); - break; - case TSC_CONTROL_REGISTERS_PAGE: - tsc2102_control_register_write(s, s->offset, value); - break; - case TSC_AUDIO_REGISTERS_PAGE: - tsc2102_audio_register_write(s, s->offset, value); - break; - default: - hw_error("tsc210x_write: wrong memory page\n"); - } - - tsc210x_pin_update(s); - s->state = 0; - } -} - -uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len) -{ - TSC210xState *s = opaque; - uint32_t ret = 0; - - if (len != 16) - hw_error("%s: FIXME: bad SPI word width %i\n", __FUNCTION__, len); - - /* TODO: sequential reads etc - how do we make sure the host doesn't - * unintentionally read out a conversion result from a register while - * transmitting the command word of the next command? */ - if (!value || (s->state && s->command)) - ret = tsc210x_read(s); - if (value || (s->state && !s->command)) - tsc210x_write(s, value); - - return ret; -} - -static void tsc210x_timer_tick(void *opaque) -{ - TSC210xState *s = opaque; - - /* Timer ticked -- a set of conversions has been finished. */ - - if (!s->busy) - return; - - s->busy = 0; - s->dav |= mode_regs[s->function]; - tsc210x_pin_update(s); - qemu_irq_lower(s->davint); -} - -static void tsc210x_touchscreen_event(void *opaque, - int x, int y, int z, int buttons_state) -{ - TSC210xState *s = opaque; - int p = s->pressure; - - if (buttons_state) { - s->x = x; - s->y = y; - } - s->pressure = !!buttons_state; - - /* - * Note: We would get better responsiveness in the guest by - * signaling TS events immediately, but for now we simulate - * the first conversion delay for sake of correctness. - */ - if (p != s->pressure) - tsc210x_pin_update(s); -} - -static void tsc210x_i2s_swallow(TSC210xState *s) -{ - if (s->dac_voice[0]) - tsc210x_out_flush(s, s->codec.out.len); - else - s->codec.out.len = 0; -} - -static void tsc210x_i2s_set_rate(TSC210xState *s, int in, int out) -{ - s->i2s_tx_rate = out; - s->i2s_rx_rate = in; -} - -static void tsc210x_save(QEMUFile *f, void *opaque) -{ - TSC210xState *s = (TSC210xState *) opaque; - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - int i; - - qemu_put_be16(f, s->x); - qemu_put_be16(f, s->y); - qemu_put_byte(f, s->pressure); - - qemu_put_byte(f, s->state); - qemu_put_byte(f, s->page); - qemu_put_byte(f, s->offset); - qemu_put_byte(f, s->command); - - qemu_put_byte(f, s->irq); - qemu_put_be16s(f, &s->dav); - - timer_put(f, s->timer); - qemu_put_byte(f, s->enabled); - qemu_put_byte(f, s->host_mode); - qemu_put_byte(f, s->function); - qemu_put_byte(f, s->nextfunction); - qemu_put_byte(f, s->precision); - qemu_put_byte(f, s->nextprecision); - qemu_put_byte(f, s->filter); - qemu_put_byte(f, s->pin_func); - qemu_put_byte(f, s->ref); - qemu_put_byte(f, s->timing); - qemu_put_be32(f, s->noise); - - qemu_put_be16s(f, &s->audio_ctrl1); - qemu_put_be16s(f, &s->audio_ctrl2); - qemu_put_be16s(f, &s->audio_ctrl3); - qemu_put_be16s(f, &s->pll[0]); - qemu_put_be16s(f, &s->pll[1]); - qemu_put_be16s(f, &s->volume); - qemu_put_sbe64(f, (s->volume_change - now)); - qemu_put_sbe64(f, (s->powerdown - now)); - qemu_put_byte(f, s->softstep); - qemu_put_be16s(f, &s->dac_power); - - for (i = 0; i < 0x14; i ++) - qemu_put_be16s(f, &s->filter_data[i]); -} - -static int tsc210x_load(QEMUFile *f, void *opaque, int version_id) -{ - TSC210xState *s = (TSC210xState *) opaque; - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - int i; - - s->x = qemu_get_be16(f); - s->y = qemu_get_be16(f); - s->pressure = qemu_get_byte(f); - - s->state = qemu_get_byte(f); - s->page = qemu_get_byte(f); - s->offset = qemu_get_byte(f); - s->command = qemu_get_byte(f); - - s->irq = qemu_get_byte(f); - qemu_get_be16s(f, &s->dav); - - timer_get(f, s->timer); - s->enabled = qemu_get_byte(f); - s->host_mode = qemu_get_byte(f); - s->function = qemu_get_byte(f); - if (s->function < 0 || s->function >= ARRAY_SIZE(mode_regs)) { - return -EINVAL; - } - s->nextfunction = qemu_get_byte(f); - if (s->nextfunction < 0 || s->nextfunction >= ARRAY_SIZE(mode_regs)) { - return -EINVAL; - } - s->precision = qemu_get_byte(f); - if (s->precision < 0 || s->precision >= ARRAY_SIZE(resolution)) { - return -EINVAL; - } - s->nextprecision = qemu_get_byte(f); - if (s->nextprecision < 0 || s->nextprecision >= ARRAY_SIZE(resolution)) { - return -EINVAL; - } - s->filter = qemu_get_byte(f); - s->pin_func = qemu_get_byte(f); - s->ref = qemu_get_byte(f); - s->timing = qemu_get_byte(f); - s->noise = qemu_get_be32(f); - - qemu_get_be16s(f, &s->audio_ctrl1); - qemu_get_be16s(f, &s->audio_ctrl2); - qemu_get_be16s(f, &s->audio_ctrl3); - qemu_get_be16s(f, &s->pll[0]); - qemu_get_be16s(f, &s->pll[1]); - qemu_get_be16s(f, &s->volume); - s->volume_change = qemu_get_sbe64(f) + now; - s->powerdown = qemu_get_sbe64(f) + now; - s->softstep = qemu_get_byte(f); - qemu_get_be16s(f, &s->dac_power); - - for (i = 0; i < 0x14; i ++) - qemu_get_be16s(f, &s->filter_data[i]); - - s->busy = timer_pending(s->timer); - qemu_set_irq(s->pint, !s->irq); - qemu_set_irq(s->davint, !s->dav); - - return 0; -} - -uWireSlave *tsc2102_init(qemu_irq pint) -{ - TSC210xState *s; - - s = g_new0(TSC210xState, 1); - s->x = 160; - s->y = 160; - s->pressure = 0; - s->precision = s->nextprecision = 0; - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tsc210x_timer_tick, s); - s->pint = pint; - s->model = 0x2102; - s->name = "tsc2102"; - - s->tr[0] = 0; - s->tr[1] = 1; - s->tr[2] = 1; - s->tr[3] = 0; - s->tr[4] = 1; - s->tr[5] = 0; - s->tr[6] = 1; - s->tr[7] = 0; - - s->chip.opaque = s; - s->chip.send = (void *) tsc210x_write; - s->chip.receive = (void *) tsc210x_read; - - s->codec.opaque = s; - s->codec.tx_swallow = (void *) tsc210x_i2s_swallow; - s->codec.set_rate = (void *) tsc210x_i2s_set_rate; - s->codec.in.fifo = s->in_fifo; - s->codec.out.fifo = s->out_fifo; - - tsc210x_reset(s); - - qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1, - "QEMU TSC2102-driven Touchscreen"); - - AUD_register_card(s->name, &s->card); - - qemu_register_reset((void *) tsc210x_reset, s); - register_savevm(NULL, s->name, -1, 0, - tsc210x_save, tsc210x_load, s); - - return &s->chip; -} - -uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav) -{ - TSC210xState *s; - - s = g_new0(TSC210xState, 1); - s->x = 400; - s->y = 240; - s->pressure = 0; - s->precision = s->nextprecision = 0; - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tsc210x_timer_tick, s); - s->pint = penirq; - s->kbint = kbirq; - s->davint = dav; - s->model = 0x2301; - s->name = "tsc2301"; - - s->tr[0] = 0; - s->tr[1] = 1; - s->tr[2] = 1; - s->tr[3] = 0; - s->tr[4] = 1; - s->tr[5] = 0; - s->tr[6] = 1; - s->tr[7] = 0; - - s->chip.opaque = s; - s->chip.send = (void *) tsc210x_write; - s->chip.receive = (void *) tsc210x_read; - - s->codec.opaque = s; - s->codec.tx_swallow = (void *) tsc210x_i2s_swallow; - s->codec.set_rate = (void *) tsc210x_i2s_set_rate; - s->codec.in.fifo = s->in_fifo; - s->codec.out.fifo = s->out_fifo; - - tsc210x_reset(s); - - qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1, - "QEMU TSC2301-driven Touchscreen"); - - AUD_register_card(s->name, &s->card); - - qemu_register_reset((void *) tsc210x_reset, s); - register_savevm(NULL, s->name, -1, 0, tsc210x_save, tsc210x_load, s); - - return &s->chip; -} - -I2SCodec *tsc210x_codec(uWireSlave *chip) -{ - TSC210xState *s = (TSC210xState *) chip->opaque; - - return &s->codec; -} - -/* - * Use tslib generated calibration data to generate ADC input values - * from the touchscreen. Assuming 12-bit precision was used during - * tslib calibration. - */ -void tsc210x_set_transform(uWireSlave *chip, - MouseTransformInfo *info) -{ - TSC210xState *s = (TSC210xState *) chip->opaque; -#if 0 - int64_t ltr[8]; - - ltr[0] = (int64_t) info->a[1] * info->y; - ltr[1] = (int64_t) info->a[4] * info->x; - ltr[2] = (int64_t) info->a[1] * info->a[3] - - (int64_t) info->a[4] * info->a[0]; - ltr[3] = (int64_t) info->a[2] * info->a[4] - - (int64_t) info->a[5] * info->a[1]; - ltr[4] = (int64_t) info->a[0] * info->y; - ltr[5] = (int64_t) info->a[3] * info->x; - ltr[6] = (int64_t) info->a[4] * info->a[0] - - (int64_t) info->a[1] * info->a[3]; - ltr[7] = (int64_t) info->a[2] * info->a[3] - - (int64_t) info->a[5] * info->a[0]; - - /* Avoid integer overflow */ - s->tr[0] = ltr[0] >> 11; - s->tr[1] = ltr[1] >> 11; - s->tr[2] = muldiv64(ltr[2], 1, info->a[6]); - s->tr[3] = muldiv64(ltr[3], 1 << 4, ltr[2]); - s->tr[4] = ltr[4] >> 11; - s->tr[5] = ltr[5] >> 11; - s->tr[6] = muldiv64(ltr[6], 1, info->a[6]); - s->tr[7] = muldiv64(ltr[7], 1 << 4, ltr[6]); -#else - - /* This version assumes touchscreen X & Y axis are parallel or - * perpendicular to LCD's X & Y axis in some way. */ - if (abs(info->a[0]) > abs(info->a[1])) { - s->tr[0] = 0; - s->tr[1] = -info->a[6] * info->x; - s->tr[2] = info->a[0]; - s->tr[3] = -info->a[2] / info->a[0]; - s->tr[4] = info->a[6] * info->y; - s->tr[5] = 0; - s->tr[6] = info->a[4]; - s->tr[7] = -info->a[5] / info->a[4]; - } else { - s->tr[0] = info->a[6] * info->y; - s->tr[1] = 0; - s->tr[2] = info->a[1]; - s->tr[3] = -info->a[2] / info->a[1]; - s->tr[4] = 0; - s->tr[5] = -info->a[6] * info->x; - s->tr[6] = info->a[3]; - s->tr[7] = -info->a[5] / info->a[3]; - } - - s->tr[0] >>= 11; - s->tr[1] >>= 11; - s->tr[3] <<= 4; - s->tr[4] >>= 11; - s->tr[5] >>= 11; - s->tr[7] <<= 4; -#endif -} - -void tsc210x_key_event(uWireSlave *chip, int key, int down) -{ - TSC210xState *s = (TSC210xState *) chip->opaque; - - if (down) - s->kb.down |= 1 << key; - else - s->kb.down &= ~(1 << key); - - if (down && (s->kb.down & ~s->kb.mask) && !s->kb.intr) { - s->kb.intr = 1; - qemu_irq_lower(s->kbint); - } else if (s->kb.intr && !(s->kb.down & ~s->kb.mask) && - !(s->kb.mode & 1)) { - s->kb.intr = 0; - qemu_irq_raise(s->kbint); - } -} diff --git a/qemu/hw/input/virtio-input-hid.c b/qemu/hw/input/virtio-input-hid.c deleted file mode 100644 index 3ee0c1814..000000000 --- a/qemu/hw/input/virtio-input-hid.c +++ /dev/null @@ -1,525 +0,0 @@ -/* - * 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 "qemu/iov.h" - -#include "hw/qdev.h" -#include "hw/virtio/virtio.h" -#include "hw/virtio/virtio-input.h" - -#undef CONFIG_CURSES -#include "ui/console.h" - -#include "standard-headers/linux/input.h" - -#define VIRTIO_ID_NAME_KEYBOARD "QEMU Virtio Keyboard" -#define VIRTIO_ID_NAME_MOUSE "QEMU Virtio Mouse" -#define VIRTIO_ID_NAME_TABLET "QEMU Virtio Tablet" - -/* ----------------------------------------------------------------- */ - -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, - [Q_KEY_CODE_3] = KEY_3, - [Q_KEY_CODE_4] = KEY_4, - [Q_KEY_CODE_5] = KEY_5, - [Q_KEY_CODE_6] = KEY_6, - [Q_KEY_CODE_7] = KEY_7, - [Q_KEY_CODE_8] = KEY_8, - [Q_KEY_CODE_9] = KEY_9, - [Q_KEY_CODE_0] = KEY_0, - [Q_KEY_CODE_MINUS] = KEY_MINUS, - [Q_KEY_CODE_EQUAL] = KEY_EQUAL, - [Q_KEY_CODE_BACKSPACE] = KEY_BACKSPACE, - - [Q_KEY_CODE_TAB] = KEY_TAB, - [Q_KEY_CODE_Q] = KEY_Q, - [Q_KEY_CODE_W] = KEY_W, - [Q_KEY_CODE_E] = KEY_E, - [Q_KEY_CODE_R] = KEY_R, - [Q_KEY_CODE_T] = KEY_T, - [Q_KEY_CODE_Y] = KEY_Y, - [Q_KEY_CODE_U] = KEY_U, - [Q_KEY_CODE_I] = KEY_I, - [Q_KEY_CODE_O] = KEY_O, - [Q_KEY_CODE_P] = KEY_P, - [Q_KEY_CODE_BRACKET_LEFT] = KEY_LEFTBRACE, - [Q_KEY_CODE_BRACKET_RIGHT] = KEY_RIGHTBRACE, - [Q_KEY_CODE_RET] = KEY_ENTER, - - [Q_KEY_CODE_CTRL] = KEY_LEFTCTRL, - [Q_KEY_CODE_A] = KEY_A, - [Q_KEY_CODE_S] = KEY_S, - [Q_KEY_CODE_D] = KEY_D, - [Q_KEY_CODE_F] = KEY_F, - [Q_KEY_CODE_G] = KEY_G, - [Q_KEY_CODE_H] = KEY_H, - [Q_KEY_CODE_J] = KEY_J, - [Q_KEY_CODE_K] = KEY_K, - [Q_KEY_CODE_L] = KEY_L, - [Q_KEY_CODE_SEMICOLON] = KEY_SEMICOLON, - [Q_KEY_CODE_APOSTROPHE] = KEY_APOSTROPHE, - [Q_KEY_CODE_GRAVE_ACCENT] = KEY_GRAVE, - - [Q_KEY_CODE_SHIFT] = KEY_LEFTSHIFT, - [Q_KEY_CODE_BACKSLASH] = KEY_BACKSLASH, - [Q_KEY_CODE_LESS] = KEY_102ND, - [Q_KEY_CODE_Z] = KEY_Z, - [Q_KEY_CODE_X] = KEY_X, - [Q_KEY_CODE_C] = KEY_C, - [Q_KEY_CODE_V] = KEY_V, - [Q_KEY_CODE_B] = KEY_B, - [Q_KEY_CODE_N] = KEY_N, - [Q_KEY_CODE_M] = KEY_M, - [Q_KEY_CODE_COMMA] = KEY_COMMA, - [Q_KEY_CODE_DOT] = KEY_DOT, - [Q_KEY_CODE_SLASH] = KEY_SLASH, - [Q_KEY_CODE_SHIFT_R] = KEY_RIGHTSHIFT, - - [Q_KEY_CODE_ALT] = KEY_LEFTALT, - [Q_KEY_CODE_SPC] = KEY_SPACE, - [Q_KEY_CODE_CAPS_LOCK] = KEY_CAPSLOCK, - - [Q_KEY_CODE_F1] = KEY_F1, - [Q_KEY_CODE_F2] = KEY_F2, - [Q_KEY_CODE_F3] = KEY_F3, - [Q_KEY_CODE_F4] = KEY_F4, - [Q_KEY_CODE_F5] = KEY_F5, - [Q_KEY_CODE_F6] = KEY_F6, - [Q_KEY_CODE_F7] = KEY_F7, - [Q_KEY_CODE_F8] = KEY_F8, - [Q_KEY_CODE_F9] = KEY_F9, - [Q_KEY_CODE_F10] = KEY_F10, - [Q_KEY_CODE_NUM_LOCK] = KEY_NUMLOCK, - [Q_KEY_CODE_SCROLL_LOCK] = KEY_SCROLLLOCK, - - [Q_KEY_CODE_KP_0] = KEY_KP0, - [Q_KEY_CODE_KP_1] = KEY_KP1, - [Q_KEY_CODE_KP_2] = KEY_KP2, - [Q_KEY_CODE_KP_3] = KEY_KP3, - [Q_KEY_CODE_KP_4] = KEY_KP4, - [Q_KEY_CODE_KP_5] = KEY_KP5, - [Q_KEY_CODE_KP_6] = KEY_KP6, - [Q_KEY_CODE_KP_7] = KEY_KP7, - [Q_KEY_CODE_KP_8] = KEY_KP8, - [Q_KEY_CODE_KP_9] = KEY_KP9, - [Q_KEY_CODE_KP_SUBTRACT] = KEY_KPMINUS, - [Q_KEY_CODE_KP_ADD] = KEY_KPPLUS, - [Q_KEY_CODE_KP_DECIMAL] = KEY_KPDOT, - [Q_KEY_CODE_KP_ENTER] = KEY_KPENTER, - [Q_KEY_CODE_KP_DIVIDE] = KEY_KPSLASH, - [Q_KEY_CODE_KP_MULTIPLY] = KEY_KPASTERISK, - - [Q_KEY_CODE_F11] = KEY_F11, - [Q_KEY_CODE_F12] = KEY_F12, - - [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, - [Q_KEY_CODE_UP] = KEY_UP, - [Q_KEY_CODE_PGUP] = KEY_PAGEUP, - [Q_KEY_CODE_LEFT] = KEY_LEFT, - [Q_KEY_CODE_RIGHT] = KEY_RIGHT, - [Q_KEY_CODE_END] = KEY_END, - [Q_KEY_CODE_DOWN] = KEY_DOWN, - [Q_KEY_CODE_PGDN] = KEY_PAGEDOWN, - [Q_KEY_CODE_INSERT] = KEY_INSERT, - [Q_KEY_CODE_DELETE] = KEY_DELETE, - - [Q_KEY_CODE_META_L] = KEY_LEFTMETA, - [Q_KEY_CODE_META_R] = KEY_RIGHTMETA, - [Q_KEY_CODE_MENU] = KEY_MENU, -}; - -static const unsigned int keymap_button[INPUT_BUTTON__MAX] = { - [INPUT_BUTTON_LEFT] = BTN_LEFT, - [INPUT_BUTTON_RIGHT] = BTN_RIGHT, - [INPUT_BUTTON_MIDDLE] = BTN_MIDDLE, - [INPUT_BUTTON_WHEEL_UP] = BTN_GEAR_UP, - [INPUT_BUTTON_WHEEL_DOWN] = BTN_GEAR_DOWN, -}; - -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] = { - [INPUT_AXIS_X] = ABS_X, - [INPUT_AXIS_Y] = ABS_Y, -}; - -/* ----------------------------------------------------------------- */ - -static void virtio_input_key_config(VirtIOInput *vinput, - const unsigned int *keymap, - size_t mapsize) -{ - virtio_input_config keys; - int i, bit, byte, bmax = 0; - - memset(&keys, 0, sizeof(keys)); - for (i = 0; i < mapsize; i++) { - bit = keymap[i]; - if (!bit) { - continue; - } - byte = bit / 8; - bit = bit % 8; - keys.u.bitmap[byte] |= (1 << bit); - if (bmax < byte+1) { - bmax = byte+1; - } - } - keys.select = VIRTIO_INPUT_CFG_EV_BITS; - keys.subsel = EV_KEY; - keys.size = bmax; - virtio_input_add_config(vinput, &keys); -} - -static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) -{ - VirtIOInput *vinput = VIRTIO_INPUT(dev); - virtio_input_event event; - int qcode; - InputKeyEvent *key; - InputMoveEvent *move; - InputBtnEvent *btn; - - switch (evt->type) { - case INPUT_EVENT_KIND_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(key->down ? 1 : 0); - virtio_input_send(vinput, &event); - } else { - if (key->down) { - fprintf(stderr, "%s: unmapped key: %d [%s]\n", __func__, - qcode, QKeyCode_lookup[qcode]); - } - } - break; - case INPUT_EVENT_KIND_BTN: - btn = evt->u.btn.data; - if (keymap_button[btn->button]) { - event.type = cpu_to_le16(EV_KEY); - 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 (btn->down) { - fprintf(stderr, "%s: unmapped button: %d [%s]\n", __func__, - 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[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[move->axis]); - event.value = cpu_to_le32(move->value); - virtio_input_send(vinput, &event); - break; - default: - /* keep gcc happy */ - break; - } -} - -static void virtio_input_handle_sync(DeviceState *dev) -{ - VirtIOInput *vinput = VIRTIO_INPUT(dev); - virtio_input_event event = { - .type = cpu_to_le16(EV_SYN), - .code = cpu_to_le16(SYN_REPORT), - .value = 0, - }; - - virtio_input_send(vinput, &event); -} - -static void virtio_input_hid_realize(DeviceState *dev, Error **errp) -{ - VirtIOInputHID *vhid = VIRTIO_INPUT_HID(dev); - - vhid->hs = qemu_input_handler_register(dev, vhid->handler); - if (vhid->display && vhid->hs) { - qemu_input_handler_bind(vhid->hs, vhid->display, vhid->head, NULL); - } -} - -static void virtio_input_hid_unrealize(DeviceState *dev, Error **errp) -{ - VirtIOInputHID *vhid = VIRTIO_INPUT_HID(dev); - qemu_input_handler_unregister(vhid->hs); -} - -static void virtio_input_hid_change_active(VirtIOInput *vinput) -{ - VirtIOInputHID *vhid = VIRTIO_INPUT_HID(vinput); - - if (vinput->active) { - qemu_input_handler_activate(vhid->hs); - } else { - qemu_input_handler_deactivate(vhid->hs); - } -} - -static void virtio_input_hid_handle_status(VirtIOInput *vinput, - virtio_input_event *event) -{ - VirtIOInputHID *vhid = VIRTIO_INPUT_HID(vinput); - int ledbit = 0; - - switch (le16_to_cpu(event->type)) { - case EV_LED: - if (event->code == LED_NUML) { - ledbit = QEMU_NUM_LOCK_LED; - } else if (event->code == LED_CAPSL) { - ledbit = QEMU_CAPS_LOCK_LED; - } else if (event->code == LED_SCROLLL) { - ledbit = QEMU_SCROLL_LOCK_LED; - } - if (event->value) { - vhid->ledstate |= ledbit; - } else { - vhid->ledstate &= ~ledbit; - } - kbd_put_ledstate(vhid->ledstate); - break; - default: - fprintf(stderr, "%s: unknown type %d\n", __func__, - le16_to_cpu(event->type)); - break; - } -} - -static Property virtio_input_hid_properties[] = { - DEFINE_PROP_STRING("display", VirtIOInputHID, display), - DEFINE_PROP_UINT32("head", VirtIOInputHID, head, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_input_hid_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOInputClass *vic = VIRTIO_INPUT_CLASS(klass); - - dc->props = virtio_input_hid_properties; - vic->realize = virtio_input_hid_realize; - vic->unrealize = virtio_input_hid_unrealize; - vic->change_active = virtio_input_hid_change_active; - vic->handle_status = virtio_input_hid_handle_status; -} - -static const TypeInfo virtio_input_hid_info = { - .name = TYPE_VIRTIO_INPUT_HID, - .parent = TYPE_VIRTIO_INPUT, - .instance_size = sizeof(VirtIOInputHID), - .class_init = virtio_input_hid_class_init, - .abstract = true, -}; - -/* ----------------------------------------------------------------- */ - -static QemuInputHandler virtio_keyboard_handler = { - .name = VIRTIO_ID_NAME_KEYBOARD, - .mask = INPUT_EVENT_MASK_KEY, - .event = virtio_input_handle_event, - .sync = virtio_input_handle_sync, -}; - -static struct virtio_input_config virtio_keyboard_config[] = { - { - .select = VIRTIO_INPUT_CFG_ID_NAME, - .size = sizeof(VIRTIO_ID_NAME_KEYBOARD), - .u.string = VIRTIO_ID_NAME_KEYBOARD, - },{ - .select = VIRTIO_INPUT_CFG_ID_DEVIDS, - .size = sizeof(struct virtio_input_devids), - .u.ids = { - .bustype = const_le16(BUS_VIRTUAL), - .vendor = const_le16(0x0627), /* same we use for usb hid devices */ - .product = const_le16(0x0001), - .version = const_le16(0x0001), - }, - },{ - .select = VIRTIO_INPUT_CFG_EV_BITS, - .subsel = EV_REP, - .size = 1, - },{ - .select = VIRTIO_INPUT_CFG_EV_BITS, - .subsel = EV_LED, - .size = 1, - .u.bitmap = { - (1 << LED_NUML) | (1 << LED_CAPSL) | (1 << LED_SCROLLL), - }, - }, - { /* end of list */ }, -}; - -static void virtio_keyboard_init(Object *obj) -{ - VirtIOInputHID *vhid = VIRTIO_INPUT_HID(obj); - VirtIOInput *vinput = VIRTIO_INPUT(obj); - - vhid->handler = &virtio_keyboard_handler; - virtio_input_init_config(vinput, virtio_keyboard_config); - virtio_input_key_config(vinput, keymap_qcode, - ARRAY_SIZE(keymap_qcode)); -} - -static const TypeInfo virtio_keyboard_info = { - .name = TYPE_VIRTIO_KEYBOARD, - .parent = TYPE_VIRTIO_INPUT_HID, - .instance_size = sizeof(VirtIOInputHID), - .instance_init = virtio_keyboard_init, -}; - -/* ----------------------------------------------------------------- */ - -static QemuInputHandler virtio_mouse_handler = { - .name = VIRTIO_ID_NAME_MOUSE, - .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL, - .event = virtio_input_handle_event, - .sync = virtio_input_handle_sync, -}; - -static struct virtio_input_config virtio_mouse_config[] = { - { - .select = VIRTIO_INPUT_CFG_ID_NAME, - .size = sizeof(VIRTIO_ID_NAME_MOUSE), - .u.string = VIRTIO_ID_NAME_MOUSE, - },{ - .select = VIRTIO_INPUT_CFG_ID_DEVIDS, - .size = sizeof(struct virtio_input_devids), - .u.ids = { - .bustype = const_le16(BUS_VIRTUAL), - .vendor = const_le16(0x0627), /* same we use for usb hid devices */ - .product = const_le16(0x0002), - .version = const_le16(0x0001), - }, - },{ - .select = VIRTIO_INPUT_CFG_EV_BITS, - .subsel = EV_REL, - .size = 1, - .u.bitmap = { - (1 << REL_X) | (1 << REL_Y), - }, - }, - { /* end of list */ }, -}; - -static void virtio_mouse_init(Object *obj) -{ - VirtIOInputHID *vhid = VIRTIO_INPUT_HID(obj); - VirtIOInput *vinput = VIRTIO_INPUT(obj); - - vhid->handler = &virtio_mouse_handler; - virtio_input_init_config(vinput, virtio_mouse_config); - virtio_input_key_config(vinput, keymap_button, - ARRAY_SIZE(keymap_button)); -} - -static const TypeInfo virtio_mouse_info = { - .name = TYPE_VIRTIO_MOUSE, - .parent = TYPE_VIRTIO_INPUT_HID, - .instance_size = sizeof(VirtIOInputHID), - .instance_init = virtio_mouse_init, -}; - -/* ----------------------------------------------------------------- */ - -static QemuInputHandler virtio_tablet_handler = { - .name = VIRTIO_ID_NAME_TABLET, - .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, - .event = virtio_input_handle_event, - .sync = virtio_input_handle_sync, -}; - -static struct virtio_input_config virtio_tablet_config[] = { - { - .select = VIRTIO_INPUT_CFG_ID_NAME, - .size = sizeof(VIRTIO_ID_NAME_TABLET), - .u.string = VIRTIO_ID_NAME_TABLET, - },{ - .select = VIRTIO_INPUT_CFG_ID_DEVIDS, - .size = sizeof(struct virtio_input_devids), - .u.ids = { - .bustype = const_le16(BUS_VIRTUAL), - .vendor = const_le16(0x0627), /* same we use for usb hid devices */ - .product = const_le16(0x0003), - .version = const_le16(0x0001), - }, - },{ - .select = VIRTIO_INPUT_CFG_EV_BITS, - .subsel = EV_ABS, - .size = 1, - .u.bitmap = { - (1 << ABS_X) | (1 << ABS_Y), - }, - },{ - .select = VIRTIO_INPUT_CFG_ABS_INFO, - .subsel = ABS_X, - .size = sizeof(virtio_input_absinfo), - .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 - 1), - }, - { /* end of list */ }, -}; - -static void virtio_tablet_init(Object *obj) -{ - VirtIOInputHID *vhid = VIRTIO_INPUT_HID(obj); - VirtIOInput *vinput = VIRTIO_INPUT(obj); - - vhid->handler = &virtio_tablet_handler; - virtio_input_init_config(vinput, virtio_tablet_config); - virtio_input_key_config(vinput, keymap_button, - ARRAY_SIZE(keymap_button)); -} - -static const TypeInfo virtio_tablet_info = { - .name = TYPE_VIRTIO_TABLET, - .parent = TYPE_VIRTIO_INPUT_HID, - .instance_size = sizeof(VirtIOInputHID), - .instance_init = virtio_tablet_init, -}; - -/* ----------------------------------------------------------------- */ - -static void virtio_register_types(void) -{ - type_register_static(&virtio_input_hid_info); - type_register_static(&virtio_keyboard_info); - type_register_static(&virtio_mouse_info); - type_register_static(&virtio_tablet_info); -} - -type_init(virtio_register_types) diff --git a/qemu/hw/input/virtio-input-host.c b/qemu/hw/input/virtio-input-host.c deleted file mode 100644 index cb79e8002..000000000 --- a/qemu/hw/input/virtio-input-host.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - * 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 "qapi/error.h" -#include "qemu-common.h" -#include "qemu/sockets.h" - -#include "hw/qdev.h" -#include "hw/virtio/virtio.h" -#include "hw/virtio/virtio-input.h" - -#include -#include "standard-headers/linux/input.h" - -/* ----------------------------------------------------------------- */ - -static struct virtio_input_config virtio_input_host_config[] = { - { /* empty list */ }, -}; - -static void virtio_input_host_event(void *opaque) -{ - VirtIOInputHost *vih = opaque; - VirtIOInput *vinput = VIRTIO_INPUT(vih); - struct virtio_input_event virtio; - struct input_event evdev; - int rc; - - for (;;) { - rc = read(vih->fd, &evdev, sizeof(evdev)); - if (rc != sizeof(evdev)) { - break; - } - - virtio.type = cpu_to_le16(evdev.type); - virtio.code = cpu_to_le16(evdev.code); - virtio.value = cpu_to_le32(evdev.value); - virtio_input_send(vinput, &virtio); - } -} - -static void virtio_input_bits_config(VirtIOInputHost *vih, - int type, int count) -{ - virtio_input_config bits; - int rc, i, size = 0; - - memset(&bits, 0, sizeof(bits)); - rc = ioctl(vih->fd, EVIOCGBIT(type, count/8), bits.u.bitmap); - if (rc < 0) { - return; - } - - for (i = 0; i < count/8; i++) { - if (bits.u.bitmap[i]) { - size = i+1; - } - } - if (size == 0) { - return; - } - - bits.select = VIRTIO_INPUT_CFG_EV_BITS; - bits.subsel = type; - bits.size = size; - 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, *abs; - struct input_id ids; - int rc, ver, i, axis; - uint8_t byte; - - if (!vih->evdev) { - error_setg(errp, "evdev property is required"); - return; - } - - vih->fd = open(vih->evdev, O_RDWR); - if (vih->fd < 0) { - error_setg_file_open(errp, errno, vih->evdev); - return; - } - qemu_set_nonblock(vih->fd); - - rc = ioctl(vih->fd, EVIOCGVERSION, &ver); - if (rc < 0) { - error_setg(errp, "%s: is not an evdev device", vih->evdev); - goto err_close; - } - - rc = ioctl(vih->fd, EVIOCGRAB, 1); - if (rc < 0) { - error_setg_errno(errp, errno, "%s: failed to get exclusive access", - vih->evdev); - goto err_close; - } - - memset(&id, 0, sizeof(id)); - ioctl(vih->fd, EVIOCGNAME(sizeof(id.u.string)-1), id.u.string); - id.select = VIRTIO_INPUT_CFG_ID_NAME; - id.size = strlen(id.u.string); - virtio_input_add_config(vinput, &id); - - if (ioctl(vih->fd, EVIOCGID, &ids) == 0) { - memset(&id, 0, sizeof(id)); - id.select = VIRTIO_INPUT_CFG_ID_DEVIDS; - id.size = sizeof(struct virtio_input_devids); - id.u.ids.bustype = cpu_to_le16(ids.bustype); - id.u.ids.vendor = cpu_to_le16(ids.vendor); - id.u.ids.product = cpu_to_le16(ids.product); - id.u.ids.version = cpu_to_le16(ids.version); - virtio_input_add_config(vinput, &id); - } - - virtio_input_bits_config(vih, EV_KEY, KEY_CNT); - virtio_input_bits_config(vih, EV_REL, REL_CNT); - 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; - -err_close: - close(vih->fd); - vih->fd = -1; - return; -} - -static void virtio_input_host_unrealize(DeviceState *dev, Error **errp) -{ - VirtIOInputHost *vih = VIRTIO_INPUT_HOST(dev); - - if (vih->fd > 0) { - qemu_set_fd_handler(vih->fd, NULL, NULL, NULL); - close(vih->fd); - } -} - -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, -}; - -static Property virtio_input_host_properties[] = { - DEFINE_PROP_STRING("evdev", VirtIOInputHost, evdev), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_input_host_class_init(ObjectClass *klass, void *data) -{ - VirtIOInputClass *vic = VIRTIO_INPUT_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_virtio_input_host; - 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) -{ - VirtIOInput *vinput = VIRTIO_INPUT(obj); - - virtio_input_init_config(vinput, virtio_input_host_config); -} - -static const TypeInfo virtio_input_host_info = { - .name = TYPE_VIRTIO_INPUT_HOST, - .parent = TYPE_VIRTIO_INPUT, - .instance_size = sizeof(VirtIOInputHost), - .instance_init = virtio_input_host_init, - .class_init = virtio_input_host_class_init, -}; - -/* ----------------------------------------------------------------- */ - -static void virtio_register_types(void) -{ - type_register_static(&virtio_input_host_info); -} - -type_init(virtio_register_types) diff --git a/qemu/hw/input/virtio-input.c b/qemu/hw/input/virtio-input.c deleted file mode 100644 index f59749a94..000000000 --- a/qemu/hw/input/virtio-input.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * 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 "qapi/error.h" -#include "qemu/iov.h" - -#include "hw/qdev.h" -#include "hw/virtio/virtio.h" -#include "hw/virtio/virtio-input.h" - -#include "standard-headers/linux/input.h" - -#define VIRTIO_INPUT_VM_VERSION 1 - -/* ----------------------------------------------------------------- */ - -void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event) -{ - VirtQueueElement *elem; - unsigned have, need; - int i, len; - - if (!vinput->active) { - return; - } - - /* queue up events ... */ - if (vinput->qindex == vinput->qsize) { - vinput->qsize++; - vinput->queue = realloc(vinput->queue, vinput->qsize * - sizeof(virtio_input_event)); - } - vinput->queue[vinput->qindex++] = *event; - - /* ... until we see a report sync ... */ - if (event->type != cpu_to_le16(EV_SYN) || - event->code != cpu_to_le16(SYN_REPORT)) { - return; - } - - /* ... then check available space ... */ - need = sizeof(virtio_input_event) * vinput->qindex; - virtqueue_get_avail_bytes(vinput->evt, &have, NULL, need, 0); - if (have < need) { - vinput->qindex = 0; - fprintf(stderr, "%s: ENOSPC in vq, dropping events\n", __func__); - return; - } - - /* ... and finally pass them to the guest */ - for (i = 0; i < vinput->qindex; i++) { - 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, - 0, vinput->queue+i, sizeof(virtio_input_event)); - virtqueue_push(vinput->evt, elem, len); - g_free(elem); - } - virtio_notify(VIRTIO_DEVICE(vinput), vinput->evt); - vinput->qindex = 0; -} - -static void virtio_input_handle_evt(VirtIODevice *vdev, VirtQueue *vq) -{ - /* nothing */ -} - -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; - int len; - - 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, - 0, &event, sizeof(event)); - if (vic->handle_status) { - vic->handle_status(vinput, &event); - } - virtqueue_push(vinput->sts, elem, len); - g_free(elem); - } - virtio_notify(vdev, vinput->sts); -} - -virtio_input_config *virtio_input_find_config(VirtIOInput *vinput, - uint8_t select, - uint8_t subsel) -{ - VirtIOInputConfig *cfg; - - QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) { - if (select == cfg->config.select && - subsel == cfg->config.subsel) { - return &cfg->config; - } - } - return NULL; -} - -void virtio_input_add_config(VirtIOInput *vinput, - virtio_input_config *config) -{ - VirtIOInputConfig *cfg; - - if (virtio_input_find_config(vinput, config->select, config->subsel)) { - /* should not happen */ - fprintf(stderr, "%s: duplicate config: %d/%d\n", - __func__, config->select, config->subsel); - abort(); - } - - cfg = g_new0(VirtIOInputConfig, 1); - cfg->config = *config; - QTAILQ_INSERT_TAIL(&vinput->cfg_list, cfg, node); -} - -void virtio_input_init_config(VirtIOInput *vinput, - virtio_input_config *config) -{ - int i = 0; - - QTAILQ_INIT(&vinput->cfg_list); - while (config[i].select) { - virtio_input_add_config(vinput, config + i); - i++; - } -} - -void virtio_input_idstr_config(VirtIOInput *vinput, - uint8_t select, const char *string) -{ - virtio_input_config id; - - if (!string) { - return; - } - memset(&id, 0, sizeof(id)); - id.select = select; - id.size = snprintf(id.u.string, sizeof(id.u.string), "%s", string); - virtio_input_add_config(vinput, &id); -} - -static void virtio_input_get_config(VirtIODevice *vdev, uint8_t *config_data) -{ - VirtIOInput *vinput = VIRTIO_INPUT(vdev); - virtio_input_config *config; - - config = virtio_input_find_config(vinput, vinput->cfg_select, - vinput->cfg_subsel); - if (config) { - memcpy(config_data, config, vinput->cfg_size); - } else { - memset(config_data, 0, vinput->cfg_size); - } -} - -static void virtio_input_set_config(VirtIODevice *vdev, - const uint8_t *config_data) -{ - VirtIOInput *vinput = VIRTIO_INPUT(vdev); - virtio_input_config *config = (virtio_input_config *)config_data; - - vinput->cfg_select = config->select; - vinput->cfg_subsel = config->subsel; - virtio_notify_config(vdev); -} - -static uint64_t virtio_input_get_features(VirtIODevice *vdev, uint64_t f, - Error **errp) -{ - return f; -} - -static void virtio_input_set_status(VirtIODevice *vdev, uint8_t val) -{ - VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev); - VirtIOInput *vinput = VIRTIO_INPUT(vdev); - - if (val & VIRTIO_CONFIG_S_DRIVER_OK) { - if (!vinput->active) { - vinput->active = true; - if (vic->change_active) { - vic->change_active(vinput); - } - } - } -} - -static void virtio_input_reset(VirtIODevice *vdev) -{ - VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev); - VirtIOInput *vinput = VIRTIO_INPUT(vdev); - - if (vinput->active) { - vinput->active = false; - if (vic->change_active) { - vic->change_active(vinput); - } - } -} - -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); - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIOInput *vinput = VIRTIO_INPUT(dev); - VirtIOInputConfig *cfg; - Error *local_err = NULL; - - if (vic->realize) { - vic->realize(dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - } - - virtio_input_idstr_config(vinput, VIRTIO_INPUT_CFG_ID_SERIAL, - vinput->serial); - - QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) { - if (vinput->cfg_size < cfg->config.size) { - vinput->cfg_size = cfg->config.size; - } - } - vinput->cfg_size += 8; - assert(vinput->cfg_size <= sizeof(virtio_input_config)); - - virtio_init(vdev, "virtio-input", VIRTIO_ID_INPUT, - 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) { - error_propagate(errp, local_err); - return; - } - } - virtio_cleanup(vdev); -} - -static Property virtio_input_properties[] = { - DEFINE_PROP_STRING("serial", VirtIOInput, serial), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_input_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - - dc->props = virtio_input_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - vdc->realize = virtio_input_device_realize; - vdc->unrealize = virtio_input_device_unrealize; - vdc->get_config = virtio_input_get_config; - vdc->set_config = virtio_input_set_config; - vdc->get_features = virtio_input_get_features; - vdc->set_status = virtio_input_set_status; - vdc->reset = virtio_input_reset; -} - -static const TypeInfo virtio_input_info = { - .name = TYPE_VIRTIO_INPUT, - .parent = TYPE_VIRTIO_DEVICE, - .instance_size = sizeof(VirtIOInput), - .class_size = sizeof(VirtIOInputClass), - .class_init = virtio_input_class_init, - .abstract = true, -}; - -/* ----------------------------------------------------------------- */ - -static void virtio_register_types(void) -{ - type_register_static(&virtio_input_info); -} - -type_init(virtio_register_types) diff --git a/qemu/hw/input/vmmouse.c b/qemu/hw/input/vmmouse.c deleted file mode 100644 index 6d15a887c..000000000 --- a/qemu/hw/input/vmmouse.c +++ /dev/null @@ -1,304 +0,0 @@ -/* - * QEMU VMMouse emulation - * - * Copyright (C) 2007 Anthony Liguori - * - * 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 "ui/console.h" -#include "hw/input/ps2.h" -#include "hw/i386/pc.h" -#include "hw/qdev.h" - -/* debug only vmmouse */ -//#define DEBUG_VMMOUSE - -/* VMMouse Commands */ -#define VMMOUSE_GETVERSION 10 -#define VMMOUSE_DATA 39 -#define VMMOUSE_STATUS 40 -#define VMMOUSE_COMMAND 41 - -#define VMMOUSE_READ_ID 0x45414552 -#define VMMOUSE_DISABLE 0x000000f5 -#define VMMOUSE_REQUEST_RELATIVE 0x4c455252 -#define VMMOUSE_REQUEST_ABSOLUTE 0x53424152 - -#define VMMOUSE_QUEUE_SIZE 1024 - -#define VMMOUSE_VERSION 0x3442554a - -#ifdef DEBUG_VMMOUSE -#define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) -#else -#define DPRINTF(fmt, ...) do { } while (0) -#endif - -#define TYPE_VMMOUSE "vmmouse" -#define VMMOUSE(obj) OBJECT_CHECK(VMMouseState, (obj), TYPE_VMMOUSE) - -typedef struct VMMouseState -{ - ISADevice parent_obj; - - uint32_t queue[VMMOUSE_QUEUE_SIZE]; - int32_t queue_size; - uint16_t nb_queue; - uint16_t status; - uint8_t absolute; - QEMUPutMouseEntry *entry; - void *ps2_mouse; -} VMMouseState; - -static uint32_t vmmouse_get_status(VMMouseState *s) -{ - DPRINTF("vmmouse_get_status()\n"); - return (s->status << 16) | s->nb_queue; -} - -static void vmmouse_mouse_event(void *opaque, int x, int y, int dz, int buttons_state) -{ - VMMouseState *s = opaque; - int buttons = 0; - - if (s->nb_queue > (VMMOUSE_QUEUE_SIZE - 4)) - return; - - DPRINTF("vmmouse_mouse_event(%d, %d, %d, %d)\n", - x, y, dz, buttons_state); - - if ((buttons_state & MOUSE_EVENT_LBUTTON)) - buttons |= 0x20; - if ((buttons_state & MOUSE_EVENT_RBUTTON)) - buttons |= 0x10; - if ((buttons_state & MOUSE_EVENT_MBUTTON)) - buttons |= 0x08; - - if (s->absolute) { - x <<= 1; - y <<= 1; - } - - s->queue[s->nb_queue++] = buttons; - s->queue[s->nb_queue++] = x; - s->queue[s->nb_queue++] = y; - s->queue[s->nb_queue++] = dz; - - /* need to still generate PS2 events to notify driver to - read from queue */ - i8042_isa_mouse_fake_event(s->ps2_mouse); -} - -static void vmmouse_remove_handler(VMMouseState *s) -{ - if (s->entry) { - qemu_remove_mouse_event_handler(s->entry); - s->entry = NULL; - } -} - -static void vmmouse_update_handler(VMMouseState *s, int absolute) -{ - if (s->status != 0) { - return; - } - if (s->absolute != absolute) { - s->absolute = absolute; - vmmouse_remove_handler(s); - } - if (s->entry == NULL) { - s->entry = qemu_add_mouse_event_handler(vmmouse_mouse_event, - s, s->absolute, - "vmmouse"); - qemu_activate_mouse_event_handler(s->entry); - } -} - -static void vmmouse_read_id(VMMouseState *s) -{ - DPRINTF("vmmouse_read_id()\n"); - - if (s->nb_queue == VMMOUSE_QUEUE_SIZE) - return; - - s->queue[s->nb_queue++] = VMMOUSE_VERSION; - s->status = 0; -} - -static void vmmouse_request_relative(VMMouseState *s) -{ - DPRINTF("vmmouse_request_relative()\n"); - vmmouse_update_handler(s, 0); -} - -static void vmmouse_request_absolute(VMMouseState *s) -{ - DPRINTF("vmmouse_request_absolute()\n"); - vmmouse_update_handler(s, 1); -} - -static void vmmouse_disable(VMMouseState *s) -{ - DPRINTF("vmmouse_disable()\n"); - s->status = 0xffff; - vmmouse_remove_handler(s); -} - -static void vmmouse_data(VMMouseState *s, uint32_t *data, uint32_t size) -{ - int i; - - DPRINTF("vmmouse_data(%d)\n", size); - - if (size == 0 || size > 6 || size > s->nb_queue) { - printf("vmmouse: driver requested too much data %d\n", size); - s->status = 0xffff; - vmmouse_remove_handler(s); - return; - } - - for (i = 0; i < size; i++) - data[i] = s->queue[i]; - - s->nb_queue -= size; - if (s->nb_queue) - memmove(s->queue, &s->queue[size], sizeof(s->queue[0]) * s->nb_queue); -} - -static uint32_t vmmouse_ioport_read(void *opaque, uint32_t addr) -{ - VMMouseState *s = opaque; - uint32_t data[6]; - uint16_t command; - - vmmouse_get_data(data); - - command = data[2] & 0xFFFF; - - switch (command) { - case VMMOUSE_STATUS: - data[0] = vmmouse_get_status(s); - break; - case VMMOUSE_COMMAND: - switch (data[1]) { - case VMMOUSE_DISABLE: - vmmouse_disable(s); - break; - case VMMOUSE_READ_ID: - vmmouse_read_id(s); - break; - case VMMOUSE_REQUEST_RELATIVE: - vmmouse_request_relative(s); - break; - case VMMOUSE_REQUEST_ABSOLUTE: - vmmouse_request_absolute(s); - break; - default: - printf("vmmouse: unknown command %x\n", data[1]); - break; - } - break; - case VMMOUSE_DATA: - vmmouse_data(s, data, data[1]); - break; - default: - printf("vmmouse: unknown command %x\n", command); - break; - } - - vmmouse_set_data(data); - return data[0]; -} - -static int vmmouse_post_load(void *opaque, int version_id) -{ - VMMouseState *s = opaque; - - vmmouse_remove_handler(s); - vmmouse_update_handler(s, s->absolute); - return 0; -} - -static const VMStateDescription vmstate_vmmouse = { - .name = "vmmouse", - .version_id = 0, - .minimum_version_id = 0, - .post_load = vmmouse_post_load, - .fields = (VMStateField[]) { - VMSTATE_INT32_EQUAL(queue_size, VMMouseState), - VMSTATE_UINT32_ARRAY(queue, VMMouseState, VMMOUSE_QUEUE_SIZE), - VMSTATE_UINT16(nb_queue, VMMouseState), - VMSTATE_UINT16(status, VMMouseState), - VMSTATE_UINT8(absolute, VMMouseState), - VMSTATE_END_OF_LIST() - } -}; - -static void vmmouse_reset(DeviceState *d) -{ - VMMouseState *s = VMMOUSE(d); - - s->queue_size = VMMOUSE_QUEUE_SIZE; - - vmmouse_disable(s); -} - -static void vmmouse_realizefn(DeviceState *dev, Error **errp) -{ - VMMouseState *s = VMMOUSE(dev); - - DPRINTF("vmmouse_init\n"); - - vmport_register(VMMOUSE_STATUS, vmmouse_ioport_read, s); - vmport_register(VMMOUSE_COMMAND, vmmouse_ioport_read, s); - vmport_register(VMMOUSE_DATA, vmmouse_ioport_read, s); -} - -static Property vmmouse_properties[] = { - DEFINE_PROP_PTR("ps2_mouse", VMMouseState, ps2_mouse), - DEFINE_PROP_END_OF_LIST(), -}; - -static void vmmouse_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = vmmouse_realizefn; - dc->reset = vmmouse_reset; - dc->vmsd = &vmstate_vmmouse; - dc->props = vmmouse_properties; - /* Reason: pointer property "ps2_mouse" */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo vmmouse_info = { - .name = TYPE_VMMOUSE, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(VMMouseState), - .class_init = vmmouse_class_initfn, -}; - -static void vmmouse_register_types(void) -{ - type_register_static(&vmmouse_info); -} - -type_init(vmmouse_register_types) diff --git a/qemu/hw/intc/Makefile.objs b/qemu/hw/intc/Makefile.objs deleted file mode 100644 index 0e47f0f9e..000000000 --- a/qemu/hw/intc/Makefile.objs +++ /dev/null @@ -1,34 +0,0 @@ -common-obj-$(CONFIG_HEATHROW_PIC) += heathrow_pic.o -common-obj-$(CONFIG_I8259) += i8259_common.o i8259.o -common-obj-$(CONFIG_PL190) += pl190.o -common-obj-$(CONFIG_PUV3) += puv3_intc.o -common-obj-$(CONFIG_XILINX) += xilinx_intc.o -common-obj-$(CONFIG_ETRAXFS) += etraxfs_pic.o -common-obj-$(CONFIG_IMX) += imx_avic.o -common-obj-$(CONFIG_LM32) += lm32_pic.o -common-obj-$(CONFIG_REALVIEW) += realview_gic.o -common-obj-$(CONFIG_SLAVIO) += slavio_intctl.o -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 deleted file mode 100644 index dc971a160..000000000 --- a/qemu/hw/intc/allwinner-a10-pic.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Allwinner A10 interrupt controller device emulation - * - * Copyright (C) 2013 Li Guang - * Written by Li Guang - * - * 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. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "hw/devices.h" -#include "sysemu/sysemu.h" -#include "hw/intc/allwinner-a10-pic.h" - -static void aw_a10_pic_update(AwA10PICState *s) -{ - uint8_t i; - int irq = 0, fiq = 0, zeroes; - - s->vector = 0; - - for (i = 0; i < AW_A10_PIC_REG_NUM; i++) { - irq |= s->irq_pending[i] & ~s->mask[i]; - fiq |= s->select[i] & s->irq_pending[i] & ~s->mask[i]; - - if (!s->vector) { - zeroes = ctz32(s->irq_pending[i] & ~s->mask[i]); - if (zeroes != 32) { - s->vector = (i * 32 + zeroes) * 4; - } - } - } - - qemu_set_irq(s->parent_irq, !!irq); - qemu_set_irq(s->parent_fiq, !!fiq); -} - -static void aw_a10_pic_set_irq(void *opaque, int irq, int level) -{ - AwA10PICState *s = opaque; - - if (level) { - set_bit(irq % 32, (void *)&s->irq_pending[irq / 32]); - } else { - clear_bit(irq % 32, (void *)&s->irq_pending[irq / 32]); - } - aw_a10_pic_update(s); -} - -static uint64_t aw_a10_pic_read(void *opaque, hwaddr offset, unsigned size) -{ - AwA10PICState *s = opaque; - uint8_t index = (offset & 0xc) / 4; - - switch (offset) { - case AW_A10_PIC_VECTOR: - return s->vector; - case AW_A10_PIC_BASE_ADDR: - return s->base_addr; - case AW_A10_PIC_PROTECT: - return s->protect; - case AW_A10_PIC_NMI: - return s->nmi; - case AW_A10_PIC_IRQ_PENDING ... AW_A10_PIC_IRQ_PENDING + 8: - return s->irq_pending[index]; - case AW_A10_PIC_FIQ_PENDING ... AW_A10_PIC_FIQ_PENDING + 8: - return s->fiq_pending[index]; - case AW_A10_PIC_SELECT ... AW_A10_PIC_SELECT + 8: - return s->select[index]; - case AW_A10_PIC_ENABLE ... AW_A10_PIC_ENABLE + 8: - return s->enable[index]; - case AW_A10_PIC_MASK ... AW_A10_PIC_MASK + 8: - return s->mask[index]; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset 0x%x\n", __func__, (int)offset); - break; - } - - return 0; -} - -static void aw_a10_pic_write(void *opaque, hwaddr offset, uint64_t value, - unsigned size) -{ - AwA10PICState *s = opaque; - uint8_t index = (offset & 0xc) / 4; - - switch (offset) { - case AW_A10_PIC_BASE_ADDR: - s->base_addr = value & ~0x3; - break; - case AW_A10_PIC_PROTECT: - s->protect = value; - break; - case AW_A10_PIC_NMI: - s->nmi = value; - break; - case AW_A10_PIC_IRQ_PENDING ... AW_A10_PIC_IRQ_PENDING + 8: - /* - * The register is read-only; nevertheless, Linux (including - * the version originally shipped by Allwinner) pretends to - * write to the register. Just ignore it. - */ - break; - case AW_A10_PIC_FIQ_PENDING ... AW_A10_PIC_FIQ_PENDING + 8: - s->fiq_pending[index] &= ~value; - break; - case AW_A10_PIC_SELECT ... AW_A10_PIC_SELECT + 8: - s->select[index] = value; - break; - case AW_A10_PIC_ENABLE ... AW_A10_PIC_ENABLE + 8: - s->enable[index] = value; - break; - case AW_A10_PIC_MASK ... AW_A10_PIC_MASK + 8: - s->mask[index] = value; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset 0x%x\n", __func__, (int)offset); - break; - } - - aw_a10_pic_update(s); -} - -static const MemoryRegionOps aw_a10_pic_ops = { - .read = aw_a10_pic_read, - .write = aw_a10_pic_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_aw_a10_pic = { - .name = "a10.pic", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(vector, AwA10PICState), - VMSTATE_UINT32(base_addr, AwA10PICState), - VMSTATE_UINT32(protect, AwA10PICState), - VMSTATE_UINT32(nmi, AwA10PICState), - VMSTATE_UINT32_ARRAY(irq_pending, AwA10PICState, AW_A10_PIC_REG_NUM), - VMSTATE_UINT32_ARRAY(fiq_pending, AwA10PICState, AW_A10_PIC_REG_NUM), - VMSTATE_UINT32_ARRAY(enable, AwA10PICState, AW_A10_PIC_REG_NUM), - VMSTATE_UINT32_ARRAY(select, AwA10PICState, AW_A10_PIC_REG_NUM), - VMSTATE_UINT32_ARRAY(mask, AwA10PICState, AW_A10_PIC_REG_NUM), - VMSTATE_END_OF_LIST() - } -}; - -static void aw_a10_pic_init(Object *obj) -{ - AwA10PICState *s = AW_A10_PIC(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); - - qdev_init_gpio_in(DEVICE(dev), aw_a10_pic_set_irq, AW_A10_PIC_INT_NR); - sysbus_init_irq(dev, &s->parent_irq); - sysbus_init_irq(dev, &s->parent_fiq); - memory_region_init_io(&s->iomem, OBJECT(s), &aw_a10_pic_ops, s, - TYPE_AW_A10_PIC, 0x400); - sysbus_init_mmio(dev, &s->iomem); -} - -static void aw_a10_pic_reset(DeviceState *d) -{ - AwA10PICState *s = AW_A10_PIC(d); - uint8_t i; - - s->base_addr = 0; - s->protect = 0; - s->nmi = 0; - s->vector = 0; - for (i = 0; i < AW_A10_PIC_REG_NUM; i++) { - s->irq_pending[i] = 0; - s->fiq_pending[i] = 0; - s->select[i] = 0; - s->enable[i] = 0; - s->mask[i] = 0; - } -} - -static void aw_a10_pic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = aw_a10_pic_reset; - dc->desc = "allwinner a10 pic"; - dc->vmsd = &vmstate_aw_a10_pic; - } - -static const TypeInfo aw_a10_pic_info = { - .name = TYPE_AW_A10_PIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(AwA10PICState), - .instance_init = aw_a10_pic_init, - .class_init = aw_a10_pic_class_init, -}; - -static void aw_a10_register_types(void) -{ - type_register_static(&aw_a10_pic_info); -} - -type_init(aw_a10_register_types); diff --git a/qemu/hw/intc/apic.c b/qemu/hw/intc/apic.c deleted file mode 100644 index 28c2ea540..000000000 --- a/qemu/hw/intc/apic.c +++ /dev/null @@ -1,906 +0,0 @@ -/* - * APIC support - * - * Copyright (c) 2004-2005 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see - */ -#include "qemu/osdep.h" -#include "qemu/thread.h" -#include "hw/i386/apic_internal.h" -#include "hw/i386/apic.h" -#include "hw/i386/ioapic.h" -#include "hw/pci/msi.h" -#include "qemu/host-utils.h" -#include "trace.h" -#include "hw/i386/pc.h" -#include "hw/i386/apic-msidef.h" - -#define MAX_APIC_WORDS 8 - -#define SYNC_FROM_VAPIC 0x1 -#define SYNC_TO_VAPIC 0x2 -#define SYNC_ISR_IRR_TO_VAPIC 0x4 - -static APICCommonState *local_apics[MAX_APICS + 1]; - -static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode); -static void apic_update_irq(APICCommonState *s); -static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask, - uint8_t dest, uint8_t dest_mode); - -/* Find first bit starting from msb */ -static int apic_fls_bit(uint32_t value) -{ - return 31 - clz32(value); -} - -/* Find first bit starting from lsb */ -static int apic_ffs_bit(uint32_t value) -{ - return ctz32(value); -} - -static inline void apic_reset_bit(uint32_t *tab, int index) -{ - int i, mask; - i = index >> 5; - mask = 1 << (index & 0x1f); - tab[i] &= ~mask; -} - -/* return -1 if no bit is set */ -static int get_highest_priority_int(uint32_t *tab) -{ - int i; - for (i = 7; i >= 0; i--) { - if (tab[i] != 0) { - return i * 32 + apic_fls_bit(tab[i]); - } - } - return -1; -} - -static void apic_sync_vapic(APICCommonState *s, int sync_type) -{ - VAPICState vapic_state; - size_t length; - off_t start; - int vector; - - if (!s->vapic_paddr) { - return; - } - if (sync_type & SYNC_FROM_VAPIC) { - cpu_physical_memory_read(s->vapic_paddr, &vapic_state, - sizeof(vapic_state)); - s->tpr = vapic_state.tpr; - } - if (sync_type & (SYNC_TO_VAPIC | SYNC_ISR_IRR_TO_VAPIC)) { - start = offsetof(VAPICState, isr); - length = offsetof(VAPICState, enabled) - offsetof(VAPICState, isr); - - if (sync_type & SYNC_TO_VAPIC) { - assert(qemu_cpu_is_self(CPU(s->cpu))); - - vapic_state.tpr = s->tpr; - vapic_state.enabled = 1; - start = 0; - length = sizeof(VAPICState); - } - - vector = get_highest_priority_int(s->isr); - if (vector < 0) { - vector = 0; - } - vapic_state.isr = vector & 0xf0; - - vapic_state.zero = 0; - - vector = get_highest_priority_int(s->irr); - if (vector < 0) { - vector = 0; - } - vapic_state.irr = vector & 0xff; - - cpu_physical_memory_write_rom(&address_space_memory, - s->vapic_paddr + start, - ((void *)&vapic_state) + start, length); - } -} - -static void apic_vapic_base_update(APICCommonState *s) -{ - apic_sync_vapic(s, SYNC_TO_VAPIC); -} - -static void apic_local_deliver(APICCommonState *s, int vector) -{ - uint32_t lvt = s->lvt[vector]; - int trigger_mode; - - trace_apic_local_deliver(vector, (lvt >> 8) & 7); - - if (lvt & APIC_LVT_MASKED) - return; - - switch ((lvt >> 8) & 7) { - case APIC_DM_SMI: - cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_SMI); - break; - - case APIC_DM_NMI: - cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_NMI); - break; - - case APIC_DM_EXTINT: - cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_HARD); - break; - - case APIC_DM_FIXED: - trigger_mode = APIC_TRIGGER_EDGE; - if ((vector == APIC_LVT_LINT0 || vector == APIC_LVT_LINT1) && - (lvt & APIC_LVT_LEVEL_TRIGGER)) - trigger_mode = APIC_TRIGGER_LEVEL; - apic_set_irq(s, lvt & 0xff, trigger_mode); - } -} - -void apic_deliver_pic_intr(DeviceState *dev, int level) -{ - APICCommonState *s = APIC_COMMON(dev); - - if (level) { - apic_local_deliver(s, APIC_LVT_LINT0); - } else { - uint32_t lvt = s->lvt[APIC_LVT_LINT0]; - - switch ((lvt >> 8) & 7) { - case APIC_DM_FIXED: - if (!(lvt & APIC_LVT_LEVEL_TRIGGER)) - break; - apic_reset_bit(s->irr, lvt & 0xff); - /* fall through */ - case APIC_DM_EXTINT: - apic_update_irq(s); - break; - } - } -} - -static void apic_external_nmi(APICCommonState *s) -{ - apic_local_deliver(s, APIC_LVT_LINT1); -} - -#define foreach_apic(apic, deliver_bitmask, code) \ -{\ - int __i, __j;\ - for(__i = 0; __i < MAX_APIC_WORDS; __i++) {\ - uint32_t __mask = deliver_bitmask[__i];\ - if (__mask) {\ - for(__j = 0; __j < 32; __j++) {\ - if (__mask & (1U << __j)) {\ - apic = local_apics[__i * 32 + __j];\ - if (apic) {\ - code;\ - }\ - }\ - }\ - }\ - }\ -} - -static void apic_bus_deliver(const uint32_t *deliver_bitmask, - uint8_t delivery_mode, uint8_t vector_num, - uint8_t trigger_mode) -{ - APICCommonState *apic_iter; - - switch (delivery_mode) { - case APIC_DM_LOWPRI: - /* XXX: search for focus processor, arbitration */ - { - int i, d; - d = -1; - for(i = 0; i < MAX_APIC_WORDS; i++) { - if (deliver_bitmask[i]) { - d = i * 32 + apic_ffs_bit(deliver_bitmask[i]); - break; - } - } - if (d >= 0) { - apic_iter = local_apics[d]; - if (apic_iter) { - apic_set_irq(apic_iter, vector_num, trigger_mode); - } - } - } - return; - - case APIC_DM_FIXED: - break; - - case APIC_DM_SMI: - foreach_apic(apic_iter, deliver_bitmask, - cpu_interrupt(CPU(apic_iter->cpu), CPU_INTERRUPT_SMI) - ); - return; - - case APIC_DM_NMI: - foreach_apic(apic_iter, deliver_bitmask, - cpu_interrupt(CPU(apic_iter->cpu), CPU_INTERRUPT_NMI) - ); - return; - - case APIC_DM_INIT: - /* normal INIT IPI sent to processors */ - foreach_apic(apic_iter, deliver_bitmask, - cpu_interrupt(CPU(apic_iter->cpu), - CPU_INTERRUPT_INIT) - ); - return; - - case APIC_DM_EXTINT: - /* handled in I/O APIC code */ - break; - - default: - return; - } - - foreach_apic(apic_iter, deliver_bitmask, - apic_set_irq(apic_iter, vector_num, trigger_mode) ); -} - -void apic_deliver_irq(uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, - uint8_t vector_num, uint8_t trigger_mode) -{ - uint32_t deliver_bitmask[MAX_APIC_WORDS]; - - trace_apic_deliver_irq(dest, dest_mode, delivery_mode, vector_num, - trigger_mode); - - apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode); - apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, trigger_mode); -} - -static void apic_set_base(APICCommonState *s, uint64_t val) -{ - s->apicbase = (val & 0xfffff000) | - (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE)); - /* if disabled, cannot be enabled again */ - if (!(val & MSR_IA32_APICBASE_ENABLE)) { - s->apicbase &= ~MSR_IA32_APICBASE_ENABLE; - cpu_clear_apic_feature(&s->cpu->env); - s->spurious_vec &= ~APIC_SV_ENABLE; - } -} - -static void apic_set_tpr(APICCommonState *s, uint8_t val) -{ - /* Updates from cr8 are ignored while the VAPIC is active */ - if (!s->vapic_paddr) { - s->tpr = val << 4; - apic_update_irq(s); - } -} - -static uint8_t apic_get_tpr(APICCommonState *s) -{ - apic_sync_vapic(s, SYNC_FROM_VAPIC); - return s->tpr >> 4; -} - -int apic_get_ppr(APICCommonState *s) -{ - int tpr, isrv, ppr; - - tpr = (s->tpr >> 4); - isrv = get_highest_priority_int(s->isr); - if (isrv < 0) - isrv = 0; - isrv >>= 4; - if (tpr >= isrv) - ppr = s->tpr; - else - ppr = isrv << 4; - return ppr; -} - -static int apic_get_arb_pri(APICCommonState *s) -{ - /* XXX: arbitration */ - return 0; -} - - -/* - * <0 - low prio interrupt, - * 0 - no interrupt, - * >0 - interrupt number - */ -static int apic_irq_pending(APICCommonState *s) -{ - int irrv, ppr; - - if (!(s->spurious_vec & APIC_SV_ENABLE)) { - return 0; - } - - irrv = get_highest_priority_int(s->irr); - if (irrv < 0) { - return 0; - } - ppr = apic_get_ppr(s); - if (ppr && (irrv & 0xf0) <= (ppr & 0xf0)) { - return -1; - } - - return irrv; -} - -/* signal the CPU if an irq is pending */ -static void apic_update_irq(APICCommonState *s) -{ - CPUState *cpu; - DeviceState *dev = (DeviceState *)s; - - cpu = CPU(s->cpu); - if (!qemu_cpu_is_self(cpu)) { - cpu_interrupt(cpu, CPU_INTERRUPT_POLL); - } else if (apic_irq_pending(s) > 0) { - cpu_interrupt(cpu, CPU_INTERRUPT_HARD); - } else if (!apic_accept_pic_intr(dev) || !pic_get_output(isa_pic)) { - cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD); - } -} - -void apic_poll_irq(DeviceState *dev) -{ - APICCommonState *s = APIC_COMMON(dev); - - apic_sync_vapic(s, SYNC_FROM_VAPIC); - apic_update_irq(s); -} - -static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode) -{ - apic_report_irq_delivered(!apic_get_bit(s->irr, vector_num)); - - apic_set_bit(s->irr, vector_num); - if (trigger_mode) - apic_set_bit(s->tmr, vector_num); - else - apic_reset_bit(s->tmr, vector_num); - if (s->vapic_paddr) { - apic_sync_vapic(s, SYNC_ISR_IRR_TO_VAPIC); - /* - * The vcpu thread needs to see the new IRR before we pull its current - * TPR value. That way, if we miss a lowering of the TRP, the guest - * has the chance to notice the new IRR and poll for IRQs on its own. - */ - smp_wmb(); - apic_sync_vapic(s, SYNC_FROM_VAPIC); - } - apic_update_irq(s); -} - -static void apic_eoi(APICCommonState *s) -{ - int isrv; - isrv = get_highest_priority_int(s->isr); - if (isrv < 0) - return; - apic_reset_bit(s->isr, isrv); - if (!(s->spurious_vec & APIC_SV_DIRECTED_IO) && apic_get_bit(s->tmr, isrv)) { - ioapic_eoi_broadcast(isrv); - } - apic_sync_vapic(s, SYNC_FROM_VAPIC | SYNC_TO_VAPIC); - apic_update_irq(s); -} - -static int apic_find_dest(uint8_t dest) -{ - APICCommonState *apic = local_apics[dest]; - int i; - - if (apic && apic->id == dest) - return dest; /* shortcut in case apic->id == apic->idx */ - - for (i = 0; i < MAX_APICS; i++) { - apic = local_apics[i]; - if (apic && apic->id == dest) - return i; - if (!apic) - break; - } - - return -1; -} - -static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask, - uint8_t dest, uint8_t dest_mode) -{ - APICCommonState *apic_iter; - int i; - - if (dest_mode == 0) { - if (dest == 0xff) { - memset(deliver_bitmask, 0xff, MAX_APIC_WORDS * sizeof(uint32_t)); - } else { - int idx = apic_find_dest(dest); - memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t)); - if (idx >= 0) - apic_set_bit(deliver_bitmask, idx); - } - } else { - /* XXX: cluster mode */ - memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t)); - for(i = 0; i < MAX_APICS; i++) { - apic_iter = local_apics[i]; - if (apic_iter) { - if (apic_iter->dest_mode == 0xf) { - if (dest & apic_iter->log_dest) - apic_set_bit(deliver_bitmask, i); - } else if (apic_iter->dest_mode == 0x0) { - if ((dest & 0xf0) == (apic_iter->log_dest & 0xf0) && - (dest & apic_iter->log_dest & 0x0f)) { - apic_set_bit(deliver_bitmask, i); - } - } - } else { - break; - } - } - } -} - -static void apic_startup(APICCommonState *s, int vector_num) -{ - s->sipi_vector = vector_num; - cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_SIPI); -} - -void apic_sipi(DeviceState *dev) -{ - APICCommonState *s = APIC_COMMON(dev); - - cpu_reset_interrupt(CPU(s->cpu), CPU_INTERRUPT_SIPI); - - if (!s->wait_for_sipi) - return; - cpu_x86_load_seg_cache_sipi(s->cpu, s->sipi_vector); - s->wait_for_sipi = 0; -} - -static void apic_deliver(DeviceState *dev, uint8_t dest, uint8_t dest_mode, - uint8_t delivery_mode, uint8_t vector_num, - uint8_t trigger_mode) -{ - APICCommonState *s = APIC_COMMON(dev); - uint32_t deliver_bitmask[MAX_APIC_WORDS]; - int dest_shorthand = (s->icr[0] >> 18) & 3; - APICCommonState *apic_iter; - - switch (dest_shorthand) { - case 0: - apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode); - break; - case 1: - memset(deliver_bitmask, 0x00, sizeof(deliver_bitmask)); - apic_set_bit(deliver_bitmask, s->idx); - break; - case 2: - memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask)); - break; - case 3: - memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask)); - apic_reset_bit(deliver_bitmask, s->idx); - break; - } - - switch (delivery_mode) { - case APIC_DM_INIT: - { - int trig_mode = (s->icr[0] >> 15) & 1; - int level = (s->icr[0] >> 14) & 1; - if (level == 0 && trig_mode == 1) { - foreach_apic(apic_iter, deliver_bitmask, - apic_iter->arb_id = apic_iter->id ); - return; - } - } - break; - - case APIC_DM_SIPI: - foreach_apic(apic_iter, deliver_bitmask, - apic_startup(apic_iter, vector_num) ); - return; - } - - apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, trigger_mode); -} - -static bool apic_check_pic(APICCommonState *s) -{ - DeviceState *dev = (DeviceState *)s; - - if (!apic_accept_pic_intr(dev) || !pic_get_output(isa_pic)) { - return false; - } - apic_deliver_pic_intr(dev, 1); - return true; -} - -int apic_get_interrupt(DeviceState *dev) -{ - APICCommonState *s = APIC_COMMON(dev); - int intno; - - /* if the APIC is installed or enabled, we let the 8259 handle the - IRQs */ - if (!s) - return -1; - if (!(s->spurious_vec & APIC_SV_ENABLE)) - return -1; - - apic_sync_vapic(s, SYNC_FROM_VAPIC); - intno = apic_irq_pending(s); - - /* if there is an interrupt from the 8259, let the caller handle - * that first since ExtINT interrupts ignore the priority. - */ - if (intno == 0 || apic_check_pic(s)) { - apic_sync_vapic(s, SYNC_TO_VAPIC); - return -1; - } else if (intno < 0) { - apic_sync_vapic(s, SYNC_TO_VAPIC); - return s->spurious_vec & 0xff; - } - apic_reset_bit(s->irr, intno); - apic_set_bit(s->isr, intno); - apic_sync_vapic(s, SYNC_TO_VAPIC); - - apic_update_irq(s); - - return intno; -} - -int apic_accept_pic_intr(DeviceState *dev) -{ - APICCommonState *s = APIC_COMMON(dev); - uint32_t lvt0; - - if (!s) - return -1; - - lvt0 = s->lvt[APIC_LVT_LINT0]; - - if ((s->apicbase & MSR_IA32_APICBASE_ENABLE) == 0 || - (lvt0 & APIC_LVT_MASKED) == 0) - return 1; - - return 0; -} - -static uint32_t apic_get_current_count(APICCommonState *s) -{ - int64_t d; - uint32_t val; - d = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->initial_count_load_time) >> - s->count_shift; - if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { - /* periodic */ - val = s->initial_count - (d % ((uint64_t)s->initial_count + 1)); - } else { - if (d >= s->initial_count) - val = 0; - else - val = s->initial_count - d; - } - return val; -} - -static void apic_timer_update(APICCommonState *s, int64_t current_time) -{ - if (apic_next_timer(s, current_time)) { - timer_mod(s->timer, s->next_time); - } else { - timer_del(s->timer); - } -} - -static void apic_timer(void *opaque) -{ - APICCommonState *s = opaque; - - apic_local_deliver(s, APIC_LVT_TIMER); - apic_timer_update(s, s->next_time); -} - -static uint32_t apic_mem_readb(void *opaque, hwaddr addr) -{ - return 0; -} - -static uint32_t apic_mem_readw(void *opaque, hwaddr addr) -{ - return 0; -} - -static void apic_mem_writeb(void *opaque, hwaddr addr, uint32_t val) -{ -} - -static void apic_mem_writew(void *opaque, hwaddr addr, uint32_t val) -{ -} - -static uint32_t apic_mem_readl(void *opaque, hwaddr addr) -{ - DeviceState *dev; - APICCommonState *s; - uint32_t val; - int index; - - dev = cpu_get_current_apic(); - if (!dev) { - return 0; - } - s = APIC_COMMON(dev); - - index = (addr >> 4) & 0xff; - switch(index) { - case 0x02: /* id */ - val = s->id << 24; - break; - case 0x03: /* version */ - val = s->version | ((APIC_LVT_NB - 1) << 16); - break; - case 0x08: - apic_sync_vapic(s, SYNC_FROM_VAPIC); - if (apic_report_tpr_access) { - cpu_report_tpr_access(&s->cpu->env, TPR_ACCESS_READ); - } - val = s->tpr; - break; - case 0x09: - val = apic_get_arb_pri(s); - break; - case 0x0a: - /* ppr */ - val = apic_get_ppr(s); - break; - case 0x0b: - val = 0; - break; - case 0x0d: - val = s->log_dest << 24; - break; - case 0x0e: - val = (s->dest_mode << 28) | 0xfffffff; - break; - case 0x0f: - val = s->spurious_vec; - break; - case 0x10 ... 0x17: - val = s->isr[index & 7]; - break; - case 0x18 ... 0x1f: - val = s->tmr[index & 7]; - break; - case 0x20 ... 0x27: - val = s->irr[index & 7]; - break; - case 0x28: - val = s->esr; - break; - case 0x30: - case 0x31: - val = s->icr[index & 1]; - break; - case 0x32 ... 0x37: - val = s->lvt[index - 0x32]; - break; - case 0x38: - val = s->initial_count; - break; - case 0x39: - val = apic_get_current_count(s); - break; - case 0x3e: - val = s->divide_conf; - break; - default: - s->esr |= APIC_ESR_ILLEGAL_ADDRESS; - val = 0; - break; - } - trace_apic_mem_readl(addr, val); - return val; -} - -static void apic_send_msi(hwaddr addr, uint32_t data) -{ - uint8_t dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT; - uint8_t vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT; - uint8_t dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1; - uint8_t trigger_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1; - uint8_t delivery = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7; - /* XXX: Ignore redirection hint. */ - apic_deliver_irq(dest, dest_mode, delivery, vector, trigger_mode); -} - -static void apic_mem_writel(void *opaque, hwaddr addr, uint32_t val) -{ - DeviceState *dev; - APICCommonState *s; - int index = (addr >> 4) & 0xff; - if (addr > 0xfff || !index) { - /* MSI and MMIO APIC are at the same memory location, - * but actually not on the global bus: MSI is on PCI bus - * APIC is connected directly to the CPU. - * Mapping them on the global bus happens to work because - * MSI registers are reserved in APIC MMIO and vice versa. */ - apic_send_msi(addr, val); - return; - } - - dev = cpu_get_current_apic(); - if (!dev) { - return; - } - s = APIC_COMMON(dev); - - trace_apic_mem_writel(addr, val); - - switch(index) { - case 0x02: - s->id = (val >> 24); - break; - case 0x03: - break; - case 0x08: - if (apic_report_tpr_access) { - cpu_report_tpr_access(&s->cpu->env, TPR_ACCESS_WRITE); - } - s->tpr = val; - apic_sync_vapic(s, SYNC_TO_VAPIC); - apic_update_irq(s); - break; - case 0x09: - case 0x0a: - break; - case 0x0b: /* EOI */ - apic_eoi(s); - break; - case 0x0d: - s->log_dest = val >> 24; - break; - case 0x0e: - s->dest_mode = val >> 28; - break; - case 0x0f: - s->spurious_vec = val & 0x1ff; - apic_update_irq(s); - break; - case 0x10 ... 0x17: - case 0x18 ... 0x1f: - case 0x20 ... 0x27: - case 0x28: - break; - case 0x30: - s->icr[0] = val; - apic_deliver(dev, (s->icr[1] >> 24) & 0xff, (s->icr[0] >> 11) & 1, - (s->icr[0] >> 8) & 7, (s->icr[0] & 0xff), - (s->icr[0] >> 15) & 1); - break; - case 0x31: - s->icr[1] = val; - break; - case 0x32 ... 0x37: - { - int n = index - 0x32; - s->lvt[n] = val; - if (n == APIC_LVT_TIMER) { - apic_timer_update(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - } else if (n == APIC_LVT_LINT0 && apic_check_pic(s)) { - apic_update_irq(s); - } - } - break; - case 0x38: - s->initial_count = val; - s->initial_count_load_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - apic_timer_update(s, s->initial_count_load_time); - break; - case 0x39: - break; - case 0x3e: - { - int v; - s->divide_conf = val & 0xb; - v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4); - s->count_shift = (v + 1) & 7; - } - break; - default: - s->esr |= APIC_ESR_ILLEGAL_ADDRESS; - break; - } -} - -static void apic_pre_save(APICCommonState *s) -{ - apic_sync_vapic(s, SYNC_FROM_VAPIC); -} - -static void apic_post_load(APICCommonState *s) -{ - if (s->timer_expiry != -1) { - timer_mod(s->timer, s->timer_expiry); - } else { - timer_del(s->timer); - } -} - -static const MemoryRegionOps apic_io_ops = { - .old_mmio = { - .read = { apic_mem_readb, apic_mem_readw, apic_mem_readl, }, - .write = { apic_mem_writeb, apic_mem_writew, apic_mem_writel, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void apic_realize(DeviceState *dev, Error **errp) -{ - APICCommonState *s = APIC_COMMON(dev); - - memory_region_init_io(&s->io_memory, OBJECT(s), &apic_io_ops, s, "apic-msi", - APIC_SPACE_SIZE); - - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, apic_timer, s); - local_apics[s->idx] = s; - - msi_nonbroken = true; -} - -static void apic_class_init(ObjectClass *klass, void *data) -{ - APICCommonClass *k = APIC_COMMON_CLASS(klass); - - k->realize = apic_realize; - k->set_base = apic_set_base; - k->set_tpr = apic_set_tpr; - k->get_tpr = apic_get_tpr; - k->vapic_base_update = apic_vapic_base_update; - k->external_nmi = apic_external_nmi; - k->pre_save = apic_pre_save; - k->post_load = apic_post_load; -} - -static const TypeInfo apic_info = { - .name = "apic", - .instance_size = sizeof(APICCommonState), - .parent = TYPE_APIC_COMMON, - .class_init = apic_class_init, -}; - -static void apic_register_types(void) -{ - type_register_static(&apic_info); -} - -type_init(apic_register_types) diff --git a/qemu/hw/intc/apic_common.c b/qemu/hw/intc/apic_common.c deleted file mode 100644 index 4abe145c6..000000000 --- a/qemu/hw/intc/apic_common.c +++ /dev/null @@ -1,451 +0,0 @@ -/* - * APIC support - common bits of emulated and KVM kernel model - * - * Copyright (c) 2004-2005 Fabrice Bellard - * Copyright (c) 2011 Jan Kiszka, Siemens AG - * - * 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 - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/i386/apic.h" -#include "hw/i386/apic_internal.h" -#include "trace.h" -#include "sysemu/kvm.h" -#include "hw/qdev.h" -#include "hw/sysbus.h" - -static int apic_irq_delivered; -bool apic_report_tpr_access; - -void cpu_set_apic_base(DeviceState *dev, uint64_t val) -{ - trace_cpu_set_apic_base(val); - - if (dev) { - APICCommonState *s = APIC_COMMON(dev); - APICCommonClass *info = APIC_COMMON_GET_CLASS(s); - info->set_base(s, val); - } -} - -uint64_t cpu_get_apic_base(DeviceState *dev) -{ - if (dev) { - APICCommonState *s = APIC_COMMON(dev); - trace_cpu_get_apic_base((uint64_t)s->apicbase); - return s->apicbase; - } else { - trace_cpu_get_apic_base(MSR_IA32_APICBASE_BSP); - return MSR_IA32_APICBASE_BSP; - } -} - -void cpu_set_apic_tpr(DeviceState *dev, uint8_t val) -{ - APICCommonState *s; - APICCommonClass *info; - - if (!dev) { - return; - } - - s = APIC_COMMON(dev); - info = APIC_COMMON_GET_CLASS(s); - - info->set_tpr(s, val); -} - -uint8_t cpu_get_apic_tpr(DeviceState *dev) -{ - APICCommonState *s; - APICCommonClass *info; - - if (!dev) { - return 0; - } - - s = APIC_COMMON(dev); - info = APIC_COMMON_GET_CLASS(s); - - return info->get_tpr(s); -} - -void apic_enable_tpr_access_reporting(DeviceState *dev, bool enable) -{ - APICCommonState *s = APIC_COMMON(dev); - APICCommonClass *info = APIC_COMMON_GET_CLASS(s); - - apic_report_tpr_access = enable; - if (info->enable_tpr_reporting) { - info->enable_tpr_reporting(s, enable); - } -} - -void apic_enable_vapic(DeviceState *dev, hwaddr paddr) -{ - APICCommonState *s = APIC_COMMON(dev); - APICCommonClass *info = APIC_COMMON_GET_CLASS(s); - - s->vapic_paddr = paddr; - info->vapic_base_update(s); -} - -void apic_handle_tpr_access_report(DeviceState *dev, target_ulong ip, - TPRAccess access) -{ - APICCommonState *s = APIC_COMMON(dev); - - vapic_report_tpr_access(s->vapic, CPU(s->cpu), ip, access); -} - -void apic_report_irq_delivered(int delivered) -{ - apic_irq_delivered += delivered; - - trace_apic_report_irq_delivered(apic_irq_delivered); -} - -void apic_reset_irq_delivered(void) -{ - /* Copy this into a local variable to encourage gcc to emit a plain - * register for a sys/sdt.h marker. For details on this workaround, see: - * https://sourceware.org/bugzilla/show_bug.cgi?id=13296 - */ - volatile int a_i_d = apic_irq_delivered; - trace_apic_reset_irq_delivered(a_i_d); - - apic_irq_delivered = 0; -} - -int apic_get_irq_delivered(void) -{ - trace_apic_get_irq_delivered(apic_irq_delivered); - - return apic_irq_delivered; -} - -void apic_deliver_nmi(DeviceState *dev) -{ - APICCommonState *s = APIC_COMMON(dev); - APICCommonClass *info = APIC_COMMON_GET_CLASS(s); - - info->external_nmi(s); -} - -bool apic_next_timer(APICCommonState *s, int64_t current_time) -{ - int64_t d; - - /* We need to store the timer state separately to support APIC - * implementations that maintain a non-QEMU timer, e.g. inside the - * host kernel. This open-coded state allows us to migrate between - * both models. */ - s->timer_expiry = -1; - - if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED) { - return false; - } - - d = (current_time - s->initial_count_load_time) >> s->count_shift; - - if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) { - if (!s->initial_count) { - return false; - } - d = ((d / ((uint64_t)s->initial_count + 1)) + 1) * - ((uint64_t)s->initial_count + 1); - } else { - if (d >= s->initial_count) { - return false; - } - d = (uint64_t)s->initial_count + 1; - } - s->next_time = s->initial_count_load_time + (d << s->count_shift); - s->timer_expiry = s->next_time; - return true; -} - -void apic_init_reset(DeviceState *dev) -{ - APICCommonState *s; - APICCommonClass *info; - int i; - - if (!dev) { - return; - } - s = APIC_COMMON(dev); - s->tpr = 0; - s->spurious_vec = 0xff; - s->log_dest = 0; - s->dest_mode = 0xf; - memset(s->isr, 0, sizeof(s->isr)); - memset(s->tmr, 0, sizeof(s->tmr)); - memset(s->irr, 0, sizeof(s->irr)); - for (i = 0; i < APIC_LVT_NB; i++) { - s->lvt[i] = APIC_LVT_MASKED; - } - s->esr = 0; - memset(s->icr, 0, sizeof(s->icr)); - s->divide_conf = 0; - s->count_shift = 0; - s->initial_count = 0; - s->initial_count_load_time = 0; - s->next_time = 0; - s->wait_for_sipi = !cpu_is_bsp(s->cpu); - - if (s->timer) { - timer_del(s->timer); - } - s->timer_expiry = -1; - - info = APIC_COMMON_GET_CLASS(s); - if (info->reset) { - info->reset(s); - } -} - -void apic_designate_bsp(DeviceState *dev, bool bsp) -{ - if (dev == NULL) { - return; - } - - APICCommonState *s = APIC_COMMON(dev); - if (bsp) { - s->apicbase |= MSR_IA32_APICBASE_BSP; - } else { - s->apicbase &= ~MSR_IA32_APICBASE_BSP; - } -} - -static void apic_reset_common(DeviceState *dev) -{ - APICCommonState *s = APIC_COMMON(dev); - APICCommonClass *info = APIC_COMMON_GET_CLASS(s); - uint32_t bsp; - - bsp = s->apicbase & MSR_IA32_APICBASE_BSP; - s->apicbase = APIC_DEFAULT_ADDRESS | bsp | MSR_IA32_APICBASE_ENABLE; - - s->vapic_paddr = 0; - info->vapic_base_update(s); - - apic_init_reset(dev); -} - -/* This function is only used for old state version 1 and 2 */ -static int apic_load_old(QEMUFile *f, void *opaque, int version_id) -{ - APICCommonState *s = opaque; - APICCommonClass *info = APIC_COMMON_GET_CLASS(s); - int i; - - if (version_id > 2) { - return -EINVAL; - } - - /* XXX: what if the base changes? (registered memory regions) */ - qemu_get_be32s(f, &s->apicbase); - qemu_get_8s(f, &s->id); - qemu_get_8s(f, &s->arb_id); - qemu_get_8s(f, &s->tpr); - qemu_get_be32s(f, &s->spurious_vec); - qemu_get_8s(f, &s->log_dest); - qemu_get_8s(f, &s->dest_mode); - for (i = 0; i < 8; i++) { - qemu_get_be32s(f, &s->isr[i]); - qemu_get_be32s(f, &s->tmr[i]); - qemu_get_be32s(f, &s->irr[i]); - } - for (i = 0; i < APIC_LVT_NB; i++) { - qemu_get_be32s(f, &s->lvt[i]); - } - qemu_get_be32s(f, &s->esr); - qemu_get_be32s(f, &s->icr[0]); - qemu_get_be32s(f, &s->icr[1]); - qemu_get_be32s(f, &s->divide_conf); - s->count_shift = qemu_get_be32(f); - qemu_get_be32s(f, &s->initial_count); - s->initial_count_load_time = qemu_get_be64(f); - s->next_time = qemu_get_be64(f); - - if (version_id >= 2) { - s->timer_expiry = qemu_get_be64(f); - } - - if (info->post_load) { - info->post_load(s); - } - return 0; -} - -static void apic_common_realize(DeviceState *dev, Error **errp) -{ - APICCommonState *s = APIC_COMMON(dev); - APICCommonClass *info; - static DeviceState *vapic; - static int apic_no; - - if (apic_no >= MAX_APICS) { - error_setg(errp, "%s initialization failed.", - object_get_typename(OBJECT(dev))); - return; - } - s->idx = apic_no++; - - info = APIC_COMMON_GET_CLASS(s); - info->realize(dev, errp); - - /* Note: We need at least 1M to map the VAPIC option ROM */ - if (!vapic && s->vapic_control & VAPIC_ENABLE_MASK && - ram_size >= 1024 * 1024) { - vapic = sysbus_create_simple("kvmvapic", -1, NULL); - } - s->vapic = vapic; - if (apic_report_tpr_access && info->enable_tpr_reporting) { - info->enable_tpr_reporting(s, true); - } - -} - -static int apic_pre_load(void *opaque) -{ - APICCommonState *s = APIC_COMMON(opaque); - - /* The default is !cpu_is_bsp(s->cpu), but the common value is 0 - * so that's what apic_common_sipi_needed checks for. Reset to - * the value that is assumed when the apic_sipi subsection is - * absent. - */ - s->wait_for_sipi = 0; - return 0; -} - -static void apic_dispatch_pre_save(void *opaque) -{ - APICCommonState *s = APIC_COMMON(opaque); - APICCommonClass *info = APIC_COMMON_GET_CLASS(s); - - if (info->pre_save) { - info->pre_save(s); - } -} - -static int apic_dispatch_post_load(void *opaque, int version_id) -{ - APICCommonState *s = APIC_COMMON(opaque); - APICCommonClass *info = APIC_COMMON_GET_CLASS(s); - - if (info->post_load) { - info->post_load(s); - } - return 0; -} - -static bool apic_common_sipi_needed(void *opaque) -{ - APICCommonState *s = APIC_COMMON(opaque); - return s->wait_for_sipi != 0; -} - -static const VMStateDescription vmstate_apic_common_sipi = { - .name = "apic_sipi", - .version_id = 1, - .minimum_version_id = 1, - .needed = apic_common_sipi_needed, - .fields = (VMStateField[]) { - VMSTATE_INT32(sipi_vector, APICCommonState), - VMSTATE_INT32(wait_for_sipi, APICCommonState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_apic_common = { - .name = "apic", - .version_id = 3, - .minimum_version_id = 3, - .minimum_version_id_old = 1, - .load_state_old = apic_load_old, - .pre_load = apic_pre_load, - .pre_save = apic_dispatch_pre_save, - .post_load = apic_dispatch_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(apicbase, APICCommonState), - VMSTATE_UINT8(id, APICCommonState), - VMSTATE_UINT8(arb_id, APICCommonState), - VMSTATE_UINT8(tpr, APICCommonState), - VMSTATE_UINT32(spurious_vec, APICCommonState), - VMSTATE_UINT8(log_dest, APICCommonState), - VMSTATE_UINT8(dest_mode, APICCommonState), - VMSTATE_UINT32_ARRAY(isr, APICCommonState, 8), - VMSTATE_UINT32_ARRAY(tmr, APICCommonState, 8), - VMSTATE_UINT32_ARRAY(irr, APICCommonState, 8), - VMSTATE_UINT32_ARRAY(lvt, APICCommonState, APIC_LVT_NB), - VMSTATE_UINT32(esr, APICCommonState), - VMSTATE_UINT32_ARRAY(icr, APICCommonState, 2), - VMSTATE_UINT32(divide_conf, APICCommonState), - VMSTATE_INT32(count_shift, APICCommonState), - VMSTATE_UINT32(initial_count, APICCommonState), - VMSTATE_INT64(initial_count_load_time, APICCommonState), - VMSTATE_INT64(next_time, APICCommonState), - VMSTATE_INT64(timer_expiry, - APICCommonState), /* open-coded timer state */ - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_apic_common_sipi, - NULL - } -}; - -static Property apic_properties_common[] = { - DEFINE_PROP_UINT8("id", APICCommonState, id, -1), - DEFINE_PROP_UINT8("version", APICCommonState, version, 0x14), - DEFINE_PROP_BIT("vapic", APICCommonState, vapic_control, VAPIC_ENABLE_BIT, - true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void apic_common_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_apic_common; - dc->reset = apic_reset_common; - dc->props = apic_properties_common; - dc->realize = apic_common_realize; - /* - * Reason: APIC and CPU need to be wired up by - * x86_cpu_apic_create() - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo apic_common_type = { - .name = TYPE_APIC_COMMON, - .parent = TYPE_DEVICE, - .instance_size = sizeof(APICCommonState), - .class_size = sizeof(APICCommonClass), - .class_init = apic_common_class_init, - .abstract = true, -}; - -static void apic_common_register_types(void) -{ - type_register_static(&apic_common_type); -} - -type_init(apic_common_register_types) diff --git a/qemu/hw/intc/arm_gic.c b/qemu/hw/intc/arm_gic.c deleted file mode 100644 index f55124174..000000000 --- a/qemu/hw/intc/arm_gic.c +++ /dev/null @@ -1,1385 +0,0 @@ -/* - * ARM Generic/Distributed Interrupt Controller - * - * Copyright (c) 2006-2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ - -/* This file contains implementation code for the RealView EB interrupt - * controller, MPCore distributed interrupt controller and ARMv7-M - * Nested Vectored Interrupt Controller. - * It is compiled in two ways: - * (1) as a standalone file to produce a sysbus device which is a GIC - * that can be used on the realview board and as one of the builtin - * private peripherals for the ARM MP CPUs (11MPCore, A9, etc) - * (2) by being directly #included into armv7m_nvic.c to produce the - * 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 - -#ifdef DEBUG_GIC -#define DPRINTF(fmt, ...) \ -do { fprintf(stderr, "arm_gic: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - -static const uint8_t gic_id_11mpcore[] = { - 0x00, 0x00, 0x00, 0x00, 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 -}; - -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) -{ - if (s->num_cpu > 1) { - return current_cpu->cpu_index; - } - return 0; -} - -/* Return true if this GIC config has interrupt groups, which is - * true if we're a GICv2, or a GICv1 with the security extensions. - */ -static inline bool gic_has_groups(GICState *s) -{ - return s->revision == 2 || s->security_extn; -} - -/* TODO: Many places that call this routine could be optimized. */ -/* Update interrupt status after enabled or pending bits have been changed. */ -void gic_update(GICState *s) -{ - int best_irq; - int best_prio; - int irq; - int irq_level, fiq_level; - int cpu; - int cm; - - 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)) - || !(s->cpu_ctlr[cpu] & (GICC_CTLR_EN_GRP0 | GICC_CTLR_EN_GRP1))) { - qemu_irq_lower(s->parent_irq[cpu]); - qemu_irq_lower(s->parent_fiq[cpu]); - continue; - } - best_prio = 0x100; - best_irq = 1023; - for (irq = 0; irq < s->num_irq; irq++) { - if (GIC_TEST_ENABLED(irq, cm) && gic_test_pending(s, irq, cm) && - (irq < GIC_INTERNAL || GIC_TARGET(irq) & cm)) { - if (GIC_GET_PRIORITY(irq, cpu) < best_prio) { - best_prio = GIC_GET_PRIORITY(irq, cpu); - best_irq = irq; - } - } - } - - irq_level = fiq_level = 0; - - if (best_prio < s->priority_mask[cpu]) { - s->current_pending[cpu] = best_irq; - if (best_prio < s->running_priority[cpu]) { - int group = GIC_TEST_GROUP(best_irq, cm); - - if (extract32(s->ctlr, group, 1) && - extract32(s->cpu_ctlr[cpu], group, 1)) { - if (group == 0 && s->cpu_ctlr[cpu] & GICC_CTLR_FIQ_EN) { - DPRINTF("Raised pending FIQ %d (cpu %d)\n", - best_irq, cpu); - fiq_level = 1; - } else { - DPRINTF("Raised pending IRQ %d (cpu %d)\n", - best_irq, cpu); - irq_level = 1; - } - } - } - } - - qemu_set_irq(s->parent_irq[cpu], irq_level); - qemu_set_irq(s->parent_fiq[cpu], fiq_level); - } -} - -void gic_set_pending_private(GICState *s, int cpu, int irq) -{ - int cm = 1 << cpu; - - if (gic_test_pending(s, irq, cm)) { - return; - } - - DPRINTF("Set %d pending cpu %d\n", irq, cpu); - GIC_SET_PENDING(irq, cm); - gic_update(s); -} - -static void gic_set_irq_11mpcore(GICState *s, int irq, int level, - int cm, int target) -{ - if (level) { - GIC_SET_LEVEL(irq, cm); - if (GIC_TEST_EDGE_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) { - DPRINTF("Set %d pending mask %x\n", irq, target); - GIC_SET_PENDING(irq, target); - } - } else { - GIC_CLEAR_LEVEL(irq, cm); - } -} - -static void gic_set_irq_generic(GICState *s, int irq, int level, - int cm, int target) -{ - if (level) { - GIC_SET_LEVEL(irq, cm); - DPRINTF("Set %d pending mask %x\n", irq, target); - if (GIC_TEST_EDGE_TRIGGER(irq)) { - GIC_SET_PENDING(irq, target); - } - } else { - GIC_CLEAR_LEVEL(irq, cm); - } -} - -/* Process a change in an external IRQ input. */ -static void gic_set_irq(void *opaque, int irq, int level) -{ - /* Meaning of the 'irq' parameter: - * [0..N-1] : external interrupts - * [N..N+31] : PPI (internal) interrupts for CPU 0 - * [N+32..N+63] : PPI (internal interrupts for CPU 1 - * ... - */ - GICState *s = (GICState *)opaque; - int cm, target; - if (irq < (s->num_irq - GIC_INTERNAL)) { - /* The first external input line is internal interrupt 32. */ - cm = ALL_CPU_MASK; - irq += GIC_INTERNAL; - target = GIC_TARGET(irq); - } else { - int cpu; - irq -= (s->num_irq - GIC_INTERNAL); - cpu = irq / GIC_INTERNAL; - irq %= GIC_INTERNAL; - cm = 1 << cpu; - target = cm; - } - - assert(irq >= GIC_NR_SGIS); - - if (level == GIC_TEST_LEVEL(irq, cm)) { - return; - } - - if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { - gic_set_irq_11mpcore(s, irq, level, cm, target); - } else { - gic_set_irq_generic(s, irq, level, cm, target); - } - - gic_update(s); -} - -static uint16_t gic_get_current_pending_irq(GICState *s, int cpu, - MemTxAttrs attrs) -{ - uint16_t pending_irq = s->current_pending[cpu]; - - if (pending_irq < GIC_MAXIRQ && gic_has_groups(s)) { - int group = GIC_TEST_GROUP(pending_irq, (1 << cpu)); - /* On a GIC without the security extensions, reading this register - * behaves in the same way as a secure access to a GIC with them. - */ - bool secure = !s->security_extn || attrs.secure; - - if (group == 0 && !secure) { - /* Group0 interrupts hidden from Non-secure access */ - return 1023; - } - if (group == 1 && secure && !(s->cpu_ctlr[cpu] & GICC_CTLR_ACK_CTL)) { - /* Group1 interrupts only seen by Secure access if - * AckCtl bit set. - */ - return 1022; - } - } - return pending_irq; -} - -static int gic_get_group_priority(GICState *s, int cpu, int irq) -{ - /* 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 { - bpr = s->bpr[cpu]; - } - - /* 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) -{ - int ret, irq, src; - int cm = 1 << cpu; - - /* gic_get_current_pending_irq() will return 1022 or 1023 appropriately - * 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); - - if (irq >= GIC_MAXIRQ) { - DPRINTF("ACK, no pending interrupt or it is hidden: %d\n", irq); - return irq; - } - - if (GIC_GET_PRIORITY(irq, cpu) >= s->running_priority[cpu]) { - DPRINTF("ACK, pending interrupt (%d) has insufficient priority\n", irq); - return 1023; - } - - if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { - /* Clear pending flags for both level and edge triggered interrupts. - * Level triggered IRQs will be reasserted once they become inactive. - */ - GIC_CLEAR_PENDING(irq, GIC_TEST_MODEL(irq) ? ALL_CPU_MASK : cm); - ret = irq; - } else { - if (irq < GIC_NR_SGIS) { - /* Lookup the source CPU for the SGI and clear this in the - * sgi_pending map. Return the src and clear the overall pending - * state on this CPU if the SGI is not pending from any CPUs. - */ - assert(s->sgi_pending[irq][cpu] != 0); - src = ctz32(s->sgi_pending[irq][cpu]); - s->sgi_pending[irq][cpu] &= ~(1 << src); - if (s->sgi_pending[irq][cpu] == 0) { - GIC_CLEAR_PENDING(irq, GIC_TEST_MODEL(irq) ? ALL_CPU_MASK : cm); - } - ret = irq | ((src & 0x7) << 10); - } else { - /* Clear pending state for both level and edge triggered - * interrupts. (level triggered interrupts with an active line - * remain pending, see gic_test_pending) - */ - GIC_CLEAR_PENDING(irq, GIC_TEST_MODEL(irq) ? ALL_CPU_MASK : cm); - ret = irq; - } - } - - gic_activate_irq(s, cpu, irq); - gic_update(s); - DPRINTF("ACK %d\n", irq); - return ret; -} - -void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val, - MemTxAttrs attrs) -{ - if (s->security_extn && !attrs.secure) { - if (!GIC_TEST_GROUP(irq, (1 << cpu))) { - return; /* Ignore Non-secure access of Group0 IRQ */ - } - val = 0x80 | (val >> 1); /* Non-secure view */ - } - - if (irq < GIC_INTERNAL) { - s->priority1[irq][cpu] = val; - } else { - s->priority2[(irq) - GIC_INTERNAL] = val; - } -} - -static uint32_t gic_get_priority(GICState *s, int cpu, int irq, - MemTxAttrs attrs) -{ - uint32_t prio = GIC_GET_PRIORITY(irq, cpu); - - if (s->security_extn && !attrs.secure) { - if (!GIC_TEST_GROUP(irq, (1 << cpu))) { - return 0; /* Non-secure access cannot read priority of Group0 IRQ */ - } - prio = (prio << 1) & 0xff; /* Non-secure view */ - } - return prio; -} - -static void gic_set_priority_mask(GICState *s, int cpu, uint8_t pmask, - MemTxAttrs attrs) -{ - if (s->security_extn && !attrs.secure) { - if (s->priority_mask[cpu] & 0x80) { - /* Priority Mask in upper half */ - pmask = 0x80 | (pmask >> 1); - } else { - /* Non-secure write ignored if priority mask is in lower half */ - return; - } - } - s->priority_mask[cpu] = pmask; -} - -static uint32_t gic_get_priority_mask(GICState *s, int cpu, MemTxAttrs attrs) -{ - uint32_t pmask = s->priority_mask[cpu]; - - if (s->security_extn && !attrs.secure) { - if (pmask & 0x80) { - /* Priority Mask in upper half, return Non-secure view */ - pmask = (pmask << 1) & 0xff; - } else { - /* Priority Mask in lower half, RAZ */ - pmask = 0; - } - } - return pmask; -} - -static uint32_t gic_get_cpu_control(GICState *s, int cpu, MemTxAttrs attrs) -{ - uint32_t ret = s->cpu_ctlr[cpu]; - - if (s->security_extn && !attrs.secure) { - /* Construct the NS banked view of GICC_CTLR from the correct - * bits of the S banked view. We don't need to move the bypass - * control bits because we don't implement that (IMPDEF) part - * of the GIC architecture. - */ - ret = (ret & (GICC_CTLR_EN_GRP1 | GICC_CTLR_EOIMODE_NS)) >> 1; - } - return ret; -} - -static void gic_set_cpu_control(GICState *s, int cpu, uint32_t value, - MemTxAttrs attrs) -{ - uint32_t mask; - - if (s->security_extn && !attrs.secure) { - /* The NS view can only write certain bits in the register; - * the rest are unchanged - */ - mask = GICC_CTLR_EN_GRP1; - if (s->revision == 2) { - mask |= GICC_CTLR_EOIMODE_NS; - } - s->cpu_ctlr[cpu] &= ~mask; - s->cpu_ctlr[cpu] |= (value << 1) & mask; - } else { - if (s->revision == 2) { - mask = s->security_extn ? GICC_CTLR_V2_S_MASK : GICC_CTLR_V2_MASK; - } else { - mask = s->security_extn ? GICC_CTLR_V1_S_MASK : GICC_CTLR_V1_MASK; - } - s->cpu_ctlr[cpu] = value & mask; - } - DPRINTF("CPU Interface %d: Group0 Interrupts %sabled, " - "Group1 Interrupts %sabled\n", cpu, - (s->cpu_ctlr[cpu] & GICC_CTLR_EN_GRP0) ? "En" : "Dis", - (s->cpu_ctlr[cpu] & GICC_CTLR_EN_GRP1) ? "En" : "Dis"); -} - -static uint8_t gic_get_running_priority(GICState *s, int cpu, MemTxAttrs attrs) -{ - if (s->security_extn && !attrs.secure) { - if (s->running_priority[cpu] & 0x80) { - /* Running priority in upper half of range: return the Non-secure - * view of the priority. - */ - return s->running_priority[cpu] << 1; - } else { - /* Running priority in lower half of range: RAZ */ - return 0; - } - } else { - return s->running_priority[cpu]; - } -} - -/* 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 cm = 1 << cpu; - int group; - - DPRINTF("EOI %d\n", irq); - if (irq >= s->num_irq) { - /* This handles two cases: - * 1. If software writes the ID of a spurious interrupt [ie 1023] - * to the GICC_EOIR, the GIC ignores that write. - * 2. If software writes the number of a non-existent interrupt - * this must be a subcase of "value written does not match the last - * valid interrupt value read from the Interrupt Acknowledge - * register" and so this is UNPREDICTABLE. We choose to ignore it. - */ - return; - } - 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 - raised. */ - if (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm) - && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) { - DPRINTF("Set %d pending mask %x\n", irq, cm); - GIC_SET_PENDING(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; - } - - /* Secure EOI with GICC_CTLR.AckCtl == 0 when the IRQ is a Group 1 - * interrupt is UNPREDICTABLE. We choose to handle it as if AckCtl == 1, - * i.e. go ahead and complete the irq anyway. - */ - - 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) -{ - GICState *s = (GICState *)opaque; - uint32_t res; - int irq; - int i; - int cpu; - int cm; - int mask; - - cpu = gic_get_current_cpu(s); - cm = 1 << cpu; - if (offset < 0x100) { - if (offset == 0) { /* GICD_CTLR */ - if (s->security_extn && !attrs.secure) { - /* The NS bank of this register is just an alias of the - * EnableGrp1 bit in the S bank version. - */ - return extract32(s->ctlr, 1, 1); - } else { - return s->ctlr; - } - } - if (offset == 4) - /* Interrupt Controller Type Register */ - return ((s->num_irq / 32) - 1) - | ((s->num_cpu - 1) << 5) - | (s->security_extn << 10); - if (offset < 0x08) - return 0; - if (offset >= 0x80) { - /* Interrupt Group Registers: these RAZ/WI if this is an NS - * access to a GIC with the security extensions, or if the GIC - * doesn't have groups at all. - */ - res = 0; - if (!(s->security_extn && !attrs.secure) && gic_has_groups(s)) { - /* Every byte offset holds 8 group status bits */ - irq = (offset - 0x080) * 8 + GIC_BASE_IRQ; - if (irq >= s->num_irq) { - goto bad_reg; - } - for (i = 0; i < 8; i++) { - if (GIC_TEST_GROUP(irq + i, cm)) { - res |= (1 << i); - } - } - } - return res; - } - goto bad_reg; - } else if (offset < 0x200) { - /* Interrupt Set/Clear Enable. */ - if (offset < 0x180) - irq = (offset - 0x100) * 8; - else - irq = (offset - 0x180) * 8; - irq += GIC_BASE_IRQ; - if (irq >= s->num_irq) - goto bad_reg; - res = 0; - for (i = 0; i < 8; i++) { - if (GIC_TEST_ENABLED(irq + i, cm)) { - res |= (1 << i); - } - } - } else if (offset < 0x300) { - /* Interrupt Set/Clear Pending. */ - if (offset < 0x280) - irq = (offset - 0x200) * 8; - else - irq = (offset - 0x280) * 8; - irq += GIC_BASE_IRQ; - if (irq >= s->num_irq) - goto bad_reg; - res = 0; - mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK; - for (i = 0; i < 8; i++) { - if (gic_test_pending(s, irq + i, mask)) { - res |= (1 << i); - } - } - } else if (offset < 0x400) { - /* Interrupt Active. */ - irq = (offset - 0x300) * 8 + GIC_BASE_IRQ; - if (irq >= s->num_irq) - goto bad_reg; - res = 0; - mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK; - for (i = 0; i < 8; i++) { - if (GIC_TEST_ACTIVE(irq + i, mask)) { - res |= (1 << i); - } - } - } else if (offset < 0x800) { - /* Interrupt Priority. */ - irq = (offset - 0x400) + GIC_BASE_IRQ; - if (irq >= s->num_irq) - goto bad_reg; - res = gic_get_priority(s, cpu, irq, attrs); - } else if (offset < 0xc00) { - /* Interrupt CPU Target. */ - if (s->num_cpu == 1 && s->revision != REV_11MPCORE) { - /* For uniprocessor GICs these RAZ/WI */ - res = 0; - } else { - irq = (offset - 0x800) + GIC_BASE_IRQ; - if (irq >= s->num_irq) { - goto bad_reg; - } - if (irq >= 29 && irq <= 31) { - res = cm; - } else { - res = GIC_TARGET(irq); - } - } - } else if (offset < 0xf00) { - /* Interrupt Configuration. */ - irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ; - if (irq >= s->num_irq) - goto bad_reg; - res = 0; - for (i = 0; i < 4; i++) { - if (GIC_TEST_MODEL(irq + i)) - res |= (1 << (i * 2)); - if (GIC_TEST_EDGE_TRIGGER(irq + i)) - res |= (2 << (i * 2)); - } - } else if (offset < 0xf10) { - goto bad_reg; - } else if (offset < 0xf30) { - if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { - goto bad_reg; - } - - if (offset < 0xf20) { - /* GICD_CPENDSGIRn */ - irq = (offset - 0xf10); - } else { - irq = (offset - 0xf20); - /* GICD_SPENDSGIRn */ - } - - res = s->sgi_pending[irq][cpu]; - } else if (offset < 0xfd0) { - goto bad_reg; - } else if (offset < 0x1000) { - if (offset & 3) { - res = 0; - } else { - 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: - qemu_log_mask(LOG_GUEST_ERROR, - "gic_dist_readb: Bad offset %x\n", (int)offset); - return 0; -} - -static MemTxResult gic_dist_read(void *opaque, hwaddr offset, uint64_t *data, - unsigned size, MemTxAttrs attrs) -{ - switch (size) { - case 1: - *data = gic_dist_readb(opaque, offset, attrs); - return MEMTX_OK; - case 2: - *data = gic_dist_readb(opaque, offset, attrs); - *data |= gic_dist_readb(opaque, offset + 1, attrs) << 8; - return MEMTX_OK; - case 4: - *data = gic_dist_readb(opaque, offset, attrs); - *data |= gic_dist_readb(opaque, offset + 1, attrs) << 8; - *data |= gic_dist_readb(opaque, offset + 2, attrs) << 16; - *data |= gic_dist_readb(opaque, offset + 3, attrs) << 24; - return MEMTX_OK; - default: - return MEMTX_ERROR; - } -} - -static void gic_dist_writeb(void *opaque, hwaddr offset, - uint32_t value, MemTxAttrs attrs) -{ - GICState *s = (GICState *)opaque; - int irq; - int i; - int cpu; - - cpu = gic_get_current_cpu(s); - if (offset < 0x100) { - if (offset == 0) { - if (s->security_extn && !attrs.secure) { - /* NS version is just an alias of the S version's bit 1 */ - s->ctlr = deposit32(s->ctlr, 1, 1, value); - } else if (gic_has_groups(s)) { - s->ctlr = value & (GICD_CTLR_EN_GRP0 | GICD_CTLR_EN_GRP1); - } else { - s->ctlr = value & GICD_CTLR_EN_GRP0; - } - DPRINTF("Distributor: Group0 %sabled; Group 1 %sabled\n", - s->ctlr & GICD_CTLR_EN_GRP0 ? "En" : "Dis", - s->ctlr & GICD_CTLR_EN_GRP1 ? "En" : "Dis"); - } else if (offset < 4) { - /* ignored. */ - } else if (offset >= 0x80) { - /* Interrupt Group Registers: RAZ/WI for NS access to secure - * GIC, or for GICs without groups. - */ - if (!(s->security_extn && !attrs.secure) && gic_has_groups(s)) { - /* Every byte offset holds 8 group status bits */ - irq = (offset - 0x80) * 8 + GIC_BASE_IRQ; - if (irq >= s->num_irq) { - goto bad_reg; - } - for (i = 0; i < 8; i++) { - /* Group bits are banked for private interrupts */ - int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; - if (value & (1 << i)) { - /* Group1 (Non-secure) */ - GIC_SET_GROUP(irq + i, cm); - } else { - /* Group0 (Secure) */ - GIC_CLEAR_GROUP(irq + i, cm); - } - } - } - } else { - goto bad_reg; - } - } else if (offset < 0x180) { - /* Interrupt Set Enable. */ - irq = (offset - 0x100) * 8 + GIC_BASE_IRQ; - if (irq >= s->num_irq) - goto bad_reg; - if (irq < GIC_NR_SGIS) { - value = 0xff; - } - - for (i = 0; i < 8; i++) { - if (value & (1 << i)) { - int mask = - (irq < GIC_INTERNAL) ? (1 << cpu) : GIC_TARGET(irq + i); - int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; - - if (!GIC_TEST_ENABLED(irq + i, cm)) { - DPRINTF("Enabled IRQ %d\n", irq + i); - } - GIC_SET_ENABLED(irq + i, cm); - /* If a raised level triggered IRQ enabled then mark - is as pending. */ - if (GIC_TEST_LEVEL(irq + i, mask) - && !GIC_TEST_EDGE_TRIGGER(irq + i)) { - DPRINTF("Set %d pending mask %x\n", irq + i, mask); - GIC_SET_PENDING(irq + i, mask); - } - } - } - } else if (offset < 0x200) { - /* Interrupt Clear Enable. */ - irq = (offset - 0x180) * 8 + GIC_BASE_IRQ; - if (irq >= s->num_irq) - goto bad_reg; - if (irq < GIC_NR_SGIS) { - value = 0; - } - - for (i = 0; i < 8; i++) { - if (value & (1 << i)) { - int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; - - if (GIC_TEST_ENABLED(irq + i, cm)) { - DPRINTF("Disabled IRQ %d\n", irq + i); - } - GIC_CLEAR_ENABLED(irq + i, cm); - } - } - } else if (offset < 0x280) { - /* Interrupt Set Pending. */ - irq = (offset - 0x200) * 8 + GIC_BASE_IRQ; - if (irq >= s->num_irq) - goto bad_reg; - if (irq < GIC_NR_SGIS) { - value = 0; - } - - for (i = 0; i < 8; i++) { - if (value & (1 << i)) { - GIC_SET_PENDING(irq + i, GIC_TARGET(irq + i)); - } - } - } else if (offset < 0x300) { - /* Interrupt Clear Pending. */ - irq = (offset - 0x280) * 8 + GIC_BASE_IRQ; - if (irq >= s->num_irq) - goto bad_reg; - if (irq < GIC_NR_SGIS) { - value = 0; - } - - for (i = 0; i < 8; i++) { - /* ??? This currently clears the pending bit for all CPUs, even - for per-CPU interrupts. It's unclear whether this is the - corect behavior. */ - if (value & (1 << i)) { - GIC_CLEAR_PENDING(irq + i, ALL_CPU_MASK); - } - } - } else if (offset < 0x400) { - /* Interrupt Active. */ - goto bad_reg; - } else if (offset < 0x800) { - /* Interrupt Priority. */ - irq = (offset - 0x400) + GIC_BASE_IRQ; - if (irq >= s->num_irq) - goto bad_reg; - gic_set_priority(s, cpu, irq, value, attrs); - } else if (offset < 0xc00) { - /* Interrupt CPU Target. RAZ/WI on uniprocessor GICs, with the - * annoying exception of the 11MPCore's GIC. - */ - if (s->num_cpu != 1 || s->revision == REV_11MPCORE) { - irq = (offset - 0x800) + GIC_BASE_IRQ; - if (irq >= s->num_irq) { - goto bad_reg; - } - if (irq < 29) { - value = 0; - } else if (irq < GIC_INTERNAL) { - value = ALL_CPU_MASK; - } - s->irq_target[irq] = value & ALL_CPU_MASK; - } - } else if (offset < 0xf00) { - /* Interrupt Configuration. */ - irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ; - if (irq >= s->num_irq) - goto bad_reg; - if (irq < GIC_NR_SGIS) - value |= 0xaa; - for (i = 0; i < 4; i++) { - if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { - if (value & (1 << (i * 2))) { - GIC_SET_MODEL(irq + i); - } else { - GIC_CLEAR_MODEL(irq + i); - } - } - if (value & (2 << (i * 2))) { - GIC_SET_EDGE_TRIGGER(irq + i); - } else { - GIC_CLEAR_EDGE_TRIGGER(irq + i); - } - } - } else if (offset < 0xf10) { - /* 0xf00 is only handled for 32-bit writes. */ - goto bad_reg; - } else if (offset < 0xf20) { - /* GICD_CPENDSGIRn */ - if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { - goto bad_reg; - } - irq = (offset - 0xf10); - - s->sgi_pending[irq][cpu] &= ~value; - if (s->sgi_pending[irq][cpu] == 0) { - GIC_CLEAR_PENDING(irq, 1 << cpu); - } - } else if (offset < 0xf30) { - /* GICD_SPENDSGIRn */ - if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { - goto bad_reg; - } - irq = (offset - 0xf20); - - GIC_SET_PENDING(irq, 1 << cpu); - s->sgi_pending[irq][cpu] |= value; - } else { - goto bad_reg; - } - gic_update(s); - return; -bad_reg: - qemu_log_mask(LOG_GUEST_ERROR, - "gic_dist_writeb: Bad offset %x\n", (int)offset); -} - -static void gic_dist_writew(void *opaque, hwaddr offset, - uint32_t value, MemTxAttrs attrs) -{ - gic_dist_writeb(opaque, offset, value & 0xff, attrs); - gic_dist_writeb(opaque, offset + 1, value >> 8, attrs); -} - -static void gic_dist_writel(void *opaque, hwaddr offset, - uint32_t value, MemTxAttrs attrs) -{ - GICState *s = (GICState *)opaque; - if (offset == 0xf00) { - int cpu; - int irq; - int mask; - int target_cpu; - - cpu = gic_get_current_cpu(s); - irq = value & 0x3ff; - switch ((value >> 24) & 3) { - case 0: - mask = (value >> 16) & ALL_CPU_MASK; - break; - case 1: - mask = ALL_CPU_MASK ^ (1 << cpu); - break; - case 2: - mask = 1 << cpu; - break; - default: - DPRINTF("Bad Soft Int target filter\n"); - mask = ALL_CPU_MASK; - break; - } - GIC_SET_PENDING(irq, mask); - target_cpu = ctz32(mask); - while (target_cpu < GIC_NCPU) { - s->sgi_pending[irq][target_cpu] |= (1 << cpu); - mask &= ~(1 << target_cpu); - target_cpu = ctz32(mask); - } - gic_update(s); - return; - } - gic_dist_writew(opaque, offset, value & 0xffff, attrs); - gic_dist_writew(opaque, offset + 2, value >> 16, attrs); -} - -static MemTxResult gic_dist_write(void *opaque, hwaddr offset, uint64_t data, - unsigned size, MemTxAttrs attrs) -{ - switch (size) { - case 1: - gic_dist_writeb(opaque, offset, data, attrs); - return MEMTX_OK; - case 2: - gic_dist_writew(opaque, offset, data, attrs); - return MEMTX_OK; - case 4: - gic_dist_writel(opaque, offset, data, attrs); - return MEMTX_OK; - default: - return MEMTX_ERROR; - } -} - -static inline uint32_t gic_apr_ns_view(GICState *s, int cpu, int regno) -{ - /* Return the Nonsecure view of GICC_APR. 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. */ - 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) -{ - switch (offset) { - case 0x00: /* Control */ - *data = gic_get_cpu_control(s, cpu, attrs); - break; - case 0x04: /* Priority mask */ - *data = gic_get_priority_mask(s, cpu, attrs); - break; - case 0x08: /* Binary Point */ - if (s->security_extn && !attrs.secure) { - /* BPR is banked. Non-secure copy stored in ABPR. */ - *data = s->abpr[cpu]; - } else { - *data = s->bpr[cpu]; - } - break; - case 0x0c: /* Acknowledge */ - *data = gic_acknowledge_irq(s, cpu, attrs); - break; - case 0x14: /* Running Priority */ - *data = gic_get_running_priority(s, cpu, attrs); - break; - case 0x18: /* Highest Pending Interrupt */ - *data = gic_get_current_pending_irq(s, cpu, attrs); - break; - case 0x1c: /* Aliased Binary Point */ - /* GIC v2, no security: ABPR - * GIC v1, no security: not implemented (RAZ/WI) - * With security extensions, secure access: ABPR (alias of NS BPR) - * With security extensions, nonsecure access: RAZ/WI - */ - if (!gic_has_groups(s) || (s->security_extn && !attrs.secure)) { - *data = 0; - } else { - *data = s->abpr[cpu]; - } - break; - case 0xd0: case 0xd4: case 0xd8: case 0xdc: - { - 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 is the top half of GIC_NSAPR */ - *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); - return MEMTX_ERROR; - } - return MEMTX_OK; -} - -static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset, - uint32_t value, MemTxAttrs attrs) -{ - switch (offset) { - case 0x00: /* Control */ - gic_set_cpu_control(s, cpu, value, attrs); - break; - case 0x04: /* Priority mask */ - gic_set_priority_mask(s, cpu, value, attrs); - break; - case 0x08: /* Binary Point */ - if (s->security_extn && !attrs.secure) { - s->abpr[cpu] = MAX(value & 0x7, GIC_MIN_ABPR); - } else { - s->bpr[cpu] = MAX(value & 0x7, GIC_MIN_BPR); - } - break; - case 0x10: /* End Of Interrupt */ - gic_complete_irq(s, cpu, value & 0x3ff, attrs); - return MEMTX_OK; - case 0x1c: /* Aliased Binary Point */ - if (!gic_has_groups(s) || (s->security_extn && !attrs.secure)) { - /* unimplemented, or NS access: RAZ/WI */ - return MEMTX_OK; - } else { - s->abpr[cpu] = MAX(value & 0x7, GIC_MIN_ABPR); - } - break; - case 0xd0: case 0xd4: case 0xd8: case 0xdc: - { - 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 is the top half of GIC_NSAPR */ - 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, - "gic_cpu_write: Bad offset %x\n", (int)offset); - return MEMTX_ERROR; - } - gic_update(s); - return MEMTX_OK; -} - -/* Wrappers to read/write the GIC CPU interface for the current CPU */ -static MemTxResult gic_thiscpu_read(void *opaque, hwaddr addr, uint64_t *data, - unsigned size, MemTxAttrs attrs) -{ - GICState *s = (GICState *)opaque; - return gic_cpu_read(s, gic_get_current_cpu(s), addr, data, attrs); -} - -static MemTxResult gic_thiscpu_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size, - MemTxAttrs attrs) -{ - GICState *s = (GICState *)opaque; - return gic_cpu_write(s, gic_get_current_cpu(s), addr, value, attrs); -} - -/* Wrappers to read/write the GIC CPU interface for a specific CPU. - * These just decode the opaque pointer into GICState* + cpu id. - */ -static MemTxResult gic_do_cpu_read(void *opaque, hwaddr addr, uint64_t *data, - unsigned size, MemTxAttrs attrs) -{ - GICState **backref = (GICState **)opaque; - GICState *s = *backref; - int id = (backref - s->backref); - return gic_cpu_read(s, id, addr, data, attrs); -} - -static MemTxResult gic_do_cpu_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size, - MemTxAttrs attrs) -{ - GICState **backref = (GICState **)opaque; - GICState *s = *backref; - int id = (backref - s->backref); - return gic_cpu_write(s, id, addr, value, attrs); -} - -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 = { - .read_with_attrs = gic_do_cpu_read, - .write_with_attrs = gic_do_cpu_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* This function is used by nvic model */ -void gic_init_irqs_and_distributor(GICState *s) -{ - gic_init_irqs_and_mmio(s, gic_set_irq, gic_ops); -} - -static void arm_gic_realize(DeviceState *dev, Error **errp) -{ - /* Device instance realize function for the GIC sysbus device */ - int i; - GICState *s = ARM_GIC(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - ARMGICClass *agc = ARM_GIC_GET_CLASS(s); - Error *local_err = NULL; - - agc->parent_realize(dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - /* This creates distributor and main CPU interface (s->cpuiomem[0]) */ - gic_init_irqs_and_mmio(s, gic_set_irq, gic_ops); - - /* 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. - */ - 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); - sysbus_init_mmio(sbd, &s->cpuiomem[i+1]); - } -} - -static void arm_gic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - ARMGICClass *agc = ARM_GIC_CLASS(klass); - - agc->parent_realize = dc->realize; - dc->realize = arm_gic_realize; -} - -static const TypeInfo arm_gic_info = { - .name = TYPE_ARM_GIC, - .parent = TYPE_ARM_GIC_COMMON, - .instance_size = sizeof(GICState), - .class_init = arm_gic_class_init, - .class_size = sizeof(ARMGICClass), -}; - -static void arm_gic_register_types(void) -{ - type_register_static(&arm_gic_info); -} - -type_init(arm_gic_register_types) diff --git a/qemu/hw/intc/arm_gic_common.c b/qemu/hw/intc/arm_gic_common.c deleted file mode 100644 index 0a1f56af1..000000000 --- a/qemu/hw/intc/arm_gic_common.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - * ARM GIC support - common bits of emulated and KVM kernel model - * - * Copyright (c) 2012 Linaro Limited - * Written 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 . - */ - -#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) -{ - GICState *s = (GICState *)opaque; - ARMGICCommonClass *c = ARM_GIC_COMMON_GET_CLASS(s); - - if (c->pre_save) { - c->pre_save(s); - } -} - -static int gic_post_load(void *opaque, int version_id) -{ - GICState *s = (GICState *)opaque; - ARMGICCommonClass *c = ARM_GIC_COMMON_GET_CLASS(s); - - if (c->post_load) { - c->post_load(s); - } - return 0; -} - -static const VMStateDescription vmstate_gic_irq_state = { - .name = "arm_gic_irq_state", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(enabled, gic_irq_state), - VMSTATE_UINT8(pending, gic_irq_state), - VMSTATE_UINT8(active, gic_irq_state), - VMSTATE_UINT8(level, gic_irq_state), - VMSTATE_BOOL(model, gic_irq_state), - VMSTATE_BOOL(edge_trigger, gic_irq_state), - VMSTATE_UINT8(group, gic_irq_state), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_gic = { - .name = "arm_gic", - .version_id = 12, - .minimum_version_id = 12, - .pre_save = gic_pre_save, - .post_load = gic_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(ctlr, GICState), - VMSTATE_UINT32_ARRAY(cpu_ctlr, GICState, GIC_NCPU), - VMSTATE_STRUCT_ARRAY(irq_state, GICState, GIC_MAXIRQ, 1, - vmstate_gic_irq_state, gic_irq_state), - 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_UINT8_2DARRAY(sgi_pending, GICState, GIC_NR_SGIS, GIC_NCPU), - VMSTATE_UINT16_ARRAY(priority_mask, 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); - int num_irq = s->num_irq; - - if (s->num_cpu > GIC_NCPU) { - error_setg(errp, "requested %u CPUs exceeds GIC maximum %d", - s->num_cpu, GIC_NCPU); - return; - } - s->num_irq += GIC_BASE_IRQ; - if (s->num_irq > GIC_MAXIRQ) { - error_setg(errp, - "requested %u interrupt lines exceeds GIC maximum %d", - num_irq, GIC_MAXIRQ); - return; - } - /* ITLinesNumber is represented as (N / 32) - 1 (see - * gic_dist_readb) so this is an implementation imposed - * restriction, not an architectural one: - */ - if (s->num_irq < 32 || (s->num_irq % 32)) { - error_setg(errp, - "%d interrupt lines unsupported: not divisible by 32", - num_irq); - return; - } - - if (s->security_extn && - (s->revision == REV_11MPCORE || s->revision == REV_NVIC)) { - error_setg(errp, "this GIC revision does not implement " - "the security extensions"); - return; - } -} - -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] = resetprio; - } - s->current_pending[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] = resetprio; - } - for (j = 0; j < GIC_NR_SGIS; j++) { - s->sgi_pending[j][i] = 0; - } - } - for (i = 0; i < GIC_NR_SGIS; i++) { - GIC_SET_ENABLED(i, ALL_CPU_MASK); - GIC_SET_EDGE_TRIGGER(i); - } - - for (i = 0; i < ARRAY_SIZE(s->priority2); i++) { - s->priority2[i] = resetprio; - } - - for (i = 0; i < GIC_MAXIRQ; i++) { - /* For uniprocessor GICs all interrupts always target the sole CPU */ - if (s->num_cpu == 1) { - s->irq_target[i] = 1; - } else { - 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), - /* Revision can be 1 or 2 for GIC architecture specification - * versions 1 or 2, or 0 to indicate the legacy 11MPCore GIC. - * (Internally, 0xffffffff also indicates "not a GIC but an NVIC".) - */ - DEFINE_PROP_UINT32("revision", GICState, revision, 1), - /* True if the GIC should implement the security extensions */ - DEFINE_PROP_BOOL("has-security-extensions", GICState, security_extn, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -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 = { - .name = TYPE_ARM_GIC_COMMON, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(GICState), - .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) -{ - type_register_static(&arm_gic_common_type); -} - -type_init(register_types) diff --git a/qemu/hw/intc/arm_gic_kvm.c b/qemu/hw/intc/arm_gic_kvm.c deleted file mode 100644 index bc85ab769..000000000 --- a/qemu/hw/intc/arm_gic_kvm.c +++ /dev/null @@ -1,607 +0,0 @@ -/* - * ARM Generic Interrupt Controller using KVM in-kernel support - * - * Copyright (c) 2012 Linaro Limited - * Written by Peter Maydell - * Save/Restore logic added by Christoffer Dall. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "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 - -#ifdef DEBUG_GIC_KVM -static const int debug_gic_kvm = 1; -#else -static const int debug_gic_kvm = 0; -#endif - -#define DPRINTF(fmt, ...) do { \ - if (debug_gic_kvm) { \ - printf("arm_gic: " fmt , ## __VA_ARGS__); \ - } \ - } while (0) - -#define TYPE_KVM_ARM_GIC "kvm-arm-gic" -#define KVM_ARM_GIC(obj) \ - OBJECT_CHECK(GICState, (obj), TYPE_KVM_ARM_GIC) -#define KVM_ARM_GIC_CLASS(klass) \ - OBJECT_CLASS_CHECK(KVMARMGICClass, (klass), TYPE_KVM_ARM_GIC) -#define KVM_ARM_GIC_GET_CLASS(obj) \ - OBJECT_GET_CLASS(KVMARMGICClass, (obj), TYPE_KVM_ARM_GIC) - -typedef struct KVMARMGICClass { - ARMGICCommonClass parent_class; - DeviceRealize parent_realize; - void (*parent_reset)(DeviceState *dev); -} KVMARMGICClass; - -void kvm_arm_gic_set_irq(uint32_t num_irq, int irq, int level) -{ - /* Meaning of the 'irq' parameter: - * [0..N-1] : external interrupts - * [N..N+31] : PPI (internal) interrupts for CPU 0 - * [N+32..N+63] : PPI (internal interrupts for CPU 1 - * ... - * Convert this to the kernel's desired encoding, which - * has separate fields in the irq number for type, - * CPU number and interrupt number. - */ - int kvm_irq, irqtype, cpu; - - 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. - */ - irqtype = KVM_ARM_IRQ_TYPE_SPI; - cpu = 0; - irq += GIC_INTERNAL; - } else { - /* Internal interrupt: decode into (cpu, interrupt id) */ - irqtype = KVM_ARM_IRQ_TYPE_PPI; - irq -= (num_irq - GIC_INTERNAL); - cpu = irq / GIC_INTERNAL; - irq %= GIC_INTERNAL; - } - kvm_irq = (irqtype << KVM_ARM_IRQ_TYPE_SHIFT) - | (cpu << KVM_ARM_IRQ_VCPU_SHIFT) | irq; - - kvm_set_irq(kvm_state, kvm_irq, !!level); -} - -static void kvm_arm_gicv2_set_irq(void *opaque, int irq, int level) -{ - GICState *s = (GICState *)opaque; - - kvm_arm_gic_set_irq(s->num_irq, irq, level); -} - -static bool kvm_arm_gic_can_save_restore(GICState *s) -{ - 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_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_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) \ - for (_ctr = 0; _ctr < ((_max_irq) / (32 / (_field_width))); _ctr++) - -/* - * Translate from the in-kernel field for an IRQ value to/from the qemu - * representation. - */ -typedef void (*vgic_translate_fn)(GICState *s, int irq, int cpu, - uint32_t *field, bool to_kernel); - -/* synthetic translate function used for clear/set registers to completely - * clear a setting using a clear-register before setting the remaining bits - * using a set-register */ -static void translate_clear(GICState *s, int irq, int cpu, - uint32_t *field, bool to_kernel) -{ - if (to_kernel) { - *field = ~0; - } else { - /* does not make sense: qemu model doesn't use set/clear regs */ - abort(); - } -} - -static void translate_group(GICState *s, int irq, int cpu, - uint32_t *field, bool to_kernel) -{ - int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; - - if (to_kernel) { - *field = GIC_TEST_GROUP(irq, cm); - } else { - if (*field & 1) { - GIC_SET_GROUP(irq, cm); - } - } -} - -static void translate_enabled(GICState *s, int irq, int cpu, - uint32_t *field, bool to_kernel) -{ - int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; - - if (to_kernel) { - *field = GIC_TEST_ENABLED(irq, cm); - } else { - if (*field & 1) { - GIC_SET_ENABLED(irq, cm); - } - } -} - -static void translate_pending(GICState *s, int irq, int cpu, - uint32_t *field, bool to_kernel) -{ - int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; - - if (to_kernel) { - *field = gic_test_pending(s, irq, cm); - } else { - if (*field & 1) { - GIC_SET_PENDING(irq, cm); - /* TODO: Capture is level-line is held high in the kernel */ - } - } -} - -static void translate_active(GICState *s, int irq, int cpu, - uint32_t *field, bool to_kernel) -{ - int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; - - if (to_kernel) { - *field = GIC_TEST_ACTIVE(irq, cm); - } else { - if (*field & 1) { - GIC_SET_ACTIVE(irq, cm); - } - } -} - -static void translate_trigger(GICState *s, int irq, int cpu, - uint32_t *field, bool to_kernel) -{ - if (to_kernel) { - *field = (GIC_TEST_EDGE_TRIGGER(irq)) ? 0x2 : 0x0; - } else { - if (*field & 0x2) { - GIC_SET_EDGE_TRIGGER(irq); - } - } -} - -static void translate_priority(GICState *s, int irq, int cpu, - uint32_t *field, bool to_kernel) -{ - if (to_kernel) { - *field = GIC_GET_PRIORITY(irq, cpu) & 0xff; - } else { - gic_set_priority(s, cpu, irq, *field & 0xff, MEMTXATTRS_UNSPECIFIED); - } -} - -static void translate_targets(GICState *s, int irq, int cpu, - uint32_t *field, bool to_kernel) -{ - if (to_kernel) { - *field = s->irq_target[irq] & 0xff; - } else { - s->irq_target[irq] = *field & 0xff; - } -} - -static void translate_sgisource(GICState *s, int irq, int cpu, - uint32_t *field, bool to_kernel) -{ - if (to_kernel) { - *field = s->sgi_pending[irq][cpu] & 0xff; - } else { - s->sgi_pending[irq][cpu] = *field & 0xff; - } -} - -/* Read a register group from the kernel VGIC */ -static void kvm_dist_get(GICState *s, uint32_t offset, int width, - int maxirq, vgic_translate_fn translate_fn) -{ - uint32_t reg; - int i; - int j; - int irq; - int cpu; - int regsz = 32 / width; /* irqs per kernel register */ - uint32_t field; - - for_each_irq_reg(i, maxirq, width) { - irq = i * regsz; - cpu = 0; - while ((cpu < s->num_cpu && irq < GIC_INTERNAL) || cpu == 0) { - kvm_gicd_access(s, offset, cpu, ®, false); - for (j = 0; j < regsz; j++) { - field = extract32(reg, j * width, width); - translate_fn(s, irq + j, cpu, &field, false); - } - - cpu++; - } - offset += 4; - } -} - -/* Write a register group to the kernel VGIC */ -static void kvm_dist_put(GICState *s, uint32_t offset, int width, - int maxirq, vgic_translate_fn translate_fn) -{ - uint32_t reg; - int i; - int j; - int irq; - int cpu; - int regsz = 32 / width; /* irqs per kernel register */ - uint32_t field; - - for_each_irq_reg(i, maxirq, width) { - irq = i * regsz; - cpu = 0; - while ((cpu < s->num_cpu && irq < GIC_INTERNAL) || cpu == 0) { - reg = 0; - for (j = 0; j < regsz; j++) { - translate_fn(s, irq + j, cpu, &field, true); - reg = deposit32(reg, j * width, width, field); - } - kvm_gicd_access(s, offset, cpu, ®, true); - - cpu++; - } - offset += 4; - } -} - -static void kvm_arm_gic_put(GICState *s) -{ - uint32_t reg; - int i; - int cpu; - int num_cpu; - int num_irq; - - /* 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 */ - - /***************************************************************** - * Distributor State - */ - - /* s->ctlr -> GICD_CTLR */ - reg = s->ctlr; - kvm_gicd_access(s, 0x0, 0, ®, true); - - /* Sanity checking on GICD_TYPER and s->num_irq, s->num_cpu */ - kvm_gicd_access(s, 0x4, 0, ®, false); - num_irq = ((reg & 0x1f) + 1) * 32; - num_cpu = ((reg & 0xe0) >> 5) + 1; - - if (num_irq < s->num_irq) { - fprintf(stderr, "Restoring %u IRQs, but kernel supports max %d\n", - s->num_irq, num_irq); - abort(); - } else if (num_cpu != s->num_cpu) { - fprintf(stderr, "Restoring %u CPU interfaces, kernel only has %d\n", - s->num_cpu, num_cpu); - /* Did we not create the VCPUs in the kernel yet? */ - abort(); - } - - /* TODO: Consider checking compatibility with the IIDR ? */ - - /* irq_state[n].enabled -> GICD_ISENABLERn */ - kvm_dist_put(s, 0x180, 1, s->num_irq, translate_clear); - kvm_dist_put(s, 0x100, 1, s->num_irq, translate_enabled); - - /* irq_state[n].group -> GICD_IGROUPRn */ - kvm_dist_put(s, 0x80, 1, s->num_irq, translate_group); - - /* s->irq_target[irq] -> GICD_ITARGETSRn - * (restore targets before pending to ensure the pending state is set on - * the appropriate CPU interfaces in the kernel) */ - kvm_dist_put(s, 0x800, 8, s->num_irq, translate_targets); - - /* irq_state[n].trigger -> GICD_ICFGRn - * (restore configuration registers before pending IRQs so we treat - * level/edge correctly) */ - kvm_dist_put(s, 0xc00, 2, s->num_irq, translate_trigger); - - /* irq_state[n].pending + irq_state[n].level -> GICD_ISPENDRn */ - kvm_dist_put(s, 0x280, 1, s->num_irq, translate_clear); - kvm_dist_put(s, 0x200, 1, s->num_irq, translate_pending); - - /* irq_state[n].active -> GICD_ISACTIVERn */ - kvm_dist_put(s, 0x380, 1, s->num_irq, translate_clear); - kvm_dist_put(s, 0x300, 1, s->num_irq, translate_active); - - - /* s->priorityX[irq] -> ICD_IPRIORITYRn */ - kvm_dist_put(s, 0x400, 8, s->num_irq, translate_priority); - - /* s->sgi_pending -> ICD_CPENDSGIRn */ - kvm_dist_put(s, 0xf10, 8, GIC_NR_SGIS, translate_clear); - kvm_dist_put(s, 0xf20, 8, GIC_NR_SGIS, translate_sgisource); - - - /***************************************************************** - * CPU Interface(s) State - */ - - for (cpu = 0; cpu < s->num_cpu; cpu++) { - /* s->cpu_ctlr[cpu] -> GICC_CTLR */ - reg = s->cpu_ctlr[cpu]; - kvm_gicc_access(s, 0x00, cpu, ®, true); - - /* s->priority_mask[cpu] -> GICC_PMR */ - reg = (s->priority_mask[cpu] & 0xff); - kvm_gicc_access(s, 0x04, cpu, ®, true); - - /* s->bpr[cpu] -> GICC_BPR */ - reg = (s->bpr[cpu] & 0x7); - kvm_gicc_access(s, 0x08, cpu, ®, true); - - /* s->abpr[cpu] -> GICC_ABPR */ - reg = (s->abpr[cpu] & 0x7); - kvm_gicc_access(s, 0x1c, cpu, ®, true); - - /* s->apr[n][cpu] -> GICC_APRn */ - for (i = 0; i < 4; i++) { - reg = s->apr[i][cpu]; - kvm_gicc_access(s, 0xd0 + i * 4, cpu, ®, true); - } - } -} - -static void kvm_arm_gic_get(GICState *s) -{ - uint32_t reg; - int i; - int cpu; - - /***************************************************************** - * Distributor State - */ - - /* GICD_CTLR -> s->ctlr */ - kvm_gicd_access(s, 0x0, 0, ®, false); - s->ctlr = reg; - - /* Sanity checking on GICD_TYPER -> s->num_irq, s->num_cpu */ - kvm_gicd_access(s, 0x4, 0, ®, false); - s->num_irq = ((reg & 0x1f) + 1) * 32; - s->num_cpu = ((reg & 0xe0) >> 5) + 1; - - if (s->num_irq > GIC_MAXIRQ) { - fprintf(stderr, "Too many IRQs reported from the kernel: %d\n", - s->num_irq); - abort(); - } - - /* GICD_IIDR -> ? */ - kvm_gicd_access(s, 0x8, 0, ®, false); - - /* Clear all the IRQ settings */ - for (i = 0; i < s->num_irq; i++) { - memset(&s->irq_state[i], 0, sizeof(s->irq_state[0])); - } - - /* GICD_IGROUPRn -> irq_state[n].group */ - kvm_dist_get(s, 0x80, 1, s->num_irq, translate_group); - - /* GICD_ISENABLERn -> irq_state[n].enabled */ - kvm_dist_get(s, 0x100, 1, s->num_irq, translate_enabled); - - /* GICD_ISPENDRn -> irq_state[n].pending + irq_state[n].level */ - kvm_dist_get(s, 0x200, 1, s->num_irq, translate_pending); - - /* GICD_ISACTIVERn -> irq_state[n].active */ - kvm_dist_get(s, 0x300, 1, s->num_irq, translate_active); - - /* GICD_ICFRn -> irq_state[n].trigger */ - kvm_dist_get(s, 0xc00, 2, s->num_irq, translate_trigger); - - /* GICD_IPRIORITYRn -> s->priorityX[irq] */ - kvm_dist_get(s, 0x400, 8, s->num_irq, translate_priority); - - /* GICD_ITARGETSRn -> s->irq_target[irq] */ - kvm_dist_get(s, 0x800, 8, s->num_irq, translate_targets); - - /* GICD_CPENDSGIRn -> s->sgi_pending */ - kvm_dist_get(s, 0xf10, 8, GIC_NR_SGIS, translate_sgisource); - - - /***************************************************************** - * CPU Interface(s) State - */ - - for (cpu = 0; cpu < s->num_cpu; cpu++) { - /* GICC_CTLR -> s->cpu_ctlr[cpu] */ - kvm_gicc_access(s, 0x00, cpu, ®, false); - s->cpu_ctlr[cpu] = reg; - - /* GICC_PMR -> s->priority_mask[cpu] */ - kvm_gicc_access(s, 0x04, cpu, ®, false); - s->priority_mask[cpu] = (reg & 0xff); - - /* GICC_BPR -> s->bpr[cpu] */ - kvm_gicc_access(s, 0x08, cpu, ®, false); - s->bpr[cpu] = (reg & 0x7); - - /* GICC_ABPR -> s->abpr[cpu] */ - kvm_gicc_access(s, 0x1c, cpu, ®, false); - s->abpr[cpu] = (reg & 0x7); - - /* GICC_APRn -> s->apr[n][cpu] */ - for (i = 0; i < 4; i++) { - kvm_gicc_access(s, 0xd0 + i * 4, cpu, ®, false); - s->apr[i][cpu] = reg; - } - } -} - -static void kvm_arm_gic_reset(DeviceState *dev) -{ - GICState *s = ARM_GIC_COMMON(dev); - KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s); - - kgc->parent_reset(dev); - - 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); - KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s); - Error *local_err = NULL; - int ret; - - 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 VGIC does not implement the " - "security extensions"); - return; - } - - 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); - } - - /* 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; - } - - /* Distributor */ - kvm_arm_register_device(&s->iomem, - (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT) - | KVM_VGIC_V2_ADDR_TYPE_DIST, - KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V2_ADDR_TYPE_DIST, - s->dev_fd); - /* CPU interface for current core. Unlike arm_gic, we don't - * provide the "interface for core #N" memory regions, because - * cores with a VGIC don't have those. - */ - 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) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - ARMGICCommonClass *agcc = ARM_GIC_COMMON_CLASS(klass); - KVMARMGICClass *kgc = KVM_ARM_GIC_CLASS(klass); - - agcc->pre_save = kvm_arm_gic_get; - agcc->post_load = kvm_arm_gic_put; - kgc->parent_realize = dc->realize; - kgc->parent_reset = dc->reset; - dc->realize = kvm_arm_gic_realize; - dc->reset = kvm_arm_gic_reset; -} - -static const TypeInfo kvm_arm_gic_info = { - .name = TYPE_KVM_ARM_GIC, - .parent = TYPE_ARM_GIC_COMMON, - .instance_size = sizeof(GICState), - .class_init = kvm_arm_gic_class_init, - .class_size = sizeof(KVMARMGICClass), -}; - -static void kvm_arm_gic_register_types(void) -{ - type_register_static(&kvm_arm_gic_info); -} - -type_init(kvm_arm_gic_register_types) diff --git a/qemu/hw/intc/arm_gicv2m.c b/qemu/hw/intc/arm_gicv2m.c deleted file mode 100644 index e8b5177dc..000000000 --- a/qemu/hw/intc/arm_gicv2m.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * GICv2m extension for MSI/MSI-x support with a GICv2-based system - * - * Copyright (C) 2015 Linaro, All rights reserved. - * - * Author: Christoffer Dall - * - * 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 . - */ - -/* This file implements an emulated GICv2m widget as described in the ARM - * Server Base System Architecture (SBSA) specification Version 2.2 - * (ARM-DEN-0029 v2.2) pages 35-39 without any optional implementation defined - * 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" - -#define TYPE_ARM_GICV2M "arm-gicv2m" -#define ARM_GICV2M(obj) OBJECT_CHECK(ARMGICv2mState, (obj), TYPE_ARM_GICV2M) - -#define GICV2M_NUM_SPI_MAX 128 - -#define V2M_MSI_TYPER 0x008 -#define V2M_MSI_SETSPI_NS 0x040 -#define V2M_MSI_IIDR 0xFCC -#define V2M_IIDR0 0xFD0 -#define V2M_IIDR11 0xFFC - -#define PRODUCT_ID_QEMU 0x51 /* ASCII code Q */ - -typedef struct ARMGICv2mState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq spi[GICV2M_NUM_SPI_MAX]; - - uint32_t base_spi; - uint32_t num_spi; -} ARMGICv2mState; - -static void gicv2m_set_irq(void *opaque, int irq) -{ - ARMGICv2mState *s = (ARMGICv2mState *)opaque; - - qemu_irq_pulse(s->spi[irq]); -} - -static uint64_t gicv2m_read(void *opaque, hwaddr offset, - unsigned size) -{ - ARMGICv2mState *s = (ARMGICv2mState *)opaque; - uint32_t val; - - if (size != 4) { - qemu_log_mask(LOG_GUEST_ERROR, "gicv2m_read: bad size %u\n", size); - return 0; - } - - switch (offset) { - case V2M_MSI_TYPER: - val = (s->base_spi + 32) << 16; - val |= s->num_spi; - return val; - case V2M_MSI_IIDR: - /* We don't have any valid implementor so we leave that field as zero - * and we return 0 in the arch revision as per the spec. - */ - return (PRODUCT_ID_QEMU << 20); - case V2M_IIDR0 ... V2M_IIDR11: - /* We do not implement any optional identification registers and the - * mandatory MSI_PIDR2 register reads as 0x0, so we capture all - * implementation defined registers here. - */ - return 0; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "gicv2m_read: Bad offset %x\n", (int)offset); - return 0; - } -} - -static void gicv2m_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - ARMGICv2mState *s = (ARMGICv2mState *)opaque; - - if (size != 2 && size != 4) { - qemu_log_mask(LOG_GUEST_ERROR, "gicv2m_write: bad size %u\n", size); - return; - } - - switch (offset) { - case V2M_MSI_SETSPI_NS: { - int spi; - - spi = (value & 0x3ff) - (s->base_spi + 32); - if (spi >= 0 && spi < s->num_spi) { - gicv2m_set_irq(s, spi); - } - return; - } - default: - qemu_log_mask(LOG_GUEST_ERROR, - "gicv2m_write: Bad offset %x\n", (int)offset); - } -} - -static const MemoryRegionOps gicv2m_ops = { - .read = gicv2m_read, - .write = gicv2m_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void gicv2m_realize(DeviceState *dev, Error **errp) -{ - ARMGICv2mState *s = ARM_GICV2M(dev); - int i; - - if (s->num_spi > GICV2M_NUM_SPI_MAX) { - error_setg(errp, - "requested %u SPIs exceeds GICv2m frame maximum %d", - s->num_spi, GICV2M_NUM_SPI_MAX); - return; - } - - if (s->base_spi + 32 > 1020 - s->num_spi) { - error_setg(errp, - "requested base SPI %u+%u exceeds max. number 1020", - s->base_spi + 32, s->num_spi); - return; - } - - for (i = 0; i < s->num_spi; i++) { - sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->spi[i]); - } - - msi_nonbroken = true; - kvm_gsi_direct_mapping = true; - kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled(); -} - -static void gicv2m_init(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - ARMGICv2mState *s = ARM_GICV2M(obj); - - memory_region_init_io(&s->iomem, OBJECT(s), &gicv2m_ops, s, - "gicv2m", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); -} - -static Property gicv2m_properties[] = { - DEFINE_PROP_UINT32("base-spi", ARMGICv2mState, base_spi, 0), - DEFINE_PROP_UINT32("num-spi", ARMGICv2mState, num_spi, 64), - DEFINE_PROP_END_OF_LIST(), -}; - -static void gicv2m_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->props = gicv2m_properties; - dc->realize = gicv2m_realize; -} - -static const TypeInfo gicv2m_info = { - .name = TYPE_ARM_GICV2M, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ARMGICv2mState), - .instance_init = gicv2m_init, - .class_init = gicv2m_class_init, -}; - -static void gicv2m_register_types(void) -{ - type_register_static(&gicv2m_info); -} - -type_init(gicv2m_register_types) diff --git a/qemu/hw/intc/arm_gicv3_common.c b/qemu/hw/intc/arm_gicv3_common.c deleted file mode 100644 index b9d3824f2..000000000 --- a/qemu/hw/intc/arm_gicv3_common.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * 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 . - */ - -#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 deleted file mode 100644 index acc173004..000000000 --- a/qemu/hw/intc/arm_gicv3_kvm.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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 . - */ - -#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 deleted file mode 100644 index 669e82adf..000000000 --- a/qemu/hw/intc/armv7m_nvic.c +++ /dev/null @@ -1,581 +0,0 @@ -/* - * ARM Nested Vectored Interrupt Controller - * - * Copyright (c) 2006-2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - * - * The ARMv7M System controller is fairly tightly tied in with the - * 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" -#include "exec/address-spaces.h" -#include "gic_internal.h" - -typedef struct { - GICState gic; - struct { - uint32_t control; - uint32_t reload; - int64_t tick; - QEMUTimer *timer; - } systick; - MemoryRegion sysregmem; - MemoryRegion gic_iomem_alias; - MemoryRegion container; - uint32_t num_irq; - qemu_irq sysresetreq; -} nvic_state; - -#define TYPE_NVIC "armv7m_nvic" -/** - * NVICClass: - * @parent_reset: the parent class' reset handler. - * - * A model of the v7M NVIC and System Controller - */ -typedef struct NVICClass { - /*< private >*/ - ARMGICClass parent_class; - /*< public >*/ - DeviceRealize parent_realize; - void (*parent_reset)(DeviceState *dev); -} NVICClass; - -#define NVIC_CLASS(klass) \ - OBJECT_CLASS_CHECK(NVICClass, (klass), TYPE_NVIC) -#define NVIC_GET_CLASS(obj) \ - OBJECT_GET_CLASS(NVICClass, (obj), TYPE_NVIC) -#define NVIC(obj) \ - OBJECT_CHECK(nvic_state, (obj), TYPE_NVIC) - -static const uint8_t nvic_id[] = { - 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 -}; - -/* qemu timers run at 1GHz. We want something closer to 1MHz. */ -#define SYSTICK_SCALE 1000ULL - -#define SYSTICK_ENABLE (1 << 0) -#define SYSTICK_TICKINT (1 << 1) -#define SYSTICK_CLKSOURCE (1 << 2) -#define SYSTICK_COUNTFLAG (1 << 16) - -int system_clock_scale; - -/* Conversion factor from qemu timer to SysTick frequencies. */ -static inline int64_t systick_scale(nvic_state *s) -{ - if (s->systick.control & SYSTICK_CLKSOURCE) - return system_clock_scale; - else - return 1000; -} - -static void systick_reload(nvic_state *s, int reset) -{ - /* The Cortex-M3 Devices Generic User Guide says that "When the - * ENABLE bit is set to 1, the counter loads the RELOAD value from the - * SYST RVR register and then counts down". So, we need to check the - * ENABLE bit before reloading the value. - */ - if ((s->systick.control & SYSTICK_ENABLE) == 0) { - return; - } - - if (reset) - s->systick.tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - s->systick.tick += (s->systick.reload + 1) * systick_scale(s); - timer_mod(s->systick.timer, s->systick.tick); -} - -static void systick_timer_tick(void * opaque) -{ - nvic_state *s = (nvic_state *)opaque; - s->systick.control |= SYSTICK_COUNTFLAG; - if (s->systick.control & SYSTICK_TICKINT) { - /* Trigger the interrupt. */ - armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK); - } - if (s->systick.reload == 0) { - s->systick.control &= ~SYSTICK_ENABLE; - } else { - systick_reload(s, 0); - } -} - -static void systick_reset(nvic_state *s) -{ - s->systick.control = 0; - s->systick.reload = 0; - s->systick.tick = 0; - timer_del(s->systick.timer); -} - -/* The external routines use the hardware vector numbering, ie. the first - IRQ is #16. The internal GIC routines use #32 as the first IRQ. */ -void armv7m_nvic_set_pending(void *opaque, int irq) -{ - nvic_state *s = (nvic_state *)opaque; - if (irq >= 16) - irq += 16; - gic_set_pending_private(&s->gic, 0, irq); -} - -/* Make pending IRQ active. */ -int armv7m_nvic_acknowledge_irq(void *opaque) -{ - nvic_state *s = (nvic_state *)opaque; - uint32_t irq; - - irq = gic_acknowledge_irq(&s->gic, 0, MEMTXATTRS_UNSPECIFIED); - if (irq == 1023) - hw_error("Interrupt but no vector\n"); - if (irq >= 32) - irq -= 16; - return irq; -} - -void armv7m_nvic_complete_irq(void *opaque, int irq) -{ - nvic_state *s = (nvic_state *)opaque; - if (irq >= 16) - irq += 16; - gic_complete_irq(&s->gic, 0, irq, MEMTXATTRS_UNSPECIFIED); -} - -static uint32_t nvic_readl(nvic_state *s, uint32_t offset) -{ - ARMCPU *cpu; - uint32_t val; - int irq; - - switch (offset) { - case 4: /* Interrupt Control Type. */ - return (s->num_irq / 32) - 1; - case 0x10: /* SysTick Control and Status. */ - val = s->systick.control; - s->systick.control &= ~SYSTICK_COUNTFLAG; - return val; - case 0x14: /* SysTick Reload Value. */ - return s->systick.reload; - case 0x18: /* SysTick Current Value. */ - { - int64_t t; - if ((s->systick.control & SYSTICK_ENABLE) == 0) - return 0; - t = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - if (t >= s->systick.tick) - return 0; - val = ((s->systick.tick - (t + 1)) / systick_scale(s)) + 1; - /* The interrupt in triggered when the timer reaches zero. - However the counter is not reloaded until the next clock - tick. This is a hack to return zero during the first tick. */ - if (val > s->systick.reload) - val = 0; - return val; - } - case 0x1c: /* SysTick Calibration Value. */ - return 10000; - case 0xd00: /* CPUID Base. */ - cpu = ARM_CPU(current_cpu); - return cpu->midr; - case 0xd04: /* Interrupt Control State. */ - /* VECTACTIVE */ - cpu = ARM_CPU(current_cpu); - val = cpu->env.v7m.exception; - if (val == 1023) { - val = 0; - } else if (val >= 32) { - val -= 16; - } - /* VECTPENDING */ - if (s->gic.current_pending[0] != 1023) - val |= (s->gic.current_pending[0] << 12); - /* 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) - val |= (1 << 26); - /* PENDSVSET */ - if (s->gic.irq_state[ARMV7M_EXCP_PENDSV].pending) - val |= (1 << 28); - /* NMIPENDSET */ - if (s->gic.irq_state[ARMV7M_EXCP_NMI].pending) - val |= (1 << 31); - return val; - case 0xd08: /* Vector Table Offset. */ - cpu = ARM_CPU(current_cpu); - return cpu->env.v7m.vecbase; - case 0xd0c: /* Application Interrupt/Reset Control. */ - return 0xfa050000; - case 0xd10: /* System Control. */ - /* TODO: Implement SLEEPONEXIT. */ - return 0; - case 0xd14: /* Configuration Control. */ - /* TODO: Implement Configuration Control bits. */ - return 0; - case 0xd24: /* System Handler Status. */ - val = 0; - if (s->gic.irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0); - if (s->gic.irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1); - if (s->gic.irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3); - if (s->gic.irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7); - if (s->gic.irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8); - if (s->gic.irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10); - if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11); - if (s->gic.irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12); - if (s->gic.irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13); - if (s->gic.irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14); - if (s->gic.irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15); - if (s->gic.irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16); - if (s->gic.irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17); - if (s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18); - return val; - case 0xd28: /* Configurable Fault Status. */ - /* TODO: Implement Fault Status. */ - qemu_log_mask(LOG_UNIMP, "Configurable Fault Status unimplemented\n"); - return 0; - case 0xd2c: /* Hard Fault Status. */ - case 0xd30: /* Debug Fault Status. */ - case 0xd34: /* Mem Manage Address. */ - case 0xd38: /* Bus Fault Address. */ - case 0xd3c: /* Aux Fault Status. */ - /* TODO: Implement fault status registers. */ - qemu_log_mask(LOG_UNIMP, "Fault status registers unimplemented\n"); - return 0; - case 0xd40: /* PFR0. */ - return 0x00000030; - case 0xd44: /* PRF1. */ - return 0x00000200; - case 0xd48: /* DFR0. */ - return 0x00100000; - case 0xd4c: /* AFR0. */ - return 0x00000000; - case 0xd50: /* MMFR0. */ - return 0x00000030; - case 0xd54: /* MMFR1. */ - return 0x00000000; - case 0xd58: /* MMFR2. */ - return 0x00000000; - case 0xd5c: /* MMFR3. */ - return 0x00000000; - case 0xd60: /* ISAR0. */ - return 0x01141110; - case 0xd64: /* ISAR1. */ - return 0x02111000; - case 0xd68: /* ISAR2. */ - return 0x21112231; - case 0xd6c: /* ISAR3. */ - return 0x01111110; - case 0xd70: /* ISAR4. */ - return 0x01310102; - /* TODO: Implement debug registers. */ - default: - qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad read offset 0x%x\n", offset); - return 0; - } -} - -static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value) -{ - ARMCPU *cpu; - uint32_t oldval; - switch (offset) { - case 0x10: /* SysTick Control and Status. */ - oldval = s->systick.control; - s->systick.control &= 0xfffffff8; - s->systick.control |= value & 7; - if ((oldval ^ value) & SYSTICK_ENABLE) { - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - if (value & SYSTICK_ENABLE) { - if (s->systick.tick) { - s->systick.tick += now; - timer_mod(s->systick.timer, s->systick.tick); - } else { - systick_reload(s, 1); - } - } else { - timer_del(s->systick.timer); - s->systick.tick -= now; - if (s->systick.tick < 0) - s->systick.tick = 0; - } - } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) { - /* This is a hack. Force the timer to be reloaded - when the reference clock is changed. */ - systick_reload(s, 1); - } - break; - case 0x14: /* SysTick Reload Value. */ - s->systick.reload = value; - break; - case 0x18: /* SysTick Current Value. Writes reload the timer. */ - systick_reload(s, 1); - s->systick.control &= ~SYSTICK_COUNTFLAG; - break; - case 0xd04: /* Interrupt Control State. */ - if (value & (1 << 31)) { - armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI); - } - if (value & (1 << 28)) { - armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV); - } else if (value & (1 << 27)) { - s->gic.irq_state[ARMV7M_EXCP_PENDSV].pending = 0; - gic_update(&s->gic); - } - if (value & (1 << 26)) { - armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK); - } else if (value & (1 << 25)) { - s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending = 0; - gic_update(&s->gic); - } - break; - case 0xd08: /* Vector Table Offset. */ - cpu = ARM_CPU(current_cpu); - cpu->env.v7m.vecbase = value & 0xffffff80; - 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 & 1) { - qemu_log_mask(LOG_UNIMP, "AIRCR system reset unimplemented\n"); - } - if (value & 0x700) { - qemu_log_mask(LOG_UNIMP, "PRIGROUP unimplemented\n"); - } - } - break; - case 0xd10: /* System Control. */ - case 0xd14: /* Configuration Control. */ - /* TODO: Implement control registers. */ - qemu_log_mask(LOG_UNIMP, "NVIC: SCR and CCR unimplemented\n"); - break; - case 0xd24: /* System Handler Control. */ - /* TODO: Real hardware allows you to set/clear the active bits - under some circumstances. We don't implement this. */ - s->gic.irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0; - s->gic.irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0; - s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0; - break; - case 0xd28: /* Configurable Fault Status. */ - case 0xd2c: /* Hard Fault Status. */ - case 0xd30: /* Debug Fault Status. */ - case 0xd34: /* Mem Manage Address. */ - case 0xd38: /* Bus Fault Address. */ - case 0xd3c: /* Aux Fault Status. */ - qemu_log_mask(LOG_UNIMP, - "NVIC: fault status registers unimplemented\n"); - break; - case 0xf00: /* Software Triggered Interrupt Register */ - if ((value & 0x1ff) < s->num_irq) { - gic_set_pending_private(&s->gic, 0, value & 0x1ff); - } - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "NVIC: Bad write offset 0x%x\n", offset); - } -} - -static uint64_t nvic_sysreg_read(void *opaque, hwaddr addr, - unsigned size) -{ - nvic_state *s = (nvic_state *)opaque; - uint32_t offset = addr; - int i; - uint32_t val; - - switch (offset) { - case 0xd18 ... 0xd23: /* System Handler Priority. */ - val = 0; - for (i = 0; i < size; i++) { - val |= s->gic.priority1[(offset - 0xd14) + i][0] << (i * 8); - } - return val; - case 0xfe0 ... 0xfff: /* ID. */ - if (offset & 3) { - return 0; - } - return nvic_id[(offset - 0xfe0) >> 2]; - } - if (size == 4) { - return nvic_readl(s, offset); - } - qemu_log_mask(LOG_GUEST_ERROR, - "NVIC: Bad read of size %d at offset 0x%x\n", size, offset); - return 0; -} - -static void nvic_sysreg_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - nvic_state *s = (nvic_state *)opaque; - uint32_t offset = addr; - int i; - - switch (offset) { - case 0xd18 ... 0xd23: /* System Handler Priority. */ - for (i = 0; i < size; i++) { - s->gic.priority1[(offset - 0xd14) + i][0] = - (value >> (i * 8)) & 0xff; - } - gic_update(&s->gic); - return; - } - if (size == 4) { - nvic_writel(s, offset, value); - return; - } - qemu_log_mask(LOG_GUEST_ERROR, - "NVIC: Bad write of size %d at offset 0x%x\n", size, offset); -} - -static const MemoryRegionOps nvic_sysreg_ops = { - .read = nvic_sysreg_read, - .write = nvic_sysreg_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_nvic = { - .name = "armv7m_nvic", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(systick.control, nvic_state), - VMSTATE_UINT32(systick.reload, nvic_state), - VMSTATE_INT64(systick.tick, nvic_state), - VMSTATE_TIMER_PTR(systick.timer, nvic_state), - VMSTATE_END_OF_LIST() - } -}; - -static void armv7m_nvic_reset(DeviceState *dev) -{ - nvic_state *s = NVIC(dev); - NVICClass *nc = NVIC_GET_CLASS(s); - nc->parent_reset(dev); - /* Common GIC reset resets to disabled; the NVIC doesn't have - * per-CPU interfaces so mark our non-existent CPU interface - * as enabled by default, and with a priority mask which allows - * all interrupts through. - */ - s->gic.cpu_ctlr[0] = GICC_CTLR_EN_GRP0; - s->gic.priority_mask[0] = 0x100; - /* The NVIC as a whole is always enabled. */ - s->gic.ctlr = 1; - systick_reset(s); -} - -static void armv7m_nvic_realize(DeviceState *dev, Error **errp) -{ - nvic_state *s = NVIC(dev); - NVICClass *nc = NVIC_GET_CLASS(s); - Error *local_err = NULL; - - /* The NVIC always has only one CPU */ - s->gic.num_cpu = 1; - /* Tell the common code we're an NVIC */ - s->gic.revision = 0xffffffff; - s->num_irq = s->gic.num_irq; - nc->parent_realize(dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - gic_init_irqs_and_distributor(&s->gic); - /* The NVIC and system controller register area looks like this: - * 0..0xff : system control registers, including systick - * 0x100..0xcff : GIC-like registers - * 0xd00..0xfff : system control registers - * We use overlaying to put the GIC like registers - * over the top of the system control register region. - */ - memory_region_init(&s->container, OBJECT(s), "nvic", 0x1000); - /* The system register region goes at the bottom of the priority - * stack as it covers the whole page. - */ - memory_region_init_io(&s->sysregmem, OBJECT(s), &nvic_sysreg_ops, s, - "nvic_sysregs", 0x1000); - memory_region_add_subregion(&s->container, 0, &s->sysregmem); - /* Alias the GIC region so we can get only the section of it - * we need, and layer it on top of the system register region. - */ - memory_region_init_alias(&s->gic_iomem_alias, OBJECT(s), - "nvic-gic", &s->gic.iomem, - 0x100, 0xc00); - memory_region_add_subregion_overlap(&s->container, 0x100, - &s->gic_iomem_alias, 1); - /* Map the whole thing into system memory at the location required - * by the v7M architecture. - */ - memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->container); - s->systick.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, systick_timer_tick, s); -} - -static void armv7m_nvic_instance_init(Object *obj) -{ - /* We have a different default value for the num-irq property - * than our superclass. This function runs after qdev init - * has set the defaults from the Property array and before - * any user-specified property setting, so just modify the - * 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) -{ - NVICClass *nc = NVIC_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - nc->parent_reset = dc->reset; - nc->parent_realize = dc->realize; - dc->vmsd = &vmstate_nvic; - dc->reset = armv7m_nvic_reset; - dc->realize = armv7m_nvic_realize; -} - -static const TypeInfo armv7m_nvic_info = { - .name = TYPE_NVIC, - .parent = TYPE_ARM_GIC_COMMON, - .instance_init = armv7m_nvic_instance_init, - .instance_size = sizeof(nvic_state), - .class_init = armv7m_nvic_class_init, - .class_size = sizeof(NVICClass), -}; - -static void armv7m_nvic_register_types(void) -{ - type_register_static(&armv7m_nvic_info); -} - -type_init(armv7m_nvic_register_types) diff --git a/qemu/hw/intc/aspeed_vic.c b/qemu/hw/intc/aspeed_vic.c deleted file mode 100644 index 19a0ff748..000000000 --- a/qemu/hw/intc/aspeed_vic.c +++ /dev/null @@ -1,339 +0,0 @@ -/* - * ASPEED Interrupt Controller (New) - * - * Andrew Jeffery - * - * 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 -#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 deleted file mode 100644 index 80513b28f..000000000 --- a/qemu/hw/intc/bcm2835_ic.c +++ /dev/null @@ -1,237 +0,0 @@ -/* - * 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 deleted file mode 100644 index d0271810c..000000000 --- a/qemu/hw/intc/bcm2836_control.c +++ /dev/null @@ -1,304 +0,0 @@ -/* - * 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 deleted file mode 100644 index 48f947706..000000000 --- a/qemu/hw/intc/etraxfs_pic.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * QEMU ETRAX Interrupt Controller. - * - * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB. - * - * 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/sysbus.h" -#include "hw/hw.h" -//#include "pc.h" -//#include "etraxfs.h" - -#define D(x) - -#define R_RW_MASK 0 -#define R_R_VECT 1 -#define R_R_MASKED_VECT 2 -#define R_R_NMI 3 -#define R_R_GURU 4 -#define R_MAX 5 - -#define TYPE_ETRAX_FS_PIC "etraxfs,pic" -#define ETRAX_FS_PIC(obj) \ - OBJECT_CHECK(struct etrax_pic, (obj), TYPE_ETRAX_FS_PIC) - -struct etrax_pic -{ - SysBusDevice parent_obj; - - MemoryRegion mmio; - void *interrupt_vector; - qemu_irq parent_irq; - qemu_irq parent_nmi; - uint32_t regs[R_MAX]; -}; - -static void pic_update(struct etrax_pic *fs) -{ - uint32_t vector = 0; - int i; - - fs->regs[R_R_MASKED_VECT] = fs->regs[R_R_VECT] & fs->regs[R_RW_MASK]; - - /* The ETRAX interrupt controller signals interrupts to the core - through an interrupt request wire and an irq vector bus. If - multiple interrupts are simultaneously active it chooses vector - 0x30 and lets the sw choose the priorities. */ - if (fs->regs[R_R_MASKED_VECT]) { - uint32_t mv = fs->regs[R_R_MASKED_VECT]; - for (i = 0; i < 31; i++) { - if (mv & 1) { - vector = 0x31 + i; - /* Check for multiple interrupts. */ - if (mv > 1) - vector = 0x30; - break; - } - mv >>= 1; - } - } - - if (fs->interrupt_vector) { - /* hack alert: ptr property */ - *(uint32_t*)(fs->interrupt_vector) = vector; - } - qemu_set_irq(fs->parent_irq, !!vector); -} - -static uint64_t -pic_read(void *opaque, hwaddr addr, unsigned int size) -{ - struct etrax_pic *fs = opaque; - uint32_t rval; - - rval = fs->regs[addr >> 2]; - D(printf("%s %x=%x\n", __func__, addr, rval)); - return rval; -} - -static void pic_write(void *opaque, hwaddr addr, - uint64_t value, unsigned int size) -{ - struct etrax_pic *fs = opaque; - D(printf("%s addr=%x val=%x\n", __func__, addr, value)); - - if (addr == R_RW_MASK) { - fs->regs[R_RW_MASK] = value; - pic_update(fs); - } -} - -static const MemoryRegionOps pic_ops = { - .read = pic_read, - .write = pic_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static void nmi_handler(void *opaque, int irq, int level) -{ - struct etrax_pic *fs = (void *)opaque; - uint32_t mask; - - mask = 1 << irq; - if (level) - fs->regs[R_R_NMI] |= mask; - else - fs->regs[R_R_NMI] &= ~mask; - - qemu_set_irq(fs->parent_nmi, !!fs->regs[R_R_NMI]); -} - -static void irq_handler(void *opaque, int irq, int level) -{ - struct etrax_pic *fs = (void *)opaque; - - if (irq >= 30) { - nmi_handler(opaque, irq, level); - return; - } - - irq -= 1; - fs->regs[R_R_VECT] &= ~(1 << irq); - fs->regs[R_R_VECT] |= (!!level << irq); - pic_update(fs); -} - -static int etraxfs_pic_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - struct etrax_pic *s = ETRAX_FS_PIC(dev); - - qdev_init_gpio_in(dev, irq_handler, 32); - sysbus_init_irq(sbd, &s->parent_irq); - sysbus_init_irq(sbd, &s->parent_nmi); - - memory_region_init_io(&s->mmio, OBJECT(s), &pic_ops, s, - "etraxfs-pic", R_MAX * 4); - sysbus_init_mmio(sbd, &s->mmio); - return 0; -} - -static Property etraxfs_pic_properties[] = { - DEFINE_PROP_PTR("interrupt_vector", struct etrax_pic, interrupt_vector), - DEFINE_PROP_END_OF_LIST(), -}; - -static void etraxfs_pic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = etraxfs_pic_init; - dc->props = etraxfs_pic_properties; - /* - * Note: pointer property "interrupt_vector" may remain null, thus - * no need for dc->cannot_instantiate_with_device_add_yet = true; - */ -} - -static const TypeInfo etraxfs_pic_info = { - .name = TYPE_ETRAX_FS_PIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct etrax_pic), - .class_init = etraxfs_pic_class_init, -}; - -static void etraxfs_pic_register_types(void) -{ - type_register_static(&etraxfs_pic_info); -} - -type_init(etraxfs_pic_register_types) diff --git a/qemu/hw/intc/exynos4210_combiner.c b/qemu/hw/intc/exynos4210_combiner.c deleted file mode 100644 index dc0c90326..000000000 --- a/qemu/hw/intc/exynos4210_combiner.c +++ /dev/null @@ -1,459 +0,0 @@ -/* - * Samsung exynos4210 Interrupt Combiner - * - * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. - * All rights reserved. - * - * Evgeny Voevodin - * - * 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 . - */ - -/* - * Exynos4210 Combiner represents an OR gate for SOC's IRQ lines. It combines - * IRQ sources into groups and provides signal output to GIC from each group. It - * is driven by common mask and enable/disable logic. Take a note that not all - * IRQs are passed to GIC through Combiner. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" - -#include "hw/arm/exynos4210.h" - -//#define DEBUG_COMBINER - -#ifdef DEBUG_COMBINER -#define DPRINTF(fmt, ...) \ - do { fprintf(stdout, "COMBINER: [%s:%d] " fmt, __func__ , __LINE__, \ - ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while (0) -#endif - -#define IIC_NGRP 64 /* Internal Interrupt Combiner - Groups number */ -#define IIC_NIRQ (IIC_NGRP * 8)/* Internal Interrupt Combiner - Interrupts number */ -#define IIC_REGION_SIZE 0x108 /* Size of memory mapped region */ -#define IIC_REGSET_SIZE 0x41 - -/* - * State for each output signal of internal combiner - */ -typedef struct CombinerGroupState { - uint8_t src_mask; /* 1 - source enabled, 0 - disabled */ - uint8_t src_pending; /* Pending source interrupts before masking */ -} CombinerGroupState; - -#define TYPE_EXYNOS4210_COMBINER "exynos4210.combiner" -#define EXYNOS4210_COMBINER(obj) \ - OBJECT_CHECK(Exynos4210CombinerState, (obj), TYPE_EXYNOS4210_COMBINER) - -typedef struct Exynos4210CombinerState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - - struct CombinerGroupState group[IIC_NGRP]; - uint32_t reg_set[IIC_REGSET_SIZE]; - uint32_t icipsr[2]; - uint32_t external; /* 1 means that this combiner is external */ - - qemu_irq output_irq[IIC_NGRP]; -} Exynos4210CombinerState; - -static const VMStateDescription vmstate_exynos4210_combiner_group_state = { - .name = "exynos4210.combiner.groupstate", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(src_mask, CombinerGroupState), - VMSTATE_UINT8(src_pending, CombinerGroupState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_exynos4210_combiner = { - .name = "exynos4210.combiner", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(group, Exynos4210CombinerState, IIC_NGRP, 0, - vmstate_exynos4210_combiner_group_state, CombinerGroupState), - VMSTATE_UINT32_ARRAY(reg_set, Exynos4210CombinerState, - IIC_REGSET_SIZE), - VMSTATE_UINT32_ARRAY(icipsr, Exynos4210CombinerState, 2), - VMSTATE_UINT32(external, Exynos4210CombinerState), - VMSTATE_END_OF_LIST() - } -}; - -/* - * Get Combiner input GPIO into irqs structure - */ -void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev, - int ext) -{ - int n; - int bit; - int max; - qemu_irq *irq; - - max = ext ? EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ : - EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; - irq = ext ? irqs->ext_combiner_irq : irqs->int_combiner_irq; - - /* - * Some IRQs of Int/External Combiner are going to two Combiners groups, - * so let split them. - */ - for (n = 0; n < max; n++) { - - bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n); - - switch (n) { - /* MDNIE_LCD1 INTG1 */ - case EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 0) ... - EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 3): - irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), - irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(0, bit + 4)]); - continue; - - /* TMU INTG3 */ - case EXYNOS4210_COMBINER_GET_IRQ_NUM(3, 4): - irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), - irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(2, bit)]); - continue; - - /* LCD1 INTG12 */ - case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 0) ... - EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 3): - irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), - irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(11, bit + 4)]); - continue; - - /* Multi-Core Timer INTG12 */ - case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4) ... - EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 8): - irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), - irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); - continue; - - /* Multi-Core Timer INTG35 */ - case EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 4) ... - EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 8): - irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), - irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); - continue; - - /* Multi-Core Timer INTG51 */ - case EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 4) ... - EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 8): - irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), - irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); - continue; - - /* Multi-Core Timer INTG53 */ - case EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 4) ... - EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 8): - irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), - irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); - continue; - } - - irq[n] = qdev_get_gpio_in(dev, n); - } -} - -static uint64_t -exynos4210_combiner_read(void *opaque, hwaddr offset, unsigned size) -{ - struct Exynos4210CombinerState *s = - (struct Exynos4210CombinerState *)opaque; - uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and - get a start of corresponding group quad */ - uint32_t grp_quad_base_n; /* Base of group quad */ - uint32_t reg_n; /* Register number inside the quad */ - uint32_t val; - - req_quad_base_n = offset >> 4; - grp_quad_base_n = req_quad_base_n << 2; - reg_n = (offset - (req_quad_base_n << 4)) >> 2; - - if (req_quad_base_n >= IIC_NGRP) { - /* Read of ICIPSR register */ - return s->icipsr[reg_n]; - } - - val = 0; - - switch (reg_n) { - /* IISTR */ - case 2: - val |= s->group[grp_quad_base_n].src_pending; - val |= s->group[grp_quad_base_n + 1].src_pending << 8; - val |= s->group[grp_quad_base_n + 2].src_pending << 16; - val |= s->group[grp_quad_base_n + 3].src_pending << 24; - break; - /* IIMSR */ - case 3: - val |= s->group[grp_quad_base_n].src_mask & - s->group[grp_quad_base_n].src_pending; - val |= (s->group[grp_quad_base_n + 1].src_mask & - s->group[grp_quad_base_n + 1].src_pending) << 8; - val |= (s->group[grp_quad_base_n + 2].src_mask & - s->group[grp_quad_base_n + 2].src_pending) << 16; - val |= (s->group[grp_quad_base_n + 3].src_mask & - s->group[grp_quad_base_n + 3].src_pending) << 24; - break; - default: - if (offset >> 2 >= IIC_REGSET_SIZE) { - hw_error("exynos4210.combiner: overflow of reg_set by 0x" - TARGET_FMT_plx "offset\n", offset); - } - val = s->reg_set[offset >> 2]; - return 0; - } - return val; -} - -static void exynos4210_combiner_update(void *opaque, uint8_t group_n) -{ - struct Exynos4210CombinerState *s = - (struct Exynos4210CombinerState *)opaque; - - /* Send interrupt if needed */ - if (s->group[group_n].src_mask & s->group[group_n].src_pending) { -#ifdef DEBUG_COMBINER - if (group_n != 26) { - /* skip uart */ - DPRINTF("%s raise IRQ[%d]\n", s->external ? "EXT" : "INT", group_n); - } -#endif - - /* Set Combiner interrupt pending status after masking */ - if (group_n >= 32) { - s->icipsr[1] |= 1 << (group_n - 32); - } else { - s->icipsr[0] |= 1 << group_n; - } - - qemu_irq_raise(s->output_irq[group_n]); - } else { -#ifdef DEBUG_COMBINER - if (group_n != 26) { - /* skip uart */ - DPRINTF("%s lower IRQ[%d]\n", s->external ? "EXT" : "INT", group_n); - } -#endif - - /* Set Combiner interrupt pending status after masking */ - if (group_n >= 32) { - s->icipsr[1] &= ~(1 << (group_n - 32)); - } else { - s->icipsr[0] &= ~(1 << group_n); - } - - qemu_irq_lower(s->output_irq[group_n]); - } -} - -static void exynos4210_combiner_write(void *opaque, hwaddr offset, - uint64_t val, unsigned size) -{ - struct Exynos4210CombinerState *s = - (struct Exynos4210CombinerState *)opaque; - uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and - get a start of corresponding group quad */ - uint32_t grp_quad_base_n; /* Base of group quad */ - uint32_t reg_n; /* Register number inside the quad */ - - req_quad_base_n = offset >> 4; - grp_quad_base_n = req_quad_base_n << 2; - reg_n = (offset - (req_quad_base_n << 4)) >> 2; - - if (req_quad_base_n >= IIC_NGRP) { - hw_error("exynos4210.combiner: unallowed write access at offset 0x" - TARGET_FMT_plx "\n", offset); - return; - } - - if (reg_n > 1) { - hw_error("exynos4210.combiner: unallowed write access at offset 0x" - TARGET_FMT_plx "\n", offset); - return; - } - - if (offset >> 2 >= IIC_REGSET_SIZE) { - hw_error("exynos4210.combiner: overflow of reg_set by 0x" - TARGET_FMT_plx "offset\n", offset); - } - s->reg_set[offset >> 2] = val; - - switch (reg_n) { - /* IIESR */ - case 0: - /* FIXME: what if irq is pending, allowed by mask, and we allow it - * again. Interrupt will rise again! */ - - DPRINTF("%s enable IRQ for groups %d, %d, %d, %d\n", - s->external ? "EXT" : "INT", - grp_quad_base_n, - grp_quad_base_n + 1, - grp_quad_base_n + 2, - grp_quad_base_n + 3); - - /* Enable interrupt sources */ - s->group[grp_quad_base_n].src_mask |= val & 0xFF; - s->group[grp_quad_base_n + 1].src_mask |= (val & 0xFF00) >> 8; - s->group[grp_quad_base_n + 2].src_mask |= (val & 0xFF0000) >> 16; - s->group[grp_quad_base_n + 3].src_mask |= (val & 0xFF000000) >> 24; - - exynos4210_combiner_update(s, grp_quad_base_n); - exynos4210_combiner_update(s, grp_quad_base_n + 1); - exynos4210_combiner_update(s, grp_quad_base_n + 2); - exynos4210_combiner_update(s, grp_quad_base_n + 3); - break; - /* IIECR */ - case 1: - DPRINTF("%s disable IRQ for groups %d, %d, %d, %d\n", - s->external ? "EXT" : "INT", - grp_quad_base_n, - grp_quad_base_n + 1, - grp_quad_base_n + 2, - grp_quad_base_n + 3); - - /* Disable interrupt sources */ - s->group[grp_quad_base_n].src_mask &= ~(val & 0xFF); - s->group[grp_quad_base_n + 1].src_mask &= ~((val & 0xFF00) >> 8); - s->group[grp_quad_base_n + 2].src_mask &= ~((val & 0xFF0000) >> 16); - s->group[grp_quad_base_n + 3].src_mask &= ~((val & 0xFF000000) >> 24); - - exynos4210_combiner_update(s, grp_quad_base_n); - exynos4210_combiner_update(s, grp_quad_base_n + 1); - exynos4210_combiner_update(s, grp_quad_base_n + 2); - exynos4210_combiner_update(s, grp_quad_base_n + 3); - break; - default: - hw_error("exynos4210.combiner: unallowed write access at offset 0x" - TARGET_FMT_plx "\n", offset); - break; - } -} - -/* Get combiner group and bit from irq number */ -static uint8_t get_combiner_group_and_bit(int irq, uint8_t *bit) -{ - *bit = irq - ((irq >> 3) << 3); - return irq >> 3; -} - -/* Process a change in an external IRQ input. */ -static void exynos4210_combiner_handler(void *opaque, int irq, int level) -{ - struct Exynos4210CombinerState *s = - (struct Exynos4210CombinerState *)opaque; - uint8_t bit_n, group_n; - - group_n = get_combiner_group_and_bit(irq, &bit_n); - - if (s->external && group_n >= EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ) { - DPRINTF("%s unallowed IRQ group 0x%x\n", s->external ? "EXT" : "INT" - , group_n); - return; - } - - if (level) { - s->group[group_n].src_pending |= 1 << bit_n; - } else { - s->group[group_n].src_pending &= ~(1 << bit_n); - } - - exynos4210_combiner_update(s, group_n); -} - -static void exynos4210_combiner_reset(DeviceState *d) -{ - struct Exynos4210CombinerState *s = (struct Exynos4210CombinerState *)d; - - memset(&s->group, 0, sizeof(s->group)); - memset(&s->reg_set, 0, sizeof(s->reg_set)); - - s->reg_set[0xC0 >> 2] = 0x01010101; - s->reg_set[0xC4 >> 2] = 0x01010101; - s->reg_set[0xD0 >> 2] = 0x01010101; - s->reg_set[0xD4 >> 2] = 0x01010101; -} - -static const MemoryRegionOps exynos4210_combiner_ops = { - .read = exynos4210_combiner_read, - .write = exynos4210_combiner_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* - * Internal Combiner initialization. - */ -static int exynos4210_combiner_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - Exynos4210CombinerState *s = EXYNOS4210_COMBINER(dev); - unsigned int i; - - /* Allocate general purpose input signals and connect a handler to each of - * them */ - qdev_init_gpio_in(dev, exynos4210_combiner_handler, IIC_NIRQ); - - /* Connect SysBusDev irqs to device specific irqs */ - for (i = 0; i < IIC_NGRP; i++) { - sysbus_init_irq(sbd, &s->output_irq[i]); - } - - memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_combiner_ops, s, - "exynos4210-combiner", IIC_REGION_SIZE); - sysbus_init_mmio(sbd, &s->iomem); - - return 0; -} - -static Property exynos4210_combiner_properties[] = { - DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void exynos4210_combiner_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = exynos4210_combiner_init; - dc->reset = exynos4210_combiner_reset; - dc->props = exynos4210_combiner_properties; - dc->vmsd = &vmstate_exynos4210_combiner; -} - -static const TypeInfo exynos4210_combiner_info = { - .name = TYPE_EXYNOS4210_COMBINER, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Exynos4210CombinerState), - .class_init = exynos4210_combiner_class_init, -}; - -static void exynos4210_combiner_register_types(void) -{ - type_register_static(&exynos4210_combiner_info); -} - -type_init(exynos4210_combiner_register_types) diff --git a/qemu/hw/intc/exynos4210_gic.c b/qemu/hw/intc/exynos4210_gic.c deleted file mode 100644 index 4f7e89f7b..000000000 --- a/qemu/hw/intc/exynos4210_gic.c +++ /dev/null @@ -1,472 +0,0 @@ -/* - * Samsung exynos4210 GIC implementation. Based on hw/arm_gic.c - * - * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. - * All rights reserved. - * - * Evgeny Voevodin - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "qemu-common.h" -#include "hw/irq.h" -#include "hw/arm/exynos4210.h" - -enum ExtGicId { - EXT_GIC_ID_MDMA_LCD0 = 66, - EXT_GIC_ID_PDMA0, - EXT_GIC_ID_PDMA1, - EXT_GIC_ID_TIMER0, - EXT_GIC_ID_TIMER1, - EXT_GIC_ID_TIMER2, - EXT_GIC_ID_TIMER3, - EXT_GIC_ID_TIMER4, - EXT_GIC_ID_MCT_L0, - EXT_GIC_ID_WDT, - EXT_GIC_ID_RTC_ALARM, - EXT_GIC_ID_RTC_TIC, - EXT_GIC_ID_GPIO_XB, - EXT_GIC_ID_GPIO_XA, - EXT_GIC_ID_MCT_L1, - EXT_GIC_ID_IEM_APC, - EXT_GIC_ID_IEM_IEC, - EXT_GIC_ID_NFC, - EXT_GIC_ID_UART0, - EXT_GIC_ID_UART1, - EXT_GIC_ID_UART2, - EXT_GIC_ID_UART3, - EXT_GIC_ID_UART4, - EXT_GIC_ID_MCT_G0, - EXT_GIC_ID_I2C0, - EXT_GIC_ID_I2C1, - EXT_GIC_ID_I2C2, - EXT_GIC_ID_I2C3, - EXT_GIC_ID_I2C4, - EXT_GIC_ID_I2C5, - EXT_GIC_ID_I2C6, - EXT_GIC_ID_I2C7, - EXT_GIC_ID_SPI0, - EXT_GIC_ID_SPI1, - EXT_GIC_ID_SPI2, - EXT_GIC_ID_MCT_G1, - EXT_GIC_ID_USB_HOST, - EXT_GIC_ID_USB_DEVICE, - EXT_GIC_ID_MODEMIF, - EXT_GIC_ID_HSMMC0, - EXT_GIC_ID_HSMMC1, - EXT_GIC_ID_HSMMC2, - EXT_GIC_ID_HSMMC3, - EXT_GIC_ID_SDMMC, - EXT_GIC_ID_MIPI_CSI_4LANE, - EXT_GIC_ID_MIPI_DSI_4LANE, - EXT_GIC_ID_MIPI_CSI_2LANE, - EXT_GIC_ID_MIPI_DSI_2LANE, - EXT_GIC_ID_ONENAND_AUDI, - EXT_GIC_ID_ROTATOR, - EXT_GIC_ID_FIMC0, - EXT_GIC_ID_FIMC1, - EXT_GIC_ID_FIMC2, - EXT_GIC_ID_FIMC3, - EXT_GIC_ID_JPEG, - EXT_GIC_ID_2D, - EXT_GIC_ID_PCIe, - EXT_GIC_ID_MIXER, - EXT_GIC_ID_HDMI, - EXT_GIC_ID_HDMI_I2C, - EXT_GIC_ID_MFC, - EXT_GIC_ID_TVENC, -}; - -enum ExtInt { - EXT_GIC_ID_EXTINT0 = 48, - EXT_GIC_ID_EXTINT1, - EXT_GIC_ID_EXTINT2, - EXT_GIC_ID_EXTINT3, - EXT_GIC_ID_EXTINT4, - EXT_GIC_ID_EXTINT5, - EXT_GIC_ID_EXTINT6, - EXT_GIC_ID_EXTINT7, - EXT_GIC_ID_EXTINT8, - EXT_GIC_ID_EXTINT9, - EXT_GIC_ID_EXTINT10, - EXT_GIC_ID_EXTINT11, - EXT_GIC_ID_EXTINT12, - EXT_GIC_ID_EXTINT13, - EXT_GIC_ID_EXTINT14, - EXT_GIC_ID_EXTINT15 -}; - -/* - * External GIC sources which are not from External Interrupt Combiner or - * External Interrupts are starting from EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ, - * which is INTG16 in Internal Interrupt Combiner. - */ - -static uint32_t -combiner_grp_to_gic_id[64-EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][8] = { - /* int combiner groups 16-19 */ - { }, { }, { }, { }, - /* int combiner group 20 */ - { 0, EXT_GIC_ID_MDMA_LCD0 }, - /* int combiner group 21 */ - { EXT_GIC_ID_PDMA0, EXT_GIC_ID_PDMA1 }, - /* int combiner group 22 */ - { EXT_GIC_ID_TIMER0, EXT_GIC_ID_TIMER1, EXT_GIC_ID_TIMER2, - EXT_GIC_ID_TIMER3, EXT_GIC_ID_TIMER4 }, - /* int combiner group 23 */ - { EXT_GIC_ID_RTC_ALARM, EXT_GIC_ID_RTC_TIC }, - /* int combiner group 24 */ - { EXT_GIC_ID_GPIO_XB, EXT_GIC_ID_GPIO_XA }, - /* int combiner group 25 */ - { EXT_GIC_ID_IEM_APC, EXT_GIC_ID_IEM_IEC }, - /* int combiner group 26 */ - { EXT_GIC_ID_UART0, EXT_GIC_ID_UART1, EXT_GIC_ID_UART2, EXT_GIC_ID_UART3, - EXT_GIC_ID_UART4 }, - /* int combiner group 27 */ - { EXT_GIC_ID_I2C0, EXT_GIC_ID_I2C1, EXT_GIC_ID_I2C2, EXT_GIC_ID_I2C3, - EXT_GIC_ID_I2C4, EXT_GIC_ID_I2C5, EXT_GIC_ID_I2C6, - EXT_GIC_ID_I2C7 }, - /* int combiner group 28 */ - { EXT_GIC_ID_SPI0, EXT_GIC_ID_SPI1, EXT_GIC_ID_SPI2 , EXT_GIC_ID_USB_HOST}, - /* int combiner group 29 */ - { EXT_GIC_ID_HSMMC0, EXT_GIC_ID_HSMMC1, EXT_GIC_ID_HSMMC2, - EXT_GIC_ID_HSMMC3, EXT_GIC_ID_SDMMC }, - /* int combiner group 30 */ - { EXT_GIC_ID_MIPI_CSI_4LANE, EXT_GIC_ID_MIPI_CSI_2LANE }, - /* int combiner group 31 */ - { EXT_GIC_ID_MIPI_DSI_4LANE, EXT_GIC_ID_MIPI_DSI_2LANE }, - /* int combiner group 32 */ - { EXT_GIC_ID_FIMC0, EXT_GIC_ID_FIMC1 }, - /* int combiner group 33 */ - { EXT_GIC_ID_FIMC2, EXT_GIC_ID_FIMC3 }, - /* int combiner group 34 */ - { EXT_GIC_ID_ONENAND_AUDI, EXT_GIC_ID_NFC }, - /* int combiner group 35 */ - { 0, 0, 0, EXT_GIC_ID_MCT_L1, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 }, - /* int combiner group 36 */ - { EXT_GIC_ID_MIXER }, - /* int combiner group 37 */ - { EXT_GIC_ID_EXTINT4, EXT_GIC_ID_EXTINT5, EXT_GIC_ID_EXTINT6, - EXT_GIC_ID_EXTINT7 }, - /* groups 38-50 */ - { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, - /* int combiner group 51 */ - { EXT_GIC_ID_MCT_L0, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 }, - /* group 52 */ - { }, - /* int combiner group 53 */ - { EXT_GIC_ID_WDT, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 }, - /* groups 54-63 */ - { }, { }, { }, { }, { }, { }, { }, { }, { }, { } -}; - -#define EXYNOS4210_GIC_NIRQ 160 - -#define EXYNOS4210_EXT_GIC_CPU_REGION_SIZE 0x10000 -#define EXYNOS4210_EXT_GIC_DIST_REGION_SIZE 0x10000 - -#define EXYNOS4210_EXT_GIC_PER_CPU_OFFSET 0x8000 -#define EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(n) \ - ((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET) -#define EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(n) \ - ((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET) - -#define EXYNOS4210_GIC_CPU_REGION_SIZE 0x100 -#define EXYNOS4210_GIC_DIST_REGION_SIZE 0x1000 - -static void exynos4210_irq_handler(void *opaque, int irq, int level) -{ - Exynos4210Irq *s = (Exynos4210Irq *)opaque; - - /* Bypass */ - qemu_set_irq(s->board_irqs[irq], level); -} - -/* - * Initialize exynos4210 IRQ subsystem stub. - */ -qemu_irq *exynos4210_init_irq(Exynos4210Irq *s) -{ - return qemu_allocate_irqs(exynos4210_irq_handler, s, - EXYNOS4210_MAX_INT_COMBINER_IN_IRQ); -} - -/* - * Initialize board IRQs. - * These IRQs contain splitted Int/External Combiner and External Gic IRQs. - */ -void exynos4210_init_board_irqs(Exynos4210Irq *s) -{ - uint32_t grp, bit, irq_id, n; - - for (n = 0; n < EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ; n++) { - irq_id = 0; - if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 4) || - n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4)) { - /* MCT_G0 is passed to External GIC */ - irq_id = EXT_GIC_ID_MCT_G0; - } - if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 5) || - n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 5)) { - /* MCT_G1 is passed to External and GIC */ - irq_id = EXT_GIC_ID_MCT_G1; - } - if (irq_id) { - s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n], - s->ext_gic_irq[irq_id-32]); - } else { - s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n], - s->ext_combiner_irq[n]); - } - } - for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) { - /* these IDs are passed to Internal Combiner and External GIC */ - grp = EXYNOS4210_COMBINER_GET_GRP_NUM(n); - bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n); - irq_id = combiner_grp_to_gic_id[grp - - EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][bit]; - - if (irq_id) { - s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n], - s->ext_gic_irq[irq_id-32]); - } - } -} - -/* - * Get IRQ number from exynos4210 IRQ subsystem stub. - * To identify IRQ source use internal combiner group and bit number - * grp - group number - * bit - bit number inside group - */ -uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit) -{ - return EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit); -} - -/********* GIC part *********/ - -#define TYPE_EXYNOS4210_GIC "exynos4210.gic" -#define EXYNOS4210_GIC(obj) \ - OBJECT_CHECK(Exynos4210GicState, (obj), TYPE_EXYNOS4210_GIC) - -typedef struct { - SysBusDevice parent_obj; - - MemoryRegion cpu_container; - MemoryRegion dist_container; - MemoryRegion cpu_alias[EXYNOS4210_NCPUS]; - MemoryRegion dist_alias[EXYNOS4210_NCPUS]; - uint32_t num_cpu; - DeviceState *gic; -} Exynos4210GicState; - -static void exynos4210_gic_set_irq(void *opaque, int irq, int level) -{ - Exynos4210GicState *s = (Exynos4210GicState *)opaque; - qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level); -} - -static int exynos4210_gic_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - Exynos4210GicState *s = EXYNOS4210_GIC(dev); - uint32_t i; - const char cpu_prefix[] = "exynos4210-gic-alias_cpu"; - const char dist_prefix[] = "exynos4210-gic-alias_dist"; - char cpu_alias_name[sizeof(cpu_prefix) + 3]; - char dist_alias_name[sizeof(cpu_prefix) + 3]; - SysBusDevice *busdev; - - s->gic = qdev_create(NULL, "arm_gic"); - qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu); - qdev_prop_set_uint32(s->gic, "num-irq", EXYNOS4210_GIC_NIRQ); - qdev_init_nofail(s->gic); - busdev = SYS_BUS_DEVICE(s->gic); - - /* Pass through outbound IRQ lines from the GIC */ - sysbus_pass_irq(sbd, busdev); - - /* Pass through inbound GPIO lines to the GIC */ - qdev_init_gpio_in(dev, exynos4210_gic_set_irq, - EXYNOS4210_GIC_NIRQ - 32); - - memory_region_init(&s->cpu_container, OBJECT(s), "exynos4210-cpu-container", - EXYNOS4210_EXT_GIC_CPU_REGION_SIZE); - memory_region_init(&s->dist_container, OBJECT(s), "exynos4210-dist-container", - EXYNOS4210_EXT_GIC_DIST_REGION_SIZE); - - for (i = 0; i < s->num_cpu; i++) { - /* Map CPU interface per SMP Core */ - sprintf(cpu_alias_name, "%s%x", cpu_prefix, i); - memory_region_init_alias(&s->cpu_alias[i], OBJECT(s), - cpu_alias_name, - sysbus_mmio_get_region(busdev, 1), - 0, - EXYNOS4210_GIC_CPU_REGION_SIZE); - memory_region_add_subregion(&s->cpu_container, - EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(i), &s->cpu_alias[i]); - - /* Map Distributor per SMP Core */ - sprintf(dist_alias_name, "%s%x", dist_prefix, i); - memory_region_init_alias(&s->dist_alias[i], OBJECT(s), - dist_alias_name, - sysbus_mmio_get_region(busdev, 0), - 0, - EXYNOS4210_GIC_DIST_REGION_SIZE); - memory_region_add_subregion(&s->dist_container, - EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(i), &s->dist_alias[i]); - } - - sysbus_init_mmio(sbd, &s->cpu_container); - sysbus_init_mmio(sbd, &s->dist_container); - - return 0; -} - -static Property exynos4210_gic_properties[] = { - DEFINE_PROP_UINT32("num-cpu", Exynos4210GicState, num_cpu, 1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void exynos4210_gic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = exynos4210_gic_init; - dc->props = exynos4210_gic_properties; -} - -static const TypeInfo exynos4210_gic_info = { - .name = TYPE_EXYNOS4210_GIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Exynos4210GicState), - .class_init = exynos4210_gic_class_init, -}; - -static void exynos4210_gic_register_types(void) -{ - type_register_static(&exynos4210_gic_info); -} - -type_init(exynos4210_gic_register_types) - -/* IRQ OR Gate struct. - * - * This device models an OR gate. There are n_in input qdev gpio lines and one - * output sysbus IRQ line. The output IRQ level is formed as OR between all - * gpio inputs. - */ - -#define TYPE_EXYNOS4210_IRQ_GATE "exynos4210.irq_gate" -#define EXYNOS4210_IRQ_GATE(obj) \ - OBJECT_CHECK(Exynos4210IRQGateState, (obj), TYPE_EXYNOS4210_IRQ_GATE) - -typedef struct Exynos4210IRQGateState { - SysBusDevice parent_obj; - - uint32_t n_in; /* inputs amount */ - uint32_t *level; /* input levels */ - qemu_irq out; /* output IRQ */ -} Exynos4210IRQGateState; - -static Property exynos4210_irq_gate_properties[] = { - DEFINE_PROP_UINT32("n_in", Exynos4210IRQGateState, n_in, 1), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription vmstate_exynos4210_irq_gate = { - .name = "exynos4210.irq_gate", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_VBUFFER_UINT32(level, Exynos4210IRQGateState, 1, NULL, 0, n_in), - VMSTATE_END_OF_LIST() - } -}; - -/* Process a change in IRQ input. */ -static void exynos4210_irq_gate_handler(void *opaque, int irq, int level) -{ - Exynos4210IRQGateState *s = (Exynos4210IRQGateState *)opaque; - uint32_t i; - - assert(irq < s->n_in); - - s->level[irq] = level; - - for (i = 0; i < s->n_in; i++) { - if (s->level[i] >= 1) { - qemu_irq_raise(s->out); - return; - } - } - - qemu_irq_lower(s->out); -} - -static void exynos4210_irq_gate_reset(DeviceState *d) -{ - Exynos4210IRQGateState *s = EXYNOS4210_IRQ_GATE(d); - - memset(s->level, 0, s->n_in * sizeof(*s->level)); -} - -/* - * IRQ Gate initialization. - */ -static int exynos4210_irq_gate_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - Exynos4210IRQGateState *s = EXYNOS4210_IRQ_GATE(dev); - - /* Allocate general purpose input signals and connect a handler to each of - * them */ - qdev_init_gpio_in(dev, exynos4210_irq_gate_handler, s->n_in); - - s->level = g_malloc0(s->n_in * sizeof(*s->level)); - - sysbus_init_irq(sbd, &s->out); - - return 0; -} - -static void exynos4210_irq_gate_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = exynos4210_irq_gate_init; - dc->reset = exynos4210_irq_gate_reset; - dc->vmsd = &vmstate_exynos4210_irq_gate; - dc->props = exynos4210_irq_gate_properties; -} - -static const TypeInfo exynos4210_irq_gate_info = { - .name = TYPE_EXYNOS4210_IRQ_GATE, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Exynos4210IRQGateState), - .class_init = exynos4210_irq_gate_class_init, -}; - -static void exynos4210_irq_gate_register_types(void) -{ - type_register_static(&exynos4210_irq_gate_info); -} - -type_init(exynos4210_irq_gate_register_types) diff --git a/qemu/hw/intc/gic_internal.h b/qemu/hw/intc/gic_internal.h deleted file mode 100644 index 20c1e8a24..000000000 --- a/qemu/hw/intc/gic_internal.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * ARM GIC support - internal interfaces - * - * Copyright (c) 2012 Linaro Limited - * Written 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 . - */ - -#ifndef QEMU_ARM_GIC_INTERNAL_H -#define QEMU_ARM_GIC_INTERNAL_H - -#include "hw/intc/arm_gic.h" - -#define ALL_CPU_MASK ((unsigned)(((1 << GIC_NCPU) - 1))) - -/* The NVIC has 16 internal vectors. However these are not exposed - through the normal GIC interface. */ -#define GIC_BASE_IRQ ((s->revision == REV_NVIC) ? 32 : 0) - -#define GIC_SET_ENABLED(irq, cm) s->irq_state[irq].enabled |= (cm) -#define GIC_CLEAR_ENABLED(irq, cm) s->irq_state[irq].enabled &= ~(cm) -#define GIC_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0) -#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm) -#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm) -#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm) -#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm) -#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0) -#define GIC_SET_MODEL(irq) s->irq_state[irq].model = true -#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = false -#define GIC_TEST_MODEL(irq) s->irq_state[irq].model -#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level |= (cm) -#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm) -#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0) -#define GIC_SET_EDGE_TRIGGER(irq) s->irq_state[irq].edge_trigger = true -#define GIC_CLEAR_EDGE_TRIGGER(irq) s->irq_state[irq].edge_trigger = false -#define GIC_TEST_EDGE_TRIGGER(irq) (s->irq_state[irq].edge_trigger) -#define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ? \ - s->priority1[irq][cpu] : \ - s->priority2[(irq) - GIC_INTERNAL]) -#define GIC_TARGET(irq) s->irq_target[irq] -#define GIC_CLEAR_GROUP(irq, cm) (s->irq_state[irq].group &= ~(cm)) -#define GIC_SET_GROUP(irq, cm) (s->irq_state[irq].group |= (cm)) -#define GIC_TEST_GROUP(irq, cm) ((s->irq_state[irq].group & (cm)) != 0) - -#define GICD_CTLR_EN_GRP0 (1U << 0) -#define GICD_CTLR_EN_GRP1 (1U << 1) - -#define GICC_CTLR_EN_GRP0 (1U << 0) -#define GICC_CTLR_EN_GRP1 (1U << 1) -#define GICC_CTLR_ACK_CTL (1U << 2) -#define GICC_CTLR_FIQ_EN (1U << 3) -#define GICC_CTLR_CBPR (1U << 4) /* GICv1: SBPR */ -#define GICC_CTLR_EOIMODE (1U << 9) -#define GICC_CTLR_EOIMODE_NS (1U << 10) - -/* Valid bits for GICC_CTLR for GICv1, v1 with security extensions, - * GICv2 and GICv2 with security extensions: - */ -#define GICC_CTLR_V1_MASK 0x1 -#define GICC_CTLR_V1_S_MASK 0x1f -#define GICC_CTLR_V2_MASK 0x21f -#define GICC_CTLR_V2_S_MASK 0x61f - -/* The special cases for the revision property: */ -#define REV_11MPCORE 0 -#define REV_NVIC 0xffffffff - -void gic_set_pending_private(GICState *s, int cpu, int irq); -uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs); -void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs); -void gic_update(GICState *s); -void gic_init_irqs_and_distributor(GICState *s); -void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val, - MemTxAttrs attrs); - -static inline bool gic_test_pending(GICState *s, int irq, int cm) -{ - if (s->revision == REV_NVIC || s->revision == REV_11MPCORE) { - return s->irq_state[irq].pending & cm; - } else { - /* Edge-triggered interrupts are marked pending on a rising edge, but - * level-triggered interrupts are either considered pending when the - * level is active or if software has explicitly written to - * GICD_ISPENDR to set the state pending. - */ - return (s->irq_state[irq].pending & cm) || - (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_LEVEL(irq, cm)); - } -} - -#endif /* !QEMU_ARM_GIC_INTERNAL_H */ diff --git a/qemu/hw/intc/grlib_irqmp.c b/qemu/hw/intc/grlib_irqmp.c deleted file mode 100644 index f5ca8f752..000000000 --- a/qemu/hw/intc/grlib_irqmp.c +++ /dev/null @@ -1,375 +0,0 @@ -/* - * QEMU GRLIB IRQMP Emulator - * - * (Multiprocessor and extended interrupt not supported) - * - * Copyright (c) 2010-2011 AdaCore - * - * 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/sysbus.h" -#include "cpu.h" - -#include "hw/sparc/grlib.h" - -#include "trace.h" - -#define IRQMP_MAX_CPU 16 -#define IRQMP_REG_SIZE 256 /* Size of memory mapped registers */ - -/* Memory mapped register offsets */ -#define LEVEL_OFFSET 0x00 -#define PENDING_OFFSET 0x04 -#define FORCE0_OFFSET 0x08 -#define CLEAR_OFFSET 0x0C -#define MP_STATUS_OFFSET 0x10 -#define BROADCAST_OFFSET 0x14 -#define MASK_OFFSET 0x40 -#define FORCE_OFFSET 0x80 -#define EXTENDED_OFFSET 0xC0 - -#define TYPE_GRLIB_IRQMP "grlib,irqmp" -#define GRLIB_IRQMP(obj) OBJECT_CHECK(IRQMP, (obj), TYPE_GRLIB_IRQMP) - -typedef struct IRQMPState IRQMPState; - -typedef struct IRQMP { - SysBusDevice parent_obj; - - MemoryRegion iomem; - - void *set_pil_in; - void *set_pil_in_opaque; - - IRQMPState *state; -} IRQMP; - -struct IRQMPState { - uint32_t level; - uint32_t pending; - uint32_t clear; - uint32_t broadcast; - - uint32_t mask[IRQMP_MAX_CPU]; - uint32_t force[IRQMP_MAX_CPU]; - uint32_t extended[IRQMP_MAX_CPU]; - - IRQMP *parent; -}; - -static void grlib_irqmp_check_irqs(IRQMPState *state) -{ - uint32_t pend = 0; - uint32_t level0 = 0; - uint32_t level1 = 0; - set_pil_in_fn set_pil_in; - - assert(state != NULL); - assert(state->parent != NULL); - - /* IRQ for CPU 0 (no SMP support) */ - pend = (state->pending | state->force[0]) - & state->mask[0]; - - level0 = pend & ~state->level; - level1 = pend & state->level; - - trace_grlib_irqmp_check_irqs(state->pending, state->force[0], - state->mask[0], level1, level0); - - set_pil_in = (set_pil_in_fn)state->parent->set_pil_in; - - /* Trigger level1 interrupt first and level0 if there is no level1 */ - if (level1 != 0) { - set_pil_in(state->parent->set_pil_in_opaque, level1); - } else { - set_pil_in(state->parent->set_pil_in_opaque, level0); - } -} - -void grlib_irqmp_ack(DeviceState *dev, int intno) -{ - IRQMP *irqmp = GRLIB_IRQMP(dev); - IRQMPState *state; - uint32_t mask; - - state = irqmp->state; - assert(state != NULL); - - intno &= 15; - mask = 1 << intno; - - trace_grlib_irqmp_ack(intno); - - /* Clear registers */ - state->pending &= ~mask; - state->force[0] &= ~mask; /* Only CPU 0 (No SMP support) */ - - grlib_irqmp_check_irqs(state); -} - -void grlib_irqmp_set_irq(void *opaque, int irq, int level) -{ - IRQMP *irqmp = GRLIB_IRQMP(opaque); - IRQMPState *s; - int i = 0; - - s = irqmp->state; - assert(s != NULL); - assert(s->parent != NULL); - - - if (level) { - trace_grlib_irqmp_set_irq(irq); - - if (s->broadcast & 1 << irq) { - /* Broadcasted IRQ */ - for (i = 0; i < IRQMP_MAX_CPU; i++) { - s->force[i] |= 1 << irq; - } - } else { - s->pending |= 1 << irq; - } - grlib_irqmp_check_irqs(s); - - } -} - -static uint64_t grlib_irqmp_read(void *opaque, hwaddr addr, - unsigned size) -{ - IRQMP *irqmp = opaque; - IRQMPState *state; - - assert(irqmp != NULL); - state = irqmp->state; - assert(state != NULL); - - addr &= 0xff; - - /* global registers */ - switch (addr) { - case LEVEL_OFFSET: - return state->level; - - case PENDING_OFFSET: - return state->pending; - - case FORCE0_OFFSET: - /* This register is an "alias" for the force register of CPU 0 */ - return state->force[0]; - - case CLEAR_OFFSET: - case MP_STATUS_OFFSET: - /* Always read as 0 */ - return 0; - - case BROADCAST_OFFSET: - return state->broadcast; - - default: - break; - } - - /* mask registers */ - if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) { - int cpu = (addr - MASK_OFFSET) / 4; - assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); - - return state->mask[cpu]; - } - - /* force registers */ - if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) { - int cpu = (addr - FORCE_OFFSET) / 4; - assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); - - return state->force[cpu]; - } - - /* extended (not supported) */ - if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) { - int cpu = (addr - EXTENDED_OFFSET) / 4; - assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); - - return state->extended[cpu]; - } - - trace_grlib_irqmp_readl_unknown(addr); - return 0; -} - -static void grlib_irqmp_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - IRQMP *irqmp = opaque; - IRQMPState *state; - - assert(irqmp != NULL); - state = irqmp->state; - assert(state != NULL); - - addr &= 0xff; - - /* global registers */ - switch (addr) { - case LEVEL_OFFSET: - value &= 0xFFFF << 1; /* clean up the value */ - state->level = value; - return; - - case PENDING_OFFSET: - /* Read Only */ - return; - - case FORCE0_OFFSET: - /* This register is an "alias" for the force register of CPU 0 */ - - value &= 0xFFFE; /* clean up the value */ - state->force[0] = value; - grlib_irqmp_check_irqs(irqmp->state); - return; - - case CLEAR_OFFSET: - value &= ~1; /* clean up the value */ - state->pending &= ~value; - return; - - case MP_STATUS_OFFSET: - /* Read Only (no SMP support) */ - return; - - case BROADCAST_OFFSET: - value &= 0xFFFE; /* clean up the value */ - state->broadcast = value; - return; - - default: - break; - } - - /* mask registers */ - if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) { - int cpu = (addr - MASK_OFFSET) / 4; - assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); - - value &= ~1; /* clean up the value */ - state->mask[cpu] = value; - grlib_irqmp_check_irqs(irqmp->state); - return; - } - - /* force registers */ - if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) { - int cpu = (addr - FORCE_OFFSET) / 4; - assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); - - uint32_t force = value & 0xFFFE; - uint32_t clear = (value >> 16) & 0xFFFE; - uint32_t old = state->force[cpu]; - - state->force[cpu] = (old | force) & ~clear; - grlib_irqmp_check_irqs(irqmp->state); - return; - } - - /* extended (not supported) */ - if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) { - int cpu = (addr - EXTENDED_OFFSET) / 4; - assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); - - value &= 0xF; /* clean up the value */ - state->extended[cpu] = value; - return; - } - - trace_grlib_irqmp_writel_unknown(addr, value); -} - -static const MemoryRegionOps grlib_irqmp_ops = { - .read = grlib_irqmp_read, - .write = grlib_irqmp_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void grlib_irqmp_reset(DeviceState *d) -{ - IRQMP *irqmp = GRLIB_IRQMP(d); - assert(irqmp->state != NULL); - - memset(irqmp->state, 0, sizeof *irqmp->state); - irqmp->state->parent = irqmp; -} - -static int grlib_irqmp_init(SysBusDevice *dev) -{ - IRQMP *irqmp = GRLIB_IRQMP(dev); - - /* Check parameters */ - if (irqmp->set_pil_in == NULL) { - return -1; - } - - memory_region_init_io(&irqmp->iomem, OBJECT(dev), &grlib_irqmp_ops, irqmp, - "irqmp", IRQMP_REG_SIZE); - - irqmp->state = g_malloc0(sizeof *irqmp->state); - - sysbus_init_mmio(dev, &irqmp->iomem); - - return 0; -} - -static Property grlib_irqmp_properties[] = { - DEFINE_PROP_PTR("set_pil_in", IRQMP, set_pil_in), - DEFINE_PROP_PTR("set_pil_in_opaque", IRQMP, set_pil_in_opaque), - DEFINE_PROP_END_OF_LIST(), -}; - -static void grlib_irqmp_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = grlib_irqmp_init; - dc->reset = grlib_irqmp_reset; - dc->props = grlib_irqmp_properties; - /* Reason: pointer properties "set_pil_in", "set_pil_in_opaque" */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo grlib_irqmp_info = { - .name = TYPE_GRLIB_IRQMP, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IRQMP), - .class_init = grlib_irqmp_class_init, -}; - -static void grlib_irqmp_register_types(void) -{ - type_register_static(&grlib_irqmp_info); -} - -type_init(grlib_irqmp_register_types) diff --git a/qemu/hw/intc/heathrow_pic.c b/qemu/hw/intc/heathrow_pic.c deleted file mode 100644 index 171f5ed81..000000000 --- a/qemu/hw/intc/heathrow_pic.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Heathrow PIC support (OldWorld PowerMac) - * - * Copyright (c) 2005-2007 Fabrice Bellard - * Copyright (c) 2007 Jocelyn Mayer - * - * 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/ppc/mac.h" - -/* debug PIC */ -//#define DEBUG_PIC - -#ifdef DEBUG_PIC -#define PIC_DPRINTF(fmt, ...) \ - do { printf("PIC: " fmt , ## __VA_ARGS__); } while (0) -#else -#define PIC_DPRINTF(fmt, ...) -#endif - -typedef struct HeathrowPIC { - uint32_t events; - uint32_t mask; - uint32_t levels; - uint32_t level_triggered; -} HeathrowPIC; - -typedef struct HeathrowPICS { - MemoryRegion mem; - HeathrowPIC pics[2]; - qemu_irq *irqs; -} HeathrowPICS; - -static inline int check_irq(HeathrowPIC *pic) -{ - return (pic->events | (pic->levels & pic->level_triggered)) & pic->mask; -} - -/* update the CPU irq state */ -static void heathrow_pic_update(HeathrowPICS *s) -{ - if (check_irq(&s->pics[0]) || check_irq(&s->pics[1])) { - qemu_irq_raise(s->irqs[0]); - } else { - qemu_irq_lower(s->irqs[0]); - } -} - -static void pic_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - HeathrowPICS *s = opaque; - HeathrowPIC *pic; - unsigned int n; - - n = ((addr & 0xfff) - 0x10) >> 4; - PIC_DPRINTF("writel: " TARGET_FMT_plx " %u: %08x\n", addr, n, value); - if (n >= 2) - return; - pic = &s->pics[n]; - switch(addr & 0xf) { - case 0x04: - pic->mask = value; - heathrow_pic_update(s); - break; - case 0x08: - /* do not reset level triggered IRQs */ - value &= ~pic->level_triggered; - pic->events &= ~value; - heathrow_pic_update(s); - break; - default: - break; - } -} - -static uint64_t pic_read(void *opaque, hwaddr addr, - unsigned size) -{ - HeathrowPICS *s = opaque; - HeathrowPIC *pic; - unsigned int n; - uint32_t value; - - n = ((addr & 0xfff) - 0x10) >> 4; - if (n >= 2) { - value = 0; - } else { - pic = &s->pics[n]; - switch(addr & 0xf) { - case 0x0: - value = pic->events; - break; - case 0x4: - value = pic->mask; - break; - case 0xc: - value = pic->levels; - break; - default: - value = 0; - break; - } - } - PIC_DPRINTF("readl: " TARGET_FMT_plx " %u: %08x\n", addr, n, value); - return value; -} - -static const MemoryRegionOps heathrow_pic_ops = { - .read = pic_read, - .write = pic_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void heathrow_pic_set_irq(void *opaque, int num, int level) -{ - HeathrowPICS *s = opaque; - HeathrowPIC *pic; - unsigned int irq_bit; - -#if defined(DEBUG) - { - static int last_level[64]; - if (last_level[num] != level) { - PIC_DPRINTF("set_irq: num=0x%02x level=%d\n", num, level); - last_level[num] = level; - } - } -#endif - pic = &s->pics[1 - (num >> 5)]; - irq_bit = 1 << (num & 0x1f); - if (level) { - pic->events |= irq_bit & ~pic->level_triggered; - pic->levels |= irq_bit; - } else { - pic->levels &= ~irq_bit; - } - heathrow_pic_update(s); -} - -static const VMStateDescription vmstate_heathrow_pic_one = { - .name = "heathrow_pic_one", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(events, HeathrowPIC), - VMSTATE_UINT32(mask, HeathrowPIC), - VMSTATE_UINT32(levels, HeathrowPIC), - VMSTATE_UINT32(level_triggered, HeathrowPIC), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_heathrow_pic = { - .name = "heathrow_pic", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(pics, HeathrowPICS, 2, 1, - vmstate_heathrow_pic_one, HeathrowPIC), - VMSTATE_END_OF_LIST() - } -}; - -static void heathrow_pic_reset_one(HeathrowPIC *s) -{ - memset(s, '\0', sizeof(HeathrowPIC)); -} - -static void heathrow_pic_reset(void *opaque) -{ - HeathrowPICS *s = opaque; - - heathrow_pic_reset_one(&s->pics[0]); - heathrow_pic_reset_one(&s->pics[1]); - - s->pics[0].level_triggered = 0; - s->pics[1].level_triggered = 0x1ff00000; -} - -qemu_irq *heathrow_pic_init(MemoryRegion **pmem, - int nb_cpus, qemu_irq **irqs) -{ - HeathrowPICS *s; - - s = g_malloc0(sizeof(HeathrowPICS)); - /* only 1 CPU */ - s->irqs = irqs[0]; - memory_region_init_io(&s->mem, NULL, &heathrow_pic_ops, s, - "heathrow-pic", 0x1000); - *pmem = &s->mem; - - vmstate_register(NULL, -1, &vmstate_heathrow_pic, s); - qemu_register_reset(heathrow_pic_reset, s); - return qemu_allocate_irqs(heathrow_pic_set_irq, s, 64); -} diff --git a/qemu/hw/intc/i8259.c b/qemu/hw/intc/i8259.c deleted file mode 100644 index bb43669b9..000000000 --- a/qemu/hw/intc/i8259.c +++ /dev/null @@ -1,524 +0,0 @@ -/* - * QEMU 8259 interrupt controller emulation - * - * Copyright (c) 2003-2004 Fabrice Bellard - * - * 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/i386/pc.h" -#include "hw/isa/isa.h" -#include "monitor/monitor.h" -#include "qemu/timer.h" -#include "hw/isa/i8259_internal.h" - -/* debug PIC */ -//#define DEBUG_PIC - -#ifdef DEBUG_PIC -#define DPRINTF(fmt, ...) \ - do { printf("pic: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) -#endif - -//#define DEBUG_IRQ_LATENCY -//#define DEBUG_IRQ_COUNT - -#define TYPE_I8259 "isa-i8259" -#define PIC_CLASS(class) OBJECT_CLASS_CHECK(PICClass, (class), TYPE_I8259) -#define PIC_GET_CLASS(obj) OBJECT_GET_CLASS(PICClass, (obj), TYPE_I8259) - -/** - * PICClass: - * @parent_realize: The parent's realizefn. - */ -typedef struct PICClass { - PICCommonClass parent_class; - - DeviceRealize parent_realize; -} PICClass; - -#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) -static int irq_level[16]; -#endif -#ifdef DEBUG_IRQ_COUNT -static uint64_t irq_count[16]; -#endif -#ifdef DEBUG_IRQ_LATENCY -static int64_t irq_time[16]; -#endif -DeviceState *isa_pic; -static PICCommonState *slave_pic; - -/* return the highest priority found in mask (highest = smallest - number). Return 8 if no irq */ -static int get_priority(PICCommonState *s, int mask) -{ - int priority; - - if (mask == 0) { - return 8; - } - priority = 0; - while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) { - priority++; - } - return priority; -} - -/* return the pic wanted interrupt. return -1 if none */ -static int pic_get_irq(PICCommonState *s) -{ - int mask, cur_priority, priority; - - mask = s->irr & ~s->imr; - priority = get_priority(s, mask); - if (priority == 8) { - return -1; - } - /* compute current priority. If special fully nested mode on the - master, the IRQ coming from the slave is not taken into account - for the priority computation. */ - mask = s->isr; - if (s->special_mask) { - mask &= ~s->imr; - } - if (s->special_fully_nested_mode && s->master) { - mask &= ~(1 << 2); - } - cur_priority = get_priority(s, mask); - if (priority < cur_priority) { - /* higher priority found: an irq should be generated */ - return (priority + s->priority_add) & 7; - } else { - return -1; - } -} - -/* Update INT output. Must be called every time the output may have changed. */ -static void pic_update_irq(PICCommonState *s) -{ - int irq; - - irq = pic_get_irq(s); - if (irq >= 0) { - DPRINTF("pic%d: imr=%x irr=%x padd=%d\n", - s->master ? 0 : 1, s->imr, s->irr, s->priority_add); - qemu_irq_raise(s->int_out[0]); - } else { - qemu_irq_lower(s->int_out[0]); - } -} - -/* set irq level. If an edge is detected, then the IRR is set to 1 */ -static void pic_set_irq(void *opaque, int irq, int level) -{ - PICCommonState *s = opaque; - int mask = 1 << irq; - -#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) || \ - defined(DEBUG_IRQ_LATENCY) - int irq_index = s->master ? irq : irq + 8; -#endif -#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) - if (level != irq_level[irq_index]) { - DPRINTF("pic_set_irq: irq=%d level=%d\n", irq_index, level); - irq_level[irq_index] = level; -#ifdef DEBUG_IRQ_COUNT - if (level == 1) { - irq_count[irq_index]++; - } -#endif - } -#endif -#ifdef DEBUG_IRQ_LATENCY - if (level) { - irq_time[irq_index] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - } -#endif - - if (s->elcr & mask) { - /* level triggered */ - if (level) { - s->irr |= mask; - s->last_irr |= mask; - } else { - s->irr &= ~mask; - s->last_irr &= ~mask; - } - } else { - /* edge triggered */ - if (level) { - if ((s->last_irr & mask) == 0) { - s->irr |= mask; - } - s->last_irr |= mask; - } else { - s->last_irr &= ~mask; - } - } - pic_update_irq(s); -} - -/* acknowledge interrupt 'irq' */ -static void pic_intack(PICCommonState *s, int irq) -{ - if (s->auto_eoi) { - if (s->rotate_on_auto_eoi) { - s->priority_add = (irq + 1) & 7; - } - } else { - s->isr |= (1 << irq); - } - /* We don't clear a level sensitive interrupt here */ - if (!(s->elcr & (1 << irq))) { - s->irr &= ~(1 << irq); - } - pic_update_irq(s); -} - -int pic_read_irq(DeviceState *d) -{ - PICCommonState *s = PIC_COMMON(d); - int irq, irq2, intno; - - irq = pic_get_irq(s); - if (irq >= 0) { - if (irq == 2) { - irq2 = pic_get_irq(slave_pic); - if (irq2 >= 0) { - pic_intack(slave_pic, irq2); - } else { - /* spurious IRQ on slave controller */ - irq2 = 7; - } - intno = slave_pic->irq_base + irq2; - } else { - intno = s->irq_base + irq; - } - pic_intack(s, irq); - } else { - /* spurious IRQ on host controller */ - irq = 7; - intno = s->irq_base + irq; - } - -#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_LATENCY) - if (irq == 2) { - irq = irq2 + 8; - } -#endif -#ifdef DEBUG_IRQ_LATENCY - printf("IRQ%d latency=%0.3fus\n", - irq, - (double)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - - irq_time[irq]) * 1000000.0 / NANOSECONDS_PER_SECOND); -#endif - DPRINTF("pic_interrupt: irq=%d\n", irq); - return intno; -} - -static void pic_init_reset(PICCommonState *s) -{ - pic_reset_common(s); - pic_update_irq(s); -} - -static void pic_reset(DeviceState *dev) -{ - PICCommonState *s = PIC_COMMON(dev); - - s->elcr = 0; - pic_init_reset(s); -} - -static void pic_ioport_write(void *opaque, hwaddr addr64, - uint64_t val64, unsigned size) -{ - PICCommonState *s = opaque; - uint32_t addr = addr64; - uint32_t val = val64; - int priority, cmd, irq; - - DPRINTF("write: addr=0x%02x val=0x%02x\n", addr, val); - if (addr == 0) { - if (val & 0x10) { - pic_init_reset(s); - s->init_state = 1; - s->init4 = val & 1; - s->single_mode = val & 2; - if (val & 0x08) { - qemu_log_mask(LOG_UNIMP, - "i8259: level sensitive irq not supported\n"); - } - } else if (val & 0x08) { - if (val & 0x04) { - s->poll = 1; - } - if (val & 0x02) { - s->read_reg_select = val & 1; - } - if (val & 0x40) { - s->special_mask = (val >> 5) & 1; - } - } else { - cmd = val >> 5; - switch (cmd) { - case 0: - case 4: - s->rotate_on_auto_eoi = cmd >> 2; - break; - case 1: /* end of interrupt */ - case 5: - priority = get_priority(s, s->isr); - if (priority != 8) { - irq = (priority + s->priority_add) & 7; - s->isr &= ~(1 << irq); - if (cmd == 5) { - s->priority_add = (irq + 1) & 7; - } - pic_update_irq(s); - } - break; - case 3: - irq = val & 7; - s->isr &= ~(1 << irq); - pic_update_irq(s); - break; - case 6: - s->priority_add = (val + 1) & 7; - pic_update_irq(s); - break; - case 7: - irq = val & 7; - s->isr &= ~(1 << irq); - s->priority_add = (irq + 1) & 7; - pic_update_irq(s); - break; - default: - /* no operation */ - break; - } - } - } else { - switch (s->init_state) { - case 0: - /* normal mode */ - s->imr = val; - pic_update_irq(s); - break; - case 1: - s->irq_base = val & 0xf8; - s->init_state = s->single_mode ? (s->init4 ? 3 : 0) : 2; - break; - case 2: - if (s->init4) { - s->init_state = 3; - } else { - s->init_state = 0; - } - break; - case 3: - s->special_fully_nested_mode = (val >> 4) & 1; - s->auto_eoi = (val >> 1) & 1; - s->init_state = 0; - break; - } - } -} - -static uint64_t pic_ioport_read(void *opaque, hwaddr addr, - unsigned size) -{ - PICCommonState *s = opaque; - int ret; - - if (s->poll) { - ret = pic_get_irq(s); - if (ret >= 0) { - pic_intack(s, ret); - ret |= 0x80; - } else { - ret = 0; - } - s->poll = 0; - } else { - if (addr == 0) { - if (s->read_reg_select) { - ret = s->isr; - } else { - ret = s->irr; - } - } else { - ret = s->imr; - } - } - DPRINTF("read: addr=0x%02" HWADDR_PRIx " val=0x%02x\n", addr, ret); - return ret; -} - -int pic_get_output(DeviceState *d) -{ - PICCommonState *s = PIC_COMMON(d); - - return (pic_get_irq(s) >= 0); -} - -static void elcr_ioport_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - PICCommonState *s = opaque; - s->elcr = val & s->elcr_mask; -} - -static uint64_t elcr_ioport_read(void *opaque, hwaddr addr, - unsigned size) -{ - PICCommonState *s = opaque; - return s->elcr; -} - -static const MemoryRegionOps pic_base_ioport_ops = { - .read = pic_ioport_read, - .write = pic_ioport_write, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static const MemoryRegionOps pic_elcr_ioport_ops = { - .read = elcr_ioport_read, - .write = elcr_ioport_write, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void pic_realize(DeviceState *dev, Error **errp) -{ - PICCommonState *s = PIC_COMMON(dev); - PICClass *pc = PIC_GET_CLASS(dev); - - memory_region_init_io(&s->base_io, OBJECT(s), &pic_base_ioport_ops, s, - "pic", 2); - memory_region_init_io(&s->elcr_io, OBJECT(s), &pic_elcr_ioport_ops, s, - "elcr", 1); - - qdev_init_gpio_out(dev, s->int_out, ARRAY_SIZE(s->int_out)); - qdev_init_gpio_in(dev, pic_set_irq, 8); - - pc->parent_realize(dev, errp); -} - -void hmp_info_pic(Monitor *mon, const QDict *qdict) -{ - int i; - PICCommonState *s; - - if (!isa_pic) { - return; - } - for (i = 0; i < 2; i++) { - s = i == 0 ? PIC_COMMON(isa_pic) : slave_pic; - monitor_printf(mon, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d " - "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n", - i, s->irr, s->imr, s->isr, s->priority_add, - s->irq_base, s->read_reg_select, s->elcr, - s->special_fully_nested_mode); - } -} - -void hmp_info_irq(Monitor *mon, const QDict *qdict) -{ -#ifndef DEBUG_IRQ_COUNT - monitor_printf(mon, "irq statistic code not compiled.\n"); -#else - int i; - int64_t count; - - monitor_printf(mon, "IRQ statistics:\n"); - for (i = 0; i < 16; i++) { - count = irq_count[i]; - if (count > 0) { - monitor_printf(mon, "%2d: %" PRId64 "\n", i, count); - } - } -#endif -} - -qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq) -{ - qemu_irq *irq_set; - DeviceState *dev; - ISADevice *isadev; - int i; - - irq_set = g_new0(qemu_irq, ISA_NUM_IRQS); - - isadev = i8259_init_chip(TYPE_I8259, bus, true); - dev = DEVICE(isadev); - - qdev_connect_gpio_out(dev, 0, parent_irq); - for (i = 0 ; i < 8; i++) { - irq_set[i] = qdev_get_gpio_in(dev, i); - } - - isa_pic = dev; - - isadev = i8259_init_chip(TYPE_I8259, bus, false); - dev = DEVICE(isadev); - - qdev_connect_gpio_out(dev, 0, irq_set[2]); - for (i = 0 ; i < 8; i++) { - irq_set[i + 8] = qdev_get_gpio_in(dev, i); - } - - slave_pic = PIC_COMMON(dev); - - return irq_set; -} - -static void i8259_class_init(ObjectClass *klass, void *data) -{ - PICClass *k = PIC_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->parent_realize = dc->realize; - dc->realize = pic_realize; - dc->reset = pic_reset; -} - -static const TypeInfo i8259_info = { - .name = TYPE_I8259, - .instance_size = sizeof(PICCommonState), - .parent = TYPE_PIC_COMMON, - .class_init = i8259_class_init, - .class_size = sizeof(PICClass), -}; - -static void pic_register_types(void) -{ - type_register_static(&i8259_info); -} - -type_init(pic_register_types) diff --git a/qemu/hw/intc/i8259_common.c b/qemu/hw/intc/i8259_common.c deleted file mode 100644 index 3a850b0c6..000000000 --- a/qemu/hw/intc/i8259_common.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * QEMU 8259 - common bits of emulated and KVM kernel model - * - * Copyright (c) 2003-2004 Fabrice Bellard - * Copyright (c) 2011 Jan Kiszka, Siemens AG - * - * 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/i386/pc.h" -#include "hw/isa/i8259_internal.h" - -void pic_reset_common(PICCommonState *s) -{ - s->last_irr = 0; - s->irr &= s->elcr; - s->imr = 0; - s->isr = 0; - s->priority_add = 0; - s->irq_base = 0; - s->read_reg_select = 0; - s->poll = 0; - s->special_mask = 0; - s->init_state = 0; - s->auto_eoi = 0; - s->rotate_on_auto_eoi = 0; - s->special_fully_nested_mode = 0; - s->init4 = 0; - s->single_mode = 0; - /* Note: ELCR is not reset */ -} - -static void pic_dispatch_pre_save(void *opaque) -{ - PICCommonState *s = opaque; - PICCommonClass *info = PIC_COMMON_GET_CLASS(s); - - if (info->pre_save) { - info->pre_save(s); - } -} - -static int pic_dispatch_post_load(void *opaque, int version_id) -{ - PICCommonState *s = opaque; - PICCommonClass *info = PIC_COMMON_GET_CLASS(s); - - if (info->post_load) { - info->post_load(s); - } - return 0; -} - -static void pic_common_realize(DeviceState *dev, Error **errp) -{ - PICCommonState *s = PIC_COMMON(dev); - - isa_register_ioport(NULL, &s->base_io, s->iobase); - if (s->elcr_addr != -1) { - isa_register_ioport(NULL, &s->elcr_io, s->elcr_addr); - } - - qdev_set_legacy_instance_id(dev, s->iobase, 1); -} - -ISADevice *i8259_init_chip(const char *name, ISABus *bus, bool master) -{ - DeviceState *dev; - ISADevice *isadev; - - isadev = isa_create(bus, name); - dev = DEVICE(isadev); - qdev_prop_set_uint32(dev, "iobase", master ? 0x20 : 0xa0); - qdev_prop_set_uint32(dev, "elcr_addr", master ? 0x4d0 : 0x4d1); - qdev_prop_set_uint8(dev, "elcr_mask", master ? 0xf8 : 0xde); - qdev_prop_set_bit(dev, "master", master); - qdev_init_nofail(dev); - - return isadev; -} - -static const VMStateDescription vmstate_pic_common = { - .name = "i8259", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = pic_dispatch_pre_save, - .post_load = pic_dispatch_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(last_irr, PICCommonState), - VMSTATE_UINT8(irr, PICCommonState), - VMSTATE_UINT8(imr, PICCommonState), - VMSTATE_UINT8(isr, PICCommonState), - VMSTATE_UINT8(priority_add, PICCommonState), - VMSTATE_UINT8(irq_base, PICCommonState), - VMSTATE_UINT8(read_reg_select, PICCommonState), - VMSTATE_UINT8(poll, PICCommonState), - VMSTATE_UINT8(special_mask, PICCommonState), - VMSTATE_UINT8(init_state, PICCommonState), - VMSTATE_UINT8(auto_eoi, PICCommonState), - VMSTATE_UINT8(rotate_on_auto_eoi, PICCommonState), - VMSTATE_UINT8(special_fully_nested_mode, PICCommonState), - VMSTATE_UINT8(init4, PICCommonState), - VMSTATE_UINT8(single_mode, PICCommonState), - VMSTATE_UINT8(elcr, PICCommonState), - VMSTATE_END_OF_LIST() - } -}; - -static Property pic_properties_common[] = { - DEFINE_PROP_UINT32("iobase", PICCommonState, iobase, -1), - DEFINE_PROP_UINT32("elcr_addr", PICCommonState, elcr_addr, -1), - DEFINE_PROP_UINT8("elcr_mask", PICCommonState, elcr_mask, -1), - DEFINE_PROP_BIT("master", PICCommonState, master, 0, false), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pic_common_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_pic_common; - dc->props = pic_properties_common; - dc->realize = pic_common_realize; - /* - * Reason: unlike ordinary ISA devices, the PICs need additional - * wiring: its IRQ input lines are set up by board code, and the - * wiring of the slave to the master is hard-coded in device model - * code. - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo pic_common_type = { - .name = TYPE_PIC_COMMON, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(PICCommonState), - .class_size = sizeof(PICCommonClass), - .class_init = pic_common_class_init, - .abstract = true, -}; - -static void pic_common_register_types(void) -{ - type_register_static(&pic_common_type); -} - -type_init(pic_common_register_types) diff --git a/qemu/hw/intc/imx_avic.c b/qemu/hw/intc/imx_avic.c deleted file mode 100644 index 702765577..000000000 --- a/qemu/hw/intc/imx_avic.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - * i.MX31 Vectored Interrupt Controller - * - * Note this is NOT the PL192 provided by ARM, but - * a custom implementation by Freescale. - * - * Copyright (c) 2008 OKL - * Copyright (c) 2011 NICTA Pty Ltd - * Originally written by Hans Jiang - * Updated by Jean-Christophe Dubois - * - * This code is licensed under the GPL version 2 or later. See - * the COPYING file in the top-level directory. - * - * TODO: implement vectors. - */ - -#include "qemu/osdep.h" -#include "hw/intc/imx_avic.h" - -#ifndef DEBUG_IMX_AVIC -#define DEBUG_IMX_AVIC 0 -#endif - -#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 = TYPE_IMX_AVIC, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT64(pending, IMXAVICState), - VMSTATE_UINT64(enabled, IMXAVICState), - VMSTATE_UINT64(is_fiq, IMXAVICState), - VMSTATE_UINT32(intcntl, IMXAVICState), - VMSTATE_UINT32(intmask, IMXAVICState), - VMSTATE_UINT32_ARRAY(prio, IMXAVICState, PRIO_WORDS), - VMSTATE_END_OF_LIST() - }, -}; - -static inline int imx_avic_prio(IMXAVICState *s, int irq) -{ - uint32_t word = irq / PRIO_PER_WORD; - uint32_t part = 4 * (irq % PRIO_PER_WORD); - return 0xf & (s->prio[word] >> part); -} - -/* Update interrupts. */ -static void imx_avic_update(IMXAVICState *s) -{ - int i; - uint64_t new = s->pending & s->enabled; - uint64_t flags; - - flags = new & s->is_fiq; - qemu_set_irq(s->fiq, !!flags); - - flags = new & ~s->is_fiq; - if (!flags || (s->intmask == 0x1f)) { - qemu_set_irq(s->irq, !!flags); - return; - } - - /* - * Take interrupt if there's a pending interrupt with - * priority higher than the value of intmask - */ - for (i = 0; i < IMX_AVIC_NUM_IRQS; i++) { - if (flags & (1UL << i)) { - if (imx_avic_prio(s, i) > s->intmask) { - qemu_set_irq(s->irq, 1); - return; - } - } - } - qemu_set_irq(s->irq, 0); -} - -static void imx_avic_set_irq(void *opaque, int irq, int level) -{ - IMXAVICState *s = (IMXAVICState *)opaque; - - if (level) { - DPRINTF("Raising IRQ %d, prio %d\n", - irq, imx_avic_prio(s, irq)); - s->pending |= (1ULL << irq); - } else { - DPRINTF("Clearing IRQ %d, prio %d\n", - irq, imx_avic_prio(s, irq)); - s->pending &= ~(1ULL << irq); - } - - imx_avic_update(s); -} - - -static uint64_t imx_avic_read(void *opaque, - hwaddr offset, unsigned size) -{ - IMXAVICState *s = (IMXAVICState *)opaque; - - DPRINTF("read(offset = 0x%" HWADDR_PRIx ")\n", offset); - - switch (offset >> 2) { - case 0: /* INTCNTL */ - return s->intcntl; - - case 1: /* Normal Interrupt Mask Register, NIMASK */ - return s->intmask; - - case 2: /* Interrupt Enable Number Register, INTENNUM */ - case 3: /* Interrupt Disable Number Register, INTDISNUM */ - return 0; - - case 4: /* Interrupt Enabled Number Register High */ - return s->enabled >> 32; - - case 5: /* Interrupt Enabled Number Register Low */ - return s->enabled & 0xffffffffULL; - - case 6: /* Interrupt Type Register High */ - return s->is_fiq >> 32; - - case 7: /* Interrupt Type Register Low */ - return s->is_fiq & 0xffffffffULL; - - case 8: /* Normal Interrupt Priority Register 7 */ - case 9: /* Normal Interrupt Priority Register 6 */ - case 10:/* Normal Interrupt Priority Register 5 */ - case 11:/* Normal Interrupt Priority Register 4 */ - case 12:/* Normal Interrupt Priority Register 3 */ - case 13:/* Normal Interrupt Priority Register 2 */ - case 14:/* Normal Interrupt Priority Register 1 */ - case 15:/* Normal Interrupt Priority Register 0 */ - return s->prio[15-(offset>>2)]; - - case 16: /* Normal interrupt vector and status register */ - { - /* - * This returns the highest priority - * outstanding interrupt. Where there is more than - * one pending IRQ with the same priority, - * take the highest numbered one. - */ - uint64_t flags = s->pending & s->enabled & ~s->is_fiq; - int i; - int prio = -1; - int irq = -1; - for (i = 63; i >= 0; --i) { - if (flags & (1ULL< prio) { - irq = i; - prio = irq_prio; - } - } - } - if (irq >= 0) { - imx_avic_set_irq(s, irq, 0); - return irq << 16 | prio; - } - return 0xffffffffULL; - } - case 17:/* Fast Interrupt vector and status register */ - { - uint64_t flags = s->pending & s->enabled & s->is_fiq; - int i = ctz64(flags); - if (i < 64) { - imx_avic_set_irq(opaque, i, 0); - return i; - } - return 0xffffffffULL; - } - case 18:/* Interrupt source register high */ - return s->pending >> 32; - - case 19:/* Interrupt source register low */ - return s->pending & 0xffffffffULL; - - case 20:/* Interrupt Force Register high */ - case 21:/* Interrupt Force Register low */ - return 0; - - case 22:/* Normal Interrupt Pending Register High */ - return (s->pending & s->enabled & ~s->is_fiq) >> 32; - - case 23:/* Normal Interrupt Pending Register Low */ - return (s->pending & s->enabled & ~s->is_fiq) & 0xffffffffULL; - - case 24: /* Fast Interrupt Pending Register High */ - return (s->pending & s->enabled & s->is_fiq) >> 32; - - case 25: /* Fast Interrupt Pending Register Low */ - return (s->pending & s->enabled & s->is_fiq) & 0xffffffffULL; - - case 0x40: /* AVIC vector 0, use for WFI WAR */ - return 0x4; - - default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" - HWADDR_PRIx "\n", TYPE_IMX_AVIC, __func__, offset); - return 0; - } -} - -static void imx_avic_write(void *opaque, hwaddr offset, - uint64_t val, unsigned size) -{ - IMXAVICState *s = (IMXAVICState *)opaque; - - /* Vector Registers not yet supported */ - if (offset >= 0x100 && offset <= 0x2fc) { - qemu_log_mask(LOG_UNIMP, "[%s]%s: vector %d ignored\n", - TYPE_IMX_AVIC, __func__, (int)((offset - 0x100) >> 2)); - return; - } - - 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); - if (s->intcntl & ABFEN) { - s->intcntl &= ~(val & ABFLAG); - } - break; - - case 1: /* Normal Interrupt Mask Register, NIMASK */ - s->intmask = val & 0x1f; - break; - - case 2: /* Interrupt Enable Number Register, INTENNUM */ - DPRINTF("enable(%d)\n", (int)val); - val &= 0x3f; - s->enabled |= (1ULL << val); - break; - - case 3: /* Interrupt Disable Number Register, INTDISNUM */ - DPRINTF("disable(%d)\n", (int)val); - val &= 0x3f; - s->enabled &= ~(1ULL << val); - break; - - case 4: /* Interrupt Enable Number Register High */ - s->enabled = (s->enabled & 0xffffffffULL) | (val << 32); - break; - - case 5: /* Interrupt Enable Number Register Low */ - s->enabled = (s->enabled & 0xffffffff00000000ULL) | val; - break; - - case 6: /* Interrupt Type Register High */ - s->is_fiq = (s->is_fiq & 0xffffffffULL) | (val << 32); - break; - - case 7: /* Interrupt Type Register Low */ - s->is_fiq = (s->is_fiq & 0xffffffff00000000ULL) | val; - break; - - case 8: /* Normal Interrupt Priority Register 7 */ - case 9: /* Normal Interrupt Priority Register 6 */ - case 10:/* Normal Interrupt Priority Register 5 */ - case 11:/* Normal Interrupt Priority Register 4 */ - case 12:/* Normal Interrupt Priority Register 3 */ - case 13:/* Normal Interrupt Priority Register 2 */ - case 14:/* Normal Interrupt Priority Register 1 */ - case 15:/* Normal Interrupt Priority Register 0 */ - s->prio[15-(offset>>2)] = val; - break; - - /* Read-only registers, writes ignored */ - case 16:/* Normal Interrupt Vector and Status register */ - case 17:/* Fast Interrupt vector and status register */ - case 18:/* Interrupt source register high */ - case 19:/* Interrupt source register low */ - return; - - case 20:/* Interrupt Force Register high */ - s->pending = (s->pending & 0xffffffffULL) | (val << 32); - break; - - case 21:/* Interrupt Force Register low */ - s->pending = (s->pending & 0xffffffff00000000ULL) | val; - break; - - case 22:/* Normal Interrupt Pending Register High */ - case 23:/* Normal Interrupt Pending Register Low */ - case 24: /* Fast Interrupt Pending Register High */ - case 25: /* Fast Interrupt Pending Register Low */ - return; - - default: - 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); -} - -static const MemoryRegionOps imx_avic_ops = { - .read = imx_avic_read, - .write = imx_avic_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void imx_avic_reset(DeviceState *dev) -{ - IMXAVICState *s = IMX_AVIC(dev); - - s->pending = 0; - s->enabled = 0; - s->is_fiq = 0; - s->intmask = 0x1f; - s->intcntl = 0; - memset(s->prio, 0, sizeof s->prio); -} - -static int imx_avic_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - IMXAVICState *s = IMX_AVIC(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &imx_avic_ops, s, - TYPE_IMX_AVIC, 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - - qdev_init_gpio_in(dev, imx_avic_set_irq, IMX_AVIC_NUM_IRQS); - sysbus_init_irq(sbd, &s->irq); - sysbus_init_irq(sbd, &s->fiq); - - return 0; -} - - -static void imx_avic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = imx_avic_init; - dc->vmsd = &vmstate_imx_avic; - dc->reset = imx_avic_reset; - dc->desc = "i.MX Advanced Vector Interrupt Controller"; -} - -static const TypeInfo imx_avic_info = { - .name = TYPE_IMX_AVIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IMXAVICState), - .class_init = imx_avic_class_init, -}; - -static void imx_avic_register_types(void) -{ - type_register_static(&imx_avic_info); -} - -type_init(imx_avic_register_types) diff --git a/qemu/hw/intc/ioapic.c b/qemu/hw/intc/ioapic.c deleted file mode 100644 index 378e663f6..000000000 --- a/qemu/hw/intc/ioapic.c +++ /dev/null @@ -1,338 +0,0 @@ -/* - * ioapic.c IOAPIC emulation logic - * - * Copyright (c) 2004-2005 Fabrice Bellard - * - * Split the ioapic logic from apic.c - * Xiantao Zhang - * - * 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 . - */ - -#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 - -#ifdef DEBUG_IOAPIC -#define DPRINTF(fmt, ...) \ - do { printf("ioapic: " fmt , ## __VA_ARGS__); } while (0) -#else -#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 */ -extern int ioapic_no; - -static void ioapic_service(IOAPICCommonState *s) -{ - uint8_t i; - uint8_t trig_mode; - uint8_t vector; - uint8_t delivery_mode; - uint32_t mask; - uint64_t entry; - uint8_t dest; - uint8_t dest_mode; - - 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); - dest = entry >> IOAPIC_LVT_DEST_SHIFT; - dest_mode = (entry >> IOAPIC_LVT_DEST_MODE_SHIFT) & 1; - delivery_mode = - (entry >> IOAPIC_LVT_DELIV_MODE_SHIFT) & IOAPIC_DM_MASK; - 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) { - vector = pic_read_irq(isa_pic); - } else { - vector = entry & IOAPIC_VECTOR_MASK; - } -#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); - } - } - } -} - -static void ioapic_set_irq(void *opaque, int vector, int level) -{ - IOAPICCommonState *s = opaque; - - /* ISA IRQs map to GSI 1-1 except for IRQ0 which maps - * to GSI 2. GSI maps to ioapic 1-1. This is not - * the cleanest way of doing it but it should work. */ - - DPRINTF("%s: %s vec %x\n", __func__, level ? "raise" : "lower", vector); - if (vector == 0) { - vector = 2; - } - if (vector >= 0 && vector < IOAPIC_NUM_PINS) { - uint32_t mask = 1 << vector; - uint64_t entry = s->ioredtbl[vector]; - - if (((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1) == - IOAPIC_TRIGGER_LEVEL) { - /* level triggered */ - if (level) { - s->irr |= mask; - if (!(entry & IOAPIC_LVT_REMOTE_IRR)) { - ioapic_service(s); - } - } else { - s->irr &= ~mask; - } - } else { - /* According to the 82093AA manual, we must ignore edge requests - * if the input pin is masked. */ - if (level && !(entry & IOAPIC_LVT_MASKED)) { - s->irr |= mask; - ioapic_service(s); - } - } - } -} - -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; - uint64_t entry; - int i, n; - - for (i = 0; i < MAX_IOAPICS; i++) { - s = ioapics[i]; - if (!s) { - continue; - } - for (n = 0; n < IOAPIC_NUM_PINS; n++) { - entry = s->ioredtbl[n]; - if ((entry & IOAPIC_LVT_REMOTE_IRR) - && (entry & IOAPIC_VECTOR_MASK) == vector) { - s->ioredtbl[n] = entry & ~IOAPIC_LVT_REMOTE_IRR; - if (!(entry & IOAPIC_LVT_MASKED) && (s->irr & (1 << n))) { - ioapic_service(s); - } - } - } - } -} - -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) -{ - IOAPICCommonState *s = opaque; - int index; - uint32_t val = 0; - - switch (addr & 0xff) { - case IOAPIC_IOREGSEL: - val = s->ioregsel; - break; - case IOAPIC_IOWIN: - if (size != 4) { - break; - } - 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; - default: - index = (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1; - if (index >= 0 && index < IOAPIC_NUM_PINS) { - if (s->ioregsel & 1) { - val = s->ioredtbl[index] >> 32; - } else { - val = s->ioredtbl[index] & 0xffffffff; - } - } - } - DPRINTF("read: %08x = %08x\n", s->ioregsel, val); - break; - } - return val; -} - -static void -ioapic_mem_write(void *opaque, hwaddr addr, uint64_t val, - unsigned int size) -{ - IOAPICCommonState *s = opaque; - int index; - - switch (addr & 0xff) { - case IOAPIC_IOREGSEL: - s->ioregsel = val; - break; - case IOAPIC_IOWIN: - if (size != 4) { - break; - } - DPRINTF("write: %08x = %08" PRIx64 "\n", s->ioregsel, val); - switch (s->ioregsel) { - case IOAPIC_REG_ID: - s->id = (val >> IOAPIC_ID_SHIFT) & IOAPIC_ID_MASK; - break; - case IOAPIC_REG_VER: - case IOAPIC_REG_ARB: - break; - default: - index = (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1; - if (index >= 0 && index < IOAPIC_NUM_PINS) { - if (s->ioregsel & 1) { - s->ioredtbl[index] &= 0xffffffff; - s->ioredtbl[index] |= (uint64_t)val << 32; - } else { - s->ioredtbl[index] &= ~0xffffffffULL; - s->ioredtbl[index] |= val; - } - ioapic_service(s); - } - } - break; - } - - ioapic_update_kvm_routes(s); -} - -static const MemoryRegionOps ioapic_io_ops = { - .read = ioapic_mem_read, - .write = ioapic_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void ioapic_realize(DeviceState *dev, Error **errp) -{ - IOAPICCommonState *s = IOAPIC_COMMON(dev); - - memory_region_init_io(&s->io_memory, OBJECT(s), &ioapic_io_ops, s, - "ioapic", 0x1000); - - qdev_init_gpio_in(dev, ioapic_set_irq, IOAPIC_NUM_PINS); - - ioapics[ioapic_no] = s; -} - -static void ioapic_class_init(ObjectClass *klass, void *data) -{ - IOAPICCommonClass *k = IOAPIC_COMMON_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = ioapic_realize; - dc->reset = ioapic_reset_common; -} - -static const TypeInfo ioapic_info = { - .name = "ioapic", - .parent = TYPE_IOAPIC_COMMON, - .instance_size = sizeof(IOAPICCommonState), - .class_init = ioapic_class_init, -}; - -static void ioapic_register_types(void) -{ - type_register_static(&ioapic_info); -} - -type_init(ioapic_register_types) diff --git a/qemu/hw/intc/ioapic_common.c b/qemu/hw/intc/ioapic_common.c deleted file mode 100644 index 1b7ec5ec2..000000000 --- a/qemu/hw/intc/ioapic_common.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * IOAPIC emulation logic - common bits of emulated and KVM kernel model - * - * Copyright (c) 2004-2005 Fabrice Bellard - * Copyright (c) 2009 Xiantao Zhang, Intel - * Copyright (c) 2011 Jan Kiszka, Siemens AG - * - * 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 . - */ - -#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" - -/* ioapic_no count start from 0 to MAX_IOAPICS, - * remove as static variable from ioapic_common_init. - * now as a global variable, let child to increase the counter - * then we can drop the 'instance_no' argument - * and convert to our QOM's realize function - */ -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); - int i; - - s->id = 0; - s->ioregsel = 0; - s->irr = 0; - for (i = 0; i < IOAPIC_NUM_PINS; i++) { - s->ioredtbl[i] = 1 << IOAPIC_LVT_MASKED_SHIFT; - } -} - -static void ioapic_dispatch_pre_save(void *opaque) -{ - IOAPICCommonState *s = IOAPIC_COMMON(opaque); - IOAPICCommonClass *info = IOAPIC_COMMON_GET_CLASS(s); - - if (info->pre_save) { - info->pre_save(s); - } -} - -static int ioapic_dispatch_post_load(void *opaque, int version_id) -{ - IOAPICCommonState *s = IOAPIC_COMMON(opaque); - IOAPICCommonClass *info = IOAPIC_COMMON_GET_CLASS(s); - - if (info->post_load) { - info->post_load(s); - } - return 0; -} - -static void ioapic_common_realize(DeviceState *dev, Error **errp) -{ - IOAPICCommonState *s = IOAPIC_COMMON(dev); - IOAPICCommonClass *info; - - if (ioapic_no >= MAX_IOAPICS) { - error_setg(errp, "Only %d ioapics allowed", MAX_IOAPICS); - return; - } - - info = IOAPIC_COMMON_GET_CLASS(s); - info->realize(dev, errp); - - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->io_memory); - ioapic_no++; -} - -static const VMStateDescription vmstate_ioapic_common = { - .name = "ioapic", - .version_id = 3, - .minimum_version_id = 1, - .pre_save = ioapic_dispatch_pre_save, - .post_load = ioapic_dispatch_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(id, IOAPICCommonState), - VMSTATE_UINT8(ioregsel, IOAPICCommonState), - VMSTATE_UNUSED_V(2, 8), /* to account for qemu-kvm's v2 format */ - VMSTATE_UINT32_V(irr, IOAPICCommonState, 2), - VMSTATE_UINT64_ARRAY(ioredtbl, IOAPICCommonState, IOAPIC_NUM_PINS), - VMSTATE_END_OF_LIST() - } -}; - -static void ioapic_common_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = ioapic_common_realize; - dc->vmsd = &vmstate_ioapic_common; -} - -static const TypeInfo ioapic_common_type = { - .name = TYPE_IOAPIC_COMMON, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IOAPICCommonState), - .class_size = sizeof(IOAPICCommonClass), - .class_init = ioapic_common_class_init, - .abstract = true, -}; - -static void ioapic_common_register_types(void) -{ - type_register_static(&ioapic_common_type); -} - -type_init(ioapic_common_register_types) diff --git a/qemu/hw/intc/lm32_pic.c b/qemu/hw/intc/lm32_pic.c deleted file mode 100644 index edc08f184..000000000 --- a/qemu/hw/intc/lm32_pic.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * LatticeMico32 CPU interrupt controller logic. - * - * Copyright (c) 2010 Michael Walle - * - * 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 . - */ - -#include "qemu/osdep.h" - -#include "hw/hw.h" -#include "hw/i386/pc.h" -#include "monitor/monitor.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "hw/lm32/lm32_pic.h" - -#define TYPE_LM32_PIC "lm32-pic" -#define LM32_PIC(obj) OBJECT_CHECK(LM32PicState, (obj), TYPE_LM32_PIC) - -struct LM32PicState { - SysBusDevice parent_obj; - - qemu_irq parent_irq; - uint32_t im; /* interrupt mask */ - uint32_t ip; /* interrupt pending */ - uint32_t irq_state; - - /* statistics */ - uint32_t stats_irq_count[32]; -}; -typedef struct LM32PicState LM32PicState; - -static LM32PicState *pic; -void lm32_hmp_info_pic(Monitor *mon, const QDict *qdict) -{ - if (pic == NULL) { - return; - } - - monitor_printf(mon, "lm32-pic: im=%08x ip=%08x irq_state=%08x\n", - pic->im, pic->ip, pic->irq_state); -} - -void lm32_hmp_info_irq(Monitor *mon, const QDict *qdict) -{ - int i; - uint32_t count; - - if (pic == NULL) { - return; - } - - monitor_printf(mon, "IRQ statistics:\n"); - for (i = 0; i < 32; i++) { - count = pic->stats_irq_count[i]; - if (count > 0) { - monitor_printf(mon, "%2d: %u\n", i, count); - } - } -} - -static void update_irq(LM32PicState *s) -{ - s->ip |= s->irq_state; - - if (s->ip & s->im) { - trace_lm32_pic_raise_irq(); - qemu_irq_raise(s->parent_irq); - } else { - trace_lm32_pic_lower_irq(); - qemu_irq_lower(s->parent_irq); - } -} - -static void irq_handler(void *opaque, int irq, int level) -{ - LM32PicState *s = opaque; - - assert(irq < 32); - trace_lm32_pic_interrupt(irq, level); - - if (level) { - s->irq_state |= (1 << irq); - s->stats_irq_count[irq]++; - } else { - s->irq_state &= ~(1 << irq); - } - - update_irq(s); -} - -void lm32_pic_set_im(DeviceState *d, uint32_t im) -{ - LM32PicState *s = LM32_PIC(d); - - trace_lm32_pic_set_im(im); - s->im = im; - - update_irq(s); -} - -void lm32_pic_set_ip(DeviceState *d, uint32_t ip) -{ - LM32PicState *s = LM32_PIC(d); - - trace_lm32_pic_set_ip(ip); - - /* ack interrupt */ - s->ip &= ~ip; - - update_irq(s); -} - -uint32_t lm32_pic_get_im(DeviceState *d) -{ - LM32PicState *s = LM32_PIC(d); - - trace_lm32_pic_get_im(s->im); - return s->im; -} - -uint32_t lm32_pic_get_ip(DeviceState *d) -{ - LM32PicState *s = LM32_PIC(d); - - trace_lm32_pic_get_ip(s->ip); - return s->ip; -} - -static void pic_reset(DeviceState *d) -{ - LM32PicState *s = LM32_PIC(d); - int i; - - s->im = 0; - s->ip = 0; - s->irq_state = 0; - for (i = 0; i < 32; i++) { - s->stats_irq_count[i] = 0; - } -} - -static int lm32_pic_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - LM32PicState *s = LM32_PIC(dev); - - qdev_init_gpio_in(dev, irq_handler, 32); - sysbus_init_irq(sbd, &s->parent_irq); - - pic = s; - - return 0; -} - -static const VMStateDescription vmstate_lm32_pic = { - .name = "lm32-pic", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(im, LM32PicState), - VMSTATE_UINT32(ip, LM32PicState), - VMSTATE_UINT32(irq_state, LM32PicState), - VMSTATE_UINT32_ARRAY(stats_irq_count, LM32PicState, 32), - VMSTATE_END_OF_LIST() - } -}; - -static void lm32_pic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = lm32_pic_init; - dc->reset = pic_reset; - dc->vmsd = &vmstate_lm32_pic; -} - -static const TypeInfo lm32_pic_info = { - .name = TYPE_LM32_PIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(LM32PicState), - .class_init = lm32_pic_class_init, -}; - -static void lm32_pic_register_types(void) -{ - type_register_static(&lm32_pic_info); -} - -type_init(lm32_pic_register_types) diff --git a/qemu/hw/intc/omap_intc.c b/qemu/hw/intc/omap_intc.c deleted file mode 100644 index 336882510..000000000 --- a/qemu/hw/intc/omap_intc.c +++ /dev/null @@ -1,672 +0,0 @@ -/* - * TI OMAP interrupt controller emulation. - * - * Copyright (C) 2006-2008 Andrzej Zaborowski - * Copyright (C) 2007-2008 Nokia Corporation - * - * 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 . - */ -#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 { - uint32_t irqs; - uint32_t inputs; - uint32_t mask; - uint32_t fiq; - uint32_t sens_edge; - uint32_t swi; - unsigned char priority[32]; -}; - -#define TYPE_OMAP_INTC "common-omap-intc" -#define OMAP_INTC(obj) \ - OBJECT_CHECK(struct omap_intr_handler_s, (obj), TYPE_OMAP_INTC) - -struct omap_intr_handler_s { - SysBusDevice parent_obj; - - qemu_irq *pins; - qemu_irq parent_intr[2]; - MemoryRegion mmio; - void *iclk; - void *fclk; - unsigned char nbanks; - int level_only; - uint32_t size; - - uint8_t revision; - - /* state */ - uint32_t new_agr[2]; - int sir_intr[2]; - int autoidle; - uint32_t mask; - struct omap_intr_handler_bank_s bank[3]; -}; - -static void omap_inth_sir_update(struct omap_intr_handler_s *s, int is_fiq) -{ - int i, j, sir_intr, p_intr, p; - uint32_t level; - sir_intr = 0; - p_intr = 255; - - /* Find the interrupt line with the highest dynamic priority. - * Note: 0 denotes the hightest priority. - * If all interrupts have the same priority, the default order is IRQ_N, - * IRQ_N-1,...,IRQ_0. */ - for (j = 0; j < s->nbanks; ++j) { - level = s->bank[j].irqs & ~s->bank[j].mask & - (is_fiq ? s->bank[j].fiq : ~s->bank[j].fiq); - - while (level != 0) { - i = ctz32(level); - p = s->bank[j].priority[i]; - if (p <= p_intr) { - p_intr = p; - sir_intr = 32 * j + i; - } - level &= level - 1; - } - } - s->sir_intr[is_fiq] = sir_intr; -} - -static inline void omap_inth_update(struct omap_intr_handler_s *s, int is_fiq) -{ - int i; - uint32_t has_intr = 0; - - for (i = 0; i < s->nbanks; ++i) - has_intr |= s->bank[i].irqs & ~s->bank[i].mask & - (is_fiq ? s->bank[i].fiq : ~s->bank[i].fiq); - - if (s->new_agr[is_fiq] & has_intr & s->mask) { - s->new_agr[is_fiq] = 0; - omap_inth_sir_update(s, is_fiq); - qemu_set_irq(s->parent_intr[is_fiq], 1); - } -} - -#define INT_FALLING_EDGE 0 -#define INT_LOW_LEVEL 1 - -static void omap_set_intr(void *opaque, int irq, int req) -{ - struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque; - uint32_t rise; - - struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5]; - int n = irq & 31; - - if (req) { - rise = ~bank->irqs & (1 << n); - if (~bank->sens_edge & (1 << n)) - rise &= ~bank->inputs; - - bank->inputs |= (1 << n); - if (rise) { - bank->irqs |= rise; - omap_inth_update(ih, 0); - omap_inth_update(ih, 1); - } - } else { - rise = bank->sens_edge & bank->irqs & (1 << n); - bank->irqs &= ~rise; - bank->inputs &= ~(1 << n); - } -} - -/* Simplified version with no edge detection */ -static void omap_set_intr_noedge(void *opaque, int irq, int req) -{ - struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque; - uint32_t rise; - - struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5]; - int n = irq & 31; - - if (req) { - rise = ~bank->inputs & (1 << n); - if (rise) { - bank->irqs |= bank->inputs |= rise; - omap_inth_update(ih, 0); - omap_inth_update(ih, 1); - } - } else - bank->irqs = (bank->inputs &= ~(1 << n)) | bank->swi; -} - -static uint64_t omap_inth_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; - int i, offset = addr; - int bank_no = offset >> 8; - int line_no; - struct omap_intr_handler_bank_s *bank = &s->bank[bank_no]; - offset &= 0xff; - - switch (offset) { - case 0x00: /* ITR */ - return bank->irqs; - - case 0x04: /* MIR */ - return bank->mask; - - case 0x10: /* SIR_IRQ_CODE */ - case 0x14: /* SIR_FIQ_CODE */ - if (bank_no != 0) - break; - line_no = s->sir_intr[(offset - 0x10) >> 2]; - bank = &s->bank[line_no >> 5]; - i = line_no & 31; - if (((bank->sens_edge >> i) & 1) == INT_FALLING_EDGE) - bank->irqs &= ~(1 << i); - return line_no; - - case 0x18: /* CONTROL_REG */ - if (bank_no != 0) - break; - return 0; - - case 0x1c: /* ILR0 */ - case 0x20: /* ILR1 */ - case 0x24: /* ILR2 */ - case 0x28: /* ILR3 */ - case 0x2c: /* ILR4 */ - case 0x30: /* ILR5 */ - case 0x34: /* ILR6 */ - case 0x38: /* ILR7 */ - case 0x3c: /* ILR8 */ - case 0x40: /* ILR9 */ - case 0x44: /* ILR10 */ - case 0x48: /* ILR11 */ - case 0x4c: /* ILR12 */ - case 0x50: /* ILR13 */ - case 0x54: /* ILR14 */ - case 0x58: /* ILR15 */ - case 0x5c: /* ILR16 */ - case 0x60: /* ILR17 */ - case 0x64: /* ILR18 */ - case 0x68: /* ILR19 */ - case 0x6c: /* ILR20 */ - case 0x70: /* ILR21 */ - case 0x74: /* ILR22 */ - case 0x78: /* ILR23 */ - case 0x7c: /* ILR24 */ - case 0x80: /* ILR25 */ - case 0x84: /* ILR26 */ - case 0x88: /* ILR27 */ - case 0x8c: /* ILR28 */ - case 0x90: /* ILR29 */ - case 0x94: /* ILR30 */ - case 0x98: /* ILR31 */ - i = (offset - 0x1c) >> 2; - return (bank->priority[i] << 2) | - (((bank->sens_edge >> i) & 1) << 1) | - ((bank->fiq >> i) & 1); - - case 0x9c: /* ISR */ - return 0x00000000; - - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_inth_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; - int i, offset = addr; - int bank_no = offset >> 8; - struct omap_intr_handler_bank_s *bank = &s->bank[bank_no]; - offset &= 0xff; - - switch (offset) { - case 0x00: /* ITR */ - /* Important: ignore the clearing if the IRQ is level-triggered and - the input bit is 1 */ - bank->irqs &= value | (bank->inputs & bank->sens_edge); - return; - - case 0x04: /* MIR */ - bank->mask = value; - omap_inth_update(s, 0); - omap_inth_update(s, 1); - return; - - case 0x10: /* SIR_IRQ_CODE */ - case 0x14: /* SIR_FIQ_CODE */ - OMAP_RO_REG(addr); - break; - - case 0x18: /* CONTROL_REG */ - if (bank_no != 0) - break; - if (value & 2) { - qemu_set_irq(s->parent_intr[1], 0); - s->new_agr[1] = ~0; - omap_inth_update(s, 1); - } - if (value & 1) { - qemu_set_irq(s->parent_intr[0], 0); - s->new_agr[0] = ~0; - omap_inth_update(s, 0); - } - return; - - case 0x1c: /* ILR0 */ - case 0x20: /* ILR1 */ - case 0x24: /* ILR2 */ - case 0x28: /* ILR3 */ - case 0x2c: /* ILR4 */ - case 0x30: /* ILR5 */ - case 0x34: /* ILR6 */ - case 0x38: /* ILR7 */ - case 0x3c: /* ILR8 */ - case 0x40: /* ILR9 */ - case 0x44: /* ILR10 */ - case 0x48: /* ILR11 */ - case 0x4c: /* ILR12 */ - case 0x50: /* ILR13 */ - case 0x54: /* ILR14 */ - case 0x58: /* ILR15 */ - case 0x5c: /* ILR16 */ - case 0x60: /* ILR17 */ - case 0x64: /* ILR18 */ - case 0x68: /* ILR19 */ - case 0x6c: /* ILR20 */ - case 0x70: /* ILR21 */ - case 0x74: /* ILR22 */ - case 0x78: /* ILR23 */ - case 0x7c: /* ILR24 */ - case 0x80: /* ILR25 */ - case 0x84: /* ILR26 */ - case 0x88: /* ILR27 */ - case 0x8c: /* ILR28 */ - case 0x90: /* ILR29 */ - case 0x94: /* ILR30 */ - case 0x98: /* ILR31 */ - i = (offset - 0x1c) >> 2; - bank->priority[i] = (value >> 2) & 0x1f; - bank->sens_edge &= ~(1 << i); - bank->sens_edge |= ((value >> 1) & 1) << i; - bank->fiq &= ~(1 << i); - bank->fiq |= (value & 1) << i; - return; - - case 0x9c: /* ISR */ - for (i = 0; i < 32; i ++) - if (value & (1 << i)) { - omap_set_intr(s, 32 * bank_no + i, 1); - return; - } - return; - } - OMAP_BAD_REG(addr); -} - -static const MemoryRegionOps omap_inth_mem_ops = { - .read = omap_inth_read, - .write = omap_inth_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void omap_inth_reset(DeviceState *dev) -{ - struct omap_intr_handler_s *s = OMAP_INTC(dev); - int i; - - for (i = 0; i < s->nbanks; ++i){ - s->bank[i].irqs = 0x00000000; - s->bank[i].mask = 0xffffffff; - s->bank[i].sens_edge = 0x00000000; - s->bank[i].fiq = 0x00000000; - s->bank[i].inputs = 0x00000000; - s->bank[i].swi = 0x00000000; - memset(s->bank[i].priority, 0, sizeof(s->bank[i].priority)); - - if (s->level_only) - s->bank[i].sens_edge = 0xffffffff; - } - - s->new_agr[0] = ~0; - s->new_agr[1] = ~0; - s->sir_intr[0] = 0; - s->sir_intr[1] = 0; - s->autoidle = 0; - s->mask = ~0; - - qemu_set_irq(s->parent_intr[0], 0); - qemu_set_irq(s->parent_intr[1], 0); -} - -static int omap_intc_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - struct omap_intr_handler_s *s = OMAP_INTC(dev); - - if (!s->iclk) { - error_report("omap-intc: clk not connected"); - return -1; - } - s->nbanks = 1; - sysbus_init_irq(sbd, &s->parent_intr[0]); - sysbus_init_irq(sbd, &s->parent_intr[1]); - qdev_init_gpio_in(dev, omap_set_intr, s->nbanks * 32); - memory_region_init_io(&s->mmio, OBJECT(s), &omap_inth_mem_ops, s, - "omap-intc", s->size); - sysbus_init_mmio(sbd, &s->mmio); - return 0; -} - -static Property omap_intc_properties[] = { - DEFINE_PROP_UINT32("size", struct omap_intr_handler_s, size, 0x100), - DEFINE_PROP_PTR("clk", struct omap_intr_handler_s, iclk), - DEFINE_PROP_END_OF_LIST(), -}; - -static void omap_intc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = omap_intc_init; - dc->reset = omap_inth_reset; - dc->props = omap_intc_properties; - /* Reason: pointer property "clk" */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo omap_intc_info = { - .name = "omap-intc", - .parent = TYPE_OMAP_INTC, - .class_init = omap_intc_class_init, -}; - -static uint64_t omap2_inth_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; - int offset = addr; - int bank_no, line_no; - struct omap_intr_handler_bank_s *bank = NULL; - - if ((offset & 0xf80) == 0x80) { - bank_no = (offset & 0x60) >> 5; - if (bank_no < s->nbanks) { - offset &= ~0x60; - bank = &s->bank[bank_no]; - } else { - OMAP_BAD_REG(addr); - return 0; - } - } - - switch (offset) { - case 0x00: /* INTC_REVISION */ - return s->revision; - - case 0x10: /* INTC_SYSCONFIG */ - return (s->autoidle >> 2) & 1; - - case 0x14: /* INTC_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x40: /* INTC_SIR_IRQ */ - return s->sir_intr[0]; - - case 0x44: /* INTC_SIR_FIQ */ - return s->sir_intr[1]; - - case 0x48: /* INTC_CONTROL */ - return (!s->mask) << 2; /* GLOBALMASK */ - - case 0x4c: /* INTC_PROTECTION */ - return 0; - - case 0x50: /* INTC_IDLE */ - return s->autoidle & 3; - - /* Per-bank registers */ - case 0x80: /* INTC_ITR */ - return bank->inputs; - - case 0x84: /* INTC_MIR */ - return bank->mask; - - case 0x88: /* INTC_MIR_CLEAR */ - case 0x8c: /* INTC_MIR_SET */ - return 0; - - case 0x90: /* INTC_ISR_SET */ - return bank->swi; - - case 0x94: /* INTC_ISR_CLEAR */ - return 0; - - case 0x98: /* INTC_PENDING_IRQ */ - return bank->irqs & ~bank->mask & ~bank->fiq; - - case 0x9c: /* INTC_PENDING_FIQ */ - return bank->irqs & ~bank->mask & bank->fiq; - - /* Per-line registers */ - case 0x100 ... 0x300: /* INTC_ILR */ - bank_no = (offset - 0x100) >> 7; - if (bank_no > s->nbanks) - break; - bank = &s->bank[bank_no]; - line_no = (offset & 0x7f) >> 2; - return (bank->priority[line_no] << 2) | - ((bank->fiq >> line_no) & 1); - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap2_inth_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; - int offset = addr; - int bank_no, line_no; - struct omap_intr_handler_bank_s *bank = NULL; - - if ((offset & 0xf80) == 0x80) { - bank_no = (offset & 0x60) >> 5; - if (bank_no < s->nbanks) { - offset &= ~0x60; - bank = &s->bank[bank_no]; - } else { - OMAP_BAD_REG(addr); - return; - } - } - - switch (offset) { - case 0x10: /* INTC_SYSCONFIG */ - s->autoidle &= 4; - s->autoidle |= (value & 1) << 2; - if (value & 2) { /* SOFTRESET */ - omap_inth_reset(DEVICE(s)); - } - return; - - case 0x48: /* INTC_CONTROL */ - s->mask = (value & 4) ? 0 : ~0; /* GLOBALMASK */ - if (value & 2) { /* NEWFIQAGR */ - qemu_set_irq(s->parent_intr[1], 0); - s->new_agr[1] = ~0; - omap_inth_update(s, 1); - } - if (value & 1) { /* NEWIRQAGR */ - qemu_set_irq(s->parent_intr[0], 0); - s->new_agr[0] = ~0; - omap_inth_update(s, 0); - } - return; - - case 0x4c: /* INTC_PROTECTION */ - /* TODO: Make a bitmap (or sizeof(char)map) of access privileges - * for every register, see Chapter 3 and 4 for privileged mode. */ - if (value & 1) - fprintf(stderr, "%s: protection mode enable attempt\n", - __FUNCTION__); - return; - - case 0x50: /* INTC_IDLE */ - s->autoidle &= ~3; - s->autoidle |= value & 3; - return; - - /* Per-bank registers */ - case 0x84: /* INTC_MIR */ - bank->mask = value; - omap_inth_update(s, 0); - omap_inth_update(s, 1); - return; - - case 0x88: /* INTC_MIR_CLEAR */ - bank->mask &= ~value; - omap_inth_update(s, 0); - omap_inth_update(s, 1); - return; - - case 0x8c: /* INTC_MIR_SET */ - bank->mask |= value; - return; - - case 0x90: /* INTC_ISR_SET */ - bank->irqs |= bank->swi |= value; - omap_inth_update(s, 0); - omap_inth_update(s, 1); - return; - - case 0x94: /* INTC_ISR_CLEAR */ - bank->swi &= ~value; - bank->irqs = bank->swi & bank->inputs; - return; - - /* Per-line registers */ - case 0x100 ... 0x300: /* INTC_ILR */ - bank_no = (offset - 0x100) >> 7; - if (bank_no > s->nbanks) - break; - bank = &s->bank[bank_no]; - line_no = (offset & 0x7f) >> 2; - bank->priority[line_no] = (value >> 2) & 0x3f; - bank->fiq &= ~(1 << line_no); - bank->fiq |= (value & 1) << line_no; - return; - - case 0x00: /* INTC_REVISION */ - case 0x14: /* INTC_SYSSTATUS */ - case 0x40: /* INTC_SIR_IRQ */ - case 0x44: /* INTC_SIR_FIQ */ - case 0x80: /* INTC_ITR */ - case 0x98: /* INTC_PENDING_IRQ */ - case 0x9c: /* INTC_PENDING_FIQ */ - OMAP_RO_REG(addr); - return; - } - OMAP_BAD_REG(addr); -} - -static const MemoryRegionOps omap2_inth_mem_ops = { - .read = omap2_inth_read, - .write = omap2_inth_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static int omap2_intc_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - struct omap_intr_handler_s *s = OMAP_INTC(dev); - - if (!s->iclk) { - error_report("omap2-intc: iclk not connected"); - return -1; - } - if (!s->fclk) { - error_report("omap2-intc: fclk not connected"); - return -1; - } - s->level_only = 1; - s->nbanks = 3; - sysbus_init_irq(sbd, &s->parent_intr[0]); - sysbus_init_irq(sbd, &s->parent_intr[1]); - qdev_init_gpio_in(dev, omap_set_intr_noedge, s->nbanks * 32); - memory_region_init_io(&s->mmio, OBJECT(s), &omap2_inth_mem_ops, s, - "omap2-intc", 0x1000); - sysbus_init_mmio(sbd, &s->mmio); - return 0; -} - -static Property omap2_intc_properties[] = { - DEFINE_PROP_UINT8("revision", struct omap_intr_handler_s, - revision, 0x21), - DEFINE_PROP_PTR("iclk", struct omap_intr_handler_s, iclk), - DEFINE_PROP_PTR("fclk", struct omap_intr_handler_s, fclk), - DEFINE_PROP_END_OF_LIST(), -}; - -static void omap2_intc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = omap2_intc_init; - dc->reset = omap_inth_reset; - dc->props = omap2_intc_properties; - /* Reason: pointer property "iclk", "fclk" */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo omap2_intc_info = { - .name = "omap2-intc", - .parent = TYPE_OMAP_INTC, - .class_init = omap2_intc_class_init, -}; - -static const TypeInfo omap_intc_type_info = { - .name = TYPE_OMAP_INTC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct omap_intr_handler_s), - .abstract = true, -}; - -static void omap_intc_register_types(void) -{ - type_register_static(&omap_intc_type_info); - type_register_static(&omap_intc_info); - type_register_static(&omap2_intc_info); -} - -type_init(omap_intc_register_types) diff --git a/qemu/hw/intc/openpic.c b/qemu/hw/intc/openpic.c deleted file mode 100644 index 2d3769310..000000000 --- a/qemu/hw/intc/openpic.c +++ /dev/null @@ -1,1664 +0,0 @@ -/* - * OpenPIC emulation - * - * Copyright (c) 2004 Jocelyn Mayer - * 2011 Alexander Graf - * - * 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. - */ -/* - * - * Based on OpenPic implementations: - * - Intel GW80314 I/O companion chip developer's manual - * - Motorola MPC8245 & MPC8540 user manuals. - * - Motorola MCP750 (aka Raven) programmer manual. - * - Motorola Harrier programmer manuel - * - * 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" -#include "hw/ppc/openpic.h" -#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" - -//#define DEBUG_OPENPIC - -#ifdef DEBUG_OPENPIC -static const int debug_openpic = 1; -#else -static const int debug_openpic = 0; -#endif - -#define DPRINTF(fmt, ...) do { \ - if (debug_openpic) { \ - printf(fmt , ## __VA_ARGS__); \ - } \ - } while (0) - -#define MAX_CPU 32 -#define MAX_MSI 8 -#define VID 0x03 /* MPIC version ID */ - -/* OpenPIC capability flags */ -#define OPENPIC_FLAG_IDR_CRIT (1 << 0) -#define OPENPIC_FLAG_ILR (2 << 0) - -/* OpenPIC address map */ -#define OPENPIC_GLB_REG_START 0x0 -#define OPENPIC_GLB_REG_SIZE 0x10F0 -#define OPENPIC_TMR_REG_START 0x10F0 -#define OPENPIC_TMR_REG_SIZE 0x220 -#define OPENPIC_MSI_REG_START 0x1600 -#define OPENPIC_MSI_REG_SIZE 0x200 -#define OPENPIC_SUMMARY_REG_START 0x3800 -#define OPENPIC_SUMMARY_REG_SIZE 0x800 -#define OPENPIC_SRC_REG_START 0x10000 -#define OPENPIC_SRC_REG_SIZE (OPENPIC_MAX_SRC * 0x20) -#define OPENPIC_CPU_REG_START 0x20000 -#define OPENPIC_CPU_REG_SIZE 0x100 + ((MAX_CPU - 1) * 0x1000) - -/* Raven */ -#define RAVEN_MAX_CPU 2 -#define RAVEN_MAX_EXT 48 -#define RAVEN_MAX_IRQ 64 -#define RAVEN_MAX_TMR OPENPIC_MAX_TMR -#define RAVEN_MAX_IPI OPENPIC_MAX_IPI - -/* Interrupt definitions */ -#define RAVEN_FE_IRQ (RAVEN_MAX_EXT) /* Internal functional IRQ */ -#define RAVEN_ERR_IRQ (RAVEN_MAX_EXT + 1) /* Error IRQ */ -#define RAVEN_TMR_IRQ (RAVEN_MAX_EXT + 2) /* First timer IRQ */ -#define RAVEN_IPI_IRQ (RAVEN_TMR_IRQ + RAVEN_MAX_TMR) /* First IPI IRQ */ -/* First doorbell IRQ */ -#define RAVEN_DBL_IRQ (RAVEN_IPI_IRQ + (RAVEN_MAX_CPU * RAVEN_MAX_IPI)) - -typedef struct FslMpicInfo { - int max_ext; -} FslMpicInfo; - -static FslMpicInfo fsl_mpic_20 = { - .max_ext = 12, -}; - -static FslMpicInfo fsl_mpic_42 = { - .max_ext = 12, -}; - -#define FRR_NIRQ_SHIFT 16 -#define FRR_NCPU_SHIFT 8 -#define FRR_VID_SHIFT 0 - -#define VID_REVISION_1_2 2 -#define VID_REVISION_1_3 3 - -#define VIR_GENERIC 0x00000000 /* Generic Vendor ID */ - -#define GCR_RESET 0x80000000 -#define GCR_MODE_PASS 0x00000000 -#define GCR_MODE_MIXED 0x20000000 -#define GCR_MODE_PROXY 0x60000000 - -#define TBCR_CI 0x80000000 /* count inhibit */ -#define TCCR_TOG 0x80000000 /* toggles when decrement to zero */ - -#define IDR_EP_SHIFT 31 -#define IDR_EP_MASK (1U << IDR_EP_SHIFT) -#define IDR_CI0_SHIFT 30 -#define IDR_CI1_SHIFT 29 -#define IDR_P1_SHIFT 1 -#define IDR_P0_SHIFT 0 - -#define ILR_INTTGT_MASK 0x000000ff -#define ILR_INTTGT_INT 0x00 -#define ILR_INTTGT_CINT 0x01 /* critical */ -#define ILR_INTTGT_MCP 0x02 /* machine check */ - -/* The currently supported INTTGT values happen to be the same as QEMU's - * openpic output codes, but don't depend on this. The output codes - * could change (unlikely, but...) or support could be added for - * more INTTGT values. - */ -static const int inttgt_output[][2] = { - { ILR_INTTGT_INT, OPENPIC_OUTPUT_INT }, - { ILR_INTTGT_CINT, OPENPIC_OUTPUT_CINT }, - { ILR_INTTGT_MCP, OPENPIC_OUTPUT_MCK }, -}; - -static int inttgt_to_output(int inttgt) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(inttgt_output); i++) { - if (inttgt_output[i][0] == inttgt) { - return inttgt_output[i][1]; - } - } - - fprintf(stderr, "%s: unsupported inttgt %d\n", __func__, inttgt); - return OPENPIC_OUTPUT_INT; -} - -static int output_to_inttgt(int output) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(inttgt_output); i++) { - if (inttgt_output[i][1] == output) { - return inttgt_output[i][0]; - } - } - - abort(); -} - -#define MSIIR_OFFSET 0x140 -#define MSIIR_SRS_SHIFT 29 -#define MSIIR_SRS_MASK (0x7 << MSIIR_SRS_SHIFT) -#define MSIIR_IBS_SHIFT 24 -#define MSIIR_IBS_MASK (0x1f << MSIIR_IBS_SHIFT) - -static int get_current_cpu(void) -{ - if (!current_cpu) { - return -1; - } - - return current_cpu->cpu_index; -} - -static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr, - int idx); -static void openpic_cpu_write_internal(void *opaque, hwaddr addr, - uint32_t val, int idx); -static void openpic_reset(DeviceState *d); - -typedef enum IRQType { - IRQ_TYPE_NORMAL = 0, - IRQ_TYPE_FSLINT, /* FSL internal interrupt -- level only */ - IRQ_TYPE_FSLSPECIAL, /* FSL timer/IPI interrupt, edge, no polarity */ -} IRQType; - -/* Round up to the nearest 64 IRQs so that the queue length - * won't change when moving between 32 and 64 bit hosts. - */ -#define IRQQUEUE_SIZE_BITS ((OPENPIC_MAX_IRQ + 63) & ~63) - -typedef struct IRQQueue { - unsigned long *queue; - int32_t queue_size; /* Only used for VMSTATE_BITMAP */ - int next; - int priority; -} IRQQueue; - -typedef struct IRQSource { - uint32_t ivpr; /* IRQ vector/priority register */ - uint32_t idr; /* IRQ destination register */ - uint32_t destmask; /* bitmap of CPU destinations */ - int last_cpu; - int output; /* IRQ level, e.g. OPENPIC_OUTPUT_INT */ - int pending; /* TRUE if IRQ is pending */ - IRQType type; - bool level:1; /* level-triggered */ - bool nomask:1; /* critical interrupts ignore mask on some FSL MPICs */ -} IRQSource; - -#define IVPR_MASK_SHIFT 31 -#define IVPR_MASK_MASK (1U << IVPR_MASK_SHIFT) -#define IVPR_ACTIVITY_SHIFT 30 -#define IVPR_ACTIVITY_MASK (1U << IVPR_ACTIVITY_SHIFT) -#define IVPR_MODE_SHIFT 29 -#define IVPR_MODE_MASK (1U << IVPR_MODE_SHIFT) -#define IVPR_POLARITY_SHIFT 23 -#define IVPR_POLARITY_MASK (1U << IVPR_POLARITY_SHIFT) -#define IVPR_SENSE_SHIFT 22 -#define IVPR_SENSE_MASK (1U << IVPR_SENSE_SHIFT) - -#define IVPR_PRIORITY_MASK (0xFU << 16) -#define IVPR_PRIORITY(_ivprr_) ((int)(((_ivprr_) & IVPR_PRIORITY_MASK) >> 16)) -#define IVPR_VECTOR(opp, _ivprr_) ((_ivprr_) & (opp)->vector_mask) - -/* IDR[EP/CI] are only for FSL MPIC prior to v4.0 */ -#define IDR_EP 0x80000000 /* external pin */ -#define IDR_CI 0x40000000 /* critical interrupt */ - -typedef struct OpenPICTimer { - uint32_t tccr; /* Global timer current count register */ - uint32_t tbcr; /* Global timer base count register */ -} OpenPICTimer; - -typedef struct OpenPICMSI { - uint32_t msir; /* Shared Message Signaled Interrupt Register */ -} OpenPICMSI; - -typedef struct IRQDest { - int32_t ctpr; /* CPU current task priority */ - IRQQueue raised; - IRQQueue servicing; - qemu_irq *irqs; - - /* Count of IRQ sources asserting on non-INT outputs */ - uint32_t outputs_active[OPENPIC_OUTPUT_NB]; -} IRQDest; - -#define OPENPIC(obj) OBJECT_CHECK(OpenPICState, (obj), TYPE_OPENPIC) - -typedef struct OpenPICState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion mem; - - /* Behavior control */ - FslMpicInfo *fsl; - uint32_t model; - uint32_t flags; - uint32_t nb_irqs; - uint32_t vid; - uint32_t vir; /* Vendor identification register */ - uint32_t vector_mask; - uint32_t tfrr_reset; - uint32_t ivpr_reset; - uint32_t idr_reset; - uint32_t brr1; - uint32_t mpic_mode_mask; - - /* Sub-regions */ - MemoryRegion sub_io_mem[6]; - - /* Global registers */ - uint32_t frr; /* Feature reporting register */ - uint32_t gcr; /* Global configuration register */ - uint32_t pir; /* Processor initialization register */ - uint32_t spve; /* Spurious vector register */ - uint32_t tfrr; /* Timer frequency reporting register */ - /* Source registers */ - IRQSource src[OPENPIC_MAX_IRQ]; - /* Local registers per output pin */ - IRQDest dst[MAX_CPU]; - uint32_t nb_cpus; - /* Timer registers */ - OpenPICTimer timers[OPENPIC_MAX_TMR]; - /* Shared MSI registers */ - OpenPICMSI msi[MAX_MSI]; - uint32_t max_irq; - uint32_t irq_ipi0; - uint32_t irq_tim0; - uint32_t irq_msi; -} OpenPICState; - -static inline void IRQ_setbit(IRQQueue *q, int n_IRQ) -{ - set_bit(n_IRQ, q->queue); -} - -static inline void IRQ_resetbit(IRQQueue *q, int n_IRQ) -{ - clear_bit(n_IRQ, q->queue); -} - -static void IRQ_check(OpenPICState *opp, IRQQueue *q) -{ - int irq = -1; - int next = -1; - int priority = -1; - - for (;;) { - irq = find_next_bit(q->queue, opp->max_irq, irq + 1); - if (irq == opp->max_irq) { - break; - } - - DPRINTF("IRQ_check: irq %d set ivpr_pr=%d pr=%d\n", - irq, IVPR_PRIORITY(opp->src[irq].ivpr), priority); - - if (IVPR_PRIORITY(opp->src[irq].ivpr) > priority) { - next = irq; - priority = IVPR_PRIORITY(opp->src[irq].ivpr); - } - } - - q->next = next; - q->priority = priority; -} - -static int IRQ_get_next(OpenPICState *opp, IRQQueue *q) -{ - /* XXX: optimize */ - IRQ_check(opp, q); - - return q->next; -} - -static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ, - bool active, bool was_active) -{ - IRQDest *dst; - IRQSource *src; - int priority; - - dst = &opp->dst[n_CPU]; - src = &opp->src[n_IRQ]; - - DPRINTF("%s: IRQ %d active %d was %d\n", - __func__, n_IRQ, active, was_active); - - if (src->output != OPENPIC_OUTPUT_INT) { - DPRINTF("%s: output %d irq %d active %d was %d count %d\n", - __func__, src->output, n_IRQ, active, was_active, - dst->outputs_active[src->output]); - - /* On Freescale MPIC, critical interrupts ignore priority, - * IACK, EOI, etc. Before MPIC v4.1 they also ignore - * masking. - */ - if (active) { - if (!was_active && dst->outputs_active[src->output]++ == 0) { - DPRINTF("%s: Raise OpenPIC output %d cpu %d irq %d\n", - __func__, src->output, n_CPU, n_IRQ); - qemu_irq_raise(dst->irqs[src->output]); - } - } else { - if (was_active && --dst->outputs_active[src->output] == 0) { - DPRINTF("%s: Lower OpenPIC output %d cpu %d irq %d\n", - __func__, src->output, n_CPU, n_IRQ); - qemu_irq_lower(dst->irqs[src->output]); - } - } - - return; - } - - priority = IVPR_PRIORITY(src->ivpr); - - /* Even if the interrupt doesn't have enough priority, - * it is still raised, in case ctpr is lowered later. - */ - if (active) { - IRQ_setbit(&dst->raised, n_IRQ); - } else { - IRQ_resetbit(&dst->raised, n_IRQ); - } - - IRQ_check(opp, &dst->raised); - - if (active && priority <= dst->ctpr) { - DPRINTF("%s: IRQ %d priority %d too low for ctpr %d on CPU %d\n", - __func__, n_IRQ, priority, dst->ctpr, n_CPU); - active = 0; - } - - if (active) { - if (IRQ_get_next(opp, &dst->servicing) >= 0 && - priority <= dst->servicing.priority) { - DPRINTF("%s: IRQ %d is hidden by servicing IRQ %d on CPU %d\n", - __func__, n_IRQ, dst->servicing.next, n_CPU); - } else { - DPRINTF("%s: Raise OpenPIC INT output cpu %d irq %d/%d\n", - __func__, n_CPU, n_IRQ, dst->raised.next); - qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]); - } - } else { - IRQ_get_next(opp, &dst->servicing); - if (dst->raised.priority > dst->ctpr && - dst->raised.priority > dst->servicing.priority) { - DPRINTF("%s: IRQ %d inactive, IRQ %d prio %d above %d/%d, CPU %d\n", - __func__, n_IRQ, dst->raised.next, dst->raised.priority, - dst->ctpr, dst->servicing.priority, n_CPU); - /* IRQ line stays asserted */ - } else { - DPRINTF("%s: IRQ %d inactive, current prio %d/%d, CPU %d\n", - __func__, n_IRQ, dst->ctpr, dst->servicing.priority, n_CPU); - qemu_irq_lower(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]); - } - } -} - -/* update pic state because registers for n_IRQ have changed value */ -static void openpic_update_irq(OpenPICState *opp, int n_IRQ) -{ - IRQSource *src; - bool active, was_active; - int i; - - src = &opp->src[n_IRQ]; - active = src->pending; - - if ((src->ivpr & IVPR_MASK_MASK) && !src->nomask) { - /* Interrupt source is disabled */ - DPRINTF("%s: IRQ %d is disabled\n", __func__, n_IRQ); - active = false; - } - - was_active = !!(src->ivpr & IVPR_ACTIVITY_MASK); - - /* - * We don't have a similar check for already-active because - * ctpr may have changed and we need to withdraw the interrupt. - */ - if (!active && !was_active) { - DPRINTF("%s: IRQ %d is already inactive\n", __func__, n_IRQ); - return; - } - - if (active) { - src->ivpr |= IVPR_ACTIVITY_MASK; - } else { - src->ivpr &= ~IVPR_ACTIVITY_MASK; - } - - if (src->destmask == 0) { - /* No target */ - DPRINTF("%s: IRQ %d has no target\n", __func__, n_IRQ); - return; - } - - if (src->destmask == (1 << src->last_cpu)) { - /* Only one CPU is allowed to receive this IRQ */ - IRQ_local_pipe(opp, src->last_cpu, n_IRQ, active, was_active); - } else if (!(src->ivpr & IVPR_MODE_MASK)) { - /* Directed delivery mode */ - for (i = 0; i < opp->nb_cpus; i++) { - if (src->destmask & (1 << i)) { - IRQ_local_pipe(opp, i, n_IRQ, active, was_active); - } - } - } else { - /* Distributed delivery mode */ - for (i = src->last_cpu + 1; i != src->last_cpu; i++) { - if (i == opp->nb_cpus) { - i = 0; - } - if (src->destmask & (1 << i)) { - IRQ_local_pipe(opp, i, n_IRQ, active, was_active); - src->last_cpu = i; - break; - } - } - } -} - -static void openpic_set_irq(void *opaque, int n_IRQ, int level) -{ - OpenPICState *opp = opaque; - IRQSource *src; - - if (n_IRQ >= OPENPIC_MAX_IRQ) { - fprintf(stderr, "%s: IRQ %d out of range\n", __func__, n_IRQ); - abort(); - } - - src = &opp->src[n_IRQ]; - DPRINTF("openpic: set irq %d = %d ivpr=0x%08x\n", - n_IRQ, level, src->ivpr); - if (src->level) { - /* level-sensitive irq */ - src->pending = level; - openpic_update_irq(opp, n_IRQ); - } else { - /* edge-sensitive irq */ - if (level) { - src->pending = 1; - openpic_update_irq(opp, n_IRQ); - } - - if (src->output != OPENPIC_OUTPUT_INT) { - /* Edge-triggered interrupts shouldn't be used - * with non-INT delivery, but just in case, - * try to make it do something sane rather than - * cause an interrupt storm. This is close to - * what you'd probably see happen in real hardware. - */ - src->pending = 0; - openpic_update_irq(opp, n_IRQ); - } - } -} - -static inline uint32_t read_IRQreg_idr(OpenPICState *opp, int n_IRQ) -{ - return opp->src[n_IRQ].idr; -} - -static inline uint32_t read_IRQreg_ilr(OpenPICState *opp, int n_IRQ) -{ - if (opp->flags & OPENPIC_FLAG_ILR) { - return output_to_inttgt(opp->src[n_IRQ].output); - } - - return 0xffffffff; -} - -static inline uint32_t read_IRQreg_ivpr(OpenPICState *opp, int n_IRQ) -{ - return opp->src[n_IRQ].ivpr; -} - -static inline void write_IRQreg_idr(OpenPICState *opp, int n_IRQ, uint32_t val) -{ - IRQSource *src = &opp->src[n_IRQ]; - uint32_t normal_mask = (1UL << opp->nb_cpus) - 1; - uint32_t crit_mask = 0; - uint32_t mask = normal_mask; - int crit_shift = IDR_EP_SHIFT - opp->nb_cpus; - int i; - - if (opp->flags & OPENPIC_FLAG_IDR_CRIT) { - crit_mask = mask << crit_shift; - mask |= crit_mask | IDR_EP; - } - - src->idr = val & mask; - DPRINTF("Set IDR %d to 0x%08x\n", n_IRQ, src->idr); - - if (opp->flags & OPENPIC_FLAG_IDR_CRIT) { - if (src->idr & crit_mask) { - if (src->idr & normal_mask) { - DPRINTF("%s: IRQ configured for multiple output types, using " - "critical\n", __func__); - } - - src->output = OPENPIC_OUTPUT_CINT; - src->nomask = true; - src->destmask = 0; - - for (i = 0; i < opp->nb_cpus; i++) { - int n_ci = IDR_CI0_SHIFT - i; - - if (src->idr & (1UL << n_ci)) { - src->destmask |= 1UL << i; - } - } - } else { - src->output = OPENPIC_OUTPUT_INT; - src->nomask = false; - src->destmask = src->idr & normal_mask; - } - } else { - src->destmask = src->idr; - } -} - -static inline void write_IRQreg_ilr(OpenPICState *opp, int n_IRQ, uint32_t val) -{ - if (opp->flags & OPENPIC_FLAG_ILR) { - IRQSource *src = &opp->src[n_IRQ]; - - src->output = inttgt_to_output(val & ILR_INTTGT_MASK); - DPRINTF("Set ILR %d to 0x%08x, output %d\n", n_IRQ, src->idr, - src->output); - - /* TODO: on MPIC v4.0 only, set nomask for non-INT */ - } -} - -static inline void write_IRQreg_ivpr(OpenPICState *opp, int n_IRQ, uint32_t val) -{ - uint32_t mask; - - /* NOTE when implementing newer FSL MPIC models: starting with v4.0, - * the polarity bit is read-only on internal interrupts. - */ - mask = IVPR_MASK_MASK | IVPR_PRIORITY_MASK | IVPR_SENSE_MASK | - IVPR_POLARITY_MASK | opp->vector_mask; - - /* ACTIVITY bit is read-only */ - opp->src[n_IRQ].ivpr = - (opp->src[n_IRQ].ivpr & IVPR_ACTIVITY_MASK) | (val & mask); - - /* For FSL internal interrupts, The sense bit is reserved and zero, - * and the interrupt is always level-triggered. Timers and IPIs - * have no sense or polarity bits, and are edge-triggered. - */ - switch (opp->src[n_IRQ].type) { - case IRQ_TYPE_NORMAL: - opp->src[n_IRQ].level = !!(opp->src[n_IRQ].ivpr & IVPR_SENSE_MASK); - break; - - case IRQ_TYPE_FSLINT: - opp->src[n_IRQ].ivpr &= ~IVPR_SENSE_MASK; - break; - - case IRQ_TYPE_FSLSPECIAL: - opp->src[n_IRQ].ivpr &= ~(IVPR_POLARITY_MASK | IVPR_SENSE_MASK); - break; - } - - openpic_update_irq(opp, n_IRQ); - DPRINTF("Set IVPR %d to 0x%08x -> 0x%08x\n", n_IRQ, val, - opp->src[n_IRQ].ivpr); -} - -static void openpic_gcr_write(OpenPICState *opp, uint64_t val) -{ - bool mpic_proxy = false; - - if (val & GCR_RESET) { - openpic_reset(DEVICE(opp)); - return; - } - - opp->gcr &= ~opp->mpic_mode_mask; - opp->gcr |= val & opp->mpic_mode_mask; - - /* Set external proxy mode */ - if ((val & opp->mpic_mode_mask) == GCR_MODE_PROXY) { - mpic_proxy = true; - } - - ppce500_set_mpic_proxy(mpic_proxy); -} - -static void openpic_gbl_write(void *opaque, hwaddr addr, uint64_t val, - unsigned len) -{ - OpenPICState *opp = opaque; - IRQDest *dst; - int idx; - - DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n", - __func__, addr, val); - if (addr & 0xF) { - return; - } - switch (addr) { - case 0x00: /* Block Revision Register1 (BRR1) is Readonly */ - break; - case 0x40: - case 0x50: - case 0x60: - case 0x70: - case 0x80: - case 0x90: - case 0xA0: - case 0xB0: - openpic_cpu_write_internal(opp, addr, val, get_current_cpu()); - break; - case 0x1000: /* FRR */ - break; - case 0x1020: /* GCR */ - openpic_gcr_write(opp, val); - break; - case 0x1080: /* VIR */ - break; - case 0x1090: /* PIR */ - for (idx = 0; idx < opp->nb_cpus; idx++) { - if ((val & (1 << idx)) && !(opp->pir & (1 << idx))) { - DPRINTF("Raise OpenPIC RESET output for CPU %d\n", idx); - dst = &opp->dst[idx]; - qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_RESET]); - } else if (!(val & (1 << idx)) && (opp->pir & (1 << idx))) { - DPRINTF("Lower OpenPIC RESET output for CPU %d\n", idx); - dst = &opp->dst[idx]; - qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_RESET]); - } - } - opp->pir = val; - break; - case 0x10A0: /* IPI_IVPR */ - case 0x10B0: - case 0x10C0: - case 0x10D0: - { - int idx; - idx = (addr - 0x10A0) >> 4; - write_IRQreg_ivpr(opp, opp->irq_ipi0 + idx, val); - } - break; - case 0x10E0: /* SPVE */ - opp->spve = val & opp->vector_mask; - break; - default: - break; - } -} - -static uint64_t openpic_gbl_read(void *opaque, hwaddr addr, unsigned len) -{ - OpenPICState *opp = opaque; - uint32_t retval; - - DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); - retval = 0xFFFFFFFF; - if (addr & 0xF) { - return retval; - } - switch (addr) { - case 0x1000: /* FRR */ - retval = opp->frr; - break; - case 0x1020: /* GCR */ - retval = opp->gcr; - break; - case 0x1080: /* VIR */ - retval = opp->vir; - break; - case 0x1090: /* PIR */ - retval = 0x00000000; - break; - case 0x00: /* Block Revision Register1 (BRR1) */ - retval = opp->brr1; - break; - case 0x40: - case 0x50: - case 0x60: - case 0x70: - case 0x80: - case 0x90: - case 0xA0: - case 0xB0: - retval = openpic_cpu_read_internal(opp, addr, get_current_cpu()); - break; - case 0x10A0: /* IPI_IVPR */ - case 0x10B0: - case 0x10C0: - case 0x10D0: - { - int idx; - idx = (addr - 0x10A0) >> 4; - retval = read_IRQreg_ivpr(opp, opp->irq_ipi0 + idx); - } - break; - case 0x10E0: /* SPVE */ - retval = opp->spve; - break; - default: - break; - } - DPRINTF("%s: => 0x%08x\n", __func__, retval); - - return retval; -} - -static void openpic_tmr_write(void *opaque, hwaddr addr, uint64_t val, - unsigned len) -{ - OpenPICState *opp = opaque; - int idx; - - addr += 0x10f0; - - DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n", - __func__, addr, val); - if (addr & 0xF) { - return; - } - - if (addr == 0x10f0) { - /* TFRR */ - opp->tfrr = val; - return; - } - - idx = (addr >> 6) & 0x3; - addr = addr & 0x30; - - switch (addr & 0x30) { - case 0x00: /* TCCR */ - break; - case 0x10: /* TBCR */ - if ((opp->timers[idx].tccr & TCCR_TOG) != 0 && - (val & TBCR_CI) == 0 && - (opp->timers[idx].tbcr & TBCR_CI) != 0) { - opp->timers[idx].tccr &= ~TCCR_TOG; - } - opp->timers[idx].tbcr = val; - break; - case 0x20: /* TVPR */ - write_IRQreg_ivpr(opp, opp->irq_tim0 + idx, val); - break; - case 0x30: /* TDR */ - write_IRQreg_idr(opp, opp->irq_tim0 + idx, val); - break; - } -} - -static uint64_t openpic_tmr_read(void *opaque, hwaddr addr, unsigned len) -{ - OpenPICState *opp = opaque; - uint32_t retval = -1; - int idx; - - DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); - if (addr & 0xF) { - goto out; - } - idx = (addr >> 6) & 0x3; - if (addr == 0x0) { - /* TFRR */ - retval = opp->tfrr; - goto out; - } - switch (addr & 0x30) { - case 0x00: /* TCCR */ - retval = opp->timers[idx].tccr; - break; - case 0x10: /* TBCR */ - retval = opp->timers[idx].tbcr; - break; - case 0x20: /* TIPV */ - retval = read_IRQreg_ivpr(opp, opp->irq_tim0 + idx); - break; - case 0x30: /* TIDE (TIDR) */ - retval = read_IRQreg_idr(opp, opp->irq_tim0 + idx); - break; - } - -out: - DPRINTF("%s: => 0x%08x\n", __func__, retval); - - return retval; -} - -static void openpic_src_write(void *opaque, hwaddr addr, uint64_t val, - unsigned len) -{ - OpenPICState *opp = opaque; - int idx; - - DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n", - __func__, addr, val); - - addr = addr & 0xffff; - idx = addr >> 5; - - switch (addr & 0x1f) { - case 0x00: - write_IRQreg_ivpr(opp, idx, val); - break; - case 0x10: - write_IRQreg_idr(opp, idx, val); - break; - case 0x18: - write_IRQreg_ilr(opp, idx, val); - break; - } -} - -static uint64_t openpic_src_read(void *opaque, uint64_t addr, unsigned len) -{ - OpenPICState *opp = opaque; - uint32_t retval; - int idx; - - DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); - retval = 0xFFFFFFFF; - - addr = addr & 0xffff; - idx = addr >> 5; - - switch (addr & 0x1f) { - case 0x00: - retval = read_IRQreg_ivpr(opp, idx); - break; - case 0x10: - retval = read_IRQreg_idr(opp, idx); - break; - case 0x18: - retval = read_IRQreg_ilr(opp, idx); - break; - } - - DPRINTF("%s: => 0x%08x\n", __func__, retval); - return retval; -} - -static void openpic_msi_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - OpenPICState *opp = opaque; - int idx = opp->irq_msi; - int srs, ibs; - - DPRINTF("%s: addr %#" HWADDR_PRIx " <= 0x%08" PRIx64 "\n", - __func__, addr, val); - if (addr & 0xF) { - return; - } - - switch (addr) { - case MSIIR_OFFSET: - srs = val >> MSIIR_SRS_SHIFT; - idx += srs; - ibs = (val & MSIIR_IBS_MASK) >> MSIIR_IBS_SHIFT; - opp->msi[srs].msir |= 1 << ibs; - openpic_set_irq(opp, idx, 1); - break; - default: - /* most registers are read-only, thus ignored */ - break; - } -} - -static uint64_t openpic_msi_read(void *opaque, hwaddr addr, unsigned size) -{ - OpenPICState *opp = opaque; - uint64_t r = 0; - int i, srs; - - DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); - if (addr & 0xF) { - return -1; - } - - srs = addr >> 4; - - switch (addr) { - case 0x00: - case 0x10: - case 0x20: - case 0x30: - case 0x40: - case 0x50: - case 0x60: - case 0x70: /* MSIRs */ - r = opp->msi[srs].msir; - /* Clear on read */ - opp->msi[srs].msir = 0; - openpic_set_irq(opp, opp->irq_msi + srs, 0); - break; - case 0x120: /* MSISR */ - for (i = 0; i < MAX_MSI; i++) { - r |= (opp->msi[i].msir ? 1 : 0) << i; - } - break; - } - - return r; -} - -static uint64_t openpic_summary_read(void *opaque, hwaddr addr, unsigned size) -{ - uint64_t r = 0; - - DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); - - /* TODO: EISR/EIMR */ - - return r; -} - -static void openpic_summary_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - DPRINTF("%s: addr %#" HWADDR_PRIx " <= 0x%08" PRIx64 "\n", - __func__, addr, val); - - /* TODO: EISR/EIMR */ -} - -static void openpic_cpu_write_internal(void *opaque, hwaddr addr, - uint32_t val, int idx) -{ - OpenPICState *opp = opaque; - IRQSource *src; - IRQDest *dst; - int s_IRQ, n_IRQ; - - DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx " <= 0x%08x\n", __func__, idx, - addr, val); - - if (idx < 0 || idx >= opp->nb_cpus) { - return; - } - - if (addr & 0xF) { - return; - } - dst = &opp->dst[idx]; - addr &= 0xFF0; - switch (addr) { - case 0x40: /* IPIDR */ - case 0x50: - case 0x60: - case 0x70: - idx = (addr - 0x40) >> 4; - /* we use IDE as mask which CPUs to deliver the IPI to still. */ - opp->src[opp->irq_ipi0 + idx].destmask |= val; - openpic_set_irq(opp, opp->irq_ipi0 + idx, 1); - openpic_set_irq(opp, opp->irq_ipi0 + idx, 0); - break; - case 0x80: /* CTPR */ - dst->ctpr = val & 0x0000000F; - - DPRINTF("%s: set CPU %d ctpr to %d, raised %d servicing %d\n", - __func__, idx, dst->ctpr, dst->raised.priority, - dst->servicing.priority); - - if (dst->raised.priority <= dst->ctpr) { - DPRINTF("%s: Lower OpenPIC INT output cpu %d due to ctpr\n", - __func__, idx); - qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]); - } else if (dst->raised.priority > dst->servicing.priority) { - DPRINTF("%s: Raise OpenPIC INT output cpu %d irq %d\n", - __func__, idx, dst->raised.next); - qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_INT]); - } - - break; - case 0x90: /* WHOAMI */ - /* Read-only register */ - break; - case 0xA0: /* IACK */ - /* Read-only register */ - break; - case 0xB0: /* EOI */ - DPRINTF("EOI\n"); - s_IRQ = IRQ_get_next(opp, &dst->servicing); - - if (s_IRQ < 0) { - DPRINTF("%s: EOI with no interrupt in service\n", __func__); - break; - } - - IRQ_resetbit(&dst->servicing, s_IRQ); - /* Set up next servicing IRQ */ - s_IRQ = IRQ_get_next(opp, &dst->servicing); - /* Check queued interrupts. */ - n_IRQ = IRQ_get_next(opp, &dst->raised); - src = &opp->src[n_IRQ]; - if (n_IRQ != -1 && - (s_IRQ == -1 || - IVPR_PRIORITY(src->ivpr) > dst->servicing.priority)) { - DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n", - idx, n_IRQ); - qemu_irq_raise(opp->dst[idx].irqs[OPENPIC_OUTPUT_INT]); - } - break; - default: - break; - } -} - -static void openpic_cpu_write(void *opaque, hwaddr addr, uint64_t val, - unsigned len) -{ - openpic_cpu_write_internal(opaque, addr, val, (addr & 0x1f000) >> 12); -} - - -static uint32_t openpic_iack(OpenPICState *opp, IRQDest *dst, int cpu) -{ - IRQSource *src; - int retval, irq; - - DPRINTF("Lower OpenPIC INT output\n"); - qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]); - - irq = IRQ_get_next(opp, &dst->raised); - DPRINTF("IACK: irq=%d\n", irq); - - if (irq == -1) { - /* No more interrupt pending */ - return opp->spve; - } - - src = &opp->src[irq]; - if (!(src->ivpr & IVPR_ACTIVITY_MASK) || - !(IVPR_PRIORITY(src->ivpr) > dst->ctpr)) { - fprintf(stderr, "%s: bad raised IRQ %d ctpr %d ivpr 0x%08x\n", - __func__, irq, dst->ctpr, src->ivpr); - openpic_update_irq(opp, irq); - retval = opp->spve; - } else { - /* IRQ enter servicing state */ - IRQ_setbit(&dst->servicing, irq); - retval = IVPR_VECTOR(opp, src->ivpr); - } - - if (!src->level) { - /* edge-sensitive IRQ */ - src->ivpr &= ~IVPR_ACTIVITY_MASK; - src->pending = 0; - IRQ_resetbit(&dst->raised, irq); - } - - if ((irq >= opp->irq_ipi0) && (irq < (opp->irq_ipi0 + OPENPIC_MAX_IPI))) { - src->destmask &= ~(1 << cpu); - if (src->destmask && !src->level) { - /* trigger on CPUs that didn't know about it yet */ - openpic_set_irq(opp, irq, 1); - openpic_set_irq(opp, irq, 0); - /* if all CPUs knew about it, set active bit again */ - src->ivpr |= IVPR_ACTIVITY_MASK; - } - } - - return retval; -} - -static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr, - int idx) -{ - OpenPICState *opp = opaque; - IRQDest *dst; - uint32_t retval; - - DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx "\n", __func__, idx, addr); - retval = 0xFFFFFFFF; - - if (idx < 0 || idx >= opp->nb_cpus) { - return retval; - } - - if (addr & 0xF) { - return retval; - } - dst = &opp->dst[idx]; - addr &= 0xFF0; - switch (addr) { - case 0x80: /* CTPR */ - retval = dst->ctpr; - break; - case 0x90: /* WHOAMI */ - retval = idx; - break; - case 0xA0: /* IACK */ - retval = openpic_iack(opp, dst, idx); - break; - case 0xB0: /* EOI */ - retval = 0; - break; - default: - break; - } - DPRINTF("%s: => 0x%08x\n", __func__, retval); - - return retval; -} - -static uint64_t openpic_cpu_read(void *opaque, hwaddr addr, unsigned len) -{ - return openpic_cpu_read_internal(opaque, addr, (addr & 0x1f000) >> 12); -} - -static const MemoryRegionOps openpic_glb_ops_le = { - .write = openpic_gbl_write, - .read = openpic_gbl_read, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const MemoryRegionOps openpic_glb_ops_be = { - .write = openpic_gbl_write, - .read = openpic_gbl_read, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const MemoryRegionOps openpic_tmr_ops_le = { - .write = openpic_tmr_write, - .read = openpic_tmr_read, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const MemoryRegionOps openpic_tmr_ops_be = { - .write = openpic_tmr_write, - .read = openpic_tmr_read, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const MemoryRegionOps openpic_cpu_ops_le = { - .write = openpic_cpu_write, - .read = openpic_cpu_read, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const MemoryRegionOps openpic_cpu_ops_be = { - .write = openpic_cpu_write, - .read = openpic_cpu_read, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const MemoryRegionOps openpic_src_ops_le = { - .write = openpic_src_write, - .read = openpic_src_read, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const MemoryRegionOps openpic_src_ops_be = { - .write = openpic_src_write, - .read = openpic_src_read, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const MemoryRegionOps openpic_msi_ops_be = { - .read = openpic_msi_read, - .write = openpic_msi_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const MemoryRegionOps openpic_summary_ops_be = { - .read = openpic_summary_read, - .write = openpic_summary_write, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void openpic_reset(DeviceState *d) -{ - OpenPICState *opp = OPENPIC(d); - int i; - - opp->gcr = GCR_RESET; - /* Initialise controller registers */ - opp->frr = ((opp->nb_irqs - 1) << FRR_NIRQ_SHIFT) | - ((opp->nb_cpus - 1) << FRR_NCPU_SHIFT) | - (opp->vid << FRR_VID_SHIFT); - - opp->pir = 0; - opp->spve = -1 & opp->vector_mask; - opp->tfrr = opp->tfrr_reset; - /* Initialise IRQ sources */ - for (i = 0; i < opp->max_irq; i++) { - opp->src[i].ivpr = opp->ivpr_reset; - switch (opp->src[i].type) { - case IRQ_TYPE_NORMAL: - opp->src[i].level = !!(opp->ivpr_reset & IVPR_SENSE_MASK); - break; - - case IRQ_TYPE_FSLINT: - opp->src[i].ivpr |= IVPR_POLARITY_MASK; - break; - - case IRQ_TYPE_FSLSPECIAL: - break; - } - - write_IRQreg_idr(opp, i, opp->idr_reset); - } - /* Initialise IRQ destinations */ - for (i = 0; i < opp->nb_cpus; i++) { - opp->dst[i].ctpr = 15; - opp->dst[i].raised.next = -1; - opp->dst[i].raised.priority = 0; - bitmap_clear(opp->dst[i].raised.queue, 0, IRQQUEUE_SIZE_BITS); - opp->dst[i].servicing.next = -1; - opp->dst[i].servicing.priority = 0; - bitmap_clear(opp->dst[i].servicing.queue, 0, IRQQUEUE_SIZE_BITS); - } - /* Initialise timers */ - for (i = 0; i < OPENPIC_MAX_TMR; i++) { - opp->timers[i].tccr = 0; - opp->timers[i].tbcr = TBCR_CI; - } - /* Go out of RESET state */ - opp->gcr = 0; -} - -typedef struct MemReg { - const char *name; - MemoryRegionOps const *ops; - hwaddr start_addr; - ram_addr_t size; -} MemReg; - -static void fsl_common_init(OpenPICState *opp) -{ - int i; - int virq = OPENPIC_MAX_SRC; - - opp->vid = VID_REVISION_1_2; - opp->vir = VIR_GENERIC; - opp->vector_mask = 0xFFFF; - opp->tfrr_reset = 0; - opp->ivpr_reset = IVPR_MASK_MASK; - opp->idr_reset = 1 << 0; - opp->max_irq = OPENPIC_MAX_IRQ; - - opp->irq_ipi0 = virq; - virq += OPENPIC_MAX_IPI; - opp->irq_tim0 = virq; - virq += OPENPIC_MAX_TMR; - - assert(virq <= OPENPIC_MAX_IRQ); - - opp->irq_msi = 224; - - msi_nonbroken = true; - for (i = 0; i < opp->fsl->max_ext; i++) { - opp->src[i].level = false; - } - - /* Internal interrupts, including message and MSI */ - for (i = 16; i < OPENPIC_MAX_SRC; i++) { - opp->src[i].type = IRQ_TYPE_FSLINT; - opp->src[i].level = true; - } - - /* timers and IPIs */ - for (i = OPENPIC_MAX_SRC; i < virq; i++) { - opp->src[i].type = IRQ_TYPE_FSLSPECIAL; - opp->src[i].level = false; - } -} - -static void map_list(OpenPICState *opp, const MemReg *list, int *count) -{ - while (list->name) { - assert(*count < ARRAY_SIZE(opp->sub_io_mem)); - - memory_region_init_io(&opp->sub_io_mem[*count], OBJECT(opp), list->ops, - opp, list->name, list->size); - - memory_region_add_subregion(&opp->mem, list->start_addr, - &opp->sub_io_mem[*count]); - - (*count)++; - list++; - } -} - -static const VMStateDescription vmstate_openpic_irq_queue = { - .name = "openpic_irq_queue", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_BITMAP(queue, IRQQueue, 0, queue_size), - VMSTATE_INT32(next, IRQQueue), - VMSTATE_INT32(priority, IRQQueue), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_openpic_irqdest = { - .name = "openpic_irqdest", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_INT32(ctpr, IRQDest), - VMSTATE_STRUCT(raised, IRQDest, 0, vmstate_openpic_irq_queue, - IRQQueue), - VMSTATE_STRUCT(servicing, IRQDest, 0, vmstate_openpic_irq_queue, - IRQQueue), - VMSTATE_UINT32_ARRAY(outputs_active, IRQDest, OPENPIC_OUTPUT_NB), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_openpic_irqsource = { - .name = "openpic_irqsource", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(ivpr, IRQSource), - VMSTATE_UINT32(idr, IRQSource), - VMSTATE_UINT32(destmask, IRQSource), - VMSTATE_INT32(last_cpu, IRQSource), - VMSTATE_INT32(pending, IRQSource), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_openpic_timer = { - .name = "openpic_timer", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(tccr, OpenPICTimer), - VMSTATE_UINT32(tbcr, OpenPICTimer), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_openpic_msi = { - .name = "openpic_msi", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(msir, OpenPICMSI), - VMSTATE_END_OF_LIST() - } -}; - -static int openpic_post_load(void *opaque, int version_id) -{ - OpenPICState *opp = (OpenPICState *)opaque; - int i; - - /* Update internal ivpr and idr variables */ - for (i = 0; i < opp->max_irq; i++) { - write_IRQreg_idr(opp, i, opp->src[i].idr); - write_IRQreg_ivpr(opp, i, opp->src[i].ivpr); - } - - return 0; -} - -static const VMStateDescription vmstate_openpic = { - .name = "openpic", - .version_id = 3, - .minimum_version_id = 3, - .post_load = openpic_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(gcr, OpenPICState), - VMSTATE_UINT32(vir, OpenPICState), - VMSTATE_UINT32(pir, OpenPICState), - VMSTATE_UINT32(spve, OpenPICState), - VMSTATE_UINT32(tfrr, OpenPICState), - VMSTATE_UINT32(max_irq, OpenPICState), - VMSTATE_STRUCT_VARRAY_UINT32(src, OpenPICState, max_irq, 0, - vmstate_openpic_irqsource, IRQSource), - VMSTATE_UINT32_EQUAL(nb_cpus, OpenPICState), - VMSTATE_STRUCT_VARRAY_UINT32(dst, OpenPICState, nb_cpus, 0, - vmstate_openpic_irqdest, IRQDest), - VMSTATE_STRUCT_ARRAY(timers, OpenPICState, OPENPIC_MAX_TMR, 0, - vmstate_openpic_timer, OpenPICTimer), - VMSTATE_STRUCT_ARRAY(msi, OpenPICState, MAX_MSI, 0, - vmstate_openpic_msi, OpenPICMSI), - VMSTATE_UINT32(irq_ipi0, OpenPICState), - VMSTATE_UINT32(irq_tim0, OpenPICState), - VMSTATE_UINT32(irq_msi, OpenPICState), - VMSTATE_END_OF_LIST() - } -}; - -static void openpic_init(Object *obj) -{ - OpenPICState *opp = OPENPIC(obj); - - memory_region_init(&opp->mem, obj, "openpic", 0x40000); -} - -static void openpic_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *d = SYS_BUS_DEVICE(dev); - OpenPICState *opp = OPENPIC(dev); - int i, j; - int list_count = 0; - static const MemReg list_le[] = { - {"glb", &openpic_glb_ops_le, - OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE}, - {"tmr", &openpic_tmr_ops_le, - OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE}, - {"src", &openpic_src_ops_le, - OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE}, - {"cpu", &openpic_cpu_ops_le, - OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE}, - {NULL} - }; - static const MemReg list_be[] = { - {"glb", &openpic_glb_ops_be, - OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE}, - {"tmr", &openpic_tmr_ops_be, - OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE}, - {"src", &openpic_src_ops_be, - OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE}, - {"cpu", &openpic_cpu_ops_be, - OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE}, - {NULL} - }; - static const MemReg list_fsl[] = { - {"msi", &openpic_msi_ops_be, - OPENPIC_MSI_REG_START, OPENPIC_MSI_REG_SIZE}, - {"summary", &openpic_summary_ops_be, - OPENPIC_SUMMARY_REG_START, OPENPIC_SUMMARY_REG_SIZE}, - {NULL} - }; - - if (opp->nb_cpus > MAX_CPU) { - error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, - TYPE_OPENPIC, "nb_cpus", (uint64_t)opp->nb_cpus, - (uint64_t)0, (uint64_t)MAX_CPU); - return; - } - - switch (opp->model) { - case OPENPIC_MODEL_FSL_MPIC_20: - default: - opp->fsl = &fsl_mpic_20; - opp->brr1 = 0x00400200; - opp->flags |= OPENPIC_FLAG_IDR_CRIT; - opp->nb_irqs = 80; - opp->mpic_mode_mask = GCR_MODE_MIXED; - - fsl_common_init(opp); - map_list(opp, list_be, &list_count); - map_list(opp, list_fsl, &list_count); - - break; - - case OPENPIC_MODEL_FSL_MPIC_42: - opp->fsl = &fsl_mpic_42; - opp->brr1 = 0x00400402; - opp->flags |= OPENPIC_FLAG_ILR; - opp->nb_irqs = 196; - opp->mpic_mode_mask = GCR_MODE_PROXY; - - fsl_common_init(opp); - map_list(opp, list_be, &list_count); - map_list(opp, list_fsl, &list_count); - - break; - - case OPENPIC_MODEL_RAVEN: - opp->nb_irqs = RAVEN_MAX_EXT; - opp->vid = VID_REVISION_1_3; - opp->vir = VIR_GENERIC; - opp->vector_mask = 0xFF; - opp->tfrr_reset = 4160000; - opp->ivpr_reset = IVPR_MASK_MASK | IVPR_MODE_MASK; - opp->idr_reset = 0; - opp->max_irq = RAVEN_MAX_IRQ; - opp->irq_ipi0 = RAVEN_IPI_IRQ; - opp->irq_tim0 = RAVEN_TMR_IRQ; - opp->brr1 = -1; - opp->mpic_mode_mask = GCR_MODE_MIXED; - - if (opp->nb_cpus != 1) { - error_setg(errp, "Only UP supported today"); - return; - } - - map_list(opp, list_le, &list_count); - break; - } - - for (i = 0; i < opp->nb_cpus; i++) { - opp->dst[i].irqs = g_new0(qemu_irq, OPENPIC_OUTPUT_NB); - for (j = 0; j < OPENPIC_OUTPUT_NB; j++) { - sysbus_init_irq(d, &opp->dst[i].irqs[j]); - } - - opp->dst[i].raised.queue_size = IRQQUEUE_SIZE_BITS; - opp->dst[i].raised.queue = bitmap_new(IRQQUEUE_SIZE_BITS); - opp->dst[i].servicing.queue_size = IRQQUEUE_SIZE_BITS; - opp->dst[i].servicing.queue = bitmap_new(IRQQUEUE_SIZE_BITS); - } - - sysbus_init_mmio(d, &opp->mem); - qdev_init_gpio_in(dev, openpic_set_irq, opp->max_irq); -} - -static Property openpic_properties[] = { - DEFINE_PROP_UINT32("model", OpenPICState, model, OPENPIC_MODEL_FSL_MPIC_20), - DEFINE_PROP_UINT32("nb_cpus", OpenPICState, nb_cpus, 1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void openpic_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = openpic_realize; - dc->props = openpic_properties; - dc->reset = openpic_reset; - dc->vmsd = &vmstate_openpic; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo openpic_info = { - .name = TYPE_OPENPIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(OpenPICState), - .instance_init = openpic_init, - .class_init = openpic_class_init, -}; - -static void openpic_register_types(void) -{ - type_register_static(&openpic_info); -} - -type_init(openpic_register_types) diff --git a/qemu/hw/intc/openpic_kvm.c b/qemu/hw/intc/openpic_kvm.c deleted file mode 100644 index e47e94f2c..000000000 --- a/qemu/hw/intc/openpic_kvm.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * KVM in-kernel OpenPIC - * - * Copyright 2013 Freescale Semiconductor, Inc. - * - * 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 -#include "exec/address-spaces.h" -#include "hw/hw.h" -#include "hw/ppc/openpic.h" -#include "hw/pci/msi.h" -#include "hw/sysbus.h" -#include "sysemu/kvm.h" -#include "qemu/log.h" - -#define GCR_RESET 0x80000000 - -#define KVM_OPENPIC(obj) \ - OBJECT_CHECK(KVMOpenPICState, (obj), TYPE_KVM_OPENPIC) - -typedef struct KVMOpenPICState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion mem; - MemoryListener mem_listener; - uint32_t fd; - uint32_t model; - hwaddr mapped; -} KVMOpenPICState; - -static void kvm_openpic_set_irq(void *opaque, int n_IRQ, int level) -{ - kvm_set_irq(kvm_state, n_IRQ, level); -} - -static void kvm_openpic_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - KVMOpenPICState *opp = opaque; - struct kvm_device_attr attr; - uint32_t val32 = val; - int ret; - - attr.group = KVM_DEV_MPIC_GRP_REGISTER; - attr.attr = addr; - attr.addr = (uint64_t)(unsigned long)&val32; - - ret = ioctl(opp->fd, KVM_SET_DEVICE_ATTR, &attr); - if (ret < 0) { - qemu_log_mask(LOG_UNIMP, "%s: %s %" PRIx64 "\n", __func__, - strerror(errno), attr.attr); - } -} - -static void kvm_openpic_reset(DeviceState *d) -{ - KVMOpenPICState *opp = KVM_OPENPIC(d); - - /* Trigger the GCR.RESET bit to reset the PIC */ - kvm_openpic_write(opp, 0x1020, GCR_RESET, sizeof(uint32_t)); -} - -static uint64_t kvm_openpic_read(void *opaque, hwaddr addr, unsigned size) -{ - KVMOpenPICState *opp = opaque; - struct kvm_device_attr attr; - uint32_t val = 0xdeadbeef; - int ret; - - attr.group = KVM_DEV_MPIC_GRP_REGISTER; - attr.attr = addr; - attr.addr = (uint64_t)(unsigned long)&val; - - ret = ioctl(opp->fd, KVM_GET_DEVICE_ATTR, &attr); - if (ret < 0) { - qemu_log_mask(LOG_UNIMP, "%s: %s %" PRIx64 "\n", __func__, - strerror(errno), attr.attr); - return 0; - } - - return val; -} - -static const MemoryRegionOps kvm_openpic_mem_ops = { - .write = kvm_openpic_write, - .read = kvm_openpic_read, - .endianness = DEVICE_BIG_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void kvm_openpic_region_add(MemoryListener *listener, - MemoryRegionSection *section) -{ - KVMOpenPICState *opp = container_of(listener, KVMOpenPICState, - mem_listener); - struct kvm_device_attr attr; - uint64_t reg_base; - int ret; - - if (section->address_space != &address_space_memory) { - abort(); - } - - /* Ignore events on regions that are not us */ - if (section->mr != &opp->mem) { - return; - } - - if (opp->mapped) { - /* - * We can only map the MPIC once. Since we are already mapped, - * the best we can do is ignore new maps. - */ - return; - } - - reg_base = section->offset_within_address_space; - opp->mapped = reg_base; - - attr.group = KVM_DEV_MPIC_GRP_MISC; - attr.attr = KVM_DEV_MPIC_BASE_ADDR; - attr.addr = (uint64_t)(unsigned long)®_base; - - ret = ioctl(opp->fd, KVM_SET_DEVICE_ATTR, &attr); - if (ret < 0) { - fprintf(stderr, "%s: %s %" PRIx64 "\n", __func__, - strerror(errno), reg_base); - } -} - -static void kvm_openpic_region_del(MemoryListener *listener, - MemoryRegionSection *section) -{ - KVMOpenPICState *opp = container_of(listener, KVMOpenPICState, - mem_listener); - struct kvm_device_attr attr; - uint64_t reg_base = 0; - int ret; - - /* Ignore events on regions that are not us */ - if (section->mr != &opp->mem) { - return; - } - - if (section->offset_within_address_space != opp->mapped) { - /* - * We can only map the MPIC once. This mapping was a secondary - * one that we couldn't fulfill. Ignore it. - */ - return; - } - opp->mapped = 0; - - attr.group = KVM_DEV_MPIC_GRP_MISC; - attr.attr = KVM_DEV_MPIC_BASE_ADDR; - attr.addr = (uint64_t)(unsigned long)®_base; - - ret = ioctl(opp->fd, KVM_SET_DEVICE_ATTR, &attr); - if (ret < 0) { - fprintf(stderr, "%s: %s %" PRIx64 "\n", __func__, - strerror(errno), reg_base); - } -} - -static void kvm_openpic_init(Object *obj) -{ - KVMOpenPICState *opp = KVM_OPENPIC(obj); - - memory_region_init_io(&opp->mem, OBJECT(opp), &kvm_openpic_mem_ops, opp, - "kvm-openpic", 0x40000); -} - -static void kvm_openpic_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *d = SYS_BUS_DEVICE(dev); - KVMOpenPICState *opp = KVM_OPENPIC(dev); - KVMState *s = kvm_state; - int kvm_openpic_model; - struct kvm_create_device cd = {0}; - int ret, i; - - if (!kvm_check_extension(s, KVM_CAP_DEVICE_CTRL)) { - error_setg(errp, "Kernel is lacking Device Control API"); - return; - } - - switch (opp->model) { - case OPENPIC_MODEL_FSL_MPIC_20: - kvm_openpic_model = KVM_DEV_TYPE_FSL_MPIC_20; - break; - - case OPENPIC_MODEL_FSL_MPIC_42: - kvm_openpic_model = KVM_DEV_TYPE_FSL_MPIC_42; - break; - - default: - error_setg(errp, "Unsupported OpenPIC model %" PRIu32, opp->model); - return; - } - - cd.type = kvm_openpic_model; - ret = kvm_vm_ioctl(s, KVM_CREATE_DEVICE, &cd); - if (ret < 0) { - error_setg(errp, "Can't create device %d: %s", - cd.type, strerror(errno)); - return; - } - opp->fd = cd.fd; - - sysbus_init_mmio(d, &opp->mem); - qdev_init_gpio_in(dev, kvm_openpic_set_irq, OPENPIC_MAX_IRQ); - - opp->mem_listener.region_add = kvm_openpic_region_add; - opp->mem_listener.region_del = kvm_openpic_region_del; - memory_listener_register(&opp->mem_listener, &address_space_memory); - - /* indicate pic capabilities */ - msi_nonbroken = true; - kvm_kernel_irqchip = true; - kvm_async_interrupts_allowed = true; - - /* set up irq routing */ - kvm_init_irq_routing(kvm_state); - for (i = 0; i < 256; ++i) { - kvm_irqchip_add_irq_route(kvm_state, i, 0, i); - } - - kvm_msi_via_irqfd_allowed = true; - kvm_gsi_routing_allowed = true; - - kvm_irqchip_commit_routes(s); -} - -int kvm_openpic_connect_vcpu(DeviceState *d, CPUState *cs) -{ - KVMOpenPICState *opp = KVM_OPENPIC(d); - - return kvm_vcpu_enable_cap(cs, KVM_CAP_IRQ_MPIC, 0, opp->fd, - kvm_arch_vcpu_id(cs)); -} - -static Property kvm_openpic_properties[] = { - DEFINE_PROP_UINT32("model", KVMOpenPICState, model, - OPENPIC_MODEL_FSL_MPIC_20), - DEFINE_PROP_END_OF_LIST(), -}; - -static void kvm_openpic_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - 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 = { - .name = TYPE_KVM_OPENPIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(KVMOpenPICState), - .instance_init = kvm_openpic_init, - .class_init = kvm_openpic_class_init, -}; - -static void kvm_openpic_register_types(void) -{ - type_register_static(&kvm_openpic_info); -} - -type_init(kvm_openpic_register_types) diff --git a/qemu/hw/intc/pl190.c b/qemu/hw/intc/pl190.c deleted file mode 100644 index 5ecbc4a48..000000000 --- a/qemu/hw/intc/pl190.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * 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/sysbus.h" - -/* The number of virtual priority levels. 16 user vectors plus the - unvectored IRQ. Chained interrupts would require an additional level - if implemented. */ - -#define PL190_NUM_PRIO 17 - -#define TYPE_PL190 "pl190" -#define PL190(obj) OBJECT_CHECK(PL190State, (obj), TYPE_PL190) - -typedef struct PL190State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t level; - uint32_t soft_level; - uint32_t irq_enable; - uint32_t fiq_select; - uint8_t vect_control[16]; - uint32_t vect_addr[PL190_NUM_PRIO]; - /* Mask containing interrupts with higher priority than this one. */ - uint32_t prio_mask[PL190_NUM_PRIO + 1]; - int protected; - /* Current priority level. */ - int priority; - int prev_prio[PL190_NUM_PRIO]; - qemu_irq irq; - qemu_irq fiq; -} PL190State; - -static const unsigned char pl190_id[] = -{ 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 }; - -static inline uint32_t pl190_irq_level(PL190State *s) -{ - return (s->level | s->soft_level) & s->irq_enable & ~s->fiq_select; -} - -/* Update interrupts. */ -static void pl190_update(PL190State *s) -{ - uint32_t level = pl190_irq_level(s); - int set; - - set = (level & s->prio_mask[s->priority]) != 0; - qemu_set_irq(s->irq, set); - set = ((s->level | s->soft_level) & s->fiq_select) != 0; - qemu_set_irq(s->fiq, set); -} - -static void pl190_set_irq(void *opaque, int irq, int level) -{ - PL190State *s = (PL190State *)opaque; - - if (level) - s->level |= 1u << irq; - else - s->level &= ~(1u << irq); - pl190_update(s); -} - -static void pl190_update_vectors(PL190State *s) -{ - uint32_t mask; - int i; - int n; - - mask = 0; - for (i = 0; i < 16; i++) - { - s->prio_mask[i] = mask; - if (s->vect_control[i] & 0x20) - { - n = s->vect_control[i] & 0x1f; - mask |= 1 << n; - } - } - s->prio_mask[16] = mask; - pl190_update(s); -} - -static uint64_t pl190_read(void *opaque, hwaddr offset, - unsigned size) -{ - PL190State *s = (PL190State *)opaque; - int i; - - if (offset >= 0xfe0 && offset < 0x1000) { - return pl190_id[(offset - 0xfe0) >> 2]; - } - if (offset >= 0x100 && offset < 0x140) { - return s->vect_addr[(offset - 0x100) >> 2]; - } - if (offset >= 0x200 && offset < 0x240) { - return s->vect_control[(offset - 0x200) >> 2]; - } - switch (offset >> 2) { - case 0: /* IRQSTATUS */ - return pl190_irq_level(s); - case 1: /* FIQSATUS */ - return (s->level | s->soft_level) & s->fiq_select; - case 2: /* RAWINTR */ - return s->level | s->soft_level; - case 3: /* INTSELECT */ - return s->fiq_select; - case 4: /* INTENABLE */ - return s->irq_enable; - case 6: /* SOFTINT */ - return s->soft_level; - case 8: /* PROTECTION */ - return s->protected; - case 12: /* VECTADDR */ - /* Read vector address at the start of an ISR. Increases the - * current priority level to that of the current interrupt. - * - * Since an enabled interrupt X at priority P causes prio_mask[Y] - * to have bit X set for all Y > P, this loop will stop with - * i == the priority of the highest priority set interrupt. - */ - for (i = 0; i < s->priority; i++) { - if ((s->level | s->soft_level) & s->prio_mask[i + 1]) { - break; - } - } - - /* Reading this value with no pending interrupts is undefined. - We return the default address. */ - if (i == PL190_NUM_PRIO) - return s->vect_addr[16]; - if (i < s->priority) - { - s->prev_prio[i] = s->priority; - s->priority = i; - pl190_update(s); - } - return s->vect_addr[s->priority]; - case 13: /* DEFVECTADDR */ - return s->vect_addr[16]; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pl190_read: Bad offset %x\n", (int)offset); - return 0; - } -} - -static void pl190_write(void *opaque, hwaddr offset, - uint64_t val, unsigned size) -{ - PL190State *s = (PL190State *)opaque; - - if (offset >= 0x100 && offset < 0x140) { - s->vect_addr[(offset - 0x100) >> 2] = val; - pl190_update_vectors(s); - return; - } - if (offset >= 0x200 && offset < 0x240) { - s->vect_control[(offset - 0x200) >> 2] = val; - pl190_update_vectors(s); - return; - } - switch (offset >> 2) { - case 0: /* SELECT */ - /* This is a readonly register, but linux tries to write to it - anyway. Ignore the write. */ - break; - case 3: /* INTSELECT */ - s->fiq_select = val; - break; - case 4: /* INTENABLE */ - s->irq_enable |= val; - break; - case 5: /* INTENCLEAR */ - s->irq_enable &= ~val; - break; - case 6: /* SOFTINT */ - s->soft_level |= val; - break; - case 7: /* SOFTINTCLEAR */ - s->soft_level &= ~val; - break; - case 8: /* PROTECTION */ - /* TODO: Protection (supervisor only access) is not implemented. */ - s->protected = val & 1; - break; - case 12: /* VECTADDR */ - /* Restore the previous priority level. The value written is - ignored. */ - if (s->priority < PL190_NUM_PRIO) - s->priority = s->prev_prio[s->priority]; - break; - case 13: /* DEFVECTADDR */ - s->vect_addr[16] = val; - break; - case 0xc0: /* ITCR */ - if (val) { - qemu_log_mask(LOG_UNIMP, "pl190: Test mode not implemented\n"); - } - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pl190_write: Bad offset %x\n", (int)offset); - return; - } - pl190_update(s); -} - -static const MemoryRegionOps pl190_ops = { - .read = pl190_read, - .write = pl190_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pl190_reset(DeviceState *d) -{ - PL190State *s = PL190(d); - int i; - - for (i = 0; i < 16; i++) { - s->vect_addr[i] = 0; - s->vect_control[i] = 0; - } - s->vect_addr[16] = 0; - s->prio_mask[17] = 0xffffffff; - s->priority = PL190_NUM_PRIO; - pl190_update_vectors(s); -} - -static int pl190_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - PL190State *s = PL190(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &pl190_ops, s, "pl190", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - qdev_init_gpio_in(dev, pl190_set_irq, 32); - sysbus_init_irq(sbd, &s->irq); - sysbus_init_irq(sbd, &s->fiq); - return 0; -} - -static const VMStateDescription vmstate_pl190 = { - .name = "pl190", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(level, PL190State), - VMSTATE_UINT32(soft_level, PL190State), - VMSTATE_UINT32(irq_enable, PL190State), - VMSTATE_UINT32(fiq_select, PL190State), - VMSTATE_UINT8_ARRAY(vect_control, PL190State, 16), - VMSTATE_UINT32_ARRAY(vect_addr, PL190State, PL190_NUM_PRIO), - VMSTATE_UINT32_ARRAY(prio_mask, PL190State, PL190_NUM_PRIO+1), - VMSTATE_INT32(protected, PL190State), - VMSTATE_INT32(priority, PL190State), - VMSTATE_INT32_ARRAY(prev_prio, PL190State, PL190_NUM_PRIO), - VMSTATE_END_OF_LIST() - } -}; - -static void pl190_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = pl190_init; - dc->reset = pl190_reset; - dc->vmsd = &vmstate_pl190; -} - -static const TypeInfo pl190_info = { - .name = TYPE_PL190, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PL190State), - .class_init = pl190_class_init, -}; - -static void pl190_register_types(void) -{ - type_register_static(&pl190_info); -} - -type_init(pl190_register_types) diff --git a/qemu/hw/intc/puv3_intc.c b/qemu/hw/intc/puv3_intc.c deleted file mode 100644 index ef8488aac..000000000 --- a/qemu/hw/intc/puv3_intc.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * INTC device simulation in PKUnity SoC - * - * Copyright (C) 2010-2012 Guan Xuetao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation, or any later version. - * See the COPYING file in the top-level directory. - */ -#include "qemu/osdep.h" -#include "hw/sysbus.h" - -#undef DEBUG_PUV3 -#include "hw/unicore32/puv3.h" - -#define TYPE_PUV3_INTC "puv3_intc" -#define PUV3_INTC(obj) OBJECT_CHECK(PUV3INTCState, (obj), TYPE_PUV3_INTC) - -typedef struct PUV3INTCState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq parent_irq; - - uint32_t reg_ICMR; - uint32_t reg_ICPR; -} PUV3INTCState; - -/* Update interrupt status after enabled or pending bits have been changed. */ -static void puv3_intc_update(PUV3INTCState *s) -{ - if (s->reg_ICMR & s->reg_ICPR) { - qemu_irq_raise(s->parent_irq); - } else { - qemu_irq_lower(s->parent_irq); - } -} - -/* Process a change in an external INTC input. */ -static void puv3_intc_handler(void *opaque, int irq, int level) -{ - PUV3INTCState *s = opaque; - - DPRINTF("irq 0x%x, level 0x%x\n", irq, level); - if (level) { - s->reg_ICPR |= (1 << irq); - } else { - s->reg_ICPR &= ~(1 << irq); - } - puv3_intc_update(s); -} - -static uint64_t puv3_intc_read(void *opaque, hwaddr offset, - unsigned size) -{ - PUV3INTCState *s = opaque; - uint32_t ret = 0; - - switch (offset) { - case 0x04: /* INTC_ICMR */ - ret = s->reg_ICMR; - break; - case 0x0c: /* INTC_ICIP */ - ret = s->reg_ICPR; /* the same value with ICPR */ - break; - default: - DPRINTF("Bad offset %x\n", (int)offset); - } - DPRINTF("offset 0x%x, value 0x%x\n", offset, ret); - return ret; -} - -static void puv3_intc_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PUV3INTCState *s = opaque; - - DPRINTF("offset 0x%x, value 0x%x\n", offset, value); - switch (offset) { - case 0x00: /* INTC_ICLR */ - case 0x14: /* INTC_ICCR */ - break; - case 0x04: /* INTC_ICMR */ - s->reg_ICMR = value; - break; - default: - DPRINTF("Bad offset 0x%x\n", (int)offset); - return; - } - puv3_intc_update(s); -} - -static const MemoryRegionOps puv3_intc_ops = { - .read = puv3_intc_read, - .write = puv3_intc_write, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int puv3_intc_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - PUV3INTCState *s = PUV3_INTC(dev); - - qdev_init_gpio_in(dev, puv3_intc_handler, PUV3_IRQS_NR); - sysbus_init_irq(sbd, &s->parent_irq); - - s->reg_ICMR = 0; - s->reg_ICPR = 0; - - memory_region_init_io(&s->iomem, OBJECT(s), &puv3_intc_ops, s, "puv3_intc", - PUV3_REGS_OFFSET); - sysbus_init_mmio(sbd, &s->iomem); - - return 0; -} - -static void puv3_intc_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = puv3_intc_init; -} - -static const TypeInfo puv3_intc_info = { - .name = TYPE_PUV3_INTC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PUV3INTCState), - .class_init = puv3_intc_class_init, -}; - -static void puv3_intc_register_type(void) -{ - type_register_static(&puv3_intc_info); -} - -type_init(puv3_intc_register_type) diff --git a/qemu/hw/intc/realview_gic.c b/qemu/hw/intc/realview_gic.c deleted file mode 100644 index 50bbab66e..000000000 --- a/qemu/hw/intc/realview_gic.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * ARM RealView Emulation Baseboard Interrupt Controller - * - * Copyright (c) 2006-2007 CodeSourcery. - * Written by Paul Brook - * - * 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) -{ - RealViewGICState *s = (RealViewGICState *)opaque; - - qemu_set_irq(qdev_get_gpio_in(DEVICE(&s->gic), irq), level); -} - -static void realview_gic_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - RealViewGICState *s = REALVIEW_GIC(dev); - SysBusDevice *busdev; - Error *err = NULL; - /* The GICs on the RealView boards have a fixed nonconfigurable - * number of interrupt lines, so we don't need to expose this as - * a qdev property. - */ - int numirq = 96; - - qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", numirq); - object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - busdev = SYS_BUS_DEVICE(&s->gic); - - /* Pass through outbound IRQ lines from the GIC */ - sysbus_pass_irq(sbd, busdev); - - /* Pass through inbound GPIO lines to the GIC */ - qdev_init_gpio_in(dev, realview_gic_set_irq, numirq - 32); - - memory_region_add_subregion(&s->container, 0, - sysbus_mmio_get_region(busdev, 1)); - memory_region_add_subregion(&s->container, 0x1000, - sysbus_mmio_get_region(busdev, 0)); -} - -static void realview_gic_init(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - RealViewGICState *s = REALVIEW_GIC(obj); - DeviceState *gicdev; - - memory_region_init(&s->container, OBJECT(s), - "realview-gic-container", 0x2000); - sysbus_init_mmio(sbd, &s->container); - - object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC); - gicdev = DEVICE(&s->gic); - qdev_set_parent_bus(gicdev, sysbus_get_default()); - qdev_prop_set_uint32(gicdev, "num-cpu", 1); -} - -static void realview_gic_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = realview_gic_realize; -} - -static const TypeInfo realview_gic_info = { - .name = TYPE_REALVIEW_GIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(RealViewGICState), - .instance_init = realview_gic_init, - .class_init = realview_gic_class_init, -}; - -static void realview_gic_register_types(void) -{ - type_register_static(&realview_gic_info); -} - -type_init(realview_gic_register_types) diff --git a/qemu/hw/intc/s390_flic.c b/qemu/hw/intc/s390_flic.c deleted file mode 100644 index bc75fa7d9..000000000 --- a/qemu/hw/intc/s390_flic.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * QEMU S390x floating interrupt controller (flic) - * - * Copyright 2014 IBM Corp. - * Author(s): Jens Freimann - * Cornelia Huck - * - * 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 "qemu/error-report.h" -#include "hw/sysbus.h" -#include "migration/qemu-file.h" -#include "hw/s390x/s390_flic.h" -#include "trace.h" - -S390FLICState *s390_get_flic(void) -{ - S390FLICState *fs; - - fs = S390_FLIC_COMMON(object_resolve_path(TYPE_KVM_S390_FLIC, NULL)); - if (!fs) { - fs = S390_FLIC_COMMON(object_resolve_path(TYPE_QEMU_S390_FLIC, NULL)); - } - return fs; -} - -void s390_flic_init(void) -{ - DeviceState *dev; - - dev = s390_flic_kvm_create(); - if (!dev) { - dev = qdev_create(NULL, TYPE_QEMU_S390_FLIC); - object_property_add_child(qdev_get_machine(), TYPE_QEMU_S390_FLIC, - OBJECT(dev), NULL); - } - qdev_init_nofail(dev); -} - -static int qemu_s390_register_io_adapter(S390FLICState *fs, uint32_t id, - uint8_t isc, bool swap, - bool is_maskable) -{ - /* nothing to do */ - return 0; -} - -static int qemu_s390_io_adapter_map(S390FLICState *fs, uint32_t id, - uint64_t map_addr, bool do_map) -{ - /* nothing to do */ - return 0; -} - -static int qemu_s390_add_adapter_routes(S390FLICState *fs, - AdapterRoutes *routes) -{ - return -ENOSYS; -} - -static void qemu_s390_release_adapter_routes(S390FLICState *fs, - AdapterRoutes *routes) -{ -} - -static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) -{ - S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); - - fsc->register_io_adapter = qemu_s390_register_io_adapter; - fsc->io_adapter_map = qemu_s390_io_adapter_map; - fsc->add_adapter_routes = qemu_s390_add_adapter_routes; - fsc->release_adapter_routes = qemu_s390_release_adapter_routes; -} - -static const TypeInfo qemu_s390_flic_info = { - .name = TYPE_QEMU_S390_FLIC, - .parent = TYPE_S390_FLIC_COMMON, - .instance_size = sizeof(QEMUS390FLICState), - .class_init = qemu_s390_flic_class_init, -}; - -static const TypeInfo s390_flic_common_info = { - .name = TYPE_S390_FLIC_COMMON, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(S390FLICState), - .class_size = sizeof(S390FLICStateClass), -}; - -static void qemu_s390_flic_register_types(void) -{ - type_register_static(&s390_flic_common_info); - type_register_static(&qemu_s390_flic_info); -} - -type_init(qemu_s390_flic_register_types) diff --git a/qemu/hw/intc/s390_flic_kvm.c b/qemu/hw/intc/s390_flic_kvm.c deleted file mode 100644 index 02449b390..000000000 --- a/qemu/hw/intc/s390_flic_kvm.c +++ /dev/null @@ -1,435 +0,0 @@ -/* - * QEMU S390x KVM floating interrupt controller (flic) - * - * Copyright 2014 IBM Corp. - * Author(s): Jens Freimann - * Cornelia Huck - * - * 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 -#include "qemu/error-report.h" -#include "hw/sysbus.h" -#include "sysemu/kvm.h" -#include "migration/qemu-file.h" -#include "hw/s390x/s390_flic.h" -#include "hw/s390x/adapter.h" -#include "trace.h" - -#define FLIC_SAVE_INITIAL_SIZE getpagesize() -#define FLIC_FAILED (-1UL) -#define FLIC_SAVEVM_VERSION 1 - -typedef struct KVMS390FLICState { - S390FLICState parent_obj; - - uint32_t fd; -} KVMS390FLICState; - -DeviceState *s390_flic_kvm_create(void) -{ - DeviceState *dev = NULL; - - if (kvm_enabled()) { - dev = qdev_create(NULL, TYPE_KVM_S390_FLIC); - object_property_add_child(qdev_get_machine(), TYPE_KVM_S390_FLIC, - OBJECT(dev), NULL); - } - return dev; -} - -/** - * flic_get_all_irqs - store all pending irqs in buffer - * @buf: pointer to buffer which is passed to kernel - * @len: length of buffer - * @flic: pointer to flic device state - * - * Returns: -ENOMEM if buffer is too small, - * -EINVAL if attr.group is invalid, - * -EFAULT if copying to userspace failed, - * on success return number of stored interrupts - */ -static int flic_get_all_irqs(KVMS390FLICState *flic, - void *buf, int len) -{ - struct kvm_device_attr attr = { - .group = KVM_DEV_FLIC_GET_ALL_IRQS, - .addr = (uint64_t) buf, - .attr = len, - }; - int rc; - - rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr); - - return rc == -1 ? -errno : rc; -} - -static void flic_enable_pfault(KVMS390FLICState *flic) -{ - struct kvm_device_attr attr = { - .group = KVM_DEV_FLIC_APF_ENABLE, - }; - int rc; - - rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); - - if (rc) { - fprintf(stderr, "flic: couldn't enable pfault\n"); - } -} - -static void flic_disable_wait_pfault(KVMS390FLICState *flic) -{ - struct kvm_device_attr attr = { - .group = KVM_DEV_FLIC_APF_DISABLE_WAIT, - }; - int rc; - - rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); - - if (rc) { - fprintf(stderr, "flic: couldn't disable pfault\n"); - } -} - -/** flic_enqueue_irqs - returns 0 on success - * @buf: pointer to buffer which is passed to kernel - * @len: length of buffer - * @flic: pointer to flic device state - * - * Returns: -EINVAL if attr.group is unknown - */ -static int flic_enqueue_irqs(void *buf, uint64_t len, - KVMS390FLICState *flic) -{ - int rc; - struct kvm_device_attr attr = { - .group = KVM_DEV_FLIC_ENQUEUE, - .addr = (uint64_t) buf, - .attr = len, - }; - - rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); - - return rc ? -errno : 0; -} - -int kvm_s390_inject_flic(struct kvm_s390_irq *irq) -{ - static KVMS390FLICState *flic; - - if (unlikely(!flic)) { - flic = KVM_S390_FLIC(s390_get_flic()); - } - return flic_enqueue_irqs(irq, sizeof(*irq), flic); -} - -/** - * __get_all_irqs - store all pending irqs in buffer - * @flic: pointer to flic device state - * @buf: pointer to pointer to a buffer - * @len: length of buffer - * - * Returns: return value of flic_get_all_irqs - * Note: Retry and increase buffer size until flic_get_all_irqs - * either returns a value >= 0 or a negative error code. - * -ENOMEM is an exception, which means the buffer is too small - * and we should try again. Other negative error codes can be - * -EFAULT and -EINVAL which we ignore at this point - */ -static int __get_all_irqs(KVMS390FLICState *flic, - void **buf, int len) -{ - int r; - - do { - /* returns -ENOMEM if buffer is too small and number - * of queued interrupts on success */ - r = flic_get_all_irqs(flic, *buf, len); - if (r >= 0) { - break; - } - len *= 2; - *buf = g_try_realloc(*buf, len); - if (!buf) { - return -ENOMEM; - } - } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER); - - return r; -} - -static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id, - uint8_t isc, bool swap, - bool is_maskable) -{ - struct kvm_s390_io_adapter adapter = { - .id = id, - .isc = isc, - .maskable = is_maskable, - .swap = swap, - }; - KVMS390FLICState *flic = KVM_S390_FLIC(fs); - int r, ret; - struct kvm_device_attr attr = { - .group = KVM_DEV_FLIC_ADAPTER_REGISTER, - .addr = (uint64_t)&adapter, - }; - - if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) { - /* nothing to do */ - return 0; - } - - r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); - - ret = r ? -errno : 0; - return ret; -} - -static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id, - uint64_t map_addr, bool do_map) -{ - struct kvm_s390_io_adapter_req req = { - .id = id, - .type = do_map ? KVM_S390_IO_ADAPTER_MAP : KVM_S390_IO_ADAPTER_UNMAP, - .addr = map_addr, - }; - struct kvm_device_attr attr = { - .group = KVM_DEV_FLIC_ADAPTER_MODIFY, - .addr = (uint64_t)&req, - }; - KVMS390FLICState *flic = KVM_S390_FLIC(fs); - int r; - - if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) { - /* nothing to do */ - return 0; - } - - r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); - return r ? -errno : 0; -} - -static int kvm_s390_add_adapter_routes(S390FLICState *fs, - AdapterRoutes *routes) -{ - int ret, i; - uint64_t ind_offset = routes->adapter.ind_offset; - - for (i = 0; i < routes->num_routes; i++) { - ret = kvm_irqchip_add_adapter_route(kvm_state, &routes->adapter); - if (ret < 0) { - goto out_undo; - } - 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; -out_undo: - while (--i >= 0) { - kvm_irqchip_release_virq(kvm_state, routes->gsi[i]); - routes->gsi[i] = -1; - } - routes->adapter.ind_offset = ind_offset; - return ret; -} - -static void kvm_s390_release_adapter_routes(S390FLICState *fs, - AdapterRoutes *routes) -{ - int i; - - for (i = 0; i < routes->num_routes; i++) { - if (routes->gsi[i] >= 0) { - kvm_irqchip_release_virq(kvm_state, routes->gsi[i]); - routes->gsi[i] = -1; - } - } -} - -/** - * kvm_flic_save - Save pending floating interrupts - * @f: QEMUFile containing migration state - * @opaque: pointer to flic device state - * - * Note: Pass buf and len to kernel. Start with one page and - * increase until buffer is sufficient or maxium size is - * reached - */ -static void kvm_flic_save(QEMUFile *f, void *opaque) -{ - KVMS390FLICState *flic = opaque; - int len = FLIC_SAVE_INITIAL_SIZE; - void *buf; - int count; - - flic_disable_wait_pfault((struct KVMS390FLICState *) opaque); - - buf = g_try_malloc0(len); - if (!buf) { - /* Storing FLIC_FAILED into the count field here will cause the - * target system to fail when attempting to load irqs from the - * migration state */ - error_report("flic: couldn't allocate memory"); - qemu_put_be64(f, FLIC_FAILED); - return; - } - - count = __get_all_irqs(flic, &buf, len); - if (count < 0) { - error_report("flic: couldn't retrieve irqs from kernel, rc %d", - count); - /* Storing FLIC_FAILED into the count field here will cause the - * target system to fail when attempting to load irqs from the - * migration state */ - qemu_put_be64(f, FLIC_FAILED); - } else { - qemu_put_be64(f, count); - qemu_put_buffer(f, (uint8_t *) buf, - count * sizeof(struct kvm_s390_irq)); - } - g_free(buf); -} - -/** - * kvm_flic_load - Load pending floating interrupts - * @f: QEMUFile containing migration state - * @opaque: pointer to flic device state - * @version_id: version id for migration - * - * Returns: value of flic_enqueue_irqs, -EINVAL on error - * Note: Do nothing when no interrupts where stored - * in QEMUFile - */ -static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id) -{ - uint64_t len = 0; - uint64_t count = 0; - void *buf = NULL; - int r = 0; - - if (version_id != FLIC_SAVEVM_VERSION) { - r = -EINVAL; - goto out; - } - - flic_enable_pfault((struct KVMS390FLICState *) opaque); - - count = qemu_get_be64(f); - len = count * sizeof(struct kvm_s390_irq); - if (count == FLIC_FAILED) { - r = -EINVAL; - goto out; - } - if (count == 0) { - r = 0; - goto out; - } - buf = g_try_malloc0(len); - if (!buf) { - r = -ENOMEM; - goto out; - } - - if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) { - r = -EINVAL; - goto out_free; - } - r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque); - -out_free: - g_free(buf); -out: - return r; -} - -static void kvm_s390_flic_realize(DeviceState *dev, Error **errp) -{ - KVMS390FLICState *flic_state = KVM_S390_FLIC(dev); - struct kvm_create_device cd = {0}; - int ret; - - flic_state->fd = -1; - if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) { - trace_flic_no_device_api(errno); - return; - } - - cd.type = KVM_DEV_TYPE_FLIC; - ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd); - if (ret < 0) { - trace_flic_create_device(errno); - return; - } - flic_state->fd = cd.fd; - - /* Register savevm handler for floating interrupts */ - register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save, - kvm_flic_load, (void *) flic_state); -} - -static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp) -{ - KVMS390FLICState *flic_state = KVM_S390_FLIC(dev); - - unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state); -} - -static void kvm_s390_flic_reset(DeviceState *dev) -{ - KVMS390FLICState *flic = KVM_S390_FLIC(dev); - struct kvm_device_attr attr = { - .group = KVM_DEV_FLIC_CLEAR_IRQS, - }; - int rc = 0; - - if (flic->fd == -1) { - return; - } - - flic_disable_wait_pfault(flic); - - rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); - if (rc) { - trace_flic_reset_failed(errno); - } - - flic_enable_pfault(flic); -} - -static void kvm_s390_flic_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); - - dc->realize = kvm_s390_flic_realize; - dc->unrealize = kvm_s390_flic_unrealize; - dc->reset = kvm_s390_flic_reset; - fsc->register_io_adapter = kvm_s390_register_io_adapter; - fsc->io_adapter_map = kvm_s390_io_adapter_map; - fsc->add_adapter_routes = kvm_s390_add_adapter_routes; - fsc->release_adapter_routes = kvm_s390_release_adapter_routes; -} - -static const TypeInfo kvm_s390_flic_info = { - .name = TYPE_KVM_S390_FLIC, - .parent = TYPE_S390_FLIC_COMMON, - .instance_size = sizeof(KVMS390FLICState), - .class_init = kvm_s390_flic_class_init, -}; - -static void kvm_s390_flic_register_types(void) -{ - type_register_static(&kvm_s390_flic_info); -} - -type_init(kvm_s390_flic_register_types) diff --git a/qemu/hw/intc/sh_intc.c b/qemu/hw/intc/sh_intc.c deleted file mode 100644 index 6ce2a8084..000000000 --- a/qemu/hw/intc/sh_intc.c +++ /dev/null @@ -1,515 +0,0 @@ -/* - * SuperH interrupt controller module - * - * Copyright (c) 2007 Magnus Damm - * Based on sh_timer.c and arm_timer.c by Paul Brook - * Copyright (c) 2005-2006 CodeSourcery. - * - * 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" - -//#define DEBUG_INTC -//#define DEBUG_INTC_SOURCES - -#define INTC_A7(x) ((x) & 0x1fffffff) - -void sh_intc_toggle_source(struct intc_source *source, - int enable_adj, int assert_adj) -{ - int enable_changed = 0; - int pending_changed = 0; - int old_pending; - - if ((source->enable_count == source->enable_max) && (enable_adj == -1)) - enable_changed = -1; - - source->enable_count += enable_adj; - - if (source->enable_count == source->enable_max) - enable_changed = 1; - - source->asserted += assert_adj; - - old_pending = source->pending; - source->pending = source->asserted && - (source->enable_count == source->enable_max); - - if (old_pending != source->pending) - pending_changed = 1; - - if (pending_changed) { - if (source->pending) { - source->parent->pending++; - if (source->parent->pending == 1) { - cpu_interrupt(first_cpu, CPU_INTERRUPT_HARD); - } - } else { - source->parent->pending--; - if (source->parent->pending == 0) { - cpu_reset_interrupt(first_cpu, CPU_INTERRUPT_HARD); - } - } - } - - if (enable_changed || assert_adj || pending_changed) { -#ifdef DEBUG_INTC_SOURCES - printf("sh_intc: (%d/%d/%d/%d) interrupt source 0x%x %s%s%s\n", - source->parent->pending, - source->asserted, - source->enable_count, - source->enable_max, - source->vect, - source->asserted ? "asserted " : - assert_adj ? "deasserted" : "", - enable_changed == 1 ? "enabled " : - enable_changed == -1 ? "disabled " : "", - source->pending ? "pending" : ""); -#endif - } -} - -static void sh_intc_set_irq (void *opaque, int n, int level) -{ - struct intc_desc *desc = opaque; - struct intc_source *source = &(desc->sources[n]); - - if (level && !source->asserted) - sh_intc_toggle_source(source, 0, 1); - else if (!level && source->asserted) - sh_intc_toggle_source(source, 0, -1); -} - -int sh_intc_get_pending_vector(struct intc_desc *desc, int imask) -{ - unsigned int i; - - /* slow: use a linked lists of pending sources instead */ - /* wrong: take interrupt priority into account (one list per priority) */ - - if (imask == 0x0f) { - return -1; /* FIXME, update code to include priority per source */ - } - - for (i = 0; i < desc->nr_sources; i++) { - struct intc_source *source = desc->sources + i; - - if (source->pending) { -#ifdef DEBUG_INTC_SOURCES - printf("sh_intc: (%d) returning interrupt source 0x%x\n", - desc->pending, source->vect); -#endif - return source->vect; - } - } - - abort(); -} - -#define INTC_MODE_NONE 0 -#define INTC_MODE_DUAL_SET 1 -#define INTC_MODE_DUAL_CLR 2 -#define INTC_MODE_ENABLE_REG 3 -#define INTC_MODE_MASK_REG 4 -#define INTC_MODE_IS_PRIO 8 - -static unsigned int sh_intc_mode(unsigned long address, - unsigned long set_reg, unsigned long clr_reg) -{ - if ((address != INTC_A7(set_reg)) && - (address != INTC_A7(clr_reg))) - return INTC_MODE_NONE; - - if (set_reg && clr_reg) { - if (address == INTC_A7(set_reg)) - return INTC_MODE_DUAL_SET; - else - return INTC_MODE_DUAL_CLR; - } - - if (set_reg) - return INTC_MODE_ENABLE_REG; - else - return INTC_MODE_MASK_REG; -} - -static void sh_intc_locate(struct intc_desc *desc, - unsigned long address, - unsigned long **datap, - intc_enum **enums, - unsigned int *first, - unsigned int *width, - unsigned int *modep) -{ - unsigned int i, mode; - - /* this is slow but works for now */ - - if (desc->mask_regs) { - for (i = 0; i < desc->nr_mask_regs; i++) { - struct intc_mask_reg *mr = desc->mask_regs + i; - - mode = sh_intc_mode(address, mr->set_reg, mr->clr_reg); - if (mode == INTC_MODE_NONE) - continue; - - *modep = mode; - *datap = &mr->value; - *enums = mr->enum_ids; - *first = mr->reg_width - 1; - *width = 1; - return; - } - } - - if (desc->prio_regs) { - for (i = 0; i < desc->nr_prio_regs; i++) { - struct intc_prio_reg *pr = desc->prio_regs + i; - - mode = sh_intc_mode(address, pr->set_reg, pr->clr_reg); - if (mode == INTC_MODE_NONE) - continue; - - *modep = mode | INTC_MODE_IS_PRIO; - *datap = &pr->value; - *enums = pr->enum_ids; - *first = (pr->reg_width / pr->field_width) - 1; - *width = pr->field_width; - return; - } - } - - abort(); -} - -static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id, - int enable, int is_group) -{ - struct intc_source *source = desc->sources + id; - - if (!id) - return; - - if (!source->next_enum_id && (!source->enable_max || !source->vect)) { -#ifdef DEBUG_INTC_SOURCES - printf("sh_intc: reserved interrupt source %d modified\n", id); -#endif - return; - } - - if (source->vect) - sh_intc_toggle_source(source, enable ? 1 : -1, 0); - -#ifdef DEBUG_INTC - else { - printf("setting interrupt group %d to %d\n", id, !!enable); - } -#endif - - if ((is_group || !source->vect) && source->next_enum_id) { - sh_intc_toggle_mask(desc, source->next_enum_id, enable, 1); - } - -#ifdef DEBUG_INTC - if (!source->vect) { - printf("setting interrupt group %d to %d - done\n", id, !!enable); - } -#endif -} - -static uint64_t sh_intc_read(void *opaque, hwaddr offset, - unsigned size) -{ - struct intc_desc *desc = opaque; - intc_enum *enum_ids = NULL; - unsigned int first = 0; - unsigned int width = 0; - unsigned int mode = 0; - unsigned long *valuep; - -#ifdef DEBUG_INTC - printf("sh_intc_read 0x%lx\n", (unsigned long) offset); -#endif - - sh_intc_locate(desc, (unsigned long)offset, &valuep, - &enum_ids, &first, &width, &mode); - return *valuep; -} - -static void sh_intc_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - struct intc_desc *desc = opaque; - intc_enum *enum_ids = NULL; - unsigned int first = 0; - unsigned int width = 0; - unsigned int mode = 0; - unsigned int k; - unsigned long *valuep; - unsigned long mask; - -#ifdef DEBUG_INTC - printf("sh_intc_write 0x%lx 0x%08x\n", (unsigned long) offset, value); -#endif - - sh_intc_locate(desc, (unsigned long)offset, &valuep, - &enum_ids, &first, &width, &mode); - - switch (mode) { - case INTC_MODE_ENABLE_REG | INTC_MODE_IS_PRIO: break; - case INTC_MODE_DUAL_SET: value |= *valuep; break; - case INTC_MODE_DUAL_CLR: value = *valuep & ~value; break; - default: abort(); - } - - for (k = 0; k <= first; k++) { - mask = ((1 << width) - 1) << ((first - k) * width); - - if ((*valuep & mask) == (value & mask)) - continue; -#if 0 - printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n", - k, first, enum_ids[k], (unsigned int)mask); -#endif - sh_intc_toggle_mask(desc, enum_ids[k], value & mask, 0); - } - - *valuep = value; - -#ifdef DEBUG_INTC - printf("sh_intc_write 0x%lx -> 0x%08x\n", (unsigned long) offset, value); -#endif -} - -static const MemoryRegionOps sh_intc_ops = { - .read = sh_intc_read, - .write = sh_intc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id) -{ - if (id) - return desc->sources + id; - - return NULL; -} - -static unsigned int sh_intc_register(MemoryRegion *sysmem, - struct intc_desc *desc, - const unsigned long address, - const char *type, - const char *action, - const unsigned int index) -{ - char name[60]; - MemoryRegion *iomem, *iomem_p4, *iomem_a7; - - if (!address) { - return 0; - } - - iomem = &desc->iomem; - iomem_p4 = desc->iomem_aliases + index; - iomem_a7 = iomem_p4 + 1; - -#define SH_INTC_IOMEM_FORMAT "interrupt-controller-%s-%s-%s" - snprintf(name, sizeof(name), SH_INTC_IOMEM_FORMAT, type, action, "p4"); - memory_region_init_alias(iomem_p4, NULL, name, iomem, INTC_A7(address), 4); - memory_region_add_subregion(sysmem, P4ADDR(address), iomem_p4); - - snprintf(name, sizeof(name), SH_INTC_IOMEM_FORMAT, type, action, "a7"); - memory_region_init_alias(iomem_a7, NULL, name, iomem, INTC_A7(address), 4); - memory_region_add_subregion(sysmem, A7ADDR(address), iomem_a7); -#undef SH_INTC_IOMEM_FORMAT - - /* used to increment aliases index */ - return 2; -} - -static void sh_intc_register_source(struct intc_desc *desc, - intc_enum source, - struct intc_group *groups, - int nr_groups) -{ - unsigned int i, k; - struct intc_source *s; - - if (desc->mask_regs) { - for (i = 0; i < desc->nr_mask_regs; i++) { - struct intc_mask_reg *mr = desc->mask_regs + i; - - for (k = 0; k < ARRAY_SIZE(mr->enum_ids); k++) { - if (mr->enum_ids[k] != source) - continue; - - s = sh_intc_source(desc, mr->enum_ids[k]); - if (s) - s->enable_max++; - } - } - } - - if (desc->prio_regs) { - for (i = 0; i < desc->nr_prio_regs; i++) { - struct intc_prio_reg *pr = desc->prio_regs + i; - - for (k = 0; k < ARRAY_SIZE(pr->enum_ids); k++) { - if (pr->enum_ids[k] != source) - continue; - - s = sh_intc_source(desc, pr->enum_ids[k]); - if (s) - s->enable_max++; - } - } - } - - if (groups) { - for (i = 0; i < nr_groups; i++) { - struct intc_group *gr = groups + i; - - for (k = 0; k < ARRAY_SIZE(gr->enum_ids); k++) { - if (gr->enum_ids[k] != source) - continue; - - s = sh_intc_source(desc, gr->enum_ids[k]); - if (s) - s->enable_max++; - } - } - } - -} - -void sh_intc_register_sources(struct intc_desc *desc, - struct intc_vect *vectors, - int nr_vectors, - struct intc_group *groups, - int nr_groups) -{ - unsigned int i, k; - struct intc_source *s; - - for (i = 0; i < nr_vectors; i++) { - struct intc_vect *vect = vectors + i; - - sh_intc_register_source(desc, vect->enum_id, groups, nr_groups); - s = sh_intc_source(desc, vect->enum_id); - if (s) { - s->vect = vect->vect; - -#ifdef DEBUG_INTC_SOURCES - printf("sh_intc: registered source %d -> 0x%04x (%d/%d)\n", - vect->enum_id, s->vect, s->enable_count, s->enable_max); -#endif - } - } - - if (groups) { - for (i = 0; i < nr_groups; i++) { - struct intc_group *gr = groups + i; - - s = sh_intc_source(desc, gr->enum_id); - s->next_enum_id = gr->enum_ids[0]; - - for (k = 1; k < ARRAY_SIZE(gr->enum_ids); k++) { - if (!gr->enum_ids[k]) - continue; - - s = sh_intc_source(desc, gr->enum_ids[k - 1]); - s->next_enum_id = gr->enum_ids[k]; - } - -#ifdef DEBUG_INTC_SOURCES - printf("sh_intc: registered group %d (%d/%d)\n", - gr->enum_id, s->enable_count, s->enable_max); -#endif - } - } -} - -int sh_intc_init(MemoryRegion *sysmem, - struct intc_desc *desc, - int nr_sources, - struct intc_mask_reg *mask_regs, - int nr_mask_regs, - struct intc_prio_reg *prio_regs, - int nr_prio_regs) -{ - unsigned int i, j; - - desc->pending = 0; - desc->nr_sources = nr_sources; - desc->mask_regs = mask_regs; - desc->nr_mask_regs = nr_mask_regs; - desc->prio_regs = prio_regs; - desc->nr_prio_regs = nr_prio_regs; - /* Allocate 4 MemoryRegions per register (2 actions * 2 aliases). - **/ - desc->iomem_aliases = g_new0(MemoryRegion, - (nr_mask_regs + nr_prio_regs) * 4); - - j = 0; - i = sizeof(struct intc_source) * nr_sources; - desc->sources = g_malloc0(i); - - for (i = 0; i < desc->nr_sources; i++) { - struct intc_source *source = desc->sources + i; - - source->parent = desc; - } - - desc->irqs = qemu_allocate_irqs(sh_intc_set_irq, desc, nr_sources); - - memory_region_init_io(&desc->iomem, NULL, &sh_intc_ops, desc, - "interrupt-controller", 0x100000000ULL); - -#define INT_REG_PARAMS(reg_struct, type, action, j) \ - reg_struct->action##_reg, #type, #action, j - if (desc->mask_regs) { - for (i = 0; i < desc->nr_mask_regs; i++) { - struct intc_mask_reg *mr = desc->mask_regs + i; - - j += sh_intc_register(sysmem, desc, - INT_REG_PARAMS(mr, mask, set, j)); - j += sh_intc_register(sysmem, desc, - INT_REG_PARAMS(mr, mask, clr, j)); - } - } - - if (desc->prio_regs) { - for (i = 0; i < desc->nr_prio_regs; i++) { - struct intc_prio_reg *pr = desc->prio_regs + i; - - j += sh_intc_register(sysmem, desc, - INT_REG_PARAMS(pr, prio, set, j)); - j += sh_intc_register(sysmem, desc, - INT_REG_PARAMS(pr, prio, clr, j)); - } - } -#undef INT_REG_PARAMS - - return 0; -} - -/* Assert level IRL interrupt. - 0:deassert. 1:lowest priority,... 15:highest priority. */ -void sh_intc_set_irl(void *opaque, int n, int level) -{ - struct intc_source *s = opaque; - int i, irl = level ^ 15; - for (i = 0; (s = sh_intc_source(s->parent, s->next_enum_id)); i++) { - if (i == irl) - sh_intc_toggle_source(s, s->enable_count?0:1, s->asserted?0:1); - else - if (s->asserted) - sh_intc_toggle_source(s, 0, -1); - } -} diff --git a/qemu/hw/intc/slavio_intctl.c b/qemu/hw/intc/slavio_intctl.c deleted file mode 100644 index c9486ed99..000000000 --- a/qemu/hw/intc/slavio_intctl.c +++ /dev/null @@ -1,472 +0,0 @@ -/* - * QEMU Sparc SLAVIO interrupt controller emulation - * - * Copyright (c) 2003-2005 Fabrice Bellard - * - * 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/sparc/sun4m.h" -#include "monitor/monitor.h" -#include "hw/sysbus.h" -#include "trace.h" - -//#define DEBUG_IRQ_COUNT - -/* - * Registers of interrupt controller in sun4m. - * - * This is the interrupt controller part of chip STP2001 (Slave I/O), also - * produced as NCR89C105. See - * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt - * - * There is a system master controller and one for each cpu. - * - */ - -#define MAX_CPUS 16 -#define MAX_PILS 16 - -struct SLAVIO_INTCTLState; - -typedef struct SLAVIO_CPUINTCTLState { - MemoryRegion iomem; - struct SLAVIO_INTCTLState *master; - uint32_t intreg_pending; - uint32_t cpu; - uint32_t irl_out; -} SLAVIO_CPUINTCTLState; - -#define TYPE_SLAVIO_INTCTL "slavio_intctl" -#define SLAVIO_INTCTL(obj) \ - OBJECT_CHECK(SLAVIO_INTCTLState, (obj), TYPE_SLAVIO_INTCTL) - -typedef struct SLAVIO_INTCTLState { - SysBusDevice parent_obj; - - MemoryRegion iomem; -#ifdef DEBUG_IRQ_COUNT - uint64_t irq_count[32]; -#endif - qemu_irq cpu_irqs[MAX_CPUS][MAX_PILS]; - SLAVIO_CPUINTCTLState slaves[MAX_CPUS]; - uint32_t intregm_pending; - uint32_t intregm_disabled; - uint32_t target_cpu; -} SLAVIO_INTCTLState; - -#define INTCTL_MAXADDR 0xf -#define INTCTL_SIZE (INTCTL_MAXADDR + 1) -#define INTCTLM_SIZE 0x14 -#define MASTER_IRQ_MASK ~0x0fa2007f -#define MASTER_DISABLE 0x80000000 -#define CPU_SOFTIRQ_MASK 0xfffe0000 -#define CPU_IRQ_INT15_IN (1 << 15) -#define CPU_IRQ_TIMER_IN (1 << 14) - -static void slavio_check_interrupts(SLAVIO_INTCTLState *s, int set_irqs); - -// per-cpu interrupt controller -static uint64_t slavio_intctl_mem_readl(void *opaque, hwaddr addr, - unsigned size) -{ - SLAVIO_CPUINTCTLState *s = opaque; - uint32_t saddr, ret; - - saddr = addr >> 2; - switch (saddr) { - case 0: - ret = s->intreg_pending; - break; - default: - ret = 0; - break; - } - trace_slavio_intctl_mem_readl(s->cpu, addr, ret); - - return ret; -} - -static void slavio_intctl_mem_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - SLAVIO_CPUINTCTLState *s = opaque; - uint32_t saddr; - - saddr = addr >> 2; - trace_slavio_intctl_mem_writel(s->cpu, addr, val); - switch (saddr) { - case 1: // clear pending softints - val &= CPU_SOFTIRQ_MASK | CPU_IRQ_INT15_IN; - s->intreg_pending &= ~val; - slavio_check_interrupts(s->master, 1); - trace_slavio_intctl_mem_writel_clear(s->cpu, val, s->intreg_pending); - break; - case 2: // set softint - val &= CPU_SOFTIRQ_MASK; - s->intreg_pending |= val; - slavio_check_interrupts(s->master, 1); - trace_slavio_intctl_mem_writel_set(s->cpu, val, s->intreg_pending); - break; - default: - break; - } -} - -static const MemoryRegionOps slavio_intctl_mem_ops = { - .read = slavio_intctl_mem_readl, - .write = slavio_intctl_mem_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -// master system interrupt controller -static uint64_t slavio_intctlm_mem_readl(void *opaque, hwaddr addr, - unsigned size) -{ - SLAVIO_INTCTLState *s = opaque; - uint32_t saddr, ret; - - saddr = addr >> 2; - switch (saddr) { - case 0: - ret = s->intregm_pending & ~MASTER_DISABLE; - break; - case 1: - ret = s->intregm_disabled & MASTER_IRQ_MASK; - break; - case 4: - ret = s->target_cpu; - break; - default: - ret = 0; - break; - } - trace_slavio_intctlm_mem_readl(addr, ret); - - return ret; -} - -static void slavio_intctlm_mem_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - SLAVIO_INTCTLState *s = opaque; - uint32_t saddr; - - saddr = addr >> 2; - trace_slavio_intctlm_mem_writel(addr, val); - switch (saddr) { - case 2: // clear (enable) - // Force clear unused bits - val &= MASTER_IRQ_MASK; - s->intregm_disabled &= ~val; - trace_slavio_intctlm_mem_writel_enable(val, s->intregm_disabled); - slavio_check_interrupts(s, 1); - break; - case 3: // set (disable; doesn't affect pending) - // Force clear unused bits - val &= MASTER_IRQ_MASK; - s->intregm_disabled |= val; - slavio_check_interrupts(s, 1); - trace_slavio_intctlm_mem_writel_disable(val, s->intregm_disabled); - break; - case 4: - s->target_cpu = val & (MAX_CPUS - 1); - slavio_check_interrupts(s, 1); - trace_slavio_intctlm_mem_writel_target(s->target_cpu); - break; - default: - break; - } -} - -static const MemoryRegionOps slavio_intctlm_mem_ops = { - .read = slavio_intctlm_mem_readl, - .write = slavio_intctlm_mem_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -void slavio_pic_info(Monitor *mon, DeviceState *dev) -{ - SLAVIO_INTCTLState *s = SLAVIO_INTCTL(dev); - int i; - - for (i = 0; i < MAX_CPUS; i++) { - monitor_printf(mon, "per-cpu %d: pending 0x%08x\n", i, - s->slaves[i].intreg_pending); - } - monitor_printf(mon, "master: pending 0x%08x, disabled 0x%08x\n", - s->intregm_pending, s->intregm_disabled); -} - -void slavio_irq_info(Monitor *mon, DeviceState *dev) -{ -#ifndef DEBUG_IRQ_COUNT - monitor_printf(mon, "irq statistic code not compiled.\n"); -#else - SLAVIO_INTCTLState *s = SLAVIO_INTCTL(dev); - int i; - int64_t count; - - s = SLAVIO_INTCTL(dev); - monitor_printf(mon, "IRQ statistics:\n"); - for (i = 0; i < 32; i++) { - count = s->irq_count[i]; - if (count > 0) - monitor_printf(mon, "%2d: %" PRId64 "\n", i, count); - } -#endif -} - -static const uint32_t intbit_to_level[] = { - 2, 3, 5, 7, 9, 11, 13, 2, 3, 5, 7, 9, 11, 13, 12, 12, - 6, 13, 4, 10, 8, 9, 11, 0, 0, 0, 0, 15, 15, 15, 15, 0, -}; - -static void slavio_check_interrupts(SLAVIO_INTCTLState *s, int set_irqs) -{ - uint32_t pending = s->intregm_pending, pil_pending; - unsigned int i, j; - - pending &= ~s->intregm_disabled; - - trace_slavio_check_interrupts(pending, s->intregm_disabled); - for (i = 0; i < MAX_CPUS; i++) { - pil_pending = 0; - - /* If we are the current interrupt target, get hard interrupts */ - if (pending && !(s->intregm_disabled & MASTER_DISABLE) && - (i == s->target_cpu)) { - for (j = 0; j < 32; j++) { - if ((pending & (1 << j)) && intbit_to_level[j]) { - pil_pending |= 1 << intbit_to_level[j]; - } - } - } - - /* Calculate current pending hard interrupts for display */ - s->slaves[i].intreg_pending &= CPU_SOFTIRQ_MASK | CPU_IRQ_INT15_IN | - CPU_IRQ_TIMER_IN; - if (i == s->target_cpu) { - for (j = 0; j < 32; j++) { - if ((s->intregm_pending & (1U << j)) && intbit_to_level[j]) { - s->slaves[i].intreg_pending |= 1 << intbit_to_level[j]; - } - } - } - - /* Level 15 and CPU timer interrupts are only masked when - the MASTER_DISABLE bit is set */ - if (!(s->intregm_disabled & MASTER_DISABLE)) { - pil_pending |= s->slaves[i].intreg_pending & - (CPU_IRQ_INT15_IN | CPU_IRQ_TIMER_IN); - } - - /* Add soft interrupts */ - pil_pending |= (s->slaves[i].intreg_pending & CPU_SOFTIRQ_MASK) >> 16; - - if (set_irqs) { - /* Since there is not really an interrupt 0 (and pil_pending - * and irl_out bit zero are thus always zero) there is no need - * to do anything with cpu_irqs[i][0] and it is OK not to do - * the j=0 iteration of this loop. - */ - for (j = MAX_PILS-1; j > 0; j--) { - if (pil_pending & (1 << j)) { - if (!(s->slaves[i].irl_out & (1 << j))) { - qemu_irq_raise(s->cpu_irqs[i][j]); - } - } else { - if (s->slaves[i].irl_out & (1 << j)) { - qemu_irq_lower(s->cpu_irqs[i][j]); - } - } - } - } - s->slaves[i].irl_out = pil_pending; - } -} - -/* - * "irq" here is the bit number in the system interrupt register to - * separate serial and keyboard interrupts sharing a level. - */ -static void slavio_set_irq(void *opaque, int irq, int level) -{ - SLAVIO_INTCTLState *s = opaque; - uint32_t mask = 1 << irq; - uint32_t pil = intbit_to_level[irq]; - unsigned int i; - - trace_slavio_set_irq(s->target_cpu, irq, pil, level); - if (pil > 0) { - if (level) { -#ifdef DEBUG_IRQ_COUNT - s->irq_count[pil]++; -#endif - s->intregm_pending |= mask; - if (pil == 15) { - for (i = 0; i < MAX_CPUS; i++) { - s->slaves[i].intreg_pending |= 1 << pil; - } - } - } else { - s->intregm_pending &= ~mask; - if (pil == 15) { - for (i = 0; i < MAX_CPUS; i++) { - s->slaves[i].intreg_pending &= ~(1 << pil); - } - } - } - slavio_check_interrupts(s, 1); - } -} - -static void slavio_set_timer_irq_cpu(void *opaque, int cpu, int level) -{ - SLAVIO_INTCTLState *s = opaque; - - trace_slavio_set_timer_irq_cpu(cpu, level); - - if (level) { - s->slaves[cpu].intreg_pending |= CPU_IRQ_TIMER_IN; - } else { - s->slaves[cpu].intreg_pending &= ~CPU_IRQ_TIMER_IN; - } - - slavio_check_interrupts(s, 1); -} - -static void slavio_set_irq_all(void *opaque, int irq, int level) -{ - if (irq < 32) { - slavio_set_irq(opaque, irq, level); - } else { - slavio_set_timer_irq_cpu(opaque, irq - 32, level); - } -} - -static int vmstate_intctl_post_load(void *opaque, int version_id) -{ - SLAVIO_INTCTLState *s = opaque; - - slavio_check_interrupts(s, 0); - return 0; -} - -static const VMStateDescription vmstate_intctl_cpu = { - .name ="slavio_intctl_cpu", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(intreg_pending, SLAVIO_CPUINTCTLState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_intctl = { - .name ="slavio_intctl", - .version_id = 1, - .minimum_version_id = 1, - .post_load = vmstate_intctl_post_load, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(slaves, SLAVIO_INTCTLState, MAX_CPUS, 1, - vmstate_intctl_cpu, SLAVIO_CPUINTCTLState), - VMSTATE_UINT32(intregm_pending, SLAVIO_INTCTLState), - VMSTATE_UINT32(intregm_disabled, SLAVIO_INTCTLState), - VMSTATE_UINT32(target_cpu, SLAVIO_INTCTLState), - VMSTATE_END_OF_LIST() - } -}; - -static void slavio_intctl_reset(DeviceState *d) -{ - SLAVIO_INTCTLState *s = SLAVIO_INTCTL(d); - int i; - - for (i = 0; i < MAX_CPUS; i++) { - s->slaves[i].intreg_pending = 0; - s->slaves[i].irl_out = 0; - } - s->intregm_disabled = ~MASTER_IRQ_MASK; - s->intregm_pending = 0; - s->target_cpu = 0; - slavio_check_interrupts(s, 0); -} - -static int slavio_intctl_init1(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - SLAVIO_INTCTLState *s = SLAVIO_INTCTL(dev); - unsigned int i, j; - char slave_name[45]; - - qdev_init_gpio_in(dev, slavio_set_irq_all, 32 + MAX_CPUS); - memory_region_init_io(&s->iomem, OBJECT(s), &slavio_intctlm_mem_ops, s, - "master-interrupt-controller", INTCTLM_SIZE); - sysbus_init_mmio(sbd, &s->iomem); - - for (i = 0; i < MAX_CPUS; i++) { - snprintf(slave_name, sizeof(slave_name), - "slave-interrupt-controller-%i", i); - for (j = 0; j < MAX_PILS; j++) { - sysbus_init_irq(sbd, &s->cpu_irqs[i][j]); - } - memory_region_init_io(&s->slaves[i].iomem, OBJECT(s), - &slavio_intctl_mem_ops, - &s->slaves[i], slave_name, INTCTL_SIZE); - sysbus_init_mmio(sbd, &s->slaves[i].iomem); - s->slaves[i].cpu = i; - s->slaves[i].master = s; - } - - return 0; -} - -static void slavio_intctl_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = slavio_intctl_init1; - dc->reset = slavio_intctl_reset; - dc->vmsd = &vmstate_intctl; -} - -static const TypeInfo slavio_intctl_info = { - .name = TYPE_SLAVIO_INTCTL, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SLAVIO_INTCTLState), - .class_init = slavio_intctl_class_init, -}; - -static void slavio_intctl_register_types(void) -{ - type_register_static(&slavio_intctl_info); -} - -type_init(slavio_intctl_register_types) diff --git a/qemu/hw/intc/vgic_common.h b/qemu/hw/intc/vgic_common.h deleted file mode 100644 index 80d919eb9..000000000 --- a/qemu/hw/intc/vgic_common.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 . - */ - -#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 deleted file mode 100644 index 8659be017..000000000 --- a/qemu/hw/intc/xics.c +++ /dev/null @@ -1,1093 +0,0 @@ -/* - * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator - * - * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics - * - * Copyright (c) 2010,2011 David Gibson, IBM Corporation. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/hw.h" -#include "trace.h" -#include "qemu/timer.h" -#include "hw/ppc/spapr.h" -#include "hw/ppc/xics.h" -#include "qemu/error-report.h" -#include "qapi/visitor.h" - -static int get_cpu_index_by_dt_id(int cpu_dt_id) -{ - PowerPCCPU *cpu = ppc_get_vcpu_by_dt_id(cpu_dt_id); - - if (cpu) { - return cpu->parent_obj.cpu_index; - } - - return -1; -} - -void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu) -{ - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - ICPState *ss = &icp->ss[cs->cpu_index]; - XICSStateClass *info = XICS_COMMON_GET_CLASS(icp); - - assert(cs->cpu_index < icp->nr_servers); - - if (info->cpu_setup) { - info->cpu_setup(icp, cpu); - } - - switch (PPC_INPUT(env)) { - case PPC_FLAGS_INPUT_POWER7: - ss->output = env->irq_inputs[POWER7_INPUT_INT]; - break; - - case PPC_FLAGS_INPUT_970: - ss->output = env->irq_inputs[PPC970_INPUT_INT]; - break; - - default: - error_report("XICS interrupt controller does not support this CPU " - "bus model"); - abort(); - } -} - -/* - * XICS Common class - parent for emulated XICS and KVM-XICS - */ -static void xics_common_reset(DeviceState *d) -{ - XICSState *icp = XICS_COMMON(d); - int i; - - for (i = 0; i < icp->nr_servers; i++) { - device_reset(DEVICE(&icp->ss[i])); - } - - device_reset(DEVICE(icp->ics)); -} - -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, name, &value, 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, name, &value, &error); - if (error) { - error_propagate(errp, error); - return; - } - if (icp->nr_irqs) { - error_setg(errp, "Number of interrupts is already set to %u", - icp->nr_irqs); - return; - } - - assert(info->set_nr_irqs); - assert(icp->ics); - info->set_nr_irqs(icp, value, errp); -} - -static void xics_prop_get_nr_servers(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - XICSState *icp = XICS_COMMON(obj); - int64_t value = icp->nr_servers; - - visit_type_int(v, name, &value, errp); -} - -static void xics_prop_set_nr_servers(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, name, &value, &error); - if (error) { - error_propagate(errp, error); - return; - } - if (icp->nr_servers) { - error_setg(errp, "Number of servers is already set to %u", - icp->nr_servers); - return; - } - - assert(info->set_nr_servers); - info->set_nr_servers(icp, value, errp); -} - -static void xics_common_initfn(Object *obj) -{ - object_property_add(obj, "nr_irqs", "int", - xics_prop_get_nr_irqs, xics_prop_set_nr_irqs, - NULL, NULL, NULL); - object_property_add(obj, "nr_servers", "int", - xics_prop_get_nr_servers, xics_prop_set_nr_servers, - NULL, NULL, NULL); -} - -static void xics_common_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->reset = xics_common_reset; -} - -static const TypeInfo xics_common_info = { - .name = TYPE_XICS_COMMON, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(XICSState), - .class_size = sizeof(XICSStateClass), - .instance_init = xics_common_initfn, - .class_init = xics_common_class_init, -}; - -/* - * ICP: Presentation layer - */ - -#define XISR_MASK 0x00ffffff -#define CPPR_MASK 0xff000000 - -#define XISR(ss) (((ss)->xirr) & XISR_MASK) -#define CPPR(ss) (((ss)->xirr) >> 24) - -static void ics_reject(ICSState *ics, int nr); -static void ics_resend(ICSState *ics); -static void ics_eoi(ICSState *ics, int nr); - -static void icp_check_ipi(XICSState *icp, int server) -{ - ICPState *ss = icp->ss + server; - - if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) { - return; - } - - trace_xics_icp_check_ipi(server, ss->mfrr); - - if (XISR(ss)) { - ics_reject(icp->ics, XISR(ss)); - } - - ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI; - ss->pending_priority = ss->mfrr; - qemu_irq_raise(ss->output); -} - -static void icp_resend(XICSState *icp, int server) -{ - ICPState *ss = icp->ss + server; - - if (ss->mfrr < CPPR(ss)) { - icp_check_ipi(icp, server); - } - ics_resend(icp->ics); -} - -static void icp_set_cppr(XICSState *icp, int server, uint8_t cppr) -{ - ICPState *ss = icp->ss + server; - uint8_t old_cppr; - uint32_t old_xisr; - - old_cppr = CPPR(ss); - ss->xirr = (ss->xirr & ~CPPR_MASK) | (cppr << 24); - - if (cppr < old_cppr) { - if (XISR(ss) && (cppr <= ss->pending_priority)) { - old_xisr = XISR(ss); - ss->xirr &= ~XISR_MASK; /* Clear XISR */ - ss->pending_priority = 0xff; - qemu_irq_lower(ss->output); - ics_reject(icp->ics, old_xisr); - } - } else { - if (!XISR(ss)) { - icp_resend(icp, server); - } - } -} - -static void icp_set_mfrr(XICSState *icp, int server, uint8_t mfrr) -{ - ICPState *ss = icp->ss + server; - - ss->mfrr = mfrr; - if (mfrr < CPPR(ss)) { - icp_check_ipi(icp, server); - } -} - -static uint32_t icp_accept(ICPState *ss) -{ - uint32_t xirr = ss->xirr; - - qemu_irq_lower(ss->output); - ss->xirr = ss->pending_priority << 24; - ss->pending_priority = 0xff; - - trace_xics_icp_accept(xirr, ss->xirr); - - return xirr; -} - -static void icp_eoi(XICSState *icp, int server, uint32_t xirr) -{ - ICPState *ss = icp->ss + server; - - /* Send EOI -> ICS */ - ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK); - trace_xics_icp_eoi(server, xirr, ss->xirr); - ics_eoi(icp->ics, xirr & XISR_MASK); - if (!XISR(ss)) { - icp_resend(icp, server); - } -} - -static void icp_irq(XICSState *icp, int server, int nr, uint8_t priority) -{ - ICPState *ss = icp->ss + server; - - trace_xics_icp_irq(server, nr, priority); - - if ((priority >= CPPR(ss)) - || (XISR(ss) && (ss->pending_priority <= priority))) { - ics_reject(icp->ics, nr); - } else { - if (XISR(ss)) { - ics_reject(icp->ics, XISR(ss)); - } - ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK); - ss->pending_priority = priority; - trace_xics_icp_raise(ss->xirr, ss->pending_priority); - qemu_irq_raise(ss->output); - } -} - -static void icp_dispatch_pre_save(void *opaque) -{ - ICPState *ss = opaque; - ICPStateClass *info = ICP_GET_CLASS(ss); - - if (info->pre_save) { - info->pre_save(ss); - } -} - -static int icp_dispatch_post_load(void *opaque, int version_id) -{ - ICPState *ss = opaque; - ICPStateClass *info = ICP_GET_CLASS(ss); - - if (info->post_load) { - return info->post_load(ss, version_id); - } - - return 0; -} - -static const VMStateDescription vmstate_icp_server = { - .name = "icp/server", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = icp_dispatch_pre_save, - .post_load = icp_dispatch_post_load, - .fields = (VMStateField[]) { - /* Sanity check */ - VMSTATE_UINT32(xirr, ICPState), - VMSTATE_UINT8(pending_priority, ICPState), - VMSTATE_UINT8(mfrr, ICPState), - VMSTATE_END_OF_LIST() - }, -}; - -static void icp_reset(DeviceState *dev) -{ - ICPState *icp = ICP(dev); - - icp->xirr = 0; - icp->pending_priority = 0xff; - icp->mfrr = 0xff; - - /* Make all outputs are deasserted */ - qemu_set_irq(icp->output, 0); -} - -static void icp_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = icp_reset; - dc->vmsd = &vmstate_icp_server; -} - -static const TypeInfo icp_info = { - .name = TYPE_ICP, - .parent = TYPE_DEVICE, - .instance_size = sizeof(ICPState), - .class_init = icp_class_init, - .class_size = sizeof(ICPStateClass), -}; - -/* - * ICS: Source layer - */ -static int ics_valid_irq(ICSState *ics, uint32_t nr) -{ - return (nr >= ics->offset) - && (nr < (ics->offset + ics->nr_irqs)); -} - -static void resend_msi(ICSState *ics, int srcno) -{ - ICSIRQState *irq = ics->irqs + srcno; - - /* FIXME: filter by server#? */ - if (irq->status & XICS_STATUS_REJECTED) { - irq->status &= ~XICS_STATUS_REJECTED; - if (irq->priority != 0xff) { - icp_irq(ics->icp, irq->server, srcno + ics->offset, - irq->priority); - } - } -} - -static void resend_lsi(ICSState *ics, int srcno) -{ - ICSIRQState *irq = ics->irqs + srcno; - - if ((irq->priority != 0xff) - && (irq->status & XICS_STATUS_ASSERTED) - && !(irq->status & XICS_STATUS_SENT)) { - irq->status |= XICS_STATUS_SENT; - icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); - } -} - -static void set_irq_msi(ICSState *ics, int srcno, int val) -{ - ICSIRQState *irq = ics->irqs + srcno; - - trace_xics_set_irq_msi(srcno, srcno + ics->offset); - - if (val) { - if (irq->priority == 0xff) { - irq->status |= XICS_STATUS_MASKED_PENDING; - trace_xics_masked_pending(); - } else { - icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); - } - } -} - -static void set_irq_lsi(ICSState *ics, int srcno, int val) -{ - ICSIRQState *irq = ics->irqs + srcno; - - trace_xics_set_irq_lsi(srcno, srcno + ics->offset); - if (val) { - irq->status |= XICS_STATUS_ASSERTED; - } else { - irq->status &= ~XICS_STATUS_ASSERTED; - } - resend_lsi(ics, srcno); -} - -static void ics_set_irq(void *opaque, int srcno, int val) -{ - ICSState *ics = (ICSState *)opaque; - - if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_LSI) { - set_irq_lsi(ics, srcno, val); - } else { - set_irq_msi(ics, srcno, val); - } -} - -static void write_xive_msi(ICSState *ics, int srcno) -{ - ICSIRQState *irq = ics->irqs + srcno; - - if (!(irq->status & XICS_STATUS_MASKED_PENDING) - || (irq->priority == 0xff)) { - return; - } - - irq->status &= ~XICS_STATUS_MASKED_PENDING; - icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); -} - -static void write_xive_lsi(ICSState *ics, int srcno) -{ - resend_lsi(ics, srcno); -} - -static void ics_write_xive(ICSState *ics, int nr, int server, - uint8_t priority, uint8_t saved_priority) -{ - int srcno = nr - ics->offset; - ICSIRQState *irq = ics->irqs + srcno; - - irq->server = server; - irq->priority = priority; - irq->saved_priority = saved_priority; - - trace_xics_ics_write_xive(nr, srcno, server, priority); - - if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_LSI) { - write_xive_lsi(ics, srcno); - } else { - write_xive_msi(ics, srcno); - } -} - -static void ics_reject(ICSState *ics, int nr) -{ - ICSIRQState *irq = ics->irqs + nr - ics->offset; - - trace_xics_ics_reject(nr, nr - ics->offset); - irq->status |= XICS_STATUS_REJECTED; /* Irrelevant but harmless for LSI */ - irq->status &= ~XICS_STATUS_SENT; /* Irrelevant but harmless for MSI */ -} - -static void ics_resend(ICSState *ics) -{ - int i; - - for (i = 0; i < ics->nr_irqs; i++) { - /* FIXME: filter by server#? */ - if (ics->irqs[i].flags & XICS_FLAGS_IRQ_LSI) { - resend_lsi(ics, i); - } else { - resend_msi(ics, i); - } - } -} - -static void ics_eoi(ICSState *ics, int nr) -{ - int srcno = nr - ics->offset; - ICSIRQState *irq = ics->irqs + srcno; - - trace_xics_ics_eoi(nr); - - if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_LSI) { - irq->status &= ~XICS_STATUS_SENT; - } -} - -static void ics_reset(DeviceState *dev) -{ - ICSState *ics = ICS(dev); - int i; - uint8_t flags[ics->nr_irqs]; - - for (i = 0; i < ics->nr_irqs; i++) { - flags[i] = ics->irqs[i].flags; - } - - memset(ics->irqs, 0, sizeof(ICSIRQState) * ics->nr_irqs); - - for (i = 0; i < ics->nr_irqs; i++) { - ics->irqs[i].priority = 0xff; - ics->irqs[i].saved_priority = 0xff; - ics->irqs[i].flags = flags[i]; - } -} - -static int ics_post_load(ICSState *ics, int version_id) -{ - int i; - - for (i = 0; i < ics->icp->nr_servers; i++) { - icp_resend(ics->icp, i); - } - - return 0; -} - -static void ics_dispatch_pre_save(void *opaque) -{ - ICSState *ics = opaque; - ICSStateClass *info = ICS_GET_CLASS(ics); - - if (info->pre_save) { - info->pre_save(ics); - } -} - -static int ics_dispatch_post_load(void *opaque, int version_id) -{ - ICSState *ics = opaque; - ICSStateClass *info = ICS_GET_CLASS(ics); - - if (info->post_load) { - return info->post_load(ics, version_id); - } - - return 0; -} - -static const VMStateDescription vmstate_ics_irq = { - .name = "ics/irq", - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(server, ICSIRQState), - VMSTATE_UINT8(priority, ICSIRQState), - VMSTATE_UINT8(saved_priority, ICSIRQState), - VMSTATE_UINT8(status, ICSIRQState), - VMSTATE_UINT8(flags, ICSIRQState), - VMSTATE_END_OF_LIST() - }, -}; - -static const VMStateDescription vmstate_ics = { - .name = "ics", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = ics_dispatch_pre_save, - .post_load = ics_dispatch_post_load, - .fields = (VMStateField[]) { - /* Sanity check */ - VMSTATE_UINT32_EQUAL(nr_irqs, ICSState), - - VMSTATE_STRUCT_VARRAY_POINTER_UINT32(irqs, ICSState, nr_irqs, - vmstate_ics_irq, ICSIRQState), - VMSTATE_END_OF_LIST() - }, -}; - -static void ics_initfn(Object *obj) -{ - ICSState *ics = ICS(obj); - - ics->offset = XICS_IRQ_BASE; -} - -static void ics_realize(DeviceState *dev, Error **errp) -{ - ICSState *ics = ICS(dev); - - if (!ics->nr_irqs) { - error_setg(errp, "Number of interrupts needs to be greater 0"); - return; - } - ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); - ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, ics->nr_irqs); -} - -static void ics_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - ICSStateClass *isc = ICS_CLASS(klass); - - dc->realize = ics_realize; - dc->vmsd = &vmstate_ics; - dc->reset = ics_reset; - isc->post_load = ics_post_load; -} - -static const TypeInfo ics_info = { - .name = TYPE_ICS, - .parent = TYPE_DEVICE, - .instance_size = sizeof(ICSState), - .class_init = ics_class_init, - .class_size = sizeof(ICSStateClass), - .instance_init = ics_initfn, -}; - -/* - * Exported functions - */ -static int xics_find_source(XICSState *icp, int irq) -{ - int sources = 1; - int src; - - /* FIXME: implement multiple sources */ - for (src = 0; src < sources; ++src) { - ICSState *ics = &icp->ics[src]; - if (ics_valid_irq(ics, irq)) { - return src; - } - } - - return -1; -} - -qemu_irq xics_get_qirq(XICSState *icp, int irq) -{ - int src = xics_find_source(icp, irq); - - if (src >= 0) { - ICSState *ics = &icp->ics[src]; - return ics->qirqs[irq - ics->offset]; - } - - return NULL; -} - -static void ics_set_irq_type(ICSState *ics, int srcno, bool lsi) -{ - assert(!(ics->irqs[srcno].flags & XICS_FLAGS_IRQ_MASK)); - - ics->irqs[srcno].flags |= - lsi ? XICS_FLAGS_IRQ_LSI : XICS_FLAGS_IRQ_MSI; -} - -void xics_set_irq_type(XICSState *icp, int irq, bool lsi) -{ - int src = xics_find_source(icp, irq); - ICSState *ics; - - assert(src >= 0); - - ics = &icp->ics[src]; - ics_set_irq_type(ics, irq - ics->offset, lsi); -} - -#define ICS_IRQ_FREE(ics, srcno) \ - (!((ics)->irqs[(srcno)].flags & (XICS_FLAGS_IRQ_MASK))) - -static int ics_find_free_block(ICSState *ics, int num, int alignnum) -{ - int first, i; - - for (first = 0; first < ics->nr_irqs; first += alignnum) { - if (num > (ics->nr_irqs - first)) { - return -1; - } - for (i = first; i < first + num; ++i) { - if (!ICS_IRQ_FREE(ics, i)) { - break; - } - } - if (i == (first + num)) { - return first; - } - } - - return -1; -} - -int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi, Error **errp) -{ - ICSState *ics = &icp->ics[src]; - int irq; - - if (irq_hint) { - assert(src == xics_find_source(icp, irq_hint)); - if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) { - 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) { - error_setg(errp, "can't allocate IRQ: no IRQ left"); - return -1; - } - irq += ics->offset; - } - - ics_set_irq_type(ics, irq - ics->offset, lsi); - trace_xics_alloc(src, irq); - - return irq; -} - -/* - * 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, - Error **errp) -{ - int i, first = -1; - ICSState *ics = &icp->ics[src]; - - assert(src == 0); - /* - * MSIMesage::data is used for storing VIRQ so - * it has to be aligned to num to support multiple - * MSI vectors. MSI-X is not affected by this. - * The hint is used for the first IRQ, the rest should - * be allocated continuously. - */ - if (align) { - assert((num == 1) || (num == 2) || (num == 4) || - (num == 8) || (num == 16) || (num == 32)); - first = ics_find_free_block(ics, num, num); - } 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) { - ics_set_irq_type(ics, i, lsi); - } - } - first += ics->offset; - - trace_xics_alloc_block(src, first, num, lsi, align); - - return first; -} - -static void ics_free(ICSState *ics, int srcno, int num) -{ - int i; - - for (i = srcno; i < srcno + num; ++i) { - if (ICS_IRQ_FREE(ics, i)) { - trace_xics_ics_free_warn(ics - ics->icp->ics, i + ics->offset); - } - memset(&ics->irqs[i], 0, sizeof(ICSIRQState)); - } -} - -void xics_free(XICSState *icp, int irq, int num) -{ - int src = xics_find_source(icp, irq); - - if (src >= 0) { - ICSState *ics = &icp->ics[src]; - - /* FIXME: implement multiple sources */ - assert(src == 0); - - trace_xics_ics_free(ics - icp->ics, irq, num); - ics_free(ics, irq - ics->offset, num); - } -} - -/* - * Guest interfaces - */ - -static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUState *cs = CPU(cpu); - target_ulong cppr = args[0]; - - icp_set_cppr(spapr->icp, cs->cpu_index, cppr); - return H_SUCCESS; -} - -static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong server = get_cpu_index_by_dt_id(args[0]); - target_ulong mfrr = args[1]; - - if (server >= spapr->icp->nr_servers) { - return H_PARAMETER; - } - - icp_set_mfrr(spapr->icp, server, mfrr); - return H_SUCCESS; -} - -static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUState *cs = CPU(cpu); - uint32_t xirr = icp_accept(spapr->icp->ss + cs->cpu_index); - - args[0] = xirr; - return H_SUCCESS; -} - -static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUState *cs = CPU(cpu); - ICPState *ss = &spapr->icp->ss[cs->cpu_index]; - uint32_t xirr = icp_accept(ss); - - args[0] = xirr; - args[1] = cpu_get_host_ticks(); - return H_SUCCESS; -} - -static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUState *cs = CPU(cpu); - target_ulong xirr = args[0]; - - icp_eoi(spapr->icp, cs->cpu_index, xirr); - return H_SUCCESS; -} - -static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUState *cs = CPU(cpu); - ICPState *ss = &spapr->icp->ss[cs->cpu_index]; - - args[0] = ss->xirr; - args[1] = ss->mfrr; - - return H_SUCCESS; -} - -static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, - uint32_t nargs, target_ulong args, - uint32_t nret, target_ulong rets) -{ - ICSState *ics = spapr->icp->ics; - uint32_t nr, server, priority; - - if ((nargs != 3) || (nret != 1)) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - nr = rtas_ld(args, 0); - server = get_cpu_index_by_dt_id(rtas_ld(args, 1)); - priority = rtas_ld(args, 2); - - if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers) - || (priority > 0xff)) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - ics_write_xive(ics, nr, server, priority, priority); - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); -} - -static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, - uint32_t nargs, target_ulong args, - uint32_t nret, target_ulong rets) -{ - ICSState *ics = spapr->icp->ics; - uint32_t nr; - - if ((nargs != 1) || (nret != 3)) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - nr = rtas_ld(args, 0); - - if (!ics_valid_irq(ics, nr)) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - rtas_st(rets, 1, ics->irqs[nr - ics->offset].server); - rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority); -} - -static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, - uint32_t nargs, target_ulong args, - uint32_t nret, target_ulong rets) -{ - ICSState *ics = spapr->icp->ics; - uint32_t nr; - - if ((nargs != 1) || (nret != 1)) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - nr = rtas_ld(args, 0); - - if (!ics_valid_irq(ics, nr)) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff, - ics->irqs[nr - ics->offset].priority); - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); -} - -static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, - uint32_t nargs, target_ulong args, - uint32_t nret, target_ulong rets) -{ - ICSState *ics = spapr->icp->ics; - uint32_t nr; - - if ((nargs != 1) || (nret != 1)) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - nr = rtas_ld(args, 0); - - if (!ics_valid_irq(ics, nr)) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, - ics->irqs[nr - ics->offset].saved_priority, - ics->irqs[nr - ics->offset].saved_priority); - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); -} - -/* - * XICS - */ - -static void xics_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp) -{ - icp->nr_irqs = icp->ics->nr_irqs = nr_irqs; -} - -static void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers, - Error **errp) -{ - int i; - - icp->nr_servers = nr_servers; - - icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState)); - for (i = 0; i < icp->nr_servers; i++) { - char buffer[32]; - object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP); - snprintf(buffer, sizeof(buffer), "icp[%d]", i); - object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]), - errp); - } -} - -static void xics_realize(DeviceState *dev, Error **errp) -{ - XICSState *icp = XICS(dev); - Error *error = NULL; - int i; - - if (!icp->nr_servers) { - error_setg(errp, "Number of servers needs to be greater 0"); - return; - } - - /* Registration of global state belongs into realize */ - spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive); - spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_get_xive); - spapr_rtas_register(RTAS_IBM_INT_OFF, "ibm,int-off", rtas_int_off); - spapr_rtas_register(RTAS_IBM_INT_ON, "ibm,int-on", rtas_int_on); - - spapr_register_hypercall(H_CPPR, h_cppr); - spapr_register_hypercall(H_IPI, h_ipi); - spapr_register_hypercall(H_XIRR, h_xirr); - spapr_register_hypercall(H_XIRR_X, h_xirr_x); - spapr_register_hypercall(H_EOI, h_eoi); - spapr_register_hypercall(H_IPOLL, h_ipoll); - - object_property_set_bool(OBJECT(icp->ics), true, "realized", &error); - if (error) { - error_propagate(errp, error); - return; - } - - for (i = 0; i < icp->nr_servers; i++) { - object_property_set_bool(OBJECT(&icp->ss[i]), true, "realized", &error); - if (error) { - error_propagate(errp, error); - return; - } - } -} - -static void xics_initfn(Object *obj) -{ - XICSState *xics = XICS(obj); - - xics->ics = ICS(object_new(TYPE_ICS)); - object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL); - xics->ics->icp = xics; -} - -static void xics_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - XICSStateClass *xsc = XICS_CLASS(oc); - - dc->realize = xics_realize; - xsc->set_nr_irqs = xics_set_nr_irqs; - xsc->set_nr_servers = xics_set_nr_servers; -} - -static const TypeInfo xics_info = { - .name = TYPE_XICS, - .parent = TYPE_XICS_COMMON, - .instance_size = sizeof(XICSState), - .class_size = sizeof(XICSStateClass), - .class_init = xics_class_init, - .instance_init = xics_initfn, -}; - -static void xics_register_types(void) -{ - type_register_static(&xics_common_info); - type_register_static(&xics_info); - type_register_static(&ics_info); - type_register_static(&icp_info); -} - -type_init(xics_register_types) diff --git a/qemu/hw/intc/xics_kvm.c b/qemu/hw/intc/xics_kvm.c deleted file mode 100644 index 9029d9ee0..000000000 --- a/qemu/hw/intc/xics_kvm.c +++ /dev/null @@ -1,512 +0,0 @@ -/* - * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator - * - * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics, in-kernel emulation - * - * Copyright (c) 2013 David Gibson, IBM Corporation. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/hw.h" -#include "trace.h" -#include "hw/ppc/spapr.h" -#include "hw/ppc/xics.h" -#include "kvm_ppc.h" -#include "qemu/config-file.h" -#include "qemu/error-report.h" - -#include - -typedef struct KVMXICSState { - XICSState parent_obj; - - int kernel_xics_fd; -} KVMXICSState; - -/* - * ICP-KVM - */ -static void icp_get_kvm_state(ICPState *ss) -{ - uint64_t state; - struct kvm_one_reg reg = { - .id = KVM_REG_PPC_ICP_STATE, - .addr = (uintptr_t)&state, - }; - int ret; - - /* ICP for this CPU thread is not in use, exiting */ - if (!ss->cs) { - return; - } - - ret = kvm_vcpu_ioctl(ss->cs, KVM_GET_ONE_REG, ®); - if (ret != 0) { - error_report("Unable to retrieve KVM interrupt controller state" - " for CPU %ld: %s", kvm_arch_vcpu_id(ss->cs), strerror(errno)); - exit(1); - } - - ss->xirr = state >> KVM_REG_PPC_ICP_XISR_SHIFT; - ss->mfrr = (state >> KVM_REG_PPC_ICP_MFRR_SHIFT) - & KVM_REG_PPC_ICP_MFRR_MASK; - ss->pending_priority = (state >> KVM_REG_PPC_ICP_PPRI_SHIFT) - & KVM_REG_PPC_ICP_PPRI_MASK; -} - -static int icp_set_kvm_state(ICPState *ss, int version_id) -{ - uint64_t state; - struct kvm_one_reg reg = { - .id = KVM_REG_PPC_ICP_STATE, - .addr = (uintptr_t)&state, - }; - int ret; - - /* ICP for this CPU thread is not in use, exiting */ - if (!ss->cs) { - return 0; - } - - state = ((uint64_t)ss->xirr << KVM_REG_PPC_ICP_XISR_SHIFT) - | ((uint64_t)ss->mfrr << KVM_REG_PPC_ICP_MFRR_SHIFT) - | ((uint64_t)ss->pending_priority << KVM_REG_PPC_ICP_PPRI_SHIFT); - - ret = kvm_vcpu_ioctl(ss->cs, KVM_SET_ONE_REG, ®); - if (ret != 0) { - error_report("Unable to restore KVM interrupt controller state (0x%" - PRIx64 ") for CPU %ld: %s", state, kvm_arch_vcpu_id(ss->cs), - strerror(errno)); - return ret; - } - - return 0; -} - -static void icp_kvm_reset(DeviceState *dev) -{ - ICPState *icp = ICP(dev); - - icp->xirr = 0; - icp->pending_priority = 0xff; - icp->mfrr = 0xff; - - /* Make all outputs are deasserted */ - qemu_set_irq(icp->output, 0); - - icp_set_kvm_state(icp, 1); -} - -static void icp_kvm_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - ICPStateClass *icpc = ICP_CLASS(klass); - - dc->reset = icp_kvm_reset; - icpc->pre_save = icp_get_kvm_state; - icpc->post_load = icp_set_kvm_state; -} - -static const TypeInfo icp_kvm_info = { - .name = TYPE_KVM_ICP, - .parent = TYPE_ICP, - .instance_size = sizeof(ICPState), - .class_init = icp_kvm_class_init, - .class_size = sizeof(ICPStateClass), -}; - -/* - * ICS-KVM - */ -static void ics_get_kvm_state(ICSState *ics) -{ - KVMXICSState *icpkvm = KVM_XICS(ics->icp); - uint64_t state; - struct kvm_device_attr attr = { - .flags = 0, - .group = KVM_DEV_XICS_GRP_SOURCES, - .addr = (uint64_t)(uintptr_t)&state, - }; - int i; - - for (i = 0; i < ics->nr_irqs; i++) { - ICSIRQState *irq = &ics->irqs[i]; - int ret; - - attr.attr = i + ics->offset; - - ret = ioctl(icpkvm->kernel_xics_fd, KVM_GET_DEVICE_ATTR, &attr); - if (ret != 0) { - error_report("Unable to retrieve KVM interrupt controller state" - " for IRQ %d: %s", i + ics->offset, strerror(errno)); - exit(1); - } - - irq->server = state & KVM_XICS_DESTINATION_MASK; - irq->saved_priority = (state >> KVM_XICS_PRIORITY_SHIFT) - & KVM_XICS_PRIORITY_MASK; - /* - * To be consistent with the software emulation in xics.c, we - * split out the masked state + priority that we get from the - * kernel into 'current priority' (0xff if masked) and - * 'saved priority' (if masked, this is the priority the - * interrupt had before it was masked). Masking and unmasking - * are done with the ibm,int-off and ibm,int-on RTAS calls. - */ - if (state & KVM_XICS_MASKED) { - irq->priority = 0xff; - } else { - irq->priority = irq->saved_priority; - } - - if (state & KVM_XICS_PENDING) { - if (state & KVM_XICS_LEVEL_SENSITIVE) { - irq->status |= XICS_STATUS_ASSERTED; - } else { - /* - * A pending edge-triggered interrupt (or MSI) - * must have been rejected previously when we - * first detected it and tried to deliver it, - * so mark it as pending and previously rejected - * for consistency with how xics.c works. - */ - irq->status |= XICS_STATUS_MASKED_PENDING - | XICS_STATUS_REJECTED; - } - } - } -} - -static int ics_set_kvm_state(ICSState *ics, int version_id) -{ - KVMXICSState *icpkvm = KVM_XICS(ics->icp); - uint64_t state; - struct kvm_device_attr attr = { - .flags = 0, - .group = KVM_DEV_XICS_GRP_SOURCES, - .addr = (uint64_t)(uintptr_t)&state, - }; - int i; - - for (i = 0; i < ics->nr_irqs; i++) { - ICSIRQState *irq = &ics->irqs[i]; - int ret; - - attr.attr = i + ics->offset; - - state = irq->server; - state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK) - << KVM_XICS_PRIORITY_SHIFT; - if (irq->priority != irq->saved_priority) { - assert(irq->priority == 0xff); - state |= KVM_XICS_MASKED; - } - - if (ics->irqs[i].flags & XICS_FLAGS_IRQ_LSI) { - state |= KVM_XICS_LEVEL_SENSITIVE; - if (irq->status & XICS_STATUS_ASSERTED) { - state |= KVM_XICS_PENDING; - } - } else { - if (irq->status & XICS_STATUS_MASKED_PENDING) { - state |= KVM_XICS_PENDING; - } - } - - ret = ioctl(icpkvm->kernel_xics_fd, KVM_SET_DEVICE_ATTR, &attr); - if (ret != 0) { - error_report("Unable to restore KVM interrupt controller state" - " for IRQs %d: %s", i + ics->offset, strerror(errno)); - return ret; - } - } - - return 0; -} - -static void ics_kvm_set_irq(void *opaque, int srcno, int val) -{ - ICSState *ics = opaque; - struct kvm_irq_level args; - int rc; - - args.irq = srcno + ics->offset; - if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_MSI) { - if (!val) { - return; - } - args.level = KVM_INTERRUPT_SET; - } else { - args.level = val ? KVM_INTERRUPT_SET_LEVEL : KVM_INTERRUPT_UNSET; - } - rc = kvm_vm_ioctl(kvm_state, KVM_IRQ_LINE, &args); - if (rc < 0) { - perror("kvm_irq_line"); - } -} - -static void ics_kvm_reset(DeviceState *dev) -{ - ICSState *ics = ICS(dev); - int i; - uint8_t flags[ics->nr_irqs]; - - for (i = 0; i < ics->nr_irqs; i++) { - flags[i] = ics->irqs[i].flags; - } - - memset(ics->irqs, 0, sizeof(ICSIRQState) * ics->nr_irqs); - - for (i = 0; i < ics->nr_irqs; i++) { - ics->irqs[i].priority = 0xff; - ics->irqs[i].saved_priority = 0xff; - ics->irqs[i].flags = flags[i]; - } - - ics_set_kvm_state(ics, 1); -} - -static void ics_kvm_realize(DeviceState *dev, Error **errp) -{ - ICSState *ics = ICS(dev); - - if (!ics->nr_irqs) { - error_setg(errp, "Number of interrupts needs to be greater 0"); - return; - } - ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); - ics->qirqs = qemu_allocate_irqs(ics_kvm_set_irq, ics, ics->nr_irqs); -} - -static void ics_kvm_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - ICSStateClass *icsc = ICS_CLASS(klass); - - dc->realize = ics_kvm_realize; - dc->reset = ics_kvm_reset; - icsc->pre_save = ics_get_kvm_state; - icsc->post_load = ics_set_kvm_state; -} - -static const TypeInfo ics_kvm_info = { - .name = TYPE_KVM_ICS, - .parent = TYPE_ICS, - .instance_size = sizeof(ICSState), - .class_init = ics_kvm_class_init, -}; - -/* - * XICS-KVM - */ -static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu) -{ - CPUState *cs; - ICPState *ss; - KVMXICSState *icpkvm = KVM_XICS(icp); - - cs = CPU(cpu); - ss = &icp->ss[cs->cpu_index]; - - assert(cs->cpu_index < icp->nr_servers); - if (icpkvm->kernel_xics_fd == -1) { - abort(); - } - - /* - * If we are reusing a parked vCPU fd corresponding to the CPU - * which was hot-removed earlier we don't have to renable - * KVM_CAP_IRQ_XICS capability again. - */ - if (ss->cap_irq_xics_enabled) { - return; - } - - if (icpkvm->kernel_xics_fd != -1) { - int ret; - - ss->cs = cs; - - ret = kvm_vcpu_enable_cap(cs, KVM_CAP_IRQ_XICS, 0, - icpkvm->kernel_xics_fd, kvm_arch_vcpu_id(cs)); - if (ret < 0) { - error_report("Unable to connect CPU%ld to kernel XICS: %s", - kvm_arch_vcpu_id(cs), strerror(errno)); - exit(1); - } - ss->cap_irq_xics_enabled = true; - } -} - -static void xics_kvm_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp) -{ - icp->nr_irqs = icp->ics->nr_irqs = nr_irqs; -} - -static void xics_kvm_set_nr_servers(XICSState *icp, uint32_t nr_servers, - Error **errp) -{ - int i; - - icp->nr_servers = nr_servers; - - icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState)); - for (i = 0; i < icp->nr_servers; i++) { - char buffer[32]; - object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_KVM_ICP); - snprintf(buffer, sizeof(buffer), "icp[%d]", i); - object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]), - errp); - } -} - -static void rtas_dummy(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, - uint32_t nargs, target_ulong args, - uint32_t nret, target_ulong rets) -{ - error_report("pseries: %s must never be called for in-kernel XICS", - __func__); -} - -static void xics_kvm_realize(DeviceState *dev, Error **errp) -{ - KVMXICSState *icpkvm = KVM_XICS(dev); - XICSState *icp = XICS_COMMON(dev); - int i, rc; - Error *error = NULL; - struct kvm_create_device xics_create_device = { - .type = KVM_DEV_TYPE_XICS, - .flags = 0, - }; - - if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_IRQ_XICS)) { - error_setg(errp, - "KVM and IRQ_XICS capability must be present for in-kernel XICS"); - goto fail; - } - - spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_dummy); - spapr_rtas_register(RTAS_IBM_GET_XIVE, "ibm,get-xive", rtas_dummy); - spapr_rtas_register(RTAS_IBM_INT_OFF, "ibm,int-off", rtas_dummy); - spapr_rtas_register(RTAS_IBM_INT_ON, "ibm,int-on", rtas_dummy); - - rc = kvmppc_define_rtas_kernel_token(RTAS_IBM_SET_XIVE, "ibm,set-xive"); - if (rc < 0) { - error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,set-xive"); - goto fail; - } - - rc = kvmppc_define_rtas_kernel_token(RTAS_IBM_GET_XIVE, "ibm,get-xive"); - if (rc < 0) { - error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,get-xive"); - goto fail; - } - - rc = kvmppc_define_rtas_kernel_token(RTAS_IBM_INT_ON, "ibm,int-on"); - if (rc < 0) { - error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,int-on"); - goto fail; - } - - rc = kvmppc_define_rtas_kernel_token(RTAS_IBM_INT_OFF, "ibm,int-off"); - if (rc < 0) { - error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,int-off"); - goto fail; - } - - /* Create the kernel ICP */ - rc = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &xics_create_device); - if (rc < 0) { - error_setg_errno(errp, -rc, "Error on KVM_CREATE_DEVICE for XICS"); - goto fail; - } - - icpkvm->kernel_xics_fd = xics_create_device.fd; - - object_property_set_bool(OBJECT(icp->ics), true, "realized", &error); - if (error) { - error_propagate(errp, error); - goto fail; - } - - assert(icp->nr_servers); - for (i = 0; i < icp->nr_servers; i++) { - object_property_set_bool(OBJECT(&icp->ss[i]), true, "realized", &error); - if (error) { - error_propagate(errp, error); - goto fail; - } - } - - kvm_kernel_irqchip = true; - kvm_msi_via_irqfd_allowed = true; - kvm_gsi_direct_mapping = true; - - return; - -fail: - kvmppc_define_rtas_kernel_token(0, "ibm,set-xive"); - kvmppc_define_rtas_kernel_token(0, "ibm,get-xive"); - kvmppc_define_rtas_kernel_token(0, "ibm,int-on"); - kvmppc_define_rtas_kernel_token(0, "ibm,int-off"); -} - -static void xics_kvm_initfn(Object *obj) -{ - XICSState *xics = XICS_COMMON(obj); - - xics->ics = ICS(object_new(TYPE_KVM_ICS)); - object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL); - xics->ics->icp = xics; -} - -static void xics_kvm_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - XICSStateClass *xsc = XICS_COMMON_CLASS(oc); - - dc->realize = xics_kvm_realize; - xsc->cpu_setup = xics_kvm_cpu_setup; - xsc->set_nr_irqs = xics_kvm_set_nr_irqs; - xsc->set_nr_servers = xics_kvm_set_nr_servers; -} - -static const TypeInfo xics_kvm_info = { - .name = TYPE_KVM_XICS, - .parent = TYPE_XICS_COMMON, - .instance_size = sizeof(KVMXICSState), - .class_init = xics_kvm_class_init, - .instance_init = xics_kvm_initfn, -}; - -static void xics_kvm_register_types(void) -{ - type_register_static(&xics_kvm_info); - type_register_static(&ics_kvm_info); - type_register_static(&icp_kvm_info); -} - -type_init(xics_kvm_register_types) diff --git a/qemu/hw/intc/xilinx_intc.c b/qemu/hw/intc/xilinx_intc.c deleted file mode 100644 index 9d8139bc6..000000000 --- a/qemu/hw/intc/xilinx_intc.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * QEMU Xilinx OPB Interrupt Controller. - * - * Copyright (c) 2009 Edgar E. Iglesias. - * - * 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/sysbus.h" -#include "hw/hw.h" - -#define D(x) - -#define R_ISR 0 -#define R_IPR 1 -#define R_IER 2 -#define R_IAR 3 -#define R_SIE 4 -#define R_CIE 5 -#define R_IVR 6 -#define R_MER 7 -#define R_MAX 8 - -#define TYPE_XILINX_INTC "xlnx.xps-intc" -#define XILINX_INTC(obj) OBJECT_CHECK(struct xlx_pic, (obj), TYPE_XILINX_INTC) - -struct xlx_pic -{ - SysBusDevice parent_obj; - - MemoryRegion mmio; - qemu_irq parent_irq; - - /* Configuration reg chosen at synthesis-time. QEMU populates - the bits at board-setup. */ - uint32_t c_kind_of_intr; - - /* Runtime control registers. */ - uint32_t regs[R_MAX]; - /* state of the interrupt input pins */ - uint32_t irq_pin_state; -}; - -static void update_irq(struct xlx_pic *p) -{ - uint32_t i; - - /* level triggered interrupt */ - if (p->regs[R_MER] & 2) { - p->regs[R_ISR] |= p->irq_pin_state & ~p->c_kind_of_intr; - } - - /* Update the pending register. */ - p->regs[R_IPR] = p->regs[R_ISR] & p->regs[R_IER]; - - /* Update the vector register. */ - for (i = 0; i < 32; i++) { - if (p->regs[R_IPR] & (1U << i)) { - break; - } - } - if (i == 32) - i = ~0; - - p->regs[R_IVR] = i; - qemu_set_irq(p->parent_irq, (p->regs[R_MER] & 1) && p->regs[R_IPR]); -} - -static uint64_t -pic_read(void *opaque, hwaddr addr, unsigned int size) -{ - struct xlx_pic *p = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) - { - default: - if (addr < ARRAY_SIZE(p->regs)) - r = p->regs[addr]; - break; - - } - D(printf("%s %x=%x\n", __func__, addr * 4, r)); - return r; -} - -static void -pic_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - struct xlx_pic *p = opaque; - uint32_t value = val64; - - addr >>= 2; - D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value)); - switch (addr) - { - case R_IAR: - p->regs[R_ISR] &= ~value; /* ACK. */ - break; - case R_SIE: - p->regs[R_IER] |= value; /* Atomic set ie. */ - break; - case R_CIE: - p->regs[R_IER] &= ~value; /* Atomic clear ie. */ - break; - case R_MER: - p->regs[R_MER] = value & 0x3; - break; - case R_ISR: - if ((p->regs[R_MER] & 2)) { - break; - } - /* fallthrough */ - default: - if (addr < ARRAY_SIZE(p->regs)) - p->regs[addr] = value; - break; - } - update_irq(p); -} - -static const MemoryRegionOps pic_ops = { - .read = pic_read, - .write = pic_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static void irq_handler(void *opaque, int irq, int level) -{ - struct xlx_pic *p = opaque; - - /* edge triggered interrupt */ - if (p->c_kind_of_intr & (1 << irq) && p->regs[R_MER] & 2) { - p->regs[R_ISR] |= (level << irq); - } - - p->irq_pin_state &= ~(1 << irq); - p->irq_pin_state |= level << irq; - update_irq(p); -} - -static void xilinx_intc_init(Object *obj) -{ - struct xlx_pic *p = XILINX_INTC(obj); - - qdev_init_gpio_in(DEVICE(obj), irq_handler, 32); - sysbus_init_irq(SYS_BUS_DEVICE(obj), &p->parent_irq); - - memory_region_init_io(&p->mmio, obj, &pic_ops, p, "xlnx.xps-intc", - R_MAX * 4); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio); -} - -static Property xilinx_intc_properties[] = { - DEFINE_PROP_UINT32("kind-of-intr", struct xlx_pic, c_kind_of_intr, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void xilinx_intc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->props = xilinx_intc_properties; -} - -static const TypeInfo xilinx_intc_info = { - .name = TYPE_XILINX_INTC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct xlx_pic), - .instance_init = xilinx_intc_init, - .class_init = xilinx_intc_class_init, -}; - -static void xilinx_intc_register_types(void) -{ - type_register_static(&xilinx_intc_info); -} - -type_init(xilinx_intc_register_types) diff --git a/qemu/hw/ipack/Makefile.objs b/qemu/hw/ipack/Makefile.objs deleted file mode 100644 index 8b9bdcb54..000000000 --- a/qemu/hw/ipack/Makefile.objs +++ /dev/null @@ -1,2 +0,0 @@ -common-obj-$(CONFIG_IPACK) += ipack.o -common-obj-$(CONFIG_IPACK) += tpci200.o diff --git a/qemu/hw/ipack/ipack.c b/qemu/hw/ipack/ipack.c deleted file mode 100644 index 5f99ed9a7..000000000 --- a/qemu/hw/ipack/ipack.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * QEMU IndustryPack emulation - * - * Copyright (C) 2012 Igalia, S.L. - * Author: Alberto Garcia - * - * This code is licensed under the GNU GPL v2 or (at your option) any - * later version. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/ipack/ipack.h" - -IPackDevice *ipack_device_find(IPackBus *bus, int32_t slot) -{ - BusChild *kid; - - QTAILQ_FOREACH(kid, &BUS(bus)->children, sibling) { - DeviceState *qdev = kid->child; - IPackDevice *ip = IPACK_DEVICE(qdev); - if (ip->slot == slot) { - return ip; - } - } - return NULL; -} - -void ipack_bus_new_inplace(IPackBus *bus, size_t bus_size, - DeviceState *parent, - const char *name, uint8_t n_slots, - qemu_irq_handler handler) -{ - qbus_create_inplace(bus, bus_size, TYPE_IPACK_BUS, parent, name); - bus->n_slots = n_slots; - bus->set_irq = handler; -} - -static void ipack_device_realize(DeviceState *dev, Error **errp) -{ - IPackDevice *idev = IPACK_DEVICE(dev); - IPackBus *bus = IPACK_BUS(qdev_get_parent_bus(dev)); - IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev); - - if (idev->slot < 0) { - idev->slot = bus->free_slot; - } - if (idev->slot >= bus->n_slots) { - error_setg(errp, "Only %" PRIu8 " slots available.", bus->n_slots); - return; - } - bus->free_slot = idev->slot + 1; - - idev->irq = qemu_allocate_irqs(bus->set_irq, idev, 2); - - k->realize(dev, errp); -} - -static void ipack_device_unrealize(DeviceState *dev, Error **errp) -{ - IPackDevice *idev = IPACK_DEVICE(dev); - IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev); - Error *err = NULL; - - if (k->unrealize) { - k->unrealize(dev, &err); - error_propagate(errp, err); - return; - } - - qemu_free_irqs(idev->irq, 2); -} - -static Property ipack_device_props[] = { - DEFINE_PROP_INT32("slot", IPackDevice, slot, -1), - DEFINE_PROP_END_OF_LIST() -}; - -static void ipack_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_INPUT, k->categories); - k->bus_type = TYPE_IPACK_BUS; - k->realize = ipack_device_realize; - k->unrealize = ipack_device_unrealize; - k->props = ipack_device_props; -} - -const VMStateDescription vmstate_ipack_device = { - .name = "ipack_device", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32(slot, IPackDevice), - VMSTATE_END_OF_LIST() - } -}; - -static const TypeInfo ipack_device_info = { - .name = TYPE_IPACK_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(IPackDevice), - .class_size = sizeof(IPackDeviceClass), - .class_init = ipack_device_class_init, - .abstract = true, -}; - -static const TypeInfo ipack_bus_info = { - .name = TYPE_IPACK_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(IPackBus), -}; - -static void ipack_register_types(void) -{ - type_register_static(&ipack_device_info); - type_register_static(&ipack_bus_info); -} - -type_init(ipack_register_types) diff --git a/qemu/hw/ipack/tpci200.c b/qemu/hw/ipack/tpci200.c deleted file mode 100644 index fdda6f414..000000000 --- a/qemu/hw/ipack/tpci200.c +++ /dev/null @@ -1,656 +0,0 @@ -/* - * QEMU TEWS TPCI200 IndustryPack carrier emulation - * - * Copyright (C) 2012 Igalia, S.L. - * Author: Alberto Garcia - * - * This code is licensed under the GNU GPL v2 or (at your option) any - * later version. - */ - -#include "qemu/osdep.h" -#include "hw/ipack/ipack.h" -#include "hw/pci/pci.h" -#include "qemu/bitops.h" - -/* #define DEBUG_TPCI */ - -#ifdef DEBUG_TPCI -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, "TPCI200: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do { } while (0) -#endif - -#define N_MODULES 4 - -#define IP_ID_SPACE 2 -#define IP_INT_SPACE 3 -#define IP_IO_SPACE_ADDR_MASK 0x7F -#define IP_ID_SPACE_ADDR_MASK 0x3F -#define IP_INT_SPACE_ADDR_MASK 0x3F - -#define STATUS_INT(IP, INTNO) BIT((IP) * 2 + (INTNO)) -#define STATUS_TIME(IP) BIT((IP) + 12) -#define STATUS_ERR_ANY 0xF00 - -#define CTRL_CLKRATE BIT(0) -#define CTRL_RECOVER BIT(1) -#define CTRL_TIME_INT BIT(2) -#define CTRL_ERR_INT BIT(3) -#define CTRL_INT_EDGE(INTNO) BIT(4 + (INTNO)) -#define CTRL_INT(INTNO) BIT(6 + (INTNO)) - -#define REG_REV_ID 0x00 -#define REG_IP_A_CTRL 0x02 -#define REG_IP_B_CTRL 0x04 -#define REG_IP_C_CTRL 0x06 -#define REG_IP_D_CTRL 0x08 -#define REG_RESET 0x0A -#define REG_STATUS 0x0C -#define IP_N_FROM_REG(REG) ((REG) / 2 - 1) - -typedef struct { - PCIDevice dev; - IPackBus bus; - MemoryRegion mmio; - MemoryRegion io; - MemoryRegion las0; - MemoryRegion las1; - MemoryRegion las2; - MemoryRegion las3; - bool big_endian[3]; - uint8_t ctrl[N_MODULES]; - uint16_t status; - uint8_t int_set; -} TPCI200State; - -#define TYPE_TPCI200 "tpci200" - -#define TPCI200(obj) \ - OBJECT_CHECK(TPCI200State, (obj), TYPE_TPCI200) - -static const uint8_t local_config_regs[] = { - 0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xFC, 0xFF, 0x0F, 0x00, 0x00, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x08, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x60, 0x41, 0xD4, - 0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x01, - 0x14, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x08, 0x01, 0x02, - 0x00, 0x04, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x80, 0x02, 0x41, - 0x00, 0x00, 0x00, 0x00, 0x40, 0x7A, 0x00, 0x52, 0x92, 0x24, 0x02 -}; - -static void adjust_addr(bool big_endian, hwaddr *addr, unsigned size) -{ - /* During 8 bit access in big endian mode, - odd and even addresses are swapped */ - if (big_endian && size == 1) { - *addr ^= 1; - } -} - -static uint64_t adjust_value(bool big_endian, uint64_t *val, unsigned size) -{ - /* Local spaces only support 8/16 bit access, - * so there's no need to care for sizes > 2 */ - if (big_endian && size == 2) { - *val = bswap16(*val); - } - return *val; -} - -static void tpci200_set_irq(void *opaque, int intno, int level) -{ - IPackDevice *ip = opaque; - IPackBus *bus = IPACK_BUS(qdev_get_parent_bus(DEVICE(ip))); - PCIDevice *pcidev = PCI_DEVICE(BUS(bus)->parent); - TPCI200State *dev = TPCI200(pcidev); - unsigned ip_n = ip->slot; - uint16_t prev_status = dev->status; - - assert(ip->slot >= 0 && ip->slot < N_MODULES); - - /* The requested interrupt must be enabled in the IP CONTROL - * register */ - if (!(dev->ctrl[ip_n] & CTRL_INT(intno))) { - return; - } - - /* Update the interrupt status in the IP STATUS register */ - if (level) { - dev->status |= STATUS_INT(ip_n, intno); - } else { - dev->status &= ~STATUS_INT(ip_n, intno); - } - - /* Return if there are no changes */ - if (dev->status == prev_status) { - return; - } - - DPRINTF("IP %u INT%u#: %u\n", ip_n, intno, level); - - /* Check if the interrupt is edge sensitive */ - if (dev->ctrl[ip_n] & CTRL_INT_EDGE(intno)) { - if (level) { - pci_set_irq(&dev->dev, !dev->int_set); - pci_set_irq(&dev->dev, dev->int_set); - } - } else { - unsigned i, j; - uint16_t level_status = dev->status; - - /* Check if there are any level sensitive interrupts set by - removing the ones that are edge sensitive from the status - register */ - for (i = 0; i < N_MODULES; i++) { - for (j = 0; j < 2; j++) { - if (dev->ctrl[i] & CTRL_INT_EDGE(j)) { - level_status &= ~STATUS_INT(i, j); - } - } - } - - if (level_status && !dev->int_set) { - pci_irq_assert(&dev->dev); - dev->int_set = 1; - } else if (!level_status && dev->int_set) { - pci_irq_deassert(&dev->dev); - dev->int_set = 0; - } - } -} - -static uint64_t tpci200_read_cfg(void *opaque, hwaddr addr, unsigned size) -{ - TPCI200State *s = opaque; - uint8_t ret = 0; - if (addr < ARRAY_SIZE(local_config_regs)) { - ret = local_config_regs[addr]; - } - /* Endianness is stored in the first bit of these registers */ - if ((addr == 0x2b && s->big_endian[0]) || - (addr == 0x2f && s->big_endian[1]) || - (addr == 0x33 && s->big_endian[2])) { - ret |= 1; - } - DPRINTF("Read from LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) ret); - return ret; -} - -static void tpci200_write_cfg(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - TPCI200State *s = opaque; - /* Endianness is stored in the first bit of these registers */ - if (addr == 0x2b || addr == 0x2f || addr == 0x33) { - unsigned las = (addr - 0x2b) / 4; - s->big_endian[las] = val & 1; - DPRINTF("LAS%u big endian mode: %u\n", las, (unsigned) val & 1); - } else { - DPRINTF("Write to LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) val); - } -} - -static uint64_t tpci200_read_las0(void *opaque, hwaddr addr, unsigned size) -{ - TPCI200State *s = opaque; - uint64_t ret = 0; - - switch (addr) { - - case REG_REV_ID: - DPRINTF("Read REVISION ID\n"); /* Current value is 0x00 */ - break; - - case REG_IP_A_CTRL: - case REG_IP_B_CTRL: - case REG_IP_C_CTRL: - case REG_IP_D_CTRL: - { - unsigned ip_n = IP_N_FROM_REG(addr); - ret = s->ctrl[ip_n]; - DPRINTF("Read IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) ret); - } - break; - - case REG_RESET: - DPRINTF("Read RESET\n"); /* Not implemented */ - break; - - case REG_STATUS: - ret = s->status; - DPRINTF("Read STATUS: 0x%x\n", (unsigned) ret); - break; - - /* Reserved */ - default: - DPRINTF("Unsupported read from LAS0 0x%x\n", (unsigned) addr); - break; - } - - return adjust_value(s->big_endian[0], &ret, size); -} - -static void tpci200_write_las0(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - TPCI200State *s = opaque; - - adjust_value(s->big_endian[0], &val, size); - - switch (addr) { - - case REG_REV_ID: - DPRINTF("Write Revision ID: 0x%x\n", (unsigned) val); /* No effect */ - break; - - case REG_IP_A_CTRL: - case REG_IP_B_CTRL: - case REG_IP_C_CTRL: - case REG_IP_D_CTRL: - { - unsigned ip_n = IP_N_FROM_REG(addr); - s->ctrl[ip_n] = val; - DPRINTF("Write IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) val); - } - break; - - case REG_RESET: - DPRINTF("Write RESET: 0x%x\n", (unsigned) val); /* Not implemented */ - break; - - case REG_STATUS: - { - unsigned i; - - for (i = 0; i < N_MODULES; i++) { - IPackDevice *ip = ipack_device_find(&s->bus, i); - - if (ip != NULL) { - if (val & STATUS_INT(i, 0)) { - DPRINTF("Clear IP %c INT0# status\n", 'A' + i); - qemu_irq_lower(ip->irq[0]); - } - if (val & STATUS_INT(i, 1)) { - DPRINTF("Clear IP %c INT1# status\n", 'A' + i); - qemu_irq_lower(ip->irq[1]); - } - } - - if (val & STATUS_TIME(i)) { - DPRINTF("Clear IP %c timeout\n", 'A' + i); - s->status &= ~STATUS_TIME(i); - } - } - - if (val & STATUS_ERR_ANY) { - DPRINTF("Unexpected write to STATUS register: 0x%x\n", - (unsigned) val); - } - } - break; - - /* Reserved */ - default: - DPRINTF("Unsupported write to LAS0 0x%x: 0x%x\n", - (unsigned) addr, (unsigned) val); - break; - } -} - -static uint64_t tpci200_read_las1(void *opaque, hwaddr addr, unsigned size) -{ - TPCI200State *s = opaque; - IPackDevice *ip; - uint64_t ret = 0; - unsigned ip_n, space; - uint8_t offset; - - adjust_addr(s->big_endian[1], &addr, size); - - /* - * The address is divided into the IP module number (0-4), the IP - * address space (I/O, ID, INT) and the offset within that space. - */ - ip_n = addr >> 8; - space = (addr >> 6) & 3; - ip = ipack_device_find(&s->bus, ip_n); - - if (ip == NULL) { - DPRINTF("Read LAS1: IP module %u not installed\n", ip_n); - } else { - IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); - switch (space) { - - case IP_ID_SPACE: - offset = addr & IP_ID_SPACE_ADDR_MASK; - if (k->id_read) { - ret = k->id_read(ip, offset); - } - break; - - case IP_INT_SPACE: - offset = addr & IP_INT_SPACE_ADDR_MASK; - - /* Read address 0 to ACK IP INT0# and address 2 to ACK IP INT1# */ - if (offset == 0 || offset == 2) { - unsigned intno = offset / 2; - bool int_set = s->status & STATUS_INT(ip_n, intno); - bool int_edge_sensitive = s->ctrl[ip_n] & CTRL_INT_EDGE(intno); - if (int_set && !int_edge_sensitive) { - qemu_irq_lower(ip->irq[intno]); - } - } - - if (k->int_read) { - ret = k->int_read(ip, offset); - } - break; - - default: - offset = addr & IP_IO_SPACE_ADDR_MASK; - if (k->io_read) { - ret = k->io_read(ip, offset); - } - break; - } - } - - return adjust_value(s->big_endian[1], &ret, size); -} - -static void tpci200_write_las1(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - TPCI200State *s = opaque; - IPackDevice *ip; - unsigned ip_n, space; - uint8_t offset; - - adjust_addr(s->big_endian[1], &addr, size); - adjust_value(s->big_endian[1], &val, size); - - /* - * The address is divided into the IP module number, the IP - * address space (I/O, ID, INT) and the offset within that space. - */ - ip_n = addr >> 8; - space = (addr >> 6) & 3; - ip = ipack_device_find(&s->bus, ip_n); - - if (ip == NULL) { - DPRINTF("Write LAS1: IP module %u not installed\n", ip_n); - } else { - IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); - switch (space) { - - case IP_ID_SPACE: - offset = addr & IP_ID_SPACE_ADDR_MASK; - if (k->id_write) { - k->id_write(ip, offset, val); - } - break; - - case IP_INT_SPACE: - offset = addr & IP_INT_SPACE_ADDR_MASK; - if (k->int_write) { - k->int_write(ip, offset, val); - } - break; - - default: - offset = addr & IP_IO_SPACE_ADDR_MASK; - if (k->io_write) { - k->io_write(ip, offset, val); - } - break; - } - } -} - -static uint64_t tpci200_read_las2(void *opaque, hwaddr addr, unsigned size) -{ - TPCI200State *s = opaque; - IPackDevice *ip; - uint64_t ret = 0; - unsigned ip_n; - uint32_t offset; - - adjust_addr(s->big_endian[2], &addr, size); - - /* - * The address is divided into the IP module number and the offset - * within the IP module MEM space. - */ - ip_n = addr >> 23; - offset = addr & 0x7fffff; - ip = ipack_device_find(&s->bus, ip_n); - - if (ip == NULL) { - DPRINTF("Read LAS2: IP module %u not installed\n", ip_n); - } else { - IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); - if (k->mem_read16) { - ret = k->mem_read16(ip, offset); - } - } - - return adjust_value(s->big_endian[2], &ret, size); -} - -static void tpci200_write_las2(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - TPCI200State *s = opaque; - IPackDevice *ip; - unsigned ip_n; - uint32_t offset; - - adjust_addr(s->big_endian[2], &addr, size); - adjust_value(s->big_endian[2], &val, size); - - /* - * The address is divided into the IP module number and the offset - * within the IP module MEM space. - */ - ip_n = addr >> 23; - offset = addr & 0x7fffff; - ip = ipack_device_find(&s->bus, ip_n); - - if (ip == NULL) { - DPRINTF("Write LAS2: IP module %u not installed\n", ip_n); - } else { - IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); - if (k->mem_write16) { - k->mem_write16(ip, offset, val); - } - } -} - -static uint64_t tpci200_read_las3(void *opaque, hwaddr addr, unsigned size) -{ - TPCI200State *s = opaque; - IPackDevice *ip; - uint64_t ret = 0; - /* - * The address is divided into the IP module number and the offset - * within the IP module MEM space. - */ - unsigned ip_n = addr >> 22; - uint32_t offset = addr & 0x3fffff; - - ip = ipack_device_find(&s->bus, ip_n); - - if (ip == NULL) { - DPRINTF("Read LAS3: IP module %u not installed\n", ip_n); - } else { - IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); - if (k->mem_read8) { - ret = k->mem_read8(ip, offset); - } - } - - return ret; -} - -static void tpci200_write_las3(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - TPCI200State *s = opaque; - IPackDevice *ip; - /* - * The address is divided into the IP module number and the offset - * within the IP module MEM space. - */ - unsigned ip_n = addr >> 22; - uint32_t offset = addr & 0x3fffff; - - ip = ipack_device_find(&s->bus, ip_n); - - if (ip == NULL) { - DPRINTF("Write LAS3: IP module %u not installed\n", ip_n); - } else { - IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); - if (k->mem_write8) { - k->mem_write8(ip, offset, val); - } - } -} - -static const MemoryRegionOps tpci200_cfg_ops = { - .read = tpci200_read_cfg, - .write = tpci200_write_cfg, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 4 - }, - .impl = { - .min_access_size = 1, - .max_access_size = 1 - } -}; - -static const MemoryRegionOps tpci200_las0_ops = { - .read = tpci200_read_las0, - .write = tpci200_write_las0, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 2, - .max_access_size = 2 - } -}; - -static const MemoryRegionOps tpci200_las1_ops = { - .read = tpci200_read_las1, - .write = tpci200_write_las1, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 2 - } -}; - -static const MemoryRegionOps tpci200_las2_ops = { - .read = tpci200_read_las2, - .write = tpci200_write_las2, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 2 - } -}; - -static const MemoryRegionOps tpci200_las3_ops = { - .read = tpci200_read_las3, - .write = tpci200_write_las3, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1 - } -}; - -static void tpci200_realize(PCIDevice *pci_dev, Error **errp) -{ - TPCI200State *s = TPCI200(pci_dev); - uint8_t *c = s->dev.config; - - pci_set_word(c + PCI_COMMAND, 0x0003); - pci_set_word(c + PCI_STATUS, 0x0280); - - pci_set_byte(c + PCI_INTERRUPT_PIN, 0x01); /* Interrupt pin A */ - - pci_set_byte(c + PCI_CAPABILITY_LIST, 0x40); - pci_set_long(c + 0x40, 0x48014801); - pci_set_long(c + 0x48, 0x00024C06); - pci_set_long(c + 0x4C, 0x00000003); - - memory_region_init_io(&s->mmio, OBJECT(s), &tpci200_cfg_ops, - s, "tpci200_mmio", 128); - memory_region_init_io(&s->io, OBJECT(s), &tpci200_cfg_ops, - s, "tpci200_io", 128); - memory_region_init_io(&s->las0, OBJECT(s), &tpci200_las0_ops, - s, "tpci200_las0", 256); - memory_region_init_io(&s->las1, OBJECT(s), &tpci200_las1_ops, - s, "tpci200_las1", 1024); - memory_region_init_io(&s->las2, OBJECT(s), &tpci200_las2_ops, - s, "tpci200_las2", 1024*1024*32); - memory_region_init_io(&s->las3, OBJECT(s), &tpci200_las3_ops, - s, "tpci200_las3", 1024*1024*16); - pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); - pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io); - pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las0); - pci_register_bar(&s->dev, 3, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las1); - pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las2); - pci_register_bar(&s->dev, 5, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las3); - - ipack_bus_new_inplace(&s->bus, sizeof(s->bus), DEVICE(pci_dev), NULL, - N_MODULES, tpci200_set_irq); -} - -static const VMStateDescription vmstate_tpci200 = { - .name = "tpci200", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, TPCI200State), - VMSTATE_BOOL_ARRAY(big_endian, TPCI200State, 3), - VMSTATE_UINT8_ARRAY(ctrl, TPCI200State, N_MODULES), - VMSTATE_UINT16(status, TPCI200State), - VMSTATE_UINT8(int_set, TPCI200State), - VMSTATE_END_OF_LIST() - } -}; - -static void tpci200_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = tpci200_realize; - k->vendor_id = PCI_VENDOR_ID_TEWS; - k->device_id = PCI_DEVICE_ID_TEWS_TPCI200; - k->class_id = PCI_CLASS_BRIDGE_OTHER; - k->subsystem_vendor_id = PCI_VENDOR_ID_TEWS; - k->subsystem_id = 0x300A; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - dc->desc = "TEWS TPCI200 IndustryPack carrier"; - dc->vmsd = &vmstate_tpci200; -} - -static const TypeInfo tpci200_info = { - .name = TYPE_TPCI200, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(TPCI200State), - .class_init = tpci200_class_init, -}; - -static void tpci200_register_types(void) -{ - type_register_static(&tpci200_info); -} - -type_init(tpci200_register_types) diff --git a/qemu/hw/ipmi/Makefile.objs b/qemu/hw/ipmi/Makefile.objs deleted file mode 100644 index a90318d5b..000000000 --- a/qemu/hw/ipmi/Makefile.objs +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index 6adec1e99..000000000 --- a/qemu/hw/ipmi/ipmi.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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 deleted file mode 100644 index fe12112a2..000000000 --- a/qemu/hw/ipmi/ipmi_bmc_extern.c +++ /dev/null @@ -1,519 +0,0 @@ -/* - * 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 deleted file mode 100644 index dc9c14cd2..000000000 --- a/qemu/hw/ipmi/ipmi_bmc_sim.c +++ /dev/null @@ -1,1810 +0,0 @@ -/* - * 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 deleted file mode 100644 index aaea12ecd..000000000 --- a/qemu/hw/ipmi/isa_ipmi_bt.c +++ /dev/null @@ -1,530 +0,0 @@ -/* - * 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 deleted file mode 100644 index 2742ce06c..000000000 --- a/qemu/hw/ipmi/isa_ipmi_kcs.c +++ /dev/null @@ -1,495 +0,0 @@ -/* - * 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/Makefile.objs b/qemu/hw/isa/Makefile.objs deleted file mode 100644 index 9164556a4..000000000 --- a/qemu/hw/isa/Makefile.objs +++ /dev/null @@ -1,8 +0,0 @@ -common-obj-y += isa-bus.o -common-obj-$(CONFIG_APM) += apm.o -common-obj-$(CONFIG_I82378) += i82378.o -common-obj-$(CONFIG_PC87312) += pc87312.o -common-obj-$(CONFIG_PIIX4) += piix4.o -common-obj-$(CONFIG_VT82C686) += vt82c686.o - -obj-$(CONFIG_LPC_ICH9) += lpc_ich9.o diff --git a/qemu/hw/isa/apm.c b/qemu/hw/isa/apm.c deleted file mode 100644 index e232b0da0..000000000 --- a/qemu/hw/isa/apm.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * QEMU PC APM controller Emulation - * This is split out from acpi.c - * - * 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, see - * - * 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/isa/apm.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" - -//#define DEBUG - -#ifdef DEBUG -# define APM_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) -#else -# define APM_DPRINTF(format, ...) do { } while (0) -#endif - -/* fixed I/O location */ -#define APM_CNT_IOPORT 0xb2 -#define APM_STS_IOPORT 0xb3 - -static void apm_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - APMState *apm = opaque; - addr &= 1; - APM_DPRINTF("apm_ioport_writeb addr=0x%" HWADDR_PRIx - " val=0x%02" PRIx64 "\n", addr, val); - if (addr == 0) { - apm->apmc = val; - - if (apm->callback) { - (apm->callback)(val, apm->arg); - } - } else { - apm->apms = val; - } -} - -static uint64_t apm_ioport_readb(void *opaque, hwaddr addr, unsigned size) -{ - APMState *apm = opaque; - uint32_t val; - - addr &= 1; - if (addr == 0) { - val = apm->apmc; - } else { - val = apm->apms; - } - APM_DPRINTF("apm_ioport_readb addr=0x%" HWADDR_PRIx " val=0x%02x\n", addr, val); - return val; -} - -const VMStateDescription vmstate_apm = { - .name = "APM State", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(apmc, APMState), - VMSTATE_UINT8(apms, APMState), - VMSTATE_END_OF_LIST() - } -}; - -static const MemoryRegionOps apm_ops = { - .read = apm_ioport_readb, - .write = apm_ioport_writeb, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -void apm_init(PCIDevice *dev, APMState *apm, apm_ctrl_changed_t callback, - void *arg) -{ - apm->callback = callback; - apm->arg = arg; - - /* ioport 0xb2, 0xb3 */ - memory_region_init_io(&apm->io, OBJECT(dev), &apm_ops, apm, "apm-io", 2); - memory_region_add_subregion(pci_address_space_io(dev), APM_CNT_IOPORT, - &apm->io); -} diff --git a/qemu/hw/isa/i82378.c b/qemu/hw/isa/i82378.c deleted file mode 100644 index 4d29a9900..000000000 --- a/qemu/hw/isa/i82378.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * QEMU Intel i82378 emulation (PCI to ISA bridge) - * - * Copyright (c) 2010-2011 Hervé Poussineau - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "hw/pci/pci.h" -#include "hw/i386/pc.h" -#include "hw/timer/i8254.h" -#include "hw/audio/pcspk.h" - -#define TYPE_I82378 "i82378" -#define I82378(obj) \ - OBJECT_CHECK(I82378State, (obj), TYPE_I82378) - -typedef struct I82378State { - PCIDevice parent_obj; - - qemu_irq out[2]; - qemu_irq *i8259; - MemoryRegion io; -} I82378State; - -static const VMStateDescription vmstate_i82378 = { - .name = "pci-i82378", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, I82378State), - VMSTATE_END_OF_LIST() - }, -}; - -static void i82378_request_out0_irq(void *opaque, int irq, int level) -{ - I82378State *s = opaque; - qemu_set_irq(s->out[0], level); -} - -static void i82378_request_pic_irq(void *opaque, int irq, int level) -{ - DeviceState *dev = opaque; - I82378State *s = I82378(dev); - - qemu_set_irq(s->i8259[irq], level); -} - -static void i82378_realize(PCIDevice *pci, Error **errp) -{ - DeviceState *dev = DEVICE(pci); - I82378State *s = I82378(dev); - uint8_t *pci_conf; - ISABus *isabus; - ISADevice *isa; - - pci_conf = pci->config; - pci_set_word(pci_conf + PCI_COMMAND, - PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - pci_set_word(pci_conf + PCI_STATUS, - PCI_STATUS_DEVSEL_MEDIUM); - - pci_config_set_interrupt_pin(pci_conf, 1); /* interrupt pin 0 */ - - isabus = isa_bus_new(dev, get_system_memory(), - pci_address_space_io(pci), errp); - if (!isabus) { - return; - } - - /* This device has: - 2 82C59 (irq) - 1 82C54 (pit) - 2 82C37 (dma) - NMI - Utility Bus Support Registers - - All devices accept byte access only, except timer - */ - - /* 2 82C59 (irq) */ - s->i8259 = i8259_init(isabus, - qemu_allocate_irq(i82378_request_out0_irq, s, 0)); - isa_bus_irqs(isabus, s->i8259); - - /* 1 82C54 (pit) */ - isa = pit_init(isabus, 0x40, 0, NULL); - - /* speaker */ - pcspk_init(isabus, isa); - - /* 2 82C37 (dma) */ - isa = isa_create_simple(isabus, "i82374"); - - /* timer */ - isa_create_simple(isabus, "mc146818rtc"); -} - -static void i82378_init(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - I82378State *s = I82378(obj); - - qdev_init_gpio_out(dev, s->out, 1); - qdev_init_gpio_in(dev, i82378_request_pic_irq, 16); -} - -static void i82378_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = i82378_realize; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82378; - k->revision = 0x03; - k->class_id = PCI_CLASS_BRIDGE_ISA; - dc->vmsd = &vmstate_i82378; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); -} - -static const TypeInfo i82378_type_info = { - .name = TYPE_I82378, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(I82378State), - .instance_init = i82378_init, - .class_init = i82378_class_init, -}; - -static void i82378_register_types(void) -{ - type_register_static(&i82378_type_info); -} - -type_init(i82378_register_types) diff --git a/qemu/hw/isa/isa-bus.c b/qemu/hw/isa/isa-bus.c deleted file mode 100644 index 7aa115caf..000000000 --- a/qemu/hw/isa/isa-bus.c +++ /dev/null @@ -1,310 +0,0 @@ -/* - * isa bus support for qdev. - * - * Copyright (c) 2009 Gerd Hoffmann - * - * 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 . - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/hw.h" -#include "monitor/monitor.h" -#include "hw/sysbus.h" -#include "sysemu/sysemu.h" -#include "hw/isa/isa.h" -#include "hw/i386/pc.h" - -static ISABus *isabus; - -static void isabus_dev_print(Monitor *mon, DeviceState *dev, int indent); -static char *isabus_get_fw_dev_path(DeviceState *dev); - -static void isa_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - - k->print_dev = isabus_dev_print; - 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, - .instance_size = sizeof(ISABus), - .class_init = isa_bus_class_init, -}; - -ISABus *isa_bus_new(DeviceState *dev, MemoryRegion* address_space, - MemoryRegion *address_space_io, Error **errp) -{ - if (isabus) { - error_setg(errp, "Can't create a second ISA bus"); - return NULL; - } - if (!dev) { - dev = qdev_create(NULL, "isabus-bridge"); - qdev_init_nofail(dev); - } - - isabus = ISA_BUS(qbus_create(TYPE_ISA_BUS, dev, NULL)); - isabus->address_space = address_space; - isabus->address_space_io = address_space_io; - return isabus; -} - -void isa_bus_irqs(ISABus *bus, qemu_irq *irqs) -{ - bus->irqs = irqs; -} - -/* - * isa_get_irq() returns the corresponding qemu_irq entry for the i8259. - * - * This function is only for special cases such as the 'ferr', and - * temporary use for normal devices until they are converted to qdev. - */ -qemu_irq isa_get_irq(ISADevice *dev, int isairq) -{ - assert(!dev || ISA_BUS(qdev_get_parent_bus(DEVICE(dev))) == isabus); - if (isairq < 0 || isairq > 15) { - hw_error("isa irq %d invalid", isairq); - } - return isabus->irqs[isairq]; -} - -void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq) -{ - assert(dev->nirqs < ARRAY_SIZE(dev->isairq)); - dev->isairq[dev->nirqs] = isairq; - *p = isa_get_irq(dev, 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)) { - dev->ioport_id = ioport; - } -} - -void isa_register_ioport(ISADevice *dev, MemoryRegion *io, uint16_t start) -{ - memory_region_add_subregion(isabus->address_space_io, start, io); - isa_init_ioport(dev, start); -} - -void isa_register_portio_list(ISADevice *dev, uint16_t start, - const MemoryRegionPortio *pio_start, - void *opaque, const char *name) -{ - PortioList piolist; - - /* START is how we should treat DEV, regardless of the actual - contents of the portio array. This is how the old code - actually handled e.g. the FDC device. */ - isa_init_ioport(dev, start); - - /* FIXME: the device should store created PortioList in its state. Note - that DEV can be NULL here and that single device can register several - portio lists. Current implementation is leaking memory allocated - in portio_list_init. The leak is not critical because it happens only - at initialization time. */ - portio_list_init(&piolist, OBJECT(dev), pio_start, opaque, name); - portio_list_add(&piolist, isabus->address_space_io, start); -} - -static void isa_device_init(Object *obj) -{ - ISADevice *dev = ISA_DEVICE(obj); - - dev->isairq[0] = -1; - dev->isairq[1] = -1; -} - -ISADevice *isa_create(ISABus *bus, const char *name) -{ - DeviceState *dev; - - dev = qdev_create(BUS(bus), name); - return ISA_DEVICE(dev); -} - -ISADevice *isa_try_create(ISABus *bus, const char *name) -{ - DeviceState *dev; - - dev = qdev_try_create(BUS(bus), name); - return ISA_DEVICE(dev); -} - -ISADevice *isa_create_simple(ISABus *bus, const char *name) -{ - ISADevice *dev; - - dev = isa_create(bus, name); - qdev_init_nofail(DEVICE(dev)); - return dev; -} - -ISADevice *isa_vga_init(ISABus *bus) -{ - switch (vga_interface_type) { - case VGA_CIRRUS: - return isa_create_simple(bus, "isa-cirrus-vga"); - case VGA_QXL: - fprintf(stderr, "%s: qxl: no PCI bus\n", __func__); - return NULL; - case VGA_STD: - return isa_create_simple(bus, "isa-vga"); - case VGA_VMWARE: - fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __func__); - return NULL; - case VGA_VIRTIO: - fprintf(stderr, "%s: virtio-vga: no PCI bus\n", __func__); - return NULL; - case VGA_NONE: - default: - return NULL; - } -} - -static void isabus_dev_print(Monitor *mon, DeviceState *dev, int indent) -{ - ISADevice *d = ISA_DEVICE(dev); - - if (d->isairq[1] != -1) { - monitor_printf(mon, "%*sisa irqs %d,%d\n", indent, "", - d->isairq[0], d->isairq[1]); - } else if (d->isairq[0] != -1) { - monitor_printf(mon, "%*sisa irq %d\n", indent, "", - d->isairq[0]); - } -} - -static void isabus_bridge_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->fw_name = "isa"; -} - -static const TypeInfo isabus_bridge_info = { - .name = "isabus-bridge", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysBusDevice), - .class_init = isabus_bridge_class_init, -}; - -static void isa_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - k->bus_type = TYPE_ISA_BUS; -} - -static const TypeInfo isa_device_type_info = { - .name = TYPE_ISA_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(ISADevice), - .instance_init = isa_device_init, - .abstract = true, - .class_size = sizeof(ISADeviceClass), - .class_init = isa_device_class_init, -}; - -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); -} - -static char *isabus_get_fw_dev_path(DeviceState *dev) -{ - ISADevice *d = ISA_DEVICE(dev); - char path[40]; - int off; - - off = snprintf(path, sizeof(path), "%s", qdev_fw_name(dev)); - if (d->ioport_id) { - snprintf(path + off, sizeof(path) - off, "@%04x", d->ioport_id); - } - - return g_strdup(path); -} - -MemoryRegion *isa_address_space(ISADevice *dev) -{ - if (dev) { - return isa_bus_from_device(dev)->address_space; - } - - return isabus->address_space; -} - -MemoryRegion *isa_address_space_io(ISADevice *dev) -{ - if (dev) { - return isa_bus_from_device(dev)->address_space_io; - } - - return isabus->address_space_io; -} - -type_init(isabus_register_types) - -static void parallel_init(ISABus *bus, int index, CharDriverState *chr) -{ - DeviceState *dev; - ISADevice *isadev; - - isadev = isa_create(bus, "isa-parallel"); - dev = DEVICE(isadev); - qdev_prop_set_uint32(dev, "index", index); - qdev_prop_set_chr(dev, "chardev", chr); - qdev_init_nofail(dev); -} - -void parallel_hds_isa_init(ISABus *bus, int n) -{ - int i; - - assert(n <= MAX_PARALLEL_PORTS); - - for (i = 0; i < n; i++) { - if (parallel_hds[i]) { - parallel_init(bus, i, parallel_hds[i]); - } - } -} diff --git a/qemu/hw/isa/lpc_ich9.c b/qemu/hw/isa/lpc_ich9.c deleted file mode 100644 index 99cd3ba9e..000000000 --- a/qemu/hw/isa/lpc_ich9.c +++ /dev/null @@ -1,752 +0,0 @@ -/* - * QEMU ICH9 Emulation - * - * Copyright (c) 2006 Fabrice Bellard - * Copyright (c) 2009, 2010, 2011 - * Isaku Yamahata - * VA Linux Systems Japan K.K. - * Copyright (C) 2012 Jason Baron - * - * This is based on piix.c, but heavily modified. - * - * 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 "qemu-common.h" -#include "cpu.h" -#include "hw/hw.h" -#include "qapi/visitor.h" -#include "qemu/range.h" -#include "hw/isa/isa.h" -#include "hw/sysbus.h" -#include "hw/i386/pc.h" -#include "hw/isa/apm.h" -#include "hw/i386/ioapic.h" -#include "hw/pci/pci.h" -#include "hw/pci/pcie_host.h" -#include "hw/pci/pci_bridge.h" -#include "hw/i386/ich9.h" -#include "hw/acpi/acpi.h" -#include "hw/acpi/ich9.h" -#include "hw/pci/pci_bus.h" -#include "exec/address-spaces.h" -#include "sysemu/sysemu.h" - -static int ich9_lpc_sci_irq(ICH9LPCState *lpc); - -/*****************************************************************************/ -/* ICH9 LPC PCI to ISA bridge */ - -static void ich9_lpc_reset(DeviceState *qdev); - -/* chipset configuration register - * to access chipset configuration registers, pci_[sg]et_{byte, word, long} - * are used. - * Although it's not pci configuration space, it's little endian as Intel. - */ - -static void ich9_cc_update_ir(uint8_t irr[PCI_NUM_PINS], uint16_t ir) -{ - int intx; - for (intx = 0; intx < PCI_NUM_PINS; intx++) { - irr[intx] = (ir >> (intx * ICH9_CC_DIR_SHIFT)) & ICH9_CC_DIR_MASK; - } -} - -static void ich9_cc_update(ICH9LPCState *lpc) -{ - int slot; - int pci_intx; - - const int reg_offsets[] = { - ICH9_CC_D25IR, - ICH9_CC_D26IR, - ICH9_CC_D27IR, - ICH9_CC_D28IR, - ICH9_CC_D29IR, - ICH9_CC_D30IR, - ICH9_CC_D31IR, - }; - const int *offset; - - /* D{25 - 31}IR, but D30IR is read only to 0. */ - for (slot = 25, offset = reg_offsets; slot < 32; slot++, offset++) { - if (slot == 30) { - continue; - } - ich9_cc_update_ir(lpc->irr[slot], - pci_get_word(lpc->chip_config + *offset)); - } - - /* - * D30: DMI2PCI bridge - * It is arbitrarily decided how INTx lines of PCI devicesbehind the bridge - * are connected to pirq lines. Our choice is PIRQ[E-H]. - * INT[A-D] are connected to PIRQ[E-H] - */ - for (pci_intx = 0; pci_intx < PCI_NUM_PINS; pci_intx++) { - lpc->irr[30][pci_intx] = pci_intx + 4; - } -} - -static void ich9_cc_init(ICH9LPCState *lpc) -{ - int slot; - int intx; - - /* the default irq routing is arbitrary as long as it matches with - * acpi irq routing table. - * The one that is incompatible with piix_pci(= bochs) one is - * intentionally chosen to let the users know that the different - * board is used. - * - * int[A-D] -> pirq[E-F] - * avoid pirq A-D because they are used for pci express port - */ - for (slot = 0; slot < PCI_SLOT_MAX; slot++) { - for (intx = 0; intx < PCI_NUM_PINS; intx++) { - lpc->irr[slot][intx] = (slot + intx) % 4 + 4; - } - } - ich9_cc_update(lpc); -} - -static void ich9_cc_reset(ICH9LPCState *lpc) -{ - uint8_t *c = lpc->chip_config; - - memset(lpc->chip_config, 0, sizeof(lpc->chip_config)); - - pci_set_long(c + ICH9_CC_D31IR, ICH9_CC_DIR_DEFAULT); - pci_set_long(c + ICH9_CC_D30IR, ICH9_CC_D30IR_DEFAULT); - pci_set_long(c + ICH9_CC_D29IR, ICH9_CC_DIR_DEFAULT); - pci_set_long(c + ICH9_CC_D28IR, ICH9_CC_DIR_DEFAULT); - pci_set_long(c + ICH9_CC_D27IR, ICH9_CC_DIR_DEFAULT); - pci_set_long(c + ICH9_CC_D26IR, ICH9_CC_DIR_DEFAULT); - pci_set_long(c + ICH9_CC_D25IR, ICH9_CC_DIR_DEFAULT); - pci_set_long(c + ICH9_CC_GCS, ICH9_CC_GCS_DEFAULT); - - ich9_cc_update(lpc); -} - -static void ich9_cc_addr_len(uint64_t *addr, unsigned *len) -{ - *addr &= ICH9_CC_ADDR_MASK; - if (*addr + *len >= ICH9_CC_SIZE) { - *len = ICH9_CC_SIZE - *addr; - } -} - -/* val: little endian */ -static void ich9_cc_write(void *opaque, hwaddr addr, - uint64_t val, unsigned len) -{ - ICH9LPCState *lpc = (ICH9LPCState *)opaque; - - ich9_cc_addr_len(&addr, &len); - memcpy(lpc->chip_config + addr, &val, len); - pci_bus_fire_intx_routing_notifier(lpc->d.bus); - ich9_cc_update(lpc); -} - -/* return value: little endian */ -static uint64_t ich9_cc_read(void *opaque, hwaddr addr, - unsigned len) -{ - ICH9LPCState *lpc = (ICH9LPCState *)opaque; - - uint32_t val = 0; - ich9_cc_addr_len(&addr, &len); - memcpy(&val, lpc->chip_config + addr, len); - return val; -} - -/* IRQ routing */ -/* */ -static void ich9_lpc_rout(uint8_t pirq_rout, int *pic_irq, int *pic_dis) -{ - *pic_irq = pirq_rout & ICH9_LPC_PIRQ_ROUT_MASK; - *pic_dis = pirq_rout & ICH9_LPC_PIRQ_ROUT_IRQEN; -} - -static void ich9_lpc_pic_irq(ICH9LPCState *lpc, int pirq_num, - int *pic_irq, int *pic_dis) -{ - switch (pirq_num) { - case 0 ... 3: /* A-D */ - ich9_lpc_rout(lpc->d.config[ICH9_LPC_PIRQA_ROUT + pirq_num], - pic_irq, pic_dis); - return; - case 4 ... 7: /* E-H */ - ich9_lpc_rout(lpc->d.config[ICH9_LPC_PIRQE_ROUT + (pirq_num - 4)], - pic_irq, pic_dis); - return; - default: - break; - } - abort(); -} - -/* pic_irq: i8254 irq 0-15 */ -static void ich9_lpc_update_pic(ICH9LPCState *lpc, int pic_irq) -{ - int i, pic_level; - - /* The pic level is the logical OR of all the PCI irqs mapped to it */ - pic_level = 0; - for (i = 0; i < ICH9_LPC_NB_PIRQS; i++) { - int tmp_irq; - int tmp_dis; - ich9_lpc_pic_irq(lpc, i, &tmp_irq, &tmp_dis); - if (!tmp_dis && pic_irq == tmp_irq) { - pic_level |= pci_bus_get_irq_level(lpc->d.bus, i); - } - } - if (pic_irq == ich9_lpc_sci_irq(lpc)) { - pic_level |= lpc->sci_level; - } - - qemu_set_irq(lpc->pic[pic_irq], pic_level); -} - -/* pirq: pirq[A-H] 0-7*/ -static void ich9_lpc_update_by_pirq(ICH9LPCState *lpc, int pirq) -{ - int pic_irq; - int pic_dis; - - ich9_lpc_pic_irq(lpc, pirq, &pic_irq, &pic_dis); - assert(pic_irq < ICH9_LPC_PIC_NUM_PINS); - if (pic_dis) { - return; - } - - ich9_lpc_update_pic(lpc, pic_irq); -} - -/* APIC mode: GSIx: PIRQ[A-H] -> GSI 16, ... no pirq shares same APIC pins. */ -static int ich9_pirq_to_gsi(int pirq) -{ - return pirq + ICH9_LPC_PIC_NUM_PINS; -} - -static int ich9_gsi_to_pirq(int gsi) -{ - return gsi - ICH9_LPC_PIC_NUM_PINS; -} - -static void ich9_lpc_update_apic(ICH9LPCState *lpc, int gsi) -{ - int level = 0; - - if (gsi >= ICH9_LPC_PIC_NUM_PINS) { - level |= pci_bus_get_irq_level(lpc->d.bus, ich9_gsi_to_pirq(gsi)); - } - if (gsi == ich9_lpc_sci_irq(lpc)) { - level |= lpc->sci_level; - } - - qemu_set_irq(lpc->ioapic[gsi], level); -} - -void ich9_lpc_set_irq(void *opaque, int pirq, int level) -{ - ICH9LPCState *lpc = opaque; - - assert(0 <= pirq); - assert(pirq < ICH9_LPC_NB_PIRQS); - - ich9_lpc_update_apic(lpc, ich9_pirq_to_gsi(pirq)); - ich9_lpc_update_by_pirq(lpc, pirq); -} - -/* return the pirq number (PIRQ[A-H]:0-7) corresponding to - * a given device irq pin. - */ -int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx) -{ - BusState *bus = qdev_get_parent_bus(&pci_dev->qdev); - PCIBus *pci_bus = PCI_BUS(bus); - PCIDevice *lpc_pdev = - pci_bus->devices[PCI_DEVFN(ICH9_LPC_DEV, ICH9_LPC_FUNC)]; - ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pdev); - - return lpc->irr[PCI_SLOT(pci_dev->devfn)][intx]; -} - -PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin) -{ - ICH9LPCState *lpc = opaque; - PCIINTxRoute route; - int pic_irq; - int pic_dis; - - assert(0 <= pirq_pin); - assert(pirq_pin < ICH9_LPC_NB_PIRQS); - - route.mode = PCI_INTX_ENABLED; - ich9_lpc_pic_irq(lpc, pirq_pin, &pic_irq, &pic_dis); - if (!pic_dis) { - if (pic_irq < ICH9_LPC_PIC_NUM_PINS) { - route.irq = pic_irq; - } else { - route.mode = PCI_INTX_DISABLED; - route.irq = -1; - } - } else { - route.irq = ich9_pirq_to_gsi(pirq_pin); - } - - return route; -} - -void ich9_generate_smi(void) -{ - cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI); -} - -void ich9_generate_nmi(void) -{ - cpu_interrupt(first_cpu, CPU_INTERRUPT_NMI); -} - -static int ich9_lpc_sci_irq(ICH9LPCState *lpc) -{ - switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] & - ICH9_LPC_ACPI_CTRL_SCI_IRQ_SEL_MASK) { - case ICH9_LPC_ACPI_CTRL_9: - return 9; - case ICH9_LPC_ACPI_CTRL_10: - return 10; - case ICH9_LPC_ACPI_CTRL_11: - return 11; - case ICH9_LPC_ACPI_CTRL_20: - return 20; - case ICH9_LPC_ACPI_CTRL_21: - return 21; - default: - /* reserved */ - break; - } - return -1; -} - -static void ich9_set_sci(void *opaque, int irq_num, int level) -{ - ICH9LPCState *lpc = opaque; - int irq; - - assert(irq_num == 0); - level = !!level; - if (level == lpc->sci_level) { - return; - } - lpc->sci_level = level; - - irq = ich9_lpc_sci_irq(lpc); - if (irq < 0) { - return; - } - - ich9_lpc_update_apic(lpc, irq); - if (irq < ICH9_LPC_PIC_NUM_PINS) { - ich9_lpc_update_pic(lpc, irq); - } -} - -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, sci_irq); - ich9_lpc_reset(&lpc->d.qdev); -} - -/* APM */ - -static void ich9_apm_ctrl_changed(uint32_t val, void *arg) -{ - ICH9LPCState *lpc = arg; - - /* ACPI specs 3.0, 4.7.2.5 */ - acpi_pm1_cnt_update(&lpc->pm.acpi_regs, - val == ICH9_APM_ACPI_ENABLE, - val == ICH9_APM_ACPI_DISABLE); - if (val == ICH9_APM_ACPI_ENABLE || val == ICH9_APM_ACPI_DISABLE) { - return; - } - - /* SMI_EN = PMBASE + 30. SMI control and enable register */ - if (lpc->pm.smi_en & ICH9_PMIO_SMI_EN_APMC_EN) { - cpu_interrupt(current_cpu, CPU_INTERRUPT_SMI); - } -} - -/* config:PMBASE */ -static void -ich9_lpc_pmbase_update(ICH9LPCState *lpc) -{ - uint32_t pm_io_base = pci_get_long(lpc->d.config + ICH9_LPC_PMBASE); - pm_io_base &= ICH9_LPC_PMBASE_BASE_ADDRESS_MASK; - - ich9_pm_iospace_update(&lpc->pm, pm_io_base); -} - -/* config:RCBA */ -static void ich9_lpc_rcba_update(ICH9LPCState *lpc, uint32_t rcba_old) -{ - uint32_t rcba = pci_get_long(lpc->d.config + ICH9_LPC_RCBA); - - if (rcba_old & ICH9_LPC_RCBA_EN) { - memory_region_del_subregion(get_system_memory(), &lpc->rcrb_mem); - } - if (rcba & ICH9_LPC_RCBA_EN) { - memory_region_add_subregion_overlap(get_system_memory(), - rcba & ICH9_LPC_RCBA_BA_MASK, - &lpc->rcrb_mem, 1); - } -} - -/* config:GEN_PMCON* */ -static void -ich9_lpc_pmcon_update(ICH9LPCState *lpc) -{ - uint16_t gen_pmcon_1 = pci_get_word(lpc->d.config + ICH9_LPC_GEN_PMCON_1); - uint16_t wmask; - - if (gen_pmcon_1 & ICH9_LPC_GEN_PMCON_1_SMI_LOCK) { - wmask = pci_get_word(lpc->d.wmask + ICH9_LPC_GEN_PMCON_1); - wmask &= ~ICH9_LPC_GEN_PMCON_1_SMI_LOCK; - pci_set_word(lpc->d.wmask + ICH9_LPC_GEN_PMCON_1, wmask); - lpc->pm.smi_en_wmask &= ~1; - } -} - -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_RCBA_EN */); - ich9_lpc_pmcon_update(lpc); - return 0; -} - -static void ich9_lpc_config_write(PCIDevice *d, - uint32_t addr, uint32_t val, int len) -{ - ICH9LPCState *lpc = ICH9_LPC_DEVICE(d); - 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, rcba_old); - } - if (ranges_overlap(addr, len, ICH9_LPC_PIRQA_ROUT, 4)) { - pci_bus_fire_intx_routing_notifier(lpc->d.bus); - } - if (ranges_overlap(addr, len, ICH9_LPC_PIRQE_ROUT, 4)) { - pci_bus_fire_intx_routing_notifier(lpc->d.bus); - } - if (ranges_overlap(addr, len, ICH9_LPC_GEN_PMCON_1, 8)) { - ich9_lpc_pmcon_update(lpc); - } -} - -static void ich9_lpc_reset(DeviceState *qdev) -{ - PCIDevice *d = PCI_DEVICE(qdev); - ICH9LPCState *lpc = ICH9_LPC_DEVICE(d); - uint32_t rcba_old = pci_get_long(d->config + ICH9_LPC_RCBA); - int i; - - for (i = 0; i < 4; i++) { - pci_set_byte(d->config + ICH9_LPC_PIRQA_ROUT + i, - ICH9_LPC_PIRQ_ROUT_DEFAULT); - } - for (i = 0; i < 4; i++) { - pci_set_byte(d->config + ICH9_LPC_PIRQE_ROUT + i, - ICH9_LPC_PIRQ_ROUT_DEFAULT); - } - pci_set_byte(d->config + ICH9_LPC_ACPI_CTRL, ICH9_LPC_ACPI_CTRL_DEFAULT); - - pci_set_long(d->config + ICH9_LPC_PMBASE, ICH9_LPC_PMBASE_DEFAULT); - pci_set_long(d->config + ICH9_LPC_RCBA, ICH9_LPC_RCBA_DEFAULT); - - ich9_cc_reset(lpc); - - ich9_lpc_pmbase_update(lpc); - ich9_lpc_rcba_update(lpc, rcba_old); - - lpc->sci_level = 0; - lpc->rst_cnt = 0; -} - -/* 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, -}; - -static void ich9_lpc_machine_ready(Notifier *n, void *opaque) -{ - ICH9LPCState *s = container_of(n, ICH9LPCState, machine_ready); - MemoryRegion *io_as = pci_address_space_io(&s->d); - uint8_t *pci_conf; - - pci_conf = s->d.config; - if (memory_region_present(io_as, 0x3f8)) { - /* com1 */ - pci_conf[0x82] |= 0x01; - } - if (memory_region_present(io_as, 0x2f8)) { - /* com2 */ - pci_conf[0x82] |= 0x02; - } - if (memory_region_present(io_as, 0x378)) { - /* lpt */ - pci_conf[0x82] |= 0x04; - } - if (memory_region_present(io_as, 0x3f2)) { - /* floppy */ - pci_conf[0x82] |= 0x08; - } -} - -/* reset control */ -static void ich9_rst_cnt_write(void *opaque, hwaddr addr, uint64_t val, - unsigned len) -{ - ICH9LPCState *lpc = opaque; - - if (val & 4) { - qemu_system_reset_request(); - return; - } - lpc->rst_cnt = val & 0xA; /* keep FULL_RST (bit 3) and SYS_RST (bit 1) */ -} - -static uint64_t ich9_rst_cnt_read(void *opaque, hwaddr addr, unsigned len) -{ - ICH9LPCState *lpc = opaque; - - return lpc->rst_cnt; -} - -static const MemoryRegionOps ich9_rst_cnt_ops = { - .read = ich9_rst_cnt_read, - .write = ich9_rst_cnt_write, - .endianness = DEVICE_LITTLE_ENDIAN -}; - -Object *ich9_lpc_find(void) -{ - bool ambig; - Object *o = object_resolve_path_type("", TYPE_ICH9_LPC_DEVICE, &ambig); - - if (ambig) { - return NULL; - } - return o; -} - -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, name, &value, errp); -} - -static void ich9_lpc_add_properties(ICH9LPCState *lpc) -{ - static const uint8_t acpi_enable_cmd = ICH9_APM_ACPI_ENABLE; - static const uint8_t acpi_disable_cmd = ICH9_APM_ACPI_DISABLE; - - object_property_add(OBJECT(lpc), ACPI_PM_PROP_SCI_INT, "uint32", - ich9_lpc_get_sci_int, - NULL, NULL, NULL, NULL); - object_property_add_uint8_ptr(OBJECT(lpc), ACPI_PM_PROP_ACPI_ENABLE_CMD, - &acpi_enable_cmd, NULL); - object_property_add_uint8_ptr(OBJECT(lpc), ACPI_PM_PROP_ACPI_DISABLE_CMD, - &acpi_disable_cmd, NULL); - - ich9_pm_add_properties(OBJECT(lpc), &lpc->pm, NULL); -} - -static void ich9_lpc_initfn(Object *obj) -{ - ICH9LPCState *lpc = ICH9_LPC_DEVICE(obj); - - ich9_lpc_add_properties(lpc); -} - -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(), - errp); - if (!isa_bus) { - return; - } - - pci_set_long(d->wmask + ICH9_LPC_PMBASE, - ICH9_LPC_PMBASE_BASE_ADDRESS_MASK); - - memory_region_init_io(&lpc->rcrb_mem, OBJECT(d), &rcrb_mmio_ops, lpc, - "lpc-rcrb-mmio", ICH9_CC_SIZE); - - lpc->isa_bus = isa_bus; - - ich9_cc_init(lpc); - apm_init(d, &lpc->apm, ich9_apm_ctrl_changed, lpc); - - lpc->machine_ready.notify = ich9_lpc_machine_ready; - qemu_add_machine_init_done_notifier(&lpc->machine_ready); - - memory_region_init_io(&lpc->rst_cnt_mem, OBJECT(d), &ich9_rst_cnt_ops, lpc, - "lpc-reset-control", 1); - memory_region_add_subregion_overlap(pci_address_space_io(d), - ICH9_RST_CNT_IOPORT, &lpc->rst_cnt_mem, - 1); -} - -static void ich9_device_plug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); - - ich9_pm_device_plug_cb(&lpc->pm, dev, errp); -} - -static void ich9_device_unplug_request_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); - - ich9_pm_device_unplug_request_cb(&lpc->pm, dev, errp); -} - -static void ich9_device_unplug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); - - ich9_pm_device_unplug_cb(&lpc->pm, dev, errp); -} - -static bool ich9_rst_cnt_needed(void *opaque) -{ - ICH9LPCState *lpc = opaque; - - return (lpc->rst_cnt != 0); -} - -static const VMStateDescription vmstate_ich9_rst_cnt = { - .name = "ICH9LPC/rst_cnt", - .version_id = 1, - .minimum_version_id = 1, - .needed = ich9_rst_cnt_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(rst_cnt, ICH9LPCState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_ich9_lpc = { - .name = "ICH9LPC", - .version_id = 1, - .minimum_version_id = 1, - .post_load = ich9_lpc_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(d, ICH9LPCState), - VMSTATE_STRUCT(apm, ICH9LPCState, 0, vmstate_apm, APMState), - VMSTATE_STRUCT(pm, ICH9LPCState, 0, vmstate_ich9_pm, ICH9LPCPMRegs), - VMSTATE_UINT8_ARRAY(chip_config, ICH9LPCState, ICH9_CC_SIZE), - VMSTATE_UINT32(sci_level, ICH9LPCState), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_ich9_rst_cnt, - NULL - } -}; - -static Property ich9_lpc_properties[] = { - DEFINE_PROP_BOOL("noreboot", ICH9LPCState, pin_strap.spkr_hi, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ich9_lpc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(klass); - - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->reset = ich9_lpc_reset; - k->realize = ich9_lpc_realize; - dc->vmsd = &vmstate_ich9_lpc; - dc->props = ich9_lpc_properties; - k->config_write = ich9_lpc_config_write; - dc->desc = "ICH9 LPC bridge"; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_ICH9_8; - k->revision = ICH9_A2_LPC_REVISION; - k->class_id = PCI_CLASS_BRIDGE_ISA; - /* - * Reason: part of ICH9 southbridge, needs to be wired up by - * pc_q35_init() - */ - dc->cannot_instantiate_with_device_add_yet = true; - hc->plug = ich9_device_plug_cb; - hc->unplug_request = ich9_device_unplug_request_cb; - hc->unplug = ich9_device_unplug_cb; - adevc->ospm_status = ich9_pm_ospm_status; -} - -static const TypeInfo ich9_lpc_info = { - .name = TYPE_ICH9_LPC_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(struct ICH9LPCState), - .instance_init = ich9_lpc_initfn, - .class_init = ich9_lpc_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { TYPE_ACPI_DEVICE_IF }, - { } - } -}; - -static void ich9_lpc_register(void) -{ - type_register_static(&ich9_lpc_info); -} - -type_init(ich9_lpc_register); diff --git a/qemu/hw/isa/pc87312.c b/qemu/hw/isa/pc87312.c deleted file mode 100644 index c3ebf3e7a..000000000 --- a/qemu/hw/isa/pc87312.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * QEMU National Semiconductor PC87312 (Super I/O) - * - * Copyright (c) 2010-2012 Herve Poussineau - * Copyright (c) 2011-2012 Andreas Färber - * - * 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/isa/pc87312.h" -#include "qapi/error.h" -#include "qemu/error-report.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" -#include "sysemu/char.h" -#include "trace.h" - - -#define REG_FER 0 -#define REG_FAR 1 -#define REG_PTR 2 - -#define FER_PARALLEL_EN 0x01 -#define FER_UART1_EN 0x02 -#define FER_UART2_EN 0x04 -#define FER_FDC_EN 0x08 -#define FER_FDC_4 0x10 -#define FER_FDC_ADDR 0x20 -#define FER_IDE_EN 0x40 -#define FER_IDE_ADDR 0x80 - -#define FAR_PARALLEL_ADDR 0x03 -#define FAR_UART1_ADDR 0x0C -#define FAR_UART2_ADDR 0x30 -#define FAR_UART_3_4 0xC0 - -#define PTR_POWER_DOWN 0x01 -#define PTR_CLOCK_DOWN 0x02 -#define PTR_PWDN 0x04 -#define PTR_IRQ_5_7 0x08 -#define PTR_UART1_TEST 0x10 -#define PTR_UART2_TEST 0x20 -#define PTR_LOCK_CONF 0x40 -#define PTR_EPP_MODE 0x80 - - -/* Parallel port */ - -static inline bool is_parallel_enabled(PC87312State *s) -{ - return s->regs[REG_FER] & FER_PARALLEL_EN; -} - -static const uint32_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 }; - -static inline uint32_t get_parallel_iobase(PC87312State *s) -{ - return parallel_base[s->regs[REG_FAR] & FAR_PARALLEL_ADDR]; -} - -static const uint32_t parallel_irq[] = { 5, 7, 5, 0 }; - -static inline uint32_t get_parallel_irq(PC87312State *s) -{ - int idx; - idx = (s->regs[REG_FAR] & FAR_PARALLEL_ADDR); - if (idx == 0) { - return (s->regs[REG_PTR] & PTR_IRQ_5_7) ? 7 : 5; - } else { - return parallel_irq[idx]; - } -} - - -/* UARTs */ - -static const uint32_t uart_base[2][4] = { - { 0x3e8, 0x338, 0x2e8, 0x220 }, - { 0x2e8, 0x238, 0x2e0, 0x228 } -}; - -static inline uint32_t get_uart_iobase(PC87312State *s, int i) -{ - int idx; - idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3; - if (idx == 0) { - return 0x3f8; - } else if (idx == 1) { - return 0x2f8; - } else { - return uart_base[idx & 1][(s->regs[REG_FAR] & FAR_UART_3_4) >> 6]; - } -} - -static inline uint32_t get_uart_irq(PC87312State *s, int i) -{ - int idx; - idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3; - return (idx & 1) ? 3 : 4; -} - -static inline bool is_uart_enabled(PC87312State *s, int i) -{ - return s->regs[REG_FER] & (FER_UART1_EN << i); -} - - -/* Floppy controller */ - -static inline bool is_fdc_enabled(PC87312State *s) -{ - return s->regs[REG_FER] & FER_FDC_EN; -} - -static inline uint32_t get_fdc_iobase(PC87312State *s) -{ - return (s->regs[REG_FER] & FER_FDC_ADDR) ? 0x370 : 0x3f0; -} - - -/* IDE controller */ - -static inline bool is_ide_enabled(PC87312State *s) -{ - return s->regs[REG_FER] & FER_IDE_EN; -} - -static inline uint32_t get_ide_iobase(PC87312State *s) -{ - return (s->regs[REG_FER] & FER_IDE_ADDR) ? 0x170 : 0x1f0; -} - - -static void reconfigure_devices(PC87312State *s) -{ - error_report("pc87312: unsupported device reconfiguration (%02x %02x %02x)", - s->regs[REG_FER], s->regs[REG_FAR], s->regs[REG_PTR]); -} - -static void pc87312_soft_reset(PC87312State *s) -{ - static const uint8_t fer_init[] = { - 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4b, 0x4b, - 0x4b, 0x4b, 0x4b, 0x4b, 0x0f, 0x0f, 0x0f, 0x0f, - 0x49, 0x49, 0x49, 0x49, 0x07, 0x07, 0x07, 0x07, - 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x08, 0x00, - }; - static const uint8_t far_init[] = { - 0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x00, 0x01, - 0x01, 0x09, 0x08, 0x08, 0x10, 0x11, 0x39, 0x24, - 0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x39, 0x24, - 0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x10, 0x10, - }; - static const uint8_t ptr_init[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - }; - - s->read_id_step = 0; - s->selected_index = REG_FER; - - s->regs[REG_FER] = fer_init[s->config & 0x1f]; - s->regs[REG_FAR] = far_init[s->config & 0x1f]; - s->regs[REG_PTR] = ptr_init[s->config & 0x1f]; -} - -static void pc87312_hard_reset(PC87312State *s) -{ - pc87312_soft_reset(s); -} - -static void pc87312_io_write(void *opaque, hwaddr addr, uint64_t val, - unsigned int size) -{ - PC87312State *s = opaque; - - trace_pc87312_io_write(addr, val); - - if ((addr & 1) == 0) { - /* Index register */ - s->read_id_step = 2; - s->selected_index = val; - } else { - /* Data register */ - if (s->selected_index < 3) { - s->regs[s->selected_index] = val; - reconfigure_devices(s); - } - } -} - -static uint64_t pc87312_io_read(void *opaque, hwaddr addr, unsigned int size) -{ - PC87312State *s = opaque; - uint32_t val; - - if ((addr & 1) == 0) { - /* Index register */ - if (s->read_id_step++ == 0) { - val = 0x88; - } else if (s->read_id_step++ == 1) { - val = 0; - } else { - val = s->selected_index; - } - } else { - /* Data register */ - if (s->selected_index < 3) { - val = s->regs[s->selected_index]; - } else { - /* Invalid selected index */ - val = 0; - } - } - - trace_pc87312_io_read(addr, val); - return val; -} - -static const MemoryRegionOps pc87312_io_ops = { - .read = pc87312_io_read, - .write = pc87312_io_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static int pc87312_post_load(void *opaque, int version_id) -{ - PC87312State *s = opaque; - - reconfigure_devices(s); - return 0; -} - -static void pc87312_reset(DeviceState *d) -{ - PC87312State *s = PC87312(d); - - pc87312_soft_reset(s); -} - -static void pc87312_realize(DeviceState *dev, Error **errp) -{ - PC87312State *s; - DeviceState *d; - ISADevice *isa; - ISABus *bus; - CharDriverState *chr; - DriveInfo *drive; - char name[5]; - int i; - - s = PC87312(dev); - isa = ISA_DEVICE(dev); - bus = isa_bus_from_device(isa); - isa_register_ioport(isa, &s->io, s->iobase); - pc87312_hard_reset(s); - - if (is_parallel_enabled(s)) { - /* FIXME use a qdev chardev prop instead of parallel_hds[] */ - chr = parallel_hds[0]; - if (chr == NULL) { - chr = qemu_chr_new("par0", "null", NULL); - } - isa = isa_create(bus, "isa-parallel"); - d = DEVICE(isa); - qdev_prop_set_uint32(d, "index", 0); - qdev_prop_set_uint32(d, "iobase", get_parallel_iobase(s)); - qdev_prop_set_uint32(d, "irq", get_parallel_irq(s)); - qdev_prop_set_chr(d, "chardev", chr); - qdev_init_nofail(d); - s->parallel.dev = isa; - trace_pc87312_info_parallel(get_parallel_iobase(s), - get_parallel_irq(s)); - } - - for (i = 0; i < 2; i++) { - if (is_uart_enabled(s, i)) { - /* FIXME use a qdev chardev prop instead of serial_hds[] */ - chr = serial_hds[i]; - if (chr == NULL) { - snprintf(name, sizeof(name), "ser%d", i); - chr = qemu_chr_new(name, "null", NULL); - } - isa = isa_create(bus, "isa-serial"); - d = DEVICE(isa); - qdev_prop_set_uint32(d, "index", i); - qdev_prop_set_uint32(d, "iobase", get_uart_iobase(s, i)); - qdev_prop_set_uint32(d, "irq", get_uart_irq(s, i)); - qdev_prop_set_chr(d, "chardev", chr); - qdev_init_nofail(d); - s->uart[i].dev = isa; - trace_pc87312_info_serial(i, get_uart_iobase(s, i), - get_uart_irq(s, i)); - } - } - - if (is_fdc_enabled(s)) { - isa = isa_create(bus, "isa-fdc"); - d = DEVICE(isa); - qdev_prop_set_uint32(d, "iobase", get_fdc_iobase(s)); - qdev_prop_set_uint32(d, "irq", 6); - /* FIXME use a qdev drive property instead of drive_get() */ - drive = drive_get(IF_FLOPPY, 0, 0); - if (drive != NULL) { - 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(d, "driveB", blk_by_legacy_dinfo(drive), - &error_fatal); - } - qdev_init_nofail(d); - s->fdc.dev = isa; - trace_pc87312_info_floppy(get_fdc_iobase(s)); - } - - if (is_ide_enabled(s)) { - isa = isa_create(bus, "isa-ide"); - d = DEVICE(isa); - qdev_prop_set_uint32(d, "iobase", get_ide_iobase(s)); - qdev_prop_set_uint32(d, "iobase2", get_ide_iobase(s) + 0x206); - qdev_prop_set_uint32(d, "irq", 14); - qdev_init_nofail(d); - s->ide.dev = isa; - trace_pc87312_info_ide(get_ide_iobase(s)); - } -} - -static void pc87312_initfn(Object *obj) -{ - PC87312State *s = PC87312(obj); - - memory_region_init_io(&s->io, obj, &pc87312_io_ops, s, "pc87312", 2); -} - -static const VMStateDescription vmstate_pc87312 = { - .name = "pc87312", - .version_id = 1, - .minimum_version_id = 1, - .post_load = pc87312_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(read_id_step, PC87312State), - VMSTATE_UINT8(selected_index, PC87312State), - VMSTATE_UINT8_ARRAY(regs, PC87312State, 3), - VMSTATE_END_OF_LIST() - } -}; - -static Property pc87312_properties[] = { - DEFINE_PROP_UINT32("iobase", PC87312State, iobase, 0x398), - DEFINE_PROP_UINT8("config", PC87312State, config, 1), - DEFINE_PROP_END_OF_LIST() -}; - -static void pc87312_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = pc87312_realize; - dc->reset = pc87312_reset; - dc->vmsd = &vmstate_pc87312; - dc->props = pc87312_properties; -} - -static const TypeInfo pc87312_type_info = { - .name = TYPE_PC87312, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(PC87312State), - .instance_init = pc87312_initfn, - .class_init = pc87312_class_init, -}; - -static void pc87312_register_types(void) -{ - type_register_static(&pc87312_type_info); -} - -type_init(pc87312_register_types) diff --git a/qemu/hw/isa/piix4.c b/qemu/hw/isa/piix4.c deleted file mode 100644 index 5500fcc4d..000000000 --- a/qemu/hw/isa/piix4.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * QEMU PIIX4 PCI Bridge Emulation - * - * Copyright (c) 2006 Fabrice Bellard - * - * 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/i386/pc.h" -#include "hw/pci/pci.h" -#include "hw/isa/isa.h" -#include "hw/sysbus.h" - -PCIDevice *piix4_dev; - -typedef struct PIIX4State { - PCIDevice dev; -} PIIX4State; - -#define TYPE_PIIX4_PCI_DEVICE "PIIX4" -#define PIIX4_PCI_DEVICE(obj) \ - OBJECT_CHECK(PIIX4State, (obj), TYPE_PIIX4_PCI_DEVICE) - -static void piix4_reset(void *opaque) -{ - PIIX4State *d = opaque; - uint8_t *pci_conf = d->dev.config; - - pci_conf[0x04] = 0x07; // master, memory and I/O - pci_conf[0x05] = 0x00; - pci_conf[0x06] = 0x00; - pci_conf[0x07] = 0x02; // PCI_status_devsel_medium - pci_conf[0x4c] = 0x4d; - pci_conf[0x4e] = 0x03; - pci_conf[0x4f] = 0x00; - pci_conf[0x60] = 0x0a; // PCI A -> IRQ 10 - pci_conf[0x61] = 0x0a; // PCI B -> IRQ 10 - pci_conf[0x62] = 0x0b; // PCI C -> IRQ 11 - pci_conf[0x63] = 0x0b; // PCI D -> IRQ 11 - pci_conf[0x69] = 0x02; - pci_conf[0x70] = 0x80; - pci_conf[0x76] = 0x0c; - pci_conf[0x77] = 0x0c; - pci_conf[0x78] = 0x02; - pci_conf[0x79] = 0x00; - pci_conf[0x80] = 0x00; - pci_conf[0x82] = 0x00; - pci_conf[0xa0] = 0x08; - pci_conf[0xa2] = 0x00; - pci_conf[0xa3] = 0x00; - pci_conf[0xa4] = 0x00; - pci_conf[0xa5] = 0x00; - pci_conf[0xa6] = 0x00; - pci_conf[0xa7] = 0x00; - pci_conf[0xa8] = 0x0f; - pci_conf[0xaa] = 0x00; - pci_conf[0xab] = 0x00; - pci_conf[0xac] = 0x00; - pci_conf[0xae] = 0x00; -} - -static const VMStateDescription vmstate_piix4 = { - .name = "PIIX4", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, PIIX4State), - VMSTATE_END_OF_LIST() - } -}; - -static void piix4_realize(PCIDevice *dev, Error **errp) -{ - PIIX4State *d = PIIX4_PCI_DEVICE(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); -} - -int piix4_init(PCIBus *bus, ISABus **isa_bus, int devfn) -{ - PCIDevice *d; - - d = pci_create_simple_multifunction(bus, devfn, true, "PIIX4"); - *isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(d), "isa.0")); - return d->devfn; -} - -static void piix4_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = piix4_realize; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82371AB_0; - k->class_id = PCI_CLASS_BRIDGE_ISA; - dc->desc = "ISA bridge"; - dc->vmsd = &vmstate_piix4; - /* - * Reason: part of PIIX4 southbridge, needs to be wired up, - * e.g. by mips_malta_init() - */ - dc->cannot_instantiate_with_device_add_yet = true; - dc->hotpluggable = false; -} - -static const TypeInfo piix4_info = { - .name = TYPE_PIIX4_PCI_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PIIX4State), - .class_init = piix4_class_init, -}; - -static void piix4_register_types(void) -{ - type_register_static(&piix4_info); -} - -type_init(piix4_register_types) diff --git a/qemu/hw/isa/vt82c686.c b/qemu/hw/isa/vt82c686.c deleted file mode 100644 index 41d5254f8..000000000 --- a/qemu/hw/isa/vt82c686.c +++ /dev/null @@ -1,515 +0,0 @@ -/* - * VT82C686B south bridge support - * - * Copyright (c) 2008 yajin (yajin@vm-kernel.org) - * Copyright (c) 2009 chenming (chenming@rdc.faw.com.cn) - * Copyright (c) 2010 Huacai Chen (zltjiangshi@gmail.com) - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/i386/pc.h" -#include "hw/isa/vt82c686.h" -#include "hw/i2c/i2c.h" -#include "hw/i2c/smbus.h" -#include "hw/pci/pci.h" -#include "hw/isa/isa.h" -#include "hw/sysbus.h" -#include "hw/mips/mips.h" -#include "hw/isa/apm.h" -#include "hw/acpi/acpi.h" -#include "hw/i2c/pm_smbus.h" -#include "sysemu/sysemu.h" -#include "qemu/timer.h" -#include "exec/address-spaces.h" - -//#define DEBUG_VT82C686B - -#ifdef DEBUG_VT82C686B -#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__) -#else -#define DPRINTF(fmt, ...) -#endif - -typedef struct SuperIOConfig -{ - uint8_t config[0x100]; - uint8_t index; - uint8_t data; -} SuperIOConfig; - -typedef struct VT82C686BState { - PCIDevice dev; - MemoryRegion superio; - SuperIOConfig superio_conf; -} VT82C686BState; - -#define TYPE_VT82C686B_DEVICE "VT82C686B" -#define VT82C686B_DEVICE(obj) \ - OBJECT_CHECK(VT82C686BState, (obj), TYPE_VT82C686B_DEVICE) - -static void superio_ioport_writeb(void *opaque, hwaddr addr, uint64_t data, - unsigned size) -{ - SuperIOConfig *superio_conf = opaque; - - DPRINTF("superio_ioport_writeb address 0x%x val 0x%x\n", addr, data); - if (addr == 0x3f0) { - superio_conf->index = data & 0xff; - } else { - bool can_write = true; - /* 0x3f1 */ - switch (superio_conf->index) { - case 0x00 ... 0xdf: - case 0xe4: - case 0xe5: - case 0xe9 ... 0xed: - case 0xf3: - case 0xf5: - case 0xf7: - case 0xf9 ... 0xfb: - case 0xfd ... 0xff: - can_write = false; - break; - case 0xe7: - if ((data & 0xff) != 0xfe) { - DPRINTF("change uart 1 base. unsupported yet\n"); - can_write = false; - } - break; - case 0xe8: - if ((data & 0xff) != 0xbe) { - DPRINTF("change uart 2 base. unsupported yet\n"); - can_write = false; - } - break; - default: - break; - - } - if (can_write) { - superio_conf->config[superio_conf->index] = data & 0xff; - } - } -} - -static uint64_t superio_ioport_readb(void *opaque, hwaddr addr, unsigned size) -{ - SuperIOConfig *superio_conf = opaque; - - DPRINTF("superio_ioport_readb address 0x%x\n", addr); - return (superio_conf->config[superio_conf->index]); -} - -static const MemoryRegionOps superio_ops = { - .read = superio_ioport_readb, - .write = superio_ioport_writeb, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void vt82c686b_reset(void * opaque) -{ - PCIDevice *d = opaque; - uint8_t *pci_conf = d->config; - VT82C686BState *vt82c = VT82C686B_DEVICE(d); - - pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0); - pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER | PCI_COMMAND_SPECIAL); - pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM); - - pci_conf[0x48] = 0x01; /* Miscellaneous Control 3 */ - pci_conf[0x4a] = 0x04; /* IDE interrupt Routing */ - pci_conf[0x4f] = 0x03; /* DMA/Master Mem Access Control 3 */ - pci_conf[0x50] = 0x2d; /* PnP DMA Request Control */ - pci_conf[0x59] = 0x04; - pci_conf[0x5a] = 0x04; /* KBC/RTC Control*/ - pci_conf[0x5f] = 0x04; - pci_conf[0x77] = 0x10; /* GPIO Control 1/2/3/4 */ - - vt82c->superio_conf.config[0xe0] = 0x3c; - vt82c->superio_conf.config[0xe2] = 0x03; - vt82c->superio_conf.config[0xe3] = 0xfc; - vt82c->superio_conf.config[0xe6] = 0xde; - vt82c->superio_conf.config[0xe7] = 0xfe; - vt82c->superio_conf.config[0xe8] = 0xbe; -} - -/* write config pci function0 registers. PCI-ISA bridge */ -static void vt82c686b_write_config(PCIDevice * d, uint32_t address, - uint32_t val, int len) -{ - VT82C686BState *vt686 = VT82C686B_DEVICE(d); - - DPRINTF("vt82c686b_write_config address 0x%x val 0x%x len 0x%x\n", - address, val, len); - - pci_default_write_config(d, address, val, len); - if (address == 0x85) { /* enable or disable super IO configure */ - memory_region_set_enabled(&vt686->superio, val & 0x2); - } -} - -#define ACPI_DBG_IO_ADDR 0xb044 - -typedef struct VT686PMState { - PCIDevice dev; - MemoryRegion io; - ACPIREGS ar; - APMState apm; - PMSMBus smb; - uint32_t smb_io_base; -} VT686PMState; - -typedef struct VT686AC97State { - PCIDevice dev; -} VT686AC97State; - -typedef struct VT686MC97State { - PCIDevice dev; -} VT686MC97State; - -#define TYPE_VT82C686B_PM_DEVICE "VT82C686B_PM" -#define VT82C686B_PM_DEVICE(obj) \ - OBJECT_CHECK(VT686PMState, (obj), TYPE_VT82C686B_PM_DEVICE) - -#define TYPE_VT82C686B_MC97_DEVICE "VT82C686B_MC97" -#define VT82C686B_MC97_DEVICE(obj) \ - OBJECT_CHECK(VT686MC97State, (obj), TYPE_VT82C686B_MC97_DEVICE) - -#define TYPE_VT82C686B_AC97_DEVICE "VT82C686B_AC97" -#define VT82C686B_AC97_DEVICE(obj) \ - OBJECT_CHECK(VT686AC97State, (obj), TYPE_VT82C686B_AC97_DEVICE) - -static void pm_update_sci(VT686PMState *s) -{ - int sci_level, pmsts; - - pmsts = acpi_pm1_evt_get_sts(&s->ar); - sci_level = (((pmsts & s->ar.pm1.evt.en) & - (ACPI_BITMASK_RT_CLOCK_ENABLE | - ACPI_BITMASK_POWER_BUTTON_ENABLE | - ACPI_BITMASK_GLOBAL_LOCK_ENABLE | - ACPI_BITMASK_TIMER_ENABLE)) != 0); - pci_set_irq(&s->dev, sci_level); - /* schedule a timer interruption if needed */ - acpi_pm_tmr_update(&s->ar, (s->ar.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) && - !(pmsts & ACPI_BITMASK_TIMER_STATUS)); -} - -static void pm_tmr_timer(ACPIREGS *ar) -{ - VT686PMState *s = container_of(ar, VT686PMState, ar); - pm_update_sci(s); -} - -static void pm_io_space_update(VT686PMState *s) -{ - uint32_t pm_io_base; - - pm_io_base = pci_get_long(s->dev.config + 0x40); - pm_io_base &= 0xffc0; - - memory_region_transaction_begin(); - memory_region_set_enabled(&s->io, s->dev.config[0x80] & 1); - memory_region_set_address(&s->io, pm_io_base); - memory_region_transaction_commit(); -} - -static void pm_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len) -{ - DPRINTF("pm_write_config address 0x%x val 0x%x len 0x%x\n", - address, val, len); - pci_default_write_config(d, address, val, len); -} - -static int vmstate_acpi_post_load(void *opaque, int version_id) -{ - VT686PMState *s = opaque; - - pm_io_space_update(s); - return 0; -} - -static const VMStateDescription vmstate_acpi = { - .name = "vt82c686b_pm", - .version_id = 1, - .minimum_version_id = 1, - .post_load = vmstate_acpi_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, VT686PMState), - VMSTATE_UINT16(ar.pm1.evt.sts, VT686PMState), - VMSTATE_UINT16(ar.pm1.evt.en, VT686PMState), - VMSTATE_UINT16(ar.pm1.cnt.cnt, VT686PMState), - VMSTATE_STRUCT(apm, VT686PMState, 0, vmstate_apm, APMState), - VMSTATE_TIMER_PTR(ar.tmr.timer, VT686PMState), - VMSTATE_INT64(ar.tmr.overflow_time, VT686PMState), - VMSTATE_END_OF_LIST() - } -}; - -/* - * TODO: vt82c686b_ac97_init() and vt82c686b_mc97_init() - * just register a PCI device now, functionalities will be implemented later. - */ - -static void vt82c686b_ac97_realize(PCIDevice *dev, Error **errp) -{ - VT686AC97State *s = VT82C686B_AC97_DEVICE(dev); - uint8_t *pci_conf = s->dev.config; - - pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_INVALIDATE | - PCI_COMMAND_PARITY); - pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_CAP_LIST | - PCI_STATUS_DEVSEL_MEDIUM); - pci_set_long(pci_conf + PCI_INTERRUPT_PIN, 0x03); -} - -void vt82c686b_ac97_init(PCIBus *bus, int devfn) -{ - PCIDevice *dev; - - dev = pci_create(bus, devfn, TYPE_VT82C686B_AC97_DEVICE); - qdev_init_nofail(&dev->qdev); -} - -static void via_ac97_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = vt82c686b_ac97_realize; - k->vendor_id = PCI_VENDOR_ID_VIA; - k->device_id = PCI_DEVICE_ID_VIA_AC97; - k->revision = 0x50; - k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO; - set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - dc->desc = "AC97"; -} - -static const TypeInfo via_ac97_info = { - .name = TYPE_VT82C686B_AC97_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VT686AC97State), - .class_init = via_ac97_class_init, -}; - -static void vt82c686b_mc97_realize(PCIDevice *dev, Error **errp) -{ - VT686MC97State *s = VT82C686B_MC97_DEVICE(dev); - uint8_t *pci_conf = s->dev.config; - - pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_INVALIDATE | - PCI_COMMAND_VGA_PALETTE); - pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM); - pci_set_long(pci_conf + PCI_INTERRUPT_PIN, 0x03); -} - -void vt82c686b_mc97_init(PCIBus *bus, int devfn) -{ - PCIDevice *dev; - - dev = pci_create(bus, devfn, TYPE_VT82C686B_MC97_DEVICE); - qdev_init_nofail(&dev->qdev); -} - -static void via_mc97_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = vt82c686b_mc97_realize; - k->vendor_id = PCI_VENDOR_ID_VIA; - k->device_id = PCI_DEVICE_ID_VIA_MC97; - k->class_id = PCI_CLASS_COMMUNICATION_OTHER; - k->revision = 0x30; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->desc = "MC97"; -} - -static const TypeInfo via_mc97_info = { - .name = TYPE_VT82C686B_MC97_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VT686MC97State), - .class_init = via_mc97_class_init, -}; - -/* vt82c686 pm init */ -static void vt82c686b_pm_realize(PCIDevice *dev, Error **errp) -{ - VT686PMState *s = VT82C686B_PM_DEVICE(dev); - uint8_t *pci_conf; - - pci_conf = s->dev.config; - pci_set_word(pci_conf + PCI_COMMAND, 0); - pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK | - PCI_STATUS_DEVSEL_MEDIUM); - - /* 0x48-0x4B is Power Management I/O Base */ - pci_set_long(pci_conf + 0x48, 0x00000001); - - /* SMB ports:0xeee0~0xeeef */ - s->smb_io_base =((s->smb_io_base & 0xfff0) + 0x0); - pci_conf[0x90] = s->smb_io_base | 1; - pci_conf[0x91] = s->smb_io_base >> 8; - pci_conf[0xd2] = 0x90; - pm_smbus_init(&s->dev.qdev, &s->smb); - memory_region_add_subregion(get_system_io(), s->smb_io_base, &s->smb.io); - - apm_init(dev, &s->apm, NULL, s); - - memory_region_init(&s->io, OBJECT(dev), "vt82c686-pm", 64); - memory_region_set_enabled(&s->io, false); - memory_region_add_subregion(get_system_io(), 0, &s->io); - - acpi_pm_tmr_init(&s->ar, pm_tmr_timer, &s->io); - acpi_pm1_evt_init(&s->ar, pm_tmr_timer, &s->io); - acpi_pm1_cnt_init(&s->ar, &s->io, false, false, 2); -} - -I2CBus *vt82c686b_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, - qemu_irq sci_irq) -{ - PCIDevice *dev; - VT686PMState *s; - - dev = pci_create(bus, devfn, TYPE_VT82C686B_PM_DEVICE); - qdev_prop_set_uint32(&dev->qdev, "smb_io_base", smb_io_base); - - s = VT82C686B_PM_DEVICE(dev); - - qdev_init_nofail(&dev->qdev); - - return s->smb.smbus; -} - -static Property via_pm_properties[] = { - DEFINE_PROP_UINT32("smb_io_base", VT686PMState, smb_io_base, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void via_pm_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = vt82c686b_pm_realize; - k->config_write = pm_write_config; - k->vendor_id = PCI_VENDOR_ID_VIA; - k->device_id = PCI_DEVICE_ID_VIA_ACPI; - k->class_id = PCI_CLASS_BRIDGE_OTHER; - k->revision = 0x40; - dc->desc = "PM"; - dc->vmsd = &vmstate_acpi; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->props = via_pm_properties; -} - -static const TypeInfo via_pm_info = { - .name = TYPE_VT82C686B_PM_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VT686PMState), - .class_init = via_pm_class_init, -}; - -static const VMStateDescription vmstate_via = { - .name = "vt82c686b", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, VT82C686BState), - VMSTATE_END_OF_LIST() - } -}; - -/* init the PCI-to-ISA bridge */ -static void vt82c686b_realize(PCIDevice *d, Error **errp) -{ - VT82C686BState *vt82c = VT82C686B_DEVICE(d); - uint8_t *pci_conf; - ISABus *isa_bus; - uint8_t *wmask; - int i; - - isa_bus = isa_bus_new(DEVICE(d), get_system_memory(), - pci_address_space_io(d), errp); - if (!isa_bus) { - return; - } - - pci_conf = d->config; - pci_config_set_prog_interface(pci_conf, 0x0); - - wmask = d->wmask; - for (i = 0x00; i < 0xff; i++) { - if (i<=0x03 || (i>=0x08 && i<=0x3f)) { - wmask[i] = 0x00; - } - } - - memory_region_init_io(&vt82c->superio, OBJECT(d), &superio_ops, - &vt82c->superio_conf, "superio", 2); - memory_region_set_enabled(&vt82c->superio, false); - /* The floppy also uses 0x3f0 and 0x3f1. - * But we do not emulate a floppy, so just set it here. */ - memory_region_add_subregion(isa_bus->address_space_io, 0x3f0, - &vt82c->superio); - - qemu_register_reset(vt82c686b_reset, d); -} - -ISABus *vt82c686b_init(PCIBus *bus, int devfn) -{ - PCIDevice *d; - - d = pci_create_simple_multifunction(bus, devfn, true, - TYPE_VT82C686B_DEVICE); - - return ISA_BUS(qdev_get_child_bus(DEVICE(d), "isa.0")); -} - -static void via_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = vt82c686b_realize; - k->config_write = vt82c686b_write_config; - k->vendor_id = PCI_VENDOR_ID_VIA; - k->device_id = PCI_DEVICE_ID_VIA_ISA_BRIDGE; - k->class_id = PCI_CLASS_BRIDGE_ISA; - k->revision = 0x40; - dc->desc = "ISA bridge"; - dc->vmsd = &vmstate_via; - /* - * Reason: part of VIA VT82C686 southbridge, needs to be wired up, - * e.g. by mips_fulong2e_init() - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo via_info = { - .name = TYPE_VT82C686B_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VT82C686BState), - .class_init = via_class_init, -}; - -static void vt82c686b_register_types(void) -{ - type_register_static(&via_ac97_info); - type_register_static(&via_mc97_info); - type_register_static(&via_pm_info); - type_register_static(&via_info); -} - -type_init(vt82c686b_register_types) diff --git a/qemu/hw/lm32/Makefile.objs b/qemu/hw/lm32/Makefile.objs deleted file mode 100644 index ea6418ae5..000000000 --- a/qemu/hw/lm32/Makefile.objs +++ /dev/null @@ -1,3 +0,0 @@ -# LM32 boards -obj-y += lm32_boards.o -obj-y += milkymist.o diff --git a/qemu/hw/lm32/lm32.h b/qemu/hw/lm32/lm32.h deleted file mode 100644 index 18aa6fdc1..000000000 --- a/qemu/hw/lm32/lm32.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef HW_LM32_H -#define HW_LM32_H 1 - -#include "hw/char/lm32_juart.h" - -static inline DeviceState *lm32_pic_init(qemu_irq cpu_irq) -{ - DeviceState *dev; - SysBusDevice *d; - - dev = qdev_create(NULL, "lm32-pic"); - qdev_init_nofail(dev); - d = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(d, 0, cpu_irq); - - return dev; -} - -static inline DeviceState *lm32_juart_init(void) -{ - DeviceState *dev; - - dev = qdev_create(NULL, TYPE_LM32_JUART); - qdev_init_nofail(dev); - - return dev; -} - -#endif diff --git a/qemu/hw/lm32/lm32_boards.c b/qemu/hw/lm32/lm32_boards.c deleted file mode 100644 index c0290560f..000000000 --- a/qemu/hw/lm32/lm32_boards.c +++ /dev/null @@ -1,334 +0,0 @@ -/* - * QEMU models for LatticeMico32 uclinux and evr32 boards. - * - * Copyright (c) 2010 Michael Walle - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/sysbus.h" -#include "hw/hw.h" -#include "hw/block/flash.h" -#include "hw/devices.h" -#include "hw/boards.h" -#include "hw/loader.h" -#include "sysemu/block-backend.h" -#include "elf.h" -#include "lm32_hwsetup.h" -#include "lm32.h" -#include "exec/address-spaces.h" - -typedef struct { - LM32CPU *cpu; - hwaddr bootstrap_pc; - hwaddr flash_base; - hwaddr hwsetup_base; - hwaddr initrd_base; - size_t initrd_size; - hwaddr cmdline_base; -} ResetInfo; - -static void cpu_irq_handler(void *opaque, int irq, int level) -{ - LM32CPU *cpu = opaque; - CPUState *cs = CPU(cpu); - - if (level) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } -} - -static void main_cpu_reset(void *opaque) -{ - ResetInfo *reset_info = opaque; - CPULM32State *env = &reset_info->cpu->env; - - cpu_reset(CPU(reset_info->cpu)); - - /* init defaults */ - env->pc = (uint32_t)reset_info->bootstrap_pc; - env->regs[R_R1] = (uint32_t)reset_info->hwsetup_base; - env->regs[R_R2] = (uint32_t)reset_info->cmdline_base; - env->regs[R_R3] = (uint32_t)reset_info->initrd_base; - env->regs[R_R4] = (uint32_t)(reset_info->initrd_base + - reset_info->initrd_size); - env->eba = reset_info->flash_base; - env->deba = reset_info->flash_base; -} - -static void lm32_evr_init(MachineState *machine) -{ - const char *cpu_model = machine->cpu_model; - const char *kernel_filename = machine->kernel_filename; - LM32CPU *cpu; - CPULM32State *env; - DriveInfo *dinfo; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *phys_ram = g_new(MemoryRegion, 1); - qemu_irq irq[32]; - ResetInfo *reset_info; - int i; - - /* memory map */ - hwaddr flash_base = 0x04000000; - size_t flash_sector_size = 256 * 1024; - size_t flash_size = 32 * 1024 * 1024; - hwaddr ram_base = 0x08000000; - size_t ram_size = 64 * 1024 * 1024; - hwaddr timer0_base = 0x80002000; - hwaddr uart0_base = 0x80006000; - hwaddr timer1_base = 0x8000a000; - int uart0_irq = 0; - int timer0_irq = 1; - int timer1_irq = 3; - - reset_info = g_malloc0(sizeof(ResetInfo)); - - if (cpu_model == NULL) { - cpu_model = "lm32-full"; - } - cpu = cpu_lm32_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "qemu: unable to find CPU '%s'\n", cpu_model); - exit(1); - } - - env = &cpu->env; - reset_info->cpu = cpu; - - reset_info->flash_base = flash_base; - - memory_region_allocate_system_memory(phys_ram, NULL, "lm32_evr.sdram", - ram_size); - memory_region_add_subregion(address_space_mem, ram_base, phys_ram); - - dinfo = drive_get(IF_PFLASH, 0, 0); - /* Spansion S29NS128P */ - pflash_cfi02_register(flash_base, NULL, "lm32_evr.flash", flash_size, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - flash_sector_size, flash_size / flash_sector_size, - 1, 2, 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1); - - /* create irq lines */ - env->pic_state = lm32_pic_init(qemu_allocate_irq(cpu_irq_handler, cpu, 0)); - for (i = 0; i < 32; i++) { - irq[i] = qdev_get_gpio_in(env->pic_state, i); - } - - sysbus_create_simple("lm32-uart", uart0_base, irq[uart0_irq]); - sysbus_create_simple("lm32-timer", timer0_base, irq[timer0_irq]); - sysbus_create_simple("lm32-timer", timer1_base, irq[timer1_irq]); - - /* make sure juart isn't the first chardev */ - env->juart_state = lm32_juart_init(); - - reset_info->bootstrap_pc = flash_base; - - if (kernel_filename) { - uint64_t entry; - int kernel_size; - - kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, - 1, EM_LATTICEMICO32, 0, 0); - reset_info->bootstrap_pc = entry; - - if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, ram_base, - ram_size); - reset_info->bootstrap_pc = ram_base; - } - - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); - exit(1); - } - } - - qemu_register_reset(main_cpu_reset, reset_info); -} - -static void lm32_uclinux_init(MachineState *machine) -{ - 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; - LM32CPU *cpu; - CPULM32State *env; - DriveInfo *dinfo; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *phys_ram = g_new(MemoryRegion, 1); - qemu_irq irq[32]; - HWSetup *hw; - ResetInfo *reset_info; - int i; - - /* memory map */ - hwaddr flash_base = 0x04000000; - size_t flash_sector_size = 256 * 1024; - size_t flash_size = 32 * 1024 * 1024; - hwaddr ram_base = 0x08000000; - size_t ram_size = 64 * 1024 * 1024; - hwaddr uart0_base = 0x80000000; - hwaddr timer0_base = 0x80002000; - hwaddr timer1_base = 0x80010000; - hwaddr timer2_base = 0x80012000; - int uart0_irq = 0; - int timer0_irq = 1; - int timer1_irq = 20; - int timer2_irq = 21; - hwaddr hwsetup_base = 0x0bffe000; - hwaddr cmdline_base = 0x0bfff000; - hwaddr initrd_base = 0x08400000; - size_t initrd_max = 0x01000000; - - reset_info = g_malloc0(sizeof(ResetInfo)); - - if (cpu_model == NULL) { - cpu_model = "lm32-full"; - } - cpu = cpu_lm32_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "qemu: unable to find CPU '%s'\n", cpu_model); - exit(1); - } - - env = &cpu->env; - reset_info->cpu = cpu; - - reset_info->flash_base = flash_base; - - memory_region_allocate_system_memory(phys_ram, NULL, - "lm32_uclinux.sdram", ram_size); - memory_region_add_subregion(address_space_mem, ram_base, phys_ram); - - dinfo = drive_get(IF_PFLASH, 0, 0); - /* Spansion S29NS128P */ - pflash_cfi02_register(flash_base, NULL, "lm32_uclinux.flash", flash_size, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - flash_sector_size, flash_size / flash_sector_size, - 1, 2, 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1); - - /* create irq lines */ - env->pic_state = lm32_pic_init(qemu_allocate_irq(cpu_irq_handler, env, 0)); - for (i = 0; i < 32; i++) { - irq[i] = qdev_get_gpio_in(env->pic_state, i); - } - - sysbus_create_simple("lm32-uart", uart0_base, irq[uart0_irq]); - sysbus_create_simple("lm32-timer", timer0_base, irq[timer0_irq]); - sysbus_create_simple("lm32-timer", timer1_base, irq[timer1_irq]); - sysbus_create_simple("lm32-timer", timer2_base, irq[timer2_irq]); - - /* make sure juart isn't the first chardev */ - env->juart_state = lm32_juart_init(); - - reset_info->bootstrap_pc = flash_base; - - if (kernel_filename) { - uint64_t entry; - int kernel_size; - - kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, - 1, EM_LATTICEMICO32, 0, 0); - reset_info->bootstrap_pc = entry; - - if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, ram_base, - ram_size); - reset_info->bootstrap_pc = ram_base; - } - - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); - exit(1); - } - } - - /* generate a rom with the hardware description */ - hw = hwsetup_init(); - hwsetup_add_cpu(hw, "LM32", 75000000); - hwsetup_add_flash(hw, "flash", flash_base, flash_size); - hwsetup_add_ddr_sdram(hw, "ddr_sdram", ram_base, ram_size); - hwsetup_add_timer(hw, "timer0", timer0_base, timer0_irq); - hwsetup_add_timer(hw, "timer1_dev_only", timer1_base, timer1_irq); - hwsetup_add_timer(hw, "timer2_dev_only", timer2_base, timer2_irq); - hwsetup_add_uart(hw, "uart", uart0_base, uart0_irq); - hwsetup_add_trailer(hw); - hwsetup_create_rom(hw, hwsetup_base); - hwsetup_free(hw); - - reset_info->hwsetup_base = hwsetup_base; - - if (kernel_cmdline && strlen(kernel_cmdline)) { - pstrcpy_targphys("cmdline", cmdline_base, TARGET_PAGE_SIZE, - kernel_cmdline); - reset_info->cmdline_base = cmdline_base; - } - - if (initrd_filename) { - size_t initrd_size; - initrd_size = load_image_targphys(initrd_filename, initrd_base, - initrd_max); - reset_info->initrd_base = initrd_base; - reset_info->initrd_size = initrd_size; - } - - qemu_register_reset(main_cpu_reset, reset_info); -} - -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 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) -{ - type_register_static(&lm32_evr_type); - type_register_static(&lm32_uclinux_type); -} - -type_init(lm32_machine_init) diff --git a/qemu/hw/lm32/lm32_hwsetup.h b/qemu/hw/lm32/lm32_hwsetup.h deleted file mode 100644 index b71e6eafb..000000000 --- a/qemu/hw/lm32/lm32_hwsetup.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * LatticeMico32 hwsetup helper functions. - * - * Copyright (c) 2010 Michael Walle - * - * 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 . - */ - -/* - * These are helper functions for creating the hardware description blob used - * in the Theobroma's uClinux port. - */ - -#ifndef QEMU_HW_LM32_HWSETUP_H -#define QEMU_HW_LM32_HWSETUP_H - -#include "qemu-common.h" -#include "qemu/cutils.h" -#include "hw/loader.h" - -typedef struct { - void *data; - void *ptr; -} HWSetup; - -enum hwsetup_tag { - HWSETUP_TAG_EOL = 0, - HWSETUP_TAG_CPU = 1, - HWSETUP_TAG_ASRAM = 2, - HWSETUP_TAG_FLASH = 3, - HWSETUP_TAG_SDRAM = 4, - HWSETUP_TAG_OCM = 5, - HWSETUP_TAG_DDR_SDRAM = 6, - HWSETUP_TAG_DDR2_SDRAM = 7, - HWSETUP_TAG_TIMER = 8, - HWSETUP_TAG_UART = 9, - HWSETUP_TAG_GPIO = 10, - HWSETUP_TAG_TRISPEEDMAC = 11, - HWSETUP_TAG_I2CM = 12, - HWSETUP_TAG_LEDS = 13, - HWSETUP_TAG_7SEG = 14, - HWSETUP_TAG_SPI_S = 15, - HWSETUP_TAG_SPI_M = 16, -}; - -static inline HWSetup *hwsetup_init(void) -{ - HWSetup *hw; - - hw = g_malloc(sizeof(HWSetup)); - hw->data = g_malloc0(TARGET_PAGE_SIZE); - hw->ptr = hw->data; - - return hw; -} - -static inline void hwsetup_free(HWSetup *hw) -{ - g_free(hw->data); - g_free(hw); -} - -static inline void hwsetup_create_rom(HWSetup *hw, - hwaddr base) -{ - rom_add_blob("hwsetup", hw->data, TARGET_PAGE_SIZE, - TARGET_PAGE_SIZE, base, NULL, NULL, NULL); -} - -static inline void hwsetup_add_u8(HWSetup *hw, uint8_t u) -{ - stb_p(hw->ptr, u); - hw->ptr += 1; -} - -static inline void hwsetup_add_u32(HWSetup *hw, uint32_t u) -{ - stl_p(hw->ptr, u); - hw->ptr += 4; -} - -static inline void hwsetup_add_tag(HWSetup *hw, enum hwsetup_tag t) -{ - stl_p(hw->ptr, t); - hw->ptr += 4; -} - -static inline void hwsetup_add_str(HWSetup *hw, const char *str) -{ - pstrcpy(hw->ptr, 32, str); - hw->ptr += 32; -} - -static inline void hwsetup_add_trailer(HWSetup *hw) -{ - hwsetup_add_u32(hw, 8); /* size */ - hwsetup_add_tag(hw, HWSETUP_TAG_EOL); -} - -static inline void hwsetup_add_cpu(HWSetup *hw, - const char *name, uint32_t frequency) -{ - hwsetup_add_u32(hw, 44); /* size */ - hwsetup_add_tag(hw, HWSETUP_TAG_CPU); - hwsetup_add_str(hw, name); - hwsetup_add_u32(hw, frequency); -} - -static inline void hwsetup_add_flash(HWSetup *hw, - const char *name, uint32_t base, uint32_t size) -{ - hwsetup_add_u32(hw, 52); /* size */ - hwsetup_add_tag(hw, HWSETUP_TAG_FLASH); - hwsetup_add_str(hw, name); - hwsetup_add_u32(hw, base); - hwsetup_add_u32(hw, size); - hwsetup_add_u8(hw, 8); /* read latency */ - hwsetup_add_u8(hw, 8); /* write latency */ - hwsetup_add_u8(hw, 25); /* address width */ - hwsetup_add_u8(hw, 32); /* data width */ -} - -static inline void hwsetup_add_ddr_sdram(HWSetup *hw, - const char *name, uint32_t base, uint32_t size) -{ - hwsetup_add_u32(hw, 48); /* size */ - hwsetup_add_tag(hw, HWSETUP_TAG_DDR_SDRAM); - hwsetup_add_str(hw, name); - hwsetup_add_u32(hw, base); - hwsetup_add_u32(hw, size); -} - -static inline void hwsetup_add_timer(HWSetup *hw, - const char *name, uint32_t base, uint32_t irq) -{ - hwsetup_add_u32(hw, 56); /* size */ - hwsetup_add_tag(hw, HWSETUP_TAG_TIMER); - hwsetup_add_str(hw, name); - hwsetup_add_u32(hw, base); - hwsetup_add_u8(hw, 1); /* wr_tickcount */ - hwsetup_add_u8(hw, 1); /* rd_tickcount */ - hwsetup_add_u8(hw, 1); /* start_stop_control */ - hwsetup_add_u8(hw, 32); /* counter_width */ - hwsetup_add_u32(hw, 20); /* reload_ticks */ - hwsetup_add_u8(hw, irq); - hwsetup_add_u8(hw, 0); /* padding */ - hwsetup_add_u8(hw, 0); /* padding */ - hwsetup_add_u8(hw, 0); /* padding */ -} - -static inline void hwsetup_add_uart(HWSetup *hw, - const char *name, uint32_t base, uint32_t irq) -{ - hwsetup_add_u32(hw, 56); /* size */ - hwsetup_add_tag(hw, HWSETUP_TAG_UART); - hwsetup_add_str(hw, name); - hwsetup_add_u32(hw, base); - hwsetup_add_u32(hw, 115200); /* baudrate */ - hwsetup_add_u8(hw, 8); /* databits */ - hwsetup_add_u8(hw, 1); /* stopbits */ - hwsetup_add_u8(hw, 1); /* use_interrupt */ - hwsetup_add_u8(hw, 1); /* block_on_transmit */ - hwsetup_add_u8(hw, 1); /* block_on_receive */ - hwsetup_add_u8(hw, 4); /* rx_buffer_size */ - hwsetup_add_u8(hw, 4); /* tx_buffer_size */ - hwsetup_add_u8(hw, irq); -} - -#endif /* QEMU_HW_LM32_HWSETUP_H */ diff --git a/qemu/hw/lm32/milkymist-hw.h b/qemu/hw/lm32/milkymist-hw.h deleted file mode 100644 index c8dfb4d2d..000000000 --- a/qemu/hw/lm32/milkymist-hw.h +++ /dev/null @@ -1,208 +0,0 @@ -#ifndef QEMU_HW_MILKYMIST_H -#define QEMU_HW_MILKYMIST_H - -#include "hw/qdev.h" -#include "net/net.h" - -static inline DeviceState *milkymist_uart_create(hwaddr base, - qemu_irq irq) -{ - DeviceState *dev; - - dev = qdev_create(NULL, "milkymist-uart"); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); - - return dev; -} - -static inline DeviceState *milkymist_hpdmc_create(hwaddr base) -{ - DeviceState *dev; - - dev = qdev_create(NULL, "milkymist-hpdmc"); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - - return dev; -} - -static inline DeviceState *milkymist_memcard_create(hwaddr base) -{ - DeviceState *dev; - - dev = qdev_create(NULL, "milkymist-memcard"); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - - return dev; -} - -static inline DeviceState *milkymist_vgafb_create(hwaddr base, - uint32_t fb_offset, uint32_t fb_mask) -{ - DeviceState *dev; - - dev = qdev_create(NULL, "milkymist-vgafb"); - qdev_prop_set_uint32(dev, "fb_offset", fb_offset); - qdev_prop_set_uint32(dev, "fb_mask", fb_mask); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - - return dev; -} - -static inline DeviceState *milkymist_sysctl_create(hwaddr base, - qemu_irq gpio_irq, qemu_irq timer0_irq, qemu_irq timer1_irq, - uint32_t freq_hz, uint32_t system_id, uint32_t capabilities, - uint32_t gpio_strappings) -{ - DeviceState *dev; - - dev = qdev_create(NULL, "milkymist-sysctl"); - qdev_prop_set_uint32(dev, "frequency", freq_hz); - qdev_prop_set_uint32(dev, "systemid", system_id); - qdev_prop_set_uint32(dev, "capabilities", capabilities); - qdev_prop_set_uint32(dev, "gpio_strappings", gpio_strappings); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, gpio_irq); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, timer0_irq); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, timer1_irq); - - return dev; -} - -static inline DeviceState *milkymist_pfpu_create(hwaddr base, - qemu_irq irq) -{ - DeviceState *dev; - - dev = qdev_create(NULL, "milkymist-pfpu"); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); - return dev; -} - -#ifdef CONFIG_OPENGL -#include -#include -#include -static const int glx_fbconfig_attr[] = { - GLX_GREEN_SIZE, 5, - GLX_GREEN_SIZE, 6, - GLX_BLUE_SIZE, 5, - None -}; -#endif - -static inline DeviceState *milkymist_tmu2_create(hwaddr base, - qemu_irq irq) -{ -#ifdef CONFIG_OPENGL - DeviceState *dev; - Display *d; - GLXFBConfig *configs; - int nelements; - int ver_major, ver_minor; - - if (display_type == DT_NOGRAPHIC) { - return NULL; - } - - /* check that GLX will work */ - d = XOpenDisplay(NULL); - if (d == NULL) { - return NULL; - } - - if (!glXQueryVersion(d, &ver_major, &ver_minor)) { - /* Yeah, sometimes getting the GLX version can fail. - * Isn't X beautiful? */ - XCloseDisplay(d); - return NULL; - } - - if ((ver_major < 1) || ((ver_major == 1) && (ver_minor < 3))) { - printf("Your GLX version is %d.%d," - "but TMU emulation needs at least 1.3. TMU disabled.\n", - ver_major, ver_minor); - XCloseDisplay(d); - return NULL; - } - - configs = glXChooseFBConfig(d, 0, glx_fbconfig_attr, &nelements); - if (configs == NULL) { - XCloseDisplay(d); - return NULL; - } - - XFree(configs); - XCloseDisplay(d); - - dev = qdev_create(NULL, "milkymist-tmu2"); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); - - return dev; -#else - return NULL; -#endif -} - -static inline DeviceState *milkymist_ac97_create(hwaddr base, - qemu_irq crrequest_irq, qemu_irq crreply_irq, qemu_irq dmar_irq, - qemu_irq dmaw_irq) -{ - DeviceState *dev; - - dev = qdev_create(NULL, "milkymist-ac97"); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, crrequest_irq); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, crreply_irq); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, dmar_irq); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 3, dmaw_irq); - - return dev; -} - -static inline DeviceState *milkymist_minimac2_create(hwaddr base, - hwaddr buffers_base, qemu_irq rx_irq, qemu_irq tx_irq) -{ - DeviceState *dev; - - qemu_check_nic_model(&nd_table[0], "minimac2"); - dev = qdev_create(NULL, "milkymist-minimac2"); - qdev_set_nic_properties(dev, &nd_table[0]); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, buffers_base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, rx_irq); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, tx_irq); - - return dev; -} - -static inline DeviceState *milkymist_softusb_create(hwaddr base, - qemu_irq irq, uint32_t pmem_base, uint32_t pmem_size, - uint32_t dmem_base, uint32_t dmem_size) -{ - DeviceState *dev; - - dev = qdev_create(NULL, "milkymist-softusb"); - qdev_prop_set_uint32(dev, "pmem_size", pmem_size); - qdev_prop_set_uint32(dev, "dmem_size", dmem_size); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, pmem_base); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, dmem_base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); - - return dev; -} - -#endif /* QEMU_HW_MILKYMIST_H */ diff --git a/qemu/hw/lm32/milkymist.c b/qemu/hw/lm32/milkymist.c deleted file mode 100644 index 96e6f4dc2..000000000 --- a/qemu/hw/lm32/milkymist.c +++ /dev/null @@ -1,223 +0,0 @@ -/* - * QEMU model for the Milkymist board. - * - * Copyright (c) 2010 Michael Walle - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/sysbus.h" -#include "hw/hw.h" -#include "hw/block/flash.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "hw/devices.h" -#include "hw/boards.h" -#include "hw/loader.h" -#include "elf.h" -#include "sysemu/block-backend.h" -#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 -#define BIOS_SIZE (512*1024) -#define KERNEL_LOAD_ADDR 0x40000000 - -typedef struct { - LM32CPU *cpu; - hwaddr bootstrap_pc; - hwaddr flash_base; - hwaddr initrd_base; - size_t initrd_size; - hwaddr cmdline_base; -} ResetInfo; - -static void cpu_irq_handler(void *opaque, int irq, int level) -{ - LM32CPU *cpu = opaque; - CPUState *cs = CPU(cpu); - - if (level) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } -} - -static void main_cpu_reset(void *opaque) -{ - ResetInfo *reset_info = opaque; - CPULM32State *env = &reset_info->cpu->env; - - cpu_reset(CPU(reset_info->cpu)); - - /* init defaults */ - env->pc = reset_info->bootstrap_pc; - env->regs[R_R1] = reset_info->cmdline_base; - env->regs[R_R2] = reset_info->initrd_base; - env->regs[R_R3] = reset_info->initrd_base + reset_info->initrd_size; - env->eba = reset_info->flash_base; - env->deba = reset_info->flash_base; -} - -static void -milkymist_init(MachineState *machine) -{ - 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; - LM32CPU *cpu; - CPULM32State *env; - int kernel_size; - DriveInfo *dinfo; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *phys_sdram = g_new(MemoryRegion, 1); - qemu_irq irq[32]; - int i; - char *bios_filename; - ResetInfo *reset_info; - - /* memory map */ - hwaddr flash_base = 0x00000000; - size_t flash_sector_size = 128 * 1024; - size_t flash_size = 32 * 1024 * 1024; - hwaddr sdram_base = 0x40000000; - size_t sdram_size = 128 * 1024 * 1024; - - hwaddr initrd_base = sdram_base + 0x1002000; - hwaddr cmdline_base = sdram_base + 0x1000000; - size_t initrd_max = sdram_size - 0x1002000; - - reset_info = g_malloc0(sizeof(ResetInfo)); - - if (cpu_model == NULL) { - cpu_model = "lm32-full"; - } - cpu = cpu_lm32_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "qemu: unable to find CPU '%s'\n", cpu_model); - exit(1); - } - - env = &cpu->env; - reset_info->cpu = cpu; - - cpu_lm32_set_phys_msb_ignore(env, 1); - - memory_region_allocate_system_memory(phys_sdram, NULL, "milkymist.sdram", - sdram_size); - memory_region_add_subregion(address_space_mem, sdram_base, phys_sdram); - - dinfo = drive_get(IF_PFLASH, 0, 0); - /* Numonyx JS28F256J3F105 */ - pflash_cfi01_register(flash_base, NULL, "milkymist.flash", flash_size, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - flash_sector_size, flash_size / flash_sector_size, - 2, 0x00, 0x89, 0x00, 0x1d, 1); - - /* create irq lines */ - env->pic_state = lm32_pic_init(qemu_allocate_irq(cpu_irq_handler, cpu, 0)); - for (i = 0; i < 32; i++) { - irq[i] = qdev_get_gpio_in(env->pic_state, i); - } - - /* load bios rom */ - if (bios_name == NULL) { - bios_name = BIOS_FILENAME; - } - bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - - if (bios_filename) { - load_image_targphys(bios_filename, BIOS_OFFSET, BIOS_SIZE); - } - - reset_info->bootstrap_pc = BIOS_OFFSET; - - /* if no kernel is given no valid bios rom is a fatal error */ - if (!kernel_filename && !dinfo && !bios_filename && !qtest_enabled()) { - fprintf(stderr, "qemu: could not load Milkymist One bios '%s'\n", - bios_name); - exit(1); - } - g_free(bios_filename); - - milkymist_uart_create(0x60000000, irq[0]); - milkymist_sysctl_create(0x60001000, irq[1], irq[2], irq[3], - 80000000, 0x10014d31, 0x0000041f, 0x00000001); - milkymist_hpdmc_create(0x60002000); - milkymist_vgafb_create(0x60003000, 0x40000000, 0x0fffffff); - milkymist_memcard_create(0x60004000); - milkymist_ac97_create(0x60005000, irq[4], irq[5], irq[6], irq[7]); - milkymist_pfpu_create(0x60006000, irq[8]); - milkymist_tmu2_create(0x60007000, irq[9]); - milkymist_minimac2_create(0x60008000, 0x30000000, irq[10], irq[11]); - milkymist_softusb_create(0x6000f000, irq[15], - 0x20000000, 0x1000, 0x20020000, 0x2000); - - /* make sure juart isn't the first chardev */ - env->juart_state = lm32_juart_init(); - - if (kernel_filename) { - uint64_t entry; - - /* Boots a kernel elf binary. */ - kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, - 1, EM_LATTICEMICO32, 0, 0); - reset_info->bootstrap_pc = entry; - - if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, sdram_base, - sdram_size); - reset_info->bootstrap_pc = sdram_base; - } - - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); - exit(1); - } - } - - if (kernel_cmdline && strlen(kernel_cmdline)) { - pstrcpy_targphys("cmdline", cmdline_base, TARGET_PAGE_SIZE, - kernel_cmdline); - reset_info->cmdline_base = (uint32_t)cmdline_base; - } - - if (initrd_filename) { - size_t initrd_size; - initrd_size = load_image_targphys(initrd_filename, initrd_base, - initrd_max); - reset_info->initrd_base = (uint32_t)initrd_base; - reset_info->initrd_size = (uint32_t)initrd_size; - } - - qemu_register_reset(main_cpu_reset, reset_info); -} - -static void milkymist_machine_init(MachineClass *mc) -{ - mc->desc = "Milkymist One"; - mc->init = milkymist_init; - mc->is_default = 0; -} - -DEFINE_MACHINE("milkymist", milkymist_machine_init) diff --git a/qemu/hw/m68k/Makefile.objs b/qemu/hw/m68k/Makefile.objs deleted file mode 100644 index c4352e783..000000000 --- a/qemu/hw/m68k/Makefile.objs +++ /dev/null @@ -1,4 +0,0 @@ -obj-y += an5206.o mcf5208.o -obj-y += dummy_m68k.o - -obj-y += mcf5206.o mcf_intc.o diff --git a/qemu/hw/m68k/an5206.c b/qemu/hw/m68k/an5206.c deleted file mode 100644 index 142bab98c..000000000 --- a/qemu/hw/m68k/an5206.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Arnewsh 5206 ColdFire system emulation. - * - * Copyright (c) 2007 CodeSourcery. - * - * 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 -#define AN5206_MBAR_ADDR 0x10000000 -#define AN5206_RAMBAR_ADDR 0x20000000 - -/* Board init. */ - -static void an5206_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; - M68kCPU *cpu; - CPUM68KState *env; - int kernel_size; - uint64_t elf_entry; - hwaddr entry; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *sram = g_new(MemoryRegion, 1); - - if (!cpu_model) { - cpu_model = "m5206"; - } - cpu = cpu_m68k_init(cpu_model); - if (!cpu) { - error_report("Unable to find m68k CPU definition"); - exit(1); - } - env = &cpu->env; - - /* Initialize CPU registers. */ - env->vbr = 0; - /* TODO: allow changing MBAR and RAMBAR. */ - env->mbar = AN5206_MBAR_ADDR | 1; - env->rambar0 = AN5206_RAMBAR_ADDR | 1; - - /* DRAM at address zero */ - memory_region_allocate_system_memory(ram, NULL, "an5206.ram", ram_size); - memory_region_add_subregion(address_space_mem, 0, ram); - - /* Internal SRAM. */ - 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); - - mcf5206_init(address_space_mem, AN5206_MBAR_ADDR, cpu); - - /* Load kernel. */ - if (!kernel_filename) { - if (qtest_enabled()) { - return; - } - fprintf(stderr, "Kernel image must be specified\n"); - exit(1); - } - - kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, - NULL, NULL, 1, EM_68K, 0, 0); - entry = elf_entry; - if (kernel_size < 0) { - kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, - NULL, NULL); - } - if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR, - ram_size - KERNEL_LOAD_ADDR); - entry = KERNEL_LOAD_ADDR; - } - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); - exit(1); - } - - env->pc = entry; -} - -static void an5206_machine_init(MachineClass *mc) -{ - mc->desc = "Arnewsh 5206"; - mc->init = an5206_init; -} - -DEFINE_MACHINE("an5206", an5206_machine_init) diff --git a/qemu/hw/m68k/dummy_m68k.c b/qemu/hw/m68k/dummy_m68k.c deleted file mode 100644 index 0b11d2074..000000000 --- a/qemu/hw/m68k/dummy_m68k.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Dummy board with just RAM and CPU for use as an ISS. - * - * Copyright (c) 2007 CodeSourcery. - * - * 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" -#include "elf.h" -#include "exec/address-spaces.h" - -#define KERNEL_LOAD_ADDR 0x10000 - -/* Board init. */ - -static void dummy_m68k_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; - M68kCPU *cpu; - CPUM68KState *env; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - int kernel_size; - uint64_t elf_entry; - hwaddr entry; - - if (!cpu_model) - cpu_model = "cfv4e"; - cpu = cpu_m68k_init(cpu_model); - if (!cpu) { - fprintf(stderr, "Unable to find m68k CPU definition\n"); - exit(1); - } - env = &cpu->env; - - /* Initialize CPU registers. */ - env->vbr = 0; - - /* RAM at address zero */ - memory_region_allocate_system_memory(ram, NULL, "dummy_m68k.ram", - ram_size); - memory_region_add_subregion(address_space_mem, 0, ram); - - /* Load kernel. */ - if (kernel_filename) { - kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, - NULL, NULL, 1, EM_68K, 0, 0); - entry = elf_entry; - if (kernel_size < 0) { - kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, - NULL, NULL); - } - if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, - KERNEL_LOAD_ADDR, - ram_size - KERNEL_LOAD_ADDR); - entry = KERNEL_LOAD_ADDR; - } - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); - exit(1); - } - } else { - entry = 0; - } - env->pc = entry; -} - -static void dummy_m68k_machine_init(MachineClass *mc) -{ - mc->desc = "Dummy board"; - mc->init = dummy_m68k_init; -} - -DEFINE_MACHINE("dummy", dummy_m68k_machine_init) diff --git a/qemu/hw/m68k/mcf5206.c b/qemu/hw/m68k/mcf5206.c deleted file mode 100644 index e14896e52..000000000 --- a/qemu/hw/m68k/mcf5206.c +++ /dev/null @@ -1,551 +0,0 @@ -/* - * Motorola ColdFire MCF5206 SoC embedded peripheral emulation. - * - * Copyright (c) 2007 CodeSourcery. - * - * 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" -#include "hw/ptimer.h" -#include "sysemu/sysemu.h" -#include "exec/address-spaces.h" - -/* General purpose timer module. */ -typedef struct { - uint16_t tmr; - uint16_t trr; - uint16_t tcr; - uint16_t ter; - ptimer_state *timer; - qemu_irq irq; - int irq_state; -} m5206_timer_state; - -#define TMR_RST 0x01 -#define TMR_CLK 0x06 -#define TMR_FRR 0x08 -#define TMR_ORI 0x10 -#define TMR_OM 0x20 -#define TMR_CE 0xc0 - -#define TER_CAP 0x01 -#define TER_REF 0x02 - -static void m5206_timer_update(m5206_timer_state *s) -{ - if ((s->tmr & TMR_ORI) != 0 && (s->ter & TER_REF)) - qemu_irq_raise(s->irq); - else - qemu_irq_lower(s->irq); -} - -static void m5206_timer_reset(m5206_timer_state *s) -{ - s->tmr = 0; - s->trr = 0; -} - -static void m5206_timer_recalibrate(m5206_timer_state *s) -{ - int prescale; - int mode; - - ptimer_stop(s->timer); - - if ((s->tmr & TMR_RST) == 0) - return; - - prescale = (s->tmr >> 8) + 1; - mode = (s->tmr >> 1) & 3; - if (mode == 2) - prescale *= 16; - - if (mode == 3 || mode == 0) - hw_error("m5206_timer: mode %d not implemented\n", mode); - if ((s->tmr & TMR_FRR) == 0) - hw_error("m5206_timer: free running mode not implemented\n"); - - /* Assume 66MHz system clock. */ - ptimer_set_freq(s->timer, 66000000 / prescale); - - ptimer_set_limit(s->timer, s->trr, 0); - - ptimer_run(s->timer, 0); -} - -static void m5206_timer_trigger(void *opaque) -{ - m5206_timer_state *s = (m5206_timer_state *)opaque; - s->ter |= TER_REF; - m5206_timer_update(s); -} - -static uint32_t m5206_timer_read(m5206_timer_state *s, uint32_t addr) -{ - switch (addr) { - case 0: - return s->tmr; - case 4: - return s->trr; - case 8: - return s->tcr; - case 0xc: - return s->trr - ptimer_get_count(s->timer); - case 0x11: - return s->ter; - default: - return 0; - } -} - -static void m5206_timer_write(m5206_timer_state *s, uint32_t addr, uint32_t val) -{ - switch (addr) { - case 0: - if ((s->tmr & TMR_RST) != 0 && (val & TMR_RST) == 0) { - m5206_timer_reset(s); - } - s->tmr = val; - m5206_timer_recalibrate(s); - break; - case 4: - s->trr = val; - m5206_timer_recalibrate(s); - break; - case 8: - s->tcr = val; - break; - case 0xc: - ptimer_set_count(s->timer, val); - break; - case 0x11: - s->ter &= ~val; - break; - default: - break; - } - m5206_timer_update(s); -} - -static m5206_timer_state *m5206_timer_init(qemu_irq irq) -{ - m5206_timer_state *s; - QEMUBH *bh; - - s = (m5206_timer_state *)g_malloc0(sizeof(m5206_timer_state)); - bh = qemu_bh_new(m5206_timer_trigger, s); - s->timer = ptimer_init(bh); - s->irq = irq; - m5206_timer_reset(s); - return s; -} - -/* System Integration Module. */ - -typedef struct { - M68kCPU *cpu; - MemoryRegion iomem; - m5206_timer_state *timer[2]; - void *uart[2]; - uint8_t scr; - uint8_t icr[14]; - uint16_t imr; /* 1 == interrupt is masked. */ - uint16_t ipr; - uint8_t rsr; - uint8_t swivr; - uint8_t par; - /* Include the UART vector registers here. */ - uint8_t uivr[2]; -} m5206_mbar_state; - -/* Interrupt controller. */ - -static int m5206_find_pending_irq(m5206_mbar_state *s) -{ - int level; - int vector; - uint16_t active; - int i; - - level = 0; - vector = 0; - active = s->ipr & ~s->imr; - if (!active) - return 0; - - for (i = 1; i < 14; i++) { - if (active & (1 << i)) { - if ((s->icr[i] & 0x1f) > level) { - level = s->icr[i] & 0x1f; - vector = i; - } - } - } - - if (level < 4) - vector = 0; - - return vector; -} - -static void m5206_mbar_update(m5206_mbar_state *s) -{ - int irq; - int vector; - int level; - - irq = m5206_find_pending_irq(s); - if (irq) { - int tmp; - tmp = s->icr[irq]; - level = (tmp >> 2) & 7; - if (tmp & 0x80) { - /* Autovector. */ - vector = 24 + level; - } else { - switch (irq) { - case 8: /* SWT */ - vector = s->swivr; - break; - case 12: /* UART1 */ - vector = s->uivr[0]; - break; - case 13: /* UART2 */ - vector = s->uivr[1]; - break; - default: - /* Unknown vector. */ - fprintf(stderr, "Unhandled vector for IRQ %d\n", irq); - vector = 0xf; - break; - } - } - } else { - level = 0; - vector = 0; - } - m68k_set_irq_level(s->cpu, level, vector); -} - -static void m5206_mbar_set_irq(void *opaque, int irq, int level) -{ - m5206_mbar_state *s = (m5206_mbar_state *)opaque; - if (level) { - s->ipr |= 1 << irq; - } else { - s->ipr &= ~(1 << irq); - } - m5206_mbar_update(s); -} - -/* System Integration Module. */ - -static void m5206_mbar_reset(m5206_mbar_state *s) -{ - s->scr = 0xc0; - s->icr[1] = 0x04; - s->icr[2] = 0x08; - s->icr[3] = 0x0c; - s->icr[4] = 0x10; - s->icr[5] = 0x14; - s->icr[6] = 0x18; - s->icr[7] = 0x1c; - s->icr[8] = 0x1c; - s->icr[9] = 0x80; - s->icr[10] = 0x80; - s->icr[11] = 0x80; - s->icr[12] = 0x00; - s->icr[13] = 0x00; - s->imr = 0x3ffe; - s->rsr = 0x80; - s->swivr = 0x0f; - s->par = 0; -} - -static uint64_t m5206_mbar_read(m5206_mbar_state *s, - uint64_t offset, unsigned size) -{ - if (offset >= 0x100 && offset < 0x120) { - return m5206_timer_read(s->timer[0], offset - 0x100); - } else if (offset >= 0x120 && offset < 0x140) { - return m5206_timer_read(s->timer[1], offset - 0x120); - } else if (offset >= 0x140 && offset < 0x160) { - return mcf_uart_read(s->uart[0], offset - 0x140, size); - } else if (offset >= 0x180 && offset < 0x1a0) { - return mcf_uart_read(s->uart[1], offset - 0x180, size); - } - switch (offset) { - case 0x03: return s->scr; - case 0x14 ... 0x20: return s->icr[offset - 0x13]; - case 0x36: return s->imr; - case 0x3a: return s->ipr; - case 0x40: return s->rsr; - case 0x41: return 0; - case 0x42: return s->swivr; - case 0x50: - /* DRAM mask register. */ - /* FIXME: currently hardcoded to 128Mb. */ - { - uint32_t mask = ~0; - while (mask > ram_size) - mask >>= 1; - return mask & 0x0ffe0000; - } - case 0x5c: return 1; /* DRAM bank 1 empty. */ - case 0xcb: return s->par; - case 0x170: return s->uivr[0]; - case 0x1b0: return s->uivr[1]; - } - hw_error("Bad MBAR read offset 0x%x", (int)offset); - return 0; -} - -static void m5206_mbar_write(m5206_mbar_state *s, uint32_t offset, - uint64_t value, unsigned size) -{ - if (offset >= 0x100 && offset < 0x120) { - m5206_timer_write(s->timer[0], offset - 0x100, value); - return; - } else if (offset >= 0x120 && offset < 0x140) { - m5206_timer_write(s->timer[1], offset - 0x120, value); - return; - } else if (offset >= 0x140 && offset < 0x160) { - mcf_uart_write(s->uart[0], offset - 0x140, value, size); - return; - } else if (offset >= 0x180 && offset < 0x1a0) { - mcf_uart_write(s->uart[1], offset - 0x180, value, size); - return; - } - switch (offset) { - case 0x03: - s->scr = value; - break; - case 0x14 ... 0x20: - s->icr[offset - 0x13] = value; - m5206_mbar_update(s); - break; - case 0x36: - s->imr = value; - m5206_mbar_update(s); - break; - case 0x40: - s->rsr &= ~value; - break; - case 0x41: - /* TODO: implement watchdog. */ - break; - case 0x42: - s->swivr = value; - break; - case 0xcb: - s->par = value; - break; - case 0x170: - s->uivr[0] = value; - break; - case 0x178: case 0x17c: case 0x1c8: case 0x1bc: - /* Not implemented: UART Output port bits. */ - break; - case 0x1b0: - s->uivr[1] = value; - break; - default: - hw_error("Bad MBAR write offset 0x%x", (int)offset); - break; - } -} - -/* Internal peripherals use a variety of register widths. - This lookup table allows a single routine to handle all of them. */ -static const uint8_t m5206_mbar_width[] = -{ - /* 000-040 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, - /* 040-080 */ 1, 2, 2, 2, 4, 1, 2, 4, 1, 2, 4, 2, 2, 4, 2, 2, - /* 080-0c0 */ 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, - /* 0c0-100 */ 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 100-140 */ 2, 2, 2, 2, 1, 0, 0, 0, 2, 2, 2, 2, 1, 0, 0, 0, - /* 140-180 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - /* 180-1c0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - /* 1c0-200 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -}; - -static uint32_t m5206_mbar_readw(void *opaque, hwaddr offset); -static uint32_t m5206_mbar_readl(void *opaque, hwaddr offset); - -static uint32_t m5206_mbar_readb(void *opaque, hwaddr offset) -{ - m5206_mbar_state *s = (m5206_mbar_state *)opaque; - offset &= 0x3ff; - if (offset >= 0x200) { - hw_error("Bad MBAR read offset 0x%x", (int)offset); - } - if (m5206_mbar_width[offset >> 2] > 1) { - uint16_t val; - val = m5206_mbar_readw(opaque, offset & ~1); - if ((offset & 1) == 0) { - val >>= 8; - } - return val & 0xff; - } - return m5206_mbar_read(s, offset, 1); -} - -static uint32_t m5206_mbar_readw(void *opaque, hwaddr offset) -{ - m5206_mbar_state *s = (m5206_mbar_state *)opaque; - int width; - offset &= 0x3ff; - if (offset >= 0x200) { - hw_error("Bad MBAR read offset 0x%x", (int)offset); - } - width = m5206_mbar_width[offset >> 2]; - if (width > 2) { - uint32_t val; - val = m5206_mbar_readl(opaque, offset & ~3); - if ((offset & 3) == 0) - val >>= 16; - return val & 0xffff; - } else if (width < 2) { - uint16_t val; - val = m5206_mbar_readb(opaque, offset) << 8; - val |= m5206_mbar_readb(opaque, offset + 1); - return val; - } - return m5206_mbar_read(s, offset, 2); -} - -static uint32_t m5206_mbar_readl(void *opaque, hwaddr offset) -{ - m5206_mbar_state *s = (m5206_mbar_state *)opaque; - int width; - offset &= 0x3ff; - if (offset >= 0x200) { - hw_error("Bad MBAR read offset 0x%x", (int)offset); - } - width = m5206_mbar_width[offset >> 2]; - if (width < 4) { - uint32_t val; - val = m5206_mbar_readw(opaque, offset) << 16; - val |= m5206_mbar_readw(opaque, offset + 2); - return val; - } - return m5206_mbar_read(s, offset, 4); -} - -static void m5206_mbar_writew(void *opaque, hwaddr offset, - uint32_t value); -static void m5206_mbar_writel(void *opaque, hwaddr offset, - uint32_t value); - -static void m5206_mbar_writeb(void *opaque, hwaddr offset, - uint32_t value) -{ - m5206_mbar_state *s = (m5206_mbar_state *)opaque; - int width; - offset &= 0x3ff; - if (offset >= 0x200) { - hw_error("Bad MBAR write offset 0x%x", (int)offset); - } - width = m5206_mbar_width[offset >> 2]; - if (width > 1) { - uint32_t tmp; - tmp = m5206_mbar_readw(opaque, offset & ~1); - if (offset & 1) { - tmp = (tmp & 0xff00) | value; - } else { - tmp = (tmp & 0x00ff) | (value << 8); - } - m5206_mbar_writew(opaque, offset & ~1, tmp); - return; - } - m5206_mbar_write(s, offset, value, 1); -} - -static void m5206_mbar_writew(void *opaque, hwaddr offset, - uint32_t value) -{ - m5206_mbar_state *s = (m5206_mbar_state *)opaque; - int width; - offset &= 0x3ff; - if (offset >= 0x200) { - hw_error("Bad MBAR write offset 0x%x", (int)offset); - } - width = m5206_mbar_width[offset >> 2]; - if (width > 2) { - uint32_t tmp; - tmp = m5206_mbar_readl(opaque, offset & ~3); - if (offset & 3) { - tmp = (tmp & 0xffff0000) | value; - } else { - tmp = (tmp & 0x0000ffff) | (value << 16); - } - m5206_mbar_writel(opaque, offset & ~3, tmp); - return; - } else if (width < 2) { - m5206_mbar_writeb(opaque, offset, value >> 8); - m5206_mbar_writeb(opaque, offset + 1, value & 0xff); - return; - } - m5206_mbar_write(s, offset, value, 2); -} - -static void m5206_mbar_writel(void *opaque, hwaddr offset, - uint32_t value) -{ - m5206_mbar_state *s = (m5206_mbar_state *)opaque; - int width; - offset &= 0x3ff; - if (offset >= 0x200) { - hw_error("Bad MBAR write offset 0x%x", (int)offset); - } - width = m5206_mbar_width[offset >> 2]; - if (width < 4) { - m5206_mbar_writew(opaque, offset, value >> 16); - m5206_mbar_writew(opaque, offset + 2, value & 0xffff); - return; - } - m5206_mbar_write(s, offset, value, 4); -} - -static const MemoryRegionOps m5206_mbar_ops = { - .old_mmio = { - .read = { - m5206_mbar_readb, - m5206_mbar_readw, - m5206_mbar_readl, - }, - .write = { - m5206_mbar_writeb, - m5206_mbar_writew, - m5206_mbar_writel, - }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -qemu_irq *mcf5206_init(MemoryRegion *sysmem, uint32_t base, M68kCPU *cpu) -{ - m5206_mbar_state *s; - qemu_irq *pic; - - s = (m5206_mbar_state *)g_malloc0(sizeof(m5206_mbar_state)); - - memory_region_init_io(&s->iomem, NULL, &m5206_mbar_ops, s, - "mbar", 0x00001000); - memory_region_add_subregion(sysmem, base, &s->iomem); - - pic = qemu_allocate_irqs(m5206_mbar_set_irq, s, 14); - s->timer[0] = m5206_timer_init(pic[9]); - s->timer[1] = m5206_timer_init(pic[10]); - s->uart[0] = mcf_uart_init(pic[12], serial_hds[0]); - s->uart[1] = mcf_uart_init(pic[13], serial_hds[1]); - s->cpu = cpu; - - m5206_mbar_reset(s); - return pic; -} diff --git a/qemu/hw/m68k/mcf5208.c b/qemu/hw/m68k/mcf5208.c deleted file mode 100644 index 24155574f..000000000 --- a/qemu/hw/m68k/mcf5208.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Motorola ColdFire MCF5208 SoC emulation. - * - * Copyright (c) 2007 CodeSourcery. - * - * 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" -#include "hw/ptimer.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "net/net.h" -#include "hw/boards.h" -#include "hw/loader.h" -#include "elf.h" -#include "exec/address-spaces.h" - -#define SYS_FREQ 66000000 - -#define PCSR_EN 0x0001 -#define PCSR_RLD 0x0002 -#define PCSR_PIF 0x0004 -#define PCSR_PIE 0x0008 -#define PCSR_OVW 0x0010 -#define PCSR_DBG 0x0020 -#define PCSR_DOZE 0x0040 -#define PCSR_PRE_SHIFT 8 -#define PCSR_PRE_MASK 0x0f00 - -typedef struct { - MemoryRegion iomem; - qemu_irq irq; - ptimer_state *timer; - uint16_t pcsr; - uint16_t pmr; - uint16_t pcntr; -} m5208_timer_state; - -static void m5208_timer_update(m5208_timer_state *s) -{ - if ((s->pcsr & (PCSR_PIE | PCSR_PIF)) == (PCSR_PIE | PCSR_PIF)) - qemu_irq_raise(s->irq); - else - qemu_irq_lower(s->irq); -} - -static void m5208_timer_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - m5208_timer_state *s = (m5208_timer_state *)opaque; - int prescale; - int limit; - switch (offset) { - case 0: - /* The PIF bit is set-to-clear. */ - if (value & PCSR_PIF) { - s->pcsr &= ~PCSR_PIF; - value &= ~PCSR_PIF; - } - /* Avoid frobbing the timer if we're just twiddling IRQ bits. */ - if (((s->pcsr ^ value) & ~PCSR_PIE) == 0) { - s->pcsr = value; - m5208_timer_update(s); - return; - } - - if (s->pcsr & PCSR_EN) - ptimer_stop(s->timer); - - s->pcsr = value; - - prescale = 1 << ((s->pcsr & PCSR_PRE_MASK) >> PCSR_PRE_SHIFT); - ptimer_set_freq(s->timer, (SYS_FREQ / 2) / prescale); - if (s->pcsr & PCSR_RLD) - limit = s->pmr; - else - limit = 0xffff; - ptimer_set_limit(s->timer, limit, 0); - - if (s->pcsr & PCSR_EN) - ptimer_run(s->timer, 0); - break; - case 2: - s->pmr = value; - s->pcsr &= ~PCSR_PIF; - if ((s->pcsr & PCSR_RLD) == 0) { - if (s->pcsr & PCSR_OVW) - ptimer_set_count(s->timer, value); - } else { - ptimer_set_limit(s->timer, value, s->pcsr & PCSR_OVW); - } - break; - case 4: - break; - default: - hw_error("m5208_timer_write: Bad offset 0x%x\n", (int)offset); - break; - } - m5208_timer_update(s); -} - -static void m5208_timer_trigger(void *opaque) -{ - m5208_timer_state *s = (m5208_timer_state *)opaque; - s->pcsr |= PCSR_PIF; - m5208_timer_update(s); -} - -static uint64_t m5208_timer_read(void *opaque, hwaddr addr, - unsigned size) -{ - m5208_timer_state *s = (m5208_timer_state *)opaque; - switch (addr) { - case 0: - return s->pcsr; - case 2: - return s->pmr; - case 4: - return ptimer_get_count(s->timer); - default: - hw_error("m5208_timer_read: Bad offset 0x%x\n", (int)addr); - return 0; - } -} - -static const MemoryRegionOps m5208_timer_ops = { - .read = m5208_timer_read, - .write = m5208_timer_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t m5208_sys_read(void *opaque, hwaddr addr, - unsigned size) -{ - switch (addr) { - case 0x110: /* SDCS0 */ - { - int n; - for (n = 0; n < 32; n++) { - if (ram_size < (2u << n)) - break; - } - return (n - 1) | 0x40000000; - } - case 0x114: /* SDCS1 */ - return 0; - - default: - hw_error("m5208_sys_read: Bad offset 0x%x\n", (int)addr); - return 0; - } -} - -static void m5208_sys_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - hw_error("m5208_sys_write: Bad offset 0x%x\n", (int)addr); -} - -static const MemoryRegionOps m5208_sys_ops = { - .read = m5208_sys_read, - .write = m5208_sys_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void mcf5208_sys_init(MemoryRegion *address_space, qemu_irq *pic) -{ - MemoryRegion *iomem = g_new(MemoryRegion, 1); - m5208_timer_state *s; - QEMUBH *bh; - int i; - - /* SDRAMC. */ - memory_region_init_io(iomem, NULL, &m5208_sys_ops, NULL, "m5208-sys", 0x00004000); - memory_region_add_subregion(address_space, 0xfc0a8000, iomem); - /* Timers. */ - for (i = 0; i < 2; i++) { - s = (m5208_timer_state *)g_malloc0(sizeof(m5208_timer_state)); - bh = qemu_bh_new(m5208_timer_trigger, s); - s->timer = ptimer_init(bh); - memory_region_init_io(&s->iomem, NULL, &m5208_timer_ops, s, - "m5208-timer", 0x00004000); - memory_region_add_subregion(address_space, 0xfc080000 + 0x4000 * i, - &s->iomem); - s->irq = pic[4 + i]; - } -} - -static void mcf5208evb_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; - M68kCPU *cpu; - CPUM68KState *env; - int kernel_size; - uint64_t elf_entry; - hwaddr entry; - qemu_irq *pic; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *sram = g_new(MemoryRegion, 1); - - if (!cpu_model) { - cpu_model = "m5208"; - } - cpu = cpu_m68k_init(cpu_model); - if (!cpu) { - fprintf(stderr, "Unable to find m68k CPU definition\n"); - exit(1); - } - env = &cpu->env; - - /* Initialize CPU registers. */ - env->vbr = 0; - /* TODO: Configure BARs. */ - - /* DRAM at 0x40000000 */ - memory_region_allocate_system_memory(ram, NULL, "mcf5208.ram", ram_size); - memory_region_add_subregion(address_space_mem, 0x40000000, ram); - - /* Internal SRAM. */ - 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); - - /* Internal peripherals. */ - pic = mcf_intc_init(address_space_mem, 0xfc048000, cpu); - - mcf_uart_mm_init(address_space_mem, 0xfc060000, pic[26], serial_hds[0]); - mcf_uart_mm_init(address_space_mem, 0xfc064000, pic[27], serial_hds[1]); - mcf_uart_mm_init(address_space_mem, 0xfc068000, pic[28], serial_hds[2]); - - mcf5208_sys_init(address_space_mem, pic); - - if (nb_nics > 1) { - fprintf(stderr, "Too many NICs\n"); - exit(1); - } - if (nd_table[0].used) - mcf_fec_init(address_space_mem, &nd_table[0], - 0xfc030000, pic + 36); - - /* 0xfc000000 SCM. */ - /* 0xfc004000 XBS. */ - /* 0xfc008000 FlexBus CS. */ - /* 0xfc030000 FEC. */ - /* 0xfc040000 SCM + Power management. */ - /* 0xfc044000 eDMA. */ - /* 0xfc048000 INTC. */ - /* 0xfc058000 I2C. */ - /* 0xfc05c000 QSPI. */ - /* 0xfc060000 UART0. */ - /* 0xfc064000 UART0. */ - /* 0xfc068000 UART0. */ - /* 0xfc070000 DMA timers. */ - /* 0xfc080000 PIT0. */ - /* 0xfc084000 PIT1. */ - /* 0xfc088000 EPORT. */ - /* 0xfc08c000 Watchdog. */ - /* 0xfc090000 clock module. */ - /* 0xfc0a0000 CCM + reset. */ - /* 0xfc0a4000 GPIO. */ - /* 0xfc0a8000 SDRAM controller. */ - - /* Load kernel. */ - if (!kernel_filename) { - if (qtest_enabled()) { - return; - } - fprintf(stderr, "Kernel image must be specified\n"); - exit(1); - } - - kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, - NULL, NULL, 1, EM_68K, 0, 0); - entry = elf_entry; - if (kernel_size < 0) { - kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL, - NULL, NULL); - } - if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, 0x40000000, - ram_size); - entry = 0x40000000; - } - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); - exit(1); - } - - env->pc = entry; -} - -static void mcf5208evb_machine_init(MachineClass *mc) -{ - mc->desc = "MCF5206EVB"; - mc->init = mcf5208evb_init; - mc->is_default = 1; -} - -DEFINE_MACHINE("mcf5208evb", mcf5208evb_machine_init) diff --git a/qemu/hw/m68k/mcf_intc.c b/qemu/hw/m68k/mcf_intc.c deleted file mode 100644 index cf581324e..000000000 --- a/qemu/hw/m68k/mcf_intc.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * ColdFire Interrupt Controller emulation. - * - * Copyright (c) 2007 CodeSourcery. - * - * 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" - -typedef struct { - MemoryRegion iomem; - uint64_t ipr; - uint64_t imr; - uint64_t ifr; - uint64_t enabled; - uint8_t icr[64]; - M68kCPU *cpu; - int active_vector; -} mcf_intc_state; - -static void mcf_intc_update(mcf_intc_state *s) -{ - uint64_t active; - int i; - int best; - int best_level; - - active = (s->ipr | s->ifr) & s->enabled & ~s->imr; - best_level = 0; - best = 64; - if (active) { - for (i = 0; i < 64; i++) { - if ((active & 1) != 0 && s->icr[i] >= best_level) { - best_level = s->icr[i]; - best = i; - } - active >>= 1; - } - } - s->active_vector = ((best == 64) ? 24 : (best + 64)); - m68k_set_irq_level(s->cpu, best_level, s->active_vector); -} - -static uint64_t mcf_intc_read(void *opaque, hwaddr addr, - unsigned size) -{ - int offset; - mcf_intc_state *s = (mcf_intc_state *)opaque; - offset = addr & 0xff; - if (offset >= 0x40 && offset < 0x80) { - return s->icr[offset - 0x40]; - } - switch (offset) { - case 0x00: - return (uint32_t)(s->ipr >> 32); - case 0x04: - return (uint32_t)s->ipr; - case 0x08: - return (uint32_t)(s->imr >> 32); - case 0x0c: - return (uint32_t)s->imr; - case 0x10: - return (uint32_t)(s->ifr >> 32); - case 0x14: - return (uint32_t)s->ifr; - case 0xe0: /* SWIACK. */ - return s->active_vector; - case 0xe1: case 0xe2: case 0xe3: case 0xe4: - case 0xe5: case 0xe6: case 0xe7: - /* LnIACK */ - hw_error("mcf_intc_read: LnIACK not implemented\n"); - default: - return 0; - } -} - -static void mcf_intc_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - int offset; - mcf_intc_state *s = (mcf_intc_state *)opaque; - offset = addr & 0xff; - if (offset >= 0x40 && offset < 0x80) { - int n = offset - 0x40; - s->icr[n] = val; - if (val == 0) - s->enabled &= ~(1ull << n); - else - s->enabled |= (1ull << n); - mcf_intc_update(s); - return; - } - switch (offset) { - case 0x00: case 0x04: - /* Ignore IPR writes. */ - return; - case 0x08: - s->imr = (s->imr & 0xffffffff) | ((uint64_t)val << 32); - break; - case 0x0c: - s->imr = (s->imr & 0xffffffff00000000ull) | (uint32_t)val; - break; - case 0x1c: - if (val & 0x40) { - s->imr = ~0ull; - } else { - s->imr |= (0x1ull << (val & 0x3f)); - } - break; - case 0x1d: - if (val & 0x40) { - s->imr = 0ull; - } else { - s->imr &= ~(0x1ull << (val & 0x3f)); - } - break; - default: - hw_error("mcf_intc_write: Bad write offset %d\n", offset); - break; - } - mcf_intc_update(s); -} - -static void mcf_intc_set_irq(void *opaque, int irq, int level) -{ - mcf_intc_state *s = (mcf_intc_state *)opaque; - if (irq >= 64) - return; - if (level) - s->ipr |= 1ull << irq; - else - s->ipr &= ~(1ull << irq); - mcf_intc_update(s); -} - -static void mcf_intc_reset(mcf_intc_state *s) -{ - s->imr = ~0ull; - s->ipr = 0; - s->ifr = 0; - s->enabled = 0; - memset(s->icr, 0, 64); - s->active_vector = 24; -} - -static const MemoryRegionOps mcf_intc_ops = { - .read = mcf_intc_read, - .write = mcf_intc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -qemu_irq *mcf_intc_init(MemoryRegion *sysmem, - hwaddr base, - M68kCPU *cpu) -{ - mcf_intc_state *s; - - s = g_malloc0(sizeof(mcf_intc_state)); - s->cpu = cpu; - mcf_intc_reset(s); - - memory_region_init_io(&s->iomem, NULL, &mcf_intc_ops, s, "mcf", 0x100); - memory_region_add_subregion(sysmem, base, &s->iomem); - - return qemu_allocate_irqs(mcf_intc_set_irq, s, 64); -} diff --git a/qemu/hw/mem/Makefile.objs b/qemu/hw/mem/Makefile.objs deleted file mode 100644 index f12f8b97a..000000000 --- a/qemu/hw/mem/Makefile.objs +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index 0a602f28b..000000000 --- a/qemu/hw/mem/nvdimm.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Non-Volatile Dual In-line Memory Module Virtualization Implementation - * - * Copyright(C) 2015 Intel Corporation. - * - * Author: - * Xiao Guangrong - * - * 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 - */ - -#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 deleted file mode 100644 index 9e7de5682..000000000 --- a/qemu/hw/mem/pc-dimm.c +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Dimm device for Memory Hotplug - * - * Copyright ProfitBricks GmbH 2012 - * Copyright (C) 2014 Red Hat Inc - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see - */ - -#include "qemu/osdep.h" -#include "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; - Error **errp; -} pc_dimms_capacity; - -void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, - MemoryRegion *mr, uint64_t align, Error **errp) -{ - int slot; - MachineState *machine = MACHINE(qdev_get_machine()); - PCDIMMDevice *dimm = PC_DIMM(dev); - Error *local_err = NULL; - uint64_t existing_dimms_capacity = 0; - uint64_t addr; - - addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err); - if (local_err) { - goto out; - } - - addr = pc_dimm_get_free_addr(hpms->base, - memory_region_size(&hpms->mr), - !addr ? NULL : &addr, align, - memory_region_size(mr), &local_err); - if (local_err) { - goto out; - } - - existing_dimms_capacity = pc_existing_dimms_capacity(&local_err); - if (local_err) { - goto out; - } - - if (existing_dimms_capacity + memory_region_size(mr) > - machine->maxram_size - machine->ram_size) { - error_setg(&local_err, "not enough space, currently 0x%" PRIx64 - " in use of total hot pluggable 0x" RAM_ADDR_FMT, - existing_dimms_capacity, - machine->maxram_size - machine->ram_size); - goto out; - } - - object_property_set_int(OBJECT(dev), addr, PC_DIMM_ADDR_PROP, &local_err); - if (local_err) { - goto out; - } - trace_mhp_pc_dimm_assigned_address(addr); - - slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, &local_err); - if (local_err) { - goto out; - } - - slot = pc_dimm_get_free_slot(slot == PC_DIMM_UNASSIGNED_SLOT ? NULL : &slot, - machine->ram_slots, &local_err); - if (local_err) { - goto out; - } - object_property_set_int(OBJECT(dev), slot, PC_DIMM_SLOT_PROP, &local_err); - if (local_err) { - goto out; - } - trace_mhp_pc_dimm_assigned_slot(slot); - - if (kvm_enabled() && !kvm_has_free_slot(machine)) { - error_setg(&local_err, "hypervisor has no free memory slots left"); - 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); - -out: - error_propagate(errp, local_err); -} - -void pc_dimm_memory_unplug(DeviceState *dev, MemoryHotplugState *hpms, - MemoryRegion *mr) -{ - PCDIMMDevice *dimm = PC_DIMM(dev); - - numa_unset_mem_node_id(dimm->addr, memory_region_size(mr), dimm->node); - memory_region_del_subregion(&hpms->mr, mr); - vmstate_unregister_ram(mr, dev); -} - -static int pc_existing_dimms_capacity_internal(Object *obj, void *opaque) -{ - pc_dimms_capacity *cap = opaque; - uint64_t *size = &cap->size; - - if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { - DeviceState *dev = DEVICE(obj); - - if (dev->realized) { - (*size) += object_property_get_int(obj, PC_DIMM_SIZE_PROP, - cap->errp); - } - - if (cap->errp && *cap->errp) { - return 1; - } - } - object_child_foreach(obj, pc_existing_dimms_capacity_internal, opaque); - return 0; -} - -uint64_t pc_existing_dimms_capacity(Error **errp) -{ - pc_dimms_capacity cap; - - cap.size = 0; - cap.errp = errp; - - pc_existing_dimms_capacity_internal(qdev_get_machine(), &cap); - return cap.size; -} - -int qmp_pc_dimm_device_list(Object *obj, void *opaque) -{ - MemoryDeviceInfoList ***prev = opaque; - - if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { - DeviceState *dev = DEVICE(obj); - - if (dev->realized) { - MemoryDeviceInfoList *elem = g_new0(MemoryDeviceInfoList, 1); - MemoryDeviceInfo *info = g_new0(MemoryDeviceInfo, 1); - PCDIMMDeviceInfo *di = g_new0(PCDIMMDeviceInfo, 1); - DeviceClass *dc = DEVICE_GET_CLASS(obj); - PCDIMMDevice *dimm = PC_DIMM(obj); - - if (dev->id) { - di->has_id = true; - di->id = g_strdup(dev->id); - } - di->hotplugged = dev->hotplugged; - di->hotpluggable = dc->hotpluggable; - di->addr = dimm->addr; - di->slot = dimm->slot; - di->node = dimm->node; - di->size = object_property_get_int(OBJECT(dimm), PC_DIMM_SIZE_PROP, - NULL); - di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem)); - - info->u.dimm.data = di; - elem->value = info; - elem->next = NULL; - **prev = elem; - *prev = &elem->next; - } - } - - object_child_foreach(obj, qmp_pc_dimm_device_list, opaque); - return 0; -} - -static int pc_dimm_slot2bitmap(Object *obj, void *opaque) -{ - unsigned long *bitmap = opaque; - - if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { - DeviceState *dev = DEVICE(obj); - if (dev->realized) { /* count only realized DIMMs */ - PCDIMMDevice *d = PC_DIMM(obj); - set_bit(d->slot, bitmap); - } - } - - object_child_foreach(obj, pc_dimm_slot2bitmap, opaque); - return 0; -} - -int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp) -{ - unsigned long *bitmap = bitmap_new(max_slots); - int slot = 0; - - object_child_foreach(qdev_get_machine(), pc_dimm_slot2bitmap, bitmap); - - /* check if requested slot is not occupied */ - if (hint) { - if (*hint >= max_slots) { - error_setg(errp, "invalid slot# %d, should be less than %d", - *hint, max_slots); - } else if (!test_bit(*hint, bitmap)) { - slot = *hint; - } else { - error_setg(errp, "slot %d is busy", *hint); - } - goto out; - } - - /* search for free slot */ - slot = find_first_zero_bit(bitmap, max_slots); - if (slot == max_slots) { - error_setg(errp, "no free slots available"); - } -out: - g_free(bitmap); - return slot; -} - -static gint pc_dimm_addr_sort(gconstpointer a, gconstpointer b) -{ - PCDIMMDevice *x = PC_DIMM(a); - PCDIMMDevice *y = PC_DIMM(b); - Int128 diff = int128_sub(int128_make64(x->addr), int128_make64(y->addr)); - - if (int128_lt(diff, int128_zero())) { - return -1; - } else if (int128_gt(diff, int128_zero())) { - return 1; - } - return 0; -} - -static int pc_dimm_built_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_insert_sorted(*list, dev, pc_dimm_addr_sort); - } - } - - object_child_foreach(obj, pc_dimm_built_list, opaque); - return 0; -} - -uint64_t pc_dimm_get_free_addr(uint64_t address_space_start, - uint64_t address_space_size, - uint64_t *hint, uint64_t align, uint64_t size, - Error **errp) -{ - GSList *list = NULL, *item; - uint64_t new_addr, ret = 0; - uint64_t address_space_end = address_space_start + address_space_size; - - g_assert(QEMU_ALIGN_UP(address_space_start, align) == address_space_start); - - if (!address_space_size) { - error_setg(errp, "memory hotplug is not enabled, " - "please add maxmem option"); - goto out; - } - - if (hint && QEMU_ALIGN_UP(*hint, align) != *hint) { - error_setg(errp, "address must be aligned to 0x%" PRIx64 " bytes", - align); - goto out; - } - - if (QEMU_ALIGN_UP(size, align) != size) { - error_setg(errp, "backend memory size must be multiple of 0x%" - PRIx64, align); - goto out; - } - - assert(address_space_end > address_space_start); - object_child_foreach(qdev_get_machine(), pc_dimm_built_list, &list); - - if (hint) { - new_addr = *hint; - } else { - new_addr = address_space_start; - } - - /* find address range that will fit new DIMM */ - for (item = list; item; item = g_slist_next(item)) { - PCDIMMDevice *dimm = item->data; - uint64_t dimm_size = object_property_get_int(OBJECT(dimm), - PC_DIMM_SIZE_PROP, - errp); - if (errp && *errp) { - goto out; - } - - if (ranges_overlap(dimm->addr, dimm_size, new_addr, size)) { - if (hint) { - DeviceState *d = DEVICE(dimm); - error_setg(errp, "address range conflicts with '%s'", d->id); - goto out; - } - new_addr = QEMU_ALIGN_UP(dimm->addr + dimm_size, align); - } - } - ret = new_addr; - - if (new_addr < address_space_start) { - error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 - "] at 0x%" PRIx64, new_addr, size, address_space_start); - } else if ((new_addr + size) > address_space_end) { - error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 - "] beyond 0x%" PRIx64, new_addr, size, address_space_end); - } - -out: - g_slist_free(list); - return ret; -} - -static Property pc_dimm_properties[] = { - DEFINE_PROP_UINT64(PC_DIMM_ADDR_PROP, PCDIMMDevice, addr, 0), - DEFINE_PROP_UINT32(PC_DIMM_NODE_PROP, PCDIMMDevice, node, 0), - DEFINE_PROP_INT32(PC_DIMM_SLOT_PROP, PCDIMMDevice, slot, - PC_DIMM_UNASSIGNED_SLOT), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - int64_t value; - MemoryRegion *mr; - PCDIMMDevice *dimm = PC_DIMM(obj); - - mr = host_memory_backend_get_memory(dimm->hostmem, errp); - value = memory_region_size(mr); - - 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), &local_err); - if (local_err) { - goto out; - } - if (memory_region_is_mapped(mr)) { - char *path = object_get_canonical_path_component(val); - 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, &local_err); - } - -out: - error_propagate(errp, local_err); -} - -static void pc_dimm_init(Object *obj) -{ - PCDIMMDevice *dimm = PC_DIMM(obj); - - object_property_add(obj, PC_DIMM_SIZE_PROP, "int", pc_dimm_get_size, - NULL, NULL, NULL, &error_abort); - object_property_add_link(obj, PC_DIMM_MEMDEV_PROP, TYPE_MEMORY_BACKEND, - (Object **)&dimm->hostmem, - pc_dimm_check_memdev_is_busy, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); -} - -static void pc_dimm_realize(DeviceState *dev, Error **errp) -{ - PCDIMMDevice *dimm = PC_DIMM(dev); - - if (!dimm->hostmem) { - error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property is not set"); - return; - } - 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 ? nb_numa_nodes : 1); - return; - } -} - -static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm) -{ - return host_memory_backend_get_memory(dimm->hostmem, &error_abort); -} - -static void pc_dimm_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc); - - dc->realize = pc_dimm_realize; - dc->props = pc_dimm_properties; - dc->desc = "DIMM memory module"; - - ddc->get_memory_region = pc_dimm_get_memory_region; -} - -static TypeInfo pc_dimm_info = { - .name = TYPE_PC_DIMM, - .parent = TYPE_DEVICE, - .instance_size = sizeof(PCDIMMDevice), - .instance_init = pc_dimm_init, - .class_init = pc_dimm_class_init, - .class_size = sizeof(PCDIMMDeviceClass), -}; - -static void pc_dimm_register_types(void) -{ - type_register_static(&pc_dimm_info); -} - -type_init(pc_dimm_register_types) diff --git a/qemu/hw/microblaze/Makefile.objs b/qemu/hw/microblaze/Makefile.objs deleted file mode 100644 index b2517d87f..000000000 --- a/qemu/hw/microblaze/Makefile.objs +++ /dev/null @@ -1,3 +0,0 @@ -obj-y += petalogix_s3adsp1800_mmu.o -obj-y += petalogix_ml605_mmu.o -obj-y += boot.o diff --git a/qemu/hw/microblaze/boot.c b/qemu/hw/microblaze/boot.c deleted file mode 100644 index 9eebb1a52..000000000 --- a/qemu/hw/microblaze/boot.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Microblaze kernel loader - * - * Copyright (c) 2012 Peter Crosthwaite - * Copyright (c) 2012 PetaLogix - * Copyright (c) 2009 Edgar E. Iglesias. - * - * 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 "qemu-common.h" -#include "cpu.h" -#include "qemu/option.h" -#include "qemu/config-file.h" -#include "qemu/error-report.h" -#include "qemu-common.h" -#include "sysemu/device_tree.h" -#include "sysemu/sysemu.h" -#include "hw/loader.h" -#include "elf.h" -#include "qemu/cutils.h" - -#include "boot.h" - -static struct -{ - void (*machine_cpu_reset)(MicroBlazeCPU *); - uint32_t bootstrap_pc; - uint32_t cmdline; - uint32_t initrd_start; - uint32_t initrd_end; - uint32_t fdt; -} boot_info; - -static void main_cpu_reset(void *opaque) -{ - MicroBlazeCPU *cpu = opaque; - CPUState *cs = CPU(cpu); - CPUMBState *env = &cpu->env; - - cpu_reset(cs); - env->regs[5] = boot_info.cmdline; - env->regs[6] = boot_info.initrd_start; - env->regs[7] = boot_info.fdt; - cpu_set_pc(cs, boot_info.bootstrap_pc); - if (boot_info.machine_cpu_reset) { - boot_info.machine_cpu_reset(cpu); - } -} - -static int microblaze_load_dtb(hwaddr addr, - uint32_t ramsize, - uint32_t initrd_start, - uint32_t initrd_end, - const char *kernel_cmdline, - const char *dtb_filename) -{ - int fdt_size; - void *fdt = NULL; - int r; - - if (dtb_filename) { - fdt = load_device_tree(dtb_filename, &fdt_size); - } - if (!fdt) { - return 0; - } - - if (kernel_cmdline) { - r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", - kernel_cmdline); - if (r < 0) { - fprintf(stderr, "couldn't set /chosen/bootargs\n"); - } - } - - if (initrd_start) { - qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", - initrd_start); - - qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", - initrd_end); - } - - cpu_physical_memory_write(addr, fdt, fdt_size); - return fdt_size; -} - -static uint64_t translate_kernel_address(void *opaque, uint64_t addr) -{ - return addr - 0x30000000LL; -} - -void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, - uint32_t ramsize, - const char *initrd_filename, - const char *dtb_filename, - void (*machine_cpu_reset)(MicroBlazeCPU *)) -{ - QemuOpts *machine_opts; - const char *kernel_filename; - const char *kernel_cmdline; - const char *dtb_arg; - char *filename = NULL; - - machine_opts = qemu_get_machine_opts(); - kernel_filename = qemu_opt_get(machine_opts, "kernel"); - kernel_cmdline = qemu_opt_get(machine_opts, "append"); - dtb_arg = qemu_opt_get(machine_opts, "dtb"); - /* default to pcbios dtb as passed by machine_init */ - if (!dtb_arg) { - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_filename); - } - - boot_info.machine_cpu_reset = machine_cpu_reset; - qemu_register_reset(main_cpu_reset, cpu); - - if (kernel_filename) { - int kernel_size; - uint64_t entry, low, high; - uint32_t base32; - int big_endian = 0; - -#ifdef TARGET_WORDS_BIGENDIAN - big_endian = 1; -#endif - - /* Boots a kernel elf binary. */ - kernel_size = load_elf(kernel_filename, NULL, NULL, - &entry, &low, &high, - 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, EM_MICROBLAZE, 0, 0); - } - /* Always boot into physical ram. */ - boot_info.bootstrap_pc = (uint32_t)entry; - - /* If it wasn't an ELF image, try an u-boot image. */ - if (kernel_size < 0) { - hwaddr uentry, loadaddr; - - kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0, - NULL, NULL); - boot_info.bootstrap_pc = uentry; - high = (loadaddr + kernel_size + 3) & ~3; - } - - /* Not an ELF image nor an u-boot image, try a RAW image. */ - if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, ddr_base, - ram_size); - boot_info.bootstrap_pc = ddr_base; - high = (ddr_base + kernel_size + 3) & ~3; - } - - if (initrd_filename) { - int initrd_size; - uint32_t initrd_offset; - - high = ROUND_UP(high + kernel_size, 4); - boot_info.initrd_start = high; - initrd_offset = boot_info.initrd_start - ddr_base; - - initrd_size = load_ramdisk(initrd_filename, - boot_info.initrd_start, - ram_size - initrd_offset); - if (initrd_size < 0) { - initrd_size = load_image_targphys(initrd_filename, - boot_info.initrd_start, - ram_size - initrd_offset); - } - if (initrd_size < 0) { - error_report("qemu: could not load initrd '%s'", - initrd_filename); - exit(EXIT_FAILURE); - } - boot_info.initrd_end = boot_info.initrd_start + initrd_size; - high = ROUND_UP(high + initrd_size, 4); - } - - boot_info.cmdline = high + 4096; - if (kernel_cmdline && strlen(kernel_cmdline)) { - pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline); - } - /* Provide a device-tree. */ - boot_info.fdt = boot_info.cmdline + 4096; - microblaze_load_dtb(boot_info.fdt, ram_size, - boot_info.initrd_start, - boot_info.initrd_end, - kernel_cmdline, - /* Preference a -dtb argument */ - dtb_arg ? dtb_arg : filename); - } - g_free(filename); -} diff --git a/qemu/hw/microblaze/boot.h b/qemu/hw/microblaze/boot.h deleted file mode 100644 index 0eb7f8e4f..000000000 --- a/qemu/hw/microblaze/boot.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __MICROBLAZE_BOOT__ -#define __MICROBLAZE_BOOT__ - -#include "hw/hw.h" - -void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, - uint32_t ramsize, - const char *initrd_filename, - const char *dtb_filename, - void (*machine_cpu_reset)(MicroBlazeCPU *)); - -#endif /* __MICROBLAZE_BOOT __ */ diff --git a/qemu/hw/microblaze/petalogix_ml605_mmu.c b/qemu/hw/microblaze/petalogix_ml605_mmu.c deleted file mode 100644 index 07527b677..000000000 --- a/qemu/hw/microblaze/petalogix_ml605_mmu.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Model of Petalogix linux reference design targeting Xilinx Spartan ml605 - * board. - * - * Copyright (c) 2011 Michal Simek - * Copyright (c) 2011 PetaLogix - * Copyright (c) 2009 Edgar E. Iglesias. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/sysbus.h" -#include "hw/hw.h" -#include "net/net.h" -#include "hw/block/flash.h" -#include "sysemu/sysemu.h" -#include "hw/devices.h" -#include "hw/boards.h" -#include "sysemu/block-backend.h" -#include "hw/char/serial.h" -#include "exec/address-spaces.h" -#include "hw/ssi/ssi.h" - -#include "boot.h" - -#include "hw/stream.h" - -#define LMB_BRAM_SIZE (128 * 1024) -#define FLASH_SIZE (32 * 1024 * 1024) - -#define BINARY_DEVICE_TREE_FILE "petalogix-ml605.dtb" - -#define NUM_SPI_FLASHES 4 - -#define SPI_BASEADDR 0x40a00000 -#define MEMORY_BASEADDR 0x50000000 -#define FLASH_BASEADDR 0x86000000 -#define INTC_BASEADDR 0x81800000 -#define TIMER_BASEADDR 0x83c00000 -#define UART16550_BASEADDR 0x83e00000 -#define AXIENET_BASEADDR 0x82780000 -#define AXIDMA_BASEADDR 0x84600000 - -#define AXIDMA_IRQ1 0 -#define AXIDMA_IRQ0 1 -#define TIMER_IRQ 2 -#define AXIENET_IRQ 3 -#define SPI_IRQ 4 -#define UART16550_IRQ 5 - -static void -petalogix_ml605_init(MachineState *machine) -{ - ram_addr_t ram_size = machine->ram_size; - MemoryRegion *address_space_mem = get_system_memory(); - DeviceState *dev, *dma, *eth0; - Object *ds, *cs; - MicroBlazeCPU *cpu; - SysBusDevice *busdev; - DriveInfo *dinfo; - int i; - MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1); - MemoryRegion *phys_ram = g_new(MemoryRegion, 1); - qemu_irq irq[32]; - - /* 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 - */ - object_property_set_int(OBJECT(cpu), 1, "use-fpu", &error_abort); - object_property_set_bool(OBJECT(cpu), true, "dcache-writeback", - &error_abort); - object_property_set_bool(OBJECT(cpu), true, "endianness", &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_ml605.lmb_bram", - 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_fatal); - vmstate_register_ram_global(phys_ram); - memory_region_add_subregion(address_space_mem, MEMORY_BASEADDR, phys_ram); - - dinfo = drive_get(IF_PFLASH, 0, 0); - /* 5th parameter 2 means bank-width - * 10th paremeter 0 means little-endian */ - pflash_cfi01_register(FLASH_BASEADDR, - NULL, "petalogix_ml605.flash", FLASH_SIZE, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - (64 * 1024), FLASH_SIZE >> 16, - 2, 0x89, 0x18, 0x0000, 0x0, 0); - - - dev = qdev_create(NULL, "xlnx.xps-intc"); - qdev_prop_set_uint32(dev, "kind-of-intr", 1 << TIMER_IRQ); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, - qdev_get_gpio_in(DEVICE(cpu), MB_CPU_IRQ)); - for (i = 0; i < 32; i++) { - irq[i] = qdev_get_gpio_in(dev, i); - } - - serial_mm_init(address_space_mem, UART16550_BASEADDR + 0x1000, 2, - irq[UART16550_IRQ], 115200, serial_hds[0], - DEVICE_LITTLE_ENDIAN); - - /* 2 timers at irq 2 @ 100 Mhz. */ - dev = qdev_create(NULL, "xlnx.xps-timer"); - qdev_prop_set_uint32(dev, "one-timer-only", 0); - qdev_prop_set_uint32(dev, "clock-frequency", 100 * 1000000); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ]); - - /* axi ethernet and dma initialization. */ - qemu_check_nic_model(&nd_table[0], "xlnx.axi-ethernet"); - eth0 = qdev_create(NULL, "xlnx.axi-ethernet"); - dma = qdev_create(NULL, "xlnx.axi-dma"); - - /* FIXME: attach to the sysbus instead */ - object_property_add_child(qdev_get_machine(), "xilinx-eth", OBJECT(eth0), - NULL); - object_property_add_child(qdev_get_machine(), "xilinx-dma", OBJECT(dma), - NULL); - - ds = object_property_get_link(OBJECT(dma), - "axistream-connected-target", NULL); - cs = object_property_get_link(OBJECT(dma), - "axistream-control-connected-target", NULL); - qdev_set_nic_properties(eth0, &nd_table[0]); - qdev_prop_set_uint32(eth0, "rxmem", 0x1000); - qdev_prop_set_uint32(eth0, "txmem", 0x1000); - object_property_set_link(OBJECT(eth0), OBJECT(ds), - "axistream-connected", &error_abort); - object_property_set_link(OBJECT(eth0), OBJECT(cs), - "axistream-control-connected", &error_abort); - qdev_init_nofail(eth0); - sysbus_mmio_map(SYS_BUS_DEVICE(eth0), 0, AXIENET_BASEADDR); - sysbus_connect_irq(SYS_BUS_DEVICE(eth0), 0, irq[AXIENET_IRQ]); - - ds = object_property_get_link(OBJECT(eth0), - "axistream-connected-target", NULL); - cs = object_property_get_link(OBJECT(eth0), - "axistream-control-connected-target", NULL); - qdev_prop_set_uint32(dma, "freqhz", 100 * 1000000); - object_property_set_link(OBJECT(dma), OBJECT(ds), - "axistream-connected", &error_abort); - object_property_set_link(OBJECT(dma), OBJECT(cs), - "axistream-control-connected", &error_abort); - qdev_init_nofail(dma); - sysbus_mmio_map(SYS_BUS_DEVICE(dma), 0, AXIDMA_BASEADDR); - sysbus_connect_irq(SYS_BUS_DEVICE(dma), 0, irq[AXIDMA_IRQ0]); - sysbus_connect_irq(SYS_BUS_DEVICE(dma), 1, irq[AXIDMA_IRQ1]); - - { - SSIBus *spi; - - dev = qdev_create(NULL, "xlnx.xps-spi"); - qdev_prop_set_uint8(dev, "num-ss-bits", NUM_SPI_FLASHES); - qdev_init_nofail(dev); - busdev = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(busdev, 0, SPI_BASEADDR); - sysbus_connect_irq(busdev, 0, irq[SPI_IRQ]); - - spi = (SSIBus *)qdev_get_child_bus(dev, "spi"); - - for (i = 0; i < NUM_SPI_FLASHES; i++) { - qemu_irq cs_line; - - dev = ssi_create_slave(spi, "n25q128"); - cs_line = qdev_get_gpio_in_named(dev, SSI_GPIO_CS, 0); - sysbus_connect_irq(busdev, i+1, cs_line); - } - } - - /* setup PVR to match kernel settings */ - cpu->env.pvr.regs[4] = 0xc56b8000; - cpu->env.pvr.regs[5] = 0xc56be000; - cpu->env.pvr.regs[10] = 0x0e000000; /* virtex 6 */ - - microblaze_load_kernel(cpu, MEMORY_BASEADDR, ram_size, - machine->initrd_filename, - BINARY_DEVICE_TREE_FILE, - NULL); - -} - -static void petalogix_ml605_machine_init(MachineClass *mc) -{ - mc->desc = "PetaLogix linux refdesign for xilinx ml605 little endian"; - mc->init = petalogix_ml605_init; - mc->is_default = 0; -} - -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 deleted file mode 100644 index f821e1cfe..000000000 --- a/qemu/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Model of Petalogix linux reference design targeting Xilinx Spartan 3ADSP-1800 - * boards. - * - * Copyright (c) 2009 Edgar E. Iglesias. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/sysbus.h" -#include "hw/hw.h" -#include "net/net.h" -#include "hw/block/flash.h" -#include "sysemu/sysemu.h" -#include "hw/devices.h" -#include "hw/boards.h" -#include "sysemu/block-backend.h" -#include "exec/address-spaces.h" - -#include "boot.h" - -#define LMB_BRAM_SIZE (128 * 1024) -#define FLASH_SIZE (16 * 1024 * 1024) - -#define BINARY_DEVICE_TREE_FILE "petalogix-s3adsp1800.dtb" - -#define MEMORY_BASEADDR 0x90000000 -#define FLASH_BASEADDR 0xa0000000 -#define INTC_BASEADDR 0x81800000 -#define TIMER_BASEADDR 0x83c00000 -#define UARTLITE_BASEADDR 0x84000000 -#define ETHLITE_BASEADDR 0x81000000 - -#define TIMER_IRQ 0 -#define ETHLITE_IRQ 1 -#define UARTLITE_IRQ 3 - -static void -petalogix_s3adsp1800_init(MachineState *machine) -{ - ram_addr_t ram_size = machine->ram_size; - DeviceState *dev; - MicroBlazeCPU *cpu; - DriveInfo *dinfo; - int i; - hwaddr ddr_base = MEMORY_BASEADDR; - MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1); - MemoryRegion *phys_ram = g_new(MemoryRegion, 1); - qemu_irq irq[32]; - 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_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_fatal); - vmstate_register_ram_global(phys_ram); - memory_region_add_subregion(sysmem, ddr_base, phys_ram); - - dinfo = drive_get(IF_PFLASH, 0, 0); - pflash_cfi01_register(FLASH_BASEADDR, - NULL, "petalogix_s3adsp1800.flash", FLASH_SIZE, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - (64 * 1024), FLASH_SIZE >> 16, - 1, 0x89, 0x18, 0x0000, 0x0, 1); - - dev = qdev_create(NULL, "xlnx.xps-intc"); - qdev_prop_set_uint32(dev, "kind-of-intr", - 1 << ETHLITE_IRQ | 1 << UARTLITE_IRQ); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, - qdev_get_gpio_in(DEVICE(cpu), MB_CPU_IRQ)); - for (i = 0; i < 32; i++) { - irq[i] = qdev_get_gpio_in(dev, i); - } - - sysbus_create_simple("xlnx.xps-uartlite", UARTLITE_BASEADDR, - irq[UARTLITE_IRQ]); - - /* 2 timers at irq 2 @ 62 Mhz. */ - dev = qdev_create(NULL, "xlnx.xps-timer"); - qdev_prop_set_uint32(dev, "one-timer-only", 0); - qdev_prop_set_uint32(dev, "clock-frequency", 62 * 1000000); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ]); - - qemu_check_nic_model(&nd_table[0], "xlnx.xps-ethernetlite"); - dev = qdev_create(NULL, "xlnx.xps-ethernetlite"); - qdev_set_nic_properties(dev, &nd_table[0]); - qdev_prop_set_uint32(dev, "tx-ping-pong", 0); - qdev_prop_set_uint32(dev, "rx-ping-pong", 0); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, ETHLITE_BASEADDR); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[ETHLITE_IRQ]); - - microblaze_load_kernel(cpu, ddr_base, ram_size, - machine->initrd_filename, - BINARY_DEVICE_TREE_FILE, - NULL); -} - -static void petalogix_s3adsp1800_machine_init(MachineClass *mc) -{ - mc->desc = "PetaLogix linux refdesign for xilinx Spartan 3ADSP1800"; - mc->init = petalogix_s3adsp1800_init; - mc->is_default = 1; -} - -DEFINE_MACHINE("petalogix-s3adsp1800", petalogix_s3adsp1800_machine_init) diff --git a/qemu/hw/mips/Makefile.objs b/qemu/hw/mips/Makefile.objs deleted file mode 100644 index 9352a1c06..000000000 --- a/qemu/hw/mips/Makefile.objs +++ /dev/null @@ -1,6 +0,0 @@ -obj-y += mips_r4k.o mips_malta.o mips_mipssim.o -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 deleted file mode 100644 index e4e86b4a7..000000000 --- a/qemu/hw/mips/addr.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * QEMU MIPS address translation support - * - * 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/mips/cpudevs.h" - -uint64_t cpu_mips_kseg0_to_phys(void *opaque, uint64_t addr) -{ - return addr & 0x1fffffffll; -} - -uint64_t cpu_mips_phys_to_kseg0(void *opaque, uint64_t addr) -{ - return addr | ~0x7fffffffll; -} - -uint64_t cpu_mips_kvm_um_phys_to_kseg0(void *opaque, uint64_t addr) -{ - return addr | 0x40000000ll; -} diff --git a/qemu/hw/mips/cps.c b/qemu/hw/mips/cps.c deleted file mode 100644 index 1bafbbb27..000000000 --- a/qemu/hw/mips/cps.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * 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 . - */ - -#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 deleted file mode 100644 index efb227d06..000000000 --- a/qemu/hw/mips/cputimer.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * QEMU MIPS timer support - * - * 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/mips/cpudevs.h" -#include "qemu/timer.h" -#include "sysemu/kvm.h" - -#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 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 { - /* 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; -} - -/* MIPS R4K timer */ -static void cpu_mips_timer_update(CPUMIPSState *env) -{ - uint64_t now, next; - uint32_t wait; - - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - wait = env->CP0_Compare - env->CP0_Count - (uint32_t)(now / TIMER_PERIOD); - next = now + (uint64_t)wait * TIMER_PERIOD; - timer_mod(env->timer, next); -} - -/* Expire the timer. */ -static void cpu_mips_timer_expire(CPUMIPSState *env) -{ - cpu_mips_timer_update(env); - if (env->insn_flags & ISA_MIPS32R2) { - env->CP0_Cause |= 1 << CP0Ca_TI; - } - qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]); -} - -uint32_t cpu_mips_get_count (CPUMIPSState *env) -{ - if (env->CP0_Cause & (1 << CP0Ca_DC)) { - return env->CP0_Count; - } else { - uint64_t now; - - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - if (timer_pending(env->timer) - && timer_expired(env->timer, now)) { - /* The timer has already expired. */ - cpu_mips_timer_expire(env); - } - - return env->CP0_Count + (uint32_t)(now / TIMER_PERIOD); - } -} - -void cpu_mips_store_count (CPUMIPSState *env, uint32_t count) -{ - /* - * This gets called from cpu_state_reset(), potentially before timer init. - * So env->timer may be NULL, which is also the case with KVM enabled so - * treat timer as disabled in that case. - */ - if (env->CP0_Cause & (1 << CP0Ca_DC) || !env->timer) - env->CP0_Count = count; - else { - /* Store new count register */ - env->CP0_Count = count - - (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD); - /* Update timer timer */ - cpu_mips_timer_update(env); - } -} - -void cpu_mips_store_compare (CPUMIPSState *env, uint32_t value) -{ - env->CP0_Compare = value; - if (!(env->CP0_Cause & (1 << CP0Ca_DC))) - cpu_mips_timer_update(env); - if (env->insn_flags & ISA_MIPS32R2) - env->CP0_Cause &= ~(1 << CP0Ca_TI); - qemu_irq_lower(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]); -} - -void cpu_mips_start_count(CPUMIPSState *env) -{ - cpu_mips_store_count(env, env->CP0_Count); -} - -void cpu_mips_stop_count(CPUMIPSState *env) -{ - /* Store the current value */ - env->CP0_Count += (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / - TIMER_PERIOD); -} - -static void mips_timer_cb (void *opaque) -{ - CPUMIPSState *env; - - env = opaque; -#if 0 - qemu_log("%s\n", __func__); -#endif - - if (env->CP0_Cause & (1 << CP0Ca_DC)) - return; - - /* ??? This callback should occur when the counter is exactly equal to - the comparator value. Offset the count by one to avoid immediately - retriggering the callback before any virtual time has passed. */ - env->CP0_Count++; - cpu_mips_timer_expire(env); - env->CP0_Count--; -} - -void cpu_mips_clock_init (CPUMIPSState *env) -{ - /* - * If we're in KVM mode, don't create the periodic timer, that is handled in - * kernel. - */ - if (!kvm_enabled()) { - env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &mips_timer_cb, env); - } -} diff --git a/qemu/hw/mips/gt64xxx_pci.c b/qemu/hw/mips/gt64xxx_pci.c deleted file mode 100644 index 3f4523df2..000000000 --- a/qemu/hw/mips/gt64xxx_pci.c +++ /dev/null @@ -1,1259 +0,0 @@ -/* - * QEMU GT64120 PCI host - * - * Copyright (c) 2006,2007 Aurelien Jarno - * - * 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/mips/mips.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "hw/i386/pc.h" -#include "exec/address-spaces.h" - -//#define DEBUG - -#ifdef DEBUG -#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__) -#else -#define DPRINTF(fmt, ...) -#endif - -#define GT_REGS (0x1000 >> 2) - -/* CPU Configuration */ -#define GT_CPU (0x000 >> 2) -#define GT_MULTI (0x120 >> 2) - -/* CPU Address Decode */ -#define GT_SCS10LD (0x008 >> 2) -#define GT_SCS10HD (0x010 >> 2) -#define GT_SCS32LD (0x018 >> 2) -#define GT_SCS32HD (0x020 >> 2) -#define GT_CS20LD (0x028 >> 2) -#define GT_CS20HD (0x030 >> 2) -#define GT_CS3BOOTLD (0x038 >> 2) -#define GT_CS3BOOTHD (0x040 >> 2) -#define GT_PCI0IOLD (0x048 >> 2) -#define GT_PCI0IOHD (0x050 >> 2) -#define GT_PCI0M0LD (0x058 >> 2) -#define GT_PCI0M0HD (0x060 >> 2) -#define GT_PCI0M1LD (0x080 >> 2) -#define GT_PCI0M1HD (0x088 >> 2) -#define GT_PCI1IOLD (0x090 >> 2) -#define GT_PCI1IOHD (0x098 >> 2) -#define GT_PCI1M0LD (0x0a0 >> 2) -#define GT_PCI1M0HD (0x0a8 >> 2) -#define GT_PCI1M1LD (0x0b0 >> 2) -#define GT_PCI1M1HD (0x0b8 >> 2) -#define GT_ISD (0x068 >> 2) - -#define GT_SCS10AR (0x0d0 >> 2) -#define GT_SCS32AR (0x0d8 >> 2) -#define GT_CS20R (0x0e0 >> 2) -#define GT_CS3BOOTR (0x0e8 >> 2) - -#define GT_PCI0IOREMAP (0x0f0 >> 2) -#define GT_PCI0M0REMAP (0x0f8 >> 2) -#define GT_PCI0M1REMAP (0x100 >> 2) -#define GT_PCI1IOREMAP (0x108 >> 2) -#define GT_PCI1M0REMAP (0x110 >> 2) -#define GT_PCI1M1REMAP (0x118 >> 2) - -/* CPU Error Report */ -#define GT_CPUERR_ADDRLO (0x070 >> 2) -#define GT_CPUERR_ADDRHI (0x078 >> 2) -#define GT_CPUERR_DATALO (0x128 >> 2) /* GT-64120A only */ -#define GT_CPUERR_DATAHI (0x130 >> 2) /* GT-64120A only */ -#define GT_CPUERR_PARITY (0x138 >> 2) /* GT-64120A only */ - -/* CPU Sync Barrier */ -#define GT_PCI0SYNC (0x0c0 >> 2) -#define GT_PCI1SYNC (0x0c8 >> 2) - -/* SDRAM and Device Address Decode */ -#define GT_SCS0LD (0x400 >> 2) -#define GT_SCS0HD (0x404 >> 2) -#define GT_SCS1LD (0x408 >> 2) -#define GT_SCS1HD (0x40c >> 2) -#define GT_SCS2LD (0x410 >> 2) -#define GT_SCS2HD (0x414 >> 2) -#define GT_SCS3LD (0x418 >> 2) -#define GT_SCS3HD (0x41c >> 2) -#define GT_CS0LD (0x420 >> 2) -#define GT_CS0HD (0x424 >> 2) -#define GT_CS1LD (0x428 >> 2) -#define GT_CS1HD (0x42c >> 2) -#define GT_CS2LD (0x430 >> 2) -#define GT_CS2HD (0x434 >> 2) -#define GT_CS3LD (0x438 >> 2) -#define GT_CS3HD (0x43c >> 2) -#define GT_BOOTLD (0x440 >> 2) -#define GT_BOOTHD (0x444 >> 2) -#define GT_ADERR (0x470 >> 2) - -/* SDRAM Configuration */ -#define GT_SDRAM_CFG (0x448 >> 2) -#define GT_SDRAM_OPMODE (0x474 >> 2) -#define GT_SDRAM_BM (0x478 >> 2) -#define GT_SDRAM_ADDRDECODE (0x47c >> 2) - -/* SDRAM Parameters */ -#define GT_SDRAM_B0 (0x44c >> 2) -#define GT_SDRAM_B1 (0x450 >> 2) -#define GT_SDRAM_B2 (0x454 >> 2) -#define GT_SDRAM_B3 (0x458 >> 2) - -/* Device Parameters */ -#define GT_DEV_B0 (0x45c >> 2) -#define GT_DEV_B1 (0x460 >> 2) -#define GT_DEV_B2 (0x464 >> 2) -#define GT_DEV_B3 (0x468 >> 2) -#define GT_DEV_BOOT (0x46c >> 2) - -/* ECC */ -#define GT_ECC_ERRDATALO (0x480 >> 2) /* GT-64120A only */ -#define GT_ECC_ERRDATAHI (0x484 >> 2) /* GT-64120A only */ -#define GT_ECC_MEM (0x488 >> 2) /* GT-64120A only */ -#define GT_ECC_CALC (0x48c >> 2) /* GT-64120A only */ -#define GT_ECC_ERRADDR (0x490 >> 2) /* GT-64120A only */ - -/* DMA Record */ -#define GT_DMA0_CNT (0x800 >> 2) -#define GT_DMA1_CNT (0x804 >> 2) -#define GT_DMA2_CNT (0x808 >> 2) -#define GT_DMA3_CNT (0x80c >> 2) -#define GT_DMA0_SA (0x810 >> 2) -#define GT_DMA1_SA (0x814 >> 2) -#define GT_DMA2_SA (0x818 >> 2) -#define GT_DMA3_SA (0x81c >> 2) -#define GT_DMA0_DA (0x820 >> 2) -#define GT_DMA1_DA (0x824 >> 2) -#define GT_DMA2_DA (0x828 >> 2) -#define GT_DMA3_DA (0x82c >> 2) -#define GT_DMA0_NEXT (0x830 >> 2) -#define GT_DMA1_NEXT (0x834 >> 2) -#define GT_DMA2_NEXT (0x838 >> 2) -#define GT_DMA3_NEXT (0x83c >> 2) -#define GT_DMA0_CUR (0x870 >> 2) -#define GT_DMA1_CUR (0x874 >> 2) -#define GT_DMA2_CUR (0x878 >> 2) -#define GT_DMA3_CUR (0x87c >> 2) - -/* DMA Channel Control */ -#define GT_DMA0_CTRL (0x840 >> 2) -#define GT_DMA1_CTRL (0x844 >> 2) -#define GT_DMA2_CTRL (0x848 >> 2) -#define GT_DMA3_CTRL (0x84c >> 2) - -/* DMA Arbiter */ -#define GT_DMA_ARB (0x860 >> 2) - -/* Timer/Counter */ -#define GT_TC0 (0x850 >> 2) -#define GT_TC1 (0x854 >> 2) -#define GT_TC2 (0x858 >> 2) -#define GT_TC3 (0x85c >> 2) -#define GT_TC_CONTROL (0x864 >> 2) - -/* PCI Internal */ -#define GT_PCI0_CMD (0xc00 >> 2) -#define GT_PCI0_TOR (0xc04 >> 2) -#define GT_PCI0_BS_SCS10 (0xc08 >> 2) -#define GT_PCI0_BS_SCS32 (0xc0c >> 2) -#define GT_PCI0_BS_CS20 (0xc10 >> 2) -#define GT_PCI0_BS_CS3BT (0xc14 >> 2) -#define GT_PCI1_IACK (0xc30 >> 2) -#define GT_PCI0_IACK (0xc34 >> 2) -#define GT_PCI0_BARE (0xc3c >> 2) -#define GT_PCI0_PREFMBR (0xc40 >> 2) -#define GT_PCI0_SCS10_BAR (0xc48 >> 2) -#define GT_PCI0_SCS32_BAR (0xc4c >> 2) -#define GT_PCI0_CS20_BAR (0xc50 >> 2) -#define GT_PCI0_CS3BT_BAR (0xc54 >> 2) -#define GT_PCI0_SSCS10_BAR (0xc58 >> 2) -#define GT_PCI0_SSCS32_BAR (0xc5c >> 2) -#define GT_PCI0_SCS3BT_BAR (0xc64 >> 2) -#define GT_PCI1_CMD (0xc80 >> 2) -#define GT_PCI1_TOR (0xc84 >> 2) -#define GT_PCI1_BS_SCS10 (0xc88 >> 2) -#define GT_PCI1_BS_SCS32 (0xc8c >> 2) -#define GT_PCI1_BS_CS20 (0xc90 >> 2) -#define GT_PCI1_BS_CS3BT (0xc94 >> 2) -#define GT_PCI1_BARE (0xcbc >> 2) -#define GT_PCI1_PREFMBR (0xcc0 >> 2) -#define GT_PCI1_SCS10_BAR (0xcc8 >> 2) -#define GT_PCI1_SCS32_BAR (0xccc >> 2) -#define GT_PCI1_CS20_BAR (0xcd0 >> 2) -#define GT_PCI1_CS3BT_BAR (0xcd4 >> 2) -#define GT_PCI1_SSCS10_BAR (0xcd8 >> 2) -#define GT_PCI1_SSCS32_BAR (0xcdc >> 2) -#define GT_PCI1_SCS3BT_BAR (0xce4 >> 2) -#define GT_PCI1_CFGADDR (0xcf0 >> 2) -#define GT_PCI1_CFGDATA (0xcf4 >> 2) -#define GT_PCI0_CFGADDR (0xcf8 >> 2) -#define GT_PCI0_CFGDATA (0xcfc >> 2) - -/* Interrupts */ -#define GT_INTRCAUSE (0xc18 >> 2) -#define GT_INTRMASK (0xc1c >> 2) -#define GT_PCI0_ICMASK (0xc24 >> 2) -#define GT_PCI0_SERR0MASK (0xc28 >> 2) -#define GT_CPU_INTSEL (0xc70 >> 2) -#define GT_PCI0_INTSEL (0xc74 >> 2) -#define GT_HINTRCAUSE (0xc98 >> 2) -#define GT_HINTRMASK (0xc9c >> 2) -#define GT_PCI0_HICMASK (0xca4 >> 2) -#define GT_PCI1_SERR1MASK (0xca8 >> 2) - -#define PCI_MAPPING_ENTRY(regname) \ - hwaddr regname ##_start; \ - hwaddr regname ##_length; \ - MemoryRegion regname ##_mem - -#define TYPE_GT64120_PCI_HOST_BRIDGE "gt64120" - -#define GT64120_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(GT64120State, (obj), TYPE_GT64120_PCI_HOST_BRIDGE) - -typedef struct GT64120State { - PCIHostState parent_obj; - - uint32_t regs[GT_REGS]; - PCI_MAPPING_ENTRY(PCI0IO); - PCI_MAPPING_ENTRY(PCI0M0); - PCI_MAPPING_ENTRY(PCI0M1); - PCI_MAPPING_ENTRY(ISD); - MemoryRegion pci0_mem; - AddressSpace pci0_mem_as; -} GT64120State; - -/* Adjust range to avoid touching space which isn't mappable via PCI */ -/* XXX: Hardcoded values for Malta: 0x1e000000 - 0x1f100000 - 0x1fc00000 - 0x1fd00000 */ -static void check_reserved_space (hwaddr *start, - hwaddr *length) -{ - hwaddr begin = *start; - hwaddr end = *start + *length; - - if (end >= 0x1e000000LL && end < 0x1f100000LL) - end = 0x1e000000LL; - if (begin >= 0x1e000000LL && begin < 0x1f100000LL) - begin = 0x1f100000LL; - if (end >= 0x1fc00000LL && end < 0x1fd00000LL) - end = 0x1fc00000LL; - if (begin >= 0x1fc00000LL && begin < 0x1fd00000LL) - begin = 0x1fd00000LL; - /* XXX: This is broken when a reserved range splits the requested range */ - if (end >= 0x1f100000LL && begin < 0x1e000000LL) - end = 0x1e000000LL; - if (end >= 0x1fd00000LL && begin < 0x1fc00000LL) - end = 0x1fc00000LL; - - *start = begin; - *length = end - begin; -} - -static void gt64120_isd_mapping(GT64120State *s) -{ - /* 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) { - memory_region_del_subregion(get_system_memory(), &s->ISD_mem); - } - check_reserved_space(&start, &length); - length = 0x1000; - /* Map new address */ - DPRINTF("ISD: "TARGET_FMT_plx"@"TARGET_FMT_plx - " -> "TARGET_FMT_plx"@"TARGET_FMT_plx"\n", - s->ISD_length, s->ISD_start, length, start); - s->ISD_start = start; - s->ISD_length = length; - memory_region_add_subregion(get_system_memory(), s->ISD_start, &s->ISD_mem); -} - -static void gt64120_pci_mapping(GT64120State *s) -{ - /* Update PCI0IO mapping */ - if ((s->regs[GT_PCI0IOLD] & 0x7f) <= s->regs[GT_PCI0IOHD]) { - /* Unmap old IO address */ - if (s->PCI0IO_length) { - memory_region_del_subregion(get_system_memory(), &s->PCI0IO_mem); - object_unparent(OBJECT(&s->PCI0IO_mem)); - } - /* Map new IO address */ - s->PCI0IO_start = s->regs[GT_PCI0IOLD] << 21; - s->PCI0IO_length = ((s->regs[GT_PCI0IOHD] + 1) - - (s->regs[GT_PCI0IOLD] & 0x7f)) << 21; - if (s->PCI0IO_length) { - memory_region_init_alias(&s->PCI0IO_mem, OBJECT(s), "pci0-io", - get_system_io(), 0, s->PCI0IO_length); - memory_region_add_subregion(get_system_memory(), s->PCI0IO_start, - &s->PCI0IO_mem); - } - } - - /* Update PCI0M0 mapping */ - if ((s->regs[GT_PCI0M0LD] & 0x7f) <= s->regs[GT_PCI0M0HD]) { - /* Unmap old MEM address */ - if (s->PCI0M0_length) { - memory_region_del_subregion(get_system_memory(), &s->PCI0M0_mem); - object_unparent(OBJECT(&s->PCI0M0_mem)); - } - /* Map new mem address */ - s->PCI0M0_start = s->regs[GT_PCI0M0LD] << 21; - s->PCI0M0_length = ((s->regs[GT_PCI0M0HD] + 1) - - (s->regs[GT_PCI0M0LD] & 0x7f)) << 21; - if (s->PCI0M0_length) { - memory_region_init_alias(&s->PCI0M0_mem, OBJECT(s), "pci0-mem0", - &s->pci0_mem, s->PCI0M0_start, - s->PCI0M0_length); - memory_region_add_subregion(get_system_memory(), s->PCI0M0_start, - &s->PCI0M0_mem); - } - } - - /* Update PCI0M1 mapping */ - if ((s->regs[GT_PCI0M1LD] & 0x7f) <= s->regs[GT_PCI0M1HD]) { - /* Unmap old MEM address */ - if (s->PCI0M1_length) { - memory_region_del_subregion(get_system_memory(), &s->PCI0M1_mem); - object_unparent(OBJECT(&s->PCI0M1_mem)); - } - /* Map new mem address */ - s->PCI0M1_start = s->regs[GT_PCI0M1LD] << 21; - s->PCI0M1_length = ((s->regs[GT_PCI0M1HD] + 1) - - (s->regs[GT_PCI0M1LD] & 0x7f)) << 21; - if (s->PCI0M1_length) { - memory_region_init_alias(&s->PCI0M1_mem, OBJECT(s), "pci0-mem1", - &s->pci0_mem, s->PCI0M1_start, - s->PCI0M1_length); - memory_region_add_subregion(get_system_memory(), s->PCI0M1_start, - &s->PCI0M1_mem); - } - } -} - -static int gt64120_post_load(void *opaque, int version_id) -{ - GT64120State *s = opaque; - - gt64120_isd_mapping(s); - gt64120_pci_mapping(s); - - return 0; -} - -static const VMStateDescription vmstate_gt64120 = { - .name = "gt64120", - .version_id = 1, - .minimum_version_id = 1, - .post_load = gt64120_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, GT64120State, GT_REGS), - VMSTATE_END_OF_LIST() - } -}; - -static void gt64120_writel (void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - GT64120State *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); - uint32_t saddr; - - if (!(s->regs[GT_CPU] & 0x00001000)) - val = bswap32(val); - - saddr = (addr & 0xfff) >> 2; - switch (saddr) { - - /* CPU Configuration */ - case GT_CPU: - s->regs[GT_CPU] = val; - break; - case GT_MULTI: - /* Read-only register as only one GT64xxx is present on the CPU bus */ - break; - - /* CPU Address Decode */ - case GT_PCI0IOLD: - s->regs[GT_PCI0IOLD] = val & 0x00007fff; - s->regs[GT_PCI0IOREMAP] = val & 0x000007ff; - gt64120_pci_mapping(s); - break; - case GT_PCI0M0LD: - s->regs[GT_PCI0M0LD] = val & 0x00007fff; - s->regs[GT_PCI0M0REMAP] = val & 0x000007ff; - gt64120_pci_mapping(s); - break; - case GT_PCI0M1LD: - s->regs[GT_PCI0M1LD] = val & 0x00007fff; - s->regs[GT_PCI0M1REMAP] = val & 0x000007ff; - gt64120_pci_mapping(s); - break; - case GT_PCI1IOLD: - s->regs[GT_PCI1IOLD] = val & 0x00007fff; - s->regs[GT_PCI1IOREMAP] = val & 0x000007ff; - break; - case GT_PCI1M0LD: - s->regs[GT_PCI1M0LD] = val & 0x00007fff; - s->regs[GT_PCI1M0REMAP] = val & 0x000007ff; - break; - case GT_PCI1M1LD: - s->regs[GT_PCI1M1LD] = val & 0x00007fff; - s->regs[GT_PCI1M1REMAP] = val & 0x000007ff; - break; - case GT_PCI0M0HD: - case GT_PCI0M1HD: - case GT_PCI0IOHD: - s->regs[saddr] = val & 0x0000007f; - gt64120_pci_mapping(s); - break; - case GT_PCI1IOHD: - case GT_PCI1M0HD: - case GT_PCI1M1HD: - s->regs[saddr] = val & 0x0000007f; - break; - case GT_ISD: - s->regs[saddr] = val & 0x00007fff; - gt64120_isd_mapping(s); - break; - - case GT_PCI0IOREMAP: - case GT_PCI0M0REMAP: - case GT_PCI0M1REMAP: - case GT_PCI1IOREMAP: - case GT_PCI1M0REMAP: - case GT_PCI1M1REMAP: - s->regs[saddr] = val & 0x000007ff; - break; - - /* CPU Error Report */ - case GT_CPUERR_ADDRLO: - case GT_CPUERR_ADDRHI: - case GT_CPUERR_DATALO: - case GT_CPUERR_DATAHI: - case GT_CPUERR_PARITY: - /* Read-only registers, do nothing */ - break; - - /* CPU Sync Barrier */ - case GT_PCI0SYNC: - case GT_PCI1SYNC: - /* Read-only registers, do nothing */ - break; - - /* SDRAM and Device Address Decode */ - case GT_SCS0LD: - case GT_SCS0HD: - case GT_SCS1LD: - case GT_SCS1HD: - case GT_SCS2LD: - case GT_SCS2HD: - case GT_SCS3LD: - case GT_SCS3HD: - case GT_CS0LD: - case GT_CS0HD: - case GT_CS1LD: - case GT_CS1HD: - case GT_CS2LD: - case GT_CS2HD: - case GT_CS3LD: - case GT_CS3HD: - case GT_BOOTLD: - case GT_BOOTHD: - case GT_ADERR: - /* SDRAM Configuration */ - case GT_SDRAM_CFG: - case GT_SDRAM_OPMODE: - case GT_SDRAM_BM: - case GT_SDRAM_ADDRDECODE: - /* Accept and ignore SDRAM interleave configuration */ - s->regs[saddr] = val; - break; - - /* Device Parameters */ - case GT_DEV_B0: - case GT_DEV_B1: - case GT_DEV_B2: - case GT_DEV_B3: - case GT_DEV_BOOT: - /* Not implemented */ - DPRINTF ("Unimplemented device register offset 0x%x\n", saddr << 2); - break; - - /* ECC */ - case GT_ECC_ERRDATALO: - case GT_ECC_ERRDATAHI: - case GT_ECC_MEM: - case GT_ECC_CALC: - case GT_ECC_ERRADDR: - /* Read-only registers, do nothing */ - break; - - /* DMA Record */ - case GT_DMA0_CNT: - case GT_DMA1_CNT: - case GT_DMA2_CNT: - case GT_DMA3_CNT: - case GT_DMA0_SA: - case GT_DMA1_SA: - case GT_DMA2_SA: - case GT_DMA3_SA: - case GT_DMA0_DA: - case GT_DMA1_DA: - case GT_DMA2_DA: - case GT_DMA3_DA: - case GT_DMA0_NEXT: - case GT_DMA1_NEXT: - case GT_DMA2_NEXT: - case GT_DMA3_NEXT: - case GT_DMA0_CUR: - case GT_DMA1_CUR: - case GT_DMA2_CUR: - case GT_DMA3_CUR: - /* Not implemented */ - DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2); - break; - - /* DMA Channel Control */ - case GT_DMA0_CTRL: - case GT_DMA1_CTRL: - case GT_DMA2_CTRL: - case GT_DMA3_CTRL: - /* Not implemented */ - DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2); - break; - - /* DMA Arbiter */ - case GT_DMA_ARB: - /* Not implemented */ - DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2); - break; - - /* Timer/Counter */ - case GT_TC0: - case GT_TC1: - case GT_TC2: - case GT_TC3: - case GT_TC_CONTROL: - /* Not implemented */ - DPRINTF ("Unimplemented timer register offset 0x%x\n", saddr << 2); - break; - - /* PCI Internal */ - case GT_PCI0_CMD: - case GT_PCI1_CMD: - s->regs[saddr] = val & 0x0401fc0f; - break; - case GT_PCI0_TOR: - case GT_PCI0_BS_SCS10: - case GT_PCI0_BS_SCS32: - case GT_PCI0_BS_CS20: - case GT_PCI0_BS_CS3BT: - case GT_PCI1_IACK: - case GT_PCI0_IACK: - case GT_PCI0_BARE: - case GT_PCI0_PREFMBR: - case GT_PCI0_SCS10_BAR: - case GT_PCI0_SCS32_BAR: - case GT_PCI0_CS20_BAR: - case GT_PCI0_CS3BT_BAR: - case GT_PCI0_SSCS10_BAR: - case GT_PCI0_SSCS32_BAR: - case GT_PCI0_SCS3BT_BAR: - case GT_PCI1_TOR: - case GT_PCI1_BS_SCS10: - case GT_PCI1_BS_SCS32: - case GT_PCI1_BS_CS20: - case GT_PCI1_BS_CS3BT: - case GT_PCI1_BARE: - case GT_PCI1_PREFMBR: - case GT_PCI1_SCS10_BAR: - case GT_PCI1_SCS32_BAR: - case GT_PCI1_CS20_BAR: - case GT_PCI1_CS3BT_BAR: - case GT_PCI1_SSCS10_BAR: - case GT_PCI1_SSCS32_BAR: - case GT_PCI1_SCS3BT_BAR: - case GT_PCI1_CFGADDR: - case GT_PCI1_CFGDATA: - /* not implemented */ - break; - case GT_PCI0_CFGADDR: - phb->config_reg = val & 0x80fffffc; - break; - case GT_PCI0_CFGDATA: - if (!(s->regs[GT_PCI0_CMD] & 1) && (phb->config_reg & 0x00fff800)) { - val = bswap32(val); - } - if (phb->config_reg & (1u << 31)) { - pci_data_write(phb->bus, phb->config_reg, val, 4); - } - break; - - /* Interrupts */ - case GT_INTRCAUSE: - /* not really implemented */ - s->regs[saddr] = ~(~(s->regs[saddr]) | ~(val & 0xfffffffe)); - s->regs[saddr] |= !!(s->regs[saddr] & 0xfffffffe); - DPRINTF("INTRCAUSE %" PRIx64 "\n", val); - break; - case GT_INTRMASK: - s->regs[saddr] = val & 0x3c3ffffe; - DPRINTF("INTRMASK %" PRIx64 "\n", val); - break; - case GT_PCI0_ICMASK: - s->regs[saddr] = val & 0x03fffffe; - DPRINTF("ICMASK %" PRIx64 "\n", val); - break; - case GT_PCI0_SERR0MASK: - s->regs[saddr] = val & 0x0000003f; - DPRINTF("SERR0MASK %" PRIx64 "\n", val); - break; - - /* Reserved when only PCI_0 is configured. */ - case GT_HINTRCAUSE: - case GT_CPU_INTSEL: - case GT_PCI0_INTSEL: - case GT_HINTRMASK: - case GT_PCI0_HICMASK: - case GT_PCI1_SERR1MASK: - /* not implemented */ - break; - - /* SDRAM Parameters */ - case GT_SDRAM_B0: - case GT_SDRAM_B1: - case GT_SDRAM_B2: - case GT_SDRAM_B3: - /* We don't simulate electrical parameters of the SDRAM. - Accept, but ignore the values. */ - s->regs[saddr] = val; - break; - - default: - DPRINTF ("Bad register offset 0x%x\n", (int)addr); - break; - } -} - -static uint64_t gt64120_readl (void *opaque, - hwaddr addr, unsigned size) -{ - GT64120State *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); - uint32_t val; - uint32_t saddr; - - saddr = (addr & 0xfff) >> 2; - switch (saddr) { - - /* CPU Configuration */ - case GT_MULTI: - /* Only one GT64xxx is present on the CPU bus, return - the initial value */ - val = s->regs[saddr]; - break; - - /* CPU Error Report */ - case GT_CPUERR_ADDRLO: - case GT_CPUERR_ADDRHI: - case GT_CPUERR_DATALO: - case GT_CPUERR_DATAHI: - case GT_CPUERR_PARITY: - /* Emulated memory has no error, always return the initial - values */ - val = s->regs[saddr]; - break; - - /* CPU Sync Barrier */ - case GT_PCI0SYNC: - case GT_PCI1SYNC: - /* Reading those register should empty all FIFO on the PCI - bus, which are not emulated. The return value should be - a random value that should be ignored. */ - val = 0xc000ffee; - break; - - /* ECC */ - case GT_ECC_ERRDATALO: - case GT_ECC_ERRDATAHI: - case GT_ECC_MEM: - case GT_ECC_CALC: - case GT_ECC_ERRADDR: - /* Emulated memory has no error, always return the initial - values */ - val = s->regs[saddr]; - break; - - case GT_CPU: - case GT_SCS10LD: - case GT_SCS10HD: - case GT_SCS32LD: - case GT_SCS32HD: - case GT_CS20LD: - case GT_CS20HD: - case GT_CS3BOOTLD: - case GT_CS3BOOTHD: - case GT_SCS10AR: - case GT_SCS32AR: - case GT_CS20R: - case GT_CS3BOOTR: - case GT_PCI0IOLD: - case GT_PCI0M0LD: - case GT_PCI0M1LD: - case GT_PCI1IOLD: - case GT_PCI1M0LD: - case GT_PCI1M1LD: - case GT_PCI0IOHD: - case GT_PCI0M0HD: - case GT_PCI0M1HD: - case GT_PCI1IOHD: - case GT_PCI1M0HD: - case GT_PCI1M1HD: - case GT_PCI0IOREMAP: - case GT_PCI0M0REMAP: - case GT_PCI0M1REMAP: - case GT_PCI1IOREMAP: - case GT_PCI1M0REMAP: - case GT_PCI1M1REMAP: - case GT_ISD: - val = s->regs[saddr]; - break; - case GT_PCI0_IACK: - /* Read the IRQ number */ - val = pic_read_irq(isa_pic); - break; - - /* SDRAM and Device Address Decode */ - case GT_SCS0LD: - case GT_SCS0HD: - case GT_SCS1LD: - case GT_SCS1HD: - case GT_SCS2LD: - case GT_SCS2HD: - case GT_SCS3LD: - case GT_SCS3HD: - case GT_CS0LD: - case GT_CS0HD: - case GT_CS1LD: - case GT_CS1HD: - case GT_CS2LD: - case GT_CS2HD: - case GT_CS3LD: - case GT_CS3HD: - case GT_BOOTLD: - case GT_BOOTHD: - case GT_ADERR: - val = s->regs[saddr]; - break; - - /* SDRAM Configuration */ - case GT_SDRAM_CFG: - case GT_SDRAM_OPMODE: - case GT_SDRAM_BM: - case GT_SDRAM_ADDRDECODE: - val = s->regs[saddr]; - break; - - /* SDRAM Parameters */ - case GT_SDRAM_B0: - case GT_SDRAM_B1: - case GT_SDRAM_B2: - case GT_SDRAM_B3: - /* We don't simulate electrical parameters of the SDRAM. - Just return the last written value. */ - val = s->regs[saddr]; - break; - - /* Device Parameters */ - case GT_DEV_B0: - case GT_DEV_B1: - case GT_DEV_B2: - case GT_DEV_B3: - case GT_DEV_BOOT: - val = s->regs[saddr]; - break; - - /* DMA Record */ - case GT_DMA0_CNT: - case GT_DMA1_CNT: - case GT_DMA2_CNT: - case GT_DMA3_CNT: - case GT_DMA0_SA: - case GT_DMA1_SA: - case GT_DMA2_SA: - case GT_DMA3_SA: - case GT_DMA0_DA: - case GT_DMA1_DA: - case GT_DMA2_DA: - case GT_DMA3_DA: - case GT_DMA0_NEXT: - case GT_DMA1_NEXT: - case GT_DMA2_NEXT: - case GT_DMA3_NEXT: - case GT_DMA0_CUR: - case GT_DMA1_CUR: - case GT_DMA2_CUR: - case GT_DMA3_CUR: - val = s->regs[saddr]; - break; - - /* DMA Channel Control */ - case GT_DMA0_CTRL: - case GT_DMA1_CTRL: - case GT_DMA2_CTRL: - case GT_DMA3_CTRL: - val = s->regs[saddr]; - break; - - /* DMA Arbiter */ - case GT_DMA_ARB: - val = s->regs[saddr]; - break; - - /* Timer/Counter */ - case GT_TC0: - case GT_TC1: - case GT_TC2: - case GT_TC3: - case GT_TC_CONTROL: - val = s->regs[saddr]; - break; - - /* PCI Internal */ - case GT_PCI0_CFGADDR: - val = phb->config_reg; - break; - case GT_PCI0_CFGDATA: - if (!(phb->config_reg & (1 << 31))) { - val = 0xffffffff; - } else { - val = pci_data_read(phb->bus, phb->config_reg, 4); - } - if (!(s->regs[GT_PCI0_CMD] & 1) && (phb->config_reg & 0x00fff800)) { - val = bswap32(val); - } - break; - - case GT_PCI0_CMD: - case GT_PCI0_TOR: - case GT_PCI0_BS_SCS10: - case GT_PCI0_BS_SCS32: - case GT_PCI0_BS_CS20: - case GT_PCI0_BS_CS3BT: - case GT_PCI1_IACK: - case GT_PCI0_BARE: - case GT_PCI0_PREFMBR: - case GT_PCI0_SCS10_BAR: - case GT_PCI0_SCS32_BAR: - case GT_PCI0_CS20_BAR: - case GT_PCI0_CS3BT_BAR: - case GT_PCI0_SSCS10_BAR: - case GT_PCI0_SSCS32_BAR: - case GT_PCI0_SCS3BT_BAR: - case GT_PCI1_CMD: - case GT_PCI1_TOR: - case GT_PCI1_BS_SCS10: - case GT_PCI1_BS_SCS32: - case GT_PCI1_BS_CS20: - case GT_PCI1_BS_CS3BT: - case GT_PCI1_BARE: - case GT_PCI1_PREFMBR: - case GT_PCI1_SCS10_BAR: - case GT_PCI1_SCS32_BAR: - case GT_PCI1_CS20_BAR: - case GT_PCI1_CS3BT_BAR: - case GT_PCI1_SSCS10_BAR: - case GT_PCI1_SSCS32_BAR: - case GT_PCI1_SCS3BT_BAR: - case GT_PCI1_CFGADDR: - case GT_PCI1_CFGDATA: - val = s->regs[saddr]; - break; - - /* Interrupts */ - case GT_INTRCAUSE: - val = s->regs[saddr]; - DPRINTF("INTRCAUSE %x\n", val); - break; - case GT_INTRMASK: - val = s->regs[saddr]; - DPRINTF("INTRMASK %x\n", val); - break; - case GT_PCI0_ICMASK: - val = s->regs[saddr]; - DPRINTF("ICMASK %x\n", val); - break; - case GT_PCI0_SERR0MASK: - val = s->regs[saddr]; - DPRINTF("SERR0MASK %x\n", val); - break; - - /* Reserved when only PCI_0 is configured. */ - case GT_HINTRCAUSE: - case GT_CPU_INTSEL: - case GT_PCI0_INTSEL: - case GT_HINTRMASK: - case GT_PCI0_HICMASK: - case GT_PCI1_SERR1MASK: - val = s->regs[saddr]; - break; - - default: - val = s->regs[saddr]; - DPRINTF ("Bad register offset 0x%x\n", (int)addr); - break; - } - - if (!(s->regs[GT_CPU] & 0x00001000)) - val = bswap32(val); - - return val; -} - -static const MemoryRegionOps isd_mem_ops = { - .read = gt64120_readl, - .write = gt64120_writel, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int gt64120_pci_map_irq(PCIDevice *pci_dev, int irq_num) -{ - int slot; - - slot = (pci_dev->devfn >> 3); - - switch (slot) { - /* PIIX4 USB */ - case 10: - return 3; - /* AMD 79C973 Ethernet */ - case 11: - return 1; - /* Crystal 4281 Sound */ - case 12: - return 2; - /* PCI slot 1 to 4 */ - case 18 ... 21: - return ((slot - 18) + irq_num) & 0x03; - /* Unknown device, don't do any translation */ - default: - return irq_num; - } -} - -static int pci_irq_levels[4]; - -static void gt64120_pci_set_irq(void *opaque, int irq_num, int level) -{ - int i, pic_irq, pic_level; - qemu_irq *pic = opaque; - - pci_irq_levels[irq_num] = level; - - /* now we change the pic irq level according to the piix irq mappings */ - /* XXX: optimize */ - pic_irq = piix4_dev->config[0x60 + irq_num]; - if (pic_irq < 16) { - /* The pic level is the logical OR of all the PCI irqs mapped - to it */ - pic_level = 0; - for (i = 0; i < 4; i++) { - if (pic_irq == piix4_dev->config[0x60 + i]) - pic_level |= pci_irq_levels[i]; - } - qemu_set_irq(pic[pic_irq], pic_level); - } -} - - -static void gt64120_reset(void *opaque) -{ - GT64120State *s = opaque; - - /* FIXME: Malta specific hw assumptions ahead */ - - /* CPU Configuration */ -#ifdef TARGET_WORDS_BIGENDIAN - s->regs[GT_CPU] = 0x00000000; -#else - s->regs[GT_CPU] = 0x00001000; -#endif - s->regs[GT_MULTI] = 0x00000003; - - /* CPU Address decode */ - s->regs[GT_SCS10LD] = 0x00000000; - s->regs[GT_SCS10HD] = 0x00000007; - s->regs[GT_SCS32LD] = 0x00000008; - s->regs[GT_SCS32HD] = 0x0000000f; - s->regs[GT_CS20LD] = 0x000000e0; - s->regs[GT_CS20HD] = 0x00000070; - s->regs[GT_CS3BOOTLD] = 0x000000f8; - s->regs[GT_CS3BOOTHD] = 0x0000007f; - - s->regs[GT_PCI0IOLD] = 0x00000080; - s->regs[GT_PCI0IOHD] = 0x0000000f; - s->regs[GT_PCI0M0LD] = 0x00000090; - s->regs[GT_PCI0M0HD] = 0x0000001f; - s->regs[GT_ISD] = 0x000000a0; - s->regs[GT_PCI0M1LD] = 0x00000790; - s->regs[GT_PCI0M1HD] = 0x0000001f; - s->regs[GT_PCI1IOLD] = 0x00000100; - s->regs[GT_PCI1IOHD] = 0x0000000f; - s->regs[GT_PCI1M0LD] = 0x00000110; - s->regs[GT_PCI1M0HD] = 0x0000001f; - s->regs[GT_PCI1M1LD] = 0x00000120; - s->regs[GT_PCI1M1HD] = 0x0000002f; - - s->regs[GT_SCS10AR] = 0x00000000; - s->regs[GT_SCS32AR] = 0x00000008; - s->regs[GT_CS20R] = 0x000000e0; - s->regs[GT_CS3BOOTR] = 0x000000f8; - - s->regs[GT_PCI0IOREMAP] = 0x00000080; - s->regs[GT_PCI0M0REMAP] = 0x00000090; - s->regs[GT_PCI0M1REMAP] = 0x00000790; - s->regs[GT_PCI1IOREMAP] = 0x00000100; - s->regs[GT_PCI1M0REMAP] = 0x00000110; - s->regs[GT_PCI1M1REMAP] = 0x00000120; - - /* CPU Error Report */ - s->regs[GT_CPUERR_ADDRLO] = 0x00000000; - s->regs[GT_CPUERR_ADDRHI] = 0x00000000; - s->regs[GT_CPUERR_DATALO] = 0xffffffff; - s->regs[GT_CPUERR_DATAHI] = 0xffffffff; - s->regs[GT_CPUERR_PARITY] = 0x000000ff; - - /* CPU Sync Barrier */ - s->regs[GT_PCI0SYNC] = 0x00000000; - s->regs[GT_PCI1SYNC] = 0x00000000; - - /* SDRAM and Device Address Decode */ - s->regs[GT_SCS0LD] = 0x00000000; - s->regs[GT_SCS0HD] = 0x00000007; - s->regs[GT_SCS1LD] = 0x00000008; - s->regs[GT_SCS1HD] = 0x0000000f; - s->regs[GT_SCS2LD] = 0x00000010; - s->regs[GT_SCS2HD] = 0x00000017; - s->regs[GT_SCS3LD] = 0x00000018; - s->regs[GT_SCS3HD] = 0x0000001f; - s->regs[GT_CS0LD] = 0x000000c0; - s->regs[GT_CS0HD] = 0x000000c7; - s->regs[GT_CS1LD] = 0x000000c8; - s->regs[GT_CS1HD] = 0x000000cf; - s->regs[GT_CS2LD] = 0x000000d0; - s->regs[GT_CS2HD] = 0x000000df; - s->regs[GT_CS3LD] = 0x000000f0; - s->regs[GT_CS3HD] = 0x000000fb; - s->regs[GT_BOOTLD] = 0x000000fc; - s->regs[GT_BOOTHD] = 0x000000ff; - s->regs[GT_ADERR] = 0xffffffff; - - /* SDRAM Configuration */ - s->regs[GT_SDRAM_CFG] = 0x00000200; - s->regs[GT_SDRAM_OPMODE] = 0x00000000; - s->regs[GT_SDRAM_BM] = 0x00000007; - s->regs[GT_SDRAM_ADDRDECODE] = 0x00000002; - - /* SDRAM Parameters */ - s->regs[GT_SDRAM_B0] = 0x00000005; - s->regs[GT_SDRAM_B1] = 0x00000005; - s->regs[GT_SDRAM_B2] = 0x00000005; - s->regs[GT_SDRAM_B3] = 0x00000005; - - /* ECC */ - s->regs[GT_ECC_ERRDATALO] = 0x00000000; - s->regs[GT_ECC_ERRDATAHI] = 0x00000000; - s->regs[GT_ECC_MEM] = 0x00000000; - s->regs[GT_ECC_CALC] = 0x00000000; - s->regs[GT_ECC_ERRADDR] = 0x00000000; - - /* Device Parameters */ - s->regs[GT_DEV_B0] = 0x386fffff; - s->regs[GT_DEV_B1] = 0x386fffff; - s->regs[GT_DEV_B2] = 0x386fffff; - s->regs[GT_DEV_B3] = 0x386fffff; - s->regs[GT_DEV_BOOT] = 0x146fffff; - - /* DMA registers are all zeroed at reset */ - - /* Timer/Counter */ - s->regs[GT_TC0] = 0xffffffff; - s->regs[GT_TC1] = 0x00ffffff; - s->regs[GT_TC2] = 0x00ffffff; - s->regs[GT_TC3] = 0x00ffffff; - s->regs[GT_TC_CONTROL] = 0x00000000; - - /* PCI Internal */ -#ifdef TARGET_WORDS_BIGENDIAN - s->regs[GT_PCI0_CMD] = 0x00000000; -#else - s->regs[GT_PCI0_CMD] = 0x00010001; -#endif - s->regs[GT_PCI0_TOR] = 0x0000070f; - s->regs[GT_PCI0_BS_SCS10] = 0x00fff000; - s->regs[GT_PCI0_BS_SCS32] = 0x00fff000; - s->regs[GT_PCI0_BS_CS20] = 0x01fff000; - s->regs[GT_PCI0_BS_CS3BT] = 0x00fff000; - s->regs[GT_PCI1_IACK] = 0x00000000; - s->regs[GT_PCI0_IACK] = 0x00000000; - s->regs[GT_PCI0_BARE] = 0x0000000f; - s->regs[GT_PCI0_PREFMBR] = 0x00000040; - s->regs[GT_PCI0_SCS10_BAR] = 0x00000000; - s->regs[GT_PCI0_SCS32_BAR] = 0x01000000; - s->regs[GT_PCI0_CS20_BAR] = 0x1c000000; - s->regs[GT_PCI0_CS3BT_BAR] = 0x1f000000; - s->regs[GT_PCI0_SSCS10_BAR] = 0x00000000; - s->regs[GT_PCI0_SSCS32_BAR] = 0x01000000; - s->regs[GT_PCI0_SCS3BT_BAR] = 0x1f000000; -#ifdef TARGET_WORDS_BIGENDIAN - s->regs[GT_PCI1_CMD] = 0x00000000; -#else - s->regs[GT_PCI1_CMD] = 0x00010001; -#endif - s->regs[GT_PCI1_TOR] = 0x0000070f; - s->regs[GT_PCI1_BS_SCS10] = 0x00fff000; - s->regs[GT_PCI1_BS_SCS32] = 0x00fff000; - s->regs[GT_PCI1_BS_CS20] = 0x01fff000; - s->regs[GT_PCI1_BS_CS3BT] = 0x00fff000; - s->regs[GT_PCI1_BARE] = 0x0000000f; - s->regs[GT_PCI1_PREFMBR] = 0x00000040; - s->regs[GT_PCI1_SCS10_BAR] = 0x00000000; - s->regs[GT_PCI1_SCS32_BAR] = 0x01000000; - s->regs[GT_PCI1_CS20_BAR] = 0x1c000000; - s->regs[GT_PCI1_CS3BT_BAR] = 0x1f000000; - s->regs[GT_PCI1_SSCS10_BAR] = 0x00000000; - s->regs[GT_PCI1_SSCS32_BAR] = 0x01000000; - s->regs[GT_PCI1_SCS3BT_BAR] = 0x1f000000; - s->regs[GT_PCI1_CFGADDR] = 0x00000000; - s->regs[GT_PCI1_CFGDATA] = 0x00000000; - s->regs[GT_PCI0_CFGADDR] = 0x00000000; - - /* Interrupt registers are all zeroed at reset */ - - gt64120_isd_mapping(s); - gt64120_pci_mapping(s); -} - -PCIBus *gt64120_register(qemu_irq *pic) -{ - GT64120State *d; - PCIHostState *phb; - DeviceState *dev; - - dev = qdev_create(NULL, TYPE_GT64120_PCI_HOST_BRIDGE); - qdev_init_nofail(dev); - d = GT64120_PCI_HOST_BRIDGE(dev); - phb = PCI_HOST_BRIDGE(dev); - memory_region_init(&d->pci0_mem, OBJECT(dev), "pci0-mem", UINT32_MAX); - address_space_init(&d->pci0_mem_as, &d->pci0_mem, "pci0-mem"); - phb->bus = pci_register_bus(dev, "pci", - gt64120_pci_set_irq, gt64120_pci_map_irq, - pic, - &d->pci0_mem, - get_system_io(), - PCI_DEVFN(18, 0), 4, TYPE_PCI_BUS); - memory_region_init_io(&d->ISD_mem, OBJECT(dev), &isd_mem_ops, d, "isd-mem", 0x1000); - - pci_create_simple(phb->bus, PCI_DEVFN(0, 0), "gt64120_pci"); - return phb->bus; -} - -static int gt64120_init(SysBusDevice *dev) -{ - GT64120State *s; - - s = GT64120_PCI_HOST_BRIDGE(dev); - - qemu_register_reset(gt64120_reset, s); - return 0; -} - -static void gt64120_pci_realize(PCIDevice *d, Error **errp) -{ - /* FIXME: Malta specific hw assumptions ahead */ - pci_set_word(d->config + PCI_COMMAND, 0); - pci_set_word(d->config + PCI_STATUS, - PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); - pci_config_set_prog_interface(d->config, 0); - pci_set_long(d->config + PCI_BASE_ADDRESS_0, 0x00000008); - pci_set_long(d->config + PCI_BASE_ADDRESS_1, 0x01000008); - pci_set_long(d->config + PCI_BASE_ADDRESS_2, 0x1c000000); - pci_set_long(d->config + PCI_BASE_ADDRESS_3, 0x1f000000); - 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); -} - -static void gt64120_pci_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = gt64120_pci_realize; - k->vendor_id = PCI_VENDOR_ID_MARVELL; - k->device_id = PCI_DEVICE_ID_MARVELL_GT6412X; - k->revision = 0x10; - k->class_id = PCI_CLASS_BRIDGE_HOST; - /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo gt64120_pci_info = { - .name = "gt64120_pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = gt64120_pci_class_init, -}; - -static void gt64120_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = gt64120_init; - dc->vmsd = &vmstate_gt64120; -} - -static const TypeInfo gt64120_info = { - .name = TYPE_GT64120_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(GT64120State), - .class_init = gt64120_class_init, -}; - -static void gt64120_pci_register_types(void) -{ - type_register_static(>64120_info); - type_register_static(>64120_pci_info); -} - -type_init(gt64120_pci_register_types) diff --git a/qemu/hw/mips/mips_fulong2e.c b/qemu/hw/mips/mips_fulong2e.c deleted file mode 100644 index bdb716e72..000000000 --- a/qemu/hw/mips/mips_fulong2e.c +++ /dev/null @@ -1,392 +0,0 @@ -/* - * QEMU fulong 2e mini pc support - * - * Copyright (c) 2008 yajin (yajin@vm-kernel.org) - * Copyright (c) 2009 chenming (chenming@rdc.faw.com.cn) - * Copyright (c) 2010 Huacai Chen (zltjiangshi@gmail.com) - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -/* - * Fulong 2e mini pc is based on ICT/ST Loongson 2e CPU (MIPS III like, 800MHz) - * http://www.linux-mips.org/wiki/Fulong - * - * Loongson 2e user manual: - * 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" -#include "hw/block/fdc.h" -#include "net/net.h" -#include "hw/boards.h" -#include "hw/i2c/smbus.h" -#include "sysemu/block-backend.h" -#include "hw/block/flash.h" -#include "hw/mips/mips.h" -#include "hw/mips/cpudevs.h" -#include "hw/pci/pci.h" -#include "sysemu/char.h" -#include "sysemu/sysemu.h" -#include "audio/audio.h" -#include "qemu/log.h" -#include "hw/loader.h" -#include "hw/mips/bios.h" -#include "hw/ide.h" -#include "elf.h" -#include "hw/isa/vt82c686.h" -#include "hw/timer/mc146818rtc.h" -#include "hw/timer/i8254.h" -#include "sysemu/blockdev.h" -#include "exec/address-spaces.h" -#include "sysemu/qtest.h" -#include "qemu/error-report.h" - -#define DEBUG_FULONG2E_INIT - -#define ENVP_ADDR 0x80002000l -#define ENVP_NB_ENTRIES 16 -#define ENVP_ENTRY_SIZE 256 - -#define MAX_IDE_BUS 2 - -/* - * PMON is not part of qemu and released with BSD license, anyone - * who want to build a pmon binary please first git-clone the source - * from the git repository at: - * http://www.loongson.cn/support/git/pmon - * Then follow the "Compile Guide" available at: - * http://dev.lemote.com/code/pmon - * - * Notes: - * 1, don't use the source at http://dev.lemote.com/http_git/pmon.git - * 2, use "Bonito2edev" to replace "dir_corresponding_to_your_target_hardware" - * in the "Compile Guide". - */ -#define FULONG_BIOSNAME "pmon_fulong2e.bin" - -/* PCI SLOT in fulong 2e */ -#define FULONG2E_VIA_SLOT 5 -#define FULONG2E_ATI_SLOT 6 -#define FULONG2E_RTL8139_SLOT 7 - -static ISADevice *pit; - -static struct _loaderparams { - int ram_size; - const char *kernel_filename; - const char *kernel_cmdline; - const char *initrd_filename; -} loaderparams; - -static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t* prom_buf, int index, - const char *string, ...) -{ - va_list ap; - int32_t table_addr; - - if (index >= ENVP_NB_ENTRIES) - return; - - if (string == NULL) { - prom_buf[index] = 0; - return; - } - - table_addr = sizeof(int32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE; - prom_buf[index] = tswap32(ENVP_ADDR + table_addr); - - va_start(ap, string); - vsnprintf((char *)prom_buf + table_addr, ENVP_ENTRY_SIZE, string, ap); - va_end(ap); -} - -static int64_t load_kernel (CPUMIPSState *env) -{ - int64_t kernel_entry, kernel_low, kernel_high; - int index = 0; - long initrd_size; - ram_addr_t initrd_offset; - uint32_t *prom_buf; - long prom_size; - - 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, EM_MIPS, 1, 0) < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - loaderparams.kernel_filename); - exit(1); - } - - /* load initrd */ - initrd_size = 0; - initrd_offset = 0; - if (loaderparams.initrd_filename) { - initrd_size = get_image_size (loaderparams.initrd_filename); - if (initrd_size > 0) { - initrd_offset = (kernel_high + ~INITRD_PAGE_MASK) & INITRD_PAGE_MASK; - if (initrd_offset + initrd_size > ram_size) { - fprintf(stderr, - "qemu: memory too small for initial ram disk '%s'\n", - loaderparams.initrd_filename); - exit(1); - } - initrd_size = load_image_targphys(loaderparams.initrd_filename, - initrd_offset, ram_size - initrd_offset); - } - if (initrd_size == (target_ulong) -1) { - fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", - loaderparams.initrd_filename); - exit(1); - } - } - - /* Setup prom parameters. */ - prom_size = ENVP_NB_ENTRIES * (sizeof(int32_t) + ENVP_ENTRY_SIZE); - prom_buf = g_malloc(prom_size); - - prom_set(prom_buf, index++, "%s", loaderparams.kernel_filename); - if (initrd_size > 0) { - prom_set(prom_buf, index++, "rd_start=0x%" PRIx64 " rd_size=%li %s", - cpu_mips_phys_to_kseg0(NULL, initrd_offset), initrd_size, - loaderparams.kernel_cmdline); - } else { - prom_set(prom_buf, index++, "%s", loaderparams.kernel_cmdline); - } - - /* Setup minimum environment variables */ - prom_set(prom_buf, index++, "busclock=33000000"); - prom_set(prom_buf, index++, "cpuclock=100000000"); - prom_set(prom_buf, index++, "memsize=%i", loaderparams.ram_size/1024/1024); - prom_set(prom_buf, index++, "modetty0=38400n8r"); - prom_set(prom_buf, index++, NULL); - - rom_add_blob_fixed("prom", prom_buf, prom_size, - cpu_mips_kseg0_to_phys(NULL, ENVP_ADDR)); - - g_free(prom_buf); - return kernel_entry; -} - -static void write_bootloader (CPUMIPSState *env, uint8_t *base, int64_t kernel_addr) -{ - uint32_t *p; - - /* Small bootloader */ - p = (uint32_t *) base; - - stl_p(p++, 0x0bf00010); /* j 0x1fc00040 */ - stl_p(p++, 0x00000000); /* nop */ - - /* Second part of the bootloader */ - p = (uint32_t *) (base + 0x040); - - stl_p(p++, 0x3c040000); /* lui a0, 0 */ - stl_p(p++, 0x34840002); /* ori a0, a0, 2 */ - stl_p(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff)); /* lui a1, high(ENVP_ADDR) */ - stl_p(p++, 0x34a50000 | (ENVP_ADDR & 0xffff)); /* ori a1, a0, low(ENVP_ADDR) */ - stl_p(p++, 0x3c060000 | (((ENVP_ADDR + 8) >> 16) & 0xffff)); /* lui a2, high(ENVP_ADDR + 8) */ - stl_p(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff)); /* ori a2, a2, low(ENVP_ADDR + 8) */ - stl_p(p++, 0x3c070000 | (loaderparams.ram_size >> 16)); /* lui a3, high(env->ram_size) */ - stl_p(p++, 0x34e70000 | (loaderparams.ram_size & 0xffff)); /* ori a3, a3, low(env->ram_size) */ - stl_p(p++, 0x3c1f0000 | ((kernel_addr >> 16) & 0xffff)); /* lui ra, high(kernel_addr) */; - stl_p(p++, 0x37ff0000 | (kernel_addr & 0xffff)); /* ori ra, ra, low(kernel_addr) */ - stl_p(p++, 0x03e00008); /* jr ra */ - stl_p(p++, 0x00000000); /* nop */ -} - - -static void main_cpu_reset(void *opaque) -{ - MIPSCPU *cpu = opaque; - CPUMIPSState *env = &cpu->env; - - cpu_reset(CPU(cpu)); - /* TODO: 2E reset stuff */ - if (loaderparams.kernel_filename) { - env->CP0_Status &= ~((1 << CP0St_BEV) | (1 << CP0St_ERL)); - } -} - -static const uint8_t eeprom_spd[0x80] = { - 0x80,0x08,0x07,0x0d,0x09,0x02,0x40,0x00,0x04,0x70, - 0x70,0x00,0x82,0x10,0x00,0x01,0x0e,0x04,0x0c,0x01, - 0x02,0x20,0x80,0x75,0x70,0x00,0x00,0x50,0x3c,0x50, - 0x2d,0x20,0xb0,0xb0,0x50,0x50,0x00,0x00,0x00,0x00, - 0x00,0x41,0x48,0x3c,0x32,0x75,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x9c,0x7b,0x07,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x48,0x42,0x35,0x34,0x41,0x32, - 0x35,0x36,0x38,0x4b,0x4e,0x2d,0x41,0x37,0x35,0x42, - 0x20,0x30,0x20 -}; - -/* Audio support */ -static void audio_init (PCIBus *pci_bus) -{ - vt82c686b_ac97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 5)); - vt82c686b_mc97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 6)); -} - -/* Network support */ -static void network_init (PCIBus *pci_bus) -{ - int i; - - for(i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - const char *default_devaddr = NULL; - - if (i == 0 && (!nd->model || strcmp(nd->model, "rtl8139") == 0)) { - /* The fulong board has a RTL8139 card using PCI SLOT 7 */ - default_devaddr = "07"; - } - - pci_nic_init_nofail(nd, pci_bus, "rtl8139", default_devaddr); - } -} - -static void mips_fulong2e_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; - char *filename; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *bios = g_new(MemoryRegion, 1); - long bios_size; - int64_t kernel_entry; - qemu_irq *i8259; - PCIBus *pci_bus; - ISABus *isa_bus; - I2CBus *smbus; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - MIPSCPU *cpu; - CPUMIPSState *env; - - /* init CPUs */ - if (cpu_model == NULL) { - cpu_model = "Loongson-2E"; - } - cpu = cpu_mips_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - env = &cpu->env; - - qemu_register_reset(main_cpu_reset, cpu); - - /* fulong 2e has 256M ram. */ - ram_size = 256 * 1024 * 1024; - - /* fulong 2e has a 1M flash.Winbond W39L040AP70Z */ - bios_size = 1024 * 1024; - - /* allocate RAM */ - memory_region_allocate_system_memory(ram, NULL, "fulong2e.ram", ram_size); - memory_region_init_ram(bios, NULL, "fulong2e.bios", bios_size, - &error_fatal); - vmstate_register_ram_global(bios); - memory_region_set_readonly(bios, true); - - memory_region_add_subregion(address_space_mem, 0, ram); - memory_region_add_subregion(address_space_mem, 0x1fc00000LL, bios); - - /* We do not support flash operation, just loading pmon.bin as raw BIOS. - * Please use -L to set the BIOS path and -bios to set bios name. */ - - if (kernel_filename) { - loaderparams.ram_size = ram_size; - loaderparams.kernel_filename = kernel_filename; - loaderparams.kernel_cmdline = kernel_cmdline; - loaderparams.initrd_filename = initrd_filename; - kernel_entry = load_kernel (env); - write_bootloader(env, memory_region_get_ram_ptr(bios), kernel_entry); - } else { - if (bios_name == NULL) { - bios_name = FULONG_BIOSNAME; - } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - bios_size = load_image_targphys(filename, 0x1fc00000LL, - BIOS_SIZE); - g_free(filename); - } else { - bios_size = -1; - } - - if ((bios_size < 0 || bios_size > BIOS_SIZE) && - !kernel_filename && !qtest_enabled()) { - error_report("Could not load MIPS bios '%s'", bios_name); - exit(1); - } - } - - /* Init internal devices */ - cpu_mips_irq_init_cpu(env); - cpu_mips_clock_init(env); - - /* North bridge, Bonito --> IP2 */ - pci_bus = bonito_init((qemu_irq *)&(env->irq[2])); - - /* South bridge */ - ide_drive_get(hd, ARRAY_SIZE(hd)); - - isa_bus = vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 0)); - if (!isa_bus) { - fprintf(stderr, "vt82c686b_init error\n"); - exit(1); - } - - /* Interrupt controller */ - /* The 8259 -> IP5 */ - i8259 = i8259_init(isa_bus, env->irq[5]); - isa_bus_irqs(isa_bus, i8259); - - vt82c686b_ide_init(pci_bus, hd, PCI_DEVFN(FULONG2E_VIA_SLOT, 1)); - pci_create_simple(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 2), - "vt82c686b-usb-uhci"); - pci_create_simple(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 3), - "vt82c686b-usb-uhci"); - - smbus = vt82c686b_pm_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 4), - 0xeee1, NULL); - /* TODO: Populate SPD eeprom data. */ - smbus_eeprom_init(smbus, 1, eeprom_spd, sizeof(eeprom_spd)); - - /* init other devices */ - pit = pit_init(isa_bus, 0x40, 0, NULL); - DMA_init(isa_bus, 0); - - /* Super I/O */ - isa_create_simple(isa_bus, "i8042"); - - rtc_init(isa_bus, 2000, NULL); - - serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS); - parallel_hds_isa_init(isa_bus, 1); - - /* Sound card */ - audio_init(pci_bus); - /* Network card */ - network_init(pci_bus); -} - -static void mips_fulong2e_machine_init(MachineClass *mc) -{ - mc->desc = "Fulong 2e mini pc"; - mc->init = mips_fulong2e_init; -} - -DEFINE_MACHINE("fulong2e", mips_fulong2e_machine_init) diff --git a/qemu/hw/mips/mips_int.c b/qemu/hw/mips/mips_int.c deleted file mode 100644 index 59081f9d1..000000000 --- a/qemu/hw/mips/mips_int.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * QEMU MIPS interrupt support - * - * 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/mips/cpudevs.h" -#include "cpu.h" -#include "sysemu/kvm.h" -#include "kvm_mips.h" - -static void cpu_mips_irq_request(void *opaque, int irq, int level) -{ - MIPSCPU *cpu = opaque; - CPUMIPSState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - if (irq < 0 || irq > 7) - return; - - if (level) { - env->CP0_Cause |= 1 << (irq + CP0Ca_IP); - - if (kvm_enabled() && irq == 2) { - kvm_mips_set_interrupt(cpu, irq, level); - } - - } else { - env->CP0_Cause &= ~(1 << (irq + CP0Ca_IP)); - - if (kvm_enabled() && irq == 2) { - kvm_mips_set_interrupt(cpu, irq, level); - } - } - - if (env->CP0_Cause & CP0Ca_IP_mask) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } -} - -void cpu_mips_irq_init_cpu(CPUMIPSState *env) -{ - qemu_irq *qi; - int i; - - qi = qemu_allocate_irqs(cpu_mips_irq_request, mips_env_get_cpu(env), 8); - for (i = 0; i < 8; i++) { - env->irq[i] = qi[i]; - } -} - -void cpu_mips_soft_irq(CPUMIPSState *env, int irq, int level) -{ - if (irq < 0 || irq > 2) { - return; - } - - qemu_set_irq(env->irq[irq], level); -} diff --git a/qemu/hw/mips/mips_jazz.c b/qemu/hw/mips/mips_jazz.c deleted file mode 100644 index ac7c64125..000000000 --- a/qemu/hw/mips/mips_jazz.c +++ /dev/null @@ -1,391 +0,0 @@ -/* - * QEMU MIPS Jazz support - * - * Copyright (c) 2007-2008 Hervé Poussineau - * - * 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/mips/mips.h" -#include "hw/mips/cpudevs.h" -#include "hw/i386/pc.h" -#include "hw/char/serial.h" -#include "hw/isa/isa.h" -#include "hw/block/fdc.h" -#include "sysemu/sysemu.h" -#include "sysemu/arch_init.h" -#include "hw/boards.h" -#include "net/net.h" -#include "hw/scsi/esp.h" -#include "hw/mips/bios.h" -#include "hw/loader.h" -#include "hw/timer/mc146818rtc.h" -#include "hw/timer/i8254.h" -#include "hw/audio/pcspk.h" -#include "sysemu/block-backend.h" -#include "hw/sysbus.h" -#include "exec/address-spaces.h" -#include "sysemu/qtest.h" -#include "qemu/error-report.h" -#include "qemu/help_option.h" - -enum jazz_model_e -{ - JAZZ_MAGNUM, - JAZZ_PICA61, -}; - -static void main_cpu_reset(void *opaque) -{ - MIPSCPU *cpu = opaque; - - cpu_reset(CPU(cpu)); -} - -static uint64_t rtc_read(void *opaque, hwaddr addr, unsigned size) -{ - uint8_t val; - address_space_read(&address_space_memory, 0x90000071, - MEMTXATTRS_UNSPECIFIED, &val, 1); - return val; -} - -static void rtc_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - uint8_t buf = val & 0xff; - address_space_write(&address_space_memory, 0x90000071, - MEMTXATTRS_UNSPECIFIED, &buf, 1); -} - -static const MemoryRegionOps rtc_ops = { - .read = rtc_read, - .write = rtc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t dma_dummy_read(void *opaque, hwaddr addr, - unsigned size) -{ - /* Nothing to do. That is only to ensure that - * the current DMA acknowledge cycle is completed. */ - return 0xff; -} - -static void dma_dummy_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - /* Nothing to do. That is only to ensure that - * the current DMA acknowledge cycle is completed. */ -} - -static const MemoryRegionOps dma_dummy_ops = { - .read = dma_dummy_read, - .write = dma_dummy_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -#define MAGNUM_BIOS_SIZE_MAX 0x7e000 -#define MAGNUM_BIOS_SIZE (BIOS_SIZE < MAGNUM_BIOS_SIZE_MAX ? BIOS_SIZE : MAGNUM_BIOS_SIZE_MAX) - -static CPUUnassignedAccess real_do_unassigned_access; -static void mips_jazz_do_unassigned_access(CPUState *cpu, hwaddr addr, - bool is_write, bool is_exec, - int opaque, unsigned size) -{ - if (!is_exec) { - /* ignore invalid access (ie do not raise exception) */ - return; - } - (*real_do_unassigned_access)(cpu, addr, is_write, is_exec, opaque, size); -} - -static void mips_jazz_init(MachineState *machine, - enum jazz_model_e jazz_model) -{ - MemoryRegion *address_space = get_system_memory(); - const char *cpu_model = machine->cpu_model; - char *filename; - int bios_size, n; - MIPSCPU *cpu; - CPUClass *cc; - CPUMIPSState *env; - qemu_irq *i8259; - rc4030_dma *dmas; - MemoryRegion *rc4030_dma_mr; - MemoryRegion *isa_mem = g_new(MemoryRegion, 1); - MemoryRegion *isa_io = g_new(MemoryRegion, 1); - MemoryRegion *rtc = g_new(MemoryRegion, 1); - MemoryRegion *i8042 = g_new(MemoryRegion, 1); - MemoryRegion *dma_dummy = g_new(MemoryRegion, 1); - NICInfo *nd; - DeviceState *dev, *rc4030; - SysBusDevice *sysbus; - ISABus *isa_bus; - ISADevice *pit; - DriveInfo *fds[MAX_FD]; - qemu_irq esp_reset, dma_enable; - MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *bios = g_new(MemoryRegion, 1); - MemoryRegion *bios2 = g_new(MemoryRegion, 1); - - /* init CPUs */ - if (cpu_model == NULL) { - cpu_model = "R4000"; - } - cpu = cpu_mips_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - env = &cpu->env; - qemu_register_reset(main_cpu_reset, cpu); - - /* Chipset returns 0 in invalid reads and do not raise data exceptions. - * However, we can't simply add a global memory region to catch - * everything, as memory core directly call unassigned_mem_read/write - * on some invalid accesses, which call do_unassigned_access on the - * CPU, which raise an exception. - * Handle that case by hijacking the do_unassigned_access method on - * the CPU, and do not raise exceptions for data access. */ - cc = CPU_GET_CLASS(cpu); - real_do_unassigned_access = cc->do_unassigned_access; - cc->do_unassigned_access = mips_jazz_do_unassigned_access; - - /* allocate RAM */ - memory_region_allocate_system_memory(ram, NULL, "mips_jazz.ram", - machine->ram_size); - memory_region_add_subregion(address_space, 0, ram); - - memory_region_init_ram(bios, NULL, "mips_jazz.bios", MAGNUM_BIOS_SIZE, - &error_fatal); - vmstate_register_ram_global(bios); - memory_region_set_readonly(bios, true); - memory_region_init_alias(bios2, NULL, "mips_jazz.bios", bios, - 0, MAGNUM_BIOS_SIZE); - memory_region_add_subregion(address_space, 0x1fc00000LL, bios); - memory_region_add_subregion(address_space, 0xfff00000LL, bios2); - - /* load the BIOS image. */ - if (bios_name == NULL) - bios_name = BIOS_FILENAME; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - bios_size = load_image_targphys(filename, 0xfff00000LL, - MAGNUM_BIOS_SIZE); - g_free(filename); - } else { - bios_size = -1; - } - if ((bios_size < 0 || bios_size > MAGNUM_BIOS_SIZE) && !qtest_enabled()) { - error_report("Could not load MIPS bios '%s'", bios_name); - exit(1); - } - - /* Init CPU internal devices */ - cpu_mips_irq_init_cpu(env); - cpu_mips_clock_init(env); - - /* Chipset */ - rc4030 = rc4030_init(&dmas, &rc4030_dma_mr); - sysbus = SYS_BUS_DEVICE(rc4030); - sysbus_connect_irq(sysbus, 0, env->irq[6]); - sysbus_connect_irq(sysbus, 1, env->irq[3]); - memory_region_add_subregion(address_space, 0x80000000, - sysbus_mmio_get_region(sysbus, 0)); - memory_region_add_subregion(address_space, 0xf0000000, - sysbus_mmio_get_region(sysbus, 1)); - memory_region_init_io(dma_dummy, NULL, &dma_dummy_ops, NULL, "dummy_dma", 0x1000); - memory_region_add_subregion(address_space, 0x8000d000, dma_dummy); - - /* ISA bus: IO space at 0x90000000, mem space at 0x91000000 */ - memory_region_init(isa_io, NULL, "isa-io", 0x00010000); - 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, &error_abort); - - /* ISA devices */ - i8259 = i8259_init(isa_bus, env->irq[4]); - isa_bus_irqs(isa_bus, i8259); - DMA_init(isa_bus, 0); - pit = pit_init(isa_bus, 0x40, 0, NULL); - pcspk_init(isa_bus, pit); - - /* Video card */ - switch (jazz_model) { - case JAZZ_MAGNUM: - dev = qdev_create(NULL, "sysbus-g364"); - qdev_init_nofail(dev); - sysbus = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(sysbus, 0, 0x60080000); - sysbus_mmio_map(sysbus, 1, 0x40000000); - sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 3)); - { - /* 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_fatal); - vmstate_register_ram_global(rom_mr); - memory_region_set_readonly(rom_mr, true); - uint8_t *rom = memory_region_get_ram_ptr(rom_mr); - memory_region_add_subregion(address_space, 0x60000000, rom_mr); - rom[0] = 0x10; /* Mips G364 */ - } - break; - case JAZZ_PICA61: - isa_vga_mm_init(0x40000000, 0x60000000, 0, get_system_memory()); - break; - default: - break; - } - - /* Network controller */ - for (n = 0; n < nb_nics; n++) { - nd = &nd_table[n]; - if (!nd->model) - nd->model = g_strdup("dp83932"); - if (strcmp(nd->model, "dp83932") == 0) { - qemu_check_nic_model(nd, "dp83932"); - - dev = qdev_create(NULL, "dp8393x"); - qdev_set_nic_properties(dev, nd); - qdev_prop_set_uint8(dev, "it_shift", 2); - qdev_prop_set_ptr(dev, "dma_mr", rc4030_dma_mr); - qdev_init_nofail(dev); - sysbus = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(sysbus, 0, 0x80001000); - sysbus_mmio_map(sysbus, 1, 0x8000b000); - sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 4)); - break; - } else if (is_help_option(nd->model)) { - fprintf(stderr, "qemu: Supported NICs: dp83932\n"); - exit(1); - } else { - fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd->model); - exit(1); - } - } - - /* SCSI adapter */ - esp_init(0x80002000, 0, - rc4030_dma_read, rc4030_dma_write, dmas[0], - qdev_get_gpio_in(rc4030, 5), &esp_reset, &dma_enable); - - /* Floppy */ - if (drive_get_max_bus(IF_FLOPPY) >= MAX_FD) { - fprintf(stderr, "qemu: too many floppy drives\n"); - exit(1); - } - for (n = 0; n < MAX_FD; n++) { - fds[n] = drive_get(IF_FLOPPY, 0, n); - } - /* 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); - memory_region_init_io(rtc, NULL, &rtc_ops, NULL, "rtc", 0x1000); - memory_region_add_subregion(address_space, 0x80004000, rtc); - - /* Keyboard (i8042) */ - i8042_mm_init(qdev_get_gpio_in(rc4030, 6), qdev_get_gpio_in(rc4030, 7), - i8042, 0x1000, 0x1); - memory_region_add_subregion(address_space, 0x80005000, i8042); - - /* Serial ports */ - if (serial_hds[0]) { - serial_mm_init(address_space, 0x80006000, 0, - qdev_get_gpio_in(rc4030, 8), 8000000/16, - serial_hds[0], DEVICE_NATIVE_ENDIAN); - } - if (serial_hds[1]) { - serial_mm_init(address_space, 0x80007000, 0, - qdev_get_gpio_in(rc4030, 9), 8000000/16, - serial_hds[1], DEVICE_NATIVE_ENDIAN); - } - - /* Parallel port */ - if (parallel_hds[0]) - parallel_mm_init(address_space, 0x80008000, 0, - qdev_get_gpio_in(rc4030, 0), parallel_hds[0]); - - /* FIXME: missing Jazz sound at 0x8000c000, rc4030[2] */ - - /* NVRAM */ - dev = qdev_create(NULL, "ds1225y"); - qdev_init_nofail(dev); - sysbus = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(sysbus, 0, 0x80009000); - - /* LED indicator */ - sysbus_create_simple("jazz-led", 0x8000f000, NULL); -} - -static -void mips_magnum_init(MachineState *machine) -{ - mips_jazz_init(machine, JAZZ_MAGNUM); -} - -static -void mips_pica61_init(MachineState *machine) -{ - mips_jazz_init(machine, JAZZ_PICA61); -} - -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 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) -{ - type_register_static(&mips_magnum_type); - type_register_static(&mips_pica61_type); -} - -type_init(mips_jazz_machine_init) diff --git a/qemu/hw/mips/mips_malta.c b/qemu/hw/mips/mips_malta.c deleted file mode 100644 index fa769e5c0..000000000 --- a/qemu/hw/mips/mips_malta.c +++ /dev/null @@ -1,1270 +0,0 @@ -/* - * QEMU Malta board support - * - * Copyright (c) 2006 Aurelien Jarno - * - * 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 "qemu-common.h" -#include "cpu.h" -#include "hw/hw.h" -#include "hw/i386/pc.h" -#include "hw/char/serial.h" -#include "hw/block/fdc.h" -#include "net/net.h" -#include "hw/boards.h" -#include "hw/i2c/smbus.h" -#include "sysemu/block-backend.h" -#include "hw/block/flash.h" -#include "hw/mips/mips.h" -#include "hw/mips/cpudevs.h" -#include "hw/pci/pci.h" -#include "sysemu/char.h" -#include "sysemu/sysemu.h" -#include "sysemu/arch_init.h" -#include "qemu/log.h" -#include "hw/mips/bios.h" -#include "hw/ide.h" -#include "hw/loader.h" -#include "elf.h" -#include "hw/timer/mc146818rtc.h" -#include "hw/timer/i8254.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "exec/address-spaces.h" -#include "hw/sysbus.h" /* SysBusDevice */ -#include "qemu/host-utils.h" -#include "sysemu/qtest.h" -#include "qemu/error-report.h" -#include "hw/empty_slot.h" -#include "sysemu/kvm.h" -#include "exec/semihost.h" -#include "hw/mips/cps.h" - -//#define DEBUG_BOARD_INIT - -#define ENVP_ADDR 0x80002000l -#define ENVP_NB_ENTRIES 16 -#define ENVP_ENTRY_SIZE 256 - -/* Hardware addresses */ -#define FLASH_ADDRESS 0x1e000000ULL -#define FPGA_ADDRESS 0x1f000000ULL -#define RESET_ADDRESS 0x1fc00000ULL - -#define FLASH_SIZE 0x400000 - -#define MAX_IDE_BUS 2 - -typedef struct { - MemoryRegion iomem; - MemoryRegion iomem_lo; /* 0 - 0x900 */ - MemoryRegion iomem_hi; /* 0xa00 - 0x100000 */ - uint32_t leds; - uint32_t brk; - uint32_t gpout; - uint32_t i2cin; - uint32_t i2coe; - uint32_t i2cout; - uint32_t i2csel; - CharDriverState *display; - char display_text[9]; - SerialState *uart; -} MaltaFPGAState; - -#define TYPE_MIPS_MALTA "mips-malta" -#define MIPS_MALTA(obj) OBJECT_CHECK(MaltaState, (obj), TYPE_MIPS_MALTA) - -typedef struct { - SysBusDevice parent_obj; - - MIPSCPSState *cps; - qemu_irq *i8259; -} MaltaState; - -static ISADevice *pit; - -static struct _loaderparams { - int ram_size, ram_low_size; - const char *kernel_filename; - const char *kernel_cmdline; - const char *initrd_filename; -} loaderparams; - -/* Malta FPGA */ -static void malta_fpga_update_display(void *opaque) -{ - char leds_text[9]; - int i; - MaltaFPGAState *s = opaque; - - for (i = 7 ; i >= 0 ; i--) { - if (s->leds & (1 << i)) - leds_text[i] = '#'; - else - leds_text[i] = ' '; - } - leds_text[8] = '\0'; - - qemu_chr_fe_printf(s->display, "\e[H\n\n|\e[32m%-8.8s\e[00m|\r\n", leds_text); - qemu_chr_fe_printf(s->display, "\n\n\n\n|\e[31m%-8.8s\e[00m|", s->display_text); -} - -/* - * EEPROM 24C01 / 24C02 emulation. - * - * Emulation for serial EEPROMs: - * 24C01 - 1024 bit (128 x 8) - * 24C02 - 2048 bit (256 x 8) - * - * Typical device names include Microchip 24C02SC or SGS Thomson ST24C02. - */ - -//~ #define DEBUG - -#if defined(DEBUG) -# define logout(fmt, ...) fprintf(stderr, "MALTA\t%-24s" fmt, __func__, ## __VA_ARGS__) -#else -# define logout(fmt, ...) ((void)0) -#endif - -struct _eeprom24c0x_t { - uint8_t tick; - uint8_t address; - uint8_t command; - uint8_t ack; - uint8_t scl; - uint8_t sda; - uint8_t data; - //~ uint16_t size; - uint8_t contents[256]; -}; - -typedef struct _eeprom24c0x_t eeprom24c0x_t; - -static eeprom24c0x_t spd_eeprom = { - .contents = { - /* 00000000: */ 0x80,0x08,0xFF,0x0D,0x0A,0xFF,0x40,0x00, - /* 00000008: */ 0x01,0x75,0x54,0x00,0x82,0x08,0x00,0x01, - /* 00000010: */ 0x8F,0x04,0x02,0x01,0x01,0x00,0x00,0x00, - /* 00000018: */ 0x00,0x00,0x00,0x14,0x0F,0x14,0x2D,0xFF, - /* 00000020: */ 0x15,0x08,0x15,0x08,0x00,0x00,0x00,0x00, - /* 00000028: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - /* 00000030: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - /* 00000038: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x12,0xD0, - /* 00000040: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - /* 00000048: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - /* 00000050: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - /* 00000058: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - /* 00000060: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - /* 00000068: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - /* 00000070: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - /* 00000078: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x64,0xF4, - }, -}; - -static void generate_eeprom_spd(uint8_t *eeprom, ram_addr_t ram_size) -{ - enum { SDR = 0x4, DDR2 = 0x8 } type; - uint8_t *spd = spd_eeprom.contents; - uint8_t nbanks = 0; - uint16_t density = 0; - int i; - - /* work in terms of MB */ - ram_size >>= 20; - - while ((ram_size >= 4) && (nbanks <= 2)) { - int sz_log2 = MIN(31 - clz32(ram_size), 14); - nbanks++; - density |= 1 << (sz_log2 - 2); - ram_size -= 1 << sz_log2; - } - - /* split to 2 banks if possible */ - if ((nbanks == 1) && (density > 1)) { - nbanks++; - density >>= 1; - } - - if (density & 0xff00) { - density = (density & 0xe0) | ((density >> 8) & 0x1f); - type = DDR2; - } else if (!(density & 0x1f)) { - type = DDR2; - } else { - type = SDR; - } - - if (ram_size) { - fprintf(stderr, "Warning: SPD cannot represent final %dMB" - " of SDRAM\n", (int)ram_size); - } - - /* fill in SPD memory information */ - spd[2] = type; - spd[5] = nbanks; - spd[31] = density; - - /* checksum */ - spd[63] = 0; - for (i = 0; i < 63; i++) { - spd[63] += spd[i]; - } - - /* copy for SMBUS */ - memcpy(eeprom, spd, sizeof(spd_eeprom.contents)); -} - -static void generate_eeprom_serial(uint8_t *eeprom) -{ - int i, pos = 0; - uint8_t mac[6] = { 0x00 }; - uint8_t sn[5] = { 0x01, 0x23, 0x45, 0x67, 0x89 }; - - /* version */ - eeprom[pos++] = 0x01; - - /* count */ - eeprom[pos++] = 0x02; - - /* MAC address */ - eeprom[pos++] = 0x01; /* MAC */ - eeprom[pos++] = 0x06; /* length */ - memcpy(&eeprom[pos], mac, sizeof(mac)); - pos += sizeof(mac); - - /* serial number */ - eeprom[pos++] = 0x02; /* serial */ - eeprom[pos++] = 0x05; /* length */ - memcpy(&eeprom[pos], sn, sizeof(sn)); - pos += sizeof(sn); - - /* checksum */ - eeprom[pos] = 0; - for (i = 0; i < pos; i++) { - eeprom[pos] += eeprom[i]; - } -} - -static uint8_t eeprom24c0x_read(eeprom24c0x_t *eeprom) -{ - logout("%u: scl = %u, sda = %u, data = 0x%02x\n", - eeprom->tick, eeprom->scl, eeprom->sda, eeprom->data); - return eeprom->sda; -} - -static void eeprom24c0x_write(eeprom24c0x_t *eeprom, int scl, int sda) -{ - if (eeprom->scl && scl && (eeprom->sda != sda)) { - logout("%u: scl = %u->%u, sda = %u->%u i2c %s\n", - eeprom->tick, eeprom->scl, scl, eeprom->sda, sda, - sda ? "stop" : "start"); - if (!sda) { - eeprom->tick = 1; - eeprom->command = 0; - } - } else if (eeprom->tick == 0 && !eeprom->ack) { - /* Waiting for start. */ - logout("%u: scl = %u->%u, sda = %u->%u wait for i2c start\n", - eeprom->tick, eeprom->scl, scl, eeprom->sda, sda); - } else if (!eeprom->scl && scl) { - logout("%u: scl = %u->%u, sda = %u->%u trigger bit\n", - eeprom->tick, eeprom->scl, scl, eeprom->sda, sda); - if (eeprom->ack) { - logout("\ti2c ack bit = 0\n"); - sda = 0; - eeprom->ack = 0; - } else if (eeprom->sda == sda) { - uint8_t bit = (sda != 0); - logout("\ti2c bit = %d\n", bit); - if (eeprom->tick < 9) { - eeprom->command <<= 1; - eeprom->command += bit; - eeprom->tick++; - if (eeprom->tick == 9) { - logout("\tcommand 0x%04x, %s\n", eeprom->command, - bit ? "read" : "write"); - eeprom->ack = 1; - } - } else if (eeprom->tick < 17) { - if (eeprom->command & 1) { - sda = ((eeprom->data & 0x80) != 0); - } - eeprom->address <<= 1; - eeprom->address += bit; - eeprom->tick++; - eeprom->data <<= 1; - if (eeprom->tick == 17) { - eeprom->data = eeprom->contents[eeprom->address]; - logout("\taddress 0x%04x, data 0x%02x\n", - eeprom->address, eeprom->data); - eeprom->ack = 1; - eeprom->tick = 0; - } - } else if (eeprom->tick >= 17) { - sda = 0; - } - } else { - logout("\tsda changed with raising scl\n"); - } - } else { - logout("%u: scl = %u->%u, sda = %u->%u\n", eeprom->tick, eeprom->scl, - scl, eeprom->sda, sda); - } - eeprom->scl = scl; - eeprom->sda = sda; -} - -static uint64_t malta_fpga_read(void *opaque, hwaddr addr, - unsigned size) -{ - MaltaFPGAState *s = opaque; - uint32_t val = 0; - uint32_t saddr; - - saddr = (addr & 0xfffff); - - switch (saddr) { - - /* SWITCH Register */ - case 0x00200: - val = 0x00000000; /* All switches closed */ - break; - - /* STATUS Register */ - case 0x00208: -#ifdef TARGET_WORDS_BIGENDIAN - val = 0x00000012; -#else - val = 0x00000010; -#endif - break; - - /* JMPRS Register */ - case 0x00210: - val = 0x00; - break; - - /* LEDBAR Register */ - case 0x00408: - val = s->leds; - break; - - /* BRKRES Register */ - case 0x00508: - val = s->brk; - break; - - /* UART Registers are handled directly by the serial device */ - - /* GPOUT Register */ - case 0x00a00: - val = s->gpout; - break; - - /* XXX: implement a real I2C controller */ - - /* GPINP Register */ - case 0x00a08: - /* IN = OUT until a real I2C control is implemented */ - if (s->i2csel) - val = s->i2cout; - else - val = 0x00; - break; - - /* I2CINP Register */ - case 0x00b00: - val = ((s->i2cin & ~1) | eeprom24c0x_read(&spd_eeprom)); - break; - - /* I2COE Register */ - case 0x00b08: - val = s->i2coe; - break; - - /* I2COUT Register */ - case 0x00b10: - val = s->i2cout; - break; - - /* I2CSEL Register */ - case 0x00b18: - val = s->i2csel; - break; - - default: -#if 0 - printf ("malta_fpga_read: Bad register offset 0x" TARGET_FMT_lx "\n", - addr); -#endif - break; - } - return val; -} - -static void malta_fpga_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - MaltaFPGAState *s = opaque; - uint32_t saddr; - - saddr = (addr & 0xfffff); - - switch (saddr) { - - /* SWITCH Register */ - case 0x00200: - break; - - /* JMPRS Register */ - case 0x00210: - break; - - /* LEDBAR Register */ - case 0x00408: - s->leds = val & 0xff; - malta_fpga_update_display(s); - break; - - /* ASCIIWORD Register */ - case 0x00410: - snprintf(s->display_text, 9, "%08X", (uint32_t)val); - malta_fpga_update_display(s); - break; - - /* ASCIIPOS0 to ASCIIPOS7 Registers */ - case 0x00418: - case 0x00420: - case 0x00428: - case 0x00430: - case 0x00438: - case 0x00440: - case 0x00448: - case 0x00450: - s->display_text[(saddr - 0x00418) >> 3] = (char) val; - malta_fpga_update_display(s); - break; - - /* SOFTRES Register */ - case 0x00500: - if (val == 0x42) - qemu_system_reset_request (); - break; - - /* BRKRES Register */ - case 0x00508: - s->brk = val & 0xff; - break; - - /* UART Registers are handled directly by the serial device */ - - /* GPOUT Register */ - case 0x00a00: - s->gpout = val & 0xff; - break; - - /* I2COE Register */ - case 0x00b08: - s->i2coe = val & 0x03; - break; - - /* I2COUT Register */ - case 0x00b10: - eeprom24c0x_write(&spd_eeprom, val & 0x02, val & 0x01); - s->i2cout = val; - break; - - /* I2CSEL Register */ - case 0x00b18: - s->i2csel = val & 0x01; - break; - - default: -#if 0 - printf ("malta_fpga_write: Bad register offset 0x" TARGET_FMT_lx "\n", - addr); -#endif - break; - } -} - -static const MemoryRegionOps malta_fpga_ops = { - .read = malta_fpga_read, - .write = malta_fpga_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void malta_fpga_reset(void *opaque) -{ - MaltaFPGAState *s = opaque; - - s->leds = 0x00; - s->brk = 0x0a; - s->gpout = 0x00; - s->i2cin = 0x3; - s->i2coe = 0x0; - s->i2cout = 0x3; - s->i2csel = 0x1; - - s->display_text[8] = '\0'; - snprintf(s->display_text, 9, " "); -} - -static void malta_fpga_led_init(CharDriverState *chr) -{ - qemu_chr_fe_printf(chr, "\e[HMalta LEDBAR\r\n"); - qemu_chr_fe_printf(chr, "+--------+\r\n"); - qemu_chr_fe_printf(chr, "+ +\r\n"); - qemu_chr_fe_printf(chr, "+--------+\r\n"); - qemu_chr_fe_printf(chr, "\n"); - qemu_chr_fe_printf(chr, "Malta ASCII\r\n"); - qemu_chr_fe_printf(chr, "+--------+\r\n"); - qemu_chr_fe_printf(chr, "+ +\r\n"); - qemu_chr_fe_printf(chr, "+--------+\r\n"); -} - -static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space, - hwaddr base, qemu_irq uart_irq, CharDriverState *uart_chr) -{ - MaltaFPGAState *s; - - s = (MaltaFPGAState *)g_malloc0(sizeof(MaltaFPGAState)); - - memory_region_init_io(&s->iomem, NULL, &malta_fpga_ops, s, - "malta-fpga", 0x100000); - memory_region_init_alias(&s->iomem_lo, NULL, "malta-fpga", - &s->iomem, 0, 0x900); - memory_region_init_alias(&s->iomem_hi, NULL, "malta-fpga", - &s->iomem, 0xa00, 0x10000-0xa00); - - memory_region_add_subregion(address_space, base, &s->iomem_lo); - memory_region_add_subregion(address_space, base + 0xa00, &s->iomem_hi); - - s->display = qemu_chr_new("fpga", "vc:320x200", malta_fpga_led_init); - - s->uart = serial_mm_init(address_space, base + 0x900, 3, uart_irq, - 230400, uart_chr, DEVICE_NATIVE_ENDIAN); - - malta_fpga_reset(s); - qemu_register_reset(malta_fpga_reset, s); - - return s; -} - -/* Network support */ -static void network_init(PCIBus *pci_bus) -{ - int i; - - for(i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - const char *default_devaddr = NULL; - - if (i == 0 && (!nd->model || strcmp(nd->model, "pcnet") == 0)) - /* The malta board has a PCNet card using PCI SLOT 11 */ - default_devaddr = "0b"; - - pci_nic_init_nofail(nd, pci_bus, "pcnet", default_devaddr); - } -} - -/* ROM and pseudo bootloader - - The following code implements a very very simple bootloader. It first - loads the registers a0 to a3 to the values expected by the OS, and - then jump at the kernel address. - - The bootloader should pass the locations of the kernel arguments and - environment variables tables. Those tables contain the 32-bit address - of NULL terminated strings. The environment variables table should be - terminated by a NULL address. - - For a simpler implementation, the number of kernel arguments is fixed - to two (the name of the kernel and the command line), and the two - tables are actually the same one. - - The registers a0 to a3 should contain the following values: - a0 - number of kernel arguments - a1 - 32-bit address of the kernel arguments table - a2 - 32-bit address of the environment variables table - a3 - RAM size in bytes -*/ - -static void write_bootloader(uint8_t *base, int64_t run_addr, - int64_t kernel_entry) -{ - uint32_t *p; - - /* Small bootloader */ - p = (uint32_t *)base; - - stl_p(p++, 0x08000000 | /* j 0x1fc00580 */ - ((run_addr + 0x580) & 0x0fffffff) >> 2); - stl_p(p++, 0x00000000); /* nop */ - - /* YAMON service vector */ - stl_p(base + 0x500, run_addr + 0x0580); /* start: */ - stl_p(base + 0x504, run_addr + 0x083c); /* print_count: */ - stl_p(base + 0x520, run_addr + 0x0580); /* start: */ - stl_p(base + 0x52c, run_addr + 0x0800); /* flush_cache: */ - stl_p(base + 0x534, run_addr + 0x0808); /* print: */ - stl_p(base + 0x538, run_addr + 0x0800); /* reg_cpu_isr: */ - stl_p(base + 0x53c, run_addr + 0x0800); /* unred_cpu_isr: */ - stl_p(base + 0x540, run_addr + 0x0800); /* reg_ic_isr: */ - stl_p(base + 0x544, run_addr + 0x0800); /* unred_ic_isr: */ - stl_p(base + 0x548, run_addr + 0x0800); /* reg_esr: */ - stl_p(base + 0x54c, run_addr + 0x0800); /* unreg_esr: */ - stl_p(base + 0x550, run_addr + 0x0800); /* getchar: */ - stl_p(base + 0x554, run_addr + 0x0800); /* syscon_read: */ - - - /* Second part of the bootloader */ - p = (uint32_t *) (base + 0x580); - - if (semihosting_get_argc()) { - /* Preserve a0 content as arguments have been passed */ - stl_p(p++, 0x00000000); /* nop */ - } else { - stl_p(p++, 0x24040002); /* addiu a0, zero, 2 */ - } - stl_p(p++, 0x3c1d0000 | (((ENVP_ADDR - 64) >> 16) & 0xffff)); /* lui sp, high(ENVP_ADDR) */ - stl_p(p++, 0x37bd0000 | ((ENVP_ADDR - 64) & 0xffff)); /* ori sp, sp, low(ENVP_ADDR) */ - stl_p(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff)); /* lui a1, high(ENVP_ADDR) */ - stl_p(p++, 0x34a50000 | (ENVP_ADDR & 0xffff)); /* ori a1, a1, low(ENVP_ADDR) */ - stl_p(p++, 0x3c060000 | (((ENVP_ADDR + 8) >> 16) & 0xffff)); /* lui a2, high(ENVP_ADDR + 8) */ - stl_p(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff)); /* ori a2, a2, low(ENVP_ADDR + 8) */ - stl_p(p++, 0x3c070000 | (loaderparams.ram_low_size >> 16)); /* lui a3, high(ram_low_size) */ - stl_p(p++, 0x34e70000 | (loaderparams.ram_low_size & 0xffff)); /* ori a3, a3, low(ram_low_size) */ - - /* Load BAR registers as done by YAMON */ - stl_p(p++, 0x3c09b400); /* lui t1, 0xb400 */ - -#ifdef TARGET_WORDS_BIGENDIAN - stl_p(p++, 0x3c08df00); /* lui t0, 0xdf00 */ -#else - stl_p(p++, 0x340800df); /* ori t0, r0, 0x00df */ -#endif - stl_p(p++, 0xad280068); /* sw t0, 0x0068(t1) */ - - stl_p(p++, 0x3c09bbe0); /* lui t1, 0xbbe0 */ - -#ifdef TARGET_WORDS_BIGENDIAN - stl_p(p++, 0x3c08c000); /* lui t0, 0xc000 */ -#else - stl_p(p++, 0x340800c0); /* ori t0, r0, 0x00c0 */ -#endif - stl_p(p++, 0xad280048); /* sw t0, 0x0048(t1) */ -#ifdef TARGET_WORDS_BIGENDIAN - stl_p(p++, 0x3c084000); /* lui t0, 0x4000 */ -#else - stl_p(p++, 0x34080040); /* ori t0, r0, 0x0040 */ -#endif - stl_p(p++, 0xad280050); /* sw t0, 0x0050(t1) */ - -#ifdef TARGET_WORDS_BIGENDIAN - stl_p(p++, 0x3c088000); /* lui t0, 0x8000 */ -#else - stl_p(p++, 0x34080080); /* ori t0, r0, 0x0080 */ -#endif - stl_p(p++, 0xad280058); /* sw t0, 0x0058(t1) */ -#ifdef TARGET_WORDS_BIGENDIAN - stl_p(p++, 0x3c083f00); /* lui t0, 0x3f00 */ -#else - stl_p(p++, 0x3408003f); /* ori t0, r0, 0x003f */ -#endif - stl_p(p++, 0xad280060); /* sw t0, 0x0060(t1) */ - -#ifdef TARGET_WORDS_BIGENDIAN - stl_p(p++, 0x3c08c100); /* lui t0, 0xc100 */ -#else - stl_p(p++, 0x340800c1); /* ori t0, r0, 0x00c1 */ -#endif - stl_p(p++, 0xad280080); /* sw t0, 0x0080(t1) */ -#ifdef TARGET_WORDS_BIGENDIAN - stl_p(p++, 0x3c085e00); /* lui t0, 0x5e00 */ -#else - stl_p(p++, 0x3408005e); /* ori t0, r0, 0x005e */ -#endif - stl_p(p++, 0xad280088); /* sw t0, 0x0088(t1) */ - - /* Jump to kernel code */ - stl_p(p++, 0x3c1f0000 | ((kernel_entry >> 16) & 0xffff)); /* lui ra, high(kernel_entry) */ - stl_p(p++, 0x37ff0000 | (kernel_entry & 0xffff)); /* ori ra, ra, low(kernel_entry) */ - stl_p(p++, 0x03e00009); /* jalr ra */ - stl_p(p++, 0x00000000); /* nop */ - - /* YAMON subroutines */ - p = (uint32_t *) (base + 0x800); - stl_p(p++, 0x03e00009); /* jalr ra */ - stl_p(p++, 0x24020000); /* li v0,0 */ - /* 808 YAMON print */ - stl_p(p++, 0x03e06821); /* move t5,ra */ - stl_p(p++, 0x00805821); /* move t3,a0 */ - stl_p(p++, 0x00a05021); /* move t2,a1 */ - stl_p(p++, 0x91440000); /* lbu a0,0(t2) */ - stl_p(p++, 0x254a0001); /* addiu t2,t2,1 */ - stl_p(p++, 0x10800005); /* beqz a0,834 */ - stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x0ff0021c); /* jal 870 */ - stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x08000205); /* j 814 */ - stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x01a00009); /* jalr t5 */ - stl_p(p++, 0x01602021); /* move a0,t3 */ - /* 0x83c YAMON print_count */ - stl_p(p++, 0x03e06821); /* move t5,ra */ - stl_p(p++, 0x00805821); /* move t3,a0 */ - stl_p(p++, 0x00a05021); /* move t2,a1 */ - stl_p(p++, 0x00c06021); /* move t4,a2 */ - stl_p(p++, 0x91440000); /* lbu a0,0(t2) */ - stl_p(p++, 0x0ff0021c); /* jal 870 */ - stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x254a0001); /* addiu t2,t2,1 */ - stl_p(p++, 0x258cffff); /* addiu t4,t4,-1 */ - stl_p(p++, 0x1580fffa); /* bnez t4,84c */ - stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x01a00009); /* jalr t5 */ - stl_p(p++, 0x01602021); /* move a0,t3 */ - /* 0x870 */ - stl_p(p++, 0x3c08b800); /* lui t0,0xb400 */ - stl_p(p++, 0x350803f8); /* ori t0,t0,0x3f8 */ - stl_p(p++, 0x91090005); /* lbu t1,5(t0) */ - stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x31290040); /* andi t1,t1,0x40 */ - stl_p(p++, 0x1120fffc); /* beqz t1,878 */ - stl_p(p++, 0x00000000); /* nop */ - stl_p(p++, 0x03e00009); /* jalr ra */ - stl_p(p++, 0xa1040000); /* sb a0,0(t0) */ - -} - -static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t* prom_buf, int index, - const char *string, ...) -{ - va_list ap; - int32_t table_addr; - - if (index >= ENVP_NB_ENTRIES) - return; - - if (string == NULL) { - prom_buf[index] = 0; - return; - } - - table_addr = sizeof(int32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE; - prom_buf[index] = tswap32(ENVP_ADDR + table_addr); - - va_start(ap, string); - vsnprintf((char *)prom_buf + table_addr, ENVP_ENTRY_SIZE, string, ap); - va_end(ap); -} - -/* Kernel */ -static int64_t load_kernel (void) -{ - int64_t kernel_entry, kernel_high; - long initrd_size; - ram_addr_t initrd_offset; - int big_endian; - uint32_t *prom_buf; - long prom_size; - int prom_index = 0; - uint64_t (*xlate_to_kseg0) (void *opaque, uint64_t addr); - -#ifdef TARGET_WORDS_BIGENDIAN - big_endian = 1; -#else - big_endian = 0; -#endif - - if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL, - (uint64_t *)&kernel_entry, NULL, (uint64_t *)&kernel_high, - big_endian, EM_MIPS, 1, 0) < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - loaderparams.kernel_filename); - exit(1); - } - - /* Sanity check where the kernel has been linked */ - if (kvm_enabled()) { - if (kernel_entry & 0x80000000ll) { - error_report("KVM guest kernels must be linked in useg. " - "Did you forget to enable CONFIG_KVM_GUEST?"); - exit(1); - } - - xlate_to_kseg0 = cpu_mips_kvm_um_phys_to_kseg0; - } else { - if (!(kernel_entry & 0x80000000ll)) { - error_report("KVM guest kernels aren't supported with TCG. " - "Did you unintentionally enable CONFIG_KVM_GUEST?"); - exit(1); - } - - xlate_to_kseg0 = cpu_mips_phys_to_kseg0; - } - - /* load initrd */ - initrd_size = 0; - initrd_offset = 0; - if (loaderparams.initrd_filename) { - initrd_size = get_image_size (loaderparams.initrd_filename); - if (initrd_size > 0) { - initrd_offset = (kernel_high + ~INITRD_PAGE_MASK) & INITRD_PAGE_MASK; - if (initrd_offset + initrd_size > ram_size) { - fprintf(stderr, - "qemu: memory too small for initial ram disk '%s'\n", - loaderparams.initrd_filename); - exit(1); - } - initrd_size = load_image_targphys(loaderparams.initrd_filename, - initrd_offset, - ram_size - initrd_offset); - } - if (initrd_size == (target_ulong) -1) { - fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", - loaderparams.initrd_filename); - exit(1); - } - } - - /* Setup prom parameters. */ - prom_size = ENVP_NB_ENTRIES * (sizeof(int32_t) + ENVP_ENTRY_SIZE); - prom_buf = g_malloc(prom_size); - - prom_set(prom_buf, prom_index++, "%s", loaderparams.kernel_filename); - if (initrd_size > 0) { - prom_set(prom_buf, prom_index++, "rd_start=0x%" PRIx64 " rd_size=%li %s", - xlate_to_kseg0(NULL, initrd_offset), initrd_size, - loaderparams.kernel_cmdline); - } else { - prom_set(prom_buf, prom_index++, "%s", loaderparams.kernel_cmdline); - } - - prom_set(prom_buf, prom_index++, "memsize"); - prom_set(prom_buf, prom_index++, "%u", loaderparams.ram_low_size); - - prom_set(prom_buf, prom_index++, "ememsize"); - prom_set(prom_buf, prom_index++, "%u", loaderparams.ram_size); - - prom_set(prom_buf, prom_index++, "modetty0"); - prom_set(prom_buf, prom_index++, "38400n8r"); - prom_set(prom_buf, prom_index++, NULL); - - rom_add_blob_fixed("prom", prom_buf, prom_size, - cpu_mips_kseg0_to_phys(NULL, ENVP_ADDR)); - - g_free(prom_buf); - return kernel_entry; -} - -static void malta_mips_config(MIPSCPU *cpu) -{ - CPUMIPSState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - env->mvp->CP0_MVPConf0 |= ((smp_cpus - 1) << CP0MVPC0_PVPE) | - ((smp_cpus * cs->nr_threads - 1) << CP0MVPC0_PTC); -} - -static void main_cpu_reset(void *opaque) -{ - MIPSCPU *cpu = opaque; - CPUMIPSState *env = &cpu->env; - - cpu_reset(CPU(cpu)); - - /* The bootloader does not need to be rewritten as it is located in a - read only location. The kernel location and the arguments table - location does not change. */ - if (loaderparams.kernel_filename) { - env->CP0_Status &= ~(1 << CP0St_ERL); - } - - malta_mips_config(cpu); - - if (kvm_enabled()) { - /* Start running from the bootloader we wrote to end of RAM */ - env->active_tc.PC = 0x40000000 + loaderparams.ram_low_size; - } -} - -static void create_cpu_without_cps(const char *cpu_model, - qemu_irq *cbus_irq, qemu_irq *i8259_irq) -{ - CPUMIPSState *env; - MIPSCPU *cpu; - int i; - - 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); - } -} - -static -void mips_malta_init(MachineState *machine) -{ - ram_addr_t ram_size = machine->ram_size; - ram_addr_t ram_low_size; - const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; - const char *initrd_filename = machine->initrd_filename; - char *filename; - pflash_t *fl; - MemoryRegion *system_memory = get_system_memory(); - MemoryRegion *ram_high = g_new(MemoryRegion, 1); - MemoryRegion *ram_low_preio = g_new(MemoryRegion, 1); - MemoryRegion *ram_low_postio; - MemoryRegion *bios, *bios_copy = g_new(MemoryRegion, 1); - target_long bios_size = FLASH_SIZE; - const size_t smbus_eeprom_size = 8 * 256; - uint8_t *smbus_eeprom_buf = g_malloc0(smbus_eeprom_size); - int64_t kernel_entry, bootloader_run_addr; - PCIBus *pci_bus; - ISABus *isa_bus; - qemu_irq *isa_irq; - qemu_irq cbus_irq, i8259_irq; - int piix4_devfn; - I2CBus *smbus; - int i; - DriveInfo *dinfo; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - DriveInfo *fd[MAX_FD]; - int fl_idx = 0; - int fl_sectors = bios_size >> 16; - int be; - - DeviceState *dev = qdev_create(NULL, TYPE_MIPS_MALTA); - MaltaState *s = MIPS_MALTA(dev); - - /* The whole address space decoded by the GT-64120A doesn't generate - exception when accessing invalid memory. Create an empty slot to - emulate this feature. */ - empty_slot_init(0, 0x20000000); - - qdev_init_nofail(dev); - - /* Make sure the first 3 serial ports are associated with a device. */ - for(i = 0; i < 3; i++) { - if (!serial_hds[i]) { - char label[32]; - snprintf(label, sizeof(label), "serial%d", i); - serial_hds[i] = qemu_chr_new(label, "null", NULL); - } - } - - /* create CPU */ - create_cpu(s, machine->cpu_model, &cbus_irq, &i8259_irq); - - /* allocate RAM */ - if (ram_size > (2048u << 20)) { - fprintf(stderr, - "qemu: Too much memory for this machine: %d MB, maximum 2048 MB\n", - ((unsigned int)ram_size / (1 << 20))); - exit(1); - } - - /* register RAM at high address where it is undisturbed by IO */ - memory_region_allocate_system_memory(ram_high, NULL, "mips_malta.ram", - ram_size); - memory_region_add_subregion(system_memory, 0x80000000, ram_high); - - /* alias for pre IO hole access */ - memory_region_init_alias(ram_low_preio, NULL, "mips_malta_low_preio.ram", - ram_high, 0, MIN(ram_size, (256 << 20))); - memory_region_add_subregion(system_memory, 0, ram_low_preio); - - /* alias for post IO hole access, if there is enough RAM */ - if (ram_size > (512 << 20)) { - ram_low_postio = g_new(MemoryRegion, 1); - memory_region_init_alias(ram_low_postio, NULL, - "mips_malta_low_postio.ram", - ram_high, 512 << 20, - ram_size - (512 << 20)); - memory_region_add_subregion(system_memory, 512 << 20, ram_low_postio); - } - - /* generate SPD EEPROM data */ - generate_eeprom_spd(&smbus_eeprom_buf[0 * 256], ram_size); - generate_eeprom_serial(&smbus_eeprom_buf[6 * 256]); - -#ifdef TARGET_WORDS_BIGENDIAN - be = 1; -#else - be = 0; -#endif - /* FPGA */ - /* The CBUS UART is attached to the MIPS CPU INT2 pin, ie interrupt 4 */ - 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); -#ifdef DEBUG_BOARD_INIT - if (dinfo) { - printf("Register parallel flash %d size " TARGET_FMT_lx " at " - "addr %08llx '%s' %x\n", - fl_idx, bios_size, FLASH_ADDRESS, - blk_name(dinfo->bdrv), fl_sectors); - } -#endif - fl = pflash_cfi01_register(FLASH_ADDRESS, NULL, "mips_malta.bios", - BIOS_SIZE, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - 65536, fl_sectors, - 4, 0x0000, 0x0000, 0x0000, 0x0000, be); - bios = pflash_cfi01_get_memory(fl); - fl_idx++; - if (kernel_filename) { - ram_low_size = MIN(ram_size, 256 << 20); - /* For KVM we reserve 1MB of RAM for running bootloader */ - if (kvm_enabled()) { - ram_low_size -= 0x100000; - bootloader_run_addr = 0x40000000 + ram_low_size; - } else { - bootloader_run_addr = 0xbfc00000; - } - - /* Write a small bootloader to the flash location. */ - loaderparams.ram_size = ram_size; - loaderparams.ram_low_size = ram_low_size; - loaderparams.kernel_filename = kernel_filename; - loaderparams.kernel_cmdline = kernel_cmdline; - loaderparams.initrd_filename = initrd_filename; - kernel_entry = load_kernel(); - - 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(memory_region_get_ram_ptr(ram_low_preio) + - ram_low_size, - bootloader_run_addr, kernel_entry); - } - } else { - /* The flash region isn't executable from a KVM guest */ - if (kvm_enabled()) { - error_report("KVM enabled but no -kernel argument was specified. " - "Booting from flash is not supported with KVM."); - exit(1); - } - /* Load firmware from flash. */ - if (!dinfo) { - /* Load a BIOS image. */ - if (bios_name == NULL) { - bios_name = BIOS_FILENAME; - } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - bios_size = load_image_targphys(filename, FLASH_ADDRESS, - BIOS_SIZE); - g_free(filename); - } else { - bios_size = -1; - } - if ((bios_size < 0 || bios_size > BIOS_SIZE) && - !kernel_filename && !qtest_enabled()) { - error_report("Could not load MIPS bios '%s', and no " - "-kernel argument was specified", bios_name); - exit(1); - } - } - /* In little endian mode the 32bit words in the bios are swapped, - a neat trick which allows bi-endian firmware. */ -#ifndef TARGET_WORDS_BIGENDIAN - { - uint32_t *end, *addr = rom_ptr(FLASH_ADDRESS); - if (!addr) { - addr = memory_region_get_ram_ptr(bios); - } - end = (void *)addr + MIN(bios_size, 0x3e0000); - while (addr < end) { - bswap32s(addr); - addr++; - } - } -#endif - } - - /* - * Map the BIOS at a 2nd physical location, as on the real board. - * Copy it so that we can patch in the MIPS revision, which cannot be - * handled by an overlapping region as the resulting ROM code subpage - * regions are not executable. - */ - memory_region_init_ram(bios_copy, NULL, "bios.1fc", BIOS_SIZE, - &error_fatal); - if (!rom_copy(memory_region_get_ram_ptr(bios_copy), - FLASH_ADDRESS, BIOS_SIZE)) { - memcpy(memory_region_get_ram_ptr(bios_copy), - memory_region_get_ram_ptr(bios), BIOS_SIZE); - } - memory_region_set_readonly(bios_copy, true); - memory_region_add_subregion(system_memory, RESET_ADDRESS, bios_copy); - - /* Board ID = 0x420 (Malta Board with CoreLV) */ - stl_p(memory_region_get_ram_ptr(bios_copy) + 0x10, 0x00000420); - - /* - * We have a circular dependency problem: pci_bus depends on isa_irq, - * isa_irq is provided by i8259, i8259 depends on ISA, ISA depends - * on piix4, and piix4 depends on pci_bus. To stop the cycle we have - * qemu_irq_proxy() adds an extra bit of indirection, allowing us - * to resolve the isa_irq -> i8259 dependency after i8259 is initialized. - */ - isa_irq = qemu_irq_proxy(&s->i8259, 16); - - /* Northbridge */ - pci_bus = gt64120_register(isa_irq); - - /* Southbridge */ - ide_drive_get(hd, ARRAY_SIZE(hd)); - - piix4_devfn = piix4_init(pci_bus, &isa_bus, 80); - - /* Interrupt controller */ - /* The 8259 is attached to the MIPS CPU INT0 pin, ie interrupt 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); - pci_create_simple(pci_bus, piix4_devfn + 2, "piix4-usb-uhci"); - smbus = piix4_pm_init(pci_bus, piix4_devfn + 3, 0x1100, - isa_get_irq(NULL, 9), NULL, 0, NULL); - smbus_eeprom_init(smbus, 8, smbus_eeprom_buf, smbus_eeprom_size); - g_free(smbus_eeprom_buf); - pit = pit_init(isa_bus, 0x40, 0, NULL); - DMA_init(isa_bus, 0); - - /* Super I/O */ - isa_create_simple(isa_bus, "i8042"); - - rtc_init(isa_bus, 2000, NULL); - serial_hds_isa_init(isa_bus, 2); - parallel_hds_isa_init(isa_bus, 1); - - for(i = 0; i < MAX_FD; i++) { - fd[i] = drive_get(IF_FLOPPY, 0, i); - } - fdctrl_init_isa(isa_bus, fd); - - /* Network card */ - network_init(pci_bus); - - /* Optional PCI video card */ - pci_vga_init(pci_bus); -} - -static int mips_malta_sysbus_device_init(SysBusDevice *sysbusdev) -{ - return 0; -} - -static void mips_malta_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = mips_malta_sysbus_device_init; -} - -static const TypeInfo mips_malta_device = { - .name = TYPE_MIPS_MALTA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MaltaState), - .class_init = mips_malta_class_init, -}; - -static void mips_malta_machine_init(MachineClass *mc) -{ - mc->desc = "MIPS Malta Core LV"; - mc->init = mips_malta_init; - mc->max_cpus = 16; - mc->is_default = 1; -} - -DEFINE_MACHINE("malta", mips_malta_machine_init) - -static void mips_malta_register_types(void) -{ - type_register_static(&mips_malta_device); -} - -type_init(mips_malta_register_types) diff --git a/qemu/hw/mips/mips_mipssim.c b/qemu/hw/mips/mips_mipssim.c deleted file mode 100644 index a2c2a1646..000000000 --- a/qemu/hw/mips/mips_mipssim.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * QEMU/mipssim emulation - * - * Emulates a very simple machine model similar to the one used by the - * proprietary MIPS emulator. - * - * Copyright (c) 2007 Thiemo Seufer - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/hw.h" -#include "hw/mips/mips.h" -#include "hw/mips/cpudevs.h" -#include "hw/char/serial.h" -#include "hw/isa/isa.h" -#include "net/net.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" -#include "hw/mips/bios.h" -#include "hw/loader.h" -#include "elf.h" -#include "hw/sysbus.h" -#include "exec/address-spaces.h" -#include "qemu/error-report.h" -#include "sysemu/qtest.h" - -static struct _loaderparams { - int ram_size; - const char *kernel_filename; - const char *kernel_cmdline; - const char *initrd_filename; -} loaderparams; - -typedef struct ResetData { - MIPSCPU *cpu; - uint64_t vector; -} ResetData; - -static int64_t load_kernel(void) -{ - int64_t entry, kernel_high; - long kernel_size; - long initrd_size; - ram_addr_t initrd_offset; - int big_endian; - -#ifdef TARGET_WORDS_BIGENDIAN - big_endian = 1; -#else - big_endian = 0; -#endif - - kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, - NULL, (uint64_t *)&entry, NULL, - (uint64_t *)&kernel_high, big_endian, - EM_MIPS, 1, 0); - if (kernel_size >= 0) { - if ((entry & ~0x7fffffffULL) == 0x80000000) - entry = (int32_t)entry; - } else { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - loaderparams.kernel_filename); - exit(1); - } - - /* load initrd */ - initrd_size = 0; - initrd_offset = 0; - if (loaderparams.initrd_filename) { - initrd_size = get_image_size (loaderparams.initrd_filename); - if (initrd_size > 0) { - initrd_offset = (kernel_high + ~INITRD_PAGE_MASK) & INITRD_PAGE_MASK; - if (initrd_offset + initrd_size > loaderparams.ram_size) { - fprintf(stderr, - "qemu: memory too small for initial ram disk '%s'\n", - loaderparams.initrd_filename); - exit(1); - } - initrd_size = load_image_targphys(loaderparams.initrd_filename, - initrd_offset, loaderparams.ram_size - initrd_offset); - } - if (initrd_size == (target_ulong) -1) { - fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", - loaderparams.initrd_filename); - exit(1); - } - } - return entry; -} - -static void main_cpu_reset(void *opaque) -{ - ResetData *s = (ResetData *)opaque; - CPUMIPSState *env = &s->cpu->env; - - cpu_reset(CPU(s->cpu)); - env->active_tc.PC = s->vector & ~(target_ulong)1; - if (s->vector & 1) { - env->hflags |= MIPS_HFLAG_M16; - } -} - -static void mipsnet_init(int base, qemu_irq irq, NICInfo *nd) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_create(NULL, "mipsnet"); - qdev_set_nic_properties(dev, nd); - qdev_init_nofail(dev); - - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, irq); - memory_region_add_subregion(get_system_io(), - base, - sysbus_mmio_get_region(s, 0)); -} - -static void -mips_mipssim_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; - char *filename; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *isa = g_new(MemoryRegion, 1); - MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *bios = g_new(MemoryRegion, 1); - MIPSCPU *cpu; - CPUMIPSState *env; - ResetData *reset_info; - int bios_size; - - /* Init CPUs. */ - if (cpu_model == NULL) { -#ifdef TARGET_MIPS64 - cpu_model = "5Kf"; -#else - cpu_model = "24Kf"; -#endif - } - cpu = cpu_mips_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - env = &cpu->env; - - reset_info = g_malloc0(sizeof(ResetData)); - reset_info->cpu = cpu; - reset_info->vector = env->active_tc.PC; - qemu_register_reset(main_cpu_reset, reset_info); - - /* Allocate RAM. */ - memory_region_allocate_system_memory(ram, NULL, "mips_mipssim.ram", - ram_size); - memory_region_init_ram(bios, NULL, "mips_mipssim.bios", BIOS_SIZE, - &error_fatal); - vmstate_register_ram_global(bios); - memory_region_set_readonly(bios, true); - - memory_region_add_subregion(address_space_mem, 0, ram); - - /* Map the BIOS / boot exception handler. */ - memory_region_add_subregion(address_space_mem, 0x1fc00000LL, bios); - /* Load a BIOS / boot exception handler image. */ - if (bios_name == NULL) - bios_name = BIOS_FILENAME; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - bios_size = load_image_targphys(filename, 0x1fc00000LL, BIOS_SIZE); - g_free(filename); - } else { - bios_size = -1; - } - if ((bios_size < 0 || bios_size > BIOS_SIZE) && - !kernel_filename && !qtest_enabled()) { - /* Bail out if we have neither a kernel image nor boot vector code. */ - error_report("Could not load MIPS bios '%s', and no " - "-kernel argument was specified", bios_name); - exit(1); - } else { - /* We have a boot vector start address. */ - env->active_tc.PC = (target_long)(int32_t)0xbfc00000; - } - - if (kernel_filename) { - loaderparams.ram_size = ram_size; - loaderparams.kernel_filename = kernel_filename; - loaderparams.kernel_cmdline = kernel_cmdline; - loaderparams.initrd_filename = initrd_filename; - reset_info->vector = load_kernel(); - } - - /* Init CPU internal devices. */ - cpu_mips_irq_init_cpu(env); - cpu_mips_clock_init(env); - - /* Register 64 KB of ISA IO space at 0x1fd00000. */ - memory_region_init_alias(isa, NULL, "isa_mmio", - get_system_io(), 0, 0x00010000); - memory_region_add_subregion(get_system_memory(), 0x1fd00000, isa); - - /* A single 16450 sits at offset 0x3f8. It is attached to - MIPS CPU INT2, which is interrupt 4. */ - if (serial_hds[0]) - serial_init(0x3f8, env->irq[4], 115200, serial_hds[0], - get_system_io()); - - if (nd_table[0].used) - /* MIPSnet uses the MIPS CPU INT0, which is interrupt 2. */ - mipsnet_init(0x4200, env->irq[2], &nd_table[0]); -} - -static void mips_mipssim_machine_init(MachineClass *mc) -{ - mc->desc = "MIPS MIPSsim platform"; - mc->init = mips_mipssim_init; -} - -DEFINE_MACHINE("mipssim", mips_mipssim_machine_init) diff --git a/qemu/hw/mips/mips_r4k.c b/qemu/hw/mips/mips_r4k.c deleted file mode 100644 index 21aca981c..000000000 --- a/qemu/hw/mips/mips_r4k.c +++ /dev/null @@ -1,311 +0,0 @@ -/* - * QEMU/MIPS pseudo-board - * - * emulates a simple machine with ISA-like bus. - * ISA IO space mapped to the 0x14000000 (PHYS) and - * ISA memory at the 0x10000000 (PHYS, 16Mb in size). - * 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" -#include "hw/i386/pc.h" -#include "hw/char/serial.h" -#include "hw/isa/isa.h" -#include "net/net.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" -#include "hw/block/flash.h" -#include "qemu/log.h" -#include "hw/mips/bios.h" -#include "hw/ide.h" -#include "hw/loader.h" -#include "elf.h" -#include "hw/timer/mc146818rtc.h" -#include "hw/timer/i8254.h" -#include "sysemu/block-backend.h" -#include "exec/address-spaces.h" -#include "sysemu/qtest.h" - -#define MAX_IDE_BUS 2 - -static const int ide_iobase[2] = { 0x1f0, 0x170 }; -static const int ide_iobase2[2] = { 0x3f6, 0x376 }; -static const int ide_irq[2] = { 14, 15 }; - -static ISADevice *pit; /* PIT i8254 */ - -/* i8254 PIT is attached to the IRQ0 at PIC i8259 */ - -static struct _loaderparams { - int ram_size; - const char *kernel_filename; - const char *kernel_cmdline; - const char *initrd_filename; -} loaderparams; - -static void mips_qemu_write (void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - if ((addr & 0xffff) == 0 && val == 42) - qemu_system_reset_request (); - else if ((addr & 0xffff) == 4 && val == 42) - qemu_system_shutdown_request (); -} - -static uint64_t mips_qemu_read (void *opaque, hwaddr addr, - unsigned size) -{ - return 0; -} - -static const MemoryRegionOps mips_qemu_ops = { - .read = mips_qemu_read, - .write = mips_qemu_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -typedef struct ResetData { - MIPSCPU *cpu; - uint64_t vector; -} ResetData; - -static int64_t load_kernel(void) -{ - int64_t entry, kernel_high; - long kernel_size, initrd_size, params_size; - ram_addr_t initrd_offset; - uint32_t *params_buf; - int big_endian; - -#ifdef TARGET_WORDS_BIGENDIAN - big_endian = 1; -#else - big_endian = 0; -#endif - kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, - NULL, (uint64_t *)&entry, NULL, - (uint64_t *)&kernel_high, big_endian, - EM_MIPS, 1, 0); - if (kernel_size >= 0) { - if ((entry & ~0x7fffffffULL) == 0x80000000) - entry = (int32_t)entry; - } else { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - loaderparams.kernel_filename); - exit(1); - } - - /* load initrd */ - initrd_size = 0; - initrd_offset = 0; - if (loaderparams.initrd_filename) { - initrd_size = get_image_size (loaderparams.initrd_filename); - if (initrd_size > 0) { - initrd_offset = (kernel_high + ~INITRD_PAGE_MASK) & INITRD_PAGE_MASK; - if (initrd_offset + initrd_size > ram_size) { - fprintf(stderr, - "qemu: memory too small for initial ram disk '%s'\n", - loaderparams.initrd_filename); - exit(1); - } - initrd_size = load_image_targphys(loaderparams.initrd_filename, - initrd_offset, - ram_size - initrd_offset); - } - if (initrd_size == (target_ulong) -1) { - fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", - loaderparams.initrd_filename); - exit(1); - } - } - - /* Store command line. */ - params_size = 264; - params_buf = g_malloc(params_size); - - params_buf[0] = tswap32(ram_size); - params_buf[1] = tswap32(0x12345678); - - if (initrd_size > 0) { - snprintf((char *)params_buf + 8, 256, "rd_start=0x%" PRIx64 " rd_size=%li %s", - cpu_mips_phys_to_kseg0(NULL, initrd_offset), - initrd_size, loaderparams.kernel_cmdline); - } else { - snprintf((char *)params_buf + 8, 256, "%s", loaderparams.kernel_cmdline); - } - - rom_add_blob_fixed("params", params_buf, params_size, - (16 << 20) - 264); - - g_free(params_buf); - return entry; -} - -static void main_cpu_reset(void *opaque) -{ - ResetData *s = (ResetData *)opaque; - CPUMIPSState *env = &s->cpu->env; - - cpu_reset(CPU(s->cpu)); - env->active_tc.PC = s->vector; -} - -static const int sector_len = 32 * 1024; -static -void mips_r4k_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; - char *filename; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *bios; - MemoryRegion *iomem = g_new(MemoryRegion, 1); - MemoryRegion *isa_io = g_new(MemoryRegion, 1); - MemoryRegion *isa_mem = g_new(MemoryRegion, 1); - int bios_size; - MIPSCPU *cpu; - CPUMIPSState *env; - ResetData *reset_info; - int i; - qemu_irq *i8259; - ISABus *isa_bus; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - DriveInfo *dinfo; - int be; - - /* init CPUs */ - if (cpu_model == NULL) { -#ifdef TARGET_MIPS64 - cpu_model = "R4000"; -#else - cpu_model = "24Kf"; -#endif - } - cpu = cpu_mips_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - env = &cpu->env; - - reset_info = g_malloc0(sizeof(ResetData)); - reset_info->cpu = cpu; - reset_info->vector = env->active_tc.PC; - qemu_register_reset(main_cpu_reset, reset_info); - - /* allocate RAM */ - if (ram_size > (256 << 20)) { - fprintf(stderr, - "qemu: Too much memory for this machine: %d MB, maximum 256 MB\n", - ((unsigned int)ram_size / (1 << 20))); - exit(1); - } - memory_region_allocate_system_memory(ram, NULL, "mips_r4k.ram", ram_size); - - memory_region_add_subregion(address_space_mem, 0, ram); - - memory_region_init_io(iomem, NULL, &mips_qemu_ops, NULL, "mips-qemu", 0x10000); - memory_region_add_subregion(address_space_mem, 0x1fbf0000, iomem); - - /* Try to load a BIOS image. If this fails, we continue regardless, - but initialize the hardware ourselves. When a kernel gets - preloaded we also initialize the hardware, since the BIOS wasn't - run. */ - if (bios_name == NULL) - bios_name = BIOS_FILENAME; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - bios_size = get_image_size(filename); - } else { - bios_size = -1; - } -#ifdef TARGET_WORDS_BIGENDIAN - be = 1; -#else - be = 0; -#endif - 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_fatal); - vmstate_register_ram_global(bios); - memory_region_set_readonly(bios, true); - memory_region_add_subregion(get_system_memory(), 0x1fc00000, bios); - - load_image_targphys(filename, 0x1fc00000, BIOS_SIZE); - } else if ((dinfo = drive_get(IF_PFLASH, 0, 0)) != NULL) { - uint32_t mips_rom = 0x00400000; - if (!pflash_cfi01_register(0x1fc00000, NULL, "mips_r4k.bios", mips_rom, - blk_by_legacy_dinfo(dinfo), - sector_len, mips_rom / sector_len, - 4, 0, 0, 0, 0, be)) { - fprintf(stderr, "qemu: Error registering flash memory.\n"); - } - } else if (!qtest_enabled()) { - /* not fatal */ - fprintf(stderr, "qemu: Warning, could not load MIPS bios '%s'\n", - bios_name); - } - g_free(filename); - - if (kernel_filename) { - loaderparams.ram_size = ram_size; - loaderparams.kernel_filename = kernel_filename; - loaderparams.kernel_cmdline = kernel_cmdline; - loaderparams.initrd_filename = initrd_filename; - reset_info->vector = load_kernel(); - } - - /* Init CPU internal devices */ - cpu_mips_irq_init_cpu(env); - cpu_mips_clock_init(env); - - /* ISA bus: IO space at 0x14000000, mem space at 0x10000000 */ - memory_region_init_alias(isa_io, NULL, "isa-io", - get_system_io(), 0, 0x00010000); - 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(), &error_abort); - - /* The PIC is attached to the MIPS CPU INT0 pin */ - i8259 = i8259_init(isa_bus, env->irq[2]); - isa_bus_irqs(isa_bus, i8259); - - rtc_init(isa_bus, 2000, NULL); - - pit = pit_init(isa_bus, 0x40, 0, NULL); - - serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS); - - isa_vga_init(isa_bus); - - if (nd_table[0].used) - isa_ne2000_init(isa_bus, 0x300, 9, &nd_table[0]); - - ide_drive_get(hd, ARRAY_SIZE(hd)); - for(i = 0; i < MAX_IDE_BUS; i++) - isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], ide_irq[i], - hd[MAX_IDE_DEVS * i], - hd[MAX_IDE_DEVS * i + 1]); - - isa_create_simple(isa_bus, "i8042"); -} - -static void mips_machine_init(MachineClass *mc) -{ - mc->desc = "mips r4k platform"; - mc->init = mips_r4k_init; -} - -DEFINE_MACHINE("mips", mips_machine_init) diff --git a/qemu/hw/misc/Makefile.objs b/qemu/hw/misc/Makefile.objs deleted file mode 100644 index 93f952880..000000000 --- a/qemu/hw/misc/Makefile.objs +++ /dev/null @@ -1,52 +0,0 @@ -common-obj-$(CONFIG_APPLESMC) += applesmc.o -common-obj-$(CONFIG_MAX111X) += max111x.o -common-obj-$(CONFIG_TMP105) += tmp105.o -common-obj-$(CONFIG_ISA_DEBUG) += debugexit.o -common-obj-$(CONFIG_SGA) += sga.o -common-obj-$(CONFIG_ISA_TESTDEV) += pc-testdev.o -common-obj-$(CONFIG_PCI_TESTDEV) += pci-testdev.o - -obj-$(CONFIG_VMPORT) += vmport.o - -# ARM devices -common-obj-$(CONFIG_PL310) += arm_l2x0.o -common-obj-$(CONFIG_INTEGRATOR_DEBUG) += arm_integrator_debug.o -common-obj-$(CONFIG_A9SCU) += a9scu.o -common-obj-$(CONFIG_ARM11SCU) += arm11scu.o - -# PKUnity SoC devices -common-obj-$(CONFIG_PUV3) += puv3_pm.o - -common-obj-$(CONFIG_MACIO) += macio/ - -obj-$(CONFIG_IVSHMEM) += ivshmem.o - -obj-$(CONFIG_REALVIEW) += arm_sysctl.o -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 -obj-$(CONFIG_OMAP) += omap_clk.o -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 deleted file mode 100644 index 3e8ad8cd7..000000000 --- a/qemu/hw/misc/a9scu.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Cortex-A9MPCore Snoop Control Unit (SCU) emulation. - * - * Copyright (c) 2009 CodeSourcery. - * Copyright (c) 2011 Linaro Limited. - * Written by Paul Brook, Peter Maydell. - * - * 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, - unsigned size) -{ - A9SCUState *s = (A9SCUState *)opaque; - switch (offset) { - case 0x00: /* Control */ - return s->control; - case 0x04: /* Configuration */ - return (((1 << s->num_cpu) - 1) << 4) | (s->num_cpu - 1); - case 0x08: /* CPU Power Status */ - return s->status; - case 0x09: /* CPU status. */ - return s->status >> 8; - case 0x0a: /* CPU status. */ - return s->status >> 16; - case 0x0b: /* CPU status. */ - return s->status >> 24; - case 0x0c: /* Invalidate All Registers In Secure State */ - return 0; - case 0x40: /* Filtering Start Address Register */ - case 0x44: /* Filtering End Address Register */ - /* RAZ/WI, like an implementation with only one AXI master */ - return 0; - case 0x50: /* SCU Access Control Register */ - case 0x54: /* SCU Non-secure Access Control Register */ - /* unimplemented, fall through */ - default: - return 0; - } -} - -static void a9_scu_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - A9SCUState *s = (A9SCUState *)opaque; - uint32_t mask; - uint32_t shift; - switch (size) { - case 1: - mask = 0xff; - break; - case 2: - mask = 0xffff; - break; - case 4: - mask = 0xffffffff; - break; - default: - fprintf(stderr, "Invalid size %u in write to a9 scu register %x\n", - size, (unsigned)offset); - return; - } - - switch (offset) { - case 0x00: /* Control */ - s->control = value & 1; - break; - case 0x4: /* Configuration: RO */ - break; - case 0x08: case 0x09: case 0x0A: case 0x0B: /* Power Control */ - shift = (offset - 0x8) * 8; - s->status &= ~(mask << shift); - s->status |= ((value & mask) << shift); - break; - case 0x0c: /* Invalidate All Registers In Secure State */ - /* no-op as we do not implement caches */ - break; - case 0x40: /* Filtering Start Address Register */ - case 0x44: /* Filtering End Address Register */ - /* RAZ/WI, like an implementation with only one AXI master */ - break; - case 0x50: /* SCU Access Control Register */ - case 0x54: /* SCU Non-secure Access Control Register */ - /* unimplemented, fall through */ - default: - break; - } -} - -static const MemoryRegionOps a9_scu_ops = { - .read = a9_scu_read, - .write = a9_scu_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void a9_scu_reset(DeviceState *dev) -{ - A9SCUState *s = A9_SCU(dev); - s->control = 0; -} - -static void a9_scu_init(Object *obj) -{ - A9SCUState *s = A9_SCU(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - memory_region_init_io(&s->iomem, obj, &a9_scu_ops, s, - "a9-scu", 0x100); - sysbus_init_mmio(sbd, &s->iomem); -} - -static const VMStateDescription vmstate_a9_scu = { - .name = "a9-scu", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(control, A9SCUState), - VMSTATE_UINT32(status, A9SCUState), - VMSTATE_END_OF_LIST() - } -}; - -static Property a9_scu_properties[] = { - DEFINE_PROP_UINT32("num-cpu", A9SCUState, num_cpu, 1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void a9_scu_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->props = a9_scu_properties; - dc->vmsd = &vmstate_a9_scu; - dc->reset = a9_scu_reset; -} - -static const TypeInfo a9_scu_info = { - .name = TYPE_A9_SCU, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(A9SCUState), - .instance_init = a9_scu_init, - .class_init = a9_scu_class_init, -}; - -static void a9mp_register_types(void) -{ - type_register_static(&a9_scu_info); -} - -type_init(a9mp_register_types) diff --git a/qemu/hw/misc/applesmc.c b/qemu/hw/misc/applesmc.c deleted file mode 100644 index 77fab5b9d..000000000 --- a/qemu/hw/misc/applesmc.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Apple SMC controller - * - * Copyright (c) 2007 Alexander Graf - * - * Authors: Alexander Graf - * Susanne Graf - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 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 . - * - * ***************************************************************** - * - * In all Intel-based Apple hardware there is an SMC chip to control the - * backlight, fans and several other generic device parameters. It also - * contains the magic keys used to dongle Mac OS X to the device. - * - * This driver was mostly created by looking at the Linux AppleSMC driver - * implementation and does not support IRQ. - * - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/isa/isa.h" -#include "ui/console.h" -#include "qemu/timer.h" - -/* #define DEBUG_SMC */ - -#define APPLESMC_DEFAULT_IOBASE 0x300 -/* data port used by Apple SMC */ -#define APPLESMC_DATA_PORT 0x0 -/* command/status port used by Apple SMC */ -#define APPLESMC_CMD_PORT 0x4 -#define APPLESMC_NR_PORTS 32 - -#define APPLESMC_READ_CMD 0x10 -#define APPLESMC_WRITE_CMD 0x11 -#define APPLESMC_GET_KEY_BY_INDEX_CMD 0x12 -#define APPLESMC_GET_KEY_TYPE_CMD 0x13 - -#ifdef DEBUG_SMC -#define smc_debug(...) fprintf(stderr, "AppleSMC: " __VA_ARGS__) -#else -#define smc_debug(...) do { } while(0) -#endif - -static char default_osk[64] = "This is a dummy key. Enter the real key " - "using the -osk parameter"; - -struct AppleSMCData { - uint8_t len; - const char *key; - const char *data; - QLIST_ENTRY(AppleSMCData) node; -}; - -#define APPLE_SMC(obj) OBJECT_CHECK(AppleSMCState, (obj), TYPE_APPLE_SMC) - -typedef struct AppleSMCState AppleSMCState; -struct AppleSMCState { - ISADevice parent_obj; - - MemoryRegion io_data; - MemoryRegion io_cmd; - uint32_t iobase; - uint8_t cmd; - uint8_t status; - uint8_t key[4]; - uint8_t read_pos; - uint8_t data_len; - uint8_t data_pos; - uint8_t data[255]; - uint8_t charactic[4]; - char *osk; - QLIST_HEAD(, AppleSMCData) data_def; -}; - -static void applesmc_io_cmd_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - AppleSMCState *s = opaque; - - smc_debug("CMD Write B: %#x = %#x\n", addr, val); - switch(val) { - case APPLESMC_READ_CMD: - s->status = 0x0c; - break; - } - s->cmd = val; - s->read_pos = 0; - s->data_pos = 0; -} - -static void applesmc_fill_data(AppleSMCState *s) -{ - struct AppleSMCData *d; - - QLIST_FOREACH(d, &s->data_def, node) { - if (!memcmp(d->key, s->key, 4)) { - smc_debug("Key matched (%s Len=%d Data=%s)\n", d->key, - d->len, d->data); - memcpy(s->data, d->data, d->len); - return; - } - } -} - -static void applesmc_io_data_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - AppleSMCState *s = opaque; - - smc_debug("DATA Write B: %#x = %#x\n", addr, val); - switch(s->cmd) { - case APPLESMC_READ_CMD: - if(s->read_pos < 4) { - s->key[s->read_pos] = val; - s->status = 0x04; - } else if(s->read_pos == 4) { - s->data_len = val; - s->status = 0x05; - s->data_pos = 0; - smc_debug("Key = %c%c%c%c Len = %d\n", s->key[0], - s->key[1], s->key[2], s->key[3], val); - applesmc_fill_data(s); - } - s->read_pos++; - break; - } -} - -static uint64_t applesmc_io_data_read(void *opaque, hwaddr addr1, - unsigned size) -{ - AppleSMCState *s = opaque; - uint8_t retval = 0; - - switch(s->cmd) { - case APPLESMC_READ_CMD: - if(s->data_pos < s->data_len) { - retval = s->data[s->data_pos]; - smc_debug("READ_DATA[%d] = %#hhx\n", s->data_pos, - retval); - s->data_pos++; - if(s->data_pos == s->data_len) { - s->status = 0x00; - smc_debug("EOF\n"); - } else - s->status = 0x05; - } - } - smc_debug("DATA Read b: %#x = %#x\n", addr1, retval); - - return retval; -} - -static uint64_t applesmc_io_cmd_read(void *opaque, hwaddr addr1, unsigned size) -{ - AppleSMCState *s = opaque; - - smc_debug("CMD Read B: %#x\n", addr1); - return s->status; -} - -static void applesmc_add_key(AppleSMCState *s, const char *key, - int len, const char *data) -{ - struct AppleSMCData *def; - - def = g_malloc0(sizeof(struct AppleSMCData)); - def->key = key; - def->len = len; - def->data = data; - - QLIST_INSERT_HEAD(&s->data_def, def, node); -} - -static void qdev_applesmc_isa_reset(DeviceState *dev) -{ - AppleSMCState *s = APPLE_SMC(dev); - struct AppleSMCData *d, *next; - - /* Remove existing entries */ - QLIST_FOREACH_SAFE(d, &s->data_def, node, next) { - QLIST_REMOVE(d, node); - } - - applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03"); - applesmc_add_key(s, "OSK0", 32, s->osk); - applesmc_add_key(s, "OSK1", 32, s->osk + 32); - applesmc_add_key(s, "NATJ", 1, "\0"); - applesmc_add_key(s, "MSSP", 1, "\0"); - applesmc_add_key(s, "MSSD", 1, "\0x3"); -} - -static const MemoryRegionOps applesmc_data_io_ops = { - .write = applesmc_io_data_write, - .read = applesmc_io_data_read, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static const MemoryRegionOps applesmc_cmd_io_ops = { - .write = applesmc_io_cmd_write, - .read = applesmc_io_cmd_read, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void applesmc_isa_realize(DeviceState *dev, Error **errp) -{ - AppleSMCState *s = APPLE_SMC(dev); - - memory_region_init_io(&s->io_data, OBJECT(s), &applesmc_data_io_ops, s, - "applesmc-data", 4); - isa_register_ioport(&s->parent_obj, &s->io_data, - s->iobase + APPLESMC_DATA_PORT); - - memory_region_init_io(&s->io_cmd, OBJECT(s), &applesmc_cmd_io_ops, s, - "applesmc-cmd", 4); - isa_register_ioport(&s->parent_obj, &s->io_cmd, - s->iobase + APPLESMC_CMD_PORT); - - if (!s->osk || (strlen(s->osk) != 64)) { - fprintf(stderr, "WARNING: Using AppleSMC with invalid key\n"); - s->osk = default_osk; - } - - QLIST_INIT(&s->data_def); - qdev_applesmc_isa_reset(dev); -} - -static Property applesmc_isa_properties[] = { - DEFINE_PROP_UINT32(APPLESMC_PROP_IO_BASE, AppleSMCState, iobase, - APPLESMC_DEFAULT_IOBASE), - DEFINE_PROP_STRING("osk", AppleSMCState, osk), - DEFINE_PROP_END_OF_LIST(), -}; - -static void qdev_applesmc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = applesmc_isa_realize; - dc->reset = qdev_applesmc_isa_reset; - dc->props = applesmc_isa_properties; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo applesmc_isa_info = { - .name = TYPE_APPLE_SMC, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(AppleSMCState), - .class_init = qdev_applesmc_class_init, -}; - -static void applesmc_register_types(void) -{ - type_register_static(&applesmc_isa_info); -} - -type_init(applesmc_register_types) diff --git a/qemu/hw/misc/arm11scu.c b/qemu/hw/misc/arm11scu.c deleted file mode 100644 index 5e54b494b..000000000 --- a/qemu/hw/misc/arm11scu.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * ARM11MPCore Snoop Control Unit (SCU) emulation - * - * Copyright (c) 2006-2007 CodeSourcery. - * Copyright (c) 2013 SUSE LINUX Products GmbH - * Written by Paul Brook and Andreas Färber - * - * 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, - unsigned size) -{ - ARM11SCUState *s = (ARM11SCUState *)opaque; - int id; - /* SCU */ - switch (offset) { - case 0x00: /* Control. */ - return s->control; - case 0x04: /* Configuration. */ - id = ((1 << s->num_cpu) - 1) << 4; - return id | (s->num_cpu - 1); - case 0x08: /* CPU status. */ - return 0; - case 0x0c: /* Invalidate all. */ - return 0; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "mpcore_priv_read: Bad offset %x\n", (int)offset); - return 0; - } -} - -static void mpcore_scu_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - ARM11SCUState *s = (ARM11SCUState *)opaque; - /* SCU */ - switch (offset) { - case 0: /* Control register. */ - s->control = value & 1; - break; - case 0x0c: /* Invalidate all. */ - /* This is a no-op as cache is not emulated. */ - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "mpcore_priv_read: Bad offset %x\n", (int)offset); - } -} - -static const MemoryRegionOps mpcore_scu_ops = { - .read = mpcore_scu_read, - .write = mpcore_scu_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void arm11_scu_realize(DeviceState *dev, Error **errp) -{ -} - -static void arm11_scu_init(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - ARM11SCUState *s = ARM11_SCU(obj); - - memory_region_init_io(&s->iomem, OBJECT(s), - &mpcore_scu_ops, s, "mpcore-scu", 0x100); - sysbus_init_mmio(sbd, &s->iomem); -} - -static Property arm11_scu_properties[] = { - DEFINE_PROP_UINT32("num-cpu", ARM11SCUState, num_cpu, 1), - DEFINE_PROP_END_OF_LIST() -}; - -static void arm11_scu_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = arm11_scu_realize; - dc->props = arm11_scu_properties; -} - -static const TypeInfo arm11_scu_type_info = { - .name = TYPE_ARM11_SCU, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ARM11SCUState), - .instance_init = arm11_scu_init, - .class_init = arm11_scu_class_init, -}; - -static void arm11_scu_register_types(void) -{ - type_register_static(&arm11_scu_type_info); -} - -type_init(arm11_scu_register_types) diff --git a/qemu/hw/misc/arm_integrator_debug.c b/qemu/hw/misc/arm_integrator_debug.c deleted file mode 100644 index 902605fef..000000000 --- a/qemu/hw/misc/arm_integrator_debug.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * LED, Switch and Debug control registers for ARM Integrator Boards - * - * This is currently a stub for this functionality but at least - * ensures something other than unassigned_mem_read() handles access - * to this area. - * - * The real h/w is described at: - * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0159b/Babbfijf.html - * - * Copyright (c) 2013 Alex Bennée - * - * 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/sysbus.h" -#include "exec/address-spaces.h" -#include "hw/misc/arm_integrator_debug.h" - -#define INTEGRATOR_DEBUG(obj) \ - OBJECT_CHECK(IntegratorDebugState, (obj), TYPE_INTEGRATOR_DEBUG) - -typedef struct { - SysBusDevice parent_obj; - - MemoryRegion iomem; -} IntegratorDebugState; - -static uint64_t intdbg_control_read(void *opaque, hwaddr offset, - unsigned size) -{ - switch (offset >> 2) { - case 0: /* ALPHA */ - case 1: /* LEDS */ - case 2: /* SWITCHES */ - qemu_log_mask(LOG_UNIMP, - "%s: returning zero from %" HWADDR_PRIx ":%u\n", - __func__, offset, size); - return 0; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset %" HWADDR_PRIx, - __func__, offset); - return 0; - } -} - -static void intdbg_control_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - switch (offset >> 2) { - case 1: /* ALPHA */ - case 2: /* LEDS */ - case 3: /* SWITCHES */ - /* Nothing interesting implemented yet. */ - qemu_log_mask(LOG_UNIMP, - "%s: ignoring write of %" PRIu64 - " to %" HWADDR_PRIx ":%u\n", - __func__, value, offset, size); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: write of %" PRIu64 - " to bad offset %" HWADDR_PRIx "\n", - __func__, value, offset); - } -} - -static const MemoryRegionOps intdbg_control_ops = { - .read = intdbg_control_read, - .write = intdbg_control_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void intdbg_control_init(Object *obj) -{ - SysBusDevice *sd = SYS_BUS_DEVICE(obj); - IntegratorDebugState *s = INTEGRATOR_DEBUG(obj); - - memory_region_init_io(&s->iomem, obj, &intdbg_control_ops, - NULL, "dbg-leds", 0x1000000); - sysbus_init_mmio(sd, &s->iomem); -} - -static const TypeInfo intdbg_info = { - .name = TYPE_INTEGRATOR_DEBUG, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IntegratorDebugState), - .instance_init = intdbg_control_init, -}; - -static void intdbg_register_types(void) -{ - type_register_static(&intdbg_info); -} - -type_init(intdbg_register_types) diff --git a/qemu/hw/misc/arm_l2x0.c b/qemu/hw/misc/arm_l2x0.c deleted file mode 100644 index 7e179f1a4..000000000 --- a/qemu/hw/misc/arm_l2x0.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - * ARM dummy L210, L220, PL310 cache controller. - * - * Copyright (c) 2010-2012 Calxeda - * - * 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 any later version, as published by the Free Software - * Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" - -/* L2C-310 r3p2 */ -#define CACHE_ID 0x410000c8 - -#define TYPE_ARM_L2X0 "l2x0" -#define ARM_L2X0(obj) OBJECT_CHECK(L2x0State, (obj), TYPE_ARM_L2X0) - -typedef struct L2x0State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t cache_type; - uint32_t ctrl; - uint32_t aux_ctrl; - uint32_t data_ctrl; - uint32_t tag_ctrl; - uint32_t filter_start; - uint32_t filter_end; -} L2x0State; - -static const VMStateDescription vmstate_l2x0 = { - .name = "l2x0", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(ctrl, L2x0State), - VMSTATE_UINT32(aux_ctrl, L2x0State), - VMSTATE_UINT32(data_ctrl, L2x0State), - VMSTATE_UINT32(tag_ctrl, L2x0State), - VMSTATE_UINT32(filter_start, L2x0State), - VMSTATE_UINT32(filter_end, L2x0State), - VMSTATE_END_OF_LIST() - } -}; - - -static uint64_t l2x0_priv_read(void *opaque, hwaddr offset, - unsigned size) -{ - uint32_t cache_data; - L2x0State *s = (L2x0State *)opaque; - offset &= 0xfff; - if (offset >= 0x730 && offset < 0x800) { - return 0; /* cache ops complete */ - } - switch (offset) { - case 0: - return CACHE_ID; - case 0x4: - /* aux_ctrl values affect cache_type values */ - cache_data = (s->aux_ctrl & (7 << 17)) >> 15; - cache_data |= (s->aux_ctrl & (1 << 16)) >> 16; - return s->cache_type |= (cache_data << 18) | (cache_data << 6); - case 0x100: - return s->ctrl; - case 0x104: - return s->aux_ctrl; - case 0x108: - return s->tag_ctrl; - case 0x10C: - return s->data_ctrl; - case 0xC00: - return s->filter_start; - case 0xC04: - return s->filter_end; - case 0xF40: - return 0; - case 0xF60: - return 0; - case 0xF80: - return 0; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "l2x0_priv_read: Bad offset %x\n", (int)offset); - break; - } - return 0; -} - -static void l2x0_priv_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - L2x0State *s = (L2x0State *)opaque; - offset &= 0xfff; - if (offset >= 0x730 && offset < 0x800) { - /* ignore */ - return; - } - switch (offset) { - case 0x100: - s->ctrl = value & 1; - break; - case 0x104: - s->aux_ctrl = value; - break; - case 0x108: - s->tag_ctrl = value; - break; - case 0x10C: - s->data_ctrl = value; - break; - case 0xC00: - s->filter_start = value; - break; - case 0xC04: - s->filter_end = value; - break; - case 0xF40: - return; - case 0xF60: - return; - case 0xF80: - return; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "l2x0_priv_write: Bad offset %x\n", (int)offset); - break; - } -} - -static void l2x0_priv_reset(DeviceState *dev) -{ - L2x0State *s = ARM_L2X0(dev); - - s->ctrl = 0; - s->aux_ctrl = 0x02020000; - s->tag_ctrl = 0; - s->data_ctrl = 0; - s->filter_start = 0; - s->filter_end = 0; -} - -static const MemoryRegionOps l2x0_mem_ops = { - .read = l2x0_priv_read, - .write = l2x0_priv_write, - .endianness = DEVICE_NATIVE_ENDIAN, - }; - -static int l2x0_priv_init(SysBusDevice *dev) -{ - L2x0State *s = ARM_L2X0(dev); - - memory_region_init_io(&s->iomem, OBJECT(dev), &l2x0_mem_ops, s, - "l2x0_cc", 0x1000); - sysbus_init_mmio(dev, &s->iomem); - return 0; -} - -static Property l2x0_properties[] = { - DEFINE_PROP_UINT32("cache-type", L2x0State, cache_type, 0x1c100100), - DEFINE_PROP_END_OF_LIST(), -}; - -static void l2x0_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->init = l2x0_priv_init; - dc->vmsd = &vmstate_l2x0; - dc->props = l2x0_properties; - dc->reset = l2x0_priv_reset; -} - -static const TypeInfo l2x0_info = { - .name = TYPE_ARM_L2X0, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(L2x0State), - .class_init = l2x0_class_init, -}; - -static void l2x0_register_types(void) -{ - type_register_static(&l2x0_info); -} - -type_init(l2x0_register_types) diff --git a/qemu/hw/misc/arm_sysctl.c b/qemu/hw/misc/arm_sysctl.c deleted file mode 100644 index 34d90d523..000000000 --- a/qemu/hw/misc/arm_sysctl.c +++ /dev/null @@ -1,658 +0,0 @@ -/* - * Status and system control registers for ARM RealView/Versatile boards. - * - * Copyright (c) 2006-2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "qemu/timer.h" -#include "qemu/bitops.h" -#include "hw/sysbus.h" -#include "hw/arm/primecell.h" -#include "sysemu/sysemu.h" - -#define LOCK_VALUE 0xa05f - -#define TYPE_ARM_SYSCTL "realview_sysctl" -#define ARM_SYSCTL(obj) \ - OBJECT_CHECK(arm_sysctl_state, (obj), TYPE_ARM_SYSCTL) - -typedef struct { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq pl110_mux_ctrl; - - uint32_t sys_id; - uint32_t leds; - uint16_t lockval; - uint32_t cfgdata1; - uint32_t cfgdata2; - uint32_t flags; - uint32_t nvflags; - uint32_t resetlevel; - uint32_t proc_id; - uint32_t sys_mci; - uint32_t sys_cfgdata; - uint32_t sys_cfgctrl; - uint32_t sys_cfgstat; - uint32_t sys_clcd; - uint32_t mb_clock[6]; - uint32_t *db_clock; - uint32_t db_num_vsensors; - uint32_t *db_voltage; - uint32_t db_num_clocks; - uint32_t *db_clock_reset; -} arm_sysctl_state; - -static const VMStateDescription vmstate_arm_sysctl = { - .name = "realview_sysctl", - .version_id = 4, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(leds, arm_sysctl_state), - VMSTATE_UINT16(lockval, arm_sysctl_state), - VMSTATE_UINT32(cfgdata1, arm_sysctl_state), - VMSTATE_UINT32(cfgdata2, arm_sysctl_state), - VMSTATE_UINT32(flags, arm_sysctl_state), - VMSTATE_UINT32(nvflags, arm_sysctl_state), - VMSTATE_UINT32(resetlevel, arm_sysctl_state), - VMSTATE_UINT32_V(sys_mci, arm_sysctl_state, 2), - VMSTATE_UINT32_V(sys_cfgdata, arm_sysctl_state, 2), - VMSTATE_UINT32_V(sys_cfgctrl, arm_sysctl_state, 2), - VMSTATE_UINT32_V(sys_cfgstat, arm_sysctl_state, 2), - VMSTATE_UINT32_V(sys_clcd, arm_sysctl_state, 3), - VMSTATE_UINT32_ARRAY_V(mb_clock, arm_sysctl_state, 6, 4), - VMSTATE_VARRAY_UINT32(db_clock, arm_sysctl_state, db_num_clocks, - 4, vmstate_info_uint32, uint32_t), - VMSTATE_END_OF_LIST() - } -}; - -/* The PB926 actually uses a different format for - * its SYS_ID register. Fortunately the bits which are - * board type on later boards are distinct. - */ -#define BOARD_ID_PB926 0x100 -#define BOARD_ID_EB 0x140 -#define BOARD_ID_PBA8 0x178 -#define BOARD_ID_PBX 0x182 -#define BOARD_ID_VEXPRESS 0x190 - -static int board_id(arm_sysctl_state *s) -{ - /* Extract the board ID field from the SYS_ID register value */ - return (s->sys_id >> 16) & 0xfff; -} - -static void arm_sysctl_reset(DeviceState *d) -{ - arm_sysctl_state *s = ARM_SYSCTL(d); - int i; - - s->leds = 0; - s->lockval = 0; - s->cfgdata1 = 0; - s->cfgdata2 = 0; - s->flags = 0; - s->resetlevel = 0; - /* Motherboard oscillators (in Hz) */ - s->mb_clock[0] = 50000000; /* Static memory clock: 50MHz */ - s->mb_clock[1] = 23750000; /* motherboard CLCD clock: 23.75MHz */ - s->mb_clock[2] = 24000000; /* IO FPGA peripheral clock: 24MHz */ - s->mb_clock[3] = 24000000; /* IO FPGA reserved clock: 24MHz */ - s->mb_clock[4] = 24000000; /* System bus global clock: 24MHz */ - s->mb_clock[5] = 24000000; /* IO FPGA reserved clock: 24MHz */ - /* Daughterboard oscillators: reset from property values */ - for (i = 0; i < s->db_num_clocks; i++) { - s->db_clock[i] = s->db_clock_reset[i]; - } - if (board_id(s) == BOARD_ID_VEXPRESS) { - /* On VExpress this register will RAZ/WI */ - s->sys_clcd = 0; - } else { - /* All others: CLCDID 0x1f, indicating VGA */ - s->sys_clcd = 0x1f00; - } -} - -static uint64_t arm_sysctl_read(void *opaque, hwaddr offset, - unsigned size) -{ - arm_sysctl_state *s = (arm_sysctl_state *)opaque; - - switch (offset) { - case 0x00: /* ID */ - return s->sys_id; - case 0x04: /* SW */ - /* General purpose hardware switches. - We don't have a useful way of exposing these to the user. */ - return 0; - case 0x08: /* LED */ - return s->leds; - case 0x20: /* LOCK */ - return s->lockval; - case 0x0c: /* OSC0 */ - case 0x10: /* OSC1 */ - case 0x14: /* OSC2 */ - case 0x18: /* OSC3 */ - case 0x1c: /* OSC4 */ - case 0x24: /* 100HZ */ - /* ??? Implement these. */ - return 0; - case 0x28: /* CFGDATA1 */ - return s->cfgdata1; - case 0x2c: /* CFGDATA2 */ - return s->cfgdata2; - case 0x30: /* FLAGS */ - return s->flags; - case 0x38: /* NVFLAGS */ - return s->nvflags; - case 0x40: /* RESETCTL */ - if (board_id(s) == BOARD_ID_VEXPRESS) { - /* reserved: RAZ/WI */ - return 0; - } - return s->resetlevel; - case 0x44: /* PCICTL */ - return 1; - case 0x48: /* MCI */ - return s->sys_mci; - case 0x4c: /* FLASH */ - return 0; - case 0x50: /* CLCD */ - return s->sys_clcd; - case 0x54: /* CLCDSER */ - return 0; - case 0x58: /* BOOTCS */ - return 0; - case 0x5c: /* 24MHz */ - return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24000000, - NANOSECONDS_PER_SECOND); - case 0x60: /* MISC */ - return 0; - case 0x84: /* PROCID0 */ - return s->proc_id; - case 0x88: /* PROCID1 */ - return 0xff000000; - case 0x64: /* DMAPSR0 */ - case 0x68: /* DMAPSR1 */ - case 0x6c: /* DMAPSR2 */ - case 0x70: /* IOSEL */ - case 0x74: /* PLDCTL */ - case 0x80: /* BUSID */ - case 0x8c: /* OSCRESET0 */ - case 0x90: /* OSCRESET1 */ - case 0x94: /* OSCRESET2 */ - case 0x98: /* OSCRESET3 */ - case 0x9c: /* OSCRESET4 */ - case 0xc0: /* SYS_TEST_OSC0 */ - case 0xc4: /* SYS_TEST_OSC1 */ - case 0xc8: /* SYS_TEST_OSC2 */ - case 0xcc: /* SYS_TEST_OSC3 */ - case 0xd0: /* SYS_TEST_OSC4 */ - return 0; - case 0xa0: /* SYS_CFGDATA */ - if (board_id(s) != BOARD_ID_VEXPRESS) { - goto bad_reg; - } - return s->sys_cfgdata; - case 0xa4: /* SYS_CFGCTRL */ - if (board_id(s) != BOARD_ID_VEXPRESS) { - goto bad_reg; - } - return s->sys_cfgctrl; - case 0xa8: /* SYS_CFGSTAT */ - if (board_id(s) != BOARD_ID_VEXPRESS) { - goto bad_reg; - } - return s->sys_cfgstat; - default: - bad_reg: - qemu_log_mask(LOG_GUEST_ERROR, - "arm_sysctl_read: Bad register offset 0x%x\n", - (int)offset); - return 0; - } -} - -/* SYS_CFGCTRL functions */ -#define SYS_CFG_OSC 1 -#define SYS_CFG_VOLT 2 -#define SYS_CFG_AMP 3 -#define SYS_CFG_TEMP 4 -#define SYS_CFG_RESET 5 -#define SYS_CFG_SCC 6 -#define SYS_CFG_MUXFPGA 7 -#define SYS_CFG_SHUTDOWN 8 -#define SYS_CFG_REBOOT 9 -#define SYS_CFG_DVIMODE 11 -#define SYS_CFG_POWER 12 -#define SYS_CFG_ENERGY 13 - -/* SYS_CFGCTRL site field values */ -#define SYS_CFG_SITE_MB 0 -#define SYS_CFG_SITE_DB1 1 -#define SYS_CFG_SITE_DB2 2 - -/** - * vexpress_cfgctrl_read: - * @s: arm_sysctl_state pointer - * @dcc, @function, @site, @position, @device: split out values from - * SYS_CFGCTRL register - * @val: pointer to where to put the read data on success - * - * Handle a VExpress SYS_CFGCTRL register read. On success, return true and - * write the read value to *val. On failure, return false (and val may - * or may not be written to). - */ -static bool vexpress_cfgctrl_read(arm_sysctl_state *s, unsigned int dcc, - unsigned int function, unsigned int site, - unsigned int position, unsigned int device, - uint32_t *val) -{ - /* We don't support anything other than DCC 0, board stack position 0 - * or sites other than motherboard/daughterboard: - */ - if (dcc != 0 || position != 0 || - (site != SYS_CFG_SITE_MB && site != SYS_CFG_SITE_DB1)) { - goto cfgctrl_unimp; - } - - switch (function) { - case SYS_CFG_VOLT: - if (site == SYS_CFG_SITE_DB1 && device < s->db_num_vsensors) { - *val = s->db_voltage[device]; - return true; - } - if (site == SYS_CFG_SITE_MB && device == 0) { - /* There is only one motherboard voltage sensor: - * VIO : 3.3V : bus voltage between mother and daughterboard - */ - *val = 3300000; - return true; - } - break; - case SYS_CFG_OSC: - if (site == SYS_CFG_SITE_MB && device < ARRAY_SIZE(s->mb_clock)) { - /* motherboard clock */ - *val = s->mb_clock[device]; - return true; - } - if (site == SYS_CFG_SITE_DB1 && device < s->db_num_clocks) { - /* daughterboard clock */ - *val = s->db_clock[device]; - return true; - } - break; - default: - break; - } - -cfgctrl_unimp: - qemu_log_mask(LOG_UNIMP, - "arm_sysctl: Unimplemented SYS_CFGCTRL read of function " - "0x%x DCC 0x%x site 0x%x position 0x%x device 0x%x\n", - function, dcc, site, position, device); - return false; -} - -/** - * vexpress_cfgctrl_write: - * @s: arm_sysctl_state pointer - * @dcc, @function, @site, @position, @device: split out values from - * SYS_CFGCTRL register - * @val: data to write - * - * Handle a VExpress SYS_CFGCTRL register write. On success, return true. - * On failure, return false. - */ -static bool vexpress_cfgctrl_write(arm_sysctl_state *s, unsigned int dcc, - unsigned int function, unsigned int site, - unsigned int position, unsigned int device, - uint32_t val) -{ - /* We don't support anything other than DCC 0, board stack position 0 - * or sites other than motherboard/daughterboard: - */ - if (dcc != 0 || position != 0 || - (site != SYS_CFG_SITE_MB && site != SYS_CFG_SITE_DB1)) { - goto cfgctrl_unimp; - } - - switch (function) { - case SYS_CFG_OSC: - if (site == SYS_CFG_SITE_MB && device < ARRAY_SIZE(s->mb_clock)) { - /* motherboard clock */ - s->mb_clock[device] = val; - return true; - } - if (site == SYS_CFG_SITE_DB1 && device < s->db_num_clocks) { - /* daughterboard clock */ - s->db_clock[device] = val; - return true; - } - break; - case SYS_CFG_MUXFPGA: - if (site == SYS_CFG_SITE_MB && device == 0) { - /* Select whether video output comes from motherboard - * or daughterboard: log and ignore as QEMU doesn't - * support this. - */ - qemu_log_mask(LOG_UNIMP, "arm_sysctl: selection of video output " - "not supported, ignoring\n"); - return true; - } - break; - case SYS_CFG_SHUTDOWN: - if (site == SYS_CFG_SITE_MB && device == 0) { - qemu_system_shutdown_request(); - return true; - } - break; - case SYS_CFG_REBOOT: - if (site == SYS_CFG_SITE_MB && device == 0) { - qemu_system_reset_request(); - return true; - } - break; - case SYS_CFG_DVIMODE: - if (site == SYS_CFG_SITE_MB && device == 0) { - /* Selecting DVI mode is meaningless for QEMU: we will - * always display the output correctly according to the - * pixel height/width programmed into the CLCD controller. - */ - return true; - } - default: - break; - } - -cfgctrl_unimp: - qemu_log_mask(LOG_UNIMP, - "arm_sysctl: Unimplemented SYS_CFGCTRL write of function " - "0x%x DCC 0x%x site 0x%x position 0x%x device 0x%x\n", - function, dcc, site, position, device); - return false; -} - -static void arm_sysctl_write(void *opaque, hwaddr offset, - uint64_t val, unsigned size) -{ - arm_sysctl_state *s = (arm_sysctl_state *)opaque; - - switch (offset) { - case 0x08: /* LED */ - s->leds = val; - break; - case 0x0c: /* OSC0 */ - case 0x10: /* OSC1 */ - case 0x14: /* OSC2 */ - case 0x18: /* OSC3 */ - case 0x1c: /* OSC4 */ - /* ??? */ - break; - case 0x20: /* LOCK */ - if (val == LOCK_VALUE) - s->lockval = val; - else - s->lockval = val & 0x7fff; - break; - case 0x28: /* CFGDATA1 */ - /* ??? Need to implement this. */ - s->cfgdata1 = val; - break; - case 0x2c: /* CFGDATA2 */ - /* ??? Need to implement this. */ - s->cfgdata2 = val; - break; - case 0x30: /* FLAGSSET */ - s->flags |= val; - break; - case 0x34: /* FLAGSCLR */ - s->flags &= ~val; - break; - case 0x38: /* NVFLAGSSET */ - s->nvflags |= val; - break; - case 0x3c: /* NVFLAGSCLR */ - s->nvflags &= ~val; - break; - case 0x40: /* RESETCTL */ - switch (board_id(s)) { - case BOARD_ID_PB926: - if (s->lockval == LOCK_VALUE) { - s->resetlevel = val; - if (val & 0x100) { - qemu_system_reset_request(); - } - } - break; - case BOARD_ID_PBX: - case BOARD_ID_PBA8: - if (s->lockval == LOCK_VALUE) { - s->resetlevel = val; - if (val & 0x04) { - qemu_system_reset_request(); - } - } - break; - case BOARD_ID_VEXPRESS: - case BOARD_ID_EB: - default: - /* reserved: RAZ/WI */ - break; - } - break; - case 0x44: /* PCICTL */ - /* nothing to do. */ - break; - case 0x4c: /* FLASH */ - break; - case 0x50: /* CLCD */ - switch (board_id(s)) { - case BOARD_ID_PB926: - /* On 926 bits 13:8 are R/O, bits 1:0 control - * the mux that defines how to interpret the PL110 - * graphics format, and other bits are r/w but we - * don't implement them to do anything. - */ - s->sys_clcd &= 0x3f00; - s->sys_clcd |= val & ~0x3f00; - qemu_set_irq(s->pl110_mux_ctrl, val & 3); - break; - case BOARD_ID_EB: - /* The EB is the same except that there is no mux since - * the EB has a PL111. - */ - s->sys_clcd &= 0x3f00; - s->sys_clcd |= val & ~0x3f00; - break; - case BOARD_ID_PBA8: - case BOARD_ID_PBX: - /* On PBA8 and PBX bit 7 is r/w and all other bits - * are either r/o or RAZ/WI. - */ - s->sys_clcd &= (1 << 7); - s->sys_clcd |= val & ~(1 << 7); - break; - case BOARD_ID_VEXPRESS: - default: - /* On VExpress this register is unimplemented and will RAZ/WI */ - break; - } - break; - case 0x54: /* CLCDSER */ - case 0x64: /* DMAPSR0 */ - case 0x68: /* DMAPSR1 */ - case 0x6c: /* DMAPSR2 */ - case 0x70: /* IOSEL */ - case 0x74: /* PLDCTL */ - case 0x80: /* BUSID */ - case 0x84: /* PROCID0 */ - case 0x88: /* PROCID1 */ - case 0x8c: /* OSCRESET0 */ - case 0x90: /* OSCRESET1 */ - case 0x94: /* OSCRESET2 */ - case 0x98: /* OSCRESET3 */ - case 0x9c: /* OSCRESET4 */ - break; - case 0xa0: /* SYS_CFGDATA */ - if (board_id(s) != BOARD_ID_VEXPRESS) { - goto bad_reg; - } - s->sys_cfgdata = val; - return; - case 0xa4: /* SYS_CFGCTRL */ - if (board_id(s) != BOARD_ID_VEXPRESS) { - goto bad_reg; - } - /* Undefined bits [19:18] are RAZ/WI, and writing to - * the start bit just triggers the action; it always reads - * as zero. - */ - s->sys_cfgctrl = val & ~((3 << 18) | (1 << 31)); - if (val & (1 << 31)) { - /* Start bit set -- actually do something */ - unsigned int dcc = extract32(s->sys_cfgctrl, 26, 4); - unsigned int function = extract32(s->sys_cfgctrl, 20, 6); - unsigned int site = extract32(s->sys_cfgctrl, 16, 2); - unsigned int position = extract32(s->sys_cfgctrl, 12, 4); - unsigned int device = extract32(s->sys_cfgctrl, 0, 12); - s->sys_cfgstat = 1; /* complete */ - if (s->sys_cfgctrl & (1 << 30)) { - if (!vexpress_cfgctrl_write(s, dcc, function, site, position, - device, s->sys_cfgdata)) { - s->sys_cfgstat |= 2; /* error */ - } - } else { - uint32_t val; - if (!vexpress_cfgctrl_read(s, dcc, function, site, position, - device, &val)) { - s->sys_cfgstat |= 2; /* error */ - } else { - s->sys_cfgdata = val; - } - } - } - s->sys_cfgctrl &= ~(1 << 31); - return; - case 0xa8: /* SYS_CFGSTAT */ - if (board_id(s) != BOARD_ID_VEXPRESS) { - goto bad_reg; - } - s->sys_cfgstat = val & 3; - return; - default: - bad_reg: - qemu_log_mask(LOG_GUEST_ERROR, - "arm_sysctl_write: Bad register offset 0x%x\n", - (int)offset); - return; - } -} - -static const MemoryRegionOps arm_sysctl_ops = { - .read = arm_sysctl_read, - .write = arm_sysctl_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void arm_sysctl_gpio_set(void *opaque, int line, int level) -{ - arm_sysctl_state *s = (arm_sysctl_state *)opaque; - switch (line) { - case ARM_SYSCTL_GPIO_MMC_WPROT: - { - /* For PB926 and EB write-protect is bit 2 of SYS_MCI; - * for all later boards it is bit 1. - */ - int bit = 2; - if ((board_id(s) == BOARD_ID_PB926) || (board_id(s) == BOARD_ID_EB)) { - bit = 4; - } - s->sys_mci &= ~bit; - if (level) { - s->sys_mci |= bit; - } - break; - } - case ARM_SYSCTL_GPIO_MMC_CARDIN: - s->sys_mci &= ~1; - if (level) { - s->sys_mci |= 1; - } - break; - } -} - -static void arm_sysctl_init(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - SysBusDevice *sd = SYS_BUS_DEVICE(obj); - arm_sysctl_state *s = ARM_SYSCTL(obj); - - memory_region_init_io(&s->iomem, OBJECT(dev), &arm_sysctl_ops, s, - "arm-sysctl", 0x1000); - sysbus_init_mmio(sd, &s->iomem); - qdev_init_gpio_in(dev, arm_sysctl_gpio_set, 2); - qdev_init_gpio_out(dev, &s->pl110_mux_ctrl, 1); -} - -static void arm_sysctl_realize(DeviceState *d, Error **errp) -{ - arm_sysctl_state *s = ARM_SYSCTL(d); - - s->db_clock = g_new0(uint32_t, s->db_num_clocks); -} - -static void arm_sysctl_finalize(Object *obj) -{ - arm_sysctl_state *s = ARM_SYSCTL(obj); - - g_free(s->db_voltage); - g_free(s->db_clock); - g_free(s->db_clock_reset); -} - -static Property arm_sysctl_properties[] = { - DEFINE_PROP_UINT32("sys_id", arm_sysctl_state, sys_id, 0), - DEFINE_PROP_UINT32("proc_id", arm_sysctl_state, proc_id, 0), - /* Daughterboard power supply voltages (as reported via SYS_CFG) */ - DEFINE_PROP_ARRAY("db-voltage", arm_sysctl_state, db_num_vsensors, - db_voltage, qdev_prop_uint32, uint32_t), - /* Daughterboard clock reset values (as reported via SYS_CFG) */ - DEFINE_PROP_ARRAY("db-clock", arm_sysctl_state, db_num_clocks, - db_clock_reset, qdev_prop_uint32, uint32_t), - DEFINE_PROP_END_OF_LIST(), -}; - -static void arm_sysctl_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = arm_sysctl_realize; - dc->reset = arm_sysctl_reset; - dc->vmsd = &vmstate_arm_sysctl; - dc->props = arm_sysctl_properties; -} - -static const TypeInfo arm_sysctl_info = { - .name = TYPE_ARM_SYSCTL, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(arm_sysctl_state), - .instance_init = arm_sysctl_init, - .instance_finalize = arm_sysctl_finalize, - .class_init = arm_sysctl_class_init, -}; - -static void arm_sysctl_register_types(void) -{ - type_register_static(&arm_sysctl_info); -} - -type_init(arm_sysctl_register_types) diff --git a/qemu/hw/misc/bcm2835_mbox.c b/qemu/hw/misc/bcm2835_mbox.c deleted file mode 100644 index 263280fd4..000000000 --- a/qemu/hw/misc/bcm2835_mbox.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - * 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 deleted file mode 100644 index 530411f84..000000000 --- a/qemu/hw/misc/bcm2835_property.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * 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 = α - 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 deleted file mode 100644 index 0c207e310..000000000 --- a/qemu/hw/misc/cbus.c +++ /dev/null @@ -1,619 +0,0 @@ -/* - * CBUS three-pin bus and the Retu / Betty / Tahvo / Vilma / Avilma / - * Hinku / Vinku / Ahne / Pihi chips used in various Nokia platforms. - * Based on reverse-engineering of a linux driver. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/irq.h" -#include "hw/devices.h" -#include "sysemu/sysemu.h" - -//#define DEBUG - -typedef struct { - void *opaque; - void (*io)(void *opaque, int rw, int reg, uint16_t *val); - int addr; -} CBusSlave; - -typedef struct { - CBus cbus; - - int sel; - int dat; - int clk; - int bit; - int dir; - uint16_t val; - qemu_irq dat_out; - - int addr; - int reg; - int rw; - enum { - cbus_address, - cbus_value, - } cycle; - - CBusSlave *slave[8]; -} CBusPriv; - -static void cbus_io(CBusPriv *s) -{ - if (s->slave[s->addr]) - s->slave[s->addr]->io(s->slave[s->addr]->opaque, - s->rw, s->reg, &s->val); - else - hw_error("%s: bad slave address %i\n", __FUNCTION__, s->addr); -} - -static void cbus_cycle(CBusPriv *s) -{ - switch (s->cycle) { - case cbus_address: - s->addr = (s->val >> 6) & 7; - s->rw = (s->val >> 5) & 1; - s->reg = (s->val >> 0) & 0x1f; - - s->cycle = cbus_value; - s->bit = 15; - s->dir = !s->rw; - s->val = 0; - - if (s->rw) - cbus_io(s); - break; - - case cbus_value: - if (!s->rw) - cbus_io(s); - - s->cycle = cbus_address; - s->bit = 8; - s->dir = 1; - s->val = 0; - break; - } -} - -static void cbus_clk(void *opaque, int line, int level) -{ - CBusPriv *s = (CBusPriv *) opaque; - - if (!s->sel && level && !s->clk) { - if (s->dir) - s->val |= s->dat << (s->bit --); - else - qemu_set_irq(s->dat_out, (s->val >> (s->bit --)) & 1); - - if (s->bit < 0) - cbus_cycle(s); - } - - s->clk = level; -} - -static void cbus_dat(void *opaque, int line, int level) -{ - CBusPriv *s = (CBusPriv *) opaque; - - s->dat = level; -} - -static void cbus_sel(void *opaque, int line, int level) -{ - CBusPriv *s = (CBusPriv *) opaque; - - if (!level) { - s->dir = 1; - s->bit = 8; - s->val = 0; - } - - s->sel = level; -} - -CBus *cbus_init(qemu_irq dat) -{ - CBusPriv *s = (CBusPriv *) g_malloc0(sizeof(*s)); - - s->dat_out = dat; - s->cbus.clk = qemu_allocate_irq(cbus_clk, s, 0); - s->cbus.dat = qemu_allocate_irq(cbus_dat, s, 0); - s->cbus.sel = qemu_allocate_irq(cbus_sel, s, 0); - - s->sel = 1; - s->clk = 0; - s->dat = 0; - - return &s->cbus; -} - -void cbus_attach(CBus *bus, void *slave_opaque) -{ - CBusSlave *slave = (CBusSlave *) slave_opaque; - CBusPriv *s = (CBusPriv *) bus; - - s->slave[slave->addr] = slave; -} - -/* Retu/Vilma */ -typedef struct { - uint16_t irqst; - uint16_t irqen; - uint16_t cc[2]; - int channel; - uint16_t result[16]; - uint16_t sample; - uint16_t status; - - struct { - uint16_t cal; - } rtc; - - int is_vilma; - qemu_irq irq; - CBusSlave cbus; -} CBusRetu; - -static void retu_interrupt_update(CBusRetu *s) -{ - qemu_set_irq(s->irq, s->irqst & ~s->irqen); -} - -#define RETU_REG_ASICR 0x00 /* (RO) ASIC ID & revision */ -#define RETU_REG_IDR 0x01 /* (T) Interrupt ID */ -#define RETU_REG_IMR 0x02 /* (RW) Interrupt mask */ -#define RETU_REG_RTCDSR 0x03 /* (RW) RTC seconds register */ -#define RETU_REG_RTCHMR 0x04 /* (RO) RTC hours and minutes reg */ -#define RETU_REG_RTCHMAR 0x05 /* (RW) RTC hours and minutes set reg */ -#define RETU_REG_RTCCALR 0x06 /* (RW) RTC calibration register */ -#define RETU_REG_ADCR 0x08 /* (RW) ADC result register */ -#define RETU_REG_ADCSCR 0x09 /* (RW) ADC sample control register */ -#define RETU_REG_AFCR 0x0a /* (RW) AFC register */ -#define RETU_REG_ANTIFR 0x0b /* (RW) AntiF register */ -#define RETU_REG_CALIBR 0x0c /* (RW) CalibR register*/ -#define RETU_REG_CCR1 0x0d /* (RW) Common control register 1 */ -#define RETU_REG_CCR2 0x0e /* (RW) Common control register 2 */ -#define RETU_REG_RCTRL_CLR 0x0f /* (T) Regulator clear register */ -#define RETU_REG_RCTRL_SET 0x10 /* (T) Regulator set register */ -#define RETU_REG_TXCR 0x11 /* (RW) TxC register */ -#define RETU_REG_STATUS 0x16 /* (RO) Status register */ -#define RETU_REG_WATCHDOG 0x17 /* (RW) Watchdog register */ -#define RETU_REG_AUDTXR 0x18 /* (RW) Audio Codec Tx register */ -#define RETU_REG_AUDPAR 0x19 /* (RW) AudioPA register */ -#define RETU_REG_AUDRXR1 0x1a /* (RW) Audio receive register 1 */ -#define RETU_REG_AUDRXR2 0x1b /* (RW) Audio receive register 2 */ -#define RETU_REG_SGR1 0x1c /* (RW) */ -#define RETU_REG_SCR1 0x1d /* (RW) */ -#define RETU_REG_SGR2 0x1e /* (RW) */ -#define RETU_REG_SCR2 0x1f /* (RW) */ - -/* Retu Interrupt sources */ -enum { - retu_int_pwr = 0, /* Power button */ - retu_int_char = 1, /* Charger */ - retu_int_rtcs = 2, /* Seconds */ - retu_int_rtcm = 3, /* Minutes */ - retu_int_rtcd = 4, /* Days */ - retu_int_rtca = 5, /* Alarm */ - retu_int_hook = 6, /* Hook */ - retu_int_head = 7, /* Headset */ - retu_int_adcs = 8, /* ADC sample */ -}; - -/* Retu ADC channel wiring */ -enum { - retu_adc_bsi = 1, /* BSI */ - retu_adc_batt_temp = 2, /* Battery temperature */ - retu_adc_chg_volt = 3, /* Charger voltage */ - retu_adc_head_det = 4, /* Headset detection */ - retu_adc_hook_det = 5, /* Hook detection */ - retu_adc_rf_gp = 6, /* RF GP */ - retu_adc_tx_det = 7, /* Wideband Tx detection */ - retu_adc_batt_volt = 8, /* Battery voltage */ - retu_adc_sens = 10, /* Light sensor */ - retu_adc_sens_temp = 11, /* Light sensor temperature */ - retu_adc_bbatt_volt = 12, /* Backup battery voltage */ - retu_adc_self_temp = 13, /* RETU temperature */ -}; - -static inline uint16_t retu_read(CBusRetu *s, int reg) -{ -#ifdef DEBUG - printf("RETU read at %02x\n", reg); -#endif - - switch (reg) { - case RETU_REG_ASICR: - return 0x0215 | (s->is_vilma << 7); - - case RETU_REG_IDR: /* TODO: Or is this ffs(s->irqst)? */ - return s->irqst; - - case RETU_REG_IMR: - return s->irqen; - - case RETU_REG_RTCDSR: - case RETU_REG_RTCHMR: - case RETU_REG_RTCHMAR: - /* TODO */ - return 0x0000; - - case RETU_REG_RTCCALR: - return s->rtc.cal; - - case RETU_REG_ADCR: - return (s->channel << 10) | s->result[s->channel]; - case RETU_REG_ADCSCR: - return s->sample; - - case RETU_REG_AFCR: - case RETU_REG_ANTIFR: - case RETU_REG_CALIBR: - /* TODO */ - return 0x0000; - - case RETU_REG_CCR1: - return s->cc[0]; - case RETU_REG_CCR2: - return s->cc[1]; - - case RETU_REG_RCTRL_CLR: - case RETU_REG_RCTRL_SET: - case RETU_REG_TXCR: - /* TODO */ - return 0x0000; - - case RETU_REG_STATUS: - return s->status; - - case RETU_REG_WATCHDOG: - case RETU_REG_AUDTXR: - case RETU_REG_AUDPAR: - case RETU_REG_AUDRXR1: - case RETU_REG_AUDRXR2: - case RETU_REG_SGR1: - case RETU_REG_SCR1: - case RETU_REG_SGR2: - case RETU_REG_SCR2: - /* TODO */ - return 0x0000; - - default: - hw_error("%s: bad register %02x\n", __FUNCTION__, reg); - } -} - -static inline void retu_write(CBusRetu *s, int reg, uint16_t val) -{ -#ifdef DEBUG - printf("RETU write of %04x at %02x\n", val, reg); -#endif - - switch (reg) { - case RETU_REG_IDR: - s->irqst ^= val; - retu_interrupt_update(s); - break; - - case RETU_REG_IMR: - s->irqen = val; - retu_interrupt_update(s); - break; - - case RETU_REG_RTCDSR: - case RETU_REG_RTCHMAR: - /* TODO */ - break; - - case RETU_REG_RTCCALR: - s->rtc.cal = val; - break; - - case RETU_REG_ADCR: - s->channel = (val >> 10) & 0xf; - s->irqst |= 1 << retu_int_adcs; - retu_interrupt_update(s); - break; - case RETU_REG_ADCSCR: - s->sample &= ~val; - break; - - case RETU_REG_AFCR: - case RETU_REG_ANTIFR: - case RETU_REG_CALIBR: - - case RETU_REG_CCR1: - s->cc[0] = val; - break; - case RETU_REG_CCR2: - s->cc[1] = val; - break; - - case RETU_REG_RCTRL_CLR: - case RETU_REG_RCTRL_SET: - /* TODO */ - break; - - case RETU_REG_WATCHDOG: - if (val == 0 && (s->cc[0] & 2)) - qemu_system_shutdown_request(); - break; - - case RETU_REG_TXCR: - case RETU_REG_AUDTXR: - case RETU_REG_AUDPAR: - case RETU_REG_AUDRXR1: - case RETU_REG_AUDRXR2: - case RETU_REG_SGR1: - case RETU_REG_SCR1: - case RETU_REG_SGR2: - case RETU_REG_SCR2: - /* TODO */ - break; - - default: - hw_error("%s: bad register %02x\n", __FUNCTION__, reg); - } -} - -static void retu_io(void *opaque, int rw, int reg, uint16_t *val) -{ - CBusRetu *s = (CBusRetu *) opaque; - - if (rw) - *val = retu_read(s, reg); - else - retu_write(s, reg, *val); -} - -void *retu_init(qemu_irq irq, int vilma) -{ - CBusRetu *s = (CBusRetu *) g_malloc0(sizeof(*s)); - - s->irq = irq; - s->irqen = 0xffff; - s->irqst = 0x0000; - s->status = 0x0020; - s->is_vilma = !!vilma; - s->rtc.cal = 0x01; - s->result[retu_adc_bsi] = 0x3c2; - s->result[retu_adc_batt_temp] = 0x0fc; - s->result[retu_adc_chg_volt] = 0x165; - s->result[retu_adc_head_det] = 123; - s->result[retu_adc_hook_det] = 1023; - s->result[retu_adc_rf_gp] = 0x11; - s->result[retu_adc_tx_det] = 0x11; - s->result[retu_adc_batt_volt] = 0x250; - s->result[retu_adc_sens] = 2; - s->result[retu_adc_sens_temp] = 0x11; - s->result[retu_adc_bbatt_volt] = 0x3d0; - s->result[retu_adc_self_temp] = 0x330; - - s->cbus.opaque = s; - s->cbus.io = retu_io; - s->cbus.addr = 1; - - return &s->cbus; -} - -void retu_key_event(void *retu, int state) -{ - CBusSlave *slave = (CBusSlave *) retu; - CBusRetu *s = (CBusRetu *) slave->opaque; - - s->irqst |= 1 << retu_int_pwr; - retu_interrupt_update(s); - - if (state) - s->status &= ~(1 << 5); - else - s->status |= 1 << 5; -} - -#if 0 -static void retu_head_event(void *retu, int state) -{ - CBusSlave *slave = (CBusSlave *) retu; - CBusRetu *s = (CBusRetu *) slave->opaque; - - if ((s->cc[0] & 0x500) == 0x500) { /* TODO: Which bits? */ - /* TODO: reissue the interrupt every 100ms or so. */ - s->irqst |= 1 << retu_int_head; - retu_interrupt_update(s); - } - - if (state) - s->result[retu_adc_head_det] = 50; - else - s->result[retu_adc_head_det] = 123; -} - -static void retu_hook_event(void *retu, int state) -{ - CBusSlave *slave = (CBusSlave *) retu; - CBusRetu *s = (CBusRetu *) slave->opaque; - - if ((s->cc[0] & 0x500) == 0x500) { - /* TODO: reissue the interrupt every 100ms or so. */ - s->irqst |= 1 << retu_int_hook; - retu_interrupt_update(s); - } - - if (state) - s->result[retu_adc_hook_det] = 50; - else - s->result[retu_adc_hook_det] = 123; -} -#endif - -/* Tahvo/Betty */ -typedef struct { - uint16_t irqst; - uint16_t irqen; - uint8_t charger; - uint8_t backlight; - uint16_t usbr; - uint16_t power; - - int is_betty; - qemu_irq irq; - CBusSlave cbus; -} CBusTahvo; - -static void tahvo_interrupt_update(CBusTahvo *s) -{ - qemu_set_irq(s->irq, s->irqst & ~s->irqen); -} - -#define TAHVO_REG_ASICR 0x00 /* (RO) ASIC ID & revision */ -#define TAHVO_REG_IDR 0x01 /* (T) Interrupt ID */ -#define TAHVO_REG_IDSR 0x02 /* (RO) Interrupt status */ -#define TAHVO_REG_IMR 0x03 /* (RW) Interrupt mask */ -#define TAHVO_REG_CHAPWMR 0x04 /* (RW) Charger PWM */ -#define TAHVO_REG_LEDPWMR 0x05 /* (RW) LED PWM */ -#define TAHVO_REG_USBR 0x06 /* (RW) USB control */ -#define TAHVO_REG_RCR 0x07 /* (RW) Some kind of power management */ -#define TAHVO_REG_CCR1 0x08 /* (RW) Common control register 1 */ -#define TAHVO_REG_CCR2 0x09 /* (RW) Common control register 2 */ -#define TAHVO_REG_TESTR1 0x0a /* (RW) Test register 1 */ -#define TAHVO_REG_TESTR2 0x0b /* (RW) Test register 2 */ -#define TAHVO_REG_NOPR 0x0c /* (RW) Number of periods */ -#define TAHVO_REG_FRR 0x0d /* (RO) FR */ - -static inline uint16_t tahvo_read(CBusTahvo *s, int reg) -{ -#ifdef DEBUG - printf("TAHVO read at %02x\n", reg); -#endif - - switch (reg) { - case TAHVO_REG_ASICR: - return 0x0021 | (s->is_betty ? 0x0b00 : 0x0300); /* 22 in N810 */ - - case TAHVO_REG_IDR: - case TAHVO_REG_IDSR: /* XXX: what does this do? */ - return s->irqst; - - case TAHVO_REG_IMR: - return s->irqen; - - case TAHVO_REG_CHAPWMR: - return s->charger; - - case TAHVO_REG_LEDPWMR: - return s->backlight; - - case TAHVO_REG_USBR: - return s->usbr; - - case TAHVO_REG_RCR: - return s->power; - - case TAHVO_REG_CCR1: - case TAHVO_REG_CCR2: - case TAHVO_REG_TESTR1: - case TAHVO_REG_TESTR2: - case TAHVO_REG_NOPR: - case TAHVO_REG_FRR: - return 0x0000; - - default: - hw_error("%s: bad register %02x\n", __FUNCTION__, reg); - } -} - -static inline void tahvo_write(CBusTahvo *s, int reg, uint16_t val) -{ -#ifdef DEBUG - printf("TAHVO write of %04x at %02x\n", val, reg); -#endif - - switch (reg) { - case TAHVO_REG_IDR: - s->irqst ^= val; - tahvo_interrupt_update(s); - break; - - case TAHVO_REG_IMR: - s->irqen = val; - tahvo_interrupt_update(s); - break; - - case TAHVO_REG_CHAPWMR: - s->charger = val; - break; - - case TAHVO_REG_LEDPWMR: - if (s->backlight != (val & 0x7f)) { - s->backlight = val & 0x7f; - printf("%s: LCD backlight now at %i / 127\n", - __FUNCTION__, s->backlight); - } - break; - - case TAHVO_REG_USBR: - s->usbr = val; - break; - - case TAHVO_REG_RCR: - s->power = val; - break; - - case TAHVO_REG_CCR1: - case TAHVO_REG_CCR2: - case TAHVO_REG_TESTR1: - case TAHVO_REG_TESTR2: - case TAHVO_REG_NOPR: - case TAHVO_REG_FRR: - break; - - default: - hw_error("%s: bad register %02x\n", __FUNCTION__, reg); - } -} - -static void tahvo_io(void *opaque, int rw, int reg, uint16_t *val) -{ - CBusTahvo *s = (CBusTahvo *) opaque; - - if (rw) - *val = tahvo_read(s, reg); - else - tahvo_write(s, reg, *val); -} - -void *tahvo_init(qemu_irq irq, int betty) -{ - CBusTahvo *s = (CBusTahvo *) g_malloc0(sizeof(*s)); - - s->irq = irq; - s->irqen = 0xffff; - s->irqst = 0x0000; - s->is_betty = !!betty; - - s->cbus.opaque = s; - s->cbus.io = tahvo_io; - s->cbus.addr = 2; - - return &s->cbus; -} diff --git a/qemu/hw/misc/debugexit.c b/qemu/hw/misc/debugexit.c deleted file mode 100644 index 84fa1a5b9..000000000 --- a/qemu/hw/misc/debugexit.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * debug exit port emulation - * - * 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) any later version. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/isa/isa.h" - -#define TYPE_ISA_DEBUG_EXIT_DEVICE "isa-debug-exit" -#define ISA_DEBUG_EXIT_DEVICE(obj) \ - OBJECT_CHECK(ISADebugExitState, (obj), TYPE_ISA_DEBUG_EXIT_DEVICE) - -typedef struct ISADebugExitState { - ISADevice parent_obj; - - uint32_t iobase; - uint32_t iosize; - MemoryRegion io; -} ISADebugExitState; - -static void debug_exit_write(void *opaque, hwaddr addr, uint64_t val, - unsigned width) -{ - exit((val << 1) | 1); -} - -static const MemoryRegionOps debug_exit_ops = { - .write = debug_exit_write, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void debug_exit_realizefn(DeviceState *d, Error **errp) -{ - ISADevice *dev = ISA_DEVICE(d); - ISADebugExitState *isa = ISA_DEBUG_EXIT_DEVICE(d); - - memory_region_init_io(&isa->io, OBJECT(dev), &debug_exit_ops, isa, - TYPE_ISA_DEBUG_EXIT_DEVICE, isa->iosize); - memory_region_add_subregion(isa_address_space_io(dev), - isa->iobase, &isa->io); -} - -static Property debug_exit_properties[] = { - DEFINE_PROP_UINT32("iobase", ISADebugExitState, iobase, 0x501), - DEFINE_PROP_UINT32("iosize", ISADebugExitState, iosize, 0x02), - DEFINE_PROP_END_OF_LIST(), -}; - -static void debug_exit_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = debug_exit_realizefn; - dc->props = debug_exit_properties; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo debug_exit_info = { - .name = TYPE_ISA_DEBUG_EXIT_DEVICE, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(ISADebugExitState), - .class_init = debug_exit_class_initfn, -}; - -static void debug_exit_register_types(void) -{ - type_register_static(&debug_exit_info); -} - -type_init(debug_exit_register_types) diff --git a/qemu/hw/misc/eccmemctl.c b/qemu/hw/misc/eccmemctl.c deleted file mode 100644 index a0071f3ea..000000000 --- a/qemu/hw/misc/eccmemctl.c +++ /dev/null @@ -1,345 +0,0 @@ -/* - * QEMU Sparc Sun4m ECC memory controller emulation - * - * Copyright (c) 2007 Robert Reif - * - * 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/sysbus.h" -#include "trace.h" - -/* There are 3 versions of this chip used in SMP sun4m systems: - * MCC (version 0, implementation 0) SS-600MP - * EMC (version 0, implementation 1) SS-10 - * SMC (version 0, implementation 2) SS-10SX and SS-20 - * - * Chipset docs: - * "Sun-4M System Architecture (revision 2.0) by Chuck Narad", 950-1373-01, - * http://mediacast.sun.com/users/Barton808/media/Sun4M_SystemArchitecture_edited2.pdf - */ - -#define ECC_MCC 0x00000000 -#define ECC_EMC 0x10000000 -#define ECC_SMC 0x20000000 - -/* Register indexes */ -#define ECC_MER 0 /* Memory Enable Register */ -#define ECC_MDR 1 /* Memory Delay Register */ -#define ECC_MFSR 2 /* Memory Fault Status Register */ -#define ECC_VCR 3 /* Video Configuration Register */ -#define ECC_MFAR0 4 /* Memory Fault Address Register 0 */ -#define ECC_MFAR1 5 /* Memory Fault Address Register 1 */ -#define ECC_DR 6 /* Diagnostic Register */ -#define ECC_ECR0 7 /* Event Count Register 0 */ -#define ECC_ECR1 8 /* Event Count Register 1 */ - -/* ECC fault control register */ -#define ECC_MER_EE 0x00000001 /* Enable ECC checking */ -#define ECC_MER_EI 0x00000002 /* Enable Interrupts on - correctable errors */ -#define ECC_MER_MRR0 0x00000004 /* SIMM 0 */ -#define ECC_MER_MRR1 0x00000008 /* SIMM 1 */ -#define ECC_MER_MRR2 0x00000010 /* SIMM 2 */ -#define ECC_MER_MRR3 0x00000020 /* SIMM 3 */ -#define ECC_MER_MRR4 0x00000040 /* SIMM 4 */ -#define ECC_MER_MRR5 0x00000080 /* SIMM 5 */ -#define ECC_MER_MRR6 0x00000100 /* SIMM 6 */ -#define ECC_MER_MRR7 0x00000200 /* SIMM 7 */ -#define ECC_MER_REU 0x00000100 /* Memory Refresh Enable (600MP) */ -#define ECC_MER_MRR 0x000003fc /* MRR mask */ -#define ECC_MER_A 0x00000400 /* Memory controller addr map select */ -#define ECC_MER_DCI 0x00000800 /* Disables Coherent Invalidate ACK */ -#define ECC_MER_VER 0x0f000000 /* Version */ -#define ECC_MER_IMPL 0xf0000000 /* Implementation */ -#define ECC_MER_MASK_0 0x00000103 /* Version 0 (MCC) mask */ -#define ECC_MER_MASK_1 0x00000bff /* Version 1 (EMC) mask */ -#define ECC_MER_MASK_2 0x00000bff /* Version 2 (SMC) mask */ - -/* ECC memory delay register */ -#define ECC_MDR_RRI 0x000003ff /* Refresh Request Interval */ -#define ECC_MDR_MI 0x00001c00 /* MIH Delay */ -#define ECC_MDR_CI 0x0000e000 /* Coherent Invalidate Delay */ -#define ECC_MDR_MDL 0x001f0000 /* MBus Master arbitration delay */ -#define ECC_MDR_MDH 0x03e00000 /* MBus Master arbitration delay */ -#define ECC_MDR_GAD 0x7c000000 /* Graphics Arbitration Delay */ -#define ECC_MDR_RSC 0x80000000 /* Refresh load control */ -#define ECC_MDR_MASK 0x7fffffff - -/* ECC fault status register */ -#define ECC_MFSR_CE 0x00000001 /* Correctable error */ -#define ECC_MFSR_BS 0x00000002 /* C2 graphics bad slot access */ -#define ECC_MFSR_TO 0x00000004 /* Timeout on write */ -#define ECC_MFSR_UE 0x00000008 /* Uncorrectable error */ -#define ECC_MFSR_DW 0x000000f0 /* Index of double word in block */ -#define ECC_MFSR_SYND 0x0000ff00 /* Syndrome for correctable error */ -#define ECC_MFSR_ME 0x00010000 /* Multiple errors */ -#define ECC_MFSR_C2ERR 0x00020000 /* C2 graphics error */ - -/* ECC fault address register 0 */ -#define ECC_MFAR0_PADDR 0x0000000f /* PA[32-35] */ -#define ECC_MFAR0_TYPE 0x000000f0 /* Transaction type */ -#define ECC_MFAR0_SIZE 0x00000700 /* Transaction size */ -#define ECC_MFAR0_CACHE 0x00000800 /* Mapped cacheable */ -#define ECC_MFAR0_LOCK 0x00001000 /* Error occurred in atomic cycle */ -#define ECC_MFAR0_BMODE 0x00002000 /* Boot mode */ -#define ECC_MFAR0_VADDR 0x003fc000 /* VA[12-19] (superset bits) */ -#define ECC_MFAR0_S 0x08000000 /* Supervisor mode */ -#define ECC_MFARO_MID 0xf0000000 /* Module ID */ - -/* ECC diagnostic register */ -#define ECC_DR_CBX 0x00000001 -#define ECC_DR_CB0 0x00000002 -#define ECC_DR_CB1 0x00000004 -#define ECC_DR_CB2 0x00000008 -#define ECC_DR_CB4 0x00000010 -#define ECC_DR_CB8 0x00000020 -#define ECC_DR_CB16 0x00000040 -#define ECC_DR_CB32 0x00000080 -#define ECC_DR_DMODE 0x00000c00 - -#define ECC_NREGS 9 -#define ECC_SIZE (ECC_NREGS * sizeof(uint32_t)) - -#define ECC_DIAG_SIZE 4 -#define ECC_DIAG_MASK (ECC_DIAG_SIZE - 1) - -#define TYPE_ECC_MEMCTL "eccmemctl" -#define ECC_MEMCTL(obj) OBJECT_CHECK(ECCState, (obj), TYPE_ECC_MEMCTL) - -typedef struct ECCState { - SysBusDevice parent_obj; - - MemoryRegion iomem, iomem_diag; - qemu_irq irq; - uint32_t regs[ECC_NREGS]; - uint8_t diag[ECC_DIAG_SIZE]; - uint32_t version; -} ECCState; - -static void ecc_mem_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - ECCState *s = opaque; - - switch (addr >> 2) { - case ECC_MER: - if (s->version == ECC_MCC) - s->regs[ECC_MER] = (val & ECC_MER_MASK_0); - else if (s->version == ECC_EMC) - s->regs[ECC_MER] = s->version | (val & ECC_MER_MASK_1); - else if (s->version == ECC_SMC) - s->regs[ECC_MER] = s->version | (val & ECC_MER_MASK_2); - trace_ecc_mem_writel_mer(val); - break; - case ECC_MDR: - s->regs[ECC_MDR] = val & ECC_MDR_MASK; - trace_ecc_mem_writel_mdr(val); - break; - case ECC_MFSR: - s->regs[ECC_MFSR] = val; - qemu_irq_lower(s->irq); - trace_ecc_mem_writel_mfsr(val); - break; - case ECC_VCR: - s->regs[ECC_VCR] = val; - trace_ecc_mem_writel_vcr(val); - break; - case ECC_DR: - s->regs[ECC_DR] = val; - trace_ecc_mem_writel_dr(val); - break; - case ECC_ECR0: - s->regs[ECC_ECR0] = val; - trace_ecc_mem_writel_ecr0(val); - break; - case ECC_ECR1: - s->regs[ECC_ECR0] = val; - trace_ecc_mem_writel_ecr1(val); - break; - } -} - -static uint64_t ecc_mem_read(void *opaque, hwaddr addr, - unsigned size) -{ - ECCState *s = opaque; - uint32_t ret = 0; - - switch (addr >> 2) { - case ECC_MER: - ret = s->regs[ECC_MER]; - trace_ecc_mem_readl_mer(ret); - break; - case ECC_MDR: - ret = s->regs[ECC_MDR]; - trace_ecc_mem_readl_mdr(ret); - break; - case ECC_MFSR: - ret = s->regs[ECC_MFSR]; - trace_ecc_mem_readl_mfsr(ret); - break; - case ECC_VCR: - ret = s->regs[ECC_VCR]; - trace_ecc_mem_readl_vcr(ret); - break; - case ECC_MFAR0: - ret = s->regs[ECC_MFAR0]; - trace_ecc_mem_readl_mfar0(ret); - break; - case ECC_MFAR1: - ret = s->regs[ECC_MFAR1]; - trace_ecc_mem_readl_mfar1(ret); - break; - case ECC_DR: - ret = s->regs[ECC_DR]; - trace_ecc_mem_readl_dr(ret); - break; - case ECC_ECR0: - ret = s->regs[ECC_ECR0]; - trace_ecc_mem_readl_ecr0(ret); - break; - case ECC_ECR1: - ret = s->regs[ECC_ECR0]; - trace_ecc_mem_readl_ecr1(ret); - break; - } - return ret; -} - -static const MemoryRegionOps ecc_mem_ops = { - .read = ecc_mem_read, - .write = ecc_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void ecc_diag_mem_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - ECCState *s = opaque; - - trace_ecc_diag_mem_writeb(addr, val); - s->diag[addr & ECC_DIAG_MASK] = val; -} - -static uint64_t ecc_diag_mem_read(void *opaque, hwaddr addr, - unsigned size) -{ - ECCState *s = opaque; - uint32_t ret = s->diag[(int)addr]; - - trace_ecc_diag_mem_readb(addr, ret); - return ret; -} - -static const MemoryRegionOps ecc_diag_mem_ops = { - .read = ecc_diag_mem_read, - .write = ecc_diag_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static const VMStateDescription vmstate_ecc = { - .name ="ECC", - .version_id = 3, - .minimum_version_id = 3, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, ECCState, ECC_NREGS), - VMSTATE_BUFFER(diag, ECCState), - VMSTATE_UINT32(version, ECCState), - VMSTATE_END_OF_LIST() - } -}; - -static void ecc_reset(DeviceState *d) -{ - ECCState *s = ECC_MEMCTL(d); - - if (s->version == ECC_MCC) { - s->regs[ECC_MER] &= ECC_MER_REU; - } else { - s->regs[ECC_MER] &= (ECC_MER_VER | ECC_MER_IMPL | ECC_MER_MRR | - ECC_MER_DCI); - } - s->regs[ECC_MDR] = 0x20; - s->regs[ECC_MFSR] = 0; - s->regs[ECC_VCR] = 0; - s->regs[ECC_MFAR0] = 0x07c00000; - s->regs[ECC_MFAR1] = 0; - s->regs[ECC_DR] = 0; - s->regs[ECC_ECR0] = 0; - s->regs[ECC_ECR1] = 0; -} - -static int ecc_init1(SysBusDevice *dev) -{ - ECCState *s = ECC_MEMCTL(dev); - - sysbus_init_irq(dev, &s->irq); - s->regs[0] = s->version; - memory_region_init_io(&s->iomem, OBJECT(dev), &ecc_mem_ops, s, "ecc", ECC_SIZE); - sysbus_init_mmio(dev, &s->iomem); - - if (s->version == ECC_MCC) { // SS-600MP only - memory_region_init_io(&s->iomem_diag, OBJECT(dev), &ecc_diag_mem_ops, s, - "ecc.diag", ECC_DIAG_SIZE); - sysbus_init_mmio(dev, &s->iomem_diag); - } - - return 0; -} - -static Property ecc_properties[] = { - DEFINE_PROP_UINT32("version", ECCState, version, -1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ecc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = ecc_init1; - dc->reset = ecc_reset; - dc->vmsd = &vmstate_ecc; - dc->props = ecc_properties; -} - -static const TypeInfo ecc_info = { - .name = TYPE_ECC_MEMCTL, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ECCState), - .class_init = ecc_class_init, -}; - - -static void ecc_register_types(void) -{ - type_register_static(&ecc_info); -} - -type_init(ecc_register_types) diff --git a/qemu/hw/misc/edu.c b/qemu/hw/misc/edu.c deleted file mode 100644 index 888ba49a0..000000000 --- a/qemu/hw/misc/edu.c +++ /dev/null @@ -1,407 +0,0 @@ -/* - * QEMU educational PCI device - * - * Copyright (c) 2012-2015 Jiri Slaby - * - * 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/pci/pci.h" -#include "qemu/timer.h" -#include "qemu/main-loop.h" /* iothread mutex */ -#include "qapi/visitor.h" - -#define EDU(obj) OBJECT_CHECK(EduState, obj, "edu") - -#define FACT_IRQ 0x00000001 -#define DMA_IRQ 0x00000100 - -#define DMA_START 0x40000 -#define DMA_SIZE 4096 - -typedef struct { - PCIDevice pdev; - MemoryRegion mmio; - - QemuThread thread; - QemuMutex thr_mutex; - QemuCond thr_cond; - bool stopping; - - uint32_t addr4; - uint32_t fact; -#define EDU_STATUS_COMPUTING 0x01 -#define EDU_STATUS_IRQFACT 0x80 - uint32_t status; - - uint32_t irq_status; - -#define EDU_DMA_RUN 0x1 -#define EDU_DMA_DIR(cmd) (((cmd) & 0x2) >> 1) -# define EDU_DMA_FROM_PCI 0 -# define EDU_DMA_TO_PCI 1 -#define EDU_DMA_IRQ 0x4 - struct dma_state { - dma_addr_t src; - dma_addr_t dst; - dma_addr_t cnt; - dma_addr_t cmd; - } dma; - QEMUTimer dma_timer; - char dma_buf[DMA_SIZE]; - uint64_t dma_mask; -} EduState; - -static void edu_raise_irq(EduState *edu, uint32_t val) -{ - edu->irq_status |= val; - if (edu->irq_status) { - pci_set_irq(&edu->pdev, 1); - } -} - -static void edu_lower_irq(EduState *edu, uint32_t val) -{ - edu->irq_status &= ~val; - - if (!edu->irq_status) { - pci_set_irq(&edu->pdev, 0); - } -} - -static bool within(uint32_t addr, uint32_t start, uint32_t end) -{ - return start <= addr && addr < end; -} - -static void edu_check_range(uint32_t addr, uint32_t size1, uint32_t start, - uint32_t size2) -{ - uint32_t end1 = addr + size1; - uint32_t end2 = start + size2; - - if (within(addr, start, end2) && - end1 > addr && within(end1, start, end2)) { - return; - } - - hw_error("EDU: DMA range 0x%.8x-0x%.8x out of bounds (0x%.8x-0x%.8x)!", - addr, end1 - 1, start, end2 - 1); -} - -static dma_addr_t edu_clamp_addr(const EduState *edu, dma_addr_t addr) -{ - dma_addr_t res = addr & edu->dma_mask; - - if (addr != res) { - printf("EDU: clamping DMA %#.16"PRIx64" to %#.16"PRIx64"!\n", addr, res); - } - - return res; -} - -static void edu_dma_timer(void *opaque) -{ - EduState *edu = opaque; - bool raise_irq = false; - - if (!(edu->dma.cmd & EDU_DMA_RUN)) { - return; - } - - if (EDU_DMA_DIR(edu->dma.cmd) == EDU_DMA_FROM_PCI) { - uint32_t dst = edu->dma.dst; - edu_check_range(dst, edu->dma.cnt, DMA_START, DMA_SIZE); - dst -= DMA_START; - pci_dma_read(&edu->pdev, edu_clamp_addr(edu, edu->dma.src), - edu->dma_buf + dst, edu->dma.cnt); - } else { - uint32_t src = edu->dma.src; - edu_check_range(src, edu->dma.cnt, DMA_START, DMA_SIZE); - src -= DMA_START; - pci_dma_write(&edu->pdev, edu_clamp_addr(edu, edu->dma.dst), - edu->dma_buf + src, edu->dma.cnt); - } - - edu->dma.cmd &= ~EDU_DMA_RUN; - if (edu->dma.cmd & EDU_DMA_IRQ) { - raise_irq = true; - } - - if (raise_irq) { - edu_raise_irq(edu, DMA_IRQ); - } -} - -static void dma_rw(EduState *edu, bool write, dma_addr_t *val, dma_addr_t *dma, - bool timer) -{ - if (write && (edu->dma.cmd & EDU_DMA_RUN)) { - return; - } - - if (write) { - *dma = *val; - } else { - *val = *dma; - } - - if (timer) { - timer_mod(&edu->dma_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 100); - } -} - -static uint64_t edu_mmio_read(void *opaque, hwaddr addr, unsigned size) -{ - EduState *edu = opaque; - uint64_t val = ~0ULL; - - if (size != 4) { - return val; - } - - switch (addr) { - case 0x00: - val = 0x010000edu; - break; - case 0x04: - val = edu->addr4; - break; - case 0x08: - qemu_mutex_lock(&edu->thr_mutex); - val = edu->fact; - qemu_mutex_unlock(&edu->thr_mutex); - break; - case 0x20: - val = atomic_read(&edu->status); - break; - case 0x24: - val = edu->irq_status; - break; - case 0x80: - dma_rw(edu, false, &val, &edu->dma.src, false); - break; - case 0x88: - dma_rw(edu, false, &val, &edu->dma.dst, false); - break; - case 0x90: - dma_rw(edu, false, &val, &edu->dma.cnt, false); - break; - case 0x98: - dma_rw(edu, false, &val, &edu->dma.cmd, false); - break; - } - - return val; -} - -static void edu_mmio_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - EduState *edu = opaque; - - if (addr < 0x80 && size != 4) { - return; - } - - if (addr >= 0x80 && size != 4 && size != 8) { - return; - } - - switch (addr) { - case 0x04: - edu->addr4 = ~val; - break; - case 0x08: - if (atomic_read(&edu->status) & EDU_STATUS_COMPUTING) { - break; - } - /* EDU_STATUS_COMPUTING cannot go 0->1 concurrently, because it is only - * set in this function and it is under the iothread mutex. - */ - qemu_mutex_lock(&edu->thr_mutex); - edu->fact = val; - atomic_or(&edu->status, EDU_STATUS_COMPUTING); - qemu_cond_signal(&edu->thr_cond); - qemu_mutex_unlock(&edu->thr_mutex); - break; - case 0x20: - if (val & EDU_STATUS_IRQFACT) { - atomic_or(&edu->status, EDU_STATUS_IRQFACT); - } else { - atomic_and(&edu->status, ~EDU_STATUS_IRQFACT); - } - break; - case 0x60: - edu_raise_irq(edu, val); - break; - case 0x64: - edu_lower_irq(edu, val); - break; - case 0x80: - dma_rw(edu, true, &val, &edu->dma.src, false); - break; - case 0x88: - dma_rw(edu, true, &val, &edu->dma.dst, false); - break; - case 0x90: - dma_rw(edu, true, &val, &edu->dma.cnt, false); - break; - case 0x98: - if (!(val & EDU_DMA_RUN)) { - break; - } - dma_rw(edu, true, &val, &edu->dma.cmd, true); - break; - } -} - -static const MemoryRegionOps edu_mmio_ops = { - .read = edu_mmio_read, - .write = edu_mmio_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* - * We purposely use a thread, so that users are forced to wait for the status - * register. - */ -static void *edu_fact_thread(void *opaque) -{ - EduState *edu = opaque; - - while (1) { - uint32_t val, ret = 1; - - qemu_mutex_lock(&edu->thr_mutex); - while ((atomic_read(&edu->status) & EDU_STATUS_COMPUTING) == 0 && - !edu->stopping) { - qemu_cond_wait(&edu->thr_cond, &edu->thr_mutex); - } - - if (edu->stopping) { - qemu_mutex_unlock(&edu->thr_mutex); - break; - } - - val = edu->fact; - qemu_mutex_unlock(&edu->thr_mutex); - - while (val > 0) { - ret *= val--; - } - - /* - * We should sleep for a random period here, so that students are - * forced to check the status properly. - */ - - qemu_mutex_lock(&edu->thr_mutex); - edu->fact = ret; - qemu_mutex_unlock(&edu->thr_mutex); - atomic_and(&edu->status, ~EDU_STATUS_COMPUTING); - - if (atomic_read(&edu->status) & EDU_STATUS_IRQFACT) { - qemu_mutex_lock_iothread(); - edu_raise_irq(edu, FACT_IRQ); - qemu_mutex_unlock_iothread(); - } - } - - return NULL; -} - -static void pci_edu_realize(PCIDevice *pdev, Error **errp) -{ - EduState *edu = DO_UPCAST(EduState, pdev, pdev); - uint8_t *pci_conf = pdev->config; - - timer_init_ms(&edu->dma_timer, QEMU_CLOCK_VIRTUAL, edu_dma_timer, edu); - - qemu_mutex_init(&edu->thr_mutex); - qemu_cond_init(&edu->thr_cond); - qemu_thread_create(&edu->thread, "edu", edu_fact_thread, - edu, QEMU_THREAD_JOINABLE); - - pci_config_set_interrupt_pin(pci_conf, 1); - - 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); -} - -static void pci_edu_uninit(PCIDevice *pdev) -{ - EduState *edu = DO_UPCAST(EduState, pdev, pdev); - - qemu_mutex_lock(&edu->thr_mutex); - edu->stopping = true; - qemu_mutex_unlock(&edu->thr_mutex); - qemu_cond_signal(&edu->thr_cond); - qemu_thread_join(&edu->thread); - - qemu_cond_destroy(&edu->thr_cond); - qemu_mutex_destroy(&edu->thr_mutex); - - timer_del(&edu->dma_timer); -} - -static void edu_obj_uint64(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint64_t *val = opaque; - - visit_type_uint64(v, name, val, errp); -} - -static void edu_instance_init(Object *obj) -{ - EduState *edu = EDU(obj); - - edu->dma_mask = (1UL << 28) - 1; - object_property_add(obj, "dma_mask", "uint64", edu_obj_uint64, - edu_obj_uint64, NULL, &edu->dma_mask, NULL); -} - -static void edu_class_init(ObjectClass *class, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(class); - - k->realize = pci_edu_realize; - k->exit = pci_edu_uninit; - k->vendor_id = PCI_VENDOR_ID_QEMU; - k->device_id = 0x11e8; - k->revision = 0x10; - k->class_id = PCI_CLASS_OTHERS; -} - -static void pci_edu_register_types(void) -{ - static const TypeInfo edu_info = { - .name = "edu", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(EduState), - .instance_init = edu_instance_init, - .class_init = edu_class_init, - }; - - type_register_static(&edu_info); -} -type_init(pci_edu_register_types) diff --git a/qemu/hw/misc/exynos4210_pmu.c b/qemu/hw/misc/exynos4210_pmu.c deleted file mode 100644 index 889abadfe..000000000 --- a/qemu/hw/misc/exynos4210_pmu.c +++ /dev/null @@ -1,503 +0,0 @@ -/* - * Exynos4210 Power Management Unit (PMU) Emulation - * - * Copyright (C) 2011 Samsung Electronics Co Ltd. - * Maksim Kozlov - * - * 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 . - */ - -/* - * This model implements PMU registers just as a bulk of memory. Currently, - * the only reason this device exists is that secondary CPU boot loader - * uses PMU INFORM5 register as a holding pen. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" - -#ifndef DEBUG_PMU -#define DEBUG_PMU 0 -#endif - -#ifndef DEBUG_PMU_EXTEND -#define DEBUG_PMU_EXTEND 0 -#endif - -#if DEBUG_PMU -#define PRINT_DEBUG(fmt, args...) \ - do { \ - fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ - } while (0) - -#if DEBUG_PMU_EXTEND -#define PRINT_DEBUG_EXTEND(fmt, args...) \ - do { \ - fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ - } while (0) -#else -#define PRINT_DEBUG_EXTEND(fmt, args...) do {} while (0) -#endif /* EXTEND */ - -#else -#define PRINT_DEBUG(fmt, args...) do {} while (0) -#define PRINT_DEBUG_EXTEND(fmt, args...) do {} while (0) -#endif - -/* - * Offsets for PMU registers - */ -#define OM_STAT 0x0000 /* OM status register */ -#define RTC_CLKO_SEL 0x000C /* Controls RTCCLKOUT */ -#define GNSS_RTC_OUT_CTRL 0x0010 /* Controls GNSS_RTC_OUT */ -/* Decides whether system-level low-power mode is used. */ -#define SYSTEM_POWER_DOWN_CTRL 0x0200 -/* Sets control options for CENTRAL_SEQ */ -#define SYSTEM_POWER_DOWN_OPTION 0x0208 -#define SWRESET 0x0400 /* Generate software reset */ -#define RST_STAT 0x0404 /* Reset status register */ -#define WAKEUP_STAT 0x0600 /* Wakeup status register */ -#define EINT_WAKEUP_MASK 0x0604 /* Configure External INTerrupt mask */ -#define WAKEUP_MASK 0x0608 /* Configure wakeup source mask */ -#define HDMI_PHY_CONTROL 0x0700 /* HDMI PHY control register */ -#define USBDEVICE_PHY_CONTROL 0x0704 /* USB Device PHY control register */ -#define USBHOST_PHY_CONTROL 0x0708 /* USB HOST PHY control register */ -#define DAC_PHY_CONTROL 0x070C /* DAC control register */ -#define MIPI_PHY0_CONTROL 0x0710 /* MIPI PHY control register */ -#define MIPI_PHY1_CONTROL 0x0714 /* MIPI PHY control register */ -#define ADC_PHY_CONTROL 0x0718 /* TS-ADC control register */ -#define PCIe_PHY_CONTROL 0x071C /* TS-PCIe control register */ -#define SATA_PHY_CONTROL 0x0720 /* TS-SATA control register */ -#define INFORM0 0x0800 /* Information register 0 */ -#define INFORM1 0x0804 /* Information register 1 */ -#define INFORM2 0x0808 /* Information register 2 */ -#define INFORM3 0x080C /* Information register 3 */ -#define INFORM4 0x0810 /* Information register 4 */ -#define INFORM5 0x0814 /* Information register 5 */ -#define INFORM6 0x0818 /* Information register 6 */ -#define INFORM7 0x081C /* Information register 7 */ -#define PMU_DEBUG 0x0A00 /* PMU debug register */ -/* Registers to set system-level low-power option */ -#define ARM_CORE0_SYS_PWR_REG 0x1000 -#define ARM_CORE1_SYS_PWR_REG 0x1010 -#define ARM_COMMON_SYS_PWR_REG 0x1080 -#define ARM_CPU_L2_0_SYS_PWR_REG 0x10C0 -#define ARM_CPU_L2_1_SYS_PWR_REG 0x10C4 -#define CMU_ACLKSTOP_SYS_PWR_REG 0x1100 -#define CMU_SCLKSTOP_SYS_PWR_REG 0x1104 -#define CMU_RESET_SYS_PWR_REG 0x110C -#define APLL_SYSCLK_SYS_PWR_REG 0x1120 -#define MPLL_SYSCLK_SYS_PWR_REG 0x1124 -#define VPLL_SYSCLK_SYS_PWR_REG 0x1128 -#define EPLL_SYSCLK_SYS_PWR_REG 0x112C -#define CMU_CLKSTOP_GPS_ALIVE_SYS_PWR_REG 0x1138 -#define CMU_RESET_GPS_ALIVE_SYS_PWR_REG 0x113C -#define CMU_CLKSTOP_CAM_SYS_PWR_REG 0x1140 -#define CMU_CLKSTOP_TV_SYS_PWR_REG 0x1144 -#define CMU_CLKSTOP_MFC_SYS_PWR_REG 0x1148 -#define CMU_CLKSTOP_G3D_SYS_PWR_REG 0x114C -#define CMU_CLKSTOP_LCD0_SYS_PWR_REG 0x1150 -#define CMU_CLKSTOP_LCD1_SYS_PWR_REG 0x1154 -#define CMU_CLKSTOP_MAUDIO_SYS_PWR_REG 0x1158 -#define CMU_CLKSTOP_GPS_SYS_PWR_REG 0x115C -#define CMU_RESET_CAM_SYS_PWR_REG 0x1160 -#define CMU_RESET_TV_SYS_PWR_REG 0x1164 -#define CMU_RESET_MFC_SYS_PWR_REG 0x1168 -#define CMU_RESET_G3D_SYS_PWR_REG 0x116C -#define CMU_RESET_LCD0_SYS_PWR_REG 0x1170 -#define CMU_RESET_LCD1_SYS_PWR_REG 0x1174 -#define CMU_RESET_MAUDIO_SYS_PWR_REG 0x1178 -#define CMU_RESET_GPS_SYS_PWR_REG 0x117C -#define TOP_BUS_SYS_PWR_REG 0x1180 -#define TOP_RETENTION_SYS_PWR_REG 0x1184 -#define TOP_PWR_SYS_PWR_REG 0x1188 -#define LOGIC_RESET_SYS_PWR_REG 0x11A0 -#define OneNANDXL_MEM_SYS_PWR_REG 0x11C0 -#define MODEMIF_MEM_SYS_PWR_REG 0x11C4 -#define USBDEVICE_MEM_SYS_PWR_REG 0x11CC -#define SDMMC_MEM_SYS_PWR_REG 0x11D0 -#define CSSYS_MEM_SYS_PWR_REG 0x11D4 -#define SECSS_MEM_SYS_PWR_REG 0x11D8 -#define PCIe_MEM_SYS_PWR_REG 0x11E0 -#define SATA_MEM_SYS_PWR_REG 0x11E4 -#define PAD_RETENTION_DRAM_SYS_PWR_REG 0x1200 -#define PAD_RETENTION_MAUDIO_SYS_PWR_REG 0x1204 -#define PAD_RETENTION_GPIO_SYS_PWR_REG 0x1220 -#define PAD_RETENTION_UART_SYS_PWR_REG 0x1224 -#define PAD_RETENTION_MMCA_SYS_PWR_REG 0x1228 -#define PAD_RETENTION_MMCB_SYS_PWR_REG 0x122C -#define PAD_RETENTION_EBIA_SYS_PWR_REG 0x1230 -#define PAD_RETENTION_EBIB_SYS_PWR_REG 0x1234 -#define PAD_ISOLATION_SYS_PWR_REG 0x1240 -#define PAD_ALV_SEL_SYS_PWR_REG 0x1260 -#define XUSBXTI_SYS_PWR_REG 0x1280 -#define XXTI_SYS_PWR_REG 0x1284 -#define EXT_REGULATOR_SYS_PWR_REG 0x12C0 -#define GPIO_MODE_SYS_PWR_REG 0x1300 -#define GPIO_MODE_MAUDIO_SYS_PWR_REG 0x1340 -#define CAM_SYS_PWR_REG 0x1380 -#define TV_SYS_PWR_REG 0x1384 -#define MFC_SYS_PWR_REG 0x1388 -#define G3D_SYS_PWR_REG 0x138C -#define LCD0_SYS_PWR_REG 0x1390 -#define LCD1_SYS_PWR_REG 0x1394 -#define MAUDIO_SYS_PWR_REG 0x1398 -#define GPS_SYS_PWR_REG 0x139C -#define GPS_ALIVE_SYS_PWR_REG 0x13A0 -#define ARM_CORE0_CONFIGURATION 0x2000 /* Configure power mode of ARM_CORE0 */ -#define ARM_CORE0_STATUS 0x2004 /* Check power mode of ARM_CORE0 */ -#define ARM_CORE0_OPTION 0x2008 /* Sets control options for ARM_CORE0 */ -#define ARM_CORE1_CONFIGURATION 0x2080 /* Configure power mode of ARM_CORE1 */ -#define ARM_CORE1_STATUS 0x2084 /* Check power mode of ARM_CORE1 */ -#define ARM_CORE1_OPTION 0x2088 /* Sets control options for ARM_CORE0 */ -#define ARM_COMMON_OPTION 0x2408 /* Sets control options for ARM_COMMON */ -/* Configure power mode of ARM_CPU_L2_0 */ -#define ARM_CPU_L2_0_CONFIGURATION 0x2600 -#define ARM_CPU_L2_0_STATUS 0x2604 /* Check power mode of ARM_CPU_L2_0 */ -/* Configure power mode of ARM_CPU_L2_1 */ -#define ARM_CPU_L2_1_CONFIGURATION 0x2620 -#define ARM_CPU_L2_1_STATUS 0x2624 /* Check power mode of ARM_CPU_L2_1 */ -/* Sets control options for PAD_RETENTION_MAUDIO */ -#define PAD_RETENTION_MAUDIO_OPTION 0x3028 -/* Sets control options for PAD_RETENTION_GPIO */ -#define PAD_RETENTION_GPIO_OPTION 0x3108 -/* Sets control options for PAD_RETENTION_UART */ -#define PAD_RETENTION_UART_OPTION 0x3128 -/* Sets control options for PAD_RETENTION_MMCA */ -#define PAD_RETENTION_MMCA_OPTION 0x3148 -/* Sets control options for PAD_RETENTION_MMCB */ -#define PAD_RETENTION_MMCB_OPTION 0x3168 -/* Sets control options for PAD_RETENTION_EBIA */ -#define PAD_RETENTION_EBIA_OPTION 0x3188 -/* Sets control options for PAD_RETENTION_EBIB */ -#define PAD_RETENTION_EBIB_OPTION 0x31A8 -#define PS_HOLD_CONTROL 0x330C /* PS_HOLD control register */ -#define XUSBXTI_CONFIGURATION 0x3400 /* Configure the pad of XUSBXTI */ -#define XUSBXTI_STATUS 0x3404 /* Check the pad of XUSBXTI */ -/* Sets time required for XUSBXTI to be stabilized */ -#define XUSBXTI_DURATION 0x341C -#define XXTI_CONFIGURATION 0x3420 /* Configure the pad of XXTI */ -#define XXTI_STATUS 0x3424 /* Check the pad of XXTI */ -/* Sets time required for XXTI to be stabilized */ -#define XXTI_DURATION 0x343C -/* Sets time required for EXT_REGULATOR to be stabilized */ -#define EXT_REGULATOR_DURATION 0x361C -#define CAM_CONFIGURATION 0x3C00 /* Configure power mode of CAM */ -#define CAM_STATUS 0x3C04 /* Check power mode of CAM */ -#define CAM_OPTION 0x3C08 /* Sets control options for CAM */ -#define TV_CONFIGURATION 0x3C20 /* Configure power mode of TV */ -#define TV_STATUS 0x3C24 /* Check power mode of TV */ -#define TV_OPTION 0x3C28 /* Sets control options for TV */ -#define MFC_CONFIGURATION 0x3C40 /* Configure power mode of MFC */ -#define MFC_STATUS 0x3C44 /* Check power mode of MFC */ -#define MFC_OPTION 0x3C48 /* Sets control options for MFC */ -#define G3D_CONFIGURATION 0x3C60 /* Configure power mode of G3D */ -#define G3D_STATUS 0x3C64 /* Check power mode of G3D */ -#define G3D_OPTION 0x3C68 /* Sets control options for G3D */ -#define LCD0_CONFIGURATION 0x3C80 /* Configure power mode of LCD0 */ -#define LCD0_STATUS 0x3C84 /* Check power mode of LCD0 */ -#define LCD0_OPTION 0x3C88 /* Sets control options for LCD0 */ -#define LCD1_CONFIGURATION 0x3CA0 /* Configure power mode of LCD1 */ -#define LCD1_STATUS 0x3CA4 /* Check power mode of LCD1 */ -#define LCD1_OPTION 0x3CA8 /* Sets control options for LCD1 */ -#define GPS_CONFIGURATION 0x3CE0 /* Configure power mode of GPS */ -#define GPS_STATUS 0x3CE4 /* Check power mode of GPS */ -#define GPS_OPTION 0x3CE8 /* Sets control options for GPS */ -#define GPS_ALIVE_CONFIGURATION 0x3D00 /* Configure power mode of GPS */ -#define GPS_ALIVE_STATUS 0x3D04 /* Check power mode of GPS */ -#define GPS_ALIVE_OPTION 0x3D08 /* Sets control options for GPS */ - -#define EXYNOS4210_PMU_REGS_MEM_SIZE 0x3d0c - -typedef struct Exynos4210PmuReg { - const char *name; /* for debug only */ - uint32_t offset; - uint32_t reset_value; -} Exynos4210PmuReg; - -static const Exynos4210PmuReg exynos4210_pmu_regs[] = { - {"OM_STAT", OM_STAT, 0x00000000}, - {"RTC_CLKO_SEL", RTC_CLKO_SEL, 0x00000000}, - {"GNSS_RTC_OUT_CTRL", GNSS_RTC_OUT_CTRL, 0x00000001}, - {"SYSTEM_POWER_DOWN_CTRL", SYSTEM_POWER_DOWN_CTRL, 0x00010000}, - {"SYSTEM_POWER_DOWN_OPTION", SYSTEM_POWER_DOWN_OPTION, 0x03030000}, - {"SWRESET", SWRESET, 0x00000000}, - {"RST_STAT", RST_STAT, 0x00000000}, - {"WAKEUP_STAT", WAKEUP_STAT, 0x00000000}, - {"EINT_WAKEUP_MASK", EINT_WAKEUP_MASK, 0x00000000}, - {"WAKEUP_MASK", WAKEUP_MASK, 0x00000000}, - {"HDMI_PHY_CONTROL", HDMI_PHY_CONTROL, 0x00960000}, - {"USBDEVICE_PHY_CONTROL", USBDEVICE_PHY_CONTROL, 0x00000000}, - {"USBHOST_PHY_CONTROL", USBHOST_PHY_CONTROL, 0x00000000}, - {"DAC_PHY_CONTROL", DAC_PHY_CONTROL, 0x00000000}, - {"MIPI_PHY0_CONTROL", MIPI_PHY0_CONTROL, 0x00000000}, - {"MIPI_PHY1_CONTROL", MIPI_PHY1_CONTROL, 0x00000000}, - {"ADC_PHY_CONTROL", ADC_PHY_CONTROL, 0x00000001}, - {"PCIe_PHY_CONTROL", PCIe_PHY_CONTROL, 0x00000000}, - {"SATA_PHY_CONTROL", SATA_PHY_CONTROL, 0x00000000}, - {"INFORM0", INFORM0, 0x00000000}, - {"INFORM1", INFORM1, 0x00000000}, - {"INFORM2", INFORM2, 0x00000000}, - {"INFORM3", INFORM3, 0x00000000}, - {"INFORM4", INFORM4, 0x00000000}, - {"INFORM5", INFORM5, 0x00000000}, - {"INFORM6", INFORM6, 0x00000000}, - {"INFORM7", INFORM7, 0x00000000}, - {"PMU_DEBUG", PMU_DEBUG, 0x00000000}, - {"ARM_CORE0_SYS_PWR_REG", ARM_CORE0_SYS_PWR_REG, 0xFFFFFFFF}, - {"ARM_CORE1_SYS_PWR_REG", ARM_CORE1_SYS_PWR_REG, 0xFFFFFFFF}, - {"ARM_COMMON_SYS_PWR_REG", ARM_COMMON_SYS_PWR_REG, 0xFFFFFFFF}, - {"ARM_CPU_L2_0_SYS_PWR_REG", ARM_CPU_L2_0_SYS_PWR_REG, 0xFFFFFFFF}, - {"ARM_CPU_L2_1_SYS_PWR_REG", ARM_CPU_L2_1_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_ACLKSTOP_SYS_PWR_REG", CMU_ACLKSTOP_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_SCLKSTOP_SYS_PWR_REG", CMU_SCLKSTOP_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_RESET_SYS_PWR_REG", CMU_RESET_SYS_PWR_REG, 0xFFFFFFFF}, - {"APLL_SYSCLK_SYS_PWR_REG", APLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF}, - {"MPLL_SYSCLK_SYS_PWR_REG", MPLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF}, - {"VPLL_SYSCLK_SYS_PWR_REG", VPLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF}, - {"EPLL_SYSCLK_SYS_PWR_REG", EPLL_SYSCLK_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_CLKSTOP_GPS_ALIVE_SYS_PWR_REG", CMU_CLKSTOP_GPS_ALIVE_SYS_PWR_REG, - 0xFFFFFFFF}, - {"CMU_RESET_GPS_ALIVE_SYS_PWR_REG", CMU_RESET_GPS_ALIVE_SYS_PWR_REG, - 0xFFFFFFFF}, - {"CMU_CLKSTOP_CAM_SYS_PWR_REG", CMU_CLKSTOP_CAM_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_CLKSTOP_TV_SYS_PWR_REG", CMU_CLKSTOP_TV_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_CLKSTOP_MFC_SYS_PWR_REG", CMU_CLKSTOP_MFC_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_CLKSTOP_G3D_SYS_PWR_REG", CMU_CLKSTOP_G3D_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_CLKSTOP_LCD0_SYS_PWR_REG", CMU_CLKSTOP_LCD0_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_CLKSTOP_LCD1_SYS_PWR_REG", CMU_CLKSTOP_LCD1_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_CLKSTOP_MAUDIO_SYS_PWR_REG", CMU_CLKSTOP_MAUDIO_SYS_PWR_REG, - 0xFFFFFFFF}, - {"CMU_CLKSTOP_GPS_SYS_PWR_REG", CMU_CLKSTOP_GPS_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_RESET_CAM_SYS_PWR_REG", CMU_RESET_CAM_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_RESET_TV_SYS_PWR_REG", CMU_RESET_TV_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_RESET_MFC_SYS_PWR_REG", CMU_RESET_MFC_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_RESET_G3D_SYS_PWR_REG", CMU_RESET_G3D_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_RESET_LCD0_SYS_PWR_REG", CMU_RESET_LCD0_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_RESET_LCD1_SYS_PWR_REG", CMU_RESET_LCD1_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_RESET_MAUDIO_SYS_PWR_REG", CMU_RESET_MAUDIO_SYS_PWR_REG, 0xFFFFFFFF}, - {"CMU_RESET_GPS_SYS_PWR_REG", CMU_RESET_GPS_SYS_PWR_REG, 0xFFFFFFFF}, - {"TOP_BUS_SYS_PWR_REG", TOP_BUS_SYS_PWR_REG, 0xFFFFFFFF}, - {"TOP_RETENTION_SYS_PWR_REG", TOP_RETENTION_SYS_PWR_REG, 0xFFFFFFFF}, - {"TOP_PWR_SYS_PWR_REG", TOP_PWR_SYS_PWR_REG, 0xFFFFFFFF}, - {"LOGIC_RESET_SYS_PWR_REG", LOGIC_RESET_SYS_PWR_REG, 0xFFFFFFFF}, - {"OneNANDXL_MEM_SYS_PWR_REG", OneNANDXL_MEM_SYS_PWR_REG, 0xFFFFFFFF}, - {"MODEMIF_MEM_SYS_PWR_REG", MODEMIF_MEM_SYS_PWR_REG, 0xFFFFFFFF}, - {"USBDEVICE_MEM_SYS_PWR_REG", USBDEVICE_MEM_SYS_PWR_REG, 0xFFFFFFFF}, - {"SDMMC_MEM_SYS_PWR_REG", SDMMC_MEM_SYS_PWR_REG, 0xFFFFFFFF}, - {"CSSYS_MEM_SYS_PWR_REG", CSSYS_MEM_SYS_PWR_REG, 0xFFFFFFFF}, - {"SECSS_MEM_SYS_PWR_REG", SECSS_MEM_SYS_PWR_REG, 0xFFFFFFFF}, - {"PCIe_MEM_SYS_PWR_REG", PCIe_MEM_SYS_PWR_REG, 0xFFFFFFFF}, - {"SATA_MEM_SYS_PWR_REG", SATA_MEM_SYS_PWR_REG, 0xFFFFFFFF}, - {"PAD_RETENTION_DRAM_SYS_PWR_REG", PAD_RETENTION_DRAM_SYS_PWR_REG, - 0xFFFFFFFF}, - {"PAD_RETENTION_MAUDIO_SYS_PWR_REG", PAD_RETENTION_MAUDIO_SYS_PWR_REG, - 0xFFFFFFFF}, - {"PAD_RETENTION_GPIO_SYS_PWR_REG", PAD_RETENTION_GPIO_SYS_PWR_REG, - 0xFFFFFFFF}, - {"PAD_RETENTION_UART_SYS_PWR_REG", PAD_RETENTION_UART_SYS_PWR_REG, - 0xFFFFFFFF}, - {"PAD_RETENTION_MMCA_SYS_PWR_REG", PAD_RETENTION_MMCA_SYS_PWR_REG, - 0xFFFFFFFF}, - {"PAD_RETENTION_MMCB_SYS_PWR_REG", PAD_RETENTION_MMCB_SYS_PWR_REG, - 0xFFFFFFFF}, - {"PAD_RETENTION_EBIA_SYS_PWR_REG", PAD_RETENTION_EBIA_SYS_PWR_REG, - 0xFFFFFFFF}, - {"PAD_RETENTION_EBIB_SYS_PWR_REG", PAD_RETENTION_EBIB_SYS_PWR_REG, - 0xFFFFFFFF}, - {"PAD_ISOLATION_SYS_PWR_REG", PAD_ISOLATION_SYS_PWR_REG, 0xFFFFFFFF}, - {"PAD_ALV_SEL_SYS_PWR_REG", PAD_ALV_SEL_SYS_PWR_REG, 0xFFFFFFFF}, - {"XUSBXTI_SYS_PWR_REG", XUSBXTI_SYS_PWR_REG, 0xFFFFFFFF}, - {"XXTI_SYS_PWR_REG", XXTI_SYS_PWR_REG, 0xFFFFFFFF}, - {"EXT_REGULATOR_SYS_PWR_REG", EXT_REGULATOR_SYS_PWR_REG, 0xFFFFFFFF}, - {"GPIO_MODE_SYS_PWR_REG", GPIO_MODE_SYS_PWR_REG, 0xFFFFFFFF}, - {"GPIO_MODE_MAUDIO_SYS_PWR_REG", GPIO_MODE_MAUDIO_SYS_PWR_REG, 0xFFFFFFFF}, - {"CAM_SYS_PWR_REG", CAM_SYS_PWR_REG, 0xFFFFFFFF}, - {"TV_SYS_PWR_REG", TV_SYS_PWR_REG, 0xFFFFFFFF}, - {"MFC_SYS_PWR_REG", MFC_SYS_PWR_REG, 0xFFFFFFFF}, - {"G3D_SYS_PWR_REG", G3D_SYS_PWR_REG, 0xFFFFFFFF}, - {"LCD0_SYS_PWR_REG", LCD0_SYS_PWR_REG, 0xFFFFFFFF}, - {"LCD1_SYS_PWR_REG", LCD1_SYS_PWR_REG, 0xFFFFFFFF}, - {"MAUDIO_SYS_PWR_REG", MAUDIO_SYS_PWR_REG, 0xFFFFFFFF}, - {"GPS_SYS_PWR_REG", GPS_SYS_PWR_REG, 0xFFFFFFFF}, - {"GPS_ALIVE_SYS_PWR_REG", GPS_ALIVE_SYS_PWR_REG, 0xFFFFFFFF}, - {"ARM_CORE0_CONFIGURATION", ARM_CORE0_CONFIGURATION, 0x00000003}, - {"ARM_CORE0_STATUS", ARM_CORE0_STATUS, 0x00030003}, - {"ARM_CORE0_OPTION", ARM_CORE0_OPTION, 0x01010001}, - {"ARM_CORE1_CONFIGURATION", ARM_CORE1_CONFIGURATION, 0x00000003}, - {"ARM_CORE1_STATUS", ARM_CORE1_STATUS, 0x00030003}, - {"ARM_CORE1_OPTION", ARM_CORE1_OPTION, 0x01010001}, - {"ARM_COMMON_OPTION", ARM_COMMON_OPTION, 0x00000001}, - {"ARM_CPU_L2_0_CONFIGURATION", ARM_CPU_L2_0_CONFIGURATION, 0x00000003}, - {"ARM_CPU_L2_0_STATUS", ARM_CPU_L2_0_STATUS, 0x00000003}, - {"ARM_CPU_L2_1_CONFIGURATION", ARM_CPU_L2_1_CONFIGURATION, 0x00000003}, - {"ARM_CPU_L2_1_STATUS", ARM_CPU_L2_1_STATUS, 0x00000003}, - {"PAD_RETENTION_MAUDIO_OPTION", PAD_RETENTION_MAUDIO_OPTION, 0x00000000}, - {"PAD_RETENTION_GPIO_OPTION", PAD_RETENTION_GPIO_OPTION, 0x00000000}, - {"PAD_RETENTION_UART_OPTION", PAD_RETENTION_UART_OPTION, 0x00000000}, - {"PAD_RETENTION_MMCA_OPTION", PAD_RETENTION_MMCA_OPTION, 0x00000000}, - {"PAD_RETENTION_MMCB_OPTION", PAD_RETENTION_MMCB_OPTION, 0x00000000}, - {"PAD_RETENTION_EBIA_OPTION", PAD_RETENTION_EBIA_OPTION, 0x00000000}, - {"PAD_RETENTION_EBIB_OPTION", PAD_RETENTION_EBIB_OPTION, 0x00000000}, - {"PS_HOLD_CONTROL", PS_HOLD_CONTROL, 0x00005200}, - {"XUSBXTI_CONFIGURATION", XUSBXTI_CONFIGURATION, 0x00000001}, - {"XUSBXTI_STATUS", XUSBXTI_STATUS, 0x00000001}, - {"XUSBXTI_DURATION", XUSBXTI_DURATION, 0xFFF00000}, - {"XXTI_CONFIGURATION", XXTI_CONFIGURATION, 0x00000001}, - {"XXTI_STATUS", XXTI_STATUS, 0x00000001}, - {"XXTI_DURATION", XXTI_DURATION, 0xFFF00000}, - {"EXT_REGULATOR_DURATION", EXT_REGULATOR_DURATION, 0xFFF03FFF}, - {"CAM_CONFIGURATION", CAM_CONFIGURATION, 0x00000007}, - {"CAM_STATUS", CAM_STATUS, 0x00060007}, - {"CAM_OPTION", CAM_OPTION, 0x00000001}, - {"TV_CONFIGURATION", TV_CONFIGURATION, 0x00000007}, - {"TV_STATUS", TV_STATUS, 0x00060007}, - {"TV_OPTION", TV_OPTION, 0x00000001}, - {"MFC_CONFIGURATION", MFC_CONFIGURATION, 0x00000007}, - {"MFC_STATUS", MFC_STATUS, 0x00060007}, - {"MFC_OPTION", MFC_OPTION, 0x00000001}, - {"G3D_CONFIGURATION", G3D_CONFIGURATION, 0x00000007}, - {"G3D_STATUS", G3D_STATUS, 0x00060007}, - {"G3D_OPTION", G3D_OPTION, 0x00000001}, - {"LCD0_CONFIGURATION", LCD0_CONFIGURATION, 0x00000007}, - {"LCD0_STATUS", LCD0_STATUS, 0x00060007}, - {"LCD0_OPTION", LCD0_OPTION, 0x00000001}, - {"LCD1_CONFIGURATION", LCD1_CONFIGURATION, 0x00000007}, - {"LCD1_STATUS", LCD1_STATUS, 0x00060007}, - {"LCD1_OPTION", LCD1_OPTION, 0x00000001}, - {"GPS_CONFIGURATION", GPS_CONFIGURATION, 0x00000007}, - {"GPS_STATUS", GPS_STATUS, 0x00060007}, - {"GPS_OPTION", GPS_OPTION, 0x00000001}, - {"GPS_ALIVE_CONFIGURATION", GPS_ALIVE_CONFIGURATION, 0x00000007}, - {"GPS_ALIVE_STATUS", GPS_ALIVE_STATUS, 0x00060007}, - {"GPS_ALIVE_OPTION", GPS_ALIVE_OPTION, 0x00000001}, -}; - -#define PMU_NUM_OF_REGISTERS ARRAY_SIZE(exynos4210_pmu_regs) - -#define TYPE_EXYNOS4210_PMU "exynos4210.pmu" -#define EXYNOS4210_PMU(obj) \ - OBJECT_CHECK(Exynos4210PmuState, (obj), TYPE_EXYNOS4210_PMU) - -typedef struct Exynos4210PmuState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t reg[PMU_NUM_OF_REGISTERS]; -} Exynos4210PmuState; - -static uint64_t exynos4210_pmu_read(void *opaque, hwaddr offset, - unsigned size) -{ - Exynos4210PmuState *s = (Exynos4210PmuState *)opaque; - unsigned i; - const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs; - - for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) { - if (reg_p->offset == offset) { - PRINT_DEBUG_EXTEND("%s [0x%04x] -> 0x%04x\n", reg_p->name, - (uint32_t)offset, s->reg[i]); - return s->reg[i]; - } - reg_p++; - } - PRINT_DEBUG("QEMU PMU ERROR: bad read offset 0x%04x\n", (uint32_t)offset); - return 0; -} - -static void exynos4210_pmu_write(void *opaque, hwaddr offset, - uint64_t val, unsigned size) -{ - Exynos4210PmuState *s = (Exynos4210PmuState *)opaque; - unsigned i; - const Exynos4210PmuReg *reg_p = exynos4210_pmu_regs; - - for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) { - if (reg_p->offset == offset) { - PRINT_DEBUG_EXTEND("%s <0x%04x> <- 0x%04x\n", reg_p->name, - (uint32_t)offset, (uint32_t)val); - s->reg[i] = val; - return; - } - reg_p++; - } - PRINT_DEBUG("QEMU PMU ERROR: bad write offset 0x%04x\n", (uint32_t)offset); -} - -static const MemoryRegionOps exynos4210_pmu_ops = { - .read = exynos4210_pmu_read, - .write = exynos4210_pmu_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false - } -}; - -static void exynos4210_pmu_reset(DeviceState *dev) -{ - Exynos4210PmuState *s = EXYNOS4210_PMU(dev); - unsigned i; - - /* Set default values for registers */ - for (i = 0; i < PMU_NUM_OF_REGISTERS; i++) { - s->reg[i] = exynos4210_pmu_regs[i].reset_value; - } -} - -static int exynos4210_pmu_init(SysBusDevice *dev) -{ - Exynos4210PmuState *s = EXYNOS4210_PMU(dev); - - /* memory mapping */ - memory_region_init_io(&s->iomem, OBJECT(dev), &exynos4210_pmu_ops, s, - "exynos4210.pmu", EXYNOS4210_PMU_REGS_MEM_SIZE); - sysbus_init_mmio(dev, &s->iomem); - return 0; -} - -static const VMStateDescription exynos4210_pmu_vmstate = { - .name = "exynos4210.pmu", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(reg, Exynos4210PmuState, PMU_NUM_OF_REGISTERS), - VMSTATE_END_OF_LIST() - } -}; - -static void exynos4210_pmu_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = exynos4210_pmu_init; - dc->reset = exynos4210_pmu_reset; - dc->vmsd = &exynos4210_pmu_vmstate; -} - -static const TypeInfo exynos4210_pmu_info = { - .name = TYPE_EXYNOS4210_PMU, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Exynos4210PmuState), - .class_init = exynos4210_pmu_class_init, -}; - -static void exynos4210_pmu_register(void) -{ - type_register_static(&exynos4210_pmu_info); -} - -type_init(exynos4210_pmu_register) diff --git a/qemu/hw/misc/hyperv_testdev.c b/qemu/hw/misc/hyperv_testdev.c deleted file mode 100644 index 1883fd7f2..000000000 --- a/qemu/hw/misc/hyperv_testdev.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * QEMU KVM Hyper-V test device to support Hyper-V kvm-unit-tests - * - * Copyright (C) 2015 Andrey Smetanin - * - * Authors: - * Andrey Smetanin - * - * 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 deleted file mode 100644 index 225604d82..000000000 --- a/qemu/hw/misc/imx25_ccm.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * IMX25 Clock Control Module - * - * Copyright (C) 2012 NICTA - * Updated by Jean-Christophe Dubois - * - * 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 deleted file mode 100644 index 80c164716..000000000 --- a/qemu/hw/misc/imx31_ccm.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * IMX31 Clock Control Module - * - * Copyright (C) 2012 NICTA - * Updated by Jean-Christophe Dubois - * - * 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 deleted file mode 100644 index 4e1d49da6..000000000 --- a/qemu/hw/misc/imx6_ccm.c +++ /dev/null @@ -1,774 +0,0 @@ -/* - * IMX6 Clock Control Module - * - * Copyright (c) 2015 Jean-Christophe Dubois - * - * 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 deleted file mode 100644 index 986d890ca..000000000 --- a/qemu/hw/misc/imx_ccm.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * IMX31 Clock Control Module - * - * Copyright (C) 2012 NICTA - * Updated by Jean-Christophe Dubois - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - * This is an abstract base class used to get a common interface to - * retrieve the CCM frequencies from the various i.MX SOC. - */ - -#include "qemu/osdep.h" -#include "hw/misc/imx_ccm.h" - -#ifndef DEBUG_IMX_CCM -#define DEBUG_IMX_CCM 0 -#endif - -#define DPRINTF(fmt, args...) \ - do { \ - if (DEBUG_IMX_CCM) { \ - fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_CCM, \ - __func__, ##args); \ - } \ - } while (0) - - -uint32_t imx_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) -{ - uint32_t freq = 0; - IMXCCMClass *klass = IMX_GET_CLASS(dev); - - if (klass->get_clock_frequency) { - freq = klass->get_clock_frequency(dev, clock); - } - - DPRINTF("(clock = %d) = %d\n", clock, freq); - - return freq; -} - -/* - * Calculate PLL output frequency - */ -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 */ - uint32_t pd = 1 + PD(pllreg); /* Pre-divider */ - - if (mfi < 5) { - mfi = 5; - } - - /* mfn is 10-bit signed twos-complement */ - mfn <<= 32 - 10; - mfn >>= 32 - 10; - - freq = ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) / - (mfd * pd)) << 10; - - DPRINTF("(pllreg = 0x%08x, base_freq = %d) = %d\n", pllreg, base_freq, - freq); - - return freq; -} - -static const TypeInfo imx_ccm_info = { - .name = TYPE_IMX_CCM, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IMXCCMState), - .class_size = sizeof(IMXCCMClass), - .abstract = true, -}; - -static void imx_ccm_register_types(void) -{ - type_register_static(&imx_ccm_info); -} - -type_init(imx_ccm_register_types) diff --git a/qemu/hw/misc/ivshmem.c b/qemu/hw/misc/ivshmem.c deleted file mode 100644 index e40f23bfc..000000000 --- a/qemu/hw/misc/ivshmem.c +++ /dev/null @@ -1,1323 +0,0 @@ -/* - * Inter-VM Shared Memory PCI device. - * - * Author: - * Cam Macdonell - * - * Based On: cirrus_vga.c - * Copyright (c) 2004 Fabrice Bellard - * Copyright (c) 2004 Makoto Suzuki (suzu) - * - * and rtl8139.c - * Copyright (c) 2006 Igor Kovalenko - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ -#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 "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 - -#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_REG_BAR_SIZE 0x100 - -#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) \ - OBJECT_CHECK(IVShmemState, (obj), TYPE_IVSHMEM) - -typedef struct Peer { - int nb_eventfds; - EventNotifier *eventfds; -} Peer; - -typedef struct MSIVector { - PCIDevice *pdev; - 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; - int vm_id; - - /* 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; /* space in @peers[] */ - uint32_t vectors; - 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; - - /* 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 */ -enum ivshmem_registers { - INTRMASK = 0, - INTRSTATUS = 4, - IVPOSITION = 8, - DOORBELL = 12, -}; - -static inline uint32_t ivshmem_has_feature(IVShmemState *ivs, - unsigned int feature) { - return (ivs->features & (1 << feature)); -} - -static inline bool ivshmem_is_master(IVShmemState *s) -{ - assert(s->master != ON_OFF_AUTO_AUTO); - return s->master == ON_OFF_AUTO_ON; -} - -static void ivshmem_update_irq(IVShmemState *s) -{ - PCIDevice *d = PCI_DEVICE(s); - 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) { - IVSHMEM_DPRINTF("Set IRQ to %d (%04x %04x)\n", - isr ? 1 : 0, s->intrstatus, s->intrmask); - } - - pci_set_irq(d, isr != 0); -} - -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); -} - -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; -} - -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); -} - -static uint32_t ivshmem_IntrStatus_read(IVShmemState *s) -{ - uint32_t ret = s->intrstatus; - - /* reading ISR clears all interrupts */ - s->intrstatus = 0; - ivshmem_update_irq(s); - return ret; -} - -static void ivshmem_io_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - IVShmemState *s = opaque; - - uint16_t dest = val >> 16; - uint16_t vector = val & 0xff; - - addr &= 0xfc; - - IVSHMEM_DPRINTF("writing to addr " TARGET_FMT_plx "\n", addr); - switch (addr) - { - case INTRMASK: - ivshmem_IntrMask_write(s, val); - break; - - case INTRSTATUS: - ivshmem_IntrStatus_write(s, val); - break; - - case DOORBELL: - /* check that dest VM ID is reasonable */ - if (dest >= s->nb_peers) { - IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest); - break; - } - - /* check doorbell range */ - 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("Unhandled write " TARGET_FMT_plx "\n", addr); - } -} - -static uint64_t ivshmem_io_read(void *opaque, hwaddr addr, - unsigned size) -{ - - IVShmemState *s = opaque; - uint32_t ret; - - switch (addr) - { - case INTRMASK: - ret = ivshmem_IntrMask_read(s); - break; - - case INTRSTATUS: - ret = ivshmem_IntrStatus_read(s); - break; - - case IVPOSITION: - ret = s->vm_id; - break; - - default: - IVSHMEM_DPRINTF("why are we reading " TARGET_FMT_plx "\n", addr); - ret = 0; - } - - return ret; -} - -static const MemoryRegionOps ivshmem_mmio_ops = { - .read = ivshmem_io_read, - .write = ivshmem_io_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void ivshmem_vector_notify(void *opaque) -{ - 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]; - - if (!event_notifier_test_and_clear(n)) { - return; - } - - 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 int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector, - MSIMessage msg) -{ - IVShmemState *s = IVSHMEM_COMMON(dev); - EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; - MSIVector *v = &s->msi_vectors[vector]; - int ret; - - IVSHMEM_DPRINTF("vector unmask %p %d\n", dev, vector); - - ret = kvm_irqchip_update_msi_route(kvm_state, v->virq, msg, dev); - if (ret < 0) { - return ret; - } - - return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, v->virq); -} - -static void ivshmem_vector_mask(PCIDevice *dev, unsigned vector) -{ - IVShmemState *s = IVSHMEM_COMMON(dev); - EventNotifier *n = &s->peers[s->vm_id].eventfds[vector]; - int ret; - - IVSHMEM_DPRINTF("vector mask %p %d\n", dev, vector); - - 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"); - } -} - -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); - - vector_end = MIN(vector_end, s->vectors); - - for (vector = vector_start; vector < vector_end; vector++) { - EventNotifier *notifier = &s->peers[s->vm_id].eventfds[vector]; - - if (!msix_is_masked(dev, vector)) { - continue; - } - - if (event_notifier_test_and_clear(notifier)) { - msix_set_pending(dev, vector); - } - } -} - -static void watch_vector_notifier(IVShmemState *s, EventNotifier *n, - int vector) -{ - int eventfd = event_notifier_get_fd(n); - - assert(!s->msi_vectors[vector].pdev); - s->msi_vectors[vector].pdev = PCI_DEVICE(s); - - qemu_set_fd_handler(eventfd, ivshmem_vector_notify, - NULL, &s->msi_vectors[vector]); -} - -static void ivshmem_add_eventfd(IVShmemState *s, int posn, int i) -{ - memory_region_add_eventfd(&s->ivshmem_mmio, - DOORBELL, - 4, - true, - (posn << 16) | i, - &s->peers[posn].eventfds[i]); -} - -static void ivshmem_del_eventfd(IVShmemState *s, int posn, int i) -{ - memory_region_del_eventfd(&s->ivshmem_mmio, - DOORBELL, - 4, - true, - (posn << 16) | i, - &s->peers[posn].eventfds[i]); -} - -static void close_peer_eventfds(IVShmemState *s, int posn) -{ - int i, n; - - assert(posn >= 0 && posn < s->nb_peers); - n = s->peers[posn].nb_eventfds; - - 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(); - } - - for (i = 0; i < n; i++) { - event_notifier_cleanup(&s->peers[posn].eventfds[i]); - } - - g_free(s->peers[posn].eventfds); - s->peers[posn].nb_eventfds = 0; -} - -static void resize_peers(IVShmemState *s, int nb_peers) -{ - int old_nb_peers = s->nb_peers; - int i; - - assert(nb_peers > old_nb_peers); - IVSHMEM_DPRINTF("bumping storage to %d peers\n", nb_peers); - - s->peers = g_realloc(s->peers, nb_peers * sizeof(Peer)); - s->nb_peers = nb_peers; - - for (i = old_nb_peers; i < nb_peers; i++) { - s->peers[i].eventfds = g_new0(EventNotifier, s->vectors); - s->peers[i].nb_eventfds = 0; - } -} - -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; - - 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; - } - - s->msi_vectors[vector].virq = ret; - s->msi_vectors[vector].pdev = pdev; -} - -static void setup_interrupt(IVShmemState *s, int vector, Error **errp) -{ - 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; - } - - 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"); - } -} - -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; - } - - size = buf.st_size; - - /* 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; - } - - /* 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; -} - -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); -} - -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 (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { - ivshmem_add_eventfd(s, posn, vector); - } -} - -static void process_msg(IVShmemState *s, int64_t msg, int fd, Error **errp) -{ - IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n", msg, fd); - - if (msg < -1 || msg > IVSHMEM_MAX_PEERS) { - error_setg(errp, "server sent invalid message %" PRId64, msg); - close(fd); - return; - } - - if (msg == -1) { - process_msg_shmem(s, fd, errp); - return; - } - - if (msg >= s->nb_peers) { - resize_peers(s, msg + 1); - } - - if (fd >= 0) { - process_msg_connect(s, msg, fd, errp); - } else { - process_msg_disconnect(s, msg, errp); - } -} - -static int ivshmem_can_receive(void *opaque) -{ - IVShmemState *s = opaque; - - 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; - - fd = qemu_chr_fe_get_msgfd(s->server_chr); - IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n", msg, fd); - - process_msg(s, msg, fd, &err); - if (err) { - error_report_err(err); - } -} - -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)); - - *pfd = qemu_chr_fe_get_msgfd(s->server_chr); - return msg; -} - -static void ivshmem_recv_setup(IVShmemState *s, Error **errp) -{ - Error *err = NULL; - int64_t msg; - int fd; - - 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; - } - - /* - * 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_msix_vector_use(IVShmemState *s) -{ - PCIDevice *d = PCI_DEVICE(s); - int i; - - for (i = 0; i < s->vectors; i++) { - msix_vector_use(d, i); - } -} - -static void ivshmem_reset(DeviceState *d) -{ - IVShmemState *s = IVSHMEM_COMMON(d); - - s->intrstatus = 0; - s->intrmask = 0; - if (ivshmem_has_feature(s, IVSHMEM_MSI)) { - ivshmem_msix_vector_use(s); - } -} - -static int ivshmem_setup_interrupts(IVShmemState *s) -{ - /* allocate QEMU callback data for receiving interrupts */ - s->msi_vectors = g_malloc0(s->vectors * sizeof(MSIVector)); - - if (ivshmem_has_feature(s, IVSHMEM_MSI)) { - if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1)) { - return -1; - } - - IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors); - ivshmem_msix_vector_use(s); - } - - 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? */ - } - } - - 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_remove_kvm_msi_virq(IVShmemState *s, int vector) -{ - IVSHMEM_DPRINTF("ivshmem_remove_kvm_msi_virq vector:%d\n", vector); - - if (s->msi_vectors[vector].pdev == NULL) { - return; - } - - /* it was cleaned when masked in the frontend. */ - kvm_irqchip_release_virq(kvm_state, s->msi_vectors[vector].virq); - - s->msi_vectors[vector].pdev = NULL; -} - -static void ivshmem_disable_irqfd(IVShmemState *s) -{ - PCIDevice *pdev = PCI_DEVICE(s); - int i; - - 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 void ivshmem_write_config(PCIDevice *pdev, uint32_t address, - uint32_t val, int len) -{ - IVShmemState *s = IVSHMEM_COMMON(pdev); - int is_enabled, was_enabled = msix_enabled(pdev); - - pci_default_write_config(pdev, address, val, len); - is_enabled = msix_enabled(pdev); - - 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); - } - } -} - -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; - } - - 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 (s->hostmem != NULL) { - IVSHMEM_DPRINTF("using hostmem\n"); - - s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem, - &error_abort); - } else { - 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; - } - } - - vmstate_register_ram(s->ivshmem_bar2, DEVICE(s)); - pci_register_bar(PCI_DEVICE(s), 2, attr, s->ivshmem_bar2); - - 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 void ivshmem_exit(PCIDevice *dev) -{ - IVShmemState *s = IVSHMEM_COMMON(dev); - int i; - - if (s->migration_blocker) { - migrate_del_blocker(s->migration_blocker); - error_free(s->migration_blocker); - } - - if (memory_region_is_mapped(s->ivshmem_bar2)) { - if (!s->hostmem) { - void *addr = memory_region_get_ram_ptr(s->ivshmem_bar2); - int fd; - - if (munmap(addr, memory_region_size(s->ivshmem_bar2) == -1)) { - error_report("Failed to munmap shared memory %s", - strerror(errno)); - } - - fd = qemu_get_ram_fd(memory_region_get_ram_addr(s->ivshmem_bar2)); - close(fd); - } - - vmstate_unregister_ram(s->ivshmem_bar2, DEVICE(dev)); - } - - if (s->peers) { - for (i = 0; i < s->nb_peers; i++) { - close_peer_eventfds(s, i); - } - g_free(s->peers); - } - - if (ivshmem_has_feature(s, IVSHMEM_MSI)) { - msix_uninit_exclusive_bar(dev); - } - - g_free(s->msi_vectors); -} - -static int ivshmem_pre_load(void *opaque) -{ - IVShmemState *s = opaque; - - if (!ivshmem_is_master(s)) { - error_report("'peer' devices are not migratable"); - return -EINVAL; - } - - return 0; -} - -static int ivshmem_post_load(void *opaque, int version_id) -{ - IVShmemState *s = opaque; - - if (ivshmem_has_feature(s, IVSHMEM_MSI)) { - ivshmem_msix_vector_use(s); - } - return 0; -} - -static void ivshmem_common_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - 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"; -} - -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, -}; - -static void ivshmem_check_memdev_is_busy(Object *obj, const char *name, - Object *val, Error **errp) -{ - MemoryRegion *mr; - - 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() - }, -}; - -static Property ivshmem_plain_properties[] = { - DEFINE_PROP_ON_OFF_AUTO("master", IVShmemState, master, ON_OFF_AUTO_OFF), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ivshmem_plain_init(Object *obj) -{ - IVShmemState *s = IVSHMEM_PLAIN(obj); - - 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); -} - -static void ivshmem_plain_realize(PCIDevice *dev, Error **errp) -{ - IVShmemState *s = IVSHMEM_COMMON(dev); - - if (!s->hostmem) { - error_setg(errp, "You must specify a 'memdev'"); - return; - } - - ivshmem_common_realize(dev, errp); -} - -static void ivshmem_plain_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - 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, -}; - -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() - }, -}; - -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; - } - - ivshmem_common_realize(dev, errp); -} - -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 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 = opaque; - PCIDevice *pdev = PCI_DEVICE(s); - int ret; - - 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); - } - - 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("msi", IVShmemState, features, IVSHMEM_MSI, true), - DEFINE_PROP_STRING("shm", IVShmemState, shmobj), - DEFINE_PROP_STRING("role", IVShmemState, role), - 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->realize = ivshmem_realize; - k->revision = 0; - dc->desc = "Inter-VM shared memory (legacy)"; - dc->props = ivshmem_properties; - dc->vmsd = &ivshmem_vmsd; -} - -static const TypeInfo ivshmem_info = { - .name = TYPE_IVSHMEM, - .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); -} - -type_init(ivshmem_register_types) diff --git a/qemu/hw/misc/macio/Makefile.objs b/qemu/hw/misc/macio/Makefile.objs deleted file mode 100644 index ef7ac249e..000000000 --- a/qemu/hw/misc/macio/Makefile.objs +++ /dev/null @@ -1,3 +0,0 @@ -common-obj-y += macio.o -common-obj-$(CONFIG_CUDA) += cuda.o -common-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o diff --git a/qemu/hw/misc/macio/cuda.c b/qemu/hw/misc/macio/cuda.c deleted file mode 100644 index f15f30110..000000000 --- a/qemu/hw/misc/macio/cuda.c +++ /dev/null @@ -1,964 +0,0 @@ -/* - * QEMU PowerMac CUDA device support - * - * Copyright (c) 2004-2007 Fabrice Bellard - * Copyright (c) 2007 Jocelyn Mayer - * - * 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/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 */ - -/* debug CUDA */ -//#define DEBUG_CUDA - -/* debug CUDA packets */ -//#define DEBUG_CUDA_PACKET - -#ifdef DEBUG_CUDA -#define CUDA_DPRINTF(fmt, ...) \ - do { printf("CUDA: " fmt , ## __VA_ARGS__); } while (0) -#else -#define CUDA_DPRINTF(fmt, ...) -#endif - -/* Bits in B data register: all active low */ -#define TREQ 0x08 /* Transfer request (input) */ -#define TACK 0x10 /* Transfer acknowledge (output) */ -#define TIP 0x20 /* Transfer in progress (output) */ - -/* Bits in ACR */ -#define SR_CTRL 0x1c /* Shift register control bits */ -#define SR_EXT 0x0c /* Shift on external clock */ -#define SR_OUT 0x10 /* Shift out if 1 */ - -/* Bits in IFR and IER */ -#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 */ - -/* Bits in ACR */ -#define T1MODE 0xc0 /* Timer 1 mode */ -#define T1MODE_CONT 0x40 /* continuous interrupts */ - -/* commands (1st byte) */ -#define ADB_PACKET 0 -#define CUDA_PACKET 1 -#define ERROR_PACKET 2 -#define TIMER_PACKET 3 -#define POWER_PACKET 4 -#define MACIIC_PACKET 5 -#define PMU_PACKET 6 - - -/* CUDA commands (2nd byte) */ -#define CUDA_WARM_START 0x0 -#define CUDA_AUTOPOLL 0x1 -#define CUDA_GET_6805_ADDR 0x2 -#define CUDA_GET_TIME 0x3 -#define CUDA_GET_PRAM 0x7 -#define CUDA_SET_6805_ADDR 0x8 -#define CUDA_SET_TIME 0x9 -#define CUDA_POWERDOWN 0xa -#define CUDA_POWERUP_TIME 0xb -#define CUDA_SET_PRAM 0xc -#define CUDA_MS_RESET 0xd -#define CUDA_SEND_DFAC 0xe -#define CUDA_BATTERY_SWAP_SENSE 0x10 -#define CUDA_RESET_SYSTEM 0x11 -#define CUDA_SET_IPL 0x12 -#define CUDA_FILE_SERVER_FLAG 0x13 -#define CUDA_SET_AUTO_RATE 0x14 -#define CUDA_GET_AUTO_RATE 0x16 -#define CUDA_SET_DEVICE_LIST 0x19 -#define CUDA_GET_DEVICE_LIST 0x1a -#define CUDA_SET_ONE_SECOND_MODE 0x1b -#define CUDA_SET_POWER_MESSAGES 0x21 -#define CUDA_GET_SET_IIC 0x22 -#define CUDA_WAKEUP 0x23 -#define CUDA_TIMER_TICKLE 0x24 -#define CUDA_COMBINED_FORMAT_IIC 0x25 - -#define CUDA_TIMER_FREQ (4700000 / 6) - -/* 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); -static void cuda_timer_update(CUDAState *s, CUDATimer *ti, - int64_t current_time); - -static void cuda_update_irq(CUDAState *s) -{ - 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 time, uint64_t freq) -{ - return muldiv64(time, freq, NANOSECONDS_PER_SECOND); -} - -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(current_time, ti->frequency) - ti->load_time; - d = (tb_diff * 0xBF401675E5DULL) / (ti->frequency << 24); - - if (ti->index == 0) { - /* the timer goes down from latch to -1 (period of latch + 2) */ - if (d <= (ti->counter_value + 1)) { - counter = (ti->counter_value - d) & 0xffff; - } else { - counter = (d - (ti->counter_value + 1)) % (ti->latch + 2); - counter = (ti->latch - counter) & 0xffff; - } - } else { - 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->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); -} - -static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time) -{ - int64_t d, next_time; - unsigned int counter; - - /* current counter value */ - d = muldiv64(current_time - s->load_time, - 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; - } else { - counter = (d - (s->counter_value + 1)) % (s->latch + 2); - counter = (s->latch - counter) & 0xffff; - } - - /* Note: we consider the irq is raised on 0 */ - if (counter == 0xffff) { - next_time = d + s->latch + 1; - } else if (counter == 0) { - next_time = d + s->latch + 2; - } else { - next_time = d + counter; - } - CUDA_DPRINTF("latch=%d counter=%" PRId64 " delta_next=%" PRId64 "\n", - s->latch, d, next_time - d); - next_time = muldiv64(next_time, NANOSECONDS_PER_SECOND, CUDA_TIMER_FREQ) + - s->load_time; - if (next_time <= current_time) - next_time = current_time + 1; - return next_time; -} - -static void cuda_timer_update(CUDAState *s, CUDATimer *ti, - int64_t current_time) -{ - if (!ti->timer) - return; - 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); - timer_mod(ti->timer, ti->next_irq_time); - } -} - -static void cuda_timer1(void *opaque) -{ - CUDAState *s = opaque; - CUDATimer *ti = &s->timers[0]; - - cuda_timer_update(s, ti, ti->next_irq_time); - s->ifr |= T1_INT; - 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; - uint32_t val; - - addr = (addr >> 9) & 0xf; - switch(addr) { - case CUDA_REG_B: - val = s->b; - break; - case CUDA_REG_A: - val = s->a; - break; - case CUDA_REG_DIRB: - val = s->dirb; - break; - case CUDA_REG_DIRA: - val = s->dira; - break; - case CUDA_REG_T1CL: - val = get_counter(&s->timers[0]) & 0xff; - s->ifr &= ~T1_INT; - cuda_update_irq(s); - break; - case CUDA_REG_T1CH: - val = get_counter(&s->timers[0]) >> 8; - cuda_update_irq(s); - break; - case CUDA_REG_T1LL: - val = s->timers[0].latch & 0xff; - break; - case CUDA_REG_T1LH: - /* XXX: check this */ - val = (s->timers[0].latch >> 8) & 0xff; - break; - case CUDA_REG_T2CL: - val = get_counter(&s->timers[1]) & 0xff; - s->ifr &= ~T2_INT; - cuda_update_irq(s); - break; - case CUDA_REG_T2CH: - val = get_counter(&s->timers[1]) >> 8; - break; - case CUDA_REG_SR: - val = s->sr; - s->ifr &= ~(SR_INT | SR_CLOCK_INT | SR_DATA_INT); - cuda_update_irq(s); - break; - case CUDA_REG_ACR: - val = s->acr; - break; - case CUDA_REG_PCR: - val = s->pcr; - break; - case CUDA_REG_IFR: - val = s->ifr; - if (s->ifr & s->ier) { - val |= 0x80; - } - break; - case CUDA_REG_IER: - val = s->ier | 0x80; - break; - default: - case CUDA_REG_ANH: - val = s->anh; - break; - } - if (addr != CUDA_REG_IFR || val != 0) { - CUDA_DPRINTF("read: reg=0x%x val=%02x\n", (int)addr, val); - } - - return val; -} - -static void cuda_writeb(void *opaque, hwaddr addr, uint32_t val) -{ - CUDAState *s = opaque; - - addr = (addr >> 9) & 0xf; - CUDA_DPRINTF("write: reg=0x%x val=%02x\n", (int)addr, val); - - switch(addr) { - case CUDA_REG_B: - s->b = val; - cuda_update(s); - break; - case CUDA_REG_A: - s->a = val; - break; - case CUDA_REG_DIRB: - s->dirb = val; - break; - case CUDA_REG_DIRA: - s->dira = val; - break; - 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 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 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 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 CUDA_REG_T2CL: - s->timers[1].latch = (s->timers[1].latch & 0xff00) | val; - break; - 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 CUDA_REG_SR: - s->sr = val; - break; - 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 CUDA_REG_PCR: - s->pcr = val; - break; - case CUDA_REG_IFR: - /* reset bits */ - s->ifr &= ~val; - cuda_update_irq(s); - break; - case CUDA_REG_IER: - if (val & IER_SET) { - /* set bits */ - s->ier |= val & 0x7f; - } else { - /* reset bits */ - s->ier &= ~val; - } - cuda_update_irq(s); - break; - default: - case CUDA_REG_ANH: - s->anh = val; - break; - } -} - -/* NOTE: TIP and TREQ are negated */ -static void cuda_update(CUDAState *s) -{ - int packet_received, len; - - packet_received = 0; - if (!(s->b & TIP)) { - /* transfer requested from host */ - - if (s->acr & SR_OUT) { - /* data output */ - if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) { - 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; - cuda_delay_set_sr_int(s); - } - } - } else { - if (s->data_in_index < s->data_in_size) { - /* data input */ - if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) { - s->sr = s->data_in[s->data_in_index++]; - CUDA_DPRINTF("recv: %02x\n", s->sr); - /* indicate end of transfer */ - if (s->data_in_index >= s->data_in_size) { - s->b = (s->b | TREQ); - } - cuda_delay_set_sr_int(s); - } - } - } - } else { - /* no transfer requested: handle sync case */ - if ((s->last_b & TIP) && (s->b & TACK) != (s->last_b & TACK)) { - /* update TREQ state each time TACK change state */ - if (s->b & TACK) - s->b = (s->b | TREQ); - else - s->b = (s->b & ~TREQ); - 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 */ - cuda_delay_set_sr_int(s); - } - /* signal if there is data to read */ - if (s->data_in_index < s->data_in_size) { - s->b = (s->b & ~TREQ); - } - } - } - - s->last_acr = s->acr; - s->last_b = s->b; - - /* NOTE: cuda_receive_packet_from_host() can call cuda_update() - recursively */ - if (packet_received) { - len = s->data_out_index; - s->data_out_index = 0; - cuda_receive_packet_from_host(s, s->data_out, len); - } -} - -static void cuda_send_packet_to_host(CUDAState *s, - const uint8_t *data, int len) -{ -#ifdef DEBUG_CUDA_PACKET - { - int i; - printf("cuda_send_packet_to_host:\n"); - for(i = 0; i < len; i++) - printf(" %02x", data[i]); - printf("\n"); - } -#endif - memcpy(s->data_in, data, len); - s->data_in_size = len; - s->data_in_index = 0; - cuda_update(s); - cuda_delay_set_sr_int(s); -} - -static void cuda_adb_poll(void *opaque) -{ - CUDAState *s = opaque; - uint8_t obuf[ADB_MAX_OUT_LEN + 2]; - int olen; - - olen = adb_poll(&s->adb_bus, obuf + 2, s->adb_poll_mask); - if (olen > 0) { - obuf[0] = ADB_PACKET; - obuf[1] = 0x40; /* polled data */ - cuda_send_packet_to_host(s, obuf, olen + 2); - } - timer_mod(s->adb_poll_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms))); -} - -/* 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) -{ - 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; - - 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 { - 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; - } - } - - 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, - const uint8_t *data, int len) -{ -#ifdef DEBUG_CUDA_PACKET - { - int i; - printf("cuda_receive_packet_from_host:\n"); - for(i = 0; i < len; i++) - printf(" %02x", data[i]); - printf("\n"); - } -#endif - switch(data[0]) { - case ADB_PACKET: - { - 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); - } - } - break; - case CUDA_PACKET: - cuda_receive_packet(s, data + 1, len - 1); - break; - } -} - -static void cuda_writew (void *opaque, hwaddr addr, uint32_t value) -{ -} - -static void cuda_writel (void *opaque, hwaddr addr, uint32_t value) -{ -} - -static uint32_t cuda_readw (void *opaque, hwaddr addr) -{ - return 0; -} - -static uint32_t cuda_readl (void *opaque, hwaddr addr) -{ - return 0; -} - -static const MemoryRegionOps cuda_ops = { - .old_mmio = { - .write = { - cuda_writeb, - cuda_writew, - cuda_writel, - }, - .read = { - cuda_readb, - cuda_readw, - cuda_readl, - }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static bool cuda_timer_exist(void *opaque, int version_id) -{ - CUDATimer *s = opaque; - - return s->timer != NULL; -} - -static const VMStateDescription vmstate_cuda_timer = { - .name = "cuda_timer", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT16(latch, CUDATimer), - VMSTATE_UINT16(counter_value, CUDATimer), - VMSTATE_INT64(load_time, CUDATimer), - VMSTATE_INT64(next_irq_time, CUDATimer), - VMSTATE_TIMER_PTR_TEST(timer, CUDATimer, cuda_timer_exist), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_cuda = { - .name = "cuda", - .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), - VMSTATE_UINT8(anh, CUDAState), - VMSTATE_INT32(data_in_size, CUDAState), - 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() - } -}; - -static void cuda_reset(DeviceState *dev) -{ - CUDAState *s = CUDA(dev); - - s->b = 0; - s->a = 0; - s->dirb = 0xff; - s->dira = 0; - s->sr = 0; - s->acr = 0; - s->pcr = 0; - s->ifr = 0; - s->ier = 0; - // s->ier = T1_INT | SR_INT; - s->anh = 0; - s->data_in_size = 0; - s->data_in_index = 0; - s->data_out_index = 0; - s->autopoll = 0; - - s->timers[0].latch = 0xffff; - set_counter(s, &s->timers[0], 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) -{ - CUDAState *s = CUDA(dev); - struct tm tm; - - s->timers[0].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_timer1, s); - s->timers[0].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) -{ - SysBusDevice *d = SYS_BUS_DEVICE(obj); - CUDAState *s = CUDA(obj); - int i; - - memory_region_init_io(&s->mem, obj, &cuda_ops, s, "cuda", 0x2000); - sysbus_init_mmio(d, &s->mem); - sysbus_init_irq(d, &s->irq); - - for (i = 0; i < ARRAY_SIZE(s->timers); i++) { - s->timers[i].index = i; - } - - qbus_create_inplace(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS, - DEVICE(obj), "adb.0"); -} - -static Property cuda_properties[] = { - DEFINE_PROP_UINT64("frequency", CUDAState, frequency, 0), - DEFINE_PROP_END_OF_LIST() -}; - -static void cuda_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = cuda_realizefn; - 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 = { - .name = TYPE_CUDA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(CUDAState), - .instance_init = cuda_initfn, - .class_init = cuda_class_init, -}; - -static void cuda_register_types(void) -{ - type_register_static(&cuda_type_info); -} - -type_init(cuda_register_types) diff --git a/qemu/hw/misc/macio/mac_dbdma.c b/qemu/hw/misc/macio/mac_dbdma.c deleted file mode 100644 index 6051f17db..000000000 --- a/qemu/hw/misc/macio/mac_dbdma.c +++ /dev/null @@ -1,820 +0,0 @@ -/* - * PowerMac descriptor-based DMA emulation - * - * Copyright (c) 2005-2007 Fabrice Bellard - * Copyright (c) 2007 Jocelyn Mayer - * Copyright (c) 2009 Laurent Vivier - * - * some parts from linux-2.6.28, arch/powerpc/include/asm/dbdma.h - * - * Definitions for using the Apple Descriptor-Based DMA controller - * in Power Macintosh computers. - * - * Copyright (C) 1996 Paul Mackerras. - * - * some parts from mol 0.9.71 - * - * Descriptor based DMA emulation - * - * Copyright (C) 1998-2004 Samuel Rydh (samuel@ibrium.se) - * - * 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/isa/isa.h" -#include "hw/ppc/mac_dbdma.h" -#include "qemu/main-loop.h" - -/* debug DBDMA */ -//#define DEBUG_DBDMA - -#ifdef DEBUG_DBDMA -#define DBDMA_DPRINTF(fmt, ...) \ - do { printf("DBDMA: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DBDMA_DPRINTF(fmt, ...) -#endif - -/* - */ - -static DBDMAState *dbdma_from_ch(DBDMA_channel *ch) -{ - return container_of(ch, DBDMAState, channels[ch->channel]); -} - -#ifdef DEBUG_DBDMA -static void dump_dbdma_cmd(dbdma_cmd *cmd) -{ - printf("dbdma_cmd %p\n", cmd); - printf(" req_count 0x%04x\n", le16_to_cpu(cmd->req_count)); - printf(" command 0x%04x\n", le16_to_cpu(cmd->command)); - printf(" phy_addr 0x%08x\n", le32_to_cpu(cmd->phy_addr)); - printf(" cmd_dep 0x%08x\n", le32_to_cpu(cmd->cmd_dep)); - printf(" res_count 0x%04x\n", le16_to_cpu(cmd->res_count)); - printf(" xfer_status 0x%04x\n", le16_to_cpu(cmd->xfer_status)); -} -#else -static void dump_dbdma_cmd(dbdma_cmd *cmd) -{ -} -#endif -static void dbdma_cmdptr_load(DBDMA_channel *ch) -{ - DBDMA_DPRINTF("dbdma_cmdptr_load 0x%08x\n", - ch->regs[DBDMA_CMDPTR_LO]); - cpu_physical_memory_read(ch->regs[DBDMA_CMDPTR_LO], - &ch->current, sizeof(dbdma_cmd)); -} - -static void dbdma_cmdptr_save(DBDMA_channel *ch) -{ - DBDMA_DPRINTF("dbdma_cmdptr_save 0x%08x\n", - ch->regs[DBDMA_CMDPTR_LO]); - DBDMA_DPRINTF("xfer_status 0x%08x res_count 0x%04x\n", - le16_to_cpu(ch->current.xfer_status), - le16_to_cpu(ch->current.res_count)); - cpu_physical_memory_write(ch->regs[DBDMA_CMDPTR_LO], - &ch->current, sizeof(dbdma_cmd)); -} - -static void kill_channel(DBDMA_channel *ch) -{ - DBDMA_DPRINTF("kill_channel\n"); - - ch->regs[DBDMA_STATUS] |= DEAD; - ch->regs[DBDMA_STATUS] &= ~ACTIVE; - - qemu_irq_raise(ch->irq); -} - -static void conditional_interrupt(DBDMA_channel *ch) -{ - dbdma_cmd *current = &ch->current; - uint16_t intr; - uint16_t sel_mask, sel_value; - uint32_t status; - int cond; - - DBDMA_DPRINTF("%s\n", __func__); - - intr = le16_to_cpu(current->command) & INTR_MASK; - - switch(intr) { - case INTR_NEVER: /* don't interrupt */ - return; - case INTR_ALWAYS: /* always interrupt */ - qemu_irq_raise(ch->irq); - DBDMA_DPRINTF("%s: raise\n", __func__); - return; - } - - status = ch->regs[DBDMA_STATUS] & DEVSTAT; - - sel_mask = (ch->regs[DBDMA_INTR_SEL] >> 16) & 0x0f; - sel_value = ch->regs[DBDMA_INTR_SEL] & 0x0f; - - cond = (status & sel_mask) == (sel_value & sel_mask); - - switch(intr) { - case INTR_IFSET: /* intr if condition bit is 1 */ - if (cond) { - qemu_irq_raise(ch->irq); - DBDMA_DPRINTF("%s: raise\n", __func__); - } - return; - case INTR_IFCLR: /* intr if condition bit is 0 */ - if (!cond) { - qemu_irq_raise(ch->irq); - DBDMA_DPRINTF("%s: raise\n", __func__); - } - return; - } -} - -static int conditional_wait(DBDMA_channel *ch) -{ - dbdma_cmd *current = &ch->current; - uint16_t wait; - uint16_t sel_mask, sel_value; - uint32_t status; - int cond; - - DBDMA_DPRINTF("conditional_wait\n"); - - wait = le16_to_cpu(current->command) & WAIT_MASK; - - switch(wait) { - case WAIT_NEVER: /* don't wait */ - return 0; - case WAIT_ALWAYS: /* always wait */ - return 1; - } - - status = ch->regs[DBDMA_STATUS] & DEVSTAT; - - sel_mask = (ch->regs[DBDMA_WAIT_SEL] >> 16) & 0x0f; - sel_value = ch->regs[DBDMA_WAIT_SEL] & 0x0f; - - cond = (status & sel_mask) == (sel_value & sel_mask); - - switch(wait) { - case WAIT_IFSET: /* wait if condition bit is 1 */ - if (cond) - return 1; - return 0; - case WAIT_IFCLR: /* wait if condition bit is 0 */ - if (!cond) - return 1; - return 0; - } - return 0; -} - -static void next(DBDMA_channel *ch) -{ - uint32_t cp; - - ch->regs[DBDMA_STATUS] &= ~BT; - - cp = ch->regs[DBDMA_CMDPTR_LO]; - ch->regs[DBDMA_CMDPTR_LO] = cp + sizeof(dbdma_cmd); - dbdma_cmdptr_load(ch); -} - -static void branch(DBDMA_channel *ch) -{ - dbdma_cmd *current = &ch->current; - - ch->regs[DBDMA_CMDPTR_LO] = current->cmd_dep; - ch->regs[DBDMA_STATUS] |= BT; - dbdma_cmdptr_load(ch); -} - -static void conditional_branch(DBDMA_channel *ch) -{ - dbdma_cmd *current = &ch->current; - uint16_t br; - uint16_t sel_mask, sel_value; - uint32_t status; - int cond; - - DBDMA_DPRINTF("conditional_branch\n"); - - /* check if we must branch */ - - br = le16_to_cpu(current->command) & BR_MASK; - - switch(br) { - case BR_NEVER: /* don't branch */ - next(ch); - return; - case BR_ALWAYS: /* always branch */ - branch(ch); - return; - } - - status = ch->regs[DBDMA_STATUS] & DEVSTAT; - - sel_mask = (ch->regs[DBDMA_BRANCH_SEL] >> 16) & 0x0f; - sel_value = ch->regs[DBDMA_BRANCH_SEL] & 0x0f; - - cond = (status & sel_mask) == (sel_value & sel_mask); - - switch(br) { - case BR_IFSET: /* branch if condition bit is 1 */ - if (cond) - branch(ch); - else - next(ch); - return; - case BR_IFCLR: /* branch if condition bit is 0 */ - if (!cond) - branch(ch); - else - next(ch); - return; - } -} - -static void channel_run(DBDMA_channel *ch); - -static void dbdma_end(DBDMA_io *io) -{ - DBDMA_channel *ch = io->channel; - dbdma_cmd *current = &ch->current; - - DBDMA_DPRINTF("%s\n", __func__); - - if (conditional_wait(ch)) - goto wait; - - current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]); - current->res_count = cpu_to_le16(io->len); - dbdma_cmdptr_save(ch); - if (io->is_last) - ch->regs[DBDMA_STATUS] &= ~FLUSH; - - conditional_interrupt(ch); - conditional_branch(ch); - -wait: - /* Indicate that we're ready for a new DMA round */ - ch->io.processing = false; - - if ((ch->regs[DBDMA_STATUS] & RUN) && - (ch->regs[DBDMA_STATUS] & ACTIVE)) - channel_run(ch); -} - -static void start_output(DBDMA_channel *ch, int key, uint32_t addr, - uint16_t req_count, int is_last) -{ - DBDMA_DPRINTF("start_output\n"); - - /* KEY_REGS, KEY_DEVICE and KEY_STREAM - * are not implemented in the mac-io chip - */ - - DBDMA_DPRINTF("addr 0x%x key 0x%x\n", addr, key); - if (!addr || key > KEY_STREAM3) { - kill_channel(ch); - return; - } - - ch->io.addr = addr; - ch->io.len = req_count; - ch->io.is_last = is_last; - ch->io.dma_end = dbdma_end; - ch->io.is_dma_out = 1; - ch->io.processing = true; - if (ch->rw) { - ch->rw(&ch->io); - } -} - -static void start_input(DBDMA_channel *ch, int key, uint32_t addr, - uint16_t req_count, int is_last) -{ - DBDMA_DPRINTF("start_input\n"); - - /* KEY_REGS, KEY_DEVICE and KEY_STREAM - * are not implemented in the mac-io chip - */ - - DBDMA_DPRINTF("addr 0x%x key 0x%x\n", addr, key); - if (!addr || key > KEY_STREAM3) { - kill_channel(ch); - return; - } - - ch->io.addr = addr; - ch->io.len = req_count; - ch->io.is_last = is_last; - ch->io.dma_end = dbdma_end; - ch->io.is_dma_out = 0; - ch->io.processing = true; - if (ch->rw) { - ch->rw(&ch->io); - } -} - -static void load_word(DBDMA_channel *ch, int key, uint32_t addr, - uint16_t len) -{ - dbdma_cmd *current = &ch->current; - uint32_t val; - - DBDMA_DPRINTF("load_word\n"); - - /* only implements KEY_SYSTEM */ - - if (key != KEY_SYSTEM) { - printf("DBDMA: LOAD_WORD, unimplemented key %x\n", key); - kill_channel(ch); - return; - } - - cpu_physical_memory_read(addr, &val, len); - - if (len == 2) - val = (val << 16) | (current->cmd_dep & 0x0000ffff); - else if (len == 1) - val = (val << 24) | (current->cmd_dep & 0x00ffffff); - - current->cmd_dep = val; - - if (conditional_wait(ch)) - goto wait; - - current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]); - dbdma_cmdptr_save(ch); - ch->regs[DBDMA_STATUS] &= ~FLUSH; - - conditional_interrupt(ch); - next(ch); - -wait: - DBDMA_kick(dbdma_from_ch(ch)); -} - -static void store_word(DBDMA_channel *ch, int key, uint32_t addr, - uint16_t len) -{ - dbdma_cmd *current = &ch->current; - uint32_t val; - - DBDMA_DPRINTF("store_word\n"); - - /* only implements KEY_SYSTEM */ - - if (key != KEY_SYSTEM) { - printf("DBDMA: STORE_WORD, unimplemented key %x\n", key); - kill_channel(ch); - return; - } - - val = current->cmd_dep; - if (len == 2) - val >>= 16; - else if (len == 1) - val >>= 24; - - cpu_physical_memory_write(addr, &val, len); - - if (conditional_wait(ch)) - goto wait; - - current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]); - dbdma_cmdptr_save(ch); - ch->regs[DBDMA_STATUS] &= ~FLUSH; - - conditional_interrupt(ch); - next(ch); - -wait: - DBDMA_kick(dbdma_from_ch(ch)); -} - -static void nop(DBDMA_channel *ch) -{ - dbdma_cmd *current = &ch->current; - - if (conditional_wait(ch)) - goto wait; - - current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]); - dbdma_cmdptr_save(ch); - - conditional_interrupt(ch); - conditional_branch(ch); - -wait: - DBDMA_kick(dbdma_from_ch(ch)); -} - -static void stop(DBDMA_channel *ch) -{ - ch->regs[DBDMA_STATUS] &= ~(ACTIVE|DEAD|FLUSH); - - /* the stop command does not increment command pointer */ -} - -static void channel_run(DBDMA_channel *ch) -{ - dbdma_cmd *current = &ch->current; - uint16_t cmd, key; - uint16_t req_count; - uint32_t phy_addr; - - DBDMA_DPRINTF("channel_run\n"); - dump_dbdma_cmd(current); - - /* clear WAKE flag at command fetch */ - - ch->regs[DBDMA_STATUS] &= ~WAKE; - - cmd = le16_to_cpu(current->command) & COMMAND_MASK; - - switch (cmd) { - case DBDMA_NOP: - nop(ch); - return; - - case DBDMA_STOP: - stop(ch); - return; - } - - key = le16_to_cpu(current->command) & 0x0700; - req_count = le16_to_cpu(current->req_count); - phy_addr = le32_to_cpu(current->phy_addr); - - if (key == KEY_STREAM4) { - printf("command %x, invalid key 4\n", cmd); - kill_channel(ch); - return; - } - - switch (cmd) { - case OUTPUT_MORE: - start_output(ch, key, phy_addr, req_count, 0); - return; - - case OUTPUT_LAST: - start_output(ch, key, phy_addr, req_count, 1); - return; - - case INPUT_MORE: - start_input(ch, key, phy_addr, req_count, 0); - return; - - case INPUT_LAST: - start_input(ch, key, phy_addr, req_count, 1); - return; - } - - if (key < KEY_REGS) { - printf("command %x, invalid key %x\n", cmd, key); - key = KEY_SYSTEM; - } - - /* for LOAD_WORD and STORE_WORD, req_count is on 3 bits - * and BRANCH is invalid - */ - - req_count = req_count & 0x0007; - if (req_count & 0x4) { - req_count = 4; - phy_addr &= ~3; - } else if (req_count & 0x2) { - req_count = 2; - phy_addr &= ~1; - } else - req_count = 1; - - switch (cmd) { - case LOAD_WORD: - load_word(ch, key, phy_addr, req_count); - return; - - case STORE_WORD: - store_word(ch, key, phy_addr, req_count); - return; - } -} - -static void DBDMA_run(DBDMAState *s) -{ - int channel; - - for (channel = 0; channel < DBDMA_CHANNELS; channel++) { - DBDMA_channel *ch = &s->channels[channel]; - uint32_t status = ch->regs[DBDMA_STATUS]; - if (!ch->io.processing && (status & RUN) && (status & ACTIVE)) { - channel_run(ch); - } - } -} - -static void DBDMA_run_bh(void *opaque) -{ - DBDMAState *s = opaque; - - DBDMA_DPRINTF("DBDMA_run_bh\n"); - - DBDMA_run(s); -} - -void DBDMA_kick(DBDMAState *dbdma) -{ - qemu_bh_schedule(dbdma->bh); -} - -void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq, - DBDMA_rw rw, DBDMA_flush flush, - void *opaque) -{ - DBDMAState *s = dbdma; - DBDMA_channel *ch = &s->channels[nchan]; - - DBDMA_DPRINTF("DBDMA_register_channel 0x%x\n", nchan); - - assert(rw); - assert(flush); - - ch->irq = irq; - ch->rw = rw; - ch->flush = flush; - ch->io.opaque = opaque; -} - -static void -dbdma_control_write(DBDMA_channel *ch) -{ - uint16_t mask, value; - uint32_t status; - - mask = (ch->regs[DBDMA_CONTROL] >> 16) & 0xffff; - value = ch->regs[DBDMA_CONTROL] & 0xffff; - - value &= (RUN | PAUSE | FLUSH | WAKE | DEVSTAT); - - status = ch->regs[DBDMA_STATUS]; - - status = (value & mask) | (status & ~mask); - - if (status & WAKE) - status |= ACTIVE; - if (status & RUN) { - status |= ACTIVE; - status &= ~DEAD; - } - if (status & PAUSE) - status &= ~ACTIVE; - 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; - } - - DBDMA_DPRINTF(" status 0x%08x\n", status); - - ch->regs[DBDMA_STATUS] = status; - - if (status & ACTIVE) { - DBDMA_kick(dbdma_from_ch(ch)); - } -} - -static void dbdma_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - int channel = addr >> DBDMA_CHANNEL_SHIFT; - DBDMAState *s = opaque; - DBDMA_channel *ch = &s->channels[channel]; - int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2; - - DBDMA_DPRINTF("writel 0x" TARGET_FMT_plx " <= 0x%08"PRIx64"\n", - addr, value); - DBDMA_DPRINTF("channel 0x%x reg 0x%x\n", - (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg); - - /* cmdptr cannot be modified if channel is ACTIVE */ - - if (reg == DBDMA_CMDPTR_LO && (ch->regs[DBDMA_STATUS] & ACTIVE)) { - return; - } - - ch->regs[reg] = value; - - switch(reg) { - case DBDMA_CONTROL: - dbdma_control_write(ch); - break; - case DBDMA_CMDPTR_LO: - /* 16-byte aligned */ - ch->regs[DBDMA_CMDPTR_LO] &= ~0xf; - dbdma_cmdptr_load(ch); - break; - case DBDMA_STATUS: - case DBDMA_INTR_SEL: - case DBDMA_BRANCH_SEL: - case DBDMA_WAIT_SEL: - /* nothing to do */ - break; - case DBDMA_XFER_MODE: - case DBDMA_CMDPTR_HI: - case DBDMA_DATA2PTR_HI: - case DBDMA_DATA2PTR_LO: - case DBDMA_ADDRESS_HI: - case DBDMA_BRANCH_ADDR_HI: - case DBDMA_RES1: - case DBDMA_RES2: - case DBDMA_RES3: - case DBDMA_RES4: - /* unused */ - break; - } -} - -static uint64_t dbdma_read(void *opaque, hwaddr addr, - unsigned size) -{ - uint32_t value; - int channel = addr >> DBDMA_CHANNEL_SHIFT; - DBDMAState *s = opaque; - DBDMA_channel *ch = &s->channels[channel]; - int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2; - - value = ch->regs[reg]; - - DBDMA_DPRINTF("readl 0x" TARGET_FMT_plx " => 0x%08x\n", addr, value); - DBDMA_DPRINTF("channel 0x%x reg 0x%x\n", - (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg); - - switch(reg) { - case DBDMA_CONTROL: - value = 0; - break; - case DBDMA_STATUS: - case DBDMA_CMDPTR_LO: - case DBDMA_INTR_SEL: - case DBDMA_BRANCH_SEL: - case DBDMA_WAIT_SEL: - /* nothing to do */ - break; - case DBDMA_XFER_MODE: - case DBDMA_CMDPTR_HI: - case DBDMA_DATA2PTR_HI: - case DBDMA_DATA2PTR_LO: - case DBDMA_ADDRESS_HI: - case DBDMA_BRANCH_ADDR_HI: - /* unused */ - value = 0; - break; - case DBDMA_RES1: - case DBDMA_RES2: - case DBDMA_RES3: - case DBDMA_RES4: - /* reserved */ - break; - } - - return value; -} - -static const MemoryRegionOps dbdma_ops = { - .read = dbdma_read, - .write = dbdma_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -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 = 3, - .minimum_version_id = 3, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(channels, DBDMAState, DBDMA_CHANNELS, 1, - vmstate_dbdma_channel, DBDMA_channel), - VMSTATE_END_OF_LIST() - } -}; - -static void dbdma_reset(void *opaque) -{ - DBDMAState *s = opaque; - int i; - - for (i = 0; i < DBDMA_CHANNELS; i++) - 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; - int i; - - s = g_malloc0(sizeof(DBDMAState)); - - 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); - *dbdma_mem = &s->mem; - vmstate_register(NULL, -1, &vmstate_dbdma, s); - qemu_register_reset(dbdma_reset, s); - - s->bh = qemu_bh_new(DBDMA_run_bh, s); - - return s; -} diff --git a/qemu/hw/misc/macio/macio.c b/qemu/hw/misc/macio/macio.c deleted file mode 100644 index be03926b9..000000000 --- a/qemu/hw/misc/macio/macio.c +++ /dev/null @@ -1,449 +0,0 @@ -/* - * PowerMac MacIO device emulation - * - * Copyright (c) 2005-2007 Fabrice Bellard - * Copyright (c) 2007 Jocelyn Mayer - * - * 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/ppc/mac.h" -#include "hw/pci/pci.h" -#include "hw/ppc/mac_dbdma.h" -#include "hw/char/escc.h" - -#define TYPE_MACIO "macio" -#define MACIO(obj) OBJECT_CHECK(MacIOState, (obj), TYPE_MACIO) - -typedef struct MacIOState -{ - /*< private >*/ - PCIDevice parent; - /*< public >*/ - - MemoryRegion bar; - CUDAState cuda; - void *dbdma; - MemoryRegion *pic_mem; - MemoryRegion *escc_mem; - uint64_t frequency; -} MacIOState; - -#define OLDWORLD_MACIO(obj) \ - OBJECT_CHECK(OldWorldMacIOState, (obj), TYPE_OLDWORLD_MACIO) - -typedef struct OldWorldMacIOState { - /*< private >*/ - MacIOState parent_obj; - /*< public >*/ - - qemu_irq irqs[5]; - - MacIONVRAMState nvram; - MACIOIDEState ide[2]; -} OldWorldMacIOState; - -#define NEWWORLD_MACIO(obj) \ - OBJECT_CHECK(NewWorldMacIOState, (obj), TYPE_NEWWORLD_MACIO) - -typedef struct NewWorldMacIOState { - /*< private >*/ - MacIOState parent_obj; - /*< public >*/ - qemu_irq irqs[5]; - MACIOIDEState ide[2]; -} NewWorldMacIOState; - -/* - * The mac-io has two interfaces to the ESCC. One is called "escc-legacy", - * while the other one is the normal, current ESCC interface. - * - * The magic below creates memory aliases to spawn the escc-legacy device - * purely by rerouting the respective registers to our escc region. This - * works because the only difference between the two memory regions is the - * register layout, not their semantics. - * - * Reference: ftp://ftp.software.ibm.com/rs6000/technology/spec/chrp/inwork/CHRP_IORef_1.0.pdf - */ -static void macio_escc_legacy_setup(MacIOState *macio_state) -{ - MemoryRegion *escc_legacy = g_new(MemoryRegion, 1); - MemoryRegion *bar = &macio_state->bar; - int i; - static const int maps[] = { - 0x00, 0x00, - 0x02, 0x20, - 0x04, 0x10, - 0x06, 0x30, - 0x08, 0x40, - 0x0A, 0x50, - 0x60, 0x60, - 0x70, 0x70, - 0x80, 0x70, - 0x90, 0x80, - 0xA0, 0x90, - 0xB0, 0xA0, - 0xC0, 0xB0, - 0xD0, 0xC0, - 0xE0, 0xD0, - 0xF0, 0xE0, - }; - - 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, OBJECT(macio_state), "escc-legacy-port", - macio_state->escc_mem, maps[i+1], 0x2); - memory_region_add_subregion(escc_legacy, maps[i], port); - } - - memory_region_add_subregion(bar, 0x12000, escc_legacy); -} - -static void macio_bar_setup(MacIOState *macio_state) -{ - MemoryRegion *bar = &macio_state->bar; - - if (macio_state->escc_mem) { - memory_region_add_subregion(bar, 0x13000, macio_state->escc_mem); - macio_escc_legacy_setup(macio_state); - } -} - -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) { - error_propagate(errp, err); - return; - } - sysbus_dev = SYS_BUS_DEVICE(&s->cuda); - memory_region_add_subregion(&s->bar, 0x16000, - sysbus_mmio_get_region(sysbus_dev, 0)); - - macio_bar_setup(s); - pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar); -} - -static void macio_realize_ide(MacIOState *s, MACIOIDEState *ide, - qemu_irq irq0, qemu_irq irq1, int dmaid, - Error **errp) -{ - SysBusDevice *sysbus_dev; - - sysbus_dev = SYS_BUS_DEVICE(ide); - sysbus_connect_irq(sysbus_dev, 0, irq0); - sysbus_connect_irq(sysbus_dev, 1, irq1); - macio_ide_register_dma(ide, s->dbdma, dmaid); - object_property_set_bool(OBJECT(ide), true, "realized", errp); -} - -static void macio_oldworld_realize(PCIDevice *d, Error **errp) -{ - MacIOState *s = MACIO(d); - OldWorldMacIOState *os = OLDWORLD_MACIO(d); - Error *err = NULL; - SysBusDevice *sysbus_dev; - int i; - int cur_irq = 0; - - macio_common_realize(d, &err); - if (err) { - error_propagate(errp, err); - return; - } - - sysbus_dev = SYS_BUS_DEVICE(&s->cuda); - sysbus_connect_irq(sysbus_dev, 0, os->irqs[cur_irq++]); - - object_property_set_bool(OBJECT(&os->nvram), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_dev = SYS_BUS_DEVICE(&os->nvram); - memory_region_add_subregion(&s->bar, 0x60000, - sysbus_mmio_get_region(sysbus_dev, 0)); - pmac_format_nvram_partition(&os->nvram, os->nvram.size); - - if (s->pic_mem) { - /* Heathrow PIC */ - memory_region_add_subregion(&s->bar, 0x00000, s->pic_mem); - } - - /* IDE buses */ - for (i = 0; i < ARRAY_SIZE(os->ide); i++) { - qemu_irq irq0 = os->irqs[cur_irq++]; - qemu_irq irq1 = os->irqs[cur_irq++]; - - macio_realize_ide(s, &os->ide[i], irq0, irq1, 0x16 + (i * 4), &err); - if (err) { - error_propagate(errp, err); - return; - } - } -} - -static void macio_init_ide(MacIOState *s, MACIOIDEState *ide, size_t ide_size, - int index) -{ - gchar *name; - - object_initialize(ide, ide_size, TYPE_MACIO_IDE); - qdev_set_parent_bus(DEVICE(ide), sysbus_get_default()); - memory_region_add_subregion(&s->bar, 0x1f000 + ((index + 1) * 0x1000), - &ide->mem); - name = g_strdup_printf("ide[%i]", index); - object_property_add_child(OBJECT(s), name, OBJECT(ide), NULL); - g_free(name); -} - -static void macio_oldworld_init(Object *obj) -{ - MacIOState *s = MACIO(obj); - OldWorldMacIOState *os = OLDWORLD_MACIO(obj); - DeviceState *dev; - int i; - - qdev_init_gpio_out(DEVICE(obj), os->irqs, ARRAY_SIZE(os->irqs)); - - object_initialize(&os->nvram, sizeof(os->nvram), TYPE_MACIO_NVRAM); - dev = DEVICE(&os->nvram); - qdev_prop_set_uint32(dev, "size", 0x2000); - qdev_prop_set_uint32(dev, "it_shift", 4); - - for (i = 0; i < 2; i++) { - macio_init_ide(s, &os->ide[i], sizeof(os->ide[i]), i); - } -} - -static void timer_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ -} - -static uint64_t timer_read(void *opaque, hwaddr addr, unsigned size) -{ - uint32_t value = 0; - uint64_t systime = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - uint64_t kltime; - - kltime = muldiv64(systime, 4194300, NANOSECONDS_PER_SECOND * 4); - kltime = muldiv64(kltime, 18432000, 1048575); - - switch (addr) { - case 0x38: - value = kltime; - break; - case 0x3c: - value = kltime >> 32; - break; - } - - return value; -} - -static const MemoryRegionOps timer_ops = { - .read = timer_read, - .write = timer_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void macio_newworld_realize(PCIDevice *d, Error **errp) -{ - MacIOState *s = MACIO(d); - NewWorldMacIOState *ns = NEWWORLD_MACIO(d); - Error *err = NULL; - SysBusDevice *sysbus_dev; - MemoryRegion *timer_memory = NULL; - int i; - int cur_irq = 0; - - macio_common_realize(d, &err); - if (err) { - error_propagate(errp, err); - return; - } - - sysbus_dev = SYS_BUS_DEVICE(&s->cuda); - sysbus_connect_irq(sysbus_dev, 0, ns->irqs[cur_irq++]); - - if (s->pic_mem) { - /* OpenPIC */ - memory_region_add_subregion(&s->bar, 0x40000, s->pic_mem); - } - - /* IDE buses */ - for (i = 0; i < ARRAY_SIZE(ns->ide); i++) { - qemu_irq irq0 = ns->irqs[cur_irq++]; - qemu_irq irq1 = ns->irqs[cur_irq++]; - - macio_realize_ide(s, &ns->ide[i], irq0, irq1, 0x16 + (i * 4), &err); - if (err) { - error_propagate(errp, err); - return; - } - } - - /* Timer */ - timer_memory = g_new(MemoryRegion, 1); - memory_region_init_io(timer_memory, OBJECT(s), &timer_ops, NULL, "timer", - 0x1000); - memory_region_add_subregion(&s->bar, 0x15000, timer_memory); -} - -static void macio_newworld_init(Object *obj) -{ - MacIOState *s = MACIO(obj); - NewWorldMacIOState *ns = NEWWORLD_MACIO(obj); - int i; - - qdev_init_gpio_out(DEVICE(obj), ns->irqs, ARRAY_SIZE(ns->irqs)); - - for (i = 0; i < 2; i++) { - macio_init_ide(s, &ns->ide[i], sizeof(ns->ide[i]), i); - } -} - -static void macio_instance_init(Object *obj) -{ - MacIOState *s = MACIO(obj); - - 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); -} - -static const VMStateDescription vmstate_macio_oldworld = { - .name = "macio-oldworld", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj.parent, OldWorldMacIOState), - VMSTATE_END_OF_LIST() - } -}; - -static void macio_oldworld_class_init(ObjectClass *oc, void *data) -{ - PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); - DeviceClass *dc = DEVICE_CLASS(oc); - - pdc->realize = macio_oldworld_realize; - pdc->device_id = PCI_DEVICE_ID_APPLE_343S1201; - dc->vmsd = &vmstate_macio_oldworld; -} - -static const VMStateDescription vmstate_macio_newworld = { - .name = "macio-newworld", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj.parent, NewWorldMacIOState), - VMSTATE_END_OF_LIST() - } -}; - -static void macio_newworld_class_init(ObjectClass *oc, void *data) -{ - PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); - DeviceClass *dc = DEVICE_CLASS(oc); - - pdc->realize = macio_newworld_realize; - pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL; - dc->vmsd = &vmstate_macio_newworld; -} - -static Property macio_properties[] = { - DEFINE_PROP_UINT64("frequency", MacIOState, frequency, 0), - DEFINE_PROP_END_OF_LIST() -}; - -static void macio_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - 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 = { - .name = TYPE_OLDWORLD_MACIO, - .parent = TYPE_MACIO, - .instance_size = sizeof(OldWorldMacIOState), - .instance_init = macio_oldworld_init, - .class_init = macio_oldworld_class_init, -}; - -static const TypeInfo macio_newworld_type_info = { - .name = TYPE_NEWWORLD_MACIO, - .parent = TYPE_MACIO, - .instance_size = sizeof(NewWorldMacIOState), - .instance_init = macio_newworld_init, - .class_init = macio_newworld_class_init, -}; - -static const TypeInfo macio_type_info = { - .name = TYPE_MACIO, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(MacIOState), - .instance_init = macio_instance_init, - .abstract = true, - .class_init = macio_class_init, -}; - -static void macio_register_types(void) -{ - type_register_static(&macio_type_info); - type_register_static(&macio_oldworld_type_info); - type_register_static(&macio_newworld_type_info); -} - -type_init(macio_register_types) - -void macio_init(PCIDevice *d, - MemoryRegion *pic_mem, - MemoryRegion *escc_mem) -{ - MacIOState *macio_state = MACIO(d); - - macio_state->pic_mem = pic_mem; - macio_state->escc_mem = escc_mem; - /* Note: this code is strongly inspirated from the corresponding code - in PearPC */ - qdev_prop_set_uint64(DEVICE(&macio_state->cuda), "frequency", - macio_state->frequency); - - qdev_init_nofail(DEVICE(d)); -} diff --git a/qemu/hw/misc/max111x.c b/qemu/hw/misc/max111x.c deleted file mode 100644 index 9014f0f70..000000000 --- a/qemu/hw/misc/max111x.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Maxim MAX1110/1111 ADC chip emulation. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GNU GPLv2. - * - * 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/ssi/ssi.h" - -typedef struct { - SSISlave parent_obj; - - qemu_irq interrupt; - uint8_t tb1, rb2, rb3; - int cycle; - - uint8_t input[8]; - int inputs, com; -} MAX111xState; - -#define TYPE_MAX_111X "max111x" - -#define MAX_111X(obj) \ - OBJECT_CHECK(MAX111xState, (obj), TYPE_MAX_111X) - -#define TYPE_MAX_1110 "max1110" -#define TYPE_MAX_1111 "max1111" - -/* Control-byte bitfields */ -#define CB_PD0 (1 << 0) -#define CB_PD1 (1 << 1) -#define CB_SGL (1 << 2) -#define CB_UNI (1 << 3) -#define CB_SEL0 (1 << 4) -#define CB_SEL1 (1 << 5) -#define CB_SEL2 (1 << 6) -#define CB_START (1 << 7) - -#define CHANNEL_NUM(v, b0, b1, b2) \ - ((((v) >> (2 + (b0))) & 4) | \ - (((v) >> (3 + (b1))) & 2) | \ - (((v) >> (4 + (b2))) & 1)) - -static uint32_t max111x_read(MAX111xState *s) -{ - if (!s->tb1) - return 0; - - switch (s->cycle ++) { - case 1: - return s->rb2; - case 2: - return s->rb3; - } - - return 0; -} - -/* Interpret a control-byte */ -static void max111x_write(MAX111xState *s, uint32_t value) -{ - int measure, chan; - - /* Ignore the value if START bit is zero */ - if (!(value & CB_START)) - return; - - s->cycle = 0; - - if (!(value & CB_PD1)) { - s->tb1 = 0; - return; - } - - s->tb1 = value; - - if (s->inputs == 8) - chan = CHANNEL_NUM(value, 1, 0, 2); - else - chan = CHANNEL_NUM(value & ~CB_SEL0, 0, 1, 2); - - if (value & CB_SGL) - measure = s->input[chan] - s->com; - else - measure = s->input[chan] - s->input[chan ^ 1]; - - if (!(value & CB_UNI)) - measure ^= 0x80; - - s->rb2 = (measure >> 2) & 0x3f; - s->rb3 = (measure << 6) & 0xc0; - - /* FIXME: When should the IRQ be lowered? */ - qemu_irq_raise(s->interrupt); -} - -static uint32_t max111x_transfer(SSISlave *dev, uint32_t value) -{ - MAX111xState *s = MAX_111X(dev); - max111x_write(s, value); - return max111x_read(s); -} - -static const VMStateDescription vmstate_max111x = { - .name = "max111x", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_SSI_SLAVE(parent_obj, MAX111xState), - VMSTATE_UINT8(tb1, MAX111xState), - VMSTATE_UINT8(rb2, MAX111xState), - VMSTATE_UINT8(rb3, MAX111xState), - VMSTATE_INT32_EQUAL(inputs, MAX111xState), - VMSTATE_INT32(com, MAX111xState), - VMSTATE_ARRAY_INT32_UNSAFE(input, MAX111xState, inputs, - vmstate_info_uint8, uint8_t), - VMSTATE_END_OF_LIST() - } -}; - -static int max111x_init(SSISlave *d, int inputs) -{ - DeviceState *dev = DEVICE(d); - MAX111xState *s = MAX_111X(dev); - - qdev_init_gpio_out(dev, &s->interrupt, 1); - - s->inputs = inputs; - /* TODO: add a user interface for setting these */ - s->input[0] = 0xf0; - s->input[1] = 0xe0; - s->input[2] = 0xd0; - s->input[3] = 0xc0; - s->input[4] = 0xb0; - s->input[5] = 0xa0; - s->input[6] = 0x90; - s->input[7] = 0x80; - s->com = 0; - - vmstate_register(dev, -1, &vmstate_max111x, s); - return 0; -} - -static int max1110_init(SSISlave *dev) -{ - return max111x_init(dev, 8); -} - -static int max1111_init(SSISlave *dev) -{ - return max111x_init(dev, 4); -} - -void max111x_set_input(DeviceState *dev, int line, uint8_t value) -{ - MAX111xState *s = MAX_111X(dev); - assert(line >= 0 && line < s->inputs); - s->input[line] = value; -} - -static void max111x_class_init(ObjectClass *klass, void *data) -{ - SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - - k->transfer = max111x_transfer; -} - -static const TypeInfo max111x_info = { - .name = TYPE_MAX_111X, - .parent = TYPE_SSI_SLAVE, - .instance_size = sizeof(MAX111xState), - .class_init = max111x_class_init, - .abstract = true, -}; - -static void max1110_class_init(ObjectClass *klass, void *data) -{ - SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - - k->init = max1110_init; -} - -static const TypeInfo max1110_info = { - .name = TYPE_MAX_1110, - .parent = TYPE_MAX_111X, - .class_init = max1110_class_init, -}; - -static void max1111_class_init(ObjectClass *klass, void *data) -{ - SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - - k->init = max1111_init; -} - -static const TypeInfo max1111_info = { - .name = TYPE_MAX_1111, - .parent = TYPE_MAX_111X, - .class_init = max1111_class_init, -}; - -static void max111x_register_types(void) -{ - type_register_static(&max111x_info); - type_register_static(&max1110_info); - type_register_static(&max1111_info); -} - -type_init(max111x_register_types) diff --git a/qemu/hw/misc/milkymist-hpdmc.c b/qemu/hw/misc/milkymist-hpdmc.c deleted file mode 100644 index b97000fc4..000000000 --- a/qemu/hw/misc/milkymist-hpdmc.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - * QEMU model of the Milkymist High Performance Dynamic Memory Controller. - * - * Copyright (c) 2010 Michael Walle - * - * 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 . - * - * - * Specification available at: - * http://www.milkymist.org/socdoc/hpdmc.pdf - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "qemu/error-report.h" - -enum { - R_SYSTEM = 0, - R_BYPASS, - R_TIMING, - R_IODELAY, - R_MAX -}; - -enum { - IODELAY_DQSDELAY_RDY = (1<<5), - IODELAY_PLL1_LOCKED = (1<<6), - IODELAY_PLL2_LOCKED = (1<<7), -}; - -#define TYPE_MILKYMIST_HPDMC "milkymist-hpdmc" -#define MILKYMIST_HPDMC(obj) \ - OBJECT_CHECK(MilkymistHpdmcState, (obj), TYPE_MILKYMIST_HPDMC) - -struct MilkymistHpdmcState { - SysBusDevice parent_obj; - - MemoryRegion regs_region; - - uint32_t regs[R_MAX]; -}; -typedef struct MilkymistHpdmcState MilkymistHpdmcState; - -static uint64_t hpdmc_read(void *opaque, hwaddr addr, - unsigned size) -{ - MilkymistHpdmcState *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_SYSTEM: - case R_BYPASS: - case R_TIMING: - case R_IODELAY: - r = s->regs[addr]; - break; - - default: - error_report("milkymist_hpdmc: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_milkymist_hpdmc_memory_read(addr << 2, r); - - return r; -} - -static void hpdmc_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - MilkymistHpdmcState *s = opaque; - - trace_milkymist_hpdmc_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_SYSTEM: - case R_BYPASS: - case R_TIMING: - s->regs[addr] = value; - break; - case R_IODELAY: - /* ignore writes */ - break; - - default: - error_report("milkymist_hpdmc: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } -} - -static const MemoryRegionOps hpdmc_mmio_ops = { - .read = hpdmc_read, - .write = hpdmc_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void milkymist_hpdmc_reset(DeviceState *d) -{ - MilkymistHpdmcState *s = MILKYMIST_HPDMC(d); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } - - /* defaults */ - s->regs[R_IODELAY] = IODELAY_DQSDELAY_RDY | IODELAY_PLL1_LOCKED - | IODELAY_PLL2_LOCKED; -} - -static int milkymist_hpdmc_init(SysBusDevice *dev) -{ - MilkymistHpdmcState *s = MILKYMIST_HPDMC(dev); - - memory_region_init_io(&s->regs_region, OBJECT(dev), &hpdmc_mmio_ops, s, - "milkymist-hpdmc", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); - - return 0; -} - -static const VMStateDescription vmstate_milkymist_hpdmc = { - .name = "milkymist-hpdmc", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, MilkymistHpdmcState, R_MAX), - VMSTATE_END_OF_LIST() - } -}; - -static void milkymist_hpdmc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = milkymist_hpdmc_init; - dc->reset = milkymist_hpdmc_reset; - dc->vmsd = &vmstate_milkymist_hpdmc; -} - -static const TypeInfo milkymist_hpdmc_info = { - .name = TYPE_MILKYMIST_HPDMC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MilkymistHpdmcState), - .class_init = milkymist_hpdmc_class_init, -}; - -static void milkymist_hpdmc_register_types(void) -{ - type_register_static(&milkymist_hpdmc_info); -} - -type_init(milkymist_hpdmc_register_types) diff --git a/qemu/hw/misc/milkymist-pfpu.c b/qemu/hw/misc/milkymist-pfpu.c deleted file mode 100644 index 57acd7b36..000000000 --- a/qemu/hw/misc/milkymist-pfpu.c +++ /dev/null @@ -1,549 +0,0 @@ -/* - * QEMU model of the Milkymist programmable FPU. - * - * Copyright (c) 2010 Michael Walle - * - * 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 . - * - * - * Specification available at: - * http://www.milkymist.org/socdoc/pfpu.pdf - * - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "qemu/log.h" -#include "qemu/error-report.h" -#include - -/* #define TRACE_EXEC */ - -#ifdef TRACE_EXEC -# define D_EXEC(x) x -#else -# define D_EXEC(x) -#endif - -enum { - R_CTL = 0, - R_MESHBASE, - R_HMESHLAST, - R_VMESHLAST, - R_CODEPAGE, - R_VERTICES, - R_COLLISIONS, - R_STRAYWRITES, - R_LASTDMA, - R_PC, - R_DREGBASE, - R_CODEBASE, - R_MAX -}; - -enum { - CTL_START_BUSY = (1<<0), -}; - -enum { - OP_NOP = 0, - OP_FADD, - OP_FSUB, - OP_FMUL, - OP_FABS, - OP_F2I, - OP_I2F, - OP_VECTOUT, - OP_SIN, - OP_COS, - OP_ABOVE, - OP_EQUAL, - OP_COPY, - OP_IF, - OP_TSIGN, - OP_QUAKE, -}; - -enum { - GPR_X = 0, - GPR_Y = 1, - GPR_FLAGS = 2, -}; - -enum { - LATENCY_FADD = 5, - LATENCY_FSUB = 5, - LATENCY_FMUL = 7, - LATENCY_FABS = 2, - LATENCY_F2I = 2, - LATENCY_I2F = 3, - LATENCY_VECTOUT = 0, - LATENCY_SIN = 4, - LATENCY_COS = 4, - LATENCY_ABOVE = 2, - LATENCY_EQUAL = 2, - LATENCY_COPY = 2, - LATENCY_IF = 2, - LATENCY_TSIGN = 2, - LATENCY_QUAKE = 2, - MAX_LATENCY = 7 -}; - -#define GPR_BEGIN 0x100 -#define GPR_END 0x17f -#define MICROCODE_BEGIN 0x200 -#define MICROCODE_END 0x3ff -#define MICROCODE_WORDS 2048 - -#define REINTERPRET_CAST(type, val) (*((type *)&(val))) - -#ifdef TRACE_EXEC -static const char *opcode_to_str[] = { - "NOP", "FADD", "FSUB", "FMUL", "FABS", "F2I", "I2F", "VECTOUT", - "SIN", "COS", "ABOVE", "EQUAL", "COPY", "IF", "TSIGN", "QUAKE", -}; -#endif - -#define TYPE_MILKYMIST_PFPU "milkymist-pfpu" -#define MILKYMIST_PFPU(obj) \ - OBJECT_CHECK(MilkymistPFPUState, (obj), TYPE_MILKYMIST_PFPU) - -struct MilkymistPFPUState { - SysBusDevice parent_obj; - - MemoryRegion regs_region; - CharDriverState *chr; - qemu_irq irq; - - uint32_t regs[R_MAX]; - uint32_t gp_regs[128]; - uint32_t microcode[MICROCODE_WORDS]; - - int output_queue_pos; - uint32_t output_queue[MAX_LATENCY]; -}; -typedef struct MilkymistPFPUState MilkymistPFPUState; - -static inline hwaddr -get_dma_address(uint32_t base, uint32_t x, uint32_t y) -{ - return base + 8 * (128 * y + x); -} - -static inline void -output_queue_insert(MilkymistPFPUState *s, uint32_t val, int pos) -{ - s->output_queue[(s->output_queue_pos + pos) % MAX_LATENCY] = val; -} - -static inline uint32_t -output_queue_remove(MilkymistPFPUState *s) -{ - return s->output_queue[s->output_queue_pos]; -} - -static inline void -output_queue_advance(MilkymistPFPUState *s) -{ - s->output_queue[s->output_queue_pos] = 0; - s->output_queue_pos = (s->output_queue_pos + 1) % MAX_LATENCY; -} - -static int pfpu_decode_insn(MilkymistPFPUState *s) -{ - uint32_t pc = s->regs[R_PC]; - uint32_t insn = s->microcode[pc]; - uint32_t reg_a = (insn >> 18) & 0x7f; - uint32_t reg_b = (insn >> 11) & 0x7f; - uint32_t op = (insn >> 7) & 0xf; - uint32_t reg_d = insn & 0x7f; - uint32_t r = 0; - int latency = 0; - - switch (op) { - case OP_NOP: - break; - case OP_FADD: - { - float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); - float b = REINTERPRET_CAST(float, s->gp_regs[reg_b]); - float t = a + b; - r = REINTERPRET_CAST(uint32_t, t); - latency = LATENCY_FADD; - D_EXEC(qemu_log("ADD a=%f b=%f t=%f, r=%08x\n", a, b, t, r)); - } break; - case OP_FSUB: - { - float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); - float b = REINTERPRET_CAST(float, s->gp_regs[reg_b]); - float t = a - b; - r = REINTERPRET_CAST(uint32_t, t); - latency = LATENCY_FSUB; - D_EXEC(qemu_log("SUB a=%f b=%f t=%f, r=%08x\n", a, b, t, r)); - } break; - case OP_FMUL: - { - float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); - float b = REINTERPRET_CAST(float, s->gp_regs[reg_b]); - float t = a * b; - r = REINTERPRET_CAST(uint32_t, t); - latency = LATENCY_FMUL; - D_EXEC(qemu_log("MUL a=%f b=%f t=%f, r=%08x\n", a, b, t, r)); - } break; - case OP_FABS: - { - float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); - float t = fabsf(a); - r = REINTERPRET_CAST(uint32_t, t); - latency = LATENCY_FABS; - D_EXEC(qemu_log("ABS a=%f t=%f, r=%08x\n", a, t, r)); - } break; - case OP_F2I: - { - float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); - int32_t t = a; - r = REINTERPRET_CAST(uint32_t, t); - latency = LATENCY_F2I; - D_EXEC(qemu_log("F2I a=%f t=%d, r=%08x\n", a, t, r)); - } break; - case OP_I2F: - { - int32_t a = REINTERPRET_CAST(int32_t, s->gp_regs[reg_a]); - float t = a; - r = REINTERPRET_CAST(uint32_t, t); - latency = LATENCY_I2F; - D_EXEC(qemu_log("I2F a=%08x t=%f, r=%08x\n", a, t, r)); - } break; - case OP_VECTOUT: - { - uint32_t a = cpu_to_be32(s->gp_regs[reg_a]); - uint32_t b = cpu_to_be32(s->gp_regs[reg_b]); - hwaddr dma_ptr = - get_dma_address(s->regs[R_MESHBASE], - s->gp_regs[GPR_X], s->gp_regs[GPR_Y]); - cpu_physical_memory_write(dma_ptr, &a, 4); - cpu_physical_memory_write(dma_ptr + 4, &b, 4); - s->regs[R_LASTDMA] = dma_ptr + 4; - D_EXEC(qemu_log("VECTOUT a=%08x b=%08x dma=%08x\n", a, b, dma_ptr)); - trace_milkymist_pfpu_vectout(a, b, dma_ptr); - } break; - case OP_SIN: - { - int32_t a = REINTERPRET_CAST(int32_t, s->gp_regs[reg_a]); - float t = sinf(a * (1.0f / (M_PI * 4096.0f))); - r = REINTERPRET_CAST(uint32_t, t); - latency = LATENCY_SIN; - D_EXEC(qemu_log("SIN a=%d t=%f, r=%08x\n", a, t, r)); - } break; - case OP_COS: - { - int32_t a = REINTERPRET_CAST(int32_t, s->gp_regs[reg_a]); - float t = cosf(a * (1.0f / (M_PI * 4096.0f))); - r = REINTERPRET_CAST(uint32_t, t); - latency = LATENCY_COS; - D_EXEC(qemu_log("COS a=%d t=%f, r=%08x\n", a, t, r)); - } break; - case OP_ABOVE: - { - float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); - float b = REINTERPRET_CAST(float, s->gp_regs[reg_b]); - float t = (a > b) ? 1.0f : 0.0f; - r = REINTERPRET_CAST(uint32_t, t); - latency = LATENCY_ABOVE; - D_EXEC(qemu_log("ABOVE a=%f b=%f t=%f, r=%08x\n", a, b, t, r)); - } break; - case OP_EQUAL: - { - float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); - float b = REINTERPRET_CAST(float, s->gp_regs[reg_b]); - float t = (a == b) ? 1.0f : 0.0f; - r = REINTERPRET_CAST(uint32_t, t); - latency = LATENCY_EQUAL; - D_EXEC(qemu_log("EQUAL a=%f b=%f t=%f, r=%08x\n", a, b, t, r)); - } break; - case OP_COPY: - { - r = s->gp_regs[reg_a]; - latency = LATENCY_COPY; - D_EXEC(qemu_log("COPY")); - } break; - case OP_IF: - { - float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); - float b = REINTERPRET_CAST(float, s->gp_regs[reg_b]); - uint32_t f = s->gp_regs[GPR_FLAGS]; - float t = (f != 0) ? a : b; - r = REINTERPRET_CAST(uint32_t, t); - latency = LATENCY_IF; - D_EXEC(qemu_log("IF f=%u a=%f b=%f t=%f, r=%08x\n", f, a, b, t, r)); - } break; - case OP_TSIGN: - { - float a = REINTERPRET_CAST(float, s->gp_regs[reg_a]); - float b = REINTERPRET_CAST(float, s->gp_regs[reg_b]); - float t = (b < 0) ? -a : a; - r = REINTERPRET_CAST(uint32_t, t); - latency = LATENCY_TSIGN; - D_EXEC(qemu_log("TSIGN a=%f b=%f t=%f, r=%08x\n", a, b, t, r)); - } break; - case OP_QUAKE: - { - uint32_t a = s->gp_regs[reg_a]; - r = 0x5f3759df - (a >> 1); - latency = LATENCY_QUAKE; - D_EXEC(qemu_log("QUAKE a=%d r=%08x\n", a, r)); - } break; - - default: - error_report("milkymist_pfpu: unknown opcode %d", op); - break; - } - - if (!reg_d) { - D_EXEC(qemu_log("%04d %8s R%03d, R%03d \n", - s->regs[R_PC], opcode_to_str[op], reg_a, reg_b, latency, - s->regs[R_PC] + latency)); - } else { - D_EXEC(qemu_log("%04d %8s R%03d, R%03d -> R%03d\n", - s->regs[R_PC], opcode_to_str[op], reg_a, reg_b, latency, - s->regs[R_PC] + latency, reg_d)); - } - - if (op == OP_VECTOUT) { - return 0; - } - - /* store output for this cycle */ - if (reg_d) { - uint32_t val = output_queue_remove(s); - D_EXEC(qemu_log("R%03d <- 0x%08x\n", reg_d, val)); - s->gp_regs[reg_d] = val; - } - - output_queue_advance(s); - - /* store op output */ - if (op != OP_NOP) { - output_queue_insert(s, r, latency-1); - } - - /* advance PC */ - s->regs[R_PC]++; - - return 1; -}; - -static void pfpu_start(MilkymistPFPUState *s) -{ - int x, y; - int i; - - for (y = 0; y <= s->regs[R_VMESHLAST]; y++) { - for (x = 0; x <= s->regs[R_HMESHLAST]; x++) { - D_EXEC(qemu_log("\nprocessing x=%d y=%d\n", x, y)); - - /* set current position */ - s->gp_regs[GPR_X] = x; - s->gp_regs[GPR_Y] = y; - - /* run microcode on this position */ - i = 0; - while (pfpu_decode_insn(s)) { - /* decode at most MICROCODE_WORDS instructions */ - if (++i >= MICROCODE_WORDS) { - error_report("milkymist_pfpu: too many instructions " - "executed in microcode. No VECTOUT?"); - break; - } - } - - /* reset pc for next run */ - s->regs[R_PC] = 0; - } - } - - s->regs[R_VERTICES] = x * y; - - trace_milkymist_pfpu_pulse_irq(); - qemu_irq_pulse(s->irq); -} - -static inline int get_microcode_address(MilkymistPFPUState *s, uint32_t addr) -{ - return (512 * s->regs[R_CODEPAGE]) + addr - MICROCODE_BEGIN; -} - -static uint64_t pfpu_read(void *opaque, hwaddr addr, - unsigned size) -{ - MilkymistPFPUState *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_CTL: - case R_MESHBASE: - case R_HMESHLAST: - case R_VMESHLAST: - case R_CODEPAGE: - case R_VERTICES: - case R_COLLISIONS: - case R_STRAYWRITES: - case R_LASTDMA: - case R_PC: - case R_DREGBASE: - case R_CODEBASE: - r = s->regs[addr]; - break; - case GPR_BEGIN ... GPR_END: - r = s->gp_regs[addr - GPR_BEGIN]; - break; - case MICROCODE_BEGIN ... MICROCODE_END: - r = s->microcode[get_microcode_address(s, addr)]; - break; - - default: - error_report("milkymist_pfpu: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_milkymist_pfpu_memory_read(addr << 2, r); - - return r; -} - -static void pfpu_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - MilkymistPFPUState *s = opaque; - - trace_milkymist_pfpu_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_CTL: - if (value & CTL_START_BUSY) { - pfpu_start(s); - } - break; - case R_MESHBASE: - case R_HMESHLAST: - case R_VMESHLAST: - case R_CODEPAGE: - case R_VERTICES: - case R_COLLISIONS: - case R_STRAYWRITES: - case R_LASTDMA: - case R_PC: - case R_DREGBASE: - case R_CODEBASE: - s->regs[addr] = value; - break; - case GPR_BEGIN ... GPR_END: - s->gp_regs[addr - GPR_BEGIN] = value; - break; - case MICROCODE_BEGIN ... MICROCODE_END: - s->microcode[get_microcode_address(s, addr)] = value; - break; - - default: - error_report("milkymist_pfpu: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } -} - -static const MemoryRegionOps pfpu_mmio_ops = { - .read = pfpu_read, - .write = pfpu_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void milkymist_pfpu_reset(DeviceState *d) -{ - MilkymistPFPUState *s = MILKYMIST_PFPU(d); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } - for (i = 0; i < 128; i++) { - s->gp_regs[i] = 0; - } - for (i = 0; i < MICROCODE_WORDS; i++) { - s->microcode[i] = 0; - } - s->output_queue_pos = 0; - for (i = 0; i < MAX_LATENCY; i++) { - s->output_queue[i] = 0; - } -} - -static int milkymist_pfpu_init(SysBusDevice *dev) -{ - MilkymistPFPUState *s = MILKYMIST_PFPU(dev); - - sysbus_init_irq(dev, &s->irq); - - memory_region_init_io(&s->regs_region, OBJECT(dev), &pfpu_mmio_ops, s, - "milkymist-pfpu", MICROCODE_END * 4); - sysbus_init_mmio(dev, &s->regs_region); - - return 0; -} - -static const VMStateDescription vmstate_milkymist_pfpu = { - .name = "milkymist-pfpu", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, MilkymistPFPUState, R_MAX), - VMSTATE_UINT32_ARRAY(gp_regs, MilkymistPFPUState, 128), - VMSTATE_UINT32_ARRAY(microcode, MilkymistPFPUState, MICROCODE_WORDS), - VMSTATE_INT32(output_queue_pos, MilkymistPFPUState), - VMSTATE_UINT32_ARRAY(output_queue, MilkymistPFPUState, MAX_LATENCY), - VMSTATE_END_OF_LIST() - } -}; - -static void milkymist_pfpu_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = milkymist_pfpu_init; - dc->reset = milkymist_pfpu_reset; - dc->vmsd = &vmstate_milkymist_pfpu; -} - -static const TypeInfo milkymist_pfpu_info = { - .name = TYPE_MILKYMIST_PFPU, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MilkymistPFPUState), - .class_init = milkymist_pfpu_class_init, -}; - -static void milkymist_pfpu_register_types(void) -{ - type_register_static(&milkymist_pfpu_info); -} - -type_init(milkymist_pfpu_register_types) diff --git a/qemu/hw/misc/mips_cmgcr.c b/qemu/hw/misc/mips_cmgcr.c deleted file mode 100644 index 37be23995..000000000 --- a/qemu/hw/misc/mips_cmgcr.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * 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 - * - * 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 deleted file mode 100644 index d2b8e42da..000000000 --- a/qemu/hw/misc/mips_cpc.c +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 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 . - */ - -#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 deleted file mode 100644 index da5455062..000000000 --- a/qemu/hw/misc/mips_itu.c +++ /dev/null @@ -1,521 +0,0 @@ -/* - * 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 . - */ - -#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 deleted file mode 100644 index 48d7dfb2d..000000000 --- a/qemu/hw/misc/mst_fpga.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * PXA270-based Intel Mainstone platforms. - * FPGA driver - * - * Copyright (c) 2007 by Armin Kuster or - * - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" - -/* Mainstone FPGA for extern irqs */ -#define FPGA_GPIO_PIN 0 -#define MST_NUM_IRQS 16 -#define MST_LEDDAT1 0x10 -#define MST_LEDDAT2 0x14 -#define MST_LEDCTRL 0x40 -#define MST_GPSWR 0x60 -#define MST_MSCWR1 0x80 -#define MST_MSCWR2 0x84 -#define MST_MSCWR3 0x88 -#define MST_MSCRD 0x90 -#define MST_INTMSKENA 0xc0 -#define MST_INTSETCLR 0xd0 -#define MST_PCMCIA0 0xe0 -#define MST_PCMCIA1 0xe4 - -#define MST_PCMCIAx_READY (1 << 10) -#define MST_PCMCIAx_nCD (1 << 5) - -#define MST_PCMCIA_CD0_IRQ 9 -#define MST_PCMCIA_CD1_IRQ 13 - -#define TYPE_MAINSTONE_FPGA "mainstone-fpga" -#define MAINSTONE_FPGA(obj) \ - OBJECT_CHECK(mst_irq_state, (obj), TYPE_MAINSTONE_FPGA) - -typedef struct mst_irq_state{ - SysBusDevice parent_obj; - - MemoryRegion iomem; - - qemu_irq parent; - - uint32_t prev_level; - uint32_t leddat1; - uint32_t leddat2; - uint32_t ledctrl; - uint32_t gpswr; - uint32_t mscwr1; - uint32_t mscwr2; - uint32_t mscwr3; - uint32_t mscrd; - uint32_t intmskena; - uint32_t intsetclr; - uint32_t pcmcia0; - uint32_t pcmcia1; -}mst_irq_state; - -static void -mst_fpga_set_irq(void *opaque, int irq, int level) -{ - mst_irq_state *s = (mst_irq_state *)opaque; - uint32_t oldint = s->intsetclr & s->intmskena; - - if (level) - s->prev_level |= 1u << irq; - else - s->prev_level &= ~(1u << irq); - - switch(irq) { - case MST_PCMCIA_CD0_IRQ: - if (level) - s->pcmcia0 &= ~MST_PCMCIAx_nCD; - else - s->pcmcia0 |= MST_PCMCIAx_nCD; - break; - case MST_PCMCIA_CD1_IRQ: - if (level) - s->pcmcia1 &= ~MST_PCMCIAx_nCD; - else - s->pcmcia1 |= MST_PCMCIAx_nCD; - break; - } - - if ((s->intmskena & (1u << irq)) && level) - s->intsetclr |= 1u << irq; - - if (oldint != (s->intsetclr & s->intmskena)) - qemu_set_irq(s->parent, s->intsetclr & s->intmskena); -} - - -static uint64_t -mst_fpga_readb(void *opaque, hwaddr addr, unsigned size) -{ - mst_irq_state *s = (mst_irq_state *) opaque; - - switch (addr) { - case MST_LEDDAT1: - return s->leddat1; - case MST_LEDDAT2: - return s->leddat2; - case MST_LEDCTRL: - return s->ledctrl; - case MST_GPSWR: - return s->gpswr; - case MST_MSCWR1: - return s->mscwr1; - case MST_MSCWR2: - return s->mscwr2; - case MST_MSCWR3: - return s->mscwr3; - case MST_MSCRD: - return s->mscrd; - case MST_INTMSKENA: - return s->intmskena; - case MST_INTSETCLR: - return s->intsetclr; - case MST_PCMCIA0: - return s->pcmcia0; - case MST_PCMCIA1: - return s->pcmcia1; - default: - printf("Mainstone - mst_fpga_readb: Bad register offset " - "0x" TARGET_FMT_plx "\n", addr); - } - return 0; -} - -static void -mst_fpga_writeb(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - mst_irq_state *s = (mst_irq_state *) opaque; - value &= 0xffffffff; - - switch (addr) { - case MST_LEDDAT1: - s->leddat1 = value; - break; - case MST_LEDDAT2: - s->leddat2 = value; - break; - case MST_LEDCTRL: - s->ledctrl = value; - break; - case MST_GPSWR: - s->gpswr = value; - break; - case MST_MSCWR1: - s->mscwr1 = value; - break; - case MST_MSCWR2: - s->mscwr2 = value; - break; - case MST_MSCWR3: - s->mscwr3 = value; - break; - case MST_MSCRD: - s->mscrd = value; - break; - case MST_INTMSKENA: /* Mask interrupt */ - s->intmskena = (value & 0xFEEFF); - qemu_set_irq(s->parent, s->intsetclr & s->intmskena); - break; - case MST_INTSETCLR: /* clear or set interrupt */ - s->intsetclr = (value & 0xFEEFF); - qemu_set_irq(s->parent, s->intsetclr & s->intmskena); - break; - /* For PCMCIAx allow the to change only power and reset */ - case MST_PCMCIA0: - s->pcmcia0 = (value & 0x1f) | (s->pcmcia0 & ~0x1f); - break; - case MST_PCMCIA1: - s->pcmcia1 = (value & 0x1f) | (s->pcmcia1 & ~0x1f); - break; - default: - printf("Mainstone - mst_fpga_writeb: Bad register offset " - "0x" TARGET_FMT_plx "\n", addr); - } -} - -static const MemoryRegionOps mst_fpga_ops = { - .read = mst_fpga_readb, - .write = mst_fpga_writeb, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int mst_fpga_post_load(void *opaque, int version_id) -{ - mst_irq_state *s = (mst_irq_state *) opaque; - - qemu_set_irq(s->parent, s->intsetclr & s->intmskena); - return 0; -} - -static int mst_fpga_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - mst_irq_state *s = MAINSTONE_FPGA(dev); - - s->pcmcia0 = MST_PCMCIAx_READY | MST_PCMCIAx_nCD; - s->pcmcia1 = MST_PCMCIAx_READY | MST_PCMCIAx_nCD; - - sysbus_init_irq(sbd, &s->parent); - - /* alloc the external 16 irqs */ - qdev_init_gpio_in(dev, mst_fpga_set_irq, MST_NUM_IRQS); - - memory_region_init_io(&s->iomem, OBJECT(s), &mst_fpga_ops, s, - "fpga", 0x00100000); - sysbus_init_mmio(sbd, &s->iomem); - return 0; -} - -static VMStateDescription vmstate_mst_fpga_regs = { - .name = "mainstone_fpga", - .version_id = 0, - .minimum_version_id = 0, - .post_load = mst_fpga_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(prev_level, mst_irq_state), - VMSTATE_UINT32(leddat1, mst_irq_state), - VMSTATE_UINT32(leddat2, mst_irq_state), - VMSTATE_UINT32(ledctrl, mst_irq_state), - VMSTATE_UINT32(gpswr, mst_irq_state), - VMSTATE_UINT32(mscwr1, mst_irq_state), - VMSTATE_UINT32(mscwr2, mst_irq_state), - VMSTATE_UINT32(mscwr3, mst_irq_state), - VMSTATE_UINT32(mscrd, mst_irq_state), - VMSTATE_UINT32(intmskena, mst_irq_state), - VMSTATE_UINT32(intsetclr, mst_irq_state), - VMSTATE_UINT32(pcmcia0, mst_irq_state), - VMSTATE_UINT32(pcmcia1, mst_irq_state), - VMSTATE_END_OF_LIST(), - }, -}; - -static void mst_fpga_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = mst_fpga_init; - dc->desc = "Mainstone II FPGA"; - dc->vmsd = &vmstate_mst_fpga_regs; -} - -static const TypeInfo mst_fpga_info = { - .name = TYPE_MAINSTONE_FPGA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(mst_irq_state), - .class_init = mst_fpga_class_init, -}; - -static void mst_fpga_register_types(void) -{ - type_register_static(&mst_fpga_info); -} - -type_init(mst_fpga_register_types) diff --git a/qemu/hw/misc/omap_clk.c b/qemu/hw/misc/omap_clk.c deleted file mode 100644 index 19151d07d..000000000 --- a/qemu/hw/misc/omap_clk.c +++ /dev/null @@ -1,1265 +0,0 @@ -/* - * OMAP clocks. - * - * Copyright (C) 2006-2008 Andrzej Zaborowski - * - * Clocks data comes in part from arch/arm/mach-omap1/clock.h in Linux. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/arm/omap.h" - -struct clk { - const char *name; - const char *alias; - struct clk *parent; - struct clk *child1; - struct clk *sibling; -#define ALWAYS_ENABLED (1 << 0) -#define CLOCK_IN_OMAP310 (1 << 10) -#define CLOCK_IN_OMAP730 (1 << 11) -#define CLOCK_IN_OMAP1510 (1 << 12) -#define CLOCK_IN_OMAP16XX (1 << 13) -#define CLOCK_IN_OMAP242X (1 << 14) -#define CLOCK_IN_OMAP243X (1 << 15) -#define CLOCK_IN_OMAP343X (1 << 16) - uint32_t flags; - int id; - - int running; /* Is currently ticking */ - int enabled; /* Is enabled, regardless of its input clk */ - unsigned long rate; /* Current rate (if .running) */ - unsigned int divisor; /* Rate relative to input (if .enabled) */ - unsigned int multiplier; /* Rate relative to input (if .enabled) */ - qemu_irq users[16]; /* Who to notify on change */ - int usecount; /* Automatically idle when unused */ -}; - -static struct clk xtal_osc12m = { - .name = "xtal_osc_12m", - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, -}; - -static struct clk xtal_osc32k = { - .name = "xtal_osc_32k", - .rate = 32768, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | - CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, -}; - -static struct clk ck_ref = { - .name = "ck_ref", - .alias = "clkin", - .parent = &xtal_osc12m, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | - ALWAYS_ENABLED, -}; - -/* If a dpll is disabled it becomes a bypass, child clocks don't stop */ -static struct clk dpll1 = { - .name = "dpll1", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | - ALWAYS_ENABLED, -}; - -static struct clk dpll2 = { - .name = "dpll2", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP310 | ALWAYS_ENABLED, -}; - -static struct clk dpll3 = { - .name = "dpll3", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP310 | ALWAYS_ENABLED, -}; - -static struct clk dpll4 = { - .name = "dpll4", - .parent = &ck_ref, - .multiplier = 4, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, -}; - -static struct clk apll = { - .name = "apll", - .parent = &ck_ref, - .multiplier = 48, - .divisor = 12, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, -}; - -static struct clk ck_48m = { - .name = "ck_48m", - .parent = &dpll4, /* either dpll4 or apll */ - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, -}; - -static struct clk ck_dpll1out = { - .name = "ck_dpll1out", - .parent = &dpll1, - .flags = CLOCK_IN_OMAP16XX, -}; - -static struct clk sossi_ck = { - .name = "ck_sossi", - .parent = &ck_dpll1out, - .flags = CLOCK_IN_OMAP16XX, -}; - -static struct clk clkm1 = { - .name = "clkm1", - .alias = "ck_gen1", - .parent = &dpll1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | - ALWAYS_ENABLED, -}; - -static struct clk clkm2 = { - .name = "clkm2", - .alias = "ck_gen2", - .parent = &dpll1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | - ALWAYS_ENABLED, -}; - -static struct clk clkm3 = { - .name = "clkm3", - .alias = "ck_gen3", - .parent = &dpll1, /* either dpll1 or ck_ref */ - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | - ALWAYS_ENABLED, -}; - -static struct clk arm_ck = { - .name = "arm_ck", - .alias = "mpu_ck", - .parent = &clkm1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | - ALWAYS_ENABLED, -}; - -static struct clk armper_ck = { - .name = "armper_ck", - .alias = "mpuper_ck", - .parent = &clkm1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, -}; - -static struct clk arm_gpio_ck = { - .name = "arm_gpio_ck", - .alias = "mpu_gpio_ck", - .parent = &clkm1, - .divisor = 1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, -}; - -static struct clk armxor_ck = { - .name = "armxor_ck", - .alias = "mpuxor_ck", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, -}; - -static struct clk armtim_ck = { - .name = "armtim_ck", - .alias = "mputim_ck", - .parent = &ck_ref, /* either CLKIN or DPLL1 */ - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, -}; - -static struct clk armwdt_ck = { - .name = "armwdt_ck", - .alias = "mpuwd_ck", - .parent = &clkm1, - .divisor = 14, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | - ALWAYS_ENABLED, -}; - -static struct clk arminth_ck16xx = { - .name = "arminth_ck", - .parent = &arm_ck, - .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, - /* Note: On 16xx the frequency can be divided by 2 by programming - * ARM_CKCTL:ARM_INTHCK_SEL(14) to 1 - * - * 1510 version is in TC clocks. - */ -}; - -static struct clk dsp_ck = { - .name = "dsp_ck", - .parent = &clkm2, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, -}; - -static struct clk dspmmu_ck = { - .name = "dspmmu_ck", - .parent = &clkm2, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - ALWAYS_ENABLED, -}; - -static struct clk dspper_ck = { - .name = "dspper_ck", - .parent = &clkm2, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, -}; - -static struct clk dspxor_ck = { - .name = "dspxor_ck", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, -}; - -static struct clk dsptim_ck = { - .name = "dsptim_ck", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, -}; - -static struct clk tc_ck = { - .name = "tc_ck", - .parent = &clkm3, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - CLOCK_IN_OMAP730 | CLOCK_IN_OMAP310 | - ALWAYS_ENABLED, -}; - -static struct clk arminth_ck15xx = { - .name = "arminth_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, - /* Note: On 1510 the frequency follows TC_CK - * - * 16xx version is in MPU clocks. - */ -}; - -static struct clk tipb_ck = { - /* No-idle controlled by "tc_ck" */ - .name = "tipb_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, -}; - -static struct clk l3_ocpi_ck = { - /* No-idle controlled by "tc_ck" */ - .name = "l3_ocpi_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX, -}; - -static struct clk tc1_ck = { - .name = "tc1_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX, -}; - -static struct clk tc2_ck = { - .name = "tc2_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX, -}; - -static struct clk dma_ck = { - /* No-idle controlled by "tc_ck" */ - .name = "dma_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | - ALWAYS_ENABLED, -}; - -static struct clk dma_lcdfree_ck = { - .name = "dma_lcdfree_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, -}; - -static struct clk api_ck = { - .name = "api_ck", - .alias = "mpui_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, -}; - -static struct clk lb_ck = { - .name = "lb_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, -}; - -static struct clk lbfree_ck = { - .name = "lbfree_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, -}; - -static struct clk hsab_ck = { - .name = "hsab_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, -}; - -static struct clk rhea1_ck = { - .name = "rhea1_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, -}; - -static struct clk rhea2_ck = { - .name = "rhea2_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, -}; - -static struct clk lcd_ck_16xx = { - .name = "lcd_ck", - .parent = &clkm3, - .flags = CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP730, -}; - -static struct clk lcd_ck_1510 = { - .name = "lcd_ck", - .parent = &clkm3, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, -}; - -static struct clk uart1_1510 = { - .name = "uart1_ck", - /* Direct from ULPD, no real parent */ - .parent = &armper_ck, /* either armper_ck or dpll4 */ - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, -}; - -static struct clk uart1_16xx = { - .name = "uart1_ck", - /* Direct from ULPD, no real parent */ - .parent = &armper_ck, - .rate = 48000000, - .flags = CLOCK_IN_OMAP16XX, -}; - -static struct clk uart2_ck = { - .name = "uart2_ck", - /* Direct from ULPD, no real parent */ - .parent = &armper_ck, /* either armper_ck or dpll4 */ - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | - ALWAYS_ENABLED, -}; - -static struct clk uart3_1510 = { - .name = "uart3_ck", - /* Direct from ULPD, no real parent */ - .parent = &armper_ck, /* either armper_ck or dpll4 */ - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, -}; - -static struct clk uart3_16xx = { - .name = "uart3_ck", - /* Direct from ULPD, no real parent */ - .parent = &armper_ck, - .rate = 48000000, - .flags = CLOCK_IN_OMAP16XX, -}; - -static struct clk usb_clk0 = { /* 6 MHz output on W4_USB_CLK0 */ - .name = "usb_clk0", - .alias = "usb.clko", - /* Direct from ULPD, no parent */ - .rate = 6000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, -}; - -static struct clk usb_hhc_ck1510 = { - .name = "usb_hhc_ck", - /* Direct from ULPD, no parent */ - .rate = 48000000, /* Actually 2 clocks, 12MHz and 48MHz */ - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, -}; - -static struct clk usb_hhc_ck16xx = { - .name = "usb_hhc_ck", - /* Direct from ULPD, no parent */ - .rate = 48000000, - /* OTG_SYSCON_2.OTG_PADEN == 0 (not 1510-compatible) */ - .flags = CLOCK_IN_OMAP16XX, -}; - -static struct clk usb_w2fc_mclk = { - .name = "usb_w2fc_mclk", - .alias = "usb_w2fc_ck", - .parent = &ck_48m, - .rate = 48000000, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, -}; - -static struct clk mclk_1510 = { - .name = "mclk", - /* Direct from ULPD, no parent. May be enabled by ext hardware. */ - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510, -}; - -static struct clk bclk_310 = { - .name = "bt_mclk_out", /* Alias midi_mclk_out? */ - .parent = &armper_ck, - .flags = CLOCK_IN_OMAP310, -}; - -static struct clk mclk_310 = { - .name = "com_mclk_out", - .parent = &armper_ck, - .flags = CLOCK_IN_OMAP310, -}; - -static struct clk mclk_16xx = { - .name = "mclk", - /* Direct from ULPD, no parent. May be enabled by ext hardware. */ - .flags = CLOCK_IN_OMAP16XX, -}; - -static struct clk bclk_1510 = { - .name = "bclk", - /* Direct from ULPD, no parent. May be enabled by ext hardware. */ - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510, -}; - -static struct clk bclk_16xx = { - .name = "bclk", - /* Direct from ULPD, no parent. May be enabled by ext hardware. */ - .flags = CLOCK_IN_OMAP16XX, -}; - -static struct clk mmc1_ck = { - .name = "mmc_ck", - .id = 1, - /* Functional clock is direct from ULPD, interface clock is ARMPER */ - .parent = &armper_ck, /* either armper_ck or dpll4 */ - .rate = 48000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, -}; - -static struct clk mmc2_ck = { - .name = "mmc_ck", - .id = 2, - /* Functional clock is direct from ULPD, interface clock is ARMPER */ - .parent = &armper_ck, - .rate = 48000000, - .flags = CLOCK_IN_OMAP16XX, -}; - -static struct clk cam_mclk = { - .name = "cam.mclk", - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, - .rate = 12000000, -}; - -static struct clk cam_exclk = { - .name = "cam.exclk", - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, - /* Either 12M from cam.mclk or 48M from dpll4 */ - .parent = &cam_mclk, -}; - -static struct clk cam_lclk = { - .name = "cam.lclk", - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, -}; - -static struct clk i2c_fck = { - .name = "i2c_fck", - .id = 1, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - ALWAYS_ENABLED, - .parent = &armxor_ck, -}; - -static struct clk i2c_ick = { - .name = "i2c_ick", - .id = 1, - .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, - .parent = &armper_ck, -}; - -static struct clk clk32k = { - .name = "clk32-kHz", - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, - .parent = &xtal_osc32k, -}; - -static struct clk ref_clk = { - .name = "ref_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, - .rate = 12000000, /* 12 MHz or 13 MHz or 19.2 MHz */ - /*.parent = sys.xtalin */ -}; - -static struct clk apll_96m = { - .name = "apll_96m", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, - .rate = 96000000, - /*.parent = ref_clk */ -}; - -static struct clk apll_54m = { - .name = "apll_54m", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, - .rate = 54000000, - /*.parent = ref_clk */ -}; - -static struct clk sys_clk = { - .name = "sys_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, - .rate = 32768, - /*.parent = sys.xtalin */ -}; - -static struct clk sleep_clk = { - .name = "sleep_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, - .rate = 32768, - /*.parent = sys.xtalin */ -}; - -static struct clk dpll_ck = { - .name = "dpll", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, - .parent = &ref_clk, -}; - -static struct clk dpll_x2_ck = { - .name = "dpll_x2", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, - .parent = &ref_clk, -}; - -static struct clk wdt1_sys_clk = { - .name = "wdt1_sys_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, - .rate = 32768, - /*.parent = sys.xtalin */ -}; - -static struct clk func_96m_clk = { - .name = "func_96m_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .divisor = 1, - .parent = &apll_96m, -}; - -static struct clk func_48m_clk = { - .name = "func_48m_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .divisor = 2, - .parent = &apll_96m, -}; - -static struct clk func_12m_clk = { - .name = "func_12m_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .divisor = 8, - .parent = &apll_96m, -}; - -static struct clk func_54m_clk = { - .name = "func_54m_clk", - .flags = CLOCK_IN_OMAP242X, - .divisor = 1, - .parent = &apll_54m, -}; - -static struct clk sys_clkout = { - .name = "clkout", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk sys_clkout2 = { - .name = "clkout2", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_clk = { - .name = "core_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &dpll_x2_ck, /* Switchable between dpll_ck and clk32k */ -}; - -static struct clk l3_clk = { - .name = "l3_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_clk, -}; - -static struct clk core_l4_iclk = { - .name = "core_l4_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &l3_clk, -}; - -static struct clk wu_l4_iclk = { - .name = "wu_l4_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &l3_clk, -}; - -static struct clk core_l3_iclk = { - .name = "core_l3_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_clk, -}; - -static struct clk core_l4_usb_clk = { - .name = "core_l4_usb_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &l3_clk, -}; - -static struct clk wu_gpt1_clk = { - .name = "wu_gpt1_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk wu_32k_clk = { - .name = "wu_32k_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk uart1_fclk = { - .name = "uart1_fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_48m_clk, -}; - -static struct clk uart1_iclk = { - .name = "uart1_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, -}; - -static struct clk uart2_fclk = { - .name = "uart2_fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_48m_clk, -}; - -static struct clk uart2_iclk = { - .name = "uart2_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, -}; - -static struct clk uart3_fclk = { - .name = "uart3_fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_48m_clk, -}; - -static struct clk uart3_iclk = { - .name = "uart3_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, -}; - -static struct clk mpu_fclk = { - .name = "mpu_fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_clk, -}; - -static struct clk mpu_iclk = { - .name = "mpu_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_clk, -}; - -static struct clk int_m_fclk = { - .name = "int_m_fclk", - .alias = "mpu_intc_fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_clk, -}; - -static struct clk int_m_iclk = { - .name = "int_m_iclk", - .alias = "mpu_intc_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_clk, -}; - -static struct clk core_gpt2_clk = { - .name = "core_gpt2_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt3_clk = { - .name = "core_gpt3_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt4_clk = { - .name = "core_gpt4_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt5_clk = { - .name = "core_gpt5_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt6_clk = { - .name = "core_gpt6_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt7_clk = { - .name = "core_gpt7_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt8_clk = { - .name = "core_gpt8_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt9_clk = { - .name = "core_gpt9_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt10_clk = { - .name = "core_gpt10_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt11_clk = { - .name = "core_gpt11_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt12_clk = { - .name = "core_gpt12_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk mcbsp1_clk = { - .name = "mcbsp1_cg", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .divisor = 2, - .parent = &func_96m_clk, -}; - -static struct clk mcbsp2_clk = { - .name = "mcbsp2_cg", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .divisor = 2, - .parent = &func_96m_clk, -}; - -static struct clk emul_clk = { - .name = "emul_ck", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_54m_clk, -}; - -static struct clk sdma_fclk = { - .name = "sdma_fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &l3_clk, -}; - -static struct clk sdma_iclk = { - .name = "sdma_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l3_iclk, /* core_l4_iclk for the configuration port */ -}; - -static struct clk i2c1_fclk = { - .name = "i2c1.fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_12m_clk, - .divisor = 1, -}; - -static struct clk i2c1_iclk = { - .name = "i2c1.iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, -}; - -static struct clk i2c2_fclk = { - .name = "i2c2.fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_12m_clk, - .divisor = 1, -}; - -static struct clk i2c2_iclk = { - .name = "i2c2.iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, -}; - -static struct clk gpio_dbclk[5] = { - { - .name = "gpio1_dbclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &wu_32k_clk, - }, { - .name = "gpio2_dbclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &wu_32k_clk, - }, { - .name = "gpio3_dbclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &wu_32k_clk, - }, { - .name = "gpio4_dbclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &wu_32k_clk, - }, { - .name = "gpio5_dbclk", - .flags = CLOCK_IN_OMAP243X, - .parent = &wu_32k_clk, - }, -}; - -static struct clk gpio_iclk = { - .name = "gpio_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &wu_l4_iclk, -}; - -static struct clk mmc_fck = { - .name = "mmc_fclk", - .flags = CLOCK_IN_OMAP242X, - .parent = &func_96m_clk, -}; - -static struct clk mmc_ick = { - .name = "mmc_iclk", - .flags = CLOCK_IN_OMAP242X, - .parent = &core_l4_iclk, -}; - -static struct clk spi_fclk[3] = { - { - .name = "spi1_fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_48m_clk, - }, { - .name = "spi2_fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_48m_clk, - }, { - .name = "spi3_fclk", - .flags = CLOCK_IN_OMAP243X, - .parent = &func_48m_clk, - }, -}; - -static struct clk dss_clk[2] = { - { - .name = "dss_clk1", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_clk, - }, { - .name = "dss_clk2", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, - }, -}; - -static struct clk dss_54m_clk = { - .name = "dss_54m_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_54m_clk, -}; - -static struct clk dss_l3_iclk = { - .name = "dss_l3_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l3_iclk, -}; - -static struct clk dss_l4_iclk = { - .name = "dss_l4_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, -}; - -static struct clk spi_iclk[3] = { - { - .name = "spi1_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, - }, { - .name = "spi2_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, - }, { - .name = "spi3_iclk", - .flags = CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, - }, -}; - -static struct clk omapctrl_clk = { - .name = "omapctrl_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - /* XXX Should be in WKUP domain */ - .parent = &core_l4_iclk, -}; - -static struct clk *onchip_clks[] = { - /* OMAP 1 */ - - /* non-ULPD clocks */ - &xtal_osc12m, - &xtal_osc32k, - &ck_ref, - &dpll1, - &dpll2, - &dpll3, - &dpll4, - &apll, - &ck_48m, - /* CK_GEN1 clocks */ - &clkm1, - &ck_dpll1out, - &sossi_ck, - &arm_ck, - &armper_ck, - &arm_gpio_ck, - &armxor_ck, - &armtim_ck, - &armwdt_ck, - &arminth_ck15xx, &arminth_ck16xx, - /* CK_GEN2 clocks */ - &clkm2, - &dsp_ck, - &dspmmu_ck, - &dspper_ck, - &dspxor_ck, - &dsptim_ck, - /* CK_GEN3 clocks */ - &clkm3, - &tc_ck, - &tipb_ck, - &l3_ocpi_ck, - &tc1_ck, - &tc2_ck, - &dma_ck, - &dma_lcdfree_ck, - &api_ck, - &lb_ck, - &lbfree_ck, - &hsab_ck, - &rhea1_ck, - &rhea2_ck, - &lcd_ck_16xx, - &lcd_ck_1510, - /* ULPD clocks */ - &uart1_1510, - &uart1_16xx, - &uart2_ck, - &uart3_1510, - &uart3_16xx, - &usb_clk0, - &usb_hhc_ck1510, &usb_hhc_ck16xx, - &mclk_1510, &mclk_16xx, &mclk_310, - &bclk_1510, &bclk_16xx, &bclk_310, - &mmc1_ck, - &mmc2_ck, - &cam_mclk, - &cam_exclk, - &cam_lclk, - &clk32k, - &usb_w2fc_mclk, - /* Virtual clocks */ - &i2c_fck, - &i2c_ick, - - /* OMAP 2 */ - - &ref_clk, - &apll_96m, - &apll_54m, - &sys_clk, - &sleep_clk, - &dpll_ck, - &dpll_x2_ck, - &wdt1_sys_clk, - &func_96m_clk, - &func_48m_clk, - &func_12m_clk, - &func_54m_clk, - &sys_clkout, - &sys_clkout2, - &core_clk, - &l3_clk, - &core_l4_iclk, - &wu_l4_iclk, - &core_l3_iclk, - &core_l4_usb_clk, - &wu_gpt1_clk, - &wu_32k_clk, - &uart1_fclk, - &uart1_iclk, - &uart2_fclk, - &uart2_iclk, - &uart3_fclk, - &uart3_iclk, - &mpu_fclk, - &mpu_iclk, - &int_m_fclk, - &int_m_iclk, - &core_gpt2_clk, - &core_gpt3_clk, - &core_gpt4_clk, - &core_gpt5_clk, - &core_gpt6_clk, - &core_gpt7_clk, - &core_gpt8_clk, - &core_gpt9_clk, - &core_gpt10_clk, - &core_gpt11_clk, - &core_gpt12_clk, - &mcbsp1_clk, - &mcbsp2_clk, - &emul_clk, - &sdma_fclk, - &sdma_iclk, - &i2c1_fclk, - &i2c1_iclk, - &i2c2_fclk, - &i2c2_iclk, - &gpio_dbclk[0], - &gpio_dbclk[1], - &gpio_dbclk[2], - &gpio_dbclk[3], - &gpio_iclk, - &mmc_fck, - &mmc_ick, - &spi_fclk[0], - &spi_iclk[0], - &spi_fclk[1], - &spi_iclk[1], - &spi_fclk[2], - &spi_iclk[2], - &dss_clk[0], - &dss_clk[1], - &dss_54m_clk, - &dss_l3_iclk, - &dss_l4_iclk, - &omapctrl_clk, - - NULL -}; - -void omap_clk_adduser(struct clk *clk, qemu_irq user) -{ - qemu_irq *i; - - for (i = clk->users; *i; i ++); - *i = user; -} - -struct clk *omap_findclk(struct omap_mpu_state_s *mpu, const char *name) -{ - struct clk *i; - - for (i = mpu->clks; i->name; i ++) - if (!strcmp(i->name, name) || (i->alias && !strcmp(i->alias, name))) - return i; - hw_error("%s: %s not found\n", __FUNCTION__, name); -} - -void omap_clk_get(struct clk *clk) -{ - clk->usecount ++; -} - -void omap_clk_put(struct clk *clk) -{ - if (!(clk->usecount --)) - hw_error("%s: %s is not in use\n", __FUNCTION__, clk->name); -} - -static void omap_clk_update(struct clk *clk) -{ - int parent, running; - qemu_irq *user; - struct clk *i; - - if (clk->parent) - parent = clk->parent->running; - else - parent = 1; - - running = parent && (clk->enabled || - ((clk->flags & ALWAYS_ENABLED) && clk->usecount)); - if (clk->running != running) { - clk->running = running; - for (user = clk->users; *user; user ++) - qemu_set_irq(*user, running); - for (i = clk->child1; i; i = i->sibling) - omap_clk_update(i); - } -} - -static void omap_clk_rate_update_full(struct clk *clk, unsigned long int rate, - unsigned long int div, unsigned long int mult) -{ - struct clk *i; - qemu_irq *user; - - clk->rate = muldiv64(rate, mult, div); - if (clk->running) - for (user = clk->users; *user; user ++) - qemu_irq_raise(*user); - for (i = clk->child1; i; i = i->sibling) - omap_clk_rate_update_full(i, rate, - div * i->divisor, mult * i->multiplier); -} - -static void omap_clk_rate_update(struct clk *clk) -{ - struct clk *i; - unsigned long int div, mult = div = 1; - - for (i = clk; i->parent; i = i->parent) { - div *= i->divisor; - mult *= i->multiplier; - } - - omap_clk_rate_update_full(clk, i->rate, div, mult); -} - -void omap_clk_reparent(struct clk *clk, struct clk *parent) -{ - struct clk **p; - - if (clk->parent) { - for (p = &clk->parent->child1; *p != clk; p = &(*p)->sibling); - *p = clk->sibling; - } - - clk->parent = parent; - if (parent) { - clk->sibling = parent->child1; - parent->child1 = clk; - omap_clk_update(clk); - omap_clk_rate_update(clk); - } else - clk->sibling = NULL; -} - -void omap_clk_onoff(struct clk *clk, int on) -{ - clk->enabled = on; - omap_clk_update(clk); -} - -void omap_clk_canidle(struct clk *clk, int can) -{ - if (can) - omap_clk_put(clk); - else - omap_clk_get(clk); -} - -void omap_clk_setrate(struct clk *clk, int divide, int multiply) -{ - clk->divisor = divide; - clk->multiplier = multiply; - omap_clk_rate_update(clk); -} - -int64_t omap_clk_getrate(omap_clk clk) -{ - return clk->rate; -} - -void omap_clk_init(struct omap_mpu_state_s *mpu) -{ - struct clk **i, *j, *k; - int count; - int flag; - - if (cpu_is_omap310(mpu)) - flag = CLOCK_IN_OMAP310; - else if (cpu_is_omap1510(mpu)) - flag = CLOCK_IN_OMAP1510; - else if (cpu_is_omap2410(mpu) || cpu_is_omap2420(mpu)) - flag = CLOCK_IN_OMAP242X; - else if (cpu_is_omap2430(mpu)) - flag = CLOCK_IN_OMAP243X; - else if (cpu_is_omap3430(mpu)) - flag = CLOCK_IN_OMAP243X; - else - return; - - for (i = onchip_clks, count = 0; *i; i ++) - if ((*i)->flags & flag) - count ++; - 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)); - for (k = mpu->clks; k < j; k ++) - if (j->parent && !strcmp(j->parent->name, k->name)) { - j->parent = k; - j->sibling = k->child1; - k->child1 = j; - } else if (k->parent && !strcmp(k->parent->name, j->name)) { - k->parent = j; - k->sibling = j->child1; - j->child1 = k; - } - j->divisor = j->divisor ?: 1; - j->multiplier = j->multiplier ?: 1; - j ++; - } - for (j = mpu->clks; count --; j ++) { - omap_clk_update(j); - omap_clk_rate_update(j); - } -} diff --git a/qemu/hw/misc/omap_gpmc.c b/qemu/hw/misc/omap_gpmc.c deleted file mode 100644 index 67d8e2f02..000000000 --- a/qemu/hw/misc/omap_gpmc.c +++ /dev/null @@ -1,897 +0,0 @@ -/* - * TI OMAP general purpose memory controller emulation. - * - * Copyright (C) 2007-2009 Nokia Corporation - * Original code written by Andrzej Zaborowski - * Enhancements for OMAP3 and NAND support written by Juha Riihimäki - * - * 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) any later version 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 . - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/block/flash.h" -#include "hw/arm/omap.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" - -/* General-Purpose Memory Controller */ -struct omap_gpmc_s { - qemu_irq irq; - qemu_irq drq; - MemoryRegion iomem; - int accept_256; - - uint8_t revision; - uint8_t sysconfig; - uint16_t irqst; - uint16_t irqen; - uint16_t lastirq; - uint16_t timeout; - uint16_t config; - struct omap_gpmc_cs_file_s { - uint32_t config[7]; - MemoryRegion *iomem; - MemoryRegion container; - MemoryRegion nandiomem; - DeviceState *dev; - } cs_file[8]; - int ecc_cs; - int ecc_ptr; - uint32_t ecc_cfg; - ECCState ecc[9]; - struct prefetch { - uint32_t config1; /* GPMC_PREFETCH_CONFIG1 */ - uint32_t transfercount; /* GPMC_PREFETCH_CONFIG2:TRANSFERCOUNT */ - int startengine; /* GPMC_PREFETCH_CONTROL:STARTENGINE */ - int fifopointer; /* GPMC_PREFETCH_STATUS:FIFOPOINTER */ - int count; /* GPMC_PREFETCH_STATUS:COUNTVALUE */ - MemoryRegion iomem; - uint8_t fifo[64]; - } prefetch; -}; - -#define OMAP_GPMC_8BIT 0 -#define OMAP_GPMC_16BIT 1 -#define OMAP_GPMC_NOR 0 -#define OMAP_GPMC_NAND 2 - -static int omap_gpmc_devtype(struct omap_gpmc_cs_file_s *f) -{ - return (f->config[0] >> 10) & 3; -} - -static int omap_gpmc_devsize(struct omap_gpmc_cs_file_s *f) -{ - /* devsize field is really 2 bits but we ignore the high - * bit to ensure consistent behaviour if the guest sets - * it (values 2 and 3 are reserved in the TRM) - */ - return (f->config[0] >> 12) & 1; -} - -/* Extract the chip-select value from the prefetch config1 register */ -static int prefetch_cs(uint32_t config1) -{ - return (config1 >> 24) & 7; -} - -static int prefetch_threshold(uint32_t config1) -{ - return (config1 >> 8) & 0x7f; -} - -static void omap_gpmc_int_update(struct omap_gpmc_s *s) -{ - /* The TRM is a bit unclear, but it seems to say that - * the TERMINALCOUNTSTATUS bit is set only on the - * transition when the prefetch engine goes from - * active to inactive, whereas the FIFOEVENTSTATUS - * bit is held high as long as the fifo has at - * least THRESHOLD bytes available. - * So we do the latter here, but TERMINALCOUNTSTATUS - * is set elsewhere. - */ - if (s->prefetch.fifopointer >= prefetch_threshold(s->prefetch.config1)) { - s->irqst |= 1; - } - if ((s->irqen & s->irqst) != s->lastirq) { - s->lastirq = s->irqen & s->irqst; - qemu_set_irq(s->irq, s->lastirq); - } -} - -static void omap_gpmc_dma_update(struct omap_gpmc_s *s, int value) -{ - if (s->prefetch.config1 & 4) { - qemu_set_irq(s->drq, value); - } -} - -/* Access functions for when a NAND-like device is mapped into memory: - * all addresses in the region behave like accesses to the relevant - * GPMC_NAND_DATA_i register (which is actually implemented to call these) - */ -static uint64_t omap_nand_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_gpmc_cs_file_s *f = (struct omap_gpmc_cs_file_s *)opaque; - uint64_t v; - nand_setpins(f->dev, 0, 0, 0, 1, 0); - switch (omap_gpmc_devsize(f)) { - case OMAP_GPMC_8BIT: - v = nand_getio(f->dev); - if (size == 1) { - return v; - } - v |= (nand_getio(f->dev) << 8); - if (size == 2) { - return v; - } - v |= (nand_getio(f->dev) << 16); - v |= (nand_getio(f->dev) << 24); - return v; - case OMAP_GPMC_16BIT: - v = nand_getio(f->dev); - if (size == 1) { - /* 8 bit read from 16 bit device : probably a guest bug */ - return v & 0xff; - } - if (size == 2) { - return v; - } - v |= (nand_getio(f->dev) << 16); - return v; - default: - abort(); - } -} - -static void omap_nand_setio(DeviceState *dev, uint64_t value, - int nandsize, int size) -{ - /* Write the specified value to the NAND device, respecting - * both size of the NAND device and size of the write access. - */ - switch (nandsize) { - case OMAP_GPMC_8BIT: - switch (size) { - case 1: - nand_setio(dev, value & 0xff); - break; - case 2: - nand_setio(dev, value & 0xff); - nand_setio(dev, (value >> 8) & 0xff); - break; - case 4: - default: - nand_setio(dev, value & 0xff); - nand_setio(dev, (value >> 8) & 0xff); - nand_setio(dev, (value >> 16) & 0xff); - nand_setio(dev, (value >> 24) & 0xff); - break; - } - break; - case OMAP_GPMC_16BIT: - switch (size) { - case 1: - /* writing to a 16bit device with 8bit access is probably a guest - * bug; pass the value through anyway. - */ - case 2: - nand_setio(dev, value & 0xffff); - break; - case 4: - default: - nand_setio(dev, value & 0xffff); - nand_setio(dev, (value >> 16) & 0xffff); - break; - } - break; - } -} - -static void omap_nand_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_gpmc_cs_file_s *f = (struct omap_gpmc_cs_file_s *)opaque; - nand_setpins(f->dev, 0, 0, 0, 1, 0); - omap_nand_setio(f->dev, value, omap_gpmc_devsize(f), size); -} - -static const MemoryRegionOps omap_nand_ops = { - .read = omap_nand_read, - .write = omap_nand_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void fill_prefetch_fifo(struct omap_gpmc_s *s) -{ - /* Fill the prefetch FIFO by reading data from NAND. - * We do this synchronously, unlike the hardware which - * will do this asynchronously. We refill when the - * FIFO has THRESHOLD bytes free, and we always refill - * as much data as possible starting at the top end - * of the FIFO. - * (We have to refill at THRESHOLD rather than waiting - * for the FIFO to empty to allow for the case where - * the FIFO size isn't an exact multiple of THRESHOLD - * and we're doing DMA transfers.) - * This means we never need to handle wrap-around in - * the fifo-reading code, and the next byte of data - * to read is always fifo[63 - fifopointer]. - */ - int fptr; - int cs = prefetch_cs(s->prefetch.config1); - int is16bit = (((s->cs_file[cs].config[0] >> 12) & 3) != 0); - int bytes; - /* Don't believe the bit of the OMAP TRM that says that COUNTVALUE - * and TRANSFERCOUNT are in units of 16 bit words for 16 bit NAND. - * Instead believe the bit that says it is always a byte count. - */ - bytes = 64 - s->prefetch.fifopointer; - if (bytes > s->prefetch.count) { - bytes = s->prefetch.count; - } - if (is16bit) { - bytes &= ~1; - } - - s->prefetch.count -= bytes; - s->prefetch.fifopointer += bytes; - fptr = 64 - s->prefetch.fifopointer; - /* Move the existing data in the FIFO so it sits just - * before what we're about to read in - */ - while (fptr < (64 - bytes)) { - s->prefetch.fifo[fptr] = s->prefetch.fifo[fptr + bytes]; - fptr++; - } - while (fptr < 64) { - if (is16bit) { - uint32_t v = omap_nand_read(&s->cs_file[cs], 0, 2); - s->prefetch.fifo[fptr++] = v & 0xff; - s->prefetch.fifo[fptr++] = (v >> 8) & 0xff; - } else { - s->prefetch.fifo[fptr++] = omap_nand_read(&s->cs_file[cs], 0, 1); - } - } - if (s->prefetch.startengine && (s->prefetch.count == 0)) { - /* This was the final transfer: raise TERMINALCOUNTSTATUS */ - s->irqst |= 2; - s->prefetch.startengine = 0; - } - /* If there are any bytes in the FIFO at this point then - * we must raise a DMA request (either this is a final part - * transfer, or we filled the FIFO in which case we certainly - * have THRESHOLD bytes available) - */ - if (s->prefetch.fifopointer != 0) { - omap_gpmc_dma_update(s, 1); - } - omap_gpmc_int_update(s); -} - -/* Access functions for a NAND-like device when the prefetch/postwrite - * engine is enabled -- all addresses in the region behave alike: - * data is read or written to the FIFO. - */ -static uint64_t omap_gpmc_prefetch_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; - uint32_t data; - if (s->prefetch.config1 & 1) { - /* The TRM doesn't define the behaviour if you read from the - * FIFO when the prefetch engine is in write mode. We choose - * to always return zero. - */ - return 0; - } - /* Note that trying to read an empty fifo repeats the last byte */ - if (s->prefetch.fifopointer) { - s->prefetch.fifopointer--; - } - data = s->prefetch.fifo[63 - s->prefetch.fifopointer]; - if (s->prefetch.fifopointer == - (64 - prefetch_threshold(s->prefetch.config1))) { - /* We've drained THRESHOLD bytes now. So deassert the - * DMA request, then refill the FIFO (which will probably - * assert it again.) - */ - omap_gpmc_dma_update(s, 0); - fill_prefetch_fifo(s); - } - omap_gpmc_int_update(s); - return data; -} - -static void omap_gpmc_prefetch_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; - int cs = prefetch_cs(s->prefetch.config1); - if ((s->prefetch.config1 & 1) == 0) { - /* The TRM doesn't define the behaviour of writing to the - * FIFO when the prefetch engine is in read mode. We - * choose to ignore the write. - */ - return; - } - if (s->prefetch.count == 0) { - /* The TRM doesn't define the behaviour of writing to the - * FIFO if the transfer is complete. We choose to ignore. - */ - return; - } - /* The only reason we do any data buffering in postwrite - * mode is if we are talking to a 16 bit NAND device, in - * which case we need to buffer the first byte of the - * 16 bit word until the other byte arrives. - */ - int is16bit = (((s->cs_file[cs].config[0] >> 12) & 3) != 0); - if (is16bit) { - /* fifopointer alternates between 64 (waiting for first - * byte of word) and 63 (waiting for second byte) - */ - if (s->prefetch.fifopointer == 64) { - s->prefetch.fifo[0] = value; - s->prefetch.fifopointer--; - } else { - value = (value << 8) | s->prefetch.fifo[0]; - omap_nand_write(&s->cs_file[cs], 0, value, 2); - s->prefetch.count--; - s->prefetch.fifopointer = 64; - } - } else { - /* Just write the byte : fifopointer remains 64 at all times */ - omap_nand_write(&s->cs_file[cs], 0, value, 1); - s->prefetch.count--; - } - if (s->prefetch.count == 0) { - /* Final transfer: raise TERMINALCOUNTSTATUS */ - s->irqst |= 2; - s->prefetch.startengine = 0; - } - omap_gpmc_int_update(s); -} - -static const MemoryRegionOps omap_prefetch_ops = { - .read = omap_gpmc_prefetch_read, - .write = omap_gpmc_prefetch_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl.min_access_size = 1, - .impl.max_access_size = 1, -}; - -static MemoryRegion *omap_gpmc_cs_memregion(struct omap_gpmc_s *s, int cs) -{ - /* Return the MemoryRegion* to map/unmap for this chipselect */ - struct omap_gpmc_cs_file_s *f = &s->cs_file[cs]; - if (omap_gpmc_devtype(f) == OMAP_GPMC_NOR) { - return f->iomem; - } - if ((s->prefetch.config1 & 0x80) && - (prefetch_cs(s->prefetch.config1) == cs)) { - /* The prefetch engine is enabled for this CS: map the FIFO */ - return &s->prefetch.iomem; - } - return &f->nandiomem; -} - -static void omap_gpmc_cs_map(struct omap_gpmc_s *s, int cs) -{ - struct omap_gpmc_cs_file_s *f = &s->cs_file[cs]; - uint32_t mask = (f->config[6] >> 8) & 0xf; - uint32_t base = f->config[6] & 0x3f; - uint32_t size; - - if (!f->iomem && !f->dev) { - return; - } - - if (!(f->config[6] & (1 << 6))) { - /* Do nothing unless CSVALID */ - return; - } - - /* TODO: check for overlapping regions and report access errors */ - if (mask != 0x8 && mask != 0xc && mask != 0xe && mask != 0xf - && !(s->accept_256 && !mask)) { - fprintf(stderr, "%s: invalid chip-select mask address (0x%x)\n", - __func__, mask); - } - - base <<= 24; - size = (0x0fffffff & ~(mask << 24)) + 1; - /* TODO: rather than setting the size of the mapping (which should be - * constant), the mask should cause wrapping of the address space, so - * that the same memory becomes accessible at every size bytes - * starting from base. */ - memory_region_init(&f->container, NULL, "omap-gpmc-file", size); - memory_region_add_subregion(&f->container, 0, - omap_gpmc_cs_memregion(s, cs)); - memory_region_add_subregion(get_system_memory(), base, - &f->container); -} - -static void omap_gpmc_cs_unmap(struct omap_gpmc_s *s, int cs) -{ - struct omap_gpmc_cs_file_s *f = &s->cs_file[cs]; - if (!(f->config[6] & (1 << 6))) { - /* Do nothing unless CSVALID */ - return; - } - if (!f->iomem && !f->dev) { - return; - } - memory_region_del_subregion(get_system_memory(), &f->container); - memory_region_del_subregion(&f->container, omap_gpmc_cs_memregion(s, cs)); - object_unparent(OBJECT(&f->container)); -} - -void omap_gpmc_reset(struct omap_gpmc_s *s) -{ - int i; - - s->sysconfig = 0; - s->irqst = 0; - s->irqen = 0; - omap_gpmc_int_update(s); - for (i = 0; i < 8; i++) { - /* This has to happen before we change any of the config - * used to determine which memory regions are mapped or unmapped. - */ - omap_gpmc_cs_unmap(s, i); - } - s->timeout = 0; - s->config = 0xa00; - s->prefetch.config1 = 0x00004000; - s->prefetch.transfercount = 0x00000000; - s->prefetch.startengine = 0; - s->prefetch.fifopointer = 0; - s->prefetch.count = 0; - for (i = 0; i < 8; i ++) { - s->cs_file[i].config[1] = 0x101001; - s->cs_file[i].config[2] = 0x020201; - s->cs_file[i].config[3] = 0x10031003; - s->cs_file[i].config[4] = 0x10f1111; - s->cs_file[i].config[5] = 0; - s->cs_file[i].config[6] = 0xf00; - /* In theory we could probe attached devices for some CFG1 - * bits here, but we just retain them across resets as they - * were set initially by omap_gpmc_attach(). - */ - if (i == 0) { - s->cs_file[i].config[0] &= 0x00433e00; - s->cs_file[i].config[6] |= 1 << 6; /* CSVALID */ - omap_gpmc_cs_map(s, i); - } else { - s->cs_file[i].config[0] &= 0x00403c00; - } - } - s->ecc_cs = 0; - s->ecc_ptr = 0; - s->ecc_cfg = 0x3fcff000; - for (i = 0; i < 9; i ++) - ecc_reset(&s->ecc[i]); -} - -static int gpmc_wordaccess_only(hwaddr addr) -{ - /* Return true if the register offset is to a register that - * only permits word width accesses. - * Non-word accesses are only OK for GPMC_NAND_DATA/ADDRESS/COMMAND - * for any chipselect. - */ - if (addr >= 0x60 && addr <= 0x1d4) { - int cs = (addr - 0x60) / 0x30; - addr -= cs * 0x30; - if (addr >= 0x7c && addr < 0x88) { - /* GPMC_NAND_COMMAND, GPMC_NAND_ADDRESS, GPMC_NAND_DATA */ - return 0; - } - } - return 1; -} - -static uint64_t omap_gpmc_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; - int cs; - struct omap_gpmc_cs_file_s *f; - - if (size != 4 && gpmc_wordaccess_only(addr)) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x000: /* GPMC_REVISION */ - return s->revision; - - case 0x010: /* GPMC_SYSCONFIG */ - return s->sysconfig; - - case 0x014: /* GPMC_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x018: /* GPMC_IRQSTATUS */ - return s->irqst; - - case 0x01c: /* GPMC_IRQENABLE */ - return s->irqen; - - case 0x040: /* GPMC_TIMEOUT_CONTROL */ - return s->timeout; - - case 0x044: /* GPMC_ERR_ADDRESS */ - case 0x048: /* GPMC_ERR_TYPE */ - return 0; - - case 0x050: /* GPMC_CONFIG */ - return s->config; - - case 0x054: /* GPMC_STATUS */ - return 0x001; - - case 0x060 ... 0x1d4: - cs = (addr - 0x060) / 0x30; - addr -= cs * 0x30; - f = s->cs_file + cs; - switch (addr) { - case 0x60: /* GPMC_CONFIG1 */ - return f->config[0]; - case 0x64: /* GPMC_CONFIG2 */ - return f->config[1]; - case 0x68: /* GPMC_CONFIG3 */ - return f->config[2]; - case 0x6c: /* GPMC_CONFIG4 */ - return f->config[3]; - case 0x70: /* GPMC_CONFIG5 */ - return f->config[4]; - case 0x74: /* GPMC_CONFIG6 */ - return f->config[5]; - case 0x78: /* GPMC_CONFIG7 */ - return f->config[6]; - case 0x84 ... 0x87: /* GPMC_NAND_DATA */ - if (omap_gpmc_devtype(f) == OMAP_GPMC_NAND) { - return omap_nand_read(f, 0, size); - } - return 0; - } - break; - - case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */ - return s->prefetch.config1; - case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */ - return s->prefetch.transfercount; - case 0x1ec: /* GPMC_PREFETCH_CONTROL */ - return s->prefetch.startengine; - case 0x1f0: /* GPMC_PREFETCH_STATUS */ - /* NB: The OMAP3 TRM is inconsistent about whether the GPMC - * FIFOTHRESHOLDSTATUS bit should be set when - * FIFOPOINTER > FIFOTHRESHOLD or when it is >= FIFOTHRESHOLD. - * Apparently the underlying functional spec from which the TRM was - * created states that the behaviour is ">=", and this also - * makes more conceptual sense. - */ - return (s->prefetch.fifopointer << 24) | - ((s->prefetch.fifopointer >= - ((s->prefetch.config1 >> 8) & 0x7f) ? 1 : 0) << 16) | - s->prefetch.count; - - case 0x1f4: /* GPMC_ECC_CONFIG */ - return s->ecc_cs; - case 0x1f8: /* GPMC_ECC_CONTROL */ - return s->ecc_ptr; - case 0x1fc: /* GPMC_ECC_SIZE_CONFIG */ - return s->ecc_cfg; - case 0x200 ... 0x220: /* GPMC_ECC_RESULT */ - cs = (addr & 0x1f) >> 2; - /* TODO: check correctness */ - return - ((s->ecc[cs].cp & 0x07) << 0) | - ((s->ecc[cs].cp & 0x38) << 13) | - ((s->ecc[cs].lp[0] & 0x1ff) << 3) | - ((s->ecc[cs].lp[1] & 0x1ff) << 19); - - case 0x230: /* GPMC_TESTMODE_CTRL */ - return 0; - case 0x234: /* GPMC_PSA_LSB */ - case 0x238: /* GPMC_PSA_MSB */ - return 0x00000000; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_gpmc_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; - int cs; - struct omap_gpmc_cs_file_s *f; - - if (size != 4 && gpmc_wordaccess_only(addr)) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x000: /* GPMC_REVISION */ - case 0x014: /* GPMC_SYSSTATUS */ - case 0x054: /* GPMC_STATUS */ - case 0x1f0: /* GPMC_PREFETCH_STATUS */ - case 0x200 ... 0x220: /* GPMC_ECC_RESULT */ - case 0x234: /* GPMC_PSA_LSB */ - case 0x238: /* GPMC_PSA_MSB */ - OMAP_RO_REG(addr); - break; - - case 0x010: /* GPMC_SYSCONFIG */ - if ((value >> 3) == 0x3) - fprintf(stderr, "%s: bad SDRAM idle mode %"PRIi64"\n", - __FUNCTION__, value >> 3); - if (value & 2) - omap_gpmc_reset(s); - s->sysconfig = value & 0x19; - break; - - case 0x018: /* GPMC_IRQSTATUS */ - s->irqst &= ~value; - omap_gpmc_int_update(s); - break; - - case 0x01c: /* GPMC_IRQENABLE */ - s->irqen = value & 0xf03; - omap_gpmc_int_update(s); - break; - - case 0x040: /* GPMC_TIMEOUT_CONTROL */ - s->timeout = value & 0x1ff1; - break; - - case 0x044: /* GPMC_ERR_ADDRESS */ - case 0x048: /* GPMC_ERR_TYPE */ - break; - - case 0x050: /* GPMC_CONFIG */ - s->config = value & 0xf13; - break; - - case 0x060 ... 0x1d4: - cs = (addr - 0x060) / 0x30; - addr -= cs * 0x30; - f = s->cs_file + cs; - switch (addr) { - case 0x60: /* GPMC_CONFIG1 */ - f->config[0] = value & 0xffef3e13; - break; - case 0x64: /* GPMC_CONFIG2 */ - f->config[1] = value & 0x001f1f8f; - break; - case 0x68: /* GPMC_CONFIG3 */ - f->config[2] = value & 0x001f1f8f; - break; - case 0x6c: /* GPMC_CONFIG4 */ - f->config[3] = value & 0x1f8f1f8f; - break; - case 0x70: /* GPMC_CONFIG5 */ - f->config[4] = value & 0x0f1f1f1f; - break; - case 0x74: /* GPMC_CONFIG6 */ - f->config[5] = value & 0x00000fcf; - break; - case 0x78: /* GPMC_CONFIG7 */ - if ((f->config[6] ^ value) & 0xf7f) { - omap_gpmc_cs_unmap(s, cs); - f->config[6] = value & 0x00000f7f; - omap_gpmc_cs_map(s, cs); - } - break; - case 0x7c ... 0x7f: /* GPMC_NAND_COMMAND */ - if (omap_gpmc_devtype(f) == OMAP_GPMC_NAND) { - nand_setpins(f->dev, 1, 0, 0, 1, 0); /* CLE */ - omap_nand_setio(f->dev, value, omap_gpmc_devsize(f), size); - } - break; - case 0x80 ... 0x83: /* GPMC_NAND_ADDRESS */ - if (omap_gpmc_devtype(f) == OMAP_GPMC_NAND) { - nand_setpins(f->dev, 0, 1, 0, 1, 0); /* ALE */ - omap_nand_setio(f->dev, value, omap_gpmc_devsize(f), size); - } - break; - case 0x84 ... 0x87: /* GPMC_NAND_DATA */ - if (omap_gpmc_devtype(f) == OMAP_GPMC_NAND) { - omap_nand_write(f, 0, value, size); - } - break; - default: - goto bad_reg; - } - break; - - case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */ - if (!s->prefetch.startengine) { - uint32_t newconfig1 = value & 0x7f8f7fbf; - uint32_t changed; - changed = newconfig1 ^ s->prefetch.config1; - if (changed & (0x80 | 0x7000000)) { - /* Turning the engine on or off, or mapping it somewhere else. - * cs_map() and cs_unmap() check the prefetch config and - * overall CSVALID bits, so it is sufficient to unmap-and-map - * both the old cs and the new one. Note that we adhere to - * the "unmap/change config/map" order (and not unmap twice - * if newcs == oldcs), otherwise we'll try to delete the wrong - * memory region. - */ - int oldcs = prefetch_cs(s->prefetch.config1); - int newcs = prefetch_cs(newconfig1); - omap_gpmc_cs_unmap(s, oldcs); - if (oldcs != newcs) { - omap_gpmc_cs_unmap(s, newcs); - } - s->prefetch.config1 = newconfig1; - omap_gpmc_cs_map(s, oldcs); - if (oldcs != newcs) { - omap_gpmc_cs_map(s, newcs); - } - } else { - s->prefetch.config1 = newconfig1; - } - } - break; - - case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */ - if (!s->prefetch.startengine) { - s->prefetch.transfercount = value & 0x3fff; - } - break; - - case 0x1ec: /* GPMC_PREFETCH_CONTROL */ - if (s->prefetch.startengine != (value & 1)) { - s->prefetch.startengine = value & 1; - if (s->prefetch.startengine) { - /* Prefetch engine start */ - s->prefetch.count = s->prefetch.transfercount; - if (s->prefetch.config1 & 1) { - /* Write */ - s->prefetch.fifopointer = 64; - } else { - /* Read */ - s->prefetch.fifopointer = 0; - fill_prefetch_fifo(s); - } - } else { - /* Prefetch engine forcibly stopped. The TRM - * doesn't define the behaviour if you do this. - * We clear the prefetch count, which means that - * we permit no more writes, and don't read any - * more data from NAND. The CPU can still drain - * the FIFO of unread data. - */ - s->prefetch.count = 0; - } - omap_gpmc_int_update(s); - } - break; - - case 0x1f4: /* GPMC_ECC_CONFIG */ - s->ecc_cs = 0x8f; - break; - case 0x1f8: /* GPMC_ECC_CONTROL */ - if (value & (1 << 8)) - for (cs = 0; cs < 9; cs ++) - ecc_reset(&s->ecc[cs]); - s->ecc_ptr = value & 0xf; - if (s->ecc_ptr == 0 || s->ecc_ptr > 9) { - s->ecc_ptr = 0; - s->ecc_cs &= ~1; - } - break; - case 0x1fc: /* GPMC_ECC_SIZE_CONFIG */ - s->ecc_cfg = value & 0x3fcff1ff; - break; - case 0x230: /* GPMC_TESTMODE_CTRL */ - if (value & 7) - fprintf(stderr, "%s: test mode enable attempt\n", __FUNCTION__); - break; - - default: - bad_reg: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_gpmc_ops = { - .read = omap_gpmc_read, - .write = omap_gpmc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_gpmc_s *omap_gpmc_init(struct omap_mpu_state_s *mpu, - hwaddr base, - qemu_irq irq, qemu_irq drq) -{ - int cs; - 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); - - s->irq = irq; - s->drq = drq; - s->accept_256 = cpu_is_omap3630(mpu); - s->revision = cpu_class_omap3(mpu) ? 0x50 : 0x20; - s->lastirq = 0; - omap_gpmc_reset(s); - - /* We have to register a different IO memory handler for each - * chip select region in case a NAND device is mapped there. We - * make the region the worst-case size of 256MB and rely on the - * container memory region in cs_map to chop it down to the actual - * guest-requested size. - */ - for (cs = 0; cs < 8; cs++) { - memory_region_init_io(&s->cs_file[cs].nandiomem, NULL, - &omap_nand_ops, - &s->cs_file[cs], - "omap-nand", - 256 * 1024 * 1024); - } - - memory_region_init_io(&s->prefetch.iomem, NULL, &omap_prefetch_ops, s, - "omap-gpmc-prefetch", 256 * 1024 * 1024); - return s; -} - -void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, MemoryRegion *iomem) -{ - struct omap_gpmc_cs_file_s *f; - assert(iomem); - - if (cs < 0 || cs >= 8) { - fprintf(stderr, "%s: bad chip-select %i\n", __FUNCTION__, cs); - exit(-1); - } - f = &s->cs_file[cs]; - - omap_gpmc_cs_unmap(s, cs); - f->config[0] &= ~(0xf << 10); - f->iomem = iomem; - omap_gpmc_cs_map(s, cs); -} - -void omap_gpmc_attach_nand(struct omap_gpmc_s *s, int cs, DeviceState *nand) -{ - struct omap_gpmc_cs_file_s *f; - assert(nand); - - if (cs < 0 || cs >= 8) { - fprintf(stderr, "%s: bad chip-select %i\n", __func__, cs); - exit(-1); - } - f = &s->cs_file[cs]; - - omap_gpmc_cs_unmap(s, cs); - f->config[0] &= ~(0xf << 10); - f->config[0] |= (OMAP_GPMC_NAND << 10); - f->dev = nand; - if (nand_getbuswidth(f->dev) == 16) { - f->config[0] |= OMAP_GPMC_16BIT << 12; - } - omap_gpmc_cs_map(s, cs); -} diff --git a/qemu/hw/misc/omap_l4.c b/qemu/hw/misc/omap_l4.c deleted file mode 100644 index 88c533a0f..000000000 --- a/qemu/hw/misc/omap_l4.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * TI OMAP L4 interconnect emulation. - * - * Copyright (C) 2007-2009 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version 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 . - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/arm/omap.h" - -struct omap_l4_s { - MemoryRegion *address_space; - hwaddr base; - int ta_num; - struct omap_target_agent_s ta[0]; -}; - -struct omap_l4_s *omap_l4_init(MemoryRegion *address_space, - hwaddr base, int ta_num) -{ - struct omap_l4_s *bus = g_malloc0( - sizeof(*bus) + ta_num * sizeof(*bus->ta)); - - bus->address_space = address_space; - bus->ta_num = ta_num; - bus->base = base; - - return bus; -} - -hwaddr omap_l4_region_base(struct omap_target_agent_s *ta, - int region) -{ - return ta->bus->base + ta->start[region].offset; -} - -hwaddr omap_l4_region_size(struct omap_target_agent_s *ta, - int region) -{ - return ta->start[region].size; -} - -static uint64_t omap_l4ta_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_target_agent_s *s = (struct omap_target_agent_s *) opaque; - - if (size != 2) { - return omap_badwidth_read16(opaque, addr); - } - - switch (addr) { - case 0x00: /* COMPONENT */ - return s->component; - - case 0x20: /* AGENT_CONTROL */ - return s->control; - - case 0x28: /* AGENT_STATUS */ - return s->status; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_l4ta_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_target_agent_s *s = (struct omap_target_agent_s *) opaque; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x00: /* COMPONENT */ - case 0x28: /* AGENT_STATUS */ - OMAP_RO_REG(addr); - break; - - case 0x20: /* AGENT_CONTROL */ - s->control = value & 0x01000700; - if (value & 1) /* OCP_RESET */ - s->status &= ~1; /* REQ_TIMEOUT */ - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_l4ta_ops = { - .read = omap_l4ta_read, - .write = omap_l4ta_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_target_agent_s *omap_l4ta_get(struct omap_l4_s *bus, - const struct omap_l4_region_s *regions, - const struct omap_l4_agent_info_s *agents, - int cs) -{ - int i; - struct omap_target_agent_s *ta = NULL; - const struct omap_l4_agent_info_s *info = NULL; - - for (i = 0; i < bus->ta_num; i ++) - if (agents[i].ta == cs) { - ta = &bus->ta[i]; - info = &agents[i]; - break; - } - if (!ta) { - fprintf(stderr, "%s: bad target agent (%i)\n", __FUNCTION__, cs); - exit(-1); - } - - ta->bus = bus; - ta->start = ®ions[info->region]; - ta->regions = info->regions; - - ta->component = ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0); - ta->status = 0x00000000; - ta->control = 0x00000200; /* XXX 01000200 for L4TAO */ - - memory_region_init_io(&ta->iomem, NULL, &omap_l4ta_ops, ta, "omap.l4ta", - omap_l4_region_size(ta, info->ta_region)); - omap_l4_attach(ta, info->ta_region, &ta->iomem); - - return ta; -} - -hwaddr omap_l4_attach(struct omap_target_agent_s *ta, - int region, MemoryRegion *mr) -{ - hwaddr base; - - if (region < 0 || region >= ta->regions) { - fprintf(stderr, "%s: bad io region (%i)\n", __FUNCTION__, region); - exit(-1); - } - - base = ta->bus->base + ta->start[region].offset; - if (mr) { - memory_region_add_subregion(ta->bus->address_space, base, mr); - } - - return base; -} diff --git a/qemu/hw/misc/omap_sdrc.c b/qemu/hw/misc/omap_sdrc.c deleted file mode 100644 index dff37ecaf..000000000 --- a/qemu/hw/misc/omap_sdrc.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * TI OMAP SDRAM controller emulation. - * - * Copyright (C) 2007-2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version 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 . - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/arm/omap.h" - -/* SDRAM Controller Subsystem */ -struct omap_sdrc_s { - MemoryRegion iomem; - uint8_t config; -}; - -void omap_sdrc_reset(struct omap_sdrc_s *s) -{ - s->config = 0x10; -} - -static uint64_t omap_sdrc_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_sdrc_s *s = (struct omap_sdrc_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* SDRC_REVISION */ - return 0x20; - - case 0x10: /* SDRC_SYSCONFIG */ - return s->config; - - case 0x14: /* SDRC_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x40: /* SDRC_CS_CFG */ - case 0x44: /* SDRC_SHARING */ - case 0x48: /* SDRC_ERR_ADDR */ - case 0x4c: /* SDRC_ERR_TYPE */ - case 0x60: /* SDRC_DLLA_SCTRL */ - case 0x64: /* SDRC_DLLA_STATUS */ - case 0x68: /* SDRC_DLLB_CTRL */ - case 0x6c: /* SDRC_DLLB_STATUS */ - case 0x70: /* SDRC_POWER */ - case 0x80: /* SDRC_MCFG_0 */ - case 0x84: /* SDRC_MR_0 */ - case 0x88: /* SDRC_EMR1_0 */ - case 0x8c: /* SDRC_EMR2_0 */ - case 0x90: /* SDRC_EMR3_0 */ - case 0x94: /* SDRC_DCDL1_CTRL */ - case 0x98: /* SDRC_DCDL2_CTRL */ - case 0x9c: /* SDRC_ACTIM_CTRLA_0 */ - case 0xa0: /* SDRC_ACTIM_CTRLB_0 */ - case 0xa4: /* SDRC_RFR_CTRL_0 */ - case 0xa8: /* SDRC_MANUAL_0 */ - case 0xb0: /* SDRC_MCFG_1 */ - case 0xb4: /* SDRC_MR_1 */ - case 0xb8: /* SDRC_EMR1_1 */ - case 0xbc: /* SDRC_EMR2_1 */ - case 0xc0: /* SDRC_EMR3_1 */ - case 0xc4: /* SDRC_ACTIM_CTRLA_1 */ - case 0xc8: /* SDRC_ACTIM_CTRLB_1 */ - case 0xd4: /* SDRC_RFR_CTRL_1 */ - case 0xd8: /* SDRC_MANUAL_1 */ - return 0x00; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_sdrc_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_sdrc_s *s = (struct omap_sdrc_s *) opaque; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x00: /* SDRC_REVISION */ - case 0x14: /* SDRC_SYSSTATUS */ - case 0x48: /* SDRC_ERR_ADDR */ - case 0x64: /* SDRC_DLLA_STATUS */ - case 0x6c: /* SDRC_DLLB_STATUS */ - OMAP_RO_REG(addr); - return; - - case 0x10: /* SDRC_SYSCONFIG */ - if ((value >> 3) != 0x2) - fprintf(stderr, "%s: bad SDRAM idle mode %i\n", - __FUNCTION__, (unsigned)value >> 3); - if (value & 2) - omap_sdrc_reset(s); - s->config = value & 0x18; - break; - - case 0x40: /* SDRC_CS_CFG */ - case 0x44: /* SDRC_SHARING */ - case 0x4c: /* SDRC_ERR_TYPE */ - case 0x60: /* SDRC_DLLA_SCTRL */ - case 0x68: /* SDRC_DLLB_CTRL */ - case 0x70: /* SDRC_POWER */ - case 0x80: /* SDRC_MCFG_0 */ - case 0x84: /* SDRC_MR_0 */ - case 0x88: /* SDRC_EMR1_0 */ - case 0x8c: /* SDRC_EMR2_0 */ - case 0x90: /* SDRC_EMR3_0 */ - case 0x94: /* SDRC_DCDL1_CTRL */ - case 0x98: /* SDRC_DCDL2_CTRL */ - case 0x9c: /* SDRC_ACTIM_CTRLA_0 */ - case 0xa0: /* SDRC_ACTIM_CTRLB_0 */ - case 0xa4: /* SDRC_RFR_CTRL_0 */ - case 0xa8: /* SDRC_MANUAL_0 */ - case 0xb0: /* SDRC_MCFG_1 */ - case 0xb4: /* SDRC_MR_1 */ - case 0xb8: /* SDRC_EMR1_1 */ - case 0xbc: /* SDRC_EMR2_1 */ - case 0xc0: /* SDRC_EMR3_1 */ - case 0xc4: /* SDRC_ACTIM_CTRLA_1 */ - case 0xc8: /* SDRC_ACTIM_CTRLB_1 */ - case 0xd4: /* SDRC_RFR_CTRL_1 */ - case 0xd8: /* SDRC_MANUAL_1 */ - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_sdrc_ops = { - .read = omap_sdrc_read, - .write = omap_sdrc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_sdrc_s *omap_sdrc_init(MemoryRegion *sysmem, - hwaddr base) -{ - struct omap_sdrc_s *s = g_new0(struct omap_sdrc_s, 1); - - omap_sdrc_reset(s); - - memory_region_init_io(&s->iomem, NULL, &omap_sdrc_ops, s, "omap.sdrc", 0x1000); - memory_region_add_subregion(sysmem, base, &s->iomem); - - return s; -} diff --git a/qemu/hw/misc/omap_tap.c b/qemu/hw/misc/omap_tap.c deleted file mode 100644 index e6ea8ee23..000000000 --- a/qemu/hw/misc/omap_tap.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * TI OMAP TEST-Chip-level TAP emulation. - * - * Copyright (C) 2007-2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version 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 . - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/arm/omap.h" - -/* TEST-Chip-level TAP */ -static uint64_t omap_tap_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x204: /* IDCODE_reg */ - switch (s->mpu_model) { - case omap2420: - case omap2422: - case omap2423: - return 0x5b5d902f; /* ES 2.2 */ - case omap2430: - return 0x5b68a02f; /* ES 2.2 */ - case omap3430: - return 0x1b7ae02f; /* ES 2 */ - default: - hw_error("%s: Bad mpu model\n", __FUNCTION__); - } - - case 0x208: /* PRODUCTION_ID_reg for OMAP2 */ - case 0x210: /* PRODUCTION_ID_reg for OMAP3 */ - switch (s->mpu_model) { - case omap2420: - return 0x000254f0; /* POP ESHS2.1.1 in N91/93/95, ES2 in N800 */ - case omap2422: - return 0x000400f0; - case omap2423: - return 0x000800f0; - case omap2430: - return 0x000000f0; - case omap3430: - return 0x000000f0; - default: - hw_error("%s: Bad mpu model\n", __FUNCTION__); - } - - case 0x20c: - switch (s->mpu_model) { - case omap2420: - case omap2422: - case omap2423: - return 0xcafeb5d9; /* ES 2.2 */ - case omap2430: - return 0xcafeb68a; /* ES 2.2 */ - case omap3430: - return 0xcafeb7ae; /* ES 2 */ - default: - hw_error("%s: Bad mpu model\n", __FUNCTION__); - } - - case 0x218: /* DIE_ID_reg */ - return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0); - case 0x21c: /* DIE_ID_reg */ - return 0x54 << 24; - case 0x220: /* DIE_ID_reg */ - return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0); - case 0x224: /* DIE_ID_reg */ - return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0); - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_tap_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - OMAP_BAD_REG(addr); -} - -static const MemoryRegionOps omap_tap_ops = { - .read = omap_tap_read, - .write = omap_tap_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -void omap_tap_init(struct omap_target_agent_s *ta, - struct omap_mpu_state_s *mpu) -{ - memory_region_init_io(&mpu->tap_iomem, NULL, &omap_tap_ops, mpu, "omap.tap", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &mpu->tap_iomem); -} diff --git a/qemu/hw/misc/pc-testdev.c b/qemu/hw/misc/pc-testdev.c deleted file mode 100644 index 086893dcc..000000000 --- a/qemu/hw/misc/pc-testdev.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * QEMU x86 ISA testdev - * - * Copyright (c) 2012 Avi Kivity, Gerd Hoffmann, Marcelo Tosatti - * - * 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 device is used to test KVM features specific to the x86 port, such - * as emulation, power management, interrupt routing, among others. It's meant - * to be used like: - * - * qemu-system-x86_64 -device pc-testdev -serial stdio \ - * -device isa-debug-exit,iobase=0xf4,iosize=0x4 \ - * -kernel /home/lmr/Code/virt-test.git/kvm/unittests/msr.flat - * - * Where msr.flat is one of the KVM unittests, present on a separate repo, - * git://git.kernel.org/pub/scm/virt/kvm/kvm-unit-tests.git -*/ - -#include "qemu/osdep.h" -#if defined(CONFIG_POSIX) -#include -#endif -#include "hw/hw.h" -#include "hw/qdev.h" -#include "hw/isa/isa.h" - -#define IOMEM_LEN 0x10000 - -typedef struct PCTestdev { - ISADevice parent_obj; - - MemoryRegion ioport; - MemoryRegion ioport_byte; - MemoryRegion flush; - MemoryRegion irq; - MemoryRegion iomem; - uint32_t ioport_data; - char iomem_buf[IOMEM_LEN]; -} PCTestdev; - -#define TYPE_TESTDEV "pc-testdev" -#define TESTDEV(obj) \ - OBJECT_CHECK(PCTestdev, (obj), TYPE_TESTDEV) - -static void test_irq_line(void *opaque, hwaddr addr, uint64_t data, - unsigned len) -{ - PCTestdev *dev = opaque; - ISADevice *isa = ISA_DEVICE(dev); - - qemu_set_irq(isa_get_irq(isa, addr), !!data); -} - -static const MemoryRegionOps test_irq_ops = { - .write = test_irq_line, - .valid.min_access_size = 1, - .valid.max_access_size = 1, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void test_ioport_write(void *opaque, hwaddr addr, uint64_t data, - unsigned len) -{ - PCTestdev *dev = opaque; - int bits = len * 8; - int start_bit = (addr & 3) * 8; - uint32_t mask = ((uint32_t)-1 >> (32 - bits)) << start_bit; - dev->ioport_data &= ~mask; - dev->ioport_data |= data << start_bit; -} - -static uint64_t test_ioport_read(void *opaque, hwaddr addr, unsigned len) -{ - PCTestdev *dev = opaque; - int bits = len * 8; - int start_bit = (addr & 3) * 8; - uint32_t mask = ((uint32_t)-1 >> (32 - bits)) << start_bit; - return (dev->ioport_data & mask) >> start_bit; -} - -static const MemoryRegionOps test_ioport_ops = { - .read = test_ioport_read, - .write = test_ioport_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps test_ioport_byte_ops = { - .read = test_ioport_read, - .write = test_ioport_write, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .impl.min_access_size = 1, - .impl.max_access_size = 1, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void test_flush_page(void *opaque, hwaddr addr, uint64_t data, - unsigned len) -{ - hwaddr page = 4096; - void *a = cpu_physical_memory_map(data & ~0xffful, &page, 0); - - /* We might not be able to get the full page, only mprotect what we actually - have mapped */ -#if defined(CONFIG_POSIX) - mprotect(a, page, PROT_NONE); - mprotect(a, page, PROT_READ|PROT_WRITE); -#endif - cpu_physical_memory_unmap(a, page, 0, 0); -} - -static const MemoryRegionOps test_flush_ops = { - .write = test_flush_page, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static uint64_t test_iomem_read(void *opaque, hwaddr addr, unsigned len) -{ - PCTestdev *dev = opaque; - uint64_t ret = 0; - memcpy(&ret, &dev->iomem_buf[addr], len); - - return ret; -} - -static void test_iomem_write(void *opaque, hwaddr addr, uint64_t val, - unsigned len) -{ - PCTestdev *dev = opaque; - memcpy(&dev->iomem_buf[addr], &val, len); - dev->iomem_buf[addr] = val; -} - -static const MemoryRegionOps test_iomem_ops = { - .read = test_iomem_read, - .write = test_iomem_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void testdev_realizefn(DeviceState *d, Error **errp) -{ - ISADevice *isa = ISA_DEVICE(d); - PCTestdev *dev = TESTDEV(d); - MemoryRegion *mem = isa_address_space(isa); - MemoryRegion *io = isa_address_space_io(isa); - - memory_region_init_io(&dev->ioport, OBJECT(dev), &test_ioport_ops, dev, - "pc-testdev-ioport", 4); - memory_region_init_io(&dev->ioport_byte, OBJECT(dev), - &test_ioport_byte_ops, dev, - "pc-testdev-ioport-byte", 4); - memory_region_init_io(&dev->flush, OBJECT(dev), &test_flush_ops, dev, - "pc-testdev-flush-page", 4); - memory_region_init_io(&dev->irq, OBJECT(dev), &test_irq_ops, dev, - "pc-testdev-irq-line", 24); - memory_region_init_io(&dev->iomem, OBJECT(dev), &test_iomem_ops, dev, - "pc-testdev-iomem", IOMEM_LEN); - - memory_region_add_subregion(io, 0xe0, &dev->ioport); - memory_region_add_subregion(io, 0xe4, &dev->flush); - memory_region_add_subregion(io, 0xe8, &dev->ioport_byte); - memory_region_add_subregion(io, 0x2000, &dev->irq); - memory_region_add_subregion(mem, 0xff000000, &dev->iomem); -} - -static void testdev_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->realize = testdev_realizefn; -} - -static const TypeInfo testdev_info = { - .name = TYPE_TESTDEV, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(PCTestdev), - .class_init = testdev_class_init, -}; - -static void testdev_register_types(void) -{ - type_register_static(&testdev_info); -} - -type_init(testdev_register_types) diff --git a/qemu/hw/misc/pci-testdev.c b/qemu/hw/misc/pci-testdev.c deleted file mode 100644 index 2f2e98977..000000000 --- a/qemu/hw/misc/pci-testdev.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - * QEMU PCI test device - * - * Copyright (c) 2012 Red Hat Inc. - * Author: Michael S. Tsirkin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "qemu/event_notifier.h" - -typedef struct PCITestDevHdr { - uint8_t test; - uint8_t width; - uint8_t pad0[2]; - uint32_t offset; - uint8_t data; - uint8_t pad1[3]; - uint32_t count; - uint8_t name[]; -} PCITestDevHdr; - -typedef struct IOTest { - MemoryRegion *mr; - EventNotifier notifier; - bool hasnotifier; - unsigned size; - bool match_data; - PCITestDevHdr *hdr; - unsigned bufsize; -} IOTest; - -#define IOTEST_DATAMATCH 0xFA -#define IOTEST_NOMATCH 0xCE - -#define IOTEST_IOSIZE 128 -#define IOTEST_MEMSIZE 2048 - -static const char *iotest_test[] = { - "no-eventfd", - "wildcard-eventfd", - "datamatch-eventfd" -}; - -static const char *iotest_type[] = { - "mmio", - "portio" -}; - -#define IOTEST_TEST(i) (iotest_test[((i) % ARRAY_SIZE(iotest_test))]) -#define IOTEST_TYPE(i) (iotest_type[((i) / ARRAY_SIZE(iotest_test))]) -#define IOTEST_MAX_TEST (ARRAY_SIZE(iotest_test)) -#define IOTEST_MAX_TYPE (ARRAY_SIZE(iotest_type)) -#define IOTEST_MAX (IOTEST_MAX_TEST * IOTEST_MAX_TYPE) - -enum { - IOTEST_ACCESS_NAME, - IOTEST_ACCESS_DATA, - IOTEST_ACCESS_MAX, -}; - -#define IOTEST_ACCESS_TYPE uint8_t -#define IOTEST_ACCESS_WIDTH (sizeof(uint8_t)) - -typedef struct PCITestDevState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - MemoryRegion mmio; - MemoryRegion portio; - IOTest *tests; - int current; -} PCITestDevState; - -#define TYPE_PCI_TEST_DEV "pci-testdev" - -#define PCI_TEST_DEV(obj) \ - OBJECT_CHECK(PCITestDevState, (obj), TYPE_PCI_TEST_DEV) - -#define IOTEST_IS_MEM(i) (strcmp(IOTEST_TYPE(i), "portio")) -#define IOTEST_REGION(d, i) (IOTEST_IS_MEM(i) ? &(d)->mmio : &(d)->portio) -#define IOTEST_SIZE(i) (IOTEST_IS_MEM(i) ? IOTEST_MEMSIZE : IOTEST_IOSIZE) -#define IOTEST_PCI_BAR(i) (IOTEST_IS_MEM(i) ? PCI_BASE_ADDRESS_SPACE_MEMORY : \ - PCI_BASE_ADDRESS_SPACE_IO) - -static int pci_testdev_start(IOTest *test) -{ - test->hdr->count = 0; - if (!test->hasnotifier) { - return 0; - } - event_notifier_test_and_clear(&test->notifier); - memory_region_add_eventfd(test->mr, - le32_to_cpu(test->hdr->offset), - test->size, - test->match_data, - test->hdr->data, - &test->notifier); - return 0; -} - -static void pci_testdev_stop(IOTest *test) -{ - if (!test->hasnotifier) { - return; - } - memory_region_del_eventfd(test->mr, - le32_to_cpu(test->hdr->offset), - test->size, - test->match_data, - test->hdr->data, - &test->notifier); -} - -static void -pci_testdev_reset(PCITestDevState *d) -{ - if (d->current == -1) { - return; - } - pci_testdev_stop(&d->tests[d->current]); - d->current = -1; -} - -static void pci_testdev_inc(IOTest *test, unsigned inc) -{ - uint32_t c = le32_to_cpu(test->hdr->count); - test->hdr->count = cpu_to_le32(c + inc); -} - -static void -pci_testdev_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size, int type) -{ - PCITestDevState *d = opaque; - IOTest *test; - int t, r; - - if (addr == offsetof(PCITestDevHdr, test)) { - pci_testdev_reset(d); - if (val >= IOTEST_MAX_TEST) { - return; - } - t = type * IOTEST_MAX_TEST + val; - r = pci_testdev_start(&d->tests[t]); - if (r < 0) { - return; - } - d->current = t; - return; - } - if (d->current < 0) { - return; - } - test = &d->tests[d->current]; - if (addr != le32_to_cpu(test->hdr->offset)) { - return; - } - if (test->match_data && test->size != size) { - return; - } - if (test->match_data && val != test->hdr->data) { - return; - } - pci_testdev_inc(test, 1); -} - -static uint64_t -pci_testdev_read(void *opaque, hwaddr addr, unsigned size) -{ - PCITestDevState *d = opaque; - const char *buf; - IOTest *test; - if (d->current < 0) { - return 0; - } - test = &d->tests[d->current]; - buf = (const char *)test->hdr; - if (addr + size >= test->bufsize) { - return 0; - } - if (test->hasnotifier) { - event_notifier_test_and_clear(&test->notifier); - } - return buf[addr]; -} - -static void -pci_testdev_mmio_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - pci_testdev_write(opaque, addr, val, size, 0); -} - -static void -pci_testdev_pio_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - pci_testdev_write(opaque, addr, val, size, 1); -} - -static const MemoryRegionOps pci_testdev_mmio_ops = { - .read = pci_testdev_read, - .write = pci_testdev_mmio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static const MemoryRegionOps pci_testdev_pio_ops = { - .read = pci_testdev_read, - .write = pci_testdev_pio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp) -{ - PCITestDevState *d = PCI_TEST_DEV(pci_dev); - uint8_t *pci_conf; - char *name; - int r, i; - bool fastmmio = kvm_ioeventfd_any_length_enabled(); - - pci_conf = pci_dev->config; - - pci_conf[PCI_INTERRUPT_PIN] = 0; /* no interrupt pin */ - - memory_region_init_io(&d->mmio, OBJECT(d), &pci_testdev_mmio_ops, d, - "pci-testdev-mmio", IOTEST_MEMSIZE * 2); - memory_region_init_io(&d->portio, OBJECT(d), &pci_testdev_pio_ops, d, - "pci-testdev-portio", IOTEST_IOSIZE * 2); - pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); - pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->portio); - - d->current = -1; - d->tests = g_malloc0(IOTEST_MAX * sizeof *d->tests); - for (i = 0; i < IOTEST_MAX; ++i) { - IOTest *test = &d->tests[i]; - name = g_strdup_printf("%s-%s", IOTEST_TYPE(i), IOTEST_TEST(i)); - test->bufsize = sizeof(PCITestDevHdr) + strlen(name) + 1; - test->hdr = g_malloc0(test->bufsize); - 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->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; - test->mr = IOTEST_REGION(d, i); - if (!strcmp(IOTEST_TEST(i), "no-eventfd")) { - test->hasnotifier = false; - continue; - } - r = event_notifier_init(&test->notifier, 0); - assert(r >= 0); - test->hasnotifier = true; - } -} - -static void -pci_testdev_uninit(PCIDevice *dev) -{ - PCITestDevState *d = PCI_TEST_DEV(dev); - int i; - - pci_testdev_reset(d); - for (i = 0; i < IOTEST_MAX; ++i) { - if (d->tests[i].hasnotifier) { - event_notifier_cleanup(&d->tests[i].notifier); - } - g_free(d->tests[i].hdr); - } - g_free(d->tests); -} - -static void qdev_pci_testdev_reset(DeviceState *dev) -{ - PCITestDevState *d = PCI_TEST_DEV(dev); - pci_testdev_reset(d); -} - -static void pci_testdev_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = pci_testdev_realize; - k->exit = pci_testdev_uninit; - k->vendor_id = PCI_VENDOR_ID_REDHAT; - k->device_id = PCI_DEVICE_ID_REDHAT_TEST; - k->revision = 0x00; - k->class_id = PCI_CLASS_OTHERS; - dc->desc = "PCI Test Device"; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->reset = qdev_pci_testdev_reset; -} - -static const TypeInfo pci_testdev_info = { - .name = TYPE_PCI_TEST_DEV, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCITestDevState), - .class_init = pci_testdev_class_init, -}; - -static void pci_testdev_register_types(void) -{ - type_register_static(&pci_testdev_info); -} - -type_init(pci_testdev_register_types) diff --git a/qemu/hw/misc/puv3_pm.c b/qemu/hw/misc/puv3_pm.c deleted file mode 100644 index 577cebaac..000000000 --- a/qemu/hw/misc/puv3_pm.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Power Management device simulation in PKUnity SoC - * - * Copyright (C) 2010-2012 Guan Xuetao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation, 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" - -#undef DEBUG_PUV3 -#include "hw/unicore32/puv3.h" - -#define TYPE_PUV3_PM "puv3_pm" -#define PUV3_PM(obj) OBJECT_CHECK(PUV3PMState, (obj), TYPE_PUV3_PM) - -typedef struct PUV3PMState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - - uint32_t reg_PMCR; - uint32_t reg_PCGR; - uint32_t reg_PLL_SYS_CFG; - uint32_t reg_PLL_DDR_CFG; - uint32_t reg_PLL_VGA_CFG; - uint32_t reg_DIVCFG; -} PUV3PMState; - -static uint64_t puv3_pm_read(void *opaque, hwaddr offset, - unsigned size) -{ - PUV3PMState *s = opaque; - uint32_t ret = 0; - - switch (offset) { - case 0x14: - ret = s->reg_PCGR; - break; - case 0x18: - ret = s->reg_PLL_SYS_CFG; - break; - case 0x1c: - ret = s->reg_PLL_DDR_CFG; - break; - case 0x20: - ret = s->reg_PLL_VGA_CFG; - break; - case 0x24: - ret = s->reg_DIVCFG; - break; - case 0x28: /* PLL SYS STATUS */ - ret = 0x00002401; - break; - case 0x2c: /* PLL DDR STATUS */ - ret = 0x00100c00; - break; - case 0x30: /* PLL VGA STATUS */ - ret = 0x00003801; - break; - case 0x34: /* DIV STATUS */ - ret = 0x22f52015; - break; - case 0x38: /* SW RESET */ - ret = 0x0; - break; - case 0x44: /* PLL DFC DONE */ - ret = 0x7; - break; - default: - DPRINTF("Bad offset 0x%x\n", offset); - } - DPRINTF("offset 0x%x, value 0x%x\n", offset, ret); - - return ret; -} - -static void puv3_pm_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PUV3PMState *s = opaque; - - switch (offset) { - case 0x0: - s->reg_PMCR = value; - break; - case 0x14: - s->reg_PCGR = value; - break; - case 0x18: - s->reg_PLL_SYS_CFG = value; - break; - case 0x1c: - s->reg_PLL_DDR_CFG = value; - break; - case 0x20: - s->reg_PLL_VGA_CFG = value; - break; - case 0x24: - case 0x38: - break; - default: - DPRINTF("Bad offset 0x%x\n", offset); - } - DPRINTF("offset 0x%x, value 0x%x\n", offset, value); -} - -static const MemoryRegionOps puv3_pm_ops = { - .read = puv3_pm_read, - .write = puv3_pm_write, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int puv3_pm_init(SysBusDevice *dev) -{ - PUV3PMState *s = PUV3_PM(dev); - - s->reg_PCGR = 0x0; - - memory_region_init_io(&s->iomem, OBJECT(s), &puv3_pm_ops, s, "puv3_pm", - PUV3_REGS_OFFSET); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static void puv3_pm_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = puv3_pm_init; -} - -static const TypeInfo puv3_pm_info = { - .name = TYPE_PUV3_PM, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PUV3PMState), - .class_init = puv3_pm_class_init, -}; - -static void puv3_pm_register_type(void) -{ - type_register_static(&puv3_pm_info); -} - -type_init(puv3_pm_register_type) diff --git a/qemu/hw/misc/pvpanic.c b/qemu/hw/misc/pvpanic.c deleted file mode 100644 index 0ac1e6ac9..000000000 --- a/qemu/hw/misc/pvpanic.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * QEMU simulated pvpanic device. - * - * Copyright Fujitsu, Corp. 2013 - * - * Authors: - * Wen Congyang - * Hu Tao - * - * 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 "qapi/qmp/qobject.h" -#include "qapi/qmp/qjson.h" -#include "sysemu/sysemu.h" -#include "qemu/log.h" - -#include "hw/nvram/fw_cfg.h" -#include "hw/i386/pc.h" -#include "qapi-event.h" - -/* The bit of supported pv event */ -#define PVPANIC_F_PANICKED 0 - -/* The pv event value */ -#define PVPANIC_PANICKED (1 << PVPANIC_F_PANICKED) - -#define TYPE_ISA_PVPANIC_DEVICE "pvpanic" -#define ISA_PVPANIC_DEVICE(obj) \ - OBJECT_CHECK(PVPanicState, (obj), TYPE_ISA_PVPANIC_DEVICE) - -static void handle_event(int event) -{ - static bool logged; - - if (event & ~PVPANIC_PANICKED && !logged) { - qemu_log_mask(LOG_GUEST_ERROR, "pvpanic: unknown event %#x.\n", event); - logged = true; - } - - if (event & PVPANIC_PANICKED) { - qemu_system_guest_panicked(); - return; - } -} - -#include "hw/isa/isa.h" - -typedef struct PVPanicState { - ISADevice parent_obj; - - MemoryRegion io; - uint16_t ioport; -} PVPanicState; - -/* return supported events on read */ -static uint64_t pvpanic_ioport_read(void *opaque, hwaddr addr, unsigned size) -{ - return PVPANIC_PANICKED; -} - -static void pvpanic_ioport_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - handle_event(val); -} - -static const MemoryRegionOps pvpanic_ops = { - .read = pvpanic_ioport_read, - .write = pvpanic_ioport_write, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void pvpanic_isa_initfn(Object *obj) -{ - PVPanicState *s = ISA_PVPANIC_DEVICE(obj); - - memory_region_init_io(&s->io, OBJECT(s), &pvpanic_ops, s, "pvpanic", 1); -} - -static void pvpanic_isa_realizefn(DeviceState *dev, Error **errp) -{ - ISADevice *d = ISA_DEVICE(dev); - PVPanicState *s = ISA_PVPANIC_DEVICE(dev); - FWCfgState *fw_cfg = fw_cfg_find(); - uint16_t *pvpanic_port; - - if (!fw_cfg) { - return; - } - - pvpanic_port = g_malloc(sizeof(*pvpanic_port)); - *pvpanic_port = cpu_to_le16(s->ioport); - fw_cfg_add_file(fw_cfg, "etc/pvpanic-port", pvpanic_port, - sizeof(*pvpanic_port)); - - isa_register_ioport(d, &s->io, s->ioport); -} - -#define PVPANIC_IOPORT_PROP "ioport" - -uint16_t pvpanic_port(void) -{ - Object *o = object_resolve_path_type("", TYPE_ISA_PVPANIC_DEVICE, NULL); - if (!o) { - return 0; - } - return object_property_get_int(o, PVPANIC_IOPORT_PROP, NULL); -} - -static Property pvpanic_isa_properties[] = { - DEFINE_PROP_UINT16(PVPANIC_IOPORT_PROP, PVPanicState, ioport, 0x505), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pvpanic_isa_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = pvpanic_isa_realizefn; - dc->props = pvpanic_isa_properties; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static TypeInfo pvpanic_isa_info = { - .name = TYPE_ISA_PVPANIC_DEVICE, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(PVPanicState), - .instance_init = pvpanic_isa_initfn, - .class_init = pvpanic_isa_class_init, -}; - -static void pvpanic_register_types(void) -{ - type_register_static(&pvpanic_isa_info); -} - -type_init(pvpanic_register_types) diff --git a/qemu/hw/misc/sga.c b/qemu/hw/misc/sga.c deleted file mode 100644 index 03b006d6f..000000000 --- a/qemu/hw/misc/sga.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * QEMU dummy ISA device for loading sgabios option rom. - * - * Copyright (c) 2011 Glauber Costa, Red Hat Inc. - * - * 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. - * - * 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" -#include "sysemu/sysemu.h" - -#define SGABIOS_FILENAME "sgabios.bin" - -#define TYPE_SGA "sga" -#define SGA(obj) OBJECT_CHECK(ISASGAState, (obj), TYPE_SGA) - -typedef struct ISASGAState { - ISADevice parent_obj; -} ISASGAState; - -static void sga_realizefn(DeviceState *dev, Error **errp) -{ - rom_add_vga(SGABIOS_FILENAME); -} - -static void sga_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); - dc->realize = sga_realizefn; - dc->desc = "Serial Graphics Adapter"; -} - -static const TypeInfo sga_info = { - .name = TYPE_SGA, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(ISASGAState), - .class_init = sga_class_initfn, -}; - -static void sga_register_types(void) -{ - type_register_static(&sga_info); -} - -type_init(sga_register_types) diff --git a/qemu/hw/misc/slavio_misc.c b/qemu/hw/misc/slavio_misc.c deleted file mode 100644 index edd5de070..000000000 --- a/qemu/hw/misc/slavio_misc.c +++ /dev/null @@ -1,518 +0,0 @@ -/* - * QEMU Sparc SLAVIO aux io port emulation - * - * Copyright (c) 2005 Fabrice Bellard - * - * 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 "hw/sysbus.h" -#include "trace.h" - -/* - * This is the auxio port, chip control and system control part of - * chip STP2001 (Slave I/O), also produced as NCR89C105. See - * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt - * - * This also includes the PMC CPU idle controller. - */ - -#define TYPE_SLAVIO_MISC "slavio_misc" -#define SLAVIO_MISC(obj) OBJECT_CHECK(MiscState, (obj), TYPE_SLAVIO_MISC) - -typedef struct MiscState { - SysBusDevice parent_obj; - - MemoryRegion cfg_iomem; - MemoryRegion diag_iomem; - MemoryRegion mdm_iomem; - MemoryRegion led_iomem; - MemoryRegion sysctrl_iomem; - MemoryRegion aux1_iomem; - MemoryRegion aux2_iomem; - qemu_irq irq; - qemu_irq fdc_tc; - uint32_t dummy; - uint8_t config; - uint8_t aux1, aux2; - uint8_t diag, mctrl; - uint8_t sysctrl; - uint16_t leds; -} MiscState; - -#define TYPE_APC "apc" -#define APC(obj) OBJECT_CHECK(APCState, (obj), TYPE_APC) - -typedef struct APCState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq cpu_halt; -} APCState; - -#define MISC_SIZE 1 -#define LED_SIZE 2 -#define SYSCTRL_SIZE 4 - -#define AUX1_TC 0x02 - -#define AUX2_PWROFF 0x01 -#define AUX2_PWRINTCLR 0x02 -#define AUX2_PWRFAIL 0x20 - -#define CFG_PWRINTEN 0x08 - -#define SYS_RESET 0x01 -#define SYS_RESETSTAT 0x02 - -static void slavio_misc_update_irq(void *opaque) -{ - MiscState *s = opaque; - - if ((s->aux2 & AUX2_PWRFAIL) && (s->config & CFG_PWRINTEN)) { - trace_slavio_misc_update_irq_raise(); - qemu_irq_raise(s->irq); - } else { - trace_slavio_misc_update_irq_lower(); - qemu_irq_lower(s->irq); - } -} - -static void slavio_misc_reset(DeviceState *d) -{ - MiscState *s = SLAVIO_MISC(d); - - // Diagnostic and system control registers not cleared in reset - s->config = s->aux1 = s->aux2 = s->mctrl = 0; -} - -static void slavio_set_power_fail(void *opaque, int irq, int power_failing) -{ - MiscState *s = opaque; - - trace_slavio_set_power_fail(power_failing, s->config); - if (power_failing && (s->config & CFG_PWRINTEN)) { - s->aux2 |= AUX2_PWRFAIL; - } else { - s->aux2 &= ~AUX2_PWRFAIL; - } - slavio_misc_update_irq(s); -} - -static void slavio_cfg_mem_writeb(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - MiscState *s = opaque; - - trace_slavio_cfg_mem_writeb(val & 0xff); - s->config = val & 0xff; - slavio_misc_update_irq(s); -} - -static uint64_t slavio_cfg_mem_readb(void *opaque, hwaddr addr, - unsigned size) -{ - MiscState *s = opaque; - uint32_t ret = 0; - - ret = s->config; - trace_slavio_cfg_mem_readb(ret); - return ret; -} - -static const MemoryRegionOps slavio_cfg_mem_ops = { - .read = slavio_cfg_mem_readb, - .write = slavio_cfg_mem_writeb, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void slavio_diag_mem_writeb(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - MiscState *s = opaque; - - trace_slavio_diag_mem_writeb(val & 0xff); - s->diag = val & 0xff; -} - -static uint64_t slavio_diag_mem_readb(void *opaque, hwaddr addr, - unsigned size) -{ - MiscState *s = opaque; - uint32_t ret = 0; - - ret = s->diag; - trace_slavio_diag_mem_readb(ret); - return ret; -} - -static const MemoryRegionOps slavio_diag_mem_ops = { - .read = slavio_diag_mem_readb, - .write = slavio_diag_mem_writeb, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void slavio_mdm_mem_writeb(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - MiscState *s = opaque; - - trace_slavio_mdm_mem_writeb(val & 0xff); - s->mctrl = val & 0xff; -} - -static uint64_t slavio_mdm_mem_readb(void *opaque, hwaddr addr, - unsigned size) -{ - MiscState *s = opaque; - uint32_t ret = 0; - - ret = s->mctrl; - trace_slavio_mdm_mem_readb(ret); - return ret; -} - -static const MemoryRegionOps slavio_mdm_mem_ops = { - .read = slavio_mdm_mem_readb, - .write = slavio_mdm_mem_writeb, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void slavio_aux1_mem_writeb(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - MiscState *s = opaque; - - trace_slavio_aux1_mem_writeb(val & 0xff); - if (val & AUX1_TC) { - // Send a pulse to floppy terminal count line - if (s->fdc_tc) { - qemu_irq_raise(s->fdc_tc); - qemu_irq_lower(s->fdc_tc); - } - val &= ~AUX1_TC; - } - s->aux1 = val & 0xff; -} - -static uint64_t slavio_aux1_mem_readb(void *opaque, hwaddr addr, - unsigned size) -{ - MiscState *s = opaque; - uint32_t ret = 0; - - ret = s->aux1; - trace_slavio_aux1_mem_readb(ret); - return ret; -} - -static const MemoryRegionOps slavio_aux1_mem_ops = { - .read = slavio_aux1_mem_readb, - .write = slavio_aux1_mem_writeb, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void slavio_aux2_mem_writeb(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - MiscState *s = opaque; - - val &= AUX2_PWRINTCLR | AUX2_PWROFF; - trace_slavio_aux2_mem_writeb(val & 0xff); - val |= s->aux2 & AUX2_PWRFAIL; - if (val & AUX2_PWRINTCLR) // Clear Power Fail int - val &= AUX2_PWROFF; - s->aux2 = val; - if (val & AUX2_PWROFF) - qemu_system_shutdown_request(); - slavio_misc_update_irq(s); -} - -static uint64_t slavio_aux2_mem_readb(void *opaque, hwaddr addr, - unsigned size) -{ - MiscState *s = opaque; - uint32_t ret = 0; - - ret = s->aux2; - trace_slavio_aux2_mem_readb(ret); - return ret; -} - -static const MemoryRegionOps slavio_aux2_mem_ops = { - .read = slavio_aux2_mem_readb, - .write = slavio_aux2_mem_writeb, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void apc_mem_writeb(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - APCState *s = opaque; - - trace_apc_mem_writeb(val & 0xff); - qemu_irq_raise(s->cpu_halt); -} - -static uint64_t apc_mem_readb(void *opaque, hwaddr addr, - unsigned size) -{ - uint32_t ret = 0; - - trace_apc_mem_readb(ret); - return ret; -} - -static const MemoryRegionOps apc_mem_ops = { - .read = apc_mem_readb, - .write = apc_mem_writeb, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - } -}; - -static uint64_t slavio_sysctrl_mem_readl(void *opaque, hwaddr addr, - unsigned size) -{ - MiscState *s = opaque; - uint32_t ret = 0; - - switch (addr) { - case 0: - ret = s->sysctrl; - break; - default: - break; - } - trace_slavio_sysctrl_mem_readl(ret); - return ret; -} - -static void slavio_sysctrl_mem_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - MiscState *s = opaque; - - trace_slavio_sysctrl_mem_writel(val); - switch (addr) { - case 0: - if (val & SYS_RESET) { - s->sysctrl = SYS_RESETSTAT; - qemu_system_reset_request(); - } - break; - default: - break; - } -} - -static const MemoryRegionOps slavio_sysctrl_mem_ops = { - .read = slavio_sysctrl_mem_readl, - .write = slavio_sysctrl_mem_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static uint64_t slavio_led_mem_readw(void *opaque, hwaddr addr, - unsigned size) -{ - MiscState *s = opaque; - uint32_t ret = 0; - - switch (addr) { - case 0: - ret = s->leds; - break; - default: - break; - } - trace_slavio_led_mem_readw(ret); - return ret; -} - -static void slavio_led_mem_writew(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - MiscState *s = opaque; - - trace_slavio_led_mem_writew(val & 0xffff); - switch (addr) { - case 0: - s->leds = val; - break; - default: - break; - } -} - -static const MemoryRegionOps slavio_led_mem_ops = { - .read = slavio_led_mem_readw, - .write = slavio_led_mem_writew, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 2, - .max_access_size = 2, - }, -}; - -static const VMStateDescription vmstate_misc = { - .name ="slavio_misc", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(dummy, MiscState), - VMSTATE_UINT8(config, MiscState), - VMSTATE_UINT8(aux1, MiscState), - VMSTATE_UINT8(aux2, MiscState), - VMSTATE_UINT8(diag, MiscState), - VMSTATE_UINT8(mctrl, MiscState), - VMSTATE_UINT8(sysctrl, MiscState), - VMSTATE_END_OF_LIST() - } -}; - -static int apc_init1(SysBusDevice *dev) -{ - APCState *s = APC(dev); - - sysbus_init_irq(dev, &s->cpu_halt); - - /* Power management (APC) XXX: not a Slavio device */ - memory_region_init_io(&s->iomem, OBJECT(s), &apc_mem_ops, s, - "apc", MISC_SIZE); - sysbus_init_mmio(dev, &s->iomem); - return 0; -} - -static int slavio_misc_init1(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - MiscState *s = SLAVIO_MISC(dev); - - sysbus_init_irq(sbd, &s->irq); - sysbus_init_irq(sbd, &s->fdc_tc); - - /* 8 bit registers */ - /* Slavio control */ - memory_region_init_io(&s->cfg_iomem, OBJECT(s), &slavio_cfg_mem_ops, s, - "configuration", MISC_SIZE); - sysbus_init_mmio(sbd, &s->cfg_iomem); - - /* Diagnostics */ - memory_region_init_io(&s->diag_iomem, OBJECT(s), &slavio_diag_mem_ops, s, - "diagnostic", MISC_SIZE); - sysbus_init_mmio(sbd, &s->diag_iomem); - - /* Modem control */ - memory_region_init_io(&s->mdm_iomem, OBJECT(s), &slavio_mdm_mem_ops, s, - "modem", MISC_SIZE); - sysbus_init_mmio(sbd, &s->mdm_iomem); - - /* 16 bit registers */ - /* ss600mp diag LEDs */ - memory_region_init_io(&s->led_iomem, OBJECT(s), &slavio_led_mem_ops, s, - "leds", LED_SIZE); - sysbus_init_mmio(sbd, &s->led_iomem); - - /* 32 bit registers */ - /* System control */ - memory_region_init_io(&s->sysctrl_iomem, OBJECT(s), &slavio_sysctrl_mem_ops, s, - "system-control", SYSCTRL_SIZE); - sysbus_init_mmio(sbd, &s->sysctrl_iomem); - - /* AUX 1 (Misc System Functions) */ - memory_region_init_io(&s->aux1_iomem, OBJECT(s), &slavio_aux1_mem_ops, s, - "misc-system-functions", MISC_SIZE); - sysbus_init_mmio(sbd, &s->aux1_iomem); - - /* AUX 2 (Software Powerdown Control) */ - memory_region_init_io(&s->aux2_iomem, OBJECT(s), &slavio_aux2_mem_ops, s, - "software-powerdown-control", MISC_SIZE); - sysbus_init_mmio(sbd, &s->aux2_iomem); - - qdev_init_gpio_in(dev, slavio_set_power_fail, 1); - - return 0; -} - -static void slavio_misc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = slavio_misc_init1; - dc->reset = slavio_misc_reset; - dc->vmsd = &vmstate_misc; -} - -static const TypeInfo slavio_misc_info = { - .name = TYPE_SLAVIO_MISC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MiscState), - .class_init = slavio_misc_class_init, -}; - -static void apc_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = apc_init1; -} - -static const TypeInfo apc_info = { - .name = TYPE_APC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MiscState), - .class_init = apc_class_init, -}; - -static void slavio_misc_register_types(void) -{ - type_register_static(&slavio_misc_info); - type_register_static(&apc_info); -} - -type_init(slavio_misc_register_types) diff --git a/qemu/hw/misc/stm32f2xx_syscfg.c b/qemu/hw/misc/stm32f2xx_syscfg.c deleted file mode 100644 index d0d7076ef..000000000 --- a/qemu/hw/misc/stm32f2xx_syscfg.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * STM32F2XX SYSCFG - * - * Copyright (c) 2014 Alistair Francis - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/misc/stm32f2xx_syscfg.h" - -#ifndef STM_SYSCFG_ERR_DEBUG -#define STM_SYSCFG_ERR_DEBUG 0 -#endif - -#define DB_PRINT_L(lvl, fmt, args...) do { \ - if (STM_SYSCFG_ERR_DEBUG >= lvl) { \ - qemu_log("%s: " fmt, __func__, ## args); \ - } \ -} while (0); - -#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) - -static void stm32f2xx_syscfg_reset(DeviceState *dev) -{ - STM32F2XXSyscfgState *s = STM32F2XX_SYSCFG(dev); - - s->syscfg_memrmp = 0x00000000; - s->syscfg_pmc = 0x00000000; - s->syscfg_exticr1 = 0x00000000; - s->syscfg_exticr2 = 0x00000000; - s->syscfg_exticr3 = 0x00000000; - s->syscfg_exticr4 = 0x00000000; - s->syscfg_cmpcr = 0x00000000; -} - -static uint64_t stm32f2xx_syscfg_read(void *opaque, hwaddr addr, - unsigned int size) -{ - STM32F2XXSyscfgState *s = opaque; - - DB_PRINT("0x%"HWADDR_PRIx"\n", addr); - - switch (addr) { - case SYSCFG_MEMRMP: - return s->syscfg_memrmp; - case SYSCFG_PMC: - return s->syscfg_pmc; - case SYSCFG_EXTICR1: - return s->syscfg_exticr1; - case SYSCFG_EXTICR2: - return s->syscfg_exticr2; - case SYSCFG_EXTICR3: - return s->syscfg_exticr3; - case SYSCFG_EXTICR4: - return s->syscfg_exticr4; - case SYSCFG_CMPCR: - return s->syscfg_cmpcr; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); - return 0; - } - - return 0; -} - -static void stm32f2xx_syscfg_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - STM32F2XXSyscfgState *s = opaque; - uint32_t value = val64; - - DB_PRINT("0x%x, 0x%"HWADDR_PRIx"\n", value, addr); - - switch (addr) { - case SYSCFG_MEMRMP: - qemu_log_mask(LOG_UNIMP, - "%s: Changeing the memory mapping isn't supported " \ - "in QEMU\n", __func__); - return; - case SYSCFG_PMC: - qemu_log_mask(LOG_UNIMP, - "%s: Changeing the memory mapping isn't supported " \ - "in QEMU\n", __func__); - return; - case SYSCFG_EXTICR1: - s->syscfg_exticr1 = (value & 0xFFFF); - return; - case SYSCFG_EXTICR2: - s->syscfg_exticr2 = (value & 0xFFFF); - return; - case SYSCFG_EXTICR3: - s->syscfg_exticr3 = (value & 0xFFFF); - return; - case SYSCFG_EXTICR4: - s->syscfg_exticr4 = (value & 0xFFFF); - return; - case SYSCFG_CMPCR: - s->syscfg_cmpcr = value; - return; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); - } -} - -static const MemoryRegionOps stm32f2xx_syscfg_ops = { - .read = stm32f2xx_syscfg_read, - .write = stm32f2xx_syscfg_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void stm32f2xx_syscfg_init(Object *obj) -{ - STM32F2XXSyscfgState *s = STM32F2XX_SYSCFG(obj); - - sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); - - memory_region_init_io(&s->mmio, obj, &stm32f2xx_syscfg_ops, s, - TYPE_STM32F2XX_SYSCFG, 0x400); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); -} - -static void stm32f2xx_syscfg_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = stm32f2xx_syscfg_reset; -} - -static const TypeInfo stm32f2xx_syscfg_info = { - .name = TYPE_STM32F2XX_SYSCFG, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(STM32F2XXSyscfgState), - .instance_init = stm32f2xx_syscfg_init, - .class_init = stm32f2xx_syscfg_class_init, -}; - -static void stm32f2xx_syscfg_register_types(void) -{ - type_register_static(&stm32f2xx_syscfg_info); -} - -type_init(stm32f2xx_syscfg_register_types) diff --git a/qemu/hw/misc/tmp105.c b/qemu/hw/misc/tmp105.c deleted file mode 100644 index f5c2472b5..000000000 --- a/qemu/hw/misc/tmp105.c +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Texas Instruments TMP105 temperature sensor. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#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) -{ - qemu_set_irq(s->pin, s->alarm ^ ((~s->config >> 2) & 1)); /* POL */ -} - -static void tmp105_alarm_update(TMP105State *s) -{ - if ((s->config >> 0) & 1) { /* SD */ - if ((s->config >> 7) & 1) /* OS */ - s->config &= ~(1 << 7); /* OS */ - else - return; - } - - if ((s->config >> 1) & 1) { /* TM */ - if (s->temperature >= s->limit[1]) - s->alarm = 1; - else if (s->temperature < s->limit[0]) - s->alarm = 1; - } else { - if (s->temperature >= s->limit[1]) - s->alarm = 1; - else if (s->temperature < s->limit[0]) - s->alarm = 0; - } - - tmp105_interrupt_update(s); -} - -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, 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, const char *name, - void *opaque, Error **errp) -{ - TMP105State *s = TMP105(obj); - Error *local_err = NULL; - int64_t temp; - - visit_type_int(v, name, &temp, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - if (temp >= 128000 || temp < -128000) { - error_setg(errp, "value %" PRId64 ".%03" PRIu64 " °C is out of range", - temp / 1000, temp % 1000); - return; - } - - s->temperature = (int16_t) (temp * 256 / 1000); - - tmp105_alarm_update(s); -} - -static const int tmp105_faultq[4] = { 1, 2, 4, 6 }; - -static void tmp105_read(TMP105State *s) -{ - s->len = 0; - - if ((s->config >> 1) & 1) { /* TM */ - s->alarm = 0; - tmp105_interrupt_update(s); - } - - switch (s->pointer & 3) { - case TMP105_REG_TEMPERATURE: - s->buf[s->len ++] = (((uint16_t) s->temperature) >> 8); - s->buf[s->len ++] = (((uint16_t) s->temperature) >> 0) & - (0xf0 << ((~s->config >> 5) & 3)); /* R */ - break; - - case TMP105_REG_CONFIG: - s->buf[s->len ++] = s->config; - break; - - case TMP105_REG_T_LOW: - s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 8; - s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 0; - break; - - case TMP105_REG_T_HIGH: - s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 8; - s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 0; - break; - } -} - -static void tmp105_write(TMP105State *s) -{ - switch (s->pointer & 3) { - case TMP105_REG_TEMPERATURE: - break; - - case TMP105_REG_CONFIG: - if (s->buf[0] & ~s->config & (1 << 0)) /* SD */ - printf("%s: TMP105 shutdown\n", __FUNCTION__); - s->config = s->buf[0]; - s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */ - tmp105_alarm_update(s); - break; - - case TMP105_REG_T_LOW: - case TMP105_REG_T_HIGH: - if (s->len >= 3) - s->limit[s->pointer & 1] = (int16_t) - ((((uint16_t) s->buf[0]) << 8) | s->buf[1]); - tmp105_alarm_update(s); - break; - } -} - -static int tmp105_rx(I2CSlave *i2c) -{ - TMP105State *s = TMP105(i2c); - - if (s->len < 2) { - return s->buf[s->len ++]; - } else { - return 0xff; - } -} - -static int tmp105_tx(I2CSlave *i2c, uint8_t data) -{ - TMP105State *s = TMP105(i2c); - - if (s->len == 0) { - s->pointer = data; - s->len++; - } else { - if (s->len <= 2) { - s->buf[s->len - 1] = data; - } - s->len++; - tmp105_write(s); - } - - return 0; -} - -static void tmp105_event(I2CSlave *i2c, enum i2c_event event) -{ - TMP105State *s = TMP105(i2c); - - if (event == I2C_START_RECV) { - tmp105_read(s); - } - - s->len = 0; -} - -static int tmp105_post_load(void *opaque, int version_id) -{ - TMP105State *s = opaque; - - s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */ - - tmp105_interrupt_update(s); - return 0; -} - -static const VMStateDescription vmstate_tmp105 = { - .name = "TMP105", - .version_id = 0, - .minimum_version_id = 0, - .post_load = tmp105_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(len, TMP105State), - VMSTATE_UINT8_ARRAY(buf, TMP105State, 2), - VMSTATE_UINT8(pointer, TMP105State), - VMSTATE_UINT8(config, TMP105State), - VMSTATE_INT16(temperature, TMP105State), - VMSTATE_INT16_ARRAY(limit, TMP105State, 2), - VMSTATE_UINT8(alarm, TMP105State), - VMSTATE_I2C_SLAVE(i2c, TMP105State), - VMSTATE_END_OF_LIST() - } -}; - -static void tmp105_reset(I2CSlave *i2c) -{ - TMP105State *s = TMP105(i2c); - - s->temperature = 0; - s->pointer = 0; - s->config = 0; - s->faults = tmp105_faultq[(s->config >> 3) & 3]; - s->alarm = 0; - - tmp105_interrupt_update(s); -} - -static int tmp105_init(I2CSlave *i2c) -{ - TMP105State *s = TMP105(i2c); - - qdev_init_gpio_out(&i2c->qdev, &s->pin, 1); - - tmp105_reset(&s->i2c); - - return 0; -} - -static void tmp105_initfn(Object *obj) -{ - object_property_add(obj, "temperature", "int", - tmp105_get_temperature, - tmp105_set_temperature, NULL, NULL, NULL); -} - -static void tmp105_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - - k->init = tmp105_init; - k->event = tmp105_event; - k->recv = tmp105_rx; - k->send = tmp105_tx; - dc->vmsd = &vmstate_tmp105; -} - -static const TypeInfo tmp105_info = { - .name = TYPE_TMP105, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(TMP105State), - .instance_init = tmp105_initfn, - .class_init = tmp105_class_init, -}; - -static void tmp105_register_types(void) -{ - type_register_static(&tmp105_info); -} - -type_init(tmp105_register_types) diff --git a/qemu/hw/misc/tmp105.h b/qemu/hw/misc/tmp105.h deleted file mode 100644 index 9ba05ecc9..000000000 --- a/qemu/hw/misc/tmp105.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Texas Instruments TMP105 Temperature Sensor - * - * Browse the data sheet: - * - * http://www.ti.com/lit/gpn/tmp105 - * - * Copyright (C) 2012 Alex Horn - * Copyright (C) 2008-2012 Andrzej Zaborowski - * - * This work is licensed under the terms of the GNU GPL, version 2 or - * later. See the COPYING file in the top-level directory. - */ -#ifndef QEMU_TMP105_H -#define QEMU_TMP105_H - -#include "hw/i2c/i2c.h" -#include "hw/misc/tmp105_regs.h" - -#define TYPE_TMP105 "tmp105" -#define TMP105(obj) OBJECT_CHECK(TMP105State, (obj), TYPE_TMP105) - -/** - * TMP105State: - * @config: Bits 5 and 6 (value 32 and 64) determine the precision of the - * temperature. See Table 8 in the data sheet. - * - * @see_also: http://www.ti.com/lit/gpn/tmp105 - */ -typedef struct TMP105State { - /*< private >*/ - I2CSlave i2c; - /*< public >*/ - - uint8_t len; - uint8_t buf[2]; - qemu_irq pin; - - uint8_t pointer; - uint8_t config; - int16_t temperature; - int16_t limit[2]; - int faults; - uint8_t alarm; -} TMP105State; - -#endif diff --git a/qemu/hw/misc/vmport.c b/qemu/hw/misc/vmport.c deleted file mode 100644 index 689678980..000000000 --- a/qemu/hw/misc/vmport.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * QEMU VMPort emulation - * - * Copyright (C) 2007 Hervé Poussineau - * - * 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/isa/isa.h" -#include "hw/i386/pc.h" -#include "sysemu/kvm.h" -#include "hw/qdev.h" - -//#define VMPORT_DEBUG - -#define VMPORT_CMD_GETVERSION 0x0a -#define VMPORT_CMD_GETRAMSIZE 0x14 - -#define VMPORT_ENTRIES 0x2c -#define VMPORT_MAGIC 0x564D5868 - -#define TYPE_VMPORT "vmport" -#define VMPORT(obj) OBJECT_CHECK(VMPortState, (obj), TYPE_VMPORT) - -typedef struct VMPortState -{ - ISADevice parent_obj; - - MemoryRegion io; - VMPortReadFunc *func[VMPORT_ENTRIES]; - void *opaque[VMPORT_ENTRIES]; -} VMPortState; - -static VMPortState *port_state; - -void vmport_register(unsigned char command, VMPortReadFunc *func, void *opaque) -{ - if (command >= VMPORT_ENTRIES) - return; - - port_state->func[command] = func; - port_state->opaque[command] = opaque; -} - -static uint64_t vmport_ioport_read(void *opaque, hwaddr addr, - unsigned size) -{ - VMPortState *s = opaque; - CPUState *cs = current_cpu; - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - unsigned char command; - uint32_t eax; - - cpu_synchronize_state(cs); - - eax = env->regs[R_EAX]; - if (eax != VMPORT_MAGIC) - return eax; - - command = env->regs[R_ECX]; - if (command >= VMPORT_ENTRIES) - return eax; - if (!s->func[command]) - { -#ifdef VMPORT_DEBUG - fprintf(stderr, "vmport: unknown command %x\n", command); -#endif - return eax; - } - - return s->func[command](s->opaque[command], addr); -} - -static void vmport_ioport_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - X86CPU *cpu = X86_CPU(current_cpu); - - cpu->env.regs[R_EAX] = vmport_ioport_read(opaque, addr, 4); -} - -static uint32_t vmport_cmd_get_version(void *opaque, uint32_t addr) -{ - X86CPU *cpu = X86_CPU(current_cpu); - - cpu->env.regs[R_EBX] = VMPORT_MAGIC; - return 6; -} - -static uint32_t vmport_cmd_ram_size(void *opaque, uint32_t addr) -{ - X86CPU *cpu = X86_CPU(current_cpu); - - cpu->env.regs[R_EBX] = 0x1177; - return ram_size; -} - -/* vmmouse helpers */ -void vmmouse_get_data(uint32_t *data) -{ - X86CPU *cpu = X86_CPU(current_cpu); - CPUX86State *env = &cpu->env; - - data[0] = env->regs[R_EAX]; data[1] = env->regs[R_EBX]; - data[2] = env->regs[R_ECX]; data[3] = env->regs[R_EDX]; - data[4] = env->regs[R_ESI]; data[5] = env->regs[R_EDI]; -} - -void vmmouse_set_data(const uint32_t *data) -{ - X86CPU *cpu = X86_CPU(current_cpu); - CPUX86State *env = &cpu->env; - - env->regs[R_EAX] = data[0]; env->regs[R_EBX] = data[1]; - env->regs[R_ECX] = data[2]; env->regs[R_EDX] = data[3]; - env->regs[R_ESI] = data[4]; env->regs[R_EDI] = data[5]; -} - -static const MemoryRegionOps vmport_ops = { - .read = vmport_ioport_read, - .write = vmport_ioport_write, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void vmport_realizefn(DeviceState *dev, Error **errp) -{ - ISADevice *isadev = ISA_DEVICE(dev); - VMPortState *s = VMPORT(dev); - - memory_region_init_io(&s->io, OBJECT(s), &vmport_ops, s, "vmport", 1); - isa_register_ioport(isadev, &s->io, 0x5658); - - port_state = s; - /* Register some generic port commands */ - vmport_register(VMPORT_CMD_GETVERSION, vmport_cmd_get_version, NULL); - vmport_register(VMPORT_CMD_GETRAMSIZE, vmport_cmd_ram_size, NULL); -} - -static void vmport_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = vmport_realizefn; - /* Reason: realize sets global port_state */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo vmport_info = { - .name = TYPE_VMPORT, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(VMPortState), - .class_init = vmport_class_initfn, -}; - -static void vmport_register_types(void) -{ - type_register_static(&vmport_info); -} - -type_init(vmport_register_types) diff --git a/qemu/hw/misc/zynq-xadc.c b/qemu/hw/misc/zynq-xadc.c deleted file mode 100644 index 71fbccd79..000000000 --- a/qemu/hw/misc/zynq-xadc.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * 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 . - */ - -#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 deleted file mode 100644 index b1b7591ef..000000000 --- a/qemu/hw/misc/zynq_slcr.c +++ /dev/null @@ -1,459 +0,0 @@ -/* - * Status and system control registers for Xilinx Zynq Platform - * - * Copyright (c) 2011 Michal Simek - * Copyright (c) 2012 PetaLogix Pty Ltd. - * Based on hw/arm_sysctl.c, written by Paul Brook - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "qemu/timer.h" -#include "hw/sysbus.h" -#include "sysemu/sysemu.h" - -#ifndef ZYNQ_SLCR_ERR_DEBUG -#define ZYNQ_SLCR_ERR_DEBUG 0 -#endif - -#define DB_PRINT(...) do { \ - if (ZYNQ_SLCR_ERR_DEBUG) { \ - fprintf(stderr, ": %s: ", __func__); \ - fprintf(stderr, ## __VA_ARGS__); \ - } \ - } while (0); - -#define XILINX_LOCK_KEY 0x767b -#define XILINX_UNLOCK_KEY 0xdf0d - -#define R_PSS_RST_CTRL_SOFT_RST 0x1 - -enum { - SCL = 0x000 / 4, - LOCK, - UNLOCK, - LOCKSTA, - - ARM_PLL_CTRL = 0x100 / 4, - DDR_PLL_CTRL, - IO_PLL_CTRL, - PLL_STATUS, - ARM_PLL_CFG, - DDR_PLL_CFG, - IO_PLL_CFG, - - ARM_CLK_CTRL = 0x120 / 4, - DDR_CLK_CTRL, - DCI_CLK_CTRL, - APER_CLK_CTRL, - USB0_CLK_CTRL, - USB1_CLK_CTRL, - GEM0_RCLK_CTRL, - GEM1_RCLK_CTRL, - GEM0_CLK_CTRL, - GEM1_CLK_CTRL, - SMC_CLK_CTRL, - LQSPI_CLK_CTRL, - SDIO_CLK_CTRL, - UART_CLK_CTRL, - SPI_CLK_CTRL, - CAN_CLK_CTRL, - CAN_MIOCLK_CTRL, - DBG_CLK_CTRL, - PCAP_CLK_CTRL, - TOPSW_CLK_CTRL, - -#define FPGA_CTRL_REGS(n, start) \ - FPGA ## n ## _CLK_CTRL = (start) / 4, \ - FPGA ## n ## _THR_CTRL, \ - FPGA ## n ## _THR_CNT, \ - FPGA ## n ## _THR_STA, - FPGA_CTRL_REGS(0, 0x170) - FPGA_CTRL_REGS(1, 0x180) - FPGA_CTRL_REGS(2, 0x190) - FPGA_CTRL_REGS(3, 0x1a0) - - BANDGAP_TRIP = 0x1b8 / 4, - PLL_PREDIVISOR = 0x1c0 / 4, - CLK_621_TRUE, - - PSS_RST_CTRL = 0x200 / 4, - DDR_RST_CTRL, - TOPSW_RESET_CTRL, - DMAC_RST_CTRL, - USB_RST_CTRL, - GEM_RST_CTRL, - SDIO_RST_CTRL, - SPI_RST_CTRL, - CAN_RST_CTRL, - I2C_RST_CTRL, - UART_RST_CTRL, - GPIO_RST_CTRL, - LQSPI_RST_CTRL, - SMC_RST_CTRL, - OCM_RST_CTRL, - FPGA_RST_CTRL = 0x240 / 4, - A9_CPU_RST_CTRL, - - RS_AWDT_CTRL = 0x24c / 4, - RST_REASON, - - REBOOT_STATUS = 0x258 / 4, - BOOT_MODE, - - APU_CTRL = 0x300 / 4, - WDT_CLK_SEL, - - TZ_DMA_NS = 0x440 / 4, - TZ_DMA_IRQ_NS, - TZ_DMA_PERIPH_NS, - - PSS_IDCODE = 0x530 / 4, - - DDR_URGENT = 0x600 / 4, - DDR_CAL_START = 0x60c / 4, - DDR_REF_START = 0x614 / 4, - DDR_CMD_STA, - DDR_URGENT_SEL, - DDR_DFI_STATUS, - - MIO = 0x700 / 4, -#define MIO_LENGTH 54 - - MIO_LOOPBACK = 0x804 / 4, - MIO_MST_TRI0, - MIO_MST_TRI1, - - SD0_WP_CD_SEL = 0x830 / 4, - SD1_WP_CD_SEL, - - LVL_SHFTR_EN = 0x900 / 4, - OCM_CFG = 0x910 / 4, - - CPU_RAM = 0xa00 / 4, - - IOU = 0xa30 / 4, - - DMAC_RAM = 0xa50 / 4, - - AFI0 = 0xa60 / 4, - AFI1 = AFI0 + 3, - AFI2 = AFI1 + 3, - AFI3 = AFI2 + 3, -#define AFI_LENGTH 3 - - OCM = 0xa90 / 4, - - DEVCI_RAM = 0xaa0 / 4, - - CSG_RAM = 0xab0 / 4, - - GPIOB_CTRL = 0xb00 / 4, - GPIOB_CFG_CMOS18, - GPIOB_CFG_CMOS25, - GPIOB_CFG_CMOS33, - GPIOB_CFG_HSTL = 0xb14 / 4, - GPIOB_DRVR_BIAS_CTRL, - - DDRIOB = 0xb40 / 4, -#define DDRIOB_LENGTH 14 -}; - -#define ZYNQ_SLCR_MMIO_SIZE 0x1000 -#define ZYNQ_SLCR_NUM_REGS (ZYNQ_SLCR_MMIO_SIZE / 4) - -#define TYPE_ZYNQ_SLCR "xilinx,zynq_slcr" -#define ZYNQ_SLCR(obj) OBJECT_CHECK(ZynqSLCRState, (obj), TYPE_ZYNQ_SLCR) - -typedef struct ZynqSLCRState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - - uint32_t regs[ZYNQ_SLCR_NUM_REGS]; -} ZynqSLCRState; - -static void zynq_slcr_reset(DeviceState *d) -{ - ZynqSLCRState *s = ZYNQ_SLCR(d); - int i; - - DB_PRINT("RESET\n"); - - s->regs[LOCKSTA] = 1; - /* 0x100 - 0x11C */ - s->regs[ARM_PLL_CTRL] = 0x0001A008; - s->regs[DDR_PLL_CTRL] = 0x0001A008; - s->regs[IO_PLL_CTRL] = 0x0001A008; - s->regs[PLL_STATUS] = 0x0000003F; - s->regs[ARM_PLL_CFG] = 0x00014000; - s->regs[DDR_PLL_CFG] = 0x00014000; - s->regs[IO_PLL_CFG] = 0x00014000; - - /* 0x120 - 0x16C */ - s->regs[ARM_CLK_CTRL] = 0x1F000400; - s->regs[DDR_CLK_CTRL] = 0x18400003; - s->regs[DCI_CLK_CTRL] = 0x01E03201; - s->regs[APER_CLK_CTRL] = 0x01FFCCCD; - s->regs[USB0_CLK_CTRL] = s->regs[USB1_CLK_CTRL] = 0x00101941; - s->regs[GEM0_RCLK_CTRL] = s->regs[GEM1_RCLK_CTRL] = 0x00000001; - s->regs[GEM0_CLK_CTRL] = s->regs[GEM1_CLK_CTRL] = 0x00003C01; - s->regs[SMC_CLK_CTRL] = 0x00003C01; - s->regs[LQSPI_CLK_CTRL] = 0x00002821; - s->regs[SDIO_CLK_CTRL] = 0x00001E03; - s->regs[UART_CLK_CTRL] = 0x00003F03; - s->regs[SPI_CLK_CTRL] = 0x00003F03; - s->regs[CAN_CLK_CTRL] = 0x00501903; - s->regs[DBG_CLK_CTRL] = 0x00000F03; - s->regs[PCAP_CLK_CTRL] = 0x00000F01; - - /* 0x170 - 0x1AC */ - s->regs[FPGA0_CLK_CTRL] = s->regs[FPGA1_CLK_CTRL] = s->regs[FPGA2_CLK_CTRL] - = s->regs[FPGA3_CLK_CTRL] = 0x00101800; - s->regs[FPGA0_THR_STA] = s->regs[FPGA1_THR_STA] = s->regs[FPGA2_THR_STA] - = s->regs[FPGA3_THR_STA] = 0x00010000; - - /* 0x1B0 - 0x1D8 */ - s->regs[BANDGAP_TRIP] = 0x0000001F; - s->regs[PLL_PREDIVISOR] = 0x00000001; - s->regs[CLK_621_TRUE] = 0x00000001; - - /* 0x200 - 0x25C */ - s->regs[FPGA_RST_CTRL] = 0x01F33F0F; - s->regs[RST_REASON] = 0x00000040; - - s->regs[BOOT_MODE] = 0x00000001; - - /* 0x700 - 0x7D4 */ - for (i = 0; i < 54; i++) { - s->regs[MIO + i] = 0x00001601; - } - for (i = 2; i <= 8; i++) { - s->regs[MIO + i] = 0x00000601; - } - - s->regs[MIO_MST_TRI0] = s->regs[MIO_MST_TRI1] = 0xFFFFFFFF; - - s->regs[CPU_RAM + 0] = s->regs[CPU_RAM + 1] = s->regs[CPU_RAM + 3] - = s->regs[CPU_RAM + 4] = s->regs[CPU_RAM + 7] - = 0x00010101; - s->regs[CPU_RAM + 2] = s->regs[CPU_RAM + 5] = 0x01010101; - s->regs[CPU_RAM + 6] = 0x00000001; - - s->regs[IOU + 0] = s->regs[IOU + 1] = s->regs[IOU + 2] = s->regs[IOU + 3] - = 0x09090909; - s->regs[IOU + 4] = s->regs[IOU + 5] = 0x00090909; - s->regs[IOU + 6] = 0x00000909; - - s->regs[DMAC_RAM] = 0x00000009; - - s->regs[AFI0 + 0] = s->regs[AFI0 + 1] = 0x09090909; - s->regs[AFI1 + 0] = s->regs[AFI1 + 1] = 0x09090909; - s->regs[AFI2 + 0] = s->regs[AFI2 + 1] = 0x09090909; - s->regs[AFI3 + 0] = s->regs[AFI3 + 1] = 0x09090909; - s->regs[AFI0 + 2] = s->regs[AFI1 + 2] = s->regs[AFI2 + 2] - = s->regs[AFI3 + 2] = 0x00000909; - - s->regs[OCM + 0] = 0x01010101; - s->regs[OCM + 1] = s->regs[OCM + 2] = 0x09090909; - - s->regs[DEVCI_RAM] = 0x00000909; - s->regs[CSG_RAM] = 0x00000001; - - s->regs[DDRIOB + 0] = s->regs[DDRIOB + 1] = s->regs[DDRIOB + 2] - = s->regs[DDRIOB + 3] = 0x00000e00; - s->regs[DDRIOB + 4] = s->regs[DDRIOB + 5] = s->regs[DDRIOB + 6] - = 0x00000e00; - s->regs[DDRIOB + 12] = 0x00000021; -} - - -static bool zynq_slcr_check_offset(hwaddr offset, bool rnw) -{ - switch (offset) { - case LOCK: - case UNLOCK: - case DDR_CAL_START: - case DDR_REF_START: - return !rnw; /* Write only */ - case LOCKSTA: - case FPGA0_THR_STA: - case FPGA1_THR_STA: - case FPGA2_THR_STA: - case FPGA3_THR_STA: - case BOOT_MODE: - case PSS_IDCODE: - case DDR_CMD_STA: - case DDR_DFI_STATUS: - case PLL_STATUS: - return rnw;/* read only */ - case SCL: - case ARM_PLL_CTRL ... IO_PLL_CTRL: - case ARM_PLL_CFG ... IO_PLL_CFG: - case ARM_CLK_CTRL ... TOPSW_CLK_CTRL: - case FPGA0_CLK_CTRL ... FPGA0_THR_CNT: - case FPGA1_CLK_CTRL ... FPGA1_THR_CNT: - case FPGA2_CLK_CTRL ... FPGA2_THR_CNT: - case FPGA3_CLK_CTRL ... FPGA3_THR_CNT: - case BANDGAP_TRIP: - case PLL_PREDIVISOR: - case CLK_621_TRUE: - case PSS_RST_CTRL ... A9_CPU_RST_CTRL: - case RS_AWDT_CTRL: - case RST_REASON: - case REBOOT_STATUS: - case APU_CTRL: - case WDT_CLK_SEL: - case TZ_DMA_NS ... TZ_DMA_PERIPH_NS: - case DDR_URGENT: - case DDR_URGENT_SEL: - case MIO ... MIO + MIO_LENGTH - 1: - case MIO_LOOPBACK ... MIO_MST_TRI1: - case SD0_WP_CD_SEL: - case SD1_WP_CD_SEL: - case LVL_SHFTR_EN: - case OCM_CFG: - case CPU_RAM: - case IOU: - case DMAC_RAM: - case AFI0 ... AFI3 + AFI_LENGTH - 1: - case OCM: - case DEVCI_RAM: - case CSG_RAM: - case GPIOB_CTRL ... GPIOB_CFG_CMOS33: - case GPIOB_CFG_HSTL: - case GPIOB_DRVR_BIAS_CTRL: - case DDRIOB ... DDRIOB + DDRIOB_LENGTH - 1: - return true; - default: - return false; - } -} - -static uint64_t zynq_slcr_read(void *opaque, hwaddr offset, - unsigned size) -{ - ZynqSLCRState *s = opaque; - offset /= 4; - uint32_t ret = s->regs[offset]; - - if (!zynq_slcr_check_offset(offset, true)) { - qemu_log_mask(LOG_GUEST_ERROR, "zynq_slcr: Invalid read access to " - " addr %" HWADDR_PRIx "\n", offset * 4); - } - - DB_PRINT("addr: %08" HWADDR_PRIx " data: %08" PRIx32 "\n", offset * 4, ret); - return ret; -} - -static void zynq_slcr_write(void *opaque, hwaddr offset, - uint64_t val, unsigned size) -{ - ZynqSLCRState *s = (ZynqSLCRState *)opaque; - offset /= 4; - - DB_PRINT("addr: %08" HWADDR_PRIx " data: %08" PRIx64 "\n", offset * 4, val); - - if (!zynq_slcr_check_offset(offset, false)) { - qemu_log_mask(LOG_GUEST_ERROR, "zynq_slcr: Invalid write access to " - "addr %" HWADDR_PRIx "\n", offset * 4); - return; - } - - switch (offset) { - case SCL: - s->regs[SCL] = val & 0x1; - return; - case LOCK: - if ((val & 0xFFFF) == XILINX_LOCK_KEY) { - DB_PRINT("XILINX LOCK 0xF8000000 + 0x%x <= 0x%x\n", (int)offset, - (unsigned)val & 0xFFFF); - s->regs[LOCKSTA] = 1; - } else { - DB_PRINT("WRONG XILINX LOCK KEY 0xF8000000 + 0x%x <= 0x%x\n", - (int)offset, (unsigned)val & 0xFFFF); - } - return; - case UNLOCK: - if ((val & 0xFFFF) == XILINX_UNLOCK_KEY) { - DB_PRINT("XILINX UNLOCK 0xF8000000 + 0x%x <= 0x%x\n", (int)offset, - (unsigned)val & 0xFFFF); - s->regs[LOCKSTA] = 0; - } else { - DB_PRINT("WRONG XILINX UNLOCK KEY 0xF8000000 + 0x%x <= 0x%x\n", - (int)offset, (unsigned)val & 0xFFFF); - } - return; - } - - 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: - if (val & R_PSS_RST_CTRL_SOFT_RST) { - qemu_system_reset_request(); - } - break; - } -} - -static const MemoryRegionOps slcr_ops = { - .read = zynq_slcr_read, - .write = zynq_slcr_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void zynq_slcr_init(Object *obj) -{ - ZynqSLCRState *s = ZYNQ_SLCR(obj); - - memory_region_init_io(&s->iomem, obj, &slcr_ops, s, "slcr", - ZYNQ_SLCR_MMIO_SIZE); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); -} - -static const VMStateDescription vmstate_zynq_slcr = { - .name = "zynq_slcr", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, ZynqSLCRState, ZYNQ_SLCR_NUM_REGS), - VMSTATE_END_OF_LIST() - } -}; - -static void zynq_slcr_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_zynq_slcr; - dc->reset = zynq_slcr_reset; -} - -static const TypeInfo zynq_slcr_info = { - .class_init = zynq_slcr_class_init, - .name = TYPE_ZYNQ_SLCR, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ZynqSLCRState), - .instance_init = zynq_slcr_init, -}; - -static void zynq_slcr_register_types(void) -{ - type_register_static(&zynq_slcr_info); -} - -type_init(zynq_slcr_register_types) diff --git a/qemu/hw/moxie/Makefile.objs b/qemu/hw/moxie/Makefile.objs deleted file mode 100644 index bfc90012f..000000000 --- a/qemu/hw/moxie/Makefile.objs +++ /dev/null @@ -1,2 +0,0 @@ -# moxie boards -obj-y += moxiesim.o diff --git a/qemu/hw/moxie/moxiesim.c b/qemu/hw/moxie/moxiesim.c deleted file mode 100644 index 3069834cf..000000000 --- a/qemu/hw/moxie/moxiesim.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * QEMU/moxiesim emulation - * - * Emulates a very simple machine model similar to the one used by the - * GDB moxie simulator. - * - * Copyright (c) 2008, 2009, 2010, 2013 Anthony Green - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/sysbus.h" -#include "hw/hw.h" -#include "hw/i386/pc.h" -#include "hw/isa/isa.h" -#include "net/net.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" -#include "hw/loader.h" -#include "hw/char/serial.h" -#include "exec/address-spaces.h" -#include "elf.h" - -#define PHYS_MEM_BASE 0x80000000 - -typedef struct { - uint64_t ram_size; - const char *kernel_filename; - const char *kernel_cmdline; - const char *initrd_filename; -} LoaderParams; - -static void load_kernel(MoxieCPU *cpu, LoaderParams *loader_params) -{ - uint64_t entry, kernel_low, kernel_high; - long kernel_size; - long initrd_size; - ram_addr_t initrd_offset; - - kernel_size = load_elf(loader_params->kernel_filename, NULL, NULL, - &entry, &kernel_low, &kernel_high, 1, EM_MOXIE, - 0, 0); - - if (kernel_size <= 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - loader_params->kernel_filename); - exit(1); - } - - /* load initrd */ - initrd_size = 0; - initrd_offset = 0; - if (loader_params->initrd_filename) { - initrd_size = get_image_size(loader_params->initrd_filename); - if (initrd_size > 0) { - initrd_offset = (kernel_high + ~TARGET_PAGE_MASK) - & TARGET_PAGE_MASK; - if (initrd_offset + initrd_size > loader_params->ram_size) { - fprintf(stderr, - "qemu: memory too small for initial ram disk '%s'\n", - loader_params->initrd_filename); - exit(1); - } - initrd_size = load_image_targphys(loader_params->initrd_filename, - initrd_offset, - ram_size); - } - if (initrd_size == (target_ulong)-1) { - fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", - loader_params->initrd_filename); - exit(1); - } - } -} - -static void main_cpu_reset(void *opaque) -{ - MoxieCPU *cpu = opaque; - - cpu_reset(CPU(cpu)); -} - -static void moxiesim_init(MachineState *machine) -{ - MoxieCPU *cpu = NULL; - 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; - CPUMoxieState *env; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *rom = g_new(MemoryRegion, 1); - hwaddr ram_base = 0x200000; - LoaderParams loader_params; - - /* Init CPUs. */ - if (cpu_model == NULL) { - cpu_model = "MoxieLite-moxie-cpu"; - } - cpu = cpu_moxie_init(cpu_model); - if (!cpu) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - env = &cpu->env; - - qemu_register_reset(main_cpu_reset, cpu); - - /* Allocate RAM. */ - 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_fatal); - vmstate_register_ram_global(rom); - memory_region_add_subregion(get_system_memory(), 0x1000, rom); - - if (kernel_filename) { - loader_params.ram_size = ram_size; - loader_params.kernel_filename = kernel_filename; - loader_params.kernel_cmdline = kernel_cmdline; - loader_params.initrd_filename = initrd_filename; - load_kernel(cpu, &loader_params); - } - - /* A single 16450 sits at offset 0x3f8. */ - if (serial_hds[0]) { - serial_mm_init(address_space_mem, 0x3f8, 0, env->irq[4], - 8000000/16, serial_hds[0], DEVICE_LITTLE_ENDIAN); - } -} - -static void moxiesim_machine_init(MachineClass *mc) -{ - mc->desc = "Moxie simulator platform"; - mc->init = moxiesim_init; - mc->is_default = 1; -} - -DEFINE_MACHINE("moxiesim", moxiesim_machine_init) diff --git a/qemu/hw/net/Makefile.objs b/qemu/hw/net/Makefile.objs deleted file mode 100644 index 64d044923..000000000 --- a/qemu/hw/net/Makefile.objs +++ /dev/null @@ -1,43 +0,0 @@ -common-obj-$(CONFIG_DP8393X) += dp8393x.o -common-obj-$(CONFIG_XEN_BACKEND) += xen_nic.o - -# PCI network cards -common-obj-$(CONFIG_NE2000_PCI) += ne2000.o -common-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o -common-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o -common-obj-$(CONFIG_PCNET_COMMON) += pcnet.o -common-obj-$(CONFIG_E1000_PCI) += e1000.o -common-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o -common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet_tx_pkt.o vmxnet_rx_pkt.o -common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet3.o - -common-obj-$(CONFIG_SMC91C111) += smc91c111.o -common-obj-$(CONFIG_LAN9118) += lan9118.o -common-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o -common-obj-$(CONFIG_OPENCORES_ETH) += opencores_eth.o -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 -common-obj-$(CONFIG_LANCE) += lance.o - -obj-$(CONFIG_ETRAXFS) += etraxfs_eth.o -obj-$(CONFIG_COLDFIRE) += mcf_fec.o -obj-$(CONFIG_MILKYMIST) += milkymist-minimac2.o -obj-$(CONFIG_PSERIES) += spapr_llan.o -obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o - -obj-$(CONFIG_VIRTIO) += virtio-net.o -obj-y += vhost_net.o - -obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \ - fsl_etsec/rings.o fsl_etsec/miim.o - -common-obj-$(CONFIG_ROCKER) += rocker/rocker.o rocker/rocker_fp.o \ - rocker/rocker_desc.o rocker/rocker_world.o \ - rocker/rocker_of_dpa.o -obj-$(call lnot,$(CONFIG_ROCKER)) += rocker/qmp-norocker.o diff --git a/qemu/hw/net/allwinner_emac.c b/qemu/hw/net/allwinner_emac.c deleted file mode 100644 index 16d4b63ba..000000000 --- a/qemu/hw/net/allwinner_emac.c +++ /dev/null @@ -1,534 +0,0 @@ -/* - * Emulation of Allwinner EMAC Fast Ethernet controller and - * Realtek RTL8201CP PHY - * - * Copyright (C) 2014 Beniamino Galvani - * - * This model is based on reverse-engineering of Linux kernel driver. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "net/net.h" -#include "qemu/fifo8.h" -#include "hw/net/allwinner_emac.h" -#include - -static uint8_t padding[60]; - -static void mii_set_link(RTL8201CPState *mii, bool link_ok) -{ - if (link_ok) { - mii->bmsr |= MII_BMSR_LINK_ST | MII_BMSR_AN_COMP; - mii->anlpar |= MII_ANAR_TXFD | MII_ANAR_10FD | MII_ANAR_10 | - MII_ANAR_CSMACD; - } else { - mii->bmsr &= ~(MII_BMSR_LINK_ST | MII_BMSR_AN_COMP); - mii->anlpar = MII_ANAR_TX; - } -} - -static void mii_reset(RTL8201CPState *mii, bool link_ok) -{ - mii->bmcr = MII_BMCR_FD | MII_BMCR_AUTOEN | MII_BMCR_SPEED; - mii->bmsr = MII_BMSR_100TX_FD | MII_BMSR_100TX_HD | MII_BMSR_10T_FD | - MII_BMSR_10T_HD | MII_BMSR_MFPS | MII_BMSR_AUTONEG; - mii->anar = MII_ANAR_TXFD | MII_ANAR_TX | MII_ANAR_10FD | MII_ANAR_10 | - MII_ANAR_CSMACD; - mii->anlpar = MII_ANAR_TX; - - mii_set_link(mii, link_ok); -} - -static uint16_t RTL8201CP_mdio_read(AwEmacState *s, uint8_t addr, uint8_t reg) -{ - RTL8201CPState *mii = &s->mii; - uint16_t ret = 0xffff; - - if (addr == s->phy_addr) { - switch (reg) { - case MII_BMCR: - return mii->bmcr; - case MII_BMSR: - return mii->bmsr; - case MII_PHYID1: - return RTL8201CP_PHYID1; - case MII_PHYID2: - return RTL8201CP_PHYID2; - case MII_ANAR: - return mii->anar; - case MII_ANLPAR: - return mii->anlpar; - case MII_ANER: - case MII_NSR: - case MII_LBREMR: - case MII_REC: - case MII_SNRDR: - case MII_TEST: - qemu_log_mask(LOG_UNIMP, - "allwinner_emac: read from unimpl. mii reg 0x%x\n", - reg); - return 0; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "allwinner_emac: read from invalid mii reg 0x%x\n", - reg); - return 0; - } - } - return ret; -} - -static void RTL8201CP_mdio_write(AwEmacState *s, uint8_t addr, uint8_t reg, - uint16_t value) -{ - RTL8201CPState *mii = &s->mii; - NetClientState *nc; - - if (addr == s->phy_addr) { - switch (reg) { - case MII_BMCR: - if (value & MII_BMCR_RESET) { - nc = qemu_get_queue(s->nic); - mii_reset(mii, !nc->link_down); - } else { - mii->bmcr = value; - } - break; - case MII_ANAR: - mii->anar = value; - break; - case MII_BMSR: - case MII_PHYID1: - case MII_PHYID2: - case MII_ANLPAR: - case MII_ANER: - qemu_log_mask(LOG_GUEST_ERROR, - "allwinner_emac: write to read-only mii reg 0x%x\n", - reg); - break; - case MII_NSR: - case MII_LBREMR: - case MII_REC: - case MII_SNRDR: - case MII_TEST: - qemu_log_mask(LOG_UNIMP, - "allwinner_emac: write to unimpl. mii reg 0x%x\n", - reg); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "allwinner_emac: write to invalid mii reg 0x%x\n", - reg); - } - } -} - -static void aw_emac_update_irq(AwEmacState *s) -{ - qemu_set_irq(s->irq, (s->int_sta & s->int_ctl) != 0); -} - -static void aw_emac_tx_reset(AwEmacState *s, int chan) -{ - fifo8_reset(&s->tx_fifo[chan]); - s->tx_length[chan] = 0; -} - -static void aw_emac_rx_reset(AwEmacState *s) -{ - fifo8_reset(&s->rx_fifo); - s->rx_num_packets = 0; - s->rx_packet_size = 0; - s->rx_packet_pos = 0; -} - -static void fifo8_push_word(Fifo8 *fifo, uint32_t val) -{ - fifo8_push(fifo, val); - fifo8_push(fifo, val >> 8); - fifo8_push(fifo, val >> 16); - fifo8_push(fifo, val >> 24); -} - -static uint32_t fifo8_pop_word(Fifo8 *fifo) -{ - uint32_t ret; - - ret = fifo8_pop(fifo); - ret |= fifo8_pop(fifo) << 8; - ret |= fifo8_pop(fifo) << 16; - ret |= fifo8_pop(fifo) << 24; - - return ret; -} - -static int aw_emac_can_receive(NetClientState *nc) -{ - AwEmacState *s = qemu_get_nic_opaque(nc); - - /* - * To avoid packet drops, allow reception only when there is space - * for a full frame: 1522 + 8 (rx headers) + 2 (padding). - */ - return (s->ctl & EMAC_CTL_RX_EN) && (fifo8_num_free(&s->rx_fifo) >= 1532); -} - -static ssize_t aw_emac_receive(NetClientState *nc, const uint8_t *buf, - size_t size) -{ - AwEmacState *s = qemu_get_nic_opaque(nc); - Fifo8 *fifo = &s->rx_fifo; - size_t padded_size, total_size; - uint32_t crc; - - padded_size = size > 60 ? size : 60; - total_size = QEMU_ALIGN_UP(RX_HDR_SIZE + padded_size + CRC_SIZE, 4); - - if (!(s->ctl & EMAC_CTL_RX_EN) || (fifo8_num_free(fifo) < total_size)) { - return -1; - } - - fifo8_push_word(fifo, EMAC_UNDOCUMENTED_MAGIC); - fifo8_push_word(fifo, EMAC_RX_HEADER(padded_size + CRC_SIZE, - EMAC_RX_IO_DATA_STATUS_OK)); - fifo8_push_all(fifo, buf, size); - crc = crc32(~0, buf, size); - - if (padded_size != size) { - fifo8_push_all(fifo, padding, padded_size - size); - crc = crc32(crc, padding, padded_size - size); - } - - fifo8_push_word(fifo, crc); - fifo8_push_all(fifo, padding, QEMU_ALIGN_UP(padded_size, 4) - padded_size); - s->rx_num_packets++; - - s->int_sta |= EMAC_INT_RX; - aw_emac_update_irq(s); - - return size; -} - -static void aw_emac_reset(DeviceState *dev) -{ - AwEmacState *s = AW_EMAC(dev); - NetClientState *nc = qemu_get_queue(s->nic); - - s->ctl = 0; - s->tx_mode = 0; - s->int_ctl = 0; - s->int_sta = 0; - s->tx_channel = 0; - s->phy_target = 0; - - aw_emac_tx_reset(s, 0); - aw_emac_tx_reset(s, 1); - aw_emac_rx_reset(s); - - mii_reset(&s->mii, !nc->link_down); -} - -static uint64_t aw_emac_read(void *opaque, hwaddr offset, unsigned size) -{ - AwEmacState *s = opaque; - Fifo8 *fifo = &s->rx_fifo; - NetClientState *nc; - uint64_t ret; - - switch (offset) { - case EMAC_CTL_REG: - return s->ctl; - case EMAC_TX_MODE_REG: - return s->tx_mode; - case EMAC_TX_INS_REG: - return s->tx_channel; - case EMAC_RX_CTL_REG: - return s->rx_ctl; - case EMAC_RX_IO_DATA_REG: - if (!s->rx_num_packets) { - qemu_log_mask(LOG_GUEST_ERROR, - "Read IO data register when no packet available"); - return 0; - } - - ret = fifo8_pop_word(fifo); - - switch (s->rx_packet_pos) { - case 0: /* Word is magic header */ - s->rx_packet_pos += 4; - break; - case 4: /* Word is rx info header */ - s->rx_packet_pos += 4; - s->rx_packet_size = QEMU_ALIGN_UP(extract32(ret, 0, 16), 4); - break; - default: /* Word is packet data */ - s->rx_packet_pos += 4; - s->rx_packet_size -= 4; - - if (!s->rx_packet_size) { - s->rx_packet_pos = 0; - s->rx_num_packets--; - nc = qemu_get_queue(s->nic); - if (aw_emac_can_receive(nc)) { - qemu_flush_queued_packets(nc); - } - } - } - return ret; - case EMAC_RX_FBC_REG: - return s->rx_num_packets; - case EMAC_INT_CTL_REG: - return s->int_ctl; - case EMAC_INT_STA_REG: - return s->int_sta; - case EMAC_MAC_MRDD_REG: - return RTL8201CP_mdio_read(s, - extract32(s->phy_target, PHY_ADDR_SHIFT, 8), - extract32(s->phy_target, PHY_REG_SHIFT, 8)); - default: - qemu_log_mask(LOG_UNIMP, - "allwinner_emac: read access to unknown register 0x" - TARGET_FMT_plx "\n", offset); - ret = 0; - } - - return ret; -} - -static void aw_emac_write(void *opaque, hwaddr offset, uint64_t value, - unsigned size) -{ - AwEmacState *s = opaque; - Fifo8 *fifo; - NetClientState *nc = qemu_get_queue(s->nic); - int chan; - - switch (offset) { - case EMAC_CTL_REG: - if (value & EMAC_CTL_RESET) { - aw_emac_reset(DEVICE(s)); - value &= ~EMAC_CTL_RESET; - } - s->ctl = value; - if (aw_emac_can_receive(nc)) { - qemu_flush_queued_packets(nc); - } - break; - case EMAC_TX_MODE_REG: - s->tx_mode = value; - break; - case EMAC_TX_CTL0_REG: - case EMAC_TX_CTL1_REG: - chan = (offset == EMAC_TX_CTL0_REG ? 0 : 1); - if ((value & 1) && (s->ctl & EMAC_CTL_TX_EN)) { - uint32_t len, ret; - const uint8_t *data; - - fifo = &s->tx_fifo[chan]; - len = s->tx_length[chan]; - - if (len > fifo8_num_used(fifo)) { - len = fifo8_num_used(fifo); - qemu_log_mask(LOG_GUEST_ERROR, - "allwinner_emac: TX length > fifo data length\n"); - } - if (len > 0) { - data = fifo8_pop_buf(fifo, len, &ret); - qemu_send_packet(nc, data, ret); - aw_emac_tx_reset(s, chan); - /* Raise TX interrupt */ - s->int_sta |= EMAC_INT_TX_CHAN(chan); - aw_emac_update_irq(s); - } - } - break; - case EMAC_TX_INS_REG: - s->tx_channel = value < NUM_TX_FIFOS ? value : 0; - break; - case EMAC_TX_PL0_REG: - case EMAC_TX_PL1_REG: - chan = (offset == EMAC_TX_PL0_REG ? 0 : 1); - if (value > TX_FIFO_SIZE) { - qemu_log_mask(LOG_GUEST_ERROR, - "allwinner_emac: invalid TX frame length %d\n", - (int)value); - value = TX_FIFO_SIZE; - } - s->tx_length[chan] = value; - break; - case EMAC_TX_IO_DATA_REG: - fifo = &s->tx_fifo[s->tx_channel]; - if (fifo8_num_free(fifo) < 4) { - qemu_log_mask(LOG_GUEST_ERROR, - "allwinner_emac: TX data overruns fifo\n"); - break; - } - fifo8_push_word(fifo, value); - break; - case EMAC_RX_CTL_REG: - s->rx_ctl = value; - break; - case EMAC_RX_FBC_REG: - if (value == 0) { - aw_emac_rx_reset(s); - } - break; - case EMAC_INT_CTL_REG: - s->int_ctl = value; - aw_emac_update_irq(s); - break; - case EMAC_INT_STA_REG: - s->int_sta &= ~value; - aw_emac_update_irq(s); - break; - case EMAC_MAC_MADR_REG: - s->phy_target = value; - break; - case EMAC_MAC_MWTD_REG: - RTL8201CP_mdio_write(s, extract32(s->phy_target, PHY_ADDR_SHIFT, 8), - extract32(s->phy_target, PHY_REG_SHIFT, 8), value); - break; - default: - qemu_log_mask(LOG_UNIMP, - "allwinner_emac: write access to unknown register 0x" - TARGET_FMT_plx "\n", offset); - } -} - -static void aw_emac_set_link(NetClientState *nc) -{ - AwEmacState *s = qemu_get_nic_opaque(nc); - - mii_set_link(&s->mii, !nc->link_down); -} - -static const MemoryRegionOps aw_emac_mem_ops = { - .read = aw_emac_read, - .write = aw_emac_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static NetClientInfo net_aw_emac_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .can_receive = aw_emac_can_receive, - .receive = aw_emac_receive, - .link_status_changed = aw_emac_set_link, -}; - -static void aw_emac_init(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - AwEmacState *s = AW_EMAC(obj); - - memory_region_init_io(&s->iomem, OBJECT(s), &aw_emac_mem_ops, s, - "aw_emac", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); -} - -static void aw_emac_realize(DeviceState *dev, Error **errp) -{ - AwEmacState *s = AW_EMAC(dev); - - qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->nic = qemu_new_nic(&net_aw_emac_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - fifo8_create(&s->rx_fifo, RX_FIFO_SIZE); - fifo8_create(&s->tx_fifo[0], TX_FIFO_SIZE); - fifo8_create(&s->tx_fifo[1], TX_FIFO_SIZE); -} - -static Property aw_emac_properties[] = { - DEFINE_NIC_PROPERTIES(AwEmacState, conf), - DEFINE_PROP_UINT8("phy-addr", AwEmacState, phy_addr, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription vmstate_mii = { - .name = "rtl8201cp", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT16(bmcr, RTL8201CPState), - VMSTATE_UINT16(bmsr, RTL8201CPState), - VMSTATE_UINT16(anar, RTL8201CPState), - VMSTATE_UINT16(anlpar, RTL8201CPState), - VMSTATE_END_OF_LIST() - } -}; - -static int aw_emac_post_load(void *opaque, int version_id) -{ - AwEmacState *s = opaque; - - aw_emac_set_link(qemu_get_queue(s->nic)); - - return 0; -} - -static const VMStateDescription vmstate_aw_emac = { - .name = "allwinner_emac", - .version_id = 1, - .minimum_version_id = 1, - .post_load = aw_emac_post_load, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(mii, AwEmacState, 1, vmstate_mii, RTL8201CPState), - VMSTATE_UINT32(ctl, AwEmacState), - VMSTATE_UINT32(tx_mode, AwEmacState), - VMSTATE_UINT32(rx_ctl, AwEmacState), - VMSTATE_UINT32(int_ctl, AwEmacState), - VMSTATE_UINT32(int_sta, AwEmacState), - VMSTATE_UINT32(phy_target, AwEmacState), - VMSTATE_FIFO8(rx_fifo, AwEmacState), - VMSTATE_UINT32(rx_num_packets, AwEmacState), - VMSTATE_UINT32(rx_packet_size, AwEmacState), - VMSTATE_UINT32(rx_packet_pos, AwEmacState), - VMSTATE_STRUCT_ARRAY(tx_fifo, AwEmacState, NUM_TX_FIFOS, 1, - vmstate_fifo8, Fifo8), - VMSTATE_UINT32_ARRAY(tx_length, AwEmacState, NUM_TX_FIFOS), - VMSTATE_UINT32(tx_channel, AwEmacState), - VMSTATE_END_OF_LIST() - } -}; - -static void aw_emac_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = aw_emac_realize; - dc->props = aw_emac_properties; - dc->reset = aw_emac_reset; - dc->vmsd = &vmstate_aw_emac; -} - -static const TypeInfo aw_emac_info = { - .name = TYPE_AW_EMAC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(AwEmacState), - .instance_init = aw_emac_init, - .class_init = aw_emac_class_init, -}; - -static void aw_emac_register_types(void) -{ - type_register_static(&aw_emac_info); -} - -type_init(aw_emac_register_types) diff --git a/qemu/hw/net/cadence_gem.c b/qemu/hw/net/cadence_gem.c deleted file mode 100644 index 0346f3e33..000000000 --- a/qemu/hw/net/cadence_gem.c +++ /dev/null @@ -1,1267 +0,0 @@ -/* - * QEMU Cadence GEM emulation - * - * Copyright (c) 2011 Xilinx, Inc. - * - * 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 /* For crc32 */ - -#include "hw/net/cadence_gem.h" -#include "net/checksum.h" - -#ifdef CADENCE_GEM_ERR_DEBUG -#define DB_PRINT(...) do { \ - fprintf(stderr, ": %s: ", __func__); \ - fprintf(stderr, ## __VA_ARGS__); \ - } while (0); -#else - #define DB_PRINT(...) -#endif - -#define GEM_NWCTRL (0x00000000/4) /* Network Control reg */ -#define GEM_NWCFG (0x00000004/4) /* Network Config reg */ -#define GEM_NWSTATUS (0x00000008/4) /* Network Status reg */ -#define GEM_USERIO (0x0000000C/4) /* User IO reg */ -#define GEM_DMACFG (0x00000010/4) /* DMA Control reg */ -#define GEM_TXSTATUS (0x00000014/4) /* TX Status reg */ -#define GEM_RXQBASE (0x00000018/4) /* RX Q Base address reg */ -#define GEM_TXQBASE (0x0000001C/4) /* TX Q Base address reg */ -#define GEM_RXSTATUS (0x00000020/4) /* RX Status reg */ -#define GEM_ISR (0x00000024/4) /* Interrupt Status reg */ -#define GEM_IER (0x00000028/4) /* Interrupt Enable reg */ -#define GEM_IDR (0x0000002C/4) /* Interrupt Disable reg */ -#define GEM_IMR (0x00000030/4) /* Interrupt Mask reg */ -#define GEM_PHYMNTNC (0x00000034/4) /* Phy Maintenance reg */ -#define GEM_RXPAUSE (0x00000038/4) /* RX Pause Time reg */ -#define GEM_TXPAUSE (0x0000003C/4) /* TX Pause Time reg */ -#define GEM_TXPARTIALSF (0x00000040/4) /* TX Partial Store and Forward */ -#define GEM_RXPARTIALSF (0x00000044/4) /* RX Partial Store and Forward */ -#define GEM_HASHLO (0x00000080/4) /* Hash Low address reg */ -#define GEM_HASHHI (0x00000084/4) /* Hash High address reg */ -#define GEM_SPADDR1LO (0x00000088/4) /* Specific addr 1 low reg */ -#define GEM_SPADDR1HI (0x0000008C/4) /* Specific addr 1 high reg */ -#define GEM_SPADDR2LO (0x00000090/4) /* Specific addr 2 low reg */ -#define GEM_SPADDR2HI (0x00000094/4) /* Specific addr 2 high reg */ -#define GEM_SPADDR3LO (0x00000098/4) /* Specific addr 3 low reg */ -#define GEM_SPADDR3HI (0x0000009C/4) /* Specific addr 3 high reg */ -#define GEM_SPADDR4LO (0x000000A0/4) /* Specific addr 4 low reg */ -#define GEM_SPADDR4HI (0x000000A4/4) /* Specific addr 4 high reg */ -#define GEM_TIDMATCH1 (0x000000A8/4) /* Type ID1 Match reg */ -#define GEM_TIDMATCH2 (0x000000AC/4) /* Type ID2 Match reg */ -#define GEM_TIDMATCH3 (0x000000B0/4) /* Type ID3 Match reg */ -#define GEM_TIDMATCH4 (0x000000B4/4) /* Type ID4 Match reg */ -#define GEM_WOLAN (0x000000B8/4) /* Wake on LAN reg */ -#define GEM_IPGSTRETCH (0x000000BC/4) /* IPG Stretch reg */ -#define GEM_SVLAN (0x000000C0/4) /* Stacked VLAN reg */ -#define GEM_MODID (0x000000FC/4) /* Module ID reg */ -#define GEM_OCTTXLO (0x00000100/4) /* Octects transmitted Low reg */ -#define GEM_OCTTXHI (0x00000104/4) /* Octects transmitted High reg */ -#define GEM_TXCNT (0x00000108/4) /* Error-free Frames transmitted */ -#define GEM_TXBCNT (0x0000010C/4) /* Error-free Broadcast Frames */ -#define GEM_TXMCNT (0x00000110/4) /* Error-free Multicast Frame */ -#define GEM_TXPAUSECNT (0x00000114/4) /* Pause Frames Transmitted */ -#define GEM_TX64CNT (0x00000118/4) /* Error-free 64 TX */ -#define GEM_TX65CNT (0x0000011C/4) /* Error-free 65-127 TX */ -#define GEM_TX128CNT (0x00000120/4) /* Error-free 128-255 TX */ -#define GEM_TX256CNT (0x00000124/4) /* Error-free 256-511 */ -#define GEM_TX512CNT (0x00000128/4) /* Error-free 512-1023 TX */ -#define GEM_TX1024CNT (0x0000012C/4) /* Error-free 1024-1518 TX */ -#define GEM_TX1519CNT (0x00000130/4) /* Error-free larger than 1519 TX */ -#define GEM_TXURUNCNT (0x00000134/4) /* TX under run error counter */ -#define GEM_SINGLECOLLCNT (0x00000138/4) /* Single Collision Frames */ -#define GEM_MULTCOLLCNT (0x0000013C/4) /* Multiple Collision Frames */ -#define GEM_EXCESSCOLLCNT (0x00000140/4) /* Excessive Collision Frames */ -#define GEM_LATECOLLCNT (0x00000144/4) /* Late Collision Frames */ -#define GEM_DEFERTXCNT (0x00000148/4) /* Deferred Transmission Frames */ -#define GEM_CSENSECNT (0x0000014C/4) /* Carrier Sense Error Counter */ -#define GEM_OCTRXLO (0x00000150/4) /* Octects Received register Low */ -#define GEM_OCTRXHI (0x00000154/4) /* Octects Received register High */ -#define GEM_RXCNT (0x00000158/4) /* Error-free Frames Received */ -#define GEM_RXBROADCNT (0x0000015C/4) /* Error-free Broadcast Frames RX */ -#define GEM_RXMULTICNT (0x00000160/4) /* Error-free Multicast Frames RX */ -#define GEM_RXPAUSECNT (0x00000164/4) /* Pause Frames Received Counter */ -#define GEM_RX64CNT (0x00000168/4) /* Error-free 64 byte Frames RX */ -#define GEM_RX65CNT (0x0000016C/4) /* Error-free 65-127B Frames RX */ -#define GEM_RX128CNT (0x00000170/4) /* Error-free 128-255B Frames RX */ -#define GEM_RX256CNT (0x00000174/4) /* Error-free 256-512B Frames RX */ -#define GEM_RX512CNT (0x00000178/4) /* Error-free 512-1023B Frames RX */ -#define GEM_RX1024CNT (0x0000017C/4) /* Error-free 1024-1518B Frames RX */ -#define GEM_RX1519CNT (0x00000180/4) /* Error-free 1519-max Frames RX */ -#define GEM_RXUNDERCNT (0x00000184/4) /* Undersize Frames Received */ -#define GEM_RXOVERCNT (0x00000188/4) /* Oversize Frames Received */ -#define GEM_RXJABCNT (0x0000018C/4) /* Jabbers Received Counter */ -#define GEM_RXFCSCNT (0x00000190/4) /* Frame Check seq. Error Counter */ -#define GEM_RXLENERRCNT (0x00000194/4) /* Length Field Error Counter */ -#define GEM_RXSYMERRCNT (0x00000198/4) /* Symbol Error Counter */ -#define GEM_RXALIGNERRCNT (0x0000019C/4) /* Alignment Error Counter */ -#define GEM_RXRSCERRCNT (0x000001A0/4) /* Receive Resource Error Counter */ -#define GEM_RXORUNCNT (0x000001A4/4) /* Receive Overrun Counter */ -#define GEM_RXIPCSERRCNT (0x000001A8/4) /* IP header Checksum Error Counter */ -#define GEM_RXTCPCCNT (0x000001AC/4) /* TCP Checksum Error Counter */ -#define GEM_RXUDPCCNT (0x000001B0/4) /* UDP Checksum Error Counter */ - -#define GEM_1588S (0x000001D0/4) /* 1588 Timer Seconds */ -#define GEM_1588NS (0x000001D4/4) /* 1588 Timer Nanoseconds */ -#define GEM_1588ADJ (0x000001D8/4) /* 1588 Timer Adjust */ -#define GEM_1588INC (0x000001DC/4) /* 1588 Timer Increment */ -#define GEM_PTPETXS (0x000001E0/4) /* PTP Event Frame Transmitted (s) */ -#define GEM_PTPETXNS (0x000001E4/4) /* PTP Event Frame Transmitted (ns) */ -#define GEM_PTPERXS (0x000001E8/4) /* PTP Event Frame Received (s) */ -#define GEM_PTPERXNS (0x000001EC/4) /* PTP Event Frame Received (ns) */ -#define GEM_PTPPTXS (0x000001E0/4) /* PTP Peer Frame Transmitted (s) */ -#define GEM_PTPPTXNS (0x000001E4/4) /* PTP Peer Frame Transmitted (ns) */ -#define GEM_PTPPRXS (0x000001E8/4) /* PTP Peer Frame Received (s) */ -#define GEM_PTPPRXNS (0x000001EC/4) /* PTP Peer Frame Received (ns) */ - -/* Design Configuration Registers */ -#define GEM_DESCONF (0x00000280/4) -#define GEM_DESCONF2 (0x00000284/4) -#define GEM_DESCONF3 (0x00000288/4) -#define GEM_DESCONF4 (0x0000028C/4) -#define GEM_DESCONF5 (0x00000290/4) -#define GEM_DESCONF6 (0x00000294/4) -#define GEM_DESCONF7 (0x00000298/4) - -/*****************************************/ -#define GEM_NWCTRL_TXSTART 0x00000200 /* Transmit Enable */ -#define GEM_NWCTRL_TXENA 0x00000008 /* Transmit Enable */ -#define GEM_NWCTRL_RXENA 0x00000004 /* Receive Enable */ -#define GEM_NWCTRL_LOCALLOOP 0x00000002 /* Local Loopback */ - -#define GEM_NWCFG_STRIP_FCS 0x00020000 /* Strip FCS field */ -#define GEM_NWCFG_LERR_DISC 0x00010000 /* Discard RX frames with len err */ -#define GEM_NWCFG_BUFF_OFST_M 0x0000C000 /* Receive buffer offset mask */ -#define GEM_NWCFG_BUFF_OFST_S 14 /* Receive buffer offset shift */ -#define GEM_NWCFG_UCAST_HASH 0x00000080 /* accept unicast if hash match */ -#define GEM_NWCFG_MCAST_HASH 0x00000040 /* accept multicast if hash match */ -#define GEM_NWCFG_BCAST_REJ 0x00000020 /* Reject broadcast packets */ -#define GEM_NWCFG_PROMISC 0x00000010 /* Accept all packets */ - -#define GEM_DMACFG_RBUFSZ_M 0x00FF0000 /* DMA RX Buffer Size mask */ -#define GEM_DMACFG_RBUFSZ_S 16 /* DMA RX Buffer Size shift */ -#define GEM_DMACFG_RBUFSZ_MUL 64 /* DMA RX Buffer Size multiplier */ -#define GEM_DMACFG_TXCSUM_OFFL 0x00000800 /* Transmit checksum offload */ - -#define GEM_TXSTATUS_TXCMPL 0x00000020 /* Transmit Complete */ -#define GEM_TXSTATUS_USED 0x00000001 /* sw owned descriptor encountered */ - -#define GEM_RXSTATUS_FRMRCVD 0x00000002 /* Frame received */ -#define GEM_RXSTATUS_NOBUF 0x00000001 /* Buffer unavailable */ - -/* GEM_ISR GEM_IER GEM_IDR GEM_IMR */ -#define GEM_INT_TXCMPL 0x00000080 /* Transmit Complete */ -#define GEM_INT_TXUSED 0x00000008 -#define GEM_INT_RXUSED 0x00000004 -#define GEM_INT_RXCMPL 0x00000002 - -#define GEM_PHYMNTNC_OP_R 0x20000000 /* read operation */ -#define GEM_PHYMNTNC_OP_W 0x10000000 /* write operation */ -#define GEM_PHYMNTNC_ADDR 0x0F800000 /* Address bits */ -#define GEM_PHYMNTNC_ADDR_SHFT 23 -#define GEM_PHYMNTNC_REG 0x007C0000 /* register bits */ -#define GEM_PHYMNTNC_REG_SHIFT 18 - -/* Marvell PHY definitions */ -#define BOARD_PHY_ADDRESS 23 /* PHY address we will emulate a device at */ - -#define PHY_REG_CONTROL 0 -#define PHY_REG_STATUS 1 -#define PHY_REG_PHYID1 2 -#define PHY_REG_PHYID2 3 -#define PHY_REG_ANEGADV 4 -#define PHY_REG_LINKPABIL 5 -#define PHY_REG_ANEGEXP 6 -#define PHY_REG_NEXTP 7 -#define PHY_REG_LINKPNEXTP 8 -#define PHY_REG_100BTCTRL 9 -#define PHY_REG_1000BTSTAT 10 -#define PHY_REG_EXTSTAT 15 -#define PHY_REG_PHYSPCFC_CTL 16 -#define PHY_REG_PHYSPCFC_ST 17 -#define PHY_REG_INT_EN 18 -#define PHY_REG_INT_ST 19 -#define PHY_REG_EXT_PHYSPCFC_CTL 20 -#define PHY_REG_RXERR 21 -#define PHY_REG_EACD 22 -#define PHY_REG_LED 24 -#define PHY_REG_LED_OVRD 25 -#define PHY_REG_EXT_PHYSPCFC_CTL2 26 -#define PHY_REG_EXT_PHYSPCFC_ST 27 -#define PHY_REG_CABLE_DIAG 28 - -#define PHY_REG_CONTROL_RST 0x8000 -#define PHY_REG_CONTROL_LOOP 0x4000 -#define PHY_REG_CONTROL_ANEG 0x1000 - -#define PHY_REG_STATUS_LINK 0x0004 -#define PHY_REG_STATUS_ANEGCMPL 0x0020 - -#define PHY_REG_INT_ST_ANEGCMPL 0x0800 -#define PHY_REG_INT_ST_LINKC 0x0400 -#define PHY_REG_INT_ST_ENERGY 0x0010 - -/***********************************************************************/ -#define GEM_RX_REJECT (-1) -#define GEM_RX_PROMISCUOUS_ACCEPT (-2) -#define GEM_RX_BROADCAST_ACCEPT (-3) -#define GEM_RX_MULTICAST_HASH_ACCEPT (-4) -#define GEM_RX_UNICAST_HASH_ACCEPT (-5) - -#define GEM_RX_SAR_ACCEPT 0 - -/***********************************************************************/ - -#define DESC_1_USED 0x80000000 -#define DESC_1_LENGTH 0x00001FFF - -#define DESC_1_TX_WRAP 0x40000000 -#define DESC_1_TX_LAST 0x00008000 - -#define DESC_0_RX_WRAP 0x00000002 -#define DESC_0_RX_OWNERSHIP 0x00000001 - -#define R_DESC_1_RX_SAR_SHIFT 25 -#define R_DESC_1_RX_SAR_LENGTH 2 -#define R_DESC_1_RX_SAR_MATCH (1 << 27) -#define R_DESC_1_RX_UNICAST_HASH (1 << 29) -#define R_DESC_1_RX_MULTICAST_HASH (1 << 30) -#define R_DESC_1_RX_BROADCAST (1 << 31) - -#define DESC_1_RX_SOF 0x00004000 -#define DESC_1_RX_EOF 0x00008000 - -static inline unsigned tx_desc_get_buffer(unsigned *desc) -{ - return desc[0]; -} - -static inline unsigned tx_desc_get_used(unsigned *desc) -{ - return (desc[1] & DESC_1_USED) ? 1 : 0; -} - -static inline void tx_desc_set_used(unsigned *desc) -{ - desc[1] |= DESC_1_USED; -} - -static inline unsigned tx_desc_get_wrap(unsigned *desc) -{ - return (desc[1] & DESC_1_TX_WRAP) ? 1 : 0; -} - -static inline unsigned tx_desc_get_last(unsigned *desc) -{ - return (desc[1] & DESC_1_TX_LAST) ? 1 : 0; -} - -static inline unsigned tx_desc_get_length(unsigned *desc) -{ - return desc[1] & DESC_1_LENGTH; -} - -static inline void print_gem_tx_desc(unsigned *desc) -{ - DB_PRINT("TXDESC:\n"); - DB_PRINT("bufaddr: 0x%08x\n", *desc); - DB_PRINT("used_hw: %d\n", tx_desc_get_used(desc)); - DB_PRINT("wrap: %d\n", tx_desc_get_wrap(desc)); - DB_PRINT("last: %d\n", tx_desc_get_last(desc)); - DB_PRINT("length: %d\n", tx_desc_get_length(desc)); -} - -static inline unsigned rx_desc_get_buffer(unsigned *desc) -{ - return desc[0] & ~0x3UL; -} - -static inline unsigned rx_desc_get_wrap(unsigned *desc) -{ - return desc[0] & DESC_0_RX_WRAP ? 1 : 0; -} - -static inline unsigned rx_desc_get_ownership(unsigned *desc) -{ - return desc[0] & DESC_0_RX_OWNERSHIP ? 1 : 0; -} - -static inline void rx_desc_set_ownership(unsigned *desc) -{ - desc[0] |= DESC_0_RX_OWNERSHIP; -} - -static inline void rx_desc_set_sof(unsigned *desc) -{ - desc[1] |= DESC_1_RX_SOF; -} - -static inline void rx_desc_set_eof(unsigned *desc) -{ - desc[1] |= DESC_1_RX_EOF; -} - -static inline void rx_desc_set_length(unsigned *desc, unsigned len) -{ - desc[1] &= ~DESC_1_LENGTH; - desc[1] |= len; -} - -static inline void rx_desc_set_broadcast(unsigned *desc) -{ - desc[1] |= R_DESC_1_RX_BROADCAST; -} - -static inline void rx_desc_set_unicast_hash(unsigned *desc) -{ - desc[1] |= R_DESC_1_RX_UNICAST_HASH; -} - -static inline void rx_desc_set_multicast_hash(unsigned *desc) -{ - desc[1] |= R_DESC_1_RX_MULTICAST_HASH; -} - -static inline void rx_desc_set_sar(unsigned *desc, int sar_idx) -{ - desc[1] = deposit32(desc[1], R_DESC_1_RX_SAR_SHIFT, R_DESC_1_RX_SAR_LENGTH, - sar_idx); - desc[1] |= R_DESC_1_RX_SAR_MATCH; -} - -/* The broadcast MAC address: 0xFFFFFFFFFFFF */ -static const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - -/* - * gem_init_register_masks: - * One time initialization. - * Set masks to identify which register bits have magical clear properties - */ -static void gem_init_register_masks(CadenceGEMState *s) -{ - /* Mask of register bits which are read only */ - memset(&s->regs_ro[0], 0, sizeof(s->regs_ro)); - s->regs_ro[GEM_NWCTRL] = 0xFFF80000; - s->regs_ro[GEM_NWSTATUS] = 0xFFFFFFFF; - s->regs_ro[GEM_DMACFG] = 0xFE00F000; - s->regs_ro[GEM_TXSTATUS] = 0xFFFFFE08; - s->regs_ro[GEM_RXQBASE] = 0x00000003; - s->regs_ro[GEM_TXQBASE] = 0x00000003; - s->regs_ro[GEM_RXSTATUS] = 0xFFFFFFF0; - s->regs_ro[GEM_ISR] = 0xFFFFFFFF; - s->regs_ro[GEM_IMR] = 0xFFFFFFFF; - s->regs_ro[GEM_MODID] = 0xFFFFFFFF; - - /* Mask of register bits which are clear on read */ - memset(&s->regs_rtc[0], 0, sizeof(s->regs_rtc)); - s->regs_rtc[GEM_ISR] = 0xFFFFFFFF; - - /* Mask of register bits which are write 1 to clear */ - memset(&s->regs_w1c[0], 0, sizeof(s->regs_w1c)); - s->regs_w1c[GEM_TXSTATUS] = 0x000001F7; - s->regs_w1c[GEM_RXSTATUS] = 0x0000000F; - - /* Mask of register bits which are write only */ - memset(&s->regs_wo[0], 0, sizeof(s->regs_wo)); - s->regs_wo[GEM_NWCTRL] = 0x00073E60; - s->regs_wo[GEM_IER] = 0x07FFFFFF; - s->regs_wo[GEM_IDR] = 0x07FFFFFF; -} - -/* - * phy_update_link: - * Make the emulated PHY link state match the QEMU "interface" state. - */ -static void phy_update_link(CadenceGEMState *s) -{ - DB_PRINT("down %d\n", qemu_get_queue(s->nic)->link_down); - - /* Autonegotiation status mirrors link status. */ - if (qemu_get_queue(s->nic)->link_down) { - s->phy_regs[PHY_REG_STATUS] &= ~(PHY_REG_STATUS_ANEGCMPL | - PHY_REG_STATUS_LINK); - s->phy_regs[PHY_REG_INT_ST] |= PHY_REG_INT_ST_LINKC; - } else { - s->phy_regs[PHY_REG_STATUS] |= (PHY_REG_STATUS_ANEGCMPL | - PHY_REG_STATUS_LINK); - s->phy_regs[PHY_REG_INT_ST] |= (PHY_REG_INT_ST_LINKC | - PHY_REG_INT_ST_ANEGCMPL | - PHY_REG_INT_ST_ENERGY); - } -} - -static int gem_can_receive(NetClientState *nc) -{ - CadenceGEMState *s; - - s = qemu_get_nic_opaque(nc); - - /* Do nothing if receive is not enabled. */ - if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_RXENA)) { - if (s->can_rx_state != 1) { - s->can_rx_state = 1; - DB_PRINT("can't receive - no enable\n"); - } - return 0; - } - - if (rx_desc_get_ownership(s->rx_desc) == 1) { - if (s->can_rx_state != 2) { - s->can_rx_state = 2; - DB_PRINT("can't receive - busy buffer descriptor 0x%x\n", - s->rx_desc_addr); - } - return 0; - } - - if (s->can_rx_state != 0) { - s->can_rx_state = 0; - DB_PRINT("can receive 0x%x\n", s->rx_desc_addr); - } - return 1; -} - -/* - * gem_update_int_status: - * Raise or lower interrupt based on current status. - */ -static void gem_update_int_status(CadenceGEMState *s) -{ - if (s->regs[GEM_ISR]) { - DB_PRINT("asserting int. (0x%08x)\n", s->regs[GEM_ISR]); - qemu_set_irq(s->irq, 1); - } -} - -/* - * gem_receive_updatestats: - * Increment receive statistics. - */ -static void gem_receive_updatestats(CadenceGEMState *s, const uint8_t *packet, - unsigned bytes) -{ - uint64_t octets; - - /* Total octets (bytes) received */ - octets = ((uint64_t)(s->regs[GEM_OCTRXLO]) << 32) | - s->regs[GEM_OCTRXHI]; - octets += bytes; - s->regs[GEM_OCTRXLO] = octets >> 32; - s->regs[GEM_OCTRXHI] = octets; - - /* Error-free Frames received */ - s->regs[GEM_RXCNT]++; - - /* Error-free Broadcast Frames counter */ - if (!memcmp(packet, broadcast_addr, 6)) { - s->regs[GEM_RXBROADCNT]++; - } - - /* Error-free Multicast Frames counter */ - if (packet[0] == 0x01) { - s->regs[GEM_RXMULTICNT]++; - } - - if (bytes <= 64) { - s->regs[GEM_RX64CNT]++; - } else if (bytes <= 127) { - s->regs[GEM_RX65CNT]++; - } else if (bytes <= 255) { - s->regs[GEM_RX128CNT]++; - } else if (bytes <= 511) { - s->regs[GEM_RX256CNT]++; - } else if (bytes <= 1023) { - s->regs[GEM_RX512CNT]++; - } else if (bytes <= 1518) { - s->regs[GEM_RX1024CNT]++; - } else { - s->regs[GEM_RX1519CNT]++; - } -} - -/* - * Get the MAC Address bit from the specified position - */ -static unsigned get_bit(const uint8_t *mac, unsigned bit) -{ - unsigned byte; - - byte = mac[bit / 8]; - byte >>= (bit & 0x7); - byte &= 1; - - return byte; -} - -/* - * Calculate a GEM MAC Address hash index - */ -static unsigned calc_mac_hash(const uint8_t *mac) -{ - int index_bit, mac_bit; - unsigned hash_index; - - hash_index = 0; - mac_bit = 5; - for (index_bit = 5; index_bit >= 0; index_bit--) { - hash_index |= (get_bit(mac, mac_bit) ^ - get_bit(mac, mac_bit + 6) ^ - get_bit(mac, mac_bit + 12) ^ - get_bit(mac, mac_bit + 18) ^ - get_bit(mac, mac_bit + 24) ^ - get_bit(mac, mac_bit + 30) ^ - get_bit(mac, mac_bit + 36) ^ - get_bit(mac, mac_bit + 42)) << index_bit; - mac_bit--; - } - - return hash_index; -} - -/* - * gem_mac_address_filter: - * Accept or reject this destination address? - * Returns: - * GEM_RX_REJECT: reject - * >= 0: Specific address accept (which matched SAR is returned) - * others for various other modes of accept: - * GEM_RM_PROMISCUOUS_ACCEPT, GEM_RX_BROADCAST_ACCEPT, - * GEM_RX_MULTICAST_HASH_ACCEPT or GEM_RX_UNICAST_HASH_ACCEPT - */ -static int gem_mac_address_filter(CadenceGEMState *s, const uint8_t *packet) -{ - uint8_t *gem_spaddr; - int i; - - /* Promiscuous mode? */ - if (s->regs[GEM_NWCFG] & GEM_NWCFG_PROMISC) { - return GEM_RX_PROMISCUOUS_ACCEPT; - } - - if (!memcmp(packet, broadcast_addr, 6)) { - /* Reject broadcast packets? */ - if (s->regs[GEM_NWCFG] & GEM_NWCFG_BCAST_REJ) { - return GEM_RX_REJECT; - } - return GEM_RX_BROADCAST_ACCEPT; - } - - /* Accept packets -w- hash match? */ - if ((packet[0] == 0x01 && (s->regs[GEM_NWCFG] & GEM_NWCFG_MCAST_HASH)) || - (packet[0] != 0x01 && (s->regs[GEM_NWCFG] & GEM_NWCFG_UCAST_HASH))) { - unsigned hash_index; - - hash_index = calc_mac_hash(packet); - if (hash_index < 32) { - if (s->regs[GEM_HASHLO] & (1<regs[GEM_HASHHI] & (1<regs[GEM_SPADDR1LO]); - for (i = 3; i >= 0; i--) { - if (s->sar_active[i] && !memcmp(packet, gem_spaddr + 8 * i, 6)) { - return GEM_RX_SAR_ACCEPT + i; - } - } - - /* No address match; reject the packet */ - return GEM_RX_REJECT; -} - -static void gem_get_rx_desc(CadenceGEMState *s) -{ - DB_PRINT("read descriptor 0x%x\n", (unsigned)s->rx_desc_addr); - /* read current descriptor */ - cpu_physical_memory_read(s->rx_desc_addr, - (uint8_t *)s->rx_desc, sizeof(s->rx_desc)); - - /* Descriptor owned by software ? */ - if (rx_desc_get_ownership(s->rx_desc) == 1) { - DB_PRINT("descriptor 0x%x owned by sw.\n", - (unsigned)s->rx_desc_addr); - s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_NOBUF; - s->regs[GEM_ISR] |= GEM_INT_RXUSED & ~(s->regs[GEM_IMR]); - /* Handle interrupt consequences */ - gem_update_int_status(s); - } -} - -/* - * gem_receive: - * Fit a packet handed to us by QEMU into the receive descriptor ring. - */ -static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) -{ - CadenceGEMState *s; - unsigned rxbufsize, bytes_to_copy; - unsigned rxbuf_offset; - uint8_t rxbuf[2048]; - uint8_t *rxbuf_ptr; - bool first_desc = true; - int maf; - - s = qemu_get_nic_opaque(nc); - - /* Is this destination MAC address "for us" ? */ - maf = gem_mac_address_filter(s, buf); - if (maf == GEM_RX_REJECT) { - return -1; - } - - /* Discard packets with receive length error enabled ? */ - if (s->regs[GEM_NWCFG] & GEM_NWCFG_LERR_DISC) { - unsigned type_len; - - /* Fish the ethertype / length field out of the RX packet */ - type_len = buf[12] << 8 | buf[13]; - /* It is a length field, not an ethertype */ - if (type_len < 0x600) { - if (size < type_len) { - /* discard */ - return -1; - } - } - } - - /* - * Determine configured receive buffer offset (probably 0) - */ - rxbuf_offset = (s->regs[GEM_NWCFG] & GEM_NWCFG_BUFF_OFST_M) >> - GEM_NWCFG_BUFF_OFST_S; - - /* The configure size of each receive buffer. Determines how many - * buffers needed to hold this packet. - */ - rxbufsize = ((s->regs[GEM_DMACFG] & GEM_DMACFG_RBUFSZ_M) >> - GEM_DMACFG_RBUFSZ_S) * GEM_DMACFG_RBUFSZ_MUL; - bytes_to_copy = size; - - /* Pad to minimum length. Assume FCS field is stripped, logic - * below will increment it to the real minimum of 64 when - * not FCS stripping - */ - if (size < 60) { - size = 60; - } - - /* Strip of FCS field ? (usually yes) */ - if (s->regs[GEM_NWCFG] & GEM_NWCFG_STRIP_FCS) { - rxbuf_ptr = (void *)buf; - } 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. - */ - - memcpy(rxbuf, buf, size); - memset(rxbuf + size, 0, sizeof(rxbuf) - size); - rxbuf_ptr = rxbuf; - crc_val = cpu_to_le32(crc32(0, rxbuf, MAX(size, 60))); - memcpy(rxbuf + size, &crc_val, sizeof(crc_val)); - - bytes_to_copy += 4; - size += 4; - } - - DB_PRINT("config bufsize: %d packet size: %ld\n", rxbufsize, size); - - while (bytes_to_copy) { - /* Do nothing if receive is not enabled. */ - if (!gem_can_receive(nc)) { - assert(!first_desc); - return -1; - } - - DB_PRINT("copy %d bytes to 0x%x\n", MIN(bytes_to_copy, rxbufsize), - rx_desc_get_buffer(s->rx_desc)); - - /* Copy packet data to emulated DMA buffer */ - cpu_physical_memory_write(rx_desc_get_buffer(s->rx_desc) + rxbuf_offset, - rxbuf_ptr, MIN(bytes_to_copy, rxbufsize)); - rxbuf_ptr += MIN(bytes_to_copy, rxbufsize); - bytes_to_copy -= MIN(bytes_to_copy, rxbufsize); - - /* Update the descriptor. */ - if (first_desc) { - rx_desc_set_sof(s->rx_desc); - first_desc = false; - } - if (bytes_to_copy == 0) { - rx_desc_set_eof(s->rx_desc); - rx_desc_set_length(s->rx_desc, size); - } - rx_desc_set_ownership(s->rx_desc); - - switch (maf) { - case GEM_RX_PROMISCUOUS_ACCEPT: - break; - case GEM_RX_BROADCAST_ACCEPT: - rx_desc_set_broadcast(s->rx_desc); - break; - case GEM_RX_UNICAST_HASH_ACCEPT: - rx_desc_set_unicast_hash(s->rx_desc); - break; - case GEM_RX_MULTICAST_HASH_ACCEPT: - rx_desc_set_multicast_hash(s->rx_desc); - break; - case GEM_RX_REJECT: - abort(); - default: /* SAR */ - rx_desc_set_sar(s->rx_desc, maf); - } - - /* Descriptor write-back. */ - cpu_physical_memory_write(s->rx_desc_addr, - (uint8_t *)s->rx_desc, sizeof(s->rx_desc)); - - /* Next descriptor */ - if (rx_desc_get_wrap(s->rx_desc)) { - DB_PRINT("wrapping RX descriptor list\n"); - s->rx_desc_addr = s->regs[GEM_RXQBASE]; - } else { - DB_PRINT("incrementing RX descriptor list\n"); - s->rx_desc_addr += 8; - } - gem_get_rx_desc(s); - } - - /* Count it */ - gem_receive_updatestats(s, buf, size); - - s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_FRMRCVD; - s->regs[GEM_ISR] |= GEM_INT_RXCMPL & ~(s->regs[GEM_IMR]); - - /* Handle interrupt consequences */ - gem_update_int_status(s); - - return size; -} - -/* - * gem_transmit_updatestats: - * Increment transmit statistics. - */ -static void gem_transmit_updatestats(CadenceGEMState *s, const uint8_t *packet, - unsigned bytes) -{ - uint64_t octets; - - /* Total octets (bytes) transmitted */ - octets = ((uint64_t)(s->regs[GEM_OCTTXLO]) << 32) | - s->regs[GEM_OCTTXHI]; - octets += bytes; - s->regs[GEM_OCTTXLO] = octets >> 32; - s->regs[GEM_OCTTXHI] = octets; - - /* Error-free Frames transmitted */ - s->regs[GEM_TXCNT]++; - - /* Error-free Broadcast Frames counter */ - if (!memcmp(packet, broadcast_addr, 6)) { - s->regs[GEM_TXBCNT]++; - } - - /* Error-free Multicast Frames counter */ - if (packet[0] == 0x01) { - s->regs[GEM_TXMCNT]++; - } - - if (bytes <= 64) { - s->regs[GEM_TX64CNT]++; - } else if (bytes <= 127) { - s->regs[GEM_TX65CNT]++; - } else if (bytes <= 255) { - s->regs[GEM_TX128CNT]++; - } else if (bytes <= 511) { - s->regs[GEM_TX256CNT]++; - } else if (bytes <= 1023) { - s->regs[GEM_TX512CNT]++; - } else if (bytes <= 1518) { - s->regs[GEM_TX1024CNT]++; - } else { - s->regs[GEM_TX1519CNT]++; - } -} - -/* - * gem_transmit: - * Fish packets out of the descriptor ring and feed them to QEMU - */ -static void gem_transmit(CadenceGEMState *s) -{ - unsigned desc[2]; - hwaddr packet_desc_addr; - uint8_t tx_packet[2048]; - uint8_t *p; - unsigned total_bytes; - - /* Do nothing if transmit is not enabled. */ - if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_TXENA)) { - return; - } - - DB_PRINT("\n"); - - /* The packet we will hand off to QEMU. - * Packets scattered across multiple descriptors are gathered to this - * one contiguous buffer first. - */ - p = tx_packet; - total_bytes = 0; - - /* read current descriptor */ - packet_desc_addr = s->tx_desc_addr; - - DB_PRINT("read descriptor 0x%" HWADDR_PRIx "\n", packet_desc_addr); - cpu_physical_memory_read(packet_desc_addr, - (uint8_t *)desc, sizeof(desc)); - /* Handle all descriptors owned by hardware */ - while (tx_desc_get_used(desc) == 0) { - - /* Do nothing if transmit is not enabled. */ - if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_TXENA)) { - return; - } - print_gem_tx_desc(desc); - - /* The real hardware would eat this (and possibly crash). - * For QEMU let's lend a helping hand. - */ - if ((tx_desc_get_buffer(desc) == 0) || - (tx_desc_get_length(desc) == 0)) { - DB_PRINT("Invalid TX descriptor @ 0x%x\n", - (unsigned)packet_desc_addr); - 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. - */ - cpu_physical_memory_read(tx_desc_get_buffer(desc), p, - tx_desc_get_length(desc)); - p += tx_desc_get_length(desc); - total_bytes += tx_desc_get_length(desc); - - /* Last descriptor for this packet; hand the whole thing off */ - if (tx_desc_get_last(desc)) { - unsigned desc_first[2]; - - /* Modify the 1st descriptor of this packet to be owned by - * the processor. - */ - cpu_physical_memory_read(s->tx_desc_addr, (uint8_t *)desc_first, - sizeof(desc_first)); - tx_desc_set_used(desc_first); - cpu_physical_memory_write(s->tx_desc_addr, (uint8_t *)desc_first, - sizeof(desc_first)); - /* Advance the hardware current descriptor past this packet */ - if (tx_desc_get_wrap(desc)) { - s->tx_desc_addr = s->regs[GEM_TXQBASE]; - } else { - s->tx_desc_addr = packet_desc_addr + 8; - } - DB_PRINT("TX descriptor next: 0x%08x\n", s->tx_desc_addr); - - s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_TXCMPL; - s->regs[GEM_ISR] |= GEM_INT_TXCMPL & ~(s->regs[GEM_IMR]); - - /* Handle interrupt consequences */ - gem_update_int_status(s); - - /* Is checksum offload enabled? */ - if (s->regs[GEM_DMACFG] & GEM_DMACFG_TXCSUM_OFFL) { - net_checksum_calculate(tx_packet, total_bytes); - } - - /* Update MAC statistics */ - gem_transmit_updatestats(s, tx_packet, total_bytes); - - /* Send the packet somewhere */ - if (s->phy_loop || (s->regs[GEM_NWCTRL] & GEM_NWCTRL_LOCALLOOP)) { - gem_receive(qemu_get_queue(s->nic), tx_packet, total_bytes); - } else { - qemu_send_packet(qemu_get_queue(s->nic), tx_packet, - total_bytes); - } - - /* Prepare for next packet */ - p = tx_packet; - total_bytes = 0; - } - - /* read next descriptor */ - if (tx_desc_get_wrap(desc)) { - packet_desc_addr = s->regs[GEM_TXQBASE]; - } else { - packet_desc_addr += 8; - } - DB_PRINT("read descriptor 0x%" HWADDR_PRIx "\n", packet_desc_addr); - cpu_physical_memory_read(packet_desc_addr, - (uint8_t *)desc, sizeof(desc)); - } - - if (tx_desc_get_used(desc)) { - s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_USED; - s->regs[GEM_ISR] |= GEM_INT_TXUSED & ~(s->regs[GEM_IMR]); - gem_update_int_status(s); - } -} - -static void gem_phy_reset(CadenceGEMState *s) -{ - memset(&s->phy_regs[0], 0, sizeof(s->phy_regs)); - s->phy_regs[PHY_REG_CONTROL] = 0x1140; - s->phy_regs[PHY_REG_STATUS] = 0x7969; - s->phy_regs[PHY_REG_PHYID1] = 0x0141; - s->phy_regs[PHY_REG_PHYID2] = 0x0CC2; - s->phy_regs[PHY_REG_ANEGADV] = 0x01E1; - s->phy_regs[PHY_REG_LINKPABIL] = 0xCDE1; - s->phy_regs[PHY_REG_ANEGEXP] = 0x000F; - s->phy_regs[PHY_REG_NEXTP] = 0x2001; - s->phy_regs[PHY_REG_LINKPNEXTP] = 0x40E6; - s->phy_regs[PHY_REG_100BTCTRL] = 0x0300; - 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] = 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; - s->phy_regs[PHY_REG_EXT_PHYSPCFC_ST] = 0x848B; - - phy_update_link(s); -} - -static void gem_reset(DeviceState *d) -{ - int i; - CadenceGEMState *s = CADENCE_GEM(d); - const uint8_t *a; - - DB_PRINT("\n"); - - /* Set post reset register values */ - memset(&s->regs[0], 0, sizeof(s->regs)); - s->regs[GEM_NWCFG] = 0x00080000; - s->regs[GEM_NWSTATUS] = 0x00000006; - s->regs[GEM_DMACFG] = 0x00020784; - s->regs[GEM_IMR] = 0x07ffffff; - s->regs[GEM_TXPAUSE] = 0x0000ffff; - s->regs[GEM_TXPARTIALSF] = 0x000003ff; - s->regs[GEM_RXPARTIALSF] = 0x000003ff; - s->regs[GEM_MODID] = 0x00020118; - s->regs[GEM_DESCONF] = 0x02500111; - s->regs[GEM_DESCONF2] = 0x2ab13fff; - 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; - } - - gem_phy_reset(s); - - gem_update_int_status(s); -} - -static uint16_t gem_phy_read(CadenceGEMState *s, unsigned reg_num) -{ - DB_PRINT("reg: %d value: 0x%04x\n", reg_num, s->phy_regs[reg_num]); - return s->phy_regs[reg_num]; -} - -static void gem_phy_write(CadenceGEMState *s, unsigned reg_num, uint16_t val) -{ - DB_PRINT("reg: %d value: 0x%04x\n", reg_num, val); - - switch (reg_num) { - case PHY_REG_CONTROL: - if (val & PHY_REG_CONTROL_RST) { - /* Phy reset */ - gem_phy_reset(s); - val &= ~(PHY_REG_CONTROL_RST | PHY_REG_CONTROL_LOOP); - s->phy_loop = 0; - } - if (val & PHY_REG_CONTROL_ANEG) { - /* Complete autonegotiation immediately */ - val &= ~PHY_REG_CONTROL_ANEG; - s->phy_regs[PHY_REG_STATUS] |= PHY_REG_STATUS_ANEGCMPL; - } - if (val & PHY_REG_CONTROL_LOOP) { - DB_PRINT("PHY placed in loopback\n"); - s->phy_loop = 1; - } else { - s->phy_loop = 0; - } - break; - } - s->phy_regs[reg_num] = val; -} - -/* - * gem_read32: - * Read a GEM register. - */ -static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size) -{ - CadenceGEMState *s; - uint32_t retval; - - s = (CadenceGEMState *)opaque; - - offset >>= 2; - retval = s->regs[offset]; - - DB_PRINT("offset: 0x%04x read: 0x%08x\n", (unsigned)offset*4, retval); - - switch (offset) { - case GEM_ISR: - DB_PRINT("lowering irq on ISR read\n"); - qemu_set_irq(s->irq, 0); - break; - case GEM_PHYMNTNC: - if (retval & GEM_PHYMNTNC_OP_R) { - uint32_t phy_addr, reg_num; - - phy_addr = (retval & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT; - if (phy_addr == BOARD_PHY_ADDRESS || phy_addr == 0) { - reg_num = (retval & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT; - retval &= 0xFFFF0000; - retval |= gem_phy_read(s, reg_num); - } else { - retval |= 0xFFFF; /* No device at this address */ - } - } - break; - } - - /* Squash read to clear bits */ - s->regs[offset] &= ~(s->regs_rtc[offset]); - - /* Do not provide write only bits */ - retval &= ~(s->regs_wo[offset]); - - DB_PRINT("0x%08x\n", retval); - return retval; -} - -/* - * gem_write32: - * Write a GEM register. - */ -static void gem_write(void *opaque, hwaddr offset, uint64_t val, - unsigned size) -{ - CadenceGEMState *s = (CadenceGEMState *)opaque; - uint32_t readonly; - - DB_PRINT("offset: 0x%04x write: 0x%08x ", (unsigned)offset, (unsigned)val); - offset >>= 2; - - /* Squash bits which are read only in write value */ - val &= ~(s->regs_ro[offset]); - /* Preserve (only) bits which are read only and wtc in register */ - readonly = s->regs[offset] & (s->regs_ro[offset] | s->regs_w1c[offset]); - - /* Copy register write to backing store */ - s->regs[offset] = (val & ~s->regs_w1c[offset]) | readonly; - - /* do w1c */ - s->regs[offset] &= ~(s->regs_w1c[offset] & val); - - /* Handle register write side effects */ - switch (offset) { - case GEM_NWCTRL: - if (val & GEM_NWCTRL_RXENA) { - gem_get_rx_desc(s); - } - if (val & GEM_NWCTRL_TXSTART) { - gem_transmit(s); - } - if (!(val & GEM_NWCTRL_TXENA)) { - /* Reset to start of Q when transmit disabled. */ - s->tx_desc_addr = s->regs[GEM_TXQBASE]; - } - if (gem_can_receive(qemu_get_queue(s->nic))) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } - break; - - case GEM_TXSTATUS: - gem_update_int_status(s); - break; - case GEM_RXQBASE: - s->rx_desc_addr = val; - break; - case GEM_TXQBASE: - s->tx_desc_addr = val; - break; - case GEM_RXSTATUS: - gem_update_int_status(s); - break; - case GEM_IER: - s->regs[GEM_IMR] &= ~val; - gem_update_int_status(s); - break; - case GEM_IDR: - s->regs[GEM_IMR] |= val; - gem_update_int_status(s); - break; - case GEM_SPADDR1LO: - case GEM_SPADDR2LO: - case GEM_SPADDR3LO: - case GEM_SPADDR4LO: - s->sar_active[(offset - GEM_SPADDR1LO) / 2] = false; - break; - case GEM_SPADDR1HI: - case GEM_SPADDR2HI: - case GEM_SPADDR3HI: - case GEM_SPADDR4HI: - s->sar_active[(offset - GEM_SPADDR1HI) / 2] = true; - break; - case GEM_PHYMNTNC: - if (val & GEM_PHYMNTNC_OP_W) { - uint32_t phy_addr, reg_num; - - phy_addr = (val & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT; - if (phy_addr == BOARD_PHY_ADDRESS || phy_addr == 0) { - reg_num = (val & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT; - gem_phy_write(s, reg_num, val); - } - } - break; - } - - DB_PRINT("newval: 0x%08x\n", s->regs[offset]); -} - -static const MemoryRegionOps gem_ops = { - .read = gem_read, - .write = gem_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void gem_set_link(NetClientState *nc) -{ - DB_PRINT("\n"); - phy_update_link(qemu_get_nic_opaque(nc)); -} - -static NetClientInfo net_gem_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .can_receive = gem_can_receive, - .receive = gem_receive, - .link_status_changed = gem_set_link, -}; - -static int gem_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - CadenceGEMState *s = CADENCE_GEM(dev); - - DB_PRINT("\n"); - - gem_init_register_masks(s); - memory_region_init_io(&s->iomem, OBJECT(s), &gem_ops, s, - "enet", sizeof(s->regs)); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - qemu_macaddr_default_if_unset(&s->conf.macaddr); - - s->nic = qemu_new_nic(&net_gem_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); - - return 0; -} - -static const VMStateDescription vmstate_cadence_gem = { - .name = "cadence_gem", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, CadenceGEMState, CADENCE_GEM_MAXREG), - VMSTATE_UINT16_ARRAY(phy_regs, CadenceGEMState, 32), - VMSTATE_UINT8(phy_loop, CadenceGEMState), - VMSTATE_UINT32(rx_desc_addr, CadenceGEMState), - VMSTATE_UINT32(tx_desc_addr, CadenceGEMState), - VMSTATE_BOOL_ARRAY(sar_active, CadenceGEMState, 4), - VMSTATE_END_OF_LIST(), - } -}; - -static Property gem_properties[] = { - DEFINE_NIC_PROPERTIES(CadenceGEMState, conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void gem_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = gem_init; - dc->props = gem_properties; - dc->vmsd = &vmstate_cadence_gem; - dc->reset = gem_reset; -} - -static const TypeInfo gem_info = { - .name = TYPE_CADENCE_GEM, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(CadenceGEMState), - .class_init = gem_class_init, -}; - -static void gem_register_types(void) -{ - type_register_static(&gem_info); -} - -type_init(gem_register_types) diff --git a/qemu/hw/net/dp8393x.c b/qemu/hw/net/dp8393x.c deleted file mode 100644 index 0fa652c39..000000000 --- a/qemu/hw/net/dp8393x.c +++ /dev/null @@ -1,912 +0,0 @@ -/* - * QEMU NS SONIC DP8393x netcard - * - * Copyright (c) 2008-2009 Herve Poussineau - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "hw/devices.h" -#include "net/net.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include - -//#define DEBUG_SONIC - -#define SONIC_PROM_SIZE 0x1000 - -#ifdef DEBUG_SONIC -#define DPRINTF(fmt, ...) \ -do { printf("sonic: " fmt , ## __VA_ARGS__); } while (0) -static const char* reg_names[] = { - "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA", - "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0", - "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP", - "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA", - "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC", - "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT", - "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37", - "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" }; -#else -#define DPRINTF(fmt, ...) do {} while (0) -#endif - -#define SONIC_ERROR(fmt, ...) \ -do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0) - -#define SONIC_CR 0x00 -#define SONIC_DCR 0x01 -#define SONIC_RCR 0x02 -#define SONIC_TCR 0x03 -#define SONIC_IMR 0x04 -#define SONIC_ISR 0x05 -#define SONIC_UTDA 0x06 -#define SONIC_CTDA 0x07 -#define SONIC_TPS 0x08 -#define SONIC_TFC 0x09 -#define SONIC_TSA0 0x0a -#define SONIC_TSA1 0x0b -#define SONIC_TFS 0x0c -#define SONIC_URDA 0x0d -#define SONIC_CRDA 0x0e -#define SONIC_CRBA0 0x0f -#define SONIC_CRBA1 0x10 -#define SONIC_RBWC0 0x11 -#define SONIC_RBWC1 0x12 -#define SONIC_EOBC 0x13 -#define SONIC_URRA 0x14 -#define SONIC_RSA 0x15 -#define SONIC_REA 0x16 -#define SONIC_RRP 0x17 -#define SONIC_RWP 0x18 -#define SONIC_TRBA0 0x19 -#define SONIC_TRBA1 0x1a -#define SONIC_LLFA 0x1f -#define SONIC_TTDA 0x20 -#define SONIC_CEP 0x21 -#define SONIC_CAP2 0x22 -#define SONIC_CAP1 0x23 -#define SONIC_CAP0 0x24 -#define SONIC_CE 0x25 -#define SONIC_CDP 0x26 -#define SONIC_CDC 0x27 -#define SONIC_SR 0x28 -#define SONIC_WT0 0x29 -#define SONIC_WT1 0x2a -#define SONIC_RSC 0x2b -#define SONIC_CRCT 0x2c -#define SONIC_FAET 0x2d -#define SONIC_MPT 0x2e -#define SONIC_MDT 0x2f -#define SONIC_DCR2 0x3f - -#define SONIC_CR_HTX 0x0001 -#define SONIC_CR_TXP 0x0002 -#define SONIC_CR_RXDIS 0x0004 -#define SONIC_CR_RXEN 0x0008 -#define SONIC_CR_STP 0x0010 -#define SONIC_CR_ST 0x0020 -#define SONIC_CR_RST 0x0080 -#define SONIC_CR_RRRA 0x0100 -#define SONIC_CR_LCAM 0x0200 -#define SONIC_CR_MASK 0x03bf - -#define SONIC_DCR_DW 0x0020 -#define SONIC_DCR_LBR 0x2000 -#define SONIC_DCR_EXBUS 0x8000 - -#define SONIC_RCR_PRX 0x0001 -#define SONIC_RCR_LBK 0x0002 -#define SONIC_RCR_FAER 0x0004 -#define SONIC_RCR_CRCR 0x0008 -#define SONIC_RCR_CRS 0x0020 -#define SONIC_RCR_LPKT 0x0040 -#define SONIC_RCR_BC 0x0080 -#define SONIC_RCR_MC 0x0100 -#define SONIC_RCR_LB0 0x0200 -#define SONIC_RCR_LB1 0x0400 -#define SONIC_RCR_AMC 0x0800 -#define SONIC_RCR_PRO 0x1000 -#define SONIC_RCR_BRD 0x2000 -#define SONIC_RCR_RNT 0x4000 - -#define SONIC_TCR_PTX 0x0001 -#define SONIC_TCR_BCM 0x0002 -#define SONIC_TCR_FU 0x0004 -#define SONIC_TCR_EXC 0x0040 -#define SONIC_TCR_CRSL 0x0080 -#define SONIC_TCR_NCRS 0x0100 -#define SONIC_TCR_EXD 0x0400 -#define SONIC_TCR_CRCI 0x2000 -#define SONIC_TCR_PINT 0x8000 - -#define SONIC_ISR_RBE 0x0020 -#define SONIC_ISR_RDE 0x0040 -#define SONIC_ISR_TC 0x0080 -#define SONIC_ISR_TXDN 0x0200 -#define SONIC_ISR_PKTRX 0x0400 -#define SONIC_ISR_PINT 0x0800 -#define SONIC_ISR_LCD 0x1000 - -#define TYPE_DP8393X "dp8393x" -#define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X) - -typedef struct dp8393xState { - SysBusDevice parent_obj; - - /* Hardware */ - uint8_t it_shift; - qemu_irq irq; -#ifdef DEBUG_SONIC - int irq_level; -#endif - QEMUTimer *watchdog; - int64_t wt_last_update; - NICConf conf; - NICState *nic; - MemoryRegion mmio; - MemoryRegion prom; - - /* Registers */ - uint8_t cam[16][6]; - uint16_t regs[0x40]; - - /* Temporaries */ - uint8_t tx_buffer[0x10000]; - int loopback_packet; - - /* Memory access */ - void *dma_mr; - AddressSpace as; -} dp8393xState; - -static void dp8393x_update_irq(dp8393xState *s) -{ - int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0; - -#ifdef DEBUG_SONIC - if (level != s->irq_level) { - s->irq_level = level; - if (level) { - DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]); - } else { - DPRINTF("lower irq\n"); - } - } -#endif - - qemu_set_irq(s->irq, level); -} - -static void dp8393x_do_load_cam(dp8393xState *s) -{ - uint16_t data[8]; - int width, size; - uint16_t index = 0; - - width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; - size = sizeof(uint16_t) * 4 * width; - - while (s->regs[SONIC_CDC] & 0x1f) { - /* Fill current entry */ - address_space_rw(&s->as, - (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP], - MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); - s->cam[index][0] = data[1 * width] & 0xff; - s->cam[index][1] = data[1 * width] >> 8; - s->cam[index][2] = data[2 * width] & 0xff; - s->cam[index][3] = data[2 * width] >> 8; - s->cam[index][4] = data[3 * width] & 0xff; - s->cam[index][5] = data[3 * width] >> 8; - DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index, - s->cam[index][0], s->cam[index][1], s->cam[index][2], - s->cam[index][3], s->cam[index][4], s->cam[index][5]); - /* Move to next entry */ - s->regs[SONIC_CDC]--; - s->regs[SONIC_CDP] += size; - index++; - } - - /* Read CAM enable */ - address_space_rw(&s->as, - (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP], - MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); - s->regs[SONIC_CE] = data[0 * width]; - DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]); - - /* Done */ - s->regs[SONIC_CR] &= ~SONIC_CR_LCAM; - s->regs[SONIC_ISR] |= SONIC_ISR_LCD; - dp8393x_update_irq(s); -} - -static void dp8393x_do_read_rra(dp8393xState *s) -{ - uint16_t data[8]; - int width, size; - - /* Read memory */ - width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; - size = sizeof(uint16_t) * 4 * width; - address_space_rw(&s->as, - (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP], - MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); - - /* Update SONIC registers */ - s->regs[SONIC_CRBA0] = data[0 * width]; - s->regs[SONIC_CRBA1] = data[1 * width]; - s->regs[SONIC_RBWC0] = data[2 * width]; - s->regs[SONIC_RBWC1] = data[3 * width]; - DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n", - s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1], - s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]); - - /* Go to next entry */ - s->regs[SONIC_RRP] += size; - - /* Handle wrap */ - if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) { - s->regs[SONIC_RRP] = s->regs[SONIC_RSA]; - } - - /* Check resource exhaustion */ - if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) - { - s->regs[SONIC_ISR] |= SONIC_ISR_RBE; - dp8393x_update_irq(s); - } - - /* Done */ - s->regs[SONIC_CR] &= ~SONIC_CR_RRRA; -} - -static void dp8393x_do_software_reset(dp8393xState *s) -{ - timer_del(s->watchdog); - - s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX); - s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS; -} - -static void dp8393x_set_next_tick(dp8393xState *s) -{ - uint32_t ticks; - int64_t delay; - - if (s->regs[SONIC_CR] & SONIC_CR_STP) { - timer_del(s->watchdog); - return; - } - - ticks = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0]; - s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - delay = NANOSECONDS_PER_SECOND * ticks / 5000000; - timer_mod(s->watchdog, s->wt_last_update + delay); -} - -static void dp8393x_update_wt_regs(dp8393xState *s) -{ - int64_t elapsed; - uint32_t val; - - if (s->regs[SONIC_CR] & SONIC_CR_STP) { - timer_del(s->watchdog); - return; - } - - elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - val = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0]; - val -= elapsed / 5000000; - s->regs[SONIC_WT1] = (val >> 16) & 0xffff; - s->regs[SONIC_WT0] = (val >> 0) & 0xffff; - dp8393x_set_next_tick(s); - -} - -static void dp8393x_do_start_timer(dp8393xState *s) -{ - s->regs[SONIC_CR] &= ~SONIC_CR_STP; - dp8393x_set_next_tick(s); -} - -static void dp8393x_do_stop_timer(dp8393xState *s) -{ - s->regs[SONIC_CR] &= ~SONIC_CR_ST; - dp8393x_update_wt_regs(s); -} - -static int dp8393x_can_receive(NetClientState *nc); - -static void dp8393x_do_receiver_enable(dp8393xState *s) -{ - s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS; - if (dp8393x_can_receive(s->nic->ncs)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } -} - -static void dp8393x_do_receiver_disable(dp8393xState *s) -{ - s->regs[SONIC_CR] &= ~SONIC_CR_RXEN; -} - -static void dp8393x_do_transmit_packets(dp8393xState *s) -{ - NetClientState *nc = qemu_get_queue(s->nic); - uint16_t data[12]; - int width, size; - int tx_len, len; - uint16_t i; - - width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; - - while (1) { - /* Read memory */ - DPRINTF("Transmit packet at %08x\n", - (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_CTDA]); - size = sizeof(uint16_t) * 6 * width; - s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA]; - address_space_rw(&s->as, - ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * width, - MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); - tx_len = 0; - - /* Update registers */ - s->regs[SONIC_TCR] = data[0 * width] & 0xf000; - s->regs[SONIC_TPS] = data[1 * width]; - s->regs[SONIC_TFC] = data[2 * width]; - s->regs[SONIC_TSA0] = data[3 * width]; - s->regs[SONIC_TSA1] = data[4 * width]; - s->regs[SONIC_TFS] = data[5 * width]; - - /* Handle programmable interrupt */ - if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) { - s->regs[SONIC_ISR] |= SONIC_ISR_PINT; - } else { - s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT; - } - - for (i = 0; i < s->regs[SONIC_TFC]; ) { - /* Append fragment */ - len = s->regs[SONIC_TFS]; - if (tx_len + len > sizeof(s->tx_buffer)) { - len = sizeof(s->tx_buffer) - tx_len; - } - address_space_rw(&s->as, - (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0], - MEMTXATTRS_UNSPECIFIED, &s->tx_buffer[tx_len], len, 0); - tx_len += len; - - i++; - if (i != s->regs[SONIC_TFC]) { - /* Read next fragment details */ - size = sizeof(uint16_t) * 3 * width; - address_space_rw(&s->as, - ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * i) * width, - MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); - s->regs[SONIC_TSA0] = data[0 * width]; - s->regs[SONIC_TSA1] = data[1 * width]; - s->regs[SONIC_TFS] = data[2 * width]; - } - } - - /* Handle Ethernet checksum */ - if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) { - /* Don't append FCS there, to look like slirp packets - * which don't have one */ - } else { - /* Remove existing FCS */ - tx_len -= 4; - } - - if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) { - /* Loopback */ - s->regs[SONIC_TCR] |= SONIC_TCR_CRSL; - if (nc->info->can_receive(nc)) { - s->loopback_packet = 1; - nc->info->receive(nc, s->tx_buffer, tx_len); - } - } else { - /* Transmit packet */ - qemu_send_packet(nc, s->tx_buffer, tx_len); - } - s->regs[SONIC_TCR] |= SONIC_TCR_PTX; - - /* Write status */ - data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */ - size = sizeof(uint16_t) * width; - address_space_rw(&s->as, - (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA], - MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1); - - if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) { - /* Read footer of packet */ - size = sizeof(uint16_t) * width; - address_space_rw(&s->as, - ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * s->regs[SONIC_TFC]) * width, - MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); - s->regs[SONIC_CTDA] = data[0 * width] & ~0x1; - if (data[0 * width] & 0x1) { - /* EOL detected */ - break; - } - } - } - - /* Done */ - s->regs[SONIC_CR] &= ~SONIC_CR_TXP; - s->regs[SONIC_ISR] |= SONIC_ISR_TXDN; - dp8393x_update_irq(s); -} - -static void dp8393x_do_halt_transmission(dp8393xState *s) -{ - /* Nothing to do */ -} - -static void dp8393x_do_command(dp8393xState *s, uint16_t command) -{ - if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) { - s->regs[SONIC_CR] &= ~SONIC_CR_RST; - return; - } - - s->regs[SONIC_CR] |= (command & SONIC_CR_MASK); - - if (command & SONIC_CR_HTX) - dp8393x_do_halt_transmission(s); - if (command & SONIC_CR_TXP) - dp8393x_do_transmit_packets(s); - if (command & SONIC_CR_RXDIS) - dp8393x_do_receiver_disable(s); - if (command & SONIC_CR_RXEN) - dp8393x_do_receiver_enable(s); - if (command & SONIC_CR_STP) - dp8393x_do_stop_timer(s); - if (command & SONIC_CR_ST) - dp8393x_do_start_timer(s); - if (command & SONIC_CR_RST) - dp8393x_do_software_reset(s); - if (command & SONIC_CR_RRRA) - dp8393x_do_read_rra(s); - if (command & SONIC_CR_LCAM) - dp8393x_do_load_cam(s); -} - -static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size) -{ - dp8393xState *s = opaque; - int reg = addr >> s->it_shift; - uint16_t val = 0; - - switch (reg) { - /* Update data before reading it */ - case SONIC_WT0: - case SONIC_WT1: - dp8393x_update_wt_regs(s); - val = s->regs[reg]; - break; - /* Accept read to some registers only when in reset mode */ - case SONIC_CAP2: - case SONIC_CAP1: - case SONIC_CAP0: - if (s->regs[SONIC_CR] & SONIC_CR_RST) { - val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8; - val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)]; - } - break; - /* All other registers have no special contrainst */ - default: - val = s->regs[reg]; - } - - DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]); - - return val; -} - -static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data, - unsigned int size) -{ - dp8393xState *s = opaque; - int reg = addr >> s->it_shift; - - DPRINTF("write 0x%04x to reg %s\n", (uint16_t)data, reg_names[reg]); - - switch (reg) { - /* Command register */ - case SONIC_CR: - dp8393x_do_command(s, data); - break; - /* Prevent write to read-only registers */ - case SONIC_CAP2: - case SONIC_CAP1: - case SONIC_CAP0: - case SONIC_SR: - case SONIC_MDT: - DPRINTF("writing to reg %d invalid\n", reg); - break; - /* Accept write to some registers only when in reset mode */ - case SONIC_DCR: - if (s->regs[SONIC_CR] & SONIC_CR_RST) { - s->regs[reg] = data & 0xbfff; - } else { - DPRINTF("writing to DCR invalid\n"); - } - break; - case SONIC_DCR2: - if (s->regs[SONIC_CR] & SONIC_CR_RST) { - s->regs[reg] = data & 0xf017; - } else { - DPRINTF("writing to DCR2 invalid\n"); - } - break; - /* 12 lower bytes are Read Only */ - case SONIC_TCR: - s->regs[reg] = data & 0xf000; - break; - /* 9 lower bytes are Read Only */ - case SONIC_RCR: - s->regs[reg] = data & 0xffe0; - break; - /* Ignore most significant bit */ - case SONIC_IMR: - s->regs[reg] = data & 0x7fff; - dp8393x_update_irq(s); - break; - /* Clear bits by writing 1 to them */ - case SONIC_ISR: - data &= s->regs[reg]; - s->regs[reg] &= ~data; - if (data & SONIC_ISR_RBE) { - dp8393x_do_read_rra(s); - } - dp8393x_update_irq(s); - if (dp8393x_can_receive(s->nic->ncs)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } - break; - /* Ignore least significant bit */ - case SONIC_RSA: - case SONIC_REA: - case SONIC_RRP: - case SONIC_RWP: - s->regs[reg] = data & 0xfffe; - break; - /* Invert written value for some registers */ - case SONIC_CRCT: - case SONIC_FAET: - case SONIC_MPT: - s->regs[reg] = data ^ 0xffff; - break; - /* All other registers have no special contrainst */ - default: - s->regs[reg] = data; - } - - if (reg == SONIC_WT0 || reg == SONIC_WT1) { - dp8393x_set_next_tick(s); - } -} - -static const MemoryRegionOps dp8393x_ops = { - .read = dp8393x_read, - .write = dp8393x_write, - .impl.min_access_size = 2, - .impl.max_access_size = 2, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void dp8393x_watchdog(void *opaque) -{ - dp8393xState *s = opaque; - - if (s->regs[SONIC_CR] & SONIC_CR_STP) { - return; - } - - s->regs[SONIC_WT1] = 0xffff; - s->regs[SONIC_WT0] = 0xffff; - dp8393x_set_next_tick(s); - - /* Signal underflow */ - s->regs[SONIC_ISR] |= SONIC_ISR_TC; - dp8393x_update_irq(s); -} - -static int dp8393x_can_receive(NetClientState *nc) -{ - dp8393xState *s = qemu_get_nic_opaque(nc); - - if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN)) - return 0; - if (s->regs[SONIC_ISR] & SONIC_ISR_RBE) - return 0; - return 1; -} - -static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf, - int size) -{ - static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - int i; - - /* Check promiscuous mode */ - if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) { - return 0; - } - - /* Check multicast packets */ - if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) { - return SONIC_RCR_MC; - } - - /* Check broadcast */ - if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) { - return SONIC_RCR_BC; - } - - /* Check CAM */ - for (i = 0; i < 16; i++) { - if (s->regs[SONIC_CE] & (1 << i)) { - /* Entry enabled */ - if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) { - return 0; - } - } - } - - return -1; -} - -static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, - size_t size) -{ - dp8393xState *s = qemu_get_nic_opaque(nc); - uint16_t data[10]; - int packet_type; - uint32_t available, address; - int width, rx_len = size; - uint32_t checksum; - - width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; - - s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER | - SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC); - - packet_type = dp8393x_receive_filter(s, buf, size); - if (packet_type < 0) { - DPRINTF("packet not for netcard\n"); - return -1; - } - - /* XXX: Check byte ordering */ - - /* Check for EOL */ - if (s->regs[SONIC_LLFA] & 0x1) { - /* Are we still in resource exhaustion? */ - size = sizeof(uint16_t) * 1 * width; - address = ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width; - address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED, - (uint8_t *)data, size, 0); - if (data[0 * width] & 0x1) { - /* Still EOL ; stop reception */ - return -1; - } else { - s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; - } - } - - /* Save current position */ - s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1]; - s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0]; - - /* Calculate the ethernet checksum */ - checksum = cpu_to_le32(crc32(0, buf, rx_len)); - - /* Put packet into RBA */ - DPRINTF("Receive packet at %08x\n", (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]); - address = (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]; - address_space_rw(&s->as, address, - MEMTXATTRS_UNSPECIFIED, (uint8_t *)buf, rx_len, 1); - address += rx_len; - address_space_rw(&s->as, address, - MEMTXATTRS_UNSPECIFIED, (uint8_t *)&checksum, 4, 1); - rx_len += 4; - s->regs[SONIC_CRBA1] = address >> 16; - s->regs[SONIC_CRBA0] = address & 0xffff; - available = (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]; - available -= rx_len / 2; - s->regs[SONIC_RBWC1] = available >> 16; - s->regs[SONIC_RBWC0] = available & 0xffff; - - /* Update status */ - if (((s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]) < s->regs[SONIC_EOBC]) { - s->regs[SONIC_RCR] |= SONIC_RCR_LPKT; - } - s->regs[SONIC_RCR] |= packet_type; - s->regs[SONIC_RCR] |= SONIC_RCR_PRX; - if (s->loopback_packet) { - s->regs[SONIC_RCR] |= SONIC_RCR_LBK; - s->loopback_packet = 0; - } - - /* Write status to memory */ - DPRINTF("Write status at %08x\n", (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]); - data[0 * width] = s->regs[SONIC_RCR]; /* status */ - data[1 * width] = rx_len; /* byte count */ - data[2 * width] = s->regs[SONIC_TRBA0]; /* pkt_ptr0 */ - data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */ - data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */ - size = sizeof(uint16_t) * 5 * width; - address_space_rw(&s->as, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA], - MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1); - - /* Move to next descriptor */ - size = sizeof(uint16_t) * width; - address_space_rw(&s->as, - ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width, - MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); - s->regs[SONIC_LLFA] = data[0 * width]; - if (s->regs[SONIC_LLFA] & 0x1) { - /* EOL detected */ - s->regs[SONIC_ISR] |= SONIC_ISR_RDE; - } else { - data[0 * width] = 0; /* in_use */ - address_space_rw(&s->as, - ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 6 * width, - MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, sizeof(uint16_t), 1); - s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; - s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX; - s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff); - - if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) { - /* Read next RRA */ - dp8393x_do_read_rra(s); - } - } - - /* Done */ - dp8393x_update_irq(s); - - return size; -} - -static void dp8393x_reset(DeviceState *dev) -{ - dp8393xState *s = DP8393X(dev); - timer_del(s->watchdog); - - memset(s->regs, 0, sizeof(s->regs)); - s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS; - s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR); - s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT); - s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX; - s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM; - s->regs[SONIC_IMR] = 0; - s->regs[SONIC_ISR] = 0; - s->regs[SONIC_DCR2] = 0; - s->regs[SONIC_EOBC] = 0x02F8; - s->regs[SONIC_RSC] = 0; - s->regs[SONIC_CE] = 0; - s->regs[SONIC_RSC] = 0; - - /* Network cable is connected */ - s->regs[SONIC_RCR] |= SONIC_RCR_CRS; - - dp8393x_update_irq(s); -} - -static NetClientInfo net_dp83932_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .can_receive = dp8393x_can_receive, - .receive = dp8393x_receive, -}; - -static void dp8393x_instance_init(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - dp8393xState *s = DP8393X(obj); - - sysbus_init_mmio(sbd, &s->mmio); - sysbus_init_mmio(sbd, &s->prom); - sysbus_init_irq(sbd, &s->irq); -} - -static void dp8393x_realize(DeviceState *dev, Error **errp) -{ - dp8393xState *s = DP8393X(dev); - int i, checksum; - uint8_t *prom; - Error *local_err = NULL; - - address_space_init(&s->as, s->dma_mr, "dp8393x"); - memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s, - "dp8393x-regs", 0x40 << s->it_shift); - - s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s); - s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */ - - memory_region_init_ram(&s->prom, OBJECT(dev), - "dp8393x-prom", SONIC_PROM_SIZE, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - memory_region_set_readonly(&s->prom, true); - prom = memory_region_get_ram_ptr(&s->prom); - checksum = 0; - for (i = 0; i < 6; i++) { - prom[i] = s->conf.macaddr.a[i]; - checksum += prom[i]; - if (checksum > 0xff) { - checksum = (checksum + 1) & 0xff; - } - } - prom[7] = 0xff - checksum; -} - -static const VMStateDescription vmstate_dp8393x = { - .name = "dp8393x", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField []) { - VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6), - VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40), - VMSTATE_END_OF_LIST() - } -}; - -static Property dp8393x_properties[] = { - DEFINE_NIC_PROPERTIES(dp8393xState, conf), - DEFINE_PROP_PTR("dma_mr", dp8393xState, dma_mr), - DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void dp8393x_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->realize = dp8393x_realize; - dc->reset = dp8393x_reset; - dc->vmsd = &vmstate_dp8393x; - dc->props = dp8393x_properties; - /* Reason: dma_mr property can't be set */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo dp8393x_info = { - .name = TYPE_DP8393X, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(dp8393xState), - .instance_init = dp8393x_instance_init, - .class_init = dp8393x_class_init, -}; - -static void dp8393x_register_types(void) -{ - type_register_static(&dp8393x_info); -} - -type_init(dp8393x_register_types) diff --git a/qemu/hw/net/e1000.c b/qemu/hw/net/e1000.c deleted file mode 100644 index 8e79b550e..000000000 --- a/qemu/hw/net/e1000.c +++ /dev/null @@ -1,1959 +0,0 @@ -/* - * QEMU e1000 emulation - * - * Software developer's manual: - * http://download.intel.com/design/network/manuals/8254x_GBe_SDM.pdf - * - * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. - * Copyright (c) 2008 Qumranet - * Based on work done by: - * Copyright (c) 2007 Dan Aloni - * Copyright (c) 2004 Antony T Curtis - * - * 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 . - */ - - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "net/net.h" -#include "net/checksum.h" -#include "hw/loader.h" -#include "sysemu/sysemu.h" -#include "sysemu/dma.h" -#include "qemu/iov.h" -#include "qemu/range.h" - -#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_RXFILTER, DEBUG_PHY, DEBUG_NOTYET, -}; -#define DBGBIT(x) (1<= 10.6 - * Others never tested - */ - -typedef struct E1000State_st { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - NICState *nic; - NICConf conf; - MemoryRegion mmio; - MemoryRegion io; - - uint32_t mac_reg[0x8000]; - uint16_t phy_reg[0x20]; - uint16_t eeprom_data[64]; - - uint32_t rxbuf_size; - uint32_t rxbuf_min_shift; - struct e1000_tx { - unsigned char header[256]; - unsigned char vlan_header[4]; - /* Fields vlan and data must not be reordered or separated. */ - unsigned char vlan[4]; - unsigned char data[0x10000]; - uint16_t size; - unsigned char sum_needed; - unsigned char vlan_needed; - uint8_t ipcss; - uint8_t ipcso; - uint16_t ipcse; - uint8_t tucss; - uint8_t tucso; - uint16_t tucse; - uint8_t hdr_len; - uint16_t mss; - uint32_t paylen; - uint16_t tso_frames; - char tse; - int8_t ip; - int8_t tcp; - char cptse; // current packet tse bit - } tx; - - struct { - uint32_t val_in; /* shifted in from guest driver */ - uint16_t bitnum_in; - uint16_t bitnum_out; - uint16_t reading; - uint32_t old_eecd; - } eecd_state; - - QEMUTimer *autoneg_timer; - - QEMUTimer *mit_timer; /* Mitigation timer. */ - bool mit_timer_on; /* Mitigation timer is running. */ - bool mit_irq_level; /* Tracks interrupt pin level. */ - uint32_t mit_ide; /* Tracks E1000_TXD_CMD_IDE bit. */ - -/* 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; -} E1000BaseClass; - -#define TYPE_E1000_BASE "e1000-base" - -#define E1000(obj) \ - OBJECT_CHECK(E1000State, (obj), TYPE_E1000_BASE) - -#define E1000_DEVICE_CLASS(klass) \ - OBJECT_CLASS_CHECK(E1000BaseClass, (klass), TYPE_E1000_BASE) -#define E1000_DEVICE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(E1000BaseClass, (obj), TYPE_E1000_BASE) - -#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(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 -e1000_link_down(E1000State *s) -{ - s->mac_reg[STATUS] &= ~E1000_STATUS_LU; - s->phy_reg[PHY_STATUS] &= ~MII_SR_LINK_STATUS; - s->phy_reg[PHY_STATUS] &= ~MII_SR_AUTONEG_COMPLETE; - s->phy_reg[PHY_LP_ABILITY] &= ~MII_LPAR_LPACK; -} - -static void -e1000_link_up(E1000State *s) -{ - s->mac_reg[STATUS] |= E1000_STATUS_LU; - s->phy_reg[PHY_STATUS] |= MII_SR_LINK_STATUS; - - /* E1000_STATUS_LU is tested by e1000_can_receive() */ - qemu_flush_queued_packets(qemu_get_queue(s->nic)); -} - -static bool -have_autoneg(E1000State *s) -{ - return chkflag(AUTONEG) && (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN); -} - -static void -set_phy_ctrl(E1000State *s, int index, uint16_t val) -{ - /* bits 0-5 reserved; MII_CR_[RESTART_AUTO_NEG,RESET] are self clearing */ - s->phy_reg[PHY_CTRL] = val & ~(0x3f | - MII_CR_RESET | - MII_CR_RESTART_AUTO_NEG); - - /* - * QEMU 1.3 does not support link auto-negotiation emulation, so if we - * migrate during auto negotiation, after migration the link will be - * down. - */ - if (have_autoneg(s) && (val & MII_CR_RESTART_AUTO_NEG)) { - e1000_link_down(s); - DBGOUT(PHY, "Start link auto negotiation\n"); - timer_mod(s->autoneg_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); - } -} - -static void (*phyreg_writeops[])(E1000State *, int, uint16_t) = { - [PHY_CTRL] = set_phy_ctrl, -}; - -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_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 | - MII_CR_FULL_DUPLEX | - MII_CR_AUTO_NEG_EN, - - [PHY_STATUS] = MII_SR_EXTENDED_CAPS | - MII_SR_LINK_STATUS | /* link initially up */ - MII_SR_AUTONEG_CAPS | - /* MII_SR_AUTONEG_COMPLETE: initially NOT completed */ - MII_SR_PREAMBLE_SUPPRESS | - MII_SR_EXTENDED_STATUS | - MII_SR_10T_HD_CAPS | - MII_SR_10T_FD_CAPS | - MII_SR_100X_HD_CAPS | - MII_SR_100X_FD_CAPS, - - [PHY_ID1] = 0x141, - /* [PHY_ID2] configured per DevId, from e1000_reset() */ - [PHY_AUTONEG_ADV] = 0xde1, - [PHY_LP_ABILITY] = 0x1e0, - [PHY_1000T_CTRL] = 0x0e00, - [PHY_1000T_STATUS] = 0x3c00, - [M88E1000_PHY_SPEC_CTRL] = 0x360, - [M88E1000_PHY_SPEC_STATUS] = 0xac00, - [M88E1000_EXT_PHY_SPEC_CTRL] = 0x0d60, -}; - -static const uint32_t mac_reg_init[] = { - [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 | - 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 | - E1000_MANC_ARP_EN | E1000_MANC_0298_EN | - E1000_MANC_RMCP_EN, -}; - -/* Helper function, *curr == 0 means the value is not set */ -static inline void -mit_update_delay(uint32_t *curr, uint32_t value) -{ - if (value && (*curr == 0 || value < *curr)) { - *curr = value; - } -} - -static void -set_interrupt_cause(E1000State *s, int index, uint32_t val) -{ - PCIDevice *d = PCI_DEVICE(s); - uint32_t pending_ints; - uint32_t mit_delay; - - s->mac_reg[ICR] = val; - - /* - * Make sure ICR and ICS registers have the same value. - * The spec says that the ICS register is write-only. However in practice, - * on real hardware ICS is readable, and for reads it has the same value as - * ICR (except that ICS does not have the clear on read behaviour of ICR). - * - * The VxWorks PRO/1000 driver uses this behaviour. - */ - s->mac_reg[ICS] = val; - - pending_ints = (s->mac_reg[IMS] & s->mac_reg[ICR]); - if (!s->mit_irq_level && pending_ints) { - /* - * Here we detect a potential raising edge. We postpone raising the - * interrupt line if we are inside the mitigation delay window - * (s->mit_timer_on == 1). - * We provide a partial implementation of interrupt mitigation, - * emulating only RADV, TADV and ITR (lower 16 bits, 1024ns units for - * RADV and TADV, 256ns units for ITR). RDTR is only used to enable - * RADV; relative timers based on TIDV and RDTR are not implemented. - */ - if (s->mit_timer_on) { - return; - } - if (chkflag(MIT)) { - /* Compute the next mitigation delay according to pending - * interrupts and the current values of RADV (provided - * RDTR!=0), TADV and ITR. - * Then rearm the timer. - */ - mit_delay = 0; - if (s->mit_ide && - (pending_ints & (E1000_ICR_TXQE | E1000_ICR_TXDW))) { - mit_update_delay(&mit_delay, s->mac_reg[TADV] * 4); - } - if (s->mac_reg[RDTR] && (pending_ints & E1000_ICS_RXT0)) { - mit_update_delay(&mit_delay, s->mac_reg[RADV] * 4); - } - 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) + - mit_delay * 256); - } - s->mit_ide = 0; - } - } - - s->mit_irq_level = (pending_ints != 0); - pci_set_irq(d, s->mit_irq_level); -} - -static void -e1000_mit_timer(void *opaque) -{ - E1000State *s = opaque; - - s->mit_timer_on = 0; - /* Call set_interrupt_cause to update the irq level (if necessary). */ - set_interrupt_cause(s, 0, s->mac_reg[ICR]); -} - -static void -set_ics(E1000State *s, int index, uint32_t val) -{ - DBGOUT(INTERRUPT, "set_ics %x, ICR %x, IMR %x\n", val, s->mac_reg[ICR], - s->mac_reg[IMS]); - set_interrupt_cause(s, 0, val | s->mac_reg[ICR]); -} - -static void -e1000_autoneg_timer(void *opaque) -{ - E1000State *s = opaque; - if (!qemu_get_queue(s->nic)->link_down) { - e1000_link_up(s); - s->phy_reg[PHY_LP_ABILITY] |= MII_LPAR_LPACK; - s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; - DBGOUT(PHY, "Auto negotiation is completed\n"); - set_ics(s, 0, E1000_ICS_LSC); /* signal link status change to guest */ - } -} - -static int -rxbufsize(uint32_t v) -{ - v &= E1000_RCTL_BSEX | E1000_RCTL_SZ_16384 | E1000_RCTL_SZ_8192 | - E1000_RCTL_SZ_4096 | E1000_RCTL_SZ_2048 | E1000_RCTL_SZ_1024 | - E1000_RCTL_SZ_512 | E1000_RCTL_SZ_256; - switch (v) { - case E1000_RCTL_BSEX | E1000_RCTL_SZ_16384: - return 16384; - case E1000_RCTL_BSEX | E1000_RCTL_SZ_8192: - return 8192; - case E1000_RCTL_BSEX | E1000_RCTL_SZ_4096: - return 4096; - case E1000_RCTL_SZ_1024: - return 1024; - case E1000_RCTL_SZ_512: - return 512; - case E1000_RCTL_SZ_256: - return 256; - } - return 2048; -} - -static void e1000_reset(void *opaque) -{ - E1000State *d = opaque; - E1000BaseClass *edc = E1000_DEVICE_GET_CLASS(d); - uint8_t *macaddr = d->conf.macaddr.a; - int i; - - timer_del(d->autoneg_timer); - timer_del(d->mit_timer); - d->mit_timer_on = 0; - d->mit_irq_level = 0; - d->mit_ide = 0; - memset(d->phy_reg, 0, sizeof d->phy_reg); - memmove(d->phy_reg, phy_reg_init, sizeof phy_reg_init); - d->phy_reg[PHY_ID2] = edc->phy_id2; - memset(d->mac_reg, 0, sizeof d->mac_reg); - memmove(d->mac_reg, mac_reg_init, sizeof mac_reg_init); - d->rxbuf_min_shift = 1; - memset(&d->tx, 0, sizeof d->tx); - - if (qemu_get_queue(d->nic)->link_down) { - e1000_link_down(d); - } - - /* Some guests expect pre-initialized RAH/RAL (AddrValid flag + MACaddr) */ - d->mac_reg[RA] = 0; - d->mac_reg[RA + 1] = E1000_RAH_AV; - for (i = 0; i < 4; i++) { - d->mac_reg[RA] |= macaddr[i] << (8 * i); - d->mac_reg[RA + 1] |= (i < 2) ? macaddr[i + 4] << (8 * i) : 0; - } - qemu_format_nic_info_str(qemu_get_queue(d->nic), macaddr); -} - -static void -set_ctrl(E1000State *s, int index, uint32_t val) -{ - /* RST is self clearing */ - s->mac_reg[CTRL] = val & ~E1000_CTRL_RST; -} - -static void -set_rx_control(E1000State *s, int index, uint32_t val) -{ - s->mac_reg[RCTL] = val; - s->rxbuf_size = rxbufsize(val); - s->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1; - DBGOUT(RX, "RCTL: %d, mac_reg[RCTL] = 0x%x\n", s->mac_reg[RDT], - s->mac_reg[RCTL]); - qemu_flush_queued_packets(qemu_get_queue(s->nic)); -} - -static void -set_mdic(E1000State *s, int index, uint32_t val) -{ - uint32_t data = val & E1000_MDIC_DATA_MASK; - uint32_t addr = ((val & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT); - - if ((val & E1000_MDIC_PHY_MASK) >> E1000_MDIC_PHY_SHIFT != 1) // phy # - val = s->mac_reg[MDIC] | E1000_MDIC_ERROR; - else if (val & E1000_MDIC_OP_READ) { - DBGOUT(MDIC, "MDIC read reg 0x%x\n", addr); - if (!(phy_regcap[addr] & PHY_R)) { - DBGOUT(MDIC, "MDIC read reg %x unhandled\n", addr); - val |= E1000_MDIC_ERROR; - } else - val = (val ^ data) | s->phy_reg[addr]; - } else if (val & E1000_MDIC_OP_WRITE) { - DBGOUT(MDIC, "MDIC write reg 0x%x, value 0x%x\n", addr, data); - if (!(phy_regcap[addr] & PHY_W)) { - DBGOUT(MDIC, "MDIC write reg %x unhandled\n", addr); - val |= E1000_MDIC_ERROR; - } else { - if (addr < NPHYWRITEOPS && phyreg_writeops[addr]) { - phyreg_writeops[addr](s, index, data); - } else { - s->phy_reg[addr] = data; - } - } - } - s->mac_reg[MDIC] = val | E1000_MDIC_READY; - - if (val & E1000_MDIC_INT_EN) { - set_ics(s, 0, E1000_ICR_MDAC); - } -} - -static uint32_t -get_eecd(E1000State *s, int index) -{ - uint32_t ret = E1000_EECD_PRES|E1000_EECD_GNT | s->eecd_state.old_eecd; - - DBGOUT(EEPROM, "reading eeprom bit %d (reading %d)\n", - s->eecd_state.bitnum_out, s->eecd_state.reading); - if (!s->eecd_state.reading || - ((s->eeprom_data[(s->eecd_state.bitnum_out >> 4) & 0x3f] >> - ((s->eecd_state.bitnum_out & 0xf) ^ 0xf))) & 1) - ret |= E1000_EECD_DO; - return ret; -} - -static void -set_eecd(E1000State *s, int index, uint32_t val) -{ - uint32_t oldval = s->eecd_state.old_eecd; - - 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_SK & (val ^ oldval))) { /* no clock edge */ - return; - } - if (!(E1000_EECD_SK & val)) { /* falling edge */ - s->eecd_state.bitnum_out++; - return; - } - s->eecd_state.val_in <<= 1; - if (val & E1000_EECD_DI) - s->eecd_state.val_in |= 1; - if (++s->eecd_state.bitnum_in == 9 && !s->eecd_state.reading) { - s->eecd_state.bitnum_out = ((s->eecd_state.val_in & 0x3f)<<4)-1; - s->eecd_state.reading = (((s->eecd_state.val_in >> 6) & 7) == - EEPROM_READ_OPCODE_MICROWIRE); - } - DBGOUT(EEPROM, "eeprom bitnum in %d out %d, reading %d\n", - s->eecd_state.bitnum_in, s->eecd_state.bitnum_out, - s->eecd_state.reading); -} - -static uint32_t -flash_eerd_read(E1000State *s, int x) -{ - unsigned int index, r = s->mac_reg[EERD] & ~E1000_EEPROM_RW_REG_START; - - if ((s->mac_reg[EERD] & E1000_EEPROM_RW_REG_START) == 0) - return (s->mac_reg[EERD]); - - if ((index = r >> E1000_EEPROM_RW_ADDR_SHIFT) > EEPROM_CHECKSUM_REG) - return (E1000_EEPROM_RW_REG_DONE | r); - - return ((s->eeprom_data[index] << E1000_EEPROM_RW_REG_DATA) | - E1000_EEPROM_RW_REG_DONE | r); -} - -static void -putsum(uint8_t *data, uint32_t n, uint32_t sloc, uint32_t css, uint32_t cse) -{ - uint32_t sum; - - if (cse && cse < n) - n = cse + 1; - if (sloc < n-1) { - sum = net_checksum_add(n-css, data+css); - stw_be_p(data + sloc, net_checksum_finish(sum)); - } -} - -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) -{ - return ((s->mac_reg[CTRL] & E1000_CTRL_VME) != 0); -} - -static inline int -vlan_rx_filter_enabled(E1000State *s) -{ - return ((s->mac_reg[RCTL] & E1000_RCTL_VFE) != 0); -} - -static inline int -is_vlan_packet(E1000State *s, const uint8_t *buf) -{ - return (be16_to_cpup((uint16_t *)(buf + 12)) == - le16_to_cpu(s->mac_reg[VET])); -} - -static inline int -is_vlan_txd(uint32_t txd_lower) -{ - return ((txd_lower & E1000_TXD_CMD_VLE) != 0); -} - -/* FCS aka Ethernet CRC-32. We don't get it from backends and can't - * fill it in, just pad descriptor length by 4 bytes unless guest - * told us to strip it off the packet. */ -static inline int -fcs_len(E1000State *s) -{ - return (s->mac_reg[RCTL] & E1000_RCTL_SECRC) ? 0 : 4; -} - -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; - 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 */ - 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 */ - 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 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; - // add pseudo-header length before checksum calculation - sp = (uint16_t *)(tp->data + tp->tucso); - phsum = be16_to_cpup(sp) + len; - phsum = (phsum >> 16) + (phsum & 0xffff); - stw_be_p(sp, phsum); - } - tp->tso_frames++; - } - - if (tp->sum_needed & E1000_TXD_POPTS_TXSM) - putsum(tp->data, tp->size, tp->tucso, tp->tucss, tp->tucse); - if (tp->sum_needed & E1000_TXD_POPTS_IXSM) - putsum(tp->data, tp->size, tp->ipcso, tp->ipcss, tp->ipcse); - if (tp->vlan_needed) { - memmove(tp->vlan, tp->data, 4); - 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 { - e1000_send_packet(s, tp->data, tp->size); - } - - 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 -process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) -{ - PCIDevice *d = PCI_DEVICE(s); - uint32_t txd_lower = le32_to_cpu(dp->lower.data); - uint32_t dtype = txd_lower & (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D); - unsigned int split_size = txd_lower & 0xffff, bytes, sz, op; - unsigned int msh = 0xfffff; - uint64_t addr; - struct e1000_context_desc *xp = (struct e1000_context_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 */ - op = le32_to_cpu(xp->cmd_and_length); - tp->ipcss = xp->lower_setup.ip_fields.ipcss; - tp->ipcso = xp->lower_setup.ip_fields.ipcso; - tp->ipcse = le16_to_cpu(xp->lower_setup.ip_fields.ipcse); - tp->tucss = xp->upper_setup.tcp_fields.tucss; - tp->tucso = xp->upper_setup.tcp_fields.tucso; - tp->tucse = le16_to_cpu(xp->upper_setup.tcp_fields.tucse); - tp->paylen = op & 0xfffff; - tp->hdr_len = xp->tcp_seg_setup.fields.hdr_len; - tp->mss = le16_to_cpu(xp->tcp_seg_setup.fields.mss); - tp->ip = (op & E1000_TXD_CMD_IP) ? 1 : 0; - 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 */ - DBGOUT(TXSUM, "TCP/UDP: cso 0!\n"); - tp->tucso = tp->tucss + (tp->tcp ? 16 : 6); - } - return; - } else if (dtype == (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D)) { - // data descriptor - if (tp->size == 0) { - tp->sum_needed = le32_to_cpu(dp->upper.data) >> 8; - } - tp->cptse = ( txd_lower & E1000_TXD_CMD_TSE ) ? 1 : 0; - } else { - // legacy descriptor - tp->cptse = 0; - } - - if (vlan_enabled(s) && is_vlan_txd(txd_lower) && - (tp->cptse || txd_lower & E1000_TXD_CMD_EOP)) { - tp->vlan_needed = 1; - stw_be_p(tp->vlan_header, - le16_to_cpu(s->mac_reg[VET])); - 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; - do { - bytes = split_size; - if (tp->size + bytes > msh) - bytes = msh - tp->size; - - bytes = MIN(sizeof(tp->data) - tp->size, bytes); - pci_dma_read(d, addr, tp->data + tp->size, bytes); - sz = tp->size + bytes; - if (sz >= tp->hdr_len && tp->size < tp->hdr_len) { - memmove(tp->header, tp->data, tp->hdr_len); - } - tp->size = sz; - addr += bytes; - if (sz == msh) { - xmit_seg(s); - memmove(tp->data, tp->header, tp->hdr_len); - tp->size = tp->hdr_len; - } - split_size -= bytes; - } while (bytes && split_size); - } else if (!tp->tse && tp->cptse) { - // context descriptor TSE is not set, while data descriptor TSE is set - DBGOUT(TXERR, "TCP segmentation error\n"); - } else { - split_size = MIN(sizeof(tp->data) - tp->size, split_size); - pci_dma_read(d, addr, tp->data + tp->size, split_size); - tp->size += split_size; - } - - if (!(txd_lower & E1000_TXD_CMD_EOP)) - return; - if (!(tp->tse && tp->cptse && tp->size < tp->hdr_len)) { - xmit_seg(s); - } - tp->tso_frames = 0; - tp->sum_needed = 0; - tp->vlan_needed = 0; - tp->size = 0; - tp->cptse = 0; -} - -static uint32_t -txdesc_writeback(E1000State *s, dma_addr_t base, struct e1000_tx_desc *dp) -{ - PCIDevice *d = PCI_DEVICE(s); - uint32_t txd_upper, txd_lower = le32_to_cpu(dp->lower.data); - - if (!(txd_lower & (E1000_TXD_CMD_RS|E1000_TXD_CMD_RPS))) - return 0; - txd_upper = (le32_to_cpu(dp->upper.data) | E1000_TXD_STAT_DD) & - ~(E1000_TXD_STAT_EC | E1000_TXD_STAT_LC | E1000_TXD_STAT_TU); - dp->upper.data = cpu_to_le32(txd_upper); - pci_dma_write(d, base + ((char *)&dp->upper - (char *)dp), - &dp->upper, sizeof(dp->upper)); - return E1000_ICR_TXDW; -} - -static uint64_t tx_desc_base(E1000State *s) -{ - uint64_t bah = s->mac_reg[TDBAH]; - uint64_t bal = s->mac_reg[TDBAL] & ~0xf; - - return (bah << 32) + bal; -} - -static void -start_xmit(E1000State *s) -{ - PCIDevice *d = PCI_DEVICE(s); - dma_addr_t base; - struct e1000_tx_desc desc; - uint32_t tdh_start = s->mac_reg[TDH], cause = E1000_ICS_TXQE; - - if (!(s->mac_reg[TCTL] & E1000_TCTL_EN)) { - DBGOUT(TX, "tx disabled\n"); - return; - } - - while (s->mac_reg[TDH] != s->mac_reg[TDT]) { - base = tx_desc_base(s) + - sizeof(struct e1000_tx_desc) * s->mac_reg[TDH]; - pci_dma_read(d, base, &desc, sizeof(desc)); - - DBGOUT(TX, "index %d: %p : %x %x\n", s->mac_reg[TDH], - (void *)(intptr_t)desc.buffer_addr, desc.lower.data, - desc.upper.data); - - process_tx_desc(s, &desc); - cause |= txdesc_writeback(s, base, &desc); - - if (++s->mac_reg[TDH] * sizeof(desc) >= s->mac_reg[TDLEN]) - s->mac_reg[TDH] = 0; - /* - * the following could happen only if guest sw assigns - * bogus values to TDT/TDLEN. - * there's nothing too intelligent we could do about this. - */ - 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; - } - } - set_ics(s, 0, cause); -} - -static int -receive_filter(E1000State *s, const uint8_t *buf, int size) -{ - 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)); - uint32_t vfta = le32_to_cpup((uint32_t *)(s->mac_reg + VFTA) + - ((vid >> 5) & 0x7f)); - if ((vfta & (1 << (vid & 0x1f))) == 0) - return 0; - } - - if (!isbcast && !ismcast && (rctl & E1000_RCTL_UPE)) { /* promiscuous ucast */ - return 1; - } - - if (ismcast && (rctl & E1000_RCTL_MPE)) { /* promiscuous mcast */ - inc_reg_if_not_full(s, MPRC); - return 1; - } - - 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)) - continue; - ra[0] = cpu_to_le32(rp[0]); - ra[1] = cpu_to_le32(rp[1]); - if (!memcmp(buf, (uint8_t *)ra, 6)) { - DBGOUT(RXFILTER, - "unicast match[%d]: %02x:%02x:%02x:%02x:%02x:%02x\n", - (int)(rp - s->mac_reg - RA)/2, - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); - return 1; - } - } - DBGOUT(RXFILTER, "unicast mismatch: %02x:%02x:%02x:%02x:%02x:%02x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); - - 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))) { - 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], - (rctl >> E1000_RCTL_MO_SHIFT) & 3, f >> 5, - s->mac_reg[MTA + (f >> 5)]); - - return 0; -} - -static void -e1000_set_link_status(NetClientState *nc) -{ - E1000State *s = qemu_get_nic_opaque(nc); - uint32_t old_status = s->mac_reg[STATUS]; - - if (nc->link_down) { - e1000_link_down(s); - } else { - if (have_autoneg(s) && - !(s->phy_reg[PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) { - /* emulate auto-negotiation if supported */ - timer_mod(s->autoneg_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); - } else { - e1000_link_up(s); - } - } - - if (s->mac_reg[STATUS] != old_status) - set_ics(s, 0, E1000_ICR_LSC); -} - -static bool e1000_has_rxbufs(E1000State *s, size_t total_size) -{ - int bufs; - /* Fast-path short packets */ - if (total_size <= s->rxbuf_size) { - return s->mac_reg[RDH] != s->mac_reg[RDT]; - } - if (s->mac_reg[RDH] < s->mac_reg[RDT]) { - bufs = s->mac_reg[RDT] - s->mac_reg[RDH]; - } else if (s->mac_reg[RDH] > s->mac_reg[RDT]) { - bufs = s->mac_reg[RDLEN] / sizeof(struct e1000_rx_desc) + - s->mac_reg[RDT] - s->mac_reg[RDH]; - } else { - return false; - } - return total_size <= bufs * s->rxbuf_size; -} - -static int -e1000_can_receive(NetClientState *nc) -{ - E1000State *s = qemu_get_nic_opaque(nc); - - return (s->mac_reg[STATUS] & E1000_STATUS_LU) && - (s->mac_reg[RCTL] & E1000_RCTL_EN) && - (s->parent_obj.config[PCI_COMMAND] & PCI_COMMAND_MASTER) && - e1000_has_rxbufs(s, 1); -} - -static uint64_t rx_desc_base(E1000State *s) -{ - uint64_t bah = s->mac_reg[RDBAH]; - uint64_t bal = s->mac_reg[RDBAL] & ~0xf; - - return (bah << 32) + bal; -} - -static ssize_t -e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) -{ - E1000State *s = qemu_get_nic_opaque(nc); - PCIDevice *d = PCI_DEVICE(s); - struct e1000_rx_desc desc; - dma_addr_t base; - unsigned int n, rdt; - uint32_t rdh_start; - uint16_t vlan_special = 0; - uint8_t vlan_status = 0; - uint8_t min_buf[MIN_BUF_SIZE]; - struct iovec min_iov; - uint8_t *filter_buf = iov->iov_base; - size_t size = iov_size(iov, iovcnt); - size_t iov_ofs = 0; - 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; - } - - if (!(s->mac_reg[RCTL] & E1000_RCTL_EN)) { - return -1; - } - - /* Pad to minimum Ethernet frame length */ - 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; - iov = &min_iov; - } else if (iov->iov_len < MAXIMUM_ETHERNET_HDR_LEN) { - /* This is very unlikely, but may happen. */ - iov_to_buf(iov, iovcnt, 0, min_buf, MAXIMUM_ETHERNET_HDR_LEN); - filter_buf = min_buf; - } - - /* Discard oversized packets if !LPE and !SBP. */ - if ((size > MAXIMUM_ETHERNET_LPE_SIZE || - (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; - } - - if (!receive_filter(s, filter_buf, size)) { - return size; - } - - if (vlan_enabled(s) && is_vlan_packet(s, filter_buf)) { - vlan_special = cpu_to_le16(be16_to_cpup((uint16_t *)(filter_buf - + 14))); - iov_ofs = 4; - if (filter_buf == iov->iov_base) { - memmove(filter_buf + 4, filter_buf, 12); - } else { - iov_from_buf(iov, iovcnt, 4, filter_buf, 12); - while (iov->iov_len <= iov_ofs) { - iov_ofs -= iov->iov_len; - iov++; - } - } - vlan_status = E1000_RXD_STAT_VP; - size -= 4; - } - - rdh_start = s->mac_reg[RDH]; - desc_offset = 0; - total_size = size + fcs_len(s); - if (!e1000_has_rxbufs(s, total_size)) { - set_ics(s, 0, E1000_ICS_RXO); - return -1; - } - do { - desc_size = total_size - desc_offset; - if (desc_size > s->rxbuf_size) { - desc_size = s->rxbuf_size; - } - base = rx_desc_base(s) + sizeof(desc) * s->mac_reg[RDH]; - pci_dma_read(d, base, &desc, sizeof(desc)); - desc.special = vlan_special; - desc.status |= (vlan_status | E1000_RXD_STAT_DD); - if (desc.buffer_addr) { - if (desc_offset < size) { - size_t iov_copy; - hwaddr ba = le64_to_cpu(desc.buffer_addr); - size_t copy_size = size - desc_offset; - if (copy_size > s->rxbuf_size) { - copy_size = s->rxbuf_size; - } - do { - iov_copy = MIN(copy_size, iov->iov_len - iov_ofs); - pci_dma_write(d, ba, iov->iov_base + iov_ofs, iov_copy); - copy_size -= iov_copy; - ba += iov_copy; - iov_ofs += iov_copy; - if (iov_ofs == iov->iov_len) { - iov++; - iov_ofs = 0; - } - } while (copy_size); - } - desc_offset += desc_size; - desc.length = cpu_to_le16(desc_size); - if (desc_offset >= total_size) { - desc.status |= E1000_RXD_STAT_EOP | E1000_RXD_STAT_IXSM; - } else { - /* Guest zeroing out status is not a hardware requirement. - Clear EOP in case guest didn't do it. */ - desc.status &= ~E1000_RXD_STAT_EOP; - } - } else { // as per intel docs; skip descriptors with null buf addr - DBGOUT(RX, "Null RX descriptor!!\n"); - } - pci_dma_write(d, base, &desc, sizeof(desc)); - - 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 || - 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); - return -1; - } - } while (desc_offset < total_size); - - 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 field through the field, inclusively. - * Always include FCS length (4) in size. - */ - 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]) - rdt += s->mac_reg[RDLEN] / sizeof(desc); - if (((rdt - s->mac_reg[RDH]) * sizeof(desc)) <= s->mac_reg[RDLEN] >> - s->rxbuf_min_shift) - n |= E1000_ICS_RXDMT0; - - set_ics(s, 0, n); - - return size; -} - -static ssize_t -e1000_receive(NetClientState *nc, const uint8_t *buf, size_t size) -{ - const struct iovec iov = { - .iov_base = (uint8_t *)buf, - .iov_len = size - }; - - return e1000_receive_iov(nc, &iov, 1); -} - -static uint32_t -mac_readreg(E1000State *s, int index) -{ - return s->mac_reg[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]; - - DBGOUT(INTERRUPT, "ICR read: %x\n", ret); - set_interrupt_cause(s, 0, 0); - return ret; -} - -static uint32_t -mac_read_clr4(E1000State *s, int index) -{ - uint32_t ret = s->mac_reg[index]; - - s->mac_reg[index] = 0; - return ret; -} - -static uint32_t -mac_read_clr8(E1000State *s, int index) -{ - uint32_t ret = s->mac_reg[index]; - - s->mac_reg[index] = 0; - s->mac_reg[index-1] = 0; - return ret; -} - -static void -mac_writereg(E1000State *s, int index, uint32_t val) -{ - uint32_t macaddr[2]; - - s->mac_reg[index] = val; - - if (index == RA + 1) { - macaddr[0] = cpu_to_le32(s->mac_reg[RA]); - macaddr[1] = cpu_to_le32(s->mac_reg[RA + 1]); - qemu_format_nic_info_str(qemu_get_queue(s->nic), (uint8_t *)macaddr); - } -} - -static void -set_rdt(E1000State *s, int index, uint32_t val) -{ - s->mac_reg[index] = val & 0xffff; - if (e1000_has_rxbufs(s, 1)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } -} - -static void -set_16bit(E1000State *s, int index, uint32_t val) -{ - s->mac_reg[index] = val & 0xffff; -} - -static void -set_dlen(E1000State *s, int index, uint32_t val) -{ - s->mac_reg[index] = val & 0xfff80; -} - -static void -set_tctl(E1000State *s, int index, uint32_t val) -{ - s->mac_reg[index] = val; - s->mac_reg[TDT] &= 0xffff; - start_xmit(s); -} - -static void -set_icr(E1000State *s, int index, uint32_t val) -{ - DBGOUT(INTERRUPT, "set_icr %x\n", val); - set_interrupt_cause(s, 0, s->mac_reg[ICR] & ~val); -} - -static void -set_imc(E1000State *s, int index, uint32_t val) -{ - s->mac_reg[IMS] &= ~val; - set_ics(s, 0, 0); -} - -static void -set_ims(E1000State *s, int index, uint32_t val) -{ - s->mac_reg[IMS] |= val; - set_ics(s, 0, 0); -} - -#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), 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 -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), 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) -{ - E1000State *s = opaque; - unsigned int index = (addr & 0x1ffff) >> 2; - - if (index < NWRITEOPS && macreg_writeops[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, "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); - } else { - DBGOUT(UNKNOWN, "MMIO unknown write addr=0x%08x,val=0x%08"PRIx64"\n", - index<<2, val); - } -} - -static uint64_t -e1000_mmio_read(void *opaque, hwaddr addr, unsigned size) -{ - E1000State *s = opaque; - unsigned int index = (addr & 0x1ffff) >> 2; - - 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); - } - return 0; -} - -static const MemoryRegionOps e1000_mmio_ops = { - .read = e1000_mmio_read, - .write = e1000_mmio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static uint64_t e1000_io_read(void *opaque, hwaddr addr, - unsigned size) -{ - E1000State *s = opaque; - - (void)s; - return 0; -} - -static void e1000_io_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - E1000State *s = opaque; - - (void)s; -} - -static const MemoryRegionOps e1000_io_ops = { - .read = e1000_io_read, - .write = e1000_io_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static bool is_version_1(void *opaque, int version_id) -{ - return version_id == 1; -} - -static void e1000_pre_save(void *opaque) -{ - E1000State *s = opaque; - NetClientState *nc = qemu_get_queue(s->nic); - - /* If the mitigation timer is active, emulate a timeout now. */ - if (s->mit_timer_on) { - e1000_mit_timer(s); - } - - /* - * If link is down and auto-negotiation is supported and ongoing, - * complete auto-negotiation immediately. This allows us to look - * at MII_SR_AUTONEG_COMPLETE to infer link status on load. - */ - if (nc->link_down && have_autoneg(s)) { - s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; - } -} - -static int e1000_post_load(void *opaque, int version_id) -{ - E1000State *s = opaque; - NetClientState *nc = qemu_get_queue(s->nic); - - 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; - } - s->mit_ide = 0; - s->mit_timer_on = false; - - /* nc.link_down can't be migrated, so infer link_down according - * to link status bit in mac_reg[STATUS]. - * Alternatively, restart link negotiation if it was in progress. */ - nc->link_down = (s->mac_reg[STATUS] & E1000_STATUS_LU) == 0; - - if (have_autoneg(s) && - !(s->phy_reg[PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) { - nc->link_down = false; - timer_mod(s->autoneg_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500); - } - - return 0; -} - -static bool e1000_mit_state_needed(void *opaque) -{ - E1000State *s = opaque; - - return chkflag(MIT); -} - -static bool e1000_full_mac_needed(void *opaque) -{ - E1000State *s = opaque; - - return chkflag(MAC); -} - -static const VMStateDescription vmstate_e1000_mit_state = { - .name = "e1000/mit_state", - .version_id = 1, - .minimum_version_id = 1, - .needed = e1000_mit_state_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT32(mac_reg[RDTR], E1000State), - VMSTATE_UINT32(mac_reg[RADV], E1000State), - VMSTATE_UINT32(mac_reg[TADV], E1000State), - VMSTATE_UINT32(mac_reg[ITR], E1000State), - VMSTATE_BOOL(mit_irq_level, E1000State), - VMSTATE_END_OF_LIST() - } -}; - -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, - .minimum_version_id = 1, - .pre_save = e1000_pre_save, - .post_load = e1000_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, E1000State), - VMSTATE_UNUSED_TEST(is_version_1, 4), /* was instance id */ - VMSTATE_UNUSED(4), /* Was mmio_base. */ - VMSTATE_UINT32(rxbuf_size, E1000State), - VMSTATE_UINT32(rxbuf_min_shift, E1000State), - VMSTATE_UINT32(eecd_state.val_in, E1000State), - VMSTATE_UINT16(eecd_state.bitnum_in, E1000State), - VMSTATE_UINT16(eecd_state.bitnum_out, E1000State), - VMSTATE_UINT16(eecd_state.reading, E1000State), - VMSTATE_UINT32(eecd_state.old_eecd, E1000State), - VMSTATE_UINT8(tx.ipcss, E1000State), - VMSTATE_UINT8(tx.ipcso, E1000State), - VMSTATE_UINT16(tx.ipcse, E1000State), - VMSTATE_UINT8(tx.tucss, E1000State), - VMSTATE_UINT8(tx.tucso, E1000State), - VMSTATE_UINT16(tx.tucse, E1000State), - VMSTATE_UINT32(tx.paylen, E1000State), - VMSTATE_UINT8(tx.hdr_len, E1000State), - VMSTATE_UINT16(tx.mss, E1000State), - VMSTATE_UINT16(tx.size, E1000State), - VMSTATE_UINT16(tx.tso_frames, E1000State), - VMSTATE_UINT8(tx.sum_needed, E1000State), - VMSTATE_INT8(tx.ip, E1000State), - VMSTATE_INT8(tx.tcp, E1000State), - VMSTATE_BUFFER(tx.header, E1000State), - VMSTATE_BUFFER(tx.data, E1000State), - VMSTATE_UINT16_ARRAY(eeprom_data, E1000State, 64), - VMSTATE_UINT16_ARRAY(phy_reg, E1000State, 0x20), - VMSTATE_UINT32(mac_reg[CTRL], E1000State), - VMSTATE_UINT32(mac_reg[EECD], E1000State), - VMSTATE_UINT32(mac_reg[EERD], E1000State), - VMSTATE_UINT32(mac_reg[GPRC], E1000State), - VMSTATE_UINT32(mac_reg[GPTC], E1000State), - VMSTATE_UINT32(mac_reg[ICR], E1000State), - VMSTATE_UINT32(mac_reg[ICS], E1000State), - VMSTATE_UINT32(mac_reg[IMC], E1000State), - VMSTATE_UINT32(mac_reg[IMS], E1000State), - VMSTATE_UINT32(mac_reg[LEDCTL], E1000State), - VMSTATE_UINT32(mac_reg[MANC], E1000State), - VMSTATE_UINT32(mac_reg[MDIC], E1000State), - VMSTATE_UINT32(mac_reg[MPC], E1000State), - VMSTATE_UINT32(mac_reg[PBA], E1000State), - VMSTATE_UINT32(mac_reg[RCTL], E1000State), - VMSTATE_UINT32(mac_reg[RDBAH], E1000State), - VMSTATE_UINT32(mac_reg[RDBAL], E1000State), - VMSTATE_UINT32(mac_reg[RDH], E1000State), - VMSTATE_UINT32(mac_reg[RDLEN], E1000State), - VMSTATE_UINT32(mac_reg[RDT], E1000State), - VMSTATE_UINT32(mac_reg[STATUS], E1000State), - VMSTATE_UINT32(mac_reg[SWSM], E1000State), - VMSTATE_UINT32(mac_reg[TCTL], E1000State), - VMSTATE_UINT32(mac_reg[TDBAH], E1000State), - VMSTATE_UINT32(mac_reg[TDBAL], E1000State), - VMSTATE_UINT32(mac_reg[TDH], E1000State), - VMSTATE_UINT32(mac_reg[TDLEN], E1000State), - VMSTATE_UINT32(mac_reg[TDT], E1000State), - VMSTATE_UINT32(mac_reg[TORH], E1000State), - VMSTATE_UINT32(mac_reg[TORL], E1000State), - VMSTATE_UINT32(mac_reg[TOTH], E1000State), - VMSTATE_UINT32(mac_reg[TOTL], E1000State), - VMSTATE_UINT32(mac_reg[TPR], E1000State), - VMSTATE_UINT32(mac_reg[TPT], E1000State), - VMSTATE_UINT32(mac_reg[TXDCTL], E1000State), - VMSTATE_UINT32(mac_reg[WUFC], E1000State), - VMSTATE_UINT32(mac_reg[VET], E1000State), - VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, RA, 32), - VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, MTA, 128), - VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, VFTA, 128), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_e1000_mit_state, - &vmstate_e1000_full_mac_state, - NULL - } -}; - -/* - * EEPROM contents documented in Tables 5-2 and 5-3, pp. 98-102. - * Note: A valid DevId will be inserted during pci_e1000_init(). - */ -static const uint16_t e1000_eeprom_template[64] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, - 0x3000, 0x1000, 0x6403, 0 /*DevId*/, 0x8086, 0 /*DevId*/, 0x8086, 0x3040, - 0x0008, 0x2000, 0x7e14, 0x0048, 0x1000, 0x00d8, 0x0000, 0x2700, - 0x6cc9, 0x3150, 0x0722, 0x040b, 0x0984, 0x0000, 0xc000, 0x0706, - 0x1008, 0x0000, 0x0f04, 0x7fff, 0x4d01, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0x0100, 0x4000, 0x121c, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, -}; - -/* PCI interface */ - -static void -e1000_mmio_setup(E1000State *d) -{ - int i; - const uint32_t excluded_regs[] = { - E1000_MDIC, E1000_ICR, E1000_ICS, E1000_IMS, - E1000_IMC, E1000_TCTL, E1000_TDT, PNPMMIO_SIZE - }; - - memory_region_init_io(&d->mmio, OBJECT(d), &e1000_mmio_ops, d, - "e1000-mmio", PNPMMIO_SIZE); - memory_region_add_coalescing(&d->mmio, 0, excluded_regs[0]); - for (i = 0; excluded_regs[i] != PNPMMIO_SIZE; i++) - memory_region_add_coalescing(&d->mmio, excluded_regs[i] + 4, - excluded_regs[i+1] - excluded_regs[i] - 4); - memory_region_init_io(&d->io, OBJECT(d), &e1000_io_ops, d, "e1000-io", IOPORT_SIZE); -} - -static void -pci_e1000_uninit(PCIDevice *dev) -{ - E1000State *d = E1000(dev); - - timer_del(d->autoneg_timer); - timer_free(d->autoneg_timer); - timer_del(d->mit_timer); - timer_free(d->mit_timer); - qemu_del_nic(d->nic); -} - -static NetClientInfo net_e1000_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .can_receive = e1000_can_receive, - .receive = e1000_receive, - .receive_iov = e1000_receive_iov, - .link_status_changed = e1000_set_link_status, -}; - -static void e1000_write_config(PCIDevice *pci_dev, uint32_t address, - uint32_t val, int len) -{ - E1000State *s = E1000(pci_dev); - - pci_default_write_config(pci_dev, address, val, len); - - if (range_covers_byte(address, len, PCI_COMMAND) && - (pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } -} - - -static void pci_e1000_realize(PCIDevice *pci_dev, Error **errp) -{ - DeviceState *dev = DEVICE(pci_dev); - E1000State *d = E1000(pci_dev); - PCIDeviceClass *pdc = PCI_DEVICE_GET_CLASS(pci_dev); - uint8_t *pci_conf; - uint16_t checksum = 0; - int i; - uint8_t *macaddr; - - pci_dev->config_write = e1000_write_config; - - pci_conf = pci_dev->config; - - /* TODO: RST# value should be 0, PCI spec 6.2.4 */ - pci_conf[PCI_CACHE_LINE_SIZE] = 0x10; - - pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */ - - e1000_mmio_setup(d); - - pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); - - pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->io); - - memmove(d->eeprom_data, e1000_eeprom_template, - sizeof e1000_eeprom_template); - qemu_macaddr_default_if_unset(&d->conf.macaddr); - macaddr = d->conf.macaddr.a; - for (i = 0; i < 3; i++) - d->eeprom_data[i] = (macaddr[2*i+1]<<8) | macaddr[2*i]; - d->eeprom_data[11] = d->eeprom_data[13] = pdc->device_id; - for (i = 0; i < EEPROM_CHECKSUM_REG; i++) - checksum += d->eeprom_data[i]; - checksum = (uint16_t) EEPROM_SUM - checksum; - d->eeprom_data[EEPROM_CHECKSUM_REG] = checksum; - - d->nic = qemu_new_nic(&net_e1000_info, &d->conf, - object_get_typename(OBJECT(d)), dev->id, d); - - qemu_format_nic_info_str(qemu_get_queue(d->nic), macaddr); - - d->autoneg_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, e1000_autoneg_timer, d); - d->mit_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, e1000_mit_timer, d); -} - -static void qdev_e1000_reset(DeviceState *dev) -{ - E1000State *d = E1000(dev); - e1000_reset(d); -} - -static Property e1000_properties[] = { - DEFINE_NIC_PROPERTIES(E1000State, conf), - DEFINE_PROP_BIT("autonegotiation", E1000State, - 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(), -}; - -typedef struct E1000Info { - const char *name; - uint16_t device_id; - uint8_t revision; - uint16_t phy_id2; -} E1000Info; - -static void e1000_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - E1000BaseClass *e = E1000_DEVICE_CLASS(klass); - const E1000Info *info = data; - - k->realize = pci_e1000_realize; - k->exit = pci_e1000_uninit; - k->romfile = "efi-e1000.rom"; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = info->device_id; - k->revision = info->revision; - e->phy_id2 = info->phy_id2; - k->class_id = PCI_CLASS_NETWORK_ETHERNET; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->desc = "Intel Gigabit Ethernet"; - dc->reset = qdev_e1000_reset; - dc->vmsd = &vmstate_e1000; - dc->props = e1000_properties; -} - -static void e1000_instance_init(Object *obj) -{ - E1000State *n = E1000(obj); - device_add_bootindex_property(obj, &n->conf.bootindex, - "bootindex", "/ethernet-phy@0", - DEVICE(n), NULL); -} - -static const TypeInfo e1000_base_info = { - .name = TYPE_E1000_BASE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(E1000State), - .instance_init = e1000_instance_init, - .class_size = sizeof(E1000BaseClass), - .abstract = true, -}; - -static const E1000Info e1000_devices[] = { - { - .name = "e1000", - .device_id = E1000_DEV_ID_82540EM, - .revision = 0x03, - .phy_id2 = E1000_PHY_ID2_8254xx_DEFAULT, - }, - { - .name = "e1000-82544gc", - .device_id = E1000_DEV_ID_82544GC_COPPER, - .revision = 0x03, - .phy_id2 = E1000_PHY_ID2_82544x, - }, - { - .name = "e1000-82545em", - .device_id = E1000_DEV_ID_82545EM_COPPER, - .revision = 0x03, - .phy_id2 = E1000_PHY_ID2_8254xx_DEFAULT, - }, -}; - -static void e1000_register_types(void) -{ - int i; - - type_register_static(&e1000_base_info); - for (i = 0; i < ARRAY_SIZE(e1000_devices); i++) { - const E1000Info *info = &e1000_devices[i]; - TypeInfo type_info = {}; - - type_info.name = info->name; - type_info.parent = TYPE_E1000_BASE; - type_info.class_data = (void *)info; - type_info.class_init = e1000_class_init; - type_info.instance_init = e1000_instance_init; - - type_register(&type_info); - } -} - -type_init(e1000_register_types) diff --git a/qemu/hw/net/e1000_regs.h b/qemu/hw/net/e1000_regs.h deleted file mode 100644 index 1c40244ab..000000000 --- a/qemu/hw/net/e1000_regs.h +++ /dev/null @@ -1,908 +0,0 @@ -/******************************************************************************* - - Intel PRO/1000 Linux driver - Copyright(c) 1999 - 2006 Intel Corporation. - - 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, 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 . - - The full GNU General Public License is included in this distribution in - the file called "COPYING". - - Contact Information: - Linux NICS - e1000-devel Mailing List - Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - -*******************************************************************************/ - -/* e1000_hw.h - * Structures, enums, and macros for the MAC - */ - -#ifndef _E1000_HW_H_ -#define _E1000_HW_H_ - - -/* PCI Device IDs */ -#define E1000_DEV_ID_82542 0x1000 -#define E1000_DEV_ID_82543GC_FIBER 0x1001 -#define E1000_DEV_ID_82543GC_COPPER 0x1004 -#define E1000_DEV_ID_82544EI_COPPER 0x1008 -#define E1000_DEV_ID_82544EI_FIBER 0x1009 -#define E1000_DEV_ID_82544GC_COPPER 0x100C -#define E1000_DEV_ID_82544GC_LOM 0x100D -#define E1000_DEV_ID_82540EM 0x100E -#define E1000_DEV_ID_82540EM_LOM 0x1015 -#define E1000_DEV_ID_82540EP_LOM 0x1016 -#define E1000_DEV_ID_82540EP 0x1017 -#define E1000_DEV_ID_82540EP_LP 0x101E -#define E1000_DEV_ID_82545EM_COPPER 0x100F -#define E1000_DEV_ID_82545EM_FIBER 0x1011 -#define E1000_DEV_ID_82545GM_COPPER 0x1026 -#define E1000_DEV_ID_82545GM_FIBER 0x1027 -#define E1000_DEV_ID_82545GM_SERDES 0x1028 -#define E1000_DEV_ID_82546EB_COPPER 0x1010 -#define E1000_DEV_ID_82546EB_FIBER 0x1012 -#define E1000_DEV_ID_82546EB_QUAD_COPPER 0x101D -#define E1000_DEV_ID_82541EI 0x1013 -#define E1000_DEV_ID_82541EI_MOBILE 0x1018 -#define E1000_DEV_ID_82541ER_LOM 0x1014 -#define E1000_DEV_ID_82541ER 0x1078 -#define E1000_DEV_ID_82547GI 0x1075 -#define E1000_DEV_ID_82541GI 0x1076 -#define E1000_DEV_ID_82541GI_MOBILE 0x1077 -#define E1000_DEV_ID_82541GI_LF 0x107C -#define E1000_DEV_ID_82546GB_COPPER 0x1079 -#define E1000_DEV_ID_82546GB_FIBER 0x107A -#define E1000_DEV_ID_82546GB_SERDES 0x107B -#define E1000_DEV_ID_82546GB_PCIE 0x108A -#define E1000_DEV_ID_82546GB_QUAD_COPPER 0x1099 -#define E1000_DEV_ID_82547EI 0x1019 -#define E1000_DEV_ID_82547EI_MOBILE 0x101A -#define E1000_DEV_ID_82571EB_COPPER 0x105E -#define E1000_DEV_ID_82571EB_FIBER 0x105F -#define E1000_DEV_ID_82571EB_SERDES 0x1060 -#define E1000_DEV_ID_82571EB_QUAD_COPPER 0x10A4 -#define E1000_DEV_ID_82571PT_QUAD_COPPER 0x10D5 -#define E1000_DEV_ID_82571EB_QUAD_FIBER 0x10A5 -#define E1000_DEV_ID_82571EB_QUAD_COPPER_LOWPROFILE 0x10BC -#define E1000_DEV_ID_82571EB_SERDES_DUAL 0x10D9 -#define E1000_DEV_ID_82571EB_SERDES_QUAD 0x10DA -#define E1000_DEV_ID_82572EI_COPPER 0x107D -#define E1000_DEV_ID_82572EI_FIBER 0x107E -#define E1000_DEV_ID_82572EI_SERDES 0x107F -#define E1000_DEV_ID_82572EI 0x10B9 -#define E1000_DEV_ID_82573E 0x108B -#define E1000_DEV_ID_82573E_IAMT 0x108C -#define E1000_DEV_ID_82573L 0x109A -#define E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3 0x10B5 -#define E1000_DEV_ID_80003ES2LAN_COPPER_DPT 0x1096 -#define E1000_DEV_ID_80003ES2LAN_SERDES_DPT 0x1098 -#define E1000_DEV_ID_80003ES2LAN_COPPER_SPT 0x10BA -#define E1000_DEV_ID_80003ES2LAN_SERDES_SPT 0x10BB - -#define E1000_DEV_ID_ICH8_IGP_M_AMT 0x1049 -#define E1000_DEV_ID_ICH8_IGP_AMT 0x104A -#define E1000_DEV_ID_ICH8_IGP_C 0x104B -#define E1000_DEV_ID_ICH8_IFE 0x104C -#define E1000_DEV_ID_ICH8_IFE_GT 0x10C4 -#define E1000_DEV_ID_ICH8_IFE_G 0x10C5 -#define E1000_DEV_ID_ICH8_IGP_M 0x104D - -/* Device Specific Register Defaults */ -#define E1000_PHY_ID2_82541x 0x380 -#define E1000_PHY_ID2_82544x 0xC30 -#define E1000_PHY_ID2_8254xx_DEFAULT 0xC20 /* 82540x, 82545x, and 82546x */ -#define E1000_PHY_ID2_82573x 0xCC0 - -/* Register Set. (82543, 82544) - * - * Registers are defined to be 32 bits and should be accessed as 32 bit values. - * These registers are physically located on the NIC, but are mapped into the - * host memory address space. - * - * RW - register is both readable and writable - * RO - register is read only - * WO - register is write only - * R/clr - register is read only and is cleared when read - * A - register array - */ -#define E1000_CTRL 0x00000 /* Device Control - RW */ -#define E1000_CTRL_DUP 0x00004 /* Device Control Duplicate (Shadow) - RW */ -#define E1000_STATUS 0x00008 /* Device Status - RO */ -#define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */ -#define E1000_EERD 0x00014 /* EEPROM Read - RW */ -#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */ -#define E1000_FLA 0x0001C /* Flash Access - RW */ -#define E1000_MDIC 0x00020 /* MDI Control - RW */ -#define E1000_SCTL 0x00024 /* SerDes Control - RW */ -#define E1000_FEXTNVM 0x00028 /* Future Extended NVM register */ -#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */ -#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */ -#define E1000_FCT 0x00030 /* Flow Control Type - RW */ -#define E1000_VET 0x00038 /* VLAN Ether Type - RW */ -#define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */ -#define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */ -#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */ -#define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */ -#define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */ -#define E1000_IAM 0x000E0 /* Interrupt Acknowledge Auto Mask */ -#define E1000_RCTL 0x00100 /* RX Control - RW */ -#define E1000_RDTR1 0x02820 /* RX Delay Timer (1) - RW */ -#define E1000_RDBAL1 0x02900 /* RX Descriptor Base Address Low (1) - RW */ -#define E1000_RDBAH1 0x02904 /* RX Descriptor Base Address High (1) - RW */ -#define E1000_RDLEN1 0x02908 /* RX Descriptor Length (1) - RW */ -#define E1000_RDH1 0x02910 /* RX Descriptor Head (1) - RW */ -#define E1000_RDT1 0x02918 /* RX Descriptor Tail (1) - RW */ -#define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */ -#define E1000_TXCW 0x00178 /* TX Configuration Word - RW */ -#define E1000_RXCW 0x00180 /* RX Configuration Word - RO */ -#define E1000_TCTL 0x00400 /* TX Control - RW */ -#define E1000_TCTL_EXT 0x00404 /* Extended TX Control - RW */ -#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ -#define E1000_TBT 0x00448 /* TX Burst Timer - RW */ -#define E1000_AIT 0x00458 /* Adaptive Interframe Spacing Throttle - RW */ -#define E1000_LEDCTL 0x00E00 /* LED Control - RW */ -#define E1000_EXTCNF_CTRL 0x00F00 /* Extended Configuration Control */ -#define E1000_EXTCNF_SIZE 0x00F08 /* Extended Configuration Size */ -#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_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 */ -#define E1000_FLASHT 0x01028 /* FLASH Timer Register */ -#define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */ -#define E1000_FLSWCTL 0x01030 /* FLASH control register */ -#define E1000_FLSWDATA 0x01034 /* FLASH data register */ -#define E1000_FLSWCNT 0x01038 /* FLASH Access Counter */ -#define E1000_FLOP 0x0103C /* FLASH Opcode Register */ -#define E1000_ERT 0x02008 /* Early Rx Threshold - RW */ -#define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */ -#define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - RW */ -#define E1000_PSRCTL 0x02170 /* Packet Split Receive Control - RW */ -#define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */ -#define E1000_RDBAH 0x02804 /* RX Descriptor Base Address High - RW */ -#define E1000_RDLEN 0x02808 /* RX Descriptor Length - RW */ -#define E1000_RDH 0x02810 /* RX Descriptor Head - RW */ -#define E1000_RDT 0x02818 /* RX Descriptor Tail - RW */ -#define E1000_RDTR 0x02820 /* RX Delay Timer - RW */ -#define E1000_RDBAL0 E1000_RDBAL /* RX Desc Base Address Low (0) - RW */ -#define E1000_RDBAH0 E1000_RDBAH /* RX Desc Base Address High (0) - RW */ -#define E1000_RDLEN0 E1000_RDLEN /* RX Desc Length (0) - RW */ -#define E1000_RDH0 E1000_RDH /* RX Desc Head (0) - RW */ -#define E1000_RDT0 E1000_RDT /* RX Desc Tail (0) - RW */ -#define E1000_RDTR0 E1000_RDTR /* RX Delay Timer (0) - RW */ -#define E1000_RXDCTL 0x02828 /* RX Descriptor Control queue 0 - RW */ -#define E1000_RXDCTL1 0x02928 /* RX Descriptor Control queue 1 - RW */ -#define E1000_RADV 0x0282C /* RX Interrupt Absolute Delay Timer - RW */ -#define E1000_RSRPD 0x02C00 /* RX Small Packet Detect - RW */ -#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 */ -#define E1000_TDFTS 0x03428 /* TX Data FIFO Tail Saved - RW */ -#define E1000_TDFPC 0x03430 /* TX Data FIFO Packet Count - RW */ -#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ -#define E1000_TDBAH 0x03804 /* TX Descriptor Base Address High - RW */ -#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */ -#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */ -#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */ -#define E1000_TIDV 0x03820 /* TX Interrupt Delay Value - RW */ -#define E1000_TXDCTL 0x03828 /* TX Descriptor Control - RW */ -#define E1000_TADV 0x0382C /* TX Interrupt Absolute Delay Val - RW */ -#define E1000_TSPMT 0x03830 /* TCP Segmentation PAD & Min Threshold - RW */ -#define E1000_TARC0 0x03840 /* TX Arbitration Count (0) */ -#define E1000_TDBAL1 0x03900 /* TX Desc Base Address Low (1) - RW */ -#define E1000_TDBAH1 0x03904 /* TX Desc Base Address High (1) - RW */ -#define E1000_TDLEN1 0x03908 /* TX Desc Length (1) - RW */ -#define E1000_TDH1 0x03910 /* TX Desc Head (1) - RW */ -#define E1000_TDT1 0x03918 /* TX Desc Tail (1) - RW */ -#define E1000_TXDCTL1 0x03928 /* TX Descriptor Control (1) - RW */ -#define E1000_TARC1 0x03940 /* TX Arbitration Count (1) */ -#define E1000_CRCERRS 0x04000 /* CRC Error Count - R/clr */ -#define E1000_ALGNERRC 0x04004 /* Alignment Error Count - R/clr */ -#define E1000_SYMERRS 0x04008 /* Symbol Error Count - R/clr */ -#define E1000_RXERRC 0x0400C /* Receive Error Count - R/clr */ -#define E1000_MPC 0x04010 /* Missed Packet Count - R/clr */ -#define E1000_SCC 0x04014 /* Single Collision Count - R/clr */ -#define E1000_ECOL 0x04018 /* Excessive Collision Count - R/clr */ -#define E1000_MCC 0x0401C /* Multiple Collision Count - R/clr */ -#define E1000_LATECOL 0x04020 /* Late Collision Count - R/clr */ -#define E1000_COLC 0x04028 /* Collision Count - R/clr */ -#define E1000_DC 0x04030 /* Defer Count - R/clr */ -#define E1000_TNCRS 0x04034 /* TX-No CRS - R/clr */ -#define E1000_SEC 0x04038 /* Sequence Error Count - R/clr */ -#define E1000_CEXTERR 0x0403C /* Carrier Extension Error Count - R/clr */ -#define E1000_RLEC 0x04040 /* Receive Length Error Count - R/clr */ -#define E1000_XONRXC 0x04048 /* XON RX Count - R/clr */ -#define E1000_XONTXC 0x0404C /* XON TX Count - R/clr */ -#define E1000_XOFFRXC 0x04050 /* XOFF RX Count - R/clr */ -#define E1000_XOFFTXC 0x04054 /* XOFF TX Count - R/clr */ -#define E1000_FCRUC 0x04058 /* Flow Control RX Unsupported Count- R/clr */ -#define E1000_PRC64 0x0405C /* Packets RX (64 bytes) - R/clr */ -#define E1000_PRC127 0x04060 /* Packets RX (65-127 bytes) - R/clr */ -#define E1000_PRC255 0x04064 /* Packets RX (128-255 bytes) - R/clr */ -#define E1000_PRC511 0x04068 /* Packets RX (255-511 bytes) - R/clr */ -#define E1000_PRC1023 0x0406C /* Packets RX (512-1023 bytes) - R/clr */ -#define E1000_PRC1522 0x04070 /* Packets RX (1024-1522 bytes) - R/clr */ -#define E1000_GPRC 0x04074 /* Good Packets RX Count - R/clr */ -#define E1000_BPRC 0x04078 /* Broadcast Packets RX Count - R/clr */ -#define E1000_MPRC 0x0407C /* Multicast Packets RX Count - R/clr */ -#define E1000_GPTC 0x04080 /* Good Packets TX Count - R/clr */ -#define E1000_GORCL 0x04088 /* Good Octets RX Count Low - R/clr */ -#define E1000_GORCH 0x0408C /* Good Octets RX Count High - R/clr */ -#define E1000_GOTCL 0x04090 /* Good Octets TX Count Low - R/clr */ -#define E1000_GOTCH 0x04094 /* Good Octets TX Count High - R/clr */ -#define E1000_RNBC 0x040A0 /* RX No Buffers Count - R/clr */ -#define E1000_RUC 0x040A4 /* RX Undersize Count - R/clr */ -#define E1000_RFC 0x040A8 /* RX Fragment Count - R/clr */ -#define E1000_ROC 0x040AC /* RX Oversize Count - R/clr */ -#define E1000_RJC 0x040B0 /* RX Jabber Count - R/clr */ -#define E1000_MGTPRC 0x040B4 /* Management Packets RX Count - R/clr */ -#define E1000_MGTPDC 0x040B8 /* Management Packets Dropped Count - R/clr */ -#define E1000_MGTPTC 0x040BC /* Management Packets TX Count - R/clr */ -#define E1000_TORL 0x040C0 /* Total Octets RX Low - R/clr */ -#define E1000_TORH 0x040C4 /* Total Octets RX High - R/clr */ -#define E1000_TOTL 0x040C8 /* Total Octets TX Low - R/clr */ -#define E1000_TOTH 0x040CC /* Total Octets TX High - R/clr */ -#define E1000_TPR 0x040D0 /* Total Packets RX - R/clr */ -#define E1000_TPT 0x040D4 /* Total Packets TX - R/clr */ -#define E1000_PTC64 0x040D8 /* Packets TX (64 bytes) - R/clr */ -#define E1000_PTC127 0x040DC /* Packets TX (65-127 bytes) - R/clr */ -#define E1000_PTC255 0x040E0 /* Packets TX (128-255 bytes) - R/clr */ -#define E1000_PTC511 0x040E4 /* Packets TX (256-511 bytes) - R/clr */ -#define E1000_PTC1023 0x040E8 /* Packets TX (512-1023 bytes) - R/clr */ -#define E1000_PTC1522 0x040EC /* Packets TX (1024-1522 Bytes) - R/clr */ -#define E1000_MPTC 0x040F0 /* Multicast Packets TX Count - R/clr */ -#define E1000_BPTC 0x040F4 /* Broadcast Packets TX Count - R/clr */ -#define E1000_TSCTC 0x040F8 /* TCP Segmentation Context TX - R/clr */ -#define E1000_TSCTFC 0x040FC /* TCP Segmentation Context TX Fail - R/clr */ -#define E1000_IAC 0x04100 /* Interrupt Assertion Count */ -#define E1000_ICRXPTC 0x04104 /* Interrupt Cause Rx Packet Timer Expire Count */ -#define E1000_ICRXATC 0x04108 /* Interrupt Cause Rx Absolute Timer Expire Count */ -#define E1000_ICTXPTC 0x0410C /* Interrupt Cause Tx Packet Timer Expire Count */ -#define E1000_ICTXATC 0x04110 /* Interrupt Cause Tx Absolute Timer Expire Count */ -#define E1000_ICTXQEC 0x04118 /* Interrupt Cause Tx Queue Empty Count */ -#define E1000_ICTXQMTC 0x0411C /* Interrupt Cause Tx Queue Minimum Threshold Count */ -#define E1000_ICRXDMTC 0x04120 /* Interrupt Cause Rx Descriptor Minimum Threshold Count */ -#define E1000_ICRXOC 0x04124 /* Interrupt Cause Receiver Overrun Count */ -#define E1000_RXCSUM 0x05000 /* RX Checksum Control - RW */ -#define E1000_RFCTL 0x05008 /* Receive Filter Control*/ -#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */ -#define E1000_RA 0x05400 /* Receive Address - RW Array */ -#define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */ -#define E1000_WUC 0x05800 /* Wakeup Control - RW */ -#define E1000_WUFC 0x05808 /* Wakeup Filter Control - RW */ -#define E1000_WUS 0x05810 /* Wakeup Status - RO */ -#define E1000_MANC 0x05820 /* Management Control - RW */ -#define E1000_IPAV 0x05838 /* IP Address Valid - RW */ -#define E1000_IP4AT 0x05840 /* IPv4 Address Table - RW Array */ -#define E1000_IP6AT 0x05880 /* IPv6 Address Table - RW Array */ -#define E1000_WUPL 0x05900 /* Wakeup Packet Length - RW */ -#define E1000_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */ -#define E1000_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */ -#define E1000_HOST_IF 0x08800 /* Host Interface */ -#define E1000_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */ -#define E1000_FFVT 0x09800 /* Flexible Filter Value Table - RW Array */ - -#define E1000_KUMCTRLSTA 0x00034 /* MAC-PHY interface - RW */ -#define E1000_MDPHYA 0x0003C /* PHY address - RW */ -#define E1000_MANC2H 0x05860 /* Management Control To Host - RW */ -#define E1000_SW_FW_SYNC 0x05B5C /* Software-Firmware Synchronization - RW */ - -#define E1000_GCR 0x05B00 /* PCI-Ex Control */ -#define E1000_GSCL_1 0x05B10 /* PCI-Ex Statistic Control #1 */ -#define E1000_GSCL_2 0x05B14 /* PCI-Ex Statistic Control #2 */ -#define E1000_GSCL_3 0x05B18 /* PCI-Ex Statistic Control #3 */ -#define E1000_GSCL_4 0x05B1C /* PCI-Ex Statistic Control #4 */ -#define E1000_FACTPS 0x05B30 /* Function Active and Power State to MNG */ -#define E1000_SWSM 0x05B50 /* SW Semaphore */ -#define E1000_FWSM 0x05B54 /* FW Semaphore */ -#define E1000_FFLT_DBG 0x05F04 /* Debug Register */ -#define E1000_HICR 0x08F00 /* Host Inteface Control */ - -/* RSS registers */ -#define E1000_CPUVEC 0x02C10 /* CPU Vector Register - RW */ -#define E1000_MRQC 0x05818 /* Multiple Receive Control - RW */ -#define E1000_RETA 0x05C00 /* Redirection Table - RW Array */ -#define E1000_RSSRK 0x05C80 /* RSS Random Key - RW Array */ -#define E1000_RSSIM 0x05864 /* RSS Interrupt Mask */ -#define E1000_RSSIR 0x05868 /* RSS Interrupt Request */ - -/* PHY 1000 MII Register/Bit Definitions */ -/* PHY Registers defined by IEEE */ -#define PHY_CTRL 0x00 /* Control Register */ -#define PHY_STATUS 0x01 /* Status Regiser */ -#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */ -#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */ -#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */ -#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */ -#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */ -#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */ -#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */ -#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */ -#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ -#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ - -#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */ -#define MAX_PHY_MULTI_PAGE_REG 0xF /* Registers equal on all pages */ - -/* M88E1000 Specific Registers */ -#define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */ -#define M88E1000_PHY_SPEC_STATUS 0x11 /* PHY Specific Status Register */ -#define M88E1000_INT_ENABLE 0x12 /* Interrupt Enable Register */ -#define M88E1000_INT_STATUS 0x13 /* Interrupt Status Register */ -#define M88E1000_EXT_PHY_SPEC_CTRL 0x14 /* Extended PHY Specific Control */ -#define M88E1000_RX_ERR_CNTR 0x15 /* Receive Error Counter */ - -#define M88E1000_PHY_EXT_CTRL 0x1A /* PHY extend control register */ -#define M88E1000_PHY_PAGE_SELECT 0x1D /* Reg 29 for page number setting */ -#define M88E1000_PHY_GEN_CONTROL 0x1E /* Its meaning depends on reg 29 */ -#define M88E1000_PHY_VCO_REG_BIT8 0x100 /* Bits 8 & 11 are adjusted for */ -#define M88E1000_PHY_VCO_REG_BIT11 0x800 /* improved BER performance */ - -/* PHY Control Register */ -#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ -#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ -#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ -#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ -#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ -#define MII_CR_POWER_DOWN 0x0800 /* Power down */ -#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ -#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ -#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ -#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ - -/* PHY Status Register */ -#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ -#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ -#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ -#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ -#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ -#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ -#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ -#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ -#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ -#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ -#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ -#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ -#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ -#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ -#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ - -/* PHY Link Partner Ability Register */ -#define MII_LPAR_LPACK 0x4000 /* Acked by link partner */ - -/* Interrupt Cause Read */ -#define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */ -#define E1000_ICR_TXQE 0x00000002 /* Transmit Queue empty */ -#define E1000_ICR_LSC 0x00000004 /* Link Status Change */ -#define E1000_ICR_RXSEQ 0x00000008 /* rx sequence error */ -#define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */ -#define E1000_ICR_RXO 0x00000040 /* rx overrun */ -#define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */ -#define E1000_ICR_MDAC 0x00000200 /* MDIO access complete */ -#define E1000_ICR_RXCFG 0x00000400 /* RX /c/ ordered set */ -#define E1000_ICR_GPI_EN0 0x00000800 /* GP Int 0 */ -#define E1000_ICR_GPI_EN1 0x00001000 /* GP Int 1 */ -#define E1000_ICR_GPI_EN2 0x00002000 /* GP Int 2 */ -#define E1000_ICR_GPI_EN3 0x00004000 /* GP Int 3 */ -#define E1000_ICR_TXD_LOW 0x00008000 -#define E1000_ICR_SRPD 0x00010000 -#define E1000_ICR_ACK 0x00020000 /* Receive Ack frame */ -#define E1000_ICR_MNG 0x00040000 /* Manageability event */ -#define E1000_ICR_DOCK 0x00080000 /* Dock/Undock */ -#define E1000_ICR_INT_ASSERTED 0x80000000 /* If this bit asserted, the driver should claim the interrupt */ -#define E1000_ICR_RXD_FIFO_PAR0 0x00100000 /* queue 0 Rx descriptor FIFO parity error */ -#define E1000_ICR_TXD_FIFO_PAR0 0x00200000 /* queue 0 Tx descriptor FIFO parity error */ -#define E1000_ICR_HOST_ARB_PAR 0x00400000 /* host arb read buffer parity error */ -#define E1000_ICR_PB_PAR 0x00800000 /* packet buffer parity error */ -#define E1000_ICR_RXD_FIFO_PAR1 0x01000000 /* queue 1 Rx descriptor FIFO parity error */ -#define E1000_ICR_TXD_FIFO_PAR1 0x02000000 /* queue 1 Tx descriptor FIFO parity error */ -#define E1000_ICR_ALL_PARITY 0x03F00000 /* all parity error bits */ -#define E1000_ICR_DSW 0x00000020 /* FW changed the status of DISSW bit in the FWSM */ -#define E1000_ICR_PHYINT 0x00001000 /* LAN connected device generates an interrupt */ -#define E1000_ICR_EPRST 0x00100000 /* ME handware reset occurs */ - -/* Interrupt Cause Set */ -#define E1000_ICS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ -#define E1000_ICS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ -#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */ -#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ -#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ -#define E1000_ICS_RXO E1000_ICR_RXO /* rx overrun */ -#define E1000_ICS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ -#define E1000_ICS_MDAC E1000_ICR_MDAC /* MDIO access complete */ -#define E1000_ICS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ -#define E1000_ICS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ -#define E1000_ICS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ -#define E1000_ICS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ -#define E1000_ICS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ -#define E1000_ICS_TXD_LOW E1000_ICR_TXD_LOW -#define E1000_ICS_SRPD E1000_ICR_SRPD -#define E1000_ICS_ACK E1000_ICR_ACK /* Receive Ack frame */ -#define E1000_ICS_MNG E1000_ICR_MNG /* Manageability event */ -#define E1000_ICS_DOCK E1000_ICR_DOCK /* Dock/Undock */ -#define E1000_ICS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ -#define E1000_ICS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ -#define E1000_ICS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ -#define E1000_ICS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ -#define E1000_ICS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ -#define E1000_ICS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ -#define E1000_ICS_DSW E1000_ICR_DSW -#define E1000_ICS_PHYINT E1000_ICR_PHYINT -#define E1000_ICS_EPRST E1000_ICR_EPRST - -/* Interrupt Mask Set */ -#define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ -#define E1000_IMS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ -#define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */ -#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ -#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ -#define E1000_IMS_RXO E1000_ICR_RXO /* rx overrun */ -#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ -#define E1000_IMS_MDAC E1000_ICR_MDAC /* MDIO access complete */ -#define E1000_IMS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ -#define E1000_IMS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ -#define E1000_IMS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ -#define E1000_IMS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ -#define E1000_IMS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ -#define E1000_IMS_TXD_LOW E1000_ICR_TXD_LOW -#define E1000_IMS_SRPD E1000_ICR_SRPD -#define E1000_IMS_ACK E1000_ICR_ACK /* Receive Ack frame */ -#define E1000_IMS_MNG E1000_ICR_MNG /* Manageability event */ -#define E1000_IMS_DOCK E1000_ICR_DOCK /* Dock/Undock */ -#define E1000_IMS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ -#define E1000_IMS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ -#define E1000_IMS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ -#define E1000_IMS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ -#define E1000_IMS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ -#define E1000_IMS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ -#define E1000_IMS_DSW E1000_ICR_DSW -#define E1000_IMS_PHYINT E1000_ICR_PHYINT -#define E1000_IMS_EPRST E1000_ICR_EPRST - -/* Interrupt Mask Clear */ -#define E1000_IMC_TXDW E1000_ICR_TXDW /* Transmit desc written back */ -#define E1000_IMC_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ -#define E1000_IMC_LSC E1000_ICR_LSC /* Link Status Change */ -#define E1000_IMC_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ -#define E1000_IMC_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ -#define E1000_IMC_RXO E1000_ICR_RXO /* rx overrun */ -#define E1000_IMC_RXT0 E1000_ICR_RXT0 /* rx timer intr */ -#define E1000_IMC_MDAC E1000_ICR_MDAC /* MDIO access complete */ -#define E1000_IMC_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ -#define E1000_IMC_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ -#define E1000_IMC_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ -#define E1000_IMC_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ -#define E1000_IMC_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ -#define E1000_IMC_TXD_LOW E1000_ICR_TXD_LOW -#define E1000_IMC_SRPD E1000_ICR_SRPD -#define E1000_IMC_ACK E1000_ICR_ACK /* Receive Ack frame */ -#define E1000_IMC_MNG E1000_ICR_MNG /* Manageability event */ -#define E1000_IMC_DOCK E1000_ICR_DOCK /* Dock/Undock */ -#define E1000_IMC_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ -#define E1000_IMC_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ -#define E1000_IMC_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ -#define E1000_IMC_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ -#define E1000_IMC_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ -#define E1000_IMC_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ -#define E1000_IMC_DSW E1000_ICR_DSW -#define E1000_IMC_PHYINT E1000_ICR_PHYINT -#define E1000_IMC_EPRST E1000_ICR_EPRST - -/* Receive Control */ -#define E1000_RCTL_RST 0x00000001 /* Software reset */ -#define E1000_RCTL_EN 0x00000002 /* enable */ -#define E1000_RCTL_SBP 0x00000004 /* store bad packet */ -#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */ -#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */ -#define E1000_RCTL_LPE 0x00000020 /* long packet enable */ -#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */ -#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */ -#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */ -#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */ -#define E1000_RCTL_DTYP_MASK 0x00000C00 /* Descriptor type mask */ -#define E1000_RCTL_DTYP_PS 0x00000400 /* Packet Split descriptor */ -#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */ -#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */ -#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */ -#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */ -#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */ -#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */ -#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */ -#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */ -#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */ -#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ -/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */ -#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */ -#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */ -#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */ -#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */ -/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */ -#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */ -#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */ -#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */ -#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */ -#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */ -#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */ -#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */ -#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */ -#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */ -#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ -#define E1000_RCTL_FLXBUF_MASK 0x78000000 /* Flexible buffer size */ -#define E1000_RCTL_FLXBUF_SHIFT 27 /* Flexible buffer shift */ - - -#define E1000_EEPROM_SWDPIN0 0x0001 /* SWDPIN 0 EEPROM Value */ -#define E1000_EEPROM_LED_LOGIC 0x0020 /* Led Logic Word */ -#define E1000_EEPROM_RW_REG_DATA 16 /* Offset to data in EEPROM read/write registers */ -#define E1000_EEPROM_RW_REG_DONE 0x10 /* Offset to READ/WRITE done bit */ -#define E1000_EEPROM_RW_REG_START 1 /* First bit for telling part to start operation */ -#define E1000_EEPROM_RW_ADDR_SHIFT 8 /* Shift to the address bits */ -#define E1000_EEPROM_POLL_WRITE 1 /* Flag for polling for write complete */ -#define E1000_EEPROM_POLL_READ 0 /* Flag for polling for read complete */ -/* Register Bit Masks */ -/* Device Control */ -#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */ -#define E1000_CTRL_BEM 0x00000002 /* Endian Mode.0=little,1=big */ -#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */ -#define E1000_CTRL_GIO_MASTER_DISABLE 0x00000004 /*Blocks new Master requests */ -#define E1000_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */ -#define E1000_CTRL_TME 0x00000010 /* Test mode. 0=normal,1=test */ -#define E1000_CTRL_SLE 0x00000020 /* Serial Link on 0=dis,1=en */ -#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */ -#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */ -#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */ -#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */ -#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */ -#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */ -#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */ -#define E1000_CTRL_BEM32 0x00000400 /* Big Endian 32 mode */ -#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */ -#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */ -#define E1000_CTRL_D_UD_EN 0x00002000 /* Dock/Undock enable */ -#define E1000_CTRL_D_UD_POLARITY 0x00004000 /* Defined polarity of Dock/Undock indication in SDP[0] */ -#define E1000_CTRL_FORCE_PHY_RESET 0x00008000 /* Reset both PHY ports, through PHYRST_N pin */ -#define E1000_CTRL_EXT_LINK_EN 0x00010000 /* enable link status from external LINK_0 and LINK_1 pins */ -#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */ -#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */ -#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */ -#define E1000_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */ -#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */ -#define E1000_CTRL_SWDPIO1 0x00800000 /* SWDPIN 1 input or output */ -#define E1000_CTRL_SWDPIO2 0x01000000 /* SWDPIN 2 input or output */ -#define E1000_CTRL_SWDPIO3 0x02000000 /* SWDPIN 3 input or output */ -#define E1000_CTRL_RST 0x04000000 /* Global reset */ -#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */ -#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */ -#define E1000_CTRL_RTE 0x20000000 /* Routing tag enable */ -#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */ -#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */ -#define E1000_CTRL_SW2FW_INT 0x02000000 /* Initiate an interrupt to manageability engine */ - -/* Device Status */ -#define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */ -#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */ -#define E1000_STATUS_FUNC_MASK 0x0000000C /* PCI Function Mask */ -#define E1000_STATUS_FUNC_SHIFT 2 -#define E1000_STATUS_FUNC_0 0x00000000 /* Function 0 */ -#define E1000_STATUS_FUNC_1 0x00000004 /* Function 1 */ -#define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */ -#define E1000_STATUS_TBIMODE 0x00000020 /* TBI mode */ -#define E1000_STATUS_SPEED_MASK 0x000000C0 -#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */ -#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */ -#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */ -#define E1000_STATUS_LAN_INIT_DONE 0x00000200 /* Lan Init Completion - by EEPROM/Flash */ -#define E1000_STATUS_ASDV 0x00000300 /* Auto speed detect value */ -#define E1000_STATUS_DOCK_CI 0x00000800 /* Change in Dock/Undock state. Clear on write '0'. */ -#define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 /* Status of Master requests. */ -#define E1000_STATUS_MTXCKOK 0x00000400 /* MTX clock running OK */ -#define E1000_STATUS_PCI66 0x00000800 /* In 66Mhz slot */ -#define E1000_STATUS_BUS64 0x00001000 /* In 64 bit slot */ -#define E1000_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */ -#define E1000_STATUS_PCIX_SPEED 0x0000C000 /* PCI-X bus speed */ -#define E1000_STATUS_BMC_SKU_0 0x00100000 /* BMC USB redirect disabled */ -#define E1000_STATUS_BMC_SKU_1 0x00200000 /* BMC SRAM disabled */ -#define E1000_STATUS_BMC_SKU_2 0x00400000 /* BMC SDRAM disabled */ -#define E1000_STATUS_BMC_CRYPTO 0x00800000 /* BMC crypto disabled */ -#define E1000_STATUS_BMC_LITE 0x01000000 /* BMC external code execution disabled */ -#define E1000_STATUS_RGMII_ENABLE 0x02000000 /* RGMII disabled */ -#define E1000_STATUS_FUSE_8 0x04000000 -#define E1000_STATUS_FUSE_9 0x08000000 -#define E1000_STATUS_SERDES0_DIS 0x10000000 /* SERDES disabled on port 0 */ -#define E1000_STATUS_SERDES1_DIS 0x20000000 /* SERDES disabled on port 1 */ - -/* EEPROM/Flash Control */ -#define E1000_EECD_SK 0x00000001 /* EEPROM Clock */ -#define E1000_EECD_CS 0x00000002 /* EEPROM Chip Select */ -#define E1000_EECD_DI 0x00000004 /* EEPROM Data In */ -#define E1000_EECD_DO 0x00000008 /* EEPROM Data Out */ -#define E1000_EECD_FWE_MASK 0x00000030 -#define E1000_EECD_FWE_DIS 0x00000010 /* Disable FLASH writes */ -#define E1000_EECD_FWE_EN 0x00000020 /* Enable FLASH writes */ -#define E1000_EECD_FWE_SHIFT 4 -#define E1000_EECD_REQ 0x00000040 /* EEPROM Access Request */ -#define E1000_EECD_GNT 0x00000080 /* EEPROM Access Grant */ -#define E1000_EECD_PRES 0x00000100 /* EEPROM Present */ -#define E1000_EECD_SIZE 0x00000200 /* EEPROM Size (0=64 word 1=256 word) */ -#define E1000_EECD_ADDR_BITS 0x00000400 /* EEPROM Addressing bits based on type - * (0-small, 1-large) */ -#define E1000_EECD_TYPE 0x00002000 /* EEPROM Type (1-SPI, 0-Microwire) */ -#ifndef E1000_EEPROM_GRANT_ATTEMPTS -#define E1000_EEPROM_GRANT_ATTEMPTS 1000 /* EEPROM # attempts to gain grant */ -#endif -#define E1000_EECD_AUTO_RD 0x00000200 /* EEPROM Auto Read done */ -#define E1000_EECD_SIZE_EX_MASK 0x00007800 /* EEprom Size */ -#define E1000_EECD_SIZE_EX_SHIFT 11 -#define E1000_EECD_NVADDS 0x00018000 /* NVM Address Size */ -#define E1000_EECD_SELSHAD 0x00020000 /* Select Shadow RAM */ -#define E1000_EECD_INITSRAM 0x00040000 /* Initialize Shadow RAM */ -#define E1000_EECD_FLUPD 0x00080000 /* Update FLASH */ -#define E1000_EECD_AUPDEN 0x00100000 /* Enable Autonomous FLASH update */ -#define E1000_EECD_SHADV 0x00200000 /* Shadow RAM Data Valid */ -#define E1000_EECD_SEC1VAL 0x00400000 /* Sector One Valid */ -#define E1000_EECD_SECVAL_SHIFT 22 -#define E1000_STM_OPCODE 0xDB00 -#define E1000_HICR_FW_RESET 0xC0 - -#define E1000_SHADOW_RAM_WORDS 2048 -#define E1000_ICH_NVM_SIG_WORD 0x13 -#define E1000_ICH_NVM_SIG_MASK 0xC0 - -/* MDI Control */ -#define E1000_MDIC_DATA_MASK 0x0000FFFF -#define E1000_MDIC_REG_MASK 0x001F0000 -#define E1000_MDIC_REG_SHIFT 16 -#define E1000_MDIC_PHY_MASK 0x03E00000 -#define E1000_MDIC_PHY_SHIFT 21 -#define E1000_MDIC_OP_WRITE 0x04000000 -#define E1000_MDIC_OP_READ 0x08000000 -#define E1000_MDIC_READY 0x10000000 -#define E1000_MDIC_INT_EN 0x20000000 -#define E1000_MDIC_ERROR 0x40000000 - -/* EEPROM Commands - Microwire */ -#define EEPROM_READ_OPCODE_MICROWIRE 0x6 /* EEPROM read opcode */ -#define EEPROM_WRITE_OPCODE_MICROWIRE 0x5 /* EEPROM write opcode */ -#define EEPROM_ERASE_OPCODE_MICROWIRE 0x7 /* EEPROM erase opcode */ -#define EEPROM_EWEN_OPCODE_MICROWIRE 0x13 /* EEPROM erase/write enable */ -#define EEPROM_EWDS_OPCODE_MICROWIRE 0x10 /* EEPROM erast/write disable */ - -/* EEPROM Word Offsets */ -#define EEPROM_COMPAT 0x0003 -#define EEPROM_ID_LED_SETTINGS 0x0004 -#define EEPROM_VERSION 0x0005 -#define EEPROM_SERDES_AMPLITUDE 0x0006 /* For SERDES output amplitude adjustment. */ -#define EEPROM_PHY_CLASS_WORD 0x0007 -#define EEPROM_INIT_CONTROL1_REG 0x000A -#define EEPROM_INIT_CONTROL2_REG 0x000F -#define EEPROM_SWDEF_PINS_CTRL_PORT_1 0x0010 -#define EEPROM_INIT_CONTROL3_PORT_B 0x0014 -#define EEPROM_INIT_3GIO_3 0x001A -#define EEPROM_SWDEF_PINS_CTRL_PORT_0 0x0020 -#define EEPROM_INIT_CONTROL3_PORT_A 0x0024 -#define EEPROM_CFG 0x0012 -#define EEPROM_FLASH_VERSION 0x0032 -#define EEPROM_CHECKSUM_REG 0x003F - -#define E1000_EEPROM_CFG_DONE 0x00040000 /* MNG config cycle done */ -#define E1000_EEPROM_CFG_DONE_PORT_1 0x00080000 /* ...for second port */ - -/* Transmit Descriptor */ -struct e1000_tx_desc { - uint64_t buffer_addr; /* Address of the descriptor's data buffer */ - union { - uint32_t data; - struct { - uint16_t length; /* Data buffer length */ - uint8_t cso; /* Checksum offset */ - uint8_t cmd; /* Descriptor control */ - } flags; - } lower; - union { - uint32_t data; - struct { - uint8_t status; /* Descriptor status */ - uint8_t css; /* Checksum start */ - uint16_t special; - } fields; - } upper; -}; - -/* Transmit Descriptor bit definitions */ -#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */ -#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */ -#define E1000_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */ -#define E1000_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */ -#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */ -#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ -#define E1000_TXD_CMD_IC 0x04000000 /* Insert Checksum */ -#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */ -#define E1000_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */ -#define E1000_TXD_CMD_DEXT 0x20000000 /* Descriptor extension (0 = legacy) */ -#define E1000_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ -#define E1000_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */ -#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ -#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ -#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ -#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ -#define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */ -#define E1000_TXD_CMD_IP 0x02000000 /* IP packet */ -#define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ -#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ - -/* Transmit Control */ -#define E1000_TCTL_RST 0x00000001 /* software reset */ -#define E1000_TCTL_EN 0x00000002 /* enable tx */ -#define E1000_TCTL_BCE 0x00000004 /* busy check enable */ -#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ -#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ -#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ -#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */ -#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */ -#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ -#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ -#define E1000_TCTL_MULR 0x10000000 /* Multiple request support */ - -/* Receive Descriptor */ -struct e1000_rx_desc { - uint64_t buffer_addr; /* Address of the descriptor's data buffer */ - uint16_t length; /* Length of data DMAed into data buffer */ - uint16_t csum; /* Packet checksum */ - uint8_t status; /* Descriptor status */ - uint8_t errors; /* Descriptor Errors */ - uint16_t special; -}; - -/* Receive Descriptor bit definitions */ -#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ -#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ -#define E1000_RXD_STAT_IXSM 0x04 /* Ignore checksum */ -#define E1000_RXD_STAT_VP 0x08 /* IEEE VLAN Packet */ -#define E1000_RXD_STAT_UDPCS 0x10 /* UDP xsum caculated */ -#define E1000_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */ -#define E1000_RXD_STAT_IPCS 0x40 /* IP xsum calculated */ -#define E1000_RXD_STAT_PIF 0x80 /* passed in-exact filter */ -#define E1000_RXD_STAT_IPIDV 0x200 /* IP identification valid */ -#define E1000_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */ -#define E1000_RXD_STAT_ACK 0x8000 /* ACK Packet indication */ -#define E1000_RXD_ERR_CE 0x01 /* CRC Error */ -#define E1000_RXD_ERR_SE 0x02 /* Symbol Error */ -#define E1000_RXD_ERR_SEQ 0x04 /* Sequence Error */ -#define E1000_RXD_ERR_CXE 0x10 /* Carrier Extension Error */ -#define E1000_RXD_ERR_TCPE 0x20 /* TCP/UDP Checksum Error */ -#define E1000_RXD_ERR_IPE 0x40 /* IP Checksum Error */ -#define E1000_RXD_ERR_RXE 0x80 /* Rx Data Error */ -#define E1000_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */ -#define E1000_RXD_SPC_PRI_MASK 0xE000 /* Priority is in upper 3 bits */ -#define E1000_RXD_SPC_PRI_SHIFT 13 -#define E1000_RXD_SPC_CFI_MASK 0x1000 /* CFI is bit 12 */ -#define E1000_RXD_SPC_CFI_SHIFT 12 - -#define E1000_RXDEXT_STATERR_CE 0x01000000 -#define E1000_RXDEXT_STATERR_SE 0x02000000 -#define E1000_RXDEXT_STATERR_SEQ 0x04000000 -#define E1000_RXDEXT_STATERR_CXE 0x10000000 -#define E1000_RXDEXT_STATERR_TCPE 0x20000000 -#define E1000_RXDEXT_STATERR_IPE 0x40000000 -#define E1000_RXDEXT_STATERR_RXE 0x80000000 - -#define E1000_RXDPS_HDRSTAT_HDRSP 0x00008000 -#define E1000_RXDPS_HDRSTAT_HDRLEN_MASK 0x000003FF - -/* Receive Address */ -#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ - -/* Offload Context Descriptor */ -struct e1000_context_desc { - union { - uint32_t ip_config; - struct { - uint8_t ipcss; /* IP checksum start */ - uint8_t ipcso; /* IP checksum offset */ - uint16_t ipcse; /* IP checksum end */ - } ip_fields; - } lower_setup; - union { - uint32_t tcp_config; - struct { - uint8_t tucss; /* TCP checksum start */ - uint8_t tucso; /* TCP checksum offset */ - uint16_t tucse; /* TCP checksum end */ - } tcp_fields; - } upper_setup; - uint32_t cmd_and_length; /* */ - union { - uint32_t data; - struct { - uint8_t status; /* Descriptor status */ - uint8_t hdr_len; /* Header length */ - uint16_t mss; /* Maximum segment size */ - } fields; - } tcp_seg_setup; -}; - -/* Offload data descriptor */ -struct e1000_data_desc { - uint64_t buffer_addr; /* Address of the descriptor's buffer address */ - union { - uint32_t data; - struct { - uint16_t length; /* Data buffer length */ - uint8_t typ_len_ext; /* */ - uint8_t cmd; /* */ - } flags; - } lower; - union { - uint32_t data; - struct { - uint8_t status; /* Descriptor status */ - uint8_t popts; /* Packet Options */ - uint16_t special; /* */ - } fields; - } upper; -}; - -/* Management Control */ -#define E1000_MANC_SMBUS_EN 0x00000001 /* SMBus Enabled - RO */ -#define E1000_MANC_ASF_EN 0x00000002 /* ASF Enabled - RO */ -#define E1000_MANC_R_ON_FORCE 0x00000004 /* Reset on Force TCO - RO */ -#define E1000_MANC_RMCP_EN 0x00000100 /* Enable RCMP 026Fh Filtering */ -#define E1000_MANC_0298_EN 0x00000200 /* Enable RCMP 0298h Filtering */ -#define E1000_MANC_IPV4_EN 0x00000400 /* Enable IPv4 */ -#define E1000_MANC_IPV6_EN 0x00000800 /* Enable IPv6 */ -#define E1000_MANC_SNAP_EN 0x00001000 /* Accept LLC/SNAP */ -#define E1000_MANC_ARP_EN 0x00002000 /* Enable ARP Request Filtering */ -#define E1000_MANC_NEIGHBOR_EN 0x00004000 /* Enable Neighbor Discovery - * Filtering */ -#define E1000_MANC_ARP_RES_EN 0x00008000 /* Enable ARP response Filtering */ -#define E1000_MANC_TCO_RESET 0x00010000 /* TCO Reset Occurred */ -#define E1000_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */ -#define E1000_MANC_REPORT_STATUS 0x00040000 /* Status Reporting Enabled */ -#define E1000_MANC_RCV_ALL 0x00080000 /* Receive All Enabled */ -#define E1000_MANC_BLK_PHY_RST_ON_IDE 0x00040000 /* Block phy resets */ -#define E1000_MANC_EN_MAC_ADDR_FILTER 0x00100000 /* Enable MAC address - * filtering */ -#define E1000_MANC_EN_MNG2HOST 0x00200000 /* Enable MNG packets to host - * memory */ -#define E1000_MANC_EN_IP_ADDR_FILTER 0x00400000 /* Enable IP address - * filtering */ -#define E1000_MANC_EN_XSUM_FILTER 0x00800000 /* Enable checksum filtering */ -#define E1000_MANC_BR_EN 0x01000000 /* Enable broadcast filtering */ -#define E1000_MANC_SMB_REQ 0x01000000 /* SMBus Request */ -#define E1000_MANC_SMB_GNT 0x02000000 /* SMBus Grant */ -#define E1000_MANC_SMB_CLK_IN 0x04000000 /* SMBus Clock In */ -#define E1000_MANC_SMB_DATA_IN 0x08000000 /* SMBus Data In */ -#define E1000_MANC_SMB_DATA_OUT 0x10000000 /* SMBus Data Out */ -#define E1000_MANC_SMB_CLK_OUT 0x20000000 /* SMBus Clock Out */ - -#define E1000_MANC_SMB_DATA_OUT_SHIFT 28 /* SMBus Data Out Shift */ -#define E1000_MANC_SMB_CLK_OUT_SHIFT 29 /* SMBus Clock Out Shift */ - -/* For checksumming, the sum of all words in the EEPROM should equal 0xBABA. */ -#define EEPROM_SUM 0xBABA - -#endif /* _E1000_HW_H_ */ diff --git a/qemu/hw/net/eepro100.c b/qemu/hw/net/eepro100.c deleted file mode 100644 index 9b4b9b59d..000000000 --- a/qemu/hw/net/eepro100.c +++ /dev/null @@ -1,2114 +0,0 @@ -/* - * QEMU i8255x (PRO100) emulation - * - * Copyright (C) 2006-2011 Stefan Weil - * - * Portions of the code are copies from grub / etherboot eepro100.c - * and linux e100.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) version 3 or 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 . - * - * Tested features (i82559): - * PXE boot (i386 guest, i386 / mips / mipsel / ppc host) ok - * Linux networking (i386) ok - * - * Untested: - * Windows networking - * - * References: - * - * Intel 8255x 10/100 Mbps Ethernet Controller Family - * Open Source Software Developer Manual - * - * TODO: - * * PHY emulation should be separated from nic emulation. - * Most nic emulations could share the same phy code. - * * i82550 is untested. It is programmed like the i82559. - * * i82562 is untested. It is programmed like the i82559. - * * Power management (i82558 and later) is not implemented. - * * Wake-on-LAN is not implemented. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "net/net.h" -#include "hw/nvram/eeprom93xx.h" -#include "sysemu/sysemu.h" -#include "sysemu/dma.h" -#include "qemu/bitops.h" - -/* QEMU sends frames smaller than 60 bytes to ethernet nics. - * Such frames are rejected by real nics and their emulations. - * To avoid this behaviour, other nic emulations pad received - * frames. The following definition enables this padding for - * eepro100, too. We keep the define around in case it might - * become useful the future if the core networking is ever - * changed to pad short packets itself. */ -#define CONFIG_PAD_RECEIVED_FRAMES - -#define KiB 1024 - -/* Debug EEPRO100 card. */ -#if 0 -# define DEBUG_EEPRO100 -#endif - -#ifdef DEBUG_EEPRO100 -#define logout(fmt, ...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ## __VA_ARGS__) -#else -#define logout(fmt, ...) ((void)0) -#endif - -/* Set flags to 0 to disable debug output. */ -#define INT 1 /* interrupt related actions */ -#define MDI 1 /* mdi related actions */ -#define OTHER 1 -#define RXTX 1 -#define EEPROM 1 /* eeprom related actions */ - -#define TRACE(flag, command) ((flag) ? (command) : (void)0) - -#define missing(text) fprintf(stderr, "eepro100: feature is missing in this emulation: " text "\n") - -#define MAX_ETH_FRAME_SIZE 1514 - -/* This driver supports several different devices which are declared here. */ -#define i82550 0x82550 -#define i82551 0x82551 -#define i82557A 0x82557a -#define i82557B 0x82557b -#define i82557C 0x82557c -#define i82558A 0x82558a -#define i82558B 0x82558b -#define i82559A 0x82559a -#define i82559B 0x82559b -#define i82559C 0x82559c -#define i82559ER 0x82559e -#define i82562 0x82562 -#define i82801 0x82801 - -/* Use 64 word EEPROM. TODO: could be a runtime option. */ -#define EEPROM_SIZE 64 - -#define PCI_MEM_SIZE (4 * KiB) -#define PCI_IO_SIZE 64 -#define PCI_FLASH_SIZE (128 * KiB) - -#define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m) - -/* The SCB accepts the following controls for the Tx and Rx units: */ -#define CU_NOP 0x0000 /* No operation. */ -#define CU_START 0x0010 /* CU start. */ -#define CU_RESUME 0x0020 /* CU resume. */ -#define CU_STATSADDR 0x0040 /* Load dump counters address. */ -#define CU_SHOWSTATS 0x0050 /* Dump statistical counters. */ -#define CU_CMD_BASE 0x0060 /* Load CU base address. */ -#define CU_DUMPSTATS 0x0070 /* Dump and reset statistical counters. */ -#define CU_SRESUME 0x00a0 /* CU static resume. */ - -#define RU_NOP 0x0000 -#define RX_START 0x0001 -#define RX_RESUME 0x0002 -#define RU_ABORT 0x0004 -#define RX_ADDR_LOAD 0x0006 -#define RX_RESUMENR 0x0007 -#define INT_MASK 0x0100 -#define DRVR_INT 0x0200 /* Driver generated interrupt. */ - -typedef struct { - const char *name; - const char *desc; - uint16_t device_id; - uint8_t revision; - uint16_t subsystem_vendor_id; - uint16_t subsystem_id; - - uint32_t device; - uint8_t stats_size; - bool has_extended_tcb_support; - bool power_management; -} E100PCIDeviceInfo; - -/* Offsets to the various registers. - All accesses need not be longword aligned. */ -typedef enum { - SCBStatus = 0, /* Status Word. */ - SCBAck = 1, - SCBCmd = 2, /* Rx/Command Unit command and status. */ - SCBIntmask = 3, - SCBPointer = 4, /* General purpose pointer. */ - SCBPort = 8, /* Misc. commands and operands. */ - SCBflash = 12, /* Flash memory control. */ - SCBeeprom = 14, /* EEPROM control. */ - SCBCtrlMDI = 16, /* MDI interface control. */ - SCBEarlyRx = 20, /* Early receive byte count. */ - SCBFlow = 24, /* Flow Control. */ - SCBpmdr = 27, /* Power Management Driver. */ - SCBgctrl = 28, /* General Control. */ - SCBgstat = 29, /* General Status. */ -} E100RegisterOffset; - -/* A speedo3 transmit buffer descriptor with two buffers... */ -typedef struct { - uint16_t status; - uint16_t command; - uint32_t link; /* void * */ - uint32_t tbd_array_addr; /* transmit buffer descriptor array address. */ - uint16_t tcb_bytes; /* transmit command block byte count (in lower 14 bits */ - uint8_t tx_threshold; /* transmit threshold */ - uint8_t tbd_count; /* TBD number */ -#if 0 - /* This constitutes two "TBD" entries: hdr and data */ - uint32_t tx_buf_addr0; /* void *, header of frame to be transmitted. */ - int32_t tx_buf_size0; /* Length of Tx hdr. */ - uint32_t tx_buf_addr1; /* void *, data to be transmitted. */ - int32_t tx_buf_size1; /* Length of Tx data. */ -#endif -} eepro100_tx_t; - -/* Receive frame descriptor. */ -typedef struct { - int16_t status; - uint16_t command; - uint32_t link; /* struct RxFD * */ - uint32_t rx_buf_addr; /* void * */ - uint16_t count; - uint16_t size; - /* Ethernet frame data follows. */ -} eepro100_rx_t; - -typedef enum { - COMMAND_EL = BIT(15), - COMMAND_S = BIT(14), - COMMAND_I = BIT(13), - COMMAND_NC = BIT(4), - COMMAND_SF = BIT(3), - COMMAND_CMD = BITS(2, 0), -} scb_command_bit; - -typedef enum { - STATUS_C = BIT(15), - STATUS_OK = BIT(13), -} scb_status_bit; - -typedef struct { - uint32_t tx_good_frames, tx_max_collisions, tx_late_collisions, - tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions, - tx_multiple_collisions, tx_total_collisions; - uint32_t rx_good_frames, rx_crc_errors, rx_alignment_errors, - rx_resource_errors, rx_overrun_errors, rx_cdt_errors, - rx_short_frame_errors; - uint32_t fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported; - uint16_t xmt_tco_frames, rcv_tco_frames; - /* TODO: i82559 has six reserved statistics but a total of 24 dwords. */ - uint32_t reserved[4]; -} eepro100_stats_t; - -typedef enum { - cu_idle = 0, - cu_suspended = 1, - cu_active = 2, - cu_lpq_active = 2, - cu_hqp_active = 3 -} cu_state_t; - -typedef enum { - ru_idle = 0, - ru_suspended = 1, - ru_no_resources = 2, - ru_ready = 4 -} ru_state_t; - -typedef struct { - PCIDevice dev; - /* Hash register (multicast mask array, multiple individual addresses). */ - uint8_t mult[8]; - MemoryRegion mmio_bar; - MemoryRegion io_bar; - MemoryRegion flash_bar; - NICState *nic; - NICConf conf; - uint8_t scb_stat; /* SCB stat/ack byte */ - uint8_t int_stat; /* PCI interrupt status */ - /* region must not be saved by nic_save. */ - uint16_t mdimem[32]; - eeprom_t *eeprom; - uint32_t device; /* device variant */ - /* (cu_base + cu_offset) address the next command block in the command block list. */ - uint32_t cu_base; /* CU base address */ - uint32_t cu_offset; /* CU address offset */ - /* (ru_base + ru_offset) address the RFD in the Receive Frame Area. */ - uint32_t ru_base; /* RU base address */ - uint32_t ru_offset; /* RU address offset */ - uint32_t statsaddr; /* pointer to eepro100_stats_t */ - - /* Temporary status information (no need to save these values), - * used while processing CU commands. */ - eepro100_tx_t tx; /* transmit buffer descriptor */ - uint32_t cb_address; /* = cu_base + cu_offset */ - - /* Statistical counters. Also used for wake-up packet (i82559). */ - eepro100_stats_t statistics; - - /* Data in mem is always in the byte order of the controller (le). - * It must be dword aligned to allow direct access to 32 bit values. */ - uint8_t mem[PCI_MEM_SIZE] __attribute__((aligned(8))); - - /* Configuration bytes. */ - uint8_t configuration[22]; - - /* vmstate for each particular nic */ - VMStateDescription *vmstate; - - /* Quasi static device properties (no need to save them). */ - uint16_t stats_size; - bool has_extended_tcb_support; -} EEPRO100State; - -/* Word indices in EEPROM. */ -typedef enum { - EEPROM_CNFG_MDIX = 0x03, - EEPROM_ID = 0x05, - EEPROM_PHY_ID = 0x06, - EEPROM_VENDOR_ID = 0x0c, - EEPROM_CONFIG_ASF = 0x0d, - EEPROM_DEVICE_ID = 0x23, - EEPROM_SMBUS_ADDR = 0x90, -} EEPROMOffset; - -/* Bit values for EEPROM ID word. */ -typedef enum { - EEPROM_ID_MDM = BIT(0), /* Modem */ - EEPROM_ID_STB = BIT(1), /* Standby Enable */ - EEPROM_ID_WMR = BIT(2), /* ??? */ - EEPROM_ID_WOL = BIT(5), /* Wake on LAN */ - EEPROM_ID_DPD = BIT(6), /* Deep Power Down */ - EEPROM_ID_ALT = BIT(7), /* */ - /* BITS(10, 8) device revision */ - EEPROM_ID_BD = BIT(11), /* boot disable */ - EEPROM_ID_ID = BIT(13), /* id bit */ - /* BITS(15, 14) signature */ - EEPROM_ID_VALID = BIT(14), /* signature for valid eeprom */ -} eeprom_id_bit; - -/* Default values for MDI (PHY) registers */ -static const uint16_t eepro100_mdi_default[] = { - /* MDI Registers 0 - 6, 7 */ - 0x3000, 0x780d, 0x02a8, 0x0154, 0x05e1, 0x0000, 0x0000, 0x0000, - /* MDI Registers 8 - 15 */ - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - /* MDI Registers 16 - 31 */ - 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -/* Readonly mask for MDI (PHY) registers */ -static const uint16_t eepro100_mdi_mask[] = { - 0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, - 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -#define POLYNOMIAL 0x04c11db6 - -static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s); - -/* From FreeBSD (locally modified). */ -static unsigned e100_compute_mcast_idx(const uint8_t *ep) -{ - uint32_t crc; - int carry, i, j; - uint8_t b; - - crc = 0xffffffff; - for (i = 0; i < 6; i++) { - b = *ep++; - for (j = 0; j < 8; j++) { - carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01); - crc <<= 1; - b >>= 1; - if (carry) { - crc = ((crc ^ POLYNOMIAL) | carry); - } - } - } - return (crc & BITS(7, 2)) >> 2; -} - -/* Read a 16 bit control/status (CSR) register. */ -static uint16_t e100_read_reg2(EEPRO100State *s, E100RegisterOffset addr) -{ - assert(!((uintptr_t)&s->mem[addr] & 1)); - return le16_to_cpup((uint16_t *)&s->mem[addr]); -} - -/* Read a 32 bit control/status (CSR) register. */ -static uint32_t e100_read_reg4(EEPRO100State *s, E100RegisterOffset addr) -{ - assert(!((uintptr_t)&s->mem[addr] & 3)); - return le32_to_cpup((uint32_t *)&s->mem[addr]); -} - -/* Write a 16 bit control/status (CSR) register. */ -static void e100_write_reg2(EEPRO100State *s, E100RegisterOffset addr, - uint16_t val) -{ - assert(!((uintptr_t)&s->mem[addr] & 1)); - cpu_to_le16w((uint16_t *)&s->mem[addr], val); -} - -/* Read a 32 bit control/status (CSR) register. */ -static void e100_write_reg4(EEPRO100State *s, E100RegisterOffset addr, - uint32_t val) -{ - assert(!((uintptr_t)&s->mem[addr] & 3)); - cpu_to_le32w((uint32_t *)&s->mem[addr], val); -} - -#if defined(DEBUG_EEPRO100) -static const char *nic_dump(const uint8_t * buf, unsigned size) -{ - static char dump[3 * 16 + 1]; - char *p = &dump[0]; - if (size > 16) { - size = 16; - } - while (size-- > 0) { - p += sprintf(p, " %02x", *buf++); - } - return dump; -} -#endif /* DEBUG_EEPRO100 */ - -enum scb_stat_ack { - stat_ack_not_ours = 0x00, - stat_ack_sw_gen = 0x04, - stat_ack_rnr = 0x10, - stat_ack_cu_idle = 0x20, - stat_ack_frame_rx = 0x40, - stat_ack_cu_cmd_done = 0x80, - stat_ack_not_present = 0xFF, - stat_ack_rx = (stat_ack_sw_gen | stat_ack_rnr | stat_ack_frame_rx), - stat_ack_tx = (stat_ack_cu_idle | stat_ack_cu_cmd_done), -}; - -static void disable_interrupt(EEPRO100State * s) -{ - if (s->int_stat) { - TRACE(INT, logout("interrupt disabled\n")); - pci_irq_deassert(&s->dev); - s->int_stat = 0; - } -} - -static void enable_interrupt(EEPRO100State * s) -{ - if (!s->int_stat) { - TRACE(INT, logout("interrupt enabled\n")); - pci_irq_assert(&s->dev); - s->int_stat = 1; - } -} - -static void eepro100_acknowledge(EEPRO100State * s) -{ - s->scb_stat &= ~s->mem[SCBAck]; - s->mem[SCBAck] = s->scb_stat; - if (s->scb_stat == 0) { - disable_interrupt(s); - } -} - -static void eepro100_interrupt(EEPRO100State * s, uint8_t status) -{ - uint8_t mask = ~s->mem[SCBIntmask]; - s->mem[SCBAck] |= status; - status = s->scb_stat = s->mem[SCBAck]; - status &= (mask | 0x0f); -#if 0 - status &= (~s->mem[SCBIntmask] | 0x0xf); -#endif - if (status && (mask & 0x01)) { - /* SCB mask and SCB Bit M do not disable interrupt. */ - enable_interrupt(s); - } else if (s->int_stat) { - disable_interrupt(s); - } -} - -static void eepro100_cx_interrupt(EEPRO100State * s) -{ - /* CU completed action command. */ - /* Transmit not ok (82557 only, not in emulation). */ - eepro100_interrupt(s, 0x80); -} - -static void eepro100_cna_interrupt(EEPRO100State * s) -{ - /* CU left the active state. */ - eepro100_interrupt(s, 0x20); -} - -static void eepro100_fr_interrupt(EEPRO100State * s) -{ - /* RU received a complete frame. */ - eepro100_interrupt(s, 0x40); -} - -static void eepro100_rnr_interrupt(EEPRO100State * s) -{ - /* RU is not ready. */ - eepro100_interrupt(s, 0x10); -} - -static void eepro100_mdi_interrupt(EEPRO100State * s) -{ - /* MDI completed read or write cycle. */ - eepro100_interrupt(s, 0x08); -} - -static void eepro100_swi_interrupt(EEPRO100State * s) -{ - /* Software has requested an interrupt. */ - eepro100_interrupt(s, 0x04); -} - -#if 0 -static void eepro100_fcp_interrupt(EEPRO100State * s) -{ - /* Flow control pause interrupt (82558 and later). */ - eepro100_interrupt(s, 0x01); -} -#endif - -static void e100_pci_reset(EEPRO100State * s) -{ - E100PCIDeviceInfo *info = eepro100_get_class(s); - uint32_t device = s->device; - uint8_t *pci_conf = s->dev.config; - - TRACE(OTHER, logout("%p\n", s)); - - /* PCI Status */ - pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM | - PCI_STATUS_FAST_BACK); - /* PCI Latency Timer */ - pci_set_byte(pci_conf + PCI_LATENCY_TIMER, 0x20); /* latency timer = 32 clocks */ - /* Capability Pointer is set by PCI framework. */ - /* Interrupt Line */ - /* Interrupt Pin */ - pci_set_byte(pci_conf + PCI_INTERRUPT_PIN, 1); /* interrupt pin A */ - /* Minimum Grant */ - pci_set_byte(pci_conf + PCI_MIN_GNT, 0x08); - /* Maximum Latency */ - pci_set_byte(pci_conf + PCI_MAX_LAT, 0x18); - - s->stats_size = info->stats_size; - s->has_extended_tcb_support = info->has_extended_tcb_support; - - switch (device) { - case i82550: - case i82551: - case i82557A: - case i82557B: - case i82557C: - case i82558A: - case i82558B: - case i82559A: - case i82559B: - case i82559ER: - case i82562: - case i82801: - case i82559C: - break; - default: - logout("Device %X is undefined!\n", device); - } - - /* Standard TxCB. */ - s->configuration[6] |= BIT(4); - - /* Standard statistical counters. */ - s->configuration[6] |= BIT(5); - - if (s->stats_size == 80) { - /* TODO: check TCO Statistical Counters bit. Documentation not clear. */ - if (s->configuration[6] & BIT(2)) { - /* TCO statistical counters. */ - assert(s->configuration[6] & BIT(5)); - } else { - if (s->configuration[6] & BIT(5)) { - /* No extended statistical counters, i82557 compatible. */ - s->stats_size = 64; - } else { - /* i82558 compatible. */ - s->stats_size = 76; - } - } - } else { - if (s->configuration[6] & BIT(5)) { - /* No extended statistical counters. */ - s->stats_size = 64; - } - } - assert(s->stats_size > 0 && s->stats_size <= sizeof(s->statistics)); - - if (info->power_management) { - /* Power Management Capabilities */ - int cfg_offset = 0xdc; - int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM, - cfg_offset, PCI_PM_SIZEOF); - assert(r >= 0); - pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21); -#if 0 /* TODO: replace dummy code for power management emulation. */ - /* TODO: Power Management Control / Status. */ - pci_set_word(pci_conf + cfg_offset + PCI_PM_CTRL, 0x0000); - /* TODO: Ethernet Power Consumption Registers (i82559 and later). */ - pci_set_byte(pci_conf + cfg_offset + PCI_PM_PPB_EXTENSIONS, 0x0000); -#endif - } - -#if EEPROM_SIZE > 0 - if (device == i82557C || device == i82558B || device == i82559C) { - /* - TODO: get vendor id from EEPROM for i82557C or later. - TODO: get device id from EEPROM for i82557C or later. - TODO: status bit 4 can be disabled by EEPROM for i82558, i82559. - TODO: header type is determined by EEPROM for i82559. - TODO: get subsystem id from EEPROM for i82557C or later. - TODO: get subsystem vendor id from EEPROM for i82557C or later. - TODO: exp. rom baddr depends on a bit in EEPROM for i82558 or later. - TODO: capability pointer depends on EEPROM for i82558. - */ - logout("Get device id and revision from EEPROM!!!\n"); - } -#endif /* EEPROM_SIZE > 0 */ -} - -static void nic_selective_reset(EEPRO100State * s) -{ - size_t i; - uint16_t *eeprom_contents = eeprom93xx_data(s->eeprom); -#if 0 - eeprom93xx_reset(s->eeprom); -#endif - memcpy(eeprom_contents, s->conf.macaddr.a, 6); - eeprom_contents[EEPROM_ID] = EEPROM_ID_VALID; - if (s->device == i82557B || s->device == i82557C) - eeprom_contents[5] = 0x0100; - eeprom_contents[EEPROM_PHY_ID] = 1; - uint16_t sum = 0; - for (i = 0; i < EEPROM_SIZE - 1; i++) { - sum += eeprom_contents[i]; - } - eeprom_contents[EEPROM_SIZE - 1] = 0xbaba - sum; - TRACE(EEPROM, logout("checksum=0x%04x\n", eeprom_contents[EEPROM_SIZE - 1])); - - memset(s->mem, 0, sizeof(s->mem)); - e100_write_reg4(s, SCBCtrlMDI, BIT(21)); - - assert(sizeof(s->mdimem) == sizeof(eepro100_mdi_default)); - memcpy(&s->mdimem[0], &eepro100_mdi_default[0], sizeof(s->mdimem)); -} - -static void nic_reset(void *opaque) -{ - EEPRO100State *s = opaque; - TRACE(OTHER, logout("%p\n", s)); - /* TODO: Clearing of hash register for selective reset, too? */ - memset(&s->mult[0], 0, sizeof(s->mult)); - nic_selective_reset(s); -} - -#if defined(DEBUG_EEPRO100) -static const char * const e100_reg[PCI_IO_SIZE / 4] = { - "Command/Status", - "General Pointer", - "Port", - "EEPROM/Flash Control", - "MDI Control", - "Receive DMA Byte Count", - "Flow Control", - "General Status/Control" -}; - -static char *regname(uint32_t addr) -{ - static char buf[32]; - if (addr < PCI_IO_SIZE) { - const char *r = e100_reg[addr / 4]; - if (r != 0) { - snprintf(buf, sizeof(buf), "%s+%u", r, addr % 4); - } else { - snprintf(buf, sizeof(buf), "0x%02x", addr); - } - } else { - snprintf(buf, sizeof(buf), "??? 0x%08x", addr); - } - return buf; -} -#endif /* DEBUG_EEPRO100 */ - -/***************************************************************************** - * - * Command emulation. - * - ****************************************************************************/ - -#if 0 -static uint16_t eepro100_read_command(EEPRO100State * s) -{ - uint16_t val = 0xffff; - TRACE(OTHER, logout("val=0x%04x\n", val)); - return val; -} -#endif - -/* Commands that can be put in a command list entry. */ -enum commands { - CmdNOp = 0, - CmdIASetup = 1, - CmdConfigure = 2, - CmdMulticastList = 3, - CmdTx = 4, - CmdTDR = 5, /* load microcode */ - CmdDump = 6, - CmdDiagnose = 7, - - /* And some extra flags: */ - CmdSuspend = 0x4000, /* Suspend after completion. */ - CmdIntr = 0x2000, /* Interrupt after completion. */ - CmdTxFlex = 0x0008, /* Use "Flexible mode" for CmdTx command. */ -}; - -static cu_state_t get_cu_state(EEPRO100State * s) -{ - return ((s->mem[SCBStatus] & BITS(7, 6)) >> 6); -} - -static void set_cu_state(EEPRO100State * s, cu_state_t state) -{ - s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(7, 6)) + (state << 6); -} - -static ru_state_t get_ru_state(EEPRO100State * s) -{ - return ((s->mem[SCBStatus] & BITS(5, 2)) >> 2); -} - -static void set_ru_state(EEPRO100State * s, ru_state_t state) -{ - s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(5, 2)) + (state << 2); -} - -static void dump_statistics(EEPRO100State * s) -{ - /* Dump statistical data. Most data is never changed by the emulation - * and always 0, so we first just copy the whole block and then those - * values which really matter. - * Number of data should check configuration!!! - */ - pci_dma_write(&s->dev, s->statsaddr, &s->statistics, s->stats_size); - stl_le_pci_dma(&s->dev, s->statsaddr + 0, - s->statistics.tx_good_frames); - stl_le_pci_dma(&s->dev, s->statsaddr + 36, - s->statistics.rx_good_frames); - stl_le_pci_dma(&s->dev, s->statsaddr + 48, - s->statistics.rx_resource_errors); - stl_le_pci_dma(&s->dev, s->statsaddr + 60, - s->statistics.rx_short_frame_errors); -#if 0 - stw_le_pci_dma(&s->dev, s->statsaddr + 76, s->statistics.xmt_tco_frames); - stw_le_pci_dma(&s->dev, s->statsaddr + 78, s->statistics.rcv_tco_frames); - missing("CU dump statistical counters"); -#endif -} - -static void read_cb(EEPRO100State *s) -{ - pci_dma_read(&s->dev, s->cb_address, &s->tx, sizeof(s->tx)); - s->tx.status = le16_to_cpu(s->tx.status); - s->tx.command = le16_to_cpu(s->tx.command); - s->tx.link = le32_to_cpu(s->tx.link); - s->tx.tbd_array_addr = le32_to_cpu(s->tx.tbd_array_addr); - s->tx.tcb_bytes = le16_to_cpu(s->tx.tcb_bytes); -} - -static void tx_command(EEPRO100State *s) -{ - uint32_t tbd_array = le32_to_cpu(s->tx.tbd_array_addr); - uint16_t tcb_bytes = (le16_to_cpu(s->tx.tcb_bytes) & 0x3fff); - /* Sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes. */ - uint8_t buf[2600]; - uint16_t size = 0; - uint32_t tbd_address = s->cb_address + 0x10; - TRACE(RXTX, logout - ("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n", - tbd_array, tcb_bytes, s->tx.tbd_count)); - - if (tcb_bytes > 2600) { - logout("TCB byte count too large, using 2600\n"); - tcb_bytes = 2600; - } - if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) { - logout - ("illegal values of TBD array address and TCB byte count!\n"); - } - assert(tcb_bytes <= sizeof(buf)); - while (size < tcb_bytes) { - uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address); - uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4); -#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", - tx_buffer_address, tx_buffer_size)); - tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); - pci_dma_read(&s->dev, tx_buffer_address, &buf[size], tx_buffer_size); - size += tx_buffer_size; - } - if (tbd_array == 0xffffffff) { - /* Simplified mode. Was already handled by code above. */ - } else { - /* Flexible mode. */ - uint8_t tbd_count = 0; - if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) { - /* Extended Flexible TCB. */ - for (; tbd_count < 2; tbd_count++) { - uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, - tbd_address); - uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, - tbd_address + 4); - uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, - tbd_address + 6); - tbd_address += 8; - TRACE(RXTX, logout - ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n", - tx_buffer_address, tx_buffer_size)); - tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); - pci_dma_read(&s->dev, tx_buffer_address, - &buf[size], tx_buffer_size); - size += tx_buffer_size; - if (tx_buffer_el & 1) { - break; - } - } - } - tbd_address = tbd_array; - for (; tbd_count < s->tx.tbd_count; tbd_count++) { - uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address); - uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4); - uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6); - tbd_address += 8; - TRACE(RXTX, logout - ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n", - tx_buffer_address, tx_buffer_size)); - tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); - pci_dma_read(&s->dev, tx_buffer_address, - &buf[size], tx_buffer_size); - size += tx_buffer_size; - if (tx_buffer_el & 1) { - break; - } - } - } - TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size))); - qemu_send_packet(qemu_get_queue(s->nic), buf, size); - s->statistics.tx_good_frames++; - /* Transmit with bad status would raise an CX/TNO interrupt. - * (82557 only). Emulation never has bad status. */ -#if 0 - eepro100_cx_interrupt(s); -#endif -} - -static void set_multicast_list(EEPRO100State *s) -{ - uint16_t multicast_count = s->tx.tbd_array_addr & BITS(13, 0); - uint16_t i; - memset(&s->mult[0], 0, sizeof(s->mult)); - TRACE(OTHER, logout("multicast list, multicast count = %u\n", multicast_count)); - for (i = 0; i < multicast_count; i += 6) { - uint8_t multicast_addr[6]; - pci_dma_read(&s->dev, s->cb_address + 10 + i, multicast_addr, 6); - TRACE(OTHER, logout("multicast entry %s\n", nic_dump(multicast_addr, 6))); - unsigned mcast_idx = e100_compute_mcast_idx(multicast_addr); - assert(mcast_idx < 64); - s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7)); - } -} - -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; - bool bit_i; - bool bit_nc; - uint16_t ok_status = STATUS_OK; - s->cb_address = s->cu_base + s->cu_offset; - read_cb(s); - bit_el = ((s->tx.command & COMMAND_EL) != 0); - bit_s = ((s->tx.command & COMMAND_S) != 0); - bit_i = ((s->tx.command & COMMAND_I) != 0); - bit_nc = ((s->tx.command & COMMAND_NC) != 0); -#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", - s->tx.status, s->tx.command, s->tx.link)); - switch (s->tx.command & COMMAND_CMD) { - case CmdNOp: - /* Do nothing. */ - break; - case CmdIASetup: - pci_dma_read(&s->dev, s->cb_address + 8, &s->conf.macaddr.a[0], 6); - TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6))); - break; - case CmdConfigure: - pci_dma_read(&s->dev, s->cb_address + 8, - &s->configuration[0], sizeof(s->configuration)); - TRACE(OTHER, logout("configuration: %s\n", - nic_dump(&s->configuration[0], 16))); - TRACE(OTHER, logout("configuration: %s\n", - nic_dump(&s->configuration[16], - ARRAY_SIZE(s->configuration) - 16))); - if (s->configuration[20] & BIT(6)) { - TRACE(OTHER, logout("Multiple IA bit\n")); - } - break; - case CmdMulticastList: - set_multicast_list(s); - break; - case CmdTx: - if (bit_nc) { - missing("CmdTx: NC = 0"); - ok_status = 0; - break; - } - tx_command(s); - break; - case CmdTDR: - TRACE(OTHER, logout("load microcode\n")); - /* Starting with offset 8, the command contains - * 64 dwords microcode which we just ignore here. */ - break; - case CmdDiagnose: - TRACE(OTHER, logout("diagnose\n")); - /* Make sure error flag is not set. */ - s->tx.status = 0; - break; - default: - missing("undefined command"); - ok_status = 0; - break; - } - /* Write new status. */ - stw_le_pci_dma(&s->dev, s->cb_address, - s->tx.status | ok_status | STATUS_C); - if (bit_i) { - /* CU completed action. */ - eepro100_cx_interrupt(s); - } - if (bit_el) { - /* CU becomes idle. Terminate command loop. */ - set_cu_state(s, cu_idle); - eepro100_cna_interrupt(s); - break; - } else if (bit_s) { - /* CU becomes suspended. Terminate command loop. */ - set_cu_state(s, cu_suspended); - eepro100_cna_interrupt(s); - break; - } else { - /* More entries in list. */ - TRACE(OTHER, logout("CU list with at least one more entry\n")); - } - } - TRACE(OTHER, logout("CU list empty\n")); - /* List is empty. Now CU is idle or suspended. */ -} - -static void eepro100_cu_command(EEPRO100State * s, uint8_t val) -{ - cu_state_t cu_state; - switch (val) { - case CU_NOP: - /* No operation. */ - break; - case CU_START: - cu_state = get_cu_state(s); - if (cu_state != cu_idle && cu_state != cu_suspended) { - /* Intel documentation says that CU must be idle or suspended - * for the CU start command. */ - logout("unexpected CU state is %u\n", cu_state); - } - set_cu_state(s, cu_active); - s->cu_offset = e100_read_reg4(s, SCBPointer); - action_command(s); - break; - case CU_RESUME: - if (get_cu_state(s) != cu_suspended) { - logout("bad CU resume from CU state %u\n", get_cu_state(s)); - /* Workaround for bad Linux eepro100 driver which resumes - * from idle state. */ -#if 0 - missing("cu resume"); -#endif - set_cu_state(s, cu_suspended); - } - if (get_cu_state(s) == cu_suspended) { - TRACE(OTHER, logout("CU resuming\n")); - set_cu_state(s, cu_active); - action_command(s); - } - break; - case CU_STATSADDR: - /* Load dump counters address. */ - s->statsaddr = e100_read_reg4(s, SCBPointer); - TRACE(OTHER, logout("val=0x%02x (dump counters address)\n", val)); - if (s->statsaddr & 3) { - /* Memory must be Dword aligned. */ - logout("unaligned dump counters address\n"); - /* Handling of misaligned addresses is undefined. - * Here we align the address by ignoring the lower bits. */ - /* TODO: Test unaligned dump counter address on real hardware. */ - s->statsaddr &= ~3; - } - break; - case CU_SHOWSTATS: - /* Dump statistical counters. */ - TRACE(OTHER, logout("val=0x%02x (dump stats)\n", val)); - dump_statistics(s); - stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa005); - break; - case CU_CMD_BASE: - /* Load CU base. */ - TRACE(OTHER, logout("val=0x%02x (CU base address)\n", val)); - s->cu_base = e100_read_reg4(s, SCBPointer); - break; - case CU_DUMPSTATS: - /* Dump and reset statistical counters. */ - TRACE(OTHER, logout("val=0x%02x (dump stats and reset)\n", val)); - dump_statistics(s); - stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa007); - memset(&s->statistics, 0, sizeof(s->statistics)); - break; - case CU_SRESUME: - /* CU static resume. */ - missing("CU static resume"); - break; - default: - missing("Undefined CU command"); - } -} - -static void eepro100_ru_command(EEPRO100State * s, uint8_t val) -{ - switch (val) { - case RU_NOP: - /* No operation. */ - break; - case RX_START: - /* RU start. */ - if (get_ru_state(s) != ru_idle) { - logout("RU state is %u, should be %u\n", get_ru_state(s), ru_idle); -#if 0 - assert(!"wrong RU state"); -#endif - } - set_ru_state(s, ru_ready); - s->ru_offset = e100_read_reg4(s, SCBPointer); - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - TRACE(OTHER, logout("val=0x%02x (rx start)\n", val)); - break; - case RX_RESUME: - /* Restart RU. */ - if (get_ru_state(s) != ru_suspended) { - logout("RU state is %u, should be %u\n", get_ru_state(s), - ru_suspended); -#if 0 - assert(!"wrong RU state"); -#endif - } - set_ru_state(s, ru_ready); - break; - case RU_ABORT: - /* RU abort. */ - if (get_ru_state(s) == ru_ready) { - eepro100_rnr_interrupt(s); - } - set_ru_state(s, ru_idle); - break; - case RX_ADDR_LOAD: - /* Load RU base. */ - TRACE(OTHER, logout("val=0x%02x (RU base address)\n", val)); - s->ru_base = e100_read_reg4(s, SCBPointer); - break; - default: - logout("val=0x%02x (undefined RU command)\n", val); - missing("Undefined SU command"); - } -} - -static void eepro100_write_command(EEPRO100State * s, uint8_t val) -{ - eepro100_ru_command(s, val & 0x0f); - eepro100_cu_command(s, val & 0xf0); - if ((val) == 0) { - TRACE(OTHER, logout("val=0x%02x\n", val)); - } - /* Clear command byte after command was accepted. */ - s->mem[SCBCmd] = 0; -} - -/***************************************************************************** - * - * EEPROM emulation. - * - ****************************************************************************/ - -#define EEPROM_CS 0x02 -#define EEPROM_SK 0x01 -#define EEPROM_DI 0x04 -#define EEPROM_DO 0x08 - -static uint16_t eepro100_read_eeprom(EEPRO100State * s) -{ - uint16_t val = e100_read_reg2(s, SCBeeprom); - if (eeprom93xx_read(s->eeprom)) { - val |= EEPROM_DO; - } else { - val &= ~EEPROM_DO; - } - TRACE(EEPROM, logout("val=0x%04x\n", val)); - return val; -} - -static void eepro100_write_eeprom(eeprom_t * eeprom, uint8_t val) -{ - TRACE(EEPROM, logout("val=0x%02x\n", val)); - - /* mask unwritable bits */ -#if 0 - val = SET_MASKED(val, 0x31, eeprom->value); -#endif - - int eecs = ((val & EEPROM_CS) != 0); - int eesk = ((val & EEPROM_SK) != 0); - int eedi = ((val & EEPROM_DI) != 0); - eeprom93xx_write(eeprom, eecs, eesk, eedi); -} - -/***************************************************************************** - * - * MDI emulation. - * - ****************************************************************************/ - -#if defined(DEBUG_EEPRO100) -static const char * const mdi_op_name[] = { - "opcode 0", - "write", - "read", - "opcode 3" -}; - -static const char * const mdi_reg_name[] = { - "Control", - "Status", - "PHY Identification (Word 1)", - "PHY Identification (Word 2)", - "Auto-Negotiation Advertisement", - "Auto-Negotiation Link Partner Ability", - "Auto-Negotiation Expansion" -}; - -static const char *reg2name(uint8_t reg) -{ - static char buffer[10]; - const char *p = buffer; - if (reg < ARRAY_SIZE(mdi_reg_name)) { - p = mdi_reg_name[reg]; - } else { - snprintf(buffer, sizeof(buffer), "reg=0x%02x", reg); - } - return p; -} -#endif /* DEBUG_EEPRO100 */ - -static uint32_t eepro100_read_mdi(EEPRO100State * s) -{ - uint32_t val = e100_read_reg4(s, SCBCtrlMDI); - -#ifdef DEBUG_EEPRO100 - uint8_t raiseint = (val & BIT(29)) >> 29; - uint8_t opcode = (val & BITS(27, 26)) >> 26; - uint8_t phy = (val & BITS(25, 21)) >> 21; - uint8_t reg = (val & BITS(20, 16)) >> 16; - uint16_t data = (val & BITS(15, 0)); -#endif - /* Emulation takes no time to finish MDI transaction. */ - val |= BIT(28); - TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n", - val, raiseint, mdi_op_name[opcode], phy, - reg2name(reg), data)); - return val; -} - -static void eepro100_write_mdi(EEPRO100State *s) -{ - uint32_t val = e100_read_reg4(s, SCBCtrlMDI); - uint8_t raiseint = (val & BIT(29)) >> 29; - uint8_t opcode = (val & BITS(27, 26)) >> 26; - uint8_t phy = (val & BITS(25, 21)) >> 21; - uint8_t reg = (val & BITS(20, 16)) >> 16; - uint16_t data = (val & BITS(15, 0)); - TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n", - val, raiseint, mdi_op_name[opcode], phy, reg2name(reg), data)); - if (phy != 1) { - /* Unsupported PHY address. */ -#if 0 - logout("phy must be 1 but is %u\n", phy); -#endif - data = 0; - } else if (opcode != 1 && opcode != 2) { - /* Unsupported opcode. */ - logout("opcode must be 1 or 2 but is %u\n", opcode); - data = 0; - } else if (reg > 6) { - /* Unsupported register. */ - logout("register must be 0...6 but is %u\n", reg); - data = 0; - } else { - TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n", - val, raiseint, mdi_op_name[opcode], phy, - reg2name(reg), data)); - if (opcode == 1) { - /* MDI write */ - switch (reg) { - case 0: /* Control Register */ - if (data & 0x8000) { - /* Reset status and control registers to default. */ - s->mdimem[0] = eepro100_mdi_default[0]; - s->mdimem[1] = eepro100_mdi_default[1]; - data = s->mdimem[reg]; - } else { - /* Restart Auto Configuration = Normal Operation */ - data &= ~0x0200; - } - break; - case 1: /* Status Register */ - missing("not writable"); - break; - case 2: /* PHY Identification Register (Word 1) */ - case 3: /* PHY Identification Register (Word 2) */ - missing("not implemented"); - break; - case 4: /* Auto-Negotiation Advertisement Register */ - case 5: /* Auto-Negotiation Link Partner Ability Register */ - break; - case 6: /* Auto-Negotiation Expansion Register */ - default: - missing("not implemented"); - } - s->mdimem[reg] &= eepro100_mdi_mask[reg]; - s->mdimem[reg] |= data & ~eepro100_mdi_mask[reg]; - } else if (opcode == 2) { - /* MDI read */ - switch (reg) { - case 0: /* Control Register */ - if (data & 0x8000) { - /* Reset status and control registers to default. */ - s->mdimem[0] = eepro100_mdi_default[0]; - s->mdimem[1] = eepro100_mdi_default[1]; - } - break; - case 1: /* Status Register */ - s->mdimem[reg] |= 0x0020; - break; - case 2: /* PHY Identification Register (Word 1) */ - case 3: /* PHY Identification Register (Word 2) */ - case 4: /* Auto-Negotiation Advertisement Register */ - break; - case 5: /* Auto-Negotiation Link Partner Ability Register */ - s->mdimem[reg] = 0x41fe; - break; - case 6: /* Auto-Negotiation Expansion Register */ - s->mdimem[reg] = 0x0001; - break; - } - data = s->mdimem[reg]; - } - /* Emulation takes no time to finish MDI transaction. - * Set MDI bit in SCB status register. */ - s->mem[SCBAck] |= 0x08; - val |= BIT(28); - if (raiseint) { - eepro100_mdi_interrupt(s); - } - } - val = (val & 0xffff0000) + data; - e100_write_reg4(s, SCBCtrlMDI, val); -} - -/***************************************************************************** - * - * Port emulation. - * - ****************************************************************************/ - -#define PORT_SOFTWARE_RESET 0 -#define PORT_SELFTEST 1 -#define PORT_SELECTIVE_RESET 2 -#define PORT_DUMP 3 -#define PORT_SELECTION_MASK 3 - -typedef struct { - uint32_t st_sign; /* Self Test Signature */ - uint32_t st_result; /* Self Test Results */ -} eepro100_selftest_t; - -static uint32_t eepro100_read_port(EEPRO100State * s) -{ - return 0; -} - -static void eepro100_write_port(EEPRO100State *s) -{ - uint32_t val = e100_read_reg4(s, SCBPort); - uint32_t address = (val & ~PORT_SELECTION_MASK); - uint8_t selection = (val & PORT_SELECTION_MASK); - switch (selection) { - case PORT_SOFTWARE_RESET: - nic_reset(s); - break; - case PORT_SELFTEST: - TRACE(OTHER, logout("selftest address=0x%08x\n", address)); - eepro100_selftest_t data; - pci_dma_read(&s->dev, address, (uint8_t *) &data, sizeof(data)); - data.st_sign = 0xffffffff; - data.st_result = 0; - pci_dma_write(&s->dev, address, (uint8_t *) &data, sizeof(data)); - break; - case PORT_SELECTIVE_RESET: - TRACE(OTHER, logout("selective reset, selftest address=0x%08x\n", address)); - nic_selective_reset(s); - break; - default: - logout("val=0x%08x\n", val); - missing("unknown port selection"); - } -} - -/***************************************************************************** - * - * General hardware emulation. - * - ****************************************************************************/ - -static uint8_t eepro100_read1(EEPRO100State * s, uint32_t addr) -{ - uint8_t val = 0; - if (addr <= sizeof(s->mem) - sizeof(val)) { - val = s->mem[addr]; - } - - switch (addr) { - case SCBStatus: - case SCBAck: - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - break; - case SCBCmd: - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); -#if 0 - val = eepro100_read_command(s); -#endif - break; - case SCBIntmask: - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - break; - case SCBPort + 3: - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - break; - case SCBeeprom: - val = eepro100_read_eeprom(s); - break; - case SCBCtrlMDI: - case SCBCtrlMDI + 1: - case SCBCtrlMDI + 2: - case SCBCtrlMDI + 3: - val = (uint8_t)(eepro100_read_mdi(s) >> (8 * (addr & 3))); - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - break; - case SCBpmdr: /* Power Management Driver Register */ - val = 0; - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - break; - case SCBgctrl: /* General Control Register */ - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - break; - case SCBgstat: /* General Status Register */ - /* 100 Mbps full duplex, valid link */ - val = 0x07; - TRACE(OTHER, logout("addr=General Status val=%02x\n", val)); - break; - default: - logout("addr=%s val=0x%02x\n", regname(addr), val); - missing("unknown byte read"); - } - return val; -} - -static uint16_t eepro100_read2(EEPRO100State * s, uint32_t addr) -{ - uint16_t val = 0; - if (addr <= sizeof(s->mem) - sizeof(val)) { - val = e100_read_reg2(s, addr); - } - - switch (addr) { - case SCBStatus: - case SCBCmd: - TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); - break; - case SCBeeprom: - val = eepro100_read_eeprom(s); - TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); - break; - case SCBCtrlMDI: - case SCBCtrlMDI + 2: - val = (uint16_t)(eepro100_read_mdi(s) >> (8 * (addr & 3))); - TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); - break; - default: - logout("addr=%s val=0x%04x\n", regname(addr), val); - missing("unknown word read"); - } - return val; -} - -static uint32_t eepro100_read4(EEPRO100State * s, uint32_t addr) -{ - uint32_t val = 0; - if (addr <= sizeof(s->mem) - sizeof(val)) { - val = e100_read_reg4(s, addr); - } - - switch (addr) { - case SCBStatus: - TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); - break; - case SCBPointer: - TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); - break; - case SCBPort: - val = eepro100_read_port(s); - TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); - break; - case SCBflash: - val = eepro100_read_eeprom(s); - TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); - break; - case SCBCtrlMDI: - val = eepro100_read_mdi(s); - break; - default: - logout("addr=%s val=0x%08x\n", regname(addr), val); - missing("unknown longword read"); - } - return val; -} - -static void eepro100_write1(EEPRO100State * s, uint32_t addr, uint8_t val) -{ - /* SCBStatus is readonly. */ - if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) { - s->mem[addr] = val; - } - - switch (addr) { - case SCBStatus: - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - break; - case SCBAck: - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - eepro100_acknowledge(s); - break; - case SCBCmd: - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - eepro100_write_command(s, val); - break; - case SCBIntmask: - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - if (val & BIT(1)) { - eepro100_swi_interrupt(s); - } - eepro100_interrupt(s, 0); - break; - case SCBPointer: - case SCBPointer + 1: - case SCBPointer + 2: - case SCBPointer + 3: - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - break; - case SCBPort: - case SCBPort + 1: - case SCBPort + 2: - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - break; - case SCBPort + 3: - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - eepro100_write_port(s); - break; - case SCBFlow: /* does not exist on 82557 */ - case SCBFlow + 1: - case SCBFlow + 2: - case SCBpmdr: /* does not exist on 82557 */ - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - break; - case SCBeeprom: - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - eepro100_write_eeprom(s->eeprom, val); - break; - case SCBCtrlMDI: - case SCBCtrlMDI + 1: - case SCBCtrlMDI + 2: - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - break; - case SCBCtrlMDI + 3: - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - eepro100_write_mdi(s); - break; - default: - logout("addr=%s val=0x%02x\n", regname(addr), val); - missing("unknown byte write"); - } -} - -static void eepro100_write2(EEPRO100State * s, uint32_t addr, uint16_t val) -{ - /* SCBStatus is readonly. */ - if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) { - e100_write_reg2(s, addr, val); - } - - switch (addr) { - case SCBStatus: - TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); - s->mem[SCBAck] = (val >> 8); - eepro100_acknowledge(s); - break; - case SCBCmd: - TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); - eepro100_write_command(s, val); - eepro100_write1(s, SCBIntmask, val >> 8); - break; - case SCBPointer: - case SCBPointer + 2: - TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); - break; - case SCBPort: - TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); - break; - case SCBPort + 2: - TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); - eepro100_write_port(s); - break; - case SCBeeprom: - TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); - eepro100_write_eeprom(s->eeprom, val); - break; - case SCBCtrlMDI: - TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); - break; - case SCBCtrlMDI + 2: - TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); - eepro100_write_mdi(s); - break; - default: - logout("addr=%s val=0x%04x\n", regname(addr), val); - missing("unknown word write"); - } -} - -static void eepro100_write4(EEPRO100State * s, uint32_t addr, uint32_t val) -{ - if (addr <= sizeof(s->mem) - sizeof(val)) { - e100_write_reg4(s, addr, val); - } - - switch (addr) { - case SCBPointer: - TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); - break; - case SCBPort: - TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); - eepro100_write_port(s); - break; - case SCBflash: - TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); - val = val >> 16; - eepro100_write_eeprom(s->eeprom, val); - break; - case SCBCtrlMDI: - TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); - eepro100_write_mdi(s); - break; - default: - logout("addr=%s val=0x%08x\n", regname(addr), val); - missing("unknown longword write"); - } -} - -static uint64_t eepro100_read(void *opaque, hwaddr addr, - unsigned size) -{ - EEPRO100State *s = opaque; - - switch (size) { - case 1: return eepro100_read1(s, addr); - case 2: return eepro100_read2(s, addr); - case 4: return eepro100_read4(s, addr); - default: abort(); - } -} - -static void eepro100_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - EEPRO100State *s = opaque; - - switch (size) { - case 1: - eepro100_write1(s, addr, data); - break; - case 2: - eepro100_write2(s, addr, data); - break; - case 4: - eepro100_write4(s, addr, data); - break; - default: - abort(); - } -} - -static const MemoryRegionOps eepro100_ops = { - .read = eepro100_read, - .write = eepro100_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size) -{ - /* TODO: - * - Magic packets should set bit 30 in power management driver register. - * - Interesting packets should set bit 29 in power management driver register. - */ - EEPRO100State *s = qemu_get_nic_opaque(nc); - uint16_t rfd_status = 0xa000; -#if defined(CONFIG_PAD_RECEIVED_FRAMES) - uint8_t min_buf[60]; -#endif - static const uint8_t broadcast_macaddr[6] = - { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - -#if defined(CONFIG_PAD_RECEIVED_FRAMES) - /* Pad to minimum Ethernet frame length */ - if (size < sizeof(min_buf)) { - memcpy(min_buf, buf, size); - memset(&min_buf[size], 0, sizeof(min_buf) - size); - buf = min_buf; - size = sizeof(min_buf); - } -#endif - - if (s->configuration[8] & 0x80) { - /* CSMA is disabled. */ - logout("%p received while CSMA is disabled\n", s); - return -1; -#if !defined(CONFIG_PAD_RECEIVED_FRAMES) - } else if (size < 64 && (s->configuration[7] & BIT(0))) { - /* Short frame and configuration byte 7/0 (discard short receive) set: - * Short frame is discarded */ - logout("%p received short frame (%zu byte)\n", s, size); - s->statistics.rx_short_frame_errors++; - return -1; -#endif - } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & BIT(3))) { - /* Long frame and configuration byte 18/3 (long receive ok) not set: - * Long frames are discarded. */ - logout("%p received long frame (%zu byte), ignored\n", s, size); - return -1; - } else if (memcmp(buf, s->conf.macaddr.a, 6) == 0) { /* !!! */ - /* Frame matches individual address. */ - /* TODO: check configuration byte 15/4 (ignore U/L). */ - TRACE(RXTX, logout("%p received frame for me, len=%zu\n", s, size)); - } else if (memcmp(buf, broadcast_macaddr, 6) == 0) { - /* Broadcast frame. */ - TRACE(RXTX, logout("%p received broadcast, len=%zu\n", s, size)); - rfd_status |= 0x0002; - } else if (buf[0] & 0x01) { - /* Multicast frame. */ - TRACE(RXTX, logout("%p received multicast, len=%zu,%s\n", s, size, nic_dump(buf, size))); - if (s->configuration[21] & BIT(3)) { - /* Multicast all bit is set, receive all multicast frames. */ - } else { - unsigned mcast_idx = e100_compute_mcast_idx(buf); - assert(mcast_idx < 64); - if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) { - /* Multicast frame is allowed in hash table. */ - } else if (s->configuration[15] & BIT(0)) { - /* Promiscuous: receive all. */ - rfd_status |= 0x0004; - } else { - TRACE(RXTX, logout("%p multicast ignored\n", s)); - return -1; - } - } - /* TODO: Next not for promiscuous mode? */ - rfd_status |= 0x0002; - } else if (s->configuration[15] & BIT(0)) { - /* Promiscuous: receive all. */ - TRACE(RXTX, logout("%p received frame in promiscuous mode, len=%zu\n", s, size)); - rfd_status |= 0x0004; - } else if (s->configuration[20] & BIT(6)) { - /* Multiple IA bit set. */ - unsigned mcast_idx = compute_mcast_idx(buf); - assert(mcast_idx < 64); - if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) { - TRACE(RXTX, logout("%p accepted, multiple IA bit set\n", s)); - } else { - TRACE(RXTX, logout("%p frame ignored, multiple IA bit set\n", s)); - return -1; - } - } else { - TRACE(RXTX, logout("%p received frame, ignored, len=%zu,%s\n", s, size, - nic_dump(buf, size))); - return size; - } - - if (get_ru_state(s) != ru_ready) { - /* No resources available. */ - logout("no resources, state=%u\n", get_ru_state(s)); - /* TODO: RNR interrupt only at first failed frame? */ - eepro100_rnr_interrupt(s); - s->statistics.rx_resource_errors++; -#if 0 - assert(!"no resources"); -#endif - return -1; - } - /* !!! */ - eepro100_rx_t rx; - pci_dma_read(&s->dev, s->ru_base + s->ru_offset, - &rx, sizeof(eepro100_rx_t)); - uint16_t rfd_command = le16_to_cpu(rx.command); - uint16_t rfd_size = le16_to_cpu(rx.size); - - if (size > rfd_size) { - logout("Receive buffer (%" PRId16 " bytes) too small for data " - "(%zu bytes); data truncated\n", rfd_size, size); - size = rfd_size; - } -#if !defined(CONFIG_PAD_RECEIVED_FRAMES) - if (size < 64) { - rfd_status |= 0x0080; - } -#endif - TRACE(OTHER, logout("command 0x%04x, link 0x%08x, addr 0x%08x, size %u\n", - rfd_command, rx.link, rx.rx_buf_addr, rfd_size)); - stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset + - offsetof(eepro100_rx_t, status), rfd_status); - stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset + - offsetof(eepro100_rx_t, count), size); - /* Early receive interrupt not supported. */ -#if 0 - eepro100_er_interrupt(s); -#endif - /* Receive CRC Transfer not supported. */ - if (s->configuration[18] & BIT(2)) { - missing("Receive CRC Transfer"); - return -1; - } - /* TODO: check stripping enable bit. */ -#if 0 - assert(!(s->configuration[17] & BIT(0))); -#endif - pci_dma_write(&s->dev, s->ru_base + s->ru_offset + - sizeof(eepro100_rx_t), buf, size); - s->statistics.rx_good_frames++; - eepro100_fr_interrupt(s); - s->ru_offset = le32_to_cpu(rx.link); - if (rfd_command & COMMAND_EL) { - /* EL bit is set, so this was the last frame. */ - logout("receive: Running out of frames\n"); - set_ru_state(s, ru_no_resources); - eepro100_rnr_interrupt(s); - } - if (rfd_command & COMMAND_S) { - /* S bit is set. */ - set_ru_state(s, ru_suspended); - } - return size; -} - -static const VMStateDescription vmstate_eepro100 = { - .version_id = 3, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, EEPRO100State), - VMSTATE_UNUSED(32), - VMSTATE_BUFFER(mult, EEPRO100State), - VMSTATE_BUFFER(mem, EEPRO100State), - /* Save all members of struct between scb_stat and mem. */ - VMSTATE_UINT8(scb_stat, EEPRO100State), - VMSTATE_UINT8(int_stat, EEPRO100State), - VMSTATE_UNUSED(3*4), - VMSTATE_MACADDR(conf.macaddr, EEPRO100State), - VMSTATE_UNUSED(19*4), - VMSTATE_UINT16_ARRAY(mdimem, EEPRO100State, 32), - /* The eeprom should be saved and restored by its own routines. */ - VMSTATE_UINT32(device, EEPRO100State), - /* TODO check device. */ - VMSTATE_UINT32(cu_base, EEPRO100State), - VMSTATE_UINT32(cu_offset, EEPRO100State), - VMSTATE_UINT32(ru_base, EEPRO100State), - VMSTATE_UINT32(ru_offset, EEPRO100State), - VMSTATE_UINT32(statsaddr, EEPRO100State), - /* Save eepro100_stats_t statistics. */ - VMSTATE_UINT32(statistics.tx_good_frames, EEPRO100State), - VMSTATE_UINT32(statistics.tx_max_collisions, EEPRO100State), - VMSTATE_UINT32(statistics.tx_late_collisions, EEPRO100State), - VMSTATE_UINT32(statistics.tx_underruns, EEPRO100State), - VMSTATE_UINT32(statistics.tx_lost_crs, EEPRO100State), - VMSTATE_UINT32(statistics.tx_deferred, EEPRO100State), - VMSTATE_UINT32(statistics.tx_single_collisions, EEPRO100State), - VMSTATE_UINT32(statistics.tx_multiple_collisions, EEPRO100State), - VMSTATE_UINT32(statistics.tx_total_collisions, EEPRO100State), - VMSTATE_UINT32(statistics.rx_good_frames, EEPRO100State), - VMSTATE_UINT32(statistics.rx_crc_errors, EEPRO100State), - VMSTATE_UINT32(statistics.rx_alignment_errors, EEPRO100State), - VMSTATE_UINT32(statistics.rx_resource_errors, EEPRO100State), - VMSTATE_UINT32(statistics.rx_overrun_errors, EEPRO100State), - VMSTATE_UINT32(statistics.rx_cdt_errors, EEPRO100State), - VMSTATE_UINT32(statistics.rx_short_frame_errors, EEPRO100State), - VMSTATE_UINT32(statistics.fc_xmt_pause, EEPRO100State), - VMSTATE_UINT32(statistics.fc_rcv_pause, EEPRO100State), - VMSTATE_UINT32(statistics.fc_rcv_unsupported, EEPRO100State), - VMSTATE_UINT16(statistics.xmt_tco_frames, EEPRO100State), - VMSTATE_UINT16(statistics.rcv_tco_frames, EEPRO100State), - /* Configuration bytes. */ - VMSTATE_BUFFER(configuration, EEPRO100State), - VMSTATE_END_OF_LIST() - } -}; - -static void pci_nic_uninit(PCIDevice *pci_dev) -{ - EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); - - vmstate_unregister(&pci_dev->qdev, s->vmstate, s); - eeprom93xx_free(&pci_dev->qdev, s->eeprom); - qemu_del_nic(s->nic); -} - -static NetClientInfo net_eepro100_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = nic_receive, -}; - -static void e100_nic_realize(PCIDevice *pci_dev, Error **errp) -{ - EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); - E100PCIDeviceInfo *info = eepro100_get_class(s); - - TRACE(OTHER, logout("\n")); - - s->device = info->device; - - e100_pci_reset(s); - - /* Add 64 * 2 EEPROM. i82557 and i82558 support a 64 word EEPROM, - * i82559 and later support 64 or 256 word EEPROM. */ - s->eeprom = eeprom93xx_new(&pci_dev->qdev, EEPROM_SIZE); - - /* Handler for memory-mapped I/O */ - memory_region_init_io(&s->mmio_bar, OBJECT(s), &eepro100_ops, s, - "eepro100-mmio", PCI_MEM_SIZE); - pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->mmio_bar); - memory_region_init_io(&s->io_bar, OBJECT(s), &eepro100_ops, s, - "eepro100-io", PCI_IO_SIZE); - pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar); - /* FIXME: flash aliases to mmio?! */ - memory_region_init_io(&s->flash_bar, OBJECT(s), &eepro100_ops, s, - "eepro100-flash", PCI_FLASH_SIZE); - pci_register_bar(&s->dev, 2, 0, &s->flash_bar); - - qemu_macaddr_default_if_unset(&s->conf.macaddr); - logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6)); - - nic_reset(s); - - s->nic = qemu_new_nic(&net_eepro100_info, &s->conf, - object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s); - - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str)); - - qemu_register_reset(nic_reset, s); - - s->vmstate = g_malloc(sizeof(vmstate_eepro100)); - memcpy(s->vmstate, &vmstate_eepro100, sizeof(vmstate_eepro100)); - s->vmstate->name = qemu_get_queue(s->nic)->model; - vmstate_register(&pci_dev->qdev, -1, s->vmstate, s); -} - -static void eepro100_instance_init(Object *obj) -{ - EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, PCI_DEVICE(obj)); - device_add_bootindex_property(obj, &s->conf.bootindex, - "bootindex", "/ethernet-phy@0", - DEVICE(s), NULL); -} - -static E100PCIDeviceInfo e100_devices[] = { - { - .name = "i82550", - .desc = "Intel i82550 Ethernet", - .device = i82550, - /* TODO: check device id. */ - .device_id = PCI_DEVICE_ID_INTEL_82551IT, - /* Revision ID: 0x0c, 0x0d, 0x0e. */ - .revision = 0x0e, - /* TODO: check size of statistical counters. */ - .stats_size = 80, - /* TODO: check extended tcb support. */ - .has_extended_tcb_support = true, - .power_management = true, - },{ - .name = "i82551", - .desc = "Intel i82551 Ethernet", - .device = i82551, - .device_id = PCI_DEVICE_ID_INTEL_82551IT, - /* Revision ID: 0x0f, 0x10. */ - .revision = 0x0f, - /* TODO: check size of statistical counters. */ - .stats_size = 80, - .has_extended_tcb_support = true, - .power_management = true, - },{ - .name = "i82557a", - .desc = "Intel i82557A Ethernet", - .device = i82557A, - .device_id = PCI_DEVICE_ID_INTEL_82557, - .revision = 0x01, - .power_management = false, - },{ - .name = "i82557b", - .desc = "Intel i82557B Ethernet", - .device = i82557B, - .device_id = PCI_DEVICE_ID_INTEL_82557, - .revision = 0x02, - .power_management = false, - },{ - .name = "i82557c", - .desc = "Intel i82557C Ethernet", - .device = i82557C, - .device_id = PCI_DEVICE_ID_INTEL_82557, - .revision = 0x03, - .power_management = false, - },{ - .name = "i82558a", - .desc = "Intel i82558A Ethernet", - .device = i82558A, - .device_id = PCI_DEVICE_ID_INTEL_82557, - .revision = 0x04, - .stats_size = 76, - .has_extended_tcb_support = true, - .power_management = true, - },{ - .name = "i82558b", - .desc = "Intel i82558B Ethernet", - .device = i82558B, - .device_id = PCI_DEVICE_ID_INTEL_82557, - .revision = 0x05, - .stats_size = 76, - .has_extended_tcb_support = true, - .power_management = true, - },{ - .name = "i82559a", - .desc = "Intel i82559A Ethernet", - .device = i82559A, - .device_id = PCI_DEVICE_ID_INTEL_82557, - .revision = 0x06, - .stats_size = 80, - .has_extended_tcb_support = true, - .power_management = true, - },{ - .name = "i82559b", - .desc = "Intel i82559B Ethernet", - .device = i82559B, - .device_id = PCI_DEVICE_ID_INTEL_82557, - .revision = 0x07, - .stats_size = 80, - .has_extended_tcb_support = true, - .power_management = true, - },{ - .name = "i82559c", - .desc = "Intel i82559C Ethernet", - .device = i82559C, - .device_id = PCI_DEVICE_ID_INTEL_82557, -#if 0 - .revision = 0x08, -#endif - /* TODO: Windows wants revision id 0x0c. */ - .revision = 0x0c, -#if EEPROM_SIZE > 0 - .subsystem_vendor_id = PCI_VENDOR_ID_INTEL, - .subsystem_id = 0x0040, -#endif - .stats_size = 80, - .has_extended_tcb_support = true, - .power_management = true, - },{ - .name = "i82559er", - .desc = "Intel i82559ER Ethernet", - .device = i82559ER, - .device_id = PCI_DEVICE_ID_INTEL_82551IT, - .revision = 0x09, - .stats_size = 80, - .has_extended_tcb_support = true, - .power_management = true, - },{ - .name = "i82562", - .desc = "Intel i82562 Ethernet", - .device = i82562, - /* TODO: check device id. */ - .device_id = PCI_DEVICE_ID_INTEL_82551IT, - /* TODO: wrong revision id. */ - .revision = 0x0e, - .stats_size = 80, - .has_extended_tcb_support = true, - .power_management = true, - },{ - /* Toshiba Tecra 8200. */ - .name = "i82801", - .desc = "Intel i82801 Ethernet", - .device = i82801, - .device_id = 0x2449, - .revision = 0x03, - .stats_size = 80, - .has_extended_tcb_support = true, - .power_management = true, - } -}; - -static E100PCIDeviceInfo *eepro100_get_class_by_name(const char *typename) -{ - E100PCIDeviceInfo *info = NULL; - int i; - - /* This is admittedly awkward but also temporary. QOM allows for - * parameterized typing and for subclassing both of which would suitable - * handle what's going on here. But class_data is already being used as - * a stop-gap hack to allow incremental qdev conversion so we cannot use it - * right now. Once we merge the final QOM series, we can come back here and - * do this in a much more elegant fashion. - */ - for (i = 0; i < ARRAY_SIZE(e100_devices); i++) { - if (strcmp(e100_devices[i].name, typename) == 0) { - info = &e100_devices[i]; - break; - } - } - assert(info != NULL); - - return info; -} - -static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s) -{ - return eepro100_get_class_by_name(object_get_typename(OBJECT(s))); -} - -static Property e100_properties[] = { - DEFINE_NIC_PROPERTIES(EEPRO100State, conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void eepro100_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - E100PCIDeviceInfo *info; - - info = eepro100_get_class_by_name(object_class_get_name(klass)); - - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->props = e100_properties; - dc->desc = info->desc; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->class_id = PCI_CLASS_NETWORK_ETHERNET; - k->romfile = "pxe-eepro100.rom"; - k->realize = e100_nic_realize; - k->exit = pci_nic_uninit; - k->device_id = info->device_id; - k->revision = info->revision; - k->subsystem_vendor_id = info->subsystem_vendor_id; - k->subsystem_id = info->subsystem_id; -} - -static void eepro100_register_types(void) -{ - size_t i; - for (i = 0; i < ARRAY_SIZE(e100_devices); i++) { - TypeInfo type_info = {}; - E100PCIDeviceInfo *info = &e100_devices[i]; - - type_info.name = info->name; - type_info.parent = TYPE_PCI_DEVICE; - type_info.class_init = eepro100_class_init; - type_info.instance_size = sizeof(EEPRO100State); - type_info.instance_init = eepro100_instance_init; - - type_register(&type_info); - } -} - -type_init(eepro100_register_types) diff --git a/qemu/hw/net/etraxfs_eth.c b/qemu/hw/net/etraxfs_eth.c deleted file mode 100644 index 05495ec40..000000000 --- a/qemu/hw/net/etraxfs_eth.c +++ /dev/null @@ -1,648 +0,0 @@ -/* - * QEMU ETRAX Ethernet Controller. - * - * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB. - * - * 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/sysbus.h" -#include "net/net.h" -#include "hw/cris/etraxfs.h" -#include "qemu/error-report.h" - -#define D(x) - -/* Advertisement control register. */ -#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ -#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ -#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ -#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ - -/* - * The MDIO extensions in the TDK PHY model were reversed engineered from the - * linux driver (PHYID and Diagnostics reg). - * TODO: Add friendly names for the register nums. - */ -struct qemu_phy -{ - uint32_t regs[32]; - - int link; - - unsigned int (*read)(struct qemu_phy *phy, unsigned int req); - void (*write)(struct qemu_phy *phy, unsigned int req, unsigned int data); -}; - -static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req) -{ - int regnum; - unsigned r = 0; - - regnum = req & 0x1f; - - switch (regnum) { - case 1: - if (!phy->link) { - break; - } - /* MR1. */ - /* Speeds and modes. */ - r |= (1 << 13) | (1 << 14); - r |= (1 << 11) | (1 << 12); - r |= (1 << 5); /* Autoneg complete. */ - r |= (1 << 3); /* Autoneg able. */ - r |= (1 << 2); /* link. */ - break; - case 5: - /* Link partner ability. - We are kind; always agree with whatever best mode - the guest advertises. */ - r = 1 << 14; /* Success. */ - /* Copy advertised modes. */ - r |= phy->regs[4] & (15 << 5); - /* Autoneg support. */ - r |= 1; - break; - case 18: - { - /* Diagnostics reg. */ - int duplex = 0; - int speed_100 = 0; - - if (!phy->link) { - break; - } - - /* Are we advertising 100 half or 100 duplex ? */ - speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF); - speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL); - - /* Are we advertising 10 duplex or 100 duplex ? */ - duplex = !!(phy->regs[4] & ADVERTISE_100FULL); - duplex |= !!(phy->regs[4] & ADVERTISE_10FULL); - r = (speed_100 << 10) | (duplex << 11); - } - break; - - default: - r = phy->regs[regnum]; - break; - } - D(printf("\n%s %x = reg[%d]\n", __func__, r, regnum)); - return r; -} - -static void -tdk_write(struct qemu_phy *phy, unsigned int req, unsigned int data) -{ - int regnum; - - regnum = req & 0x1f; - D(printf("%s reg[%d] = %x\n", __func__, regnum, data)); - switch (regnum) { - default: - phy->regs[regnum] = data; - break; - } -} - -static void -tdk_init(struct qemu_phy *phy) -{ - phy->regs[0] = 0x3100; - /* PHY Id. */ - phy->regs[2] = 0x0300; - phy->regs[3] = 0xe400; - /* Autonegotiation advertisement reg. */ - phy->regs[4] = 0x01E1; - phy->link = 1; - - phy->read = tdk_read; - phy->write = tdk_write; -} - -struct qemu_mdio -{ - /* bus. */ - int mdc; - int mdio; - - /* decoder. */ - enum { - PREAMBLE, - SOF, - OPC, - ADDR, - REQ, - TURNAROUND, - DATA - } state; - unsigned int drive; - - unsigned int cnt; - unsigned int addr; - unsigned int opc; - unsigned int req; - unsigned int data; - - struct qemu_phy *devs[32]; -}; - -static void -mdio_attach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr) -{ - bus->devs[addr & 0x1f] = phy; -} - -#ifdef USE_THIS_DEAD_CODE -static void -mdio_detach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr) -{ - bus->devs[addr & 0x1f] = NULL; -} -#endif - -static void mdio_read_req(struct qemu_mdio *bus) -{ - struct qemu_phy *phy; - - phy = bus->devs[bus->addr]; - if (phy && phy->read) { - bus->data = phy->read(phy, bus->req); - } else { - bus->data = 0xffff; - } -} - -static void mdio_write_req(struct qemu_mdio *bus) -{ - struct qemu_phy *phy; - - phy = bus->devs[bus->addr]; - if (phy && phy->write) { - phy->write(phy, bus->req, bus->data); - } -} - -static void mdio_cycle(struct qemu_mdio *bus) -{ - bus->cnt++; - - D(printf("mdc=%d mdio=%d state=%d cnt=%d drv=%d\n", - bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive)); -#if 0 - if (bus->mdc) { - printf("%d", bus->mdio); - } -#endif - switch (bus->state) { - case PREAMBLE: - if (bus->mdc) { - if (bus->cnt >= (32 * 2) && !bus->mdio) { - bus->cnt = 0; - bus->state = SOF; - bus->data = 0; - } - } - break; - case SOF: - if (bus->mdc) { - if (bus->mdio != 1) { - printf("WARNING: no SOF\n"); - } - if (bus->cnt == 1*2) { - bus->cnt = 0; - bus->opc = 0; - bus->state = OPC; - } - } - break; - case OPC: - if (bus->mdc) { - bus->opc <<= 1; - bus->opc |= bus->mdio & 1; - if (bus->cnt == 2*2) { - bus->cnt = 0; - bus->addr = 0; - bus->state = ADDR; - } - } - break; - case ADDR: - if (bus->mdc) { - bus->addr <<= 1; - bus->addr |= bus->mdio & 1; - - if (bus->cnt == 5*2) { - bus->cnt = 0; - bus->req = 0; - bus->state = REQ; - } - } - break; - case REQ: - if (bus->mdc) { - bus->req <<= 1; - bus->req |= bus->mdio & 1; - if (bus->cnt == 5*2) { - bus->cnt = 0; - bus->state = TURNAROUND; - } - } - break; - case TURNAROUND: - if (bus->mdc && bus->cnt == 2*2) { - bus->mdio = 0; - bus->cnt = 0; - - if (bus->opc == 2) { - bus->drive = 1; - mdio_read_req(bus); - bus->mdio = bus->data & 1; - } - bus->state = DATA; - } - break; - case DATA: - if (!bus->mdc) { - if (bus->drive) { - bus->mdio = !!(bus->data & (1 << 15)); - bus->data <<= 1; - } - } else { - if (!bus->drive) { - bus->data <<= 1; - bus->data |= bus->mdio; - } - if (bus->cnt == 16 * 2) { - bus->cnt = 0; - bus->state = PREAMBLE; - if (!bus->drive) { - mdio_write_req(bus); - } - bus->drive = 0; - } - } - break; - default: - break; - } -} - -/* ETRAX-FS Ethernet MAC block starts here. */ - -#define RW_MA0_LO 0x00 -#define RW_MA0_HI 0x01 -#define RW_MA1_LO 0x02 -#define RW_MA1_HI 0x03 -#define RW_GA_LO 0x04 -#define RW_GA_HI 0x05 -#define RW_GEN_CTRL 0x06 -#define RW_REC_CTRL 0x07 -#define RW_TR_CTRL 0x08 -#define RW_CLR_ERR 0x09 -#define RW_MGM_CTRL 0x0a -#define R_STAT 0x0b -#define FS_ETH_MAX_REGS 0x17 - -#define TYPE_ETRAX_FS_ETH "etraxfs-eth" -#define ETRAX_FS_ETH(obj) \ - OBJECT_CHECK(ETRAXFSEthState, (obj), TYPE_ETRAX_FS_ETH) - -typedef struct ETRAXFSEthState -{ - SysBusDevice parent_obj; - - MemoryRegion mmio; - NICState *nic; - NICConf conf; - - /* Two addrs in the filter. */ - uint8_t macaddr[2][6]; - uint32_t regs[FS_ETH_MAX_REGS]; - - union { - void *vdma_out; - struct etraxfs_dma_client *dma_out; - }; - union { - void *vdma_in; - struct etraxfs_dma_client *dma_in; - }; - - /* MDIO bus. */ - struct qemu_mdio mdio_bus; - unsigned int phyaddr; - int duplex_mismatch; - - /* PHY. */ - struct qemu_phy phy; -} ETRAXFSEthState; - -static void eth_validate_duplex(ETRAXFSEthState *eth) -{ - struct qemu_phy *phy; - unsigned int phy_duplex; - unsigned int mac_duplex; - int new_mm = 0; - - phy = eth->mdio_bus.devs[eth->phyaddr]; - phy_duplex = !!(phy->read(phy, 18) & (1 << 11)); - mac_duplex = !!(eth->regs[RW_REC_CTRL] & 128); - - if (mac_duplex != phy_duplex) { - new_mm = 1; - } - - if (eth->regs[RW_GEN_CTRL] & 1) { - if (new_mm != eth->duplex_mismatch) { - if (new_mm) { - printf("HW: WARNING ETH duplex mismatch MAC=%d PHY=%d\n", - mac_duplex, phy_duplex); - } else { - printf("HW: ETH duplex ok.\n"); - } - } - eth->duplex_mismatch = new_mm; - } -} - -static uint64_t -eth_read(void *opaque, hwaddr addr, unsigned int size) -{ - ETRAXFSEthState *eth = opaque; - uint32_t r = 0; - - addr >>= 2; - - switch (addr) { - case R_STAT: - r = eth->mdio_bus.mdio & 1; - break; - default: - r = eth->regs[addr]; - D(printf("%s %x\n", __func__, addr * 4)); - break; - } - return r; -} - -static void eth_update_ma(ETRAXFSEthState *eth, int ma) -{ - int reg; - int i = 0; - - ma &= 1; - - reg = RW_MA0_LO; - if (ma) { - reg = RW_MA1_LO; - } - - eth->macaddr[ma][i++] = eth->regs[reg]; - eth->macaddr[ma][i++] = eth->regs[reg] >> 8; - eth->macaddr[ma][i++] = eth->regs[reg] >> 16; - eth->macaddr[ma][i++] = eth->regs[reg] >> 24; - eth->macaddr[ma][i++] = eth->regs[reg + 1]; - eth->macaddr[ma][i] = eth->regs[reg + 1] >> 8; - - D(printf("set mac%d=%x.%x.%x.%x.%x.%x\n", ma, - eth->macaddr[ma][0], eth->macaddr[ma][1], - eth->macaddr[ma][2], eth->macaddr[ma][3], - eth->macaddr[ma][4], eth->macaddr[ma][5])); -} - -static void -eth_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - ETRAXFSEthState *eth = opaque; - uint32_t value = val64; - - addr >>= 2; - switch (addr) { - case RW_MA0_LO: - case RW_MA0_HI: - eth->regs[addr] = value; - eth_update_ma(eth, 0); - break; - case RW_MA1_LO: - case RW_MA1_HI: - eth->regs[addr] = value; - eth_update_ma(eth, 1); - break; - - case RW_MGM_CTRL: - /* Attach an MDIO/PHY abstraction. */ - if (value & 2) { - eth->mdio_bus.mdio = value & 1; - } - if (eth->mdio_bus.mdc != (value & 4)) { - mdio_cycle(ð->mdio_bus); - eth_validate_duplex(eth); - } - eth->mdio_bus.mdc = !!(value & 4); - eth->regs[addr] = value; - break; - - case RW_REC_CTRL: - eth->regs[addr] = value; - eth_validate_duplex(eth); - break; - - default: - eth->regs[addr] = value; - D(printf("%s %x %x\n", __func__, addr, value)); - break; - } -} - -/* The ETRAX FS has a groupt address table (GAT) which works like a k=1 bloom - filter dropping group addresses we have not joined. The filter has 64 - bits (m). The has function is a simple nible xor of the group addr. */ -static int eth_match_groupaddr(ETRAXFSEthState *eth, const unsigned char *sa) -{ - unsigned int hsh; - int m_individual = eth->regs[RW_REC_CTRL] & 4; - int match; - - /* First bit on the wire of a MAC address signals multicast or - physical address. */ - if (!m_individual && !(sa[0] & 1)) { - return 0; - } - - /* Calculate the hash index for the GA registers. */ - hsh = 0; - hsh ^= (*sa) & 0x3f; - hsh ^= ((*sa) >> 6) & 0x03; - ++sa; - hsh ^= ((*sa) << 2) & 0x03c; - hsh ^= ((*sa) >> 4) & 0xf; - ++sa; - hsh ^= ((*sa) << 4) & 0x30; - hsh ^= ((*sa) >> 2) & 0x3f; - ++sa; - hsh ^= (*sa) & 0x3f; - hsh ^= ((*sa) >> 6) & 0x03; - ++sa; - hsh ^= ((*sa) << 2) & 0x03c; - hsh ^= ((*sa) >> 4) & 0xf; - ++sa; - hsh ^= ((*sa) << 4) & 0x30; - hsh ^= ((*sa) >> 2) & 0x3f; - - hsh &= 63; - if (hsh > 31) { - match = eth->regs[RW_GA_HI] & (1 << (hsh - 32)); - } else { - match = eth->regs[RW_GA_LO] & (1 << hsh); - } - D(printf("hsh=%x ga=%x.%x mtch=%d\n", hsh, - eth->regs[RW_GA_HI], eth->regs[RW_GA_LO], match)); - return match; -} - -static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) -{ - unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - ETRAXFSEthState *eth = qemu_get_nic_opaque(nc); - int use_ma0 = eth->regs[RW_REC_CTRL] & 1; - int use_ma1 = eth->regs[RW_REC_CTRL] & 2; - int r_bcast = eth->regs[RW_REC_CTRL] & 8; - - if (size < 12) { - return -1; - } - - D(printf("%x.%x.%x.%x.%x.%x ma=%d %d bc=%d\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], - use_ma0, use_ma1, r_bcast)); - - /* Does the frame get through the address filters? */ - if ((!use_ma0 || memcmp(buf, eth->macaddr[0], 6)) - && (!use_ma1 || memcmp(buf, eth->macaddr[1], 6)) - && (!r_bcast || memcmp(buf, sa_bcast, 6)) - && !eth_match_groupaddr(eth, buf)) { - return size; - } - - /* FIXME: Find another way to pass on the fake csum. */ - etraxfs_dmac_input(eth->dma_in, (void *)buf, size + 4, 1); - - return size; -} - -static int eth_tx_push(void *opaque, unsigned char *buf, int len, bool eop) -{ - ETRAXFSEthState *eth = opaque; - - D(printf("%s buf=%p len=%d\n", __func__, buf, len)); - qemu_send_packet(qemu_get_queue(eth->nic), buf, len); - return len; -} - -static void eth_set_link(NetClientState *nc) -{ - ETRAXFSEthState *eth = qemu_get_nic_opaque(nc); - D(printf("%s %d\n", __func__, nc->link_down)); - eth->phy.link = !nc->link_down; -} - -static const MemoryRegionOps eth_ops = { - .read = eth_read, - .write = eth_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static NetClientInfo net_etraxfs_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = eth_receive, - .link_status_changed = eth_set_link, -}; - -static int fs_eth_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - ETRAXFSEthState *s = ETRAX_FS_ETH(dev); - - if (!s->dma_out || !s->dma_in) { - error_report("Unconnected ETRAX-FS Ethernet MAC"); - return -1; - } - - s->dma_out->client.push = eth_tx_push; - s->dma_out->client.opaque = s; - s->dma_in->client.opaque = s; - s->dma_in->client.pull = NULL; - - memory_region_init_io(&s->mmio, OBJECT(dev), ð_ops, s, - "etraxfs-eth", 0x5c); - sysbus_init_mmio(sbd, &s->mmio); - - qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->nic = qemu_new_nic(&net_etraxfs_info, &s->conf, - object_get_typename(OBJECT(s)), dev->id, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - - tdk_init(&s->phy); - mdio_attach(&s->mdio_bus, &s->phy, s->phyaddr); - return 0; -} - -static Property etraxfs_eth_properties[] = { - DEFINE_PROP_UINT32("phyaddr", ETRAXFSEthState, phyaddr, 1), - DEFINE_PROP_PTR("dma_out", ETRAXFSEthState, vdma_out), - DEFINE_PROP_PTR("dma_in", ETRAXFSEthState, vdma_in), - DEFINE_NIC_PROPERTIES(ETRAXFSEthState, conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void etraxfs_eth_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = fs_eth_init; - dc->props = etraxfs_eth_properties; - /* Reason: pointer properties "dma_out", "dma_in" */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo etraxfs_eth_info = { - .name = TYPE_ETRAX_FS_ETH, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ETRAXFSEthState), - .class_init = etraxfs_eth_class_init, -}; - -static void etraxfs_eth_register_types(void) -{ - type_register_static(&etraxfs_eth_info); -} - -type_init(etraxfs_eth_register_types) diff --git a/qemu/hw/net/fsl_etsec/etsec.c b/qemu/hw/net/fsl_etsec/etsec.c deleted file mode 100644 index 1e35f7f8c..000000000 --- a/qemu/hw/net/fsl_etsec/etsec.c +++ /dev/null @@ -1,457 +0,0 @@ -/* - * QEMU Freescale eTSEC Emulator - * - * Copyright (c) 2011-2013 AdaCore - * - * 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 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" -#include "hw/ptimer.h" -#include "etsec.h" -#include "registers.h" - -/* #define HEX_DUMP */ -/* #define DEBUG_REGISTER */ - -#ifdef DEBUG_REGISTER -static const int debug_etsec = 1; -#else -static const int debug_etsec; -#endif - -#define DPRINTF(fmt, ...) do { \ - if (debug_etsec) { \ - qemu_log(fmt , ## __VA_ARGS__); \ - } \ - } while (0) - -static uint64_t etsec_read(void *opaque, hwaddr addr, unsigned size) -{ - eTSEC *etsec = opaque; - uint32_t reg_index = addr / 4; - eTSEC_Register *reg = NULL; - uint32_t ret = 0x0; - - assert(reg_index < ETSEC_REG_NUMBER); - - reg = &etsec->regs[reg_index]; - - - switch (reg->access) { - case ACC_WO: - ret = 0x00000000; - break; - - case ACC_RW: - case ACC_W1C: - case ACC_RO: - default: - ret = reg->value; - break; - } - - DPRINTF("Read 0x%08x @ 0x" TARGET_FMT_plx - " : %s (%s)\n", - ret, addr, reg->name, reg->desc); - - return ret; -} - -static void write_tstat(eTSEC *etsec, - eTSEC_Register *reg, - uint32_t reg_index, - uint32_t value) -{ - int i = 0; - - for (i = 0; i < 8; i++) { - /* Check THLTi flag in TSTAT */ - if (value & (1 << (31 - i))) { - etsec_walk_tx_ring(etsec, i); - } - } - - /* Write 1 to clear */ - reg->value &= ~value; -} - -static void write_rstat(eTSEC *etsec, - eTSEC_Register *reg, - uint32_t reg_index, - uint32_t value) -{ - int i = 0; - - for (i = 0; i < 8; i++) { - /* Check QHLTi flag in RSTAT */ - if (value & (1 << (23 - i)) && !(reg->value & (1 << (23 - i)))) { - etsec_walk_rx_ring(etsec, i); - } - } - - /* Write 1 to clear */ - reg->value &= ~value; -} - -static void write_tbasex(eTSEC *etsec, - eTSEC_Register *reg, - uint32_t reg_index, - uint32_t value) -{ - reg->value = value & ~0x7; - - /* Copy this value in the ring's TxBD pointer */ - etsec->regs[TBPTR0 + (reg_index - TBASE0)].value = value & ~0x7; -} - -static void write_rbasex(eTSEC *etsec, - eTSEC_Register *reg, - uint32_t reg_index, - uint32_t value) -{ - reg->value = value & ~0x7; - - /* Copy this value in the ring's RxBD pointer */ - etsec->regs[RBPTR0 + (reg_index - RBASE0)].value = value & ~0x7; -} - -static void write_ievent(eTSEC *etsec, - eTSEC_Register *reg, - uint32_t reg_index, - uint32_t value) -{ - /* Write 1 to clear */ - reg->value &= ~value; - - if (!(reg->value & (IEVENT_TXF | IEVENT_TXF))) { - qemu_irq_lower(etsec->tx_irq); - } - if (!(reg->value & (IEVENT_RXF | IEVENT_RXF))) { - qemu_irq_lower(etsec->rx_irq); - } - - if (!(reg->value & (IEVENT_MAG | IEVENT_GTSC | IEVENT_GRSC | IEVENT_TXC | - IEVENT_RXC | IEVENT_BABR | IEVENT_BABT | IEVENT_LC | - IEVENT_CRL | IEVENT_FGPI | IEVENT_FIR | IEVENT_FIQ | - IEVENT_DPE | IEVENT_PERR | IEVENT_EBERR | IEVENT_TXE | - IEVENT_XFUN | IEVENT_BSY | IEVENT_MSRO | IEVENT_MMRD | - IEVENT_MMRW))) { - qemu_irq_lower(etsec->err_irq); - } -} - -static void write_dmactrl(eTSEC *etsec, - eTSEC_Register *reg, - uint32_t reg_index, - uint32_t value) -{ - reg->value = value; - - if (value & DMACTRL_GRS) { - - if (etsec->rx_buffer_len != 0) { - /* Graceful receive stop delayed until end of frame */ - } else { - /* Graceful receive stop now */ - etsec->regs[IEVENT].value |= IEVENT_GRSC; - if (etsec->regs[IMASK].value & IMASK_GRSCEN) { - qemu_irq_raise(etsec->err_irq); - } - } - } - - if (value & DMACTRL_GTS) { - - if (etsec->tx_buffer_len != 0) { - /* Graceful transmit stop delayed until end of frame */ - } else { - /* Graceful transmit stop now */ - etsec->regs[IEVENT].value |= IEVENT_GTSC; - if (etsec->regs[IMASK].value & IMASK_GTSCEN) { - qemu_irq_raise(etsec->err_irq); - } - } - } - - if (!(value & DMACTRL_WOP)) { - /* Start polling */ - ptimer_stop(etsec->ptimer); - ptimer_set_count(etsec->ptimer, 1); - ptimer_run(etsec->ptimer, 1); - } -} - -static void etsec_write(void *opaque, - hwaddr addr, - uint64_t value, - unsigned size) -{ - eTSEC *etsec = opaque; - uint32_t reg_index = addr / 4; - eTSEC_Register *reg = NULL; - uint32_t before = 0x0; - - assert(reg_index < ETSEC_REG_NUMBER); - - reg = &etsec->regs[reg_index]; - before = reg->value; - - switch (reg_index) { - case IEVENT: - write_ievent(etsec, reg, reg_index, value); - break; - - case DMACTRL: - write_dmactrl(etsec, reg, reg_index, value); - break; - - case TSTAT: - write_tstat(etsec, reg, reg_index, value); - break; - - case RSTAT: - write_rstat(etsec, reg, reg_index, value); - break; - - case TBASE0 ... TBASE7: - write_tbasex(etsec, reg, reg_index, value); - break; - - case RBASE0 ... RBASE7: - write_rbasex(etsec, reg, reg_index, value); - break; - - case MIIMCFG ... MIIMIND: - etsec_write_miim(etsec, reg, reg_index, value); - break; - - default: - /* Default handling */ - switch (reg->access) { - - case ACC_RW: - case ACC_WO: - reg->value = value; - break; - - case ACC_W1C: - reg->value &= ~value; - break; - - case ACC_RO: - default: - /* Read Only or Unknown register */ - break; - } - } - - DPRINTF("Write 0x%08x @ 0x" TARGET_FMT_plx - " val:0x%08x->0x%08x : %s (%s)\n", - (unsigned int)value, addr, before, reg->value, - reg->name, reg->desc); -} - -static const MemoryRegionOps etsec_ops = { - .read = etsec_read, - .write = etsec_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void etsec_timer_hit(void *opaque) -{ - eTSEC *etsec = opaque; - - ptimer_stop(etsec->ptimer); - - if (!(etsec->regs[DMACTRL].value & DMACTRL_WOP)) { - - if (!(etsec->regs[DMACTRL].value & DMACTRL_GTS)) { - etsec_walk_tx_ring(etsec, 0); - } - ptimer_set_count(etsec->ptimer, 1); - ptimer_run(etsec->ptimer, 1); - } -} - -static void etsec_reset(DeviceState *d) -{ - eTSEC *etsec = ETSEC_COMMON(d); - int i = 0; - int reg_index = 0; - - /* Default value for all registers */ - for (i = 0; i < ETSEC_REG_NUMBER; i++) { - etsec->regs[i].name = "Reserved"; - etsec->regs[i].desc = ""; - etsec->regs[i].access = ACC_UNKNOWN; - etsec->regs[i].value = 0x00000000; - } - - /* Set-up known registers */ - for (i = 0; eTSEC_registers_def[i].name != NULL; i++) { - - reg_index = eTSEC_registers_def[i].offset / 4; - - etsec->regs[reg_index].name = eTSEC_registers_def[i].name; - etsec->regs[reg_index].desc = eTSEC_registers_def[i].desc; - etsec->regs[reg_index].access = eTSEC_registers_def[i].access; - etsec->regs[reg_index].value = eTSEC_registers_def[i].reset; - } - - etsec->tx_buffer = NULL; - etsec->tx_buffer_len = 0; - etsec->rx_buffer = NULL; - etsec->rx_buffer_len = 0; - - etsec->phy_status = - MII_SR_EXTENDED_CAPS | MII_SR_LINK_STATUS | MII_SR_AUTONEG_CAPS | - MII_SR_AUTONEG_COMPLETE | MII_SR_PREAMBLE_SUPPRESS | - MII_SR_EXTENDED_STATUS | MII_SR_100T2_HD_CAPS | MII_SR_100T2_FD_CAPS | - MII_SR_10T_HD_CAPS | MII_SR_10T_FD_CAPS | MII_SR_100X_HD_CAPS | - MII_SR_100X_FD_CAPS | MII_SR_100T4_CAPS; -} - -static ssize_t etsec_receive(NetClientState *nc, - const uint8_t *buf, - size_t size) -{ - ssize_t ret; - eTSEC *etsec = qemu_get_nic_opaque(nc); - -#if defined(HEX_DUMP) - fprintf(stderr, "%s receive size:%d\n", etsec->nic->nc.name, size); - qemu_hexdump(buf, stderr, "", size); -#endif - /* Flush is unnecessary as are already in receiving path */ - 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 available - * again. */ - etsec->need_flush = true; - } - return ret; -} - - -static void etsec_set_link_status(NetClientState *nc) -{ - eTSEC *etsec = qemu_get_nic_opaque(nc); - - etsec_miim_link_status(etsec, nc); -} - -static NetClientInfo net_etsec_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = etsec_receive, - .link_status_changed = etsec_set_link_status, -}; - -static void etsec_realize(DeviceState *dev, Error **errp) -{ - eTSEC *etsec = ETSEC_COMMON(dev); - - etsec->nic = qemu_new_nic(&net_etsec_info, &etsec->conf, - object_get_typename(OBJECT(dev)), dev->id, etsec); - qemu_format_nic_info_str(qemu_get_queue(etsec->nic), etsec->conf.macaddr.a); - - - etsec->bh = qemu_bh_new(etsec_timer_hit, etsec); - etsec->ptimer = ptimer_init(etsec->bh); - ptimer_set_freq(etsec->ptimer, 100); -} - -static void etsec_instance_init(Object *obj) -{ - eTSEC *etsec = ETSEC_COMMON(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - memory_region_init_io(&etsec->io_area, OBJECT(etsec), &etsec_ops, etsec, - "eTSEC", 0x1000); - sysbus_init_mmio(sbd, &etsec->io_area); - - sysbus_init_irq(sbd, &etsec->tx_irq); - sysbus_init_irq(sbd, &etsec->rx_irq); - sysbus_init_irq(sbd, &etsec->err_irq); -} - -static Property etsec_properties[] = { - DEFINE_NIC_PROPERTIES(eTSEC, conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void etsec_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = etsec_realize; - dc->reset = etsec_reset; - dc->props = etsec_properties; -} - -static TypeInfo etsec_info = { - .name = "eTSEC", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(eTSEC), - .class_init = etsec_class_init, - .instance_init = etsec_instance_init, -}; - -static void etsec_register_types(void) -{ - type_register_static(&etsec_info); -} - -type_init(etsec_register_types) - -DeviceState *etsec_create(hwaddr base, - MemoryRegion * mr, - NICInfo * nd, - qemu_irq tx_irq, - qemu_irq rx_irq, - qemu_irq err_irq) -{ - DeviceState *dev; - - dev = qdev_create(NULL, "eTSEC"); - qdev_set_nic_properties(dev, nd); - qdev_init_nofail(dev); - - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, tx_irq); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, rx_irq); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, err_irq); - - memory_region_add_subregion(mr, base, - SYS_BUS_DEVICE(dev)->mmio[0].memory); - - return dev; -} diff --git a/qemu/hw/net/fsl_etsec/etsec.h b/qemu/hw/net/fsl_etsec/etsec.h deleted file mode 100644 index e7dc0a4b9..000000000 --- a/qemu/hw/net/fsl_etsec/etsec.h +++ /dev/null @@ -1,176 +0,0 @@ -/* - * QEMU Freescale eTSEC Emulator - * - * Copyright (c) 2011-2013 AdaCore - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef _ETSEC_H_ -#define _ETSEC_H_ - -#include "hw/qdev.h" -#include "hw/sysbus.h" -#include "net/net.h" -#include "hw/ptimer.h" - -/* Buffer Descriptors */ - -typedef struct eTSEC_rxtx_bd { - uint16_t flags; - uint16_t length; - uint32_t bufptr; -} eTSEC_rxtx_bd; - -#define BD_WRAP (1 << 13) -#define BD_INTERRUPT (1 << 12) -#define BD_LAST (1 << 11) - -#define BD_TX_READY (1 << 15) -#define BD_TX_PADCRC (1 << 14) -#define BD_TX_TC (1 << 10) -#define BD_TX_PREDEF (1 << 9) -#define BD_TX_HFELC (1 << 7) -#define BD_TX_CFRL (1 << 6) -#define BD_TX_RC_MASK 0xF -#define BD_TX_RC_OFFSET 0x2 -#define BD_TX_TOEUN (1 << 1) -#define BD_TX_TR (1 << 0) - -#define BD_RX_EMPTY (1 << 15) -#define BD_RX_RO1 (1 << 14) -#define BD_RX_FIRST (1 << 10) -#define BD_RX_MISS (1 << 8) -#define BD_RX_BROADCAST (1 << 7) -#define BD_RX_MULTICAST (1 << 6) -#define BD_RX_LG (1 << 5) -#define BD_RX_NO (1 << 4) -#define BD_RX_SH (1 << 3) -#define BD_RX_CR (1 << 2) -#define BD_RX_OV (1 << 1) -#define BD_RX_TR (1 << 0) - -/* Tx FCB flags */ -#define FCB_TX_VLN (1 << 7) -#define FCB_TX_IP (1 << 6) -#define FCB_TX_IP6 (1 << 5) -#define FCB_TX_TUP (1 << 4) -#define FCB_TX_UDP (1 << 3) -#define FCB_TX_CIP (1 << 2) -#define FCB_TX_CTU (1 << 1) -#define FCB_TX_NPH (1 << 0) - -/* PHY Status Register */ -#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ -#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ -#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ -#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ -#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ -#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ -#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ -#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ -#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ -#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ -#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ -#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ -#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ -#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ -#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ - -/* eTSEC */ - -/* Number of register in the device */ -#define ETSEC_REG_NUMBER 1024 - -typedef struct eTSEC_Register { - const char *name; - const char *desc; - uint32_t access; - uint32_t value; -} eTSEC_Register; - -typedef struct eTSEC { - SysBusDevice busdev; - - MemoryRegion io_area; - - eTSEC_Register regs[ETSEC_REG_NUMBER]; - - NICState *nic; - NICConf conf; - - /* Tx */ - - uint8_t *tx_buffer; - uint32_t tx_buffer_len; - eTSEC_rxtx_bd first_bd; - - /* Rx */ - - uint8_t *rx_buffer; - uint32_t rx_buffer_len; - uint32_t rx_remaining_data; - uint8_t rx_first_in_frame; - uint8_t rx_fcb_size; - eTSEC_rxtx_bd rx_first_bd; - uint8_t rx_fcb[10]; - uint32_t rx_padding; - - /* IRQs */ - qemu_irq tx_irq; - qemu_irq rx_irq; - qemu_irq err_irq; - - - uint16_t phy_status; - uint16_t phy_control; - - /* Polling */ - QEMUBH *bh; - struct ptimer_state *ptimer; - - /* Whether we should flush the rx queue when buffer becomes available. */ - bool need_flush; -} eTSEC; - -#define TYPE_ETSEC_COMMON "eTSEC" -#define ETSEC_COMMON(obj) \ - OBJECT_CHECK(eTSEC, (obj), TYPE_ETSEC_COMMON) - -#define eTSEC_TRANSMIT 1 -#define eTSEC_RECEIVE 2 - -DeviceState *etsec_create(hwaddr base, - MemoryRegion *mr, - NICInfo *nd, - qemu_irq tx_irq, - qemu_irq rx_irq, - qemu_irq err_irq); - -void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr); -void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr); -ssize_t etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size); - -void etsec_write_miim(eTSEC *etsec, - eTSEC_Register *reg, - uint32_t reg_index, - uint32_t value); - -void etsec_miim_link_status(eTSEC *etsec, NetClientState *nc); - -#endif /* ! _ETSEC_H_ */ diff --git a/qemu/hw/net/fsl_etsec/miim.c b/qemu/hw/net/fsl_etsec/miim.c deleted file mode 100644 index 6bba01c82..000000000 --- a/qemu/hw/net/fsl_etsec/miim.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * QEMU Freescale eTSEC Emulator - * - * Copyright (c) 2011-2013 AdaCore - * - * 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 "etsec.h" -#include "registers.h" - -/* #define DEBUG_MIIM */ - -#define MIIM_CONTROL 0 -#define MIIM_STATUS 1 -#define MIIM_PHY_ID_1 2 -#define MIIM_PHY_ID_2 3 -#define MIIM_T2_STATUS 10 -#define MIIM_EXT_STATUS 15 - -static void miim_read_cycle(eTSEC *etsec) -{ - uint8_t phy; - uint8_t addr; - uint16_t value; - - phy = (etsec->regs[MIIMADD].value >> 8) & 0x1F; - (void)phy; /* Unreferenced */ - addr = etsec->regs[MIIMADD].value & 0x1F; - - switch (addr) { - case MIIM_CONTROL: - value = etsec->phy_control; - break; - case MIIM_STATUS: - value = etsec->phy_status; - break; - case MIIM_T2_STATUS: - value = 0x1800; /* Local and remote receivers OK */ - break; - default: - value = 0x0; - break; - }; - -#ifdef DEBUG_MIIM - qemu_log("%s phy:%d addr:0x%x value:0x%x\n", __func__, phy, addr, value); -#endif - - etsec->regs[MIIMSTAT].value = value; -} - -static void miim_write_cycle(eTSEC *etsec) -{ - uint8_t phy; - uint8_t addr; - uint16_t value; - - phy = (etsec->regs[MIIMADD].value >> 8) & 0x1F; - (void)phy; /* Unreferenced */ - addr = etsec->regs[MIIMADD].value & 0x1F; - value = etsec->regs[MIIMCON].value & 0xffff; - -#ifdef DEBUG_MIIM - qemu_log("%s phy:%d addr:0x%x value:0x%x\n", __func__, phy, addr, value); -#endif - - switch (addr) { - case MIIM_CONTROL: - etsec->phy_control = value & ~(0x8100); - break; - default: - break; - }; -} - -void etsec_write_miim(eTSEC *etsec, - eTSEC_Register *reg, - uint32_t reg_index, - uint32_t value) -{ - - switch (reg_index) { - - case MIIMCOM: - /* Read and scan cycle */ - - if ((!(reg->value & MIIMCOM_READ)) && (value & MIIMCOM_READ)) { - /* Read */ - miim_read_cycle(etsec); - } - reg->value = value; - break; - - case MIIMCON: - reg->value = value & 0xffff; - miim_write_cycle(etsec); - break; - - default: - /* Default handling */ - switch (reg->access) { - - case ACC_RW: - case ACC_WO: - reg->value = value; - break; - - case ACC_W1C: - reg->value &= ~value; - break; - - case ACC_RO: - default: - /* Read Only or Unknown register */ - break; - } - } - -} - -void etsec_miim_link_status(eTSEC *etsec, NetClientState *nc) -{ - /* Set link status */ - if (nc->link_down) { - etsec->phy_status &= ~MII_SR_LINK_STATUS; - } else { - etsec->phy_status |= MII_SR_LINK_STATUS; - } -} diff --git a/qemu/hw/net/fsl_etsec/registers.c b/qemu/hw/net/fsl_etsec/registers.c deleted file mode 100644 index 46ce7a84b..000000000 --- a/qemu/hw/net/fsl_etsec/registers.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * QEMU Freescale eTSEC Emulator - * - * Copyright (c) 2011-2013 AdaCore - * - * 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 "registers.h" - -const eTSEC_Register_Definition eTSEC_registers_def[] = { -{0x000, "TSEC_ID", "Controller ID register", ACC_RO, 0x01240000}, -{0x004, "TSEC_ID2", "Controller ID register 2", ACC_RO, 0x003000F0}, -{0x010, "IEVENT", "Interrupt event register", ACC_W1C, 0x00000000}, -{0x014, "IMASK", "Interrupt mask register", ACC_RW, 0x00000000}, -{0x018, "EDIS", "Error disabled register", ACC_RW, 0x00000000}, -{0x020, "ECNTRL", "Ethernet control register", ACC_RW, 0x00000040}, -{0x028, "PTV", "Pause time value register", ACC_RW, 0x00000000}, -{0x02C, "DMACTRL", "DMA control register", ACC_RW, 0x00000000}, -{0x030, "TBIPA", "TBI PHY address register", ACC_RW, 0x00000000}, - -/* eTSEC FIFO Control and Status Registers */ - -{0x058, "FIFO_RX_ALARM", "FIFO receive alarm start threshold register", ACC_RW, 0x00000040}, -{0x05C, "FIFO_RX_ALARM_SHUTOFF", "FIFO receive alarm shut-off threshold register", ACC_RW, 0x00000080}, -{0x08C, "FIFO_TX_THR", "FIFO transmit threshold register", ACC_RW, 0x00000080}, -{0x098, "FIFO_TX_STARVE", "FIFO transmit starve register", ACC_RW, 0x00000040}, -{0x09C, "FIFO_TX_STARVE_SHUTOFF", "FIFO transmit starve shut-off register", ACC_RW, 0x00000080}, - -/* eTSEC Transmit Control and Status Registers */ - -{0x100, "TCTRL", "Transmit control register", ACC_RW, 0x00000000}, -{0x104, "TSTAT", "Transmit status register", ACC_W1C, 0x00000000}, -{0x108, "DFVLAN", "Default VLAN control word", ACC_RW, 0x81000000}, -{0x110, "TXIC", "Transmit interrupt coalescing register", ACC_RW, 0x00000000}, -{0x114, "TQUEUE", "Transmit queue control register", ACC_RW, 0x00008000}, -{0x140, "TR03WT", "TxBD Rings 0-3 round-robin weightings", ACC_RW, 0x00000000}, -{0x144, "TR47WT", "TxBD Rings 4-7 round-robin weightings", ACC_RW, 0x00000000}, -{0x180, "TBDBPH", "Tx data buffer pointer high bits", ACC_RW, 0x00000000}, -{0x184, "TBPTR0", "TxBD pointer for ring 0", ACC_RW, 0x00000000}, -{0x18C, "TBPTR1", "TxBD pointer for ring 1", ACC_RW, 0x00000000}, -{0x194, "TBPTR2", "TxBD pointer for ring 2", ACC_RW, 0x00000000}, -{0x19C, "TBPTR3", "TxBD pointer for ring 3", ACC_RW, 0x00000000}, -{0x1A4, "TBPTR4", "TxBD pointer for ring 4", ACC_RW, 0x00000000}, -{0x1AC, "TBPTR5", "TxBD pointer for ring 5", ACC_RW, 0x00000000}, -{0x1B4, "TBPTR6", "TxBD pointer for ring 6", ACC_RW, 0x00000000}, -{0x1BC, "TBPTR7", "TxBD pointer for ring 7", ACC_RW, 0x00000000}, -{0x200, "TBASEH", "TxBD base address high bits", ACC_RW, 0x00000000}, -{0x204, "TBASE0", "TxBD base address of ring 0", ACC_RW, 0x00000000}, -{0x20C, "TBASE1", "TxBD base address of ring 1", ACC_RW, 0x00000000}, -{0x214, "TBASE2", "TxBD base address of ring 2", ACC_RW, 0x00000000}, -{0x21C, "TBASE3", "TxBD base address of ring 3", ACC_RW, 0x00000000}, -{0x224, "TBASE4", "TxBD base address of ring 4", ACC_RW, 0x00000000}, -{0x22C, "TBASE5", "TxBD base address of ring 5", ACC_RW, 0x00000000}, -{0x234, "TBASE6", "TxBD base address of ring 6", ACC_RW, 0x00000000}, -{0x23C, "TBASE7", "TxBD base address of ring 7", ACC_RW, 0x00000000}, -{0x280, "TMR_TXTS1_ID", "Tx time stamp identification tag (set 1)", ACC_RO, 0x00000000}, -{0x284, "TMR_TXTS2_ID", "Tx time stamp identification tag (set 2)", ACC_RO, 0x00000000}, -{0x2C0, "TMR_TXTS1_H", "Tx time stamp high (set 1)", ACC_RO, 0x00000000}, -{0x2C4, "TMR_TXTS1_L", "Tx time stamp high (set 1)", ACC_RO, 0x00000000}, -{0x2C8, "TMR_TXTS2_H", "Tx time stamp high (set 2)", ACC_RO, 0x00000000}, -{0x2CC, "TMR_TXTS2_L", "Tx time stamp high (set 2)", ACC_RO, 0x00000000}, - -/* eTSEC Receive Control and Status Registers */ - -{0x300, "RCTRL", "Receive control register", ACC_RW, 0x00000000}, -{0x304, "RSTAT", "Receive status register", ACC_W1C, 0x00000000}, -{0x310, "RXIC", "Receive interrupt coalescing register", ACC_RW, 0x00000000}, -{0x314, "RQUEUE", "Receive queue control register.", ACC_RW, 0x00800080}, -{0x330, "RBIFX", "Receive bit field extract control register", ACC_RW, 0x00000000}, -{0x334, "RQFAR", "Receive queue filing table address register", ACC_RW, 0x00000000}, -{0x338, "RQFCR", "Receive queue filing table control register", ACC_RW, 0x00000000}, -{0x33C, "RQFPR", "Receive queue filing table property register", ACC_RW, 0x00000000}, -{0x340, "MRBLR", "Maximum receive buffer length register", ACC_RW, 0x00000000}, -{0x380, "RBDBPH", "Rx data buffer pointer high bits", ACC_RW, 0x00000000}, -{0x384, "RBPTR0", "RxBD pointer for ring 0", ACC_RW, 0x00000000}, -{0x38C, "RBPTR1", "RxBD pointer for ring 1", ACC_RW, 0x00000000}, -{0x394, "RBPTR2", "RxBD pointer for ring 2", ACC_RW, 0x00000000}, -{0x39C, "RBPTR3", "RxBD pointer for ring 3", ACC_RW, 0x00000000}, -{0x3A4, "RBPTR4", "RxBD pointer for ring 4", ACC_RW, 0x00000000}, -{0x3AC, "RBPTR5", "RxBD pointer for ring 5", ACC_RW, 0x00000000}, -{0x3B4, "RBPTR6", "RxBD pointer for ring 6", ACC_RW, 0x00000000}, -{0x3BC, "RBPTR7", "RxBD pointer for ring 7", ACC_RW, 0x00000000}, -{0x400, "RBASEH", "RxBD base address high bits", ACC_RW, 0x00000000}, -{0x404, "RBASE0", "RxBD base address of ring 0", ACC_RW, 0x00000000}, -{0x40C, "RBASE1", "RxBD base address of ring 1", ACC_RW, 0x00000000}, -{0x414, "RBASE2", "RxBD base address of ring 2", ACC_RW, 0x00000000}, -{0x41C, "RBASE3", "RxBD base address of ring 3", ACC_RW, 0x00000000}, -{0x424, "RBASE4", "RxBD base address of ring 4", ACC_RW, 0x00000000}, -{0x42C, "RBASE5", "RxBD base address of ring 5", ACC_RW, 0x00000000}, -{0x434, "RBASE6", "RxBD base address of ring 6", ACC_RW, 0x00000000}, -{0x43C, "RBASE7", "RxBD base address of ring 7", ACC_RW, 0x00000000}, -{0x4C0, "TMR_RXTS_H", "Rx timer time stamp register high", ACC_RW, 0x00000000}, -{0x4C4, "TMR_RXTS_L", "Rx timer time stamp register low", ACC_RW, 0x00000000}, - -/* eTSEC MAC Registers */ - -{0x500, "MACCFG1", "MAC configuration register 1", ACC_RW, 0x00000000}, -{0x504, "MACCFG2", "MAC configuration register 2", ACC_RW, 0x00007000}, -{0x508, "IPGIFG", "Inter-packet/inter-frame gap register", ACC_RW, 0x40605060}, -{0x50C, "HAFDUP", "Half-duplex control", ACC_RW, 0x00A1F037}, -{0x510, "MAXFRM", "Maximum frame length", ACC_RW, 0x00000600}, -{0x520, "MIIMCFG", "MII management configuration", ACC_RW, 0x00000007}, -{0x524, "MIIMCOM", "MII management command", ACC_RW, 0x00000000}, -{0x528, "MIIMADD", "MII management address", ACC_RW, 0x00000000}, -{0x52C, "MIIMCON", "MII management control", ACC_WO, 0x00000000}, -{0x530, "MIIMSTAT", "MII management status", ACC_RO, 0x00000000}, -{0x534, "MIIMIND", "MII management indicator", ACC_RO, 0x00000000}, -{0x53C, "IFSTAT", "Interface status", ACC_RO, 0x00000000}, -{0x540, "MACSTNADDR1", "MAC station address register 1", ACC_RW, 0x00000000}, -{0x544, "MACSTNADDR2", "MAC station address register 2", ACC_RW, 0x00000000}, -{0x548, "MAC01ADDR1", "MAC exact match address 1, part 1", ACC_RW, 0x00000000}, -{0x54C, "MAC01ADDR2", "MAC exact match address 1, part 2", ACC_RW, 0x00000000}, -{0x550, "MAC02ADDR1", "MAC exact match address 2, part 1", ACC_RW, 0x00000000}, -{0x554, "MAC02ADDR2", "MAC exact match address 2, part 2", ACC_RW, 0x00000000}, -{0x558, "MAC03ADDR1", "MAC exact match address 3, part 1", ACC_RW, 0x00000000}, -{0x55C, "MAC03ADDR2", "MAC exact match address 3, part 2", ACC_RW, 0x00000000}, -{0x560, "MAC04ADDR1", "MAC exact match address 4, part 1", ACC_RW, 0x00000000}, -{0x564, "MAC04ADDR2", "MAC exact match address 4, part 2", ACC_RW, 0x00000000}, -{0x568, "MAC05ADDR1", "MAC exact match address 5, part 1", ACC_RW, 0x00000000}, -{0x56C, "MAC05ADDR2", "MAC exact match address 5, part 2", ACC_RW, 0x00000000}, -{0x570, "MAC06ADDR1", "MAC exact match address 6, part 1", ACC_RW, 0x00000000}, -{0x574, "MAC06ADDR2", "MAC exact match address 6, part 2", ACC_RW, 0x00000000}, -{0x578, "MAC07ADDR1", "MAC exact match address 7, part 1", ACC_RW, 0x00000000}, -{0x57C, "MAC07ADDR2", "MAC exact match address 7, part 2", ACC_RW, 0x00000000}, -{0x580, "MAC08ADDR1", "MAC exact match address 8, part 1", ACC_RW, 0x00000000}, -{0x584, "MAC08ADDR2", "MAC exact match address 8, part 2", ACC_RW, 0x00000000}, -{0x588, "MAC09ADDR1", "MAC exact match address 9, part 1", ACC_RW, 0x00000000}, -{0x58C, "MAC09ADDR2", "MAC exact match address 9, part 2", ACC_RW, 0x00000000}, -{0x590, "MAC10ADDR1", "MAC exact match address 10, part 1", ACC_RW, 0x00000000}, -{0x594, "MAC10ADDR2", "MAC exact match address 10, part 2", ACC_RW, 0x00000000}, -{0x598, "MAC11ADDR1", "MAC exact match address 11, part 1", ACC_RW, 0x00000000}, -{0x59C, "MAC11ADDR2", "MAC exact match address 11, part 2", ACC_RW, 0x00000000}, -{0x5A0, "MAC12ADDR1", "MAC exact match address 12, part 1", ACC_RW, 0x00000000}, -{0x5A4, "MAC12ADDR2", "MAC exact match address 12, part 2", ACC_RW, 0x00000000}, -{0x5A8, "MAC13ADDR1", "MAC exact match address 13, part 1", ACC_RW, 0x00000000}, -{0x5AC, "MAC13ADDR2", "MAC exact match address 13, part 2", ACC_RW, 0x00000000}, -{0x5B0, "MAC14ADDR1", "MAC exact match address 14, part 1", ACC_RW, 0x00000000}, -{0x5B4, "MAC14ADDR2", "MAC exact match address 14, part 2", ACC_RW, 0x00000000}, -{0x5B8, "MAC15ADDR1", "MAC exact match address 15, part 1", ACC_RW, 0x00000000}, -{0x5BC, "MAC15ADDR2", "MAC exact match address 15, part 2", ACC_RW, 0x00000000}, - -/* eTSEC, "Transmit", "and", Receive, Counters */ - -{0x680, "TR64", "Transmit and receive 64-byte frame counter ", ACC_RW, 0x00000000}, -{0x684, "TR127", "Transmit and receive 65- to 127-byte frame counter", ACC_RW, 0x00000000}, -{0x688, "TR255", "Transmit and receive 128- to 255-byte frame counter", ACC_RW, 0x00000000}, -{0x68C, "TR511", "Transmit and receive 256- to 511-byte frame counter", ACC_RW, 0x00000000}, -{0x690, "TR1K", "Transmit and receive 512- to 1023-byte frame counter", ACC_RW, 0x00000000}, -{0x694, "TRMAX", "Transmit and receive 1024- to 1518-byte frame counter", ACC_RW, 0x00000000}, -{0x698, "TRMGV", "Transmit and receive 1519- to 1522-byte good VLAN frame count", ACC_RW, 0x00000000}, - -/* eTSEC Receive Counters */ - -{0x69C, "RBYT", "Receive byte counter", ACC_RW, 0x00000000}, -{0x6A0, "RPKT", "Receive packet counter", ACC_RW, 0x00000000}, -{0x6A4, "RFCS", "Receive FCS error counter", ACC_RW, 0x00000000}, -{0x6A8, "RMCA", "Receive multicast packet counter", ACC_RW, 0x00000000}, -{0x6AC, "RBCA", "Receive broadcast packet counter", ACC_RW, 0x00000000}, -{0x6B0, "RXCF", "Receive control frame packet counter ", ACC_RW, 0x00000000}, -{0x6B4, "RXPF", "Receive PAUSE frame packet counter", ACC_RW, 0x00000000}, -{0x6B8, "RXUO", "Receive unknown OP code counter ", ACC_RW, 0x00000000}, -{0x6BC, "RALN", "Receive alignment error counter ", ACC_RW, 0x00000000}, -{0x6C0, "RFLR", "Receive frame length error counter ", ACC_RW, 0x00000000}, -{0x6C4, "RCDE", "Receive code error counter ", ACC_RW, 0x00000000}, -{0x6C8, "RCSE", "Receive carrier sense error counter", ACC_RW, 0x00000000}, -{0x6CC, "RUND", "Receive undersize packet counter", ACC_RW, 0x00000000}, -{0x6D0, "ROVR", "Receive oversize packet counter ", ACC_RW, 0x00000000}, -{0x6D4, "RFRG", "Receive fragments counter", ACC_RW, 0x00000000}, -{0x6D8, "RJBR", "Receive jabber counter ", ACC_RW, 0x00000000}, -{0x6DC, "RDRP", "Receive drop counter", ACC_RW, 0x00000000}, - -/* eTSEC Transmit Counters */ - -{0x6E0, "TBYT", "Transmit byte counter", ACC_RW, 0x00000000}, -{0x6E4, "TPKT", "Transmit packet counter", ACC_RW, 0x00000000}, -{0x6E8, "TMCA", "Transmit multicast packet counter ", ACC_RW, 0x00000000}, -{0x6EC, "TBCA", "Transmit broadcast packet counter ", ACC_RW, 0x00000000}, -{0x6F0, "TXPF", "Transmit PAUSE control frame counter ", ACC_RW, 0x00000000}, -{0x6F4, "TDFR", "Transmit deferral packet counter ", ACC_RW, 0x00000000}, -{0x6F8, "TEDF", "Transmit excessive deferral packet counter ", ACC_RW, 0x00000000}, -{0x6FC, "TSCL", "Transmit single collision packet counter", ACC_RW, 0x00000000}, -{0x700, "TMCL", "Transmit multiple collision packet counter", ACC_RW, 0x00000000}, -{0x704, "TLCL", "Transmit late collision packet counter", ACC_RW, 0x00000000}, -{0x708, "TXCL", "Transmit excessive collision packet counter", ACC_RW, 0x00000000}, -{0x70C, "TNCL", "Transmit total collision counter ", ACC_RW, 0x00000000}, -{0x714, "TDRP", "Transmit drop frame counter", ACC_RW, 0x00000000}, -{0x718, "TJBR", "Transmit jabber frame counter ", ACC_RW, 0x00000000}, -{0x71C, "TFCS", "Transmit FCS error counter", ACC_RW, 0x00000000}, -{0x720, "TXCF", "Transmit control frame counter ", ACC_RW, 0x00000000}, -{0x724, "TOVR", "Transmit oversize frame counter", ACC_RW, 0x00000000}, -{0x728, "TUND", "Transmit undersize frame counter ", ACC_RW, 0x00000000}, -{0x72C, "TFRG", "Transmit fragments frame counter ", ACC_RW, 0x00000000}, - -/* eTSEC Counter Control and TOE Statistics Registers */ - -{0x730, "CAR1", "Carry register one register", ACC_W1C, 0x00000000}, -{0x734, "CAR2", "Carry register two register ", ACC_W1C, 0x00000000}, -{0x738, "CAM1", "Carry register one mask register ", ACC_RW, 0xFE03FFFF}, -{0x73C, "CAM2", "Carry register two mask register ", ACC_RW, 0x000FFFFD}, -{0x740, "RREJ", "Receive filer rejected packet counter", ACC_RW, 0x00000000}, - -/* Hash Function Registers */ - -{0x800, "IGADDR0", "Individual/group address register 0", ACC_RW, 0x00000000}, -{0x804, "IGADDR1", "Individual/group address register 1", ACC_RW, 0x00000000}, -{0x808, "IGADDR2", "Individual/group address register 2", ACC_RW, 0x00000000}, -{0x80C, "IGADDR3", "Individual/group address register 3", ACC_RW, 0x00000000}, -{0x810, "IGADDR4", "Individual/group address register 4", ACC_RW, 0x00000000}, -{0x814, "IGADDR5", "Individual/group address register 5", ACC_RW, 0x00000000}, -{0x818, "IGADDR6", "Individual/group address register 6", ACC_RW, 0x00000000}, -{0x81C, "IGADDR7", "Individual/group address register 7", ACC_RW, 0x00000000}, -{0x880, "GADDR0", "Group address register 0", ACC_RW, 0x00000000}, -{0x884, "GADDR1", "Group address register 1", ACC_RW, 0x00000000}, -{0x888, "GADDR2", "Group address register 2", ACC_RW, 0x00000000}, -{0x88C, "GADDR3", "Group address register 3", ACC_RW, 0x00000000}, -{0x890, "GADDR4", "Group address register 4", ACC_RW, 0x00000000}, -{0x894, "GADDR5", "Group address register 5", ACC_RW, 0x00000000}, -{0x898, "GADDR6", "Group address register 6", ACC_RW, 0x00000000}, -{0x89C, "GADDR7", "Group address register 7", ACC_RW, 0x00000000}, - -/* eTSEC DMA Attribute Registers */ - -{0xBF8, "ATTR", "Attribute register", ACC_RW, 0x00000000}, -{0xBFC, "ATTRELI", "Attribute extract length and extract index register", ACC_RW, 0x00000000}, - - -/* eTSEC Lossless Flow Control Registers */ - -{0xC00, "RQPRM0", "Receive Queue Parameters register 0 ", ACC_RW, 0x00000000}, -{0xC04, "RQPRM1", "Receive Queue Parameters register 1 ", ACC_RW, 0x00000000}, -{0xC08, "RQPRM2", "Receive Queue Parameters register 2 ", ACC_RW, 0x00000000}, -{0xC0C, "RQPRM3", "Receive Queue Parameters register 3 ", ACC_RW, 0x00000000}, -{0xC10, "RQPRM4", "Receive Queue Parameters register 4 ", ACC_RW, 0x00000000}, -{0xC14, "RQPRM5", "Receive Queue Parameters register 5 ", ACC_RW, 0x00000000}, -{0xC18, "RQPRM6", "Receive Queue Parameters register 6 ", ACC_RW, 0x00000000}, -{0xC1C, "RQPRM7", "Receive Queue Parameters register 7 ", ACC_RW, 0x00000000}, -{0xC44, "RFBPTR0", "Last Free RxBD pointer for ring 0", ACC_RW, 0x00000000}, -{0xC4C, "RFBPTR1", "Last Free RxBD pointer for ring 1", ACC_RW, 0x00000000}, -{0xC54, "RFBPTR2", "Last Free RxBD pointer for ring 2", ACC_RW, 0x00000000}, -{0xC5C, "RFBPTR3", "Last Free RxBD pointer for ring 3", ACC_RW, 0x00000000}, -{0xC64, "RFBPTR4", "Last Free RxBD pointer for ring 4", ACC_RW, 0x00000000}, -{0xC6C, "RFBPTR5", "Last Free RxBD pointer for ring 5", ACC_RW, 0x00000000}, -{0xC74, "RFBPTR6", "Last Free RxBD pointer for ring 6", ACC_RW, 0x00000000}, -{0xC7C, "RFBPTR7", "Last Free RxBD pointer for ring 7", ACC_RW, 0x00000000}, - -/* eTSEC Future Expansion Space */ - -/* Reserved*/ - -/* eTSEC IEEE 1588 Registers */ - -{0xE00, "TMR_CTRL", "Timer control register", ACC_RW, 0x00010001}, -{0xE04, "TMR_TEVENT", "time stamp event register", ACC_W1C, 0x00000000}, -{0xE08, "TMR_TEMASK", "Timer event mask register", ACC_RW, 0x00000000}, -{0xE0C, "TMR_PEVENT", "time stamp event register", ACC_RW, 0x00000000}, -{0xE10, "TMR_PEMASK", "Timer event mask register", ACC_RW, 0x00000000}, -{0xE14, "TMR_STAT", "time stamp status register", ACC_RW, 0x00000000}, -{0xE18, "TMR_CNT_H", "timer counter high register", ACC_RW, 0x00000000}, -{0xE1C, "TMR_CNT_L", "timer counter low register", ACC_RW, 0x00000000}, -{0xE20, "TMR_ADD", "Timer drift compensation addend register", ACC_RW, 0x00000000}, -{0xE24, "TMR_ACC", "Timer accumulator register", ACC_RW, 0x00000000}, -{0xE28, "TMR_PRSC", "Timer prescale", ACC_RW, 0x00000002}, -{0xE30, "TMROFF_H", "Timer offset high", ACC_RW, 0x00000000}, -{0xE34, "TMROFF_L", "Timer offset low", ACC_RW, 0x00000000}, -{0xE40, "TMR_ALARM1_H", "Timer alarm 1 high register", ACC_RW, 0xFFFFFFFF}, -{0xE44, "TMR_ALARM1_L", "Timer alarm 1 high register", ACC_RW, 0xFFFFFFFF}, -{0xE48, "TMR_ALARM2_H", "Timer alarm 2 high register", ACC_RW, 0xFFFFFFFF}, -{0xE4C, "TMR_ALARM2_L", "Timer alarm 2 high register", ACC_RW, 0xFFFFFFFF}, -{0xE80, "TMR_FIPER1", "Timer fixed period interval", ACC_RW, 0xFFFFFFFF}, -{0xE84, "TMR_FIPER2", "Timer fixed period interval", ACC_RW, 0xFFFFFFFF}, -{0xE88, "TMR_FIPER3", "Timer fixed period interval", ACC_RW, 0xFFFFFFFF}, -{0xEA0, "TMR_ETTS1_H", "Time stamp of general purpose external trigger ", ACC_RW, 0x00000000}, -{0xEA4, "TMR_ETTS1_L", "Time stamp of general purpose external trigger", ACC_RW, 0x00000000}, -{0xEA8, "TMR_ETTS2_H", "Time stamp of general purpose external trigger ", ACC_RW, 0x00000000}, -{0xEAC, "TMR_ETTS2_L", "Time stamp of general purpose external trigger", ACC_RW, 0x00000000}, - -/* End Of Table */ -{0x0, 0x0, 0x0, 0x0, 0x0} -}; diff --git a/qemu/hw/net/fsl_etsec/registers.h b/qemu/hw/net/fsl_etsec/registers.h deleted file mode 100644 index 6fb96842b..000000000 --- a/qemu/hw/net/fsl_etsec/registers.h +++ /dev/null @@ -1,319 +0,0 @@ -/* - * QEMU Freescale eTSEC Emulator - * - * Copyright (c) 2011-2013 AdaCore - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef _ETSEC_REGISTERS_H_ -#define _ETSEC_REGISTERS_H_ - - -enum eTSEC_Register_Access_Type { - ACC_RW = 1, /* Read/Write */ - ACC_RO = 2, /* Read Only */ - ACC_WO = 3, /* Write Only */ - ACC_W1C = 4, /* Write 1 to clear */ - ACC_UNKNOWN = 5 /* Unknown register*/ -}; - -typedef struct eTSEC_Register_Definition { - uint32_t offset; - const char *name; - const char *desc; - enum eTSEC_Register_Access_Type access; - uint32_t reset; -} eTSEC_Register_Definition; - -extern const eTSEC_Register_Definition eTSEC_registers_def[]; - -#define DMACTRL_LE (1 << 15) -#define DMACTRL_GRS (1 << 4) -#define DMACTRL_GTS (1 << 3) -#define DMACTRL_WOP (1 << 0) - -#define IEVENT_PERR (1 << 0) -#define IEVENT_DPE (1 << 1) -#define IEVENT_FIQ (1 << 2) -#define IEVENT_FIR (1 << 3) -#define IEVENT_FGPI (1 << 4) -#define IEVENT_RXF (1 << 7) -#define IEVENT_GRSC (1 << 8) -#define IEVENT_MMRW (1 << 9) -#define IEVENT_MMRD (1 << 10) -#define IEVENT_MAG (1 << 11) -#define IEVENT_RXB (1 << 15) -#define IEVENT_XFUN (1 << 16) -#define IEVENT_CRL (1 << 17) -#define IEVENT_LC (1 << 18) -#define IEVENT_TXF (1 << 20) -#define IEVENT_TXB (1 << 21) -#define IEVENT_TXE (1 << 22) -#define IEVENT_TXC (1 << 23) -#define IEVENT_BABT (1 << 24) -#define IEVENT_GTSC (1 << 25) -#define IEVENT_MSRO (1 << 26) -#define IEVENT_EBERR (1 << 28) -#define IEVENT_BSY (1 << 29) -#define IEVENT_RXC (1 << 30) -#define IEVENT_BABR (1 << 31) - -#define IMASK_RXFEN (1 << 7) -#define IMASK_GRSCEN (1 << 8) -#define IMASK_RXBEN (1 << 15) -#define IMASK_TXFEN (1 << 20) -#define IMASK_TXBEN (1 << 21) -#define IMASK_GTSCEN (1 << 25) - -#define MACCFG1_TX_EN (1 << 0) -#define MACCFG1_RX_EN (1 << 2) - -#define MACCFG2_CRC_EN (1 << 1) -#define MACCFG2_PADCRC (1 << 2) - -#define MIIMCOM_READ (1 << 0) -#define MIIMCOM_SCAN (1 << 1) - -#define RCTRL_PRSDEP_MASK (0x3) -#define RCTRL_PRSDEP_OFFSET (6) -#define RCTRL_RSF (1 << 2) - -/* Index of each register */ - -#define TSEC_ID (0x000 / 4) -#define TSEC_ID2 (0x004 / 4) -#define IEVENT (0x010 / 4) -#define IMASK (0x014 / 4) -#define EDIS (0x018 / 4) -#define ECNTRL (0x020 / 4) -#define PTV (0x028 / 4) -#define DMACTRL (0x02C / 4) -#define TBIPA (0x030 / 4) -#define TCTRL (0x100 / 4) -#define TSTAT (0x104 / 4) -#define DFVLAN (0x108 / 4) -#define TXIC (0x110 / 4) -#define TQUEUE (0x114 / 4) -#define TR03WT (0x140 / 4) -#define TR47WT (0x144 / 4) -#define TBDBPH (0x180 / 4) -#define TBPTR0 (0x184 / 4) -#define TBPTR1 (0x18C / 4) -#define TBPTR2 (0x194 / 4) -#define TBPTR3 (0x19C / 4) -#define TBPTR4 (0x1A4 / 4) -#define TBPTR5 (0x1AC / 4) -#define TBPTR6 (0x1B4 / 4) -#define TBPTR7 (0x1BC / 4) -#define TBASEH (0x200 / 4) -#define TBASE0 (0x204 / 4) -#define TBASE1 (0x20C / 4) -#define TBASE2 (0x214 / 4) -#define TBASE3 (0x21C / 4) -#define TBASE4 (0x224 / 4) -#define TBASE5 (0x22C / 4) -#define TBASE6 (0x234 / 4) -#define TBASE7 (0x23C / 4) -#define TMR_TXTS1_ID (0x280 / 4) -#define TMR_TXTS2_ID (0x284 / 4) -#define TMR_TXTS1_H (0x2C0 / 4) -#define TMR_TXTS1_L (0x2C4 / 4) -#define TMR_TXTS2_H (0x2C8 / 4) -#define TMR_TXTS2_L (0x2CC / 4) -#define RCTRL (0x300 / 4) -#define RSTAT (0x304 / 4) -#define RXIC (0x310 / 4) -#define RQUEUE (0x314 / 4) -#define RBIFX (0x330 / 4) -#define RQFAR (0x334 / 4) -#define RQFCR (0x338 / 4) -#define RQFPR (0x33C / 4) -#define MRBLR (0x340 / 4) -#define RBDBPH (0x380 / 4) -#define RBPTR0 (0x384 / 4) -#define RBPTR1 (0x38C / 4) -#define RBPTR2 (0x394 / 4) -#define RBPTR3 (0x39C / 4) -#define RBPTR4 (0x3A4 / 4) -#define RBPTR5 (0x3AC / 4) -#define RBPTR6 (0x3B4 / 4) -#define RBPTR7 (0x3BC / 4) -#define RBASEH (0x400 / 4) -#define RBASE0 (0x404 / 4) -#define RBASE1 (0x40C / 4) -#define RBASE2 (0x414 / 4) -#define RBASE3 (0x41C / 4) -#define RBASE4 (0x424 / 4) -#define RBASE5 (0x42C / 4) -#define RBASE6 (0x434 / 4) -#define RBASE7 (0x43C / 4) -#define TMR_RXTS_H (0x4C0 / 4) -#define TMR_RXTS_L (0x4C4 / 4) -#define MACCFG1 (0x500 / 4) -#define MACCFG2 (0x504 / 4) -#define IPGIFG (0x508 / 4) -#define HAFDUP (0x50C / 4) -#define MAXFRM (0x510 / 4) -#define MIIMCFG (0x520 / 4) -#define MIIMCOM (0x524 / 4) -#define MIIMADD (0x528 / 4) -#define MIIMCON (0x52C / 4) -#define MIIMSTAT (0x530 / 4) -#define MIIMIND (0x534 / 4) -#define IFSTAT (0x53C / 4) -#define MACSTNADDR1 (0x540 / 4) -#define MACSTNADDR2 (0x544 / 4) -#define MAC01ADDR1 (0x548 / 4) -#define MAC01ADDR2 (0x54C / 4) -#define MAC02ADDR1 (0x550 / 4) -#define MAC02ADDR2 (0x554 / 4) -#define MAC03ADDR1 (0x558 / 4) -#define MAC03ADDR2 (0x55C / 4) -#define MAC04ADDR1 (0x560 / 4) -#define MAC04ADDR2 (0x564 / 4) -#define MAC05ADDR1 (0x568 / 4) -#define MAC05ADDR2 (0x56C / 4) -#define MAC06ADDR1 (0x570 / 4) -#define MAC06ADDR2 (0x574 / 4) -#define MAC07ADDR1 (0x578 / 4) -#define MAC07ADDR2 (0x57C / 4) -#define MAC08ADDR1 (0x580 / 4) -#define MAC08ADDR2 (0x584 / 4) -#define MAC09ADDR1 (0x588 / 4) -#define MAC09ADDR2 (0x58C / 4) -#define MAC10ADDR1 (0x590 / 4) -#define MAC10ADDR2 (0x594 / 4) -#define MAC11ADDR1 (0x598 / 4) -#define MAC11ADDR2 (0x59C / 4) -#define MAC12ADDR1 (0x5A0 / 4) -#define MAC12ADDR2 (0x5A4 / 4) -#define MAC13ADDR1 (0x5A8 / 4) -#define MAC13ADDR2 (0x5AC / 4) -#define MAC14ADDR1 (0x5B0 / 4) -#define MAC14ADDR2 (0x5B4 / 4) -#define MAC15ADDR1 (0x5B8 / 4) -#define MAC15ADDR2 (0x5BC / 4) -#define TR64 (0x680 / 4) -#define TR127 (0x684 / 4) -#define TR255 (0x688 / 4) -#define TR511 (0x68C / 4) -#define TR1K (0x690 / 4) -#define TRMAX (0x694 / 4) -#define TRMGV (0x698 / 4) -#define RBYT (0x69C / 4) -#define RPKT (0x6A0 / 4) -#define RFCS (0x6A4 / 4) -#define RMCA (0x6A8 / 4) -#define RBCA (0x6AC / 4) -#define RXCF (0x6B0 / 4) -#define RXPF (0x6B4 / 4) -#define RXUO (0x6B8 / 4) -#define RALN (0x6BC / 4) -#define RFLR (0x6C0 / 4) -#define RCDE (0x6C4 / 4) -#define RCSE (0x6C8 / 4) -#define RUND (0x6CC / 4) -#define ROVR (0x6D0 / 4) -#define RFRG (0x6D4 / 4) -#define RJBR (0x6D8 / 4) -#define RDRP (0x6DC / 4) -#define TBYT (0x6E0 / 4) -#define TPKT (0x6E4 / 4) -#define TMCA (0x6E8 / 4) -#define TBCA (0x6EC / 4) -#define TXPF (0x6F0 / 4) -#define TDFR (0x6F4 / 4) -#define TEDF (0x6F8 / 4) -#define TSCL (0x6FC / 4) -#define TMCL (0x700 / 4) -#define TLCL (0x704 / 4) -#define TXCL (0x708 / 4) -#define TNCL (0x70C / 4) -#define TDRP (0x714 / 4) -#define TJBR (0x718 / 4) -#define TFCS (0x71C / 4) -#define TXCF (0x720 / 4) -#define TOVR (0x724 / 4) -#define TUND (0x728 / 4) -#define TFRG (0x72C / 4) -#define CAR1 (0x730 / 4) -#define CAR2 (0x734 / 4) -#define CAM1 (0x738 / 4) -#define CAM2 (0x73C / 4) -#define RREJ (0x740 / 4) -#define IGADDR0 (0x800 / 4) -#define IGADDR1 (0x804 / 4) -#define IGADDR2 (0x808 / 4) -#define IGADDR3 (0x80C / 4) -#define IGADDR4 (0x810 / 4) -#define IGADDR5 (0x814 / 4) -#define IGADDR6 (0x818 / 4) -#define IGADDR7 (0x81C / 4) -#define GADDR0 (0x880 / 4) -#define GADDR1 (0x884 / 4) -#define GADDR2 (0x888 / 4) -#define GADDR3 (0x88C / 4) -#define GADDR4 (0x890 / 4) -#define GADDR5 (0x894 / 4) -#define GADDR6 (0x898 / 4) -#define GADDR7 (0x89C / 4) -#define ATTR (0xBF8 / 4) -#define ATTRELI (0xBFC / 4) -#define RQPRM0 (0xC00 / 4) -#define RQPRM1 (0xC04 / 4) -#define RQPRM2 (0xC08 / 4) -#define RQPRM3 (0xC0C / 4) -#define RQPRM4 (0xC10 / 4) -#define RQPRM5 (0xC14 / 4) -#define RQPRM6 (0xC18 / 4) -#define RQPRM7 (0xC1C / 4) -#define RFBPTR0 (0xC44 / 4) -#define RFBPTR1 (0xC4C / 4) -#define RFBPTR2 (0xC54 / 4) -#define RFBPTR3 (0xC5C / 4) -#define RFBPTR4 (0xC64 / 4) -#define RFBPTR5 (0xC6C / 4) -#define RFBPTR6 (0xC74 / 4) -#define RFBPTR7 (0xC7C / 4) -#define TMR_CTRL (0xE00 / 4) -#define TMR_TEVENT (0xE04 / 4) -#define TMR_TEMASK (0xE08 / 4) -#define TMR_PEVENT (0xE0C / 4) -#define TMR_PEMASK (0xE10 / 4) -#define TMR_STAT (0xE14 / 4) -#define TMR_CNT_H (0xE18 / 4) -#define TMR_CNT_L (0xE1C / 4) -#define TMR_ADD (0xE20 / 4) -#define TMR_ACC (0xE24 / 4) -#define TMR_PRSC (0xE28 / 4) -#define TMROFF_H (0xE30 / 4) -#define TMROFF_L (0xE34 / 4) -#define TMR_ALARM1_H (0xE40 / 4) -#define TMR_ALARM1_L (0xE44 / 4) -#define TMR_ALARM2_H (0xE48 / 4) -#define TMR_ALARM2_L (0xE4C / 4) -#define TMR_FIPER1 (0xE80 / 4) -#define TMR_FIPER2 (0xE84 / 4) -#define TMR_FIPER3 (0xE88 / 4) -#define TMR_ETTS1_H (0xEA0 / 4) -#define TMR_ETTS1_L (0xEA4 / 4) -#define TMR_ETTS2_H (0xEA8 / 4) -#define TMR_ETTS2_L (0xEAC / 4) - -#endif /* ! _ETSEC_REGISTERS_H_ */ diff --git a/qemu/hw/net/fsl_etsec/rings.c b/qemu/hw/net/fsl_etsec/rings.c deleted file mode 100644 index ed1de7da9..000000000 --- a/qemu/hw/net/fsl_etsec/rings.c +++ /dev/null @@ -1,654 +0,0 @@ -/* - * QEMU Freescale eTSEC Emulator - * - * Copyright (c) 2011-2013 AdaCore - * - * 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 "net/checksum.h" - -#include "etsec.h" -#include "registers.h" - -/* #define ETSEC_RING_DEBUG */ -/* #define HEX_DUMP */ -/* #define DEBUG_BD */ - -#ifdef ETSEC_RING_DEBUG -static const int debug_etsec = 1; -#else -static const int debug_etsec; -#endif - -#define RING_DEBUG(fmt, ...) do { \ - if (debug_etsec) { \ - qemu_log(fmt , ## __VA_ARGS__); \ - } \ - } while (0) - -#ifdef DEBUG_BD - -static void print_tx_bd_flags(uint16_t flags) -{ - qemu_log(" Ready: %d\n", !!(flags & BD_TX_READY)); - qemu_log(" PAD/CRC: %d\n", !!(flags & BD_TX_PADCRC)); - qemu_log(" Wrap: %d\n", !!(flags & BD_WRAP)); - qemu_log(" Interrupt: %d\n", !!(flags & BD_INTERRUPT)); - qemu_log(" Last in frame: %d\n", !!(flags & BD_LAST)); - qemu_log(" Tx CRC: %d\n", !!(flags & BD_TX_TC)); - qemu_log(" User-defined preamble / defer: %d\n", - !!(flags & BD_TX_PREDEF)); - qemu_log(" Huge frame enable / Late collision: %d\n", - !!(flags & BD_TX_HFELC)); - qemu_log(" Control frame / Retransmission Limit: %d\n", - !!(flags & BD_TX_CFRL)); - qemu_log(" Retry count: %d\n", - (flags >> BD_TX_RC_OFFSET) & BD_TX_RC_MASK); - qemu_log(" Underrun / TCP/IP off-load enable: %d\n", - !!(flags & BD_TX_TOEUN)); - qemu_log(" Truncation: %d\n", !!(flags & BD_TX_TR)); -} - -static void print_rx_bd_flags(uint16_t flags) -{ - qemu_log(" Empty: %d\n", !!(flags & BD_RX_EMPTY)); - qemu_log(" Receive software ownership: %d\n", !!(flags & BD_RX_RO1)); - qemu_log(" Wrap: %d\n", !!(flags & BD_WRAP)); - qemu_log(" Interrupt: %d\n", !!(flags & BD_INTERRUPT)); - qemu_log(" Last in frame: %d\n", !!(flags & BD_LAST)); - qemu_log(" First in frame: %d\n", !!(flags & BD_RX_FIRST)); - qemu_log(" Miss: %d\n", !!(flags & BD_RX_MISS)); - qemu_log(" Broadcast: %d\n", !!(flags & BD_RX_BROADCAST)); - qemu_log(" Multicast: %d\n", !!(flags & BD_RX_MULTICAST)); - qemu_log(" Rx frame length violation: %d\n", !!(flags & BD_RX_LG)); - qemu_log(" Rx non-octet aligned frame: %d\n", !!(flags & BD_RX_NO)); - qemu_log(" Short frame: %d\n", !!(flags & BD_RX_SH)); - qemu_log(" Rx CRC Error: %d\n", !!(flags & BD_RX_CR)); - qemu_log(" Overrun: %d\n", !!(flags & BD_RX_OV)); - qemu_log(" Truncation: %d\n", !!(flags & BD_RX_TR)); -} - - -static void print_bd(eTSEC_rxtx_bd bd, int mode, uint32_t index) -{ - qemu_log("eTSEC %s Data Buffer Descriptor (%u)\n", - mode == eTSEC_TRANSMIT ? "Transmit" : "Receive", - index); - qemu_log(" Flags : 0x%04x\n", bd.flags); - if (mode == eTSEC_TRANSMIT) { - print_tx_bd_flags(bd.flags); - } else { - print_rx_bd_flags(bd.flags); - } - qemu_log(" Length : 0x%04x\n", bd.length); - qemu_log(" Pointer : 0x%08x\n", bd.bufptr); -} - -#endif /* DEBUG_BD */ - -static void read_buffer_descriptor(eTSEC *etsec, - hwaddr addr, - eTSEC_rxtx_bd *bd) -{ - assert(bd != NULL); - - RING_DEBUG("READ Buffer Descriptor @ 0x" TARGET_FMT_plx"\n", addr); - cpu_physical_memory_read(addr, - bd, - sizeof(eTSEC_rxtx_bd)); - - if (etsec->regs[DMACTRL].value & DMACTRL_LE) { - bd->flags = lduw_le_p(&bd->flags); - bd->length = lduw_le_p(&bd->length); - bd->bufptr = ldl_le_p(&bd->bufptr); - } else { - bd->flags = lduw_be_p(&bd->flags); - bd->length = lduw_be_p(&bd->length); - bd->bufptr = ldl_be_p(&bd->bufptr); - } -} - -static void write_buffer_descriptor(eTSEC *etsec, - hwaddr addr, - eTSEC_rxtx_bd *bd) -{ - assert(bd != NULL); - - if (etsec->regs[DMACTRL].value & DMACTRL_LE) { - stw_le_p(&bd->flags, bd->flags); - stw_le_p(&bd->length, bd->length); - stl_le_p(&bd->bufptr, bd->bufptr); - } else { - stw_be_p(&bd->flags, bd->flags); - stw_be_p(&bd->length, bd->length); - stl_be_p(&bd->bufptr, bd->bufptr); - } - - RING_DEBUG("Write Buffer Descriptor @ 0x" TARGET_FMT_plx"\n", addr); - cpu_physical_memory_write(addr, - bd, - sizeof(eTSEC_rxtx_bd)); -} - -static void ievent_set(eTSEC *etsec, - uint32_t flags) -{ - etsec->regs[IEVENT].value |= flags; - - if ((flags & IEVENT_TXB && etsec->regs[IMASK].value & IMASK_TXBEN) - || (flags & IEVENT_TXF && etsec->regs[IMASK].value & IMASK_TXFEN)) { - qemu_irq_raise(etsec->tx_irq); - RING_DEBUG("%s Raise Tx IRQ\n", __func__); - } - - if ((flags & IEVENT_RXB && etsec->regs[IMASK].value & IMASK_RXBEN) - || (flags & IEVENT_RXF && etsec->regs[IMASK].value & IMASK_RXFEN)) { - qemu_irq_raise(etsec->rx_irq); - RING_DEBUG("%s Raise Rx IRQ\n", __func__); - } -} - -static void tx_padding_and_crc(eTSEC *etsec, uint32_t min_frame_len) -{ - int add = min_frame_len - etsec->tx_buffer_len; - - /* Padding */ - if (add > 0) { - RING_DEBUG("pad:%u\n", add); - etsec->tx_buffer = g_realloc(etsec->tx_buffer, - etsec->tx_buffer_len + add); - - memset(etsec->tx_buffer + etsec->tx_buffer_len, 0x0, add); - etsec->tx_buffer_len += add; - } - - /* Never add CRC in QEMU */ -} - -static void process_tx_fcb(eTSEC *etsec) -{ - uint8_t flags = (uint8_t)(*etsec->tx_buffer); - /* L3 header offset from start of frame */ - uint8_t l3_header_offset = (uint8_t)*(etsec->tx_buffer + 3); - /* L4 header offset from start of L3 header */ - uint8_t l4_header_offset = (uint8_t)*(etsec->tx_buffer + 2); - /* L3 header */ - uint8_t *l3_header = etsec->tx_buffer + 8 + l3_header_offset; - /* L4 header */ - uint8_t *l4_header = l3_header + l4_header_offset; - - /* if packet is IP4 and IP checksum is requested */ - if (flags & FCB_TX_IP && flags & FCB_TX_CIP) { - /* do IP4 checksum (TODO This function does TCP/UDP checksum - * but not sure if it also does IP4 checksum.) */ - net_checksum_calculate(etsec->tx_buffer + 8, - etsec->tx_buffer_len - 8); - } - /* TODO Check the correct usage of the PHCS field of the FCB in case the NPH - * flag is on */ - - /* if packet is IP4 and TCP or UDP */ - if (flags & FCB_TX_IP && flags & FCB_TX_TUP) { - /* if UDP */ - if (flags & FCB_TX_UDP) { - /* if checksum is requested */ - if (flags & FCB_TX_CTU) { - /* do UDP checksum */ - - net_checksum_calculate(etsec->tx_buffer + 8, - etsec->tx_buffer_len - 8); - } else { - /* set checksum field to 0 */ - l4_header[6] = 0; - l4_header[7] = 0; - } - } else if (flags & FCB_TX_CTU) { /* if TCP and checksum is requested */ - /* do TCP checksum */ - net_checksum_calculate(etsec->tx_buffer + 8, - etsec->tx_buffer_len - 8); - } - } -} - -static void process_tx_bd(eTSEC *etsec, - eTSEC_rxtx_bd *bd) -{ - uint8_t *tmp_buff = NULL; - hwaddr tbdbth = (hwaddr)(etsec->regs[TBDBPH].value & 0xF) << 32; - - if (bd->length == 0) { - /* ERROR */ - return; - } - - if (etsec->tx_buffer_len == 0) { - /* It's the first BD */ - etsec->first_bd = *bd; - } - - /* TODO: if TxBD[TOE/UN] skip the Tx Frame Control Block*/ - - /* Load this Data Buffer */ - etsec->tx_buffer = g_realloc(etsec->tx_buffer, - etsec->tx_buffer_len + bd->length); - tmp_buff = etsec->tx_buffer + etsec->tx_buffer_len; - cpu_physical_memory_read(bd->bufptr + tbdbth, tmp_buff, bd->length); - - /* Update buffer length */ - etsec->tx_buffer_len += bd->length; - - - if (etsec->tx_buffer_len != 0 && (bd->flags & BD_LAST)) { - if (etsec->regs[MACCFG1].value & MACCFG1_TX_EN) { - /* MAC Transmit enabled */ - - /* Process offload Tx FCB */ - if (etsec->first_bd.flags & BD_TX_TOEUN) { - process_tx_fcb(etsec); - } - - if (etsec->first_bd.flags & BD_TX_PADCRC - || etsec->regs[MACCFG2].value & MACCFG2_PADCRC) { - - /* Padding and CRC (Padding implies CRC) */ - tx_padding_and_crc(etsec, 64); - - } else if (etsec->first_bd.flags & BD_TX_TC - || etsec->regs[MACCFG2].value & MACCFG2_CRC_EN) { - - /* Only CRC */ - /* Never add CRC in QEMU */ - } - -#if defined(HEX_DUMP) - qemu_log("eTSEC Send packet size:%d\n", etsec->tx_buffer_len); - qemu_hexdump(etsec->tx_buffer, stderr, "", etsec->tx_buffer_len); -#endif /* ETSEC_RING_DEBUG */ - - if (etsec->first_bd.flags & BD_TX_TOEUN) { - qemu_send_packet(qemu_get_queue(etsec->nic), - etsec->tx_buffer + 8, - etsec->tx_buffer_len - 8); - } else { - qemu_send_packet(qemu_get_queue(etsec->nic), - etsec->tx_buffer, - etsec->tx_buffer_len); - } - - } - - etsec->tx_buffer_len = 0; - - if (bd->flags & BD_INTERRUPT) { - ievent_set(etsec, IEVENT_TXF); - } - } else { - if (bd->flags & BD_INTERRUPT) { - ievent_set(etsec, IEVENT_TXB); - } - } - - /* Update DB flags */ - - /* Clear Ready */ - bd->flags &= ~BD_TX_READY; - - /* Clear Defer */ - bd->flags &= ~BD_TX_PREDEF; - - /* Clear Late Collision */ - bd->flags &= ~BD_TX_HFELC; - - /* Clear Retransmission Limit */ - bd->flags &= ~BD_TX_CFRL; - - /* Clear Retry Count */ - bd->flags &= ~(BD_TX_RC_MASK << BD_TX_RC_OFFSET); - - /* Clear Underrun */ - bd->flags &= ~BD_TX_TOEUN; - - /* Clear Truncation */ - bd->flags &= ~BD_TX_TR; -} - -void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr) -{ - hwaddr ring_base = 0; - hwaddr bd_addr = 0; - eTSEC_rxtx_bd bd; - uint16_t bd_flags; - - if (!(etsec->regs[MACCFG1].value & MACCFG1_TX_EN)) { - RING_DEBUG("%s: MAC Transmit not enabled\n", __func__); - return; - } - - ring_base = (hwaddr)(etsec->regs[TBASEH].value & 0xF) << 32; - ring_base += etsec->regs[TBASE0 + ring_nbr].value & ~0x7; - bd_addr = etsec->regs[TBPTR0 + ring_nbr].value & ~0x7; - - do { - read_buffer_descriptor(etsec, bd_addr, &bd); - -#ifdef DEBUG_BD - print_bd(bd, - eTSEC_TRANSMIT, - (bd_addr - ring_base) / sizeof(eTSEC_rxtx_bd)); - -#endif /* DEBUG_BD */ - - /* Save flags before BD update */ - bd_flags = bd.flags; - - if (bd_flags & BD_TX_READY) { - process_tx_bd(etsec, &bd); - - /* Write back BD after update */ - write_buffer_descriptor(etsec, bd_addr, &bd); - } - - /* Wrap or next BD */ - if (bd_flags & BD_WRAP) { - bd_addr = ring_base; - } else { - bd_addr += sizeof(eTSEC_rxtx_bd); - } - - } while (bd_addr != ring_base); - - bd_addr = ring_base; - - /* Save the Buffer Descriptor Pointers to current bd */ - etsec->regs[TBPTR0 + ring_nbr].value = bd_addr; - - /* Set transmit halt THLTx */ - etsec->regs[TSTAT].value |= 1 << (31 - ring_nbr); -} - -static void fill_rx_bd(eTSEC *etsec, - eTSEC_rxtx_bd *bd, - const uint8_t **buf, - size_t *size) -{ - uint16_t to_write; - hwaddr bufptr = bd->bufptr + - ((hwaddr)(etsec->regs[TBDBPH].value & 0xF) << 32); - uint8_t padd[etsec->rx_padding]; - uint8_t rem; - - RING_DEBUG("eTSEC fill Rx buffer @ 0x%016" HWADDR_PRIx - " size:%zu(padding + crc:%u) + fcb:%u\n", - bufptr, *size, etsec->rx_padding, etsec->rx_fcb_size); - - bd->length = 0; - - /* This operation will only write FCB */ - if (etsec->rx_fcb_size != 0) { - - cpu_physical_memory_write(bufptr, etsec->rx_fcb, etsec->rx_fcb_size); - - bufptr += etsec->rx_fcb_size; - bd->length += etsec->rx_fcb_size; - etsec->rx_fcb_size = 0; - - } - - /* We remove padding from the computation of to_write because it is not - * allocated in the buffer. - */ - to_write = MIN(*size - etsec->rx_padding, - etsec->regs[MRBLR].value - etsec->rx_fcb_size); - - /* This operation can only write packet data and no padding */ - if (to_write > 0) { - cpu_physical_memory_write(bufptr, *buf, to_write); - - *buf += to_write; - bufptr += to_write; - *size -= to_write; - - bd->flags &= ~BD_RX_EMPTY; - bd->length += to_write; - } - - if (*size == etsec->rx_padding) { - /* The remaining bytes are only for padding which is not actually - * allocated in the data buffer. - */ - - rem = MIN(etsec->regs[MRBLR].value - bd->length, etsec->rx_padding); - - if (rem > 0) { - memset(padd, 0x0, sizeof(padd)); - etsec->rx_padding -= rem; - *size -= rem; - bd->length += rem; - cpu_physical_memory_write(bufptr, padd, rem); - } - } -} - -static void rx_init_frame(eTSEC *etsec, const uint8_t *buf, size_t size) -{ - uint32_t fcb_size = 0; - uint8_t prsdep = (etsec->regs[RCTRL].value >> RCTRL_PRSDEP_OFFSET) - & RCTRL_PRSDEP_MASK; - - if (prsdep != 0) { - /* Prepend FCB (FCB size + RCTRL[PAL]) */ - fcb_size = 8 + ((etsec->regs[RCTRL].value >> 16) & 0x1F); - - etsec->rx_fcb_size = fcb_size; - - /* TODO: fill_FCB(etsec); */ - memset(etsec->rx_fcb, 0x0, sizeof(etsec->rx_fcb)); - - } else { - etsec->rx_fcb_size = 0; - } - - g_free(etsec->rx_buffer); - - /* Do not copy the frame for now */ - etsec->rx_buffer = (uint8_t *)buf; - etsec->rx_buffer_len = size; - - /* CRC padding (We don't have to compute the CRC) */ - etsec->rx_padding = 4; - - etsec->rx_first_in_frame = 1; - etsec->rx_remaining_data = etsec->rx_buffer_len; - RING_DEBUG("%s: rx_buffer_len:%u rx_padding+crc:%u\n", __func__, - etsec->rx_buffer_len, etsec->rx_padding); -} - -ssize_t etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size) -{ - int ring_nbr = 0; /* Always use ring0 (no filer) */ - - if (etsec->rx_buffer_len != 0) { - RING_DEBUG("%s: We can't receive now," - " a buffer is already in the pipe\n", __func__); - return 0; - } - - if (etsec->regs[RSTAT].value & 1 << (23 - ring_nbr)) { - RING_DEBUG("%s: The ring is halted\n", __func__); - return -1; - } - - if (etsec->regs[DMACTRL].value & DMACTRL_GRS) { - RING_DEBUG("%s: Graceful receive stop\n", __func__); - return -1; - } - - if (!(etsec->regs[MACCFG1].value & MACCFG1_RX_EN)) { - RING_DEBUG("%s: MAC Receive not enabled\n", __func__); - return -1; - } - - if ((etsec->regs[RCTRL].value & RCTRL_RSF) && (size < 60)) { - /* CRC is not in the packet yet, so short frame is below 60 bytes */ - RING_DEBUG("%s: Drop short frame\n", __func__); - return -1; - } - - rx_init_frame(etsec, buf, size); - - etsec_walk_rx_ring(etsec, ring_nbr); - - return size; -} - -void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr) -{ - hwaddr ring_base = 0; - hwaddr bd_addr = 0; - hwaddr start_bd_addr = 0; - eTSEC_rxtx_bd bd; - uint16_t bd_flags; - size_t remaining_data; - const uint8_t *buf; - uint8_t *tmp_buf; - size_t size; - - if (etsec->rx_buffer_len == 0) { - /* No frame to send */ - RING_DEBUG("No frame to send\n"); - return; - } - - remaining_data = etsec->rx_remaining_data + etsec->rx_padding; - buf = etsec->rx_buffer - + (etsec->rx_buffer_len - etsec->rx_remaining_data); - size = etsec->rx_buffer_len + etsec->rx_padding; - - ring_base = (hwaddr)(etsec->regs[RBASEH].value & 0xF) << 32; - ring_base += etsec->regs[RBASE0 + ring_nbr].value & ~0x7; - start_bd_addr = bd_addr = etsec->regs[RBPTR0 + ring_nbr].value & ~0x7; - - do { - read_buffer_descriptor(etsec, bd_addr, &bd); - -#ifdef DEBUG_BD - print_bd(bd, - eTSEC_RECEIVE, - (bd_addr - ring_base) / sizeof(eTSEC_rxtx_bd)); - -#endif /* DEBUG_BD */ - - /* Save flags before BD update */ - bd_flags = bd.flags; - - if (bd_flags & BD_RX_EMPTY) { - fill_rx_bd(etsec, &bd, &buf, &remaining_data); - - if (etsec->rx_first_in_frame) { - bd.flags |= BD_RX_FIRST; - etsec->rx_first_in_frame = 0; - etsec->rx_first_bd = bd; - } - - /* Last in frame */ - if (remaining_data == 0) { - - /* Clear flags */ - - bd.flags &= ~0x7ff; - - bd.flags |= BD_LAST; - - /* NOTE: non-octet aligned frame is impossible in qemu */ - - if (size >= etsec->regs[MAXFRM].value) { - /* frame length violation */ - qemu_log("%s frame length violation: size:%zu MAXFRM:%d\n", - __func__, size, etsec->regs[MAXFRM].value); - - bd.flags |= BD_RX_LG; - } - - if (size < 64) { - /* Short frame */ - bd.flags |= BD_RX_SH; - } - - /* TODO: Broadcast and Multicast */ - - if (bd.flags & BD_INTERRUPT) { - /* Set RXFx */ - etsec->regs[RSTAT].value |= 1 << (7 - ring_nbr); - - /* Set IEVENT */ - ievent_set(etsec, IEVENT_RXF); - } - - } else { - if (bd.flags & BD_INTERRUPT) { - /* Set IEVENT */ - ievent_set(etsec, IEVENT_RXB); - } - } - - /* Write back BD after update */ - write_buffer_descriptor(etsec, bd_addr, &bd); - } - - /* Wrap or next BD */ - if (bd_flags & BD_WRAP) { - bd_addr = ring_base; - } else { - bd_addr += sizeof(eTSEC_rxtx_bd); - } - } while (remaining_data != 0 - && (bd_flags & BD_RX_EMPTY) - && bd_addr != start_bd_addr); - - /* Reset ring ptr */ - etsec->regs[RBPTR0 + ring_nbr].value = bd_addr; - - /* The frame is too large to fit in the Rx ring */ - if (remaining_data > 0) { - - /* Set RSTAT[QHLTx] */ - etsec->regs[RSTAT].value |= 1 << (23 - ring_nbr); - - /* Save remaining data to send the end of the frame when the ring will - * be restarted - */ - etsec->rx_remaining_data = remaining_data; - - /* Copy the frame */ - tmp_buf = g_malloc(size); - memcpy(tmp_buf, etsec->rx_buffer, size); - etsec->rx_buffer = tmp_buf; - - RING_DEBUG("no empty RxBD available any more\n"); - } else { - etsec->rx_buffer_len = 0; - etsec->rx_buffer = NULL; - if (etsec->need_flush) { - qemu_flush_queued_packets(qemu_get_queue(etsec->nic)); - } - } - - RING_DEBUG("eTSEC End of ring_write: remaining_data:%zu\n", remaining_data); -} diff --git a/qemu/hw/net/imx_fec.c b/qemu/hw/net/imx_fec.c deleted file mode 100644 index e60e3380e..000000000 --- a/qemu/hw/net/imx_fec.c +++ /dev/null @@ -1,711 +0,0 @@ -/* - * i.MX Fast Ethernet Controller emulation. - * - * Copyright (c) 2013 Jean-Christophe Dubois. - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "hw/net/imx_fec.h" -#include "sysemu/dma.h" - -/* For crc32 */ -#include - -#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 deleted file mode 100644 index 08dc474d6..000000000 --- a/qemu/hw/net/lan9118.c +++ /dev/null @@ -1,1399 +0,0 @@ -/* - * SMSC LAN9118 Ethernet interface emulation - * - * Copyright (c) 2009 CodeSourcery, LLC. - * Written by Paul Brook - * - * This code is licensed under the GNU GPL v2 - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "net/net.h" -#include "hw/devices.h" -#include "sysemu/sysemu.h" -#include "hw/ptimer.h" -/* For crc32 */ -#include - -//#define DEBUG_LAN9118 - -#ifdef DEBUG_LAN9118 -#define DPRINTF(fmt, ...) \ -do { printf("lan9118: " fmt , ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { hw_error("lan9118: error: " fmt , ## __VA_ARGS__);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0) -#endif - -#define CSR_ID_REV 0x50 -#define CSR_IRQ_CFG 0x54 -#define CSR_INT_STS 0x58 -#define CSR_INT_EN 0x5c -#define CSR_BYTE_TEST 0x64 -#define CSR_FIFO_INT 0x68 -#define CSR_RX_CFG 0x6c -#define CSR_TX_CFG 0x70 -#define CSR_HW_CFG 0x74 -#define CSR_RX_DP_CTRL 0x78 -#define CSR_RX_FIFO_INF 0x7c -#define CSR_TX_FIFO_INF 0x80 -#define CSR_PMT_CTRL 0x84 -#define CSR_GPIO_CFG 0x88 -#define CSR_GPT_CFG 0x8c -#define CSR_GPT_CNT 0x90 -#define CSR_WORD_SWAP 0x98 -#define CSR_FREE_RUN 0x9c -#define CSR_RX_DROP 0xa0 -#define CSR_MAC_CSR_CMD 0xa4 -#define CSR_MAC_CSR_DATA 0xa8 -#define CSR_AFC_CFG 0xac -#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 -#define IRQ_POL 0x00000010 -#define IRQ_TYPE 0x00000001 - -/* INT_STS/INT_EN */ -#define SW_INT 0x80000000 -#define TXSTOP_INT 0x02000000 -#define RXSTOP_INT 0x01000000 -#define RXDFH_INT 0x00800000 -#define TX_IOC_INT 0x00200000 -#define RXD_INT 0x00100000 -#define GPT_INT 0x00080000 -#define PHY_INT 0x00040000 -#define PME_INT 0x00020000 -#define TXSO_INT 0x00010000 -#define RWT_INT 0x00008000 -#define RXE_INT 0x00004000 -#define TXE_INT 0x00002000 -#define TDFU_INT 0x00000800 -#define TDFO_INT 0x00000400 -#define TDFA_INT 0x00000200 -#define TSFF_INT 0x00000100 -#define TSFL_INT 0x00000080 -#define RXDF_INT 0x00000040 -#define RDFL_INT 0x00000020 -#define RSFF_INT 0x00000010 -#define RSFL_INT 0x00000008 -#define GPIO2_INT 0x00000004 -#define GPIO1_INT 0x00000002 -#define GPIO0_INT 0x00000001 -#define RESERVED_INT 0x7c001000 - -#define MAC_CR 1 -#define MAC_ADDRH 2 -#define MAC_ADDRL 3 -#define MAC_HASHH 4 -#define MAC_HASHL 5 -#define MAC_MII_ACC 6 -#define MAC_MII_DATA 7 -#define MAC_FLOW 8 -#define MAC_VLAN1 9 /* TODO */ -#define MAC_VLAN2 10 /* TODO */ -#define MAC_WUFF 11 /* TODO */ -#define MAC_WUCSR 12 /* TODO */ - -#define MAC_CR_RXALL 0x80000000 -#define MAC_CR_RCVOWN 0x00800000 -#define MAC_CR_LOOPBK 0x00200000 -#define MAC_CR_FDPX 0x00100000 -#define MAC_CR_MCPAS 0x00080000 -#define MAC_CR_PRMS 0x00040000 -#define MAC_CR_INVFILT 0x00020000 -#define MAC_CR_PASSBAD 0x00010000 -#define MAC_CR_HO 0x00008000 -#define MAC_CR_HPFILT 0x00002000 -#define MAC_CR_LCOLL 0x00001000 -#define MAC_CR_BCAST 0x00000800 -#define MAC_CR_DISRTY 0x00000400 -#define MAC_CR_PADSTR 0x00000100 -#define MAC_CR_BOLMT 0x000000c0 -#define MAC_CR_DFCHK 0x00000020 -#define MAC_CR_TXEN 0x00000008 -#define MAC_CR_RXEN 0x00000004 -#define MAC_CR_RESERVED 0x7f404213 - -#define PHY_INT_ENERGYON 0x80 -#define PHY_INT_AUTONEG_COMPLETE 0x40 -#define PHY_INT_FAULT 0x20 -#define PHY_INT_DOWN 0x10 -#define PHY_INT_AUTONEG_LP 0x08 -#define PHY_INT_PARFAULT 0x04 -#define PHY_INT_AUTONEG_PAGE 0x02 - -#define GPT_TIMER_EN 0x20000000 - -enum tx_state { - TX_IDLE, - TX_B, - TX_DATA -}; - -typedef struct { - /* state is a tx_state but we can't put enums in VMStateDescriptions. */ - uint32_t state; - uint32_t cmd_a; - uint32_t cmd_b; - int32_t buffer_size; - int32_t offset; - int32_t pad; - int32_t fifo_used; - int32_t len; - uint8_t data[2048]; -} LAN9118Packet; - -static const VMStateDescription vmstate_lan9118_packet = { - .name = "lan9118_packet", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(state, LAN9118Packet), - VMSTATE_UINT32(cmd_a, LAN9118Packet), - VMSTATE_UINT32(cmd_b, LAN9118Packet), - VMSTATE_INT32(buffer_size, LAN9118Packet), - VMSTATE_INT32(offset, LAN9118Packet), - VMSTATE_INT32(pad, LAN9118Packet), - VMSTATE_INT32(fifo_used, LAN9118Packet), - VMSTATE_INT32(len, LAN9118Packet), - VMSTATE_UINT8_ARRAY(data, LAN9118Packet, 2048), - VMSTATE_END_OF_LIST() - } -}; - -#define TYPE_LAN9118 "lan9118" -#define LAN9118(obj) OBJECT_CHECK(lan9118_state, (obj), TYPE_LAN9118) - -typedef struct { - SysBusDevice parent_obj; - - NICState *nic; - NICConf conf; - qemu_irq irq; - MemoryRegion mmio; - ptimer_state *timer; - - uint32_t irq_cfg; - uint32_t int_sts; - uint32_t int_en; - uint32_t fifo_int; - uint32_t rx_cfg; - uint32_t tx_cfg; - uint32_t hw_cfg; - uint32_t pmt_ctrl; - uint32_t gpio_cfg; - uint32_t gpt_cfg; - uint32_t word_swap; - uint32_t free_timer_start; - uint32_t mac_cmd; - uint32_t mac_data; - uint32_t afc_cfg; - uint32_t e2p_cmd; - uint32_t e2p_data; - - uint32_t mac_cr; - uint32_t mac_hashh; - uint32_t mac_hashl; - uint32_t mac_mii_acc; - uint32_t mac_mii_data; - uint32_t mac_flow; - - uint32_t phy_status; - uint32_t phy_control; - uint32_t phy_advertise; - uint32_t phy_int; - uint32_t phy_int_mask; - - int32_t eeprom_writable; - uint8_t eeprom[128]; - - int32_t tx_fifo_size; - LAN9118Packet *txp; - LAN9118Packet tx_packet; - - int32_t tx_status_fifo_used; - int32_t tx_status_fifo_head; - uint32_t tx_status_fifo[512]; - - int32_t rx_status_fifo_size; - int32_t rx_status_fifo_used; - int32_t rx_status_fifo_head; - uint32_t rx_status_fifo[896]; - int32_t rx_fifo_size; - int32_t rx_fifo_used; - int32_t rx_fifo_head; - uint32_t rx_fifo[3360]; - int32_t rx_packet_size_head; - int32_t rx_packet_size_tail; - int32_t rx_packet_size[1024]; - - int32_t rxp_offset; - int32_t rxp_size; - int32_t rxp_pad; - - uint32_t write_word_prev_offset; - uint32_t write_word_n; - uint16_t write_word_l; - uint16_t write_word_h; - uint32_t read_word_prev_offset; - uint32_t read_word_n; - uint32_t read_long; - - uint32_t mode_16bit; -} lan9118_state; - -static const VMStateDescription vmstate_lan9118 = { - .name = "lan9118", - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PTIMER(timer, lan9118_state), - VMSTATE_UINT32(irq_cfg, lan9118_state), - VMSTATE_UINT32(int_sts, lan9118_state), - VMSTATE_UINT32(int_en, lan9118_state), - VMSTATE_UINT32(fifo_int, lan9118_state), - VMSTATE_UINT32(rx_cfg, lan9118_state), - VMSTATE_UINT32(tx_cfg, lan9118_state), - VMSTATE_UINT32(hw_cfg, lan9118_state), - VMSTATE_UINT32(pmt_ctrl, lan9118_state), - VMSTATE_UINT32(gpio_cfg, lan9118_state), - VMSTATE_UINT32(gpt_cfg, lan9118_state), - VMSTATE_UINT32(word_swap, lan9118_state), - VMSTATE_UINT32(free_timer_start, lan9118_state), - VMSTATE_UINT32(mac_cmd, lan9118_state), - VMSTATE_UINT32(mac_data, lan9118_state), - VMSTATE_UINT32(afc_cfg, lan9118_state), - VMSTATE_UINT32(e2p_cmd, lan9118_state), - VMSTATE_UINT32(e2p_data, lan9118_state), - VMSTATE_UINT32(mac_cr, lan9118_state), - VMSTATE_UINT32(mac_hashh, lan9118_state), - VMSTATE_UINT32(mac_hashl, lan9118_state), - VMSTATE_UINT32(mac_mii_acc, lan9118_state), - VMSTATE_UINT32(mac_mii_data, lan9118_state), - VMSTATE_UINT32(mac_flow, lan9118_state), - VMSTATE_UINT32(phy_status, lan9118_state), - VMSTATE_UINT32(phy_control, lan9118_state), - VMSTATE_UINT32(phy_advertise, lan9118_state), - VMSTATE_UINT32(phy_int, lan9118_state), - VMSTATE_UINT32(phy_int_mask, lan9118_state), - VMSTATE_INT32(eeprom_writable, lan9118_state), - VMSTATE_UINT8_ARRAY(eeprom, lan9118_state, 128), - VMSTATE_INT32(tx_fifo_size, lan9118_state), - /* txp always points at tx_packet so need not be saved */ - VMSTATE_STRUCT(tx_packet, lan9118_state, 0, - vmstate_lan9118_packet, LAN9118Packet), - VMSTATE_INT32(tx_status_fifo_used, lan9118_state), - VMSTATE_INT32(tx_status_fifo_head, lan9118_state), - VMSTATE_UINT32_ARRAY(tx_status_fifo, lan9118_state, 512), - VMSTATE_INT32(rx_status_fifo_size, lan9118_state), - VMSTATE_INT32(rx_status_fifo_used, lan9118_state), - VMSTATE_INT32(rx_status_fifo_head, lan9118_state), - VMSTATE_UINT32_ARRAY(rx_status_fifo, lan9118_state, 896), - VMSTATE_INT32(rx_fifo_size, lan9118_state), - VMSTATE_INT32(rx_fifo_used, lan9118_state), - VMSTATE_INT32(rx_fifo_head, lan9118_state), - VMSTATE_UINT32_ARRAY(rx_fifo, lan9118_state, 3360), - VMSTATE_INT32(rx_packet_size_head, lan9118_state), - VMSTATE_INT32(rx_packet_size_tail, lan9118_state), - VMSTATE_INT32_ARRAY(rx_packet_size, lan9118_state, 1024), - VMSTATE_INT32(rxp_offset, lan9118_state), - VMSTATE_INT32(rxp_size, lan9118_state), - VMSTATE_INT32(rxp_pad, lan9118_state), - VMSTATE_UINT32_V(write_word_prev_offset, lan9118_state, 2), - VMSTATE_UINT32_V(write_word_n, lan9118_state, 2), - VMSTATE_UINT16_V(write_word_l, lan9118_state, 2), - VMSTATE_UINT16_V(write_word_h, lan9118_state, 2), - VMSTATE_UINT32_V(read_word_prev_offset, lan9118_state, 2), - VMSTATE_UINT32_V(read_word_n, lan9118_state, 2), - VMSTATE_UINT32_V(read_long, lan9118_state, 2), - VMSTATE_UINT32_V(mode_16bit, lan9118_state, 2), - VMSTATE_END_OF_LIST() - } -}; - -static void lan9118_update(lan9118_state *s) -{ - int level; - - /* TODO: Implement FIFO level IRQs. */ - level = (s->int_sts & s->int_en) != 0; - if (level) { - s->irq_cfg |= IRQ_INT; - } else { - s->irq_cfg &= ~IRQ_INT; - } - if ((s->irq_cfg & IRQ_EN) == 0) { - level = 0; - } - if ((s->irq_cfg & (IRQ_TYPE | IRQ_POL)) != (IRQ_TYPE | IRQ_POL)) { - /* Interrupt is active low unless we're configured as - * active-high polarity, push-pull type. - */ - level = !level; - } - qemu_set_irq(s->irq, level); -} - -static void lan9118_mac_changed(lan9118_state *s) -{ - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); -} - -static void lan9118_reload_eeprom(lan9118_state *s) -{ - int i; - if (s->eeprom[0] != 0xa5) { - 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 |= E2P_CMD_MAC_ADDR_LOADED; - DPRINTF("MACADDR loaded from eeprom\n"); - lan9118_mac_changed(s); -} - -static void phy_update_irq(lan9118_state *s) -{ - if (s->phy_int & s->phy_int_mask) { - s->int_sts |= PHY_INT; - } else { - s->int_sts &= ~PHY_INT; - } - lan9118_update(s); -} - -static void phy_update_link(lan9118_state *s) -{ - /* Autonegotiation status mirrors link status. */ - if (qemu_get_queue(s->nic)->link_down) { - s->phy_status &= ~0x0024; - s->phy_int |= PHY_INT_DOWN; - } else { - s->phy_status |= 0x0024; - s->phy_int |= PHY_INT_ENERGYON; - s->phy_int |= PHY_INT_AUTONEG_COMPLETE; - } - phy_update_irq(s); -} - -static void lan9118_set_link(NetClientState *nc) -{ - phy_update_link(qemu_get_nic_opaque(nc)); -} - -static void phy_reset(lan9118_state *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 void lan9118_reset(DeviceState *d) -{ - lan9118_state *s = LAN9118(d); - - s->irq_cfg &= (IRQ_TYPE | IRQ_POL); - s->int_sts = 0; - s->int_en = 0; - s->fifo_int = 0x48000000; - s->rx_cfg = 0; - s->tx_cfg = 0; - s->hw_cfg = s->mode_16bit ? 0x00050000 : 0x00050004; - s->pmt_ctrl &= 0x45; - s->gpio_cfg = 0; - s->txp->fifo_used = 0; - s->txp->state = TX_IDLE; - s->txp->cmd_a = 0xffffffffu; - s->txp->cmd_b = 0xffffffffu; - s->txp->len = 0; - s->txp->fifo_used = 0; - s->tx_fifo_size = 4608; - s->tx_status_fifo_used = 0; - s->rx_status_fifo_size = 704; - s->rx_fifo_size = 2640; - s->rx_fifo_used = 0; - s->rx_status_fifo_size = 176; - s->rx_status_fifo_used = 0; - s->rxp_offset = 0; - s->rxp_size = 0; - s->rxp_pad = 0; - s->rx_packet_size_tail = s->rx_packet_size_head; - s->rx_packet_size[s->rx_packet_size_head] = 0; - s->mac_cmd = 0; - s->mac_data = 0; - s->afc_cfg = 0; - s->e2p_cmd = 0; - s->e2p_data = 0; - s->free_timer_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 40; - - ptimer_stop(s->timer); - ptimer_set_count(s->timer, 0xffff); - s->gpt_cfg = 0xffff; - - s->mac_cr = MAC_CR_PRMS; - s->mac_hashh = 0; - s->mac_hashl = 0; - s->mac_mii_acc = 0; - s->mac_mii_data = 0; - s->mac_flow = 0; - - s->read_word_n = 0; - s->write_word_n = 0; - - phy_reset(s); - - s->eeprom_writable = 0; - lan9118_reload_eeprom(s); -} - -static void rx_fifo_push(lan9118_state *s, uint32_t val) -{ - int fifo_pos; - fifo_pos = s->rx_fifo_head + s->rx_fifo_used; - if (fifo_pos >= s->rx_fifo_size) - fifo_pos -= s->rx_fifo_size; - s->rx_fifo[fifo_pos] = val; - s->rx_fifo_used++; -} - -/* Return nonzero if the packet is accepted by the filter. */ -static int lan9118_filter(lan9118_state *s, const uint8_t *addr) -{ - int multicast; - uint32_t hash; - - if (s->mac_cr & MAC_CR_PRMS) { - return 1; - } - if (addr[0] == 0xff && addr[1] == 0xff && addr[2] == 0xff && - addr[3] == 0xff && addr[4] == 0xff && addr[5] == 0xff) { - return (s->mac_cr & MAC_CR_BCAST) == 0; - } - - multicast = addr[0] & 1; - if (multicast &&s->mac_cr & MAC_CR_MCPAS) { - return 1; - } - if (multicast ? (s->mac_cr & MAC_CR_HPFILT) == 0 - : (s->mac_cr & MAC_CR_HO) == 0) { - /* Exact matching. */ - hash = memcmp(addr, s->conf.macaddr.a, 6); - if (s->mac_cr & MAC_CR_INVFILT) { - return hash != 0; - } else { - return hash == 0; - } - } else { - /* Hash matching */ - hash = compute_mcast_idx(addr); - if (hash & 0x20) { - return (s->mac_hashh >> (hash & 0x1f)) & 1; - } else { - return (s->mac_hashl >> (hash & 0x1f)) & 1; - } - } -} - -static ssize_t lan9118_receive(NetClientState *nc, const uint8_t *buf, - size_t size) -{ - lan9118_state *s = qemu_get_nic_opaque(nc); - int fifo_len; - int offset; - int src_pos; - int n; - int filter; - uint32_t val; - uint32_t crc; - uint32_t status; - - if ((s->mac_cr & MAC_CR_RXEN) == 0) { - return -1; - } - - if (size >= 2048 || size < 14) { - return -1; - } - - /* TODO: Implement FIFO overflow notification. */ - if (s->rx_status_fifo_used == s->rx_status_fifo_size) { - return -1; - } - - filter = lan9118_filter(s, buf); - if (!filter && (s->mac_cr & MAC_CR_RXALL) == 0) { - return size; - } - - offset = (s->rx_cfg >> 8) & 0x1f; - n = offset & 3; - fifo_len = (size + n + 3) >> 2; - /* Add a word for the CRC. */ - fifo_len++; - if (s->rx_fifo_size - s->rx_fifo_used < fifo_len) { - return -1; - } - - DPRINTF("Got packet len:%d fifo:%d filter:%s\n", - (int)size, fifo_len, filter ? "pass" : "fail"); - val = 0; - crc = bswap32(crc32(~0, buf, size)); - for (src_pos = 0; src_pos < size; src_pos++) { - val = (val >> 8) | ((uint32_t)buf[src_pos] << 24); - n++; - if (n == 4) { - n = 0; - rx_fifo_push(s, val); - val = 0; - } - } - if (n) { - val >>= ((4 - n) * 8); - val |= crc << (n * 8); - rx_fifo_push(s, val); - val = crc >> ((4 - n) * 8); - rx_fifo_push(s, val); - } else { - rx_fifo_push(s, crc); - } - n = s->rx_status_fifo_head + s->rx_status_fifo_used; - if (n >= s->rx_status_fifo_size) { - n -= s->rx_status_fifo_size; - } - s->rx_packet_size[s->rx_packet_size_tail] = fifo_len; - s->rx_packet_size_tail = (s->rx_packet_size_tail + 1023) & 1023; - s->rx_status_fifo_used++; - - status = (size + 4) << 16; - if (buf[0] == 0xff && buf[1] == 0xff && buf[2] == 0xff && - buf[3] == 0xff && buf[4] == 0xff && buf[5] == 0xff) { - status |= 0x00002000; - } else if (buf[0] & 1) { - status |= 0x00000400; - } - if (!filter) { - status |= 0x40000000; - } - s->rx_status_fifo[n] = status; - - if (s->rx_status_fifo_used > (s->fifo_int & 0xff)) { - s->int_sts |= RSFL_INT; - } - lan9118_update(s); - - return size; -} - -static uint32_t rx_fifo_pop(lan9118_state *s) -{ - int n; - uint32_t val; - - if (s->rxp_size == 0 && s->rxp_pad == 0) { - s->rxp_size = s->rx_packet_size[s->rx_packet_size_head]; - s->rx_packet_size[s->rx_packet_size_head] = 0; - if (s->rxp_size != 0) { - s->rx_packet_size_head = (s->rx_packet_size_head + 1023) & 1023; - s->rxp_offset = (s->rx_cfg >> 10) & 7; - n = s->rxp_offset + s->rxp_size; - switch (s->rx_cfg >> 30) { - case 1: - n = (-n) & 3; - break; - case 2: - n = (-n) & 7; - break; - default: - n = 0; - break; - } - s->rxp_pad = n; - DPRINTF("Pop packet size:%d offset:%d pad: %d\n", - s->rxp_size, s->rxp_offset, s->rxp_pad); - } - } - if (s->rxp_offset > 0) { - s->rxp_offset--; - val = 0; - } else if (s->rxp_size > 0) { - s->rxp_size--; - val = s->rx_fifo[s->rx_fifo_head++]; - if (s->rx_fifo_head >= s->rx_fifo_size) { - s->rx_fifo_head -= s->rx_fifo_size; - } - s->rx_fifo_used--; - } else if (s->rxp_pad > 0) { - s->rxp_pad--; - val = 0; - } else { - DPRINTF("RX underflow\n"); - s->int_sts |= RXE_INT; - val = 0; - } - lan9118_update(s); - return val; -} - -static void do_tx_packet(lan9118_state *s) -{ - int n; - uint32_t status; - - /* FIXME: Honor TX disable, and allow queueing of packets. */ - if (s->phy_control & 0x4000) { - /* This assumes the receive routine doesn't touch the VLANClient. */ - lan9118_receive(qemu_get_queue(s->nic), s->txp->data, s->txp->len); - } else { - qemu_send_packet(qemu_get_queue(s->nic), s->txp->data, s->txp->len); - } - s->txp->fifo_used = 0; - - if (s->tx_status_fifo_used == 512) { - /* Status FIFO full */ - return; - } - /* Add entry to status FIFO. */ - status = s->txp->cmd_b & 0xffff0000u; - DPRINTF("Sent packet tag:%04x len %d\n", status >> 16, s->txp->len); - n = (s->tx_status_fifo_head + s->tx_status_fifo_used) & 511; - s->tx_status_fifo[n] = status; - s->tx_status_fifo_used++; - if (s->tx_status_fifo_used == 512) { - s->int_sts |= TSFF_INT; - /* TODO: Stop transmission. */ - } -} - -static uint32_t rx_status_fifo_pop(lan9118_state *s) -{ - uint32_t val; - - val = s->rx_status_fifo[s->rx_status_fifo_head]; - if (s->rx_status_fifo_used != 0) { - s->rx_status_fifo_used--; - s->rx_status_fifo_head++; - if (s->rx_status_fifo_head >= s->rx_status_fifo_size) { - s->rx_status_fifo_head -= s->rx_status_fifo_size; - } - /* ??? What value should be returned when the FIFO is empty? */ - DPRINTF("RX status pop 0x%08x\n", val); - } - return val; -} - -static uint32_t tx_status_fifo_pop(lan9118_state *s) -{ - uint32_t val; - - val = s->tx_status_fifo[s->tx_status_fifo_head]; - if (s->tx_status_fifo_used != 0) { - s->tx_status_fifo_used--; - s->tx_status_fifo_head = (s->tx_status_fifo_head + 1) & 511; - /* ??? What value should be returned when the FIFO is empty? */ - } - return val; -} - -static void tx_fifo_push(lan9118_state *s, uint32_t val) -{ - int n; - - if (s->txp->fifo_used == s->tx_fifo_size) { - s->int_sts |= TDFO_INT; - return; - } - switch (s->txp->state) { - case TX_IDLE: - s->txp->cmd_a = val & 0x831f37ff; - s->txp->fifo_used++; - s->txp->state = TX_B; - s->txp->buffer_size = extract32(s->txp->cmd_a, 0, 11); - s->txp->offset = extract32(s->txp->cmd_a, 16, 5); - break; - case TX_B: - if (s->txp->cmd_a & 0x2000) { - /* First segment */ - s->txp->cmd_b = val; - s->txp->fifo_used++; - /* End alignment does not include command words. */ - n = (s->txp->buffer_size + s->txp->offset + 3) >> 2; - switch ((n >> 24) & 3) { - case 1: - n = (-n) & 3; - break; - case 2: - n = (-n) & 7; - break; - default: - n = 0; - } - s->txp->pad = n; - s->txp->len = 0; - } - DPRINTF("Block len:%d offset:%d pad:%d cmd %08x\n", - s->txp->buffer_size, s->txp->offset, s->txp->pad, - s->txp->cmd_a); - s->txp->state = TX_DATA; - break; - case TX_DATA: - if (s->txp->offset >= 4) { - s->txp->offset -= 4; - break; - } - if (s->txp->buffer_size <= 0 && s->txp->pad != 0) { - s->txp->pad--; - } else { - n = MIN(4, s->txp->buffer_size + s->txp->offset); - while (s->txp->offset) { - val >>= 8; - n--; - s->txp->offset--; - } - /* Documentation is somewhat unclear on the ordering of bytes - in FIFO words. Empirical results show it to be little-endian. - */ - /* TODO: FIFO overflow checking. */ - while (n--) { - s->txp->data[s->txp->len] = val & 0xff; - s->txp->len++; - val >>= 8; - s->txp->buffer_size--; - } - s->txp->fifo_used++; - } - if (s->txp->buffer_size <= 0 && s->txp->pad == 0) { - if (s->txp->cmd_a & 0x1000) { - do_tx_packet(s); - } - if (s->txp->cmd_a & 0x80000000) { - s->int_sts |= TX_IOC_INT; - } - s->txp->state = TX_IDLE; - } - break; - } -} - -static uint32_t do_phy_read(lan9118_state *s, int reg) -{ - uint32_t val; - - switch (reg) { - case 0: /* Basic Control */ - return s->phy_control; - case 1: /* Basic Status */ - return s->phy_status; - case 2: /* ID1 */ - return 0x0007; - case 3: /* ID2 */ - return 0xc0d1; - case 4: /* Auto-neg advertisement */ - return s->phy_advertise; - case 5: /* Auto-neg Link Partner Ability */ - return 0x0f71; - case 6: /* Auto-neg Expansion */ - return 1; - /* TODO 17, 18, 27, 29, 30, 31 */ - case 29: /* Interrupt source. */ - val = s->phy_int; - s->phy_int = 0; - phy_update_irq(s); - return val; - case 30: /* Interrupt mask */ - return s->phy_int_mask; - default: - BADF("PHY read reg %d\n", reg); - return 0; - } -} - -static void do_phy_write(lan9118_state *s, int reg, uint32_t val) -{ - switch (reg) { - case 0: /* Basic Control */ - if (val & 0x8000) { - phy_reset(s); - break; - } - 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; - /* TODO 17, 18, 27, 31 */ - case 30: /* Interrupt mask */ - s->phy_int_mask = val & 0xff; - phy_update_irq(s); - break; - default: - BADF("PHY write reg %d = 0x%04x\n", reg, val); - } -} - -static void do_mac_write(lan9118_state *s, int reg, uint32_t val) -{ - switch (reg) { - case MAC_CR: - if ((s->mac_cr & MAC_CR_RXEN) != 0 && (val & MAC_CR_RXEN) == 0) { - s->int_sts |= RXSTOP_INT; - } - s->mac_cr = val & ~MAC_CR_RESERVED; - DPRINTF("MAC_CR: %08x\n", val); - break; - case MAC_ADDRH: - s->conf.macaddr.a[4] = val & 0xff; - s->conf.macaddr.a[5] = (val >> 8) & 0xff; - lan9118_mac_changed(s); - break; - case MAC_ADDRL: - s->conf.macaddr.a[0] = val & 0xff; - s->conf.macaddr.a[1] = (val >> 8) & 0xff; - s->conf.macaddr.a[2] = (val >> 16) & 0xff; - s->conf.macaddr.a[3] = (val >> 24) & 0xff; - lan9118_mac_changed(s); - break; - case MAC_HASHH: - s->mac_hashh = val; - break; - case MAC_HASHL: - s->mac_hashl = val; - break; - case MAC_MII_ACC: - s->mac_mii_acc = val & 0xffc2; - if (val & 2) { - DPRINTF("PHY write %d = 0x%04x\n", - (val >> 6) & 0x1f, s->mac_mii_data); - do_phy_write(s, (val >> 6) & 0x1f, s->mac_mii_data); - } else { - s->mac_mii_data = do_phy_read(s, (val >> 6) & 0x1f); - DPRINTF("PHY read %d = 0x%04x\n", - (val >> 6) & 0x1f, s->mac_mii_data); - } - break; - case MAC_MII_DATA: - s->mac_mii_data = val & 0xffff; - break; - case MAC_FLOW: - s->mac_flow = val & 0xffff0000; - break; - case MAC_VLAN1: - /* Writing to this register changes a condition for - * FrameTooLong bit in rx_status. Since we do not set - * FrameTooLong anyway, just ignore write to this. - */ - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "lan9118: Unimplemented MAC register write: %d = 0x%x\n", - s->mac_cmd & 0xf, val); - } -} - -static uint32_t do_mac_read(lan9118_state *s, int reg) -{ - switch (reg) { - case MAC_CR: - return s->mac_cr; - case MAC_ADDRH: - return s->conf.macaddr.a[4] | (s->conf.macaddr.a[5] << 8); - case MAC_ADDRL: - return s->conf.macaddr.a[0] | (s->conf.macaddr.a[1] << 8) - | (s->conf.macaddr.a[2] << 16) | (s->conf.macaddr.a[3] << 24); - case MAC_HASHH: - return s->mac_hashh; - break; - case MAC_HASHL: - return s->mac_hashl; - break; - case MAC_MII_ACC: - return s->mac_mii_acc; - case MAC_MII_DATA: - return s->mac_mii_data; - case MAC_FLOW: - return s->mac_flow; - default: - 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 & E2P_CMD_MAC_ADDR_LOADED) | (cmd << 28) | addr; - switch (cmd) { - case 0: - s->e2p_data = s->eeprom[addr]; - DPRINTF("EEPROM Read %d = 0x%02x\n", addr, s->e2p_data); - break; - case 1: - s->eeprom_writable = 0; - DPRINTF("EEPROM Write Disable\n"); - break; - case 2: /* EWEN */ - s->eeprom_writable = 1; - DPRINTF("EEPROM Write Enable\n"); - break; - case 3: /* WRITE */ - if (s->eeprom_writable) { - s->eeprom[addr] &= s->e2p_data; - DPRINTF("EEPROM Write %d = 0x%02x\n", addr, s->e2p_data); - } else { - DPRINTF("EEPROM Write %d (ignored)\n", addr); - } - break; - case 4: /* WRAL */ - if (s->eeprom_writable) { - for (addr = 0; addr < 128; addr++) { - s->eeprom[addr] &= s->e2p_data; - } - DPRINTF("EEPROM Write All 0x%02x\n", s->e2p_data); - } else { - DPRINTF("EEPROM Write All (ignored)\n"); - } - break; - case 5: /* ERASE */ - if (s->eeprom_writable) { - s->eeprom[addr] = 0xff; - DPRINTF("EEPROM Erase %d\n", addr); - } else { - DPRINTF("EEPROM Erase %d (ignored)\n", addr); - } - break; - case 6: /* ERAL */ - if (s->eeprom_writable) { - memset(s->eeprom, 0xff, 128); - DPRINTF("EEPROM Erase All\n"); - } else { - DPRINTF("EEPROM Erase All (ignored)\n"); - } - break; - case 7: /* RELOAD */ - lan9118_reload_eeprom(s); - break; - } -} - -static void lan9118_tick(void *opaque) -{ - lan9118_state *s = (lan9118_state *)opaque; - if (s->int_en & GPT_INT) { - s->int_sts |= GPT_INT; - } - lan9118_update(s); -} - -static void lan9118_writel(void *opaque, hwaddr offset, - uint64_t val, unsigned size) -{ - lan9118_state *s = (lan9118_state *)opaque; - offset &= 0xff; - - //DPRINTF("Write reg 0x%02x = 0x%08x\n", (int)offset, val); - if (offset >= 0x20 && offset < 0x40) { - /* TX FIFO */ - tx_fifo_push(s, val); - return; - } - switch (offset) { - case CSR_IRQ_CFG: - /* TODO: Implement interrupt deassertion intervals. */ - val &= (IRQ_EN | IRQ_POL | IRQ_TYPE); - s->irq_cfg = (s->irq_cfg & IRQ_INT) | val; - break; - case CSR_INT_STS: - s->int_sts &= ~val; - break; - case CSR_INT_EN: - s->int_en = val & ~RESERVED_INT; - s->int_sts |= val & SW_INT; - break; - case CSR_FIFO_INT: - DPRINTF("FIFO INT levels %08x\n", val); - s->fifo_int = val; - break; - case CSR_RX_CFG: - if (val & 0x8000) { - /* RX_DUMP */ - s->rx_fifo_used = 0; - s->rx_status_fifo_used = 0; - s->rx_packet_size_tail = s->rx_packet_size_head; - s->rx_packet_size[s->rx_packet_size_head] = 0; - } - s->rx_cfg = val & 0xcfff1ff0; - break; - case CSR_TX_CFG: - if (val & 0x8000) { - s->tx_status_fifo_used = 0; - } - if (val & 0x4000) { - s->txp->state = TX_IDLE; - s->txp->fifo_used = 0; - s->txp->cmd_a = 0xffffffff; - } - s->tx_cfg = val & 6; - break; - case CSR_HW_CFG: - if (val & 1) { - /* SRST */ - lan9118_reset(DEVICE(s)); - } else { - s->hw_cfg = (val & 0x003f300) | (s->hw_cfg & 0x4); - } - break; - case CSR_RX_DP_CTRL: - if (val & 0x80000000) { - /* Skip forward to next packet. */ - s->rxp_pad = 0; - s->rxp_offset = 0; - if (s->rxp_size == 0) { - /* Pop a word to start the next packet. */ - rx_fifo_pop(s); - s->rxp_pad = 0; - s->rxp_offset = 0; - } - s->rx_fifo_head += s->rxp_size; - if (s->rx_fifo_head >= s->rx_fifo_size) { - s->rx_fifo_head -= s->rx_fifo_size; - } - } - break; - case CSR_PMT_CTRL: - if (val & 0x400) { - phy_reset(s); - } - s->pmt_ctrl &= ~0x34e; - s->pmt_ctrl |= (val & 0x34e); - break; - case CSR_GPIO_CFG: - /* Probably just enabling LEDs. */ - s->gpio_cfg = val & 0x7777071f; - break; - case CSR_GPT_CFG: - if ((s->gpt_cfg ^ val) & GPT_TIMER_EN) { - if (val & GPT_TIMER_EN) { - ptimer_set_count(s->timer, val & 0xffff); - ptimer_run(s->timer, 0); - } else { - ptimer_stop(s->timer); - ptimer_set_count(s->timer, 0xffff); - } - } - s->gpt_cfg = val & (GPT_TIMER_EN | 0xffff); - break; - case CSR_WORD_SWAP: - /* Ignored because we're in 32-bit mode. */ - s->word_swap = val; - break; - case CSR_MAC_CSR_CMD: - s->mac_cmd = val & 0x4000000f; - if (val & 0x80000000) { - if (val & 0x40000000) { - s->mac_data = do_mac_read(s, val & 0xf); - DPRINTF("MAC read %d = 0x%08x\n", val & 0xf, s->mac_data); - } else { - DPRINTF("MAC write %d = 0x%08x\n", val & 0xf, s->mac_data); - do_mac_write(s, val & 0xf, s->mac_data); - } - } - break; - case CSR_MAC_CSR_DATA: - s->mac_data = val; - break; - case CSR_AFC_CFG: - s->afc_cfg = val & 0x00ffffff; - break; - case CSR_E2P_CMD: - lan9118_eeprom_cmd(s, (val >> 28) & 7, val & 0x7f); - break; - case CSR_E2P_DATA: - s->e2p_data = val & 0xff; - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, "lan9118_write: Bad reg 0x%x = %x\n", - (int)offset, (int)val); - break; - } - lan9118_update(s); -} - -static void lan9118_writew(void *opaque, hwaddr offset, - uint32_t val) -{ - lan9118_state *s = (lan9118_state *)opaque; - offset &= 0xff; - - if (s->write_word_prev_offset != (offset & ~0x3)) { - /* New offset, reset word counter */ - s->write_word_n = 0; - s->write_word_prev_offset = offset & ~0x3; - } - - if (offset & 0x2) { - s->write_word_h = val; - } else { - s->write_word_l = val; - } - - //DPRINTF("Writew reg 0x%02x = 0x%08x\n", (int)offset, val); - s->write_word_n++; - if (s->write_word_n == 2) { - s->write_word_n = 0; - lan9118_writel(s, offset & ~3, s->write_word_l + - (s->write_word_h << 16), 4); - } -} - -static void lan9118_16bit_mode_write(void *opaque, hwaddr offset, - uint64_t val, unsigned size) -{ - switch (size) { - case 2: - lan9118_writew(opaque, offset, (uint32_t)val); - return; - case 4: - lan9118_writel(opaque, offset, val, size); - return; - } - - hw_error("lan9118_write: Bad size 0x%x\n", size); -} - -static uint64_t lan9118_readl(void *opaque, hwaddr offset, - unsigned size) -{ - lan9118_state *s = (lan9118_state *)opaque; - - //DPRINTF("Read reg 0x%02x\n", (int)offset); - if (offset < 0x20) { - /* RX FIFO */ - return rx_fifo_pop(s); - } - switch (offset) { - case 0x40: - return rx_status_fifo_pop(s); - case 0x44: - return s->rx_status_fifo[s->tx_status_fifo_head]; - case 0x48: - return tx_status_fifo_pop(s); - case 0x4c: - return s->tx_status_fifo[s->tx_status_fifo_head]; - case CSR_ID_REV: - return 0x01180001; - case CSR_IRQ_CFG: - return s->irq_cfg; - case CSR_INT_STS: - return s->int_sts; - case CSR_INT_EN: - return s->int_en; - case CSR_BYTE_TEST: - return 0x87654321; - case CSR_FIFO_INT: - return s->fifo_int; - case CSR_RX_CFG: - return s->rx_cfg; - case CSR_TX_CFG: - return s->tx_cfg; - case CSR_HW_CFG: - return s->hw_cfg; - case CSR_RX_DP_CTRL: - return 0; - case CSR_RX_FIFO_INF: - return (s->rx_status_fifo_used << 16) | (s->rx_fifo_used << 2); - case CSR_TX_FIFO_INF: - return (s->tx_status_fifo_used << 16) - | (s->tx_fifo_size - s->txp->fifo_used); - case CSR_PMT_CTRL: - return s->pmt_ctrl; - case CSR_GPIO_CFG: - return s->gpio_cfg; - case CSR_GPT_CFG: - return s->gpt_cfg; - case CSR_GPT_CNT: - return ptimer_get_count(s->timer); - case CSR_WORD_SWAP: - return s->word_swap; - case CSR_FREE_RUN: - return (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 40) - s->free_timer_start; - case CSR_RX_DROP: - /* TODO: Implement dropped frames counter. */ - return 0; - case CSR_MAC_CSR_CMD: - return s->mac_cmd; - case CSR_MAC_CSR_DATA: - return s->mac_data; - case CSR_AFC_CFG: - return s->afc_cfg; - case CSR_E2P_CMD: - return s->e2p_cmd; - case CSR_E2P_DATA: - return s->e2p_data; - } - qemu_log_mask(LOG_GUEST_ERROR, "lan9118_read: Bad reg 0x%x\n", (int)offset); - return 0; -} - -static uint32_t lan9118_readw(void *opaque, hwaddr offset) -{ - lan9118_state *s = (lan9118_state *)opaque; - uint32_t val; - - if (s->read_word_prev_offset != (offset & ~0x3)) { - /* New offset, reset word counter */ - s->read_word_n = 0; - s->read_word_prev_offset = offset & ~0x3; - } - - s->read_word_n++; - if (s->read_word_n == 1) { - s->read_long = lan9118_readl(s, offset & ~3, 4); - } else { - s->read_word_n = 0; - } - - if (offset & 2) { - val = s->read_long >> 16; - } else { - val = s->read_long & 0xFFFF; - } - - //DPRINTF("Readw reg 0x%02x, val 0x%x\n", (int)offset, val); - return val; -} - -static uint64_t lan9118_16bit_mode_read(void *opaque, hwaddr offset, - unsigned size) -{ - switch (size) { - case 2: - return lan9118_readw(opaque, offset); - case 4: - return lan9118_readl(opaque, offset, size); - } - - hw_error("lan9118_read: Bad size 0x%x\n", size); - return 0; -} - -static const MemoryRegionOps lan9118_mem_ops = { - .read = lan9118_readl, - .write = lan9118_writel, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const MemoryRegionOps lan9118_16bit_mem_ops = { - .read = lan9118_16bit_mode_read, - .write = lan9118_16bit_mode_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static NetClientInfo net_lan9118_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = lan9118_receive, - .link_status_changed = lan9118_set_link, -}; - -static int lan9118_init1(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - lan9118_state *s = LAN9118(dev); - QEMUBH *bh; - int i; - const MemoryRegionOps *mem_ops = - s->mode_16bit ? &lan9118_16bit_mem_ops : &lan9118_mem_ops; - - memory_region_init_io(&s->mmio, OBJECT(dev), mem_ops, s, - "lan9118-mmio", 0x100); - sysbus_init_mmio(sbd, &s->mmio); - sysbus_init_irq(sbd, &s->irq); - qemu_macaddr_default_if_unset(&s->conf.macaddr); - - s->nic = qemu_new_nic(&net_lan9118_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - s->eeprom[0] = 0xa5; - for (i = 0; i < 6; i++) { - s->eeprom[i + 1] = s->conf.macaddr.a[i]; - } - s->pmt_ctrl = 1; - s->txp = &s->tx_packet; - - bh = qemu_bh_new(lan9118_tick, s); - s->timer = ptimer_init(bh); - ptimer_set_freq(s->timer, 10000); - ptimer_set_limit(s->timer, 0xffff, 1); - - return 0; -} - -static Property lan9118_properties[] = { - DEFINE_NIC_PROPERTIES(lan9118_state, conf), - DEFINE_PROP_UINT32("mode_16bit", lan9118_state, mode_16bit, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void lan9118_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = lan9118_init1; - dc->reset = lan9118_reset; - dc->props = lan9118_properties; - dc->vmsd = &vmstate_lan9118; -} - -static const TypeInfo lan9118_info = { - .name = TYPE_LAN9118, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(lan9118_state), - .class_init = lan9118_class_init, -}; - -static void lan9118_register_types(void) -{ - type_register_static(&lan9118_info); -} - -/* Legacy helper function. Should go away when machine config files are - implemented. */ -void lan9118_init(NICInfo *nd, uint32_t base, qemu_irq irq) -{ - DeviceState *dev; - SysBusDevice *s; - - qemu_check_nic_model(nd, "lan9118"); - dev = qdev_create(NULL, TYPE_LAN9118); - qdev_set_nic_properties(dev, nd); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(s, 0, base); - sysbus_connect_irq(s, 0, irq); -} - -type_init(lan9118_register_types) diff --git a/qemu/hw/net/lance.c b/qemu/hw/net/lance.c deleted file mode 100644 index 6253d2103..000000000 --- a/qemu/hw/net/lance.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * QEMU AMD PC-Net II (Am79C970A) emulation - * - * Copyright (c) 2004 Antony T Curtis - * - * 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 software was written to be compatible with the specification: - * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet - * AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000 - */ - -/* - * On Sparc32, this is the Lance (Am7990) part of chip STP2000 (Master I/O), also - * produced as NCR89C100. See - * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt - * and - * 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" -#include "qemu/sockets.h" -#include "hw/sparc/sun4m.h" -#include "pcnet.h" -#include "trace.h" -#include "sysemu/sysemu.h" - -#define TYPE_LANCE "lance" -#define SYSBUS_PCNET(obj) \ - OBJECT_CHECK(SysBusPCNetState, (obj), TYPE_LANCE) - -typedef struct { - SysBusDevice parent_obj; - - PCNetState state; -} SysBusPCNetState; - -static void parent_lance_reset(void *opaque, int irq, int level) -{ - SysBusPCNetState *d = opaque; - if (level) - pcnet_h_reset(&d->state); -} - -static void lance_mem_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - SysBusPCNetState *d = opaque; - - trace_lance_mem_writew(addr, val & 0xffff); - pcnet_ioport_writew(&d->state, addr, val & 0xffff); -} - -static uint64_t lance_mem_read(void *opaque, hwaddr addr, - unsigned size) -{ - SysBusPCNetState *d = opaque; - uint32_t val; - - val = pcnet_ioport_readw(&d->state, addr); - trace_lance_mem_readw(addr, val & 0xffff); - return val & 0xffff; -} - -static const MemoryRegionOps lance_mem_ops = { - .read = lance_mem_read, - .write = lance_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 2, - .max_access_size = 2, - }, -}; - -static NetClientInfo net_lance_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = pcnet_receive, - .link_status_changed = pcnet_set_link_status, -}; - -static const VMStateDescription vmstate_lance = { - .name = "pcnet", - .version_id = 3, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(state, SysBusPCNetState, 0, vmstate_pcnet, PCNetState), - VMSTATE_END_OF_LIST() - } -}; - -static int lance_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - SysBusPCNetState *d = SYSBUS_PCNET(dev); - PCNetState *s = &d->state; - - memory_region_init_io(&s->mmio, OBJECT(d), &lance_mem_ops, d, - "lance-mmio", 4); - - qdev_init_gpio_in(dev, parent_lance_reset, 1); - - sysbus_init_mmio(sbd, &s->mmio); - - sysbus_init_irq(sbd, &s->irq); - - s->phys_mem_read = ledma_memory_read; - s->phys_mem_write = ledma_memory_write; - pcnet_common_init(dev, s, &net_lance_info); - return 0; -} - -static void lance_reset(DeviceState *dev) -{ - SysBusPCNetState *d = SYSBUS_PCNET(dev); - - pcnet_h_reset(&d->state); -} - -static void lance_instance_init(Object *obj) -{ - SysBusPCNetState *d = SYSBUS_PCNET(obj); - PCNetState *s = &d->state; - - device_add_bootindex_property(obj, &s->conf.bootindex, - "bootindex", "/ethernet-phy@0", - DEVICE(obj), NULL); -} - -static Property lance_properties[] = { - DEFINE_PROP_PTR("dma", SysBusPCNetState, state.dma_opaque), - DEFINE_NIC_PROPERTIES(SysBusPCNetState, state.conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void lance_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = lance_init; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->fw_name = "ethernet"; - dc->reset = lance_reset; - dc->vmsd = &vmstate_lance; - dc->props = lance_properties; - /* Reason: pointer property "dma" */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo lance_info = { - .name = TYPE_LANCE, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysBusPCNetState), - .class_init = lance_class_init, - .instance_init = lance_instance_init, -}; - -static void lance_register_types(void) -{ - type_register_static(&lance_info); -} - -type_init(lance_register_types) diff --git a/qemu/hw/net/mcf_fec.c b/qemu/hw/net/mcf_fec.c deleted file mode 100644 index 7c0398ed9..000000000 --- a/qemu/hw/net/mcf_fec.c +++ /dev/null @@ -1,535 +0,0 @@ -/* - * ColdFire Fast Ethernet Controller emulation. - * - * Copyright (c) 2007 CodeSourcery. - * - * This code is licensed under the GPL - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "net/net.h" -#include "hw/m68k/mcf.h" -#include "hw/net/mii.h" -/* For crc32 */ -#include -#include "exec/address-spaces.h" - -//#define DEBUG_FEC 1 - -#ifdef DEBUG_FEC -#define DPRINTF(fmt, ...) \ -do { printf("mcf_fec: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - -#define FEC_MAX_FRAME_SIZE 2032 - -typedef struct { - MemoryRegion *sysmem; - MemoryRegion iomem; - qemu_irq *irq; - NICState *nic; - NICConf conf; - uint32_t irq_state; - uint32_t eir; - uint32_t eimr; - int rx_enabled; - uint32_t rx_descriptor; - uint32_t tx_descriptor; - uint32_t ecr; - uint32_t mmfr; - uint32_t mscr; - uint32_t rcr; - uint32_t tcr; - uint32_t tfwr; - uint32_t rfsr; - uint32_t erdsr; - uint32_t etdsr; - uint32_t emrbr; -} mcf_fec_state; - -#define FEC_INT_HB 0x80000000 -#define FEC_INT_BABR 0x40000000 -#define FEC_INT_BABT 0x20000000 -#define FEC_INT_GRA 0x10000000 -#define FEC_INT_TXF 0x08000000 -#define FEC_INT_TXB 0x04000000 -#define FEC_INT_RXF 0x02000000 -#define FEC_INT_RXB 0x01000000 -#define FEC_INT_MII 0x00800000 -#define FEC_INT_EB 0x00400000 -#define FEC_INT_LC 0x00200000 -#define FEC_INT_RL 0x00100000 -#define FEC_INT_UN 0x00080000 - -#define FEC_EN 2 -#define FEC_RESET 1 - -/* Map interrupt flags onto IRQ lines. */ -#define FEC_NUM_IRQ 13 -static const uint32_t mcf_fec_irq_map[FEC_NUM_IRQ] = { - FEC_INT_TXF, - FEC_INT_TXB, - FEC_INT_UN, - FEC_INT_RL, - FEC_INT_RXF, - FEC_INT_RXB, - FEC_INT_MII, - FEC_INT_LC, - FEC_INT_HB, - FEC_INT_GRA, - FEC_INT_EB, - FEC_INT_BABT, - FEC_INT_BABR -}; - -/* Buffer Descriptor. */ -typedef struct { - uint16_t flags; - uint16_t length; - uint32_t data; -} mcf_fec_bd; - -#define FEC_BD_R 0x8000 -#define FEC_BD_E 0x8000 -#define FEC_BD_O1 0x4000 -#define FEC_BD_W 0x2000 -#define FEC_BD_O2 0x1000 -#define FEC_BD_L 0x0800 -#define FEC_BD_TC 0x0400 -#define FEC_BD_ABC 0x0200 -#define FEC_BD_M 0x0100 -#define FEC_BD_BC 0x0080 -#define FEC_BD_MC 0x0040 -#define FEC_BD_LG 0x0020 -#define FEC_BD_NO 0x0010 -#define FEC_BD_CR 0x0004 -#define FEC_BD_OV 0x0002 -#define FEC_BD_TR 0x0001 - -static void mcf_fec_read_bd(mcf_fec_bd *bd, uint32_t addr) -{ - cpu_physical_memory_read(addr, bd, sizeof(*bd)); - be16_to_cpus(&bd->flags); - be16_to_cpus(&bd->length); - be32_to_cpus(&bd->data); -} - -static void mcf_fec_write_bd(mcf_fec_bd *bd, uint32_t addr) -{ - mcf_fec_bd tmp; - tmp.flags = cpu_to_be16(bd->flags); - tmp.length = cpu_to_be16(bd->length); - tmp.data = cpu_to_be32(bd->data); - cpu_physical_memory_write(addr, &tmp, sizeof(tmp)); -} - -static void mcf_fec_update(mcf_fec_state *s) -{ - uint32_t active; - uint32_t changed; - uint32_t mask; - int i; - - active = s->eir & s->eimr; - changed = active ^s->irq_state; - for (i = 0; i < FEC_NUM_IRQ; i++) { - mask = mcf_fec_irq_map[i]; - if (changed & mask) { - DPRINTF("IRQ %d = %d\n", i, (active & mask) != 0); - qemu_set_irq(s->irq[i], (active & mask) != 0); - } - } - s->irq_state = active; -} - -static void mcf_fec_do_tx(mcf_fec_state *s) -{ - uint32_t addr; - mcf_fec_bd bd; - int frame_size; - int len; - uint8_t frame[FEC_MAX_FRAME_SIZE]; - uint8_t *ptr; - - DPRINTF("do_tx\n"); - ptr = frame; - frame_size = 0; - addr = s->tx_descriptor; - while (1) { - mcf_fec_read_bd(&bd, addr); - DPRINTF("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; - } - cpu_physical_memory_read(bd.data, ptr, len); - ptr += len; - frame_size += len; - if (bd.flags & FEC_BD_L) { - /* Last buffer in frame. */ - DPRINTF("Sending packet\n"); - 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. */ - mcf_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; -} - -static void mcf_fec_enable_rx(mcf_fec_state *s) -{ - NetClientState *nc = qemu_get_queue(s->nic); - mcf_fec_bd bd; - - mcf_fec_read_bd(&bd, s->rx_descriptor); - s->rx_enabled = ((bd.flags & FEC_BD_E) != 0); - if (s->rx_enabled) { - qemu_flush_queued_packets(nc); - } -} - -static void mcf_fec_reset(mcf_fec_state *s) -{ - s->eir = 0; - s->eimr = 0; - s->rx_enabled = 0; - s->ecr = 0; - s->mscr = 0; - s->rcr = 0x05ee0001; - s->tcr = 0; - s->tfwr = 0; - s->rfsr = 0x500; -} - -#define MMFR_WRITE_OP (1 << 28) -#define MMFR_READ_OP (2 << 28) -#define MMFR_PHYADDR(v) (((v) >> 23) & 0x1f) -#define MMFR_REGNUM(v) (((v) >> 18) & 0x1f) - -static uint64_t mcf_fec_read_mdio(mcf_fec_state *s) -{ - uint64_t v; - - if (s->mmfr & MMFR_WRITE_OP) - return s->mmfr; - if (MMFR_PHYADDR(s->mmfr) != 1) - return s->mmfr |= 0xffff; - - switch (MMFR_REGNUM(s->mmfr)) { - case MII_BMCR: - v = MII_BMCR_SPEED | MII_BMCR_AUTOEN | MII_BMCR_FD; - break; - case MII_BMSR: - v = MII_BMSR_100TX_FD | MII_BMSR_100TX_HD | MII_BMSR_10T_FD | - MII_BMSR_10T_HD | MII_BMSR_MFPS | MII_BMSR_AN_COMP | - MII_BMSR_AUTONEG | MII_BMSR_LINK_ST; - break; - case MII_PHYID1: - v = DP83848_PHYID1; - break; - case MII_PHYID2: - v = DP83848_PHYID2; - break; - case MII_ANAR: - v = MII_ANAR_TXFD | MII_ANAR_TX | MII_ANAR_10FD | - MII_ANAR_10 | MII_ANAR_CSMACD; - break; - case MII_ANLPAR: - v = MII_ANLPAR_ACK | MII_ANLPAR_TXFD | MII_ANLPAR_TX | - MII_ANLPAR_10FD | MII_ANLPAR_10 | MII_ANLPAR_CSMACD; - break; - default: - v = 0xffff; - break; - } - s->mmfr = (s->mmfr & ~0xffff) | v; - return s->mmfr; -} - -static uint64_t mcf_fec_read(void *opaque, hwaddr addr, - unsigned size) -{ - mcf_fec_state *s = (mcf_fec_state *)opaque; - 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 mcf_fec_read_mdio(s); - case 0x044: return s->mscr; - case 0x064: return 0; /* 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->rfsr; - case 0x180: return s->erdsr; - case 0x184: return s->etdsr; - case 0x188: return s->emrbr; - default: - hw_error("mcf_fec_read: Bad address 0x%x\n", (int)addr); - return 0; - } -} - -static void mcf_fec_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - mcf_fec_state *s = (mcf_fec_state *)opaque; - switch (addr & 0x3ff) { - case 0x004: - s->eir &= ~value; - break; - case 0x008: - s->eimr = value; - break; - case 0x010: /* RDAR */ - if ((s->ecr & FEC_EN) && !s->rx_enabled) { - DPRINTF("RX enable\n"); - mcf_fec_enable_rx(s); - } - break; - case 0x014: /* TDAR */ - if (s->ecr & FEC_EN) { - mcf_fec_do_tx(s); - } - break; - case 0x024: - s->ecr = value; - if (value & FEC_RESET) { - DPRINTF("Reset\n"); - mcf_fec_reset(s); - } - if ((s->ecr & FEC_EN) == 0) { - s->rx_enabled = 0; - } - break; - case 0x040: - s->mmfr = value; - s->eir |= FEC_INT_MII; - break; - case 0x044: - s->mscr = value & 0xfe; - break; - case 0x064: - /* TODO: Implement MIB. */ - break; - case 0x084: - 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: - /* OPD */ - break; - case 0x118: - case 0x11c: - case 0x120: - case 0x124: - /* TODO: implement MAC hash filtering. */ - break; - case 0x144: - s->tfwr = value & 3; - break; - case 0x14c: - /* FRBR writes ignored. */ - break; - case 0x150: - s->rfsr = (value & 0x3fc) | 0x400; - break; - case 0x180: - s->erdsr = value & ~3; - s->rx_descriptor = s->erdsr; - break; - case 0x184: - s->etdsr = value & ~3; - s->tx_descriptor = s->etdsr; - break; - case 0x188: - s->emrbr = value & 0x7f0; - break; - default: - hw_error("mcf_fec_write Bad address 0x%x\n", (int)addr); - } - mcf_fec_update(s); -} - -static int mcf_fec_have_receive_space(mcf_fec_state *s, size_t want) -{ - mcf_fec_bd bd; - uint32_t addr; - - /* Walk descriptor list to determine if we have enough buffer */ - addr = s->rx_descriptor; - while (want > 0) { - mcf_fec_read_bd(&bd, addr); - if ((bd.flags & FEC_BD_E) == 0) { - return 0; - } - if (want < s->emrbr) { - return 1; - } - want -= s->emrbr; - /* Advance to the next descriptor. */ - if ((bd.flags & FEC_BD_W) != 0) { - addr = s->erdsr; - } else { - addr += 8; - } - } - return 0; -} - -static ssize_t mcf_fec_receive(NetClientState *nc, const uint8_t *buf, size_t size) -{ - mcf_fec_state *s = qemu_get_nic_opaque(nc); - mcf_fec_bd 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 retsize; - - DPRINTF("do_rx len %d\n", size); - if (!s->rx_enabled) { - return -1; - } - /* 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; - } - /* Check if we have enough space in current descriptors */ - if (!mcf_fec_have_receive_space(s, size)) { - return 0; - } - addr = s->rx_descriptor; - retsize = size; - while (size > 0) { - mcf_fec_read_bd(&bd, addr); - buf_len = (size <= s->emrbr) ? size: s->emrbr; - bd.length = buf_len; - size -= buf_len; - DPRINTF("rx_bd %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; - cpu_physical_memory_write(buf_addr, buf, buf_len); - buf += buf_len; - if (size < 4) { - cpu_physical_memory_write(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; - DPRINTF("rx frame flags %04x\n", bd.flags); - s->eir |= FEC_INT_RXF; - } else { - s->eir |= FEC_INT_RXB; - } - mcf_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; - mcf_fec_enable_rx(s); - mcf_fec_update(s); - return retsize; -} - -static const MemoryRegionOps mcf_fec_ops = { - .read = mcf_fec_read, - .write = mcf_fec_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static NetClientInfo net_mcf_fec_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = mcf_fec_receive, -}; - -void mcf_fec_init(MemoryRegion *sysmem, NICInfo *nd, - hwaddr base, qemu_irq *irq) -{ - mcf_fec_state *s; - - qemu_check_nic_model(nd, "mcf_fec"); - - s = (mcf_fec_state *)g_malloc0(sizeof(mcf_fec_state)); - s->sysmem = sysmem; - s->irq = irq; - - memory_region_init_io(&s->iomem, NULL, &mcf_fec_ops, s, "fec", 0x400); - memory_region_add_subregion(sysmem, base, &s->iomem); - - s->conf.macaddr = nd->macaddr; - s->conf.peers.ncs[0] = nd->netdev; - - s->nic = qemu_new_nic(&net_mcf_fec_info, &s->conf, nd->model, nd->name, s); - - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); -} diff --git a/qemu/hw/net/milkymist-minimac2.c b/qemu/hw/net/milkymist-minimac2.c deleted file mode 100644 index 1e147c33c..000000000 --- a/qemu/hw/net/milkymist-minimac2.c +++ /dev/null @@ -1,544 +0,0 @@ -/* - * QEMU model of the Milkymist minimac2 block. - * - * Copyright (c) 2011 Michael Walle - * - * 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 . - * - * - * Specification available at: - * not available yet - * - */ - -#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" -#include "net/net.h" -#include "qemu/error-report.h" - -#include - -enum { - R_SETUP = 0, - R_MDIO, - R_STATE0, - R_COUNT0, - R_STATE1, - R_COUNT1, - R_TXCOUNT, - R_MAX -}; - -enum { - SETUP_PHY_RST = (1<<0), -}; - -enum { - MDIO_DO = (1<<0), - MDIO_DI = (1<<1), - MDIO_OE = (1<<2), - MDIO_CLK = (1<<3), -}; - -enum { - STATE_EMPTY = 0, - STATE_LOADED = 1, - STATE_PENDING = 2, -}; - -enum { - MDIO_OP_WRITE = 1, - MDIO_OP_READ = 2, -}; - -enum mdio_state { - MDIO_STATE_IDLE, - MDIO_STATE_READING, - MDIO_STATE_WRITING, -}; - -enum { - R_PHY_ID1 = 2, - R_PHY_ID2 = 3, - R_PHY_MAX = 32 -}; - -#define MINIMAC2_MTU 1530 -#define MINIMAC2_BUFFER_SIZE 2048 - -struct MilkymistMinimac2MdioState { - int last_clk; - int count; - uint32_t data; - uint16_t data_out; - int state; - - uint8_t phy_addr; - uint8_t reg_addr; -}; -typedef struct MilkymistMinimac2MdioState MilkymistMinimac2MdioState; - -#define TYPE_MILKYMIST_MINIMAC2 "milkymist-minimac2" -#define MILKYMIST_MINIMAC2(obj) \ - OBJECT_CHECK(MilkymistMinimac2State, (obj), TYPE_MILKYMIST_MINIMAC2) - -struct MilkymistMinimac2State { - SysBusDevice parent_obj; - - NICState *nic; - NICConf conf; - char *phy_model; - MemoryRegion buffers; - MemoryRegion regs_region; - - qemu_irq rx_irq; - qemu_irq tx_irq; - - uint32_t regs[R_MAX]; - - MilkymistMinimac2MdioState mdio; - - uint16_t phy_regs[R_PHY_MAX]; - - uint8_t *rx0_buf; - uint8_t *rx1_buf; - uint8_t *tx_buf; -}; -typedef struct MilkymistMinimac2State MilkymistMinimac2State; - -static const uint8_t preamble_sfd[] = { - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5 -}; - -static void minimac2_mdio_write_reg(MilkymistMinimac2State *s, - uint8_t phy_addr, uint8_t reg_addr, uint16_t value) -{ - trace_milkymist_minimac2_mdio_write(phy_addr, reg_addr, value); - - /* nop */ -} - -static uint16_t minimac2_mdio_read_reg(MilkymistMinimac2State *s, - uint8_t phy_addr, uint8_t reg_addr) -{ - uint16_t r = s->phy_regs[reg_addr]; - - trace_milkymist_minimac2_mdio_read(phy_addr, reg_addr, r); - - return r; -} - -static void minimac2_update_mdio(MilkymistMinimac2State *s) -{ - MilkymistMinimac2MdioState *m = &s->mdio; - - /* detect rising clk edge */ - if (m->last_clk == 0 && (s->regs[R_MDIO] & MDIO_CLK)) { - /* shift data in */ - int bit = ((s->regs[R_MDIO] & MDIO_DO) - && (s->regs[R_MDIO] & MDIO_OE)) ? 1 : 0; - m->data = (m->data << 1) | bit; - - /* check for sync */ - if (m->data == 0xffffffff) { - m->count = 32; - } - - if (m->count == 16) { - uint8_t start = (m->data >> 14) & 0x3; - uint8_t op = (m->data >> 12) & 0x3; - uint8_t ta = (m->data) & 0x3; - - if (start == 1 && op == MDIO_OP_WRITE && ta == 2) { - m->state = MDIO_STATE_WRITING; - } else if (start == 1 && op == MDIO_OP_READ && (ta & 1) == 0) { - m->state = MDIO_STATE_READING; - } else { - m->state = MDIO_STATE_IDLE; - } - - if (m->state != MDIO_STATE_IDLE) { - m->phy_addr = (m->data >> 7) & 0x1f; - m->reg_addr = (m->data >> 2) & 0x1f; - } - - if (m->state == MDIO_STATE_READING) { - m->data_out = minimac2_mdio_read_reg(s, m->phy_addr, - m->reg_addr); - } - } - - if (m->count < 16 && m->state == MDIO_STATE_READING) { - int bit = (m->data_out & 0x8000) ? 1 : 0; - m->data_out <<= 1; - - if (bit) { - s->regs[R_MDIO] |= MDIO_DI; - } else { - s->regs[R_MDIO] &= ~MDIO_DI; - } - } - - if (m->count == 0 && m->state) { - if (m->state == MDIO_STATE_WRITING) { - uint16_t data = m->data & 0xffff; - minimac2_mdio_write_reg(s, m->phy_addr, m->reg_addr, data); - } - m->state = MDIO_STATE_IDLE; - } - m->count--; - } - - m->last_clk = (s->regs[R_MDIO] & MDIO_CLK) ? 1 : 0; -} - -static size_t assemble_frame(uint8_t *buf, size_t size, - const uint8_t *payload, size_t payload_size) -{ - uint32_t crc; - - if (size < payload_size + 12) { - error_report("milkymist_minimac2: received too big ethernet frame"); - return 0; - } - - /* prepend preamble and sfd */ - memcpy(buf, preamble_sfd, 8); - - /* now copy the payload */ - memcpy(buf + 8, payload, payload_size); - - /* pad frame if needed */ - if (payload_size < 60) { - memset(buf + payload_size + 8, 0, 60 - payload_size); - payload_size = 60; - } - - /* append fcs */ - crc = cpu_to_le32(crc32(0, buf + 8, payload_size)); - memcpy(buf + payload_size + 8, &crc, 4); - - return payload_size + 12; -} - -static void minimac2_tx(MilkymistMinimac2State *s) -{ - uint32_t txcount = s->regs[R_TXCOUNT]; - uint8_t *buf = s->tx_buf; - - if (txcount < 64) { - error_report("milkymist_minimac2: ethernet frame too small (%u < %u)", - txcount, 64); - goto err; - } - - if (txcount > MINIMAC2_MTU) { - error_report("milkymist_minimac2: MTU exceeded (%u > %u)", - txcount, MINIMAC2_MTU); - goto err; - } - - if (memcmp(buf, preamble_sfd, 8) != 0) { - error_report("milkymist_minimac2: frame doesn't contain the preamble " - "and/or the SFD (%02x %02x %02x %02x %02x %02x %02x %02x)", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); - goto err; - } - - trace_milkymist_minimac2_tx_frame(txcount - 12); - - /* send packet, skipping preamble and sfd */ - qemu_send_packet_raw(qemu_get_queue(s->nic), buf + 8, txcount - 12); - - s->regs[R_TXCOUNT] = 0; - -err: - trace_milkymist_minimac2_pulse_irq_tx(); - qemu_irq_pulse(s->tx_irq); -} - -static void update_rx_interrupt(MilkymistMinimac2State *s) -{ - if (s->regs[R_STATE0] == STATE_PENDING - || s->regs[R_STATE1] == STATE_PENDING) { - trace_milkymist_minimac2_raise_irq_rx(); - qemu_irq_raise(s->rx_irq); - } else { - trace_milkymist_minimac2_lower_irq_rx(); - qemu_irq_lower(s->rx_irq); - } -} - -static ssize_t minimac2_rx(NetClientState *nc, const uint8_t *buf, size_t size) -{ - MilkymistMinimac2State *s = qemu_get_nic_opaque(nc); - - uint32_t r_count; - uint32_t r_state; - uint8_t *rx_buf; - - size_t frame_size; - - trace_milkymist_minimac2_rx_frame(buf, size); - - /* choose appropriate slot */ - if (s->regs[R_STATE0] == STATE_LOADED) { - r_count = R_COUNT0; - r_state = R_STATE0; - rx_buf = s->rx0_buf; - } else if (s->regs[R_STATE1] == STATE_LOADED) { - r_count = R_COUNT1; - r_state = R_STATE1; - rx_buf = s->rx1_buf; - } else { - return 0; - } - - /* assemble frame */ - frame_size = assemble_frame(rx_buf, MINIMAC2_BUFFER_SIZE, buf, size); - - if (frame_size == 0) { - return size; - } - - trace_milkymist_minimac2_rx_transfer(rx_buf, frame_size); - - /* update slot */ - s->regs[r_count] = frame_size; - s->regs[r_state] = STATE_PENDING; - - update_rx_interrupt(s); - - return size; -} - -static uint64_t -minimac2_read(void *opaque, hwaddr addr, unsigned size) -{ - MilkymistMinimac2State *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_SETUP: - case R_MDIO: - case R_STATE0: - case R_COUNT0: - case R_STATE1: - case R_COUNT1: - case R_TXCOUNT: - r = s->regs[addr]; - break; - - default: - error_report("milkymist_minimac2: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_milkymist_minimac2_memory_read(addr << 2, r); - - return r; -} - -static int minimac2_can_rx(MilkymistMinimac2State *s) -{ - if (s->regs[R_STATE0] == STATE_LOADED) { - return 1; - } - if (s->regs[R_STATE1] == STATE_LOADED) { - return 1; - } - - return 0; -} - -static void -minimac2_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - MilkymistMinimac2State *s = opaque; - - trace_milkymist_minimac2_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_MDIO: - { - /* MDIO_DI is read only */ - int mdio_di = (s->regs[R_MDIO] & MDIO_DI); - s->regs[R_MDIO] = value; - if (mdio_di) { - s->regs[R_MDIO] |= mdio_di; - } else { - s->regs[R_MDIO] &= ~mdio_di; - } - - minimac2_update_mdio(s); - } break; - case R_TXCOUNT: - s->regs[addr] = value; - if (value > 0) { - minimac2_tx(s); - } - break; - case R_STATE0: - case R_STATE1: - s->regs[addr] = value; - update_rx_interrupt(s); - if (minimac2_can_rx(s)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } - break; - case R_SETUP: - case R_COUNT0: - case R_COUNT1: - s->regs[addr] = value; - break; - - default: - error_report("milkymist_minimac2: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } -} - -static const MemoryRegionOps minimac2_ops = { - .read = minimac2_read, - .write = minimac2_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void milkymist_minimac2_reset(DeviceState *d) -{ - MilkymistMinimac2State *s = MILKYMIST_MINIMAC2(d); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } - for (i = 0; i < R_PHY_MAX; i++) { - s->phy_regs[i] = 0; - } - - /* defaults */ - s->phy_regs[R_PHY_ID1] = 0x0022; /* Micrel KSZ8001L */ - s->phy_regs[R_PHY_ID2] = 0x161a; -} - -static NetClientInfo net_milkymist_minimac2_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = minimac2_rx, -}; - -static int milkymist_minimac2_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - MilkymistMinimac2State *s = MILKYMIST_MINIMAC2(dev); - size_t buffers_size = TARGET_PAGE_ALIGN(3 * MINIMAC2_BUFFER_SIZE); - - sysbus_init_irq(sbd, &s->rx_irq); - sysbus_init_irq(sbd, &s->tx_irq); - - memory_region_init_io(&s->regs_region, OBJECT(dev), &minimac2_ops, s, - "milkymist-minimac2", R_MAX * 4); - sysbus_init_mmio(sbd, &s->regs_region); - - /* register buffers memory */ - memory_region_init_ram(&s->buffers, OBJECT(dev), "milkymist-minimac2.buffers", - 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; - s->tx_buf = s->rx1_buf + MINIMAC2_BUFFER_SIZE; - - sysbus_init_mmio(sbd, &s->buffers); - - qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->nic = qemu_new_nic(&net_milkymist_minimac2_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - return 0; -} - -static const VMStateDescription vmstate_milkymist_minimac2_mdio = { - .name = "milkymist-minimac2-mdio", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32(last_clk, MilkymistMinimac2MdioState), - VMSTATE_INT32(count, MilkymistMinimac2MdioState), - VMSTATE_UINT32(data, MilkymistMinimac2MdioState), - VMSTATE_UINT16(data_out, MilkymistMinimac2MdioState), - VMSTATE_INT32(state, MilkymistMinimac2MdioState), - VMSTATE_UINT8(phy_addr, MilkymistMinimac2MdioState), - VMSTATE_UINT8(reg_addr, MilkymistMinimac2MdioState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_milkymist_minimac2 = { - .name = "milkymist-minimac2", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, MilkymistMinimac2State, R_MAX), - VMSTATE_UINT16_ARRAY(phy_regs, MilkymistMinimac2State, R_PHY_MAX), - VMSTATE_STRUCT(mdio, MilkymistMinimac2State, 0, - vmstate_milkymist_minimac2_mdio, MilkymistMinimac2MdioState), - VMSTATE_END_OF_LIST() - } -}; - -static Property milkymist_minimac2_properties[] = { - DEFINE_NIC_PROPERTIES(MilkymistMinimac2State, conf), - DEFINE_PROP_STRING("phy_model", MilkymistMinimac2State, phy_model), - DEFINE_PROP_END_OF_LIST(), -}; - -static void milkymist_minimac2_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = milkymist_minimac2_init; - dc->reset = milkymist_minimac2_reset; - dc->vmsd = &vmstate_milkymist_minimac2; - dc->props = milkymist_minimac2_properties; -} - -static const TypeInfo milkymist_minimac2_info = { - .name = TYPE_MILKYMIST_MINIMAC2, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MilkymistMinimac2State), - .class_init = milkymist_minimac2_class_init, -}; - -static void milkymist_minimac2_register_types(void) -{ - type_register_static(&milkymist_minimac2_info); -} - -type_init(milkymist_minimac2_register_types) diff --git a/qemu/hw/net/mipsnet.c b/qemu/hw/net/mipsnet.c deleted file mode 100644 index 740cd98ff..000000000 --- a/qemu/hw/net/mipsnet.c +++ /dev/null @@ -1,287 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "net/net.h" -#include "trace.h" -#include "hw/sysbus.h" - -/* MIPSnet register offsets */ - -#define MIPSNET_DEV_ID 0x00 -#define MIPSNET_BUSY 0x08 -#define MIPSNET_RX_DATA_COUNT 0x0c -#define MIPSNET_TX_DATA_COUNT 0x10 -#define MIPSNET_INT_CTL 0x14 -# define MIPSNET_INTCTL_TXDONE 0x00000001 -# define MIPSNET_INTCTL_RXDONE 0x00000002 -# define MIPSNET_INTCTL_TESTBIT 0x80000000 -#define MIPSNET_INTERRUPT_INFO 0x18 -#define MIPSNET_RX_DATA_BUFFER 0x1c -#define MIPSNET_TX_DATA_BUFFER 0x20 - -#define MAX_ETH_FRAME_SIZE 1514 - -#define TYPE_MIPS_NET "mipsnet" -#define MIPS_NET(obj) OBJECT_CHECK(MIPSnetState, (obj), TYPE_MIPS_NET) - -typedef struct MIPSnetState { - SysBusDevice parent_obj; - - uint32_t busy; - uint32_t rx_count; - uint32_t rx_read; - uint32_t tx_count; - uint32_t tx_written; - uint32_t intctl; - uint8_t rx_buffer[MAX_ETH_FRAME_SIZE]; - uint8_t tx_buffer[MAX_ETH_FRAME_SIZE]; - MemoryRegion io; - qemu_irq irq; - NICState *nic; - NICConf conf; -} MIPSnetState; - -static void mipsnet_reset(MIPSnetState *s) -{ - s->busy = 1; - s->rx_count = 0; - s->rx_read = 0; - s->tx_count = 0; - s->tx_written = 0; - s->intctl = 0; - memset(s->rx_buffer, 0, MAX_ETH_FRAME_SIZE); - memset(s->tx_buffer, 0, MAX_ETH_FRAME_SIZE); -} - -static void mipsnet_update_irq(MIPSnetState *s) -{ - int isr = !!s->intctl; - trace_mipsnet_irq(isr, s->intctl); - qemu_set_irq(s->irq, isr); -} - -static int mipsnet_buffer_full(MIPSnetState *s) -{ - if (s->rx_count >= MAX_ETH_FRAME_SIZE) - return 1; - return 0; -} - -static int mipsnet_can_receive(NetClientState *nc) -{ - MIPSnetState *s = qemu_get_nic_opaque(nc); - - if (s->busy) - return 0; - return !mipsnet_buffer_full(s); -} - -static ssize_t mipsnet_receive(NetClientState *nc, const uint8_t *buf, size_t size) -{ - MIPSnetState *s = qemu_get_nic_opaque(nc); - - trace_mipsnet_receive(size); - if (!mipsnet_can_receive(nc)) - return 0; - - s->busy = 1; - - /* Just accept everything. */ - - /* Write packet data. */ - memcpy(s->rx_buffer, buf, size); - - s->rx_count = size; - s->rx_read = 0; - - /* Now we can signal we have received something. */ - s->intctl |= MIPSNET_INTCTL_RXDONE; - mipsnet_update_irq(s); - - return size; -} - -static uint64_t mipsnet_ioport_read(void *opaque, hwaddr addr, - unsigned int size) -{ - MIPSnetState *s = opaque; - int ret = 0; - - addr &= 0x3f; - switch (addr) { - case MIPSNET_DEV_ID: - ret = be32_to_cpu(0x4d495053); /* MIPS */ - break; - case MIPSNET_DEV_ID + 4: - ret = be32_to_cpu(0x4e455430); /* NET0 */ - break; - case MIPSNET_BUSY: - ret = s->busy; - break; - case MIPSNET_RX_DATA_COUNT: - ret = s->rx_count; - break; - case MIPSNET_TX_DATA_COUNT: - ret = s->tx_count; - break; - case MIPSNET_INT_CTL: - ret = s->intctl; - s->intctl &= ~MIPSNET_INTCTL_TESTBIT; - break; - case MIPSNET_INTERRUPT_INFO: - /* XXX: This seems to be a per-VPE interrupt number. */ - ret = 0; - break; - case MIPSNET_RX_DATA_BUFFER: - if (s->rx_count) { - s->rx_count--; - ret = s->rx_buffer[s->rx_read++]; - if (mipsnet_can_receive(s->nic->ncs)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } - } - break; - /* Reads as zero. */ - case MIPSNET_TX_DATA_BUFFER: - default: - break; - } - trace_mipsnet_read(addr, ret); - return ret; -} - -static void mipsnet_ioport_write(void *opaque, hwaddr addr, - uint64_t val, unsigned int size) -{ - MIPSnetState *s = opaque; - - addr &= 0x3f; - trace_mipsnet_write(addr, val); - switch (addr) { - case MIPSNET_TX_DATA_COUNT: - s->tx_count = (val <= MAX_ETH_FRAME_SIZE) ? val : 0; - s->tx_written = 0; - break; - case MIPSNET_INT_CTL: - if (val & MIPSNET_INTCTL_TXDONE) { - s->intctl &= ~MIPSNET_INTCTL_TXDONE; - } else if (val & MIPSNET_INTCTL_RXDONE) { - s->intctl &= ~MIPSNET_INTCTL_RXDONE; - } else if (val & MIPSNET_INTCTL_TESTBIT) { - mipsnet_reset(s); - s->intctl |= MIPSNET_INTCTL_TESTBIT; - } else if (!val) { - /* ACK testbit interrupt, flag was cleared on read. */ - } - s->busy = !!s->intctl; - mipsnet_update_irq(s); - if (mipsnet_can_receive(s->nic->ncs)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } - break; - case MIPSNET_TX_DATA_BUFFER: - s->tx_buffer[s->tx_written++] = val; - if (s->tx_written == s->tx_count) { - /* Send buffer. */ - trace_mipsnet_send(s->tx_count); - qemu_send_packet(qemu_get_queue(s->nic), s->tx_buffer, s->tx_count); - s->tx_count = s->tx_written = 0; - s->intctl |= MIPSNET_INTCTL_TXDONE; - s->busy = 1; - mipsnet_update_irq(s); - } - break; - /* Read-only registers */ - case MIPSNET_DEV_ID: - case MIPSNET_BUSY: - case MIPSNET_RX_DATA_COUNT: - case MIPSNET_INTERRUPT_INFO: - case MIPSNET_RX_DATA_BUFFER: - default: - break; - } -} - -static const VMStateDescription vmstate_mipsnet = { - .name = "mipsnet", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(busy, MIPSnetState), - VMSTATE_UINT32(rx_count, MIPSnetState), - VMSTATE_UINT32(rx_read, MIPSnetState), - VMSTATE_UINT32(tx_count, MIPSnetState), - VMSTATE_UINT32(tx_written, MIPSnetState), - VMSTATE_UINT32(intctl, MIPSnetState), - VMSTATE_BUFFER(rx_buffer, MIPSnetState), - VMSTATE_BUFFER(tx_buffer, MIPSnetState), - VMSTATE_END_OF_LIST() - } -}; - -static NetClientInfo net_mipsnet_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = mipsnet_receive, -}; - -static const MemoryRegionOps mipsnet_ioport_ops = { - .read = mipsnet_ioport_read, - .write = mipsnet_ioport_write, - .impl.min_access_size = 1, - .impl.max_access_size = 4, -}; - -static int mipsnet_sysbus_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - MIPSnetState *s = MIPS_NET(dev); - - memory_region_init_io(&s->io, OBJECT(dev), &mipsnet_ioport_ops, s, - "mipsnet-io", 36); - sysbus_init_mmio(sbd, &s->io); - sysbus_init_irq(sbd, &s->irq); - - s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - return 0; -} - -static void mipsnet_sysbus_reset(DeviceState *dev) -{ - MIPSnetState *s = MIPS_NET(dev); - mipsnet_reset(s); -} - -static Property mipsnet_properties[] = { - DEFINE_NIC_PROPERTIES(MIPSnetState, conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void mipsnet_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = mipsnet_sysbus_init; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->desc = "MIPS Simulator network device"; - dc->reset = mipsnet_sysbus_reset; - dc->vmsd = &vmstate_mipsnet; - dc->props = mipsnet_properties; -} - -static const TypeInfo mipsnet_info = { - .name = TYPE_MIPS_NET, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MIPSnetState), - .class_init = mipsnet_class_init, -}; - -static void mipsnet_register_types(void) -{ - type_register_static(&mipsnet_info); -} - -type_init(mipsnet_register_types) diff --git a/qemu/hw/net/ne2000-isa.c b/qemu/hw/net/ne2000-isa.c deleted file mode 100644 index a7f5a9464..000000000 --- a/qemu/hw/net/ne2000-isa.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * QEMU NE2000 emulation -- isa bus windup - * - * Copyright (c) 2003-2004 Fabrice Bellard - * - * 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/i386/pc.h" -#include "hw/isa/isa.h" -#include "hw/qdev.h" -#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" -#define ISA_NE2000(obj) OBJECT_CHECK(ISANE2000State, (obj), TYPE_ISA_NE2000) - -typedef struct ISANE2000State { - ISADevice parent_obj; - - uint32_t iobase; - uint32_t isairq; - NE2000State ne2000; -} ISANE2000State; - -static NetClientInfo net_ne2000_isa_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = ne2000_receive, -}; - -static const VMStateDescription vmstate_isa_ne2000 = { - .name = "ne2000", - .version_id = 2, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(ne2000, ISANE2000State, 0, vmstate_ne2000, NE2000State), - VMSTATE_END_OF_LIST() - } -}; - -static void isa_ne2000_realizefn(DeviceState *dev, Error **errp) -{ - ISADevice *isadev = ISA_DEVICE(dev); - ISANE2000State *isa = ISA_NE2000(dev); - NE2000State *s = &isa->ne2000; - - ne2000_setup_io(s, DEVICE(isadev), 0x20); - isa_register_ioport(isadev, &s->io, isa->iobase); - - isa_init_irq(isadev, &s->irq, isa->isairq); - - qemu_macaddr_default_if_unset(&s->c.macaddr); - ne2000_reset(s); - - s->nic = qemu_new_nic(&net_ne2000_isa_info, &s->c, - object_get_typename(OBJECT(dev)), dev->id, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a); -} - -static Property ne2000_isa_properties[] = { - DEFINE_PROP_UINT32("iobase", ISANE2000State, iobase, 0x300), - DEFINE_PROP_UINT32("irq", ISANE2000State, isairq, 9), - DEFINE_NIC_PROPERTIES(ISANE2000State, ne2000.c), - DEFINE_PROP_END_OF_LIST(), -}; - -static void isa_ne2000_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = isa_ne2000_realizefn; - dc->props = ne2000_isa_properties; - dc->vmsd = &vmstate_isa_ne2000; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); -} - -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, name, &s->c.bootindex, 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, name, &boot_index, &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->c.bootindex = boot_index; - -out: - if (local_err) { - error_propagate(errp, local_err); - } -} - -static void isa_ne2000_instance_init(Object *obj) -{ - object_property_add(obj, "bootindex", "int32", - isa_ne2000_get_bootindex, - isa_ne2000_set_bootindex, NULL, NULL, NULL); - object_property_set_int(obj, -1, "bootindex", NULL); -} -static const TypeInfo ne2000_isa_info = { - .name = TYPE_ISA_NE2000, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(ISANE2000State), - .class_init = isa_ne2000_class_initfn, - .instance_init = isa_ne2000_instance_init, -}; - -static void ne2000_isa_register_types(void) -{ - type_register_static(&ne2000_isa_info); -} - -type_init(ne2000_isa_register_types) diff --git a/qemu/hw/net/ne2000.c b/qemu/hw/net/ne2000.c deleted file mode 100644 index f0feaf96b..000000000 --- a/qemu/hw/net/ne2000.c +++ /dev/null @@ -1,796 +0,0 @@ -/* - * QEMU NE2000 emulation - * - * Copyright (c) 2003-2004 Fabrice Bellard - * - * 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/pci/pci.h" -#include "net/net.h" -#include "ne2000.h" -#include "hw/loader.h" -#include "sysemu/sysemu.h" - -/* debug NE2000 card */ -//#define DEBUG_NE2000 - -#define MAX_ETH_FRAME_SIZE 1514 - -#define E8390_CMD 0x00 /* The command register (for all pages) */ -/* Page 0 register offsets. */ -#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */ -#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */ -#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */ -#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */ -#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */ -#define EN0_TSR 0x04 /* Transmit status reg RD */ -#define EN0_TPSR 0x04 /* Transmit starting page WR */ -#define EN0_NCR 0x05 /* Number of collision reg RD */ -#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */ -#define EN0_FIFO 0x06 /* FIFO RD */ -#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */ -#define EN0_ISR 0x07 /* Interrupt status reg RD WR */ -#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */ -#define EN0_RSARLO 0x08 /* Remote start address reg 0 */ -#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */ -#define EN0_RSARHI 0x09 /* Remote start address reg 1 */ -#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */ -#define EN0_RTL8029ID0 0x0a /* Realtek ID byte #1 RD */ -#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */ -#define EN0_RTL8029ID1 0x0b /* Realtek ID byte #2 RD */ -#define EN0_RSR 0x0c /* rx status reg RD */ -#define EN0_RXCR 0x0c /* RX configuration reg WR */ -#define EN0_TXCR 0x0d /* TX configuration reg WR */ -#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */ -#define EN0_DCFG 0x0e /* Data configuration reg WR */ -#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */ -#define EN0_IMR 0x0f /* Interrupt mask reg WR */ -#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */ - -#define EN1_PHYS 0x11 -#define EN1_CURPAG 0x17 -#define EN1_MULT 0x18 - -#define EN2_STARTPG 0x21 /* Starting page of ring bfr RD */ -#define EN2_STOPPG 0x22 /* Ending page +1 of ring bfr RD */ - -#define EN3_CONFIG0 0x33 -#define EN3_CONFIG1 0x34 -#define EN3_CONFIG2 0x35 -#define EN3_CONFIG3 0x36 - -/* Register accessed at EN_CMD, the 8390 base addr. */ -#define E8390_STOP 0x01 /* Stop and reset the chip */ -#define E8390_START 0x02 /* Start the chip, clear reset */ -#define E8390_TRANS 0x04 /* Transmit a frame */ -#define E8390_RREAD 0x08 /* Remote read */ -#define E8390_RWRITE 0x10 /* Remote write */ -#define E8390_NODMA 0x20 /* Remote DMA */ -#define E8390_PAGE0 0x00 /* Select page chip registers */ -#define E8390_PAGE1 0x40 /* using the two high-order bits */ -#define E8390_PAGE2 0x80 /* Page 3 is invalid. */ - -/* Bits in EN0_ISR - Interrupt status register */ -#define ENISR_RX 0x01 /* Receiver, no error */ -#define ENISR_TX 0x02 /* Transmitter, no error */ -#define ENISR_RX_ERR 0x04 /* Receiver, with error */ -#define ENISR_TX_ERR 0x08 /* Transmitter, with error */ -#define ENISR_OVER 0x10 /* Receiver overwrote the ring */ -#define ENISR_COUNTERS 0x20 /* Counters need emptying */ -#define ENISR_RDC 0x40 /* remote dma complete */ -#define ENISR_RESET 0x80 /* Reset completed */ -#define ENISR_ALL 0x3f /* Interrupts we will enable */ - -/* Bits in received packet status byte and EN0_RSR*/ -#define ENRSR_RXOK 0x01 /* Received a good packet */ -#define ENRSR_CRC 0x02 /* CRC error */ -#define ENRSR_FAE 0x04 /* frame alignment error */ -#define ENRSR_FO 0x08 /* FIFO overrun */ -#define ENRSR_MPA 0x10 /* missed pkt */ -#define ENRSR_PHY 0x20 /* physical/multicast address */ -#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */ -#define ENRSR_DEF 0x80 /* deferring */ - -/* Transmitted packet status, EN0_TSR. */ -#define ENTSR_PTX 0x01 /* Packet transmitted without error */ -#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */ -#define ENTSR_COL 0x04 /* The transmit collided at least once. */ -#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */ -#define ENTSR_CRS 0x10 /* The carrier sense was lost. */ -#define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */ -#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */ -#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */ - -typedef struct PCINE2000State { - PCIDevice dev; - NE2000State ne2000; -} PCINE2000State; - -void ne2000_reset(NE2000State *s) -{ - int i; - - s->isr = ENISR_RESET; - memcpy(s->mem, &s->c.macaddr, 6); - s->mem[14] = 0x57; - s->mem[15] = 0x57; - - /* duplicate prom data */ - for(i = 15;i >= 0; i--) { - s->mem[2 * i] = s->mem[i]; - s->mem[2 * i + 1] = s->mem[i]; - } -} - -static void ne2000_update_irq(NE2000State *s) -{ - int isr; - isr = (s->isr & s->imr) & 0x7f; -#if defined(DEBUG_NE2000) - printf("NE2000: Set IRQ to %d (%02x %02x)\n", - isr ? 1 : 0, s->isr, s->imr); -#endif - qemu_set_irq(s->irq, (isr != 0)); -} - -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) - avail = boundary - index; - else - avail = (s->stop - s->start) - (index - boundary); - if (avail < (MAX_ETH_FRAME_SIZE + 4)) - return 1; - return 0; -} - -#define MIN_BUF_SIZE 60 - -ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_) -{ - NE2000State *s = qemu_get_nic_opaque(nc); - int size = size_; - uint8_t *p; - unsigned int total_len, next, avail, len, index, mcast_idx; - uint8_t buf1[60]; - static const uint8_t broadcast_macaddr[6] = - { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - -#if defined(DEBUG_NE2000) - printf("NE2000: received len=%d\n", size); -#endif - - if (s->cmd & E8390_STOP || ne2000_buffer_full(s)) - return -1; - - /* XXX: check this */ - if (s->rxcr & 0x10) { - /* promiscuous: receive all */ - } else { - if (!memcmp(buf, broadcast_macaddr, 6)) { - /* broadcast address */ - if (!(s->rxcr & 0x04)) - return size; - } else if (buf[0] & 0x01) { - /* multicast */ - if (!(s->rxcr & 0x08)) - return size; - mcast_idx = compute_mcast_idx(buf); - if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) - return size; - } else if (s->mem[0] == buf[0] && - s->mem[2] == buf[1] && - s->mem[4] == buf[2] && - s->mem[6] == buf[3] && - s->mem[8] == buf[4] && - s->mem[10] == buf[5]) { - /* match */ - } else { - return size; - } - } - - - /* if too small buffer, then expand it */ - if (size < MIN_BUF_SIZE) { - memcpy(buf1, buf, size); - memset(buf1 + size, 0, MIN_BUF_SIZE - size); - buf = buf1; - size = MIN_BUF_SIZE; - } - - index = s->curpag << 8; - if (index >= NE2000_PMEM_END) { - index = s->start; - } - /* 4 bytes for header */ - total_len = size + 4; - /* address for next packet (4 bytes for CRC) */ - next = index + ((total_len + 4 + 255) & ~0xff); - if (next >= s->stop) - next -= (s->stop - s->start); - /* prepare packet header */ - p = s->mem + index; - s->rsr = ENRSR_RXOK; /* receive status */ - /* XXX: check this */ - if (buf[0] & 0x01) - s->rsr |= ENRSR_PHY; - p[0] = s->rsr; - p[1] = next >> 8; - p[2] = total_len; - p[3] = total_len >> 8; - index += 4; - - /* write packet data */ - while (size > 0) { - if (index <= s->stop) - avail = s->stop - index; - else - break; - len = size; - if (len > avail) - len = avail; - memcpy(s->mem + index, buf, len); - buf += len; - index += len; - if (index == s->stop) - index = s->start; - size -= len; - } - s->curpag = next >> 8; - - /* now we can signal we have received something */ - s->isr |= ENISR_RX; - ne2000_update_irq(s); - - return size_; -} - -static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val) -{ - NE2000State *s = opaque; - int offset, page, index; - - addr &= 0xf; -#ifdef DEBUG_NE2000 - printf("NE2000: write addr=0x%x val=0x%02x\n", addr, val); -#endif - if (addr == E8390_CMD) { - /* control register */ - s->cmd = val; - if (!(val & E8390_STOP)) { /* START bit makes no sense on RTL8029... */ - s->isr &= ~ENISR_RESET; - /* test specific case: zero length transfer */ - if ((val & (E8390_RREAD | E8390_RWRITE)) && - s->rcnt == 0) { - s->isr |= ENISR_RDC; - ne2000_update_irq(s); - } - if (val & E8390_TRANS) { - index = (s->tpsr << 8); - /* XXX: next 2 lines are a hack to make netware 3.11 work */ - if (index >= NE2000_PMEM_END) - index -= NE2000_PMEM_SIZE; - /* fail safe: check range on the transmitted length */ - if (index + s->tcnt <= NE2000_PMEM_END) { - qemu_send_packet(qemu_get_queue(s->nic), s->mem + index, - s->tcnt); - } - /* signal end of transfer */ - s->tsr = ENTSR_PTX; - s->isr |= ENISR_TX; - s->cmd &= ~E8390_TRANS; - ne2000_update_irq(s); - } - } - } else { - page = s->cmd >> 6; - offset = addr | (page << 4); - switch(offset) { - case EN0_STARTPG: - if (val << 8 <= NE2000_PMEM_END) { - s->start = val << 8; - } - break; - case EN0_STOPPG: - if (val << 8 <= NE2000_PMEM_END) { - s->stop = val << 8; - } - break; - case EN0_BOUNDARY: - if (val << 8 < NE2000_PMEM_END) { - s->boundary = val; - } - break; - case EN0_IMR: - s->imr = val; - ne2000_update_irq(s); - break; - case EN0_TPSR: - s->tpsr = val; - break; - case EN0_TCNTLO: - s->tcnt = (s->tcnt & 0xff00) | val; - break; - case EN0_TCNTHI: - s->tcnt = (s->tcnt & 0x00ff) | (val << 8); - break; - case EN0_RSARLO: - s->rsar = (s->rsar & 0xff00) | val; - break; - case EN0_RSARHI: - s->rsar = (s->rsar & 0x00ff) | (val << 8); - break; - case EN0_RCNTLO: - s->rcnt = (s->rcnt & 0xff00) | val; - break; - case EN0_RCNTHI: - s->rcnt = (s->rcnt & 0x00ff) | (val << 8); - break; - case EN0_RXCR: - s->rxcr = val; - break; - case EN0_DCFG: - s->dcfg = val; - break; - case EN0_ISR: - s->isr &= ~(val & 0x7f); - ne2000_update_irq(s); - break; - case EN1_PHYS ... EN1_PHYS + 5: - s->phys[offset - EN1_PHYS] = val; - break; - case EN1_CURPAG: - if (val << 8 < NE2000_PMEM_END) { - s->curpag = val; - } - break; - case EN1_MULT ... EN1_MULT + 7: - s->mult[offset - EN1_MULT] = val; - break; - } - } -} - -static uint32_t ne2000_ioport_read(void *opaque, uint32_t addr) -{ - NE2000State *s = opaque; - int offset, page, ret; - - addr &= 0xf; - if (addr == E8390_CMD) { - ret = s->cmd; - } else { - page = s->cmd >> 6; - offset = addr | (page << 4); - switch(offset) { - case EN0_TSR: - ret = s->tsr; - break; - case EN0_BOUNDARY: - ret = s->boundary; - break; - case EN0_ISR: - ret = s->isr; - break; - case EN0_RSARLO: - ret = s->rsar & 0x00ff; - break; - case EN0_RSARHI: - ret = s->rsar >> 8; - break; - case EN1_PHYS ... EN1_PHYS + 5: - ret = s->phys[offset - EN1_PHYS]; - break; - case EN1_CURPAG: - ret = s->curpag; - break; - case EN1_MULT ... EN1_MULT + 7: - ret = s->mult[offset - EN1_MULT]; - break; - case EN0_RSR: - ret = s->rsr; - break; - case EN2_STARTPG: - ret = s->start >> 8; - break; - case EN2_STOPPG: - ret = s->stop >> 8; - break; - case EN0_RTL8029ID0: - ret = 0x50; - break; - case EN0_RTL8029ID1: - ret = 0x43; - break; - case EN3_CONFIG0: - ret = 0; /* 10baseT media */ - break; - case EN3_CONFIG2: - ret = 0x40; /* 10baseT active */ - break; - case EN3_CONFIG3: - ret = 0x40; /* Full duplex */ - break; - default: - ret = 0x00; - break; - } - } -#ifdef DEBUG_NE2000 - printf("NE2000: read addr=0x%x val=%02x\n", addr, ret); -#endif - return ret; -} - -static inline void ne2000_mem_writeb(NE2000State *s, uint32_t addr, - uint32_t val) -{ - if (addr < 32 || - (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { - s->mem[addr] = val; - } -} - -static inline void ne2000_mem_writew(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)) { - *(uint16_t *)(s->mem + addr) = cpu_to_le16(val); - } -} - -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 + sizeof(uint32_t) <= NE2000_MEM_SIZE)) { - stl_le_p(s->mem + addr, val); - } -} - -static inline uint32_t ne2000_mem_readb(NE2000State *s, uint32_t addr) -{ - if (addr < 32 || - (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { - return s->mem[addr]; - } else { - return 0xff; - } -} - -static inline uint32_t ne2000_mem_readw(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)) { - return le16_to_cpu(*(uint16_t *)(s->mem + addr)); - } else { - return 0xffff; - } -} - -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 + sizeof(uint32_t) <= NE2000_MEM_SIZE)) { - return ldl_le_p(s->mem + addr); - } else { - return 0xffffffff; - } -} - -static inline void ne2000_dma_update(NE2000State *s, int len) -{ - s->rsar += len; - /* wrap */ - /* XXX: check what to do if rsar > stop */ - if (s->rsar == s->stop) - s->rsar = s->start; - - if (s->rcnt <= len) { - s->rcnt = 0; - /* signal end of transfer */ - s->isr |= ENISR_RDC; - ne2000_update_irq(s); - } else { - s->rcnt -= len; - } -} - -static void ne2000_asic_ioport_write(void *opaque, uint32_t addr, uint32_t val) -{ - NE2000State *s = opaque; - -#ifdef DEBUG_NE2000 - printf("NE2000: asic write val=0x%04x\n", val); -#endif - if (s->rcnt == 0) - return; - if (s->dcfg & 0x01) { - /* 16 bit access */ - ne2000_mem_writew(s, s->rsar, val); - ne2000_dma_update(s, 2); - } else { - /* 8 bit access */ - ne2000_mem_writeb(s, s->rsar, val); - ne2000_dma_update(s, 1); - } -} - -static uint32_t ne2000_asic_ioport_read(void *opaque, uint32_t addr) -{ - NE2000State *s = opaque; - int ret; - - if (s->dcfg & 0x01) { - /* 16 bit access */ - ret = ne2000_mem_readw(s, s->rsar); - ne2000_dma_update(s, 2); - } else { - /* 8 bit access */ - ret = ne2000_mem_readb(s, s->rsar); - ne2000_dma_update(s, 1); - } -#ifdef DEBUG_NE2000 - printf("NE2000: asic read val=0x%04x\n", ret); -#endif - return ret; -} - -static void ne2000_asic_ioport_writel(void *opaque, uint32_t addr, uint32_t val) -{ - NE2000State *s = opaque; - -#ifdef DEBUG_NE2000 - printf("NE2000: asic writel val=0x%04x\n", val); -#endif - if (s->rcnt == 0) - return; - /* 32 bit access */ - ne2000_mem_writel(s, s->rsar, val); - ne2000_dma_update(s, 4); -} - -static uint32_t ne2000_asic_ioport_readl(void *opaque, uint32_t addr) -{ - NE2000State *s = opaque; - int ret; - - /* 32 bit access */ - ret = ne2000_mem_readl(s, s->rsar); - ne2000_dma_update(s, 4); -#ifdef DEBUG_NE2000 - printf("NE2000: asic readl val=0x%04x\n", ret); -#endif - return ret; -} - -static void ne2000_reset_ioport_write(void *opaque, uint32_t addr, uint32_t val) -{ - /* nothing to do (end of reset pulse) */ -} - -static uint32_t ne2000_reset_ioport_read(void *opaque, uint32_t addr) -{ - NE2000State *s = opaque; - ne2000_reset(s); - return 0; -} - -static int ne2000_post_load(void* opaque, int version_id) -{ - NE2000State* s = opaque; - - if (version_id < 2) { - s->rxcr = 0x0c; - } - return 0; -} - -const VMStateDescription vmstate_ne2000 = { - .name = "ne2000", - .version_id = 2, - .minimum_version_id = 0, - .post_load = ne2000_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8_V(rxcr, NE2000State, 2), - VMSTATE_UINT8(cmd, NE2000State), - VMSTATE_UINT32(start, NE2000State), - VMSTATE_UINT32(stop, NE2000State), - VMSTATE_UINT8(boundary, NE2000State), - VMSTATE_UINT8(tsr, NE2000State), - VMSTATE_UINT8(tpsr, NE2000State), - VMSTATE_UINT16(tcnt, NE2000State), - VMSTATE_UINT16(rcnt, NE2000State), - VMSTATE_UINT32(rsar, NE2000State), - VMSTATE_UINT8(rsr, NE2000State), - VMSTATE_UINT8(isr, NE2000State), - VMSTATE_UINT8(dcfg, NE2000State), - VMSTATE_UINT8(imr, NE2000State), - VMSTATE_BUFFER(phys, NE2000State), - VMSTATE_UINT8(curpag, NE2000State), - VMSTATE_BUFFER(mult, NE2000State), - VMSTATE_UNUSED(4), /* was irq */ - VMSTATE_BUFFER(mem, NE2000State), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_pci_ne2000 = { - .name = "ne2000", - .version_id = 3, - .minimum_version_id = 3, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, PCINE2000State), - VMSTATE_STRUCT(ne2000, PCINE2000State, 0, vmstate_ne2000, NE2000State), - VMSTATE_END_OF_LIST() - } -}; - -static uint64_t ne2000_read(void *opaque, hwaddr addr, - unsigned size) -{ - NE2000State *s = opaque; - - if (addr < 0x10 && size == 1) { - return ne2000_ioport_read(s, addr); - } else if (addr == 0x10) { - if (size <= 2) { - return ne2000_asic_ioport_read(s, addr); - } else { - return ne2000_asic_ioport_readl(s, addr); - } - } else if (addr == 0x1f && size == 1) { - return ne2000_reset_ioport_read(s, addr); - } - return ((uint64_t)1 << (size * 8)) - 1; -} - -static void ne2000_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - NE2000State *s = opaque; - - if (addr < 0x10 && size == 1) { - ne2000_ioport_write(s, addr, data); - } else if (addr == 0x10) { - if (size <= 2) { - ne2000_asic_ioport_write(s, addr, data); - } else { - ne2000_asic_ioport_writel(s, addr, data); - } - } else if (addr == 0x1f && size == 1) { - ne2000_reset_ioport_write(s, addr, data); - } -} - -static const MemoryRegionOps ne2000_ops = { - .read = ne2000_read, - .write = ne2000_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -/***********************************************************/ -/* PCI NE2000 definitions */ - -void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size) -{ - memory_region_init_io(&s->io, OBJECT(dev), &ne2000_ops, s, "ne2000", size); -} - -static NetClientInfo net_ne2000_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = ne2000_receive, -}; - -static void pci_ne2000_realize(PCIDevice *pci_dev, Error **errp) -{ - PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev); - NE2000State *s; - uint8_t *pci_conf; - - pci_conf = d->dev.config; - pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */ - - s = &d->ne2000; - ne2000_setup_io(s, DEVICE(pci_dev), 0x100); - pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io); - s->irq = pci_allocate_irq(&d->dev); - - qemu_macaddr_default_if_unset(&s->c.macaddr); - ne2000_reset(s); - - s->nic = qemu_new_nic(&net_ne2000_info, &s->c, - object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a); -} - -static void pci_ne2000_exit(PCIDevice *pci_dev) -{ - PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev); - NE2000State *s = &d->ne2000; - - qemu_del_nic(s->nic); - qemu_free_irq(s->irq); -} - -static void ne2000_instance_init(Object *obj) -{ - PCIDevice *pci_dev = PCI_DEVICE(obj); - PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev); - NE2000State *s = &d->ne2000; - - device_add_bootindex_property(obj, &s->c.bootindex, - "bootindex", "/ethernet-phy@0", - &pci_dev->qdev, NULL); -} - -static Property ne2000_properties[] = { - DEFINE_NIC_PROPERTIES(PCINE2000State, ne2000.c), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ne2000_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = pci_ne2000_realize; - k->exit = pci_ne2000_exit; - k->romfile = "efi-ne2k_pci.rom", - k->vendor_id = PCI_VENDOR_ID_REALTEK; - k->device_id = PCI_DEVICE_ID_REALTEK_8029; - k->class_id = PCI_CLASS_NETWORK_ETHERNET; - dc->vmsd = &vmstate_pci_ne2000; - dc->props = ne2000_properties; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); -} - -static const TypeInfo ne2000_info = { - .name = "ne2k_pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCINE2000State), - .class_init = ne2000_class_init, - .instance_init = ne2000_instance_init, -}; - -static void ne2000_register_types(void) -{ - type_register_static(&ne2000_info); -} - -type_init(ne2000_register_types) diff --git a/qemu/hw/net/ne2000.h b/qemu/hw/net/ne2000.h deleted file mode 100644 index d022b28fc..000000000 --- a/qemu/hw/net/ne2000.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef HW_NE2000_H -#define HW_NE2000_H 1 - -#define NE2000_PMEM_SIZE (32*1024) -#define NE2000_PMEM_START (16*1024) -#define NE2000_PMEM_END (NE2000_PMEM_SIZE+NE2000_PMEM_START) -#define NE2000_MEM_SIZE NE2000_PMEM_END - -typedef struct NE2000State { - MemoryRegion io; - uint8_t cmd; - uint32_t start; - uint32_t stop; - uint8_t boundary; - uint8_t tsr; - uint8_t tpsr; - uint16_t tcnt; - uint16_t rcnt; - uint32_t rsar; - uint8_t rsr; - uint8_t rxcr; - uint8_t isr; - uint8_t dcfg; - uint8_t imr; - uint8_t phys[6]; /* mac address */ - uint8_t curpag; - uint8_t mult[8]; /* multicast mask array */ - qemu_irq irq; - NICState *nic; - NICConf c; - uint8_t mem[NE2000_MEM_SIZE]; -} NE2000State; - -void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size); -extern const VMStateDescription vmstate_ne2000; -void ne2000_reset(NE2000State *s); -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 deleted file mode 100644 index c6094fbb5..000000000 --- a/qemu/hw/net/opencores_eth.c +++ /dev/null @@ -1,765 +0,0 @@ -/* - * OpenCores Ethernet MAC 10/100 + subset of - * National Semiconductors DP83848C 10/100 PHY - * - * http://opencores.org/svnget,ethmac?file=%2Ftrunk%2F%2Fdoc%2Feth_speci.pdf - * http://cache.national.com/ds/DP/DP83848C.pdf - * - * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the Open Source and Linux Lab 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 AUTHOR 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 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "net/net.h" -#include "sysemu/sysemu.h" -#include "trace.h" - -/* RECSMALL is not used because it breaks tap networking in linux: - * incoming ARP responses are too short - */ -#undef USE_RECSMALL - -#define GET_FIELD(v, field) (((v) & (field)) >> (field ## _LBN)) -#define GET_REGBIT(s, reg, field) ((s)->regs[reg] & (reg ## _ ## field)) -#define GET_REGFIELD(s, reg, field) \ - GET_FIELD((s)->regs[reg], reg ## _ ## field) - -#define SET_FIELD(v, field, data) \ - ((v) = (((v) & ~(field)) | (((data) << (field ## _LBN)) & (field)))) -#define SET_REGFIELD(s, reg, field, data) \ - SET_FIELD((s)->regs[reg], reg ## _ ## field, data) - -/* PHY MII registers */ -enum { - MII_BMCR, - MII_BMSR, - MII_PHYIDR1, - MII_PHYIDR2, - MII_ANAR, - MII_ANLPAR, - MII_REG_MAX = 16, -}; - -typedef struct Mii { - uint16_t regs[MII_REG_MAX]; - bool link_ok; -} Mii; - -static void mii_set_link(Mii *s, bool link_ok) -{ - if (link_ok) { - s->regs[MII_BMSR] |= 0x4; - s->regs[MII_ANLPAR] |= 0x01e1; - } else { - s->regs[MII_BMSR] &= ~0x4; - s->regs[MII_ANLPAR] &= 0x01ff; - } - s->link_ok = link_ok; -} - -static void mii_reset(Mii *s) -{ - memset(s->regs, 0, sizeof(s->regs)); - s->regs[MII_BMCR] = 0x1000; - s->regs[MII_BMSR] = 0x7868; /* no ext regs */ - s->regs[MII_PHYIDR1] = 0x2000; - s->regs[MII_PHYIDR2] = 0x5c90; - s->regs[MII_ANAR] = 0x01e1; - mii_set_link(s, s->link_ok); -} - -static void mii_ro(Mii *s, uint16_t v) -{ -} - -static void mii_write_bmcr(Mii *s, uint16_t v) -{ - if (v & 0x8000) { - mii_reset(s); - } else { - s->regs[MII_BMCR] = v; - } -} - -static void mii_write_host(Mii *s, unsigned idx, uint16_t v) -{ - static void (*reg_write[MII_REG_MAX])(Mii *s, uint16_t v) = { - [MII_BMCR] = mii_write_bmcr, - [MII_BMSR] = mii_ro, - [MII_PHYIDR1] = mii_ro, - [MII_PHYIDR2] = mii_ro, - }; - - if (idx < MII_REG_MAX) { - trace_open_eth_mii_write(idx, v); - if (reg_write[idx]) { - reg_write[idx](s, v); - } else { - s->regs[idx] = v; - } - } -} - -static uint16_t mii_read_host(Mii *s, unsigned idx) -{ - trace_open_eth_mii_read(idx, s->regs[idx]); - return s->regs[idx]; -} - -/* OpenCores Ethernet registers */ -enum { - MODER, - INT_SOURCE, - INT_MASK, - IPGT, - IPGR1, - IPGR2, - PACKETLEN, - COLLCONF, - TX_BD_NUM, - CTRLMODER, - MIIMODER, - MIICOMMAND, - MIIADDRESS, - MIITX_DATA, - MIIRX_DATA, - MIISTATUS, - MAC_ADDR0, - MAC_ADDR1, - HASH0, - HASH1, - TXCTRL, - REG_MAX, -}; - -enum { - MODER_RECSMALL = 0x10000, - MODER_PAD = 0x8000, - MODER_HUGEN = 0x4000, - MODER_RST = 0x800, - MODER_LOOPBCK = 0x80, - MODER_PRO = 0x20, - MODER_IAM = 0x10, - MODER_BRO = 0x8, - MODER_TXEN = 0x2, - MODER_RXEN = 0x1, -}; - -enum { - INT_SOURCE_BUSY = 0x10, - INT_SOURCE_RXB = 0x4, - INT_SOURCE_TXB = 0x1, -}; - -enum { - PACKETLEN_MINFL = 0xffff0000, - PACKETLEN_MINFL_LBN = 16, - PACKETLEN_MAXFL = 0xffff, - PACKETLEN_MAXFL_LBN = 0, -}; - -enum { - MIICOMMAND_WCTRLDATA = 0x4, - MIICOMMAND_RSTAT = 0x2, - MIICOMMAND_SCANSTAT = 0x1, -}; - -enum { - MIIADDRESS_RGAD = 0x1f00, - MIIADDRESS_RGAD_LBN = 8, - MIIADDRESS_FIAD = 0x1f, - MIIADDRESS_FIAD_LBN = 0, -}; - -enum { - MIITX_DATA_CTRLDATA = 0xffff, - MIITX_DATA_CTRLDATA_LBN = 0, -}; - -enum { - MIIRX_DATA_PRSD = 0xffff, - MIIRX_DATA_PRSD_LBN = 0, -}; - -enum { - MIISTATUS_LINKFAIL = 0x1, - MIISTATUS_LINKFAIL_LBN = 0, -}; - -enum { - MAC_ADDR0_BYTE2 = 0xff000000, - MAC_ADDR0_BYTE2_LBN = 24, - MAC_ADDR0_BYTE3 = 0xff0000, - MAC_ADDR0_BYTE3_LBN = 16, - MAC_ADDR0_BYTE4 = 0xff00, - MAC_ADDR0_BYTE4_LBN = 8, - MAC_ADDR0_BYTE5 = 0xff, - MAC_ADDR0_BYTE5_LBN = 0, -}; - -enum { - MAC_ADDR1_BYTE0 = 0xff00, - MAC_ADDR1_BYTE0_LBN = 8, - MAC_ADDR1_BYTE1 = 0xff, - MAC_ADDR1_BYTE1_LBN = 0, -}; - -enum { - TXD_LEN = 0xffff0000, - TXD_LEN_LBN = 16, - TXD_RD = 0x8000, - TXD_IRQ = 0x4000, - TXD_WR = 0x2000, - TXD_PAD = 0x1000, - TXD_CRC = 0x800, - TXD_UR = 0x100, - TXD_RTRY = 0xf0, - TXD_RTRY_LBN = 4, - TXD_RL = 0x8, - TXD_LC = 0x4, - TXD_DF = 0x2, - TXD_CS = 0x1, -}; - -enum { - RXD_LEN = 0xffff0000, - RXD_LEN_LBN = 16, - RXD_E = 0x8000, - RXD_IRQ = 0x4000, - RXD_WRAP = 0x2000, - RXD_CF = 0x100, - RXD_M = 0x80, - RXD_OR = 0x40, - RXD_IS = 0x20, - RXD_DN = 0x10, - RXD_TL = 0x8, - RXD_SF = 0x4, - RXD_CRC = 0x2, - RXD_LC = 0x1, -}; - -typedef struct desc { - uint32_t len_flags; - uint32_t buf_ptr; -} desc; - -#define DEFAULT_PHY 1 - -#define TYPE_OPEN_ETH "open_eth" -#define OPEN_ETH(obj) OBJECT_CHECK(OpenEthState, (obj), TYPE_OPEN_ETH) - -typedef struct OpenEthState { - SysBusDevice parent_obj; - - NICState *nic; - NICConf conf; - MemoryRegion reg_io; - MemoryRegion desc_io; - qemu_irq irq; - - Mii mii; - uint32_t regs[REG_MAX]; - unsigned tx_desc; - unsigned rx_desc; - desc desc[128]; -} OpenEthState; - -static desc *rx_desc(OpenEthState *s) -{ - return s->desc + s->rx_desc; -} - -static desc *tx_desc(OpenEthState *s) -{ - return s->desc + s->tx_desc; -} - -static void open_eth_update_irq(OpenEthState *s, - uint32_t old, uint32_t new) -{ - if (!old != !new) { - trace_open_eth_update_irq(new); - qemu_set_irq(s->irq, new); - } -} - -static void open_eth_int_source_write(OpenEthState *s, - uint32_t val) -{ - uint32_t old_val = s->regs[INT_SOURCE]; - - s->regs[INT_SOURCE] = val; - open_eth_update_irq(s, old_val & s->regs[INT_MASK], - s->regs[INT_SOURCE] & s->regs[INT_MASK]); -} - -static void open_eth_set_link_status(NetClientState *nc) -{ - OpenEthState *s = qemu_get_nic_opaque(nc); - - if (GET_REGBIT(s, MIICOMMAND, SCANSTAT)) { - SET_REGFIELD(s, MIISTATUS, LINKFAIL, nc->link_down); - } - mii_set_link(&s->mii, !nc->link_down); -} - -static void open_eth_reset(void *opaque) -{ - OpenEthState *s = opaque; - - memset(s->regs, 0, sizeof(s->regs)); - s->regs[MODER] = 0xa000; - s->regs[IPGT] = 0x12; - s->regs[IPGR1] = 0xc; - s->regs[IPGR2] = 0x12; - s->regs[PACKETLEN] = 0x400600; - s->regs[COLLCONF] = 0xf003f; - s->regs[TX_BD_NUM] = 0x40; - s->regs[MIIMODER] = 0x64; - - s->tx_desc = 0; - s->rx_desc = 0x40; - - mii_reset(&s->mii); - open_eth_set_link_status(qemu_get_queue(s->nic)); -} - -static int open_eth_can_receive(NetClientState *nc) -{ - OpenEthState *s = qemu_get_nic_opaque(nc); - - return GET_REGBIT(s, MODER, RXEN) && - (s->regs[TX_BD_NUM] < 0x80); -} - -static ssize_t open_eth_receive(NetClientState *nc, - const uint8_t *buf, size_t size) -{ - OpenEthState *s = qemu_get_nic_opaque(nc); - size_t maxfl = GET_REGFIELD(s, PACKETLEN, MAXFL); - size_t minfl = GET_REGFIELD(s, PACKETLEN, MINFL); - size_t fcsl = 4; - bool miss = true; - - trace_open_eth_receive((unsigned)size); - - if (size >= 6) { - static const uint8_t bcast_addr[] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff - }; - if (memcmp(buf, bcast_addr, sizeof(bcast_addr)) == 0) { - miss = GET_REGBIT(s, MODER, BRO); - } else if ((buf[0] & 0x1) || GET_REGBIT(s, MODER, IAM)) { - unsigned mcast_idx = compute_mcast_idx(buf); - miss = !(s->regs[HASH0 + mcast_idx / 32] & - (1 << (mcast_idx % 32))); - trace_open_eth_receive_mcast( - mcast_idx, s->regs[HASH0], s->regs[HASH1]); - } else { - miss = GET_REGFIELD(s, MAC_ADDR1, BYTE0) != buf[0] || - GET_REGFIELD(s, MAC_ADDR1, BYTE1) != buf[1] || - GET_REGFIELD(s, MAC_ADDR0, BYTE2) != buf[2] || - GET_REGFIELD(s, MAC_ADDR0, BYTE3) != buf[3] || - GET_REGFIELD(s, MAC_ADDR0, BYTE4) != buf[4] || - GET_REGFIELD(s, MAC_ADDR0, BYTE5) != buf[5]; - } - } - - if (miss && !GET_REGBIT(s, MODER, PRO)) { - trace_open_eth_receive_reject(); - return size; - } - -#ifdef USE_RECSMALL - if (GET_REGBIT(s, MODER, RECSMALL) || size >= minfl) { -#else - { -#endif - static const uint8_t zero[64] = {0}; - desc *desc = rx_desc(s); - size_t copy_size = GET_REGBIT(s, MODER, HUGEN) ? 65536 : maxfl; - - if (!(desc->len_flags & RXD_E)) { - open_eth_int_source_write(s, - s->regs[INT_SOURCE] | INT_SOURCE_BUSY); - return size; - } - - desc->len_flags &= ~(RXD_CF | RXD_M | RXD_OR | - RXD_IS | RXD_DN | RXD_TL | RXD_SF | RXD_CRC | RXD_LC); - - if (copy_size > size) { - copy_size = size; - } else { - fcsl = 0; - } - if (miss) { - desc->len_flags |= RXD_M; - } - if (GET_REGBIT(s, MODER, HUGEN) && size > maxfl) { - desc->len_flags |= RXD_TL; - } -#ifdef USE_RECSMALL - if (size < minfl) { - desc->len_flags |= RXD_SF; - } -#endif - - cpu_physical_memory_write(desc->buf_ptr, buf, copy_size); - - if (GET_REGBIT(s, MODER, PAD) && copy_size < minfl) { - if (minfl - copy_size > fcsl) { - fcsl = 0; - } else { - fcsl -= minfl - copy_size; - } - while (copy_size < minfl) { - size_t zero_sz = minfl - copy_size < sizeof(zero) ? - minfl - copy_size : sizeof(zero); - - cpu_physical_memory_write(desc->buf_ptr + copy_size, - zero, zero_sz); - copy_size += zero_sz; - } - } - - /* There's no FCS in the frames handed to us by the QEMU, zero fill it. - * Don't do it if the frame is cut at the MAXFL or padded with 4 or - * more bytes to the MINFL. - */ - cpu_physical_memory_write(desc->buf_ptr + copy_size, zero, fcsl); - copy_size += fcsl; - - SET_FIELD(desc->len_flags, RXD_LEN, copy_size); - - if ((desc->len_flags & RXD_WRAP) || s->rx_desc == 0x7f) { - s->rx_desc = s->regs[TX_BD_NUM]; - } else { - ++s->rx_desc; - } - desc->len_flags &= ~RXD_E; - - trace_open_eth_receive_desc(desc->buf_ptr, desc->len_flags); - - if (desc->len_flags & RXD_IRQ) { - open_eth_int_source_write(s, - s->regs[INT_SOURCE] | INT_SOURCE_RXB); - } - } - return size; -} - -static NetClientInfo net_open_eth_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .can_receive = open_eth_can_receive, - .receive = open_eth_receive, - .link_status_changed = open_eth_set_link_status, -}; - -static void open_eth_start_xmit(OpenEthState *s, desc *tx) -{ - uint8_t buf[65536]; - unsigned len = GET_FIELD(tx->len_flags, TXD_LEN); - unsigned tx_len = len; - - if ((tx->len_flags & TXD_PAD) && - tx_len < GET_REGFIELD(s, PACKETLEN, MINFL)) { - tx_len = GET_REGFIELD(s, PACKETLEN, MINFL); - } - if (!GET_REGBIT(s, MODER, HUGEN) && - tx_len > GET_REGFIELD(s, PACKETLEN, MAXFL)) { - tx_len = GET_REGFIELD(s, PACKETLEN, MAXFL); - } - - trace_open_eth_start_xmit(tx->buf_ptr, len, tx_len); - - if (len > tx_len) { - len = tx_len; - } - cpu_physical_memory_read(tx->buf_ptr, buf, len); - if (tx_len > len) { - memset(buf + len, 0, tx_len - len); - } - qemu_send_packet(qemu_get_queue(s->nic), buf, tx_len); - - if (tx->len_flags & TXD_WR) { - s->tx_desc = 0; - } else { - ++s->tx_desc; - if (s->tx_desc >= s->regs[TX_BD_NUM]) { - s->tx_desc = 0; - } - } - tx->len_flags &= ~(TXD_RD | TXD_UR | - TXD_RTRY | TXD_RL | TXD_LC | TXD_DF | TXD_CS); - if (tx->len_flags & TXD_IRQ) { - open_eth_int_source_write(s, s->regs[INT_SOURCE] | INT_SOURCE_TXB); - } - -} - -static void open_eth_check_start_xmit(OpenEthState *s) -{ - desc *tx = tx_desc(s); - if (GET_REGBIT(s, MODER, TXEN) && s->regs[TX_BD_NUM] > 0 && - (tx->len_flags & TXD_RD) && - GET_FIELD(tx->len_flags, TXD_LEN) > 4) { - open_eth_start_xmit(s, tx); - } -} - -static uint64_t open_eth_reg_read(void *opaque, - hwaddr addr, unsigned int size) -{ - static uint32_t (*reg_read[REG_MAX])(OpenEthState *s) = { - }; - OpenEthState *s = opaque; - unsigned idx = addr / 4; - uint64_t v = 0; - - if (idx < REG_MAX) { - if (reg_read[idx]) { - v = reg_read[idx](s); - } else { - v = s->regs[idx]; - } - } - trace_open_eth_reg_read((uint32_t)addr, (uint32_t)v); - return v; -} - -static void open_eth_notify_can_receive(OpenEthState *s) -{ - NetClientState *nc = qemu_get_queue(s->nic); - - if (open_eth_can_receive(nc)) { - qemu_flush_queued_packets(nc); - } -} - -static void open_eth_ro(OpenEthState *s, uint32_t val) -{ -} - -static void open_eth_moder_host_write(OpenEthState *s, uint32_t val) -{ - uint32_t set = val & ~s->regs[MODER]; - - if (set & MODER_RST) { - open_eth_reset(s); - } - - s->regs[MODER] = val; - - if (set & MODER_RXEN) { - s->rx_desc = s->regs[TX_BD_NUM]; - open_eth_notify_can_receive(s); - } - if (set & MODER_TXEN) { - s->tx_desc = 0; - open_eth_check_start_xmit(s); - } -} - -static void open_eth_int_source_host_write(OpenEthState *s, uint32_t val) -{ - uint32_t old = s->regs[INT_SOURCE]; - - s->regs[INT_SOURCE] &= ~val; - open_eth_update_irq(s, old & s->regs[INT_MASK], - s->regs[INT_SOURCE] & s->regs[INT_MASK]); -} - -static void open_eth_int_mask_host_write(OpenEthState *s, uint32_t val) -{ - uint32_t old = s->regs[INT_MASK]; - - s->regs[INT_MASK] = val; - open_eth_update_irq(s, s->regs[INT_SOURCE] & old, - s->regs[INT_SOURCE] & s->regs[INT_MASK]); -} - -static void open_eth_tx_bd_num_host_write(OpenEthState *s, uint32_t val) -{ - if (val < 0x80) { - bool enable = s->regs[TX_BD_NUM] == 0x80; - - s->regs[TX_BD_NUM] = val; - if (enable) { - open_eth_notify_can_receive(s); - } - } -} - -static void open_eth_mii_command_host_write(OpenEthState *s, uint32_t val) -{ - unsigned fiad = GET_REGFIELD(s, MIIADDRESS, FIAD); - unsigned rgad = GET_REGFIELD(s, MIIADDRESS, RGAD); - - if (val & MIICOMMAND_WCTRLDATA) { - if (fiad == DEFAULT_PHY) { - mii_write_host(&s->mii, rgad, - GET_REGFIELD(s, MIITX_DATA, CTRLDATA)); - } - } - if (val & MIICOMMAND_RSTAT) { - if (fiad == DEFAULT_PHY) { - SET_REGFIELD(s, MIIRX_DATA, PRSD, - mii_read_host(&s->mii, rgad)); - } else { - s->regs[MIIRX_DATA] = 0xffff; - } - SET_REGFIELD(s, MIISTATUS, LINKFAIL, qemu_get_queue(s->nic)->link_down); - } -} - -static void open_eth_mii_tx_host_write(OpenEthState *s, uint32_t val) -{ - SET_REGFIELD(s, MIITX_DATA, CTRLDATA, val); - if (GET_REGFIELD(s, MIIADDRESS, FIAD) == DEFAULT_PHY) { - mii_write_host(&s->mii, GET_REGFIELD(s, MIIADDRESS, RGAD), - GET_REGFIELD(s, MIITX_DATA, CTRLDATA)); - } -} - -static void open_eth_reg_write(void *opaque, - hwaddr addr, uint64_t val, unsigned int size) -{ - static void (*reg_write[REG_MAX])(OpenEthState *s, uint32_t val) = { - [MODER] = open_eth_moder_host_write, - [INT_SOURCE] = open_eth_int_source_host_write, - [INT_MASK] = open_eth_int_mask_host_write, - [TX_BD_NUM] = open_eth_tx_bd_num_host_write, - [MIICOMMAND] = open_eth_mii_command_host_write, - [MIITX_DATA] = open_eth_mii_tx_host_write, - [MIISTATUS] = open_eth_ro, - }; - OpenEthState *s = opaque; - unsigned idx = addr / 4; - - if (idx < REG_MAX) { - trace_open_eth_reg_write((uint32_t)addr, (uint32_t)val); - if (reg_write[idx]) { - reg_write[idx](s, val); - } else { - s->regs[idx] = val; - } - } -} - -static uint64_t open_eth_desc_read(void *opaque, - hwaddr addr, unsigned int size) -{ - OpenEthState *s = opaque; - uint64_t v = 0; - - addr &= 0x3ff; - memcpy(&v, (uint8_t *)s->desc + addr, size); - trace_open_eth_desc_read((uint32_t)addr, (uint32_t)v); - return v; -} - -static void open_eth_desc_write(void *opaque, - hwaddr addr, uint64_t val, unsigned int size) -{ - OpenEthState *s = opaque; - - addr &= 0x3ff; - trace_open_eth_desc_write((uint32_t)addr, (uint32_t)val); - memcpy((uint8_t *)s->desc + addr, &val, size); - open_eth_check_start_xmit(s); -} - - -static const MemoryRegionOps open_eth_reg_ops = { - .read = open_eth_reg_read, - .write = open_eth_reg_write, -}; - -static const MemoryRegionOps open_eth_desc_ops = { - .read = open_eth_desc_read, - .write = open_eth_desc_write, -}; - -static int sysbus_open_eth_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - OpenEthState *s = OPEN_ETH(dev); - - memory_region_init_io(&s->reg_io, OBJECT(dev), &open_eth_reg_ops, s, - "open_eth.regs", 0x54); - sysbus_init_mmio(sbd, &s->reg_io); - - memory_region_init_io(&s->desc_io, OBJECT(dev), &open_eth_desc_ops, s, - "open_eth.desc", 0x400); - sysbus_init_mmio(sbd, &s->desc_io); - - sysbus_init_irq(sbd, &s->irq); - - s->nic = qemu_new_nic(&net_open_eth_info, &s->conf, - object_get_typename(OBJECT(s)), dev->id, s); - return 0; -} - -static void qdev_open_eth_reset(DeviceState *dev) -{ - OpenEthState *d = OPEN_ETH(dev); - - open_eth_reset(d); -} - -static Property open_eth_properties[] = { - DEFINE_NIC_PROPERTIES(OpenEthState, conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void open_eth_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = sysbus_open_eth_init; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->desc = "Opencores 10/100 Mbit Ethernet"; - dc->reset = qdev_open_eth_reset; - dc->props = open_eth_properties; -} - -static const TypeInfo open_eth_info = { - .name = TYPE_OPEN_ETH, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(OpenEthState), - .class_init = open_eth_class_init, -}; - -static void open_eth_register_types(void) -{ - type_register_static(&open_eth_info); -} - -type_init(open_eth_register_types) diff --git a/qemu/hw/net/pcnet-pci.c b/qemu/hw/net/pcnet-pci.c deleted file mode 100644 index 595439a65..000000000 --- a/qemu/hw/net/pcnet-pci.c +++ /dev/null @@ -1,375 +0,0 @@ -/* - * QEMU AMD PC-Net II (Am79C970A) PCI emulation - * - * Copyright (c) 2004 Antony T Curtis - * - * 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 software was written to be compatible with the specification: - * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet - * 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" -#include "qemu/timer.h" -#include "sysemu/dma.h" -#include "sysemu/sysemu.h" -#include "trace.h" - -#include "pcnet.h" - -//#define PCNET_DEBUG -//#define PCNET_DEBUG_IO -//#define PCNET_DEBUG_BCR -//#define PCNET_DEBUG_CSR -//#define PCNET_DEBUG_RMD -//#define PCNET_DEBUG_TMD -//#define PCNET_DEBUG_MATCH - -#define TYPE_PCI_PCNET "pcnet" - -#define PCI_PCNET(obj) \ - OBJECT_CHECK(PCIPCNetState, (obj), TYPE_PCI_PCNET) - -typedef struct { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - PCNetState state; - MemoryRegion io_bar; -} PCIPCNetState; - -static void pcnet_aprom_writeb(void *opaque, uint32_t addr, uint32_t val) -{ - PCNetState *s = opaque; - - trace_pcnet_aprom_writeb(opaque, addr, val); - if (BCR_APROMWE(s)) { - s->prom[addr & 15] = val; - } -} - -static uint32_t pcnet_aprom_readb(void *opaque, uint32_t addr) -{ - PCNetState *s = opaque; - uint32_t val = s->prom[addr & 15]; - - trace_pcnet_aprom_readb(opaque, addr, val); - return val; -} - -static uint64_t pcnet_ioport_read(void *opaque, hwaddr addr, - unsigned size) -{ - PCNetState *d = opaque; - - trace_pcnet_ioport_read(opaque, addr, size); - if (addr < 0x10) { - if (!BCR_DWIO(d) && size == 1) { - return pcnet_aprom_readb(d, addr); - } else if (!BCR_DWIO(d) && (addr & 1) == 0 && size == 2) { - return pcnet_aprom_readb(d, addr) | - (pcnet_aprom_readb(d, addr + 1) << 8); - } else if (BCR_DWIO(d) && (addr & 3) == 0 && size == 4) { - return pcnet_aprom_readb(d, addr) | - (pcnet_aprom_readb(d, addr + 1) << 8) | - (pcnet_aprom_readb(d, addr + 2) << 16) | - (pcnet_aprom_readb(d, addr + 3) << 24); - } - } else { - if (size == 2) { - return pcnet_ioport_readw(d, addr); - } else if (size == 4) { - return pcnet_ioport_readl(d, addr); - } - } - return ((uint64_t)1 << (size * 8)) - 1; -} - -static void pcnet_ioport_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - PCNetState *d = opaque; - - trace_pcnet_ioport_write(opaque, addr, data, size); - if (addr < 0x10) { - if (!BCR_DWIO(d) && size == 1) { - pcnet_aprom_writeb(d, addr, data); - } else if (!BCR_DWIO(d) && (addr & 1) == 0 && size == 2) { - pcnet_aprom_writeb(d, addr, data & 0xff); - pcnet_aprom_writeb(d, addr + 1, data >> 8); - } else if (BCR_DWIO(d) && (addr & 3) == 0 && size == 4) { - pcnet_aprom_writeb(d, addr, data & 0xff); - pcnet_aprom_writeb(d, addr + 1, (data >> 8) & 0xff); - pcnet_aprom_writeb(d, addr + 2, (data >> 16) & 0xff); - pcnet_aprom_writeb(d, addr + 3, data >> 24); - } - } else { - if (size == 2) { - pcnet_ioport_writew(d, addr, data); - } else if (size == 4) { - pcnet_ioport_writel(d, addr, data); - } - } -} - -static const MemoryRegionOps pcnet_io_ops = { - .read = pcnet_ioport_read, - .write = pcnet_ioport_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void pcnet_mmio_writeb(void *opaque, hwaddr addr, uint32_t val) -{ - PCNetState *d = opaque; - - trace_pcnet_mmio_writeb(opaque, addr, val); - if (!(addr & 0x10)) - pcnet_aprom_writeb(d, addr & 0x0f, val); -} - -static uint32_t pcnet_mmio_readb(void *opaque, hwaddr addr) -{ - PCNetState *d = opaque; - uint32_t val = -1; - - if (!(addr & 0x10)) - val = pcnet_aprom_readb(d, addr & 0x0f); - trace_pcnet_mmio_readb(opaque, addr, val); - return val; -} - -static void pcnet_mmio_writew(void *opaque, hwaddr addr, uint32_t val) -{ - PCNetState *d = opaque; - - trace_pcnet_mmio_writew(opaque, addr, val); - if (addr & 0x10) - pcnet_ioport_writew(d, addr & 0x0f, val); - else { - addr &= 0x0f; - pcnet_aprom_writeb(d, addr, val & 0xff); - pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8); - } -} - -static uint32_t pcnet_mmio_readw(void *opaque, hwaddr addr) -{ - PCNetState *d = opaque; - uint32_t val = -1; - - if (addr & 0x10) - val = pcnet_ioport_readw(d, addr & 0x0f); - else { - addr &= 0x0f; - val = pcnet_aprom_readb(d, addr+1); - val <<= 8; - val |= pcnet_aprom_readb(d, addr); - } - trace_pcnet_mmio_readw(opaque, addr, val); - return val; -} - -static void pcnet_mmio_writel(void *opaque, hwaddr addr, uint32_t val) -{ - PCNetState *d = opaque; - - trace_pcnet_mmio_writel(opaque, addr, val); - if (addr & 0x10) - pcnet_ioport_writel(d, addr & 0x0f, val); - else { - addr &= 0x0f; - pcnet_aprom_writeb(d, addr, val & 0xff); - pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8); - pcnet_aprom_writeb(d, addr+2, (val & 0xff0000) >> 16); - pcnet_aprom_writeb(d, addr+3, (val & 0xff000000) >> 24); - } -} - -static uint32_t pcnet_mmio_readl(void *opaque, hwaddr addr) -{ - PCNetState *d = opaque; - uint32_t val; - - if (addr & 0x10) - val = pcnet_ioport_readl(d, addr & 0x0f); - else { - addr &= 0x0f; - val = pcnet_aprom_readb(d, addr+3); - val <<= 8; - val |= pcnet_aprom_readb(d, addr+2); - val <<= 8; - val |= pcnet_aprom_readb(d, addr+1); - val <<= 8; - val |= pcnet_aprom_readb(d, addr); - } - trace_pcnet_mmio_readl(opaque, addr, val); - return val; -} - -static const VMStateDescription vmstate_pci_pcnet = { - .name = "pcnet", - .version_id = 3, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, PCIPCNetState), - VMSTATE_STRUCT(state, PCIPCNetState, 0, vmstate_pcnet, PCNetState), - VMSTATE_END_OF_LIST() - } -}; - -/* PCI interface */ - -static const MemoryRegionOps pcnet_mmio_ops = { - .old_mmio = { - .read = { pcnet_mmio_readb, pcnet_mmio_readw, pcnet_mmio_readl }, - .write = { pcnet_mmio_writeb, pcnet_mmio_writew, pcnet_mmio_writel }, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void pci_physical_memory_write(void *dma_opaque, hwaddr addr, - uint8_t *buf, int len, int do_bswap) -{ - pci_dma_write(dma_opaque, addr, buf, len); -} - -static void pci_physical_memory_read(void *dma_opaque, hwaddr addr, - uint8_t *buf, int len, int do_bswap) -{ - pci_dma_read(dma_opaque, addr, buf, len); -} - -static void pci_pcnet_uninit(PCIDevice *dev) -{ - PCIPCNetState *d = PCI_PCNET(dev); - - qemu_free_irq(d->state.irq); - timer_del(d->state.poll_timer); - timer_free(d->state.poll_timer); - qemu_del_nic(d->state.nic); -} - -static NetClientInfo net_pci_pcnet_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = pcnet_receive, - .link_status_changed = pcnet_set_link_status, -}; - -static void pci_pcnet_realize(PCIDevice *pci_dev, Error **errp) -{ - PCIPCNetState *d = PCI_PCNET(pci_dev); - PCNetState *s = &d->state; - uint8_t *pci_conf; - -#if 0 - printf("sizeof(RMD)=%d, sizeof(TMD)=%d\n", - sizeof(struct pcnet_RMD), sizeof(struct pcnet_TMD)); -#endif - - pci_conf = pci_dev->config; - - pci_set_word(pci_conf + PCI_STATUS, - PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); - - pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, 0x0); - pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, 0x0); - - pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */ - pci_conf[PCI_MIN_GNT] = 0x06; - pci_conf[PCI_MAX_LAT] = 0xff; - - /* Handler for memory-mapped I/O */ - memory_region_init_io(&d->state.mmio, OBJECT(d), &pcnet_mmio_ops, s, - "pcnet-mmio", PCNET_PNPMMIO_SIZE); - - memory_region_init_io(&d->io_bar, OBJECT(d), &pcnet_io_ops, s, "pcnet-io", - PCNET_IOPORT_SIZE); - pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->io_bar); - - pci_register_bar(pci_dev, 1, 0, &s->mmio); - - s->irq = pci_allocate_irq(pci_dev); - s->phys_mem_read = pci_physical_memory_read; - s->phys_mem_write = pci_physical_memory_write; - s->dma_opaque = pci_dev; - - pcnet_common_init(DEVICE(pci_dev), s, &net_pci_pcnet_info); -} - -static void pci_reset(DeviceState *dev) -{ - PCIPCNetState *d = PCI_PCNET(dev); - - pcnet_h_reset(&d->state); -} - -static void pcnet_instance_init(Object *obj) -{ - PCIPCNetState *d = PCI_PCNET(obj); - PCNetState *s = &d->state; - - device_add_bootindex_property(obj, &s->conf.bootindex, - "bootindex", "/ethernet-phy@0", - DEVICE(obj), NULL); -} - -static Property pcnet_properties[] = { - DEFINE_NIC_PROPERTIES(PCIPCNetState, state.conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pcnet_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = pci_pcnet_realize; - k->exit = pci_pcnet_uninit; - k->romfile = "efi-pcnet.rom", - k->vendor_id = PCI_VENDOR_ID_AMD; - k->device_id = PCI_DEVICE_ID_AMD_LANCE; - k->revision = 0x10; - k->class_id = PCI_CLASS_NETWORK_ETHERNET; - dc->reset = pci_reset; - dc->vmsd = &vmstate_pci_pcnet; - dc->props = pcnet_properties; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); -} - -static const TypeInfo pcnet_info = { - .name = TYPE_PCI_PCNET, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIPCNetState), - .class_init = pcnet_class_init, - .instance_init = pcnet_instance_init, -}; - -static void pci_pcnet_register_types(void) -{ - type_register_static(&pcnet_info); -} - -type_init(pci_pcnet_register_types) diff --git a/qemu/hw/net/pcnet.c b/qemu/hw/net/pcnet.c deleted file mode 100644 index 198a01f92..000000000 --- a/qemu/hw/net/pcnet.c +++ /dev/null @@ -1,1761 +0,0 @@ -/* - * QEMU AMD PC-Net II (Am79C970A) emulation - * - * Copyright (c) 2004 Antony T Curtis - * - * 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 software was written to be compatible with the specification: - * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet - * AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000 - */ - -/* - * On Sparc32, this is the Lance (Am7990) part of chip STP2000 (Master I/O), also - * produced as NCR89C100. See - * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt - * and - * 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" -#include "qemu/sockets.h" -#include "sysemu/sysemu.h" -#include "trace.h" - -#include "pcnet.h" - -//#define PCNET_DEBUG -//#define PCNET_DEBUG_IO -//#define PCNET_DEBUG_BCR -//#define PCNET_DEBUG_CSR -//#define PCNET_DEBUG_RMD -//#define PCNET_DEBUG_TMD -//#define PCNET_DEBUG_MATCH - - -struct qemu_ether_header { - uint8_t ether_dhost[6]; - uint8_t ether_shost[6]; - uint16_t ether_type; -}; - -#define CSR_INIT(S) !!(((S)->csr[0])&0x0001) -#define CSR_STRT(S) !!(((S)->csr[0])&0x0002) -#define CSR_STOP(S) !!(((S)->csr[0])&0x0004) -#define CSR_TDMD(S) !!(((S)->csr[0])&0x0008) -#define CSR_TXON(S) !!(((S)->csr[0])&0x0010) -#define CSR_RXON(S) !!(((S)->csr[0])&0x0020) -#define CSR_INEA(S) !!(((S)->csr[0])&0x0040) -#define CSR_BSWP(S) !!(((S)->csr[3])&0x0004) -#define CSR_LAPPEN(S) !!(((S)->csr[3])&0x0020) -#define CSR_DXSUFLO(S) !!(((S)->csr[3])&0x0040) -#define CSR_ASTRP_RCV(S) !!(((S)->csr[4])&0x0800) -#define CSR_DPOLL(S) !!(((S)->csr[4])&0x1000) -#define CSR_SPND(S) !!(((S)->csr[5])&0x0001) -#define CSR_LTINTEN(S) !!(((S)->csr[5])&0x4000) -#define CSR_TOKINTD(S) !!(((S)->csr[5])&0x8000) -#define CSR_DRX(S) !!(((S)->csr[15])&0x0001) -#define CSR_DTX(S) !!(((S)->csr[15])&0x0002) -#define CSR_LOOP(S) !!(((S)->csr[15])&0x0004) -#define CSR_DXMTFCS(S) !!(((S)->csr[15])&0x0008) -#define CSR_INTL(S) !!(((S)->csr[15])&0x0040) -#define CSR_DRCVPA(S) !!(((S)->csr[15])&0x2000) -#define CSR_DRCVBC(S) !!(((S)->csr[15])&0x4000) -#define CSR_PROM(S) !!(((S)->csr[15])&0x8000) - -#define CSR_CRBC(S) ((S)->csr[40]) -#define CSR_CRST(S) ((S)->csr[41]) -#define CSR_CXBC(S) ((S)->csr[42]) -#define CSR_CXST(S) ((S)->csr[43]) -#define CSR_NRBC(S) ((S)->csr[44]) -#define CSR_NRST(S) ((S)->csr[45]) -#define CSR_POLL(S) ((S)->csr[46]) -#define CSR_PINT(S) ((S)->csr[47]) -#define CSR_RCVRC(S) ((S)->csr[72]) -#define CSR_XMTRC(S) ((S)->csr[74]) -#define CSR_RCVRL(S) ((S)->csr[76]) -#define CSR_XMTRL(S) ((S)->csr[78]) -#define CSR_MISSC(S) ((S)->csr[112]) - -#define CSR_IADR(S) ((S)->csr[ 1] | ((uint32_t)(S)->csr[ 2] << 16)) -#define CSR_CRBA(S) ((S)->csr[18] | ((uint32_t)(S)->csr[19] << 16)) -#define CSR_CXBA(S) ((S)->csr[20] | ((uint32_t)(S)->csr[21] << 16)) -#define CSR_NRBA(S) ((S)->csr[22] | ((uint32_t)(S)->csr[23] << 16)) -#define CSR_BADR(S) ((S)->csr[24] | ((uint32_t)(S)->csr[25] << 16)) -#define CSR_NRDA(S) ((S)->csr[26] | ((uint32_t)(S)->csr[27] << 16)) -#define CSR_CRDA(S) ((S)->csr[28] | ((uint32_t)(S)->csr[29] << 16)) -#define CSR_BADX(S) ((S)->csr[30] | ((uint32_t)(S)->csr[31] << 16)) -#define CSR_NXDA(S) ((S)->csr[32] | ((uint32_t)(S)->csr[33] << 16)) -#define CSR_CXDA(S) ((S)->csr[34] | ((uint32_t)(S)->csr[35] << 16)) -#define CSR_NNRD(S) ((S)->csr[36] | ((uint32_t)(S)->csr[37] << 16)) -#define CSR_NNXD(S) ((S)->csr[38] | ((uint32_t)(S)->csr[39] << 16)) -#define CSR_PXDA(S) ((S)->csr[60] | ((uint32_t)(S)->csr[61] << 16)) -#define CSR_NXBA(S) ((S)->csr[64] | ((uint32_t)(S)->csr[65] << 16)) - -#define PHYSADDR(S,A) \ - (BCR_SSIZE32(S) ? (A) : (A) | ((0xff00 & (uint32_t)(S)->csr[2])<<16)) - -struct pcnet_initblk16 { - uint16_t mode; - uint16_t padr[3]; - uint16_t ladrf[4]; - uint32_t rdra; - uint32_t tdra; -}; - -struct pcnet_initblk32 { - uint16_t mode; - uint8_t rlen; - uint8_t tlen; - uint16_t padr[3]; - uint16_t _res; - uint16_t ladrf[4]; - uint32_t rdra; - uint32_t tdra; -}; - -struct pcnet_TMD { - uint32_t tbadr; - int16_t length; - int16_t status; - uint32_t misc; - uint32_t res; -}; - -#define TMDL_BCNT_MASK 0x0fff -#define TMDL_BCNT_SH 0 -#define TMDL_ONES_MASK 0xf000 -#define TMDL_ONES_SH 12 - -#define TMDS_BPE_MASK 0x0080 -#define TMDS_BPE_SH 7 -#define TMDS_ENP_MASK 0x0100 -#define TMDS_ENP_SH 8 -#define TMDS_STP_MASK 0x0200 -#define TMDS_STP_SH 9 -#define TMDS_DEF_MASK 0x0400 -#define TMDS_DEF_SH 10 -#define TMDS_ONE_MASK 0x0800 -#define TMDS_ONE_SH 11 -#define TMDS_LTINT_MASK 0x1000 -#define TMDS_LTINT_SH 12 -#define TMDS_NOFCS_MASK 0x2000 -#define TMDS_NOFCS_SH 13 -#define TMDS_ADDFCS_MASK TMDS_NOFCS_MASK -#define TMDS_ADDFCS_SH TMDS_NOFCS_SH -#define TMDS_ERR_MASK 0x4000 -#define TMDS_ERR_SH 14 -#define TMDS_OWN_MASK 0x8000 -#define TMDS_OWN_SH 15 - -#define TMDM_TRC_MASK 0x0000000f -#define TMDM_TRC_SH 0 -#define TMDM_TDR_MASK 0x03ff0000 -#define TMDM_TDR_SH 16 -#define TMDM_RTRY_MASK 0x04000000 -#define TMDM_RTRY_SH 26 -#define TMDM_LCAR_MASK 0x08000000 -#define TMDM_LCAR_SH 27 -#define TMDM_LCOL_MASK 0x10000000 -#define TMDM_LCOL_SH 28 -#define TMDM_EXDEF_MASK 0x20000000 -#define TMDM_EXDEF_SH 29 -#define TMDM_UFLO_MASK 0x40000000 -#define TMDM_UFLO_SH 30 -#define TMDM_BUFF_MASK 0x80000000 -#define TMDM_BUFF_SH 31 - -struct pcnet_RMD { - uint32_t rbadr; - int16_t buf_length; - int16_t status; - uint32_t msg_length; - uint32_t res; -}; - -#define RMDL_BCNT_MASK 0x0fff -#define RMDL_BCNT_SH 0 -#define RMDL_ONES_MASK 0xf000 -#define RMDL_ONES_SH 12 - -#define RMDS_BAM_MASK 0x0010 -#define RMDS_BAM_SH 4 -#define RMDS_LFAM_MASK 0x0020 -#define RMDS_LFAM_SH 5 -#define RMDS_PAM_MASK 0x0040 -#define RMDS_PAM_SH 6 -#define RMDS_BPE_MASK 0x0080 -#define RMDS_BPE_SH 7 -#define RMDS_ENP_MASK 0x0100 -#define RMDS_ENP_SH 8 -#define RMDS_STP_MASK 0x0200 -#define RMDS_STP_SH 9 -#define RMDS_BUFF_MASK 0x0400 -#define RMDS_BUFF_SH 10 -#define RMDS_CRC_MASK 0x0800 -#define RMDS_CRC_SH 11 -#define RMDS_OFLO_MASK 0x1000 -#define RMDS_OFLO_SH 12 -#define RMDS_FRAM_MASK 0x2000 -#define RMDS_FRAM_SH 13 -#define RMDS_ERR_MASK 0x4000 -#define RMDS_ERR_SH 14 -#define RMDS_OWN_MASK 0x8000 -#define RMDS_OWN_SH 15 - -#define RMDM_MCNT_MASK 0x00000fff -#define RMDM_MCNT_SH 0 -#define RMDM_ZEROS_MASK 0x0000f000 -#define RMDM_ZEROS_SH 12 -#define RMDM_RPC_MASK 0x00ff0000 -#define RMDM_RPC_SH 16 -#define RMDM_RCC_MASK 0xff000000 -#define RMDM_RCC_SH 24 - -#define SET_FIELD(regp, name, field, value) \ - (*(regp) = (*(regp) & ~(name ## _ ## field ## _MASK)) \ - | ((value) << name ## _ ## field ## _SH)) - -#define GET_FIELD(reg, name, field) \ - (((reg) & name ## _ ## field ## _MASK) >> name ## _ ## field ## _SH) - -#define PRINT_TMD(T) printf( \ - "TMD0 : TBADR=0x%08x\n" \ - "TMD1 : OWN=%d, ERR=%d, FCS=%d, LTI=%d, " \ - "ONE=%d, DEF=%d, STP=%d, ENP=%d,\n" \ - " BPE=%d, BCNT=%d\n" \ - "TMD2 : BUF=%d, UFL=%d, EXD=%d, LCO=%d, " \ - "LCA=%d, RTR=%d,\n" \ - " TDR=%d, TRC=%d\n", \ - (T)->tbadr, \ - GET_FIELD((T)->status, TMDS, OWN), \ - GET_FIELD((T)->status, TMDS, ERR), \ - GET_FIELD((T)->status, TMDS, NOFCS), \ - GET_FIELD((T)->status, TMDS, LTINT), \ - GET_FIELD((T)->status, TMDS, ONE), \ - GET_FIELD((T)->status, TMDS, DEF), \ - GET_FIELD((T)->status, TMDS, STP), \ - GET_FIELD((T)->status, TMDS, ENP), \ - GET_FIELD((T)->status, TMDS, BPE), \ - 4096-GET_FIELD((T)->length, TMDL, BCNT), \ - GET_FIELD((T)->misc, TMDM, BUFF), \ - GET_FIELD((T)->misc, TMDM, UFLO), \ - GET_FIELD((T)->misc, TMDM, EXDEF), \ - GET_FIELD((T)->misc, TMDM, LCOL), \ - GET_FIELD((T)->misc, TMDM, LCAR), \ - GET_FIELD((T)->misc, TMDM, RTRY), \ - GET_FIELD((T)->misc, TMDM, TDR), \ - GET_FIELD((T)->misc, TMDM, TRC)) - -#define PRINT_RMD(R) printf( \ - "RMD0 : RBADR=0x%08x\n" \ - "RMD1 : OWN=%d, ERR=%d, FRAM=%d, OFLO=%d, " \ - "CRC=%d, BUFF=%d, STP=%d, ENP=%d,\n " \ - "BPE=%d, PAM=%d, LAFM=%d, BAM=%d, ONES=%d, BCNT=%d\n" \ - "RMD2 : RCC=%d, RPC=%d, MCNT=%d, ZEROS=%d\n", \ - (R)->rbadr, \ - GET_FIELD((R)->status, RMDS, OWN), \ - GET_FIELD((R)->status, RMDS, ERR), \ - GET_FIELD((R)->status, RMDS, FRAM), \ - GET_FIELD((R)->status, RMDS, OFLO), \ - GET_FIELD((R)->status, RMDS, CRC), \ - GET_FIELD((R)->status, RMDS, BUFF), \ - GET_FIELD((R)->status, RMDS, STP), \ - GET_FIELD((R)->status, RMDS, ENP), \ - GET_FIELD((R)->status, RMDS, BPE), \ - GET_FIELD((R)->status, RMDS, PAM), \ - GET_FIELD((R)->status, RMDS, LFAM), \ - GET_FIELD((R)->status, RMDS, BAM), \ - GET_FIELD((R)->buf_length, RMDL, ONES), \ - 4096-GET_FIELD((R)->buf_length, RMDL, BCNT), \ - GET_FIELD((R)->msg_length, RMDM, RCC), \ - GET_FIELD((R)->msg_length, RMDM, RPC), \ - GET_FIELD((R)->msg_length, RMDM, MCNT), \ - GET_FIELD((R)->msg_length, RMDM, ZEROS)) - -static inline void pcnet_tmd_load(PCNetState *s, struct pcnet_TMD *tmd, - hwaddr addr) -{ - if (!BCR_SSIZE32(s)) { - struct { - uint32_t tbadr; - int16_t length; - int16_t status; - } xda; - s->phys_mem_read(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0); - tmd->tbadr = le32_to_cpu(xda.tbadr) & 0xffffff; - tmd->length = le16_to_cpu(xda.length); - tmd->status = (le32_to_cpu(xda.tbadr) >> 16) & 0xff00; - tmd->misc = le16_to_cpu(xda.status) << 16; - tmd->res = 0; - } else { - s->phys_mem_read(s->dma_opaque, addr, (void *)tmd, sizeof(*tmd), 0); - le32_to_cpus(&tmd->tbadr); - le16_to_cpus((uint16_t *)&tmd->length); - le16_to_cpus((uint16_t *)&tmd->status); - le32_to_cpus(&tmd->misc); - le32_to_cpus(&tmd->res); - if (BCR_SWSTYLE(s) == 3) { - uint32_t tmp = tmd->tbadr; - tmd->tbadr = tmd->misc; - tmd->misc = tmp; - } - } -} - -static inline void pcnet_tmd_store(PCNetState *s, const struct pcnet_TMD *tmd, - hwaddr addr) -{ - if (!BCR_SSIZE32(s)) { - struct { - uint32_t tbadr; - int16_t length; - int16_t status; - } xda; - xda.tbadr = cpu_to_le32((tmd->tbadr & 0xffffff) | - ((tmd->status & 0xff00) << 16)); - xda.length = cpu_to_le16(tmd->length); - xda.status = cpu_to_le16(tmd->misc >> 16); - s->phys_mem_write(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0); - } else { - struct { - uint32_t tbadr; - int16_t length; - int16_t status; - uint32_t misc; - uint32_t res; - } xda; - xda.tbadr = cpu_to_le32(tmd->tbadr); - xda.length = cpu_to_le16(tmd->length); - xda.status = cpu_to_le16(tmd->status); - xda.misc = cpu_to_le32(tmd->misc); - xda.res = cpu_to_le32(tmd->res); - if (BCR_SWSTYLE(s) == 3) { - uint32_t tmp = xda.tbadr; - xda.tbadr = xda.misc; - xda.misc = tmp; - } - s->phys_mem_write(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0); - } -} - -static inline void pcnet_rmd_load(PCNetState *s, struct pcnet_RMD *rmd, - hwaddr addr) -{ - if (!BCR_SSIZE32(s)) { - struct { - uint32_t rbadr; - int16_t buf_length; - int16_t msg_length; - } rda; - s->phys_mem_read(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0); - rmd->rbadr = le32_to_cpu(rda.rbadr) & 0xffffff; - rmd->buf_length = le16_to_cpu(rda.buf_length); - rmd->status = (le32_to_cpu(rda.rbadr) >> 16) & 0xff00; - rmd->msg_length = le16_to_cpu(rda.msg_length); - rmd->res = 0; - } else { - s->phys_mem_read(s->dma_opaque, addr, (void *)rmd, sizeof(*rmd), 0); - le32_to_cpus(&rmd->rbadr); - le16_to_cpus((uint16_t *)&rmd->buf_length); - le16_to_cpus((uint16_t *)&rmd->status); - le32_to_cpus(&rmd->msg_length); - le32_to_cpus(&rmd->res); - if (BCR_SWSTYLE(s) == 3) { - uint32_t tmp = rmd->rbadr; - rmd->rbadr = rmd->msg_length; - rmd->msg_length = tmp; - } - } -} - -static inline void pcnet_rmd_store(PCNetState *s, struct pcnet_RMD *rmd, - hwaddr addr) -{ - if (!BCR_SSIZE32(s)) { - struct { - uint32_t rbadr; - int16_t buf_length; - int16_t msg_length; - } rda; - rda.rbadr = cpu_to_le32((rmd->rbadr & 0xffffff) | - ((rmd->status & 0xff00) << 16)); - rda.buf_length = cpu_to_le16(rmd->buf_length); - rda.msg_length = cpu_to_le16(rmd->msg_length); - s->phys_mem_write(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0); - } else { - struct { - uint32_t rbadr; - int16_t buf_length; - int16_t status; - uint32_t msg_length; - uint32_t res; - } rda; - rda.rbadr = cpu_to_le32(rmd->rbadr); - rda.buf_length = cpu_to_le16(rmd->buf_length); - rda.status = cpu_to_le16(rmd->status); - rda.msg_length = cpu_to_le32(rmd->msg_length); - rda.res = cpu_to_le32(rmd->res); - if (BCR_SWSTYLE(s) == 3) { - uint32_t tmp = rda.rbadr; - rda.rbadr = rda.msg_length; - rda.msg_length = tmp; - } - s->phys_mem_write(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0); - } -} - - -#define TMDLOAD(TMD,ADDR) pcnet_tmd_load(s,TMD,ADDR) - -#define TMDSTORE(TMD,ADDR) pcnet_tmd_store(s,TMD,ADDR) - -#define RMDLOAD(RMD,ADDR) pcnet_rmd_load(s,RMD,ADDR) - -#define RMDSTORE(RMD,ADDR) pcnet_rmd_store(s,RMD,ADDR) - -#if 1 - -#define CHECK_RMD(ADDR,RES) do { \ - struct pcnet_RMD rmd; \ - RMDLOAD(&rmd,(ADDR)); \ - (RES) |= (GET_FIELD(rmd.buf_length, RMDL, ONES) != 15) \ - || (GET_FIELD(rmd.msg_length, RMDM, ZEROS) != 0); \ -} while (0) - -#define CHECK_TMD(ADDR,RES) do { \ - struct pcnet_TMD tmd; \ - TMDLOAD(&tmd,(ADDR)); \ - (RES) |= (GET_FIELD(tmd.length, TMDL, ONES) != 15); \ -} while (0) - -#else - -#define CHECK_RMD(ADDR,RES) do { \ - switch (BCR_SWSTYLE(s)) { \ - case 0x00: \ - do { \ - uint16_t rda[4]; \ - s->phys_mem_read(s->dma_opaque, (ADDR), \ - (void *)&rda[0], sizeof(rda), 0); \ - (RES) |= (rda[2] & 0xf000)!=0xf000; \ - (RES) |= (rda[3] & 0xf000)!=0x0000; \ - } while (0); \ - break; \ - case 0x01: \ - case 0x02: \ - do { \ - uint32_t rda[4]; \ - s->phys_mem_read(s->dma_opaque, (ADDR), \ - (void *)&rda[0], sizeof(rda), 0); \ - (RES) |= (rda[1] & 0x0000f000L)!=0x0000f000L; \ - (RES) |= (rda[2] & 0x0000f000L)!=0x00000000L; \ - } while (0); \ - break; \ - case 0x03: \ - do { \ - uint32_t rda[4]; \ - s->phys_mem_read(s->dma_opaque, (ADDR), \ - (void *)&rda[0], sizeof(rda), 0); \ - (RES) |= (rda[0] & 0x0000f000L)!=0x00000000L; \ - (RES) |= (rda[1] & 0x0000f000L)!=0x0000f000L; \ - } while (0); \ - break; \ - } \ -} while (0) - -#define CHECK_TMD(ADDR,RES) do { \ - switch (BCR_SWSTYLE(s)) { \ - case 0x00: \ - do { \ - uint16_t xda[4]; \ - s->phys_mem_read(s->dma_opaque, (ADDR), \ - (void *)&xda[0], sizeof(xda), 0); \ - (RES) |= (xda[2] & 0xf000)!=0xf000; \ - } while (0); \ - break; \ - case 0x01: \ - case 0x02: \ - case 0x03: \ - do { \ - uint32_t xda[4]; \ - s->phys_mem_read(s->dma_opaque, (ADDR), \ - (void *)&xda[0], sizeof(xda), 0); \ - (RES) |= (xda[1] & 0x0000f000L)!=0x0000f000L; \ - } while (0); \ - break; \ - } \ -} while (0) - -#endif - -#define PRINT_PKTHDR(BUF) do { \ - struct qemu_ether_header *hdr = (void *)(BUF); \ - printf("packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, " \ - "shost=%02x:%02x:%02x:%02x:%02x:%02x, " \ - "type=0x%04x\n", \ - hdr->ether_dhost[0],hdr->ether_dhost[1],hdr->ether_dhost[2], \ - hdr->ether_dhost[3],hdr->ether_dhost[4],hdr->ether_dhost[5], \ - hdr->ether_shost[0],hdr->ether_shost[1],hdr->ether_shost[2], \ - hdr->ether_shost[3],hdr->ether_shost[4],hdr->ether_shost[5], \ - be16_to_cpu(hdr->ether_type)); \ -} while (0) - -#define MULTICAST_FILTER_LEN 8 - -static inline uint32_t lnc_mchash(const uint8_t *ether_addr) -{ -#define LNC_POLYNOMIAL 0xEDB88320UL - uint32_t crc = 0xFFFFFFFF; - int idx, bit; - uint8_t data; - - for (idx = 0; idx < 6; idx++) { - for (data = *ether_addr++, bit = 0; bit < MULTICAST_FILTER_LEN; bit++) { - crc = (crc >> 1) ^ (((crc ^ data) & 1) ? LNC_POLYNOMIAL : 0); - data >>= 1; - } - } - return crc; -#undef LNC_POLYNOMIAL -} - -#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff]) - -/* generated using the AUTODIN II polynomial - * x^32 + x^26 + x^23 + x^22 + x^16 + - * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 - */ -static const uint32_t crctab[256] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, -}; - -static inline int padr_match(PCNetState *s, const uint8_t *buf, int size) -{ - struct qemu_ether_header *hdr = (void *)buf; - uint8_t padr[6] = { - s->csr[12] & 0xff, s->csr[12] >> 8, - s->csr[13] & 0xff, s->csr[13] >> 8, - s->csr[14] & 0xff, s->csr[14] >> 8 - }; - int result = (!CSR_DRCVPA(s)) && !memcmp(hdr->ether_dhost, padr, 6); -#ifdef PCNET_DEBUG_MATCH - printf("packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, " - "padr=%02x:%02x:%02x:%02x:%02x:%02x\n", - hdr->ether_dhost[0],hdr->ether_dhost[1],hdr->ether_dhost[2], - hdr->ether_dhost[3],hdr->ether_dhost[4],hdr->ether_dhost[5], - padr[0],padr[1],padr[2],padr[3],padr[4],padr[5]); - printf("padr_match result=%d\n", result); -#endif - return result; -} - -static inline int padr_bcast(PCNetState *s, const uint8_t *buf, int size) -{ - static const uint8_t BCAST[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - struct qemu_ether_header *hdr = (void *)buf; - int result = !CSR_DRCVBC(s) && !memcmp(hdr->ether_dhost, BCAST, 6); -#ifdef PCNET_DEBUG_MATCH - printf("padr_bcast result=%d\n", result); -#endif - return result; -} - -static inline int ladr_match(PCNetState *s, const uint8_t *buf, int size) -{ - struct qemu_ether_header *hdr = (void *)buf; - if ((*(hdr->ether_dhost)&0x01) && - ((uint64_t *)&s->csr[8])[0] != 0LL) { - uint8_t ladr[8] = { - s->csr[8] & 0xff, s->csr[8] >> 8, - s->csr[9] & 0xff, s->csr[9] >> 8, - s->csr[10] & 0xff, s->csr[10] >> 8, - s->csr[11] & 0xff, s->csr[11] >> 8 - }; - int index = lnc_mchash(hdr->ether_dhost) >> 26; - return !!(ladr[index >> 3] & (1 << (index & 7))); - } - return 0; -} - -static inline hwaddr pcnet_rdra_addr(PCNetState *s, int idx) -{ - while (idx < 1) idx += CSR_RCVRL(s); - return s->rdra + ((CSR_RCVRL(s) - idx) * (BCR_SWSTYLE(s) ? 16 : 8)); -} - -static inline int64_t pcnet_get_next_poll_time(PCNetState *s, int64_t current_time) -{ - int64_t next_time = current_time + - (65536 - (CSR_SPND(s) ? 0 : CSR_POLL(s))) * 30; - if (next_time <= current_time) - next_time = current_time + 1; - return next_time; -} - -static void pcnet_poll(PCNetState *s); -static void pcnet_poll_timer(void *opaque); - -static uint32_t pcnet_csr_readw(PCNetState *s, uint32_t rap); -static void pcnet_csr_writew(PCNetState *s, uint32_t rap, uint32_t new_value); -static void pcnet_bcr_writew(PCNetState *s, uint32_t rap, uint32_t val); - -static void pcnet_s_reset(PCNetState *s) -{ - trace_pcnet_s_reset(s); - - s->rdra = 0; - s->tdra = 0; - s->rap = 0; - - s->bcr[BCR_BSBC] &= ~0x0080; - - s->csr[0] = 0x0004; - s->csr[3] = 0x0000; - s->csr[4] = 0x0115; - s->csr[5] = 0x0000; - s->csr[6] = 0x0000; - s->csr[8] = 0; - s->csr[9] = 0; - s->csr[10] = 0; - s->csr[11] = 0; - s->csr[12] = le16_to_cpu(((uint16_t *)&s->prom[0])[0]); - s->csr[13] = le16_to_cpu(((uint16_t *)&s->prom[0])[1]); - s->csr[14] = le16_to_cpu(((uint16_t *)&s->prom[0])[2]); - s->csr[15] &= 0x21c4; - s->csr[72] = 1; - s->csr[74] = 1; - s->csr[76] = 1; - s->csr[78] = 1; - s->csr[80] = 0x1410; - s->csr[88] = 0x1003; - s->csr[89] = 0x0262; - s->csr[94] = 0x0000; - s->csr[100] = 0x0200; - s->csr[103] = 0x0105; - s->csr[112] = 0x0000; - s->csr[114] = 0x0000; - s->csr[122] = 0x0000; - s->csr[124] = 0x0000; - - s->tx_busy = 0; -} - -static void pcnet_update_irq(PCNetState *s) -{ - int isr = 0; - s->csr[0] &= ~0x0080; - -#if 1 - if (((s->csr[0] & ~s->csr[3]) & 0x5f00) || - (((s->csr[4]>>1) & ~s->csr[4]) & 0x0115) || - (((s->csr[5]>>1) & s->csr[5]) & 0x0048)) -#else - if ((!(s->csr[3] & 0x4000) && !!(s->csr[0] & 0x4000)) /* BABL */ || - (!(s->csr[3] & 0x1000) && !!(s->csr[0] & 0x1000)) /* MISS */ || - (!(s->csr[3] & 0x0100) && !!(s->csr[0] & 0x0100)) /* IDON */ || - (!(s->csr[3] & 0x0200) && !!(s->csr[0] & 0x0200)) /* TINT */ || - (!(s->csr[3] & 0x0400) && !!(s->csr[0] & 0x0400)) /* RINT */ || - (!(s->csr[3] & 0x0800) && !!(s->csr[0] & 0x0800)) /* MERR */ || - (!(s->csr[4] & 0x0001) && !!(s->csr[4] & 0x0002)) /* JAB */ || - (!(s->csr[4] & 0x0004) && !!(s->csr[4] & 0x0008)) /* TXSTRT */ || - (!(s->csr[4] & 0x0010) && !!(s->csr[4] & 0x0020)) /* RCVO */ || - (!(s->csr[4] & 0x0100) && !!(s->csr[4] & 0x0200)) /* MFCO */ || - (!!(s->csr[5] & 0x0040) && !!(s->csr[5] & 0x0080)) /* EXDINT */ || - (!!(s->csr[5] & 0x0008) && !!(s->csr[5] & 0x0010)) /* MPINT */) -#endif - { - - isr = CSR_INEA(s); - s->csr[0] |= 0x0080; - } - - if (!!(s->csr[4] & 0x0080) && CSR_INEA(s)) { /* UINT */ - s->csr[4] &= ~0x0080; - s->csr[4] |= 0x0040; - s->csr[0] |= 0x0080; - isr = 1; - trace_pcnet_user_int(s); - } - -#if 1 - if (((s->csr[5]>>1) & s->csr[5]) & 0x0500) -#else - if ((!!(s->csr[5] & 0x0400) && !!(s->csr[5] & 0x0800)) /* SINT */ || - (!!(s->csr[5] & 0x0100) && !!(s->csr[5] & 0x0200)) /* SLPINT */ ) -#endif - { - isr = 1; - s->csr[0] |= 0x0080; - } - - if (isr != s->isr) { - trace_pcnet_isr_change(s, isr, s->isr); - } - qemu_set_irq(s->irq, isr); - s->isr = isr; -} - -static void pcnet_init(PCNetState *s) -{ - int rlen, tlen; - uint16_t padr[3], ladrf[4], mode; - uint32_t rdra, tdra; - - trace_pcnet_init(s, PHYSADDR(s, CSR_IADR(s))); - - if (BCR_SSIZE32(s)) { - struct pcnet_initblk32 initblk; - s->phys_mem_read(s->dma_opaque, PHYSADDR(s,CSR_IADR(s)), - (uint8_t *)&initblk, sizeof(initblk), 0); - mode = le16_to_cpu(initblk.mode); - rlen = initblk.rlen >> 4; - tlen = initblk.tlen >> 4; - ladrf[0] = le16_to_cpu(initblk.ladrf[0]); - ladrf[1] = le16_to_cpu(initblk.ladrf[1]); - ladrf[2] = le16_to_cpu(initblk.ladrf[2]); - ladrf[3] = le16_to_cpu(initblk.ladrf[3]); - padr[0] = le16_to_cpu(initblk.padr[0]); - padr[1] = le16_to_cpu(initblk.padr[1]); - padr[2] = le16_to_cpu(initblk.padr[2]); - rdra = le32_to_cpu(initblk.rdra); - tdra = le32_to_cpu(initblk.tdra); - } else { - struct pcnet_initblk16 initblk; - s->phys_mem_read(s->dma_opaque, PHYSADDR(s,CSR_IADR(s)), - (uint8_t *)&initblk, sizeof(initblk), 0); - mode = le16_to_cpu(initblk.mode); - ladrf[0] = le16_to_cpu(initblk.ladrf[0]); - ladrf[1] = le16_to_cpu(initblk.ladrf[1]); - ladrf[2] = le16_to_cpu(initblk.ladrf[2]); - ladrf[3] = le16_to_cpu(initblk.ladrf[3]); - padr[0] = le16_to_cpu(initblk.padr[0]); - padr[1] = le16_to_cpu(initblk.padr[1]); - padr[2] = le16_to_cpu(initblk.padr[2]); - rdra = le32_to_cpu(initblk.rdra); - tdra = le32_to_cpu(initblk.tdra); - rlen = rdra >> 29; - tlen = tdra >> 29; - rdra &= 0x00ffffff; - tdra &= 0x00ffffff; - } - - trace_pcnet_rlen_tlen(s, rlen, tlen); - - CSR_RCVRL(s) = (rlen < 9) ? (1 << rlen) : 512; - CSR_XMTRL(s) = (tlen < 9) ? (1 << tlen) : 512; - s->csr[ 6] = (tlen << 12) | (rlen << 8); - s->csr[15] = mode; - s->csr[ 8] = ladrf[0]; - s->csr[ 9] = ladrf[1]; - s->csr[10] = ladrf[2]; - s->csr[11] = ladrf[3]; - s->csr[12] = padr[0]; - s->csr[13] = padr[1]; - s->csr[14] = padr[2]; - s->rdra = PHYSADDR(s, rdra); - s->tdra = PHYSADDR(s, tdra); - - CSR_RCVRC(s) = CSR_RCVRL(s); - CSR_XMTRC(s) = CSR_XMTRL(s); - - trace_pcnet_ss32_rdra_tdra(s, BCR_SSIZE32(s), - s->rdra, CSR_RCVRL(s), s->tdra, CSR_XMTRL(s)); - - s->csr[0] |= 0x0101; - s->csr[0] &= ~0x0004; /* clear STOP bit */ - - qemu_flush_queued_packets(qemu_get_queue(s->nic)); -} - -static void pcnet_start(PCNetState *s) -{ -#ifdef PCNET_DEBUG - printf("pcnet_start\n"); -#endif - - if (!CSR_DTX(s)) - s->csr[0] |= 0x0010; /* set TXON */ - - if (!CSR_DRX(s)) - s->csr[0] |= 0x0020; /* set RXON */ - - s->csr[0] &= ~0x0004; /* clear STOP bit */ - s->csr[0] |= 0x0002; - pcnet_poll_timer(s); - - qemu_flush_queued_packets(qemu_get_queue(s->nic)); -} - -static void pcnet_stop(PCNetState *s) -{ -#ifdef PCNET_DEBUG - printf("pcnet_stop\n"); -#endif - s->csr[0] &= ~0xffeb; - s->csr[0] |= 0x0014; - s->csr[4] &= ~0x02c2; - s->csr[5] &= ~0x0011; - pcnet_poll_timer(s); -} - -static void pcnet_rdte_poll(PCNetState *s) -{ - s->csr[28] = s->csr[29] = 0; - if (s->rdra) { - int bad = 0; -#if 1 - hwaddr crda = pcnet_rdra_addr(s, CSR_RCVRC(s)); - hwaddr nrda = pcnet_rdra_addr(s, -1 + CSR_RCVRC(s)); - hwaddr nnrd = pcnet_rdra_addr(s, -2 + CSR_RCVRC(s)); -#else - hwaddr crda = s->rdra + - (CSR_RCVRL(s) - CSR_RCVRC(s)) * - (BCR_SWSTYLE(s) ? 16 : 8 ); - int nrdc = CSR_RCVRC(s)<=1 ? CSR_RCVRL(s) : CSR_RCVRC(s)-1; - hwaddr nrda = s->rdra + - (CSR_RCVRL(s) - nrdc) * - (BCR_SWSTYLE(s) ? 16 : 8 ); - int nnrc = nrdc<=1 ? CSR_RCVRL(s) : nrdc-1; - hwaddr nnrd = s->rdra + - (CSR_RCVRL(s) - nnrc) * - (BCR_SWSTYLE(s) ? 16 : 8 ); -#endif - - CHECK_RMD(crda, bad); - if (!bad) { - CHECK_RMD(nrda, bad); - if (bad || (nrda == crda)) nrda = 0; - CHECK_RMD(nnrd, bad); - if (bad || (nnrd == crda)) nnrd = 0; - - s->csr[28] = crda & 0xffff; - s->csr[29] = crda >> 16; - s->csr[26] = nrda & 0xffff; - s->csr[27] = nrda >> 16; - s->csr[36] = nnrd & 0xffff; - s->csr[37] = nnrd >> 16; -#ifdef PCNET_DEBUG - if (bad) { - printf("pcnet: BAD RMD RECORDS AFTER 0x" TARGET_FMT_plx "\n", - crda); - } - } else { - printf("pcnet: BAD RMD RDA=0x" TARGET_FMT_plx "\n", - crda); -#endif - } - } - - if (CSR_CRDA(s)) { - struct pcnet_RMD rmd; - RMDLOAD(&rmd, PHYSADDR(s,CSR_CRDA(s))); - CSR_CRBC(s) = GET_FIELD(rmd.buf_length, RMDL, BCNT); - CSR_CRST(s) = rmd.status; -#ifdef PCNET_DEBUG_RMD_X - printf("CRDA=0x%08x CRST=0x%04x RCVRC=%d RMDL=0x%04x RMDS=0x%04x RMDM=0x%08x\n", - PHYSADDR(s,CSR_CRDA(s)), CSR_CRST(s), CSR_RCVRC(s), - rmd.buf_length, rmd.status, rmd.msg_length); - PRINT_RMD(&rmd); -#endif - } else { - CSR_CRBC(s) = CSR_CRST(s) = 0; - } - - if (CSR_NRDA(s)) { - struct pcnet_RMD rmd; - RMDLOAD(&rmd, PHYSADDR(s,CSR_NRDA(s))); - CSR_NRBC(s) = GET_FIELD(rmd.buf_length, RMDL, BCNT); - CSR_NRST(s) = rmd.status; - } else { - CSR_NRBC(s) = CSR_NRST(s) = 0; - } - -} - -static int pcnet_tdte_poll(PCNetState *s) -{ - s->csr[34] = s->csr[35] = 0; - if (s->tdra) { - hwaddr cxda = s->tdra + - (CSR_XMTRL(s) - CSR_XMTRC(s)) * - (BCR_SWSTYLE(s) ? 16 : 8); - int bad = 0; - CHECK_TMD(cxda, bad); - if (!bad) { - if (CSR_CXDA(s) != cxda) { - s->csr[60] = s->csr[34]; - s->csr[61] = s->csr[35]; - s->csr[62] = CSR_CXBC(s); - s->csr[63] = CSR_CXST(s); - } - s->csr[34] = cxda & 0xffff; - s->csr[35] = cxda >> 16; -#ifdef PCNET_DEBUG_X - printf("pcnet: BAD TMD XDA=0x%08x\n", cxda); -#endif - } - } - - if (CSR_CXDA(s)) { - struct pcnet_TMD tmd; - - TMDLOAD(&tmd, PHYSADDR(s,CSR_CXDA(s))); - - CSR_CXBC(s) = GET_FIELD(tmd.length, TMDL, BCNT); - CSR_CXST(s) = tmd.status; - } else { - CSR_CXBC(s) = CSR_CXST(s) = 0; - } - - return !!(CSR_CXST(s) & 0x8000); -} - -#define MIN_BUF_SIZE 60 - -ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) -{ - PCNetState *s = qemu_get_nic_opaque(nc); - int is_padr = 0, is_bcast = 0, is_ladr = 0; - uint8_t buf1[60]; - int remaining; - int crc_err = 0; - int size = size_; - - if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size || - (CSR_LOOP(s) && !s->looptest)) { - return -1; - } -#ifdef PCNET_DEBUG - printf("pcnet_receive size=%d\n", size); -#endif - - /* if too small buffer, then expand it */ - if (size < MIN_BUF_SIZE) { - memcpy(buf1, buf, size); - memset(buf1 + size, 0, MIN_BUF_SIZE - size); - buf = buf1; - size = MIN_BUF_SIZE; - } - - if (CSR_PROM(s) - || (is_padr=padr_match(s, buf, size)) - || (is_bcast=padr_bcast(s, buf, size)) - || (is_ladr=ladr_match(s, buf, size))) { - - pcnet_rdte_poll(s); - - if (!(CSR_CRST(s) & 0x8000) && s->rdra) { - struct pcnet_RMD rmd; - int rcvrc = CSR_RCVRC(s)-1,i; - hwaddr nrda; - for (i = CSR_RCVRL(s)-1; i > 0; i--, rcvrc--) { - if (rcvrc <= 1) - rcvrc = CSR_RCVRL(s); - nrda = s->rdra + - (CSR_RCVRL(s) - rcvrc) * - (BCR_SWSTYLE(s) ? 16 : 8 ); - RMDLOAD(&rmd, nrda); - if (GET_FIELD(rmd.status, RMDS, OWN)) { -#ifdef PCNET_DEBUG_RMD - printf("pcnet - scan buffer: RCVRC=%d PREV_RCVRC=%d\n", - rcvrc, CSR_RCVRC(s)); -#endif - CSR_RCVRC(s) = rcvrc; - pcnet_rdte_poll(s); - break; - } - } - } - - if (!(CSR_CRST(s) & 0x8000)) { -#ifdef PCNET_DEBUG_RMD - printf("pcnet - no buffer: RCVRC=%d\n", CSR_RCVRC(s)); -#endif - s->csr[0] |= 0x1000; /* Set MISS flag */ - CSR_MISSC(s)++; - } else { - uint8_t *src = s->buffer; - hwaddr crda = CSR_CRDA(s); - struct pcnet_RMD rmd; - 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; - src[size + 1] = 0; - src[size + 2] = 0; - src[size + 3] = 0; - size += 4; - } else if (s->looptest == PCNET_LOOPTEST_CRC || - !CSR_DXMTFCS(s) || size < MIN_BUF_SIZE+4) { - uint32_t fcs = ~0; - uint8_t *p = src; - - while (p != &src[size]) - CRC(fcs, *p++); - *(uint32_t *)p = htonl(fcs); - size += 4; - } else { - uint32_t fcs = ~0; - uint8_t *p = src; - - while (p != &src[size]) - CRC(fcs, *p++); - crc_err = (*(uint32_t *)p != htonl(fcs)); - } - -#ifdef PCNET_DEBUG_MATCH - PRINT_PKTHDR(buf); -#endif - - RMDLOAD(&rmd, PHYSADDR(s,crda)); - /*if (!CSR_LAPPEN(s))*/ - SET_FIELD(&rmd.status, RMDS, STP, 1); - -#define PCNET_RECV_STORE() do { \ - int count = MIN(4096 - GET_FIELD(rmd.buf_length, RMDL, BCNT),remaining); \ - hwaddr rbadr = PHYSADDR(s, rmd.rbadr); \ - s->phys_mem_write(s->dma_opaque, rbadr, src, count, CSR_BSWP(s)); \ - src += count; remaining -= count; \ - SET_FIELD(&rmd.status, RMDS, OWN, 0); \ - RMDSTORE(&rmd, PHYSADDR(s,crda)); \ - pktcount++; \ -} while (0) - - remaining = size; - PCNET_RECV_STORE(); - if ((remaining > 0) && CSR_NRDA(s)) { - hwaddr nrda = CSR_NRDA(s); -#ifdef PCNET_DEBUG_RMD - PRINT_RMD(&rmd); -#endif - RMDLOAD(&rmd, PHYSADDR(s,nrda)); - if (GET_FIELD(rmd.status, RMDS, OWN)) { - crda = nrda; - PCNET_RECV_STORE(); -#ifdef PCNET_DEBUG_RMD - PRINT_RMD(&rmd); -#endif - if ((remaining > 0) && (nrda=CSR_NNRD(s))) { - RMDLOAD(&rmd, PHYSADDR(s,nrda)); - if (GET_FIELD(rmd.status, RMDS, OWN)) { - crda = nrda; - PCNET_RECV_STORE(); - } - } - } - } - -#undef PCNET_RECV_STORE - - RMDLOAD(&rmd, PHYSADDR(s,crda)); - if (remaining == 0) { - SET_FIELD(&rmd.msg_length, RMDM, MCNT, size); - SET_FIELD(&rmd.status, RMDS, ENP, 1); - SET_FIELD(&rmd.status, RMDS, PAM, !CSR_PROM(s) && is_padr); - SET_FIELD(&rmd.status, RMDS, LFAM, !CSR_PROM(s) && is_ladr); - SET_FIELD(&rmd.status, RMDS, BAM, !CSR_PROM(s) && is_bcast); - if (crc_err) { - SET_FIELD(&rmd.status, RMDS, CRC, 1); - SET_FIELD(&rmd.status, RMDS, ERR, 1); - } - } else { - SET_FIELD(&rmd.status, RMDS, OFLO, 1); - SET_FIELD(&rmd.status, RMDS, BUFF, 1); - SET_FIELD(&rmd.status, RMDS, ERR, 1); - } - RMDSTORE(&rmd, PHYSADDR(s,crda)); - s->csr[0] |= 0x0400; - -#ifdef PCNET_DEBUG - printf("RCVRC=%d CRDA=0x%08x BLKS=%d\n", - CSR_RCVRC(s), PHYSADDR(s,CSR_CRDA(s)), pktcount); -#endif -#ifdef PCNET_DEBUG_RMD - PRINT_RMD(&rmd); -#endif - - while (pktcount--) { - if (CSR_RCVRC(s) <= 1) - CSR_RCVRC(s) = CSR_RCVRL(s); - else - CSR_RCVRC(s)--; - } - - pcnet_rdte_poll(s); - - } - } - - pcnet_poll(s); - pcnet_update_irq(s); - - return size_; -} - -void pcnet_set_link_status(NetClientState *nc) -{ - PCNetState *d = qemu_get_nic_opaque(nc); - - d->lnkst = nc->link_down ? 0 : 0x40; -} - -static void pcnet_transmit(PCNetState *s) -{ - hwaddr xmit_cxda = 0; - int count = CSR_XMTRL(s)-1; - int add_crc = 0; - int bcnt; - s->xmit_pos = -1; - - if (!CSR_TXON(s)) { - s->csr[0] &= ~0x0008; - return; - } - - s->tx_busy = 1; - - txagain: - if (pcnet_tdte_poll(s)) { - struct pcnet_TMD tmd; - - TMDLOAD(&tmd, PHYSADDR(s,CSR_CXDA(s))); - -#ifdef PCNET_DEBUG_TMD - printf(" TMDLOAD 0x%08x\n", PHYSADDR(s,CSR_CXDA(s))); - PRINT_TMD(&tmd); -#endif - if (GET_FIELD(tmd.status, TMDS, STP)) { - s->xmit_pos = 0; - xmit_cxda = PHYSADDR(s,CSR_CXDA(s)); - if (BCR_SWSTYLE(s) != 1) - add_crc = GET_FIELD(tmd.status, TMDS, ADDFCS); - } - if (s->lnkst == 0 && - (!CSR_LOOP(s) || (!CSR_INTL(s) && !BCR_TMAULOOP(s)))) { - SET_FIELD(&tmd.misc, TMDM, LCAR, 1); - SET_FIELD(&tmd.status, TMDS, ERR, 1); - SET_FIELD(&tmd.status, TMDS, OWN, 0); - s->csr[0] |= 0xa000; /* ERR | CERR */ - s->xmit_pos = -1; - goto txdone; - } - - if (s->xmit_pos < 0) { - goto txdone; - } - - 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. - * 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; - } - - s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr), - s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s)); - s->xmit_pos += bcnt; - - if (!GET_FIELD(tmd.status, TMDS, ENP)) { - goto txdone; - } - -#ifdef PCNET_DEBUG - printf("pcnet_transmit size=%d\n", s->xmit_pos); -#endif - if (CSR_LOOP(s)) { - if (BCR_SWSTYLE(s) == 1) - add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS); - s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC; - pcnet_receive(qemu_get_queue(s->nic), s->buffer, s->xmit_pos); - s->looptest = 0; - } else { - if (s->nic) { - qemu_send_packet(qemu_get_queue(s->nic), s->buffer, - s->xmit_pos); - } - } - - s->csr[0] &= ~0x0008; /* clear TDMD */ - s->csr[4] |= 0x0004; /* set TXSTRT */ - s->xmit_pos = -1; - - txdone: - SET_FIELD(&tmd.status, TMDS, OWN, 0); - TMDSTORE(&tmd, PHYSADDR(s,CSR_CXDA(s))); - if (!CSR_TOKINTD(s) || (CSR_LTINTEN(s) && GET_FIELD(tmd.status, TMDS, LTINT))) - s->csr[0] |= 0x0200; /* set TINT */ - - if (CSR_XMTRC(s)<=1) - CSR_XMTRC(s) = CSR_XMTRL(s); - else - CSR_XMTRC(s)--; - if (count--) - goto txagain; - - } else - if (s->xmit_pos >= 0) { - struct pcnet_TMD tmd; - TMDLOAD(&tmd, xmit_cxda); - SET_FIELD(&tmd.misc, TMDM, BUFF, 1); - SET_FIELD(&tmd.misc, TMDM, UFLO, 1); - SET_FIELD(&tmd.status, TMDS, ERR, 1); - SET_FIELD(&tmd.status, TMDS, OWN, 0); - TMDSTORE(&tmd, xmit_cxda); - s->csr[0] |= 0x0200; /* set TINT */ - if (!CSR_DXSUFLO(s)) { - s->csr[0] &= ~0x0010; - } else - if (count--) - goto txagain; - } - - s->tx_busy = 0; -} - -static void pcnet_poll(PCNetState *s) -{ - if (CSR_RXON(s)) { - pcnet_rdte_poll(s); - } - - if (CSR_TDMD(s) || - (CSR_TXON(s) && !CSR_DPOLL(s) && pcnet_tdte_poll(s))) - { - /* prevent recursion */ - if (s->tx_busy) - return; - - pcnet_transmit(s); - } -} - -static void pcnet_poll_timer(void *opaque) -{ - PCNetState *s = opaque; - - timer_del(s->poll_timer); - - if (CSR_TDMD(s)) { - pcnet_transmit(s); - } - - pcnet_update_irq(s); - - if (!CSR_STOP(s) && !CSR_SPND(s) && !CSR_DPOLL(s)) { - uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) * 33; - if (!s->timer || !now) - s->timer = now; - else { - uint64_t t = now - s->timer + CSR_POLL(s); - if (t > 0xffffLL) { - pcnet_poll(s); - CSR_POLL(s) = CSR_PINT(s); - } else - CSR_POLL(s) = t; - } - timer_mod(s->poll_timer, - pcnet_get_next_poll_time(s,qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL))); - } -} - - -static void pcnet_csr_writew(PCNetState *s, uint32_t rap, uint32_t new_value) -{ - uint16_t val = new_value; -#ifdef PCNET_DEBUG_CSR - printf("pcnet_csr_writew rap=%d val=0x%04x\n", rap, val); -#endif - switch (rap) { - case 0: - s->csr[0] &= ~(val & 0x7f00); /* Clear any interrupt flags */ - - s->csr[0] = (s->csr[0] & ~0x0040) | (val & 0x0048); - - val = (val & 0x007f) | (s->csr[0] & 0x7f00); - - /* IFF STOP, STRT and INIT are set, clear STRT and INIT */ - if ((val&7) == 7) - val &= ~3; - - if (!CSR_STOP(s) && (val & 4)) - pcnet_stop(s); - - if (!CSR_INIT(s) && (val & 1)) - pcnet_init(s); - - if (!CSR_STRT(s) && (val & 2)) - pcnet_start(s); - - if (CSR_TDMD(s)) - pcnet_transmit(s); - - return; - case 1: - case 2: - case 8: - case 9: - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - case 18: /* CRBAL */ - case 19: /* CRBAU */ - case 20: /* CXBAL */ - case 21: /* CXBAU */ - case 22: /* NRBAU */ - case 23: /* NRBAU */ - case 24: - case 25: - case 26: - case 27: - case 28: - case 29: - case 30: - case 31: - case 32: - case 33: - case 34: - case 35: - case 36: - case 37: - case 38: - case 39: - case 40: /* CRBC */ - case 41: - case 42: /* CXBC */ - case 43: - case 44: - case 45: - case 46: /* POLL */ - case 47: /* POLLINT */ - case 72: - case 74: - case 76: /* RCVRL */ - case 78: /* XMTRL */ - case 112: - if (CSR_STOP(s) || CSR_SPND(s)) - break; - return; - case 3: - break; - case 4: - s->csr[4] &= ~(val & 0x026a); - val &= ~0x026a; val |= s->csr[4] & 0x026a; - break; - case 5: - s->csr[5] &= ~(val & 0x0a90); - val &= ~0x0a90; val |= s->csr[5] & 0x0a90; - break; - case 16: - pcnet_csr_writew(s,1,val); - return; - case 17: - pcnet_csr_writew(s,2,val); - return; - case 58: - pcnet_bcr_writew(s,BCR_SWS,val); - break; - default: - return; - } - s->csr[rap] = val; -} - -static uint32_t pcnet_csr_readw(PCNetState *s, uint32_t rap) -{ - uint32_t val; - switch (rap) { - case 0: - pcnet_update_irq(s); - val = s->csr[0]; - val |= (val & 0x7800) ? 0x8000 : 0; - break; - case 16: - return pcnet_csr_readw(s,1); - case 17: - return pcnet_csr_readw(s,2); - case 58: - return pcnet_bcr_readw(s,BCR_SWS); - case 88: - val = s->csr[89]; - val <<= 16; - val |= s->csr[88]; - break; - default: - val = s->csr[rap]; - } -#ifdef PCNET_DEBUG_CSR - printf("pcnet_csr_readw rap=%d val=0x%04x\n", rap, val); -#endif - return val; -} - -static void pcnet_bcr_writew(PCNetState *s, uint32_t rap, uint32_t val) -{ - rap &= 127; -#ifdef PCNET_DEBUG_BCR - printf("pcnet_bcr_writew rap=%d val=0x%04x\n", rap, val); -#endif - switch (rap) { - case BCR_SWS: - if (!(CSR_STOP(s) || CSR_SPND(s))) - return; - val &= ~0x0300; - switch (val & 0x00ff) { - case 0: - val |= 0x0200; - break; - case 1: - val |= 0x0100; - break; - case 2: - case 3: - val |= 0x0300; - break; - default: - printf("Bad SWSTYLE=0x%02x\n", val & 0xff); - val = 0x0200; - break; - } -#ifdef PCNET_DEBUG - printf("BCR_SWS=0x%04x\n", val); -#endif - /* fall through */ - case BCR_LNKST: - case BCR_LED1: - case BCR_LED2: - case BCR_LED3: - case BCR_MC: - case BCR_FDC: - case BCR_BSBC: - case BCR_EECAS: - case BCR_PLAT: - s->bcr[rap] = val; - break; - default: - break; - } -} - -uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap) -{ - uint32_t val; - rap &= 127; - switch (rap) { - case BCR_LNKST: - case BCR_LED1: - case BCR_LED2: - case BCR_LED3: - val = s->bcr[rap] & ~0x8000; - val |= (val & 0x017f & s->lnkst) ? 0x8000 : 0; - break; - default: - val = rap < 32 ? s->bcr[rap] : 0; - break; - } -#ifdef PCNET_DEBUG_BCR - printf("pcnet_bcr_readw rap=%d val=0x%04x\n", rap, val); -#endif - return val; -} - -void pcnet_h_reset(void *opaque) -{ - PCNetState *s = opaque; - - s->bcr[BCR_MSRDA] = 0x0005; - s->bcr[BCR_MSWRA] = 0x0005; - s->bcr[BCR_MC ] = 0x0002; - s->bcr[BCR_LNKST] = 0x00c0; - s->bcr[BCR_LED1 ] = 0x0084; - s->bcr[BCR_LED2 ] = 0x0088; - s->bcr[BCR_LED3 ] = 0x0090; - s->bcr[BCR_FDC ] = 0x0000; - s->bcr[BCR_BSBC ] = 0x9001; - s->bcr[BCR_EECAS] = 0x0002; - s->bcr[BCR_SWS ] = 0x0200; - s->bcr[BCR_PLAT ] = 0xff06; - - pcnet_s_reset(s); - pcnet_update_irq(s); - pcnet_poll_timer(s); -} - -void pcnet_ioport_writew(void *opaque, uint32_t addr, uint32_t val) -{ - PCNetState *s = opaque; - pcnet_poll_timer(s); -#ifdef PCNET_DEBUG_IO - printf("pcnet_ioport_writew addr=0x%08x val=0x%04x\n", addr, val); -#endif - if (!BCR_DWIO(s)) { - switch (addr & 0x0f) { - case 0x00: /* RDP */ - pcnet_csr_writew(s, s->rap, val); - break; - case 0x02: - s->rap = val & 0x7f; - break; - case 0x06: - pcnet_bcr_writew(s, s->rap, val); - break; - } - } - pcnet_update_irq(s); -} - -uint32_t pcnet_ioport_readw(void *opaque, uint32_t addr) -{ - PCNetState *s = opaque; - uint32_t val = -1; - pcnet_poll_timer(s); - if (!BCR_DWIO(s)) { - switch (addr & 0x0f) { - case 0x00: /* RDP */ - val = pcnet_csr_readw(s, s->rap); - break; - case 0x02: - val = s->rap; - break; - case 0x04: - pcnet_s_reset(s); - val = 0; - break; - case 0x06: - val = pcnet_bcr_readw(s, s->rap); - break; - } - } - pcnet_update_irq(s); -#ifdef PCNET_DEBUG_IO - printf("pcnet_ioport_readw addr=0x%08x val=0x%04x\n", addr, val & 0xffff); -#endif - return val; -} - -void pcnet_ioport_writel(void *opaque, uint32_t addr, uint32_t val) -{ - PCNetState *s = opaque; - pcnet_poll_timer(s); -#ifdef PCNET_DEBUG_IO - printf("pcnet_ioport_writel addr=0x%08x val=0x%08x\n", addr, val); -#endif - if (BCR_DWIO(s)) { - switch (addr & 0x0f) { - case 0x00: /* RDP */ - pcnet_csr_writew(s, s->rap, val & 0xffff); - break; - case 0x04: - s->rap = val & 0x7f; - break; - case 0x0c: - pcnet_bcr_writew(s, s->rap, val & 0xffff); - break; - } - } else - if ((addr & 0x0f) == 0) { - /* switch device to dword i/o mode */ - pcnet_bcr_writew(s, BCR_BSBC, pcnet_bcr_readw(s, BCR_BSBC) | 0x0080); -#ifdef PCNET_DEBUG_IO - printf("device switched into dword i/o mode\n"); -#endif - } - pcnet_update_irq(s); -} - -uint32_t pcnet_ioport_readl(void *opaque, uint32_t addr) -{ - PCNetState *s = opaque; - uint32_t val = -1; - pcnet_poll_timer(s); - if (BCR_DWIO(s)) { - switch (addr & 0x0f) { - case 0x00: /* RDP */ - val = pcnet_csr_readw(s, s->rap); - break; - case 0x04: - val = s->rap; - break; - case 0x08: - pcnet_s_reset(s); - val = 0; - break; - case 0x0c: - val = pcnet_bcr_readw(s, s->rap); - break; - } - } - pcnet_update_irq(s); -#ifdef PCNET_DEBUG_IO - printf("pcnet_ioport_readl addr=0x%08x val=0x%08x\n", addr, val); -#endif - return val; -} - -static bool is_version_2(void *opaque, int version_id) -{ - return version_id == 2; -} - -const VMStateDescription vmstate_pcnet = { - .name = "pcnet", - .version_id = 3, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_INT32(rap, PCNetState), - VMSTATE_INT32(isr, PCNetState), - VMSTATE_INT32(lnkst, PCNetState), - VMSTATE_UINT32(rdra, PCNetState), - VMSTATE_UINT32(tdra, PCNetState), - VMSTATE_BUFFER(prom, PCNetState), - VMSTATE_UINT16_ARRAY(csr, PCNetState, 128), - VMSTATE_UINT16_ARRAY(bcr, PCNetState, 32), - VMSTATE_UINT64(timer, PCNetState), - VMSTATE_INT32(xmit_pos, PCNetState), - VMSTATE_BUFFER(buffer, PCNetState), - VMSTATE_UNUSED_TEST(is_version_2, 4), - VMSTATE_INT32(tx_busy, PCNetState), - VMSTATE_TIMER_PTR(poll_timer, PCNetState), - VMSTATE_END_OF_LIST() - } -}; - -void pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info) -{ - int i; - uint16_t checksum; - - s->poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pcnet_poll_timer, s); - - qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - /* Initialize the PROM */ - - /* - Datasheet: http://pdfdata.datasheetsite.com/web/24528/AM79C970A.pdf - page 95 - */ - memcpy(s->prom, s->conf.macaddr.a, 6); - /* Reserved Location: must be 00h */ - s->prom[6] = s->prom[7] = 0x00; - /* Reserved Location: must be 00h */ - s->prom[8] = 0x00; - /* Hardware ID: must be 11h if compatibility to AMD drivers is desired */ - s->prom[9] = 0x11; - /* User programmable space, init with 0 */ - s->prom[10] = s->prom[11] = 0x00; - /* LSByte of two-byte checksum, which is the sum of bytes 00h-0Bh - and bytes 0Eh and 0Fh, must therefore be initialized with 0! */ - s->prom[12] = s->prom[13] = 0x00; - /* Must be ASCII W (57h) if compatibility to AMD - driver software is desired */ - s->prom[14] = s->prom[15] = 0x57; - - for (i = 0, checksum = 0; i < 16; i++) { - checksum += s->prom[i]; - } - *(uint16_t *)&s->prom[12] = cpu_to_le16(checksum); - - s->lnkst = 0x40; /* initial link state: up */ -} diff --git a/qemu/hw/net/pcnet.h b/qemu/hw/net/pcnet.h deleted file mode 100644 index dec8de834..000000000 --- a/qemu/hw/net/pcnet.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef HW_PCNET_H -#define HW_PCNET_H 1 - -#define PCNET_IOPORT_SIZE 0x20 -#define PCNET_PNPMMIO_SIZE 0x20 - -#define PCNET_LOOPTEST_CRC 1 -#define PCNET_LOOPTEST_NOCRC 2 - -#include "exec/memory.h" - -/* BUS CONFIGURATION REGISTERS */ -#define BCR_MSRDA 0 -#define BCR_MSWRA 1 -#define BCR_MC 2 -#define BCR_LNKST 4 -#define BCR_LED1 5 -#define BCR_LED2 6 -#define BCR_LED3 7 -#define BCR_FDC 9 -#define BCR_BSBC 18 -#define BCR_EECAS 19 -#define BCR_SWS 20 -#define BCR_PLAT 22 - -#define BCR_TMAULOOP(S) !!((S)->bcr[BCR_MC ] & 0x4000) -#define BCR_APROMWE(S) !!((S)->bcr[BCR_MC ] & 0x0100) -#define BCR_DWIO(S) !!((S)->bcr[BCR_BSBC] & 0x0080) -#define BCR_SSIZE32(S) !!((S)->bcr[BCR_SWS ] & 0x0100) -#define BCR_SWSTYLE(S) ((S)->bcr[BCR_SWS ] & 0x00FF) - -typedef struct PCNetState_st PCNetState; - -struct PCNetState_st { - NICState *nic; - NICConf conf; - QEMUTimer *poll_timer; - int rap, isr, lnkst; - uint32_t rdra, tdra; - uint8_t prom[16]; - uint16_t csr[128]; - uint16_t bcr[32]; - int xmit_pos; - uint64_t timer; - MemoryRegion mmio; - uint8_t buffer[4096]; - qemu_irq irq; - void (*phys_mem_read)(void *dma_opaque, hwaddr addr, - uint8_t *buf, int len, int do_bswap); - void (*phys_mem_write)(void *dma_opaque, hwaddr addr, - uint8_t *buf, int len, int do_bswap); - void *dma_opaque; - int tx_busy; - int looptest; -}; - -void pcnet_h_reset(void *opaque); -void pcnet_ioport_writew(void *opaque, uint32_t addr, uint32_t val); -uint32_t pcnet_ioport_readw(void *opaque, uint32_t addr); -void pcnet_ioport_writel(void *opaque, uint32_t addr, uint32_t val); -uint32_t pcnet_ioport_readl(void *opaque, uint32_t addr); -uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap); -ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_); -void pcnet_set_link_status(NetClientState *nc); -void pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info); -extern const VMStateDescription vmstate_pcnet; -#endif diff --git a/qemu/hw/net/rocker/qmp-norocker.c b/qemu/hw/net/rocker/qmp-norocker.c deleted file mode 100644 index 6acbcdb02..000000000 --- a/qemu/hw/net/rocker/qmp-norocker.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * QMP Target options - Commands handled based on a target config - * versus a host config - * - * Copyright (c) 2015 David Ahern - * - * 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. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qmp-commands.h" -#include "qapi/qmp/qerror.h" - -RockerSwitch *qmp_query_rocker(const char *name, Error **errp) -{ - error_setg(errp, QERR_FEATURE_DISABLED, "rocker"); - return NULL; -}; - -RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp) -{ - error_setg(errp, QERR_FEATURE_DISABLED, "rocker"); - return NULL; -}; - -RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name, - bool has_tbl_id, - uint32_t tbl_id, - Error **errp) -{ - error_setg(errp, QERR_FEATURE_DISABLED, "rocker"); - return NULL; -}; - -RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name, - bool has_type, - uint8_t type, - Error **errp) -{ - error_setg(errp, QERR_FEATURE_DISABLED, "rocker"); - return NULL; -}; diff --git a/qemu/hw/net/rocker/rocker.c b/qemu/hw/net/rocker/rocker.c deleted file mode 100644 index 30f2ce417..000000000 --- a/qemu/hw/net/rocker/rocker.c +++ /dev/null @@ -1,1584 +0,0 @@ -/* - * QEMU rocker switch emulation - PCI device - * - * Copyright (c) 2014 Scott Feldman - * Copyright (c) 2014 Jiri Pirko - * - * 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. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "hw/pci/msix.h" -#include "net/net.h" -#include "net/eth.h" -#include "qemu/iov.h" -#include "qemu/bitops.h" -#include "qmp-commands.h" - -#include "rocker.h" -#include "rocker_hw.h" -#include "rocker_fp.h" -#include "rocker_desc.h" -#include "rocker_tlv.h" -#include "rocker_world.h" -#include "rocker_of_dpa.h" - -struct rocker { - /* private */ - PCIDevice parent_obj; - /* public */ - - MemoryRegion mmio; - MemoryRegion msix_bar; - - /* 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 */ - uint64_t switch_id; /* switch id */ - - /* front-panel ports */ - FpPort *fp_port[ROCKER_FP_PORTS_MAX]; - - /* register backings */ - uint32_t test_reg; - uint64_t test_reg64; - dma_addr_t test_dma_addr; - uint32_t test_dma_size; - uint64_t lower32; /* lower 32-bit val in 2-part 64-bit access */ - - /* desc rings */ - DescRing **rings; - - /* switch worlds */ - World *worlds[ROCKER_WORLD_TYPE_MAX]; - World *world_dflt; - - QLIST_ENTRY(rocker) next; -}; - -#define ROCKER "rocker" - -#define to_rocker(obj) \ - OBJECT_CHECK(Rocker, (obj), ROCKER) - -static QLIST_HEAD(, rocker) rockers; - -Rocker *rocker_find(const char *name) -{ - Rocker *r; - - QLIST_FOREACH(r, &rockers, next) - if (strcmp(r->name, name) == 0) { - return r; - } - - return NULL; -} - -World *rocker_get_world(Rocker *r, enum rocker_world_type type) -{ - if (type < ROCKER_WORLD_TYPE_MAX) { - return r->worlds[type]; - } - return NULL; -} - -RockerSwitch *qmp_query_rocker(const char *name, Error **errp) -{ - RockerSwitch *rocker; - Rocker *r; - - r = rocker_find(name); - if (!r) { - error_setg(errp, "rocker %s not found", name); - return NULL; - } - - rocker = g_new0(RockerSwitch, 1); - rocker->name = g_strdup(r->name); - rocker->id = r->switch_id; - rocker->ports = r->fp_ports; - - return rocker; -} - -RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp) -{ - RockerPortList *list = NULL; - Rocker *r; - int i; - - r = rocker_find(name); - if (!r) { - error_setg(errp, "rocker %s not found", name); - return NULL; - } - - for (i = r->fp_ports - 1; i >= 0; i--) { - RockerPortList *info = g_malloc0(sizeof(*info)); - info->value = g_malloc0(sizeof(*info->value)); - struct fp_port *port = r->fp_port[i]; - - fp_port_get_info(port, info); - info->next = list; - list = info; - } - - return list; -} - -uint32_t rocker_fp_ports(Rocker *r) -{ - return r->fp_ports; -} - -static uint32_t rocker_get_pport_by_tx_ring(Rocker *r, - DescRing *ring) -{ - return (desc_ring_index(ring) - 2) / 2 + 1; -} - -static int tx_consume(Rocker *r, DescInfo *info) -{ - PCIDevice *dev = PCI_DEVICE(r); - char *buf = desc_get_buf(info, true); - RockerTlv *tlv_frag; - RockerTlv *tlvs[ROCKER_TLV_TX_MAX + 1]; - struct iovec iov[ROCKER_TX_FRAGS_MAX] = { { 0, }, }; - uint32_t pport; - uint32_t port; - uint16_t tx_offload = ROCKER_TX_OFFLOAD_NONE; - uint16_t tx_l3_csum_off = 0; - uint16_t tx_tso_mss = 0; - uint16_t tx_tso_hdr_len = 0; - int iovcnt = 0; - int err = ROCKER_OK; - int rem; - int i; - - if (!buf) { - return -ROCKER_ENXIO; - } - - rocker_tlv_parse(tlvs, ROCKER_TLV_TX_MAX, buf, desc_tlv_size(info)); - - if (!tlvs[ROCKER_TLV_TX_FRAGS]) { - return -ROCKER_EINVAL; - } - - pport = rocker_get_pport_by_tx_ring(r, desc_get_ring(info)); - if (!fp_port_from_pport(pport, &port)) { - return -ROCKER_EINVAL; - } - - if (tlvs[ROCKER_TLV_TX_OFFLOAD]) { - tx_offload = rocker_tlv_get_u8(tlvs[ROCKER_TLV_TX_OFFLOAD]); - } - - switch (tx_offload) { - case ROCKER_TX_OFFLOAD_L3_CSUM: - if (!tlvs[ROCKER_TLV_TX_L3_CSUM_OFF]) { - return -ROCKER_EINVAL; - } - break; - case ROCKER_TX_OFFLOAD_TSO: - if (!tlvs[ROCKER_TLV_TX_TSO_MSS] || - !tlvs[ROCKER_TLV_TX_TSO_HDR_LEN]) { - return -ROCKER_EINVAL; - } - break; - } - - if (tlvs[ROCKER_TLV_TX_L3_CSUM_OFF]) { - tx_l3_csum_off = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_L3_CSUM_OFF]); - } - - if (tlvs[ROCKER_TLV_TX_TSO_MSS]) { - tx_tso_mss = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_TSO_MSS]); - } - - if (tlvs[ROCKER_TLV_TX_TSO_HDR_LEN]) { - tx_tso_hdr_len = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_TSO_HDR_LEN]); - } - - rocker_tlv_for_each_nested(tlv_frag, tlvs[ROCKER_TLV_TX_FRAGS], rem) { - hwaddr frag_addr; - uint16_t frag_len; - - if (rocker_tlv_type(tlv_frag) != ROCKER_TLV_TX_FRAG) { - err = -ROCKER_EINVAL; - goto err_bad_attr; - } - - rocker_tlv_parse_nested(tlvs, ROCKER_TLV_TX_FRAG_ATTR_MAX, tlv_frag); - - if (!tlvs[ROCKER_TLV_TX_FRAG_ATTR_ADDR] || - !tlvs[ROCKER_TLV_TX_FRAG_ATTR_LEN]) { - err = -ROCKER_EINVAL; - goto err_bad_attr; - } - - 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) { - err = -ROCKER_ENOMEM; - goto err_no_mem; - } - - if (pci_dma_read(dev, frag_addr, iov[iovcnt].iov_base, - iov[iovcnt].iov_len)) { - err = -ROCKER_ENXIO; - goto err_bad_io; - } - iovcnt++; - } - - if (iovcnt) { - /* XXX perform Tx offloads */ - /* XXX silence compiler for now */ - tx_l3_csum_off += tx_tso_mss = tx_tso_hdr_len = 0; - } - - err = fp_port_eg(r->fp_port[port], iov, iovcnt); - -err_too_many_frags: -err_bad_io: -err_no_mem: -err_bad_attr: - for (i = 0; i < ROCKER_TX_FRAGS_MAX; i++) { - g_free(iov[i].iov_base); - } - - return err; -} - -static int cmd_get_port_settings(Rocker *r, - DescInfo *info, char *buf, - RockerTlv *cmd_info_tlv) -{ - RockerTlv *tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1]; - RockerTlv *nest; - FpPort *fp_port; - uint32_t pport; - uint32_t port; - uint32_t speed; - uint8_t duplex; - uint8_t autoneg; - uint8_t learning; - char *phys_name; - MACAddr macaddr; - enum rocker_world_type mode; - size_t tlv_size; - int pos; - int err; - - rocker_tlv_parse_nested(tlvs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX, - cmd_info_tlv); - - if (!tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_PPORT]) { - return -ROCKER_EINVAL; - } - - pport = rocker_tlv_get_le32(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_PPORT]); - if (!fp_port_from_pport(pport, &port)) { - return -ROCKER_EINVAL; - } - fp_port = r->fp_port[port]; - - err = fp_port_get_settings(fp_port, &speed, &duplex, &autoneg); - if (err) { - return err; - } - - fp_port_get_macaddr(fp_port, &macaddr); - mode = world_type(fp_port_get_world(fp_port)); - learning = fp_port_get_learning(fp_port); - phys_name = fp_port_get_name(fp_port); - - tlv_size = rocker_tlv_total_size(0) + /* nest */ - rocker_tlv_total_size(sizeof(uint32_t)) + /* pport */ - rocker_tlv_total_size(sizeof(uint32_t)) + /* speed */ - rocker_tlv_total_size(sizeof(uint8_t)) + /* duplex */ - rocker_tlv_total_size(sizeof(uint8_t)) + /* autoneg */ - rocker_tlv_total_size(sizeof(macaddr.a)) + /* macaddr */ - rocker_tlv_total_size(sizeof(uint8_t)) + /* mode */ - rocker_tlv_total_size(sizeof(uint8_t)) + /* learning */ - rocker_tlv_total_size(strlen(phys_name)); - - if (tlv_size > desc_buf_size(info)) { - return -ROCKER_EMSGSIZE; - } - - pos = 0; - nest = rocker_tlv_nest_start(buf, &pos, ROCKER_TLV_CMD_INFO); - rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT, pport); - rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_SPEED, speed); - rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX, duplex); - rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG, autoneg); - rocker_tlv_put(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR, - sizeof(macaddr.a), macaddr.a); - rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_MODE, mode); - rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING, - learning); - rocker_tlv_put(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME, - strlen(phys_name), phys_name); - rocker_tlv_nest_end(buf, &pos, nest); - - return desc_set_buf(info, tlv_size); -} - -static int cmd_set_port_settings(Rocker *r, - RockerTlv *cmd_info_tlv) -{ - RockerTlv *tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1]; - FpPort *fp_port; - uint32_t pport; - uint32_t port; - uint32_t speed; - uint8_t duplex; - uint8_t autoneg; - uint8_t learning; - MACAddr macaddr; - enum rocker_world_type mode; - int err; - - rocker_tlv_parse_nested(tlvs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX, - cmd_info_tlv); - - if (!tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_PPORT]) { - return -ROCKER_EINVAL; - } - - pport = rocker_tlv_get_le32(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_PPORT]); - if (!fp_port_from_pport(pport, &port)) { - return -ROCKER_EINVAL; - } - fp_port = r->fp_port[port]; - - if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED] && - tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX] && - tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG]) { - - speed = rocker_tlv_get_le32(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED]); - duplex = rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX]); - autoneg = rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG]); - - err = fp_port_set_settings(fp_port, speed, duplex, autoneg); - if (err) { - return err; - } - } - - if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR]) { - if (rocker_tlv_len(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR]) != - sizeof(macaddr.a)) { - return -ROCKER_EINVAL; - } - memcpy(macaddr.a, - rocker_tlv_data(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR]), - sizeof(macaddr.a)); - fp_port_set_macaddr(fp_port, &macaddr); - } - - if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]) { - mode = rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_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]) { - learning = - rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING]); - fp_port_set_learning(fp_port, learning); - } - - return ROCKER_OK; -} - -static int cmd_consume(Rocker *r, DescInfo *info) -{ - char *buf = desc_get_buf(info, false); - RockerTlv *tlvs[ROCKER_TLV_CMD_MAX + 1]; - RockerTlv *info_tlv; - World *world; - uint16_t cmd; - int err; - - if (!buf) { - return -ROCKER_ENXIO; - } - - rocker_tlv_parse(tlvs, ROCKER_TLV_CMD_MAX, buf, desc_tlv_size(info)); - - if (!tlvs[ROCKER_TLV_CMD_TYPE] || !tlvs[ROCKER_TLV_CMD_INFO]) { - return -ROCKER_EINVAL; - } - - cmd = rocker_tlv_get_le16(tlvs[ROCKER_TLV_CMD_TYPE]); - info_tlv = tlvs[ROCKER_TLV_CMD_INFO]; - - /* This might be reworked to something like this: - * Every world will have an array of command handlers from - * ROCKER_TLV_CMD_TYPE_UNSPEC to ROCKER_TLV_CMD_TYPE_MAX. There is - * up to each world to implement whatever command it want. - * It can reference "generic" commands as cmd_set_port_settings or - * cmd_get_port_settings - */ - - switch (cmd) { - case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD: - case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD: - case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL: - case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_GET_STATS: - case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD: - case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD: - case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL: - case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_GET_STATS: - world = r->worlds[ROCKER_WORLD_TYPE_OF_DPA]; - err = world_do_cmd(world, info, buf, cmd, info_tlv); - break; - case ROCKER_TLV_CMD_TYPE_GET_PORT_SETTINGS: - err = cmd_get_port_settings(r, info, buf, info_tlv); - break; - case ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS: - err = cmd_set_port_settings(r, info_tlv); - break; - default: - err = -ROCKER_EINVAL; - break; - } - - return err; -} - -static void rocker_msix_irq(Rocker *r, unsigned vector) -{ - PCIDevice *dev = PCI_DEVICE(r); - - DPRINTF("MSI-X notify request for vector %d\n", vector); - if (vector >= ROCKER_MSIX_VEC_COUNT(r->fp_ports)) { - DPRINTF("incorrect vector %d\n", vector); - return; - } - msix_notify(dev, vector); -} - -int rocker_event_link_changed(Rocker *r, uint32_t pport, bool link_up) -{ - DescRing *ring = r->rings[ROCKER_RING_EVENT]; - DescInfo *info = desc_ring_fetch_desc(ring); - RockerTlv *nest; - char *buf; - size_t tlv_size; - int pos; - int err; - - if (!info) { - return -ROCKER_ENOBUFS; - } - - tlv_size = rocker_tlv_total_size(sizeof(uint16_t)) + /* event type */ - rocker_tlv_total_size(0) + /* nest */ - rocker_tlv_total_size(sizeof(uint32_t)) + /* pport */ - rocker_tlv_total_size(sizeof(uint8_t)); /* link up */ - - if (tlv_size > desc_buf_size(info)) { - err = -ROCKER_EMSGSIZE; - goto err_too_big; - } - - buf = desc_get_buf(info, false); - if (!buf) { - err = -ROCKER_ENOMEM; - goto err_no_mem; - } - - pos = 0; - rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_EVENT_TYPE, - ROCKER_TLV_EVENT_TYPE_LINK_CHANGED); - nest = rocker_tlv_nest_start(buf, &pos, ROCKER_TLV_EVENT_INFO); - rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_EVENT_LINK_CHANGED_PPORT, pport); - rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_EVENT_LINK_CHANGED_LINKUP, - link_up ? 1 : 0); - rocker_tlv_nest_end(buf, &pos, nest); - - err = desc_set_buf(info, tlv_size); - -err_too_big: -err_no_mem: - if (desc_ring_post_desc(ring, err)) { - rocker_msix_irq(r, ROCKER_MSIX_VEC_EVENT); - } - - return err; -} - -int rocker_event_mac_vlan_seen(Rocker *r, uint32_t pport, uint8_t *addr, - uint16_t vlan_id) -{ - DescRing *ring = r->rings[ROCKER_RING_EVENT]; - DescInfo *info; - FpPort *fp_port; - uint32_t port; - RockerTlv *nest; - char *buf; - size_t tlv_size; - int pos; - int err; - - if (!fp_port_from_pport(pport, &port)) { - return -ROCKER_EINVAL; - } - fp_port = r->fp_port[port]; - if (!fp_port_get_learning(fp_port)) { - return ROCKER_OK; - } - - info = desc_ring_fetch_desc(ring); - if (!info) { - return -ROCKER_ENOBUFS; - } - - tlv_size = rocker_tlv_total_size(sizeof(uint16_t)) + /* event type */ - rocker_tlv_total_size(0) + /* nest */ - rocker_tlv_total_size(sizeof(uint32_t)) + /* pport */ - rocker_tlv_total_size(ETH_ALEN) + /* mac addr */ - rocker_tlv_total_size(sizeof(uint16_t)); /* vlan_id */ - - if (tlv_size > desc_buf_size(info)) { - err = -ROCKER_EMSGSIZE; - goto err_too_big; - } - - buf = desc_get_buf(info, false); - if (!buf) { - err = -ROCKER_ENOMEM; - goto err_no_mem; - } - - pos = 0; - rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_EVENT_TYPE, - ROCKER_TLV_EVENT_TYPE_MAC_VLAN_SEEN); - nest = rocker_tlv_nest_start(buf, &pos, ROCKER_TLV_EVENT_INFO); - rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_EVENT_MAC_VLAN_PPORT, pport); - rocker_tlv_put(buf, &pos, ROCKER_TLV_EVENT_MAC_VLAN_MAC, ETH_ALEN, addr); - rocker_tlv_put_u16(buf, &pos, ROCKER_TLV_EVENT_MAC_VLAN_VLAN_ID, vlan_id); - rocker_tlv_nest_end(buf, &pos, nest); - - err = desc_set_buf(info, tlv_size); - -err_too_big: -err_no_mem: - if (desc_ring_post_desc(ring, err)) { - rocker_msix_irq(r, ROCKER_MSIX_VEC_EVENT); - } - - return err; -} - -static DescRing *rocker_get_rx_ring_by_pport(Rocker *r, - uint32_t pport) -{ - return r->rings[(pport - 1) * 2 + 3]; -} - -int rx_produce(World *world, uint32_t pport, - const struct iovec *iov, int iovcnt, uint8_t copy_to_cpu) -{ - Rocker *r = world_rocker(world); - PCIDevice *dev = (PCIDevice *)r; - DescRing *ring = rocker_get_rx_ring_by_pport(r, pport); - DescInfo *info = desc_ring_fetch_desc(ring); - char *data; - size_t data_size = iov_size(iov, iovcnt); - char *buf; - uint16_t rx_flags = 0; - uint16_t rx_csum = 0; - size_t tlv_size; - RockerTlv *tlvs[ROCKER_TLV_RX_MAX + 1]; - hwaddr frag_addr; - uint16_t frag_max_len; - int pos; - int err; - - if (!info) { - return -ROCKER_ENOBUFS; - } - - buf = desc_get_buf(info, false); - if (!buf) { - err = -ROCKER_ENXIO; - goto out; - } - rocker_tlv_parse(tlvs, ROCKER_TLV_RX_MAX, buf, desc_tlv_size(info)); - - if (!tlvs[ROCKER_TLV_RX_FRAG_ADDR] || - !tlvs[ROCKER_TLV_RX_FRAG_MAX_LEN]) { - err = -ROCKER_EINVAL; - goto out; - } - - frag_addr = rocker_tlv_get_le64(tlvs[ROCKER_TLV_RX_FRAG_ADDR]); - frag_max_len = rocker_tlv_get_le16(tlvs[ROCKER_TLV_RX_FRAG_MAX_LEN]); - - if (data_size > frag_max_len) { - err = -ROCKER_EMSGSIZE; - goto out; - } - - if (copy_to_cpu) { - rx_flags |= ROCKER_RX_FLAGS_FWD_OFFLOAD; - } - - /* XXX calc rx flags/csum */ - - tlv_size = rocker_tlv_total_size(sizeof(uint16_t)) + /* flags */ - rocker_tlv_total_size(sizeof(uint16_t)) + /* scum */ - rocker_tlv_total_size(sizeof(uint64_t)) + /* frag addr */ - rocker_tlv_total_size(sizeof(uint16_t)) + /* frag max len */ - rocker_tlv_total_size(sizeof(uint16_t)); /* frag len */ - - if (tlv_size > desc_buf_size(info)) { - err = -ROCKER_EMSGSIZE; - goto out; - } - - /* TODO: - * iov dma write can be optimized in similar way e1000 does it in - * e1000_receive_iov. But maybe if would make sense to introduce - * generic helper iov_dma_write. - */ - - data = g_malloc(data_size); - if (!data) { - err = -ROCKER_ENOMEM; - goto out; - } - iov_to_buf(iov, iovcnt, 0, data, data_size); - pci_dma_write(dev, frag_addr, data, data_size); - g_free(data); - - pos = 0; - rocker_tlv_put_le16(buf, &pos, ROCKER_TLV_RX_FLAGS, rx_flags); - rocker_tlv_put_le16(buf, &pos, ROCKER_TLV_RX_CSUM, rx_csum); - rocker_tlv_put_le64(buf, &pos, ROCKER_TLV_RX_FRAG_ADDR, frag_addr); - rocker_tlv_put_le16(buf, &pos, ROCKER_TLV_RX_FRAG_MAX_LEN, frag_max_len); - rocker_tlv_put_le16(buf, &pos, ROCKER_TLV_RX_FRAG_LEN, data_size); - - err = desc_set_buf(info, tlv_size); - -out: - if (desc_ring_post_desc(ring, err)) { - rocker_msix_irq(r, ROCKER_MSIX_VEC_RX(pport - 1)); - } - - return err; -} - -int rocker_port_eg(Rocker *r, uint32_t pport, - const struct iovec *iov, int iovcnt) -{ - FpPort *fp_port; - uint32_t port; - - if (!fp_port_from_pport(pport, &port)) { - return -ROCKER_EINVAL; - } - - fp_port = r->fp_port[port]; - - return fp_port_eg(fp_port, iov, iovcnt); -} - -static void rocker_test_dma_ctrl(Rocker *r, uint32_t val) -{ - PCIDevice *dev = PCI_DEVICE(r); - char *buf; - int i; - - buf = g_malloc(r->test_dma_size); - - if (!buf) { - DPRINTF("test dma buffer alloc failed"); - return; - } - - switch (val) { - case ROCKER_TEST_DMA_CTRL_CLEAR: - memset(buf, 0, r->test_dma_size); - break; - case ROCKER_TEST_DMA_CTRL_FILL: - memset(buf, 0x96, r->test_dma_size); - break; - case ROCKER_TEST_DMA_CTRL_INVERT: - pci_dma_read(dev, r->test_dma_addr, buf, r->test_dma_size); - for (i = 0; i < r->test_dma_size; i++) { - buf[i] = ~buf[i]; - } - break; - default: - DPRINTF("not test dma control val=0x%08x\n", val); - goto err_out; - } - pci_dma_write(dev, r->test_dma_addr, buf, r->test_dma_size); - - rocker_msix_irq(r, ROCKER_MSIX_VEC_TEST); - -err_out: - g_free(buf); -} - -static void rocker_reset(DeviceState *dev); - -static void rocker_control(Rocker *r, uint32_t val) -{ - if (val & ROCKER_CONTROL_RESET) { - rocker_reset(DEVICE(r)); - } -} - -static int rocker_pci_ring_count(Rocker *r) -{ - /* There are: - * - command ring - * - event ring - * - tx and rx ring per each port - */ - return 2 + (2 * r->fp_ports); -} - -static bool rocker_addr_is_desc_reg(Rocker *r, hwaddr addr) -{ - hwaddr start = ROCKER_DMA_DESC_BASE; - hwaddr end = start + (ROCKER_DMA_DESC_SIZE * rocker_pci_ring_count(r)); - - return addr >= start && addr < end; -} - -static void rocker_port_phys_enable_write(Rocker *r, uint64_t new) -{ - int i; - bool old_enabled; - bool new_enabled; - FpPort *fp_port; - - for (i = 0; i < r->fp_ports; i++) { - fp_port = r->fp_port[i]; - old_enabled = fp_port_enabled(fp_port); - new_enabled = (new >> (i + 1)) & 0x1; - if (new_enabled == old_enabled) { - continue; - } - if (new_enabled) { - fp_port_enable(r->fp_port[i]); - } else { - fp_port_disable(r->fp_port[i]); - } - } -} - -static void rocker_io_writel(void *opaque, hwaddr addr, uint32_t val) -{ - Rocker *r = opaque; - - if (rocker_addr_is_desc_reg(r, addr)) { - unsigned index = ROCKER_RING_INDEX(addr); - unsigned offset = addr & ROCKER_DMA_DESC_MASK; - - switch (offset) { - case ROCKER_DMA_DESC_ADDR_OFFSET: - r->lower32 = (uint64_t)val; - break; - case ROCKER_DMA_DESC_ADDR_OFFSET + 4: - desc_ring_set_base_addr(r->rings[index], - ((uint64_t)val) << 32 | r->lower32); - r->lower32 = 0; - break; - case ROCKER_DMA_DESC_SIZE_OFFSET: - desc_ring_set_size(r->rings[index], val); - break; - case ROCKER_DMA_DESC_HEAD_OFFSET: - if (desc_ring_set_head(r->rings[index], val)) { - rocker_msix_irq(r, desc_ring_get_msix_vector(r->rings[index])); - } - break; - case ROCKER_DMA_DESC_CTRL_OFFSET: - desc_ring_set_ctrl(r->rings[index], val); - break; - case ROCKER_DMA_DESC_CREDITS_OFFSET: - if (desc_ring_ret_credits(r->rings[index], val)) { - rocker_msix_irq(r, desc_ring_get_msix_vector(r->rings[index])); - } - break; - default: - DPRINTF("not implemented dma reg write(l) addr=0x" TARGET_FMT_plx - " val=0x%08x (ring %d, addr=0x%02x)\n", - addr, val, index, offset); - break; - } - return; - } - - switch (addr) { - case ROCKER_TEST_REG: - r->test_reg = val; - break; - case ROCKER_TEST_REG64: - case ROCKER_TEST_DMA_ADDR: - case ROCKER_PORT_PHYS_ENABLE: - r->lower32 = (uint64_t)val; - break; - case ROCKER_TEST_REG64 + 4: - r->test_reg64 = ((uint64_t)val) << 32 | r->lower32; - r->lower32 = 0; - break; - case ROCKER_TEST_IRQ: - rocker_msix_irq(r, val); - break; - case ROCKER_TEST_DMA_SIZE: - r->test_dma_size = val; - break; - case ROCKER_TEST_DMA_ADDR + 4: - r->test_dma_addr = ((uint64_t)val) << 32 | r->lower32; - r->lower32 = 0; - break; - case ROCKER_TEST_DMA_CTRL: - rocker_test_dma_ctrl(r, val); - break; - case ROCKER_CONTROL: - rocker_control(r, val); - break; - case ROCKER_PORT_PHYS_ENABLE + 4: - rocker_port_phys_enable_write(r, ((uint64_t)val) << 32 | r->lower32); - r->lower32 = 0; - break; - default: - DPRINTF("not implemented write(l) addr=0x" TARGET_FMT_plx - " val=0x%08x\n", addr, val); - break; - } -} - -static void rocker_io_writeq(void *opaque, hwaddr addr, uint64_t val) -{ - Rocker *r = opaque; - - if (rocker_addr_is_desc_reg(r, addr)) { - unsigned index = ROCKER_RING_INDEX(addr); - unsigned offset = addr & ROCKER_DMA_DESC_MASK; - - switch (offset) { - case ROCKER_DMA_DESC_ADDR_OFFSET: - desc_ring_set_base_addr(r->rings[index], val); - break; - default: - DPRINTF("not implemented dma reg write(q) addr=0x" TARGET_FMT_plx - " val=0x" TARGET_FMT_plx " (ring %d, offset=0x%02x)\n", - addr, val, index, offset); - break; - } - return; - } - - switch (addr) { - case ROCKER_TEST_REG64: - r->test_reg64 = val; - break; - case ROCKER_TEST_DMA_ADDR: - r->test_dma_addr = val; - break; - case ROCKER_PORT_PHYS_ENABLE: - rocker_port_phys_enable_write(r, val); - break; - default: - DPRINTF("not implemented write(q) addr=0x" TARGET_FMT_plx - " val=0x" TARGET_FMT_plx "\n", addr, val); - break; - } -} - -#ifdef DEBUG_ROCKER -#define regname(reg) case (reg): return #reg -static const char *rocker_reg_name(void *opaque, hwaddr addr) -{ - Rocker *r = opaque; - - if (rocker_addr_is_desc_reg(r, addr)) { - unsigned index = ROCKER_RING_INDEX(addr); - unsigned offset = addr & ROCKER_DMA_DESC_MASK; - static char buf[100]; - char ring_name[10]; - - switch (index) { - case 0: - sprintf(ring_name, "cmd"); - break; - case 1: - sprintf(ring_name, "event"); - break; - default: - sprintf(ring_name, "%s-%d", index % 2 ? "rx" : "tx", - (index - 2) / 2); - } - - switch (offset) { - case ROCKER_DMA_DESC_ADDR_OFFSET: - sprintf(buf, "Ring[%s] ADDR", ring_name); - return buf; - case ROCKER_DMA_DESC_ADDR_OFFSET+4: - sprintf(buf, "Ring[%s] ADDR+4", ring_name); - return buf; - case ROCKER_DMA_DESC_SIZE_OFFSET: - sprintf(buf, "Ring[%s] SIZE", ring_name); - return buf; - case ROCKER_DMA_DESC_HEAD_OFFSET: - sprintf(buf, "Ring[%s] HEAD", ring_name); - return buf; - case ROCKER_DMA_DESC_TAIL_OFFSET: - sprintf(buf, "Ring[%s] TAIL", ring_name); - return buf; - case ROCKER_DMA_DESC_CTRL_OFFSET: - sprintf(buf, "Ring[%s] CTRL", ring_name); - return buf; - case ROCKER_DMA_DESC_CREDITS_OFFSET: - sprintf(buf, "Ring[%s] CREDITS", ring_name); - return buf; - default: - sprintf(buf, "Ring[%s] ???", ring_name); - return buf; - } - } else { - switch (addr) { - regname(ROCKER_BOGUS_REG0); - regname(ROCKER_BOGUS_REG1); - regname(ROCKER_BOGUS_REG2); - regname(ROCKER_BOGUS_REG3); - regname(ROCKER_TEST_REG); - regname(ROCKER_TEST_REG64); - regname(ROCKER_TEST_REG64+4); - regname(ROCKER_TEST_IRQ); - regname(ROCKER_TEST_DMA_ADDR); - regname(ROCKER_TEST_DMA_ADDR+4); - regname(ROCKER_TEST_DMA_SIZE); - regname(ROCKER_TEST_DMA_CTRL); - regname(ROCKER_CONTROL); - regname(ROCKER_PORT_PHYS_COUNT); - regname(ROCKER_PORT_PHYS_LINK_STATUS); - regname(ROCKER_PORT_PHYS_LINK_STATUS+4); - regname(ROCKER_PORT_PHYS_ENABLE); - regname(ROCKER_PORT_PHYS_ENABLE+4); - regname(ROCKER_SWITCH_ID); - regname(ROCKER_SWITCH_ID+4); - } - } - return "???"; -} -#else -static const char *rocker_reg_name(void *opaque, hwaddr addr) -{ - return NULL; -} -#endif - -static void rocker_mmio_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - DPRINTF("Write %s addr " TARGET_FMT_plx - ", size %u, val " TARGET_FMT_plx "\n", - rocker_reg_name(opaque, addr), addr, size, val); - - switch (size) { - case 4: - rocker_io_writel(opaque, addr, val); - break; - case 8: - rocker_io_writeq(opaque, addr, val); - break; - } -} - -static uint64_t rocker_port_phys_link_status(Rocker *r) -{ - int i; - uint64_t status = 0; - - for (i = 0; i < r->fp_ports; i++) { - FpPort *port = r->fp_port[i]; - - if (fp_port_get_link_up(port)) { - status |= 1 << (i + 1); - } - } - return status; -} - -static uint64_t rocker_port_phys_enable_read(Rocker *r) -{ - int i; - uint64_t ret = 0; - - for (i = 0; i < r->fp_ports; i++) { - FpPort *port = r->fp_port[i]; - - if (fp_port_enabled(port)) { - ret |= 1 << (i + 1); - } - } - return ret; -} - -static uint32_t rocker_io_readl(void *opaque, hwaddr addr) -{ - Rocker *r = opaque; - uint32_t ret; - - if (rocker_addr_is_desc_reg(r, addr)) { - unsigned index = ROCKER_RING_INDEX(addr); - unsigned offset = addr & ROCKER_DMA_DESC_MASK; - - switch (offset) { - case ROCKER_DMA_DESC_ADDR_OFFSET: - ret = (uint32_t)desc_ring_get_base_addr(r->rings[index]); - break; - case ROCKER_DMA_DESC_ADDR_OFFSET + 4: - ret = (uint32_t)(desc_ring_get_base_addr(r->rings[index]) >> 32); - break; - case ROCKER_DMA_DESC_SIZE_OFFSET: - ret = desc_ring_get_size(r->rings[index]); - break; - case ROCKER_DMA_DESC_HEAD_OFFSET: - ret = desc_ring_get_head(r->rings[index]); - break; - case ROCKER_DMA_DESC_TAIL_OFFSET: - ret = desc_ring_get_tail(r->rings[index]); - break; - case ROCKER_DMA_DESC_CREDITS_OFFSET: - ret = desc_ring_get_credits(r->rings[index]); - break; - default: - DPRINTF("not implemented dma reg read(l) addr=0x" TARGET_FMT_plx - " (ring %d, addr=0x%02x)\n", addr, index, offset); - ret = 0; - break; - } - return ret; - } - - switch (addr) { - case ROCKER_BOGUS_REG0: - case ROCKER_BOGUS_REG1: - case ROCKER_BOGUS_REG2: - case ROCKER_BOGUS_REG3: - ret = 0xDEADBABE; - break; - case ROCKER_TEST_REG: - ret = r->test_reg * 2; - break; - case ROCKER_TEST_REG64: - ret = (uint32_t)(r->test_reg64 * 2); - break; - case ROCKER_TEST_REG64 + 4: - ret = (uint32_t)((r->test_reg64 * 2) >> 32); - break; - case ROCKER_TEST_DMA_SIZE: - ret = r->test_dma_size; - break; - case ROCKER_TEST_DMA_ADDR: - ret = (uint32_t)r->test_dma_addr; - break; - case ROCKER_TEST_DMA_ADDR + 4: - ret = (uint32_t)(r->test_dma_addr >> 32); - break; - case ROCKER_PORT_PHYS_COUNT: - ret = r->fp_ports; - break; - case ROCKER_PORT_PHYS_LINK_STATUS: - ret = (uint32_t)rocker_port_phys_link_status(r); - break; - case ROCKER_PORT_PHYS_LINK_STATUS + 4: - ret = (uint32_t)(rocker_port_phys_link_status(r) >> 32); - break; - case ROCKER_PORT_PHYS_ENABLE: - ret = (uint32_t)rocker_port_phys_enable_read(r); - break; - case ROCKER_PORT_PHYS_ENABLE + 4: - ret = (uint32_t)(rocker_port_phys_enable_read(r) >> 32); - break; - case ROCKER_SWITCH_ID: - ret = (uint32_t)r->switch_id; - break; - case ROCKER_SWITCH_ID + 4: - ret = (uint32_t)(r->switch_id >> 32); - break; - default: - DPRINTF("not implemented read(l) addr=0x" TARGET_FMT_plx "\n", addr); - ret = 0; - break; - } - return ret; -} - -static uint64_t rocker_io_readq(void *opaque, hwaddr addr) -{ - Rocker *r = opaque; - uint64_t ret; - - if (rocker_addr_is_desc_reg(r, addr)) { - unsigned index = ROCKER_RING_INDEX(addr); - unsigned offset = addr & ROCKER_DMA_DESC_MASK; - - switch (addr & ROCKER_DMA_DESC_MASK) { - case ROCKER_DMA_DESC_ADDR_OFFSET: - ret = desc_ring_get_base_addr(r->rings[index]); - break; - default: - DPRINTF("not implemented dma reg read(q) addr=0x" TARGET_FMT_plx - " (ring %d, addr=0x%02x)\n", addr, index, offset); - ret = 0; - break; - } - return ret; - } - - switch (addr) { - case ROCKER_BOGUS_REG0: - case ROCKER_BOGUS_REG2: - ret = 0xDEADBABEDEADBABEULL; - break; - case ROCKER_TEST_REG64: - ret = r->test_reg64 * 2; - break; - case ROCKER_TEST_DMA_ADDR: - ret = r->test_dma_addr; - break; - case ROCKER_PORT_PHYS_LINK_STATUS: - ret = rocker_port_phys_link_status(r); - break; - case ROCKER_PORT_PHYS_ENABLE: - ret = rocker_port_phys_enable_read(r); - break; - case ROCKER_SWITCH_ID: - ret = r->switch_id; - break; - default: - DPRINTF("not implemented read(q) addr=0x" TARGET_FMT_plx "\n", addr); - ret = 0; - break; - } - return ret; -} - -static uint64_t rocker_mmio_read(void *opaque, hwaddr addr, unsigned size) -{ - DPRINTF("Read %s addr " TARGET_FMT_plx ", size %u\n", - rocker_reg_name(opaque, addr), addr, size); - - switch (size) { - case 4: - return rocker_io_readl(opaque, addr); - case 8: - return rocker_io_readq(opaque, addr); - } - - return -1; -} - -static const MemoryRegionOps rocker_mmio_ops = { - .read = rocker_mmio_read, - .write = rocker_mmio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 8, - }, - .impl = { - .min_access_size = 4, - .max_access_size = 8, - }, -}; - -static void rocker_msix_vectors_unuse(Rocker *r, - unsigned int num_vectors) -{ - PCIDevice *dev = PCI_DEVICE(r); - int i; - - for (i = 0; i < num_vectors; i++) { - msix_vector_unuse(dev, i); - } -} - -static int rocker_msix_vectors_use(Rocker *r, - unsigned int num_vectors) -{ - PCIDevice *dev = PCI_DEVICE(r); - int err; - int i; - - for (i = 0; i < num_vectors; i++) { - err = msix_vector_use(dev, i); - if (err) { - goto rollback; - } - } - return 0; - -rollback: - rocker_msix_vectors_unuse(r, i); - return err; -} - -static int rocker_msix_init(Rocker *r) -{ - PCIDevice *dev = PCI_DEVICE(r); - int err; - - err = msix_init(dev, ROCKER_MSIX_VEC_COUNT(r->fp_ports), - &r->msix_bar, - ROCKER_PCI_MSIX_BAR_IDX, ROCKER_PCI_MSIX_TABLE_OFFSET, - &r->msix_bar, - ROCKER_PCI_MSIX_BAR_IDX, ROCKER_PCI_MSIX_PBA_OFFSET, - 0); - if (err) { - return err; - } - - err = rocker_msix_vectors_use(r, ROCKER_MSIX_VEC_COUNT(r->fp_ports)); - if (err) { - goto err_msix_vectors_use; - } - - return 0; - -err_msix_vectors_use: - msix_uninit(dev, &r->msix_bar, &r->msix_bar); - return err; -} - -static void rocker_msix_uninit(Rocker *r) -{ - PCIDevice *dev = PCI_DEVICE(r); - - msix_uninit(dev, &r->msix_bar, &r->msix_bar); - 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); - const MACAddr zero = { .a = { 0, 0, 0, 0, 0, 0 } }; - const MACAddr dflt = { .a = { 0x52, 0x54, 0x00, 0x12, 0x35, 0x01 } }; - static int sw_index; - int i, err = 0; - - /* allocate worlds */ - - r->worlds[ROCKER_WORLD_TYPE_OF_DPA] = of_dpa_world_alloc(r); - - 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, - "rocker-mmio", ROCKER_PCI_BAR0_SIZE); - pci_register_bar(dev, ROCKER_PCI_BAR0_IDX, - PCI_BASE_ADDRESS_SPACE_MEMORY, &r->mmio); - - /* set up memory-mapped region for MSI-X */ - - memory_region_init(&r->msix_bar, OBJECT(r), "rocker-msix-bar", - ROCKER_PCI_MSIX_BAR_SIZE); - pci_register_bar(dev, ROCKER_PCI_MSIX_BAR_IDX, - PCI_BASE_ADDRESS_SPACE_MEMORY, &r->msix_bar); - - /* MSI-X init */ - - err = rocker_msix_init(r); - if (err) { - goto err_msix_init; - } - - /* validate switch properties */ - - if (!r->name) { - r->name = g_strdup(ROCKER); - } - - if (rocker_find(r->name)) { - err = -EEXIST; - goto err_duplicate; - } - - /* Rocker name is passed in port name requests to OS with the intention - * that the name is used in interface names. Limit the length of the - * rocker name to avoid naming problems in the OS. Also, adding the - * port number as p# and unganged breakout b#, where # is at most 2 - * digits, so leave room for it too (-1 for string terminator, -3 for - * p# and -3 for b#) - */ -#define ROCKER_IFNAMSIZ 16 -#define MAX_ROCKER_NAME_LEN (ROCKER_IFNAMSIZ - 1 - 3 - 3) - if (strlen(r->name) > MAX_ROCKER_NAME_LEN) { - fprintf(stderr, - "rocker: name too long; please shorten to at most %d chars\n", - MAX_ROCKER_NAME_LEN); - return -EINVAL; - } - - if (memcmp(&r->fp_start_macaddr, &zero, sizeof(zero)) == 0) { - memcpy(&r->fp_start_macaddr, &dflt, sizeof(dflt)); - r->fp_start_macaddr.a[4] += (sw_index++); - } - - if (!r->switch_id) { - memcpy(&r->switch_id, &r->fp_start_macaddr, - sizeof(r->fp_start_macaddr)); - } - - if (r->fp_ports > ROCKER_FP_PORTS_MAX) { - r->fp_ports = ROCKER_FP_PORTS_MAX; - } - - r->rings = g_new(DescRing *, rocker_pci_ring_count(r)); - if (!r->rings) { - goto err_rings_alloc; - } - - /* Rings are ordered like this: - * - command ring - * - event ring - * - port0 tx ring - * - port0 rx ring - * - port1 tx ring - * - port1 rx ring - * ..... - */ - - err = -ENOMEM; - for (i = 0; i < rocker_pci_ring_count(r); i++) { - DescRing *ring = desc_ring_alloc(r, i); - - if (!ring) { - goto err_ring_alloc; - } - - if (i == ROCKER_RING_CMD) { - desc_ring_set_consume(ring, cmd_consume, ROCKER_MSIX_VEC_CMD); - } else if (i == ROCKER_RING_EVENT) { - desc_ring_set_consume(ring, NULL, ROCKER_MSIX_VEC_EVENT); - } else if (i % 2 == 0) { - desc_ring_set_consume(ring, tx_consume, - ROCKER_MSIX_VEC_TX((i - 2) / 2)); - } else if (i % 2 == 1) { - desc_ring_set_consume(ring, NULL, ROCKER_MSIX_VEC_RX((i - 3) / 2)); - } - - r->rings[i] = ring; - } - - for (i = 0; i < r->fp_ports; i++) { - FpPort *port = - fp_port_alloc(r, r->name, &r->fp_start_macaddr, - i, &r->fp_ports_peers[i]); - - if (!port) { - goto err_port_alloc; - } - - r->fp_port[i] = port; - fp_port_set_world(port, r->world_dflt); - } - - QLIST_INSERT_HEAD(&rockers, r, next); - - return 0; - -err_port_alloc: - for (--i; i >= 0; i--) { - FpPort *port = r->fp_port[i]; - fp_port_free(port); - } - i = rocker_pci_ring_count(r); -err_ring_alloc: - for (--i; i >= 0; i--) { - desc_ring_free(r->rings[i]); - } - g_free(r->rings); -err_rings_alloc: -err_duplicate: - rocker_msix_uninit(r); -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]) { - world_free(r->worlds[i]); - } - } - return err; -} - -static void pci_rocker_uninit(PCIDevice *dev) -{ - Rocker *r = to_rocker(dev); - int i; - - QLIST_REMOVE(r, next); - - for (i = 0; i < r->fp_ports; i++) { - FpPort *port = r->fp_port[i]; - - fp_port_free(port); - r->fp_port[i] = NULL; - } - - for (i = 0; i < rocker_pci_ring_count(r); i++) { - if (r->rings[i]) { - desc_ring_free(r->rings[i]); - } - } - g_free(r->rings); - - rocker_msix_uninit(r); - object_unparent(OBJECT(&r->msix_bar)); - object_unparent(OBJECT(&r->mmio)); - - for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) { - if (r->worlds[i]) { - world_free(r->worlds[i]); - } - } - g_free(r->fp_ports_peers); -} - -static void rocker_reset(DeviceState *dev) -{ - Rocker *r = to_rocker(dev); - int i; - - for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) { - if (r->worlds[i]) { - world_reset(r->worlds[i]); - } - } - for (i = 0; i < r->fp_ports; i++) { - fp_port_reset(r->fp_port[i]); - fp_port_set_world(r->fp_port[i], r->world_dflt); - } - - r->test_reg = 0; - r->test_reg64 = 0; - r->test_dma_addr = 0; - r->test_dma_size = 0; - - for (i = 0; i < rocker_pci_ring_count(r); i++) { - desc_ring_reset(r->rings[i]); - } - - DPRINTF("Reset done\n"); -} - -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, - switch_id, 0), - DEFINE_PROP_ARRAY("ports", Rocker, fp_ports, - fp_ports_peers, qdev_prop_netdev, NICPeers), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription rocker_vmsd = { - .name = ROCKER, - .unmigratable = 1, -}; - -static void rocker_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = pci_rocker_init; - k->exit = pci_rocker_uninit; - k->vendor_id = PCI_VENDOR_ID_REDHAT; - k->device_id = PCI_DEVICE_ID_REDHAT_ROCKER; - k->revision = ROCKER_PCI_REVISION; - k->class_id = PCI_CLASS_NETWORK_OTHER; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->desc = "Rocker Switch"; - dc->reset = rocker_reset; - dc->props = rocker_properties; - dc->vmsd = &rocker_vmsd; -} - -static const TypeInfo rocker_info = { - .name = ROCKER, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(Rocker), - .class_init = rocker_class_init, -}; - -static void rocker_register_types(void) -{ - type_register_static(&rocker_info); -} - -type_init(rocker_register_types) diff --git a/qemu/hw/net/rocker/rocker.h b/qemu/hw/net/rocker/rocker.h deleted file mode 100644 index f9c80f801..000000000 --- a/qemu/hw/net/rocker/rocker.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * QEMU rocker switch emulation - * - * Copyright (c) 2014 Scott Feldman - * Copyright (c) 2014 Jiri Pirko - * Copyright (c) 2014 Neil Horman - * - * 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. - */ - -#ifndef _ROCKER_H_ -#define _ROCKER_H_ - -#include "qemu/sockets.h" - -#if defined(DEBUG_ROCKER) -# define DPRINTF(fmt, ...) \ - do { \ - struct timeval tv; \ - char timestr[64]; \ - time_t now; \ - gettimeofday(&tv, NULL); \ - now = tv.tv_sec; \ - strftime(timestr, sizeof(timestr), "%T", localtime(&now)); \ - fprintf(stderr, "%s.%06ld ", timestr, tv.tv_usec); \ - fprintf(stderr, "ROCKER: " fmt, ## __VA_ARGS__); \ - } while (0) -#else -static inline GCC_FMT_ATTR(1, 2) int DPRINTF(const char *fmt, ...) -{ - return 0; -} -#endif - -#define __le16 uint16_t -#define __le32 uint32_t -#define __le64 uint64_t - -#define __be16 uint16_t -#define __be32 uint32_t -#define __be64 uint64_t - -static inline bool ipv4_addr_is_multicast(__be32 addr) -{ - return (addr & htonl(0xf0000000)) == htonl(0xe0000000); -} - -typedef struct ipv6_addr { - union { - uint8_t addr8[16]; - __be16 addr16[8]; - __be32 addr32[4]; - }; -} Ipv6Addr; - -static inline bool ipv6_addr_is_multicast(const Ipv6Addr *addr) -{ - return (addr->addr32[0] & htonl(0xFF000000)) == htonl(0xFF000000); -} - -typedef struct rocker Rocker; -typedef struct world World; -typedef struct desc_info DescInfo; -typedef struct desc_ring DescRing; - -Rocker *rocker_find(const char *name); -uint32_t rocker_fp_ports(Rocker *r); -int rocker_event_link_changed(Rocker *r, uint32_t pport, bool link_up); -int rocker_event_mac_vlan_seen(Rocker *r, uint32_t pport, uint8_t *addr, - uint16_t vlan_id); -int rx_produce(World *world, uint32_t pport, - const struct iovec *iov, int iovcnt, uint8_t copy_to_cpu); -int rocker_port_eg(Rocker *r, uint32_t pport, - const struct iovec *iov, int iovcnt); - -#endif /* _ROCKER_H_ */ diff --git a/qemu/hw/net/rocker/rocker_desc.c b/qemu/hw/net/rocker/rocker_desc.c deleted file mode 100644 index ac02797b7..000000000 --- a/qemu/hw/net/rocker/rocker_desc.c +++ /dev/null @@ -1,374 +0,0 @@ -/* - * QEMU rocker switch emulation - Descriptor ring support - * - * Copyright (c) 2014 Scott Feldman - * - * 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. - */ - -#include "qemu/osdep.h" -#include "net/net.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" - -#include "rocker.h" -#include "rocker_hw.h" -#include "rocker_desc.h" - -struct desc_ring { - hwaddr base_addr; - uint32_t size; - uint32_t head; - uint32_t tail; - uint32_t ctrl; - uint32_t credits; - Rocker *r; - DescInfo *info; - int index; - desc_ring_consume *consume; - unsigned msix_vector; -}; - -struct desc_info { - DescRing *ring; - RockerDesc desc; - char *buf; - size_t buf_size; -}; - -uint16_t desc_buf_size(DescInfo *info) -{ - return le16_to_cpu(info->desc.buf_size); -} - -uint16_t desc_tlv_size(DescInfo *info) -{ - return le16_to_cpu(info->desc.tlv_size); -} - -char *desc_get_buf(DescInfo *info, bool read_only) -{ - PCIDevice *dev = PCI_DEVICE(info->ring->r); - size_t size = read_only ? le16_to_cpu(info->desc.tlv_size) : - le16_to_cpu(info->desc.buf_size); - - if (size > info->buf_size) { - info->buf = g_realloc(info->buf, size); - info->buf_size = size; - } - - if (!info->buf) { - return NULL; - } - - if (pci_dma_read(dev, le64_to_cpu(info->desc.buf_addr), info->buf, size)) { - return NULL; - } - - return info->buf; -} - -int desc_set_buf(DescInfo *info, size_t tlv_size) -{ - PCIDevice *dev = PCI_DEVICE(info->ring->r); - - if (tlv_size > info->buf_size) { - DPRINTF("ERROR: trying to write more to desc buf than it " - "can hold buf_size %zu tlv_size %zu\n", - info->buf_size, tlv_size); - return -ROCKER_EMSGSIZE; - } - - info->desc.tlv_size = cpu_to_le16(tlv_size); - pci_dma_write(dev, le64_to_cpu(info->desc.buf_addr), info->buf, tlv_size); - - return ROCKER_OK; -} - -DescRing *desc_get_ring(DescInfo *info) -{ - return info->ring; -} - -int desc_ring_index(DescRing *ring) -{ - return ring->index; -} - -static bool desc_ring_empty(DescRing *ring) -{ - return ring->head == ring->tail; -} - -bool desc_ring_set_base_addr(DescRing *ring, uint64_t base_addr) -{ - if (base_addr & 0x7) { - DPRINTF("ERROR: ring[%d] desc base addr (0x" TARGET_FMT_plx - ") not 8-byte aligned\n", ring->index, base_addr); - return false; - } - - ring->base_addr = base_addr; - - return true; -} - -uint64_t desc_ring_get_base_addr(DescRing *ring) -{ - return ring->base_addr; -} - -bool desc_ring_set_size(DescRing *ring, uint32_t size) -{ - int i; - - if (size < 2 || size > 0x10000 || (size & (size - 1))) { - DPRINTF("ERROR: ring[%d] size (%d) not a power of 2 " - "or in range [2, 64K]\n", ring->index, size); - return false; - } - - for (i = 0; i < ring->size; i++) { - g_free(ring->info[i].buf); - } - - ring->size = size; - ring->head = ring->tail = 0; - - ring->info = g_renew(DescInfo, ring->info, size); - if (!ring->info) { - return false; - } - - memset(ring->info, 0, size * sizeof(DescInfo)); - - for (i = 0; i < size; i++) { - ring->info[i].ring = ring; - } - - return true; -} - -uint32_t desc_ring_get_size(DescRing *ring) -{ - return ring->size; -} - -static DescInfo *desc_read(DescRing *ring, uint32_t index) -{ - PCIDevice *dev = PCI_DEVICE(ring->r); - DescInfo *info = &ring->info[index]; - hwaddr addr = ring->base_addr + (sizeof(RockerDesc) * index); - - pci_dma_read(dev, addr, &info->desc, sizeof(info->desc)); - - return info; -} - -static void desc_write(DescRing *ring, uint32_t index) -{ - PCIDevice *dev = PCI_DEVICE(ring->r); - DescInfo *info = &ring->info[index]; - hwaddr addr = ring->base_addr + (sizeof(RockerDesc) * index); - - pci_dma_write(dev, addr, &info->desc, sizeof(info->desc)); -} - -static bool desc_ring_base_addr_check(DescRing *ring) -{ - if (!ring->base_addr) { - DPRINTF("ERROR: ring[%d] not-initialized desc base address!\n", - ring->index); - return false; - } - return true; -} - -static DescInfo *__desc_ring_fetch_desc(DescRing *ring) -{ - return desc_read(ring, ring->tail); -} - -DescInfo *desc_ring_fetch_desc(DescRing *ring) -{ - if (desc_ring_empty(ring) || !desc_ring_base_addr_check(ring)) { - return NULL; - } - - return desc_read(ring, ring->tail); -} - -static bool __desc_ring_post_desc(DescRing *ring, int err) -{ - uint16_t comp_err = 0x8000 | (uint16_t)-err; - DescInfo *info = &ring->info[ring->tail]; - - info->desc.comp_err = cpu_to_le16(comp_err); - desc_write(ring, ring->tail); - ring->tail = (ring->tail + 1) % ring->size; - - /* return true if starting credit count */ - - return ring->credits++ == 0; -} - -bool desc_ring_post_desc(DescRing *ring, int err) -{ - if (desc_ring_empty(ring)) { - DPRINTF("ERROR: ring[%d] trying to post desc to empty ring\n", - ring->index); - return false; - } - - if (!desc_ring_base_addr_check(ring)) { - return false; - } - - return __desc_ring_post_desc(ring, err); -} - -static bool ring_pump(DescRing *ring) -{ - DescInfo *info; - bool primed = false; - int err; - - /* If the ring has a consumer, call consumer for each - * desc starting at tail and stopping when tail reaches - * head (the empty ring condition). - */ - - if (ring->consume) { - while (ring->head != ring->tail) { - info = __desc_ring_fetch_desc(ring); - err = ring->consume(ring->r, info); - if (__desc_ring_post_desc(ring, err)) { - primed = true; - } - } - } - - return primed; -} - -bool desc_ring_set_head(DescRing *ring, uint32_t new) -{ - uint32_t tail = ring->tail; - uint32_t head = ring->head; - - if (!desc_ring_base_addr_check(ring)) { - return false; - } - - if (new >= ring->size) { - DPRINTF("ERROR: trying to set head (%d) past ring[%d] size (%d)\n", - new, ring->index, ring->size); - return false; - } - - if (((head < tail) && ((new >= tail) || (new < head))) || - ((head > tail) && ((new >= tail) && (new < head)))) { - DPRINTF("ERROR: trying to wrap ring[%d] " - "(head %d, tail %d, new head %d)\n", - ring->index, head, tail, new); - return false; - } - - if (new == ring->head) { - DPRINTF("WARNING: setting head (%d) to current head position\n", new); - } - - ring->head = new; - - return ring_pump(ring); -} - -uint32_t desc_ring_get_head(DescRing *ring) -{ - return ring->head; -} - -uint32_t desc_ring_get_tail(DescRing *ring) -{ - return ring->tail; -} - -void desc_ring_set_ctrl(DescRing *ring, uint32_t val) -{ - if (val & ROCKER_DMA_DESC_CTRL_RESET) { - DPRINTF("ring[%d] resetting\n", ring->index); - desc_ring_reset(ring); - } -} - -bool desc_ring_ret_credits(DescRing *ring, uint32_t credits) -{ - if (credits > ring->credits) { - DPRINTF("ERROR: trying to return more credits (%d) " - "than are outstanding (%d)\n", credits, ring->credits); - ring->credits = 0; - return false; - } - - ring->credits -= credits; - - /* return true if credits are still outstanding */ - - return ring->credits > 0; -} - -uint32_t desc_ring_get_credits(DescRing *ring) -{ - return ring->credits; -} - -void desc_ring_set_consume(DescRing *ring, desc_ring_consume *consume, - unsigned vector) -{ - ring->consume = consume; - ring->msix_vector = vector; -} - -unsigned desc_ring_get_msix_vector(DescRing *ring) -{ - return ring->msix_vector; -} - -DescRing *desc_ring_alloc(Rocker *r, int index) -{ - DescRing *ring; - - ring = g_new0(DescRing, 1); - if (!ring) { - return NULL; - } - - ring->r = r; - ring->index = index; - - return ring; -} - -void desc_ring_free(DescRing *ring) -{ - g_free(ring->info); - g_free(ring); -} - -void desc_ring_reset(DescRing *ring) -{ - ring->base_addr = 0; - ring->size = 0; - ring->head = 0; - ring->tail = 0; - ring->ctrl = 0; - ring->credits = 0; -} diff --git a/qemu/hw/net/rocker/rocker_desc.h b/qemu/hw/net/rocker/rocker_desc.h deleted file mode 100644 index d4041f5c4..000000000 --- a/qemu/hw/net/rocker/rocker_desc.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * QEMU rocker switch emulation - Descriptor ring support - * - * Copyright (c) 2014 Scott Feldman - * - * 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. - */ - - -#ifndef _ROCKER_DESC_H_ -#define _ROCKER_DESC_H_ - -#include "rocker_hw.h" - -typedef int (desc_ring_consume)(Rocker *r, DescInfo *info); - -uint16_t desc_buf_size(DescInfo *info); -uint16_t desc_tlv_size(DescInfo *info); -char *desc_get_buf(DescInfo *info, bool read_only); -int desc_set_buf(DescInfo *info, size_t tlv_size); -DescRing *desc_get_ring(DescInfo *info); - -int desc_ring_index(DescRing *ring); -bool desc_ring_set_base_addr(DescRing *ring, uint64_t base_addr); -uint64_t desc_ring_get_base_addr(DescRing *ring); -bool desc_ring_set_size(DescRing *ring, uint32_t size); -uint32_t desc_ring_get_size(DescRing *ring); -bool desc_ring_set_head(DescRing *ring, uint32_t new); -uint32_t desc_ring_get_head(DescRing *ring); -uint32_t desc_ring_get_tail(DescRing *ring); -void desc_ring_set_ctrl(DescRing *ring, uint32_t val); -bool desc_ring_ret_credits(DescRing *ring, uint32_t credits); -uint32_t desc_ring_get_credits(DescRing *ring); - -DescInfo *desc_ring_fetch_desc(DescRing *ring); -bool desc_ring_post_desc(DescRing *ring, int status); - -void desc_ring_set_consume(DescRing *ring, desc_ring_consume *consume, - unsigned vector); -unsigned desc_ring_get_msix_vector(DescRing *ring); -DescRing *desc_ring_alloc(Rocker *r, int index); -void desc_ring_free(DescRing *ring); -void desc_ring_reset(DescRing *ring); - -#endif diff --git a/qemu/hw/net/rocker/rocker_fp.c b/qemu/hw/net/rocker/rocker_fp.c deleted file mode 100644 index 0149899c6..000000000 --- a/qemu/hw/net/rocker/rocker_fp.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - * QEMU rocker switch emulation - front-panel ports - * - * Copyright (c) 2014 Scott Feldman - * - * 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. - */ - -#include "qemu/osdep.h" -#include "net/clients.h" - -#include "rocker.h" -#include "rocker_hw.h" -#include "rocker_fp.h" -#include "rocker_world.h" - -enum duplex { - DUPLEX_HALF = 0, - DUPLEX_FULL -}; - -struct fp_port { - Rocker *r; - World *world; - unsigned int index; - char *name; - uint32_t pport; - bool enabled; - uint32_t speed; - uint8_t duplex; - uint8_t autoneg; - uint8_t learning; - NICState *nic; - NICConf conf; -}; - -char *fp_port_get_name(FpPort *port) -{ - return port->name; -} - -bool fp_port_get_link_up(FpPort *port) -{ - return !qemu_get_queue(port->nic)->link_down; -} - -void fp_port_get_info(FpPort *port, RockerPortList *info) -{ - info->value->name = g_strdup(port->name); - info->value->enabled = port->enabled; - info->value->link_up = fp_port_get_link_up(port); - info->value->speed = port->speed; - info->value->duplex = port->duplex; - info->value->autoneg = port->autoneg; -} - -void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr) -{ - memcpy(macaddr->a, port->conf.macaddr.a, sizeof(macaddr->a)); -} - -void fp_port_set_macaddr(FpPort *port, MACAddr *macaddr) -{ -/* XXX TODO implement and test setting mac addr - * XXX memcpy(port->conf.macaddr.a, macaddr.a, sizeof(port->conf.macaddr.a)); - */ -} - -uint8_t fp_port_get_learning(FpPort *port) -{ - return port->learning; -} - -void fp_port_set_learning(FpPort *port, uint8_t learning) -{ - port->learning = learning; -} - -int fp_port_get_settings(FpPort *port, uint32_t *speed, - uint8_t *duplex, uint8_t *autoneg) -{ - *speed = port->speed; - *duplex = port->duplex; - *autoneg = port->autoneg; - - return ROCKER_OK; -} - -int fp_port_set_settings(FpPort *port, uint32_t speed, - uint8_t duplex, uint8_t autoneg) -{ - /* XXX validate inputs */ - - port->speed = speed; - port->duplex = duplex; - port->autoneg = autoneg; - - return ROCKER_OK; -} - -bool fp_port_from_pport(uint32_t pport, uint32_t *port) -{ - if (pport < 1 || pport > ROCKER_FP_PORTS_MAX) { - return false; - } - *port = pport - 1; - return true; -} - -int fp_port_eg(FpPort *port, const struct iovec *iov, int iovcnt) -{ - NetClientState *nc = qemu_get_queue(port->nic); - - if (port->enabled) { - qemu_sendv_packet(nc, iov, iovcnt); - } - - return ROCKER_OK; -} - -static ssize_t fp_port_receive_iov(NetClientState *nc, const struct iovec *iov, - int iovcnt) -{ - FpPort *port = qemu_get_nic_opaque(nc); - - /* If the port is disabled, we want to drop this pkt - * now rather than queing it for later. We don't want - * any stale pkts getting into the device when the port - * transitions to enabled. - */ - - if (!port->enabled) { - return -1; - } - - return world_ingress(port->world, port->pport, iov, iovcnt); -} - -static ssize_t fp_port_receive(NetClientState *nc, const uint8_t *buf, - size_t size) -{ - const struct iovec iov = { - .iov_base = (uint8_t *)buf, - .iov_len = size - }; - - return fp_port_receive_iov(nc, &iov, 1); -} - -static void fp_port_cleanup(NetClientState *nc) -{ -} - -static void fp_port_set_link_status(NetClientState *nc) -{ - FpPort *port = qemu_get_nic_opaque(nc); - - rocker_event_link_changed(port->r, port->pport, !nc->link_down); -} - -static NetClientInfo fp_port_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = fp_port_receive, - .receive_iov = fp_port_receive_iov, - .cleanup = fp_port_cleanup, - .link_status_changed = fp_port_set_link_status, -}; - -World *fp_port_get_world(FpPort *port) -{ - return port->world; -} - -void fp_port_set_world(FpPort *port, World *world) -{ - DPRINTF("port %d setting world \"%s\"\n", port->index, world_name(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; -} - -static void fp_port_set_link(FpPort *port, bool up) -{ - NetClientState *nc = qemu_get_queue(port->nic); - - if (up == nc->link_down) { - nc->link_down = !up; - nc->info->link_status_changed(nc); - } -} - -void fp_port_enable(FpPort *port) -{ - fp_port_set_link(port, true); - port->enabled = true; - DPRINTF("port %d enabled\n", port->index); -} - -void fp_port_disable(FpPort *port) -{ - port->enabled = false; - fp_port_set_link(port, false); - DPRINTF("port %d disabled\n", port->index); -} - -FpPort *fp_port_alloc(Rocker *r, char *sw_name, - MACAddr *start_mac, unsigned int index, - NICPeers *peers) -{ - FpPort *port = g_new0(FpPort, 1); - - if (!port) { - return NULL; - } - - port->r = r; - port->index = index; - port->pport = index + 1; - - /* front-panel switch port names are 1-based */ - - port->name = g_strdup_printf("%sp%d", sw_name, port->pport); - - memcpy(port->conf.macaddr.a, start_mac, sizeof(port->conf.macaddr.a)); - port->conf.macaddr.a[5] += index; - port->conf.bootindex = -1; - port->conf.peers = *peers; - - port->nic = qemu_new_nic(&fp_port_info, &port->conf, - sw_name, NULL, port); - qemu_format_nic_info_str(qemu_get_queue(port->nic), - port->conf.macaddr.a); - - fp_port_reset(port); - - return port; -} - -void fp_port_free(FpPort *port) -{ - qemu_del_nic(port->nic); - g_free(port->name); - g_free(port); -} - -void fp_port_reset(FpPort *port) -{ - fp_port_disable(port); - port->speed = 10000; /* 10Gbps */ - port->duplex = DUPLEX_FULL; - port->autoneg = 0; -} diff --git a/qemu/hw/net/rocker/rocker_fp.h b/qemu/hw/net/rocker/rocker_fp.h deleted file mode 100644 index 04592bbfd..000000000 --- a/qemu/hw/net/rocker/rocker_fp.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * QEMU rocker switch emulation - front-panel ports - * - * Copyright (c) 2014 Scott Feldman - * - * 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. - */ - -#ifndef _ROCKER_FP_H_ -#define _ROCKER_FP_H_ - -#include "net/net.h" -#include "qemu/iov.h" - -#define ROCKER_FP_PORTS_MAX 62 - -typedef struct fp_port FpPort; - -int fp_port_eg(FpPort *port, const struct iovec *iov, int iovcnt); - -char *fp_port_get_name(FpPort *port); -bool fp_port_get_link_up(FpPort *port); -void fp_port_get_info(FpPort *port, RockerPortList *info); -void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr); -void fp_port_set_macaddr(FpPort *port, MACAddr *macaddr); -uint8_t fp_port_get_learning(FpPort *port); -void fp_port_set_learning(FpPort *port, uint8_t learning); -int fp_port_get_settings(FpPort *port, uint32_t *speed, - uint8_t *duplex, uint8_t *autoneg); -int fp_port_set_settings(FpPort *port, uint32_t speed, - uint8_t duplex, uint8_t autoneg); -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); - -FpPort *fp_port_alloc(Rocker *r, char *sw_name, - MACAddr *start_mac, unsigned int index, - NICPeers *peers); -void fp_port_free(FpPort *port); -void fp_port_reset(FpPort *port); - -#endif /* _ROCKER_FP_H_ */ diff --git a/qemu/hw/net/rocker/rocker_hw.h b/qemu/hw/net/rocker/rocker_hw.h deleted file mode 100644 index 8c5083032..000000000 --- a/qemu/hw/net/rocker/rocker_hw.h +++ /dev/null @@ -1,493 +0,0 @@ -/* - * Rocker switch hardware register and descriptor definitions. - * - * Copyright (c) 2014 Scott Feldman - * Copyright (c) 2014 Jiri Pirko - * - */ - -#ifndef _ROCKER_HW_ -#define _ROCKER_HW_ - -#define __le16 uint16_t -#define __le32 uint32_t -#define __le64 uint64_t - -/* - * Return codes - */ - -enum { - ROCKER_OK = 0, - ROCKER_ENOENT = 2, - ROCKER_ENXIO = 6, - ROCKER_ENOMEM = 12, - ROCKER_EEXIST = 17, - ROCKER_EINVAL = 22, - ROCKER_EMSGSIZE = 90, - ROCKER_ENOTSUP = 95, - ROCKER_ENOBUFS = 105, -}; - -/* - * PCI configuration space - */ - -#define ROCKER_PCI_REVISION 0x1 -#define ROCKER_PCI_BAR0_IDX 0 -#define ROCKER_PCI_BAR0_SIZE 0x2000 -#define ROCKER_PCI_MSIX_BAR_IDX 1 -#define ROCKER_PCI_MSIX_BAR_SIZE 0x2000 -#define ROCKER_PCI_MSIX_TABLE_OFFSET 0x0000 -#define ROCKER_PCI_MSIX_PBA_OFFSET 0x1000 - -/* - * MSI-X vectors - */ - -enum { - ROCKER_MSIX_VEC_CMD, - ROCKER_MSIX_VEC_EVENT, - ROCKER_MSIX_VEC_TEST, - ROCKER_MSIX_VEC_RESERVED0, - __ROCKER_MSIX_VEC_TX, - __ROCKER_MSIX_VEC_RX, -#define ROCKER_MSIX_VEC_TX(port) \ - (__ROCKER_MSIX_VEC_TX + ((port) * 2)) -#define ROCKER_MSIX_VEC_RX(port) \ - (__ROCKER_MSIX_VEC_RX + ((port) * 2)) -#define ROCKER_MSIX_VEC_COUNT(portcnt) \ - (ROCKER_MSIX_VEC_RX((portcnt) - 1) + 1) -}; - -/* - * Rocker bogus registers - */ -#define ROCKER_BOGUS_REG0 0x0000 -#define ROCKER_BOGUS_REG1 0x0004 -#define ROCKER_BOGUS_REG2 0x0008 -#define ROCKER_BOGUS_REG3 0x000c - -/* - * Rocker test registers - */ -#define ROCKER_TEST_REG 0x0010 -#define ROCKER_TEST_REG64 0x0018 /* 8-byte */ -#define ROCKER_TEST_IRQ 0x0020 -#define ROCKER_TEST_DMA_ADDR 0x0028 /* 8-byte */ -#define ROCKER_TEST_DMA_SIZE 0x0030 -#define ROCKER_TEST_DMA_CTRL 0x0034 - -/* - * Rocker test register ctrl - */ -#define ROCKER_TEST_DMA_CTRL_CLEAR (1 << 0) -#define ROCKER_TEST_DMA_CTRL_FILL (1 << 1) -#define ROCKER_TEST_DMA_CTRL_INVERT (1 << 2) - -/* - * Rocker DMA ring register offsets - */ -#define ROCKER_DMA_DESC_BASE 0x1000 -#define ROCKER_DMA_DESC_SIZE 32 -#define ROCKER_DMA_DESC_MASK 0x1F -#define ROCKER_DMA_DESC_TOTAL_SIZE \ - (ROCKER_DMA_DESC_SIZE * 64) /* 62 ports + event + cmd */ -#define ROCKER_DMA_DESC_ADDR_OFFSET 0x00 /* 8-byte */ -#define ROCKER_DMA_DESC_SIZE_OFFSET 0x08 -#define ROCKER_DMA_DESC_HEAD_OFFSET 0x0c -#define ROCKER_DMA_DESC_TAIL_OFFSET 0x10 -#define ROCKER_DMA_DESC_CTRL_OFFSET 0x14 -#define ROCKER_DMA_DESC_CREDITS_OFFSET 0x18 -#define ROCKER_DMA_DESC_RSVD_OFFSET 0x1c - -/* - * Rocker dma ctrl register bits - */ -#define ROCKER_DMA_DESC_CTRL_RESET (1 << 0) - -/* - * Rocker ring indices - */ -#define ROCKER_RING_CMD 0 -#define ROCKER_RING_EVENT 1 - -/* - * Helper macro to do convert a dma ring register - * to its index. Based on the fact that the register - * group stride is 32 bytes. - */ -#define ROCKER_RING_INDEX(reg) ((reg >> 5) & 0x7F) - -/* - * Rocker DMA Descriptor - */ - -typedef struct rocker_desc { - __le64 buf_addr; - uint64_t cookie; - __le16 buf_size; - __le16 tlv_size; - __le16 rsvd[5]; /* pad to 32 bytes */ - __le16 comp_err; -} __attribute__((packed, aligned(8))) RockerDesc; - -/* - * Rocker TLV type fields - */ - -typedef struct rocker_tlv { - __le32 type; - __le16 len; - __le16 rsvd; -} __attribute__((packed, aligned(8))) RockerTlv; - -/* cmd msg */ -enum { - ROCKER_TLV_CMD_UNSPEC, - ROCKER_TLV_CMD_TYPE, /* u16 */ - ROCKER_TLV_CMD_INFO, /* nest */ - - __ROCKER_TLV_CMD_MAX, - ROCKER_TLV_CMD_MAX = __ROCKER_TLV_CMD_MAX - 1, -}; - -enum { - ROCKER_TLV_CMD_TYPE_UNSPEC, - ROCKER_TLV_CMD_TYPE_GET_PORT_SETTINGS, - ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS, - ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD, - ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD, - ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL, - ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_GET_STATS, - ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD, - ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD, - ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL, - ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_GET_STATS, - - __ROCKER_TLV_CMD_TYPE_MAX, - ROCKER_TLV_CMD_TYPE_MAX = __ROCKER_TLV_CMD_TYPE_MAX - 1, -}; - -/* cmd info nested for set/get port settings */ -enum { - ROCKER_TLV_CMD_PORT_SETTINGS_UNSPEC, - ROCKER_TLV_CMD_PORT_SETTINGS_PPORT, /* u32 */ - ROCKER_TLV_CMD_PORT_SETTINGS_SPEED, /* u32 */ - ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX, /* u8 */ - ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG, /* u8 */ - ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR, /* binary */ - ROCKER_TLV_CMD_PORT_SETTINGS_MODE, /* u8 */ - ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING, /* u8 */ - ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME, /* binary */ - - __ROCKER_TLV_CMD_PORT_SETTINGS_MAX, - ROCKER_TLV_CMD_PORT_SETTINGS_MAX = __ROCKER_TLV_CMD_PORT_SETTINGS_MAX - 1, -}; - -enum { - ROCKER_PORT_MODE_OF_DPA, -}; - -/* event msg */ -enum { - ROCKER_TLV_EVENT_UNSPEC, - ROCKER_TLV_EVENT_TYPE, /* u16 */ - ROCKER_TLV_EVENT_INFO, /* nest */ - - __ROCKER_TLV_EVENT_MAX, - ROCKER_TLV_EVENT_MAX = __ROCKER_TLV_EVENT_MAX - 1, -}; - -enum { - ROCKER_TLV_EVENT_TYPE_UNSPEC, - ROCKER_TLV_EVENT_TYPE_LINK_CHANGED, - ROCKER_TLV_EVENT_TYPE_MAC_VLAN_SEEN, - - __ROCKER_TLV_EVENT_TYPE_MAX, - ROCKER_TLV_EVENT_TYPE_MAX = __ROCKER_TLV_EVENT_TYPE_MAX - 1, -}; - -/* event info nested for link changed */ -enum { - ROCKER_TLV_EVENT_LINK_CHANGED_UNSPEC, - ROCKER_TLV_EVENT_LINK_CHANGED_PPORT, /* u32 */ - ROCKER_TLV_EVENT_LINK_CHANGED_LINKUP, /* u8 */ - - __ROCKER_TLV_EVENT_LINK_CHANGED_MAX, - ROCKER_TLV_EVENT_LINK_CHANGED_MAX = __ROCKER_TLV_EVENT_LINK_CHANGED_MAX - 1, -}; - -/* event info nested for MAC/VLAN */ -enum { - ROCKER_TLV_EVENT_MAC_VLAN_UNSPEC, - ROCKER_TLV_EVENT_MAC_VLAN_PPORT, /* u32 */ - ROCKER_TLV_EVENT_MAC_VLAN_MAC, /* binary */ - ROCKER_TLV_EVENT_MAC_VLAN_VLAN_ID, /* __be16 */ - - __ROCKER_TLV_EVENT_MAC_VLAN_MAX, - ROCKER_TLV_EVENT_MAC_VLAN_MAX = __ROCKER_TLV_EVENT_MAC_VLAN_MAX - 1, -}; - -/* Rx msg */ -enum { - ROCKER_TLV_RX_UNSPEC, - ROCKER_TLV_RX_FLAGS, /* u16, see RX_FLAGS_ */ - ROCKER_TLV_RX_CSUM, /* u16 */ - ROCKER_TLV_RX_FRAG_ADDR, /* u64 */ - ROCKER_TLV_RX_FRAG_MAX_LEN, /* u16 */ - ROCKER_TLV_RX_FRAG_LEN, /* u16 */ - - __ROCKER_TLV_RX_MAX, - ROCKER_TLV_RX_MAX = __ROCKER_TLV_RX_MAX - 1, -}; - -#define ROCKER_RX_FLAGS_IPV4 (1 << 0) -#define ROCKER_RX_FLAGS_IPV6 (1 << 1) -#define ROCKER_RX_FLAGS_CSUM_CALC (1 << 2) -#define ROCKER_RX_FLAGS_IPV4_CSUM_GOOD (1 << 3) -#define ROCKER_RX_FLAGS_IP_FRAG (1 << 4) -#define ROCKER_RX_FLAGS_TCP (1 << 5) -#define ROCKER_RX_FLAGS_UDP (1 << 6) -#define ROCKER_RX_FLAGS_TCP_UDP_CSUM_GOOD (1 << 7) -#define ROCKER_RX_FLAGS_FWD_OFFLOAD (1 << 8) - -/* Tx msg */ -enum { - ROCKER_TLV_TX_UNSPEC, - ROCKER_TLV_TX_OFFLOAD, /* u8, see TX_OFFLOAD_ */ - ROCKER_TLV_TX_L3_CSUM_OFF, /* u16 */ - ROCKER_TLV_TX_TSO_MSS, /* u16 */ - ROCKER_TLV_TX_TSO_HDR_LEN, /* u16 */ - ROCKER_TLV_TX_FRAGS, /* array */ - - __ROCKER_TLV_TX_MAX, - ROCKER_TLV_TX_MAX = __ROCKER_TLV_TX_MAX - 1, -}; - -#define ROCKER_TX_OFFLOAD_NONE 0 -#define ROCKER_TX_OFFLOAD_IP_CSUM 1 -#define ROCKER_TX_OFFLOAD_TCP_UDP_CSUM 2 -#define ROCKER_TX_OFFLOAD_L3_CSUM 3 -#define ROCKER_TX_OFFLOAD_TSO 4 - -#define ROCKER_TX_FRAGS_MAX 16 - -enum { - ROCKER_TLV_TX_FRAG_UNSPEC, - ROCKER_TLV_TX_FRAG, /* nest */ - - __ROCKER_TLV_TX_FRAG_MAX, - ROCKER_TLV_TX_FRAG_MAX = __ROCKER_TLV_TX_FRAG_MAX - 1, -}; - -enum { - ROCKER_TLV_TX_FRAG_ATTR_UNSPEC, - ROCKER_TLV_TX_FRAG_ATTR_ADDR, /* u64 */ - ROCKER_TLV_TX_FRAG_ATTR_LEN, /* u16 */ - - __ROCKER_TLV_TX_FRAG_ATTR_MAX, - ROCKER_TLV_TX_FRAG_ATTR_MAX = __ROCKER_TLV_TX_FRAG_ATTR_MAX - 1, -}; - -/* - * cmd info nested for OF-DPA msgs - */ - -enum { - ROCKER_TLV_OF_DPA_UNSPEC, - ROCKER_TLV_OF_DPA_TABLE_ID, /* u16 */ - ROCKER_TLV_OF_DPA_PRIORITY, /* u32 */ - ROCKER_TLV_OF_DPA_HARDTIME, /* u32 */ - ROCKER_TLV_OF_DPA_IDLETIME, /* u32 */ - ROCKER_TLV_OF_DPA_COOKIE, /* u64 */ - ROCKER_TLV_OF_DPA_IN_PPORT, /* u32 */ - ROCKER_TLV_OF_DPA_IN_PPORT_MASK, /* u32 */ - ROCKER_TLV_OF_DPA_OUT_PPORT, /* u32 */ - ROCKER_TLV_OF_DPA_GOTO_TABLE_ID, /* u16 */ - ROCKER_TLV_OF_DPA_GROUP_ID, /* u32 */ - ROCKER_TLV_OF_DPA_GROUP_ID_LOWER, /* u32 */ - ROCKER_TLV_OF_DPA_GROUP_COUNT, /* u16 */ - ROCKER_TLV_OF_DPA_GROUP_IDS, /* u32 array */ - ROCKER_TLV_OF_DPA_VLAN_ID, /* __be16 */ - ROCKER_TLV_OF_DPA_VLAN_ID_MASK, /* __be16 */ - ROCKER_TLV_OF_DPA_VLAN_PCP, /* __be16 */ - ROCKER_TLV_OF_DPA_VLAN_PCP_MASK, /* __be16 */ - ROCKER_TLV_OF_DPA_VLAN_PCP_ACTION, /* u8 */ - ROCKER_TLV_OF_DPA_NEW_VLAN_ID, /* __be16 */ - ROCKER_TLV_OF_DPA_NEW_VLAN_PCP, /* u8 */ - ROCKER_TLV_OF_DPA_TUNNEL_ID, /* u32 */ - ROCKER_TLV_OF_DPA_TUNNEL_LPORT, /* u32 */ - ROCKER_TLV_OF_DPA_ETHERTYPE, /* __be16 */ - ROCKER_TLV_OF_DPA_DST_MAC, /* binary */ - ROCKER_TLV_OF_DPA_DST_MAC_MASK, /* binary */ - ROCKER_TLV_OF_DPA_SRC_MAC, /* binary */ - ROCKER_TLV_OF_DPA_SRC_MAC_MASK, /* binary */ - ROCKER_TLV_OF_DPA_IP_PROTO, /* u8 */ - ROCKER_TLV_OF_DPA_IP_PROTO_MASK, /* u8 */ - ROCKER_TLV_OF_DPA_IP_DSCP, /* u8 */ - ROCKER_TLV_OF_DPA_IP_DSCP_MASK, /* u8 */ - ROCKER_TLV_OF_DPA_IP_DSCP_ACTION, /* u8 */ - ROCKER_TLV_OF_DPA_NEW_IP_DSCP, /* u8 */ - ROCKER_TLV_OF_DPA_IP_ECN, /* u8 */ - ROCKER_TLV_OF_DPA_IP_ECN_MASK, /* u8 */ - ROCKER_TLV_OF_DPA_DST_IP, /* __be32 */ - ROCKER_TLV_OF_DPA_DST_IP_MASK, /* __be32 */ - ROCKER_TLV_OF_DPA_SRC_IP, /* __be32 */ - ROCKER_TLV_OF_DPA_SRC_IP_MASK, /* __be32 */ - ROCKER_TLV_OF_DPA_DST_IPV6, /* binary */ - ROCKER_TLV_OF_DPA_DST_IPV6_MASK, /* binary */ - ROCKER_TLV_OF_DPA_SRC_IPV6, /* binary */ - ROCKER_TLV_OF_DPA_SRC_IPV6_MASK, /* binary */ - ROCKER_TLV_OF_DPA_SRC_ARP_IP, /* __be32 */ - ROCKER_TLV_OF_DPA_SRC_ARP_IP_MASK, /* __be32 */ - ROCKER_TLV_OF_DPA_L4_DST_PORT, /* __be16 */ - ROCKER_TLV_OF_DPA_L4_DST_PORT_MASK, /* __be16 */ - ROCKER_TLV_OF_DPA_L4_SRC_PORT, /* __be16 */ - ROCKER_TLV_OF_DPA_L4_SRC_PORT_MASK, /* __be16 */ - ROCKER_TLV_OF_DPA_ICMP_TYPE, /* u8 */ - ROCKER_TLV_OF_DPA_ICMP_TYPE_MASK, /* u8 */ - ROCKER_TLV_OF_DPA_ICMP_CODE, /* u8 */ - ROCKER_TLV_OF_DPA_ICMP_CODE_MASK, /* u8 */ - ROCKER_TLV_OF_DPA_IPV6_LABEL, /* __be32 */ - ROCKER_TLV_OF_DPA_IPV6_LABEL_MASK, /* __be32 */ - ROCKER_TLV_OF_DPA_QUEUE_ID_ACTION, /* u8 */ - ROCKER_TLV_OF_DPA_NEW_QUEUE_ID, /* u8 */ - ROCKER_TLV_OF_DPA_CLEAR_ACTIONS, /* u32 */ - ROCKER_TLV_OF_DPA_POP_VLAN, /* u8 */ - ROCKER_TLV_OF_DPA_TTL_CHECK, /* u8 */ - ROCKER_TLV_OF_DPA_COPY_CPU_ACTION, /* u8 */ - - __ROCKER_TLV_OF_DPA_MAX, - ROCKER_TLV_OF_DPA_MAX = __ROCKER_TLV_OF_DPA_MAX - 1, -}; - -/* - * OF-DPA table IDs - */ - -enum rocker_of_dpa_table_id { - ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT = 0, - ROCKER_OF_DPA_TABLE_ID_VLAN = 10, - ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC = 20, - ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING = 30, - ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING = 40, - ROCKER_OF_DPA_TABLE_ID_BRIDGING = 50, - ROCKER_OF_DPA_TABLE_ID_ACL_POLICY = 60, -}; - -/* - * OF-DPA flow stats - */ - -enum { - ROCKER_TLV_OF_DPA_FLOW_STAT_UNSPEC, - ROCKER_TLV_OF_DPA_FLOW_STAT_DURATION, /* u32 */ - ROCKER_TLV_OF_DPA_FLOW_STAT_RX_PKTS, /* u64 */ - ROCKER_TLV_OF_DPA_FLOW_STAT_TX_PKTS, /* u64 */ - - __ROCKER_TLV_OF_DPA_FLOW_STAT_MAX, - ROCKER_TLV_OF_DPA_FLOW_STAT_MAX = __ROCKER_TLV_OF_DPA_FLOW_STAT_MAX - 1, -}; - -/* - * OF-DPA group types - */ - -enum rocker_of_dpa_group_type { - ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE = 0, - ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE, - ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST, - ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST, - ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD, - ROCKER_OF_DPA_GROUP_TYPE_L3_INTERFACE, - ROCKER_OF_DPA_GROUP_TYPE_L3_MCAST, - ROCKER_OF_DPA_GROUP_TYPE_L3_ECMP, - ROCKER_OF_DPA_GROUP_TYPE_L2_OVERLAY, -}; - -/* - * OF-DPA group L2 overlay types - */ - -enum rocker_of_dpa_overlay_type { - ROCKER_OF_DPA_OVERLAY_TYPE_FLOOD_UCAST = 0, - ROCKER_OF_DPA_OVERLAY_TYPE_FLOOD_MCAST, - ROCKER_OF_DPA_OVERLAY_TYPE_MCAST_UCAST, - ROCKER_OF_DPA_OVERLAY_TYPE_MCAST_MCAST, -}; - -/* - * OF-DPA group ID encoding - */ - -#define ROCKER_GROUP_TYPE_SHIFT 28 -#define ROCKER_GROUP_TYPE_MASK 0xf0000000 -#define ROCKER_GROUP_VLAN_ID_SHIFT 16 -#define ROCKER_GROUP_VLAN_ID_MASK 0x0fff0000 -#define ROCKER_GROUP_PORT_SHIFT 0 -#define ROCKER_GROUP_PORT_MASK 0x0000ffff -#define ROCKER_GROUP_TUNNEL_ID_SHIFT 12 -#define ROCKER_GROUP_TUNNEL_ID_MASK 0x0ffff000 -#define ROCKER_GROUP_SUBTYPE_SHIFT 10 -#define ROCKER_GROUP_SUBTYPE_MASK 0x00000c00 -#define ROCKER_GROUP_INDEX_SHIFT 0 -#define ROCKER_GROUP_INDEX_MASK 0x0000ffff -#define ROCKER_GROUP_INDEX_LONG_SHIFT 0 -#define ROCKER_GROUP_INDEX_LONG_MASK 0x0fffffff - -#define ROCKER_GROUP_TYPE_GET(group_id) \ - (((group_id) & ROCKER_GROUP_TYPE_MASK) >> ROCKER_GROUP_TYPE_SHIFT) -#define ROCKER_GROUP_TYPE_SET(type) \ - (((type) << ROCKER_GROUP_TYPE_SHIFT) & ROCKER_GROUP_TYPE_MASK) -#define ROCKER_GROUP_VLAN_GET(group_id) \ - (((group_id) & ROCKER_GROUP_VLAN_ID_MASK) >> ROCKER_GROUP_VLAN_ID_SHIFT) -#define ROCKER_GROUP_VLAN_SET(vlan_id) \ - (((vlan_id) << ROCKER_GROUP_VLAN_ID_SHIFT) & ROCKER_GROUP_VLAN_ID_MASK) -#define ROCKER_GROUP_PORT_GET(group_id) \ - (((group_id) & ROCKER_GROUP_PORT_MASK) >> ROCKER_GROUP_PORT_SHIFT) -#define ROCKER_GROUP_PORT_SET(port) \ - (((port) << ROCKER_GROUP_PORT_SHIFT) & ROCKER_GROUP_PORT_MASK) -#define ROCKER_GROUP_INDEX_GET(group_id) \ - (((group_id) & ROCKER_GROUP_INDEX_MASK) >> ROCKER_GROUP_INDEX_SHIFT) -#define ROCKER_GROUP_INDEX_SET(index) \ - (((index) << ROCKER_GROUP_INDEX_SHIFT) & ROCKER_GROUP_INDEX_MASK) -#define ROCKER_GROUP_INDEX_LONG_GET(group_id) \ - (((group_id) & ROCKER_GROUP_INDEX_LONG_MASK) >> \ - ROCKER_GROUP_INDEX_LONG_SHIFT) -#define ROCKER_GROUP_INDEX_LONG_SET(index) \ - (((index) << ROCKER_GROUP_INDEX_LONG_SHIFT) & \ - ROCKER_GROUP_INDEX_LONG_MASK) - -#define ROCKER_GROUP_NONE 0 -#define ROCKER_GROUP_L2_INTERFACE(vlan_id, port) \ - (ROCKER_GROUP_TYPE_SET(ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE) |\ - ROCKER_GROUP_VLAN_SET(ntohs(vlan_id)) | ROCKER_GROUP_PORT_SET(port)) -#define ROCKER_GROUP_L2_REWRITE(index) \ - (ROCKER_GROUP_TYPE_SET(ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE) |\ - ROCKER_GROUP_INDEX_LONG_SET(index)) -#define ROCKER_GROUP_L2_MCAST(vlan_id, index) \ - (ROCKER_GROUP_TYPE_SET(ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST) |\ - ROCKER_GROUP_VLAN_SET(ntohs(vlan_id)) | ROCKER_GROUP_INDEX_SET(index)) -#define ROCKER_GROUP_L2_FLOOD(vlan_id, index) \ - (ROCKER_GROUP_TYPE_SET(ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD) |\ - ROCKER_GROUP_VLAN_SET(ntohs(vlan_id)) | ROCKER_GROUP_INDEX_SET(index)) -#define ROCKER_GROUP_L3_UNICAST(index) \ - (ROCKER_GROUP_TYPE_SET(ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST) |\ - ROCKER_GROUP_INDEX_LONG_SET(index)) - -/* - * Rocker general purpose registers - */ -#define ROCKER_CONTROL 0x0300 -#define ROCKER_PORT_PHYS_COUNT 0x0304 -#define ROCKER_PORT_PHYS_LINK_STATUS 0x0310 /* 8-byte */ -#define ROCKER_PORT_PHYS_ENABLE 0x0318 /* 8-byte */ -#define ROCKER_SWITCH_ID 0x0320 /* 8-byte */ - -/* - * Rocker control bits - */ -#define ROCKER_CONTROL_RESET (1 << 0) - -#endif /* _ROCKER_HW_ */ diff --git a/qemu/hw/net/rocker/rocker_of_dpa.c b/qemu/hw/net/rocker/rocker_of_dpa.c deleted file mode 100644 index 0a134ebca..000000000 --- a/qemu/hw/net/rocker/rocker_of_dpa.c +++ /dev/null @@ -1,2627 +0,0 @@ -/* - * QEMU rocker switch emulation - OF-DPA flow processing support - * - * Copyright (c) 2014 Scott Feldman - * - * 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. - */ - -#include "qemu/osdep.h" -#include "net/eth.h" -#include "qemu/iov.h" -#include "qemu/timer.h" -#include "qmp-commands.h" - -#include "rocker.h" -#include "rocker_hw.h" -#include "rocker_fp.h" -#include "rocker_tlv.h" -#include "rocker_world.h" -#include "rocker_desc.h" -#include "rocker_of_dpa.h" - -static const MACAddr zero_mac = { .a = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; -static const MACAddr ff_mac = { .a = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; - -typedef struct of_dpa { - World *world; - GHashTable *flow_tbl; - GHashTable *group_tbl; - unsigned int flow_tbl_max_size; - unsigned int group_tbl_max_size; -} OfDpa; - -/* flow_key stolen mostly from OVS - * - * Note: fields that compare with network packet header fields - * are stored in network order (BE) to avoid per-packet field - * byte-swaps. - */ - -typedef struct of_dpa_flow_key { - uint32_t in_pport; /* ingress port */ - uint32_t tunnel_id; /* overlay tunnel id */ - uint32_t tbl_id; /* table id */ - struct { - __be16 vlan_id; /* 0 if no VLAN */ - MACAddr src; /* ethernet source address */ - MACAddr dst; /* ethernet destination address */ - __be16 type; /* ethernet frame type */ - } eth; - struct { - uint8_t proto; /* IP protocol or ARP opcode */ - uint8_t tos; /* IP ToS */ - uint8_t ttl; /* IP TTL/hop limit */ - uint8_t frag; /* one of FRAG_TYPE_* */ - } ip; - union { - struct { - struct { - __be32 src; /* IP source address */ - __be32 dst; /* IP destination address */ - } addr; - union { - struct { - __be16 src; /* TCP/UDP/SCTP source port */ - __be16 dst; /* TCP/UDP/SCTP destination port */ - __be16 flags; /* TCP flags */ - } tp; - struct { - MACAddr sha; /* ARP source hardware address */ - MACAddr tha; /* ARP target hardware address */ - } arp; - }; - } ipv4; - struct { - struct { - Ipv6Addr src; /* IPv6 source address */ - Ipv6Addr dst; /* IPv6 destination address */ - } addr; - __be32 label; /* IPv6 flow label */ - struct { - __be16 src; /* TCP/UDP/SCTP source port */ - __be16 dst; /* TCP/UDP/SCTP destination port */ - __be16 flags; /* TCP flags */ - } tp; - struct { - Ipv6Addr target; /* ND target address */ - MACAddr sll; /* ND source link layer address */ - MACAddr tll; /* ND target link layer address */ - } nd; - } ipv6; - }; - int width; /* how many uint64_t's in key? */ -} OfDpaFlowKey; - -/* Width of key which includes field 'f' in u64s, rounded up */ -#define FLOW_KEY_WIDTH(f) \ - ((offsetof(OfDpaFlowKey, f) + \ - sizeof(((OfDpaFlowKey *)0)->f) + \ - sizeof(uint64_t) - 1) / sizeof(uint64_t)) - -typedef struct of_dpa_flow_action { - uint32_t goto_tbl; - struct { - uint32_t group_id; - uint32_t tun_log_lport; - __be16 vlan_id; - } write; - struct { - __be16 new_vlan_id; - uint32_t out_pport; - uint8_t copy_to_cpu; - __be16 vlan_id; - } apply; -} OfDpaFlowAction; - -typedef struct of_dpa_flow { - uint32_t lpm; - uint32_t priority; - uint32_t hardtime; - uint32_t idletime; - uint64_t cookie; - OfDpaFlowKey key; - OfDpaFlowKey mask; - OfDpaFlowAction action; - struct { - uint64_t hits; - int64_t install_time; - int64_t refresh_time; - uint64_t rx_pkts; - uint64_t tx_pkts; - } stats; -} OfDpaFlow; - -typedef struct of_dpa_flow_pkt_fields { - uint32_t tunnel_id; - struct eth_header *ethhdr; - __be16 *h_proto; - struct vlan_header *vlanhdr; - struct ip_header *ipv4hdr; - struct ip6_header *ipv6hdr; - Ipv6Addr *ipv6_src_addr; - Ipv6Addr *ipv6_dst_addr; -} OfDpaFlowPktFields; - -typedef struct of_dpa_flow_context { - uint32_t in_pport; - uint32_t tunnel_id; - struct iovec *iov; - int iovcnt; - struct eth_header ethhdr_rewrite; - struct vlan_header vlanhdr_rewrite; - struct vlan_header vlanhdr; - OfDpa *of_dpa; - OfDpaFlowPktFields fields; - OfDpaFlowAction action_set; -} OfDpaFlowContext; - -typedef struct of_dpa_flow_match { - OfDpaFlowKey value; - OfDpaFlow *best; -} OfDpaFlowMatch; - -typedef struct of_dpa_group { - uint32_t id; - union { - struct { - uint32_t out_pport; - uint8_t pop_vlan; - } l2_interface; - struct { - uint32_t group_id; - MACAddr src_mac; - MACAddr dst_mac; - __be16 vlan_id; - } l2_rewrite; - struct { - uint16_t group_count; - uint32_t *group_ids; - } l2_flood; - struct { - uint32_t group_id; - MACAddr src_mac; - MACAddr dst_mac; - __be16 vlan_id; - uint8_t ttl_check; - } l3_unicast; - }; -} OfDpaGroup; - -static int of_dpa_mask2prefix(__be32 mask) -{ - int i; - int count = 32; - - for (i = 0; i < 32; i++) { - if (!(ntohl(mask) & ((2 << i) - 1))) { - count--; - } - } - - return count; -} - -#if defined(DEBUG_ROCKER) -static void of_dpa_flow_key_dump(OfDpaFlowKey *key, OfDpaFlowKey *mask) -{ - char buf[512], *b = buf, *mac; - - b += sprintf(b, " tbl %2d", key->tbl_id); - - if (key->in_pport || (mask && mask->in_pport)) { - b += sprintf(b, " in_pport %2d", key->in_pport); - if (mask && mask->in_pport != 0xffffffff) { - b += sprintf(b, "/0x%08x", key->in_pport); - } - } - - if (key->tunnel_id || (mask && mask->tunnel_id)) { - b += sprintf(b, " tun %8d", key->tunnel_id); - if (mask && mask->tunnel_id != 0xffffffff) { - b += sprintf(b, "/0x%08x", key->tunnel_id); - } - } - - if (key->eth.vlan_id || (mask && mask->eth.vlan_id)) { - b += sprintf(b, " vlan %4d", ntohs(key->eth.vlan_id)); - if (mask && mask->eth.vlan_id != 0xffff) { - b += sprintf(b, "/0x%04x", ntohs(key->eth.vlan_id)); - } - } - - if (memcmp(key->eth.src.a, zero_mac.a, ETH_ALEN) || - (mask && memcmp(mask->eth.src.a, zero_mac.a, ETH_ALEN))) { - mac = qemu_mac_strdup_printf(key->eth.src.a); - b += sprintf(b, " src %s", mac); - g_free(mac); - if (mask && memcmp(mask->eth.src.a, ff_mac.a, ETH_ALEN)) { - mac = qemu_mac_strdup_printf(mask->eth.src.a); - b += sprintf(b, "/%s", mac); - g_free(mac); - } - } - - if (memcmp(key->eth.dst.a, zero_mac.a, ETH_ALEN) || - (mask && memcmp(mask->eth.dst.a, zero_mac.a, ETH_ALEN))) { - mac = qemu_mac_strdup_printf(key->eth.dst.a); - b += sprintf(b, " dst %s", mac); - g_free(mac); - if (mask && memcmp(mask->eth.dst.a, ff_mac.a, ETH_ALEN)) { - mac = qemu_mac_strdup_printf(mask->eth.dst.a); - b += sprintf(b, "/%s", mac); - g_free(mac); - } - } - - if (key->eth.type || (mask && mask->eth.type)) { - b += sprintf(b, " type 0x%04x", ntohs(key->eth.type)); - if (mask && mask->eth.type != 0xffff) { - b += sprintf(b, "/0x%04x", ntohs(mask->eth.type)); - } - switch (ntohs(key->eth.type)) { - case 0x0800: - case 0x86dd: - if (key->ip.proto || (mask && mask->ip.proto)) { - b += sprintf(b, " ip proto %2d", key->ip.proto); - if (mask && mask->ip.proto != 0xff) { - b += sprintf(b, "/0x%02x", mask->ip.proto); - } - } - if (key->ip.tos || (mask && mask->ip.tos)) { - b += sprintf(b, " ip tos %2d", key->ip.tos); - if (mask && mask->ip.tos != 0xff) { - b += sprintf(b, "/0x%02x", mask->ip.tos); - } - } - break; - } - switch (ntohs(key->eth.type)) { - case 0x0800: - if (key->ipv4.addr.dst || (mask && mask->ipv4.addr.dst)) { - b += sprintf(b, " dst %s", - inet_ntoa(*(struct in_addr *)&key->ipv4.addr.dst)); - if (mask) { - b += sprintf(b, "/%d", - of_dpa_mask2prefix(mask->ipv4.addr.dst)); - } - } - break; - } - } - - DPRINTF("%s\n", buf); -} -#else -#define of_dpa_flow_key_dump(k, m) -#endif - -static void _of_dpa_flow_match(void *key, void *value, void *user_data) -{ - OfDpaFlow *flow = value; - OfDpaFlowMatch *match = user_data; - uint64_t *k = (uint64_t *)&flow->key; - uint64_t *m = (uint64_t *)&flow->mask; - uint64_t *v = (uint64_t *)&match->value; - int i; - - if (flow->key.tbl_id == match->value.tbl_id) { - of_dpa_flow_key_dump(&flow->key, &flow->mask); - } - - if (flow->key.width > match->value.width) { - return; - } - - for (i = 0; i < flow->key.width; i++, k++, m++, v++) { - if ((~*k & *m & *v) | (*k & *m & ~*v)) { - return; - } - } - - DPRINTF("match\n"); - - if (!match->best || - flow->priority > match->best->priority || - flow->lpm > match->best->lpm) { - match->best = flow; - } -} - -static OfDpaFlow *of_dpa_flow_match(OfDpa *of_dpa, OfDpaFlowMatch *match) -{ - DPRINTF("\nnew search\n"); - of_dpa_flow_key_dump(&match->value, NULL); - - g_hash_table_foreach(of_dpa->flow_tbl, _of_dpa_flow_match, match); - - return match->best; -} - -static OfDpaFlow *of_dpa_flow_find(OfDpa *of_dpa, uint64_t cookie) -{ - return g_hash_table_lookup(of_dpa->flow_tbl, &cookie); -} - -static int of_dpa_flow_add(OfDpa *of_dpa, OfDpaFlow *flow) -{ - g_hash_table_insert(of_dpa->flow_tbl, &flow->cookie, flow); - - return ROCKER_OK; -} - -static void of_dpa_flow_del(OfDpa *of_dpa, OfDpaFlow *flow) -{ - g_hash_table_remove(of_dpa->flow_tbl, &flow->cookie); -} - -static OfDpaFlow *of_dpa_flow_alloc(uint64_t cookie) -{ - OfDpaFlow *flow; - int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) / 1000; - - flow = g_new0(OfDpaFlow, 1); - if (!flow) { - return NULL; - } - - flow->cookie = cookie; - flow->mask.tbl_id = 0xffffffff; - - flow->stats.install_time = flow->stats.refresh_time = now; - - return flow; -} - -static void of_dpa_flow_pkt_hdr_reset(OfDpaFlowContext *fc) -{ - OfDpaFlowPktFields *fields = &fc->fields; - - fc->iov[0].iov_base = fields->ethhdr; - fc->iov[0].iov_len = sizeof(struct eth_header); - fc->iov[1].iov_base = fields->vlanhdr; - fc->iov[1].iov_len = fields->vlanhdr ? sizeof(struct vlan_header) : 0; -} - -static void of_dpa_flow_pkt_parse(OfDpaFlowContext *fc, - const struct iovec *iov, int iovcnt) -{ - OfDpaFlowPktFields *fields = &fc->fields; - size_t sofar = 0; - int i; - - sofar += sizeof(struct eth_header); - if (iov->iov_len < sofar) { - DPRINTF("flow_pkt_parse underrun on eth_header\n"); - return; - } - - fields->ethhdr = iov->iov_base; - fields->h_proto = &fields->ethhdr->h_proto; - - if (ntohs(*fields->h_proto) == ETH_P_VLAN) { - sofar += sizeof(struct vlan_header); - if (iov->iov_len < sofar) { - DPRINTF("flow_pkt_parse underrun on vlan_header\n"); - return; - } - fields->vlanhdr = (struct vlan_header *)(fields->ethhdr + 1); - fields->h_proto = &fields->vlanhdr->h_proto; - } - - switch (ntohs(*fields->h_proto)) { - case ETH_P_IP: - sofar += sizeof(struct ip_header); - if (iov->iov_len < sofar) { - DPRINTF("flow_pkt_parse underrun on ip_header\n"); - return; - } - fields->ipv4hdr = (struct ip_header *)(fields->h_proto + 1); - break; - case ETH_P_IPV6: - sofar += sizeof(struct ip6_header); - if (iov->iov_len < sofar) { - DPRINTF("flow_pkt_parse underrun on ip6_header\n"); - return; - } - fields->ipv6hdr = (struct ip6_header *)(fields->h_proto + 1); - break; - } - - /* To facilitate (potential) VLAN tag insertion, Make a - * copy of the iov and insert two new vectors at the - * beginning for eth hdr and vlan hdr. No data is copied, - * just the vectors. - */ - - of_dpa_flow_pkt_hdr_reset(fc); - - fc->iov[2].iov_base = fields->h_proto + 1; - fc->iov[2].iov_len = iov->iov_len - fc->iov[0].iov_len - fc->iov[1].iov_len; - - for (i = 1; i < iovcnt; i++) { - fc->iov[i+2] = iov[i]; - } - - fc->iovcnt = iovcnt + 2; -} - -static void of_dpa_flow_pkt_insert_vlan(OfDpaFlowContext *fc, __be16 vlan_id) -{ - OfDpaFlowPktFields *fields = &fc->fields; - uint16_t h_proto = fields->ethhdr->h_proto; - - if (fields->vlanhdr) { - DPRINTF("flow_pkt_insert_vlan packet already has vlan\n"); - return; - } - - fields->ethhdr->h_proto = htons(ETH_P_VLAN); - fields->vlanhdr = &fc->vlanhdr; - fields->vlanhdr->h_tci = vlan_id; - fields->vlanhdr->h_proto = h_proto; - fields->h_proto = &fields->vlanhdr->h_proto; - - fc->iov[1].iov_base = fields->vlanhdr; - fc->iov[1].iov_len = sizeof(struct vlan_header); -} - -static void of_dpa_flow_pkt_strip_vlan(OfDpaFlowContext *fc) -{ - OfDpaFlowPktFields *fields = &fc->fields; - - if (!fields->vlanhdr) { - return; - } - - fc->iov[0].iov_len -= sizeof(fields->ethhdr->h_proto); - fc->iov[1].iov_base = fields->h_proto; - fc->iov[1].iov_len = sizeof(fields->ethhdr->h_proto); -} - -static void of_dpa_flow_pkt_hdr_rewrite(OfDpaFlowContext *fc, - uint8_t *src_mac, uint8_t *dst_mac, - __be16 vlan_id) -{ - OfDpaFlowPktFields *fields = &fc->fields; - - if (src_mac || dst_mac) { - memcpy(&fc->ethhdr_rewrite, fields->ethhdr, sizeof(struct eth_header)); - if (src_mac && memcmp(src_mac, zero_mac.a, ETH_ALEN)) { - memcpy(fc->ethhdr_rewrite.h_source, src_mac, ETH_ALEN); - } - if (dst_mac && memcmp(dst_mac, zero_mac.a, ETH_ALEN)) { - memcpy(fc->ethhdr_rewrite.h_dest, dst_mac, ETH_ALEN); - } - fc->iov[0].iov_base = &fc->ethhdr_rewrite; - } - - if (vlan_id && fields->vlanhdr) { - fc->vlanhdr_rewrite = fc->vlanhdr; - fc->vlanhdr_rewrite.h_tci = vlan_id; - fc->iov[1].iov_base = &fc->vlanhdr_rewrite; - } -} - -static void of_dpa_flow_ig_tbl(OfDpaFlowContext *fc, uint32_t tbl_id); - -static void of_dpa_ig_port_build_match(OfDpaFlowContext *fc, - OfDpaFlowMatch *match) -{ - match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT; - match->value.in_pport = fc->in_pport; - match->value.width = FLOW_KEY_WIDTH(tbl_id); -} - -static void of_dpa_ig_port_miss(OfDpaFlowContext *fc) -{ - uint32_t port; - - /* The default on miss is for packets from physical ports - * to go to the VLAN Flow Table. There is no default rule - * for packets from logical ports, which are dropped on miss. - */ - - if (fp_port_from_pport(fc->in_pport, &port)) { - of_dpa_flow_ig_tbl(fc, ROCKER_OF_DPA_TABLE_ID_VLAN); - } -} - -static void of_dpa_vlan_build_match(OfDpaFlowContext *fc, - OfDpaFlowMatch *match) -{ - match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_VLAN; - match->value.in_pport = fc->in_pport; - if (fc->fields.vlanhdr) { - match->value.eth.vlan_id = fc->fields.vlanhdr->h_tci; - } - match->value.width = FLOW_KEY_WIDTH(eth.vlan_id); -} - -static void of_dpa_vlan_insert(OfDpaFlowContext *fc, - OfDpaFlow *flow) -{ - if (flow->action.apply.new_vlan_id) { - of_dpa_flow_pkt_insert_vlan(fc, flow->action.apply.new_vlan_id); - } -} - -static void of_dpa_term_mac_build_match(OfDpaFlowContext *fc, - OfDpaFlowMatch *match) -{ - match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC; - match->value.in_pport = fc->in_pport; - match->value.eth.type = *fc->fields.h_proto; - match->value.eth.vlan_id = fc->fields.vlanhdr->h_tci; - memcpy(match->value.eth.dst.a, fc->fields.ethhdr->h_dest, - sizeof(match->value.eth.dst.a)); - match->value.width = FLOW_KEY_WIDTH(eth.type); -} - -static void of_dpa_term_mac_miss(OfDpaFlowContext *fc) -{ - of_dpa_flow_ig_tbl(fc, ROCKER_OF_DPA_TABLE_ID_BRIDGING); -} - -static void of_dpa_apply_actions(OfDpaFlowContext *fc, - OfDpaFlow *flow) -{ - fc->action_set.apply.copy_to_cpu = flow->action.apply.copy_to_cpu; - fc->action_set.apply.vlan_id = flow->key.eth.vlan_id; -} - -static void of_dpa_bridging_build_match(OfDpaFlowContext *fc, - OfDpaFlowMatch *match) -{ - match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_BRIDGING; - if (fc->fields.vlanhdr) { - match->value.eth.vlan_id = fc->fields.vlanhdr->h_tci; - } else if (fc->tunnel_id) { - match->value.tunnel_id = fc->tunnel_id; - } - memcpy(match->value.eth.dst.a, fc->fields.ethhdr->h_dest, - sizeof(match->value.eth.dst.a)); - match->value.width = FLOW_KEY_WIDTH(eth.dst); -} - -static void of_dpa_bridging_learn(OfDpaFlowContext *fc, - OfDpaFlow *dst_flow) -{ - OfDpaFlowMatch match = { { 0, }, }; - OfDpaFlow *flow; - uint8_t *addr; - uint16_t vlan_id; - int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) / 1000; - int64_t refresh_delay = 1; - - /* Do a lookup in bridge table by src_mac/vlan */ - - addr = fc->fields.ethhdr->h_source; - vlan_id = fc->fields.vlanhdr->h_tci; - - match.value.tbl_id = ROCKER_OF_DPA_TABLE_ID_BRIDGING; - match.value.eth.vlan_id = vlan_id; - memcpy(match.value.eth.dst.a, addr, sizeof(match.value.eth.dst.a)); - match.value.width = FLOW_KEY_WIDTH(eth.dst); - - flow = of_dpa_flow_match(fc->of_dpa, &match); - if (flow) { - if (!memcmp(flow->mask.eth.dst.a, ff_mac.a, - sizeof(flow->mask.eth.dst.a))) { - /* src_mac/vlan already learned; if in_port and out_port - * don't match, the end station has moved and the port - * needs updating */ - /* XXX implement the in_port/out_port check */ - if (now - flow->stats.refresh_time < refresh_delay) { - return; - } - flow->stats.refresh_time = now; - } - } - - /* Let driver know about mac/vlan. This may be a new mac/vlan - * or a refresh of existing mac/vlan that's been hit after the - * refresh_delay. - */ - - rocker_event_mac_vlan_seen(world_rocker(fc->of_dpa->world), - fc->in_pport, addr, vlan_id); -} - -static void of_dpa_bridging_miss(OfDpaFlowContext *fc) -{ - of_dpa_bridging_learn(fc, NULL); - of_dpa_flow_ig_tbl(fc, ROCKER_OF_DPA_TABLE_ID_ACL_POLICY); -} - -static void of_dpa_bridging_action_write(OfDpaFlowContext *fc, - OfDpaFlow *flow) -{ - if (flow->action.write.group_id != ROCKER_GROUP_NONE) { - fc->action_set.write.group_id = flow->action.write.group_id; - } - fc->action_set.write.tun_log_lport = flow->action.write.tun_log_lport; -} - -static void of_dpa_unicast_routing_build_match(OfDpaFlowContext *fc, - OfDpaFlowMatch *match) -{ - match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING; - match->value.eth.type = *fc->fields.h_proto; - if (fc->fields.ipv4hdr) { - match->value.ipv4.addr.dst = fc->fields.ipv4hdr->ip_dst; - } - if (fc->fields.ipv6_dst_addr) { - memcpy(&match->value.ipv6.addr.dst, fc->fields.ipv6_dst_addr, - sizeof(match->value.ipv6.addr.dst)); - } - match->value.width = FLOW_KEY_WIDTH(ipv6.addr.dst); -} - -static void of_dpa_unicast_routing_miss(OfDpaFlowContext *fc) -{ - of_dpa_flow_ig_tbl(fc, ROCKER_OF_DPA_TABLE_ID_ACL_POLICY); -} - -static void of_dpa_unicast_routing_action_write(OfDpaFlowContext *fc, - OfDpaFlow *flow) -{ - if (flow->action.write.group_id != ROCKER_GROUP_NONE) { - fc->action_set.write.group_id = flow->action.write.group_id; - } -} - -static void -of_dpa_multicast_routing_build_match(OfDpaFlowContext *fc, - OfDpaFlowMatch *match) -{ - match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING; - match->value.eth.type = *fc->fields.h_proto; - match->value.eth.vlan_id = fc->fields.vlanhdr->h_tci; - if (fc->fields.ipv4hdr) { - match->value.ipv4.addr.src = fc->fields.ipv4hdr->ip_src; - match->value.ipv4.addr.dst = fc->fields.ipv4hdr->ip_dst; - } - if (fc->fields.ipv6_src_addr) { - memcpy(&match->value.ipv6.addr.src, fc->fields.ipv6_src_addr, - sizeof(match->value.ipv6.addr.src)); - } - if (fc->fields.ipv6_dst_addr) { - memcpy(&match->value.ipv6.addr.dst, fc->fields.ipv6_dst_addr, - sizeof(match->value.ipv6.addr.dst)); - } - match->value.width = FLOW_KEY_WIDTH(ipv6.addr.dst); -} - -static void of_dpa_multicast_routing_miss(OfDpaFlowContext *fc) -{ - of_dpa_flow_ig_tbl(fc, ROCKER_OF_DPA_TABLE_ID_ACL_POLICY); -} - -static void -of_dpa_multicast_routing_action_write(OfDpaFlowContext *fc, - OfDpaFlow *flow) -{ - if (flow->action.write.group_id != ROCKER_GROUP_NONE) { - fc->action_set.write.group_id = flow->action.write.group_id; - } - fc->action_set.write.vlan_id = flow->action.write.vlan_id; -} - -static void of_dpa_acl_build_match(OfDpaFlowContext *fc, - OfDpaFlowMatch *match) -{ - match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_ACL_POLICY; - match->value.in_pport = fc->in_pport; - memcpy(match->value.eth.src.a, fc->fields.ethhdr->h_source, - sizeof(match->value.eth.src.a)); - memcpy(match->value.eth.dst.a, fc->fields.ethhdr->h_dest, - sizeof(match->value.eth.dst.a)); - match->value.eth.type = *fc->fields.h_proto; - match->value.eth.vlan_id = fc->fields.vlanhdr->h_tci; - match->value.width = FLOW_KEY_WIDTH(eth.type); - if (fc->fields.ipv4hdr) { - match->value.ip.proto = fc->fields.ipv4hdr->ip_p; - match->value.ip.tos = fc->fields.ipv4hdr->ip_tos; - match->value.width = FLOW_KEY_WIDTH(ip.tos); - } else if (fc->fields.ipv6hdr) { - match->value.ip.proto = - fc->fields.ipv6hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt; - match->value.ip.tos = 0; /* XXX what goes here? */ - match->value.width = FLOW_KEY_WIDTH(ip.tos); - } -} - -static void of_dpa_eg(OfDpaFlowContext *fc); -static void of_dpa_acl_hit(OfDpaFlowContext *fc, - OfDpaFlow *dst_flow) -{ - of_dpa_eg(fc); -} - -static void of_dpa_acl_action_write(OfDpaFlowContext *fc, - OfDpaFlow *flow) -{ - if (flow->action.write.group_id != ROCKER_GROUP_NONE) { - fc->action_set.write.group_id = flow->action.write.group_id; - } -} - -static void of_dpa_drop(OfDpaFlowContext *fc) -{ - /* drop packet */ -} - -static OfDpaGroup *of_dpa_group_find(OfDpa *of_dpa, - uint32_t group_id) -{ - return g_hash_table_lookup(of_dpa->group_tbl, &group_id); -} - -static int of_dpa_group_add(OfDpa *of_dpa, OfDpaGroup *group) -{ - g_hash_table_insert(of_dpa->group_tbl, &group->id, group); - - return 0; -} - -#if 0 -static int of_dpa_group_mod(OfDpa *of_dpa, OfDpaGroup *group) -{ - OfDpaGroup *old_group = of_dpa_group_find(of_dpa, group->id); - - if (!old_group) { - return -ENOENT; - } - - /* XXX */ - - return 0; -} -#endif - -static int of_dpa_group_del(OfDpa *of_dpa, OfDpaGroup *group) -{ - g_hash_table_remove(of_dpa->group_tbl, &group->id); - - return 0; -} - -#if 0 -static int of_dpa_group_get_stats(OfDpa *of_dpa, uint32_t id) -{ - OfDpaGroup *group = of_dpa_group_find(of_dpa, id); - - if (!group) { - return -ENOENT; - } - - /* XXX get/return stats */ - - return 0; -} -#endif - -static OfDpaGroup *of_dpa_group_alloc(uint32_t id) -{ - OfDpaGroup *group = g_new0(OfDpaGroup, 1); - - if (!group) { - return NULL; - } - - group->id = id; - - return group; -} - -static void of_dpa_output_l2_interface(OfDpaFlowContext *fc, - OfDpaGroup *group) -{ - uint8_t copy_to_cpu = fc->action_set.apply.copy_to_cpu; - - if (group->l2_interface.pop_vlan) { - of_dpa_flow_pkt_strip_vlan(fc); - } - - /* Note: By default, and as per the OpenFlow 1.3.1 - * specification, a packet cannot be forwarded back - * to the IN_PORT from which it came in. An action - * bucket that specifies the particular packet's - * egress port is not evaluated. - */ - - if (group->l2_interface.out_pport == 0) { - rx_produce(fc->of_dpa->world, fc->in_pport, fc->iov, fc->iovcnt, - copy_to_cpu); - } else if (group->l2_interface.out_pport != fc->in_pport) { - rocker_port_eg(world_rocker(fc->of_dpa->world), - group->l2_interface.out_pport, - fc->iov, fc->iovcnt); - } -} - -static void of_dpa_output_l2_rewrite(OfDpaFlowContext *fc, - OfDpaGroup *group) -{ - OfDpaGroup *l2_group = - of_dpa_group_find(fc->of_dpa, group->l2_rewrite.group_id); - - if (!l2_group) { - return; - } - - of_dpa_flow_pkt_hdr_rewrite(fc, group->l2_rewrite.src_mac.a, - group->l2_rewrite.dst_mac.a, - group->l2_rewrite.vlan_id); - of_dpa_output_l2_interface(fc, l2_group); -} - -static void of_dpa_output_l2_flood(OfDpaFlowContext *fc, - OfDpaGroup *group) -{ - OfDpaGroup *l2_group; - int i; - - for (i = 0; i < group->l2_flood.group_count; i++) { - of_dpa_flow_pkt_hdr_reset(fc); - l2_group = of_dpa_group_find(fc->of_dpa, group->l2_flood.group_ids[i]); - if (!l2_group) { - continue; - } - switch (ROCKER_GROUP_TYPE_GET(l2_group->id)) { - case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE: - of_dpa_output_l2_interface(fc, l2_group); - break; - case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE: - of_dpa_output_l2_rewrite(fc, l2_group); - break; - } - } -} - -static void of_dpa_output_l3_unicast(OfDpaFlowContext *fc, OfDpaGroup *group) -{ - OfDpaGroup *l2_group = - of_dpa_group_find(fc->of_dpa, group->l3_unicast.group_id); - - if (!l2_group) { - return; - } - - of_dpa_flow_pkt_hdr_rewrite(fc, group->l3_unicast.src_mac.a, - group->l3_unicast.dst_mac.a, - group->l3_unicast.vlan_id); - /* XXX need ttl_check */ - of_dpa_output_l2_interface(fc, l2_group); -} - -static void of_dpa_eg(OfDpaFlowContext *fc) -{ - OfDpaFlowAction *set = &fc->action_set; - OfDpaGroup *group; - uint32_t group_id; - - /* send a copy of pkt to CPU (controller)? */ - - if (set->apply.copy_to_cpu) { - group_id = ROCKER_GROUP_L2_INTERFACE(set->apply.vlan_id, 0); - group = of_dpa_group_find(fc->of_dpa, group_id); - if (group) { - of_dpa_output_l2_interface(fc, group); - of_dpa_flow_pkt_hdr_reset(fc); - } - } - - /* process group write actions */ - - if (!set->write.group_id) { - return; - } - - group = of_dpa_group_find(fc->of_dpa, set->write.group_id); - if (!group) { - return; - } - - switch (ROCKER_GROUP_TYPE_GET(group->id)) { - case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE: - of_dpa_output_l2_interface(fc, group); - break; - case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE: - of_dpa_output_l2_rewrite(fc, group); - break; - case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD: - case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST: - of_dpa_output_l2_flood(fc, group); - break; - case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST: - of_dpa_output_l3_unicast(fc, group); - break; - } -} - -typedef struct of_dpa_flow_tbl_ops { - void (*build_match)(OfDpaFlowContext *fc, OfDpaFlowMatch *match); - void (*hit)(OfDpaFlowContext *fc, OfDpaFlow *flow); - void (*miss)(OfDpaFlowContext *fc); - void (*hit_no_goto)(OfDpaFlowContext *fc); - void (*action_apply)(OfDpaFlowContext *fc, OfDpaFlow *flow); - void (*action_write)(OfDpaFlowContext *fc, OfDpaFlow *flow); -} OfDpaFlowTblOps; - -static OfDpaFlowTblOps of_dpa_tbl_ops[] = { - [ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT] = { - .build_match = of_dpa_ig_port_build_match, - .miss = of_dpa_ig_port_miss, - .hit_no_goto = of_dpa_drop, - }, - [ROCKER_OF_DPA_TABLE_ID_VLAN] = { - .build_match = of_dpa_vlan_build_match, - .hit_no_goto = of_dpa_drop, - .action_apply = of_dpa_vlan_insert, - }, - [ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC] = { - .build_match = of_dpa_term_mac_build_match, - .miss = of_dpa_term_mac_miss, - .hit_no_goto = of_dpa_drop, - .action_apply = of_dpa_apply_actions, - }, - [ROCKER_OF_DPA_TABLE_ID_BRIDGING] = { - .build_match = of_dpa_bridging_build_match, - .hit = of_dpa_bridging_learn, - .miss = of_dpa_bridging_miss, - .hit_no_goto = of_dpa_drop, - .action_apply = of_dpa_apply_actions, - .action_write = of_dpa_bridging_action_write, - }, - [ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING] = { - .build_match = of_dpa_unicast_routing_build_match, - .miss = of_dpa_unicast_routing_miss, - .hit_no_goto = of_dpa_drop, - .action_write = of_dpa_unicast_routing_action_write, - }, - [ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING] = { - .build_match = of_dpa_multicast_routing_build_match, - .miss = of_dpa_multicast_routing_miss, - .hit_no_goto = of_dpa_drop, - .action_write = of_dpa_multicast_routing_action_write, - }, - [ROCKER_OF_DPA_TABLE_ID_ACL_POLICY] = { - .build_match = of_dpa_acl_build_match, - .hit = of_dpa_acl_hit, - .miss = of_dpa_eg, - .action_apply = of_dpa_apply_actions, - .action_write = of_dpa_acl_action_write, - }, -}; - -static void of_dpa_flow_ig_tbl(OfDpaFlowContext *fc, uint32_t tbl_id) -{ - OfDpaFlowTblOps *ops = &of_dpa_tbl_ops[tbl_id]; - OfDpaFlowMatch match = { { 0, }, }; - OfDpaFlow *flow; - - if (ops->build_match) { - ops->build_match(fc, &match); - } else { - return; - } - - flow = of_dpa_flow_match(fc->of_dpa, &match); - if (!flow) { - if (ops->miss) { - ops->miss(fc); - } - return; - } - - flow->stats.hits++; - - if (ops->action_apply) { - ops->action_apply(fc, flow); - } - - if (ops->action_write) { - ops->action_write(fc, flow); - } - - if (ops->hit) { - ops->hit(fc, flow); - } - - if (flow->action.goto_tbl) { - of_dpa_flow_ig_tbl(fc, flow->action.goto_tbl); - } else if (ops->hit_no_goto) { - ops->hit_no_goto(fc); - } - - /* drop packet */ -} - -static ssize_t of_dpa_ig(World *world, uint32_t pport, - const struct iovec *iov, int iovcnt) -{ - struct iovec iov_copy[iovcnt + 2]; - OfDpaFlowContext fc = { - .of_dpa = world_private(world), - .in_pport = pport, - .iov = iov_copy, - .iovcnt = iovcnt + 2, - }; - - of_dpa_flow_pkt_parse(&fc, iov, iovcnt); - of_dpa_flow_ig_tbl(&fc, ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT); - - return iov_size(iov, iovcnt); -} - -#define ROCKER_TUNNEL_LPORT 0x00010000 - -static int of_dpa_cmd_add_ig_port(OfDpaFlow *flow, RockerTlv **flow_tlvs) -{ - OfDpaFlowKey *key = &flow->key; - OfDpaFlowKey *mask = &flow->mask; - OfDpaFlowAction *action = &flow->action; - bool overlay_tunnel; - - if (!flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT] || - !flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) { - return -ROCKER_EINVAL; - } - - key->tbl_id = ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT; - key->width = FLOW_KEY_WIDTH(tbl_id); - - key->in_pport = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT]); - if (flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK]) { - mask->in_pport = - rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK]); - } - - overlay_tunnel = !!(key->in_pport & ROCKER_TUNNEL_LPORT); - - action->goto_tbl = - rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]); - - if (!overlay_tunnel && action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_VLAN) { - return -ROCKER_EINVAL; - } - - if (overlay_tunnel && action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_BRIDGING) { - return -ROCKER_EINVAL; - } - - return ROCKER_OK; -} - -static int of_dpa_cmd_add_vlan(OfDpaFlow *flow, RockerTlv **flow_tlvs) -{ - OfDpaFlowKey *key = &flow->key; - OfDpaFlowKey *mask = &flow->mask; - OfDpaFlowAction *action = &flow->action; - uint32_t port; - bool untagged; - - if (!flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT] || - !flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) { - DPRINTF("Must give in_pport and vlan_id to install VLAN tbl entry\n"); - return -ROCKER_EINVAL; - } - - key->tbl_id = ROCKER_OF_DPA_TABLE_ID_VLAN; - key->width = FLOW_KEY_WIDTH(eth.vlan_id); - - key->in_pport = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT]); - if (!fp_port_from_pport(key->in_pport, &port)) { - DPRINTF("in_pport (%d) not a front-panel port\n", key->in_pport); - return -ROCKER_EINVAL; - } - mask->in_pport = 0xffffffff; - - key->eth.vlan_id = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]); - - if (flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]) { - mask->eth.vlan_id = - rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]); - } - - if (key->eth.vlan_id) { - untagged = false; /* filtering */ - } else { - untagged = true; - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) { - action->goto_tbl = - rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]); - if (action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC) { - DPRINTF("Goto tbl (%d) must be TERM_MAC\n", action->goto_tbl); - return -ROCKER_EINVAL; - } - } - - if (untagged) { - if (!flow_tlvs[ROCKER_TLV_OF_DPA_NEW_VLAN_ID]) { - DPRINTF("Must specify new vlan_id if untagged\n"); - return -ROCKER_EINVAL; - } - action->apply.new_vlan_id = - rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_NEW_VLAN_ID]); - if (1 > ntohs(action->apply.new_vlan_id) || - ntohs(action->apply.new_vlan_id) > 4095) { - DPRINTF("New vlan_id (%d) must be between 1 and 4095\n", - ntohs(action->apply.new_vlan_id)); - return -ROCKER_EINVAL; - } - } - - return ROCKER_OK; -} - -static int of_dpa_cmd_add_term_mac(OfDpaFlow *flow, RockerTlv **flow_tlvs) -{ - OfDpaFlowKey *key = &flow->key; - OfDpaFlowKey *mask = &flow->mask; - OfDpaFlowAction *action = &flow->action; - const MACAddr ipv4_mcast = { .a = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 } }; - const MACAddr ipv4_mask = { .a = { 0xff, 0xff, 0xff, 0x80, 0x00, 0x00 } }; - const MACAddr ipv6_mcast = { .a = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 } }; - const MACAddr ipv6_mask = { .a = { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } }; - uint32_t port; - bool unicast = false; - bool multicast = false; - - if (!flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT] || - !flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK] || - !flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE] || - !flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC] || - !flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK] || - !flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID] || - !flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]) { - return -ROCKER_EINVAL; - } - - key->tbl_id = ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC; - key->width = FLOW_KEY_WIDTH(eth.type); - - key->in_pport = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT]); - if (!fp_port_from_pport(key->in_pport, &port)) { - return -ROCKER_EINVAL; - } - mask->in_pport = - rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK]); - - key->eth.type = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]); - if (key->eth.type != htons(0x0800) && key->eth.type != htons(0x86dd)) { - return -ROCKER_EINVAL; - } - mask->eth.type = htons(0xffff); - - memcpy(key->eth.dst.a, - rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]), - sizeof(key->eth.dst.a)); - memcpy(mask->eth.dst.a, - rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK]), - sizeof(mask->eth.dst.a)); - - if ((key->eth.dst.a[0] & 0x01) == 0x00) { - unicast = true; - } - - /* only two wildcard rules are acceptable for IPv4 and IPv6 multicast */ - if (memcmp(key->eth.dst.a, ipv4_mcast.a, sizeof(key->eth.dst.a)) == 0 && - memcmp(mask->eth.dst.a, ipv4_mask.a, sizeof(mask->eth.dst.a)) == 0) { - multicast = true; - } - if (memcmp(key->eth.dst.a, ipv6_mcast.a, sizeof(key->eth.dst.a)) == 0 && - memcmp(mask->eth.dst.a, ipv6_mask.a, sizeof(mask->eth.dst.a)) == 0) { - multicast = true; - } - - if (!unicast && !multicast) { - return -ROCKER_EINVAL; - } - - key->eth.vlan_id = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]); - mask->eth.vlan_id = - rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]); - - if (flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) { - action->goto_tbl = - rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]); - - if (action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING && - action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING) { - return -ROCKER_EINVAL; - } - - if (unicast && - action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING) { - return -ROCKER_EINVAL; - } - - if (multicast && - action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING) { - return -ROCKER_EINVAL; - } - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]) { - action->apply.copy_to_cpu = - rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]); - } - - return ROCKER_OK; -} - -static int of_dpa_cmd_add_bridging(OfDpaFlow *flow, RockerTlv **flow_tlvs) -{ - OfDpaFlowKey *key = &flow->key; - OfDpaFlowKey *mask = &flow->mask; - OfDpaFlowAction *action = &flow->action; - bool unicast = false; - bool dst_mac = false; - bool dst_mac_mask = false; - enum { - BRIDGING_MODE_UNKNOWN, - BRIDGING_MODE_VLAN_UCAST, - BRIDGING_MODE_VLAN_MCAST, - BRIDGING_MODE_VLAN_DFLT, - BRIDGING_MODE_TUNNEL_UCAST, - BRIDGING_MODE_TUNNEL_MCAST, - BRIDGING_MODE_TUNNEL_DFLT, - } mode = BRIDGING_MODE_UNKNOWN; - - key->tbl_id = ROCKER_OF_DPA_TABLE_ID_BRIDGING; - - if (flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) { - key->eth.vlan_id = - rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]); - mask->eth.vlan_id = 0xffff; - key->width = FLOW_KEY_WIDTH(eth.vlan_id); - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_TUNNEL_ID]) { - key->tunnel_id = - rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_TUNNEL_ID]); - mask->tunnel_id = 0xffffffff; - key->width = FLOW_KEY_WIDTH(tunnel_id); - } - - /* can't do VLAN bridging and tunnel bridging at same time */ - if (key->eth.vlan_id && key->tunnel_id) { - DPRINTF("can't do VLAN bridging and tunnel bridging at same time\n"); - return -ROCKER_EINVAL; - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]) { - memcpy(key->eth.dst.a, - rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]), - sizeof(key->eth.dst.a)); - key->width = FLOW_KEY_WIDTH(eth.dst); - dst_mac = true; - unicast = (key->eth.dst.a[0] & 0x01) == 0x00; - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK]) { - memcpy(mask->eth.dst.a, - rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK]), - sizeof(mask->eth.dst.a)); - key->width = FLOW_KEY_WIDTH(eth.dst); - dst_mac_mask = true; - } else if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]) { - memcpy(mask->eth.dst.a, ff_mac.a, sizeof(mask->eth.dst.a)); - } - - if (key->eth.vlan_id) { - if (dst_mac && !dst_mac_mask) { - mode = unicast ? BRIDGING_MODE_VLAN_UCAST : - BRIDGING_MODE_VLAN_MCAST; - } else if ((dst_mac && dst_mac_mask) || !dst_mac) { - mode = BRIDGING_MODE_VLAN_DFLT; - } - } else if (key->tunnel_id) { - if (dst_mac && !dst_mac_mask) { - mode = unicast ? BRIDGING_MODE_TUNNEL_UCAST : - BRIDGING_MODE_TUNNEL_MCAST; - } else if ((dst_mac && dst_mac_mask) || !dst_mac) { - mode = BRIDGING_MODE_TUNNEL_DFLT; - } - } - - if (mode == BRIDGING_MODE_UNKNOWN) { - DPRINTF("Unknown bridging mode\n"); - return -ROCKER_EINVAL; - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) { - action->goto_tbl = - rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]); - if (action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_ACL_POLICY) { - DPRINTF("Briding goto tbl must be ACL policy\n"); - return -ROCKER_EINVAL; - } - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]) { - action->write.group_id = - rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]); - switch (mode) { - case BRIDGING_MODE_VLAN_UCAST: - if (ROCKER_GROUP_TYPE_GET(action->write.group_id) != - ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE) { - DPRINTF("Bridging mode vlan ucast needs L2 " - "interface group (0x%08x)\n", - action->write.group_id); - return -ROCKER_EINVAL; - } - break; - case BRIDGING_MODE_VLAN_MCAST: - if (ROCKER_GROUP_TYPE_GET(action->write.group_id) != - ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST) { - DPRINTF("Bridging mode vlan mcast needs L2 " - "mcast group (0x%08x)\n", - action->write.group_id); - return -ROCKER_EINVAL; - } - break; - case BRIDGING_MODE_VLAN_DFLT: - if (ROCKER_GROUP_TYPE_GET(action->write.group_id) != - ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD) { - DPRINTF("Bridging mode vlan dflt needs L2 " - "flood group (0x%08x)\n", - action->write.group_id); - return -ROCKER_EINVAL; - } - break; - case BRIDGING_MODE_TUNNEL_MCAST: - if (ROCKER_GROUP_TYPE_GET(action->write.group_id) != - ROCKER_OF_DPA_GROUP_TYPE_L2_OVERLAY) { - DPRINTF("Bridging mode tunnel mcast needs L2 " - "overlay group (0x%08x)\n", - action->write.group_id); - return -ROCKER_EINVAL; - } - break; - case BRIDGING_MODE_TUNNEL_DFLT: - if (ROCKER_GROUP_TYPE_GET(action->write.group_id) != - ROCKER_OF_DPA_GROUP_TYPE_L2_OVERLAY) { - DPRINTF("Bridging mode tunnel dflt needs L2 " - "overlay group (0x%08x)\n", - action->write.group_id); - return -ROCKER_EINVAL; - } - break; - default: - return -ROCKER_EINVAL; - } - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_TUNNEL_LPORT]) { - action->write.tun_log_lport = - rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_TUNNEL_LPORT]); - if (mode != BRIDGING_MODE_TUNNEL_UCAST) { - DPRINTF("Have tunnel logical port but not " - "in bridging tunnel mode\n"); - return -ROCKER_EINVAL; - } - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]) { - action->apply.copy_to_cpu = - rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]); - } - - return ROCKER_OK; -} - -static int of_dpa_cmd_add_unicast_routing(OfDpaFlow *flow, - RockerTlv **flow_tlvs) -{ - OfDpaFlowKey *key = &flow->key; - OfDpaFlowKey *mask = &flow->mask; - OfDpaFlowAction *action = &flow->action; - enum { - UNICAST_ROUTING_MODE_UNKNOWN, - UNICAST_ROUTING_MODE_IPV4, - UNICAST_ROUTING_MODE_IPV6, - } mode = UNICAST_ROUTING_MODE_UNKNOWN; - uint8_t type; - - if (!flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]) { - return -ROCKER_EINVAL; - } - - key->tbl_id = ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING; - key->width = FLOW_KEY_WIDTH(ipv6.addr.dst); - - key->eth.type = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]); - switch (ntohs(key->eth.type)) { - case 0x0800: - mode = UNICAST_ROUTING_MODE_IPV4; - break; - case 0x86dd: - mode = UNICAST_ROUTING_MODE_IPV6; - break; - default: - return -ROCKER_EINVAL; - } - mask->eth.type = htons(0xffff); - - switch (mode) { - case UNICAST_ROUTING_MODE_IPV4: - if (!flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP]) { - return -ROCKER_EINVAL; - } - key->ipv4.addr.dst = - rocker_tlv_get_u32(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP]); - if (ipv4_addr_is_multicast(key->ipv4.addr.dst)) { - return -ROCKER_EINVAL; - } - flow->lpm = of_dpa_mask2prefix(htonl(0xffffffff)); - if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP_MASK]) { - mask->ipv4.addr.dst = - rocker_tlv_get_u32(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP_MASK]); - flow->lpm = of_dpa_mask2prefix(mask->ipv4.addr.dst); - } - break; - case UNICAST_ROUTING_MODE_IPV6: - if (!flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6]) { - return -ROCKER_EINVAL; - } - memcpy(&key->ipv6.addr.dst, - rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6]), - sizeof(key->ipv6.addr.dst)); - if (ipv6_addr_is_multicast(&key->ipv6.addr.dst)) { - return -ROCKER_EINVAL; - } - if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6_MASK]) { - memcpy(&mask->ipv6.addr.dst, - rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6_MASK]), - sizeof(mask->ipv6.addr.dst)); - } - break; - default: - return -ROCKER_EINVAL; - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) { - action->goto_tbl = - rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]); - if (action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_ACL_POLICY) { - return -ROCKER_EINVAL; - } - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]) { - action->write.group_id = - rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]); - type = ROCKER_GROUP_TYPE_GET(action->write.group_id); - if (type != ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE && - type != ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST && - type != ROCKER_OF_DPA_GROUP_TYPE_L3_ECMP) { - return -ROCKER_EINVAL; - } - } - - return ROCKER_OK; -} - -static int of_dpa_cmd_add_multicast_routing(OfDpaFlow *flow, - RockerTlv **flow_tlvs) -{ - OfDpaFlowKey *key = &flow->key; - OfDpaFlowKey *mask = &flow->mask; - OfDpaFlowAction *action = &flow->action; - enum { - MULTICAST_ROUTING_MODE_UNKNOWN, - MULTICAST_ROUTING_MODE_IPV4, - MULTICAST_ROUTING_MODE_IPV6, - } mode = MULTICAST_ROUTING_MODE_UNKNOWN; - - if (!flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE] || - !flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) { - return -ROCKER_EINVAL; - } - - key->tbl_id = ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING; - key->width = FLOW_KEY_WIDTH(ipv6.addr.dst); - - key->eth.type = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]); - switch (ntohs(key->eth.type)) { - case 0x0800: - mode = MULTICAST_ROUTING_MODE_IPV4; - break; - case 0x86dd: - mode = MULTICAST_ROUTING_MODE_IPV6; - break; - default: - return -ROCKER_EINVAL; - } - - key->eth.vlan_id = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]); - - switch (mode) { - case MULTICAST_ROUTING_MODE_IPV4: - - if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IP]) { - key->ipv4.addr.src = - rocker_tlv_get_u32(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IP]); - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IP_MASK]) { - mask->ipv4.addr.src = - rocker_tlv_get_u32(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IP_MASK]); - } - - if (!flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IP]) { - if (mask->ipv4.addr.src != 0) { - return -ROCKER_EINVAL; - } - } - - if (!flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP]) { - return -ROCKER_EINVAL; - } - - key->ipv4.addr.dst = - rocker_tlv_get_u32(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP]); - if (!ipv4_addr_is_multicast(key->ipv4.addr.dst)) { - return -ROCKER_EINVAL; - } - - break; - - case MULTICAST_ROUTING_MODE_IPV6: - - if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IPV6]) { - memcpy(&key->ipv6.addr.src, - rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IPV6]), - sizeof(key->ipv6.addr.src)); - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IPV6_MASK]) { - memcpy(&mask->ipv6.addr.src, - rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IPV6_MASK]), - sizeof(mask->ipv6.addr.src)); - } - - if (!flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IPV6]) { - if (mask->ipv6.addr.src.addr32[0] != 0 && - mask->ipv6.addr.src.addr32[1] != 0 && - mask->ipv6.addr.src.addr32[2] != 0 && - mask->ipv6.addr.src.addr32[3] != 0) { - return -ROCKER_EINVAL; - } - } - - if (!flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6]) { - return -ROCKER_EINVAL; - } - - memcpy(&key->ipv6.addr.dst, - rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6]), - sizeof(key->ipv6.addr.dst)); - if (!ipv6_addr_is_multicast(&key->ipv6.addr.dst)) { - return -ROCKER_EINVAL; - } - - break; - - default: - return -ROCKER_EINVAL; - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) { - action->goto_tbl = - rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]); - if (action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_ACL_POLICY) { - return -ROCKER_EINVAL; - } - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]) { - action->write.group_id = - rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]); - if (ROCKER_GROUP_TYPE_GET(action->write.group_id) != - ROCKER_OF_DPA_GROUP_TYPE_L3_MCAST) { - return -ROCKER_EINVAL; - } - action->write.vlan_id = key->eth.vlan_id; - } - - return ROCKER_OK; -} - -static int of_dpa_cmd_add_acl_ip(OfDpaFlowKey *key, OfDpaFlowKey *mask, - RockerTlv **flow_tlvs) -{ - key->width = FLOW_KEY_WIDTH(ip.tos); - - key->ip.proto = 0; - key->ip.tos = 0; - mask->ip.proto = 0; - mask->ip.tos = 0; - - if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_PROTO]) { - key->ip.proto = - rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_PROTO]); - } - if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_PROTO_MASK]) { - mask->ip.proto = - rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_PROTO_MASK]); - } - if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_DSCP]) { - key->ip.tos = - rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_DSCP]); - } - if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_DSCP_MASK]) { - mask->ip.tos = - rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_DSCP_MASK]); - } - if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_ECN]) { - key->ip.tos |= - rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_ECN]) << 6; - } - if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_ECN_MASK]) { - mask->ip.tos |= - rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_ECN_MASK]) << 6; - } - - return ROCKER_OK; -} - -static int of_dpa_cmd_add_acl(OfDpaFlow *flow, RockerTlv **flow_tlvs) -{ - OfDpaFlowKey *key = &flow->key; - OfDpaFlowKey *mask = &flow->mask; - OfDpaFlowAction *action = &flow->action; - enum { - ACL_MODE_UNKNOWN, - ACL_MODE_IPV4_VLAN, - ACL_MODE_IPV6_VLAN, - ACL_MODE_IPV4_TENANT, - ACL_MODE_IPV6_TENANT, - ACL_MODE_NON_IP_VLAN, - ACL_MODE_NON_IP_TENANT, - ACL_MODE_ANY_VLAN, - ACL_MODE_ANY_TENANT, - } mode = ACL_MODE_UNKNOWN; - int err = ROCKER_OK; - - if (!flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT] || - !flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]) { - return -ROCKER_EINVAL; - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID] && - flow_tlvs[ROCKER_TLV_OF_DPA_TUNNEL_ID]) { - return -ROCKER_EINVAL; - } - - key->tbl_id = ROCKER_OF_DPA_TABLE_ID_ACL_POLICY; - key->width = FLOW_KEY_WIDTH(eth.type); - - key->in_pport = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT]); - if (flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK]) { - mask->in_pport = - rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK]); - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]) { - memcpy(key->eth.src.a, - rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]), - sizeof(key->eth.src.a)); - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC_MASK]) { - memcpy(mask->eth.src.a, - rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC_MASK]), - sizeof(mask->eth.src.a)); - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]) { - memcpy(key->eth.dst.a, - rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]), - sizeof(key->eth.dst.a)); - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK]) { - memcpy(mask->eth.dst.a, - rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK]), - sizeof(mask->eth.dst.a)); - } - - key->eth.type = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]); - if (key->eth.type) { - mask->eth.type = 0xffff; - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) { - key->eth.vlan_id = - rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]); - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]) { - mask->eth.vlan_id = - rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]); - } - - switch (ntohs(key->eth.type)) { - case 0x0000: - mode = (key->eth.vlan_id) ? ACL_MODE_ANY_VLAN : ACL_MODE_ANY_TENANT; - break; - case 0x0800: - mode = (key->eth.vlan_id) ? ACL_MODE_IPV4_VLAN : ACL_MODE_IPV4_TENANT; - break; - case 0x86dd: - mode = (key->eth.vlan_id) ? ACL_MODE_IPV6_VLAN : ACL_MODE_IPV6_TENANT; - break; - default: - mode = (key->eth.vlan_id) ? ACL_MODE_NON_IP_VLAN : - ACL_MODE_NON_IP_TENANT; - break; - } - - /* XXX only supporting VLAN modes for now */ - if (mode != ACL_MODE_IPV4_VLAN && - mode != ACL_MODE_IPV6_VLAN && - mode != ACL_MODE_NON_IP_VLAN && - mode != ACL_MODE_ANY_VLAN) { - return -ROCKER_EINVAL; - } - - switch (ntohs(key->eth.type)) { - case 0x0800: - case 0x86dd: - err = of_dpa_cmd_add_acl_ip(key, mask, flow_tlvs); - break; - } - - if (err) { - return err; - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]) { - action->write.group_id = - rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]); - } - - if (flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]) { - action->apply.copy_to_cpu = - rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]); - } - - return ROCKER_OK; -} - -static int of_dpa_cmd_flow_add_mod(OfDpa *of_dpa, OfDpaFlow *flow, - RockerTlv **flow_tlvs) -{ - enum rocker_of_dpa_table_id tbl; - int err = ROCKER_OK; - - if (!flow_tlvs[ROCKER_TLV_OF_DPA_TABLE_ID] || - !flow_tlvs[ROCKER_TLV_OF_DPA_PRIORITY] || - !flow_tlvs[ROCKER_TLV_OF_DPA_HARDTIME]) { - return -ROCKER_EINVAL; - } - - tbl = rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_TABLE_ID]); - flow->priority = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_PRIORITY]); - flow->hardtime = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_HARDTIME]); - - if (flow_tlvs[ROCKER_TLV_OF_DPA_IDLETIME]) { - if (tbl == ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT || - tbl == ROCKER_OF_DPA_TABLE_ID_VLAN || - tbl == ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC) { - return -ROCKER_EINVAL; - } - flow->idletime = - rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IDLETIME]); - } - - switch (tbl) { - case ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT: - err = of_dpa_cmd_add_ig_port(flow, flow_tlvs); - break; - case ROCKER_OF_DPA_TABLE_ID_VLAN: - err = of_dpa_cmd_add_vlan(flow, flow_tlvs); - break; - case ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC: - err = of_dpa_cmd_add_term_mac(flow, flow_tlvs); - break; - case ROCKER_OF_DPA_TABLE_ID_BRIDGING: - err = of_dpa_cmd_add_bridging(flow, flow_tlvs); - break; - case ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING: - err = of_dpa_cmd_add_unicast_routing(flow, flow_tlvs); - break; - case ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING: - err = of_dpa_cmd_add_multicast_routing(flow, flow_tlvs); - break; - case ROCKER_OF_DPA_TABLE_ID_ACL_POLICY: - err = of_dpa_cmd_add_acl(flow, flow_tlvs); - break; - } - - return err; -} - -static int of_dpa_cmd_flow_add(OfDpa *of_dpa, uint64_t cookie, - RockerTlv **flow_tlvs) -{ - OfDpaFlow *flow = of_dpa_flow_find(of_dpa, cookie); - int err = ROCKER_OK; - - if (flow) { - return -ROCKER_EEXIST; - } - - flow = of_dpa_flow_alloc(cookie); - if (!flow) { - return -ROCKER_ENOMEM; - } - - err = of_dpa_cmd_flow_add_mod(of_dpa, flow, flow_tlvs); - if (err) { - g_free(flow); - return err; - } - - return of_dpa_flow_add(of_dpa, flow); -} - -static int of_dpa_cmd_flow_mod(OfDpa *of_dpa, uint64_t cookie, - RockerTlv **flow_tlvs) -{ - OfDpaFlow *flow = of_dpa_flow_find(of_dpa, cookie); - - if (!flow) { - return -ROCKER_ENOENT; - } - - return of_dpa_cmd_flow_add_mod(of_dpa, flow, flow_tlvs); -} - -static int of_dpa_cmd_flow_del(OfDpa *of_dpa, uint64_t cookie) -{ - OfDpaFlow *flow = of_dpa_flow_find(of_dpa, cookie); - - if (!flow) { - return -ROCKER_ENOENT; - } - - of_dpa_flow_del(of_dpa, flow); - - return ROCKER_OK; -} - -static int of_dpa_cmd_flow_get_stats(OfDpa *of_dpa, uint64_t cookie, - struct desc_info *info, char *buf) -{ - OfDpaFlow *flow = of_dpa_flow_find(of_dpa, cookie); - size_t tlv_size; - int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) / 1000; - int pos; - - if (!flow) { - return -ROCKER_ENOENT; - } - - tlv_size = rocker_tlv_total_size(sizeof(uint32_t)) + /* duration */ - rocker_tlv_total_size(sizeof(uint64_t)) + /* rx_pkts */ - rocker_tlv_total_size(sizeof(uint64_t)); /* tx_ptks */ - - if (tlv_size > desc_buf_size(info)) { - return -ROCKER_EMSGSIZE; - } - - pos = 0; - rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_OF_DPA_FLOW_STAT_DURATION, - (int32_t)(now - flow->stats.install_time)); - rocker_tlv_put_le64(buf, &pos, ROCKER_TLV_OF_DPA_FLOW_STAT_RX_PKTS, - flow->stats.rx_pkts); - rocker_tlv_put_le64(buf, &pos, ROCKER_TLV_OF_DPA_FLOW_STAT_TX_PKTS, - flow->stats.tx_pkts); - - return desc_set_buf(info, tlv_size); -} - -static int of_dpa_flow_cmd(OfDpa *of_dpa, struct desc_info *info, - char *buf, uint16_t cmd, - RockerTlv **flow_tlvs) -{ - uint64_t cookie; - - if (!flow_tlvs[ROCKER_TLV_OF_DPA_COOKIE]) { - return -ROCKER_EINVAL; - } - - cookie = rocker_tlv_get_le64(flow_tlvs[ROCKER_TLV_OF_DPA_COOKIE]); - - switch (cmd) { - case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD: - return of_dpa_cmd_flow_add(of_dpa, cookie, flow_tlvs); - case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD: - return of_dpa_cmd_flow_mod(of_dpa, cookie, flow_tlvs); - case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL: - return of_dpa_cmd_flow_del(of_dpa, cookie); - case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_GET_STATS: - return of_dpa_cmd_flow_get_stats(of_dpa, cookie, info, buf); - } - - return -ROCKER_ENOTSUP; -} - -static int of_dpa_cmd_add_l2_interface(OfDpaGroup *group, - RockerTlv **group_tlvs) -{ - if (!group_tlvs[ROCKER_TLV_OF_DPA_OUT_PPORT] || - !group_tlvs[ROCKER_TLV_OF_DPA_POP_VLAN]) { - return -ROCKER_EINVAL; - } - - group->l2_interface.out_pport = - rocker_tlv_get_le32(group_tlvs[ROCKER_TLV_OF_DPA_OUT_PPORT]); - group->l2_interface.pop_vlan = - rocker_tlv_get_u8(group_tlvs[ROCKER_TLV_OF_DPA_POP_VLAN]); - - return ROCKER_OK; -} - -static int of_dpa_cmd_add_l2_rewrite(OfDpa *of_dpa, OfDpaGroup *group, - RockerTlv **group_tlvs) -{ - OfDpaGroup *l2_interface_group; - - if (!group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID_LOWER]) { - return -ROCKER_EINVAL; - } - - group->l2_rewrite.group_id = - rocker_tlv_get_le32(group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID_LOWER]); - - l2_interface_group = of_dpa_group_find(of_dpa, group->l2_rewrite.group_id); - if (!l2_interface_group || - ROCKER_GROUP_TYPE_GET(l2_interface_group->id) != - ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE) { - DPRINTF("l2 rewrite group needs a valid l2 interface group\n"); - return -ROCKER_EINVAL; - } - - if (group_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]) { - memcpy(group->l2_rewrite.src_mac.a, - rocker_tlv_data(group_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]), - sizeof(group->l2_rewrite.src_mac.a)); - } - - if (group_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]) { - memcpy(group->l2_rewrite.dst_mac.a, - rocker_tlv_data(group_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]), - sizeof(group->l2_rewrite.dst_mac.a)); - } - - if (group_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) { - group->l2_rewrite.vlan_id = - rocker_tlv_get_u16(group_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]); - if (ROCKER_GROUP_VLAN_GET(l2_interface_group->id) != - (ntohs(group->l2_rewrite.vlan_id) & VLAN_VID_MASK)) { - DPRINTF("Set VLAN ID must be same as L2 interface group\n"); - return -ROCKER_EINVAL; - } - } - - return ROCKER_OK; -} - -static int of_dpa_cmd_add_l2_flood(OfDpa *of_dpa, OfDpaGroup *group, - RockerTlv **group_tlvs) -{ - OfDpaGroup *l2_group; - RockerTlv **tlvs; - int err; - int i; - - if (!group_tlvs[ROCKER_TLV_OF_DPA_GROUP_COUNT] || - !group_tlvs[ROCKER_TLV_OF_DPA_GROUP_IDS]) { - return -ROCKER_EINVAL; - } - - group->l2_flood.group_count = - rocker_tlv_get_le16(group_tlvs[ROCKER_TLV_OF_DPA_GROUP_COUNT]); - - 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_new0(uint32_t, group->l2_flood.group_count); - if (!group->l2_flood.group_ids) { - err = -ROCKER_ENOMEM; - goto err_out; - } - - rocker_tlv_parse_nested(tlvs, group->l2_flood.group_count, - group_tlvs[ROCKER_TLV_OF_DPA_GROUP_IDS]); - - for (i = 0; i < group->l2_flood.group_count; i++) { - group->l2_flood.group_ids[i] = rocker_tlv_get_le32(tlvs[i + 1]); - } - - /* All of the L2 interface groups referenced by the L2 flood - * must have same VLAN - */ - - for (i = 0; i < group->l2_flood.group_count; i++) { - l2_group = of_dpa_group_find(of_dpa, group->l2_flood.group_ids[i]); - if (!l2_group) { - continue; - } - if ((ROCKER_GROUP_TYPE_GET(l2_group->id) == - ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE) && - (ROCKER_GROUP_VLAN_GET(l2_group->id) != - ROCKER_GROUP_VLAN_GET(group->id))) { - DPRINTF("l2 interface group 0x%08x VLAN doesn't match l2 " - "flood group 0x%08x\n", - group->l2_flood.group_ids[i], group->id); - err = -ROCKER_EINVAL; - goto err_out; - } - } - - g_free(tlvs); - return ROCKER_OK; - -err_out: - group->l2_flood.group_count = 0; - g_free(group->l2_flood.group_ids); - g_free(tlvs); - - return err; -} - -static int of_dpa_cmd_add_l3_unicast(OfDpaGroup *group, RockerTlv **group_tlvs) -{ - if (!group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID_LOWER]) { - return -ROCKER_EINVAL; - } - - group->l3_unicast.group_id = - rocker_tlv_get_le32(group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID_LOWER]); - - if (group_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]) { - memcpy(group->l3_unicast.src_mac.a, - rocker_tlv_data(group_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]), - sizeof(group->l3_unicast.src_mac.a)); - } - - if (group_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]) { - memcpy(group->l3_unicast.dst_mac.a, - rocker_tlv_data(group_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]), - sizeof(group->l3_unicast.dst_mac.a)); - } - - if (group_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) { - group->l3_unicast.vlan_id = - rocker_tlv_get_u16(group_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]); - } - - if (group_tlvs[ROCKER_TLV_OF_DPA_TTL_CHECK]) { - group->l3_unicast.ttl_check = - rocker_tlv_get_u8(group_tlvs[ROCKER_TLV_OF_DPA_TTL_CHECK]); - } - - return ROCKER_OK; -} - -static int of_dpa_cmd_group_do(OfDpa *of_dpa, uint32_t group_id, - OfDpaGroup *group, RockerTlv **group_tlvs) -{ - uint8_t type = ROCKER_GROUP_TYPE_GET(group_id); - - switch (type) { - case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE: - return of_dpa_cmd_add_l2_interface(group, group_tlvs); - case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE: - return of_dpa_cmd_add_l2_rewrite(of_dpa, group, group_tlvs); - case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD: - /* Treat L2 multicast group same as a L2 flood group */ - case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST: - return of_dpa_cmd_add_l2_flood(of_dpa, group, group_tlvs); - case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST: - return of_dpa_cmd_add_l3_unicast(group, group_tlvs); - } - - return -ROCKER_ENOTSUP; -} - -static int of_dpa_cmd_group_add(OfDpa *of_dpa, uint32_t group_id, - RockerTlv **group_tlvs) -{ - OfDpaGroup *group = of_dpa_group_find(of_dpa, group_id); - int err; - - if (group) { - return -ROCKER_EEXIST; - } - - group = of_dpa_group_alloc(group_id); - if (!group) { - return -ROCKER_ENOMEM; - } - - err = of_dpa_cmd_group_do(of_dpa, group_id, group, group_tlvs); - if (err) { - goto err_cmd_add; - } - - err = of_dpa_group_add(of_dpa, group); - if (err) { - goto err_cmd_add; - } - - return ROCKER_OK; - -err_cmd_add: - g_free(group); - return err; -} - -static int of_dpa_cmd_group_mod(OfDpa *of_dpa, uint32_t group_id, - RockerTlv **group_tlvs) -{ - OfDpaGroup *group = of_dpa_group_find(of_dpa, group_id); - - if (!group) { - return -ROCKER_ENOENT; - } - - return of_dpa_cmd_group_do(of_dpa, group_id, group, group_tlvs); -} - -static int of_dpa_cmd_group_del(OfDpa *of_dpa, uint32_t group_id) -{ - OfDpaGroup *group = of_dpa_group_find(of_dpa, group_id); - - if (!group) { - return -ROCKER_ENOENT; - } - - return of_dpa_group_del(of_dpa, group); -} - -static int of_dpa_cmd_group_get_stats(OfDpa *of_dpa, uint32_t group_id, - struct desc_info *info, char *buf) -{ - return -ROCKER_ENOTSUP; -} - -static int of_dpa_group_cmd(OfDpa *of_dpa, struct desc_info *info, - char *buf, uint16_t cmd, RockerTlv **group_tlvs) -{ - uint32_t group_id; - - if (!group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]) { - return -ROCKER_EINVAL; - } - - group_id = rocker_tlv_get_le32(group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]); - - switch (cmd) { - case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD: - return of_dpa_cmd_group_add(of_dpa, group_id, group_tlvs); - case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD: - return of_dpa_cmd_group_mod(of_dpa, group_id, group_tlvs); - case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL: - return of_dpa_cmd_group_del(of_dpa, group_id); - case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_GET_STATS: - return of_dpa_cmd_group_get_stats(of_dpa, group_id, info, buf); - } - - return -ROCKER_ENOTSUP; -} - -static int of_dpa_cmd(World *world, struct desc_info *info, - char *buf, uint16_t cmd, RockerTlv *cmd_info_tlv) -{ - OfDpa *of_dpa = world_private(world); - RockerTlv *tlvs[ROCKER_TLV_OF_DPA_MAX + 1]; - - rocker_tlv_parse_nested(tlvs, ROCKER_TLV_OF_DPA_MAX, cmd_info_tlv); - - switch (cmd) { - case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD: - case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD: - case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL: - case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_GET_STATS: - return of_dpa_flow_cmd(of_dpa, info, buf, cmd, tlvs); - case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD: - case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD: - case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL: - case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_GET_STATS: - return of_dpa_group_cmd(of_dpa, info, buf, cmd, tlvs); - } - - return -ROCKER_ENOTSUP; -} - -static gboolean rocker_int64_equal(gconstpointer v1, gconstpointer v2) -{ - return *((const uint64_t *)v1) == *((const uint64_t *)v2); -} - -static guint rocker_int64_hash(gconstpointer v) -{ - return (guint)*(const uint64_t *)v; -} - -static int of_dpa_init(World *world) -{ - OfDpa *of_dpa = world_private(world); - - of_dpa->world = world; - - of_dpa->flow_tbl = g_hash_table_new_full(rocker_int64_hash, - rocker_int64_equal, - NULL, g_free); - if (!of_dpa->flow_tbl) { - return -ENOMEM; - } - - of_dpa->group_tbl = g_hash_table_new_full(g_int_hash, g_int_equal, - NULL, g_free); - if (!of_dpa->group_tbl) { - goto err_group_tbl; - } - - /* XXX hardcode some artificial table max values */ - of_dpa->flow_tbl_max_size = 100; - of_dpa->group_tbl_max_size = 100; - - return 0; - -err_group_tbl: - g_hash_table_destroy(of_dpa->flow_tbl); - return -ENOMEM; -} - -static void of_dpa_uninit(World *world) -{ - OfDpa *of_dpa = world_private(world); - - g_hash_table_destroy(of_dpa->group_tbl); - g_hash_table_destroy(of_dpa->flow_tbl); -} - -struct of_dpa_flow_fill_context { - RockerOfDpaFlowList *list; - uint32_t tbl_id; -}; - -static void of_dpa_flow_fill(void *cookie, void *value, void *user_data) -{ - struct of_dpa_flow *flow = value; - struct of_dpa_flow_key *key = &flow->key; - struct of_dpa_flow_key *mask = &flow->mask; - struct of_dpa_flow_fill_context *flow_context = user_data; - RockerOfDpaFlowList *new; - RockerOfDpaFlow *nflow; - RockerOfDpaFlowKey *nkey; - RockerOfDpaFlowMask *nmask; - RockerOfDpaFlowAction *naction; - - if (flow_context->tbl_id != -1 && - flow_context->tbl_id != key->tbl_id) { - return; - } - - new = g_malloc0(sizeof(*new)); - nflow = new->value = g_malloc0(sizeof(*nflow)); - nkey = nflow->key = g_malloc0(sizeof(*nkey)); - nmask = nflow->mask = g_malloc0(sizeof(*nmask)); - naction = nflow->action = g_malloc0(sizeof(*naction)); - - nflow->cookie = flow->cookie; - nflow->hits = flow->stats.hits; - nkey->priority = flow->priority; - nkey->tbl_id = key->tbl_id; - - if (key->in_pport || mask->in_pport) { - nkey->has_in_pport = true; - nkey->in_pport = key->in_pport; - } - - if (nkey->has_in_pport && mask->in_pport != 0xffffffff) { - nmask->has_in_pport = true; - nmask->in_pport = mask->in_pport; - } - - if (key->eth.vlan_id || mask->eth.vlan_id) { - nkey->has_vlan_id = true; - nkey->vlan_id = ntohs(key->eth.vlan_id); - } - - if (nkey->has_vlan_id && mask->eth.vlan_id != 0xffff) { - nmask->has_vlan_id = true; - nmask->vlan_id = ntohs(mask->eth.vlan_id); - } - - if (key->tunnel_id || mask->tunnel_id) { - nkey->has_tunnel_id = true; - nkey->tunnel_id = key->tunnel_id; - } - - if (nkey->has_tunnel_id && mask->tunnel_id != 0xffffffff) { - nmask->has_tunnel_id = true; - nmask->tunnel_id = mask->tunnel_id; - } - - if (memcmp(key->eth.src.a, zero_mac.a, ETH_ALEN) || - memcmp(mask->eth.src.a, zero_mac.a, ETH_ALEN)) { - nkey->has_eth_src = true; - nkey->eth_src = qemu_mac_strdup_printf(key->eth.src.a); - } - - if (nkey->has_eth_src && memcmp(mask->eth.src.a, ff_mac.a, ETH_ALEN)) { - nmask->has_eth_src = true; - nmask->eth_src = qemu_mac_strdup_printf(mask->eth.src.a); - } - - if (memcmp(key->eth.dst.a, zero_mac.a, ETH_ALEN) || - memcmp(mask->eth.dst.a, zero_mac.a, ETH_ALEN)) { - nkey->has_eth_dst = true; - nkey->eth_dst = qemu_mac_strdup_printf(key->eth.dst.a); - } - - if (nkey->has_eth_dst && memcmp(mask->eth.dst.a, ff_mac.a, ETH_ALEN)) { - nmask->has_eth_dst = true; - nmask->eth_dst = qemu_mac_strdup_printf(mask->eth.dst.a); - } - - if (key->eth.type) { - - nkey->has_eth_type = true; - nkey->eth_type = ntohs(key->eth.type); - - switch (ntohs(key->eth.type)) { - case 0x0800: - case 0x86dd: - if (key->ip.proto || mask->ip.proto) { - nkey->has_ip_proto = true; - nkey->ip_proto = key->ip.proto; - } - if (nkey->has_ip_proto && mask->ip.proto != 0xff) { - nmask->has_ip_proto = true; - nmask->ip_proto = mask->ip.proto; - } - if (key->ip.tos || mask->ip.tos) { - nkey->has_ip_tos = true; - nkey->ip_tos = key->ip.tos; - } - if (nkey->has_ip_tos && mask->ip.tos != 0xff) { - nmask->has_ip_tos = true; - nmask->ip_tos = mask->ip.tos; - } - break; - } - - switch (ntohs(key->eth.type)) { - case 0x0800: - if (key->ipv4.addr.dst || mask->ipv4.addr.dst) { - char *dst = inet_ntoa(*(struct in_addr *)&key->ipv4.addr.dst); - int dst_len = of_dpa_mask2prefix(mask->ipv4.addr.dst); - nkey->has_ip_dst = true; - nkey->ip_dst = g_strdup_printf("%s/%d", dst, dst_len); - } - break; - } - } - - if (flow->action.goto_tbl) { - naction->has_goto_tbl = true; - naction->goto_tbl = flow->action.goto_tbl; - } - - if (flow->action.write.group_id) { - naction->has_group_id = true; - naction->group_id = flow->action.write.group_id; - } - - if (flow->action.apply.new_vlan_id) { - naction->has_new_vlan_id = true; - naction->new_vlan_id = flow->action.apply.new_vlan_id; - } - - new->next = flow_context->list; - flow_context->list = new; -} - -RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name, - bool has_tbl_id, - uint32_t tbl_id, - Error **errp) -{ - struct rocker *r; - struct world *w; - struct of_dpa *of_dpa; - struct of_dpa_flow_fill_context fill_context = { - .list = NULL, - .tbl_id = tbl_id, - }; - - r = rocker_find(name); - if (!r) { - error_setg(errp, "rocker %s not found", name); - return NULL; - } - - w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA); - if (!w) { - error_setg(errp, "rocker %s doesn't have OF-DPA world", name); - return NULL; - } - - of_dpa = world_private(w); - - g_hash_table_foreach(of_dpa->flow_tbl, of_dpa_flow_fill, &fill_context); - - return fill_context.list; -} - -struct of_dpa_group_fill_context { - RockerOfDpaGroupList *list; - uint8_t type; -}; - -static void of_dpa_group_fill(void *key, void *value, void *user_data) -{ - struct of_dpa_group *group = value; - struct of_dpa_group_fill_context *flow_context = user_data; - RockerOfDpaGroupList *new; - RockerOfDpaGroup *ngroup; - struct uint32List *id; - int i; - - if (flow_context->type != 9 && - flow_context->type != ROCKER_GROUP_TYPE_GET(group->id)) { - return; - } - - new = g_malloc0(sizeof(*new)); - ngroup = new->value = g_malloc0(sizeof(*ngroup)); - - ngroup->id = group->id; - - ngroup->type = ROCKER_GROUP_TYPE_GET(group->id); - - switch (ngroup->type) { - case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE: - ngroup->has_vlan_id = true; - ngroup->vlan_id = ROCKER_GROUP_VLAN_GET(group->id); - ngroup->has_pport = true; - ngroup->pport = ROCKER_GROUP_PORT_GET(group->id); - ngroup->has_out_pport = true; - ngroup->out_pport = group->l2_interface.out_pport; - ngroup->has_pop_vlan = true; - ngroup->pop_vlan = group->l2_interface.pop_vlan; - break; - case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE: - ngroup->has_index = true; - ngroup->index = ROCKER_GROUP_INDEX_LONG_GET(group->id); - ngroup->has_group_id = true; - ngroup->group_id = group->l2_rewrite.group_id; - if (group->l2_rewrite.vlan_id) { - ngroup->has_set_vlan_id = true; - ngroup->set_vlan_id = ntohs(group->l2_rewrite.vlan_id); - } - if (memcmp(group->l2_rewrite.src_mac.a, zero_mac.a, ETH_ALEN)) { - ngroup->has_set_eth_src = true; - ngroup->set_eth_src = - qemu_mac_strdup_printf(group->l2_rewrite.src_mac.a); - } - if (memcmp(group->l2_rewrite.dst_mac.a, zero_mac.a, ETH_ALEN)) { - ngroup->has_set_eth_dst = true; - ngroup->set_eth_dst = - qemu_mac_strdup_printf(group->l2_rewrite.dst_mac.a); - } - break; - case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD: - case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST: - ngroup->has_vlan_id = true; - ngroup->vlan_id = ROCKER_GROUP_VLAN_GET(group->id); - ngroup->has_index = true; - ngroup->index = ROCKER_GROUP_INDEX_GET(group->id); - for (i = 0; i < group->l2_flood.group_count; i++) { - ngroup->has_group_ids = true; - id = g_malloc0(sizeof(*id)); - id->value = group->l2_flood.group_ids[i]; - id->next = ngroup->group_ids; - ngroup->group_ids = id; - } - break; - case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST: - ngroup->has_index = true; - ngroup->index = ROCKER_GROUP_INDEX_LONG_GET(group->id); - ngroup->has_group_id = true; - ngroup->group_id = group->l3_unicast.group_id; - if (group->l3_unicast.vlan_id) { - ngroup->has_set_vlan_id = true; - ngroup->set_vlan_id = ntohs(group->l3_unicast.vlan_id); - } - if (memcmp(group->l3_unicast.src_mac.a, zero_mac.a, ETH_ALEN)) { - ngroup->has_set_eth_src = true; - ngroup->set_eth_src = - qemu_mac_strdup_printf(group->l3_unicast.src_mac.a); - } - if (memcmp(group->l3_unicast.dst_mac.a, zero_mac.a, ETH_ALEN)) { - ngroup->has_set_eth_dst = true; - ngroup->set_eth_dst = - qemu_mac_strdup_printf(group->l3_unicast.dst_mac.a); - } - if (group->l3_unicast.ttl_check) { - ngroup->has_ttl_check = true; - ngroup->ttl_check = group->l3_unicast.ttl_check; - } - break; - } - - new->next = flow_context->list; - flow_context->list = new; -} - -RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name, - bool has_type, - uint8_t type, - Error **errp) -{ - struct rocker *r; - struct world *w; - struct of_dpa *of_dpa; - struct of_dpa_group_fill_context fill_context = { - .list = NULL, - .type = type, - }; - - r = rocker_find(name); - if (!r) { - error_setg(errp, "rocker %s not found", name); - return NULL; - } - - w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA); - if (!w) { - error_setg(errp, "rocker %s doesn't have OF-DPA world", name); - return NULL; - } - - of_dpa = world_private(w); - - g_hash_table_foreach(of_dpa->group_tbl, of_dpa_group_fill, &fill_context); - - return fill_context.list; -} - -static WorldOps of_dpa_ops = { - .name = "ofdpa", - .init = of_dpa_init, - .uninit = of_dpa_uninit, - .ig = of_dpa_ig, - .cmd = of_dpa_cmd, -}; - -World *of_dpa_world_alloc(Rocker *r) -{ - return world_alloc(r, sizeof(OfDpa), ROCKER_WORLD_TYPE_OF_DPA, &of_dpa_ops); -} diff --git a/qemu/hw/net/rocker/rocker_of_dpa.h b/qemu/hw/net/rocker/rocker_of_dpa.h deleted file mode 100644 index f3f6d7780..000000000 --- a/qemu/hw/net/rocker/rocker_of_dpa.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * QEMU rocker switch emulation - OF-DPA flow processing support - * - * Copyright (c) 2014 Scott Feldman - * - * 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. - */ - -#ifndef _ROCKER_OF_DPA_H_ -#define _ROCKER_OF_DPA_H_ - -World *of_dpa_world_alloc(Rocker *r); - -#endif /* _ROCKER_OF_DPA_H_ */ diff --git a/qemu/hw/net/rocker/rocker_tlv.h b/qemu/hw/net/rocker/rocker_tlv.h deleted file mode 100644 index e3c4ab679..000000000 --- a/qemu/hw/net/rocker/rocker_tlv.h +++ /dev/null @@ -1,244 +0,0 @@ -/* - * QEMU rocker switch emulation - TLV parsing and composing - * - * Copyright (c) 2014 Jiri Pirko - * - * 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. - */ - -#ifndef _ROCKER_TLV_H_ -#define _ROCKER_TLV_H_ - -#define ROCKER_TLV_ALIGNTO 8U -#define ROCKER_TLV_ALIGN(len) \ - (((len) + ROCKER_TLV_ALIGNTO - 1) & ~(ROCKER_TLV_ALIGNTO - 1)) -#define ROCKER_TLV_HDRLEN ROCKER_TLV_ALIGN(sizeof(RockerTlv)) - -/* - * <------- ROCKER_TLV_HDRLEN -------> <--- ROCKER_TLV_ALIGN(payload) ---> - * +-----------------------------+- - -+- - - - - - - - - - - - - - -+- - -+ - * | Header | Pad | Payload | Pad | - * | (RockerTlv) | ing | | ing | - * +-----------------------------+- - -+- - - - - - - - - - - - - - -+- - -+ - * <--------------------------- tlv->len --------------------------> - */ - -static inline RockerTlv *rocker_tlv_next(const RockerTlv *tlv, int *remaining) -{ - int totlen = ROCKER_TLV_ALIGN(le16_to_cpu(tlv->len)); - - *remaining -= totlen; - return (RockerTlv *) ((char *) tlv + totlen); -} - -static inline int rocker_tlv_ok(const RockerTlv *tlv, int remaining) -{ - return remaining >= (int) ROCKER_TLV_HDRLEN && - le16_to_cpu(tlv->len) >= ROCKER_TLV_HDRLEN && - le16_to_cpu(tlv->len) <= remaining; -} - -#define rocker_tlv_for_each(pos, head, len, rem) \ - for (pos = head, rem = len; \ - rocker_tlv_ok(pos, rem); \ - pos = rocker_tlv_next(pos, &(rem))) - -#define rocker_tlv_for_each_nested(pos, tlv, rem) \ - rocker_tlv_for_each(pos, rocker_tlv_data(tlv), rocker_tlv_len(tlv), rem) - -static inline int rocker_tlv_size(int payload) -{ - return ROCKER_TLV_HDRLEN + payload; -} - -static inline int rocker_tlv_total_size(int payload) -{ - return ROCKER_TLV_ALIGN(rocker_tlv_size(payload)); -} - -static inline int rocker_tlv_padlen(int payload) -{ - return rocker_tlv_total_size(payload) - rocker_tlv_size(payload); -} - -static inline int rocker_tlv_type(const RockerTlv *tlv) -{ - return le32_to_cpu(tlv->type); -} - -static inline void *rocker_tlv_data(const RockerTlv *tlv) -{ - return (char *) tlv + ROCKER_TLV_HDRLEN; -} - -static inline int rocker_tlv_len(const RockerTlv *tlv) -{ - return le16_to_cpu(tlv->len) - ROCKER_TLV_HDRLEN; -} - -static inline uint8_t rocker_tlv_get_u8(const RockerTlv *tlv) -{ - return *(uint8_t *) rocker_tlv_data(tlv); -} - -static inline uint16_t rocker_tlv_get_u16(const RockerTlv *tlv) -{ - return *(uint16_t *) rocker_tlv_data(tlv); -} - -static inline uint32_t rocker_tlv_get_u32(const RockerTlv *tlv) -{ - return *(uint32_t *) rocker_tlv_data(tlv); -} - -static inline uint64_t rocker_tlv_get_u64(const RockerTlv *tlv) -{ - return *(uint64_t *) rocker_tlv_data(tlv); -} - -static inline uint16_t rocker_tlv_get_le16(const RockerTlv *tlv) -{ - return le16_to_cpup((uint16_t *) rocker_tlv_data(tlv)); -} - -static inline uint32_t rocker_tlv_get_le32(const RockerTlv *tlv) -{ - return le32_to_cpup((uint32_t *) rocker_tlv_data(tlv)); -} - -static inline uint64_t rocker_tlv_get_le64(const RockerTlv *tlv) -{ - return le64_to_cpup((uint64_t *) rocker_tlv_data(tlv)); -} - -static inline void rocker_tlv_parse(RockerTlv **tb, int maxtype, - const char *buf, int buf_len) -{ - const RockerTlv *tlv; - const RockerTlv *head = (const RockerTlv *) buf; - int rem; - - memset(tb, 0, sizeof(RockerTlv *) * (maxtype + 1)); - - rocker_tlv_for_each(tlv, head, buf_len, rem) { - uint32_t type = rocker_tlv_type(tlv); - - if (type > 0 && type <= maxtype) { - tb[type] = (RockerTlv *) tlv; - } - } -} - -static inline void rocker_tlv_parse_nested(RockerTlv **tb, int maxtype, - const RockerTlv *tlv) -{ - rocker_tlv_parse(tb, maxtype, rocker_tlv_data(tlv), rocker_tlv_len(tlv)); -} - -static inline RockerTlv *rocker_tlv_start(char *buf, int buf_pos) -{ - return (RockerTlv *) (buf + buf_pos); -} - -static inline void rocker_tlv_put_iov(char *buf, int *buf_pos, - int type, const struct iovec *iov, - const unsigned int iovcnt) -{ - size_t len = iov_size(iov, iovcnt); - int total_size = rocker_tlv_total_size(len); - RockerTlv *tlv; - - tlv = rocker_tlv_start(buf, *buf_pos); - *buf_pos += total_size; - tlv->type = cpu_to_le32(type); - tlv->len = cpu_to_le16(rocker_tlv_size(len)); - iov_to_buf(iov, iovcnt, 0, rocker_tlv_data(tlv), len); - memset((char *) tlv + le16_to_cpu(tlv->len), 0, rocker_tlv_padlen(len)); -} - -static inline void rocker_tlv_put(char *buf, int *buf_pos, - int type, int len, void *data) -{ - struct iovec iov = { - .iov_base = data, - .iov_len = len, - }; - - rocker_tlv_put_iov(buf, buf_pos, type, &iov, 1); -} - -static inline void rocker_tlv_put_u8(char *buf, int *buf_pos, - int type, uint8_t value) -{ - rocker_tlv_put(buf, buf_pos, type, sizeof(uint8_t), &value); -} - -static inline void rocker_tlv_put_u16(char *buf, int *buf_pos, - int type, uint16_t value) -{ - rocker_tlv_put(buf, buf_pos, type, sizeof(uint16_t), &value); -} - -static inline void rocker_tlv_put_u32(char *buf, int *buf_pos, - int type, uint32_t value) -{ - rocker_tlv_put(buf, buf_pos, type, sizeof(uint32_t), &value); -} - -static inline void rocker_tlv_put_u64(char *buf, int *buf_pos, - int type, uint64_t value) -{ - rocker_tlv_put(buf, buf_pos, type, sizeof(uint64_t), &value); -} - -static inline void rocker_tlv_put_le16(char *buf, int *buf_pos, - int type, uint16_t value) -{ - value = cpu_to_le16(value); - rocker_tlv_put(buf, buf_pos, type, sizeof(uint16_t), &value); -} - -static inline void rocker_tlv_put_le32(char *buf, int *buf_pos, - int type, uint32_t value) -{ - value = cpu_to_le32(value); - rocker_tlv_put(buf, buf_pos, type, sizeof(uint32_t), &value); -} - -static inline void rocker_tlv_put_le64(char *buf, int *buf_pos, - int type, uint64_t value) -{ - value = cpu_to_le64(value); - rocker_tlv_put(buf, buf_pos, type, sizeof(uint64_t), &value); -} - -static inline RockerTlv *rocker_tlv_nest_start(char *buf, int *buf_pos, - int type) -{ - RockerTlv *start = rocker_tlv_start(buf, *buf_pos); - - rocker_tlv_put(buf, buf_pos, type, 0, NULL); - return start; -} - -static inline void rocker_tlv_nest_end(char *buf, int *buf_pos, - RockerTlv *start) -{ - start->len = (char *) rocker_tlv_start(buf, *buf_pos) - (char *) start; -} - -static inline void rocker_tlv_nest_cancel(char *buf, int *buf_pos, - RockerTlv *start) -{ - *buf_pos = (char *) start - buf; -} - -#endif diff --git a/qemu/hw/net/rocker/rocker_world.c b/qemu/hw/net/rocker/rocker_world.c deleted file mode 100644 index 89777e968..000000000 --- a/qemu/hw/net/rocker/rocker_world.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * QEMU rocker switch emulation - switch worlds - * - * Copyright (c) 2014 Scott Feldman - * - * 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. - */ - -#include "qemu/osdep.h" -#include "qemu/iov.h" - -#include "rocker.h" -#include "rocker_world.h" - -struct world { - Rocker *r; - enum rocker_world_type type; - WorldOps *ops; -}; - -ssize_t world_ingress(World *world, uint32_t pport, - const struct iovec *iov, int iovcnt) -{ - if (world->ops->ig) { - return world->ops->ig(world, pport, iov, iovcnt); - } - - return -1; -} - -int world_do_cmd(World *world, DescInfo *info, - char *buf, uint16_t cmd, RockerTlv *cmd_info_tlv) -{ - if (world->ops->cmd) { - return world->ops->cmd(world, info, buf, cmd, cmd_info_tlv); - } - - return -ROCKER_ENOTSUP; -} - -World *world_alloc(Rocker *r, size_t sizeof_private, - enum rocker_world_type type, WorldOps *ops) -{ - World *w = g_malloc0(sizeof(World) + sizeof_private); - - if (w) { - w->r = r; - w->type = type; - w->ops = ops; - if (w->ops->init) { - w->ops->init(w); - } - } - - return w; -} - -void world_free(World *world) -{ - if (world->ops->uninit) { - world->ops->uninit(world); - } - g_free(world); -} - -void world_reset(World *world) -{ - if (world->ops->uninit) { - world->ops->uninit(world); - } - if (world->ops->init) { - world->ops->init(world); - } -} - -void *world_private(World *world) -{ - return world + 1; -} - -Rocker *world_rocker(World *world) -{ - return world->r; -} - -enum rocker_world_type world_type(World *world) -{ - return world->type; -} - -const char *world_name(World *world) -{ - return world->ops->name; -} diff --git a/qemu/hw/net/rocker/rocker_world.h b/qemu/hw/net/rocker/rocker_world.h deleted file mode 100644 index 58ade4733..000000000 --- a/qemu/hw/net/rocker/rocker_world.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * QEMU rocker switch emulation - switch worlds - * - * Copyright (c) 2014 Scott Feldman - * - * 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. - */ - -#ifndef _ROCKER_WORLD_H_ -#define _ROCKER_WORLD_H_ - -#include "rocker_hw.h" - -enum rocker_world_type { - ROCKER_WORLD_TYPE_OF_DPA = ROCKER_PORT_MODE_OF_DPA, - ROCKER_WORLD_TYPE_MAX, -}; - -typedef int (world_init)(World *world); -typedef void (world_uninit)(World *world); -typedef ssize_t (world_ig)(World *world, uint32_t pport, - const struct iovec *iov, int iovcnt); -typedef int (world_cmd)(World *world, DescInfo *info, - char *buf, uint16_t cmd, - RockerTlv *cmd_info_tlv); - -typedef struct world_ops { - const char *name; - world_init *init; - world_uninit *uninit; - world_ig *ig; - world_cmd *cmd; -} WorldOps; - -ssize_t world_ingress(World *world, uint32_t pport, - const struct iovec *iov, int iovcnt); -int world_do_cmd(World *world, DescInfo *info, - char *buf, uint16_t cmd, RockerTlv *cmd_info_tlv); - -World *world_alloc(Rocker *r, size_t sizeof_private, - enum rocker_world_type type, WorldOps *ops); -void world_free(World *world); -void world_reset(World *world); - -void *world_private(World *world); -Rocker *world_rocker(World *world); - -enum rocker_world_type world_type(World *world); -const char *world_name(World *world); - -World *rocker_get_world(Rocker *r, enum rocker_world_type type); - -#endif /* _ROCKER_WORLD_H_ */ diff --git a/qemu/hw/net/rtl8139.c b/qemu/hw/net/rtl8139.c deleted file mode 100644 index 1e5ec149f..000000000 --- a/qemu/hw/net/rtl8139.c +++ /dev/null @@ -1,3509 +0,0 @@ -/** - * QEMU RTL8139 emulation - * - * Copyright (c) 2006 Igor Kovalenko - * - * 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. - - * Modifications: - * 2006-Jan-28 Mark Malakanov : TSAD and CSCR implementation (for Windows driver) - * - * 2006-Apr-28 Juergen Lock : EEPROM emulation changes for FreeBSD driver - * HW revision ID changes for FreeBSD driver - * - * 2006-Jul-01 Igor Kovalenko : Implemented loopback mode for FreeBSD driver - * Corrected packet transfer reassembly routine for 8139C+ mode - * Rearranged debugging print statements - * Implemented PCI timer interrupt (disabled by default) - * Implemented Tally Counters, increased VM load/save version - * Implemented IP/TCP/UDP checksum task offloading - * - * 2006-Jul-04 Igor Kovalenko : Implemented TCP segmentation offloading - * Fixed MTU=1500 for produced ethernet frames - * - * 2006-Jul-09 Igor Kovalenko : Fixed TCP header length calculation while processing - * segmentation offloading - * Removed slirp.h dependency - * 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 - * Darwin) - * 2011-Mar-22 Benjamin Poirier: Implemented VLAN offloading - */ - -/* For crc32 */ -#include "qemu/osdep.h" -#include - -#include "hw/hw.h" -#include "hw/pci/pci.h" -#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" - -/* debug RTL8139 card */ -//#define DEBUG_RTL8139 1 - -#define PCI_PERIOD 30 /* 30 ns period = 33.333333 Mhz frequency */ - -#define SET_MASKED(input, mask, curr) \ - ( ( (input) & ~(mask) ) | ( (curr) & (mask) ) ) - -/* arg % size for size which is a power of 2 */ -#define MOD2(input, size) \ - ( ( input ) & ( size - 1 ) ) - -#define ETHER_TYPE_LEN 2 -#define ETH_MTU 1500 - -#define VLAN_TCI_LEN 2 -#define VLAN_HLEN (ETHER_TYPE_LEN + VLAN_TCI_LEN) - -#if defined (DEBUG_RTL8139) -# define DPRINTF(fmt, ...) \ - do { fprintf(stderr, "RTL8139: " fmt, ## __VA_ARGS__); } while (0) -#else -static inline GCC_FMT_ATTR(1, 2) int DPRINTF(const char *fmt, ...) -{ - return 0; -} -#endif - -#define TYPE_RTL8139 "rtl8139" - -#define RTL8139(obj) \ - OBJECT_CHECK(RTL8139State, (obj), TYPE_RTL8139) - -/* Symbolic offsets to registers. */ -enum RTL8139_registers { - MAC0 = 0, /* Ethernet hardware address. */ - MAR0 = 8, /* Multicast filter. */ - TxStatus0 = 0x10,/* Transmit status (Four 32bit registers). C mode only */ - /* Dump Tally Conter control register(64bit). C+ mode only */ - TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */ - RxBuf = 0x30, - ChipCmd = 0x37, - RxBufPtr = 0x38, - RxBufAddr = 0x3A, - IntrMask = 0x3C, - IntrStatus = 0x3E, - TxConfig = 0x40, - RxConfig = 0x44, - Timer = 0x48, /* A general-purpose counter. */ - RxMissed = 0x4C, /* 24 bits valid, write clears. */ - Cfg9346 = 0x50, - Config0 = 0x51, - Config1 = 0x52, - FlashReg = 0x54, - MediaStatus = 0x58, - Config3 = 0x59, - Config4 = 0x5A, /* absent on RTL-8139A */ - HltClk = 0x5B, - MultiIntr = 0x5C, - PCIRevisionID = 0x5E, - TxSummary = 0x60, /* TSAD register. Transmit Status of All Descriptors*/ - BasicModeCtrl = 0x62, - BasicModeStatus = 0x64, - NWayAdvert = 0x66, - NWayLPAR = 0x68, - NWayExpansion = 0x6A, - /* Undocumented registers, but required for proper operation. */ - FIFOTMS = 0x70, /* FIFO Control and test. */ - CSCR = 0x74, /* Chip Status and Configuration Register. */ - PARA78 = 0x78, - PARA7c = 0x7c, /* Magic transceiver parameter register. */ - Config5 = 0xD8, /* absent on RTL-8139A */ - /* C+ mode */ - TxPoll = 0xD9, /* Tell chip to check Tx descriptors for work */ - RxMaxSize = 0xDA, /* Max size of an Rx packet (8169 only) */ - CpCmd = 0xE0, /* C+ Command register (C+ mode only) */ - IntrMitigate = 0xE2, /* rx/tx interrupt mitigation control */ - RxRingAddrLO = 0xE4, /* 64-bit start addr of Rx ring */ - RxRingAddrHI = 0xE8, /* 64-bit start addr of Rx ring */ - TxThresh = 0xEC, /* Early Tx threshold */ -}; - -enum ClearBitMasks { - MultiIntrClear = 0xF000, - ChipCmdClear = 0xE2, - Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1), -}; - -enum ChipCmdBits { - CmdReset = 0x10, - CmdRxEnb = 0x08, - CmdTxEnb = 0x04, - RxBufEmpty = 0x01, -}; - -/* C+ mode */ -enum CplusCmdBits { - CPlusRxVLAN = 0x0040, /* enable receive VLAN detagging */ - CPlusRxChkSum = 0x0020, /* enable receive checksum offloading */ - CPlusRxEnb = 0x0002, - CPlusTxEnb = 0x0001, -}; - -/* Interrupt register bits, using my own meaningful names. */ -enum IntrStatusBits { - PCIErr = 0x8000, - PCSTimeout = 0x4000, - RxFIFOOver = 0x40, - RxUnderrun = 0x20, /* Packet Underrun / Link Change */ - RxOverflow = 0x10, - TxErr = 0x08, - TxOK = 0x04, - RxErr = 0x02, - RxOK = 0x01, - - RxAckBits = RxFIFOOver | RxOverflow | RxOK, -}; - -enum TxStatusBits { - TxHostOwns = 0x2000, - TxUnderrun = 0x4000, - TxStatOK = 0x8000, - TxOutOfWindow = 0x20000000, - TxAborted = 0x40000000, - TxCarrierLost = 0x80000000, -}; -enum RxStatusBits { - RxMulticast = 0x8000, - RxPhysical = 0x4000, - RxBroadcast = 0x2000, - RxBadSymbol = 0x0020, - RxRunt = 0x0010, - RxTooLong = 0x0008, - RxCRCErr = 0x0004, - RxBadAlign = 0x0002, - RxStatusOK = 0x0001, -}; - -/* Bits in RxConfig. */ -enum rx_mode_bits { - AcceptErr = 0x20, - AcceptRunt = 0x10, - AcceptBroadcast = 0x08, - AcceptMulticast = 0x04, - AcceptMyPhys = 0x02, - AcceptAllPhys = 0x01, -}; - -/* Bits in TxConfig. */ -enum tx_config_bits { - - /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */ - TxIFGShift = 24, - TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */ - TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */ - TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */ - TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */ - - TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */ - TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */ - TxClearAbt = (1 << 0), /* Clear abort (WO) */ - TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */ - TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */ - - TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */ -}; - - -/* Transmit Status of All Descriptors (TSAD) Register */ -enum TSAD_bits { - TSAD_TOK3 = 1<<15, // TOK bit of Descriptor 3 - TSAD_TOK2 = 1<<14, // TOK bit of Descriptor 2 - TSAD_TOK1 = 1<<13, // TOK bit of Descriptor 1 - TSAD_TOK0 = 1<<12, // TOK bit of Descriptor 0 - TSAD_TUN3 = 1<<11, // TUN bit of Descriptor 3 - TSAD_TUN2 = 1<<10, // TUN bit of Descriptor 2 - TSAD_TUN1 = 1<<9, // TUN bit of Descriptor 1 - TSAD_TUN0 = 1<<8, // TUN bit of Descriptor 0 - TSAD_TABT3 = 1<<07, // TABT bit of Descriptor 3 - TSAD_TABT2 = 1<<06, // TABT bit of Descriptor 2 - TSAD_TABT1 = 1<<05, // TABT bit of Descriptor 1 - TSAD_TABT0 = 1<<04, // TABT bit of Descriptor 0 - TSAD_OWN3 = 1<<03, // OWN bit of Descriptor 3 - TSAD_OWN2 = 1<<02, // OWN bit of Descriptor 2 - TSAD_OWN1 = 1<<01, // OWN bit of Descriptor 1 - TSAD_OWN0 = 1<<00, // OWN bit of Descriptor 0 -}; - - -/* Bits in Config1 */ -enum Config1Bits { - Cfg1_PM_Enable = 0x01, - Cfg1_VPD_Enable = 0x02, - Cfg1_PIO = 0x04, - Cfg1_MMIO = 0x08, - LWAKE = 0x10, /* not on 8139, 8139A */ - Cfg1_Driver_Load = 0x20, - Cfg1_LED0 = 0x40, - Cfg1_LED1 = 0x80, - SLEEP = (1 << 1), /* only on 8139, 8139A */ - PWRDN = (1 << 0), /* only on 8139, 8139A */ -}; - -/* Bits in Config3 */ -enum Config3Bits { - Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */ - Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */ - Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */ - Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */ - Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */ - Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */ - Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */ - Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */ -}; - -/* Bits in Config4 */ -enum Config4Bits { - LWPTN = (1 << 2), /* not on 8139, 8139A */ -}; - -/* Bits in Config5 */ -enum Config5Bits { - Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */ - Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */ - Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */ - Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */ - Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */ - Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */ - Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */ -}; - -enum RxConfigBits { - /* rx fifo threshold */ - RxCfgFIFOShift = 13, - RxCfgFIFONone = (7 << RxCfgFIFOShift), - - /* Max DMA burst */ - RxCfgDMAShift = 8, - RxCfgDMAUnlimited = (7 << RxCfgDMAShift), - - /* rx ring buffer length */ - RxCfgRcv8K = 0, - RxCfgRcv16K = (1 << 11), - RxCfgRcv32K = (1 << 12), - RxCfgRcv64K = (1 << 11) | (1 << 12), - - /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */ - RxNoWrap = (1 << 7), -}; - -/* Twister tuning parameters from RealTek. - Completely undocumented, but required to tune bad links on some boards. */ -/* -enum CSCRBits { - CSCR_LinkOKBit = 0x0400, - CSCR_LinkChangeBit = 0x0800, - CSCR_LinkStatusBits = 0x0f000, - CSCR_LinkDownOffCmd = 0x003c0, - CSCR_LinkDownCmd = 0x0f3c0, -*/ -enum CSCRBits { - CSCR_Testfun = 1<<15, /* 1 = Auto-neg speeds up internal timer, WO, def 0 */ - CSCR_LD = 1<<9, /* Active low TPI link disable signal. When low, TPI still transmits link pulses and TPI stays in good link state. def 1*/ - CSCR_HEART_BIT = 1<<8, /* 1 = HEART BEAT enable, 0 = HEART BEAT disable. HEART BEAT function is only valid in 10Mbps mode. def 1*/ - CSCR_JBEN = 1<<7, /* 1 = enable jabber function. 0 = disable jabber function, def 1*/ - CSCR_F_LINK_100 = 1<<6, /* Used to login force good link in 100Mbps for diagnostic purposes. 1 = DISABLE, 0 = ENABLE. def 1*/ - CSCR_F_Connect = 1<<5, /* Assertion of this bit forces the disconnect function to be bypassed. def 0*/ - CSCR_Con_status = 1<<3, /* This bit indicates the status of the connection. 1 = valid connected link detected; 0 = disconnected link detected. RO def 0*/ - CSCR_Con_status_En = 1<<2, /* Assertion of this bit configures LED1 pin to indicate connection status. def 0*/ - CSCR_PASS_SCR = 1<<0, /* Bypass Scramble, def 0*/ -}; - -enum Cfg9346Bits { - Cfg9346_Normal = 0x00, - Cfg9346_Autoload = 0x40, - Cfg9346_Programming = 0x80, - Cfg9346_ConfigWrite = 0xC0, -}; - -typedef enum { - CH_8139 = 0, - CH_8139_K, - CH_8139A, - CH_8139A_G, - CH_8139B, - CH_8130, - CH_8139C, - CH_8100, - CH_8100B_8139D, - CH_8101, -} chip_t; - -enum chip_flags { - HasHltClk = (1 << 0), - HasLWake = (1 << 1), -}; - -#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \ - (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22) -#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1) - -#define RTL8139_PCI_REVID_8139 0x10 -#define RTL8139_PCI_REVID_8139CPLUS 0x20 - -#define RTL8139_PCI_REVID RTL8139_PCI_REVID_8139CPLUS - -/* Size is 64 * 16bit words */ -#define EEPROM_9346_ADDR_BITS 6 -#define EEPROM_9346_SIZE (1 << EEPROM_9346_ADDR_BITS) -#define EEPROM_9346_ADDR_MASK (EEPROM_9346_SIZE - 1) - -enum Chip9346Operation -{ - Chip9346_op_mask = 0xc0, /* 10 zzzzzz */ - Chip9346_op_read = 0x80, /* 10 AAAAAA */ - Chip9346_op_write = 0x40, /* 01 AAAAAA D(15)..D(0) */ - Chip9346_op_ext_mask = 0xf0, /* 11 zzzzzz */ - Chip9346_op_write_enable = 0x30, /* 00 11zzzz */ - Chip9346_op_write_all = 0x10, /* 00 01zzzz */ - Chip9346_op_write_disable = 0x00, /* 00 00zzzz */ -}; - -enum Chip9346Mode -{ - Chip9346_none = 0, - Chip9346_enter_command_mode, - Chip9346_read_command, - Chip9346_data_read, /* from output register */ - Chip9346_data_write, /* to input register, then to contents at specified address */ - Chip9346_data_write_all, /* to input register, then filling contents */ -}; - -typedef struct EEprom9346 -{ - uint16_t contents[EEPROM_9346_SIZE]; - int mode; - uint32_t tick; - uint8_t address; - uint16_t input; - uint16_t output; - - uint8_t eecs; - uint8_t eesk; - uint8_t eedi; - uint8_t eedo; -} EEprom9346; - -typedef struct RTL8139TallyCounters -{ - /* Tally counters */ - uint64_t TxOk; - uint64_t RxOk; - uint64_t TxERR; - uint32_t RxERR; - uint16_t MissPkt; - uint16_t FAE; - uint32_t Tx1Col; - uint32_t TxMCol; - uint64_t RxOkPhy; - uint64_t RxOkBrd; - uint32_t RxOkMul; - uint16_t TxAbt; - uint16_t TxUndrn; -} RTL8139TallyCounters; - -/* Clears all tally counters */ -static void RTL8139TallyCounters_clear(RTL8139TallyCounters* counters); - -typedef struct RTL8139State { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - uint8_t phys[8]; /* mac address */ - uint8_t mult[8]; /* multicast mask array */ - - uint32_t TxStatus[4]; /* TxStatus0 in C mode*/ /* also DTCCR[0] and DTCCR[1] in C+ mode */ - uint32_t TxAddr[4]; /* TxAddr0 */ - uint32_t RxBuf; /* Receive buffer */ - uint32_t RxBufferSize;/* internal variable, receive ring buffer size in C mode */ - uint32_t RxBufPtr; - uint32_t RxBufAddr; - - uint16_t IntrStatus; - uint16_t IntrMask; - - uint32_t TxConfig; - uint32_t RxConfig; - uint32_t RxMissed; - - uint16_t CSCR; - - uint8_t Cfg9346; - uint8_t Config0; - uint8_t Config1; - uint8_t Config3; - uint8_t Config4; - uint8_t Config5; - - uint8_t clock_enabled; - uint8_t bChipCmdState; - - uint16_t MultiIntr; - - uint16_t BasicModeCtrl; - uint16_t BasicModeStatus; - uint16_t NWayAdvert; - uint16_t NWayLPAR; - uint16_t NWayExpansion; - - uint16_t CpCmd; - uint8_t TxThresh; - - NICState *nic; - NICConf conf; - - /* C ring mode */ - uint32_t currTxDesc; - - /* C+ mode */ - uint32_t cplus_enabled; - - uint32_t currCPlusRxDesc; - uint32_t currCPlusTxDesc; - - uint32_t RxRingAddrLO; - uint32_t RxRingAddrHI; - - EEprom9346 eeprom; - - uint32_t TCTR; - uint32_t TimerInt; - int64_t TCTR_base; - - /* Tally counters */ - RTL8139TallyCounters tally_counters; - - /* Non-persistent data */ - uint8_t *cplus_txbuffer; - int cplus_txbuffer_len; - int cplus_txbuffer_offset; - - /* PCI interrupt timer */ - QEMUTimer *timer; - - MemoryRegion bar_io; - MemoryRegion bar_mem; - - /* Support migration to/from old versions */ - int rtl8139_mmio_io_addr_dummy; -} RTL8139State; - -/* Writes tally counters to memory via DMA */ -static void RTL8139TallyCounters_dma_write(RTL8139State *s, dma_addr_t tc_addr); - -static void rtl8139_set_next_tctr_time(RTL8139State *s); - -static void prom9346_decode_command(EEprom9346 *eeprom, uint8_t command) -{ - DPRINTF("eeprom command 0x%02x\n", command); - - switch (command & Chip9346_op_mask) - { - case Chip9346_op_read: - { - eeprom->address = command & EEPROM_9346_ADDR_MASK; - eeprom->output = eeprom->contents[eeprom->address]; - eeprom->eedo = 0; - eeprom->tick = 0; - eeprom->mode = Chip9346_data_read; - DPRINTF("eeprom read from address 0x%02x data=0x%04x\n", - eeprom->address, eeprom->output); - } - break; - - case Chip9346_op_write: - { - eeprom->address = command & EEPROM_9346_ADDR_MASK; - eeprom->input = 0; - eeprom->tick = 0; - eeprom->mode = Chip9346_none; /* Chip9346_data_write */ - DPRINTF("eeprom begin write to address 0x%02x\n", - eeprom->address); - } - break; - default: - eeprom->mode = Chip9346_none; - switch (command & Chip9346_op_ext_mask) - { - case Chip9346_op_write_enable: - DPRINTF("eeprom write enabled\n"); - break; - case Chip9346_op_write_all: - DPRINTF("eeprom begin write all\n"); - break; - case Chip9346_op_write_disable: - DPRINTF("eeprom write disabled\n"); - break; - } - break; - } -} - -static void prom9346_shift_clock(EEprom9346 *eeprom) -{ - int bit = eeprom->eedi?1:0; - - ++ eeprom->tick; - - DPRINTF("eeprom: tick %d eedi=%d eedo=%d\n", eeprom->tick, eeprom->eedi, - eeprom->eedo); - - switch (eeprom->mode) - { - case Chip9346_enter_command_mode: - if (bit) - { - eeprom->mode = Chip9346_read_command; - eeprom->tick = 0; - eeprom->input = 0; - DPRINTF("eeprom: +++ synchronized, begin command read\n"); - } - break; - - case Chip9346_read_command: - eeprom->input = (eeprom->input << 1) | (bit & 1); - if (eeprom->tick == 8) - { - prom9346_decode_command(eeprom, eeprom->input & 0xff); - } - break; - - case Chip9346_data_read: - eeprom->eedo = (eeprom->output & 0x8000)?1:0; - eeprom->output <<= 1; - if (eeprom->tick == 16) - { -#if 1 - // the FreeBSD drivers (rl and re) don't explicitly toggle - // CS between reads (or does setting Cfg9346 to 0 count too?), - // so we need to enter wait-for-command state here - eeprom->mode = Chip9346_enter_command_mode; - eeprom->input = 0; - eeprom->tick = 0; - - DPRINTF("eeprom: +++ end of read, awaiting next command\n"); -#else - // original behaviour - ++eeprom->address; - eeprom->address &= EEPROM_9346_ADDR_MASK; - eeprom->output = eeprom->contents[eeprom->address]; - eeprom->tick = 0; - - DPRINTF("eeprom: +++ read next address 0x%02x data=0x%04x\n", - eeprom->address, eeprom->output); -#endif - } - break; - - case Chip9346_data_write: - eeprom->input = (eeprom->input << 1) | (bit & 1); - if (eeprom->tick == 16) - { - DPRINTF("eeprom write to address 0x%02x data=0x%04x\n", - eeprom->address, eeprom->input); - - eeprom->contents[eeprom->address] = eeprom->input; - eeprom->mode = Chip9346_none; /* waiting for next command after CS cycle */ - eeprom->tick = 0; - eeprom->input = 0; - } - break; - - case Chip9346_data_write_all: - eeprom->input = (eeprom->input << 1) | (bit & 1); - if (eeprom->tick == 16) - { - int i; - for (i = 0; i < EEPROM_9346_SIZE; i++) - { - eeprom->contents[i] = eeprom->input; - } - DPRINTF("eeprom filled with data=0x%04x\n", eeprom->input); - - eeprom->mode = Chip9346_enter_command_mode; - eeprom->tick = 0; - eeprom->input = 0; - } - break; - - default: - break; - } -} - -static int prom9346_get_wire(RTL8139State *s) -{ - EEprom9346 *eeprom = &s->eeprom; - if (!eeprom->eecs) - return 0; - - return eeprom->eedo; -} - -/* FIXME: This should be merged into/replaced by eeprom93xx.c. */ -static void prom9346_set_wire(RTL8139State *s, int eecs, int eesk, int eedi) -{ - EEprom9346 *eeprom = &s->eeprom; - uint8_t old_eecs = eeprom->eecs; - uint8_t old_eesk = eeprom->eesk; - - eeprom->eecs = eecs; - eeprom->eesk = eesk; - eeprom->eedi = eedi; - - DPRINTF("eeprom: +++ wires CS=%d SK=%d DI=%d DO=%d\n", eeprom->eecs, - eeprom->eesk, eeprom->eedi, eeprom->eedo); - - if (!old_eecs && eecs) - { - /* Synchronize start */ - eeprom->tick = 0; - eeprom->input = 0; - eeprom->output = 0; - eeprom->mode = Chip9346_enter_command_mode; - - DPRINTF("=== eeprom: begin access, enter command mode\n"); - } - - if (!eecs) - { - DPRINTF("=== eeprom: end access\n"); - return; - } - - if (!old_eesk && eesk) - { - /* SK front rules */ - prom9346_shift_clock(eeprom); - } -} - -static void rtl8139_update_irq(RTL8139State *s) -{ - PCIDevice *d = PCI_DEVICE(s); - int isr; - isr = (s->IntrStatus & s->IntrMask) & 0xffff; - - DPRINTF("Set IRQ to %d (%04x %04x)\n", isr ? 1 : 0, s->IntrStatus, - s->IntrMask); - - pci_set_irq(d, (isr != 0)); -} - -static int rtl8139_RxWrap(RTL8139State *s) -{ - /* wrapping enabled; assume 1.5k more buffer space if size < 65536 */ - return (s->RxConfig & (1 << 7)); -} - -static int rtl8139_receiver_enabled(RTL8139State *s) -{ - return s->bChipCmdState & CmdRxEnb; -} - -static int rtl8139_transmitter_enabled(RTL8139State *s) -{ - return s->bChipCmdState & CmdTxEnb; -} - -static int rtl8139_cp_receiver_enabled(RTL8139State *s) -{ - return s->CpCmd & CPlusRxEnb; -} - -static int rtl8139_cp_transmitter_enabled(RTL8139State *s) -{ - return s->CpCmd & CPlusTxEnb; -} - -static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size) -{ - PCIDevice *d = PCI_DEVICE(s); - - if (s->RxBufAddr + size > s->RxBufferSize) - { - int wrapped = MOD2(s->RxBufAddr + size, s->RxBufferSize); - - /* write packet data */ - if (wrapped && !(s->RxBufferSize < 65536 && rtl8139_RxWrap(s))) - { - DPRINTF(">>> rx packet wrapped in buffer at %d\n", size - wrapped); - - if (size > wrapped) - { - pci_dma_write(d, s->RxBuf + s->RxBufAddr, - buf, size-wrapped); - } - - /* reset buffer pointer */ - s->RxBufAddr = 0; - - pci_dma_write(d, s->RxBuf + s->RxBufAddr, - buf + (size-wrapped), wrapped); - - s->RxBufAddr = wrapped; - - return; - } - } - - /* non-wrapping path or overwrapping enabled */ - pci_dma_write(d, s->RxBuf + s->RxBufAddr, buf, size); - - s->RxBufAddr += size; -} - -#define MIN_BUF_SIZE 60 -static inline dma_addr_t rtl8139_addr64(uint32_t low, uint32_t high) -{ - return low | ((uint64_t)high << 32); -} - -/* Workaround for buggy guest driver such as linux who allocates rx - * rings after the receiver were enabled. */ -static bool rtl8139_cp_rx_valid(RTL8139State *s) -{ - return !(s->RxRingAddrLO == 0 && s->RxRingAddrHI == 0); -} - -static int rtl8139_can_receive(NetClientState *nc) -{ - RTL8139State *s = qemu_get_nic_opaque(nc); - int avail; - - /* Receive (drop) packets if card is disabled. */ - if (!s->clock_enabled) - return 1; - if (!rtl8139_receiver_enabled(s)) - return 1; - - if (rtl8139_cp_receiver_enabled(s) && rtl8139_cp_rx_valid(s)) { - /* ??? Flow control not implemented in c+ mode. - This is a hack to work around slirp deficiencies anyway. */ - return 1; - } else { - avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr, - s->RxBufferSize); - return (avail == 0 || avail >= 1514 || (s->IntrMask & RxOverflow)); - } -} - -static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t size_, int do_interrupt) -{ - RTL8139State *s = qemu_get_nic_opaque(nc); - PCIDevice *d = PCI_DEVICE(s); - /* size is the length of the buffer passed to the driver */ - int size = size_; - const uint8_t *dot1q_buf = NULL; - - uint32_t packet_header = 0; - - uint8_t buf1[MIN_BUF_SIZE + VLAN_HLEN]; - static const uint8_t broadcast_macaddr[6] = - { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - - DPRINTF(">>> received len=%d\n", size); - - /* test if board clock is stopped */ - if (!s->clock_enabled) - { - DPRINTF("stopped ==========================\n"); - return -1; - } - - /* first check if receiver is enabled */ - - if (!rtl8139_receiver_enabled(s)) - { - DPRINTF("receiver disabled ================\n"); - return -1; - } - - /* XXX: check this */ - if (s->RxConfig & AcceptAllPhys) { - /* promiscuous: receive all */ - DPRINTF(">>> packet received in promiscuous mode\n"); - - } else { - if (!memcmp(buf, broadcast_macaddr, 6)) { - /* broadcast address */ - if (!(s->RxConfig & AcceptBroadcast)) - { - DPRINTF(">>> broadcast packet rejected\n"); - - /* update tally counter */ - ++s->tally_counters.RxERR; - - return size; - } - - packet_header |= RxBroadcast; - - DPRINTF(">>> broadcast packet received\n"); - - /* update tally counter */ - ++s->tally_counters.RxOkBrd; - - } else if (buf[0] & 0x01) { - /* multicast */ - if (!(s->RxConfig & AcceptMulticast)) - { - DPRINTF(">>> multicast packet rejected\n"); - - /* update tally counter */ - ++s->tally_counters.RxERR; - - return size; - } - - int mcast_idx = compute_mcast_idx(buf); - - if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) - { - DPRINTF(">>> multicast address mismatch\n"); - - /* update tally counter */ - ++s->tally_counters.RxERR; - - return size; - } - - packet_header |= RxMulticast; - - DPRINTF(">>> multicast packet received\n"); - - /* update tally counter */ - ++s->tally_counters.RxOkMul; - - } else if (s->phys[0] == buf[0] && - s->phys[1] == buf[1] && - s->phys[2] == buf[2] && - s->phys[3] == buf[3] && - s->phys[4] == buf[4] && - s->phys[5] == buf[5]) { - /* match */ - if (!(s->RxConfig & AcceptMyPhys)) - { - DPRINTF(">>> rejecting physical address matching packet\n"); - - /* update tally counter */ - ++s->tally_counters.RxERR; - - return size; - } - - packet_header |= RxPhysical; - - DPRINTF(">>> physical address matching packet received\n"); - - /* update tally counter */ - ++s->tally_counters.RxOkPhy; - - } else { - - DPRINTF(">>> unknown packet\n"); - - /* update tally counter */ - ++s->tally_counters.RxERR; - - return size; - } - } - - /* if too small buffer, then expand it - * Include some tailroom in case a vlan tag is later removed. */ - if (size < MIN_BUF_SIZE + VLAN_HLEN) { - memcpy(buf1, buf, size); - memset(buf1 + size, 0, MIN_BUF_SIZE + VLAN_HLEN - size); - buf = buf1; - if (size < MIN_BUF_SIZE) { - size = MIN_BUF_SIZE; - } - } - - if (rtl8139_cp_receiver_enabled(s)) - { - if (!rtl8139_cp_rx_valid(s)) { - return size; - } - - DPRINTF("in C+ Rx mode ================\n"); - - /* begin C+ receiver mode */ - -/* w0 ownership flag */ -#define CP_RX_OWN (1<<31) -/* w0 end of ring flag */ -#define CP_RX_EOR (1<<30) -/* w0 bits 0...12 : buffer size */ -#define CP_RX_BUFFER_SIZE_MASK ((1<<13) - 1) -/* w1 tag available flag */ -#define CP_RX_TAVA (1<<16) -/* w1 bits 0...15 : VLAN tag */ -#define CP_RX_VLAN_TAG_MASK ((1<<16) - 1) -/* w2 low 32bit of Rx buffer ptr */ -/* w3 high 32bit of Rx buffer ptr */ - - int descriptor = s->currCPlusRxDesc; - dma_addr_t cplus_rx_ring_desc; - - cplus_rx_ring_desc = rtl8139_addr64(s->RxRingAddrLO, s->RxRingAddrHI); - cplus_rx_ring_desc += 16 * descriptor; - - DPRINTF("+++ C+ mode reading RX descriptor %d from host memory at " - "%08x %08x = "DMA_ADDR_FMT"\n", descriptor, s->RxRingAddrHI, - s->RxRingAddrLO, cplus_rx_ring_desc); - - uint32_t val, rxdw0,rxdw1,rxbufLO,rxbufHI; - - pci_dma_read(d, cplus_rx_ring_desc, &val, 4); - rxdw0 = le32_to_cpu(val); - pci_dma_read(d, cplus_rx_ring_desc+4, &val, 4); - rxdw1 = le32_to_cpu(val); - pci_dma_read(d, cplus_rx_ring_desc+8, &val, 4); - rxbufLO = le32_to_cpu(val); - pci_dma_read(d, cplus_rx_ring_desc+12, &val, 4); - rxbufHI = le32_to_cpu(val); - - DPRINTF("+++ C+ mode RX descriptor %d %08x %08x %08x %08x\n", - descriptor, rxdw0, rxdw1, rxbufLO, rxbufHI); - - if (!(rxdw0 & CP_RX_OWN)) - { - DPRINTF("C+ Rx mode : descriptor %d is owned by host\n", - descriptor); - - s->IntrStatus |= RxOverflow; - ++s->RxMissed; - - /* update tally counter */ - ++s->tally_counters.RxERR; - ++s->tally_counters.MissPkt; - - rtl8139_update_irq(s); - return size_; - } - - uint32_t rx_space = rxdw0 & CP_RX_BUFFER_SIZE_MASK; - - /* write VLAN info to descriptor variables. */ - if (s->CpCmd & CPlusRxVLAN && be16_to_cpup((uint16_t *) - &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) { - size = MIN_BUF_SIZE; - } - - rxdw1 &= ~CP_RX_VLAN_TAG_MASK; - /* BE + ~le_to_cpu()~ + cpu_to_le() = BE */ - rxdw1 |= CP_RX_TAVA | le16_to_cpup((uint16_t *) - &dot1q_buf[ETHER_TYPE_LEN]); - - DPRINTF("C+ Rx mode : extracted vlan tag with tci: ""%u\n", - be16_to_cpup((uint16_t *)&dot1q_buf[ETHER_TYPE_LEN])); - } else { - /* reset VLAN tag flag */ - rxdw1 &= ~CP_RX_TAVA; - } - - /* TODO: scatter the packet over available receive ring descriptors space */ - - if (size+4 > rx_space) - { - DPRINTF("C+ Rx mode : descriptor %d size %d received %d + 4\n", - descriptor, rx_space, size); - - s->IntrStatus |= RxOverflow; - ++s->RxMissed; - - /* update tally counter */ - ++s->tally_counters.RxERR; - ++s->tally_counters.MissPkt; - - rtl8139_update_irq(s); - return size_; - } - - dma_addr_t rx_addr = rtl8139_addr64(rxbufLO, rxbufHI); - - /* receive/copy to target memory */ - if (dot1q_buf) { - 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); - } - - if (s->CpCmd & CPlusRxChkSum) - { - /* do some packet checksumming */ - } - - /* write checksum */ - val = cpu_to_le32(crc32(0, buf, size_)); - pci_dma_write(d, rx_addr+size, (uint8_t *)&val, 4); - -/* first segment of received packet flag */ -#define CP_RX_STATUS_FS (1<<29) -/* last segment of received packet flag */ -#define CP_RX_STATUS_LS (1<<28) -/* multicast packet flag */ -#define CP_RX_STATUS_MAR (1<<26) -/* physical-matching packet flag */ -#define CP_RX_STATUS_PAM (1<<25) -/* broadcast packet flag */ -#define CP_RX_STATUS_BAR (1<<24) -/* runt packet flag */ -#define CP_RX_STATUS_RUNT (1<<19) -/* crc error flag */ -#define CP_RX_STATUS_CRC (1<<18) -/* IP checksum error flag */ -#define CP_RX_STATUS_IPF (1<<15) -/* UDP checksum error flag */ -#define CP_RX_STATUS_UDPF (1<<14) -/* TCP checksum error flag */ -#define CP_RX_STATUS_TCPF (1<<13) - - /* transfer ownership to target */ - rxdw0 &= ~CP_RX_OWN; - - /* set first segment bit */ - rxdw0 |= CP_RX_STATUS_FS; - - /* set last segment bit */ - rxdw0 |= CP_RX_STATUS_LS; - - /* set received packet type flags */ - if (packet_header & RxBroadcast) - rxdw0 |= CP_RX_STATUS_BAR; - if (packet_header & RxMulticast) - rxdw0 |= CP_RX_STATUS_MAR; - if (packet_header & RxPhysical) - rxdw0 |= CP_RX_STATUS_PAM; - - /* set received size */ - rxdw0 &= ~CP_RX_BUFFER_SIZE_MASK; - rxdw0 |= (size+4); - - /* update ring data */ - val = cpu_to_le32(rxdw0); - pci_dma_write(d, cplus_rx_ring_desc, (uint8_t *)&val, 4); - val = cpu_to_le32(rxdw1); - pci_dma_write(d, cplus_rx_ring_desc+4, (uint8_t *)&val, 4); - - /* update tally counter */ - ++s->tally_counters.RxOk; - - /* seek to next Rx descriptor */ - if (rxdw0 & CP_RX_EOR) - { - s->currCPlusRxDesc = 0; - } - else - { - ++s->currCPlusRxDesc; - } - - DPRINTF("done C+ Rx mode ----------------\n"); - - } - else - { - DPRINTF("in ring Rx mode ================\n"); - - /* begin ring receiver mode */ - int avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr, s->RxBufferSize); - - /* if receiver buffer is empty then avail == 0 */ - -#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", - s->RxBufferSize, s->RxBufAddr, s->RxBufPtr, avail, size + 8); - - s->IntrStatus |= RxOverflow; - ++s->RxMissed; - rtl8139_update_irq(s); - return 0; - } - - packet_header |= RxStatusOK; - - packet_header |= (((size+4) << 16) & 0xffff0000); - - /* write header */ - uint32_t val = cpu_to_le32(packet_header); - - rtl8139_write_buffer(s, (uint8_t *)&val, 4); - - rtl8139_write_buffer(s, buf, size); - - /* write checksum */ - val = cpu_to_le32(crc32(0, buf, size)); - rtl8139_write_buffer(s, (uint8_t *)&val, 4); - - /* correct buffer write pointer */ - s->RxBufAddr = MOD2(RX_ALIGN(s->RxBufAddr), s->RxBufferSize); - - /* now we can signal we have received something */ - - DPRINTF("received: rx buffer length %d head 0x%04x read 0x%04x\n", - s->RxBufferSize, s->RxBufAddr, s->RxBufPtr); - } - - s->IntrStatus |= RxOK; - - if (do_interrupt) - { - rtl8139_update_irq(s); - } - - return size_; -} - -static ssize_t rtl8139_receive(NetClientState *nc, const uint8_t *buf, size_t size) -{ - return rtl8139_do_receive(nc, buf, size, 1); -} - -static void rtl8139_reset_rxring(RTL8139State *s, uint32_t bufferSize) -{ - s->RxBufferSize = bufferSize; - s->RxBufPtr = 0; - s->RxBufAddr = 0; -} - -static void rtl8139_reset(DeviceState *d) -{ - RTL8139State *s = RTL8139(d); - int i; - - /* restore MAC address */ - memcpy(s->phys, s->conf.macaddr.a, 6); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->phys); - - /* reset interrupt mask */ - s->IntrStatus = 0; - s->IntrMask = 0; - - rtl8139_update_irq(s); - - /* mark all status registers as owned by host */ - for (i = 0; i < 4; ++i) - { - s->TxStatus[i] = TxHostOwns; - } - - s->currTxDesc = 0; - s->currCPlusRxDesc = 0; - s->currCPlusTxDesc = 0; - - s->RxRingAddrLO = 0; - s->RxRingAddrHI = 0; - - s->RxBuf = 0; - - rtl8139_reset_rxring(s, 8192); - - /* ACK the reset */ - s->TxConfig = 0; - -#if 0 -// s->TxConfig |= HW_REVID(1, 0, 0, 0, 0, 0, 0); // RTL-8139 HasHltClk - s->clock_enabled = 0; -#else - s->TxConfig |= HW_REVID(1, 1, 1, 0, 1, 1, 0); // RTL-8139C+ HasLWake - s->clock_enabled = 1; -#endif - - s->bChipCmdState = CmdReset; /* RxBufEmpty bit is calculated on read from ChipCmd */; - - /* set initial state data */ - s->Config0 = 0x0; /* No boot ROM */ - s->Config1 = 0xC; /* IO mapped and MEM mapped registers available */ - s->Config3 = 0x1; /* fast back-to-back compatible */ - s->Config5 = 0x0; - - s->CSCR = CSCR_F_LINK_100 | CSCR_HEART_BIT | CSCR_LD; - - s->CpCmd = 0x0; /* reset C+ mode */ - s->cplus_enabled = 0; - - -// s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation -// s->BasicModeCtrl = 0x2100; // 100Mbps, full duplex - s->BasicModeCtrl = 0x1000; // autonegotiation - - s->BasicModeStatus = 0x7809; - //s->BasicModeStatus |= 0x0040; /* UTP medium */ - s->BasicModeStatus |= 0x0020; /* autonegotiation completed */ - /* preserve link state */ - s->BasicModeStatus |= qemu_get_queue(s->nic)->link_down ? 0 : 0x04; - - s->NWayAdvert = 0x05e1; /* all modes, full duplex */ - s->NWayLPAR = 0x05e1; /* all modes, full duplex */ - s->NWayExpansion = 0x0001; /* autonegotiation supported */ - - /* also reset timer and disable timer interrupt */ - s->TCTR = 0; - s->TimerInt = 0; - s->TCTR_base = 0; - rtl8139_set_next_tctr_time(s); - - /* reset tally counters */ - RTL8139TallyCounters_clear(&s->tally_counters); -} - -static void RTL8139TallyCounters_clear(RTL8139TallyCounters* counters) -{ - counters->TxOk = 0; - counters->RxOk = 0; - counters->TxERR = 0; - counters->RxERR = 0; - counters->MissPkt = 0; - counters->FAE = 0; - counters->Tx1Col = 0; - counters->TxMCol = 0; - counters->RxOkPhy = 0; - counters->RxOkBrd = 0; - counters->RxOkMul = 0; - counters->TxAbt = 0; - counters->TxUndrn = 0; -} - -static void RTL8139TallyCounters_dma_write(RTL8139State *s, dma_addr_t tc_addr) -{ - PCIDevice *d = PCI_DEVICE(s); - RTL8139TallyCounters *tally_counters = &s->tally_counters; - uint16_t val16; - uint32_t val32; - uint64_t val64; - - val64 = cpu_to_le64(tally_counters->TxOk); - pci_dma_write(d, tc_addr + 0, (uint8_t *)&val64, 8); - - val64 = cpu_to_le64(tally_counters->RxOk); - pci_dma_write(d, tc_addr + 8, (uint8_t *)&val64, 8); - - val64 = cpu_to_le64(tally_counters->TxERR); - pci_dma_write(d, tc_addr + 16, (uint8_t *)&val64, 8); - - val32 = cpu_to_le32(tally_counters->RxERR); - pci_dma_write(d, tc_addr + 24, (uint8_t *)&val32, 4); - - val16 = cpu_to_le16(tally_counters->MissPkt); - pci_dma_write(d, tc_addr + 28, (uint8_t *)&val16, 2); - - val16 = cpu_to_le16(tally_counters->FAE); - pci_dma_write(d, tc_addr + 30, (uint8_t *)&val16, 2); - - val32 = cpu_to_le32(tally_counters->Tx1Col); - pci_dma_write(d, tc_addr + 32, (uint8_t *)&val32, 4); - - val32 = cpu_to_le32(tally_counters->TxMCol); - pci_dma_write(d, tc_addr + 36, (uint8_t *)&val32, 4); - - val64 = cpu_to_le64(tally_counters->RxOkPhy); - pci_dma_write(d, tc_addr + 40, (uint8_t *)&val64, 8); - - val64 = cpu_to_le64(tally_counters->RxOkBrd); - pci_dma_write(d, tc_addr + 48, (uint8_t *)&val64, 8); - - val32 = cpu_to_le32(tally_counters->RxOkMul); - pci_dma_write(d, tc_addr + 56, (uint8_t *)&val32, 4); - - val16 = cpu_to_le16(tally_counters->TxAbt); - pci_dma_write(d, tc_addr + 60, (uint8_t *)&val16, 2); - - val16 = cpu_to_le16(tally_counters->TxUndrn); - pci_dma_write(d, tc_addr + 62, (uint8_t *)&val16, 2); -} - -/* Loads values of tally counters from VM state file */ - -static const VMStateDescription vmstate_tally_counters = { - .name = "tally_counters", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT64(TxOk, RTL8139TallyCounters), - VMSTATE_UINT64(RxOk, RTL8139TallyCounters), - VMSTATE_UINT64(TxERR, RTL8139TallyCounters), - VMSTATE_UINT32(RxERR, RTL8139TallyCounters), - VMSTATE_UINT16(MissPkt, RTL8139TallyCounters), - VMSTATE_UINT16(FAE, RTL8139TallyCounters), - VMSTATE_UINT32(Tx1Col, RTL8139TallyCounters), - VMSTATE_UINT32(TxMCol, RTL8139TallyCounters), - VMSTATE_UINT64(RxOkPhy, RTL8139TallyCounters), - VMSTATE_UINT64(RxOkBrd, RTL8139TallyCounters), - VMSTATE_UINT16(TxAbt, RTL8139TallyCounters), - VMSTATE_UINT16(TxUndrn, RTL8139TallyCounters), - VMSTATE_END_OF_LIST() - } -}; - -static void rtl8139_ChipCmd_write(RTL8139State *s, uint32_t val) -{ - DeviceState *d = DEVICE(s); - - val &= 0xff; - - DPRINTF("ChipCmd write val=0x%08x\n", val); - - if (val & CmdReset) - { - DPRINTF("ChipCmd reset\n"); - rtl8139_reset(d); - } - if (val & CmdRxEnb) - { - DPRINTF("ChipCmd enable receiver\n"); - - s->currCPlusRxDesc = 0; - } - if (val & CmdTxEnb) - { - DPRINTF("ChipCmd enable transmitter\n"); - - s->currCPlusTxDesc = 0; - } - - /* mask unwritable bits */ - val = SET_MASKED(val, 0xe3, s->bChipCmdState); - - /* Deassert reset pin before next read */ - val &= ~CmdReset; - - s->bChipCmdState = val; -} - -static int rtl8139_RxBufferEmpty(RTL8139State *s) -{ - int unread = MOD2(s->RxBufferSize + s->RxBufAddr - s->RxBufPtr, s->RxBufferSize); - - if (unread != 0) - { - DPRINTF("receiver buffer data available 0x%04x\n", unread); - return 0; - } - - DPRINTF("receiver buffer is empty\n"); - - return 1; -} - -static uint32_t rtl8139_ChipCmd_read(RTL8139State *s) -{ - uint32_t ret = s->bChipCmdState; - - if (rtl8139_RxBufferEmpty(s)) - ret |= RxBufEmpty; - - DPRINTF("ChipCmd read val=0x%04x\n", ret); - - return ret; -} - -static void rtl8139_CpCmd_write(RTL8139State *s, uint32_t val) -{ - val &= 0xffff; - - DPRINTF("C+ command register write(w) val=0x%04x\n", val); - - s->cplus_enabled = 1; - - /* mask unwritable bits */ - val = SET_MASKED(val, 0xff84, s->CpCmd); - - s->CpCmd = val; -} - -static uint32_t rtl8139_CpCmd_read(RTL8139State *s) -{ - uint32_t ret = s->CpCmd; - - DPRINTF("C+ command register read(w) val=0x%04x\n", ret); - - return ret; -} - -static void rtl8139_IntrMitigate_write(RTL8139State *s, uint32_t val) -{ - DPRINTF("C+ IntrMitigate register write(w) val=0x%04x\n", val); -} - -static uint32_t rtl8139_IntrMitigate_read(RTL8139State *s) -{ - uint32_t ret = 0; - - DPRINTF("C+ IntrMitigate register read(w) val=0x%04x\n", ret); - - return ret; -} - -static int rtl8139_config_writable(RTL8139State *s) -{ - if ((s->Cfg9346 & Chip9346_op_mask) == Cfg9346_ConfigWrite) - { - return 1; - } - - DPRINTF("Configuration registers are write-protected\n"); - - return 0; -} - -static void rtl8139_BasicModeCtrl_write(RTL8139State *s, uint32_t val) -{ - val &= 0xffff; - - DPRINTF("BasicModeCtrl register write(w) val=0x%04x\n", val); - - /* mask unwritable bits */ - uint32_t mask = 0x4cff; - - if (1 || !rtl8139_config_writable(s)) - { - /* Speed setting and autonegotiation enable bits are read-only */ - mask |= 0x3000; - /* Duplex mode setting is read-only */ - mask |= 0x0100; - } - - val = SET_MASKED(val, mask, s->BasicModeCtrl); - - s->BasicModeCtrl = val; -} - -static uint32_t rtl8139_BasicModeCtrl_read(RTL8139State *s) -{ - uint32_t ret = s->BasicModeCtrl; - - DPRINTF("BasicModeCtrl register read(w) val=0x%04x\n", ret); - - return ret; -} - -static void rtl8139_BasicModeStatus_write(RTL8139State *s, uint32_t val) -{ - val &= 0xffff; - - DPRINTF("BasicModeStatus register write(w) val=0x%04x\n", val); - - /* mask unwritable bits */ - val = SET_MASKED(val, 0xff3f, s->BasicModeStatus); - - s->BasicModeStatus = val; -} - -static uint32_t rtl8139_BasicModeStatus_read(RTL8139State *s) -{ - uint32_t ret = s->BasicModeStatus; - - DPRINTF("BasicModeStatus register read(w) val=0x%04x\n", ret); - - return ret; -} - -static void rtl8139_Cfg9346_write(RTL8139State *s, uint32_t val) -{ - DeviceState *d = DEVICE(s); - - val &= 0xff; - - DPRINTF("Cfg9346 write val=0x%02x\n", val); - - /* mask unwritable bits */ - val = SET_MASKED(val, 0x31, s->Cfg9346); - - uint32_t opmode = val & 0xc0; - uint32_t eeprom_val = val & 0xf; - - if (opmode == 0x80) { - /* eeprom access */ - int eecs = (eeprom_val & 0x08)?1:0; - int eesk = (eeprom_val & 0x04)?1:0; - int eedi = (eeprom_val & 0x02)?1:0; - prom9346_set_wire(s, eecs, eesk, eedi); - } else if (opmode == 0x40) { - /* Reset. */ - val = 0; - rtl8139_reset(d); - } - - s->Cfg9346 = val; -} - -static uint32_t rtl8139_Cfg9346_read(RTL8139State *s) -{ - uint32_t ret = s->Cfg9346; - - uint32_t opmode = ret & 0xc0; - - if (opmode == 0x80) - { - /* eeprom access */ - int eedo = prom9346_get_wire(s); - if (eedo) - { - ret |= 0x01; - } - else - { - ret &= ~0x01; - } - } - - DPRINTF("Cfg9346 read val=0x%02x\n", ret); - - return ret; -} - -static void rtl8139_Config0_write(RTL8139State *s, uint32_t val) -{ - val &= 0xff; - - DPRINTF("Config0 write val=0x%02x\n", val); - - if (!rtl8139_config_writable(s)) { - return; - } - - /* mask unwritable bits */ - val = SET_MASKED(val, 0xf8, s->Config0); - - s->Config0 = val; -} - -static uint32_t rtl8139_Config0_read(RTL8139State *s) -{ - uint32_t ret = s->Config0; - - DPRINTF("Config0 read val=0x%02x\n", ret); - - return ret; -} - -static void rtl8139_Config1_write(RTL8139State *s, uint32_t val) -{ - val &= 0xff; - - DPRINTF("Config1 write val=0x%02x\n", val); - - if (!rtl8139_config_writable(s)) { - return; - } - - /* mask unwritable bits */ - val = SET_MASKED(val, 0xC, s->Config1); - - s->Config1 = val; -} - -static uint32_t rtl8139_Config1_read(RTL8139State *s) -{ - uint32_t ret = s->Config1; - - DPRINTF("Config1 read val=0x%02x\n", ret); - - return ret; -} - -static void rtl8139_Config3_write(RTL8139State *s, uint32_t val) -{ - val &= 0xff; - - DPRINTF("Config3 write val=0x%02x\n", val); - - if (!rtl8139_config_writable(s)) { - return; - } - - /* mask unwritable bits */ - val = SET_MASKED(val, 0x8F, s->Config3); - - s->Config3 = val; -} - -static uint32_t rtl8139_Config3_read(RTL8139State *s) -{ - uint32_t ret = s->Config3; - - DPRINTF("Config3 read val=0x%02x\n", ret); - - return ret; -} - -static void rtl8139_Config4_write(RTL8139State *s, uint32_t val) -{ - val &= 0xff; - - DPRINTF("Config4 write val=0x%02x\n", val); - - if (!rtl8139_config_writable(s)) { - return; - } - - /* mask unwritable bits */ - val = SET_MASKED(val, 0x0a, s->Config4); - - s->Config4 = val; -} - -static uint32_t rtl8139_Config4_read(RTL8139State *s) -{ - uint32_t ret = s->Config4; - - DPRINTF("Config4 read val=0x%02x\n", ret); - - return ret; -} - -static void rtl8139_Config5_write(RTL8139State *s, uint32_t val) -{ - val &= 0xff; - - DPRINTF("Config5 write val=0x%02x\n", val); - - /* mask unwritable bits */ - val = SET_MASKED(val, 0x80, s->Config5); - - s->Config5 = val; -} - -static uint32_t rtl8139_Config5_read(RTL8139State *s) -{ - uint32_t ret = s->Config5; - - DPRINTF("Config5 read val=0x%02x\n", ret); - - return ret; -} - -static void rtl8139_TxConfig_write(RTL8139State *s, uint32_t val) -{ - if (!rtl8139_transmitter_enabled(s)) - { - DPRINTF("transmitter disabled; no TxConfig write val=0x%08x\n", val); - return; - } - - DPRINTF("TxConfig write val=0x%08x\n", val); - - val = SET_MASKED(val, TxVersionMask | 0x8070f80f, s->TxConfig); - - s->TxConfig = val; -} - -static void rtl8139_TxConfig_writeb(RTL8139State *s, uint32_t val) -{ - DPRINTF("RTL8139C TxConfig via write(b) val=0x%02x\n", val); - - uint32_t tc = s->TxConfig; - tc &= 0xFFFFFF00; - tc |= (val & 0x000000FF); - rtl8139_TxConfig_write(s, tc); -} - -static uint32_t rtl8139_TxConfig_read(RTL8139State *s) -{ - uint32_t ret = s->TxConfig; - - DPRINTF("TxConfig read val=0x%04x\n", ret); - - return ret; -} - -static void rtl8139_RxConfig_write(RTL8139State *s, uint32_t val) -{ - DPRINTF("RxConfig write val=0x%08x\n", val); - - /* mask unwritable bits */ - val = SET_MASKED(val, 0xf0fc0040, s->RxConfig); - - s->RxConfig = val; - - /* reset buffer size and read/write pointers */ - rtl8139_reset_rxring(s, 8192 << ((s->RxConfig >> 11) & 0x3)); - - DPRINTF("RxConfig write reset buffer size to %d\n", s->RxBufferSize); -} - -static uint32_t rtl8139_RxConfig_read(RTL8139State *s) -{ - uint32_t ret = s->RxConfig; - - DPRINTF("RxConfig read val=0x%08x\n", ret); - - return ret; -} - -static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size, - int do_interrupt, const uint8_t *dot1q_buf) -{ - struct iovec *iov = NULL; - struct iovec vlan_iov[3]; - - if (!size) - { - DPRINTF("+++ empty ethernet frame\n"); - return; - } - - if (dot1q_buf && size >= ETH_ALEN * 2) { - iov = (struct iovec[3]) { - { .iov_base = buf, .iov_len = ETH_ALEN * 2 }, - { .iov_base = (void *) dot1q_buf, .iov_len = VLAN_HLEN }, - { .iov_base = buf + ETH_ALEN * 2, - .iov_len = size - ETH_ALEN * 2 }, - }; - - memcpy(vlan_iov, iov, sizeof(vlan_iov)); - iov = vlan_iov; - } - - if (TxLoopBack == (s->TxConfig & TxLoopBack)) - { - size_t buf2_size; - uint8_t *buf2; - - if (iov) { - buf2_size = iov_size(iov, 3); - buf2 = g_malloc(buf2_size); - iov_to_buf(iov, 3, 0, buf2, buf2_size); - buf = buf2; - } - - DPRINTF("+++ transmit loopback mode\n"); - rtl8139_do_receive(qemu_get_queue(s->nic), buf, size, do_interrupt); - - if (iov) { - g_free(buf2); - } - } - else - { - if (iov) { - qemu_sendv_packet(qemu_get_queue(s->nic), iov, 3); - } else { - qemu_send_packet(qemu_get_queue(s->nic), buf, size); - } - } -} - -static int rtl8139_transmit_one(RTL8139State *s, int descriptor) -{ - if (!rtl8139_transmitter_enabled(s)) - { - DPRINTF("+++ cannot transmit from descriptor %d: transmitter " - "disabled\n", descriptor); - return 0; - } - - if (s->TxStatus[descriptor] & TxHostOwns) - { - DPRINTF("+++ cannot transmit from descriptor %d: owned by host " - "(%08x)\n", descriptor, s->TxStatus[descriptor]); - return 0; - } - - DPRINTF("+++ transmitting from descriptor %d\n", descriptor); - - PCIDevice *d = PCI_DEVICE(s); - int txsize = s->TxStatus[descriptor] & 0x1fff; - uint8_t txbuffer[0x2000]; - - DPRINTF("+++ transmit reading %d bytes from host memory at 0x%08x\n", - txsize, s->TxAddr[descriptor]); - - pci_dma_read(d, s->TxAddr[descriptor], txbuffer, txsize); - - /* Mark descriptor as transferred */ - s->TxStatus[descriptor] |= TxHostOwns; - s->TxStatus[descriptor] |= TxStatOK; - - rtl8139_transfer_frame(s, txbuffer, txsize, 0, NULL); - - DPRINTF("+++ transmitted %d bytes from descriptor %d\n", txsize, - descriptor); - - /* update interrupt */ - s->IntrStatus |= TxOK; - rtl8139_update_irq(s); - - return 1; -} - -/* structures and macros for task offloading */ -#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))) - -/* produces ones' complement sum of data */ -static uint16_t ones_complement_sum(uint8_t *data, size_t len) -{ - uint32_t result = 0; - - for (; len > 1; data+=2, len-=2) - { - result += *(uint16_t*)data; - } - - /* add the remainder byte */ - if (len) - { - uint8_t odd[2] = {*data, 0}; - result += *(uint16_t*)odd; - } - - while (result>>16) - result = (result & 0xffff) + (result >> 16); - - return result; -} - -static uint16_t ip_checksum(void *data, size_t len) -{ - return ~ones_complement_sum((uint8_t*)data, len); -} - -static int rtl8139_cplus_transmit_one(RTL8139State *s) -{ - if (!rtl8139_transmitter_enabled(s)) - { - DPRINTF("+++ C+ mode: transmitter disabled\n"); - return 0; - } - - if (!rtl8139_cp_transmitter_enabled(s)) - { - DPRINTF("+++ C+ mode: C+ transmitter disabled\n"); - return 0 ; - } - - PCIDevice *d = PCI_DEVICE(s); - int descriptor = s->currCPlusTxDesc; - - dma_addr_t cplus_tx_ring_desc = rtl8139_addr64(s->TxAddr[0], s->TxAddr[1]); - - /* Normal priority ring */ - cplus_tx_ring_desc += 16 * descriptor; - - DPRINTF("+++ C+ mode reading TX descriptor %d from host memory at " - "%08x %08x = 0x"DMA_ADDR_FMT"\n", descriptor, s->TxAddr[1], - s->TxAddr[0], cplus_tx_ring_desc); - - uint32_t val, txdw0,txdw1,txbufLO,txbufHI; - - pci_dma_read(d, cplus_tx_ring_desc, (uint8_t *)&val, 4); - txdw0 = le32_to_cpu(val); - pci_dma_read(d, cplus_tx_ring_desc+4, (uint8_t *)&val, 4); - txdw1 = le32_to_cpu(val); - pci_dma_read(d, cplus_tx_ring_desc+8, (uint8_t *)&val, 4); - txbufLO = le32_to_cpu(val); - pci_dma_read(d, cplus_tx_ring_desc+12, (uint8_t *)&val, 4); - txbufHI = le32_to_cpu(val); - - DPRINTF("+++ C+ mode TX descriptor %d %08x %08x %08x %08x\n", descriptor, - txdw0, txdw1, txbufLO, txbufHI); - -/* w0 ownership flag */ -#define CP_TX_OWN (1<<31) -/* w0 end of ring flag */ -#define CP_TX_EOR (1<<30) -/* first segment of received packet flag */ -#define CP_TX_FS (1<<29) -/* last segment of received packet flag */ -#define CP_TX_LS (1<<28) -/* large send packet flag */ -#define CP_TX_LGSEN (1<<27) -/* large send MSS mask, bits 16...25 */ -#define CP_TC_LGSEN_MSS_MASK ((1 << 12) - 1) - -/* IP checksum offload flag */ -#define CP_TX_IPCS (1<<18) -/* UDP checksum offload flag */ -#define CP_TX_UDPCS (1<<17) -/* TCP checksum offload flag */ -#define CP_TX_TCPCS (1<<16) - -/* w0 bits 0...15 : buffer size */ -#define CP_TX_BUFFER_SIZE (1<<16) -#define CP_TX_BUFFER_SIZE_MASK (CP_TX_BUFFER_SIZE - 1) -/* w1 add tag flag */ -#define CP_TX_TAGC (1<<17) -/* w1 bits 0...15 : VLAN tag (big endian) */ -#define CP_TX_VLAN_TAG_MASK ((1<<16) - 1) -/* w2 low 32bit of Rx buffer ptr */ -/* w3 high 32bit of Rx buffer ptr */ - -/* set after transmission */ -/* FIFO underrun flag */ -#define CP_TX_STATUS_UNF (1<<25) -/* transmit error summary flag, valid if set any of three below */ -#define CP_TX_STATUS_TES (1<<23) -/* out-of-window collision flag */ -#define CP_TX_STATUS_OWC (1<<22) -/* link failure flag */ -#define CP_TX_STATUS_LNKF (1<<21) -/* excessive collisions flag */ -#define CP_TX_STATUS_EXC (1<<20) - - if (!(txdw0 & CP_TX_OWN)) - { - DPRINTF("C+ Tx mode : descriptor %d is owned by host\n", descriptor); - return 0 ; - } - - DPRINTF("+++ C+ Tx mode : transmitting from descriptor %d\n", descriptor); - - if (txdw0 & CP_TX_FS) - { - DPRINTF("+++ C+ Tx mode : descriptor %d is first segment " - "descriptor\n", descriptor); - - /* reset internal buffer offset */ - s->cplus_txbuffer_offset = 0; - } - - int txsize = txdw0 & CP_TX_BUFFER_SIZE_MASK; - dma_addr_t tx_addr = rtl8139_addr64(txbufLO, txbufHI); - - /* make sure we have enough space to assemble the packet */ - if (!s->cplus_txbuffer) - { - s->cplus_txbuffer_len = CP_TX_BUFFER_SIZE; - s->cplus_txbuffer = g_malloc(s->cplus_txbuffer_len); - s->cplus_txbuffer_offset = 0; - - DPRINTF("+++ C+ mode transmission buffer allocated space %d\n", - s->cplus_txbuffer_len); - } - - if (s->cplus_txbuffer_offset + txsize >= s->cplus_txbuffer_len) - { - /* The spec didn't tell the maximum size, stick to CP_TX_BUFFER_SIZE */ - txsize = s->cplus_txbuffer_len - s->cplus_txbuffer_offset; - DPRINTF("+++ C+ mode transmission buffer overrun, truncated descriptor" - "length to %d\n", txsize); - } - - /* append more data to the packet */ - - DPRINTF("+++ C+ mode transmit reading %d bytes from host memory at " - DMA_ADDR_FMT" to offset %d\n", txsize, tx_addr, - s->cplus_txbuffer_offset); - - pci_dma_read(d, tx_addr, - s->cplus_txbuffer + s->cplus_txbuffer_offset, txsize); - s->cplus_txbuffer_offset += txsize; - - /* seek to next Rx descriptor */ - if (txdw0 & CP_TX_EOR) - { - s->currCPlusTxDesc = 0; - } - else - { - ++s->currCPlusTxDesc; - if (s->currCPlusTxDesc >= 64) - s->currCPlusTxDesc = 0; - } - - /* transfer ownership to target */ - txdw0 &= ~CP_TX_OWN; - - /* reset error indicator bits */ - txdw0 &= ~CP_TX_STATUS_UNF; - txdw0 &= ~CP_TX_STATUS_TES; - txdw0 &= ~CP_TX_STATUS_OWC; - txdw0 &= ~CP_TX_STATUS_LNKF; - txdw0 &= ~CP_TX_STATUS_EXC; - - /* update ring data */ - val = cpu_to_le32(txdw0); - pci_dma_write(d, cplus_tx_ring_desc, (uint8_t *)&val, 4); - - /* Now decide if descriptor being processed is holding the last segment of packet */ - if (txdw0 & CP_TX_LS) - { - uint8_t dot1q_buffer_space[VLAN_HLEN]; - uint16_t *dot1q_buffer; - - DPRINTF("+++ C+ Tx mode : descriptor %d is last segment descriptor\n", - descriptor); - - /* can transfer fully assembled packet */ - - uint8_t *saved_buffer = s->cplus_txbuffer; - int saved_size = s->cplus_txbuffer_offset; - int saved_buffer_len = s->cplus_txbuffer_len; - - /* create vlan tag */ - if (txdw1 & CP_TX_TAGC) { - /* the vlan tag is in BE byte order in the descriptor - * BE + le_to_cpu() + ~swap()~ = cpu */ - DPRINTF("+++ C+ Tx mode : inserting vlan tag with ""tci: %u\n", - bswap16(txdw1 & CP_TX_VLAN_TAG_MASK)); - - dot1q_buffer = (uint16_t *) dot1q_buffer_space; - 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 { - dot1q_buffer = NULL; - } - - /* reset the card space to protect from recursive call */ - s->cplus_txbuffer = NULL; - s->cplus_txbuffer_offset = 0; - s->cplus_txbuffer_len = 0; - - if (txdw0 & (CP_TX_IPCS | CP_TX_UDPCS | CP_TX_TCPCS | CP_TX_LGSEN)) - { - DPRINTF("+++ C+ mode offloaded task checksum\n"); - - /* Large enough for Ethernet and IP headers? */ - if (saved_size < ETH_HLEN + sizeof(struct ip_header)) { - goto skip_offload; - } - - /* ip packet header */ - struct ip_header *ip = NULL; - int hlen = 0; - uint8_t ip_protocol = 0; - uint16_t ip_data_len = 0; - - uint8_t *eth_payload_data = NULL; - size_t eth_payload_len = 0; - - int proto = be16_to_cpu(*(uint16_t *)(saved_buffer + 12)); - if (proto != ETH_P_IP) - { - goto skip_offload; - } - - DPRINTF("+++ C+ mode has IP packet\n"); - - /* 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 = (struct ip_header*)eth_payload_data; - - if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) { - DPRINTF("+++ C+ mode packet has bad IP version %d " - "expected %d\n", IP_HEADER_VERSION(ip), - IP_HEADER_VERSION_4); - goto skip_offload; - } - - hlen = IP_HDR_GET_LEN(ip); - if (hlen < sizeof(struct ip_header) || hlen > eth_payload_len) { - goto skip_offload; - } - - ip_protocol = ip->ip_p; - - ip_data_len = be16_to_cpu(ip->ip_len); - if (ip_data_len < hlen || ip_data_len > eth_payload_len) { - goto skip_offload; - } - ip_data_len -= hlen; - - if (txdw0 & CP_TX_IPCS) - { - DPRINTF("+++ C+ mode need IP checksum\n"); - - ip->ip_sum = 0; - ip->ip_sum = ip_checksum(ip, hlen); - DPRINTF("+++ C+ mode IP header len=%d checksum=%04x\n", - hlen, ip->ip_sum); - } - - if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP) - { - /* Large enough for the TCP header? */ - if (ip_data_len < sizeof(tcp_header)) { - goto skip_offload; - } - - int large_send_mss = (txdw0 >> 16) & CP_TC_LGSEN_MSS_MASK; - - DPRINTF("+++ C+ mode offloaded task TSO MTU=%d IP data %d " - "frame data %d specified MSS=%d\n", ETH_MTU, - ip_data_len, saved_size - ETH_HLEN, large_send_mss); - - int tcp_send_offset = 0; - int send_count = 0; - - /* maximum IP header length is 60 bytes */ - uint8_t saved_ip_header[60]; - - /* save IP header template; data area is used in tcp checksum calculation */ - memcpy(saved_ip_header, eth_payload_data, hlen); - - /* a placeholder for checksum calculation routine in tcp case */ - uint8_t *data_to_checksum = eth_payload_data + hlen - 12; - // size_t data_to_checksum_len = eth_payload_len - hlen + 12; - - /* pointer to TCP header */ - tcp_header *p_tcp_hdr = (tcp_header*)(eth_payload_data + hlen); - - int tcp_hlen = TCP_HEADER_DATA_OFFSET(p_tcp_hdr); - - /* Invalid TCP data offset? */ - if (tcp_hlen < sizeof(tcp_header) || tcp_hlen > ip_data_len) { - goto skip_offload; - } - - /* ETH_MTU = ip header len + tcp header len + payload */ - int tcp_data_len = ip_data_len - tcp_hlen; - int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen; - - DPRINTF("+++ C+ mode TSO IP data len %d TCP hlen %d TCP " - "data len %d TCP chunk size %d\n", ip_data_len, - tcp_hlen, tcp_data_len, tcp_chunk_size); - - /* note the cycle below overwrites IP header data, - but restores it from saved_ip_header before sending packet */ - - int is_last_frame = 0; - - for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += tcp_chunk_size) - { - uint16_t chunk_size = tcp_chunk_size; - - /* check if this is the last frame */ - if (tcp_send_offset + tcp_chunk_size >= tcp_data_len) - { - is_last_frame = 1; - chunk_size = tcp_data_len - tcp_send_offset; - } - - DPRINTF("+++ C+ mode TSO TCP seqno %08x\n", - ldl_be_p(&p_tcp_hdr->th_seq)); - - /* add 4 TCP pseudoheader fields */ - /* copy IP source and destination fields */ - memcpy(data_to_checksum, saved_ip_header + 12, 8); - - DPRINTF("+++ C+ mode TSO calculating TCP checksum for " - "packet with %d bytes data\n", tcp_hlen + - chunk_size); - - if (tcp_send_offset) - { - memcpy((uint8_t*)p_tcp_hdr + tcp_hlen, (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size); - } - - /* keep PUSH and FIN flags only for the last frame */ - if (!is_last_frame) - { - TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TH_PUSH | TH_FIN); - } - - /* recalculate TCP checksum */ - ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum; - p_tcpip_hdr->zeros = 0; - p_tcpip_hdr->ip_proto = IP_PROTO_TCP; - p_tcpip_hdr->ip_payload = cpu_to_be16(tcp_hlen + chunk_size); - - p_tcp_hdr->th_sum = 0; - - int tcp_checksum = ip_checksum(data_to_checksum, tcp_hlen + chunk_size + 12); - DPRINTF("+++ C+ mode TSO TCP checksum %04x\n", - tcp_checksum); - - p_tcp_hdr->th_sum = tcp_checksum; - - /* restore IP header */ - memcpy(eth_payload_data, saved_ip_header, hlen); - - /* set IP data length and recalculate IP checksum */ - ip->ip_len = cpu_to_be16(hlen + tcp_hlen + chunk_size); - - /* increment IP id for subsequent frames */ - ip->ip_id = cpu_to_be16(tcp_send_offset/tcp_chunk_size + be16_to_cpu(ip->ip_id)); - - ip->ip_sum = 0; - ip->ip_sum = ip_checksum(eth_payload_data, hlen); - DPRINTF("+++ C+ mode TSO IP header len=%d " - "checksum=%04x\n", hlen, ip->ip_sum); - - int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size; - DPRINTF("+++ C+ mode TSO transferring packet size " - "%d\n", tso_send_size); - rtl8139_transfer_frame(s, saved_buffer, tso_send_size, - 0, (uint8_t *) dot1q_buffer); - - /* add transferred count to TCP sequence number */ - stl_be_p(&p_tcp_hdr->th_seq, - chunk_size + ldl_be_p(&p_tcp_hdr->th_seq)); - ++send_count; - } - - /* Stop sending this frame */ - saved_size = 0; - } - else if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS)) - { - DPRINTF("+++ C+ mode need TCP or UDP checksum\n"); - - /* maximum IP header length is 60 bytes */ - uint8_t saved_ip_header[60]; - memcpy(saved_ip_header, eth_payload_data, hlen); - - uint8_t *data_to_checksum = eth_payload_data + hlen - 12; - // size_t data_to_checksum_len = eth_payload_len - hlen + 12; - - /* add 4 TCP pseudoheader fields */ - /* copy IP source and destination fields */ - memcpy(data_to_checksum, saved_ip_header + 12, 8); - - if ((txdw0 & CP_TX_TCPCS) && ip_protocol == IP_PROTO_TCP) - { - DPRINTF("+++ C+ mode calculating TCP checksum for " - "packet with %d bytes data\n", ip_data_len); - - ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum; - p_tcpip_hdr->zeros = 0; - p_tcpip_hdr->ip_proto = IP_PROTO_TCP; - p_tcpip_hdr->ip_payload = cpu_to_be16(ip_data_len); - - tcp_header* p_tcp_hdr = (tcp_header *) (data_to_checksum+12); - - p_tcp_hdr->th_sum = 0; - - int tcp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12); - DPRINTF("+++ C+ mode TCP checksum %04x\n", - tcp_checksum); - - p_tcp_hdr->th_sum = tcp_checksum; - } - else if ((txdw0 & CP_TX_UDPCS) && ip_protocol == IP_PROTO_UDP) - { - DPRINTF("+++ C+ mode calculating UDP checksum for " - "packet with %d bytes data\n", ip_data_len); - - ip_pseudo_header *p_udpip_hdr = (ip_pseudo_header *)data_to_checksum; - p_udpip_hdr->zeros = 0; - p_udpip_hdr->ip_proto = IP_PROTO_UDP; - p_udpip_hdr->ip_payload = cpu_to_be16(ip_data_len); - - udp_header *p_udp_hdr = (udp_header *) (data_to_checksum+12); - - p_udp_hdr->uh_sum = 0; - - int udp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12); - DPRINTF("+++ C+ mode UDP checksum %04x\n", - udp_checksum); - - p_udp_hdr->uh_sum = udp_checksum; - } - - /* restore IP header */ - memcpy(eth_payload_data, saved_ip_header, hlen); - } - } - -skip_offload: - /* update tally counter */ - ++s->tally_counters.TxOk; - - DPRINTF("+++ C+ mode transmitting %d bytes packet\n", saved_size); - - rtl8139_transfer_frame(s, saved_buffer, saved_size, 1, - (uint8_t *) dot1q_buffer); - - /* restore card space if there was no recursion and reset offset */ - if (!s->cplus_txbuffer) - { - s->cplus_txbuffer = saved_buffer; - s->cplus_txbuffer_len = saved_buffer_len; - s->cplus_txbuffer_offset = 0; - } - else - { - g_free(saved_buffer); - } - } - else - { - DPRINTF("+++ C+ mode transmission continue to next descriptor\n"); - } - - return 1; -} - -static void rtl8139_cplus_transmit(RTL8139State *s) -{ - int txcount = 0; - - while (rtl8139_cplus_transmit_one(s)) - { - ++txcount; - } - - /* Mark transfer completed */ - if (!txcount) - { - DPRINTF("C+ mode : transmitter queue stalled, current TxDesc = %d\n", - s->currCPlusTxDesc); - } - else - { - /* update interrupt status */ - s->IntrStatus |= TxOK; - rtl8139_update_irq(s); - } -} - -static void rtl8139_transmit(RTL8139State *s) -{ - int descriptor = s->currTxDesc, txcount = 0; - - /*while*/ - if (rtl8139_transmit_one(s, descriptor)) - { - ++s->currTxDesc; - s->currTxDesc %= 4; - ++txcount; - } - - /* Mark transfer completed */ - if (!txcount) - { - DPRINTF("transmitter queue stalled, current TxDesc = %d\n", - s->currTxDesc); - } -} - -static void rtl8139_TxStatus_write(RTL8139State *s, uint32_t txRegOffset, uint32_t val) -{ - - int descriptor = txRegOffset/4; - - /* handle C+ transmit mode register configuration */ - - if (s->cplus_enabled) - { - DPRINTF("RTL8139C+ DTCCR write offset=0x%x val=0x%08x " - "descriptor=%d\n", txRegOffset, val, descriptor); - - /* handle Dump Tally Counters command */ - s->TxStatus[descriptor] = val; - - if (descriptor == 0 && (val & 0x8)) - { - hwaddr tc_addr = rtl8139_addr64(s->TxStatus[0] & ~0x3f, s->TxStatus[1]); - - /* dump tally counters to specified memory location */ - RTL8139TallyCounters_dma_write(s, tc_addr); - - /* mark dump completed */ - s->TxStatus[0] &= ~0x8; - } - - return; - } - - DPRINTF("TxStatus write offset=0x%x val=0x%08x descriptor=%d\n", - txRegOffset, val, descriptor); - - /* mask only reserved bits */ - val &= ~0xff00c000; /* these bits are reset on write */ - val = SET_MASKED(val, 0x00c00000, s->TxStatus[descriptor]); - - s->TxStatus[descriptor] = val; - - /* attempt to start transmission */ - rtl8139_transmit(s); -} - -static uint32_t rtl8139_TxStatus_TxAddr_read(RTL8139State *s, uint32_t regs[], - uint32_t base, uint8_t addr, - int size) -{ - uint32_t reg = (addr - base) / 4; - uint32_t offset = addr & 0x3; - uint32_t ret = 0; - - if (addr & (size - 1)) { - DPRINTF("not implemented read for TxStatus/TxAddr " - "addr=0x%x size=0x%x\n", addr, size); - return ret; - } - - switch (size) { - case 1: /* fall through */ - case 2: /* fall through */ - case 4: - ret = (regs[reg] >> offset * 8) & (((uint64_t)1 << (size * 8)) - 1); - DPRINTF("TxStatus/TxAddr[%d] read addr=0x%x size=0x%x val=0x%08x\n", - reg, addr, size, ret); - break; - default: - DPRINTF("unsupported size 0x%x of TxStatus/TxAddr reading\n", size); - break; - } - - return ret; -} - -static uint16_t rtl8139_TSAD_read(RTL8139State *s) -{ - uint16_t ret = 0; - - /* Simulate TSAD, it is read only anyway */ - - ret = ((s->TxStatus[3] & TxStatOK )?TSAD_TOK3:0) - |((s->TxStatus[2] & TxStatOK )?TSAD_TOK2:0) - |((s->TxStatus[1] & TxStatOK )?TSAD_TOK1:0) - |((s->TxStatus[0] & TxStatOK )?TSAD_TOK0:0) - - |((s->TxStatus[3] & TxUnderrun)?TSAD_TUN3:0) - |((s->TxStatus[2] & TxUnderrun)?TSAD_TUN2:0) - |((s->TxStatus[1] & TxUnderrun)?TSAD_TUN1:0) - |((s->TxStatus[0] & TxUnderrun)?TSAD_TUN0:0) - - |((s->TxStatus[3] & TxAborted )?TSAD_TABT3:0) - |((s->TxStatus[2] & TxAborted )?TSAD_TABT2:0) - |((s->TxStatus[1] & TxAborted )?TSAD_TABT1:0) - |((s->TxStatus[0] & TxAborted )?TSAD_TABT0:0) - - |((s->TxStatus[3] & TxHostOwns )?TSAD_OWN3:0) - |((s->TxStatus[2] & TxHostOwns )?TSAD_OWN2:0) - |((s->TxStatus[1] & TxHostOwns )?TSAD_OWN1:0) - |((s->TxStatus[0] & TxHostOwns )?TSAD_OWN0:0) ; - - - DPRINTF("TSAD read val=0x%04x\n", ret); - - return ret; -} - -static uint16_t rtl8139_CSCR_read(RTL8139State *s) -{ - uint16_t ret = s->CSCR; - - DPRINTF("CSCR read val=0x%04x\n", ret); - - return ret; -} - -static void rtl8139_TxAddr_write(RTL8139State *s, uint32_t txAddrOffset, uint32_t val) -{ - DPRINTF("TxAddr write offset=0x%x val=0x%08x\n", txAddrOffset, val); - - s->TxAddr[txAddrOffset/4] = val; -} - -static uint32_t rtl8139_TxAddr_read(RTL8139State *s, uint32_t txAddrOffset) -{ - uint32_t ret = s->TxAddr[txAddrOffset/4]; - - DPRINTF("TxAddr read offset=0x%x val=0x%08x\n", txAddrOffset, ret); - - return ret; -} - -static void rtl8139_RxBufPtr_write(RTL8139State *s, uint32_t val) -{ - DPRINTF("RxBufPtr write val=0x%04x\n", val); - - /* this value is off by 16 */ - s->RxBufPtr = MOD2(val + 0x10, s->RxBufferSize); - - /* more buffer space may be available so try to receive */ - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - - DPRINTF(" CAPR write: rx buffer length %d head 0x%04x read 0x%04x\n", - s->RxBufferSize, s->RxBufAddr, s->RxBufPtr); -} - -static uint32_t rtl8139_RxBufPtr_read(RTL8139State *s) -{ - /* this value is off by 16 */ - uint32_t ret = s->RxBufPtr - 0x10; - - DPRINTF("RxBufPtr read val=0x%04x\n", ret); - - return ret; -} - -static uint32_t rtl8139_RxBufAddr_read(RTL8139State *s) -{ - /* this value is NOT off by 16 */ - uint32_t ret = s->RxBufAddr; - - DPRINTF("RxBufAddr read val=0x%04x\n", ret); - - return ret; -} - -static void rtl8139_RxBuf_write(RTL8139State *s, uint32_t val) -{ - DPRINTF("RxBuf write val=0x%08x\n", val); - - s->RxBuf = val; - - /* may need to reset rxring here */ -} - -static uint32_t rtl8139_RxBuf_read(RTL8139State *s) -{ - uint32_t ret = s->RxBuf; - - DPRINTF("RxBuf read val=0x%08x\n", ret); - - return ret; -} - -static void rtl8139_IntrMask_write(RTL8139State *s, uint32_t val) -{ - DPRINTF("IntrMask write(w) val=0x%04x\n", val); - - /* mask unwritable bits */ - val = SET_MASKED(val, 0x1e00, s->IntrMask); - - s->IntrMask = val; - - rtl8139_update_irq(s); - -} - -static uint32_t rtl8139_IntrMask_read(RTL8139State *s) -{ - uint32_t ret = s->IntrMask; - - DPRINTF("IntrMask read(w) val=0x%04x\n", ret); - - return ret; -} - -static void rtl8139_IntrStatus_write(RTL8139State *s, uint32_t val) -{ - DPRINTF("IntrStatus write(w) val=0x%04x\n", val); - -#if 0 - - /* writing to ISR has no effect */ - - return; - -#else - uint16_t newStatus = s->IntrStatus & ~val; - - /* mask unwritable bits */ - newStatus = SET_MASKED(newStatus, 0x1e00, s->IntrStatus); - - /* writing 1 to interrupt status register bit clears it */ - s->IntrStatus = 0; - rtl8139_update_irq(s); - - s->IntrStatus = newStatus; - rtl8139_set_next_tctr_time(s); - rtl8139_update_irq(s); - -#endif -} - -static uint32_t rtl8139_IntrStatus_read(RTL8139State *s) -{ - uint32_t ret = s->IntrStatus; - - DPRINTF("IntrStatus read(w) val=0x%04x\n", ret); - -#if 0 - - /* reading ISR clears all interrupts */ - s->IntrStatus = 0; - - rtl8139_update_irq(s); - -#endif - - return ret; -} - -static void rtl8139_MultiIntr_write(RTL8139State *s, uint32_t val) -{ - DPRINTF("MultiIntr write(w) val=0x%04x\n", val); - - /* mask unwritable bits */ - val = SET_MASKED(val, 0xf000, s->MultiIntr); - - s->MultiIntr = val; -} - -static uint32_t rtl8139_MultiIntr_read(RTL8139State *s) -{ - uint32_t ret = s->MultiIntr; - - DPRINTF("MultiIntr read(w) val=0x%04x\n", ret); - - return ret; -} - -static void rtl8139_io_writeb(void *opaque, uint8_t addr, uint32_t val) -{ - RTL8139State *s = opaque; - - switch (addr) - { - case MAC0 ... MAC0+4: - s->phys[addr - MAC0] = val; - break; - case MAC0+5: - s->phys[addr - MAC0] = val; - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->phys); - break; - case MAC0+6 ... MAC0+7: - /* reserved */ - break; - case MAR0 ... MAR0+7: - s->mult[addr - MAR0] = val; - break; - case ChipCmd: - rtl8139_ChipCmd_write(s, val); - break; - case Cfg9346: - rtl8139_Cfg9346_write(s, val); - break; - case TxConfig: /* windows driver sometimes writes using byte-lenth call */ - rtl8139_TxConfig_writeb(s, val); - break; - case Config0: - rtl8139_Config0_write(s, val); - break; - case Config1: - rtl8139_Config1_write(s, val); - break; - case Config3: - rtl8139_Config3_write(s, val); - break; - case Config4: - rtl8139_Config4_write(s, val); - break; - case Config5: - rtl8139_Config5_write(s, val); - break; - case MediaStatus: - /* ignore */ - DPRINTF("not implemented write(b) to MediaStatus val=0x%02x\n", - val); - break; - - case HltClk: - DPRINTF("HltClk write val=0x%08x\n", val); - if (val == 'R') - { - s->clock_enabled = 1; - } - else if (val == 'H') - { - s->clock_enabled = 0; - } - break; - - case TxThresh: - DPRINTF("C+ TxThresh write(b) val=0x%02x\n", val); - s->TxThresh = val; - break; - - case TxPoll: - DPRINTF("C+ TxPoll write(b) val=0x%02x\n", val); - if (val & (1 << 7)) - { - DPRINTF("C+ TxPoll high priority transmission (not " - "implemented)\n"); - //rtl8139_cplus_transmit(s); - } - if (val & (1 << 6)) - { - DPRINTF("C+ TxPoll normal priority transmission\n"); - rtl8139_cplus_transmit(s); - } - - break; - - default: - DPRINTF("not implemented write(b) addr=0x%x val=0x%02x\n", addr, - val); - break; - } -} - -static void rtl8139_io_writew(void *opaque, uint8_t addr, uint32_t val) -{ - RTL8139State *s = opaque; - - switch (addr) - { - case IntrMask: - rtl8139_IntrMask_write(s, val); - break; - - case IntrStatus: - rtl8139_IntrStatus_write(s, val); - break; - - case MultiIntr: - rtl8139_MultiIntr_write(s, val); - break; - - case RxBufPtr: - rtl8139_RxBufPtr_write(s, val); - break; - - case BasicModeCtrl: - rtl8139_BasicModeCtrl_write(s, val); - break; - case BasicModeStatus: - rtl8139_BasicModeStatus_write(s, val); - break; - case NWayAdvert: - DPRINTF("NWayAdvert write(w) val=0x%04x\n", val); - s->NWayAdvert = val; - break; - case NWayLPAR: - DPRINTF("forbidden NWayLPAR write(w) val=0x%04x\n", val); - break; - case NWayExpansion: - DPRINTF("NWayExpansion write(w) val=0x%04x\n", val); - s->NWayExpansion = val; - break; - - case CpCmd: - rtl8139_CpCmd_write(s, val); - break; - - case IntrMitigate: - rtl8139_IntrMitigate_write(s, val); - break; - - default: - DPRINTF("ioport write(w) addr=0x%x val=0x%04x via write(b)\n", - addr, val); - - rtl8139_io_writeb(opaque, addr, val & 0xff); - rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff); - break; - } -} - -static void rtl8139_set_next_tctr_time(RTL8139State *s) -{ - const uint64_t ns_per_period = (uint64_t)PCI_PERIOD << 32; - - DPRINTF("entered rtl8139_set_next_tctr_time\n"); - - /* This function is called at least once per period, so it is a good - * place to update the timer base. - * - * After one iteration of this loop the value in the Timer register does - * not change, but the device model is counting up by 2^32 ticks (approx. - * 130 seconds). - */ - while (s->TCTR_base + ns_per_period <= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) { - s->TCTR_base += ns_per_period; - } - - if (!s->TimerInt) { - timer_del(s->timer); - } else { - 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; - } - timer_mod(s->timer, s->TCTR_base + delta); - } -} - -static void rtl8139_io_writel(void *opaque, uint8_t addr, uint32_t val) -{ - RTL8139State *s = opaque; - - switch (addr) - { - case RxMissed: - DPRINTF("RxMissed clearing on write\n"); - s->RxMissed = 0; - break; - - case TxConfig: - rtl8139_TxConfig_write(s, val); - break; - - case RxConfig: - rtl8139_RxConfig_write(s, val); - break; - - case TxStatus0 ... TxStatus0+4*4-1: - rtl8139_TxStatus_write(s, addr-TxStatus0, val); - break; - - case TxAddr0 ... TxAddr0+4*4-1: - rtl8139_TxAddr_write(s, addr-TxAddr0, val); - break; - - case RxBuf: - rtl8139_RxBuf_write(s, val); - break; - - case RxRingAddrLO: - DPRINTF("C+ RxRing low bits write val=0x%08x\n", val); - s->RxRingAddrLO = val; - break; - - case RxRingAddrHI: - DPRINTF("C+ RxRing high bits write val=0x%08x\n", val); - s->RxRingAddrHI = val; - break; - - case Timer: - DPRINTF("TCTR Timer reset on write\n"); - s->TCTR_base = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - rtl8139_set_next_tctr_time(s); - break; - - case FlashReg: - DPRINTF("FlashReg TimerInt write val=0x%08x\n", val); - if (s->TimerInt != val) { - s->TimerInt = val; - rtl8139_set_next_tctr_time(s); - } - break; - - default: - DPRINTF("ioport write(l) addr=0x%x val=0x%08x via write(b)\n", - addr, val); - rtl8139_io_writeb(opaque, addr, val & 0xff); - rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff); - rtl8139_io_writeb(opaque, addr + 2, (val >> 16) & 0xff); - rtl8139_io_writeb(opaque, addr + 3, (val >> 24) & 0xff); - break; - } -} - -static uint32_t rtl8139_io_readb(void *opaque, uint8_t addr) -{ - RTL8139State *s = opaque; - int ret; - - switch (addr) - { - case MAC0 ... MAC0+5: - ret = s->phys[addr - MAC0]; - break; - case MAC0+6 ... MAC0+7: - ret = 0; - break; - case MAR0 ... MAR0+7: - ret = s->mult[addr - MAR0]; - break; - case TxStatus0 ... TxStatus0+4*4-1: - ret = rtl8139_TxStatus_TxAddr_read(s, s->TxStatus, TxStatus0, - addr, 1); - break; - case ChipCmd: - ret = rtl8139_ChipCmd_read(s); - break; - case Cfg9346: - ret = rtl8139_Cfg9346_read(s); - break; - case Config0: - ret = rtl8139_Config0_read(s); - break; - case Config1: - ret = rtl8139_Config1_read(s); - break; - case Config3: - ret = rtl8139_Config3_read(s); - break; - case Config4: - ret = rtl8139_Config4_read(s); - break; - case Config5: - ret = rtl8139_Config5_read(s); - break; - - case MediaStatus: - /* The LinkDown bit of MediaStatus is inverse with link status */ - ret = 0xd0 | (~s->BasicModeStatus & 0x04); - DPRINTF("MediaStatus read 0x%x\n", ret); - break; - - case HltClk: - ret = s->clock_enabled; - DPRINTF("HltClk read 0x%x\n", ret); - break; - - case PCIRevisionID: - ret = RTL8139_PCI_REVID; - DPRINTF("PCI Revision ID read 0x%x\n", ret); - break; - - case TxThresh: - ret = s->TxThresh; - DPRINTF("C+ TxThresh read(b) val=0x%02x\n", ret); - break; - - case 0x43: /* Part of TxConfig register. Windows driver tries to read it */ - ret = s->TxConfig >> 24; - DPRINTF("RTL8139C TxConfig at 0x43 read(b) val=0x%02x\n", ret); - break; - - default: - DPRINTF("not implemented read(b) addr=0x%x\n", addr); - ret = 0; - break; - } - - return ret; -} - -static uint32_t rtl8139_io_readw(void *opaque, uint8_t addr) -{ - RTL8139State *s = opaque; - uint32_t ret; - - switch (addr) - { - case TxAddr0 ... TxAddr0+4*4-1: - ret = rtl8139_TxStatus_TxAddr_read(s, s->TxAddr, TxAddr0, addr, 2); - break; - case IntrMask: - ret = rtl8139_IntrMask_read(s); - break; - - case IntrStatus: - ret = rtl8139_IntrStatus_read(s); - break; - - case MultiIntr: - ret = rtl8139_MultiIntr_read(s); - break; - - case RxBufPtr: - ret = rtl8139_RxBufPtr_read(s); - break; - - case RxBufAddr: - ret = rtl8139_RxBufAddr_read(s); - break; - - case BasicModeCtrl: - ret = rtl8139_BasicModeCtrl_read(s); - break; - case BasicModeStatus: - ret = rtl8139_BasicModeStatus_read(s); - break; - case NWayAdvert: - ret = s->NWayAdvert; - DPRINTF("NWayAdvert read(w) val=0x%04x\n", ret); - break; - case NWayLPAR: - ret = s->NWayLPAR; - DPRINTF("NWayLPAR read(w) val=0x%04x\n", ret); - break; - case NWayExpansion: - ret = s->NWayExpansion; - DPRINTF("NWayExpansion read(w) val=0x%04x\n", ret); - break; - - case CpCmd: - ret = rtl8139_CpCmd_read(s); - break; - - case IntrMitigate: - ret = rtl8139_IntrMitigate_read(s); - break; - - case TxSummary: - ret = rtl8139_TSAD_read(s); - break; - - case CSCR: - ret = rtl8139_CSCR_read(s); - break; - - default: - DPRINTF("ioport read(w) addr=0x%x via read(b)\n", addr); - - ret = rtl8139_io_readb(opaque, addr); - ret |= rtl8139_io_readb(opaque, addr + 1) << 8; - - DPRINTF("ioport read(w) addr=0x%x val=0x%04x\n", addr, ret); - break; - } - - return ret; -} - -static uint32_t rtl8139_io_readl(void *opaque, uint8_t addr) -{ - RTL8139State *s = opaque; - uint32_t ret; - - switch (addr) - { - case RxMissed: - ret = s->RxMissed; - - DPRINTF("RxMissed read val=0x%08x\n", ret); - break; - - case TxConfig: - ret = rtl8139_TxConfig_read(s); - break; - - case RxConfig: - ret = rtl8139_RxConfig_read(s); - break; - - case TxStatus0 ... TxStatus0+4*4-1: - ret = rtl8139_TxStatus_TxAddr_read(s, s->TxStatus, TxStatus0, - addr, 4); - break; - - case TxAddr0 ... TxAddr0+4*4-1: - ret = rtl8139_TxAddr_read(s, addr-TxAddr0); - break; - - case RxBuf: - ret = rtl8139_RxBuf_read(s); - break; - - case RxRingAddrLO: - ret = s->RxRingAddrLO; - DPRINTF("C+ RxRing low bits read val=0x%08x\n", ret); - break; - - case RxRingAddrHI: - ret = s->RxRingAddrHI; - DPRINTF("C+ RxRing high bits read val=0x%08x\n", ret); - break; - - case Timer: - ret = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->TCTR_base) / - PCI_PERIOD; - DPRINTF("TCTR Timer read val=0x%08x\n", ret); - break; - - case FlashReg: - ret = s->TimerInt; - DPRINTF("FlashReg TimerInt read val=0x%08x\n", ret); - break; - - default: - DPRINTF("ioport read(l) addr=0x%x via read(b)\n", addr); - - ret = rtl8139_io_readb(opaque, addr); - ret |= rtl8139_io_readb(opaque, addr + 1) << 8; - ret |= rtl8139_io_readb(opaque, addr + 2) << 16; - ret |= rtl8139_io_readb(opaque, addr + 3) << 24; - - DPRINTF("read(l) addr=0x%x val=%08x\n", addr, ret); - break; - } - - return ret; -} - -/* */ - -static void rtl8139_mmio_writeb(void *opaque, hwaddr addr, uint32_t val) -{ - rtl8139_io_writeb(opaque, addr & 0xFF, val); -} - -static void rtl8139_mmio_writew(void *opaque, hwaddr addr, uint32_t val) -{ - rtl8139_io_writew(opaque, addr & 0xFF, val); -} - -static void rtl8139_mmio_writel(void *opaque, hwaddr addr, uint32_t val) -{ - rtl8139_io_writel(opaque, addr & 0xFF, val); -} - -static uint32_t rtl8139_mmio_readb(void *opaque, hwaddr addr) -{ - return rtl8139_io_readb(opaque, addr & 0xFF); -} - -static uint32_t rtl8139_mmio_readw(void *opaque, hwaddr addr) -{ - uint32_t val = rtl8139_io_readw(opaque, addr & 0xFF); - return val; -} - -static uint32_t rtl8139_mmio_readl(void *opaque, hwaddr addr) -{ - uint32_t val = rtl8139_io_readl(opaque, addr & 0xFF); - return val; -} - -static int rtl8139_post_load(void *opaque, int version_id) -{ - RTL8139State* s = opaque; - rtl8139_set_next_tctr_time(s); - if (version_id < 4) { - s->cplus_enabled = s->CpCmd != 0; - } - - /* nc.link_down can't be migrated, so infer link_down according - * to link status bit in BasicModeStatus */ - qemu_get_queue(s->nic)->link_down = (s->BasicModeStatus & 0x04) == 0; - - return 0; -} - -static bool rtl8139_hotplug_ready_needed(void *opaque) -{ - return qdev_machine_modified(); -} - -static const VMStateDescription vmstate_rtl8139_hotplug_ready ={ - .name = "rtl8139/hotplug_ready", - .version_id = 1, - .minimum_version_id = 1, - .needed = rtl8139_hotplug_ready_needed, - .fields = (VMStateField[]) { - VMSTATE_END_OF_LIST() - } -}; - -static void rtl8139_pre_save(void *opaque) -{ - RTL8139State* s = opaque; - int64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - /* for migration to older versions */ - s->TCTR = (current_time - s->TCTR_base) / PCI_PERIOD; - s->rtl8139_mmio_io_addr_dummy = 0; -} - -static const VMStateDescription vmstate_rtl8139 = { - .name = "rtl8139", - .version_id = 4, - .minimum_version_id = 3, - .post_load = rtl8139_post_load, - .pre_save = rtl8139_pre_save, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, RTL8139State), - VMSTATE_PARTIAL_BUFFER(phys, RTL8139State, 6), - VMSTATE_BUFFER(mult, RTL8139State), - VMSTATE_UINT32_ARRAY(TxStatus, RTL8139State, 4), - VMSTATE_UINT32_ARRAY(TxAddr, RTL8139State, 4), - - VMSTATE_UINT32(RxBuf, RTL8139State), - VMSTATE_UINT32(RxBufferSize, RTL8139State), - VMSTATE_UINT32(RxBufPtr, RTL8139State), - VMSTATE_UINT32(RxBufAddr, RTL8139State), - - VMSTATE_UINT16(IntrStatus, RTL8139State), - VMSTATE_UINT16(IntrMask, RTL8139State), - - VMSTATE_UINT32(TxConfig, RTL8139State), - VMSTATE_UINT32(RxConfig, RTL8139State), - VMSTATE_UINT32(RxMissed, RTL8139State), - VMSTATE_UINT16(CSCR, RTL8139State), - - VMSTATE_UINT8(Cfg9346, RTL8139State), - VMSTATE_UINT8(Config0, RTL8139State), - VMSTATE_UINT8(Config1, RTL8139State), - VMSTATE_UINT8(Config3, RTL8139State), - VMSTATE_UINT8(Config4, RTL8139State), - VMSTATE_UINT8(Config5, RTL8139State), - - VMSTATE_UINT8(clock_enabled, RTL8139State), - VMSTATE_UINT8(bChipCmdState, RTL8139State), - - VMSTATE_UINT16(MultiIntr, RTL8139State), - - VMSTATE_UINT16(BasicModeCtrl, RTL8139State), - VMSTATE_UINT16(BasicModeStatus, RTL8139State), - VMSTATE_UINT16(NWayAdvert, RTL8139State), - VMSTATE_UINT16(NWayLPAR, RTL8139State), - VMSTATE_UINT16(NWayExpansion, RTL8139State), - - VMSTATE_UINT16(CpCmd, RTL8139State), - VMSTATE_UINT8(TxThresh, RTL8139State), - - VMSTATE_UNUSED(4), - VMSTATE_MACADDR(conf.macaddr, RTL8139State), - VMSTATE_INT32(rtl8139_mmio_io_addr_dummy, RTL8139State), - - VMSTATE_UINT32(currTxDesc, RTL8139State), - VMSTATE_UINT32(currCPlusRxDesc, RTL8139State), - VMSTATE_UINT32(currCPlusTxDesc, RTL8139State), - VMSTATE_UINT32(RxRingAddrLO, RTL8139State), - VMSTATE_UINT32(RxRingAddrHI, RTL8139State), - - VMSTATE_UINT16_ARRAY(eeprom.contents, RTL8139State, EEPROM_9346_SIZE), - VMSTATE_INT32(eeprom.mode, RTL8139State), - VMSTATE_UINT32(eeprom.tick, RTL8139State), - VMSTATE_UINT8(eeprom.address, RTL8139State), - VMSTATE_UINT16(eeprom.input, RTL8139State), - VMSTATE_UINT16(eeprom.output, RTL8139State), - - VMSTATE_UINT8(eeprom.eecs, RTL8139State), - VMSTATE_UINT8(eeprom.eesk, RTL8139State), - VMSTATE_UINT8(eeprom.eedi, RTL8139State), - VMSTATE_UINT8(eeprom.eedo, RTL8139State), - - VMSTATE_UINT32(TCTR, RTL8139State), - VMSTATE_UINT32(TimerInt, RTL8139State), - VMSTATE_INT64(TCTR_base, RTL8139State), - - VMSTATE_STRUCT(tally_counters, RTL8139State, 0, - vmstate_tally_counters, RTL8139TallyCounters), - - VMSTATE_UINT32_V(cplus_enabled, RTL8139State, 4), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_rtl8139_hotplug_ready, - NULL - } -}; - -/***********************************************************/ -/* PCI RTL8139 definitions */ - -static void rtl8139_ioport_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - switch (size) { - case 1: - rtl8139_io_writeb(opaque, addr, val); - break; - case 2: - rtl8139_io_writew(opaque, addr, val); - break; - case 4: - rtl8139_io_writel(opaque, addr, val); - break; - } -} - -static uint64_t rtl8139_ioport_read(void *opaque, hwaddr addr, - unsigned size) -{ - switch (size) { - case 1: - return rtl8139_io_readb(opaque, addr); - case 2: - return rtl8139_io_readw(opaque, addr); - case 4: - return rtl8139_io_readl(opaque, addr); - } - - return -1; -} - -static const MemoryRegionOps rtl8139_io_ops = { - .read = rtl8139_ioport_read, - .write = rtl8139_ioport_write, - .impl = { - .min_access_size = 1, - .max_access_size = 4, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps rtl8139_mmio_ops = { - .old_mmio = { - .read = { - rtl8139_mmio_readb, - rtl8139_mmio_readw, - rtl8139_mmio_readl, - }, - .write = { - rtl8139_mmio_writeb, - rtl8139_mmio_writew, - rtl8139_mmio_writel, - }, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void rtl8139_timer(void *opaque) -{ - RTL8139State *s = opaque; - - if (!s->clock_enabled) - { - DPRINTF(">>> timer: clock is not running\n"); - return; - } - - s->IntrStatus |= PCSTimeout; - rtl8139_update_irq(s); - rtl8139_set_next_tctr_time(s); -} - -static void pci_rtl8139_uninit(PCIDevice *dev) -{ - RTL8139State *s = RTL8139(dev); - - g_free(s->cplus_txbuffer); - s->cplus_txbuffer = NULL; - timer_del(s->timer); - timer_free(s->timer); - qemu_del_nic(s->nic); -} - -static void rtl8139_set_link_status(NetClientState *nc) -{ - RTL8139State *s = qemu_get_nic_opaque(nc); - - if (nc->link_down) { - s->BasicModeStatus &= ~0x04; - } else { - s->BasicModeStatus |= 0x04; - } - - s->IntrStatus |= RxUnderrun; - rtl8139_update_irq(s); -} - -static NetClientInfo net_rtl8139_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .can_receive = rtl8139_can_receive, - .receive = rtl8139_receive, - .link_status_changed = rtl8139_set_link_status, -}; - -static void pci_rtl8139_realize(PCIDevice *dev, Error **errp) -{ - RTL8139State *s = RTL8139(dev); - DeviceState *d = DEVICE(dev); - uint8_t *pci_conf; - - pci_conf = dev->config; - pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */ - /* TODO: start of capability list, but no capability - * list bit in status register, and offset 0xdc seems unused. */ - pci_conf[PCI_CAPABILITY_LIST] = 0xdc; - - memory_region_init_io(&s->bar_io, OBJECT(s), &rtl8139_io_ops, s, - "rtl8139", 0x100); - memory_region_init_io(&s->bar_mem, OBJECT(s), &rtl8139_mmio_ops, s, - "rtl8139", 0x100); - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->bar_io); - pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar_mem); - - qemu_macaddr_default_if_unset(&s->conf.macaddr); - - /* prepare eeprom */ - s->eeprom.contents[0] = 0x8129; -#if 1 - /* PCI vendor and device ID should be mirrored here */ - s->eeprom.contents[1] = PCI_VENDOR_ID_REALTEK; - s->eeprom.contents[2] = PCI_DEVICE_ID_REALTEK_8139; -#endif - s->eeprom.contents[7] = s->conf.macaddr.a[0] | s->conf.macaddr.a[1] << 8; - s->eeprom.contents[8] = s->conf.macaddr.a[2] | s->conf.macaddr.a[3] << 8; - s->eeprom.contents[9] = s->conf.macaddr.a[4] | s->conf.macaddr.a[5] << 8; - - s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf, - object_get_typename(OBJECT(dev)), d->id, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - s->cplus_txbuffer = NULL; - s->cplus_txbuffer_len = 0; - s->cplus_txbuffer_offset = 0; - - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, rtl8139_timer, s); -} - -static void rtl8139_instance_init(Object *obj) -{ - RTL8139State *s = RTL8139(obj); - - device_add_bootindex_property(obj, &s->conf.bootindex, - "bootindex", "/ethernet-phy@0", - DEVICE(obj), NULL); -} - -static Property rtl8139_properties[] = { - DEFINE_NIC_PROPERTIES(RTL8139State, conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void rtl8139_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = pci_rtl8139_realize; - k->exit = pci_rtl8139_uninit; - k->romfile = "efi-rtl8139.rom"; - k->vendor_id = PCI_VENDOR_ID_REALTEK; - k->device_id = PCI_DEVICE_ID_REALTEK_8139; - k->revision = RTL8139_PCI_REVID; /* >=0x20 is for 8139C+ */ - k->class_id = PCI_CLASS_NETWORK_ETHERNET; - dc->reset = rtl8139_reset; - dc->vmsd = &vmstate_rtl8139; - dc->props = rtl8139_properties; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); -} - -static const TypeInfo rtl8139_info = { - .name = TYPE_RTL8139, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(RTL8139State), - .class_init = rtl8139_class_init, - .instance_init = rtl8139_instance_init, -}; - -static void rtl8139_register_types(void) -{ - type_register_static(&rtl8139_info); -} - -type_init(rtl8139_register_types) diff --git a/qemu/hw/net/smc91c111.c b/qemu/hw/net/smc91c111.c deleted file mode 100644 index 21c1b8f54..000000000 --- a/qemu/hw/net/smc91c111.c +++ /dev/null @@ -1,825 +0,0 @@ -/* - * SMSC 91C111 Ethernet interface emulation - * - * Copyright (c) 2005 CodeSourcery, LLC. - * Written by Paul Brook - * - * This code is licensed under the GPL - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "net/net.h" -#include "hw/devices.h" -/* For crc32 */ -#include - -/* Number of 2k memory pages available. */ -#define NUM_PACKETS 4 - -#define TYPE_SMC91C111 "smc91c111" -#define SMC91C111(obj) OBJECT_CHECK(smc91c111_state, (obj), TYPE_SMC91C111) - -typedef struct { - SysBusDevice parent_obj; - - NICState *nic; - NICConf conf; - uint16_t tcr; - uint16_t rcr; - uint16_t cr; - uint16_t ctr; - uint16_t gpr; - uint16_t ptr; - uint16_t ercv; - qemu_irq irq; - int bank; - int packet_num; - int tx_alloc; - /* Bitmask of allocated packets. */ - int allocated; - int tx_fifo_len; - int tx_fifo[NUM_PACKETS]; - int rx_fifo_len; - int rx_fifo[NUM_PACKETS]; - int tx_fifo_done_len; - int tx_fifo_done[NUM_PACKETS]; - /* Packet buffer memory. */ - uint8_t data[NUM_PACKETS][2048]; - uint8_t int_level; - uint8_t int_mask; - MemoryRegion mmio; -} smc91c111_state; - -static const VMStateDescription vmstate_smc91c111 = { - .name = "smc91c111", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT16(tcr, smc91c111_state), - VMSTATE_UINT16(rcr, smc91c111_state), - VMSTATE_UINT16(cr, smc91c111_state), - VMSTATE_UINT16(ctr, smc91c111_state), - VMSTATE_UINT16(gpr, smc91c111_state), - VMSTATE_UINT16(ptr, smc91c111_state), - VMSTATE_UINT16(ercv, smc91c111_state), - VMSTATE_INT32(bank, smc91c111_state), - VMSTATE_INT32(packet_num, smc91c111_state), - VMSTATE_INT32(tx_alloc, smc91c111_state), - VMSTATE_INT32(allocated, smc91c111_state), - VMSTATE_INT32(tx_fifo_len, smc91c111_state), - VMSTATE_INT32_ARRAY(tx_fifo, smc91c111_state, NUM_PACKETS), - VMSTATE_INT32(rx_fifo_len, smc91c111_state), - VMSTATE_INT32_ARRAY(rx_fifo, smc91c111_state, NUM_PACKETS), - VMSTATE_INT32(tx_fifo_done_len, smc91c111_state), - VMSTATE_INT32_ARRAY(tx_fifo_done, smc91c111_state, NUM_PACKETS), - VMSTATE_BUFFER_UNSAFE(data, smc91c111_state, 0, NUM_PACKETS * 2048), - VMSTATE_UINT8(int_level, smc91c111_state), - VMSTATE_UINT8(int_mask, smc91c111_state), - VMSTATE_END_OF_LIST() - } -}; - -#define RCR_SOFT_RST 0x8000 -#define RCR_STRIP_CRC 0x0200 -#define RCR_RXEN 0x0100 - -#define TCR_EPH_LOOP 0x2000 -#define TCR_NOCRC 0x0100 -#define TCR_PAD_EN 0x0080 -#define TCR_FORCOL 0x0004 -#define TCR_LOOP 0x0002 -#define TCR_TXEN 0x0001 - -#define INT_MD 0x80 -#define INT_ERCV 0x40 -#define INT_EPH 0x20 -#define INT_RX_OVRN 0x10 -#define INT_ALLOC 0x08 -#define INT_TX_EMPTY 0x04 -#define INT_TX 0x02 -#define INT_RCV 0x01 - -#define CTR_AUTO_RELEASE 0x0800 -#define CTR_RELOAD 0x0002 -#define CTR_STORE 0x0001 - -#define RS_ALGNERR 0x8000 -#define RS_BRODCAST 0x4000 -#define RS_BADCRC 0x2000 -#define RS_ODDFRAME 0x1000 -#define RS_TOOLONG 0x0800 -#define RS_TOOSHORT 0x0400 -#define RS_MULTICAST 0x0001 - -/* Update interrupt status. */ -static void smc91c111_update(smc91c111_state *s) -{ - int level; - - if (s->tx_fifo_len == 0) - s->int_level |= INT_TX_EMPTY; - if (s->tx_fifo_done_len != 0) - s->int_level |= INT_TX; - level = (s->int_level & s->int_mask) != 0; - 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) -{ - int i; - if (s->allocated == (1 << NUM_PACKETS) - 1) { - return 0x80; - } - - for (i = 0; i < NUM_PACKETS; i++) { - if ((s->allocated & (1 << i)) == 0) - break; - } - s->allocated |= 1 << i; - return i; -} - - -/* Process a pending TX allocate. */ -static void smc91c111_tx_alloc(smc91c111_state *s) -{ - s->tx_alloc = smc91c111_allocate_packet(s); - if (s->tx_alloc == 0x80) - return; - s->int_level |= INT_ALLOC; - smc91c111_update(s); -} - -/* Remove and item from the RX FIFO. */ -static void smc91c111_pop_rx_fifo(smc91c111_state *s) -{ - int i; - - s->rx_fifo_len--; - if (s->rx_fifo_len) { - for (i = 0; i < s->rx_fifo_len; i++) - s->rx_fifo[i] = s->rx_fifo[i + 1]; - s->int_level |= INT_RCV; - } else { - s->int_level &= ~INT_RCV; - } - smc91c111_flush_queued_packets(s); - smc91c111_update(s); -} - -/* Remove an item from the TX completion FIFO. */ -static void smc91c111_pop_tx_fifo_done(smc91c111_state *s) -{ - int i; - - if (s->tx_fifo_done_len == 0) - return; - s->tx_fifo_done_len--; - for (i = 0; i < s->tx_fifo_done_len; i++) - s->tx_fifo_done[i] = s->tx_fifo_done[i + 1]; -} - -/* Release the memory allocated to a packet. */ -static void smc91c111_release_packet(smc91c111_state *s, int packet) -{ - s->allocated &= ~(1 << packet); - if (s->tx_alloc == 0x80) - smc91c111_tx_alloc(s); - smc91c111_flush_queued_packets(s); -} - -/* Flush the TX FIFO. */ -static void smc91c111_do_tx(smc91c111_state *s) -{ - int i; - int len; - int control; - int packetnum; - uint8_t *p; - - if ((s->tcr & TCR_TXEN) == 0) - return; - if (s->tx_fifo_len == 0) - return; - for (i = 0; i < s->tx_fifo_len; i++) { - packetnum = s->tx_fifo[i]; - p = &s->data[packetnum][0]; - /* Set status word. */ - *(p++) = 0x01; - *(p++) = 0x40; - len = *(p++); - len |= ((int)*(p++)) << 8; - len -= 6; - control = p[len + 1]; - if (control & 0x20) - len++; - /* ??? This overwrites the data following the buffer. - Don't know what real hardware does. */ - if (len < 64 && (s->tcr & TCR_PAD_EN)) { - memset(p + len, 0, 64 - len); - len = 64; - } -#if 0 - { - int add_crc; - - /* The card is supposed to append the CRC to the frame. - However none of the other network traffic has the CRC - appended. Suspect this is low level ethernet detail we - don't need to worry about. */ - add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0; - if (add_crc) { - uint32_t crc; - - crc = crc32(~0, p, len); - memcpy(p + len, &crc, 4); - len += 4; - } - } -#endif - if (s->ctr & CTR_AUTO_RELEASE) - /* Race? */ - smc91c111_release_packet(s, packetnum); - else if (s->tx_fifo_done_len < NUM_PACKETS) - s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum; - qemu_send_packet(qemu_get_queue(s->nic), p, len); - } - s->tx_fifo_len = 0; - smc91c111_update(s); -} - -/* Add a packet to the TX FIFO. */ -static void smc91c111_queue_tx(smc91c111_state *s, int packet) -{ - if (s->tx_fifo_len == NUM_PACKETS) - return; - s->tx_fifo[s->tx_fifo_len++] = packet; - smc91c111_do_tx(s); -} - -static void smc91c111_reset(DeviceState *dev) -{ - smc91c111_state *s = SMC91C111(dev); - - s->bank = 0; - s->tx_fifo_len = 0; - s->tx_fifo_done_len = 0; - s->rx_fifo_len = 0; - s->allocated = 0; - s->packet_num = 0; - s->tx_alloc = 0; - s->tcr = 0; - s->rcr = 0; - s->cr = 0xa0b1; - s->ctr = 0x1210; - s->ptr = 0; - s->ercv = 0x1f; - s->int_level = INT_TX_EMPTY; - s->int_mask = 0; - smc91c111_update(s); -} - -#define SET_LOW(name, val) s->name = (s->name & 0xff00) | val -#define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8) - -static void smc91c111_writeb(void *opaque, hwaddr offset, - uint32_t value) -{ - smc91c111_state *s = (smc91c111_state *)opaque; - - offset = offset & 0xf; - if (offset == 14) { - s->bank = value; - return; - } - if (offset == 15) - return; - switch (s->bank) { - case 0: - switch (offset) { - case 0: /* TCR */ - SET_LOW(tcr, value); - return; - case 1: - SET_HIGH(tcr, value); - return; - case 4: /* RCR */ - SET_LOW(rcr, value); - return; - case 5: - SET_HIGH(rcr, value); - if (s->rcr & RCR_SOFT_RST) { - smc91c111_reset(DEVICE(s)); - } - smc91c111_flush_queued_packets(s); - return; - case 10: case 11: /* RPCR */ - /* Ignored */ - return; - case 12: case 13: /* Reserved */ - return; - } - break; - - case 1: - switch (offset) { - case 0: /* CONFIG */ - SET_LOW(cr, value); - return; - case 1: - SET_HIGH(cr,value); - return; - case 2: case 3: /* BASE */ - case 4: case 5: case 6: case 7: case 8: case 9: /* IA */ - /* Not implemented. */ - return; - case 10: /* Genral Purpose */ - SET_LOW(gpr, value); - return; - case 11: - SET_HIGH(gpr, value); - return; - case 12: /* Control */ - if (value & 1) - fprintf(stderr, "smc91c111:EEPROM store not implemented\n"); - if (value & 2) - fprintf(stderr, "smc91c111:EEPROM reload not implemented\n"); - value &= ~3; - SET_LOW(ctr, value); - return; - case 13: - SET_HIGH(ctr, value); - return; - } - break; - - case 2: - switch (offset) { - case 0: /* MMU Command */ - switch (value >> 5) { - case 0: /* no-op */ - break; - case 1: /* Allocate for TX. */ - s->tx_alloc = 0x80; - s->int_level &= ~INT_ALLOC; - smc91c111_update(s); - smc91c111_tx_alloc(s); - break; - case 2: /* Reset MMU. */ - s->allocated = 0; - s->tx_fifo_len = 0; - s->tx_fifo_done_len = 0; - s->rx_fifo_len = 0; - s->tx_alloc = 0; - break; - case 3: /* Remove from RX FIFO. */ - smc91c111_pop_rx_fifo(s); - break; - case 4: /* Remove from RX FIFO and release. */ - if (s->rx_fifo_len > 0) { - smc91c111_release_packet(s, s->rx_fifo[0]); - } - smc91c111_pop_rx_fifo(s); - break; - case 5: /* Release. */ - smc91c111_release_packet(s, s->packet_num); - break; - case 6: /* Add to TX FIFO. */ - smc91c111_queue_tx(s, s->packet_num); - break; - case 7: /* Reset TX FIFO. */ - s->tx_fifo_len = 0; - s->tx_fifo_done_len = 0; - break; - } - return; - case 1: - /* Ignore. */ - return; - case 2: /* Packet Number Register */ - s->packet_num = value; - return; - case 3: case 4: case 5: - /* Should be readonly, but linux writes to them anyway. Ignore. */ - return; - case 6: /* Pointer */ - SET_LOW(ptr, value); - return; - case 7: - SET_HIGH(ptr, value); - return; - case 8: case 9: case 10: case 11: /* Data */ - { - int p; - int n; - - if (s->ptr & 0x8000) - n = s->rx_fifo[0]; - else - n = s->packet_num; - p = s->ptr & 0x07ff; - if (s->ptr & 0x4000) { - s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff); - } else { - p += (offset & 3); - } - s->data[n][p] = value; - } - return; - case 12: /* Interrupt ACK. */ - s->int_level &= ~(value & 0xd6); - if (value & INT_TX) - smc91c111_pop_tx_fifo_done(s); - smc91c111_update(s); - return; - case 13: /* Interrupt mask. */ - s->int_mask = value; - smc91c111_update(s); - return; - } - break; - - case 3: - switch (offset) { - case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: - /* Multicast table. */ - /* Not implemented. */ - return; - case 8: case 9: /* Management Interface. */ - /* Not implemented. */ - return; - case 12: /* Early receive. */ - s->ercv = value & 0x1f; - return; - case 13: - /* Ignore. */ - return; - } - break; - } - hw_error("smc91c111_write: Bad reg %d:%x\n", s->bank, (int)offset); -} - -static uint32_t smc91c111_readb(void *opaque, hwaddr offset) -{ - smc91c111_state *s = (smc91c111_state *)opaque; - - offset = offset & 0xf; - if (offset == 14) { - return s->bank; - } - if (offset == 15) - return 0x33; - switch (s->bank) { - case 0: - switch (offset) { - case 0: /* TCR */ - return s->tcr & 0xff; - case 1: - return s->tcr >> 8; - case 2: /* EPH Status */ - return 0; - case 3: - return 0x40; - case 4: /* RCR */ - return s->rcr & 0xff; - case 5: - return s->rcr >> 8; - case 6: /* Counter */ - case 7: - /* Not implemented. */ - return 0; - case 8: /* Memory size. */ - return NUM_PACKETS; - case 9: /* Free memory available. */ - { - int i; - int n; - n = 0; - for (i = 0; i < NUM_PACKETS; i++) { - if (s->allocated & (1 << i)) - n++; - } - return n; - } - case 10: case 11: /* RPCR */ - /* Not implemented. */ - return 0; - case 12: case 13: /* Reserved */ - return 0; - } - break; - - case 1: - switch (offset) { - case 0: /* CONFIG */ - return s->cr & 0xff; - case 1: - return s->cr >> 8; - case 2: case 3: /* BASE */ - /* Not implemented. */ - return 0; - case 4: case 5: case 6: case 7: case 8: case 9: /* IA */ - return s->conf.macaddr.a[offset - 4]; - case 10: /* General Purpose */ - return s->gpr & 0xff; - case 11: - return s->gpr >> 8; - case 12: /* Control */ - return s->ctr & 0xff; - case 13: - return s->ctr >> 8; - } - break; - - case 2: - switch (offset) { - case 0: case 1: /* MMUCR Busy bit. */ - return 0; - case 2: /* Packet Number. */ - return s->packet_num; - case 3: /* Allocation Result. */ - return s->tx_alloc; - case 4: /* TX FIFO */ - if (s->tx_fifo_done_len == 0) - return 0x80; - else - return s->tx_fifo_done[0]; - case 5: /* RX FIFO */ - if (s->rx_fifo_len == 0) - return 0x80; - else - return s->rx_fifo[0]; - case 6: /* Pointer */ - return s->ptr & 0xff; - case 7: - return (s->ptr >> 8) & 0xf7; - case 8: case 9: case 10: case 11: /* Data */ - { - int p; - int n; - - if (s->ptr & 0x8000) - n = s->rx_fifo[0]; - else - n = s->packet_num; - p = s->ptr & 0x07ff; - if (s->ptr & 0x4000) { - s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff); - } else { - p += (offset & 3); - } - return s->data[n][p]; - } - case 12: /* Interrupt status. */ - return s->int_level; - case 13: /* Interrupt mask. */ - return s->int_mask; - } - break; - - case 3: - switch (offset) { - case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: - /* Multicast table. */ - /* Not implemented. */ - return 0; - case 8: /* Management Interface. */ - /* Not implemented. */ - return 0x30; - case 9: - return 0x33; - case 10: /* Revision. */ - return 0x91; - case 11: - return 0x33; - case 12: - return s->ercv; - case 13: - return 0; - } - break; - } - hw_error("smc91c111_read: Bad reg %d:%x\n", s->bank, (int)offset); - return 0; -} - -static void smc91c111_writew(void *opaque, hwaddr offset, - uint32_t value) -{ - smc91c111_writeb(opaque, offset, value & 0xff); - smc91c111_writeb(opaque, offset + 1, value >> 8); -} - -static void smc91c111_writel(void *opaque, hwaddr offset, - uint32_t value) -{ - /* 32-bit writes to offset 0xc only actually write to the bank select - register (offset 0xe) */ - if (offset != 0xc) - smc91c111_writew(opaque, offset, value & 0xffff); - smc91c111_writew(opaque, offset + 2, value >> 16); -} - -static uint32_t smc91c111_readw(void *opaque, hwaddr offset) -{ - uint32_t val; - val = smc91c111_readb(opaque, offset); - val |= smc91c111_readb(opaque, offset + 1) << 8; - return val; -} - -static uint32_t smc91c111_readl(void *opaque, hwaddr offset) -{ - uint32_t val; - val = smc91c111_readw(opaque, offset); - val |= smc91c111_readw(opaque, offset + 2) << 16; - return val; -} - -static int smc91c111_can_receive_nc(NetClientState *nc) -{ - smc91c111_state *s = qemu_get_nic_opaque(nc); - - return smc91c111_can_receive(s); -} - -static ssize_t smc91c111_receive(NetClientState *nc, const uint8_t *buf, size_t size) -{ - smc91c111_state *s = qemu_get_nic_opaque(nc); - int status; - int packetsize; - uint32_t crc; - int packetnum; - uint8_t *p; - - if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST)) - return -1; - /* Short packets are padded with zeros. Receiving a packet - < 64 bytes long is considered an error condition. */ - if (size < 64) - packetsize = 64; - else - packetsize = (size & ~1); - packetsize += 6; - crc = (s->rcr & RCR_STRIP_CRC) == 0; - if (crc) - packetsize += 4; - /* TODO: Flag overrun and receive errors. */ - if (packetsize > 2048) - return -1; - packetnum = smc91c111_allocate_packet(s); - if (packetnum == 0x80) - return -1; - s->rx_fifo[s->rx_fifo_len++] = packetnum; - - p = &s->data[packetnum][0]; - /* ??? Multicast packets? */ - status = 0; - if (size > 1518) - status |= RS_TOOLONG; - if (size & 1) - status |= RS_ODDFRAME; - *(p++) = status & 0xff; - *(p++) = status >> 8; - *(p++) = packetsize & 0xff; - *(p++) = packetsize >> 8; - memcpy(p, buf, size & ~1); - p += (size & ~1); - /* Pad short packets. */ - if (size < 64) { - int pad; - - if (size & 1) - *(p++) = buf[size - 1]; - pad = 64 - size; - memset(p, 0, pad); - p += pad; - size = 64; - } - /* It's not clear if the CRC should go before or after the last byte in - odd sized packets. Linux disables the CRC, so that's no help. - The pictures in the documentation show the CRC aligned on a 16-bit - boundary before the last odd byte, so that's what we do. */ - if (crc) { - crc = crc32(~0, buf, size); - *(p++) = crc & 0xff; crc >>= 8; - *(p++) = crc & 0xff; crc >>= 8; - *(p++) = crc & 0xff; crc >>= 8; - *(p++) = crc & 0xff; - } - if (size & 1) { - *(p++) = buf[size - 1]; - *p = 0x60; - } else { - *(p++) = 0; - *p = 0x40; - } - /* TODO: Raise early RX interrupt? */ - s->int_level |= INT_RCV; - smc91c111_update(s); - - return size; -} - -static const MemoryRegionOps smc91c111_mem_ops = { - /* The special case for 32 bit writes to 0xc means we can't just - * set .impl.min/max_access_size to 1, unfortunately - */ - .old_mmio = { - .read = { smc91c111_readb, smc91c111_readw, smc91c111_readl, }, - .write = { smc91c111_writeb, smc91c111_writew, smc91c111_writel, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static NetClientInfo net_smc91c111_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .can_receive = smc91c111_can_receive_nc, - .receive = smc91c111_receive, -}; - -static int smc91c111_init1(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - smc91c111_state *s = SMC91C111(dev); - - memory_region_init_io(&s->mmio, OBJECT(s), &smc91c111_mem_ops, s, - "smc91c111-mmio", 16); - sysbus_init_mmio(sbd, &s->mmio); - sysbus_init_irq(sbd, &s->irq); - qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->nic = qemu_new_nic(&net_smc91c111_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - /* ??? Save/restore. */ - return 0; -} - -static Property smc91c111_properties[] = { - DEFINE_NIC_PROPERTIES(smc91c111_state, conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void smc91c111_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = smc91c111_init1; - dc->reset = smc91c111_reset; - dc->vmsd = &vmstate_smc91c111; - dc->props = smc91c111_properties; -} - -static const TypeInfo smc91c111_info = { - .name = TYPE_SMC91C111, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(smc91c111_state), - .class_init = smc91c111_class_init, -}; - -static void smc91c111_register_types(void) -{ - type_register_static(&smc91c111_info); -} - -/* Legacy helper function. Should go away when machine config files are - implemented. */ -void smc91c111_init(NICInfo *nd, uint32_t base, qemu_irq irq) -{ - DeviceState *dev; - SysBusDevice *s; - - qemu_check_nic_model(nd, "smc91c111"); - dev = qdev_create(NULL, TYPE_SMC91C111); - qdev_set_nic_properties(dev, nd); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(s, 0, base); - sysbus_connect_irq(s, 0, irq); -} - -type_init(smc91c111_register_types) diff --git a/qemu/hw/net/spapr_llan.c b/qemu/hw/net/spapr_llan.c deleted file mode 100644 index a647f25d9..000000000 --- a/qemu/hw/net/spapr_llan.c +++ /dev/null @@ -1,820 +0,0 @@ -/* - * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator - * - * PAPR Inter-VM Logical Lan, aka ibmveth - * - * Copyright (c) 2010,2011 David Gibson, IBM Corporation. - * - * 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 "qemu-common.h" -#include "cpu.h" -#include "hw/hw.h" -#include "net/net.h" -#include "hw/qdev.h" -#include "hw/ppc/spapr.h" -#include "hw/ppc/spapr_vio.h" -#include "sysemu/sysemu.h" - -#include - -#define ETH_ALEN 6 -#define MAX_PACKET_SIZE 65536 - -/*#define DEBUG*/ - -#ifdef DEBUG -#define DPRINTF(fmt...) do { fprintf(stderr, fmt); } while (0) -#else -#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 - */ - -typedef uint64_t vlan_bd_t; - -#define VLAN_BD_VALID 0x8000000000000000ULL -#define VLAN_BD_TOGGLE 0x4000000000000000ULL -#define VLAN_BD_NO_CSUM 0x0200000000000000ULL -#define VLAN_BD_CSUM_GOOD 0x0100000000000000ULL -#define VLAN_BD_LEN_MASK 0x00ffffff00000000ULL -#define VLAN_BD_LEN(bd) (((bd) & VLAN_BD_LEN_MASK) >> 32) -#define VLAN_BD_ADDR_MASK 0x00000000ffffffffULL -#define VLAN_BD_ADDR(bd) ((bd) & VLAN_BD_ADDR_MASK) - -#define VLAN_VALID_BD(addr, len) (VLAN_BD_VALID | \ - (((len) << 32) & VLAN_BD_LEN_MASK) | \ - (addr & VLAN_BD_ADDR_MASK)) - -#define VLAN_RXQC_TOGGLE 0x80 -#define VLAN_RXQC_VALID 0x40 -#define VLAN_RXQC_NO_CSUM 0x02 -#define VLAN_RXQC_CSUM_GOOD 0x01 - -#define VLAN_RQ_ALIGNMENT 16 -#define VLAN_RXQ_BD_OFF 0 -#define VLAN_FILTER_BD_OFF 8 -#define VLAN_RX_BDS_OFF 16 -/* - * The final 8 bytes of the buffer list is a counter of frames dropped - * because there was not a buffer in the buffer list capable of holding - * the frame. We must avoid it, or the operating system will report garbage - * for this statistic. - */ -#define VLAN_RX_BDS_LEN (SPAPR_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF - 8) -#define VLAN_MAX_BUFS (VLAN_RX_BDS_LEN / 8) - -#define TYPE_VIO_SPAPR_VLAN_DEVICE "spapr-vlan" -#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; - NICState *nic; - bool isopen; - 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) -{ - VIOsPAPRVLANDevice *dev = qemu_get_nic_opaque(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) -{ - VIOsPAPRVLANDevice *dev = qemu_get_nic_opaque(nc); - 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; - uint64_t handle; - uint8_t control; - - DPRINTF("spapr_vlan_receive() [%s] rx_bufs=%d\n", sdev->qdev.id, - dev->rx_bufs); - - if (!dev->isopen) { - return -1; - } - - if (!dev->rx_bufs) { - return -1; - } - - 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; - } - - dev->rx_bufs--; - - /* Transfer the packet data */ - if (spapr_vio_dma_write(sdev, VLAN_BD_ADDR(bd) + 8, buf, size) < 0) { - return -1; - } - - DPRINTF("spapr_vlan_receive: DMA write completed\n"); - - /* Update the receive queue */ - control = VLAN_RXQC_TOGGLE | VLAN_RXQC_VALID; - if (rxq_bd & VLAN_BD_TOGGLE) { - control ^= VLAN_RXQC_TOGGLE; - } - - handle = vio_ldq(sdev, VLAN_BD_ADDR(bd)); - vio_stq(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 8, handle); - vio_stl(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 4, size); - vio_sth(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr + 2, 8); - vio_stb(sdev, VLAN_BD_ADDR(rxq_bd) + dev->rxq_ptr, control); - - DPRINTF("wrote rxq entry (ptr=0x%llx): 0x%016llx 0x%016llx\n", - (unsigned long long)dev->rxq_ptr, - (unsigned long long)vio_ldq(sdev, VLAN_BD_ADDR(rxq_bd) + - dev->rxq_ptr), - (unsigned long long)vio_ldq(sdev, VLAN_BD_ADDR(rxq_bd) + - dev->rxq_ptr + 8)); - - dev->rxq_ptr += 16; - if (dev->rxq_ptr >= VLAN_BD_LEN(rxq_bd)) { - dev->rxq_ptr = 0; - vio_stq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF, rxq_bd ^ VLAN_BD_TOGGLE); - } - - if (sdev->signal_state & 1) { - qemu_irq_pulse(spapr_vio_qirq(sdev)); - } - - return size; -} - -static NetClientInfo net_spapr_vlan_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .can_receive = spapr_vlan_can_receive, - .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) -{ - VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); - - qemu_macaddr_default_if_unset(&dev->nicconf.macaddr); - - dev->nic = qemu_new_nic(&net_spapr_vlan_info, &dev->nicconf, - object_get_typename(OBJECT(sdev)), sdev->qdev.id, dev); - qemu_format_nic_info_str(qemu_get_queue(dev->nic), dev->nicconf.macaddr.a); -} - -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) -{ - DeviceState *dev; - - dev = qdev_create(&bus->bus, "spapr-vlan"); - - qdev_set_nic_properties(dev, nd); - - qdev_init_nofail(dev); -} - -static int spapr_vlan_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) -{ - VIOsPAPRVLANDevice *vdev = VIO_SPAPR_VLAN_DEVICE(dev); - uint8_t padded_mac[8] = {0, 0}; - int ret; - - /* Some old phyp versions give the mac address in an 8-byte - * property. The kernel driver has an insane workaround for this; - * rather than doing the obvious thing and checking the property - * length, it checks whether the first byte has 0b10 in the low - * bits. If a correct 6-byte property has a different first byte - * the kernel will get the wrong mac address, overrunning its - * buffer in the process (read only, thank goodness). - * - * Here we workaround the kernel workaround by always supplying an - * 8-byte property, with the mac address in the last six bytes */ - memcpy(&padded_mac[2], &vdev->nicconf.macaddr, ETH_ALEN); - ret = fdt_setprop(fdt, node_off, "local-mac-address", - padded_mac, sizeof(padded_mac)); - if (ret < 0) { - return ret; - } - - ret = fdt_setprop_cell(fdt, node_off, "ibm,mac-address-filters", 0); - if (ret < 0) { - return ret; - } - - return 0; -} - -static int check_bd(VIOsPAPRVLANDevice *dev, vlan_bd_t bd, - target_ulong alignment) -{ - if ((VLAN_BD_ADDR(bd) % alignment) - || (VLAN_BD_LEN(bd) % alignment)) { - return -1; - } - - if (!spapr_vio_dma_valid(&dev->sdev, VLAN_BD_ADDR(bd), - VLAN_BD_LEN(bd), DMA_DIRECTION_FROM_DEVICE) - || !spapr_vio_dma_valid(&dev->sdev, VLAN_BD_ADDR(bd), - VLAN_BD_LEN(bd), DMA_DIRECTION_TO_DEVICE)) { - return -1; - } - - return 0; -} - -static target_ulong h_register_logical_lan(PowerPCCPU *cpu, - sPAPRMachineState *spapr, - target_ulong opcode, - target_ulong *args) -{ - target_ulong reg = args[0]; - target_ulong buf_list = args[1]; - target_ulong rec_queue = args[2]; - target_ulong filter_list = args[3]; - VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); - vlan_bd_t filter_list_bd; - - if (!dev) { - return H_PARAMETER; - } - - if (dev->isopen) { - hcall_dprintf("H_REGISTER_LOGICAL_LAN called twice without " - "H_FREE_LOGICAL_LAN\n"); - return H_RESOURCE; - } - - if (check_bd(dev, VLAN_VALID_BD(buf_list, SPAPR_TCE_PAGE_SIZE), - SPAPR_TCE_PAGE_SIZE) < 0) { - hcall_dprintf("Bad buf_list 0x" TARGET_FMT_lx "\n", buf_list); - return H_PARAMETER; - } - - filter_list_bd = VLAN_VALID_BD(filter_list, SPAPR_TCE_PAGE_SIZE); - if (check_bd(dev, filter_list_bd, SPAPR_TCE_PAGE_SIZE) < 0) { - hcall_dprintf("Bad filter_list 0x" TARGET_FMT_lx "\n", filter_list); - return H_PARAMETER; - } - - if (!(rec_queue & VLAN_BD_VALID) - || (check_bd(dev, rec_queue, VLAN_RQ_ALIGNMENT) < 0)) { - hcall_dprintf("Bad receive queue\n"); - return H_PARAMETER; - } - - dev->buf_list = buf_list; - sdev->signal_state = 0; - - rec_queue &= ~VLAN_BD_TOGGLE; - - /* Initialize the buffer list */ - vio_stq(sdev, buf_list, rec_queue); - vio_stq(sdev, buf_list + 8, filter_list_bd); - spapr_vio_dma_set(sdev, buf_list + VLAN_RX_BDS_OFF, 0, - SPAPR_TCE_PAGE_SIZE - VLAN_RX_BDS_OFF); - dev->add_buf_ptr = VLAN_RX_BDS_OFF - 8; - dev->use_buf_ptr = VLAN_RX_BDS_OFF - 8; - dev->rx_bufs = 0; - dev->rxq_ptr = 0; - - /* Initialize the receive queue */ - spapr_vio_dma_set(sdev, VLAN_BD_ADDR(rec_queue), 0, VLAN_BD_LEN(rec_queue)); - - dev->isopen = 1; - qemu_flush_queued_packets(qemu_get_queue(dev->nic)); - - return H_SUCCESS; -} - - -static target_ulong h_free_logical_lan(PowerPCCPU *cpu, - sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong reg = args[0]; - VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); - - if (!dev) { - return H_PARAMETER; - } - - if (!dev->isopen) { - hcall_dprintf("H_FREE_LOGICAL_LAN called without " - "H_REGISTER_LOGICAL_LAN\n"); - return H_RESOURCE; - } - - spapr_vlan_reset(sdev); - 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, - target_ulong *args) -{ - target_ulong reg = args[0]; - target_ulong buf = args[1]; - VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); - target_long ret; - - DPRINTF("H_ADD_LOGICAL_LAN_BUFFER(0x" TARGET_FMT_lx - ", 0x" TARGET_FMT_lx ")\n", reg, buf); - - if (!sdev) { - hcall_dprintf("Bad device\n"); - return H_PARAMETER; - } - - if ((check_bd(dev, buf, 4) < 0) - || (VLAN_BD_LEN(buf) < 16)) { - hcall_dprintf("Bad buffer enqueued\n"); - return H_PARAMETER; - } - - if (!dev->isopen) { - return H_RESOURCE; - } - - 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)); - - return H_SUCCESS; -} - -static target_ulong h_send_logical_lan(PowerPCCPU *cpu, - sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong reg = args[0]; - target_ulong *bufs = args + 1; - target_ulong continue_token = args[7]; - VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); - unsigned total_len; - uint8_t *lbuf, *p; - int i, nbufs; - int ret; - - DPRINTF("H_SEND_LOGICAL_LAN(0x" TARGET_FMT_lx ", , 0x" - TARGET_FMT_lx ")\n", reg, continue_token); - - if (!sdev) { - return H_PARAMETER; - } - - DPRINTF("rxbufs = %d\n", dev->rx_bufs); - - if (!dev->isopen) { - return H_DROPPED; - } - - if (continue_token) { - return H_HARDWARE; /* FIXME actually handle this */ - } - - total_len = 0; - for (i = 0; i < 6; i++) { - DPRINTF(" buf desc: 0x" TARGET_FMT_lx "\n", bufs[i]); - if (!(bufs[i] & VLAN_BD_VALID)) { - break; - } - total_len += VLAN_BD_LEN(bufs[i]); - } - - nbufs = i; - DPRINTF("h_send_logical_lan() %d buffers, total length 0x%x\n", - nbufs, total_len); - - if (total_len == 0) { - return H_SUCCESS; - } - - if (total_len > MAX_PACKET_SIZE) { - /* Don't let the guest force too large an allocation */ - return H_RESOURCE; - } - - lbuf = alloca(total_len); - p = lbuf; - for (i = 0; i < nbufs; i++) { - ret = spapr_vio_dma_read(sdev, VLAN_BD_ADDR(bufs[i]), - p, VLAN_BD_LEN(bufs[i])); - if (ret < 0) { - return ret; - } - - p += VLAN_BD_LEN(bufs[i]); - } - - qemu_send_packet(qemu_get_queue(dev->nic), lbuf, total_len); - - return H_SUCCESS; -} - -static target_ulong h_multicast_ctrl(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong reg = args[0]; - VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - - if (!dev) { - return H_PARAMETER; - } - - return H_SUCCESS; -} - -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, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_SPAPR_VIO(sdev, VIOsPAPRVLANDevice), - /* LLAN state */ - VMSTATE_BOOL(isopen, VIOsPAPRVLANDevice), - VMSTATE_UINTTL(buf_list, VIOsPAPRVLANDevice), - VMSTATE_UINT32(add_buf_ptr, VIOsPAPRVLANDevice), - VMSTATE_UINT32(use_buf_ptr, VIOsPAPRVLANDevice), - VMSTATE_UINT32(rx_bufs, VIOsPAPRVLANDevice), - VMSTATE_UINTTL(rxq_ptr, VIOsPAPRVLANDevice), - - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription * []) { - &vmstate_rx_pools, - NULL - } -}; - -static void spapr_vlan_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); - - k->realize = spapr_vlan_realize; - k->reset = spapr_vlan_reset; - k->devnode = spapr_vlan_devnode; - k->dt_name = "l-lan"; - k->dt_type = "network"; - k->dt_compatible = "IBM,l-lan"; - k->signal_mask = 0x1; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->props = spapr_vlan_properties; - k->rtce_window_size = 0x10000000; - dc->vmsd = &vmstate_spapr_llan; -} - -static const TypeInfo spapr_vlan_info = { - .name = TYPE_VIO_SPAPR_VLAN_DEVICE, - .parent = TYPE_VIO_SPAPR_DEVICE, - .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) -{ - spapr_register_hypercall(H_REGISTER_LOGICAL_LAN, h_register_logical_lan); - spapr_register_hypercall(H_FREE_LOGICAL_LAN, h_free_logical_lan); - spapr_register_hypercall(H_SEND_LOGICAL_LAN, h_send_logical_lan); - spapr_register_hypercall(H_ADD_LOGICAL_LAN_BUFFER, - h_add_logical_lan_buffer); - spapr_register_hypercall(H_MULTICAST_CTRL, h_multicast_ctrl); - type_register_static(&spapr_vlan_info); -} - -type_init(spapr_vlan_register_types) diff --git a/qemu/hw/net/stellaris_enet.c b/qemu/hw/net/stellaris_enet.c deleted file mode 100644 index 688089494..000000000 --- a/qemu/hw/net/stellaris_enet.c +++ /dev/null @@ -1,514 +0,0 @@ -/* - * Luminary Micro Stellaris Ethernet Controller - * - * Copyright (c) 2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "net/net.h" -#include - -//#define DEBUG_STELLARIS_ENET 1 - -#ifdef DEBUG_STELLARIS_ENET -#define DPRINTF(fmt, ...) \ -do { printf("stellaris_enet: " fmt , ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "stellaris_enet: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "stellaris_enet: error: " fmt , ## __VA_ARGS__);} while (0) -#endif - -#define SE_INT_RX 0x01 -#define SE_INT_TXER 0x02 -#define SE_INT_TXEMP 0x04 -#define SE_INT_FOV 0x08 -#define SE_INT_RXER 0x10 -#define SE_INT_MD 0x20 -#define SE_INT_PHY 0x40 - -#define SE_RCTL_RXEN 0x01 -#define SE_RCTL_AMUL 0x02 -#define SE_RCTL_PRMS 0x04 -#define SE_RCTL_BADCRC 0x08 -#define SE_RCTL_RSTFIFO 0x10 - -#define SE_TCTL_TXEN 0x01 -#define SE_TCTL_PADEN 0x02 -#define SE_TCTL_CRC 0x04 -#define SE_TCTL_DUPLEX 0x08 - -#define TYPE_STELLARIS_ENET "stellaris_enet" -#define STELLARIS_ENET(obj) \ - OBJECT_CHECK(stellaris_enet_state, (obj), TYPE_STELLARIS_ENET) - -typedef struct { - uint8_t data[2048]; - uint32_t len; -} StellarisEnetRxFrame; - -typedef struct { - SysBusDevice parent_obj; - - uint32_t ris; - uint32_t im; - uint32_t rctl; - uint32_t tctl; - uint32_t thr; - uint32_t mctl; - uint32_t mdv; - uint32_t mtxd; - uint32_t mrxd; - uint32_t np; - uint32_t tx_fifo_len; - uint8_t tx_fifo[2048]; - /* Real hardware has a 2k fifo, which works out to be at most 31 packets. - We implement a full 31 packet fifo. */ - StellarisEnetRxFrame rx[31]; - uint32_t rx_fifo_offset; - uint32_t next_packet; - NICState *nic; - NICConf conf; - qemu_irq irq; - MemoryRegion mmio; -} stellaris_enet_state; - -static const VMStateDescription vmstate_rx_frame = { - .name = "stellaris_enet/rx_frame", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8_ARRAY(data, StellarisEnetRxFrame, 2048), - VMSTATE_UINT32(len, StellarisEnetRxFrame), - VMSTATE_END_OF_LIST() - } -}; - -static int stellaris_enet_post_load(void *opaque, int version_id) -{ - stellaris_enet_state *s = opaque; - int i; - - /* Sanitize inbound state. Note that next_packet is an index but - * np is a size; hence their valid upper bounds differ. - */ - if (s->next_packet >= ARRAY_SIZE(s->rx)) { - return -1; - } - - if (s->np > ARRAY_SIZE(s->rx)) { - return -1; - } - - for (i = 0; i < ARRAY_SIZE(s->rx); i++) { - if (s->rx[i].len > ARRAY_SIZE(s->rx[i].data)) { - return -1; - } - } - - if (s->rx_fifo_offset > ARRAY_SIZE(s->rx[0].data) - 4) { - return -1; - } - - if (s->tx_fifo_len > ARRAY_SIZE(s->tx_fifo)) { - return -1; - } - - return 0; -} - -static const VMStateDescription vmstate_stellaris_enet = { - .name = "stellaris_enet", - .version_id = 2, - .minimum_version_id = 2, - .post_load = stellaris_enet_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(ris, stellaris_enet_state), - VMSTATE_UINT32(im, stellaris_enet_state), - VMSTATE_UINT32(rctl, stellaris_enet_state), - VMSTATE_UINT32(tctl, stellaris_enet_state), - VMSTATE_UINT32(thr, stellaris_enet_state), - VMSTATE_UINT32(mctl, stellaris_enet_state), - VMSTATE_UINT32(mdv, stellaris_enet_state), - VMSTATE_UINT32(mtxd, stellaris_enet_state), - VMSTATE_UINT32(mrxd, stellaris_enet_state), - VMSTATE_UINT32(np, stellaris_enet_state), - VMSTATE_UINT32(tx_fifo_len, stellaris_enet_state), - VMSTATE_UINT8_ARRAY(tx_fifo, stellaris_enet_state, 2048), - VMSTATE_STRUCT_ARRAY(rx, stellaris_enet_state, 31, 1, - vmstate_rx_frame, StellarisEnetRxFrame), - VMSTATE_UINT32(rx_fifo_offset, stellaris_enet_state), - VMSTATE_UINT32(next_packet, stellaris_enet_state), - VMSTATE_END_OF_LIST() - } -}; - -static void stellaris_enet_update(stellaris_enet_state *s) -{ - qemu_set_irq(s->irq, (s->ris & s->im) != 0); -} - -/* Return the data length of the packet currently being assembled - * in the TX fifo. - */ -static inline int stellaris_txpacket_datalen(stellaris_enet_state *s) -{ - return s->tx_fifo[0] | (s->tx_fifo[1] << 8); -} - -/* Return true if the packet currently in the TX FIFO is complete, -* ie the FIFO holds enough bytes for the data length, ethernet header, -* payload and optionally CRC. -*/ -static inline bool stellaris_txpacket_complete(stellaris_enet_state *s) -{ - int framelen = stellaris_txpacket_datalen(s); - framelen += 16; - if (!(s->tctl & SE_TCTL_CRC)) { - framelen += 4; - } - /* Cover the corner case of a 2032 byte payload with auto-CRC disabled: - * this requires more bytes than will fit in the FIFO. It's not totally - * clear how the h/w handles this, but if using threshold-based TX - * it will definitely try to transmit something. - */ - framelen = MIN(framelen, ARRAY_SIZE(s->tx_fifo)); - return s->tx_fifo_len >= framelen; -} - -/* Return true if the TX FIFO threshold is enabled and the FIFO - * has filled enough to reach it. - */ -static inline bool stellaris_tx_thr_reached(stellaris_enet_state *s) -{ - return (s->thr < 0x3f && - (s->tx_fifo_len >= 4 * (s->thr * 8 + 1))); -} - -/* Send the packet currently in the TX FIFO */ -static void stellaris_enet_send(stellaris_enet_state *s) -{ - int framelen = stellaris_txpacket_datalen(s); - - /* Ethernet header is in the FIFO but not in the datacount. - * We don't implement explicit CRC, so just ignore any - * CRC value in the FIFO. - */ - framelen += 14; - if ((s->tctl & SE_TCTL_PADEN) && framelen < 60) { - memset(&s->tx_fifo[framelen + 2], 0, 60 - framelen); - framelen = 60; - } - /* This MIN will have no effect unless the FIFO data is corrupt - * (eg bad data from an incoming migration); otherwise the check - * on the datalen at the start of writing the data into the FIFO - * will have caught this. Silently write a corrupt half-packet, - * which is what the hardware does in FIFO underrun situations. - */ - framelen = MIN(framelen, ARRAY_SIZE(s->tx_fifo) - 2); - qemu_send_packet(qemu_get_queue(s->nic), s->tx_fifo + 2, framelen); - s->tx_fifo_len = 0; - s->ris |= SE_INT_TXEMP; - stellaris_enet_update(s); - DPRINTF("Done TX\n"); -} - -/* TODO: Implement MAC address filtering. */ -static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, size_t size) -{ - stellaris_enet_state *s = qemu_get_nic_opaque(nc); - int n; - uint8_t *p; - uint32_t crc; - - if ((s->rctl & SE_RCTL_RXEN) == 0) - return -1; - if (s->np >= 31) { - return 0; - } - - DPRINTF("Received packet len=%zu\n", size); - n = s->next_packet + s->np; - if (n >= 31) - n -= 31; - - 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); - *(p++) = (size + 6) >> 8; - memcpy (p, buf, size); - p += size; - crc = crc32(~0, buf, size); - *(p++) = crc; - *(p++) = crc >> 8; - *(p++) = crc >> 16; - *(p++) = crc >> 24; - /* Clear the remaining bytes in the last word. */ - if ((size & 3) != 2) { - memset(p, 0, (6 - size) & 3); - } - - s->ris |= SE_INT_RX; - stellaris_enet_update(s); - - return size; -} - -static int stellaris_enet_can_receive(stellaris_enet_state *s) -{ - return (s->np < 31); -} - -static uint64_t stellaris_enet_read(void *opaque, hwaddr offset, - unsigned size) -{ - stellaris_enet_state *s = (stellaris_enet_state *)opaque; - uint32_t val; - - switch (offset) { - case 0x00: /* RIS */ - DPRINTF("IRQ status %02x\n", s->ris); - return s->ris; - case 0x04: /* IM */ - return s->im; - case 0x08: /* RCTL */ - return s->rctl; - case 0x0c: /* TCTL */ - return s->tctl; - case 0x10: /* DATA */ - { - uint8_t *rx_fifo; - - if (s->np == 0) { - BADF("RX underflow\n"); - return 0; - } - - rx_fifo = s->rx[s->next_packet].data + s->rx_fifo_offset; - - val = rx_fifo[0] | (rx_fifo[1] << 8) | (rx_fifo[2] << 16) - | (rx_fifo[3] << 24); - s->rx_fifo_offset += 4; - if (s->rx_fifo_offset >= s->rx[s->next_packet].len) { - s->rx_fifo_offset = 0; - s->next_packet++; - if (s->next_packet >= 31) - s->next_packet = 0; - s->np--; - DPRINTF("RX done np=%d\n", s->np); - if (!s->np && stellaris_enet_can_receive(s)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } - } - return val; - } - case 0x14: /* IA0 */ - return s->conf.macaddr.a[0] | (s->conf.macaddr.a[1] << 8) - | (s->conf.macaddr.a[2] << 16) - | ((uint32_t)s->conf.macaddr.a[3] << 24); - case 0x18: /* IA1 */ - return s->conf.macaddr.a[4] | (s->conf.macaddr.a[5] << 8); - case 0x1c: /* THR */ - return s->thr; - case 0x20: /* MCTL */ - return s->mctl; - case 0x24: /* MDV */ - return s->mdv; - case 0x28: /* MADD */ - return 0; - case 0x2c: /* MTXD */ - return s->mtxd; - case 0x30: /* MRXD */ - return s->mrxd; - case 0x34: /* NP */ - return s->np; - case 0x38: /* TR */ - return 0; - case 0x3c: /* Undocuented: Timestamp? */ - return 0; - default: - hw_error("stellaris_enet_read: Bad offset %x\n", (int)offset); - return 0; - } -} - -static void stellaris_enet_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - stellaris_enet_state *s = (stellaris_enet_state *)opaque; - - switch (offset) { - case 0x00: /* IACK */ - s->ris &= ~value; - DPRINTF("IRQ ack %02" PRIx64 "/%02x\n", value, s->ris); - stellaris_enet_update(s); - /* Clearing TXER also resets the TX fifo. */ - if (value & SE_INT_TXER) { - s->tx_fifo_len = 0; - } - break; - case 0x04: /* IM */ - DPRINTF("IRQ mask %02" PRIx64 "/%02x\n", value, s->ris); - s->im = value; - stellaris_enet_update(s); - break; - case 0x08: /* RCTL */ - s->rctl = value; - if (value & SE_RCTL_RSTFIFO) { - s->np = 0; - s->rx_fifo_offset = 0; - stellaris_enet_update(s); - } - break; - case 0x0c: /* TCTL */ - s->tctl = value; - break; - case 0x10: /* DATA */ - if (s->tx_fifo_len == 0) { - /* The first word is special, it contains the data length */ - int framelen = value & 0xffff; - if (framelen > 2032) { - DPRINTF("TX frame too long (%d)\n", framelen); - s->ris |= SE_INT_TXER; - stellaris_enet_update(s); - break; - } - } - - if (s->tx_fifo_len + 4 <= ARRAY_SIZE(s->tx_fifo)) { - s->tx_fifo[s->tx_fifo_len++] = value; - s->tx_fifo[s->tx_fifo_len++] = value >> 8; - s->tx_fifo[s->tx_fifo_len++] = value >> 16; - s->tx_fifo[s->tx_fifo_len++] = value >> 24; - } - - if (stellaris_tx_thr_reached(s) && stellaris_txpacket_complete(s)) { - stellaris_enet_send(s); - } - break; - case 0x14: /* IA0 */ - s->conf.macaddr.a[0] = value; - s->conf.macaddr.a[1] = value >> 8; - s->conf.macaddr.a[2] = value >> 16; - s->conf.macaddr.a[3] = value >> 24; - break; - case 0x18: /* IA1 */ - s->conf.macaddr.a[4] = value; - s->conf.macaddr.a[5] = value >> 8; - break; - case 0x1c: /* THR */ - s->thr = value; - break; - case 0x20: /* MCTL */ - s->mctl = value; - break; - case 0x24: /* MDV */ - s->mdv = value; - break; - case 0x28: /* MADD */ - /* ignored. */ - break; - case 0x2c: /* MTXD */ - s->mtxd = value & 0xff; - break; - case 0x38: /* TR */ - if (value & 1) { - stellaris_enet_send(s); - } - break; - case 0x30: /* MRXD */ - case 0x34: /* NP */ - /* Ignored. */ - case 0x3c: /* Undocuented: Timestamp? */ - /* Ignored. */ - break; - default: - hw_error("stellaris_enet_write: Bad offset %x\n", (int)offset); - } -} - -static const MemoryRegionOps stellaris_enet_ops = { - .read = stellaris_enet_read, - .write = stellaris_enet_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void stellaris_enet_reset(stellaris_enet_state *s) -{ - s->mdv = 0x80; - s->rctl = SE_RCTL_BADCRC; - s->im = SE_INT_PHY | SE_INT_MD | SE_INT_RXER | SE_INT_FOV | SE_INT_TXEMP - | SE_INT_TXER | SE_INT_RX; - s->thr = 0x3f; - s->tx_fifo_len = 0; -} - -static NetClientInfo net_stellaris_enet_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = stellaris_enet_receive, -}; - -static int stellaris_enet_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - stellaris_enet_state *s = STELLARIS_ENET(dev); - - memory_region_init_io(&s->mmio, OBJECT(s), &stellaris_enet_ops, s, - "stellaris_enet", 0x1000); - sysbus_init_mmio(sbd, &s->mmio); - sysbus_init_irq(sbd, &s->irq); - qemu_macaddr_default_if_unset(&s->conf.macaddr); - - s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - stellaris_enet_reset(s); - return 0; -} - -static Property stellaris_enet_properties[] = { - DEFINE_NIC_PROPERTIES(stellaris_enet_state, conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void stellaris_enet_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = stellaris_enet_init; - dc->props = stellaris_enet_properties; - dc->vmsd = &vmstate_stellaris_enet; -} - -static const TypeInfo stellaris_enet_info = { - .name = TYPE_STELLARIS_ENET, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(stellaris_enet_state), - .class_init = stellaris_enet_class_init, -}; - -static void stellaris_enet_register_types(void) -{ - type_register_static(&stellaris_enet_info); -} - -type_init(stellaris_enet_register_types) diff --git a/qemu/hw/net/vhost_net.c b/qemu/hw/net/vhost_net.c deleted file mode 100644 index 6e1032fc1..000000000 --- a/qemu/hw/net/vhost_net.c +++ /dev/null @@ -1,473 +0,0 @@ -/* - * vhost-net support - * - * Copyright Red Hat, Inc. 2010 - * - * Authors: - * Michael S. Tsirkin - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * 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 "net/net.h" -#include "net/tap.h" -#include "net/vhost-user.h" - -#include "hw/virtio/virtio-net.h" -#include "net/vhost_net.h" -#include "qemu/error-report.h" - - -#ifdef CONFIG_VHOST_NET -#include -#include -#include -#include -#include -#include -#include - - -#include "standard-headers/linux/virtio_ring.h" -#include "hw/virtio/vhost.h" -#include "hw/virtio/virtio-bus.h" - -struct vhost_net { - struct vhost_dev dev; - struct vhost_virtqueue vqs[2]; - int backend; - NetClientState *nc; -}; - -/* Features supported by host kernel. */ -static const int kernel_feature_bits[] = { - VIRTIO_F_NOTIFY_ON_EMPTY, - VIRTIO_RING_F_INDIRECT_DESC, - VIRTIO_RING_F_EVENT_IDX, - VIRTIO_NET_F_MRG_RXBUF, - VIRTIO_F_VERSION_1, - VHOST_INVALID_FEATURE_BIT -}; - -/* Features supported by others. */ -static const int user_feature_bits[] = { - VIRTIO_F_NOTIFY_ON_EMPTY, - VIRTIO_RING_F_INDIRECT_DESC, - VIRTIO_RING_F_EVENT_IDX, - - VIRTIO_F_ANY_LAYOUT, - VIRTIO_F_VERSION_1, - VIRTIO_NET_F_CSUM, - VIRTIO_NET_F_GUEST_CSUM, - VIRTIO_NET_F_GSO, - VIRTIO_NET_F_GUEST_TSO4, - VIRTIO_NET_F_GUEST_TSO6, - VIRTIO_NET_F_GUEST_ECN, - VIRTIO_NET_F_GUEST_UFO, - VIRTIO_NET_F_HOST_TSO4, - VIRTIO_NET_F_HOST_TSO6, - VIRTIO_NET_F_HOST_ECN, - VIRTIO_NET_F_HOST_UFO, - VIRTIO_NET_F_MRG_RXBUF, - - /* This bit implies RARP isn't sent by QEMU out of band */ - VIRTIO_NET_F_GUEST_ANNOUNCE, - - VIRTIO_NET_F_MQ, - - VHOST_INVALID_FEATURE_BIT -}; - -static const int *vhost_net_get_feature_bits(struct vhost_net *net) -{ - const int *feature_bits = 0; - - switch (net->nc->info->type) { - case NET_CLIENT_OPTIONS_KIND_TAP: - feature_bits = kernel_feature_bits; - break; - case NET_CLIENT_OPTIONS_KIND_VHOST_USER: - feature_bits = user_feature_bits; - break; - default: - error_report("Feature bits not defined for this type: %d", - net->nc->info->type); - break; - } - - return feature_bits; -} - -uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features) -{ - return vhost_get_features(&net->dev, vhost_net_get_feature_bits(net), - features); -} - -void vhost_net_ack_features(struct vhost_net *net, uint64_t features) -{ - net->dev.acked_features = net->dev.backend_features; - vhost_ack_features(&net->dev, vhost_net_get_feature_bits(net), features); -} - -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) { - case NET_CLIENT_OPTIONS_KIND_TAP: - return tap_get_fd(backend); - default: - fprintf(stderr, "vhost-net requires tap backend\n"); - return -EBADFD; - } -} - -struct vhost_net *vhost_net_init(VhostNetOptions *options) -{ - int r; - bool backend_kernel = options->backend_type == VHOST_BACKEND_TYPE_KERNEL; - struct vhost_net *net = g_malloc(sizeof *net); - - if (!options->net_backend) { - 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); - if (r < 0) { - goto fail; - } - 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; - - /* 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); - if (r < 0) { - goto fail; - } - if (backend_kernel) { - if (!qemu_has_vnet_hdr_len(options->net_backend, - sizeof(struct virtio_net_hdr_mrg_rxbuf))) { - net->dev.features &= ~(1ULL << VIRTIO_NET_F_MRG_RXBUF); - } - if (~net->dev.features & net->dev.backend_features) { - fprintf(stderr, "vhost lacks feature mask %" PRIu64 - " for backend\n", - (uint64_t)(~net->dev.features & net->dev.backend_features)); - vhost_dev_cleanup(&net->dev); - goto fail; - } - } - /* Set sane init value. Override when guest acks. */ - vhost_net_ack_features(net, 0); - return net; -fail: - g_free(net); - return NULL; -} - -static void vhost_net_set_vq_index(struct vhost_net *net, int vq_index) -{ - net->dev.vq_index = vq_index; -} - -static int vhost_net_start_one(struct vhost_net *net, - VirtIODevice *dev) -{ - struct vhost_vring_file file = { }; - int r; - - net->dev.nvqs = 2; - net->dev.vqs = net->vqs; - - r = vhost_dev_enable_notifiers(&net->dev, dev); - if (r < 0) { - goto fail_notifiers; - } - - r = vhost_dev_start(&net->dev, dev); - if (r < 0) { - goto fail_start; - } - - if (net->nc->info->poll) { - net->nc->info->poll(net->nc, false); - } - - if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) { - qemu_set_fd_handler(net->backend, NULL, NULL, NULL); - 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_net_set_backend(&net->dev, &file); - if (r < 0) { - r = -errno; - goto fail; - } - } - } - return 0; -fail: - file.fd = -1; - 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_net_set_backend(&net->dev, &file); - assert(r >= 0); - } - } - if (net->nc->info->poll) { - net->nc->info->poll(net->nc, true); - } - vhost_dev_stop(&net->dev, dev); -fail_start: - vhost_dev_disable_notifiers(&net->dev, dev); -fail_notifiers: - return r; -} - -static void vhost_net_stop_one(struct vhost_net *net, - VirtIODevice *dev) -{ - struct vhost_vring_file file = { .fd = -1 }; - - 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_net_set_backend(&net->dev, &file); - assert(r >= 0); - } - } - if (net->nc->info->poll) { - net->nc->info->poll(net->nc, true); - } - vhost_dev_stop(&net->dev, dev); - vhost_dev_disable_notifiers(&net->dev, dev); -} - -int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, - int total_queues) -{ - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev))); - VirtioBusState *vbus = VIRTIO_BUS(qbus); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); - int r, e, i; - - if (!k->set_guest_notifiers) { - error_report("binding does not support guest notifiers"); - return -ENOSYS; - } - - for (i = 0; i < total_queues; i++) { - 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; - } - - for (i = 0; i < total_queues; i++) { - r = vhost_net_start_one(get_vhost_net(ncs[i].peer), dev); - - if (r < 0) { - goto err_start; - } - } - - return 0; - -err_start: - while (--i >= 0) { - vhost_net_stop_one(get_vhost_net(ncs[i].peer), dev); - } - e = k->set_guest_notifiers(qbus->parent, total_queues * 2, false); - if (e < 0) { - fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", e); - fflush(stderr); - } -err: - return r; -} - -void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, - int total_queues) -{ - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev))); - VirtioBusState *vbus = VIRTIO_BUS(qbus); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); - int i, r; - - for (i = 0; i < total_queues; i++) { - vhost_net_stop_one(get_vhost_net(ncs[i].peer), dev); - } - - r = k->set_guest_notifiers(qbus->parent, total_queues * 2, false); - if (r < 0) { - fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r); - fflush(stderr); - } - assert(r >= 0); -} - -void vhost_net_cleanup(struct vhost_net *net) -{ - vhost_dev_cleanup(&net->dev); - 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); -} - -void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, - int idx, bool mask) -{ - vhost_virtqueue_mask(&net->dev, dev, idx, mask); -} - -VHostNetState *get_vhost_net(NetClientState *nc) -{ - VHostNetState *vhost_net = 0; - - if (!nc) { - return 0; - } - - switch (nc->info->type) { - case NET_CLIENT_OPTIONS_KIND_TAP: - vhost_net = tap_get_vhost_net(nc); - break; - case NET_CLIENT_OPTIONS_KIND_VHOST_USER: - vhost_net = vhost_user_get_vhost_net(nc); - break; - default: - break; - } - - 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"); - return NULL; -} - -int vhost_net_start(VirtIODevice *dev, - NetClientState *ncs, - int total_queues) -{ - return -ENOSYS; -} -void vhost_net_stop(VirtIODevice *dev, - NetClientState *ncs, - int total_queues) -{ -} - -void vhost_net_cleanup(struct vhost_net *net) -{ -} - -uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features) -{ - return features; -} -void vhost_net_ack_features(struct vhost_net *net, uint64_t features) -{ -} - -bool vhost_net_virtqueue_pending(VHostNetState *net, int idx) -{ - return false; -} - -void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, - int idx, bool mask) -{ -} - -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 deleted file mode 100644 index 5798f87d8..000000000 --- a/qemu/hw/net/virtio-net.c +++ /dev/null @@ -1,1950 +0,0 @@ -/* - * Virtio Network Device - * - * Copyright IBM, Corp. 2007 - * - * Authors: - * Anthony Liguori - * - * 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/iov.h" -#include "hw/virtio/virtio.h" -#include "net/net.h" -#include "net/checksum.h" -#include "net/tap.h" -#include "qemu/error-report.h" -#include "qemu/timer.h" -#include "hw/virtio/virtio-net.h" -#include "net/vhost_net.h" -#include "hw/virtio/virtio-bus.h" -#include "qapi/qmp/qjson.h" -#include "qapi-event.h" -#include "hw/virtio/virtio-access.h" - -#define VIRTIO_NET_VM_VERSION 11 - -#define MAC_TABLE_ENTRIES 64 -#define MAX_VLAN (1 << 12) /* Per 802.1Q definition */ - -/* - * Calculate the number of bytes up to and including the given 'field' of - * 'container'. - */ -#define endof(container, field) \ - (offsetof(container, field) + sizeof(((container *)0)->field)) - -typedef struct VirtIOFeature { - uint32_t flags; - size_t end; -} VirtIOFeature; - -static VirtIOFeature feature_sizes[] = { - {.flags = 1 << VIRTIO_NET_F_MAC, - .end = endof(struct virtio_net_config, mac)}, - {.flags = 1 << VIRTIO_NET_F_STATUS, - .end = endof(struct virtio_net_config, status)}, - {.flags = 1 << VIRTIO_NET_F_MQ, - .end = endof(struct virtio_net_config, max_virtqueue_pairs)}, - {} -}; - -static VirtIONetQueue *virtio_net_get_subqueue(NetClientState *nc) -{ - VirtIONet *n = qemu_get_nic_opaque(nc); - - return &n->vqs[nc->queue_index]; -} - -static int vq2q(int queue_index) -{ - return queue_index / 2; -} - -/* TODO - * - we could suppress RX interrupt if we were so inclined. - */ - -static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) -{ - VirtIONet *n = VIRTIO_NET(vdev); - struct virtio_net_config netcfg; - - virtio_stw_p(vdev, &netcfg.status, n->status); - virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues); - memcpy(netcfg.mac, n->mac, ETH_ALEN); - memcpy(config, &netcfg, n->config_size); -} - -static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) -{ - VirtIONet *n = VIRTIO_NET(vdev); - struct virtio_net_config netcfg = {}; - - memcpy(&netcfg, config, n->config_size); - - 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); - } -} - -static bool virtio_net_started(VirtIONet *n, uint8_t status) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(n); - return (status & VIRTIO_CONFIG_S_DRIVER_OK) && - (n->status & VIRTIO_NET_S_LINK_UP) && vdev->vm_running; -} - -static void virtio_net_announce_timer(void *opaque) -{ - VirtIONet *n = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(n); - - n->announce_counter--; - n->status |= VIRTIO_NET_S_ANNOUNCE; - virtio_notify_config(vdev); -} - -static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(n); - NetClientState *nc = qemu_get_queue(n->nic); - int queues = n->multiqueue ? n->max_queues : 1; - - if (!get_vhost_net(nc->peer)) { - return; - } - - if ((virtio_net_started(n, status) && !nc->peer->link_down) == - !!n->vhost_started) { - return; - } - 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. - */ - for (i = 0; i < queues; i++) { - NetClientState *qnc = qemu_get_subqueue(n->nic, i); - - /* Purge both directions: TX and RX. */ - qemu_net_queue_purge(qnc->peer->incoming_queue, qnc); - qemu_net_queue_purge(qnc->incoming_queue, qnc->peer); - } - - n->vhost_started = 1; - r = vhost_net_start(vdev, n->nic->ncs, queues); - if (r < 0) { - error_report("unable to start vhost net: %d: " - "falling back on userspace virtio", -r); - n->vhost_started = 0; - } - } else { - vhost_net_stop(vdev, n->nic->ncs, queues); - n->vhost_started = 0; - } -} - -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); - VirtIONetQueue *q; - 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++) { - NetClientState *ncs = qemu_get_subqueue(n->nic, i); - bool queue_started; - q = &n->vqs[i]; - - if ((!n->multiqueue && i != 0) || i >= n->curr_queues) { - queue_status = 0; - } else { - queue_status = status; - } - queue_started = - virtio_net_started(n, queue_status) && !n->vhost_started; - - if (queue_started) { - qemu_flush_queued_packets(ncs); - } - - if (!q->tx_waiting) { - continue; - } - - if (queue_started) { - if (q->tx_timer) { - timer_mod(q->tx_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout); - } else { - qemu_bh_schedule(q->tx_bh); - } - } else { - if (q->tx_timer) { - timer_del(q->tx_timer); - } else { - qemu_bh_cancel(q->tx_bh); - } - } - } -} - -static void virtio_net_set_link_status(NetClientState *nc) -{ - VirtIONet *n = qemu_get_nic_opaque(nc); - VirtIODevice *vdev = VIRTIO_DEVICE(n); - uint16_t old_status = n->status; - - if (nc->link_down) - n->status &= ~VIRTIO_NET_S_LINK_UP; - else - n->status |= VIRTIO_NET_S_LINK_UP; - - if (n->status != old_status) - virtio_notify_config(vdev); - - virtio_net_set_status(vdev, vdev->status); -} - -static void rxfilter_notify(NetClientState *nc) -{ - VirtIONet *n = qemu_get_nic_opaque(nc); - - if (nc->rxfilter_notify_enabled) { - gchar *path = object_get_canonical_path(OBJECT(n->qdev)); - qapi_event_send_nic_rx_filter_changed(!!n->netclient_name, - n->netclient_name, path, &error_abort); - g_free(path); - - /* disable event notification to avoid events flooding */ - nc->rxfilter_notify_enabled = 0; - } -} - -static intList *get_vlan_table(VirtIONet *n) -{ - intList *list, *entry; - int i, j; - - list = NULL; - for (i = 0; i < MAX_VLAN >> 5; i++) { - for (j = 0; n->vlans[i] && j <= 0x1f; j++) { - if (n->vlans[i] & (1U << j)) { - entry = g_malloc0(sizeof(*entry)); - entry->value = (i << 5) + j; - entry->next = list; - list = entry; - } - } - } - - return list; -} - -static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc) -{ - VirtIONet *n = qemu_get_nic_opaque(nc); - VirtIODevice *vdev = VIRTIO_DEVICE(n); - RxFilterInfo *info; - strList *str_list, *entry; - int i; - - info = g_malloc0(sizeof(*info)); - info->name = g_strdup(nc->name); - info->promiscuous = n->promisc; - - if (n->nouni) { - info->unicast = RX_STATE_NONE; - } else if (n->alluni) { - info->unicast = RX_STATE_ALL; - } else { - info->unicast = RX_STATE_NORMAL; - } - - if (n->nomulti) { - info->multicast = RX_STATE_NONE; - } else if (n->allmulti) { - info->multicast = RX_STATE_ALL; - } else { - info->multicast = RX_STATE_NORMAL; - } - - info->broadcast_allowed = n->nobcast; - info->multicast_overflow = n->mac_table.multi_overflow; - info->unicast_overflow = n->mac_table.uni_overflow; - - info->main_mac = qemu_mac_strdup_printf(n->mac); - - str_list = NULL; - for (i = 0; i < n->mac_table.first_multi; i++) { - entry = g_malloc0(sizeof(*entry)); - entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN); - entry->next = str_list; - str_list = entry; - } - info->unicast_table = str_list; - - str_list = NULL; - for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) { - entry = g_malloc0(sizeof(*entry)); - entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN); - entry->next = str_list; - str_list = entry; - } - info->multicast_table = str_list; - info->vlan_table = get_vlan_table(n); - - 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; - } else { - info->vlan = RX_STATE_NORMAL; - } - - /* enable event notification after query */ - nc->rxfilter_notify_enabled = 1; - - return info; -} - -static void virtio_net_reset(VirtIODevice *vdev) -{ - VirtIONet *n = VIRTIO_NET(vdev); - - /* Reset back to compatibility mode */ - n->promisc = 1; - n->allmulti = 0; - n->alluni = 0; - n->nomulti = 0; - n->nouni = 0; - n->nobcast = 0; - /* multiqueue is disabled by default */ - n->curr_queues = 1; - timer_del(n->announce_timer); - n->announce_counter = 0; - n->status &= ~VIRTIO_NET_S_ANNOUNCE; - - /* Flush any MAC and VLAN filter table state */ - n->mac_table.in_use = 0; - n->mac_table.first_multi = 0; - n->mac_table.multi_overflow = 0; - n->mac_table.uni_overflow = 0; - memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); - memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac)); - qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); - memset(n->vlans, 0, MAX_VLAN >> 3); -} - -static void peer_test_vnet_hdr(VirtIONet *n) -{ - NetClientState *nc = qemu_get_queue(n->nic); - if (!nc->peer) { - return; - } - - n->has_vnet_hdr = qemu_has_vnet_hdr(nc->peer); -} - -static int peer_has_vnet_hdr(VirtIONet *n) -{ - return n->has_vnet_hdr; -} - -static int peer_has_ufo(VirtIONet *n) -{ - if (!peer_has_vnet_hdr(n)) - return 0; - - n->has_ufo = qemu_has_ufo(qemu_get_queue(n->nic)->peer); - - return n->has_ufo; -} - -static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, - int version_1) -{ - int i; - NetClientState *nc; - - n->mergeable_rx_bufs = mergeable_rx_bufs; - - if (version_1) { - n->guest_hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf); - } else { - n->guest_hdr_len = n->mergeable_rx_bufs ? - sizeof(struct virtio_net_hdr_mrg_rxbuf) : - sizeof(struct virtio_net_hdr); - } - - for (i = 0; i < n->max_queues; i++) { - nc = qemu_get_subqueue(n->nic, i); - - if (peer_has_vnet_hdr(n) && - qemu_has_vnet_hdr_len(nc->peer, n->guest_hdr_len)) { - qemu_set_vnet_hdr_len(nc->peer, n->guest_hdr_len); - n->host_hdr_len = n->guest_hdr_len; - } - } -} - -static int peer_attach(VirtIONet *n, int index) -{ - NetClientState *nc = qemu_get_subqueue(n->nic, index); - - if (!nc->peer) { - 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; - } - - return tap_enable(nc->peer); -} - -static int peer_detach(VirtIONet *n, int index) -{ - NetClientState *nc = qemu_get_subqueue(n->nic, index); - - if (!nc->peer) { - 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; - } - - return tap_disable(nc->peer); -} - -static void virtio_net_set_queues(VirtIONet *n) -{ - int i; - int r; - - for (i = 0; i < n->max_queues; i++) { - if (i < n->curr_queues) { - r = peer_attach(n, i); - assert(!r); - } else { - r = peer_detach(n, i); - assert(!r); - } - } -} - -static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue); - -static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features, - Error **errp) -{ - VirtIONet *n = VIRTIO_NET(vdev); - NetClientState *nc = qemu_get_queue(n->nic); - - /* Firstly sync all virtio-net possible supported features */ - features |= n->host_features; - - virtio_add_feature(&features, VIRTIO_NET_F_MAC); - - if (!peer_has_vnet_hdr(n)) { - virtio_clear_feature(&features, VIRTIO_NET_F_CSUM); - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO4); - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO6); - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_ECN); - - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_CSUM); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO4); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO6); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ECN); - } - - if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) { - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_UFO); - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_UFO); - } - - if (!get_vhost_net(nc->peer)) { - return features; - } - return vhost_net_get_features(get_vhost_net(nc->peer), features); -} - -static uint64_t virtio_net_bad_features(VirtIODevice *vdev) -{ - uint64_t features = 0; - - /* Linux kernel 2.6.25. It understood MAC (as everyone must), - * but also these: */ - virtio_add_feature(&features, VIRTIO_NET_F_MAC); - virtio_add_feature(&features, VIRTIO_NET_F_CSUM); - virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO4); - virtio_add_feature(&features, VIRTIO_NET_F_HOST_TSO6); - virtio_add_feature(&features, VIRTIO_NET_F_HOST_ECN); - - return features; -} - -static void virtio_net_apply_guest_offloads(VirtIONet *n) -{ - qemu_set_offload(qemu_get_queue(n->nic)->peer, - !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_CSUM)), - !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO4)), - !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_TSO6)), - !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_ECN)), - !!(n->curr_guest_offloads & (1ULL << VIRTIO_NET_F_GUEST_UFO))); -} - -static uint64_t virtio_net_guest_offloads_by_features(uint32_t features) -{ - static const uint64_t guest_offloads_mask = - (1ULL << VIRTIO_NET_F_GUEST_CSUM) | - (1ULL << VIRTIO_NET_F_GUEST_TSO4) | - (1ULL << VIRTIO_NET_F_GUEST_TSO6) | - (1ULL << VIRTIO_NET_F_GUEST_ECN) | - (1ULL << VIRTIO_NET_F_GUEST_UFO); - - return guest_offloads_mask & features; -} - -static inline uint64_t virtio_net_supported_guest_offloads(VirtIONet *n) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(n); - return virtio_net_guest_offloads_by_features(vdev->guest_features); -} - -static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) -{ - VirtIONet *n = VIRTIO_NET(vdev); - int i; - - virtio_net_set_multiqueue(n, - 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)); - - if (n->has_vnet_hdr) { - n->curr_guest_offloads = - virtio_net_guest_offloads_by_features(features); - virtio_net_apply_guest_offloads(n); - } - - for (i = 0; i < n->max_queues; i++) { - NetClientState *nc = qemu_get_subqueue(n->nic, i); - - if (!get_vhost_net(nc->peer)) { - continue; - } - vhost_net_ack_features(get_vhost_net(nc->peer), features); - } - - 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); - } -} - -static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd, - struct iovec *iov, unsigned int iov_cnt) -{ - uint8_t on; - size_t s; - NetClientState *nc = qemu_get_queue(n->nic); - - s = iov_to_buf(iov, iov_cnt, 0, &on, sizeof(on)); - if (s != sizeof(on)) { - return VIRTIO_NET_ERR; - } - - if (cmd == VIRTIO_NET_CTRL_RX_PROMISC) { - n->promisc = on; - } else if (cmd == VIRTIO_NET_CTRL_RX_ALLMULTI) { - n->allmulti = on; - } else if (cmd == VIRTIO_NET_CTRL_RX_ALLUNI) { - n->alluni = on; - } else if (cmd == VIRTIO_NET_CTRL_RX_NOMULTI) { - n->nomulti = on; - } else if (cmd == VIRTIO_NET_CTRL_RX_NOUNI) { - n->nouni = on; - } else if (cmd == VIRTIO_NET_CTRL_RX_NOBCAST) { - n->nobcast = on; - } else { - return VIRTIO_NET_ERR; - } - - rxfilter_notify(nc); - - return VIRTIO_NET_OK; -} - -static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd, - struct iovec *iov, unsigned int iov_cnt) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(n); - uint64_t offloads; - size_t s; - - if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { - return VIRTIO_NET_ERR; - } - - s = iov_to_buf(iov, iov_cnt, 0, &offloads, sizeof(offloads)); - if (s != sizeof(offloads)) { - return VIRTIO_NET_ERR; - } - - if (cmd == VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET) { - uint64_t supported_offloads; - - if (!n->has_vnet_hdr) { - return VIRTIO_NET_ERR; - } - - supported_offloads = virtio_net_supported_guest_offloads(n); - if (offloads & ~supported_offloads) { - return VIRTIO_NET_ERR; - } - - n->curr_guest_offloads = offloads; - virtio_net_apply_guest_offloads(n); - - return VIRTIO_NET_OK; - } else { - return VIRTIO_NET_ERR; - } -} - -static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, - struct iovec *iov, unsigned int iov_cnt) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(n); - struct virtio_net_ctrl_mac mac_data; - size_t s; - NetClientState *nc = qemu_get_queue(n->nic); - - if (cmd == VIRTIO_NET_CTRL_MAC_ADDR_SET) { - if (iov_size(iov, iov_cnt) != sizeof(n->mac)) { - return VIRTIO_NET_ERR; - } - s = iov_to_buf(iov, iov_cnt, 0, &n->mac, sizeof(n->mac)); - assert(s == sizeof(n->mac)); - qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); - rxfilter_notify(nc); - - return VIRTIO_NET_OK; - } - - if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET) { - return VIRTIO_NET_ERR; - } - - int in_use = 0; - int first_multi = 0; - uint8_t uni_overflow = 0; - uint8_t multi_overflow = 0; - uint8_t *macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); - - s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, - sizeof(mac_data.entries)); - mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries); - if (s != sizeof(mac_data.entries)) { - goto error; - } - iov_discard_front(&iov, &iov_cnt, s); - - if (mac_data.entries * ETH_ALEN > iov_size(iov, iov_cnt)) { - goto error; - } - - if (mac_data.entries <= MAC_TABLE_ENTRIES) { - s = iov_to_buf(iov, iov_cnt, 0, macs, - mac_data.entries * ETH_ALEN); - if (s != mac_data.entries * ETH_ALEN) { - goto error; - } - in_use += mac_data.entries; - } else { - uni_overflow = 1; - } - - iov_discard_front(&iov, &iov_cnt, mac_data.entries * ETH_ALEN); - - first_multi = in_use; - - s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, - sizeof(mac_data.entries)); - mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries); - if (s != sizeof(mac_data.entries)) { - goto error; - } - - iov_discard_front(&iov, &iov_cnt, s); - - if (mac_data.entries * ETH_ALEN != iov_size(iov, iov_cnt)) { - goto error; - } - - if (mac_data.entries <= MAC_TABLE_ENTRIES - in_use) { - s = iov_to_buf(iov, iov_cnt, 0, &macs[in_use * ETH_ALEN], - mac_data.entries * ETH_ALEN); - if (s != mac_data.entries * ETH_ALEN) { - goto error; - } - in_use += mac_data.entries; - } else { - multi_overflow = 1; - } - - n->mac_table.in_use = in_use; - n->mac_table.first_multi = first_multi; - n->mac_table.uni_overflow = uni_overflow; - n->mac_table.multi_overflow = multi_overflow; - memcpy(n->mac_table.macs, macs, MAC_TABLE_ENTRIES * ETH_ALEN); - g_free(macs); - rxfilter_notify(nc); - - return VIRTIO_NET_OK; - -error: - g_free(macs); - return VIRTIO_NET_ERR; -} - -static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd, - struct iovec *iov, unsigned int iov_cnt) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(n); - uint16_t vid; - size_t s; - NetClientState *nc = qemu_get_queue(n->nic); - - s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid)); - vid = virtio_lduw_p(vdev, &vid); - if (s != sizeof(vid)) { - return VIRTIO_NET_ERR; - } - - if (vid >= MAX_VLAN) - return VIRTIO_NET_ERR; - - if (cmd == VIRTIO_NET_CTRL_VLAN_ADD) - n->vlans[vid >> 5] |= (1U << (vid & 0x1f)); - else if (cmd == VIRTIO_NET_CTRL_VLAN_DEL) - n->vlans[vid >> 5] &= ~(1U << (vid & 0x1f)); - else - return VIRTIO_NET_ERR; - - rxfilter_notify(nc); - - return VIRTIO_NET_OK; -} - -static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd, - struct iovec *iov, unsigned int iov_cnt) -{ - if (cmd == VIRTIO_NET_CTRL_ANNOUNCE_ACK && - n->status & VIRTIO_NET_S_ANNOUNCE) { - n->status &= ~VIRTIO_NET_S_ANNOUNCE; - if (n->announce_counter) { - timer_mod(n->announce_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + - self_announce_delay(n->announce_counter)); - } - return VIRTIO_NET_OK; - } else { - return VIRTIO_NET_ERR; - } -} - -static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, - struct iovec *iov, unsigned int iov_cnt) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(n); - struct virtio_net_ctrl_mq mq; - size_t s; - uint16_t queues; - - s = iov_to_buf(iov, iov_cnt, 0, &mq, sizeof(mq)); - if (s != sizeof(mq)) { - return VIRTIO_NET_ERR; - } - - if (cmd != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) { - return VIRTIO_NET_ERR; - } - - queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs); - - if (queues < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN || - queues > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX || - queues > n->max_queues || - !n->multiqueue) { - return VIRTIO_NET_ERR; - } - - n->curr_queues = queues; - /* stop the backend before changing the number of queues to avoid handling a - * disabled queue */ - virtio_net_set_status(vdev, vdev->status); - virtio_net_set_queues(n); - - return VIRTIO_NET_OK; -} -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; - size_t s; - struct iovec *iov, *iov2; - unsigned int iov_cnt; - - 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); - s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl)); - iov_discard_front(&iov, &iov_cnt, sizeof(ctrl)); - if (s != sizeof(ctrl)) { - status = VIRTIO_NET_ERR; - } else if (ctrl.class == VIRTIO_NET_CTRL_RX) { - status = virtio_net_handle_rx_mode(n, ctrl.cmd, iov, iov_cnt); - } else if (ctrl.class == VIRTIO_NET_CTRL_MAC) { - status = virtio_net_handle_mac(n, ctrl.cmd, iov, iov_cnt); - } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) { - status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, iov_cnt); - } else if (ctrl.class == VIRTIO_NET_CTRL_ANNOUNCE) { - status = virtio_net_handle_announce(n, ctrl.cmd, iov, iov_cnt); - } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) { - status = virtio_net_handle_mq(n, ctrl.cmd, iov, iov_cnt); - } else if (ctrl.class == VIRTIO_NET_CTRL_GUEST_OFFLOADS) { - 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)); - assert(s == sizeof(status)); - - virtqueue_push(vq, elem, sizeof(status)); - virtio_notify(vdev, vq); - g_free(iov2); - g_free(elem); - } -} - -/* RX */ - -static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIONet *n = VIRTIO_NET(vdev); - int queue_index = vq2q(virtio_get_queue_index(vq)); - - qemu_flush_queued_packets(qemu_get_subqueue(n->nic, queue_index)); -} - -static int virtio_net_can_receive(NetClientState *nc) -{ - VirtIONet *n = qemu_get_nic_opaque(nc); - VirtIODevice *vdev = VIRTIO_DEVICE(n); - VirtIONetQueue *q = virtio_net_get_subqueue(nc); - - if (!vdev->vm_running) { - return 0; - } - - if (nc->queue_index >= n->curr_queues) { - return 0; - } - - if (!virtio_queue_ready(q->rx_vq) || - !(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { - return 0; - } - - return 1; -} - -static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize) -{ - VirtIONet *n = q->n; - if (virtio_queue_empty(q->rx_vq) || - (n->mergeable_rx_bufs && - !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { - virtio_queue_set_notification(q->rx_vq, 1); - - /* To avoid a race condition where the guest has made some buffers - * available after the above check but before notification was - * enabled, check for available buffers again. - */ - if (virtio_queue_empty(q->rx_vq) || - (n->mergeable_rx_bufs && - !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { - return 0; - } - } - - virtio_queue_set_notification(q->rx_vq, 0); - return 1; -} - -static void virtio_net_hdr_swap(VirtIODevice *vdev, struct virtio_net_hdr *hdr) -{ - virtio_tswap16s(vdev, &hdr->hdr_len); - virtio_tswap16s(vdev, &hdr->gso_size); - virtio_tswap16s(vdev, &hdr->csum_start); - virtio_tswap16s(vdev, &hdr->csum_offset); -} - -/* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so - * it never finds out that the packets don't have valid checksums. This - * causes dhclient to get upset. Fedora's carried a patch for ages to - * fix this with Xen but it hasn't appeared in an upstream release of - * dhclient yet. - * - * To avoid breaking existing guests, we catch udp packets and add - * checksums. This is terrible but it's better than hacking the guest - * kernels. - * - * N.B. if we introduce a zero-copy API, this operation is no longer free so - * we should provide a mechanism to disable it to avoid polluting the host - * cache. - */ -static void work_around_broken_dhclient(struct virtio_net_hdr *hdr, - uint8_t *buf, size_t size) -{ - if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */ - (size > 27 && size < 1500) && /* normal sized MTU */ - (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */ - (buf[23] == 17) && /* ip.protocol == UDP */ - (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */ - net_checksum_calculate(buf, size); - hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM; - } -} - -static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt, - const void *buf, size_t size) -{ - if (n->has_vnet_hdr) { - /* FIXME this cast is evil */ - void *wbuf = (void *)buf; - work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len, - size - n->host_hdr_len); - - 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 = { - .flags = 0, - .gso_type = VIRTIO_NET_HDR_GSO_NONE - }; - iov_from_buf(iov, iov_cnt, 0, &hdr, sizeof hdr); - } -} - -static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) -{ - static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - static const uint8_t vlan[] = {0x81, 0x00}; - uint8_t *ptr = (uint8_t *)buf; - int i; - - if (n->promisc) - return 1; - - ptr += n->host_hdr_len; - - if (!memcmp(&ptr[12], vlan, sizeof(vlan))) { - int vid = be16_to_cpup((uint16_t *)(ptr + 14)) & 0xfff; - if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f)))) - return 0; - } - - if (ptr[0] & 1) { // multicast - if (!memcmp(ptr, bcast, sizeof(bcast))) { - return !n->nobcast; - } else if (n->nomulti) { - return 0; - } else if (n->allmulti || n->mac_table.multi_overflow) { - return 1; - } - - for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) { - if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) { - return 1; - } - } - } else { // unicast - if (n->nouni) { - return 0; - } else if (n->alluni || n->mac_table.uni_overflow) { - return 1; - } else if (!memcmp(ptr, n->mac, ETH_ALEN)) { - return 1; - } - - for (i = 0; i < n->mac_table.first_multi; i++) { - if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) { - return 1; - } - } - } - - return 0; -} - -static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t size) -{ - VirtIONet *n = qemu_get_nic_opaque(nc); - VirtIONetQueue *q = virtio_net_get_subqueue(nc); - VirtIODevice *vdev = VIRTIO_DEVICE(n); - struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE]; - struct virtio_net_hdr_mrg_rxbuf mhdr; - unsigned mhdr_cnt = 0; - size_t offset, i, guest_offset; - - if (!virtio_net_can_receive(nc)) { - return -1; - } - - /* hdr_len refers to the header we supply to the guest */ - if (!virtio_net_has_buffers(q, size + n->guest_hdr_len - n->host_hdr_len)) { - return 0; - } - - if (!receive_filter(n, buf, size)) - return size; - - offset = i = 0; - - while (offset < size) { - VirtQueueElement *elem; - int len, total; - const struct iovec *sg; - - total = 0; - - elem = virtqueue_pop(q->rx_vq, sizeof(VirtQueueElement)); - if (!elem) { - if (i == 0) - return -1; - error_report("virtio-net unexpected empty queue: " - "i %zd mergeable %d offset %zd, size %zd, " - "guest hdr len %zd, host hdr len %zd " - "guest features 0x%" PRIx64, - i, n->mergeable_rx_bufs, offset, size, - n->guest_hdr_len, n->host_hdr_len, - vdev->guest_features); - exit(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, - offsetof(typeof(mhdr), num_buffers), - sizeof(mhdr.num_buffers)); - } - - 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; - } else { - guest_offset = 0; - } - - /* copy in packet. ugh */ - len = iov_from_buf(sg, elem->in_num, guest_offset, - buf + offset, size - offset); - total += len; - offset += len; - /* If buffers can't be merged, at this point we - * must have consumed the complete packet. - * Otherwise, drop it. */ - if (!n->mergeable_rx_bufs && offset < size) { - virtqueue_discard(q->rx_vq, elem, total); - g_free(elem); - return size; - } - - /* signal other side */ - virtqueue_fill(q->rx_vq, elem, total, i++); - g_free(elem); - } - - if (mhdr_cnt) { - virtio_stw_p(vdev, &mhdr.num_buffers, i); - iov_from_buf(mhdr_sg, mhdr_cnt, - 0, - &mhdr.num_buffers, sizeof mhdr.num_buffers); - } - - virtqueue_flush(q->rx_vq, i); - virtio_notify(vdev, q->rx_vq); - - return size; -} - -static int32_t virtio_net_flush_tx(VirtIONetQueue *q); - -static void virtio_net_tx_complete(NetClientState *nc, ssize_t len) -{ - VirtIONet *n = qemu_get_nic_opaque(nc); - VirtIONetQueue *q = virtio_net_get_subqueue(nc); - VirtIODevice *vdev = VIRTIO_DEVICE(n); - - virtqueue_push(q->tx_vq, q->async_tx.elem, 0); - virtio_notify(vdev, q->tx_vq); - - g_free(q->async_tx.elem); - q->async_tx.elem = NULL; - - virtio_queue_set_notification(q->tx_vq, 1); - virtio_net_flush_tx(q); -} - -/* TX */ -static int32_t virtio_net_flush_tx(VirtIONetQueue *q) -{ - VirtIONet *n = q->n; - VirtIODevice *vdev = VIRTIO_DEVICE(n); - 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) { - virtio_queue_set_notification(q->tx_vq, 0); - return num_packets; - } - - 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); - } - - if (n->has_vnet_hdr) { - if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) < - n->guest_hdr_len) { - error_report("virtio-net header incorrect"); - exit(1); - } - 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; - out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1, - out_sg, out_num, - n->guest_hdr_len, -1); - if (out_num == VIRTQUEUE_MAX_SIZE) { - goto drop; - } - out_num += 1; - out_sg = sg2; - } - } - /* - * If host wants to see the guest header as is, we can - * pass it on unchanged. Otherwise, copy just the parts - * that host is interested in. - */ - assert(n->host_hdr_len <= n->guest_hdr_len); - if (n->host_hdr_len != n->guest_hdr_len) { - unsigned sg_num = iov_copy(sg, ARRAY_SIZE(sg), - out_sg, out_num, - 0, n->host_hdr_len); - sg_num += iov_copy(sg + sg_num, ARRAY_SIZE(sg) - sg_num, - out_sg, out_num, - n->guest_hdr_len, -1); - out_num = sg_num; - out_sg = sg; - } - - 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; - return -EBUSY; - } - -drop: - virtqueue_push(q->tx_vq, elem, 0); - virtio_notify(vdev, q->tx_vq); - g_free(elem); - - if (++num_packets >= n->tx_burst) { - break; - } - } - return num_packets; -} - -static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIONet *n = VIRTIO_NET(vdev); - VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))]; - - /* This happens when device was stopped but VCPU wasn't. */ - if (!vdev->vm_running) { - q->tx_waiting = 1; - return; - } - - if (q->tx_waiting) { - virtio_queue_set_notification(vq, 1); - timer_del(q->tx_timer); - q->tx_waiting = 0; - virtio_net_flush_tx(q); - } else { - timer_mod(q->tx_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout); - q->tx_waiting = 1; - virtio_queue_set_notification(vq, 0); - } -} - -static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIONet *n = VIRTIO_NET(vdev); - VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))]; - - if (unlikely(q->tx_waiting)) { - return; - } - q->tx_waiting = 1; - /* This happens when device was stopped but VCPU wasn't. */ - if (!vdev->vm_running) { - return; - } - virtio_queue_set_notification(vq, 0); - qemu_bh_schedule(q->tx_bh); -} - -static void virtio_net_tx_timer(void *opaque) -{ - VirtIONetQueue *q = opaque; - VirtIONet *n = q->n; - VirtIODevice *vdev = VIRTIO_DEVICE(n); - /* This happens when device was stopped but BH wasn't. */ - if (!vdev->vm_running) { - /* Make sure tx waiting is set, so we'll run when restarted. */ - assert(q->tx_waiting); - return; - } - - q->tx_waiting = 0; - - /* Just in case the driver is not ready on more */ - if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { - return; - } - - virtio_queue_set_notification(q->tx_vq, 1); - virtio_net_flush_tx(q); -} - -static void virtio_net_tx_bh(void *opaque) -{ - VirtIONetQueue *q = opaque; - VirtIONet *n = q->n; - VirtIODevice *vdev = VIRTIO_DEVICE(n); - int32_t ret; - - /* This happens when device was stopped but BH wasn't. */ - if (!vdev->vm_running) { - /* Make sure tx waiting is set, so we'll run when restarted. */ - assert(q->tx_waiting); - return; - } - - q->tx_waiting = 0; - - /* Just in case the driver is not ready on more */ - if (unlikely(!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK))) { - return; - } - - ret = virtio_net_flush_tx(q); - if (ret == -EBUSY) { - return; /* Notification re-enable handled by tx_complete */ - } - - /* If we flush a full burst of packets, assume there are - * more coming and immediately reschedule */ - if (ret >= n->tx_burst) { - qemu_bh_schedule(q->tx_bh); - q->tx_waiting = 1; - return; - } - - /* If less than a full burst, re-enable notification and flush - * anything that may have come in while we weren't looking. If - * we find something, assume the guest is still active and reschedule */ - virtio_queue_set_notification(q->tx_vq, 1); - if (virtio_net_flush_tx(q) > 0) { - virtio_queue_set_notification(q->tx_vq, 0); - qemu_bh_schedule(q->tx_bh); - q->tx_waiting = 1; - } -} - -static void virtio_net_add_queue(VirtIONet *n, int index) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(n); - - n->vqs[index].rx_vq = virtio_add_queue(vdev, 256, virtio_net_handle_rx); - if (n->net_conf.tx && !strcmp(n->net_conf.tx, "timer")) { - n->vqs[index].tx_vq = - virtio_add_queue(vdev, 256, virtio_net_handle_tx_timer); - n->vqs[index].tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - virtio_net_tx_timer, - &n->vqs[index]); - } else { - n->vqs[index].tx_vq = - virtio_add_queue(vdev, 256, virtio_net_handle_tx_bh); - n->vqs[index].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[index]); - } - - n->vqs[index].tx_waiting = 0; - n->vqs[index].n = n; -} - -static void virtio_net_del_queue(VirtIONet *n, int index) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(n); - VirtIONetQueue *q = &n->vqs[index]; - NetClientState *nc = qemu_get_subqueue(n->nic, index); - - qemu_purge_queued_packets(nc); - - virtio_del_queue(vdev, index * 2); - if (q->tx_timer) { - timer_del(q->tx_timer); - timer_free(q->tx_timer); - } else { - qemu_bh_delete(q->tx_bh); - } - virtio_del_queue(vdev, index * 2 + 1); -} - -static void virtio_net_change_num_queues(VirtIONet *n, int new_max_queues) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(n); - int old_num_queues = virtio_get_num_queues(vdev); - int new_num_queues = new_max_queues * 2 + 1; - int i; - - assert(old_num_queues >= 3); - assert(old_num_queues % 2 == 1); - - if (old_num_queues == new_num_queues) { - return; - } - - /* - * We always need to remove and add ctrl vq if - * old_num_queues != new_num_queues. Remove ctrl_vq first, - * and then we only enter one of the following too loops. - */ - virtio_del_queue(vdev, old_num_queues - 1); - - for (i = new_num_queues - 1; i < old_num_queues - 1; i += 2) { - /* new_num_queues < old_num_queues */ - virtio_net_del_queue(n, i / 2); - } - - for (i = old_num_queues - 1; i < new_num_queues - 1; i += 2) { - /* new_num_queues > old_num_queues */ - virtio_net_add_queue(n, i / 2); - } - - /* add ctrl_vq last */ - n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl); -} - -static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue) -{ - int max = multiqueue ? n->max_queues : 1; - - n->multiqueue = multiqueue; - virtio_net_change_num_queues(n, max); - - virtio_net_set_queues(n); -} - -static void virtio_net_save(QEMUFile *f, void *opaque) -{ - VirtIONet *n = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(n); - - /* At this point, backend must be stopped, otherwise - * it might keep writing to memory. */ - assert(!n->vhost_started); - virtio_save(vdev, f); -} - -static void virtio_net_save_device(VirtIODevice *vdev, QEMUFile *f) -{ - VirtIONet *n = VIRTIO_NET(vdev); - int i; - - qemu_put_buffer(f, n->mac, ETH_ALEN); - qemu_put_be32(f, n->vqs[0].tx_waiting); - qemu_put_be32(f, n->mergeable_rx_bufs); - qemu_put_be16(f, n->status); - qemu_put_byte(f, n->promisc); - qemu_put_byte(f, n->allmulti); - qemu_put_be32(f, n->mac_table.in_use); - qemu_put_buffer(f, n->mac_table.macs, n->mac_table.in_use * ETH_ALEN); - qemu_put_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3); - qemu_put_be32(f, n->has_vnet_hdr); - qemu_put_byte(f, n->mac_table.multi_overflow); - qemu_put_byte(f, n->mac_table.uni_overflow); - qemu_put_byte(f, n->alluni); - qemu_put_byte(f, n->nomulti); - qemu_put_byte(f, n->nouni); - qemu_put_byte(f, n->nobcast); - qemu_put_byte(f, n->has_ufo); - if (n->max_queues > 1) { - qemu_put_be16(f, n->max_queues); - qemu_put_be16(f, n->curr_queues); - for (i = 1; i < n->curr_queues; i++) { - qemu_put_be32(f, n->vqs[i].tx_waiting); - } - } - - if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) { - qemu_put_be64(f, n->curr_guest_offloads); - } -} - -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; - - 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, - int version_id) -{ - VirtIONet *n = VIRTIO_NET(vdev); - int i, link_down; - - qemu_get_buffer(f, n->mac, ETH_ALEN); - n->vqs[0].tx_waiting = qemu_get_be32(f); - - virtio_net_set_mrg_rx_bufs(n, qemu_get_be32(f), - virtio_vdev_has_feature(vdev, - VIRTIO_F_VERSION_1)); - - if (version_id >= 3) - n->status = qemu_get_be16(f); - - if (version_id >= 4) { - if (version_id < 8) { - n->promisc = qemu_get_be32(f); - n->allmulti = qemu_get_be32(f); - } else { - n->promisc = qemu_get_byte(f); - n->allmulti = qemu_get_byte(f); - } - } - - if (version_id >= 5) { - n->mac_table.in_use = qemu_get_be32(f); - /* MAC_TABLE_ENTRIES may be different from the saved image */ - if (n->mac_table.in_use <= MAC_TABLE_ENTRIES) { - qemu_get_buffer(f, n->mac_table.macs, - n->mac_table.in_use * ETH_ALEN); - } else { - int64_t i; - - /* Overflow detected - can happen if source has a larger MAC table. - * We simply set overflow flag so there's no need to maintain the - * table of addresses, discard them all. - * Note: 64 bit math to avoid integer overflow. - */ - for (i = 0; i < (int64_t)n->mac_table.in_use * ETH_ALEN; ++i) { - qemu_get_byte(f); - } - n->mac_table.multi_overflow = n->mac_table.uni_overflow = 1; - n->mac_table.in_use = 0; - } - } - - if (version_id >= 6) - qemu_get_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3); - - if (version_id >= 7) { - if (qemu_get_be32(f) && !peer_has_vnet_hdr(n)) { - error_report("virtio-net: saved image requires vnet_hdr=on"); - return -1; - } - } - - if (version_id >= 9) { - n->mac_table.multi_overflow = qemu_get_byte(f); - n->mac_table.uni_overflow = qemu_get_byte(f); - } - - if (version_id >= 10) { - n->alluni = qemu_get_byte(f); - n->nomulti = qemu_get_byte(f); - n->nouni = qemu_get_byte(f); - n->nobcast = qemu_get_byte(f); - } - - if (version_id >= 11) { - if (qemu_get_byte(f) && !peer_has_ufo(n)) { - error_report("virtio-net: saved image requires TUN_F_UFO support"); - return -1; - } - } - - if (n->max_queues > 1) { - if (n->max_queues != qemu_get_be16(f)) { - error_report("virtio-net: different max_queues "); - return -1; - } - - n->curr_queues = qemu_get_be16(f); - if (n->curr_queues > n->max_queues) { - error_report("virtio-net: curr_queues %x > max_queues %x", - n->curr_queues, n->max_queues); - return -1; - } - for (i = 1; i < n->curr_queues; i++) { - n->vqs[i].tx_waiting = qemu_get_be32(f); - } - } - - virtio_net_set_queues(n); - - /* Find the first multicast entry in the saved MAC filter */ - for (i = 0; i < n->mac_table.in_use; i++) { - if (n->mac_table.macs[i * ETH_ALEN] & 1) { - break; - } - } - n->mac_table.first_multi = i; - - /* nc.link_down can't be migrated, so infer link_down according - * to link status bit in n->status */ - link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0; - for (i = 0; i < n->max_queues; i++) { - qemu_get_subqueue(n->nic, i)->link_down = link_down; - } - - return 0; -} - -static NetClientInfo net_virtio_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .can_receive = virtio_net_can_receive, - .receive = virtio_net_receive, - .link_status_changed = virtio_net_set_link_status, - .query_rx_filter = virtio_net_query_rxfilter, -}; - -static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx) -{ - VirtIONet *n = VIRTIO_NET(vdev); - NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx)); - assert(n->vhost_started); - return vhost_net_virtqueue_pending(get_vhost_net(nc->peer), idx); -} - -static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx, - bool mask) -{ - VirtIONet *n = VIRTIO_NET(vdev); - NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx)); - assert(n->vhost_started); - vhost_net_virtqueue_mask(get_vhost_net(nc->peer), - vdev, idx, mask); -} - -static void virtio_net_set_config_size(VirtIONet *n, uint64_t host_features) -{ - int i, config_size = 0; - virtio_add_feature(&host_features, VIRTIO_NET_F_MAC); - for (i = 0; feature_sizes[i].flags != 0; i++) { - if (host_features & feature_sizes[i].flags) { - config_size = MAX(feature_sizes[i].end, config_size); - } - } - n->config_size = config_size; -} - -void virtio_net_set_netclient_name(VirtIONet *n, const char *name, - const char *type) -{ - /* - * The name can be NULL, the netclient name will be type.x. - */ - assert(type != NULL); - - g_free(n->netclient_name); - g_free(n->netclient_type); - n->netclient_name = g_strdup(name); - n->netclient_type = g_strdup(type); -} - -static void virtio_net_device_realize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIONet *n = VIRTIO_NET(dev); - NetClientState *nc; - int i; - - virtio_net_set_config_size(n, n->host_features); - virtio_init(vdev, "virtio-net", VIRTIO_ID_NET, n->config_size); - - n->max_queues = MAX(n->nic_conf.peers.queues, 1); - if (n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX) { - error_setg(errp, "Invalid number of queues (= %" PRIu32 "), " - "must be a positive integer less than %d.", - n->max_queues, (VIRTIO_QUEUE_MAX - 1) / 2); - virtio_cleanup(vdev); - return; - } - n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues); - n->curr_queues = 1; - n->tx_timeout = n->net_conf.txtimer; - - if (n->net_conf.tx && strcmp(n->net_conf.tx, "timer") - && strcmp(n->net_conf.tx, "bh")) { - error_report("virtio-net: " - "Unknown option tx=%s, valid options: \"timer\" \"bh\"", - n->net_conf.tx); - error_report("Defaulting to \"bh\""); - } - - for (i = 0; i < n->max_queues; i++) { - virtio_net_add_queue(n, i); - } - - n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl); - qemu_macaddr_default_if_unset(&n->nic_conf.macaddr); - memcpy(&n->mac[0], &n->nic_conf.macaddr, sizeof(n->mac)); - n->status = VIRTIO_NET_S_LINK_UP; - n->announce_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, - virtio_net_announce_timer, n); - - if (n->netclient_type) { - /* - * Happen when virtio_net_set_netclient_name has been called. - */ - n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf, - n->netclient_type, n->netclient_name, n); - } else { - n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf, - object_get_typename(OBJECT(dev)), dev->id, n); - } - - peer_test_vnet_hdr(n); - if (peer_has_vnet_hdr(n)) { - for (i = 0; i < n->max_queues; i++) { - qemu_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true); - } - n->host_hdr_len = sizeof(struct virtio_net_hdr); - } else { - n->host_hdr_len = 0; - } - - qemu_format_nic_info_str(qemu_get_queue(n->nic), n->nic_conf.macaddr.a); - - n->vqs[0].tx_waiting = 0; - n->tx_burst = n->net_conf.txburst; - virtio_net_set_mrg_rx_bufs(n, 0, 0); - n->promisc = 1; /* for compatibility */ - - n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); - - n->vlans = g_malloc0(MAX_VLAN >> 3); - - nc = qemu_get_queue(n->nic); - nc->rxfilter_notify_enabled = 1; - - n->qdev = dev; - register_savevm(dev, "virtio-net", -1, VIRTIO_NET_VM_VERSION, - virtio_net_save, virtio_net_load, n); -} - -static void virtio_net_device_unrealize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIONet *n = VIRTIO_NET(dev); - int i, max_queues; - - /* This will stop vhost backend if appropriate. */ - virtio_net_set_status(vdev, 0); - - unregister_savevm(dev, "virtio-net", n); - - g_free(n->netclient_name); - n->netclient_name = NULL; - g_free(n->netclient_type); - n->netclient_type = NULL; - - g_free(n->mac_table.macs); - g_free(n->vlans); - - max_queues = n->multiqueue ? n->max_queues : 1; - for (i = 0; i < max_queues; i++) { - virtio_net_del_queue(n, i); - } - - timer_del(n->announce_timer); - timer_free(n->announce_timer); - g_free(n->vqs); - qemu_del_nic(n->nic); - virtio_cleanup(vdev); -} - -static void virtio_net_instance_init(Object *obj) -{ - VirtIONet *n = VIRTIO_NET(obj); - - /* - * The default config_size is sizeof(struct virtio_net_config). - * Can be overriden with virtio_net_set_config_size. - */ - n->config_size = sizeof(struct virtio_net_config); - device_add_bootindex_property(obj, &n->nic_conf.bootindex, - "bootindex", "/ethernet-phy@0", - DEVICE(n), NULL); -} - -static Property virtio_net_properties[] = { - DEFINE_PROP_BIT("csum", VirtIONet, host_features, VIRTIO_NET_F_CSUM, true), - DEFINE_PROP_BIT("guest_csum", VirtIONet, host_features, - VIRTIO_NET_F_GUEST_CSUM, true), - DEFINE_PROP_BIT("gso", VirtIONet, host_features, VIRTIO_NET_F_GSO, true), - DEFINE_PROP_BIT("guest_tso4", VirtIONet, host_features, - VIRTIO_NET_F_GUEST_TSO4, true), - DEFINE_PROP_BIT("guest_tso6", VirtIONet, host_features, - VIRTIO_NET_F_GUEST_TSO6, true), - DEFINE_PROP_BIT("guest_ecn", VirtIONet, host_features, - VIRTIO_NET_F_GUEST_ECN, true), - DEFINE_PROP_BIT("guest_ufo", VirtIONet, host_features, - VIRTIO_NET_F_GUEST_UFO, true), - DEFINE_PROP_BIT("guest_announce", VirtIONet, host_features, - VIRTIO_NET_F_GUEST_ANNOUNCE, true), - DEFINE_PROP_BIT("host_tso4", VirtIONet, host_features, - VIRTIO_NET_F_HOST_TSO4, true), - DEFINE_PROP_BIT("host_tso6", VirtIONet, host_features, - VIRTIO_NET_F_HOST_TSO6, true), - DEFINE_PROP_BIT("host_ecn", VirtIONet, host_features, - VIRTIO_NET_F_HOST_ECN, true), - DEFINE_PROP_BIT("host_ufo", VirtIONet, host_features, - VIRTIO_NET_F_HOST_UFO, true), - DEFINE_PROP_BIT("mrg_rxbuf", VirtIONet, host_features, - VIRTIO_NET_F_MRG_RXBUF, true), - DEFINE_PROP_BIT("status", VirtIONet, host_features, - VIRTIO_NET_F_STATUS, true), - DEFINE_PROP_BIT("ctrl_vq", VirtIONet, host_features, - VIRTIO_NET_F_CTRL_VQ, true), - DEFINE_PROP_BIT("ctrl_rx", VirtIONet, host_features, - VIRTIO_NET_F_CTRL_RX, true), - DEFINE_PROP_BIT("ctrl_vlan", VirtIONet, host_features, - VIRTIO_NET_F_CTRL_VLAN, true), - DEFINE_PROP_BIT("ctrl_rx_extra", VirtIONet, host_features, - VIRTIO_NET_F_CTRL_RX_EXTRA, true), - DEFINE_PROP_BIT("ctrl_mac_addr", VirtIONet, host_features, - VIRTIO_NET_F_CTRL_MAC_ADDR, true), - DEFINE_PROP_BIT("ctrl_guest_offloads", VirtIONet, host_features, - VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, true), - DEFINE_PROP_BIT("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false), - DEFINE_NIC_PROPERTIES(VirtIONet, nic_conf), - DEFINE_PROP_UINT32("x-txtimer", VirtIONet, net_conf.txtimer, - TX_TIMER_INTERVAL), - DEFINE_PROP_INT32("x-txburst", VirtIONet, net_conf.txburst, TX_BURST), - DEFINE_PROP_STRING("tx", VirtIONet, net_conf.tx), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_net_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - - dc->props = virtio_net_properties; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - vdc->realize = virtio_net_device_realize; - vdc->unrealize = virtio_net_device_unrealize; - vdc->get_config = virtio_net_get_config; - vdc->set_config = virtio_net_set_config; - vdc->get_features = virtio_net_get_features; - vdc->set_features = virtio_net_set_features; - vdc->bad_features = virtio_net_bad_features; - vdc->reset = virtio_net_reset; - vdc->set_status = virtio_net_set_status; - vdc->guest_notifier_mask = virtio_net_guest_notifier_mask; - vdc->guest_notifier_pending = virtio_net_guest_notifier_pending; - vdc->load = virtio_net_load_device; - vdc->save = virtio_net_save_device; -} - -static const TypeInfo virtio_net_info = { - .name = TYPE_VIRTIO_NET, - .parent = TYPE_VIRTIO_DEVICE, - .instance_size = sizeof(VirtIONet), - .instance_init = virtio_net_instance_init, - .class_init = virtio_net_class_init, -}; - -static void virtio_register_types(void) -{ - type_register_static(&virtio_net_info); -} - -type_init(virtio_register_types) diff --git a/qemu/hw/net/vmware_utils.h b/qemu/hw/net/vmware_utils.h deleted file mode 100644 index c0dbb2ff4..000000000 --- a/qemu/hw/net/vmware_utils.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * QEMU VMWARE paravirtual devices - auxiliary code - * - * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) - * - * Developed by Daynix Computing LTD (http://www.daynix.com) - * - * Authors: - * Dmitry Fleytman - * Yan Vugenfirer - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef VMWARE_UTILS_H -#define VMWARE_UTILS_H - -#include "qemu/range.h" -#include "vmxnet_debug.h" - -/* - * Shared memory access functions with byte swap support - * Each function contains printout for reverse-engineering needs - * - */ -static inline void -vmw_shmem_read(hwaddr addr, void *buf, int len) -{ - VMW_SHPRN("SHMEM r: %" PRIx64 ", len: %d to %p", addr, len, buf); - cpu_physical_memory_read(addr, buf, len); -} - -static inline void -vmw_shmem_write(hwaddr addr, void *buf, int len) -{ - VMW_SHPRN("SHMEM w: %" PRIx64 ", len: %d to %p", addr, len, buf); - cpu_physical_memory_write(addr, buf, len); -} - -static inline void -vmw_shmem_rw(hwaddr addr, void *buf, int len, int is_write) -{ - VMW_SHPRN("SHMEM r/w: %" PRIx64 ", len: %d (to %p), is write: %d", - addr, len, buf, is_write); - - cpu_physical_memory_rw(addr, buf, len, is_write); -} - -static inline void -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); - - for (i = 0; i < len; i++) { - cpu_physical_memory_write(addr + i, &val, 1); - } -} - -static inline uint32_t -vmw_shmem_ld8(hwaddr addr) -{ - uint8_t res = ldub_phys(&address_space_memory, addr); - VMW_SHPRN("SHMEM load8: %" PRIx64 " (value 0x%X)", addr, res); - return res; -} - -static inline void -vmw_shmem_st8(hwaddr addr, uint8_t value) -{ - VMW_SHPRN("SHMEM store8: %" PRIx64 " (value 0x%X)", addr, value); - stb_phys(&address_space_memory, addr, value); -} - -static inline uint32_t -vmw_shmem_ld16(hwaddr addr) -{ - uint16_t res = lduw_le_phys(&address_space_memory, addr); - VMW_SHPRN("SHMEM load16: %" PRIx64 " (value 0x%X)", addr, res); - return res; -} - -static inline void -vmw_shmem_st16(hwaddr addr, uint16_t value) -{ - VMW_SHPRN("SHMEM store16: %" PRIx64 " (value 0x%X)", addr, value); - stw_le_phys(&address_space_memory, addr, value); -} - -static inline uint32_t -vmw_shmem_ld32(hwaddr addr) -{ - uint32_t res = ldl_le_phys(&address_space_memory, addr); - VMW_SHPRN("SHMEM load32: %" PRIx64 " (value 0x%X)", addr, res); - return res; -} - -static inline void -vmw_shmem_st32(hwaddr addr, uint32_t value) -{ - VMW_SHPRN("SHMEM store32: %" PRIx64 " (value 0x%X)", addr, value); - stl_le_phys(&address_space_memory, addr, value); -} - -static inline uint64_t -vmw_shmem_ld64(hwaddr addr) -{ - uint64_t res = ldq_le_phys(&address_space_memory, addr); - VMW_SHPRN("SHMEM load64: %" PRIx64 " (value %" PRIx64 ")", addr, res); - return res; -} - -static inline void -vmw_shmem_st64(hwaddr addr, uint64_t value) -{ - VMW_SHPRN("SHMEM store64: %" PRIx64 " (value %" PRIx64 ")", addr, value); - stq_le_phys(&address_space_memory, addr, value); -} - -/* Macros for simplification of operations on array-style registers */ - -/* - * Whether lies inside of array-style register defined by , - * number of elements () and element size () - * -*/ -#define VMW_IS_MULTIREG_ADDR(addr, base, cnt, regsize) \ - range_covers_byte(base, cnt * regsize, addr) - -/* - * Returns index of given register () in array-style register defined by - * and element size () - * -*/ -#define VMW_MULTIREG_IDX_BY_ADDR(addr, base, regsize) \ - (((addr) - (base)) / (regsize)) - -#endif diff --git a/qemu/hw/net/vmxnet3.c b/qemu/hw/net/vmxnet3.c deleted file mode 100644 index 093a71e12..000000000 --- a/qemu/hw/net/vmxnet3.c +++ /dev/null @@ -1,2723 +0,0 @@ -/* - * QEMU VMWARE VMXNET3 paravirtual NIC - * - * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) - * - * Developed by Daynix Computing LTD (http://www.daynix.com) - * - * Authors: - * Dmitry Fleytman - * Tamir Shomer - * Yan Vugenfirer - * - * 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/hw.h" -#include "hw/pci/pci.h" -#include "net/net.h" -#include "net/tap.h" -#include "net/checksum.h" -#include "sysemu/sysemu.h" -#include "qemu-common.h" -#include "qemu/bswap.h" -#include "hw/pci/msix.h" -#include "hw/pci/msi.h" - -#include "vmxnet3.h" -#include "vmxnet_debug.h" -#include "vmware_utils.h" -#include "vmxnet_tx_pkt.h" -#include "vmxnet_rx_pkt.h" - -#define PCI_DEVICE_ID_VMWARE_VMXNET3_REVISION 0x1 -#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(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) - -/* Link status: 1 - up, 0 - down. */ -#define VMXNET3_LINK_STATUS_UP 0x1 - -/* Least significant bit should be set for revision and version */ -#define VMXNET3_UPT_REVISION 0x1 -#define VMXNET3_DEVICE_REVISION 0x1 - -/* Number of interrupt vectors for non-MSIx modes */ -#define VMXNET3_MAX_NMSIX_INTRS (1) - -/* Macros for rings descriptors access */ -#define VMXNET3_READ_TX_QUEUE_DESCR8(dpa, field) \ - (vmw_shmem_ld8(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field))) - -#define VMXNET3_WRITE_TX_QUEUE_DESCR8(dpa, field, value) \ - (vmw_shmem_st8(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field, value))) - -#define VMXNET3_READ_TX_QUEUE_DESCR32(dpa, field) \ - (vmw_shmem_ld32(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field))) - -#define VMXNET3_WRITE_TX_QUEUE_DESCR32(dpa, field, value) \ - (vmw_shmem_st32(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field), value)) - -#define VMXNET3_READ_TX_QUEUE_DESCR64(dpa, field) \ - (vmw_shmem_ld64(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field))) - -#define VMXNET3_WRITE_TX_QUEUE_DESCR64(dpa, field, value) \ - (vmw_shmem_st64(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field), value)) - -#define VMXNET3_READ_RX_QUEUE_DESCR64(dpa, field) \ - (vmw_shmem_ld64(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field))) - -#define VMXNET3_READ_RX_QUEUE_DESCR32(dpa, field) \ - (vmw_shmem_ld32(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field))) - -#define VMXNET3_WRITE_RX_QUEUE_DESCR64(dpa, field, value) \ - (vmw_shmem_st64(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field), value)) - -#define VMXNET3_WRITE_RX_QUEUE_DESCR8(dpa, field, value) \ - (vmw_shmem_st8(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field), value)) - -/* Macros for guest driver shared area access */ -#define VMXNET3_READ_DRV_SHARED64(shpa, field) \ - (vmw_shmem_ld64(shpa + offsetof(struct Vmxnet3_DriverShared, field))) - -#define VMXNET3_READ_DRV_SHARED32(shpa, field) \ - (vmw_shmem_ld32(shpa + offsetof(struct Vmxnet3_DriverShared, field))) - -#define VMXNET3_WRITE_DRV_SHARED32(shpa, field, val) \ - (vmw_shmem_st32(shpa + offsetof(struct Vmxnet3_DriverShared, field), val)) - -#define VMXNET3_READ_DRV_SHARED16(shpa, field) \ - (vmw_shmem_ld16(shpa + offsetof(struct Vmxnet3_DriverShared, field))) - -#define VMXNET3_READ_DRV_SHARED8(shpa, field) \ - (vmw_shmem_ld8(shpa + offsetof(struct Vmxnet3_DriverShared, field))) - -#define VMXNET3_READ_DRV_SHARED(shpa, field, b, l) \ - (vmw_shmem_read(shpa + offsetof(struct Vmxnet3_DriverShared, field), b, l)) - -#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; - size_t size; - size_t cell_size; - size_t next; - uint8_t gen; -} Vmxnet3Ring; - -static inline void vmxnet3_ring_init(Vmxnet3Ring *ring, - hwaddr pa, - size_t size, - size_t cell_size, - bool zero_region) -{ - ring->pa = pa; - ring->size = size; - ring->cell_size = cell_size; - ring->gen = VMXNET3_INIT_GEN; - ring->next = 0; - - if (zero_region) { - vmw_shmem_set(pa, 0, size * cell_size); - } -} - -#define VMXNET3_RING_DUMP(macro, ring_name, ridx, r) \ - 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) - -static inline void vmxnet3_ring_inc(Vmxnet3Ring *ring) -{ - if (++ring->next >= ring->size) { - ring->next = 0; - ring->gen ^= 1; - } -} - -static inline void vmxnet3_ring_dec(Vmxnet3Ring *ring) -{ - if (ring->next-- == 0) { - ring->next = ring->size - 1; - ring->gen ^= 1; - } -} - -static inline hwaddr vmxnet3_ring_curr_cell_pa(Vmxnet3Ring *ring) -{ - return ring->pa + ring->next * ring->cell_size; -} - -static inline void vmxnet3_ring_read_curr_cell(Vmxnet3Ring *ring, void *buff) -{ - vmw_shmem_read(vmxnet3_ring_curr_cell_pa(ring), buff, ring->cell_size); -} - -static inline void vmxnet3_ring_write_curr_cell(Vmxnet3Ring *ring, void *buff) -{ - vmw_shmem_write(vmxnet3_ring_curr_cell_pa(ring), buff, ring->cell_size); -} - -static inline size_t vmxnet3_ring_curr_cell_idx(Vmxnet3Ring *ring) -{ - return ring->next; -} - -static inline uint8_t vmxnet3_ring_curr_gen(Vmxnet3Ring *ring) -{ - return ring->gen; -} - -/* Debug trace-related functions */ -static inline void -vmxnet3_dump_tx_descr(struct Vmxnet3_TxDesc *descr) -{ - VMW_PKPRN("TX DESCR: " - "addr %" PRIx64 ", len: %d, gen: %d, rsvd: %d, " - "dtype: %d, ext1: %d, msscof: %d, hlen: %d, om: %d, " - "eop: %d, cq: %d, ext2: %d, ti: %d, tci: %d", - le64_to_cpu(descr->addr), descr->len, descr->gen, descr->rsvd, - descr->dtype, descr->ext1, descr->msscof, descr->hlen, descr->om, - descr->eop, descr->cq, descr->ext2, descr->ti, descr->tci); -} - -static inline void -vmxnet3_dump_virt_hdr(struct virtio_net_hdr *vhdr) -{ - VMW_PKPRN("VHDR: flags 0x%x, gso_type: 0x%x, hdr_len: %d, gso_size: %d, " - "csum_start: %d, csum_offset: %d", - vhdr->flags, vhdr->gso_type, vhdr->hdr_len, vhdr->gso_size, - vhdr->csum_start, vhdr->csum_offset); -} - -static inline void -vmxnet3_dump_rx_descr(struct Vmxnet3_RxDesc *descr) -{ - VMW_PKPRN("RX DESCR: addr %" PRIx64 ", len: %d, gen: %d, rsvd: %d, " - "dtype: %d, ext1: %d, btype: %d", - le64_to_cpu(descr->addr), descr->len, descr->gen, - descr->rsvd, descr->dtype, descr->ext1, descr->btype); -} - -/* Device state and helper functions */ -#define VMXNET3_RX_RINGS_PER_QUEUE (2) - -typedef struct { - Vmxnet3Ring tx_ring; - Vmxnet3Ring comp_ring; - - uint8_t intr_idx; - hwaddr tx_stats_pa; - struct UPT1_TxStats txq_stats; -} Vmxnet3TxqDescr; - -typedef struct { - Vmxnet3Ring rx_ring[VMXNET3_RX_RINGS_PER_QUEUE]; - Vmxnet3Ring comp_ring; - uint8_t intr_idx; - hwaddr rx_stats_pa; - struct UPT1_RxStats rxq_stats; -} Vmxnet3RxqDescr; - -typedef struct { - bool is_masked; - bool is_pending; - bool is_asserted; -} Vmxnet3IntState; - -typedef struct { - PCIDevice parent_obj; - NICState *nic; - NICConf conf; - MemoryRegion bar0; - MemoryRegion bar1; - MemoryRegion msix_bar; - - Vmxnet3RxqDescr rxq_descr[VMXNET3_DEVICE_MAX_RX_QUEUES]; - Vmxnet3TxqDescr txq_descr[VMXNET3_DEVICE_MAX_TX_QUEUES]; - - /* Whether MSI-X support was installed successfully */ - bool msix_used; - /* Whether MSI support was installed successfully */ - bool msi_used; - hwaddr drv_shmem; - hwaddr temp_shared_guest_driver_memory; - - uint8_t txq_num; - - /* This boolean tells whether RX packet being indicated has to */ - /* be split into head and body chunks from different RX rings */ - bool rx_packets_compound; - - bool rx_vlan_stripping; - bool lro_supported; - - uint8_t rxq_num; - - /* Network MTU */ - uint32_t mtu; - - /* Maximum number of fragments for indicated TX packets */ - uint32_t max_tx_frags; - - /* Maximum number of fragments for indicated RX packets */ - uint16_t max_rx_frags; - - /* Index for events interrupt */ - uint8_t event_int_idx; - - /* Whether automatic interrupts masking enabled */ - bool auto_int_masking; - - bool peer_has_vhdr; - - /* TX packets to QEMU interface */ - struct VmxnetTxPkt *tx_pkt; - uint32_t offload_mode; - uint32_t cso_or_gso_size; - uint16_t tci; - bool needs_vlan; - - struct VmxnetRxPkt *rx_pkt; - - bool tx_sop; - bool skip_current_tx_pkt; - - uint32_t device_active; - uint32_t last_command; - - uint32_t link_status_and_speed; - - Vmxnet3IntState interrupt_states[VMXNET3_MAX_INTRS]; - - uint32_t temp_mac; /* To store the low part first */ - - MACAddr perm_mac; - uint32_t vlan_table[VMXNET3_VFT_SIZE]; - uint32_t rx_mode; - 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 */ - -/* - *This function returns sign whether interrupt line is in asserted state - * This depends on the type of interrupt used. For INTX interrupt line will - * be asserted until explicit deassertion, for MSI(X) interrupt line will - * be deasserted automatically due to notification semantics of the MSI(X) - * interrupts - */ -static bool _vmxnet3_assert_interrupt_line(VMXNET3State *s, uint32_t int_idx) -{ - PCIDevice *d = PCI_DEVICE(s); - - if (s->msix_used && msix_enabled(d)) { - VMW_IRPRN("Sending MSI-X notification for vector %u", int_idx); - msix_notify(d, int_idx); - return false; - } - if (s->msi_used && msi_enabled(d)) { - VMW_IRPRN("Sending MSI notification for vector %u", int_idx); - msi_notify(d, int_idx); - return false; - } - - VMW_IRPRN("Asserting line for interrupt %u", int_idx); - pci_irq_assert(d); - return true; -} - -static void _vmxnet3_deassert_interrupt_line(VMXNET3State *s, int lidx) -{ - PCIDevice *d = PCI_DEVICE(s); - - /* - * This function should never be called for MSI(X) interrupts - * because deassertion never required for message interrupts - */ - assert(!s->msix_used || !msix_enabled(d)); - /* - * This function should never be called for MSI(X) interrupts - * because deassertion never required for message interrupts - */ - assert(!s->msi_used || !msi_enabled(d)); - - VMW_IRPRN("Deasserting line for interrupt %u", lidx); - pci_irq_deassert(d); -} - -static void vmxnet3_update_interrupt_line_state(VMXNET3State *s, int lidx) -{ - if (!s->interrupt_states[lidx].is_pending && - s->interrupt_states[lidx].is_asserted) { - VMW_IRPRN("New interrupt line state for index %d is DOWN", lidx); - _vmxnet3_deassert_interrupt_line(s, lidx); - s->interrupt_states[lidx].is_asserted = false; - return; - } - - if (s->interrupt_states[lidx].is_pending && - !s->interrupt_states[lidx].is_masked && - !s->interrupt_states[lidx].is_asserted) { - VMW_IRPRN("New interrupt line state for index %d is UP", lidx); - s->interrupt_states[lidx].is_asserted = - _vmxnet3_assert_interrupt_line(s, lidx); - s->interrupt_states[lidx].is_pending = false; - return; - } -} - -static void vmxnet3_trigger_interrupt(VMXNET3State *s, int lidx) -{ - PCIDevice *d = PCI_DEVICE(s); - s->interrupt_states[lidx].is_pending = true; - vmxnet3_update_interrupt_line_state(s, lidx); - - if (s->msix_used && msix_enabled(d) && s->auto_int_masking) { - goto do_automask; - } - - if (s->msi_used && msi_enabled(d) && s->auto_int_masking) { - goto do_automask; - } - - return; - -do_automask: - s->interrupt_states[lidx].is_masked = true; - vmxnet3_update_interrupt_line_state(s, lidx); -} - -static bool vmxnet3_interrupt_asserted(VMXNET3State *s, int lidx) -{ - return s->interrupt_states[lidx].is_asserted; -} - -static void vmxnet3_clear_interrupt(VMXNET3State *s, int int_idx) -{ - s->interrupt_states[int_idx].is_pending = false; - if (s->auto_int_masking) { - s->interrupt_states[int_idx].is_masked = true; - } - vmxnet3_update_interrupt_line_state(s, int_idx); -} - -static void -vmxnet3_on_interrupt_mask_changed(VMXNET3State *s, int lidx, bool is_masked) -{ - s->interrupt_states[lidx].is_masked = is_masked; - vmxnet3_update_interrupt_line_state(s, lidx); -} - -static bool vmxnet3_verify_driver_magic(hwaddr dshmem) -{ - return (VMXNET3_READ_DRV_SHARED32(dshmem, magic) == VMXNET3_REV1_MAGIC); -} - -#define VMXNET3_GET_BYTE(x, byte_num) (((x) >> (byte_num)*8) & 0xFF) -#define VMXNET3_MAKE_BYTE(byte_num, val) \ - (((uint32_t)((val) & 0xFF)) << (byte_num)*8) - -static void vmxnet3_set_variable_mac(VMXNET3State *s, uint32_t h, uint32_t l) -{ - s->conf.macaddr.a[0] = VMXNET3_GET_BYTE(l, 0); - s->conf.macaddr.a[1] = VMXNET3_GET_BYTE(l, 1); - s->conf.macaddr.a[2] = VMXNET3_GET_BYTE(l, 2); - s->conf.macaddr.a[3] = VMXNET3_GET_BYTE(l, 3); - s->conf.macaddr.a[4] = VMXNET3_GET_BYTE(h, 0); - s->conf.macaddr.a[5] = VMXNET3_GET_BYTE(h, 1); - - VMW_CFPRN("Variable MAC: " VMXNET_MF, VMXNET_MA(s->conf.macaddr.a)); - - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); -} - -static uint64_t vmxnet3_get_mac_low(MACAddr *addr) -{ - return VMXNET3_MAKE_BYTE(0, addr->a[0]) | - VMXNET3_MAKE_BYTE(1, addr->a[1]) | - VMXNET3_MAKE_BYTE(2, addr->a[2]) | - VMXNET3_MAKE_BYTE(3, addr->a[3]); -} - -static uint64_t vmxnet3_get_mac_high(MACAddr *addr) -{ - return VMXNET3_MAKE_BYTE(0, addr->a[4]) | - VMXNET3_MAKE_BYTE(1, addr->a[5]); -} - -static void -vmxnet3_inc_tx_consumption_counter(VMXNET3State *s, int qidx) -{ - vmxnet3_ring_inc(&s->txq_descr[qidx].tx_ring); -} - -static inline void -vmxnet3_inc_rx_consumption_counter(VMXNET3State *s, int qidx, int ridx) -{ - vmxnet3_ring_inc(&s->rxq_descr[qidx].rx_ring[ridx]); -} - -static inline void -vmxnet3_inc_tx_completion_counter(VMXNET3State *s, int qidx) -{ - vmxnet3_ring_inc(&s->txq_descr[qidx].comp_ring); -} - -static void -vmxnet3_inc_rx_completion_counter(VMXNET3State *s, int qidx) -{ - vmxnet3_ring_inc(&s->rxq_descr[qidx].comp_ring); -} - -static void -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_t tx_ridx) -{ - struct Vmxnet3_TxCompDesc txcq_descr; - - VMXNET3_RING_DUMP(VMW_RIPRN, "TXC", qidx, &s->txq_descr[qidx].comp_ring); - - txcq_descr.txdIdx = tx_ridx; - txcq_descr.gen = vmxnet3_ring_curr_gen(&s->txq_descr[qidx].comp_ring); - - vmxnet3_ring_write_curr_cell(&s->txq_descr[qidx].comp_ring, &txcq_descr); - - /* Flush changes in TX descriptor before changing the counter value */ - smp_wmb(); - - vmxnet3_inc_tx_completion_counter(s, qidx); - vmxnet3_trigger_interrupt(s, s->txq_descr[qidx].intr_idx); -} - -static bool -vmxnet3_setup_tx_offloads(VMXNET3State *s) -{ - switch (s->offload_mode) { - case VMXNET3_OM_NONE: - vmxnet_tx_pkt_build_vheader(s->tx_pkt, false, false, 0); - break; - - case VMXNET3_OM_CSUM: - vmxnet_tx_pkt_build_vheader(s->tx_pkt, false, true, 0); - VMW_PKPRN("L4 CSO requested\n"); - break; - - case VMXNET3_OM_TSO: - vmxnet_tx_pkt_build_vheader(s->tx_pkt, true, true, - s->cso_or_gso_size); - vmxnet_tx_pkt_update_ip_checksums(s->tx_pkt); - VMW_PKPRN("GSO offload requested."); - break; - - default: - g_assert_not_reached(); - return false; - } - - return true; -} - -static void -vmxnet3_tx_retrieve_metadata(VMXNET3State *s, - const struct Vmxnet3_TxDesc *txd) -{ - s->offload_mode = txd->om; - s->cso_or_gso_size = txd->msscof; - s->tci = txd->tci; - s->needs_vlan = txd->ti; -} - -typedef enum { - VMXNET3_PKT_STATUS_OK, - VMXNET3_PKT_STATUS_ERROR, - VMXNET3_PKT_STATUS_DISCARD,/* only for tx */ - VMXNET3_PKT_STATUS_OUT_OF_BUF /* only for rx */ -} Vmxnet3PktStatus; - -static void -vmxnet3_on_tx_done_update_stats(VMXNET3State *s, int qidx, - Vmxnet3PktStatus status) -{ - size_t tot_len = vmxnet_tx_pkt_get_total_len(s->tx_pkt); - struct UPT1_TxStats *stats = &s->txq_descr[qidx].txq_stats; - - switch (status) { - case VMXNET3_PKT_STATUS_OK: - switch (vmxnet_tx_pkt_get_packet_type(s->tx_pkt)) { - case ETH_PKT_BCAST: - stats->bcastPktsTxOK++; - stats->bcastBytesTxOK += tot_len; - break; - case ETH_PKT_MCAST: - stats->mcastPktsTxOK++; - stats->mcastBytesTxOK += tot_len; - break; - case ETH_PKT_UCAST: - stats->ucastPktsTxOK++; - stats->ucastBytesTxOK += tot_len; - break; - default: - g_assert_not_reached(); - } - - if (s->offload_mode == VMXNET3_OM_TSO) { - /* - * According to VMWARE headers this statistic is a number - * of packets after segmentation but since we don't have - * this information in QEMU model, the best we can do is to - * provide number of non-segmented packets - */ - stats->TSOPktsTxOK++; - stats->TSOBytesTxOK += tot_len; - } - break; - - case VMXNET3_PKT_STATUS_DISCARD: - stats->pktsTxDiscard++; - break; - - case VMXNET3_PKT_STATUS_ERROR: - stats->pktsTxError++; - break; - - default: - g_assert_not_reached(); - } -} - -static void -vmxnet3_on_rx_done_update_stats(VMXNET3State *s, - int qidx, - Vmxnet3PktStatus status) -{ - struct UPT1_RxStats *stats = &s->rxq_descr[qidx].rxq_stats; - size_t tot_len = vmxnet_rx_pkt_get_total_len(s->rx_pkt); - - switch (status) { - case VMXNET3_PKT_STATUS_OUT_OF_BUF: - stats->pktsRxOutOfBuf++; - break; - - case VMXNET3_PKT_STATUS_ERROR: - stats->pktsRxError++; - break; - case VMXNET3_PKT_STATUS_OK: - switch (vmxnet_rx_pkt_get_packet_type(s->rx_pkt)) { - case ETH_PKT_BCAST: - stats->bcastPktsRxOK++; - stats->bcastBytesRxOK += tot_len; - break; - case ETH_PKT_MCAST: - stats->mcastPktsRxOK++; - stats->mcastBytesRxOK += tot_len; - break; - case ETH_PKT_UCAST: - stats->ucastPktsRxOK++; - stats->ucastBytesRxOK += tot_len; - break; - default: - g_assert_not_reached(); - } - - if (tot_len > s->mtu) { - stats->LROPktsRxOK++; - stats->LROBytesRxOK += tot_len; - } - break; - default: - g_assert_not_reached(); - } -} - -static inline bool -vmxnet3_pop_next_tx_descr(VMXNET3State *s, - int qidx, - struct Vmxnet3_TxDesc *txd, - uint32_t *descr_idx) -{ - Vmxnet3Ring *ring = &s->txq_descr[qidx].tx_ring; - - vmxnet3_ring_read_curr_cell(ring, txd); - if (txd->gen == vmxnet3_ring_curr_gen(ring)) { - /* Only read after generation field verification */ - smp_rmb(); - /* Re-read to be sure we got the latest version */ - vmxnet3_ring_read_curr_cell(ring, txd); - VMXNET3_RING_DUMP(VMW_RIPRN, "TX", qidx, ring); - *descr_idx = vmxnet3_ring_curr_cell_idx(ring); - vmxnet3_inc_tx_consumption_counter(s, qidx); - return true; - } - - return false; -} - -static bool -vmxnet3_send_packet(VMXNET3State *s, uint32_t qidx) -{ - Vmxnet3PktStatus status = VMXNET3_PKT_STATUS_OK; - - if (!vmxnet3_setup_tx_offloads(s)) { - status = VMXNET3_PKT_STATUS_ERROR; - goto func_exit; - } - - /* debug prints */ - vmxnet3_dump_virt_hdr(vmxnet_tx_pkt_get_vhdr(s->tx_pkt)); - vmxnet_tx_pkt_dump(s->tx_pkt); - - if (!vmxnet_tx_pkt_send(s->tx_pkt, qemu_get_queue(s->nic))) { - status = VMXNET3_PKT_STATUS_DISCARD; - goto func_exit; - } - -func_exit: - vmxnet3_on_tx_done_update_stats(s, qidx, status); - return (status == VMXNET3_PKT_STATUS_OK); -} - -static void vmxnet3_process_tx_queue(VMXNET3State *s, int qidx) -{ - struct Vmxnet3_TxDesc txd; - uint32_t txd_idx; - uint32_t data_len; - hwaddr data_pa; - - for (;;) { - if (!vmxnet3_pop_next_tx_descr(s, qidx, &txd, &txd_idx)) { - break; - } - - vmxnet3_dump_tx_descr(&txd); - - if (!s->skip_current_tx_pkt) { - data_len = (txd.len > 0) ? txd.len : VMXNET3_MAX_TX_BUF_SIZE; - data_pa = le64_to_cpu(txd.addr); - - if (!vmxnet_tx_pkt_add_raw_fragment(s->tx_pkt, - data_pa, - data_len)) { - s->skip_current_tx_pkt = true; - } - } - - if (s->tx_sop) { - vmxnet3_tx_retrieve_metadata(s, &txd); - s->tx_sop = false; - } - - if (txd.eop) { - 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); - } - - vmxnet3_send_packet(s, qidx); - } else { - vmxnet3_on_tx_done_update_stats(s, qidx, - VMXNET3_PKT_STATUS_ERROR); - } - - vmxnet3_complete_packet(s, qidx, txd_idx); - s->tx_sop = true; - s->skip_current_tx_pkt = false; - vmxnet_tx_pkt_reset(s->tx_pkt); - } - } -} - -static inline void -vmxnet3_read_next_rx_descr(VMXNET3State *s, int qidx, int ridx, - struct Vmxnet3_RxDesc *dbuf, uint32_t *didx) -{ - Vmxnet3Ring *ring = &s->rxq_descr[qidx].rx_ring[ridx]; - *didx = vmxnet3_ring_curr_cell_idx(ring); - vmxnet3_ring_read_curr_cell(ring, dbuf); -} - -static inline uint8_t -vmxnet3_get_rx_ring_gen(VMXNET3State *s, int qidx, int ridx) -{ - return s->rxq_descr[qidx].rx_ring[ridx].gen; -} - -static inline hwaddr -vmxnet3_pop_rxc_descr(VMXNET3State *s, int qidx, uint32_t *descr_gen) -{ - uint8_t ring_gen; - struct Vmxnet3_RxCompDesc rxcd; - - hwaddr daddr = - vmxnet3_ring_curr_cell_pa(&s->rxq_descr[qidx].comp_ring); - - cpu_physical_memory_read(daddr, &rxcd, sizeof(struct Vmxnet3_RxCompDesc)); - ring_gen = vmxnet3_ring_curr_gen(&s->rxq_descr[qidx].comp_ring); - - if (rxcd.gen != ring_gen) { - *descr_gen = ring_gen; - vmxnet3_inc_rx_completion_counter(s, qidx); - return daddr; - } - - return 0; -} - -static inline void -vmxnet3_revert_rxc_descr(VMXNET3State *s, int qidx) -{ - vmxnet3_dec_rx_completion_counter(s, qidx); -} - -#define RXQ_IDX (0) -#define RX_HEAD_BODY_RING (0) -#define RX_BODY_ONLY_RING (1) - -static bool -vmxnet3_get_next_head_rx_descr(VMXNET3State *s, - struct Vmxnet3_RxDesc *descr_buf, - uint32_t *descr_idx, - uint32_t *ridx) -{ - for (;;) { - uint32_t ring_gen; - vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING, - descr_buf, descr_idx); - - /* If no more free descriptors - return */ - ring_gen = vmxnet3_get_rx_ring_gen(s, RXQ_IDX, RX_HEAD_BODY_RING); - if (descr_buf->gen != ring_gen) { - return false; - } - - /* Only read after generation field verification */ - smp_rmb(); - /* Re-read to be sure we got the latest version */ - vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING, - descr_buf, descr_idx); - - /* Mark current descriptor as used/skipped */ - vmxnet3_inc_rx_consumption_counter(s, RXQ_IDX, RX_HEAD_BODY_RING); - - /* If this is what we are looking for - return */ - if (descr_buf->btype == VMXNET3_RXD_BTYPE_HEAD) { - *ridx = RX_HEAD_BODY_RING; - return true; - } - } -} - -static bool -vmxnet3_get_next_body_rx_descr(VMXNET3State *s, - struct Vmxnet3_RxDesc *d, - uint32_t *didx, - uint32_t *ridx) -{ - vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING, d, didx); - - /* Try to find corresponding descriptor in head/body ring */ - if (d->gen == vmxnet3_get_rx_ring_gen(s, RXQ_IDX, RX_HEAD_BODY_RING)) { - /* Only read after generation field verification */ - smp_rmb(); - /* Re-read to be sure we got the latest version */ - vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING, d, didx); - if (d->btype == VMXNET3_RXD_BTYPE_BODY) { - vmxnet3_inc_rx_consumption_counter(s, RXQ_IDX, RX_HEAD_BODY_RING); - *ridx = RX_HEAD_BODY_RING; - return true; - } - } - - /* - * If there is no free descriptors on head/body ring or next free - * descriptor is a head descriptor switch to body only ring - */ - vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_BODY_ONLY_RING, d, didx); - - /* If no more free descriptors - return */ - if (d->gen == vmxnet3_get_rx_ring_gen(s, RXQ_IDX, RX_BODY_ONLY_RING)) { - /* Only read after generation field verification */ - smp_rmb(); - /* Re-read to be sure we got the latest version */ - vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_BODY_ONLY_RING, d, didx); - assert(d->btype == VMXNET3_RXD_BTYPE_BODY); - *ridx = RX_BODY_ONLY_RING; - vmxnet3_inc_rx_consumption_counter(s, RXQ_IDX, RX_BODY_ONLY_RING); - return true; - } - - return false; -} - -static inline bool -vmxnet3_get_next_rx_descr(VMXNET3State *s, bool is_head, - struct Vmxnet3_RxDesc *descr_buf, - uint32_t *descr_idx, - uint32_t *ridx) -{ - if (is_head || !s->rx_packets_compound) { - return vmxnet3_get_next_head_rx_descr(s, descr_buf, descr_idx, ridx); - } else { - return vmxnet3_get_next_body_rx_descr(s, descr_buf, descr_idx, ridx); - } -} - -/* In case packet was csum offloaded (either NEEDS_CSUM or DATA_VALID), - * the implementation always passes an RxCompDesc with a "Checksum - * calculated and found correct" to the OS (cnc=0 and tuc=1, see - * vmxnet3_rx_update_descr). This emulates the observed ESXi behavior. - * - * Therefore, if packet has the NEEDS_CSUM set, we must calculate - * and place a fully computed checksum into the tcp/udp header. - * Otherwise, the OS driver will receive a checksum-correct indication - * (CHECKSUM_UNNECESSARY), but with the actual tcp/udp checksum field - * having just the pseudo header csum value. - * - * While this is not a problem if packet is destined for local delivery, - * in the case the host OS performs forwarding, it will forward an - * incorrectly checksummed packet. - */ -static void vmxnet3_rx_need_csum_calculate(struct VmxnetRxPkt *pkt, - const void *pkt_data, - size_t pkt_len) -{ - struct virtio_net_hdr *vhdr; - bool isip4, isip6, istcp, isudp; - uint8_t *data; - int len; - - if (!vmxnet_rx_pkt_has_virt_hdr(pkt)) { - return; - } - - vhdr = vmxnet_rx_pkt_get_vhdr(pkt); - if (!VMXNET_FLAG_IS_SET(vhdr->flags, VIRTIO_NET_HDR_F_NEEDS_CSUM)) { - return; - } - - vmxnet_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); - if (!(isip4 || isip6) || !(istcp || isudp)) { - return; - } - - vmxnet3_dump_virt_hdr(vhdr); - - /* 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:%zu < csum_start(%d) + csum_offset(%d) + 2, " - "cannot calculate checksum", - pkt_len, vhdr->csum_start, vhdr->csum_offset); - return; - } - - data = (uint8_t *)pkt_data + vhdr->csum_start; - len = pkt_len - vhdr->csum_start; - /* Put the checksum obtained into the packet */ - stw_be_p(data + vhdr->csum_offset, net_raw_checksum(data, len)); - - vhdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM; - vhdr->flags |= VIRTIO_NET_HDR_F_DATA_VALID; -} - -static void vmxnet3_rx_update_descr(struct VmxnetRxPkt *pkt, - struct Vmxnet3_RxCompDesc *rxcd) -{ - int csum_ok, is_gso; - bool isip4, isip6, istcp, isudp; - struct virtio_net_hdr *vhdr; - uint8_t offload_type; - - if (vmxnet_rx_pkt_is_vlan_stripped(pkt)) { - rxcd->ts = 1; - rxcd->tci = vmxnet_rx_pkt_get_vlan_tag(pkt); - } - - if (!vmxnet_rx_pkt_has_virt_hdr(pkt)) { - goto nocsum; - } - - vhdr = vmxnet_rx_pkt_get_vhdr(pkt); - /* - * Checksum is valid when lower level tell so or when lower level - * requires checksum offload telling that packet produced/bridged - * locally and did travel over network after last checksum calculation - * or production - */ - csum_ok = VMXNET_FLAG_IS_SET(vhdr->flags, VIRTIO_NET_HDR_F_DATA_VALID) || - VMXNET_FLAG_IS_SET(vhdr->flags, VIRTIO_NET_HDR_F_NEEDS_CSUM); - - offload_type = vhdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN; - is_gso = (offload_type != VIRTIO_NET_HDR_GSO_NONE) ? 1 : 0; - - if (!csum_ok && !is_gso) { - goto nocsum; - } - - vmxnet_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp); - if ((!istcp && !isudp) || (!isip4 && !isip6)) { - goto nocsum; - } - - rxcd->cnc = 0; - rxcd->v4 = isip4 ? 1 : 0; - rxcd->v6 = isip6 ? 1 : 0; - rxcd->tcp = istcp ? 1 : 0; - rxcd->udp = isudp ? 1 : 0; - rxcd->fcs = rxcd->tuc = rxcd->ipc = 1; - return; - -nocsum: - rxcd->cnc = 1; - return; -} - -static void -vmxnet3_physical_memory_writev(const struct iovec *iov, - size_t start_iov_off, - hwaddr target_addr, - size_t bytes_to_copy) -{ - size_t curr_off = 0; - size_t copied = 0; - - while (bytes_to_copy) { - if (start_iov_off < (curr_off + iov->iov_len)) { - size_t chunk_len = - MIN((curr_off + iov->iov_len) - start_iov_off, bytes_to_copy); - - cpu_physical_memory_write(target_addr + copied, - iov->iov_base + start_iov_off - curr_off, - chunk_len); - - copied += chunk_len; - start_iov_off += chunk_len; - curr_off = start_iov_off; - bytes_to_copy -= chunk_len; - } else { - curr_off += iov->iov_len; - } - iov++; - } -} - -static bool -vmxnet3_indicate_packet(VMXNET3State *s) -{ - struct Vmxnet3_RxDesc rxd; - bool is_head = true; - uint32_t rxd_idx; - uint32_t rx_ridx = 0; - - struct Vmxnet3_RxCompDesc rxcd; - uint32_t new_rxcd_gen = VMXNET3_INIT_GEN; - hwaddr new_rxcd_pa = 0; - hwaddr ready_rxcd_pa = 0; - struct iovec *data = vmxnet_rx_pkt_get_iovec(s->rx_pkt); - size_t bytes_copied = 0; - size_t bytes_left = vmxnet_rx_pkt_get_total_len(s->rx_pkt); - uint16_t num_frags = 0; - size_t chunk_size; - - vmxnet_rx_pkt_dump(s->rx_pkt); - - while (bytes_left > 0) { - - /* cannot add more frags to packet */ - if (num_frags == s->max_rx_frags) { - break; - } - - new_rxcd_pa = vmxnet3_pop_rxc_descr(s, RXQ_IDX, &new_rxcd_gen); - if (!new_rxcd_pa) { - break; - } - - if (!vmxnet3_get_next_rx_descr(s, is_head, &rxd, &rxd_idx, &rx_ridx)) { - break; - } - - chunk_size = MIN(bytes_left, rxd.len); - vmxnet3_physical_memory_writev(data, bytes_copied, - le64_to_cpu(rxd.addr), chunk_size); - bytes_copied += chunk_size; - bytes_left -= chunk_size; - - vmxnet3_dump_rx_descr(&rxd); - - if (ready_rxcd_pa != 0) { - cpu_physical_memory_write(ready_rxcd_pa, &rxcd, sizeof(rxcd)); - } - - memset(&rxcd, 0, sizeof(struct Vmxnet3_RxCompDesc)); - rxcd.rxdIdx = rxd_idx; - rxcd.len = chunk_size; - rxcd.sop = is_head; - rxcd.gen = new_rxcd_gen; - rxcd.rqID = RXQ_IDX + rx_ridx * s->rxq_num; - - if (bytes_left == 0) { - vmxnet3_rx_update_descr(s->rx_pkt, &rxcd); - } - - VMW_RIPRN("RX Completion descriptor: rxRing: %lu rxIdx %lu len %lu " - "sop %d csum_correct %lu", - (unsigned long) rx_ridx, - (unsigned long) rxcd.rxdIdx, - (unsigned long) rxcd.len, - (int) rxcd.sop, - (unsigned long) rxcd.tuc); - - is_head = false; - ready_rxcd_pa = new_rxcd_pa; - new_rxcd_pa = 0; - num_frags++; - } - - if (ready_rxcd_pa != 0) { - rxcd.eop = 1; - rxcd.err = (bytes_left != 0); - cpu_physical_memory_write(ready_rxcd_pa, &rxcd, sizeof(rxcd)); - - /* Flush RX descriptor changes */ - smp_wmb(); - } - - if (new_rxcd_pa != 0) { - vmxnet3_revert_rxc_descr(s, RXQ_IDX); - } - - vmxnet3_trigger_interrupt(s, s->rxq_descr[RXQ_IDX].intr_idx); - - if (bytes_left == 0) { - vmxnet3_on_rx_done_update_stats(s, RXQ_IDX, VMXNET3_PKT_STATUS_OK); - return true; - } else if (num_frags == s->max_rx_frags) { - vmxnet3_on_rx_done_update_stats(s, RXQ_IDX, VMXNET3_PKT_STATUS_ERROR); - return false; - } else { - vmxnet3_on_rx_done_update_stats(s, RXQ_IDX, - VMXNET3_PKT_STATUS_OUT_OF_BUF); - return false; - } -} - -static void -vmxnet3_io_bar0_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - VMXNET3State *s = opaque; - - if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_TXPROD, - VMXNET3_DEVICE_MAX_TX_QUEUES, VMXNET3_REG_ALIGN)) { - int tx_queue_idx = - VMW_MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_TXPROD, - VMXNET3_REG_ALIGN); - assert(tx_queue_idx <= s->txq_num); - vmxnet3_process_tx_queue(s, tx_queue_idx); - return; - } - - if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_IMR, - VMXNET3_MAX_INTRS, VMXNET3_REG_ALIGN)) { - int l = VMW_MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_IMR, - VMXNET3_REG_ALIGN); - - VMW_CBPRN("Interrupt mask for line %d written: 0x%" PRIx64, l, val); - - vmxnet3_on_interrupt_mask_changed(s, l, val); - return; - } - - if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_RXPROD, - VMXNET3_DEVICE_MAX_RX_QUEUES, VMXNET3_REG_ALIGN) || - VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_RXPROD2, - VMXNET3_DEVICE_MAX_RX_QUEUES, VMXNET3_REG_ALIGN)) { - return; - } - - VMW_WRPRN("BAR0 unknown write [%" PRIx64 "] = %" PRIx64 ", size %d", - (uint64_t) addr, val, size); -} - -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)) { - 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); - return 0; -} - -static void vmxnet3_reset_interrupt_states(VMXNET3State *s) -{ - int i; - for (i = 0; i < ARRAY_SIZE(s->interrupt_states); i++) { - s->interrupt_states[i].is_asserted = false; - s->interrupt_states[i].is_pending = false; - s->interrupt_states[i].is_masked = true; - } -} - -static void vmxnet3_reset_mac(VMXNET3State *s) -{ - memcpy(&s->conf.macaddr.a, &s->perm_mac.a, sizeof(s->perm_mac.a)); - VMW_CFPRN("MAC address set to: " VMXNET_MF, VMXNET_MA(s->conf.macaddr.a)); -} - -static void vmxnet3_deactivate_device(VMXNET3State *s) -{ - 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) -{ - VMW_CBPRN("Resetting vmxnet3..."); - - vmxnet3_deactivate_device(s); - vmxnet3_reset_interrupt_states(s); - s->drv_shmem = 0; - s->tx_sop = true; - s->skip_current_tx_pkt = false; -} - -static void vmxnet3_update_rx_mode(VMXNET3State *s) -{ - s->rx_mode = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, - devRead.rxFilterConf.rxMode); - VMW_CFPRN("RX mode: 0x%08X", s->rx_mode); -} - -static void vmxnet3_update_vlan_filters(VMXNET3State *s) -{ - int i; - - /* Copy configuration from shared memory */ - VMXNET3_READ_DRV_SHARED(s->drv_shmem, - devRead.rxFilterConf.vfTable, - s->vlan_table, - sizeof(s->vlan_table)); - - /* Invert byte order when needed */ - for (i = 0; i < ARRAY_SIZE(s->vlan_table); i++) { - s->vlan_table[i] = le32_to_cpu(s->vlan_table[i]); - } - - /* Dump configuration for debugging purposes */ - VMW_CFPRN("Configured VLANs:"); - for (i = 0; i < sizeof(s->vlan_table) * 8; i++) { - if (VMXNET3_VFTABLE_ENTRY_IS_SET(s->vlan_table, i)) { - VMW_CFPRN("\tVLAN %d is present", i); - } - } -} - -static void vmxnet3_update_mcast_filters(VMXNET3State *s) -{ - uint16_t list_bytes = - VMXNET3_READ_DRV_SHARED16(s->drv_shmem, - devRead.rxFilterConf.mfTableLen); - - s->mcast_list_len = list_bytes / sizeof(s->mcast_list[0]); - - s->mcast_list = g_realloc(s->mcast_list, list_bytes); - if (!s->mcast_list) { - if (s->mcast_list_len == 0) { - VMW_CFPRN("Current multicast list is empty"); - } else { - VMW_ERPRN("Failed to allocate multicast list of %d elements", - s->mcast_list_len); - } - s->mcast_list_len = 0; - } else { - int i; - hwaddr mcast_list_pa = - VMXNET3_READ_DRV_SHARED64(s->drv_shmem, - devRead.rxFilterConf.mfTablePA); - - cpu_physical_memory_read(mcast_list_pa, s->mcast_list, list_bytes); - VMW_CFPRN("Current multicast list len is %d:", s->mcast_list_len); - for (i = 0; i < s->mcast_list_len; i++) { - VMW_CFPRN("\t" VMXNET_MF, VMXNET_MA(s->mcast_list[i].a)); - } - } -} - -static void vmxnet3_setup_rx_filtering(VMXNET3State *s) -{ - vmxnet3_update_rx_mode(s); - vmxnet3_update_vlan_filters(s); - vmxnet3_update_mcast_filters(s); -} - -static uint32_t vmxnet3_get_interrupt_config(VMXNET3State *s) -{ - uint32_t interrupt_mode = VMXNET3_IT_AUTO | (VMXNET3_IMM_AUTO << 2); - VMW_CFPRN("Interrupt config is 0x%X", interrupt_mode); - return interrupt_mode; -} - -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, - sizeof(s->txq_descr[i].txq_stats)); - } - - for (i = 0; i < s->rxq_num; i++) { - cpu_physical_memory_write(s->rxq_descr[i].rx_stats_pa, - &s->rxq_descr[i].rxq_stats, - sizeof(s->rxq_descr[i].rxq_stats)); - } -} - -static void vmxnet3_adjust_by_guest_type(VMXNET3State *s) -{ - struct Vmxnet3_GOSInfo gos; - - VMXNET3_READ_DRV_SHARED(s->drv_shmem, devRead.misc.driverInfo.gos, - &gos, sizeof(gos)); - s->rx_packets_compound = - (gos.gosType == VMXNET3_GOS_TYPE_WIN) ? false : true; - - VMW_CFPRN("Guest type specifics: RXCOMPOUND: %d", s->rx_packets_compound); -} - -static void -vmxnet3_dump_conf_descr(const char *name, - struct Vmxnet3_VariableLenConfDesc *pm_descr) -{ - VMW_CFPRN("%s descriptor dump: Version %u, Length %u", - name, pm_descr->confVer, pm_descr->confLen); - -}; - -static void vmxnet3_update_pm_state(VMXNET3State *s) -{ - struct Vmxnet3_VariableLenConfDesc pm_descr; - - pm_descr.confLen = - VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.pmConfDesc.confLen); - pm_descr.confVer = - VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.pmConfDesc.confVer); - pm_descr.confPA = - VMXNET3_READ_DRV_SHARED64(s->drv_shmem, devRead.pmConfDesc.confPA); - - vmxnet3_dump_conf_descr("PM State", &pm_descr); -} - -static void vmxnet3_update_features(VMXNET3State *s) -{ - uint32_t guest_features; - int rxcso_supported; - - guest_features = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, - devRead.misc.uptFeatures); - - rxcso_supported = VMXNET_FLAG_IS_SET(guest_features, UPT1_F_RXCSUM); - s->rx_vlan_stripping = VMXNET_FLAG_IS_SET(guest_features, UPT1_F_RXVLAN); - s->lro_supported = VMXNET_FLAG_IS_SET(guest_features, UPT1_F_LRO); - - VMW_CFPRN("Features configuration: LRO: %d, RXCSUM: %d, VLANSTRIP: %d", - s->lro_supported, rxcso_supported, - s->rx_vlan_stripping); - if (s->peer_has_vhdr) { - qemu_set_offload(qemu_get_queue(s->nic)->peer, - rxcso_supported, - s->lro_supported, - s->lro_supported, - 0, - 0); - } -} - -static bool vmxnet3_verify_intx(VMXNET3State *s, int intx) -{ - return s->msix_used || s->msi_used || (intx == - (pci_get_byte(s->parent_obj.config + PCI_INTERRUPT_PIN) - 1)); -} - -static void vmxnet3_validate_interrupt_idx(bool is_msix, int idx) -{ - int max_ints = is_msix ? VMXNET3_MAX_INTRS : VMXNET3_MAX_NMSIX_INTRS; - if (idx >= max_ints) { - hw_error("Bad interrupt index: %d\n", idx); - } -} - -static void vmxnet3_validate_interrupts(VMXNET3State *s) -{ - int i; - - VMW_CFPRN("Verifying event interrupt index (%d)", s->event_int_idx); - vmxnet3_validate_interrupt_idx(s->msix_used, s->event_int_idx); - - for (i = 0; i < s->txq_num; i++) { - int idx = s->txq_descr[i].intr_idx; - VMW_CFPRN("Verifying TX queue %d interrupt index (%d)", i, idx); - vmxnet3_validate_interrupt_idx(s->msix_used, idx); - } - - for (i = 0; i < s->rxq_num; i++) { - int idx = s->rxq_descr[i].intr_idx; - VMW_CFPRN("Verifying RX queue %d interrupt index (%d)", i, idx); - vmxnet3_validate_interrupt_idx(s->msix_used, idx); - } -} - -static void vmxnet3_validate_queues(VMXNET3State *s) -{ - /* - * txq_num and rxq_num are total number of queues - * configured by guest. These numbers must not - * exceed corresponding maximal values. - */ - - if (s->txq_num > VMXNET3_DEVICE_MAX_TX_QUEUES) { - hw_error("Bad TX queues number: %d\n", s->txq_num); - } - - if (s->rxq_num > VMXNET3_DEVICE_MAX_RX_QUEUES) { - hw_error("Bad RX queues number: %d\n", s->rxq_num); - } -} - -static void vmxnet3_activate_device(VMXNET3State *s) -{ - int i; - static const uint32_t VMXNET3_DEF_TX_THRESHOLD = 1; - hwaddr qdescr_table_pa; - uint64_t pa; - uint32_t size; - - /* Verify configuration consistency */ - if (!vmxnet3_verify_driver_magic(s->drv_shmem)) { - VMW_ERPRN("Device configuration received from driver is invalid"); - 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); - vmxnet3_setup_rx_filtering(s); - /* Cache fields from shared memory */ - s->mtu = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.misc.mtu); - VMW_CFPRN("MTU is %u", s->mtu); - - s->max_rx_frags = - VMXNET3_READ_DRV_SHARED16(s->drv_shmem, devRead.misc.maxNumRxSG); - - if (s->max_rx_frags == 0) { - s->max_rx_frags = 1; - } - - VMW_CFPRN("Max RX fragments is %u", s->max_rx_frags); - - s->event_int_idx = - VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.intrConf.eventIntrIdx); - assert(vmxnet3_verify_intx(s, s->event_int_idx)); - VMW_CFPRN("Events interrupt line is %u", s->event_int_idx); - - s->auto_int_masking = - VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.intrConf.autoMask); - VMW_CFPRN("Automatic interrupt masking is %d", (int)s->auto_int_masking); - - s->txq_num = - VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.misc.numTxQueues); - s->rxq_num = - VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.misc.numRxQueues); - - VMW_CFPRN("Number of TX/RX queues %u/%u", s->txq_num, s->rxq_num); - vmxnet3_validate_queues(s); - - qdescr_table_pa = - VMXNET3_READ_DRV_SHARED64(s->drv_shmem, devRead.misc.queueDescPA); - VMW_CFPRN("TX queues descriptors table is at 0x%" PRIx64, qdescr_table_pa); - - /* - * Worst-case scenario is a packet that holds all TX rings space so - * we calculate total size of all TX rings for max TX fragments number - */ - s->max_tx_frags = 0; - - /* TX queues */ - for (i = 0; i < s->txq_num; i++) { - hwaddr qdescr_pa = - qdescr_table_pa + i * sizeof(struct Vmxnet3_TxQueueDesc); - - /* Read interrupt number for this TX queue */ - s->txq_descr[i].intr_idx = - VMXNET3_READ_TX_QUEUE_DESCR8(qdescr_pa, conf.intrIdx); - assert(vmxnet3_verify_intx(s, s->txq_descr[i].intr_idx)); - - VMW_CFPRN("TX Queue %d interrupt: %d", i, s->txq_descr[i].intr_idx); - - /* Read rings memory locations for TX queues */ - pa = VMXNET3_READ_TX_QUEUE_DESCR64(qdescr_pa, conf.txRingBasePA); - size = VMXNET3_READ_TX_QUEUE_DESCR32(qdescr_pa, conf.txRingSize); - - vmxnet3_ring_init(&s->txq_descr[i].tx_ring, pa, size, - sizeof(struct Vmxnet3_TxDesc), false); - VMXNET3_RING_DUMP(VMW_CFPRN, "TX", i, &s->txq_descr[i].tx_ring); - - s->max_tx_frags += size; - - /* TXC ring */ - pa = VMXNET3_READ_TX_QUEUE_DESCR64(qdescr_pa, conf.compRingBasePA); - size = VMXNET3_READ_TX_QUEUE_DESCR32(qdescr_pa, conf.compRingSize); - vmxnet3_ring_init(&s->txq_descr[i].comp_ring, pa, size, - sizeof(struct Vmxnet3_TxCompDesc), true); - VMXNET3_RING_DUMP(VMW_CFPRN, "TXC", i, &s->txq_descr[i].comp_ring); - - s->txq_descr[i].tx_stats_pa = - qdescr_pa + offsetof(struct Vmxnet3_TxQueueDesc, stats); - - memset(&s->txq_descr[i].txq_stats, 0, - sizeof(s->txq_descr[i].txq_stats)); - - /* Fill device-managed parameters for queues */ - VMXNET3_WRITE_TX_QUEUE_DESCR32(qdescr_pa, - ctrl.txThreshold, - VMXNET3_DEF_TX_THRESHOLD); - } - - /* Preallocate TX packet wrapper */ - VMW_CFPRN("Max TX fragments is %u", s->max_tx_frags); - vmxnet_tx_pkt_init(&s->tx_pkt, s->max_tx_frags, s->peer_has_vhdr); - vmxnet_rx_pkt_init(&s->rx_pkt, s->peer_has_vhdr); - - /* Read rings memory locations for RX queues */ - for (i = 0; i < s->rxq_num; i++) { - int j; - hwaddr qd_pa = - qdescr_table_pa + s->txq_num * sizeof(struct Vmxnet3_TxQueueDesc) + - i * sizeof(struct Vmxnet3_RxQueueDesc); - - /* Read interrupt number for this RX queue */ - s->rxq_descr[i].intr_idx = - VMXNET3_READ_TX_QUEUE_DESCR8(qd_pa, conf.intrIdx); - assert(vmxnet3_verify_intx(s, s->rxq_descr[i].intr_idx)); - - VMW_CFPRN("RX Queue %d interrupt: %d", i, s->rxq_descr[i].intr_idx); - - /* Read rings memory locations */ - for (j = 0; j < VMXNET3_RX_RINGS_PER_QUEUE; j++) { - /* RX rings */ - pa = VMXNET3_READ_RX_QUEUE_DESCR64(qd_pa, conf.rxRingBasePA[j]); - size = VMXNET3_READ_RX_QUEUE_DESCR32(qd_pa, conf.rxRingSize[j]); - vmxnet3_ring_init(&s->rxq_descr[i].rx_ring[j], pa, size, - sizeof(struct Vmxnet3_RxDesc), false); - VMW_CFPRN("RX queue %d:%d: Base: %" PRIx64 ", Size: %d", - i, j, pa, size); - } - - /* RXC ring */ - pa = VMXNET3_READ_RX_QUEUE_DESCR64(qd_pa, conf.compRingBasePA); - size = VMXNET3_READ_RX_QUEUE_DESCR32(qd_pa, conf.compRingSize); - vmxnet3_ring_init(&s->rxq_descr[i].comp_ring, pa, size, - sizeof(struct Vmxnet3_RxCompDesc), true); - VMW_CFPRN("RXC queue %d: Base: %" PRIx64 ", Size: %d", i, pa, size); - - s->rxq_descr[i].rx_stats_pa = - qd_pa + offsetof(struct Vmxnet3_RxQueueDesc, stats); - memset(&s->rxq_descr[i].rxq_stats, 0, - sizeof(s->rxq_descr[i].rxq_stats)); - } - - vmxnet3_validate_interrupts(s); - - /* Make sure everything is in place before device activation */ - smp_wmb(); - - vmxnet3_reset_mac(s); - - s->device_active = true; -} - -static void vmxnet3_handle_command(VMXNET3State *s, uint64_t cmd) -{ - s->last_command = cmd; - - switch (cmd) { - case VMXNET3_CMD_GET_PERM_MAC_HI: - VMW_CBPRN("Set: Get upper part of permanent MAC"); - break; - - case VMXNET3_CMD_GET_PERM_MAC_LO: - VMW_CBPRN("Set: Get lower part of permanent MAC"); - break; - - case VMXNET3_CMD_GET_STATS: - VMW_CBPRN("Set: Get device statistics"); - vmxnet3_fill_stats(s); - break; - - case VMXNET3_CMD_ACTIVATE_DEV: - VMW_CBPRN("Set: Activating vmxnet3 device"); - vmxnet3_activate_device(s); - break; - - case VMXNET3_CMD_UPDATE_RX_MODE: - VMW_CBPRN("Set: Update rx mode"); - vmxnet3_update_rx_mode(s); - break; - - case VMXNET3_CMD_UPDATE_VLAN_FILTERS: - VMW_CBPRN("Set: Update VLAN filters"); - vmxnet3_update_vlan_filters(s); - break; - - case VMXNET3_CMD_UPDATE_MAC_FILTERS: - VMW_CBPRN("Set: Update MAC filters"); - vmxnet3_update_mcast_filters(s); - break; - - case VMXNET3_CMD_UPDATE_FEATURE: - VMW_CBPRN("Set: Update features"); - vmxnet3_update_features(s); - break; - - case VMXNET3_CMD_UPDATE_PMCFG: - VMW_CBPRN("Set: Update power management config"); - vmxnet3_update_pm_state(s); - break; - - case VMXNET3_CMD_GET_LINK: - VMW_CBPRN("Set: Get link"); - break; - - case VMXNET3_CMD_RESET_DEV: - VMW_CBPRN("Set: Reset device"); - vmxnet3_reset(s); - break; - - case VMXNET3_CMD_QUIESCE_DEV: - VMW_CBPRN("Set: VMXNET3_CMD_QUIESCE_DEV - deactivate the device"); - vmxnet3_deactivate_device(s); - break; - - case VMXNET3_CMD_GET_CONF_INTR: - 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; - } -} - -static uint64_t vmxnet3_get_command_status(VMXNET3State *s) -{ - uint64_t ret; - - switch (s->last_command) { - case VMXNET3_CMD_ACTIVATE_DEV: - 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; - - case VMXNET3_CMD_GET_LINK: - ret = s->link_status_and_speed; - VMW_CFPRN("Link and speed: %" PRIx64, ret); - break; - - case VMXNET3_CMD_GET_PERM_MAC_LO: - ret = vmxnet3_get_mac_low(&s->perm_mac); - break; - - case VMXNET3_CMD_GET_PERM_MAC_HI: - ret = vmxnet3_get_mac_high(&s->perm_mac); - break; - - case VMXNET3_CMD_GET_CONF_INTR: - 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 = 0; - break; - } - - return ret; -} - -static void vmxnet3_set_events(VMXNET3State *s, uint32_t val) -{ - uint32_t events; - - VMW_CBPRN("Setting events: 0x%x", val); - events = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, ecr) | val; - VMXNET3_WRITE_DRV_SHARED32(s->drv_shmem, ecr, events); -} - -static void vmxnet3_ack_events(VMXNET3State *s, uint32_t val) -{ - uint32_t events; - - VMW_CBPRN("Clearing events: 0x%x", val); - events = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, ecr) & ~val; - VMXNET3_WRITE_DRV_SHARED32(s->drv_shmem, ecr, events); -} - -static void -vmxnet3_io_bar1_write(void *opaque, - hwaddr addr, - uint64_t val, - unsigned size) -{ - VMXNET3State *s = opaque; - - switch (addr) { - /* Vmxnet3 Revision Report Selection */ - case VMXNET3_REG_VRRS: - VMW_CBPRN("Write BAR1 [VMXNET3_REG_VRRS] = %" PRIx64 ", size %d", - val, size); - break; - - /* UPT Version Report Selection */ - case VMXNET3_REG_UVRS: - VMW_CBPRN("Write BAR1 [VMXNET3_REG_UVRS] = %" PRIx64 ", size %d", - val, size); - break; - - /* Driver Shared Address Low */ - case VMXNET3_REG_DSAL: - VMW_CBPRN("Write BAR1 [VMXNET3_REG_DSAL] = %" PRIx64 ", size %d", - val, size); - /* - * Guest driver will first write the low part of the shared - * memory address. We save it to temp variable and set the - * shared address only after we get the high part - */ - if (val == 0) { - vmxnet3_deactivate_device(s); - } - s->temp_shared_guest_driver_memory = val; - s->drv_shmem = 0; - break; - - /* Driver Shared Address High */ - case VMXNET3_REG_DSAH: - VMW_CBPRN("Write BAR1 [VMXNET3_REG_DSAH] = %" PRIx64 ", size %d", - val, size); - /* - * Set the shared memory between guest driver and device. - * We already should have low address part. - */ - s->drv_shmem = s->temp_shared_guest_driver_memory | (val << 32); - break; - - /* Command */ - case VMXNET3_REG_CMD: - VMW_CBPRN("Write BAR1 [VMXNET3_REG_CMD] = %" PRIx64 ", size %d", - val, size); - vmxnet3_handle_command(s, val); - break; - - /* MAC Address Low */ - case VMXNET3_REG_MACL: - VMW_CBPRN("Write BAR1 [VMXNET3_REG_MACL] = %" PRIx64 ", size %d", - val, size); - s->temp_mac = val; - break; - - /* MAC Address High */ - case VMXNET3_REG_MACH: - VMW_CBPRN("Write BAR1 [VMXNET3_REG_MACH] = %" PRIx64 ", size %d", - val, size); - vmxnet3_set_variable_mac(s, val, s->temp_mac); - break; - - /* Interrupt Cause Register */ - case VMXNET3_REG_ICR: - VMW_CBPRN("Write BAR1 [VMXNET3_REG_ICR] = %" PRIx64 ", size %d", - val, size); - g_assert_not_reached(); - break; - - /* Event Cause Register */ - case VMXNET3_REG_ECR: - VMW_CBPRN("Write BAR1 [VMXNET3_REG_ECR] = %" PRIx64 ", size %d", - val, size); - vmxnet3_ack_events(s, val); - break; - - default: - VMW_CBPRN("Unknown Write to BAR1 [%" PRIx64 "] = %" PRIx64 ", size %d", - addr, val, size); - break; - } -} - -static uint64_t -vmxnet3_io_bar1_read(void *opaque, hwaddr addr, unsigned size) -{ - VMXNET3State *s = opaque; - uint64_t ret = 0; - - switch (addr) { - /* Vmxnet3 Revision Report Selection */ - case VMXNET3_REG_VRRS: - VMW_CBPRN("Read BAR1 [VMXNET3_REG_VRRS], size %d", size); - ret = VMXNET3_DEVICE_REVISION; - break; - - /* UPT Version Report Selection */ - case VMXNET3_REG_UVRS: - VMW_CBPRN("Read BAR1 [VMXNET3_REG_UVRS], size %d", size); - ret = VMXNET3_UPT_REVISION; - break; - - /* Command */ - case VMXNET3_REG_CMD: - VMW_CBPRN("Read BAR1 [VMXNET3_REG_CMD], size %d", size); - ret = vmxnet3_get_command_status(s); - break; - - /* MAC Address Low */ - case VMXNET3_REG_MACL: - VMW_CBPRN("Read BAR1 [VMXNET3_REG_MACL], size %d", size); - ret = vmxnet3_get_mac_low(&s->conf.macaddr); - break; - - /* MAC Address High */ - case VMXNET3_REG_MACH: - VMW_CBPRN("Read BAR1 [VMXNET3_REG_MACH], size %d", size); - ret = vmxnet3_get_mac_high(&s->conf.macaddr); - break; - - /* - * Interrupt Cause Register - * Used for legacy interrupts only so interrupt index always 0 - */ - case VMXNET3_REG_ICR: - VMW_CBPRN("Read BAR1 [VMXNET3_REG_ICR], size %d", size); - if (vmxnet3_interrupt_asserted(s, 0)) { - vmxnet3_clear_interrupt(s, 0); - ret = true; - } else { - ret = false; - } - break; - - default: - VMW_CBPRN("Unknow read BAR1[%" PRIx64 "], %d bytes", addr, size); - break; - } - - return ret; -} - -static int -vmxnet3_can_receive(NetClientState *nc) -{ - VMXNET3State *s = qemu_get_nic_opaque(nc); - return s->device_active && - VMXNET_FLAG_IS_SET(s->link_status_and_speed, VMXNET3_LINK_STATUS_UP); -} - -static inline bool -vmxnet3_is_registered_vlan(VMXNET3State *s, const void *data) -{ - uint16_t vlan_tag = eth_get_pkt_tci(data) & VLAN_VID_MASK; - if (IS_SPECIAL_VLAN_ID(vlan_tag)) { - return true; - } - - return VMXNET3_VFTABLE_ENTRY_IS_SET(s->vlan_table, vlan_tag); -} - -static bool -vmxnet3_is_allowed_mcast_group(VMXNET3State *s, const uint8_t *group_mac) -{ - int i; - for (i = 0; i < s->mcast_list_len; i++) { - if (!memcmp(group_mac, s->mcast_list[i].a, sizeof(s->mcast_list[i]))) { - return true; - } - } - return false; -} - -static bool -vmxnet3_rx_filter_may_indicate(VMXNET3State *s, const void *data, - size_t size) -{ - struct eth_header *ehdr = PKT_GET_ETH_HDR(data); - - if (VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_PROMISC)) { - return true; - } - - if (!vmxnet3_is_registered_vlan(s, data)) { - return false; - } - - switch (vmxnet_rx_pkt_get_packet_type(s->rx_pkt)) { - case ETH_PKT_UCAST: - if (!VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_UCAST)) { - return false; - } - if (memcmp(s->conf.macaddr.a, ehdr->h_dest, ETH_ALEN)) { - return false; - } - break; - - case ETH_PKT_BCAST: - if (!VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_BCAST)) { - return false; - } - break; - - case ETH_PKT_MCAST: - if (VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_ALL_MULTI)) { - return true; - } - if (!VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_MCAST)) { - return false; - } - if (!vmxnet3_is_allowed_mcast_group(s, ehdr->h_dest)) { - return false; - } - break; - - default: - g_assert_not_reached(); - } - - return true; -} - -static ssize_t -vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size) -{ - VMXNET3State *s = qemu_get_nic_opaque(nc); - size_t bytes_indicated; - uint8_t min_buf[MIN_BUF_SIZE]; - - if (!vmxnet3_can_receive(nc)) { - VMW_PKPRN("Cannot receive now"); - return -1; - } - - if (s->peer_has_vhdr) { - vmxnet_rx_pkt_set_vhdr(s->rx_pkt, (struct virtio_net_hdr *)buf); - buf += sizeof(struct virtio_net_hdr); - size -= sizeof(struct virtio_net_hdr); - } - - /* Pad to minimum Ethernet frame length */ - if (size < sizeof(min_buf)) { - memcpy(min_buf, buf, size); - memset(&min_buf[size], 0, sizeof(min_buf) - size); - buf = min_buf; - size = sizeof(min_buf); - } - - vmxnet_rx_pkt_set_packet_type(s->rx_pkt, - get_eth_packet_type(PKT_GET_ETH_HDR(buf))); - - if (vmxnet3_rx_filter_may_indicate(s, buf, size)) { - vmxnet_rx_pkt_set_protocols(s->rx_pkt, buf, size); - vmxnet3_rx_need_csum_calculate(s->rx_pkt, buf, 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: %zu of %zu bytes indicated", bytes_indicated, size); - } - } else { - VMW_PKPRN("Packet dropped by RX filter"); - bytes_indicated = size; - } - - assert(size > 0); - assert(bytes_indicated != 0); - return bytes_indicated; -} - -static void vmxnet3_set_link_status(NetClientState *nc) -{ - VMXNET3State *s = qemu_get_nic_opaque(nc); - - if (nc->link_down) { - s->link_status_and_speed &= ~VMXNET3_LINK_STATUS_UP; - } else { - s->link_status_and_speed |= VMXNET3_LINK_STATUS_UP; - } - - vmxnet3_set_events(s, VMXNET3_ECR_LINK); - vmxnet3_trigger_interrupt(s, s->event_int_idx); -} - -static NetClientInfo net_vmxnet3_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = vmxnet3_receive, - .link_status_changed = vmxnet3_set_link_status, -}; - -static bool vmxnet3_peer_has_vnet_hdr(VMXNET3State *s) -{ - NetClientState *nc = qemu_get_queue(s->nic); - - if (qemu_has_vnet_hdr(nc->peer)) { - return true; - } - - return false; -} - -static void vmxnet3_net_uninit(VMXNET3State *s) -{ - g_free(s->mcast_list); - vmxnet3_deactivate_device(s); - qemu_del_nic(s->nic); -} - -static void vmxnet3_net_init(VMXNET3State *s) -{ - DeviceState *d = DEVICE(s); - - VMW_CBPRN("vmxnet3_net_init called..."); - - qemu_macaddr_default_if_unset(&s->conf.macaddr); - - /* Windows guest will query the address that was set on init */ - memcpy(&s->perm_mac.a, &s->conf.macaddr.a, sizeof(s->perm_mac.a)); - - s->mcast_list = NULL; - s->mcast_list_len = 0; - - s->link_status_and_speed = VMXNET3_LINK_SPEED | VMXNET3_LINK_STATUS_UP; - - 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)), - d->id, s); - - s->peer_has_vhdr = vmxnet3_peer_has_vnet_hdr(s); - s->tx_sop = true; - s->skip_current_tx_pkt = false; - s->tx_pkt = NULL; - s->rx_pkt = NULL; - s->rx_vlan_stripping = false; - s->lro_supported = false; - - if (s->peer_has_vhdr) { - qemu_set_vnet_hdr_len(qemu_get_queue(s->nic)->peer, - sizeof(struct virtio_net_hdr)); - - qemu_using_vnet_hdr(qemu_get_queue(s->nic)->peer, 1); - } - - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); -} - -static void -vmxnet3_unuse_msix_vectors(VMXNET3State *s, int num_vectors) -{ - PCIDevice *d = PCI_DEVICE(s); - int i; - for (i = 0; i < num_vectors; i++) { - msix_vector_unuse(d, i); - } -} - -static bool -vmxnet3_use_msix_vectors(VMXNET3State *s, int num_vectors) -{ - PCIDevice *d = PCI_DEVICE(s); - int i; - for (i = 0; i < num_vectors; i++) { - int res = msix_vector_use(d, i); - if (0 > res) { - VMW_WRPRN("Failed to use MSI-X vector %d, error %d", i, res); - vmxnet3_unuse_msix_vectors(s, i); - return false; - } - } - return true; -} - -static bool -vmxnet3_init_msix(VMXNET3State *s) -{ - PCIDevice *d = PCI_DEVICE(s); - int res = msix_init(d, VMXNET3_MAX_INTRS, - &s->msix_bar, - VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_TABLE, - &s->msix_bar, - VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_PBA(s), - VMXNET3_MSIX_OFFSET(s)); - - if (0 > res) { - VMW_WRPRN("Failed to initialize MSI-X, error %d", res); - s->msix_used = false; - } else { - if (!vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS)) { - VMW_WRPRN("Failed to use MSI-X vectors, error %d", res); - msix_uninit(d, &s->msix_bar, &s->msix_bar); - s->msix_used = false; - } else { - s->msix_used = true; - } - } - return s->msix_used; -} - -static void -vmxnet3_cleanup_msix(VMXNET3State *s) -{ - PCIDevice *d = PCI_DEVICE(s); - - if (s->msix_used) { - vmxnet3_unuse_msix_vectors(s, VMXNET3_MAX_INTRS); - msix_uninit(d, &s->msix_bar, &s->msix_bar); - } -} - -#define VMXNET3_USE_64BIT (true) -#define VMXNET3_PER_VECTOR_MASK (false) - -static bool -vmxnet3_init_msi(VMXNET3State *s) -{ - PCIDevice *d = PCI_DEVICE(s); - int res; - - 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); - s->msi_used = false; - } else { - s->msi_used = true; - } - - return s->msi_used; -} - -static void -vmxnet3_cleanup_msi(VMXNET3State *s) -{ - PCIDevice *d = PCI_DEVICE(s); - - if (s->msi_used) { - msi_uninit(d); - } -} - -static void -vmxnet3_msix_save(QEMUFile *f, void *opaque) -{ - PCIDevice *d = PCI_DEVICE(opaque); - msix_save(d, f); -} - -static int -vmxnet3_msix_load(QEMUFile *f, void *opaque, int version_id) -{ - PCIDevice *d = PCI_DEVICE(opaque); - msix_load(d, f); - return 0; -} - -static const MemoryRegionOps b0_ops = { - .read = vmxnet3_io_bar0_read, - .write = vmxnet3_io_bar0_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const MemoryRegionOps b1_ops = { - .read = vmxnet3_io_bar1_read, - .write = vmxnet3_io_bar1_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -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); - VMXNET3State *s = VMXNET3(pci_dev); - - VMW_CBPRN("Starting init..."); - - memory_region_init_io(&s->bar0, OBJECT(s), &b0_ops, s, - "vmxnet3-b0", VMXNET3_PT_REG_SIZE); - pci_register_bar(pci_dev, VMXNET3_BAR0_IDX, - PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0); - - memory_region_init_io(&s->bar1, OBJECT(s), &b1_ops, s, - "vmxnet3-b1", VMXNET3_VD_REG_SIZE); - pci_register_bar(pci_dev, VMXNET3_BAR1_IDX, - PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar1); - - memory_region_init(&s->msix_bar, OBJECT(s), "vmxnet3-msix-bar", - VMXNET3_MSIX_BAR_SIZE); - pci_register_bar(pci_dev, VMXNET3_MSIX_BAR_IDX, - PCI_BASE_ADDRESS_SPACE_MEMORY, &s->msix_bar); - - vmxnet3_reset_interrupt_states(s); - - /* Interrupt pin A */ - pci_dev->config[PCI_INTERRUPT_PIN] = 0x01; - - if (!vmxnet3_init_msix(s)) { - VMW_WRPRN("Failed to initialize MSI-X, configuration is inconsistent."); - } - - if (!vmxnet3_init_msi(s)) { - VMW_WRPRN("Failed to initialize MSI, configuration is inconsistent."); - } - - 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); -} - -static void vmxnet3_instance_init(Object *obj) -{ - VMXNET3State *s = VMXNET3(obj); - device_add_bootindex_property(obj, &s->conf.bootindex, - "bootindex", "/ethernet-phy@0", - DEVICE(obj), NULL); -} - -static void vmxnet3_pci_uninit(PCIDevice *pci_dev) -{ - DeviceState *dev = DEVICE(pci_dev); - VMXNET3State *s = VMXNET3(pci_dev); - - VMW_CBPRN("Starting uninit..."); - - unregister_savevm(dev, "vmxnet3-msix", s); - - vmxnet3_net_uninit(s); - - vmxnet3_cleanup_msix(s); - - vmxnet3_cleanup_msi(s); -} - -static void vmxnet3_qdev_reset(DeviceState *dev) -{ - PCIDevice *d = PCI_DEVICE(dev); - VMXNET3State *s = VMXNET3(d); - - VMW_CBPRN("Starting QDEV reset..."); - vmxnet3_reset(s); -} - -static bool vmxnet3_mc_list_needed(void *opaque) -{ - return true; -} - -static int vmxnet3_mcast_list_pre_load(void *opaque) -{ - VMXNET3State *s = opaque; - - s->mcast_list = g_malloc(s->mcast_list_buff_size); - - return 0; -} - - -static void vmxnet3_pre_save(void *opaque) -{ - VMXNET3State *s = opaque; - - s->mcast_list_buff_size = s->mcast_list_len * sizeof(MACAddr); -} - -static const VMStateDescription vmxstate_vmxnet3_mcast_list = { - .name = "vmxnet3/mcast_list", - .version_id = 1, - .minimum_version_id = 1, - .pre_load = vmxnet3_mcast_list_pre_load, - .needed = vmxnet3_mc_list_needed, - .fields = (VMStateField[]) { - VMSTATE_VBUFFER_UINT32(mcast_list, VMXNET3State, 0, NULL, 0, - mcast_list_buff_size), - VMSTATE_END_OF_LIST() - } -}; - -static void vmxnet3_get_ring_from_file(QEMUFile *f, Vmxnet3Ring *r) -{ - r->pa = qemu_get_be64(f); - r->size = qemu_get_be32(f); - r->cell_size = qemu_get_be32(f); - r->next = qemu_get_be32(f); - r->gen = qemu_get_byte(f); -} - -static void vmxnet3_put_ring_to_file(QEMUFile *f, Vmxnet3Ring *r) -{ - qemu_put_be64(f, r->pa); - qemu_put_be32(f, r->size); - qemu_put_be32(f, r->cell_size); - qemu_put_be32(f, r->next); - qemu_put_byte(f, r->gen); -} - -static void vmxnet3_get_tx_stats_from_file(QEMUFile *f, - struct UPT1_TxStats *tx_stat) -{ - tx_stat->TSOPktsTxOK = qemu_get_be64(f); - tx_stat->TSOBytesTxOK = qemu_get_be64(f); - tx_stat->ucastPktsTxOK = qemu_get_be64(f); - tx_stat->ucastBytesTxOK = qemu_get_be64(f); - tx_stat->mcastPktsTxOK = qemu_get_be64(f); - tx_stat->mcastBytesTxOK = qemu_get_be64(f); - tx_stat->bcastPktsTxOK = qemu_get_be64(f); - tx_stat->bcastBytesTxOK = qemu_get_be64(f); - tx_stat->pktsTxError = qemu_get_be64(f); - tx_stat->pktsTxDiscard = qemu_get_be64(f); -} - -static void vmxnet3_put_tx_stats_to_file(QEMUFile *f, - struct UPT1_TxStats *tx_stat) -{ - qemu_put_be64(f, tx_stat->TSOPktsTxOK); - qemu_put_be64(f, tx_stat->TSOBytesTxOK); - qemu_put_be64(f, tx_stat->ucastPktsTxOK); - qemu_put_be64(f, tx_stat->ucastBytesTxOK); - qemu_put_be64(f, tx_stat->mcastPktsTxOK); - qemu_put_be64(f, tx_stat->mcastBytesTxOK); - qemu_put_be64(f, tx_stat->bcastPktsTxOK); - qemu_put_be64(f, tx_stat->bcastBytesTxOK); - qemu_put_be64(f, tx_stat->pktsTxError); - qemu_put_be64(f, tx_stat->pktsTxDiscard); -} - -static int vmxnet3_get_txq_descr(QEMUFile *f, void *pv, size_t size) -{ - Vmxnet3TxqDescr *r = pv; - - vmxnet3_get_ring_from_file(f, &r->tx_ring); - vmxnet3_get_ring_from_file(f, &r->comp_ring); - r->intr_idx = qemu_get_byte(f); - r->tx_stats_pa = qemu_get_be64(f); - - vmxnet3_get_tx_stats_from_file(f, &r->txq_stats); - - return 0; -} - -static void vmxnet3_put_txq_descr(QEMUFile *f, void *pv, size_t size) -{ - Vmxnet3TxqDescr *r = pv; - - vmxnet3_put_ring_to_file(f, &r->tx_ring); - vmxnet3_put_ring_to_file(f, &r->comp_ring); - qemu_put_byte(f, r->intr_idx); - qemu_put_be64(f, r->tx_stats_pa); - vmxnet3_put_tx_stats_to_file(f, &r->txq_stats); -} - -static const VMStateInfo txq_descr_info = { - .name = "txq_descr", - .get = vmxnet3_get_txq_descr, - .put = vmxnet3_put_txq_descr -}; - -static void vmxnet3_get_rx_stats_from_file(QEMUFile *f, - struct UPT1_RxStats *rx_stat) -{ - rx_stat->LROPktsRxOK = qemu_get_be64(f); - rx_stat->LROBytesRxOK = qemu_get_be64(f); - rx_stat->ucastPktsRxOK = qemu_get_be64(f); - rx_stat->ucastBytesRxOK = qemu_get_be64(f); - rx_stat->mcastPktsRxOK = qemu_get_be64(f); - rx_stat->mcastBytesRxOK = qemu_get_be64(f); - rx_stat->bcastPktsRxOK = qemu_get_be64(f); - rx_stat->bcastBytesRxOK = qemu_get_be64(f); - rx_stat->pktsRxOutOfBuf = qemu_get_be64(f); - rx_stat->pktsRxError = qemu_get_be64(f); -} - -static void vmxnet3_put_rx_stats_to_file(QEMUFile *f, - struct UPT1_RxStats *rx_stat) -{ - qemu_put_be64(f, rx_stat->LROPktsRxOK); - qemu_put_be64(f, rx_stat->LROBytesRxOK); - qemu_put_be64(f, rx_stat->ucastPktsRxOK); - qemu_put_be64(f, rx_stat->ucastBytesRxOK); - qemu_put_be64(f, rx_stat->mcastPktsRxOK); - qemu_put_be64(f, rx_stat->mcastBytesRxOK); - qemu_put_be64(f, rx_stat->bcastPktsRxOK); - qemu_put_be64(f, rx_stat->bcastBytesRxOK); - qemu_put_be64(f, rx_stat->pktsRxOutOfBuf); - qemu_put_be64(f, rx_stat->pktsRxError); -} - -static int vmxnet3_get_rxq_descr(QEMUFile *f, void *pv, size_t size) -{ - Vmxnet3RxqDescr *r = pv; - int i; - - for (i = 0; i < VMXNET3_RX_RINGS_PER_QUEUE; i++) { - vmxnet3_get_ring_from_file(f, &r->rx_ring[i]); - } - - vmxnet3_get_ring_from_file(f, &r->comp_ring); - r->intr_idx = qemu_get_byte(f); - r->rx_stats_pa = qemu_get_be64(f); - - vmxnet3_get_rx_stats_from_file(f, &r->rxq_stats); - - return 0; -} - -static void vmxnet3_put_rxq_descr(QEMUFile *f, void *pv, size_t size) -{ - Vmxnet3RxqDescr *r = pv; - int i; - - for (i = 0; i < VMXNET3_RX_RINGS_PER_QUEUE; i++) { - vmxnet3_put_ring_to_file(f, &r->rx_ring[i]); - } - - vmxnet3_put_ring_to_file(f, &r->comp_ring); - qemu_put_byte(f, r->intr_idx); - qemu_put_be64(f, r->rx_stats_pa); - vmxnet3_put_rx_stats_to_file(f, &r->rxq_stats); -} - -static int vmxnet3_post_load(void *opaque, int version_id) -{ - VMXNET3State *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - - vmxnet_tx_pkt_init(&s->tx_pkt, s->max_tx_frags, s->peer_has_vhdr); - vmxnet_rx_pkt_init(&s->rx_pkt, s->peer_has_vhdr); - - if (s->msix_used) { - if (!vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS)) { - VMW_WRPRN("Failed to re-use MSI-X vectors"); - msix_uninit(d, &s->msix_bar, &s->msix_bar); - s->msix_used = false; - return -1; - } - } - - vmxnet3_validate_queues(s); - vmxnet3_validate_interrupts(s); - - return 0; -} - -static const VMStateInfo rxq_descr_info = { - .name = "rxq_descr", - .get = vmxnet3_get_rxq_descr, - .put = vmxnet3_put_rxq_descr -}; - -static int vmxnet3_get_int_state(QEMUFile *f, void *pv, size_t size) -{ - Vmxnet3IntState *r = pv; - - r->is_masked = qemu_get_byte(f); - r->is_pending = qemu_get_byte(f); - r->is_asserted = qemu_get_byte(f); - - return 0; -} - -static void vmxnet3_put_int_state(QEMUFile *f, void *pv, size_t size) -{ - Vmxnet3IntState *r = pv; - - qemu_put_byte(f, r->is_masked); - qemu_put_byte(f, r->is_pending); - qemu_put_byte(f, r->is_asserted); -} - -static const VMStateInfo int_state_info = { - .name = "int_state", - .get = vmxnet3_get_int_state, - .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, - .minimum_version_id = 1, - .pre_save = vmxnet3_pre_save, - .post_load = vmxnet3_post_load, - .fields = (VMStateField[]) { - 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), - VMSTATE_UINT32(rx_mode, VMXNET3State), - VMSTATE_UINT32(mcast_list_len, VMXNET3State), - VMSTATE_UINT32(mcast_list_buff_size, VMXNET3State), - VMSTATE_UINT32_ARRAY(vlan_table, VMXNET3State, VMXNET3_VFT_SIZE), - VMSTATE_UINT32(mtu, VMXNET3State), - VMSTATE_UINT16(max_rx_frags, VMXNET3State), - VMSTATE_UINT32(max_tx_frags, VMXNET3State), - VMSTATE_UINT8(event_int_idx, VMXNET3State), - VMSTATE_BOOL(auto_int_masking, VMXNET3State), - VMSTATE_UINT8(txq_num, VMXNET3State), - VMSTATE_UINT8(rxq_num, VMXNET3State), - VMSTATE_UINT32(device_active, VMXNET3State), - VMSTATE_UINT32(last_command, VMXNET3State), - VMSTATE_UINT32(link_status_and_speed, VMXNET3State), - VMSTATE_UINT32(temp_mac, VMXNET3State), - VMSTATE_UINT64(drv_shmem, VMXNET3State), - VMSTATE_UINT64(temp_shared_guest_driver_memory, VMXNET3State), - - VMSTATE_ARRAY(txq_descr, VMXNET3State, - VMXNET3_DEVICE_MAX_TX_QUEUES, 0, txq_descr_info, - Vmxnet3TxqDescr), - VMSTATE_ARRAY(rxq_descr, VMXNET3State, - VMXNET3_DEVICE_MAX_RX_QUEUES, 0, rxq_descr_info, - Vmxnet3RxqDescr), - VMSTATE_ARRAY(interrupt_states, VMXNET3State, VMXNET3_MAX_INTRS, - 0, int_state_info, Vmxnet3IntState), - - VMSTATE_END_OF_LIST() - }, - .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; - c->vendor_id = PCI_VENDOR_ID_VMWARE; - c->device_id = PCI_DEVICE_ID_VMWARE_VMXNET3; - c->revision = PCI_DEVICE_ID_VMWARE_VMXNET3_REVISION; - 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; - dc->props = vmxnet3_properties; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); -} - -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, -}; - -static void vmxnet3_register_types(void) -{ - VMW_CBPRN("vmxnet3_register_types called..."); - type_register_static(&vmxnet3_info); -} - -type_init(vmxnet3_register_types) diff --git a/qemu/hw/net/vmxnet3.h b/qemu/hw/net/vmxnet3.h deleted file mode 100644 index f7006afe9..000000000 --- a/qemu/hw/net/vmxnet3.h +++ /dev/null @@ -1,759 +0,0 @@ -/* - * QEMU VMWARE VMXNET3 paravirtual NIC interface definitions - * - * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) - * - * Developed by Daynix Computing LTD (http://www.daynix.com) - * - * Authors: - * Dmitry Fleytman - * Tamir Shomer - * Yan Vugenfirer - * - * This work is licensed under the terms of the GNU GPL, version 2. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef _QEMU_VMXNET3_H -#define _QEMU_VMXNET3_H - -#define VMXNET3_DEVICE_MAX_TX_QUEUES 8 -#define VMXNET3_DEVICE_MAX_RX_QUEUES 8 /* Keep this value as a power of 2 */ - -/* - * VMWARE headers we got from Linux kernel do not fully comply QEMU coding - * standards in sense of types and defines used. - * Since we didn't want to change VMWARE code, following set of typedefs - * and defines needed to compile these headers with QEMU introduced. - */ -#define u64 uint64_t -#define u32 uint32_t -#define u16 uint16_t -#define u8 uint8_t -#define __le16 uint16_t -#define __le32 uint32_t -#define __le64 uint64_t - -#if defined(HOST_WORDS_BIGENDIAN) -#define __BIG_ENDIAN_BITFIELD -#else -#endif - -/* - * Following is an interface definition for - * VMXNET3 device as provided by VMWARE - * See original copyright from Linux kernel v3.2.8 - * header file drivers/net/vmxnet3/vmxnet3_defs.h below. - */ - -/* - * Linux driver for VMware's vmxnet3 ethernet NIC. - * - * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved. - * - * 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; version 2 of the License and no 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, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Maintained by: Shreyas Bhatewara - * - */ - -struct UPT1_TxStats { - u64 TSOPktsTxOK; /* TSO pkts post-segmentation */ - u64 TSOBytesTxOK; - u64 ucastPktsTxOK; - u64 ucastBytesTxOK; - u64 mcastPktsTxOK; - u64 mcastBytesTxOK; - u64 bcastPktsTxOK; - u64 bcastBytesTxOK; - u64 pktsTxError; - u64 pktsTxDiscard; -}; - -struct UPT1_RxStats { - u64 LROPktsRxOK; /* LRO pkts */ - u64 LROBytesRxOK; /* bytes from LRO pkts */ - /* the following counters are for pkts from the wire, i.e., pre-LRO */ - u64 ucastPktsRxOK; - u64 ucastBytesRxOK; - u64 mcastPktsRxOK; - u64 mcastBytesRxOK; - u64 bcastPktsRxOK; - u64 bcastBytesRxOK; - u64 pktsRxOutOfBuf; - u64 pktsRxError; -}; - -/* interrupt moderation level */ -enum { - UPT1_IML_NONE = 0, /* no interrupt moderation */ - UPT1_IML_HIGHEST = 7, /* least intr generated */ - UPT1_IML_ADAPTIVE = 8, /* adpative intr moderation */ -}; -/* values for UPT1_RSSConf.hashFunc */ -enum { - UPT1_RSS_HASH_TYPE_NONE = 0x0, - UPT1_RSS_HASH_TYPE_IPV4 = 0x01, - UPT1_RSS_HASH_TYPE_TCP_IPV4 = 0x02, - UPT1_RSS_HASH_TYPE_IPV6 = 0x04, - UPT1_RSS_HASH_TYPE_TCP_IPV6 = 0x08, -}; - -enum { - UPT1_RSS_HASH_FUNC_NONE = 0x0, - UPT1_RSS_HASH_FUNC_TOEPLITZ = 0x01, -}; - -#define UPT1_RSS_MAX_KEY_SIZE 40 -#define UPT1_RSS_MAX_IND_TABLE_SIZE 128 - -struct UPT1_RSSConf { - u16 hashType; - u16 hashFunc; - u16 hashKeySize; - u16 indTableSize; - u8 hashKey[UPT1_RSS_MAX_KEY_SIZE]; - u8 indTable[UPT1_RSS_MAX_IND_TABLE_SIZE]; -}; - -/* features */ -enum { - UPT1_F_RXCSUM = 0x0001, /* rx csum verification */ - UPT1_F_RSS = 0x0002, - UPT1_F_RXVLAN = 0x0004, /* VLAN tag stripping */ - UPT1_F_LRO = 0x0008, -}; - -/* all registers are 32 bit wide */ -/* BAR 1 */ -enum { - VMXNET3_REG_VRRS = 0x0, /* Vmxnet3 Revision Report Selection */ - VMXNET3_REG_UVRS = 0x8, /* UPT Version Report Selection */ - VMXNET3_REG_DSAL = 0x10, /* Driver Shared Address Low */ - VMXNET3_REG_DSAH = 0x18, /* Driver Shared Address High */ - VMXNET3_REG_CMD = 0x20, /* Command */ - VMXNET3_REG_MACL = 0x28, /* MAC Address Low */ - VMXNET3_REG_MACH = 0x30, /* MAC Address High */ - VMXNET3_REG_ICR = 0x38, /* Interrupt Cause Register */ - VMXNET3_REG_ECR = 0x40 /* Event Cause Register */ -}; - -/* BAR 0 */ -enum { - VMXNET3_REG_IMR = 0x0, /* Interrupt Mask Register */ - VMXNET3_REG_TXPROD = 0x600, /* Tx Producer Index */ - VMXNET3_REG_RXPROD = 0x800, /* Rx Producer Index for ring 1 */ - VMXNET3_REG_RXPROD2 = 0xA00 /* Rx Producer Index for ring 2 */ -}; - -#define VMXNET3_PT_REG_SIZE 4096 /* BAR 0 */ -#define VMXNET3_VD_REG_SIZE 4096 /* BAR 1 */ - -#define VMXNET3_REG_ALIGN 8 /* All registers are 8-byte aligned. */ -#define VMXNET3_REG_ALIGN_MASK 0x7 - -/* I/O Mapped access to registers */ -#define VMXNET3_IO_TYPE_PT 0 -#define VMXNET3_IO_TYPE_VD 1 -#define VMXNET3_IO_ADDR(type, reg) (((type) << 24) | ((reg) & 0xFFFFFF)) -#define VMXNET3_IO_TYPE(addr) ((addr) >> 24) -#define VMXNET3_IO_REG(addr) ((addr) & 0xFFFFFF) - -enum { - VMXNET3_CMD_FIRST_SET = 0xCAFE0000, - VMXNET3_CMD_ACTIVATE_DEV = VMXNET3_CMD_FIRST_SET, /* 0xCAFE0000 */ - VMXNET3_CMD_QUIESCE_DEV, /* 0xCAFE0001 */ - VMXNET3_CMD_RESET_DEV, /* 0xCAFE0002 */ - VMXNET3_CMD_UPDATE_RX_MODE, /* 0xCAFE0003 */ - VMXNET3_CMD_UPDATE_MAC_FILTERS, /* 0xCAFE0004 */ - VMXNET3_CMD_UPDATE_VLAN_FILTERS, /* 0xCAFE0005 */ - VMXNET3_CMD_UPDATE_RSSIDT, /* 0xCAFE0006 */ - VMXNET3_CMD_UPDATE_IML, /* 0xCAFE0007 */ - VMXNET3_CMD_UPDATE_PMCFG, /* 0xCAFE0008 */ - VMXNET3_CMD_UPDATE_FEATURE, /* 0xCAFE0009 */ - VMXNET3_CMD_LOAD_PLUGIN, /* 0xCAFE000A */ - - VMXNET3_CMD_FIRST_GET = 0xF00D0000, - VMXNET3_CMD_GET_QUEUE_STATUS = VMXNET3_CMD_FIRST_GET, /* 0xF00D0000 */ - VMXNET3_CMD_GET_STATS, /* 0xF00D0001 */ - VMXNET3_CMD_GET_LINK, /* 0xF00D0002 */ - VMXNET3_CMD_GET_PERM_MAC_LO, /* 0xF00D0003 */ - VMXNET3_CMD_GET_PERM_MAC_HI, /* 0xF00D0004 */ - 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_ADAPTIVE_RING_INFO /* 0xF00D0009 */ -}; - -/* Adaptive Ring Info Flags */ -#define VMXNET3_DISABLE_ADAPTIVE_RING 1 - -/* - * Little Endian layout of bitfields - - * Byte 0 : 7.....len.....0 - * Byte 1 : rsvd gen 13.len.8 - * Byte 2 : 5.msscof.0 ext1 dtype - * Byte 3 : 13...msscof...6 - * - * Big Endian layout of bitfields - - * Byte 0: 13...msscof...6 - * Byte 1 : 5.msscof.0 ext1 dtype - * Byte 2 : rsvd gen 13.len.8 - * Byte 3 : 7.....len.....0 - * - * Thus, le32_to_cpu on the dword will allow the big endian driver to read - * the bit fields correctly. And cpu_to_le32 will convert bitfields - * bit fields written by big endian driver to format required by device. - */ - -struct Vmxnet3_TxDesc { - __le64 addr; - -#ifdef __BIG_ENDIAN_BITFIELD - u32 msscof:14; /* MSS, checksum offset, flags */ - u32 ext1:1; - u32 dtype:1; /* descriptor type */ - u32 rsvd:1; - u32 gen:1; /* generation bit */ - u32 len:14; -#else - u32 len:14; - u32 gen:1; /* generation bit */ - u32 rsvd:1; - u32 dtype:1; /* descriptor type */ - u32 ext1:1; - u32 msscof:14; /* MSS, checksum offset, flags */ -#endif /* __BIG_ENDIAN_BITFIELD */ - -#ifdef __BIG_ENDIAN_BITFIELD - u32 tci:16; /* Tag to Insert */ - u32 ti:1; /* VLAN Tag Insertion */ - u32 ext2:1; - u32 cq:1; /* completion request */ - u32 eop:1; /* End Of Packet */ - u32 om:2; /* offload mode */ - u32 hlen:10; /* header len */ -#else - u32 hlen:10; /* header len */ - u32 om:2; /* offload mode */ - u32 eop:1; /* End Of Packet */ - u32 cq:1; /* completion request */ - u32 ext2:1; - u32 ti:1; /* VLAN Tag Insertion */ - u32 tci:16; /* Tag to Insert */ -#endif /* __BIG_ENDIAN_BITFIELD */ -}; - -/* TxDesc.OM values */ -#define VMXNET3_OM_NONE 0 -#define VMXNET3_OM_CSUM 2 -#define VMXNET3_OM_TSO 3 - -/* fields in TxDesc we access w/o using bit fields */ -#define VMXNET3_TXD_EOP_SHIFT 12 -#define VMXNET3_TXD_CQ_SHIFT 13 -#define VMXNET3_TXD_GEN_SHIFT 14 -#define VMXNET3_TXD_EOP_DWORD_SHIFT 3 -#define VMXNET3_TXD_GEN_DWORD_SHIFT 2 - -#define VMXNET3_TXD_CQ (1 << VMXNET3_TXD_CQ_SHIFT) -#define VMXNET3_TXD_EOP (1 << VMXNET3_TXD_EOP_SHIFT) -#define VMXNET3_TXD_GEN (1 << VMXNET3_TXD_GEN_SHIFT) - -#define VMXNET3_HDR_COPY_SIZE 128 - - -struct Vmxnet3_TxDataDesc { - u8 data[VMXNET3_HDR_COPY_SIZE]; -}; - -#define VMXNET3_TCD_GEN_SHIFT 31 -#define VMXNET3_TCD_GEN_SIZE 1 -#define VMXNET3_TCD_TXIDX_SHIFT 0 -#define VMXNET3_TCD_TXIDX_SIZE 12 -#define VMXNET3_TCD_GEN_DWORD_SHIFT 3 - -struct Vmxnet3_TxCompDesc { - u32 txdIdx:12; /* Index of the EOP TxDesc */ - u32 ext1:20; - - __le32 ext2; - __le32 ext3; - - u32 rsvd:24; - u32 type:7; /* completion type */ - u32 gen:1; /* generation bit */ -}; - -struct Vmxnet3_RxDesc { - __le64 addr; - -#ifdef __BIG_ENDIAN_BITFIELD - u32 gen:1; /* Generation bit */ - u32 rsvd:15; - u32 dtype:1; /* Descriptor type */ - u32 btype:1; /* Buffer Type */ - u32 len:14; -#else - u32 len:14; - u32 btype:1; /* Buffer Type */ - u32 dtype:1; /* Descriptor type */ - u32 rsvd:15; - u32 gen:1; /* Generation bit */ -#endif - u32 ext1; -}; - -/* values of RXD.BTYPE */ -#define VMXNET3_RXD_BTYPE_HEAD 0 /* head only */ -#define VMXNET3_RXD_BTYPE_BODY 1 /* body only */ - -/* fields in RxDesc we access w/o using bit fields */ -#define VMXNET3_RXD_BTYPE_SHIFT 14 -#define VMXNET3_RXD_GEN_SHIFT 31 - -struct Vmxnet3_RxCompDesc { -#ifdef __BIG_ENDIAN_BITFIELD - u32 ext2:1; - u32 cnc:1; /* Checksum Not Calculated */ - u32 rssType:4; /* RSS hash type used */ - u32 rqID:10; /* rx queue/ring ID */ - u32 sop:1; /* Start of Packet */ - u32 eop:1; /* End of Packet */ - u32 ext1:2; - u32 rxdIdx:12; /* Index of the RxDesc */ -#else - u32 rxdIdx:12; /* Index of the RxDesc */ - u32 ext1:2; - u32 eop:1; /* End of Packet */ - u32 sop:1; /* Start of Packet */ - u32 rqID:10; /* rx queue/ring ID */ - u32 rssType:4; /* RSS hash type used */ - u32 cnc:1; /* Checksum Not Calculated */ - u32 ext2:1; -#endif /* __BIG_ENDIAN_BITFIELD */ - - __le32 rssHash; /* RSS hash value */ - -#ifdef __BIG_ENDIAN_BITFIELD - u32 tci:16; /* Tag stripped */ - u32 ts:1; /* Tag is stripped */ - u32 err:1; /* Error */ - u32 len:14; /* data length */ -#else - u32 len:14; /* data length */ - u32 err:1; /* Error */ - u32 ts:1; /* Tag is stripped */ - u32 tci:16; /* Tag stripped */ -#endif /* __BIG_ENDIAN_BITFIELD */ - - -#ifdef __BIG_ENDIAN_BITFIELD - u32 gen:1; /* generation bit */ - u32 type:7; /* completion type */ - u32 fcs:1; /* Frame CRC correct */ - u32 frg:1; /* IP Fragment */ - u32 v4:1; /* IPv4 */ - u32 v6:1; /* IPv6 */ - u32 ipc:1; /* IP Checksum Correct */ - u32 tcp:1; /* TCP packet */ - u32 udp:1; /* UDP packet */ - u32 tuc:1; /* TCP/UDP Checksum Correct */ - u32 csum:16; -#else - u32 csum:16; - u32 tuc:1; /* TCP/UDP Checksum Correct */ - u32 udp:1; /* UDP packet */ - u32 tcp:1; /* TCP packet */ - u32 ipc:1; /* IP Checksum Correct */ - u32 v6:1; /* IPv6 */ - u32 v4:1; /* IPv4 */ - u32 frg:1; /* IP Fragment */ - u32 fcs:1; /* Frame CRC correct */ - u32 type:7; /* completion type */ - u32 gen:1; /* generation bit */ -#endif /* __BIG_ENDIAN_BITFIELD */ -}; - -/* fields in RxCompDesc we access via Vmxnet3_GenericDesc.dword[3] */ -#define VMXNET3_RCD_TUC_SHIFT 16 -#define VMXNET3_RCD_IPC_SHIFT 19 - -/* fields in RxCompDesc we access via Vmxnet3_GenericDesc.qword[1] */ -#define VMXNET3_RCD_TYPE_SHIFT 56 -#define VMXNET3_RCD_GEN_SHIFT 63 - -/* csum OK for TCP/UDP pkts over IP */ -#define VMXNET3_RCD_CSUM_OK (1 << VMXNET3_RCD_TUC_SHIFT | \ - 1 << VMXNET3_RCD_IPC_SHIFT) -#define VMXNET3_TXD_GEN_SIZE 1 -#define VMXNET3_TXD_EOP_SIZE 1 - -/* value of RxCompDesc.rssType */ -enum { - VMXNET3_RCD_RSS_TYPE_NONE = 0, - VMXNET3_RCD_RSS_TYPE_IPV4 = 1, - VMXNET3_RCD_RSS_TYPE_TCPIPV4 = 2, - VMXNET3_RCD_RSS_TYPE_IPV6 = 3, - VMXNET3_RCD_RSS_TYPE_TCPIPV6 = 4, -}; - - -/* a union for accessing all cmd/completion descriptors */ -union Vmxnet3_GenericDesc { - __le64 qword[2]; - __le32 dword[4]; - __le16 word[8]; - struct Vmxnet3_TxDesc txd; - struct Vmxnet3_RxDesc rxd; - struct Vmxnet3_TxCompDesc tcd; - struct Vmxnet3_RxCompDesc rcd; -}; - -#define VMXNET3_INIT_GEN 1 - -/* Max size of a single tx buffer */ -#define VMXNET3_MAX_TX_BUF_SIZE (1 << 14) - -/* # of tx desc needed for a tx buffer size */ -#define VMXNET3_TXD_NEEDED(size) (((size) + VMXNET3_MAX_TX_BUF_SIZE - 1) / \ - VMXNET3_MAX_TX_BUF_SIZE) - -/* max # of tx descs for a non-tso pkt */ -#define VMXNET3_MAX_TXD_PER_PKT 16 - -/* Max size of a single rx buffer */ -#define VMXNET3_MAX_RX_BUF_SIZE ((1 << 14) - 1) -/* Minimum size of a type 0 buffer */ -#define VMXNET3_MIN_T0_BUF_SIZE 128 -#define VMXNET3_MAX_CSUM_OFFSET 1024 - -/* Ring base address alignment */ -#define VMXNET3_RING_BA_ALIGN 512 -#define VMXNET3_RING_BA_MASK (VMXNET3_RING_BA_ALIGN - 1) - -/* Ring size must be a multiple of 32 */ -#define VMXNET3_RING_SIZE_ALIGN 32 -#define VMXNET3_RING_SIZE_MASK (VMXNET3_RING_SIZE_ALIGN - 1) - -/* Max ring size */ -#define VMXNET3_TX_RING_MAX_SIZE 4096 -#define VMXNET3_TC_RING_MAX_SIZE 4096 -#define VMXNET3_RX_RING_MAX_SIZE 4096 -#define VMXNET3_RC_RING_MAX_SIZE 8192 - -/* a list of reasons for queue stop */ - -enum { - VMXNET3_ERR_NOEOP = 0x80000000, /* cannot find the EOP desc of a pkt */ - VMXNET3_ERR_TXD_REUSE = 0x80000001, /* reuse TxDesc before tx completion */ - VMXNET3_ERR_BIG_PKT = 0x80000002, /* too many TxDesc for a pkt */ - VMXNET3_ERR_DESC_NOT_SPT = 0x80000003, /* descriptor type not supported */ - VMXNET3_ERR_SMALL_BUF = 0x80000004, /* type 0 buffer too small */ - VMXNET3_ERR_STRESS = 0x80000005, /* stress option firing in vmkernel */ - VMXNET3_ERR_SWITCH = 0x80000006, /* mode switch failure */ - VMXNET3_ERR_TXD_INVALID = 0x80000007, /* invalid TxDesc */ -}; - -/* completion descriptor types */ -#define VMXNET3_CDTYPE_TXCOMP 0 /* Tx Completion Descriptor */ -#define VMXNET3_CDTYPE_RXCOMP 3 /* Rx Completion Descriptor */ - -enum { - VMXNET3_GOS_BITS_UNK = 0, /* unknown */ - VMXNET3_GOS_BITS_32 = 1, - VMXNET3_GOS_BITS_64 = 2, -}; - -#define VMXNET3_GOS_TYPE_UNK 0 /* unknown */ -#define VMXNET3_GOS_TYPE_LINUX 1 -#define VMXNET3_GOS_TYPE_WIN 2 -#define VMXNET3_GOS_TYPE_SOLARIS 3 -#define VMXNET3_GOS_TYPE_FREEBSD 4 -#define VMXNET3_GOS_TYPE_PXE 5 - -struct Vmxnet3_GOSInfo { -#ifdef __BIG_ENDIAN_BITFIELD - u32 gosMisc:10; /* other info about gos */ - u32 gosVer:16; /* gos version */ - u32 gosType:4; /* which guest */ - u32 gosBits:2; /* 32-bit or 64-bit? */ -#else - u32 gosBits:2; /* 32-bit or 64-bit? */ - u32 gosType:4; /* which guest */ - u32 gosVer:16; /* gos version */ - u32 gosMisc:10; /* other info about gos */ -#endif /* __BIG_ENDIAN_BITFIELD */ -}; - -struct Vmxnet3_DriverInfo { - __le32 version; - struct Vmxnet3_GOSInfo gos; - __le32 vmxnet3RevSpt; - __le32 uptVerSpt; -}; - - -#define VMXNET3_REV1_MAGIC 0xbabefee1 - -/* - * QueueDescPA must be 128 bytes aligned. It points to an array of - * Vmxnet3_TxQueueDesc followed by an array of Vmxnet3_RxQueueDesc. - * The number of Vmxnet3_TxQueueDesc/Vmxnet3_RxQueueDesc are specified by - * Vmxnet3_MiscConf.numTxQueues/numRxQueues, respectively. - */ -#define VMXNET3_QUEUE_DESC_ALIGN 128 - - -struct Vmxnet3_MiscConf { - struct Vmxnet3_DriverInfo driverInfo; - __le64 uptFeatures; - __le64 ddPA; /* driver data PA */ - __le64 queueDescPA; /* queue descriptor table PA */ - __le32 ddLen; /* driver data len */ - __le32 queueDescLen; /* queue desc. table len in bytes */ - __le32 mtu; - __le16 maxNumRxSG; - u8 numTxQueues; - u8 numRxQueues; - __le32 reserved[4]; -}; - - -struct Vmxnet3_TxQueueConf { - __le64 txRingBasePA; - __le64 dataRingBasePA; - __le64 compRingBasePA; - __le64 ddPA; /* driver data */ - __le64 reserved; - __le32 txRingSize; /* # of tx desc */ - __le32 dataRingSize; /* # of data desc */ - __le32 compRingSize; /* # of comp desc */ - __le32 ddLen; /* size of driver data */ - u8 intrIdx; - u8 _pad[7]; -}; - - -struct Vmxnet3_RxQueueConf { - __le64 rxRingBasePA[2]; - __le64 compRingBasePA; - __le64 ddPA; /* driver data */ - __le64 reserved; - __le32 rxRingSize[2]; /* # of rx desc */ - __le32 compRingSize; /* # of rx comp desc */ - __le32 ddLen; /* size of driver data */ - u8 intrIdx; - u8 _pad[7]; -}; - - -enum vmxnet3_intr_mask_mode { - VMXNET3_IMM_AUTO = 0, - VMXNET3_IMM_ACTIVE = 1, - VMXNET3_IMM_LAZY = 2 -}; - -enum vmxnet3_intr_type { - VMXNET3_IT_AUTO = 0, - VMXNET3_IT_INTX = 1, - VMXNET3_IT_MSI = 2, - VMXNET3_IT_MSIX = 3 -}; - -#define VMXNET3_MAX_TX_QUEUES 8 -#define VMXNET3_MAX_RX_QUEUES 16 -/* addition 1 for events */ -#define VMXNET3_MAX_INTRS 25 - -/* value of intrCtrl */ -#define VMXNET3_IC_DISABLE_ALL 0x1 /* bit 0 */ - - -struct Vmxnet3_IntrConf { - bool autoMask; - u8 numIntrs; /* # of interrupts */ - u8 eventIntrIdx; - u8 modLevels[VMXNET3_MAX_INTRS]; /* moderation level for - * each intr */ - __le32 intrCtrl; - __le32 reserved[2]; -}; - -/* one bit per VLAN ID, the size is in the units of u32 */ -#define VMXNET3_VFT_SIZE (4096/(sizeof(uint32_t)*8)) - - -struct Vmxnet3_QueueStatus { - bool stopped; - u8 _pad[3]; - __le32 error; -}; - - -struct Vmxnet3_TxQueueCtrl { - __le32 txNumDeferred; - __le32 txThreshold; - __le64 reserved; -}; - - -struct Vmxnet3_RxQueueCtrl { - bool updateRxProd; - u8 _pad[7]; - __le64 reserved; -}; - -enum { - VMXNET3_RXM_UCAST = 0x01, /* unicast only */ - VMXNET3_RXM_MCAST = 0x02, /* multicast passing the filters */ - VMXNET3_RXM_BCAST = 0x04, /* broadcast only */ - VMXNET3_RXM_ALL_MULTI = 0x08, /* all multicast */ - VMXNET3_RXM_PROMISC = 0x10 /* promiscuous */ -}; - -struct Vmxnet3_RxFilterConf { - __le32 rxMode; /* VMXNET3_RXM_xxx */ - __le16 mfTableLen; /* size of the multicast filter table */ - __le16 _pad1; - __le64 mfTablePA; /* PA of the multicast filters table */ - __le32 vfTable[VMXNET3_VFT_SIZE]; /* vlan filter */ -}; - - -#define VMXNET3_PM_MAX_FILTERS 6 -#define VMXNET3_PM_MAX_PATTERN_SIZE 128 -#define VMXNET3_PM_MAX_MASK_SIZE (VMXNET3_PM_MAX_PATTERN_SIZE / 8) - -#define VMXNET3_PM_WAKEUP_MAGIC cpu_to_le16(0x01) /* wake up on magic pkts */ -#define VMXNET3_PM_WAKEUP_FILTER cpu_to_le16(0x02) /* wake up on pkts matching - * filters */ - - -struct Vmxnet3_PM_PktFilter { - u8 maskSize; - u8 patternSize; - u8 mask[VMXNET3_PM_MAX_MASK_SIZE]; - u8 pattern[VMXNET3_PM_MAX_PATTERN_SIZE]; - u8 pad[6]; -}; - - -struct Vmxnet3_PMConf { - __le16 wakeUpEvents; /* VMXNET3_PM_WAKEUP_xxx */ - u8 numFilters; - u8 pad[5]; - struct Vmxnet3_PM_PktFilter filters[VMXNET3_PM_MAX_FILTERS]; -}; - - -struct Vmxnet3_VariableLenConfDesc { - __le32 confVer; - __le32 confLen; - __le64 confPA; -}; - - -struct Vmxnet3_TxQueueDesc { - struct Vmxnet3_TxQueueCtrl ctrl; - struct Vmxnet3_TxQueueConf conf; - - /* Driver read after a GET command */ - struct Vmxnet3_QueueStatus status; - struct UPT1_TxStats stats; - u8 _pad[88]; /* 128 aligned */ -}; - - -struct Vmxnet3_RxQueueDesc { - struct Vmxnet3_RxQueueCtrl ctrl; - struct Vmxnet3_RxQueueConf conf; - /* Driver read after a GET commad */ - struct Vmxnet3_QueueStatus status; - struct UPT1_RxStats stats; - u8 __pad[88]; /* 128 aligned */ -}; - - -struct Vmxnet3_DSDevRead { - /* read-only region for device, read by dev in response to a SET cmd */ - struct Vmxnet3_MiscConf misc; - struct Vmxnet3_IntrConf intrConf; - struct Vmxnet3_RxFilterConf rxFilterConf; - struct Vmxnet3_VariableLenConfDesc rssConfDesc; - struct Vmxnet3_VariableLenConfDesc pmConfDesc; - struct Vmxnet3_VariableLenConfDesc pluginConfDesc; -}; - -/* All structures in DriverShared are padded to multiples of 8 bytes */ -struct Vmxnet3_DriverShared { - __le32 magic; - /* make devRead start at 64bit boundaries */ - __le32 pad; - struct Vmxnet3_DSDevRead devRead; - __le32 ecr; - __le32 reserved[5]; -}; - - -#define VMXNET3_ECR_RQERR (1 << 0) -#define VMXNET3_ECR_TQERR (1 << 1) -#define VMXNET3_ECR_LINK (1 << 2) -#define VMXNET3_ECR_DIC (1 << 3) -#define VMXNET3_ECR_DEBUG (1 << 4) - -/* flip the gen bit of a ring */ -#define VMXNET3_FLIP_RING_GEN(gen) ((gen) = (gen) ^ 0x1) - -/* only use this if moving the idx won't affect the gen bit */ -#define VMXNET3_INC_RING_IDX_ONLY(idx, ring_size) \ - do {\ - (idx)++;\ - if (unlikely((idx) == (ring_size))) {\ - (idx) = 0;\ - } \ - } while (0) - -#define VMXNET3_SET_VFTABLE_ENTRY(vfTable, vid) \ - (vfTable[vid >> 5] |= (1 << (vid & 31))) -#define VMXNET3_CLEAR_VFTABLE_ENTRY(vfTable, vid) \ - (vfTable[vid >> 5] &= ~(1 << (vid & 31))) - -#define VMXNET3_VFTABLE_ENTRY_IS_SET(vfTable, vid) \ - ((vfTable[vid >> 5] & (1 << (vid & 31))) != 0) - -#define VMXNET3_MAX_MTU 9000 -#define VMXNET3_MIN_MTU 60 - -#define VMXNET3_LINK_UP (10000 << 16 | 1) /* 10 Gbps, up */ -#define VMXNET3_LINK_DOWN 0 - -#undef u64 -#undef u32 -#undef u16 -#undef u8 -#undef __le16 -#undef __le32 -#undef __le64 -#if defined(HOST_WORDS_BIGENDIAN) -#undef __BIG_ENDIAN_BITFIELD -#endif - -#endif diff --git a/qemu/hw/net/vmxnet_debug.h b/qemu/hw/net/vmxnet_debug.h deleted file mode 100644 index 96495dbb1..000000000 --- a/qemu/hw/net/vmxnet_debug.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * QEMU VMWARE VMXNET* paravirtual NICs - debugging facilities - * - * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) - * - * Developed by Daynix Computing LTD (http://www.daynix.com) - * - * Authors: - * Dmitry Fleytman - * Tamir Shomer - * Yan Vugenfirer - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef _QEMU_VMXNET_DEBUG_H -#define _QEMU_VMXNET_DEBUG_H - -#define VMXNET_DEVICE_NAME "vmxnet3" - -#define VMXNET_DEBUG_WARNINGS -#define VMXNET_DEBUG_ERRORS - -#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 { \ - if (VMXNET_DEBUG_SHMEM_ACCESS_ENABLED) { \ - printf("[%s][SH][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ - } \ - } while (0) - -#define VMW_CBPRN(fmt, ...) \ - do { \ - if (VMXNET_DEBUG_CB_ENABLED) { \ - printf("[%s][CB][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ - } \ - } while (0) - -#define VMW_PKPRN(fmt, ...) \ - do { \ - if (VMXNET_DEBUG_PACKETS_ENABLED) { \ - printf("[%s][PK][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ - } \ - } while (0) - -#define VMW_WRPRN(fmt, ...) \ - do { \ - if (VMXNET_DEBUG_WARNINGS_ENABLED) { \ - printf("[%s][WR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ - } \ - } while (0) - -#define VMW_ERPRN(fmt, ...) \ - do { \ - if (VMXNET_DEBUG_ERRORS_ENABLED) { \ - printf("[%s][ER][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ - } \ - } while (0) - -#define VMW_IRPRN(fmt, ...) \ - do { \ - if (VMXNET_DEBUG_INTERRUPTS_ENABLED) { \ - printf("[%s][IR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ - } \ - } while (0) - -#define VMW_CFPRN(fmt, ...) \ - do { \ - if (VMXNET_DEBUG_CONFIG_ENABLED) { \ - printf("[%s][CF][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ - } \ - } while (0) - -#define VMW_RIPRN(fmt, ...) \ - do { \ - if (VMXNET_DEBUG_RINGS_ENABLED) { \ - printf("[%s][RI][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__, \ - ## __VA_ARGS__); \ - } \ - } while (0) - -#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] - -#endif /* _QEMU_VMXNET3_DEBUG_H */ diff --git a/qemu/hw/net/vmxnet_rx_pkt.c b/qemu/hw/net/vmxnet_rx_pkt.c deleted file mode 100644 index 21bb46e68..000000000 --- a/qemu/hw/net/vmxnet_rx_pkt.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - * QEMU VMWARE VMXNET* paravirtual NICs - RX packets abstractions - * - * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) - * - * Developed by Daynix Computing LTD (http://www.daynix.com) - * - * Authors: - * Dmitry Fleytman - * Tamir Shomer - * Yan Vugenfirer - * - * 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 "vmxnet_rx_pkt.h" -#include "net/eth.h" -#include "qemu-common.h" -#include "qemu/iov.h" -#include "net/checksum.h" -#include "net/tap.h" - -/* - * RX packet may contain up to 2 fragments - rebuilt eth header - * in case of VLAN tag stripping - * and payload received from QEMU - in any case - */ -#define VMXNET_MAX_RX_PACKET_FRAGMENTS (2) - -struct VmxnetRxPkt { - struct virtio_net_hdr virt_hdr; - uint8_t ehdr_buf[ETH_MAX_L2_HDR_LEN]; - struct iovec vec[VMXNET_MAX_RX_PACKET_FRAGMENTS]; - uint16_t vec_len; - uint32_t tot_len; - uint16_t tci; - bool vlan_stripped; - bool has_virt_hdr; - eth_pkt_types_e packet_type; - - /* Analysis results */ - bool isip4; - bool isip6; - bool isudp; - bool istcp; -}; - -void vmxnet_rx_pkt_init(struct VmxnetRxPkt **pkt, bool has_virt_hdr) -{ - struct VmxnetRxPkt *p = g_malloc0(sizeof *p); - p->has_virt_hdr = has_virt_hdr; - *pkt = p; -} - -void vmxnet_rx_pkt_uninit(struct VmxnetRxPkt *pkt) -{ - g_free(pkt); -} - -struct virtio_net_hdr *vmxnet_rx_pkt_get_vhdr(struct VmxnetRxPkt *pkt) -{ - assert(pkt); - return &pkt->virt_hdr; -} - -void vmxnet_rx_pkt_attach_data(struct VmxnetRxPkt *pkt, const void *data, - size_t len, bool strip_vlan) -{ - uint16_t tci = 0; - uint16_t ploff; - assert(pkt); - pkt->vlan_stripped = false; - - if (strip_vlan) { - pkt->vlan_stripped = eth_strip_vlan(data, pkt->ehdr_buf, &ploff, &tci); - } - - if (pkt->vlan_stripped) { - pkt->vec[0].iov_base = pkt->ehdr_buf; - pkt->vec[0].iov_len = ploff - sizeof(struct vlan_header); - pkt->vec[1].iov_base = (uint8_t *) data + ploff; - pkt->vec[1].iov_len = len - ploff; - pkt->vec_len = 2; - pkt->tot_len = len - ploff + sizeof(struct eth_header); - } else { - pkt->vec[0].iov_base = (void *)data; - pkt->vec[0].iov_len = len; - pkt->vec_len = 1; - pkt->tot_len = len; - } - - pkt->tci = tci; -} - -void vmxnet_rx_pkt_dump(struct VmxnetRxPkt *pkt) -{ -#ifdef VMXNET_RX_PKT_DEBUG - VmxnetRxPkt *pkt = (VmxnetRxPkt *)pkt; - assert(pkt); - - printf("RX PKT: tot_len: %d, vlan_stripped: %d, vlan_tag: %d\n", - pkt->tot_len, pkt->vlan_stripped, pkt->tci); -#endif -} - -void vmxnet_rx_pkt_set_packet_type(struct VmxnetRxPkt *pkt, - eth_pkt_types_e packet_type) -{ - assert(pkt); - - pkt->packet_type = packet_type; - -} - -eth_pkt_types_e vmxnet_rx_pkt_get_packet_type(struct VmxnetRxPkt *pkt) -{ - assert(pkt); - - return pkt->packet_type; -} - -size_t vmxnet_rx_pkt_get_total_len(struct VmxnetRxPkt *pkt) -{ - assert(pkt); - - return pkt->tot_len; -} - -void vmxnet_rx_pkt_set_protocols(struct VmxnetRxPkt *pkt, const void *data, - size_t len) -{ - assert(pkt); - - eth_get_protocols(data, len, &pkt->isip4, &pkt->isip6, - &pkt->isudp, &pkt->istcp); -} - -void vmxnet_rx_pkt_get_protocols(struct VmxnetRxPkt *pkt, - bool *isip4, bool *isip6, - bool *isudp, bool *istcp) -{ - assert(pkt); - - *isip4 = pkt->isip4; - *isip6 = pkt->isip6; - *isudp = pkt->isudp; - *istcp = pkt->istcp; -} - -struct iovec *vmxnet_rx_pkt_get_iovec(struct VmxnetRxPkt *pkt) -{ - assert(pkt); - - return pkt->vec; -} - -void vmxnet_rx_pkt_set_vhdr(struct VmxnetRxPkt *pkt, - struct virtio_net_hdr *vhdr) -{ - assert(pkt); - - memcpy(&pkt->virt_hdr, vhdr, sizeof pkt->virt_hdr); -} - -bool vmxnet_rx_pkt_is_vlan_stripped(struct VmxnetRxPkt *pkt) -{ - assert(pkt); - - return pkt->vlan_stripped; -} - -bool vmxnet_rx_pkt_has_virt_hdr(struct VmxnetRxPkt *pkt) -{ - assert(pkt); - - return pkt->has_virt_hdr; -} - -uint16_t vmxnet_rx_pkt_get_vlan_tag(struct VmxnetRxPkt *pkt) -{ - assert(pkt); - - return pkt->tci; -} diff --git a/qemu/hw/net/vmxnet_rx_pkt.h b/qemu/hw/net/vmxnet_rx_pkt.h deleted file mode 100644 index 0a45c1ba0..000000000 --- a/qemu/hw/net/vmxnet_rx_pkt.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * QEMU VMWARE VMXNET* paravirtual NICs - RX packets abstraction - * - * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) - * - * Developed by Daynix Computing LTD (http://www.daynix.com) - * - * Authors: - * Dmitry Fleytman - * Tamir Shomer - * Yan Vugenfirer - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef VMXNET_RX_PKT_H -#define VMXNET_RX_PKT_H - -#include "net/eth.h" - -/* defines to enable packet dump functions */ -/*#define VMXNET_RX_PKT_DEBUG*/ - -struct VmxnetRxPkt; - -/** - * Clean all rx packet resources - * - * @pkt: packet - * - */ -void vmxnet_rx_pkt_uninit(struct VmxnetRxPkt *pkt); - -/** - * Init function for rx packet functionality - * - * @pkt: packet pointer - * @has_virt_hdr: device uses virtio header - * - */ -void vmxnet_rx_pkt_init(struct VmxnetRxPkt **pkt, bool has_virt_hdr); - -/** - * returns total length of data attached to rx context - * - * @pkt: packet - * - * Return: nothing - * - */ -size_t vmxnet_rx_pkt_get_total_len(struct VmxnetRxPkt *pkt); - -/** - * parse and set packet analysis results - * - * @pkt: packet - * @data: pointer to the data buffer to be parsed - * @len: data length - * - */ -void vmxnet_rx_pkt_set_protocols(struct VmxnetRxPkt *pkt, const void *data, - size_t len); - -/** - * fetches packet analysis results - * - * @pkt: packet - * @isip4: whether the packet given is IPv4 - * @isip6: whether the packet given is IPv6 - * @isudp: whether the packet given is UDP - * @istcp: whether the packet given is TCP - * - */ -void vmxnet_rx_pkt_get_protocols(struct VmxnetRxPkt *pkt, - bool *isip4, bool *isip6, - bool *isudp, bool *istcp); - -/** - * returns virtio header stored in rx context - * - * @pkt: packet - * @ret: virtio header - * - */ -struct virtio_net_hdr *vmxnet_rx_pkt_get_vhdr(struct VmxnetRxPkt *pkt); - -/** - * returns packet type - * - * @pkt: packet - * @ret: packet type - * - */ -eth_pkt_types_e vmxnet_rx_pkt_get_packet_type(struct VmxnetRxPkt *pkt); - -/** - * returns vlan tag - * - * @pkt: packet - * @ret: VLAN tag - * - */ -uint16_t vmxnet_rx_pkt_get_vlan_tag(struct VmxnetRxPkt *pkt); - -/** - * tells whether vlan was stripped from the packet - * - * @pkt: packet - * @ret: VLAN stripped sign - * - */ -bool vmxnet_rx_pkt_is_vlan_stripped(struct VmxnetRxPkt *pkt); - -/** - * notifies caller if the packet has virtio header - * - * @pkt: packet - * @ret: true if packet has virtio header, false otherwize - * - */ -bool vmxnet_rx_pkt_has_virt_hdr(struct VmxnetRxPkt *pkt); - -/** - * attach data to rx packet - * - * @pkt: packet - * @data: pointer to the data buffer - * @len: data length - * @strip_vlan: should the module strip vlan from data - * - */ -void vmxnet_rx_pkt_attach_data(struct VmxnetRxPkt *pkt, const void *data, - size_t len, bool strip_vlan); - -/** - * returns io vector that holds the attached data - * - * @pkt: packet - * @ret: pointer to IOVec - * - */ -struct iovec *vmxnet_rx_pkt_get_iovec(struct VmxnetRxPkt *pkt); - -/** - * prints rx packet data if debug is enabled - * - * @pkt: packet - * - */ -void vmxnet_rx_pkt_dump(struct VmxnetRxPkt *pkt); - -/** - * copy passed vhdr data to packet context - * - * @pkt: packet - * @vhdr: VHDR buffer - * - */ -void vmxnet_rx_pkt_set_vhdr(struct VmxnetRxPkt *pkt, - struct virtio_net_hdr *vhdr); - -/** - * save packet type in packet context - * - * @pkt: packet - * @packet_type: the packet type - * - */ -void vmxnet_rx_pkt_set_packet_type(struct VmxnetRxPkt *pkt, - eth_pkt_types_e packet_type); - -#endif diff --git a/qemu/hw/net/vmxnet_tx_pkt.c b/qemu/hw/net/vmxnet_tx_pkt.c deleted file mode 100644 index 91e1e08fd..000000000 --- a/qemu/hw/net/vmxnet_tx_pkt.c +++ /dev/null @@ -1,581 +0,0 @@ -/* - * QEMU VMWARE VMXNET* paravirtual NICs - TX packets abstractions - * - * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) - * - * Developed by Daynix Computing LTD (http://www.daynix.com) - * - * Authors: - * Dmitry Fleytman - * Tamir Shomer - * Yan Vugenfirer - * - * 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 "vmxnet_tx_pkt.h" -#include "net/eth.h" -#include "qemu-common.h" -#include "qemu/iov.h" -#include "net/checksum.h" -#include "net/tap.h" -#include "net/net.h" - -enum { - VMXNET_TX_PKT_VHDR_FRAG = 0, - VMXNET_TX_PKT_L2HDR_FRAG, - VMXNET_TX_PKT_L3HDR_FRAG, - VMXNET_TX_PKT_PL_START_FRAG -}; - -/* TX packet private context */ -struct VmxnetTxPkt { - struct virtio_net_hdr virt_hdr; - bool has_virt_hdr; - - struct iovec *raw; - uint32_t raw_frags; - uint32_t max_raw_frags; - - struct iovec *vec; - - uint8_t l2_hdr[ETH_MAX_L2_HDR_LEN]; - - uint32_t payload_len; - - uint32_t payload_frags; - uint32_t max_payload_frags; - - uint16_t hdr_len; - eth_pkt_types_e packet_type; - uint8_t l4proto; -}; - -void vmxnet_tx_pkt_init(struct VmxnetTxPkt **pkt, uint32_t max_frags, - bool has_virt_hdr) -{ - struct VmxnetTxPkt *p = g_malloc0(sizeof *p); - - p->vec = g_malloc((sizeof *p->vec) * - (max_frags + VMXNET_TX_PKT_PL_START_FRAG)); - - p->raw = g_malloc((sizeof *p->raw) * max_frags); - - p->max_payload_frags = max_frags; - p->max_raw_frags = max_frags; - p->has_virt_hdr = has_virt_hdr; - p->vec[VMXNET_TX_PKT_VHDR_FRAG].iov_base = &p->virt_hdr; - p->vec[VMXNET_TX_PKT_VHDR_FRAG].iov_len = - p->has_virt_hdr ? sizeof p->virt_hdr : 0; - p->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base = &p->l2_hdr; - p->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base = NULL; - p->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len = 0; - - *pkt = p; -} - -void vmxnet_tx_pkt_uninit(struct VmxnetTxPkt *pkt) -{ - if (pkt) { - g_free(pkt->vec); - g_free(pkt->raw); - g_free(pkt); - } -} - -void vmxnet_tx_pkt_update_ip_checksums(struct VmxnetTxPkt *pkt) -{ - uint16_t csum; - uint32_t ph_raw_csum; - assert(pkt); - uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN; - struct ip_header *ip_hdr; - - if (VIRTIO_NET_HDR_GSO_TCPV4 != gso_type && - VIRTIO_NET_HDR_GSO_UDP != gso_type) { - return; - } - - ip_hdr = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base; - - if (pkt->payload_len + pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len > - ETH_MAX_IP_DGRAM_LEN) { - return; - } - - ip_hdr->ip_len = cpu_to_be16(pkt->payload_len + - pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len); - - /* Calculate IP header checksum */ - ip_hdr->ip_sum = 0; - csum = net_raw_checksum((uint8_t *)ip_hdr, - pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len); - ip_hdr->ip_sum = cpu_to_be16(csum); - - /* Calculate IP pseudo header checksum */ - ph_raw_csum = eth_calc_pseudo_hdr_csum(ip_hdr, pkt->payload_len); - csum = cpu_to_be16(~net_checksum_finish(ph_raw_csum)); - iov_from_buf(&pkt->vec[VMXNET_TX_PKT_PL_START_FRAG], pkt->payload_frags, - pkt->virt_hdr.csum_offset, &csum, sizeof(csum)); -} - -static void vmxnet_tx_pkt_calculate_hdr_len(struct VmxnetTxPkt *pkt) -{ - pkt->hdr_len = pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len + - pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len; -} - -static bool vmxnet_tx_pkt_parse_headers(struct VmxnetTxPkt *pkt) -{ - struct iovec *l2_hdr, *l3_hdr; - size_t bytes_read; - size_t full_ip6hdr_len; - uint16_t l3_proto; - - assert(pkt); - - l2_hdr = &pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG]; - l3_hdr = &pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG]; - - bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 0, l2_hdr->iov_base, - 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; - } - - l3_proto = eth_get_l3_proto(l2_hdr->iov_base, l2_hdr->iov_len); - - switch (l3_proto) { - case ETH_P_IP: - l3_hdr->iov_base = g_malloc(ETH_MAX_IP4_HDR_LEN); - - bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, - l3_hdr->iov_base, sizeof(struct ip_header)); - - if (bytes_read < sizeof(struct ip_header)) { - l3_hdr->iov_len = 0; - return false; - } - - l3_hdr->iov_len = IP_HDR_GET_LEN(l3_hdr->iov_base); - pkt->l4proto = ((struct ip_header *) l3_hdr->iov_base)->ip_p; - - /* copy optional IPv4 header data */ - bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, - l2_hdr->iov_len + sizeof(struct ip_header), - l3_hdr->iov_base + sizeof(struct ip_header), - l3_hdr->iov_len - sizeof(struct ip_header)); - if (bytes_read < l3_hdr->iov_len - sizeof(struct ip_header)) { - l3_hdr->iov_len = 0; - return false; - } - break; - - case ETH_P_IPV6: - if (!eth_parse_ipv6_hdr(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, - &pkt->l4proto, &full_ip6hdr_len)) { - l3_hdr->iov_len = 0; - return false; - } - - l3_hdr->iov_base = g_malloc(full_ip6hdr_len); - - bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len, - l3_hdr->iov_base, full_ip6hdr_len); - - if (bytes_read < full_ip6hdr_len) { - l3_hdr->iov_len = 0; - return false; - } else { - l3_hdr->iov_len = full_ip6hdr_len; - } - break; - - default: - l3_hdr->iov_len = 0; - break; - } - - vmxnet_tx_pkt_calculate_hdr_len(pkt); - pkt->packet_type = get_eth_packet_type(l2_hdr->iov_base); - return true; -} - -static bool vmxnet_tx_pkt_rebuild_payload(struct VmxnetTxPkt *pkt) -{ - size_t payload_len = iov_size(pkt->raw, pkt->raw_frags) - pkt->hdr_len; - - pkt->payload_frags = iov_copy(&pkt->vec[VMXNET_TX_PKT_PL_START_FRAG], - pkt->max_payload_frags, - pkt->raw, pkt->raw_frags, - pkt->hdr_len, payload_len); - - if (pkt->payload_frags != (uint32_t) -1) { - pkt->payload_len = payload_len; - return true; - } else { - return false; - } -} - -bool vmxnet_tx_pkt_parse(struct VmxnetTxPkt *pkt) -{ - return vmxnet_tx_pkt_parse_headers(pkt) && - vmxnet_tx_pkt_rebuild_payload(pkt); -} - -struct virtio_net_hdr *vmxnet_tx_pkt_get_vhdr(struct VmxnetTxPkt *pkt) -{ - assert(pkt); - return &pkt->virt_hdr; -} - -static uint8_t vmxnet_tx_pkt_get_gso_type(struct VmxnetTxPkt *pkt, - bool tso_enable) -{ - uint8_t rc = VIRTIO_NET_HDR_GSO_NONE; - uint16_t l3_proto; - - l3_proto = eth_get_l3_proto(pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base, - pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len); - - if (!tso_enable) { - goto func_exit; - } - - rc = eth_get_gso_type(l3_proto, pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base, - pkt->l4proto); - -func_exit: - return rc; -} - -void vmxnet_tx_pkt_build_vheader(struct VmxnetTxPkt *pkt, bool tso_enable, - bool csum_enable, uint32_t gso_size) -{ - struct tcp_hdr l4hdr; - assert(pkt); - - /* csum has to be enabled if tso is. */ - assert(csum_enable || !tso_enable); - - pkt->virt_hdr.gso_type = vmxnet_tx_pkt_get_gso_type(pkt, tso_enable); - - switch (pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { - case VIRTIO_NET_HDR_GSO_NONE: - pkt->virt_hdr.hdr_len = 0; - pkt->virt_hdr.gso_size = 0; - break; - - case VIRTIO_NET_HDR_GSO_UDP: - pkt->virt_hdr.gso_size = IP_FRAG_ALIGN_SIZE(gso_size); - pkt->virt_hdr.hdr_len = pkt->hdr_len + sizeof(struct udp_header); - break; - - case VIRTIO_NET_HDR_GSO_TCPV4: - case VIRTIO_NET_HDR_GSO_TCPV6: - iov_to_buf(&pkt->vec[VMXNET_TX_PKT_PL_START_FRAG], pkt->payload_frags, - 0, &l4hdr, sizeof(l4hdr)); - pkt->virt_hdr.hdr_len = pkt->hdr_len + l4hdr.th_off * sizeof(uint32_t); - pkt->virt_hdr.gso_size = IP_FRAG_ALIGN_SIZE(gso_size); - break; - - default: - g_assert_not_reached(); - } - - if (csum_enable) { - switch (pkt->l4proto) { - case IP_PROTO_TCP: - pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; - pkt->virt_hdr.csum_start = pkt->hdr_len; - pkt->virt_hdr.csum_offset = offsetof(struct tcp_hdr, th_sum); - break; - case IP_PROTO_UDP: - pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; - pkt->virt_hdr.csum_start = pkt->hdr_len; - pkt->virt_hdr.csum_offset = offsetof(struct udp_hdr, uh_sum); - break; - default: - break; - } - } -} - -void vmxnet_tx_pkt_setup_vlan_header(struct VmxnetTxPkt *pkt, uint16_t vlan) -{ - bool is_new; - assert(pkt); - - eth_setup_vlan_headers(pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base, - vlan, &is_new); - - /* update l2hdrlen */ - if (is_new) { - pkt->hdr_len += sizeof(struct vlan_header); - pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len += - sizeof(struct vlan_header); - } -} - -bool vmxnet_tx_pkt_add_raw_fragment(struct VmxnetTxPkt *pkt, hwaddr pa, - size_t len) -{ - hwaddr mapped_len = 0; - struct iovec *ventry; - assert(pkt); - assert(pkt->max_raw_frags > pkt->raw_frags); - - if (!len) { - return true; - } - - ventry = &pkt->raw[pkt->raw_frags]; - mapped_len = len; - - ventry->iov_base = cpu_physical_memory_map(pa, &mapped_len, false); - ventry->iov_len = mapped_len; - pkt->raw_frags += !!ventry->iov_base; - - if ((ventry->iov_base == NULL) || (len != mapped_len)) { - return false; - } - - return true; -} - -eth_pkt_types_e vmxnet_tx_pkt_get_packet_type(struct VmxnetTxPkt *pkt) -{ - assert(pkt); - - return pkt->packet_type; -} - -size_t vmxnet_tx_pkt_get_total_len(struct VmxnetTxPkt *pkt) -{ - assert(pkt); - - return pkt->hdr_len + pkt->payload_len; -} - -void vmxnet_tx_pkt_dump(struct VmxnetTxPkt *pkt) -{ -#ifdef VMXNET_TX_PKT_DEBUG - assert(pkt); - - printf("TX PKT: hdr_len: %d, pkt_type: 0x%X, l2hdr_len: %lu, " - "l3hdr_len: %lu, payload_len: %u\n", pkt->hdr_len, pkt->packet_type, - pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len, - pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len, pkt->payload_len); -#endif -} - -void vmxnet_tx_pkt_reset(struct VmxnetTxPkt *pkt) -{ - int i; - - /* no assert, as reset can be called before tx_pkt_init */ - if (!pkt) { - return; - } - - memset(&pkt->virt_hdr, 0, sizeof(pkt->virt_hdr)); - - g_free(pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base); - pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base = NULL; - - assert(pkt->vec); - for (i = VMXNET_TX_PKT_L2HDR_FRAG; - i < pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG; i++) { - pkt->vec[i].iov_len = 0; - } - pkt->payload_len = 0; - pkt->payload_frags = 0; - - assert(pkt->raw); - for (i = 0; i < pkt->raw_frags; i++) { - assert(pkt->raw[i].iov_base); - cpu_physical_memory_unmap(pkt->raw[i].iov_base, pkt->raw[i].iov_len, - false, pkt->raw[i].iov_len); - pkt->raw[i].iov_len = 0; - } - pkt->raw_frags = 0; - - pkt->hdr_len = 0; - pkt->packet_type = 0; - pkt->l4proto = 0; -} - -static void vmxnet_tx_pkt_do_sw_csum(struct VmxnetTxPkt *pkt) -{ - struct iovec *iov = &pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG]; - uint32_t csum_cntr; - uint16_t csum = 0; - /* num of iovec without vhdr */ - uint32_t iov_len = pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG - 1; - uint16_t csl; - struct ip_header *iphdr; - size_t csum_offset = pkt->virt_hdr.csum_start + pkt->virt_hdr.csum_offset; - - /* Put zero to checksum field */ - iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum); - - /* Calculate L4 TCP/UDP checksum */ - csl = pkt->payload_len; - - /* data checksum */ - csum_cntr = - net_checksum_add_iov(iov, iov_len, pkt->virt_hdr.csum_start, csl); - /* add pseudo header to csum */ - iphdr = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base; - csum_cntr += eth_calc_pseudo_hdr_csum(iphdr, csl); - - /* Put the checksum obtained into the packet */ - csum = cpu_to_be16(net_checksum_finish(csum_cntr)); - iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum); -} - -enum { - VMXNET_TX_PKT_FRAGMENT_L2_HDR_POS = 0, - VMXNET_TX_PKT_FRAGMENT_L3_HDR_POS, - VMXNET_TX_PKT_FRAGMENT_HEADER_NUM -}; - -#define VMXNET_MAX_FRAG_SG_LIST (64) - -static size_t vmxnet_tx_pkt_fetch_fragment(struct VmxnetTxPkt *pkt, - int *src_idx, size_t *src_offset, struct iovec *dst, int *dst_idx) -{ - size_t fetched = 0; - struct iovec *src = pkt->vec; - - *dst_idx = VMXNET_TX_PKT_FRAGMENT_HEADER_NUM; - - while (fetched < pkt->virt_hdr.gso_size) { - - /* no more place in fragment iov */ - if (*dst_idx == VMXNET_MAX_FRAG_SG_LIST) { - break; - } - - /* no more data in iovec */ - if (*src_idx == (pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG)) { - break; - } - - - dst[*dst_idx].iov_base = src[*src_idx].iov_base + *src_offset; - dst[*dst_idx].iov_len = MIN(src[*src_idx].iov_len - *src_offset, - pkt->virt_hdr.gso_size - fetched); - - *src_offset += dst[*dst_idx].iov_len; - fetched += dst[*dst_idx].iov_len; - - if (*src_offset == src[*src_idx].iov_len) { - *src_offset = 0; - (*src_idx)++; - } - - (*dst_idx)++; - } - - return fetched; -} - -static bool vmxnet_tx_pkt_do_sw_fragmentation(struct VmxnetTxPkt *pkt, - NetClientState *nc) -{ - struct iovec fragment[VMXNET_MAX_FRAG_SG_LIST]; - size_t fragment_len = 0; - bool more_frags = false; - - /* some pointers for shorter code */ - void *l2_iov_base, *l3_iov_base; - size_t l2_iov_len, l3_iov_len; - int src_idx = VMXNET_TX_PKT_PL_START_FRAG, dst_idx; - size_t src_offset = 0; - size_t fragment_offset = 0; - - l2_iov_base = pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base; - l2_iov_len = pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len; - l3_iov_base = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base; - l3_iov_len = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len; - - /* Copy headers */ - fragment[VMXNET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_base = l2_iov_base; - fragment[VMXNET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_len = l2_iov_len; - fragment[VMXNET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_base = l3_iov_base; - fragment[VMXNET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_len = l3_iov_len; - - - /* Put as much data as possible and send */ - do { - fragment_len = vmxnet_tx_pkt_fetch_fragment(pkt, &src_idx, &src_offset, - fragment, &dst_idx); - - more_frags = (fragment_offset + fragment_len < pkt->payload_len); - - eth_setup_ip4_fragmentation(l2_iov_base, l2_iov_len, l3_iov_base, - l3_iov_len, fragment_len, fragment_offset, more_frags); - - eth_fix_ip4_checksum(l3_iov_base, l3_iov_len); - - qemu_sendv_packet(nc, fragment, dst_idx); - - fragment_offset += fragment_len; - - } while (more_frags); - - return true; -} - -bool vmxnet_tx_pkt_send(struct VmxnetTxPkt *pkt, NetClientState *nc) -{ - assert(pkt); - - if (!pkt->has_virt_hdr && - pkt->virt_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { - vmxnet_tx_pkt_do_sw_csum(pkt); - } - - /* - * Since underlying infrastructure does not support IP datagrams longer - * than 64K we should drop such packets and don't even try to send - */ - if (VIRTIO_NET_HDR_GSO_NONE != pkt->virt_hdr.gso_type) { - if (pkt->payload_len > - ETH_MAX_IP_DGRAM_LEN - - pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len) { - return false; - } - } - - if (pkt->has_virt_hdr || - pkt->virt_hdr.gso_type == VIRTIO_NET_HDR_GSO_NONE) { - qemu_sendv_packet(nc, pkt->vec, - pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG); - return true; - } - - return vmxnet_tx_pkt_do_sw_fragmentation(pkt, nc); -} diff --git a/qemu/hw/net/vmxnet_tx_pkt.h b/qemu/hw/net/vmxnet_tx_pkt.h deleted file mode 100644 index f51e98ad9..000000000 --- a/qemu/hw/net/vmxnet_tx_pkt.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * QEMU VMWARE VMXNET* paravirtual NICs - TX packets abstraction - * - * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) - * - * Developed by Daynix Computing LTD (http://www.daynix.com) - * - * Authors: - * Dmitry Fleytman - * Tamir Shomer - * Yan Vugenfirer - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef VMXNET_TX_PKT_H -#define VMXNET_TX_PKT_H - -#include "net/eth.h" -#include "exec/hwaddr.h" - -/* define to enable packet dump functions */ -/*#define VMXNET_TX_PKT_DEBUG*/ - -struct VmxnetTxPkt; - -/** - * Init function for tx packet functionality - * - * @pkt: packet pointer - * @max_frags: max tx ip fragments - * @has_virt_hdr: device uses virtio header. - */ -void vmxnet_tx_pkt_init(struct VmxnetTxPkt **pkt, uint32_t max_frags, - bool has_virt_hdr); - -/** - * Clean all tx packet resources. - * - * @pkt: packet. - */ -void vmxnet_tx_pkt_uninit(struct VmxnetTxPkt *pkt); - -/** - * get virtio header - * - * @pkt: packet - * @ret: virtio header - */ -struct virtio_net_hdr *vmxnet_tx_pkt_get_vhdr(struct VmxnetTxPkt *pkt); - -/** - * build virtio header (will be stored in module context) - * - * @pkt: packet - * @tso_enable: TSO enabled - * @csum_enable: CSO enabled - * @gso_size: MSS size for TSO - * - */ -void vmxnet_tx_pkt_build_vheader(struct VmxnetTxPkt *pkt, bool tso_enable, - bool csum_enable, uint32_t gso_size); - -/** - * updates vlan tag, and adds vlan header in case it is missing - * - * @pkt: packet - * @vlan: VLAN tag - * - */ -void vmxnet_tx_pkt_setup_vlan_header(struct VmxnetTxPkt *pkt, uint16_t vlan); - -/** - * populate data fragment into pkt context. - * - * @pkt: packet - * @pa: physical address of fragment - * @len: length of fragment - * - */ -bool vmxnet_tx_pkt_add_raw_fragment(struct VmxnetTxPkt *pkt, hwaddr pa, - size_t len); - -/** - * fix ip header fields and calculate checksums needed. - * - * @pkt: packet - * - */ -void vmxnet_tx_pkt_update_ip_checksums(struct VmxnetTxPkt *pkt); - -/** - * get length of all populated data. - * - * @pkt: packet - * @ret: total data length - * - */ -size_t vmxnet_tx_pkt_get_total_len(struct VmxnetTxPkt *pkt); - -/** - * get packet type - * - * @pkt: packet - * @ret: packet type - * - */ -eth_pkt_types_e vmxnet_tx_pkt_get_packet_type(struct VmxnetTxPkt *pkt); - -/** - * prints packet data if debug is enabled - * - * @pkt: packet - * - */ -void vmxnet_tx_pkt_dump(struct VmxnetTxPkt *pkt); - -/** - * reset tx packet private context (needed to be called between packets) - * - * @pkt: packet - * - */ -void vmxnet_tx_pkt_reset(struct VmxnetTxPkt *pkt); - -/** - * Send packet to qemu. handles sw offloads if vhdr is not supported. - * - * @pkt: packet - * @nc: NetClientState - * @ret: operation result - * - */ -bool vmxnet_tx_pkt_send(struct VmxnetTxPkt *pkt, NetClientState *nc); - -/** - * parse raw packet data and analyze offload requirements. - * - * @pkt: packet - * - */ -bool vmxnet_tx_pkt_parse(struct VmxnetTxPkt *pkt); - -#endif diff --git a/qemu/hw/net/xen_nic.c b/qemu/hw/net/xen_nic.c deleted file mode 100644 index 7281730d9..000000000 --- a/qemu/hw/net/xen_nic.c +++ /dev/null @@ -1,412 +0,0 @@ -/* - * xen paravirt network card backend - * - * (c) Gerd Hoffmann - * - * 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; under version 2 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 . - * - * 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 -#include -#include -#include - -#include "hw/hw.h" -#include "net/net.h" -#include "net/checksum.h" -#include "net/util.h" -#include "hw/xen/xen_backend.h" - -#include - -/* ------------------------------------------------------------- */ - -struct XenNetDev { - struct XenDevice xendev; /* must be first */ - char *mac; - int tx_work; - int tx_ring_ref; - int rx_ring_ref; - struct netif_tx_sring *txs; - struct netif_rx_sring *rxs; - netif_tx_back_ring_t tx_ring; - netif_rx_back_ring_t rx_ring; - NICConf conf; - NICState *nic; -}; - -/* ------------------------------------------------------------- */ - -static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, int8_t st) -{ - RING_IDX i = netdev->tx_ring.rsp_prod_pvt; - netif_tx_response_t *resp; - int notify; - - resp = RING_GET_RESPONSE(&netdev->tx_ring, i); - resp->id = txp->id; - resp->status = st; - -#if 0 - if (txp->flags & NETTXF_extra_info) { - RING_GET_RESPONSE(&netdev->tx_ring, ++i)->status = NETIF_RSP_NULL; - } -#endif - - netdev->tx_ring.rsp_prod_pvt = ++i; - RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify); - if (notify) { - xen_be_send_notify(&netdev->xendev); - } - - if (i == netdev->tx_ring.req_cons) { - int more_to_do; - RING_FINAL_CHECK_FOR_REQUESTS(&netdev->tx_ring, more_to_do); - if (more_to_do) { - netdev->tx_work++; - } - } -} - -static void net_tx_error(struct XenNetDev *netdev, netif_tx_request_t *txp, RING_IDX end) -{ -#if 0 - /* - * Hmm, why netback fails everything in the ring? - * Should we do that even when not supporting SG and TSO? - */ - RING_IDX cons = netdev->tx_ring.req_cons; - - do { - make_tx_response(netif, txp, NETIF_RSP_ERROR); - if (cons >= end) { - break; - } - txp = RING_GET_REQUEST(&netdev->tx_ring, cons++); - } while (1); - netdev->tx_ring.req_cons = cons; - netif_schedule_work(netif); - netif_put(netif); -#else - net_tx_response(netdev, txp, NETIF_RSP_ERROR); -#endif -} - -static void net_tx_packets(struct XenNetDev *netdev) -{ - netif_tx_request_t txreq; - RING_IDX rc, rp; - void *page; - void *tmpbuf = NULL; - - for (;;) { - rc = netdev->tx_ring.req_cons; - rp = netdev->tx_ring.sring->req_prod; - xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ - - while ((rc != rp)) { - if (RING_REQUEST_CONS_OVERFLOW(&netdev->tx_ring, rc)) { - break; - } - memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq)); - netdev->tx_ring.req_cons = ++rc; - -#if 1 - /* should not happen in theory, we don't announce the * - * feature-{sg,gso,whatelse} flags in xenstore (yet?) */ - if (txreq.flags & NETTXF_extra_info) { - xen_be_printf(&netdev->xendev, 0, "FIXME: extra info flag\n"); - net_tx_error(netdev, &txreq, rc); - continue; - } - if (txreq.flags & NETTXF_more_data) { - xen_be_printf(&netdev->xendev, 0, "FIXME: more data flag\n"); - net_tx_error(netdev, &txreq, rc); - continue; - } -#endif - - if (txreq.size < 14) { - xen_be_printf(&netdev->xendev, 0, "bad packet size: %d\n", txreq.size); - net_tx_error(netdev, &txreq, rc); - continue; - } - - if ((txreq.offset + txreq.size) > XC_PAGE_SIZE) { - xen_be_printf(&netdev->xendev, 0, "error: page crossing\n"); - net_tx_error(netdev, &txreq, rc); - continue; - } - - xen_be_printf(&netdev->xendev, 3, "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n", - txreq.gref, txreq.offset, txreq.size, txreq.flags, - (txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "", - (txreq.flags & NETTXF_data_validated) ? " data_validated" : "", - (txreq.flags & NETTXF_more_data) ? " more_data" : "", - (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); - - page = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - txreq.gref, PROT_READ); - if (page == NULL) { - xen_be_printf(&netdev->xendev, 0, "error: tx gref dereference failed (%d)\n", - txreq.gref); - net_tx_error(netdev, &txreq, rc); - continue; - } - if (txreq.flags & NETTXF_csum_blank) { - /* have read-only mapping -> can't fill checksum in-place */ - if (!tmpbuf) { - tmpbuf = g_malloc(XC_PAGE_SIZE); - } - memcpy(tmpbuf, page + txreq.offset, txreq.size); - net_checksum_calculate(tmpbuf, txreq.size); - qemu_send_packet(qemu_get_queue(netdev->nic), tmpbuf, - txreq.size); - } else { - qemu_send_packet(qemu_get_queue(netdev->nic), - page + txreq.offset, txreq.size); - } - xengnttab_unmap(netdev->xendev.gnttabdev, page, 1); - net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); - } - if (!netdev->tx_work) { - break; - } - netdev->tx_work = 0; - } - g_free(tmpbuf); -} - -/* ------------------------------------------------------------- */ - -static void net_rx_response(struct XenNetDev *netdev, - netif_rx_request_t *req, int8_t st, - uint16_t offset, uint16_t size, - uint16_t flags) -{ - RING_IDX i = netdev->rx_ring.rsp_prod_pvt; - netif_rx_response_t *resp; - int notify; - - resp = RING_GET_RESPONSE(&netdev->rx_ring, i); - resp->offset = offset; - resp->flags = flags; - resp->id = req->id; - resp->status = (int16_t)size; - if (st < 0) { - resp->status = (int16_t)st; - } - - xen_be_printf(&netdev->xendev, 3, "rx response: idx %d, status %d, flags 0x%x\n", - i, resp->status, resp->flags); - - netdev->rx_ring.rsp_prod_pvt = ++i; - RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify); - if (notify) { - xen_be_send_notify(&netdev->xendev); - } -} - -#define NET_IP_ALIGN 2 - -static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size) -{ - struct XenNetDev *netdev = qemu_get_nic_opaque(nc); - netif_rx_request_t rxreq; - RING_IDX rc, rp; - void *page; - - if (netdev->xendev.be_state != XenbusStateConnected) { - return -1; - } - - rc = netdev->rx_ring.req_cons; - rp = netdev->rx_ring.sring->req_prod; - xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ - - if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { - return 0; - } - if (size > XC_PAGE_SIZE - NET_IP_ALIGN) { - xen_be_printf(&netdev->xendev, 0, "packet too big (%lu > %ld)", - (unsigned long)size, XC_PAGE_SIZE - NET_IP_ALIGN); - return -1; - } - - memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq)); - netdev->rx_ring.req_cons = ++rc; - - page = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - rxreq.gref, PROT_WRITE); - if (page == NULL) { - xen_be_printf(&netdev->xendev, 0, "error: rx gref dereference failed (%d)\n", - rxreq.gref); - net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0); - return -1; - } - memcpy(page + NET_IP_ALIGN, buf, size); - xengnttab_unmap(netdev->xendev.gnttabdev, page, 1); - net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0); - - return size; -} - -/* ------------------------------------------------------------- */ - -static NetClientInfo net_xen_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = net_rx_packet, -}; - -static int net_init(struct XenDevice *xendev) -{ - struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); - - /* read xenstore entries */ - if (netdev->mac == NULL) { - netdev->mac = xenstore_read_be_str(&netdev->xendev, "mac"); - } - - /* do we have all we need? */ - if (netdev->mac == NULL) { - return -1; - } - - if (net_parse_macaddr(netdev->conf.macaddr.a, netdev->mac) < 0) { - return -1; - } - - netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf, - "xen", NULL, netdev); - - snprintf(qemu_get_queue(netdev->nic)->info_str, - sizeof(qemu_get_queue(netdev->nic)->info_str), - "nic: xenbus vif macaddr=%s", netdev->mac); - - /* fill info */ - xenstore_write_be_int(&netdev->xendev, "feature-rx-copy", 1); - xenstore_write_be_int(&netdev->xendev, "feature-rx-flip", 0); - - return 0; -} - -static int net_connect(struct XenDevice *xendev) -{ - struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); - int rx_copy; - - if (xenstore_read_fe_int(&netdev->xendev, "tx-ring-ref", - &netdev->tx_ring_ref) == -1) { - return -1; - } - if (xenstore_read_fe_int(&netdev->xendev, "rx-ring-ref", - &netdev->rx_ring_ref) == -1) { - return 1; - } - if (xenstore_read_fe_int(&netdev->xendev, "event-channel", - &netdev->xendev.remote_port) == -1) { - return -1; - } - - if (xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy) == -1) { - rx_copy = 0; - } - if (rx_copy == 0) { - xen_be_printf(&netdev->xendev, 0, "frontend doesn't support rx-copy.\n"); - return -1; - } - - 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 = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - netdev->rx_ring_ref, - PROT_READ | PROT_WRITE); - if (!netdev->rxs) { - xengnttab_unmap(netdev->xendev.gnttabdev, netdev->txs, 1); - netdev->txs = NULL; - return -1; - } - BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XC_PAGE_SIZE); - BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XC_PAGE_SIZE); - - xen_be_bind_evtchn(&netdev->xendev); - - xen_be_printf(&netdev->xendev, 1, "ok: tx-ring-ref %d, rx-ring-ref %d, " - "remote port %d, local port %d\n", - netdev->tx_ring_ref, netdev->rx_ring_ref, - netdev->xendev.remote_port, netdev->xendev.local_port); - - net_tx_packets(netdev); - return 0; -} - -static void net_disconnect(struct XenDevice *xendev) -{ - struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); - - xen_be_unbind_evtchn(&netdev->xendev); - - if (netdev->txs) { - xengnttab_unmap(netdev->xendev.gnttabdev, netdev->txs, 1); - netdev->txs = NULL; - } - if (netdev->rxs) { - xengnttab_unmap(netdev->xendev.gnttabdev, netdev->rxs, 1); - netdev->rxs = NULL; - } -} - -static void net_event(struct XenDevice *xendev) -{ - struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); - net_tx_packets(netdev); - qemu_flush_queued_packets(qemu_get_queue(netdev->nic)); -} - -static int net_free(struct XenDevice *xendev) -{ - struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); - - if (netdev->nic) { - qemu_del_nic(netdev->nic); - netdev->nic = NULL; - } - g_free(netdev->mac); - netdev->mac = NULL; - return 0; -} - -/* ------------------------------------------------------------- */ - -struct XenDevOps xen_netdev_ops = { - .size = sizeof(struct XenNetDev), - .flags = DEVOPS_FLAG_NEED_GNTDEV, - .init = net_init, - .initialise = net_connect, - .event = net_event, - .disconnect = net_disconnect, - .free = net_free, -}; diff --git a/qemu/hw/net/xgmac.c b/qemu/hw/net/xgmac.c deleted file mode 100644 index 0c5f793bd..000000000 --- a/qemu/hw/net/xgmac.c +++ /dev/null @@ -1,433 +0,0 @@ -/* - * QEMU model of XGMAC Ethernet. - * - * derived from the Xilinx AXI-Ethernet by Edgar E. Iglesias. - * - * Copyright (c) 2011 Calxeda, Inc. - * - * 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/sysbus.h" -#include "sysemu/char.h" -#include "qemu/log.h" -#include "net/net.h" -#include "net/checksum.h" - -#ifdef DEBUG_XGMAC -#define DEBUGF_BRK(message, args...) do { \ - fprintf(stderr, (message), ## args); \ - } while (0) -#else -#define DEBUGF_BRK(message, args...) do { } while (0) -#endif - -#define XGMAC_CONTROL 0x00000000 /* MAC Configuration */ -#define XGMAC_FRAME_FILTER 0x00000001 /* MAC Frame Filter */ -#define XGMAC_FLOW_CTRL 0x00000006 /* MAC Flow Control */ -#define XGMAC_VLAN_TAG 0x00000007 /* VLAN Tags */ -#define XGMAC_VERSION 0x00000008 /* Version */ -/* VLAN tag for insertion or replacement into tx frames */ -#define XGMAC_VLAN_INCL 0x00000009 -#define XGMAC_LPI_CTRL 0x0000000a /* LPI Control and Status */ -#define XGMAC_LPI_TIMER 0x0000000b /* LPI Timers Control */ -#define XGMAC_TX_PACE 0x0000000c /* Transmit Pace and Stretch */ -#define XGMAC_VLAN_HASH 0x0000000d /* VLAN Hash Table */ -#define XGMAC_DEBUG 0x0000000e /* Debug */ -#define XGMAC_INT_STATUS 0x0000000f /* Interrupt and Control */ -/* HASH table registers */ -#define XGMAC_HASH(n) ((0x00000300/4) + (n)) -#define XGMAC_NUM_HASH 16 -/* Operation Mode */ -#define XGMAC_OPMODE (0x00000400/4) -/* Remote Wake-Up Frame Filter */ -#define XGMAC_REMOTE_WAKE (0x00000700/4) -/* PMT Control and Status */ -#define XGMAC_PMT (0x00000704/4) - -#define XGMAC_ADDR_HIGH(reg) (0x00000010+((reg) * 2)) -#define XGMAC_ADDR_LOW(reg) (0x00000011+((reg) * 2)) - -#define DMA_BUS_MODE 0x000003c0 /* Bus Mode */ -#define DMA_XMT_POLL_DEMAND 0x000003c1 /* Transmit Poll Demand */ -#define DMA_RCV_POLL_DEMAND 0x000003c2 /* Received Poll Demand */ -#define DMA_RCV_BASE_ADDR 0x000003c3 /* Receive List Base */ -#define DMA_TX_BASE_ADDR 0x000003c4 /* Transmit List Base */ -#define DMA_STATUS 0x000003c5 /* Status Register */ -#define DMA_CONTROL 0x000003c6 /* Ctrl (Operational Mode) */ -#define DMA_INTR_ENA 0x000003c7 /* Interrupt Enable */ -#define DMA_MISSED_FRAME_CTR 0x000003c8 /* Missed Frame Counter */ -/* Receive Interrupt Watchdog Timer */ -#define DMA_RI_WATCHDOG_TIMER 0x000003c9 -#define DMA_AXI_BUS 0x000003ca /* AXI Bus Mode */ -#define DMA_AXI_STATUS 0x000003cb /* AXI Status */ -#define DMA_CUR_TX_DESC_ADDR 0x000003d2 /* Current Host Tx Descriptor */ -#define DMA_CUR_RX_DESC_ADDR 0x000003d3 /* Current Host Rx Descriptor */ -#define DMA_CUR_TX_BUF_ADDR 0x000003d4 /* Current Host Tx Buffer */ -#define DMA_CUR_RX_BUF_ADDR 0x000003d5 /* Current Host Rx Buffer */ -#define DMA_HW_FEATURE 0x000003d6 /* Enabled Hardware Features */ - -/* DMA Status register defines */ -#define DMA_STATUS_GMI 0x08000000 /* MMC interrupt */ -#define DMA_STATUS_GLI 0x04000000 /* GMAC Line interface int */ -#define DMA_STATUS_EB_MASK 0x00380000 /* Error Bits Mask */ -#define DMA_STATUS_EB_TX_ABORT 0x00080000 /* Error Bits - TX Abort */ -#define DMA_STATUS_EB_RX_ABORT 0x00100000 /* Error Bits - RX Abort */ -#define DMA_STATUS_TS_MASK 0x00700000 /* Transmit Process State */ -#define DMA_STATUS_TS_SHIFT 20 -#define DMA_STATUS_RS_MASK 0x000e0000 /* Receive Process State */ -#define DMA_STATUS_RS_SHIFT 17 -#define DMA_STATUS_NIS 0x00010000 /* Normal Interrupt Summary */ -#define DMA_STATUS_AIS 0x00008000 /* Abnormal Interrupt Summary */ -#define DMA_STATUS_ERI 0x00004000 /* Early Receive Interrupt */ -#define DMA_STATUS_FBI 0x00002000 /* Fatal Bus Error Interrupt */ -#define DMA_STATUS_ETI 0x00000400 /* Early Transmit Interrupt */ -#define DMA_STATUS_RWT 0x00000200 /* Receive Watchdog Timeout */ -#define DMA_STATUS_RPS 0x00000100 /* Receive Process Stopped */ -#define DMA_STATUS_RU 0x00000080 /* Receive Buffer Unavailable */ -#define DMA_STATUS_RI 0x00000040 /* Receive Interrupt */ -#define DMA_STATUS_UNF 0x00000020 /* Transmit Underflow */ -#define DMA_STATUS_OVF 0x00000010 /* Receive Overflow */ -#define DMA_STATUS_TJT 0x00000008 /* Transmit Jabber Timeout */ -#define DMA_STATUS_TU 0x00000004 /* Transmit Buffer Unavailable */ -#define DMA_STATUS_TPS 0x00000002 /* Transmit Process Stopped */ -#define DMA_STATUS_TI 0x00000001 /* Transmit Interrupt */ - -/* DMA Control register defines */ -#define DMA_CONTROL_ST 0x00002000 /* Start/Stop Transmission */ -#define DMA_CONTROL_SR 0x00000002 /* Start/Stop Receive */ -#define DMA_CONTROL_DFF 0x01000000 /* Disable flush of rx frames */ - -struct desc { - uint32_t ctl_stat; - uint16_t buffer1_size; - uint16_t buffer2_size; - uint32_t buffer1_addr; - uint32_t buffer2_addr; - uint32_t ext_stat; - uint32_t res[3]; -}; - -#define R_MAX 0x400 - -typedef struct RxTxStats { - uint64_t rx_bytes; - uint64_t tx_bytes; - - uint64_t rx; - uint64_t rx_bcast; - uint64_t rx_mcast; -} RxTxStats; - -#define TYPE_XGMAC "xgmac" -#define XGMAC(obj) OBJECT_CHECK(XgmacState, (obj), TYPE_XGMAC) - -typedef struct XgmacState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq sbd_irq; - qemu_irq pmt_irq; - qemu_irq mci_irq; - NICState *nic; - NICConf conf; - - struct RxTxStats stats; - uint32_t regs[R_MAX]; -} XgmacState; - -static const VMStateDescription vmstate_rxtx_stats = { - .name = "xgmac_stats", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT64(rx_bytes, RxTxStats), - VMSTATE_UINT64(tx_bytes, RxTxStats), - VMSTATE_UINT64(rx, RxTxStats), - VMSTATE_UINT64(rx_bcast, RxTxStats), - VMSTATE_UINT64(rx_mcast, RxTxStats), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_xgmac = { - .name = "xgmac", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(stats, XgmacState, 0, vmstate_rxtx_stats, RxTxStats), - VMSTATE_UINT32_ARRAY(regs, XgmacState, R_MAX), - VMSTATE_END_OF_LIST() - } -}; - -static void xgmac_read_desc(XgmacState *s, struct desc *d, int rx) -{ - uint32_t addr = rx ? s->regs[DMA_CUR_RX_DESC_ADDR] : - s->regs[DMA_CUR_TX_DESC_ADDR]; - cpu_physical_memory_read(addr, d, sizeof(*d)); -} - -static void xgmac_write_desc(XgmacState *s, struct desc *d, int rx) -{ - int reg = rx ? DMA_CUR_RX_DESC_ADDR : DMA_CUR_TX_DESC_ADDR; - uint32_t addr = s->regs[reg]; - - if (!rx && (d->ctl_stat & 0x00200000)) { - s->regs[reg] = s->regs[DMA_TX_BASE_ADDR]; - } else if (rx && (d->buffer1_size & 0x8000)) { - s->regs[reg] = s->regs[DMA_RCV_BASE_ADDR]; - } else { - s->regs[reg] += sizeof(*d); - } - cpu_physical_memory_write(addr, d, sizeof(*d)); -} - -static void xgmac_enet_send(XgmacState *s) -{ - struct desc bd; - int frame_size; - int len; - uint8_t frame[8192]; - uint8_t *ptr; - - ptr = frame; - frame_size = 0; - while (1) { - xgmac_read_desc(s, &bd, 0); - if ((bd.ctl_stat & 0x80000000) == 0) { - /* Run out of descriptors to transmit. */ - break; - } - len = (bd.buffer1_size & 0xfff) + (bd.buffer2_size & 0xfff); - - if ((bd.buffer1_size & 0xfff) > 2048) { - DEBUGF_BRK("qemu:%s:ERROR...ERROR...ERROR... -- " - "xgmac buffer 1 len on send > 2048 (0x%x)\n", - __func__, bd.buffer1_size & 0xfff); - } - if ((bd.buffer2_size & 0xfff) != 0) { - DEBUGF_BRK("qemu:%s:ERROR...ERROR...ERROR... -- " - "xgmac buffer 2 len on send != 0 (0x%x)\n", - __func__, bd.buffer2_size & 0xfff); - } - if (len >= sizeof(frame)) { - DEBUGF_BRK("qemu:%s: buffer overflow %d read into %zu " - "buffer\n" , __func__, len, sizeof(frame)); - DEBUGF_BRK("qemu:%s: buffer1.size=%d; buffer2.size=%d\n", - __func__, bd.buffer1_size, bd.buffer2_size); - } - - cpu_physical_memory_read(bd.buffer1_addr, ptr, len); - ptr += len; - frame_size += len; - if (bd.ctl_stat & 0x20000000) { - /* Last buffer in frame. */ - qemu_send_packet(qemu_get_queue(s->nic), frame, len); - ptr = frame; - frame_size = 0; - s->regs[DMA_STATUS] |= DMA_STATUS_TI | DMA_STATUS_NIS; - } - bd.ctl_stat &= ~0x80000000; - /* Write back the modified descriptor. */ - xgmac_write_desc(s, &bd, 0); - } -} - -static void enet_update_irq(XgmacState *s) -{ - int stat = s->regs[DMA_STATUS] & s->regs[DMA_INTR_ENA]; - qemu_set_irq(s->sbd_irq, !!stat); -} - -static uint64_t enet_read(void *opaque, hwaddr addr, unsigned size) -{ - XgmacState *s = opaque; - uint64_t r = 0; - addr >>= 2; - - switch (addr) { - case XGMAC_VERSION: - r = 0x1012; - break; - default: - if (addr < ARRAY_SIZE(s->regs)) { - r = s->regs[addr]; - } - break; - } - return r; -} - -static void enet_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - XgmacState *s = opaque; - - addr >>= 2; - switch (addr) { - case DMA_BUS_MODE: - s->regs[DMA_BUS_MODE] = value & ~0x1; - break; - case DMA_XMT_POLL_DEMAND: - xgmac_enet_send(s); - break; - case DMA_STATUS: - s->regs[DMA_STATUS] = s->regs[DMA_STATUS] & ~value; - break; - case DMA_RCV_BASE_ADDR: - s->regs[DMA_RCV_BASE_ADDR] = s->regs[DMA_CUR_RX_DESC_ADDR] = value; - break; - case DMA_TX_BASE_ADDR: - s->regs[DMA_TX_BASE_ADDR] = s->regs[DMA_CUR_TX_DESC_ADDR] = value; - break; - default: - if (addr < ARRAY_SIZE(s->regs)) { - s->regs[addr] = value; - } - break; - } - enet_update_irq(s); -} - -static const MemoryRegionOps enet_mem_ops = { - .read = enet_read, - .write = enet_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static int eth_can_rx(XgmacState *s) -{ - /* RX enabled? */ - return s->regs[DMA_CONTROL] & DMA_CONTROL_SR; -} - -static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) -{ - XgmacState *s = qemu_get_nic_opaque(nc); - static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, - 0xff, 0xff, 0xff}; - int unicast, broadcast, multicast; - struct desc bd; - ssize_t ret; - - if (!eth_can_rx(s)) { - return -1; - } - unicast = ~buf[0] & 0x1; - broadcast = memcmp(buf, sa_bcast, 6) == 0; - multicast = !unicast && !broadcast; - if (size < 12) { - s->regs[DMA_STATUS] |= DMA_STATUS_RI | DMA_STATUS_NIS; - ret = -1; - goto out; - } - - xgmac_read_desc(s, &bd, 1); - if ((bd.ctl_stat & 0x80000000) == 0) { - s->regs[DMA_STATUS] |= DMA_STATUS_RU | DMA_STATUS_AIS; - ret = size; - goto out; - } - - cpu_physical_memory_write(bd.buffer1_addr, buf, size); - - /* Add in the 4 bytes for crc (the real hw returns length incl crc) */ - size += 4; - bd.ctl_stat = (size << 16) | 0x300; - xgmac_write_desc(s, &bd, 1); - - s->stats.rx_bytes += size; - s->stats.rx++; - if (multicast) { - s->stats.rx_mcast++; - } else if (broadcast) { - s->stats.rx_bcast++; - } - - s->regs[DMA_STATUS] |= DMA_STATUS_RI | DMA_STATUS_NIS; - ret = size; - -out: - enet_update_irq(s); - return ret; -} - -static NetClientInfo net_xgmac_enet_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = eth_rx, -}; - -static int xgmac_enet_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - XgmacState *s = XGMAC(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &enet_mem_ops, s, - "xgmac", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->sbd_irq); - sysbus_init_irq(sbd, &s->pmt_irq); - sysbus_init_irq(sbd, &s->mci_irq); - - qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->nic = qemu_new_nic(&net_xgmac_enet_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - s->regs[XGMAC_ADDR_HIGH(0)] = (s->conf.macaddr.a[5] << 8) | - s->conf.macaddr.a[4]; - s->regs[XGMAC_ADDR_LOW(0)] = (s->conf.macaddr.a[3] << 24) | - (s->conf.macaddr.a[2] << 16) | - (s->conf.macaddr.a[1] << 8) | - s->conf.macaddr.a[0]; - - return 0; -} - -static Property xgmac_properties[] = { - DEFINE_NIC_PROPERTIES(XgmacState, conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void xgmac_enet_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - sbc->init = xgmac_enet_init; - dc->vmsd = &vmstate_xgmac; - dc->props = xgmac_properties; -} - -static const TypeInfo xgmac_enet_info = { - .name = TYPE_XGMAC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(XgmacState), - .class_init = xgmac_enet_class_init, -}; - -static void xgmac_enet_register_types(void) -{ - type_register_static(&xgmac_enet_info); -} - -type_init(xgmac_enet_register_types) diff --git a/qemu/hw/net/xilinx_axienet.c b/qemu/hw/net/xilinx_axienet.c deleted file mode 100644 index de23ab5dc..000000000 --- a/qemu/hw/net/xilinx_axienet.c +++ /dev/null @@ -1,1084 +0,0 @@ -/* - * QEMU model of Xilinx AXI-Ethernet. - * - * Copyright (c) 2011 Edgar E. Iglesias. - * - * 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/sysbus.h" -#include "qapi/error.h" -#include "qemu/log.h" -#include "net/net.h" -#include "net/checksum.h" - -#include "hw/stream.h" - -#define DPHY(x) - -#define TYPE_XILINX_AXI_ENET "xlnx.axi-ethernet" -#define TYPE_XILINX_AXI_ENET_DATA_STREAM "xilinx-axienet-data-stream" -#define TYPE_XILINX_AXI_ENET_CONTROL_STREAM "xilinx-axienet-control-stream" - -#define XILINX_AXI_ENET(obj) \ - OBJECT_CHECK(XilinxAXIEnet, (obj), TYPE_XILINX_AXI_ENET) - -#define XILINX_AXI_ENET_DATA_STREAM(obj) \ - OBJECT_CHECK(XilinxAXIEnetStreamSlave, (obj),\ - TYPE_XILINX_AXI_ENET_DATA_STREAM) - -#define XILINX_AXI_ENET_CONTROL_STREAM(obj) \ - OBJECT_CHECK(XilinxAXIEnetStreamSlave, (obj),\ - TYPE_XILINX_AXI_ENET_CONTROL_STREAM) - -/* Advertisement control register. */ -#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ -#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ -#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ -#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ - -#define CONTROL_PAYLOAD_WORDS 5 -#define CONTROL_PAYLOAD_SIZE (CONTROL_PAYLOAD_WORDS * (sizeof(uint32_t))) - -struct PHY { - uint32_t regs[32]; - - int link; - - unsigned int (*read)(struct PHY *phy, unsigned int req); - void (*write)(struct PHY *phy, unsigned int req, - unsigned int data); -}; - -static unsigned int tdk_read(struct PHY *phy, unsigned int req) -{ - int regnum; - unsigned r = 0; - - regnum = req & 0x1f; - - switch (regnum) { - case 1: - if (!phy->link) { - break; - } - /* MR1. */ - /* Speeds and modes. */ - r |= (1 << 13) | (1 << 14); - r |= (1 << 11) | (1 << 12); - r |= (1 << 5); /* Autoneg complete. */ - r |= (1 << 3); /* Autoneg able. */ - r |= (1 << 2); /* link. */ - r |= (1 << 1); /* link. */ - break; - case 5: - /* Link partner ability. - We are kind; always agree with whatever best mode - the guest advertises. */ - r = 1 << 14; /* Success. */ - /* Copy advertised modes. */ - r |= phy->regs[4] & (15 << 5); - /* Autoneg support. */ - r |= 1; - break; - case 17: - /* Marvell PHY on many xilinx boards. */ - r = 0x8000; /* 1000Mb */ - break; - case 18: - { - /* Diagnostics reg. */ - int duplex = 0; - int speed_100 = 0; - - if (!phy->link) { - break; - } - - /* Are we advertising 100 half or 100 duplex ? */ - speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF); - speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL); - - /* Are we advertising 10 duplex or 100 duplex ? */ - duplex = !!(phy->regs[4] & ADVERTISE_100FULL); - duplex |= !!(phy->regs[4] & ADVERTISE_10FULL); - r = (speed_100 << 10) | (duplex << 11); - } - break; - - default: - r = phy->regs[regnum]; - break; - } - DPHY(qemu_log("\n%s %x = reg[%d]\n", __func__, r, regnum)); - return r; -} - -static void -tdk_write(struct PHY *phy, unsigned int req, unsigned int data) -{ - int regnum; - - regnum = req & 0x1f; - DPHY(qemu_log("%s reg[%d] = %x\n", __func__, regnum, data)); - switch (regnum) { - default: - phy->regs[regnum] = data; - break; - } - - /* Unconditionally clear regs[BMCR][BMCR_RESET] */ - phy->regs[0] &= ~0x8000; -} - -static void -tdk_init(struct PHY *phy) -{ - phy->regs[0] = 0x3100; - /* PHY Id. */ - phy->regs[2] = 0x0300; - phy->regs[3] = 0xe400; - /* Autonegotiation advertisement reg. */ - phy->regs[4] = 0x01E1; - phy->link = 1; - - phy->read = tdk_read; - phy->write = tdk_write; -} - -struct MDIOBus { - /* bus. */ - int mdc; - int mdio; - - /* decoder. */ - enum { - PREAMBLE, - SOF, - OPC, - ADDR, - REQ, - TURNAROUND, - DATA - } state; - unsigned int drive; - - unsigned int cnt; - unsigned int addr; - unsigned int opc; - unsigned int req; - unsigned int data; - - struct PHY *devs[32]; -}; - -static void -mdio_attach(struct MDIOBus *bus, struct PHY *phy, unsigned int addr) -{ - bus->devs[addr & 0x1f] = phy; -} - -#ifdef USE_THIS_DEAD_CODE -static void -mdio_detach(struct MDIOBus *bus, struct PHY *phy, unsigned int addr) -{ - bus->devs[addr & 0x1f] = NULL; -} -#endif - -static uint16_t mdio_read_req(struct MDIOBus *bus, unsigned int addr, - unsigned int reg) -{ - struct PHY *phy; - uint16_t data; - - phy = bus->devs[addr]; - if (phy && phy->read) { - data = phy->read(phy, reg); - } else { - data = 0xffff; - } - DPHY(qemu_log("%s addr=%d reg=%d data=%x\n", __func__, addr, reg, data)); - return data; -} - -static void mdio_write_req(struct MDIOBus *bus, unsigned int addr, - unsigned int reg, uint16_t data) -{ - struct PHY *phy; - - DPHY(qemu_log("%s addr=%d reg=%d data=%x\n", __func__, addr, reg, data)); - phy = bus->devs[addr]; - if (phy && phy->write) { - phy->write(phy, reg, data); - } -} - -#define DENET(x) - -#define R_RAF (0x000 / 4) -enum { - RAF_MCAST_REJ = (1 << 1), - RAF_BCAST_REJ = (1 << 2), - RAF_EMCF_EN = (1 << 12), - RAF_NEWFUNC_EN = (1 << 11) -}; - -#define R_IS (0x00C / 4) -enum { - IS_HARD_ACCESS_COMPLETE = 1, - IS_AUTONEG = (1 << 1), - IS_RX_COMPLETE = (1 << 2), - IS_RX_REJECT = (1 << 3), - IS_TX_COMPLETE = (1 << 5), - IS_RX_DCM_LOCK = (1 << 6), - IS_MGM_RDY = (1 << 7), - IS_PHY_RST_DONE = (1 << 8), -}; - -#define R_IP (0x010 / 4) -#define R_IE (0x014 / 4) -#define R_UAWL (0x020 / 4) -#define R_UAWU (0x024 / 4) -#define R_PPST (0x030 / 4) -enum { - PPST_LINKSTATUS = (1 << 0), - PPST_PHY_LINKSTATUS = (1 << 7), -}; - -#define R_STATS_RX_BYTESL (0x200 / 4) -#define R_STATS_RX_BYTESH (0x204 / 4) -#define R_STATS_TX_BYTESL (0x208 / 4) -#define R_STATS_TX_BYTESH (0x20C / 4) -#define R_STATS_RXL (0x290 / 4) -#define R_STATS_RXH (0x294 / 4) -#define R_STATS_RX_BCASTL (0x2a0 / 4) -#define R_STATS_RX_BCASTH (0x2a4 / 4) -#define R_STATS_RX_MCASTL (0x2a8 / 4) -#define R_STATS_RX_MCASTH (0x2ac / 4) - -#define R_RCW0 (0x400 / 4) -#define R_RCW1 (0x404 / 4) -enum { - RCW1_VLAN = (1 << 27), - RCW1_RX = (1 << 28), - RCW1_FCS = (1 << 29), - RCW1_JUM = (1 << 30), - RCW1_RST = (1 << 31), -}; - -#define R_TC (0x408 / 4) -enum { - TC_VLAN = (1 << 27), - TC_TX = (1 << 28), - TC_FCS = (1 << 29), - TC_JUM = (1 << 30), - TC_RST = (1 << 31), -}; - -#define R_EMMC (0x410 / 4) -enum { - EMMC_LINKSPEED_10MB = (0 << 30), - EMMC_LINKSPEED_100MB = (1 << 30), - EMMC_LINKSPEED_1000MB = (2 << 30), -}; - -#define R_PHYC (0x414 / 4) - -#define R_MC (0x500 / 4) -#define MC_EN (1 << 6) - -#define R_MCR (0x504 / 4) -#define R_MWD (0x508 / 4) -#define R_MRD (0x50c / 4) -#define R_MIS (0x600 / 4) -#define R_MIP (0x620 / 4) -#define R_MIE (0x640 / 4) -#define R_MIC (0x640 / 4) - -#define R_UAW0 (0x700 / 4) -#define R_UAW1 (0x704 / 4) -#define R_FMI (0x708 / 4) -#define R_AF0 (0x710 / 4) -#define R_AF1 (0x714 / 4) -#define R_MAX (0x34 / 4) - -/* Indirect registers. */ -struct TEMAC { - struct MDIOBus mdio_bus; - struct PHY phy; - - void *parent; -}; - -typedef struct XilinxAXIEnetStreamSlave XilinxAXIEnetStreamSlave; -typedef struct XilinxAXIEnet XilinxAXIEnet; - -struct XilinxAXIEnetStreamSlave { - Object parent; - - struct XilinxAXIEnet *enet; -} ; - -struct XilinxAXIEnet { - SysBusDevice busdev; - MemoryRegion iomem; - qemu_irq irq; - StreamSlave *tx_data_dev; - StreamSlave *tx_control_dev; - XilinxAXIEnetStreamSlave rx_data_dev; - XilinxAXIEnetStreamSlave rx_control_dev; - NICState *nic; - NICConf conf; - - - uint32_t c_rxmem; - uint32_t c_txmem; - uint32_t c_phyaddr; - - struct TEMAC TEMAC; - - /* MII regs. */ - union { - uint32_t regs[4]; - struct { - uint32_t mc; - uint32_t mcr; - uint32_t mwd; - uint32_t mrd; - }; - } mii; - - struct { - uint64_t rx_bytes; - uint64_t tx_bytes; - - uint64_t rx; - uint64_t rx_bcast; - uint64_t rx_mcast; - } stats; - - /* Receive configuration words. */ - uint32_t rcw[2]; - /* Transmit config. */ - uint32_t tc; - uint32_t emmc; - uint32_t phyc; - - /* Unicast Address Word. */ - uint32_t uaw[2]; - /* Unicast address filter used with extended mcast. */ - uint32_t ext_uaw[2]; - uint32_t fmi; - - uint32_t regs[R_MAX]; - - /* Multicast filter addrs. */ - uint32_t maddr[4][2]; - /* 32K x 1 lookup filter. */ - uint32_t ext_mtable[1024]; - - uint32_t hdr[CONTROL_PAYLOAD_WORDS]; - - uint8_t *rxmem; - uint32_t rxsize; - uint32_t rxpos; - - uint8_t rxapp[CONTROL_PAYLOAD_SIZE]; - uint32_t rxappsize; - - /* Whether axienet_eth_rx_notify should flush incoming queue. */ - bool need_flush; -}; - -static void axienet_rx_reset(XilinxAXIEnet *s) -{ - s->rcw[1] = RCW1_JUM | RCW1_FCS | RCW1_RX | RCW1_VLAN; -} - -static void axienet_tx_reset(XilinxAXIEnet *s) -{ - s->tc = TC_JUM | TC_TX | TC_VLAN; -} - -static inline int axienet_rx_resetting(XilinxAXIEnet *s) -{ - return s->rcw[1] & RCW1_RST; -} - -static inline int axienet_rx_enabled(XilinxAXIEnet *s) -{ - return s->rcw[1] & RCW1_RX; -} - -static inline int axienet_extmcf_enabled(XilinxAXIEnet *s) -{ - return !!(s->regs[R_RAF] & RAF_EMCF_EN); -} - -static inline int axienet_newfunc_enabled(XilinxAXIEnet *s) -{ - return !!(s->regs[R_RAF] & RAF_NEWFUNC_EN); -} - -static void xilinx_axienet_reset(DeviceState *d) -{ - XilinxAXIEnet *s = XILINX_AXI_ENET(d); - - axienet_rx_reset(s); - axienet_tx_reset(s); - - s->regs[R_PPST] = PPST_LINKSTATUS | PPST_PHY_LINKSTATUS; - s->regs[R_IS] = IS_AUTONEG | IS_RX_DCM_LOCK | IS_MGM_RDY | IS_PHY_RST_DONE; - - s->emmc = EMMC_LINKSPEED_100MB; -} - -static void enet_update_irq(XilinxAXIEnet *s) -{ - s->regs[R_IP] = s->regs[R_IS] & s->regs[R_IE]; - qemu_set_irq(s->irq, !!s->regs[R_IP]); -} - -static uint64_t enet_read(void *opaque, hwaddr addr, unsigned size) -{ - XilinxAXIEnet *s = opaque; - uint32_t r = 0; - addr >>= 2; - - switch (addr) { - case R_RCW0: - case R_RCW1: - r = s->rcw[addr & 1]; - break; - - case R_TC: - r = s->tc; - break; - - case R_EMMC: - r = s->emmc; - break; - - case R_PHYC: - r = s->phyc; - break; - - case R_MCR: - r = s->mii.regs[addr & 3] | (1 << 7); /* Always ready. */ - break; - - case R_STATS_RX_BYTESL: - case R_STATS_RX_BYTESH: - r = s->stats.rx_bytes >> (32 * (addr & 1)); - break; - - case R_STATS_TX_BYTESL: - case R_STATS_TX_BYTESH: - r = s->stats.tx_bytes >> (32 * (addr & 1)); - break; - - case R_STATS_RXL: - case R_STATS_RXH: - r = s->stats.rx >> (32 * (addr & 1)); - break; - case R_STATS_RX_BCASTL: - case R_STATS_RX_BCASTH: - r = s->stats.rx_bcast >> (32 * (addr & 1)); - break; - case R_STATS_RX_MCASTL: - case R_STATS_RX_MCASTH: - r = s->stats.rx_mcast >> (32 * (addr & 1)); - break; - - case R_MC: - case R_MWD: - case R_MRD: - r = s->mii.regs[addr & 3]; - break; - - case R_UAW0: - case R_UAW1: - r = s->uaw[addr & 1]; - break; - - case R_UAWU: - case R_UAWL: - r = s->ext_uaw[addr & 1]; - break; - - case R_FMI: - r = s->fmi; - break; - - case R_AF0: - case R_AF1: - r = s->maddr[s->fmi & 3][addr & 1]; - break; - - case 0x8000 ... 0x83ff: - r = s->ext_mtable[addr - 0x8000]; - break; - - default: - if (addr < ARRAY_SIZE(s->regs)) { - r = s->regs[addr]; - } - DENET(qemu_log("%s addr=" TARGET_FMT_plx " v=%x\n", - __func__, addr * 4, r)); - break; - } - return r; -} - -static void enet_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - XilinxAXIEnet *s = opaque; - struct TEMAC *t = &s->TEMAC; - - addr >>= 2; - switch (addr) { - case R_RCW0: - case R_RCW1: - s->rcw[addr & 1] = value; - if ((addr & 1) && value & RCW1_RST) { - axienet_rx_reset(s); - } else { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } - break; - - case R_TC: - s->tc = value; - if (value & TC_RST) { - axienet_tx_reset(s); - } - break; - - case R_EMMC: - s->emmc = value; - break; - - case R_PHYC: - s->phyc = value; - break; - - case R_MC: - value &= ((1 << 7) - 1); - - /* Enable the MII. */ - if (value & MC_EN) { - unsigned int miiclkdiv = value & ((1 << 6) - 1); - if (!miiclkdiv) { - qemu_log("AXIENET: MDIO enabled but MDIOCLK is zero!\n"); - } - } - s->mii.mc = value; - break; - - case R_MCR: { - unsigned int phyaddr = (value >> 24) & 0x1f; - unsigned int regaddr = (value >> 16) & 0x1f; - unsigned int op = (value >> 14) & 3; - unsigned int initiate = (value >> 11) & 1; - - if (initiate) { - if (op == 1) { - mdio_write_req(&t->mdio_bus, phyaddr, regaddr, s->mii.mwd); - } else if (op == 2) { - s->mii.mrd = mdio_read_req(&t->mdio_bus, phyaddr, regaddr); - } else { - qemu_log("AXIENET: invalid MDIOBus OP=%d\n", op); - } - } - s->mii.mcr = value; - break; - } - - case R_MWD: - case R_MRD: - s->mii.regs[addr & 3] = value; - break; - - - case R_UAW0: - case R_UAW1: - s->uaw[addr & 1] = value; - break; - - case R_UAWL: - case R_UAWU: - s->ext_uaw[addr & 1] = value; - break; - - case R_FMI: - s->fmi = value; - break; - - case R_AF0: - case R_AF1: - s->maddr[s->fmi & 3][addr & 1] = value; - break; - - case R_IS: - s->regs[addr] &= ~value; - break; - - case 0x8000 ... 0x83ff: - s->ext_mtable[addr - 0x8000] = value; - break; - - default: - DENET(qemu_log("%s addr=" TARGET_FMT_plx " v=%x\n", - __func__, addr * 4, (unsigned)value)); - if (addr < ARRAY_SIZE(s->regs)) { - s->regs[addr] = value; - } - break; - } - enet_update_irq(s); -} - -static const MemoryRegionOps enet_ops = { - .read = enet_read, - .write = enet_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static int eth_can_rx(XilinxAXIEnet *s) -{ - /* RX enabled? */ - return !s->rxsize && !axienet_rx_resetting(s) && axienet_rx_enabled(s); -} - -static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1) -{ - int match = 1; - - if (memcmp(buf, &f0, 4)) { - match = 0; - } - - if (buf[4] != (f1 & 0xff) || buf[5] != ((f1 >> 8) & 0xff)) { - match = 0; - } - - return match; -} - -static void axienet_eth_rx_notify(void *opaque) -{ - XilinxAXIEnet *s = XILINX_AXI_ENET(opaque); - - while (s->rxappsize && stream_can_push(s->tx_control_dev, - axienet_eth_rx_notify, s)) { - size_t ret = stream_push(s->tx_control_dev, - (void *)s->rxapp + CONTROL_PAYLOAD_SIZE - - s->rxappsize, s->rxappsize); - s->rxappsize -= ret; - } - - while (s->rxsize && stream_can_push(s->tx_data_dev, - axienet_eth_rx_notify, s)) { - size_t ret = stream_push(s->tx_data_dev, (void *)s->rxmem + s->rxpos, - s->rxsize); - s->rxsize -= ret; - s->rxpos += ret; - if (!s->rxsize) { - s->regs[R_IS] |= IS_RX_COMPLETE; - if (s->need_flush) { - s->need_flush = false; - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } - } - } - enet_update_irq(s); -} - -static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) -{ - XilinxAXIEnet *s = qemu_get_nic_opaque(nc); - static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, - 0xff, 0xff, 0xff}; - static const unsigned char sa_ipmcast[3] = {0x01, 0x00, 0x52}; - uint32_t app[CONTROL_PAYLOAD_WORDS] = {0}; - int promisc = s->fmi & (1 << 31); - int unicast, broadcast, multicast, ip_multicast = 0; - uint32_t csum32; - uint16_t csum16; - int i; - - DENET(qemu_log("%s: %zd bytes\n", __func__, size)); - - if (!eth_can_rx(s)) { - s->need_flush = true; - return 0; - } - - unicast = ~buf[0] & 0x1; - broadcast = memcmp(buf, sa_bcast, 6) == 0; - multicast = !unicast && !broadcast; - if (multicast && (memcmp(sa_ipmcast, buf, sizeof sa_ipmcast) == 0)) { - ip_multicast = 1; - } - - /* Jumbo or vlan sizes ? */ - if (!(s->rcw[1] & RCW1_JUM)) { - if (size > 1518 && size <= 1522 && !(s->rcw[1] & RCW1_VLAN)) { - return size; - } - } - - /* Basic Address filters. If you want to use the extended filters - you'll generally have to place the ethernet mac into promiscuous mode - to avoid the basic filtering from dropping most frames. */ - if (!promisc) { - if (unicast) { - if (!enet_match_addr(buf, s->uaw[0], s->uaw[1])) { - return size; - } - } else { - if (broadcast) { - /* Broadcast. */ - if (s->regs[R_RAF] & RAF_BCAST_REJ) { - return size; - } - } else { - int drop = 1; - - /* Multicast. */ - if (s->regs[R_RAF] & RAF_MCAST_REJ) { - return size; - } - - for (i = 0; i < 4; i++) { - if (enet_match_addr(buf, s->maddr[i][0], s->maddr[i][1])) { - drop = 0; - break; - } - } - - if (drop) { - return size; - } - } - } - } - - /* Extended mcast filtering enabled? */ - if (axienet_newfunc_enabled(s) && axienet_extmcf_enabled(s)) { - if (unicast) { - if (!enet_match_addr(buf, s->ext_uaw[0], s->ext_uaw[1])) { - return size; - } - } else { - if (broadcast) { - /* Broadcast. ??? */ - if (s->regs[R_RAF] & RAF_BCAST_REJ) { - return size; - } - } else { - int idx, bit; - - /* Multicast. */ - if (!memcmp(buf, sa_ipmcast, 3)) { - return size; - } - - idx = (buf[4] & 0x7f) << 8; - idx |= buf[5]; - - bit = 1 << (idx & 0x1f); - idx >>= 5; - - if (!(s->ext_mtable[idx] & bit)) { - return size; - } - } - } - } - - if (size < 12) { - s->regs[R_IS] |= IS_RX_REJECT; - enet_update_irq(s); - return -1; - } - - if (size > (s->c_rxmem - 4)) { - size = s->c_rxmem - 4; - } - - memcpy(s->rxmem, buf, size); - memset(s->rxmem + size, 0, 4); /* Clear the FCS. */ - - if (s->rcw[1] & RCW1_FCS) { - size += 4; /* fcs is inband. */ - } - - app[0] = 5 << 28; - csum32 = net_checksum_add(size - 14, (uint8_t *)s->rxmem + 14); - /* Fold it once. */ - csum32 = (csum32 & 0xffff) + (csum32 >> 16); - /* And twice to get rid of possible carries. */ - csum16 = (csum32 & 0xffff) + (csum32 >> 16); - app[3] = csum16; - app[4] = size & 0xffff; - - s->stats.rx_bytes += size; - s->stats.rx++; - if (multicast) { - s->stats.rx_mcast++; - app[2] |= 1 | (ip_multicast << 1); - } else if (broadcast) { - s->stats.rx_bcast++; - app[2] |= 1 << 3; - } - - /* Good frame. */ - app[2] |= 1 << 6; - - s->rxsize = size; - s->rxpos = 0; - for (i = 0; i < ARRAY_SIZE(app); ++i) { - app[i] = cpu_to_le32(app[i]); - } - s->rxappsize = CONTROL_PAYLOAD_SIZE; - memcpy(s->rxapp, app, s->rxappsize); - axienet_eth_rx_notify(s); - - enet_update_irq(s); - return size; -} - -static size_t -xilinx_axienet_control_stream_push(StreamSlave *obj, uint8_t *buf, size_t len) -{ - int i; - XilinxAXIEnetStreamSlave *cs = XILINX_AXI_ENET_CONTROL_STREAM(obj); - XilinxAXIEnet *s = cs->enet; - - if (len != CONTROL_PAYLOAD_SIZE) { - hw_error("AXI Enet requires %d byte control stream payload\n", - (int)CONTROL_PAYLOAD_SIZE); - } - - memcpy(s->hdr, buf, len); - - for (i = 0; i < ARRAY_SIZE(s->hdr); ++i) { - s->hdr[i] = le32_to_cpu(s->hdr[i]); - } - return len; -} - -static size_t -xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size) -{ - XilinxAXIEnetStreamSlave *ds = XILINX_AXI_ENET_DATA_STREAM(obj); - XilinxAXIEnet *s = ds->enet; - - /* TX enable ? */ - if (!(s->tc & TC_TX)) { - return size; - } - - /* Jumbo or vlan sizes ? */ - if (!(s->tc & TC_JUM)) { - if (size > 1518 && size <= 1522 && !(s->tc & TC_VLAN)) { - return size; - } - } - - if (s->hdr[0] & 1) { - unsigned int start_off = s->hdr[1] >> 16; - unsigned int write_off = s->hdr[1] & 0xffff; - uint32_t tmp_csum; - uint16_t csum; - - tmp_csum = net_checksum_add(size - start_off, - (uint8_t *)buf + start_off); - /* Accumulate the seed. */ - tmp_csum += s->hdr[2] & 0xffff; - - /* Fold the 32bit partial checksum. */ - csum = net_checksum_finish(tmp_csum); - - /* Writeback. */ - buf[write_off] = csum >> 8; - buf[write_off + 1] = csum & 0xff; - } - - qemu_send_packet(qemu_get_queue(s->nic), buf, size); - - s->stats.tx_bytes += size; - s->regs[R_IS] |= IS_TX_COMPLETE; - enet_update_irq(s); - - return size; -} - -static NetClientInfo net_xilinx_enet_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = eth_rx, -}; - -static void xilinx_enet_realize(DeviceState *dev, Error **errp) -{ - XilinxAXIEnet *s = XILINX_AXI_ENET(dev); - XilinxAXIEnetStreamSlave *ds = XILINX_AXI_ENET_DATA_STREAM(&s->rx_data_dev); - XilinxAXIEnetStreamSlave *cs = XILINX_AXI_ENET_CONTROL_STREAM( - &s->rx_control_dev); - Error *local_err = NULL; - - object_property_add_link(OBJECT(ds), "enet", "xlnx.axi-ethernet", - (Object **) &ds->enet, - object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &local_err); - object_property_add_link(OBJECT(cs), "enet", "xlnx.axi-ethernet", - (Object **) &cs->enet, - object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &local_err); - if (local_err) { - goto xilinx_enet_realize_fail; - } - object_property_set_link(OBJECT(ds), OBJECT(s), "enet", &local_err); - object_property_set_link(OBJECT(cs), OBJECT(s), "enet", &local_err); - if (local_err) { - goto xilinx_enet_realize_fail; - } - - qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->nic = qemu_new_nic(&net_xilinx_enet_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - tdk_init(&s->TEMAC.phy); - mdio_attach(&s->TEMAC.mdio_bus, &s->TEMAC.phy, s->c_phyaddr); - - s->TEMAC.parent = s; - - s->rxmem = g_malloc(s->c_rxmem); - return; - -xilinx_enet_realize_fail: - if (!*errp) { - *errp = local_err; - } -} - -static void xilinx_enet_init(Object *obj) -{ - XilinxAXIEnet *s = XILINX_AXI_ENET(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE, - (Object **) &s->tx_data_dev, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); - object_property_add_link(obj, "axistream-control-connected", - TYPE_STREAM_SLAVE, - (Object **) &s->tx_control_dev, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, - &error_abort); - - object_initialize(&s->rx_data_dev, sizeof(s->rx_data_dev), - TYPE_XILINX_AXI_ENET_DATA_STREAM); - object_initialize(&s->rx_control_dev, sizeof(s->rx_control_dev), - TYPE_XILINX_AXI_ENET_CONTROL_STREAM); - object_property_add_child(OBJECT(s), "axistream-connected-target", - (Object *)&s->rx_data_dev, &error_abort); - object_property_add_child(OBJECT(s), "axistream-control-connected-target", - (Object *)&s->rx_control_dev, &error_abort); - - sysbus_init_irq(sbd, &s->irq); - - memory_region_init_io(&s->iomem, OBJECT(s), &enet_ops, s, "enet", 0x40000); - sysbus_init_mmio(sbd, &s->iomem); -} - -static Property xilinx_enet_properties[] = { - DEFINE_PROP_UINT32("phyaddr", XilinxAXIEnet, c_phyaddr, 7), - DEFINE_PROP_UINT32("rxmem", XilinxAXIEnet, c_rxmem, 0x1000), - DEFINE_PROP_UINT32("txmem", XilinxAXIEnet, c_txmem, 0x1000), - DEFINE_NIC_PROPERTIES(XilinxAXIEnet, conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void xilinx_enet_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = xilinx_enet_realize; - dc->props = xilinx_enet_properties; - dc->reset = xilinx_axienet_reset; -} - -static void xilinx_enet_stream_class_init(ObjectClass *klass, void *data) -{ - StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass); - - ssc->push = data; -} - -static const TypeInfo xilinx_enet_info = { - .name = TYPE_XILINX_AXI_ENET, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(XilinxAXIEnet), - .class_init = xilinx_enet_class_init, - .instance_init = xilinx_enet_init, -}; - -static const TypeInfo xilinx_enet_data_stream_info = { - .name = TYPE_XILINX_AXI_ENET_DATA_STREAM, - .parent = TYPE_OBJECT, - .instance_size = sizeof(struct XilinxAXIEnetStreamSlave), - .class_init = xilinx_enet_stream_class_init, - .class_data = xilinx_axienet_data_stream_push, - .interfaces = (InterfaceInfo[]) { - { TYPE_STREAM_SLAVE }, - { } - } -}; - -static const TypeInfo xilinx_enet_control_stream_info = { - .name = TYPE_XILINX_AXI_ENET_CONTROL_STREAM, - .parent = TYPE_OBJECT, - .instance_size = sizeof(struct XilinxAXIEnetStreamSlave), - .class_init = xilinx_enet_stream_class_init, - .class_data = xilinx_axienet_control_stream_push, - .interfaces = (InterfaceInfo[]) { - { TYPE_STREAM_SLAVE }, - { } - } -}; - -static void xilinx_enet_register_types(void) -{ - type_register_static(&xilinx_enet_info); - type_register_static(&xilinx_enet_data_stream_info); - type_register_static(&xilinx_enet_control_stream_info); -} - -type_init(xilinx_enet_register_types) diff --git a/qemu/hw/net/xilinx_ethlite.c b/qemu/hw/net/xilinx_ethlite.c deleted file mode 100644 index bc846e709..000000000 --- a/qemu/hw/net/xilinx_ethlite.c +++ /dev/null @@ -1,276 +0,0 @@ -/* - * QEMU model of the Xilinx Ethernet Lite MAC. - * - * Copyright (c) 2009 Edgar E. Iglesias. - * - * 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 "qemu-common.h" -#include "cpu.h" /* FIXME should not use tswap* */ -#include "hw/sysbus.h" -#include "hw/hw.h" -#include "net/net.h" - -#define D(x) -#define R_TX_BUF0 0 -#define R_TX_LEN0 (0x07f4 / 4) -#define R_TX_GIE0 (0x07f8 / 4) -#define R_TX_CTRL0 (0x07fc / 4) -#define R_TX_BUF1 (0x0800 / 4) -#define R_TX_LEN1 (0x0ff4 / 4) -#define R_TX_CTRL1 (0x0ffc / 4) - -#define R_RX_BUF0 (0x1000 / 4) -#define R_RX_CTRL0 (0x17fc / 4) -#define R_RX_BUF1 (0x1800 / 4) -#define R_RX_CTRL1 (0x1ffc / 4) -#define R_MAX (0x2000 / 4) - -#define GIE_GIE 0x80000000 - -#define CTRL_I 0x8 -#define CTRL_P 0x2 -#define CTRL_S 0x1 - -#define TYPE_XILINX_ETHLITE "xlnx.xps-ethernetlite" -#define XILINX_ETHLITE(obj) \ - OBJECT_CHECK(struct xlx_ethlite, (obj), TYPE_XILINX_ETHLITE) - -struct xlx_ethlite -{ - SysBusDevice parent_obj; - - MemoryRegion mmio; - qemu_irq irq; - NICState *nic; - NICConf conf; - - uint32_t c_tx_pingpong; - uint32_t c_rx_pingpong; - unsigned int txbuf; - unsigned int rxbuf; - - uint32_t regs[R_MAX]; -}; - -static inline void eth_pulse_irq(struct xlx_ethlite *s) -{ - /* Only the first gie reg is active. */ - if (s->regs[R_TX_GIE0] & GIE_GIE) { - qemu_irq_pulse(s->irq); - } -} - -static uint64_t -eth_read(void *opaque, hwaddr addr, unsigned int size) -{ - struct xlx_ethlite *s = opaque; - uint32_t r = 0; - - addr >>= 2; - - switch (addr) - { - case R_TX_GIE0: - case R_TX_LEN0: - case R_TX_LEN1: - case R_TX_CTRL1: - case R_TX_CTRL0: - case R_RX_CTRL1: - case R_RX_CTRL0: - r = s->regs[addr]; - D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr * 4, r)); - break; - - default: - r = tswap32(s->regs[addr]); - break; - } - return r; -} - -static void -eth_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - struct xlx_ethlite *s = opaque; - unsigned int base = 0; - uint32_t value = val64; - - addr >>= 2; - switch (addr) - { - case R_TX_CTRL0: - case R_TX_CTRL1: - if (addr == R_TX_CTRL1) - base = 0x800 / 4; - - D(qemu_log("%s addr=" TARGET_FMT_plx " val=%x\n", - __func__, addr * 4, value)); - if ((value & (CTRL_P | CTRL_S)) == CTRL_S) { - qemu_send_packet(qemu_get_queue(s->nic), - (void *) &s->regs[base], - s->regs[base + R_TX_LEN0]); - D(qemu_log("eth_tx %d\n", s->regs[base + R_TX_LEN0])); - if (s->regs[base + R_TX_CTRL0] & CTRL_I) - eth_pulse_irq(s); - } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) { - memcpy(&s->conf.macaddr.a[0], &s->regs[base], 6); - if (s->regs[base + R_TX_CTRL0] & CTRL_I) - eth_pulse_irq(s); - } - - /* We are fast and get ready pretty much immediately so - we actually never flip the S nor P bits to one. */ - s->regs[addr] = value & ~(CTRL_P | CTRL_S); - break; - - /* Keep these native. */ - case R_RX_CTRL0: - case R_RX_CTRL1: - if (!(value & CTRL_S)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } - /* fall through */ - case R_TX_LEN0: - case R_TX_LEN1: - case R_TX_GIE0: - D(qemu_log("%s addr=" TARGET_FMT_plx " val=%x\n", - __func__, addr * 4, value)); - s->regs[addr] = value; - break; - - default: - s->regs[addr] = tswap32(value); - break; - } -} - -static const MemoryRegionOps eth_ops = { - .read = eth_read, - .write = eth_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static int eth_can_rx(NetClientState *nc) -{ - struct xlx_ethlite *s = qemu_get_nic_opaque(nc); - unsigned int rxbase = s->rxbuf * (0x800 / 4); - - return !(s->regs[rxbase + R_RX_CTRL0] & CTRL_S); -} - -static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) -{ - struct xlx_ethlite *s = qemu_get_nic_opaque(nc); - unsigned int rxbase = s->rxbuf * (0x800 / 4); - - /* DA filter. */ - if (!(buf[0] & 0x80) && memcmp(&s->conf.macaddr.a[0], buf, 6)) - return size; - - if (s->regs[rxbase + R_RX_CTRL0] & CTRL_S) { - D(qemu_log("ethlite lost packet %x\n", s->regs[R_RX_CTRL0])); - return -1; - } - - D(qemu_log("%s %zd rxbase=%x\n", __func__, size, rxbase)); - memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size); - - s->regs[rxbase + R_RX_CTRL0] |= CTRL_S; - if (s->regs[R_RX_CTRL0] & CTRL_I) { - eth_pulse_irq(s); - } - - /* If c_rx_pingpong was set flip buffers. */ - s->rxbuf ^= s->c_rx_pingpong; - return size; -} - -static void xilinx_ethlite_reset(DeviceState *dev) -{ - struct xlx_ethlite *s = XILINX_ETHLITE(dev); - - s->rxbuf = 0; -} - -static NetClientInfo net_xilinx_ethlite_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .can_receive = eth_can_rx, - .receive = eth_rx, -}; - -static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) -{ - struct xlx_ethlite *s = XILINX_ETHLITE(dev); - - qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); -} - -static void xilinx_ethlite_init(Object *obj) -{ - struct xlx_ethlite *s = XILINX_ETHLITE(obj); - - sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); - - memory_region_init_io(&s->mmio, obj, ð_ops, s, - "xlnx.xps-ethernetlite", R_MAX * 4); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); -} - -static Property xilinx_ethlite_properties[] = { - DEFINE_PROP_UINT32("tx-ping-pong", struct xlx_ethlite, c_tx_pingpong, 1), - DEFINE_PROP_UINT32("rx-ping-pong", struct xlx_ethlite, c_rx_pingpong, 1), - DEFINE_NIC_PROPERTIES(struct xlx_ethlite, conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void xilinx_ethlite_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = xilinx_ethlite_realize; - dc->reset = xilinx_ethlite_reset; - dc->props = xilinx_ethlite_properties; -} - -static const TypeInfo xilinx_ethlite_info = { - .name = TYPE_XILINX_ETHLITE, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct xlx_ethlite), - .instance_init = xilinx_ethlite_init, - .class_init = xilinx_ethlite_class_init, -}; - -static void xilinx_ethlite_register_types(void) -{ - type_register_static(&xilinx_ethlite_info); -} - -type_init(xilinx_ethlite_register_types) diff --git a/qemu/hw/nvram/Makefile.objs b/qemu/hw/nvram/Makefile.objs deleted file mode 100644 index e9a66940e..000000000 --- a/qemu/hw/nvram/Makefile.objs +++ /dev/null @@ -1,5 +0,0 @@ -common-obj-$(CONFIG_DS1225Y) += ds1225y.o -common-obj-y += eeprom93xx.o -common-obj-y += fw_cfg.o -common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o -obj-$(CONFIG_PSERIES) += spapr_nvram.o diff --git a/qemu/hw/nvram/ds1225y.c b/qemu/hw/nvram/ds1225y.c deleted file mode 100644 index 57d5ab215..000000000 --- a/qemu/hw/nvram/ds1225y.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * QEMU NVRAM emulation for DS1225Y chip - * - * Copyright (c) 2007-2008 Hervé Poussineau - * - * 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/sysbus.h" -#include "trace.h" - -typedef struct { - MemoryRegion iomem; - uint32_t chip_size; - char *filename; - FILE *file; - uint8_t *contents; -} NvRamState; - -static uint64_t nvram_read(void *opaque, hwaddr addr, unsigned size) -{ - NvRamState *s = opaque; - uint32_t val; - - val = s->contents[addr]; - trace_nvram_read(addr, val); - return val; -} - -static void nvram_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - NvRamState *s = opaque; - - val &= 0xff; - trace_nvram_write(addr, s->contents[addr], val); - - s->contents[addr] = val; - if (s->file) { - fseek(s->file, addr, SEEK_SET); - fputc(val, s->file); - fflush(s->file); - } -} - -static const MemoryRegionOps nvram_ops = { - .read = nvram_read, - .write = nvram_write, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static int nvram_post_load(void *opaque, int version_id) -{ - NvRamState *s = opaque; - - /* Close file, as filename may has changed in load/store process */ - if (s->file) { - fclose(s->file); - } - - /* Write back nvram contents */ - s->file = fopen(s->filename, "wb"); - if (s->file) { - /* Write back contents, as 'wb' mode cleaned the file */ - if (fwrite(s->contents, s->chip_size, 1, s->file) != 1) { - printf("nvram_post_load: short write\n"); - } - fflush(s->file); - } - - return 0; -} - -static const VMStateDescription vmstate_nvram = { - .name = "nvram", - .version_id = 0, - .minimum_version_id = 0, - .post_load = nvram_post_load, - .fields = (VMStateField[]) { - VMSTATE_VARRAY_UINT32(contents, NvRamState, chip_size, 0, - vmstate_info_uint8, uint8_t), - VMSTATE_END_OF_LIST() - } -}; - -#define TYPE_DS1225Y "ds1225y" -#define DS1225Y(obj) OBJECT_CHECK(SysBusNvRamState, (obj), TYPE_DS1225Y) - -typedef struct { - SysBusDevice parent_obj; - - NvRamState nvram; -} SysBusNvRamState; - -static int nvram_sysbus_initfn(SysBusDevice *dev) -{ - SysBusNvRamState *sys = DS1225Y(dev); - NvRamState *s = &sys->nvram; - FILE *file; - - s->contents = g_malloc0(s->chip_size); - - memory_region_init_io(&s->iomem, OBJECT(s), &nvram_ops, s, - "nvram", s->chip_size); - sysbus_init_mmio(dev, &s->iomem); - - /* Read current file */ - file = fopen(s->filename, "rb"); - if (file) { - /* Read nvram contents */ - if (fread(s->contents, s->chip_size, 1, file) != 1) { - printf("nvram_sysbus_initfn: short read\n"); - } - fclose(file); - } - nvram_post_load(s, 0); - - return 0; -} - -static Property nvram_sysbus_properties[] = { - DEFINE_PROP_UINT32("size", SysBusNvRamState, nvram.chip_size, 0x2000), - DEFINE_PROP_STRING("filename", SysBusNvRamState, nvram.filename), - DEFINE_PROP_END_OF_LIST(), -}; - -static void nvram_sysbus_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = nvram_sysbus_initfn; - dc->vmsd = &vmstate_nvram; - dc->props = nvram_sysbus_properties; -} - -static const TypeInfo nvram_sysbus_info = { - .name = TYPE_DS1225Y, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysBusNvRamState), - .class_init = nvram_sysbus_class_init, -}; - -static void nvram_register_types(void) -{ - type_register_static(&nvram_sysbus_info); -} - -type_init(nvram_register_types) diff --git a/qemu/hw/nvram/eeprom93xx.c b/qemu/hw/nvram/eeprom93xx.c deleted file mode 100644 index 2c16fc23d..000000000 --- a/qemu/hw/nvram/eeprom93xx.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * QEMU EEPROM 93xx emulation - * - * Copyright (c) 2006-2007 Stefan Weil - * - * 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 . - */ - -/* Emulation for serial EEPROMs: - * NMC93C06 256-Bit (16 x 16) - * NMC93C46 1024-Bit (64 x 16) - * NMC93C56 2028 Bit (128 x 16) - * NMC93C66 4096 Bit (256 x 16) - * Compatible devices include FM93C46 and others. - * - * Other drivers use these interface functions: - * eeprom93xx_new - add a new EEPROM (with 16, 64 or 256 words) - * eeprom93xx_free - destroy EEPROM - * eeprom93xx_read - read data from the EEPROM - * eeprom93xx_write - write data to the EEPROM - * eeprom93xx_data - get EEPROM data array for external manipulation - * - * Todo list: - * - No emulation of EEPROM timings. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/nvram/eeprom93xx.h" - -/* Debug EEPROM emulation. */ -//~ #define DEBUG_EEPROM - -#ifdef DEBUG_EEPROM -#define logout(fmt, ...) fprintf(stderr, "EEPROM\t%-24s" fmt, __func__, ## __VA_ARGS__) -#else -#define logout(fmt, ...) ((void)0) -#endif - -#define EEPROM_INSTANCE 0 -#define OLD_EEPROM_VERSION 20061112 -#define EEPROM_VERSION (OLD_EEPROM_VERSION + 1) - -#if 0 -typedef enum { - eeprom_read = 0x80, /* read register xx */ - eeprom_write = 0x40, /* write register xx */ - eeprom_erase = 0xc0, /* erase register xx */ - eeprom_ewen = 0x30, /* erase / write enable */ - eeprom_ewds = 0x00, /* erase / write disable */ - eeprom_eral = 0x20, /* erase all registers */ - eeprom_wral = 0x10, /* write all registers */ - eeprom_amask = 0x0f, - eeprom_imask = 0xf0 -} eeprom_instruction_t; -#endif - -#ifdef DEBUG_EEPROM -static const char *opstring[] = { - "extended", "write", "read", "erase" -}; -#endif - -struct _eeprom_t { - uint8_t tick; - uint8_t address; - uint8_t command; - uint8_t writable; - - uint8_t eecs; - uint8_t eesk; - uint8_t eedo; - - uint8_t addrbits; - uint16_t size; - uint16_t data; - uint16_t contents[0]; -}; - -/* Code for saving and restoring of EEPROM state. */ - -/* Restore an uint16_t from an uint8_t - This is a Big hack, but it is how the old state did it. - */ - -static int get_uint16_from_uint8(QEMUFile *f, void *pv, size_t size) -{ - uint16_t *v = pv; - *v = qemu_get_ubyte(f); - return 0; -} - -static void put_unused(QEMUFile *f, void *pv, size_t size) -{ - fprintf(stderr, "uint16_from_uint8 is used only for backwards compatibility.\n"); - fprintf(stderr, "Never should be used to write a new state.\n"); - exit(0); -} - -static const VMStateInfo vmstate_hack_uint16_from_uint8 = { - .name = "uint16_from_uint8", - .get = get_uint16_from_uint8, - .put = put_unused, -}; - -#define VMSTATE_UINT16_HACK_TEST(_f, _s, _t) \ - VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_hack_uint16_from_uint8, uint16_t) - -static bool is_old_eeprom_version(void *opaque, int version_id) -{ - return version_id == OLD_EEPROM_VERSION; -} - -static const VMStateDescription vmstate_eeprom = { - .name = "eeprom", - .version_id = EEPROM_VERSION, - .minimum_version_id = OLD_EEPROM_VERSION, - .fields = (VMStateField[]) { - VMSTATE_UINT8(tick, eeprom_t), - VMSTATE_UINT8(address, eeprom_t), - VMSTATE_UINT8(command, eeprom_t), - VMSTATE_UINT8(writable, eeprom_t), - - VMSTATE_UINT8(eecs, eeprom_t), - VMSTATE_UINT8(eesk, eeprom_t), - VMSTATE_UINT8(eedo, eeprom_t), - - VMSTATE_UINT8(addrbits, eeprom_t), - VMSTATE_UINT16_HACK_TEST(size, eeprom_t, is_old_eeprom_version), - VMSTATE_UNUSED_TEST(is_old_eeprom_version, 1), - VMSTATE_UINT16_EQUAL_V(size, eeprom_t, EEPROM_VERSION), - VMSTATE_UINT16(data, eeprom_t), - VMSTATE_VARRAY_UINT16_UNSAFE(contents, eeprom_t, size, 0, - vmstate_info_uint16, uint16_t), - VMSTATE_END_OF_LIST() - } -}; - -void eeprom93xx_write(eeprom_t *eeprom, int eecs, int eesk, int eedi) -{ - uint8_t tick = eeprom->tick; - uint8_t eedo = eeprom->eedo; - uint16_t address = eeprom->address; - uint8_t command = eeprom->command; - - logout("CS=%u SK=%u DI=%u DO=%u, tick = %u\n", - eecs, eesk, eedi, eedo, tick); - - if (!eeprom->eecs && eecs) { - /* Start chip select cycle. */ - logout("Cycle start, waiting for 1st start bit (0)\n"); - tick = 0; - command = 0x0; - address = 0x0; - } else if (eeprom->eecs && !eecs) { - /* End chip select cycle. This triggers write / erase. */ - if (eeprom->writable) { - uint8_t subcommand = address >> (eeprom->addrbits - 2); - if (command == 0 && subcommand == 2) { - /* Erase all. */ - for (address = 0; address < eeprom->size; address++) { - eeprom->contents[address] = 0xffff; - } - } else if (command == 3) { - /* Erase word. */ - eeprom->contents[address] = 0xffff; - } else if (tick >= 2 + 2 + eeprom->addrbits + 16) { - if (command == 1) { - /* Write word. */ - eeprom->contents[address] &= eeprom->data; - } else if (command == 0 && subcommand == 1) { - /* Write all. */ - for (address = 0; address < eeprom->size; address++) { - eeprom->contents[address] &= eeprom->data; - } - } - } - } - /* Output DO is tristate, read results in 1. */ - eedo = 1; - } else if (eecs && !eeprom->eesk && eesk) { - /* Raising edge of clock shifts data in. */ - if (tick == 0) { - /* Wait for 1st start bit. */ - if (eedi == 0) { - logout("Got correct 1st start bit, waiting for 2nd start bit (1)\n"); - tick++; - } else { - logout("wrong 1st start bit (is 1, should be 0)\n"); - tick = 2; - //~ assert(!"wrong start bit"); - } - } else if (tick == 1) { - /* Wait for 2nd start bit. */ - if (eedi != 0) { - logout("Got correct 2nd start bit, getting command + address\n"); - tick++; - } else { - logout("1st start bit is longer than needed\n"); - } - } else if (tick < 2 + 2) { - /* Got 2 start bits, transfer 2 opcode bits. */ - tick++; - command <<= 1; - if (eedi) { - command += 1; - } - } else if (tick < 2 + 2 + eeprom->addrbits) { - /* Got 2 start bits and 2 opcode bits, transfer all address bits. */ - tick++; - address = ((address << 1) | eedi); - if (tick == 2 + 2 + eeprom->addrbits) { - logout("%s command, address = 0x%02x (value 0x%04x)\n", - opstring[command], address, eeprom->contents[address]); - if (command == 2) { - eedo = 0; - } - address = address % eeprom->size; - if (command == 0) { - /* Command code in upper 2 bits of address. */ - switch (address >> (eeprom->addrbits - 2)) { - case 0: - logout("write disable command\n"); - eeprom->writable = 0; - break; - case 1: - logout("write all command\n"); - break; - case 2: - logout("erase all command\n"); - break; - case 3: - logout("write enable command\n"); - eeprom->writable = 1; - break; - } - } else { - /* Read, write or erase word. */ - eeprom->data = eeprom->contents[address]; - } - } - } else if (tick < 2 + 2 + eeprom->addrbits + 16) { - /* Transfer 16 data bits. */ - tick++; - if (command == 2) { - /* Read word. */ - eedo = ((eeprom->data & 0x8000) != 0); - } - eeprom->data <<= 1; - eeprom->data += eedi; - } else { - logout("additional unneeded tick, not processed\n"); - } - } - /* Save status of EEPROM. */ - eeprom->tick = tick; - eeprom->eecs = eecs; - eeprom->eesk = eesk; - eeprom->eedo = eedo; - eeprom->address = address; - eeprom->command = command; -} - -uint16_t eeprom93xx_read(eeprom_t *eeprom) -{ - /* Return status of pin DO (0 or 1). */ - logout("CS=%u DO=%u\n", eeprom->eecs, eeprom->eedo); - return eeprom->eedo; -} - -#if 0 -void eeprom93xx_reset(eeprom_t *eeprom) -{ - /* prepare eeprom */ - logout("eeprom = 0x%p\n", eeprom); - eeprom->tick = 0; - eeprom->command = 0; -} -#endif - -eeprom_t *eeprom93xx_new(DeviceState *dev, uint16_t nwords) -{ - /* Add a new EEPROM (with 16, 64 or 256 words). */ - eeprom_t *eeprom; - uint8_t addrbits; - - switch (nwords) { - case 16: - case 64: - addrbits = 6; - break; - case 128: - case 256: - addrbits = 8; - break; - default: - assert(!"Unsupported EEPROM size, fallback to 64 words!"); - nwords = 64; - addrbits = 6; - } - - eeprom = (eeprom_t *)g_malloc0(sizeof(*eeprom) + nwords * 2); - eeprom->size = nwords; - eeprom->addrbits = addrbits; - /* Output DO is tristate, read results in 1. */ - eeprom->eedo = 1; - logout("eeprom = 0x%p, nwords = %u\n", eeprom, nwords); - vmstate_register(dev, 0, &vmstate_eeprom, eeprom); - return eeprom; -} - -void eeprom93xx_free(DeviceState *dev, eeprom_t *eeprom) -{ - /* Destroy EEPROM. */ - logout("eeprom = 0x%p\n", eeprom); - vmstate_unregister(dev, &vmstate_eeprom, eeprom); - g_free(eeprom); -} - -uint16_t *eeprom93xx_data(eeprom_t *eeprom) -{ - /* Get EEPROM data array. */ - return &eeprom->contents[0]; -} - -/* eof */ diff --git a/qemu/hw/nvram/fw_cfg.c b/qemu/hw/nvram/fw_cfg.c deleted file mode 100644 index 999f48028..000000000 --- a/qemu/hw/nvram/fw_cfg.c +++ /dev/null @@ -1,1099 +0,0 @@ -/* - * QEMU Firmware configuration device emulation - * - * Copyright (c) 2008 Gleb Natapov - * - * 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 "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_NAME "fw_cfg" -#define FW_CFG_PATH "/machine/" FW_CFG_NAME - -#define TYPE_FW_CFG "fw_cfg" -#define TYPE_FW_CFG_IO "fw_cfg_io" -#define TYPE_FW_CFG_MEM "fw_cfg_mem" - -#define FW_CFG(obj) OBJECT_CHECK(FWCfgState, (obj), TYPE_FW_CFG) -#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; - void *callback_opaque; - FWCfgReadCallback read_callback; -} FWCfgEntry; - -struct FWCfgState { - /*< private >*/ - SysBusDevice parent_obj; - /*< 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 { - /*< private >*/ - FWCfgState parent_obj; - /*< public >*/ - - MemoryRegion comb_iomem; - uint32_t iobase, dma_iobase; -}; - -struct FWCfgMemState { - /*< private >*/ - FWCfgState parent_obj; - /*< public >*/ - - MemoryRegion ctl_iomem, data_iomem; - uint32_t data_width; - MemoryRegionOps wide_data_ops; -}; - -#define JPG_FILE 0 -#define BMP_FILE 1 - -static char *read_splashfile(char *filename, gsize *file_sizep, - int *file_typep) -{ - GError *err = NULL; - gboolean res; - gchar *content; - int file_type; - unsigned int filehead; - int bmp_bpp; - - res = g_file_get_contents(filename, &content, file_sizep, &err); - if (res == FALSE) { - error_report("failed to read splash file '%s'", filename); - g_error_free(err); - return NULL; - } - - /* check file size */ - if (*file_sizep < 30) { - goto error; - } - - /* check magic ID */ - filehead = ((content[0] & 0xff) + (content[1] << 8)) & 0xffff; - if (filehead == 0xd8ff) { - file_type = JPG_FILE; - } else if (filehead == 0x4d42) { - file_type = BMP_FILE; - } else { - goto error; - } - - /* check BMP bpp */ - if (file_type == BMP_FILE) { - bmp_bpp = (content[28] + (content[29] << 8)) & 0xffff; - if (bmp_bpp != 24) { - goto error; - } - } - - /* return values */ - *file_typep = file_type; - - return content; - -error: - error_report("splash file '%s' format not recognized; must be JPEG " - "or 24 bit BMP", filename); - g_free(content); - return NULL; -} - -static void fw_cfg_bootsplash(FWCfgState *s) -{ - int boot_splash_time = -1; - const char *boot_splash_filename = NULL; - char *p; - char *filename, *file_data; - gsize file_size; - int file_type; - const char *temp; - - /* get user configuration */ - QemuOptsList *plist = qemu_find_opts("boot-opts"); - QemuOpts *opts = QTAILQ_FIRST(&plist->head); - if (opts != NULL) { - temp = qemu_opt_get(opts, "splash"); - if (temp != NULL) { - boot_splash_filename = temp; - } - temp = qemu_opt_get(opts, "splash-time"); - if (temp != NULL) { - p = (char *)temp; - boot_splash_time = strtol(p, (char **)&p, 10); - } - } - - /* insert splash time if user configurated */ - if (boot_splash_time >= 0) { - /* validate the input */ - if (boot_splash_time > 0xffff) { - error_report("splash time is big than 65535, force it to 65535."); - boot_splash_time = 0xffff; - } - /* use little endian format */ - qemu_extra_params_fw[0] = (uint8_t)(boot_splash_time & 0xff); - qemu_extra_params_fw[1] = (uint8_t)((boot_splash_time >> 8) & 0xff); - fw_cfg_add_file(s, "etc/boot-menu-wait", qemu_extra_params_fw, 2); - } - - /* insert splash file if user configurated */ - if (boot_splash_filename != NULL) { - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, boot_splash_filename); - if (filename == NULL) { - error_report("failed to find file '%s'.", boot_splash_filename); - return; - } - - /* loading file data */ - file_data = read_splashfile(filename, &file_size, &file_type); - if (file_data == NULL) { - g_free(filename); - return; - } - g_free(boot_splash_filedata); - boot_splash_filedata = (uint8_t *)file_data; - boot_splash_filedata_size = file_size; - - /* insert data */ - if (file_type == JPG_FILE) { - fw_cfg_add_file(s, "bootsplash.jpg", - boot_splash_filedata, boot_splash_filedata_size); - } else { - fw_cfg_add_file(s, "bootsplash.bmp", - boot_splash_filedata, boot_splash_filedata_size); - } - g_free(filename); - } -} - -static void fw_cfg_reboot(FWCfgState *s) -{ - int reboot_timeout = -1; - char *p; - const char *temp; - - /* get user configuration */ - QemuOptsList *plist = qemu_find_opts("boot-opts"); - QemuOpts *opts = QTAILQ_FIRST(&plist->head); - if (opts != NULL) { - temp = qemu_opt_get(opts, "reboot-timeout"); - if (temp != NULL) { - p = (char *)temp; - reboot_timeout = strtol(p, (char **)&p, 10); - } - } - /* validate the input */ - if (reboot_timeout > 0xffff) { - error_report("reboot timeout is larger than 65535, force it to 65535."); - reboot_timeout = 0xffff; - } - fw_cfg_add_file(s, "etc/boot-fail-wait", g_memdup(&reboot_timeout, 4), 4); -} - -static void fw_cfg_write(FWCfgState *s, uint8_t value) -{ - /* nothing, write support removed in QEMU v2.4+ */ -} - -static int fw_cfg_select(FWCfgState *s, uint16_t key) -{ - int arch, ret; - FWCfgEntry *e; - - s->cur_offset = 0; - if ((key & FW_CFG_ENTRY_MASK) >= FW_CFG_MAX_ENTRY) { - s->cur_entry = FW_CFG_INVALID; - ret = 0; - } else { - s->cur_entry = key; - ret = 1; - /* 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); - } - } - - trace_fw_cfg_select(s, key, ret); - return ret; -} - -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; - - 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; -} - -static void fw_cfg_data_mem_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - FWCfgState *s = opaque; - unsigned i = size; - - do { - fw_cfg_write(s, value >> (8 * --i)); - } 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) -{ - return addr == 0; -} - -static void fw_cfg_ctl_mem_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - fw_cfg_select(opaque, (uint16_t)value); -} - -static bool fw_cfg_ctl_mem_valid(void *opaque, hwaddr addr, - unsigned size, bool is_write) -{ - return is_write && size == 2; -} - -static void fw_cfg_comb_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - switch (size) { - case 1: - fw_cfg_write(opaque, (uint8_t)value); - break; - case 2: - fw_cfg_select(opaque, (uint16_t)value); - break; - } -} - -static bool fw_cfg_comb_valid(void *opaque, hwaddr addr, - unsigned size, bool is_write) -{ - return (size == 1) || (is_write && size == 2); -} - -static const MemoryRegionOps fw_cfg_ctl_mem_ops = { - .write = fw_cfg_ctl_mem_write, - .endianness = DEVICE_BIG_ENDIAN, - .valid.accepts = fw_cfg_ctl_mem_valid, -}; - -static const MemoryRegionOps fw_cfg_data_mem_ops = { - .read = fw_cfg_data_read, - .write = fw_cfg_data_mem_write, - .endianness = DEVICE_BIG_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - .accepts = fw_cfg_data_mem_valid, - }, -}; - -static const MemoryRegionOps fw_cfg_comb_mem_ops = { - .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); - - /* 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 - This is a Big hack, but it is how the old state did it. - Or we broke compatibility in the state, or we can't use struct tm - */ - -static int get_uint32_as_uint16(QEMUFile *f, void *pv, size_t size) -{ - uint32_t *v = pv; - *v = qemu_get_be16(f); - return 0; -} - -static void put_unused(QEMUFile *f, void *pv, size_t size) -{ - fprintf(stderr, "uint32_as_uint16 is only used for backward compatibility.\n"); - fprintf(stderr, "This functions shouldn't be called.\n"); -} - -static const VMStateInfo vmstate_hack_uint32_as_uint16 = { - .name = "int32_as_uint16", - .get = get_uint32_as_uint16, - .put = put_unused, -}; - -#define VMSTATE_UINT16_HACK(_f, _s, _t) \ - VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_hack_uint32_as_uint16, uint32_t) - - -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, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT16(cur_entry, FWCfgState), - 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, - } -}; - -static void fw_cfg_add_bytes_read_callback(FWCfgState *s, uint16_t key, - FWCfgReadCallback callback, - void *callback_opaque, - void *data, size_t len) -{ - int arch = !!(key & FW_CFG_ARCH_LOCAL); - - key &= FW_CFG_ENTRY_MASK; - - assert(key < FW_CFG_MAX_ENTRY && len < UINT32_MAX); - assert(s->entries[arch][key].data == NULL); /* avoid key conflict */ - - s->entries[arch][key].data = data; - s->entries[arch][key].len = (uint32_t)len; - s->entries[arch][key].read_callback = callback; - s->entries[arch][key].callback_opaque = callback_opaque; -} - -static void *fw_cfg_modify_bytes_read(FWCfgState *s, uint16_t key, - void *data, size_t len) -{ - void *ptr; - int arch = !!(key & FW_CFG_ARCH_LOCAL); - - key &= FW_CFG_ENTRY_MASK; - - assert(key < FW_CFG_MAX_ENTRY && len < UINT32_MAX); - - /* return the old data to the function caller, avoid memory leak */ - ptr = s->entries[arch][key].data; - s->entries[arch][key].data = data; - s->entries[arch][key].len = len; - s->entries[arch][key].callback_opaque = NULL; - - return ptr; -} - -void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len) -{ - fw_cfg_add_bytes_read_callback(s, key, NULL, NULL, data, len); -} - -void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value) -{ - size_t sz = strlen(value) + 1; - - fw_cfg_add_bytes(s, key, g_memdup(value, sz), sz); -} - -void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value) -{ - uint16_t *copy; - - copy = g_malloc(sizeof(value)); - *copy = cpu_to_le16(value); - fw_cfg_add_bytes(s, key, copy, sizeof(value)); -} - -void fw_cfg_modify_i16(FWCfgState *s, uint16_t key, uint16_t value) -{ - uint16_t *copy, *old; - - copy = g_malloc(sizeof(value)); - *copy = cpu_to_le16(value); - old = fw_cfg_modify_bytes_read(s, key, copy, sizeof(value)); - g_free(old); -} - -void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value) -{ - uint32_t *copy; - - copy = g_malloc(sizeof(value)); - *copy = cpu_to_le32(value); - fw_cfg_add_bytes(s, key, copy, sizeof(value)); -} - -void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value) -{ - uint64_t *copy; - - copy = g_malloc(sizeof(value)); - *copy = cpu_to_le64(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, 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; - s->files = g_malloc0(dsize); - fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, s->files, dsize); - } - - 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--); - } - - /* - * 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); - } - } - - fw_cfg_add_bytes_read_callback(s, FW_CFG_FILE_FIRST + index, - callback, callback_opaque, data, len); - - 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(count+1); -} - -void fw_cfg_add_file(FWCfgState *s, const char *filename, - void *data, size_t len) -{ - fw_cfg_add_file_callback(s, filename, NULL, NULL, data, len); -} - -void *fw_cfg_modify_file(FWCfgState *s, const char *filename, - void *data, size_t len) -{ - int i, index; - void *ptr = NULL; - - assert(s->files); - - index = be32_to_cpu(s->files->count); - assert(index < FW_CFG_FILE_SLOTS); - - for (i = 0; i < index; i++) { - if (strcmp(filename, s->files->f[i].name) == 0) { - ptr = fw_cfg_modify_bytes_read(s, FW_CFG_FILE_FIRST + i, - data, len); - s->files->f[i].size = cpu_to_be32(len); - return ptr; - } - } - /* add new one */ - fw_cfg_add_file_callback(s, filename, NULL, NULL, data, len); - return NULL; -} - -static void fw_cfg_machine_reset(void *opaque) -{ - void *ptr; - size_t len; - FWCfgState *s = opaque; - char *bootindex = get_boot_devices_list(&len, false); - - ptr = fw_cfg_modify_file(s, "bootorder", (uint8_t *)bootindex, len); - g_free(ptr); -} - -static void fw_cfg_machine_ready(struct Notifier *n, void *data) -{ - FWCfgState *s = container_of(n, FWCfgState, machine_ready); - qemu_register_reset(fw_cfg_machine_reset, s); -} - - - -static void fw_cfg_init1(DeviceState *dev) -{ - FWCfgState *s = FW_CFG(dev); - - assert(!object_resolve_path(FW_CFG_PATH, NULL)); - - object_property_add_child(qdev_get_machine(), FW_CFG_NAME, OBJECT(s), NULL); - - qdev_init_nofail(dev); - - fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4); - 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); - fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu); - fw_cfg_bootsplash(s); - fw_cfg_reboot(s); - - s->machine_ready.notify = fw_cfg_machine_ready; - qemu_add_machine_init_done_notifier(&s->machine_ready); -} - -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; -} - -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, - 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); - - sbd = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(sbd, 0, ctl_addr); - sysbus_mmio_map(sbd, 1, data_addr); - - 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, - 0, NULL); -} - - -FWCfgState *fw_cfg_find(void) -{ - return FW_CFG(object_resolve_path(FW_CFG_PATH, NULL)); -} - -static void fw_cfg_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = fw_cfg_reset; - dc->vmsd = &vmstate_fw_cfg; -} - -static const TypeInfo fw_cfg_info = { - .name = TYPE_FW_CFG, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(FWCfgState), - .class_init = fw_cfg_class_init, -}; - - -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(), -}; - -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_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) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = fw_cfg_io_realize; - dc->props = fw_cfg_io_properties; -} - -static const TypeInfo fw_cfg_io_info = { - .name = TYPE_FW_CFG_IO, - .parent = TYPE_FW_CFG, - .instance_size = sizeof(FWCfgIoState), - .class_init = fw_cfg_io_class_init, -}; - - -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(), -}; - -static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) -{ - FWCfgMemState *s = FW_CFG_MEM(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - 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_CTL_SIZE); - sysbus_init_mmio(sbd, &s->ctl_iomem); - - if (s->data_width > data_ops->valid.max_access_size) { - /* memberwise copy because the "old_mmio" member is const */ - s->wide_data_ops.read = data_ops->read; - s->wide_data_ops.write = data_ops->write; - s->wide_data_ops.endianness = data_ops->endianness; - s->wide_data_ops.valid = data_ops->valid; - s->wide_data_ops.impl = data_ops->impl; - - s->wide_data_ops.valid.max_access_size = s->data_width; - s->wide_data_ops.impl.max_access_size = s->data_width; - data_ops = &s->wide_data_ops; - } - 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) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = fw_cfg_mem_realize; - dc->props = fw_cfg_mem_properties; -} - -static const TypeInfo fw_cfg_mem_info = { - .name = TYPE_FW_CFG_MEM, - .parent = TYPE_FW_CFG, - .instance_size = sizeof(FWCfgMemState), - .class_init = fw_cfg_mem_class_init, -}; - - -static void fw_cfg_register_types(void) -{ - type_register_static(&fw_cfg_info); - type_register_static(&fw_cfg_io_info); - type_register_static(&fw_cfg_mem_info); -} - -type_init(fw_cfg_register_types) diff --git a/qemu/hw/nvram/mac_nvram.c b/qemu/hw/nvram/mac_nvram.c deleted file mode 100644 index 24f61212b..000000000 --- a/qemu/hw/nvram/mac_nvram.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * PowerMac NVRAM emulation - * - * Copyright (c) 2005-2007 Fabrice Bellard - * Copyright (c) 2007 Jocelyn Mayer - * - * 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/nvram/openbios_firmware_abi.h" -#include "sysemu/sysemu.h" -#include "hw/ppc/mac.h" -#include "qemu/cutils.h" -#include - -/* debug NVR */ -//#define DEBUG_NVR - -#ifdef DEBUG_NVR -#define NVR_DPRINTF(fmt, ...) \ - do { printf("NVR: " fmt , ## __VA_ARGS__); } while (0) -#else -#define NVR_DPRINTF(fmt, ...) -#endif - -#define DEF_SYSTEM_SIZE 0xc10 - -/* macio style NVRAM device */ -static void macio_nvram_writeb(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - MacIONVRAMState *s = opaque; - - addr = (addr >> s->it_shift) & (s->size - 1); - s->data[addr] = value; - NVR_DPRINTF("writeb addr %04" HWADDR_PRIx " val %" PRIx64 "\n", - addr, value); -} - -static uint64_t macio_nvram_readb(void *opaque, hwaddr addr, - unsigned size) -{ - MacIONVRAMState *s = opaque; - uint32_t value; - - addr = (addr >> s->it_shift) & (s->size - 1); - value = s->data[addr]; - NVR_DPRINTF("readb addr %04" HWADDR_PRIx " val %" PRIx32 "\n", - addr, value); - - return value; -} - -static const MemoryRegionOps macio_nvram_ops = { - .read = macio_nvram_readb, - .write = macio_nvram_writeb, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .impl.min_access_size = 1, - .impl.max_access_size = 1, - .endianness = DEVICE_BIG_ENDIAN, -}; - -static const VMStateDescription vmstate_macio_nvram = { - .name = "macio_nvram", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_VBUFFER_UINT32(data, MacIONVRAMState, 0, NULL, 0, size), - VMSTATE_END_OF_LIST() - } -}; - - -static void macio_nvram_reset(DeviceState *dev) -{ -} - -static void macio_nvram_realizefn(DeviceState *dev, Error **errp) -{ - SysBusDevice *d = SYS_BUS_DEVICE(dev); - MacIONVRAMState *s = MACIO_NVRAM(dev); - - s->data = g_malloc0(s->size); - - memory_region_init_io(&s->mem, OBJECT(s), &macio_nvram_ops, s, - "macio-nvram", s->size << s->it_shift); - sysbus_init_mmio(d, &s->mem); -} - -static void macio_nvram_unrealizefn(DeviceState *dev, Error **errp) -{ - MacIONVRAMState *s = MACIO_NVRAM(dev); - - g_free(s->data); -} - -static Property macio_nvram_properties[] = { - DEFINE_PROP_UINT32("size", MacIONVRAMState, size, 0), - DEFINE_PROP_UINT32("it_shift", MacIONVRAMState, it_shift, 0), - DEFINE_PROP_END_OF_LIST() -}; - -static void macio_nvram_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = macio_nvram_realizefn; - dc->unrealize = macio_nvram_unrealizefn; - 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 = { - .name = TYPE_MACIO_NVRAM, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MacIONVRAMState), - .class_init = macio_nvram_class_init, -}; - -static void macio_nvram_register_types(void) -{ - type_register_static(&macio_nvram_type_info); -} - -/* Set up a system OpenBIOS NVRAM partition */ -static void pmac_format_nvram_partition_of(MacIONVRAMState *nvr, int off, - int len) -{ - unsigned int i; - uint32_t start = off, end; - struct OpenBIOS_nvpart_v1 *part_header; - - // OpenBIOS nvram variables - // Variable partition - part_header = (struct OpenBIOS_nvpart_v1 *)&nvr->data[start]; - part_header->signature = OPENBIOS_PART_SYSTEM; - pstrcpy(part_header->name, sizeof(part_header->name), "system"); - - end = start + sizeof(struct OpenBIOS_nvpart_v1); - for (i = 0; i < nb_prom_envs; i++) - end = OpenBIOS_set_var(nvr->data, end, prom_envs[i]); - - // End marker - nvr->data[end++] = '\0'; - - end = start + ((end - start + 15) & ~15); - /* XXX: OpenBIOS is not able to grow up a partition. Leave some space for - new variables. */ - if (end < DEF_SYSTEM_SIZE) - end = DEF_SYSTEM_SIZE; - OpenBIOS_finish_partition(part_header, end - start); - - // free partition - start = end; - part_header = (struct OpenBIOS_nvpart_v1 *)&nvr->data[start]; - part_header->signature = OPENBIOS_PART_FREE; - pstrcpy(part_header->name, sizeof(part_header->name), "free"); - - end = len; - OpenBIOS_finish_partition(part_header, end - start); -} - -#define OSX_NVRAM_SIGNATURE (0x5A) - -/* Set up a Mac OS X NVRAM partition */ -static void pmac_format_nvram_partition_osx(MacIONVRAMState *nvr, int off, - int len) -{ - uint32_t start = off; - struct OpenBIOS_nvpart_v1 *part_header; - unsigned char *data = &nvr->data[start]; - - /* empty partition */ - part_header = (struct OpenBIOS_nvpart_v1 *)data; - part_header->signature = OSX_NVRAM_SIGNATURE; - pstrcpy(part_header->name, sizeof(part_header->name), "wwwwwwwwwwww"); - - OpenBIOS_finish_partition(part_header, len); - - /* Generation */ - stl_be_p(&data[20], 2); - - /* Adler32 checksum */ - stl_be_p(&data[16], adler32(0, &data[20], len - 20)); -} - -/* Set up NVRAM with OF and OSX partitions */ -void pmac_format_nvram_partition(MacIONVRAMState *nvr, int len) -{ - /* - * Mac OS X expects side "B" of the flash at the second half of NVRAM, - * so we use half of the chip for OF and the other half for a free OSX - * partition. - */ - pmac_format_nvram_partition_of(nvr, 0, len / 2); - pmac_format_nvram_partition_osx(nvr, len / 2, len / 2); -} -type_init(macio_nvram_register_types) diff --git a/qemu/hw/nvram/spapr_nvram.c b/qemu/hw/nvram/spapr_nvram.c deleted file mode 100644 index 802636ef3..000000000 --- a/qemu/hw/nvram/spapr_nvram.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * QEMU sPAPR NVRAM emulation - * - * Copyright (C) 2012 David Gibson, IBM Corporation. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include - -#include "sysemu/block-backend.h" -#include "sysemu/device_tree.h" -#include "hw/sysbus.h" -#include "hw/ppc/spapr.h" -#include "hw/ppc/spapr_vio.h" - -typedef struct sPAPRNVRAM { - VIOsPAPRDevice sdev; - uint32_t size; - uint8_t *buf; - BlockBackend *blk; -} sPAPRNVRAM; - -#define TYPE_VIO_SPAPR_NVRAM "spapr-nvram" -#define VIO_SPAPR_NVRAM(obj) \ - OBJECT_CHECK(sPAPRNVRAM, (obj), TYPE_VIO_SPAPR_NVRAM) - -#define MIN_NVRAM_SIZE 8192 -#define DEFAULT_NVRAM_SIZE 65536 -#define MAX_NVRAM_SIZE 1048576 - -static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - sPAPRNVRAM *nvram = spapr->nvram; - hwaddr offset, buffer, len; - void *membuf; - - if ((nargs != 3) || (nret != 2)) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - if (!nvram) { - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - rtas_st(rets, 1, 0); - return; - } - - offset = rtas_ld(args, 0); - buffer = rtas_ld(args, 1); - len = rtas_ld(args, 2); - - if (((offset + len) < offset) - || ((offset + len) > nvram->size)) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - rtas_st(rets, 1, 0); - return; - } - - assert(nvram->buf); - - membuf = cpu_physical_memory_map(buffer, &len, 1); - memcpy(membuf, nvram->buf + offset, len); - cpu_physical_memory_unmap(membuf, len, 1, len); - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - rtas_st(rets, 1, len); -} - -static void rtas_nvram_store(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - sPAPRNVRAM *nvram = spapr->nvram; - hwaddr offset, buffer, len; - int alen; - void *membuf; - - if ((nargs != 3) || (nret != 2)) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - if (!nvram) { - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - - offset = rtas_ld(args, 0); - buffer = rtas_ld(args, 1); - len = rtas_ld(args, 2); - - if (((offset + len) < offset) - || ((offset + len) > nvram->size)) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - membuf = cpu_physical_memory_map(buffer, &len, 0); - - alen = len; - if (nvram->blk) { - alen = blk_pwrite(nvram->blk, offset, membuf, len); - } - - assert(nvram->buf); - memcpy(nvram->buf + offset, membuf, len); - - cpu_physical_memory_unmap(membuf, len, 0, len); - - rtas_st(rets, 0, (alen < len) ? RTAS_OUT_HW_ERROR : RTAS_OUT_SUCCESS); - rtas_st(rets, 1, (alen < 0) ? 0 : alen); -} - -static void spapr_nvram_realize(VIOsPAPRDevice *dev, Error **errp) -{ - sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(dev); - - if (nvram->blk) { - nvram->size = blk_getlength(nvram->blk); - } else { - nvram->size = DEFAULT_NVRAM_SIZE; - } - - nvram->buf = g_malloc0(nvram->size); - - if ((nvram->size < MIN_NVRAM_SIZE) || (nvram->size > MAX_NVRAM_SIZE)) { - error_setg(errp, "spapr-nvram must be between %d and %d bytes in size", - MIN_NVRAM_SIZE, MAX_NVRAM_SIZE); - return; - } - - if (nvram->blk) { - int alen = blk_pread(nvram->blk, 0, nvram->buf, nvram->size); - - if (alen != nvram->size) { - error_setg(errp, "can't read spapr-nvram contents"); - return; - } - } - - spapr_rtas_register(RTAS_NVRAM_FETCH, "nvram-fetch", rtas_nvram_fetch); - spapr_rtas_register(RTAS_NVRAM_STORE, "nvram-store", rtas_nvram_store); -} - -static int spapr_nvram_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) -{ - sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(dev); - - return fdt_setprop_cell(fdt, node_off, "#bytes", nvram->size); -} - -static int spapr_nvram_pre_load(void *opaque) -{ - sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(opaque); - - g_free(nvram->buf); - nvram->buf = NULL; - nvram->size = 0; - - return 0; -} - -static int spapr_nvram_post_load(void *opaque, int version_id) -{ - sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(opaque); - - if (nvram->blk) { - int alen = blk_pwrite(nvram->blk, 0, nvram->buf, nvram->size); - - if (alen < 0) { - return alen; - } - if (alen != nvram->size) { - return -1; - } - } - - return 0; -} - -static const VMStateDescription vmstate_spapr_nvram = { - .name = "spapr_nvram", - .version_id = 1, - .minimum_version_id = 1, - .pre_load = spapr_nvram_pre_load, - .post_load = spapr_nvram_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(size, sPAPRNVRAM), - VMSTATE_VBUFFER_ALLOC_UINT32(buf, sPAPRNVRAM, 1, NULL, 0, size), - VMSTATE_END_OF_LIST() - }, -}; - -static Property spapr_nvram_properties[] = { - DEFINE_SPAPR_PROPERTIES(sPAPRNVRAM, sdev), - DEFINE_PROP_DRIVE("drive", sPAPRNVRAM, blk), - DEFINE_PROP_END_OF_LIST(), -}; - -static void spapr_nvram_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); - - k->realize = spapr_nvram_realize; - k->devnode = spapr_nvram_devnode; - k->dt_name = "nvram"; - k->dt_type = "nvram"; - k->dt_compatible = "qemu,spapr-nvram"; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->props = spapr_nvram_properties; - dc->vmsd = &vmstate_spapr_nvram; -} - -static const TypeInfo spapr_nvram_type_info = { - .name = TYPE_VIO_SPAPR_NVRAM, - .parent = TYPE_VIO_SPAPR_DEVICE, - .instance_size = sizeof(sPAPRNVRAM), - .class_init = spapr_nvram_class_init, -}; - -static void spapr_nvram_register_types(void) -{ - type_register_static(&spapr_nvram_type_info); -} - -type_init(spapr_nvram_register_types) diff --git a/qemu/hw/openrisc/Makefile.objs b/qemu/hw/openrisc/Makefile.objs deleted file mode 100644 index 61246b149..000000000 --- a/qemu/hw/openrisc/Makefile.objs +++ /dev/null @@ -1,2 +0,0 @@ -obj-y = pic_cpu.o cputimer.o -obj-y += openrisc_sim.o diff --git a/qemu/hw/openrisc/cputimer.c b/qemu/hw/openrisc/cputimer.c deleted file mode 100644 index a98c799de..000000000 --- a/qemu/hw/openrisc/cputimer.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * QEMU OpenRISC timer support - * - * Copyright (c) 2011-2012 Jia Liu - * Zhizhou Zhang - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "hw/hw.h" -#include "qemu/timer.h" - -#define TIMER_PERIOD 50 /* 50 ns period for 20 MHz timer */ - -/* The time when TTCR changes */ -static uint64_t last_clk; -static int is_counting; - -void cpu_openrisc_count_update(OpenRISCCPU *cpu) -{ - uint64_t now; - - if (!is_counting) { - return; - } - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - cpu->env.ttcr += (uint32_t)((now - last_clk) / TIMER_PERIOD); - last_clk = now; -} - -void cpu_openrisc_timer_update(OpenRISCCPU *cpu) -{ - uint32_t wait; - uint64_t now, next; - - if (!is_counting) { - return; - } - - cpu_openrisc_count_update(cpu); - now = last_clk; - - if ((cpu->env.ttmr & TTMR_TP) <= (cpu->env.ttcr & TTMR_TP)) { - wait = TTMR_TP - (cpu->env.ttcr & TTMR_TP) + 1; - wait += cpu->env.ttmr & TTMR_TP; - } else { - wait = (cpu->env.ttmr & TTMR_TP) - (cpu->env.ttcr & TTMR_TP); - } - next = now + (uint64_t)wait * TIMER_PERIOD; - timer_mod(cpu->env.timer, next); -} - -void cpu_openrisc_count_start(OpenRISCCPU *cpu) -{ - is_counting = 1; - cpu_openrisc_count_update(cpu); -} - -void cpu_openrisc_count_stop(OpenRISCCPU *cpu) -{ - timer_del(cpu->env.timer); - cpu_openrisc_count_update(cpu); - is_counting = 0; -} - -static void openrisc_timer_cb(void *opaque) -{ - OpenRISCCPU *cpu = opaque; - - if ((cpu->env.ttmr & TTMR_IE) && - timer_expired(cpu->env.timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL))) { - CPUState *cs = CPU(cpu); - - cpu->env.ttmr |= TTMR_IP; - cs->interrupt_request |= CPU_INTERRUPT_TIMER; - } - - switch (cpu->env.ttmr & TTMR_M) { - case TIMER_NONE: - break; - case TIMER_INTR: - cpu->env.ttcr = 0; - break; - case TIMER_SHOT: - cpu_openrisc_count_stop(cpu); - break; - case TIMER_CONT: - break; - } - - cpu_openrisc_timer_update(cpu); -} - -void cpu_openrisc_clock_init(OpenRISCCPU *cpu) -{ - cpu->env.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &openrisc_timer_cb, cpu); - cpu->env.ttmr = 0x00000000; - cpu->env.ttcr = 0x00000000; -} diff --git a/qemu/hw/openrisc/openrisc_sim.c b/qemu/hw/openrisc/openrisc_sim.c deleted file mode 100644 index 6d06d5be0..000000000 --- a/qemu/hw/openrisc/openrisc_sim.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * OpenRISC simulator for use as an IIS. - * - * Copyright (c) 2011-2012 Jia Liu - * Feng Gao - * - * 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 . - */ - -#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" -#include "hw/char/serial.h" -#include "net/net.h" -#include "hw/loader.h" -#include "exec/address-spaces.h" -#include "sysemu/sysemu.h" -#include "hw/sysbus.h" -#include "sysemu/qtest.h" - -#define KERNEL_LOAD_ADDR 0x100 - -static void main_cpu_reset(void *opaque) -{ - OpenRISCCPU *cpu = opaque; - - cpu_reset(CPU(cpu)); -} - -static void openrisc_sim_net_init(MemoryRegion *address_space, - hwaddr base, - hwaddr descriptors, - qemu_irq irq, NICInfo *nd) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_create(NULL, "open_eth"); - qdev_set_nic_properties(dev, nd); - qdev_init_nofail(dev); - - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, irq); - memory_region_add_subregion(address_space, base, - sysbus_mmio_get_region(s, 0)); - memory_region_add_subregion(address_space, descriptors, - sysbus_mmio_get_region(s, 1)); -} - -static void cpu_openrisc_load_kernel(ram_addr_t ram_size, - const char *kernel_filename, - OpenRISCCPU *cpu) -{ - long kernel_size; - uint64_t elf_entry; - hwaddr entry; - - if (kernel_filename && !qtest_enabled()) { - kernel_size = load_elf(kernel_filename, NULL, NULL, - &elf_entry, NULL, NULL, 1, EM_OPENRISC, - 1, 0); - entry = elf_entry; - if (kernel_size < 0) { - kernel_size = load_uimage(kernel_filename, - &entry, NULL, NULL, NULL, NULL); - } - if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, - KERNEL_LOAD_ADDR, - ram_size - KERNEL_LOAD_ADDR); - entry = KERNEL_LOAD_ADDR; - } - - if (kernel_size < 0) { - fprintf(stderr, "QEMU: couldn't load the kernel '%s'\n", - kernel_filename); - exit(1); - } - cpu->env.pc = entry; - } -} - -static void openrisc_sim_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; - OpenRISCCPU *cpu = NULL; - MemoryRegion *ram; - int n; - - if (!cpu_model) { - cpu_model = "or1200"; - } - - for (n = 0; n < smp_cpus; n++) { - cpu = cpu_openrisc_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to find CPU definition!\n"); - exit(1); - } - qemu_register_reset(main_cpu_reset, cpu); - main_cpu_reset(cpu); - } - - ram = g_malloc(sizeof(*ram)); - 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); - - cpu_openrisc_pic_init(cpu); - cpu_openrisc_clock_init(cpu); - - serial_mm_init(get_system_memory(), 0x90000000, 0, cpu->env.irq[2], - 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); - - if (nd_table[0].used) { - openrisc_sim_net_init(get_system_memory(), 0x92000000, - 0x92000400, cpu->env.irq[4], nd_table); - } - - cpu_openrisc_load_kernel(ram_size, kernel_filename, cpu); -} - -static void openrisc_sim_machine_init(MachineClass *mc) -{ - mc->desc = "or32 simulation"; - mc->init = openrisc_sim_init; - mc->max_cpus = 1; - mc->is_default = 1; -} - -DEFINE_MACHINE("or32-sim", openrisc_sim_machine_init) diff --git a/qemu/hw/openrisc/pic_cpu.c b/qemu/hw/openrisc/pic_cpu.c deleted file mode 100644 index 569b443f5..000000000 --- a/qemu/hw/openrisc/pic_cpu.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * OpenRISC Programmable Interrupt Controller support. - * - * Copyright (c) 2011-2012 Jia Liu - * Feng Gao - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "cpu.h" - -/* OpenRISC pic handler */ -static void openrisc_pic_cpu_handler(void *opaque, int irq, int level) -{ - OpenRISCCPU *cpu = (OpenRISCCPU *)opaque; - CPUState *cs = CPU(cpu); - uint32_t irq_bit; - - if (irq > 31 || irq < 0) { - return; - } - - irq_bit = 1U << irq; - - if (level) { - cpu->env.picsr |= irq_bit; - } else { - cpu->env.picsr &= ~irq_bit; - } - - if (cpu->env.picsr & cpu->env.picmr) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - cpu->env.picsr = 0; - } -} - -void cpu_openrisc_pic_init(OpenRISCCPU *cpu) -{ - int i; - qemu_irq *qi; - qi = qemu_allocate_irqs(openrisc_pic_cpu_handler, cpu, NR_IRQS); - - for (i = 0; i < NR_IRQS; i++) { - cpu->env.irq[i] = qi[i]; - } -} diff --git a/qemu/hw/pci-bridge/Makefile.objs b/qemu/hw/pci-bridge/Makefile.objs deleted file mode 100644 index f2adfe348..000000000 --- a/qemu/hw/pci-bridge/Makefile.objs +++ /dev/null @@ -1,7 +0,0 @@ -common-obj-y += pci_bridge_dev.o -common-obj-y += pci_expander_bridge.o -common-obj-$(CONFIG_XIO3130) += xio3130_upstream.o xio3130_downstream.o -common-obj-$(CONFIG_IOH3420) += ioh3420.o -common-obj-$(CONFIG_I82801B11) += i82801b11.o -# NewWorld PowerMac -common-obj-$(CONFIG_DEC_PCI) += dec.o diff --git a/qemu/hw/pci-bridge/dec.c b/qemu/hw/pci-bridge/dec.c deleted file mode 100644 index 840c96198..000000000 --- a/qemu/hw/pci-bridge/dec.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * QEMU DEC 21154 PCI bridge - * - * Copyright (c) 2006-2007 Fabrice Bellard - * Copyright (c) 2007 Jocelyn Mayer - * - * 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 "dec.h" -#include "hw/sysbus.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "hw/pci/pci_bridge.h" -#include "hw/pci/pci_bus.h" - -/* debug DEC */ -//#define DEBUG_DEC - -#ifdef DEBUG_DEC -#define DEC_DPRINTF(fmt, ...) \ - do { printf("DEC: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DEC_DPRINTF(fmt, ...) -#endif - -#define DEC_21154(obj) OBJECT_CHECK(DECState, (obj), TYPE_DEC_21154) - -typedef struct DECState { - PCIHostState parent_obj; -} DECState; - -static int dec_map_irq(PCIDevice *pci_dev, int irq_num) -{ - return irq_num; -} - -static void dec_pci_bridge_realize(PCIDevice *pci_dev, Error **errp) -{ - pci_bridge_initfn(pci_dev, TYPE_PCI_BUS); -} - -static void dec_21154_pci_bridge_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - 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; - k->config_write = pci_bridge_write_config; - k->is_bridge = 1; - dc->desc = "DEC 21154 PCI-PCI bridge"; - dc->reset = pci_bridge_reset; - dc->vmsd = &vmstate_pci_device; -} - -static const TypeInfo dec_21154_pci_bridge_info = { - .name = "dec-21154-p2p-bridge", - .parent = TYPE_PCI_BRIDGE, - .instance_size = sizeof(PCIBridge), - .class_init = dec_21154_pci_bridge_class_init, -}; - -PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn) -{ - PCIDevice *dev; - PCIBridge *br; - - dev = pci_create_multifunction(parent_bus, devfn, false, - "dec-21154-p2p-bridge"); - br = PCI_BRIDGE(dev); - pci_bridge_map_irq(br, "DEC 21154 PCI-PCI bridge", dec_map_irq); - qdev_init_nofail(&dev->qdev); - return pci_bridge_get_sec_bus(br); -} - -static int pci_dec_21154_device_init(SysBusDevice *dev) -{ - PCIHostState *phb; - - phb = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&phb->conf_mem, OBJECT(dev), &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&phb->data_mem, OBJECT(dev), &pci_host_data_le_ops, - dev, "pci-data-idx", 0x1000); - sysbus_init_mmio(dev, &phb->conf_mem); - sysbus_init_mmio(dev, &phb->data_mem); - return 0; -} - -static void dec_21154_pci_host_realize(PCIDevice *d, Error **errp) -{ - /* PCI2PCI bridge same values as PearPC - check this */ -} - -static void dec_21154_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = dec_21154_pci_host_realize; - k->vendor_id = PCI_VENDOR_ID_DEC; - k->device_id = PCI_DEVICE_ID_DEC_21154; - k->revision = 0x02; - k->class_id = PCI_CLASS_BRIDGE_PCI; - k->is_bridge = 1; - /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo dec_21154_pci_host_info = { - .name = "dec-21154", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = dec_21154_pci_host_class_init, -}; - -static void pci_dec_21154_device_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = pci_dec_21154_device_init; -} - -static const TypeInfo pci_dec_21154_device_info = { - .name = TYPE_DEC_21154, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(DECState), - .class_init = pci_dec_21154_device_class_init, -}; - -static void dec_register_types(void) -{ - type_register_static(&pci_dec_21154_device_info); - type_register_static(&dec_21154_pci_host_info); - type_register_static(&dec_21154_pci_bridge_info); -} - -type_init(dec_register_types) diff --git a/qemu/hw/pci-bridge/dec.h b/qemu/hw/pci-bridge/dec.h deleted file mode 100644 index 17dc0c2b0..000000000 --- a/qemu/hw/pci-bridge/dec.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef DEC_PCI_H -#define DEC_PCI_H - -#include "qemu-common.h" - -#define TYPE_DEC_21154 "dec-21154-sysbus" - -PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn); - -#endif diff --git a/qemu/hw/pci-bridge/i82801b11.c b/qemu/hw/pci-bridge/i82801b11.c deleted file mode 100644 index 2404e7eba..000000000 --- a/qemu/hw/pci-bridge/i82801b11.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2006 Fabrice Bellard - * - * 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. - */ -/* - * QEMU i82801b11 dmi-to-pci Bridge Emulation - * - * Copyright (c) 2009, 2010, 2011 - * Isaku Yamahata - * VA Linux Systems Japan K.K. - * Copyright (C) 2012 Jason Baron - * - * 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 - */ - -#include "qemu/osdep.h" -#include "hw/pci/pci.h" -#include "hw/i386/ich9.h" - - -/*****************************************************************************/ -/* ICH9 DMI-to-PCI bridge */ -#define I82801ba_SSVID_OFFSET 0x50 -#define I82801ba_SSVID_SVID 0 -#define I82801ba_SSVID_SSID 0 - -typedef struct I82801b11Bridge { - /*< private >*/ - PCIBridge parent_obj; - /*< public >*/ -} I82801b11Bridge; - -static int i82801b11_bridge_initfn(PCIDevice *d) -{ - int rc; - - pci_bridge_initfn(d, TYPE_PCI_BUS); - - rc = pci_bridge_ssvid_init(d, I82801ba_SSVID_OFFSET, - I82801ba_SSVID_SVID, I82801ba_SSVID_SSID); - if (rc < 0) { - goto err_bridge; - } - pci_config_set_prog_interface(d->config, PCI_CLASS_BRIDGE_PCI_INF_SUB); - return 0; - -err_bridge: - pci_bridge_exitfn(d); - - 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); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->is_bridge = 1; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82801BA_11; - 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); -} - -static const TypeInfo i82801b11_bridge_info = { - .name = "i82801b11-bridge", - .parent = TYPE_PCI_BRIDGE, - .instance_size = sizeof(I82801b11Bridge), - .class_init = i82801b11_bridge_class_init, -}; - -static void d2pbr_register(void) -{ - type_register_static(&i82801b11_bridge_info); -} - -type_init(d2pbr_register); diff --git a/qemu/hw/pci-bridge/ioh3420.c b/qemu/hw/pci-bridge/ioh3420.c deleted file mode 100644 index 0937fa34b..000000000 --- a/qemu/hw/pci-bridge/ioh3420.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * ioh3420.c - * Intel X58 north bridge IOH - * PCI Express root port device id 3420 - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/pci/pci_ids.h" -#include "hw/pci/msi.h" -#include "hw/pci/pcie.h" -#include "ioh3420.h" - -#define PCI_DEVICE_ID_IOH_EPORT 0x3420 /* D0:F0 express mode */ -#define PCI_DEVICE_ID_IOH_REV 0x2 -#define IOH_EP_SSVID_OFFSET 0x40 -#define IOH_EP_SSVID_SVID PCI_VENDOR_ID_INTEL -#define IOH_EP_SSVID_SSID 0 -#define IOH_EP_MSI_OFFSET 0x60 -#define IOH_EP_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_MASKBIT -#define IOH_EP_MSI_NR_VECTOR 2 -#define IOH_EP_EXP_OFFSET 0x90 -#define IOH_EP_AER_OFFSET 0x100 - -/* - * If two MSI vector are allocated, Advanced Error Interrupt Message Number - * is 1. otherwise 0. - * 17.12.5.10 RPERRSTS, 32:27 bit Advanced Error Interrupt Message Number. - */ -static uint8_t ioh3420_aer_vector(const PCIDevice *d) -{ - switch (msi_nr_vectors_allocated(d)) { - case 1: - return 0; - case 2: - return 1; - case 4: - case 8: - case 16: - case 32: - default: - break; - } - abort(); - return 0; -} - -static void ioh3420_aer_vector_update(PCIDevice *d) -{ - pcie_aer_root_set_vector(d, ioh3420_aer_vector(d)); -} - -static void ioh3420_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len) -{ - uint32_t root_cmd = - pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_ROOT_COMMAND); - - pci_bridge_write_config(d, address, val, len); - ioh3420_aer_vector_update(d); - pcie_cap_slot_write_config(d, address, val, len); - pcie_aer_write_config(d, address, val, len); - pcie_aer_root_write_config(d, address, val, len, root_cmd); -} - -static void ioh3420_reset(DeviceState *qdev) -{ - PCIDevice *d = PCI_DEVICE(qdev); - - ioh3420_aer_vector_update(d); - pcie_cap_root_reset(d); - pcie_cap_deverr_reset(d); - pcie_cap_slot_reset(d); - pcie_cap_arifwd_reset(d); - pcie_aer_root_reset(d); - pci_bridge_reset(qdev); - pci_bridge_disable_base_limit(d); -} - -static int ioh3420_initfn(PCIDevice *d) -{ - PCIEPort *p = PCIE_PORT(d); - PCIESlot *s = PCIE_SLOT(d); - int rc; - - pci_bridge_initfn(d, TYPE_PCIE_BUS); - pcie_port_init_reg(d); - - rc = pci_bridge_ssvid_init(d, IOH_EP_SSVID_OFFSET, - IOH_EP_SSVID_SVID, IOH_EP_SSVID_SSID); - if (rc < 0) { - goto err_bridge; - } - rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR, - IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, - IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); - if (rc < 0) { - goto err_bridge; - } - rc = pcie_cap_init(d, IOH_EP_EXP_OFFSET, PCI_EXP_TYPE_ROOT_PORT, p->port); - if (rc < 0) { - goto err_msi; - } - - pcie_cap_arifwd_init(d); - pcie_cap_deverr_init(d); - pcie_cap_slot_init(d, s->slot); - pcie_chassis_create(s->chassis); - rc = pcie_chassis_add_slot(s); - if (rc < 0) { - goto err_pcie_cap; - } - pcie_cap_root_init(d); - rc = pcie_aer_init(d, IOH_EP_AER_OFFSET, PCI_ERR_SIZEOF); - if (rc < 0) { - goto err; - } - pcie_aer_root_init(d); - ioh3420_aer_vector_update(d); - return 0; - -err: - pcie_chassis_del_slot(s); -err_pcie_cap: - pcie_cap_exit(d); -err_msi: - msi_uninit(d); -err_bridge: - pci_bridge_exitfn(d); - return rc; -} - -static void ioh3420_exitfn(PCIDevice *d) -{ - PCIESlot *s = PCIE_SLOT(d); - - pcie_aer_exit(d); - pcie_chassis_del_slot(s); - pcie_cap_exit(d); - msi_uninit(d); - pci_bridge_exitfn(d); -} - -static Property ioh3420_props[] = { - DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present, - QEMU_PCIE_SLTCAP_PCP_BITNR, true), - DEFINE_PROP_END_OF_LIST() -}; - -static const VMStateDescription vmstate_ioh3420 = { - .name = "ioh-3240-express-root-port", - .version_id = 1, - .minimum_version_id = 1, - .post_load = pcie_cap_slot_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCIE_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot), - VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log, - PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog), - VMSTATE_END_OF_LIST() - } -}; - -static void ioh3420_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->is_express = 1; - k->is_bridge = 1; - k->config_write = ioh3420_write_config; - k->init = ioh3420_initfn; - k->exit = ioh3420_exitfn; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_IOH_EPORT; - k->revision = PCI_DEVICE_ID_IOH_REV; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->desc = "Intel IOH device id 3420 PCIE Root Port"; - dc->reset = ioh3420_reset; - dc->vmsd = &vmstate_ioh3420; - dc->props = ioh3420_props; -} - -static const TypeInfo ioh3420_info = { - .name = "ioh3420", - .parent = TYPE_PCIE_SLOT, - .class_init = ioh3420_class_init, -}; - -static void ioh3420_register_types(void) -{ - type_register_static(&ioh3420_info); -} - -type_init(ioh3420_register_types) - -/* - * Local variables: - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 8 - * indent-tab-mode: nil - * End: - */ diff --git a/qemu/hw/pci-bridge/ioh3420.h b/qemu/hw/pci-bridge/ioh3420.h deleted file mode 100644 index ea423cb99..000000000 --- a/qemu/hw/pci-bridge/ioh3420.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef QEMU_IOH3420_H -#define QEMU_IOH3420_H - -#include "hw/pci/pcie_port.h" - -#endif /* QEMU_IOH3420_H */ diff --git a/qemu/hw/pci-bridge/pci_bridge_dev.c b/qemu/hw/pci-bridge/pci_bridge_dev.c deleted file mode 100644 index 7b582e96a..000000000 --- a/qemu/hw/pci-bridge/pci_bridge_dev.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Standard PCI Bridge Device - * - * Copyright (c) 2011 Red Hat Inc. Author: Michael S. Tsirkin - * - * http://www.pcisig.com/specifications/conventional/pci_to_pci_bridge_architecture/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/pci/pci_bridge.h" -#include "hw/pci/pci_ids.h" -#include "hw/pci/msi.h" -#include "hw/pci/shpc.h" -#include "hw/pci/slotid_cap.h" -#include "exec/memory.h" -#include "hw/pci/pci_bus.h" -#include "hw/hotplug.h" - -#define TYPE_PCI_BRIDGE_DEV "pci-bridge" -#define TYPE_PCI_BRIDGE_SEAT_DEV "pci-bridge-seat" -#define PCI_BRIDGE_DEV(obj) \ - OBJECT_CHECK(PCIBridgeDev, (obj), TYPE_PCI_BRIDGE_DEV) - -struct PCIBridgeDev { - /*< private >*/ - PCIBridge parent_obj; - /*< public >*/ - - MemoryRegion bar; - uint8_t chassis_nr; -#define PCI_BRIDGE_DEV_F_MSI_REQ 0 -#define PCI_BRIDGE_DEV_F_SHPC_REQ 1 - uint32_t flags; -}; -typedef struct PCIBridgeDev PCIBridgeDev; - -static int pci_bridge_dev_initfn(PCIDevice *dev) -{ - PCIBridge *br = PCI_BRIDGE(dev); - PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev); - int err; - - 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", - shpc_bar_size(dev)); - err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0); - if (err) { - goto shpc_error; - } - } else { - /* MSI is not applicable without SHPC */ - bridge_dev->flags &= ~(1 << PCI_BRIDGE_DEV_F_MSI_REQ); - } - err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0); - if (err) { - goto slotid_error; - } - if ((bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_MSI_REQ)) && - msi_nonbroken) { - err = msi_init(dev, 0, 1, true, true); - if (err < 0) { - goto msi_error; - } - } - if (shpc_present(dev)) { - /* TODO: spec recommends using 64 bit prefetcheable BAR. - * Check whether that works well. */ - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar); - } - return 0; -msi_error: - slotid_cap_cleanup(dev); -slotid_error: - if (shpc_present(dev)) { - shpc_cleanup(dev, &bridge_dev->bar); - } -shpc_error: - pci_bridge_exitfn(dev); - - return err; -} - -static void pci_bridge_dev_exitfn(PCIDevice *dev) -{ - PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev); - if (msi_present(dev)) { - msi_uninit(dev); - } - slotid_cap_cleanup(dev); - if (shpc_present(dev)) { - shpc_cleanup(dev, &bridge_dev->bar); - } - pci_bridge_exitfn(dev); -} - -static void pci_bridge_dev_instance_finalize(Object *obj) -{ - /* this function is idempotent and handles (PCIDevice.shpc == NULL) */ - shpc_free(PCI_DEVICE(obj)); -} - -static void pci_bridge_dev_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len) -{ - pci_bridge_write_config(d, address, val, len); - if (msi_present(d)) { - msi_write_config(d, address, val, len); - } - if (shpc_present(d)) { - shpc_cap_write_config(d, address, val, len); - } -} - -static void qdev_pci_bridge_dev_reset(DeviceState *qdev) -{ - PCIDevice *dev = PCI_DEVICE(qdev); - - pci_bridge_reset(qdev); - if (shpc_present(dev)) { - shpc_reset(dev); - } -} - -static Property pci_bridge_dev_properties[] = { - /* Note: 0 is not a legal chassis number. */ - DEFINE_PROP_UINT8(PCI_BRIDGE_DEV_PROP_CHASSIS_NR, PCIBridgeDev, chassis_nr, - 0), - DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_MSI, PCIBridgeDev, flags, - PCI_BRIDGE_DEV_F_MSI_REQ, true), - DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_SHPC, PCIBridgeDev, flags, - PCI_BRIDGE_DEV_F_SHPC_REQ, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static bool pci_device_shpc_present(void *opaque, int version_id) -{ - PCIDevice *dev = opaque; - - return shpc_present(dev); -} - -static const VMStateDescription pci_bridge_dev_vmstate = { - .name = "pci_bridge", - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, PCIBridge), - SHPC_VMSTATE(shpc, PCIDevice, pci_device_shpc_present), - VMSTATE_END_OF_LIST() - } -}; - -static void pci_bridge_dev_hotplug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); - - if (!shpc_present(pci_hotplug_dev)) { - error_setg(errp, "standard hotplug controller has been disabled for " - "this %s", TYPE_PCI_BRIDGE_DEV); - return; - } - shpc_device_hotplug_cb(hotplug_dev, dev, errp); -} - -static void pci_bridge_dev_hot_unplug_request_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, - Error **errp) -{ - PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); - - if (!shpc_present(pci_hotplug_dev)) { - error_setg(errp, "standard hotplug controller has been disabled for " - "this %s", TYPE_PCI_BRIDGE_DEV); - return; - } - shpc_device_hot_unplug_request_cb(hotplug_dev, dev, errp); -} - -static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - - k->init = pci_bridge_dev_initfn; - k->exit = pci_bridge_dev_exitfn; - k->config_write = pci_bridge_dev_write_config; - k->vendor_id = PCI_VENDOR_ID_REDHAT; - k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE; - k->class_id = PCI_CLASS_BRIDGE_PCI; - k->is_bridge = 1, - dc->desc = "Standard PCI Bridge"; - dc->reset = qdev_pci_bridge_dev_reset; - dc->props = pci_bridge_dev_properties; - dc->vmsd = &pci_bridge_dev_vmstate; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - hc->plug = pci_bridge_dev_hotplug_cb; - hc->unplug_request = pci_bridge_dev_hot_unplug_request_cb; -} - -static const TypeInfo pci_bridge_dev_info = { - .name = TYPE_PCI_BRIDGE_DEV, - .parent = TYPE_PCI_BRIDGE, - .instance_size = sizeof(PCIBridgeDev), - .class_init = pci_bridge_dev_class_init, - .instance_finalize = pci_bridge_dev_instance_finalize, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - -/* - * Multiseat bridge. Same as the standard pci bridge, only with a - * different pci id, so we can match it easily in the guest for - * automagic multiseat configuration. See docs/multiseat.txt for more. - */ -static void pci_bridge_dev_seat_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE_SEAT; - dc->desc = "Standard PCI Bridge (multiseat)"; -} - -static const TypeInfo pci_bridge_dev_seat_info = { - .name = TYPE_PCI_BRIDGE_SEAT_DEV, - .parent = TYPE_PCI_BRIDGE_DEV, - .instance_size = sizeof(PCIBridgeDev), - .class_init = pci_bridge_dev_seat_class_init, -}; - -static void pci_bridge_dev_register(void) -{ - type_register_static(&pci_bridge_dev_info); - type_register_static(&pci_bridge_dev_seat_info); -} - -type_init(pci_bridge_dev_register); diff --git a/qemu/hw/pci-bridge/pci_expander_bridge.c b/qemu/hw/pci-bridge/pci_expander_bridge.c deleted file mode 100644 index ba320bd85..000000000 --- a/qemu/hw/pci-bridge/pci_expander_bridge.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - * PCI Expander Bridge Device Emulation - * - * Copyright (C) 2015 Red Hat Inc - * - * Authors: - * Marcel Apfelbaum - * - * 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/pci/pci_bus.h" -#include "hw/pci/pci_host.h" -#include "hw/pci/pci_bus.h" -#include "hw/pci/pci_bridge.h" -#include "hw/i386/pc.h" -#include "qemu/range.h" -#include "qemu/error-report.h" -#include "sysemu/numa.h" - -#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; - /*< public >*/ - - char bus_path[8]; -} 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; - /*< public >*/ - - uint8_t bus_nr; - 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 = convert_to_pxb(bus->parent_dev); - - return pxb->bus_nr; -} - -static bool pxb_is_root(PCIBus *bus) -{ - return true; /* by definition */ -} - -static uint16_t pxb_bus_numa_node(PCIBus *bus) -{ - PXBDev *pxb = convert_to_pxb(bus->parent_dev); - - return pxb->numa_node; -} - -static void pxb_bus_class_init(ObjectClass *class, void *data) -{ - PCIBusClass *pbc = PCI_BUS_CLASS(class); - - pbc->bus_num = pxb_bus_num; - pbc->is_root = pxb_is_root; - pbc->numa_node = pxb_bus_numa_node; -} - -static const TypeInfo pxb_bus_info = { - .name = TYPE_PXB_BUS, - .parent = TYPE_PCI_BUS, - .instance_size = sizeof(PXBBus), - .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 = 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; -} - -static char *pxb_host_ofw_unit_address(const SysBusDevice *dev) -{ - const PCIHostState *pxb_host; - const PCIBus *pxb_bus; - const PXBDev *pxb_dev; - int position; - const DeviceState *pxb_dev_base; - const PCIHostState *main_host; - const SysBusDevice *main_host_sbd; - - pxb_host = PCI_HOST_BRIDGE(dev); - pxb_bus = pxb_host->bus; - pxb_dev = convert_to_pxb(pxb_bus->parent_dev); - position = g_list_index(pxb_dev_list, pxb_dev); - assert(position >= 0); - - pxb_dev_base = DEVICE(pxb_dev); - main_host = PCI_HOST_BRIDGE(pxb_dev_base->parent_bus->parent); - main_host_sbd = SYS_BUS_DEVICE(main_host); - - if (main_host_sbd->num_mmio > 0) { - return g_strdup_printf(TARGET_FMT_plx ",%x", - main_host_sbd->mmio[0].addr, position + 1); - } - if (main_host_sbd->num_pio > 0) { - return g_strdup_printf("i%04x,%x", - main_host_sbd->pio[0], position + 1); - } - return NULL; -} - -static void pxb_host_class_init(ObjectClass *class, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(class); - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(class); - PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(class); - - dc->fw_name = "pci"; - sbc->explicit_ofw_unit_address = pxb_host_ofw_unit_address; - hc->root_bus_path = pxb_host_root_bus_path; -} - -static const TypeInfo pxb_host_info = { - .name = TYPE_PXB_HOST, - .parent = TYPE_PCI_HOST_BRIDGE, - .class_init = pxb_host_class_init, -}; - -/* - * Registers the PXB bus as a child of the i440fx root bus. - * - * Returns 0 on successs, -1 if i440fx host was not - * found or the bus number is already in use. - */ -static int pxb_register_bus(PCIDevice *dev, PCIBus *pxb_bus) -{ - PCIBus *bus = dev->bus; - int pxb_bus_num = pci_bus_num(pxb_bus); - - if (bus->parent_dev) { - error_report("PXB devices can be attached only to root bus."); - return -1; - } - - QLIST_FOREACH(bus, &bus->child, sibling) { - if (pci_bus_num(bus) == pxb_bus_num) { - error_report("Bus %d is already in use.", pxb_bus_num); - return -1; - } - } - QLIST_INSERT_HEAD(&dev->bus->child, pxb_bus, sibling); - - return 0; -} - -static int pxb_map_irq_fn(PCIDevice *pci_dev, int pin) -{ - PCIDevice *pxb = pci_dev->bus->parent_dev; - - /* - * The bios does not index the pxb slot number when - * it computes the IRQ because it resides on bus 0 - * and not on the current bus. - * However QEMU routes the irq through bus 0 and adds - * the pxb slot to the IRQ computation of the PXB - * device. - * - * Synchronize between bios and QEMU by canceling - * pxb's effect. - */ - return pin - PCI_SLOT(pxb->devfn); -} - -static gint pxb_compare(gconstpointer a, gconstpointer b) -{ - const PXBDev *pxb_a = a, *pxb_b = b; - - return pxb_a->bus_nr < pxb_b->bus_nr ? -1 : - pxb_a->bus_nr > pxb_b->bus_nr ? 1 : - 0; -} - -static int pxb_dev_init_common(PCIDevice *dev, bool pcie) -{ - PXBDev *pxb = convert_to_pxb(dev); - DeviceState *ds, *bds = NULL; - PCIBus *bus; - const char *dev_name = NULL; - - if (pxb->numa_node != NUMA_NODE_UNASSIGNED && - pxb->numa_node >= nb_numa_nodes) { - error_report("Illegal numa node %d.", pxb->numa_node); - return -EINVAL; - } - - if (dev->qdev.id && *dev->qdev.id) { - dev_name = dev->qdev.id; - } - - ds = qdev_create(NULL, TYPE_PXB_HOST); - 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; - - PCI_HOST_BRIDGE(ds)->bus = bus; - - if (pxb_register_bus(dev, bus)) { - goto err_register_bus; - } - - qdev_init_nofail(ds); - if (bds) { - qdev_init_nofail(bds); - } - - pci_word_test_and_set_mask(dev->config + PCI_STATUS, - PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); - pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_HOST); - - 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 = 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 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(), -}; - -static void pxb_dev_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = pxb_dev_initfn; - k->exit = pxb_dev_exitfn; - k->vendor_id = PCI_VENDOR_ID_REDHAT; - k->device_id = PCI_DEVICE_ID_REDHAT_PXB; - k->class_id = PCI_CLASS_BRIDGE_HOST; - - dc->desc = "PCI Expander Bridge"; - dc->props = pxb_dev_properties; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); -} - -static const TypeInfo pxb_dev_info = { - .name = TYPE_PXB_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PXBDev), - .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 deleted file mode 100644 index cf1ee63ab..000000000 --- a/qemu/hw/pci-bridge/xio3130_downstream.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * x3130_downstream.c - * TI X3130 pci express downstream port switch - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/pci/pci_ids.h" -#include "hw/pci/msi.h" -#include "hw/pci/pcie.h" -#include "xio3130_downstream.h" - -#define PCI_DEVICE_ID_TI_XIO3130D 0x8233 /* downstream port */ -#define XIO3130_REVISION 0x1 -#define XIO3130_MSI_OFFSET 0x70 -#define XIO3130_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_64BIT -#define XIO3130_MSI_NR_VECTOR 1 -#define XIO3130_SSVID_OFFSET 0x80 -#define XIO3130_SSVID_SVID 0 -#define XIO3130_SSVID_SSID 0 -#define XIO3130_EXP_OFFSET 0x90 -#define XIO3130_AER_OFFSET 0x100 - -static void xio3130_downstream_write_config(PCIDevice *d, uint32_t address, - uint32_t val, int len) -{ - pci_bridge_write_config(d, address, val, len); - pcie_cap_flr_write_config(d, address, val, len); - pcie_cap_slot_write_config(d, address, val, len); - pcie_aer_write_config(d, address, val, len); -} - -static void xio3130_downstream_reset(DeviceState *qdev) -{ - PCIDevice *d = PCI_DEVICE(qdev); - - pcie_cap_deverr_reset(d); - pcie_cap_slot_reset(d); - pcie_cap_arifwd_reset(d); - pci_bridge_reset(qdev); -} - -static int xio3130_downstream_initfn(PCIDevice *d) -{ - PCIEPort *p = PCIE_PORT(d); - PCIESlot *s = PCIE_SLOT(d); - int rc; - - pci_bridge_initfn(d, TYPE_PCIE_BUS); - pcie_port_init_reg(d); - - rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR, - XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, - XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); - if (rc < 0) { - goto err_bridge; - } - rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET, - XIO3130_SSVID_SVID, XIO3130_SSVID_SSID); - if (rc < 0) { - goto err_bridge; - } - rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_DOWNSTREAM, - p->port); - if (rc < 0) { - goto err_msi; - } - pcie_cap_flr_init(d); - pcie_cap_deverr_init(d); - pcie_cap_slot_init(d, s->slot); - pcie_chassis_create(s->chassis); - rc = pcie_chassis_add_slot(s); - if (rc < 0) { - goto err_pcie_cap; - } - pcie_cap_arifwd_init(d); - rc = pcie_aer_init(d, XIO3130_AER_OFFSET, PCI_ERR_SIZEOF); - if (rc < 0) { - goto err; - } - - return 0; - -err: - pcie_chassis_del_slot(s); -err_pcie_cap: - pcie_cap_exit(d); -err_msi: - msi_uninit(d); -err_bridge: - pci_bridge_exitfn(d); - return rc; -} - -static void xio3130_downstream_exitfn(PCIDevice *d) -{ - PCIESlot *s = PCIE_SLOT(d); - - pcie_aer_exit(d); - pcie_chassis_del_slot(s); - pcie_cap_exit(d); - msi_uninit(d); - pci_bridge_exitfn(d); -} - -PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction, - const char *bus_name, pci_map_irq_fn map_irq, - uint8_t port, uint8_t chassis, - uint16_t slot) -{ - PCIDevice *d; - PCIBridge *br; - DeviceState *qdev; - - d = pci_create_multifunction(bus, devfn, multifunction, - "xio3130-downstream"); - if (!d) { - return NULL; - } - br = PCI_BRIDGE(d); - - qdev = DEVICE(d); - pci_bridge_map_irq(br, bus_name, map_irq); - qdev_prop_set_uint8(qdev, "port", port); - qdev_prop_set_uint8(qdev, "chassis", chassis); - qdev_prop_set_uint16(qdev, "slot", slot); - qdev_init_nofail(qdev); - - return PCIE_SLOT(d); -} - -static Property xio3130_downstream_props[] = { - DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present, - QEMU_PCIE_SLTCAP_PCP_BITNR, true), - DEFINE_PROP_END_OF_LIST() -}; - -static const VMStateDescription vmstate_xio3130_downstream = { - .name = "xio3130-express-downstream-port", - .version_id = 1, - .minimum_version_id = 1, - .post_load = pcie_cap_slot_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCIE_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot), - VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log, - PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog), - VMSTATE_END_OF_LIST() - } -}; - -static void xio3130_downstream_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->is_express = 1; - k->is_bridge = 1; - k->config_write = xio3130_downstream_write_config; - k->init = xio3130_downstream_initfn; - k->exit = xio3130_downstream_exitfn; - k->vendor_id = PCI_VENDOR_ID_TI; - k->device_id = PCI_DEVICE_ID_TI_XIO3130D; - k->revision = XIO3130_REVISION; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->desc = "TI X3130 Downstream Port of PCI Express Switch"; - dc->reset = xio3130_downstream_reset; - dc->vmsd = &vmstate_xio3130_downstream; - dc->props = xio3130_downstream_props; -} - -static const TypeInfo xio3130_downstream_info = { - .name = "xio3130-downstream", - .parent = TYPE_PCIE_SLOT, - .class_init = xio3130_downstream_class_init, -}; - -static void xio3130_downstream_register_types(void) -{ - type_register_static(&xio3130_downstream_info); -} - -type_init(xio3130_downstream_register_types) - -/* - * Local variables: - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 8 - * indent-tab-mode: nil - * End: - */ diff --git a/qemu/hw/pci-bridge/xio3130_downstream.h b/qemu/hw/pci-bridge/xio3130_downstream.h deleted file mode 100644 index 8426d9ffa..000000000 --- a/qemu/hw/pci-bridge/xio3130_downstream.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef QEMU_XIO3130_DOWNSTREAM_H -#define QEMU_XIO3130_DOWNSTREAM_H - -#include "hw/pci/pcie_port.h" - -PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction, - const char *bus_name, pci_map_irq_fn map_irq, - uint8_t port, uint8_t chassis, - uint16_t slot); - -#endif /* QEMU_XIO3130_DOWNSTREAM_H */ diff --git a/qemu/hw/pci-bridge/xio3130_upstream.c b/qemu/hw/pci-bridge/xio3130_upstream.c deleted file mode 100644 index 164ef58c4..000000000 --- a/qemu/hw/pci-bridge/xio3130_upstream.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * xio3130_upstream.c - * TI X3130 pci express upstream port switch - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/pci/pci_ids.h" -#include "hw/pci/msi.h" -#include "hw/pci/pcie.h" -#include "xio3130_upstream.h" - -#define PCI_DEVICE_ID_TI_XIO3130U 0x8232 /* upstream port */ -#define XIO3130_REVISION 0x2 -#define XIO3130_MSI_OFFSET 0x70 -#define XIO3130_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_64BIT -#define XIO3130_MSI_NR_VECTOR 1 -#define XIO3130_SSVID_OFFSET 0x80 -#define XIO3130_SSVID_SVID 0 -#define XIO3130_SSVID_SSID 0 -#define XIO3130_EXP_OFFSET 0x90 -#define XIO3130_AER_OFFSET 0x100 - -static void xio3130_upstream_write_config(PCIDevice *d, uint32_t address, - uint32_t val, int len) -{ - pci_bridge_write_config(d, address, val, len); - pcie_cap_flr_write_config(d, address, val, len); - pcie_aer_write_config(d, address, val, len); -} - -static void xio3130_upstream_reset(DeviceState *qdev) -{ - PCIDevice *d = PCI_DEVICE(qdev); - - pci_bridge_reset(qdev); - pcie_cap_deverr_reset(d); -} - -static int xio3130_upstream_initfn(PCIDevice *d) -{ - PCIEPort *p = PCIE_PORT(d); - int rc; - - pci_bridge_initfn(d, TYPE_PCIE_BUS); - pcie_port_init_reg(d); - - rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR, - XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, - XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); - if (rc < 0) { - goto err_bridge; - } - rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET, - XIO3130_SSVID_SVID, XIO3130_SSVID_SSID); - if (rc < 0) { - goto err_bridge; - } - rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_UPSTREAM, - p->port); - if (rc < 0) { - goto err_msi; - } - pcie_cap_flr_init(d); - pcie_cap_deverr_init(d); - rc = pcie_aer_init(d, XIO3130_AER_OFFSET, PCI_ERR_SIZEOF); - if (rc < 0) { - goto err; - } - - return 0; - -err: - pcie_cap_exit(d); -err_msi: - msi_uninit(d); -err_bridge: - pci_bridge_exitfn(d); - return rc; -} - -static void xio3130_upstream_exitfn(PCIDevice *d) -{ - pcie_aer_exit(d); - pcie_cap_exit(d); - msi_uninit(d); - pci_bridge_exitfn(d); -} - -PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction, - const char *bus_name, pci_map_irq_fn map_irq, - uint8_t port) -{ - PCIDevice *d; - PCIBridge *br; - DeviceState *qdev; - - d = pci_create_multifunction(bus, devfn, multifunction, "x3130-upstream"); - if (!d) { - return NULL; - } - br = PCI_BRIDGE(d); - - qdev = DEVICE(d); - pci_bridge_map_irq(br, bus_name, map_irq); - qdev_prop_set_uint8(qdev, "port", port); - qdev_init_nofail(qdev); - - return PCIE_PORT(d); -} - -static const VMStateDescription vmstate_xio3130_upstream = { - .name = "xio3130-express-upstream-port", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PCIE_DEVICE(parent_obj.parent_obj, PCIEPort), - VMSTATE_STRUCT(parent_obj.parent_obj.exp.aer_log, PCIEPort, 0, - vmstate_pcie_aer_log, PCIEAERLog), - VMSTATE_END_OF_LIST() - } -}; - -static void xio3130_upstream_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->is_express = 1; - k->is_bridge = 1; - k->config_write = xio3130_upstream_write_config; - k->init = xio3130_upstream_initfn; - k->exit = xio3130_upstream_exitfn; - k->vendor_id = PCI_VENDOR_ID_TI; - k->device_id = PCI_DEVICE_ID_TI_XIO3130U; - k->revision = XIO3130_REVISION; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->desc = "TI X3130 Upstream Port of PCI Express Switch"; - dc->reset = xio3130_upstream_reset; - dc->vmsd = &vmstate_xio3130_upstream; -} - -static const TypeInfo xio3130_upstream_info = { - .name = "x3130-upstream", - .parent = TYPE_PCIE_PORT, - .class_init = xio3130_upstream_class_init, -}; - -static void xio3130_upstream_register_types(void) -{ - type_register_static(&xio3130_upstream_info); -} - -type_init(xio3130_upstream_register_types) - - -/* - * Local variables: - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 8 - * indent-tab-mode: nil - * End: - */ diff --git a/qemu/hw/pci-bridge/xio3130_upstream.h b/qemu/hw/pci-bridge/xio3130_upstream.h deleted file mode 100644 index 08c1d5f75..000000000 --- a/qemu/hw/pci-bridge/xio3130_upstream.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef QEMU_XIO3130_UPSTREAM_H -#define QEMU_XIO3130_UPSTREAM_H - -#include "hw/pci/pcie_port.h" - -PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction, - const char *bus_name, pci_map_irq_fn map_irq, - uint8_t port); - -#endif /* QEMU_XIO3130_H */ diff --git a/qemu/hw/pci-host/Makefile.objs b/qemu/hw/pci-host/Makefile.objs deleted file mode 100644 index 45f1f0eba..000000000 --- a/qemu/hw/pci-host/Makefile.objs +++ /dev/null @@ -1,18 +0,0 @@ -common-obj-y += pam.o - -# PPC devices -common-obj-$(CONFIG_PREP_PCI) += prep.o -common-obj-$(CONFIG_GRACKLE_PCI) += grackle.o -# NewWorld PowerMac -common-obj-$(CONFIG_UNIN_PCI) += uninorth.o -# PowerPC E500 boards -common-obj-$(CONFIG_PPCE500_PCI) += ppce500.o - -# ARM devices -common-obj-$(CONFIG_VERSATILE_PCI) += versatile.o - -common-obj-$(CONFIG_PCI_APB) += apb.o -common-obj-$(CONFIG_FULONG) += bonito.o -common-obj-$(CONFIG_PCI_PIIX) += piix.o -common-obj-$(CONFIG_PCI_Q35) += q35.o -common-obj-$(CONFIG_PCI_GENERIC) += gpex.o diff --git a/qemu/hw/pci-host/apb.c b/qemu/hw/pci-host/apb.c deleted file mode 100644 index aaef7bb3a..000000000 --- a/qemu/hw/pci-host/apb.c +++ /dev/null @@ -1,871 +0,0 @@ -/* - * QEMU Ultrasparc APB PCI host - * - * Copyright (c) 2006 Fabrice Bellard - * Copyright (c) 2012,2013 Artyom Tarasenko - * - * 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. - */ - -/* XXX This file and most of its contents are somewhat misnamed. The - 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" -#include "hw/pci/pci_bridge.h" -#include "hw/pci/pci_bus.h" -#include "hw/pci-host/apb.h" -#include "sysemu/sysemu.h" -#include "exec/address-spaces.h" - -/* debug APB */ -//#define DEBUG_APB - -#ifdef DEBUG_APB -#define APB_DPRINTF(fmt, ...) \ -do { printf("APB: " fmt , ## __VA_ARGS__); } while (0) -#else -#define APB_DPRINTF(fmt, ...) -#endif - -/* debug IOMMU */ -//#define DEBUG_IOMMU - -#ifdef DEBUG_IOMMU -#define IOMMU_DPRINTF(fmt, ...) \ -do { printf("IOMMU: " fmt , ## __VA_ARGS__); } while (0) -#else -#define IOMMU_DPRINTF(fmt, ...) -#endif - -/* - * Chipset docs: - * PBM: "UltraSPARC IIi User's Manual", - * http://www.sun.com/processors/manuals/805-0087.pdf - * - * APB: "Advanced PCI Bridge (APB) User's Manual", - * http://www.sun.com/processors/manuals/805-1251.pdf - */ - -#define PBM_PCI_IMR_MASK 0x7fffffff -#define PBM_PCI_IMR_ENABLED 0x80000000 - -#define POR (1U << 31) -#define SOFT_POR (1U << 30) -#define SOFT_XIR (1U << 29) -#define BTN_POR (1U << 28) -#define BTN_XIR (1U << 27) -#define RESET_MASK 0xf8000000 -#define RESET_WCMASK 0x98000000 -#define RESET_WMASK 0x60000000 - -#define MAX_IVEC 0x40 -#define NO_IRQ_REQUEST (MAX_IVEC + 1) - -#define IOMMU_PAGE_SIZE_8K (1ULL << 13) -#define IOMMU_PAGE_MASK_8K (~(IOMMU_PAGE_SIZE_8K - 1)) -#define IOMMU_PAGE_SIZE_64K (1ULL << 16) -#define IOMMU_PAGE_MASK_64K (~(IOMMU_PAGE_SIZE_64K - 1)) - -#define IOMMU_NREGS 3 - -#define IOMMU_CTRL 0x0 -#define IOMMU_CTRL_TBW_SIZE (1ULL << 2) -#define IOMMU_CTRL_MMU_EN (1ULL) - -#define IOMMU_CTRL_TSB_SHIFT 16 - -#define IOMMU_BASE 0x8 -#define IOMMU_FLUSH 0x10 - -#define IOMMU_TTE_DATA_V (1ULL << 63) -#define IOMMU_TTE_DATA_SIZE (1ULL << 61) -#define IOMMU_TTE_DATA_W (1ULL << 1) - -#define IOMMU_TTE_PHYS_MASK_8K 0x1ffffffe000ULL -#define IOMMU_TTE_PHYS_MASK_64K 0x1ffffff8000ULL - -#define IOMMU_TSB_8K_OFFSET_MASK_8M 0x00000000007fe000ULL -#define IOMMU_TSB_8K_OFFSET_MASK_16M 0x0000000000ffe000ULL -#define IOMMU_TSB_8K_OFFSET_MASK_32M 0x0000000001ffe000ULL -#define IOMMU_TSB_8K_OFFSET_MASK_64M 0x0000000003ffe000ULL -#define IOMMU_TSB_8K_OFFSET_MASK_128M 0x0000000007ffe000ULL -#define IOMMU_TSB_8K_OFFSET_MASK_256M 0x000000000fffe000ULL -#define IOMMU_TSB_8K_OFFSET_MASK_512M 0x000000001fffe000ULL -#define IOMMU_TSB_8K_OFFSET_MASK_1G 0x000000003fffe000ULL - -#define IOMMU_TSB_64K_OFFSET_MASK_64M 0x0000000003ff0000ULL -#define IOMMU_TSB_64K_OFFSET_MASK_128M 0x0000000007ff0000ULL -#define IOMMU_TSB_64K_OFFSET_MASK_256M 0x000000000fff0000ULL -#define IOMMU_TSB_64K_OFFSET_MASK_512M 0x000000001fff0000ULL -#define IOMMU_TSB_64K_OFFSET_MASK_1G 0x000000003fff0000ULL -#define IOMMU_TSB_64K_OFFSET_MASK_2G 0x000000007fff0000ULL - -typedef struct IOMMUState { - AddressSpace iommu_as; - MemoryRegion iommu; - - uint64_t regs[IOMMU_NREGS]; -} IOMMUState; - -#define TYPE_APB "pbm" - -#define APB_DEVICE(obj) \ - OBJECT_CHECK(APBState, (obj), TYPE_APB) - -typedef struct APBState { - PCIHostState parent_obj; - - MemoryRegion apb_config; - MemoryRegion pci_config; - MemoryRegion pci_mmio; - MemoryRegion pci_ioport; - uint64_t pci_irq_in; - IOMMUState iommu; - uint32_t pci_control[16]; - uint32_t pci_irq_map[8]; - uint32_t pci_err_irq_map[4]; - uint32_t obio_irq_map[32]; - qemu_irq *pbm_irqs; - qemu_irq *ivec_irqs; - unsigned int irq_request; - uint32_t reset_control; - unsigned int nr_resets; -} APBState; - -static inline void pbm_set_request(APBState *s, unsigned int irq_num) -{ - APB_DPRINTF("%s: request irq %d\n", __func__, irq_num); - - s->irq_request = irq_num; - qemu_set_irq(s->ivec_irqs[irq_num], 1); -} - -static inline void pbm_check_irqs(APBState *s) -{ - - unsigned int i; - - /* Previous request is not acknowledged, resubmit */ - if (s->irq_request != NO_IRQ_REQUEST) { - pbm_set_request(s, s->irq_request); - return; - } - /* no request pending */ - if (s->pci_irq_in == 0ULL) { - return; - } - for (i = 0; i < 32; i++) { - if (s->pci_irq_in & (1ULL << i)) { - if (s->pci_irq_map[i >> 2] & PBM_PCI_IMR_ENABLED) { - pbm_set_request(s, i); - return; - } - } - } - for (i = 32; i < 64; i++) { - if (s->pci_irq_in & (1ULL << i)) { - if (s->obio_irq_map[i - 32] & PBM_PCI_IMR_ENABLED) { - pbm_set_request(s, i); - break; - } - } - } -} - -static inline void pbm_clear_request(APBState *s, unsigned int irq_num) -{ - APB_DPRINTF("%s: clear request irq %d\n", __func__, irq_num); - qemu_set_irq(s->ivec_irqs[irq_num], 0); - s->irq_request = NO_IRQ_REQUEST; -} - -static AddressSpace *pbm_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) -{ - IOMMUState *is = opaque; - - return &is->iommu_as; -} - -/* Called from RCU critical section */ -static IOMMUTLBEntry pbm_translate_iommu(MemoryRegion *iommu, hwaddr addr, - bool is_write) -{ - IOMMUState *is = container_of(iommu, IOMMUState, iommu); - hwaddr baseaddr, offset; - uint64_t tte; - uint32_t tsbsize; - IOMMUTLBEntry ret = { - .target_as = &address_space_memory, - .iova = 0, - .translated_addr = 0, - .addr_mask = ~(hwaddr)0, - .perm = IOMMU_NONE, - }; - - if (!(is->regs[IOMMU_CTRL >> 3] & IOMMU_CTRL_MMU_EN)) { - /* IOMMU disabled, passthrough using standard 8K page */ - ret.iova = addr & IOMMU_PAGE_MASK_8K; - ret.translated_addr = addr; - ret.addr_mask = IOMMU_PAGE_MASK_8K; - ret.perm = IOMMU_RW; - - return ret; - } - - baseaddr = is->regs[IOMMU_BASE >> 3]; - tsbsize = (is->regs[IOMMU_CTRL >> 3] >> IOMMU_CTRL_TSB_SHIFT) & 0x7; - - if (is->regs[IOMMU_CTRL >> 3] & IOMMU_CTRL_TBW_SIZE) { - /* 64K */ - switch (tsbsize) { - case 0: - offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_64M) >> 13; - break; - case 1: - offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_128M) >> 13; - break; - case 2: - offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_256M) >> 13; - break; - case 3: - offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_512M) >> 13; - break; - case 4: - offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_1G) >> 13; - break; - case 5: - offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_2G) >> 13; - break; - default: - /* Not implemented, error */ - return ret; - } - } else { - /* 8K */ - switch (tsbsize) { - case 0: - offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_8M) >> 10; - break; - case 1: - offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_16M) >> 10; - break; - case 2: - offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_32M) >> 10; - break; - case 3: - offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_64M) >> 10; - break; - case 4: - offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_128M) >> 10; - break; - case 5: - offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_256M) >> 10; - break; - case 6: - offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_512M) >> 10; - break; - case 7: - offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_1G) >> 10; - break; - } - } - - tte = address_space_ldq_be(&address_space_memory, baseaddr + offset, - MEMTXATTRS_UNSPECIFIED, NULL); - - if (!(tte & IOMMU_TTE_DATA_V)) { - /* Invalid mapping */ - return ret; - } - - if (tte & IOMMU_TTE_DATA_W) { - /* Writeable */ - ret.perm = IOMMU_RW; - } else { - ret.perm = IOMMU_RO; - } - - /* Extract phys */ - if (tte & IOMMU_TTE_DATA_SIZE) { - /* 64K */ - ret.iova = addr & IOMMU_PAGE_MASK_64K; - ret.translated_addr = tte & IOMMU_TTE_PHYS_MASK_64K; - ret.addr_mask = (IOMMU_PAGE_SIZE_64K - 1); - } else { - /* 8K */ - ret.iova = addr & IOMMU_PAGE_MASK_8K; - ret.translated_addr = tte & IOMMU_TTE_PHYS_MASK_8K; - ret.addr_mask = (IOMMU_PAGE_SIZE_8K - 1); - } - - return ret; -} - -static MemoryRegionIOMMUOps pbm_iommu_ops = { - .translate = pbm_translate_iommu, -}; - -static void iommu_config_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - IOMMUState *is = opaque; - - IOMMU_DPRINTF("IOMMU config write: 0x%" HWADDR_PRIx " val: %" PRIx64 - " size: %d\n", addr, val, size); - - switch (addr) { - case IOMMU_CTRL: - if (size == 4) { - is->regs[IOMMU_CTRL >> 3] &= 0xffffffffULL; - is->regs[IOMMU_CTRL >> 3] |= val << 32; - } else { - is->regs[IOMMU_CTRL >> 3] = val; - } - break; - case IOMMU_CTRL + 0x4: - is->regs[IOMMU_CTRL >> 3] &= 0xffffffff00000000ULL; - is->regs[IOMMU_CTRL >> 3] |= val & 0xffffffffULL; - break; - case IOMMU_BASE: - if (size == 4) { - is->regs[IOMMU_BASE >> 3] &= 0xffffffffULL; - is->regs[IOMMU_BASE >> 3] |= val << 32; - } else { - is->regs[IOMMU_BASE >> 3] = val; - } - break; - case IOMMU_BASE + 0x4: - is->regs[IOMMU_BASE >> 3] &= 0xffffffff00000000ULL; - is->regs[IOMMU_BASE >> 3] |= val & 0xffffffffULL; - break; - case IOMMU_FLUSH: - case IOMMU_FLUSH + 0x4: - break; - default: - qemu_log_mask(LOG_UNIMP, - "apb iommu: Unimplemented register write " - "reg 0x%" HWADDR_PRIx " size 0x%x value 0x%" PRIx64 "\n", - addr, size, val); - break; - } -} - -static uint64_t iommu_config_read(void *opaque, hwaddr addr, unsigned size) -{ - IOMMUState *is = opaque; - uint64_t val; - - switch (addr) { - case IOMMU_CTRL: - if (size == 4) { - val = is->regs[IOMMU_CTRL >> 3] >> 32; - } else { - val = is->regs[IOMMU_CTRL >> 3]; - } - break; - case IOMMU_CTRL + 0x4: - val = is->regs[IOMMU_CTRL >> 3] & 0xffffffffULL; - break; - case IOMMU_BASE: - if (size == 4) { - val = is->regs[IOMMU_BASE >> 3] >> 32; - } else { - val = is->regs[IOMMU_BASE >> 3]; - } - break; - case IOMMU_BASE + 0x4: - val = is->regs[IOMMU_BASE >> 3] & 0xffffffffULL; - break; - case IOMMU_FLUSH: - case IOMMU_FLUSH + 0x4: - val = 0; - break; - default: - qemu_log_mask(LOG_UNIMP, - "apb iommu: Unimplemented register read " - "reg 0x%" HWADDR_PRIx " size 0x%x\n", - addr, size); - val = 0; - break; - } - - IOMMU_DPRINTF("IOMMU config read: 0x%" HWADDR_PRIx " val: %" PRIx64 - " size: %d\n", addr, val, size); - - return val; -} - -static void apb_config_writel (void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - APBState *s = opaque; - IOMMUState *is = &s->iommu; - - APB_DPRINTF("%s: addr " TARGET_FMT_plx " val %" PRIx64 "\n", __func__, addr, val); - - switch (addr & 0xffff) { - case 0x30 ... 0x4f: /* DMA error registers */ - /* XXX: not implemented yet */ - break; - case 0x200 ... 0x217: /* IOMMU */ - iommu_config_write(is, (addr & 0x1f), val, size); - break; - case 0xc00 ... 0xc3f: /* PCI interrupt control */ - if (addr & 4) { - unsigned int ino = (addr & 0x3f) >> 3; - s->pci_irq_map[ino] &= PBM_PCI_IMR_MASK; - s->pci_irq_map[ino] |= val & ~PBM_PCI_IMR_MASK; - if ((s->irq_request == ino) && !(val & ~PBM_PCI_IMR_MASK)) { - pbm_clear_request(s, ino); - } - pbm_check_irqs(s); - } - break; - case 0x1000 ... 0x107f: /* OBIO interrupt control */ - if (addr & 4) { - unsigned int ino = ((addr & 0xff) >> 3); - s->obio_irq_map[ino] &= PBM_PCI_IMR_MASK; - s->obio_irq_map[ino] |= val & ~PBM_PCI_IMR_MASK; - if ((s->irq_request == (ino | 0x20)) - && !(val & ~PBM_PCI_IMR_MASK)) { - pbm_clear_request(s, ino | 0x20); - } - pbm_check_irqs(s); - } - break; - case 0x1400 ... 0x14ff: /* PCI interrupt clear */ - if (addr & 4) { - unsigned int ino = (addr & 0xff) >> 5; - if ((s->irq_request / 4) == ino) { - pbm_clear_request(s, s->irq_request); - pbm_check_irqs(s); - } - } - break; - case 0x1800 ... 0x1860: /* OBIO interrupt clear */ - if (addr & 4) { - unsigned int ino = ((addr & 0xff) >> 3) | 0x20; - if (s->irq_request == ino) { - pbm_clear_request(s, ino); - pbm_check_irqs(s); - } - } - break; - case 0x2000 ... 0x202f: /* PCI control */ - s->pci_control[(addr & 0x3f) >> 2] = val; - break; - case 0xf020 ... 0xf027: /* Reset control */ - if (addr & 4) { - val &= RESET_MASK; - s->reset_control &= ~(val & RESET_WCMASK); - s->reset_control |= val & RESET_WMASK; - if (val & SOFT_POR) { - s->nr_resets = 0; - qemu_system_reset_request(); - } else if (val & SOFT_XIR) { - qemu_system_reset_request(); - } - } - break; - case 0x5000 ... 0x51cf: /* PIO/DMA diagnostics */ - case 0xa400 ... 0xa67f: /* IOMMU diagnostics */ - case 0xa800 ... 0xa80f: /* Interrupt diagnostics */ - case 0xf000 ... 0xf01f: /* FFB config, memory control */ - /* we don't care */ - default: - break; - } -} - -static uint64_t apb_config_readl (void *opaque, - hwaddr addr, unsigned size) -{ - APBState *s = opaque; - IOMMUState *is = &s->iommu; - uint32_t val; - - switch (addr & 0xffff) { - case 0x30 ... 0x4f: /* DMA error registers */ - val = 0; - /* XXX: not implemented yet */ - break; - case 0x200 ... 0x217: /* IOMMU */ - val = iommu_config_read(is, (addr & 0x1f), size); - break; - case 0xc00 ... 0xc3f: /* PCI interrupt control */ - if (addr & 4) { - val = s->pci_irq_map[(addr & 0x3f) >> 3]; - } else { - val = 0; - } - break; - case 0x1000 ... 0x107f: /* OBIO interrupt control */ - if (addr & 4) { - val = s->obio_irq_map[(addr & 0xff) >> 3]; - } else { - val = 0; - } - break; - case 0x1080 ... 0x108f: /* PCI bus error */ - if (addr & 4) { - val = s->pci_err_irq_map[(addr & 0xf) >> 3]; - } else { - val = 0; - } - break; - case 0x2000 ... 0x202f: /* PCI control */ - val = s->pci_control[(addr & 0x3f) >> 2]; - break; - case 0xf020 ... 0xf027: /* Reset control */ - if (addr & 4) { - val = s->reset_control; - } else { - val = 0; - } - break; - case 0x5000 ... 0x51cf: /* PIO/DMA diagnostics */ - case 0xa400 ... 0xa67f: /* IOMMU diagnostics */ - case 0xa800 ... 0xa80f: /* Interrupt diagnostics */ - case 0xf000 ... 0xf01f: /* FFB config, memory control */ - /* we don't care */ - default: - val = 0; - break; - } - APB_DPRINTF("%s: addr " TARGET_FMT_plx " -> %x\n", __func__, addr, val); - - return val; -} - -static const MemoryRegionOps apb_config_ops = { - .read = apb_config_readl, - .write = apb_config_writel, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void apb_pci_config_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - APBState *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); - - val = qemu_bswap_len(val, size); - APB_DPRINTF("%s: addr " TARGET_FMT_plx " val %" PRIx64 "\n", __func__, addr, val); - pci_data_write(phb->bus, addr, val, size); -} - -static uint64_t apb_pci_config_read(void *opaque, hwaddr addr, - unsigned size) -{ - uint32_t ret; - APBState *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); - - ret = pci_data_read(phb->bus, addr, size); - ret = qemu_bswap_len(ret, size); - APB_DPRINTF("%s: addr " TARGET_FMT_plx " -> %x\n", __func__, addr, ret); - return ret; -} - -/* The APB host has an IRQ line for each IRQ line of each slot. */ -static int pci_apb_map_irq(PCIDevice *pci_dev, int irq_num) -{ - return ((pci_dev->devfn & 0x18) >> 1) + irq_num; -} - -static int pci_pbm_map_irq(PCIDevice *pci_dev, int irq_num) -{ - int bus_offset; - if (pci_dev->devfn & 1) - bus_offset = 16; - else - bus_offset = 0; - return (bus_offset + (PCI_SLOT(pci_dev->devfn) << 2) + irq_num) & 0x1f; -} - -static void pci_apb_set_irq(void *opaque, int irq_num, int level) -{ - APBState *s = opaque; - - APB_DPRINTF("%s: set irq_in %d level %d\n", __func__, irq_num, level); - /* PCI IRQ map onto the first 32 INO. */ - if (irq_num < 32) { - if (level) { - s->pci_irq_in |= 1ULL << irq_num; - if (s->pci_irq_map[irq_num >> 2] & PBM_PCI_IMR_ENABLED) { - pbm_set_request(s, irq_num); - } - } else { - s->pci_irq_in &= ~(1ULL << irq_num); - } - } else { - /* OBIO IRQ map onto the next 32 INO. */ - if (level) { - APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level); - s->pci_irq_in |= 1ULL << irq_num; - if ((s->irq_request == NO_IRQ_REQUEST) - && (s->obio_irq_map[irq_num - 32] & PBM_PCI_IMR_ENABLED)) { - pbm_set_request(s, irq_num); - } - } else { - s->pci_irq_in &= ~(1ULL << irq_num); - } - } -} - -static int apb_pci_bridge_initfn(PCIDevice *dev) -{ - pci_bridge_initfn(dev, TYPE_PCI_BUS); - - /* - * command register: - * According to PCI bridge spec, after reset - * bus master bit is off - * memory space enable bit is off - * According to manual (805-1251.pdf). - * the reset value should be zero unless the boot pin is tied high - * (which is true) and thus it should be PCI_COMMAND_MEMORY. - */ - pci_set_word(dev->config + PCI_COMMAND, - PCI_COMMAND_MEMORY); - pci_set_word(dev->config + PCI_STATUS, - PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ | - PCI_STATUS_DEVSEL_MEDIUM); - return 0; -} - -PCIBus *pci_apb_init(hwaddr special_base, - hwaddr mem_base, - qemu_irq *ivec_irqs, PCIBus **bus2, PCIBus **bus3, - qemu_irq **pbm_irqs) -{ - DeviceState *dev; - SysBusDevice *s; - PCIHostState *phb; - APBState *d; - IOMMUState *is; - PCIDevice *pci_dev; - PCIBridge *br; - - /* Ultrasparc PBM main bus */ - dev = qdev_create(NULL, TYPE_APB); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - /* apb_config */ - sysbus_mmio_map(s, 0, special_base); - /* PCI configuration space */ - sysbus_mmio_map(s, 1, special_base + 0x1000000ULL); - /* pci_ioport */ - sysbus_mmio_map(s, 2, special_base + 0x2000000ULL); - d = APB_DEVICE(dev); - - memory_region_init(&d->pci_mmio, OBJECT(s), "pci-mmio", 0x100000000ULL); - memory_region_add_subregion(get_system_memory(), mem_base, &d->pci_mmio); - - phb = PCI_HOST_BRIDGE(dev); - phb->bus = pci_register_bus(DEVICE(phb), "pci", - pci_apb_set_irq, pci_pbm_map_irq, d, - &d->pci_mmio, - get_system_io(), - 0, 32, TYPE_PCI_BUS); - - *pbm_irqs = d->pbm_irqs; - d->ivec_irqs = ivec_irqs; - - pci_create_simple(phb->bus, 0, "pbm-pci"); - - /* APB IOMMU */ - is = &d->iommu; - memset(is, 0, sizeof(IOMMUState)); - - memory_region_init_iommu(&is->iommu, OBJECT(dev), &pbm_iommu_ops, - "iommu-apb", UINT64_MAX); - address_space_init(&is->iommu_as, &is->iommu, "pbm-as"); - pci_setup_iommu(phb->bus, pbm_pci_dma_iommu, is); - - /* APB secondary busses */ - pci_dev = pci_create_multifunction(phb->bus, PCI_DEVFN(1, 0), true, - "pbm-bridge"); - br = PCI_BRIDGE(pci_dev); - pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 1", - pci_apb_map_irq); - qdev_init_nofail(&pci_dev->qdev); - *bus2 = pci_bridge_get_sec_bus(br); - - pci_dev = pci_create_multifunction(phb->bus, PCI_DEVFN(1, 1), true, - "pbm-bridge"); - br = PCI_BRIDGE(pci_dev); - pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 2", - pci_apb_map_irq); - qdev_init_nofail(&pci_dev->qdev); - *bus3 = pci_bridge_get_sec_bus(br); - - return phb->bus; -} - -static void pci_pbm_reset(DeviceState *d) -{ - unsigned int i; - APBState *s = APB_DEVICE(d); - - for (i = 0; i < 8; i++) { - s->pci_irq_map[i] &= PBM_PCI_IMR_MASK; - } - for (i = 0; i < 32; i++) { - s->obio_irq_map[i] &= PBM_PCI_IMR_MASK; - } - - s->irq_request = NO_IRQ_REQUEST; - s->pci_irq_in = 0ULL; - - if (s->nr_resets++ == 0) { - /* Power on reset */ - s->reset_control = POR; - } -} - -static const MemoryRegionOps pci_config_ops = { - .read = apb_pci_config_read, - .write = apb_pci_config_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int pci_pbm_init_device(SysBusDevice *dev) -{ - APBState *s; - unsigned int i; - - s = APB_DEVICE(dev); - for (i = 0; i < 8; i++) { - s->pci_irq_map[i] = (0x1f << 6) | (i << 2); - } - for (i = 0; i < 2; i++) { - s->pci_err_irq_map[i] = (0x1f << 6) | 0x30; - } - for (i = 0; i < 32; i++) { - s->obio_irq_map[i] = ((0x1f << 6) | 0x20) + i; - } - s->pbm_irqs = qemu_allocate_irqs(pci_apb_set_irq, s, MAX_IVEC); - s->irq_request = NO_IRQ_REQUEST; - s->pci_irq_in = 0ULL; - - /* apb_config */ - memory_region_init_io(&s->apb_config, OBJECT(s), &apb_config_ops, s, - "apb-config", 0x10000); - /* at region 0 */ - sysbus_init_mmio(dev, &s->apb_config); - - memory_region_init_io(&s->pci_config, OBJECT(s), &pci_config_ops, s, - "apb-pci-config", 0x1000000); - /* at region 1 */ - sysbus_init_mmio(dev, &s->pci_config); - - /* pci_ioport */ - memory_region_init_alias(&s->pci_ioport, OBJECT(s), "apb-pci-ioport", - get_system_io(), 0, 0x10000); - /* at region 2 */ - sysbus_init_mmio(dev, &s->pci_ioport); - - return 0; -} - -static void pbm_pci_host_realize(PCIDevice *d, Error **errp) -{ - pci_set_word(d->config + PCI_COMMAND, - PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - pci_set_word(d->config + PCI_STATUS, - PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ | - PCI_STATUS_DEVSEL_MEDIUM); -} - -static void pbm_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = pbm_pci_host_realize; - k->vendor_id = PCI_VENDOR_ID_SUN; - k->device_id = PCI_DEVICE_ID_SUN_SABRE; - k->class_id = PCI_CLASS_BRIDGE_HOST; - /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo pbm_pci_host_info = { - .name = "pbm-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = pbm_pci_host_class_init, -}; - -static void pbm_host_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = pci_pbm_init_device; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->reset = pci_pbm_reset; -} - -static const TypeInfo pbm_host_info = { - .name = TYPE_APB, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(APBState), - .class_init = pbm_host_class_init, -}; - -static void pbm_pci_bridge_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = apb_pci_bridge_initfn; - k->exit = pci_bridge_exitfn; - k->vendor_id = PCI_VENDOR_ID_SUN; - k->device_id = PCI_DEVICE_ID_SUN_SIMBA; - k->revision = 0x11; - k->config_write = pci_bridge_write_config; - k->is_bridge = 1; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->reset = pci_bridge_reset; - dc->vmsd = &vmstate_pci_device; -} - -static const TypeInfo pbm_pci_bridge_info = { - .name = "pbm-bridge", - .parent = TYPE_PCI_BRIDGE, - .class_init = pbm_pci_bridge_class_init, -}; - -static void pbm_register_types(void) -{ - type_register_static(&pbm_host_info); - type_register_static(&pbm_pci_host_info); - type_register_static(&pbm_pci_bridge_info); -} - -type_init(pbm_register_types) diff --git a/qemu/hw/pci-host/bonito.c b/qemu/hw/pci-host/bonito.c deleted file mode 100644 index 1999ece59..000000000 --- a/qemu/hw/pci-host/bonito.c +++ /dev/null @@ -1,858 +0,0 @@ -/* - * bonito north bridge support - * - * Copyright (c) 2008 yajin (yajin@vm-kernel.org) - * Copyright (c) 2010 Huacai Chen (zltjiangshi@gmail.com) - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -/* - * fulong 2e mini pc has a bonito north bridge. - */ - -/* what is the meaning of devfn in qemu and IDSEL in bonito northbridge? - * - * devfn pci_slot<<3 + funno - * one pci bus can have 32 devices and each device can have 8 functions. - * - * In bonito north bridge, pci slot = IDSEL bit - 12. - * For example, PCI_IDSEL_VIA686B = 17, - * pci slot = 17-12=5 - * - * so - * VT686B_FUN0's devfn = (5<<3)+0 - * VT686B_FUN1's devfn = (5<<3)+1 - * - * qemu also uses pci address for north bridge to access pci config register. - * bus_no [23:16] - * dev_no [15:11] - * fun_no [10:8] - * reg_no [7:2] - * - * so function bonito_sbridge_pciaddr for the translation from - * north bridge address to pci address. - */ - -#include "qemu/osdep.h" - -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "hw/i386/pc.h" -#include "hw/mips/mips.h" -#include "hw/pci/pci_host.h" -#include "sysemu/sysemu.h" -#include "exec/address-spaces.h" - -//#define DEBUG_BONITO - -#ifdef DEBUG_BONITO -#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__) -#else -#define DPRINTF(fmt, ...) -#endif - -/* from linux soure code. include/asm-mips/mips-boards/bonito64.h*/ -#define BONITO_BOOT_BASE 0x1fc00000 -#define BONITO_BOOT_SIZE 0x00100000 -#define BONITO_BOOT_TOP (BONITO_BOOT_BASE+BONITO_BOOT_SIZE-1) -#define BONITO_FLASH_BASE 0x1c000000 -#define BONITO_FLASH_SIZE 0x03000000 -#define BONITO_FLASH_TOP (BONITO_FLASH_BASE+BONITO_FLASH_SIZE-1) -#define BONITO_SOCKET_BASE 0x1f800000 -#define BONITO_SOCKET_SIZE 0x00400000 -#define BONITO_SOCKET_TOP (BONITO_SOCKET_BASE+BONITO_SOCKET_SIZE-1) -#define BONITO_REG_BASE 0x1fe00000 -#define BONITO_REG_SIZE 0x00040000 -#define BONITO_REG_TOP (BONITO_REG_BASE+BONITO_REG_SIZE-1) -#define BONITO_DEV_BASE 0x1ff00000 -#define BONITO_DEV_SIZE 0x00100000 -#define BONITO_DEV_TOP (BONITO_DEV_BASE+BONITO_DEV_SIZE-1) -#define BONITO_PCILO_BASE 0x10000000 -#define BONITO_PCILO_BASE_VA 0xb0000000 -#define BONITO_PCILO_SIZE 0x0c000000 -#define BONITO_PCILO_TOP (BONITO_PCILO_BASE+BONITO_PCILO_SIZE-1) -#define BONITO_PCILO0_BASE 0x10000000 -#define BONITO_PCILO1_BASE 0x14000000 -#define BONITO_PCILO2_BASE 0x18000000 -#define BONITO_PCIHI_BASE 0x20000000 -#define BONITO_PCIHI_SIZE 0x20000000 -#define BONITO_PCIHI_TOP (BONITO_PCIHI_BASE+BONITO_PCIHI_SIZE-1) -#define BONITO_PCIIO_BASE 0x1fd00000 -#define BONITO_PCIIO_BASE_VA 0xbfd00000 -#define BONITO_PCIIO_SIZE 0x00010000 -#define BONITO_PCIIO_TOP (BONITO_PCIIO_BASE+BONITO_PCIIO_SIZE-1) -#define BONITO_PCICFG_BASE 0x1fe80000 -#define BONITO_PCICFG_SIZE 0x00080000 -#define BONITO_PCICFG_TOP (BONITO_PCICFG_BASE+BONITO_PCICFG_SIZE-1) - - -#define BONITO_PCICONFIGBASE 0x00 -#define BONITO_REGBASE 0x100 - -#define BONITO_PCICONFIG_BASE (BONITO_PCICONFIGBASE+BONITO_REG_BASE) -#define BONITO_PCICONFIG_SIZE (0x100) - -#define BONITO_INTERNAL_REG_BASE (BONITO_REGBASE+BONITO_REG_BASE) -#define BONITO_INTERNAL_REG_SIZE (0x70) - -#define BONITO_SPCICONFIG_BASE (BONITO_PCICFG_BASE) -#define BONITO_SPCICONFIG_SIZE (BONITO_PCICFG_SIZE) - - - -/* 1. Bonito h/w Configuration */ -/* Power on register */ - -#define BONITO_BONPONCFG (0x00 >> 2) /* 0x100 */ -#define BONITO_BONGENCFG_OFFSET 0x4 -#define BONITO_BONGENCFG (BONITO_BONGENCFG_OFFSET>>2) /*0x104 */ - -/* 2. IO & IDE configuration */ -#define BONITO_IODEVCFG (0x08 >> 2) /* 0x108 */ - -/* 3. IO & IDE configuration */ -#define BONITO_SDCFG (0x0c >> 2) /* 0x10c */ - -/* 4. PCI address map control */ -#define BONITO_PCIMAP (0x10 >> 2) /* 0x110 */ -#define BONITO_PCIMEMBASECFG (0x14 >> 2) /* 0x114 */ -#define BONITO_PCIMAP_CFG (0x18 >> 2) /* 0x118 */ - -/* 5. ICU & GPIO regs */ -/* GPIO Regs - r/w */ -#define BONITO_GPIODATA_OFFSET 0x1c -#define BONITO_GPIODATA (BONITO_GPIODATA_OFFSET >> 2) /* 0x11c */ -#define BONITO_GPIOIE (0x20 >> 2) /* 0x120 */ - -/* ICU Configuration Regs - r/w */ -#define BONITO_INTEDGE (0x24 >> 2) /* 0x124 */ -#define BONITO_INTSTEER (0x28 >> 2) /* 0x128 */ -#define BONITO_INTPOL (0x2c >> 2) /* 0x12c */ - -/* ICU Enable Regs - IntEn & IntISR are r/o. */ -#define BONITO_INTENSET (0x30 >> 2) /* 0x130 */ -#define BONITO_INTENCLR (0x34 >> 2) /* 0x134 */ -#define BONITO_INTEN (0x38 >> 2) /* 0x138 */ -#define BONITO_INTISR (0x3c >> 2) /* 0x13c */ - -/* PCI mail boxes */ -#define BONITO_PCIMAIL0_OFFSET 0x40 -#define BONITO_PCIMAIL1_OFFSET 0x44 -#define BONITO_PCIMAIL2_OFFSET 0x48 -#define BONITO_PCIMAIL3_OFFSET 0x4c -#define BONITO_PCIMAIL0 (0x40 >> 2) /* 0x140 */ -#define BONITO_PCIMAIL1 (0x44 >> 2) /* 0x144 */ -#define BONITO_PCIMAIL2 (0x48 >> 2) /* 0x148 */ -#define BONITO_PCIMAIL3 (0x4c >> 2) /* 0x14c */ - -/* 6. PCI cache */ -#define BONITO_PCICACHECTRL (0x50 >> 2) /* 0x150 */ -#define BONITO_PCICACHETAG (0x54 >> 2) /* 0x154 */ -#define BONITO_PCIBADADDR (0x58 >> 2) /* 0x158 */ -#define BONITO_PCIMSTAT (0x5c >> 2) /* 0x15c */ - -/* 7. other*/ -#define BONITO_TIMECFG (0x60 >> 2) /* 0x160 */ -#define BONITO_CPUCFG (0x64 >> 2) /* 0x164 */ -#define BONITO_DQCFG (0x68 >> 2) /* 0x168 */ -#define BONITO_MEMSIZE (0x6C >> 2) /* 0x16c */ - -#define BONITO_REGS (0x70 >> 2) - -/* PCI config for south bridge. type 0 */ -#define BONITO_PCICONF_IDSEL_MASK 0xfffff800 /* [31:11] */ -#define BONITO_PCICONF_IDSEL_OFFSET 11 -#define BONITO_PCICONF_FUN_MASK 0x700 /* [10:8] */ -#define BONITO_PCICONF_FUN_OFFSET 8 -#define BONITO_PCICONF_REG_MASK 0xFC -#define BONITO_PCICONF_REG_OFFSET 0 - - -/* idsel BIT = pci slot number +12 */ -#define PCI_SLOT_BASE 12 -#define PCI_IDSEL_VIA686B_BIT (17) -#define PCI_IDSEL_VIA686B (1<> 2; - - DPRINTF("bonito_writel "TARGET_FMT_plx" val %x saddr %x\n", addr, val, saddr); - switch (saddr) { - case BONITO_BONPONCFG: - case BONITO_IODEVCFG: - case BONITO_SDCFG: - case BONITO_PCIMAP: - case BONITO_PCIMEMBASECFG: - case BONITO_PCIMAP_CFG: - case BONITO_GPIODATA: - case BONITO_GPIOIE: - case BONITO_INTEDGE: - case BONITO_INTSTEER: - case BONITO_INTPOL: - case BONITO_PCIMAIL0: - case BONITO_PCIMAIL1: - case BONITO_PCIMAIL2: - case BONITO_PCIMAIL3: - case BONITO_PCICACHECTRL: - case BONITO_PCICACHETAG: - case BONITO_PCIBADADDR: - case BONITO_PCIMSTAT: - case BONITO_TIMECFG: - case BONITO_CPUCFG: - case BONITO_DQCFG: - case BONITO_MEMSIZE: - s->regs[saddr] = val; - break; - case BONITO_BONGENCFG: - if (!(s->regs[saddr] & 0x04) && (val & 0x04)) { - reset = 1; /* bit 2 jump from 0 to 1 cause reset */ - } - s->regs[saddr] = val; - if (reset) { - qemu_system_reset_request(); - } - break; - case BONITO_INTENSET: - s->regs[BONITO_INTENSET] = val; - s->regs[BONITO_INTEN] |= val; - break; - case BONITO_INTENCLR: - s->regs[BONITO_INTENCLR] = val; - s->regs[BONITO_INTEN] &= ~val; - break; - case BONITO_INTEN: - case BONITO_INTISR: - DPRINTF("write to readonly bonito register %x\n", saddr); - break; - default: - DPRINTF("write to unknown bonito register %x\n", saddr); - break; - } -} - -static uint64_t bonito_readl(void *opaque, hwaddr addr, - unsigned size) -{ - PCIBonitoState *s = opaque; - uint32_t saddr; - - saddr = addr >> 2; - - DPRINTF("bonito_readl "TARGET_FMT_plx"\n", addr); - switch (saddr) { - case BONITO_INTISR: - return s->regs[saddr]; - default: - return s->regs[saddr]; - } -} - -static const MemoryRegionOps bonito_ops = { - .read = bonito_readl, - .write = bonito_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void bonito_pciconf_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - - DPRINTF("bonito_pciconf_writel "TARGET_FMT_plx" val %x\n", addr, val); - d->config_write(d, addr, val, 4); -} - -static uint64_t bonito_pciconf_readl(void *opaque, hwaddr addr, - unsigned size) -{ - - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - - DPRINTF("bonito_pciconf_readl "TARGET_FMT_plx"\n", addr); - return d->config_read(d, addr, 4); -} - -/* north bridge PCI configure space. 0x1fe0 0000 - 0x1fe0 00ff */ - -static const MemoryRegionOps bonito_pciconf_ops = { - .read = bonito_pciconf_readl, - .write = bonito_pciconf_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static uint64_t bonito_ldma_readl(void *opaque, hwaddr addr, - unsigned size) -{ - uint32_t val; - PCIBonitoState *s = opaque; - - if (addr >= sizeof(s->bonldma)) { - return 0; - } - - val = ((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)]; - - return val; -} - -static void bonito_ldma_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - PCIBonitoState *s = opaque; - - if (addr >= sizeof(s->bonldma)) { - return; - } - - ((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)] = val & 0xffffffff; -} - -static const MemoryRegionOps bonito_ldma_ops = { - .read = bonito_ldma_readl, - .write = bonito_ldma_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static uint64_t bonito_cop_readl(void *opaque, hwaddr addr, - unsigned size) -{ - uint32_t val; - PCIBonitoState *s = opaque; - - if (addr >= sizeof(s->boncop)) { - return 0; - } - - val = ((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)]; - - return val; -} - -static void bonito_cop_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - PCIBonitoState *s = opaque; - - if (addr >= sizeof(s->boncop)) { - return; - } - - ((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)] = val & 0xffffffff; -} - -static const MemoryRegionOps bonito_cop_ops = { - .read = bonito_cop_readl, - .write = bonito_cop_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static uint32_t bonito_sbridge_pciaddr(void *opaque, hwaddr addr) -{ - PCIBonitoState *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t cfgaddr; - uint32_t idsel; - uint32_t devno; - uint32_t funno; - uint32_t regno; - uint32_t pciaddr; - - /* support type0 pci config */ - if ((s->regs[BONITO_PCIMAP_CFG] & 0x10000) != 0x0) { - return 0xffffffff; - } - - cfgaddr = addr & 0xffff; - cfgaddr |= (s->regs[BONITO_PCIMAP_CFG] & 0xffff) << 16; - - idsel = (cfgaddr & BONITO_PCICONF_IDSEL_MASK) >> BONITO_PCICONF_IDSEL_OFFSET; - devno = ctz32(idsel); - funno = (cfgaddr & BONITO_PCICONF_FUN_MASK) >> BONITO_PCICONF_FUN_OFFSET; - regno = (cfgaddr & BONITO_PCICONF_REG_MASK) >> BONITO_PCICONF_REG_OFFSET; - - if (idsel == 0) { - fprintf(stderr, "error in bonito pci config address " TARGET_FMT_plx - ",pcimap_cfg=%x\n", addr, s->regs[BONITO_PCIMAP_CFG]); - exit(1); - } - pciaddr = PCI_ADDR(pci_bus_num(phb->bus), devno, funno, regno); - DPRINTF("cfgaddr %x pciaddr %x busno %x devno %d funno %d regno %d\n", - cfgaddr, pciaddr, pci_bus_num(phb->bus), devno, funno, regno); - - return pciaddr; -} - -static void bonito_spciconf_writeb(void *opaque, hwaddr addr, - uint32_t val) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t pciaddr; - uint16_t status; - - DPRINTF("bonito_spciconf_writeb "TARGET_FMT_plx" val %x\n", addr, val); - pciaddr = bonito_sbridge_pciaddr(s, addr); - - if (pciaddr == 0xffffffff) { - return; - } - - /* set the pci address in s->config_reg */ - phb->config_reg = (pciaddr) | (1u << 31); - pci_data_write(phb->bus, phb->config_reg, val & 0xff, 1); - - /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(d->config + PCI_STATUS); - status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(d->config + PCI_STATUS, status); -} - -static void bonito_spciconf_writew(void *opaque, hwaddr addr, - uint32_t val) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t pciaddr; - uint16_t status; - - DPRINTF("bonito_spciconf_writew "TARGET_FMT_plx" val %x\n", addr, val); - assert((addr & 0x1) == 0); - - pciaddr = bonito_sbridge_pciaddr(s, addr); - - if (pciaddr == 0xffffffff) { - return; - } - - /* set the pci address in s->config_reg */ - phb->config_reg = (pciaddr) | (1u << 31); - pci_data_write(phb->bus, phb->config_reg, val, 2); - - /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(d->config + PCI_STATUS); - status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(d->config + PCI_STATUS, status); -} - -static void bonito_spciconf_writel(void *opaque, hwaddr addr, - uint32_t val) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t pciaddr; - uint16_t status; - - DPRINTF("bonito_spciconf_writel "TARGET_FMT_plx" val %x\n", addr, val); - assert((addr & 0x3) == 0); - - pciaddr = bonito_sbridge_pciaddr(s, addr); - - if (pciaddr == 0xffffffff) { - return; - } - - /* set the pci address in s->config_reg */ - phb->config_reg = (pciaddr) | (1u << 31); - pci_data_write(phb->bus, phb->config_reg, val, 4); - - /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(d->config + PCI_STATUS); - status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(d->config + PCI_STATUS, status); -} - -static uint32_t bonito_spciconf_readb(void *opaque, hwaddr addr) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t pciaddr; - uint16_t status; - - DPRINTF("bonito_spciconf_readb "TARGET_FMT_plx"\n", addr); - pciaddr = bonito_sbridge_pciaddr(s, addr); - - if (pciaddr == 0xffffffff) { - return 0xff; - } - - /* set the pci address in s->config_reg */ - phb->config_reg = (pciaddr) | (1u << 31); - - /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(d->config + PCI_STATUS); - status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(d->config + PCI_STATUS, status); - - return pci_data_read(phb->bus, phb->config_reg, 1); -} - -static uint32_t bonito_spciconf_readw(void *opaque, hwaddr addr) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t pciaddr; - uint16_t status; - - DPRINTF("bonito_spciconf_readw "TARGET_FMT_plx"\n", addr); - assert((addr & 0x1) == 0); - - pciaddr = bonito_sbridge_pciaddr(s, addr); - - if (pciaddr == 0xffffffff) { - return 0xffff; - } - - /* set the pci address in s->config_reg */ - phb->config_reg = (pciaddr) | (1u << 31); - - /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(d->config + PCI_STATUS); - status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(d->config + PCI_STATUS, status); - - return pci_data_read(phb->bus, phb->config_reg, 2); -} - -static uint32_t bonito_spciconf_readl(void *opaque, hwaddr addr) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t pciaddr; - uint16_t status; - - DPRINTF("bonito_spciconf_readl "TARGET_FMT_plx"\n", addr); - assert((addr & 0x3) == 0); - - pciaddr = bonito_sbridge_pciaddr(s, addr); - - if (pciaddr == 0xffffffff) { - return 0xffffffff; - } - - /* set the pci address in s->config_reg */ - phb->config_reg = (pciaddr) | (1u << 31); - - /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(d->config + PCI_STATUS); - status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(d->config + PCI_STATUS, status); - - return pci_data_read(phb->bus, phb->config_reg, 4); -} - -/* south bridge PCI configure space. 0x1fe8 0000 - 0x1fef ffff */ -static const MemoryRegionOps bonito_spciconf_ops = { - .old_mmio = { - .read = { - bonito_spciconf_readb, - bonito_spciconf_readw, - bonito_spciconf_readl, - }, - .write = { - bonito_spciconf_writeb, - bonito_spciconf_writew, - bonito_spciconf_writel, - }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -#define BONITO_IRQ_BASE 32 - -static void pci_bonito_set_irq(void *opaque, int irq_num, int level) -{ - BonitoState *s = opaque; - qemu_irq *pic = s->pic; - PCIBonitoState *bonito_state = s->pci_dev; - int internal_irq = irq_num - BONITO_IRQ_BASE; - - if (bonito_state->regs[BONITO_INTEDGE] & (1 << internal_irq)) { - qemu_irq_pulse(*pic); - } else { /* level triggered */ - if (bonito_state->regs[BONITO_INTPOL] & (1 << internal_irq)) { - qemu_irq_raise(*pic); - } else { - qemu_irq_lower(*pic); - } - } -} - -/* map the original irq (0~3) to bonito irq (16~47, but 16~31 are unused) */ -static int pci_bonito_map_irq(PCIDevice * pci_dev, int irq_num) -{ - int slot; - - slot = (pci_dev->devfn >> 3); - - switch (slot) { - case 5: /* FULONG2E_VIA_SLOT, SouthBridge, IDE, USB, ACPI, AC97, MC97 */ - return irq_num % 4 + BONITO_IRQ_BASE; - case 6: /* FULONG2E_ATI_SLOT, VGA */ - return 4 + BONITO_IRQ_BASE; - case 7: /* FULONG2E_RTL_SLOT, RTL8139 */ - return 5 + BONITO_IRQ_BASE; - case 8 ... 12: /* PCI slot 1 to 4 */ - return (slot - 8 + irq_num) + 6 + BONITO_IRQ_BASE; - default: /* Unknown device, don't do any translation */ - return irq_num; - } -} - -static void bonito_reset(void *opaque) -{ - PCIBonitoState *s = opaque; - - /* set the default value of north bridge registers */ - - s->regs[BONITO_BONPONCFG] = 0xc40; - s->regs[BONITO_BONGENCFG] = 0x1384; - s->regs[BONITO_IODEVCFG] = 0x2bff8010; - s->regs[BONITO_SDCFG] = 0x255e0091; - - s->regs[BONITO_GPIODATA] = 0x1ff; - s->regs[BONITO_GPIOIE] = 0x1ff; - s->regs[BONITO_DQCFG] = 0x8; - s->regs[BONITO_MEMSIZE] = 0x10000000; - s->regs[BONITO_PCIMAP] = 0x6140; -} - -static const VMStateDescription vmstate_bonito = { - .name = "Bonito", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, PCIBonitoState), - VMSTATE_END_OF_LIST() - } -}; - -static int bonito_pcihost_initfn(SysBusDevice *dev) -{ - PCIHostState *phb = PCI_HOST_BRIDGE(dev); - - phb->bus = pci_register_bus(DEVICE(dev), "pci", - pci_bonito_set_irq, pci_bonito_map_irq, dev, - get_system_memory(), get_system_io(), - 0x28, 32, TYPE_PCI_BUS); - - return 0; -} - -static void bonito_realize(PCIDevice *dev, Error **errp) -{ - PCIBonitoState *s = PCI_BONITO(dev); - SysBusDevice *sysbus = SYS_BUS_DEVICE(s->pcihost); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - - /* Bonito North Bridge, built on FPGA, VENDOR_ID/DEVICE_ID are "undefined" */ - pci_config_set_prog_interface(dev->config, 0x00); - - /* set the north bridge register mapping */ - memory_region_init_io(&s->iomem, OBJECT(s), &bonito_ops, s, - "north-bridge-register", BONITO_INTERNAL_REG_SIZE); - sysbus_init_mmio(sysbus, &s->iomem); - sysbus_mmio_map(sysbus, 0, BONITO_INTERNAL_REG_BASE); - - /* set the north bridge pci configure mapping */ - memory_region_init_io(&phb->conf_mem, OBJECT(s), &bonito_pciconf_ops, s, - "north-bridge-pci-config", BONITO_PCICONFIG_SIZE); - sysbus_init_mmio(sysbus, &phb->conf_mem); - sysbus_mmio_map(sysbus, 1, BONITO_PCICONFIG_BASE); - - /* set the south bridge pci configure mapping */ - memory_region_init_io(&phb->data_mem, OBJECT(s), &bonito_spciconf_ops, s, - "south-bridge-pci-config", BONITO_SPCICONFIG_SIZE); - sysbus_init_mmio(sysbus, &phb->data_mem); - sysbus_mmio_map(sysbus, 2, BONITO_SPCICONFIG_BASE); - - memory_region_init_io(&s->iomem_ldma, OBJECT(s), &bonito_ldma_ops, s, - "ldma", 0x100); - sysbus_init_mmio(sysbus, &s->iomem_ldma); - sysbus_mmio_map(sysbus, 3, 0xbfe00200); - - memory_region_init_io(&s->iomem_cop, OBJECT(s), &bonito_cop_ops, s, - "cop", 0x100); - sysbus_init_mmio(sysbus, &s->iomem_cop); - sysbus_mmio_map(sysbus, 4, 0xbfe00300); - - /* Map PCI IO Space 0x1fd0 0000 - 0x1fd1 0000 */ - memory_region_init_alias(&s->bonito_pciio, OBJECT(s), "isa_mmio", - get_system_io(), 0, BONITO_PCIIO_SIZE); - sysbus_init_mmio(sysbus, &s->bonito_pciio); - sysbus_mmio_map(sysbus, 5, BONITO_PCIIO_BASE); - - /* add pci local io mapping */ - memory_region_init_alias(&s->bonito_localio, OBJECT(s), "isa_mmio", - get_system_io(), 0, BONITO_DEV_SIZE); - sysbus_init_mmio(sysbus, &s->bonito_localio); - sysbus_mmio_map(sysbus, 6, BONITO_DEV_BASE); - - /* set the default value of north bridge pci config */ - pci_set_word(dev->config + PCI_COMMAND, 0x0000); - pci_set_word(dev->config + PCI_STATUS, 0x0000); - pci_set_word(dev->config + PCI_SUBSYSTEM_VENDOR_ID, 0x0000); - pci_set_word(dev->config + PCI_SUBSYSTEM_ID, 0x0000); - - pci_set_byte(dev->config + PCI_INTERRUPT_LINE, 0x00); - pci_set_byte(dev->config + PCI_INTERRUPT_PIN, 0x01); - pci_set_byte(dev->config + PCI_MIN_GNT, 0x3c); - pci_set_byte(dev->config + PCI_MAX_LAT, 0x00); - - qemu_register_reset(bonito_reset, s); -} - -PCIBus *bonito_init(qemu_irq *pic) -{ - DeviceState *dev; - BonitoState *pcihost; - PCIHostState *phb; - PCIBonitoState *s; - PCIDevice *d; - - dev = qdev_create(NULL, TYPE_BONITO_PCI_HOST_BRIDGE); - phb = PCI_HOST_BRIDGE(dev); - pcihost = BONITO_PCI_HOST_BRIDGE(dev); - pcihost->pic = pic; - qdev_init_nofail(dev); - - /* set the pcihost pointer before bonito_initfn is called */ - 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)); - - return phb->bus; -} - -static void bonito_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = bonito_realize; - k->vendor_id = 0xdf53; - k->device_id = 0x00d5; - k->revision = 0x01; - k->class_id = PCI_CLASS_BRIDGE_HOST; - dc->desc = "Host bridge"; - dc->vmsd = &vmstate_bonito; - /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo bonito_info = { - .name = TYPE_PCI_BONITO, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIBonitoState), - .class_init = bonito_class_init, -}; - -static void bonito_pcihost_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = bonito_pcihost_initfn; -} - -static const TypeInfo bonito_pcihost_info = { - .name = TYPE_BONITO_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(BonitoState), - .class_init = bonito_pcihost_class_init, -}; - -static void bonito_register_types(void) -{ - type_register_static(&bonito_pcihost_info); - type_register_static(&bonito_info); -} - -type_init(bonito_register_types) diff --git a/qemu/hw/pci-host/gpex.c b/qemu/hw/pci-host/gpex.c deleted file mode 100644 index 66055ee5c..000000000 --- a/qemu/hw/pci-host/gpex.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * QEMU Generic PCI Express Bridge Emulation - * - * Copyright (C) 2015 Alexander Graf - * - * Code loosely based on q35.c. - * - * 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. - * - * Check out these documents for more information on the device: - * - * 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" - -/**************************************************************************** - * GPEX host - */ - -static void gpex_set_irq(void *opaque, int irq_num, int level) -{ - GPEXHost *s = opaque; - - qemu_set_irq(s->irq[irq_num], level); -} - -static void gpex_host_realize(DeviceState *dev, Error **errp) -{ - PCIHostState *pci = PCI_HOST_BRIDGE(dev); - GPEXHost *s = GPEX_HOST(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - PCIExpressHost *pex = PCIE_HOST_BRIDGE(dev); - int i; - - pcie_host_mmcfg_init(pex, PCIE_MMCFG_SIZE_MAX); - memory_region_init(&s->io_mmio, OBJECT(s), "gpex_mmio", UINT64_MAX); - memory_region_init(&s->io_ioport, OBJECT(s), "gpex_ioport", 64 * 1024); - - sysbus_init_mmio(sbd, &pex->mmio); - sysbus_init_mmio(sbd, &s->io_mmio); - sysbus_init_mmio(sbd, &s->io_ioport); - for (i = 0; i < GPEX_NUM_IRQS; i++) { - sysbus_init_irq(sbd, &s->irq[i]); - } - - pci->bus = pci_register_bus(dev, "pcie.0", gpex_set_irq, - pci_swizzle_map_irq_fn, s, &s->io_mmio, - &s->io_ioport, 0, 4, TYPE_PCIE_BUS); - - qdev_set_parent_bus(DEVICE(&s->gpex_root), BUS(pci->bus)); - qdev_init_nofail(DEVICE(&s->gpex_root)); -} - -static const char *gpex_host_root_bus_path(PCIHostState *host_bridge, - PCIBus *rootbus) -{ - return "0000:00"; -} - -static void gpex_host_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); - - hc->root_bus_path = gpex_host_root_bus_path; - dc->realize = gpex_host_realize; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->fw_name = "pci"; -} - -static void gpex_host_initfn(Object *obj) -{ - GPEXHost *s = GPEX_HOST(obj); - GPEXRootState *root = &s->gpex_root; - - object_initialize(root, sizeof(*root), TYPE_GPEX_ROOT_DEVICE); - object_property_add_child(obj, "gpex_root", OBJECT(root), NULL); - qdev_prop_set_uint32(DEVICE(root), "addr", PCI_DEVFN(0, 0)); - qdev_prop_set_bit(DEVICE(root), "multifunction", false); -} - -static const TypeInfo gpex_host_info = { - .name = TYPE_GPEX_HOST, - .parent = TYPE_PCIE_HOST_BRIDGE, - .instance_size = sizeof(GPEXHost), - .instance_init = gpex_host_initfn, - .class_init = gpex_host_class_init, -}; - -/**************************************************************************** - * GPEX Root D0:F0 - */ - -static const VMStateDescription vmstate_gpex_root = { - .name = "gpex_root", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, GPEXRootState), - VMSTATE_END_OF_LIST() - } -}; - -static void gpex_root_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->desc = "QEMU generic PCIe host bridge"; - dc->vmsd = &vmstate_gpex_root; - k->vendor_id = PCI_VENDOR_ID_REDHAT; - k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_HOST; - k->revision = 0; - k->class_id = PCI_CLASS_BRIDGE_HOST; - /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo gpex_root_info = { - .name = TYPE_GPEX_ROOT_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(GPEXRootState), - .class_init = gpex_root_class_init, -}; - -static void gpex_register(void) -{ - type_register_static(&gpex_root_info); - type_register_static(&gpex_host_info); -} - -type_init(gpex_register) diff --git a/qemu/hw/pci-host/grackle.c b/qemu/hw/pci-host/grackle.c deleted file mode 100644 index 8f9121615..000000000 --- a/qemu/hw/pci-host/grackle.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * QEMU Grackle PCI host (heathrow OldWorld PowerMac) - * - * Copyright (c) 2006-2007 Fabrice Bellard - * Copyright (c) 2007 Jocelyn Mayer - * - * 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/pci/pci_host.h" -#include "hw/ppc/mac.h" -#include "hw/pci/pci.h" - -/* debug Grackle */ -//#define DEBUG_GRACKLE - -#ifdef DEBUG_GRACKLE -#define GRACKLE_DPRINTF(fmt, ...) \ - do { printf("GRACKLE: " fmt , ## __VA_ARGS__); } while (0) -#else -#define GRACKLE_DPRINTF(fmt, ...) -#endif - -#define GRACKLE_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(GrackleState, (obj), TYPE_GRACKLE_PCI_HOST_BRIDGE) - -typedef struct GrackleState { - PCIHostState parent_obj; - - MemoryRegion pci_mmio; - MemoryRegion pci_hole; -} GrackleState; - -/* Don't know if this matches real hardware, but it agrees with OHW. */ -static int pci_grackle_map_irq(PCIDevice *pci_dev, int irq_num) -{ - return (irq_num + (pci_dev->devfn >> 3)) & 3; -} - -static void pci_grackle_set_irq(void *opaque, int irq_num, int level) -{ - qemu_irq *pic = opaque; - - GRACKLE_DPRINTF("set_irq num %d level %d\n", irq_num, level); - qemu_set_irq(pic[irq_num + 0x15], level); -} - -PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io) -{ - DeviceState *dev; - SysBusDevice *s; - PCIHostState *phb; - GrackleState *d; - - dev = qdev_create(NULL, TYPE_GRACKLE_PCI_HOST_BRIDGE); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - phb = PCI_HOST_BRIDGE(dev); - d = GRACKLE_PCI_HOST_BRIDGE(dev); - - memory_region_init(&d->pci_mmio, OBJECT(s), "pci-mmio", 0x100000000ULL); - memory_region_init_alias(&d->pci_hole, OBJECT(s), "pci-hole", &d->pci_mmio, - 0x80000000ULL, 0x7e000000ULL); - memory_region_add_subregion(address_space_mem, 0x80000000ULL, - &d->pci_hole); - - phb->bus = pci_register_bus(dev, NULL, - pci_grackle_set_irq, - pci_grackle_map_irq, - pic, - &d->pci_mmio, - address_space_io, - 0, 4, TYPE_PCI_BUS); - - pci_create_simple(phb->bus, 0, "grackle"); - - sysbus_mmio_map(s, 0, base); - sysbus_mmio_map(s, 1, base + 0x00200000); - - return phb->bus; -} - -static int pci_grackle_init_device(SysBusDevice *dev) -{ - PCIHostState *phb; - - phb = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&phb->conf_mem, OBJECT(dev), &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&phb->data_mem, OBJECT(dev), &pci_host_data_le_ops, - dev, "pci-data-idx", 0x1000); - sysbus_init_mmio(dev, &phb->conf_mem); - sysbus_init_mmio(dev, &phb->data_mem); - - return 0; -} - -static void grackle_pci_host_realize(PCIDevice *d, Error **errp) -{ - d->config[0x09] = 0x01; -} - -static void grackle_pci_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = grackle_pci_host_realize; - k->vendor_id = PCI_VENDOR_ID_MOTOROLA; - k->device_id = PCI_DEVICE_ID_MOTOROLA_MPC106; - k->revision = 0x00; - k->class_id = PCI_CLASS_BRIDGE_HOST; - /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo grackle_pci_info = { - .name = "grackle", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = grackle_pci_class_init, -}; - -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 = { - .name = TYPE_GRACKLE_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(GrackleState), - .class_init = pci_grackle_class_init, -}; - -static void grackle_register_types(void) -{ - type_register_static(&grackle_pci_info); - type_register_static(&grackle_pci_host_info); -} - -type_init(grackle_register_types) diff --git a/qemu/hw/pci-host/pam.c b/qemu/hw/pci-host/pam.c deleted file mode 100644 index e361ecb7e..000000000 --- a/qemu/hw/pci-host/pam.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * QEMU Smram/pam logic implementation - * - * Copyright (c) 2006 Fabrice Bellard - * Copyright (c) 2011 Isaku Yamahata - * VA Linux Systems Japan K.K. - * Copyright (c) 2012 Jason Baron - * - * Split out from piix.c - * - * 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 "qom/object.h" -#include "sysemu/sysemu.h" -#include "hw/pci-host/pam.h" - -void init_pam(DeviceState *dev, MemoryRegion *ram_memory, - MemoryRegion *system_memory, MemoryRegion *pci_address_space, - PAMMemoryRegion *mem, uint32_t start, uint32_t size) -{ - int i; - - /* RAM */ - memory_region_init_alias(&mem->alias[3], OBJECT(dev), "pam-ram", ram_memory, - start, size); - /* ROM (XXX: not quite correct) */ - memory_region_init_alias(&mem->alias[1], OBJECT(dev), "pam-rom", ram_memory, - start, size); - memory_region_set_readonly(&mem->alias[1], true); - - /* XXX: should distinguish read/write cases */ - memory_region_init_alias(&mem->alias[0], OBJECT(dev), "pam-pci", pci_address_space, - start, size); - memory_region_init_alias(&mem->alias[2], OBJECT(dev), "pam-pci", ram_memory, - start, size); - - for (i = 0; i < 4; ++i) { - memory_region_set_enabled(&mem->alias[i], false); - memory_region_add_subregion_overlap(system_memory, start, - &mem->alias[i], 1); - } - mem->current = 0; -} - -void pam_update(PAMMemoryRegion *pam, int idx, uint8_t val) -{ - assert(0 <= idx && idx <= 12); - - memory_region_set_enabled(&pam->alias[pam->current], false); - pam->current = (val >> ((!(idx & 1)) * 4)) & PAM_ATTR_MASK; - memory_region_set_enabled(&pam->alias[pam->current], true); -} diff --git a/qemu/hw/pci-host/piix.c b/qemu/hw/pci-host/piix.c deleted file mode 100644 index df2b0e26f..000000000 --- a/qemu/hw/pci-host/piix.c +++ /dev/null @@ -1,888 +0,0 @@ -/* - * QEMU i440FX/PIIX3 PCI Bridge Emulation - * - * Copyright (c) 2006 Fabrice Bellard - * - * 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/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 I440FX_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(I440FXState, (obj), TYPE_I440FX_PCI_HOST_BRIDGE) - -typedef struct I440FXState { - PCIHostState parent_obj; - PcPciInfo pci_info; - uint64_t pci_hole64_size; - uint32_t short_root_bus; -} I440FXState; - -#define PIIX_NUM_PIC_IRQS 16 /* i8259 * 2 */ -#define PIIX_NUM_PIRQS 4ULL /* PIRQ[A-D] */ -#define XEN_PIIX_NUM_PIRQS 128ULL -#define PIIX_PIRQC 0x60 - -/* - * Reset Control Register: PCI-accessible ISA-Compatible Register at address - * 0xcf9, provided by the PCI/ISA bridge (PIIX3 PCI function 0, 8086:7000). - */ -#define RCR_IOPORT 0xcf9 - -typedef struct PIIX3State { - PCIDevice dev; - - /* - * bitmap to track pic levels. - * The pic level is the logical OR of all the PCI irqs mapped to it - * So one PIC level is tracked by PIIX_NUM_PIRQS bits. - * - * PIRQ is mapped to PIC pins, we track it by - * PIIX_NUM_PIRQS * PIIX_NUM_PIC_IRQS = 64 bits with - * pic_irq * PIIX_NUM_PIRQS + pirq - */ -#if PIIX_NUM_PIC_IRQS * PIIX_NUM_PIRQS > 64 -#error "unable to encode pic state in 64bit in pic_levels." -#endif - uint64_t pic_levels; - - qemu_irq *pic; - - /* This member isn't used. Just for save/load compatibility */ - int32_t pci_irq_levels_vmstate[PIIX_NUM_PIRQS]; - - /* Reset Control Register contents */ - uint8_t rcr; - - /* IO memory region for Reset Control Register (RCR_IOPORT) */ - MemoryRegion rcr_mem; -} PIIX3State; - -#define TYPE_PIIX3_PCI_DEVICE "pci-piix3" -#define PIIX3_PCI_DEVICE(obj) \ - OBJECT_CHECK(PIIX3State, (obj), TYPE_PIIX3_PCI_DEVICE) - -#define I440FX_PCI_DEVICE(obj) \ - OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE) - -struct PCII440FXState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - MemoryRegion *system_memory; - MemoryRegion *pci_address_space; - MemoryRegion *ram_memory; - PAMMemoryRegion pam_regions[13]; - MemoryRegion smram_region; - MemoryRegion smram, low_smram; -}; - - -#define I440FX_PAM 0x59 -#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, - uint32_t address, uint32_t val, int len); - -/* return the global irq number corresponding to a given device irq - pin. We could also use the bus number to have a more precise - mapping. */ -static int pci_slot_get_pirq(PCIDevice *pci_dev, int pci_intx) -{ - int slot_addend; - slot_addend = (pci_dev->devfn >> 3) - 1; - return (pci_intx + slot_addend) & 3; -} - -static void i440fx_update_memory_mappings(PCII440FXState *d) -{ - int i; - PCIDevice *pd = PCI_DEVICE(d); - - memory_region_transaction_begin(); - for (i = 0; i < 13; i++) { - pam_update(&d->pam_regions[i], i, - pd->config[I440FX_PAM + ((i + 1) / 2)]); - } - memory_region_set_enabled(&d->smram_region, - !(pd->config[I440FX_SMRAM] & SMRAM_D_OPEN)); - memory_region_set_enabled(&d->smram, - pd->config[I440FX_SMRAM] & SMRAM_G_SMRAME); - memory_region_transaction_commit(); -} - - -static void i440fx_write_config(PCIDevice *dev, - uint32_t address, uint32_t val, int len) -{ - PCII440FXState *d = I440FX_PCI_DEVICE(dev); - - /* XXX: implement SMRAM.D_LOCK */ - pci_default_write_config(dev, address, val, len); - if (ranges_overlap(address, len, I440FX_PAM, I440FX_PAM_SIZE) || - range_covers_byte(address, len, I440FX_SMRAM)) { - i440fx_update_memory_mappings(d); - } -} - -static int i440fx_load_old(QEMUFile* f, void *opaque, int version_id) -{ - PCII440FXState *d = opaque; - PCIDevice *pd = PCI_DEVICE(d); - int ret, i; - uint8_t smm_enabled; - - ret = pci_device_load(pd, f); - if (ret < 0) - return ret; - i440fx_update_memory_mappings(d); - qemu_get_8s(f, &smm_enabled); - - if (version_id == 2) { - for (i = 0; i < PIIX_NUM_PIRQS; i++) { - qemu_get_be32(f); /* dummy load for compatibility */ - } - } - - return 0; -} - -static int i440fx_post_load(void *opaque, int version_id) -{ - PCII440FXState *d = opaque; - - i440fx_update_memory_mappings(d); - return 0; -} - -static const VMStateDescription vmstate_i440fx = { - .name = "I440FX", - .version_id = 3, - .minimum_version_id = 3, - .minimum_version_id_old = 1, - .load_state_old = i440fx_load_old, - .post_load = i440fx_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, PCII440FXState), - /* Used to be smm_enabled, which was basically always zero because - * SeaBIOS hardly uses SMM. SMRAM is now handled by CPU code. - */ - VMSTATE_UNUSED(1), - VMSTATE_END_OF_LIST() - } -}; - -static void i440fx_pcihost_get_pci_hole_start(Object *obj, Visitor *v, - 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, name, &value, errp); -} - -static void i440fx_pcihost_get_pci_hole_end(Object *obj, Visitor *v, - 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, name, &value, errp); -} - -static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v, - 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, name, &w64.begin, errp); -} - -static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v, - 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, name, &w64.end, errp); -} - -static void i440fx_pcihost_initfn(Object *obj) -{ - PCIHostState *s = PCI_HOST_BRIDGE(obj); - I440FXState *d = I440FX_PCI_HOST_BRIDGE(obj); - - memory_region_init_io(&s->conf_mem, obj, &pci_host_conf_le_ops, s, - "pci-conf-idx", 4); - memory_region_init_io(&s->data_mem, obj, &pci_host_data_le_ops, s, - "pci-conf-data", 4); - - object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_START, "int", - i440fx_pcihost_get_pci_hole_start, - NULL, NULL, NULL, NULL); - - object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_END, "int", - i440fx_pcihost_get_pci_hole_end, - NULL, NULL, NULL, NULL); - - object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_START, "int", - i440fx_pcihost_get_pci_hole64_start, - NULL, NULL, NULL, NULL); - - object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_END, "int", - i440fx_pcihost_get_pci_hole64_end, - NULL, NULL, NULL, NULL); - - d->pci_info.w32.end = IO_APIC_DEFAULT_ADDRESS; -} - -static void i440fx_pcihost_realize(DeviceState *dev, Error **errp) -{ - PCIHostState *s = PCI_HOST_BRIDGE(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - - sysbus_add_io(sbd, 0xcf8, &s->conf_mem); - sysbus_init_ioports(sbd, 0xcf8, 4); - - sysbus_add_io(sbd, 0xcfc, &s->data_mem); - sysbus_init_ioports(sbd, 0xcfc, 4); -} - -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(const char *host_type, const char *pci_type, - PCII440FXState **pi440fx_state, - int *piix3_devfn, - ISABus **isa_bus, qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - ram_addr_t ram_size, - ram_addr_t below_4g_mem_size, - ram_addr_t above_4g_mem_size, - MemoryRegion *pci_address_space, - MemoryRegion *ram_memory) -{ - DeviceState *dev; - PCIBus *b; - PCIDevice *d; - PCIHostState *s; - PIIX3State *piix3; - PCII440FXState *f; - unsigned i; - I440FXState *i440fx; - - 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); - s->bus = b; - object_property_add_child(qdev_get_machine(), "i440fx", OBJECT(dev), NULL); - qdev_init_nofail(dev); - - d = pci_create_simple(b, 0, pci_type); - *pi440fx_state = I440FX_PCI_DEVICE(d); - f = *pi440fx_state; - f->system_memory = address_space_mem; - f->pci_address_space = pci_address_space; - f->ram_memory = ram_memory; - - i440fx = I440FX_PCI_HOST_BRIDGE(dev); - i440fx->pci_info.w32.begin = below_4g_mem_size; - - /* setup pci memory mapping */ - pc_pci_as_mapping_init(OBJECT(f), f->system_memory, - f->pci_address_space); - - /* if *disabled* show SMRAM to all CPUs */ - memory_region_init_alias(&f->smram_region, OBJECT(d), "smram-region", - f->pci_address_space, 0xa0000, 0x20000); - memory_region_add_subregion_overlap(f->system_memory, 0xa0000, - &f->smram_region, 1); - memory_region_set_enabled(&f->smram_region, true); - - /* smram, as seen by SMM CPUs */ - memory_region_init(&f->smram, OBJECT(d), "smram", 1ull << 32); - memory_region_set_enabled(&f->smram, true); - memory_region_init_alias(&f->low_smram, OBJECT(d), "smram-low", - f->ram_memory, 0xa0000, 0x20000); - memory_region_set_enabled(&f->low_smram, true); - memory_region_add_subregion(&f->smram, 0xa0000, &f->low_smram); - object_property_add_const_link(qdev_get_machine(), "smram", - OBJECT(&f->smram), &error_abort); - - init_pam(dev, f->ram_memory, f->system_memory, f->pci_address_space, - &f->pam_regions[0], PAM_BIOS_BASE, PAM_BIOS_SIZE); - for (i = 0; i < 12; ++i) { - init_pam(dev, f->ram_memory, f->system_memory, f->pci_address_space, - &f->pam_regions[i+1], PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, - PAM_EXPAN_SIZE); - } - - /* Xen supports additional interrupt routes from the PCI devices to - * the IOAPIC: the four pins of each PCI device on the bus are also - * connected to the IOAPIC directly. - * These additional routes can be discovered through ACPI. */ - if (xen_enabled()) { - PCIDevice *pci_dev = pci_create_simple_multifunction(b, - -1, true, "PIIX3-xen"); - piix3 = PIIX3_PCI_DEVICE(pci_dev); - pci_bus_irqs(b, xen_piix3_set_irq, xen_pci_slot_get_pirq, - piix3, XEN_PIIX_NUM_PIRQS); - } else { - PCIDevice *pci_dev = pci_create_simple_multifunction(b, - -1, true, "PIIX3"); - piix3 = PIIX3_PCI_DEVICE(pci_dev); - pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3, - PIIX_NUM_PIRQS); - pci_bus_set_route_irq_fn(b, piix3_route_intx_pin_to_irq); - } - piix3->pic = pic; - *isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(piix3), "isa.0")); - - *piix3_devfn = piix3->dev.devfn; - - ram_size = ram_size / 8 / 1024 / 1024; - if (ram_size > 255) { - ram_size = 255; - } - d->config[I440FX_COREBOOT_RAM_SIZE] = ram_size; - - i440fx_update_memory_mappings(f); - - return b; -} - -PCIBus *find_i440fx(void) -{ - PCIHostState *s = OBJECT_CHECK(PCIHostState, - object_resolve_path("/machine/i440fx", NULL), - TYPE_PCI_HOST_BRIDGE); - return s ? s->bus : NULL; -} - -/* PIIX3 PCI to ISA bridge */ -static void piix3_set_irq_pic(PIIX3State *piix3, int pic_irq) -{ - qemu_set_irq(piix3->pic[pic_irq], - !!(piix3->pic_levels & - (((1ULL << PIIX_NUM_PIRQS) - 1) << - (pic_irq * PIIX_NUM_PIRQS)))); -} - -static void piix3_set_irq_level_internal(PIIX3State *piix3, int pirq, int level) -{ - int pic_irq; - uint64_t mask; - - pic_irq = piix3->dev.config[PIIX_PIRQC + pirq]; - if (pic_irq >= PIIX_NUM_PIC_IRQS) { - return; - } - - mask = 1ULL << ((pic_irq * PIIX_NUM_PIRQS) + pirq); - piix3->pic_levels &= ~mask; - piix3->pic_levels |= mask * !!level; -} - -static void piix3_set_irq_level(PIIX3State *piix3, int pirq, int level) -{ - int pic_irq; - - pic_irq = piix3->dev.config[PIIX_PIRQC + pirq]; - if (pic_irq >= PIIX_NUM_PIC_IRQS) { - return; - } - - piix3_set_irq_level_internal(piix3, pirq, level); - - piix3_set_irq_pic(piix3, pic_irq); -} - -static void piix3_set_irq(void *opaque, int pirq, int level) -{ - PIIX3State *piix3 = opaque; - piix3_set_irq_level(piix3, pirq, level); -} - -static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pin) -{ - PIIX3State *piix3 = opaque; - int irq = piix3->dev.config[PIIX_PIRQC + pin]; - PCIINTxRoute route; - - if (irq < PIIX_NUM_PIC_IRQS) { - route.mode = PCI_INTX_ENABLED; - route.irq = irq; - } else { - route.mode = PCI_INTX_DISABLED; - route.irq = -1; - } - return route; -} - -/* irq routing is changed. so rebuild bitmap */ -static void piix3_update_irq_levels(PIIX3State *piix3) -{ - int pirq; - - piix3->pic_levels = 0; - for (pirq = 0; pirq < PIIX_NUM_PIRQS; pirq++) { - piix3_set_irq_level(piix3, pirq, - pci_bus_get_irq_level(piix3->dev.bus, pirq)); - } -} - -static void piix3_write_config(PCIDevice *dev, - uint32_t address, uint32_t val, int len) -{ - pci_default_write_config(dev, address, val, len); - if (ranges_overlap(address, len, PIIX_PIRQC, 4)) { - PIIX3State *piix3 = PIIX3_PCI_DEVICE(dev); - int pic_irq; - - pci_bus_fire_intx_routing_notifier(piix3->dev.bus); - piix3_update_irq_levels(piix3); - for (pic_irq = 0; pic_irq < PIIX_NUM_PIC_IRQS; pic_irq++) { - piix3_set_irq_pic(piix3, pic_irq); - } - } -} - -static void piix3_write_config_xen(PCIDevice *dev, - uint32_t address, uint32_t val, int len) -{ - xen_piix_pci_write_config_client(address, val, len); - piix3_write_config(dev, address, val, len); -} - -static void piix3_reset(void *opaque) -{ - PIIX3State *d = opaque; - uint8_t *pci_conf = d->dev.config; - - pci_conf[0x04] = 0x07; /* master, memory and I/O */ - pci_conf[0x05] = 0x00; - pci_conf[0x06] = 0x00; - pci_conf[0x07] = 0x02; /* PCI_status_devsel_medium */ - pci_conf[0x4c] = 0x4d; - pci_conf[0x4e] = 0x03; - pci_conf[0x4f] = 0x00; - pci_conf[0x60] = 0x80; - pci_conf[0x61] = 0x80; - pci_conf[0x62] = 0x80; - pci_conf[0x63] = 0x80; - pci_conf[0x69] = 0x02; - pci_conf[0x70] = 0x80; - pci_conf[0x76] = 0x0c; - pci_conf[0x77] = 0x0c; - pci_conf[0x78] = 0x02; - pci_conf[0x79] = 0x00; - pci_conf[0x80] = 0x00; - pci_conf[0x82] = 0x00; - pci_conf[0xa0] = 0x08; - pci_conf[0xa2] = 0x00; - pci_conf[0xa3] = 0x00; - pci_conf[0xa4] = 0x00; - pci_conf[0xa5] = 0x00; - pci_conf[0xa6] = 0x00; - pci_conf[0xa7] = 0x00; - pci_conf[0xa8] = 0x0f; - pci_conf[0xaa] = 0x00; - pci_conf[0xab] = 0x00; - pci_conf[0xac] = 0x00; - pci_conf[0xae] = 0x00; - - d->pic_levels = 0; - d->rcr = 0; -} - -static int piix3_post_load(void *opaque, int version_id) -{ - PIIX3State *piix3 = opaque; - int pirq; - - /* Because the i8259 has not been deserialized yet, qemu_irq_raise - * might bring the system to a different state than the saved one; - * for example, the interrupt could be masked but the i8259 would - * not know that yet and would trigger an interrupt in the CPU. - * - * Here, we update irq levels without raising the interrupt. - * Interrupt state will be deserialized separately through the i8259. - */ - piix3->pic_levels = 0; - for (pirq = 0; pirq < PIIX_NUM_PIRQS; pirq++) { - piix3_set_irq_level_internal(piix3, pirq, - pci_bus_get_irq_level(piix3->dev.bus, pirq)); - } - return 0; -} - -static void piix3_pre_save(void *opaque) -{ - int i; - PIIX3State *piix3 = opaque; - - for (i = 0; i < ARRAY_SIZE(piix3->pci_irq_levels_vmstate); i++) { - piix3->pci_irq_levels_vmstate[i] = - pci_bus_get_irq_level(piix3->dev.bus, i); - } -} - -static bool piix3_rcr_needed(void *opaque) -{ - PIIX3State *piix3 = opaque; - - return (piix3->rcr != 0); -} - -static const VMStateDescription vmstate_piix3_rcr = { - .name = "PIIX3/rcr", - .version_id = 1, - .minimum_version_id = 1, - .needed = piix3_rcr_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(rcr, PIIX3State), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_piix3 = { - .name = "PIIX3", - .version_id = 3, - .minimum_version_id = 2, - .post_load = piix3_post_load, - .pre_save = piix3_pre_save, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, PIIX3State), - VMSTATE_INT32_ARRAY_V(pci_irq_levels_vmstate, PIIX3State, - PIIX_NUM_PIRQS, 3), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_piix3_rcr, - NULL - } -}; - - -static void rcr_write(void *opaque, hwaddr addr, uint64_t val, unsigned len) -{ - PIIX3State *d = opaque; - - if (val & 4) { - qemu_system_reset_request(); - return; - } - d->rcr = val & 2; /* keep System Reset type only */ -} - -static uint64_t rcr_read(void *opaque, hwaddr addr, unsigned len) -{ - PIIX3State *d = opaque; - - return d->rcr; -} - -static const MemoryRegionOps rcr_ops = { - .read = rcr_read, - .write = rcr_write, - .endianness = DEVICE_LITTLE_ENDIAN -}; - -static void piix3_realize(PCIDevice *dev, Error **errp) -{ - PIIX3State *d = PIIX3_PCI_DEVICE(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); - memory_region_add_subregion_overlap(pci_address_space_io(dev), RCR_IOPORT, - &d->rcr_mem, 1); - - qemu_register_reset(piix3_reset, d); -} - -static void pci_piix3_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - dc->desc = "ISA bridge"; - dc->vmsd = &vmstate_piix3; - dc->hotpluggable = false; - k->realize = piix3_realize; - k->vendor_id = PCI_VENDOR_ID_INTEL; - /* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */ - k->device_id = PCI_DEVICE_ID_INTEL_82371SB_0; - k->class_id = PCI_CLASS_BRIDGE_ISA; - /* - * Reason: part of PIIX3 southbridge, needs to be wired up by - * pc_piix.c's pc_init1() - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo piix3_pci_type_info = { - .name = TYPE_PIIX3_PCI_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PIIX3State), - .abstract = true, - .class_init = pci_piix3_class_init, -}; - -static void piix3_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->config_write = piix3_write_config; -} - -static const TypeInfo piix3_info = { - .name = "PIIX3", - .parent = TYPE_PIIX3_PCI_DEVICE, - .class_init = piix3_class_init, -}; - -static void piix3_xen_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->config_write = piix3_write_config_xen; -}; - -static const TypeInfo piix3_xen_info = { - .name = "PIIX3-xen", - .parent = TYPE_PIIX3_PCI_DEVICE, - .class_init = piix3_xen_class_init, -}; - -static void i440fx_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = i440fx_realize; - k->config_write = i440fx_write_config; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82441; - k->revision = 0x02; - k->class_id = PCI_CLASS_BRIDGE_HOST; - dc->desc = "Host bridge"; - dc->vmsd = &vmstate_i440fx; - /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. - */ - dc->cannot_instantiate_with_device_add_yet = true; - dc->hotpluggable = false; -} - -static const TypeInfo i440fx_info = { - .name = TYPE_I440FX_PCI_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCII440FXState), - .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) -{ - I440FXState *s = I440FX_PCI_HOST_BRIDGE(host_bridge); - - /* For backwards compat with old device paths */ - if (s->short_root_bus) { - return "0000"; - } - return "0000:00"; -} - -static Property i440fx_props[] = { - DEFINE_PROP_SIZE(PCI_HOST_PROP_PCI_HOLE64_SIZE, I440FXState, - pci_hole64_size, DEFAULT_PCI_HOLE64_SIZE), - DEFINE_PROP_UINT32("short_root_bus", I440FXState, short_root_bus, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void i440fx_pcihost_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); - - hc->root_bus_path = i440fx_pcihost_root_bus_path; - dc->realize = i440fx_pcihost_realize; - dc->fw_name = "pci"; - dc->props = i440fx_props; -} - -static const TypeInfo i440fx_pcihost_info = { - .name = TYPE_I440FX_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(I440FXState), - .instance_init = i440fx_pcihost_initfn, - .class_init = i440fx_pcihost_class_init, -}; - -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); - type_register_static(&i440fx_pcihost_info); -} - -type_init(i440fx_register_types) diff --git a/qemu/hw/pci-host/ppce500.c b/qemu/hw/pci-host/ppce500.c deleted file mode 100644 index e502bc050..000000000 --- a/qemu/hw/pci-host/ppce500.c +++ /dev/null @@ -1,551 +0,0 @@ -/* - * QEMU PowerPC E500 embedded processors pci controller emulation - * - * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved. - * - * Author: Yu Liu, - * - * This file is derived from hw/ppc4xx_pci.c, - * the copyright for that material belongs to the original owners. - * - * This 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. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/ppc/e500-ccsr.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "qemu/bswap.h" -#include "hw/pci-host/ppce500.h" - -#ifdef DEBUG_PCI -#define pci_debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__) -#else -#define pci_debug(fmt, ...) -#endif - -#define PCIE500_CFGADDR 0x0 -#define PCIE500_CFGDATA 0x4 -#define PCIE500_REG_BASE 0xC00 -#define PCIE500_ALL_SIZE 0x1000 -#define PCIE500_REG_SIZE (PCIE500_ALL_SIZE - PCIE500_REG_BASE) - -#define PCIE500_PCI_IOLEN 0x10000ULL - -#define PPCE500_PCI_CONFIG_ADDR 0x0 -#define PPCE500_PCI_CONFIG_DATA 0x4 -#define PPCE500_PCI_INTACK 0x8 - -#define PPCE500_PCI_OW1 (0xC20 - PCIE500_REG_BASE) -#define PPCE500_PCI_OW2 (0xC40 - PCIE500_REG_BASE) -#define PPCE500_PCI_OW3 (0xC60 - PCIE500_REG_BASE) -#define PPCE500_PCI_OW4 (0xC80 - PCIE500_REG_BASE) -#define PPCE500_PCI_IW3 (0xDA0 - PCIE500_REG_BASE) -#define PPCE500_PCI_IW2 (0xDC0 - PCIE500_REG_BASE) -#define PPCE500_PCI_IW1 (0xDE0 - PCIE500_REG_BASE) - -#define PPCE500_PCI_GASKET_TIMR (0xE20 - PCIE500_REG_BASE) - -#define PCI_POTAR 0x0 -#define PCI_POTEAR 0x4 -#define PCI_POWBAR 0x8 -#define PCI_POWAR 0x10 - -#define PCI_PITAR 0x0 -#define PCI_PIWBAR 0x8 -#define PCI_PIWBEAR 0xC -#define PCI_PIWAR 0x10 - -#define PPCE500_PCI_NR_POBS 5 -#define PPCE500_PCI_NR_PIBS 3 - -#define PIWAR_EN 0x80000000 /* Enable */ -#define PIWAR_PF 0x20000000 /* prefetch */ -#define PIWAR_TGI_LOCAL 0x00f00000 /* target - local memory */ -#define PIWAR_READ_SNOOP 0x00050000 -#define PIWAR_WRITE_SNOOP 0x00005000 -#define PIWAR_SZ_MASK 0x0000003f - -struct pci_outbound { - uint32_t potar; - uint32_t potear; - uint32_t powbar; - uint32_t powar; - MemoryRegion mem; -}; - -struct pci_inbound { - uint32_t pitar; - uint32_t piwbar; - uint32_t piwbear; - uint32_t piwar; - MemoryRegion mem; -}; - -#define TYPE_PPC_E500_PCI_HOST_BRIDGE "e500-pcihost" - -#define PPC_E500_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(PPCE500PCIState, (obj), TYPE_PPC_E500_PCI_HOST_BRIDGE) - -struct PPCE500PCIState { - PCIHostState parent_obj; - - struct pci_outbound pob[PPCE500_PCI_NR_POBS]; - struct pci_inbound pib[PPCE500_PCI_NR_PIBS]; - uint32_t gasket_time; - qemu_irq irq[PCI_NUM_PINS]; - uint32_t irq_num[PCI_NUM_PINS]; - uint32_t first_slot; - uint32_t first_pin_irq; - AddressSpace bm_as; - MemoryRegion bm; - /* mmio maps */ - MemoryRegion container; - MemoryRegion iomem; - MemoryRegion pio; - MemoryRegion busmem; -}; - -#define TYPE_PPC_E500_PCI_BRIDGE "e500-host-bridge" -#define PPC_E500_PCI_BRIDGE(obj) \ - OBJECT_CHECK(PPCE500PCIBridgeState, (obj), TYPE_PPC_E500_PCI_BRIDGE) - -struct PPCE500PCIBridgeState { - /*< private >*/ - PCIDevice parent; - /*< public >*/ - - MemoryRegion bar0; -}; - -typedef struct PPCE500PCIBridgeState PPCE500PCIBridgeState; -typedef struct PPCE500PCIState PPCE500PCIState; - -static uint64_t pci_reg_read4(void *opaque, hwaddr addr, - unsigned size) -{ - PPCE500PCIState *pci = opaque; - unsigned long win; - uint32_t value = 0; - int idx; - - win = addr & 0xfe0; - - switch (win) { - case PPCE500_PCI_OW1: - case PPCE500_PCI_OW2: - case PPCE500_PCI_OW3: - case PPCE500_PCI_OW4: - idx = (addr >> 5) & 0x7; - switch (addr & 0x1F) { - case PCI_POTAR: - value = pci->pob[idx].potar; - break; - case PCI_POTEAR: - value = pci->pob[idx].potear; - break; - case PCI_POWBAR: - value = pci->pob[idx].powbar; - break; - case PCI_POWAR: - value = pci->pob[idx].powar; - break; - default: - break; - } - break; - - case PPCE500_PCI_IW3: - case PPCE500_PCI_IW2: - case PPCE500_PCI_IW1: - idx = ((addr >> 5) & 0x3) - 1; - switch (addr & 0x1F) { - case PCI_PITAR: - value = pci->pib[idx].pitar; - break; - case PCI_PIWBAR: - value = pci->pib[idx].piwbar; - break; - case PCI_PIWBEAR: - value = pci->pib[idx].piwbear; - break; - case PCI_PIWAR: - value = pci->pib[idx].piwar; - break; - default: - break; - }; - break; - - case PPCE500_PCI_GASKET_TIMR: - value = pci->gasket_time; - break; - - default: - break; - } - - pci_debug("%s: win:%lx(addr:" TARGET_FMT_plx ") -> value:%x\n", __func__, - win, addr, value); - return value; -} - -/* DMA mapping */ -static void e500_update_piw(PPCE500PCIState *pci, int idx) -{ - uint64_t tar = ((uint64_t)pci->pib[idx].pitar) << 12; - uint64_t wbar = ((uint64_t)pci->pib[idx].piwbar) << 12; - uint64_t war = pci->pib[idx].piwar; - uint64_t size = 2ULL << (war & PIWAR_SZ_MASK); - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *mem = &pci->pib[idx].mem; - MemoryRegion *bm = &pci->bm; - char *name; - - if (memory_region_is_mapped(mem)) { - /* Before we modify anything, unmap and destroy the region */ - memory_region_del_subregion(bm, mem); - object_unparent(OBJECT(mem)); - } - - if (!(war & PIWAR_EN)) { - /* Not enabled, nothing to do */ - return; - } - - name = g_strdup_printf("PCI Inbound Window %d", idx); - memory_region_init_alias(mem, OBJECT(pci), name, address_space_mem, tar, - size); - memory_region_add_subregion_overlap(bm, wbar, mem, -1); - g_free(name); - - pci_debug("%s: Added window of size=%#lx from PCI=%#lx to CPU=%#lx\n", - __func__, size, wbar, tar); -} - -/* BAR mapping */ -static void e500_update_pow(PPCE500PCIState *pci, int idx) -{ - uint64_t tar = ((uint64_t)pci->pob[idx].potar) << 12; - uint64_t wbar = ((uint64_t)pci->pob[idx].powbar) << 12; - uint64_t war = pci->pob[idx].powar; - uint64_t size = 2ULL << (war & PIWAR_SZ_MASK); - MemoryRegion *mem = &pci->pob[idx].mem; - MemoryRegion *address_space_mem = get_system_memory(); - char *name; - - if (memory_region_is_mapped(mem)) { - /* Before we modify anything, unmap and destroy the region */ - memory_region_del_subregion(address_space_mem, mem); - object_unparent(OBJECT(mem)); - } - - if (!(war & PIWAR_EN)) { - /* Not enabled, nothing to do */ - return; - } - - name = g_strdup_printf("PCI Outbound Window %d", idx); - memory_region_init_alias(mem, OBJECT(pci), name, &pci->busmem, tar, - size); - memory_region_add_subregion(address_space_mem, wbar, mem); - g_free(name); - - pci_debug("%s: Added window of size=%#lx from CPU=%#lx to PCI=%#lx\n", - __func__, size, wbar, tar); -} - -static void pci_reg_write4(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - PPCE500PCIState *pci = opaque; - unsigned long win; - int idx; - - win = addr & 0xfe0; - - pci_debug("%s: value:%x -> win:%lx(addr:" TARGET_FMT_plx ")\n", - __func__, (unsigned)value, win, addr); - - switch (win) { - case PPCE500_PCI_OW1: - case PPCE500_PCI_OW2: - case PPCE500_PCI_OW3: - case PPCE500_PCI_OW4: - idx = (addr >> 5) & 0x7; - switch (addr & 0x1F) { - case PCI_POTAR: - pci->pob[idx].potar = value; - e500_update_pow(pci, idx); - break; - case PCI_POTEAR: - pci->pob[idx].potear = value; - e500_update_pow(pci, idx); - break; - case PCI_POWBAR: - pci->pob[idx].powbar = value; - e500_update_pow(pci, idx); - break; - case PCI_POWAR: - pci->pob[idx].powar = value; - e500_update_pow(pci, idx); - break; - default: - break; - }; - break; - - case PPCE500_PCI_IW3: - case PPCE500_PCI_IW2: - case PPCE500_PCI_IW1: - idx = ((addr >> 5) & 0x3) - 1; - switch (addr & 0x1F) { - case PCI_PITAR: - pci->pib[idx].pitar = value; - e500_update_piw(pci, idx); - break; - case PCI_PIWBAR: - pci->pib[idx].piwbar = value; - e500_update_piw(pci, idx); - break; - case PCI_PIWBEAR: - pci->pib[idx].piwbear = value; - e500_update_piw(pci, idx); - break; - case PCI_PIWAR: - pci->pib[idx].piwar = value; - e500_update_piw(pci, idx); - break; - default: - break; - }; - break; - - case PPCE500_PCI_GASKET_TIMR: - pci->gasket_time = value; - break; - - default: - break; - }; -} - -static const MemoryRegionOps e500_pci_reg_ops = { - .read = pci_reg_read4, - .write = pci_reg_write4, - .endianness = DEVICE_BIG_ENDIAN, -}; - -static int mpc85xx_pci_map_irq(PCIDevice *pci_dev, int pin) -{ - int devno = pci_dev->devfn >> 3; - int ret; - - ret = ppce500_pci_map_irq_slot(devno, pin); - - pci_debug("%s: devfn %x irq %d -> %d devno:%x\n", __func__, - pci_dev->devfn, pin, ret, devno); - - return ret; -} - -static void mpc85xx_pci_set_irq(void *opaque, int pin, int level) -{ - PPCE500PCIState *s = opaque; - qemu_irq *pic = s->irq; - - pci_debug("%s: PCI irq %d, level:%d\n", __func__, pin , level); - - qemu_set_irq(pic[pin], level); -} - -static PCIINTxRoute e500_route_intx_pin_to_irq(void *opaque, int pin) -{ - PCIINTxRoute route; - PPCE500PCIState *s = opaque; - - route.mode = PCI_INTX_ENABLED; - route.irq = s->irq_num[pin]; - - pci_debug("%s: PCI irq-pin = %d, irq_num= %d\n", __func__, pin, route.irq); - return route; -} - -static const VMStateDescription vmstate_pci_outbound = { - .name = "pci_outbound", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(potar, struct pci_outbound), - VMSTATE_UINT32(potear, struct pci_outbound), - VMSTATE_UINT32(powbar, struct pci_outbound), - VMSTATE_UINT32(powar, struct pci_outbound), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_pci_inbound = { - .name = "pci_inbound", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(pitar, struct pci_inbound), - VMSTATE_UINT32(piwbar, struct pci_inbound), - VMSTATE_UINT32(piwbear, struct pci_inbound), - VMSTATE_UINT32(piwar, struct pci_inbound), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_ppce500_pci = { - .name = "ppce500_pci", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(pob, PPCE500PCIState, PPCE500_PCI_NR_POBS, 1, - vmstate_pci_outbound, struct pci_outbound), - VMSTATE_STRUCT_ARRAY(pib, PPCE500PCIState, PPCE500_PCI_NR_PIBS, 1, - vmstate_pci_inbound, struct pci_inbound), - VMSTATE_UINT32(gasket_time, PPCE500PCIState), - VMSTATE_END_OF_LIST() - } -}; - -#include "exec/address-spaces.h" - -static void e500_pcihost_bridge_realize(PCIDevice *d, Error **errp) -{ - PPCE500PCIBridgeState *b = PPC_E500_PCI_BRIDGE(d); - PPCE500CCSRState *ccsr = CCSR(container_get(qdev_get_machine(), - "/e500-ccsr")); - - pci_config_set_class(d->config, PCI_CLASS_BRIDGE_PCI); - d->config[PCI_HEADER_TYPE] = - (d->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) | - PCI_HEADER_TYPE_BRIDGE; - - memory_region_init_alias(&b->bar0, OBJECT(ccsr), "e500-pci-bar0", &ccsr->ccsr_space, - 0, int128_get64(ccsr->ccsr_space.size)); - pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &b->bar0); -} - -static AddressSpace *e500_pcihost_set_iommu(PCIBus *bus, void *opaque, - int devfn) -{ - PPCE500PCIState *s = opaque; - - return &s->bm_as; -} - -static int e500_pcihost_initfn(SysBusDevice *dev) -{ - PCIHostState *h; - PPCE500PCIState *s; - PCIBus *b; - int i; - - h = PCI_HOST_BRIDGE(dev); - s = PPC_E500_PCI_HOST_BRIDGE(dev); - - for (i = 0; i < ARRAY_SIZE(s->irq); i++) { - sysbus_init_irq(dev, &s->irq[i]); - } - - for (i = 0; i < PCI_NUM_PINS; i++) { - s->irq_num[i] = s->first_pin_irq + i; - } - - memory_region_init(&s->pio, OBJECT(s), "pci-pio", PCIE500_PCI_IOLEN); - memory_region_init(&s->busmem, OBJECT(s), "pci bus memory", UINT64_MAX); - - /* PIO lives at the bottom of our bus space */ - memory_region_add_subregion_overlap(&s->busmem, 0, &s->pio, -2); - - b = pci_register_bus(DEVICE(dev), NULL, mpc85xx_pci_set_irq, - mpc85xx_pci_map_irq, s, &s->busmem, &s->pio, - PCI_DEVFN(s->first_slot, 0), 4, TYPE_PCI_BUS); - h->bus = b; - - /* Set up PCI view of memory */ - memory_region_init(&s->bm, OBJECT(s), "bm-e500", UINT64_MAX); - memory_region_add_subregion(&s->bm, 0x0, &s->busmem); - address_space_init(&s->bm_as, &s->bm, "pci-bm"); - pci_setup_iommu(b, e500_pcihost_set_iommu, s); - - pci_create_simple(b, 0, "e500-host-bridge"); - - memory_region_init(&s->container, OBJECT(h), "pci-container", PCIE500_ALL_SIZE); - memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_be_ops, h, - "pci-conf-idx", 4); - memory_region_init_io(&h->data_mem, OBJECT(h), &pci_host_data_le_ops, h, - "pci-conf-data", 4); - memory_region_init_io(&s->iomem, OBJECT(s), &e500_pci_reg_ops, s, - "pci.reg", PCIE500_REG_SIZE); - memory_region_add_subregion(&s->container, PCIE500_CFGADDR, &h->conf_mem); - memory_region_add_subregion(&s->container, PCIE500_CFGDATA, &h->data_mem); - memory_region_add_subregion(&s->container, PCIE500_REG_BASE, &s->iomem); - sysbus_init_mmio(dev, &s->container); - pci_bus_set_route_irq_fn(b, e500_route_intx_pin_to_irq); - - return 0; -} - -static void e500_host_bridge_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = e500_pcihost_bridge_realize; - k->vendor_id = PCI_VENDOR_ID_FREESCALE; - k->device_id = PCI_DEVICE_ID_MPC8533E; - k->class_id = PCI_CLASS_PROCESSOR_POWERPC; - dc->desc = "Host bridge"; - /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo e500_host_bridge_info = { - .name = "e500-host-bridge", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PPCE500PCIBridgeState), - .class_init = e500_host_bridge_class_init, -}; - -static Property pcihost_properties[] = { - DEFINE_PROP_UINT32("first_slot", PPCE500PCIState, first_slot, 0x11), - DEFINE_PROP_UINT32("first_pin_irq", PPCE500PCIState, first_pin_irq, 0x1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void e500_pcihost_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = e500_pcihost_initfn; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->props = pcihost_properties; - dc->vmsd = &vmstate_ppce500_pci; -} - -static const TypeInfo e500_pcihost_info = { - .name = TYPE_PPC_E500_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(PPCE500PCIState), - .class_init = e500_pcihost_class_init, -}; - -static void e500_pci_register_types(void) -{ - type_register_static(&e500_pcihost_info); - type_register_static(&e500_host_bridge_info); -} - -type_init(e500_pci_register_types) diff --git a/qemu/hw/pci-host/prep.c b/qemu/hw/pci-host/prep.c deleted file mode 100644 index 487e32ecb..000000000 --- a/qemu/hw/pci-host/prep.c +++ /dev/null @@ -1,406 +0,0 @@ -/* - * QEMU PREP PCI host - * - * Copyright (c) 2006 Fabrice Bellard - * Copyright (c) 2011-2013 Andreas Färber - * - * 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/pci/pci.h" -#include "hw/pci/pci_bus.h" -#include "hw/pci/pci_host.h" -#include "hw/i386/pc.h" -#include "hw/loader.h" -#include "exec/address-spaces.h" -#include "elf.h" - -#define TYPE_RAVEN_PCI_DEVICE "raven" -#define TYPE_RAVEN_PCI_HOST_BRIDGE "raven-pcihost" - -#define RAVEN_PCI_DEVICE(obj) \ - OBJECT_CHECK(RavenPCIState, (obj), TYPE_RAVEN_PCI_DEVICE) - -typedef struct RavenPCIState { - PCIDevice dev; - - uint32_t elf_machine; - char *bios_name; - MemoryRegion bios; -} RavenPCIState; - -#define RAVEN_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(PREPPCIState, (obj), TYPE_RAVEN_PCI_HOST_BRIDGE) - -typedef struct PRePPCIState { - PCIHostState parent_obj; - - qemu_irq irq[PCI_NUM_PINS]; - PCIBus pci_bus; - AddressSpace pci_io_as; - MemoryRegion pci_io; - MemoryRegion pci_io_non_contiguous; - MemoryRegion pci_memory; - MemoryRegion pci_intack; - MemoryRegion bm; - MemoryRegion bm_ram_alias; - MemoryRegion bm_pci_memory_alias; - AddressSpace bm_as; - RavenPCIState pci_dev; - - int contiguous_map; -} PREPPCIState; - -#define BIOS_SIZE (1024 * 1024) - -static inline uint32_t raven_pci_io_config(hwaddr addr) -{ - int i; - - for (i = 0; i < 11; i++) { - if ((addr & (1 << (11 + i))) != 0) { - break; - } - } - return (addr & 0x7ff) | (i << 11); -} - -static void raven_pci_io_write(void *opaque, hwaddr addr, - uint64_t val, unsigned int size) -{ - PREPPCIState *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); - pci_data_write(phb->bus, raven_pci_io_config(addr), val, size); -} - -static uint64_t raven_pci_io_read(void *opaque, hwaddr addr, - unsigned int size) -{ - PREPPCIState *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); - return pci_data_read(phb->bus, raven_pci_io_config(addr), size); -} - -static const MemoryRegionOps raven_pci_io_ops = { - .read = raven_pci_io_read, - .write = raven_pci_io_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static uint64_t raven_intack_read(void *opaque, hwaddr addr, - unsigned int size) -{ - return pic_read_irq(isa_pic); -} - -static const MemoryRegionOps raven_intack_ops = { - .read = raven_intack_read, - .valid = { - .max_access_size = 1, - }, -}; - -static inline hwaddr raven_io_address(PREPPCIState *s, - hwaddr addr) -{ - if (s->contiguous_map == 0) { - /* 64 KB contiguous space for IOs */ - addr &= 0xFFFF; - } else { - /* 8 MB non-contiguous space for IOs */ - addr = (addr & 0x1F) | ((addr & 0x007FFF000) >> 7); - } - - /* FIXME: handle endianness switch */ - - return addr; -} - -static uint64_t raven_io_read(void *opaque, hwaddr addr, - unsigned int size) -{ - PREPPCIState *s = opaque; - uint8_t buf[4]; - - addr = raven_io_address(s, addr); - address_space_read(&s->pci_io_as, addr + 0x80000000, - MEMTXATTRS_UNSPECIFIED, buf, size); - - if (size == 1) { - return buf[0]; - } else if (size == 2) { - return lduw_le_p(buf); - } else if (size == 4) { - return ldl_le_p(buf); - } else { - g_assert_not_reached(); - } -} - -static void raven_io_write(void *opaque, hwaddr addr, - uint64_t val, unsigned int size) -{ - PREPPCIState *s = opaque; - uint8_t buf[4]; - - addr = raven_io_address(s, addr); - - if (size == 1) { - buf[0] = val; - } else if (size == 2) { - stw_le_p(buf, val); - } else if (size == 4) { - stl_le_p(buf, val); - } else { - g_assert_not_reached(); - } - - address_space_write(&s->pci_io_as, addr + 0x80000000, - MEMTXATTRS_UNSPECIFIED, buf, size); -} - -static const MemoryRegionOps raven_io_ops = { - .read = raven_io_read, - .write = raven_io_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl.max_access_size = 4, - .valid.unaligned = true, -}; - -static int raven_map_irq(PCIDevice *pci_dev, int irq_num) -{ - return (irq_num + (pci_dev->devfn >> 3)) & 1; -} - -static void raven_set_irq(void *opaque, int irq_num, int level) -{ - qemu_irq *pic = opaque; - - qemu_set_irq(pic[irq_num] , level); -} - -static AddressSpace *raven_pcihost_set_iommu(PCIBus *bus, void *opaque, - int devfn) -{ - PREPPCIState *s = opaque; - - return &s->bm_as; -} - -static void raven_change_gpio(void *opaque, int n, int level) -{ - PREPPCIState *s = opaque; - - s->contiguous_map = level; -} - -static void raven_pcihost_realizefn(DeviceState *d, Error **errp) -{ - SysBusDevice *dev = SYS_BUS_DEVICE(d); - PCIHostState *h = PCI_HOST_BRIDGE(dev); - PREPPCIState *s = RAVEN_PCI_HOST_BRIDGE(dev); - MemoryRegion *address_space_mem = get_system_memory(); - int i; - - for (i = 0; i < PCI_NUM_PINS; i++) { - sysbus_init_irq(dev, &s->irq[i]); - } - - qdev_init_gpio_in(d, raven_change_gpio, 1); - - pci_bus_irqs(&s->pci_bus, raven_set_irq, raven_map_irq, s->irq, - PCI_NUM_PINS); - - memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, s, - "pci-conf-idx", 4); - memory_region_add_subregion(&s->pci_io, 0xcf8, &h->conf_mem); - - memory_region_init_io(&h->data_mem, OBJECT(h), &pci_host_data_le_ops, s, - "pci-conf-data", 4); - memory_region_add_subregion(&s->pci_io, 0xcfc, &h->data_mem); - - memory_region_init_io(&h->mmcfg, OBJECT(s), &raven_pci_io_ops, s, - "pciio", 0x00400000); - memory_region_add_subregion(address_space_mem, 0x80800000, &h->mmcfg); - - memory_region_init_io(&s->pci_intack, OBJECT(s), &raven_intack_ops, s, - "pci-intack", 1); - memory_region_add_subregion(address_space_mem, 0xbffffff0, &s->pci_intack); - - /* TODO Remove once realize propagates to child devices. */ - object_property_set_bool(OBJECT(&s->pci_dev), true, "realized", errp); -} - -static void raven_pcihost_initfn(Object *obj) -{ - PCIHostState *h = PCI_HOST_BRIDGE(obj); - PREPPCIState *s = RAVEN_PCI_HOST_BRIDGE(obj); - MemoryRegion *address_space_mem = get_system_memory(); - DeviceState *pci_dev; - - memory_region_init(&s->pci_io, obj, "pci-io", 0x3f800000); - memory_region_init_io(&s->pci_io_non_contiguous, obj, &raven_io_ops, s, - "pci-io-non-contiguous", 0x00800000); - memory_region_init(&s->pci_memory, obj, "pci-memory", 0x3f000000); - address_space_init(&s->pci_io_as, &s->pci_io, "raven-io"); - - /* CPU address space */ - memory_region_add_subregion(address_space_mem, 0x80000000, &s->pci_io); - memory_region_add_subregion_overlap(address_space_mem, 0x80000000, - &s->pci_io_non_contiguous, 1); - memory_region_add_subregion(address_space_mem, 0xc0000000, &s->pci_memory); - pci_bus_new_inplace(&s->pci_bus, sizeof(s->pci_bus), DEVICE(obj), NULL, - &s->pci_memory, &s->pci_io, 0, TYPE_PCI_BUS); - - /* Bus master address space */ - memory_region_init(&s->bm, obj, "bm-raven", UINT32_MAX); - memory_region_init_alias(&s->bm_pci_memory_alias, obj, "bm-pci-memory", - &s->pci_memory, 0, - memory_region_size(&s->pci_memory)); - memory_region_init_alias(&s->bm_ram_alias, obj, "bm-system", - get_system_memory(), 0, 0x80000000); - memory_region_add_subregion(&s->bm, 0 , &s->bm_pci_memory_alias); - memory_region_add_subregion(&s->bm, 0x80000000, &s->bm_ram_alias); - address_space_init(&s->bm_as, &s->bm, "raven-bm"); - pci_setup_iommu(&s->pci_bus, raven_pcihost_set_iommu, s); - - h->bus = &s->pci_bus; - - object_initialize(&s->pci_dev, sizeof(s->pci_dev), TYPE_RAVEN_PCI_DEVICE); - pci_dev = DEVICE(&s->pci_dev); - qdev_set_parent_bus(pci_dev, BUS(&s->pci_bus)); - object_property_set_int(OBJECT(&s->pci_dev), PCI_DEVFN(0, 0), "addr", - NULL); - qdev_prop_set_bit(pci_dev, "multifunction", false); -} - -static void raven_realize(PCIDevice *d, Error **errp) -{ - RavenPCIState *s = RAVEN_PCI_DEVICE(d); - char *filename; - int bios_size = -1; - - d->config[0x0C] = 0x08; // cache_line_size - d->config[0x0D] = 0x10; // latency_timer - d->config[0x34] = 0x00; // capabilities_pointer - - memory_region_init_ram(&s->bios, OBJECT(s), "bios", BIOS_SIZE, - &error_fatal); - memory_region_set_readonly(&s->bios, true); - memory_region_add_subregion(get_system_memory(), (uint32_t)(-BIOS_SIZE), - &s->bios); - vmstate_register_ram_global(&s->bios); - if (s->bios_name) { - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, s->bios_name); - if (filename) { - if (s->elf_machine != EM_NONE) { - bios_size = load_elf(filename, NULL, NULL, NULL, - NULL, NULL, 1, s->elf_machine, 0, 0); - } - if (bios_size < 0) { - bios_size = get_image_size(filename); - if (bios_size > 0 && bios_size <= BIOS_SIZE) { - hwaddr bios_addr; - bios_size = (bios_size + 0xfff) & ~0xfff; - bios_addr = (uint32_t)(-BIOS_SIZE); - bios_size = load_image_targphys(filename, bios_addr, - bios_size); - } - } - } - 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); - } - g_free(filename); - } -} - -static const VMStateDescription vmstate_raven = { - .name = "raven", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, RavenPCIState), - VMSTATE_END_OF_LIST() - }, -}; - -static void raven_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = raven_realize; - k->vendor_id = PCI_VENDOR_ID_MOTOROLA; - k->device_id = PCI_DEVICE_ID_MOTOROLA_RAVEN; - k->revision = 0x00; - k->class_id = PCI_CLASS_BRIDGE_HOST; - dc->desc = "PReP Host Bridge - Motorola Raven"; - dc->vmsd = &vmstate_raven; - /* - * 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; -} - -static const TypeInfo raven_info = { - .name = TYPE_RAVEN_PCI_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(RavenPCIState), - .class_init = raven_class_init, -}; - -static Property raven_pcihost_properties[] = { - DEFINE_PROP_UINT32("elf-machine", PREPPCIState, pci_dev.elf_machine, - EM_NONE), - DEFINE_PROP_STRING("bios-name", PREPPCIState, pci_dev.bios_name), - DEFINE_PROP_END_OF_LIST() -}; - -static void raven_pcihost_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->realize = raven_pcihost_realizefn; - dc->props = raven_pcihost_properties; - dc->fw_name = "pci"; -} - -static const TypeInfo raven_pcihost_info = { - .name = TYPE_RAVEN_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(PREPPCIState), - .instance_init = raven_pcihost_initfn, - .class_init = raven_pcihost_class_init, -}; - -static void raven_register_types(void) -{ - type_register_static(&raven_pcihost_info); - type_register_static(&raven_info); -} - -type_init(raven_register_types) diff --git a/qemu/hw/pci-host/q35.c b/qemu/hw/pci-host/q35.c deleted file mode 100644 index 70f897e3a..000000000 --- a/qemu/hw/pci-host/q35.c +++ /dev/null @@ -1,560 +0,0 @@ -/* - * QEMU MCH/ICH9 PCI Bridge Emulation - * - * Copyright (c) 2006 Fabrice Bellard - * Copyright (c) 2009, 2010, 2011 - * Isaku Yamahata - * VA Linux Systems Japan K.K. - * Copyright (C) 2012 Jason Baron - * - * This is based on piix.c, but heavily modified. - * - * 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/pci-host/q35.h" -#include "qapi/error.h" -#include "qapi/visitor.h" - -/**************************************************************************** - * Q35 host - */ - -static void q35_host_realize(DeviceState *dev, Error **errp) -{ - PCIHostState *pci = PCI_HOST_BRIDGE(dev); - Q35PCIHost *s = Q35_HOST_DEVICE(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - - sysbus_add_io(sbd, MCH_HOST_BRIDGE_CONFIG_ADDR, &pci->conf_mem); - sysbus_init_ioports(sbd, MCH_HOST_BRIDGE_CONFIG_ADDR, 4); - - sysbus_add_io(sbd, MCH_HOST_BRIDGE_CONFIG_DATA, &pci->data_mem); - sysbus_init_ioports(sbd, MCH_HOST_BRIDGE_CONFIG_DATA, 4); - - pci->bus = pci_bus_new(DEVICE(s), "pcie.0", - s->mch.pci_address_space, s->mch.address_space_io, - 0, TYPE_PCIE_BUS); - qdev_set_parent_bus(DEVICE(&s->mch), BUS(pci->bus)); - qdev_init_nofail(DEVICE(&s->mch)); -} - -static const char *q35_host_root_bus_path(PCIHostState *host_bridge, - PCIBus *rootbus) -{ - Q35PCIHost *s = Q35_HOST_DEVICE(host_bridge); - - /* For backwards compat with old device paths */ - if (s->mch.short_root_bus) { - return "0000"; - } - return "0000:00"; -} - -static void q35_host_get_pci_hole_start(Object *obj, Visitor *v, - 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, name, &value, errp); -} - -static void q35_host_get_pci_hole_end(Object *obj, Visitor *v, - 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, name, &value, errp); -} - -static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v, - 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, name, &w64.begin, errp); -} - -static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v, - 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, name, &w64.end, 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, name, &value, errp); -} - -static Property mch_props[] = { - DEFINE_PROP_UINT64(PCIE_HOST_MCFG_BASE, Q35PCIHost, parent_obj.base_addr, - MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT), - DEFINE_PROP_SIZE(PCI_HOST_PROP_PCI_HOLE64_SIZE, Q35PCIHost, - mch.pci_hole64_size, DEFAULT_PCI_HOLE64_SIZE), - DEFINE_PROP_UINT32("short_root_bus", Q35PCIHost, mch.short_root_bus, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void q35_host_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); - - hc->root_bus_path = q35_host_root_bus_path; - dc->realize = q35_host_realize; - dc->props = mch_props; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->fw_name = "pci"; -} - -static void q35_host_initfn(Object *obj) -{ - Q35PCIHost *s = Q35_HOST_DEVICE(obj); - PCIHostState *phb = PCI_HOST_BRIDGE(obj); - - memory_region_init_io(&phb->conf_mem, obj, &pci_host_conf_le_ops, phb, - "pci-conf-idx", 4); - memory_region_init_io(&phb->data_mem, obj, &pci_host_data_le_ops, phb, - "pci-conf-data", 4); - - object_initialize(&s->mch, sizeof(s->mch), TYPE_MCH_PCI_DEVICE); - object_property_add_child(OBJECT(s), "mch", OBJECT(&s->mch), NULL); - qdev_prop_set_uint32(DEVICE(&s->mch), "addr", PCI_DEVFN(0, 0)); - qdev_prop_set_bit(DEVICE(&s->mch), "multifunction", false); - - object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_START, "int", - q35_host_get_pci_hole_start, - NULL, NULL, NULL, NULL); - - object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_END, "int", - q35_host_get_pci_hole_end, - NULL, NULL, NULL, NULL); - - object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_START, "int", - q35_host_get_pci_hole64_start, - NULL, NULL, NULL, NULL); - - object_property_add(obj, PCI_HOST_PROP_PCI_HOLE64_END, "int", - q35_host_get_pci_hole64_end, - NULL, NULL, NULL, NULL); - - object_property_add(obj, PCIE_HOST_MCFG_SIZE, "int", - q35_host_get_mmcfg_size, - NULL, NULL, NULL, NULL); - - /* Leave enough space for the biggest MCFG BAR */ - /* TODO: this matches current bios behaviour, but - * it's not a power of two, which means an MTRR - * can't cover it exactly. - */ - s->mch.pci_info.w32.begin = MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT + - MCH_HOST_BRIDGE_PCIEXBAR_MAX; - s->mch.pci_info.w32.end = IO_APIC_DEFAULT_ADDRESS; -} - -static const TypeInfo q35_host_info = { - .name = TYPE_Q35_HOST_DEVICE, - .parent = TYPE_PCIE_HOST_BRIDGE, - .instance_size = sizeof(Q35PCIHost), - .instance_init = q35_host_initfn, - .class_init = q35_host_class_init, -}; - -/**************************************************************************** - * MCH D0:F0 - */ - -static uint64_t tseg_blackhole_read(void *ptr, hwaddr reg, unsigned size) -{ - return 0xffffffff; -} - -static void tseg_blackhole_write(void *opaque, hwaddr addr, uint64_t val, - unsigned width) -{ - /* nothing */ -} - -static const MemoryRegionOps tseg_blackhole_ops = { - .read = tseg_blackhole_read, - .write = tseg_blackhole_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .impl.min_access_size = 4, - .impl.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -/* PCIe MMCFG */ -static void mch_update_pciexbar(MCHPCIState *mch) -{ - PCIDevice *pci_dev = PCI_DEVICE(mch); - BusState *bus = qdev_get_parent_bus(DEVICE(mch)); - PCIExpressHost *pehb = PCIE_HOST_BRIDGE(bus->parent); - - uint64_t pciexbar; - int enable; - uint64_t addr; - uint64_t addr_mask; - uint32_t length; - - pciexbar = pci_get_quad(pci_dev->config + MCH_HOST_BRIDGE_PCIEXBAR); - enable = pciexbar & MCH_HOST_BRIDGE_PCIEXBAREN; - addr_mask = MCH_HOST_BRIDGE_PCIEXBAR_ADMSK; - switch (pciexbar & MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_MASK) { - case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_256M: - length = 256 * 1024 * 1024; - break; - case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_128M: - length = 128 * 1024 * 1024; - addr_mask |= MCH_HOST_BRIDGE_PCIEXBAR_128ADMSK | - MCH_HOST_BRIDGE_PCIEXBAR_64ADMSK; - break; - case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_64M: - length = 64 * 1024 * 1024; - addr_mask |= MCH_HOST_BRIDGE_PCIEXBAR_64ADMSK; - break; - case MCH_HOST_BRIDGE_PCIEXBAR_LENGTH_RVD: - default: - enable = 0; - length = 0; - abort(); - break; - } - addr = pciexbar & addr_mask; - pcie_host_mmcfg_update(pehb, enable, addr, length); - /* Leave enough space for the MCFG BAR */ - /* - * TODO: this matches current bios behaviour, but it's not a power of two, - * which means an MTRR can't cover it exactly. - */ - if (enable) { - mch->pci_info.w32.begin = addr + length; - } else { - mch->pci_info.w32.begin = MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT; - } -} - -/* PAM */ -static void mch_update_pam(MCHPCIState *mch) -{ - PCIDevice *pd = PCI_DEVICE(mch); - int i; - - memory_region_transaction_begin(); - for (i = 0; i < 13; i++) { - pam_update(&mch->pam_regions[i], i, - pd->config[MCH_HOST_BRIDGE_PAM0 + ((i + 1) / 2)]); - } - memory_region_transaction_commit(); -} - -/* SMRAM */ -static void mch_update_smram(MCHPCIState *mch) -{ - PCIDevice *pd = PCI_DEVICE(mch); - bool h_smrame = (pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_H_SMRAME); - uint32_t tseg_size; - - /* implement SMRAM.D_LCK */ - if (pd->config[MCH_HOST_BRIDGE_SMRAM] & MCH_HOST_BRIDGE_SMRAM_D_LCK) { - pd->config[MCH_HOST_BRIDGE_SMRAM] &= ~MCH_HOST_BRIDGE_SMRAM_D_OPEN; - pd->wmask[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_WMASK_LCK; - pd->wmask[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_WMASK_LCK; - } - - memory_region_transaction_begin(); - - if (pd->config[MCH_HOST_BRIDGE_SMRAM] & SMRAM_D_OPEN) { - /* Hide (!) low SMRAM if H_SMRAME = 1 */ - memory_region_set_enabled(&mch->smram_region, h_smrame); - /* Show high SMRAM if H_SMRAME = 1 */ - memory_region_set_enabled(&mch->open_high_smram, h_smrame); - } else { - /* Hide high SMRAM and low SMRAM */ - memory_region_set_enabled(&mch->smram_region, true); - memory_region_set_enabled(&mch->open_high_smram, false); - } - - if (pd->config[MCH_HOST_BRIDGE_SMRAM] & SMRAM_G_SMRAME) { - memory_region_set_enabled(&mch->low_smram, !h_smrame); - memory_region_set_enabled(&mch->high_smram, h_smrame); - } else { - memory_region_set_enabled(&mch->low_smram, false); - memory_region_set_enabled(&mch->high_smram, false); - } - - if (pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_T_EN) { - switch (pd->config[MCH_HOST_BRIDGE_ESMRAMC] & - MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK) { - case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_1MB: - tseg_size = 1024 * 1024; - break; - case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_2MB: - tseg_size = 1024 * 1024 * 2; - break; - case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_8MB: - tseg_size = 1024 * 1024 * 8; - break; - default: - tseg_size = 0; - break; - } - } else { - tseg_size = 0; - } - memory_region_del_subregion(mch->system_memory, &mch->tseg_blackhole); - memory_region_set_enabled(&mch->tseg_blackhole, tseg_size); - memory_region_set_size(&mch->tseg_blackhole, tseg_size); - memory_region_add_subregion_overlap(mch->system_memory, - mch->below_4g_mem_size - tseg_size, - &mch->tseg_blackhole, 1); - - memory_region_set_enabled(&mch->tseg_window, tseg_size); - memory_region_set_size(&mch->tseg_window, tseg_size); - memory_region_set_address(&mch->tseg_window, - mch->below_4g_mem_size - tseg_size); - memory_region_set_alias_offset(&mch->tseg_window, - mch->below_4g_mem_size - tseg_size); - - memory_region_transaction_commit(); -} - -static void mch_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len) -{ - MCHPCIState *mch = MCH_PCI_DEVICE(d); - - pci_default_write_config(d, address, val, len); - - if (ranges_overlap(address, len, MCH_HOST_BRIDGE_PAM0, - MCH_HOST_BRIDGE_PAM_SIZE)) { - mch_update_pam(mch); - } - - if (ranges_overlap(address, len, MCH_HOST_BRIDGE_PCIEXBAR, - MCH_HOST_BRIDGE_PCIEXBAR_SIZE)) { - mch_update_pciexbar(mch); - } - - if (ranges_overlap(address, len, MCH_HOST_BRIDGE_SMRAM, - MCH_HOST_BRIDGE_SMRAM_SIZE)) { - mch_update_smram(mch); - } -} - -static void mch_update(MCHPCIState *mch) -{ - mch_update_pciexbar(mch); - mch_update_pam(mch); - mch_update_smram(mch); -} - -static int mch_post_load(void *opaque, int version_id) -{ - MCHPCIState *mch = opaque; - mch_update(mch); - return 0; -} - -static const VMStateDescription vmstate_mch = { - .name = "mch", - .version_id = 1, - .minimum_version_id = 1, - .post_load = mch_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, MCHPCIState), - /* Used to be smm_enabled, which was basically always zero because - * SeaBIOS hardly uses SMM. SMRAM is now handled by CPU code. - */ - VMSTATE_UNUSED(1), - VMSTATE_END_OF_LIST() - } -}; - -static void mch_reset(DeviceState *qdev) -{ - PCIDevice *d = PCI_DEVICE(qdev); - MCHPCIState *mch = MCH_PCI_DEVICE(d); - - pci_set_quad(d->config + MCH_HOST_BRIDGE_PCIEXBAR, - MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT); - - d->config[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_DEFAULT; - d->config[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_DEFAULT; - d->wmask[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_WMASK; - d->wmask[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_WMASK; - - mch_update(mch); -} - -static AddressSpace *q35_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) -{ - IntelIOMMUState *s = opaque; - VTDAddressSpace *vtd_as; - - assert(0 <= devfn && devfn <= VTD_PCI_DEVFN_MAX); - - vtd_as = vtd_find_add_as(s, bus, devfn); - return &vtd_as->as; -} - -static void mch_init_dmar(MCHPCIState *mch) -{ - PCIBus *pci_bus = PCI_BUS(qdev_get_parent_bus(DEVICE(mch))); - - mch->iommu = INTEL_IOMMU_DEVICE(qdev_create(NULL, TYPE_INTEL_IOMMU_DEVICE)); - object_property_add_child(OBJECT(mch), "intel-iommu", - OBJECT(mch->iommu), NULL); - qdev_init_nofail(DEVICE(mch->iommu)); - sysbus_mmio_map(SYS_BUS_DEVICE(mch->iommu), 0, Q35_HOST_BRIDGE_IOMMU_ADDR); - - pci_setup_iommu(pci_bus, q35_host_dma_iommu, mch->iommu); -} - -static void mch_realize(PCIDevice *d, Error **errp) -{ - int i; - MCHPCIState *mch = MCH_PCI_DEVICE(d); - - /* setup pci memory mapping */ - pc_pci_as_mapping_init(OBJECT(mch), mch->system_memory, - mch->pci_address_space); - - /* if *disabled* show SMRAM to all CPUs */ - memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", - mch->pci_address_space, 0xa0000, 0x20000); - memory_region_add_subregion_overlap(mch->system_memory, 0xa0000, - &mch->smram_region, 1); - memory_region_set_enabled(&mch->smram_region, true); - - memory_region_init_alias(&mch->open_high_smram, OBJECT(mch), "smram-open-high", - mch->ram_memory, 0xa0000, 0x20000); - memory_region_add_subregion_overlap(mch->system_memory, 0xfeda0000, - &mch->open_high_smram, 1); - memory_region_set_enabled(&mch->open_high_smram, false); - - /* smram, as seen by SMM CPUs */ - memory_region_init(&mch->smram, OBJECT(mch), "smram", 1ull << 32); - memory_region_set_enabled(&mch->smram, true); - memory_region_init_alias(&mch->low_smram, OBJECT(mch), "smram-low", - mch->ram_memory, 0xa0000, 0x20000); - memory_region_set_enabled(&mch->low_smram, true); - memory_region_add_subregion(&mch->smram, 0xa0000, &mch->low_smram); - memory_region_init_alias(&mch->high_smram, OBJECT(mch), "smram-high", - mch->ram_memory, 0xa0000, 0x20000); - memory_region_set_enabled(&mch->high_smram, true); - memory_region_add_subregion(&mch->smram, 0xfeda0000, &mch->high_smram); - - memory_region_init_io(&mch->tseg_blackhole, OBJECT(mch), - &tseg_blackhole_ops, NULL, - "tseg-blackhole", 0); - memory_region_set_enabled(&mch->tseg_blackhole, false); - memory_region_add_subregion_overlap(mch->system_memory, - mch->below_4g_mem_size, - &mch->tseg_blackhole, 1); - - memory_region_init_alias(&mch->tseg_window, OBJECT(mch), "tseg-window", - mch->ram_memory, mch->below_4g_mem_size, 0); - memory_region_set_enabled(&mch->tseg_window, false); - memory_region_add_subregion(&mch->smram, mch->below_4g_mem_size, - &mch->tseg_window); - object_property_add_const_link(qdev_get_machine(), "smram", - OBJECT(&mch->smram), &error_abort); - - init_pam(DEVICE(mch), mch->ram_memory, mch->system_memory, - mch->pci_address_space, &mch->pam_regions[0], - PAM_BIOS_BASE, PAM_BIOS_SIZE); - for (i = 0; i < 12; ++i) { - init_pam(DEVICE(mch), mch->ram_memory, mch->system_memory, - mch->pci_address_space, &mch->pam_regions[i+1], - PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); - } - /* Intel IOMMU (VT-d) */ - if (object_property_get_bool(qdev_get_machine(), "iommu", NULL)) { - mch_init_dmar(mch); - } -} - -uint64_t mch_mcfg_base(void) -{ - bool ambiguous; - Object *o = object_resolve_path_type("", TYPE_MCH_PCI_DEVICE, &ambiguous); - if (!o) { - return 0; - } - return MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT; -} - -static void mch_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = mch_realize; - k->config_write = mch_write_config; - dc->reset = mch_reset; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->desc = "Host bridge"; - dc->vmsd = &vmstate_mch; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_Q35_MCH; - k->revision = MCH_HOST_BRIDGE_REVISION_DEFAULT; - k->class_id = PCI_CLASS_BRIDGE_HOST; - /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo mch_info = { - .name = TYPE_MCH_PCI_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(MCHPCIState), - .class_init = mch_class_init, -}; - -static void q35_register(void) -{ - type_register_static(&mch_info); - type_register_static(&q35_host_info); -} - -type_init(q35_register); diff --git a/qemu/hw/pci-host/uninorth.c b/qemu/hw/pci-host/uninorth.c deleted file mode 100644 index 15b105423..000000000 --- a/qemu/hw/pci-host/uninorth.c +++ /dev/null @@ -1,533 +0,0 @@ -/* - * QEMU Uninorth PCI host (for all Mac99 and newer machines) - * - * Copyright (c) 2006 Fabrice Bellard - * - * 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/ppc/mac.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" - -/* debug UniNorth */ -//#define DEBUG_UNIN - -#ifdef DEBUG_UNIN -#define UNIN_DPRINTF(fmt, ...) \ - do { printf("UNIN: " fmt , ## __VA_ARGS__); } while (0) -#else -#define UNIN_DPRINTF(fmt, ...) -#endif - -static const int unin_irq_line[] = { 0x1b, 0x1c, 0x1d, 0x1e }; - -#define TYPE_UNI_NORTH_PCI_HOST_BRIDGE "uni-north-pci-pcihost" -#define TYPE_UNI_NORTH_AGP_HOST_BRIDGE "uni-north-agp-pcihost" -#define TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE "uni-north-internal-pci-pcihost" -#define TYPE_U3_AGP_HOST_BRIDGE "u3-agp-pcihost" - -#define UNI_NORTH_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_PCI_HOST_BRIDGE) -#define UNI_NORTH_AGP_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_AGP_HOST_BRIDGE) -#define UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE) -#define U3_AGP_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_U3_AGP_HOST_BRIDGE) - -typedef struct UNINState { - PCIHostState parent_obj; - - MemoryRegion pci_mmio; - MemoryRegion pci_hole; -} UNINState; - -static int pci_unin_map_irq(PCIDevice *pci_dev, int irq_num) -{ - int retval; - int devfn = pci_dev->devfn & 0x00FFFFFF; - - retval = (((devfn >> 11) & 0x1F) + irq_num) & 3; - - return retval; -} - -static void pci_unin_set_irq(void *opaque, int irq_num, int level) -{ - qemu_irq *pic = opaque; - - UNIN_DPRINTF("%s: setting INT %d = %d\n", __func__, - unin_irq_line[irq_num], level); - qemu_set_irq(pic[unin_irq_line[irq_num]], level); -} - -static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr) -{ - uint32_t retval; - - if (reg & (1u << 31)) { - /* XXX OpenBIOS compatibility hack */ - retval = reg | (addr & 3); - } else if (reg & 1) { - /* CFA1 style */ - retval = (reg & ~7u) | (addr & 7); - } else { - uint32_t slot, func; - - /* Grab CFA0 style values */ - slot = ctz32(reg & 0xfffff800); - if (slot == 32) { - slot = -1; /* XXX: should this be 0? */ - } - func = (reg >> 8) & 7; - - /* ... and then convert them to x86 format */ - /* config pointer */ - retval = (reg & (0xff - 7)) | (addr & 7); - /* slot */ - retval |= slot << 11; - /* fn */ - retval |= func << 8; - } - - - UNIN_DPRINTF("Converted config space accessor %08x/%08x -> %08x\n", - reg, addr, retval); - - return retval; -} - -static void unin_data_write(void *opaque, hwaddr addr, - uint64_t val, unsigned len) -{ - UNINState *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); - 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), - val, len); -} - -static uint64_t unin_data_read(void *opaque, hwaddr addr, - unsigned len) -{ - UNINState *s = opaque; - PCIHostState *phb = PCI_HOST_BRIDGE(s); - uint32_t val; - - 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", - addr, len, val); - return val; -} - -static const MemoryRegionOps unin_data_ops = { - .read = unin_data_read, - .write = unin_data_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static int pci_unin_main_init_device(SysBusDevice *dev) -{ - PCIHostState *h; - - /* Use values found on a real PowerMac */ - /* Uninorth main bus */ - h = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&h->data_mem, OBJECT(h), &unin_data_ops, dev, - "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &h->conf_mem); - sysbus_init_mmio(dev, &h->data_mem); - - return 0; -} - - -static int pci_u3_agp_init_device(SysBusDevice *dev) -{ - PCIHostState *h; - - /* Uninorth U3 AGP bus */ - h = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&h->data_mem, OBJECT(h), &unin_data_ops, dev, - "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &h->conf_mem); - sysbus_init_mmio(dev, &h->data_mem); - - return 0; -} - -static int pci_unin_agp_init_device(SysBusDevice *dev) -{ - PCIHostState *h; - - /* Uninorth AGP bus */ - h = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&h->data_mem, OBJECT(h), &pci_host_data_le_ops, - dev, "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &h->conf_mem); - sysbus_init_mmio(dev, &h->data_mem); - return 0; -} - -static int pci_unin_internal_init_device(SysBusDevice *dev) -{ - PCIHostState *h; - - /* Uninorth internal bus */ - h = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&h->data_mem, OBJECT(h), &pci_host_data_le_ops, - dev, "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &h->conf_mem); - sysbus_init_mmio(dev, &h->data_mem); - return 0; -} - -PCIBus *pci_pmac_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io) -{ - DeviceState *dev; - SysBusDevice *s; - PCIHostState *h; - UNINState *d; - - /* Use values found on a real PowerMac */ - /* Uninorth main bus */ - dev = qdev_create(NULL, TYPE_UNI_NORTH_PCI_HOST_BRIDGE); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - h = PCI_HOST_BRIDGE(s); - d = UNI_NORTH_PCI_HOST_BRIDGE(dev); - memory_region_init(&d->pci_mmio, OBJECT(d), "pci-mmio", 0x100000000ULL); - memory_region_init_alias(&d->pci_hole, OBJECT(d), "pci-hole", &d->pci_mmio, - 0x80000000ULL, 0x10000000ULL); - memory_region_add_subregion(address_space_mem, 0x80000000ULL, - &d->pci_hole); - - h->bus = pci_register_bus(dev, NULL, - pci_unin_set_irq, pci_unin_map_irq, - pic, - &d->pci_mmio, - address_space_io, - PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); - -#if 0 - pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north"); -#endif - - sysbus_mmio_map(s, 0, 0xf2800000); - sysbus_mmio_map(s, 1, 0xf2c00000); - - /* DEC 21154 bridge */ -#if 0 - /* XXX: not activated as PPC BIOS doesn't handle multiple buses properly */ - pci_create_simple(h->bus, PCI_DEVFN(12, 0), "dec-21154"); -#endif - - /* Uninorth AGP bus */ - pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-agp"); - dev = qdev_create(NULL, TYPE_UNI_NORTH_AGP_HOST_BRIDGE); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(s, 0, 0xf0800000); - sysbus_mmio_map(s, 1, 0xf0c00000); - - /* Uninorth internal bus */ -#if 0 - /* XXX: not needed for now */ - pci_create_simple(h->bus, PCI_DEVFN(14, 0), - "uni-north-internal-pci"); - dev = qdev_create(NULL, TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(s, 0, 0xf4800000); - sysbus_mmio_map(s, 1, 0xf4c00000); -#endif - - return h->bus; -} - -PCIBus *pci_pmac_u3_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io) -{ - DeviceState *dev; - SysBusDevice *s; - PCIHostState *h; - UNINState *d; - - /* Uninorth AGP bus */ - - dev = qdev_create(NULL, TYPE_U3_AGP_HOST_BRIDGE); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - h = PCI_HOST_BRIDGE(dev); - d = U3_AGP_HOST_BRIDGE(dev); - - memory_region_init(&d->pci_mmio, OBJECT(d), "pci-mmio", 0x100000000ULL); - memory_region_init_alias(&d->pci_hole, OBJECT(d), "pci-hole", &d->pci_mmio, - 0x80000000ULL, 0x70000000ULL); - memory_region_add_subregion(address_space_mem, 0x80000000ULL, - &d->pci_hole); - - h->bus = pci_register_bus(dev, NULL, - pci_unin_set_irq, pci_unin_map_irq, - pic, - &d->pci_mmio, - address_space_io, - PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); - - sysbus_mmio_map(s, 0, 0xf0800000); - sysbus_mmio_map(s, 1, 0xf0c00000); - - pci_create_simple(h->bus, 11 << 3, "u3-agp"); - - return h->bus; -} - -static void unin_main_pci_host_realize(PCIDevice *d, Error **errp) -{ - d->config[0x0C] = 0x08; // cache_line_size - d->config[0x0D] = 0x10; // latency_timer - d->config[0x34] = 0x00; // capabilities_pointer -} - -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) -{ - /* cache line size */ - d->config[0x0C] = 0x08; - /* latency timer */ - d->config[0x0D] = 0x10; -} - -static void unin_internal_pci_host_realize(PCIDevice *d, Error **errp) -{ - d->config[0x0C] = 0x08; // cache_line_size - d->config[0x0D] = 0x10; // latency_timer - d->config[0x34] = 0x00; // capabilities_pointer -} - -static void unin_main_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = unin_main_pci_host_realize; - k->vendor_id = PCI_VENDOR_ID_APPLE; - k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_PCI; - k->revision = 0x00; - k->class_id = PCI_CLASS_BRIDGE_HOST; - /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo unin_main_pci_host_info = { - .name = "uni-north-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = unin_main_pci_host_class_init, -}; - -static void u3_agp_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = u3_agp_pci_host_realize; - k->vendor_id = PCI_VENDOR_ID_APPLE; - k->device_id = PCI_DEVICE_ID_APPLE_U3_AGP; - k->revision = 0x00; - k->class_id = PCI_CLASS_BRIDGE_HOST; - /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo u3_agp_pci_host_info = { - .name = "u3-agp", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = u3_agp_pci_host_class_init, -}; - -static void unin_agp_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = unin_agp_pci_host_realize; - k->vendor_id = PCI_VENDOR_ID_APPLE; - k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_AGP; - k->revision = 0x00; - k->class_id = PCI_CLASS_BRIDGE_HOST; - /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo unin_agp_pci_host_info = { - .name = "uni-north-agp", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = unin_agp_pci_host_class_init, -}; - -static void unin_internal_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = unin_internal_pci_host_realize; - k->vendor_id = PCI_VENDOR_ID_APPLE; - k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_I_PCI; - k->revision = 0x00; - k->class_id = PCI_CLASS_BRIDGE_HOST; - /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo unin_internal_pci_host_info = { - .name = "uni-north-internal-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = unin_internal_pci_host_class_init, -}; - -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 = { - .name = TYPE_UNI_NORTH_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(UNINState), - .class_init = pci_unin_main_class_init, -}; - -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 = { - .name = TYPE_U3_AGP_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(UNINState), - .class_init = pci_u3_agp_class_init, -}; - -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 = { - .name = TYPE_UNI_NORTH_AGP_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(UNINState), - .class_init = pci_unin_agp_class_init, -}; - -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 = { - .name = TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(UNINState), - .class_init = pci_unin_internal_class_init, -}; - -static void unin_register_types(void) -{ - type_register_static(&unin_main_pci_host_info); - type_register_static(&u3_agp_pci_host_info); - type_register_static(&unin_agp_pci_host_info); - type_register_static(&unin_internal_pci_host_info); - - type_register_static(&pci_unin_main_info); - type_register_static(&pci_u3_agp_info); - type_register_static(&pci_unin_agp_info); - type_register_static(&pci_unin_internal_info); -} - -type_init(unin_register_types) diff --git a/qemu/hw/pci-host/versatile.c b/qemu/hw/pci-host/versatile.c deleted file mode 100644 index 339ec2c50..000000000 --- a/qemu/hw/pci-host/versatile.c +++ /dev/null @@ -1,549 +0,0 @@ -/* - * ARM Versatile/PB PCI host controller - * - * Copyright (c) 2006-2009 CodeSourcery. - * Written by Paul Brook - * - * 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" -#include "hw/pci/pci_host.h" -#include "exec/address-spaces.h" - -/* Old and buggy versions of QEMU used the wrong mapping from - * PCI IRQs to system interrupt lines. Unfortunately the Linux - * kernel also had the corresponding bug in setting up interrupts - * (so older kernels work on QEMU and not on real hardware). - * We automatically detect these broken kernels and flip back - * to the broken irq mapping by spotting guest writes to the - * PCI_INTERRUPT_LINE register to see where the guest thinks - * interrupts are going to be routed. So we start in state - * ASSUME_OK on reset, and transition to either BROKEN or - * FORCE_OK at the first write to an INTERRUPT_LINE register for - * a slot where broken and correct interrupt mapping would differ. - * Once in either BROKEN or FORCE_OK we never transition again; - * this allows a newer kernel to use the INTERRUPT_LINE - * registers arbitrarily once it has indicated that it isn't - * broken in its init code somewhere. - * - * Unfortunately we have to cope with multiple different - * variants on the broken kernel behaviour: - * phase I (before kernel commit 1bc39ac5d) kernels assume old - * QEMU behaviour, so they use IRQ 27 for all slots - * phase II (1bc39ac5d and later, but before e3e92a7be6) kernels - * swizzle IRQs between slots, but do it wrongly, so they - * work only for every fourth PCI card, and only if (like old - * QEMU) the PCI host device is at slot 0 rather than where - * the h/w actually puts it - * phase III (e3e92a7be6 and later) kernels still swizzle IRQs between - * slots wrongly, but add a fixed offset of 64 to everything - * they write to PCI_INTERRUPT_LINE. - * - * We live in hope of a mythical phase IV kernel which might - * actually behave in ways that work on the hardware. Such a - * kernel should probably start off by writing some value neither - * 27 nor 91 to slot zero's PCI_INTERRUPT_LINE register to - * disable the autodetection. After that it can do what it likes. - * - * Slot % 4 | hw | I | II | III - * ------------------------------- - * 0 | 29 | 27 | 27 | 91 - * 1 | 30 | 27 | 28 | 92 - * 2 | 27 | 27 | 29 | 93 - * 3 | 28 | 27 | 30 | 94 - * - * Since our autodetection is not perfect we also provide a - * property so the user can make us start in BROKEN or FORCE_OK - * on reset if they know they have a bad or good kernel. - */ -enum { - PCI_VPB_IRQMAP_ASSUME_OK, - PCI_VPB_IRQMAP_BROKEN, - PCI_VPB_IRQMAP_FORCE_OK, -}; - -typedef struct { - PCIHostState parent_obj; - - qemu_irq irq[4]; - MemoryRegion controlregs; - MemoryRegion mem_config; - MemoryRegion mem_config2; - /* Containers representing the PCI address spaces */ - MemoryRegion pci_io_space; - MemoryRegion pci_mem_space; - /* Alias regions into PCI address spaces which we expose as sysbus regions. - * The offsets into pci_mem_space are controlled by the imap registers. - */ - MemoryRegion pci_io_window; - MemoryRegion pci_mem_window[3]; - PCIBus pci_bus; - PCIDevice pci_dev; - - /* Constant for life of device: */ - int realview; - uint32_t mem_win_size[3]; - uint8_t irq_mapping_prop; - - /* Variable state: */ - uint32_t imap[3]; - uint32_t smap[3]; - uint32_t selfid; - uint32_t flags; - uint8_t irq_mapping; -} PCIVPBState; - -static void pci_vpb_update_window(PCIVPBState *s, int i) -{ - /* Adjust the offset of the alias region we use for - * the memory window i to account for a change in the - * value of the corresponding IMAP register. - * Note that the semantics of the IMAP register differ - * for realview and versatile variants of the controller. - */ - hwaddr offset; - if (s->realview) { - /* Top bits of register (masked according to window size) provide - * top bits of PCI address. - */ - offset = s->imap[i] & ~(s->mem_win_size[i] - 1); - } else { - /* Bottom 4 bits of register provide top 4 bits of PCI address */ - offset = s->imap[i] << 28; - } - memory_region_set_alias_offset(&s->pci_mem_window[i], offset); -} - -static void pci_vpb_update_all_windows(PCIVPBState *s) -{ - /* Update all alias windows based on the current register state */ - int i; - - for (i = 0; i < 3; i++) { - pci_vpb_update_window(s, i); - } -} - -static int pci_vpb_post_load(void *opaque, int version_id) -{ - PCIVPBState *s = opaque; - pci_vpb_update_all_windows(s); - return 0; -} - -static const VMStateDescription pci_vpb_vmstate = { - .name = "versatile-pci", - .version_id = 1, - .minimum_version_id = 1, - .post_load = pci_vpb_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(imap, PCIVPBState, 3), - VMSTATE_UINT32_ARRAY(smap, PCIVPBState, 3), - VMSTATE_UINT32(selfid, PCIVPBState), - VMSTATE_UINT32(flags, PCIVPBState), - VMSTATE_UINT8(irq_mapping, PCIVPBState), - VMSTATE_END_OF_LIST() - } -}; - -#define TYPE_VERSATILE_PCI "versatile_pci" -#define PCI_VPB(obj) \ - OBJECT_CHECK(PCIVPBState, (obj), TYPE_VERSATILE_PCI) - -#define TYPE_VERSATILE_PCI_HOST "versatile_pci_host" -#define PCI_VPB_HOST(obj) \ - OBJECT_CHECK(PCIDevice, (obj), TYPE_VERSATILE_PCIHOST) - -typedef enum { - PCI_IMAP0 = 0x0, - PCI_IMAP1 = 0x4, - PCI_IMAP2 = 0x8, - PCI_SELFID = 0xc, - PCI_FLAGS = 0x10, - PCI_SMAP0 = 0x14, - PCI_SMAP1 = 0x18, - PCI_SMAP2 = 0x1c, -} PCIVPBControlRegs; - -static void pci_vpb_reg_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - PCIVPBState *s = opaque; - - switch (addr) { - case PCI_IMAP0: - case PCI_IMAP1: - case PCI_IMAP2: - { - int win = (addr - PCI_IMAP0) >> 2; - s->imap[win] = val; - pci_vpb_update_window(s, win); - break; - } - case PCI_SELFID: - s->selfid = val; - break; - case PCI_FLAGS: - s->flags = val; - break; - case PCI_SMAP0: - case PCI_SMAP1: - case PCI_SMAP2: - { - int win = (addr - PCI_SMAP0) >> 2; - s->smap[win] = val; - break; - } - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pci_vpb_reg_write: Bad offset %x\n", (int)addr); - break; - } -} - -static uint64_t pci_vpb_reg_read(void *opaque, hwaddr addr, - unsigned size) -{ - PCIVPBState *s = opaque; - - switch (addr) { - case PCI_IMAP0: - case PCI_IMAP1: - case PCI_IMAP2: - { - int win = (addr - PCI_IMAP0) >> 2; - return s->imap[win]; - } - case PCI_SELFID: - return s->selfid; - case PCI_FLAGS: - return s->flags; - case PCI_SMAP0: - case PCI_SMAP1: - case PCI_SMAP2: - { - int win = (addr - PCI_SMAP0) >> 2; - return s->smap[win]; - } - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pci_vpb_reg_read: Bad offset %x\n", (int)addr); - return 0; - } -} - -static const MemoryRegionOps pci_vpb_reg_ops = { - .read = pci_vpb_reg_read, - .write = pci_vpb_reg_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static int pci_vpb_broken_irq(int slot, int irq) -{ - /* Determine whether this IRQ value for this slot represents a - * known broken Linux kernel behaviour for this slot. - * Return one of the PCI_VPB_IRQMAP_ constants: - * BROKEN : if this definitely looks like a broken kernel - * FORCE_OK : if this definitely looks good - * ASSUME_OK : if we can't tell - */ - slot %= PCI_NUM_PINS; - - if (irq == 27) { - if (slot == 2) { - /* Might be a Phase I kernel, or might be a fixed kernel, - * since slot 2 is where we expect this IRQ. - */ - return PCI_VPB_IRQMAP_ASSUME_OK; - } - /* Phase I kernel */ - return PCI_VPB_IRQMAP_BROKEN; - } - if (irq == slot + 27) { - /* Phase II kernel */ - return PCI_VPB_IRQMAP_BROKEN; - } - if (irq == slot + 27 + 64) { - /* Phase III kernel */ - return PCI_VPB_IRQMAP_BROKEN; - } - /* Anything else must be a fixed kernel, possibly using an - * arbitrary irq map. - */ - return PCI_VPB_IRQMAP_FORCE_OK; -} - -static void pci_vpb_config_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - PCIVPBState *s = opaque; - if (!s->realview && (addr & 0xff) == PCI_INTERRUPT_LINE - && s->irq_mapping == PCI_VPB_IRQMAP_ASSUME_OK) { - uint8_t devfn = addr >> 8; - s->irq_mapping = pci_vpb_broken_irq(PCI_SLOT(devfn), val); - } - pci_data_write(&s->pci_bus, addr, val, size); -} - -static uint64_t pci_vpb_config_read(void *opaque, hwaddr addr, - unsigned size) -{ - PCIVPBState *s = opaque; - uint32_t val; - val = pci_data_read(&s->pci_bus, addr, size); - return val; -} - -static const MemoryRegionOps pci_vpb_config_ops = { - .read = pci_vpb_config_read, - .write = pci_vpb_config_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int pci_vpb_map_irq(PCIDevice *d, int irq_num) -{ - PCIVPBState *s = container_of(d->bus, PCIVPBState, pci_bus); - - if (s->irq_mapping == PCI_VPB_IRQMAP_BROKEN) { - /* Legacy broken IRQ mapping for compatibility with old and - * buggy Linux guests - */ - return irq_num; - } - - /* Slot to IRQ mapping for RealView Platform Baseboard 926 backplane - * name slot IntA IntB IntC IntD - * A 31 IRQ28 IRQ29 IRQ30 IRQ27 - * B 30 IRQ27 IRQ28 IRQ29 IRQ30 - * C 29 IRQ30 IRQ27 IRQ28 IRQ29 - * Slot C is for the host bridge; A and B the peripherals. - * Our output irqs 0..3 correspond to the baseboard's 27..30. - * - * This mapping function takes account of an oddity in the PB926 - * board wiring, where the FPGA's P_nINTA input is connected to - * the INTB connection on the board PCI edge connector, P_nINTB - * is connected to INTC, and so on, so everything is one number - * further round from where you might expect. - */ - return pci_swizzle_map_irq_fn(d, irq_num + 2); -} - -static int pci_vpb_rv_map_irq(PCIDevice *d, int irq_num) -{ - /* Slot to IRQ mapping for RealView EB and PB1176 backplane - * name slot IntA IntB IntC IntD - * A 31 IRQ50 IRQ51 IRQ48 IRQ49 - * B 30 IRQ49 IRQ50 IRQ51 IRQ48 - * C 29 IRQ48 IRQ49 IRQ50 IRQ51 - * Slot C is for the host bridge; A and B the peripherals. - * Our output irqs 0..3 correspond to the baseboard's 48..51. - * - * The PB1176 and EB boards don't have the PB926 wiring oddity - * described above; P_nINTA connects to INTA, P_nINTB to INTB - * and so on, which is why this mapping function is different. - */ - return pci_swizzle_map_irq_fn(d, irq_num + 3); -} - -static void pci_vpb_set_irq(void *opaque, int irq_num, int level) -{ - qemu_irq *pic = opaque; - - qemu_set_irq(pic[irq_num], level); -} - -static void pci_vpb_reset(DeviceState *d) -{ - PCIVPBState *s = PCI_VPB(d); - - s->imap[0] = 0; - s->imap[1] = 0; - s->imap[2] = 0; - s->smap[0] = 0; - s->smap[1] = 0; - s->smap[2] = 0; - s->selfid = 0; - s->flags = 0; - s->irq_mapping = s->irq_mapping_prop; - - pci_vpb_update_all_windows(s); -} - -static void pci_vpb_init(Object *obj) -{ - PCIHostState *h = PCI_HOST_BRIDGE(obj); - PCIVPBState *s = PCI_VPB(obj); - - memory_region_init(&s->pci_io_space, OBJECT(s), "pci_io", 1ULL << 32); - memory_region_init(&s->pci_mem_space, OBJECT(s), "pci_mem", 1ULL << 32); - - pci_bus_new_inplace(&s->pci_bus, sizeof(s->pci_bus), DEVICE(obj), "pci", - &s->pci_mem_space, &s->pci_io_space, - PCI_DEVFN(11, 0), TYPE_PCI_BUS); - h->bus = &s->pci_bus; - - object_initialize(&s->pci_dev, sizeof(s->pci_dev), TYPE_VERSATILE_PCI_HOST); - qdev_set_parent_bus(DEVICE(&s->pci_dev), BUS(&s->pci_bus)); - - /* Window sizes for VersatilePB; realview_pci's init will override */ - s->mem_win_size[0] = 0x0c000000; - s->mem_win_size[1] = 0x10000000; - s->mem_win_size[2] = 0x10000000; -} - -static void pci_vpb_realize(DeviceState *dev, Error **errp) -{ - PCIVPBState *s = PCI_VPB(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - pci_map_irq_fn mapfn; - int i; - - for (i = 0; i < 4; i++) { - sysbus_init_irq(sbd, &s->irq[i]); - } - - if (s->realview) { - mapfn = pci_vpb_rv_map_irq; - } else { - mapfn = pci_vpb_map_irq; - } - - pci_bus_irqs(&s->pci_bus, pci_vpb_set_irq, mapfn, s->irq, 4); - - /* Our memory regions are: - * 0 : our control registers - * 1 : PCI self config window - * 2 : PCI config window - * 3 : PCI IO window - * 4..6 : PCI memory windows - */ - memory_region_init_io(&s->controlregs, OBJECT(s), &pci_vpb_reg_ops, s, - "pci-vpb-regs", 0x1000); - sysbus_init_mmio(sbd, &s->controlregs); - memory_region_init_io(&s->mem_config, OBJECT(s), &pci_vpb_config_ops, s, - "pci-vpb-selfconfig", 0x1000000); - sysbus_init_mmio(sbd, &s->mem_config); - memory_region_init_io(&s->mem_config2, OBJECT(s), &pci_vpb_config_ops, s, - "pci-vpb-config", 0x1000000); - sysbus_init_mmio(sbd, &s->mem_config2); - - /* The window into I/O space is always into a fixed base address; - * its size is the same for both realview and versatile. - */ - memory_region_init_alias(&s->pci_io_window, OBJECT(s), "pci-vbp-io-window", - &s->pci_io_space, 0, 0x100000); - - sysbus_init_mmio(sbd, &s->pci_io_space); - - /* Create the alias regions corresponding to our three windows onto - * PCI memory space. The sizes vary from board to board; the base - * offsets are guest controllable via the IMAP registers. - */ - for (i = 0; i < 3; i++) { - memory_region_init_alias(&s->pci_mem_window[i], OBJECT(s), "pci-vbp-window", - &s->pci_mem_space, 0, s->mem_win_size[i]); - sysbus_init_mmio(sbd, &s->pci_mem_window[i]); - } - - /* TODO Remove once realize propagates to child devices. */ - object_property_set_bool(OBJECT(&s->pci_dev), true, "realized", errp); -} - -static void versatile_pci_host_realize(PCIDevice *d, Error **errp) -{ - pci_set_word(d->config + PCI_STATUS, - PCI_STATUS_66MHZ | PCI_STATUS_DEVSEL_MEDIUM); - pci_set_byte(d->config + PCI_LATENCY_TIMER, 0x10); -} - -static void versatile_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = versatile_pci_host_realize; - k->vendor_id = PCI_VENDOR_ID_XILINX; - k->device_id = PCI_DEVICE_ID_XILINX_XC2VP30; - k->class_id = PCI_CLASS_PROCESSOR_CO; - /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo versatile_pci_host_info = { - .name = TYPE_VERSATILE_PCI_HOST, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = versatile_pci_host_class_init, -}; - -static Property pci_vpb_properties[] = { - DEFINE_PROP_UINT8("broken-irq-mapping", PCIVPBState, irq_mapping_prop, - PCI_VPB_IRQMAP_ASSUME_OK), - DEFINE_PROP_END_OF_LIST() -}; - -static void pci_vpb_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = pci_vpb_realize; - 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 = { - .name = TYPE_VERSATILE_PCI, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(PCIVPBState), - .instance_init = pci_vpb_init, - .class_init = pci_vpb_class_init, -}; - -static void pci_realview_init(Object *obj) -{ - PCIVPBState *s = PCI_VPB(obj); - - s->realview = 1; - /* The PCI window sizes are different on Realview boards */ - s->mem_win_size[0] = 0x01000000; - s->mem_win_size[1] = 0x04000000; - 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) -{ - type_register_static(&pci_vpb_info); - type_register_static(&pci_realview_info); - type_register_static(&versatile_pci_host_info); -} - -type_init(versatile_pci_register_types) diff --git a/qemu/hw/pci/Makefile.objs b/qemu/hw/pci/Makefile.objs deleted file mode 100644 index 9f905e634..000000000 --- a/qemu/hw/pci/Makefile.objs +++ /dev/null @@ -1,9 +0,0 @@ -common-obj-$(CONFIG_PCI) += pci.o pci_bridge.o -common-obj-$(CONFIG_PCI) += msix.o msi.o -common-obj-$(CONFIG_PCI) += shpc.o -common-obj-$(CONFIG_PCI) += slotid_cap.o -common-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o -common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o - -common-obj-$(call lnot,$(CONFIG_PCI)) += pci-stub.o -common-obj-$(CONFIG_ALL) += pci-stub.o diff --git a/qemu/hw/pci/msi.c b/qemu/hw/pci/msi.c deleted file mode 100644 index e0e64c2d9..000000000 --- a/qemu/hw/pci/msi.c +++ /dev/null @@ -1,421 +0,0 @@ -/* - * msi.c - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/pci/msi.h" -#include "hw/xen/xen.h" -#include "qemu/range.h" - -/* PCI_MSI_ADDRESS_LO */ -#define PCI_MSI_ADDRESS_LO_MASK (~0x3) - -/* If we get rid of cap allocator, we won't need those. */ -#define PCI_MSI_32_SIZEOF 0x0a -#define PCI_MSI_64_SIZEOF 0x0e -#define PCI_MSI_32M_SIZEOF 0x14 -#define PCI_MSI_64M_SIZEOF 0x18 - -#define PCI_MSI_VECTORS_MAX 32 - -/* - * 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) -{ - switch (flags & (PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT)) { - case PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT: - return PCI_MSI_64M_SIZEOF; - case PCI_MSI_FLAGS_64BIT: - return PCI_MSI_64_SIZEOF; - case PCI_MSI_FLAGS_MASKBIT: - return PCI_MSI_32M_SIZEOF; - case 0: - return PCI_MSI_32_SIZEOF; - default: - abort(); - break; - } - return 0; -} - -//#define MSI_DEBUG - -#ifdef MSI_DEBUG -# define MSI_DPRINTF(fmt, ...) \ - fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__) -#else -# define MSI_DPRINTF(fmt, ...) do { } while (0) -#endif -#define MSI_DEV_PRINTF(dev, fmt, ...) \ - MSI_DPRINTF("%s:%x " fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__) - -static inline unsigned int msi_nr_vectors(uint16_t flags) -{ - return 1U << - ((flags & PCI_MSI_FLAGS_QSIZE) >> ctz32(PCI_MSI_FLAGS_QSIZE)); -} - -static inline uint8_t msi_flags_off(const PCIDevice* dev) -{ - return dev->msi_cap + PCI_MSI_FLAGS; -} - -static inline uint8_t msi_address_lo_off(const PCIDevice* dev) -{ - return dev->msi_cap + PCI_MSI_ADDRESS_LO; -} - -static inline uint8_t msi_address_hi_off(const PCIDevice* dev) -{ - return dev->msi_cap + PCI_MSI_ADDRESS_HI; -} - -static inline uint8_t msi_data_off(const PCIDevice* dev, bool msi64bit) -{ - return dev->msi_cap + (msi64bit ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32); -} - -static inline uint8_t msi_mask_off(const PCIDevice* dev, bool msi64bit) -{ - return dev->msi_cap + (msi64bit ? PCI_MSI_MASK_64 : PCI_MSI_MASK_32); -} - -static inline uint8_t msi_pending_off(const PCIDevice* dev, bool msi64bit) -{ - return dev->msi_cap + (msi64bit ? PCI_MSI_PENDING_64 : PCI_MSI_PENDING_32); -} - -/* - * Special API for POWER to configure the vectors through - * a side channel. Should never be used by devices. - */ -void msi_set_message(PCIDevice *dev, MSIMessage msg) -{ - uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); - bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; - - if (msi64bit) { - pci_set_quad(dev->config + msi_address_lo_off(dev), msg.address); - } else { - pci_set_long(dev->config + msi_address_lo_off(dev), msg.address); - } - pci_set_word(dev->config + msi_data_off(dev, msi64bit), msg.data); -} - -MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector) -{ - uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); - bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; - unsigned int nr_vectors = msi_nr_vectors(flags); - MSIMessage msg; - - assert(vector < nr_vectors); - - if (msi64bit) { - msg.address = pci_get_quad(dev->config + msi_address_lo_off(dev)); - } else { - msg.address = pci_get_long(dev->config + msi_address_lo_off(dev)); - } - - /* upper bit 31:16 is zero */ - msg.data = pci_get_word(dev->config + msi_data_off(dev, msi64bit)); - if (nr_vectors > 1) { - msg.data &= ~(nr_vectors - 1); - msg.data |= vector; - } - - return msg; -} - -bool msi_enabled(const PCIDevice *dev) -{ - return msi_present(dev) && - (pci_get_word(dev->config + msi_flags_off(dev)) & - PCI_MSI_FLAGS_ENABLE); -} - -int msi_init(struct PCIDevice *dev, uint8_t offset, - unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask) -{ - unsigned int vectors_order; - uint16_t flags; - uint8_t cap_size; - int config_offset; - - if (!msi_nonbroken) { - return -ENOTSUP; - } - - MSI_DEV_PRINTF(dev, - "init offset: 0x%"PRIx8" vector: %"PRId8 - " 64bit %d mask %d\n", - offset, nr_vectors, msi64bit, msi_per_vector_mask); - - assert(!(nr_vectors & (nr_vectors - 1))); /* power of 2 */ - assert(nr_vectors > 0); - assert(nr_vectors <= PCI_MSI_VECTORS_MAX); - /* the nr of MSI vectors is up to 32 */ - vectors_order = ctz32(nr_vectors); - - flags = vectors_order << ctz32(PCI_MSI_FLAGS_QMASK); - if (msi64bit) { - flags |= PCI_MSI_FLAGS_64BIT; - } - if (msi_per_vector_mask) { - flags |= PCI_MSI_FLAGS_MASKBIT; - } - - cap_size = msi_cap_sizeof(flags); - config_offset = pci_add_capability(dev, PCI_CAP_ID_MSI, offset, cap_size); - if (config_offset < 0) { - return config_offset; - } - - dev->msi_cap = config_offset; - dev->cap_present |= QEMU_PCI_CAP_MSI; - - pci_set_word(dev->config + msi_flags_off(dev), flags); - pci_set_word(dev->wmask + msi_flags_off(dev), - PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); - pci_set_long(dev->wmask + msi_address_lo_off(dev), - PCI_MSI_ADDRESS_LO_MASK); - if (msi64bit) { - pci_set_long(dev->wmask + msi_address_hi_off(dev), 0xffffffff); - } - pci_set_word(dev->wmask + msi_data_off(dev, msi64bit), 0xffff); - - if (msi_per_vector_mask) { - /* Make mask bits 0 to nr_vectors - 1 writable. */ - pci_set_long(dev->wmask + msi_mask_off(dev, msi64bit), - 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors)); - } - return config_offset; -} - -void msi_uninit(struct PCIDevice *dev) -{ - uint16_t flags; - uint8_t cap_size; - - if (!msi_present(dev)) { - return; - } - flags = pci_get_word(dev->config + msi_flags_off(dev)); - cap_size = msi_cap_sizeof(flags); - pci_del_capability(dev, PCI_CAP_ID_MSI, cap_size); - dev->cap_present &= ~QEMU_PCI_CAP_MSI; - - MSI_DEV_PRINTF(dev, "uninit\n"); -} - -void msi_reset(PCIDevice *dev) -{ - uint16_t flags; - bool msi64bit; - - if (!msi_present(dev)) { - return; - } - - flags = pci_get_word(dev->config + msi_flags_off(dev)); - flags &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); - msi64bit = flags & PCI_MSI_FLAGS_64BIT; - - pci_set_word(dev->config + msi_flags_off(dev), flags); - pci_set_long(dev->config + msi_address_lo_off(dev), 0); - if (msi64bit) { - pci_set_long(dev->config + msi_address_hi_off(dev), 0); - } - pci_set_word(dev->config + msi_data_off(dev, msi64bit), 0); - if (flags & PCI_MSI_FLAGS_MASKBIT) { - pci_set_long(dev->config + msi_mask_off(dev, msi64bit), 0); - pci_set_long(dev->config + msi_pending_off(dev, msi64bit), 0); - } - MSI_DEV_PRINTF(dev, "reset\n"); -} - -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, 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); -} - -void msi_notify(PCIDevice *dev, unsigned int vector) -{ - uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); - bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; - unsigned int nr_vectors = msi_nr_vectors(flags); - MSIMessage msg; - - assert(vector < nr_vectors); - if (msi_is_masked(dev, vector)) { - assert(flags & PCI_MSI_FLAGS_MASKBIT); - pci_long_test_and_set_mask( - dev->config + msi_pending_off(dev, msi64bit), 1U << vector); - MSI_DEV_PRINTF(dev, "pending vector 0x%x\n", vector); - return; - } - - msg = msi_get_message(dev, vector); - - MSI_DEV_PRINTF(dev, - "notify vector 0x%x" - " address: 0x%"PRIx64" data: 0x%"PRIx32"\n", - vector, msg.address, msg.data); - msi_send_message(dev, msg); -} - -void msi_send_message(PCIDevice *dev, MSIMessage msg) -{ - MemTxAttrs attrs = {}; - - attrs.requester_id = pci_requester_id(dev); - address_space_stl_le(&dev->bus_master_as, msg.address, msg.data, - attrs, NULL); -} - -/* Normally called by pci_default_write_config(). */ -void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len) -{ - uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); - bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; - bool msi_per_vector_mask = flags & PCI_MSI_FLAGS_MASKBIT; - unsigned int nr_vectors; - uint8_t log_num_vecs; - uint8_t log_max_vecs; - unsigned int vector; - uint32_t pending; - - if (!msi_present(dev) || - !ranges_overlap(addr, len, dev->msi_cap, msi_cap_sizeof(flags))) { - return; - } - -#ifdef MSI_DEBUG - MSI_DEV_PRINTF(dev, "addr 0x%"PRIx32" val 0x%"PRIx32" len %d\n", - addr, val, len); - MSI_DEV_PRINTF(dev, "ctrl: 0x%"PRIx16" address: 0x%"PRIx32, - flags, - pci_get_long(dev->config + msi_address_lo_off(dev))); - if (msi64bit) { - fprintf(stderr, " address-hi: 0x%"PRIx32, - pci_get_long(dev->config + msi_address_hi_off(dev))); - } - fprintf(stderr, " data: 0x%"PRIx16, - pci_get_word(dev->config + msi_data_off(dev, msi64bit))); - if (flags & PCI_MSI_FLAGS_MASKBIT) { - fprintf(stderr, " mask 0x%"PRIx32" pending 0x%"PRIx32, - pci_get_long(dev->config + msi_mask_off(dev, msi64bit)), - pci_get_long(dev->config + msi_pending_off(dev, msi64bit))); - } - fprintf(stderr, "\n"); -#endif - - if (!(flags & PCI_MSI_FLAGS_ENABLE)) { - return; - } - - /* - * Now MSI is enabled, clear INTx# interrupts. - * the driver is prohibited from writing enable bit to mask - * a service request. But the guest OS could do this. - * So we just discard the interrupts as moderate fallback. - * - * 6.8.3.3. Enabling Operation - * While enabled for MSI or MSI-X operation, a function is prohibited - * from using its INTx# pin (if implemented) to request - * service (MSI, MSI-X, and INTx# are mutually exclusive). - */ - pci_device_deassert_intx(dev); - - /* - * nr_vectors might be set bigger than capable. So clamp it. - * This is not legal by spec, so we can do anything we like, - * just don't crash the host - */ - log_num_vecs = - (flags & PCI_MSI_FLAGS_QSIZE) >> ctz32(PCI_MSI_FLAGS_QSIZE); - log_max_vecs = - (flags & PCI_MSI_FLAGS_QMASK) >> ctz32(PCI_MSI_FLAGS_QMASK); - if (log_num_vecs > log_max_vecs) { - flags &= ~PCI_MSI_FLAGS_QSIZE; - flags |= log_max_vecs << ctz32(PCI_MSI_FLAGS_QSIZE); - pci_set_word(dev->config + msi_flags_off(dev), flags); - } - - if (!msi_per_vector_mask) { - /* if per vector masking isn't supported, - there is no pending interrupt. */ - return; - } - - nr_vectors = msi_nr_vectors(flags); - - /* This will discard pending interrupts, if any. */ - pending = pci_get_long(dev->config + msi_pending_off(dev, msi64bit)); - pending &= 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors); - pci_set_long(dev->config + msi_pending_off(dev, msi64bit), pending); - - /* deliver pending interrupts which are unmasked */ - for (vector = 0; vector < nr_vectors; ++vector) { - if (msi_is_masked(dev, vector) || !(pending & (1U << vector))) { - continue; - } - - pci_long_test_and_clear_mask( - dev->config + msi_pending_off(dev, msi64bit), 1U << vector); - msi_notify(dev, vector); - } -} - -unsigned int msi_nr_vectors_allocated(const PCIDevice *dev) -{ - uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); - return msi_nr_vectors(flags); -} diff --git a/qemu/hw/pci/msix.c b/qemu/hw/pci/msix.c deleted file mode 100644 index b75f0e9c4..000000000 --- a/qemu/hw/pci/msix.c +++ /dev/null @@ -1,621 +0,0 @@ -/* - * MSI-X device support - * - * This module includes support for MSI-X in pci devices. - * - * Author: Michael S. Tsirkin - * - * Copyright (c) 2009, Red Hat Inc, Michael S. Tsirkin (mst@redhat.com) - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * 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/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 - -/* MSI enable bit and maskall bit are in byte 1 in FLAGS register */ -#define MSIX_CONTROL_OFFSET (PCI_MSIX_FLAGS + 1) -#define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8) -#define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8) - -MSIMessage msix_get_message(PCIDevice *dev, unsigned vector) -{ - uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE; - MSIMessage msg; - - msg.address = pci_get_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR); - msg.data = pci_get_long(table_entry + PCI_MSIX_ENTRY_DATA); - return msg; -} - -/* - * Special API for POWER to configure the vectors through - * a side channel. Should never be used by devices. - */ -void msix_set_message(PCIDevice *dev, int vector, struct MSIMessage msg) -{ - uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE; - - pci_set_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR, msg.address); - pci_set_long(table_entry + PCI_MSIX_ENTRY_DATA, msg.data); - table_entry[PCI_MSIX_ENTRY_VECTOR_CTRL] &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT; -} - -static uint8_t msix_pending_mask(int vector) -{ - return 1 << (vector % 8); -} - -static uint8_t *msix_pending_byte(PCIDevice *dev, int vector) -{ - return dev->msix_pba + vector / 8; -} - -static int msix_is_pending(PCIDevice *dev, int vector) -{ - return *msix_pending_byte(dev, vector) & msix_pending_mask(vector); -} - -void msix_set_pending(PCIDevice *dev, unsigned int vector) -{ - *msix_pending_byte(dev, vector) |= msix_pending_mask(vector); -} - -static void msix_clr_pending(PCIDevice *dev, int vector) -{ - *msix_pending_byte(dev, vector) &= ~msix_pending_mask(vector); -} - -static bool msix_vector_masked(PCIDevice *dev, unsigned int vector, bool fmask) -{ - 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) -{ - return msix_vector_masked(dev, vector, dev->msix_function_masked); -} - -static void msix_fire_vector_notifier(PCIDevice *dev, - unsigned int vector, bool is_masked) -{ - MSIMessage msg; - int ret; - - if (!dev->msix_vector_use_notifier) { - return; - } - if (is_masked) { - dev->msix_vector_release_notifier(dev, vector); - } else { - msg = msix_get_message(dev, vector); - ret = dev->msix_vector_use_notifier(dev, vector, msg); - assert(ret >= 0); - } -} - -static void msix_handle_mask_update(PCIDevice *dev, int vector, bool was_masked) -{ - bool is_masked = msix_is_masked(dev, vector); - - if (is_masked == was_masked) { - return; - } - - msix_fire_vector_notifier(dev, vector, is_masked); - - if (!is_masked && msix_is_pending(dev, vector)) { - msix_clr_pending(dev, vector); - msix_notify(dev, vector); - } -} - -static void msix_update_function_masked(PCIDevice *dev) -{ - dev->msix_function_masked = !msix_enabled(dev) || - (dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & MSIX_MASKALL_MASK); -} - -/* Handle MSI-X capability config write. */ -void msix_write_config(PCIDevice *dev, uint32_t addr, - uint32_t val, int len) -{ - unsigned enable_pos = dev->msix_cap + MSIX_CONTROL_OFFSET; - int vector; - bool was_masked; - - if (!msix_present(dev) || !range_covers_byte(addr, len, enable_pos)) { - return; - } - - was_masked = dev->msix_function_masked; - msix_update_function_masked(dev); - - if (!msix_enabled(dev)) { - return; - } - - pci_device_deassert_intx(dev); - - if (dev->msix_function_masked == was_masked) { - return; - } - - for (vector = 0; vector < dev->msix_entries_nr; ++vector) { - msix_handle_mask_update(dev, vector, - msix_vector_masked(dev, vector, was_masked)); - } -} - -static uint64_t msix_table_mmio_read(void *opaque, hwaddr addr, - unsigned size) -{ - PCIDevice *dev = opaque; - - return pci_get_long(dev->msix_table + addr); -} - -static void msix_table_mmio_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - PCIDevice *dev = opaque; - int vector = addr / PCI_MSIX_ENTRY_SIZE; - bool was_masked; - - was_masked = msix_is_masked(dev, vector); - pci_set_long(dev->msix_table + addr, val); - msix_handle_mask_update(dev, vector, was_masked); -} - -static const MemoryRegionOps msix_table_mmio_ops = { - .read = msix_table_mmio_read, - .write = msix_table_mmio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr, - unsigned size) -{ - PCIDevice *dev = opaque; - if (dev->msix_vector_poll_notifier) { - unsigned vector_start = addr * 8; - unsigned vector_end = MIN(addr + size * 8, dev->msix_entries_nr); - dev->msix_vector_poll_notifier(dev, vector_start, vector_end); - } - - 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, - .max_access_size = 4, - }, -}; - -static void msix_mask_all(struct PCIDevice *dev, unsigned nentries) -{ - int vector; - - for (vector = 0; vector < nentries; ++vector) { - unsigned offset = - vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL; - bool was_masked = msix_is_masked(dev, vector); - - dev->msix_table[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT; - msix_handle_mask_update(dev, vector, was_masked); - } -} - -/* Initialize the MSI-X structures */ -int msix_init(struct PCIDevice *dev, unsigned short nentries, - MemoryRegion *table_bar, uint8_t table_bar_nr, - unsigned table_offset, MemoryRegion *pba_bar, - uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos) -{ - int cap; - unsigned table_size, pba_size; - uint8_t *config; - - /* Nothing to do if MSI is not supported by interrupt controller */ - if (!msi_nonbroken) { - return -ENOTSUP; - } - - if (nentries < 1 || nentries > PCI_MSIX_FLAGS_QSIZE + 1) { - return -EINVAL; - } - - table_size = nentries * PCI_MSIX_ENTRY_SIZE; - pba_size = QEMU_ALIGN_UP(nentries, 64) / 8; - - /* Sanity test: table & pba don't overlap, fit within BARs, min aligned */ - if ((table_bar_nr == pba_bar_nr && - ranges_overlap(table_offset, table_size, pba_offset, pba_size)) || - table_offset + table_size > memory_region_size(table_bar) || - pba_offset + pba_size > memory_region_size(pba_bar) || - (table_offset | pba_offset) & PCI_MSIX_FLAGS_BIRMASK) { - return -EINVAL; - } - - cap = pci_add_capability(dev, PCI_CAP_ID_MSIX, cap_pos, MSIX_CAP_LENGTH); - if (cap < 0) { - return cap; - } - - dev->msix_cap = cap; - dev->cap_present |= QEMU_PCI_CAP_MSIX; - config = dev->config + cap; - - pci_set_word(config + PCI_MSIX_FLAGS, nentries - 1); - dev->msix_entries_nr = nentries; - dev->msix_function_masked = true; - - pci_set_long(config + PCI_MSIX_TABLE, table_offset | table_bar_nr); - pci_set_long(config + PCI_MSIX_PBA, pba_offset | pba_bar_nr); - - /* Make flags bit writable. */ - dev->wmask[cap + MSIX_CONTROL_OFFSET] |= MSIX_ENABLE_MASK | - MSIX_MASKALL_MASK; - - dev->msix_table = g_malloc0(table_size); - dev->msix_pba = g_malloc0(pba_size); - dev->msix_entry_used = g_malloc0(nentries * sizeof *dev->msix_entry_used); - - msix_mask_all(dev, nentries); - - memory_region_init_io(&dev->msix_table_mmio, OBJECT(dev), &msix_table_mmio_ops, dev, - "msix-table", table_size); - memory_region_add_subregion(table_bar, table_offset, &dev->msix_table_mmio); - memory_region_init_io(&dev->msix_pba_mmio, OBJECT(dev), &msix_pba_mmio_ops, dev, - "msix-pba", pba_size); - memory_region_add_subregion(pba_bar, pba_offset, &dev->msix_pba_mmio); - - return 0; -} - -int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries, - uint8_t bar_nr) -{ - int ret; - char *name; - uint32_t bar_size = 4096; - uint32_t bar_pba_offset = bar_size / 2; - uint32_t bar_pba_size = (nentries / 8 + 1) * 8; - - /* - * Migration compatibility dictates that this remains a 4k - * BAR with the vector table in the lower half and PBA in - * the upper half for nentries which is lower or equal to 128. - * No need to care about using more than 65 entries for legacy - * machine types who has at most 64 queues. - */ - if (nentries * PCI_MSIX_ENTRY_SIZE > bar_pba_offset) { - bar_pba_offset = nentries * PCI_MSIX_ENTRY_SIZE; - } - - if (bar_pba_offset + bar_pba_size > 4096) { - bar_size = bar_pba_offset + bar_pba_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); - g_free(name); - - ret = msix_init(dev, nentries, &dev->msix_exclusive_bar, bar_nr, - 0, &dev->msix_exclusive_bar, - bar_nr, bar_pba_offset, - 0); - if (ret) { - return ret; - } - - pci_register_bar(dev, bar_nr, PCI_BASE_ADDRESS_SPACE_MEMORY, - &dev->msix_exclusive_bar); - - return 0; -} - -static void msix_free_irq_entries(PCIDevice *dev) -{ - int vector; - - for (vector = 0; vector < dev->msix_entries_nr; ++vector) { - dev->msix_entry_used[vector] = 0; - msix_clr_pending(dev, vector); - } -} - -static void msix_clear_all_vectors(PCIDevice *dev) -{ - int vector; - - for (vector = 0; vector < dev->msix_entries_nr; ++vector) { - msix_clr_pending(dev, vector); - } -} - -/* Clean up resources for the device. */ -void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, MemoryRegion *pba_bar) -{ - if (!msix_present(dev)) { - return; - } - pci_del_capability(dev, PCI_CAP_ID_MSIX, MSIX_CAP_LENGTH); - dev->msix_cap = 0; - msix_free_irq_entries(dev); - dev->msix_entries_nr = 0; - memory_region_del_subregion(pba_bar, &dev->msix_pba_mmio); - g_free(dev->msix_pba); - dev->msix_pba = NULL; - memory_region_del_subregion(table_bar, &dev->msix_table_mmio); - g_free(dev->msix_table); - dev->msix_table = NULL; - g_free(dev->msix_entry_used); - dev->msix_entry_used = NULL; - dev->cap_present &= ~QEMU_PCI_CAP_MSIX; -} - -void msix_uninit_exclusive_bar(PCIDevice *dev) -{ - if (msix_present(dev)) { - msix_uninit(dev, &dev->msix_exclusive_bar, &dev->msix_exclusive_bar); - } -} - -void msix_save(PCIDevice *dev, QEMUFile *f) -{ - unsigned n = dev->msix_entries_nr; - - if (!msix_present(dev)) { - return; - } - - qemu_put_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE); - qemu_put_buffer(f, dev->msix_pba, (n + 7) / 8); -} - -/* Should be called after restoring the config space. */ -void msix_load(PCIDevice *dev, QEMUFile *f) -{ - unsigned n = dev->msix_entries_nr; - unsigned int vector; - - if (!msix_present(dev)) { - return; - } - - msix_clear_all_vectors(dev); - qemu_get_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE); - qemu_get_buffer(f, dev->msix_pba, (n + 7) / 8); - msix_update_function_masked(dev); - - for (vector = 0; vector < n; vector++) { - msix_handle_mask_update(dev, vector, true); - } -} - -/* Does device support MSI-X? */ -int msix_present(PCIDevice *dev) -{ - return dev->cap_present & QEMU_PCI_CAP_MSIX; -} - -/* Is MSI-X enabled? */ -int msix_enabled(PCIDevice *dev) -{ - return (dev->cap_present & QEMU_PCI_CAP_MSIX) && - (dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & - MSIX_ENABLE_MASK); -} - -/* Send an MSI-X message */ -void msix_notify(PCIDevice *dev, unsigned vector) -{ - MSIMessage msg; - - if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) - return; - if (msix_is_masked(dev, vector)) { - msix_set_pending(dev, vector); - return; - } - - msg = msix_get_message(dev, vector); - - msi_send_message(dev, msg); -} - -void msix_reset(PCIDevice *dev) -{ - if (!msix_present(dev)) { - return; - } - msix_clear_all_vectors(dev); - dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &= - ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET]; - memset(dev->msix_table, 0, dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE); - memset(dev->msix_pba, 0, QEMU_ALIGN_UP(dev->msix_entries_nr, 64) / 8); - msix_mask_all(dev, dev->msix_entries_nr); -} - -/* PCI spec suggests that devices make it possible for software to configure - * less vectors than supported by the device, but does not specify a standard - * mechanism for devices to do so. - * - * We support this by asking devices to declare vectors software is going to - * actually use, and checking this on the notification path. Devices that - * don't want to follow the spec suggestion can declare all vectors as used. */ - -/* Mark vector as used. */ -int msix_vector_use(PCIDevice *dev, unsigned vector) -{ - if (vector >= dev->msix_entries_nr) - return -EINVAL; - dev->msix_entry_used[vector]++; - return 0; -} - -/* Mark vector as unused. */ -void msix_vector_unuse(PCIDevice *dev, unsigned vector) -{ - if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) { - return; - } - if (--dev->msix_entry_used[vector]) { - return; - } - msix_clr_pending(dev, vector); -} - -void msix_unuse_all_vectors(PCIDevice *dev) -{ - if (!msix_present(dev)) { - return; - } - msix_free_irq_entries(dev); -} - -unsigned int msix_nr_vectors_allocated(const PCIDevice *dev) -{ - return dev->msix_entries_nr; -} - -static int msix_set_notifier_for_vector(PCIDevice *dev, unsigned int vector) -{ - MSIMessage msg; - - if (msix_is_masked(dev, vector)) { - return 0; - } - msg = msix_get_message(dev, vector); - return dev->msix_vector_use_notifier(dev, vector, msg); -} - -static void msix_unset_notifier_for_vector(PCIDevice *dev, unsigned int vector) -{ - if (msix_is_masked(dev, vector)) { - return; - } - dev->msix_vector_release_notifier(dev, vector); -} - -int msix_set_vector_notifiers(PCIDevice *dev, - MSIVectorUseNotifier use_notifier, - MSIVectorReleaseNotifier release_notifier, - MSIVectorPollNotifier poll_notifier) -{ - int vector, ret; - - assert(use_notifier && release_notifier); - - dev->msix_vector_use_notifier = use_notifier; - dev->msix_vector_release_notifier = release_notifier; - dev->msix_vector_poll_notifier = poll_notifier; - - if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & - (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) { - for (vector = 0; vector < dev->msix_entries_nr; vector++) { - ret = msix_set_notifier_for_vector(dev, vector); - if (ret < 0) { - goto undo; - } - } - } - if (dev->msix_vector_poll_notifier) { - dev->msix_vector_poll_notifier(dev, 0, dev->msix_entries_nr); - } - return 0; - -undo: - while (--vector >= 0) { - msix_unset_notifier_for_vector(dev, vector); - } - dev->msix_vector_use_notifier = NULL; - dev->msix_vector_release_notifier = NULL; - return ret; -} - -void msix_unset_vector_notifiers(PCIDevice *dev) -{ - int vector; - - assert(dev->msix_vector_use_notifier && - dev->msix_vector_release_notifier); - - if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & - (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) { - for (vector = 0; vector < dev->msix_entries_nr; vector++) { - msix_unset_notifier_for_vector(dev, vector); - } - } - dev->msix_vector_use_notifier = NULL; - dev->msix_vector_release_notifier = NULL; - dev->msix_vector_poll_notifier = NULL; -} - -static void put_msix_state(QEMUFile *f, void *pv, size_t size) -{ - msix_save(pv, f); -} - -static int get_msix_state(QEMUFile *f, void *pv, size_t size) -{ - msix_load(pv, f); - return 0; -} - -static VMStateInfo vmstate_info_msix = { - .name = "msix state", - .get = get_msix_state, - .put = put_msix_state, -}; - -const VMStateDescription vmstate_msix = { - .name = "msix", - .fields = (VMStateField[]) { - { - .name = "msix", - .version_id = 0, - .field_exists = NULL, - .size = 0, /* ouch */ - .info = &vmstate_info_msix, - .flags = VMS_SINGLE, - .offset = 0, - }, - VMSTATE_END_OF_LIST() - } -}; diff --git a/qemu/hw/pci/pci-stub.c b/qemu/hw/pci/pci-stub.c deleted file mode 100644 index 36d2c430c..000000000 --- a/qemu/hw/pci/pci-stub.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * PCI stubs for platforms that don't support pci bus. - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "sysemu/sysemu.h" -#include "monitor/monitor.h" -#include "qapi/qmp/qerror.h" -#include "hw/pci/pci.h" -#include "qmp-commands.h" - -PciInfoList *qmp_query_pci(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict) -{ - monitor_printf(mon, "PCI devices not supported\n"); -} diff --git a/qemu/hw/pci/pci.c b/qemu/hw/pci/pci.c deleted file mode 100644 index bb605efae..000000000 --- a/qemu/hw/pci/pci.c +++ /dev/null @@ -1,2517 +0,0 @@ -/* - * QEMU PCI bus manager - * - * Copyright (c) 2004 Fabrice Bellard - * - * 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/pci/pci.h" -#include "hw/pci/pci_bridge.h" -#include "hw/pci/pci_bus.h" -#include "hw/pci/pci_host.h" -#include "monitor/monitor.h" -#include "net/net.h" -#include "sysemu/sysemu.h" -#include "hw/loader.h" -#include "qemu/error-report.h" -#include "qemu/range.h" -#include "qmp-commands.h" -#include "trace.h" -#include "hw/pci/msi.h" -#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 -# define PCI_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) -#else -# define PCI_DPRINTF(format, ...) do { } while (0) -#endif - -static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent); -static char *pcibus_get_dev_path(DeviceState *dev); -static char *pcibus_get_fw_dev_path(DeviceState *dev); -static void pcibus_reset(BusState *qbus); - -static Property pci_props[] = { - DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1), - DEFINE_PROP_STRING("romfile", PCIDevice, romfile), - DEFINE_PROP_UINT32("rombar", PCIDevice, rom_bar, 1), - DEFINE_PROP_BIT("multifunction", PCIDevice, cap_present, - QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false), - DEFINE_PROP_BIT("command_serr_enable", PCIDevice, cap_present, - QEMU_PCI_CAP_SERR_BITNR, true), - DEFINE_PROP_END_OF_LIST() -}; - -static const VMStateDescription vmstate_pcibus = { - .name = "PCIBUS", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32_EQUAL(nirq, PCIBus), - VMSTATE_VARRAY_INT32(irq_count, PCIBus, - nirq, 0, vmstate_info_int32, - int32_t), - VMSTATE_END_OF_LIST() - } -}; - -static void pci_bus_realize(BusState *qbus, Error **errp) -{ - PCIBus *bus = PCI_BUS(qbus); - - vmstate_register(NULL, -1, &vmstate_pcibus, bus); -} - -static void pci_bus_unrealize(BusState *qbus, Error **errp) -{ - PCIBus *bus = PCI_BUS(qbus); - - vmstate_unregister(NULL, &vmstate_pcibus, bus); -} - -static bool pcibus_is_root(PCIBus *bus) -{ - return !bus->parent_dev; -} - -static int pcibus_num(PCIBus *bus) -{ - if (pcibus_is_root(bus)) { - return 0; /* pci host bridge */ - } - return bus->parent_dev->config[PCI_SECONDARY_BUS]; -} - -static uint16_t pcibus_numa_node(PCIBus *bus) -{ - return NUMA_NODE_UNASSIGNED; -} - -static void pci_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - PCIBusClass *pbc = PCI_BUS_CLASS(klass); - - k->print_dev = pcibus_dev_print; - k->get_dev_path = pcibus_get_dev_path; - k->get_fw_dev_path = pcibus_get_fw_dev_path; - k->realize = pci_bus_realize; - k->unrealize = pci_bus_unrealize; - k->reset = pcibus_reset; - - pbc->is_root = pcibus_is_root; - pbc->bus_num = pcibus_num; - pbc->numa_node = pcibus_numa_node; -} - -static const TypeInfo pci_bus_info = { - .name = TYPE_PCI_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(PCIBus), - .class_size = sizeof(PCIBusClass), - .class_init = pci_bus_class_init, -}; - -static const TypeInfo pcie_bus_info = { - .name = TYPE_PCIE_BUS, - .parent = TYPE_PCI_BUS, -}; - -static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num); -static void pci_update_mappings(PCIDevice *d); -static void pci_irq_handler(void *opaque, int irq_num, int level); -static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, Error **); -static void pci_del_option_rom(PCIDevice *pdev); - -static uint16_t pci_default_sub_vendor_id = PCI_SUBVENDOR_ID_REDHAT_QUMRANET; -static uint16_t pci_default_sub_device_id = PCI_SUBDEVICE_ID_QEMU; - -static QLIST_HEAD(, PCIHostState) pci_host_bridges; - -int pci_bar(PCIDevice *d, int reg) -{ - uint8_t type; - - if (reg != PCI_ROM_SLOT) - return PCI_BASE_ADDRESS_0 + reg * 4; - - type = d->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; - return type == PCI_HEADER_TYPE_BRIDGE ? PCI_ROM_ADDRESS1 : PCI_ROM_ADDRESS; -} - -static inline int pci_irq_state(PCIDevice *d, int irq_num) -{ - return (d->irq_state >> irq_num) & 0x1; -} - -static inline void pci_set_irq_state(PCIDevice *d, int irq_num, int level) -{ - d->irq_state &= ~(0x1 << irq_num); - d->irq_state |= level << irq_num; -} - -static void pci_change_irq_level(PCIDevice *pci_dev, int irq_num, int change) -{ - PCIBus *bus; - for (;;) { - bus = pci_dev->bus; - irq_num = bus->map_irq(pci_dev, irq_num); - if (bus->set_irq) - break; - pci_dev = bus->parent_dev; - } - bus->irq_count[irq_num] += change; - bus->set_irq(bus->irq_opaque, irq_num, bus->irq_count[irq_num] != 0); -} - -int pci_bus_get_irq_level(PCIBus *bus, int irq_num) -{ - assert(irq_num >= 0); - assert(irq_num < bus->nirq); - return !!bus->irq_count[irq_num]; -} - -/* Update interrupt status bit in config space on interrupt - * state change. */ -static void pci_update_irq_status(PCIDevice *dev) -{ - if (dev->irq_state) { - dev->config[PCI_STATUS] |= PCI_STATUS_INTERRUPT; - } else { - dev->config[PCI_STATUS] &= ~PCI_STATUS_INTERRUPT; - } -} - -void pci_device_deassert_intx(PCIDevice *dev) -{ - int i; - for (i = 0; i < PCI_NUM_PINS; ++i) { - pci_irq_handler(dev, i, 0); - } -} - -static void pci_do_device_reset(PCIDevice *dev) -{ - int r; - - pci_device_deassert_intx(dev); - assert(dev->irq_state == 0); - - /* Clear all writable bits */ - pci_word_test_and_clear_mask(dev->config + PCI_COMMAND, - pci_get_word(dev->wmask + PCI_COMMAND) | - pci_get_word(dev->w1cmask + PCI_COMMAND)); - pci_word_test_and_clear_mask(dev->config + PCI_STATUS, - pci_get_word(dev->wmask + PCI_STATUS) | - pci_get_word(dev->w1cmask + PCI_STATUS)); - dev->config[PCI_CACHE_LINE_SIZE] = 0x0; - dev->config[PCI_INTERRUPT_LINE] = 0x0; - for (r = 0; r < PCI_NUM_REGIONS; ++r) { - PCIIORegion *region = &dev->io_regions[r]; - if (!region->size) { - continue; - } - - if (!(region->type & PCI_BASE_ADDRESS_SPACE_IO) && - region->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { - pci_set_quad(dev->config + pci_bar(dev, r), region->type); - } else { - pci_set_long(dev->config + pci_bar(dev, r), region->type); - } - } - pci_update_mappings(dev); - - msi_reset(dev); - msix_reset(dev); -} - -/* - * This function is called on #RST and FLR. - * FLR if PCI_EXP_DEVCTL_BCR_FLR is set - */ -void pci_device_reset(PCIDevice *dev) -{ - qdev_reset_all(&dev->qdev); - pci_do_device_reset(dev); -} - -/* - * Trigger pci bus reset under a given bus. - * Called via qbus_reset_all on RST# assert, after the devices - * have been reset qdev_reset_all-ed already. - */ -static void pcibus_reset(BusState *qbus) -{ - PCIBus *bus = DO_UPCAST(PCIBus, qbus, qbus); - int i; - - for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { - if (bus->devices[i]) { - pci_do_device_reset(bus->devices[i]); - } - } - - for (i = 0; i < bus->nirq; i++) { - assert(bus->irq_count[i] == 0); - } -} - -static void pci_host_bus_register(DeviceState *host) -{ - PCIHostState *host_bridge = PCI_HOST_BRIDGE(host); - - QLIST_INSERT_HEAD(&pci_host_bridges, host_bridge, next); -} - -PCIBus *pci_find_primary_bus(void) -{ - PCIBus *primary_bus = NULL; - PCIHostState *host; - - QLIST_FOREACH(host, &pci_host_bridges, next) { - if (primary_bus) { - /* We have multiple root buses, refuse to select a primary */ - return NULL; - } - primary_bus = host->bus; - } - - return primary_bus; -} - -PCIBus *pci_device_root_bus(const PCIDevice *d) -{ - PCIBus *bus = d->bus; - - while (!pci_bus_is_root(bus)) { - d = bus->parent_dev; - assert(d != NULL); - - bus = d->bus; - } - - return bus; -} - -const char *pci_root_bus_path(PCIDevice *dev) -{ - PCIBus *rootbus = pci_device_root_bus(dev); - PCIHostState *host_bridge = PCI_HOST_BRIDGE(rootbus->qbus.parent); - PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_GET_CLASS(host_bridge); - - assert(host_bridge->bus == rootbus); - - if (hc->root_bus_path) { - return (*hc->root_bus_path)(host_bridge, rootbus); - } - - return rootbus->qbus.name; -} - -static void pci_bus_init(PCIBus *bus, DeviceState *parent, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - uint8_t devfn_min) -{ - assert(PCI_FUNC(devfn_min) == 0); - bus->devfn_min = devfn_min; - bus->address_space_mem = address_space_mem; - bus->address_space_io = address_space_io; - - /* host bridge */ - QLIST_INIT(&bus->child); - - pci_host_bus_register(parent); -} - -bool pci_bus_is_express(PCIBus *bus) -{ - return object_dynamic_cast(OBJECT(bus), TYPE_PCIE_BUS); -} - -bool pci_bus_is_root(PCIBus *bus) -{ - return PCI_BUS_GET_CLASS(bus)->is_root(bus); -} - -void pci_bus_new_inplace(PCIBus *bus, size_t bus_size, DeviceState *parent, - const char *name, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - uint8_t devfn_min, const char *typename) -{ - qbus_create_inplace(bus, bus_size, typename, parent, name); - pci_bus_init(bus, parent, address_space_mem, address_space_io, devfn_min); -} - -PCIBus *pci_bus_new(DeviceState *parent, const char *name, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - uint8_t devfn_min, const char *typename) -{ - PCIBus *bus; - - bus = PCI_BUS(qbus_create(typename, parent, name)); - pci_bus_init(bus, parent, address_space_mem, address_space_io, devfn_min); - return bus; -} - -void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, - void *irq_opaque, int nirq) -{ - bus->set_irq = set_irq; - bus->map_irq = map_irq; - bus->irq_opaque = irq_opaque; - bus->nirq = nirq; - bus->irq_count = g_malloc0(nirq * sizeof(bus->irq_count[0])); -} - -PCIBus *pci_register_bus(DeviceState *parent, const char *name, - pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, - void *irq_opaque, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io, - uint8_t devfn_min, int nirq, const char *typename) -{ - PCIBus *bus; - - bus = pci_bus_new(parent, name, address_space_mem, - address_space_io, devfn_min, typename); - pci_bus_irqs(bus, set_irq, map_irq, irq_opaque, nirq); - return bus; -} - -int pci_bus_num(PCIBus *s) -{ - return PCI_BUS_GET_CLASS(s)->bus_num(s); -} - -int pci_bus_numa_node(PCIBus *bus) -{ - return PCI_BUS_GET_CLASS(bus)->numa_node(bus); -} - -static int get_pci_config_device(QEMUFile *f, void *pv, size_t size) -{ - PCIDevice *s = container_of(pv, PCIDevice, config); - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(s); - uint8_t *config; - int i; - - assert(size == pci_config_size(s)); - config = g_malloc(size); - - qemu_get_buffer(f, config, size); - for (i = 0; i < size; ++i) { - if ((config[i] ^ s->config[i]) & - s->cmask[i] & ~s->wmask[i] & ~s->w1cmask[i]) { - error_report("%s: Bad config data: i=0x%x read: %x device: %x " - "cmask: %x wmask: %x w1cmask:%x", __func__, - i, config[i], s->config[i], - s->cmask[i], s->wmask[i], s->w1cmask[i]); - g_free(config); - return -EINVAL; - } - } - memcpy(s->config, config, size); - - pci_update_mappings(s); - if (pc->is_bridge) { - PCIBridge *b = PCI_BRIDGE(s); - pci_bridge_update_mappings(b); - } - - memory_region_set_enabled(&s->bus_master_enable_region, - pci_get_word(s->config + PCI_COMMAND) - & PCI_COMMAND_MASTER); - - g_free(config); - return 0; -} - -/* just put buffer */ -static void put_pci_config_device(QEMUFile *f, void *pv, size_t size) -{ - const uint8_t **v = pv; - assert(size == pci_config_size(container_of(pv, PCIDevice, config))); - qemu_put_buffer(f, *v, size); -} - -static VMStateInfo vmstate_info_pci_config = { - .name = "pci config", - .get = get_pci_config_device, - .put = put_pci_config_device, -}; - -static int get_pci_irq_state(QEMUFile *f, void *pv, size_t size) -{ - PCIDevice *s = container_of(pv, PCIDevice, irq_state); - uint32_t irq_state[PCI_NUM_PINS]; - int i; - for (i = 0; i < PCI_NUM_PINS; ++i) { - irq_state[i] = qemu_get_be32(f); - if (irq_state[i] != 0x1 && irq_state[i] != 0) { - fprintf(stderr, "irq state %d: must be 0 or 1.\n", - irq_state[i]); - return -EINVAL; - } - } - - for (i = 0; i < PCI_NUM_PINS; ++i) { - pci_set_irq_state(s, i, irq_state[i]); - } - - return 0; -} - -static void put_pci_irq_state(QEMUFile *f, void *pv, size_t size) -{ - int i; - PCIDevice *s = container_of(pv, PCIDevice, irq_state); - - for (i = 0; i < PCI_NUM_PINS; ++i) { - qemu_put_be32(f, pci_irq_state(s, i)); - } -} - -static VMStateInfo vmstate_info_pci_irq_state = { - .name = "pci irq state", - .get = get_pci_irq_state, - .put = put_pci_irq_state, -}; - -const VMStateDescription vmstate_pci_device = { - .name = "PCIDevice", - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32_POSITIVE_LE(version_id, PCIDevice), - VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0, - vmstate_info_pci_config, - PCI_CONFIG_SPACE_SIZE), - VMSTATE_BUFFER_UNSAFE_INFO(irq_state, PCIDevice, 2, - vmstate_info_pci_irq_state, - PCI_NUM_PINS * sizeof(int32_t)), - VMSTATE_END_OF_LIST() - } -}; - -const VMStateDescription vmstate_pcie_device = { - .name = "PCIEDevice", - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32_POSITIVE_LE(version_id, PCIDevice), - VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0, - vmstate_info_pci_config, - PCIE_CONFIG_SPACE_SIZE), - VMSTATE_BUFFER_UNSAFE_INFO(irq_state, PCIDevice, 2, - vmstate_info_pci_irq_state, - PCI_NUM_PINS * sizeof(int32_t)), - VMSTATE_END_OF_LIST() - } -}; - -static inline const VMStateDescription *pci_get_vmstate(PCIDevice *s) -{ - return pci_is_express(s) ? &vmstate_pcie_device : &vmstate_pci_device; -} - -void pci_device_save(PCIDevice *s, QEMUFile *f) -{ - /* Clear interrupt status bit: it is implicit - * in irq_state which we are saving. - * This makes us compatible with old devices - * which never set or clear this bit. */ - s->config[PCI_STATUS] &= ~PCI_STATUS_INTERRUPT; - vmstate_save_state(f, pci_get_vmstate(s), s, NULL); - /* Restore the interrupt status bit. */ - pci_update_irq_status(s); -} - -int pci_device_load(PCIDevice *s, QEMUFile *f) -{ - int ret; - ret = vmstate_load_state(f, pci_get_vmstate(s), s, s->version_id); - /* Restore the interrupt status bit. */ - pci_update_irq_status(s); - return ret; -} - -static void pci_set_default_subsystem_id(PCIDevice *pci_dev) -{ - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, - pci_default_sub_vendor_id); - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, - pci_default_sub_device_id); -} - -/* - * Parse [[:]:], return -1 on error if funcp == NULL - * [[:]:]., return -1 on error - */ -static int pci_parse_devaddr(const char *addr, int *domp, int *busp, - unsigned int *slotp, unsigned int *funcp) -{ - const char *p; - char *e; - unsigned long val; - unsigned long dom = 0, bus = 0; - unsigned int slot = 0; - unsigned int func = 0; - - p = addr; - val = strtoul(p, &e, 16); - if (e == p) - return -1; - if (*e == ':') { - bus = val; - p = e + 1; - val = strtoul(p, &e, 16); - if (e == p) - return -1; - if (*e == ':') { - dom = bus; - bus = val; - p = e + 1; - val = strtoul(p, &e, 16); - if (e == p) - return -1; - } - } - - slot = val; - - if (funcp != NULL) { - if (*e != '.') - return -1; - - p = e + 1; - val = strtoul(p, &e, 16); - if (e == p) - return -1; - - func = val; - } - - /* if funcp == NULL func is 0 */ - if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7) - return -1; - - if (*e) - return -1; - - *domp = dom; - *busp = bus; - *slotp = slot; - if (funcp != NULL) - *funcp = func; - return 0; -} - -static PCIBus *pci_get_bus_devfn(int *devfnp, PCIBus *root, - const char *devaddr) -{ - int dom, bus; - unsigned slot; - - if (!root) { - fprintf(stderr, "No primary PCI bus\n"); - return NULL; - } - - assert(!root->parent_dev); - - if (!devaddr) { - *devfnp = -1; - return pci_find_bus_nr(root, 0); - } - - if (pci_parse_devaddr(devaddr, &dom, &bus, &slot, NULL) < 0) { - return NULL; - } - - if (dom != 0) { - fprintf(stderr, "No support for non-zero PCI domains\n"); - return NULL; - } - - *devfnp = PCI_DEVFN(slot, 0); - return pci_find_bus_nr(root, bus); -} - -static void pci_init_cmask(PCIDevice *dev) -{ - pci_set_word(dev->cmask + PCI_VENDOR_ID, 0xffff); - pci_set_word(dev->cmask + PCI_DEVICE_ID, 0xffff); - dev->cmask[PCI_STATUS] = PCI_STATUS_CAP_LIST; - dev->cmask[PCI_REVISION_ID] = 0xff; - dev->cmask[PCI_CLASS_PROG] = 0xff; - pci_set_word(dev->cmask + PCI_CLASS_DEVICE, 0xffff); - dev->cmask[PCI_HEADER_TYPE] = 0xff; - dev->cmask[PCI_CAPABILITY_LIST] = 0xff; -} - -static void pci_init_wmask(PCIDevice *dev) -{ - int config_size = pci_config_size(dev); - - dev->wmask[PCI_CACHE_LINE_SIZE] = 0xff; - dev->wmask[PCI_INTERRUPT_LINE] = 0xff; - pci_set_word(dev->wmask + PCI_COMMAND, - PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | - PCI_COMMAND_INTX_DISABLE); - if (dev->cap_present & QEMU_PCI_CAP_SERR) { - pci_word_test_and_set_mask(dev->wmask + PCI_COMMAND, PCI_COMMAND_SERR); - } - - memset(dev->wmask + PCI_CONFIG_HEADER_SIZE, 0xff, - config_size - PCI_CONFIG_HEADER_SIZE); -} - -static void pci_init_w1cmask(PCIDevice *dev) -{ - /* - * Note: It's okay to set w1cmask even for readonly bits as - * long as their value is hardwired to 0. - */ - pci_set_word(dev->w1cmask + PCI_STATUS, - PCI_STATUS_PARITY | PCI_STATUS_SIG_TARGET_ABORT | - PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_REC_MASTER_ABORT | - PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY); -} - -static void pci_init_mask_bridge(PCIDevice *d) -{ - /* PCI_PRIMARY_BUS, PCI_SECONDARY_BUS, PCI_SUBORDINATE_BUS and - PCI_SEC_LETENCY_TIMER */ - memset(d->wmask + PCI_PRIMARY_BUS, 0xff, 4); - - /* base and limit */ - d->wmask[PCI_IO_BASE] = PCI_IO_RANGE_MASK & 0xff; - d->wmask[PCI_IO_LIMIT] = PCI_IO_RANGE_MASK & 0xff; - pci_set_word(d->wmask + PCI_MEMORY_BASE, - PCI_MEMORY_RANGE_MASK & 0xffff); - pci_set_word(d->wmask + PCI_MEMORY_LIMIT, - PCI_MEMORY_RANGE_MASK & 0xffff); - pci_set_word(d->wmask + PCI_PREF_MEMORY_BASE, - PCI_PREF_RANGE_MASK & 0xffff); - pci_set_word(d->wmask + PCI_PREF_MEMORY_LIMIT, - PCI_PREF_RANGE_MASK & 0xffff); - - /* PCI_PREF_BASE_UPPER32 and PCI_PREF_LIMIT_UPPER32 */ - memset(d->wmask + PCI_PREF_BASE_UPPER32, 0xff, 8); - - /* Supported memory and i/o types */ - d->config[PCI_IO_BASE] |= PCI_IO_RANGE_TYPE_16; - d->config[PCI_IO_LIMIT] |= PCI_IO_RANGE_TYPE_16; - pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_BASE, - PCI_PREF_RANGE_TYPE_64); - pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_LIMIT, - PCI_PREF_RANGE_TYPE_64); - - /* - * TODO: Bridges default to 10-bit VGA decoding but we currently only - * implement 16-bit decoding (no alias support). - */ - pci_set_word(d->wmask + PCI_BRIDGE_CONTROL, - PCI_BRIDGE_CTL_PARITY | - PCI_BRIDGE_CTL_SERR | - PCI_BRIDGE_CTL_ISA | - PCI_BRIDGE_CTL_VGA | - PCI_BRIDGE_CTL_VGA_16BIT | - PCI_BRIDGE_CTL_MASTER_ABORT | - PCI_BRIDGE_CTL_BUS_RESET | - PCI_BRIDGE_CTL_FAST_BACK | - PCI_BRIDGE_CTL_DISCARD | - PCI_BRIDGE_CTL_SEC_DISCARD | - PCI_BRIDGE_CTL_DISCARD_SERR); - /* Below does not do anything as we never set this bit, put here for - * completeness. */ - pci_set_word(d->w1cmask + PCI_BRIDGE_CONTROL, - PCI_BRIDGE_CTL_DISCARD_STATUS); - d->cmask[PCI_IO_BASE] |= PCI_IO_RANGE_TYPE_MASK; - d->cmask[PCI_IO_LIMIT] |= PCI_IO_RANGE_TYPE_MASK; - pci_word_test_and_set_mask(d->cmask + PCI_PREF_MEMORY_BASE, - PCI_PREF_RANGE_TYPE_MASK); - pci_word_test_and_set_mask(d->cmask + PCI_PREF_MEMORY_LIMIT, - PCI_PREF_RANGE_TYPE_MASK); -} - -static void pci_init_multifunction(PCIBus *bus, PCIDevice *dev, Error **errp) -{ - uint8_t slot = PCI_SLOT(dev->devfn); - uint8_t func; - - if (dev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { - dev->config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; - } - - /* - * multifunction bit is interpreted in two ways as follows. - * - all functions must set the bit to 1. - * Example: Intel X53 - * - function 0 must set the bit, but the rest function (> 0) - * is allowed to leave the bit to 0. - * Example: PIIX3(also in qemu), PIIX4(also in qemu), ICH10, - * - * So OS (at least Linux) checks the bit of only function 0, - * and doesn't see the bit of function > 0. - * - * The below check allows both interpretation. - */ - if (PCI_FUNC(dev->devfn)) { - PCIDevice *f0 = bus->devices[PCI_DEVFN(slot, 0)]; - if (f0 && !(f0->cap_present & QEMU_PCI_CAP_MULTIFUNCTION)) { - /* function 0 should set multifunction bit */ - error_setg(errp, "PCI: single function device can't be populated " - "in function %x.%x", slot, PCI_FUNC(dev->devfn)); - return; - } - return; - } - - if (dev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { - return; - } - /* function 0 indicates single function, so function > 0 must be NULL */ - for (func = 1; func < PCI_FUNC_MAX; ++func) { - if (bus->devices[PCI_DEVFN(slot, func)]) { - error_setg(errp, "PCI: %x.0 indicates single function, " - "but %x.%x is already populated.", - slot, slot, func); - return; - } - } -} - -static void pci_config_alloc(PCIDevice *pci_dev) -{ - int config_size = pci_config_size(pci_dev); - - pci_dev->config = g_malloc0(config_size); - pci_dev->cmask = g_malloc0(config_size); - pci_dev->wmask = g_malloc0(config_size); - pci_dev->w1cmask = g_malloc0(config_size); - pci_dev->used = g_malloc0(config_size); -} - -static void pci_config_free(PCIDevice *pci_dev) -{ - g_free(pci_dev->config); - g_free(pci_dev->cmask); - g_free(pci_dev->wmask); - g_free(pci_dev->w1cmask); - g_free(pci_dev->used); -} - -static void do_pci_unregister_device(PCIDevice *pci_dev) -{ - pci_dev->bus->devices[pci_dev->devfn] = NULL; - pci_config_free(pci_dev); - - address_space_destroy(&pci_dev->bus_master_as); -} - -/* -1 for devfn means auto assign */ -static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, - const char *name, int devfn, - Error **errp) -{ - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev); - PCIConfigReadFunc *config_read = pc->config_read; - 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); - devfn += PCI_FUNC_MAX) { - if (!bus->devices[devfn]) - goto found; - } - error_setg(errp, "PCI: no slot/function available for %s, all in use", - name); - return NULL; - found: ; - } else if (bus->devices[devfn]) { - error_setg(errp, "PCI: slot %d function %d not available for %s," - " in use by %s", - 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->devfn = devfn; - dma_as = pci_device_iommu_address_space(pci_dev); - - memory_region_init_alias(&pci_dev->bus_master_enable_region, - OBJECT(pci_dev), "bus master", - dma_as->root, 0, memory_region_size(dma_as->root)); - memory_region_set_enabled(&pci_dev->bus_master_enable_region, false); - address_space_init(&pci_dev->bus_master_as, &pci_dev->bus_master_enable_region, - name); - - pstrcpy(pci_dev->name, sizeof(pci_dev->name), name); - pci_dev->irq_state = 0; - pci_config_alloc(pci_dev); - - pci_config_set_vendor_id(pci_dev->config, pc->vendor_id); - pci_config_set_device_id(pci_dev->config, pc->device_id); - pci_config_set_revision(pci_dev->config, pc->revision); - pci_config_set_class(pci_dev->config, pc->class_id); - - if (!pc->is_bridge) { - if (pc->subsystem_vendor_id || pc->subsystem_id) { - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, - pc->subsystem_vendor_id); - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, - pc->subsystem_id); - } else { - pci_set_default_subsystem_id(pci_dev); - } - } else { - /* subsystem_vendor_id/subsystem_id are only for header type 0 */ - assert(!pc->subsystem_vendor_id); - assert(!pc->subsystem_id); - } - pci_init_cmask(pci_dev); - pci_init_wmask(pci_dev); - pci_init_w1cmask(pci_dev); - if (pc->is_bridge) { - pci_init_mask_bridge(pci_dev); - } - pci_init_multifunction(bus, pci_dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - do_pci_unregister_device(pci_dev); - return NULL; - } - - if (!config_read) - config_read = pci_default_read_config; - if (!config_write) - config_write = pci_default_write_config; - pci_dev->config_read = config_read; - pci_dev->config_write = config_write; - bus->devices[devfn] = pci_dev; - pci_dev->version_id = 2; /* Current pci device vmstate version */ - return pci_dev; -} - -static void pci_unregister_io_regions(PCIDevice *pci_dev) -{ - PCIIORegion *r; - int i; - - for(i = 0; i < PCI_NUM_REGIONS; i++) { - r = &pci_dev->io_regions[i]; - if (!r->size || r->addr == PCI_BAR_UNMAPPED) - continue; - memory_region_del_subregion(r->address_space, r->memory); - } - - pci_unregister_vga(pci_dev); -} - -static void pci_qdev_unrealize(DeviceState *dev, Error **errp) -{ - PCIDevice *pci_dev = PCI_DEVICE(dev); - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev); - - pci_unregister_io_regions(pci_dev); - pci_del_option_rom(pci_dev); - - if (pc->exit) { - pc->exit(pci_dev); - } - - do_pci_unregister_device(pci_dev); -} - -void pci_register_bar(PCIDevice *pci_dev, int region_num, - uint8_t type, MemoryRegion *memory) -{ - PCIIORegion *r; - uint32_t addr; - uint64_t wmask; - pcibus_t size = memory_region_size(memory); - - assert(region_num >= 0); - assert(region_num < PCI_NUM_REGIONS); - if (size & (size-1)) { - fprintf(stderr, "ERROR: PCI region size must be pow2 " - "type=0x%x, size=0x%"FMT_PCIBUS"\n", type, size); - exit(1); - } - - r = &pci_dev->io_regions[region_num]; - r->addr = PCI_BAR_UNMAPPED; - r->size = size; - r->type = type; - r->memory = NULL; - - wmask = ~(size - 1); - addr = pci_bar(pci_dev, region_num); - if (region_num == PCI_ROM_SLOT) { - /* ROM enable bit is writable */ - wmask |= PCI_ROM_ADDRESS_ENABLE; - } - pci_set_long(pci_dev->config + addr, type); - if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) && - r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { - pci_set_quad(pci_dev->wmask + addr, wmask); - pci_set_quad(pci_dev->cmask + addr, ~0ULL); - } else { - pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff); - pci_set_long(pci_dev->cmask + addr, 0xffffffff); - } - pci_dev->io_regions[region_num].memory = memory; - pci_dev->io_regions[region_num].address_space - = type & PCI_BASE_ADDRESS_SPACE_IO - ? pci_dev->bus->address_space_io - : pci_dev->bus->address_space_mem; -} - -static void pci_update_vga(PCIDevice *pci_dev) -{ - uint16_t cmd; - - if (!pci_dev->has_vga) { - return; - } - - cmd = pci_get_word(pci_dev->config + PCI_COMMAND); - - memory_region_set_enabled(pci_dev->vga_regions[QEMU_PCI_VGA_MEM], - cmd & PCI_COMMAND_MEMORY); - memory_region_set_enabled(pci_dev->vga_regions[QEMU_PCI_VGA_IO_LO], - cmd & PCI_COMMAND_IO); - memory_region_set_enabled(pci_dev->vga_regions[QEMU_PCI_VGA_IO_HI], - cmd & PCI_COMMAND_IO); -} - -void pci_register_vga(PCIDevice *pci_dev, MemoryRegion *mem, - MemoryRegion *io_lo, MemoryRegion *io_hi) -{ - assert(!pci_dev->has_vga); - - assert(memory_region_size(mem) == QEMU_PCI_VGA_MEM_SIZE); - pci_dev->vga_regions[QEMU_PCI_VGA_MEM] = mem; - memory_region_add_subregion_overlap(pci_dev->bus->address_space_mem, - QEMU_PCI_VGA_MEM_BASE, mem, 1); - - assert(memory_region_size(io_lo) == QEMU_PCI_VGA_IO_LO_SIZE); - pci_dev->vga_regions[QEMU_PCI_VGA_IO_LO] = io_lo; - memory_region_add_subregion_overlap(pci_dev->bus->address_space_io, - QEMU_PCI_VGA_IO_LO_BASE, io_lo, 1); - - assert(memory_region_size(io_hi) == QEMU_PCI_VGA_IO_HI_SIZE); - pci_dev->vga_regions[QEMU_PCI_VGA_IO_HI] = io_hi; - memory_region_add_subregion_overlap(pci_dev->bus->address_space_io, - QEMU_PCI_VGA_IO_HI_BASE, io_hi, 1); - pci_dev->has_vga = true; - - pci_update_vga(pci_dev); -} - -void pci_unregister_vga(PCIDevice *pci_dev) -{ - if (!pci_dev->has_vga) { - return; - } - - memory_region_del_subregion(pci_dev->bus->address_space_mem, - pci_dev->vga_regions[QEMU_PCI_VGA_MEM]); - memory_region_del_subregion(pci_dev->bus->address_space_io, - pci_dev->vga_regions[QEMU_PCI_VGA_IO_LO]); - memory_region_del_subregion(pci_dev->bus->address_space_io, - pci_dev->vga_regions[QEMU_PCI_VGA_IO_HI]); - pci_dev->has_vga = false; -} - -pcibus_t pci_get_bar_addr(PCIDevice *pci_dev, int region_num) -{ - return pci_dev->io_regions[region_num].addr; -} - -static pcibus_t pci_bar_address(PCIDevice *d, - int reg, uint8_t type, pcibus_t size) -{ - 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)) { - return PCI_BAR_UNMAPPED; - } - new_addr = pci_get_long(d->config + bar) & ~(size - 1); - last_addr = new_addr + size - 1; - /* Check if 32 bit BAR wraps around explicitly. - * TODO: make priorities correct and remove this work around. - */ - if (last_addr <= new_addr || last_addr >= UINT32_MAX || - (!allow_0_address && new_addr == 0)) { - return PCI_BAR_UNMAPPED; - } - return new_addr; - } - - if (!(cmd & PCI_COMMAND_MEMORY)) { - return PCI_BAR_UNMAPPED; - } - if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) { - new_addr = pci_get_quad(d->config + bar); - } else { - new_addr = pci_get_long(d->config + bar); - } - /* the ROM slot has a specific enable bit */ - if (reg == PCI_ROM_SLOT && !(new_addr & PCI_ROM_ADDRESS_ENABLE)) { - return PCI_BAR_UNMAPPED; - } - new_addr &= ~(size - 1); - last_addr = new_addr + size - 1; - /* NOTE: we do not support wrapping */ - /* XXX: as we cannot support really dynamic - mappings, we handle specific values as invalid - mappings. */ - if (last_addr <= new_addr || last_addr == PCI_BAR_UNMAPPED || - (!allow_0_address && new_addr == 0)) { - return PCI_BAR_UNMAPPED; - } - - /* Now pcibus_t is 64bit. - * Check if 32 bit BAR wraps around explicitly. - * Without this, PC ide doesn't work well. - * TODO: remove this work around. - */ - if (!(type & PCI_BASE_ADDRESS_MEM_TYPE_64) && last_addr >= UINT32_MAX) { - return PCI_BAR_UNMAPPED; - } - - /* - * OS is allowed to set BAR beyond its addressable - * bits. For example, 32 bit OS can set 64bit bar - * to >4G. Check it. TODO: we might need to support - * it in the future for e.g. PAE. - */ - if (last_addr >= HWADDR_MAX) { - return PCI_BAR_UNMAPPED; - } - - return new_addr; -} - -static void pci_update_mappings(PCIDevice *d) -{ - PCIIORegion *r; - int i; - pcibus_t new_addr; - - for(i = 0; i < PCI_NUM_REGIONS; i++) { - r = &d->io_regions[i]; - - /* this region isn't registered */ - if (!r->size) - continue; - - new_addr = pci_bar_address(d, i, r->type, r->size); - - /* This bar isn't changed */ - if (new_addr == r->addr) - continue; - - /* now do the real mapping */ - if (r->addr != PCI_BAR_UNMAPPED) { - trace_pci_update_mappings_del(d, pci_bus_num(d->bus), - 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_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); - } - } - - pci_update_vga(d); -} - -static inline int pci_irq_disabled(PCIDevice *d) -{ - return pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_INTX_DISABLE; -} - -/* Called after interrupt disabled field update in config space, - * assert/deassert interrupts if necessary. - * Gets original interrupt disable bit value (before update). */ -static void pci_update_irq_disabled(PCIDevice *d, int was_irq_disabled) -{ - int i, disabled = pci_irq_disabled(d); - if (disabled == was_irq_disabled) - return; - for (i = 0; i < PCI_NUM_PINS; ++i) { - int state = pci_irq_state(d, i); - pci_change_irq_level(d, i, disabled ? -state : state); - } -} - -uint32_t pci_default_read_config(PCIDevice *d, - uint32_t address, int len) -{ - uint32_t val = 0; - - memcpy(&val, d->config + address, len); - return le32_to_cpu(val); -} - -void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int l) -{ - int i, was_irq_disabled = pci_irq_disabled(d); - uint32_t val = val_in; - - for (i = 0; i < l; val >>= 8, ++i) { - uint8_t wmask = d->wmask[addr + i]; - uint8_t w1cmask = d->w1cmask[addr + i]; - assert(!(wmask & w1cmask)); - d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask); - d->config[addr + i] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */ - } - if (ranges_overlap(addr, l, PCI_BASE_ADDRESS_0, 24) || - ranges_overlap(addr, l, PCI_ROM_ADDRESS, 4) || - ranges_overlap(addr, l, PCI_ROM_ADDRESS1, 4) || - range_covers_byte(addr, l, PCI_COMMAND)) - pci_update_mappings(d); - - if (range_covers_byte(addr, l, PCI_COMMAND)) { - pci_update_irq_disabled(d, was_irq_disabled); - memory_region_set_enabled(&d->bus_master_enable_region, - pci_get_word(d->config + PCI_COMMAND) - & PCI_COMMAND_MASTER); - } - - msi_write_config(d, addr, val_in, l); - msix_write_config(d, addr, val_in, l); -} - -/***********************************************************/ -/* generic PCI irq support */ - -/* 0 <= irq_num <= 3. level must be 0 or 1 */ -static void pci_irq_handler(void *opaque, int irq_num, int level) -{ - PCIDevice *pci_dev = opaque; - int change; - - change = level - pci_irq_state(pci_dev, irq_num); - if (!change) - return; - - pci_set_irq_state(pci_dev, irq_num, level); - pci_update_irq_status(pci_dev); - if (pci_irq_disabled(pci_dev)) - return; - pci_change_irq_level(pci_dev, irq_num, change); -} - -static inline int pci_intx(PCIDevice *pci_dev) -{ - return pci_get_byte(pci_dev->config + PCI_INTERRUPT_PIN) - 1; -} - -qemu_irq pci_allocate_irq(PCIDevice *pci_dev) -{ - int intx = pci_intx(pci_dev); - - return qemu_allocate_irq(pci_irq_handler, pci_dev, intx); -} - -void pci_set_irq(PCIDevice *pci_dev, int level) -{ - int intx = pci_intx(pci_dev); - pci_irq_handler(pci_dev, intx, level); -} - -/* Special hooks used by device assignment */ -void pci_bus_set_route_irq_fn(PCIBus *bus, pci_route_irq_fn route_intx_to_irq) -{ - assert(pci_bus_is_root(bus)); - bus->route_intx_to_irq = route_intx_to_irq; -} - -PCIINTxRoute pci_device_route_intx_to_irq(PCIDevice *dev, int pin) -{ - PCIBus *bus; - - do { - bus = dev->bus; - pin = bus->map_irq(dev, pin); - dev = bus->parent_dev; - } while (dev); - - if (!bus->route_intx_to_irq) { - error_report("PCI: Bug - unimplemented PCI INTx routing (%s)", - object_get_typename(OBJECT(bus->qbus.parent))); - return (PCIINTxRoute) { PCI_INTX_DISABLED, -1 }; - } - - return bus->route_intx_to_irq(bus->irq_opaque, pin); -} - -bool pci_intx_route_changed(PCIINTxRoute *old, PCIINTxRoute *new) -{ - return old->mode != new->mode || old->irq != new->irq; -} - -void pci_bus_fire_intx_routing_notifier(PCIBus *bus) -{ - PCIDevice *dev; - PCIBus *sec; - int i; - - for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { - dev = bus->devices[i]; - if (dev && dev->intx_routing_notifier) { - dev->intx_routing_notifier(dev); - } - } - - QLIST_FOREACH(sec, &bus->child, sibling) { - pci_bus_fire_intx_routing_notifier(sec); - } -} - -void pci_device_set_intx_routing_notifier(PCIDevice *dev, - PCIINTxRoutingNotifier notifier) -{ - dev->intx_routing_notifier = notifier; -} - -/* - * PCI-to-PCI bridge specification - * 9.1: Interrupt routing. Table 9-1 - * - * the PCI Express Base Specification, Revision 2.1 - * 2.2.8.1: INTx interrutp signaling - Rules - * the Implementation Note - * Table 2-20 - */ -/* - * 0 <= pin <= 3 0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD - * 0-origin unlike PCI interrupt pin register. - */ -int pci_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin) -{ - return (pin + PCI_SLOT(pci_dev->devfn)) % PCI_NUM_PINS; -} - -/***********************************************************/ -/* monitor info on PCI */ - -typedef struct { - uint16_t class; - const char *desc; - const char *fw_name; - uint16_t fw_ign_bits; -} pci_class_desc; - -static const pci_class_desc pci_class_descriptions[] = -{ - { 0x0001, "VGA controller", "display"}, - { 0x0100, "SCSI controller", "scsi"}, - { 0x0101, "IDE controller", "ide"}, - { 0x0102, "Floppy controller", "fdc"}, - { 0x0103, "IPI controller", "ipi"}, - { 0x0104, "RAID controller", "raid"}, - { 0x0106, "SATA controller"}, - { 0x0107, "SAS controller"}, - { 0x0180, "Storage controller"}, - { 0x0200, "Ethernet controller", "ethernet"}, - { 0x0201, "Token Ring controller", "token-ring"}, - { 0x0202, "FDDI controller", "fddi"}, - { 0x0203, "ATM controller", "atm"}, - { 0x0280, "Network controller"}, - { 0x0300, "VGA controller", "display", 0x00ff}, - { 0x0301, "XGA controller"}, - { 0x0302, "3D controller"}, - { 0x0380, "Display controller"}, - { 0x0400, "Video controller", "video"}, - { 0x0401, "Audio controller", "sound"}, - { 0x0402, "Phone"}, - { 0x0403, "Audio controller", "sound"}, - { 0x0480, "Multimedia controller"}, - { 0x0500, "RAM controller", "memory"}, - { 0x0501, "Flash controller", "flash"}, - { 0x0580, "Memory controller"}, - { 0x0600, "Host bridge", "host"}, - { 0x0601, "ISA bridge", "isa"}, - { 0x0602, "EISA bridge", "eisa"}, - { 0x0603, "MC bridge", "mca"}, - { 0x0604, "PCI bridge", "pci-bridge"}, - { 0x0605, "PCMCIA bridge", "pcmcia"}, - { 0x0606, "NUBUS bridge", "nubus"}, - { 0x0607, "CARDBUS bridge", "cardbus"}, - { 0x0608, "RACEWAY bridge"}, - { 0x0680, "Bridge"}, - { 0x0700, "Serial port", "serial"}, - { 0x0701, "Parallel port", "parallel"}, - { 0x0800, "Interrupt controller", "interrupt-controller"}, - { 0x0801, "DMA controller", "dma-controller"}, - { 0x0802, "Timer", "timer"}, - { 0x0803, "RTC", "rtc"}, - { 0x0900, "Keyboard", "keyboard"}, - { 0x0901, "Pen", "pen"}, - { 0x0902, "Mouse", "mouse"}, - { 0x0A00, "Dock station", "dock", 0x00ff}, - { 0x0B00, "i386 cpu", "cpu", 0x00ff}, - { 0x0c00, "Fireware contorller", "fireware"}, - { 0x0c01, "Access bus controller", "access-bus"}, - { 0x0c02, "SSA controller", "ssa"}, - { 0x0c03, "USB controller", "usb"}, - { 0x0c04, "Fibre channel controller", "fibre-channel"}, - { 0x0c05, "SMBus"}, - { 0, NULL} -}; - -static void pci_for_each_device_under_bus(PCIBus *bus, - void (*fn)(PCIBus *b, PCIDevice *d, - void *opaque), - void *opaque) -{ - PCIDevice *d; - int devfn; - - for(devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { - d = bus->devices[devfn]; - if (d) { - fn(bus, d, opaque); - } - } -} - -void pci_for_each_device(PCIBus *bus, int bus_num, - void (*fn)(PCIBus *b, PCIDevice *d, void *opaque), - void *opaque) -{ - bus = pci_find_bus_nr(bus, bus_num); - - if (bus) { - pci_for_each_device_under_bus(bus, fn, opaque); - } -} - -static const pci_class_desc *get_class_desc(int class) -{ - const pci_class_desc *desc; - - desc = pci_class_descriptions; - while (desc->desc && class != desc->class) { - desc++; - } - - return desc; -} - -static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num); - -static PciMemoryRegionList *qmp_query_pci_regions(const PCIDevice *dev) -{ - PciMemoryRegionList *head = NULL, *cur_item = NULL; - int i; - - for (i = 0; i < PCI_NUM_REGIONS; i++) { - const PCIIORegion *r = &dev->io_regions[i]; - PciMemoryRegionList *region; - - if (!r->size) { - continue; - } - - region = g_malloc0(sizeof(*region)); - region->value = g_malloc0(sizeof(*region->value)); - - if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { - region->value->type = g_strdup("io"); - } else { - region->value->type = g_strdup("memory"); - region->value->has_prefetch = true; - region->value->prefetch = !!(r->type & PCI_BASE_ADDRESS_MEM_PREFETCH); - region->value->has_mem_type_64 = true; - region->value->mem_type_64 = !!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64); - } - - region->value->bar = i; - region->value->address = r->addr; - region->value->size = r->size; - - /* XXX: waiting for the qapi to support GSList */ - if (!cur_item) { - head = cur_item = region; - } else { - cur_item->next = region; - cur_item = region; - } - } - - return head; -} - -static PciBridgeInfo *qmp_query_pci_bridge(PCIDevice *dev, PCIBus *bus, - int bus_num) -{ - PciBridgeInfo *info; - PciMemoryRange *range; - - info = g_new0(PciBridgeInfo, 1); - - info->bus = g_new0(PciBusInfo, 1); - info->bus->number = dev->config[PCI_PRIMARY_BUS]; - info->bus->secondary = dev->config[PCI_SECONDARY_BUS]; - info->bus->subordinate = dev->config[PCI_SUBORDINATE_BUS]; - - range = info->bus->io_range = g_new0(PciMemoryRange, 1); - range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO); - range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO); - - range = info->bus->memory_range = g_new0(PciMemoryRange, 1); - range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); - range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); - - range = info->bus->prefetchable_range = g_new0(PciMemoryRange, 1); - range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - - if (dev->config[PCI_SECONDARY_BUS] != 0) { - PCIBus *child_bus = pci_find_bus_nr(bus, dev->config[PCI_SECONDARY_BUS]); - if (child_bus) { - info->has_devices = true; - info->devices = qmp_query_pci_devices(child_bus, dev->config[PCI_SECONDARY_BUS]); - } - } - - return info; -} - -static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus, - int bus_num) -{ - const pci_class_desc *desc; - PciDeviceInfo *info; - uint8_t type; - int class; - - info = g_new0(PciDeviceInfo, 1); - info->bus = bus_num; - info->slot = PCI_SLOT(dev->devfn); - info->function = PCI_FUNC(dev->devfn); - - info->class_info = g_new0(PciDeviceClass, 1); - class = pci_get_word(dev->config + PCI_CLASS_DEVICE); - info->class_info->q_class = class; - desc = get_class_desc(class); - if (desc->desc) { - info->class_info->has_desc = true; - info->class_info->desc = g_strdup(desc->desc); - } - - info->id = g_new0(PciDeviceId, 1); - info->id->vendor = pci_get_word(dev->config + PCI_VENDOR_ID); - info->id->device = pci_get_word(dev->config + PCI_DEVICE_ID); - info->regions = qmp_query_pci_regions(dev); - info->qdev_id = g_strdup(dev->qdev.id ? dev->qdev.id : ""); - - if (dev->config[PCI_INTERRUPT_PIN] != 0) { - info->has_irq = true; - info->irq = dev->config[PCI_INTERRUPT_LINE]; - } - - type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; - if (type == PCI_HEADER_TYPE_BRIDGE) { - info->has_pci_bridge = true; - info->pci_bridge = qmp_query_pci_bridge(dev, bus, bus_num); - } - - return info; -} - -static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num) -{ - PciDeviceInfoList *info, *head = NULL, *cur_item = NULL; - PCIDevice *dev; - int devfn; - - for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { - dev = bus->devices[devfn]; - if (dev) { - info = g_malloc0(sizeof(*info)); - info->value = qmp_query_pci_device(dev, bus, bus_num); - - /* XXX: waiting for the qapi to support GSList */ - if (!cur_item) { - head = cur_item = info; - } else { - cur_item->next = info; - cur_item = info; - } - } - } - - return head; -} - -static PciInfo *qmp_query_pci_bus(PCIBus *bus, int bus_num) -{ - PciInfo *info = NULL; - - bus = pci_find_bus_nr(bus, bus_num); - if (bus) { - info = g_malloc0(sizeof(*info)); - info->bus = bus_num; - info->devices = qmp_query_pci_devices(bus, bus_num); - } - - return info; -} - -PciInfoList *qmp_query_pci(Error **errp) -{ - PciInfoList *info, *head = NULL, *cur_item = NULL; - PCIHostState *host_bridge; - - QLIST_FOREACH(host_bridge, &pci_host_bridges, next) { - info = g_malloc0(sizeof(*info)); - info->value = qmp_query_pci_bus(host_bridge->bus, - pci_bus_num(host_bridge->bus)); - - /* XXX: waiting for the qapi to support GSList */ - if (!cur_item) { - head = cur_item = info; - } else { - cur_item->next = info; - cur_item = info; - } - } - - return head; -} - -static const char * const pci_nic_models[] = { - "ne2k_pci", - "i82551", - "i82557b", - "i82559er", - "rtl8139", - "e1000", - "pcnet", - "virtio", - NULL -}; - -static const char * const pci_nic_names[] = { - "ne2k_pci", - "i82551", - "i82557b", - "i82559er", - "rtl8139", - "e1000", - "pcnet", - "virtio-net-pci", - NULL -}; - -/* Initialize a PCI NIC. */ -PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus, - const char *default_model, - const char *default_devaddr) -{ - const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr; - Error *err = NULL; - PCIBus *bus; - PCIDevice *pci_dev; - DeviceState *dev; - int devfn; - int i; - - if (qemu_show_nic_models(nd->model, pci_nic_models)) { - exit(0); - } - - i = qemu_find_nic_model(nd, pci_nic_models, default_model); - if (i < 0) { - exit(1); - } - - bus = pci_get_bus_devfn(&devfn, rootbus, devaddr); - if (!bus) { - error_report("Invalid PCI device address %s for device %s", - devaddr, pci_nic_names[i]); - exit(1); - } - - pci_dev = pci_create(bus, devfn, pci_nic_names[i]); - dev = &pci_dev->qdev; - qdev_set_nic_properties(dev, nd); - - object_property_set_bool(OBJECT(dev), true, "realized", &err); - if (err) { - error_report_err(err); - object_unparent(OBJECT(dev)); - exit(1); - } - - return pci_dev; -} - -PCIDevice *pci_vga_init(PCIBus *bus) -{ - switch (vga_interface_type) { - case VGA_CIRRUS: - return pci_create_simple(bus, -1, "cirrus-vga"); - case VGA_QXL: - return pci_create_simple(bus, -1, "qxl-vga"); - case VGA_STD: - return pci_create_simple(bus, -1, "VGA"); - case VGA_VMWARE: - return pci_create_simple(bus, -1, "vmware-svga"); - case VGA_VIRTIO: - return pci_create_simple(bus, -1, "virtio-vga"); - case VGA_NONE: - default: /* Other non-PCI types. Checking for unsupported types is already - done in vl.c. */ - return NULL; - } -} - -/* Whether a given bus number is in range of the secondary - * bus of the given bridge device. */ -static bool pci_secondary_bus_in_range(PCIDevice *dev, int bus_num) -{ - return !(pci_get_word(dev->config + PCI_BRIDGE_CONTROL) & - PCI_BRIDGE_CTL_BUS_RESET) /* Don't walk the bus if it's reset. */ && - dev->config[PCI_SECONDARY_BUS] <= bus_num && - bus_num <= dev->config[PCI_SUBORDINATE_BUS]; -} - -/* Whether a given bus number is in a range of a root bus */ -static bool pci_root_bus_in_range(PCIBus *bus, int bus_num) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { - PCIDevice *dev = bus->devices[i]; - - if (dev && PCI_DEVICE_GET_CLASS(dev)->is_bridge) { - if (pci_secondary_bus_in_range(dev, bus_num)) { - return true; - } - } - } - - return false; -} - -static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num) -{ - PCIBus *sec; - - if (!bus) { - return NULL; - } - - if (pci_bus_num(bus) == bus_num) { - return bus; - } - - /* Consider all bus numbers in range for the host pci bridge. */ - if (!pci_bus_is_root(bus) && - !pci_secondary_bus_in_range(bus->parent_dev, bus_num)) { - return NULL; - } - - /* try child bus */ - for (; bus; bus = sec) { - QLIST_FOREACH(sec, &bus->child, sibling) { - if (pci_bus_num(sec) == bus_num) { - return sec; - } - /* PXB buses assumed to be children of bus 0 */ - if (pci_bus_is_root(sec)) { - if (pci_root_bus_in_range(sec, bus_num)) { - break; - } - } else { - if (pci_secondary_bus_in_range(sec->parent_dev, bus_num)) { - break; - } - } - } - } - - return NULL; -} - -void pci_for_each_bus_depth_first(PCIBus *bus, - void *(*begin)(PCIBus *bus, void *parent_state), - void (*end)(PCIBus *bus, void *state), - void *parent_state) -{ - PCIBus *sec; - void *state; - - if (!bus) { - return; - } - - if (begin) { - state = begin(bus, parent_state); - } else { - state = parent_state; - } - - QLIST_FOREACH(sec, &bus->child, sibling) { - pci_for_each_bus_depth_first(sec, begin, end, state); - } - - if (end) { - end(bus, state); - } -} - - -PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn) -{ - bus = pci_find_bus_nr(bus, bus_num); - - if (!bus) - return NULL; - - return bus->devices[devfn]; -} - -static void pci_qdev_realize(DeviceState *qdev, Error **errp) -{ - PCIDevice *pci_dev = (PCIDevice *)qdev; - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev); - Error *local_err = NULL; - PCIBus *bus; - bool is_default_rom; - - /* initialize cap_present for pci_is_express() and pci_config_size() */ - if (pc->is_express) { - pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; - } - - bus = PCI_BUS(qdev_get_parent_bus(qdev)); - pci_dev = do_pci_register_device(pci_dev, bus, - object_get_typename(OBJECT(qdev)), - pci_dev->devfn, errp); - if (pci_dev == NULL) - return; - - if (pc->realize) { - pc->realize(pci_dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - do_pci_unregister_device(pci_dev); - return; - } - } - - /* rom loading */ - is_default_rom = false; - if (pci_dev->romfile == NULL && pc->romfile != NULL) { - pci_dev->romfile = g_strdup(pc->romfile); - is_default_rom = true; - } - - pci_add_option_rom(pci_dev, is_default_rom, &local_err); - if (local_err) { - error_propagate(errp, local_err); - pci_qdev_unrealize(DEVICE(pci_dev), NULL); - return; - } -} - -static void pci_default_realize(PCIDevice *dev, Error **errp) -{ - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); - - if (pc->init) { - if (pc->init(dev) < 0) { - error_setg(errp, "Device initialization failed"); - return; - } - } -} - -PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction, - const char *name) -{ - DeviceState *dev; - - dev = qdev_create(&bus->qbus, name); - qdev_prop_set_int32(dev, "addr", devfn); - qdev_prop_set_bit(dev, "multifunction", multifunction); - return PCI_DEVICE(dev); -} - -PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn, - bool multifunction, - const char *name) -{ - PCIDevice *dev = pci_create_multifunction(bus, devfn, multifunction, name); - qdev_init_nofail(&dev->qdev); - return dev; -} - -PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name) -{ - return pci_create_multifunction(bus, devfn, false, name); -} - -PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name) -{ - return pci_create_simple_multifunction(bus, devfn, false, name); -} - -static uint8_t pci_find_space(PCIDevice *pdev, uint8_t size) -{ - int offset = PCI_CONFIG_HEADER_SIZE; - int i; - for (i = PCI_CONFIG_HEADER_SIZE; i < PCI_CONFIG_SPACE_SIZE; ++i) { - if (pdev->used[i]) - offset = i + 1; - else if (i - offset + 1 == size) - return offset; - } - return 0; -} - -static uint8_t pci_find_capability_list(PCIDevice *pdev, uint8_t cap_id, - uint8_t *prev_p) -{ - uint8_t next, prev; - - if (!(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST)) - return 0; - - for (prev = PCI_CAPABILITY_LIST; (next = pdev->config[prev]); - prev = next + PCI_CAP_LIST_NEXT) - if (pdev->config[next + PCI_CAP_LIST_ID] == cap_id) - break; - - if (prev_p) - *prev_p = prev; - return next; -} - -static uint8_t pci_find_capability_at_offset(PCIDevice *pdev, uint8_t offset) -{ - uint8_t next, prev, found = 0; - - if (!(pdev->used[offset])) { - return 0; - } - - assert(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST); - - for (prev = PCI_CAPABILITY_LIST; (next = pdev->config[prev]); - prev = next + PCI_CAP_LIST_NEXT) { - if (next <= offset && next > found) { - found = next; - } - } - return found; -} - -/* Patch the PCI vendor and device ids in a PCI rom image if necessary. - This is needed for an option rom which is used for more than one device. */ -static void pci_patch_ids(PCIDevice *pdev, uint8_t *ptr, int size) -{ - uint16_t vendor_id; - uint16_t device_id; - uint16_t rom_vendor_id; - uint16_t rom_device_id; - uint16_t rom_magic; - uint16_t pcir_offset; - uint8_t checksum; - - /* Words in rom data are little endian (like in PCI configuration), - so they can be read / written with pci_get_word / pci_set_word. */ - - /* Only a valid rom will be patched. */ - rom_magic = pci_get_word(ptr); - if (rom_magic != 0xaa55) { - PCI_DPRINTF("Bad ROM magic %04x\n", rom_magic); - return; - } - pcir_offset = pci_get_word(ptr + 0x18); - if (pcir_offset + 8 >= size || memcmp(ptr + pcir_offset, "PCIR", 4)) { - PCI_DPRINTF("Bad PCIR offset 0x%x or signature\n", pcir_offset); - return; - } - - vendor_id = pci_get_word(pdev->config + PCI_VENDOR_ID); - device_id = pci_get_word(pdev->config + PCI_DEVICE_ID); - rom_vendor_id = pci_get_word(ptr + pcir_offset + 4); - rom_device_id = pci_get_word(ptr + pcir_offset + 6); - - PCI_DPRINTF("%s: ROM id %04x%04x / PCI id %04x%04x\n", pdev->romfile, - vendor_id, device_id, rom_vendor_id, rom_device_id); - - checksum = ptr[6]; - - if (vendor_id != rom_vendor_id) { - /* Patch vendor id and checksum (at offset 6 for etherboot roms). */ - checksum += (uint8_t)rom_vendor_id + (uint8_t)(rom_vendor_id >> 8); - checksum -= (uint8_t)vendor_id + (uint8_t)(vendor_id >> 8); - PCI_DPRINTF("ROM checksum %02x / %02x\n", ptr[6], checksum); - ptr[6] = checksum; - pci_set_word(ptr + pcir_offset + 4, vendor_id); - } - - if (device_id != rom_device_id) { - /* Patch device id and checksum (at offset 6 for etherboot roms). */ - checksum += (uint8_t)rom_device_id + (uint8_t)(rom_device_id >> 8); - checksum -= (uint8_t)device_id + (uint8_t)(device_id >> 8); - PCI_DPRINTF("ROM checksum %02x / %02x\n", ptr[6], checksum); - ptr[6] = checksum; - pci_set_word(ptr + pcir_offset + 6, device_id); - } -} - -/* Add an option rom for the device */ -static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, - Error **errp) -{ - int size; - char *path; - void *ptr; - char name[32]; - const VMStateDescription *vmsd; - - if (!pdev->romfile) - return; - if (strlen(pdev->romfile) == 0) - return; - - if (!pdev->rom_bar) { - /* - * Load rom via fw_cfg instead of creating a rom bar, - * for 0.11 compatibility. - */ - int class = pci_get_word(pdev->config + PCI_CLASS_DEVICE); - - /* - * Hot-plugged devices can't use the option ROM - * if the rom bar is disabled. - */ - if (DEVICE(pdev)->hotplugged) { - error_setg(errp, "Hot-plugged device without ROM bar" - " can't have an option ROM"); - return; - } - - if (class == 0x0300) { - rom_add_vga(pdev->romfile); - } else { - rom_add_option(pdev->romfile, -1); - } - return; - } - - path = qemu_find_file(QEMU_FILE_TYPE_BIOS, pdev->romfile); - if (path == NULL) { - path = g_strdup(pdev->romfile); - } - - size = get_image_size(path); - if (size < 0) { - error_setg(errp, "failed to find romfile \"%s\"", pdev->romfile); - g_free(path); - return; - } else if (size == 0) { - error_setg(errp, "romfile \"%s\" is empty", pdev->romfile); - g_free(path); - return; - } - size = pow2ceil(size); - - vmsd = qdev_get_vmsd(DEVICE(pdev)); - - if (vmsd) { - snprintf(name, sizeof(name), "%s.rom", vmsd->name); - } else { - 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_fatal); - vmstate_register_ram(&pdev->rom, &pdev->qdev); - ptr = memory_region_get_ram_ptr(&pdev->rom); - load_image(path, ptr); - g_free(path); - - if (is_default_rom) { - /* Only the default rom images will be patched (if needed). */ - pci_patch_ids(pdev, ptr, size); - } - - pci_register_bar(pdev, PCI_ROM_SLOT, 0, &pdev->rom); -} - -static void pci_del_option_rom(PCIDevice *pdev) -{ - if (!pdev->has_rom) - return; - - vmstate_unregister_ram(&pdev->rom, &pdev->qdev); - pdev->has_rom = false; -} - -/* - * if offset = 0, - * Find and reserve space and add capability to the linked list - * in pci config space - */ -int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, - uint8_t offset, uint8_t size) -{ - int ret; - Error *local_err = NULL; - - ret = pci_add_capability2(pdev, cap_id, offset, size, &local_err); - if (local_err) { - assert(ret < 0); - error_report_err(local_err); - } else { - /* success implies a positive offset in config space */ - assert(ret > 0); - } - return ret; -} - -int pci_add_capability2(PCIDevice *pdev, uint8_t cap_id, - uint8_t offset, uint8_t size, - Error **errp) -{ - uint8_t *config; - int i, overlapping_cap; - - if (!offset) { - offset = pci_find_space(pdev, size); - if (!offset) { - error_setg(errp, "out of PCI config space"); - return -ENOSPC; - } - } else { - /* Verify that capabilities don't overlap. Note: device assignment - * depends on this check to verify that the device is not broken. - * Should never trigger for emulated devices, but it's helpful - * for debugging these. */ - for (i = offset; i < offset + size; i++) { - overlapping_cap = pci_find_capability_at_offset(pdev, i); - if (overlapping_cap) { - error_setg(errp, "%s:%02x:%02x.%x " - "Attempt to add PCI capability %x at offset " - "%x overlaps existing capability %x at offset %x", - pci_root_bus_path(pdev), pci_bus_num(pdev->bus), - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), - cap_id, offset, overlapping_cap, i); - return -EINVAL; - } - } - } - - config = pdev->config + offset; - config[PCI_CAP_LIST_ID] = cap_id; - config[PCI_CAP_LIST_NEXT] = pdev->config[PCI_CAPABILITY_LIST]; - pdev->config[PCI_CAPABILITY_LIST] = offset; - pdev->config[PCI_STATUS] |= PCI_STATUS_CAP_LIST; - memset(pdev->used + offset, 0xFF, QEMU_ALIGN_UP(size, 4)); - /* Make capability read-only by default */ - memset(pdev->wmask + offset, 0, size); - /* Check capability by default */ - memset(pdev->cmask + offset, 0xFF, size); - return offset; -} - -/* Unlink capability from the pci config space. */ -void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) -{ - uint8_t prev, offset = pci_find_capability_list(pdev, cap_id, &prev); - if (!offset) - return; - pdev->config[prev] = pdev->config[offset + PCI_CAP_LIST_NEXT]; - /* Make capability writable again */ - memset(pdev->wmask + offset, 0xff, size); - memset(pdev->w1cmask + offset, 0, size); - /* Clear cmask as device-specific registers can't be checked */ - memset(pdev->cmask + offset, 0, size); - memset(pdev->used + offset, 0, QEMU_ALIGN_UP(size, 4)); - - if (!pdev->config[PCI_CAPABILITY_LIST]) - pdev->config[PCI_STATUS] &= ~PCI_STATUS_CAP_LIST; -} - -uint8_t pci_find_capability(PCIDevice *pdev, uint8_t cap_id) -{ - return pci_find_capability_list(pdev, cap_id, NULL); -} - -static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent) -{ - PCIDevice *d = (PCIDevice *)dev; - const pci_class_desc *desc; - char ctxt[64]; - PCIIORegion *r; - int i, class; - - class = pci_get_word(d->config + PCI_CLASS_DEVICE); - desc = pci_class_descriptions; - while (desc->desc && class != desc->class) - desc++; - if (desc->desc) { - snprintf(ctxt, sizeof(ctxt), "%s", desc->desc); - } else { - snprintf(ctxt, sizeof(ctxt), "Class %04x", class); - } - - monitor_printf(mon, "%*sclass %s, addr %02x:%02x.%x, " - "pci id %04x:%04x (sub %04x:%04x)\n", - indent, "", ctxt, pci_bus_num(d->bus), - PCI_SLOT(d->devfn), PCI_FUNC(d->devfn), - pci_get_word(d->config + PCI_VENDOR_ID), - pci_get_word(d->config + PCI_DEVICE_ID), - pci_get_word(d->config + PCI_SUBSYSTEM_VENDOR_ID), - pci_get_word(d->config + PCI_SUBSYSTEM_ID)); - for (i = 0; i < PCI_NUM_REGIONS; i++) { - r = &d->io_regions[i]; - if (!r->size) - continue; - monitor_printf(mon, "%*sbar %d: %s at 0x%"FMT_PCIBUS - " [0x%"FMT_PCIBUS"]\n", - indent, "", - i, r->type & PCI_BASE_ADDRESS_SPACE_IO ? "i/o" : "mem", - r->addr, r->addr + r->size - 1); - } -} - -static char *pci_dev_fw_name(DeviceState *dev, char *buf, int len) -{ - PCIDevice *d = (PCIDevice *)dev; - const char *name = NULL; - const pci_class_desc *desc = pci_class_descriptions; - int class = pci_get_word(d->config + PCI_CLASS_DEVICE); - - while (desc->desc && - (class & ~desc->fw_ign_bits) != - (desc->class & ~desc->fw_ign_bits)) { - desc++; - } - - if (desc->desc) { - name = desc->fw_name; - } - - if (name) { - pstrcpy(buf, len, name); - } else { - snprintf(buf, len, "pci%04x,%04x", - pci_get_word(d->config + PCI_VENDOR_ID), - pci_get_word(d->config + PCI_DEVICE_ID)); - } - - return buf; -} - -static char *pcibus_get_fw_dev_path(DeviceState *dev) -{ - PCIDevice *d = (PCIDevice *)dev; - char path[50], name[33]; - int off; - - off = snprintf(path, sizeof(path), "%s@%x", - pci_dev_fw_name(dev, name, sizeof name), - PCI_SLOT(d->devfn)); - if (PCI_FUNC(d->devfn)) - snprintf(path + off, sizeof(path) + off, ",%x", PCI_FUNC(d->devfn)); - return g_strdup(path); -} - -static char *pcibus_get_dev_path(DeviceState *dev) -{ - PCIDevice *d = container_of(dev, PCIDevice, qdev); - PCIDevice *t; - int slot_depth; - /* Path format: Domain:00:Slot.Function:Slot.Function....:Slot.Function. - * 00 is added here to make this format compatible with - * domain:Bus:Slot.Func for systems without nested PCI bridges. - * Slot.Function list specifies the slot and function numbers for all - * devices on the path from root to the specific device. */ - const char *root_bus_path; - int root_bus_len; - char slot[] = ":SS.F"; - int slot_len = sizeof slot - 1 /* For '\0' */; - int path_len; - char *path, *p; - int s; - - root_bus_path = pci_root_bus_path(d); - root_bus_len = strlen(root_bus_path); - - /* Calculate # of slots on path between device and root. */; - slot_depth = 0; - for (t = d; t; t = t->bus->parent_dev) { - ++slot_depth; - } - - path_len = root_bus_len + slot_len * slot_depth; - - /* Allocate memory, fill in the terminating null byte. */ - path = g_malloc(path_len + 1 /* For '\0' */); - path[path_len] = '\0'; - - memcpy(path, root_bus_path, root_bus_len); - - /* Fill in slot numbers. We walk up from device to root, so need to print - * them in the reverse order, last to first. */ - p = path + path_len; - for (t = d; t; t = t->bus->parent_dev) { - p -= slot_len; - s = snprintf(slot, sizeof slot, ":%02x.%x", - PCI_SLOT(t->devfn), PCI_FUNC(t->devfn)); - assert(s == slot_len); - memcpy(p, slot, slot_len); - } - - return path; -} - -static int pci_qdev_find_recursive(PCIBus *bus, - const char *id, PCIDevice **pdev) -{ - DeviceState *qdev = qdev_find_recursive(&bus->qbus, id); - if (!qdev) { - return -ENODEV; - } - - /* roughly check if given qdev is pci device */ - if (object_dynamic_cast(OBJECT(qdev), TYPE_PCI_DEVICE)) { - *pdev = PCI_DEVICE(qdev); - return 0; - } - return -EINVAL; -} - -int pci_qdev_find_device(const char *id, PCIDevice **pdev) -{ - PCIHostState *host_bridge; - int rc = -ENODEV; - - QLIST_FOREACH(host_bridge, &pci_host_bridges, next) { - int tmp = pci_qdev_find_recursive(host_bridge->bus, id, pdev); - if (!tmp) { - rc = 0; - break; - } - if (tmp != -ENODEV) { - rc = tmp; - } - } - - return rc; -} - -MemoryRegion *pci_address_space(PCIDevice *dev) -{ - return dev->bus->address_space_mem; -} - -MemoryRegion *pci_address_space_io(PCIDevice *dev) -{ - return dev->bus->address_space_io; -} - -static void pci_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); - - k->realize = pci_qdev_realize; - k->unrealize = pci_qdev_unrealize; - k->bus_type = TYPE_PCI_BUS; - k->props = pci_props; - pc->realize = pci_default_realize; -} - -AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) -{ - PCIBus *bus = PCI_BUS(dev->bus); - PCIBus *iommu_bus = bus; - - while(iommu_bus && !iommu_bus->iommu_fn && iommu_bus->parent_dev) { - iommu_bus = PCI_BUS(iommu_bus->parent_dev->bus); - } - if (iommu_bus && iommu_bus->iommu_fn) { - return iommu_bus->iommu_fn(bus, iommu_bus->iommu_opaque, dev->devfn); - } - return &address_space_memory; -} - -void pci_setup_iommu(PCIBus *bus, PCIIOMMUFunc fn, void *opaque) -{ - bus->iommu_fn = fn; - bus->iommu_opaque = opaque; -} - -static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) -{ - Range *range = opaque; - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); - uint16_t cmd = pci_get_word(dev->config + PCI_COMMAND); - int i; - - if (!(cmd & PCI_COMMAND_MEMORY)) { - return; - } - - if (pc->is_bridge) { - pcibus_t base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - pcibus_t limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - - base = MAX(base, 0x1ULL << 32); - - if (limit >= base) { - Range pref_range; - pref_range.begin = base; - pref_range.end = limit + 1; - range_extend(range, &pref_range); - } - } - for (i = 0; i < PCI_NUM_REGIONS; ++i) { - PCIIORegion *r = &dev->io_regions[i]; - Range region_range; - - if (!r->size || - (r->type & PCI_BASE_ADDRESS_SPACE_IO) || - !(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64)) { - continue; - } - region_range.begin = pci_bar_address(dev, i, r->type, r->size); - region_range.end = region_range.begin + r->size; - - if (region_range.begin == PCI_BAR_UNMAPPED) { - continue; - } - - region_range.begin = MAX(region_range.begin, 0x1ULL << 32); - - if (region_range.end - 1 >= region_range.begin) { - range_extend(range, ®ion_range); - } - } -} - -void pci_bus_get_w64_range(PCIBus *bus, Range *range) -{ - range->begin = range->end = 0; - 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, - .instance_size = sizeof(PCIDevice), - .abstract = true, - .class_size = sizeof(PCIDeviceClass), - .class_init = pci_device_class_init, -}; - -static void pci_register_types(void) -{ - type_register_static(&pci_bus_info); - type_register_static(&pcie_bus_info); - type_register_static(&pci_device_type_info); -} - -type_init(pci_register_types) diff --git a/qemu/hw/pci/pci_bridge.c b/qemu/hw/pci/pci_bridge.c deleted file mode 100644 index 3cf30bd33..000000000 --- a/qemu/hw/pci/pci_bridge.c +++ /dev/null @@ -1,419 +0,0 @@ -/* - * QEMU PCI bus manager - * - * Copyright (c) 2004 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to dea - - * 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. - */ -/* - * split out from pci.c - * Copyright (c) 2010 Isaku Yamahata - * 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" - -/* PCI bridge subsystem vendor ID helper functions */ -#define PCI_SSVID_SIZEOF 8 -#define PCI_SSVID_SVID 4 -#define PCI_SSVID_SSID 6 - -int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset, - uint16_t svid, uint16_t ssid) -{ - int pos; - pos = pci_add_capability(dev, PCI_CAP_ID_SSVID, offset, PCI_SSVID_SIZEOF); - if (pos < 0) { - return pos; - } - - pci_set_word(dev->config + pos + PCI_SSVID_SVID, svid); - pci_set_word(dev->config + pos + PCI_SSVID_SSID, ssid); - return pos; -} - -/* Accessor function to get parent bridge device from pci bus. */ -PCIDevice *pci_bridge_get_device(PCIBus *bus) -{ - return bus->parent_dev; -} - -/* Accessor function to get secondary bus from pci-to-pci bridge device */ -PCIBus *pci_bridge_get_sec_bus(PCIBridge *br) -{ - return &br->sec_bus; -} - -static uint32_t pci_config_get_io_base(const PCIDevice *d, - uint32_t base, uint32_t base_upper16) -{ - uint32_t val; - - val = ((uint32_t)d->config[base] & PCI_IO_RANGE_MASK) << 8; - if (d->config[base] & PCI_IO_RANGE_TYPE_32) { - val |= (uint32_t)pci_get_word(d->config + base_upper16) << 16; - } - return val; -} - -static pcibus_t pci_config_get_memory_base(const PCIDevice *d, uint32_t base) -{ - return ((pcibus_t)pci_get_word(d->config + base) & PCI_MEMORY_RANGE_MASK) - << 16; -} - -static pcibus_t pci_config_get_pref_base(const PCIDevice *d, - uint32_t base, uint32_t upper) -{ - pcibus_t tmp; - pcibus_t val; - - tmp = (pcibus_t)pci_get_word(d->config + base); - val = (tmp & PCI_PREF_RANGE_MASK) << 16; - if (tmp & PCI_PREF_RANGE_TYPE_64) { - val |= (pcibus_t)pci_get_long(d->config + upper) << 32; - } - return val; -} - -/* accessor function to get bridge filtering base address */ -pcibus_t pci_bridge_get_base(const PCIDevice *bridge, uint8_t type) -{ - pcibus_t base; - if (type & PCI_BASE_ADDRESS_SPACE_IO) { - base = pci_config_get_io_base(bridge, - PCI_IO_BASE, PCI_IO_BASE_UPPER16); - } else { - if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) { - base = pci_config_get_pref_base( - bridge, PCI_PREF_MEMORY_BASE, PCI_PREF_BASE_UPPER32); - } else { - base = pci_config_get_memory_base(bridge, PCI_MEMORY_BASE); - } - } - - return base; -} - -/* accessor funciton to get bridge filtering limit */ -pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type) -{ - pcibus_t limit; - if (type & PCI_BASE_ADDRESS_SPACE_IO) { - limit = pci_config_get_io_base(bridge, - PCI_IO_LIMIT, PCI_IO_LIMIT_UPPER16); - limit |= 0xfff; /* PCI bridge spec 3.2.5.6. */ - } else { - if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) { - limit = pci_config_get_pref_base( - bridge, PCI_PREF_MEMORY_LIMIT, PCI_PREF_LIMIT_UPPER32); - } else { - limit = pci_config_get_memory_base(bridge, PCI_MEMORY_LIMIT); - } - limit |= 0xfffff; /* PCI bridge spec 3.2.5.{1, 8}. */ - } - return limit; -} - -static void pci_bridge_init_alias(PCIBridge *bridge, MemoryRegion *alias, - uint8_t type, const char *name, - MemoryRegion *space, - MemoryRegion *parent_space, - bool enabled) -{ - PCIDevice *bridge_dev = PCI_DEVICE(bridge); - pcibus_t base = pci_bridge_get_base(bridge_dev, type); - pcibus_t limit = pci_bridge_get_limit(bridge_dev, type); - /* TODO: this doesn't handle base = 0 limit = 2^64 - 1 correctly. - * Apparently no way to do this with existing memory APIs. */ - pcibus_t size = enabled && limit >= base ? limit + 1 - base : 0; - - memory_region_init_alias(alias, OBJECT(bridge), name, space, base, size); - memory_region_add_subregion_overlap(parent_space, base, alias, 1); -} - -static void pci_bridge_init_vga_aliases(PCIBridge *br, PCIBus *parent, - MemoryRegion *alias_vga) -{ - PCIDevice *pd = PCI_DEVICE(br); - uint16_t brctl = pci_get_word(pd->config + PCI_BRIDGE_CONTROL); - - memory_region_init_alias(&alias_vga[QEMU_PCI_VGA_IO_LO], OBJECT(br), - "pci_bridge_vga_io_lo", &br->address_space_io, - QEMU_PCI_VGA_IO_LO_BASE, QEMU_PCI_VGA_IO_LO_SIZE); - memory_region_init_alias(&alias_vga[QEMU_PCI_VGA_IO_HI], OBJECT(br), - "pci_bridge_vga_io_hi", &br->address_space_io, - QEMU_PCI_VGA_IO_HI_BASE, QEMU_PCI_VGA_IO_HI_SIZE); - memory_region_init_alias(&alias_vga[QEMU_PCI_VGA_MEM], OBJECT(br), - "pci_bridge_vga_mem", &br->address_space_mem, - QEMU_PCI_VGA_MEM_BASE, QEMU_PCI_VGA_MEM_SIZE); - - if (brctl & PCI_BRIDGE_CTL_VGA) { - pci_register_vga(pd, &alias_vga[QEMU_PCI_VGA_MEM], - &alias_vga[QEMU_PCI_VGA_IO_LO], - &alias_vga[QEMU_PCI_VGA_IO_HI]); - } -} - -static PCIBridgeWindows *pci_bridge_region_init(PCIBridge *br) -{ - PCIDevice *pd = PCI_DEVICE(br); - PCIBus *parent = pd->bus; - PCIBridgeWindows *w = g_new(PCIBridgeWindows, 1); - uint16_t cmd = pci_get_word(pd->config + PCI_COMMAND); - - pci_bridge_init_alias(br, &w->alias_pref_mem, - PCI_BASE_ADDRESS_MEM_PREFETCH, - "pci_bridge_pref_mem", - &br->address_space_mem, - parent->address_space_mem, - cmd & PCI_COMMAND_MEMORY); - pci_bridge_init_alias(br, &w->alias_mem, - PCI_BASE_ADDRESS_SPACE_MEMORY, - "pci_bridge_mem", - &br->address_space_mem, - parent->address_space_mem, - cmd & PCI_COMMAND_MEMORY); - pci_bridge_init_alias(br, &w->alias_io, - PCI_BASE_ADDRESS_SPACE_IO, - "pci_bridge_io", - &br->address_space_io, - parent->address_space_io, - cmd & PCI_COMMAND_IO); - - pci_bridge_init_vga_aliases(br, parent, w->alias_vga); - - return w; -} - -static void pci_bridge_region_del(PCIBridge *br, PCIBridgeWindows *w) -{ - PCIDevice *pd = PCI_DEVICE(br); - PCIBus *parent = pd->bus; - - memory_region_del_subregion(parent->address_space_io, &w->alias_io); - memory_region_del_subregion(parent->address_space_mem, &w->alias_mem); - memory_region_del_subregion(parent->address_space_mem, &w->alias_pref_mem); - pci_unregister_vga(pd); -} - -static void pci_bridge_region_cleanup(PCIBridge *br, PCIBridgeWindows *w) -{ - object_unparent(OBJECT(&w->alias_io)); - object_unparent(OBJECT(&w->alias_mem)); - object_unparent(OBJECT(&w->alias_pref_mem)); - object_unparent(OBJECT(&w->alias_vga[QEMU_PCI_VGA_IO_LO])); - object_unparent(OBJECT(&w->alias_vga[QEMU_PCI_VGA_IO_HI])); - object_unparent(OBJECT(&w->alias_vga[QEMU_PCI_VGA_MEM])); - g_free(w); -} - -void pci_bridge_update_mappings(PCIBridge *br) -{ - PCIBridgeWindows *w = br->windows; - - /* Make updates atomic to: handle the case of one VCPU updating the bridge - * while another accesses an unaffected region. */ - memory_region_transaction_begin(); - pci_bridge_region_del(br, br->windows); - br->windows = pci_bridge_region_init(br); - memory_region_transaction_commit(); - pci_bridge_region_cleanup(br, w); -} - -/* default write_config function for PCI-to-PCI bridge */ -void pci_bridge_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len) -{ - PCIBridge *s = PCI_BRIDGE(d); - uint16_t oldctl = pci_get_word(d->config + PCI_BRIDGE_CONTROL); - uint16_t newctl; - - pci_default_write_config(d, address, val, len); - - if (ranges_overlap(address, len, PCI_COMMAND, 2) || - - /* io base/limit */ - ranges_overlap(address, len, PCI_IO_BASE, 2) || - - /* memory base/limit, prefetchable base/limit and - io base/limit upper 16 */ - ranges_overlap(address, len, PCI_MEMORY_BASE, 20) || - - /* vga enable */ - ranges_overlap(address, len, PCI_BRIDGE_CONTROL, 2)) { - pci_bridge_update_mappings(s); - } - - newctl = pci_get_word(d->config + PCI_BRIDGE_CONTROL); - if (~oldctl & newctl & PCI_BRIDGE_CTL_BUS_RESET) { - /* Trigger hot reset on 0->1 transition. */ - qbus_reset_all(&s->sec_bus.qbus); - } -} - -void pci_bridge_disable_base_limit(PCIDevice *dev) -{ - uint8_t *conf = dev->config; - - pci_byte_test_and_set_mask(conf + PCI_IO_BASE, - PCI_IO_RANGE_MASK & 0xff); - pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT, - PCI_IO_RANGE_MASK & 0xff); - pci_word_test_and_set_mask(conf + PCI_MEMORY_BASE, - PCI_MEMORY_RANGE_MASK & 0xffff); - pci_word_test_and_clear_mask(conf + PCI_MEMORY_LIMIT, - PCI_MEMORY_RANGE_MASK & 0xffff); - pci_word_test_and_set_mask(conf + PCI_PREF_MEMORY_BASE, - PCI_PREF_RANGE_MASK & 0xffff); - pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_LIMIT, - PCI_PREF_RANGE_MASK & 0xffff); - pci_set_long(conf + PCI_PREF_BASE_UPPER32, 0); - pci_set_long(conf + PCI_PREF_LIMIT_UPPER32, 0); -} - -/* reset bridge specific configuration registers */ -void pci_bridge_reset(DeviceState *qdev) -{ - PCIDevice *dev = PCI_DEVICE(qdev); - uint8_t *conf = dev->config; - - conf[PCI_PRIMARY_BUS] = 0; - conf[PCI_SECONDARY_BUS] = 0; - conf[PCI_SUBORDINATE_BUS] = 0; - conf[PCI_SEC_LATENCY_TIMER] = 0; - - /* - * the default values for base/limit registers aren't specified - * in the PCI-to-PCI-bridge spec. So we don't thouch them here. - * Each implementation can override it. - * typical implementation does - * zero base/limit registers or - * disable forwarding: pci_bridge_disable_base_limit() - * If disable forwarding is wanted, call pci_bridge_disable_base_limit() - * after this function. - */ - pci_byte_test_and_clear_mask(conf + PCI_IO_BASE, - PCI_IO_RANGE_MASK & 0xff); - pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT, - PCI_IO_RANGE_MASK & 0xff); - pci_word_test_and_clear_mask(conf + PCI_MEMORY_BASE, - PCI_MEMORY_RANGE_MASK & 0xffff); - pci_word_test_and_clear_mask(conf + PCI_MEMORY_LIMIT, - PCI_MEMORY_RANGE_MASK & 0xffff); - pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_BASE, - PCI_PREF_RANGE_MASK & 0xffff); - pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_LIMIT, - PCI_PREF_RANGE_MASK & 0xffff); - pci_set_long(conf + PCI_PREF_BASE_UPPER32, 0); - pci_set_long(conf + PCI_PREF_LIMIT_UPPER32, 0); - - pci_set_word(conf + PCI_BRIDGE_CONTROL, 0); -} - -/* default qdev initialization function for PCI-to-PCI bridge */ -void pci_bridge_initfn(PCIDevice *dev, const char *typename) -{ - PCIBus *parent = dev->bus; - PCIBridge *br = PCI_BRIDGE(dev); - PCIBus *sec_bus = &br->sec_bus; - - pci_word_test_and_set_mask(dev->config + PCI_STATUS, - PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); - - /* - * TODO: We implement VGA Enable in the Bridge Control Register - * therefore per the PCI to PCI bridge spec we must also implement - * VGA Palette Snooping. When done, set this bit writable: - * - * pci_word_test_and_set_mask(dev->wmask + PCI_COMMAND, - * PCI_COMMAND_VGA_PALETTE); - */ - - pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_PCI); - dev->config[PCI_HEADER_TYPE] = - (dev->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) | - PCI_HEADER_TYPE_BRIDGE; - pci_set_word(dev->config + PCI_SEC_STATUS, - PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); - - /* - * If we don't specify the name, the bus will be addressed as .0, where - * id is the device id. - * Since PCI Bridge devices have a single bus each, we don't need the index: - * let users address the bus using the device name. - */ - if (!br->bus_name && dev->qdev.id && *dev->qdev.id) { - br->bus_name = dev->qdev.id; - } - - qbus_create_inplace(sec_bus, sizeof(br->sec_bus), typename, DEVICE(dev), - br->bus_name); - sec_bus->parent_dev = dev; - sec_bus->map_irq = br->map_irq ? br->map_irq : pci_swizzle_map_irq_fn; - sec_bus->address_space_mem = &br->address_space_mem; - memory_region_init(&br->address_space_mem, OBJECT(br), "pci_bridge_pci", UINT64_MAX); - sec_bus->address_space_io = &br->address_space_io; - memory_region_init(&br->address_space_io, OBJECT(br), "pci_bridge_io", 65536); - br->windows = pci_bridge_region_init(br); - QLIST_INIT(&sec_bus->child); - QLIST_INSERT_HEAD(&parent->child, sec_bus, sibling); -} - -/* default qdev clean up function for PCI-to-PCI bridge */ -void pci_bridge_exitfn(PCIDevice *pci_dev) -{ - PCIBridge *s = PCI_BRIDGE(pci_dev); - assert(QLIST_EMPTY(&s->sec_bus.child)); - QLIST_REMOVE(&s->sec_bus, sibling); - pci_bridge_region_del(s, s->windows); - pci_bridge_region_cleanup(s, s->windows); - /* object_unparent() is called automatically during device deletion */ -} - -/* - * before qdev initialization(qdev_init()), this function sets bus_name and - * map_irq callback which are necessry for pci_bridge_initfn() to - * initialize bus. - */ -void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, - pci_map_irq_fn map_irq) -{ - br->map_irq = map_irq; - br->bus_name = bus_name; -} - -static const TypeInfo pci_bridge_type_info = { - .name = TYPE_PCI_BRIDGE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIBridge), - .abstract = true, -}; - -static void pci_bridge_register_types(void) -{ - type_register_static(&pci_bridge_type_info); -} - -type_init(pci_bridge_register_types) diff --git a/qemu/hw/pci/pci_host.c b/qemu/hw/pci/pci_host.c deleted file mode 100644 index 5eaa935cb..000000000 --- a/qemu/hw/pci/pci_host.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * pci_host.c - * - * Copyright (c) 2009 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "hw/pci/pci_bus.h" -#include "trace.h" - -/* debug PCI */ -//#define DEBUG_PCI - -#ifdef DEBUG_PCI -#define PCI_DPRINTF(fmt, ...) \ -do { printf("pci_host_data: " fmt , ## __VA_ARGS__); } while (0) -#else -#define PCI_DPRINTF(fmt, ...) -#endif - -/* - * PCI address - * bit 16 - 24: bus number - * bit 8 - 15: devfun number - * bit 0 - 7: offset in configuration space of a given pci device - */ - -/* the helper function to get a PCIDevice* for a given pci address */ -static inline PCIDevice *pci_dev_find_by_addr(PCIBus *bus, uint32_t addr) -{ - uint8_t bus_num = addr >> 16; - uint8_t devfn = addr >> 8; - - return pci_find_device(bus, bus_num, devfn); -} - -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)); -} - -uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, - uint32_t limit, uint32_t len) -{ - 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); - - return ret; -} - -void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len) -{ - PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr); - uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1); - - if (!pci_dev) { - return; - } - - PCI_DPRINTF("%s: %s: addr=%02" PRIx32 " val=%08" PRIx32 " len=%d\n", - __func__, pci_dev->name, config_addr, val, len); - pci_host_config_write_common(pci_dev, config_addr, PCI_CONFIG_SPACE_SIZE, - val, len); -} - -uint32_t pci_data_read(PCIBus *s, uint32_t addr, int len) -{ - PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr); - uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1); - uint32_t val; - - if (!pci_dev) { - return ~0x0; - } - - val = pci_host_config_read_common(pci_dev, config_addr, - PCI_CONFIG_SPACE_SIZE, len); - PCI_DPRINTF("%s: %s: addr=%02"PRIx32" val=%08"PRIx32" len=%d\n", - __func__, pci_dev->name, config_addr, val, len); - - return val; -} - -static void pci_host_config_write(void *opaque, hwaddr addr, - uint64_t val, unsigned len) -{ - PCIHostState *s = opaque; - - PCI_DPRINTF("%s addr " TARGET_FMT_plx " len %d val %"PRIx64"\n", - __func__, addr, len, val); - if (addr != 0 || len != 4) { - return; - } - s->config_reg = val; -} - -static uint64_t pci_host_config_read(void *opaque, hwaddr addr, - unsigned len) -{ - PCIHostState *s = opaque; - uint32_t val = s->config_reg; - - PCI_DPRINTF("%s addr " TARGET_FMT_plx " len %d val %"PRIx32"\n", - __func__, addr, len, val); - return val; -} - -static void pci_host_data_write(void *opaque, hwaddr addr, - uint64_t val, unsigned len) -{ - PCIHostState *s = opaque; - PCI_DPRINTF("write addr " TARGET_FMT_plx " len %d val %x\n", - addr, len, (unsigned)val); - if (s->config_reg & (1u << 31)) - pci_data_write(s->bus, s->config_reg | (addr & 3), val, len); -} - -static uint64_t pci_host_data_read(void *opaque, - hwaddr addr, unsigned len) -{ - PCIHostState *s = opaque; - uint32_t val; - if (!(s->config_reg & (1U << 31))) { - return 0xffffffff; - } - val = pci_data_read(s->bus, s->config_reg | (addr & 3), len); - PCI_DPRINTF("read addr " TARGET_FMT_plx " len %d val %x\n", - addr, len, val); - return val; -} - -const MemoryRegionOps pci_host_conf_le_ops = { - .read = pci_host_config_read, - .write = pci_host_config_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -const MemoryRegionOps pci_host_conf_be_ops = { - .read = pci_host_config_read, - .write = pci_host_config_write, - .endianness = DEVICE_BIG_ENDIAN, -}; - -const MemoryRegionOps pci_host_data_le_ops = { - .read = pci_host_data_read, - .write = pci_host_data_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -const MemoryRegionOps pci_host_data_be_ops = { - .read = pci_host_data_read, - .write = pci_host_data_write, - .endianness = DEVICE_BIG_ENDIAN, -}; - -static const TypeInfo pci_host_type_info = { - .name = TYPE_PCI_HOST_BRIDGE, - .parent = TYPE_SYS_BUS_DEVICE, - .abstract = true, - .class_size = sizeof(PCIHostBridgeClass), - .instance_size = sizeof(PCIHostState), -}; - -static void pci_host_register_types(void) -{ - type_register_static(&pci_host_type_info); -} - -type_init(pci_host_register_types) diff --git a/qemu/hw/pci/pcie.c b/qemu/hw/pci/pcie.c deleted file mode 100644 index 728386ada..000000000 --- a/qemu/hw/pci/pcie.c +++ /dev/null @@ -1,649 +0,0 @@ -/* - * pcie.c - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "hw/pci/pci_bridge.h" -#include "hw/pci/pcie.h" -#include "hw/pci/msix.h" -#include "hw/pci/msi.h" -#include "hw/pci/pci_bus.h" -#include "hw/pci/pcie_regs.h" -#include "qemu/range.h" - -//#define DEBUG_PCIE -#ifdef DEBUG_PCIE -# define PCIE_DPRINTF(fmt, ...) \ - fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__) -#else -# define PCIE_DPRINTF(fmt, ...) do {} while (0) -#endif -#define PCIE_DEV_PRINTF(dev, fmt, ...) \ - PCIE_DPRINTF("%s:%x "fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__) - - -/*************************************************************************** - * pci express capability helper functions - */ -int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port) -{ - int pos; - uint8_t *exp_cap; - - assert(pci_is_express(dev)); - - pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset, - PCI_EXP_VER2_SIZEOF); - if (pos < 0) { - return pos; - } - dev->exp.exp_cap = pos; - exp_cap = dev->config + pos; - - /* capability register - interrupt message number defaults to 0 */ - pci_set_word(exp_cap + PCI_EXP_FLAGS, - ((type << PCI_EXP_FLAGS_TYPE_SHIFT) & PCI_EXP_FLAGS_TYPE) | - PCI_EXP_FLAGS_VER2); - - /* device capability register - * table 7-12: - * roll based error reporting bit must be set by all - * Functions conforming to the ECN, PCI Express Base - * Specification, Revision 1.1., or subsequent PCI Express Base - * Specification revisions. - */ - pci_set_long(exp_cap + PCI_EXP_DEVCAP, PCI_EXP_DEVCAP_RBER); - - pci_set_long(exp_cap + PCI_EXP_LNKCAP, - (port << PCI_EXP_LNKCAP_PN_SHIFT) | - PCI_EXP_LNKCAP_ASPMS_0S | - PCI_EXP_LNK_MLW_1 | - PCI_EXP_LNK_LS_25); - - pci_set_word(exp_cap + PCI_EXP_LNKSTA, - PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25 |PCI_EXP_LNKSTA_DLLLA); - - pci_set_long(exp_cap + PCI_EXP_DEVCAP2, - PCI_EXP_DEVCAP2_EFF | PCI_EXP_DEVCAP2_EETLPP); - - pci_set_word(dev->wmask + pos + PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_EETLPPB); - return pos; -} - -int pcie_endpoint_cap_init(PCIDevice *dev, uint8_t offset) -{ - uint8_t type = PCI_EXP_TYPE_ENDPOINT; - - /* - * Windows guests will report Code 10, device cannot start, if - * a regular Endpoint type is exposed on a root complex. These - * should instead be Root Complex Integrated Endpoints. - */ - if (pci_bus_is_express(dev->bus) && pci_bus_is_root(dev->bus)) { - type = PCI_EXP_TYPE_RC_END; - } - - return pcie_cap_init(dev, offset, type, 0); -} - -void pcie_cap_exit(PCIDevice *dev) -{ - pci_del_capability(dev, PCI_CAP_ID_EXP, PCI_EXP_VER2_SIZEOF); -} - -uint8_t pcie_cap_get_type(const PCIDevice *dev) -{ - uint32_t pos = dev->exp.exp_cap; - assert(pos > 0); - return (pci_get_word(dev->config + pos + PCI_EXP_FLAGS) & - PCI_EXP_FLAGS_TYPE) >> PCI_EXP_FLAGS_TYPE_SHIFT; -} - -/* MSI/MSI-X */ -/* pci express interrupt message number */ -/* 7.8.2 PCI Express Capabilities Register: Interrupt Message Number */ -void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector) -{ - uint8_t *exp_cap = dev->config + dev->exp.exp_cap; - assert(vector < 32); - pci_word_test_and_clear_mask(exp_cap + PCI_EXP_FLAGS, PCI_EXP_FLAGS_IRQ); - pci_word_test_and_set_mask(exp_cap + PCI_EXP_FLAGS, - vector << PCI_EXP_FLAGS_IRQ_SHIFT); -} - -uint8_t pcie_cap_flags_get_vector(PCIDevice *dev) -{ - return (pci_get_word(dev->config + dev->exp.exp_cap + PCI_EXP_FLAGS) & - PCI_EXP_FLAGS_IRQ) >> PCI_EXP_FLAGS_IRQ_SHIFT; -} - -void pcie_cap_deverr_init(PCIDevice *dev) -{ - uint32_t pos = dev->exp.exp_cap; - pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_DEVCAP, - PCI_EXP_DEVCAP_RBER); - pci_long_test_and_set_mask(dev->wmask + pos + PCI_EXP_DEVCTL, - PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | - PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE); - pci_long_test_and_set_mask(dev->w1cmask + pos + PCI_EXP_DEVSTA, - PCI_EXP_DEVSTA_CED | PCI_EXP_DEVSTA_NFED | - PCI_EXP_DEVSTA_FED | PCI_EXP_DEVSTA_URD); -} - -void pcie_cap_deverr_reset(PCIDevice *dev) -{ - uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL; - pci_long_test_and_clear_mask(devctl, - PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | - PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE); -} - -static void hotplug_event_update_event_status(PCIDevice *dev) -{ - uint32_t pos = dev->exp.exp_cap; - uint8_t *exp_cap = dev->config + pos; - uint16_t sltctl = pci_get_word(exp_cap + PCI_EXP_SLTCTL); - uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); - - dev->exp.hpev_notified = (sltctl & PCI_EXP_SLTCTL_HPIE) && - (sltsta & sltctl & PCI_EXP_HP_EV_SUPPORTED); -} - -static void hotplug_event_notify(PCIDevice *dev) -{ - bool prev = dev->exp.hpev_notified; - - hotplug_event_update_event_status(dev); - - if (prev == dev->exp.hpev_notified) { - return; - } - - /* Note: the logic above does not take into account whether interrupts - * are masked. The result is that interrupt will be sent when it is - * subsequently unmasked. This appears to be legal: Section 6.7.3.4: - * The Port may optionally send an MSI when there are hot-plug events that - * occur while interrupt generation is disabled, and interrupt generation is - * subsequently enabled. */ - if (msix_enabled(dev)) { - msix_notify(dev, pcie_cap_flags_get_vector(dev)); - } else if (msi_enabled(dev)) { - msi_notify(dev, pcie_cap_flags_get_vector(dev)); - } else { - pci_set_irq(dev, dev->exp.hpev_notified); - } -} - -static void hotplug_event_clear(PCIDevice *dev) -{ - hotplug_event_update_event_status(dev); - if (!msix_enabled(dev) && !msi_enabled(dev) && !dev->exp.hpev_notified) { - pci_irq_deassert(dev); - } -} - -/* - * A PCI Express Hot-Plug Event has occurred, so update slot status register - * and notify OS of the event if necessary. - * - * 6.7.3 PCI Express Hot-Plug Events - * 6.7.3.4 Software Notification of Hot-Plug Events - */ -static void pcie_cap_slot_event(PCIDevice *dev, PCIExpressHotPlugEvent event) -{ - /* Minor optimization: if nothing changed - no event is needed. */ - if (pci_word_test_and_set_mask(dev->config + dev->exp.exp_cap + - PCI_EXP_SLTSTA, event)) { - return; - } - hotplug_event_notify(dev); -} - -static void pcie_cap_slot_hotplug_common(PCIDevice *hotplug_dev, - DeviceState *dev, - uint8_t **exp_cap, Error **errp) -{ - *exp_cap = hotplug_dev->config + hotplug_dev->exp.exp_cap; - uint16_t sltsta = pci_get_word(*exp_cap + PCI_EXP_SLTSTA); - - PCIE_DEV_PRINTF(PCI_DEVICE(dev), "hotplug state: 0x%x\n", sltsta); - if (sltsta & PCI_EXP_SLTSTA_EIS) { - /* the slot is electromechanically locked. - * This error is propagated up to qdev and then to HMP/QMP. - */ - error_setg_errno(errp, EBUSY, "slot is electromechanically locked"); - } -} - -void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp) -{ - uint8_t *exp_cap; - PCIDevice *pci_dev = PCI_DEVICE(dev); - - pcie_cap_slot_hotplug_common(PCI_DEVICE(hotplug_dev), dev, &exp_cap, errp); - - /* Don't send event when device is enabled during qemu machine creation: - * it is present on boot, no hotplug event is necessary. We do send an - * event when the device is disabled later. */ - if (!dev->hotplugged) { - pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDS); - return; - } - - /* 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. - */ - 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); - } -} - -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)); -} - -/* pci express slot for pci express root/downstream port - PCI express capability slot registers */ -void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot) -{ - uint32_t pos = dev->exp.exp_cap; - - pci_word_test_and_set_mask(dev->config + pos + PCI_EXP_FLAGS, - PCI_EXP_FLAGS_SLOT); - - pci_long_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCAP, - ~PCI_EXP_SLTCAP_PSN); - pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP, - (slot << PCI_EXP_SLTCAP_PSN_SHIFT) | - PCI_EXP_SLTCAP_EIP | - PCI_EXP_SLTCAP_HPS | - PCI_EXP_SLTCAP_HPC | - PCI_EXP_SLTCAP_PIP | - PCI_EXP_SLTCAP_AIP | - PCI_EXP_SLTCAP_ABP); - - if (dev->cap_present & QEMU_PCIE_SLTCAP_PCP) { - pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP, - PCI_EXP_SLTCAP_PCP); - pci_word_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_PCC); - pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_PCC); - } - - pci_word_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_PIC | - PCI_EXP_SLTCTL_AIC); - pci_word_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_PIC_OFF | - PCI_EXP_SLTCTL_AIC_OFF); - pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_PIC | - PCI_EXP_SLTCTL_AIC | - PCI_EXP_SLTCTL_HPIE | - PCI_EXP_SLTCTL_CCIE | - PCI_EXP_SLTCTL_PDCE | - PCI_EXP_SLTCTL_ABPE); - /* Although reading PCI_EXP_SLTCTL_EIC returns always 0, - * make the bit writable here in order to detect 1b is written. - * pcie_cap_slot_write_config() test-and-clear the bit, so - * this bit always returns 0 to the guest. - */ - pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_EIC); - - pci_word_test_and_set_mask(dev->w1cmask + pos + PCI_EXP_SLTSTA, - PCI_EXP_HP_EV_SUPPORTED); - - dev->exp.hpev_notified = false; - - qbus_set_hotplug_handler(BUS(pci_bridge_get_sec_bus(PCI_BRIDGE(dev))), - DEVICE(dev), NULL); -} - -void pcie_cap_slot_reset(PCIDevice *dev) -{ - uint8_t *exp_cap = dev->config + dev->exp.exp_cap; - uint8_t port_type = pcie_cap_get_type(dev); - - assert(port_type == PCI_EXP_TYPE_DOWNSTREAM || - port_type == PCI_EXP_TYPE_ROOT_PORT); - - PCIE_DEV_PRINTF(dev, "reset\n"); - - pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_EIC | - PCI_EXP_SLTCTL_PIC | - PCI_EXP_SLTCTL_AIC | - PCI_EXP_SLTCTL_HPIE | - PCI_EXP_SLTCTL_CCIE | - PCI_EXP_SLTCTL_PDCE | - PCI_EXP_SLTCTL_ABPE); - pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_AIC_OFF); - - if (dev->cap_present & QEMU_PCIE_SLTCAP_PCP) { - /* Downstream ports enforce device number 0. */ - bool populated = pci_bridge_get_sec_bus(PCI_BRIDGE(dev))->devices[0]; - uint16_t pic; - - if (populated) { - pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_PCC); - } else { - pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_PCC); - } - - pic = populated ? PCI_EXP_SLTCTL_PIC_ON : PCI_EXP_SLTCTL_PIC_OFF; - pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL, pic); - } - - pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_EIS |/* on reset, - the lock is released */ - PCI_EXP_SLTSTA_CC | - PCI_EXP_SLTSTA_PDC | - PCI_EXP_SLTSTA_ABP); - - hotplug_event_update_event_status(dev); -} - -void pcie_cap_slot_write_config(PCIDevice *dev, - uint32_t addr, uint32_t val, int len) -{ - uint32_t pos = dev->exp.exp_cap; - uint8_t *exp_cap = dev->config + pos; - uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); - - if (ranges_overlap(addr, len, pos + PCI_EXP_SLTSTA, 2)) { - hotplug_event_clear(dev); - } - - if (!ranges_overlap(addr, len, pos + PCI_EXP_SLTCTL, 2)) { - return; - } - - if (pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL, - PCI_EXP_SLTCTL_EIC)) { - sltsta ^= PCI_EXP_SLTSTA_EIS; /* toggle PCI_EXP_SLTSTA_EIS bit */ - pci_set_word(exp_cap + PCI_EXP_SLTSTA, sltsta); - PCIE_DEV_PRINTF(dev, "PCI_EXP_SLTCTL_EIC: " - "sltsta -> 0x%02"PRIx16"\n", - sltsta); - } - - /* - * If the slot is polulated, power indicator is off and power - * controller is off, it is safe to detach the devices. - */ - 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); - - 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); - } - - hotplug_event_notify(dev); - - /* - * 6.7.3.2 Command Completed Events - * - * Software issues a command to a hot-plug capable Downstream Port by - * issuing a write transaction that targets any portion of the Port’s Slot - * Control register. A single write to the Slot Control register is - * considered to be a single command, even if the write affects more than - * one field in the Slot Control register. In response to this transaction, - * the Port must carry out the requested actions and then set the - * associated status field for the command completed event. */ - - /* Real hardware might take a while to complete requested command because - * physical movement would be involved like locking the electromechanical - * lock. However in our case, command is completed instantaneously above, - * so send a command completion event right now. - */ - pcie_cap_slot_event(dev, PCI_EXP_HP_EV_CCI); -} - -int pcie_cap_slot_post_load(void *opaque, int version_id) -{ - PCIDevice *dev = opaque; - hotplug_event_update_event_status(dev); - return 0; -} - -void pcie_cap_slot_push_attention_button(PCIDevice *dev) -{ - pcie_cap_slot_event(dev, PCI_EXP_HP_EV_ABP); -} - -/* root control/capabilities/status. PME isn't emulated for now */ -void pcie_cap_root_init(PCIDevice *dev) -{ - pci_set_word(dev->wmask + dev->exp.exp_cap + PCI_EXP_RTCTL, - PCI_EXP_RTCTL_SECEE | PCI_EXP_RTCTL_SENFEE | - PCI_EXP_RTCTL_SEFEE); -} - -void pcie_cap_root_reset(PCIDevice *dev) -{ - pci_set_word(dev->config + dev->exp.exp_cap + PCI_EXP_RTCTL, 0); -} - -/* function level reset(FLR) */ -void pcie_cap_flr_init(PCIDevice *dev) -{ - pci_long_test_and_set_mask(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCAP, - PCI_EXP_DEVCAP_FLR); - - /* Although reading BCR_FLR returns always 0, - * the bit is made writable here in order to detect the 1b is written - * pcie_cap_flr_write_config() test-and-clear the bit, so - * this bit always returns 0 to the guest. - */ - pci_word_test_and_set_mask(dev->wmask + dev->exp.exp_cap + PCI_EXP_DEVCTL, - PCI_EXP_DEVCTL_BCR_FLR); -} - -void pcie_cap_flr_write_config(PCIDevice *dev, - uint32_t addr, uint32_t val, int len) -{ - uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL; - if (pci_get_word(devctl) & PCI_EXP_DEVCTL_BCR_FLR) { - /* Clear PCI_EXP_DEVCTL_BCR_FLR after invoking the reset handler - so the handler can detect FLR by looking at this bit. */ - pci_device_reset(dev); - pci_word_test_and_clear_mask(devctl, PCI_EXP_DEVCTL_BCR_FLR); - } -} - -/* Alternative Routing-ID Interpretation (ARI) - * forwarding support for root and downstream ports - */ -void pcie_cap_arifwd_init(PCIDevice *dev) -{ - uint32_t pos = dev->exp.exp_cap; - pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_DEVCAP2, - PCI_EXP_DEVCAP2_ARI); - pci_long_test_and_set_mask(dev->wmask + pos + PCI_EXP_DEVCTL2, - PCI_EXP_DEVCTL2_ARI); -} - -void pcie_cap_arifwd_reset(PCIDevice *dev) -{ - uint8_t *devctl2 = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2; - pci_long_test_and_clear_mask(devctl2, PCI_EXP_DEVCTL2_ARI); -} - -bool pcie_cap_is_arifwd_enabled(const PCIDevice *dev) -{ - if (!pci_is_express(dev)) { - return false; - } - if (!dev->exp.exp_cap) { - return false; - } - - return pci_get_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2) & - PCI_EXP_DEVCTL2_ARI; -} - -/************************************************************************** - * pci express extended capability list management functions - * uint16_t ext_cap_id (16 bit) - * uint8_t cap_ver (4 bit) - * uint16_t cap_offset (12 bit) - * uint16_t ext_cap_size - */ - -static uint16_t pcie_find_capability_list(PCIDevice *dev, uint16_t cap_id, - uint16_t *prev_p) -{ - uint16_t prev = 0; - uint16_t next; - uint32_t header = pci_get_long(dev->config + PCI_CONFIG_SPACE_SIZE); - - if (!header) { - /* no extended capability */ - next = 0; - goto out; - } - for (next = PCI_CONFIG_SPACE_SIZE; next; - prev = next, next = PCI_EXT_CAP_NEXT(header)) { - - assert(next >= PCI_CONFIG_SPACE_SIZE); - assert(next <= PCIE_CONFIG_SPACE_SIZE - 8); - - header = pci_get_long(dev->config + next); - if (PCI_EXT_CAP_ID(header) == cap_id) { - break; - } - } - -out: - if (prev_p) { - *prev_p = prev; - } - return next; -} - -uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id) -{ - return pcie_find_capability_list(dev, cap_id, NULL); -} - -static void pcie_ext_cap_set_next(PCIDevice *dev, uint16_t pos, uint16_t next) -{ - uint32_t header = pci_get_long(dev->config + pos); - assert(!(next & (PCI_EXT_CAP_ALIGN - 1))); - header = (header & ~PCI_EXT_CAP_NEXT_MASK) | - ((next << PCI_EXT_CAP_NEXT_SHIFT) & PCI_EXT_CAP_NEXT_MASK); - pci_set_long(dev->config + pos, header); -} - -/* - * caller must supply valid (offset, size) * such that the range shouldn't - * overlap with other capability or other registers. - * This function doesn't check it. - */ -void pcie_add_capability(PCIDevice *dev, - uint16_t cap_id, uint8_t cap_ver, - uint16_t offset, uint16_t size) -{ - uint32_t header; - uint16_t next; - - assert(offset >= PCI_CONFIG_SPACE_SIZE); - assert(offset < offset + size); - assert(offset + size <= PCIE_CONFIG_SPACE_SIZE); - assert(size >= 8); - assert(pci_is_express(dev)); - - if (offset == PCI_CONFIG_SPACE_SIZE) { - header = pci_get_long(dev->config + offset); - next = PCI_EXT_CAP_NEXT(header); - } else { - uint16_t prev; - - /* 0 is reserved cap id. use internally to find the last capability - in the linked list */ - next = pcie_find_capability_list(dev, 0, &prev); - - assert(prev >= PCI_CONFIG_SPACE_SIZE); - assert(next == 0); - pcie_ext_cap_set_next(dev, prev, offset); - } - pci_set_long(dev->config + offset, PCI_EXT_CAP(cap_id, cap_ver, next)); - - /* Make capability read-only by default */ - memset(dev->wmask + offset, 0, size); - memset(dev->w1cmask + offset, 0, size); - /* Check capability by default */ - memset(dev->cmask + offset, 0xFF, size); -} - -/************************************************************************** - * pci express extended capability helper functions - */ - -/* ARI */ -void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn) -{ - pcie_add_capability(dev, PCI_EXT_CAP_ID_ARI, PCI_ARI_VER, - offset, PCI_ARI_SIZEOF); - pci_set_long(dev->config + offset + PCI_ARI_CAP, (nextfn & 0xff) << 8); -} diff --git a/qemu/hw/pci/pcie_aer.c b/qemu/hw/pci/pcie_aer.c deleted file mode 100644 index e2d4e68ba..000000000 --- a/qemu/hw/pci/pcie_aer.c +++ /dev/null @@ -1,1039 +0,0 @@ -/* - * pcie_aer.c - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "sysemu/sysemu.h" -#include "qapi/qmp/types.h" -#include "monitor/monitor.h" -#include "hw/pci/pci_bridge.h" -#include "hw/pci/pcie.h" -#include "hw/pci/msix.h" -#include "hw/pci/msi.h" -#include "hw/pci/pci_bus.h" -#include "hw/pci/pcie_regs.h" - -//#define DEBUG_PCIE -#ifdef DEBUG_PCIE -# define PCIE_DPRINTF(fmt, ...) \ - fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__) -#else -# define PCIE_DPRINTF(fmt, ...) do {} while (0) -#endif -#define PCIE_DEV_PRINTF(dev, fmt, ...) \ - PCIE_DPRINTF("%s:%x "fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__) - -#define PCI_ERR_SRC_COR_OFFS 0 -#define PCI_ERR_SRC_UNCOR_OFFS 2 - -/* From 6.2.7 Error Listing and Rules. Table 6-2, 6-3 and 6-4 */ -static uint32_t pcie_aer_uncor_default_severity(uint32_t status) -{ - switch (status) { - case PCI_ERR_UNC_INTN: - case PCI_ERR_UNC_DLP: - case PCI_ERR_UNC_SDN: - case PCI_ERR_UNC_RX_OVER: - case PCI_ERR_UNC_FCP: - case PCI_ERR_UNC_MALF_TLP: - return PCI_ERR_ROOT_CMD_FATAL_EN; - case PCI_ERR_UNC_POISON_TLP: - case PCI_ERR_UNC_ECRC: - case PCI_ERR_UNC_UNSUP: - case PCI_ERR_UNC_COMP_TIME: - case PCI_ERR_UNC_COMP_ABORT: - case PCI_ERR_UNC_UNX_COMP: - case PCI_ERR_UNC_ACSV: - case PCI_ERR_UNC_MCBTLP: - case PCI_ERR_UNC_ATOP_EBLOCKED: - case PCI_ERR_UNC_TLP_PRF_BLOCKED: - return PCI_ERR_ROOT_CMD_NONFATAL_EN; - default: - abort(); - break; - } - return PCI_ERR_ROOT_CMD_FATAL_EN; -} - -static int aer_log_add_err(PCIEAERLog *aer_log, const PCIEAERErr *err) -{ - if (aer_log->log_num == aer_log->log_max) { - return -1; - } - memcpy(&aer_log->log[aer_log->log_num], err, sizeof *err); - aer_log->log_num++; - return 0; -} - -static void aer_log_del_err(PCIEAERLog *aer_log, PCIEAERErr *err) -{ - assert(aer_log->log_num); - *err = aer_log->log[0]; - aer_log->log_num--; - memmove(&aer_log->log[0], &aer_log->log[1], - aer_log->log_num * sizeof *err); -} - -static void aer_log_clear_all_err(PCIEAERLog *aer_log) -{ - aer_log->log_num = 0; -} - -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, size); - exp = &dev->exp; - exp->aer_cap = offset; - - /* log_max is property */ - if (dev->exp.aer_log.log_max == PCIE_AER_LOG_MAX_UNSET) { - dev->exp.aer_log.log_max = PCIE_AER_LOG_MAX_DEFAULT; - } - /* clip down the value to avoid unreasobale memory usage */ - if (dev->exp.aer_log.log_max > PCIE_AER_LOG_MAX_LIMIT) { - return -EINVAL; - } - dev->exp.aer_log.log = g_malloc0(sizeof dev->exp.aer_log.log[0] * - dev->exp.aer_log.log_max); - - pci_set_long(dev->w1cmask + offset + PCI_ERR_UNCOR_STATUS, - PCI_ERR_UNC_SUPPORTED); - - pci_set_long(dev->config + offset + PCI_ERR_UNCOR_SEVER, - PCI_ERR_UNC_SEVERITY_DEFAULT); - pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_SEVER, - PCI_ERR_UNC_SUPPORTED); - - pci_long_test_and_set_mask(dev->w1cmask + offset + PCI_ERR_COR_STATUS, - PCI_ERR_COR_SUPPORTED); - - pci_set_long(dev->config + offset + PCI_ERR_COR_MASK, - PCI_ERR_COR_MASK_DEFAULT); - pci_set_long(dev->wmask + offset + PCI_ERR_COR_MASK, - PCI_ERR_COR_SUPPORTED); - - /* capabilities and control. multiple header logging is supported */ - if (dev->exp.aer_log.log_max > 0) { - pci_set_long(dev->config + offset + PCI_ERR_CAP, - PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC | - PCI_ERR_CAP_MHRC); - pci_set_long(dev->wmask + offset + PCI_ERR_CAP, - PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE | - PCI_ERR_CAP_MHRE); - } else { - pci_set_long(dev->config + offset + PCI_ERR_CAP, - PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC); - pci_set_long(dev->wmask + offset + PCI_ERR_CAP, - PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE); - } - - switch (pcie_cap_get_type(dev)) { - case PCI_EXP_TYPE_ROOT_PORT: - /* this case will be set by pcie_aer_root_init() */ - /* fallthrough */ - case PCI_EXP_TYPE_DOWNSTREAM: - case PCI_EXP_TYPE_UPSTREAM: - pci_word_test_and_set_mask(dev->wmask + PCI_BRIDGE_CONTROL, - PCI_BRIDGE_CTL_SERR); - pci_long_test_and_set_mask(dev->w1cmask + PCI_STATUS, - PCI_SEC_STATUS_RCV_SYSTEM_ERROR); - break; - default: - /* nothing */ - break; - } - return 0; -} - -void pcie_aer_exit(PCIDevice *dev) -{ - g_free(dev->exp.aer_log.log); -} - -static void pcie_aer_update_uncor_status(PCIDevice *dev) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - PCIEAERLog *aer_log = &dev->exp.aer_log; - - uint16_t i; - for (i = 0; i < aer_log->log_num; i++) { - pci_long_test_and_set_mask(aer_cap + PCI_ERR_UNCOR_STATUS, - dev->exp.aer_log.log[i].status); - } -} - -/* - * return value: - * true: error message needs to be sent up - * false: error message is masked - * - * 6.2.6 Error Message Control - * Figure 6-3 - * all pci express devices part - */ -static bool -pcie_aer_msg_alldev(PCIDevice *dev, const PCIEAERMsg *msg) -{ - if (!(pcie_aer_msg_is_uncor(msg) && - (pci_get_word(dev->config + PCI_COMMAND) & PCI_COMMAND_SERR))) { - return false; - } - - /* Signaled System Error - * - * 7.5.1.1 Command register - * Bit 8 SERR# Enable - * - * When Set, this bit enables reporting of Non-fatal and Fatal - * errors detected by the Function to the Root Complex. Note that - * errors are reported if enabled either through this bit or through - * the PCI Express specific bits in the Device Control register (see - * Section 7.8.4). - */ - pci_word_test_and_set_mask(dev->config + PCI_STATUS, - PCI_STATUS_SIG_SYSTEM_ERROR); - - if (!(msg->severity & - pci_get_word(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL))) { - return false; - } - - /* send up error message */ - return true; -} - -/* - * return value: - * true: error message is sent up - * false: error message is masked - * - * 6.2.6 Error Message Control - * Figure 6-3 - * virtual pci bridge part - */ -static bool pcie_aer_msg_vbridge(PCIDevice *dev, const PCIEAERMsg *msg) -{ - uint16_t bridge_control = pci_get_word(dev->config + PCI_BRIDGE_CONTROL); - - if (pcie_aer_msg_is_uncor(msg)) { - /* Received System Error */ - pci_word_test_and_set_mask(dev->config + PCI_SEC_STATUS, - PCI_SEC_STATUS_RCV_SYSTEM_ERROR); - } - - if (!(bridge_control & PCI_BRIDGE_CTL_SERR)) { - return false; - } - return true; -} - -void pcie_aer_root_set_vector(PCIDevice *dev, unsigned int vector) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - assert(vector < PCI_ERR_ROOT_IRQ_MAX); - pci_long_test_and_clear_mask(aer_cap + PCI_ERR_ROOT_STATUS, - PCI_ERR_ROOT_IRQ); - pci_long_test_and_set_mask(aer_cap + PCI_ERR_ROOT_STATUS, - vector << PCI_ERR_ROOT_IRQ_SHIFT); -} - -static unsigned int pcie_aer_root_get_vector(PCIDevice *dev) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); - return (root_status & PCI_ERR_ROOT_IRQ) >> PCI_ERR_ROOT_IRQ_SHIFT; -} - -/* Given a status register, get corresponding bits in the command register */ -static uint32_t pcie_aer_status_to_cmd(uint32_t status) -{ - uint32_t cmd = 0; - if (status & PCI_ERR_ROOT_COR_RCV) { - cmd |= PCI_ERR_ROOT_CMD_COR_EN; - } - if (status & PCI_ERR_ROOT_NONFATAL_RCV) { - cmd |= PCI_ERR_ROOT_CMD_NONFATAL_EN; - } - if (status & PCI_ERR_ROOT_FATAL_RCV) { - cmd |= PCI_ERR_ROOT_CMD_FATAL_EN; - } - return cmd; -} - -static void pcie_aer_root_notify(PCIDevice *dev) -{ - if (msix_enabled(dev)) { - msix_notify(dev, pcie_aer_root_get_vector(dev)); - } else if (msi_enabled(dev)) { - msi_notify(dev, pcie_aer_root_get_vector(dev)); - } else { - pci_irq_assert(dev); - } -} - -/* - * 6.2.6 Error Message Control - * Figure 6-3 - * root port part - */ -static void pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg) -{ - uint16_t cmd; - uint8_t *aer_cap; - uint32_t root_cmd; - uint32_t root_status, prev_status; - - cmd = pci_get_word(dev->config + PCI_COMMAND); - aer_cap = dev->config + dev->exp.aer_cap; - root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND); - prev_status = root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); - - if (cmd & PCI_COMMAND_SERR) { - /* System Error. - * - * The way to report System Error is platform specific and - * it isn't implemented in qemu right now. - * So just discard the error for now. - * OS which cares of aer would receive errors via - * native aer mechanims, so this wouldn't matter. - */ - } - - /* Errro Message Received: Root Error Status register */ - switch (msg->severity) { - case PCI_ERR_ROOT_CMD_COR_EN: - if (root_status & PCI_ERR_ROOT_COR_RCV) { - root_status |= PCI_ERR_ROOT_MULTI_COR_RCV; - } else { - pci_set_word(aer_cap + PCI_ERR_ROOT_ERR_SRC + PCI_ERR_SRC_COR_OFFS, - msg->source_id); - } - root_status |= PCI_ERR_ROOT_COR_RCV; - break; - case PCI_ERR_ROOT_CMD_NONFATAL_EN: - root_status |= PCI_ERR_ROOT_NONFATAL_RCV; - break; - case PCI_ERR_ROOT_CMD_FATAL_EN: - if (!(root_status & PCI_ERR_ROOT_UNCOR_RCV)) { - root_status |= PCI_ERR_ROOT_FIRST_FATAL; - } - root_status |= PCI_ERR_ROOT_FATAL_RCV; - break; - default: - abort(); - break; - } - if (pcie_aer_msg_is_uncor(msg)) { - if (root_status & PCI_ERR_ROOT_UNCOR_RCV) { - root_status |= PCI_ERR_ROOT_MULTI_UNCOR_RCV; - } else { - pci_set_word(aer_cap + PCI_ERR_ROOT_ERR_SRC + - PCI_ERR_SRC_UNCOR_OFFS, msg->source_id); - } - root_status |= PCI_ERR_ROOT_UNCOR_RCV; - } - pci_set_long(aer_cap + PCI_ERR_ROOT_STATUS, root_status); - - /* 6.2.4.1.2 Interrupt Generation */ - /* All the above did was set some bits in the status register. - * Specifically these that match message severity. - * The below code relies on this fact. */ - if (!(root_cmd & msg->severity) || - (pcie_aer_status_to_cmd(prev_status) & root_cmd)) { - /* Condition is not being set or was already true so nothing to do. */ - return; - } - - pcie_aer_root_notify(dev); -} - -/* - * 6.2.6 Error Message Control Figure 6-3 - * - * Walk up the bus tree from the device, propagate the error message. - */ -void pcie_aer_msg(PCIDevice *dev, const PCIEAERMsg *msg) -{ - uint8_t type; - - while (dev) { - if (!pci_is_express(dev)) { - /* just ignore it */ - /* TODO: Shouldn't we set PCI_STATUS_SIG_SYSTEM_ERROR? - * Consider e.g. a PCI bridge above a PCI Express device. */ - return; - } - - type = pcie_cap_get_type(dev); - if ((type == PCI_EXP_TYPE_ROOT_PORT || - type == PCI_EXP_TYPE_UPSTREAM || - type == PCI_EXP_TYPE_DOWNSTREAM) && - !pcie_aer_msg_vbridge(dev, msg)) { - return; - } - if (!pcie_aer_msg_alldev(dev, msg)) { - return; - } - if (type == PCI_EXP_TYPE_ROOT_PORT) { - pcie_aer_msg_root_port(dev, msg); - /* Root port can notify system itself, - or send the error message to root complex event collector. */ - /* - * if root port is associated with an event collector, - * return the root complex event collector here. - * For now root complex event collector isn't supported. - */ - return; - } - dev = pci_bridge_get_device(dev->bus); - } -} - -static void pcie_aer_update_log(PCIDevice *dev, const PCIEAERErr *err) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - uint8_t first_bit = ctz32(err->status); - uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); - int i; - - assert(err->status); - assert(!(err->status & (err->status - 1))); - - errcap &= ~(PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP); - errcap |= PCI_ERR_CAP_FEP(first_bit); - - if (err->flags & PCIE_AER_ERR_HEADER_VALID) { - for (i = 0; i < ARRAY_SIZE(err->header); ++i) { - /* 7.10.8 Header Log Register */ - uint8_t *header_log = - aer_cap + PCI_ERR_HEADER_LOG + i * sizeof err->header[0]; - stl_be_p(header_log, err->header[i]); - } - } else { - assert(!(err->flags & PCIE_AER_ERR_TLP_PREFIX_PRESENT)); - memset(aer_cap + PCI_ERR_HEADER_LOG, 0, PCI_ERR_HEADER_LOG_SIZE); - } - - if ((err->flags & PCIE_AER_ERR_TLP_PREFIX_PRESENT) && - (pci_get_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCAP2) & - PCI_EXP_DEVCAP2_EETLPP)) { - for (i = 0; i < ARRAY_SIZE(err->prefix); ++i) { - /* 7.10.12 tlp prefix log register */ - uint8_t *prefix_log = - aer_cap + PCI_ERR_TLP_PREFIX_LOG + i * sizeof err->prefix[0]; - stl_be_p(prefix_log, err->prefix[i]); - } - errcap |= PCI_ERR_CAP_TLP; - } else { - memset(aer_cap + PCI_ERR_TLP_PREFIX_LOG, 0, - PCI_ERR_TLP_PREFIX_LOG_SIZE); - } - pci_set_long(aer_cap + PCI_ERR_CAP, errcap); -} - -static void pcie_aer_clear_log(PCIDevice *dev) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - - pci_long_test_and_clear_mask(aer_cap + PCI_ERR_CAP, - PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP); - - memset(aer_cap + PCI_ERR_HEADER_LOG, 0, PCI_ERR_HEADER_LOG_SIZE); - memset(aer_cap + PCI_ERR_TLP_PREFIX_LOG, 0, PCI_ERR_TLP_PREFIX_LOG_SIZE); -} - -static void pcie_aer_clear_error(PCIDevice *dev) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); - PCIEAERLog *aer_log = &dev->exp.aer_log; - PCIEAERErr err; - - if (!(errcap & PCI_ERR_CAP_MHRE) || !aer_log->log_num) { - pcie_aer_clear_log(dev); - return; - } - - /* - * If more errors are queued, set corresponding bits in uncorrectable - * error status. - * We emulate uncorrectable error status register as W1CS. - * So set bit in uncorrectable error status here again for multiple - * error recording support. - * - * 6.2.4.2 Multiple Error Handling(Advanced Error Reporting Capability) - */ - pcie_aer_update_uncor_status(dev); - - aer_log_del_err(aer_log, &err); - pcie_aer_update_log(dev, &err); -} - -static int pcie_aer_record_error(PCIDevice *dev, - const PCIEAERErr *err) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); - int fep = PCI_ERR_CAP_FEP(errcap); - - assert(err->status); - assert(!(err->status & (err->status - 1))); - - if (errcap & PCI_ERR_CAP_MHRE && - (pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS) & (1U << fep))) { - /* Not first error. queue error */ - if (aer_log_add_err(&dev->exp.aer_log, err) < 0) { - /* overflow */ - return -1; - } - return 0; - } - - pcie_aer_update_log(dev, err); - return 0; -} - -typedef struct PCIEAERInject { - PCIDevice *dev; - uint8_t *aer_cap; - const PCIEAERErr *err; - uint16_t devctl; - uint16_t devsta; - uint32_t error_status; - bool unsupported_request; - bool log_overflow; - PCIEAERMsg msg; -} PCIEAERInject; - -static bool pcie_aer_inject_cor_error(PCIEAERInject *inj, - uint32_t uncor_status, - bool is_advisory_nonfatal) -{ - PCIDevice *dev = inj->dev; - - inj->devsta |= PCI_EXP_DEVSTA_CED; - if (inj->unsupported_request) { - inj->devsta |= PCI_EXP_DEVSTA_URD; - } - pci_set_word(dev->config + dev->exp.exp_cap + PCI_EXP_DEVSTA, inj->devsta); - - if (inj->aer_cap) { - uint32_t mask; - pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_COR_STATUS, - inj->error_status); - mask = pci_get_long(inj->aer_cap + PCI_ERR_COR_MASK); - if (mask & inj->error_status) { - return false; - } - if (is_advisory_nonfatal) { - uint32_t uncor_mask = - pci_get_long(inj->aer_cap + PCI_ERR_UNCOR_MASK); - if (!(uncor_mask & uncor_status)) { - inj->log_overflow = !!pcie_aer_record_error(dev, inj->err); - } - pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS, - uncor_status); - } - } - - if (inj->unsupported_request && !(inj->devctl & PCI_EXP_DEVCTL_URRE)) { - return false; - } - if (!(inj->devctl & PCI_EXP_DEVCTL_CERE)) { - return false; - } - - inj->msg.severity = PCI_ERR_ROOT_CMD_COR_EN; - return true; -} - -static bool pcie_aer_inject_uncor_error(PCIEAERInject *inj, bool is_fatal) -{ - PCIDevice *dev = inj->dev; - uint16_t cmd; - - if (is_fatal) { - inj->devsta |= PCI_EXP_DEVSTA_FED; - } else { - inj->devsta |= PCI_EXP_DEVSTA_NFED; - } - if (inj->unsupported_request) { - inj->devsta |= PCI_EXP_DEVSTA_URD; - } - pci_set_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVSTA, inj->devsta); - - if (inj->aer_cap) { - uint32_t mask = pci_get_long(inj->aer_cap + PCI_ERR_UNCOR_MASK); - if (mask & inj->error_status) { - pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS, - inj->error_status); - return false; - } - - inj->log_overflow = !!pcie_aer_record_error(dev, inj->err); - pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS, - inj->error_status); - } - - cmd = pci_get_word(dev->config + PCI_COMMAND); - if (inj->unsupported_request && - !(inj->devctl & PCI_EXP_DEVCTL_URRE) && !(cmd & PCI_COMMAND_SERR)) { - return false; - } - if (is_fatal) { - if (!((cmd & PCI_COMMAND_SERR) || - (inj->devctl & PCI_EXP_DEVCTL_FERE))) { - return false; - } - inj->msg.severity = PCI_ERR_ROOT_CMD_FATAL_EN; - } else { - if (!((cmd & PCI_COMMAND_SERR) || - (inj->devctl & PCI_EXP_DEVCTL_NFERE))) { - return false; - } - inj->msg.severity = PCI_ERR_ROOT_CMD_NONFATAL_EN; - } - return true; -} - -/* - * non-Function specific error must be recorded in all functions. - * It is the responsibility of the caller of this function. - * It is also caller's responsibility to determine which function should - * report the error. - * - * 6.2.4 Error Logging - * 6.2.5 Sequence of Device Error Signaling and Logging Operations - * Figure 6-2: Flowchart Showing Sequence of Device Error Signaling and Logging - * Operations - */ -int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err) -{ - uint8_t *aer_cap = NULL; - uint16_t devctl = 0; - uint16_t devsta = 0; - uint32_t error_status = err->status; - PCIEAERInject inj; - - if (!pci_is_express(dev)) { - return -ENOSYS; - } - - if (err->flags & PCIE_AER_ERR_IS_CORRECTABLE) { - error_status &= PCI_ERR_COR_SUPPORTED; - } else { - error_status &= PCI_ERR_UNC_SUPPORTED; - } - - /* invalid status bit. one and only one bit must be set */ - if (!error_status || (error_status & (error_status - 1))) { - return -EINVAL; - } - - if (dev->exp.aer_cap) { - uint8_t *exp_cap = dev->config + dev->exp.exp_cap; - aer_cap = dev->config + dev->exp.aer_cap; - devctl = pci_get_long(exp_cap + PCI_EXP_DEVCTL); - devsta = pci_get_long(exp_cap + PCI_EXP_DEVSTA); - } - - inj.dev = dev; - inj.aer_cap = aer_cap; - inj.err = err; - inj.devctl = devctl; - inj.devsta = devsta; - inj.error_status = error_status; - inj.unsupported_request = !(err->flags & PCIE_AER_ERR_IS_CORRECTABLE) && - err->status == PCI_ERR_UNC_UNSUP; - inj.log_overflow = false; - - if (err->flags & PCIE_AER_ERR_IS_CORRECTABLE) { - if (!pcie_aer_inject_cor_error(&inj, 0, false)) { - return 0; - } - } else { - bool is_fatal = - pcie_aer_uncor_default_severity(error_status) == - PCI_ERR_ROOT_CMD_FATAL_EN; - if (aer_cap) { - is_fatal = - error_status & pci_get_long(aer_cap + PCI_ERR_UNCOR_SEVER); - } - if (!is_fatal && (err->flags & PCIE_AER_ERR_MAYBE_ADVISORY)) { - inj.error_status = PCI_ERR_COR_ADV_NONFATAL; - if (!pcie_aer_inject_cor_error(&inj, error_status, true)) { - return 0; - } - } else { - if (!pcie_aer_inject_uncor_error(&inj, is_fatal)) { - return 0; - } - } - } - - /* send up error message */ - inj.msg.source_id = err->source_id; - pcie_aer_msg(dev, &inj.msg); - - if (inj.log_overflow) { - PCIEAERErr header_log_overflow = { - .status = PCI_ERR_COR_HL_OVERFLOW, - .flags = PCIE_AER_ERR_IS_CORRECTABLE, - }; - int ret = pcie_aer_inject_error(dev, &header_log_overflow); - assert(!ret); - } - return 0; -} - -void pcie_aer_write_config(PCIDevice *dev, - uint32_t addr, uint32_t val, int len) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); - uint32_t first_error = 1U << PCI_ERR_CAP_FEP(errcap); - uint32_t uncorsta = pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS); - - /* uncorrectable error */ - if (!(uncorsta & first_error)) { - /* the bit that corresponds to the first error is cleared */ - pcie_aer_clear_error(dev); - } else if (errcap & PCI_ERR_CAP_MHRE) { - /* When PCI_ERR_CAP_MHRE is enabled and the first error isn't cleared - * nothing should happen. So we have to revert the modification to - * the register. - */ - pcie_aer_update_uncor_status(dev); - } else { - /* capability & control - * PCI_ERR_CAP_MHRE might be cleared, so clear of header log. - */ - aer_log_clear_all_err(&dev->exp.aer_log); - } -} - -void pcie_aer_root_init(PCIDevice *dev) -{ - uint16_t pos = dev->exp.aer_cap; - - pci_set_long(dev->wmask + pos + PCI_ERR_ROOT_COMMAND, - PCI_ERR_ROOT_CMD_EN_MASK); - pci_set_long(dev->w1cmask + pos + PCI_ERR_ROOT_STATUS, - PCI_ERR_ROOT_STATUS_REPORT_MASK); - /* PCI_ERR_ROOT_IRQ is RO but devices change it using a - * device-specific method. - */ - pci_set_long(dev->cmask + pos + PCI_ERR_ROOT_STATUS, - ~PCI_ERR_ROOT_IRQ); -} - -void pcie_aer_root_reset(PCIDevice *dev) -{ - uint8_t* aer_cap = dev->config + dev->exp.aer_cap; - - pci_set_long(aer_cap + PCI_ERR_ROOT_COMMAND, 0); - - /* - * Advanced Error Interrupt Message Number in Root Error Status Register - * must be updated by chip dependent code because it's chip dependent - * which number is used. - */ -} - -void pcie_aer_root_write_config(PCIDevice *dev, - uint32_t addr, uint32_t val, int len, - uint32_t root_cmd_prev) -{ - uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); - uint32_t enabled_cmd = pcie_aer_status_to_cmd(root_status); - uint32_t root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND); - /* 6.2.4.1.2 Interrupt Generation */ - if (!msix_enabled(dev) && !msi_enabled(dev)) { - pci_set_irq(dev, !!(root_cmd & enabled_cmd)); - return; - } - - if ((root_cmd_prev & enabled_cmd) || !(root_cmd & enabled_cmd)) { - /* Send MSI on transition from false to true. */ - return; - } - - pcie_aer_root_notify(dev); -} - -static const VMStateDescription vmstate_pcie_aer_err = { - .name = "PCIE_AER_ERROR", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(status, PCIEAERErr), - VMSTATE_UINT16(source_id, PCIEAERErr), - VMSTATE_UINT16(flags, PCIEAERErr), - VMSTATE_UINT32_ARRAY(header, PCIEAERErr, 4), - VMSTATE_UINT32_ARRAY(prefix, PCIEAERErr, 4), - VMSTATE_END_OF_LIST() - } -}; - -static bool pcie_aer_state_log_num_valid(void *opaque, int version_id) -{ - PCIEAERLog *s = opaque; - - return s->log_num <= s->log_max; -} - -const VMStateDescription vmstate_pcie_aer_log = { - .name = "PCIE_AER_ERROR_LOG", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT16(log_num, PCIEAERLog), - VMSTATE_UINT16_EQUAL(log_max, PCIEAERLog), - VMSTATE_VALIDATE("log_num <= log_max", pcie_aer_state_log_num_valid), - VMSTATE_STRUCT_VARRAY_POINTER_UINT16(log, PCIEAERLog, log_num, - vmstate_pcie_aer_err, PCIEAERErr), - VMSTATE_END_OF_LIST() - } -}; - -typedef struct PCIEAERErrorName { - const char *name; - uint32_t val; - bool correctable; -} PCIEAERErrorName; - -/* - * AER error name -> value conversion table - * This naming scheme is same to linux aer-injection tool. - */ -static const struct PCIEAERErrorName pcie_aer_error_list[] = { - { - .name = "DLP", - .val = PCI_ERR_UNC_DLP, - .correctable = false, - }, { - .name = "SDN", - .val = PCI_ERR_UNC_SDN, - .correctable = false, - }, { - .name = "POISON_TLP", - .val = PCI_ERR_UNC_POISON_TLP, - .correctable = false, - }, { - .name = "FCP", - .val = PCI_ERR_UNC_FCP, - .correctable = false, - }, { - .name = "COMP_TIME", - .val = PCI_ERR_UNC_COMP_TIME, - .correctable = false, - }, { - .name = "COMP_ABORT", - .val = PCI_ERR_UNC_COMP_ABORT, - .correctable = false, - }, { - .name = "UNX_COMP", - .val = PCI_ERR_UNC_UNX_COMP, - .correctable = false, - }, { - .name = "RX_OVER", - .val = PCI_ERR_UNC_RX_OVER, - .correctable = false, - }, { - .name = "MALF_TLP", - .val = PCI_ERR_UNC_MALF_TLP, - .correctable = false, - }, { - .name = "ECRC", - .val = PCI_ERR_UNC_ECRC, - .correctable = false, - }, { - .name = "UNSUP", - .val = PCI_ERR_UNC_UNSUP, - .correctable = false, - }, { - .name = "ACSV", - .val = PCI_ERR_UNC_ACSV, - .correctable = false, - }, { - .name = "INTN", - .val = PCI_ERR_UNC_INTN, - .correctable = false, - }, { - .name = "MCBTLP", - .val = PCI_ERR_UNC_MCBTLP, - .correctable = false, - }, { - .name = "ATOP_EBLOCKED", - .val = PCI_ERR_UNC_ATOP_EBLOCKED, - .correctable = false, - }, { - .name = "TLP_PRF_BLOCKED", - .val = PCI_ERR_UNC_TLP_PRF_BLOCKED, - .correctable = false, - }, { - .name = "RCVR", - .val = PCI_ERR_COR_RCVR, - .correctable = true, - }, { - .name = "BAD_TLP", - .val = PCI_ERR_COR_BAD_TLP, - .correctable = true, - }, { - .name = "BAD_DLLP", - .val = PCI_ERR_COR_BAD_DLLP, - .correctable = true, - }, { - .name = "REP_ROLL", - .val = PCI_ERR_COR_REP_ROLL, - .correctable = true, - }, { - .name = "REP_TIMER", - .val = PCI_ERR_COR_REP_TIMER, - .correctable = true, - }, { - .name = "ADV_NONFATAL", - .val = PCI_ERR_COR_ADV_NONFATAL, - .correctable = true, - }, { - .name = "INTERNAL", - .val = PCI_ERR_COR_INTERNAL, - .correctable = true, - }, { - .name = "HL_OVERFLOW", - .val = PCI_ERR_COR_HL_OVERFLOW, - .correctable = true, - }, -}; - -static int pcie_aer_parse_error_string(const char *error_name, - uint32_t *status, bool *correctable) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(pcie_aer_error_list); i++) { - const PCIEAERErrorName *e = &pcie_aer_error_list[i]; - if (strcmp(error_name, e->name)) { - continue; - } - - *status = e->val; - *correctable = e->correctable; - return 0; - } - return -EINVAL; -} - -static int do_pcie_aer_inject_error(Monitor *mon, - const QDict *qdict, QObject **ret_data) -{ - const char *id = qdict_get_str(qdict, "id"); - const char *error_name; - uint32_t error_status; - bool correctable; - PCIDevice *dev; - PCIEAERErr err; - int ret; - - ret = pci_qdev_find_device(id, &dev); - if (ret < 0) { - monitor_printf(mon, - "id or pci device path is invalid or device not " - "found. %s\n", id); - return ret; - } - if (!pci_is_express(dev)) { - monitor_printf(mon, "the device doesn't support pci express. %s\n", - id); - return -ENOSYS; - } - - error_name = qdict_get_str(qdict, "error_status"); - if (pcie_aer_parse_error_string(error_name, &error_status, &correctable)) { - char *e = NULL; - error_status = strtoul(error_name, &e, 0); - correctable = qdict_get_try_bool(qdict, "correctable", false); - if (!e || *e != '\0') { - monitor_printf(mon, "invalid error status value. \"%s\"", - error_name); - return -EINVAL; - } - } - err.status = error_status; - err.source_id = pci_requester_id(dev); - - err.flags = 0; - if (correctable) { - err.flags |= PCIE_AER_ERR_IS_CORRECTABLE; - } - if (qdict_get_try_bool(qdict, "advisory_non_fatal", false)) { - err.flags |= PCIE_AER_ERR_MAYBE_ADVISORY; - } - if (qdict_haskey(qdict, "header0")) { - err.flags |= PCIE_AER_ERR_HEADER_VALID; - } - if (qdict_haskey(qdict, "prefix0")) { - err.flags |= PCIE_AER_ERR_TLP_PREFIX_PRESENT; - } - - err.header[0] = qdict_get_try_int(qdict, "header0", 0); - err.header[1] = qdict_get_try_int(qdict, "header1", 0); - err.header[2] = qdict_get_try_int(qdict, "header2", 0); - err.header[3] = qdict_get_try_int(qdict, "header3", 0); - - err.prefix[0] = qdict_get_try_int(qdict, "prefix0", 0); - err.prefix[1] = qdict_get_try_int(qdict, "prefix1", 0); - err.prefix[2] = qdict_get_try_int(qdict, "prefix2", 0); - err.prefix[3] = qdict_get_try_int(qdict, "prefix3", 0); - - ret = pcie_aer_inject_error(dev, &err); - *ret_data = qobject_from_jsonf("{'id': %s, " - "'root_bus': %s, 'bus': %d, 'devfn': %d, " - "'ret': %d}", - id, pci_root_bus_path(dev), - pci_bus_num(dev->bus), dev->devfn, - ret); - assert(*ret_data); - - return 0; -} - -void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict) -{ - QObject *data; - int devfn; - - if (do_pcie_aer_inject_error(mon, qdict, &data) < 0) { - return; - } - - assert(qobject_type(data) == QTYPE_QDICT); - qdict = qobject_to_qdict(data); - - devfn = (int)qdict_get_int(qdict, "devfn"); - monitor_printf(mon, "OK id: %s root bus: %s, bus: %x devfn: %x.%x\n", - qdict_get_str(qdict, "id"), - qdict_get_str(qdict, "root_bus"), - (int) qdict_get_int(qdict, "bus"), - PCI_SLOT(devfn), PCI_FUNC(devfn)); -} diff --git a/qemu/hw/pci/pcie_host.c b/qemu/hw/pci/pcie_host.c deleted file mode 100644 index dcebf57ed..000000000 --- a/qemu/hw/pci/pcie_host.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * pcie_host.c - * utility functions for pci express host bridge. - * - * Copyright (c) 2009 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "hw/pci/pcie_host.h" -#include "exec/address-spaces.h" - -/* a helper function to get a PCIDevice for a given mmconfig address */ -static inline PCIDevice *pcie_dev_find_by_mmcfg_addr(PCIBus *s, - uint32_t mmcfg_addr) -{ - return pci_find_device(s, PCIE_MMCFG_BUS(mmcfg_addr), - PCIE_MMCFG_DEVFN(mmcfg_addr)); -} - -static void pcie_mmcfg_data_write(void *opaque, hwaddr mmcfg_addr, - uint64_t val, unsigned len) -{ - PCIExpressHost *e = opaque; - PCIBus *s = e->pci.bus; - PCIDevice *pci_dev = pcie_dev_find_by_mmcfg_addr(s, mmcfg_addr); - uint32_t addr; - uint32_t limit; - - if (!pci_dev) { - return; - } - addr = PCIE_MMCFG_CONFOFFSET(mmcfg_addr); - limit = pci_config_size(pci_dev); - if (limit <= addr) { - /* conventional pci device can be behind pcie-to-pci bridge. - 256 <= addr < 4K has no effects. */ - return; - } - pci_host_config_write_common(pci_dev, addr, limit, val, len); -} - -static uint64_t pcie_mmcfg_data_read(void *opaque, - hwaddr mmcfg_addr, - unsigned len) -{ - PCIExpressHost *e = opaque; - PCIBus *s = e->pci.bus; - PCIDevice *pci_dev = pcie_dev_find_by_mmcfg_addr(s, mmcfg_addr); - uint32_t addr; - uint32_t limit; - - if (!pci_dev) { - return ~0x0; - } - addr = PCIE_MMCFG_CONFOFFSET(mmcfg_addr); - limit = pci_config_size(pci_dev); - if (limit <= addr) { - /* conventional pci device can be behind pcie-to-pci bridge. - 256 <= addr < 4K has no effects. */ - return ~0x0; - } - return pci_host_config_read_common(pci_dev, addr, limit, len); -} - -static const MemoryRegionOps pcie_mmcfg_ops = { - .read = pcie_mmcfg_data_read, - .write = pcie_mmcfg_data_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pcie_host_init(Object *obj) -{ - PCIExpressHost *e = PCIE_HOST_BRIDGE(obj); - - e->base_addr = PCIE_BASE_ADDR_UNMAPPED; - memory_region_init_io(&e->mmio, OBJECT(e), &pcie_mmcfg_ops, e, "pcie-mmcfg-mmio", - PCIE_MMCFG_SIZE_MAX); -} - -void pcie_host_mmcfg_unmap(PCIExpressHost *e) -{ - if (e->base_addr != PCIE_BASE_ADDR_UNMAPPED) { - memory_region_del_subregion(get_system_memory(), &e->mmio); - e->base_addr = PCIE_BASE_ADDR_UNMAPPED; - } -} - -void pcie_host_mmcfg_init(PCIExpressHost *e, uint32_t size) -{ - assert(!(size & (size - 1))); /* power of 2 */ - assert(size >= PCIE_MMCFG_SIZE_MIN); - assert(size <= PCIE_MMCFG_SIZE_MAX); - e->size = size; - memory_region_set_size(&e->mmio, e->size); -} - -void pcie_host_mmcfg_map(PCIExpressHost *e, hwaddr addr, - uint32_t size) -{ - pcie_host_mmcfg_init(e, size); - e->base_addr = addr; - memory_region_add_subregion(get_system_memory(), e->base_addr, &e->mmio); -} - -void pcie_host_mmcfg_update(PCIExpressHost *e, - int enable, - hwaddr addr, - uint32_t size) -{ - memory_region_transaction_begin(); - pcie_host_mmcfg_unmap(e); - if (enable) { - pcie_host_mmcfg_map(e, addr, size); - } - memory_region_transaction_commit(); -} - -static const TypeInfo pcie_host_type_info = { - .name = TYPE_PCIE_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .abstract = true, - .instance_size = sizeof(PCIExpressHost), - .instance_init = pcie_host_init, -}; - -static void pcie_host_register_types(void) -{ - type_register_static(&pcie_host_type_info); -} - -type_init(pcie_host_register_types) diff --git a/qemu/hw/pci/pcie_port.c b/qemu/hw/pci/pcie_port.c deleted file mode 100644 index 6432b9ac1..000000000 --- a/qemu/hw/pci/pcie_port.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * pcie_port.c - * - * Copyright (c) 2010 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/pci/pcie_port.h" -#include "hw/hotplug.h" - -void pcie_port_init_reg(PCIDevice *d) -{ - /* Unlike pci bridge, - 66MHz and fast back to back don't apply to pci express port. */ - pci_set_word(d->config + PCI_STATUS, 0); - pci_set_word(d->config + PCI_SEC_STATUS, 0); - - /* - * Unlike conventional pci bridge, for some bits the spec states: - * Does not apply to PCI Express and must be hardwired to 0. - */ - pci_word_test_and_clear_mask(d->wmask + PCI_BRIDGE_CONTROL, - PCI_BRIDGE_CTL_MASTER_ABORT | - PCI_BRIDGE_CTL_FAST_BACK | - PCI_BRIDGE_CTL_DISCARD | - PCI_BRIDGE_CTL_SEC_DISCARD | - PCI_BRIDGE_CTL_DISCARD_STATUS | - PCI_BRIDGE_CTL_DISCARD_SERR); -} - -/************************************************************************** - * (chassis number, pcie physical slot number) -> pcie slot conversion - */ -struct PCIEChassis { - uint8_t number; - - QLIST_HEAD(, PCIESlot) slots; - QLIST_ENTRY(PCIEChassis) next; -}; - -static QLIST_HEAD(, PCIEChassis) chassis = QLIST_HEAD_INITIALIZER(chassis); - -static struct PCIEChassis *pcie_chassis_find(uint8_t chassis_number) -{ - struct PCIEChassis *c; - QLIST_FOREACH(c, &chassis, next) { - if (c->number == chassis_number) { - break; - } - } - return c; -} - -void pcie_chassis_create(uint8_t chassis_number) -{ - struct PCIEChassis *c; - c = pcie_chassis_find(chassis_number); - if (c) { - return; - } - c = g_malloc0(sizeof(*c)); - c->number = chassis_number; - QLIST_INIT(&c->slots); - QLIST_INSERT_HEAD(&chassis, c, next); -} - -static PCIESlot *pcie_chassis_find_slot_with_chassis(struct PCIEChassis *c, - uint8_t slot) -{ - PCIESlot *s; - QLIST_FOREACH(s, &c->slots, next) { - if (s->slot == slot) { - break; - } - } - return s; -} - -PCIESlot *pcie_chassis_find_slot(uint8_t chassis_number, uint16_t slot) -{ - struct PCIEChassis *c; - c = pcie_chassis_find(chassis_number); - if (!c) { - return NULL; - } - return pcie_chassis_find_slot_with_chassis(c, slot); -} - -int pcie_chassis_add_slot(struct PCIESlot *slot) -{ - struct PCIEChassis *c; - c = pcie_chassis_find(slot->chassis); - if (!c) { - return -ENODEV; - } - if (pcie_chassis_find_slot_with_chassis(c, slot->slot)) { - return -EBUSY; - } - QLIST_INSERT_HEAD(&c->slots, slot, next); - return 0; -} - -void pcie_chassis_del_slot(PCIESlot *s) -{ - QLIST_REMOVE(s, next); -} - -static Property pcie_port_props[] = { - DEFINE_PROP_UINT8("port", PCIEPort, port, 0), - DEFINE_PROP_UINT16("aer_log_max", PCIEPort, - parent_obj.parent_obj.exp.aer_log.log_max, - PCIE_AER_LOG_MAX_DEFAULT), - DEFINE_PROP_END_OF_LIST() -}; - -static void pcie_port_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->props = pcie_port_props; -} - -static const TypeInfo pcie_port_type_info = { - .name = TYPE_PCIE_PORT, - .parent = TYPE_PCI_BRIDGE, - .instance_size = sizeof(PCIEPort), - .abstract = true, - .class_init = pcie_port_class_init, -}; - -static Property pcie_slot_props[] = { - DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0), - DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0), - DEFINE_PROP_END_OF_LIST() -}; - -static void pcie_slot_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); - - dc->props = pcie_slot_props; - hc->plug = pcie_cap_slot_hotplug_cb; - hc->unplug_request = pcie_cap_slot_hot_unplug_request_cb; -} - -static const TypeInfo pcie_slot_type_info = { - .name = TYPE_PCIE_SLOT, - .parent = TYPE_PCIE_PORT, - .instance_size = sizeof(PCIESlot), - .abstract = true, - .class_init = pcie_slot_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - -static void pcie_port_register_types(void) -{ - type_register_static(&pcie_port_type_info); - type_register_static(&pcie_slot_type_info); -} - -type_init(pcie_port_register_types) diff --git a/qemu/hw/pci/shpc.c b/qemu/hw/pci/shpc.c deleted file mode 100644 index 3dcd472eb..000000000 --- a/qemu/hw/pci/shpc.c +++ /dev/null @@ -1,721 +0,0 @@ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "qemu/range.h" -#include "qemu/error-report.h" -#include "hw/pci/shpc.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_bus.h" -#include "hw/pci/msi.h" - -/* TODO: model power only and disabled slot states. */ -/* TODO: handle SERR and wakeups */ -/* TODO: consider enabling 66MHz support */ - -/* TODO: remove fully only on state DISABLED and LED off. - * track state to properly record this. */ - -/* SHPC Working Register Set */ -#define SHPC_BASE_OFFSET 0x00 /* 4 bytes */ -#define SHPC_SLOTS_33 0x04 /* 4 bytes. Also encodes PCI-X slots. */ -#define SHPC_SLOTS_66 0x08 /* 4 bytes. */ -#define SHPC_NSLOTS 0x0C /* 1 byte */ -#define SHPC_FIRST_DEV 0x0D /* 1 byte */ -#define SHPC_PHYS_SLOT 0x0E /* 2 byte */ -#define SHPC_PHYS_NUM_MAX 0x7ff -#define SHPC_PHYS_NUM_UP 0x2000 -#define SHPC_PHYS_MRL 0x4000 -#define SHPC_PHYS_BUTTON 0x8000 -#define SHPC_SEC_BUS 0x10 /* 2 bytes */ -#define SHPC_SEC_BUS_33 0x0 -#define SHPC_SEC_BUS_66 0x1 /* Unused */ -#define SHPC_SEC_BUS_MASK 0x7 -#define SHPC_MSI_CTL 0x12 /* 1 byte */ -#define SHPC_PROG_IFC 0x13 /* 1 byte */ -#define SHPC_PROG_IFC_1_0 0x1 -#define SHPC_CMD_CODE 0x14 /* 1 byte */ -#define SHPC_CMD_TRGT 0x15 /* 1 byte */ -#define SHPC_CMD_TRGT_MIN 0x1 -#define SHPC_CMD_TRGT_MAX 0x1f -#define SHPC_CMD_STATUS 0x16 /* 2 bytes */ -#define SHPC_CMD_STATUS_BUSY 0x1 -#define SHPC_CMD_STATUS_MRL_OPEN 0x2 -#define SHPC_CMD_STATUS_INVALID_CMD 0x4 -#define SHPC_CMD_STATUS_INVALID_MODE 0x8 -#define SHPC_INT_LOCATOR 0x18 /* 4 bytes */ -#define SHPC_INT_COMMAND 0x1 -#define SHPC_SERR_LOCATOR 0x1C /* 4 bytes */ -#define SHPC_SERR_INT 0x20 /* 4 bytes */ -#define SHPC_INT_DIS 0x1 -#define SHPC_SERR_DIS 0x2 -#define SHPC_CMD_INT_DIS 0x4 -#define SHPC_ARB_SERR_DIS 0x8 -#define SHPC_CMD_DETECTED 0x10000 -#define SHPC_ARB_DETECTED 0x20000 - /* 4 bytes * slot # (start from 0) */ -#define SHPC_SLOT_REG(s) (0x24 + (s) * 4) - /* 2 bytes */ -#define SHPC_SLOT_STATUS(s) (0x0 + SHPC_SLOT_REG(s)) - -/* Same slot state masks are used for command and status registers */ -#define SHPC_SLOT_STATE_MASK 0x03 -#define SHPC_SLOT_STATE_SHIFT \ - ctz32(SHPC_SLOT_STATE_MASK) - -#define SHPC_STATE_NO 0x0 -#define SHPC_STATE_PWRONLY 0x1 -#define SHPC_STATE_ENABLED 0x2 -#define SHPC_STATE_DISABLED 0x3 - -#define SHPC_SLOT_PWR_LED_MASK 0xC -#define SHPC_SLOT_PWR_LED_SHIFT \ - ctz32(SHPC_SLOT_PWR_LED_MASK) -#define SHPC_SLOT_ATTN_LED_MASK 0x30 -#define SHPC_SLOT_ATTN_LED_SHIFT \ - ctz32(SHPC_SLOT_ATTN_LED_MASK) - -#define SHPC_LED_NO 0x0 -#define SHPC_LED_ON 0x1 -#define SHPC_LED_BLINK 0x2 -#define SHPC_LED_OFF 0x3 - -#define SHPC_SLOT_STATUS_PWR_FAULT 0x40 -#define SHPC_SLOT_STATUS_BUTTON 0x80 -#define SHPC_SLOT_STATUS_MRL_OPEN 0x100 -#define SHPC_SLOT_STATUS_66 0x200 -#define SHPC_SLOT_STATUS_PRSNT_MASK 0xC00 -#define SHPC_SLOT_STATUS_PRSNT_EMPTY 0x3 -#define SHPC_SLOT_STATUS_PRSNT_25W 0x1 -#define SHPC_SLOT_STATUS_PRSNT_15W 0x2 -#define SHPC_SLOT_STATUS_PRSNT_7_5W 0x0 - -#define SHPC_SLOT_STATUS_PRSNT_PCIX 0x3000 - - - /* 1 byte */ -#define SHPC_SLOT_EVENT_LATCH(s) (0x2 + SHPC_SLOT_REG(s)) - /* 1 byte */ -#define SHPC_SLOT_EVENT_SERR_INT_DIS(d, s) (0x3 + SHPC_SLOT_REG(s)) -#define SHPC_SLOT_EVENT_PRESENCE 0x01 -#define SHPC_SLOT_EVENT_ISOLATED_FAULT 0x02 -#define SHPC_SLOT_EVENT_BUTTON 0x04 -#define SHPC_SLOT_EVENT_MRL 0x08 -#define SHPC_SLOT_EVENT_CONNECTED_FAULT 0x10 -/* Bits below are used for Serr/Int disable only */ -#define SHPC_SLOT_EVENT_MRL_SERR_DIS 0x20 -#define SHPC_SLOT_EVENT_CONNECTED_FAULT_SERR_DIS 0x40 - -#define SHPC_MIN_SLOTS 1 -#define SHPC_MAX_SLOTS 31 -#define SHPC_SIZEOF(d) SHPC_SLOT_REG((d)->shpc->nslots) - -/* SHPC Slot identifiers */ - -/* Hotplug supported at 31 slots out of the total 32. We reserve slot 0, - and give the rest of them physical *and* pci numbers starting from 1, so - they match logical numbers. Note: this means that multiple slots must have - different chassis number values, to make chassis+physical slot unique. - TODO: make this configurable? */ -#define SHPC_IDX_TO_LOGICAL(slot) ((slot) + 1) -#define SHPC_LOGICAL_TO_IDX(target) ((target) - 1) -#define SHPC_IDX_TO_PCI(slot) ((slot) + 1) -#define SHPC_PCI_TO_IDX(pci_slot) ((pci_slot) - 1) -#define SHPC_IDX_TO_PHYSICAL(slot) ((slot) + 1) - -static int roundup_pow_of_two(int x) -{ - x |= (x >> 1); - x |= (x >> 2); - x |= (x >> 4); - x |= (x >> 8); - x |= (x >> 16); - return x + 1; -} - -static uint16_t shpc_get_status(SHPCDevice *shpc, int slot, uint16_t msk) -{ - uint8_t *status = shpc->config + SHPC_SLOT_STATUS(slot); - return (pci_get_word(status) & msk) >> ctz32(msk); -} - -static void shpc_set_status(SHPCDevice *shpc, - int slot, uint8_t value, uint16_t msk) -{ - uint8_t *status = shpc->config + SHPC_SLOT_STATUS(slot); - pci_word_test_and_clear_mask(status, msk); - pci_word_test_and_set_mask(status, value << ctz32(msk)); -} - -static void shpc_interrupt_update(PCIDevice *d) -{ - SHPCDevice *shpc = d->shpc; - int slot; - int level = 0; - uint32_t serr_int; - uint32_t int_locator = 0; - - /* Update interrupt locator register */ - for (slot = 0; slot < shpc->nslots; ++slot) { - uint8_t event = shpc->config[SHPC_SLOT_EVENT_LATCH(slot)]; - uint8_t disable = shpc->config[SHPC_SLOT_EVENT_SERR_INT_DIS(d, slot)]; - uint32_t mask = 1U << SHPC_IDX_TO_LOGICAL(slot); - if (event & ~disable) { - int_locator |= mask; - } - } - serr_int = pci_get_long(shpc->config + SHPC_SERR_INT); - if ((serr_int & SHPC_CMD_DETECTED) && !(serr_int & SHPC_CMD_INT_DIS)) { - int_locator |= SHPC_INT_COMMAND; - } - pci_set_long(shpc->config + SHPC_INT_LOCATOR, int_locator); - level = (!(serr_int & SHPC_INT_DIS) && int_locator) ? 1 : 0; - if (msi_enabled(d) && shpc->msi_requested != level) - msi_notify(d, 0); - else - pci_set_irq(d, level); - shpc->msi_requested = level; -} - -static void shpc_set_sec_bus_speed(SHPCDevice *shpc, uint8_t speed) -{ - switch (speed) { - case SHPC_SEC_BUS_33: - shpc->config[SHPC_SEC_BUS] &= ~SHPC_SEC_BUS_MASK; - shpc->config[SHPC_SEC_BUS] |= speed; - break; - default: - pci_word_test_and_set_mask(shpc->config + SHPC_CMD_STATUS, - SHPC_CMD_STATUS_INVALID_MODE); - } -} - -void shpc_reset(PCIDevice *d) -{ - SHPCDevice *shpc = d->shpc; - int nslots = shpc->nslots; - int i; - memset(shpc->config, 0, SHPC_SIZEOF(d)); - pci_set_byte(shpc->config + SHPC_NSLOTS, nslots); - pci_set_long(shpc->config + SHPC_SLOTS_33, nslots); - pci_set_long(shpc->config + SHPC_SLOTS_66, 0); - pci_set_byte(shpc->config + SHPC_FIRST_DEV, SHPC_IDX_TO_PCI(0)); - pci_set_word(shpc->config + SHPC_PHYS_SLOT, - SHPC_IDX_TO_PHYSICAL(0) | - SHPC_PHYS_NUM_UP | - SHPC_PHYS_MRL | - SHPC_PHYS_BUTTON); - pci_set_long(shpc->config + SHPC_SERR_INT, SHPC_INT_DIS | - SHPC_SERR_DIS | - SHPC_CMD_INT_DIS | - SHPC_ARB_SERR_DIS); - pci_set_byte(shpc->config + SHPC_PROG_IFC, SHPC_PROG_IFC_1_0); - pci_set_word(shpc->config + SHPC_SEC_BUS, SHPC_SEC_BUS_33); - for (i = 0; i < shpc->nslots; ++i) { - pci_set_byte(shpc->config + SHPC_SLOT_EVENT_SERR_INT_DIS(d, i), - SHPC_SLOT_EVENT_PRESENCE | - SHPC_SLOT_EVENT_ISOLATED_FAULT | - SHPC_SLOT_EVENT_BUTTON | - SHPC_SLOT_EVENT_MRL | - SHPC_SLOT_EVENT_CONNECTED_FAULT | - SHPC_SLOT_EVENT_MRL_SERR_DIS | - SHPC_SLOT_EVENT_CONNECTED_FAULT_SERR_DIS); - if (shpc->sec_bus->devices[PCI_DEVFN(SHPC_IDX_TO_PCI(i), 0)]) { - shpc_set_status(shpc, i, SHPC_STATE_ENABLED, SHPC_SLOT_STATE_MASK); - shpc_set_status(shpc, i, 0, SHPC_SLOT_STATUS_MRL_OPEN); - shpc_set_status(shpc, i, SHPC_SLOT_STATUS_PRSNT_7_5W, - SHPC_SLOT_STATUS_PRSNT_MASK); - shpc_set_status(shpc, i, SHPC_LED_ON, SHPC_SLOT_PWR_LED_MASK); - } else { - shpc_set_status(shpc, i, SHPC_STATE_DISABLED, SHPC_SLOT_STATE_MASK); - shpc_set_status(shpc, i, 1, SHPC_SLOT_STATUS_MRL_OPEN); - shpc_set_status(shpc, i, SHPC_SLOT_STATUS_PRSNT_EMPTY, - SHPC_SLOT_STATUS_PRSNT_MASK); - shpc_set_status(shpc, i, SHPC_LED_OFF, SHPC_SLOT_PWR_LED_MASK); - } - shpc_set_status(shpc, i, 0, SHPC_SLOT_STATUS_66); - } - shpc_set_sec_bus_speed(shpc, SHPC_SEC_BUS_33); - shpc->msi_requested = 0; - shpc_interrupt_update(d); -} - -static void shpc_invalid_command(SHPCDevice *shpc) -{ - pci_word_test_and_set_mask(shpc->config + SHPC_CMD_STATUS, - SHPC_CMD_STATUS_INVALID_CMD); -} - -static void shpc_free_devices_in_slot(SHPCDevice *shpc, int slot) -{ - int devfn; - int pci_slot = SHPC_IDX_TO_PCI(slot); - for (devfn = PCI_DEVFN(pci_slot, 0); - devfn <= PCI_DEVFN(pci_slot, PCI_FUNC_MAX - 1); - ++devfn) { - PCIDevice *affected_dev = shpc->sec_bus->devices[devfn]; - if (affected_dev) { - object_unparent(OBJECT(affected_dev)); - } - } -} - -static void shpc_slot_command(SHPCDevice *shpc, uint8_t target, - uint8_t state, uint8_t power, uint8_t attn) -{ - uint8_t current_state; - int slot = SHPC_LOGICAL_TO_IDX(target); - if (target < SHPC_CMD_TRGT_MIN || slot >= shpc->nslots) { - shpc_invalid_command(shpc); - return; - } - current_state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK); - if (current_state == SHPC_STATE_ENABLED && state == SHPC_STATE_PWRONLY) { - shpc_invalid_command(shpc); - return; - } - - switch (power) { - case SHPC_LED_NO: - break; - default: - /* TODO: send event to monitor */ - shpc_set_status(shpc, slot, power, SHPC_SLOT_PWR_LED_MASK); - } - switch (attn) { - case SHPC_LED_NO: - break; - default: - /* TODO: send event to monitor */ - shpc_set_status(shpc, slot, attn, SHPC_SLOT_ATTN_LED_MASK); - } - - if ((current_state == SHPC_STATE_DISABLED && state == SHPC_STATE_PWRONLY) || - (current_state == SHPC_STATE_DISABLED && state == SHPC_STATE_ENABLED)) { - shpc_set_status(shpc, slot, state, SHPC_SLOT_STATE_MASK); - } else if ((current_state == SHPC_STATE_ENABLED || - current_state == SHPC_STATE_PWRONLY) && - state == SHPC_STATE_DISABLED) { - shpc_set_status(shpc, slot, state, SHPC_SLOT_STATE_MASK); - power = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK); - /* TODO: track what monitor requested. */ - /* Look at LED to figure out whether it's ok to remove the device. */ - if (power == SHPC_LED_OFF) { - shpc_free_devices_in_slot(shpc, slot); - shpc_set_status(shpc, slot, 1, SHPC_SLOT_STATUS_MRL_OPEN); - shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_EMPTY, - SHPC_SLOT_STATUS_PRSNT_MASK); - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= - SHPC_SLOT_EVENT_BUTTON | - SHPC_SLOT_EVENT_MRL | - SHPC_SLOT_EVENT_PRESENCE; - } - } -} - -static void shpc_command(SHPCDevice *shpc) -{ - uint8_t code = pci_get_byte(shpc->config + SHPC_CMD_CODE); - uint8_t speed; - uint8_t target; - uint8_t attn; - uint8_t power; - uint8_t state; - int i; - - /* Clear status from the previous command. */ - pci_word_test_and_clear_mask(shpc->config + SHPC_CMD_STATUS, - SHPC_CMD_STATUS_BUSY | - SHPC_CMD_STATUS_MRL_OPEN | - SHPC_CMD_STATUS_INVALID_CMD | - SHPC_CMD_STATUS_INVALID_MODE); - switch (code) { - case 0x00 ... 0x3f: - target = shpc->config[SHPC_CMD_TRGT] & SHPC_CMD_TRGT_MAX; - state = (code & SHPC_SLOT_STATE_MASK) >> SHPC_SLOT_STATE_SHIFT; - power = (code & SHPC_SLOT_PWR_LED_MASK) >> SHPC_SLOT_PWR_LED_SHIFT; - attn = (code & SHPC_SLOT_ATTN_LED_MASK) >> SHPC_SLOT_ATTN_LED_SHIFT; - shpc_slot_command(shpc, target, state, power, attn); - break; - case 0x40 ... 0x47: - speed = code & SHPC_SEC_BUS_MASK; - shpc_set_sec_bus_speed(shpc, speed); - break; - case 0x48: - /* Power only all slots */ - /* first verify no slots are enabled */ - for (i = 0; i < shpc->nslots; ++i) { - state = shpc_get_status(shpc, i, SHPC_SLOT_STATE_MASK); - if (state == SHPC_STATE_ENABLED) { - shpc_invalid_command(shpc); - goto done; - } - } - for (i = 0; i < shpc->nslots; ++i) { - if (!(shpc_get_status(shpc, i, SHPC_SLOT_STATUS_MRL_OPEN))) { - shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN, - SHPC_STATE_PWRONLY, SHPC_LED_ON, SHPC_LED_NO); - } else { - shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN, - SHPC_STATE_NO, SHPC_LED_OFF, SHPC_LED_NO); - } - } - break; - case 0x49: - /* Enable all slots */ - /* TODO: Spec says this shall fail if some are already enabled. - * This doesn't make sense - why not? a spec bug? */ - for (i = 0; i < shpc->nslots; ++i) { - state = shpc_get_status(shpc, i, SHPC_SLOT_STATE_MASK); - if (state == SHPC_STATE_ENABLED) { - shpc_invalid_command(shpc); - goto done; - } - } - for (i = 0; i < shpc->nslots; ++i) { - if (!(shpc_get_status(shpc, i, SHPC_SLOT_STATUS_MRL_OPEN))) { - shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN, - SHPC_STATE_ENABLED, SHPC_LED_ON, SHPC_LED_NO); - } else { - shpc_slot_command(shpc, i + SHPC_CMD_TRGT_MIN, - SHPC_STATE_NO, SHPC_LED_OFF, SHPC_LED_NO); - } - } - break; - default: - shpc_invalid_command(shpc); - break; - } -done: - pci_long_test_and_set_mask(shpc->config + SHPC_SERR_INT, SHPC_CMD_DETECTED); -} - -static void shpc_write(PCIDevice *d, unsigned addr, uint64_t val, int l) -{ - SHPCDevice *shpc = d->shpc; - int i; - if (addr >= SHPC_SIZEOF(d)) { - return; - } - l = MIN(l, SHPC_SIZEOF(d) - addr); - - /* TODO: code duplicated from pci.c */ - for (i = 0; i < l; val >>= 8, ++i) { - unsigned a = addr + i; - uint8_t wmask = shpc->wmask[a]; - uint8_t w1cmask = shpc->w1cmask[a]; - assert(!(wmask & w1cmask)); - shpc->config[a] = (shpc->config[a] & ~wmask) | (val & wmask); - shpc->config[a] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */ - } - if (ranges_overlap(addr, l, SHPC_CMD_CODE, 2)) { - shpc_command(shpc); - } - shpc_interrupt_update(d); -} - -static uint64_t shpc_read(PCIDevice *d, unsigned addr, int l) -{ - uint64_t val = 0x0; - if (addr >= SHPC_SIZEOF(d)) { - return val; - } - l = MIN(l, SHPC_SIZEOF(d) - addr); - memcpy(&val, d->shpc->config + addr, l); - return val; -} - -/* SHPC Bridge Capability */ -#define SHPC_CAP_LENGTH 0x08 -#define SHPC_CAP_DWORD_SELECT 0x2 /* 1 byte */ -#define SHPC_CAP_CxP 0x3 /* 1 byte: CSP, CIP */ -#define SHPC_CAP_DWORD_DATA 0x4 /* 4 bytes */ -#define SHPC_CAP_CSP_MASK 0x4 -#define SHPC_CAP_CIP_MASK 0x8 - -static uint8_t shpc_cap_dword(PCIDevice *d) -{ - return pci_get_byte(d->config + d->shpc->cap + SHPC_CAP_DWORD_SELECT); -} - -/* Update dword data capability register */ -static void shpc_cap_update_dword(PCIDevice *d) -{ - unsigned data; - data = shpc_read(d, shpc_cap_dword(d) * 4, 4); - pci_set_long(d->config + d->shpc->cap + SHPC_CAP_DWORD_DATA, data); -} - -/* Add SHPC capability to the config space for the device. */ -static int shpc_cap_add_config(PCIDevice *d) -{ - uint8_t *config; - int config_offset; - config_offset = pci_add_capability(d, PCI_CAP_ID_SHPC, - 0, SHPC_CAP_LENGTH); - if (config_offset < 0) { - return config_offset; - } - config = d->config + config_offset; - - pci_set_byte(config + SHPC_CAP_DWORD_SELECT, 0); - pci_set_byte(config + SHPC_CAP_CxP, 0); - pci_set_long(config + SHPC_CAP_DWORD_DATA, 0); - d->shpc->cap = config_offset; - /* Make dword select and data writeable. */ - pci_set_byte(d->wmask + config_offset + SHPC_CAP_DWORD_SELECT, 0xff); - pci_set_long(d->wmask + config_offset + SHPC_CAP_DWORD_DATA, 0xffffffff); - return 0; -} - -static uint64_t shpc_mmio_read(void *opaque, hwaddr addr, - unsigned size) -{ - return shpc_read(opaque, addr, size); -} - -static void shpc_mmio_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - shpc_write(opaque, addr, val, size); -} - -static const MemoryRegionOps shpc_mmio_ops = { - .read = shpc_mmio_read, - .write = shpc_mmio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - /* SHPC ECN requires dword accesses, but the original 1.0 spec doesn't. - * It's easier to suppport all sizes than worry about it. */ - .min_access_size = 1, - .max_access_size = 4, - }, -}; -static void shpc_device_hotplug_common(PCIDevice *affected_dev, int *slot, - SHPCDevice *shpc, Error **errp) -{ - int pci_slot = PCI_SLOT(affected_dev->devfn); - *slot = SHPC_PCI_TO_IDX(pci_slot); - - if (pci_slot < SHPC_IDX_TO_PCI(0) || *slot >= shpc->nslots) { - error_setg(errp, "Unsupported PCI slot %d for standard hotplug " - "controller. Valid slots are between %d and %d.", - pci_slot, SHPC_IDX_TO_PCI(0), - SHPC_IDX_TO_PCI(shpc->nslots) - 1); - return; - } -} - -void shpc_device_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp) -{ - Error *local_err = NULL; - PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); - SHPCDevice *shpc = pci_hotplug_dev->shpc; - int slot; - - shpc_device_hotplug_common(PCI_DEVICE(dev), &slot, shpc, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - /* Don't send event when device is enabled during qemu machine creation: - * it is present on boot, no hotplug event is necessary. We do send an - * event when the device is disabled later. */ - if (!dev->hotplugged) { - shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_MRL_OPEN); - shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_7_5W, - SHPC_SLOT_STATUS_PRSNT_MASK); - return; - } - - /* This could be a cancellation of the previous removal. - * We check MRL state to figure out. */ - if (shpc_get_status(shpc, slot, SHPC_SLOT_STATUS_MRL_OPEN)) { - shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_MRL_OPEN); - shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_7_5W, - SHPC_SLOT_STATUS_PRSNT_MASK); - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= - SHPC_SLOT_EVENT_BUTTON | - SHPC_SLOT_EVENT_MRL | - SHPC_SLOT_EVENT_PRESENCE; - } else { - /* Press attention button to cancel removal */ - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= - SHPC_SLOT_EVENT_BUTTON; - } - shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_66); - shpc_interrupt_update(pci_hotplug_dev); -} - -void shpc_device_hot_unplug_request_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - Error *local_err = NULL; - PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); - SHPCDevice *shpc = pci_hotplug_dev->shpc; - uint8_t state; - uint8_t led; - int slot; - - shpc_device_hotplug_common(PCI_DEVICE(dev), &slot, shpc, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= SHPC_SLOT_EVENT_BUTTON; - state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK); - led = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK); - if (state == SHPC_STATE_DISABLED && led == SHPC_LED_OFF) { - shpc_free_devices_in_slot(shpc, slot); - shpc_set_status(shpc, slot, 1, SHPC_SLOT_STATUS_MRL_OPEN); - shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_EMPTY, - SHPC_SLOT_STATUS_PRSNT_MASK); - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= - SHPC_SLOT_EVENT_MRL | - SHPC_SLOT_EVENT_PRESENCE; - } - shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_66); - shpc_interrupt_update(pci_hotplug_dev); -} - -/* Initialize the SHPC structure in bridge's BAR. */ -int shpc_init(PCIDevice *d, PCIBus *sec_bus, MemoryRegion *bar, unsigned offset) -{ - int i, ret; - int nslots = SHPC_MAX_SLOTS; /* TODO: qdev property? */ - SHPCDevice *shpc = d->shpc = g_malloc0(sizeof(*d->shpc)); - shpc->sec_bus = sec_bus; - ret = shpc_cap_add_config(d); - if (ret) { - g_free(d->shpc); - return ret; - } - if (nslots < SHPC_MIN_SLOTS) { - return 0; - } - if (nslots > SHPC_MAX_SLOTS || - SHPC_IDX_TO_PCI(nslots) > PCI_SLOT_MAX) { - /* TODO: report an error mesage that makes sense. */ - return -EINVAL; - } - shpc->nslots = nslots; - shpc->config = g_malloc0(SHPC_SIZEOF(d)); - shpc->cmask = g_malloc0(SHPC_SIZEOF(d)); - shpc->wmask = g_malloc0(SHPC_SIZEOF(d)); - shpc->w1cmask = g_malloc0(SHPC_SIZEOF(d)); - - shpc_reset(d); - - pci_set_long(shpc->config + SHPC_BASE_OFFSET, offset); - - pci_set_byte(shpc->wmask + SHPC_CMD_CODE, 0xff); - pci_set_byte(shpc->wmask + SHPC_CMD_TRGT, SHPC_CMD_TRGT_MAX); - pci_set_byte(shpc->wmask + SHPC_CMD_TRGT, SHPC_CMD_TRGT_MAX); - pci_set_long(shpc->wmask + SHPC_SERR_INT, - SHPC_INT_DIS | - SHPC_SERR_DIS | - SHPC_CMD_INT_DIS | - SHPC_ARB_SERR_DIS); - pci_set_long(shpc->w1cmask + SHPC_SERR_INT, - SHPC_CMD_DETECTED | - SHPC_ARB_DETECTED); - for (i = 0; i < nslots; ++i) { - pci_set_byte(shpc->wmask + - SHPC_SLOT_EVENT_SERR_INT_DIS(d, i), - SHPC_SLOT_EVENT_PRESENCE | - SHPC_SLOT_EVENT_ISOLATED_FAULT | - SHPC_SLOT_EVENT_BUTTON | - SHPC_SLOT_EVENT_MRL | - SHPC_SLOT_EVENT_CONNECTED_FAULT | - SHPC_SLOT_EVENT_MRL_SERR_DIS | - SHPC_SLOT_EVENT_CONNECTED_FAULT_SERR_DIS); - pci_set_byte(shpc->w1cmask + - SHPC_SLOT_EVENT_LATCH(i), - SHPC_SLOT_EVENT_PRESENCE | - SHPC_SLOT_EVENT_ISOLATED_FAULT | - SHPC_SLOT_EVENT_BUTTON | - SHPC_SLOT_EVENT_MRL | - SHPC_SLOT_EVENT_CONNECTED_FAULT); - } - - /* TODO: init cmask */ - memory_region_init_io(&shpc->mmio, OBJECT(d), &shpc_mmio_ops, - d, "shpc-mmio", SHPC_SIZEOF(d)); - shpc_cap_update_dword(d); - memory_region_add_subregion(bar, offset, &shpc->mmio); - - qbus_set_hotplug_handler(BUS(sec_bus), DEVICE(d), NULL); - - d->cap_present |= QEMU_PCI_CAP_SHPC; - return 0; -} - -int shpc_bar_size(PCIDevice *d) -{ - return roundup_pow_of_two(SHPC_SLOT_REG(SHPC_MAX_SLOTS)); -} - -void shpc_cleanup(PCIDevice *d, MemoryRegion *bar) -{ - SHPCDevice *shpc = d->shpc; - d->cap_present &= ~QEMU_PCI_CAP_SHPC; - memory_region_del_subregion(bar, &shpc->mmio); - /* TODO: cleanup config space changes? */ -} - -void shpc_free(PCIDevice *d) -{ - SHPCDevice *shpc = d->shpc; - if (!shpc) { - return; - } - object_unparent(OBJECT(&shpc->mmio)); - g_free(shpc->config); - g_free(shpc->cmask); - g_free(shpc->wmask); - g_free(shpc->w1cmask); - g_free(shpc); - d->shpc = NULL; -} - -void shpc_cap_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l) -{ - if (!ranges_overlap(addr, l, d->shpc->cap, SHPC_CAP_LENGTH)) { - return; - } - if (ranges_overlap(addr, l, d->shpc->cap + SHPC_CAP_DWORD_DATA, 4)) { - unsigned dword_data; - dword_data = pci_get_long(d->shpc->config + d->shpc->cap - + SHPC_CAP_DWORD_DATA); - shpc_write(d, shpc_cap_dword(d) * 4, dword_data, 4); - } - /* Update cap dword data in case guest is going to read it. */ - shpc_cap_update_dword(d); -} - -static void shpc_save(QEMUFile *f, void *pv, size_t size) -{ - PCIDevice *d = container_of(pv, PCIDevice, shpc); - qemu_put_buffer(f, d->shpc->config, SHPC_SIZEOF(d)); -} - -static int shpc_load(QEMUFile *f, void *pv, size_t size) -{ - PCIDevice *d = container_of(pv, PCIDevice, shpc); - int ret = qemu_get_buffer(f, d->shpc->config, SHPC_SIZEOF(d)); - if (ret != SHPC_SIZEOF(d)) { - return -EINVAL; - } - /* Make sure we don't lose notifications. An extra interrupt is harmless. */ - d->shpc->msi_requested = 0; - shpc_interrupt_update(d); - return 0; -} - -VMStateInfo shpc_vmstate_info = { - .name = "shpc", - .get = shpc_load, - .put = shpc_save, -}; diff --git a/qemu/hw/pci/slotid_cap.c b/qemu/hw/pci/slotid_cap.c deleted file mode 100644 index aec1e9166..000000000 --- a/qemu/hw/pci/slotid_cap.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/pci/slotid_cap.h" -#include "hw/pci/pci.h" -#include "qemu/error-report.h" - -#define SLOTID_CAP_LENGTH 4 -#define SLOTID_NSLOTS_SHIFT ctz32(PCI_SID_ESR_NSLOTS) - -int slotid_cap_init(PCIDevice *d, int nslots, - uint8_t chassis, - unsigned offset) -{ - int cap; - if (!chassis) { - error_report("Bridge chassis not specified. Each bridge is required " - "to be assigned a unique chassis id > 0."); - return -EINVAL; - } - if (nslots < 0 || nslots > (PCI_SID_ESR_NSLOTS >> SLOTID_NSLOTS_SHIFT)) { - /* TODO: error report? */ - return -EINVAL; - } - - cap = pci_add_capability(d, PCI_CAP_ID_SLOTID, offset, SLOTID_CAP_LENGTH); - if (cap < 0) { - return cap; - } - /* We make each chassis unique, this way each bridge is First in Chassis */ - d->config[cap + PCI_SID_ESR] = PCI_SID_ESR_FIC | - (nslots << SLOTID_NSLOTS_SHIFT); - d->cmask[cap + PCI_SID_ESR] = 0xff; - d->config[cap + PCI_SID_CHASSIS_NR] = chassis; - /* Note: Chassis number register is non-volatile, - so we don't reset it. */ - /* TODO: store in eeprom? */ - d->wmask[cap + PCI_SID_CHASSIS_NR] = 0xff; - - d->cap_present |= QEMU_PCI_CAP_SLOTID; - return 0; -} - -void slotid_cap_cleanup(PCIDevice *d) -{ - /* TODO: cleanup config space? */ - d->cap_present &= ~QEMU_PCI_CAP_SLOTID; -} diff --git a/qemu/hw/pcmcia/Makefile.objs b/qemu/hw/pcmcia/Makefile.objs deleted file mode 100644 index 4eac060c9..000000000 --- a/qemu/hw/pcmcia/Makefile.objs +++ /dev/null @@ -1,2 +0,0 @@ -common-obj-y += pcmcia.o -obj-$(CONFIG_PXA2XX) += pxa2xx.o diff --git a/qemu/hw/pcmcia/pcmcia.c b/qemu/hw/pcmcia/pcmcia.c deleted file mode 100644 index 195672186..000000000 --- a/qemu/hw/pcmcia/pcmcia.c +++ /dev/null @@ -1,25 +0,0 @@ -/* - * PCMCIA emulation - * - * Copyright 2013 SUSE LINUX Products GmbH - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "hw/hw.h" -#include "hw/pcmcia.h" - -static const TypeInfo pcmcia_card_type_info = { - .name = TYPE_PCMCIA_CARD, - .parent = TYPE_DEVICE, - .instance_size = sizeof(PCMCIACardState), - .abstract = true, - .class_size = sizeof(PCMCIACardClass), -}; - -static void pcmcia_register_types(void) -{ - type_register_static(&pcmcia_card_type_info); -} - -type_init(pcmcia_register_types) diff --git a/qemu/hw/pcmcia/pxa2xx.c b/qemu/hw/pcmcia/pxa2xx.c deleted file mode 100644 index 20c9c753d..000000000 --- a/qemu/hw/pcmcia/pxa2xx.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Intel XScale PXA255/270 PC Card and CompactFlash Interface. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GPLv2. - * - * 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/pcmcia.h" -#include "hw/arm/pxa.h" - -#define TYPE_PXA2XX_PCMCIA "pxa2xx-pcmcia" -#define PXA2XX_PCMCIA(obj) \ - OBJECT_CHECK(PXA2xxPCMCIAState, obj, TYPE_PXA2XX_PCMCIA) - -struct PXA2xxPCMCIAState { - SysBusDevice parent_obj; - - PCMCIASocket slot; - MemoryRegion container_mem; - MemoryRegion common_iomem; - MemoryRegion attr_iomem; - MemoryRegion iomem; - - qemu_irq irq; - qemu_irq cd_irq; - - PCMCIACardState *card; -}; - -static uint64_t pxa2xx_pcmcia_common_read(void *opaque, - hwaddr offset, unsigned size) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - PCMCIACardClass *pcc; - - if (s->slot.attached) { - pcc = PCMCIA_CARD_GET_CLASS(s->card); - return pcc->common_read(s->card, offset); - } - - return 0; -} - -static void pxa2xx_pcmcia_common_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - PCMCIACardClass *pcc; - - if (s->slot.attached) { - pcc = PCMCIA_CARD_GET_CLASS(s->card); - pcc->common_write(s->card, offset, value); - } -} - -static uint64_t pxa2xx_pcmcia_attr_read(void *opaque, - hwaddr offset, unsigned size) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - PCMCIACardClass *pcc; - - if (s->slot.attached) { - pcc = PCMCIA_CARD_GET_CLASS(s->card); - return pcc->attr_read(s->card, offset); - } - - return 0; -} - -static void pxa2xx_pcmcia_attr_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - PCMCIACardClass *pcc; - - if (s->slot.attached) { - pcc = PCMCIA_CARD_GET_CLASS(s->card); - pcc->attr_write(s->card, offset, value); - } -} - -static uint64_t pxa2xx_pcmcia_io_read(void *opaque, - hwaddr offset, unsigned size) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - PCMCIACardClass *pcc; - - if (s->slot.attached) { - pcc = PCMCIA_CARD_GET_CLASS(s->card); - return pcc->io_read(s->card, offset); - } - - return 0; -} - -static void pxa2xx_pcmcia_io_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - PCMCIACardClass *pcc; - - if (s->slot.attached) { - pcc = PCMCIA_CARD_GET_CLASS(s->card); - pcc->io_write(s->card, offset, value); - } -} - -static const MemoryRegionOps pxa2xx_pcmcia_common_ops = { - .read = pxa2xx_pcmcia_common_read, - .write = pxa2xx_pcmcia_common_write, - .endianness = DEVICE_NATIVE_ENDIAN -}; - -static const MemoryRegionOps pxa2xx_pcmcia_attr_ops = { - .read = pxa2xx_pcmcia_attr_read, - .write = pxa2xx_pcmcia_attr_write, - .endianness = DEVICE_NATIVE_ENDIAN -}; - -static const MemoryRegionOps pxa2xx_pcmcia_io_ops = { - .read = pxa2xx_pcmcia_io_read, - .write = pxa2xx_pcmcia_io_write, - .endianness = DEVICE_NATIVE_ENDIAN -}; - -static void pxa2xx_pcmcia_set_irq(void *opaque, int line, int level) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - if (!s->irq) - return; - - qemu_set_irq(s->irq, level); -} - -PXA2xxPCMCIAState *pxa2xx_pcmcia_init(MemoryRegion *sysmem, - hwaddr base) -{ - DeviceState *dev; - PXA2xxPCMCIAState *s; - - dev = qdev_create(NULL, TYPE_PXA2XX_PCMCIA); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - s = PXA2XX_PCMCIA(dev); - - qdev_init_nofail(dev); - - return s; -} - -static void pxa2xx_pcmcia_initfn(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - PXA2xxPCMCIAState *s = PXA2XX_PCMCIA(obj); - - memory_region_init(&s->container_mem, obj, "container", 0x10000000); - sysbus_init_mmio(sbd, &s->container_mem); - - /* Socket I/O Memory Space */ - 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); - - /* Then next 64 MB is reserved */ - - /* Socket Attribute Memory Space */ - 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, obj, &pxa2xx_pcmcia_common_ops, s, - "pxa2xx-pcmcia-common", 0x04000000); - memory_region_add_subregion(&s->container_mem, 0x0c000000, - &s->common_iomem); - - s->slot.irq = qemu_allocate_irq(pxa2xx_pcmcia_set_irq, s, 0); - - object_property_add_link(obj, "card", TYPE_PCMCIA_CARD, - (Object **)&s->card, - NULL, /* read-only property */ - 0, NULL); -} - -/* Insert a new card into a slot */ -int pxa2xx_pcmcia_attach(void *opaque, PCMCIACardState *card) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - PCMCIACardClass *pcc; - - if (s->slot.attached) { - return -EEXIST; - } - - if (s->cd_irq) { - qemu_irq_raise(s->cd_irq); - } - - s->card = card; - pcc = PCMCIA_CARD_GET_CLASS(s->card); - - s->slot.attached = true; - s->card->slot = &s->slot; - pcc->attach(s->card); - - return 0; -} - -/* Eject card from the slot */ -int pxa2xx_pcmcia_detach(void *opaque) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - PCMCIACardClass *pcc; - - if (!s->slot.attached) { - return -ENOENT; - } - - pcc = PCMCIA_CARD_GET_CLASS(s->card); - pcc->detach(s->card); - s->card->slot = NULL; - s->card = NULL; - - s->slot.attached = false; - - if (s->irq) { - qemu_irq_lower(s->irq); - } - if (s->cd_irq) { - qemu_irq_lower(s->cd_irq); - } - - return 0; -} - -/* Who to notify on card events */ -void pxa2xx_pcmcia_set_irq_cb(void *opaque, qemu_irq irq, qemu_irq cd_irq) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - s->irq = irq; - s->cd_irq = cd_irq; -} - -static const TypeInfo pxa2xx_pcmcia_type_info = { - .name = TYPE_PXA2XX_PCMCIA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxPCMCIAState), - .instance_init = pxa2xx_pcmcia_initfn, -}; - -static void pxa2xx_pcmcia_register_types(void) -{ - type_register_static(&pxa2xx_pcmcia_type_info); -} - -type_init(pxa2xx_pcmcia_register_types) diff --git a/qemu/hw/ppc/Makefile.objs b/qemu/hw/ppc/Makefile.objs deleted file mode 100644 index c1ffc7771..000000000 --- a/qemu/hw/ppc/Makefile.objs +++ /dev/null @@ -1,23 +0,0 @@ -# shared objects -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 spapr_rng.o -ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) -obj-y += spapr_pci_vfio.o -endif -# PowerPC 4xx boards -obj-y += ppc405_boards.o ppc4xx_devs.o ppc405_uc.o ppc440_bamboo.o -obj-y += ppc4xx_pci.o -# PReP -obj-$(CONFIG_PREP) += prep.o -# OldWorld PowerMac -obj-$(CONFIG_MAC) += mac_oldworld.o -# NewWorld PowerMac -obj-$(CONFIG_MAC) += mac_newworld.o -# e500 -obj-$(CONFIG_E500) += e500.o mpc8544ds.o e500plat.o -obj-$(CONFIG_E500) += mpc8544_guts.o ppce500_spin.o -# PowerPC 440 Xilinx ML507 reference board. -obj-$(CONFIG_XILINX) += virtex_ml507.o diff --git a/qemu/hw/ppc/e500-ccsr.h b/qemu/hw/ppc/e500-ccsr.h deleted file mode 100644 index 12a2ba4b9..000000000 --- a/qemu/hw/ppc/e500-ccsr.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef E500_CCSR_H -#define E500_CCSR_H - -#include "hw/sysbus.h" - -typedef struct PPCE500CCSRState { - /*< private >*/ - SysBusDevice parent; - /*< public >*/ - - MemoryRegion ccsr_space; -} PPCE500CCSRState; - -#define TYPE_CCSR "e500-ccsr" -#define CCSR(obj) OBJECT_CHECK(PPCE500CCSRState, (obj), TYPE_CCSR) - -#endif /* E500_CCSR_H */ diff --git a/qemu/hw/ppc/e500.c b/qemu/hw/ppc/e500.c deleted file mode 100644 index ee1c60b82..000000000 --- a/qemu/hw/ppc/e500.c +++ /dev/null @@ -1,1082 +0,0 @@ -/* - * QEMU PowerPC e500-based platforms - * - * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved. - * - * Author: Yu Liu, - * - * This file is derived from hw/ppc440_bamboo.c, - * the copyright for that material belongs to the original owners. - * - * This 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. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "e500.h" -#include "e500-ccsr.h" -#include "net/net.h" -#include "qemu/config-file.h" -#include "hw/hw.h" -#include "hw/char/serial.h" -#include "hw/pci/pci.h" -#include "hw/boards.h" -#include "sysemu/sysemu.h" -#include "sysemu/kvm.h" -#include "kvm_ppc.h" -#include "sysemu/device_tree.h" -#include "hw/ppc/openpic.h" -#include "hw/ppc/ppc.h" -#include "hw/loader.h" -#include "elf.h" -#include "hw/sysbus.h" -#include "exec/address-spaces.h" -#include "qemu/host-utils.h" -#include "hw/pci-host/ppce500.h" -#include "qemu/error-report.h" -#include "hw/platform-bus.h" -#include "hw/net/fsl_etsec/etsec.h" - -#define EPAPR_MAGIC (0x45504150) -#define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb" -#define DTC_LOAD_PAD 0x1800000 -#define DTC_PAD_MASK 0xFFFFF -#define DTB_MAX_SIZE (8 * 1024 * 1024) -#define INITRD_LOAD_PAD 0x2000000 -#define INITRD_PAD_MASK 0xFFFFFF - -#define RAM_SIZES_ALIGN (64UL << 20) - -/* TODO: parameterize */ -#define MPC8544_CCSRBAR_SIZE 0x00100000ULL -#define MPC8544_MPIC_REGS_OFFSET 0x40000ULL -#define MPC8544_MSI_REGS_OFFSET 0x41600ULL -#define MPC8544_SERIAL0_REGS_OFFSET 0x4500ULL -#define MPC8544_SERIAL1_REGS_OFFSET 0x4600ULL -#define MPC8544_PCI_REGS_OFFSET 0x8000ULL -#define MPC8544_PCI_REGS_SIZE 0x1000ULL -#define MPC8544_UTIL_OFFSET 0xe0000ULL -#define MPC8XXX_GPIO_OFFSET 0x000FF000ULL -#define MPC8XXX_GPIO_IRQ 47 - -struct boot_info -{ - uint32_t dt_base; - uint32_t dt_size; - uint32_t entry; -}; - -static uint32_t *pci_map_create(void *fdt, uint32_t mpic, int first_slot, - int nr_slots, int *len) -{ - int i = 0; - int slot; - int pci_irq; - int host_irq; - int last_slot = first_slot + nr_slots; - uint32_t *pci_map; - - *len = nr_slots * 4 * 7 * sizeof(uint32_t); - pci_map = g_malloc(*len); - - for (slot = first_slot; slot < last_slot; slot++) { - for (pci_irq = 0; pci_irq < 4; pci_irq++) { - pci_map[i++] = cpu_to_be32(slot << 11); - pci_map[i++] = cpu_to_be32(0x0); - pci_map[i++] = cpu_to_be32(0x0); - pci_map[i++] = cpu_to_be32(pci_irq + 1); - pci_map[i++] = cpu_to_be32(mpic); - host_irq = ppce500_pci_map_irq_slot(slot, pci_irq); - pci_map[i++] = cpu_to_be32(host_irq + 1); - pci_map[i++] = cpu_to_be32(0x1); - } - } - - assert((i * sizeof(uint32_t)) == *len); - - return pci_map; -} - -static void dt_serial_create(void *fdt, unsigned long long offset, - const char *soc, const char *mpic, - const char *alias, int idx, bool defcon) -{ - char ser[128]; - - snprintf(ser, sizeof(ser), "%s/serial@%llx", soc, offset); - qemu_fdt_add_subnode(fdt, ser); - qemu_fdt_setprop_string(fdt, ser, "device_type", "serial"); - qemu_fdt_setprop_string(fdt, ser, "compatible", "ns16550"); - qemu_fdt_setprop_cells(fdt, ser, "reg", offset, 0x100); - qemu_fdt_setprop_cell(fdt, ser, "cell-index", idx); - qemu_fdt_setprop_cell(fdt, ser, "clock-frequency", 0); - qemu_fdt_setprop_cells(fdt, ser, "interrupts", 42, 2); - qemu_fdt_setprop_phandle(fdt, ser, "interrupt-parent", mpic); - qemu_fdt_setprop_string(fdt, "/aliases", alias, ser); - - if (defcon) { - qemu_fdt_setprop_string(fdt, "/chosen", "linux,stdout-path", ser); - } -} - -static void create_dt_mpc8xxx_gpio(void *fdt, const char *soc, const char *mpic) -{ - hwaddr mmio0 = MPC8XXX_GPIO_OFFSET; - int irq0 = MPC8XXX_GPIO_IRQ; - gchar *node = g_strdup_printf("%s/gpio@%"PRIx64, soc, mmio0); - gchar *poweroff = g_strdup_printf("%s/power-off", soc); - int gpio_ph; - - qemu_fdt_add_subnode(fdt, node); - qemu_fdt_setprop_string(fdt, node, "compatible", "fsl,qoriq-gpio"); - qemu_fdt_setprop_cells(fdt, node, "reg", mmio0, 0x1000); - qemu_fdt_setprop_cells(fdt, node, "interrupts", irq0, 0x2); - qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", mpic); - qemu_fdt_setprop_cells(fdt, node, "#gpio-cells", 2); - qemu_fdt_setprop(fdt, node, "gpio-controller", NULL, 0); - gpio_ph = qemu_fdt_alloc_phandle(fdt); - qemu_fdt_setprop_cell(fdt, node, "phandle", gpio_ph); - qemu_fdt_setprop_cell(fdt, node, "linux,phandle", gpio_ph); - - /* Power Off Pin */ - qemu_fdt_add_subnode(fdt, poweroff); - qemu_fdt_setprop_string(fdt, poweroff, "compatible", "gpio-poweroff"); - qemu_fdt_setprop_cells(fdt, poweroff, "gpios", gpio_ph, 0, 0); - - g_free(node); - g_free(poweroff); -} - -typedef struct PlatformDevtreeData { - void *fdt; - const char *mpic; - int irq_start; - const char *node; - PlatformBusDevice *pbus; -} PlatformDevtreeData; - -static int create_devtree_etsec(SysBusDevice *sbdev, PlatformDevtreeData *data) -{ - eTSEC *etsec = ETSEC_COMMON(sbdev); - PlatformBusDevice *pbus = data->pbus; - hwaddr mmio0 = platform_bus_get_mmio_addr(pbus, sbdev, 0); - int irq0 = platform_bus_get_irqn(pbus, sbdev, 0); - int irq1 = platform_bus_get_irqn(pbus, sbdev, 1); - int irq2 = platform_bus_get_irqn(pbus, sbdev, 2); - gchar *node = g_strdup_printf("/platform/ethernet@%"PRIx64, mmio0); - gchar *group = g_strdup_printf("%s/queue-group", node); - void *fdt = data->fdt; - - assert((int64_t)mmio0 >= 0); - assert(irq0 >= 0); - assert(irq1 >= 0); - assert(irq2 >= 0); - - qemu_fdt_add_subnode(fdt, node); - qemu_fdt_setprop_string(fdt, node, "device_type", "network"); - qemu_fdt_setprop_string(fdt, node, "compatible", "fsl,etsec2"); - qemu_fdt_setprop_string(fdt, node, "model", "eTSEC"); - qemu_fdt_setprop(fdt, node, "local-mac-address", etsec->conf.macaddr.a, 6); - qemu_fdt_setprop_cells(fdt, node, "fixed-link", 0, 1, 1000, 0, 0); - - qemu_fdt_add_subnode(fdt, group); - qemu_fdt_setprop_cells(fdt, group, "reg", mmio0, 0x1000); - qemu_fdt_setprop_cells(fdt, group, "interrupts", - data->irq_start + irq0, 0x2, - data->irq_start + irq1, 0x2, - data->irq_start + irq2, 0x2); - - g_free(node); - g_free(group); - - return 0; -} - -static int sysbus_device_create_devtree(SysBusDevice *sbdev, void *opaque) -{ - PlatformDevtreeData *data = opaque; - bool matched = false; - - if (object_dynamic_cast(OBJECT(sbdev), TYPE_ETSEC_COMMON)) { - create_devtree_etsec(sbdev, data); - matched = true; - } - - if (!matched) { - error_report("Device %s is not supported by this machine yet.", - qdev_fw_name(DEVICE(sbdev))); - exit(1); - } - - return 0; -} - -static void platform_bus_create_devtree(PPCE500Params *params, void *fdt, - const char *mpic) -{ - gchar *node = g_strdup_printf("/platform@%"PRIx64, params->platform_bus_base); - const char platcomp[] = "qemu,platform\0simple-bus"; - uint64_t addr = params->platform_bus_base; - uint64_t size = params->platform_bus_size; - int irq_start = params->platform_bus_first_irq; - PlatformBusDevice *pbus; - DeviceState *dev; - - /* Create a /platform node that we can put all devices into */ - - qemu_fdt_add_subnode(fdt, node); - qemu_fdt_setprop(fdt, node, "compatible", platcomp, sizeof(platcomp)); - - /* Our platform bus region is less than 32bit big, so 1 cell is enough for - address and size */ - qemu_fdt_setprop_cells(fdt, node, "#size-cells", 1); - qemu_fdt_setprop_cells(fdt, node, "#address-cells", 1); - qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, size); - - qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", mpic); - - dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE); - pbus = PLATFORM_BUS_DEVICE(dev); - - /* We can only create dt nodes for dynamic devices when they're ready */ - if (pbus->done_gathering) { - PlatformDevtreeData data = { - .fdt = fdt, - .mpic = mpic, - .irq_start = irq_start, - .node = node, - .pbus = pbus, - }; - - /* Loop through all dynamic sysbus devices and create nodes for them */ - foreach_dynamic_sysbus_device(sysbus_device_create_devtree, &data); - } - - g_free(node); -} - -static int ppce500_load_device_tree(MachineState *machine, - PPCE500Params *params, - hwaddr addr, - hwaddr initrd_base, - hwaddr initrd_size, - hwaddr kernel_base, - hwaddr kernel_size, - bool dry_run) -{ - CPUPPCState *env = first_cpu->env_ptr; - int ret = -1; - uint64_t mem_reg_property[] = { 0, cpu_to_be64(machine->ram_size) }; - int fdt_size; - void *fdt; - uint8_t hypercall[16]; - uint32_t clock_freq = 400000000; - uint32_t tb_freq = 400000000; - int i; - char compatible_sb[] = "fsl,mpc8544-immr\0simple-bus"; - char soc[128]; - char mpic[128]; - uint32_t mpic_ph; - uint32_t msi_ph; - char gutil[128]; - char pci[128]; - char msi[128]; - uint32_t *pci_map = NULL; - int len; - uint32_t pci_ranges[14] = - { - 0x2000000, 0x0, params->pci_mmio_bus_base, - params->pci_mmio_base >> 32, params->pci_mmio_base, - 0x0, 0x20000000, - - 0x1000000, 0x0, 0x0, - params->pci_pio_base >> 32, params->pci_pio_base, - 0x0, 0x10000, - }; - QemuOpts *machine_opts = qemu_get_machine_opts(); - const char *dtb_file = qemu_opt_get(machine_opts, "dtb"); - const char *toplevel_compat = qemu_opt_get(machine_opts, "dt_compatible"); - - if (dtb_file) { - char *filename; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_file); - if (!filename) { - goto out; - } - - fdt = load_device_tree(filename, &fdt_size); - g_free(filename); - if (!fdt) { - goto out; - } - goto done; - } - - fdt = create_device_tree(&fdt_size); - if (fdt == NULL) { - goto out; - } - - /* Manipulate device tree in memory. */ - qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 2); - qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 2); - - qemu_fdt_add_subnode(fdt, "/memory"); - qemu_fdt_setprop_string(fdt, "/memory", "device_type", "memory"); - qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property, - sizeof(mem_reg_property)); - - qemu_fdt_add_subnode(fdt, "/chosen"); - if (initrd_size) { - ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", - initrd_base); - if (ret < 0) { - fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); - } - - ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", - (initrd_base + initrd_size)); - if (ret < 0) { - fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); - } - - } - - if (kernel_base != -1ULL) { - qemu_fdt_setprop_cells(fdt, "/chosen", "qemu,boot-kernel", - kernel_base >> 32, kernel_base, - kernel_size >> 32, kernel_size); - } - - ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", - machine->kernel_cmdline); - if (ret < 0) - fprintf(stderr, "couldn't set /chosen/bootargs\n"); - - if (kvm_enabled()) { - /* Read out host's frequencies */ - clock_freq = kvmppc_get_clockfreq(); - tb_freq = kvmppc_get_tbfreq(); - - /* indicate KVM hypercall interface */ - qemu_fdt_add_subnode(fdt, "/hypervisor"); - qemu_fdt_setprop_string(fdt, "/hypervisor", "compatible", - "linux,kvm"); - kvmppc_get_hypercall(env, hypercall, sizeof(hypercall)); - qemu_fdt_setprop(fdt, "/hypervisor", "hcall-instructions", - hypercall, sizeof(hypercall)); - /* if KVM supports the idle hcall, set property indicating this */ - if (kvmppc_get_hasidle(env)) { - qemu_fdt_setprop(fdt, "/hypervisor", "has-idle", NULL, 0); - } - } - - /* Create CPU nodes */ - qemu_fdt_add_subnode(fdt, "/cpus"); - qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 1); - qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0); - - /* We need to generate the cpu nodes in reverse order, so Linux can pick - the first node as boot node and be happy */ - for (i = smp_cpus - 1; i >= 0; i--) { - CPUState *cpu; - PowerPCCPU *pcpu; - char cpu_name[128]; - uint64_t cpu_release_addr = params->spin_base + (i * 0x20); - - cpu = qemu_get_cpu(i); - if (cpu == NULL) { - continue; - } - env = cpu->env_ptr; - pcpu = POWERPC_CPU(cpu); - - snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", - ppc_get_vcpu_dt_id(pcpu)); - qemu_fdt_add_subnode(fdt, cpu_name); - qemu_fdt_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); - qemu_fdt_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); - qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu"); - qemu_fdt_setprop_cell(fdt, cpu_name, "reg", - ppc_get_vcpu_dt_id(pcpu)); - qemu_fdt_setprop_cell(fdt, cpu_name, "d-cache-line-size", - env->dcache_line_size); - qemu_fdt_setprop_cell(fdt, cpu_name, "i-cache-line-size", - env->icache_line_size); - qemu_fdt_setprop_cell(fdt, cpu_name, "d-cache-size", 0x8000); - qemu_fdt_setprop_cell(fdt, cpu_name, "i-cache-size", 0x8000); - qemu_fdt_setprop_cell(fdt, cpu_name, "bus-frequency", 0); - if (cpu->cpu_index) { - qemu_fdt_setprop_string(fdt, cpu_name, "status", "disabled"); - qemu_fdt_setprop_string(fdt, cpu_name, "enable-method", - "spin-table"); - qemu_fdt_setprop_u64(fdt, cpu_name, "cpu-release-addr", - cpu_release_addr); - } else { - qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay"); - } - } - - qemu_fdt_add_subnode(fdt, "/aliases"); - /* XXX These should go into their respective devices' code */ - snprintf(soc, sizeof(soc), "/soc@%"PRIx64, params->ccsrbar_base); - qemu_fdt_add_subnode(fdt, soc); - qemu_fdt_setprop_string(fdt, soc, "device_type", "soc"); - qemu_fdt_setprop(fdt, soc, "compatible", compatible_sb, - sizeof(compatible_sb)); - qemu_fdt_setprop_cell(fdt, soc, "#address-cells", 1); - qemu_fdt_setprop_cell(fdt, soc, "#size-cells", 1); - qemu_fdt_setprop_cells(fdt, soc, "ranges", 0x0, - params->ccsrbar_base >> 32, params->ccsrbar_base, - MPC8544_CCSRBAR_SIZE); - /* XXX should contain a reasonable value */ - qemu_fdt_setprop_cell(fdt, soc, "bus-frequency", 0); - - snprintf(mpic, sizeof(mpic), "%s/pic@%llx", soc, MPC8544_MPIC_REGS_OFFSET); - qemu_fdt_add_subnode(fdt, mpic); - qemu_fdt_setprop_string(fdt, mpic, "device_type", "open-pic"); - qemu_fdt_setprop_string(fdt, mpic, "compatible", "fsl,mpic"); - qemu_fdt_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_OFFSET, - 0x40000); - qemu_fdt_setprop_cell(fdt, mpic, "#address-cells", 0); - qemu_fdt_setprop_cell(fdt, mpic, "#interrupt-cells", 2); - mpic_ph = qemu_fdt_alloc_phandle(fdt); - qemu_fdt_setprop_cell(fdt, mpic, "phandle", mpic_ph); - qemu_fdt_setprop_cell(fdt, mpic, "linux,phandle", mpic_ph); - qemu_fdt_setprop(fdt, mpic, "interrupt-controller", NULL, 0); - - /* - * We have to generate ser1 first, because Linux takes the first - * device it finds in the dt as serial output device. And we generate - * devices in reverse order to the dt. - */ - if (serial_hds[1]) { - dt_serial_create(fdt, MPC8544_SERIAL1_REGS_OFFSET, - soc, mpic, "serial1", 1, false); - } - - if (serial_hds[0]) { - dt_serial_create(fdt, MPC8544_SERIAL0_REGS_OFFSET, - soc, mpic, "serial0", 0, true); - } - - snprintf(gutil, sizeof(gutil), "%s/global-utilities@%llx", soc, - MPC8544_UTIL_OFFSET); - qemu_fdt_add_subnode(fdt, gutil); - qemu_fdt_setprop_string(fdt, gutil, "compatible", "fsl,mpc8544-guts"); - qemu_fdt_setprop_cells(fdt, gutil, "reg", MPC8544_UTIL_OFFSET, 0x1000); - qemu_fdt_setprop(fdt, gutil, "fsl,has-rstcr", NULL, 0); - - snprintf(msi, sizeof(msi), "/%s/msi@%llx", soc, MPC8544_MSI_REGS_OFFSET); - qemu_fdt_add_subnode(fdt, msi); - qemu_fdt_setprop_string(fdt, msi, "compatible", "fsl,mpic-msi"); - qemu_fdt_setprop_cells(fdt, msi, "reg", MPC8544_MSI_REGS_OFFSET, 0x200); - msi_ph = qemu_fdt_alloc_phandle(fdt); - qemu_fdt_setprop_cells(fdt, msi, "msi-available-ranges", 0x0, 0x100); - qemu_fdt_setprop_phandle(fdt, msi, "interrupt-parent", mpic); - qemu_fdt_setprop_cells(fdt, msi, "interrupts", - 0xe0, 0x0, - 0xe1, 0x0, - 0xe2, 0x0, - 0xe3, 0x0, - 0xe4, 0x0, - 0xe5, 0x0, - 0xe6, 0x0, - 0xe7, 0x0); - qemu_fdt_setprop_cell(fdt, msi, "phandle", msi_ph); - qemu_fdt_setprop_cell(fdt, msi, "linux,phandle", msi_ph); - - snprintf(pci, sizeof(pci), "/pci@%llx", - params->ccsrbar_base + MPC8544_PCI_REGS_OFFSET); - qemu_fdt_add_subnode(fdt, pci); - qemu_fdt_setprop_cell(fdt, pci, "cell-index", 0); - qemu_fdt_setprop_string(fdt, pci, "compatible", "fsl,mpc8540-pci"); - qemu_fdt_setprop_string(fdt, pci, "device_type", "pci"); - qemu_fdt_setprop_cells(fdt, pci, "interrupt-map-mask", 0xf800, 0x0, - 0x0, 0x7); - pci_map = pci_map_create(fdt, qemu_fdt_get_phandle(fdt, mpic), - params->pci_first_slot, params->pci_nr_slots, - &len); - qemu_fdt_setprop(fdt, pci, "interrupt-map", pci_map, len); - qemu_fdt_setprop_phandle(fdt, pci, "interrupt-parent", mpic); - qemu_fdt_setprop_cells(fdt, pci, "interrupts", 24, 2); - qemu_fdt_setprop_cells(fdt, pci, "bus-range", 0, 255); - for (i = 0; i < 14; i++) { - pci_ranges[i] = cpu_to_be32(pci_ranges[i]); - } - qemu_fdt_setprop_cell(fdt, pci, "fsl,msi", msi_ph); - qemu_fdt_setprop(fdt, pci, "ranges", pci_ranges, sizeof(pci_ranges)); - qemu_fdt_setprop_cells(fdt, pci, "reg", - (params->ccsrbar_base + MPC8544_PCI_REGS_OFFSET) >> 32, - (params->ccsrbar_base + MPC8544_PCI_REGS_OFFSET), - 0, 0x1000); - qemu_fdt_setprop_cell(fdt, pci, "clock-frequency", 66666666); - qemu_fdt_setprop_cell(fdt, pci, "#interrupt-cells", 1); - qemu_fdt_setprop_cell(fdt, pci, "#size-cells", 2); - qemu_fdt_setprop_cell(fdt, pci, "#address-cells", 3); - qemu_fdt_setprop_string(fdt, "/aliases", "pci0", pci); - - if (params->has_mpc8xxx_gpio) { - create_dt_mpc8xxx_gpio(fdt, soc, mpic); - } - - if (params->has_platform_bus) { - platform_bus_create_devtree(params, fdt, mpic); - } - - params->fixup_devtree(params, fdt); - - if (toplevel_compat) { - qemu_fdt_setprop(fdt, "/", "compatible", toplevel_compat, - strlen(toplevel_compat) + 1); - } - -done: - if (!dry_run) { - qemu_fdt_dumpdtb(fdt, fdt_size); - cpu_physical_memory_write(addr, fdt, fdt_size); - } - ret = fdt_size; - -out: - g_free(pci_map); - - return ret; -} - -typedef struct DeviceTreeParams { - MachineState *machine; - PPCE500Params params; - hwaddr addr; - hwaddr initrd_base; - hwaddr initrd_size; - hwaddr kernel_base; - hwaddr kernel_size; - Notifier notifier; -} DeviceTreeParams; - -static void ppce500_reset_device_tree(void *opaque) -{ - DeviceTreeParams *p = opaque; - ppce500_load_device_tree(p->machine, &p->params, p->addr, p->initrd_base, - p->initrd_size, p->kernel_base, p->kernel_size, - false); -} - -static void ppce500_init_notify(Notifier *notifier, void *data) -{ - DeviceTreeParams *p = container_of(notifier, DeviceTreeParams, notifier); - ppce500_reset_device_tree(p); -} - -static int ppce500_prep_device_tree(MachineState *machine, - PPCE500Params *params, - hwaddr addr, - hwaddr initrd_base, - hwaddr initrd_size, - hwaddr kernel_base, - hwaddr kernel_size) -{ - DeviceTreeParams *p = g_new(DeviceTreeParams, 1); - p->machine = machine; - p->params = *params; - p->addr = addr; - p->initrd_base = initrd_base; - p->initrd_size = initrd_size; - p->kernel_base = kernel_base; - p->kernel_size = kernel_size; - - qemu_register_reset(ppce500_reset_device_tree, p); - p->notifier.notify = ppce500_init_notify; - qemu_add_machine_init_done_notifier(&p->notifier); - - /* Issue the device tree loader once, so that we get the size of the blob */ - return ppce500_load_device_tree(machine, params, addr, initrd_base, - initrd_size, kernel_base, kernel_size, - true); -} - -/* Create -kernel TLB entries for BookE. */ -static inline hwaddr booke206_page_size_to_tlb(uint64_t size) -{ - return 63 - clz64(size >> 10); -} - -static int booke206_initial_map_tsize(CPUPPCState *env) -{ - struct boot_info *bi = env->load_info; - hwaddr dt_end; - int ps; - - /* Our initial TLB entry needs to cover everything from 0 to - the device tree top */ - dt_end = bi->dt_base + bi->dt_size; - ps = booke206_page_size_to_tlb(dt_end) + 1; - if (ps & 1) { - /* e500v2 can only do even TLB size bits */ - ps++; - } - return ps; -} - -static uint64_t mmubooke_initial_mapsize(CPUPPCState *env) -{ - int tsize; - - tsize = booke206_initial_map_tsize(env); - return (1ULL << 10 << tsize); -} - -static void mmubooke_create_initial_mapping(CPUPPCState *env) -{ - ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 0); - hwaddr size; - int ps; - - ps = booke206_initial_map_tsize(env); - size = (ps << MAS1_TSIZE_SHIFT); - tlb->mas1 = MAS1_VALID | size; - tlb->mas2 = 0; - tlb->mas7_3 = 0; - tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX; - - env->tlb_dirty = true; -} - -static void ppce500_cpu_reset_sec(void *opaque) -{ - PowerPCCPU *cpu = opaque; - CPUState *cs = CPU(cpu); - - cpu_reset(cs); - - /* Secondary CPU starts in halted state for now. Needs to change when - implementing non-kernel boot. */ - cs->halted = 1; - cs->exception_index = EXCP_HLT; -} - -static void ppce500_cpu_reset(void *opaque) -{ - PowerPCCPU *cpu = opaque; - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - struct boot_info *bi = env->load_info; - - cpu_reset(cs); - - /* Set initial guest state. */ - cs->halted = 0; - env->gpr[1] = (16<<20) - 8; - env->gpr[3] = bi->dt_base; - env->gpr[4] = 0; - env->gpr[5] = 0; - env->gpr[6] = EPAPR_MAGIC; - env->gpr[7] = mmubooke_initial_mapsize(env); - env->gpr[8] = 0; - env->gpr[9] = 0; - env->nip = bi->entry; - mmubooke_create_initial_mapping(env); -} - -static DeviceState *ppce500_init_mpic_qemu(PPCE500Params *params, - qemu_irq **irqs) -{ - DeviceState *dev; - SysBusDevice *s; - int i, j, k; - - dev = qdev_create(NULL, TYPE_OPENPIC); - qdev_prop_set_uint32(dev, "model", params->mpic_version); - qdev_prop_set_uint32(dev, "nb_cpus", smp_cpus); - - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - - k = 0; - for (i = 0; i < smp_cpus; i++) { - for (j = 0; j < OPENPIC_OUTPUT_NB; j++) { - sysbus_connect_irq(s, k++, irqs[i][j]); - } - } - - return dev; -} - -static DeviceState *ppce500_init_mpic_kvm(PPCE500Params *params, - qemu_irq **irqs, Error **errp) -{ - Error *err = NULL; - DeviceState *dev; - CPUState *cs; - - dev = qdev_create(NULL, TYPE_KVM_OPENPIC); - qdev_prop_set_uint32(dev, "model", params->mpic_version); - - object_property_set_bool(OBJECT(dev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - object_unparent(OBJECT(dev)); - return NULL; - } - - CPU_FOREACH(cs) { - if (kvm_openpic_connect_vcpu(dev, cs)) { - fprintf(stderr, "%s: failed to connect vcpu to irqchip\n", - __func__); - abort(); - } - } - - return dev; -} - -static qemu_irq *ppce500_init_mpic(MachineState *machine, PPCE500Params *params, - MemoryRegion *ccsr, qemu_irq **irqs) -{ - qemu_irq *mpic; - DeviceState *dev = NULL; - SysBusDevice *s; - int i; - - mpic = g_new0(qemu_irq, 256); - - if (kvm_enabled()) { - Error *err = NULL; - - if (machine_kernel_irqchip_allowed(machine)) { - dev = ppce500_init_mpic_kvm(params, irqs, &err); - } - if (machine_kernel_irqchip_required(machine) && !dev) { - error_reportf_err(err, - "kernel_irqchip requested but unavailable: "); - exit(1); - } - } - - if (!dev) { - dev = ppce500_init_mpic_qemu(params, irqs); - } - - for (i = 0; i < 256; i++) { - mpic[i] = qdev_get_gpio_in(dev, i); - } - - s = SYS_BUS_DEVICE(dev); - memory_region_add_subregion(ccsr, MPC8544_MPIC_REGS_OFFSET, - s->mmio[0].memory); - - return mpic; -} - -static void ppce500_power_off(void *opaque, int line, int on) -{ - if (on) { - qemu_system_shutdown_request(); - } -} - -void ppce500_init(MachineState *machine, PPCE500Params *params) -{ - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - PCIBus *pci_bus; - CPUPPCState *env = NULL; - uint64_t loadaddr; - hwaddr kernel_base = -1LL; - int kernel_size = 0; - hwaddr dt_base = 0; - hwaddr initrd_base = 0; - int initrd_size = 0; - hwaddr cur_base = 0; - char *filename; - hwaddr bios_entry = 0; - target_long bios_size; - struct boot_info *boot_info; - int dt_size; - int i; - /* irq num for pin INTA, INTB, INTC and INTD is 1, 2, 3 and - * 4 respectively */ - unsigned int pci_irq_nrs[PCI_NUM_PINS] = {1, 2, 3, 4}; - qemu_irq **irqs, *mpic; - DeviceState *dev; - CPUPPCState *firstenv = NULL; - MemoryRegion *ccsr_addr_space; - SysBusDevice *s; - PPCE500CCSRState *ccsr; - - /* Setup CPUs */ - if (machine->cpu_model == NULL) { - machine->cpu_model = "e500v2_v30"; - } - - irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); - irqs[0] = g_malloc0(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB); - for (i = 0; i < smp_cpus; i++) { - PowerPCCPU *cpu; - CPUState *cs; - qemu_irq *input; - - cpu = cpu_ppc_init(machine->cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to initialize CPU!\n"); - exit(1); - } - env = &cpu->env; - cs = CPU(cpu); - - if (!firstenv) { - firstenv = env; - } - - irqs[i] = irqs[0] + (i * OPENPIC_OUTPUT_NB); - input = (qemu_irq *)env->irq_inputs; - irqs[i][OPENPIC_OUTPUT_INT] = input[PPCE500_INPUT_INT]; - irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; - env->spr_cb[SPR_BOOKE_PIR].default_value = cs->cpu_index = i; - env->mpic_iack = params->ccsrbar_base + - MPC8544_MPIC_REGS_OFFSET + 0xa0; - - ppc_booke_timers_init(cpu, 400000000, PPC_TIMER_E500); - - /* Register reset handler */ - if (!i) { - /* Primary CPU */ - struct boot_info *boot_info; - boot_info = g_malloc0(sizeof(struct boot_info)); - qemu_register_reset(ppce500_cpu_reset, cpu); - env->load_info = boot_info; - } else { - /* Secondary CPUs */ - qemu_register_reset(ppce500_cpu_reset_sec, cpu); - } - } - - env = firstenv; - - /* Fixup Memory size on a alignment boundary */ - ram_size &= ~(RAM_SIZES_ALIGN - 1); - machine->ram_size = ram_size; - - /* Register Memory */ - memory_region_allocate_system_memory(ram, NULL, "mpc8544ds.ram", ram_size); - memory_region_add_subregion(address_space_mem, 0, ram); - - dev = qdev_create(NULL, "e500-ccsr"); - object_property_add_child(qdev_get_machine(), "e500-ccsr", - OBJECT(dev), NULL); - qdev_init_nofail(dev); - ccsr = CCSR(dev); - ccsr_addr_space = &ccsr->ccsr_space; - memory_region_add_subregion(address_space_mem, params->ccsrbar_base, - ccsr_addr_space); - - mpic = ppce500_init_mpic(machine, params, ccsr_addr_space, irqs); - - /* Serial */ - if (serial_hds[0]) { - serial_mm_init(ccsr_addr_space, MPC8544_SERIAL0_REGS_OFFSET, - 0, mpic[42], 399193, - serial_hds[0], DEVICE_BIG_ENDIAN); - } - - if (serial_hds[1]) { - serial_mm_init(ccsr_addr_space, MPC8544_SERIAL1_REGS_OFFSET, - 0, mpic[42], 399193, - serial_hds[1], DEVICE_BIG_ENDIAN); - } - - /* General Utility device */ - dev = qdev_create(NULL, "mpc8544-guts"); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - memory_region_add_subregion(ccsr_addr_space, MPC8544_UTIL_OFFSET, - sysbus_mmio_get_region(s, 0)); - - /* PCI */ - dev = qdev_create(NULL, "e500-pcihost"); - qdev_prop_set_uint32(dev, "first_slot", params->pci_first_slot); - qdev_prop_set_uint32(dev, "first_pin_irq", pci_irq_nrs[0]); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - for (i = 0; i < PCI_NUM_PINS; i++) { - sysbus_connect_irq(s, i, mpic[pci_irq_nrs[i]]); - } - - memory_region_add_subregion(ccsr_addr_space, MPC8544_PCI_REGS_OFFSET, - sysbus_mmio_get_region(s, 0)); - - pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); - if (!pci_bus) - printf("couldn't create PCI controller!\n"); - - if (pci_bus) { - /* Register network interfaces. */ - for (i = 0; i < nb_nics; i++) { - pci_nic_init_nofail(&nd_table[i], pci_bus, "virtio", NULL); - } - } - - /* Register spinning region */ - sysbus_create_simple("e500-spin", params->spin_base, NULL); - - if (cur_base < (32 * 1024 * 1024)) { - /* u-boot occupies memory up to 32MB, so load blobs above */ - cur_base = (32 * 1024 * 1024); - } - - if (params->has_mpc8xxx_gpio) { - qemu_irq poweroff_irq; - - dev = qdev_create(NULL, "mpc8xxx_gpio"); - s = SYS_BUS_DEVICE(dev); - qdev_init_nofail(dev); - sysbus_connect_irq(s, 0, mpic[MPC8XXX_GPIO_IRQ]); - memory_region_add_subregion(ccsr_addr_space, MPC8XXX_GPIO_OFFSET, - sysbus_mmio_get_region(s, 0)); - - /* Power Off GPIO at Pin 0 */ - poweroff_irq = qemu_allocate_irq(ppce500_power_off, NULL, 0); - qdev_connect_gpio_out(dev, 0, poweroff_irq); - } - - /* Platform Bus Device */ - if (params->has_platform_bus) { - dev = qdev_create(NULL, TYPE_PLATFORM_BUS_DEVICE); - dev->id = TYPE_PLATFORM_BUS_DEVICE; - qdev_prop_set_uint32(dev, "num_irqs", params->platform_bus_num_irqs); - qdev_prop_set_uint32(dev, "mmio_size", params->platform_bus_size); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - - for (i = 0; i < params->platform_bus_num_irqs; i++) { - int irqn = params->platform_bus_first_irq + i; - sysbus_connect_irq(s, i, mpic[irqn]); - } - - memory_region_add_subregion(address_space_mem, - params->platform_bus_base, - sysbus_mmio_get_region(s, 0)); - } - - /* Load kernel. */ - if (machine->kernel_filename) { - kernel_base = cur_base; - kernel_size = load_image_targphys(machine->kernel_filename, - cur_base, - ram_size - cur_base); - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - machine->kernel_filename); - exit(1); - } - - cur_base += kernel_size; - } - - /* Load initrd. */ - if (machine->initrd_filename) { - initrd_base = (cur_base + INITRD_LOAD_PAD) & ~INITRD_PAD_MASK; - initrd_size = load_image_targphys(machine->initrd_filename, initrd_base, - ram_size - initrd_base); - - if (initrd_size < 0) { - fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", - machine->initrd_filename); - exit(1); - } - - cur_base = initrd_base + initrd_size; - } - - /* - * Smart firmware defaults ahead! - * - * We follow the following table to select which payload we execute. - * - * -kernel | -bios | payload - * ---------+-------+--------- - * N | Y | u-boot - * N | N | u-boot - * Y | Y | u-boot - * Y | N | kernel - * - * This ensures backwards compatibility with how we used to expose - * -kernel to users but allows them to run through u-boot as well. - */ - if (bios_name == NULL) { - if (machine->kernel_filename) { - bios_name = machine->kernel_filename; - } else { - bios_name = "u-boot.e500"; - } - } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - - bios_size = load_elf(filename, NULL, NULL, &bios_entry, &loadaddr, NULL, - 1, PPC_ELF_MACHINE, 0, 0); - if (bios_size < 0) { - /* - * Hrm. No ELF image? Try a uImage, maybe someone is giving us an - * ePAPR compliant kernel - */ - kernel_size = load_uimage(filename, &bios_entry, &loadaddr, NULL, - NULL, NULL); - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load firmware '%s'\n", filename); - exit(1); - } - } - g_free(filename); - - /* Reserve space for dtb */ - dt_base = (loadaddr + bios_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; - - dt_size = ppce500_prep_device_tree(machine, params, dt_base, - initrd_base, initrd_size, - kernel_base, kernel_size); - if (dt_size < 0) { - fprintf(stderr, "couldn't load device tree\n"); - exit(1); - } - assert(dt_size < DTB_MAX_SIZE); - - boot_info = env->load_info; - boot_info->entry = bios_entry; - boot_info->dt_base = dt_base; - boot_info->dt_size = dt_size; -} - -static int e500_ccsr_initfn(SysBusDevice *dev) -{ - PPCE500CCSRState *ccsr; - - ccsr = CCSR(dev); - memory_region_init(&ccsr->ccsr_space, OBJECT(ccsr), "e500-ccsr", - MPC8544_CCSRBAR_SIZE); - return 0; -} - -static void e500_ccsr_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = e500_ccsr_initfn; -} - -static const TypeInfo e500_ccsr_info = { - .name = TYPE_CCSR, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PPCE500CCSRState), - .class_init = e500_ccsr_class_init, -}; - -static void e500_register_types(void) -{ - type_register_static(&e500_ccsr_info); -} - -type_init(e500_register_types) diff --git a/qemu/hw/ppc/e500.h b/qemu/hw/ppc/e500.h deleted file mode 100644 index ef224ea5e..000000000 --- a/qemu/hw/ppc/e500.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef PPCE500_H -#define PPCE500_H - -#include "hw/boards.h" - -typedef struct PPCE500Params { - int pci_first_slot; - int pci_nr_slots; - - /* required -- must at least add toplevel board compatible */ - void (*fixup_devtree)(struct PPCE500Params *params, void *fdt); - - int mpic_version; - bool has_mpc8xxx_gpio; - bool has_platform_bus; - hwaddr platform_bus_base; - hwaddr platform_bus_size; - int platform_bus_first_irq; - int platform_bus_num_irqs; - hwaddr ccsrbar_base; - hwaddr pci_pio_base; - hwaddr pci_mmio_base; - hwaddr pci_mmio_bus_base; - hwaddr spin_base; -} PPCE500Params; - -void ppce500_init(MachineState *machine, PPCE500Params *params); - -#endif diff --git a/qemu/hw/ppc/e500plat.c b/qemu/hw/ppc/e500plat.c deleted file mode 100644 index b00565c3d..000000000 --- a/qemu/hw/ppc/e500plat.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Generic device-tree-driven paravirt PPC e500 platform - * - * Copyright 2012 Freescale Semiconductor, Inc. - * - * This 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. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "e500.h" -#include "hw/boards.h" -#include "sysemu/device_tree.h" -#include "hw/pci/pci.h" -#include "hw/ppc/openpic.h" -#include "kvm_ppc.h" - -static void e500plat_fixup_devtree(PPCE500Params *params, void *fdt) -{ - const char model[] = "QEMU ppce500"; - const char compatible[] = "fsl,qemu-e500"; - - qemu_fdt_setprop(fdt, "/", "model", model, sizeof(model)); - qemu_fdt_setprop(fdt, "/", "compatible", compatible, - sizeof(compatible)); -} - -static void e500plat_init(MachineState *machine) -{ - PPCE500Params params = { - .pci_first_slot = 0x1, - .pci_nr_slots = PCI_SLOT_MAX - 1, - .fixup_devtree = e500plat_fixup_devtree, - .mpic_version = OPENPIC_MODEL_FSL_MPIC_42, - .has_mpc8xxx_gpio = true, - .has_platform_bus = true, - .platform_bus_base = 0xf00000000ULL, - .platform_bus_size = (128ULL * 1024 * 1024), - .platform_bus_first_irq = 5, - .platform_bus_num_irqs = 10, - .ccsrbar_base = 0xFE0000000ULL, - .pci_pio_base = 0xFE1000000ULL, - .pci_mmio_base = 0xC00000000ULL, - .pci_mmio_bus_base = 0xE0000000ULL, - .spin_base = 0xFEF000000ULL, - }; - - /* Older KVM versions don't support EPR which breaks guests when we announce - MPIC variants that support EPR. Revert to an older one for those */ - if (kvm_enabled() && !kvmppc_has_cap_epr()) { - params.mpic_version = OPENPIC_MODEL_FSL_MPIC_20; - } - - ppce500_init(machine, ¶ms); -} - -static void e500plat_machine_init(MachineClass *mc) -{ - mc->desc = "generic paravirt e500 platform"; - mc->init = e500plat_init; - mc->max_cpus = 32; - mc->has_dynamic_sysbus = true; -} - -DEFINE_MACHINE("ppce500", e500plat_machine_init) diff --git a/qemu/hw/ppc/mac.h b/qemu/hw/ppc/mac.h deleted file mode 100644 index 5764b86c2..000000000 --- a/qemu/hw/ppc/mac.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * QEMU PowerMac emulation shared definitions and prototypes - * - * Copyright (c) 2004-2007 Fabrice Bellard - * Copyright (c) 2007 Jocelyn Mayer - * - * 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. - */ -#if !defined(__PPC_MAC_H__) -#define __PPC_MAC_H__ - -#include "exec/memory.h" -#include "hw/sysbus.h" -#include "hw/ide/internal.h" -#include "hw/input/adb.h" - -/* SMP is not enabled, for now */ -#define MAX_CPUS 1 - -#define BIOS_SIZE (1024 * 1024) -#define NVRAM_SIZE 0x2000 -#define PROM_FILENAME "openbios-ppc" -#define PROM_ADDR 0xfff00000 - -#define KERNEL_LOAD_ADDR 0x01000000 -#define KERNEL_GAP 0x00100000 - -#define ESCC_CLOCK 3686400 - -/* Cuda */ -#define TYPE_CUDA "cuda" -#define CUDA(obj) OBJECT_CHECK(CUDAState, (obj), TYPE_CUDA) - -/** - * CUDATimer: - * @counter_value: counter value at load time - */ -typedef struct CUDATimer { - int index; - uint16_t latch; - uint16_t counter_value; - int64_t load_time; - int64_t next_irq_time; - uint64_t frequency; - QEMUTimer *timer; -} CUDATimer; - -/** - * CUDAState: - * @b: B-side data - * @a: A-side data - * @dirb: B-side direction (1=output) - * @dira: A-side direction (1=output) - * @sr: Shift register - * @acr: Auxiliary control register - * @pcr: Peripheral control register - * @ifr: Interrupt flag register - * @ier: Interrupt enable register - * @anh: A-side data, no handshake - * @last_b: last value of B register - * @last_acr: last value of ACR register - */ -typedef struct CUDAState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion mem; - /* cuda registers */ - uint8_t b; - uint8_t a; - uint8_t dirb; - uint8_t dira; - uint8_t sr; - uint8_t acr; - uint8_t pcr; - uint8_t ifr; - uint8_t ier; - uint8_t anh; - - ADBBusState adb_bus; - CUDATimer timers[2]; - - uint32_t tick_offset; - uint64_t frequency; - - 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]; - QEMUTimer *adb_poll_timer; -} CUDAState; - -/* MacIO */ -#define TYPE_OLDWORLD_MACIO "macio-oldworld" -#define TYPE_NEWWORLD_MACIO "macio-newworld" - -#define TYPE_MACIO_IDE "macio-ide" -#define MACIO_IDE(obj) OBJECT_CHECK(MACIOIDEState, (obj), TYPE_MACIO_IDE) - -typedef struct MACIOIDEState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - qemu_irq irq; - qemu_irq dma_irq; - - MemoryRegion mem; - IDEBus bus; - IDEDMA dma; - void *dbdma; - bool dma_active; -} MACIOIDEState; - -void macio_ide_init_drives(MACIOIDEState *ide, DriveInfo **hd_table); -void macio_ide_register_dma(MACIOIDEState *ide, void *dbdma, int channel); - -void macio_init(PCIDevice *dev, - MemoryRegion *pic_mem, - MemoryRegion *escc_mem); - -/* Heathrow PIC */ -qemu_irq *heathrow_pic_init(MemoryRegion **pmem, - int nb_cpus, qemu_irq **irqs); - -/* Grackle PCI */ -#define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" -PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io); - -/* UniNorth PCI */ -PCIBus *pci_pmac_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io); -PCIBus *pci_pmac_u3_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io); - -/* Mac NVRAM */ -#define TYPE_MACIO_NVRAM "macio-nvram" -#define MACIO_NVRAM(obj) \ - OBJECT_CHECK(MacIONVRAMState, (obj), TYPE_MACIO_NVRAM) - -typedef struct MacIONVRAMState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - uint32_t size; - uint32_t it_shift; - - MemoryRegion mem; - uint8_t *data; -} MacIONVRAMState; - -void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len); -#endif /* !defined(__PPC_MAC_H__) */ diff --git a/qemu/hw/ppc/mac_newworld.c b/qemu/hw/ppc/mac_newworld.c deleted file mode 100644 index 32e88b378..000000000 --- a/qemu/hw/ppc/mac_newworld.c +++ /dev/null @@ -1,535 +0,0 @@ -/* - * QEMU PowerPC CHRP (currently NewWorld PowerMac) hardware System Emulator - * - * Copyright (c) 2004-2007 Fabrice Bellard - * Copyright (c) 2007 Jocelyn Mayer - * - * 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. - * - * PCI bus layout on a real G5 (U3 based): - * - * 0000:f0:0b.0 Host bridge [0600]: Apple Computer Inc. U3 AGP [106b:004b] - * 0000:f0:10.0 VGA compatible controller [0300]: ATI Technologies Inc RV350 AP [Radeon 9600] [1002:4150] - * 0001:00:00.0 Host bridge [0600]: Apple Computer Inc. CPC945 HT Bridge [106b:004a] - * 0001:00:01.0 PCI bridge [0604]: Advanced Micro Devices [AMD] AMD-8131 PCI-X Bridge [1022:7450] (rev 12) - * 0001:00:02.0 PCI bridge [0604]: Advanced Micro Devices [AMD] AMD-8131 PCI-X Bridge [1022:7450] (rev 12) - * 0001:00:03.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0045] - * 0001:00:04.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0046] - * 0001:00:05.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0047] - * 0001:00:06.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0048] - * 0001:00:07.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0049] - * 0001:01:07.0 Class [ff00]: Apple Computer Inc. K2 KeyLargo Mac/IO [106b:0041] (rev 20) - * 0001:01:08.0 USB Controller [0c03]: Apple Computer Inc. K2 KeyLargo USB [106b:0040] - * 0001:01:09.0 USB Controller [0c03]: Apple Computer Inc. K2 KeyLargo USB [106b:0040] - * 0001:02:0b.0 USB Controller [0c03]: NEC Corporation USB [1033:0035] (rev 43) - * 0001:02:0b.1 USB Controller [0c03]: NEC Corporation USB [1033:0035] (rev 43) - * 0001:02:0b.2 USB Controller [0c03]: NEC Corporation USB 2.0 [1033:00e0] (rev 04) - * 0001:03:0d.0 Class [ff00]: Apple Computer Inc. K2 ATA/100 [106b:0043] - * 0001:03:0e.0 FireWire (IEEE 1394) [0c00]: Apple Computer Inc. K2 FireWire [106b:0042] - * 0001:04:0f.0 Ethernet controller [0200]: Apple Computer Inc. K2 GMAC (Sun GEM) [106b:004c] - * 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" -#include "hw/input/adb.h" -#include "hw/ppc/mac_dbdma.h" -#include "hw/timer/m48t59.h" -#include "hw/pci/pci.h" -#include "net/net.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" -#include "hw/nvram/fw_cfg.h" -#include "hw/char/escc.h" -#include "hw/ppc/openpic.h" -#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 -#define TBFREQ (100UL * 1000UL * 1000UL) -#define CLOCKFREQ (266UL * 1000UL * 1000UL) -#define BUSFREQ (100UL * 1000UL * 1000UL) - -/* debug UniNorth */ -//#define DEBUG_UNIN - -#ifdef DEBUG_UNIN -#define UNIN_DPRINTF(fmt, ...) \ - do { printf("UNIN: " fmt , ## __VA_ARGS__); } while (0) -#else -#define UNIN_DPRINTF(fmt, ...) -#endif - -/* UniN device */ -static void unin_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - UNIN_DPRINTF("write addr " TARGET_FMT_plx " val %"PRIx64"\n", addr, value); - if (addr == 0x0) { - *(int*)opaque = value; - } -} - -static uint64_t unin_read(void *opaque, hwaddr addr, unsigned size) -{ - uint32_t value; - - value = 0; - switch (addr) { - case 0: - value = *(int*)opaque; - } - - UNIN_DPRINTF("readl addr " TARGET_FMT_plx " val %x\n", addr, value); - - return value; -} - -static const MemoryRegionOps unin_ops = { - .read = unin_read, - .write = unin_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void fw_cfg_boot_set(void *opaque, const char *boot_device, - Error **errp) -{ - fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); -} - -static uint64_t translate_kernel_address(void *opaque, uint64_t addr) -{ - return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; -} - -static hwaddr round_page(hwaddr addr) -{ - return (addr + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; -} - -static void ppc_core99_reset(void *opaque) -{ - PowerPCCPU *cpu = opaque; - - cpu_reset(CPU(cpu)); - /* 970 CPUs want to get their initial IP as part of their boot protocol */ - cpu->env.nip = PROM_ADDR + 0x100; -} - -/* PowerPC Mac99 hardware initialisation */ -static void ppc_core99_init(MachineState *machine) -{ - ram_addr_t ram_size = machine->ram_size; - const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; - const char *initrd_filename = machine->initrd_filename; - const char *boot_device = machine->boot_order; - PowerPCCPU *cpu = NULL; - CPUPPCState *env = NULL; - char *filename; - qemu_irq *pic, **openpic_irqs; - MemoryRegion *isa = g_new(MemoryRegion, 1); - MemoryRegion *unin_memory = g_new(MemoryRegion, 1); - MemoryRegion *unin2_memory = g_new(MemoryRegion, 1); - int linux_boot, i, j, k; - MemoryRegion *ram = g_new(MemoryRegion, 1), *bios = g_new(MemoryRegion, 1); - hwaddr kernel_base, initrd_base, cmdline_base = 0; - long kernel_size, initrd_size; - PCIBus *pci_bus; - PCIDevice *macio; - MACIOIDEState *macio_ide; - BusState *adb_bus; - MacIONVRAMState *nvr; - int bios_size; - MemoryRegion *pic_mem, *escc_mem; - MemoryRegion *escc_bar = g_new(MemoryRegion, 1); - int ppc_boot_device; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - void *fw_cfg; - int machine_arch; - SysBusDevice *s; - DeviceState *dev; - int *token = g_new(int, 1); - hwaddr nvram_addr = 0xFFF04000; - uint64_t tbfreq; - - linux_boot = (kernel_filename != NULL); - - /* init CPUs */ - if (machine->cpu_model == NULL) { -#ifdef TARGET_PPC64 - machine->cpu_model = "970fx"; -#else - machine->cpu_model = "G4"; -#endif - } - 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"); - exit(1); - } - env = &cpu->env; - - /* Set time-base frequency to 100 Mhz */ - cpu_ppc_tb_init(env, TBFREQ); - qemu_register_reset(ppc_core99_reset, cpu); - } - - /* allocate RAM */ - memory_region_allocate_system_memory(ram, NULL, "ppc_core99.ram", ram_size); - memory_region_add_subregion(get_system_memory(), 0, ram); - - /* allocate and load BIOS */ - memory_region_init_ram(bios, NULL, "ppc_core99.bios", BIOS_SIZE, - &error_fatal); - vmstate_register_ram_global(bios); - - if (bios_name == NULL) - bios_name = PROM_FILENAME; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - memory_region_set_readonly(bios, true); - memory_region_add_subregion(get_system_memory(), PROM_ADDR, bios); - - /* Load OpenBIOS (ELF) */ - if (filename) { - bios_size = load_elf(filename, NULL, NULL, NULL, - NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); - - g_free(filename); - } else { - bios_size = -1; - } - if (bios_size < 0 || bios_size > BIOS_SIZE) { - error_report("could not load PowerPC bios '%s'", bios_name); - exit(1); - } - - if (linux_boot) { - uint64_t lowaddr = 0; - int bswap_needed; - -#ifdef BSWAP_NEEDED - bswap_needed = 1; -#else - bswap_needed = 0; -#endif - kernel_base = KERNEL_LOAD_ADDR; - - kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - 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, - TARGET_PAGE_SIZE); - if (kernel_size < 0) - kernel_size = load_image_targphys(kernel_filename, - kernel_base, - ram_size - kernel_base); - if (kernel_size < 0) { - error_report("could not load kernel '%s'", kernel_filename); - exit(1); - } - /* load initrd */ - if (initrd_filename) { - initrd_base = round_page(kernel_base + kernel_size + KERNEL_GAP); - initrd_size = load_image_targphys(initrd_filename, initrd_base, - ram_size - initrd_base); - if (initrd_size < 0) { - error_report("could not load initial ram disk '%s'", - initrd_filename); - exit(1); - } - cmdline_base = round_page(initrd_base + initrd_size); - } else { - initrd_base = 0; - initrd_size = 0; - cmdline_base = round_page(kernel_base + kernel_size + KERNEL_GAP); - } - ppc_boot_device = 'm'; - } else { - kernel_base = 0; - kernel_size = 0; - initrd_base = 0; - initrd_size = 0; - ppc_boot_device = '\0'; - /* We consider that NewWorld PowerMac never have any floppy drive - * For now, OHW cannot boot from the network. - */ - for (i = 0; boot_device[i] != '\0'; i++) { - if (boot_device[i] >= 'c' && boot_device[i] <= 'f') { - ppc_boot_device = boot_device[i]; - break; - } - } - if (ppc_boot_device == '\0') { - fprintf(stderr, "No valid boot device for Mac99 machine\n"); - exit(1); - } - } - - /* Register 8 MB of ISA IO space */ - memory_region_init_alias(isa, NULL, "isa_mmio", - get_system_io(), 0, 0x00800000); - memory_region_add_subregion(get_system_memory(), 0xf2000000, isa); - - /* UniN init: XXX should be a real device */ - memory_region_init_io(unin_memory, NULL, &unin_ops, token, "unin", 0x1000); - memory_region_add_subregion(get_system_memory(), 0xf8000000, unin_memory); - - memory_region_init_io(unin2_memory, NULL, &unin_ops, token, "unin", 0x1000); - memory_region_add_subregion(get_system_memory(), 0xf3000000, unin2_memory); - - openpic_irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); - openpic_irqs[0] = - g_malloc0(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB); - for (i = 0; i < smp_cpus; i++) { - /* Mac99 IRQ connection between OpenPIC outputs pins - * and PowerPC input pins - */ - switch (PPC_INPUT(env)) { - case PPC_FLAGS_INPUT_6xx: - openpic_irqs[i] = openpic_irqs[0] + (i * OPENPIC_OUTPUT_NB); - openpic_irqs[i][OPENPIC_OUTPUT_INT] = - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]; - openpic_irqs[i][OPENPIC_OUTPUT_CINT] = - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]; - openpic_irqs[i][OPENPIC_OUTPUT_MCK] = - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_MCP]; - /* Not connected ? */ - openpic_irqs[i][OPENPIC_OUTPUT_DEBUG] = NULL; - /* Check this */ - openpic_irqs[i][OPENPIC_OUTPUT_RESET] = - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_HRESET]; - break; -#if defined(TARGET_PPC64) - case PPC_FLAGS_INPUT_970: - openpic_irqs[i] = openpic_irqs[0] + (i * OPENPIC_OUTPUT_NB); - openpic_irqs[i][OPENPIC_OUTPUT_INT] = - ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_INT]; - openpic_irqs[i][OPENPIC_OUTPUT_CINT] = - ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_INT]; - openpic_irqs[i][OPENPIC_OUTPUT_MCK] = - ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_MCP]; - /* Not connected ? */ - openpic_irqs[i][OPENPIC_OUTPUT_DEBUG] = NULL; - /* Check this */ - openpic_irqs[i][OPENPIC_OUTPUT_RESET] = - ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_HRESET]; - break; -#endif /* defined(TARGET_PPC64) */ - default: - error_report("Bus model not supported on mac99 machine"); - exit(1); - } - } - - pic = g_new0(qemu_irq, 64); - - dev = qdev_create(NULL, TYPE_OPENPIC); - qdev_prop_set_uint32(dev, "model", OPENPIC_MODEL_RAVEN); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - pic_mem = s->mmio[0].memory; - k = 0; - for (i = 0; i < smp_cpus; i++) { - for (j = 0; j < OPENPIC_OUTPUT_NB; j++) { - sysbus_connect_irq(s, k++, openpic_irqs[i][j]); - } - } - - for (i = 0; i < 64; i++) { - pic[i] = qdev_get_gpio_in(dev, i); - } - - if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) { - /* 970 gets a U3 bus */ - pci_bus = pci_pmac_u3_init(pic, get_system_memory(), get_system_io()); - machine_arch = ARCH_MAC99_U3; - } 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(); - } else { - tbfreq = TBFREQ; - } - - /* init basic PC hardware */ - escc_mem = escc_init(0, pic[0x25], pic[0x24], - serial_hds[0], serial_hds[1], ESCC_CLOCK, 4); - memory_region_init_alias(escc_bar, NULL, "escc-bar", - escc_mem, 0, memory_region_size(escc_mem)); - - macio = pci_create(pci_bus, -1, TYPE_NEWWORLD_MACIO); - dev = DEVICE(macio); - qdev_connect_gpio_out(dev, 0, pic[0x19]); /* CUDA */ - qdev_connect_gpio_out(dev, 1, pic[0x0d]); /* IDE */ - qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE DMA */ - qdev_connect_gpio_out(dev, 3, pic[0x0e]); /* IDE */ - qdev_connect_gpio_out(dev, 4, pic[0x03]); /* IDE DMA */ - qdev_prop_set_uint64(dev, "frequency", tbfreq); - macio_init(macio, pic_mem, escc_bar); - - /* We only emulate 2 out of 3 IDE controllers for now */ - ide_drive_get(hd, ARRAY_SIZE(hd)); - - macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), - "ide[0]")); - macio_ide_init_drives(macio_ide, hd); - - macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), - "ide[1]")); - macio_ide_init_drives(macio_ide, &hd[MAX_IDE_DEVS]); - - dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); - adb_bus = qdev_get_child_bus(dev, "adb.0"); - dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); - qdev_init_nofail(dev); - dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); - qdev_init_nofail(dev); - - if (machine->usb) { - pci_create_simple(pci_bus, -1, "pci-ohci"); - - /* U3 needs to use USB for input because Linux doesn't support via-cuda - on PPC64 */ - if (machine_arch == ARCH_MAC99_U3) { - USBBus *usb_bus = usb_bus_find(-1); - - usb_create_simple(usb_bus, "usb-kbd"); - usb_create_simple(usb_bus, "usb-mouse"); - } - } - - pci_vga_init(pci_bus); - - if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8) { - graphic_depth = 15; - } - - for (i = 0; i < nb_nics; i++) { - pci_nic_init_nofail(&nd_table[i], pci_bus, "ne2k_pci", NULL); - } - - /* The NewWorld NVRAM is not located in the MacIO device */ -#ifdef CONFIG_KVM - if (kvm_enabled() && getpagesize() > 4096) { - /* We can't combine read-write and read-only in a single page, so - move the NVRAM out of ROM again for KVM */ - nvram_addr = 0xFFE00000; - } -#endif - dev = qdev_create(NULL, TYPE_MACIO_NVRAM); - qdev_prop_set_uint32(dev, "size", 0x2000); - qdev_prop_set_uint32(dev, "it_shift", 1); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, nvram_addr); - nvr = MACIO_NVRAM(dev); - pmac_format_nvram_partition(nvr, 0x2000); - /* No PCI init: the BIOS will do it */ - - fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2); - fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); - fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); - fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, machine_arch); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); - if (kernel_cmdline) { - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, cmdline_base); - pstrcpy_targphys("cmdline", cmdline_base, TARGET_PAGE_SIZE, kernel_cmdline); - } else { - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); - } - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_base); - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); - fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, ppc_boot_device); - - fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_WIDTH, graphic_width); - fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height); - fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth); - - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled()); - if (kvm_enabled()) { -#ifdef CONFIG_KVM - uint8_t *hypercall; - - hypercall = g_malloc(16); - kvmppc_get_hypercall(env, hypercall, 16); - fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16); - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid()); -#endif - } - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, tbfreq); - /* Mac OS X requires a "known good" clock-frequency value; pass it one. */ - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_CLOCKFREQ, CLOCKFREQ); - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_BUSFREQ, BUSFREQ); - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_NVRAM_ADDR, nvram_addr); - - qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); -} - -static int core99_kvm_type(const char *arg) -{ - /* Always force PR KVM */ - return 2; -} - -static void core99_machine_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "Mac99 based PowerMAC"; - mc->init = ppc_core99_init; - mc->max_cpus = MAX_CPUS; - mc->default_boot_order = "cd"; - mc->kvm_type = core99_kvm_type; -} - -static const TypeInfo core99_machine_info = { - .name = MACHINE_TYPE_NAME("mac99"), - .parent = TYPE_MACHINE, - .class_init = core99_machine_class_init, -}; - -static void mac_machine_register_types(void) -{ - type_register_static(&core99_machine_info); -} - -type_init(mac_machine_register_types) diff --git a/qemu/hw/ppc/mac_oldworld.c b/qemu/hw/ppc/mac_oldworld.c deleted file mode 100644 index a9bb1c27d..000000000 --- a/qemu/hw/ppc/mac_oldworld.c +++ /dev/null @@ -1,379 +0,0 @@ - -/* - * QEMU OldWorld PowerMac (currently ~G3 Beige) hardware System Emulator - * - * Copyright (c) 2004-2007 Fabrice Bellard - * Copyright (c) 2007 Jocelyn Mayer - * - * 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/ppc/ppc.h" -#include "mac.h" -#include "hw/input/adb.h" -#include "hw/timer/m48t59.h" -#include "sysemu/sysemu.h" -#include "net/net.h" -#include "hw/isa/isa.h" -#include "hw/pci/pci.h" -#include "hw/boards.h" -#include "hw/nvram/fw_cfg.h" -#include "hw/char/escc.h" -#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 -#define TBFREQ 16600000UL -#define CLOCKFREQ 266000000UL -#define BUSFREQ 66000000UL - -static void fw_cfg_boot_set(void *opaque, const char *boot_device, - Error **errp) -{ - fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); -} - -static uint64_t translate_kernel_address(void *opaque, uint64_t addr) -{ - return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; -} - -static hwaddr round_page(hwaddr addr) -{ - return (addr + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; -} - -static void ppc_heathrow_reset(void *opaque) -{ - PowerPCCPU *cpu = opaque; - - cpu_reset(CPU(cpu)); -} - -static void ppc_heathrow_init(MachineState *machine) -{ - ram_addr_t ram_size = machine->ram_size; - const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; - const char *initrd_filename = machine->initrd_filename; - const char *boot_device = machine->boot_order; - MemoryRegion *sysmem = get_system_memory(); - PowerPCCPU *cpu = NULL; - CPUPPCState *env = NULL; - char *filename; - qemu_irq *pic, **heathrow_irqs; - int linux_boot, i; - MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *bios = g_new(MemoryRegion, 1); - MemoryRegion *isa = g_new(MemoryRegion, 1); - uint32_t kernel_base, initrd_base, cmdline_base = 0; - int32_t kernel_size, initrd_size; - PCIBus *pci_bus; - PCIDevice *macio; - MACIOIDEState *macio_ide; - DeviceState *dev; - BusState *adb_bus; - int bios_size; - MemoryRegion *pic_mem; - MemoryRegion *escc_mem, *escc_bar = g_new(MemoryRegion, 1); - uint16_t ppc_boot_device; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - void *fw_cfg; - uint64_t tbfreq; - - linux_boot = (kernel_filename != NULL); - - /* init CPUs */ - if (machine->cpu_model == NULL) - machine->cpu_model = "G3"; - 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"); - exit(1); - } - env = &cpu->env; - - /* Set time-base frequency to 16.6 Mhz */ - cpu_ppc_tb_init(env, TBFREQ); - qemu_register_reset(ppc_heathrow_reset, cpu); - } - - /* allocate RAM */ - if (ram_size > (2047 << 20)) { - fprintf(stderr, - "qemu: Too much memory for this machine: %d MB, maximum 2047 MB\n", - ((unsigned int)ram_size / (1 << 20))); - exit(1); - } - - memory_region_allocate_system_memory(ram, NULL, "ppc_heathrow.ram", - ram_size); - memory_region_add_subregion(sysmem, 0, ram); - - /* allocate and load BIOS */ - memory_region_init_ram(bios, NULL, "ppc_heathrow.bios", BIOS_SIZE, - &error_fatal); - vmstate_register_ram_global(bios); - - if (bios_name == NULL) - bios_name = PROM_FILENAME; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - memory_region_set_readonly(bios, true); - memory_region_add_subregion(sysmem, PROM_ADDR, bios); - - /* Load OpenBIOS (ELF) */ - if (filename) { - bios_size = load_elf(filename, 0, NULL, NULL, NULL, NULL, - 1, PPC_ELF_MACHINE, 0, 0); - g_free(filename); - } else { - bios_size = -1; - } - if (bios_size < 0 || bios_size > BIOS_SIZE) { - error_report("could not load PowerPC bios '%s'", bios_name); - exit(1); - } - - if (linux_boot) { - uint64_t lowaddr = 0; - int bswap_needed; - -#ifdef BSWAP_NEEDED - bswap_needed = 1; -#else - bswap_needed = 0; -#endif - kernel_base = KERNEL_LOAD_ADDR; - kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - 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, - TARGET_PAGE_SIZE); - if (kernel_size < 0) - kernel_size = load_image_targphys(kernel_filename, - kernel_base, - ram_size - kernel_base); - if (kernel_size < 0) { - error_report("could not load kernel '%s'", kernel_filename); - exit(1); - } - /* load initrd */ - if (initrd_filename) { - initrd_base = round_page(kernel_base + kernel_size + KERNEL_GAP); - initrd_size = load_image_targphys(initrd_filename, initrd_base, - ram_size - initrd_base); - if (initrd_size < 0) { - error_report("could not load initial ram disk '%s'", - initrd_filename); - exit(1); - } - cmdline_base = round_page(initrd_base + initrd_size); - } else { - initrd_base = 0; - initrd_size = 0; - cmdline_base = round_page(kernel_base + kernel_size + KERNEL_GAP); - } - ppc_boot_device = 'm'; - } else { - kernel_base = 0; - kernel_size = 0; - initrd_base = 0; - initrd_size = 0; - ppc_boot_device = '\0'; - for (i = 0; boot_device[i] != '\0'; i++) { - /* TOFIX: for now, the second IDE channel is not properly - * used by OHW. The Mac floppy disk are not emulated. - * For now, OHW cannot boot from the network. - */ -#if 0 - if (boot_device[i] >= 'a' && boot_device[i] <= 'f') { - ppc_boot_device = boot_device[i]; - break; - } -#else - if (boot_device[i] >= 'c' && boot_device[i] <= 'd') { - ppc_boot_device = boot_device[i]; - break; - } -#endif - } - if (ppc_boot_device == '\0') { - fprintf(stderr, "No valid boot device for G3 Beige machine\n"); - exit(1); - } - } - - /* Register 2 MB of ISA IO space */ - memory_region_init_alias(isa, NULL, "isa_mmio", - get_system_io(), 0, 0x00200000); - memory_region_add_subregion(sysmem, 0xfe000000, isa); - - /* XXX: we register only 1 output pin for heathrow PIC */ - heathrow_irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); - heathrow_irqs[0] = - g_malloc0(smp_cpus * sizeof(qemu_irq) * 1); - /* Connect the heathrow PIC outputs to the 6xx bus */ - for (i = 0; i < smp_cpus; i++) { - switch (PPC_INPUT(env)) { - case PPC_FLAGS_INPUT_6xx: - heathrow_irqs[i] = heathrow_irqs[0] + (i * 1); - heathrow_irqs[i][0] = - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]; - break; - default: - error_report("Bus model not supported on OldWorld Mac machine"); - exit(1); - } - } - - /* Timebase Frequency */ - if (kvm_enabled()) { - tbfreq = kvmppc_get_tbfreq(); - } else { - tbfreq = TBFREQ; - } - - /* init basic PC hardware */ - if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) { - 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, - get_system_memory(), - get_system_io()); - pci_vga_init(pci_bus); - - escc_mem = escc_init(0, pic[0x0f], pic[0x10], serial_hds[0], - serial_hds[1], ESCC_CLOCK, 4); - memory_region_init_alias(escc_bar, NULL, "escc-bar", - escc_mem, 0, memory_region_size(escc_mem)); - - for(i = 0; i < nb_nics; i++) - pci_nic_init_nofail(&nd_table[i], pci_bus, "ne2k_pci", NULL); - - - ide_drive_get(hd, ARRAY_SIZE(hd)); - - macio = pci_create(pci_bus, -1, TYPE_OLDWORLD_MACIO); - dev = DEVICE(macio); - qdev_connect_gpio_out(dev, 0, pic[0x12]); /* CUDA */ - qdev_connect_gpio_out(dev, 1, pic[0x0D]); /* IDE-0 */ - qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE-0 DMA */ - qdev_connect_gpio_out(dev, 3, pic[0x0E]); /* IDE-1 */ - qdev_connect_gpio_out(dev, 4, pic[0x03]); /* IDE-1 DMA */ - qdev_prop_set_uint64(dev, "frequency", tbfreq); - macio_init(macio, pic_mem, escc_bar); - - macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), - "ide[0]")); - macio_ide_init_drives(macio_ide, hd); - - macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), - "ide[1]")); - macio_ide_init_drives(macio_ide, &hd[MAX_IDE_DEVS]); - - dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); - adb_bus = qdev_get_child_bus(dev, "adb.0"); - dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); - qdev_init_nofail(dev); - dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); - qdev_init_nofail(dev); - - if (usb_enabled()) { - pci_create_simple(pci_bus, -1, "pci-ohci"); - } - - if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8) - graphic_depth = 15; - - /* No PCI init: the BIOS will do it */ - - fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2); - fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); - fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); - fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, ARCH_HEATHROW); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); - if (kernel_cmdline) { - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, cmdline_base); - pstrcpy_targphys("cmdline", cmdline_base, TARGET_PAGE_SIZE, kernel_cmdline); - } else { - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); - } - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_base); - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); - fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, ppc_boot_device); - - fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_WIDTH, graphic_width); - fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height); - fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth); - - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled()); - if (kvm_enabled()) { -#ifdef CONFIG_KVM - uint8_t *hypercall; - - hypercall = g_malloc(16); - kvmppc_get_hypercall(env, hypercall, 16); - fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16); - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid()); -#endif - } - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, tbfreq); - /* Mac OS X requires a "known good" clock-frequency value; pass it one. */ - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_CLOCKFREQ, CLOCKFREQ); - fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_BUSFREQ, BUSFREQ); - - qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); -} - -static int heathrow_kvm_type(const char *arg) -{ - /* Always force PR KVM */ - return 2; -} - -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 - mc->is_default = 1; -#endif - /* TOFIX "cad" when Mac floppy is implemented */ - mc->default_boot_order = "cd"; - mc->kvm_type = heathrow_kvm_type; -} - -DEFINE_MACHINE("g3beige", heathrow_machine_init) diff --git a/qemu/hw/ppc/mpc8544_guts.c b/qemu/hw/ppc/mpc8544_guts.c deleted file mode 100644 index ba69178d6..000000000 --- a/qemu/hw/ppc/mpc8544_guts.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * QEMU PowerPC MPC8544 global util pseudo-device - * - * Copyright (C) 2011 Freescale Semiconductor, Inc. All rights reserved. - * - * Author: Alexander Graf, - * - * This 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. - * - * ***************************************************************** - * - * The documentation for this device is noted in the MPC8544 documentation, - * file name "MPC8544ERM.pdf". You can easily find it on the web. - * - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/hw.h" -#include "sysemu/sysemu.h" -#include "hw/sysbus.h" - -#define MPC8544_GUTS_MMIO_SIZE 0x1000 -#define MPC8544_GUTS_RSTCR_RESET 0x02 - -#define MPC8544_GUTS_ADDR_PORPLLSR 0x00 -#define MPC8544_GUTS_ADDR_PORBMSR 0x04 -#define MPC8544_GUTS_ADDR_PORIMPSCR 0x08 -#define MPC8544_GUTS_ADDR_PORDEVSR 0x0C -#define MPC8544_GUTS_ADDR_PORDBGMSR 0x10 -#define MPC8544_GUTS_ADDR_PORDEVSR2 0x14 -#define MPC8544_GUTS_ADDR_GPPORCR 0x20 -#define MPC8544_GUTS_ADDR_GPIOCR 0x30 -#define MPC8544_GUTS_ADDR_GPOUTDR 0x40 -#define MPC8544_GUTS_ADDR_GPINDR 0x50 -#define MPC8544_GUTS_ADDR_PMUXCR 0x60 -#define MPC8544_GUTS_ADDR_DEVDISR 0x70 -#define MPC8544_GUTS_ADDR_POWMGTCSR 0x80 -#define MPC8544_GUTS_ADDR_MCPSUMR 0x90 -#define MPC8544_GUTS_ADDR_RSTRSCR 0x94 -#define MPC8544_GUTS_ADDR_PVR 0xA0 -#define MPC8544_GUTS_ADDR_SVR 0xA4 -#define MPC8544_GUTS_ADDR_RSTCR 0xB0 -#define MPC8544_GUTS_ADDR_IOVSELSR 0xC0 -#define MPC8544_GUTS_ADDR_DDRCSR 0xB20 -#define MPC8544_GUTS_ADDR_DDRCDR 0xB24 -#define MPC8544_GUTS_ADDR_DDRCLKDR 0xB28 -#define MPC8544_GUTS_ADDR_CLKOCR 0xE00 -#define MPC8544_GUTS_ADDR_SRDS1CR1 0xF04 -#define MPC8544_GUTS_ADDR_SRDS2CR1 0xF10 -#define MPC8544_GUTS_ADDR_SRDS2CR3 0xF18 - -#define TYPE_MPC8544_GUTS "mpc8544-guts" -#define MPC8544_GUTS(obj) OBJECT_CHECK(GutsState, (obj), TYPE_MPC8544_GUTS) - -struct GutsState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; -}; - -typedef struct GutsState GutsState; - -static uint64_t mpc8544_guts_read(void *opaque, hwaddr addr, - unsigned size) -{ - uint32_t value = 0; - PowerPCCPU *cpu = POWERPC_CPU(current_cpu); - CPUPPCState *env = &cpu->env; - - addr &= MPC8544_GUTS_MMIO_SIZE - 1; - switch (addr) { - case MPC8544_GUTS_ADDR_PVR: - value = env->spr[SPR_PVR]; - break; - case MPC8544_GUTS_ADDR_SVR: - value = env->spr[SPR_E500_SVR]; - break; - default: - fprintf(stderr, "guts: Unknown register read: %x\n", (int)addr); - break; - } - - return value; -} - -static void mpc8544_guts_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - addr &= MPC8544_GUTS_MMIO_SIZE - 1; - - switch (addr) { - case MPC8544_GUTS_ADDR_RSTCR: - if (value & MPC8544_GUTS_RSTCR_RESET) { - qemu_system_reset_request(); - } - break; - default: - fprintf(stderr, "guts: Unknown register write: %x = %x\n", - (int)addr, (unsigned)value); - break; - } -} - -static const MemoryRegionOps mpc8544_guts_ops = { - .read = mpc8544_guts_read, - .write = mpc8544_guts_write, - .endianness = DEVICE_BIG_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void mpc8544_guts_initfn(Object *obj) -{ - SysBusDevice *d = SYS_BUS_DEVICE(obj); - GutsState *s = MPC8544_GUTS(obj); - - memory_region_init_io(&s->iomem, OBJECT(s), &mpc8544_guts_ops, s, - "mpc8544.guts", MPC8544_GUTS_MMIO_SIZE); - sysbus_init_mmio(d, &s->iomem); -} - -static const TypeInfo mpc8544_guts_info = { - .name = TYPE_MPC8544_GUTS, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(GutsState), - .instance_init = mpc8544_guts_initfn, -}; - -static void mpc8544_guts_register_types(void) -{ - type_register_static(&mpc8544_guts_info); -} - -type_init(mpc8544_guts_register_types) diff --git a/qemu/hw/ppc/mpc8544ds.c b/qemu/hw/ppc/mpc8544ds.c deleted file mode 100644 index 27b828901..000000000 --- a/qemu/hw/ppc/mpc8544ds.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Support for the PPC e500-based mpc8544ds board - * - * Copyright 2012 Freescale Semiconductor, Inc. - * - * This 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. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "e500.h" -#include "hw/boards.h" -#include "sysemu/device_tree.h" -#include "hw/ppc/openpic.h" -#include "qemu/error-report.h" - -static void mpc8544ds_fixup_devtree(PPCE500Params *params, void *fdt) -{ - const char model[] = "MPC8544DS"; - const char compatible[] = "MPC8544DS\0MPC85xxDS"; - - qemu_fdt_setprop(fdt, "/", "model", model, sizeof(model)); - qemu_fdt_setprop(fdt, "/", "compatible", compatible, - sizeof(compatible)); -} - -static void mpc8544ds_init(MachineState *machine) -{ - PPCE500Params params = { - .pci_first_slot = 0x11, - .pci_nr_slots = 2, - .fixup_devtree = mpc8544ds_fixup_devtree, - .mpic_version = OPENPIC_MODEL_FSL_MPIC_20, - .ccsrbar_base = 0xE0000000ULL, - .pci_mmio_base = 0xC0000000ULL, - .pci_mmio_bus_base = 0xC0000000ULL, - .pci_pio_base = 0xE1000000ULL, - .spin_base = 0xEF000000ULL, - }; - - if (machine->ram_size > 0xc0000000) { - error_report("The MPC8544DS board only supports up to 3GB of RAM"); - exit(1); - } - - ppce500_init(machine, ¶ms); -} - - -static void ppce500_machine_init(MachineClass *mc) -{ - mc->desc = "mpc8544ds"; - mc->init = mpc8544ds_init; - mc->max_cpus = 15; -} - -DEFINE_MACHINE("mpc8544ds", ppce500_machine_init) diff --git a/qemu/hw/ppc/ppc.c b/qemu/hw/ppc/ppc.c deleted file mode 100644 index 38ff2e159..000000000 --- a/qemu/hw/ppc/ppc.c +++ /dev/null @@ -1,1345 +0,0 @@ -/* - * QEMU generic PowerPC hardware System Emulator - * - * Copyright (c) 2003-2007 Jocelyn Mayer - * - * 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 "qemu-common.h" -#include "cpu.h" -#include "hw/hw.h" -#include "hw/ppc/ppc.h" -#include "hw/ppc/ppc_e500.h" -#include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "sysemu/cpus.h" -#include "hw/timer/m48t59.h" -#include "qemu/log.h" -#include "qemu/error-report.h" -#include "hw/loader.h" -#include "sysemu/kvm.h" -#include "kvm_ppc.h" -#include "trace.h" - -//#define PPC_DEBUG_IRQ -//#define PPC_DEBUG_TB - -#ifdef PPC_DEBUG_IRQ -# define LOG_IRQ(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__) -#else -# define LOG_IRQ(...) do { } while (0) -#endif - - -#ifdef PPC_DEBUG_TB -# define LOG_TB(...) qemu_log(__VA_ARGS__) -#else -# define LOG_TB(...) do { } while (0) -#endif - -static void cpu_ppc_tb_stop (CPUPPCState *env); -static void cpu_ppc_tb_start (CPUPPCState *env); - -void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level) -{ - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - unsigned int old_pending = env->pending_interrupts; - - if (level) { - env->pending_interrupts |= 1 << n_IRQ; - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - env->pending_interrupts &= ~(1 << n_IRQ); - if (env->pending_interrupts == 0) { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } - } - - if (old_pending != env->pending_interrupts) { -#ifdef CONFIG_KVM - kvmppc_set_interrupt(cpu, n_IRQ, level); -#endif - } - - LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32 - "req %08x\n", __func__, env, n_IRQ, level, - env->pending_interrupts, CPU(cpu)->interrupt_request); -} - -/* PowerPC 6xx / 7xx internal IRQ controller */ -static void ppc6xx_set_irq(void *opaque, int pin, int level) -{ - PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; - int cur_level; - - LOG_IRQ("%s: env %p pin %d level %d\n", __func__, - env, pin, level); - cur_level = (env->irq_input_state >> pin) & 1; - /* Don't generate spurious events */ - if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { - CPUState *cs = CPU(cpu); - - switch (pin) { - case PPC6xx_INPUT_TBEN: - /* Level sensitive - active high */ - LOG_IRQ("%s: %s the time base\n", - __func__, level ? "start" : "stop"); - if (level) { - cpu_ppc_tb_start(env); - } else { - cpu_ppc_tb_stop(env); - } - case PPC6xx_INPUT_INT: - /* Level sensitive - active high */ - LOG_IRQ("%s: set the external IRQ state to %d\n", - __func__, level); - ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); - break; - case PPC6xx_INPUT_SMI: - /* Level sensitive - active high */ - LOG_IRQ("%s: set the SMI IRQ state to %d\n", - __func__, level); - ppc_set_irq(cpu, PPC_INTERRUPT_SMI, level); - break; - case PPC6xx_INPUT_MCP: - /* Negative edge sensitive */ - /* XXX: TODO: actual reaction may depends on HID0 status - * 603/604/740/750: check HID0[EMCP] - */ - if (cur_level == 1 && level == 0) { - LOG_IRQ("%s: raise machine check state\n", - __func__); - ppc_set_irq(cpu, PPC_INTERRUPT_MCK, 1); - } - break; - case PPC6xx_INPUT_CKSTP_IN: - /* Level sensitive - active low */ - /* XXX: TODO: relay the signal to CKSTP_OUT pin */ - /* XXX: Note that the only way to restart the CPU is to reset it */ - if (level) { - LOG_IRQ("%s: stop the CPU\n", __func__); - cs->halted = 1; - } - break; - case PPC6xx_INPUT_HRESET: - /* Level sensitive - active low */ - if (level) { - LOG_IRQ("%s: reset the CPU\n", __func__); - cpu_interrupt(cs, CPU_INTERRUPT_RESET); - } - break; - case PPC6xx_INPUT_SRESET: - LOG_IRQ("%s: set the RESET IRQ state to %d\n", - __func__, level); - ppc_set_irq(cpu, PPC_INTERRUPT_RESET, level); - break; - default: - /* Unknown pin - do nothing */ - LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); - return; - } - if (level) - env->irq_input_state |= 1 << pin; - else - env->irq_input_state &= ~(1 << pin); - } -} - -void ppc6xx_irq_init(CPUPPCState *env) -{ - PowerPCCPU *cpu = ppc_env_get_cpu(env); - - env->irq_inputs = (void **)qemu_allocate_irqs(&ppc6xx_set_irq, cpu, - PPC6xx_INPUT_NB); -} - -#if defined(TARGET_PPC64) -/* PowerPC 970 internal IRQ controller */ -static void ppc970_set_irq(void *opaque, int pin, int level) -{ - PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; - int cur_level; - - LOG_IRQ("%s: env %p pin %d level %d\n", __func__, - env, pin, level); - cur_level = (env->irq_input_state >> pin) & 1; - /* Don't generate spurious events */ - if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { - CPUState *cs = CPU(cpu); - - switch (pin) { - case PPC970_INPUT_INT: - /* Level sensitive - active high */ - LOG_IRQ("%s: set the external IRQ state to %d\n", - __func__, level); - ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); - break; - case PPC970_INPUT_THINT: - /* Level sensitive - active high */ - LOG_IRQ("%s: set the SMI IRQ state to %d\n", __func__, - level); - ppc_set_irq(cpu, PPC_INTERRUPT_THERM, level); - break; - case PPC970_INPUT_MCP: - /* Negative edge sensitive */ - /* XXX: TODO: actual reaction may depends on HID0 status - * 603/604/740/750: check HID0[EMCP] - */ - if (cur_level == 1 && level == 0) { - LOG_IRQ("%s: raise machine check state\n", - __func__); - ppc_set_irq(cpu, PPC_INTERRUPT_MCK, 1); - } - break; - case PPC970_INPUT_CKSTP: - /* Level sensitive - active low */ - /* XXX: TODO: relay the signal to CKSTP_OUT pin */ - if (level) { - LOG_IRQ("%s: stop the CPU\n", __func__); - cs->halted = 1; - } else { - LOG_IRQ("%s: restart the CPU\n", __func__); - cs->halted = 0; - qemu_cpu_kick(cs); - } - break; - case PPC970_INPUT_HRESET: - /* Level sensitive - active low */ - if (level) { - cpu_interrupt(cs, CPU_INTERRUPT_RESET); - } - break; - case PPC970_INPUT_SRESET: - LOG_IRQ("%s: set the RESET IRQ state to %d\n", - __func__, level); - ppc_set_irq(cpu, PPC_INTERRUPT_RESET, level); - break; - case PPC970_INPUT_TBEN: - LOG_IRQ("%s: set the TBEN state to %d\n", __func__, - level); - /* XXX: TODO */ - break; - default: - /* Unknown pin - do nothing */ - LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); - return; - } - if (level) - env->irq_input_state |= 1 << pin; - else - env->irq_input_state &= ~(1 << pin); - } -} - -void ppc970_irq_init(CPUPPCState *env) -{ - PowerPCCPU *cpu = ppc_env_get_cpu(env); - - env->irq_inputs = (void **)qemu_allocate_irqs(&ppc970_set_irq, cpu, - PPC970_INPUT_NB); -} - -/* POWER7 internal IRQ controller */ -static void power7_set_irq(void *opaque, int pin, int level) -{ - PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; - - LOG_IRQ("%s: env %p pin %d level %d\n", __func__, - env, pin, level); - - switch (pin) { - case POWER7_INPUT_INT: - /* Level sensitive - active high */ - LOG_IRQ("%s: set the external IRQ state to %d\n", - __func__, level); - ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); - break; - default: - /* Unknown pin - do nothing */ - LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); - return; - } - if (level) { - env->irq_input_state |= 1 << pin; - } else { - env->irq_input_state &= ~(1 << pin); - } -} - -void ppcPOWER7_irq_init(CPUPPCState *env) -{ - PowerPCCPU *cpu = ppc_env_get_cpu(env); - - env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, cpu, - POWER7_INPUT_NB); -} -#endif /* defined(TARGET_PPC64) */ - -/* PowerPC 40x internal IRQ controller */ -static void ppc40x_set_irq(void *opaque, int pin, int level) -{ - PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; - int cur_level; - - LOG_IRQ("%s: env %p pin %d level %d\n", __func__, - env, pin, level); - cur_level = (env->irq_input_state >> pin) & 1; - /* Don't generate spurious events */ - if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { - CPUState *cs = CPU(cpu); - - switch (pin) { - case PPC40x_INPUT_RESET_SYS: - if (level) { - LOG_IRQ("%s: reset the PowerPC system\n", - __func__); - ppc40x_system_reset(cpu); - } - break; - case PPC40x_INPUT_RESET_CHIP: - if (level) { - LOG_IRQ("%s: reset the PowerPC chip\n", __func__); - ppc40x_chip_reset(cpu); - } - break; - case PPC40x_INPUT_RESET_CORE: - /* XXX: TODO: update DBSR[MRR] */ - if (level) { - LOG_IRQ("%s: reset the PowerPC core\n", __func__); - ppc40x_core_reset(cpu); - } - break; - case PPC40x_INPUT_CINT: - /* Level sensitive - active high */ - LOG_IRQ("%s: set the critical IRQ state to %d\n", - __func__, level); - ppc_set_irq(cpu, PPC_INTERRUPT_CEXT, level); - break; - case PPC40x_INPUT_INT: - /* Level sensitive - active high */ - LOG_IRQ("%s: set the external IRQ state to %d\n", - __func__, level); - ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); - break; - case PPC40x_INPUT_HALT: - /* Level sensitive - active low */ - if (level) { - LOG_IRQ("%s: stop the CPU\n", __func__); - cs->halted = 1; - } else { - LOG_IRQ("%s: restart the CPU\n", __func__); - cs->halted = 0; - qemu_cpu_kick(cs); - } - break; - case PPC40x_INPUT_DEBUG: - /* Level sensitive - active high */ - LOG_IRQ("%s: set the debug pin state to %d\n", - __func__, level); - ppc_set_irq(cpu, PPC_INTERRUPT_DEBUG, level); - break; - default: - /* Unknown pin - do nothing */ - LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); - return; - } - if (level) - env->irq_input_state |= 1 << pin; - else - env->irq_input_state &= ~(1 << pin); - } -} - -void ppc40x_irq_init(CPUPPCState *env) -{ - PowerPCCPU *cpu = ppc_env_get_cpu(env); - - env->irq_inputs = (void **)qemu_allocate_irqs(&ppc40x_set_irq, - cpu, PPC40x_INPUT_NB); -} - -/* PowerPC E500 internal IRQ controller */ -static void ppce500_set_irq(void *opaque, int pin, int level) -{ - PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; - int cur_level; - - LOG_IRQ("%s: env %p pin %d level %d\n", __func__, - env, pin, level); - cur_level = (env->irq_input_state >> pin) & 1; - /* Don't generate spurious events */ - if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { - switch (pin) { - case PPCE500_INPUT_MCK: - if (level) { - LOG_IRQ("%s: reset the PowerPC system\n", - __func__); - qemu_system_reset_request(); - } - break; - case PPCE500_INPUT_RESET_CORE: - if (level) { - LOG_IRQ("%s: reset the PowerPC core\n", __func__); - ppc_set_irq(cpu, PPC_INTERRUPT_MCK, level); - } - break; - case PPCE500_INPUT_CINT: - /* Level sensitive - active high */ - LOG_IRQ("%s: set the critical IRQ state to %d\n", - __func__, level); - ppc_set_irq(cpu, PPC_INTERRUPT_CEXT, level); - break; - case PPCE500_INPUT_INT: - /* Level sensitive - active high */ - LOG_IRQ("%s: set the core IRQ state to %d\n", - __func__, level); - ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); - break; - case PPCE500_INPUT_DEBUG: - /* Level sensitive - active high */ - LOG_IRQ("%s: set the debug pin state to %d\n", - __func__, level); - ppc_set_irq(cpu, PPC_INTERRUPT_DEBUG, level); - break; - default: - /* Unknown pin - do nothing */ - LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); - return; - } - if (level) - env->irq_input_state |= 1 << pin; - else - env->irq_input_state &= ~(1 << pin); - } -} - -void ppce500_irq_init(CPUPPCState *env) -{ - PowerPCCPU *cpu = ppc_env_get_cpu(env); - - env->irq_inputs = (void **)qemu_allocate_irqs(&ppce500_set_irq, - cpu, PPCE500_INPUT_NB); -} - -/* Enable or Disable the E500 EPR capability */ -void ppce500_set_mpic_proxy(bool enabled) -{ - CPUState *cs; - - CPU_FOREACH(cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - - cpu->env.mpic_proxy = enabled; - if (kvm_enabled()) { - kvmppc_set_mpic_proxy(cpu, enabled); - } - } -} - -/*****************************************************************************/ -/* PowerPC time base and decrementer emulation */ - -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, NANOSECONDS_PER_SECOND) + tb_offset; -} - -uint64_t cpu_ppc_load_tbl (CPUPPCState *env) -{ - ppc_tb_t *tb_env = env->tb_env; - uint64_t tb; - - if (kvm_enabled()) { - return env->spr[SPR_TBL]; - } - - tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); - LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); - - return tb; -} - -static inline uint32_t _cpu_ppc_load_tbu(CPUPPCState *env) -{ - ppc_tb_t *tb_env = env->tb_env; - uint64_t tb; - - tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); - LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); - - return tb >> 32; -} - -uint32_t cpu_ppc_load_tbu (CPUPPCState *env) -{ - if (kvm_enabled()) { - return env->spr[SPR_TBU]; - } - - return _cpu_ppc_load_tbu(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, NANOSECONDS_PER_SECOND); - - LOG_TB("%s: tb %016" PRIx64 " offset %08" PRIx64 "\n", - __func__, value, *tb_offsetp); -} - -void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value) -{ - ppc_tb_t *tb_env = env->tb_env; - uint64_t tb; - - tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); - tb &= 0xFFFFFFFF00000000ULL; - cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - &tb_env->tb_offset, tb | (uint64_t)value); -} - -static inline void _cpu_ppc_store_tbu(CPUPPCState *env, uint32_t value) -{ - ppc_tb_t *tb_env = env->tb_env; - uint64_t tb; - - tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); - tb &= 0x00000000FFFFFFFFULL; - cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - &tb_env->tb_offset, ((uint64_t)value << 32) | tb); -} - -void cpu_ppc_store_tbu (CPUPPCState *env, uint32_t value) -{ - _cpu_ppc_store_tbu(env, value); -} - -uint64_t cpu_ppc_load_atbl (CPUPPCState *env) -{ - ppc_tb_t *tb_env = env->tb_env; - uint64_t tb; - - tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); - LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); - - return tb; -} - -uint32_t cpu_ppc_load_atbu (CPUPPCState *env) -{ - ppc_tb_t *tb_env = env->tb_env; - uint64_t tb; - - tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); - LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); - - return tb >> 32; -} - -void cpu_ppc_store_atbl (CPUPPCState *env, uint32_t value) -{ - ppc_tb_t *tb_env = env->tb_env; - uint64_t tb; - - tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); - tb &= 0xFFFFFFFF00000000ULL; - cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - &tb_env->atb_offset, tb | (uint64_t)value); -} - -void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value) -{ - ppc_tb_t *tb_env = env->tb_env; - uint64_t tb; - - tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); - tb &= 0x00000000FFFFFFFFULL; - cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - &tb_env->atb_offset, ((uint64_t)value << 32) | tb); -} - -static void cpu_ppc_tb_stop (CPUPPCState *env) -{ - ppc_tb_t *tb_env = env->tb_env; - uint64_t tb, atb, vmclk; - - /* If the time base is already frozen, do nothing */ - if (tb_env->tb_freq != 0) { - vmclk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - /* Get the time base */ - tb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->tb_offset); - /* Get the alternate time base */ - atb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->atb_offset); - /* Store the time base value (ie compute the current offset) */ - cpu_ppc_store_tb(tb_env, vmclk, &tb_env->tb_offset, tb); - /* Store the alternate time base value (compute the current offset) */ - cpu_ppc_store_tb(tb_env, vmclk, &tb_env->atb_offset, atb); - /* Set the time base frequency to zero */ - tb_env->tb_freq = 0; - /* Now, the time bases are frozen to tb_offset / atb_offset value */ - } -} - -static void cpu_ppc_tb_start (CPUPPCState *env) -{ - ppc_tb_t *tb_env = env->tb_env; - uint64_t tb, atb, vmclk; - - /* If the time base is not frozen, do nothing */ - if (tb_env->tb_freq == 0) { - vmclk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - /* Get the time base from tb_offset */ - tb = tb_env->tb_offset; - /* Get the alternate time base from atb_offset */ - atb = tb_env->atb_offset; - /* Restore the tb frequency from the decrementer frequency */ - tb_env->tb_freq = tb_env->decr_freq; - /* Store the time base value */ - cpu_ppc_store_tb(tb_env, vmclk, &tb_env->tb_offset, tb); - /* Store the alternate time base value */ - cpu_ppc_store_tb(tb_env, vmclk, &tb_env->atb_offset, atb); - } -} - -bool ppc_decr_clear_on_delivery(CPUPPCState *env) -{ - ppc_tb_t *tb_env = env->tb_env; - int flags = PPC_DECR_UNDERFLOW_TRIGGERED | PPC_DECR_UNDERFLOW_LEVEL; - return ((tb_env->flags & flags) == PPC_DECR_UNDERFLOW_TRIGGERED); -} - -static inline uint32_t _cpu_ppc_load_decr(CPUPPCState *env, uint64_t next) -{ - ppc_tb_t *tb_env = env->tb_env; - uint32_t decr; - int64_t diff; - - diff = next - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - if (diff >= 0) { - 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, NANOSECONDS_PER_SECOND); - } - LOG_TB("%s: %08" PRIx32 "\n", __func__, decr); - - return decr; -} - -uint32_t cpu_ppc_load_decr (CPUPPCState *env) -{ - ppc_tb_t *tb_env = env->tb_env; - - if (kvm_enabled()) { - return env->spr[SPR_DECR]; - } - - return _cpu_ppc_load_decr(env, tb_env->decr_next); -} - -uint32_t cpu_ppc_load_hdecr (CPUPPCState *env) -{ - ppc_tb_t *tb_env = env->tb_env; - - return _cpu_ppc_load_decr(env, tb_env->hdecr_next); -} - -uint64_t cpu_ppc_load_purr (CPUPPCState *env) -{ - ppc_tb_t *tb_env = env->tb_env; - uint64_t diff; - - diff = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - tb_env->purr_start; - - return tb_env->purr_load + - muldiv64(diff, tb_env->tb_freq, NANOSECONDS_PER_SECOND); -} - -/* When decrementer expires, - * all we need to do is generate or queue a CPU exception - */ -static inline void cpu_ppc_decr_excp(PowerPCCPU *cpu) -{ - /* Raise it */ - LOG_TB("raise decrementer exception\n"); - ppc_set_irq(cpu, PPC_INTERRUPT_DECR, 1); -} - -static inline void cpu_ppc_decr_lower(PowerPCCPU *cpu) -{ - ppc_set_irq(cpu, PPC_INTERRUPT_DECR, 0); -} - -static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu) -{ - /* Raise it */ - LOG_TB("raise decrementer exception\n"); - ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1); -} - -static inline void cpu_ppc_hdecr_lower(PowerPCCPU *cpu) -{ - ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 0); -} - -static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, - QEMUTimer *timer, - void (*raise_excp)(void *), - void (*lower_excp)(PowerPCCPU *), - uint32_t decr, uint32_t value) -{ - CPUPPCState *env = &cpu->env; - ppc_tb_t *tb_env = env->tb_env; - uint64_t now, next; - - LOG_TB("%s: %08" PRIx32 " => %08" PRIx32 "\n", __func__, - decr, value); - - if (kvm_enabled()) { - /* KVM handles decrementer exceptions, we don't need our own timer */ - return; - } - - /* - * Going from 2 -> 1, 1 -> 0 or 0 -> -1 is the event to generate a DEC - * interrupt. - * - * If we get a really small DEC value, we can assume that by the time we - * handled it we should inject an interrupt already. - * - * On MSB level based DEC implementations the MSB always means the interrupt - * is pending, so raise it on those. - * - * On MSB edge based DEC implementations the MSB going from 0 -> 1 triggers - * an edge interrupt, so raise it here too. - */ - if ((value < 3) || - ((tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL) && (value & 0x80000000)) || - ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) && (value & 0x80000000) - && !(decr & 0x80000000))) { - (*raise_excp)(cpu); - return; - } - - /* On MSB level based systems a 0 for the MSB stops interrupt delivery */ - if (!(value & 0x80000000) && (tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL)) { - (*lower_excp)(cpu); - } - - /* Calculate the next timer event */ - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - next = now + muldiv64(value, NANOSECONDS_PER_SECOND, tb_env->decr_freq); - *nextp = next; - - /* Adjust timer */ - timer_mod(timer, next); -} - -static inline void _cpu_ppc_store_decr(PowerPCCPU *cpu, uint32_t decr, - uint32_t value) -{ - ppc_tb_t *tb_env = cpu->env.tb_env; - - __cpu_ppc_store_decr(cpu, &tb_env->decr_next, tb_env->decr_timer, - tb_env->decr_timer->cb, &cpu_ppc_decr_lower, decr, - value); -} - -void cpu_ppc_store_decr (CPUPPCState *env, uint32_t value) -{ - PowerPCCPU *cpu = ppc_env_get_cpu(env); - - _cpu_ppc_store_decr(cpu, cpu_ppc_load_decr(env), value); -} - -static void cpu_ppc_decr_cb(void *opaque) -{ - PowerPCCPU *cpu = opaque; - - cpu_ppc_decr_excp(cpu); -} - -static inline void _cpu_ppc_store_hdecr(PowerPCCPU *cpu, uint32_t hdecr, - uint32_t value) -{ - ppc_tb_t *tb_env = cpu->env.tb_env; - - if (tb_env->hdecr_timer != NULL) { - __cpu_ppc_store_decr(cpu, &tb_env->hdecr_next, tb_env->hdecr_timer, - tb_env->hdecr_timer->cb, &cpu_ppc_hdecr_lower, - hdecr, value); - } -} - -void cpu_ppc_store_hdecr (CPUPPCState *env, uint32_t value) -{ - PowerPCCPU *cpu = ppc_env_get_cpu(env); - - _cpu_ppc_store_hdecr(cpu, cpu_ppc_load_hdecr(env), value); -} - -static void cpu_ppc_hdecr_cb(void *opaque) -{ - PowerPCCPU *cpu = opaque; - - cpu_ppc_hdecr_excp(cpu); -} - -static void cpu_ppc_store_purr(PowerPCCPU *cpu, uint64_t value) -{ - ppc_tb_t *tb_env = cpu->env.tb_env; - - tb_env->purr_load = value; - tb_env->purr_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); -} - -static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) -{ - CPUPPCState *env = opaque; - PowerPCCPU *cpu = ppc_env_get_cpu(env); - ppc_tb_t *tb_env = env->tb_env; - - tb_env->tb_freq = freq; - tb_env->decr_freq = freq; - /* There is a bug in Linux 2.4 kernels: - * if a decrementer exception is pending when it enables msr_ee at startup, - * it's not ready to handle it... - */ - _cpu_ppc_store_decr(cpu, 0xFFFFFFFF, 0xFFFFFFFF); - _cpu_ppc_store_hdecr(cpu, 0xFFFFFFFF, 0xFFFFFFFF); - cpu_ppc_store_purr(cpu, 0x0000000000000000ULL); -} - -static void timebase_pre_save(void *opaque) -{ - PPCTimebase *tb = opaque; - uint64_t ticks = cpu_get_host_ticks(); - PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu); - - if (!first_ppc_cpu->env.tb_env) { - error_report("No timebase object"); - return; - } - - tb->time_of_the_day_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST); - /* - * tb_offset is only expected to be changed by migration so - * there is no need to update it from KVM here - */ - tb->guest_timebase = ticks + first_ppc_cpu->env.tb_env->tb_offset; -} - -static int timebase_post_load(void *opaque, int version_id) -{ - PPCTimebase *tb_remote = opaque; - CPUState *cpu; - PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu); - int64_t tb_off_adj, tb_off, ns_diff; - int64_t migration_duration_ns, migration_duration_tb, guest_tb, host_ns; - unsigned long freq; - - if (!first_ppc_cpu->env.tb_env) { - error_report("No timebase object"); - return -1; - } - - freq = first_ppc_cpu->env.tb_env->tb_freq; - /* - * Calculate timebase on the destination side of migration. - * The destination timebase must be not less than the source timebase. - * We try to adjust timebase by downtime if host clocks are not - * too much out of sync (1 second for now). - */ - host_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST); - ns_diff = MAX(0, host_ns - tb_remote->time_of_the_day_ns); - migration_duration_ns = MIN(NANOSECONDS_PER_SECOND, ns_diff); - migration_duration_tb = muldiv64(migration_duration_ns, freq, - NANOSECONDS_PER_SECOND); - guest_tb = tb_remote->guest_timebase + MIN(0, migration_duration_tb); - - 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, - (tb_off_adj - tb_off) / freq); - - /* Set new offset to all CPUs */ - CPU_FOREACH(cpu) { - PowerPCCPU *pcpu = POWERPC_CPU(cpu); - pcpu->env.tb_env->tb_offset = tb_off_adj; - } - - return 0; -} - -const VMStateDescription vmstate_ppc_timebase = { - .name = "timebase", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .pre_save = timebase_pre_save, - .post_load = timebase_post_load, - .fields = (VMStateField []) { - VMSTATE_UINT64(guest_timebase, PPCTimebase), - VMSTATE_INT64(time_of_the_day_ns, PPCTimebase), - VMSTATE_END_OF_LIST() - }, -}; - -/* Set up (once) timebase frequency (in Hz) */ -clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq) -{ - PowerPCCPU *cpu = ppc_env_get_cpu(env); - ppc_tb_t *tb_env; - - tb_env = g_malloc0(sizeof(ppc_tb_t)); - env->tb_env = tb_env; - tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED; - if (env->insns_flags & PPC_SEGMENT_64B) { - /* All Book3S 64bit CPUs implement level based DEC logic */ - tb_env->flags |= PPC_DECR_UNDERFLOW_LEVEL; - } - /* Create new timer */ - tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_decr_cb, cpu); - if (0) { - /* XXX: find a suitable condition to enable the hypervisor decrementer - */ - tb_env->hdecr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_hdecr_cb, - cpu); - } else { - tb_env->hdecr_timer = NULL; - } - cpu_ppc_set_tb_clk(env, freq); - - return &cpu_ppc_set_tb_clk; -} - -/* Specific helpers for POWER & PowerPC 601 RTC */ -#if 0 -static clk_setup_cb cpu_ppc601_rtc_init (CPUPPCState *env) -{ - return cpu_ppc_tb_init(env, 7812500); -} -#endif - -void cpu_ppc601_store_rtcu (CPUPPCState *env, uint32_t value) -{ - _cpu_ppc_store_tbu(env, value); -} - -uint32_t cpu_ppc601_load_rtcu (CPUPPCState *env) -{ - return _cpu_ppc_load_tbu(env); -} - -void cpu_ppc601_store_rtcl (CPUPPCState *env, uint32_t value) -{ - cpu_ppc_store_tbl(env, value & 0x3FFFFF80); -} - -uint32_t cpu_ppc601_load_rtcl (CPUPPCState *env) -{ - return cpu_ppc_load_tbl(env) & 0x3FFFFF80; -} - -/*****************************************************************************/ -/* PowerPC 40x timers */ - -/* PIT, FIT & WDT */ -typedef struct ppc40x_timer_t ppc40x_timer_t; -struct ppc40x_timer_t { - uint64_t pit_reload; /* PIT auto-reload value */ - uint64_t fit_next; /* Tick for next FIT interrupt */ - QEMUTimer *fit_timer; - uint64_t wdt_next; /* Tick for next WDT interrupt */ - QEMUTimer *wdt_timer; - - /* 405 have the PIT, 440 have a DECR. */ - unsigned int decr_excp; -}; - -/* Fixed interval timer */ -static void cpu_4xx_fit_cb (void *opaque) -{ - PowerPCCPU *cpu; - CPUPPCState *env; - ppc_tb_t *tb_env; - ppc40x_timer_t *ppc40x_timer; - uint64_t now, next; - - env = opaque; - cpu = ppc_env_get_cpu(env); - tb_env = env->tb_env; - ppc40x_timer = tb_env->opaque; - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) { - case 0: - next = 1 << 9; - break; - case 1: - next = 1 << 13; - break; - case 2: - next = 1 << 17; - break; - case 3: - next = 1 << 21; - break; - default: - /* Cannot occur, but makes gcc happy */ - return; - } - next = now + muldiv64(next, NANOSECONDS_PER_SECOND, tb_env->tb_freq); - if (next == now) - next++; - timer_mod(ppc40x_timer->fit_timer, next); - env->spr[SPR_40x_TSR] |= 1 << 26; - if ((env->spr[SPR_40x_TCR] >> 23) & 0x1) { - ppc_set_irq(cpu, PPC_INTERRUPT_FIT, 1); - } - LOG_TB("%s: ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__, - (int)((env->spr[SPR_40x_TCR] >> 23) & 0x1), - env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]); -} - -/* Programmable interval timer */ -static void start_stop_pit (CPUPPCState *env, ppc_tb_t *tb_env, int is_excp) -{ - ppc40x_timer_t *ppc40x_timer; - uint64_t now, next; - - ppc40x_timer = tb_env->opaque; - if (ppc40x_timer->pit_reload <= 1 || - !((env->spr[SPR_40x_TCR] >> 26) & 0x1) || - (is_excp && !((env->spr[SPR_40x_TCR] >> 22) & 0x1))) { - /* Stop PIT */ - LOG_TB("%s: stop PIT\n", __func__); - timer_del(tb_env->decr_timer); - } else { - LOG_TB("%s: start PIT %016" PRIx64 "\n", - __func__, ppc40x_timer->pit_reload); - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - next = now + muldiv64(ppc40x_timer->pit_reload, - NANOSECONDS_PER_SECOND, tb_env->decr_freq); - if (is_excp) - next += tb_env->decr_next - now; - if (next == now) - next++; - timer_mod(tb_env->decr_timer, next); - tb_env->decr_next = next; - } -} - -static void cpu_4xx_pit_cb (void *opaque) -{ - PowerPCCPU *cpu; - CPUPPCState *env; - ppc_tb_t *tb_env; - ppc40x_timer_t *ppc40x_timer; - - env = opaque; - cpu = ppc_env_get_cpu(env); - tb_env = env->tb_env; - ppc40x_timer = tb_env->opaque; - env->spr[SPR_40x_TSR] |= 1 << 27; - if ((env->spr[SPR_40x_TCR] >> 26) & 0x1) { - ppc_set_irq(cpu, ppc40x_timer->decr_excp, 1); - } - start_stop_pit(env, tb_env, 1); - LOG_TB("%s: ar %d ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " " - "%016" PRIx64 "\n", __func__, - (int)((env->spr[SPR_40x_TCR] >> 22) & 0x1), - (int)((env->spr[SPR_40x_TCR] >> 26) & 0x1), - env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR], - ppc40x_timer->pit_reload); -} - -/* Watchdog timer */ -static void cpu_4xx_wdt_cb (void *opaque) -{ - PowerPCCPU *cpu; - CPUPPCState *env; - ppc_tb_t *tb_env; - ppc40x_timer_t *ppc40x_timer; - uint64_t now, next; - - env = opaque; - cpu = ppc_env_get_cpu(env); - tb_env = env->tb_env; - ppc40x_timer = tb_env->opaque; - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) { - case 0: - next = 1 << 17; - break; - case 1: - next = 1 << 21; - break; - case 2: - next = 1 << 25; - break; - case 3: - next = 1 << 29; - break; - default: - /* Cannot occur, but makes gcc happy */ - return; - } - 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__, - env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]); - switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) { - case 0x0: - case 0x1: - timer_mod(ppc40x_timer->wdt_timer, next); - ppc40x_timer->wdt_next = next; - env->spr[SPR_40x_TSR] |= 1U << 31; - break; - case 0x2: - timer_mod(ppc40x_timer->wdt_timer, next); - ppc40x_timer->wdt_next = next; - env->spr[SPR_40x_TSR] |= 1 << 30; - if ((env->spr[SPR_40x_TCR] >> 27) & 0x1) { - ppc_set_irq(cpu, PPC_INTERRUPT_WDT, 1); - } - break; - case 0x3: - env->spr[SPR_40x_TSR] &= ~0x30000000; - env->spr[SPR_40x_TSR] |= env->spr[SPR_40x_TCR] & 0x30000000; - switch ((env->spr[SPR_40x_TCR] >> 28) & 0x3) { - case 0x0: - /* No reset */ - break; - case 0x1: /* Core reset */ - ppc40x_core_reset(cpu); - break; - case 0x2: /* Chip reset */ - ppc40x_chip_reset(cpu); - break; - case 0x3: /* System reset */ - ppc40x_system_reset(cpu); - break; - } - } -} - -void store_40x_pit (CPUPPCState *env, target_ulong val) -{ - ppc_tb_t *tb_env; - ppc40x_timer_t *ppc40x_timer; - - tb_env = env->tb_env; - ppc40x_timer = tb_env->opaque; - LOG_TB("%s val" TARGET_FMT_lx "\n", __func__, val); - ppc40x_timer->pit_reload = val; - start_stop_pit(env, tb_env, 0); -} - -target_ulong load_40x_pit (CPUPPCState *env) -{ - return cpu_ppc_load_decr(env); -} - -static void ppc_40x_set_tb_clk (void *opaque, uint32_t freq) -{ - CPUPPCState *env = opaque; - ppc_tb_t *tb_env = env->tb_env; - - LOG_TB("%s set new frequency to %" PRIu32 "\n", __func__, - freq); - tb_env->tb_freq = freq; - tb_env->decr_freq = freq; - /* XXX: we should also update all timers */ -} - -clk_setup_cb ppc_40x_timers_init (CPUPPCState *env, uint32_t freq, - unsigned int decr_excp) -{ - ppc_tb_t *tb_env; - ppc40x_timer_t *ppc40x_timer; - - tb_env = g_malloc0(sizeof(ppc_tb_t)); - env->tb_env = tb_env; - tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED; - ppc40x_timer = g_malloc0(sizeof(ppc40x_timer_t)); - tb_env->tb_freq = freq; - tb_env->decr_freq = freq; - tb_env->opaque = ppc40x_timer; - LOG_TB("%s freq %" PRIu32 "\n", __func__, freq); - if (ppc40x_timer != NULL) { - /* We use decr timer for PIT */ - tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_pit_cb, env); - ppc40x_timer->fit_timer = - timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_fit_cb, env); - ppc40x_timer->wdt_timer = - timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_wdt_cb, env); - ppc40x_timer->decr_excp = decr_excp; - } - - return &ppc_40x_set_tb_clk; -} - -/*****************************************************************************/ -/* Embedded PowerPC Device Control Registers */ -typedef struct ppc_dcrn_t ppc_dcrn_t; -struct ppc_dcrn_t { - dcr_read_cb dcr_read; - dcr_write_cb dcr_write; - void *opaque; -}; - -/* XXX: on 460, DCR addresses are 32 bits wide, - * using DCRIPR to get the 22 upper bits of the DCR address - */ -#define DCRN_NB 1024 -struct ppc_dcr_t { - ppc_dcrn_t dcrn[DCRN_NB]; - int (*read_error)(int dcrn); - int (*write_error)(int dcrn); -}; - -int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp) -{ - ppc_dcrn_t *dcr; - - if (dcrn < 0 || dcrn >= DCRN_NB) - goto error; - dcr = &dcr_env->dcrn[dcrn]; - if (dcr->dcr_read == NULL) - goto error; - *valp = (*dcr->dcr_read)(dcr->opaque, dcrn); - - return 0; - - error: - if (dcr_env->read_error != NULL) - return (*dcr_env->read_error)(dcrn); - - return -1; -} - -int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val) -{ - ppc_dcrn_t *dcr; - - if (dcrn < 0 || dcrn >= DCRN_NB) - goto error; - dcr = &dcr_env->dcrn[dcrn]; - if (dcr->dcr_write == NULL) - goto error; - (*dcr->dcr_write)(dcr->opaque, dcrn, val); - - return 0; - - error: - if (dcr_env->write_error != NULL) - return (*dcr_env->write_error)(dcrn); - - return -1; -} - -int ppc_dcr_register (CPUPPCState *env, int dcrn, void *opaque, - dcr_read_cb dcr_read, dcr_write_cb dcr_write) -{ - ppc_dcr_t *dcr_env; - ppc_dcrn_t *dcr; - - dcr_env = env->dcr_env; - if (dcr_env == NULL) - return -1; - if (dcrn < 0 || dcrn >= DCRN_NB) - return -1; - dcr = &dcr_env->dcrn[dcrn]; - if (dcr->opaque != NULL || - dcr->dcr_read != NULL || - dcr->dcr_write != NULL) - return -1; - dcr->opaque = opaque; - dcr->dcr_read = dcr_read; - dcr->dcr_write = dcr_write; - - return 0; -} - -int ppc_dcr_init (CPUPPCState *env, int (*read_error)(int dcrn), - int (*write_error)(int dcrn)) -{ - ppc_dcr_t *dcr_env; - - dcr_env = g_malloc0(sizeof(ppc_dcr_t)); - dcr_env->read_error = read_error; - dcr_env->write_error = write_error; - env->dcr_env = dcr_env; - - return 0; -} - -/*****************************************************************************/ -/* Debug port */ -void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val) -{ - addr &= 0xF; - switch (addr) { - case 0: - printf("%c", val); - break; - case 1: - printf("\n"); - fflush(stdout); - break; - case 2: - printf("Set loglevel to %04" PRIx32 "\n", val); - qemu_set_log(val | 0x100); - break; - } -} - -/* CPU device-tree ID helpers */ -int ppc_get_vcpu_dt_id(PowerPCCPU *cpu) -{ - return cpu->cpu_dt_id; -} - -PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id) -{ - CPUState *cs; - - CPU_FOREACH(cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - - if (cpu->cpu_dt_id == cpu_dt_id) { - return cpu; - } - } - - return NULL; -} diff --git a/qemu/hw/ppc/ppc405.h b/qemu/hw/ppc/ppc405.h deleted file mode 100644 index 1c5f04fae..000000000 --- a/qemu/hw/ppc/ppc405.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * QEMU PowerPC 405 shared definitions - * - * Copyright (c) 2007 Jocelyn Mayer - * - * 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. - */ - -#if !defined(PPC_405_H) -#define PPC_405_H - -#include "hw/ppc/ppc4xx.h" - -/* Bootinfo as set-up by u-boot */ -typedef struct ppc4xx_bd_info_t ppc4xx_bd_info_t; -struct ppc4xx_bd_info_t { - uint32_t bi_memstart; - uint32_t bi_memsize; - uint32_t bi_flashstart; - uint32_t bi_flashsize; - uint32_t bi_flashoffset; /* 0x10 */ - uint32_t bi_sramstart; - uint32_t bi_sramsize; - uint32_t bi_bootflags; - uint32_t bi_ipaddr; /* 0x20 */ - uint8_t bi_enetaddr[6]; - uint16_t bi_ethspeed; - uint32_t bi_intfreq; - uint32_t bi_busfreq; /* 0x30 */ - uint32_t bi_baudrate; - uint8_t bi_s_version[4]; - uint8_t bi_r_version[32]; - uint32_t bi_procfreq; - uint32_t bi_plb_busfreq; - uint32_t bi_pci_busfreq; - uint8_t bi_pci_enetaddr[6]; - uint32_t bi_pci_enetaddr2[6]; - uint32_t bi_opbfreq; - uint32_t bi_iic_fast[2]; -}; - -/* PowerPC 405 core */ -ram_addr_t ppc405_set_bootinfo (CPUPPCState *env, ppc4xx_bd_info_t *bd, - uint32_t flags); - -CPUPPCState *ppc405cr_init(MemoryRegion *address_space_mem, - MemoryRegion ram_memories[4], - hwaddr ram_bases[4], - hwaddr ram_sizes[4], - uint32_t sysclk, qemu_irq **picp, - int do_init); -CPUPPCState *ppc405ep_init(MemoryRegion *address_space_mem, - MemoryRegion ram_memories[2], - hwaddr ram_bases[2], - hwaddr ram_sizes[2], - uint32_t sysclk, qemu_irq **picp, - int do_init); -/* IBM STBxxx microcontrollers */ -CPUPPCState *ppc_stb025_init (MemoryRegion ram_memories[2], - hwaddr ram_bases[2], - hwaddr ram_sizes[2], - uint32_t sysclk, qemu_irq **picp, - ram_addr_t *offsetp); - -#endif /* !defined(PPC_405_H) */ diff --git a/qemu/hw/ppc/ppc405_boards.c b/qemu/hw/ppc/ppc405_boards.c deleted file mode 100644 index 4b2f07aec..000000000 --- a/qemu/hw/ppc/ppc405_boards.c +++ /dev/null @@ -1,664 +0,0 @@ -/* - * QEMU PowerPC 405 evaluation boards emulation - * - * Copyright (c) 2007 Jocelyn Mayer - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/hw.h" -#include "hw/ppc/ppc.h" -#include "ppc405.h" -#include "hw/timer/m48t59.h" -#include "hw/block/flash.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "sysemu/block-backend.h" -#include "hw/boards.h" -#include "qemu/log.h" -#include "qemu/error-report.h" -#include "hw/loader.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "exec/address-spaces.h" - -#define BIOS_FILENAME "ppc405_rom.bin" -#define BIOS_SIZE (2048 * 1024) - -#define KERNEL_LOAD_ADDR 0x00000000 -#define INITRD_LOAD_ADDR 0x01800000 - -#define USE_FLASH_BIOS - -//#define DEBUG_BOARD_INIT - -/*****************************************************************************/ -/* PPC405EP reference board (IBM) */ -/* Standalone board with: - * - PowerPC 405EP CPU - * - SDRAM (0x00000000) - * - Flash (0xFFF80000) - * - SRAM (0xFFF00000) - * - NVRAM (0xF0000000) - * - FPGA (0xF0300000) - */ -typedef struct ref405ep_fpga_t ref405ep_fpga_t; -struct ref405ep_fpga_t { - uint8_t reg0; - uint8_t reg1; -}; - -static uint32_t ref405ep_fpga_readb (void *opaque, hwaddr addr) -{ - ref405ep_fpga_t *fpga; - uint32_t ret; - - fpga = opaque; - switch (addr) { - case 0x0: - ret = fpga->reg0; - break; - case 0x1: - ret = fpga->reg1; - break; - default: - ret = 0; - break; - } - - return ret; -} - -static void ref405ep_fpga_writeb (void *opaque, - hwaddr addr, uint32_t value) -{ - ref405ep_fpga_t *fpga; - - fpga = opaque; - switch (addr) { - case 0x0: - /* Read only */ - break; - case 0x1: - fpga->reg1 = value; - break; - default: - break; - } -} - -static uint32_t ref405ep_fpga_readw (void *opaque, hwaddr addr) -{ - uint32_t ret; - - ret = ref405ep_fpga_readb(opaque, addr) << 8; - ret |= ref405ep_fpga_readb(opaque, addr + 1); - - return ret; -} - -static void ref405ep_fpga_writew (void *opaque, - hwaddr addr, uint32_t value) -{ - ref405ep_fpga_writeb(opaque, addr, (value >> 8) & 0xFF); - ref405ep_fpga_writeb(opaque, addr + 1, value & 0xFF); -} - -static uint32_t ref405ep_fpga_readl (void *opaque, hwaddr addr) -{ - uint32_t ret; - - ret = ref405ep_fpga_readb(opaque, addr) << 24; - ret |= ref405ep_fpga_readb(opaque, addr + 1) << 16; - ret |= ref405ep_fpga_readb(opaque, addr + 2) << 8; - ret |= ref405ep_fpga_readb(opaque, addr + 3); - - return ret; -} - -static void ref405ep_fpga_writel (void *opaque, - hwaddr addr, uint32_t value) -{ - ref405ep_fpga_writeb(opaque, addr, (value >> 24) & 0xFF); - ref405ep_fpga_writeb(opaque, addr + 1, (value >> 16) & 0xFF); - ref405ep_fpga_writeb(opaque, addr + 2, (value >> 8) & 0xFF); - ref405ep_fpga_writeb(opaque, addr + 3, value & 0xFF); -} - -static const MemoryRegionOps ref405ep_fpga_ops = { - .old_mmio = { - .read = { - ref405ep_fpga_readb, ref405ep_fpga_readw, ref405ep_fpga_readl, - }, - .write = { - ref405ep_fpga_writeb, ref405ep_fpga_writew, ref405ep_fpga_writel, - }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void ref405ep_fpga_reset (void *opaque) -{ - ref405ep_fpga_t *fpga; - - fpga = opaque; - fpga->reg0 = 0x00; - fpga->reg1 = 0x0F; -} - -static void ref405ep_fpga_init(MemoryRegion *sysmem, uint32_t base) -{ - ref405ep_fpga_t *fpga; - MemoryRegion *fpga_memory = g_new(MemoryRegion, 1); - - fpga = g_malloc0(sizeof(ref405ep_fpga_t)); - memory_region_init_io(fpga_memory, NULL, &ref405ep_fpga_ops, fpga, - "fpga", 0x00000100); - memory_region_add_subregion(sysmem, base, fpga_memory); - qemu_register_reset(&ref405ep_fpga_reset, fpga); -} - -static void ref405ep_init(MachineState *machine) -{ - ram_addr_t ram_size = machine->ram_size; - const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; - const char *initrd_filename = machine->initrd_filename; - char *filename; - ppc4xx_bd_info_t bd; - CPUPPCState *env; - qemu_irq *pic; - MemoryRegion *bios; - MemoryRegion *sram = g_new(MemoryRegion, 1); - ram_addr_t bdloc; - MemoryRegion *ram_memories = g_malloc(2 * sizeof(*ram_memories)); - hwaddr ram_bases[2], ram_sizes[2]; - target_ulong sram_size; - long bios_size; - //int phy_addr = 0; - //static int phy_addr = 1; - target_ulong kernel_base, initrd_base; - long kernel_size, initrd_size; - int linux_boot; - int fl_idx, fl_sectors, len; - DriveInfo *dinfo; - MemoryRegion *sysmem = get_system_memory(); - - /* XXX: fix this */ - memory_region_allocate_system_memory(&ram_memories[0], NULL, "ef405ep.ram", - 0x08000000); - ram_bases[0] = 0; - ram_sizes[0] = 0x08000000; - memory_region_init(&ram_memories[1], NULL, "ef405ep.ram1", 0); - ram_bases[1] = 0x00000000; - ram_sizes[1] = 0x00000000; - ram_size = 128 * 1024 * 1024; -#ifdef DEBUG_BOARD_INIT - printf("%s: register cpu\n", __func__); -#endif - env = ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes, - 33333333, &pic, kernel_filename == NULL ? 0 : 1); - /* allocate SRAM */ - sram_size = 512 * 1024; - 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 */ -#ifdef DEBUG_BOARD_INIT - printf("%s: register BIOS\n", __func__); -#endif - fl_idx = 0; -#ifdef USE_FLASH_BIOS - dinfo = drive_get(IF_PFLASH, 0, fl_idx); - if (dinfo) { - BlockBackend *blk = blk_by_legacy_dinfo(dinfo); - - bios_size = blk_getlength(blk); - fl_sectors = (bios_size + 65535) >> 16; -#ifdef DEBUG_BOARD_INIT - printf("Register parallel flash %d size %lx" - " at addr %lx '%s' %d\n", - fl_idx, bios_size, -bios_size, - blk_name(blk), fl_sectors); -#endif - pflash_cfi02_register((uint32_t)(-bios_size), - NULL, "ef405ep.bios", bios_size, - blk, 65536, fl_sectors, 1, - 2, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, - 1); - fl_idx++; - } else -#endif - { -#ifdef DEBUG_BOARD_INIT - printf("Load BIOS from file\n"); -#endif - bios = g_new(MemoryRegion, 1); - memory_region_init_ram(bios, NULL, "ef405ep.bios", BIOS_SIZE, - &error_fatal); - vmstate_register_ram_global(bios); - - if (bios_name == NULL) - bios_name = BIOS_FILENAME; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - bios_size = load_image(filename, memory_region_get_ram_ptr(bios)); - g_free(filename); - if (bios_size < 0 || bios_size > BIOS_SIZE) { - error_report("Could not load PowerPC BIOS '%s'", bios_name); - exit(1); - } - bios_size = (bios_size + 0xfff) & ~0xfff; - memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios); - } else if (!qtest_enabled() || kernel_filename != NULL) { - error_report("Could not load PowerPC BIOS '%s'", bios_name); - exit(1); - } else { - /* Avoid an uninitialized variable warning */ - bios_size = -1; - } - memory_region_set_readonly(bios, true); - } - /* Register FPGA */ -#ifdef DEBUG_BOARD_INIT - printf("%s: register FPGA\n", __func__); -#endif - ref405ep_fpga_init(sysmem, 0xF0300000); - /* Register NVRAM */ -#ifdef DEBUG_BOARD_INIT - printf("%s: register NVRAM\n", __func__); -#endif - m48t59_init(NULL, 0xF0000000, 0, 8192, 1968, 8); - /* Load kernel */ - linux_boot = (kernel_filename != NULL); - if (linux_boot) { -#ifdef DEBUG_BOARD_INIT - printf("%s: load kernel\n", __func__); -#endif - memset(&bd, 0, sizeof(bd)); - bd.bi_memstart = 0x00000000; - bd.bi_memsize = ram_size; - bd.bi_flashstart = -bios_size; - bd.bi_flashsize = -bios_size; - bd.bi_flashoffset = 0; - bd.bi_sramstart = 0xFFF00000; - bd.bi_sramsize = sram_size; - bd.bi_bootflags = 0; - bd.bi_intfreq = 133333333; - bd.bi_busfreq = 33333333; - bd.bi_baudrate = 115200; - bd.bi_s_version[0] = 'Q'; - bd.bi_s_version[1] = 'M'; - bd.bi_s_version[2] = 'U'; - bd.bi_s_version[3] = '\0'; - bd.bi_r_version[0] = 'Q'; - bd.bi_r_version[1] = 'E'; - bd.bi_r_version[2] = 'M'; - bd.bi_r_version[3] = 'U'; - bd.bi_r_version[4] = '\0'; - bd.bi_procfreq = 133333333; - bd.bi_plb_busfreq = 33333333; - bd.bi_pci_busfreq = 33333333; - bd.bi_opbfreq = 33333333; - bdloc = ppc405_set_bootinfo(env, &bd, 0x00000001); - env->gpr[3] = bdloc; - kernel_base = KERNEL_LOAD_ADDR; - /* now we can load the kernel */ - kernel_size = load_image_targphys(kernel_filename, kernel_base, - ram_size - kernel_base); - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); - exit(1); - } - printf("Load kernel size %ld at " TARGET_FMT_lx, - kernel_size, kernel_base); - /* load initrd */ - if (initrd_filename) { - initrd_base = INITRD_LOAD_ADDR; - initrd_size = load_image_targphys(initrd_filename, initrd_base, - ram_size - initrd_base); - if (initrd_size < 0) { - fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", - initrd_filename); - exit(1); - } - } else { - initrd_base = 0; - initrd_size = 0; - } - env->gpr[4] = initrd_base; - env->gpr[5] = initrd_size; - if (kernel_cmdline != NULL) { - len = strlen(kernel_cmdline); - bdloc -= ((len + 255) & ~255); - cpu_physical_memory_write(bdloc, kernel_cmdline, len + 1); - env->gpr[6] = bdloc; - env->gpr[7] = bdloc + len; - } else { - env->gpr[6] = 0; - env->gpr[7] = 0; - } - env->nip = KERNEL_LOAD_ADDR; - } else { - kernel_base = 0; - kernel_size = 0; - initrd_base = 0; - initrd_size = 0; - bdloc = 0; - } -#ifdef DEBUG_BOARD_INIT - printf("bdloc " RAM_ADDR_FMT "\n", bdloc); - printf("%s: Done\n", __func__); -#endif -} - -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, -}; - -/*****************************************************************************/ -/* AMCC Taihu evaluation board */ -/* - PowerPC 405EP processor - * - SDRAM 128 MB at 0x00000000 - * - Boot flash 2 MB at 0xFFE00000 - * - Application flash 32 MB at 0xFC000000 - * - 2 serial ports - * - 2 ethernet PHY - * - 1 USB 1.1 device 0x50000000 - * - 1 LCD display 0x50100000 - * - 1 CPLD 0x50100000 - * - 1 I2C EEPROM - * - 1 I2C thermal sensor - * - a set of LEDs - * - bit-bang SPI port using GPIOs - * - 1 EBC interface connector 0 0x50200000 - * - 1 cardbus controller + expansion slot. - * - 1 PCI expansion slot. - */ -typedef struct taihu_cpld_t taihu_cpld_t; -struct taihu_cpld_t { - uint8_t reg0; - uint8_t reg1; -}; - -static uint64_t taihu_cpld_read(void *opaque, hwaddr addr, unsigned size) -{ - taihu_cpld_t *cpld; - uint32_t ret; - - cpld = opaque; - switch (addr) { - case 0x0: - ret = cpld->reg0; - break; - case 0x1: - ret = cpld->reg1; - break; - default: - ret = 0; - break; - } - - return ret; -} - -static void taihu_cpld_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - taihu_cpld_t *cpld; - - cpld = opaque; - switch (addr) { - case 0x0: - /* Read only */ - break; - case 0x1: - cpld->reg1 = value; - break; - default: - break; - } -} - -static const MemoryRegionOps taihu_cpld_ops = { - .read = taihu_cpld_read, - .write = taihu_cpld_write, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void taihu_cpld_reset (void *opaque) -{ - taihu_cpld_t *cpld; - - cpld = opaque; - cpld->reg0 = 0x01; - cpld->reg1 = 0x80; -} - -static void taihu_cpld_init(MemoryRegion *sysmem, uint32_t base) -{ - taihu_cpld_t *cpld; - MemoryRegion *cpld_memory = g_new(MemoryRegion, 1); - - cpld = g_malloc0(sizeof(taihu_cpld_t)); - memory_region_init_io(cpld_memory, NULL, &taihu_cpld_ops, cpld, "cpld", 0x100); - memory_region_add_subregion(sysmem, base, cpld_memory); - qemu_register_reset(&taihu_cpld_reset, cpld); -} - -static void taihu_405ep_init(MachineState *machine) -{ - ram_addr_t ram_size = machine->ram_size; - const char *kernel_filename = machine->kernel_filename; - const char *initrd_filename = machine->initrd_filename; - char *filename; - qemu_irq *pic; - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *bios; - MemoryRegion *ram_memories = g_malloc(2 * sizeof(*ram_memories)); - MemoryRegion *ram = g_malloc0(sizeof(*ram)); - hwaddr ram_bases[2], ram_sizes[2]; - long bios_size; - target_ulong kernel_base, initrd_base; - long kernel_size, initrd_size; - int linux_boot; - int fl_idx, fl_sectors; - DriveInfo *dinfo; - - /* RAM is soldered to the board so the size cannot be changed */ - ram_size = 0x08000000; - memory_region_allocate_system_memory(ram, NULL, "taihu_405ep.ram", - ram_size); - - ram_bases[0] = 0; - ram_sizes[0] = 0x04000000; - memory_region_init_alias(&ram_memories[0], NULL, - "taihu_405ep.ram-0", ram, ram_bases[0], - ram_sizes[0]); - ram_bases[1] = 0x04000000; - ram_sizes[1] = 0x04000000; - memory_region_init_alias(&ram_memories[1], NULL, - "taihu_405ep.ram-1", ram, ram_bases[1], - ram_sizes[1]); -#ifdef DEBUG_BOARD_INIT - printf("%s: register cpu\n", __func__); -#endif - ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes, - 33333333, &pic, kernel_filename == NULL ? 0 : 1); - /* allocate and load BIOS */ -#ifdef DEBUG_BOARD_INIT - printf("%s: register BIOS\n", __func__); -#endif - fl_idx = 0; -#if defined(USE_FLASH_BIOS) - dinfo = drive_get(IF_PFLASH, 0, fl_idx); - if (dinfo) { - BlockBackend *blk = blk_by_legacy_dinfo(dinfo); - - bios_size = blk_getlength(blk); - /* XXX: should check that size is 2MB */ - // bios_size = 2 * 1024 * 1024; - fl_sectors = (bios_size + 65535) >> 16; -#ifdef DEBUG_BOARD_INIT - printf("Register parallel flash %d size %lx" - " at addr %lx '%s' %d\n", - fl_idx, bios_size, -bios_size, - blk_name(blk), fl_sectors); -#endif - pflash_cfi02_register((uint32_t)(-bios_size), - NULL, "taihu_405ep.bios", bios_size, - blk, 65536, fl_sectors, 1, - 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, - 1); - fl_idx++; - } else -#endif - { -#ifdef DEBUG_BOARD_INIT - printf("Load BIOS from file\n"); -#endif - if (bios_name == NULL) - bios_name = BIOS_FILENAME; - bios = g_new(MemoryRegion, 1); - memory_region_init_ram(bios, NULL, "taihu_405ep.bios", BIOS_SIZE, - &error_fatal); - vmstate_register_ram_global(bios); - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - bios_size = load_image(filename, memory_region_get_ram_ptr(bios)); - g_free(filename); - if (bios_size < 0 || bios_size > BIOS_SIZE) { - error_report("Could not load PowerPC BIOS '%s'", bios_name); - exit(1); - } - bios_size = (bios_size + 0xfff) & ~0xfff; - memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios); - } else if (!qtest_enabled()) { - error_report("Could not load PowerPC BIOS '%s'", bios_name); - exit(1); - } - memory_region_set_readonly(bios, true); - } - /* Register Linux flash */ - dinfo = drive_get(IF_PFLASH, 0, fl_idx); - if (dinfo) { - BlockBackend *blk = blk_by_legacy_dinfo(dinfo); - - bios_size = blk_getlength(blk); - /* XXX: should check that size is 32MB */ - bios_size = 32 * 1024 * 1024; - fl_sectors = (bios_size + 65535) >> 16; -#ifdef DEBUG_BOARD_INIT - printf("Register parallel flash %d size %lx" - " at addr " TARGET_FMT_lx " '%s'\n", - fl_idx, bios_size, (target_ulong)0xfc000000, - blk_name(blk)); -#endif - pflash_cfi02_register(0xfc000000, NULL, "taihu_405ep.flash", bios_size, - blk, 65536, fl_sectors, 1, - 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, - 1); - fl_idx++; - } - /* Register CLPD & LCD display */ -#ifdef DEBUG_BOARD_INIT - printf("%s: register CPLD\n", __func__); -#endif - taihu_cpld_init(sysmem, 0x50100000); - /* Load kernel */ - linux_boot = (kernel_filename != NULL); - if (linux_boot) { -#ifdef DEBUG_BOARD_INIT - printf("%s: load kernel\n", __func__); -#endif - kernel_base = KERNEL_LOAD_ADDR; - /* now we can load the kernel */ - kernel_size = load_image_targphys(kernel_filename, kernel_base, - ram_size - kernel_base); - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); - exit(1); - } - /* load initrd */ - if (initrd_filename) { - initrd_base = INITRD_LOAD_ADDR; - initrd_size = load_image_targphys(initrd_filename, initrd_base, - ram_size - initrd_base); - if (initrd_size < 0) { - fprintf(stderr, - "qemu: could not load initial ram disk '%s'\n", - initrd_filename); - exit(1); - } - } else { - initrd_base = 0; - initrd_size = 0; - } - } else { - kernel_base = 0; - kernel_size = 0; - initrd_base = 0; - initrd_size = 0; - } -#ifdef DEBUG_BOARD_INIT - printf("%s: Done\n", __func__); -#endif -} - -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) -{ - type_register_static(&ref405ep_type); - type_register_static(&taihu_type); -} - -type_init(ppc405_machine_init) diff --git a/qemu/hw/ppc/ppc405_uc.c b/qemu/hw/ppc/ppc405_uc.c deleted file mode 100644 index d6d3fc2c4..000000000 --- a/qemu/hw/ppc/ppc405_uc.c +++ /dev/null @@ -1,2555 +0,0 @@ -/* - * QEMU PowerPC 405 embedded processors emulation - * - * Copyright (c) 2007 Jocelyn Mayer - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/hw.h" -#include "hw/ppc/ppc.h" -#include "hw/boards.h" -#include "ppc405.h" -#include "hw/char/serial.h" -#include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "qemu/log.h" -#include "exec/address-spaces.h" - -//#define DEBUG_OPBA -//#define DEBUG_SDRAM -//#define DEBUG_GPIO -//#define DEBUG_SERIAL -//#define DEBUG_OCM -//#define DEBUG_I2C -//#define DEBUG_GPT -//#define DEBUG_MAL -//#define DEBUG_CLOCKS -//#define DEBUG_CLOCKS_LL - -ram_addr_t ppc405_set_bootinfo (CPUPPCState *env, ppc4xx_bd_info_t *bd, - uint32_t flags) -{ - CPUState *cs = CPU(ppc_env_get_cpu(env)); - ram_addr_t bdloc; - int i, n; - - /* We put the bd structure at the top of memory */ - if (bd->bi_memsize >= 0x01000000UL) - bdloc = 0x01000000UL - sizeof(struct ppc4xx_bd_info_t); - else - bdloc = bd->bi_memsize - sizeof(struct ppc4xx_bd_info_t); - stl_be_phys(cs->as, bdloc + 0x00, bd->bi_memstart); - stl_be_phys(cs->as, bdloc + 0x04, bd->bi_memsize); - stl_be_phys(cs->as, bdloc + 0x08, bd->bi_flashstart); - stl_be_phys(cs->as, bdloc + 0x0C, bd->bi_flashsize); - stl_be_phys(cs->as, bdloc + 0x10, bd->bi_flashoffset); - stl_be_phys(cs->as, bdloc + 0x14, bd->bi_sramstart); - stl_be_phys(cs->as, bdloc + 0x18, bd->bi_sramsize); - stl_be_phys(cs->as, bdloc + 0x1C, bd->bi_bootflags); - stl_be_phys(cs->as, bdloc + 0x20, bd->bi_ipaddr); - for (i = 0; i < 6; i++) { - stb_phys(cs->as, bdloc + 0x24 + i, bd->bi_enetaddr[i]); - } - stw_be_phys(cs->as, bdloc + 0x2A, bd->bi_ethspeed); - stl_be_phys(cs->as, bdloc + 0x2C, bd->bi_intfreq); - stl_be_phys(cs->as, bdloc + 0x30, bd->bi_busfreq); - stl_be_phys(cs->as, bdloc + 0x34, bd->bi_baudrate); - for (i = 0; i < 4; i++) { - stb_phys(cs->as, bdloc + 0x38 + i, bd->bi_s_version[i]); - } - for (i = 0; i < 32; i++) { - stb_phys(cs->as, bdloc + 0x3C + i, bd->bi_r_version[i]); - } - stl_be_phys(cs->as, bdloc + 0x5C, bd->bi_plb_busfreq); - stl_be_phys(cs->as, bdloc + 0x60, bd->bi_pci_busfreq); - for (i = 0; i < 6; i++) { - stb_phys(cs->as, bdloc + 0x64 + i, bd->bi_pci_enetaddr[i]); - } - n = 0x6A; - if (flags & 0x00000001) { - for (i = 0; i < 6; i++) - stb_phys(cs->as, bdloc + n++, bd->bi_pci_enetaddr2[i]); - } - stl_be_phys(cs->as, bdloc + n, bd->bi_opbfreq); - n += 4; - for (i = 0; i < 2; i++) { - stl_be_phys(cs->as, bdloc + n, bd->bi_iic_fast[i]); - n += 4; - } - - return bdloc; -} - -/*****************************************************************************/ -/* Shared peripherals */ - -/*****************************************************************************/ -/* Peripheral local bus arbitrer */ -enum { - PLB0_BESR = 0x084, - PLB0_BEAR = 0x086, - PLB0_ACR = 0x087, -}; - -typedef struct ppc4xx_plb_t ppc4xx_plb_t; -struct ppc4xx_plb_t { - uint32_t acr; - uint32_t bear; - uint32_t besr; -}; - -static uint32_t dcr_read_plb (void *opaque, int dcrn) -{ - ppc4xx_plb_t *plb; - uint32_t ret; - - plb = opaque; - switch (dcrn) { - case PLB0_ACR: - ret = plb->acr; - break; - case PLB0_BEAR: - ret = plb->bear; - break; - case PLB0_BESR: - ret = plb->besr; - break; - default: - /* Avoid gcc warning */ - ret = 0; - break; - } - - return ret; -} - -static void dcr_write_plb (void *opaque, int dcrn, uint32_t val) -{ - ppc4xx_plb_t *plb; - - plb = opaque; - switch (dcrn) { - case PLB0_ACR: - /* We don't care about the actual parameters written as - * we don't manage any priorities on the bus - */ - plb->acr = val & 0xF8000000; - break; - case PLB0_BEAR: - /* Read only */ - break; - case PLB0_BESR: - /* Write-clear */ - plb->besr &= ~val; - break; - } -} - -static void ppc4xx_plb_reset (void *opaque) -{ - ppc4xx_plb_t *plb; - - plb = opaque; - plb->acr = 0x00000000; - plb->bear = 0x00000000; - plb->besr = 0x00000000; -} - -static void ppc4xx_plb_init(CPUPPCState *env) -{ - ppc4xx_plb_t *plb; - - plb = g_malloc0(sizeof(ppc4xx_plb_t)); - ppc_dcr_register(env, PLB0_ACR, plb, &dcr_read_plb, &dcr_write_plb); - ppc_dcr_register(env, PLB0_BEAR, plb, &dcr_read_plb, &dcr_write_plb); - ppc_dcr_register(env, PLB0_BESR, plb, &dcr_read_plb, &dcr_write_plb); - qemu_register_reset(ppc4xx_plb_reset, plb); -} - -/*****************************************************************************/ -/* PLB to OPB bridge */ -enum { - POB0_BESR0 = 0x0A0, - POB0_BESR1 = 0x0A2, - POB0_BEAR = 0x0A4, -}; - -typedef struct ppc4xx_pob_t ppc4xx_pob_t; -struct ppc4xx_pob_t { - uint32_t bear; - uint32_t besr0; - uint32_t besr1; -}; - -static uint32_t dcr_read_pob (void *opaque, int dcrn) -{ - ppc4xx_pob_t *pob; - uint32_t ret; - - pob = opaque; - switch (dcrn) { - case POB0_BEAR: - ret = pob->bear; - break; - case POB0_BESR0: - ret = pob->besr0; - break; - case POB0_BESR1: - ret = pob->besr1; - break; - default: - /* Avoid gcc warning */ - ret = 0; - break; - } - - return ret; -} - -static void dcr_write_pob (void *opaque, int dcrn, uint32_t val) -{ - ppc4xx_pob_t *pob; - - pob = opaque; - switch (dcrn) { - case POB0_BEAR: - /* Read only */ - break; - case POB0_BESR0: - /* Write-clear */ - pob->besr0 &= ~val; - break; - case POB0_BESR1: - /* Write-clear */ - pob->besr1 &= ~val; - break; - } -} - -static void ppc4xx_pob_reset (void *opaque) -{ - ppc4xx_pob_t *pob; - - pob = opaque; - /* No error */ - pob->bear = 0x00000000; - pob->besr0 = 0x0000000; - pob->besr1 = 0x0000000; -} - -static void ppc4xx_pob_init(CPUPPCState *env) -{ - ppc4xx_pob_t *pob; - - pob = g_malloc0(sizeof(ppc4xx_pob_t)); - ppc_dcr_register(env, POB0_BEAR, pob, &dcr_read_pob, &dcr_write_pob); - ppc_dcr_register(env, POB0_BESR0, pob, &dcr_read_pob, &dcr_write_pob); - ppc_dcr_register(env, POB0_BESR1, pob, &dcr_read_pob, &dcr_write_pob); - qemu_register_reset(ppc4xx_pob_reset, pob); -} - -/*****************************************************************************/ -/* OPB arbitrer */ -typedef struct ppc4xx_opba_t ppc4xx_opba_t; -struct ppc4xx_opba_t { - MemoryRegion io; - uint8_t cr; - uint8_t pr; -}; - -static uint32_t opba_readb (void *opaque, hwaddr addr) -{ - ppc4xx_opba_t *opba; - uint32_t ret; - -#ifdef DEBUG_OPBA - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - opba = opaque; - switch (addr) { - case 0x00: - ret = opba->cr; - break; - case 0x01: - ret = opba->pr; - break; - default: - ret = 0x00; - break; - } - - return ret; -} - -static void opba_writeb (void *opaque, - hwaddr addr, uint32_t value) -{ - ppc4xx_opba_t *opba; - -#ifdef DEBUG_OPBA - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif - opba = opaque; - switch (addr) { - case 0x00: - opba->cr = value & 0xF8; - break; - case 0x01: - opba->pr = value & 0xFF; - break; - default: - break; - } -} - -static uint32_t opba_readw (void *opaque, hwaddr addr) -{ - uint32_t ret; - -#ifdef DEBUG_OPBA - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - ret = opba_readb(opaque, addr) << 8; - ret |= opba_readb(opaque, addr + 1); - - return ret; -} - -static void opba_writew (void *opaque, - hwaddr addr, uint32_t value) -{ -#ifdef DEBUG_OPBA - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif - opba_writeb(opaque, addr, value >> 8); - opba_writeb(opaque, addr + 1, value); -} - -static uint32_t opba_readl (void *opaque, hwaddr addr) -{ - uint32_t ret; - -#ifdef DEBUG_OPBA - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - ret = opba_readb(opaque, addr) << 24; - ret |= opba_readb(opaque, addr + 1) << 16; - - return ret; -} - -static void opba_writel (void *opaque, - hwaddr addr, uint32_t value) -{ -#ifdef DEBUG_OPBA - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif - opba_writeb(opaque, addr, value >> 24); - opba_writeb(opaque, addr + 1, value >> 16); -} - -static const MemoryRegionOps opba_ops = { - .old_mmio = { - .read = { opba_readb, opba_readw, opba_readl, }, - .write = { opba_writeb, opba_writew, opba_writel, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void ppc4xx_opba_reset (void *opaque) -{ - ppc4xx_opba_t *opba; - - opba = opaque; - opba->cr = 0x00; /* No dynamic priorities - park disabled */ - opba->pr = 0x11; -} - -static void ppc4xx_opba_init(hwaddr base) -{ - ppc4xx_opba_t *opba; - - opba = g_malloc0(sizeof(ppc4xx_opba_t)); -#ifdef DEBUG_OPBA - printf("%s: offset " TARGET_FMT_plx "\n", __func__, base); -#endif - memory_region_init_io(&opba->io, NULL, &opba_ops, opba, "opba", 0x002); - memory_region_add_subregion(get_system_memory(), base, &opba->io); - qemu_register_reset(ppc4xx_opba_reset, opba); -} - -/*****************************************************************************/ -/* Code decompression controller */ -/* XXX: TODO */ - -/*****************************************************************************/ -/* Peripheral controller */ -typedef struct ppc4xx_ebc_t ppc4xx_ebc_t; -struct ppc4xx_ebc_t { - uint32_t addr; - uint32_t bcr[8]; - uint32_t bap[8]; - uint32_t bear; - uint32_t besr0; - uint32_t besr1; - uint32_t cfg; -}; - -enum { - EBC0_CFGADDR = 0x012, - EBC0_CFGDATA = 0x013, -}; - -static uint32_t dcr_read_ebc (void *opaque, int dcrn) -{ - ppc4xx_ebc_t *ebc; - uint32_t ret; - - ebc = opaque; - switch (dcrn) { - case EBC0_CFGADDR: - ret = ebc->addr; - break; - case EBC0_CFGDATA: - switch (ebc->addr) { - case 0x00: /* B0CR */ - ret = ebc->bcr[0]; - break; - case 0x01: /* B1CR */ - ret = ebc->bcr[1]; - break; - case 0x02: /* B2CR */ - ret = ebc->bcr[2]; - break; - case 0x03: /* B3CR */ - ret = ebc->bcr[3]; - break; - case 0x04: /* B4CR */ - ret = ebc->bcr[4]; - break; - case 0x05: /* B5CR */ - ret = ebc->bcr[5]; - break; - case 0x06: /* B6CR */ - ret = ebc->bcr[6]; - break; - case 0x07: /* B7CR */ - ret = ebc->bcr[7]; - break; - case 0x10: /* B0AP */ - ret = ebc->bap[0]; - break; - case 0x11: /* B1AP */ - ret = ebc->bap[1]; - break; - case 0x12: /* B2AP */ - ret = ebc->bap[2]; - break; - case 0x13: /* B3AP */ - ret = ebc->bap[3]; - break; - case 0x14: /* B4AP */ - ret = ebc->bap[4]; - break; - case 0x15: /* B5AP */ - ret = ebc->bap[5]; - break; - case 0x16: /* B6AP */ - ret = ebc->bap[6]; - break; - case 0x17: /* B7AP */ - ret = ebc->bap[7]; - break; - case 0x20: /* BEAR */ - ret = ebc->bear; - break; - case 0x21: /* BESR0 */ - ret = ebc->besr0; - break; - case 0x22: /* BESR1 */ - ret = ebc->besr1; - break; - case 0x23: /* CFG */ - ret = ebc->cfg; - break; - default: - ret = 0x00000000; - break; - } - break; - default: - ret = 0x00000000; - break; - } - - return ret; -} - -static void dcr_write_ebc (void *opaque, int dcrn, uint32_t val) -{ - ppc4xx_ebc_t *ebc; - - ebc = opaque; - switch (dcrn) { - case EBC0_CFGADDR: - ebc->addr = val; - break; - case EBC0_CFGDATA: - switch (ebc->addr) { - case 0x00: /* B0CR */ - break; - case 0x01: /* B1CR */ - break; - case 0x02: /* B2CR */ - break; - case 0x03: /* B3CR */ - break; - case 0x04: /* B4CR */ - break; - case 0x05: /* B5CR */ - break; - case 0x06: /* B6CR */ - break; - case 0x07: /* B7CR */ - break; - case 0x10: /* B0AP */ - break; - case 0x11: /* B1AP */ - break; - case 0x12: /* B2AP */ - break; - case 0x13: /* B3AP */ - break; - case 0x14: /* B4AP */ - break; - case 0x15: /* B5AP */ - break; - case 0x16: /* B6AP */ - break; - case 0x17: /* B7AP */ - break; - case 0x20: /* BEAR */ - break; - case 0x21: /* BESR0 */ - break; - case 0x22: /* BESR1 */ - break; - case 0x23: /* CFG */ - break; - default: - break; - } - break; - default: - break; - } -} - -static void ebc_reset (void *opaque) -{ - ppc4xx_ebc_t *ebc; - int i; - - ebc = opaque; - ebc->addr = 0x00000000; - ebc->bap[0] = 0x7F8FFE80; - ebc->bcr[0] = 0xFFE28000; - for (i = 0; i < 8; i++) { - ebc->bap[i] = 0x00000000; - ebc->bcr[i] = 0x00000000; - } - ebc->besr0 = 0x00000000; - ebc->besr1 = 0x00000000; - ebc->cfg = 0x80400000; -} - -static void ppc405_ebc_init(CPUPPCState *env) -{ - ppc4xx_ebc_t *ebc; - - ebc = g_malloc0(sizeof(ppc4xx_ebc_t)); - qemu_register_reset(&ebc_reset, ebc); - ppc_dcr_register(env, EBC0_CFGADDR, - ebc, &dcr_read_ebc, &dcr_write_ebc); - ppc_dcr_register(env, EBC0_CFGDATA, - ebc, &dcr_read_ebc, &dcr_write_ebc); -} - -/*****************************************************************************/ -/* DMA controller */ -enum { - DMA0_CR0 = 0x100, - DMA0_CT0 = 0x101, - DMA0_DA0 = 0x102, - DMA0_SA0 = 0x103, - DMA0_SG0 = 0x104, - DMA0_CR1 = 0x108, - DMA0_CT1 = 0x109, - DMA0_DA1 = 0x10A, - DMA0_SA1 = 0x10B, - DMA0_SG1 = 0x10C, - DMA0_CR2 = 0x110, - DMA0_CT2 = 0x111, - DMA0_DA2 = 0x112, - DMA0_SA2 = 0x113, - DMA0_SG2 = 0x114, - DMA0_CR3 = 0x118, - DMA0_CT3 = 0x119, - DMA0_DA3 = 0x11A, - DMA0_SA3 = 0x11B, - DMA0_SG3 = 0x11C, - DMA0_SR = 0x120, - DMA0_SGC = 0x123, - DMA0_SLP = 0x125, - DMA0_POL = 0x126, -}; - -typedef struct ppc405_dma_t ppc405_dma_t; -struct ppc405_dma_t { - qemu_irq irqs[4]; - uint32_t cr[4]; - uint32_t ct[4]; - uint32_t da[4]; - uint32_t sa[4]; - uint32_t sg[4]; - uint32_t sr; - uint32_t sgc; - uint32_t slp; - uint32_t pol; -}; - -static uint32_t dcr_read_dma (void *opaque, int dcrn) -{ - return 0; -} - -static void dcr_write_dma (void *opaque, int dcrn, uint32_t val) -{ -} - -static void ppc405_dma_reset (void *opaque) -{ - ppc405_dma_t *dma; - int i; - - dma = opaque; - for (i = 0; i < 4; i++) { - dma->cr[i] = 0x00000000; - dma->ct[i] = 0x00000000; - dma->da[i] = 0x00000000; - dma->sa[i] = 0x00000000; - dma->sg[i] = 0x00000000; - } - dma->sr = 0x00000000; - dma->sgc = 0x00000000; - dma->slp = 0x7C000000; - dma->pol = 0x00000000; -} - -static void ppc405_dma_init(CPUPPCState *env, qemu_irq irqs[4]) -{ - ppc405_dma_t *dma; - - dma = g_malloc0(sizeof(ppc405_dma_t)); - memcpy(dma->irqs, irqs, 4 * sizeof(qemu_irq)); - qemu_register_reset(&ppc405_dma_reset, dma); - ppc_dcr_register(env, DMA0_CR0, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_CT0, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_DA0, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SA0, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SG0, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_CR1, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_CT1, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_DA1, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SA1, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SG1, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_CR2, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_CT2, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_DA2, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SA2, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SG2, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_CR3, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_CT3, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_DA3, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SA3, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SG3, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SR, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SGC, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_SLP, - dma, &dcr_read_dma, &dcr_write_dma); - ppc_dcr_register(env, DMA0_POL, - dma, &dcr_read_dma, &dcr_write_dma); -} - -/*****************************************************************************/ -/* GPIO */ -typedef struct ppc405_gpio_t ppc405_gpio_t; -struct ppc405_gpio_t { - MemoryRegion io; - uint32_t or; - uint32_t tcr; - uint32_t osrh; - uint32_t osrl; - uint32_t tsrh; - uint32_t tsrl; - uint32_t odr; - uint32_t ir; - uint32_t rr1; - uint32_t isr1h; - uint32_t isr1l; -}; - -static uint32_t ppc405_gpio_readb (void *opaque, hwaddr addr) -{ -#ifdef DEBUG_GPIO - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - - return 0; -} - -static void ppc405_gpio_writeb (void *opaque, - hwaddr addr, uint32_t value) -{ -#ifdef DEBUG_GPIO - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif -} - -static uint32_t ppc405_gpio_readw (void *opaque, hwaddr addr) -{ -#ifdef DEBUG_GPIO - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - - return 0; -} - -static void ppc405_gpio_writew (void *opaque, - hwaddr addr, uint32_t value) -{ -#ifdef DEBUG_GPIO - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif -} - -static uint32_t ppc405_gpio_readl (void *opaque, hwaddr addr) -{ -#ifdef DEBUG_GPIO - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - - return 0; -} - -static void ppc405_gpio_writel (void *opaque, - hwaddr addr, uint32_t value) -{ -#ifdef DEBUG_GPIO - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif -} - -static const MemoryRegionOps ppc405_gpio_ops = { - .old_mmio = { - .read = { ppc405_gpio_readb, ppc405_gpio_readw, ppc405_gpio_readl, }, - .write = { ppc405_gpio_writeb, ppc405_gpio_writew, ppc405_gpio_writel, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void ppc405_gpio_reset (void *opaque) -{ -} - -static void ppc405_gpio_init(hwaddr base) -{ - ppc405_gpio_t *gpio; - - gpio = g_malloc0(sizeof(ppc405_gpio_t)); -#ifdef DEBUG_GPIO - printf("%s: offset " TARGET_FMT_plx "\n", __func__, base); -#endif - memory_region_init_io(&gpio->io, NULL, &ppc405_gpio_ops, gpio, "pgio", 0x038); - memory_region_add_subregion(get_system_memory(), base, &gpio->io); - qemu_register_reset(&ppc405_gpio_reset, gpio); -} - -/*****************************************************************************/ -/* On Chip Memory */ -enum { - OCM0_ISARC = 0x018, - OCM0_ISACNTL = 0x019, - OCM0_DSARC = 0x01A, - OCM0_DSACNTL = 0x01B, -}; - -typedef struct ppc405_ocm_t ppc405_ocm_t; -struct ppc405_ocm_t { - MemoryRegion ram; - MemoryRegion isarc_ram; - MemoryRegion dsarc_ram; - uint32_t isarc; - uint32_t isacntl; - uint32_t dsarc; - uint32_t dsacntl; -}; - -static void ocm_update_mappings (ppc405_ocm_t *ocm, - uint32_t isarc, uint32_t isacntl, - uint32_t dsarc, uint32_t dsacntl) -{ -#ifdef DEBUG_OCM - printf("OCM update ISA %08" PRIx32 " %08" PRIx32 " (%08" PRIx32 - " %08" PRIx32 ") DSA %08" PRIx32 " %08" PRIx32 - " (%08" PRIx32 " %08" PRIx32 ")\n", - isarc, isacntl, dsarc, dsacntl, - ocm->isarc, ocm->isacntl, ocm->dsarc, ocm->dsacntl); -#endif - if (ocm->isarc != isarc || - (ocm->isacntl & 0x80000000) != (isacntl & 0x80000000)) { - if (ocm->isacntl & 0x80000000) { - /* Unmap previously assigned memory region */ - printf("OCM unmap ISA %08" PRIx32 "\n", ocm->isarc); - memory_region_del_subregion(get_system_memory(), &ocm->isarc_ram); - } - if (isacntl & 0x80000000) { - /* Map new instruction memory region */ -#ifdef DEBUG_OCM - printf("OCM map ISA %08" PRIx32 "\n", isarc); -#endif - memory_region_add_subregion(get_system_memory(), isarc, - &ocm->isarc_ram); - } - } - if (ocm->dsarc != dsarc || - (ocm->dsacntl & 0x80000000) != (dsacntl & 0x80000000)) { - if (ocm->dsacntl & 0x80000000) { - /* Beware not to unmap the region we just mapped */ - if (!(isacntl & 0x80000000) || ocm->dsarc != isarc) { - /* Unmap previously assigned memory region */ -#ifdef DEBUG_OCM - printf("OCM unmap DSA %08" PRIx32 "\n", ocm->dsarc); -#endif - memory_region_del_subregion(get_system_memory(), - &ocm->dsarc_ram); - } - } - if (dsacntl & 0x80000000) { - /* Beware not to remap the region we just mapped */ - if (!(isacntl & 0x80000000) || dsarc != isarc) { - /* Map new data memory region */ -#ifdef DEBUG_OCM - printf("OCM map DSA %08" PRIx32 "\n", dsarc); -#endif - memory_region_add_subregion(get_system_memory(), dsarc, - &ocm->dsarc_ram); - } - } - } -} - -static uint32_t dcr_read_ocm (void *opaque, int dcrn) -{ - ppc405_ocm_t *ocm; - uint32_t ret; - - ocm = opaque; - switch (dcrn) { - case OCM0_ISARC: - ret = ocm->isarc; - break; - case OCM0_ISACNTL: - ret = ocm->isacntl; - break; - case OCM0_DSARC: - ret = ocm->dsarc; - break; - case OCM0_DSACNTL: - ret = ocm->dsacntl; - break; - default: - ret = 0; - break; - } - - return ret; -} - -static void dcr_write_ocm (void *opaque, int dcrn, uint32_t val) -{ - ppc405_ocm_t *ocm; - uint32_t isarc, dsarc, isacntl, dsacntl; - - ocm = opaque; - isarc = ocm->isarc; - dsarc = ocm->dsarc; - isacntl = ocm->isacntl; - dsacntl = ocm->dsacntl; - switch (dcrn) { - case OCM0_ISARC: - isarc = val & 0xFC000000; - break; - case OCM0_ISACNTL: - isacntl = val & 0xC0000000; - break; - case OCM0_DSARC: - isarc = val & 0xFC000000; - break; - case OCM0_DSACNTL: - isacntl = val & 0xC0000000; - break; - } - ocm_update_mappings(ocm, isarc, isacntl, dsarc, dsacntl); - ocm->isarc = isarc; - ocm->dsarc = dsarc; - ocm->isacntl = isacntl; - ocm->dsacntl = dsacntl; -} - -static void ocm_reset (void *opaque) -{ - ppc405_ocm_t *ocm; - uint32_t isarc, dsarc, isacntl, dsacntl; - - ocm = opaque; - isarc = 0x00000000; - isacntl = 0x00000000; - dsarc = 0x00000000; - dsacntl = 0x00000000; - ocm_update_mappings(ocm, isarc, isacntl, dsarc, dsacntl); - ocm->isarc = isarc; - ocm->dsarc = dsarc; - ocm->isacntl = isacntl; - ocm->dsacntl = dsacntl; -} - -static void ppc405_ocm_init(CPUPPCState *env) -{ - ppc405_ocm_t *ocm; - - 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_fatal); - vmstate_register_ram_global(&ocm->isarc_ram); - memory_region_init_alias(&ocm->dsarc_ram, NULL, "ppc405.dsarc", &ocm->isarc_ram, - 0, 4096); - qemu_register_reset(&ocm_reset, ocm); - ppc_dcr_register(env, OCM0_ISARC, - ocm, &dcr_read_ocm, &dcr_write_ocm); - ppc_dcr_register(env, OCM0_ISACNTL, - ocm, &dcr_read_ocm, &dcr_write_ocm); - ppc_dcr_register(env, OCM0_DSARC, - ocm, &dcr_read_ocm, &dcr_write_ocm); - ppc_dcr_register(env, OCM0_DSACNTL, - ocm, &dcr_read_ocm, &dcr_write_ocm); -} - -/*****************************************************************************/ -/* I2C controller */ -typedef struct ppc4xx_i2c_t ppc4xx_i2c_t; -struct ppc4xx_i2c_t { - qemu_irq irq; - MemoryRegion iomem; - uint8_t mdata; - uint8_t lmadr; - uint8_t hmadr; - uint8_t cntl; - uint8_t mdcntl; - uint8_t sts; - uint8_t extsts; - uint8_t sdata; - uint8_t lsadr; - uint8_t hsadr; - uint8_t clkdiv; - uint8_t intrmsk; - uint8_t xfrcnt; - uint8_t xtcntlss; - uint8_t directcntl; -}; - -static uint32_t ppc4xx_i2c_readb (void *opaque, hwaddr addr) -{ - ppc4xx_i2c_t *i2c; - uint32_t ret; - -#ifdef DEBUG_I2C - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - i2c = opaque; - switch (addr) { - case 0x00: - // i2c_readbyte(&i2c->mdata); - ret = i2c->mdata; - break; - case 0x02: - ret = i2c->sdata; - break; - case 0x04: - ret = i2c->lmadr; - break; - case 0x05: - ret = i2c->hmadr; - break; - case 0x06: - ret = i2c->cntl; - break; - case 0x07: - ret = i2c->mdcntl; - break; - case 0x08: - ret = i2c->sts; - break; - case 0x09: - ret = i2c->extsts; - break; - case 0x0A: - ret = i2c->lsadr; - break; - case 0x0B: - ret = i2c->hsadr; - break; - case 0x0C: - ret = i2c->clkdiv; - break; - case 0x0D: - ret = i2c->intrmsk; - break; - case 0x0E: - ret = i2c->xfrcnt; - break; - case 0x0F: - ret = i2c->xtcntlss; - break; - case 0x10: - ret = i2c->directcntl; - break; - default: - ret = 0x00; - break; - } -#ifdef DEBUG_I2C - printf("%s: addr " TARGET_FMT_plx " %02" PRIx32 "\n", __func__, addr, ret); -#endif - - return ret; -} - -static void ppc4xx_i2c_writeb (void *opaque, - hwaddr addr, uint32_t value) -{ - ppc4xx_i2c_t *i2c; - -#ifdef DEBUG_I2C - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif - i2c = opaque; - switch (addr) { - case 0x00: - i2c->mdata = value; - // i2c_sendbyte(&i2c->mdata); - break; - case 0x02: - i2c->sdata = value; - break; - case 0x04: - i2c->lmadr = value; - break; - case 0x05: - i2c->hmadr = value; - break; - case 0x06: - i2c->cntl = value; - break; - case 0x07: - i2c->mdcntl = value & 0xDF; - break; - case 0x08: - i2c->sts &= ~(value & 0x0A); - break; - case 0x09: - i2c->extsts &= ~(value & 0x8F); - break; - case 0x0A: - i2c->lsadr = value; - break; - case 0x0B: - i2c->hsadr = value; - break; - case 0x0C: - i2c->clkdiv = value; - break; - case 0x0D: - i2c->intrmsk = value; - break; - case 0x0E: - i2c->xfrcnt = value & 0x77; - break; - case 0x0F: - i2c->xtcntlss = value; - break; - case 0x10: - i2c->directcntl = value & 0x7; - break; - } -} - -static uint32_t ppc4xx_i2c_readw (void *opaque, hwaddr addr) -{ - uint32_t ret; - -#ifdef DEBUG_I2C - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - ret = ppc4xx_i2c_readb(opaque, addr) << 8; - ret |= ppc4xx_i2c_readb(opaque, addr + 1); - - return ret; -} - -static void ppc4xx_i2c_writew (void *opaque, - hwaddr addr, uint32_t value) -{ -#ifdef DEBUG_I2C - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif - ppc4xx_i2c_writeb(opaque, addr, value >> 8); - ppc4xx_i2c_writeb(opaque, addr + 1, value); -} - -static uint32_t ppc4xx_i2c_readl (void *opaque, hwaddr addr) -{ - uint32_t ret; - -#ifdef DEBUG_I2C - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - ret = ppc4xx_i2c_readb(opaque, addr) << 24; - ret |= ppc4xx_i2c_readb(opaque, addr + 1) << 16; - ret |= ppc4xx_i2c_readb(opaque, addr + 2) << 8; - ret |= ppc4xx_i2c_readb(opaque, addr + 3); - - return ret; -} - -static void ppc4xx_i2c_writel (void *opaque, - hwaddr addr, uint32_t value) -{ -#ifdef DEBUG_I2C - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif - ppc4xx_i2c_writeb(opaque, addr, value >> 24); - ppc4xx_i2c_writeb(opaque, addr + 1, value >> 16); - ppc4xx_i2c_writeb(opaque, addr + 2, value >> 8); - ppc4xx_i2c_writeb(opaque, addr + 3, value); -} - -static const MemoryRegionOps i2c_ops = { - .old_mmio = { - .read = { ppc4xx_i2c_readb, ppc4xx_i2c_readw, ppc4xx_i2c_readl, }, - .write = { ppc4xx_i2c_writeb, ppc4xx_i2c_writew, ppc4xx_i2c_writel, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void ppc4xx_i2c_reset (void *opaque) -{ - ppc4xx_i2c_t *i2c; - - i2c = opaque; - i2c->mdata = 0x00; - i2c->sdata = 0x00; - i2c->cntl = 0x00; - i2c->mdcntl = 0x00; - i2c->sts = 0x00; - i2c->extsts = 0x00; - i2c->clkdiv = 0x00; - i2c->xfrcnt = 0x00; - i2c->directcntl = 0x0F; -} - -static void ppc405_i2c_init(hwaddr base, qemu_irq irq) -{ - ppc4xx_i2c_t *i2c; - - i2c = g_malloc0(sizeof(ppc4xx_i2c_t)); - i2c->irq = irq; -#ifdef DEBUG_I2C - printf("%s: offset " TARGET_FMT_plx "\n", __func__, base); -#endif - memory_region_init_io(&i2c->iomem, NULL, &i2c_ops, i2c, "i2c", 0x011); - memory_region_add_subregion(get_system_memory(), base, &i2c->iomem); - qemu_register_reset(ppc4xx_i2c_reset, i2c); -} - -/*****************************************************************************/ -/* General purpose timers */ -typedef struct ppc4xx_gpt_t ppc4xx_gpt_t; -struct ppc4xx_gpt_t { - MemoryRegion iomem; - int64_t tb_offset; - uint32_t tb_freq; - QEMUTimer *timer; - qemu_irq irqs[5]; - uint32_t oe; - uint32_t ol; - uint32_t im; - uint32_t is; - uint32_t ie; - uint32_t comp[5]; - uint32_t mask[5]; -}; - -static uint32_t ppc4xx_gpt_readb (void *opaque, hwaddr addr) -{ -#ifdef DEBUG_GPT - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - /* XXX: generate a bus fault */ - return -1; -} - -static void ppc4xx_gpt_writeb (void *opaque, - hwaddr addr, uint32_t value) -{ -#ifdef DEBUG_I2C - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif - /* XXX: generate a bus fault */ -} - -static uint32_t ppc4xx_gpt_readw (void *opaque, hwaddr addr) -{ -#ifdef DEBUG_GPT - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - /* XXX: generate a bus fault */ - return -1; -} - -static void ppc4xx_gpt_writew (void *opaque, - hwaddr addr, uint32_t value) -{ -#ifdef DEBUG_I2C - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif - /* XXX: generate a bus fault */ -} - -static int ppc4xx_gpt_compare (ppc4xx_gpt_t *gpt, int n) -{ - /* XXX: TODO */ - return 0; -} - -static void ppc4xx_gpt_set_output (ppc4xx_gpt_t *gpt, int n, int level) -{ - /* XXX: TODO */ -} - -static void ppc4xx_gpt_set_outputs (ppc4xx_gpt_t *gpt) -{ - uint32_t mask; - int i; - - mask = 0x80000000; - for (i = 0; i < 5; i++) { - if (gpt->oe & mask) { - /* Output is enabled */ - if (ppc4xx_gpt_compare(gpt, i)) { - /* Comparison is OK */ - ppc4xx_gpt_set_output(gpt, i, gpt->ol & mask); - } else { - /* Comparison is KO */ - ppc4xx_gpt_set_output(gpt, i, gpt->ol & mask ? 0 : 1); - } - } - mask = mask >> 1; - } -} - -static void ppc4xx_gpt_set_irqs (ppc4xx_gpt_t *gpt) -{ - uint32_t mask; - int i; - - mask = 0x00008000; - for (i = 0; i < 5; i++) { - if (gpt->is & gpt->im & mask) - qemu_irq_raise(gpt->irqs[i]); - else - qemu_irq_lower(gpt->irqs[i]); - mask = mask >> 1; - } -} - -static void ppc4xx_gpt_compute_timer (ppc4xx_gpt_t *gpt) -{ - /* XXX: TODO */ -} - -static uint32_t ppc4xx_gpt_readl (void *opaque, hwaddr addr) -{ - ppc4xx_gpt_t *gpt; - uint32_t ret; - int idx; - -#ifdef DEBUG_GPT - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - gpt = opaque; - switch (addr) { - case 0x00: - /* Time base counter */ - ret = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + gpt->tb_offset, - gpt->tb_freq, NANOSECONDS_PER_SECOND); - break; - case 0x10: - /* Output enable */ - ret = gpt->oe; - break; - case 0x14: - /* Output level */ - ret = gpt->ol; - break; - case 0x18: - /* Interrupt mask */ - ret = gpt->im; - break; - case 0x1C: - case 0x20: - /* Interrupt status */ - ret = gpt->is; - break; - case 0x24: - /* Interrupt enable */ - ret = gpt->ie; - break; - case 0x80 ... 0x90: - /* Compare timer */ - idx = (addr - 0x80) >> 2; - ret = gpt->comp[idx]; - break; - case 0xC0 ... 0xD0: - /* Compare mask */ - idx = (addr - 0xC0) >> 2; - ret = gpt->mask[idx]; - break; - default: - ret = -1; - break; - } - - return ret; -} - -static void ppc4xx_gpt_writel (void *opaque, - hwaddr addr, uint32_t value) -{ - ppc4xx_gpt_t *gpt; - int idx; - -#ifdef DEBUG_I2C - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif - gpt = opaque; - switch (addr) { - case 0x00: - /* Time base counter */ - gpt->tb_offset = muldiv64(value, NANOSECONDS_PER_SECOND, gpt->tb_freq) - - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ppc4xx_gpt_compute_timer(gpt); - break; - case 0x10: - /* Output enable */ - gpt->oe = value & 0xF8000000; - ppc4xx_gpt_set_outputs(gpt); - break; - case 0x14: - /* Output level */ - gpt->ol = value & 0xF8000000; - ppc4xx_gpt_set_outputs(gpt); - break; - case 0x18: - /* Interrupt mask */ - gpt->im = value & 0x0000F800; - break; - case 0x1C: - /* Interrupt status set */ - gpt->is |= value & 0x0000F800; - ppc4xx_gpt_set_irqs(gpt); - break; - case 0x20: - /* Interrupt status clear */ - gpt->is &= ~(value & 0x0000F800); - ppc4xx_gpt_set_irqs(gpt); - break; - case 0x24: - /* Interrupt enable */ - gpt->ie = value & 0x0000F800; - ppc4xx_gpt_set_irqs(gpt); - break; - case 0x80 ... 0x90: - /* Compare timer */ - idx = (addr - 0x80) >> 2; - gpt->comp[idx] = value & 0xF8000000; - ppc4xx_gpt_compute_timer(gpt); - break; - case 0xC0 ... 0xD0: - /* Compare mask */ - idx = (addr - 0xC0) >> 2; - gpt->mask[idx] = value & 0xF8000000; - ppc4xx_gpt_compute_timer(gpt); - break; - } -} - -static const MemoryRegionOps gpt_ops = { - .old_mmio = { - .read = { ppc4xx_gpt_readb, ppc4xx_gpt_readw, ppc4xx_gpt_readl, }, - .write = { ppc4xx_gpt_writeb, ppc4xx_gpt_writew, ppc4xx_gpt_writel, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void ppc4xx_gpt_cb (void *opaque) -{ - ppc4xx_gpt_t *gpt; - - gpt = opaque; - ppc4xx_gpt_set_irqs(gpt); - ppc4xx_gpt_set_outputs(gpt); - ppc4xx_gpt_compute_timer(gpt); -} - -static void ppc4xx_gpt_reset (void *opaque) -{ - ppc4xx_gpt_t *gpt; - int i; - - gpt = opaque; - timer_del(gpt->timer); - gpt->oe = 0x00000000; - gpt->ol = 0x00000000; - gpt->im = 0x00000000; - gpt->is = 0x00000000; - gpt->ie = 0x00000000; - for (i = 0; i < 5; i++) { - gpt->comp[i] = 0x00000000; - gpt->mask[i] = 0x00000000; - } -} - -static void ppc4xx_gpt_init(hwaddr base, qemu_irq irqs[5]) -{ - ppc4xx_gpt_t *gpt; - int i; - - gpt = g_malloc0(sizeof(ppc4xx_gpt_t)); - for (i = 0; i < 5; i++) { - gpt->irqs[i] = irqs[i]; - } - gpt->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &ppc4xx_gpt_cb, gpt); -#ifdef DEBUG_GPT - printf("%s: offset " TARGET_FMT_plx "\n", __func__, base); -#endif - memory_region_init_io(&gpt->iomem, NULL, &gpt_ops, gpt, "gpt", 0x0d4); - memory_region_add_subregion(get_system_memory(), base, &gpt->iomem); - qemu_register_reset(ppc4xx_gpt_reset, gpt); -} - -/*****************************************************************************/ -/* MAL */ -enum { - MAL0_CFG = 0x180, - MAL0_ESR = 0x181, - MAL0_IER = 0x182, - MAL0_TXCASR = 0x184, - MAL0_TXCARR = 0x185, - MAL0_TXEOBISR = 0x186, - MAL0_TXDEIR = 0x187, - MAL0_RXCASR = 0x190, - MAL0_RXCARR = 0x191, - MAL0_RXEOBISR = 0x192, - MAL0_RXDEIR = 0x193, - MAL0_TXCTP0R = 0x1A0, - MAL0_TXCTP1R = 0x1A1, - MAL0_TXCTP2R = 0x1A2, - MAL0_TXCTP3R = 0x1A3, - MAL0_RXCTP0R = 0x1C0, - MAL0_RXCTP1R = 0x1C1, - MAL0_RCBS0 = 0x1E0, - MAL0_RCBS1 = 0x1E1, -}; - -typedef struct ppc40x_mal_t ppc40x_mal_t; -struct ppc40x_mal_t { - qemu_irq irqs[4]; - uint32_t cfg; - uint32_t esr; - uint32_t ier; - uint32_t txcasr; - uint32_t txcarr; - uint32_t txeobisr; - uint32_t txdeir; - uint32_t rxcasr; - uint32_t rxcarr; - uint32_t rxeobisr; - uint32_t rxdeir; - uint32_t txctpr[4]; - uint32_t rxctpr[2]; - uint32_t rcbs[2]; -}; - -static void ppc40x_mal_reset (void *opaque); - -static uint32_t dcr_read_mal (void *opaque, int dcrn) -{ - ppc40x_mal_t *mal; - uint32_t ret; - - mal = opaque; - switch (dcrn) { - case MAL0_CFG: - ret = mal->cfg; - break; - case MAL0_ESR: - ret = mal->esr; - break; - case MAL0_IER: - ret = mal->ier; - break; - case MAL0_TXCASR: - ret = mal->txcasr; - break; - case MAL0_TXCARR: - ret = mal->txcarr; - break; - case MAL0_TXEOBISR: - ret = mal->txeobisr; - break; - case MAL0_TXDEIR: - ret = mal->txdeir; - break; - case MAL0_RXCASR: - ret = mal->rxcasr; - break; - case MAL0_RXCARR: - ret = mal->rxcarr; - break; - case MAL0_RXEOBISR: - ret = mal->rxeobisr; - break; - case MAL0_RXDEIR: - ret = mal->rxdeir; - break; - case MAL0_TXCTP0R: - ret = mal->txctpr[0]; - break; - case MAL0_TXCTP1R: - ret = mal->txctpr[1]; - break; - case MAL0_TXCTP2R: - ret = mal->txctpr[2]; - break; - case MAL0_TXCTP3R: - ret = mal->txctpr[3]; - break; - case MAL0_RXCTP0R: - ret = mal->rxctpr[0]; - break; - case MAL0_RXCTP1R: - ret = mal->rxctpr[1]; - break; - case MAL0_RCBS0: - ret = mal->rcbs[0]; - break; - case MAL0_RCBS1: - ret = mal->rcbs[1]; - break; - default: - ret = 0; - break; - } - - return ret; -} - -static void dcr_write_mal (void *opaque, int dcrn, uint32_t val) -{ - ppc40x_mal_t *mal; - int idx; - - mal = opaque; - switch (dcrn) { - case MAL0_CFG: - if (val & 0x80000000) - ppc40x_mal_reset(mal); - mal->cfg = val & 0x00FFC087; - break; - case MAL0_ESR: - /* Read/clear */ - mal->esr &= ~val; - break; - case MAL0_IER: - mal->ier = val & 0x0000001F; - break; - case MAL0_TXCASR: - mal->txcasr = val & 0xF0000000; - break; - case MAL0_TXCARR: - mal->txcarr = val & 0xF0000000; - break; - case MAL0_TXEOBISR: - /* Read/clear */ - mal->txeobisr &= ~val; - break; - case MAL0_TXDEIR: - /* Read/clear */ - mal->txdeir &= ~val; - break; - case MAL0_RXCASR: - mal->rxcasr = val & 0xC0000000; - break; - case MAL0_RXCARR: - mal->rxcarr = val & 0xC0000000; - break; - case MAL0_RXEOBISR: - /* Read/clear */ - mal->rxeobisr &= ~val; - break; - case MAL0_RXDEIR: - /* Read/clear */ - mal->rxdeir &= ~val; - break; - case MAL0_TXCTP0R: - idx = 0; - goto update_tx_ptr; - case MAL0_TXCTP1R: - idx = 1; - goto update_tx_ptr; - case MAL0_TXCTP2R: - idx = 2; - goto update_tx_ptr; - case MAL0_TXCTP3R: - idx = 3; - update_tx_ptr: - mal->txctpr[idx] = val; - break; - case MAL0_RXCTP0R: - idx = 0; - goto update_rx_ptr; - case MAL0_RXCTP1R: - idx = 1; - update_rx_ptr: - mal->rxctpr[idx] = val; - break; - case MAL0_RCBS0: - idx = 0; - goto update_rx_size; - case MAL0_RCBS1: - idx = 1; - update_rx_size: - mal->rcbs[idx] = val & 0x000000FF; - break; - } -} - -static void ppc40x_mal_reset (void *opaque) -{ - ppc40x_mal_t *mal; - - mal = opaque; - mal->cfg = 0x0007C000; - mal->esr = 0x00000000; - mal->ier = 0x00000000; - mal->rxcasr = 0x00000000; - mal->rxdeir = 0x00000000; - mal->rxeobisr = 0x00000000; - mal->txcasr = 0x00000000; - mal->txdeir = 0x00000000; - mal->txeobisr = 0x00000000; -} - -static void ppc405_mal_init(CPUPPCState *env, qemu_irq irqs[4]) -{ - ppc40x_mal_t *mal; - int i; - - mal = g_malloc0(sizeof(ppc40x_mal_t)); - for (i = 0; i < 4; i++) - mal->irqs[i] = irqs[i]; - qemu_register_reset(&ppc40x_mal_reset, mal); - ppc_dcr_register(env, MAL0_CFG, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_ESR, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_IER, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_TXCASR, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_TXCARR, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_TXEOBISR, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_TXDEIR, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_RXCASR, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_RXCARR, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_RXEOBISR, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_RXDEIR, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_TXCTP0R, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_TXCTP1R, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_TXCTP2R, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_TXCTP3R, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_RXCTP0R, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_RXCTP1R, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_RCBS0, - mal, &dcr_read_mal, &dcr_write_mal); - ppc_dcr_register(env, MAL0_RCBS1, - mal, &dcr_read_mal, &dcr_write_mal); -} - -/*****************************************************************************/ -/* SPR */ -void ppc40x_core_reset(PowerPCCPU *cpu) -{ - CPUPPCState *env = &cpu->env; - target_ulong dbsr; - - printf("Reset PowerPC core\n"); - cpu_interrupt(CPU(cpu), CPU_INTERRUPT_RESET); - dbsr = env->spr[SPR_40x_DBSR]; - dbsr &= ~0x00000300; - dbsr |= 0x00000100; - env->spr[SPR_40x_DBSR] = dbsr; -} - -void ppc40x_chip_reset(PowerPCCPU *cpu) -{ - CPUPPCState *env = &cpu->env; - target_ulong dbsr; - - printf("Reset PowerPC chip\n"); - cpu_interrupt(CPU(cpu), CPU_INTERRUPT_RESET); - /* XXX: TODO reset all internal peripherals */ - dbsr = env->spr[SPR_40x_DBSR]; - dbsr &= ~0x00000300; - dbsr |= 0x00000200; - env->spr[SPR_40x_DBSR] = dbsr; -} - -void ppc40x_system_reset(PowerPCCPU *cpu) -{ - printf("Reset PowerPC system\n"); - qemu_system_reset_request(); -} - -void store_40x_dbcr0 (CPUPPCState *env, uint32_t val) -{ - PowerPCCPU *cpu = ppc_env_get_cpu(env); - - switch ((val >> 28) & 0x3) { - case 0x0: - /* No action */ - break; - case 0x1: - /* Core reset */ - ppc40x_core_reset(cpu); - break; - case 0x2: - /* Chip reset */ - ppc40x_chip_reset(cpu); - break; - case 0x3: - /* System reset */ - ppc40x_system_reset(cpu); - break; - } -} - -/*****************************************************************************/ -/* PowerPC 405CR */ -enum { - PPC405CR_CPC0_PLLMR = 0x0B0, - PPC405CR_CPC0_CR0 = 0x0B1, - PPC405CR_CPC0_CR1 = 0x0B2, - PPC405CR_CPC0_PSR = 0x0B4, - PPC405CR_CPC0_JTAGID = 0x0B5, - PPC405CR_CPC0_ER = 0x0B9, - PPC405CR_CPC0_FR = 0x0BA, - PPC405CR_CPC0_SR = 0x0BB, -}; - -enum { - PPC405CR_CPU_CLK = 0, - PPC405CR_TMR_CLK = 1, - PPC405CR_PLB_CLK = 2, - PPC405CR_SDRAM_CLK = 3, - PPC405CR_OPB_CLK = 4, - PPC405CR_EXT_CLK = 5, - PPC405CR_UART_CLK = 6, - PPC405CR_CLK_NB = 7, -}; - -typedef struct ppc405cr_cpc_t ppc405cr_cpc_t; -struct ppc405cr_cpc_t { - clk_setup_t clk_setup[PPC405CR_CLK_NB]; - uint32_t sysclk; - uint32_t psr; - uint32_t cr0; - uint32_t cr1; - uint32_t jtagid; - uint32_t pllmr; - uint32_t er; - uint32_t fr; -}; - -static void ppc405cr_clk_setup (ppc405cr_cpc_t *cpc) -{ - uint64_t VCO_out, PLL_out; - uint32_t CPU_clk, TMR_clk, SDRAM_clk, PLB_clk, OPB_clk, EXT_clk, UART_clk; - int M, D0, D1, D2; - - D0 = ((cpc->pllmr >> 26) & 0x3) + 1; /* CBDV */ - if (cpc->pllmr & 0x80000000) { - D1 = (((cpc->pllmr >> 20) - 1) & 0xF) + 1; /* FBDV */ - D2 = 8 - ((cpc->pllmr >> 16) & 0x7); /* FWDVA */ - M = D0 * D1 * D2; - VCO_out = cpc->sysclk * M; - if (VCO_out < 400000000 || VCO_out > 800000000) { - /* PLL cannot lock */ - cpc->pllmr &= ~0x80000000; - goto bypass_pll; - } - PLL_out = VCO_out / D2; - } else { - /* Bypass PLL */ - bypass_pll: - M = D0; - PLL_out = cpc->sysclk * M; - } - CPU_clk = PLL_out; - if (cpc->cr1 & 0x00800000) - TMR_clk = cpc->sysclk; /* Should have a separate clock */ - else - TMR_clk = CPU_clk; - PLB_clk = CPU_clk / D0; - SDRAM_clk = PLB_clk; - D0 = ((cpc->pllmr >> 10) & 0x3) + 1; - OPB_clk = PLB_clk / D0; - D0 = ((cpc->pllmr >> 24) & 0x3) + 2; - EXT_clk = PLB_clk / D0; - D0 = ((cpc->cr0 >> 1) & 0x1F) + 1; - UART_clk = CPU_clk / D0; - /* Setup CPU clocks */ - clk_setup(&cpc->clk_setup[PPC405CR_CPU_CLK], CPU_clk); - /* Setup time-base clock */ - clk_setup(&cpc->clk_setup[PPC405CR_TMR_CLK], TMR_clk); - /* Setup PLB clock */ - clk_setup(&cpc->clk_setup[PPC405CR_PLB_CLK], PLB_clk); - /* Setup SDRAM clock */ - clk_setup(&cpc->clk_setup[PPC405CR_SDRAM_CLK], SDRAM_clk); - /* Setup OPB clock */ - clk_setup(&cpc->clk_setup[PPC405CR_OPB_CLK], OPB_clk); - /* Setup external clock */ - clk_setup(&cpc->clk_setup[PPC405CR_EXT_CLK], EXT_clk); - /* Setup UART clock */ - clk_setup(&cpc->clk_setup[PPC405CR_UART_CLK], UART_clk); -} - -static uint32_t dcr_read_crcpc (void *opaque, int dcrn) -{ - ppc405cr_cpc_t *cpc; - uint32_t ret; - - cpc = opaque; - switch (dcrn) { - case PPC405CR_CPC0_PLLMR: - ret = cpc->pllmr; - break; - case PPC405CR_CPC0_CR0: - ret = cpc->cr0; - break; - case PPC405CR_CPC0_CR1: - ret = cpc->cr1; - break; - case PPC405CR_CPC0_PSR: - ret = cpc->psr; - break; - case PPC405CR_CPC0_JTAGID: - ret = cpc->jtagid; - break; - case PPC405CR_CPC0_ER: - ret = cpc->er; - break; - case PPC405CR_CPC0_FR: - ret = cpc->fr; - break; - case PPC405CR_CPC0_SR: - ret = ~(cpc->er | cpc->fr) & 0xFFFF0000; - break; - default: - /* Avoid gcc warning */ - ret = 0; - break; - } - - return ret; -} - -static void dcr_write_crcpc (void *opaque, int dcrn, uint32_t val) -{ - ppc405cr_cpc_t *cpc; - - cpc = opaque; - switch (dcrn) { - case PPC405CR_CPC0_PLLMR: - cpc->pllmr = val & 0xFFF77C3F; - break; - case PPC405CR_CPC0_CR0: - cpc->cr0 = val & 0x0FFFFFFE; - break; - case PPC405CR_CPC0_CR1: - cpc->cr1 = val & 0x00800000; - break; - case PPC405CR_CPC0_PSR: - /* Read-only */ - break; - case PPC405CR_CPC0_JTAGID: - /* Read-only */ - break; - case PPC405CR_CPC0_ER: - cpc->er = val & 0xBFFC0000; - break; - case PPC405CR_CPC0_FR: - cpc->fr = val & 0xBFFC0000; - break; - case PPC405CR_CPC0_SR: - /* Read-only */ - break; - } -} - -static void ppc405cr_cpc_reset (void *opaque) -{ - ppc405cr_cpc_t *cpc; - int D; - - cpc = opaque; - /* Compute PLLMR value from PSR settings */ - cpc->pllmr = 0x80000000; - /* PFWD */ - switch ((cpc->psr >> 30) & 3) { - case 0: - /* Bypass */ - cpc->pllmr &= ~0x80000000; - break; - case 1: - /* Divide by 3 */ - cpc->pllmr |= 5 << 16; - break; - case 2: - /* Divide by 4 */ - cpc->pllmr |= 4 << 16; - break; - case 3: - /* Divide by 6 */ - cpc->pllmr |= 2 << 16; - break; - } - /* PFBD */ - D = (cpc->psr >> 28) & 3; - cpc->pllmr |= (D + 1) << 20; - /* PT */ - D = (cpc->psr >> 25) & 7; - switch (D) { - case 0x2: - cpc->pllmr |= 0x13; - break; - case 0x4: - cpc->pllmr |= 0x15; - break; - case 0x5: - cpc->pllmr |= 0x16; - break; - default: - break; - } - /* PDC */ - D = (cpc->psr >> 23) & 3; - cpc->pllmr |= D << 26; - /* ODP */ - D = (cpc->psr >> 21) & 3; - cpc->pllmr |= D << 10; - /* EBPD */ - D = (cpc->psr >> 17) & 3; - cpc->pllmr |= D << 24; - cpc->cr0 = 0x0000003C; - cpc->cr1 = 0x2B0D8800; - cpc->er = 0x00000000; - cpc->fr = 0x00000000; - ppc405cr_clk_setup(cpc); -} - -static void ppc405cr_clk_init (ppc405cr_cpc_t *cpc) -{ - int D; - - /* XXX: this should be read from IO pins */ - cpc->psr = 0x00000000; /* 8 bits ROM */ - /* PFWD */ - D = 0x2; /* Divide by 4 */ - cpc->psr |= D << 30; - /* PFBD */ - D = 0x1; /* Divide by 2 */ - cpc->psr |= D << 28; - /* PDC */ - D = 0x1; /* Divide by 2 */ - cpc->psr |= D << 23; - /* PT */ - D = 0x5; /* M = 16 */ - cpc->psr |= D << 25; - /* ODP */ - D = 0x1; /* Divide by 2 */ - cpc->psr |= D << 21; - /* EBDP */ - D = 0x2; /* Divide by 4 */ - cpc->psr |= D << 17; -} - -static void ppc405cr_cpc_init (CPUPPCState *env, clk_setup_t clk_setup[7], - uint32_t sysclk) -{ - ppc405cr_cpc_t *cpc; - - cpc = g_malloc0(sizeof(ppc405cr_cpc_t)); - memcpy(cpc->clk_setup, clk_setup, - PPC405CR_CLK_NB * sizeof(clk_setup_t)); - cpc->sysclk = sysclk; - cpc->jtagid = 0x42051049; - ppc_dcr_register(env, PPC405CR_CPC0_PSR, cpc, - &dcr_read_crcpc, &dcr_write_crcpc); - ppc_dcr_register(env, PPC405CR_CPC0_CR0, cpc, - &dcr_read_crcpc, &dcr_write_crcpc); - ppc_dcr_register(env, PPC405CR_CPC0_CR1, cpc, - &dcr_read_crcpc, &dcr_write_crcpc); - ppc_dcr_register(env, PPC405CR_CPC0_JTAGID, cpc, - &dcr_read_crcpc, &dcr_write_crcpc); - ppc_dcr_register(env, PPC405CR_CPC0_PLLMR, cpc, - &dcr_read_crcpc, &dcr_write_crcpc); - ppc_dcr_register(env, PPC405CR_CPC0_ER, cpc, - &dcr_read_crcpc, &dcr_write_crcpc); - ppc_dcr_register(env, PPC405CR_CPC0_FR, cpc, - &dcr_read_crcpc, &dcr_write_crcpc); - ppc_dcr_register(env, PPC405CR_CPC0_SR, cpc, - &dcr_read_crcpc, &dcr_write_crcpc); - ppc405cr_clk_init(cpc); - qemu_register_reset(ppc405cr_cpc_reset, cpc); -} - -CPUPPCState *ppc405cr_init(MemoryRegion *address_space_mem, - MemoryRegion ram_memories[4], - hwaddr ram_bases[4], - hwaddr ram_sizes[4], - uint32_t sysclk, qemu_irq **picp, - int do_init) -{ - clk_setup_t clk_setup[PPC405CR_CLK_NB]; - qemu_irq dma_irqs[4]; - PowerPCCPU *cpu; - CPUPPCState *env; - qemu_irq *pic, *irqs; - - memset(clk_setup, 0, sizeof(clk_setup)); - cpu = ppc4xx_init("405cr", &clk_setup[PPC405CR_CPU_CLK], - &clk_setup[PPC405CR_TMR_CLK], sysclk); - env = &cpu->env; - /* Memory mapped devices registers */ - /* PLB arbitrer */ - ppc4xx_plb_init(env); - /* PLB to OPB bridge */ - ppc4xx_pob_init(env); - /* OBP arbitrer */ - ppc4xx_opba_init(0xef600600); - /* Universal interrupt controller */ - irqs = g_malloc0(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB); - irqs[PPCUIC_OUTPUT_INT] = - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; - irqs[PPCUIC_OUTPUT_CINT] = - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]; - pic = ppcuic_init(env, irqs, 0x0C0, 0, 1); - *picp = pic; - /* SDRAM controller */ - ppc4xx_sdram_init(env, pic[14], 1, ram_memories, - ram_bases, ram_sizes, do_init); - /* External bus controller */ - ppc405_ebc_init(env); - /* DMA controller */ - dma_irqs[0] = pic[26]; - dma_irqs[1] = pic[25]; - dma_irqs[2] = pic[24]; - dma_irqs[3] = pic[23]; - ppc405_dma_init(env, dma_irqs); - /* Serial ports */ - if (serial_hds[0] != NULL) { - serial_mm_init(address_space_mem, 0xef600300, 0, pic[0], - PPC_SERIAL_MM_BAUDBASE, serial_hds[0], - DEVICE_BIG_ENDIAN); - } - if (serial_hds[1] != NULL) { - serial_mm_init(address_space_mem, 0xef600400, 0, pic[1], - PPC_SERIAL_MM_BAUDBASE, serial_hds[1], - DEVICE_BIG_ENDIAN); - } - /* IIC controller */ - ppc405_i2c_init(0xef600500, pic[2]); - /* GPIO */ - ppc405_gpio_init(0xef600700); - /* CPU control */ - ppc405cr_cpc_init(env, clk_setup, sysclk); - - return env; -} - -/*****************************************************************************/ -/* PowerPC 405EP */ -/* CPU control */ -enum { - PPC405EP_CPC0_PLLMR0 = 0x0F0, - PPC405EP_CPC0_BOOT = 0x0F1, - PPC405EP_CPC0_EPCTL = 0x0F3, - PPC405EP_CPC0_PLLMR1 = 0x0F4, - PPC405EP_CPC0_UCR = 0x0F5, - PPC405EP_CPC0_SRR = 0x0F6, - PPC405EP_CPC0_JTAGID = 0x0F7, - PPC405EP_CPC0_PCI = 0x0F9, -#if 0 - PPC405EP_CPC0_ER = xxx, - PPC405EP_CPC0_FR = xxx, - PPC405EP_CPC0_SR = xxx, -#endif -}; - -enum { - PPC405EP_CPU_CLK = 0, - PPC405EP_PLB_CLK = 1, - PPC405EP_OPB_CLK = 2, - PPC405EP_EBC_CLK = 3, - PPC405EP_MAL_CLK = 4, - PPC405EP_PCI_CLK = 5, - PPC405EP_UART0_CLK = 6, - PPC405EP_UART1_CLK = 7, - PPC405EP_CLK_NB = 8, -}; - -typedef struct ppc405ep_cpc_t ppc405ep_cpc_t; -struct ppc405ep_cpc_t { - uint32_t sysclk; - clk_setup_t clk_setup[PPC405EP_CLK_NB]; - uint32_t boot; - uint32_t epctl; - uint32_t pllmr[2]; - uint32_t ucr; - uint32_t srr; - uint32_t jtagid; - uint32_t pci; - /* Clock and power management */ - uint32_t er; - uint32_t fr; - uint32_t sr; -}; - -static void ppc405ep_compute_clocks (ppc405ep_cpc_t *cpc) -{ - uint32_t CPU_clk, PLB_clk, OPB_clk, EBC_clk, MAL_clk, PCI_clk; - uint32_t UART0_clk, UART1_clk; - uint64_t VCO_out, PLL_out; - int M, D; - - VCO_out = 0; - if ((cpc->pllmr[1] & 0x80000000) && !(cpc->pllmr[1] & 0x40000000)) { - M = (((cpc->pllmr[1] >> 20) - 1) & 0xF) + 1; /* FBMUL */ -#ifdef DEBUG_CLOCKS_LL - printf("FBMUL %01" PRIx32 " %d\n", (cpc->pllmr[1] >> 20) & 0xF, M); -#endif - D = 8 - ((cpc->pllmr[1] >> 16) & 0x7); /* FWDA */ -#ifdef DEBUG_CLOCKS_LL - printf("FWDA %01" PRIx32 " %d\n", (cpc->pllmr[1] >> 16) & 0x7, D); -#endif - VCO_out = cpc->sysclk * M * D; - if (VCO_out < 500000000UL || VCO_out > 1000000000UL) { - /* Error - unlock the PLL */ - printf("VCO out of range %" PRIu64 "\n", VCO_out); -#if 0 - cpc->pllmr[1] &= ~0x80000000; - goto pll_bypass; -#endif - } - PLL_out = VCO_out / D; - /* Pretend the PLL is locked */ - cpc->boot |= 0x00000001; - } else { -#if 0 - pll_bypass: -#endif - PLL_out = cpc->sysclk; - if (cpc->pllmr[1] & 0x40000000) { - /* Pretend the PLL is not locked */ - cpc->boot &= ~0x00000001; - } - } - /* Now, compute all other clocks */ - D = ((cpc->pllmr[0] >> 20) & 0x3) + 1; /* CCDV */ -#ifdef DEBUG_CLOCKS_LL - printf("CCDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 20) & 0x3, D); -#endif - CPU_clk = PLL_out / D; - D = ((cpc->pllmr[0] >> 16) & 0x3) + 1; /* CBDV */ -#ifdef DEBUG_CLOCKS_LL - printf("CBDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 16) & 0x3, D); -#endif - PLB_clk = CPU_clk / D; - D = ((cpc->pllmr[0] >> 12) & 0x3) + 1; /* OPDV */ -#ifdef DEBUG_CLOCKS_LL - printf("OPDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 12) & 0x3, D); -#endif - OPB_clk = PLB_clk / D; - D = ((cpc->pllmr[0] >> 8) & 0x3) + 2; /* EPDV */ -#ifdef DEBUG_CLOCKS_LL - printf("EPDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 8) & 0x3, D); -#endif - EBC_clk = PLB_clk / D; - D = ((cpc->pllmr[0] >> 4) & 0x3) + 1; /* MPDV */ -#ifdef DEBUG_CLOCKS_LL - printf("MPDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 4) & 0x3, D); -#endif - MAL_clk = PLB_clk / D; - D = (cpc->pllmr[0] & 0x3) + 1; /* PPDV */ -#ifdef DEBUG_CLOCKS_LL - printf("PPDV %01" PRIx32 " %d\n", cpc->pllmr[0] & 0x3, D); -#endif - PCI_clk = PLB_clk / D; - D = ((cpc->ucr - 1) & 0x7F) + 1; /* U0DIV */ -#ifdef DEBUG_CLOCKS_LL - printf("U0DIV %01" PRIx32 " %d\n", cpc->ucr & 0x7F, D); -#endif - UART0_clk = PLL_out / D; - D = (((cpc->ucr >> 8) - 1) & 0x7F) + 1; /* U1DIV */ -#ifdef DEBUG_CLOCKS_LL - printf("U1DIV %01" PRIx32 " %d\n", (cpc->ucr >> 8) & 0x7F, D); -#endif - UART1_clk = PLL_out / D; -#ifdef DEBUG_CLOCKS - printf("Setup PPC405EP clocks - sysclk %" PRIu32 " VCO %" PRIu64 - " PLL out %" PRIu64 " Hz\n", cpc->sysclk, VCO_out, PLL_out); - printf("CPU %" PRIu32 " PLB %" PRIu32 " OPB %" PRIu32 " EBC %" PRIu32 - " MAL %" PRIu32 " PCI %" PRIu32 " UART0 %" PRIu32 - " UART1 %" PRIu32 "\n", - CPU_clk, PLB_clk, OPB_clk, EBC_clk, MAL_clk, PCI_clk, - UART0_clk, UART1_clk); -#endif - /* Setup CPU clocks */ - clk_setup(&cpc->clk_setup[PPC405EP_CPU_CLK], CPU_clk); - /* Setup PLB clock */ - clk_setup(&cpc->clk_setup[PPC405EP_PLB_CLK], PLB_clk); - /* Setup OPB clock */ - clk_setup(&cpc->clk_setup[PPC405EP_OPB_CLK], OPB_clk); - /* Setup external clock */ - clk_setup(&cpc->clk_setup[PPC405EP_EBC_CLK], EBC_clk); - /* Setup MAL clock */ - clk_setup(&cpc->clk_setup[PPC405EP_MAL_CLK], MAL_clk); - /* Setup PCI clock */ - clk_setup(&cpc->clk_setup[PPC405EP_PCI_CLK], PCI_clk); - /* Setup UART0 clock */ - clk_setup(&cpc->clk_setup[PPC405EP_UART0_CLK], UART0_clk); - /* Setup UART1 clock */ - clk_setup(&cpc->clk_setup[PPC405EP_UART1_CLK], UART1_clk); -} - -static uint32_t dcr_read_epcpc (void *opaque, int dcrn) -{ - ppc405ep_cpc_t *cpc; - uint32_t ret; - - cpc = opaque; - switch (dcrn) { - case PPC405EP_CPC0_BOOT: - ret = cpc->boot; - break; - case PPC405EP_CPC0_EPCTL: - ret = cpc->epctl; - break; - case PPC405EP_CPC0_PLLMR0: - ret = cpc->pllmr[0]; - break; - case PPC405EP_CPC0_PLLMR1: - ret = cpc->pllmr[1]; - break; - case PPC405EP_CPC0_UCR: - ret = cpc->ucr; - break; - case PPC405EP_CPC0_SRR: - ret = cpc->srr; - break; - case PPC405EP_CPC0_JTAGID: - ret = cpc->jtagid; - break; - case PPC405EP_CPC0_PCI: - ret = cpc->pci; - break; - default: - /* Avoid gcc warning */ - ret = 0; - break; - } - - return ret; -} - -static void dcr_write_epcpc (void *opaque, int dcrn, uint32_t val) -{ - ppc405ep_cpc_t *cpc; - - cpc = opaque; - switch (dcrn) { - case PPC405EP_CPC0_BOOT: - /* Read-only register */ - break; - case PPC405EP_CPC0_EPCTL: - /* Don't care for now */ - cpc->epctl = val & 0xC00000F3; - break; - case PPC405EP_CPC0_PLLMR0: - cpc->pllmr[0] = val & 0x00633333; - ppc405ep_compute_clocks(cpc); - break; - case PPC405EP_CPC0_PLLMR1: - cpc->pllmr[1] = val & 0xC0F73FFF; - ppc405ep_compute_clocks(cpc); - break; - case PPC405EP_CPC0_UCR: - /* UART control - don't care for now */ - cpc->ucr = val & 0x003F7F7F; - break; - case PPC405EP_CPC0_SRR: - cpc->srr = val; - break; - case PPC405EP_CPC0_JTAGID: - /* Read-only */ - break; - case PPC405EP_CPC0_PCI: - cpc->pci = val; - break; - } -} - -static void ppc405ep_cpc_reset (void *opaque) -{ - ppc405ep_cpc_t *cpc = opaque; - - cpc->boot = 0x00000010; /* Boot from PCI - IIC EEPROM disabled */ - cpc->epctl = 0x00000000; - cpc->pllmr[0] = 0x00011010; - cpc->pllmr[1] = 0x40000000; - cpc->ucr = 0x00000000; - cpc->srr = 0x00040000; - cpc->pci = 0x00000000; - cpc->er = 0x00000000; - cpc->fr = 0x00000000; - cpc->sr = 0x00000000; - ppc405ep_compute_clocks(cpc); -} - -/* XXX: sysclk should be between 25 and 100 MHz */ -static void ppc405ep_cpc_init (CPUPPCState *env, clk_setup_t clk_setup[8], - uint32_t sysclk) -{ - ppc405ep_cpc_t *cpc; - - cpc = g_malloc0(sizeof(ppc405ep_cpc_t)); - memcpy(cpc->clk_setup, clk_setup, - PPC405EP_CLK_NB * sizeof(clk_setup_t)); - cpc->jtagid = 0x20267049; - cpc->sysclk = sysclk; - qemu_register_reset(&ppc405ep_cpc_reset, cpc); - ppc_dcr_register(env, PPC405EP_CPC0_BOOT, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_EPCTL, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_PLLMR0, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_PLLMR1, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_UCR, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_SRR, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_JTAGID, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_PCI, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); -#if 0 - ppc_dcr_register(env, PPC405EP_CPC0_ER, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_FR, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); - ppc_dcr_register(env, PPC405EP_CPC0_SR, cpc, - &dcr_read_epcpc, &dcr_write_epcpc); -#endif -} - -CPUPPCState *ppc405ep_init(MemoryRegion *address_space_mem, - MemoryRegion ram_memories[2], - hwaddr ram_bases[2], - hwaddr ram_sizes[2], - uint32_t sysclk, qemu_irq **picp, - int do_init) -{ - clk_setup_t clk_setup[PPC405EP_CLK_NB], tlb_clk_setup; - qemu_irq dma_irqs[4], gpt_irqs[5], mal_irqs[4]; - PowerPCCPU *cpu; - CPUPPCState *env; - qemu_irq *pic, *irqs; - - memset(clk_setup, 0, sizeof(clk_setup)); - /* init CPUs */ - cpu = ppc4xx_init("405ep", &clk_setup[PPC405EP_CPU_CLK], - &tlb_clk_setup, sysclk); - env = &cpu->env; - clk_setup[PPC405EP_CPU_CLK].cb = tlb_clk_setup.cb; - clk_setup[PPC405EP_CPU_CLK].opaque = tlb_clk_setup.opaque; - /* Internal devices init */ - /* Memory mapped devices registers */ - /* PLB arbitrer */ - ppc4xx_plb_init(env); - /* PLB to OPB bridge */ - ppc4xx_pob_init(env); - /* OBP arbitrer */ - ppc4xx_opba_init(0xef600600); - /* Initialize timers */ - ppc_booke_timers_init(cpu, sysclk, 0); - /* Universal interrupt controller */ - irqs = g_malloc0(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB); - irqs[PPCUIC_OUTPUT_INT] = - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; - irqs[PPCUIC_OUTPUT_CINT] = - ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]; - pic = ppcuic_init(env, irqs, 0x0C0, 0, 1); - *picp = pic; - /* SDRAM controller */ - /* XXX 405EP has no ECC interrupt */ - ppc4xx_sdram_init(env, pic[17], 2, ram_memories, - ram_bases, ram_sizes, do_init); - /* External bus controller */ - ppc405_ebc_init(env); - /* DMA controller */ - dma_irqs[0] = pic[5]; - dma_irqs[1] = pic[6]; - dma_irqs[2] = pic[7]; - dma_irqs[3] = pic[8]; - ppc405_dma_init(env, dma_irqs); - /* IIC controller */ - ppc405_i2c_init(0xef600500, pic[2]); - /* GPIO */ - ppc405_gpio_init(0xef600700); - /* Serial ports */ - if (serial_hds[0] != NULL) { - serial_mm_init(address_space_mem, 0xef600300, 0, pic[0], - PPC_SERIAL_MM_BAUDBASE, serial_hds[0], - DEVICE_BIG_ENDIAN); - } - if (serial_hds[1] != NULL) { - serial_mm_init(address_space_mem, 0xef600400, 0, pic[1], - PPC_SERIAL_MM_BAUDBASE, serial_hds[1], - DEVICE_BIG_ENDIAN); - } - /* OCM */ - ppc405_ocm_init(env); - /* GPT */ - gpt_irqs[0] = pic[19]; - gpt_irqs[1] = pic[20]; - gpt_irqs[2] = pic[21]; - gpt_irqs[3] = pic[22]; - gpt_irqs[4] = pic[23]; - ppc4xx_gpt_init(0xef600000, gpt_irqs); - /* PCI */ - /* Uses pic[3], pic[16], pic[18] */ - /* MAL */ - mal_irqs[0] = pic[11]; - mal_irqs[1] = pic[12]; - mal_irqs[2] = pic[13]; - mal_irqs[3] = pic[14]; - ppc405_mal_init(env, mal_irqs); - /* Ethernet */ - /* Uses pic[9], pic[15], pic[17] */ - /* CPU control */ - ppc405ep_cpc_init(env, clk_setup, sysclk); - - return env; -} diff --git a/qemu/hw/ppc/ppc440_bamboo.c b/qemu/hw/ppc/ppc440_bamboo.c deleted file mode 100644 index 5c535b18a..000000000 --- a/qemu/hw/ppc/ppc440_bamboo.c +++ /dev/null @@ -1,300 +0,0 @@ -/* - * QEMU PowerPC 440 Bamboo board emulation - * - * Copyright 2007 IBM Corporation. - * Authors: - * Jerone Young - * Christian Ehrhardt - * Hollis Blanchard - * - * This work is licensed under the GNU GPL license version 2 or later. - * - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "net/net.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "hw/boards.h" -#include "sysemu/kvm.h" -#include "kvm_ppc.h" -#include "sysemu/device_tree.h" -#include "hw/loader.h" -#include "elf.h" -#include "exec/address-spaces.h" -#include "hw/char/serial.h" -#include "hw/ppc/ppc.h" -#include "ppc405.h" -#include "sysemu/sysemu.h" -#include "hw/sysbus.h" - -#define BINARY_DEVICE_TREE_FILE "bamboo.dtb" - -/* from u-boot */ -#define KERNEL_ADDR 0x1000000 -#define FDT_ADDR 0x1800000 -#define RAMDISK_ADDR 0x1900000 - -#define PPC440EP_PCI_CONFIG 0xeec00000 -#define PPC440EP_PCI_INTACK 0xeed00000 -#define PPC440EP_PCI_SPECIAL 0xeed00000 -#define PPC440EP_PCI_REGS 0xef400000 -#define PPC440EP_PCI_IO 0xe8000000 -#define PPC440EP_PCI_IOLEN 0x00010000 - -#define PPC440EP_SDRAM_NR_BANKS 4 - -static const unsigned int ppc440ep_sdram_bank_sizes[] = { - 256<<20, 128<<20, 64<<20, 32<<20, 16<<20, 8<<20, 0 -}; - -static hwaddr entry; - -static int bamboo_load_device_tree(hwaddr addr, - uint32_t ramsize, - hwaddr initrd_base, - hwaddr initrd_size, - const char *kernel_cmdline) -{ - int ret = -1; - uint32_t mem_reg_property[] = { 0, 0, cpu_to_be32(ramsize) }; - char *filename; - int fdt_size; - void *fdt; - uint32_t tb_freq = 400000000; - uint32_t clock_freq = 400000000; - - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); - if (!filename) { - goto out; - } - fdt = load_device_tree(filename, &fdt_size); - g_free(filename); - if (fdt == NULL) { - goto out; - } - - /* Manipulate device tree in memory. */ - - ret = qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property, - sizeof(mem_reg_property)); - if (ret < 0) - fprintf(stderr, "couldn't set /memory/reg\n"); - - ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", - initrd_base); - if (ret < 0) - fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n"); - - ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", - (initrd_base + initrd_size)); - if (ret < 0) - fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n"); - - ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", - kernel_cmdline); - if (ret < 0) - fprintf(stderr, "couldn't set /chosen/bootargs\n"); - - /* Copy data from the host device tree into the guest. Since the guest can - * directly access the timebase without host involvement, we must expose - * the correct frequencies. */ - if (kvm_enabled()) { - tb_freq = kvmppc_get_tbfreq(); - clock_freq = kvmppc_get_clockfreq(); - } - - qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency", - clock_freq); - qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency", - tb_freq); - - rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); - g_free(fdt); - return 0; - -out: - - return ret; -} - -/* Create reset TLB entries for BookE, spanning the 32bit addr space. */ -static void mmubooke_create_initial_mapping(CPUPPCState *env, - target_ulong va, - hwaddr pa) -{ - ppcemb_tlb_t *tlb = &env->tlb.tlbe[0]; - - tlb->attr = 0; - tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); - tlb->size = 1U << 31; /* up to 0x80000000 */ - tlb->EPN = va & TARGET_PAGE_MASK; - tlb->RPN = pa & TARGET_PAGE_MASK; - tlb->PID = 0; - - tlb = &env->tlb.tlbe[1]; - tlb->attr = 0; - tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); - tlb->size = 1U << 31; /* up to 0xffffffff */ - tlb->EPN = 0x80000000 & TARGET_PAGE_MASK; - tlb->RPN = 0x80000000 & TARGET_PAGE_MASK; - tlb->PID = 0; -} - -static void main_cpu_reset(void *opaque) -{ - PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; - - cpu_reset(CPU(cpu)); - env->gpr[1] = (16<<20) - 8; - env->gpr[3] = FDT_ADDR; - env->nip = entry; - - /* Create a mapping for the kernel. */ - mmubooke_create_initial_mapping(env, 0, 0); -} - -static void bamboo_init(MachineState *machine) -{ - ram_addr_t ram_size = machine->ram_size; - const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; - const char *initrd_filename = machine->initrd_filename; - unsigned int pci_irq_nrs[4] = { 28, 27, 26, 25 }; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *isa = g_new(MemoryRegion, 1); - MemoryRegion *ram_memories - = g_malloc(PPC440EP_SDRAM_NR_BANKS * sizeof(*ram_memories)); - hwaddr ram_bases[PPC440EP_SDRAM_NR_BANKS]; - hwaddr ram_sizes[PPC440EP_SDRAM_NR_BANKS]; - qemu_irq *pic; - qemu_irq *irqs; - PCIBus *pcibus; - PowerPCCPU *cpu; - CPUPPCState *env; - uint64_t elf_entry; - uint64_t elf_lowaddr; - hwaddr loadaddr = 0; - target_long initrd_size = 0; - DeviceState *dev; - int success; - int i; - - /* Setup CPU. */ - if (machine->cpu_model == NULL) { - machine->cpu_model = "440EP"; - } - cpu = cpu_ppc_init(machine->cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to initialize CPU!\n"); - exit(1); - } - env = &cpu->env; - - qemu_register_reset(main_cpu_reset, cpu); - ppc_booke_timers_init(cpu, 400000000, 0); - ppc_dcr_init(env, NULL, NULL); - - /* interrupt controller */ - irqs = g_malloc0(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB); - irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; - irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]; - pic = ppcuic_init(env, irqs, 0x0C0, 0, 1); - - /* SDRAM controller */ - memset(ram_bases, 0, sizeof(ram_bases)); - memset(ram_sizes, 0, sizeof(ram_sizes)); - ram_size = ppc4xx_sdram_adjust(ram_size, PPC440EP_SDRAM_NR_BANKS, - ram_memories, - ram_bases, ram_sizes, - ppc440ep_sdram_bank_sizes); - /* XXX 440EP's ECC interrupts are on UIC1, but we've only created UIC0. */ - ppc4xx_sdram_init(env, pic[14], PPC440EP_SDRAM_NR_BANKS, ram_memories, - ram_bases, ram_sizes, 1); - - /* PCI */ - dev = sysbus_create_varargs(TYPE_PPC4xx_PCI_HOST_BRIDGE, - PPC440EP_PCI_CONFIG, - pic[pci_irq_nrs[0]], pic[pci_irq_nrs[1]], - pic[pci_irq_nrs[2]], pic[pci_irq_nrs[3]], - NULL); - pcibus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); - if (!pcibus) { - fprintf(stderr, "couldn't create PCI controller!\n"); - exit(1); - } - - memory_region_init_alias(isa, NULL, "isa_mmio", - get_system_io(), 0, PPC440EP_PCI_IOLEN); - memory_region_add_subregion(get_system_memory(), PPC440EP_PCI_IO, isa); - - if (serial_hds[0] != NULL) { - serial_mm_init(address_space_mem, 0xef600300, 0, pic[0], - PPC_SERIAL_MM_BAUDBASE, serial_hds[0], - DEVICE_BIG_ENDIAN); - } - if (serial_hds[1] != NULL) { - serial_mm_init(address_space_mem, 0xef600400, 0, pic[1], - PPC_SERIAL_MM_BAUDBASE, serial_hds[1], - DEVICE_BIG_ENDIAN); - } - - if (pcibus) { - /* Register network interfaces. */ - for (i = 0; i < nb_nics; i++) { - /* There are no PCI NICs on the Bamboo board, but there are - * PCI slots, so we can pick whatever default model we want. */ - pci_nic_init_nofail(&nd_table[i], pcibus, "e1000", NULL); - } - } - - /* Load kernel. */ - if (kernel_filename) { - success = load_uimage(kernel_filename, &entry, &loadaddr, NULL, - NULL, NULL); - if (success < 0) { - success = load_elf(kernel_filename, NULL, NULL, &elf_entry, - &elf_lowaddr, NULL, 1, PPC_ELF_MACHINE, - 0, 0); - entry = elf_entry; - loadaddr = elf_lowaddr; - } - /* XXX try again as binary */ - if (success < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); - exit(1); - } - } - - /* Load initrd. */ - if (initrd_filename) { - initrd_size = load_image_targphys(initrd_filename, RAMDISK_ADDR, - ram_size - RAMDISK_ADDR); - - if (initrd_size < 0) { - fprintf(stderr, "qemu: could not load ram disk '%s' at %x\n", - initrd_filename, RAMDISK_ADDR); - exit(1); - } - } - - /* If we're loading a kernel directly, we must load the device tree too. */ - if (kernel_filename) { - if (bamboo_load_device_tree(FDT_ADDR, ram_size, RAMDISK_ADDR, - initrd_size, kernel_cmdline) < 0) { - fprintf(stderr, "couldn't load device tree\n"); - exit(1); - } - } -} - -static void bamboo_machine_init(MachineClass *mc) -{ - mc->desc = "bamboo"; - mc->init = bamboo_init; -} - -DEFINE_MACHINE("bamboo", bamboo_machine_init) diff --git a/qemu/hw/ppc/ppc4xx_devs.c b/qemu/hw/ppc/ppc4xx_devs.c deleted file mode 100644 index 7d59018fc..000000000 --- a/qemu/hw/ppc/ppc4xx_devs.c +++ /dev/null @@ -1,735 +0,0 @@ -/* - * QEMU PowerPC 4xx embedded processors shared devices emulation - * - * Copyright (c) 2007 Jocelyn Mayer - * - * 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/ppc/ppc.h" -#include "hw/ppc/ppc4xx.h" -#include "hw/boards.h" -#include "qemu/log.h" -#include "exec/address-spaces.h" - -#define DEBUG_UIC - - -#ifdef DEBUG_UIC -# define LOG_UIC(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__) -#else -# define LOG_UIC(...) do { } while (0) -#endif - -static void ppc4xx_reset(void *opaque) -{ - PowerPCCPU *cpu = opaque; - - cpu_reset(CPU(cpu)); -} - -/*****************************************************************************/ -/* Generic PowerPC 4xx processor instantiation */ -PowerPCCPU *ppc4xx_init(const char *cpu_model, - clk_setup_t *cpu_clk, clk_setup_t *tb_clk, - uint32_t sysclk) -{ - PowerPCCPU *cpu; - CPUPPCState *env; - - /* init CPUs */ - cpu = cpu_ppc_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to find PowerPC %s CPU definition\n", - cpu_model); - exit(1); - } - env = &cpu->env; - - cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */ - cpu_clk->opaque = env; - /* Set time-base frequency to sysclk */ - tb_clk->cb = ppc_40x_timers_init(env, sysclk, PPC_INTERRUPT_PIT); - tb_clk->opaque = env; - ppc_dcr_init(env, NULL, NULL); - /* Register qemu callbacks */ - qemu_register_reset(ppc4xx_reset, cpu); - - return cpu; -} - -/*****************************************************************************/ -/* "Universal" Interrupt controller */ -enum { - DCR_UICSR = 0x000, - DCR_UICSRS = 0x001, - DCR_UICER = 0x002, - DCR_UICCR = 0x003, - DCR_UICPR = 0x004, - DCR_UICTR = 0x005, - DCR_UICMSR = 0x006, - DCR_UICVR = 0x007, - DCR_UICVCR = 0x008, - DCR_UICMAX = 0x009, -}; - -#define UIC_MAX_IRQ 32 -typedef struct ppcuic_t ppcuic_t; -struct ppcuic_t { - uint32_t dcr_base; - int use_vectors; - uint32_t level; /* Remembers the state of level-triggered interrupts. */ - uint32_t uicsr; /* Status register */ - uint32_t uicer; /* Enable register */ - uint32_t uiccr; /* Critical register */ - uint32_t uicpr; /* Polarity register */ - uint32_t uictr; /* Triggering register */ - uint32_t uicvcr; /* Vector configuration register */ - uint32_t uicvr; - qemu_irq *irqs; -}; - -static void ppcuic_trigger_irq (ppcuic_t *uic) -{ - uint32_t ir, cr; - int start, end, inc, i; - - /* Trigger interrupt if any is pending */ - ir = uic->uicsr & uic->uicer & (~uic->uiccr); - cr = uic->uicsr & uic->uicer & uic->uiccr; - LOG_UIC("%s: uicsr %08" PRIx32 " uicer %08" PRIx32 - " uiccr %08" PRIx32 "\n" - " %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n", - __func__, uic->uicsr, uic->uicer, uic->uiccr, - uic->uicsr & uic->uicer, ir, cr); - if (ir != 0x0000000) { - LOG_UIC("Raise UIC interrupt\n"); - qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_INT]); - } else { - LOG_UIC("Lower UIC interrupt\n"); - qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_INT]); - } - /* Trigger critical interrupt if any is pending and update vector */ - if (cr != 0x0000000) { - qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_CINT]); - if (uic->use_vectors) { - /* Compute critical IRQ vector */ - if (uic->uicvcr & 1) { - start = 31; - end = 0; - inc = -1; - } else { - start = 0; - end = 31; - inc = 1; - } - uic->uicvr = uic->uicvcr & 0xFFFFFFFC; - for (i = start; i <= end; i += inc) { - if (cr & (1 << i)) { - uic->uicvr += (i - start) * 512 * inc; - break; - } - } - } - LOG_UIC("Raise UIC critical interrupt - " - "vector %08" PRIx32 "\n", uic->uicvr); - } else { - LOG_UIC("Lower UIC critical interrupt\n"); - qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_CINT]); - uic->uicvr = 0x00000000; - } -} - -static void ppcuic_set_irq (void *opaque, int irq_num, int level) -{ - ppcuic_t *uic; - uint32_t mask, sr; - - uic = opaque; - mask = 1U << (31-irq_num); - LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32 - " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n", - __func__, irq_num, level, - uic->uicsr, mask, uic->uicsr & mask, level << irq_num); - if (irq_num < 0 || irq_num > 31) - return; - sr = uic->uicsr; - - /* Update status register */ - if (uic->uictr & mask) { - /* Edge sensitive interrupt */ - if (level == 1) - uic->uicsr |= mask; - } else { - /* Level sensitive interrupt */ - if (level == 1) { - uic->uicsr |= mask; - uic->level |= mask; - } else { - uic->uicsr &= ~mask; - uic->level &= ~mask; - } - } - LOG_UIC("%s: irq %d level %d sr %" PRIx32 " => " - "%08" PRIx32 "\n", __func__, irq_num, level, uic->uicsr, sr); - if (sr != uic->uicsr) - ppcuic_trigger_irq(uic); -} - -static uint32_t dcr_read_uic (void *opaque, int dcrn) -{ - ppcuic_t *uic; - uint32_t ret; - - uic = opaque; - dcrn -= uic->dcr_base; - switch (dcrn) { - case DCR_UICSR: - case DCR_UICSRS: - ret = uic->uicsr; - break; - case DCR_UICER: - ret = uic->uicer; - break; - case DCR_UICCR: - ret = uic->uiccr; - break; - case DCR_UICPR: - ret = uic->uicpr; - break; - case DCR_UICTR: - ret = uic->uictr; - break; - case DCR_UICMSR: - ret = uic->uicsr & uic->uicer; - break; - case DCR_UICVR: - if (!uic->use_vectors) - goto no_read; - ret = uic->uicvr; - break; - case DCR_UICVCR: - if (!uic->use_vectors) - goto no_read; - ret = uic->uicvcr; - break; - default: - no_read: - ret = 0x00000000; - break; - } - - return ret; -} - -static void dcr_write_uic (void *opaque, int dcrn, uint32_t val) -{ - ppcuic_t *uic; - - uic = opaque; - dcrn -= uic->dcr_base; - LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val); - switch (dcrn) { - case DCR_UICSR: - uic->uicsr &= ~val; - uic->uicsr |= uic->level; - ppcuic_trigger_irq(uic); - break; - case DCR_UICSRS: - uic->uicsr |= val; - ppcuic_trigger_irq(uic); - break; - case DCR_UICER: - uic->uicer = val; - ppcuic_trigger_irq(uic); - break; - case DCR_UICCR: - uic->uiccr = val; - ppcuic_trigger_irq(uic); - break; - case DCR_UICPR: - uic->uicpr = val; - break; - case DCR_UICTR: - uic->uictr = val; - ppcuic_trigger_irq(uic); - break; - case DCR_UICMSR: - break; - case DCR_UICVR: - break; - case DCR_UICVCR: - uic->uicvcr = val & 0xFFFFFFFD; - ppcuic_trigger_irq(uic); - break; - } -} - -static void ppcuic_reset (void *opaque) -{ - ppcuic_t *uic; - - uic = opaque; - uic->uiccr = 0x00000000; - uic->uicer = 0x00000000; - uic->uicpr = 0x00000000; - uic->uicsr = 0x00000000; - uic->uictr = 0x00000000; - if (uic->use_vectors) { - uic->uicvcr = 0x00000000; - uic->uicvr = 0x0000000; - } -} - -qemu_irq *ppcuic_init (CPUPPCState *env, qemu_irq *irqs, - uint32_t dcr_base, int has_ssr, int has_vr) -{ - ppcuic_t *uic; - int i; - - uic = g_malloc0(sizeof(ppcuic_t)); - uic->dcr_base = dcr_base; - uic->irqs = irqs; - if (has_vr) - uic->use_vectors = 1; - for (i = 0; i < DCR_UICMAX; i++) { - ppc_dcr_register(env, dcr_base + i, uic, - &dcr_read_uic, &dcr_write_uic); - } - qemu_register_reset(ppcuic_reset, uic); - - return qemu_allocate_irqs(&ppcuic_set_irq, uic, UIC_MAX_IRQ); -} - -/*****************************************************************************/ -/* SDRAM controller */ -typedef struct ppc4xx_sdram_t ppc4xx_sdram_t; -struct ppc4xx_sdram_t { - uint32_t addr; - int nbanks; - MemoryRegion containers[4]; /* used for clipping */ - MemoryRegion *ram_memories; - hwaddr ram_bases[4]; - hwaddr ram_sizes[4]; - uint32_t besr0; - uint32_t besr1; - uint32_t bear; - uint32_t cfg; - uint32_t status; - uint32_t rtr; - uint32_t pmit; - uint32_t bcr[4]; - uint32_t tr; - uint32_t ecccfg; - uint32_t eccesr; - qemu_irq irq; -}; - -enum { - SDRAM0_CFGADDR = 0x010, - SDRAM0_CFGDATA = 0x011, -}; - -/* XXX: TOFIX: some patches have made this code become inconsistent: - * there are type inconsistencies, mixing hwaddr, target_ulong - * and uint32_t - */ -static uint32_t sdram_bcr (hwaddr ram_base, - hwaddr ram_size) -{ - uint32_t bcr; - - switch (ram_size) { - case (4 * 1024 * 1024): - bcr = 0x00000000; - break; - case (8 * 1024 * 1024): - bcr = 0x00020000; - break; - case (16 * 1024 * 1024): - bcr = 0x00040000; - break; - case (32 * 1024 * 1024): - bcr = 0x00060000; - break; - case (64 * 1024 * 1024): - bcr = 0x00080000; - break; - case (128 * 1024 * 1024): - bcr = 0x000A0000; - break; - case (256 * 1024 * 1024): - bcr = 0x000C0000; - break; - default: - printf("%s: invalid RAM size " TARGET_FMT_plx "\n", __func__, - ram_size); - return 0x00000000; - } - bcr |= ram_base & 0xFF800000; - bcr |= 1; - - return bcr; -} - -static inline hwaddr sdram_base(uint32_t bcr) -{ - return bcr & 0xFF800000; -} - -static target_ulong sdram_size (uint32_t bcr) -{ - target_ulong size; - int sh; - - sh = (bcr >> 17) & 0x7; - if (sh == 7) - size = -1; - else - size = (4 * 1024 * 1024) << sh; - - return size; -} - -static void sdram_set_bcr(ppc4xx_sdram_t *sdram, - uint32_t *bcrp, uint32_t bcr, int enabled) -{ - unsigned n = bcrp - sdram->bcr; - - if (*bcrp & 0x00000001) { - /* Unmap RAM */ -#ifdef DEBUG_SDRAM - printf("%s: unmap RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n", - __func__, sdram_base(*bcrp), sdram_size(*bcrp)); -#endif - memory_region_del_subregion(get_system_memory(), - &sdram->containers[n]); - memory_region_del_subregion(&sdram->containers[n], - &sdram->ram_memories[n]); - object_unparent(OBJECT(&sdram->containers[n])); - } - *bcrp = bcr & 0xFFDEE001; - if (enabled && (bcr & 0x00000001)) { -#ifdef DEBUG_SDRAM - printf("%s: Map RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n", - __func__, sdram_base(bcr), sdram_size(bcr)); -#endif - memory_region_init(&sdram->containers[n], NULL, "sdram-containers", - sdram_size(bcr)); - memory_region_add_subregion(&sdram->containers[n], 0, - &sdram->ram_memories[n]); - memory_region_add_subregion(get_system_memory(), - sdram_base(bcr), - &sdram->containers[n]); - } -} - -static void sdram_map_bcr (ppc4xx_sdram_t *sdram) -{ - int i; - - for (i = 0; i < sdram->nbanks; i++) { - if (sdram->ram_sizes[i] != 0) { - sdram_set_bcr(sdram, - &sdram->bcr[i], - sdram_bcr(sdram->ram_bases[i], sdram->ram_sizes[i]), - 1); - } else { - sdram_set_bcr(sdram, &sdram->bcr[i], 0x00000000, 0); - } - } -} - -static void sdram_unmap_bcr (ppc4xx_sdram_t *sdram) -{ - int i; - - for (i = 0; i < sdram->nbanks; i++) { -#ifdef DEBUG_SDRAM - printf("%s: Unmap RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n", - __func__, sdram_base(sdram->bcr[i]), sdram_size(sdram->bcr[i])); -#endif - memory_region_del_subregion(get_system_memory(), - &sdram->ram_memories[i]); - } -} - -static uint32_t dcr_read_sdram (void *opaque, int dcrn) -{ - ppc4xx_sdram_t *sdram; - uint32_t ret; - - sdram = opaque; - switch (dcrn) { - case SDRAM0_CFGADDR: - ret = sdram->addr; - break; - case SDRAM0_CFGDATA: - switch (sdram->addr) { - case 0x00: /* SDRAM_BESR0 */ - ret = sdram->besr0; - break; - case 0x08: /* SDRAM_BESR1 */ - ret = sdram->besr1; - break; - case 0x10: /* SDRAM_BEAR */ - ret = sdram->bear; - break; - case 0x20: /* SDRAM_CFG */ - ret = sdram->cfg; - break; - case 0x24: /* SDRAM_STATUS */ - ret = sdram->status; - break; - case 0x30: /* SDRAM_RTR */ - ret = sdram->rtr; - break; - case 0x34: /* SDRAM_PMIT */ - ret = sdram->pmit; - break; - case 0x40: /* SDRAM_B0CR */ - ret = sdram->bcr[0]; - break; - case 0x44: /* SDRAM_B1CR */ - ret = sdram->bcr[1]; - break; - case 0x48: /* SDRAM_B2CR */ - ret = sdram->bcr[2]; - break; - case 0x4C: /* SDRAM_B3CR */ - ret = sdram->bcr[3]; - break; - case 0x80: /* SDRAM_TR */ - ret = -1; /* ? */ - break; - case 0x94: /* SDRAM_ECCCFG */ - ret = sdram->ecccfg; - break; - case 0x98: /* SDRAM_ECCESR */ - ret = sdram->eccesr; - break; - default: /* Error */ - ret = -1; - break; - } - break; - default: - /* Avoid gcc warning */ - ret = 0x00000000; - break; - } - - return ret; -} - -static void dcr_write_sdram (void *opaque, int dcrn, uint32_t val) -{ - ppc4xx_sdram_t *sdram; - - sdram = opaque; - switch (dcrn) { - case SDRAM0_CFGADDR: - sdram->addr = val; - break; - case SDRAM0_CFGDATA: - switch (sdram->addr) { - case 0x00: /* SDRAM_BESR0 */ - sdram->besr0 &= ~val; - break; - case 0x08: /* SDRAM_BESR1 */ - sdram->besr1 &= ~val; - break; - case 0x10: /* SDRAM_BEAR */ - sdram->bear = val; - break; - case 0x20: /* SDRAM_CFG */ - val &= 0xFFE00000; - if (!(sdram->cfg & 0x80000000) && (val & 0x80000000)) { -#ifdef DEBUG_SDRAM - printf("%s: enable SDRAM controller\n", __func__); -#endif - /* validate all RAM mappings */ - sdram_map_bcr(sdram); - sdram->status &= ~0x80000000; - } else if ((sdram->cfg & 0x80000000) && !(val & 0x80000000)) { -#ifdef DEBUG_SDRAM - printf("%s: disable SDRAM controller\n", __func__); -#endif - /* invalidate all RAM mappings */ - sdram_unmap_bcr(sdram); - sdram->status |= 0x80000000; - } - if (!(sdram->cfg & 0x40000000) && (val & 0x40000000)) - sdram->status |= 0x40000000; - else if ((sdram->cfg & 0x40000000) && !(val & 0x40000000)) - sdram->status &= ~0x40000000; - sdram->cfg = val; - break; - case 0x24: /* SDRAM_STATUS */ - /* Read-only register */ - break; - case 0x30: /* SDRAM_RTR */ - sdram->rtr = val & 0x3FF80000; - break; - case 0x34: /* SDRAM_PMIT */ - sdram->pmit = (val & 0xF8000000) | 0x07C00000; - break; - case 0x40: /* SDRAM_B0CR */ - sdram_set_bcr(sdram, &sdram->bcr[0], val, sdram->cfg & 0x80000000); - break; - case 0x44: /* SDRAM_B1CR */ - sdram_set_bcr(sdram, &sdram->bcr[1], val, sdram->cfg & 0x80000000); - break; - case 0x48: /* SDRAM_B2CR */ - sdram_set_bcr(sdram, &sdram->bcr[2], val, sdram->cfg & 0x80000000); - break; - case 0x4C: /* SDRAM_B3CR */ - sdram_set_bcr(sdram, &sdram->bcr[3], val, sdram->cfg & 0x80000000); - break; - case 0x80: /* SDRAM_TR */ - sdram->tr = val & 0x018FC01F; - break; - case 0x94: /* SDRAM_ECCCFG */ - sdram->ecccfg = val & 0x00F00000; - break; - case 0x98: /* SDRAM_ECCESR */ - val &= 0xFFF0F000; - if (sdram->eccesr == 0 && val != 0) - qemu_irq_raise(sdram->irq); - else if (sdram->eccesr != 0 && val == 0) - qemu_irq_lower(sdram->irq); - sdram->eccesr = val; - break; - default: /* Error */ - break; - } - break; - } -} - -static void sdram_reset (void *opaque) -{ - ppc4xx_sdram_t *sdram; - - sdram = opaque; - sdram->addr = 0x00000000; - sdram->bear = 0x00000000; - sdram->besr0 = 0x00000000; /* No error */ - sdram->besr1 = 0x00000000; /* No error */ - sdram->cfg = 0x00000000; - sdram->ecccfg = 0x00000000; /* No ECC */ - sdram->eccesr = 0x00000000; /* No error */ - sdram->pmit = 0x07C00000; - sdram->rtr = 0x05F00000; - sdram->tr = 0x00854009; - /* We pre-initialize RAM banks */ - sdram->status = 0x00000000; - sdram->cfg = 0x00800000; -} - -void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks, - MemoryRegion *ram_memories, - hwaddr *ram_bases, - hwaddr *ram_sizes, - int do_init) -{ - ppc4xx_sdram_t *sdram; - - sdram = g_malloc0(sizeof(ppc4xx_sdram_t)); - sdram->irq = irq; - sdram->nbanks = nbanks; - sdram->ram_memories = ram_memories; - memset(sdram->ram_bases, 0, 4 * sizeof(hwaddr)); - memcpy(sdram->ram_bases, ram_bases, - nbanks * sizeof(hwaddr)); - memset(sdram->ram_sizes, 0, 4 * sizeof(hwaddr)); - memcpy(sdram->ram_sizes, ram_sizes, - nbanks * sizeof(hwaddr)); - qemu_register_reset(&sdram_reset, sdram); - ppc_dcr_register(env, SDRAM0_CFGADDR, - sdram, &dcr_read_sdram, &dcr_write_sdram); - ppc_dcr_register(env, SDRAM0_CFGDATA, - sdram, &dcr_read_sdram, &dcr_write_sdram); - if (do_init) - sdram_map_bcr(sdram); -} - -/* Fill in consecutive SDRAM banks with 'ram_size' bytes of memory. - * - * sdram_bank_sizes[] must be 0-terminated. - * - * The 4xx SDRAM controller supports a small number of banks, and each bank - * must be one of a small set of sizes. The number of banks and the supported - * sizes varies by SoC. */ -ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks, - MemoryRegion ram_memories[], - hwaddr ram_bases[], - hwaddr ram_sizes[], - const unsigned int sdram_bank_sizes[]) -{ - MemoryRegion *ram = g_malloc0(sizeof(*ram)); - ram_addr_t size_left = ram_size; - ram_addr_t base = 0; - unsigned int bank_size; - int i; - int j; - - for (i = 0; i < nr_banks; i++) { - for (j = 0; sdram_bank_sizes[j] != 0; j++) { - bank_size = sdram_bank_sizes[j]; - if (bank_size <= size_left) { - size_left -= bank_size; - } - } - if (!size_left) { - /* No need to use the remaining banks. */ - break; - } - } - - ram_size -= size_left; - if (size_left) { - printf("Truncating memory to %d MiB to fit SDRAM controller limits.\n", - (int)(ram_size >> 20)); - } - - memory_region_allocate_system_memory(ram, NULL, "ppc4xx.sdram", ram_size); - - size_left = ram_size; - for (i = 0; i < nr_banks && size_left; i++) { - for (j = 0; sdram_bank_sizes[j] != 0; j++) { - bank_size = sdram_bank_sizes[j]; - - if (bank_size <= size_left) { - char name[32]; - snprintf(name, sizeof(name), "ppc4xx.sdram%d", i); - memory_region_init_alias(&ram_memories[i], NULL, name, ram, - base, bank_size); - ram_bases[i] = base; - ram_sizes[i] = bank_size; - base += bank_size; - size_left -= bank_size; - break; - } - } - } - - return ram_size; -} diff --git a/qemu/hw/ppc/ppc4xx_pci.c b/qemu/hw/ppc/ppc4xx_pci.c deleted file mode 100644 index 683218e5c..000000000 --- a/qemu/hw/ppc/ppc4xx_pci.c +++ /dev/null @@ -1,393 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - * Copyright IBM Corp. 2008 - * - * Authors: Hollis Blanchard - */ - -/* 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" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "exec/address-spaces.h" - -#undef DEBUG -#ifdef DEBUG -#define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) -#endif /* DEBUG */ - -struct PCIMasterMap { - uint32_t la; - uint32_t ma; - uint32_t pcila; - uint32_t pciha; -}; - -struct PCITargetMap { - uint32_t ms; - uint32_t la; -}; - -#define PPC4xx_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(PPC4xxPCIState, (obj), TYPE_PPC4xx_PCI_HOST_BRIDGE) - -#define PPC4xx_PCI_NR_PMMS 3 -#define PPC4xx_PCI_NR_PTMS 2 - -struct PPC4xxPCIState { - PCIHostState parent_obj; - - struct PCIMasterMap pmm[PPC4xx_PCI_NR_PMMS]; - struct PCITargetMap ptm[PPC4xx_PCI_NR_PTMS]; - qemu_irq irq[4]; - - MemoryRegion container; - MemoryRegion iomem; -}; -typedef struct PPC4xxPCIState PPC4xxPCIState; - -#define PCIC0_CFGADDR 0x0 -#define PCIC0_CFGDATA 0x4 - -/* PLB Memory Map (PMM) registers specify which PLB addresses are translated to - * PCI accesses. */ -#define PCIL0_PMM0LA 0x0 -#define PCIL0_PMM0MA 0x4 -#define PCIL0_PMM0PCILA 0x8 -#define PCIL0_PMM0PCIHA 0xc -#define PCIL0_PMM1LA 0x10 -#define PCIL0_PMM1MA 0x14 -#define PCIL0_PMM1PCILA 0x18 -#define PCIL0_PMM1PCIHA 0x1c -#define PCIL0_PMM2LA 0x20 -#define PCIL0_PMM2MA 0x24 -#define PCIL0_PMM2PCILA 0x28 -#define PCIL0_PMM2PCIHA 0x2c - -/* PCI Target Map (PTM) registers specify which PCI addresses are translated to - * PLB accesses. */ -#define PCIL0_PTM1MS 0x30 -#define PCIL0_PTM1LA 0x34 -#define PCIL0_PTM2MS 0x38 -#define PCIL0_PTM2LA 0x3c -#define PCI_REG_BASE 0x800000 -#define PCI_REG_SIZE 0x40 - -#define PCI_ALL_SIZE (PCI_REG_BASE + PCI_REG_SIZE) - -static void ppc4xx_pci_reg_write4(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - struct PPC4xxPCIState *pci = opaque; - - /* We ignore all target attempts at PCI configuration, effectively - * assuming a bidirectional 1:1 mapping of PLB and PCI space. */ - - switch (offset) { - case PCIL0_PMM0LA: - pci->pmm[0].la = value; - break; - case PCIL0_PMM0MA: - pci->pmm[0].ma = value; - break; - case PCIL0_PMM0PCIHA: - pci->pmm[0].pciha = value; - break; - case PCIL0_PMM0PCILA: - pci->pmm[0].pcila = value; - break; - - case PCIL0_PMM1LA: - pci->pmm[1].la = value; - break; - case PCIL0_PMM1MA: - pci->pmm[1].ma = value; - break; - case PCIL0_PMM1PCIHA: - pci->pmm[1].pciha = value; - break; - case PCIL0_PMM1PCILA: - pci->pmm[1].pcila = value; - break; - - case PCIL0_PMM2LA: - pci->pmm[2].la = value; - break; - case PCIL0_PMM2MA: - pci->pmm[2].ma = value; - break; - case PCIL0_PMM2PCIHA: - pci->pmm[2].pciha = value; - break; - case PCIL0_PMM2PCILA: - pci->pmm[2].pcila = value; - break; - - case PCIL0_PTM1MS: - pci->ptm[0].ms = value; - break; - case PCIL0_PTM1LA: - pci->ptm[0].la = value; - break; - case PCIL0_PTM2MS: - pci->ptm[1].ms = value; - break; - case PCIL0_PTM2LA: - pci->ptm[1].la = value; - break; - - default: - printf("%s: unhandled PCI internal register 0x%lx\n", __func__, - (unsigned long)offset); - break; - } -} - -static uint64_t ppc4xx_pci_reg_read4(void *opaque, hwaddr offset, - unsigned size) -{ - struct PPC4xxPCIState *pci = opaque; - uint32_t value; - - switch (offset) { - case PCIL0_PMM0LA: - value = pci->pmm[0].la; - break; - case PCIL0_PMM0MA: - value = pci->pmm[0].ma; - break; - case PCIL0_PMM0PCIHA: - value = pci->pmm[0].pciha; - break; - case PCIL0_PMM0PCILA: - value = pci->pmm[0].pcila; - break; - - case PCIL0_PMM1LA: - value = pci->pmm[1].la; - break; - case PCIL0_PMM1MA: - value = pci->pmm[1].ma; - break; - case PCIL0_PMM1PCIHA: - value = pci->pmm[1].pciha; - break; - case PCIL0_PMM1PCILA: - value = pci->pmm[1].pcila; - break; - - case PCIL0_PMM2LA: - value = pci->pmm[2].la; - break; - case PCIL0_PMM2MA: - value = pci->pmm[2].ma; - break; - case PCIL0_PMM2PCIHA: - value = pci->pmm[2].pciha; - break; - case PCIL0_PMM2PCILA: - value = pci->pmm[2].pcila; - break; - - case PCIL0_PTM1MS: - value = pci->ptm[0].ms; - break; - case PCIL0_PTM1LA: - value = pci->ptm[0].la; - break; - case PCIL0_PTM2MS: - value = pci->ptm[1].ms; - break; - case PCIL0_PTM2LA: - value = pci->ptm[1].la; - break; - - default: - printf("%s: invalid PCI internal register 0x%lx\n", __func__, - (unsigned long)offset); - value = 0; - } - - return value; -} - -static const MemoryRegionOps pci_reg_ops = { - .read = ppc4xx_pci_reg_read4, - .write = ppc4xx_pci_reg_write4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void ppc4xx_pci_reset(void *opaque) -{ - struct PPC4xxPCIState *pci = opaque; - - memset(pci->pmm, 0, sizeof(pci->pmm)); - memset(pci->ptm, 0, sizeof(pci->ptm)); -} - -/* On Bamboo, all pins from each slot are tied to a single board IRQ. This - * may need further refactoring for other boards. */ -static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num) -{ - int slot = pci_dev->devfn >> 3; - - DPRINTF("%s: devfn %x irq %d -> %d\n", __func__, - pci_dev->devfn, irq_num, slot); - - return slot - 1; -} - -static void ppc4xx_pci_set_irq(void *opaque, int irq_num, int level) -{ - qemu_irq *pci_irqs = opaque; - - DPRINTF("%s: PCI irq %d\n", __func__, irq_num); - if (irq_num < 0) { - fprintf(stderr, "%s: PCI irq %d\n", __func__, irq_num); - return; - } - qemu_set_irq(pci_irqs[irq_num], level); -} - -static const VMStateDescription vmstate_pci_master_map = { - .name = "pci_master_map", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(la, struct PCIMasterMap), - VMSTATE_UINT32(ma, struct PCIMasterMap), - VMSTATE_UINT32(pcila, struct PCIMasterMap), - VMSTATE_UINT32(pciha, struct PCIMasterMap), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_pci_target_map = { - .name = "pci_target_map", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(ms, struct PCITargetMap), - VMSTATE_UINT32(la, struct PCITargetMap), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_ppc4xx_pci = { - .name = "ppc4xx_pci", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(pmm, PPC4xxPCIState, PPC4xx_PCI_NR_PMMS, 1, - vmstate_pci_master_map, - struct PCIMasterMap), - VMSTATE_STRUCT_ARRAY(ptm, PPC4xxPCIState, PPC4xx_PCI_NR_PTMS, 1, - vmstate_pci_target_map, - struct PCITargetMap), - VMSTATE_END_OF_LIST() - } -}; - -/* XXX Interrupt acknowledge cycles not supported. */ -static int ppc4xx_pcihost_initfn(SysBusDevice *dev) -{ - PPC4xxPCIState *s; - PCIHostState *h; - PCIBus *b; - int i; - - h = PCI_HOST_BRIDGE(dev); - s = PPC4xx_PCI_HOST_BRIDGE(dev); - - for (i = 0; i < ARRAY_SIZE(s->irq); i++) { - sysbus_init_irq(dev, &s->irq[i]); - } - - b = pci_register_bus(DEVICE(dev), NULL, ppc4xx_pci_set_irq, - ppc4xx_pci_map_irq, s->irq, get_system_memory(), - get_system_io(), 0, 4, TYPE_PCI_BUS); - h->bus = b; - - pci_create_simple(b, 0, "ppc4xx-host-bridge"); - - /* XXX split into 2 memory regions, one for config space, one for regs */ - memory_region_init(&s->container, OBJECT(s), "pci-container", PCI_ALL_SIZE); - memory_region_init_io(&h->conf_mem, OBJECT(s), &pci_host_conf_le_ops, h, - "pci-conf-idx", 4); - memory_region_init_io(&h->data_mem, OBJECT(s), &pci_host_data_le_ops, h, - "pci-conf-data", 4); - memory_region_init_io(&s->iomem, OBJECT(s), &pci_reg_ops, s, - "pci.reg", PCI_REG_SIZE); - memory_region_add_subregion(&s->container, PCIC0_CFGADDR, &h->conf_mem); - memory_region_add_subregion(&s->container, PCIC0_CFGDATA, &h->data_mem); - memory_region_add_subregion(&s->container, PCI_REG_BASE, &s->iomem); - sysbus_init_mmio(dev, &s->container); - qemu_register_reset(ppc4xx_pci_reset, s); - - return 0; -} - -static void ppc4xx_host_bridge_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->desc = "Host bridge"; - k->vendor_id = PCI_VENDOR_ID_IBM; - k->device_id = PCI_DEVICE_ID_IBM_440GX; - k->class_id = PCI_CLASS_BRIDGE_OTHER; - /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo ppc4xx_host_bridge_info = { - .name = "ppc4xx-host-bridge", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = ppc4xx_host_bridge_class_init, -}; - -static void ppc4xx_pcihost_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->init = ppc4xx_pcihost_initfn; - dc->vmsd = &vmstate_ppc4xx_pci; -} - -static const TypeInfo ppc4xx_pcihost_info = { - .name = TYPE_PPC4xx_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(PPC4xxPCIState), - .class_init = ppc4xx_pcihost_class_init, -}; - -static void ppc4xx_pci_register_types(void) -{ - type_register_static(&ppc4xx_pcihost_info); - type_register_static(&ppc4xx_host_bridge_info); -} - -type_init(ppc4xx_pci_register_types) diff --git a/qemu/hw/ppc/ppc_booke.c b/qemu/hw/ppc/ppc_booke.c deleted file mode 100644 index ab8d026c3..000000000 --- a/qemu/hw/ppc/ppc_booke.c +++ /dev/null @@ -1,368 +0,0 @@ -/* - * QEMU PowerPC Booke hardware System Emulator - * - * Copyright (c) 2011 AdaCore - * - * 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 "qemu-common.h" -#include "cpu.h" -#include "hw/hw.h" -#include "hw/ppc/ppc.h" -#include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "hw/timer/m48t59.h" -#include "qemu/log.h" -#include "hw/loader.h" -#include "kvm_ppc.h" - - -/* Timer Control Register */ - -#define TCR_WP_SHIFT 30 /* Watchdog Timer Period */ -#define TCR_WP_MASK (0x3U << TCR_WP_SHIFT) -#define TCR_WRC_SHIFT 28 /* Watchdog Timer Reset Control */ -#define TCR_WRC_MASK (0x3U << TCR_WRC_SHIFT) -#define TCR_WIE (1U << 27) /* Watchdog Timer Interrupt Enable */ -#define TCR_DIE (1U << 26) /* Decrementer Interrupt Enable */ -#define TCR_FP_SHIFT 24 /* Fixed-Interval Timer Period */ -#define TCR_FP_MASK (0x3U << TCR_FP_SHIFT) -#define TCR_FIE (1U << 23) /* Fixed-Interval Timer Interrupt Enable */ -#define TCR_ARE (1U << 22) /* Auto-Reload Enable */ - -/* Timer Control Register (e500 specific fields) */ - -#define TCR_E500_FPEXT_SHIFT 13 /* Fixed-Interval Timer Period Extension */ -#define TCR_E500_FPEXT_MASK (0xf << TCR_E500_FPEXT_SHIFT) -#define TCR_E500_WPEXT_SHIFT 17 /* Watchdog Timer Period Extension */ -#define TCR_E500_WPEXT_MASK (0xf << TCR_E500_WPEXT_SHIFT) - -/* Timer Status Register */ - -#define TSR_FIS (1U << 26) /* Fixed-Interval Timer Interrupt Status */ -#define TSR_DIS (1U << 27) /* Decrementer Interrupt Status */ -#define TSR_WRS_SHIFT 28 /* Watchdog Timer Reset Status */ -#define TSR_WRS_MASK (0x3U << TSR_WRS_SHIFT) -#define TSR_WIS (1U << 30) /* Watchdog Timer Interrupt Status */ -#define TSR_ENW (1U << 31) /* Enable Next Watchdog Timer */ - -typedef struct booke_timer_t booke_timer_t; -struct booke_timer_t { - - uint64_t fit_next; - QEMUTimer *fit_timer; - - uint64_t wdt_next; - QEMUTimer *wdt_timer; - - uint32_t flags; -}; - -static void booke_update_irq(PowerPCCPU *cpu) -{ - CPUPPCState *env = &cpu->env; - - ppc_set_irq(cpu, PPC_INTERRUPT_DECR, - (env->spr[SPR_BOOKE_TSR] & TSR_DIS - && env->spr[SPR_BOOKE_TCR] & TCR_DIE)); - - ppc_set_irq(cpu, PPC_INTERRUPT_WDT, - (env->spr[SPR_BOOKE_TSR] & TSR_WIS - && env->spr[SPR_BOOKE_TCR] & TCR_WIE)); - - ppc_set_irq(cpu, PPC_INTERRUPT_FIT, - (env->spr[SPR_BOOKE_TSR] & TSR_FIS - && env->spr[SPR_BOOKE_TCR] & TCR_FIE)); -} - -/* Return the location of the bit of time base at which the FIT will raise an - interrupt */ -static uint8_t booke_get_fit_target(CPUPPCState *env, ppc_tb_t *tb_env) -{ - uint8_t fp = (env->spr[SPR_BOOKE_TCR] & TCR_FP_MASK) >> TCR_FP_SHIFT; - - if (tb_env->flags & PPC_TIMER_E500) { - /* e500 Fixed-interval timer period extension */ - uint32_t fpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_FPEXT_MASK) - >> TCR_E500_FPEXT_SHIFT; - fp = 63 - (fp | fpext << 2); - } else { - fp = env->fit_period[fp]; - } - - return fp; -} - -/* Return the location of the bit of time base at which the WDT will raise an - interrupt */ -static uint8_t booke_get_wdt_target(CPUPPCState *env, ppc_tb_t *tb_env) -{ - uint8_t wp = (env->spr[SPR_BOOKE_TCR] & TCR_WP_MASK) >> TCR_WP_SHIFT; - - if (tb_env->flags & PPC_TIMER_E500) { - /* e500 Watchdog timer period extension */ - uint32_t wpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_WPEXT_MASK) - >> TCR_E500_WPEXT_SHIFT; - wp = 63 - (wp | wpext << 2); - } else { - wp = env->wdt_period[wp]; - } - - return wp; -} - -static void booke_update_fixed_timer(CPUPPCState *env, - uint8_t target_bit, - uint64_t *next, - QEMUTimer *timer, - int tsr_bit) -{ - ppc_tb_t *tb_env = env->tb_env; - uint64_t delta_tick, ticks = 0; - uint64_t tb; - uint64_t period; - uint64_t now; - - if (!(env->spr[SPR_BOOKE_TSR] & tsr_bit)) { - /* - * Don't arm the timer again when the guest has the current - * interrupt still pending. Wait for it to ack it. - */ - return; - } - - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - tb = cpu_ppc_get_tb(tb_env, now, tb_env->tb_offset); - period = 1ULL << target_bit; - delta_tick = period - (tb & (period - 1)); - - /* the timer triggers only when the selected bit toggles from 0 to 1 */ - if (tb & period) { - ticks = period; - } - - if (ticks + delta_tick < ticks) { - /* Overflow, so assume the biggest number we can express. */ - ticks = UINT64_MAX; - } else { - ticks += delta_tick; - } - - *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; - } - - /* XXX: If expire time is now. We can't run the callback because we don't - * have access to it. So we just set the timer one nanosecond later. - */ - - if (*next == now) { - (*next)++; - } else { - /* - * There's no point to fake any granularity that's more fine grained - * than milliseconds. Anything beyond that just overloads the system. - */ - *next = MAX(*next, now + SCALE_MS); - } - - /* Fire the next timer */ - timer_mod(timer, *next); -} - -static void booke_decr_cb(void *opaque) -{ - PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; - - env->spr[SPR_BOOKE_TSR] |= TSR_DIS; - booke_update_irq(cpu); - - if (env->spr[SPR_BOOKE_TCR] & TCR_ARE) { - /* Auto Reload */ - cpu_ppc_store_decr(env, env->spr[SPR_BOOKE_DECAR]); - } -} - -static void booke_fit_cb(void *opaque) -{ - PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; - ppc_tb_t *tb_env; - booke_timer_t *booke_timer; - - tb_env = env->tb_env; - booke_timer = tb_env->opaque; - env->spr[SPR_BOOKE_TSR] |= TSR_FIS; - - booke_update_irq(cpu); - - booke_update_fixed_timer(env, - booke_get_fit_target(env, tb_env), - &booke_timer->fit_next, - booke_timer->fit_timer, - TSR_FIS); -} - -static void booke_wdt_cb(void *opaque) -{ - PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; - ppc_tb_t *tb_env; - booke_timer_t *booke_timer; - - tb_env = env->tb_env; - booke_timer = tb_env->opaque; - - /* TODO: There's lots of complicated stuff to do here */ - - booke_update_irq(cpu); - - booke_update_fixed_timer(env, - booke_get_wdt_target(env, tb_env), - &booke_timer->wdt_next, - booke_timer->wdt_timer, - TSR_WIS); -} - -void store_booke_tsr(CPUPPCState *env, target_ulong val) -{ - PowerPCCPU *cpu = ppc_env_get_cpu(env); - ppc_tb_t *tb_env = env->tb_env; - booke_timer_t *booke_timer = tb_env->opaque; - - env->spr[SPR_BOOKE_TSR] &= ~val; - kvmppc_clear_tsr_bits(cpu, val); - - if (val & TSR_FIS) { - booke_update_fixed_timer(env, - booke_get_fit_target(env, tb_env), - &booke_timer->fit_next, - booke_timer->fit_timer, - TSR_FIS); - } - - if (val & TSR_WIS) { - booke_update_fixed_timer(env, - booke_get_wdt_target(env, tb_env), - &booke_timer->wdt_next, - booke_timer->wdt_timer, - TSR_WIS); - } - - booke_update_irq(cpu); -} - -void store_booke_tcr(CPUPPCState *env, target_ulong val) -{ - PowerPCCPU *cpu = ppc_env_get_cpu(env); - ppc_tb_t *tb_env = env->tb_env; - booke_timer_t *booke_timer = tb_env->opaque; - - tb_env = env->tb_env; - env->spr[SPR_BOOKE_TCR] = val; - kvmppc_set_tcr(cpu); - - booke_update_irq(cpu); - - booke_update_fixed_timer(env, - booke_get_fit_target(env, tb_env), - &booke_timer->fit_next, - booke_timer->fit_timer, - TSR_FIS); - - booke_update_fixed_timer(env, - booke_get_wdt_target(env, tb_env), - &booke_timer->wdt_next, - booke_timer->wdt_timer, - TSR_WIS); -} - -static void ppc_booke_timer_reset_handle(void *opaque) -{ - PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; - - store_booke_tcr(env, 0); - store_booke_tsr(env, -1); -} - -/* - * This function will be called whenever the CPU state changes. - * CPU states are defined "typedef enum RunState". - * Regarding timer, When CPU state changes to running after debug halt - * or similar cases which takes time then in between final watchdog - * expiry happenes. This will cause exit to QEMU and configured watchdog - * action will be taken. To avoid this we always clear the watchdog state when - * state changes to running. - */ -static void cpu_state_change_handler(void *opaque, int running, RunState state) -{ - PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; - - if (!running) { - return; - } - - /* - * Clear watchdog interrupt condition by clearing TSR. - */ - store_booke_tsr(env, TSR_ENW | TSR_WIS | TSR_WRS_MASK); -} - -void ppc_booke_timers_init(PowerPCCPU *cpu, uint32_t freq, uint32_t flags) -{ - ppc_tb_t *tb_env; - booke_timer_t *booke_timer; - int ret = 0; - - tb_env = g_malloc0(sizeof(ppc_tb_t)); - booke_timer = g_malloc0(sizeof(booke_timer_t)); - - cpu->env.tb_env = tb_env; - tb_env->flags = flags | PPC_TIMER_BOOKE | PPC_DECR_ZERO_TRIGGERED; - - tb_env->tb_freq = freq; - tb_env->decr_freq = freq; - tb_env->opaque = booke_timer; - tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &booke_decr_cb, cpu); - - booke_timer->fit_timer = - timer_new_ns(QEMU_CLOCK_VIRTUAL, &booke_fit_cb, cpu); - booke_timer->wdt_timer = - timer_new_ns(QEMU_CLOCK_VIRTUAL, &booke_wdt_cb, cpu); - - ret = kvmppc_booke_watchdog_enable(cpu); - - if (ret) { - /* TODO: Start the QEMU emulated watchdog if not running on KVM. - * Also start the QEMU emulated watchdog if KVM does not support - * emulated watchdog or somehow it is not enabled (supported but - * not enabled is though some bug and requires debugging :)). - */ - } - - qemu_add_vm_change_state_handler(cpu_state_change_handler, cpu); - - qemu_register_reset(ppc_booke_timer_reset_handle, cpu); -} diff --git a/qemu/hw/ppc/ppce500_spin.c b/qemu/hw/ppc/ppce500_spin.c deleted file mode 100644 index 76bd78bfd..000000000 --- a/qemu/hw/ppc/ppce500_spin.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * QEMU PowerPC e500v2 ePAPR spinning code - * - * Copyright (C) 2011 Freescale Semiconductor, Inc. All rights reserved. - * - * Author: Alexander Graf, - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 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 . - * - * This code is not really a device, but models an interface that usually - * firmware takes care of. It's used when QEMU plays the role of firmware. - * - * Specification: - * - * https://www.power.org/resources/downloads/Power_ePAPR_APPROVED_v1.1.pdf - * - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "sysemu/sysemu.h" -#include "hw/sysbus.h" -#include "sysemu/kvm.h" - -#define MAX_CPUS 32 - -typedef struct spin_info { - uint64_t addr; - uint64_t r3; - uint32_t resv; - uint32_t pir; - uint64_t reserved; -} QEMU_PACKED SpinInfo; - -#define TYPE_E500_SPIN "e500-spin" -#define E500_SPIN(obj) OBJECT_CHECK(SpinState, (obj), TYPE_E500_SPIN) - -typedef struct SpinState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - SpinInfo spin[MAX_CPUS]; -} SpinState; - -typedef struct spin_kick { - PowerPCCPU *cpu; - SpinInfo *spin; -} SpinKick; - -static void spin_reset(void *opaque) -{ - SpinState *s = opaque; - int i; - - for (i = 0; i < MAX_CPUS; i++) { - SpinInfo *info = &s->spin[i]; - - stl_p(&info->pir, i); - stq_p(&info->r3, i); - stq_p(&info->addr, 1); - } -} - -/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */ -static inline hwaddr booke206_page_size_to_tlb(uint64_t size) -{ - return ctz32(size >> 10) >> 1; -} - -static void mmubooke_create_initial_mapping(CPUPPCState *env, - target_ulong va, - hwaddr pa, - hwaddr len) -{ - ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 1); - hwaddr size; - - size = (booke206_page_size_to_tlb(len) << MAS1_TSIZE_SHIFT); - tlb->mas1 = MAS1_VALID | size; - tlb->mas2 = (va & TARGET_PAGE_MASK) | MAS2_M; - tlb->mas7_3 = pa & TARGET_PAGE_MASK; - tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX; - env->tlb_dirty = true; -} - -static void spin_kick(void *data) -{ - SpinKick *kick = data; - CPUState *cpu = CPU(kick->cpu); - CPUPPCState *env = &kick->cpu->env; - SpinInfo *curspin = kick->spin; - hwaddr map_size = 64 * 1024 * 1024; - hwaddr map_start; - - cpu_synchronize_state(cpu); - stl_p(&curspin->pir, env->spr[SPR_PIR]); - env->nip = ldq_p(&curspin->addr) & (map_size - 1); - env->gpr[3] = ldq_p(&curspin->r3); - env->gpr[4] = 0; - env->gpr[5] = 0; - env->gpr[6] = 0; - env->gpr[7] = map_size; - env->gpr[8] = 0; - env->gpr[9] = 0; - - map_start = ldq_p(&curspin->addr) & ~(map_size - 1); - mmubooke_create_initial_mapping(env, 0, map_start, map_size); - - cpu->halted = 0; - cpu->exception_index = -1; - cpu->stopped = false; - qemu_cpu_kick(cpu); -} - -static void spin_write(void *opaque, hwaddr addr, uint64_t value, - unsigned len) -{ - SpinState *s = opaque; - int env_idx = addr / sizeof(SpinInfo); - CPUState *cpu; - SpinInfo *curspin = &s->spin[env_idx]; - uint8_t *curspin_p = (uint8_t*)curspin; - - cpu = qemu_get_cpu(env_idx); - if (cpu == NULL) { - /* Unknown CPU */ - return; - } - - if (cpu->cpu_index == 0) { - /* primary CPU doesn't spin */ - return; - } - - curspin_p = &curspin_p[addr % sizeof(SpinInfo)]; - switch (len) { - case 1: - stb_p(curspin_p, value); - break; - case 2: - stw_p(curspin_p, value); - break; - case 4: - stl_p(curspin_p, value); - break; - } - - if (!(ldq_p(&curspin->addr) & 1)) { - /* run CPU */ - SpinKick kick = { - .cpu = POWERPC_CPU(cpu), - .spin = curspin, - }; - - run_on_cpu(cpu, spin_kick, &kick); - } -} - -static uint64_t spin_read(void *opaque, hwaddr addr, unsigned len) -{ - SpinState *s = opaque; - uint8_t *spin_p = &((uint8_t*)s->spin)[addr]; - - switch (len) { - case 1: - return ldub_p(spin_p); - case 2: - return lduw_p(spin_p); - case 4: - return ldl_p(spin_p); - default: - hw_error("ppce500: unexpected %s with len = %u", __func__, len); - } -} - -static const MemoryRegionOps spin_rw_ops = { - .read = spin_read, - .write = spin_write, - .endianness = DEVICE_BIG_ENDIAN, -}; - -static int ppce500_spin_initfn(SysBusDevice *dev) -{ - SpinState *s = E500_SPIN(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &spin_rw_ops, s, - "e500 spin pv device", sizeof(SpinInfo) * MAX_CPUS); - sysbus_init_mmio(dev, &s->iomem); - - qemu_register_reset(spin_reset, s); - - return 0; -} - -static void ppce500_spin_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = ppce500_spin_initfn; -} - -static const TypeInfo ppce500_spin_info = { - .name = TYPE_E500_SPIN, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SpinState), - .class_init = ppce500_spin_class_init, -}; - -static void ppce500_spin_register_types(void) -{ - type_register_static(&ppce500_spin_info); -} - -type_init(ppce500_spin_register_types) diff --git a/qemu/hw/ppc/prep.c b/qemu/hw/ppc/prep.c deleted file mode 100644 index 3ffb85e60..000000000 --- a/qemu/hw/ppc/prep.c +++ /dev/null @@ -1,679 +0,0 @@ -/* - * QEMU PPC PREP hardware System Emulator - * - * Copyright (c) 2003-2007 Jocelyn Mayer - * - * 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/timer/m48t59.h" -#include "hw/i386/pc.h" -#include "hw/char/serial.h" -#include "hw/block/fdc.h" -#include "net/net.h" -#include "sysemu/sysemu.h" -#include "hw/isa/isa.h" -#include "hw/pci/pci.h" -#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" -#include "hw/timer/mc146818rtc.h" -#include "hw/isa/pc87312.h" -#include "sysemu/block-backend.h" -#include "sysemu/arch_init.h" -#include "sysemu/qtest.h" -#include "exec/address-spaces.h" -#include "trace.h" -#include "elf.h" -#include "qemu/cutils.h" - -/* SMP is not enabled, for now */ -#define MAX_CPUS 1 - -#define MAX_IDE_BUS 2 - -#define BIOS_SIZE (1024 * 1024) -#define BIOS_FILENAME "ppc_rom.bin" -#define KERNEL_LOAD_ADDR 0x01000000 -#define INITRD_LOAD_ADDR 0x01800000 - -/* Constants for devices init */ -static const int ide_iobase[2] = { 0x1f0, 0x170 }; -static const int ide_iobase2[2] = { 0x3f6, 0x376 }; -static const int ide_irq[2] = { 13, 13 }; - -#define NE2000_NB_MAX 6 - -static uint32_t ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280, 0x380 }; -static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 }; - -/* ISA IO ports bridge */ -#define PPC_IO_BASE 0x80000000 - -/* PowerPC control and status registers */ -#if 0 // Not used -static struct { - /* IDs */ - uint32_t veni_devi; - uint32_t revi; - /* Control and status */ - uint32_t gcsr; - uint32_t xcfr; - uint32_t ct32; - uint32_t mcsr; - /* General purpose registers */ - uint32_t gprg[6]; - /* Exceptions */ - uint32_t feen; - uint32_t fest; - uint32_t fema; - uint32_t fecl; - uint32_t eeen; - uint32_t eest; - uint32_t eecl; - uint32_t eeint; - uint32_t eemck0; - uint32_t eemck1; - /* Error diagnostic */ -} XCSR; - -static void PPC_XCSR_writeb (void *opaque, - hwaddr addr, uint32_t value) -{ - printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr, - value); -} - -static void PPC_XCSR_writew (void *opaque, - hwaddr addr, uint32_t value) -{ - printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr, - value); -} - -static void PPC_XCSR_writel (void *opaque, - hwaddr addr, uint32_t value) -{ - printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr, - value); -} - -static uint32_t PPC_XCSR_readb (void *opaque, hwaddr addr) -{ - uint32_t retval = 0; - - printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr, - retval); - - return retval; -} - -static uint32_t PPC_XCSR_readw (void *opaque, hwaddr addr) -{ - uint32_t retval = 0; - - printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr, - retval); - - return retval; -} - -static uint32_t PPC_XCSR_readl (void *opaque, hwaddr addr) -{ - uint32_t retval = 0; - - printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr, - retval); - - return retval; -} - -static const MemoryRegionOps PPC_XCSR_ops = { - .old_mmio = { - .read = { PPC_XCSR_readb, PPC_XCSR_readw, PPC_XCSR_readl, }, - .write = { PPC_XCSR_writeb, PPC_XCSR_writew, PPC_XCSR_writel, }, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -#endif - -/* Fake super-io ports for PREP platform (Intel 82378ZB) */ -typedef struct sysctrl_t { - qemu_irq reset_irq; - Nvram *nvram; - uint8_t state; - uint8_t syscontrol; - int contiguous_map; - qemu_irq contiguous_map_irq; - int endian; -} sysctrl_t; - -enum { - STATE_HARDFILE = 0x01, -}; - -static sysctrl_t *sysctrl; - -static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val) -{ - sysctrl_t *sysctrl = opaque; - - trace_prep_io_800_writeb(addr - PPC_IO_BASE, val); - switch (addr) { - case 0x0092: - /* Special port 92 */ - /* Check soft reset asked */ - if (val & 0x01) { - qemu_irq_raise(sysctrl->reset_irq); - } else { - qemu_irq_lower(sysctrl->reset_irq); - } - /* Check LE mode */ - if (val & 0x02) { - sysctrl->endian = 1; - } else { - sysctrl->endian = 0; - } - break; - case 0x0800: - /* Motorola CPU configuration register : read-only */ - break; - case 0x0802: - /* Motorola base module feature register : read-only */ - break; - case 0x0803: - /* Motorola base module status register : read-only */ - break; - case 0x0808: - /* Hardfile light register */ - if (val & 1) - sysctrl->state |= STATE_HARDFILE; - else - sysctrl->state &= ~STATE_HARDFILE; - break; - case 0x0810: - /* Password protect 1 register */ - if (sysctrl->nvram != NULL) { - NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram); - (k->toggle_lock)(sysctrl->nvram, 1); - } - break; - case 0x0812: - /* Password protect 2 register */ - if (sysctrl->nvram != NULL) { - NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram); - (k->toggle_lock)(sysctrl->nvram, 2); - } - break; - case 0x0814: - /* L2 invalidate register */ - // tlb_flush(first_cpu, 1); - break; - case 0x081C: - /* system control register */ - sysctrl->syscontrol = val & 0x0F; - break; - case 0x0850: - /* I/O map type register */ - sysctrl->contiguous_map = val & 0x01; - qemu_set_irq(sysctrl->contiguous_map_irq, sysctrl->contiguous_map); - break; - default: - printf("ERROR: unaffected IO port write: %04" PRIx32 - " => %02" PRIx32"\n", addr, val); - break; - } -} - -static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr) -{ - sysctrl_t *sysctrl = opaque; - uint32_t retval = 0xFF; - - switch (addr) { - case 0x0092: - /* Special port 92 */ - retval = sysctrl->endian << 1; - break; - case 0x0800: - /* Motorola CPU configuration register */ - retval = 0xEF; /* MPC750 */ - break; - case 0x0802: - /* Motorola Base module feature register */ - retval = 0xAD; /* No ESCC, PMC slot neither ethernet */ - break; - case 0x0803: - /* Motorola base module status register */ - retval = 0xE0; /* Standard MPC750 */ - break; - case 0x080C: - /* Equipment present register: - * no L2 cache - * no upgrade processor - * no cards in PCI slots - * SCSI fuse is bad - */ - retval = 0x3C; - break; - case 0x0810: - /* Motorola base module extended feature register */ - retval = 0x39; /* No USB, CF and PCI bridge. NVRAM present */ - break; - case 0x0814: - /* L2 invalidate: don't care */ - break; - case 0x0818: - /* Keylock */ - retval = 0x00; - break; - case 0x081C: - /* system control register - * 7 - 6 / 1 - 0: L2 cache enable - */ - retval = sysctrl->syscontrol; - break; - case 0x0823: - /* */ - retval = 0x03; /* no L2 cache */ - break; - case 0x0850: - /* I/O map type register */ - retval = sysctrl->contiguous_map; - break; - default: - printf("ERROR: unaffected IO port: %04" PRIx32 " read\n", addr); - break; - } - trace_prep_io_800_readb(addr - PPC_IO_BASE, retval); - - return retval; -} - - -#define NVRAM_SIZE 0x2000 - -static void ppc_prep_reset(void *opaque) -{ - PowerPCCPU *cpu = opaque; - - cpu_reset(CPU(cpu)); -} - -static const MemoryRegionPortio prep_portio_list[] = { - /* System control ports */ - { 0x0092, 1, 1, .read = PREP_io_800_readb, .write = PREP_io_800_writeb, }, - { 0x0800, 0x52, 1, - .read = PREP_io_800_readb, .write = PREP_io_800_writeb, }, - /* Special port to get debug messages from Open-Firmware */ - { 0x0F00, 4, 1, .write = PPC_debug_write, }, - PORTIO_END_OF_LIST(), -}; - -static PortioList prep_port_list; - -/*****************************************************************************/ -/* NVRAM helpers */ -static inline uint32_t nvram_read(Nvram *nvram, uint32_t addr) -{ - NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram); - return (k->read)(nvram, addr); -} - -static inline void nvram_write(Nvram *nvram, uint32_t addr, uint32_t val) -{ - NvramClass *k = NVRAM_GET_CLASS(sysctrl->nvram); - (k->write)(nvram, addr, val); -} - -static void NVRAM_set_byte(Nvram *nvram, uint32_t addr, uint8_t value) -{ - nvram_write(nvram, addr, value); -} - -static uint8_t NVRAM_get_byte(Nvram *nvram, uint32_t addr) -{ - return nvram_read(nvram, addr); -} - -static void NVRAM_set_word(Nvram *nvram, uint32_t addr, uint16_t value) -{ - nvram_write(nvram, addr, value >> 8); - nvram_write(nvram, addr + 1, value & 0xFF); -} - -static uint16_t NVRAM_get_word(Nvram *nvram, uint32_t addr) -{ - uint16_t tmp; - - tmp = nvram_read(nvram, addr) << 8; - tmp |= nvram_read(nvram, addr + 1); - - return tmp; -} - -static void NVRAM_set_lword(Nvram *nvram, uint32_t addr, uint32_t value) -{ - nvram_write(nvram, addr, value >> 24); - nvram_write(nvram, addr + 1, (value >> 16) & 0xFF); - nvram_write(nvram, addr + 2, (value >> 8) & 0xFF); - nvram_write(nvram, addr + 3, value & 0xFF); -} - -static void NVRAM_set_string(Nvram *nvram, uint32_t addr, const char *str, - uint32_t max) -{ - int i; - - for (i = 0; i < max && str[i] != '\0'; i++) { - nvram_write(nvram, addr + i, str[i]); - } - nvram_write(nvram, addr + i, str[i]); - nvram_write(nvram, addr + max - 1, '\0'); -} - -static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value) -{ - uint16_t tmp; - uint16_t pd, pd1, pd2; - - tmp = prev >> 8; - pd = prev ^ value; - pd1 = pd & 0x000F; - pd2 = ((pd >> 4) & 0x000F) ^ pd1; - tmp ^= (pd1 << 3) | (pd1 << 8); - tmp ^= pd2 | (pd2 << 7) | (pd2 << 12); - - return tmp; -} - -static uint16_t NVRAM_compute_crc (Nvram *nvram, uint32_t start, uint32_t count) -{ - uint32_t i; - uint16_t crc = 0xFFFF; - int odd; - - odd = count & 1; - count &= ~1; - for (i = 0; i != count; i++) { - crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i)); - } - if (odd) { - crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8); - } - - return crc; -} - -#define CMDLINE_ADDR 0x017ff000 - -static int PPC_NVRAM_set_params (Nvram *nvram, uint16_t NVRAM_size, - const char *arch, - uint32_t RAM_size, int boot_device, - uint32_t kernel_image, uint32_t kernel_size, - const char *cmdline, - uint32_t initrd_image, uint32_t initrd_size, - uint32_t NVRAM_image, - int width, int height, int depth) -{ - uint16_t crc; - - /* Set parameters for Open Hack'Ware BIOS */ - NVRAM_set_string(nvram, 0x00, "QEMU_BIOS", 16); - NVRAM_set_lword(nvram, 0x10, 0x00000002); /* structure v2 */ - NVRAM_set_word(nvram, 0x14, NVRAM_size); - NVRAM_set_string(nvram, 0x20, arch, 16); - NVRAM_set_lword(nvram, 0x30, RAM_size); - NVRAM_set_byte(nvram, 0x34, boot_device); - NVRAM_set_lword(nvram, 0x38, kernel_image); - NVRAM_set_lword(nvram, 0x3C, kernel_size); - if (cmdline) { - /* XXX: put the cmdline in NVRAM too ? */ - pstrcpy_targphys("cmdline", CMDLINE_ADDR, RAM_size - CMDLINE_ADDR, - cmdline); - NVRAM_set_lword(nvram, 0x40, CMDLINE_ADDR); - NVRAM_set_lword(nvram, 0x44, strlen(cmdline)); - } else { - NVRAM_set_lword(nvram, 0x40, 0); - NVRAM_set_lword(nvram, 0x44, 0); - } - NVRAM_set_lword(nvram, 0x48, initrd_image); - NVRAM_set_lword(nvram, 0x4C, initrd_size); - NVRAM_set_lword(nvram, 0x50, NVRAM_image); - - NVRAM_set_word(nvram, 0x54, width); - NVRAM_set_word(nvram, 0x56, height); - NVRAM_set_word(nvram, 0x58, depth); - crc = NVRAM_compute_crc(nvram, 0x00, 0xF8); - NVRAM_set_word(nvram, 0xFC, crc); - - return 0; -} - -/* PowerPC PREP hardware initialisation */ -static void ppc_prep_init(MachineState *machine) -{ - ram_addr_t ram_size = machine->ram_size; - const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; - const char *initrd_filename = machine->initrd_filename; - const char *boot_device = machine->boot_order; - MemoryRegion *sysmem = get_system_memory(); - PowerPCCPU *cpu = NULL; - CPUPPCState *env = NULL; - Nvram *m48t59; -#if 0 - MemoryRegion *xcsr = g_new(MemoryRegion, 1); -#endif - int linux_boot, i, nb_nics1; - MemoryRegion *ram = g_new(MemoryRegion, 1); - uint32_t kernel_base, initrd_base; - long kernel_size, initrd_size; - DeviceState *dev; - PCIHostState *pcihost; - PCIBus *pci_bus; - PCIDevice *pci; - ISABus *isa_bus; - ISADevice *isa; - int ppc_boot_device; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - - sysctrl = g_malloc0(sizeof(sysctrl_t)); - - linux_boot = (kernel_filename != NULL); - - /* init CPUs */ - if (machine->cpu_model == NULL) - machine->cpu_model = "602"; - 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"); - exit(1); - } - env = &cpu->env; - - if (env->flags & POWERPC_FLAG_RTC_CLK) { - /* POWER / PowerPC 601 RTC clock frequency is 7.8125 MHz */ - cpu_ppc_tb_init(env, 7812500UL); - } else { - /* Set time-base frequency to 100 Mhz */ - cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); - } - qemu_register_reset(ppc_prep_reset, cpu); - } - - /* allocate RAM */ - memory_region_allocate_system_memory(ram, NULL, "ppc_prep.ram", ram_size); - memory_region_add_subregion(sysmem, 0, ram); - - if (linux_boot) { - kernel_base = KERNEL_LOAD_ADDR; - /* now we can load the kernel */ - kernel_size = load_image_targphys(kernel_filename, kernel_base, - ram_size - kernel_base); - if (kernel_size < 0) { - error_report("could not load kernel '%s'", kernel_filename); - exit(1); - } - /* load initrd */ - if (initrd_filename) { - initrd_base = INITRD_LOAD_ADDR; - initrd_size = load_image_targphys(initrd_filename, initrd_base, - ram_size - initrd_base); - if (initrd_size < 0) { - error_report("could not load initial ram disk '%s'", - initrd_filename); - exit(1); - } - } else { - initrd_base = 0; - initrd_size = 0; - } - ppc_boot_device = 'm'; - } else { - kernel_base = 0; - kernel_size = 0; - initrd_base = 0; - initrd_size = 0; - ppc_boot_device = '\0'; - /* For now, OHW cannot boot from the network. */ - for (i = 0; boot_device[i] != '\0'; i++) { - if (boot_device[i] >= 'a' && boot_device[i] <= 'f') { - ppc_boot_device = boot_device[i]; - break; - } - } - if (ppc_boot_device == '\0') { - fprintf(stderr, "No valid boot device for Mac99 machine\n"); - exit(1); - } - } - - if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) { - error_report("Only 6xx bus is supported on PREP machine"); - exit(1); - } - - dev = qdev_create(NULL, "raven-pcihost"); - if (bios_name == NULL) { - bios_name = BIOS_FILENAME; - } - qdev_prop_set_string(dev, "bios-name", bios_name); - 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); - pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); - if (pci_bus == NULL) { - fprintf(stderr, "Couldn't create PCI host controller.\n"); - exit(1); - } - sysctrl->contiguous_map_irq = qdev_get_gpio_in(dev, 0); - - /* PCI -> ISA bridge */ - pci = pci_create_simple(pci_bus, PCI_DEVFN(1, 0), "i82378"); - cpu = POWERPC_CPU(first_cpu); - qdev_connect_gpio_out(&pci->qdev, 0, - cpu->env.irq_inputs[PPC6xx_INPUT_INT]); - 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)); - sysbus_connect_irq(&pcihost->busdev, 3, qdev_get_gpio_in(&pci->qdev, 11)); - isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci), "isa.0")); - - /* Super I/O (parallel + serial ports) */ - isa = isa_create(isa_bus, TYPE_PC87312); - dev = DEVICE(isa); - qdev_prop_set_uint8(dev, "config", 13); /* fdc, ser0, ser1, par0 */ - qdev_init_nofail(dev); - - /* init basic PC hardware */ - pci_vga_init(pci_bus); - - nb_nics1 = nb_nics; - if (nb_nics1 > NE2000_NB_MAX) - nb_nics1 = NE2000_NB_MAX; - for(i = 0; i < nb_nics1; i++) { - if (nd_table[i].model == NULL) { - nd_table[i].model = g_strdup("ne2k_isa"); - } - if (strcmp(nd_table[i].model, "ne2k_isa") == 0) { - isa_ne2000_init(isa_bus, ne2000_io[i], ne2000_irq[i], - &nd_table[i]); - } else { - pci_nic_init_nofail(&nd_table[i], pci_bus, "ne2k_pci", NULL); - } - } - - ide_drive_get(hd, ARRAY_SIZE(hd)); - for(i = 0; i < MAX_IDE_BUS; i++) { - isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], ide_irq[i], - hd[2 * i], - hd[2 * i + 1]); - } - isa_create_simple(isa_bus, "i8042"); - - cpu = POWERPC_CPU(first_cpu); - sysctrl->reset_irq = cpu->env.irq_inputs[PPC6xx_INPUT_HRESET]; - - portio_list_init(&prep_port_list, NULL, prep_portio_list, sysctrl, "prep"); - portio_list_add(&prep_port_list, isa_address_space_io(isa), 0x0); - - /* PowerPC control and status register group */ -#if 0 - memory_region_init_io(xcsr, NULL, &PPC_XCSR_ops, NULL, "ppc-xcsr", 0x1000); - memory_region_add_subregion(sysmem, 0xFEFF0000, xcsr); -#endif - - if (usb_enabled()) { - pci_create_simple(pci_bus, -1, "pci-ohci"); - } - - m48t59 = m48t59_init_isa(isa_bus, 0x0074, NVRAM_SIZE, 2000, 59); - if (m48t59 == NULL) - return; - sysctrl->nvram = m48t59; - - /* Initialise NVRAM */ - PPC_NVRAM_set_params(m48t59, NVRAM_SIZE, "PREP", ram_size, - ppc_boot_device, - kernel_base, kernel_size, - kernel_cmdline, - initrd_base, initrd_size, - /* XXX: need an option to load a NVRAM image */ - 0, - graphic_width, graphic_height, graphic_depth); -} - -static void prep_machine_init(MachineClass *mc) -{ - mc->desc = "PowerPC PREP platform"; - mc->init = ppc_prep_init; - mc->max_cpus = MAX_CPUS; - mc->default_boot_order = "cad"; -} - -DEFINE_MACHINE("prep", prep_machine_init) diff --git a/qemu/hw/ppc/spapr.c b/qemu/hw/ppc/spapr.c deleted file mode 100644 index b69995e0d..000000000 --- a/qemu/hw/ppc/spapr.c +++ /dev/null @@ -1,2485 +0,0 @@ -/* - * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator - * - * Copyright (c) 2004-2007 Fabrice Bellard - * Copyright (c) 2007 Jocelyn Mayer - * Copyright (c) 2010 David Gibson, IBM Corporation. - * - * 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 "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" -#include "qom/cpu.h" - -#include "hw/boards.h" -#include "hw/ppc/ppc.h" -#include "hw/loader.h" - -#include "hw/ppc/spapr.h" -#include "hw/ppc/spapr_vio.h" -#include "hw/pci-host/spapr.h" -#include "hw/ppc/xics.h" -#include "hw/pci/msi.h" - -#include "hw/pci/pci.h" -#include "hw/scsi/scsi.h" -#include "hw/virtio/virtio-scsi.h" - -#include "exec/address-spaces.h" -#include "hw/usb.h" -#include "qemu/config-file.h" -#include "qemu/error-report.h" -#include "trace.h" -#include "hw/nmi.h" - -#include "hw/compat.h" -#include "qemu/cutils.h" - -#include - -/* SLOF memory layout: - * - * SLOF raw image loaded at 0, copies its romfs right below the flat - * device-tree, then position SLOF itself 31M below that - * - * So we set FW_OVERHEAD to 40MB which should account for all of that - * and more - * - * We load our kernel at 4M, leaving space for SLOF initial image - */ -#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 -#define FW_FILE_NAME "slof.bin" -#define FW_OVERHEAD 0x2800000 -#define KERNEL_LOAD_ADDR FW_MAX_SIZE - -#define MIN_RMA_SLOF 128UL - -#define TIMEBASE_FREQ 512000000ULL - -#define PHANDLE_XICP 0x00001111 - -#define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift)) - -static XICSState *try_create_xics(const char *type, int nr_servers, - int nr_irqs, Error **errp) -{ - Error *err = NULL; - DeviceState *dev; - - dev = qdev_create(NULL, type); - qdev_prop_set_uint32(dev, "nr_servers", nr_servers); - qdev_prop_set_uint32(dev, "nr_irqs", nr_irqs); - object_property_set_bool(OBJECT(dev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - object_unparent(OBJECT(dev)); - return NULL; - } - return XICS_COMMON(dev); -} - -static XICSState *xics_system_init(MachineState *machine, - int nr_servers, int nr_irqs, Error **errp) -{ - XICSState *icp = NULL; - - if (kvm_enabled()) { - Error *err = NULL; - - if (machine_kernel_irqchip_allowed(machine)) { - icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs, &err); - } - if (machine_kernel_irqchip_required(machine) && !icp) { - 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, errp); - } - - return icp; -} - -static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu, - int smt_threads) -{ - int i, ret = 0; - uint32_t servers_prop[smt_threads]; - uint32_t gservers_prop[smt_threads * 2]; - int index = ppc_get_vcpu_dt_id(cpu); - - if (cpu->cpu_version) { - ret = fdt_setprop_cell(fdt, offset, "cpu-version", cpu->cpu_version); - if (ret < 0) { - return ret; - } - } - - /* Build interrupt servers and gservers properties */ - for (i = 0; i < smt_threads; i++) { - servers_prop[i] = cpu_to_be32(index + i); - /* Hack, direct the group queues back to cpu 0 */ - gservers_prop[i*2] = cpu_to_be32(index + i); - gservers_prop[i*2 + 1] = 0; - } - ret = fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s", - servers_prop, sizeof(servers_prop)); - if (ret < 0) { - return ret; - } - ret = fdt_setprop(fdt, offset, "ibm,ppc-interrupt-gserver#s", - gservers_prop, sizeof(gservers_prop)); - - return ret; -} - -static int spapr_fixup_cpu_numa_dt(void *fdt, int offset, CPUState *cs) -{ - int ret = 0; - PowerPCCPU *cpu = POWERPC_CPU(cs); - int index = ppc_get_vcpu_dt_id(cpu); - uint32_t associativity[] = {cpu_to_be32(0x5), - cpu_to_be32(0x0), - cpu_to_be32(0x0), - cpu_to_be32(0x0), - cpu_to_be32(cs->numa_node), - cpu_to_be32(index)}; - - /* Advertise NUMA via ibm,associativity */ - if (nb_numa_nodes > 1) { - ret = fdt_setprop(fdt, offset, "ibm,associativity", associativity, - sizeof(associativity)); - } - - return ret; -} - -static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) -{ - int ret = 0, offset, cpus_offset; - CPUState *cs; - char cpu_model[32]; - int smt = kvmppc_smt_threads(); - uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; - - CPU_FOREACH(cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - DeviceClass *dc = DEVICE_GET_CLASS(cs); - int index = ppc_get_vcpu_dt_id(cpu); - - if ((index % smt) != 0) { - continue; - } - - snprintf(cpu_model, 32, "%s@%x", dc->fw_name, index); - - cpus_offset = fdt_path_offset(fdt, "/cpus"); - if (cpus_offset < 0) { - cpus_offset = fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"), - "cpus"); - if (cpus_offset < 0) { - return cpus_offset; - } - } - offset = fdt_subnode_offset(fdt, cpus_offset, cpu_model); - if (offset < 0) { - offset = fdt_add_subnode(fdt, cpus_offset, cpu_model); - if (offset < 0) { - return offset; - } - } - - ret = fdt_setprop(fdt, offset, "ibm,pft-size", - pft_size_prop, sizeof(pft_size_prop)); - if (ret < 0) { - return ret; - } - - ret = spapr_fixup_cpu_numa_dt(fdt, offset, cs); - if (ret < 0) { - return ret; - } - - ret = spapr_fixup_cpu_smt_dt(fdt, offset, cpu, - ppc_get_compat_smt_threads(cpu)); - if (ret < 0) { - return ret; - } - } - return ret; -} - - -static size_t create_page_sizes_prop(CPUPPCState *env, uint32_t *prop, - size_t maxsize) -{ - size_t maxcells = maxsize / sizeof(uint32_t); - int i, j, count; - uint32_t *p = prop; - - for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { - struct ppc_one_seg_page_size *sps = &env->sps.sps[i]; - - if (!sps->page_shift) { - break; - } - for (count = 0; count < PPC_PAGE_SIZES_MAX_SZ; count++) { - if (sps->enc[count].page_shift == 0) { - break; - } - } - if ((p - prop) >= (maxcells - 3 - count * 2)) { - break; - } - *(p++) = cpu_to_be32(sps->page_shift); - *(p++) = cpu_to_be32(sps->slb_enc); - *(p++) = cpu_to_be32(count); - for (j = 0; j < count; j++) { - *(p++) = cpu_to_be32(sps->enc[j].page_shift); - *(p++) = cpu_to_be32(sps->enc[j].pte_enc); - } - } - - return (p - prop) * sizeof(uint32_t); -} - -static hwaddr spapr_node0_size(void) -{ - MachineState *machine = MACHINE(qdev_get_machine()); - - if (nb_numa_nodes) { - int i; - for (i = 0; i < nb_numa_nodes; ++i) { - if (numa_info[i].node_mem) { - return MIN(pow2floor(numa_info[i].node_mem), - machine->ram_size); - } - } - } - return machine->ram_size; -} - -#define _FDT(exp) \ - do { \ - int ret = (exp); \ - if (ret < 0) { \ - fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \ - #exp, fdt_strerror(ret)); \ - exit(1); \ - } \ - } while (0) - -static void add_str(GString *s, const gchar *s1) -{ - g_string_append_len(s, s1, strlen(s1) + 1); -} - -static void *spapr_create_fdt_skel(hwaddr initrd_base, - hwaddr initrd_size, - hwaddr kernel_size, - bool little_endian, - const char *kernel_cmdline, - uint32_t epow_irq) -{ - void *fdt; - uint32_t start_prop = cpu_to_be32(initrd_base); - uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size); - GString *hypertas = g_string_sized_new(256); - GString *qemu_hypertas = g_string_sized_new(256); - uint32_t refpoints[] = {cpu_to_be32(0x4), cpu_to_be32(0x4)}; - uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(max_cpus)}; - unsigned char vec5[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x80}; - char *buf; - - add_str(hypertas, "hcall-pft"); - add_str(hypertas, "hcall-term"); - add_str(hypertas, "hcall-dabr"); - add_str(hypertas, "hcall-interrupt"); - add_str(hypertas, "hcall-tce"); - add_str(hypertas, "hcall-vio"); - add_str(hypertas, "hcall-splpar"); - add_str(hypertas, "hcall-bulk"); - add_str(hypertas, "hcall-set-mode"); - add_str(qemu_hypertas, "hcall-memop1"); - - fdt = g_malloc0(FDT_MAX_SIZE); - _FDT((fdt_create(fdt, FDT_MAX_SIZE))); - - if (kernel_size) { - _FDT((fdt_add_reservemap_entry(fdt, KERNEL_LOAD_ADDR, kernel_size))); - } - if (initrd_size) { - _FDT((fdt_add_reservemap_entry(fdt, initrd_base, initrd_size))); - } - _FDT((fdt_finish_reservemap(fdt))); - - /* Root node */ - _FDT((fdt_begin_node(fdt, ""))); - _FDT((fdt_property_string(fdt, "device_type", "chrp"))); - _FDT((fdt_property_string(fdt, "model", "IBM pSeries (emulated by qemu)"))); - _FDT((fdt_property_string(fdt, "compatible", "qemu,pseries"))); - - /* - * Add info to guest to indentify which host is it being run on - * and what is the uuid of the guest - */ - if (kvmppc_get_host_model(&buf)) { - _FDT((fdt_property_string(fdt, "host-model", buf))); - g_free(buf); - } - if (kvmppc_get_host_serial(&buf)) { - _FDT((fdt_property_string(fdt, "host-serial", buf))); - g_free(buf); - } - - buf = g_strdup_printf(UUID_FMT, qemu_uuid[0], qemu_uuid[1], - qemu_uuid[2], qemu_uuid[3], qemu_uuid[4], - qemu_uuid[5], qemu_uuid[6], qemu_uuid[7], - qemu_uuid[8], qemu_uuid[9], qemu_uuid[10], - qemu_uuid[11], qemu_uuid[12], qemu_uuid[13], - 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))); - - /* /chosen */ - _FDT((fdt_begin_node(fdt, "chosen"))); - - /* Set Form1_affinity */ - _FDT((fdt_property(fdt, "ibm,architecture-vec-5", vec5, sizeof(vec5)))); - - _FDT((fdt_property_string(fdt, "bootargs", kernel_cmdline))); - _FDT((fdt_property(fdt, "linux,initrd-start", - &start_prop, sizeof(start_prop)))); - _FDT((fdt_property(fdt, "linux,initrd-end", - &end_prop, sizeof(end_prop)))); - if (kernel_size) { - uint64_t kprop[2] = { cpu_to_be64(KERNEL_LOAD_ADDR), - cpu_to_be64(kernel_size) }; - - _FDT((fdt_property(fdt, "qemu,boot-kernel", &kprop, sizeof(kprop)))); - if (little_endian) { - _FDT((fdt_property(fdt, "qemu,boot-kernel-le", NULL, 0))); - } - } - if (boot_menu) { - _FDT((fdt_property_cell(fdt, "qemu,boot-menu", boot_menu))); - } - _FDT((fdt_property_cell(fdt, "qemu,graphic-width", graphic_width))); - _FDT((fdt_property_cell(fdt, "qemu,graphic-height", graphic_height))); - _FDT((fdt_property_cell(fdt, "qemu,graphic-depth", graphic_depth))); - - _FDT((fdt_end_node(fdt))); - - /* RTAS */ - _FDT((fdt_begin_node(fdt, "rtas"))); - - if (!kvm_enabled() || kvmppc_spapr_use_multitce()) { - add_str(hypertas, "hcall-multi-tce"); - } - _FDT((fdt_property(fdt, "ibm,hypertas-functions", hypertas->str, - hypertas->len))); - g_string_free(hypertas, TRUE); - _FDT((fdt_property(fdt, "qemu,hypertas-functions", qemu_hypertas->str, - qemu_hypertas->len))); - g_string_free(qemu_hypertas, TRUE); - - _FDT((fdt_property(fdt, "ibm,associativity-reference-points", - refpoints, sizeof(refpoints)))); - - _FDT((fdt_property_cell(fdt, "rtas-error-log-max", RTAS_ERROR_LOG_MAX))); - _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. - * - * While an additional ibm,extended-os-term property indicates that - * rtas call return will always occur. Set this property. - */ - _FDT((fdt_property(fdt, "ibm,extended-os-term", NULL, 0))); - - _FDT((fdt_end_node(fdt))); - - /* interrupt controller */ - _FDT((fdt_begin_node(fdt, "interrupt-controller"))); - - _FDT((fdt_property_string(fdt, "device_type", - "PowerPC-External-Interrupt-Presentation"))); - _FDT((fdt_property_string(fdt, "compatible", "IBM,ppc-xicp"))); - _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0))); - _FDT((fdt_property(fdt, "ibm,interrupt-server-ranges", - interrupt_server_ranges_prop, - sizeof(interrupt_server_ranges_prop)))); - _FDT((fdt_property_cell(fdt, "#interrupt-cells", 2))); - _FDT((fdt_property_cell(fdt, "linux,phandle", PHANDLE_XICP))); - _FDT((fdt_property_cell(fdt, "phandle", PHANDLE_XICP))); - - _FDT((fdt_end_node(fdt))); - - /* vdevice */ - _FDT((fdt_begin_node(fdt, "vdevice"))); - - _FDT((fdt_property_string(fdt, "device_type", "vdevice"))); - _FDT((fdt_property_string(fdt, "compatible", "IBM,vdevice"))); - _FDT((fdt_property_cell(fdt, "#address-cells", 0x1))); - _FDT((fdt_property_cell(fdt, "#size-cells", 0x0))); - _FDT((fdt_property_cell(fdt, "#interrupt-cells", 0x2))); - _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0))); - - _FDT((fdt_end_node(fdt))); - - /* event-sources */ - spapr_events_fdt_skel(fdt, epow_irq); - - /* /hypervisor node */ - if (kvm_enabled()) { - uint8_t hypercall[16]; - - /* indicate KVM hypercall interface */ - _FDT((fdt_begin_node(fdt, "hypervisor"))); - _FDT((fdt_property_string(fdt, "compatible", "linux,kvm"))); - if (kvmppc_has_cap_fixup_hcalls()) { - /* - * Older KVM versions with older guest kernels were broken with the - * magic page, don't allow the guest to map it. - */ - 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))); - } - - _FDT((fdt_end_node(fdt))); /* close root node */ - _FDT((fdt_finish(fdt))); - - return fdt; -} - -static int spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start, - hwaddr size) -{ - uint32_t associativity[] = { - cpu_to_be32(0x4), /* length */ - cpu_to_be32(0x0), cpu_to_be32(0x0), - cpu_to_be32(0x0), cpu_to_be32(nodeid) - }; - char mem_name[32]; - uint64_t mem_reg_property[2]; - int off; - - mem_reg_property[0] = cpu_to_be64(start); - mem_reg_property[1] = cpu_to_be64(size); - - sprintf(mem_name, "memory@" TARGET_FMT_lx, start); - off = fdt_add_subnode(fdt, 0, mem_name); - _FDT(off); - _FDT((fdt_setprop_string(fdt, off, "device_type", "memory"))); - _FDT((fdt_setprop(fdt, off, "reg", mem_reg_property, - 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) -{ - MachineState *machine = MACHINE(spapr); - hwaddr mem_start, node_size; - int i, nb_nodes = nb_numa_nodes; - NodeInfo *nodes = numa_info; - NodeInfo ramnode; - - /* No NUMA nodes, assume there is just one node with whole RAM */ - if (!nb_numa_nodes) { - nb_nodes = 1; - ramnode.node_mem = machine->ram_size; - nodes = &ramnode; - } - - for (i = 0, mem_start = 0; i < nb_nodes; ++i) { - if (!nodes[i].node_mem) { - continue; - } - if (mem_start >= machine->ram_size) { - node_size = 0; - } else { - node_size = nodes[i].node_mem; - if (node_size > machine->ram_size - mem_start) { - node_size = machine->ram_size - mem_start; - } - } - if (!mem_start) { - /* ppc_spapr_init() checks for rma_size <= node0_size already */ - spapr_populate_memory_node(fdt, i, 0, spapr->rma_size); - mem_start += spapr->rma_size; - node_size -= spapr->rma_size; - } - for ( ; node_size; ) { - hwaddr sizetmp = pow2floor(node_size); - - /* mem_start != 0 here */ - if (ctzl(mem_start) < ctzl(sizetmp)) { - sizetmp = 1ULL << ctzl(mem_start); - } - - spapr_populate_memory_node(fdt, i, mem_start, sizetmp); - node_size -= sizetmp; - mem_start += sizetmp; - } - } - - return 0; -} - -static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, - sPAPRMachineState *spapr) -{ - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); - int index = ppc_get_vcpu_dt_id(cpu); - uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40), - 0xffffffff, 0xffffffff}; - uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() : TIMEBASE_FREQ; - uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000; - uint32_t page_sizes_prop[64]; - size_t page_sizes_prop_size; - 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"))); - - _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", env->spr[SPR_PVR]))); - _FDT((fdt_setprop_cell(fdt, offset, "d-cache-block-size", - env->dcache_line_size))); - _FDT((fdt_setprop_cell(fdt, offset, "d-cache-line-size", - env->dcache_line_size))); - _FDT((fdt_setprop_cell(fdt, offset, "i-cache-block-size", - env->icache_line_size))); - _FDT((fdt_setprop_cell(fdt, offset, "i-cache-line-size", - env->icache_line_size))); - - if (pcc->l1_dcache_size) { - _FDT((fdt_setprop_cell(fdt, offset, "d-cache-size", - pcc->l1_dcache_size))); - } else { - fprintf(stderr, "Warning: Unknown L1 dcache size for cpu\n"); - } - if (pcc->l1_icache_size) { - _FDT((fdt_setprop_cell(fdt, offset, "i-cache-size", - pcc->l1_icache_size))); - } else { - fprintf(stderr, "Warning: Unknown L1 icache size for cpu\n"); - } - - _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))); - - if (env->spr_cb[SPR_PURR].oea_read) { - _FDT((fdt_setprop(fdt, offset, "ibm,purr", NULL, 0))); - } - - if (env->mmu_model & POWERPC_MMU_1TSEG) { - _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes", - segs, sizeof(segs)))); - } - - /* Advertise VMX/VSX (vector extensions) if available - * 0 / no property == no vector extensions - * 1 == VMX / Altivec available - * 2 == VSX available */ - if (env->insns_flags & PPC_ALTIVEC) { - uint32_t vmx = (env->insns_flags2 & PPC2_VSX) ? 2 : 1; - - _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", vmx))); - } - - /* Advertise DFP (Decimal Floating Point) if available - * 0 / no property == no DFP - * 1 == DFP available */ - if (env->insns_flags2 & PPC2_DFP) { - _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1))); - } - - page_sizes_prop_size = create_page_sizes_prop(env, page_sizes_prop, - sizeof(page_sizes_prop)); - if (page_sizes_prop_size) { - _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes", - 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 / vcpus_per_socket))); - - _FDT((fdt_setprop(fdt, offset, "ibm,pft-size", - pft_size_prop, sizeof(pft_size_prop)))); - - _FDT(spapr_fixup_cpu_numa_dt(fdt, offset, cs)); - - _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, - ppc_get_compat_smt_threads(cpu))); -} - -static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr) -{ - CPUState *cs; - int cpus_offset; - char *nodename; - int smt = kvmppc_smt_threads(); - - cpus_offset = fdt_add_subnode(fdt, 0, "cpus"); - _FDT(cpus_offset); - _FDT((fdt_setprop_cell(fdt, cpus_offset, "#address-cells", 0x1))); - _FDT((fdt_setprop_cell(fdt, cpus_offset, "#size-cells", 0x0))); - - /* - * We walk the CPUs in reverse order to ensure that CPU DT nodes - * created by fdt_add_subnode() end up in the right order in FDT - * for the guest kernel the enumerate the CPUs correctly. - */ - CPU_FOREACH_REVERSE(cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - int index = ppc_get_vcpu_dt_id(cpu); - DeviceClass *dc = DEVICE_GET_CLASS(cs); - int offset; - - if ((index % smt) != 0) { - continue; - } - - nodename = g_strdup_printf("%s@%x", dc->fw_name, index); - offset = fdt_add_subnode(fdt, cpus_offset, nodename); - g_free(nodename); - _FDT(offset); - spapr_populate_cpu_dt(cs, fdt, offset, 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; - char *bootlist; - void *fdt; - sPAPRPHBState *phb; - - fdt = g_malloc(FDT_MAX_SIZE); - - /* open out the base tree into a temp buffer for the final tweaks */ - _FDT((fdt_open_into(spapr->fdt_skel, fdt, FDT_MAX_SIZE))); - - ret = spapr_populate_memory(spapr, fdt); - if (ret < 0) { - fprintf(stderr, "couldn't setup memory nodes in fdt\n"); - exit(1); - } - - ret = spapr_populate_vdevice(spapr->vio_bus, fdt); - if (ret < 0) { - fprintf(stderr, "couldn't setup vio devices in fdt\n"); - exit(1); - } - - 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); - } - } - - 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 */ - ret = spapr_rtas_device_tree_setup(fdt, rtas_addr, rtas_size); - if (ret < 0) { - fprintf(stderr, "Couldn't set up RTAS device tree properties\n"); - } - - /* cpus */ - spapr_populate_cpus_dt_node(fdt, spapr); - - bootlist = get_boot_devices_list(&cb, true); - if (cb && bootlist) { - int offset = fdt_path_offset(fdt, "/chosen"); - if (offset < 0) { - exit(1); - } - for (i = 0; i < cb; i++) { - if (bootlist[i] == '\n') { - bootlist[i] = ' '; - } - - } - ret = fdt_setprop_string(fdt, offset, "qemu,boot-list", bootlist); - } - - if (boot_device && strlen(boot_device)) { - int offset = fdt_path_offset(fdt, "/chosen"); - - if (offset < 0) { - exit(1); - } - fdt_setprop_string(fdt, offset, "qemu,boot-device", boot_device); - } - - if (!spapr->has_graphics) { - 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) { - error_report("FDT too big ! 0x%x bytes (max is 0x%x)", - fdt_totalsize(fdt), FDT_MAX_SIZE); - exit(1); - } - - qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); - cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt)); - - g_free(bootlist); - g_free(fdt); -} - -static uint64_t translate_kernel_address(void *opaque, uint64_t addr) -{ - return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; -} - -static void emulate_spapr_hypercall(PowerPCCPU *cpu) -{ - CPUPPCState *env = &cpu->env; - - if (msr_pr) { - hcall_dprintf("Hypercall made with MSR[PR]=1\n"); - env->gpr[3] = H_PRIVILEGE; - } else { - env->gpr[3] = spapr_hypercall(cpu, env->gpr[3], &env->gpr[4]); - } -} - -#define HPTE(_table, _i) (void *)(((uint64_t *)(_table)) + ((_i) * 2)) -#define HPTE_VALID(_hpte) (tswap64(*((uint64_t *)(_hpte))) & HPTE64_V_VALID) -#define HPTE_DIRTY(_hpte) (tswap64(*((uint64_t *)(_hpte))) & HPTE64_V_HPTE_DIRTY) -#define CLEAN_HPTE(_hpte) ((*(uint64_t *)(_hpte)) &= tswap64(~HPTE64_V_HPTE_DIRTY)) -#define DIRTY_HPTE(_hpte) ((*(uint64_t *)(_hpte)) |= tswap64(HPTE64_V_HPTE_DIRTY)) - -/* - * Get the fd to access the kernel htab, re-opening it if necessary - */ -static int get_htab_fd(sPAPRMachineState *spapr) -{ - 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)); - } - - return spapr->htab_fd; -} - -static void close_htab_fd(sPAPRMachineState *spapr) -{ - if (spapr->htab_fd >= 0) { - close(spapr->htab_fd); - } - spapr->htab_fd = -1; -} - -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; -} - -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) { - error_setg_errno(errp, errno, - "Could not allocate HPT of order %d", shift); - return; - } - - memset(spapr->htab, 0, size); - spapr->htab_shift = shift; - - for (i = 0; i < size / HASH_PTE_SIZE_64; i++) { - DIRTY_HPTE(HPTE(spapr->htab, i)); - } - } -} - -static int find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque) -{ - bool matched = false; - - if (object_dynamic_cast(OBJECT(sbdev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { - matched = true; - } - - if (!matched) { - error_report("Device %s is not supported by this machine yet.", - qdev_fw_name(DEVICE(sbdev))); - exit(1); - } - - return 0; -} - -static void ppc_spapr_reset(void) -{ - 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); - - /* 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(); - - /* - * We place the device tree and RTAS just below either the top of the RMA, - * or just below 2GB, whichever is lowere, so that it can be - * processed with 32-bit real mode code if necessary - */ - rtas_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR); - spapr->rtas_addr = rtas_limit - RTAS_MAX_SIZE; - spapr->fdt_addr = spapr->rtas_addr - FDT_MAX_SIZE; - - /* Load the fdt */ - spapr_finalize_fdt(spapr, spapr->fdt_addr, spapr->rtas_addr, - spapr->rtas_size); - - /* Copy RTAS over */ - cpu_physical_memory_write(spapr->rtas_addr, spapr->rtas_blob, - spapr->rtas_size); - - /* Set up the entry state */ - first_ppc_cpu = POWERPC_CPU(first_cpu); - first_ppc_cpu->env.gpr[3] = spapr->fdt_addr; - first_ppc_cpu->env.gpr[5] = 0; - first_cpu->halted = 0; - first_ppc_cpu->env.nip = SPAPR_ENTRY_POINT; - -} - -static void spapr_cpu_reset(void *opaque) -{ - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - PowerPCCPU *cpu = opaque; - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - - cpu_reset(cs); - - /* All CPUs start halted. CPU0 is unhalted from the machine level - * reset code and the rest are explicitly started up by the guest - * using an RTAS call */ - cs->halted = 1; - - env->spr[SPR_HIOR] = 0; - - ppc_hash64_set_external_hpt(cpu, spapr->htab, spapr->htab_shift, - &error_fatal); -} - -static void spapr_create_nvram(sPAPRMachineState *spapr) -{ - DeviceState *dev = qdev_create(&spapr->vio_bus->bus, "spapr-nvram"); - DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0); - - if (dinfo) { - qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo), - &error_fatal); - } - - qdev_init_nofail(dev); - - spapr->nvram = (struct sPAPRNVRAM *)dev; -} - -static void spapr_rtc_create(sPAPRMachineState *spapr) -{ - DeviceState *dev = qdev_create(NULL, TYPE_SPAPR_RTC); - - qdev_init_nofail(dev); - spapr->rtc = dev; - - object_property_add_alias(qdev_get_machine(), "rtc-time", - OBJECT(spapr->rtc), "date", NULL); -} - -/* Returns whether we want to use VGA or not */ -static bool spapr_vga_init(PCIBus *pci_bus, Error **errp) -{ - switch (vga_interface_type) { - case VGA_NONE: - return false; - case VGA_DEVICE: - return true; - case VGA_STD: - case VGA_VIRTIO: - return pci_vga_init(pci_bus) != NULL; - default: - error_setg(errp, - "Unsupported VGA mode, only -vga std or -vga virtio is supported"); - return false; - } -} - -static int spapr_post_load(void *opaque, int version_id) -{ - sPAPRMachineState *spapr = (sPAPRMachineState *)opaque; - int err = 0; - - /* In earlier versions, there was no separate qdev for the PAPR - * RTC, so the RTC offset was stored directly in sPAPREnvironment. - * So when migrating from those versions, poke the incoming offset - * value into the RTC device */ - if (version_id < 3) { - err = spapr_rtc_import_offset(spapr->rtc, spapr->rtc_offset); - } - - return err; -} - -static bool version_before_3(void *opaque, int version_id) -{ - return version_id < 3; -} - -static const VMStateDescription vmstate_spapr = { - .name = "spapr", - .version_id = 3, - .minimum_version_id = 1, - .post_load = spapr_post_load, - .fields = (VMStateField[]) { - /* used to be @next_irq */ - VMSTATE_UNUSED_BUFFER(version_before_3, 0, 4), - - /* RTC offset */ - VMSTATE_UINT64_TEST(rtc_offset, sPAPRMachineState, version_before_3), - - VMSTATE_PPC_TIMEBASE_V(tb, sPAPRMachineState, 2), - VMSTATE_END_OF_LIST() - }, -}; - -static int htab_save_setup(QEMUFile *f, void *opaque) -{ - sPAPRMachineState *spapr = opaque; - - /* "Iteration" header */ - qemu_put_be32(f, spapr->htab_shift); - - if (spapr->htab) { - spapr->htab_save_index = 0; - spapr->htab_first_pass = true; - } else { - assert(kvm_enabled()); - } - - - return 0; -} - -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); - - assert(spapr->htab_first_pass); - - do { - int chunkstart; - - /* Consume invalid HPTEs */ - while ((index < htabslots) - && !HPTE_VALID(HPTE(spapr->htab, index))) { - index++; - CLEAN_HPTE(HPTE(spapr->htab, index)); - } - - /* Consume valid HPTEs */ - chunkstart = index; - while ((index < htabslots) && (index - chunkstart < USHRT_MAX) - && HPTE_VALID(HPTE(spapr->htab, index))) { - index++; - CLEAN_HPTE(HPTE(spapr->htab, index)); - } - - if (index > chunkstart) { - int n_valid = index - chunkstart; - - qemu_put_be32(f, chunkstart); - qemu_put_be16(f, n_valid); - qemu_put_be16(f, 0); - qemu_put_buffer(f, HPTE(spapr->htab, chunkstart), - HASH_PTE_SIZE_64 * n_valid); - - if (has_timeout && - (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - starttime) > max_ns) { - break; - } - } - } while ((index < htabslots) && !qemu_file_rate_limit(f)); - - if (index >= htabslots) { - assert(index == htabslots); - index = 0; - spapr->htab_first_pass = false; - } - spapr->htab_save_index = index; -} - -static int htab_save_later_pass(QEMUFile *f, sPAPRMachineState *spapr, - int64_t max_ns) -{ - bool final = max_ns < 0; - int htabslots = HTAB_SIZE(spapr) / HASH_PTE_SIZE_64; - int examined = 0, sent = 0; - int index = spapr->htab_save_index; - int64_t starttime = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - - assert(!spapr->htab_first_pass); - - do { - int chunkstart, invalidstart; - - /* Consume non-dirty HPTEs */ - while ((index < htabslots) - && !HPTE_DIRTY(HPTE(spapr->htab, index))) { - index++; - examined++; - } - - chunkstart = index; - /* Consume valid dirty HPTEs */ - while ((index < htabslots) && (index - chunkstart < USHRT_MAX) - && HPTE_DIRTY(HPTE(spapr->htab, index)) - && HPTE_VALID(HPTE(spapr->htab, index))) { - CLEAN_HPTE(HPTE(spapr->htab, index)); - index++; - examined++; - } - - invalidstart = index; - /* Consume invalid dirty HPTEs */ - while ((index < htabslots) && (index - invalidstart < USHRT_MAX) - && HPTE_DIRTY(HPTE(spapr->htab, index)) - && !HPTE_VALID(HPTE(spapr->htab, index))) { - CLEAN_HPTE(HPTE(spapr->htab, index)); - index++; - examined++; - } - - if (index > chunkstart) { - int n_valid = invalidstart - chunkstart; - int n_invalid = index - invalidstart; - - qemu_put_be32(f, chunkstart); - qemu_put_be16(f, n_valid); - qemu_put_be16(f, n_invalid); - qemu_put_buffer(f, HPTE(spapr->htab, chunkstart), - HASH_PTE_SIZE_64 * n_valid); - sent += index - chunkstart; - - if (!final && (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - starttime) > max_ns) { - break; - } - } - - if (examined >= htabslots) { - break; - } - - if (index >= htabslots) { - assert(index == htabslots); - index = 0; - } - } while ((examined < htabslots) && (!qemu_file_rate_limit(f) || final)); - - if (index >= htabslots) { - assert(index == htabslots); - index = 0; - } - - spapr->htab_save_index = index; - - return (examined >= htabslots) && (sent == 0) ? 1 : 0; -} - -#define MAX_ITERATION_NS 5000000 /* 5 ms */ -#define MAX_KVM_BUF_SIZE 2048 - -static int htab_save_iterate(QEMUFile *f, void *opaque) -{ - sPAPRMachineState *spapr = opaque; - int fd; - int rc = 0; - - /* Iteration header */ - qemu_put_be32(f, 0); - - if (!spapr->htab) { - assert(kvm_enabled()); - - fd = get_htab_fd(spapr); - if (fd < 0) { - return fd; - } - - rc = kvmppc_save_htab(f, fd, MAX_KVM_BUF_SIZE, MAX_ITERATION_NS); - if (rc < 0) { - return rc; - } - } else if (spapr->htab_first_pass) { - htab_save_first_pass(f, spapr, MAX_ITERATION_NS); - } else { - rc = htab_save_later_pass(f, spapr, MAX_ITERATION_NS); - } - - /* End marker */ - qemu_put_be32(f, 0); - qemu_put_be16(f, 0); - qemu_put_be16(f, 0); - - return rc; -} - -static int htab_save_complete(QEMUFile *f, void *opaque) -{ - sPAPRMachineState *spapr = opaque; - int fd; - - /* Iteration header */ - qemu_put_be32(f, 0); - - if (!spapr->htab) { - int rc; - - assert(kvm_enabled()); - - fd = get_htab_fd(spapr); - if (fd < 0) { - return fd; - } - - rc = kvmppc_save_htab(f, fd, MAX_KVM_BUF_SIZE, -1); - if (rc < 0) { - return rc; - } - close_htab_fd(spapr); - } else { - if (spapr->htab_first_pass) { - htab_save_first_pass(f, spapr, -1); - } - htab_save_later_pass(f, spapr, -1); - } - - /* End marker */ - qemu_put_be32(f, 0); - qemu_put_be16(f, 0); - qemu_put_be16(f, 0); - - return 0; -} - -static int htab_load(QEMUFile *f, void *opaque, int version_id) -{ - sPAPRMachineState *spapr = opaque; - uint32_t section_hdr; - int fd = -1; - - if (version_id < 1 || version_id > 1) { - error_report("htab_load() bad version"); - return -EINVAL; - } - - section_hdr = qemu_get_be32(f); - - if (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; - } - - if (!spapr->htab) { - assert(kvm_enabled()); - - fd = kvmppc_get_htab_fd(true); - if (fd < 0) { - error_report("Unable to open fd to restore KVM hash table: %s", - strerror(errno)); - } - } - - while (true) { - uint32_t index; - uint16_t n_valid, n_invalid; - - index = qemu_get_be32(f); - n_valid = qemu_get_be16(f); - n_invalid = qemu_get_be16(f); - - if ((index == 0) && (n_valid == 0) && (n_invalid == 0)) { - /* End of Stream */ - break; - } - - if ((index + n_valid + n_invalid) > - (HTAB_SIZE(spapr) / HASH_PTE_SIZE_64)) { - /* Bad index in stream */ - 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; - } - - if (spapr->htab) { - if (n_valid) { - qemu_get_buffer(f, HPTE(spapr->htab, index), - HASH_PTE_SIZE_64 * n_valid); - } - if (n_invalid) { - memset(HPTE(spapr->htab, index + n_valid), 0, - HASH_PTE_SIZE_64 * n_invalid); - } - } else { - int rc; - - assert(fd >= 0); - - rc = kvmppc_load_htab_chunk(f, fd, index, n_valid, n_invalid); - if (rc < 0) { - return rc; - } - } - } - - if (!spapr->htab) { - assert(fd >= 0); - close(fd); - } - - return 0; -} - -static SaveVMHandlers savevm_htab_handlers = { - .save_live_setup = htab_save_setup, - .save_live_iterate = htab_save_iterate, - .save_live_complete_precopy = htab_save_complete, - .load_state = htab_load, -}; - -static void spapr_boot_set(void *opaque, const char *boot_device, - Error **errp) -{ - MachineState *machine = MACHINE(qdev_get_machine()); - machine->boot_order = g_strdup(boot_device); -} - -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); - - /* Enable PAPR mode in TCG or KVM */ - cpu_ppc_set_papr(cpu); - - if (cpu->max_compat) { - Error *local_err = NULL; - - ppc_set_compat(cpu, cpu->max_compat, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - } - - xics_cpu_setup(spapr->icp, 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; - PowerPCCPU *cpu; - PCIHostState *phb; - int i; - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *rma_region; - void *rma = NULL; - hwaddr rma_alloc_size; - hwaddr node0_size = spapr_node0_size(); - uint32_t initrd_base = 0; - long kernel_size = 0, initrd_size = 0; - long load_limit, fw_size; - bool kernel_le = false; - char *filename; - - msi_nonbroken = true; - - QLIST_INIT(&spapr->phbs); - - cpu_ppc_hypercall = emulate_spapr_hypercall; - - /* Allocate RMA if necessary */ - rma_alloc_size = kvmppc_alloc_rma(&rma); - - if (rma_alloc_size == -1) { - error_report("Unable to create RMA"); - exit(1); - } - - if (rma_alloc_size && (rma_alloc_size < node0_size)) { - spapr->rma_size = rma_alloc_size; - } else { - spapr->rma_size = node0_size; - - /* With KVM, we don't actually know whether KVM supports an - * unbounded RMA (PR KVM) or is limited by the hash table size - * (HV KVM using VRMA), so we always assume the latter - * - * In that case, we also limit the initial allocations for RTAS - * etc... to 256M since we have no way to know what the VRMA size - * is going to be as it depends on the size of the hash table - * isn't determined yet. - */ - if (kvm_enabled()) { - spapr->vrma_adjust = 1; - spapr->rma_size = MIN(spapr->rma_size, 0x10000000); - } - } - - if (spapr->rma_size > node0_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; - - /* 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, &error_fatal); - - if (smc->dr_lmb_enabled) { - spapr_validate_node_memory(machine, &error_fatal); - } - - /* init CPUs */ - if (machine->cpu_model == NULL) { - machine->cpu_model = kvm_enabled() ? "host" : "POWER7"; - } - for (i = 0; i < smp_cpus; i++) { - cpu = cpu_ppc_init(machine->cpu_model); - if (cpu == NULL) { - error_report("Unable to find PowerPC CPU definition"); - exit(1); - } - 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 */ - memory_region_allocate_system_memory(ram, NULL, "ppc_spapr.ram", - machine->ram_size); - memory_region_add_subregion(sysmem, 0, ram); - - if (rma_alloc_size && rma) { - rma_region = g_new(MemoryRegion, 1); - memory_region_init_ram_ptr(rma_region, NULL, "ppc_spapr.rma", - rma_alloc_size, rma); - vmstate_register_ram_global(rma_region); - 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"); - exit(1); - } - spapr->rtas_size = get_image_size(filename); - spapr->rtas_blob = g_malloc(spapr->rtas_size); - if (load_image_size(filename, spapr->rtas_blob, spapr->rtas_size) < 0) { - error_report("Could not load LPAR rtas '%s'", filename); - exit(1); - } - if (spapr->rtas_size > RTAS_MAX_SIZE) { - error_report("RTAS too big ! 0x%zx bytes (max is 0x%x)", - (size_t)spapr->rtas_size, RTAS_MAX_SIZE); - exit(1); - } - g_free(filename); - - /* Set up EPOW events infrastructure */ - spapr_events_init(spapr); - - /* Set up the RTC RTAS interfaces */ - spapr_rtc_create(spapr); - - /* Set up VIO bus */ - spapr->vio_bus = spapr_vio_bus_init(); - - for (i = 0; i < MAX_SERIAL_PORTS; i++) { - if (serial_hds[i]) { - spapr_vty_create(spapr->vio_bus, serial_hds[i]); - } - } - - /* We always have at least the nvram device on VIO */ - spapr_create_nvram(spapr); - - /* Set up PCI */ - spapr_pci_rtas_init(); - - phb = spapr_create_phb(spapr, 0); - - for (i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - - if (!nd->model) { - nd->model = g_strdup("ibmveth"); - } - - if (strcmp(nd->model, "ibmveth") == 0) { - spapr_vlan_create(spapr->vio_bus, nd); - } else { - pci_nic_init_nofail(&nd_table[i], phb->bus, nd->model, NULL); - } - } - - for (i = 0; i <= drive_get_max_bus(IF_SCSI); i++) { - spapr_vscsi_create(spapr->vio_bus); - } - - /* Graphics */ - if (spapr_vga_init(phb->bus, &error_fatal)) { - spapr->has_graphics = true; - machine->usb |= defaults_enabled() && !machine->usb_disabled; - } - - if (machine->usb) { - 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); - - usb_create_simple(usb_bus, "usb-kbd"); - usb_create_simple(usb_bus, "usb-mouse"); - } - } - - if (spapr->rma_size < (MIN_RMA_SLOF << 20)) { - error_report( - "pSeries SLOF firmware requires >= %ldM guest RMA (Real Mode Area memory)", - MIN_RMA_SLOF); - exit(1); - } - - if (kernel_filename) { - uint64_t lowaddr = 0; - - kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - 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, PPC_ELF_MACHINE, - 0, 0); - kernel_le = kernel_size > 0; - } - if (kernel_size < 0) { - error_report("error loading %s: %s", - kernel_filename, load_elf_strerror(kernel_size)); - exit(1); - } - - /* load initrd */ - if (initrd_filename) { - /* Try to locate the initrd in the gap between the kernel - * and the firmware. Add a bit of space just in case - */ - initrd_base = (KERNEL_LOAD_ADDR + kernel_size + 0x1ffff) & ~0xffff; - initrd_size = load_image_targphys(initrd_filename, initrd_base, - load_limit - initrd_base); - if (initrd_size < 0) { - error_report("could not load initial ram disk '%s'", - initrd_filename); - exit(1); - } - } else { - initrd_base = 0; - initrd_size = 0; - } - } - - if (bios_name == NULL) { - bios_name = FW_FILE_NAME; - } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (!filename) { - error_report("Could not find LPAR firmware '%s'", bios_name); - exit(1); - } - fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE); - if (fw_size <= 0) { - error_report("Could not load LPAR firmware '%s'", filename); - exit(1); - } - g_free(filename); - - /* FIXME: Should register things through the MachineState's qdev - * interface, this is a legacy from the sPAPREnvironment structure - * which predated MachineState but had a similar function */ - vmstate_register(NULL, 0, &vmstate_spapr, spapr); - register_savevm_live(NULL, "spapr/htab", -1, 1, - &savevm_htab_handlers, spapr); - - /* Prepare the device tree */ - spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size, - kernel_size, kernel_le, - kernel_cmdline, - spapr->check_exception_irq); - assert(spapr->fdt_skel != NULL); - - /* used by RTAS */ - QTAILQ_INIT(&spapr->ccs_list); - qemu_register_reset(spapr_ccs_reset_hook, spapr); - - qemu_register_boot_set(spapr_boot_set, spapr); -} - -static int spapr_kvm_type(const char *vm_type) -{ - if (!vm_type) { - return 0; - } - - if (!strcmp(vm_type, "HV")) { - return 1; - } - - if (!strcmp(vm_type, "PR")) { - return 2; - } - - error_report("Unknown kvm-type specified '%s'", vm_type); - exit(1); -} - -/* - * Implementation of an interface to adjust firmware path - * for the bootindex property handling. - */ -static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus, - DeviceState *dev) -{ -#define CAST(type, obj, name) \ - ((type *)object_dynamic_cast(OBJECT(obj), (name))) - SCSIDevice *d = CAST(SCSIDevice, dev, TYPE_SCSI_DEVICE); - sPAPRPHBState *phb = CAST(sPAPRPHBState, dev, TYPE_SPAPR_PCI_HOST_BRIDGE); - - if (d) { - void *spapr = CAST(void, bus->parent, "spapr-vscsi"); - VirtIOSCSI *virtio = CAST(VirtIOSCSI, bus->parent, TYPE_VIRTIO_SCSI); - USBDevice *usb = CAST(USBDevice, bus->parent, TYPE_USB_DEVICE); - - if (spapr) { - /* - * Replace "channel@0/disk@0,0" with "disk@8000000000000000": - * We use SRP luns of the form 8000 | (bus << 8) | (id << 5) | lun - * in the top 16 bits of the 64-bit LUN - */ - unsigned id = 0x8000 | (d->id << 8) | d->lun; - return g_strdup_printf("%s@%"PRIX64, qdev_fw_name(dev), - (uint64_t)id << 48); - } else if (virtio) { - /* - * We use SRP luns of the form 01000000 | (target << 8) | lun - * in the top 32 bits of the 64-bit LUN - * Note: the quote above is from SLOF and it is wrong, - * the actual binding is: - * swap 0100 or 10 << or 20 << ( target lun-id -- srplun ) - */ - unsigned id = 0x1000000 | (d->id << 16) | d->lun; - return g_strdup_printf("%s@%"PRIX64, qdev_fw_name(dev), - (uint64_t)id << 32); - } else if (usb) { - /* - * We use SRP luns of the form 01000000 | (usb-port << 16) | lun - * in the top 32 bits of the 64-bit LUN - */ - unsigned usb_port = atoi(usb->port->path); - unsigned id = 0x1000000 | (usb_port << 16) | d->lun; - return g_strdup_printf("%s@%"PRIX64, qdev_fw_name(dev), - (uint64_t)id << 32); - } - } - - if (phb) { - /* Replace "pci" with "pci@800000020000000" */ - return g_strdup_printf("pci@%"PRIX64, phb->buid); - } - - return NULL; -} - -static char *spapr_get_kvm_type(Object *obj, Error **errp) -{ - sPAPRMachineState *spapr = SPAPR_MACHINE(obj); - - return g_strdup(spapr->kvm_type); -} - -static void spapr_set_kvm_type(Object *obj, const char *value, Error **errp) -{ - sPAPRMachineState *spapr = SPAPR_MACHINE(obj); - - g_free(spapr->kvm_type); - spapr->kvm_type = g_strdup(value); -} - -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", - "Specifies the KVM virtualization mode (HV, PR)", - 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; - - cpu_synchronize_state(cs); - ppc_cpu_do_system_reset(cs); -} - -static void spapr_nmi(NMIState *n, int cpu_index, Error **errp) -{ - CPUState *cs; - - CPU_FOREACH(cs) { - async_run_on_cpu(cs, ppc_cpu_do_nmi_on_cpu, cs); - } -} - -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_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; -} - -static const TypeInfo spapr_machine_info = { - .name = TYPE_SPAPR_MACHINE, - .parent = TYPE_MACHINE, - .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 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) - -/* - * pseries-2.6 - */ -static void spapr_machine_2_6_instance_options(MachineState *machine) -{ -} - -static void spapr_machine_2_6_class_options(MachineClass *mc) -{ - /* Defaults for the latest behaviour inherited from the base class */ -} - -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_5_instance_options(MachineState *machine) -{ -} - -static void spapr_machine_2_5_class_options(MachineClass *mc) -{ - 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); -} - -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_machine_2_5_instance_options(machine); -} - -static void spapr_machine_2_4_class_options(MachineClass *mc) -{ - sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); - - spapr_machine_2_5_class_options(mc); - smc->dr_lmb_enabled = false; - SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_4); -} - -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_3_instance_options(MachineState *machine) -{ - spapr_machine_2_4_instance_options(machine); - savevm_skip_section_footers(); - global_state_set_optional(); - savevm_skip_configuration(); -} - -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); - -/* - * pseries-2.2 - */ - -#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) -{ - spapr_machine_2_3_instance_options(machine); - machine->suppress_vmdesc = true; -} - -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); - -/* - * pseries-2.1 - */ -#define SPAPR_COMPAT_2_1 \ - SPAPR_COMPAT_2_2 \ - HW_COMPAT_2_1 - -static void spapr_machine_2_1_instance_options(MachineState *machine) -{ - spapr_machine_2_2_instance_options(machine); -} - -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_init(spapr_machine_register_types) diff --git a/qemu/hw/ppc/spapr_drc.c b/qemu/hw/ppc/spapr_drc.c deleted file mode 100644 index 1f5f1d790..000000000 --- a/qemu/hw/ppc/spapr_drc.c +++ /dev/null @@ -1,843 +0,0 @@ -/* - * QEMU SPAPR Dynamic Reconfiguration Connector Implementation - * - * Copyright IBM Corp. 2014 - * - * Authors: - * Michael Roth - * - * 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 "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 */ - -#ifdef DEBUG_SPAPR_DRC -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) -#define DPRINTFN(fmt, ...) \ - do { DPRINTF(fmt, ## __VA_ARGS__); fprintf(stderr, "\n"); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#define DPRINTFN(fmt, ...) \ - do { } while (0) -#endif - -#define DRC_CONTAINER_PATH "/dr-connector" -#define DRC_INDEX_TYPE_SHIFT 28 -#define DRC_INDEX_ID_MASK ((1ULL << DRC_INDEX_TYPE_SHIFT) - 1) - -static sPAPRDRConnectorTypeShift get_type_shift(sPAPRDRConnectorType type) -{ - uint32_t shift = 0; - - /* make sure this isn't SPAPR_DR_CONNECTOR_TYPE_ANY, or some - * other wonky value. - */ - g_assert(is_power_of_2(type)); - - while (type != (1 << shift)) { - shift++; - } - return shift; -} - -static uint32_t get_index(sPAPRDRConnector *drc) -{ - /* no set format for a drc index: it only needs to be globally - * unique. this is how we encode the DRC type on bare-metal - * however, so might as well do that here - */ - return (get_type_shift(drc->type) << DRC_INDEX_TYPE_SHIFT) | - (drc->id & DRC_INDEX_ID_MASK); -} - -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) { - /* if we're awaiting release, but still in an unconfigured state, - * it's likely the guest is still in the process of configuring - * the device and is transitioning the devices to an ISOLATED - * state as a part of that process. so we only complete the - * removal when this transition happens for a device in a - * configured state, as suggested by the state diagram from - * PAPR+ 2.7, 13.4 - */ - if (drc->awaiting_release) { - if (drc->configured) { - DPRINTFN("finalizing device removal"); - drck->detach(drc, DEVICE(drc->dev), drc->detach_cb, - drc->detach_cb_opaque, NULL); - } else { - DPRINTFN("deferring device removal on unconfigured device\n"); - } - } - drc->configured = false; - } - - return RTAS_OUT_SUCCESS; -} - -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 RTAS_OUT_SUCCESS; -} - -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 && - drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) { - DPRINTFN("finalizing device removal"); - drck->detach(drc, DEVICE(drc->dev), drc->detach_cb, - drc->detach_cb_opaque, NULL); - } - } - return RTAS_OUT_SUCCESS; -} - -static uint32_t get_type(sPAPRDRConnector *drc) -{ - return drc->type; -} - -static const char *get_name(sPAPRDRConnector *drc) -{ - return drc->name; -} - -static const void *get_fdt(sPAPRDRConnector *drc, int *fdt_start_offset) -{ - if (fdt_start_offset) { - *fdt_start_offset = drc->fdt_start_offset; - } - return drc->fdt; -} - -static void set_configured(sPAPRDRConnector *drc) -{ - DPRINTFN("drc: %x, set_configured", get_index(drc)); - - if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_UNISOLATED) { - /* guest should be not configuring an isolated device */ - DPRINTFN("drc: %x, set_configured: skipping isolated device", - get_index(drc)); - return; - } - 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 - * as expected by state diagram in PAPR+ 2.7, 13.4 - * based on the current allocation/indicator/power states - * for the DR connector. - */ -static uint32_t entity_sense(sPAPRDRConnector *drc, sPAPRDREntitySense *state) -{ - if (drc->dev) { - if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI && - drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) { - /* for logical DR, we return a state of UNUSABLE - * iff the allocation state UNUSABLE. - * Otherwise, report the state as USABLE/PRESENT, - * as we would for PCI. - */ - *state = SPAPR_DR_ENTITY_SENSE_UNUSABLE; - } else { - /* this assumes all PCI devices are assigned to - * a 'live insertion' power domain, where QEMU - * manages power state automatically as opposed - * to the guest. present, non-PCI resources are - * unaffected by power state. - */ - *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; - } else { - *state = SPAPR_DR_ENTITY_SENSE_UNUSABLE; - } - } - - DPRINTFN("drc: %x, entity_sense: %x", get_index(drc), state); - return RTAS_OUT_SUCCESS; -} - -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, name, &value, 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, name, &value, errp); -} - -static char *prop_get_name(Object *obj, Error **errp) -{ - sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - return g_strdup(drck->get_name(drc)); -} - -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; - - drck->entity_sense(drc, &value); - visit_type_uint32(v, name, &value, 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; - } - - fdt = drc->fdt; - fdt_offset = drc->fdt_start_offset; - fdt_depth = 0; - - do { - const char *name = NULL; - const struct fdt_property *prop = NULL; - int prop_len = 0, name_len = 0; - uint32_t tag; - - tag = fdt_next_tag(fdt, fdt_offset, &fdt_offset_next); - switch (tag) { - case FDT_BEGIN_NODE: - fdt_depth++; - name = fdt_get_name(fdt, fdt_offset, &name_len); - 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, &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, &err); - if (err) { - error_propagate(errp, err); - return; - } - for (i = 0; i < prop_len; i++) { - visit_type_uint8(v, NULL, (uint8_t *)&prop->data[i], &err); - if (err) { - error_propagate(errp, err); - return; - } - } - visit_end_list(v); - break; - } - default: - error_setg(&error_abort, "device FDT in unexpected state: %d", tag); - } - fdt_offset = fdt_offset_next; - } while (fdt_depth != 0); -} - -static void attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt, - int fdt_start_offset, bool coldplug, Error **errp) -{ - DPRINTFN("drc: %x, attach", get_index(drc)); - - if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_ISOLATED) { - error_setg(errp, "an attached device is still awaiting release"); - return; - } - if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) { - g_assert(drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_USABLE); - } - g_assert(fdt || coldplug); - - /* NOTE: setting initial isolation state to UNISOLATED means we can't - * detach unless guest has a userspace/kernel that moves this state - * back to ISOLATED in response to an unplug event, or this is done - * manually by the admin prior. if we force things while the guest - * may be accessing the device, we can easily crash the guest, so we - * we defer completion of removal in such cases to the reset() hook. - */ - if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) { - drc->isolation_state = SPAPR_DR_ISOLATION_STATE_UNISOLATED; - } - drc->indicator_state = SPAPR_DR_INDICATOR_STATE_ACTIVE; - - drc->dev = d; - drc->fdt = fdt; - drc->fdt_start_offset = fdt_start_offset; - 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)), - (Object **)(&drc->dev), - NULL, 0, NULL); -} - -static void detach(sPAPRDRConnector *drc, DeviceState *d, - spapr_drc_detach_cb *detach_cb, - void *detach_cb_opaque, Error **errp) -{ - DPRINTFN("drc: %x, detach", get_index(drc)); - - 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; - return; - } - - if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI && - drc->allocation_state != SPAPR_DR_ALLOCATION_STATE_UNUSABLE) { - DPRINTFN("awaiting transition to unusable state before removal"); - drc->awaiting_release = true; - return; - } - - drc->indicator_state = SPAPR_DR_INDICATOR_STATE_INACTIVE; - - if (drc->detach_cb) { - drc->detach_cb(drc->dev, drc->detach_cb_opaque); - } - - drc->awaiting_release = false; - g_free(drc->fdt); - drc->fdt = NULL; - drc->fdt_start_offset = 0; - object_property_del(OBJECT(drc), "device", NULL); - drc->dev = NULL; - drc->detach_cb = NULL; - drc->detach_cb_opaque = NULL; -} - -static bool release_pending(sPAPRDRConnector *drc) -{ - return drc->awaiting_release; -} - -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 - * are pending removal can be safely removed, and that they will - * subsequently be left in an ISOLATED state. move the DRC to this - * state in these cases (which will in turn complete any pending - * device removals) - */ - if (drc->awaiting_release) { - drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_ISOLATED); - /* generally this should also finalize the removal, but if the device - * hasn't yet been configured we normally defer removal under the - * assumption that this transition is taking place as part of device - * configuration. so check if we're still waiting after this, and - * force removal if we are - */ - if (drc->awaiting_release) { - drck->detach(drc, DEVICE(drc->dev), drc->detach_cb, - drc->detach_cb_opaque, NULL); - } - - /* non-PCI devices may be awaiting a transition to UNUSABLE */ - if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI && - drc->awaiting_release) { - 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) -{ - sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d); - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - Object *root_container; - char link_name[256]; - gchar *child_name; - Error *err = NULL; - - DPRINTFN("drc realize: %x", drck->get_index(drc)); - /* NOTE: we do this as part of realize/unrealize due to the fact - * that the guest will communicate with the DRC via RTAS calls - * referencing the global DRC index. By unlinking the DRC - * from DRC_CONTAINER_PATH/ we effectively make it - * inaccessible by the guest, since lookups rely on this path - * existing in the composition tree - */ - root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); - snprintf(link_name, sizeof(link_name), "%x", drck->get_index(drc)); - child_name = object_get_canonical_path_component(OBJECT(drc)); - DPRINTFN("drc child name: %s", child_name); - object_property_add_alias(root_container, link_name, - drc->owner, child_name, &err); - if (err) { - error_report_err(err); - object_unref(OBJECT(drc)); - } - g_free(child_name); - DPRINTFN("drc realize complete"); -} - -static void unrealize(DeviceState *d, Error **errp) -{ - sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d); - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - Object *root_container; - char name[256]; - Error *err = NULL; - - DPRINTFN("drc unrealize: %x", drck->get_index(drc)); - root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); - snprintf(name, sizeof(name), "%x", drck->get_index(drc)); - object_property_del(root_container, name, &err); - if (err) { - error_report_err(err); - object_unref(OBJECT(drc)); - } -} - -sPAPRDRConnector *spapr_dr_connector_new(Object *owner, - sPAPRDRConnectorType type, - uint32_t id) -{ - 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; - 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 - * of the unique DRC index. - * - * in the case of VIO/PCI devices, it corresponds to a - * "location code" that maps a logical device/function (DRC index) - * to a physical (or virtual in the case of VIO) location in the - * system by chaining together the "location label" for each - * encapsulating component. - * - * since this is more to do with diagnosing physical hardware - * issues than guest compatibility, we choose location codes/DRC - * names that adhere to the documented format, but avoid encoding - * the entire topology information into the label/code, instead - * just using the location codes based on the labels for the - * endpoints (VIO/PCI adaptor connectors), which is basically - * just "C" followed by an integer ID. - * - * DRC names as documented by PAPR+ v2.7, 13.5.2.4 - * location codes as documented by PAPR+ v2.7, 12.3.1.5 - */ - switch (drc->type) { - case SPAPR_DR_CONNECTOR_TYPE_CPU: - drc->name = g_strdup_printf("CPU %d", id); - break; - case SPAPR_DR_CONNECTOR_TYPE_PHB: - drc->name = g_strdup_printf("PHB %d", id); - break; - case SPAPR_DR_CONNECTOR_TYPE_VIO: - case SPAPR_DR_CONNECTOR_TYPE_PCI: - drc->name = g_strdup_printf("C%d", id); - break; - case SPAPR_DR_CONNECTOR_TYPE_LMB: - drc->name = g_strdup_printf("LMB %d", id); - break; - default: - g_assert(false); - } - - /* PCI slot always start in a USABLE state, and stay there */ - if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) { - drc->allocation_state = SPAPR_DR_ALLOCATION_STATE_USABLE; - } - - return drc; -} - -static void spapr_dr_connector_instance_init(Object *obj) -{ - sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); - - object_property_add_uint32_ptr(obj, "isolation-state", - &drc->isolation_state, NULL); - object_property_add_uint32_ptr(obj, "indicator-state", - &drc->indicator_state, NULL); - object_property_add_uint32_ptr(obj, "allocation-state", - &drc->allocation_state, NULL); - object_property_add_uint32_ptr(obj, "id", &drc->id, NULL); - object_property_add(obj, "index", "uint32", prop_get_index, - NULL, NULL, NULL, NULL); - object_property_add(obj, "connector_type", "uint32", prop_get_type, - NULL, NULL, NULL, NULL); - object_property_add_str(obj, "name", prop_get_name, NULL, NULL); - object_property_add(obj, "entity-sense", "uint32", prop_get_entity_sense, - NULL, NULL, NULL, NULL); - object_property_add(obj, "fdt", "struct", prop_get_fdt, - NULL, NULL, NULL, NULL); -} - -static void spapr_dr_connector_class_init(ObjectClass *k, void *data) -{ - DeviceClass *dk = DEVICE_CLASS(k); - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); - - dk->reset = reset; - dk->realize = realize; - dk->unrealize = unrealize; - drck->set_isolation_state = set_isolation_state; - drck->set_indicator_state = set_indicator_state; - drck->set_allocation_state = set_allocation_state; - drck->get_index = get_index; - drck->get_type = get_type; - drck->get_name = get_name; - drck->get_fdt = get_fdt; - drck->set_configured = set_configured; - drck->entity_sense = entity_sense; - 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 = { - .name = TYPE_SPAPR_DR_CONNECTOR, - .parent = TYPE_DEVICE, - .instance_size = sizeof(sPAPRDRConnector), - .instance_init = spapr_dr_connector_instance_init, - .class_size = sizeof(sPAPRDRConnectorClass), - .class_init = spapr_dr_connector_class_init, -}; - -static void spapr_drc_register_types(void) -{ - type_register_static(&spapr_dr_connector_info); -} - -type_init(spapr_drc_register_types) - -/* helper functions for external users */ - -sPAPRDRConnector *spapr_dr_connector_by_index(uint32_t index) -{ - Object *obj; - char name[256]; - - snprintf(name, sizeof(name), "%s/%x", DRC_CONTAINER_PATH, index); - obj = object_resolve_path(name, NULL); - - return !obj ? NULL : SPAPR_DR_CONNECTOR(obj); -} - -sPAPRDRConnector *spapr_dr_connector_by_id(sPAPRDRConnectorType type, - uint32_t id) -{ - return spapr_dr_connector_by_index( - (get_type_shift(type) << DRC_INDEX_TYPE_SHIFT) | - (id & DRC_INDEX_ID_MASK)); -} - -/* generate a string the describes the DRC to encode into the - * device tree. - * - * as documented by PAPR+ v2.7, 13.5.2.6 and C.6.1 - */ -static const char *spapr_drc_get_type_str(sPAPRDRConnectorType type) -{ - switch (type) { - case SPAPR_DR_CONNECTOR_TYPE_CPU: - return "CPU"; - case SPAPR_DR_CONNECTOR_TYPE_PHB: - return "PHB"; - case SPAPR_DR_CONNECTOR_TYPE_VIO: - return "SLOT"; - case SPAPR_DR_CONNECTOR_TYPE_PCI: - return "28"; - case SPAPR_DR_CONNECTOR_TYPE_LMB: - return "MEM"; - default: - g_assert(false); - } - - return NULL; -} - -/** - * spapr_drc_populate_dt - * - * @fdt: libfdt device tree - * @path: path in the DT to generate properties - * @owner: parent Object/DeviceState for which to generate DRC - * descriptions for - * @drc_type_mask: mask of sPAPRDRConnectorType values corresponding - * to the types of DRCs to generate entries for - * - * generate OF properties to describe DRC topology/indices to guests - * - * as documented in PAPR+ v2.1, 13.5.2 - */ -int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner, - uint32_t drc_type_mask) -{ - Object *root_container; - ObjectProperty *prop; - ObjectPropertyIterator iter; - uint32_t drc_count = 0; - GArray *drc_indexes, *drc_power_domains; - GString *drc_names, *drc_types; - int ret; - - /* the first entry of each properties is a 32-bit integer encoding - * the number of elements in the array. we won't know this until - * we complete the iteration through all the matching DRCs, but - * reserve the space now and set the offsets accordingly so we - * can fill them in later. - */ - drc_indexes = g_array_new(false, true, sizeof(uint32_t)); - drc_indexes = g_array_set_size(drc_indexes, 1); - drc_power_domains = g_array_new(false, true, sizeof(uint32_t)); - drc_power_domains = g_array_set_size(drc_power_domains, 1); - drc_names = g_string_set_size(g_string_new(NULL), sizeof(uint32_t)); - drc_types = g_string_set_size(g_string_new(NULL), sizeof(uint32_t)); - - /* aliases for all DRConnector objects will be rooted in QOM - * composition tree at DRC_CONTAINER_PATH - */ - root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); - - object_property_iter_init(&iter, root_container); - while ((prop = object_property_iter_next(&iter))) { - Object *obj; - sPAPRDRConnector *drc; - sPAPRDRConnectorClass *drck; - uint32_t drc_index, drc_power_domain; - - if (!strstart(prop->type, "link<", NULL)) { - continue; - } - - obj = object_property_get_link(root_container, prop->name, NULL); - drc = SPAPR_DR_CONNECTOR(obj); - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - - if (owner && (drc->owner != owner)) { - continue; - } - - if ((drc->type & drc_type_mask) == 0) { - continue; - } - - drc_count++; - - /* ibm,drc-indexes */ - drc_index = cpu_to_be32(drck->get_index(drc)); - g_array_append_val(drc_indexes, drc_index); - - /* ibm,drc-power-domains */ - drc_power_domain = cpu_to_be32(-1); - g_array_append_val(drc_power_domains, drc_power_domain); - - /* ibm,drc-names */ - drc_names = g_string_append(drc_names, drck->get_name(drc)); - drc_names = g_string_insert_len(drc_names, -1, "\0", 1); - - /* ibm,drc-types */ - drc_types = g_string_append(drc_types, - spapr_drc_get_type_str(drc->type)); - drc_types = g_string_insert_len(drc_types, -1, "\0", 1); - } - - /* now write the drc count into the space we reserved at the - * beginning of the arrays previously - */ - *(uint32_t *)drc_indexes->data = cpu_to_be32(drc_count); - *(uint32_t *)drc_power_domains->data = cpu_to_be32(drc_count); - *(uint32_t *)drc_names->str = cpu_to_be32(drc_count); - *(uint32_t *)drc_types->str = cpu_to_be32(drc_count); - - ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-indexes", - drc_indexes->data, - drc_indexes->len * sizeof(uint32_t)); - if (ret) { - fprintf(stderr, "Couldn't create ibm,drc-indexes property\n"); - goto out; - } - - ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-power-domains", - drc_power_domains->data, - drc_power_domains->len * sizeof(uint32_t)); - if (ret) { - fprintf(stderr, "Couldn't finalize ibm,drc-power-domains property\n"); - goto out; - } - - ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-names", - drc_names->str, drc_names->len); - if (ret) { - fprintf(stderr, "Couldn't finalize ibm,drc-names property\n"); - goto out; - } - - ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-types", - drc_types->str, drc_types->len); - if (ret) { - fprintf(stderr, "Couldn't finalize ibm,drc-types property\n"); - goto out; - } - -out: - g_array_free(drc_indexes, true); - g_array_free(drc_power_domains, true); - g_string_free(drc_names, true); - g_string_free(drc_types, true); - - return ret; -} diff --git a/qemu/hw/ppc/spapr_events.c b/qemu/hw/ppc/spapr_events.c deleted file mode 100644 index 049fb1b32..000000000 --- a/qemu/hw/ppc/spapr_events.c +++ /dev/null @@ -1,610 +0,0 @@ -/* - * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator - * - * RTAS events handling - * - * Copyright (c) 2012 David Gibson, IBM Corporation. - * - * 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 "cpu.h" -#include "sysemu/sysemu.h" -#include "sysemu/char.h" -#include "hw/qdev.h" -#include "sysemu/device_tree.h" - -#include "hw/ppc/spapr.h" -#include "hw/ppc/spapr_vio.h" -#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 - -struct rtas_error_log { - uint32_t summary; -#define RTAS_LOG_VERSION_MASK 0xff000000 -#define RTAS_LOG_VERSION_6 0x06000000 -#define RTAS_LOG_SEVERITY_MASK 0x00e00000 -#define RTAS_LOG_SEVERITY_ALREADY_REPORTED 0x00c00000 -#define RTAS_LOG_SEVERITY_FATAL 0x00a00000 -#define RTAS_LOG_SEVERITY_ERROR 0x00800000 -#define RTAS_LOG_SEVERITY_ERROR_SYNC 0x00600000 -#define RTAS_LOG_SEVERITY_WARNING 0x00400000 -#define RTAS_LOG_SEVERITY_EVENT 0x00200000 -#define RTAS_LOG_SEVERITY_NO_ERROR 0x00000000 -#define RTAS_LOG_DISPOSITION_MASK 0x00180000 -#define RTAS_LOG_DISPOSITION_FULLY_RECOVERED 0x00000000 -#define RTAS_LOG_DISPOSITION_LIMITED_RECOVERY 0x00080000 -#define RTAS_LOG_DISPOSITION_NOT_RECOVERED 0x00100000 -#define RTAS_LOG_OPTIONAL_PART_PRESENT 0x00040000 -#define RTAS_LOG_INITIATOR_MASK 0x0000f000 -#define RTAS_LOG_INITIATOR_UNKNOWN 0x00000000 -#define RTAS_LOG_INITIATOR_CPU 0x00001000 -#define RTAS_LOG_INITIATOR_PCI 0x00002000 -#define RTAS_LOG_INITIATOR_MEMORY 0x00004000 -#define RTAS_LOG_INITIATOR_HOTPLUG 0x00006000 -#define RTAS_LOG_TARGET_MASK 0x00000f00 -#define RTAS_LOG_TARGET_UNKNOWN 0x00000000 -#define RTAS_LOG_TARGET_CPU 0x00000100 -#define RTAS_LOG_TARGET_PCI 0x00000200 -#define RTAS_LOG_TARGET_MEMORY 0x00000400 -#define RTAS_LOG_TARGET_HOTPLUG 0x00000600 -#define RTAS_LOG_TYPE_MASK 0x000000ff -#define RTAS_LOG_TYPE_OTHER 0x00000000 -#define RTAS_LOG_TYPE_RETRY 0x00000001 -#define RTAS_LOG_TYPE_TCE_ERR 0x00000002 -#define RTAS_LOG_TYPE_INTERN_DEV_FAIL 0x00000003 -#define RTAS_LOG_TYPE_TIMEOUT 0x00000004 -#define RTAS_LOG_TYPE_DATA_PARITY 0x00000005 -#define RTAS_LOG_TYPE_ADDR_PARITY 0x00000006 -#define RTAS_LOG_TYPE_CACHE_PARITY 0x00000007 -#define RTAS_LOG_TYPE_ADDR_INVALID 0x00000008 -#define RTAS_LOG_TYPE_ECC_UNCORR 0x00000009 -#define RTAS_LOG_TYPE_ECC_CORR 0x0000000a -#define RTAS_LOG_TYPE_EPOW 0x00000040 -#define RTAS_LOG_TYPE_HOTPLUG 0x000000e5 - uint32_t extended_length; -} QEMU_PACKED; - -struct rtas_event_log_v6 { - uint8_t b0; -#define RTAS_LOG_V6_B0_VALID 0x80 -#define RTAS_LOG_V6_B0_UNRECOVERABLE_ERROR 0x40 -#define RTAS_LOG_V6_B0_RECOVERABLE_ERROR 0x20 -#define RTAS_LOG_V6_B0_DEGRADED_OPERATION 0x10 -#define RTAS_LOG_V6_B0_PREDICTIVE_ERROR 0x08 -#define RTAS_LOG_V6_B0_NEW_LOG 0x04 -#define RTAS_LOG_V6_B0_BIGENDIAN 0x02 - uint8_t _resv1; - uint8_t b2; -#define RTAS_LOG_V6_B2_POWERPC_FORMAT 0x80 -#define RTAS_LOG_V6_B2_LOG_FORMAT_MASK 0x0f -#define RTAS_LOG_V6_B2_LOG_FORMAT_PLATFORM_EVENT 0x0e - uint8_t _resv2[9]; - uint32_t company; -#define RTAS_LOG_V6_COMPANY_IBM 0x49424d00 /* IBM */ -} QEMU_PACKED; - -struct rtas_event_log_v6_section_header { - uint16_t section_id; - uint16_t section_length; - uint8_t section_version; - uint8_t section_subtype; - uint16_t creator_component_id; -} QEMU_PACKED; - -struct rtas_event_log_v6_maina { -#define RTAS_LOG_V6_SECTION_ID_MAINA 0x5048 /* PH */ - struct rtas_event_log_v6_section_header hdr; - uint32_t creation_date; /* BCD: YYYYMMDD */ - uint32_t creation_time; /* BCD: HHMMSS00 */ - uint8_t _platform1[8]; - char creator_id; - uint8_t _resv1[2]; - uint8_t section_count; - uint8_t _resv2[4]; - uint8_t _platform2[8]; - uint32_t plid; - uint8_t _platform3[4]; -} QEMU_PACKED; - -struct rtas_event_log_v6_mainb { -#define RTAS_LOG_V6_SECTION_ID_MAINB 0x5548 /* UH */ - struct rtas_event_log_v6_section_header hdr; - uint8_t subsystem_id; - uint8_t _platform1; - uint8_t event_severity; - uint8_t event_subtype; - uint8_t _platform2[4]; - uint8_t _resv1[2]; - uint16_t action_flags; - uint8_t _resv2[4]; -} QEMU_PACKED; - -struct rtas_event_log_v6_epow { -#define RTAS_LOG_V6_SECTION_ID_EPOW 0x4550 /* EP */ - struct rtas_event_log_v6_section_header hdr; - uint8_t sensor_value; -#define RTAS_LOG_V6_EPOW_ACTION_RESET 0 -#define RTAS_LOG_V6_EPOW_ACTION_WARN_COOLING 1 -#define RTAS_LOG_V6_EPOW_ACTION_WARN_POWER 2 -#define RTAS_LOG_V6_EPOW_ACTION_SYSTEM_SHUTDOWN 3 -#define RTAS_LOG_V6_EPOW_ACTION_SYSTEM_HALT 4 -#define RTAS_LOG_V6_EPOW_ACTION_MAIN_ENCLOSURE 5 -#define RTAS_LOG_V6_EPOW_ACTION_POWER_OFF 7 - uint8_t event_modifier; -#define RTAS_LOG_V6_EPOW_MODIFIER_NORMAL 1 -#define RTAS_LOG_V6_EPOW_MODIFIER_ON_UPS 2 -#define RTAS_LOG_V6_EPOW_MODIFIER_CRITICAL 3 -#define RTAS_LOG_V6_EPOW_MODIFIER_TEMPERATURE 4 - uint8_t extended_modifier; -#define RTAS_LOG_V6_EPOW_XMODIFIER_SYSTEM_WIDE 0 -#define RTAS_LOG_V6_EPOW_XMODIFIER_PARTITION_SPECIFIC 1 - uint8_t _resv; - uint64_t reason_code; -} QEMU_PACKED; - -struct epow_log_full { - struct rtas_error_log hdr; - struct rtas_event_log_v6 v6hdr; - struct rtas_event_log_v6_maina maina; - struct rtas_event_log_v6_mainb mainb; - struct rtas_event_log_v6_epow epow; -} QEMU_PACKED; - -struct rtas_event_log_v6_hp { -#define RTAS_LOG_V6_SECTION_ID_HOTPLUG 0x4850 /* HP */ - struct rtas_event_log_v6_section_header hdr; - uint8_t hotplug_type; -#define RTAS_LOG_V6_HP_TYPE_CPU 1 -#define RTAS_LOG_V6_HP_TYPE_MEMORY 2 -#define RTAS_LOG_V6_HP_TYPE_SLOT 3 -#define RTAS_LOG_V6_HP_TYPE_PHB 4 -#define RTAS_LOG_V6_HP_TYPE_PCI 5 - uint8_t hotplug_action; -#define RTAS_LOG_V6_HP_ACTION_ADD 1 -#define RTAS_LOG_V6_HP_ACTION_REMOVE 2 - uint8_t hotplug_identifier; -#define RTAS_LOG_V6_HP_ID_DRC_NAME 1 -#define RTAS_LOG_V6_HP_ID_DRC_INDEX 2 -#define RTAS_LOG_V6_HP_ID_DRC_COUNT 3 - uint8_t reserved; - union { - uint32_t index; - uint32_t count; - char name[1]; - } drc; -} QEMU_PACKED; - -struct hp_log_full { - struct rtas_error_log hdr; - struct rtas_event_log_v6 v6hdr; - struct rtas_event_log_v6_maina maina; - struct rtas_event_log_v6_mainb mainb; - struct rtas_event_log_v6_hp hp; -} QEMU_PACKED; - -#define EVENT_MASK_INTERNAL_ERRORS 0x80000000 -#define EVENT_MASK_EPOW 0x40000000 -#define EVENT_MASK_HOTPLUG 0x10000000 -#define EVENT_MASK_IO 0x08000000 - -#define _FDT(exp) \ - do { \ - int ret = (exp); \ - if (ret < 0) { \ - fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \ - #exp, fdt_strerror(ret)); \ - exit(1); \ - } \ - } while (0) - -void spapr_events_fdt_skel(void *fdt, uint32_t check_exception_irq) -{ - uint32_t irq_ranges[] = {cpu_to_be32(check_exception_irq), cpu_to_be32(1)}; - uint32_t interrupts[] = {cpu_to_be32(check_exception_irq), 0}; - - _FDT((fdt_begin_node(fdt, "event-sources"))); - - _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0))); - _FDT((fdt_property_cell(fdt, "#interrupt-cells", 2))); - _FDT((fdt_property(fdt, "interrupt-ranges", - irq_ranges, sizeof(irq_ranges)))); - - _FDT((fdt_begin_node(fdt, "epow-events"))); - _FDT((fdt_property(fdt, "interrupts", interrupts, sizeof(interrupts)))); - _FDT((fdt_end_node(fdt))); - - _FDT((fdt_end_node(fdt))); -} - -static void rtas_event_log_queue(int log_type, void *data, bool exception) -{ - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - sPAPREventLogEntry *entry = g_new(sPAPREventLogEntry, 1); - - g_assert(data); - entry->log_type = log_type; - entry->exception = exception; - entry->data = data; - QTAILQ_INSERT_TAIL(&spapr->pending_events, entry, next); -} - -static sPAPREventLogEntry *rtas_event_log_dequeue(uint32_t event_mask, - bool exception) -{ - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - sPAPREventLogEntry *entry = NULL; - - /* we only queue EPOW events atm. */ - if ((event_mask & EVENT_MASK_EPOW) == 0) { - return NULL; - } - - QTAILQ_FOREACH(entry, &spapr->pending_events, next) { - if (entry->exception != exception) { - continue; - } - - /* EPOW and hotplug events are surfaced in the same manner */ - if (entry->log_type == RTAS_LOG_TYPE_EPOW || - entry->log_type == RTAS_LOG_TYPE_HOTPLUG) { - break; - } - } - - if (entry) { - QTAILQ_REMOVE(&spapr->pending_events, entry, next); - } - - return entry; -} - -static bool rtas_event_log_contains(uint32_t event_mask, bool exception) -{ - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - sPAPREventLogEntry *entry = NULL; - - /* we only queue EPOW events atm. */ - if ((event_mask & EVENT_MASK_EPOW) == 0) { - return false; - } - - QTAILQ_FOREACH(entry, &spapr->pending_events, next) { - if (entry->exception != exception) { - continue; - } - - /* EPOW and hotplug events are surfaced in the same manner */ - if (entry->log_type == RTAS_LOG_TYPE_EPOW || - entry->log_type == RTAS_LOG_TYPE_HOTPLUG) { - return true; - } - } - - return false; -} - -static uint32_t next_plid; - -static void spapr_init_v6hdr(struct rtas_event_log_v6 *v6hdr) -{ - v6hdr->b0 = RTAS_LOG_V6_B0_VALID | RTAS_LOG_V6_B0_NEW_LOG - | RTAS_LOG_V6_B0_BIGENDIAN; - v6hdr->b2 = RTAS_LOG_V6_B2_POWERPC_FORMAT - | RTAS_LOG_V6_B2_LOG_FORMAT_PLATFORM_EVENT; - v6hdr->company = cpu_to_be32(RTAS_LOG_V6_COMPANY_IBM); -} - -static void spapr_init_maina(struct rtas_event_log_v6_maina *maina, - int section_count) -{ - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - struct tm tm; - int year; - - maina->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINA); - maina->hdr.section_length = cpu_to_be16(sizeof(*maina)); - /* FIXME: section version, subtype and creator id? */ - spapr_rtc_read(spapr->rtc, &tm, NULL); - year = tm.tm_year + 1900; - maina->creation_date = cpu_to_be32((to_bcd(year / 100) << 24) - | (to_bcd(year % 100) << 16) - | (to_bcd(tm.tm_mon + 1) << 8) - | to_bcd(tm.tm_mday)); - maina->creation_time = cpu_to_be32((to_bcd(tm.tm_hour) << 24) - | (to_bcd(tm.tm_min) << 16) - | (to_bcd(tm.tm_sec) << 8)); - maina->creator_id = 'H'; /* Hypervisor */ - maina->section_count = section_count; - maina->plid = next_plid++; -} - -static void spapr_powerdown_req(Notifier *n, void *opaque) -{ - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - struct rtas_error_log *hdr; - struct rtas_event_log_v6 *v6hdr; - struct rtas_event_log_v6_maina *maina; - struct rtas_event_log_v6_mainb *mainb; - struct rtas_event_log_v6_epow *epow; - struct epow_log_full *new_epow; - - new_epow = g_malloc0(sizeof(*new_epow)); - hdr = &new_epow->hdr; - v6hdr = &new_epow->v6hdr; - maina = &new_epow->maina; - mainb = &new_epow->mainb; - epow = &new_epow->epow; - - hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6 - | RTAS_LOG_SEVERITY_EVENT - | RTAS_LOG_DISPOSITION_NOT_RECOVERED - | RTAS_LOG_OPTIONAL_PART_PRESENT - | RTAS_LOG_TYPE_EPOW); - hdr->extended_length = cpu_to_be32(sizeof(*new_epow) - - sizeof(new_epow->hdr)); - - spapr_init_v6hdr(v6hdr); - spapr_init_maina(maina, 3 /* Main-A, Main-B and EPOW */); - - mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB); - mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb)); - /* FIXME: section version, subtype and creator id? */ - mainb->subsystem_id = 0xa0; /* External environment */ - mainb->event_severity = 0x00; /* Informational / non-error */ - mainb->event_subtype = 0xd0; /* Normal shutdown */ - - epow->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_EPOW); - epow->hdr.section_length = cpu_to_be16(sizeof(*epow)); - epow->hdr.section_version = 2; /* includes extended modifier */ - /* FIXME: section subtype and creator id? */ - epow->sensor_value = RTAS_LOG_V6_EPOW_ACTION_SYSTEM_SHUTDOWN; - epow->event_modifier = RTAS_LOG_V6_EPOW_MODIFIER_NORMAL; - epow->extended_modifier = RTAS_LOG_V6_EPOW_XMODIFIER_PARTITION_SPECIFIC; - - rtas_event_log_queue(RTAS_LOG_TYPE_EPOW, new_epow, true); - - qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq)); -} - -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; - struct rtas_error_log *hdr; - struct rtas_event_log_v6 *v6hdr; - struct rtas_event_log_v6_maina *maina; - struct rtas_event_log_v6_mainb *mainb; - struct rtas_event_log_v6_hp *hp; - - new_hp = g_malloc0(sizeof(struct hp_log_full)); - hdr = &new_hp->hdr; - v6hdr = &new_hp->v6hdr; - maina = &new_hp->maina; - mainb = &new_hp->mainb; - hp = &new_hp->hp; - - hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6 - | RTAS_LOG_SEVERITY_EVENT - | RTAS_LOG_DISPOSITION_NOT_RECOVERED - | RTAS_LOG_OPTIONAL_PART_PRESENT - | RTAS_LOG_INITIATOR_HOTPLUG - | RTAS_LOG_TYPE_HOTPLUG); - hdr->extended_length = cpu_to_be32(sizeof(*new_hp) - - sizeof(new_hp->hdr)); - - spapr_init_v6hdr(v6hdr); - spapr_init_maina(maina, 3 /* Main-A, Main-B, HP */); - - mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB); - mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb)); - mainb->subsystem_id = 0x80; /* External environment */ - mainb->event_severity = 0x00; /* Informational / non-error */ - mainb->event_subtype = 0x00; /* Normal shutdown */ - - hp->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_HOTPLUG); - 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->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 - * that don't support them - */ - g_assert(false); - 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_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(RTAS_LOG_V6_HP_ID_DRC_COUNT, - RTAS_LOG_V6_HP_ACTION_ADD, drc_type, count); -} - -void spapr_hotplug_req_remove_by_count(sPAPRDRConnectorType drc_type, - uint32_t count) -{ - 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, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - uint32_t mask, buf, len, event_len; - uint64_t xinfo; - sPAPREventLogEntry *event; - struct rtas_error_log *hdr; - - if ((nargs < 6) || (nargs > 7) || nret != 1) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - xinfo = rtas_ld(args, 1); - mask = rtas_ld(args, 2); - buf = rtas_ld(args, 4); - len = rtas_ld(args, 5); - if (nargs == 7) { - xinfo |= (uint64_t)rtas_ld(args, 6) << 32; - } - - event = rtas_event_log_dequeue(mask, true); - if (!event) { - goto out_no_events; - } - - hdr = event->data; - event_len = be32_to_cpu(hdr->extended_length) + sizeof(*hdr); - - if (event_len < len) { - len = event_len; - } - - cpu_physical_memory_write(buf, event->data, len); - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - g_free(event->data); - g_free(event); - - /* according to PAPR+, the IRQ must be left asserted, or re-asserted, if - * there are still pending events to be fetched via check-exception. We - * do the latter here, since our code relies on edge-triggered - * interrupts. - */ - if (rtas_event_log_contains(mask, true)) { - qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq)); - } - - return; - -out_no_events: - rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND); -} - -static void event_scan(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - uint32_t mask, buf, len, event_len; - sPAPREventLogEntry *event; - struct rtas_error_log *hdr; - - if (nargs != 4 || nret != 1) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - mask = rtas_ld(args, 0); - buf = rtas_ld(args, 2); - len = rtas_ld(args, 3); - - event = rtas_event_log_dequeue(mask, false); - if (!event) { - goto out_no_events; - } - - hdr = event->data; - event_len = be32_to_cpu(hdr->extended_length) + sizeof(*hdr); - - if (event_len < len) { - len = event_len; - } - - cpu_physical_memory_write(buf, event->data, len); - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - g_free(event->data); - g_free(event); - return; - -out_no_events: - rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND); -} - -void spapr_events_init(sPAPRMachineState *spapr) -{ - QTAILQ_INIT(&spapr->pending_events); - 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", - check_exception); - spapr_rtas_register(RTAS_EVENT_SCAN, "event-scan", event_scan); -} diff --git a/qemu/hw/ppc/spapr_hcall.c b/qemu/hw/ppc/spapr_hcall.c deleted file mode 100644 index 8f40602a5..000000000 --- a/qemu/hw/ppc/spapr_hcall.c +++ /dev/null @@ -1,1117 +0,0 @@ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "sysemu/sysemu.h" -#include "cpu.h" -#include "helper_regs.h" -#include "hw/ppc/spapr.h" -#include "mmu-hash64.h" -#include "cpu-models.h" -#include "trace.h" -#include "kvm_ppc.h" - -struct SPRSyncState { - CPUState *cs; - int spr; - target_ulong value; - target_ulong mask; -}; - -static void do_spr_sync(void *arg) -{ - struct SPRSyncState *s = arg; - PowerPCCPU *cpu = POWERPC_CPU(s->cs); - CPUPPCState *env = &cpu->env; - - cpu_synchronize_state(s->cs); - env->spr[s->spr] &= ~s->mask; - env->spr[s->spr] |= s->value; -} - -static void set_spr(CPUState *cs, int spr, target_ulong value, - target_ulong mask) -{ - struct SPRSyncState s = { - .cs = cs, - .spr = spr, - .value = value, - .mask = mask - }; - run_on_cpu(cs, do_spr_sync, &s); -} - -static bool has_spr(PowerPCCPU *cpu, int spr) -{ - /* 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) -{ - /* - * hash value/pteg group index is normalized by htab_mask - */ - if (((pte_index & ~7ULL) / HPTES_PER_GROUP) & ~env->htab_mask) { - return false; - } - 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) -{ - 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]; - unsigned apshift, spshift; - target_ulong raddr; - target_ulong index; - uint64_t token; - - 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 << apshift) - 1); - - if (is_ram_address(spapr, raddr)) { - /* Regular RAM - should have WIMG=0010 */ - if ((ptel & HPTE64_R_WIMG) != HPTE64_R_M) { - return H_PARAMETER; - } - } else { - /* Looks like an IO address */ - /* FIXME: What WIMG combinations could be sensible for IO? - * For now we allow WIMG=010x, but are there others? */ - /* FIXME: Should we check against registered IO addresses? */ - if ((ptel & (HPTE64_R_W | HPTE64_R_I | HPTE64_R_M)) != HPTE64_R_I) { - return H_PARAMETER; - } - } - - pteh &= ~0x60ULL; - - if (!valid_pte_index(env, pte_index)) { - return H_PARAMETER; - } - - index = 0; - if (likely((flags & H_EXACT) == 0)) { - pte_index &= ~7ULL; - token = ppc_hash64_start_access(cpu, pte_index); - for (; index < 8; index++) { - if (!(ppc_hash64_load_hpte0(cpu, token, index) & HPTE64_V_VALID)) { - break; - } - } - 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(cpu, token, 0) & HPTE64_V_VALID) { - ppc_hash64_stop_access(cpu, token); - return H_PTEG_FULL; - } - ppc_hash64_stop_access(cpu, token); - } - - ppc_hash64_store_hpte(cpu, pte_index + index, - pteh | HPTE64_V_HPTE_DIRTY, ptel); - - args[0] = pte_index + index; - return H_SUCCESS; -} - -typedef enum { - REMOVE_SUCCESS = 0, - REMOVE_NOT_FOUND = 1, - REMOVE_PARM = 2, - REMOVE_HW = 3, -} RemoveResult; - -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; - - if (!valid_pte_index(env, ptex)) { - return REMOVE_PARM; - } - - 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) || - ((flags & H_ANDCOND) && (v & avpn) != 0)) { - return REMOVE_NOT_FOUND; - } - *vp = v; - *rp = r; - 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) -{ - target_ulong flags = args[0]; - target_ulong pte_index = args[1]; - target_ulong avpn = args[2]; - RemoveResult ret; - - ret = remove_hpte(cpu, pte_index, avpn, flags, - &args[0], &args[1]); - - switch (ret) { - case REMOVE_SUCCESS: - return H_SUCCESS; - - case REMOVE_NOT_FOUND: - return H_NOT_FOUND; - - case REMOVE_PARM: - return H_PARAMETER; - - case REMOVE_HW: - return H_HARDWARE; - } - - g_assert_not_reached(); -} - -#define H_BULK_REMOVE_TYPE 0xc000000000000000ULL -#define H_BULK_REMOVE_REQUEST 0x4000000000000000ULL -#define H_BULK_REMOVE_RESPONSE 0x8000000000000000ULL -#define H_BULK_REMOVE_END 0xc000000000000000ULL -#define H_BULK_REMOVE_CODE 0x3000000000000000ULL -#define H_BULK_REMOVE_SUCCESS 0x0000000000000000ULL -#define H_BULK_REMOVE_NOT_FOUND 0x1000000000000000ULL -#define H_BULK_REMOVE_PARM 0x2000000000000000ULL -#define H_BULK_REMOVE_HW 0x3000000000000000ULL -#define H_BULK_REMOVE_RC 0x0c00000000000000ULL -#define H_BULK_REMOVE_FLAGS 0x0300000000000000ULL -#define H_BULK_REMOVE_ABSOLUTE 0x0000000000000000ULL -#define H_BULK_REMOVE_ANDCOND 0x0100000000000000ULL -#define H_BULK_REMOVE_AVPN 0x0200000000000000ULL -#define H_BULK_REMOVE_PTEX 0x00ffffffffffffffULL - -#define H_BULK_REMOVE_MAX_BATCH 4 - -static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - int i; - - for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) { - target_ulong *tsh = &args[i*2]; - target_ulong tsl = args[i*2 + 1]; - target_ulong v, r, ret; - - if ((*tsh & H_BULK_REMOVE_TYPE) == H_BULK_REMOVE_END) { - break; - } else if ((*tsh & H_BULK_REMOVE_TYPE) != H_BULK_REMOVE_REQUEST) { - return H_PARAMETER; - } - - *tsh &= H_BULK_REMOVE_PTEX | H_BULK_REMOVE_FLAGS; - *tsh |= H_BULK_REMOVE_RESPONSE; - - if ((*tsh & H_BULK_REMOVE_ANDCOND) && (*tsh & H_BULK_REMOVE_AVPN)) { - *tsh |= H_BULK_REMOVE_PARM; - return H_PARAMETER; - } - - ret = remove_hpte(cpu, *tsh & H_BULK_REMOVE_PTEX, tsl, - (*tsh & H_BULK_REMOVE_FLAGS) >> 26, - &v, &r); - - *tsh |= ret << 60; - - switch (ret) { - case REMOVE_SUCCESS: - *tsh |= (r & (HPTE64_R_C | HPTE64_R_R)) << 43; - break; - - case REMOVE_PARM: - return H_PARAMETER; - - case REMOVE_HW: - return H_HARDWARE; - } - } - - return H_SUCCESS; -} - -static target_ulong h_protect(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]; - uint64_t token; - 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(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)) { - return H_NOT_FOUND; - } - - r &= ~(HPTE64_R_PP0 | HPTE64_R_PP | HPTE64_R_N | - HPTE64_R_KEY_HI | HPTE64_R_KEY_LO); - 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); - ppc_hash64_store_hpte(cpu, pte_index, - (v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY, 0); - 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(cpu, pte_index, v | HPTE64_V_HPTE_DIRTY, r); - return H_SUCCESS; -} - -static target_ulong h_read(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]; - uint8_t *hpte; - int i, ridx, n_entries = 1; - - if (!valid_pte_index(env, pte_index)) { - return H_PARAMETER; - } - - if (flags & H_READ_4) { - /* Clear the two low order bits */ - pte_index &= ~(3ULL); - n_entries = 4; - } - - hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64); - - for (i = 0, ridx = 0; i < n_entries; i++) { - args[ridx++] = ldq_p(hpte); - args[ridx++] = ldq_p(hpte + (HASH_PTE_SIZE_64/2)); - hpte += HASH_PTE_SIZE_64; - } - - 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) -{ - 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 -#define FLAGS_REGISTER_DTL 0x0000400000000000ULL -#define FLAGS_REGISTER_SLBSHADOW 0x0000600000000000ULL -#define FLAGS_DEREGISTER_VPA 0x0000a00000000000ULL -#define FLAGS_DEREGISTER_DTL 0x0000c00000000000ULL -#define FLAGS_DEREGISTER_SLBSHADOW 0x0000e00000000000ULL - -#define VPA_MIN_SIZE 640 -#define VPA_SIZE_OFFSET 0x4 -#define VPA_SHARED_PROC_OFFSET 0x9 -#define VPA_SHARED_PROC_VAL 0x2 - -static target_ulong register_vpa(CPUPPCState *env, target_ulong vpa) -{ - CPUState *cs = CPU(ppc_env_get_cpu(env)); - uint16_t size; - uint8_t tmp; - - if (vpa == 0) { - hcall_dprintf("Can't cope with registering a VPA at logical 0\n"); - return H_HARDWARE; - } - - if (vpa % env->dcache_line_size) { - return H_PARAMETER; - } - /* FIXME: bounds check the address */ - - size = lduw_be_phys(cs->as, vpa + 0x4); - - if (size < VPA_MIN_SIZE) { - return H_PARAMETER; - } - - /* VPA is not allowed to cross a page boundary */ - if ((vpa / 4096) != ((vpa + size - 1) / 4096)) { - return H_PARAMETER; - } - - env->vpa_addr = vpa; - - tmp = ldub_phys(cs->as, env->vpa_addr + VPA_SHARED_PROC_OFFSET); - tmp |= VPA_SHARED_PROC_VAL; - stb_phys(cs->as, env->vpa_addr + VPA_SHARED_PROC_OFFSET, tmp); - - return H_SUCCESS; -} - -static target_ulong deregister_vpa(CPUPPCState *env, target_ulong vpa) -{ - if (env->slb_shadow_addr) { - return H_RESOURCE; - } - - if (env->dtl_addr) { - return H_RESOURCE; - } - - env->vpa_addr = 0; - return H_SUCCESS; -} - -static target_ulong register_slb_shadow(CPUPPCState *env, target_ulong addr) -{ - CPUState *cs = CPU(ppc_env_get_cpu(env)); - uint32_t size; - - if (addr == 0) { - hcall_dprintf("Can't cope with SLB shadow at logical 0\n"); - return H_HARDWARE; - } - - size = ldl_be_phys(cs->as, addr + 0x4); - if (size < 0x8) { - return H_PARAMETER; - } - - if ((addr / 4096) != ((addr + size - 1) / 4096)) { - return H_PARAMETER; - } - - if (!env->vpa_addr) { - return H_RESOURCE; - } - - env->slb_shadow_addr = addr; - env->slb_shadow_size = size; - - return H_SUCCESS; -} - -static target_ulong deregister_slb_shadow(CPUPPCState *env, target_ulong addr) -{ - env->slb_shadow_addr = 0; - env->slb_shadow_size = 0; - return H_SUCCESS; -} - -static target_ulong register_dtl(CPUPPCState *env, target_ulong addr) -{ - CPUState *cs = CPU(ppc_env_get_cpu(env)); - uint32_t size; - - if (addr == 0) { - hcall_dprintf("Can't cope with DTL at logical 0\n"); - return H_HARDWARE; - } - - size = ldl_be_phys(cs->as, addr + 0x4); - - if (size < 48) { - return H_PARAMETER; - } - - if (!env->vpa_addr) { - return H_RESOURCE; - } - - env->dtl_addr = addr; - env->dtl_size = size; - - return H_SUCCESS; -} - -static target_ulong deregister_dtl(CPUPPCState *env, target_ulong addr) -{ - env->dtl_addr = 0; - env->dtl_size = 0; - - return H_SUCCESS; -} - -static target_ulong h_register_vpa(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong flags = args[0]; - target_ulong procno = args[1]; - target_ulong vpa = args[2]; - target_ulong ret = H_PARAMETER; - CPUPPCState *tenv; - PowerPCCPU *tcpu; - - tcpu = ppc_get_vcpu_by_dt_id(procno); - if (!tcpu) { - return H_PARAMETER; - } - tenv = &tcpu->env; - - switch (flags) { - case FLAGS_REGISTER_VPA: - ret = register_vpa(tenv, vpa); - break; - - case FLAGS_DEREGISTER_VPA: - ret = deregister_vpa(tenv, vpa); - break; - - case FLAGS_REGISTER_SLBSHADOW: - ret = register_slb_shadow(tenv, vpa); - break; - - case FLAGS_DEREGISTER_SLBSHADOW: - ret = deregister_slb_shadow(tenv, vpa); - break; - - case FLAGS_REGISTER_DTL: - ret = register_dtl(tenv, vpa); - break; - - case FLAGS_DEREGISTER_DTL: - ret = deregister_dtl(tenv, vpa); - break; - } - - return ret; -} - -static target_ulong h_cede(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUPPCState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - env->msr |= (1ULL << MSR_EE); - hreg_compute_hflags(env); - if (!cpu_has_work(cs)) { - cs->halted = 1; - cs->exception_index = EXCP_HLT; - cs->exit_request = 1; - } - return H_SUCCESS; -} - -static target_ulong h_rtas(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong rtas_r3 = args[0]; - uint32_t token = rtas_ld(rtas_r3, 0); - uint32_t nargs = rtas_ld(rtas_r3, 1); - uint32_t nret = rtas_ld(rtas_r3, 2); - - return spapr_rtas_call(cpu, spapr, token, nargs, rtas_r3 + 12, - nret, rtas_r3 + 12 + 4*nargs); -} - -static target_ulong h_logical_load(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUState *cs = CPU(cpu); - target_ulong size = args[0]; - target_ulong addr = args[1]; - - switch (size) { - case 1: - args[0] = ldub_phys(cs->as, addr); - return H_SUCCESS; - case 2: - args[0] = lduw_phys(cs->as, addr); - return H_SUCCESS; - case 4: - args[0] = ldl_phys(cs->as, addr); - return H_SUCCESS; - case 8: - args[0] = ldq_phys(cs->as, addr); - return H_SUCCESS; - } - return H_PARAMETER; -} - -static target_ulong h_logical_store(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUState *cs = CPU(cpu); - - target_ulong size = args[0]; - target_ulong addr = args[1]; - target_ulong val = args[2]; - - switch (size) { - case 1: - stb_phys(cs->as, addr, val); - return H_SUCCESS; - case 2: - stw_phys(cs->as, addr, val); - return H_SUCCESS; - case 4: - stl_phys(cs->as, addr, val); - return H_SUCCESS; - case 8: - stq_phys(cs->as, addr, val); - return H_SUCCESS; - } - return H_PARAMETER; -} - -static target_ulong h_logical_memop(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUState *cs = CPU(cpu); - - target_ulong dst = args[0]; /* Destination address */ - target_ulong src = args[1]; /* Source address */ - target_ulong esize = args[2]; /* Element size (0=1,1=2,2=4,3=8) */ - target_ulong count = args[3]; /* Element count */ - target_ulong op = args[4]; /* 0 = copy, 1 = invert */ - uint64_t tmp; - unsigned int mask = (1 << esize) - 1; - int step = 1 << esize; - - if (count > 0x80000000) { - return H_PARAMETER; - } - - if ((dst & mask) || (src & mask) || (op > 1)) { - return H_PARAMETER; - } - - if (dst >= src && dst < (src + (count << esize))) { - dst = dst + ((count - 1) << esize); - src = src + ((count - 1) << esize); - step = -step; - } - - while (count--) { - switch (esize) { - case 0: - tmp = ldub_phys(cs->as, src); - break; - case 1: - tmp = lduw_phys(cs->as, src); - break; - case 2: - tmp = ldl_phys(cs->as, src); - break; - case 3: - tmp = ldq_phys(cs->as, src); - break; - default: - return H_PARAMETER; - } - if (op == 1) { - tmp = ~tmp; - } - switch (esize) { - case 0: - stb_phys(cs->as, dst, tmp); - break; - case 1: - stw_phys(cs->as, dst, tmp); - break; - case 2: - stl_phys(cs->as, dst, tmp); - break; - case 3: - stq_phys(cs->as, dst, tmp); - break; - } - dst = dst + step; - src = src + step; - } - - return H_SUCCESS; -} - -static target_ulong h_logical_icbi(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - /* Nothing to do on emulation, KVM will trap this in the kernel */ - return H_SUCCESS; -} - -static target_ulong h_logical_dcbf(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - /* Nothing to do on emulation, KVM will trap this in the kernel */ - return H_SUCCESS; -} - -static target_ulong h_set_mode_resource_le(PowerPCCPU *cpu, - target_ulong mflags, - target_ulong value1, - target_ulong value2) -{ - CPUState *cs; - - if (value1) { - return H_P3; - } - if (value2) { - return H_P4; - } - - switch (mflags) { - case H_SET_MODE_ENDIAN_BIG: - CPU_FOREACH(cs) { - set_spr(cs, SPR_LPCR, 0, LPCR_ILE); - } - spapr_pci_switch_vga(true); - return H_SUCCESS; - - case H_SET_MODE_ENDIAN_LITTLE: - CPU_FOREACH(cs) { - set_spr(cs, SPR_LPCR, LPCR_ILE, LPCR_ILE); - } - spapr_pci_switch_vga(false); - return H_SUCCESS; - } - - return H_UNSUPPORTED_FLAG; -} - -static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu, - target_ulong mflags, - target_ulong value1, - target_ulong value2) -{ - CPUState *cs; - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - - if (!(pcc->insns_flags2 & PPC2_ISA207S)) { - return H_P2; - } - if (value1) { - return H_P3; - } - if (value2) { - return H_P4; - } - - if (mflags == AIL_RESERVED) { - return H_UNSUPPORTED_FLAG; - } - - CPU_FOREACH(cs) { - set_spr(cs, SPR_LPCR, mflags << LPCR_AIL_SHIFT, LPCR_AIL); - } - - return H_SUCCESS; -} - -static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong resource = args[1]; - target_ulong ret = H_P2; - - switch (resource) { - case H_SET_MODE_RESOURCE_LE: - ret = h_set_mode_resource_le(cpu, args[0], args[2], args[3]); - break; - case H_SET_MODE_RESOURCE_ADDR_TRANS_MODE: - ret = h_set_mode_resource_addr_trans_mode(cpu, args[0], - args[2], args[3]); - break; - } - - 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; - Error *err; -} SetCompatState; - -static void do_set_compat(void *arg) -{ - SetCompatState *s = arg; - - cpu_synchronize_state(CPU(s->cpu)); - ppc_set_compat(s->cpu, s->cpu_version, &s->err); -} - -#define get_compat_level(cpuver) ( \ - ((cpuver) == CPU_POWERPC_LOGICAL_2_05) ? 2050 : \ - ((cpuver) == CPU_POWERPC_LOGICAL_2_06) ? 2060 : \ - ((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 = ppc64_phys_to_real(args[0]); - target_ulong ov_table, ov5; - PowerPCCPUClass *pcc_ = POWERPC_CPU_GET_CLASS(cpu_); - CPUState *cs; - 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 = ldl_be_phys(&address_space_memory, list); - list += 4; - pvr = ldl_be_phys(&address_space_memory, list); - list += 4; - - trace_spapr_cas_pvr_try(pvr); - if (!max_lvl && - ((cpu_->env.spr[SPR_PVR] & pvr_mask) == (pvr & pvr_mask))) { - cpu_match = true; - cpu_version = 0; - } else if (pvr == cpu_->cpu_version) { - cpu_match = true; - cpu_version = cpu_->cpu_version; - } else if (!cpu_match) { - /* If it is a logical PVR, try to determine the highest level */ - unsigned lvl = get_compat_level(pvr); - if (lvl) { - bool is205 = (pcc_->pcr_mask & PCR_COMPAT_2_05) && - (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_05)); - bool is206 = (pcc_->pcr_mask & PCR_COMPAT_2_06) && - ((lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06)) || - (lvl == get_compat_level(CPU_POWERPC_LOGICAL_2_06_PLUS))); - - if (is205 || is206) { - if (!max_lvl) { - /* User did not set the level, choose the highest */ - if (compat_lvl <= lvl) { - compat_lvl = lvl; - cpu_version = pvr; - } - } else if (max_lvl >= lvl) { - /* User chose the level, don't set higher than this */ - compat_lvl = lvl; - cpu_version = pvr; - } - } - } - } - /* Terminator record */ - if (~pvr_mask & pvr) { - break; - } - } - - /* Parsing finished */ - trace_spapr_cas_pvr(cpu_->cpu_version, cpu_match, - cpu_version, pcc_->pcr_mask); - - /* Update CPUs */ - if (old_cpu_version != cpu_version) { - CPU_FOREACH(cs) { - SetCompatState s = { - .cpu = POWERPC_CPU(cs), - .cpu_version = cpu_version, - .err = NULL, - }; - - run_on_cpu(cs, do_set_compat, &s); - - if (s.err) { - error_report_err(s.err); - return H_HARDWARE; - } - } - } - - if (!cpu_version) { - cpu_update = false; - } - - /* 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; - } - - /* @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(); - } - - return H_SUCCESS; -} - -static spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1]; -static spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE + 1]; - -void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn) -{ - spapr_hcall_fn *slot; - - if (opcode <= MAX_HCALL_OPCODE) { - assert((opcode & 0x3) == 0); - - slot = &papr_hypercall_table[opcode / 4]; - } else { - assert((opcode >= KVMPPC_HCALL_BASE) && (opcode <= KVMPPC_HCALL_MAX)); - - slot = &kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE]; - } - - assert(!(*slot)); - *slot = fn; -} - -target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode, - target_ulong *args) -{ - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - - if ((opcode <= MAX_HCALL_OPCODE) - && ((opcode & 0x3) == 0)) { - spapr_hcall_fn fn = papr_hypercall_table[opcode / 4]; - - if (fn) { - return fn(cpu, spapr, opcode, args); - } - } else if ((opcode >= KVMPPC_HCALL_BASE) && - (opcode <= KVMPPC_HCALL_MAX)) { - spapr_hcall_fn fn = kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE]; - - if (fn) { - return fn(cpu, spapr, opcode, args); - } - } - - qemu_log_mask(LOG_UNIMP, "Unimplemented SPAPR hcall 0x" TARGET_FMT_lx "\n", - opcode); - return H_FUNCTION; -} - -static void hypercall_register_types(void) -{ - /* hcall-pft */ - spapr_register_hypercall(H_ENTER, h_enter); - spapr_register_hypercall(H_REMOVE, h_remove); - spapr_register_hypercall(H_PROTECT, h_protect); - spapr_register_hypercall(H_READ, h_read); - - /* hcall-bulk */ - spapr_register_hypercall(H_BULK_REMOVE, h_bulk_remove); - - /* 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 - * enforce the attributes more strongly - */ - spapr_register_hypercall(H_LOGICAL_CI_LOAD, h_logical_load); - spapr_register_hypercall(H_LOGICAL_CI_STORE, h_logical_store); - spapr_register_hypercall(H_LOGICAL_CACHE_LOAD, h_logical_load); - spapr_register_hypercall(H_LOGICAL_CACHE_STORE, h_logical_store); - spapr_register_hypercall(H_LOGICAL_ICBI, h_logical_icbi); - spapr_register_hypercall(H_LOGICAL_DCBF, h_logical_dcbf); - spapr_register_hypercall(KVMPPC_H_LOGICAL_MEMOP, h_logical_memop); - - /* qemu/KVM-PPC specific hcalls */ - spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas); - - /* ibm,client-architecture-support support */ - spapr_register_hypercall(KVMPPC_H_CAS, h_client_architecture_support); -} - -type_init(hypercall_register_types) diff --git a/qemu/hw/ppc/spapr_iommu.c b/qemu/hw/ppc/spapr_iommu.c deleted file mode 100644 index 7dd458846..000000000 --- a/qemu/hw/ppc/spapr_iommu.c +++ /dev/null @@ -1,512 +0,0 @@ -/* - * QEMU sPAPR IOMMU (TCE) code - * - * Copyright (c) 2010 David Gibson, IBM Corporation - * - * 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 . - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "sysemu/kvm.h" -#include "hw/qdev.h" -#include "kvm_ppc.h" -#include "sysemu/dma.h" -#include "exec/address-spaces.h" -#include "trace.h" - -#include "hw/ppc/spapr.h" -#include "hw/ppc/spapr_vio.h" - -#include - -enum sPAPRTCEAccess { - SPAPR_TCE_FAULT = 0, - SPAPR_TCE_RO = 1, - SPAPR_TCE_WO = 2, - SPAPR_TCE_RW = 3, -}; - -#define IOMMU_PAGE_SIZE(shift) (1ULL << (shift)) -#define IOMMU_PAGE_MASK(shift) (~(IOMMU_PAGE_SIZE(shift) - 1)) - -static QLIST_HEAD(spapr_tce_tables, sPAPRTCETable) spapr_tce_tables; - -sPAPRTCETable *spapr_tce_find_by_liobn(target_ulong liobn) -{ - sPAPRTCETable *tcet; - - if (liobn & 0xFFFFFFFF00000000ULL) { - hcall_dprintf("Request for out-of-bounds LIOBN 0x" TARGET_FMT_lx "\n", - liobn); - return NULL; - } - - QLIST_FOREACH(tcet, &spapr_tce_tables, list) { - if (tcet->liobn == (uint32_t)liobn) { - return tcet; - } - } - - return NULL; -} - -static IOMMUAccessFlags spapr_tce_iommu_access_flags(uint64_t tce) -{ - switch (tce & SPAPR_TCE_RW) { - case SPAPR_TCE_FAULT: - return IOMMU_NONE; - case SPAPR_TCE_RO: - return IOMMU_RO; - case SPAPR_TCE_WO: - return IOMMU_WO; - default: /* SPAPR_TCE_RW */ - return IOMMU_RW; - } -} - -/* Called from RCU critical section */ -static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr, - bool is_write) -{ - sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu); - uint64_t tce; - IOMMUTLBEntry ret = { - .target_as = &address_space_memory, - .iova = 0, - .translated_addr = 0, - .addr_mask = ~(hwaddr)0, - .perm = IOMMU_NONE, - }; - - if ((addr >> tcet->page_shift) < tcet->nb_table) { - /* Check if we are in bound */ - hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift); - - tce = tcet->table[addr >> tcet->page_shift]; - ret.iova = addr & page_mask; - ret.translated_addr = tce & page_mask; - ret.addr_mask = ~page_mask; - ret.perm = spapr_tce_iommu_access_flags(tce); - } - trace_spapr_iommu_xlate(tcet->liobn, addr, ret.iova, ret.perm, - ret.addr_mask); - - return ret; -} - -static int spapr_tce_table_post_load(void *opaque, int version_id) -{ - sPAPRTCETable *tcet = SPAPR_TCE_TABLE(opaque); - - if (tcet->vdev) { - spapr_vio_set_bypass(tcet->vdev, tcet->bypass); - } - - return 0; -} - -static const VMStateDescription vmstate_spapr_tce_table = { - .name = "spapr_iommu", - .version_id = 2, - .minimum_version_id = 2, - .post_load = spapr_tce_table_post_load, - .fields = (VMStateField []) { - /* Sanity check */ - VMSTATE_UINT32_EQUAL(liobn, sPAPRTCETable), - VMSTATE_UINT32_EQUAL(nb_table, sPAPRTCETable), - - /* IOMMU state */ - VMSTATE_BOOL(bypass, sPAPRTCETable), - VMSTATE_VARRAY_UINT32(table, sPAPRTCETable, nb_table, 0, vmstate_info_uint64, uint64_t), - - VMSTATE_END_OF_LIST() - }, -}; - -static MemoryRegionIOMMUOps spapr_iommu_ops = { - .translate = spapr_tce_translate_iommu, -}; - -static int spapr_tce_table_realize(DeviceState *dev) -{ - sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); - uint64_t window_size = (uint64_t)tcet->nb_table << tcet->page_shift; - - if (kvm_enabled() && !(window_size >> 32)) { - tcet->table = kvmppc_create_spapr_tce(tcet->liobn, - window_size, - &tcet->fd, - tcet->need_vfio); - } - - if (!tcet->table) { - size_t table_size = tcet->nb_table * sizeof(uint64_t); - tcet->table = g_malloc0(table_size); - } - - trace_spapr_iommu_new_table(tcet->liobn, tcet, tcet->table, tcet->fd); - - memory_region_init_iommu(&tcet->iommu, OBJECT(dev), &spapr_iommu_ops, - "iommu-spapr", - (uint64_t)tcet->nb_table << tcet->page_shift); - - QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list); - - vmstate_register(DEVICE(tcet), tcet->liobn, &vmstate_spapr_tce_table, - tcet); - - 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 need_vfio) -{ - sPAPRTCETable *tcet; - char tmp[64]; - - if (spapr_tce_find_by_liobn(liobn)) { - fprintf(stderr, "Attempted to create TCE table with duplicate" - " LIOBN 0x%x\n", liobn); - return NULL; - } - - if (!nb_table) { - return NULL; - } - - tcet = SPAPR_TCE_TABLE(object_new(TYPE_SPAPR_TCE_TABLE)); - tcet->liobn = liobn; - tcet->bus_offset = bus_offset; - tcet->page_shift = page_shift; - tcet->nb_table = nb_table; - tcet->need_vfio = need_vfio; - - snprintf(tmp, sizeof(tmp), "tce-table-%x", liobn); - object_property_add_child(OBJECT(owner), tmp, OBJECT(tcet), NULL); - - object_property_set_bool(OBJECT(tcet), true, "realized", NULL); - - return tcet; -} - -static void spapr_tce_table_unrealize(DeviceState *dev, Error **errp) -{ - sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); - - QLIST_REMOVE(tcet, list); - - if (!kvm_enabled() || - (kvmppc_remove_spapr_tce(tcet->table, tcet->fd, - tcet->nb_table) != 0)) { - g_free(tcet->table); - } -} - -MemoryRegion *spapr_tce_get_iommu(sPAPRTCETable *tcet) -{ - return &tcet->iommu; -} - -static void spapr_tce_reset(DeviceState *dev) -{ - sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); - size_t table_size = tcet->nb_table * sizeof(uint64_t); - - memset(tcet->table, 0, table_size); -} - -static target_ulong put_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, - target_ulong tce) -{ - IOMMUTLBEntry entry; - hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift); - unsigned long index = (ioba - tcet->bus_offset) >> tcet->page_shift; - - if (index >= tcet->nb_table) { - hcall_dprintf("spapr_vio_put_tce on out-of-bounds IOBA 0x" - TARGET_FMT_lx "\n", ioba); - return H_PARAMETER; - } - - tcet->table[index] = tce; - - entry.target_as = &address_space_memory, - entry.iova = ioba & page_mask; - entry.translated_addr = tce & page_mask; - entry.addr_mask = ~page_mask; - entry.perm = spapr_tce_iommu_access_flags(tce); - memory_region_notify_iommu(&tcet->iommu, entry); - - return H_SUCCESS; -} - -static target_ulong h_put_tce_indirect(PowerPCCPU *cpu, - sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - int i; - target_ulong liobn = args[0]; - target_ulong ioba = args[1]; - target_ulong ioba1 = ioba; - target_ulong tce_list = args[2]; - target_ulong npages = args[3]; - target_ulong ret = H_PARAMETER, tce = 0; - sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); - CPUState *cs = CPU(cpu); - hwaddr page_mask, page_size; - - if (!tcet) { - return H_PARAMETER; - } - - if ((npages > 512) || (tce_list & SPAPR_TCE_PAGE_MASK)) { - return H_PARAMETER; - } - - page_mask = IOMMU_PAGE_MASK(tcet->page_shift); - page_size = IOMMU_PAGE_SIZE(tcet->page_shift); - ioba &= page_mask; - - for (i = 0; i < npages; ++i, ioba += page_size) { - tce = ldq_be_phys(cs->as, tce_list + i * sizeof(target_ulong)); - - ret = put_tce_emu(tcet, ioba, tce); - if (ret) { - break; - } - } - - /* Trace last successful or the first problematic entry */ - i = i ? (i - 1) : 0; - if (SPAPR_IS_PCI_LIOBN(liobn)) { - trace_spapr_iommu_pci_indirect(liobn, ioba1, tce_list, i, tce, ret); - } else { - trace_spapr_iommu_indirect(liobn, ioba1, tce_list, i, tce, ret); - } - return ret; -} - -static target_ulong h_stuff_tce(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - int i; - target_ulong liobn = args[0]; - target_ulong ioba = args[1]; - target_ulong tce_value = args[2]; - target_ulong npages = args[3]; - target_ulong ret = H_PARAMETER; - sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); - hwaddr page_mask, page_size; - - if (!tcet) { - return H_PARAMETER; - } - - if (npages > tcet->nb_table) { - return H_PARAMETER; - } - - page_mask = IOMMU_PAGE_MASK(tcet->page_shift); - page_size = IOMMU_PAGE_SIZE(tcet->page_shift); - ioba &= page_mask; - - for (i = 0; i < npages; ++i, ioba += page_size) { - ret = put_tce_emu(tcet, ioba, tce_value); - if (ret) { - break; - } - } - if (SPAPR_IS_PCI_LIOBN(liobn)) { - trace_spapr_iommu_pci_stuff(liobn, ioba, tce_value, npages, ret); - } else { - trace_spapr_iommu_stuff(liobn, ioba, tce_value, npages, ret); - } - - return ret; -} - -static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong liobn = args[0]; - target_ulong ioba = args[1]; - target_ulong tce = args[2]; - target_ulong ret = H_PARAMETER; - sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); - - if (tcet) { - hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift); - - ioba &= page_mask; - - ret = put_tce_emu(tcet, ioba, tce); - } - if (SPAPR_IS_PCI_LIOBN(liobn)) { - trace_spapr_iommu_pci_put(liobn, ioba, tce, ret); - } else { - trace_spapr_iommu_put(liobn, ioba, tce, ret); - } - - return ret; -} - -static target_ulong get_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, - target_ulong *tce) -{ - unsigned long index = (ioba - tcet->bus_offset) >> tcet->page_shift; - - if (index >= tcet->nb_table) { - hcall_dprintf("spapr_iommu_get_tce on out-of-bounds IOBA 0x" - TARGET_FMT_lx "\n", ioba); - return H_PARAMETER; - } - - *tce = tcet->table[index]; - - return H_SUCCESS; -} - -static target_ulong h_get_tce(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong liobn = args[0]; - target_ulong ioba = args[1]; - target_ulong tce = 0; - target_ulong ret = H_PARAMETER; - sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); - - if (tcet) { - hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift); - - ioba &= page_mask; - - ret = get_tce_emu(tcet, ioba, &tce); - if (!ret) { - args[0] = tce; - } - } - if (SPAPR_IS_PCI_LIOBN(liobn)) { - trace_spapr_iommu_pci_get(liobn, ioba, ret, tce); - } else { - trace_spapr_iommu_get(liobn, ioba, ret, tce); - } - - return ret; -} - -int spapr_dma_dt(void *fdt, int node_off, const char *propname, - uint32_t liobn, uint64_t window, uint32_t size) -{ - uint32_t dma_prop[5]; - int ret; - - dma_prop[0] = cpu_to_be32(liobn); - dma_prop[1] = cpu_to_be32(window >> 32); - dma_prop[2] = cpu_to_be32(window & 0xFFFFFFFF); - dma_prop[3] = 0; /* window size is 32 bits */ - dma_prop[4] = cpu_to_be32(size); - - ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-address-cells", 2); - if (ret < 0) { - return ret; - } - - ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-size-cells", 2); - if (ret < 0) { - return ret; - } - - ret = fdt_setprop(fdt, node_off, propname, dma_prop, sizeof(dma_prop)); - if (ret < 0) { - return ret; - } - - return 0; -} - -int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname, - sPAPRTCETable *tcet) -{ - if (!tcet) { - return 0; - } - - return spapr_dma_dt(fdt, node_off, propname, - tcet->liobn, 0, tcet->nb_table << tcet->page_shift); -} - -static void spapr_tce_table_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - dc->init = spapr_tce_table_realize; - dc->reset = spapr_tce_reset; - dc->unrealize = spapr_tce_table_unrealize; - - QLIST_INIT(&spapr_tce_tables); - - /* hcall-tce */ - spapr_register_hypercall(H_PUT_TCE, h_put_tce); - spapr_register_hypercall(H_GET_TCE, h_get_tce); - spapr_register_hypercall(H_PUT_TCE_INDIRECT, h_put_tce_indirect); - spapr_register_hypercall(H_STUFF_TCE, h_stuff_tce); -} - -static TypeInfo spapr_tce_table_info = { - .name = TYPE_SPAPR_TCE_TABLE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(sPAPRTCETable), - .class_init = spapr_tce_table_class_init, -}; - -static void register_types(void) -{ - type_register_static(&spapr_tce_table_info); -} - -type_init(register_types); diff --git a/qemu/hw/ppc/spapr_pci.c b/qemu/hw/ppc/spapr_pci.c deleted file mode 100644 index 573e635bf..000000000 --- a/qemu/hw/ppc/spapr_pci.c +++ /dev/null @@ -1,1919 +0,0 @@ -/* - * QEMU sPAPR PCI host originated from Uninorth PCI host - * - * Copyright (c) 2011 Alexey Kardashevskiy, IBM Corporation. - * Copyright (C) 2011 David Gibson, IBM Corporation. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "hw/pci/pci.h" -#include "hw/pci/msi.h" -#include "hw/pci/msix.h" -#include "hw/pci/pci_host.h" -#include "hw/ppc/spapr.h" -#include "hw/pci-host/spapr.h" -#include "exec/address-spaces.h" -#include -#include "trace.h" -#include "qemu/error-report.h" -#include "qapi/qmp/qerror.h" - -#include "hw/pci/pci_bridge.h" -#include "hw/pci/pci_bus.h" -#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 -#define RTAS_RESET_FN 2 -#define RTAS_CHANGE_MSI_FN 3 -#define RTAS_CHANGE_MSIX_FN 4 - -/* Interrupt types to return on RTAS_CHANGE_* */ -#define RTAS_TYPE_MSI 1 -#define RTAS_TYPE_MSIX 2 - -#define FDT_NAME_MAX 128 - -#define _FDT(exp) \ - do { \ - int ret = (exp); \ - if (ret < 0) { \ - return ret; \ - } \ - } while (0) - -sPAPRPHBState *spapr_pci_find_phb(sPAPRMachineState *spapr, uint64_t buid) -{ - sPAPRPHBState *sphb; - - QLIST_FOREACH(sphb, &spapr->phbs, list) { - if (sphb->buid != buid) { - continue; - } - return sphb; - } - - return NULL; -} - -PCIDevice *spapr_pci_find_dev(sPAPRMachineState *spapr, uint64_t buid, - uint32_t config_addr) -{ - sPAPRPHBState *sphb = spapr_pci_find_phb(spapr, buid); - PCIHostState *phb = PCI_HOST_BRIDGE(sphb); - int bus_num = (config_addr >> 16) & 0xFF; - int devfn = (config_addr >> 8) & 0xFF; - - if (!phb) { - return NULL; - } - - return pci_find_device(phb->bus, bus_num, devfn); -} - -static uint32_t rtas_pci_cfgaddr(uint32_t arg) -{ - /* This handles the encoding of extended config space addresses */ - return ((arg >> 20) & 0xf00) | (arg & 0xff); -} - -static void finish_read_pci_config(sPAPRMachineState *spapr, uint64_t buid, - uint32_t addr, uint32_t size, - target_ulong rets) -{ - PCIDevice *pci_dev; - uint32_t val; - - if ((size != 1) && (size != 2) && (size != 4)) { - /* access must be 1, 2 or 4 bytes */ - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - - pci_dev = spapr_pci_find_dev(spapr, buid, addr); - addr = rtas_pci_cfgaddr(addr); - - if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) { - /* Access must be to a valid device, within bounds and - * naturally aligned */ - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - - val = pci_host_config_read_common(pci_dev, addr, - pci_config_size(pci_dev), size); - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - rtas_st(rets, 1, val); -} - -static void rtas_ibm_read_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - uint64_t buid; - uint32_t size, addr; - - if ((nargs != 4) || (nret != 2)) { - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - - buid = rtas_ldq(args, 1); - size = rtas_ld(args, 3); - addr = rtas_ld(args, 0); - - finish_read_pci_config(spapr, buid, addr, size, rets); -} - -static void rtas_read_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - uint32_t size, addr; - - if ((nargs != 2) || (nret != 2)) { - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - - size = rtas_ld(args, 1); - addr = rtas_ld(args, 0); - - finish_read_pci_config(spapr, 0, addr, size, rets); -} - -static void finish_write_pci_config(sPAPRMachineState *spapr, uint64_t buid, - uint32_t addr, uint32_t size, - uint32_t val, target_ulong rets) -{ - PCIDevice *pci_dev; - - if ((size != 1) && (size != 2) && (size != 4)) { - /* access must be 1, 2 or 4 bytes */ - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - - pci_dev = spapr_pci_find_dev(spapr, buid, addr); - addr = rtas_pci_cfgaddr(addr); - - if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) { - /* Access must be to a valid device, within bounds and - * naturally aligned */ - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - - pci_host_config_write_common(pci_dev, addr, pci_config_size(pci_dev), - val, size); - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); -} - -static void rtas_ibm_write_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - uint64_t buid; - uint32_t val, size, addr; - - if ((nargs != 5) || (nret != 1)) { - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - - buid = rtas_ldq(args, 1); - val = rtas_ld(args, 4); - size = rtas_ld(args, 3); - addr = rtas_ld(args, 0); - - finish_write_pci_config(spapr, buid, addr, size, val, rets); -} - -static void rtas_write_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - uint32_t val, size, addr; - - if ((nargs != 3) || (nret != 1)) { - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - - - val = rtas_ld(args, 2); - size = rtas_ld(args, 1); - addr = rtas_ld(args, 0); - - finish_write_pci_config(spapr, 0, addr, size, val, rets); -} - -/* - * Set MSI/MSIX message data. - * This is required for msi_notify()/msix_notify() which - * will write at the addresses via spapr_msi_write(). - * - * If hwaddr == 0, all entries will have .data == first_irq i.e. - * table will be reset. - */ -static void spapr_msi_setmsg(PCIDevice *pdev, hwaddr addr, bool msix, - unsigned first_irq, unsigned req_num) -{ - unsigned i; - MSIMessage msg = { .address = addr, .data = first_irq }; - - if (!msix) { - msi_set_message(pdev, msg); - trace_spapr_pci_msi_setup(pdev->name, 0, msg.address); - return; - } - - for (i = 0; i < req_num; ++i) { - msix_set_message(pdev, i, msg); - trace_spapr_pci_msi_setup(pdev->name, i, msg.address); - if (addr) { - ++msg.data; - } - } -} - -static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, uint32_t nret, - target_ulong rets) -{ - uint32_t config_addr = rtas_ld(args, 0); - 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; - sPAPRPHBState *phb = NULL; - PCIDevice *pdev = NULL; - spapr_pci_msi *msi; - int *config_addr_key; - Error *err = NULL; - - switch (func) { - case RTAS_CHANGE_MSI_FN: - case RTAS_CHANGE_FN: - ret_intr_type = RTAS_TYPE_MSI; - break; - case RTAS_CHANGE_MSIX_FN: - ret_intr_type = RTAS_TYPE_MSIX; - break; - default: - error_report("rtas_ibm_change_msi(%u) is not implemented", func); - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - /* Fins sPAPRPHBState */ - phb = spapr_pci_find_phb(spapr, buid); - if (phb) { - pdev = spapr_pci_find_dev(spapr, buid, config_addr); - } - if (!phb || !pdev) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - msi = (spapr_pci_msi *) g_hash_table_lookup(phb->msi, &config_addr); - - /* Releasing MSIs */ - if (!req_num) { - if (!msi) { - trace_spapr_pci_msi("Releasing wrong config", config_addr); - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - - xics_free(spapr->icp, msi->first_irq, msi->num); - if (msi_present(pdev)) { - spapr_msi_setmsg(pdev, 0, false, 0, 0); - } - if (msix_present(pdev)) { - spapr_msi_setmsg(pdev, 0, true, 0, 0); - } - g_hash_table_remove(phb->msi, &config_addr); - - trace_spapr_pci_msi("Released MSIs", config_addr); - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - rtas_st(rets, 1, 0); - return; - } - - /* Enabling MSI */ - - /* Check if the device supports as many IRQs as requested */ - if (ret_intr_type == RTAS_TYPE_MSI) { - max_irqs = msi_nr_vectors_allocated(pdev); - } else if (ret_intr_type == RTAS_TYPE_MSIX) { - max_irqs = pdev->msix_entries_nr; - } - if (!max_irqs) { - error_report("Requested interrupt type %d is not enabled for device %x", - ret_intr_type, config_addr); - rtas_st(rets, 0, -1); /* Hardware error */ - return; - } - /* Correct the number if the guest asked for too many */ - if (req_num > max_irqs) { - trace_spapr_pci_msi_retry(config_addr, req_num, max_irqs); - req_num = max_irqs; - irq = 0; /* to avoid misleading trace */ - goto out; - } - - /* Allocate MSIs */ - irq = xics_alloc_block(spapr->icp, 0, req_num, false, - 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); - - /* Add MSI device to cache */ - msi = g_new(spapr_pci_msi, 1); - msi->first_irq = irq; - msi->num = req_num; - config_addr_key = g_new(int, 1); - *config_addr_key = config_addr; - g_hash_table_insert(phb->msi, config_addr_key, msi); - -out: - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - rtas_st(rets, 1, req_num); - rtas_st(rets, 2, ++seq_num); - if (nret > 3) { - rtas_st(rets, 3, ret_intr_type); - } - - trace_spapr_pci_rtas_ibm_change_msi(config_addr, func, req_num, irq); -} - -static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu, - sPAPRMachineState *spapr, - uint32_t token, - uint32_t nargs, - target_ulong args, - uint32_t nret, - target_ulong rets) -{ - uint32_t config_addr = rtas_ld(args, 0); - 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; - spapr_pci_msi *msi; - - /* Find sPAPRPHBState */ - phb = spapr_pci_find_phb(spapr, buid); - if (phb) { - pdev = spapr_pci_find_dev(spapr, buid, config_addr); - } - if (!phb || !pdev) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - /* Find device descriptor and start IRQ */ - msi = (spapr_pci_msi *) g_hash_table_lookup(phb->msi, &config_addr); - if (!msi || !msi->first_irq || !msi->num || (ioa_intr_num >= msi->num)) { - trace_spapr_pci_msi("Failed to return vector", config_addr); - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - intr_src_num = msi->first_irq + ioa_intr_num; - trace_spapr_pci_rtas_ibm_query_interrupt_source_number(ioa_intr_num, - intr_src_num); - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - rtas_st(rets, 1, intr_src_num); - rtas_st(rets, 2, 1);/* 0 == level; 1 == edge */ -} - -static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu, - sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, uint32_t nret, - target_ulong rets) -{ - sPAPRPHBState *sphb; - uint32_t addr, option; - uint64_t buid; - int ret; - - if ((nargs != 4) || (nret != 1)) { - goto param_error_exit; - } - - buid = rtas_ldq(args, 1); - addr = rtas_ld(args, 0); - option = rtas_ld(args, 3); - - sphb = spapr_pci_find_phb(spapr, buid); - if (!sphb) { - goto param_error_exit; - } - - if (!spapr_phb_eeh_available(sphb)) { - goto param_error_exit; - } - - ret = spapr_phb_vfio_eeh_set_option(sphb, addr, option); - rtas_st(rets, 0, ret); - return; - -param_error_exit: - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); -} - -static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu, - sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, uint32_t nret, - target_ulong rets) -{ - sPAPRPHBState *sphb; - PCIDevice *pdev; - uint32_t addr, option; - uint64_t buid; - - if ((nargs != 4) || (nret != 2)) { - goto param_error_exit; - } - - buid = rtas_ldq(args, 1); - sphb = spapr_pci_find_phb(spapr, buid); - if (!sphb) { - goto param_error_exit; - } - - if (!spapr_phb_eeh_available(sphb)) { - goto param_error_exit; - } - - /* - * We always have PE address of form "00BB0001". "BB" - * represents the bus number of PE's primary bus. - */ - option = rtas_ld(args, 3); - switch (option) { - case RTAS_GET_PE_ADDR: - addr = rtas_ld(args, 0); - pdev = spapr_pci_find_dev(spapr, buid, addr); - if (!pdev) { - goto param_error_exit; - } - - rtas_st(rets, 1, (pci_bus_num(pdev->bus) << 16) + 1); - break; - case RTAS_GET_PE_MODE: - rtas_st(rets, 1, RTAS_PE_MODE_SHARED); - break; - default: - goto param_error_exit; - } - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - return; - -param_error_exit: - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); -} - -static void rtas_ibm_read_slot_reset_state2(PowerPCCPU *cpu, - sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, uint32_t nret, - target_ulong rets) -{ - sPAPRPHBState *sphb; - uint64_t buid; - int state, ret; - - if ((nargs != 3) || (nret != 4 && nret != 5)) { - goto param_error_exit; - } - - buid = rtas_ldq(args, 1); - sphb = spapr_pci_find_phb(spapr, buid); - if (!sphb) { - goto param_error_exit; - } - - if (!spapr_phb_eeh_available(sphb)) { - goto param_error_exit; - } - - ret = spapr_phb_vfio_eeh_get_state(sphb, &state); - rtas_st(rets, 0, ret); - if (ret != RTAS_OUT_SUCCESS) { - return; - } - - rtas_st(rets, 1, state); - rtas_st(rets, 2, RTAS_EEH_SUPPORT); - rtas_st(rets, 3, RTAS_EEH_PE_UNAVAIL_INFO); - if (nret >= 5) { - rtas_st(rets, 4, RTAS_EEH_PE_RECOVER_INFO); - } - return; - -param_error_exit: - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); -} - -static void rtas_ibm_set_slot_reset(PowerPCCPU *cpu, - sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, uint32_t nret, - target_ulong rets) -{ - sPAPRPHBState *sphb; - uint32_t option; - uint64_t buid; - int ret; - - if ((nargs != 4) || (nret != 1)) { - goto param_error_exit; - } - - buid = rtas_ldq(args, 1); - option = rtas_ld(args, 3); - sphb = spapr_pci_find_phb(spapr, buid); - if (!sphb) { - goto param_error_exit; - } - - if (!spapr_phb_eeh_available(sphb)) { - goto param_error_exit; - } - - ret = spapr_phb_vfio_eeh_reset(sphb, option); - rtas_st(rets, 0, ret); - return; - -param_error_exit: - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); -} - -static void rtas_ibm_configure_pe(PowerPCCPU *cpu, - sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, uint32_t nret, - target_ulong rets) -{ - sPAPRPHBState *sphb; - uint64_t buid; - int ret; - - if ((nargs != 3) || (nret != 1)) { - goto param_error_exit; - } - - buid = rtas_ldq(args, 1); - sphb = spapr_pci_find_phb(spapr, buid); - if (!sphb) { - goto param_error_exit; - } - - if (!spapr_phb_eeh_available(sphb)) { - goto param_error_exit; - } - - ret = spapr_phb_vfio_eeh_configure(sphb); - rtas_st(rets, 0, ret); - return; - -param_error_exit: - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); -} - -/* To support it later */ -static void rtas_ibm_slot_error_detail(PowerPCCPU *cpu, - sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, uint32_t nret, - target_ulong rets) -{ - sPAPRPHBState *sphb; - int option; - uint64_t buid; - - if ((nargs != 8) || (nret != 1)) { - goto param_error_exit; - } - - buid = rtas_ldq(args, 1); - sphb = spapr_pci_find_phb(spapr, buid); - if (!sphb) { - goto param_error_exit; - } - - if (!spapr_phb_eeh_available(sphb)) { - goto param_error_exit; - } - - option = rtas_ld(args, 7); - switch (option) { - case RTAS_SLOT_TEMP_ERR_LOG: - case RTAS_SLOT_PERM_ERR_LOG: - break; - default: - goto param_error_exit; - } - - /* We don't have error log yet */ - rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND); - return; - -param_error_exit: - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); -} - -static int pci_spapr_swizzle(int slot, int pin) -{ - return (slot + pin) % PCI_NUM_PINS; -} - -static int pci_spapr_map_irq(PCIDevice *pci_dev, int irq_num) -{ - /* - * Here we need to convert pci_dev + irq_num to some unique value - * which is less than number of IRQs on the specific bus (4). We - * use standard PCI swizzling, that is (slot number + pin number) - * % 4. - */ - return pci_spapr_swizzle(PCI_SLOT(pci_dev->devfn), irq_num); -} - -static void pci_spapr_set_irq(void *opaque, int irq_num, int level) -{ - /* - * Here we use the number returned by pci_spapr_map_irq to find a - * corresponding qemu_irq. - */ - sPAPRPHBState *phb = opaque; - - trace_spapr_pci_lsi_set(phb->dtbusname, irq_num, phb->lsi_table[irq_num].irq); - qemu_set_irq(spapr_phb_lsi_qirq(phb, irq_num), level); -} - -static PCIINTxRoute spapr_route_intx_pin_to_irq(void *opaque, int pin) -{ - sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(opaque); - PCIINTxRoute route; - - route.mode = PCI_INTX_ENABLED; - route.irq = sphb->lsi_table[pin].irq; - - return route; -} - -/* - * MSI/MSIX memory region implementation. - * The handler handles both MSI and MSIX. - * For MSI-X, the vector number is encoded as a part of the address, - * data is set to 0. - * For MSI, the vector number is encoded in least bits in data. - */ -static void spapr_msi_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - uint32_t irq = data; - - trace_spapr_pci_msi_write(addr, data, irq); - - qemu_irq_pulse(xics_get_qirq(spapr->icp, irq)); -} - -static const MemoryRegionOps spapr_msi_ops = { - /* There is no .read as the read result is undefined by PCI spec */ - .read = NULL, - .write = spapr_msi_write, - .endianness = DEVICE_LITTLE_ENDIAN -}; - -/* - * PHB PCI device - */ -static AddressSpace *spapr_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) -{ - sPAPRPHBState *phb = opaque; - - return &phb->iommu_as; -} - -static char *spapr_phb_vfio_get_loc_code(sPAPRPHBState *sphb, PCIDevice *pdev) -{ - char *path = NULL, *buf = NULL, *host = NULL; - - /* Get the PCI VFIO host id */ - host = object_property_get_str(OBJECT(pdev), "host", NULL); - if (!host) { - goto err_out; - } - - /* Construct the path of the file that will give us the DT location */ - path = g_strdup_printf("/sys/bus/pci/devices/%s/devspec", host); - g_free(host); - if (!path || !g_file_get_contents(path, &buf, NULL, NULL)) { - goto err_out; - } - g_free(path); - - /* Construct and read from host device tree the loc-code */ - path = g_strdup_printf("/proc/device-tree%s/ibm,loc-code", buf); - g_free(buf); - if (!path || !g_file_get_contents(path, &buf, NULL, NULL)) { - goto err_out; - } - return buf; - -err_out: - g_free(path); - return NULL; -} - -static char *spapr_phb_get_loc_code(sPAPRPHBState *sphb, PCIDevice *pdev) -{ - char *buf; - const char *devtype = "qemu"; - uint32_t busnr = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(pdev)))); - - if (object_dynamic_cast(OBJECT(pdev), "vfio-pci")) { - buf = spapr_phb_vfio_get_loc_code(sphb, pdev); - if (buf) { - return buf; - } - devtype = "vfio"; - } - /* - * For emulated devices and VFIO-failure case, make up - * the loc-code. - */ - buf = g_strdup_printf("%s_%s:%04x:%02x:%02x.%x", - devtype, pdev->name, sphb->index, busnr, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); - return buf; -} - -/* Macros to operate with address in OF binding to PCI */ -#define b_x(x, p, l) (((x) & ((1<<(l))-1)) << (p)) -#define b_n(x) b_x((x), 31, 1) /* 0 if relocatable */ -#define b_p(x) b_x((x), 30, 1) /* 1 if prefetchable */ -#define b_t(x) b_x((x), 29, 1) /* 1 if the address is aliased */ -#define b_ss(x) b_x((x), 24, 2) /* the space code */ -#define b_bbbbbbbb(x) b_x((x), 16, 8) /* bus number */ -#define b_ddddd(x) b_x((x), 11, 5) /* device number */ -#define b_fff(x) b_x((x), 8, 3) /* function number */ -#define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */ - -/* for 'reg'/'assigned-addresses' OF properties */ -#define RESOURCE_CELLS_SIZE 2 -#define RESOURCE_CELLS_ADDRESS 3 - -typedef struct ResourceFields { - uint32_t phys_hi; - uint32_t phys_mid; - uint32_t phys_lo; - uint32_t size_hi; - uint32_t size_lo; -} QEMU_PACKED ResourceFields; - -typedef struct ResourceProps { - ResourceFields reg[8]; - ResourceFields assigned[7]; - uint32_t reg_len; - uint32_t assigned_len; -} ResourceProps; - -/* fill in the 'reg'/'assigned-resources' OF properties for - * a PCI device. 'reg' describes resource requirements for a - * device's IO/MEM regions, 'assigned-addresses' describes the - * actual resource assignments. - * - * the properties are arrays of ('phys-addr', 'size') pairs describing - * the addressable regions of the PCI device, where 'phys-addr' is a - * RESOURCE_CELLS_ADDRESS-tuple of 32-bit integers corresponding to - * (phys.hi, phys.mid, phys.lo), and 'size' is a - * RESOURCE_CELLS_SIZE-tuple corresponding to (size.hi, size.lo). - * - * phys.hi = 0xYYXXXXZZ, where: - * 0xYY = npt000ss - * ||| | - * ||| +-- space code - * ||| | - * ||| + 00 if configuration space - * ||| + 01 if IO region, - * ||| + 10 if 32-bit MEM region - * ||| + 11 if 64-bit MEM region - * ||| - * ||+------ for non-relocatable IO: 1 if aliased - * || for relocatable IO: 1 if below 64KB - * || for MEM: 1 if below 1MB - * |+------- 1 if region is prefetchable - * +-------- 1 if region is non-relocatable - * 0xXXXX = bbbbbbbb dddddfff, encoding bus, slot, and function - * bits respectively - * 0xZZ = rrrrrrrr, the register number of the BAR corresponding - * to the region - * - * phys.mid and phys.lo correspond respectively to the hi/lo portions - * of the actual address of the region. - * - * how the phys-addr/size values are used differ slightly between - * 'reg' and 'assigned-addresses' properties. namely, 'reg' has - * an additional description for the config space region of the - * device, and in the case of QEMU has n=0 and phys.mid=phys.lo=0 - * to describe the region as relocatable, with an address-mapping - * that corresponds directly to the PHB's address space for the - * resource. 'assigned-addresses' always has n=1 set with an absolute - * address assigned for the resource. in general, 'assigned-addresses' - * won't be populated, since addresses for PCI devices are generally - * unmapped initially and left to the guest to assign. - * - * note also that addresses defined in these properties are, at least - * for PAPR guests, relative to the PHBs IO/MEM windows, and - * correspond directly to the addresses in the BARs. - * - * in accordance with PCI Bus Binding to Open Firmware, - * IEEE Std 1275-1994, section 4.1.1, as implemented by PAPR+ v2.7, - * Appendix C. - */ -static void populate_resource_props(PCIDevice *d, ResourceProps *rp) -{ - int bus_num = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(d)))); - uint32_t dev_id = (b_bbbbbbbb(bus_num) | - b_ddddd(PCI_SLOT(d->devfn)) | - b_fff(PCI_FUNC(d->devfn))); - ResourceFields *reg, *assigned; - int i, reg_idx = 0, assigned_idx = 0; - - /* config space region */ - reg = &rp->reg[reg_idx++]; - reg->phys_hi = cpu_to_be32(dev_id); - reg->phys_mid = 0; - reg->phys_lo = 0; - reg->size_hi = 0; - reg->size_lo = 0; - - for (i = 0; i < PCI_NUM_REGIONS; i++) { - if (!d->io_regions[i].size) { - continue; - } - - reg = &rp->reg[reg_idx++]; - - reg->phys_hi = cpu_to_be32(dev_id | b_rrrrrrrr(pci_bar(d, i))); - if (d->io_regions[i].type & PCI_BASE_ADDRESS_SPACE_IO) { - reg->phys_hi |= cpu_to_be32(b_ss(1)); - } else if (d->io_regions[i].type & PCI_BASE_ADDRESS_MEM_TYPE_64) { - reg->phys_hi |= cpu_to_be32(b_ss(3)); - } else { - reg->phys_hi |= cpu_to_be32(b_ss(2)); - } - reg->phys_mid = 0; - reg->phys_lo = 0; - reg->size_hi = cpu_to_be32(d->io_regions[i].size >> 32); - reg->size_lo = cpu_to_be32(d->io_regions[i].size); - - if (d->io_regions[i].addr == PCI_BAR_UNMAPPED) { - continue; - } - - assigned = &rp->assigned[assigned_idx++]; - assigned->phys_hi = cpu_to_be32(reg->phys_hi | b_n(1)); - assigned->phys_mid = cpu_to_be32(d->io_regions[i].addr >> 32); - assigned->phys_lo = cpu_to_be32(d->io_regions[i].addr); - assigned->size_hi = reg->size_hi; - assigned->size_lo = reg->size_lo; - } - - rp->reg_len = reg_idx * sizeof(ResourceFields); - rp->assigned_len = assigned_idx * sizeof(ResourceFields); -} - -static uint32_t spapr_phb_get_pci_drc_index(sPAPRPHBState *phb, - PCIDevice *pdev); - -static int spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset, - sPAPRPHBState *sphb) -{ - ResourceProps rp; - bool is_bridge = false; - 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) { - is_bridge = true; - } - - /* in accordance with PAPR+ v2.7 13.6.3, Table 181 */ - _FDT(fdt_setprop_cell(fdt, offset, "vendor-id", - pci_default_read_config(dev, PCI_VENDOR_ID, 2))); - _FDT(fdt_setprop_cell(fdt, offset, "device-id", - pci_default_read_config(dev, PCI_DEVICE_ID, 2))); - _FDT(fdt_setprop_cell(fdt, offset, "revision-id", - pci_default_read_config(dev, PCI_REVISION_ID, 1))); - _FDT(fdt_setprop_cell(fdt, offset, "class-code", - pci_default_read_config(dev, PCI_CLASS_PROG, 3))); - if (pci_default_read_config(dev, PCI_INTERRUPT_PIN, 1)) { - _FDT(fdt_setprop_cell(fdt, offset, "interrupts", - pci_default_read_config(dev, PCI_INTERRUPT_PIN, 1))); - } - - if (!is_bridge) { - _FDT(fdt_setprop_cell(fdt, offset, "min-grant", - pci_default_read_config(dev, PCI_MIN_GNT, 1))); - _FDT(fdt_setprop_cell(fdt, offset, "max-latency", - pci_default_read_config(dev, PCI_MAX_LAT, 1))); - } - - if (pci_default_read_config(dev, PCI_SUBSYSTEM_ID, 2)) { - _FDT(fdt_setprop_cell(fdt, offset, "subsystem-id", - pci_default_read_config(dev, PCI_SUBSYSTEM_ID, 2))); - } - - if (pci_default_read_config(dev, PCI_SUBSYSTEM_VENDOR_ID, 2)) { - _FDT(fdt_setprop_cell(fdt, offset, "subsystem-vendor-id", - pci_default_read_config(dev, PCI_SUBSYSTEM_VENDOR_ID, 2))); - } - - _FDT(fdt_setprop_cell(fdt, offset, "cache-line-size", - pci_default_read_config(dev, PCI_CACHE_LINE_SIZE, 1))); - - /* the following fdt cells are masked off the pci status register */ - pci_status = pci_default_read_config(dev, PCI_STATUS, 2); - _FDT(fdt_setprop_cell(fdt, offset, "devsel-speed", - PCI_STATUS_DEVSEL_MASK & pci_status)); - - if (pci_status & PCI_STATUS_FAST_BACK) { - _FDT(fdt_setprop(fdt, offset, "fast-back-to-back", NULL, 0)); - } - if (pci_status & PCI_STATUS_66MHZ) { - _FDT(fdt_setprop(fdt, offset, "66mhz-capable", NULL, 0)); - } - if (pci_status & PCI_STATUS_UDF) { - _FDT(fdt_setprop(fdt, offset, "udf-supported", NULL, 0)); - } - - /* NOTE: this is normally generated by firmware via path/unit name, - * but in our case we must set it manually since it does not get - * processed by OF beforehand - */ - _FDT(fdt_setprop_string(fdt, offset, "name", "pci")); - buf = spapr_phb_get_loc_code(sphb, dev); - if (!buf) { - error_report("Failed setting the ibm,loc-code"); - return -1; - } - - err = fdt_setprop_string(fdt, offset, "ibm,loc-code", buf); - g_free(buf); - if (err < 0) { - return err; - } - - if (drc_index) { - _FDT(fdt_setprop_cell(fdt, offset, "ibm,my-drc-index", drc_index)); - } - - _FDT(fdt_setprop_cell(fdt, offset, "#address-cells", - RESOURCE_CELLS_ADDRESS)); - _FDT(fdt_setprop_cell(fdt, offset, "#size-cells", - 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)); - _FDT(fdt_setprop(fdt, offset, "assigned-addresses", - (uint8_t *)rp.assigned, rp.assigned_len)); - - return 0; -} - -/* create OF node for pci device and required OF DT properties */ -static int spapr_create_pci_child_dt(sPAPRPHBState *phb, PCIDevice *dev, - void *fdt, int node_offset) -{ - int offset, ret; - int slot = PCI_SLOT(dev->devfn); - int func = PCI_FUNC(dev->devfn); - char nodename[FDT_NAME_MAX]; - - if (func != 0) { - snprintf(nodename, FDT_NAME_MAX, "pci@%x,%x", slot, func); - } else { - snprintf(nodename, FDT_NAME_MAX, "pci@%x", slot); - } - offset = fdt_add_subnode(fdt, node_offset, nodename); - ret = spapr_populate_pci_child_dt(dev, fdt, offset, phb); - - g_assert(!ret); - if (ret) { - return 0; - } - return offset; -} - -static void spapr_phb_add_pci_device(sPAPRDRConnector *drc, - sPAPRPHBState *phb, - PCIDevice *pdev, - Error **errp) -{ - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - DeviceState *dev = DEVICE(pdev); - 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); - if (!fdt_start_offset) { - error_setg(errp, "Failed to create pci child device tree node"); - goto out; - } - } - - drck->attach(drc, DEVICE(pdev), - fdt, fdt_start_offset, !dev->hotplugged, errp); -out: - if (*errp) { - g_free(fdt); - } -} - -static void spapr_phb_remove_pci_device_cb(DeviceState *dev, void *opaque) -{ - /* some version guests do not wait for completion of a device - * cleanup (generally done asynchronously by the kernel) before - * signaling to QEMU that the device is safe, but instead sleep - * for some 'safe' period of time. unfortunately on a busy host - * this sleep isn't guaranteed to be long enough, resulting in - * bad things like IRQ lines being left asserted during final - * device removal. to deal with this we call reset just prior - * to finalizing the device, which will put the device back into - * an 'idle' state, as the device cleanup code expects. - */ - pci_device_reset(PCI_DEVICE(dev)); - object_unparent(OBJECT(dev)); -} - -static void spapr_phb_remove_pci_device(sPAPRDRConnector *drc, - sPAPRPHBState *phb, - PCIDevice *pdev, - Error **errp) -{ - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - - drck->detach(drc, DEVICE(pdev), spapr_phb_remove_pci_device_cb, phb, errp); -} - -static sPAPRDRConnector *spapr_phb_get_pci_func_drc(sPAPRPHBState *phb, - uint32_t busnr, - int32_t devfn) -{ - return spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_PCI, - (phb->index << 16) | - (busnr << 8) | - 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, - PCIDevice *pdev) -{ - sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev); - sPAPRDRConnectorClass *drck; - - if (!drc) { - return 0; - } - - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - return drck->get_index(drc); -} - -static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler, - DeviceState *plugged_dev, Error **errp) -{ - sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(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 - */ - if (!phb->dr_enabled) { - /* if this is a hotplug operation initiated by the user - * we need to let them know it's not enabled - */ - if (plugged_dev->hotplugged) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, - object_get_typename(OBJECT(phb))); - } - return; - } - - 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 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); - } - } - } -} - -static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler, - DeviceState *plugged_dev, Error **errp) -{ - sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler)); - PCIDevice *pdev = PCI_DEVICE(plugged_dev); - sPAPRDRConnectorClass *drck; - sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev); - Error *local_err = NULL; - - if (!phb->dr_enabled) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, - object_get_typename(OBJECT(phb))); - return; - } - - g_assert(drc); - - 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; - } - - /* 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); - } - } - } - } -} - -static void spapr_phb_realize(DeviceState *dev, Error **errp) -{ - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - SysBusDevice *s = SYS_BUS_DEVICE(dev); - sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(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; - - if ((sphb->buid != (uint64_t)-1) || (sphb->dma_liobn != (uint32_t)-1) - || (sphb->mem_win_addr != (hwaddr)-1) - || (sphb->io_win_addr != (hwaddr)-1)) { - error_setg(errp, "Either \"index\" or other parameters must" - " be specified for PAPR PHB, not both"); - return; - } - - if (sphb->index > SPAPR_PCI_MAX_INDEX) { - error_setg(errp, "\"index\" for PAPR PHB is too large (max %u)", - SPAPR_PCI_MAX_INDEX); - return; - } - - sphb->buid = SPAPR_PCI_BASE_BUID + sphb->index; - sphb->dma_liobn = SPAPR_PCI_LIOBN(sphb->index, 0); - - windows_base = SPAPR_PCI_WINDOW_BASE - + sphb->index * SPAPR_PCI_WINDOW_SPACING; - sphb->mem_win_addr = windows_base + SPAPR_PCI_MMIO_WIN_OFF; - sphb->io_win_addr = windows_base + SPAPR_PCI_IO_WIN_OFF; - } - - if (sphb->buid == (uint64_t)-1) { - error_setg(errp, "BUID not specified for PHB"); - return; - } - - if (sphb->dma_liobn == (uint32_t)-1) { - error_setg(errp, "LIOBN not specified for PHB"); - return; - } - - if (sphb->mem_win_addr == (hwaddr)-1) { - error_setg(errp, "Memory window address not specified for PHB"); - return; - } - - if (sphb->io_win_addr == (hwaddr)-1) { - error_setg(errp, "IO window address not specified for PHB"); - return; - } - - if (spapr_pci_find_phb(spapr, sphb->buid)) { - error_setg(errp, "PCI host bridges must have unique BUIDs"); - return; - } - - sphb->dtbusname = g_strdup_printf("pci@%" PRIx64, sphb->buid); - - namebuf = alloca(strlen(sphb->dtbusname) + 32); - - /* Initialize memory regions */ - sprintf(namebuf, "%s.mmio", sphb->dtbusname); - memory_region_init(&sphb->memspace, OBJECT(sphb), namebuf, UINT64_MAX); - - sprintf(namebuf, "%s.mmio-alias", sphb->dtbusname); - memory_region_init_alias(&sphb->memwindow, OBJECT(sphb), - namebuf, &sphb->memspace, - SPAPR_PCI_MEM_WIN_BUS_OFFSET, sphb->mem_win_size); - memory_region_add_subregion(get_system_memory(), sphb->mem_win_addr, - &sphb->memwindow); - - /* Initialize IO regions */ - sprintf(namebuf, "%s.io", sphb->dtbusname); - memory_region_init(&sphb->iospace, OBJECT(sphb), - namebuf, SPAPR_PCI_IO_WIN_SIZE); - - sprintf(namebuf, "%s.io-alias", sphb->dtbusname); - memory_region_init_alias(&sphb->iowindow, OBJECT(sphb), namebuf, - &sphb->iospace, 0, SPAPR_PCI_IO_WIN_SIZE); - memory_region_add_subregion(get_system_memory(), sphb->io_win_addr, - &sphb->iowindow); - - bus = pci_register_bus(dev, NULL, - pci_spapr_set_irq, pci_spapr_map_irq, sphb, - &sphb->memspace, &sphb->iospace, - PCI_DEVFN(0, 0), PCI_NUM_PINS, TYPE_PCI_BUS); - phb->bus = bus; - qbus_set_hotplug_handler(BUS(phb->bus), DEVICE(sphb), NULL); - - /* - * Initialize PHB address space. - * By default there will be at least one subregion for default - * 32bit DMA window. - * Later the guest might want to create another DMA window - * which will become another memory subregion. - */ - sprintf(namebuf, "%s.iommu-root", sphb->dtbusname); - - memory_region_init(&sphb->iommu_root, OBJECT(sphb), - namebuf, UINT64_MAX); - address_space_init(&sphb->iommu_as, &sphb->iommu_root, - sphb->dtbusname); - - /* - * As MSI/MSIX interrupts trigger by writing at MSI/MSIX vectors, - * we need to allocate some memory to catch those writes coming - * from msi_notify()/msix_notify(). - * As MSIMessage:addr is going to be the same and MSIMessage:data - * is going to be a VIRQ number, 4 bytes of the MSI MR will only - * be used. - * - * For KVM we want to ensure that this memory is a full page so that - * our memory slot is of page size granularity. - */ -#ifdef CONFIG_KVM - if (kvm_enabled()) { - msi_window_size = getpagesize(); - } -#endif - - memory_region_init_io(&sphb->msiwindow, NULL, &spapr_msi_ops, spapr, - "msi", msi_window_size); - memory_region_add_subregion(&sphb->iommu_root, SPAPR_PCI_MSI_WINDOW, - &sphb->msiwindow); - - pci_setup_iommu(bus, spapr_pci_dma_iommu, sphb); - - pci_bus_set_route_irq_fn(bus, spapr_route_intx_pin_to_irq); - - QLIST_INSERT_HEAD(&spapr->phbs, sphb, list); - - /* 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, &local_err); - if (local_err) { - error_propagate(errp, local_err); - error_prepend(errp, "can't allocate LSIs: "); - return; - } - - sphb->lsi_table[i].irq = irq; - } - - /* allocate connectors for child PCI devices */ - if (sphb->dr_enabled) { - for (i = 0; i < PCI_SLOT_MAX * 8; i++) { - spapr_dr_connector_new(OBJECT(phb), - SPAPR_DR_CONNECTOR_TYPE_PCI, - (sphb->index << 16) | i); - } - } - - 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; - } - - /* Register default 32bit DMA window */ - 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) -{ - DeviceState *dev = (DeviceState *) object_dynamic_cast(child, TYPE_DEVICE); - - if (dev) { - device_reset(dev); - } - - return 0; -} - -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[] = { - DEFINE_PROP_UINT32("index", sPAPRPHBState, index, -1), - DEFINE_PROP_UINT64("buid", sPAPRPHBState, buid, -1), - DEFINE_PROP_UINT32("liobn", sPAPRPHBState, dma_liobn, -1), - DEFINE_PROP_UINT64("mem_win_addr", sPAPRPHBState, mem_win_addr, -1), - DEFINE_PROP_UINT64("mem_win_size", sPAPRPHBState, mem_win_size, - SPAPR_PCI_MMIO_WIN_SIZE), - DEFINE_PROP_UINT64("io_win_addr", sPAPRPHBState, io_win_addr, -1), - DEFINE_PROP_UINT64("io_win_size", sPAPRPHBState, io_win_size, - 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(), -}; - -static const VMStateDescription vmstate_spapr_pci_lsi = { - .name = "spapr_pci/lsi", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_EQUAL(irq, struct spapr_pci_lsi), - - VMSTATE_END_OF_LIST() - }, -}; - -static const VMStateDescription vmstate_spapr_pci_msi = { - .name = "spapr_pci/msi", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField []) { - VMSTATE_UINT32(key, spapr_pci_msi_mig), - VMSTATE_UINT32(value.first_irq, spapr_pci_msi_mig), - VMSTATE_UINT32(value.num, spapr_pci_msi_mig), - VMSTATE_END_OF_LIST() - }, -}; - -static void spapr_pci_pre_save(void *opaque) -{ - sPAPRPHBState *sphb = opaque; - GHashTableIter iter; - gpointer key, value; - int i; - - 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; - } - sphb->msi_devs = g_malloc(sphb->msi_devs_num * sizeof(spapr_pci_msi_mig)); - - g_hash_table_iter_init(&iter, sphb->msi); - for (i = 0; g_hash_table_iter_next(&iter, &key, &value); ++i) { - sphb->msi_devs[i].key = *(uint32_t *) key; - sphb->msi_devs[i].value = *(spapr_pci_msi *) value; - } -} - -static int spapr_pci_post_load(void *opaque, int version_id) -{ - sPAPRPHBState *sphb = opaque; - gpointer key, value; - int i; - - for (i = 0; i < sphb->msi_devs_num; ++i) { - key = g_memdup(&sphb->msi_devs[i].key, - sizeof(sphb->msi_devs[i].key)); - value = g_memdup(&sphb->msi_devs[i].value, - sizeof(sphb->msi_devs[i].value)); - g_hash_table_insert(sphb->msi, key, value); - } - g_free(sphb->msi_devs); - sphb->msi_devs = NULL; - sphb->msi_devs_num = 0; - - return 0; -} - -static const VMStateDescription vmstate_spapr_pci = { - .name = "spapr_pci", - .version_id = 2, - .minimum_version_id = 2, - .pre_save = spapr_pci_pre_save, - .post_load = spapr_pci_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT64_EQUAL(buid, sPAPRPHBState), - VMSTATE_UINT32_EQUAL(dma_liobn, sPAPRPHBState), - VMSTATE_UINT64_EQUAL(mem_win_addr, sPAPRPHBState), - VMSTATE_UINT64_EQUAL(mem_win_size, sPAPRPHBState), - VMSTATE_UINT64_EQUAL(io_win_addr, sPAPRPHBState), - VMSTATE_UINT64_EQUAL(io_win_size, sPAPRPHBState), - VMSTATE_STRUCT_ARRAY(lsi_table, sPAPRPHBState, PCI_NUM_PINS, 0, - vmstate_spapr_pci_lsi, struct spapr_pci_lsi), - VMSTATE_INT32(msi_devs_num, sPAPRPHBState), - VMSTATE_STRUCT_VARRAY_ALLOC(msi_devs, sPAPRPHBState, msi_devs_num, 0, - vmstate_spapr_pci_msi, spapr_pci_msi_mig), - VMSTATE_END_OF_LIST() - }, -}; - -static const char *spapr_phb_root_bus_path(PCIHostState *host_bridge, - PCIBus *rootbus) -{ - sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(host_bridge); - - return sphb->dtbusname; -} - -static void spapr_phb_class_init(ObjectClass *klass, void *data) -{ - PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - HotplugHandlerClass *hp = HOTPLUG_HANDLER_CLASS(klass); - - hc->root_bus_path = spapr_phb_root_bus_path; - dc->realize = spapr_phb_realize; - dc->props = spapr_phb_properties; - dc->reset = spapr_phb_reset; - dc->vmsd = &vmstate_spapr_pci; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->cannot_instantiate_with_device_add_yet = false; - hp->plug = spapr_phb_hot_plug_child; - hp->unplug = spapr_phb_hot_unplug_child; -} - -static const TypeInfo spapr_phb_info = { - .name = TYPE_SPAPR_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(sPAPRPHBState), - .class_init = spapr_phb_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - -PCIHostState *spapr_create_phb(sPAPRMachineState *spapr, int index) -{ - DeviceState *dev; - - dev = qdev_create(NULL, TYPE_SPAPR_PCI_HOST_BRIDGE); - qdev_prop_set_uint32(dev, "index", index); - qdev_init_nofail(dev); - - return PCI_HOST_BRIDGE(dev); -} - -typedef struct sPAPRFDT { - void *fdt; - int node_off; - sPAPRPHBState *sphb; -} sPAPRFDT; - -static void spapr_populate_pci_devices_dt(PCIBus *bus, PCIDevice *pdev, - void *opaque) -{ - PCIBus *sec_bus; - sPAPRFDT *p = opaque; - int offset; - sPAPRFDT s_fdt; - - offset = spapr_create_pci_child_dt(p->sphb, pdev, p->fdt, p->node_off); - if (!offset) { - error_report("Failed to create pci child device tree node"); - return; - } - - if ((pci_default_read_config(pdev, PCI_HEADER_TYPE, 1) != - PCI_HEADER_TYPE_BRIDGE)) { - return; - } - - sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev)); - if (!sec_bus) { - return; - } - - s_fdt.fdt = p->fdt; - s_fdt.node_off = offset; - s_fdt.sphb = p->sphb; - pci_for_each_device(sec_bus, pci_bus_num(sec_bus), - spapr_populate_pci_devices_dt, - &s_fdt); -} - -static void spapr_phb_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev, - void *opaque) -{ - unsigned int *bus_no = opaque; - unsigned int primary = *bus_no; - unsigned int subordinate = 0xff; - PCIBus *sec_bus = NULL; - - if ((pci_default_read_config(pdev, PCI_HEADER_TYPE, 1) != - PCI_HEADER_TYPE_BRIDGE)) { - return; - } - - (*bus_no)++; - pci_default_write_config(pdev, PCI_PRIMARY_BUS, primary, 1); - pci_default_write_config(pdev, PCI_SECONDARY_BUS, *bus_no, 1); - pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, *bus_no, 1); - - sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev)); - if (!sec_bus) { - return; - } - - pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, subordinate, 1); - pci_for_each_device(sec_bus, pci_bus_num(sec_bus), - spapr_phb_pci_enumerate_bridge, bus_no); - pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, *bus_no, 1); -} - -static void spapr_phb_pci_enumerate(sPAPRPHBState *phb) -{ - PCIBus *bus = PCI_HOST_BRIDGE(phb)->bus; - unsigned int bus_no = 0; - - pci_for_each_device(bus, pci_bus_num(bus), - spapr_phb_pci_enumerate_bridge, - &bus_no); - -} - -int spapr_populate_pci_dt(sPAPRPHBState *phb, - uint32_t xics_phandle, - void *fdt) -{ - int bus_off, i, j, ret; - char nodename[FDT_NAME_MAX]; - uint32_t bus_range[] = { cpu_to_be32(0), cpu_to_be32(0xff) }; - const uint64_t mmiosize = memory_region_size(&phb->memwindow); - const uint64_t w32max = (1ULL << 32) - SPAPR_PCI_MEM_WIN_BUS_OFFSET; - const uint64_t w32size = MIN(w32max, mmiosize); - const uint64_t w64size = (mmiosize > w32size) ? (mmiosize - w32size) : 0; - struct { - uint32_t hi; - uint64_t child; - uint64_t parent; - uint64_t size; - } QEMU_PACKED ranges[] = { - { - cpu_to_be32(b_ss(1)), cpu_to_be64(0), - cpu_to_be64(phb->io_win_addr), - cpu_to_be64(memory_region_size(&phb->iospace)), - }, - { - cpu_to_be32(b_ss(2)), cpu_to_be64(SPAPR_PCI_MEM_WIN_BUS_OFFSET), - cpu_to_be64(phb->mem_win_addr), - cpu_to_be64(w32size), - }, - { - cpu_to_be32(b_ss(3)), cpu_to_be64(1ULL << 32), - cpu_to_be64(phb->mem_win_addr + w32size), - cpu_to_be64(w64size) - }, - }; - const unsigned sizeof_ranges = (w64size ? 3 : 2) * sizeof(ranges[0]); - uint64_t bus_reg[] = { cpu_to_be64(phb->buid), 0 }; - uint32_t interrupt_map_mask[] = { - cpu_to_be32(b_ddddd(-1)|b_fff(0)), 0x0, 0x0, cpu_to_be32(-1)}; - uint32_t interrupt_map[PCI_SLOT_MAX * PCI_NUM_PINS][7]; - sPAPRTCETable *tcet; - PCIBus *bus = PCI_HOST_BRIDGE(phb)->bus; - sPAPRFDT s_fdt; - - /* Start populating the FDT */ - snprintf(nodename, FDT_NAME_MAX, "pci@%" PRIx64, phb->buid); - bus_off = fdt_add_subnode(fdt, 0, nodename); - if (bus_off < 0) { - return bus_off; - } - - /* Write PHB properties */ - _FDT(fdt_setprop_string(fdt, bus_off, "device_type", "pci")); - _FDT(fdt_setprop_string(fdt, bus_off, "compatible", "IBM,Logical_PHB")); - _FDT(fdt_setprop_cell(fdt, bus_off, "#address-cells", 0x3)); - _FDT(fdt_setprop_cell(fdt, bus_off, "#size-cells", 0x2)); - _FDT(fdt_setprop_cell(fdt, bus_off, "#interrupt-cells", 0x1)); - _FDT(fdt_setprop(fdt, bus_off, "used-by-rtas", NULL, 0)); - _FDT(fdt_setprop(fdt, bus_off, "bus-range", &bus_range, sizeof(bus_range))); - _FDT(fdt_setprop(fdt, bus_off, "ranges", &ranges, sizeof_ranges)); - _FDT(fdt_setprop(fdt, bus_off, "reg", &bus_reg, sizeof(bus_reg))); - _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pci-config-space-type", 0x1)); - _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", XICS_IRQS)); - - /* Build the interrupt-map, this must matches what is done - * in pci_spapr_map_irq - */ - _FDT(fdt_setprop(fdt, bus_off, "interrupt-map-mask", - &interrupt_map_mask, sizeof(interrupt_map_mask))); - for (i = 0; i < PCI_SLOT_MAX; i++) { - for (j = 0; j < PCI_NUM_PINS; j++) { - uint32_t *irqmap = interrupt_map[i*PCI_NUM_PINS + j]; - int lsi_num = pci_spapr_swizzle(i, j); - - irqmap[0] = cpu_to_be32(b_ddddd(i)|b_fff(0)); - irqmap[1] = 0; - irqmap[2] = 0; - irqmap[3] = cpu_to_be32(j+1); - irqmap[4] = cpu_to_be32(xics_phandle); - irqmap[5] = cpu_to_be32(phb->lsi_table[lsi_num].irq); - irqmap[6] = cpu_to_be32(0x8); - } - } - /* Write interrupt map */ - _FDT(fdt_setprop(fdt, bus_off, "interrupt-map", &interrupt_map, - 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); - - /* Walk the bridges and program the bus numbers*/ - spapr_phb_pci_enumerate(phb); - _FDT(fdt_setprop_cell(fdt, bus_off, "qemu,phb-enumerated", 0x1)); - - /* Populate tree nodes with PCI devices attached */ - s_fdt.fdt = fdt; - s_fdt.node_off = bus_off; - s_fdt.sphb = phb; - pci_for_each_device(bus, pci_bus_num(bus), - spapr_populate_pci_devices_dt, - &s_fdt); - - ret = spapr_drc_populate_dt(fdt, bus_off, OBJECT(phb), - SPAPR_DR_CONNECTOR_TYPE_PCI); - if (ret) { - return ret; - } - - return 0; -} - -void spapr_pci_rtas_init(void) -{ - spapr_rtas_register(RTAS_READ_PCI_CONFIG, "read-pci-config", - rtas_read_pci_config); - spapr_rtas_register(RTAS_WRITE_PCI_CONFIG, "write-pci-config", - rtas_write_pci_config); - spapr_rtas_register(RTAS_IBM_READ_PCI_CONFIG, "ibm,read-pci-config", - rtas_ibm_read_pci_config); - spapr_rtas_register(RTAS_IBM_WRITE_PCI_CONFIG, "ibm,write-pci-config", - rtas_ibm_write_pci_config); - if (msi_nonbroken) { - spapr_rtas_register(RTAS_IBM_QUERY_INTERRUPT_SOURCE_NUMBER, - "ibm,query-interrupt-source-number", - rtas_ibm_query_interrupt_source_number); - spapr_rtas_register(RTAS_IBM_CHANGE_MSI, "ibm,change-msi", - rtas_ibm_change_msi); - } - - spapr_rtas_register(RTAS_IBM_SET_EEH_OPTION, - "ibm,set-eeh-option", - rtas_ibm_set_eeh_option); - spapr_rtas_register(RTAS_IBM_GET_CONFIG_ADDR_INFO2, - "ibm,get-config-addr-info2", - rtas_ibm_get_config_addr_info2); - spapr_rtas_register(RTAS_IBM_READ_SLOT_RESET_STATE2, - "ibm,read-slot-reset-state2", - rtas_ibm_read_slot_reset_state2); - spapr_rtas_register(RTAS_IBM_SET_SLOT_RESET, - "ibm,set-slot-reset", - rtas_ibm_set_slot_reset); - spapr_rtas_register(RTAS_IBM_CONFIGURE_PE, - "ibm,configure-pe", - rtas_ibm_configure_pe); - spapr_rtas_register(RTAS_IBM_SLOT_ERROR_DETAIL, - "ibm,slot-error-detail", - rtas_ibm_slot_error_detail); -} - -static void spapr_pci_register_types(void) -{ - type_register_static(&spapr_phb_info); -} - -type_init(spapr_pci_register_types) - -static int spapr_switch_one_vga(DeviceState *dev, void *opaque) -{ - bool be = *(bool *)opaque; - - if (object_dynamic_cast(OBJECT(dev), "VGA") - || object_dynamic_cast(OBJECT(dev), "secondary-vga")) { - object_property_set_bool(OBJECT(dev), be, "big-endian-framebuffer", - &error_abort); - } - return 0; -} - -void spapr_pci_switch_vga(bool big_endian) -{ - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - sPAPRPHBState *sphb; - - /* - * For backward compatibility with existing guests, we switch - * the endianness of the VGA controller when changing the guest - * interrupt mode - */ - QLIST_FOREACH(sphb, &spapr->phbs, list) { - BusState *bus = &PCI_HOST_BRIDGE(sphb)->bus->qbus; - qbus_walk_children(bus, spapr_switch_one_vga, NULL, NULL, NULL, - &big_endian); - } -} diff --git a/qemu/hw/ppc/spapr_pci_vfio.c b/qemu/hw/ppc/spapr_pci_vfio.c deleted file mode 100644 index cbd3d23c9..000000000 --- a/qemu/hw/ppc/spapr_pci_vfio.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * QEMU sPAPR PCI host for VFIO - * - * Copyright (c) 2011-2014 Alexey Kardashevskiy, IBM Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, - * or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "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" - -#define TYPE_SPAPR_PCI_VFIO_HOST_BRIDGE "spapr-pci-vfio-host-bridge" - -#define SPAPR_PCI_VFIO_HOST_BRIDGE(obj) \ - OBJECT_CHECK(sPAPRPHBVFIOState, (obj), TYPE_SPAPR_PCI_VFIO_HOST_BRIDGE) - -typedef struct sPAPRPHBVFIOState sPAPRPHBVFIOState; - -struct sPAPRPHBVFIOState { - sPAPRPHBState phb; - - int32_t iommugroupid; -}; - -static Property spapr_phb_vfio_properties[] = { - DEFINE_PROP_INT32("iommu", sPAPRPHBVFIOState, iommugroupid, -1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void spapr_phb_vfio_instance_init(Object *obj) -{ - error_report("spapr-pci-vfio-host-bridge is deprecated"); -} - -bool spapr_phb_eeh_available(sPAPRPHBState *sphb) -{ - return vfio_eeh_as_ok(&sphb->iommu_as); -} - -static void spapr_phb_vfio_eeh_reenable(sPAPRPHBState *sphb) -{ - vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_ENABLE); -} - -void spapr_phb_vfio_reset(DeviceState *qdev) -{ - /* - * The PE might be in frozen state. To reenable the EEH - * functionality on it will clean the frozen state, which - * ensures that the contained PCI devices will work properly - * after reboot. - */ - spapr_phb_vfio_eeh_reenable(SPAPR_PCI_HOST_BRIDGE(qdev)); -} - -int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb, - unsigned int addr, int option) -{ - uint32_t op; - int ret; - - switch (option) { - case RTAS_EEH_DISABLE: - op = VFIO_EEH_PE_DISABLE; - break; - case RTAS_EEH_ENABLE: { - PCIHostState *phb; - PCIDevice *pdev; - - /* - * The EEH functionality is enabled on basis of PCI device, - * instead of PE. We need check the validity of the PCI - * device address. - */ - phb = PCI_HOST_BRIDGE(sphb); - pdev = pci_find_device(phb->bus, - (addr >> 16) & 0xFF, (addr >> 8) & 0xFF); - if (!pdev || !object_dynamic_cast(OBJECT(pdev), "vfio-pci")) { - return RTAS_OUT_PARAM_ERROR; - } - - op = VFIO_EEH_PE_ENABLE; - break; - } - case RTAS_EEH_THAW_IO: - op = VFIO_EEH_PE_UNFREEZE_IO; - break; - case RTAS_EEH_THAW_DMA: - op = VFIO_EEH_PE_UNFREEZE_DMA; - break; - default: - return RTAS_OUT_PARAM_ERROR; - } - - ret = vfio_eeh_as_op(&sphb->iommu_as, op); - if (ret < 0) { - return RTAS_OUT_HW_ERROR; - } - - return RTAS_OUT_SUCCESS; -} - -int spapr_phb_vfio_eeh_get_state(sPAPRPHBState *sphb, int *state) -{ - int ret; - - ret = vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_GET_STATE); - if (ret < 0) { - return RTAS_OUT_PARAM_ERROR; - } - - *state = ret; - return RTAS_OUT_SUCCESS; -} - -static void spapr_phb_vfio_eeh_clear_dev_msix(PCIBus *bus, - PCIDevice *pdev, - void *opaque) -{ - /* Check if the device is VFIO PCI device */ - if (!object_dynamic_cast(OBJECT(pdev), "vfio-pci")) { - return; - } - - /* - * The MSIx table will be cleaned out by reset. We need - * disable it so that it can be reenabled properly. Also, - * the cached MSIx table should be cleared as it's not - * reflecting the contents in hardware. - */ - if (msix_enabled(pdev)) { - uint16_t flags; - - flags = pci_host_config_read_common(pdev, - pdev->msix_cap + PCI_MSIX_FLAGS, - pci_config_size(pdev), 2); - flags &= ~PCI_MSIX_FLAGS_ENABLE; - pci_host_config_write_common(pdev, - pdev->msix_cap + PCI_MSIX_FLAGS, - pci_config_size(pdev), flags, 2); - } - - msix_reset(pdev); -} - -static void spapr_phb_vfio_eeh_clear_bus_msix(PCIBus *bus, void *opaque) -{ - pci_for_each_device(bus, pci_bus_num(bus), - spapr_phb_vfio_eeh_clear_dev_msix, NULL); -} - -static void spapr_phb_vfio_eeh_pre_reset(sPAPRPHBState *sphb) -{ - PCIHostState *phb = PCI_HOST_BRIDGE(sphb); - - pci_for_each_bus(phb->bus, spapr_phb_vfio_eeh_clear_bus_msix, NULL); -} - -int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option) -{ - uint32_t op; - int ret; - - switch (option) { - case RTAS_SLOT_RESET_DEACTIVATE: - op = VFIO_EEH_PE_RESET_DEACTIVATE; - break; - case RTAS_SLOT_RESET_HOT: - spapr_phb_vfio_eeh_pre_reset(sphb); - op = VFIO_EEH_PE_RESET_HOT; - break; - case RTAS_SLOT_RESET_FUNDAMENTAL: - spapr_phb_vfio_eeh_pre_reset(sphb); - op = VFIO_EEH_PE_RESET_FUNDAMENTAL; - break; - default: - return RTAS_OUT_PARAM_ERROR; - } - - ret = vfio_eeh_as_op(&sphb->iommu_as, op); - if (ret < 0) { - return RTAS_OUT_HW_ERROR; - } - - return RTAS_OUT_SUCCESS; -} - -int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb) -{ - int ret; - - ret = vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_CONFIGURE); - if (ret < 0) { - return RTAS_OUT_PARAM_ERROR; - } - - return RTAS_OUT_SUCCESS; -} - -static void spapr_phb_vfio_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->props = spapr_phb_vfio_properties; -} - -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, -}; - -static void spapr_pci_vfio_register_types(void) -{ - type_register_static(&spapr_phb_vfio_info); -} - -type_init(spapr_pci_vfio_register_types) diff --git a/qemu/hw/ppc/spapr_rng.c b/qemu/hw/ppc/spapr_rng.c deleted file mode 100644 index 80515eb54..000000000 --- a/qemu/hw/ppc/spapr_rng.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * 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 . - */ - -#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 deleted file mode 100644 index f07325831..000000000 --- a/qemu/hw/ppc/spapr_rtas.c +++ /dev/null @@ -1,785 +0,0 @@ -/* - * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator - * - * Hypercall based emulated RTAS - * - * Copyright (c) 2010-2011 David Gibson, IBM Corporation. - * - * 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 "cpu.h" -#include "sysemu/sysemu.h" -#include "sysemu/char.h" -#include "hw/qdev.h" -#include "sysemu/device_tree.h" -#include "sysemu/cpus.h" - -#include "hw/ppc/spapr.h" -#include "hw/ppc/spapr_vio.h" -#include "qapi-event.h" -#include "hw/boards.h" - -#include -#include "hw/ppc/spapr_drc.h" -#include "qemu/cutils.h" - -/* #define DEBUG_SPAPR */ - -#ifdef DEBUG_SPAPR -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - -static sPAPRConfigureConnectorState *spapr_ccs_find(sPAPRMachineState *spapr, - uint32_t drc_index) -{ - sPAPRConfigureConnectorState *ccs = NULL; - - QTAILQ_FOREACH(ccs, &spapr->ccs_list, next) { - if (ccs->drc_index == drc_index) { - break; - } - } - - return ccs; -} - -static void spapr_ccs_add(sPAPRMachineState *spapr, - sPAPRConfigureConnectorState *ccs) -{ - g_assert(!spapr_ccs_find(spapr, ccs->drc_index)); - QTAILQ_INSERT_HEAD(&spapr->ccs_list, ccs, next); -} - -static void spapr_ccs_remove(sPAPRMachineState *spapr, - sPAPRConfigureConnectorState *ccs) -{ - QTAILQ_REMOVE(&spapr->ccs_list, ccs, next); - g_free(ccs); -} - -void spapr_ccs_reset_hook(void *opaque) -{ - sPAPRMachineState *spapr = opaque; - sPAPRConfigureConnectorState *ccs, *ccs_tmp; - - QTAILQ_FOREACH_SAFE(ccs, &spapr->ccs_list, next, ccs_tmp) { - spapr_ccs_remove(spapr, ccs); - } -} - -static void rtas_display_character(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - uint8_t c = rtas_ld(args, 0); - VIOsPAPRDevice *sdev = vty_lookup(spapr, 0); - - if (!sdev) { - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - } else { - vty_putchars(sdev, &c, sizeof(c)); - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - } -} - -static void rtas_power_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, target_ulong args, - uint32_t nret, target_ulong rets) -{ - if (nargs != 2 || nret != 1) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - qemu_system_shutdown_request(); - cpu_stop_current(); - rtas_st(rets, 0, RTAS_OUT_SUCCESS); -} - -static void rtas_system_reboot(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - if (nargs != 0 || nret != 1) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - qemu_system_reset_request(); - rtas_st(rets, 0, RTAS_OUT_SUCCESS); -} - -static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, - sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - target_ulong id; - PowerPCCPU *cpu; - - if (nargs != 1 || nret != 2) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - id = rtas_ld(args, 0); - cpu = ppc_get_vcpu_by_dt_id(id); - if (cpu != NULL) { - if (CPU(cpu)->halted) { - rtas_st(rets, 1, 0); - } else { - rtas_st(rets, 1, 2); - } - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - return; - } - - /* Didn't find a matching cpu */ - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); -} - -static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - target_ulong id, start, r3; - PowerPCCPU *cpu; - - if (nargs != 3 || nret != 1) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - id = rtas_ld(args, 0); - start = rtas_ld(args, 1); - r3 = rtas_ld(args, 2); - - cpu = ppc_get_vcpu_by_dt_id(id); - if (cpu != NULL) { - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - - if (!cs->halted) { - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - - /* This will make sure qemu state is up to date with kvm, and - * mark it dirty so our changes get flushed back before the - * new cpu enters */ - kvm_cpu_synchronize_state(cs); - - env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME); - env->nip = start; - env->gpr[3] = r3; - cs->halted = 0; - - qemu_cpu_kick(cs); - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - return; - } - - /* Didn't find a matching cpu */ - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); -} - -static void rtas_stop_self(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - - cs->halted = 1; - qemu_cpu_kick(cs); - /* - * While stopping a CPU, the guest calls H_CPPR which - * effectively disables interrupts on XICS level. - * However decrementer interrupts in TCG can still - * wake the CPU up so here we disable interrupts in MSR - * as well. - * As rtas_start_cpu() resets the whole MSR anyway, there is - * no need to bother with specific bits, we just clear it. - */ - 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, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - target_ulong parameter = rtas_ld(args, 0); - target_ulong buffer = rtas_ld(args, 1); - target_ulong length = rtas_ld(args, 2); - target_ulong ret; - - switch (parameter) { - case RTAS_SYSPARM_SPLPAR_CHARACTERISTICS: { - 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; - - ret = sysparm_st(buffer, length, ¶m_val, sizeof(param_val)); - break; - } - case RTAS_SYSPARM_UUID: - ret = sysparm_st(buffer, length, qemu_uuid, (qemu_uuid_set ? 16 : 0)); - break; - default: - ret = RTAS_OUT_NOT_SUPPORTED; - } - - rtas_st(rets, 0, ret); -} - -static void rtas_ibm_set_system_parameter(PowerPCCPU *cpu, - sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - target_ulong parameter = rtas_ld(args, 0); - target_ulong ret = RTAS_OUT_NOT_SUPPORTED; - - switch (parameter) { - case RTAS_SYSPARM_SPLPAR_CHARACTERISTICS: - case RTAS_SYSPARM_DIAGNOSTICS_RUN_MODE: - case RTAS_SYSPARM_UUID: - ret = RTAS_OUT_NOT_AUTHORIZED; - break; - } - - rtas_st(rets, 0, ret); -} - -static void rtas_ibm_os_term(PowerPCCPU *cpu, - sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - target_ulong ret = 0; - - qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, &error_abort); - - rtas_st(rets, 0, ret); -} - -static void rtas_set_power_level(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, uint32_t nret, - target_ulong rets) -{ - int32_t power_domain; - - if (nargs != 2 || nret != 2) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - /* we currently only use a single, "live insert" powerdomain for - * hotplugged/dlpar'd resources, so the power is always live/full (100) - */ - power_domain = rtas_ld(args, 0); - if (power_domain != -1) { - rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); - return; - } - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - rtas_st(rets, 1, 100); -} - -static void rtas_get_power_level(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, uint32_t nret, - target_ulong rets) -{ - int32_t power_domain; - - if (nargs != 1 || nret != 2) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - /* we currently only use a single, "live insert" powerdomain for - * hotplugged/dlpar'd resources, so the power is always live/full (100) - */ - power_domain = rtas_ld(args, 0); - if (power_domain != -1) { - rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); - return; - } - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - rtas_st(rets, 1, 100); -} - -static bool sensor_type_is_dr(uint32_t sensor_type) -{ - switch (sensor_type) { - case RTAS_SENSOR_TYPE_ISOLATION_STATE: - case RTAS_SENSOR_TYPE_DR: - case RTAS_SENSOR_TYPE_ALLOCATION_STATE: - return true; - } - - return false; -} - -static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, uint32_t nret, - target_ulong rets) -{ - 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) { - ret = RTAS_OUT_PARAM_ERROR; - goto out; - } - - sensor_type = rtas_ld(args, 0); - sensor_index = rtas_ld(args, 1); - sensor_state = rtas_ld(args, 2); - - if (!sensor_type_is_dr(sensor_type)) { - goto out_unimplemented; - } - - /* if this is a DR sensor we can assume sensor_index == drc_index */ - drc = spapr_dr_connector_by_index(sensor_index); - if (!drc) { - DPRINTF("rtas_set_indicator: invalid sensor/DRC index: %xh\n", - sensor_index); - ret = RTAS_OUT_PARAM_ERROR; - goto out; - } - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - - switch (sensor_type) { - case RTAS_SENSOR_TYPE_ISOLATION_STATE: - /* if the guest is configuring a device attached to this - * DRC, we should reset the configuration state at this - * point since it may no longer be reliable (guest released - * device and needs to start over, or unplug occurred so - * the FDT is no longer valid) - */ - if (sensor_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) { - sPAPRConfigureConnectorState *ccs = spapr_ccs_find(spapr, - sensor_index); - if (ccs) { - spapr_ccs_remove(spapr, ccs); - } - } - ret = drck->set_isolation_state(drc, sensor_state); - break; - case RTAS_SENSOR_TYPE_DR: - ret = drck->set_indicator_state(drc, sensor_state); - break; - case RTAS_SENSOR_TYPE_ALLOCATION_STATE: - ret = drck->set_allocation_state(drc, sensor_state); - break; - default: - goto out_unimplemented; - } - -out: - rtas_st(rets, 0, ret); - return; - -out_unimplemented: - /* currently only DR-related sensors are implemented */ - DPRINTF("rtas_set_indicator: sensor/indicator not implemented: %d\n", - sensor_type); - rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); -} - -static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, uint32_t nret, - target_ulong rets) -{ - uint32_t sensor_type; - uint32_t sensor_index; - uint32_t sensor_state = 0; - sPAPRDRConnector *drc; - sPAPRDRConnectorClass *drck; - uint32_t ret = RTAS_OUT_SUCCESS; - - if (nargs != 2 || nret != 2) { - ret = RTAS_OUT_PARAM_ERROR; - goto out; - } - - sensor_type = rtas_ld(args, 0); - sensor_index = rtas_ld(args, 1); - - if (sensor_type != RTAS_SENSOR_TYPE_ENTITY_SENSE) { - /* currently only DR-related sensors are implemented */ - DPRINTF("rtas_get_sensor_state: sensor/indicator not implemented: %d\n", - sensor_type); - 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); - ret = RTAS_OUT_PARAM_ERROR; - goto out; - } - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - ret = drck->entity_sense(drc, &sensor_state); - -out: - rtas_st(rets, 0, ret); - rtas_st(rets, 1, sensor_state); -} - -/* configure-connector work area offsets, int32_t units for field - * indexes, bytes for field offset/len values. - * - * as documented by PAPR+ v2.7, 13.5.3.5 - */ -#define CC_IDX_NODE_NAME_OFFSET 2 -#define CC_IDX_PROP_NAME_OFFSET 2 -#define CC_IDX_PROP_LEN 3 -#define CC_IDX_PROP_DATA_OFFSET 4 -#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, - target_ulong args, uint32_t nret, - target_ulong rets) -{ - uint64_t wa_addr; - uint64_t wa_offset; - uint32_t drc_index; - sPAPRDRConnector *drc; - sPAPRDRConnectorClass *drck; - sPAPRConfigureConnectorState *ccs; - sPAPRDRCCResponse resp = SPAPR_DR_CC_RESPONSE_CONTINUE; - int rc; - const void *fdt; - - if (nargs != 2 || nret != 1) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - wa_addr = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 0); - - drc_index = rtas_ld(wa_addr, 0); - drc = spapr_dr_connector_by_index(drc_index); - if (!drc) { - DPRINTF("rtas_ibm_configure_connector: invalid DRC index: %xh\n", - drc_index); - rc = RTAS_OUT_PARAM_ERROR; - goto out; - } - - 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) { - ccs = g_new0(sPAPRConfigureConnectorState, 1); - (void)drck->get_fdt(drc, &ccs->fdt_offset); - ccs->drc_index = drc_index; - spapr_ccs_add(spapr, ccs); - } - - do { - uint32_t tag; - const char *name; - const struct fdt_property *prop; - int fdt_offset_next, prop_len; - - tag = fdt_next_tag(fdt, ccs->fdt_offset, &fdt_offset_next); - - switch (tag) { - case FDT_BEGIN_NODE: - ccs->fdt_depth++; - name = fdt_get_name(fdt, ccs->fdt_offset, NULL); - - /* 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); - configure_connector_st(wa_addr, wa_offset, name, strlen(name) + 1); - resp = SPAPR_DR_CC_RESPONSE_NEXT_CHILD; - break; - case FDT_END_NODE: - ccs->fdt_depth--; - if (ccs->fdt_depth == 0) { - /* done sending the device tree, don't need to track - * the state anymore - */ - drck->set_configured(drc); - spapr_ccs_remove(spapr, ccs); - ccs = NULL; - resp = SPAPR_DR_CC_RESPONSE_SUCCESS; - } else { - resp = SPAPR_DR_CC_RESPONSE_PREV_PARENT; - } - break; - case FDT_PROP: - prop = fdt_get_property_by_offset(fdt, ccs->fdt_offset, - &prop_len); - name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); - - /* 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); - 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 - * name string - */ - 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); - configure_connector_st(wa_addr, wa_offset, prop->data, prop_len); - resp = SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY; - break; - case FDT_END: - resp = SPAPR_DR_CC_RESPONSE_ERROR; - default: - /* keep seeking for an actionable tag */ - break; - } - if (ccs) { - ccs->fdt_offset = fdt_offset_next; - } - } while (resp == SPAPR_DR_CC_RESPONSE_CONTINUE); - - rc = resp; -out: - rtas_st(rets, 0, rc); -} - -static struct rtas_call { - const char *name; - spapr_rtas_fn fn; -} rtas_table[RTAS_TOKEN_MAX - RTAS_TOKEN_BASE]; - -target_ulong spapr_rtas_call(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, target_ulong args, - uint32_t nret, target_ulong rets) -{ - if ((token >= RTAS_TOKEN_BASE) && (token < RTAS_TOKEN_MAX)) { - struct rtas_call *call = rtas_table + (token - RTAS_TOKEN_BASE); - - if (call->fn) { - call->fn(cpu, spapr, token, nargs, args, nret, rets); - return H_SUCCESS; - } - } - - /* HACK: Some Linux early debug code uses RTAS display-character, - * but assumes the token value is 0xa (which it is on some real - * machines) without looking it up in the device tree. This - * special case makes this work */ - if (token == 0xa) { - rtas_display_character(cpu, spapr, 0xa, nargs, args, nret, rets); - return H_SUCCESS; - } - - hcall_dprintf("Unknown RTAS token 0x%x\n", token); - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return H_PARAMETER; -} - -void spapr_rtas_register(int token, const char *name, spapr_rtas_fn fn) -{ - assert((token >= RTAS_TOKEN_BASE) && (token < RTAS_TOKEN_MAX)); - - token -= RTAS_TOKEN_BASE; - - assert(!rtas_table[token].name); - - rtas_table[token].name = name; - rtas_table[token].fn = fn; -} - -int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, - hwaddr rtas_size) -{ - int ret; - 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) { - fprintf(stderr, "Couldn't add RTAS reserve entry: %s\n", - fdt_strerror(ret)); - return ret; - } - - ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-base", - rtas_addr); - if (ret < 0) { - fprintf(stderr, "Couldn't add linux,rtas-base property: %s\n", - fdt_strerror(ret)); - return ret; - } - - ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-entry", - rtas_addr); - if (ret < 0) { - fprintf(stderr, "Couldn't add linux,rtas-entry property: %s\n", - fdt_strerror(ret)); - return ret; - } - - ret = qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-size", - rtas_size); - if (ret < 0) { - fprintf(stderr, "Couldn't add rtas-size property: %s\n", - fdt_strerror(ret)); - return ret; - } - - for (i = 0; i < RTAS_TOKEN_MAX - RTAS_TOKEN_BASE; i++) { - struct rtas_call *call = &rtas_table[i]; - - if (!call->name) { - continue; - } - - ret = qemu_fdt_setprop_cell(fdt, "/rtas", call->name, - i + RTAS_TOKEN_BASE); - if (ret < 0) { - fprintf(stderr, "Couldn't add rtas token for %s: %s\n", - call->name, fdt_strerror(ret)); - return ret; - } - - } - - 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); - ret = qemu_fdt_setprop(fdt, "/rtas", "ibm,lrdr-capacity", lrdr_capacity, - sizeof(lrdr_capacity)); - if (ret < 0) { - fprintf(stderr, "Couldn't add ibm,lrdr-capacity rtas property\n"); - return ret; - } - - return 0; -} - -static void core_rtas_register_types(void) -{ - spapr_rtas_register(RTAS_DISPLAY_CHARACTER, "display-character", - rtas_display_character); - spapr_rtas_register(RTAS_POWER_OFF, "power-off", rtas_power_off); - spapr_rtas_register(RTAS_SYSTEM_REBOOT, "system-reboot", - rtas_system_reboot); - spapr_rtas_register(RTAS_QUERY_CPU_STOPPED_STATE, "query-cpu-stopped-state", - rtas_query_cpu_stopped_state); - spapr_rtas_register(RTAS_START_CPU, "start-cpu", rtas_start_cpu); - spapr_rtas_register(RTAS_STOP_SELF, "stop-self", rtas_stop_self); - spapr_rtas_register(RTAS_IBM_GET_SYSTEM_PARAMETER, - "ibm,get-system-parameter", - rtas_ibm_get_system_parameter); - spapr_rtas_register(RTAS_IBM_SET_SYSTEM_PARAMETER, - "ibm,set-system-parameter", - rtas_ibm_set_system_parameter); - spapr_rtas_register(RTAS_IBM_OS_TERM, "ibm,os-term", - rtas_ibm_os_term); - spapr_rtas_register(RTAS_SET_POWER_LEVEL, "set-power-level", - rtas_set_power_level); - spapr_rtas_register(RTAS_GET_POWER_LEVEL, "get-power-level", - rtas_get_power_level); - spapr_rtas_register(RTAS_SET_INDICATOR, "set-indicator", - rtas_set_indicator); - spapr_rtas_register(RTAS_GET_SENSOR_STATE, "get-sensor-state", - rtas_get_sensor_state); - spapr_rtas_register(RTAS_IBM_CONFIGURE_CONNECTOR, "ibm,configure-connector", - rtas_ibm_configure_connector); -} - -type_init(core_rtas_register_types) diff --git a/qemu/hw/ppc/spapr_rtc.c b/qemu/hw/ppc/spapr_rtc.c deleted file mode 100644 index 3a17ac42e..000000000 --- a/qemu/hw/ppc/spapr_rtc.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator - * - * RTAS Real Time Clock - * - * Copyright (c) 2010-2011 David Gibson, IBM Corporation. - * Copyright 2014 David Gibson, Red Hat. - * - * 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 "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) - -typedef struct sPAPRRTCState sPAPRRTCState; -struct sPAPRRTCState { - /*< private >*/ - SysBusDevice parent_obj; - int64_t ns_offset; -}; - -void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns) -{ - sPAPRRTCState *rtc = SPAPR_RTC(dev); - int64_t host_ns = qemu_clock_get_ns(rtc_clock); - int64_t guest_ns; - time_t guest_s; - - assert(rtc); - - guest_ns = host_ns + rtc->ns_offset; - guest_s = guest_ns / NANOSECONDS_PER_SECOND; - - if (tm) { - gmtime_r(&guest_s, tm); - } - if (ns) { - *ns = guest_ns; - } -} - -int spapr_rtc_import_offset(DeviceState *dev, int64_t legacy_offset) -{ - sPAPRRTCState *rtc; - - if (!dev) { - return -ENODEV; - } - - rtc = SPAPR_RTC(dev); - - rtc->ns_offset = legacy_offset * NANOSECONDS_PER_SECOND; - - return 0; -} - -static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - struct tm tm; - uint32_t ns; - - if ((nargs != 0) || (nret != 8)) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - if (!spapr->rtc) { - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - - spapr_rtc_read(spapr->rtc, &tm, &ns); - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - rtas_st(rets, 1, tm.tm_year + 1900); - rtas_st(rets, 2, tm.tm_mon + 1); - rtas_st(rets, 3, tm.tm_mday); - rtas_st(rets, 4, tm.tm_hour); - rtas_st(rets, 5, tm.tm_min); - rtas_st(rets, 6, tm.tm_sec); - rtas_st(rets, 7, ns); -} - -static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, uint32_t nargs, - target_ulong args, - uint32_t nret, target_ulong rets) -{ - sPAPRRTCState *rtc; - struct tm tm; - time_t new_s; - int64_t host_ns; - - if ((nargs != 7) || (nret != 1)) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - if (!spapr->rtc) { - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - - tm.tm_year = rtas_ld(args, 0) - 1900; - tm.tm_mon = rtas_ld(args, 1) - 1; - tm.tm_mday = rtas_ld(args, 2); - tm.tm_hour = rtas_ld(args, 3); - tm.tm_min = rtas_ld(args, 4); - tm.tm_sec = rtas_ld(args, 5); - - new_s = mktimegm(&tm); - if (new_s == -1) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - /* Generate a monitor event for the change */ - qapi_event_send_rtc_change(qemu_timedate_diff(&tm), &error_abort); - - rtc = SPAPR_RTC(spapr->rtc); - - host_ns = qemu_clock_get_ns(rtc_clock); - - rtc->ns_offset = (new_s * NANOSECONDS_PER_SECOND) - host_ns; - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); -} - -static void spapr_rtc_qom_date(Object *obj, struct tm *current_tm, Error **errp) -{ - spapr_rtc_read(DEVICE(obj), current_tm, NULL); -} - -static void spapr_rtc_realize(DeviceState *dev, Error **errp) -{ - sPAPRRTCState *rtc = SPAPR_RTC(dev); - struct tm tm; - time_t host_s; - int64_t rtc_ns; - - /* Initialize the RTAS RTC from host time */ - - qemu_get_timedate(&tm, 0); - host_s = mktimegm(&tm); - rtc_ns = qemu_clock_get_ns(rtc_clock); - rtc->ns_offset = host_s * NANOSECONDS_PER_SECOND - rtc_ns; - - object_property_add_tm(OBJECT(rtc), "date", spapr_rtc_qom_date, NULL); -} - -static const VMStateDescription vmstate_spapr_rtc = { - .name = "spapr/rtc", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT64(ns_offset, sPAPRRTCState), - VMSTATE_END_OF_LIST() - }, -}; - -static void spapr_rtc_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = spapr_rtc_realize; - dc->vmsd = &vmstate_spapr_rtc; - - spapr_rtas_register(RTAS_GET_TIME_OF_DAY, "get-time-of-day", - rtas_get_time_of_day); - spapr_rtas_register(RTAS_SET_TIME_OF_DAY, "set-time-of-day", - rtas_set_time_of_day); -} - -static const TypeInfo spapr_rtc_info = { - .name = TYPE_SPAPR_RTC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(sPAPRRTCState), - .class_init = spapr_rtc_class_init, -}; - -static void spapr_rtc_register_types(void) -{ - type_register_static(&spapr_rtc_info); -} -type_init(spapr_rtc_register_types) diff --git a/qemu/hw/ppc/spapr_vio.c b/qemu/hw/ppc/spapr_vio.c deleted file mode 100644 index 8aa021fde..000000000 --- a/qemu/hw/ppc/spapr_vio.c +++ /dev/null @@ -1,706 +0,0 @@ -/* - * QEMU sPAPR VIO code - * - * Copyright (c) 2010 David Gibson, IBM Corporation - * Based on the s390 virtio bus code: - * Copyright (c) 2009 Alexander Graf - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/hw.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" -#include "hw/loader.h" -#include "elf.h" -#include "hw/sysbus.h" -#include "sysemu/kvm.h" -#include "sysemu/device_tree.h" -#include "kvm_ppc.h" - -#include "hw/ppc/spapr.h" -#include "hw/ppc/spapr_vio.h" -#include "hw/ppc/xics.h" - -#include - -/* #define DEBUG_SPAPR */ - -#ifdef DEBUG_SPAPR -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - -static Property spapr_vio_props[] = { - DEFINE_PROP_UINT32("irq", VIOsPAPRDevice, irq, 0), \ - DEFINE_PROP_END_OF_LIST(), -}; - -static char *spapr_vio_get_dev_name(DeviceState *qdev) -{ - VIOsPAPRDevice *dev = VIO_SPAPR_DEVICE(qdev); - VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); - char *name; - - /* Device tree style name device@reg */ - name = g_strdup_printf("%s@%x", pc->dt_name, dev->reg); - - return name; -} - -static void spapr_vio_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - - k->get_dev_path = spapr_vio_get_dev_name; - k->get_fw_dev_path = spapr_vio_get_dev_name; -} - -static const TypeInfo spapr_vio_bus_info = { - .name = TYPE_SPAPR_VIO_BUS, - .parent = TYPE_BUS, - .class_init = spapr_vio_bus_class_init, - .instance_size = sizeof(VIOsPAPRBus), -}; - -VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg) -{ - BusChild *kid; - VIOsPAPRDevice *dev = NULL; - - QTAILQ_FOREACH(kid, &bus->bus.children, sibling) { - dev = (VIOsPAPRDevice *)kid->child; - if (dev->reg == reg) { - return dev; - } - } - - return NULL; -} - -static int vio_make_devnode(VIOsPAPRDevice *dev, - void *fdt) -{ - VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); - int vdevice_off, node_off, ret; - char *dt_name; - - vdevice_off = fdt_path_offset(fdt, "/vdevice"); - if (vdevice_off < 0) { - return vdevice_off; - } - - dt_name = spapr_vio_get_dev_name(DEVICE(dev)); - node_off = fdt_add_subnode(fdt, vdevice_off, dt_name); - g_free(dt_name); - if (node_off < 0) { - return node_off; - } - - ret = fdt_setprop_cell(fdt, node_off, "reg", dev->reg); - if (ret < 0) { - return ret; - } - - if (pc->dt_type) { - ret = fdt_setprop_string(fdt, node_off, "device_type", - pc->dt_type); - if (ret < 0) { - return ret; - } - } - - if (pc->dt_compatible) { - ret = fdt_setprop_string(fdt, node_off, "compatible", - pc->dt_compatible); - if (ret < 0) { - return ret; - } - } - - if (dev->irq) { - uint32_t ints_prop[] = {cpu_to_be32(dev->irq), 0}; - - ret = fdt_setprop(fdt, node_off, "interrupts", ints_prop, - sizeof(ints_prop)); - if (ret < 0) { - return ret; - } - } - - ret = spapr_tcet_dma_dt(fdt, node_off, "ibm,my-dma-window", dev->tcet); - if (ret < 0) { - return ret; - } - - if (pc->devnode) { - ret = (pc->devnode)(dev, fdt, node_off); - if (ret < 0) { - return ret; - } - } - - return node_off; -} - -/* - * CRQ handling - */ -static target_ulong h_reg_crq(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong reg = args[0]; - target_ulong queue_addr = args[1]; - target_ulong queue_len = args[2]; - VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - - if (!dev) { - hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg); - return H_PARAMETER; - } - - /* We can't grok a queue size bigger than 256M for now */ - if (queue_len < 0x1000 || queue_len > 0x10000000) { - hcall_dprintf("Queue size too small or too big (0x" TARGET_FMT_lx - ")\n", queue_len); - return H_PARAMETER; - } - - /* Check queue alignment */ - if (queue_addr & 0xfff) { - hcall_dprintf("Queue not aligned (0x" TARGET_FMT_lx ")\n", queue_addr); - return H_PARAMETER; - } - - /* Check if device supports CRQs */ - if (!dev->crq.SendFunc) { - hcall_dprintf("Device does not support CRQ\n"); - return H_NOT_FOUND; - } - - /* Already a queue ? */ - if (dev->crq.qsize) { - hcall_dprintf("CRQ already registered\n"); - return H_RESOURCE; - } - dev->crq.qladdr = queue_addr; - dev->crq.qsize = queue_len; - dev->crq.qnext = 0; - - DPRINTF("CRQ for dev 0x" TARGET_FMT_lx " registered at 0x" - TARGET_FMT_lx "/0x" TARGET_FMT_lx "\n", - reg, queue_addr, queue_len); - return H_SUCCESS; -} - -static target_ulong free_crq(VIOsPAPRDevice *dev) -{ - dev->crq.qladdr = 0; - dev->crq.qsize = 0; - dev->crq.qnext = 0; - - DPRINTF("CRQ for dev 0x%" PRIx32 " freed\n", dev->reg); - - return H_SUCCESS; -} - -static target_ulong h_free_crq(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong reg = args[0]; - VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - - if (!dev) { - hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg); - return H_PARAMETER; - } - - return free_crq(dev); -} - -static target_ulong h_send_crq(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong reg = args[0]; - target_ulong msg_hi = args[1]; - target_ulong msg_lo = args[2]; - VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - uint64_t crq_mangle[2]; - - if (!dev) { - hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg); - return H_PARAMETER; - } - crq_mangle[0] = cpu_to_be64(msg_hi); - crq_mangle[1] = cpu_to_be64(msg_lo); - - if (dev->crq.SendFunc) { - return dev->crq.SendFunc(dev, (uint8_t *)crq_mangle); - } - - return H_HARDWARE; -} - -static target_ulong h_enable_crq(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong reg = args[0]; - VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - - if (!dev) { - hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg); - return H_PARAMETER; - } - - return 0; -} - -/* Returns negative error, 0 success, or positive: queue full */ -int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq) -{ - int rc; - uint8_t byte; - - if (!dev->crq.qsize) { - fprintf(stderr, "spapr_vio_send_creq on uninitialized queue\n"); - return -1; - } - - /* Maybe do a fast path for KVM just writing to the pages */ - rc = spapr_vio_dma_read(dev, dev->crq.qladdr + dev->crq.qnext, &byte, 1); - if (rc) { - return rc; - } - if (byte != 0) { - return 1; - } - - rc = spapr_vio_dma_write(dev, dev->crq.qladdr + dev->crq.qnext + 8, - &crq[8], 8); - if (rc) { - return rc; - } - - kvmppc_eieio(); - - rc = spapr_vio_dma_write(dev, dev->crq.qladdr + dev->crq.qnext, crq, 8); - if (rc) { - return rc; - } - - dev->crq.qnext = (dev->crq.qnext + 16) % dev->crq.qsize; - - if (dev->signal_state & 1) { - qemu_irq_pulse(spapr_vio_qirq(dev)); - } - - return 0; -} - -/* "quiesce" handling */ - -static void spapr_vio_quiesce_one(VIOsPAPRDevice *dev) -{ - if (dev->tcet) { - device_reset(DEVICE(dev->tcet)); - } - free_crq(dev); -} - -void spapr_vio_set_bypass(VIOsPAPRDevice *dev, bool bypass) -{ - if (!dev->tcet) { - return; - } - - memory_region_set_enabled(&dev->mrbypass, bypass); - memory_region_set_enabled(spapr_tce_get_iommu(dev->tcet), !bypass); - - dev->tcet->bypass = bypass; -} - -static void rtas_set_tce_bypass(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, - uint32_t nargs, target_ulong args, - uint32_t nret, target_ulong rets) -{ - VIOsPAPRBus *bus = spapr->vio_bus; - VIOsPAPRDevice *dev; - uint32_t unit, enable; - - if (nargs != 2) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - unit = rtas_ld(args, 0); - enable = rtas_ld(args, 1); - dev = spapr_vio_find_by_reg(bus, unit); - if (!dev) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - if (!dev->tcet) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - spapr_vio_set_bypass(dev, !!enable); - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); -} - -static void rtas_quiesce(PowerPCCPU *cpu, sPAPRMachineState *spapr, - uint32_t token, - uint32_t nargs, target_ulong args, - uint32_t nret, target_ulong rets) -{ - VIOsPAPRBus *bus = spapr->vio_bus; - BusChild *kid; - VIOsPAPRDevice *dev = NULL; - - if (nargs != 0) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; - } - - QTAILQ_FOREACH(kid, &bus->bus.children, sibling) { - dev = (VIOsPAPRDevice *)kid->child; - spapr_vio_quiesce_one(dev); - } - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); -} - -static VIOsPAPRDevice *reg_conflict(VIOsPAPRDevice *dev) -{ - VIOsPAPRBus *bus = SPAPR_VIO_BUS(dev->qdev.parent_bus); - BusChild *kid; - VIOsPAPRDevice *other; - - /* - * Check for a device other than the given one which is already - * using the requested address. We have to open code this because - * the given dev might already be in the list. - */ - QTAILQ_FOREACH(kid, &bus->bus.children, sibling) { - other = VIO_SPAPR_DEVICE(kid->child); - - if (other != dev && other->reg == dev->reg) { - return other; - } - } - - return 0; -} - -static void spapr_vio_busdev_reset(DeviceState *qdev) -{ - VIOsPAPRDevice *dev = VIO_SPAPR_DEVICE(qdev); - VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); - - /* Shut down the request queue and TCEs if necessary */ - spapr_vio_quiesce_one(dev); - - dev->signal_state = 0; - - spapr_vio_set_bypass(dev, false); - if (pc->reset) { - pc->reset(dev); - } -} - -static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) -{ - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev; - VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); - char *id; - Error *local_err = NULL; - - if (dev->reg != -1) { - /* - * Explicitly assigned address, just verify that no-one else - * is using it. other mechanism). We have to open code this - * rather than using spapr_vio_find_by_reg() because sdev - * itself is already in the list. - */ - VIOsPAPRDevice *other = reg_conflict(dev); - - if (other) { - error_setg(errp, "%s and %s devices conflict at address %#x", - object_get_typename(OBJECT(qdev)), - object_get_typename(OBJECT(&other->qdev)), - dev->reg); - return; - } - } else { - /* Need to assign an address */ - VIOsPAPRBus *bus = SPAPR_VIO_BUS(dev->qdev.parent_bus); - - do { - dev->reg = bus->next_reg++; - } while (reg_conflict(dev)); - } - - /* Don't overwrite ids assigned on the command line */ - if (!dev->qdev.id) { - id = spapr_vio_get_dev_name(DEVICE(dev)); - dev->qdev.id = id; - } - - dev->irq = xics_alloc(spapr->icp, 0, dev->irq, false, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - if (pc->rtce_window_size) { - uint32_t liobn = SPAPR_VIO_LIOBN(dev->reg); - - memory_region_init(&dev->mrroot, OBJECT(dev), "iommu-spapr-root", - ram_size); - memory_region_init_alias(&dev->mrbypass, OBJECT(dev), - "iommu-spapr-bypass", get_system_memory(), - 0, ram_size); - memory_region_add_subregion_overlap(&dev->mrroot, 0, &dev->mrbypass, 1); - address_space_init(&dev->as, &dev->mrroot, qdev->id); - - dev->tcet = spapr_tce_new_table(qdev, liobn, - 0, - SPAPR_TCE_PAGE_SHIFT, - pc->rtce_window_size >> - SPAPR_TCE_PAGE_SHIFT, false); - dev->tcet->vdev = dev; - memory_region_add_subregion_overlap(&dev->mrroot, 0, - spapr_tce_get_iommu(dev->tcet), 2); - } - - pc->realize(dev, errp); -} - -static target_ulong h_vio_signal(PowerPCCPU *cpu, sPAPRMachineState *spapr, - target_ulong opcode, - target_ulong *args) -{ - target_ulong reg = args[0]; - target_ulong mode = args[1]; - VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - VIOsPAPRDeviceClass *pc; - - if (!dev) { - return H_PARAMETER; - } - - pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); - - if (mode & ~pc->signal_mask) { - return H_PARAMETER; - } - - dev->signal_state = mode; - - return H_SUCCESS; -} - -VIOsPAPRBus *spapr_vio_bus_init(void) -{ - VIOsPAPRBus *bus; - BusState *qbus; - DeviceState *dev; - - /* Create bridge device */ - 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 = SPAPR_VIO_BUS(qbus); - bus->next_reg = 0x71000000; - - /* hcall-vio */ - spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal); - - /* hcall-crq */ - spapr_register_hypercall(H_REG_CRQ, h_reg_crq); - spapr_register_hypercall(H_FREE_CRQ, h_free_crq); - spapr_register_hypercall(H_SEND_CRQ, h_send_crq); - spapr_register_hypercall(H_ENABLE_CRQ, h_enable_crq); - - /* RTAS calls */ - spapr_rtas_register(RTAS_IBM_SET_TCE_BYPASS, "ibm,set-tce-bypass", - rtas_set_tce_bypass); - spapr_rtas_register(RTAS_QUIESCE, "quiesce", rtas_quiesce); - - return bus; -} - -/* Represents sPAPR hcall VIO devices */ - -static int spapr_vio_bridge_init(SysBusDevice *dev) -{ - /* nothing */ - return 0; -} - -static void spapr_vio_bridge_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->fw_name = "vdevice"; - k->init = spapr_vio_bridge_init; -} - -static const TypeInfo spapr_vio_bridge_info = { - .name = TYPE_SPAPR_VIO_BRIDGE, - .parent = TYPE_SYS_BUS_DEVICE, - .class_init = spapr_vio_bridge_class_init, -}; - -const VMStateDescription vmstate_spapr_vio = { - .name = "spapr_vio", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - /* Sanity check */ - VMSTATE_UINT32_EQUAL(reg, VIOsPAPRDevice), - VMSTATE_UINT32_EQUAL(irq, VIOsPAPRDevice), - - /* General VIO device state */ - VMSTATE_UINTTL(signal_state, VIOsPAPRDevice), - VMSTATE_UINT64(crq.qladdr, VIOsPAPRDevice), - VMSTATE_UINT32(crq.qsize, VIOsPAPRDevice), - VMSTATE_UINT32(crq.qnext, VIOsPAPRDevice), - - VMSTATE_END_OF_LIST() - }, -}; - -static void vio_spapr_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - k->realize = spapr_vio_busdev_realize; - k->reset = spapr_vio_busdev_reset; - k->bus_type = TYPE_SPAPR_VIO_BUS; - k->props = spapr_vio_props; -} - -static const TypeInfo spapr_vio_type_info = { - .name = TYPE_VIO_SPAPR_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(VIOsPAPRDevice), - .abstract = true, - .class_size = sizeof(VIOsPAPRDeviceClass), - .class_init = vio_spapr_device_class_init, -}; - -static void spapr_vio_register_types(void) -{ - type_register_static(&spapr_vio_bus_info); - type_register_static(&spapr_vio_bridge_info); - type_register_static(&spapr_vio_type_info); -} - -type_init(spapr_vio_register_types) - -static int compare_reg(const void *p1, const void *p2) -{ - VIOsPAPRDevice const *dev1, *dev2; - - dev1 = (VIOsPAPRDevice *)*(DeviceState **)p1; - dev2 = (VIOsPAPRDevice *)*(DeviceState **)p2; - - if (dev1->reg < dev2->reg) { - return -1; - } - if (dev1->reg == dev2->reg) { - return 0; - } - - /* dev1->reg > dev2->reg */ - return 1; -} - -int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt) -{ - DeviceState *qdev, **qdevs; - BusChild *kid; - int i, num, ret = 0; - - /* Count qdevs on the bus list */ - num = 0; - QTAILQ_FOREACH(kid, &bus->bus.children, sibling) { - num++; - } - - /* Copy out into an array of pointers */ - qdevs = g_malloc(sizeof(qdev) * num); - num = 0; - QTAILQ_FOREACH(kid, &bus->bus.children, sibling) { - qdevs[num++] = kid->child; - } - - /* Sort the array */ - qsort(qdevs, num, sizeof(qdev), compare_reg); - - /* Hack alert. Give the devices to libfdt in reverse order, we happen - * to know that will mean they are in forward order in the tree. */ - for (i = num - 1; i >= 0; i--) { - VIOsPAPRDevice *dev = (VIOsPAPRDevice *)(qdevs[i]); - - ret = vio_make_devnode(dev, fdt); - - if (ret < 0) { - goto out; - } - } - - ret = 0; -out: - g_free(qdevs); - - return ret; -} - -int spapr_populate_chosen_stdout(void *fdt, VIOsPAPRBus *bus) -{ - VIOsPAPRDevice *dev; - char *name, *path; - int ret, offset; - - dev = spapr_vty_get_default(bus); - if (!dev) - return 0; - - offset = fdt_path_offset(fdt, "/chosen"); - if (offset < 0) { - return offset; - } - - name = spapr_vio_get_dev_name(DEVICE(dev)); - path = g_strdup_printf("/vdevice/%s", name); - - ret = fdt_setprop_string(fdt, offset, "linux,stdout-path", path); - - g_free(name); - g_free(path); - - return ret; -} diff --git a/qemu/hw/ppc/virtex_ml507.c b/qemu/hw/ppc/virtex_ml507.c deleted file mode 100644 index b807a08c2..000000000 --- a/qemu/hw/ppc/virtex_ml507.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Model of Xilinx Virtex5 ML507 PPC-440 refdesign. - * - * Copyright (c) 2010 Edgar E. Iglesias. - * - * 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/sysbus.h" -#include "hw/hw.h" -#include "hw/char/serial.h" -#include "hw/block/flash.h" -#include "sysemu/sysemu.h" -#include "hw/devices.h" -#include "hw/boards.h" -#include "sysemu/device_tree.h" -#include "hw/loader.h" -#include "elf.h" -#include "qemu/error-report.h" -#include "qemu/log.h" -#include "exec/address-spaces.h" - -#include "hw/ppc/ppc.h" -#include "hw/ppc/ppc4xx.h" -#include "ppc405.h" - -#include "sysemu/block-backend.h" - -#define EPAPR_MAGIC (0x45504150) -#define FLASH_SIZE (16 * 1024 * 1024) - -#define INTC_BASEADDR 0x81800000 -#define UART16550_BASEADDR 0x83e01003 -#define TIMER_BASEADDR 0x83c00000 -#define PFLASH_BASEADDR 0xfc000000 - -#define TIMER_IRQ 3 -#define UART16550_IRQ 9 - -static struct boot_info -{ - uint32_t bootstrap_pc; - uint32_t cmdline; - uint32_t fdt; - uint32_t ima_size; - void *vfdt; -} boot_info; - -/* Create reset TLB entries for BookE, spanning the 32bit addr space. */ -static void mmubooke_create_initial_mapping(CPUPPCState *env, - target_ulong va, - hwaddr pa) -{ - ppcemb_tlb_t *tlb = &env->tlb.tlbe[0]; - - tlb->attr = 0; - tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); - tlb->size = 1U << 31; /* up to 0x80000000 */ - tlb->EPN = va & TARGET_PAGE_MASK; - tlb->RPN = pa & TARGET_PAGE_MASK; - tlb->PID = 0; - - tlb = &env->tlb.tlbe[1]; - tlb->attr = 0; - tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); - tlb->size = 1U << 31; /* up to 0xffffffff */ - tlb->EPN = 0x80000000 & TARGET_PAGE_MASK; - tlb->RPN = 0x80000000 & TARGET_PAGE_MASK; - tlb->PID = 0; -} - -static PowerPCCPU *ppc440_init_xilinx(ram_addr_t *ram_size, - int do_init, - const char *cpu_model, - uint32_t sysclk) -{ - PowerPCCPU *cpu; - CPUPPCState *env; - qemu_irq *irqs; - - cpu = cpu_ppc_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to initialize CPU!\n"); - exit(1); - } - env = &cpu->env; - - ppc_booke_timers_init(cpu, sysclk, 0/* no flags */); - - ppc_dcr_init(env, NULL, NULL); - - /* interrupt controller */ - irqs = g_malloc0(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB); - irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; - irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]; - ppcuic_init(env, irqs, 0x0C0, 0, 1); - return cpu; -} - -static void main_cpu_reset(void *opaque) -{ - PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; - struct boot_info *bi = env->load_info; - - cpu_reset(CPU(cpu)); - /* Linux Kernel Parameters (passing device tree): - * r3: pointer to the fdt - * r4: 0 - * r5: 0 - * r6: epapr magic - * r7: size of IMA in bytes - * r8: 0 - * r9: 0 - */ - env->gpr[1] = (16<<20) - 8; - /* Provide a device-tree. */ - env->gpr[3] = bi->fdt; - env->nip = bi->bootstrap_pc; - - /* Create a mapping for the kernel. */ - mmubooke_create_initial_mapping(env, 0, 0); - env->gpr[6] = tswap32(EPAPR_MAGIC); - env->gpr[7] = bi->ima_size; -} - -#define BINARY_DEVICE_TREE_FILE "virtex-ml507.dtb" -static int xilinx_load_device_tree(hwaddr addr, - uint32_t ramsize, - hwaddr initrd_base, - hwaddr initrd_size, - const char *kernel_cmdline) -{ - char *path; - int fdt_size; - void *fdt = NULL; - int r; - const char *dtb_filename; - - dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb"); - if (dtb_filename) { - fdt = load_device_tree(dtb_filename, &fdt_size); - if (!fdt) { - error_report("Error while loading device tree file '%s'", - dtb_filename); - } - } else { - /* Try the local "ppc.dtb" override. */ - fdt = load_device_tree("ppc.dtb", &fdt_size); - if (!fdt) { - path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); - if (path) { - fdt = load_device_tree(path, &fdt_size); - g_free(path); - } - } - } - if (!fdt) { - return 0; - } - - r = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", - initrd_base); - if (r < 0) { - error_report("couldn't set /chosen/linux,initrd-start"); - } - - r = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", - (initrd_base + initrd_size)); - if (r < 0) { - error_report("couldn't set /chosen/linux,initrd-end"); - } - - r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); - if (r < 0) - fprintf(stderr, "couldn't set /chosen/bootargs\n"); - cpu_physical_memory_write(addr, fdt, fdt_size); - return fdt_size; -} - -static void virtex_init(MachineState *machine) -{ - ram_addr_t ram_size = machine->ram_size; - const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; - hwaddr initrd_base = 0; - int initrd_size = 0; - MemoryRegion *address_space_mem = get_system_memory(); - DeviceState *dev; - PowerPCCPU *cpu; - CPUPPCState *env; - hwaddr ram_base = 0; - DriveInfo *dinfo; - MemoryRegion *phys_ram = g_new(MemoryRegion, 1); - qemu_irq irq[32], *cpu_irq; - int kernel_size; - int i; - - /* init CPUs */ - if (machine->cpu_model == NULL) { - machine->cpu_model = "440-Xilinx"; - } - - cpu = ppc440_init_xilinx(&ram_size, 1, machine->cpu_model, 400000000); - env = &cpu->env; - qemu_register_reset(main_cpu_reset, cpu); - - memory_region_allocate_system_memory(phys_ram, NULL, "ram", ram_size); - memory_region_add_subregion(address_space_mem, ram_base, phys_ram); - - dinfo = drive_get(IF_PFLASH, 0, 0); - pflash_cfi01_register(PFLASH_BASEADDR, NULL, "virtex.flash", FLASH_SIZE, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - (64 * 1024), FLASH_SIZE >> 16, - 1, 0x89, 0x18, 0x0000, 0x0, 1); - - cpu_irq = (qemu_irq *) &env->irq_inputs[PPC40x_INPUT_INT]; - dev = qdev_create(NULL, "xlnx.xps-intc"); - qdev_prop_set_uint32(dev, "kind-of-intr", 0); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, cpu_irq[0]); - for (i = 0; i < 32; i++) { - irq[i] = qdev_get_gpio_in(dev, i); - } - - serial_mm_init(address_space_mem, UART16550_BASEADDR, 2, irq[UART16550_IRQ], - 115200, serial_hds[0], DEVICE_LITTLE_ENDIAN); - - /* 2 timers at irq 2 @ 62 Mhz. */ - dev = qdev_create(NULL, "xlnx.xps-timer"); - qdev_prop_set_uint32(dev, "one-timer-only", 0); - qdev_prop_set_uint32(dev, "clock-frequency", 62 * 1000000); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, TIMER_BASEADDR); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[TIMER_IRQ]); - - if (kernel_filename) { - uint64_t entry, low, high; - hwaddr boot_offset; - - /* Boots a kernel elf binary. */ - kernel_size = load_elf(kernel_filename, NULL, NULL, - &entry, &low, &high, 1, PPC_ELF_MACHINE, - 0, 0); - boot_info.bootstrap_pc = entry & 0x00ffffff; - - if (kernel_size < 0) { - boot_offset = 0x1200000; - /* If we failed loading ELF's try a raw image. */ - kernel_size = load_image_targphys(kernel_filename, - boot_offset, - ram_size); - boot_info.bootstrap_pc = boot_offset; - high = boot_info.bootstrap_pc + kernel_size + 8192; - } - - boot_info.ima_size = kernel_size; - - /* Load initrd. */ - if (machine->initrd_filename) { - initrd_base = high = ROUND_UP(high, 4); - initrd_size = load_image_targphys(machine->initrd_filename, - high, ram_size - high); - - if (initrd_size < 0) { - error_report("couldn't load ram disk '%s'", - machine->initrd_filename); - exit(1); - } - high = ROUND_UP(high + initrd_size, 4); - } - - /* Provide a device-tree. */ - boot_info.fdt = high + (8192 * 2); - boot_info.fdt &= ~8191; - - xilinx_load_device_tree(boot_info.fdt, ram_size, - initrd_base, initrd_size, - kernel_cmdline); - } - env->load_info = &boot_info; -} - -static void virtex_machine_init(MachineClass *mc) -{ - mc->desc = "Xilinx Virtex ML507 reference design"; - mc->init = virtex_init; -} - -DEFINE_MACHINE("virtex-ml507", virtex_machine_init) diff --git a/qemu/hw/s390x/Makefile.objs b/qemu/hw/s390x/Makefile.objs deleted file mode 100644 index 220361782..000000000 --- a/qemu/hw/s390x/Makefile.objs +++ /dev/null @@ -1,13 +0,0 @@ -obj-y += s390-virtio.o -obj-y += s390-virtio-hcall.o -obj-y += sclp.o -obj-y += event-facility.o -obj-y += sclpquiesce.o -obj-y += sclpcpu.o -obj-y += ipl.o -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 deleted file mode 100644 index 3a1d91958..000000000 --- a/qemu/hw/s390x/css.c +++ /dev/null @@ -1,1646 +0,0 @@ -/* - * Channel subsystem base support. - * - * Copyright 2012 IBM Corp. - * Author(s): Cornelia Huck - * - * 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 -#include "qemu/bitops.h" -#include "exec/address-spaces.h" -#include "cpu.h" -#include "ioinst.h" -#include "css.h" -#include "trace.h" -#include "hw/s390x/s390_flic.h" - -typedef struct CrwContainer { - CRW crw; - QTAILQ_ENTRY(CrwContainer) sibling; -} CrwContainer; - -typedef struct ChpInfo { - uint8_t in_use; - uint8_t type; - uint8_t is_virtual; -} ChpInfo; - -typedef struct SubchSet { - SubchDev *sch[MAX_SCHID + 1]; - unsigned long schids_used[BITS_TO_LONGS(MAX_SCHID + 1)]; - unsigned long devnos_used[BITS_TO_LONGS(MAX_SCHID + 1)]; -} SubchSet; - -typedef struct CssImage { - SubchSet *sch_set[MAX_SSID + 1]; - ChpInfo chpids[MAX_CHPID + 1]; -} CssImage; - -typedef struct IoAdapter { - uint32_t id; - uint8_t type; - uint8_t isc; - QTAILQ_ENTRY(IoAdapter) sibling; -} IoAdapter; - -typedef struct ChannelSubSys { - QTAILQ_HEAD(, CrwContainer) pending_crws; - bool sei_pending; - bool do_crw_mchk; - bool crws_lost; - uint8_t max_cssid; - uint8_t max_ssid; - bool chnmon_active; - uint64_t chnmon_area; - CssImage *css[MAX_CSSID + 1]; - uint8_t default_cssid; - QTAILQ_HEAD(, IoAdapter) io_adapters; - QTAILQ_HEAD(, IndAddr) indicator_addresses; -} ChannelSubSys; - -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) -{ - trace_css_new_image(cssid, default_image ? "(default)" : ""); - if (cssid > MAX_CSSID) { - return -EINVAL; - } - if (channel_subsys.css[cssid]) { - return -EBUSY; - } - channel_subsys.css[cssid] = g_malloc0(sizeof(CssImage)); - if (default_image) { - channel_subsys.default_cssid = cssid; - } - return 0; -} - -int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap, - bool maskable, uint32_t *id) -{ - IoAdapter *adapter; - bool found = false; - int ret; - S390FLICState *fs = s390_get_flic(); - S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); - - *id = 0; - QTAILQ_FOREACH(adapter, &channel_subsys.io_adapters, sibling) { - if ((adapter->type == type) && (adapter->isc == isc)) { - *id = adapter->id; - found = true; - ret = 0; - break; - } - if (adapter->id >= *id) { - *id = adapter->id + 1; - } - } - if (found) { - goto out; - } - adapter = g_new0(IoAdapter, 1); - ret = fsc->register_io_adapter(fs, *id, isc, swap, maskable); - if (ret == 0) { - adapter->id = *id; - adapter->isc = isc; - adapter->type = type; - QTAILQ_INSERT_TAIL(&channel_subsys.io_adapters, adapter, sibling); - } else { - g_free(adapter); - fprintf(stderr, "Unexpected error %d when registering adapter %d\n", - ret, *id); - } -out: - return ret; -} - -uint16_t css_build_subchannel_id(SubchDev *sch) -{ - if (channel_subsys.max_cssid > 0) { - return (sch->cssid << 8) | (1 << 3) | (sch->ssid << 1) | 1; - } - return (sch->ssid << 1) | 1; -} - -static void css_inject_io_interrupt(SubchDev *sch) -{ - uint8_t isc = (sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ISC) >> 11; - - trace_css_io_interrupt(sch->cssid, sch->ssid, sch->schid, - sch->curr_status.pmcw.intparm, isc, ""); - s390_io_interrupt(css_build_subchannel_id(sch), - sch->schid, - sch->curr_status.pmcw.intparm, - isc << 27); -} - -void css_conditional_io_interrupt(SubchDev *sch) -{ - /* - * If the subchannel is not currently status pending, make it pending - * with alert status. - */ - if (!(sch->curr_status.scsw.ctrl & SCSW_STCTL_STATUS_PEND)) { - uint8_t isc = (sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ISC) >> 11; - - trace_css_io_interrupt(sch->cssid, sch->ssid, sch->schid, - sch->curr_status.pmcw.intparm, isc, - "(unsolicited)"); - sch->curr_status.scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; - sch->curr_status.scsw.ctrl |= - SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; - /* Inject an I/O interrupt. */ - s390_io_interrupt(css_build_subchannel_id(sch), - sch->schid, - sch->curr_status.pmcw.intparm, - isc << 27); - } -} - -void css_adapter_interrupt(uint8_t isc) -{ - uint32_t io_int_word = (isc << 27) | IO_INT_WORD_AI; - - trace_css_adapter_interrupt(isc); - s390_io_interrupt(0, 0, 0, io_int_word); -} - -static void sch_handle_clear_func(SubchDev *sch) -{ - PMCW *p = &sch->curr_status.pmcw; - SCSW *s = &sch->curr_status.scsw; - int path; - - /* Path management: In our simple css, we always choose the only path. */ - path = 0x80; - - /* Reset values prior to 'issuing the clear signal'. */ - p->lpum = 0; - p->pom = 0xff; - s->flags &= ~SCSW_FLAGS_MASK_PNO; - - /* We always 'attempt to issue the clear signal', and we always succeed. */ - sch->channel_prog = 0x0; - sch->last_cmd_valid = false; - s->ctrl &= ~SCSW_ACTL_CLEAR_PEND; - s->ctrl |= SCSW_STCTL_STATUS_PEND; - - s->dstat = 0; - s->cstat = 0; - p->lpum = path; - -} - -static void sch_handle_halt_func(SubchDev *sch) -{ - - PMCW *p = &sch->curr_status.pmcw; - SCSW *s = &sch->curr_status.scsw; - hwaddr curr_ccw = sch->channel_prog; - int path; - - /* Path management: In our simple css, we always choose the only path. */ - path = 0x80; - - /* We always 'attempt to issue the halt signal', and we always succeed. */ - sch->channel_prog = 0x0; - sch->last_cmd_valid = false; - s->ctrl &= ~SCSW_ACTL_HALT_PEND; - s->ctrl |= SCSW_STCTL_STATUS_PEND; - - if ((s->ctrl & (SCSW_ACTL_SUBCH_ACTIVE | SCSW_ACTL_DEVICE_ACTIVE)) || - !((s->ctrl & SCSW_ACTL_START_PEND) || - (s->ctrl & SCSW_ACTL_SUSP))) { - s->dstat = SCSW_DSTAT_DEVICE_END; - } - if ((s->ctrl & (SCSW_ACTL_SUBCH_ACTIVE | SCSW_ACTL_DEVICE_ACTIVE)) || - (s->ctrl & SCSW_ACTL_SUSP)) { - s->cpa = curr_ccw + 8; - } - s->cstat = 0; - p->lpum = path; - -} - -static void copy_sense_id_to_guest(SenseId *dest, SenseId *src) -{ - int i; - - dest->reserved = src->reserved; - dest->cu_type = cpu_to_be16(src->cu_type); - dest->cu_model = src->cu_model; - dest->dev_type = cpu_to_be16(src->dev_type); - dest->dev_model = src->dev_model; - dest->unused = src->unused; - for (i = 0; i < ARRAY_SIZE(dest->ciw); i++) { - dest->ciw[i].type = src->ciw[i].type; - dest->ciw[i].command = src->ciw[i].command; - dest->ciw[i].count = cpu_to_be16(src->ciw[i].count); - } -} - -static CCW1 copy_ccw_from_guest(hwaddr addr, bool fmt1) -{ - CCW0 tmp0; - CCW1 tmp1; - CCW1 ret; - - if (fmt1) { - cpu_physical_memory_read(addr, &tmp1, sizeof(tmp1)); - ret.cmd_code = tmp1.cmd_code; - ret.flags = tmp1.flags; - ret.count = be16_to_cpu(tmp1.count); - ret.cda = be32_to_cpu(tmp1.cda); - } else { - cpu_physical_memory_read(addr, &tmp0, sizeof(tmp0)); - ret.cmd_code = tmp0.cmd_code; - 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, - bool suspend_allowed) -{ - int ret; - bool check_len; - int len; - CCW1 ccw; - - if (!ccw_addr) { - return -EIO; - } - - /* Translate everything to format-1 ccws - the information is the same. */ - ccw = copy_ccw_from_guest(ccw_addr, sch->ccw_fmt_1); - - /* Check for invalid command codes. */ - if ((ccw.cmd_code & 0x0f) == 0) { - return -EINVAL; - } - if (((ccw.cmd_code & 0x0f) == CCW_CMD_TIC) && - ((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 suspend_allowed ? -EINPROGRESS : -EINVAL; - } - - check_len = !((ccw.flags & CCW_FLAG_SLI) && !(ccw.flags & CCW_FLAG_DC)); - - if (!ccw.cda) { - if (sch->ccw_no_data_cnt == 255) { - return -EINVAL; - } - sch->ccw_no_data_cnt++; - } - - /* Look at the command. */ - switch (ccw.cmd_code) { - case CCW_CMD_NOOP: - /* Nothing to do. */ - ret = 0; - break; - case CCW_CMD_BASIC_SENSE: - if (check_len) { - if (ccw.count != sizeof(sch->sense_data)) { - ret = -EINVAL; - break; - } - } - len = MIN(ccw.count, sizeof(sch->sense_data)); - cpu_physical_memory_write(ccw.cda, sch->sense_data, len); - sch->curr_status.scsw.count = ccw.count - len; - memset(sch->sense_data, 0, sizeof(sch->sense_data)); - ret = 0; - break; - case CCW_CMD_SENSE_ID: - { - SenseId sense_id; - - copy_sense_id_to_guest(&sense_id, &sch->id); - /* Sense ID information is device specific. */ - if (check_len) { - if (ccw.count != sizeof(sense_id)) { - ret = -EINVAL; - break; - } - } - len = MIN(ccw.count, sizeof(sense_id)); - /* - * Only indicate 0xff in the first sense byte if we actually - * have enough place to store at least bytes 0-3. - */ - if (len >= 4) { - sense_id.reserved = 0xff; - } else { - sense_id.reserved = 0; - } - cpu_physical_memory_write(ccw.cda, &sense_id, len); - sch->curr_status.scsw.count = ccw.count - len; - ret = 0; - break; - } - case CCW_CMD_TIC: - if (sch->last_cmd_valid && (sch->last_cmd.cmd_code == CCW_CMD_TIC)) { - ret = -EINVAL; - break; - } - if (ccw.flags & (CCW_FLAG_CC | CCW_FLAG_DC)) { - ret = -EINVAL; - break; - } - sch->channel_prog = ccw.cda; - ret = -EAGAIN; - break; - default: - if (sch->ccw_cb) { - /* Handle device specific commands. */ - ret = sch->ccw_cb(sch, ccw); - } else { - ret = -ENOSYS; - } - break; - } - sch->last_cmd = ccw; - sch->last_cmd_valid = true; - if (ret == 0) { - if (ccw.flags & CCW_FLAG_CC) { - sch->channel_prog += 8; - ret = -EAGAIN; - } - } - - return ret; -} - -static void sch_handle_start_func(SubchDev *sch, ORB *orb) -{ - - PMCW *p = &sch->curr_status.pmcw; - 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; - if (!(orb->lpm & path)) { - /* Generate a deferred cc 3 condition. */ - s->flags |= SCSW_FLAGS_MASK_CC; - s->ctrl &= ~SCSW_CTRL_MASK_STCTL; - s->ctrl |= (SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND); - return; - } - 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, suspend_allowed); - switch (ret) { - case -EAGAIN: - /* ccw chain, continue processing */ - break; - case 0: - /* success */ - s->ctrl &= ~SCSW_ACTL_START_PEND; - s->ctrl &= ~SCSW_CTRL_MASK_STCTL; - s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | - SCSW_STCTL_STATUS_PEND; - s->dstat = SCSW_DSTAT_CHANNEL_END | SCSW_DSTAT_DEVICE_END; - s->cpa = sch->channel_prog + 8; - break; - case -ENOSYS: - /* unsupported command, generate unit check (command reject) */ - s->ctrl &= ~SCSW_ACTL_START_PEND; - s->dstat = SCSW_DSTAT_UNIT_CHECK; - /* Set sense bit 0 in ecw0. */ - sch->sense_data[0] = 0x80; - s->ctrl &= ~SCSW_CTRL_MASK_STCTL; - s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | - SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; - s->cpa = sch->channel_prog + 8; - break; - case -EFAULT: - /* memory problem, generate channel data check */ - s->ctrl &= ~SCSW_ACTL_START_PEND; - s->cstat = SCSW_CSTAT_DATA_CHECK; - s->ctrl &= ~SCSW_CTRL_MASK_STCTL; - s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | - SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; - s->cpa = sch->channel_prog + 8; - break; - case -EBUSY: - /* subchannel busy, generate deferred cc 1 */ - s->flags &= ~SCSW_FLAGS_MASK_CC; - s->flags |= (1 << 8); - s->ctrl &= ~SCSW_CTRL_MASK_STCTL; - s->ctrl |= SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; - break; - case -EINPROGRESS: - /* channel program has been suspended */ - s->ctrl &= ~SCSW_ACTL_START_PEND; - s->ctrl |= SCSW_ACTL_SUSP; - break; - default: - /* error, generate channel program check */ - s->ctrl &= ~SCSW_ACTL_START_PEND; - s->cstat = SCSW_CSTAT_PROG_CHECK; - s->ctrl &= ~SCSW_CTRL_MASK_STCTL; - s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | - SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; - s->cpa = sch->channel_prog + 8; - break; - } - } while (ret == -EAGAIN); - -} - -/* - * On real machines, this would run asynchronously to the main vcpus. - * We might want to make some parts of the ssch handling (interpreting - * read/writes) asynchronous later on if we start supporting more than - * our current very simple devices. - */ -static void do_subchannel_work(SubchDev *sch, ORB *orb) -{ - - SCSW *s = &sch->curr_status.scsw; - - if (s->ctrl & SCSW_FCTL_CLEAR_FUNC) { - sch_handle_clear_func(sch); - } else if (s->ctrl & SCSW_FCTL_HALT_FUNC) { - sch_handle_halt_func(sch); - } else if (s->ctrl & SCSW_FCTL_START_FUNC) { - sch_handle_start_func(sch, orb); - } else { - /* Cannot happen. */ - return; - } - css_inject_io_interrupt(sch); -} - -static void copy_pmcw_to_guest(PMCW *dest, const PMCW *src) -{ - int i; - - dest->intparm = cpu_to_be32(src->intparm); - dest->flags = cpu_to_be16(src->flags); - dest->devno = cpu_to_be16(src->devno); - dest->lpm = src->lpm; - dest->pnom = src->pnom; - dest->lpum = src->lpum; - dest->pim = src->pim; - dest->mbi = cpu_to_be16(src->mbi); - dest->pom = src->pom; - dest->pam = src->pam; - for (i = 0; i < ARRAY_SIZE(dest->chpid); i++) { - dest->chpid[i] = src->chpid[i]; - } - dest->chars = cpu_to_be32(src->chars); -} - -static void copy_scsw_to_guest(SCSW *dest, const SCSW *src) -{ - dest->flags = cpu_to_be16(src->flags); - dest->ctrl = cpu_to_be16(src->ctrl); - dest->cpa = cpu_to_be32(src->cpa); - dest->dstat = src->dstat; - dest->cstat = src->cstat; - dest->count = cpu_to_be16(src->count); -} - -static void copy_schib_to_guest(SCHIB *dest, const SCHIB *src) -{ - int i; - - copy_pmcw_to_guest(&dest->pmcw, &src->pmcw); - copy_scsw_to_guest(&dest->scsw, &src->scsw); - dest->mba = cpu_to_be64(src->mba); - for (i = 0; i < ARRAY_SIZE(dest->mda); i++) { - dest->mda[i] = src->mda[i]; - } -} - -int css_do_stsch(SubchDev *sch, SCHIB *schib) -{ - /* Use current status. */ - copy_schib_to_guest(schib, &sch->curr_status); - return 0; -} - -static void copy_pmcw_from_guest(PMCW *dest, const PMCW *src) -{ - int i; - - dest->intparm = be32_to_cpu(src->intparm); - dest->flags = be16_to_cpu(src->flags); - dest->devno = be16_to_cpu(src->devno); - dest->lpm = src->lpm; - dest->pnom = src->pnom; - dest->lpum = src->lpum; - dest->pim = src->pim; - dest->mbi = be16_to_cpu(src->mbi); - dest->pom = src->pom; - dest->pam = src->pam; - for (i = 0; i < ARRAY_SIZE(dest->chpid); i++) { - dest->chpid[i] = src->chpid[i]; - } - dest->chars = be32_to_cpu(src->chars); -} - -static void copy_scsw_from_guest(SCSW *dest, const SCSW *src) -{ - dest->flags = be16_to_cpu(src->flags); - dest->ctrl = be16_to_cpu(src->ctrl); - dest->cpa = be32_to_cpu(src->cpa); - dest->dstat = src->dstat; - dest->cstat = src->cstat; - dest->count = be16_to_cpu(src->count); -} - -static void copy_schib_from_guest(SCHIB *dest, const SCHIB *src) -{ - int i; - - copy_pmcw_from_guest(&dest->pmcw, &src->pmcw); - copy_scsw_from_guest(&dest->scsw, &src->scsw); - dest->mba = be64_to_cpu(src->mba); - for (i = 0; i < ARRAY_SIZE(dest->mda); i++) { - dest->mda[i] = src->mda[i]; - } -} - -int css_do_msch(SubchDev *sch, const SCHIB *orig_schib) -{ - SCSW *s = &sch->curr_status.scsw; - PMCW *p = &sch->curr_status.pmcw; - uint16_t oldflags; - int ret; - SCHIB schib; - - if (!(sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_DNV)) { - ret = 0; - goto out; - } - - if (s->ctrl & SCSW_STCTL_STATUS_PEND) { - ret = -EINPROGRESS; - goto out; - } - - if (s->ctrl & - (SCSW_FCTL_START_FUNC|SCSW_FCTL_HALT_FUNC|SCSW_FCTL_CLEAR_FUNC)) { - ret = -EBUSY; - goto out; - } - - copy_schib_from_guest(&schib, orig_schib); - /* Only update the program-modifiable fields. */ - p->intparm = schib.pmcw.intparm; - oldflags = p->flags; - p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | - PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | - PMCW_FLAGS_MASK_MP); - p->flags |= schib.pmcw.flags & - (PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | - PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | - PMCW_FLAGS_MASK_MP); - p->lpm = schib.pmcw.lpm; - p->mbi = schib.pmcw.mbi; - p->pom = schib.pmcw.pom; - p->chars &= ~(PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_CSENSE); - p->chars |= schib.pmcw.chars & - (PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_CSENSE); - sch->curr_status.mba = schib.mba; - - /* Has the channel been disabled? */ - if (sch->disable_cb && (oldflags & PMCW_FLAGS_MASK_ENA) != 0 - && (p->flags & PMCW_FLAGS_MASK_ENA) == 0) { - sch->disable_cb(sch); - } - - ret = 0; - -out: - return ret; -} - -int css_do_xsch(SubchDev *sch) -{ - SCSW *s = &sch->curr_status.scsw; - PMCW *p = &sch->curr_status.pmcw; - int ret; - - if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { - ret = -ENODEV; - goto out; - } - - if (!(s->ctrl & SCSW_CTRL_MASK_FCTL) || - ((s->ctrl & SCSW_CTRL_MASK_FCTL) != SCSW_FCTL_START_FUNC) || - (!(s->ctrl & - (SCSW_ACTL_RESUME_PEND | SCSW_ACTL_START_PEND | SCSW_ACTL_SUSP))) || - (s->ctrl & SCSW_ACTL_SUBCH_ACTIVE)) { - ret = -EINPROGRESS; - goto out; - } - - if (s->ctrl & SCSW_CTRL_MASK_STCTL) { - ret = -EBUSY; - goto out; - } - - /* Cancel the current operation. */ - s->ctrl &= ~(SCSW_FCTL_START_FUNC | - SCSW_ACTL_RESUME_PEND | - SCSW_ACTL_START_PEND | - SCSW_ACTL_SUSP); - sch->channel_prog = 0x0; - sch->last_cmd_valid = false; - s->dstat = 0; - s->cstat = 0; - ret = 0; - -out: - return ret; -} - -int css_do_csch(SubchDev *sch) -{ - SCSW *s = &sch->curr_status.scsw; - PMCW *p = &sch->curr_status.pmcw; - int ret; - - if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { - ret = -ENODEV; - goto out; - } - - /* Trigger the clear function. */ - s->ctrl &= ~(SCSW_CTRL_MASK_FCTL | SCSW_CTRL_MASK_ACTL); - s->ctrl |= SCSW_FCTL_CLEAR_FUNC | SCSW_ACTL_CLEAR_PEND; - - do_subchannel_work(sch, NULL); - ret = 0; - -out: - return ret; -} - -int css_do_hsch(SubchDev *sch) -{ - SCSW *s = &sch->curr_status.scsw; - PMCW *p = &sch->curr_status.pmcw; - int ret; - - if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { - ret = -ENODEV; - goto out; - } - - if (((s->ctrl & SCSW_CTRL_MASK_STCTL) == SCSW_STCTL_STATUS_PEND) || - (s->ctrl & (SCSW_STCTL_PRIMARY | - SCSW_STCTL_SECONDARY | - SCSW_STCTL_ALERT))) { - ret = -EINPROGRESS; - goto out; - } - - if (s->ctrl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { - ret = -EBUSY; - goto out; - } - - /* Trigger the halt function. */ - s->ctrl |= SCSW_FCTL_HALT_FUNC; - s->ctrl &= ~SCSW_FCTL_START_FUNC; - if (((s->ctrl & SCSW_CTRL_MASK_ACTL) == - (SCSW_ACTL_SUBCH_ACTIVE | SCSW_ACTL_DEVICE_ACTIVE)) && - ((s->ctrl & SCSW_CTRL_MASK_STCTL) == SCSW_STCTL_INTERMEDIATE)) { - s->ctrl &= ~SCSW_STCTL_STATUS_PEND; - } - s->ctrl |= SCSW_ACTL_HALT_PEND; - - do_subchannel_work(sch, NULL); - ret = 0; - -out: - return ret; -} - -static void css_update_chnmon(SubchDev *sch) -{ - if (!(sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_MME)) { - /* Not active. */ - return; - } - /* The counter is conveniently located at the beginning of the struct. */ - if (sch->curr_status.pmcw.chars & PMCW_CHARS_MASK_MBFC) { - /* Format 1, per-subchannel area. */ - uint32_t count; - - count = address_space_ldl(&address_space_memory, - sch->curr_status.mba, - MEMTXATTRS_UNSPECIFIED, - NULL); - count++; - address_space_stl(&address_space_memory, sch->curr_status.mba, count, - MEMTXATTRS_UNSPECIFIED, NULL); - } else { - /* Format 0, global area. */ - uint32_t offset; - uint16_t count; - - offset = sch->curr_status.pmcw.mbi << 5; - count = address_space_lduw(&address_space_memory, - channel_subsys.chnmon_area + offset, - MEMTXATTRS_UNSPECIFIED, - NULL); - count++; - address_space_stw(&address_space_memory, - channel_subsys.chnmon_area + offset, count, - MEMTXATTRS_UNSPECIFIED, NULL); - } -} - -int css_do_ssch(SubchDev *sch, ORB *orb) -{ - SCSW *s = &sch->curr_status.scsw; - PMCW *p = &sch->curr_status.pmcw; - int ret; - - if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { - ret = -ENODEV; - goto out; - } - - if (s->ctrl & SCSW_STCTL_STATUS_PEND) { - ret = -EINPROGRESS; - goto out; - } - - if (s->ctrl & (SCSW_FCTL_START_FUNC | - SCSW_FCTL_HALT_FUNC | - SCSW_FCTL_CLEAR_FUNC)) { - ret = -EBUSY; - goto out; - } - - /* If monitoring is active, update counter. */ - if (channel_subsys.chnmon_active) { - css_update_chnmon(sch); - } - sch->channel_prog = orb->cpa; - /* Trigger the start function. */ - s->ctrl |= (SCSW_FCTL_START_FUNC | SCSW_ACTL_START_PEND); - s->flags &= ~SCSW_FLAGS_MASK_PNO; - - do_subchannel_work(sch, orb); - ret = 0; - -out: - return ret; -} - -static void copy_irb_to_guest(IRB *dest, const IRB *src, PMCW *pmcw, - int *irb_len) -{ - int i; - uint16_t stctl = src->scsw.ctrl & SCSW_CTRL_MASK_STCTL; - uint16_t actl = src->scsw.ctrl & SCSW_CTRL_MASK_ACTL; - - copy_scsw_to_guest(&dest->scsw, &src->scsw); - - for (i = 0; i < ARRAY_SIZE(dest->esw); i++) { - dest->esw[i] = cpu_to_be32(src->esw[i]); - } - for (i = 0; i < ARRAY_SIZE(dest->ecw); i++) { - dest->ecw[i] = cpu_to_be32(src->ecw[i]); - } - *irb_len = sizeof(*dest) - sizeof(dest->emw); - - /* extended measurements enabled? */ - if ((src->scsw.flags & SCSW_FLAGS_MASK_ESWF) || - !(pmcw->flags & PMCW_FLAGS_MASK_TF) || - !(pmcw->chars & PMCW_CHARS_MASK_XMWME)) { - return; - } - /* extended measurements pending? */ - if (!(stctl & SCSW_STCTL_STATUS_PEND)) { - return; - } - if ((stctl & SCSW_STCTL_PRIMARY) || - (stctl == SCSW_STCTL_SECONDARY) || - ((stctl & SCSW_STCTL_INTERMEDIATE) && (actl & SCSW_ACTL_SUSP))) { - for (i = 0; i < ARRAY_SIZE(dest->emw); i++) { - dest->emw[i] = cpu_to_be32(src->emw[i]); - } - } - *irb_len = sizeof(*dest); -} - -int css_do_tsch_get_irb(SubchDev *sch, IRB *target_irb, int *irb_len) -{ - SCSW *s = &sch->curr_status.scsw; - PMCW *p = &sch->curr_status.pmcw; - uint16_t stctl; - IRB irb; - - if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { - return 3; - } - - stctl = s->ctrl & SCSW_CTRL_MASK_STCTL; - - /* Prepare the irb for the guest. */ - memset(&irb, 0, sizeof(IRB)); - - /* Copy scsw from current status. */ - memcpy(&irb.scsw, s, sizeof(SCSW)); - if (stctl & SCSW_STCTL_STATUS_PEND) { - if (s->cstat & (SCSW_CSTAT_DATA_CHECK | - SCSW_CSTAT_CHN_CTRL_CHK | - SCSW_CSTAT_INTF_CTRL_CHK)) { - irb.scsw.flags |= SCSW_FLAGS_MASK_ESWF; - irb.esw[0] = 0x04804000; - } else { - irb.esw[0] = 0x00800000; - } - /* 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); - } - } - /* Store the irb to the guest. */ - copy_irb_to_guest(target_irb, &irb, p, irb_len); - - return ((stctl & SCSW_STCTL_STATUS_PEND) == 0); -} - -void css_do_tsch_update_subch(SubchDev *sch) -{ - SCSW *s = &sch->curr_status.scsw; - PMCW *p = &sch->curr_status.pmcw; - uint16_t stctl; - uint16_t fctl; - uint16_t actl; - - stctl = s->ctrl & SCSW_CTRL_MASK_STCTL; - fctl = s->ctrl & SCSW_CTRL_MASK_FCTL; - actl = s->ctrl & SCSW_CTRL_MASK_ACTL; - - /* Clear conditions on subchannel, if applicable. */ - if (stctl & SCSW_STCTL_STATUS_PEND) { - s->ctrl &= ~SCSW_CTRL_MASK_STCTL; - if ((stctl != (SCSW_STCTL_INTERMEDIATE | SCSW_STCTL_STATUS_PEND)) || - ((fctl & SCSW_FCTL_HALT_FUNC) && - (actl & SCSW_ACTL_SUSP))) { - s->ctrl &= ~SCSW_CTRL_MASK_FCTL; - } - if (stctl != (SCSW_STCTL_INTERMEDIATE | SCSW_STCTL_STATUS_PEND)) { - s->flags &= ~SCSW_FLAGS_MASK_PNO; - s->ctrl &= ~(SCSW_ACTL_RESUME_PEND | - SCSW_ACTL_START_PEND | - SCSW_ACTL_HALT_PEND | - SCSW_ACTL_CLEAR_PEND | - SCSW_ACTL_SUSP); - } else { - if ((actl & SCSW_ACTL_SUSP) && - (fctl & SCSW_FCTL_START_FUNC)) { - s->flags &= ~SCSW_FLAGS_MASK_PNO; - if (fctl & SCSW_FCTL_HALT_FUNC) { - s->ctrl &= ~(SCSW_ACTL_RESUME_PEND | - SCSW_ACTL_START_PEND | - SCSW_ACTL_HALT_PEND | - SCSW_ACTL_CLEAR_PEND | - SCSW_ACTL_SUSP); - } else { - s->ctrl &= ~SCSW_ACTL_RESUME_PEND; - } - } - } - /* Clear pending sense data. */ - if (p->chars & PMCW_CHARS_MASK_CSENSE) { - memset(sch->sense_data, 0 , sizeof(sch->sense_data)); - } - } -} - -static void copy_crw_to_guest(CRW *dest, const CRW *src) -{ - dest->flags = cpu_to_be16(src->flags); - dest->rsid = cpu_to_be16(src->rsid); -} - -int css_do_stcrw(CRW *crw) -{ - CrwContainer *crw_cont; - int ret; - - crw_cont = QTAILQ_FIRST(&channel_subsys.pending_crws); - if (crw_cont) { - 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; - ret = 1; - } - - return ret; -} - -static void copy_crw_from_guest(CRW *dest, const CRW *src) -{ - dest->flags = be16_to_cpu(src->flags); - dest->rsid = be16_to_cpu(src->rsid); -} - -void css_undo_stcrw(CRW *crw) -{ - CrwContainer *crw_cont; - - crw_cont = g_try_malloc0(sizeof(CrwContainer)); - if (!crw_cont) { - channel_subsys.crws_lost = true; - return; - } - copy_crw_from_guest(&crw_cont->crw, crw); - - QTAILQ_INSERT_HEAD(&channel_subsys.pending_crws, crw_cont, sibling); -} - -int css_do_tpi(IOIntCode *int_code, int lowcore) -{ - /* No pending interrupts for !KVM. */ - return 0; - } - -int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, uint8_t l_chpid, - int rfmt, void *buf) -{ - int i, desc_size; - uint32_t words[8]; - uint32_t chpid_type_word; - CssImage *css; - - if (!m && !cssid) { - css = channel_subsys.css[channel_subsys.default_cssid]; - } else { - css = channel_subsys.css[cssid]; - } - if (!css) { - return 0; - } - desc_size = 0; - for (i = f_chpid; i <= l_chpid; i++) { - if (css->chpids[i].in_use) { - chpid_type_word = 0x80000000 | (css->chpids[i].type << 8) | i; - if (rfmt == 0) { - words[0] = cpu_to_be32(chpid_type_word); - words[1] = 0; - memcpy(buf + desc_size, words, 8); - desc_size += 8; - } else if (rfmt == 1) { - words[0] = cpu_to_be32(chpid_type_word); - words[1] = 0; - words[2] = 0; - words[3] = 0; - words[4] = 0; - words[5] = 0; - words[6] = 0; - words[7] = 0; - memcpy(buf + desc_size, words, 32); - desc_size += 32; - } - } - } - return desc_size; -} - -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) { - /* Enable measuring. */ - channel_subsys.chnmon_area = mbo; - channel_subsys.chnmon_active = true; - } - if (!update && channel_subsys.chnmon_active) { - /* Disable measuring. */ - channel_subsys.chnmon_area = 0; - channel_subsys.chnmon_active = false; - } -} - -int css_do_rsch(SubchDev *sch) -{ - SCSW *s = &sch->curr_status.scsw; - PMCW *p = &sch->curr_status.pmcw; - int ret; - - if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { - ret = -ENODEV; - goto out; - } - - if (s->ctrl & SCSW_STCTL_STATUS_PEND) { - ret = -EINPROGRESS; - goto out; - } - - if (((s->ctrl & SCSW_CTRL_MASK_FCTL) != SCSW_FCTL_START_FUNC) || - (s->ctrl & SCSW_ACTL_RESUME_PEND) || - (!(s->ctrl & SCSW_ACTL_SUSP))) { - ret = -EINVAL; - goto out; - } - - /* If monitoring is active, update counter. */ - if (channel_subsys.chnmon_active) { - css_update_chnmon(sch); - } - - s->ctrl |= SCSW_ACTL_RESUME_PEND; - do_subchannel_work(sch, NULL); - ret = 0; - -out: - return ret; -} - -int css_do_rchp(uint8_t cssid, uint8_t chpid) -{ - uint8_t real_cssid; - - if (cssid > channel_subsys.max_cssid) { - return -EINVAL; - } - if (channel_subsys.max_cssid == 0) { - real_cssid = channel_subsys.default_cssid; - } else { - real_cssid = cssid; - } - if (!channel_subsys.css[real_cssid]) { - return -EINVAL; - } - - if (!channel_subsys.css[real_cssid]->chpids[chpid].in_use) { - return -ENODEV; - } - - if (!channel_subsys.css[real_cssid]->chpids[chpid].is_virtual) { - fprintf(stderr, - "rchp unsupported for non-virtual chpid %x.%02x!\n", - real_cssid, chpid); - return -ENODEV; - } - - /* 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) { - css_queue_crw(CRW_RSC_CHP, CRW_ERC_INIT, 0, real_cssid << 8); - } - return 0; -} - -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; - if (real_cssid > MAX_CSSID || ssid > MAX_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]; - return schid > find_last_bit(set->schids_used, - (MAX_SCHID + 1) / sizeof(unsigned long)); -} - -static int css_add_virtual_chpid(uint8_t cssid, uint8_t chpid, uint8_t type) -{ - CssImage *css; - - trace_css_chpid_add(cssid, chpid, type); - if (cssid > MAX_CSSID) { - return -EINVAL; - } - css = channel_subsys.css[cssid]; - if (!css) { - return -EINVAL; - } - if (css->chpids[chpid].in_use) { - return -EEXIST; - } - css->chpids[chpid].in_use = 1; - css->chpids[chpid].type = type; - css->chpids[chpid].is_virtual = 1; - - css_generate_chp_crws(cssid, chpid); - - return 0; -} - -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]; - - assert(css != NULL); - memset(p, 0, sizeof(PMCW)); - p->flags |= PMCW_FLAGS_MASK_DNV; - p->devno = sch->devno; - /* single path */ - p->pim = 0x80; - p->pom = 0xff; - p->pam = 0x80; - p->chpid[0] = chpid; - if (!css->chpids[chpid].in_use) { - css_add_virtual_chpid(sch->cssid, chpid, type); - } - - memset(s, 0, sizeof(SCSW)); - sch->curr_status.mba = 0; - for (i = 0; i < ARRAY_SIZE(sch->curr_status.mda); i++) { - sch->curr_status.mda[i] = 0; - } -} - -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; - - if (!channel_subsys.css[real_cssid]) { - return NULL; - } - - if (!channel_subsys.css[real_cssid]->sch_set[ssid]) { - return NULL; - } - - return channel_subsys.css[real_cssid]->sch_set[ssid]->sch[schid]; -} - -bool css_subch_visible(SubchDev *sch) -{ - if (sch->ssid > channel_subsys.max_ssid) { - return false; - } - - if (sch->cssid != channel_subsys.default_cssid) { - return (channel_subsys.max_cssid > 0); - } - - return true; -} - -bool css_present(uint8_t cssid) -{ - return (channel_subsys.css[cssid] != NULL); -} - -bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno) -{ - if (!channel_subsys.css[cssid]) { - return false; - } - if (!channel_subsys.css[cssid]->sch_set[ssid]) { - return false; - } - - return !!test_bit(devno, - channel_subsys.css[cssid]->sch_set[ssid]->devnos_used); -} - -void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, - uint16_t devno, SubchDev *sch) -{ - CssImage *css; - SubchSet *s_set; - - trace_css_assign_subch(sch ? "assign" : "deassign", cssid, ssid, schid, - devno); - 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]; - - if (!css->sch_set[ssid]) { - css->sch_set[ssid] = g_malloc0(sizeof(SubchSet)); - } - s_set = css->sch_set[ssid]; - - s_set->sch[schid] = sch; - if (sch) { - set_bit(schid, s_set->schids_used); - set_bit(devno, s_set->devnos_used); - } else { - clear_bit(schid, s_set->schids_used); - clear_bit(devno, s_set->devnos_used); - } -} - -void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid) -{ - CrwContainer *crw_cont; - - trace_css_crw(rsc, erc, rsid, chain ? "(chained)" : ""); - /* TODO: Maybe use a static crw pool? */ - crw_cont = g_try_malloc0(sizeof(CrwContainer)); - if (!crw_cont) { - channel_subsys.crws_lost = true; - return; - } - crw_cont->crw.flags = (rsc << 8) | erc; - if (chain) { - crw_cont->crw.flags |= CRW_FLAGS_MASK_C; - } - crw_cont->crw.rsid = rsid; - if (channel_subsys.crws_lost) { - crw_cont->crw.flags |= CRW_FLAGS_MASK_R; - channel_subsys.crws_lost = false; - } - - QTAILQ_INSERT_TAIL(&channel_subsys.pending_crws, crw_cont, sibling); - - if (channel_subsys.do_crw_mchk) { - channel_subsys.do_crw_mchk = false; - /* Inject crw pending machine check. */ - s390_crw_mchk(); - } -} - -void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, - int hotplugged, int add) -{ - uint8_t guest_cssid; - bool chain_crw; - - if (add && !hotplugged) { - return; - } - if (channel_subsys.max_cssid == 0) { - /* Default cssid shows up as 0. */ - guest_cssid = (cssid == channel_subsys.default_cssid) ? 0 : cssid; - } else { - /* Show real cssid to the guest. */ - guest_cssid = cssid; - } - /* - * 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))) { - return; - } - 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, - (guest_cssid << 8) | (ssid << 4)); - } -} - -void css_generate_chp_crws(uint8_t cssid, uint8_t chpid) -{ - /* TODO */ -} - -void css_generate_css_crws(uint8_t 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; - return 0; -} - -int css_enable_mss(void) -{ - trace_css_enable_facility("mss"); - channel_subsys.max_ssid = MAX_SSID; - return 0; -} - -void subch_device_save(SubchDev *s, QEMUFile *f) -{ - int i; - - qemu_put_byte(f, s->cssid); - qemu_put_byte(f, s->ssid); - qemu_put_be16(f, s->schid); - qemu_put_be16(f, s->devno); - qemu_put_byte(f, s->thinint_active); - /* SCHIB */ - /* PMCW */ - qemu_put_be32(f, s->curr_status.pmcw.intparm); - qemu_put_be16(f, s->curr_status.pmcw.flags); - qemu_put_be16(f, s->curr_status.pmcw.devno); - qemu_put_byte(f, s->curr_status.pmcw.lpm); - qemu_put_byte(f, s->curr_status.pmcw.pnom); - qemu_put_byte(f, s->curr_status.pmcw.lpum); - qemu_put_byte(f, s->curr_status.pmcw.pim); - qemu_put_be16(f, s->curr_status.pmcw.mbi); - qemu_put_byte(f, s->curr_status.pmcw.pom); - qemu_put_byte(f, s->curr_status.pmcw.pam); - qemu_put_buffer(f, s->curr_status.pmcw.chpid, 8); - qemu_put_be32(f, s->curr_status.pmcw.chars); - /* SCSW */ - qemu_put_be16(f, s->curr_status.scsw.flags); - qemu_put_be16(f, s->curr_status.scsw.ctrl); - qemu_put_be32(f, s->curr_status.scsw.cpa); - qemu_put_byte(f, s->curr_status.scsw.dstat); - qemu_put_byte(f, s->curr_status.scsw.cstat); - qemu_put_be16(f, s->curr_status.scsw.count); - qemu_put_be64(f, s->curr_status.mba); - qemu_put_buffer(f, s->curr_status.mda, 4); - /* end SCHIB */ - qemu_put_buffer(f, s->sense_data, 32); - qemu_put_be64(f, s->channel_prog); - /* last cmd */ - qemu_put_byte(f, s->last_cmd.cmd_code); - qemu_put_byte(f, s->last_cmd.flags); - qemu_put_be16(f, s->last_cmd.count); - qemu_put_be32(f, s->last_cmd.cda); - qemu_put_byte(f, s->last_cmd_valid); - qemu_put_byte(f, s->id.reserved); - qemu_put_be16(f, s->id.cu_type); - qemu_put_byte(f, s->id.cu_model); - qemu_put_be16(f, s->id.dev_type); - qemu_put_byte(f, s->id.dev_model); - qemu_put_byte(f, s->id.unused); - for (i = 0; i < ARRAY_SIZE(s->id.ciw); i++) { - qemu_put_byte(f, s->id.ciw[i].type); - qemu_put_byte(f, s->id.ciw[i].command); - qemu_put_be16(f, s->id.ciw[i].count); - } - qemu_put_byte(f, s->ccw_fmt_1); - qemu_put_byte(f, s->ccw_no_data_cnt); -} - -int subch_device_load(SubchDev *s, QEMUFile *f) -{ - int i; - - s->cssid = qemu_get_byte(f); - s->ssid = qemu_get_byte(f); - s->schid = qemu_get_be16(f); - s->devno = qemu_get_be16(f); - s->thinint_active = qemu_get_byte(f); - /* SCHIB */ - /* PMCW */ - s->curr_status.pmcw.intparm = qemu_get_be32(f); - s->curr_status.pmcw.flags = qemu_get_be16(f); - s->curr_status.pmcw.devno = qemu_get_be16(f); - s->curr_status.pmcw.lpm = qemu_get_byte(f); - s->curr_status.pmcw.pnom = qemu_get_byte(f); - s->curr_status.pmcw.lpum = qemu_get_byte(f); - s->curr_status.pmcw.pim = qemu_get_byte(f); - s->curr_status.pmcw.mbi = qemu_get_be16(f); - s->curr_status.pmcw.pom = qemu_get_byte(f); - s->curr_status.pmcw.pam = qemu_get_byte(f); - qemu_get_buffer(f, s->curr_status.pmcw.chpid, 8); - s->curr_status.pmcw.chars = qemu_get_be32(f); - /* SCSW */ - s->curr_status.scsw.flags = qemu_get_be16(f); - s->curr_status.scsw.ctrl = qemu_get_be16(f); - s->curr_status.scsw.cpa = qemu_get_be32(f); - s->curr_status.scsw.dstat = qemu_get_byte(f); - s->curr_status.scsw.cstat = qemu_get_byte(f); - s->curr_status.scsw.count = qemu_get_be16(f); - s->curr_status.mba = qemu_get_be64(f); - qemu_get_buffer(f, s->curr_status.mda, 4); - /* end SCHIB */ - qemu_get_buffer(f, s->sense_data, 32); - s->channel_prog = qemu_get_be64(f); - /* last cmd */ - s->last_cmd.cmd_code = qemu_get_byte(f); - s->last_cmd.flags = qemu_get_byte(f); - s->last_cmd.count = qemu_get_be16(f); - s->last_cmd.cda = qemu_get_be32(f); - s->last_cmd_valid = qemu_get_byte(f); - s->id.reserved = qemu_get_byte(f); - s->id.cu_type = qemu_get_be16(f); - s->id.cu_model = qemu_get_byte(f); - s->id.dev_type = qemu_get_be16(f); - s->id.dev_model = qemu_get_byte(f); - s->id.unused = qemu_get_byte(f); - for (i = 0; i < ARRAY_SIZE(s->id.ciw); i++) { - s->id.ciw[i].type = qemu_get_byte(f); - s->id.ciw[i].command = qemu_get_byte(f); - s->id.ciw[i].count = qemu_get_be16(f); - } - s->ccw_fmt_1 = qemu_get_byte(f); - s->ccw_no_data_cnt = qemu_get_byte(f); - /* - * Hack alert. We don't migrate the channel subsystem status (no - * device!), but we need to find out if the guest enabled mss/mcss-e. - * If the subchannel is enabled, it certainly was able to access it, - * so adjust the max_ssid/max_cssid values for relevant ssid/cssid - * values. This is not watertight, but better than nothing. - */ - if (s->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA) { - if (s->ssid) { - channel_subsys.max_ssid = MAX_SSID; - } - if (s->cssid != channel_subsys.default_cssid) { - channel_subsys.max_cssid = MAX_CSSID; - } - } - return 0; -} - -void css_reset_sch(SubchDev *sch) -{ - PMCW *p = &sch->curr_status.pmcw; - - if ((p->flags & PMCW_FLAGS_MASK_ENA) != 0 && sch->disable_cb) { - sch->disable_cb(sch); - } - - p->intparm = 0; - p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | - PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | - PMCW_FLAGS_MASK_MP | PMCW_FLAGS_MASK_TF); - p->flags |= PMCW_FLAGS_MASK_DNV; - p->devno = sch->devno; - p->pim = 0x80; - p->lpm = p->pim; - p->pnom = 0; - p->lpum = 0; - p->mbi = 0; - p->pom = 0xff; - p->pam = 0x80; - p->chars &= ~(PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_XMWME | - PMCW_CHARS_MASK_CSENSE); - - memset(&sch->curr_status.scsw, 0, sizeof(sch->curr_status.scsw)); - sch->curr_status.mba = 0; - - sch->channel_prog = 0x0; - sch->last_cmd_valid = false; - sch->thinint_active = false; -} - -void css_reset(void) -{ - CrwContainer *crw_cont; - - /* Clean up monitoring. */ - 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); - g_free(crw_cont); - } - 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; -} diff --git a/qemu/hw/s390x/css.h b/qemu/hw/s390x/css.h deleted file mode 100644 index a320eea59..000000000 --- a/qemu/hw/s390x/css.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Channel subsystem structures and definitions. - * - * Copyright 2012 IBM Corp. - * Author(s): Cornelia Huck - * - * 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. - */ - -#ifndef CSS_H -#define CSS_H - -#include "hw/s390x/adapter.h" -#include "hw/s390x/s390_flic.h" -#include "ioinst.h" - -/* Channel subsystem constants. */ -#define MAX_SCHID 65535 -#define MAX_SSID 3 -#define MAX_CSSID 254 /* 255 is reserved */ -#define MAX_CHPID 255 - -#define MAX_CIWS 62 - -typedef struct CIW { - uint8_t type; - uint8_t command; - uint16_t count; -} QEMU_PACKED CIW; - -typedef struct SenseId { - /* common part */ - uint8_t reserved; /* always 0x'FF' */ - uint16_t cu_type; /* control unit type */ - uint8_t cu_model; /* control unit model */ - uint16_t dev_type; /* device type */ - uint8_t dev_model; /* device model */ - uint8_t unused; /* padding byte */ - /* extended part */ - CIW ciw[MAX_CIWS]; /* variable # of CIWs */ -} QEMU_PACKED SenseId; - -/* Channel measurements, from linux/drivers/s390/cio/cmf.c. */ -typedef struct CMB { - uint16_t ssch_rsch_count; - uint16_t sample_count; - uint32_t device_connect_time; - uint32_t function_pending_time; - uint32_t device_disconnect_time; - uint32_t control_unit_queuing_time; - uint32_t device_active_only_time; - uint32_t reserved[2]; -} QEMU_PACKED CMB; - -typedef struct CMBE { - uint32_t ssch_rsch_count; - uint32_t sample_count; - uint32_t device_connect_time; - uint32_t function_pending_time; - uint32_t device_disconnect_time; - uint32_t control_unit_queuing_time; - uint32_t device_active_only_time; - uint32_t device_busy_time; - uint32_t initial_command_response_time; - uint32_t reserved[7]; -} QEMU_PACKED CMBE; - -struct SubchDev { - /* channel-subsystem related things: */ - uint8_t cssid; - uint8_t ssid; - uint16_t schid; - uint16_t devno; - SCHIB curr_status; - uint8_t sense_data[32]; - hwaddr channel_prog; - CCW1 last_cmd; - bool last_cmd_valid; - bool ccw_fmt_1; - bool thinint_active; - uint8_t ccw_no_data_cnt; - /* transport-provided data: */ - int (*ccw_cb) (SubchDev *, CCW1); - void (*disable_cb)(SubchDev *); - SenseId id; - 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); -int subch_device_load(SubchDev *s, QEMUFile *f); -int css_create_css_image(uint8_t cssid, bool default_image); -bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno); -void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, - uint16_t devno, SubchDev *sch); -void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type); -uint16_t css_build_subchannel_id(SubchDev *sch); -void css_reset(void); -void css_reset_sch(SubchDev *sch); -void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid); -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 -int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap, - bool maskable, uint32_t *id); -#endif diff --git a/qemu/hw/s390x/event-facility.c b/qemu/hw/s390x/event-facility.c deleted file mode 100644 index 34b2faf01..000000000 --- a/qemu/hw/s390x/event-facility.c +++ /dev/null @@ -1,449 +0,0 @@ -/* - * SCLP - * Event Facility - * handles SCLP event types - * - Signal Quiesce - system power down - * - ASCII Console Data - VT220 read and write - * - * Copyright IBM, Corp. 2012 - * - * Authors: - * Heinz Graalfs - * - * 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 "qapi/error.h" -#include "sysemu/sysemu.h" - -#include "hw/s390x/sclp.h" -#include "hw/s390x/event-facility.h" - -typedef struct SCLPEventsBus { - BusState qbus; -} SCLPEventsBus; - -struct SCLPEventFacility { - SysBusDevice parent_obj; - SCLPEventsBus sbus; - /* guest' receive mask */ - unsigned int receive_mask; -}; - -/* return true if any child has event pending set */ -static bool event_pending(SCLPEventFacility *ef) -{ - BusChild *kid; - SCLPEvent *event; - SCLPEventClass *event_class; - - QTAILQ_FOREACH(kid, &ef->sbus.qbus.children, sibling) { - DeviceState *qdev = kid->child; - event = DO_UPCAST(SCLPEvent, qdev, qdev); - event_class = SCLP_EVENT_GET_CLASS(event); - if (event->event_pending && - event_class->get_send_mask() & ef->receive_mask) { - return true; - } - } - return false; -} - -static unsigned int get_host_send_mask(SCLPEventFacility *ef) -{ - unsigned int mask; - BusChild *kid; - SCLPEventClass *child; - - mask = 0; - - QTAILQ_FOREACH(kid, &ef->sbus.qbus.children, sibling) { - DeviceState *qdev = kid->child; - child = SCLP_EVENT_GET_CLASS((SCLPEvent *) qdev); - mask |= child->get_send_mask(); - } - return mask; -} - -static unsigned int get_host_receive_mask(SCLPEventFacility *ef) -{ - unsigned int mask; - BusChild *kid; - SCLPEventClass *child; - - mask = 0; - - QTAILQ_FOREACH(kid, &ef->sbus.qbus.children, sibling) { - DeviceState *qdev = kid->child; - child = SCLP_EVENT_GET_CLASS((SCLPEvent *) qdev); - mask |= child->get_receive_mask(); - } - return mask; -} - -static uint16_t write_event_length_check(SCCB *sccb) -{ - int slen; - unsigned elen = 0; - EventBufferHeader *event; - WriteEventData *wed = (WriteEventData *) sccb; - - event = (EventBufferHeader *) &wed->ebh; - for (slen = sccb_data_len(sccb); slen > 0; slen -= elen) { - elen = be16_to_cpu(event->length); - if (elen < sizeof(*event) || elen > slen) { - return SCLP_RC_EVENT_BUFFER_SYNTAX_ERROR; - } - event = (void *) event + elen; - } - if (slen) { - return SCLP_RC_INCONSISTENT_LENGTHS; - } - return SCLP_RC_NORMAL_COMPLETION; -} - -static uint16_t handle_write_event_buf(SCLPEventFacility *ef, - EventBufferHeader *event_buf, SCCB *sccb) -{ - uint16_t rc; - BusChild *kid; - SCLPEvent *event; - SCLPEventClass *ec; - - rc = SCLP_RC_INVALID_FUNCTION; - - QTAILQ_FOREACH(kid, &ef->sbus.qbus.children, sibling) { - DeviceState *qdev = kid->child; - event = (SCLPEvent *) qdev; - ec = SCLP_EVENT_GET_CLASS(event); - - if (ec->write_event_data && - ec->can_handle_event(event_buf->type)) { - rc = ec->write_event_data(event, event_buf); - break; - } - } - return rc; -} - -static uint16_t handle_sccb_write_events(SCLPEventFacility *ef, SCCB *sccb) -{ - uint16_t rc; - int slen; - unsigned elen = 0; - EventBufferHeader *event_buf; - WriteEventData *wed = (WriteEventData *) sccb; - - event_buf = &wed->ebh; - rc = SCLP_RC_NORMAL_COMPLETION; - - /* loop over all contained event buffers */ - for (slen = sccb_data_len(sccb); slen > 0; slen -= elen) { - elen = be16_to_cpu(event_buf->length); - - /* in case of a previous error mark all trailing buffers - * as not accepted */ - if (rc != SCLP_RC_NORMAL_COMPLETION) { - event_buf->flags &= ~(SCLP_EVENT_BUFFER_ACCEPTED); - } else { - rc = handle_write_event_buf(ef, event_buf, sccb); - } - event_buf = (void *) event_buf + elen; - } - return rc; -} - -static void write_event_data(SCLPEventFacility *ef, SCCB *sccb) -{ - if (sccb->h.function_code != SCLP_FC_NORMAL_WRITE) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_FUNCTION); - goto out; - } - if (be16_to_cpu(sccb->h.length) < 8) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH); - goto out; - } - /* first do a sanity check of the write events */ - sccb->h.response_code = cpu_to_be16(write_event_length_check(sccb)); - - /* if no early error, then execute */ - if (sccb->h.response_code == be16_to_cpu(SCLP_RC_NORMAL_COMPLETION)) { - sccb->h.response_code = - cpu_to_be16(handle_sccb_write_events(ef, sccb)); - } - -out: - return; -} - -static uint16_t handle_sccb_read_events(SCLPEventFacility *ef, SCCB *sccb, - unsigned int mask) -{ - uint16_t rc; - int slen; - unsigned elen; - BusChild *kid; - SCLPEvent *event; - SCLPEventClass *ec; - EventBufferHeader *event_buf; - ReadEventData *red = (ReadEventData *) sccb; - - event_buf = &red->ebh; - event_buf->length = 0; - slen = sizeof(sccb->data); - - rc = SCLP_RC_NO_EVENT_BUFFERS_STORED; - - QTAILQ_FOREACH(kid, &ef->sbus.qbus.children, sibling) { - DeviceState *qdev = kid->child; - event = (SCLPEvent *) qdev; - ec = SCLP_EVENT_GET_CLASS(event); - - if (mask & ec->get_send_mask()) { - if (ec->read_event_data(event, event_buf, &slen)) { - elen = be16_to_cpu(event_buf->length); - event_buf = (EventBufferHeader *) ((char *)event_buf + elen); - rc = SCLP_RC_NORMAL_COMPLETION; - } - } - } - - if (sccb->h.control_mask[2] & SCLP_VARIABLE_LENGTH_RESPONSE) { - /* architecture suggests to reset variable-length-response bit */ - sccb->h.control_mask[2] &= ~SCLP_VARIABLE_LENGTH_RESPONSE; - /* with a new length value */ - sccb->h.length = cpu_to_be16(SCCB_SIZE - slen); - } - return rc; -} - -static void read_event_data(SCLPEventFacility *ef, SCCB *sccb) -{ - unsigned int sclp_active_selection_mask; - unsigned int sclp_cp_receive_mask; - - ReadEventData *red = (ReadEventData *) sccb; - - if (be16_to_cpu(sccb->h.length) != SCCB_SIZE) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH); - goto out; - } - - sclp_cp_receive_mask = ef->receive_mask; - - /* get active selection mask */ - switch (sccb->h.function_code) { - case SCLP_UNCONDITIONAL_READ: - sclp_active_selection_mask = sclp_cp_receive_mask; - break; - case SCLP_SELECTIVE_READ: - 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; - } - break; - default: - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_FUNCTION); - goto out; - } - sccb->h.response_code = cpu_to_be16( - handle_sccb_read_events(ef, sccb, sclp_active_selection_mask)); - -out: - return; -} - -static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb) -{ - WriteEventMask *we_mask = (WriteEventMask *) sccb; - - /* Attention: We assume that Linux uses 4-byte masks, what it actually - does. Architecture allows for masks of variable size, though */ - if (be16_to_cpu(we_mask->mask_length) != 4) { - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_MASK_LENGTH); - goto out; - } - - /* keep track of the guest's capability masks */ - ef->receive_mask = be32_to_cpu(we_mask->cp_receive_mask); - - /* return the SCLP's capability masks to the guest */ - we_mask->send_mask = cpu_to_be32(get_host_send_mask(ef)); - we_mask->receive_mask = cpu_to_be32(get_host_receive_mask(ef)); - - sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); - -out: - return; -} - -/* qemu object creation and initialization functions */ - -#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 = { - .name = TYPE_SCLP_EVENTS_BUS, - .parent = TYPE_BUS, - .class_init = sclp_events_bus_class_init, -}; - -static void command_handler(SCLPEventFacility *ef, SCCB *sccb, uint64_t code) -{ - switch (code & SCLP_CMD_CODE_MASK) { - case SCLP_CMD_READ_EVENT_DATA: - read_event_data(ef, sccb); - break; - case SCLP_CMD_WRITE_EVENT_DATA: - write_event_data(ef, sccb); - break; - case SCLP_CMD_WRITE_EVENT_MASK: - write_event_mask(ef, sccb); - break; - default: - sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); - break; - } -} - -static const VMStateDescription vmstate_event_facility = { - .name = "vmstate-event-facility", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(receive_mask, SCLPEventFacility), - VMSTATE_END_OF_LIST() - } -}; - -static void init_event_facility(Object *obj) -{ - 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); - - 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); - - 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) -{ - SCLPEventFacility *sdev = EVENT_FACILITY(dev); - - sdev->receive_mask = 0; -} - -static void init_event_facility_class(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sbdc = SYS_BUS_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(sbdc); - SCLPEventFacilityClass *k = EVENT_FACILITY_CLASS(dc); - - dc->reset = reset_event_facility; - dc->vmsd = &vmstate_event_facility; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - k->command_handler = command_handler; - k->event_pending = event_pending; -} - -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), -}; - -static void event_realize(DeviceState *qdev, Error **errp) -{ - SCLPEvent *event = SCLP_EVENT(qdev); - SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event); - - if (child->init) { - int rc = child->init(event); - if (rc < 0) { - error_setg(errp, "SCLP event initialization failed."); - return; - } - } -} - -static void event_unrealize(DeviceState *qdev, Error **errp) -{ - SCLPEvent *event = SCLP_EVENT(qdev); - SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event); - if (child->exit) { - int rc = child->exit(event); - if (rc < 0) { - error_setg(errp, "SCLP event exit failed."); - return; - } - } -} - -static void event_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->bus_type = TYPE_SCLP_EVENTS_BUS; - dc->realize = event_realize; - dc->unrealize = event_unrealize; -} - -static const TypeInfo sclp_event_type_info = { - .name = TYPE_SCLP_EVENT, - .parent = TYPE_DEVICE, - .instance_size = sizeof(SCLPEvent), - .class_init = event_class_init, - .class_size = sizeof(SCLPEventClass), - .abstract = true, -}; - -static void register_types(void) -{ - type_register_static(&sclp_events_bus_info); - type_register_static(&sclp_event_facility_info); - type_register_static(&sclp_event_type_info); -} - -type_init(register_types) diff --git a/qemu/hw/s390x/ipl.c b/qemu/hw/s390x/ipl.c deleted file mode 100644 index f10420027..000000000 --- a/qemu/hw/s390x/ipl.c +++ /dev/null @@ -1,298 +0,0 @@ -/* - * bootloader support - * - * Copyright IBM, Corp. 2012 - * - * Authors: - * Christian Borntraeger - * - * 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 "qapi/error.h" -#include "sysemu/sysemu.h" -#include "cpu.h" -#include "elf.h" -#include "hw/loader.h" -#include "hw/s390x/virtio-ccw.h" -#include "hw/s390x/css.h" -#include "ipl.h" - -#define KERN_IMAGE_START 0x010000UL -#define KERN_PARM_AREA 0x010480UL -#define INITRD_START 0x800000UL -#define INITRD_PARM_START 0x010408UL -#define INITRD_PARM_SIZE 0x010410UL -#define PARMFILE_START 0x001000UL -#define ZIPL_IMAGE_START 0x009000UL -#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) - -static const VMStateDescription vmstate_iplb = { - .name = "ipl/iplb", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT8_ARRAY(reserved1, IplParameterBlock, 110), - VMSTATE_UINT16(devno, IplParameterBlock), - VMSTATE_UINT8_ARRAY(reserved2, IplParameterBlock, 88), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_ipl = { - .name = "ipl", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT64(start_addr, S390IPLState), - VMSTATE_UINT64(bios_start_addr, S390IPLState), - VMSTATE_STRUCT(iplb, S390IPLState, 0, vmstate_iplb, IplParameterBlock), - VMSTATE_BOOL(iplb_valid, S390IPLState), - VMSTATE_UINT8(cssid, S390IPLState), - VMSTATE_UINT8(ssid, S390IPLState), - VMSTATE_UINT16(devno, S390IPLState), - VMSTATE_END_OF_LIST() - } -}; - -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; - /* - * Assuming that our s390-ccw.img was linked for starting at address 0, - * we can simply add the destination address for the final location - */ - return srcaddr + dstaddr; -} - -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; - - /* - * Always load the bios if it was enforced, - * even if an external kernel has been defined. - */ - if (!ipl->kernel || ipl->enforce_bios) { - uint64_t fwbase = (MIN(ram_size, 0x80000000U) - 0x200000) & ~0xffffUL; - - if (bios_name == NULL) { - bios_name = ipl->firmware; - } - - bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (bios_filename == NULL) { - 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, - 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-ccw.img) */ - bios_size = load_image_targphys(bios_filename, ZIPL_IMAGE_START, - 4096); - ipl->bios_start_addr = ZIPL_IMAGE_START; - } - g_free(bios_filename); - - if (bios_size == -1) { - error_setg(&err, "could not load bootloader '%s'", bios_name); - goto error; - } - - /* default boot target is the bios */ - ipl->start_addr = ipl->bios_start_addr; - } - - if (ipl->kernel) { - kernel_size = load_elf(ipl->kernel, NULL, NULL, &pentry, NULL, - NULL, 1, EM_S390, 0, 0); - if (kernel_size < 0) { - kernel_size = load_image_targphys(ipl->kernel, 0, ram_size); - } - if (kernel_size < 0) { - 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 - * kernel parameters here as well. Note: For old kernels (up to 3.2) - * we can not rely on the ELF entry point - it was 0x800 (the SALIPL - * loader) and it won't work. For this case we force it to 0x10000, too. - */ - if (pentry == KERN_IMAGE_START || pentry == 0x800) { - ipl->start_addr = KERN_IMAGE_START; - /* Overwrite parameters in the kernel image, which are "rom" */ - strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline); - } else { - ipl->start_addr = pentry; - } - - if (ipl->initrd) { - ram_addr_t initrd_offset; - int initrd_size; - - initrd_offset = INITRD_START; - while (kernel_size + 0x100000 > initrd_offset) { - initrd_offset += 0x100000; - } - initrd_size = load_image_targphys(ipl->initrd, initrd_offset, - ram_size - initrd_offset); - if (initrd_size == -1) { - error_setg(&err, "could not load initrd '%s'", ipl->initrd); - goto error; - } - - /* - * we have to overwrite values in the kernel image, - * which are "rom" - */ - stq_p(rom_ptr(INITRD_PARM_START), initrd_offset); - stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size); - } - } - qemu_register_reset(qdev_reset_all_fn, dev); -error: - error_propagate(errp, err); -} - -static Property s390_ipl_properties[] = { - DEFINE_PROP_STRING("kernel", S390IPLState, kernel), - DEFINE_PROP_STRING("initrd", S390IPLState, initrd), - DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline), - DEFINE_PROP_STRING("firmware", S390IPLState, firmware), - DEFINE_PROP_BOOL("enforce_bios", S390IPLState, enforce_bios, false), - DEFINE_PROP_END_OF_LIST(), -}; - -/* - * In addition to updating the iplstate, this function returns: - * - 0 if system was ipled with external kernel - * - -1 if no valid boot device was found - * - ccw id of the boot device otherwise - */ -static uint64_t s390_update_iplstate(S390IPLState *ipl) -{ - DeviceState *dev_st; - - if (ipl->iplb_valid) { - ipl->cssid = 0; - ipl->ssid = 0; - ipl->devno = ipl->iplb.devno; - goto out; - } - - if (ipl->kernel) { - return 0; - } - - dev_st = get_boot_device(0); - if (dev_st) { - VirtioCcwDevice *ccw_dev = (VirtioCcwDevice *) object_dynamic_cast( - OBJECT(qdev_get_parent_bus(dev_st)->parent), - TYPE_VIRTIO_CCW_DEVICE); - if (ccw_dev) { - ipl->cssid = ccw_dev->sch->cssid; - ipl->ssid = ccw_dev->sch->ssid; - ipl->devno = ccw_dev->sch->devno; - goto out; - } - } - - return -1; -out: - return (uint32_t) (ipl->cssid << 24 | ipl->ssid << 16 | ipl->devno); -} - -void s390_ipl_update_diag308(IplParameterBlock *iplb) -{ - S390IPLState *ipl = get_ipl_device(); - - ipl->iplb = *iplb; - ipl->iplb_valid = true; -} - -IplParameterBlock *s390_ipl_get_iplb(void) -{ - S390IPLState *ipl = get_ipl_device(); - - if (!ipl->iplb_valid) { - return NULL; - } - return &ipl->iplb; -} - -void s390_reipl_request(void) -{ - S390IPLState *ipl = get_ipl_device(); - - 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); - - if (!ipl->reipl_requested) { - ipl->iplb_valid = false; - } - ipl->reipl_requested = false; -} - -static void s390_ipl_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = s390_ipl_realize; - dc->props = s390_ipl_properties; - dc->reset = s390_ipl_reset; - dc->vmsd = &vmstate_ipl; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo s390_ipl_info = { - .class_init = s390_ipl_class_init, - .parent = TYPE_DEVICE, - .name = TYPE_S390_IPL, - .instance_size = sizeof(S390IPLState), -}; - -static void s390_ipl_register_types(void) -{ - type_register_static(&s390_ipl_info); -} - -type_init(s390_ipl_register_types) diff --git a/qemu/hw/s390x/ipl.h b/qemu/hw/s390x/ipl.h deleted file mode 100644 index 6b48ed7b9..000000000 --- a/qemu/hw/s390x/ipl.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * s390 IPL device - * - * Copyright 2015 IBM Corp. - * Author(s): Zhang Fan - * - * 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. - */ - -#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; - -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 deleted file mode 100644 index 918b58543..000000000 --- a/qemu/hw/s390x/s390-pci-bus.c +++ /dev/null @@ -1,621 +0,0 @@ -/* - * s390 PCI BUS - * - * Copyright 2014 IBM Corp. - * Author(s): Frank Blaschka - * Hong Bo Li - * Yi Min Zhao - * - * 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 "qemu-common.h" -#include "cpu.h" -#include "s390-pci-bus.h" -#include -#include -#include - -/* #define DEBUG_S390PCI_BUS */ -#ifdef DEBUG_S390PCI_BUS -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, "S390pci-bus: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - -int chsc_sei_nt2_get_event(void *res) -{ - ChscSeiNt2Res *nt2_res = (ChscSeiNt2Res *)res; - PciCcdfAvail *accdf; - PciCcdfErr *eccdf; - int rc = 1; - SeiContainer *sei_cont; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s) { - return rc; - } - - sei_cont = QTAILQ_FIRST(&s->pending_sei); - if (sei_cont) { - QTAILQ_REMOVE(&s->pending_sei, sei_cont, link); - nt2_res->nt = 2; - nt2_res->cc = sei_cont->cc; - nt2_res->length = cpu_to_be16(sizeof(ChscSeiNt2Res)); - switch (sei_cont->cc) { - case 1: /* error event */ - eccdf = (PciCcdfErr *)nt2_res->ccdf; - eccdf->fid = cpu_to_be32(sei_cont->fid); - eccdf->fh = cpu_to_be32(sei_cont->fh); - eccdf->e = cpu_to_be32(sei_cont->e); - eccdf->faddr = cpu_to_be64(sei_cont->faddr); - eccdf->pec = cpu_to_be16(sei_cont->pec); - break; - case 2: /* availability event */ - accdf = (PciCcdfAvail *)nt2_res->ccdf; - accdf->fid = cpu_to_be32(sei_cont->fid); - accdf->fh = cpu_to_be32(sei_cont->fh); - accdf->pec = cpu_to_be16(sei_cont->pec); - break; - default: - abort(); - } - g_free(sei_cont); - rc = 0; - } - - return rc; -} - -int chsc_sei_nt2_have_event(void) -{ - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s) { - return 0; - } - - return !QTAILQ_EMPTY(&s->pending_sei); -} - -S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid) -{ - S390PCIBusDevice *pbdev; - int i; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s) { - return NULL; - } - - for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = &s->pbdev[i]; - if ((pbdev->fh != 0) && (pbdev->fid == fid)) { - return pbdev; - } - } - - return NULL; -} - -void s390_pci_sclp_configure(int configure, SCCB *sccb) -{ - PciCfgSccb *psccb = (PciCfgSccb *)sccb; - S390PCIBusDevice *pbdev = s390_pci_find_dev_by_fid(be32_to_cpu(psccb->aid)); - uint16_t rc; - - if (pbdev) { - if ((configure == 1 && pbdev->configured == true) || - (configure == 0 && pbdev->configured == false)) { - rc = SCLP_RC_NO_ACTION_REQUIRED; - } else { - pbdev->configured = !pbdev->configured; - rc = SCLP_RC_NORMAL_COMPLETION; - } - } else { - DPRINTF("sclp config %d no dev found\n", configure); - rc = SCLP_RC_ADAPTER_ID_NOT_RECOGNIZED; - } - - psccb->header.response_code = cpu_to_be16(rc); -} - -static uint32_t s390_pci_get_pfid(PCIDevice *pdev) -{ - return PCI_SLOT(pdev->devfn); -} - -static uint32_t s390_pci_get_pfh(PCIDevice *pdev) -{ - return PCI_SLOT(pdev->devfn) | FH_VIRT; -} - -S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx) -{ - S390PCIBusDevice *pbdev; - int i; - int j = 0; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s) { - return NULL; - } - - for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = &s->pbdev[i]; - - if (pbdev->fh == 0) { - continue; - } - - if (j == idx) { - return pbdev; - } - j++; - } - - return NULL; -} - -S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh) -{ - S390PCIBusDevice *pbdev; - int i; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s || !fh) { - return NULL; - } - - for (i = 0; i < PCI_SLOT_MAX; i++) { - pbdev = &s->pbdev[i]; - if (pbdev->fh == fh) { - return pbdev; - } - } - - return NULL; -} - -static void s390_pci_generate_event(uint8_t cc, uint16_t pec, uint32_t fh, - uint32_t fid, uint64_t faddr, uint32_t e) -{ - SeiContainer *sei_cont; - S390pciState *s = S390_PCI_HOST_BRIDGE( - object_resolve_path(TYPE_S390_PCI_HOST_BRIDGE, NULL)); - - if (!s) { - return; - } - - sei_cont = g_malloc0(sizeof(SeiContainer)); - sei_cont->fh = fh; - sei_cont->fid = fid; - sei_cont->cc = cc; - sei_cont->pec = pec; - sei_cont->faddr = faddr; - sei_cont->e = e; - - QTAILQ_INSERT_TAIL(&s->pending_sei, sei_cont, link); - css_generate_css_crws(0); -} - -static void s390_pci_generate_plug_event(uint16_t pec, uint32_t fh, - uint32_t fid) -{ - s390_pci_generate_event(2, pec, fh, fid, 0, 0); -} - -static void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, - uint32_t fid, uint64_t faddr, - uint32_t e) -{ - s390_pci_generate_event(1, pec, fh, fid, faddr, e); -} - -static void s390_pci_set_irq(void *opaque, int irq, int level) -{ - /* nothing to do */ -} - -static int s390_pci_map_irq(PCIDevice *pci_dev, int irq_num) -{ - /* nothing to do */ - return 0; -} - -static uint64_t s390_pci_get_table_origin(uint64_t iota) -{ - return iota & ~ZPCI_IOTA_RTTO_FLAG; -} - -static unsigned int calc_rtx(dma_addr_t ptr) -{ - return ((unsigned long) ptr >> ZPCI_RT_SHIFT) & ZPCI_INDEX_MASK; -} - -static unsigned int calc_sx(dma_addr_t ptr) -{ - return ((unsigned long) ptr >> ZPCI_ST_SHIFT) & ZPCI_INDEX_MASK; -} - -static unsigned int calc_px(dma_addr_t ptr) -{ - return ((unsigned long) ptr >> PAGE_SHIFT) & ZPCI_PT_MASK; -} - -static uint64_t get_rt_sto(uint64_t entry) -{ - return ((entry & ZPCI_TABLE_TYPE_MASK) == ZPCI_TABLE_TYPE_RTX) - ? (entry & ZPCI_RTE_ADDR_MASK) - : 0; -} - -static uint64_t get_st_pto(uint64_t entry) -{ - return ((entry & ZPCI_TABLE_TYPE_MASK) == ZPCI_TABLE_TYPE_SX) - ? (entry & ZPCI_STE_ADDR_MASK) - : 0; -} - -static uint64_t s390_guest_io_table_walk(uint64_t guest_iota, - uint64_t guest_dma_address) -{ - uint64_t sto_a, pto_a, px_a; - uint64_t sto, pto, pte; - uint32_t rtx, sx, px; - - rtx = calc_rtx(guest_dma_address); - sx = calc_sx(guest_dma_address); - px = calc_px(guest_dma_address); - - sto_a = guest_iota + rtx * sizeof(uint64_t); - sto = address_space_ldq(&address_space_memory, sto_a, - MEMTXATTRS_UNSPECIFIED, NULL); - sto = get_rt_sto(sto); - if (!sto) { - pte = 0; - goto out; - } - - pto_a = sto + sx * sizeof(uint64_t); - pto = address_space_ldq(&address_space_memory, pto_a, - MEMTXATTRS_UNSPECIFIED, NULL); - pto = get_st_pto(pto); - if (!pto) { - pte = 0; - goto out; - } - - px_a = pto + px * sizeof(uint64_t); - pte = address_space_ldq(&address_space_memory, px_a, - MEMTXATTRS_UNSPECIFIED, NULL); - -out: - return pte; -} - -static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr, - bool is_write) -{ - uint64_t pte; - uint32_t flags; - S390PCIBusDevice *pbdev = container_of(iommu, S390PCIBusDevice, iommu_mr); - S390pciState *s; - IOMMUTLBEntry ret = { - .target_as = &address_space_memory, - .iova = 0, - .translated_addr = 0, - .addr_mask = ~(hwaddr)0, - .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 - */ - if (addr == ZPCI_MSI_ADDR) { - ret.target_as = &s->msix_notify_as; - ret.iova = addr; - ret.translated_addr = addr; - ret.addr_mask = 0xfff; - ret.perm = IOMMU_RW; - return ret; - } - - if (!pbdev->g_iota) { - pbdev->error_state = true; - pbdev->lgstg_blocked = true; - s390_pci_generate_error_event(ERR_EVENT_INVALAS, pbdev->fh, pbdev->fid, - addr, 0); - return ret; - } - - if (addr < pbdev->pba || addr > pbdev->pal) { - pbdev->error_state = true; - pbdev->lgstg_blocked = true; - s390_pci_generate_error_event(ERR_EVENT_OORANGE, pbdev->fh, pbdev->fid, - addr, 0); - return ret; - } - - pte = s390_guest_io_table_walk(s390_pci_get_table_origin(pbdev->g_iota), - addr); - - if (!pte) { - pbdev->error_state = true; - pbdev->lgstg_blocked = true; - s390_pci_generate_error_event(ERR_EVENT_SERR, pbdev->fh, pbdev->fid, - addr, ERR_EVENT_Q_BIT); - return ret; - } - - flags = pte & ZPCI_PTE_FLAG_MASK; - ret.iova = addr; - ret.translated_addr = pte & ZPCI_PTE_ADDR_MASK; - ret.addr_mask = 0xfff; - - if (flags & ZPCI_PTE_INVALID) { - ret.perm = IOMMU_NONE; - } else { - ret.perm = IOMMU_RW; - } - - return ret; -} - -static const MemoryRegionIOMMUOps s390_iommu_ops = { - .translate = s390_translate_iommu, -}; - -static AddressSpace *s390_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) -{ - S390pciState *s = opaque; - - return &s->pbdev[PCI_SLOT(devfn)].as; -} - -static uint8_t set_ind_atomic(uint64_t ind_loc, uint8_t to_be_set) -{ - uint8_t ind_old, ind_new; - hwaddr len = 1; - uint8_t *ind_addr; - - ind_addr = cpu_physical_memory_map(ind_loc, &len, 1); - if (!ind_addr) { - s390_pci_generate_error_event(ERR_EVENT_AIRERR, 0, 0, 0, 0); - return -1; - } - do { - ind_old = *ind_addr; - ind_new = ind_old | to_be_set; - } while (atomic_cmpxchg(ind_addr, ind_old, ind_new) != ind_old); - cpu_physical_memory_unmap(ind_addr, len, 1, len); - - return ind_old; -} - -static void s390_msi_ctrl_write(void *opaque, hwaddr addr, uint64_t data, - unsigned int size) -{ - S390PCIBusDevice *pbdev; - uint32_t io_int_word; - uint32_t fid = data >> ZPCI_MSI_VEC_BITS; - uint32_t vec = data & ZPCI_MSI_VEC_MASK; - uint64_t ind_bit; - uint32_t sum_bit; - uint32_t e = 0; - - DPRINTF("write_msix data 0x%" PRIx64 " fid %d vec 0x%x\n", data, fid, vec); - - pbdev = s390_pci_find_dev_by_fid(fid); - if (!pbdev) { - e |= (vec << ERR_EVENT_MVN_OFFSET); - s390_pci_generate_error_event(ERR_EVENT_NOMSI, 0, fid, addr, e); - return; - } - - if (!(pbdev->fh & FH_ENABLED)) { - return; - } - - ind_bit = pbdev->routes.adapter.ind_offset; - sum_bit = pbdev->routes.adapter.summary_offset; - - set_ind_atomic(pbdev->routes.adapter.ind_addr + (ind_bit + vec) / 8, - 0x80 >> ((ind_bit + vec) % 8)); - if (!set_ind_atomic(pbdev->routes.adapter.summary_addr + sum_bit / 8, - 0x80 >> (sum_bit % 8))) { - io_int_word = (pbdev->isc << 27) | IO_INT_WORD_AI; - s390_io_interrupt(0, 0, 0, io_int_word); - } -} - -static uint64_t s390_msi_ctrl_read(void *opaque, hwaddr addr, unsigned size) -{ - return 0xffffffff; -} - -static const MemoryRegionOps s390_msi_ctrl_ops = { - .write = s390_msi_ctrl_write, - .read = s390_msi_ctrl_read, - .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++) { - 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), - &s390_msi_ctrl_ops, s, "msix-s390", UINT64_MAX); - address_space_init(&s->msix_notify_as, &s->msix_notify_mr, "msix-pci"); -} - -static int s390_pcihost_init(SysBusDevice *dev) -{ - PCIBus *b; - BusState *bus; - PCIHostState *phb = PCI_HOST_BRIDGE(dev); - S390pciState *s = S390_PCI_HOST_BRIDGE(dev); - - DPRINTF("host_init\n"); - - b = pci_register_bus(DEVICE(dev), NULL, - s390_pci_set_irq, s390_pci_map_irq, NULL, - get_system_memory(), get_system_io(), 0, 64, - TYPE_PCI_BUS); - s390_pcihost_init_as(s); - pci_setup_iommu(b, s390_pci_dma_iommu, s); - - bus = BUS(b); - qbus_set_hotplug_handler(bus, DEVICE(dev), NULL); - phb->bus = b; - QTAILQ_INIT(&s->pending_sei); - return 0; -} - -static int s390_pcihost_setup_msix(S390PCIBusDevice *pbdev) -{ - uint8_t pos; - uint16_t ctrl; - uint32_t table, pba; - - pos = pci_find_capability(pbdev->pdev, PCI_CAP_ID_MSIX); - if (!pos) { - pbdev->msix.available = false; - return 0; - } - - 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)); - pba = pci_host_config_read_common(pbdev->pdev, pos + PCI_MSIX_PBA, - pci_config_size(pbdev->pdev), sizeof(pba)); - - pbdev->msix.table_bar = table & PCI_MSIX_FLAGS_BIRMASK; - pbdev->msix.table_offset = table & ~PCI_MSIX_FLAGS_BIRMASK; - pbdev->msix.pba_bar = pba & PCI_MSIX_FLAGS_BIRMASK; - pbdev->msix.pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK; - pbdev->msix.entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1; - pbdev->msix.available = true; - return 0; -} - -static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - PCIDevice *pci_dev = PCI_DEVICE(dev); - S390PCIBusDevice *pbdev; - S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pci_dev) - ->qbus.parent); - - pbdev = &s->pbdev[PCI_SLOT(pci_dev->devfn)]; - - pbdev->fid = s390_pci_get_pfid(pci_dev); - pbdev->pdev = pci_dev; - pbdev->configured = true; - pbdev->fh = s390_pci_get_pfh(pci_dev); - - s390_pcihost_setup_msix(pbdev); - - if (dev->hotplugged) { - s390_pci_generate_plug_event(HP_EVENT_RESERVED_TO_STANDBY, - pbdev->fh, pbdev->fid); - s390_pci_generate_plug_event(HP_EVENT_TO_CONFIGURED, - pbdev->fh, pbdev->fid); - } -} - -static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - PCIDevice *pci_dev = PCI_DEVICE(dev); - S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pci_dev) - ->qbus.parent); - S390PCIBusDevice *pbdev = &s->pbdev[PCI_SLOT(pci_dev->devfn)]; - - if (pbdev->configured) { - pbdev->configured = false; - s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES, - pbdev->fh, pbdev->fid); - } - - s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED, - pbdev->fh, pbdev->fid); - pbdev->fh = 0; - pbdev->fid = 0; - pbdev->pdev = NULL; - object_unparent(OBJECT(pci_dev)); -} - -static void s390_pcihost_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - - dc->cannot_instantiate_with_device_add_yet = true; - k->init = s390_pcihost_init; - hc->plug = s390_pcihost_hot_plug; - hc->unplug = s390_pcihost_hot_unplug; - msi_nonbroken = true; -} - -static const TypeInfo s390_pcihost_info = { - .name = TYPE_S390_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(S390pciState), - .class_init = s390_pcihost_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - -static void s390_pci_register_types(void) -{ - type_register_static(&s390_pcihost_info); -} - -type_init(s390_pci_register_types) diff --git a/qemu/hw/s390x/s390-pci-bus.h b/qemu/hw/s390x/s390-pci-bus.h deleted file mode 100644 index 59fd5c958..000000000 --- a/qemu/hw/s390x/s390-pci-bus.h +++ /dev/null @@ -1,256 +0,0 @@ -/* - * s390 PCI BUS definitions - * - * Copyright 2014 IBM Corp. - * Author(s): Frank Blaschka - * Hong Bo Li - * Yi Min Zhao - * - * 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. - */ - -#ifndef HW_S390_PCI_BUS_H -#define HW_S390_PCI_BUS_H - -#include -#include -#include "hw/s390x/sclp.h" -#include "hw/s390x/s390_flic.h" -#include "hw/s390x/css.h" - -#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) \ - OBJECT_CHECK(S390pciState, (obj), TYPE_S390_PCI_HOST_BRIDGE) - -#define HP_EVENT_TO_CONFIGURED 0x0301 -#define HP_EVENT_RESERVED_TO_STANDBY 0x0302 -#define HP_EVENT_CONFIGURED_TO_STBRES 0x0304 -#define HP_EVENT_STANDBY_TO_RESERVED 0x0308 - -#define ERR_EVENT_INVALAS 0x1 -#define ERR_EVENT_OORANGE 0x2 -#define ERR_EVENT_INVALTF 0x3 -#define ERR_EVENT_TPROTE 0x4 -#define ERR_EVENT_APROTE 0x5 -#define ERR_EVENT_KEYE 0x6 -#define ERR_EVENT_INVALTE 0x7 -#define ERR_EVENT_INVALTL 0x8 -#define ERR_EVENT_TT 0x9 -#define ERR_EVENT_INVALMS 0xa -#define ERR_EVENT_SERR 0xb -#define ERR_EVENT_NOMSI 0x10 -#define ERR_EVENT_INVALBV 0x11 -#define ERR_EVENT_AIBV 0x12 -#define ERR_EVENT_AIRERR 0x13 -#define ERR_EVENT_FMBA 0x2a -#define ERR_EVENT_FMBUP 0x2b -#define ERR_EVENT_FMBPRO 0x2c -#define ERR_EVENT_CCONF 0x30 -#define ERR_EVENT_SERVAC 0x3a -#define ERR_EVENT_PERMERR 0x3b - -#define ERR_EVENT_Q_BIT 0x2 -#define ERR_EVENT_MVN_OFFSET 16 - -#define ZPCI_MSI_VEC_BITS 11 -#define ZPCI_MSI_VEC_MASK 0x7ff - -#define ZPCI_MSI_ADDR 0xfe00000000000000ULL -#define ZPCI_SDMA_ADDR 0x100000000ULL -#define ZPCI_EDMA_ADDR 0x1ffffffffffffffULL - -#define PAGE_SHIFT 12 -#define PAGE_MASK (~(PAGE_SIZE-1)) -#define PAGE_DEFAULT_ACC 0 -#define PAGE_DEFAULT_KEY (PAGE_DEFAULT_ACC << 4) - -/* I/O Translation Anchor (IOTA) */ -enum ZpciIoatDtype { - ZPCI_IOTA_STO = 0, - ZPCI_IOTA_RTTO = 1, - ZPCI_IOTA_RSTO = 2, - ZPCI_IOTA_RFTO = 3, - ZPCI_IOTA_PFAA = 4, - ZPCI_IOTA_IOPFAA = 5, - ZPCI_IOTA_IOPTO = 7 -}; - -#define ZPCI_IOTA_IOT_ENABLED 0x800ULL -#define ZPCI_IOTA_DT_ST (ZPCI_IOTA_STO << 2) -#define ZPCI_IOTA_DT_RT (ZPCI_IOTA_RTTO << 2) -#define ZPCI_IOTA_DT_RS (ZPCI_IOTA_RSTO << 2) -#define ZPCI_IOTA_DT_RF (ZPCI_IOTA_RFTO << 2) -#define ZPCI_IOTA_DT_PF (ZPCI_IOTA_PFAA << 2) -#define ZPCI_IOTA_FS_4K 0 -#define ZPCI_IOTA_FS_1M 1 -#define ZPCI_IOTA_FS_2G 2 -#define ZPCI_KEY (PAGE_DEFAULT_KEY << 5) - -#define ZPCI_IOTA_STO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_ST) -#define ZPCI_IOTA_RTTO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_RT) -#define ZPCI_IOTA_RSTO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_RS) -#define ZPCI_IOTA_RFTO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_RF) -#define ZPCI_IOTA_RFAA_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY |\ - ZPCI_IOTA_DT_PF | ZPCI_IOTA_FS_2G) - -/* I/O Region and segment tables */ -#define ZPCI_INDEX_MASK 0x7ffULL - -#define ZPCI_TABLE_TYPE_MASK 0xc -#define ZPCI_TABLE_TYPE_RFX 0xc -#define ZPCI_TABLE_TYPE_RSX 0x8 -#define ZPCI_TABLE_TYPE_RTX 0x4 -#define ZPCI_TABLE_TYPE_SX 0x0 - -#define ZPCI_TABLE_LEN_RFX 0x3 -#define ZPCI_TABLE_LEN_RSX 0x3 -#define ZPCI_TABLE_LEN_RTX 0x3 - -#define ZPCI_TABLE_OFFSET_MASK 0xc0 -#define ZPCI_TABLE_SIZE 0x4000 -#define ZPCI_TABLE_ALIGN ZPCI_TABLE_SIZE -#define ZPCI_TABLE_ENTRY_SIZE (sizeof(unsigned long)) -#define ZPCI_TABLE_ENTRIES (ZPCI_TABLE_SIZE / ZPCI_TABLE_ENTRY_SIZE) - -#define ZPCI_TABLE_BITS 11 -#define ZPCI_PT_BITS 8 -#define ZPCI_ST_SHIFT (ZPCI_PT_BITS + PAGE_SHIFT) -#define ZPCI_RT_SHIFT (ZPCI_ST_SHIFT + ZPCI_TABLE_BITS) - -#define ZPCI_RTE_FLAG_MASK 0x3fffULL -#define ZPCI_RTE_ADDR_MASK (~ZPCI_RTE_FLAG_MASK) -#define ZPCI_STE_FLAG_MASK 0x7ffULL -#define ZPCI_STE_ADDR_MASK (~ZPCI_STE_FLAG_MASK) - -/* I/O Page tables */ -#define ZPCI_PTE_VALID_MASK 0x400 -#define ZPCI_PTE_INVALID 0x400 -#define ZPCI_PTE_VALID 0x000 -#define ZPCI_PT_SIZE 0x800 -#define ZPCI_PT_ALIGN ZPCI_PT_SIZE -#define ZPCI_PT_ENTRIES (ZPCI_PT_SIZE / ZPCI_TABLE_ENTRY_SIZE) -#define ZPCI_PT_MASK (ZPCI_PT_ENTRIES - 1) - -#define ZPCI_PTE_FLAG_MASK 0xfffULL -#define ZPCI_PTE_ADDR_MASK (~ZPCI_PTE_FLAG_MASK) - -/* Shared bits */ -#define ZPCI_TABLE_VALID 0x00 -#define ZPCI_TABLE_INVALID 0x20 -#define ZPCI_TABLE_PROTECTED 0x200 -#define ZPCI_TABLE_UNPROTECTED 0x000 - -#define ZPCI_TABLE_VALID_MASK 0x20 -#define ZPCI_TABLE_PROT_MASK 0x200 - -typedef struct SeiContainer { - QTAILQ_ENTRY(SeiContainer) link; - uint32_t fid; - uint32_t fh; - uint8_t cc; - uint16_t pec; - uint64_t faddr; - uint32_t e; -} SeiContainer; - -typedef struct PciCcdfErr { - uint32_t reserved1; - uint32_t fh; - uint32_t fid; - uint32_t e; - uint64_t faddr; - uint32_t reserved3; - uint16_t reserved4; - uint16_t pec; -} QEMU_PACKED PciCcdfErr; - -typedef struct PciCcdfAvail { - uint32_t reserved1; - uint32_t fh; - uint32_t fid; - uint32_t reserved2; - uint32_t reserved3; - uint32_t reserved4; - uint32_t reserved5; - uint16_t reserved6; - uint16_t pec; -} QEMU_PACKED PciCcdfAvail; - -typedef struct ChscSeiNt2Res { - uint16_t length; - uint16_t code; - uint16_t reserved1; - uint8_t reserved2; - uint8_t nt; - uint8_t flags; - uint8_t reserved3; - uint8_t reserved4; - uint8_t cc; - uint32_t reserved5[13]; - uint8_t ccdf[4016]; -} QEMU_PACKED ChscSeiNt2Res; - -typedef struct PciCfgSccb { - SCCBHeader header; - uint8_t atype; - uint8_t reserved1; - uint16_t reserved2; - uint32_t aid; -} QEMU_PACKED PciCfgSccb; - -typedef struct S390MsixInfo { - bool available; - uint8_t table_bar; - uint8_t pba_bar; - uint16_t entries; - uint32_t table_offset; - uint32_t pba_offset; -} S390MsixInfo; - -typedef struct S390PCIBusDevice { - PCIDevice *pdev; - bool configured; - bool error_state; - bool lgstg_blocked; - uint32_t fh; - uint32_t fid; - uint64_t g_iota; - uint64_t pba; - uint64_t pal; - uint64_t fmb_addr; - uint8_t isc; - uint16_t noi; - uint8_t sum; - S390MsixInfo msix; - AdapterRoutes routes; - AddressSpace as; - MemoryRegion mr; - MemoryRegion iommu_mr; - IndAddr *summary_ind; - IndAddr *indicator; -} S390PCIBusDevice; - -typedef struct S390pciState { - PCIHostState parent_obj; - S390PCIBusDevice pbdev[PCI_SLOT_MAX]; - AddressSpace msix_notify_as; - MemoryRegion msix_notify_mr; - QTAILQ_HEAD(, SeiContainer) pending_sei; -} 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); - -#endif diff --git a/qemu/hw/s390x/s390-pci-inst.c b/qemu/hw/s390x/s390-pci-inst.c deleted file mode 100644 index b28e7d14f..000000000 --- a/qemu/hw/s390x/s390-pci-inst.c +++ /dev/null @@ -1,846 +0,0 @@ -/* - * s390 PCI instructions - * - * Copyright 2014 IBM Corp. - * Author(s): Frank Blaschka - * Hong Bo Li - * Yi Min Zhao - * - * 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 "qemu-common.h" -#include "cpu.h" -#include "s390-pci-inst.h" -#include "s390-pci-bus.h" -#include -#include - -/* #define DEBUG_S390PCI_INST */ -#ifdef DEBUG_S390PCI_INST -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, "s390pci-inst: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - -static void s390_set_status_code(CPUS390XState *env, - uint8_t r, uint64_t status_code) -{ - env->regs[r] &= ~0xff000000ULL; - env->regs[r] |= (status_code & 0xff) << 24; -} - -static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) -{ - S390PCIBusDevice *pbdev; - uint32_t res_code, initial_l2, g_l2, finish; - int rc, idx; - uint64_t resume_token; - - rc = 0; - if (lduw_p(&rrb->request.hdr.len) != 32) { - res_code = CLP_RC_LEN; - rc = -EINVAL; - goto out; - } - - if ((ldl_p(&rrb->request.fmt) & CLP_MASK_FMT) != 0) { - res_code = CLP_RC_FMT; - rc = -EINVAL; - goto out; - } - - if ((ldl_p(&rrb->request.fmt) & ~CLP_MASK_FMT) != 0 || - ldq_p(&rrb->request.reserved1) != 0 || - ldq_p(&rrb->request.reserved2) != 0) { - res_code = CLP_RC_RESNOT0; - rc = -EINVAL; - goto out; - } - - resume_token = ldq_p(&rrb->request.resume_token); - - if (resume_token) { - pbdev = s390_pci_find_dev_by_idx(resume_token); - if (!pbdev) { - res_code = CLP_RC_LISTPCI_BADRT; - rc = -EINVAL; - goto out; - } - } - - if (lduw_p(&rrb->response.hdr.len) < 48) { - res_code = CLP_RC_8K; - rc = -EINVAL; - goto out; - } - - initial_l2 = lduw_p(&rrb->response.hdr.len); - if ((initial_l2 - LIST_PCI_HDR_LEN) % sizeof(ClpFhListEntry) - != 0) { - res_code = CLP_RC_LEN; - rc = -EINVAL; - *cc = 3; - goto out; - } - - stl_p(&rrb->response.fmt, 0); - stq_p(&rrb->response.reserved1, 0); - stq_p(&rrb->response.reserved2, 0); - stl_p(&rrb->response.mdd, FH_VIRT); - stw_p(&rrb->response.max_fn, PCI_MAX_FUNCTIONS); - rrb->response.entry_size = sizeof(ClpFhListEntry); - finish = 0; - idx = resume_token; - g_l2 = LIST_PCI_HDR_LEN; - do { - pbdev = s390_pci_find_dev_by_idx(idx); - if (!pbdev) { - finish = 1; - break; - } - stw_p(&rrb->response.fh_list[idx - resume_token].device_id, - 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, - 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); - - g_l2 += sizeof(ClpFhListEntry); - /* Add endian check for DPRINTF? */ - DPRINTF("g_l2 %d vendor id 0x%x device id 0x%x fid 0x%x fh 0x%x\n", - g_l2, - lduw_p(&rrb->response.fh_list[idx - resume_token].vendor_id), - lduw_p(&rrb->response.fh_list[idx - resume_token].device_id), - ldl_p(&rrb->response.fh_list[idx - resume_token].fid), - ldl_p(&rrb->response.fh_list[idx - resume_token].fh)); - idx++; - } while (g_l2 < initial_l2); - - if (finish == 1) { - resume_token = 0; - } else { - resume_token = idx; - } - stq_p(&rrb->response.resume_token, resume_token); - stw_p(&rrb->response.hdr.len, g_l2); - stw_p(&rrb->response.hdr.rsp, CLP_RC_OK); -out: - if (rc) { - DPRINTF("list pci failed rc 0x%x\n", rc); - stw_p(&rrb->response.hdr.rsp, res_code); - } - return rc; -} - -int clp_service_call(S390CPU *cpu, uint8_t r2) -{ - ClpReqHdr *reqh; - ClpRspHdr *resh; - S390PCIBusDevice *pbdev; - uint32_t req_len; - uint32_t res_len; - uint8_t buffer[4096 * 2]; - uint8_t cc = 0; - CPUS390XState *env = &cpu->env; - int i; - - cpu_synchronize_state(CPU(cpu)); - - if (env->psw.mask & PSW_MASK_PSTATE) { - program_interrupt(env, PGM_PRIVILEGED, 4); - return 0; - } - - if (s390_cpu_virt_mem_read(cpu, env->regs[r2], r2, buffer, sizeof(*reqh))) { - return 0; - } - reqh = (ClpReqHdr *)buffer; - req_len = lduw_p(&reqh->len); - if (req_len < 16 || req_len > 8184 || (req_len % 8 != 0)) { - program_interrupt(env, PGM_OPERAND, 4); - return 0; - } - - if (s390_cpu_virt_mem_read(cpu, env->regs[r2], r2, buffer, - req_len + sizeof(*resh))) { - return 0; - } - resh = (ClpRspHdr *)(buffer + req_len); - res_len = lduw_p(&resh->len); - if (res_len < 8 || res_len > 8176 || (res_len % 8 != 0)) { - program_interrupt(env, PGM_OPERAND, 4); - return 0; - } - if ((req_len + res_len) > 8192) { - program_interrupt(env, PGM_OPERAND, 4); - return 0; - } - - if (s390_cpu_virt_mem_read(cpu, env->regs[r2], r2, buffer, - req_len + res_len)) { - return 0; - } - - if (req_len != 32) { - stw_p(&resh->rsp, CLP_RC_LEN); - goto out; - } - - switch (lduw_p(&reqh->cmd)) { - case CLP_LIST_PCI: { - ClpReqRspListPci *rrb = (ClpReqRspListPci *)buffer; - list_pci(rrb, &cc); - break; - } - case CLP_SET_PCI_FN: { - ClpReqSetPci *reqsetpci = (ClpReqSetPci *)reqh; - ClpRspSetPci *ressetpci = (ClpRspSetPci *)resh; - - pbdev = s390_pci_find_dev_by_fh(ldl_p(&reqsetpci->fh)); - if (!pbdev) { - stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FH); - goto out; - } - - switch (reqsetpci->oc) { - case CLP_SET_ENABLE_PCI_FN: - 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 & ~FH_ENABLED; - pbdev->error_state = false; - pbdev->lgstg_blocked = false; - stl_p(&ressetpci->fh, pbdev->fh); - stw_p(&ressetpci->hdr.rsp, CLP_RC_OK); - break; - default: - DPRINTF("unknown set pci command\n"); - stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); - break; - } - break; - } - case CLP_QUERY_PCI_FN: { - ClpReqQueryPci *reqquery = (ClpReqQueryPci *)reqh; - ClpRspQueryPci *resquery = (ClpRspQueryPci *)resh; - - pbdev = s390_pci_find_dev_by_fh(ldl_p(&reqquery->fh)); - if (!pbdev) { - DPRINTF("query pci no pci dev\n"); - stw_p(&resquery->hdr.rsp, CLP_RC_SETPCIFN_FH); - goto out; - } - - for (i = 0; i < PCI_BAR_COUNT; i++) { - uint32_t data = pci_get_long(pbdev->pdev->config + - PCI_BASE_ADDRESS_0 + (i * 4)); - - stl_p(&resquery->bar[i], data); - resquery->bar_size[i] = pbdev->pdev->io_regions[i].size ? - ctz64(pbdev->pdev->io_regions[i].size) : 0; - DPRINTF("bar %d addr 0x%x size 0x%" PRIx64 "barsize 0x%x\n", i, - ldl_p(&resquery->bar[i]), - pbdev->pdev->io_regions[i].size, - resquery->bar_size[i]); - } - - stq_p(&resquery->sdma, ZPCI_SDMA_ADDR); - stq_p(&resquery->edma, ZPCI_EDMA_ADDR); - stw_p(&resquery->pchid, 0); - stw_p(&resquery->ug, 1); - stl_p(&resquery->uid, pbdev->fid); - stw_p(&resquery->hdr.rsp, CLP_RC_OK); - break; - } - case CLP_QUERY_PCI_FNGRP: { - ClpRspQueryPciGrp *resgrp = (ClpRspQueryPciGrp *)resh; - resgrp->fr = 1; - stq_p(&resgrp->dasm, 0); - stq_p(&resgrp->msia, ZPCI_MSI_ADDR); - stw_p(&resgrp->mui, 0); - stw_p(&resgrp->i, 128); - resgrp->version = 0; - - stw_p(&resgrp->hdr.rsp, CLP_RC_OK); - break; - } - default: - DPRINTF("unknown clp command\n"); - stw_p(&resh->rsp, CLP_RC_CMD); - break; - } - -out: - if (s390_cpu_virt_mem_write(cpu, env->regs[r2], r2, buffer, - req_len + res_len)) { - return 0; - } - setcc(cpu, cc); - return 0; -} - -int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) -{ - CPUS390XState *env = &cpu->env; - S390PCIBusDevice *pbdev; - uint64_t offset; - uint64_t data; - uint8_t len; - uint32_t fh; - uint8_t pcias; - - cpu_synchronize_state(CPU(cpu)); - - if (env->psw.mask & PSW_MASK_PSTATE) { - program_interrupt(env, PGM_PRIVILEGED, 4); - return 0; - } - - if (r2 & 0x1) { - program_interrupt(env, PGM_SPECIFICATION, 4); - return 0; - } - - fh = env->regs[r2] >> 32; - pcias = (env->regs[r2] >> 16) & 0xf; - len = env->regs[r2] & 0xf; - offset = env->regs[r2 + 1]; - - pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { - DPRINTF("pcilg no pci dev\n"); - setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); - return 0; - } - - if (pbdev->lgstg_blocked) { - setcc(cpu, ZPCI_PCI_LS_ERR); - s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED); - return 0; - } - - if (pcias < 6) { - if ((8 - (offset & 0x7)) < len) { - program_interrupt(env, PGM_OPERAND, 4); - return 0; - } - MemoryRegion *mr = pbdev->pdev->io_regions[pcias].memory; - memory_region_dispatch_read(mr, offset, &data, len, - MEMTXATTRS_UNSPECIFIED); - } else if (pcias == 15) { - if ((4 - (offset & 0x3)) < len) { - program_interrupt(env, PGM_OPERAND, 4); - return 0; - } - data = pci_host_config_read_common( - pbdev->pdev, offset, pci_config_size(pbdev->pdev), len); - - switch (len) { - case 1: - break; - case 2: - data = bswap16(data); - break; - case 4: - data = bswap32(data); - break; - case 8: - data = bswap64(data); - break; - default: - program_interrupt(env, PGM_OPERAND, 4); - return 0; - } - } else { - DPRINTF("invalid space\n"); - setcc(cpu, ZPCI_PCI_LS_ERR); - s390_set_status_code(env, r2, ZPCI_PCI_ST_INVAL_AS); - return 0; - } - - env->regs[r1] = data; - setcc(cpu, ZPCI_PCI_LS_OK); - return 0; -} - -static void update_msix_table_msg_data(S390PCIBusDevice *pbdev, uint64_t offset, - uint64_t *data, uint8_t len) -{ - uint32_t val; - uint8_t *msg_data; - - if (offset % PCI_MSIX_ENTRY_SIZE != 8) { - return; - } - - if (len != 4) { - DPRINTF("access msix table msg data but len is %d\n", len); - return; - } - - msg_data = (uint8_t *)data - offset % PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_VECTOR_CTRL; - val = pci_get_long(msg_data) | (pbdev->fid << ZPCI_MSI_VEC_BITS); - pci_set_long(msg_data, val); - DPRINTF("update msix msg_data to 0x%" PRIx64 "\n", *data); -} - -static int trap_msix(S390PCIBusDevice *pbdev, uint64_t offset, uint8_t pcias) -{ - if (pbdev->msix.available && pbdev->msix.table_bar == pcias && - offset >= pbdev->msix.table_offset && - offset <= pbdev->msix.table_offset + - (pbdev->msix.entries - 1) * PCI_MSIX_ENTRY_SIZE) { - return 1; - } else { - return 0; - } -} - -int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) -{ - CPUS390XState *env = &cpu->env; - uint64_t offset, data; - S390PCIBusDevice *pbdev; - uint8_t len; - uint32_t fh; - uint8_t pcias; - - cpu_synchronize_state(CPU(cpu)); - - if (env->psw.mask & PSW_MASK_PSTATE) { - program_interrupt(env, PGM_PRIVILEGED, 4); - return 0; - } - - if (r2 & 0x1) { - program_interrupt(env, PGM_SPECIFICATION, 4); - return 0; - } - - fh = env->regs[r2] >> 32; - pcias = (env->regs[r2] >> 16) & 0xf; - len = env->regs[r2] & 0xf; - offset = env->regs[r2 + 1]; - - pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { - DPRINTF("pcistg no pci dev\n"); - setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); - return 0; - } - - if (pbdev->lgstg_blocked) { - setcc(cpu, ZPCI_PCI_LS_ERR); - s390_set_status_code(env, r2, ZPCI_PCI_ST_BLOCKED); - return 0; - } - - data = env->regs[r1]; - if (pcias < 6) { - if ((8 - (offset & 0x7)) < len) { - program_interrupt(env, PGM_OPERAND, 4); - return 0; - } - MemoryRegion *mr; - if (trap_msix(pbdev, offset, pcias)) { - offset = offset - pbdev->msix.table_offset; - mr = &pbdev->pdev->msix_table_mmio; - update_msix_table_msg_data(pbdev, offset, &data, len); - } else { - mr = pbdev->pdev->io_regions[pcias].memory; - } - - memory_region_dispatch_write(mr, offset, data, len, - MEMTXATTRS_UNSPECIFIED); - } else if (pcias == 15) { - if ((4 - (offset & 0x3)) < len) { - program_interrupt(env, PGM_OPERAND, 4); - return 0; - } - switch (len) { - case 1: - break; - case 2: - data = bswap16(data); - break; - case 4: - data = bswap32(data); - break; - case 8: - data = bswap64(data); - break; - default: - program_interrupt(env, PGM_OPERAND, 4); - return 0; - } - - pci_host_config_write_common(pbdev->pdev, offset, - pci_config_size(pbdev->pdev), - data, len); - } else { - DPRINTF("pcistg invalid space\n"); - setcc(cpu, ZPCI_PCI_LS_ERR); - s390_set_status_code(env, r2, ZPCI_PCI_ST_INVAL_AS); - return 0; - } - - setcc(cpu, ZPCI_PCI_LS_OK); - return 0; -} - -int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) -{ - CPUS390XState *env = &cpu->env; - uint32_t fh; - S390PCIBusDevice *pbdev; - hwaddr start, end; - IOMMUTLBEntry entry; - MemoryRegion *mr; - - cpu_synchronize_state(CPU(cpu)); - - if (env->psw.mask & PSW_MASK_PSTATE) { - program_interrupt(env, PGM_PRIVILEGED, 4); - goto out; - } - - if (r2 & 0x1) { - program_interrupt(env, PGM_SPECIFICATION, 4); - goto out; - } - - fh = env->regs[r1] >> 32; - start = env->regs[r2]; - end = start + env->regs[r2 + 1]; - - pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev || !(pbdev->fh & FH_ENABLED)) { - DPRINTF("rpcit no pci dev\n"); - setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); - goto out; - } - - mr = &pbdev->iommu_mr; - while (start < end) { - entry = mr->iommu_ops->translate(mr, start, 0); - - if (!entry.translated_addr) { - setcc(cpu, ZPCI_PCI_LS_ERR); - goto out; - } - - memory_region_notify_iommu(mr, entry); - start += entry.addr_mask + 1; - } - - setcc(cpu, ZPCI_PCI_LS_OK); -out: - return 0; -} - -int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, - uint8_t ar) -{ - CPUS390XState *env = &cpu->env; - S390PCIBusDevice *pbdev; - MemoryRegion *mr; - int i; - uint32_t fh; - uint8_t pcias; - uint8_t len; - uint8_t buffer[128]; - - if (env->psw.mask & PSW_MASK_PSTATE) { - program_interrupt(env, PGM_PRIVILEGED, 6); - return 0; - } - - fh = env->regs[r1] >> 32; - pcias = (env->regs[r1] >> 16) & 0xf; - len = env->regs[r1] & 0xff; - - if (pcias > 5) { - DPRINTF("pcistb invalid space\n"); - setcc(cpu, ZPCI_PCI_LS_ERR); - s390_set_status_code(env, r1, ZPCI_PCI_ST_INVAL_AS); - return 0; - } - - switch (len) { - case 16: - case 32: - case 64: - case 128: - break; - default: - program_interrupt(env, PGM_SPECIFICATION, 6); - return 0; - } - - pbdev = s390_pci_find_dev_by_fh(fh); - 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; - } - - if (pbdev->lgstg_blocked) { - setcc(cpu, ZPCI_PCI_LS_ERR); - s390_set_status_code(env, r1, ZPCI_PCI_ST_BLOCKED); - return 0; - } - - mr = pbdev->pdev->io_regions[pcias].memory; - if (!memory_region_access_valid(mr, env->regs[r3], len, true)) { - program_interrupt(env, PGM_ADDRESSING, 6); - return 0; - } - - if (s390_cpu_virt_mem_read(cpu, gaddr, ar, buffer, len)) { - return 0; - } - - for (i = 0; i < len / 8; i++) { - memory_region_dispatch_write(mr, env->regs[r3] + i * 8, - ldq_p(buffer + i * 8), 8, - MEMTXATTRS_UNSPECIFIED); - } - - setcc(cpu, ZPCI_PCI_LS_OK); - return 0; -} - -static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) -{ - 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); - - 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)); - pbdev->routes.adapter.ind_addr = ldq_p(&fib.aibv); - pbdev->routes.adapter.ind_offset = FIB_DATA_AIBVO(ldl_p(&fib.data)); - pbdev->isc = FIB_DATA_ISC(ldl_p(&fib.data)); - pbdev->noi = FIB_DATA_NOI(ldl_p(&fib.data)); - pbdev->sum = FIB_DATA_SUM(ldl_p(&fib.data)); - - DPRINTF("reg_irqs adapter id %d\n", pbdev->routes.adapter.adapter_id); - return 0; -} - -static int dereg_irqs(S390PCIBusDevice *pbdev) -{ - 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; - pbdev->routes.adapter.ind_offset = 0; - pbdev->isc = 0; - pbdev->noi = 0; - pbdev->sum = 0; - - DPRINTF("dereg_irqs adapter id %d\n", pbdev->routes.adapter.adapter_id); - return 0; -} - -static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) -{ - uint64_t pba = ldq_p(&fib.pba); - uint64_t pal = ldq_p(&fib.pal); - uint64_t g_iota = ldq_p(&fib.iota); - uint8_t dt = (g_iota >> 2) & 0x7; - uint8_t t = (g_iota >> 11) & 0x1; - - if (pba > pal || pba < ZPCI_SDMA_ADDR || pal > ZPCI_EDMA_ADDR) { - program_interrupt(env, PGM_OPERAND, 6); - return -EINVAL; - } - - /* currently we only support designation type 1 with translation */ - if (!(dt == ZPCI_IOTA_RTTO && t)) { - error_report("unsupported ioat dt %d t %d", dt, t); - program_interrupt(env, PGM_OPERAND, 6); - return -EINVAL; - } - - pbdev->pba = pba; - pbdev->pal = pal; - pbdev->g_iota = g_iota; - - s390_pcihost_iommu_configure(pbdev, true); - - return 0; -} - -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) -{ - CPUS390XState *env = &cpu->env; - uint8_t oc; - uint32_t fh; - ZpciFib fib; - S390PCIBusDevice *pbdev; - uint64_t cc = ZPCI_PCI_LS_OK; - - if (env->psw.mask & PSW_MASK_PSTATE) { - program_interrupt(env, PGM_PRIVILEGED, 6); - return 0; - } - - oc = env->regs[r1] & 0xff; - fh = env->regs[r1] >> 32; - - if (fiba & 0x7) { - program_interrupt(env, PGM_SPECIFICATION, 6); - return 0; - } - - pbdev = s390_pci_find_dev_by_fh(fh); - 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; - } - - if (s390_cpu_virt_mem_read(cpu, fiba, ar, (uint8_t *)&fib, sizeof(fib))) { - return 0; - } - - switch (oc) { - case ZPCI_MOD_FC_REG_INT: - if (reg_irqs(env, pbdev, fib)) { - cc = ZPCI_PCI_LS_ERR; - } - break; - case ZPCI_MOD_FC_DEREG_INT: - dereg_irqs(pbdev); - break; - case ZPCI_MOD_FC_REG_IOAT: - if (reg_ioat(env, pbdev, fib)) { - cc = ZPCI_PCI_LS_ERR; - } - break; - case ZPCI_MOD_FC_DEREG_IOAT: - dereg_ioat(pbdev); - break; - case ZPCI_MOD_FC_REREG_IOAT: - dereg_ioat(pbdev); - if (reg_ioat(env, pbdev, fib)) { - cc = ZPCI_PCI_LS_ERR; - } - break; - case ZPCI_MOD_FC_RESET_ERROR: - pbdev->error_state = false; - pbdev->lgstg_blocked = false; - break; - case ZPCI_MOD_FC_RESET_BLOCK: - pbdev->lgstg_blocked = false; - break; - case ZPCI_MOD_FC_SET_MEASURE: - pbdev->fmb_addr = ldq_p(&fib.fmb_addr); - break; - default: - program_interrupt(&cpu->env, PGM_OPERAND, 6); - cc = ZPCI_PCI_LS_ERR; - } - - setcc(cpu, cc); - return 0; -} - -int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) -{ - CPUS390XState *env = &cpu->env; - uint32_t fh; - ZpciFib fib; - S390PCIBusDevice *pbdev; - uint32_t data; - uint64_t cc = ZPCI_PCI_LS_OK; - - if (env->psw.mask & PSW_MASK_PSTATE) { - program_interrupt(env, PGM_PRIVILEGED, 6); - return 0; - } - - fh = env->regs[r1] >> 32; - - if (fiba & 0x7) { - program_interrupt(env, PGM_SPECIFICATION, 6); - return 0; - } - - pbdev = s390_pci_find_dev_by_fh(fh); - if (!pbdev) { - setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); - return 0; - } - - memset(&fib, 0, sizeof(fib)); - stq_p(&fib.pba, pbdev->pba); - stq_p(&fib.pal, pbdev->pal); - stq_p(&fib.iota, pbdev->g_iota); - stq_p(&fib.aibv, pbdev->routes.adapter.ind_addr); - stq_p(&fib.aisb, pbdev->routes.adapter.summary_addr); - stq_p(&fib.fmb_addr, pbdev->fmb_addr); - - data = ((uint32_t)pbdev->isc << 28) | ((uint32_t)pbdev->noi << 16) | - ((uint32_t)pbdev->routes.adapter.ind_offset << 8) | - ((uint32_t)pbdev->sum << 7) | pbdev->routes.adapter.summary_offset; - stl_p(&fib.data, data); - - if (pbdev->fh & FH_ENABLED) { - fib.fc |= 0x80; - } - - if (pbdev->error_state) { - fib.fc |= 0x40; - } - - if (pbdev->lgstg_blocked) { - fib.fc |= 0x20; - } - - if (pbdev->g_iota) { - fib.fc |= 0x10; - } - - if (s390_cpu_virt_mem_write(cpu, fiba, ar, (uint8_t *)&fib, sizeof(fib))) { - return 0; - } - - setcc(cpu, cc); - return 0; -} diff --git a/qemu/hw/s390x/s390-pci-inst.h b/qemu/hw/s390x/s390-pci-inst.h deleted file mode 100644 index 70fa71395..000000000 --- a/qemu/hw/s390x/s390-pci-inst.h +++ /dev/null @@ -1,289 +0,0 @@ -/* - * s390 PCI instruction definitions - * - * Copyright 2014 IBM Corp. - * Author(s): Frank Blaschka - * Hong Bo Li - * Yi Min Zhao - * - * 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. - */ - -#ifndef HW_S390_PCI_INST_H -#define HW_S390_PCI_INST_H - -#include - -/* CLP common request & response block size */ -#define CLP_BLK_SIZE 4096 -#define PCI_BAR_COUNT 6 -#define PCI_MAX_FUNCTIONS 4096 - -typedef struct ClpReqHdr { - uint16_t len; - uint16_t cmd; -} QEMU_PACKED ClpReqHdr; - -typedef struct ClpRspHdr { - uint16_t len; - uint16_t rsp; -} QEMU_PACKED ClpRspHdr; - -/* CLP Response Codes */ -#define CLP_RC_OK 0x0010 /* Command request successfully */ -#define CLP_RC_CMD 0x0020 /* Command code not recognized */ -#define CLP_RC_PERM 0x0030 /* Command not authorized */ -#define CLP_RC_FMT 0x0040 /* Invalid command request format */ -#define CLP_RC_LEN 0x0050 /* Invalid command request length */ -#define CLP_RC_8K 0x0060 /* Command requires 8K LPCB */ -#define CLP_RC_RESNOT0 0x0070 /* Reserved field not zero */ -#define CLP_RC_NODATA 0x0080 /* No data available */ -#define CLP_RC_FC_UNKNOWN 0x0100 /* Function code not recognized */ - -/* - * Call Logical Processor - Command Codes - */ -#define CLP_LIST_PCI 0x0002 -#define CLP_QUERY_PCI_FN 0x0003 -#define CLP_QUERY_PCI_FNGRP 0x0004 -#define CLP_SET_PCI_FN 0x0005 - -/* PCI function handle list entry */ -typedef struct ClpFhListEntry { - uint16_t device_id; - uint16_t vendor_id; -#define CLP_FHLIST_MASK_CONFIG 0x80000000 - uint32_t config; - uint32_t fid; - uint32_t fh; -} QEMU_PACKED ClpFhListEntry; - -#define CLP_RC_SETPCIFN_FH 0x0101 /* Invalid PCI fn handle */ -#define CLP_RC_SETPCIFN_FHOP 0x0102 /* Fn handle not valid for op */ -#define CLP_RC_SETPCIFN_DMAAS 0x0103 /* Invalid DMA addr space */ -#define CLP_RC_SETPCIFN_RES 0x0104 /* Insufficient resources */ -#define CLP_RC_SETPCIFN_ALRDY 0x0105 /* Fn already in requested state */ -#define CLP_RC_SETPCIFN_ERR 0x0106 /* Fn in permanent error state */ -#define CLP_RC_SETPCIFN_RECPND 0x0107 /* Error recovery pending */ -#define CLP_RC_SETPCIFN_BUSY 0x0108 /* Fn busy */ -#define CLP_RC_LISTPCI_BADRT 0x010a /* Resume token not recognized */ -#define CLP_RC_QUERYPCIFG_PFGID 0x010b /* Unrecognized PFGID */ - -/* request or response block header length */ -#define LIST_PCI_HDR_LEN 32 - -/* Number of function handles fitting in response block */ -#define CLP_FH_LIST_NR_ENTRIES \ - ((CLP_BLK_SIZE - 2 * LIST_PCI_HDR_LEN) \ - / sizeof(ClpFhListEntry)) - -#define CLP_SET_ENABLE_PCI_FN 0 /* Yes, 0 enables it */ -#define CLP_SET_DISABLE_PCI_FN 1 /* Yes, 1 disables it */ - -#define CLP_UTIL_STR_LEN 64 - -#define CLP_MASK_FMT 0xf0000000 - -/* List PCI functions request */ -typedef struct ClpReqListPci { - ClpReqHdr hdr; - uint32_t fmt; - uint64_t reserved1; - uint64_t resume_token; - uint64_t reserved2; -} QEMU_PACKED ClpReqListPci; - -/* List PCI functions response */ -typedef struct ClpRspListPci { - ClpRspHdr hdr; - uint32_t fmt; - uint64_t reserved1; - uint64_t resume_token; - uint32_t mdd; - uint16_t max_fn; - uint8_t reserved2; - uint8_t entry_size; - ClpFhListEntry fh_list[CLP_FH_LIST_NR_ENTRIES]; -} QEMU_PACKED ClpRspListPci; - -/* Query PCI function request */ -typedef struct ClpReqQueryPci { - ClpReqHdr hdr; - uint32_t fmt; - uint64_t reserved1; - uint32_t fh; /* function handle */ - uint32_t reserved2; - uint64_t reserved3; -} QEMU_PACKED ClpReqQueryPci; - -/* Query PCI function response */ -typedef struct ClpRspQueryPci { - ClpRspHdr hdr; - uint32_t fmt; - uint64_t reserved1; - uint16_t vfn; /* virtual fn number */ -#define CLP_RSP_QPCI_MASK_UTIL 0x100 -#define CLP_RSP_QPCI_MASK_PFGID 0xff - uint16_t ug; - uint32_t fid; /* pci function id */ - uint8_t bar_size[PCI_BAR_COUNT]; - uint16_t pchid; - uint32_t bar[PCI_BAR_COUNT]; - uint64_t reserved2; - uint64_t sdma; /* start dma as */ - uint64_t edma; /* end dma as */ - uint32_t reserved3[11]; - uint32_t uid; - uint8_t util_str[CLP_UTIL_STR_LEN]; /* utility string */ -} QEMU_PACKED ClpRspQueryPci; - -/* Query PCI function group request */ -typedef struct ClpReqQueryPciGrp { - ClpReqHdr hdr; - uint32_t fmt; - uint64_t reserved1; -#define CLP_REQ_QPCIG_MASK_PFGID 0xff - uint32_t g; - uint32_t reserved2; - uint64_t reserved3; -} QEMU_PACKED ClpReqQueryPciGrp; - -/* Query PCI function group response */ -typedef struct ClpRspQueryPciGrp { - ClpRspHdr hdr; - uint32_t fmt; - uint64_t reserved1; -#define CLP_RSP_QPCIG_MASK_NOI 0xfff - uint16_t i; - uint8_t version; -#define CLP_RSP_QPCIG_MASK_FRAME 0x2 -#define CLP_RSP_QPCIG_MASK_REFRESH 0x1 - uint8_t fr; - uint16_t reserved2; - uint16_t mui; - uint64_t reserved3; - uint64_t dasm; /* dma address space mask */ - uint64_t msia; /* MSI address */ - uint64_t reserved4; - uint64_t reserved5; -} QEMU_PACKED ClpRspQueryPciGrp; - -/* Set PCI function request */ -typedef struct ClpReqSetPci { - ClpReqHdr hdr; - uint32_t fmt; - uint64_t reserved1; - uint32_t fh; /* function handle */ - uint16_t reserved2; - uint8_t oc; /* operation controls */ - uint8_t ndas; /* number of dma spaces */ - uint64_t reserved3; -} QEMU_PACKED ClpReqSetPci; - -/* Set PCI function response */ -typedef struct ClpRspSetPci { - ClpRspHdr hdr; - uint32_t fmt; - uint64_t reserved1; - uint32_t fh; /* function handle */ - uint32_t reserved3; - uint64_t reserved4; -} QEMU_PACKED ClpRspSetPci; - -typedef struct ClpReqRspListPci { - ClpReqListPci request; - ClpRspListPci response; -} QEMU_PACKED ClpReqRspListPci; - -typedef struct ClpReqRspSetPci { - ClpReqSetPci request; - ClpRspSetPci response; -} QEMU_PACKED ClpReqRspSetPci; - -typedef struct ClpReqRspQueryPci { - ClpReqQueryPci request; - ClpRspQueryPci response; -} QEMU_PACKED ClpReqRspQueryPci; - -typedef struct ClpReqRspQueryPciGrp { - ClpReqQueryPciGrp request; - ClpRspQueryPciGrp response; -} QEMU_PACKED ClpReqRspQueryPciGrp; - -/* Load/Store status codes */ -#define ZPCI_PCI_ST_FUNC_NOT_ENABLED 4 -#define ZPCI_PCI_ST_FUNC_IN_ERR 8 -#define ZPCI_PCI_ST_BLOCKED 12 -#define ZPCI_PCI_ST_INSUF_RES 16 -#define ZPCI_PCI_ST_INVAL_AS 20 -#define ZPCI_PCI_ST_FUNC_ALREADY_ENABLED 24 -#define ZPCI_PCI_ST_DMA_AS_NOT_ENABLED 28 -#define ZPCI_PCI_ST_2ND_OP_IN_INV_AS 36 -#define ZPCI_PCI_ST_FUNC_NOT_AVAIL 40 -#define ZPCI_PCI_ST_ALREADY_IN_RQ_STATE 44 - -/* Load/Store return codes */ -#define ZPCI_PCI_LS_OK 0 -#define ZPCI_PCI_LS_ERR 1 -#define ZPCI_PCI_LS_BUSY 2 -#define ZPCI_PCI_LS_INVAL_HANDLE 3 - -/* Modify PCI Function Controls */ -#define ZPCI_MOD_FC_REG_INT 2 -#define ZPCI_MOD_FC_DEREG_INT 3 -#define ZPCI_MOD_FC_REG_IOAT 4 -#define ZPCI_MOD_FC_DEREG_IOAT 5 -#define ZPCI_MOD_FC_REREG_IOAT 6 -#define ZPCI_MOD_FC_RESET_ERROR 7 -#define ZPCI_MOD_FC_RESET_BLOCK 9 -#define ZPCI_MOD_FC_SET_MEASURE 10 - -/* FIB function controls */ -#define ZPCI_FIB_FC_ENABLED 0x80 -#define ZPCI_FIB_FC_ERROR 0x40 -#define ZPCI_FIB_FC_LS_BLOCKED 0x20 -#define ZPCI_FIB_FC_DMAAS_REG 0x10 - -/* FIB function controls */ -#define ZPCI_FIB_FC_ENABLED 0x80 -#define ZPCI_FIB_FC_ERROR 0x40 -#define ZPCI_FIB_FC_LS_BLOCKED 0x20 -#define ZPCI_FIB_FC_DMAAS_REG 0x10 - -/* Function Information Block */ -typedef struct ZpciFib { - uint8_t fmt; /* format */ - uint8_t reserved1[7]; - uint8_t fc; /* function controls */ - uint8_t reserved2; - uint16_t reserved3; - uint32_t reserved4; - uint64_t pba; /* PCI base address */ - uint64_t pal; /* PCI address limit */ - uint64_t iota; /* I/O Translation Anchor */ -#define FIB_DATA_ISC(x) (((x) >> 28) & 0x7) -#define FIB_DATA_NOI(x) (((x) >> 16) & 0xfff) -#define FIB_DATA_AIBVO(x) (((x) >> 8) & 0x3f) -#define FIB_DATA_SUM(x) (((x) >> 7) & 0x1) -#define FIB_DATA_AISBO(x) ((x) & 0x3f) - uint32_t data; - uint32_t reserved5; - uint64_t aibv; /* Adapter int bit vector address */ - uint64_t aisb; /* Adapter int summary bit address */ - uint64_t fmb_addr; /* Function measurement address and key */ - uint32_t reserved6; - uint32_t gd; -} QEMU_PACKED ZpciFib; - -int clp_service_call(S390CPU *cpu, uint8_t r2); -int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2); -int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2); -int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2); -int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, - uint8_t ar); -int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar); -int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar); - -#endif diff --git a/qemu/hw/s390x/s390-skeys-kvm.c b/qemu/hw/s390x/s390-skeys-kvm.c deleted file mode 100644 index 131da56bb..000000000 --- a/qemu/hw/s390x/s390-skeys-kvm.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * s390 storage key device - * - * Copyright 2015 IBM Corp. - * Author(s): Jason J. Herne - * - * 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 deleted file mode 100644 index 6528ffed1..000000000 --- a/qemu/hw/s390x/s390-skeys.c +++ /dev/null @@ -1,415 +0,0 @@ -/* - * s390 storage key device - * - * Copyright 2015 IBM Corp. - * Author(s): Jason J. Herne - * - * 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-ccw.c b/qemu/hw/s390x/s390-virtio-ccw.c deleted file mode 100644 index e3df9c78b..000000000 --- a/qemu/hw/s390x/s390-virtio-ccw.c +++ /dev/null @@ -1,381 +0,0 @@ -/* - * virtio ccw machine - * - * Copyright 2012 IBM Corp. - * Author(s): Cornelia Huck - * - * 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 "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/boards.h" -#include "exec/address-spaces.h" -#include "s390-virtio.h" -#include "hw/s390x/sclp.h" -#include "hw/s390x/s390_flic.h" -#include "ioinst.h" -#include "css.h" -#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", -}; - -void subsystem_reset(void) -{ - DeviceState *dev; - int i; - - 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); - } - } -} - -static int virtio_ccw_hcall_notify(const uint64_t *args) -{ - uint64_t subch_id = args[0]; - uint64_t queue = args[1]; - SubchDev *sch; - int cssid, ssid, schid, m; - - if (ioinst_disassemble_sch_ident(subch_id, &m, &cssid, &ssid, &schid)) { - return -EINVAL; - } - sch = css_find_subch(m, cssid, ssid, schid); - if (!sch || !css_subch_visible(sch)) { - return -EINVAL; - } - if (queue >= VIRTIO_CCW_QUEUE_MAX) { - return -EINVAL; - } - virtio_queue_notify(virtio_ccw_get_vdev(sch), queue); - return 0; - -} - -static int virtio_ccw_hcall_early_printk(const uint64_t *args) -{ - uint64_t mem = args[0]; - - if (mem < ram_size) { - /* Early printk */ - return 0; - } - return -EINVAL; -} - -static void virtio_ccw_register_hcalls(void) -{ - s390_register_virtio_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, - virtio_ccw_hcall_notify); - /* Tolerate early printk. */ - s390_register_virtio_hypercall(KVM_S390_VIRTIO_NOTIFY, - virtio_ccw_hcall_early_printk); -} - -void s390_memory_init(ram_addr_t mem_size) -{ - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - - /* 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; - - s390_sclp_init(); - s390_memory_init(machine->ram_size); - - /* get a BUS */ - css_bus = virtual_css_bus_init(); - s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline, - machine->initrd_filename, "s390-ccw.img", true); - s390_flic_init(); - - dev = qdev_create(NULL, TYPE_S390_PCI_HOST_BRIDGE); - object_property_add_child(qdev_get_machine(), TYPE_S390_PCI_HOST_BRIDGE, - OBJECT(dev), NULL); - qdev_init_nofail(dev); - - /* register hypercalls */ - virtio_ccw_register_hcalls(); - - /* init CPUs */ - s390_init_cpus(machine); - - if (kvm_enabled()) { - kvm_s390_enable_css_support(s390_cpu_addr2state(0)); - } - /* - * Create virtual css and set it as default so that non mcss-e - * enabled guests only see virtio devices. - */ - ret = css_create_css_image(VIRTUAL_CSSID, true); - assert(ret == 0); - - /* Create VirtIO network adapters */ - s390_create_virtio_net(BUS(css_bus), "virtio-net-ccw"); - - /* Register savevm handler for guest TOD clock */ - register_savevm(NULL, "todclock", 0, 1, - 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; - mc->no_serial = 1; - mc->no_parallel = 1; - 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; -} - -static inline bool machine_get_aes_key_wrap(Object *obj, Error **errp) -{ - S390CcwMachineState *ms = S390_CCW_MACHINE(obj); - - return ms->aes_key_wrap; -} - -static inline void machine_set_aes_key_wrap(Object *obj, bool value, - Error **errp) -{ - S390CcwMachineState *ms = S390_CCW_MACHINE(obj); - - ms->aes_key_wrap = value; -} - -static inline bool machine_get_dea_key_wrap(Object *obj, Error **errp) -{ - S390CcwMachineState *ms = S390_CCW_MACHINE(obj); - - return ms->dea_key_wrap; -} - -static inline void machine_set_dea_key_wrap(Object *obj, bool value, - Error **errp) -{ - S390CcwMachineState *ms = S390_CCW_MACHINE(obj); - - ms->dea_key_wrap = value; -} - -static inline void s390_machine_initfn(Object *obj) -{ - object_property_add_bool(obj, "aes-key-wrap", - machine_get_aes_key_wrap, - machine_set_aes_key_wrap, NULL); - object_property_set_description(obj, "aes-key-wrap", - "enable/disable AES key wrapping using the CPACF wrapping key", - NULL); - object_property_set_bool(obj, true, "aes-key-wrap", NULL); - - object_property_add_bool(obj, "dea-key-wrap", - machine_get_dea_key_wrap, - machine_set_dea_key_wrap, NULL); - object_property_set_description(obj, "dea-key-wrap", - "enable/disable DEA key wrapping using the CPACF wrapping key", - NULL); - object_property_set_bool(obj, true, "dea-key-wrap", NULL); -} - -static const TypeInfo ccw_machine_info = { - .name = TYPE_S390_CCW_MACHINE, - .parent = TYPE_MACHINE, - .abstract = true, - .instance_size = sizeof(S390CcwMachineState), - .instance_init = s390_machine_initfn, - .class_init = ccw_machine_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_NMI }, - { TYPE_HOTPLUG_HANDLER}, - { } - }, -}; - -#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) -{ -} - -static void ccw_machine_2_6_class_options(MachineClass *mc) -{ -} -DEFINE_CCW_MACHINE(2_6, "2.6", true); - -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_init(ccw_machine_register_types) diff --git a/qemu/hw/s390x/s390-virtio-hcall.c b/qemu/hw/s390x/s390-virtio-hcall.c deleted file mode 100644 index 23d67d617..000000000 --- a/qemu/hw/s390x/s390-virtio-hcall.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Support for virtio hypercalls on s390 - * - * Copyright 2012 IBM Corp. - * Author(s): Cornelia Huck - * - * This work is licensed under the terms of the GNU GPL, version 2 or (at - * your option) any later version. See the COPYING file in the top-level - * directory. - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "hw/s390x/s390-virtio.h" - -#define MAX_DIAG_SUBCODES 255 - -static s390_virtio_fn s390_diag500_table[MAX_DIAG_SUBCODES]; - -void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn) -{ - assert(code < MAX_DIAG_SUBCODES); - assert(!s390_diag500_table[code]); - - s390_diag500_table[code] = fn; -} - -int s390_virtio_hypercall(CPUS390XState *env) -{ - s390_virtio_fn fn; - - if (env->regs[1] < MAX_DIAG_SUBCODES) { - fn = s390_diag500_table[env->regs[1]]; - if (fn) { - env->regs[2] = fn(&env->regs[2]); - return 0; - } - } - - return -EINVAL; -} diff --git a/qemu/hw/s390x/s390-virtio.c b/qemu/hw/s390x/s390-virtio.c deleted file mode 100644 index 544c61643..000000000 --- a/qemu/hw/s390x/s390-virtio.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * QEMU S390 virtio target - * - * Copyright (c) 2009 Alexander Graf - * Copyright IBM Corp 2012 - * - * 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. - * - * Contributions after 2012-10-29 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - * - * You should have received a copy of the GNU (Lesser) General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "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" -#include "net/net.h" -#include "hw/boards.h" -#include "hw/loader.h" -#include "hw/virtio/virtio.h" -#include "sysemu/kvm.h" -#include "exec/address-spaces.h" -#include "sysemu/qtest.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 - -#ifdef DEBUG_S390 -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - -#define MAX_BLK_DEVS 10 - -#define S390_TOD_CLOCK_VALUE_MISSING 0x00 -#define S390_TOD_CLOCK_VALUE_PRESENT 0x01 - -static S390CPU **cpu_states; - -S390CPU *s390_cpu_addr2state(uint16_t cpu_addr) -{ - if (cpu_addr >= max_cpus) { - return NULL; - } - - /* Fast lookup via CPU ID */ - return cpu_states[cpu_addr]; -} - -void s390_init_ipl_dev(const char *kernel_filename, - const char *kernel_cmdline, - const char *initrd_filename, - const char *firmware, - bool enforce_bios) -{ - Object *new = object_new(TYPE_S390_IPL); - DeviceState *dev = DEVICE(new); - - if (kernel_filename) { - qdev_prop_set_string(dev, "kernel", kernel_filename); - } - if (initrd_filename) { - qdev_prop_set_string(dev, "initrd", initrd_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(), TYPE_S390_IPL, - new, NULL); - object_unref(new); - qdev_init_nofail(dev); -} - -void s390_init_cpus(MachineState *machine) -{ - int i; - gchar *name; - - if (machine->cpu_model == NULL) { - machine->cpu_model = "host"; - } - - cpu_states = g_new0(S390CPU *, max_cpus); - - 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); - } - - for (i = 0; i < smp_cpus; i++) { - s390x_new_cpu(machine->cpu_model, i, &error_fatal); - } -} - - -void s390_create_virtio_net(BusState *bus, const char *name) -{ - int i; - - for (i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - DeviceState *dev; - - if (!nd->model) { - nd->model = g_strdup("virtio"); - } - - qemu_check_nic_model(nd, "virtio"); - - dev = qdev_create(bus, name); - qdev_set_nic_properties(dev, nd); - qdev_init_nofail(dev); - } -} - -void gtod_save(QEMUFile *f, void *opaque) -{ - uint64_t tod_low; - uint8_t tod_high; - int r; - - r = s390_get_clock(&tod_high, &tod_low); - if (r) { - fprintf(stderr, "WARNING: Unable to get guest clock for migration. " - "Error code %d. Guest clock will not be migrated " - "which could cause the guest to hang.\n", r); - qemu_put_byte(f, S390_TOD_CLOCK_VALUE_MISSING); - return; - } - - qemu_put_byte(f, S390_TOD_CLOCK_VALUE_PRESENT); - qemu_put_byte(f, tod_high); - qemu_put_be64(f, tod_low); -} - -int gtod_load(QEMUFile *f, void *opaque, int version_id) -{ - uint64_t tod_low; - uint8_t tod_high; - int r; - - if (qemu_get_byte(f) == S390_TOD_CLOCK_VALUE_MISSING) { - fprintf(stderr, "WARNING: Guest clock was not migrated. This could " - "cause the guest to hang.\n"); - return 0; - } - - tod_high = qemu_get_byte(f); - tod_low = qemu_get_be64(f); - - r = s390_set_clock(&tod_high, &tod_low); - if (r) { - fprintf(stderr, "WARNING: Unable to set guest clock value. " - "s390_get_clock returned error %d. This could cause " - "the guest to hang.\n", r); - } - - return 0; -} - -void s390_nmi(NMIState *n, int cpu_index, Error **errp) -{ - CPUState *cs = qemu_get_cpu(cpu_index); - - if (s390_cpu_restart(S390_CPU(cs))) { - error_setg(errp, QERR_UNSUPPORTED); - } -} - -void s390_machine_reset(void) -{ - S390CPU *ipl_cpu = S390_CPU(qemu_get_cpu(0)); - - qemu_devices_reset(); - s390_cmma_reset(); - s390_crypto_reset(); - - /* 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 deleted file mode 100644 index ffd014cb5..000000000 --- a/qemu/hw/s390x/s390-virtio.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Virtio interfaces for s390 - * - * Copyright 2012 IBM Corp. - * Author(s): Cornelia Huck - * - * 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. - */ - -#ifndef HW_S390_VIRTIO_H -#define HW_S390_VIRTIO_H 1 - -#include "hw/nmi.h" -#include "standard-headers/asm-s390/kvm_virtio.h" -#include "standard-headers/asm-s390/virtio-ccw.h" - -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(MachineState *machine); -void s390_init_ipl_dev(const char *kernel_filename, - const char *kernel_cmdline, - const char *initrd_filename, - const char *firmware, - 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 deleted file mode 100644 index 85dbe1b60..000000000 --- a/qemu/hw/s390x/sclp.c +++ /dev/null @@ -1,618 +0,0 @@ -/* - * SCLP Support - * - * Copyright IBM, Corp. 2012 - * - * Authors: - * Christian Borntraeger - * Heinz Graalfs - * - * 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 "qapi/error.h" -#include "cpu.h" -#include "sysemu/kvm.h" -#include "exec/memory.h" -#include "sysemu/sysemu.h" -#include "exec/address-spaces.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 SCLPDevice *get_sclp_device(void) -{ - return SCLP(object_resolve_path_type("", TYPE_SCLP, NULL)); -} - -/* Provide information about the configuration, CPUs and storage */ -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 rnsize, rnmax; - int slots = MIN(machine->ram_slots, s390_get_memslot_count(kvm_state)); - - CPU_FOREACH(cpu) { - cpu_count++; - } - - /* CPU information */ - read_info->entries_cpu = cpu_to_be16(cpu_count); - read_info->offset_cpu = cpu_to_be16(offsetof(ReadInfo, entries)); - read_info->highest_cpu = cpu_to_be16(max_cpus); - - for (i = 0; i < cpu_count; i++) { - read_info->entries[i].address = i; - read_info->entries[i].type = 0; - } - - read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO | - SCLP_HAS_PCI_RECONFIG); - - /* Memory Hotplug is only supported for the ccw machine type */ - if (mhd) { - mhd->standby_subregion_size = MEM_SECTION_SIZE; - /* Deduct the memory slot already used for core */ - if (slots > 0) { - while ((mhd->standby_subregion_size * (slots - 1) - < mhd->standby_mem_size)) { - mhd->standby_subregion_size = mhd->standby_subregion_size << 1; - } - } - /* - * Initialize mapping of guest standby memory sections indicating which - * are and are not online. Assume all standby memory begins offline. - */ - if (mhd->standby_state_map == 0) { - if (mhd->standby_mem_size % mhd->standby_subregion_size) { - mhd->standby_state_map = g_malloc0((mhd->standby_mem_size / - mhd->standby_subregion_size + 1) * - (mhd->standby_subregion_size / - MEM_SECTION_SIZE)); - } else { - mhd->standby_state_map = g_malloc0(mhd->standby_mem_size / - MEM_SECTION_SIZE); - } - } - mhd->padded_ram_size = ram_size + mhd->pad_size; - mhd->rzm = 1 << mhd->increment_size; - - read_info->facilities |= cpu_to_be64(SCLP_FC_ASSIGN_ATTACH_READ_STOR); - } - - rnsize = 1 << (sclp->increment_size - 20); - if (rnsize <= 128) { - read_info->rnsize = rnsize; - } else { - read_info->rnsize = 0; - 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 { - read_info->rnmax = cpu_to_be16(0); - read_info->rnmax2 = cpu_to_be64(rnmax); - } - - sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); -} - -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(); - - 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); - return; - } - - /* Return information regarding core memory */ - storage_info->max_id = cpu_to_be16(mhd->standby_mem_size ? 1 : 0); - assigned = ram_size >> mhd->increment_size; - storage_info->assigned = cpu_to_be16(assigned); - - for (i = 0; i < assigned; i++) { - storage_info->entries[i] = cpu_to_be32(subincrement_id); - subincrement_id += SCLP_INCREMENT_UNIT; - } - sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); -} - -static void read_storage_element1_info(SCLPDevice *sclp, SCCB *sccb) -{ - ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb; - sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - - 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); - return; - } - - /* Return information regarding standby memory */ - storage_info->max_id = cpu_to_be16(mhd->standby_mem_size ? 1 : 0); - storage_info->assigned = cpu_to_be16(mhd->standby_mem_size >> - mhd->increment_size); - storage_info->standby = cpu_to_be16(mhd->standby_mem_size >> - mhd->increment_size); - sccb->h.response_code = cpu_to_be16(SCLP_RC_STANDBY_READ_COMPLETION); -} - -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(); - - 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); - return; - } - - assigned = mhd->standby_mem_size >> mhd->increment_size; - attach_info->assigned = cpu_to_be16(assigned); - subincrement_id = ((ram_size >> mhd->increment_size) << 16) - + SCLP_STARTING_SUBINCREMENT_ID; - for (i = 0; i < assigned; i++) { - attach_info->entries[i] = cpu_to_be32(subincrement_id); - subincrement_id += SCLP_INCREMENT_UNIT; - } - sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); -} - -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(); - 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); - - /* offset to align to standby_subregion_size for allocation */ - ram_addr_t offset = assign_addr - - (assign_addr - mhd->padded_ram_size) - % mhd->standby_subregion_size; - - /* strlen("standby.ram") + 4 (Max of KVM_MEMORY_SLOTS) + NULL */ - char id[16]; - snprintf(id, 16, "standby.ram%d", - (int)((offset - mhd->padded_ram_size) / - mhd->standby_subregion_size) + 1); - - /* Allocate a subregion of the calculated standby_subregion_size */ - if (offset + mhd->standby_subregion_size > - mhd->padded_ram_size + mhd->standby_mem_size) { - this_subregion_size = mhd->padded_ram_size + - mhd->standby_mem_size - offset; - } else { - this_subregion_size = mhd->standby_subregion_size; - } - - 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); - } - /* The specified subregion is no longer in standby */ - mhd->standby_state_map[(assign_addr - mhd->padded_ram_size) - / MEM_SECTION_SIZE] = 1; - } - sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); -} - -static void unassign_storage(SCLPDevice *sclp, SCCB *sccb) -{ - MemoryRegion *mr = NULL; - AssignStorage *assign_info = (AssignStorage *) sccb; - sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); - 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)) { - mhd->standby_state_map[(unassign_addr - - mhd->padded_ram_size) / MEM_SECTION_SIZE] = 0; - - /* 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; - ram_addr_t map_offset = (unassign_addr - mhd->padded_ram_size - - (unassign_addr - mhd->padded_ram_size) - % mhd->standby_subregion_size); - /* Mark all affected subregions as 'standby' once again */ - for (i = 0; - i < (mhd->standby_subregion_size / MEM_SECTION_SIZE); - i++) { - - if (mhd->standby_state_map[i + map_offset / MEM_SECTION_SIZE]) { - is_removable = 0; - break; - } - } - if (is_removable) { - memory_region_del_subregion(sysmem, mr); - object_unref(OBJECT(mr)); - } - } - } - sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); -} - -/* Provide information about the CPU */ -static void sclp_read_cpu_info(SCLPDevice *sclp, SCCB *sccb) -{ - ReadCpuInfo *cpu_info = (ReadCpuInfo *) sccb; - CPUState *cpu; - int cpu_count = 0; - int i = 0; - - CPU_FOREACH(cpu) { - cpu_count++; - } - - cpu_info->nr_configured = cpu_to_be16(cpu_count); - cpu_info->offset_configured = cpu_to_be16(offsetof(ReadCpuInfo, entries)); - cpu_info->nr_standby = cpu_to_be16(0); - - /* The standby offset is 16-byte for each CPU */ - cpu_info->offset_standby = cpu_to_be16(cpu_info->offset_configured - + cpu_info->nr_configured*sizeof(CPUEntry)); - - for (i = 0; i < cpu_count; i++) { - cpu_info->entries[i].address = i; - cpu_info->entries[i].type = 0; - } - - sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); -} - -static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code) -{ - 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: - sclp_c->read_SCP_info(sclp, sccb); - break; - case SCLP_CMDW_READ_CPU_INFO: - sclp_c->read_cpu_info(sclp, sccb); - break; - case SCLP_READ_STORAGE_ELEMENT_INFO: - if (code & 0xff00) { - sclp_c->read_storage_element1_info(sclp, sccb); - } else { - sclp_c->read_storage_element0_info(sclp, sccb); - } - break; - case SCLP_ATTACH_STORAGE_ELEMENT: - sclp_c->attach_storage_element(sclp, sccb, (code & 0xff00) >> 8); - break; - case SCLP_ASSIGN_STORAGE: - sclp_c->assign_storage(sclp, sccb); - break; - case SCLP_UNASSIGN_STORAGE: - sclp_c->unassign_storage(sclp, sccb); - break; - case SCLP_CMDW_CONFIGURE_PCI: - s390_pci_sclp_configure(1, sccb); - break; - case SCLP_CMDW_DECONFIGURE_PCI: - s390_pci_sclp_configure(0, sccb); - break; - default: - efc->command_handler(ef, sccb, code); - break; - } -} - -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; - - hwaddr sccb_len = sizeof(SCCB); - - /* first some basic checks on program checks */ - if (env->psw.mask & PSW_MASK_PSTATE) { - r = -PGM_PRIVILEGED; - goto out; - } - if (cpu_physical_memory_is_io(sccb)) { - r = -PGM_ADDRESSING; - goto out; - } - if ((sccb & ~0x1fffUL) == 0 || (sccb & ~0x1fffUL) == env->psa - || (sccb & ~0x7ffffff8UL) != 0) { - r = -PGM_SPECIFICATION; - goto out; - } - - /* - * we want to work on a private copy of the sccb, to prevent guests - * from playing dirty tricks by modifying the memory content after - * the host has checked the values - */ - cpu_physical_memory_read(sccb, &work_sccb, sccb_len); - - /* Valid sccb sizes */ - if (be16_to_cpu(work_sccb.h.length) < sizeof(SCCBHeader) || - be16_to_cpu(work_sccb.h.length) > SCCB_SIZE) { - r = -PGM_SPECIFICATION; - goto out; - } - - sclp_c->execute(sclp, (SCCB *)&work_sccb, code); - - cpu_physical_memory_write(sccb, &work_sccb, - be16_to_cpu(work_sccb.h.length)); - - sclp_c->service_interrupt(sclp, sccb); - -out: - return r; -} - -static void service_interrupt(SCLPDevice *sclp, uint32_t sccb) -{ - SCLPEventFacility *ef = sclp->event_facility; - SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef); - - uint32_t param = sccb & ~3; - - /* Indicate whether an event is still pending */ - param |= efc->event_pending(ef) ? 1 : 0; - - if (!param) { - /* No need to send an interrupt, there's nothing to be notified about */ - return; - } - 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) -{ - Object *new = object_new(TYPE_SCLP); - - 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; - dev = qdev_create(NULL, TYPE_SCLP_MEMORY_HOTPLUG_DEV); - object_property_add_child(qdev_get_machine(), - TYPE_SCLP_MEMORY_HOTPLUG_DEV, - OBJECT(dev), NULL); - qdev_init_nofail(dev); - return SCLP_MEMORY_HOTPLUG_DEV(object_resolve_path( - TYPE_SCLP_MEMORY_HOTPLUG_DEV, NULL)); -} - -sclpMemoryHotplugDev *get_sclp_memory_hotplug_dev(void) -{ - return SCLP_MEMORY_HOTPLUG_DEV(object_resolve_path( - TYPE_SCLP_MEMORY_HOTPLUG_DEV, NULL)); -} - -static void sclp_memory_hotplug_dev_class_init(ObjectClass *klass, - void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static TypeInfo sclp_memory_hotplug_dev_info = { - .name = TYPE_SCLP_MEMORY_HOTPLUG_DEV, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(sclpMemoryHotplugDev), - .class_init = sclp_memory_hotplug_dev_class_init, -}; - -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 deleted file mode 100644 index b1f3ef8c7..000000000 --- a/qemu/hw/s390x/sclpcpu.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * SCLP event type - * Signal CPU - Trigger SCLP interrupt for system CPU configure or - * de-configure - * - * Copyright IBM, Corp. 2013 - * - * Authors: - * Thang Pham - * - * 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 "sysemu/sysemu.h" -#include "hw/s390x/sclp.h" -#include "hw/s390x/event-facility.h" -#include "cpu.h" -#include "sysemu/cpus.h" -#include "sysemu/kvm.h" - -typedef struct ConfigMgtData { - EventBufferHeader ebh; - uint8_t reserved; - uint8_t event_qualifier; -} QEMU_PACKED ConfigMgtData; - -#define EVENT_QUAL_CPU_CHANGE 1 - -void raise_irq_cpu_hotplug(void) -{ - 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) -{ - return SCLP_EVENT_MASK_CONFIG_MGT_DATA; -} - -static unsigned int receive_mask(void) -{ - return 0; -} - -static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, - int *slen) -{ - ConfigMgtData *cdata = (ConfigMgtData *) evt_buf_hdr; - if (*slen < sizeof(ConfigMgtData)) { - return 0; - } - - /* Event is no longer pending */ - if (!event->event_pending) { - return 0; - } - event->event_pending = false; - - /* Event header data */ - cdata->ebh.length = cpu_to_be16(sizeof(ConfigMgtData)); - cdata->ebh.type = SCLP_EVENT_CONFIG_MGT_DATA; - cdata->ebh.flags |= SCLP_EVENT_BUFFER_ACCEPTED; - - /* Trigger a rescan of CPUs by setting event qualifier */ - cdata->event_qualifier = EVENT_QUAL_CPU_CHANGE; - *slen -= sizeof(ConfigMgtData); - - return 1; -} - -static void cpu_class_init(ObjectClass *oc, void *data) -{ - SCLPEventClass *k = SCLP_EVENT_CLASS(oc); - DeviceClass *dc = DEVICE_CLASS(oc); - - k->get_send_mask = send_mask; - k->get_receive_mask = receive_mask; - k->read_event_data = read_event_data; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo sclp_cpu_info = { - .name = TYPE_SCLP_CPU_HOTPLUG, - .parent = TYPE_SCLP_EVENT, - .instance_size = sizeof(SCLPEvent), - .class_init = cpu_class_init, - .class_size = sizeof(SCLPEventClass), -}; - -static void sclp_cpu_register_types(void) -{ - type_register_static(&sclp_cpu_info); -} - -type_init(sclp_cpu_register_types) diff --git a/qemu/hw/s390x/sclpquiesce.c b/qemu/hw/s390x/sclpquiesce.c deleted file mode 100644 index c0ecab9c3..000000000 --- a/qemu/hw/s390x/sclpquiesce.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * SCLP event type - * Signal Quiesce - trigger system powerdown request - * - * Copyright IBM, Corp. 2012 - * - * Authors: - * Heinz Graalfs - * - * 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 -#include "sysemu/sysemu.h" -#include "hw/s390x/sclp.h" -#include "hw/s390x/event-facility.h" - -typedef struct SignalQuiesce { - EventBufferHeader ebh; - uint16_t timeout; - uint8_t unit; -} QEMU_PACKED SignalQuiesce; - -static bool can_handle_event(uint8_t type) -{ - return type == SCLP_EVENT_SIGNAL_QUIESCE; -} - -static unsigned int send_mask(void) -{ - return SCLP_EVENT_MASK_SIGNAL_QUIESCE; -} - -static unsigned int receive_mask(void) -{ - return 0; -} - -static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, - int *slen) -{ - SignalQuiesce *sq = (SignalQuiesce *) evt_buf_hdr; - - if (*slen < sizeof(SignalQuiesce)) { - return 0; - } - - if (!event->event_pending) { - return 0; - } - event->event_pending = false; - - sq->ebh.length = cpu_to_be16(sizeof(SignalQuiesce)); - sq->ebh.type = SCLP_EVENT_SIGNAL_QUIESCE; - sq->ebh.flags |= SCLP_EVENT_BUFFER_ACCEPTED; - /* - * system_powerdown does not have a timeout. Fortunately the - * timeout value is currently ignored by Linux, anyway - */ - sq->timeout = cpu_to_be16(0); - sq->unit = cpu_to_be16(0); - *slen -= sizeof(SignalQuiesce); - - return 1; -} - -static const VMStateDescription vmstate_sclpquiesce = { - .name = TYPE_SCLP_QUIESCE, - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_BOOL(event_pending, SCLPEvent), - VMSTATE_END_OF_LIST() - } -}; - -typedef struct QuiesceNotifier QuiesceNotifier; - -static struct QuiesceNotifier { - Notifier notifier; - SCLPEvent *event; -} qn; - -static void quiesce_powerdown_req(Notifier *n, void *opaque) -{ - QuiesceNotifier *qn = container_of(n, QuiesceNotifier, notifier); - SCLPEvent *event = qn->event; - - event->event_pending = true; - /* trigger SCLP read operation */ - sclp_service_interrupt(0); -} - -static int quiesce_init(SCLPEvent *event) -{ - qn.notifier.notify = quiesce_powerdown_req; - qn.event = event; - - qemu_register_powerdown_notifier(&qn.notifier); - - return 0; -} - -static void quiesce_reset(DeviceState *dev) -{ - SCLPEvent *event = SCLP_EVENT(dev); - - event->event_pending = false; -} - -static void quiesce_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SCLPEventClass *k = SCLP_EVENT_CLASS(klass); - - dc->reset = quiesce_reset; - dc->vmsd = &vmstate_sclpquiesce; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - k->init = quiesce_init; - - k->get_send_mask = send_mask; - k->get_receive_mask = receive_mask; - k->can_handle_event = can_handle_event; - k->read_event_data = read_event_data; - k->write_event_data = NULL; -} - -static const TypeInfo sclp_quiesce_info = { - .name = TYPE_SCLP_QUIESCE, - .parent = TYPE_SCLP_EVENT, - .instance_size = sizeof(SCLPEvent), - .class_init = quiesce_class_init, - .class_size = sizeof(SCLPEventClass), -}; - -static void register_types(void) -{ - type_register_static(&sclp_quiesce_info); -} - -type_init(register_types) diff --git a/qemu/hw/s390x/virtio-ccw.c b/qemu/hw/s390x/virtio-ccw.c deleted file mode 100644 index d51642db0..000000000 --- a/qemu/hw/s390x/virtio-ccw.c +++ /dev/null @@ -1,1928 +0,0 @@ -/* - * virtio ccw target implementation - * - * Copyright 2012,2015 IBM Corp. - * Author(s): Cornelia Huck - * Pierre Morel - * - * 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 "qapi/error.h" -#include "hw/hw.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" -#include "net/net.h" -#include "hw/virtio/virtio.h" -#include "hw/virtio/virtio-serial.h" -#include "hw/virtio/virtio-net.h" -#include "hw/sysbus.h" -#include "qemu/bitops.h" -#include "qemu/error-report.h" -#include "hw/virtio/virtio-access.h" -#include "hw/virtio/virtio-bus.h" -#include "hw/s390x/adapter.h" -#include "hw/s390x/s390_flic.h" - -#include "ioinst.h" -#include "css.h" -#include "virtio-ccw.h" -#include "trace.h" - -static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, - VirtioCcwDevice *dev); - -static void virtual_css_bus_reset(BusState *qbus) -{ - /* This should actually be modelled via the generic css */ - css_reset(); -} - - -static void virtual_css_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - - k->reset = virtual_css_bus_reset; -} - -static const TypeInfo virtual_css_bus_info = { - .name = TYPE_VIRTUAL_CSS_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(VirtualCssBus), - .class_init = virtual_css_bus_class_init, -}; - -VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch) -{ - VirtIODevice *vdev = NULL; - VirtioCcwDevice *dev = sch->driver_data; - - if (dev) { - vdev = virtio_bus_get_device(&dev->bus); - } - return vdev; -} - -static int virtio_ccw_set_guest2host_notifier(VirtioCcwDevice *dev, int n, - bool assign, bool set_handler) -{ - VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - VirtQueue *vq = virtio_get_queue(vdev, n); - EventNotifier *notifier = virtio_queue_get_host_notifier(vq); - int r = 0; - SubchDev *sch = dev->sch; - uint32_t sch_id = (css_build_subchannel_id(sch) << 16) | sch->schid; - - if (assign) { - r = event_notifier_init(notifier, 1); - if (r < 0) { - error_report("%s: unable to init event notifier: %d", __func__, r); - return r; - } - virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler); - r = s390_assign_subch_ioeventfd(notifier, sch_id, n, assign); - if (r < 0) { - error_report("%s: unable to assign ioeventfd: %d", __func__, r); - virtio_queue_set_host_notifier_fd_handler(vq, false, false); - event_notifier_cleanup(notifier); - return r; - } - } else { - virtio_queue_set_host_notifier_fd_handler(vq, false, false); - s390_assign_subch_ioeventfd(notifier, sch_id, n, assign); - event_notifier_cleanup(notifier); - } - return r; -} - -static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev) -{ - VirtIODevice *vdev; - int n, r; - - if (!(dev->flags & VIRTIO_CCW_FLAG_USE_IOEVENTFD) || - dev->ioeventfd_disabled || - dev->ioeventfd_started) { - return; - } - vdev = virtio_bus_get_device(&dev->bus); - for (n = 0; n < VIRTIO_CCW_QUEUE_MAX; n++) { - if (!virtio_queue_get_num(vdev, n)) { - continue; - } - r = virtio_ccw_set_guest2host_notifier(dev, n, true, true); - if (r < 0) { - goto assign_error; - } - } - dev->ioeventfd_started = true; - return; - - assign_error: - while (--n >= 0) { - if (!virtio_queue_get_num(vdev, n)) { - continue; - } - r = virtio_ccw_set_guest2host_notifier(dev, n, false, false); - assert(r >= 0); - } - dev->ioeventfd_started = false; - /* Disable ioeventfd for this device. */ - dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD; - error_report("%s: failed. Fallback to userspace (slower).", __func__); -} - -static void virtio_ccw_stop_ioeventfd(VirtioCcwDevice *dev) -{ - VirtIODevice *vdev; - int n, r; - - if (!dev->ioeventfd_started) { - return; - } - vdev = virtio_bus_get_device(&dev->bus); - for (n = 0; n < VIRTIO_CCW_QUEUE_MAX; n++) { - if (!virtio_queue_get_num(vdev, n)) { - continue; - } - r = virtio_ccw_set_guest2host_notifier(dev, n, false, false); - assert(r >= 0); - } - dev->ioeventfd_started = false; -} - -VirtualCssBus *virtual_css_bus_init(void) -{ - VirtualCssBus *cbus; - BusState *bus; - DeviceState *dev; - - /* Create bridge device */ - dev = qdev_create(NULL, "virtual-css-bridge"); - qdev_init_nofail(dev); - - /* Create bus on bridge device */ - bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css"); - cbus = VIRTUAL_CSS_BUS(bus); - - /* Enable hotplugging */ - qbus_set_hotplug_handler(bus, dev, &error_abort); - - return cbus; -} - -/* Communication blocks used by several channel commands. */ -typedef struct VqInfoBlockLegacy { - uint64_t queue; - uint32_t align; - uint16_t index; - uint16_t num; -} QEMU_PACKED VqInfoBlockLegacy; - -typedef struct VqInfoBlock { - uint64_t desc; - uint32_t res0; - uint16_t index; - uint16_t num; - uint64_t avail; - uint64_t used; -} QEMU_PACKED VqInfoBlock; - -typedef struct VqConfigBlock { - uint16_t index; - uint16_t num_max; -} QEMU_PACKED VqConfigBlock; - -typedef struct VirtioFeatDesc { - uint32_t features; - uint8_t index; -} QEMU_PACKED VirtioFeatDesc; - -typedef struct VirtioThinintInfo { - hwaddr summary_indicator; - hwaddr device_indicator; - uint64_t ind_bit; - uint8_t isc; -} QEMU_PACKED VirtioThinintInfo; - -typedef struct VirtioRevInfo { - uint16_t revision; - uint16_t length; - uint8_t data[0]; -} QEMU_PACKED VirtioRevInfo; - -/* Specify where the virtqueues for the subchannel are in guest memory. */ -static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info, - VqInfoBlockLegacy *linfo) -{ - VirtIODevice *vdev = virtio_ccw_get_vdev(sch); - uint16_t index = info ? info->index : linfo->index; - uint16_t num = info ? info->num : linfo->num; - uint64_t desc = info ? info->desc : linfo->queue; - - if (index >= VIRTIO_CCW_QUEUE_MAX) { - return -EINVAL; - } - - /* Current code in virtio.c relies on 4K alignment. */ - if (linfo && desc && (linfo->align != 4096)) { - return -EINVAL; - } - - if (!vdev) { - return -EINVAL; - } - - if (info) { - virtio_queue_set_rings(vdev, index, desc, info->avail, info->used); - } else { - virtio_queue_set_addr(vdev, index, desc); - } - if (!desc) { - virtio_queue_set_vector(vdev, index, VIRTIO_NO_VECTOR); - } else { - 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 */ - vdev->config_vector = VIRTIO_CCW_QUEUE_MAX; - return 0; -} - -static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev) -{ - virtio_ccw_stop_ioeventfd(dev); - virtio_reset(vdev); - if (dev->indicators) { - release_indicator(&dev->routes.adapter, dev->indicators); - dev->indicators = NULL; - } - if (dev->indicators2) { - release_indicator(&dev->routes.adapter, dev->indicators2); - dev->indicators2 = NULL; - } - if (dev->summary_indicator) { - release_indicator(&dev->routes.adapter, dev->summary_indicator); - dev->summary_indicator = NULL; - } - dev->sch->thinint_active = false; -} - -static int virtio_ccw_handle_set_vq(SubchDev *sch, CCW1 ccw, bool check_len, - bool is_legacy) -{ - int ret; - VqInfoBlock info; - VqInfoBlockLegacy linfo; - size_t info_len = is_legacy ? sizeof(linfo) : sizeof(info); - - if (check_len) { - if (ccw.count != info_len) { - return -EINVAL; - } - } else if (ccw.count < info_len) { - /* Can't execute command. */ - return -EINVAL; - } - if (!ccw.cda) { - return -EFAULT; - } - if (is_legacy) { - linfo.queue = address_space_ldq_be(&address_space_memory, ccw.cda, - MEMTXATTRS_UNSPECIFIED, NULL); - linfo.align = address_space_ldl_be(&address_space_memory, - ccw.cda + sizeof(linfo.queue), - MEMTXATTRS_UNSPECIFIED, - NULL); - linfo.index = address_space_lduw_be(&address_space_memory, - ccw.cda + sizeof(linfo.queue) - + sizeof(linfo.align), - MEMTXATTRS_UNSPECIFIED, - NULL); - linfo.num = address_space_lduw_be(&address_space_memory, - ccw.cda + sizeof(linfo.queue) - + sizeof(linfo.align) - + sizeof(linfo.index), - MEMTXATTRS_UNSPECIFIED, - NULL); - ret = virtio_ccw_set_vqs(sch, NULL, &linfo); - } else { - info.desc = address_space_ldq_be(&address_space_memory, ccw.cda, - MEMTXATTRS_UNSPECIFIED, NULL); - info.index = address_space_lduw_be(&address_space_memory, - ccw.cda + sizeof(info.desc) - + sizeof(info.res0), - MEMTXATTRS_UNSPECIFIED, NULL); - info.num = address_space_lduw_be(&address_space_memory, - ccw.cda + sizeof(info.desc) - + sizeof(info.res0) - + sizeof(info.index), - MEMTXATTRS_UNSPECIFIED, NULL); - info.avail = address_space_ldq_be(&address_space_memory, - ccw.cda + sizeof(info.desc) - + sizeof(info.res0) - + sizeof(info.index) - + sizeof(info.num), - MEMTXATTRS_UNSPECIFIED, NULL); - info.used = address_space_ldq_be(&address_space_memory, - ccw.cda + sizeof(info.desc) - + sizeof(info.res0) - + sizeof(info.index) - + sizeof(info.num) - + sizeof(info.avail), - MEMTXATTRS_UNSPECIFIED, NULL); - ret = virtio_ccw_set_vqs(sch, &info, NULL); - } - sch->curr_status.scsw.count = 0; - return ret; -} - -static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) -{ - int ret; - VirtioRevInfo revinfo; - uint8_t status; - VirtioFeatDesc features; - void *config; - hwaddr indicators; - VqConfigBlock vq_config; - VirtioCcwDevice *dev = sch->driver_data; - VirtIODevice *vdev = virtio_ccw_get_vdev(sch); - bool check_len; - int len; - hwaddr hw_len; - VirtioThinintInfo *thinint; - - if (!dev) { - return -EINVAL; - } - - trace_virtio_ccw_interpret_ccw(sch->cssid, sch->ssid, sch->schid, - ccw.cmd_code); - check_len = !((ccw.flags & CCW_FLAG_SLI) && !(ccw.flags & CCW_FLAG_DC)); - - /* Look at the command. */ - switch (ccw.cmd_code) { - case CCW_CMD_SET_VQ: - ret = virtio_ccw_handle_set_vq(sch, ccw, check_len, dev->revision < 1); - break; - case CCW_CMD_VDEV_RESET: - virtio_ccw_reset_virtio(dev, vdev); - ret = 0; - break; - case CCW_CMD_READ_FEAT: - if (check_len) { - if (ccw.count != sizeof(features)) { - ret = -EINVAL; - break; - } - } else if (ccw.count < sizeof(features)) { - /* Can't execute command. */ - ret = -EINVAL; - break; - } - if (!ccw.cda) { - ret = -EFAULT; - } else { - features.index = address_space_ldub(&address_space_memory, - ccw.cda - + sizeof(features.features), - MEMTXATTRS_UNSPECIFIED, - NULL); - if (features.index == 0) { - 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)) { - /* - * Only offer feature bits beyond 31 if the guest has - * negotiated at least revision 1. - */ - features.features = (uint32_t)(vdev->host_features >> 32); - } else { - /* Return zeroes if the guest supports more feature bits. */ - features.features = 0; - } - address_space_stl_le(&address_space_memory, ccw.cda, - features.features, MEMTXATTRS_UNSPECIFIED, - NULL); - sch->curr_status.scsw.count = ccw.count - sizeof(features); - ret = 0; - } - break; - case CCW_CMD_WRITE_FEAT: - if (check_len) { - if (ccw.count != sizeof(features)) { - ret = -EINVAL; - break; - } - } else if (ccw.count < sizeof(features)) { - /* Can't execute command. */ - ret = -EINVAL; - break; - } - if (!ccw.cda) { - ret = -EFAULT; - } else { - features.index = address_space_ldub(&address_space_memory, - ccw.cda - + sizeof(features.features), - MEMTXATTRS_UNSPECIFIED, - NULL); - features.features = address_space_ldl_le(&address_space_memory, - ccw.cda, - MEMTXATTRS_UNSPECIFIED, - NULL); - if (features.index == 0) { - virtio_set_features(vdev, - (vdev->guest_features & 0xffffffff00000000ULL) | - features.features); - } else if ((features.index == 1) && (dev->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. - */ - virtio_set_features(vdev, - (vdev->guest_features & 0x00000000ffffffffULL) | - ((uint64_t)features.features << 32)); - } else { - /* - * If the guest supports more feature bits, assert that it - * passes us zeroes for those we don't support. - */ - if (features.features) { - fprintf(stderr, "Guest bug: features[%i]=%x (expected 0)\n", - features.index, features.features); - /* XXX: do a unit check here? */ - } - } - sch->curr_status.scsw.count = ccw.count - sizeof(features); - ret = 0; - } - break; - case CCW_CMD_READ_CONF: - if (check_len) { - if (ccw.count > vdev->config_len) { - ret = -EINVAL; - break; - } - } - len = MIN(ccw.count, vdev->config_len); - if (!ccw.cda) { - ret = -EFAULT; - } else { - virtio_bus_get_vdev_config(&dev->bus, vdev->config); - /* XXX config space endianness */ - cpu_physical_memory_write(ccw.cda, vdev->config, len); - sch->curr_status.scsw.count = ccw.count - len; - ret = 0; - } - break; - case CCW_CMD_WRITE_CONF: - if (check_len) { - if (ccw.count > vdev->config_len) { - ret = -EINVAL; - break; - } - } - len = MIN(ccw.count, vdev->config_len); - hw_len = len; - if (!ccw.cda) { - ret = -EFAULT; - } else { - config = cpu_physical_memory_map(ccw.cda, &hw_len, 0); - if (!config) { - ret = -EFAULT; - } else { - len = hw_len; - /* XXX config space endianness */ - memcpy(vdev->config, config, len); - cpu_physical_memory_unmap(config, hw_len, 0, hw_len); - virtio_bus_set_vdev_config(&dev->bus, vdev->config); - sch->curr_status.scsw.count = ccw.count - len; - ret = 0; - } - } - break; - case CCW_CMD_WRITE_STATUS: - if (check_len) { - if (ccw.count != sizeof(status)) { - ret = -EINVAL; - break; - } - } else if (ccw.count < sizeof(status)) { - /* Can't execute command. */ - ret = -EINVAL; - break; - } - if (!ccw.cda) { - ret = -EFAULT; - } else { - status = address_space_ldub(&address_space_memory, ccw.cda, - MEMTXATTRS_UNSPECIFIED, NULL); - if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) { - virtio_ccw_stop_ioeventfd(dev); - } - if (virtio_set_status(vdev, status) == 0) { - if (vdev->status == 0) { - virtio_ccw_reset_virtio(dev, vdev); - } - if (status & VIRTIO_CONFIG_S_DRIVER_OK) { - virtio_ccw_start_ioeventfd(dev); - } - sch->curr_status.scsw.count = ccw.count - sizeof(status); - ret = 0; - } else { - /* Trigger a command reject. */ - ret = -ENOSYS; - } - } - break; - case CCW_CMD_SET_IND: - if (check_len) { - if (ccw.count != sizeof(indicators)) { - ret = -EINVAL; - break; - } - } else if (ccw.count < sizeof(indicators)) { - /* Can't execute command. */ - ret = -EINVAL; - break; - } - if (sch->thinint_active) { - /* Trigger a command reject. */ - ret = -ENOSYS; - break; - } - if (!ccw.cda) { - ret = -EFAULT; - } else { - indicators = address_space_ldq_be(&address_space_memory, ccw.cda, - MEMTXATTRS_UNSPECIFIED, NULL); - dev->indicators = get_indicator(indicators, sizeof(uint64_t)); - sch->curr_status.scsw.count = ccw.count - sizeof(indicators); - ret = 0; - } - break; - case CCW_CMD_SET_CONF_IND: - if (check_len) { - if (ccw.count != sizeof(indicators)) { - ret = -EINVAL; - break; - } - } else if (ccw.count < sizeof(indicators)) { - /* Can't execute command. */ - ret = -EINVAL; - break; - } - if (!ccw.cda) { - ret = -EFAULT; - } else { - indicators = address_space_ldq_be(&address_space_memory, ccw.cda, - MEMTXATTRS_UNSPECIFIED, NULL); - dev->indicators2 = get_indicator(indicators, sizeof(uint64_t)); - sch->curr_status.scsw.count = ccw.count - sizeof(indicators); - ret = 0; - } - break; - case CCW_CMD_READ_VQ_CONF: - if (check_len) { - if (ccw.count != sizeof(vq_config)) { - ret = -EINVAL; - break; - } - } else if (ccw.count < sizeof(vq_config)) { - /* Can't execute command. */ - ret = -EINVAL; - break; - } - if (!ccw.cda) { - ret = -EFAULT; - } else { - vq_config.index = address_space_lduw_be(&address_space_memory, - ccw.cda, - MEMTXATTRS_UNSPECIFIED, - NULL); - if (vq_config.index >= VIRTIO_CCW_QUEUE_MAX) { - ret = -EINVAL; - break; - } - vq_config.num_max = virtio_queue_get_num(vdev, - vq_config.index); - address_space_stw_be(&address_space_memory, - ccw.cda + sizeof(vq_config.index), - vq_config.num_max, - MEMTXATTRS_UNSPECIFIED, - NULL); - sch->curr_status.scsw.count = ccw.count - sizeof(vq_config); - ret = 0; - } - break; - case CCW_CMD_SET_IND_ADAPTER: - if (check_len) { - if (ccw.count != sizeof(*thinint)) { - ret = -EINVAL; - break; - } - } else if (ccw.count < sizeof(*thinint)) { - /* Can't execute command. */ - ret = -EINVAL; - break; - } - len = sizeof(*thinint); - hw_len = len; - if (!ccw.cda) { - ret = -EFAULT; - } else if (dev->indicators && !sch->thinint_active) { - /* Trigger a command reject. */ - ret = -ENOSYS; - } else { - thinint = cpu_physical_memory_map(ccw.cda, &hw_len, 0); - if (!thinint) { - ret = -EFAULT; - } else { - uint64_t ind_bit = ldq_be_p(&thinint->ind_bit); - - len = hw_len; - dev->summary_indicator = - get_indicator(ldq_be_p(&thinint->summary_indicator), - sizeof(uint8_t)); - dev->indicators = - get_indicator(ldq_be_p(&thinint->device_indicator), - ind_bit / 8 + 1); - dev->thinint_isc = thinint->isc; - dev->routes.adapter.ind_offset = ind_bit; - dev->routes.adapter.summary_offset = 7; - cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len); - ret = css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO, - dev->thinint_isc, true, false, - &dev->routes.adapter.adapter_id); - assert(ret == 0); - sch->thinint_active = ((dev->indicators != NULL) && - (dev->summary_indicator != NULL)); - sch->curr_status.scsw.count = ccw.count - len; - ret = 0; - } - } - break; - case CCW_CMD_SET_VIRTIO_REV: - len = sizeof(revinfo); - if (ccw.count < len) { - ret = -EINVAL; - break; - } - if (!ccw.cda) { - ret = -EFAULT; - break; - } - revinfo.revision = - address_space_lduw_be(&address_space_memory, ccw.cda, - MEMTXATTRS_UNSPECIFIED, NULL); - revinfo.length = - address_space_lduw_be(&address_space_memory, - ccw.cda + sizeof(revinfo.revision), - MEMTXATTRS_UNSPECIFIED, NULL); - if (ccw.count < len + revinfo.length || - (check_len && ccw.count > len + revinfo.length)) { - ret = -EINVAL; - break; - } - /* - * Once we start to support revisions with additional data, we'll - * need to fetch it here. Nothing to do for now, though. - */ - if (dev->revision >= 0 || - revinfo.revision > virtio_ccw_rev_max(dev)) { - ret = -ENOSYS; - break; - } - ret = 0; - dev->revision = revinfo.revision; - break; - default: - ret = -ENOSYS; - break; - } - return ret; -} - -static void virtio_sch_disable_cb(SubchDev *sch) -{ - VirtioCcwDevice *dev = sch->driver_data; - - dev->revision = -1; -} - -static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) -{ - unsigned int cssid = 0; - unsigned int ssid = 0; - unsigned int schid; - unsigned int devno; - bool have_devno = false; - bool found = false; - SubchDev *sch; - int num; - Error *err = NULL; - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_GET_CLASS(dev); - - sch = g_malloc0(sizeof(SubchDev)); - - sch->driver_data = dev; - dev->sch = sch; - - dev->indicators = NULL; - - /* Initialize subchannel structure. */ - sch->channel_prog = 0x0; - sch->last_cmd_valid = false; - sch->thinint_active = false; - /* - * Use a device number if provided. Otherwise, fall back to subchannel - * number. - */ - if (dev->bus_id) { - num = sscanf(dev->bus_id, "%x.%x.%04x", &cssid, &ssid, &devno); - if (num == 3) { - if ((cssid > MAX_CSSID) || (ssid > MAX_SSID)) { - error_setg(errp, "Invalid cssid or ssid: cssid %x, ssid %x", - cssid, ssid); - goto out_err; - } - /* Enforce use of virtual cssid. */ - if (cssid != VIRTUAL_CSSID) { - error_setg(errp, "cssid %x not valid for virtio devices", - cssid); - goto out_err; - } - if (css_devno_used(cssid, ssid, devno)) { - error_setg(errp, "Device %x.%x.%04x already exists", - cssid, ssid, devno); - goto out_err; - } - sch->cssid = cssid; - sch->ssid = ssid; - sch->devno = devno; - have_devno = true; - } else { - error_setg(errp, "Malformed devno parameter '%s'", dev->bus_id); - goto out_err; - } - } - - /* Find the next free id. */ - if (have_devno) { - for (schid = 0; schid <= MAX_SCHID; schid++) { - if (!css_find_subch(1, cssid, ssid, schid)) { - sch->schid = schid; - css_subch_assign(cssid, ssid, schid, devno, sch); - found = true; - break; - } - } - if (!found) { - error_setg(errp, "No free subchannel found for %x.%x.%04x", - cssid, ssid, devno); - goto out_err; - } - trace_virtio_ccw_new_device(cssid, ssid, schid, devno, - "user-configured"); - } else { - cssid = VIRTUAL_CSSID; - for (ssid = 0; ssid <= MAX_SSID; ssid++) { - for (schid = 0; schid <= MAX_SCHID; schid++) { - if (!css_find_subch(1, cssid, ssid, schid)) { - sch->cssid = cssid; - sch->ssid = ssid; - sch->schid = schid; - devno = schid; - /* - * If the devno is already taken, look further in this - * subchannel set. - */ - while (css_devno_used(cssid, ssid, devno)) { - if (devno == MAX_SCHID) { - devno = 0; - } else if (devno == schid - 1) { - error_setg(errp, "No free devno found"); - goto out_err; - } else { - devno++; - } - } - sch->devno = devno; - css_subch_assign(cssid, ssid, schid, devno, sch); - found = true; - break; - } - } - if (found) { - break; - } - } - if (!found) { - error_setg(errp, "Virtual channel subsystem is full!"); - goto out_err; - } - trace_virtio_ccw_new_device(cssid, ssid, schid, devno, - "auto-configured"); - } - - /* Build initial schib. */ - css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE); - - sch->ccw_cb = virtio_ccw_cb; - sch->disable_cb = virtio_sch_disable_cb; - - /* Build senseid data. */ - memset(&sch->id, 0, sizeof(SenseId)); - sch->id.reserved = 0xff; - sch->id.cu_type = VIRTIO_CCW_CU_TYPE; - - dev->revision = -1; - - if (k->realize) { - k->realize(dev, &err); - } - if (err) { - error_propagate(errp, err); - css_subch_assign(cssid, ssid, schid, devno, NULL); - goto out_err; - } - - return; - -out_err: - dev->sch = NULL; - g_free(sch); -} - -static int virtio_ccw_exit(VirtioCcwDevice *dev) -{ - SubchDev *sch = dev->sch; - - if (sch) { - css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); - g_free(sch); - } - if (dev->indicators) { - release_indicator(&dev->routes.adapter, dev->indicators); - dev->indicators = NULL; - } - return 0; -} - -static void virtio_ccw_net_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - DeviceState *qdev = DEVICE(ccw_dev); - VirtIONetCcw *dev = VIRTIO_NET_CCW(ccw_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(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - } -} - -static void virtio_ccw_net_instance_init(Object *obj) -{ - VirtIONetCcw *dev = VIRTIO_NET_CCW(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 virtio_ccw_blk_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - VirtIOBlkCcw *dev = VIRTIO_BLK_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; - - qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - } -} - -static void virtio_ccw_blk_instance_init(Object *obj) -{ - VirtIOBlkCcw *dev = VIRTIO_BLK_CCW(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 virtio_ccw_serial_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - VirtioSerialCcw *dev = VIRTIO_SERIAL_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - DeviceState *proxy = DEVICE(ccw_dev); - Error *err = NULL; - char *bus_name; - - /* - * For command line compatibility, this sets the virtio-serial-device bus - * name as before. - */ - if (proxy->id) { - bus_name = g_strdup_printf("%s.0", proxy->id); - virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name); - g_free(bus_name); - } - - qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - } -} - - -static void virtio_ccw_serial_instance_init(Object *obj) -{ - VirtioSerialCcw *dev = VIRTIO_SERIAL_CCW(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_SERIAL); -} - -static void virtio_ccw_balloon_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - VirtIOBalloonCcw *dev = VIRTIO_BALLOON_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; - - qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - } -} - -static void virtio_ccw_balloon_instance_init(Object *obj) -{ - VirtIOBalloonCcw *dev = VIRTIO_BALLOON_CCW(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_BALLOON); - object_property_add_alias(obj, "guest-stats", OBJECT(&dev->vdev), - "guest-stats", &error_abort); - object_property_add_alias(obj, "guest-stats-polling-interval", - OBJECT(&dev->vdev), - "guest-stats-polling-interval", &error_abort); -} - -static void virtio_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - VirtIOSCSICcw *dev = VIRTIO_SCSI_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - DeviceState *qdev = DEVICE(ccw_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(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - } -} - -static void virtio_ccw_scsi_instance_init(Object *obj) -{ - VirtIOSCSICcw *dev = VIRTIO_SCSI_CCW(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_SCSI); - object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev), "iothread", - &error_abort); -} - -#ifdef CONFIG_VHOST_SCSI -static void vhost_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - VHostSCSICcw *dev = VHOST_SCSI_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; - - qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - } -} - -static void vhost_ccw_scsi_instance_init(Object *obj) -{ - VHostSCSICcw *dev = VHOST_SCSI_CCW(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VHOST_SCSI); -} -#endif - -static void virtio_ccw_rng_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - VirtIORNGCcw *dev = VIRTIO_RNG_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; - - qdev_set_parent_bus(vdev, BUS(&ccw_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); -} - -/* DeviceState to VirtioCcwDevice. Note: used on datapath, - * be careful and test performance if you change this. - */ -static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d) -{ - return container_of(d, VirtioCcwDevice, parent_obj); -} - -static uint8_t virtio_set_ind_atomic(SubchDev *sch, uint64_t ind_loc, - uint8_t to_be_set) -{ - uint8_t ind_old, ind_new; - hwaddr len = 1; - uint8_t *ind_addr; - - ind_addr = cpu_physical_memory_map(ind_loc, &len, 1); - if (!ind_addr) { - error_report("%s(%x.%x.%04x): unable to access indicator", - __func__, sch->cssid, sch->ssid, sch->schid); - return -1; - } - do { - ind_old = *ind_addr; - ind_new = ind_old | to_be_set; - } while (atomic_cmpxchg(ind_addr, ind_old, ind_new) != ind_old); - cpu_physical_memory_unmap(ind_addr, len, 1, len); - - return ind_old; -} - -static void virtio_ccw_notify(DeviceState *d, uint16_t vector) -{ - VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d); - SubchDev *sch = dev->sch; - uint64_t indicators; - - /* queue indicators + secondary indicators */ - if (vector >= VIRTIO_CCW_QUEUE_MAX + 64) { - return; - } - - if (vector < VIRTIO_CCW_QUEUE_MAX) { - if (!dev->indicators) { - return; - } - if (sch->thinint_active) { - /* - * In the adapter interrupt case, indicators points to a - * memory area that may be (way) larger than 64 bit and - * ind_bit indicates the start of the indicators in a big - * endian notation. - */ - uint64_t ind_bit = dev->routes.adapter.ind_offset; - - virtio_set_ind_atomic(sch, dev->indicators->addr + - (ind_bit + vector) / 8, - 0x80 >> ((ind_bit + vector) % 8)); - if (!virtio_set_ind_atomic(sch, dev->summary_indicator->addr, - 0x01)) { - css_adapter_interrupt(dev->thinint_isc); - } - } else { - indicators = address_space_ldq(&address_space_memory, - dev->indicators->addr, - MEMTXATTRS_UNSPECIFIED, - NULL); - indicators |= 1ULL << vector; - address_space_stq(&address_space_memory, dev->indicators->addr, - indicators, MEMTXATTRS_UNSPECIFIED, NULL); - css_conditional_io_interrupt(sch); - } - } else { - if (!dev->indicators2) { - return; - } - vector = 0; - indicators = address_space_ldq(&address_space_memory, - dev->indicators2->addr, - MEMTXATTRS_UNSPECIFIED, - NULL); - indicators |= 1ULL << vector; - address_space_stq(&address_space_memory, dev->indicators2->addr, - indicators, MEMTXATTRS_UNSPECIFIED, NULL); - css_conditional_io_interrupt(sch); - } -} - -static void virtio_ccw_reset(DeviceState *d) -{ - VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - - virtio_ccw_reset_virtio(dev, vdev); - css_reset_sch(dev->sch); -} - -static void virtio_ccw_vmstate_change(DeviceState *d, bool running) -{ - VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - - if (running) { - virtio_ccw_start_ioeventfd(dev); - } else { - virtio_ccw_stop_ioeventfd(dev); - } -} - -static bool virtio_ccw_query_guest_notifiers(DeviceState *d) -{ - VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - - return !!(dev->sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA); -} - -static int virtio_ccw_set_host_notifier(DeviceState *d, int n, bool assign) -{ - VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - - /* Stop using the generic ioeventfd, we are doing eventfd handling - * ourselves below */ - dev->ioeventfd_disabled = assign; - if (assign) { - virtio_ccw_stop_ioeventfd(dev); - } - return virtio_ccw_set_guest2host_notifier(dev, n, assign, false); -} - -static int virtio_ccw_get_mappings(VirtioCcwDevice *dev) -{ - int r; - - if (!dev->sch->thinint_active) { - return -EINVAL; - } - - r = map_indicator(&dev->routes.adapter, dev->summary_indicator); - if (r) { - return r; - } - r = map_indicator(&dev->routes.adapter, dev->indicators); - if (r) { - return r; - } - dev->routes.adapter.summary_addr = dev->summary_indicator->map; - dev->routes.adapter.ind_addr = dev->indicators->map; - - return 0; -} - -static int virtio_ccw_setup_irqroutes(VirtioCcwDevice *dev, int nvqs) -{ - int i; - VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - int ret; - S390FLICState *fs = s390_get_flic(); - S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); - - ret = virtio_ccw_get_mappings(dev); - if (ret) { - return ret; - } - for (i = 0; i < nvqs; i++) { - if (!virtio_queue_get_num(vdev, i)) { - break; - } - } - dev->routes.num_routes = i; - return fsc->add_adapter_routes(fs, &dev->routes); -} - -static void virtio_ccw_release_irqroutes(VirtioCcwDevice *dev, int nvqs) -{ - S390FLICState *fs = s390_get_flic(); - S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); - - fsc->release_adapter_routes(fs, &dev->routes); -} - -static int virtio_ccw_add_irqfd(VirtioCcwDevice *dev, int n) -{ - VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - VirtQueue *vq = virtio_get_queue(vdev, n); - EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); - - return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, notifier, NULL, - dev->routes.gsi[n]); -} - -static void virtio_ccw_remove_irqfd(VirtioCcwDevice *dev, int n) -{ - VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - VirtQueue *vq = virtio_get_queue(vdev, n); - EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); - int ret; - - ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, notifier, - dev->routes.gsi[n]); - assert(ret == 0); -} - -static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n, - bool assign, bool with_irqfd) -{ - VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - VirtQueue *vq = virtio_get_queue(vdev, n); - EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - - if (assign) { - int r = event_notifier_init(notifier, 0); - - if (r < 0) { - return r; - } - virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd); - if (with_irqfd) { - r = virtio_ccw_add_irqfd(dev, n); - if (r) { - virtio_queue_set_guest_notifier_fd_handler(vq, false, - with_irqfd); - return r; - } - } - /* - * We do not support individual masking for channel devices, so we - * need to manually trigger any guest masking callbacks here. - */ - if (k->guest_notifier_mask) { - k->guest_notifier_mask(vdev, n, false); - } - /* get lost events and re-inject */ - if (k->guest_notifier_pending && - k->guest_notifier_pending(vdev, n)) { - event_notifier_set(notifier); - } - } else { - if (k->guest_notifier_mask) { - k->guest_notifier_mask(vdev, n, true); - } - if (with_irqfd) { - virtio_ccw_remove_irqfd(dev, n); - } - virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd); - event_notifier_cleanup(notifier); - } - return 0; -} - -static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs, - bool assigned) -{ - VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - bool with_irqfd = dev->sch->thinint_active && kvm_irqfds_enabled(); - int r, n; - - if (with_irqfd && assigned) { - /* irq routes need to be set up before assigning irqfds */ - r = virtio_ccw_setup_irqroutes(dev, nvqs); - if (r < 0) { - goto irqroute_error; - } - } - for (n = 0; n < nvqs; n++) { - if (!virtio_queue_get_num(vdev, n)) { - break; - } - r = virtio_ccw_set_guest_notifier(dev, n, assigned, with_irqfd); - if (r < 0) { - goto assign_error; - } - } - if (with_irqfd && !assigned) { - /* release irq routes after irqfds have been released */ - virtio_ccw_release_irqroutes(dev, nvqs); - } - return 0; - -assign_error: - while (--n >= 0) { - virtio_ccw_set_guest_notifier(dev, n, !assigned, false); - } -irqroute_error: - if (with_irqfd && assigned) { - virtio_ccw_release_irqroutes(dev, nvqs); - } - return r; -} - -static void virtio_ccw_save_queue(DeviceState *d, int n, QEMUFile *f) -{ - VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - - qemu_put_be16(f, virtio_queue_vector(vdev, n)); -} - -static int virtio_ccw_load_queue(DeviceState *d, int n, QEMUFile *f) -{ - VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - uint16_t vector; - - qemu_get_be16s(f, &vector); - virtio_queue_set_vector(vdev, n , vector); - - return 0; -} - -static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f) -{ - VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - SubchDev *s = dev->sch; - VirtIODevice *vdev = virtio_ccw_get_vdev(s); - - subch_device_save(s, f); - if (dev->indicators != NULL) { - qemu_put_be32(f, dev->indicators->len); - qemu_put_be64(f, dev->indicators->addr); - } else { - qemu_put_be32(f, 0); - qemu_put_be64(f, 0UL); - } - if (dev->indicators2 != NULL) { - qemu_put_be32(f, dev->indicators2->len); - qemu_put_be64(f, dev->indicators2->addr); - } else { - qemu_put_be32(f, 0); - qemu_put_be64(f, 0UL); - } - if (dev->summary_indicator != NULL) { - qemu_put_be32(f, dev->summary_indicator->len); - qemu_put_be64(f, dev->summary_indicator->addr); - } else { - qemu_put_be32(f, 0); - qemu_put_be64(f, 0UL); - } - qemu_put_be16(f, vdev->config_vector); - qemu_put_be64(f, dev->routes.adapter.ind_offset); - qemu_put_byte(f, dev->thinint_isc); - qemu_put_be32(f, dev->revision); -} - -static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f) -{ - VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - SubchDev *s = dev->sch; - VirtIODevice *vdev = virtio_ccw_get_vdev(s); - int len; - - s->driver_data = dev; - subch_device_load(s, f); - len = qemu_get_be32(f); - if (len != 0) { - dev->indicators = get_indicator(qemu_get_be64(f), len); - } else { - qemu_get_be64(f); - dev->indicators = NULL; - } - len = qemu_get_be32(f); - if (len != 0) { - dev->indicators2 = get_indicator(qemu_get_be64(f), len); - } else { - qemu_get_be64(f); - dev->indicators2 = NULL; - } - len = qemu_get_be32(f); - if (len != 0) { - dev->summary_indicator = get_indicator(qemu_get_be64(f), len); - } else { - qemu_get_be64(f); - dev->summary_indicator = NULL; - } - qemu_get_be16s(f, &vdev->config_vector); - dev->routes.adapter.ind_offset = qemu_get_be64(f); - dev->thinint_isc = qemu_get_byte(f); - dev->revision = qemu_get_be32(f); - if (s->thinint_active) { - return css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO, - dev->thinint_isc, true, false, - &dev->routes.adapter.adapter_id); - } - - return 0; -} - -/* This is called by virtio-bus just after the device is plugged. */ -static void virtio_ccw_device_plugged(DeviceState *d, Error **errp) -{ - VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - SubchDev *sch = dev->sch; - int n = virtio_get_num_queues(vdev); - - if (virtio_get_num_queues(vdev) > VIRTIO_CCW_QUEUE_MAX) { - error_setg(errp, "The nubmer of virtqueues %d " - "exceeds ccw limit %d", n, - VIRTIO_CCW_QUEUE_MAX); - return; - } - - if (!kvm_eventfds_enabled()) { - dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD; - } - - 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); - - virtio_ccw_stop_ioeventfd(dev); -} -/**************** Virtio-ccw Bus Device Descriptions *******************/ - -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(), -}; - -static void virtio_ccw_net_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = virtio_ccw_net_realize; - k->exit = virtio_ccw_exit; - dc->reset = virtio_ccw_reset; - dc->props = virtio_ccw_net_properties; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); -} - -static const TypeInfo virtio_ccw_net = { - .name = TYPE_VIRTIO_NET_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtIONetCcw), - .instance_init = virtio_ccw_net_instance_init, - .class_init = virtio_ccw_net_class_init, -}; - -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(), -}; - -static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = virtio_ccw_blk_realize; - k->exit = virtio_ccw_exit; - dc->reset = virtio_ccw_reset; - dc->props = virtio_ccw_blk_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo virtio_ccw_blk = { - .name = TYPE_VIRTIO_BLK_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtIOBlkCcw), - .instance_init = virtio_ccw_blk_instance_init, - .class_init = virtio_ccw_blk_class_init, -}; - -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(), -}; - -static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = virtio_ccw_serial_realize; - k->exit = virtio_ccw_exit; - dc->reset = virtio_ccw_reset; - dc->props = virtio_ccw_serial_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo virtio_ccw_serial = { - .name = TYPE_VIRTIO_SERIAL_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtioSerialCcw), - .instance_init = virtio_ccw_serial_instance_init, - .class_init = virtio_ccw_serial_class_init, -}; - -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(), -}; - -static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = virtio_ccw_balloon_realize; - k->exit = virtio_ccw_exit; - dc->reset = virtio_ccw_reset; - dc->props = virtio_ccw_balloon_properties; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo virtio_ccw_balloon = { - .name = TYPE_VIRTIO_BALLOON_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtIOBalloonCcw), - .instance_init = virtio_ccw_balloon_instance_init, - .class_init = virtio_ccw_balloon_class_init, -}; - -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(), -}; - -static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = virtio_ccw_scsi_realize; - k->exit = virtio_ccw_exit; - dc->reset = virtio_ccw_reset; - dc->props = virtio_ccw_scsi_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo virtio_ccw_scsi = { - .name = TYPE_VIRTIO_SCSI_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtIOSCSICcw), - .instance_init = virtio_ccw_scsi_instance_init, - .class_init = virtio_ccw_scsi_class_init, -}; - -#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(), -}; - -static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = vhost_ccw_scsi_realize; - k->exit = virtio_ccw_exit; - dc->reset = virtio_ccw_reset; - dc->props = vhost_ccw_scsi_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo vhost_ccw_scsi = { - .name = TYPE_VHOST_SCSI_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VHostSCSICcw), - .instance_init = vhost_ccw_scsi_instance_init, - .class_init = vhost_ccw_scsi_class_init, -}; -#endif - -static void virtio_ccw_rng_instance_init(Object *obj) -{ - VirtIORNGCcw *dev = VIRTIO_RNG_CCW(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 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(), -}; - -static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = virtio_ccw_rng_realize; - k->exit = virtio_ccw_exit; - dc->reset = virtio_ccw_reset; - dc->props = virtio_ccw_rng_properties; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo virtio_ccw_rng = { - .name = TYPE_VIRTIO_RNG_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtIORNGCcw), - .instance_init = virtio_ccw_rng_instance_init, - .class_init = virtio_ccw_rng_class_init, -}; - -static void virtio_ccw_busdev_realize(DeviceState *dev, Error **errp) -{ - VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; - - virtio_ccw_bus_new(&_dev->bus, sizeof(_dev->bus), _dev); - virtio_ccw_device_realize(_dev, errp); -} - -static int virtio_ccw_busdev_exit(DeviceState *dev) -{ - VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; - VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev); - - return _info->exit(_dev); -} - -static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; - SubchDev *sch = _dev->sch; - - virtio_ccw_stop_ioeventfd(_dev); - - /* - * We should arrive here only for device_del, since we don't support - * direct hot(un)plug of channels, but only through virtio. - */ - assert(sch != NULL); - /* Subchannel is now disabled and no longer valid. */ - sch->curr_status.pmcw.flags &= ~(PMCW_FLAGS_MASK_ENA | - PMCW_FLAGS_MASK_DNV); - - css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0); - - object_unparent(OBJECT(dev)); -} - -static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = virtio_ccw_busdev_realize; - dc->exit = virtio_ccw_busdev_exit; - dc->bus_type = TYPE_VIRTUAL_CSS_BUS; -} - -static const TypeInfo virtio_ccw_device_info = { - .name = TYPE_VIRTIO_CCW_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(VirtioCcwDevice), - .class_init = virtio_ccw_device_class_init, - .class_size = sizeof(VirtIOCCWDeviceClass), - .abstract = true, -}; - -/***************** Virtual-css Bus Bridge Device ********************/ -/* Only required to have the virtio bus as child in the system bus */ - -static int virtual_css_bridge_init(SysBusDevice *dev) -{ - /* nothing */ - return 0; -} - -static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->init = virtual_css_bridge_init; - hc->unplug = virtio_ccw_busdev_unplug; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); -} - -static const TypeInfo virtual_css_bridge_info = { - .name = "virtual-css-bridge", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysBusDevice), - .class_init = virtual_css_bridge_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - -/* virtio-ccw-bus */ - -static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, - VirtioCcwDevice *dev) -{ - DeviceState *qdev = DEVICE(dev); - char virtio_bus_name[] = "virtio-bus"; - - qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_CCW_BUS, - qdev, virtio_bus_name); -} - -static void virtio_ccw_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_ccw_notify; - k->vmstate_change = virtio_ccw_vmstate_change; - k->query_guest_notifiers = virtio_ccw_query_guest_notifiers; - k->set_host_notifier = virtio_ccw_set_host_notifier; - k->set_guest_notifiers = virtio_ccw_set_guest_notifiers; - k->save_queue = virtio_ccw_save_queue; - k->load_queue = virtio_ccw_load_queue; - 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; -} - -static const TypeInfo virtio_ccw_bus_info = { - .name = TYPE_VIRTIO_CCW_BUS, - .parent = TYPE_VIRTIO_BUS, - .instance_size = sizeof(VirtioCcwBusState), - .class_init = virtio_ccw_bus_class_init, -}; - -#ifdef CONFIG_VIRTFS -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(), -}; - -static void virtio_ccw_9p_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - V9fsCCWState *dev = VIRTIO_9P_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; - - qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - } -} - -static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->exit = virtio_ccw_exit; - k->realize = virtio_ccw_9p_realize; - dc->reset = virtio_ccw_reset; - dc->props = virtio_ccw_9p_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static void virtio_ccw_9p_instance_init(Object *obj) -{ - V9fsCCWState *dev = VIRTIO_9P_CCW(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_9P); -} - -static const TypeInfo virtio_ccw_9p_info = { - .name = TYPE_VIRTIO_9P_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(V9fsCCWState), - .instance_init = virtio_ccw_9p_instance_init, - .class_init = virtio_ccw_9p_class_init, -}; -#endif - -static void virtio_ccw_register(void) -{ - type_register_static(&virtio_ccw_bus_info); - type_register_static(&virtual_css_bus_info); - type_register_static(&virtio_ccw_device_info); - type_register_static(&virtio_ccw_serial); - type_register_static(&virtio_ccw_blk); - type_register_static(&virtio_ccw_net); - type_register_static(&virtio_ccw_balloon); - type_register_static(&virtio_ccw_scsi); -#ifdef CONFIG_VHOST_SCSI - type_register_static(&vhost_ccw_scsi); -#endif - type_register_static(&virtio_ccw_rng); - type_register_static(&virtual_css_bridge_info); -#ifdef CONFIG_VIRTFS - type_register_static(&virtio_ccw_9p_info); -#endif -} - -type_init(virtio_ccw_register) diff --git a/qemu/hw/s390x/virtio-ccw.h b/qemu/hw/s390x/virtio-ccw.h deleted file mode 100644 index 66c831ba8..000000000 --- a/qemu/hw/s390x/virtio-ccw.h +++ /dev/null @@ -1,211 +0,0 @@ -/* - * virtio ccw target definitions - * - * Copyright 2012,2015 IBM Corp. - * Author(s): Cornelia Huck - * Pierre Morel - * - * 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. - */ - -#ifndef HW_S390X_VIRTIO_CCW_H -#define HW_S390X_VIRTIO_CCW_H - -#include -#include -#include -#include -#ifdef CONFIG_VHOST_SCSI -#include -#endif -#include -#include -#include - -#include "css.h" - -#define VIRTUAL_CSSID 0xfe - -#define VIRTIO_CCW_CU_TYPE 0x3832 -#define VIRTIO_CCW_CHPID_TYPE 0x32 - -#define CCW_CMD_SET_VQ 0x13 -#define CCW_CMD_VDEV_RESET 0x33 -#define CCW_CMD_READ_FEAT 0x12 -#define CCW_CMD_WRITE_FEAT 0x11 -#define CCW_CMD_READ_CONF 0x22 -#define CCW_CMD_WRITE_CONF 0x21 -#define CCW_CMD_WRITE_STATUS 0x31 -#define CCW_CMD_SET_IND 0x43 -#define CCW_CMD_SET_CONF_IND 0x53 -#define CCW_CMD_READ_VQ_CONF 0x32 -#define CCW_CMD_SET_IND_ADAPTER 0x73 -#define CCW_CMD_SET_VIRTIO_REV 0x83 - -#define TYPE_VIRTIO_CCW_DEVICE "virtio-ccw-device" -#define VIRTIO_CCW_DEVICE(obj) \ - OBJECT_CHECK(VirtioCcwDevice, (obj), TYPE_VIRTIO_CCW_DEVICE) -#define VIRTIO_CCW_DEVICE_CLASS(klass) \ - OBJECT_CLASS_CHECK(VirtIOCCWDeviceClass, (klass), TYPE_VIRTIO_CCW_DEVICE) -#define VIRTIO_CCW_DEVICE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(VirtIOCCWDeviceClass, (obj), TYPE_VIRTIO_CCW_DEVICE) - -typedef struct VirtioBusState VirtioCcwBusState; -typedef struct VirtioBusClass VirtioCcwBusClass; - -#define TYPE_VIRTIO_CCW_BUS "virtio-ccw-bus" -#define VIRTIO_CCW_BUS(obj) \ - OBJECT_CHECK(VirtioCcwBus, (obj), TYPE_VIRTIO_CCW_BUS) -#define VIRTIO_CCW_BUS_GET_CLASS(obj) \ - OBJECT_CHECK(VirtioCcwBusState, (obj), TYPE_VIRTIO_CCW_BUS) -#define VIRTIO_CCW_BUS_CLASS(klass) \ - OBJECT_CLASS_CHECK(VirtioCcwBusClass, klass, TYPE_VIRTIO_CCW_BUS) - -typedef struct VirtioCcwDevice VirtioCcwDevice; - -typedef struct VirtIOCCWDeviceClass { - DeviceClass parent_class; - void (*realize)(VirtioCcwDevice *dev, Error **errp); - int (*exit)(VirtioCcwDevice *dev); -} VirtIOCCWDeviceClass; - -/* Performance improves when virtqueue kick processing is decoupled from the - * vcpu thread using ioeventfd for some devices. */ -#define VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT 1 -#define VIRTIO_CCW_FLAG_USE_IOEVENTFD (1 << VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT) - -struct VirtioCcwDevice { - DeviceState parent_obj; - SubchDev *sch; - char *bus_id; - int revision; - uint32_t max_rev; - VirtioBusState bus; - bool ioeventfd_started; - bool ioeventfd_disabled; - uint32_t flags; - uint8_t thinint_isc; - AdapterRoutes routes; - /* Guest provided values: */ - IndAddr *indicators; - IndAddr *indicators2; - IndAddr *summary_indicator; - uint64_t ind_bit; -}; - -/* The maximum virtio revision we support. */ -#define VIRTIO_CCW_MAX_REV 1 -static inline int virtio_ccw_rev_max(VirtioCcwDevice *dev) -{ - return dev->max_rev; -} - -/* virtual css bus type */ -typedef struct VirtualCssBus { - BusState parent_obj; -} VirtualCssBus; - -#define TYPE_VIRTUAL_CSS_BUS "virtual-css-bus" -#define VIRTUAL_CSS_BUS(obj) \ - OBJECT_CHECK(VirtualCssBus, (obj), TYPE_VIRTUAL_CSS_BUS) - -/* virtio-scsi-ccw */ - -#define TYPE_VIRTIO_SCSI_CCW "virtio-scsi-ccw" -#define VIRTIO_SCSI_CCW(obj) \ - OBJECT_CHECK(VirtIOSCSICcw, (obj), TYPE_VIRTIO_SCSI_CCW) - -typedef struct VirtIOSCSICcw { - VirtioCcwDevice parent_obj; - VirtIOSCSI vdev; -} VirtIOSCSICcw; - -#ifdef CONFIG_VHOST_SCSI -/* vhost-scsi-ccw */ - -#define TYPE_VHOST_SCSI_CCW "vhost-scsi-ccw" -#define VHOST_SCSI_CCW(obj) \ - OBJECT_CHECK(VHostSCSICcw, (obj), TYPE_VHOST_SCSI_CCW) - -typedef struct VHostSCSICcw { - VirtioCcwDevice parent_obj; - VHostSCSI vdev; -} VHostSCSICcw; -#endif - -/* virtio-blk-ccw */ - -#define TYPE_VIRTIO_BLK_CCW "virtio-blk-ccw" -#define VIRTIO_BLK_CCW(obj) \ - OBJECT_CHECK(VirtIOBlkCcw, (obj), TYPE_VIRTIO_BLK_CCW) - -typedef struct VirtIOBlkCcw { - VirtioCcwDevice parent_obj; - VirtIOBlock vdev; -} VirtIOBlkCcw; - -/* virtio-balloon-ccw */ - -#define TYPE_VIRTIO_BALLOON_CCW "virtio-balloon-ccw" -#define VIRTIO_BALLOON_CCW(obj) \ - OBJECT_CHECK(VirtIOBalloonCcw, (obj), TYPE_VIRTIO_BALLOON_CCW) - -typedef struct VirtIOBalloonCcw { - VirtioCcwDevice parent_obj; - VirtIOBalloon vdev; -} VirtIOBalloonCcw; - -/* virtio-serial-ccw */ - -#define TYPE_VIRTIO_SERIAL_CCW "virtio-serial-ccw" -#define VIRTIO_SERIAL_CCW(obj) \ - OBJECT_CHECK(VirtioSerialCcw, (obj), TYPE_VIRTIO_SERIAL_CCW) - -typedef struct VirtioSerialCcw { - VirtioCcwDevice parent_obj; - VirtIOSerial vdev; -} VirtioSerialCcw; - -/* virtio-net-ccw */ - -#define TYPE_VIRTIO_NET_CCW "virtio-net-ccw" -#define VIRTIO_NET_CCW(obj) \ - OBJECT_CHECK(VirtIONetCcw, (obj), TYPE_VIRTIO_NET_CCW) - -typedef struct VirtIONetCcw { - VirtioCcwDevice parent_obj; - VirtIONet vdev; -} VirtIONetCcw; - -/* virtio-rng-ccw */ - -#define TYPE_VIRTIO_RNG_CCW "virtio-rng-ccw" -#define VIRTIO_RNG_CCW(obj) \ - OBJECT_CHECK(VirtIORNGCcw, (obj), TYPE_VIRTIO_RNG_CCW) - -typedef struct VirtIORNGCcw { - VirtioCcwDevice parent_obj; - VirtIORNG vdev; -} VirtIORNGCcw; - -VirtualCssBus *virtual_css_bus_init(void); -void virtio_ccw_device_update_status(SubchDev *sch); -VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch); - -#ifdef CONFIG_VIRTFS -#include "hw/9pfs/virtio-9p.h" - -#define TYPE_VIRTIO_9P_CCW "virtio-9p-ccw" -#define VIRTIO_9P_CCW(obj) \ - OBJECT_CHECK(V9fsCCWState, (obj), TYPE_VIRTIO_9P_CCW) - -typedef struct V9fsCCWState { - VirtioCcwDevice parent_obj; - V9fsVirtioState vdev; -} V9fsCCWState; - -#endif /* CONFIG_VIRTFS */ - -#endif diff --git a/qemu/hw/scsi/Makefile.objs b/qemu/hw/scsi/Makefile.objs deleted file mode 100644 index 5a2248be3..000000000 --- a/qemu/hw/scsi/Makefile.objs +++ /dev/null @@ -1,14 +0,0 @@ -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 -common-obj-$(CONFIG_ESP_PCI) += esp-pci.o -obj-$(CONFIG_PSERIES) += spapr_vscsi.o - -ifeq ($(CONFIG_VIRTIO),y) -obj-y += virtio-scsi.o virtio-scsi-dataplane.o -obj-$(CONFIG_VHOST_SCSI) += vhost-scsi.o -endif diff --git a/qemu/hw/scsi/esp-pci.c b/qemu/hw/scsi/esp-pci.c deleted file mode 100644 index 595f88b35..000000000 --- a/qemu/hw/scsi/esp-pci.c +++ /dev/null @@ -1,531 +0,0 @@ -/* - * QEMU ESP/NCR53C9x emulation - * - * Copyright (c) 2005-2006 Fabrice Bellard - * Copyright (c) 2012 Herve Poussineau - * - * 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/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" - -#define PCI_ESP(obj) \ - OBJECT_CHECK(PCIESPState, (obj), TYPE_AM53C974_DEVICE) - -#define DMA_CMD 0x0 -#define DMA_STC 0x1 -#define DMA_SPA 0x2 -#define DMA_WBC 0x3 -#define DMA_WAC 0x4 -#define DMA_STAT 0x5 -#define DMA_SMDLA 0x6 -#define DMA_WMAC 0x7 - -#define DMA_CMD_MASK 0x03 -#define DMA_CMD_DIAG 0x04 -#define DMA_CMD_MDL 0x10 -#define DMA_CMD_INTE_P 0x20 -#define DMA_CMD_INTE_D 0x40 -#define DMA_CMD_DIR 0x80 - -#define DMA_STAT_PWDN 0x01 -#define DMA_STAT_ERROR 0x02 -#define DMA_STAT_ABORT 0x04 -#define DMA_STAT_DONE 0x08 -#define DMA_STAT_SCSIINT 0x10 -#define DMA_STAT_BCMBLT 0x20 - -#define SBAC_STATUS 0x1000 - -typedef struct PCIESPState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - MemoryRegion io; - uint32_t dma_regs[8]; - uint32_t sbac; - ESPState esp; -} PCIESPState; - -static void esp_pci_handle_idle(PCIESPState *pci, uint32_t val) -{ - trace_esp_pci_dma_idle(val); - esp_dma_enable(&pci->esp, 0, 0); -} - -static void esp_pci_handle_blast(PCIESPState *pci, uint32_t val) -{ - trace_esp_pci_dma_blast(val); - qemu_log_mask(LOG_UNIMP, "am53c974: cmd BLAST not implemented\n"); -} - -static void esp_pci_handle_abort(PCIESPState *pci, uint32_t val) -{ - trace_esp_pci_dma_abort(val); - if (pci->esp.current_req) { - scsi_req_cancel(pci->esp.current_req); - } -} - -static void esp_pci_handle_start(PCIESPState *pci, uint32_t val) -{ - trace_esp_pci_dma_start(val); - - pci->dma_regs[DMA_WBC] = pci->dma_regs[DMA_STC]; - pci->dma_regs[DMA_WAC] = pci->dma_regs[DMA_SPA]; - pci->dma_regs[DMA_WMAC] = pci->dma_regs[DMA_SMDLA]; - - pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_BCMBLT | DMA_STAT_SCSIINT - | DMA_STAT_DONE | DMA_STAT_ABORT - | DMA_STAT_ERROR | DMA_STAT_PWDN); - - esp_dma_enable(&pci->esp, 0, 1); -} - -static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val) -{ - trace_esp_pci_dma_write(saddr, pci->dma_regs[saddr], val); - switch (saddr) { - case DMA_CMD: - pci->dma_regs[saddr] = val; - switch (val & DMA_CMD_MASK) { - case 0x0: /* IDLE */ - esp_pci_handle_idle(pci, val); - break; - case 0x1: /* BLAST */ - esp_pci_handle_blast(pci, val); - break; - case 0x2: /* ABORT */ - esp_pci_handle_abort(pci, val); - break; - case 0x3: /* START */ - esp_pci_handle_start(pci, val); - break; - default: /* can't happen */ - abort(); - } - break; - case DMA_STC: - case DMA_SPA: - case DMA_SMDLA: - pci->dma_regs[saddr] = val; - break; - case DMA_STAT: - if (!(pci->sbac & SBAC_STATUS)) { - /* clear some bits on write */ - uint32_t mask = DMA_STAT_ERROR | DMA_STAT_ABORT | DMA_STAT_DONE; - pci->dma_regs[DMA_STAT] &= ~(val & mask); - } - break; - default: - trace_esp_pci_error_invalid_write_dma(val, saddr); - return; - } -} - -static uint32_t esp_pci_dma_read(PCIESPState *pci, uint32_t saddr) -{ - uint32_t val; - - val = pci->dma_regs[saddr]; - if (saddr == DMA_STAT) { - if (pci->esp.rregs[ESP_RSTAT] & STAT_INT) { - val |= DMA_STAT_SCSIINT; - } - if (pci->sbac & SBAC_STATUS) { - pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_ERROR | DMA_STAT_ABORT | - DMA_STAT_DONE); - } - } - - trace_esp_pci_dma_read(saddr, val); - return val; -} - -static void esp_pci_io_write(void *opaque, hwaddr addr, - uint64_t val, unsigned int size) -{ - PCIESPState *pci = opaque; - - if (size < 4 || addr & 3) { - /* need to upgrade request: we only support 4-bytes accesses */ - uint32_t current = 0, mask; - int shift; - - if (addr < 0x40) { - current = pci->esp.wregs[addr >> 2]; - } else if (addr < 0x60) { - current = pci->dma_regs[(addr - 0x40) >> 2]; - } else if (addr < 0x74) { - current = pci->sbac; - } - - shift = (4 - size) * 8; - mask = (~(uint32_t)0 << shift) >> shift; - - shift = ((4 - (addr & 3)) & 3) * 8; - val <<= shift; - val |= current & ~(mask << shift); - addr &= ~3; - size = 4; - } - - if (addr < 0x40) { - /* SCSI core reg */ - esp_reg_write(&pci->esp, addr >> 2, val); - } else if (addr < 0x60) { - /* PCI DMA CCB */ - esp_pci_dma_write(pci, (addr - 0x40) >> 2, val); - } else if (addr == 0x70) { - /* DMA SCSI Bus and control */ - trace_esp_pci_sbac_write(pci->sbac, val); - pci->sbac = val; - } else { - trace_esp_pci_error_invalid_write((int)addr); - } -} - -static uint64_t esp_pci_io_read(void *opaque, hwaddr addr, - unsigned int size) -{ - PCIESPState *pci = opaque; - uint32_t ret; - - if (addr < 0x40) { - /* SCSI core reg */ - ret = esp_reg_read(&pci->esp, addr >> 2); - } else if (addr < 0x60) { - /* PCI DMA CCB */ - ret = esp_pci_dma_read(pci, (addr - 0x40) >> 2); - } else if (addr == 0x70) { - /* DMA SCSI Bus and control */ - trace_esp_pci_sbac_read(pci->sbac); - ret = pci->sbac; - } else { - /* Invalid region */ - trace_esp_pci_error_invalid_read((int)addr); - ret = 0; - } - - /* give only requested data */ - ret >>= (addr & 3) * 8; - ret &= ~(~(uint64_t)0 << (8 * size)); - - return ret; -} - -static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len, - DMADirection dir) -{ - dma_addr_t addr; - DMADirection expected_dir; - - if (pci->dma_regs[DMA_CMD] & DMA_CMD_DIR) { - expected_dir = DMA_DIRECTION_FROM_DEVICE; - } else { - expected_dir = DMA_DIRECTION_TO_DEVICE; - } - - if (dir != expected_dir) { - trace_esp_pci_error_invalid_dma_direction(); - return; - } - - if (pci->dma_regs[DMA_STAT] & DMA_CMD_MDL) { - qemu_log_mask(LOG_UNIMP, "am53c974: MDL transfer not implemented\n"); - } - - addr = pci->dma_regs[DMA_SPA]; - if (pci->dma_regs[DMA_WBC] < len) { - len = pci->dma_regs[DMA_WBC]; - } - - pci_dma_rw(PCI_DEVICE(pci), addr, buf, len, dir); - - /* update status registers */ - pci->dma_regs[DMA_WBC] -= len; - pci->dma_regs[DMA_WAC] += len; - if (pci->dma_regs[DMA_WBC] == 0) { - pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE; - } -} - -static void esp_pci_dma_memory_read(void *opaque, uint8_t *buf, int len) -{ - PCIESPState *pci = opaque; - esp_pci_dma_memory_rw(pci, buf, len, DMA_DIRECTION_TO_DEVICE); -} - -static void esp_pci_dma_memory_write(void *opaque, uint8_t *buf, int len) -{ - PCIESPState *pci = opaque; - esp_pci_dma_memory_rw(pci, buf, len, DMA_DIRECTION_FROM_DEVICE); -} - -static const MemoryRegionOps esp_pci_io_ops = { - .read = esp_pci_io_read, - .write = esp_pci_io_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 4, - }, -}; - -static void esp_pci_hard_reset(DeviceState *dev) -{ - PCIESPState *pci = PCI_ESP(dev); - esp_hard_reset(&pci->esp); - pci->dma_regs[DMA_CMD] &= ~(DMA_CMD_DIR | DMA_CMD_INTE_D | DMA_CMD_INTE_P - | DMA_CMD_MDL | DMA_CMD_DIAG | DMA_CMD_MASK); - pci->dma_regs[DMA_WBC] &= ~0xffff; - pci->dma_regs[DMA_WAC] = 0xffffffff; - pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_BCMBLT | DMA_STAT_SCSIINT - | DMA_STAT_DONE | DMA_STAT_ABORT - | DMA_STAT_ERROR); - pci->dma_regs[DMA_WMAC] = 0xfffffffd; -} - -static const VMStateDescription vmstate_esp_pci_scsi = { - .name = "pciespscsi", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, PCIESPState), - VMSTATE_BUFFER_UNSAFE(dma_regs, PCIESPState, 0, 8 * sizeof(uint32_t)), - VMSTATE_STRUCT(esp, PCIESPState, 0, vmstate_esp, ESPState), - VMSTATE_END_OF_LIST() - } -}; - -static void esp_pci_command_complete(SCSIRequest *req, uint32_t status, - size_t resid) -{ - ESPState *s = req->hba_private; - PCIESPState *pci = container_of(s, PCIESPState, esp); - - esp_command_complete(req, status, resid); - pci->dma_regs[DMA_WBC] = 0; - pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE; -} - -static const struct SCSIBusInfo esp_pci_scsi_info = { - .tcq = false, - .max_target = ESP_MAX_DEVS, - .max_lun = 7, - - .transfer_data = esp_transfer_data, - .complete = esp_pci_command_complete, - .cancel = esp_request_cancelled, -}; - -static void esp_pci_scsi_realize(PCIDevice *dev, Error **errp) -{ - PCIESPState *pci = PCI_ESP(dev); - DeviceState *d = DEVICE(dev); - ESPState *s = &pci->esp; - uint8_t *pci_conf; - - pci_conf = dev->config; - - /* Interrupt pin A */ - pci_conf[PCI_INTERRUPT_PIN] = 0x01; - - s->dma_memory_read = esp_pci_dma_memory_read; - s->dma_memory_write = esp_pci_dma_memory_write; - s->dma_opaque = pci; - s->chip_id = TCHI_AM53C974; - memory_region_init_io(&pci->io, OBJECT(pci), &esp_pci_io_ops, pci, - "esp-io", 0x80); - - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->io); - s->irq = pci_allocate_irq(dev); - - scsi_bus_new(&s->bus, sizeof(s->bus), d, &esp_pci_scsi_info, NULL); - if (!d->hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, errp); - } -} - -static void esp_pci_scsi_uninit(PCIDevice *d) -{ - PCIESPState *pci = PCI_ESP(d); - - qemu_free_irq(pci->esp.irq); -} - -static void esp_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = esp_pci_scsi_realize; - k->exit = esp_pci_scsi_uninit; - k->vendor_id = PCI_VENDOR_ID_AMD; - k->device_id = PCI_DEVICE_ID_AMD_SCSI; - k->revision = 0x10; - k->class_id = PCI_CLASS_STORAGE_SCSI; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->desc = "AMD Am53c974 PCscsi-PCI SCSI adapter"; - dc->reset = esp_pci_hard_reset; - dc->vmsd = &vmstate_esp_pci_scsi; -} - -static const TypeInfo esp_pci_info = { - .name = TYPE_AM53C974_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIESPState), - .class_init = esp_pci_class_init, -}; - -typedef struct { - PCIESPState pci; - eeprom_t *eeprom; -} DC390State; - -#define TYPE_DC390_DEVICE "dc390" -#define DC390(obj) \ - OBJECT_CHECK(DC390State, obj, TYPE_DC390_DEVICE) - -#define EE_ADAPT_SCSI_ID 64 -#define EE_MODE2 65 -#define EE_DELAY 66 -#define EE_TAG_CMD_NUM 67 -#define EE_ADAPT_OPTIONS 68 -#define EE_BOOT_SCSI_ID 69 -#define EE_BOOT_SCSI_LUN 70 -#define EE_CHKSUM1 126 -#define EE_CHKSUM2 127 - -#define EE_ADAPT_OPTION_F6_F8_AT_BOOT 0x01 -#define EE_ADAPT_OPTION_BOOT_FROM_CDROM 0x02 -#define EE_ADAPT_OPTION_INT13 0x04 -#define EE_ADAPT_OPTION_SCAM_SUPPORT 0x08 - - -static uint32_t dc390_read_config(PCIDevice *dev, uint32_t addr, int l) -{ - DC390State *pci = DC390(dev); - uint32_t val; - - val = pci_default_read_config(dev, addr, l); - - if (addr == 0x00 && l == 1) { - /* First byte of address space is AND-ed with EEPROM DO line */ - if (!eeprom93xx_read(pci->eeprom)) { - val &= ~0xff; - } - } - - return val; -} - -static void dc390_write_config(PCIDevice *dev, - uint32_t addr, uint32_t val, int l) -{ - DC390State *pci = DC390(dev); - if (addr == 0x80) { - /* EEPROM write */ - int eesk = val & 0x80 ? 1 : 0; - int eedi = val & 0x40 ? 1 : 0; - eeprom93xx_write(pci->eeprom, 1, eesk, eedi); - } else if (addr == 0xc0) { - /* EEPROM CS low */ - eeprom93xx_write(pci->eeprom, 0, 0, 0); - } else { - pci_default_write_config(dev, addr, val, l); - } -} - -static void dc390_scsi_realize(PCIDevice *dev, Error **errp) -{ - DC390State *pci = DC390(dev); - Error *err = NULL; - uint8_t *contents; - uint16_t chksum = 0; - int i; - - /* init base class */ - esp_pci_scsi_realize(dev, &err); - if (err) { - error_propagate(errp, err); - return; - } - - /* EEPROM */ - pci->eeprom = eeprom93xx_new(DEVICE(dev), 64); - - /* set default eeprom values */ - contents = (uint8_t *)eeprom93xx_data(pci->eeprom); - - for (i = 0; i < 16; i++) { - contents[i * 2] = 0x57; - contents[i * 2 + 1] = 0x00; - } - contents[EE_ADAPT_SCSI_ID] = 7; - contents[EE_MODE2] = 0x0f; - contents[EE_TAG_CMD_NUM] = 0x04; - contents[EE_ADAPT_OPTIONS] = EE_ADAPT_OPTION_F6_F8_AT_BOOT - | EE_ADAPT_OPTION_BOOT_FROM_CDROM - | EE_ADAPT_OPTION_INT13; - - /* update eeprom checksum */ - for (i = 0; i < EE_CHKSUM1; i += 2) { - chksum += contents[i] + (((uint16_t)contents[i + 1]) << 8); - } - chksum = 0x1234 - chksum; - contents[EE_CHKSUM1] = chksum & 0xff; - contents[EE_CHKSUM2] = chksum >> 8; -} - -static void dc390_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = dc390_scsi_realize; - k->config_read = dc390_read_config; - k->config_write = dc390_write_config; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->desc = "Tekram DC-390 SCSI adapter"; -} - -static const TypeInfo dc390_info = { - .name = "dc390", - .parent = TYPE_AM53C974_DEVICE, - .instance_size = sizeof(DC390State), - .class_init = dc390_class_init, -}; - -static void esp_pci_register_types(void) -{ - type_register_static(&esp_pci_info); - type_register_static(&dc390_info); -} - -type_init(esp_pci_register_types) diff --git a/qemu/hw/scsi/esp.c b/qemu/hw/scsi/esp.c deleted file mode 100644 index 8961be2f3..000000000 --- a/qemu/hw/scsi/esp.c +++ /dev/null @@ -1,745 +0,0 @@ -/* - * QEMU ESP/NCR53C9x emulation - * - * Copyright (c) 2005-2006 Fabrice Bellard - * Copyright (c) 2012 Herve Poussineau - * - * 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/sysbus.h" -#include "hw/scsi/esp.h" -#include "trace.h" -#include "qapi/error.h" -#include "qemu/log.h" - -/* - * On Sparc32, this is the ESP (NCR53C90) part of chip STP2000 (Master I/O), - * also produced as NCR89C100. See - * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt - * and - * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt - */ - -static void esp_raise_irq(ESPState *s) -{ - if (!(s->rregs[ESP_RSTAT] & STAT_INT)) { - s->rregs[ESP_RSTAT] |= STAT_INT; - qemu_irq_raise(s->irq); - trace_esp_raise_irq(); - } -} - -static void esp_lower_irq(ESPState *s) -{ - if (s->rregs[ESP_RSTAT] & STAT_INT) { - s->rregs[ESP_RSTAT] &= ~STAT_INT; - qemu_irq_lower(s->irq); - trace_esp_lower_irq(); - } -} - -void esp_dma_enable(ESPState *s, int irq, int level) -{ - if (level) { - s->dma_enabled = 1; - trace_esp_dma_enable(); - if (s->dma_cb) { - s->dma_cb(s); - s->dma_cb = NULL; - } - } else { - trace_esp_dma_disable(); - s->dma_enabled = 0; - } -} - -void esp_request_cancelled(SCSIRequest *req) -{ - ESPState *s = req->hba_private; - - if (req == s->current_req) { - scsi_req_unref(s->current_req); - s->current_req = NULL; - s->current_dev = NULL; - } -} - -static uint32_t get_cmd(ESPState *s, uint8_t *buf) -{ - uint32_t dmalen; - int target; - - target = s->wregs[ESP_WBUSID] & BUSID_DID; - if (s->dma) { - dmalen = s->rregs[ESP_TCLO]; - dmalen |= s->rregs[ESP_TCMID] << 8; - dmalen |= s->rregs[ESP_TCHI] << 16; - s->dma_memory_read(s->dma_opaque, buf, dmalen); - } else { - dmalen = s->ti_size; - memcpy(buf, s->ti_buf, dmalen); - buf[0] = buf[2] >> 5; - } - trace_esp_get_cmd(dmalen, target); - - s->ti_size = 0; - s->ti_rptr = 0; - s->ti_wptr = 0; - - if (s->current_req) { - /* Started a new command before the old one finished. Cancel it. */ - scsi_req_cancel(s->current_req); - s->async_len = 0; - } - - s->current_dev = scsi_device_find(&s->bus, 0, target, 0); - if (!s->current_dev) { - // No such drive - s->rregs[ESP_RSTAT] = 0; - s->rregs[ESP_RINTR] = INTR_DC; - s->rregs[ESP_RSEQ] = SEQ_0; - esp_raise_irq(s); - return 0; - } - return dmalen; -} - -static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid) -{ - int32_t datalen; - int lun; - SCSIDevice *current_lun; - - trace_esp_do_busid_cmd(busid); - lun = busid & 7; - current_lun = scsi_device_find(&s->bus, 0, s->current_dev->id, lun); - s->current_req = scsi_req_new(current_lun, 0, lun, buf, s); - datalen = scsi_req_enqueue(s->current_req); - s->ti_size = datalen; - if (datalen != 0) { - s->rregs[ESP_RSTAT] = STAT_TC; - s->dma_left = 0; - s->dma_counter = 0; - if (datalen > 0) { - s->rregs[ESP_RSTAT] |= STAT_DI; - } else { - s->rregs[ESP_RSTAT] |= STAT_DO; - } - scsi_req_continue(s->current_req); - } - s->rregs[ESP_RINTR] = INTR_BS | INTR_FC; - s->rregs[ESP_RSEQ] = SEQ_CD; - esp_raise_irq(s); -} - -static void do_cmd(ESPState *s, uint8_t *buf) -{ - uint8_t busid = buf[0]; - - do_busid_cmd(s, &buf[1], busid); -} - -static void handle_satn(ESPState *s) -{ - uint8_t buf[32]; - int len; - - if (s->dma && !s->dma_enabled) { - s->dma_cb = handle_satn; - return; - } - len = get_cmd(s, buf); - if (len) - do_cmd(s, buf); -} - -static void handle_s_without_atn(ESPState *s) -{ - uint8_t buf[32]; - int len; - - if (s->dma && !s->dma_enabled) { - s->dma_cb = handle_s_without_atn; - return; - } - len = get_cmd(s, buf); - if (len) { - do_busid_cmd(s, buf, 0); - } -} - -static void handle_satn_stop(ESPState *s) -{ - if (s->dma && !s->dma_enabled) { - s->dma_cb = handle_satn_stop; - return; - } - s->cmdlen = get_cmd(s, s->cmdbuf); - if (s->cmdlen) { - trace_esp_handle_satn_stop(s->cmdlen); - s->do_cmd = 1; - s->rregs[ESP_RSTAT] = STAT_TC | STAT_CD; - s->rregs[ESP_RINTR] = INTR_BS | INTR_FC; - s->rregs[ESP_RSEQ] = SEQ_CD; - esp_raise_irq(s); - } -} - -static void write_response(ESPState *s) -{ - trace_esp_write_response(s->status); - s->ti_buf[0] = s->status; - s->ti_buf[1] = 0; - if (s->dma) { - s->dma_memory_write(s->dma_opaque, s->ti_buf, 2); - s->rregs[ESP_RSTAT] = STAT_TC | STAT_ST; - s->rregs[ESP_RINTR] = INTR_BS | INTR_FC; - s->rregs[ESP_RSEQ] = SEQ_CD; - } else { - s->ti_size = 2; - s->ti_rptr = 0; - s->ti_wptr = 0; - s->rregs[ESP_RFLAGS] = 2; - } - esp_raise_irq(s); -} - -static void esp_dma_done(ESPState *s) -{ - s->rregs[ESP_RSTAT] |= STAT_TC; - s->rregs[ESP_RINTR] = INTR_BS; - s->rregs[ESP_RSEQ] = 0; - s->rregs[ESP_RFLAGS] = 0; - s->rregs[ESP_TCLO] = 0; - s->rregs[ESP_TCMID] = 0; - s->rregs[ESP_TCHI] = 0; - esp_raise_irq(s); -} - -static void esp_do_dma(ESPState *s) -{ - uint32_t len; - int to_device; - - to_device = (s->ti_size < 0); - len = s->dma_left; - if (s->do_cmd) { - trace_esp_do_dma(s->cmdlen, len); - s->dma_memory_read(s->dma_opaque, &s->cmdbuf[s->cmdlen], len); - s->ti_size = 0; - s->cmdlen = 0; - s->do_cmd = 0; - do_cmd(s, s->cmdbuf); - return; - } - if (s->async_len == 0) { - /* Defer until data is available. */ - return; - } - if (len > s->async_len) { - len = s->async_len; - } - if (to_device) { - s->dma_memory_read(s->dma_opaque, s->async_buf, len); - } else { - s->dma_memory_write(s->dma_opaque, s->async_buf, len); - } - s->dma_left -= len; - s->async_buf += len; - s->async_len -= len; - if (to_device) - s->ti_size += len; - else - s->ti_size -= len; - if (s->async_len == 0) { - scsi_req_continue(s->current_req); - /* If there is still data to be read from the device then - complete the DMA operation immediately. Otherwise defer - until the scsi layer has completed. */ - if (to_device || s->dma_left != 0 || s->ti_size == 0) { - return; - } - } - - /* Partially filled a scsi buffer. Complete immediately. */ - esp_dma_done(s); -} - -void esp_command_complete(SCSIRequest *req, uint32_t status, - size_t resid) -{ - ESPState *s = req->hba_private; - - trace_esp_command_complete(); - if (s->ti_size != 0) { - trace_esp_command_complete_unexpected(); - } - s->ti_size = 0; - s->dma_left = 0; - s->async_len = 0; - if (status) { - trace_esp_command_complete_fail(); - } - s->status = status; - s->rregs[ESP_RSTAT] = STAT_ST; - esp_dma_done(s); - if (s->current_req) { - scsi_req_unref(s->current_req); - s->current_req = NULL; - s->current_dev = NULL; - } -} - -void esp_transfer_data(SCSIRequest *req, uint32_t len) -{ - ESPState *s = req->hba_private; - - trace_esp_transfer_data(s->dma_left, s->ti_size); - s->async_len = len; - s->async_buf = scsi_req_get_buf(req); - if (s->dma_left) { - esp_do_dma(s); - } else if (s->dma_counter != 0 && s->ti_size <= 0) { - /* If this was the last part of a DMA transfer then the - completion interrupt is deferred to here. */ - esp_dma_done(s); - } -} - -static void handle_ti(ESPState *s) -{ - uint32_t dmalen, minlen; - - if (s->dma && !s->dma_enabled) { - s->dma_cb = handle_ti; - return; - } - - dmalen = s->rregs[ESP_TCLO]; - dmalen |= s->rregs[ESP_TCMID] << 8; - dmalen |= s->rregs[ESP_TCHI] << 16; - if (dmalen==0) { - dmalen=0x10000; - } - s->dma_counter = dmalen; - - if (s->do_cmd) - minlen = (dmalen < 32) ? dmalen : 32; - else if (s->ti_size < 0) - minlen = (dmalen < -s->ti_size) ? dmalen : -s->ti_size; - else - minlen = (dmalen < s->ti_size) ? dmalen : s->ti_size; - trace_esp_handle_ti(minlen); - if (s->dma) { - s->dma_left = minlen; - s->rregs[ESP_RSTAT] &= ~STAT_TC; - esp_do_dma(s); - } else if (s->do_cmd) { - trace_esp_handle_ti_cmd(s->cmdlen); - s->ti_size = 0; - s->cmdlen = 0; - s->do_cmd = 0; - do_cmd(s, s->cmdbuf); - return; - } -} - -void esp_hard_reset(ESPState *s) -{ - memset(s->rregs, 0, ESP_REGS); - memset(s->wregs, 0, ESP_REGS); - s->tchi_written = 0; - s->ti_size = 0; - s->ti_rptr = 0; - s->ti_wptr = 0; - s->dma = 0; - s->do_cmd = 0; - s->dma_cb = NULL; - - s->rregs[ESP_CFG1] = 7; -} - -static void esp_soft_reset(ESPState *s) -{ - qemu_irq_lower(s->irq); - esp_hard_reset(s); -} - -static void parent_esp_reset(ESPState *s, int irq, int level) -{ - if (level) { - esp_soft_reset(s); - } -} - -uint64_t esp_reg_read(ESPState *s, uint32_t saddr) -{ - uint32_t old_val; - - trace_esp_mem_readb(saddr, s->rregs[saddr]); - switch (saddr) { - case ESP_FIFO: - if (s->ti_size > 0) { - s->ti_size--; - if ((s->rregs[ESP_RSTAT] & STAT_PIO_MASK) == 0) { - /* Data out. */ - qemu_log_mask(LOG_UNIMP, - "esp: PIO data read not implemented\n"); - s->rregs[ESP_FIFO] = 0; - } else { - s->rregs[ESP_FIFO] = s->ti_buf[s->ti_rptr++]; - } - esp_raise_irq(s); - } - if (s->ti_size == 0) { - s->ti_rptr = 0; - s->ti_wptr = 0; - } - break; - case ESP_RINTR: - /* Clear sequence step, interrupt register and all status bits - except TC */ - old_val = s->rregs[ESP_RINTR]; - s->rregs[ESP_RINTR] = 0; - s->rregs[ESP_RSTAT] &= ~STAT_TC; - s->rregs[ESP_RSEQ] = SEQ_CD; - esp_lower_irq(s); - - return old_val; - case ESP_TCHI: - /* Return the unique id if the value has never been written */ - if (!s->tchi_written) { - return s->chip_id; - } - default: - break; - } - return s->rregs[saddr]; -} - -void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) -{ - trace_esp_mem_writeb(saddr, s->wregs[saddr], val); - switch (saddr) { - case ESP_TCHI: - s->tchi_written = true; - /* fall through */ - case ESP_TCLO: - case ESP_TCMID: - s->rregs[ESP_RSTAT] &= ~STAT_TC; - break; - case ESP_FIFO: - if (s->do_cmd) { - s->cmdbuf[s->cmdlen++] = val & 0xff; - } else if (s->ti_size == TI_BUFSZ - 1) { - trace_esp_error_fifo_overrun(); - } else { - s->ti_size++; - s->ti_buf[s->ti_wptr++] = val & 0xff; - } - break; - case ESP_CMD: - s->rregs[saddr] = val; - if (val & CMD_DMA) { - s->dma = 1; - /* Reload DMA counter. */ - s->rregs[ESP_TCLO] = s->wregs[ESP_TCLO]; - s->rregs[ESP_TCMID] = s->wregs[ESP_TCMID]; - s->rregs[ESP_TCHI] = s->wregs[ESP_TCHI]; - } else { - s->dma = 0; - } - switch(val & CMD_CMD) { - case CMD_NOP: - trace_esp_mem_writeb_cmd_nop(val); - break; - case CMD_FLUSH: - trace_esp_mem_writeb_cmd_flush(val); - //s->ti_size = 0; - s->rregs[ESP_RINTR] = INTR_FC; - s->rregs[ESP_RSEQ] = 0; - s->rregs[ESP_RFLAGS] = 0; - break; - case CMD_RESET: - trace_esp_mem_writeb_cmd_reset(val); - esp_soft_reset(s); - break; - case CMD_BUSRESET: - trace_esp_mem_writeb_cmd_bus_reset(val); - s->rregs[ESP_RINTR] = INTR_RST; - if (!(s->wregs[ESP_CFG1] & CFG1_RESREPT)) { - esp_raise_irq(s); - } - break; - case CMD_TI: - handle_ti(s); - break; - case CMD_ICCS: - trace_esp_mem_writeb_cmd_iccs(val); - write_response(s); - s->rregs[ESP_RINTR] = INTR_FC; - s->rregs[ESP_RSTAT] |= STAT_MI; - break; - case CMD_MSGACC: - trace_esp_mem_writeb_cmd_msgacc(val); - s->rregs[ESP_RINTR] = INTR_DC; - s->rregs[ESP_RSEQ] = 0; - s->rregs[ESP_RFLAGS] = 0; - esp_raise_irq(s); - break; - case CMD_PAD: - trace_esp_mem_writeb_cmd_pad(val); - s->rregs[ESP_RSTAT] = STAT_TC; - s->rregs[ESP_RINTR] = INTR_FC; - s->rregs[ESP_RSEQ] = 0; - break; - case CMD_SATN: - trace_esp_mem_writeb_cmd_satn(val); - break; - case CMD_RSTATN: - trace_esp_mem_writeb_cmd_rstatn(val); - break; - case CMD_SEL: - trace_esp_mem_writeb_cmd_sel(val); - handle_s_without_atn(s); - break; - case CMD_SELATN: - trace_esp_mem_writeb_cmd_selatn(val); - handle_satn(s); - break; - case CMD_SELATNS: - trace_esp_mem_writeb_cmd_selatns(val); - handle_satn_stop(s); - break; - case CMD_ENSEL: - trace_esp_mem_writeb_cmd_ensel(val); - s->rregs[ESP_RINTR] = 0; - break; - case CMD_DISSEL: - trace_esp_mem_writeb_cmd_dissel(val); - s->rregs[ESP_RINTR] = 0; - esp_raise_irq(s); - break; - default: - trace_esp_error_unhandled_command(val); - break; - } - break; - case ESP_WBUSID ... ESP_WSYNO: - break; - case ESP_CFG1: - case ESP_CFG2: case ESP_CFG3: - case ESP_RES3: case ESP_RES4: - s->rregs[saddr] = val; - break; - case ESP_WCCF ... ESP_WTEST: - break; - default: - trace_esp_error_invalid_write(val, saddr); - return; - } - s->wregs[saddr] = val; -} - -static bool esp_mem_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) -{ - return (size == 1) || (is_write && size == 4); -} - -const VMStateDescription vmstate_esp = { - .name ="esp", - .version_id = 3, - .minimum_version_id = 3, - .fields = (VMStateField[]) { - VMSTATE_BUFFER(rregs, ESPState), - VMSTATE_BUFFER(wregs, ESPState), - VMSTATE_INT32(ti_size, ESPState), - VMSTATE_UINT32(ti_rptr, ESPState), - VMSTATE_UINT32(ti_wptr, ESPState), - VMSTATE_BUFFER(ti_buf, ESPState), - VMSTATE_UINT32(status, ESPState), - VMSTATE_UINT32(dma, ESPState), - VMSTATE_BUFFER(cmdbuf, ESPState), - VMSTATE_UINT32(cmdlen, ESPState), - VMSTATE_UINT32(do_cmd, ESPState), - VMSTATE_UINT32(dma_left, ESPState), - VMSTATE_END_OF_LIST() - } -}; - -#define TYPE_ESP "esp" -#define ESP(obj) OBJECT_CHECK(SysBusESPState, (obj), TYPE_ESP) - -typedef struct { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - uint32_t it_shift; - ESPState esp; -} SysBusESPState; - -static void sysbus_esp_mem_write(void *opaque, hwaddr addr, - uint64_t val, unsigned int size) -{ - SysBusESPState *sysbus = opaque; - uint32_t saddr; - - saddr = addr >> sysbus->it_shift; - esp_reg_write(&sysbus->esp, saddr, val); -} - -static uint64_t sysbus_esp_mem_read(void *opaque, hwaddr addr, - unsigned int size) -{ - SysBusESPState *sysbus = opaque; - uint32_t saddr; - - saddr = addr >> sysbus->it_shift; - return esp_reg_read(&sysbus->esp, saddr); -} - -static const MemoryRegionOps sysbus_esp_mem_ops = { - .read = sysbus_esp_mem_read, - .write = sysbus_esp_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid.accepts = esp_mem_accepts, -}; - -void esp_init(hwaddr espaddr, int it_shift, - ESPDMAMemoryReadWriteFunc dma_memory_read, - ESPDMAMemoryReadWriteFunc dma_memory_write, - void *dma_opaque, qemu_irq irq, qemu_irq *reset, - qemu_irq *dma_enable) -{ - DeviceState *dev; - SysBusDevice *s; - SysBusESPState *sysbus; - ESPState *esp; - - dev = qdev_create(NULL, TYPE_ESP); - sysbus = ESP(dev); - esp = &sysbus->esp; - esp->dma_memory_read = dma_memory_read; - esp->dma_memory_write = dma_memory_write; - esp->dma_opaque = dma_opaque; - sysbus->it_shift = it_shift; - /* XXX for now until rc4030 has been changed to use DMA enable signal */ - esp->dma_enabled = 1; - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, irq); - sysbus_mmio_map(s, 0, espaddr); - *reset = qdev_get_gpio_in(dev, 0); - *dma_enable = qdev_get_gpio_in(dev, 1); -} - -static const struct SCSIBusInfo esp_scsi_info = { - .tcq = false, - .max_target = ESP_MAX_DEVS, - .max_lun = 7, - - .transfer_data = esp_transfer_data, - .complete = esp_command_complete, - .cancel = esp_request_cancelled -}; - -static void sysbus_esp_gpio_demux(void *opaque, int irq, int level) -{ - SysBusESPState *sysbus = ESP(opaque); - ESPState *s = &sysbus->esp; - - switch (irq) { - case 0: - parent_esp_reset(s, irq, level); - break; - case 1: - esp_dma_enable(opaque, irq, level); - break; - } -} - -static void sysbus_esp_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - SysBusESPState *sysbus = ESP(dev); - ESPState *s = &sysbus->esp; - Error *err = NULL; - - sysbus_init_irq(sbd, &s->irq); - assert(sysbus->it_shift != -1); - - s->chip_id = TCHI_FAS100A; - memory_region_init_io(&sysbus->iomem, OBJECT(sysbus), &sysbus_esp_mem_ops, - sysbus, "esp", ESP_REGS << sysbus->it_shift); - sysbus_init_mmio(sbd, &sysbus->iomem); - - qdev_init_gpio_in(dev, sysbus_esp_gpio_demux, 2); - - scsi_bus_new(&s->bus, sizeof(s->bus), dev, &esp_scsi_info, NULL); - scsi_bus_legacy_handle_cmdline(&s->bus, &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } -} - -static void sysbus_esp_hard_reset(DeviceState *dev) -{ - SysBusESPState *sysbus = ESP(dev); - esp_hard_reset(&sysbus->esp); -} - -static const VMStateDescription vmstate_sysbus_esp_scsi = { - .name = "sysbusespscsi", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(esp, SysBusESPState, 0, vmstate_esp, ESPState), - VMSTATE_END_OF_LIST() - } -}; - -static void sysbus_esp_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = sysbus_esp_realize; - dc->reset = sysbus_esp_hard_reset; - dc->vmsd = &vmstate_sysbus_esp_scsi; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo sysbus_esp_info = { - .name = TYPE_ESP, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysBusESPState), - .class_init = sysbus_esp_class_init, -}; - -static void esp_register_types(void) -{ - type_register_static(&sysbus_esp_info); -} - -type_init(esp_register_types) diff --git a/qemu/hw/scsi/lsi53c895a.c b/qemu/hw/scsi/lsi53c895a.c deleted file mode 100644 index df205cdaf..000000000 --- a/qemu/hw/scsi/lsi53c895a.c +++ /dev/null @@ -1,2163 +0,0 @@ -/* - * QEMU LSI53C895A SCSI Host Bus Adapter emulation - * - * Copyright (c) 2006 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the LGPL. - */ - -/* Note: - * LSI53C810 emulation is incorrect, in the sense that it supports - * features added in later evolutions. This should not be a problem, - * as well-behaved operating systems will not try to use them. - */ - -#include "qemu/osdep.h" - -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "hw/scsi/scsi.h" -#include "sysemu/dma.h" - -//#define DEBUG_LSI -//#define DEBUG_LSI_REG - -#ifdef DEBUG_LSI -#define DPRINTF(fmt, ...) \ -do { printf("lsi_scsi: " fmt , ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0) -#endif - -#define LSI_MAX_DEVS 7 - -#define LSI_SCNTL0_TRG 0x01 -#define LSI_SCNTL0_AAP 0x02 -#define LSI_SCNTL0_EPC 0x08 -#define LSI_SCNTL0_WATN 0x10 -#define LSI_SCNTL0_START 0x20 - -#define LSI_SCNTL1_SST 0x01 -#define LSI_SCNTL1_IARB 0x02 -#define LSI_SCNTL1_AESP 0x04 -#define LSI_SCNTL1_RST 0x08 -#define LSI_SCNTL1_CON 0x10 -#define LSI_SCNTL1_DHP 0x20 -#define LSI_SCNTL1_ADB 0x40 -#define LSI_SCNTL1_EXC 0x80 - -#define LSI_SCNTL2_WSR 0x01 -#define LSI_SCNTL2_VUE0 0x02 -#define LSI_SCNTL2_VUE1 0x04 -#define LSI_SCNTL2_WSS 0x08 -#define LSI_SCNTL2_SLPHBEN 0x10 -#define LSI_SCNTL2_SLPMD 0x20 -#define LSI_SCNTL2_CHM 0x40 -#define LSI_SCNTL2_SDU 0x80 - -#define LSI_ISTAT0_DIP 0x01 -#define LSI_ISTAT0_SIP 0x02 -#define LSI_ISTAT0_INTF 0x04 -#define LSI_ISTAT0_CON 0x08 -#define LSI_ISTAT0_SEM 0x10 -#define LSI_ISTAT0_SIGP 0x20 -#define LSI_ISTAT0_SRST 0x40 -#define LSI_ISTAT0_ABRT 0x80 - -#define LSI_ISTAT1_SI 0x01 -#define LSI_ISTAT1_SRUN 0x02 -#define LSI_ISTAT1_FLSH 0x04 - -#define LSI_SSTAT0_SDP0 0x01 -#define LSI_SSTAT0_RST 0x02 -#define LSI_SSTAT0_WOA 0x04 -#define LSI_SSTAT0_LOA 0x08 -#define LSI_SSTAT0_AIP 0x10 -#define LSI_SSTAT0_OLF 0x20 -#define LSI_SSTAT0_ORF 0x40 -#define LSI_SSTAT0_ILF 0x80 - -#define LSI_SIST0_PAR 0x01 -#define LSI_SIST0_RST 0x02 -#define LSI_SIST0_UDC 0x04 -#define LSI_SIST0_SGE 0x08 -#define LSI_SIST0_RSL 0x10 -#define LSI_SIST0_SEL 0x20 -#define LSI_SIST0_CMP 0x40 -#define LSI_SIST0_MA 0x80 - -#define LSI_SIST1_HTH 0x01 -#define LSI_SIST1_GEN 0x02 -#define LSI_SIST1_STO 0x04 -#define LSI_SIST1_SBMC 0x10 - -#define LSI_SOCL_IO 0x01 -#define LSI_SOCL_CD 0x02 -#define LSI_SOCL_MSG 0x04 -#define LSI_SOCL_ATN 0x08 -#define LSI_SOCL_SEL 0x10 -#define LSI_SOCL_BSY 0x20 -#define LSI_SOCL_ACK 0x40 -#define LSI_SOCL_REQ 0x80 - -#define LSI_DSTAT_IID 0x01 -#define LSI_DSTAT_SIR 0x04 -#define LSI_DSTAT_SSI 0x08 -#define LSI_DSTAT_ABRT 0x10 -#define LSI_DSTAT_BF 0x20 -#define LSI_DSTAT_MDPE 0x40 -#define LSI_DSTAT_DFE 0x80 - -#define LSI_DCNTL_COM 0x01 -#define LSI_DCNTL_IRQD 0x02 -#define LSI_DCNTL_STD 0x04 -#define LSI_DCNTL_IRQM 0x08 -#define LSI_DCNTL_SSM 0x10 -#define LSI_DCNTL_PFEN 0x20 -#define LSI_DCNTL_PFF 0x40 -#define LSI_DCNTL_CLSE 0x80 - -#define LSI_DMODE_MAN 0x01 -#define LSI_DMODE_BOF 0x02 -#define LSI_DMODE_ERMP 0x04 -#define LSI_DMODE_ERL 0x08 -#define LSI_DMODE_DIOM 0x10 -#define LSI_DMODE_SIOM 0x20 - -#define LSI_CTEST2_DACK 0x01 -#define LSI_CTEST2_DREQ 0x02 -#define LSI_CTEST2_TEOP 0x04 -#define LSI_CTEST2_PCICIE 0x08 -#define LSI_CTEST2_CM 0x10 -#define LSI_CTEST2_CIO 0x20 -#define LSI_CTEST2_SIGP 0x40 -#define LSI_CTEST2_DDIR 0x80 - -#define LSI_CTEST5_BL2 0x04 -#define LSI_CTEST5_DDIR 0x08 -#define LSI_CTEST5_MASR 0x10 -#define LSI_CTEST5_DFSN 0x20 -#define LSI_CTEST5_BBCK 0x40 -#define LSI_CTEST5_ADCK 0x80 - -#define LSI_CCNTL0_DILS 0x01 -#define LSI_CCNTL0_DISFC 0x10 -#define LSI_CCNTL0_ENNDJ 0x20 -#define LSI_CCNTL0_PMJCTL 0x40 -#define LSI_CCNTL0_ENPMJ 0x80 - -#define LSI_CCNTL1_EN64DBMV 0x01 -#define LSI_CCNTL1_EN64TIBMV 0x02 -#define LSI_CCNTL1_64TIMOD 0x04 -#define LSI_CCNTL1_DDAC 0x08 -#define LSI_CCNTL1_ZMOD 0x80 - -/* Enable Response to Reselection */ -#define LSI_SCID_RRE 0x60 - -#define LSI_CCNTL1_40BIT (LSI_CCNTL1_EN64TIBMV|LSI_CCNTL1_64TIMOD) - -#define PHASE_DO 0 -#define PHASE_DI 1 -#define PHASE_CMD 2 -#define PHASE_ST 3 -#define PHASE_MO 6 -#define PHASE_MI 7 -#define PHASE_MASK 7 - -/* Maximum length of MSG IN data. */ -#define LSI_MAX_MSGIN_LEN 8 - -/* Flag set if this is a tagged command. */ -#define LSI_TAG_VALID (1 << 16) - -typedef struct lsi_request { - SCSIRequest *req; - uint32_t tag; - uint32_t dma_len; - uint8_t *dma_buf; - uint32_t pending; - int out; - QTAILQ_ENTRY(lsi_request) next; -} lsi_request; - -typedef struct { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - MemoryRegion mmio_io; - MemoryRegion ram_io; - MemoryRegion io_io; - - int carry; /* ??? Should this be an a visible register somewhere? */ - int status; - /* Action to take at the end of a MSG IN phase. - 0 = COMMAND, 1 = disconnect, 2 = DATA OUT, 3 = DATA IN. */ - int msg_action; - int msg_len; - uint8_t msg[LSI_MAX_MSGIN_LEN]; - /* 0 if SCRIPTS are running or stopped. - * 1 if a Wait Reselect instruction has been issued. - * 2 if processing DMA from lsi_execute_script. - * 3 if a DMA operation is in progress. */ - int waiting; - SCSIBus bus; - int current_lun; - /* The tag is a combination of the device ID and the SCSI tag. */ - uint32_t select_tag; - int command_complete; - QTAILQ_HEAD(, lsi_request) queue; - lsi_request *current; - - uint32_t dsa; - uint32_t temp; - uint32_t dnad; - uint32_t dbc; - uint8_t istat0; - uint8_t istat1; - uint8_t dcmd; - uint8_t dstat; - uint8_t dien; - uint8_t sist0; - uint8_t sist1; - uint8_t sien0; - uint8_t sien1; - uint8_t mbox0; - uint8_t mbox1; - uint8_t dfifo; - uint8_t ctest2; - uint8_t ctest3; - uint8_t ctest4; - uint8_t ctest5; - uint8_t ccntl0; - uint8_t ccntl1; - uint32_t dsp; - uint32_t dsps; - uint8_t dmode; - uint8_t dcntl; - uint8_t scntl0; - uint8_t scntl1; - uint8_t scntl2; - uint8_t scntl3; - uint8_t sstat0; - uint8_t sstat1; - uint8_t scid; - uint8_t sxfer; - uint8_t socl; - uint8_t sdid; - uint8_t ssid; - uint8_t sfbr; - uint8_t stest1; - uint8_t stest2; - uint8_t stest3; - uint8_t sidl; - uint8_t stime0; - uint8_t respid0; - uint8_t respid1; - uint32_t mmrs; - uint32_t mmws; - uint32_t sfs; - uint32_t drs; - uint32_t sbms; - uint32_t dbms; - uint32_t dnad64; - uint32_t pmjad1; - uint32_t pmjad2; - uint32_t rbc; - uint32_t ua; - uint32_t ia; - uint32_t sbc; - uint32_t csbc; - uint32_t scratch[18]; /* SCRATCHA-SCRATCHR */ - uint8_t sbr; - uint32_t adder; - - /* Script ram is stored as 32-bit words in host byteorder. */ - uint32_t script_ram[2048]; -} LSIState; - -#define TYPE_LSI53C810 "lsi53c810" -#define TYPE_LSI53C895A "lsi53c895a" - -#define LSI53C895A(obj) \ - OBJECT_CHECK(LSIState, (obj), TYPE_LSI53C895A) - -static inline int lsi_irq_on_rsl(LSIState *s) -{ - return (s->sien0 & LSI_SIST0_RSL) && (s->scid & LSI_SCID_RRE); -} - -static void lsi_soft_reset(LSIState *s) -{ - DPRINTF("Reset\n"); - s->carry = 0; - - s->msg_action = 0; - s->msg_len = 0; - s->waiting = 0; - s->dsa = 0; - s->dnad = 0; - s->dbc = 0; - s->temp = 0; - memset(s->scratch, 0, sizeof(s->scratch)); - s->istat0 = 0; - s->istat1 = 0; - s->dcmd = 0x40; - s->dstat = LSI_DSTAT_DFE; - s->dien = 0; - s->sist0 = 0; - s->sist1 = 0; - s->sien0 = 0; - s->sien1 = 0; - s->mbox0 = 0; - s->mbox1 = 0; - s->dfifo = 0; - s->ctest2 = LSI_CTEST2_DACK; - s->ctest3 = 0; - s->ctest4 = 0; - s->ctest5 = 0; - s->ccntl0 = 0; - s->ccntl1 = 0; - s->dsp = 0; - s->dsps = 0; - s->dmode = 0; - s->dcntl = 0; - s->scntl0 = 0xc0; - s->scntl1 = 0; - s->scntl2 = 0; - s->scntl3 = 0; - s->sstat0 = 0; - s->sstat1 = 0; - s->scid = 7; - s->sxfer = 0; - s->socl = 0; - s->sdid = 0; - s->ssid = 0; - s->stest1 = 0; - s->stest2 = 0; - s->stest3 = 0; - s->sidl = 0; - s->stime0 = 0; - s->respid0 = 0x80; - s->respid1 = 0; - s->mmrs = 0; - s->mmws = 0; - s->sfs = 0; - s->drs = 0; - s->sbms = 0; - s->dbms = 0; - s->dnad64 = 0; - s->pmjad1 = 0; - s->pmjad2 = 0; - s->rbc = 0; - s->ua = 0; - s->ia = 0; - s->sbc = 0; - s->csbc = 0; - s->sbr = 0; - assert(QTAILQ_EMPTY(&s->queue)); - assert(!s->current); -} - -static int lsi_dma_40bit(LSIState *s) -{ - if ((s->ccntl1 & LSI_CCNTL1_40BIT) == LSI_CCNTL1_40BIT) - return 1; - return 0; -} - -static int lsi_dma_ti64bit(LSIState *s) -{ - if ((s->ccntl1 & LSI_CCNTL1_EN64TIBMV) == LSI_CCNTL1_EN64TIBMV) - return 1; - return 0; -} - -static int lsi_dma_64bit(LSIState *s) -{ - if ((s->ccntl1 & LSI_CCNTL1_EN64DBMV) == LSI_CCNTL1_EN64DBMV) - return 1; - return 0; -} - -static uint8_t lsi_reg_readb(LSIState *s, int offset); -static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val); -static void lsi_execute_script(LSIState *s); -static void lsi_reselect(LSIState *s, lsi_request *p); - -static inline uint32_t read_dword(LSIState *s, uint32_t addr) -{ - uint32_t buf; - - pci_dma_read(PCI_DEVICE(s), addr, &buf, 4); - return cpu_to_le32(buf); -} - -static void lsi_stop_script(LSIState *s) -{ - s->istat1 &= ~LSI_ISTAT1_SRUN; -} - -static void lsi_update_irq(LSIState *s) -{ - PCIDevice *d = PCI_DEVICE(s); - int level; - static int last_level; - lsi_request *p; - - /* It's unclear whether the DIP/SIP bits should be cleared when the - Interrupt Status Registers are cleared or when istat0 is read. - We currently do the formwer, which seems to work. */ - level = 0; - if (s->dstat) { - if (s->dstat & s->dien) - level = 1; - s->istat0 |= LSI_ISTAT0_DIP; - } else { - s->istat0 &= ~LSI_ISTAT0_DIP; - } - - if (s->sist0 || s->sist1) { - if ((s->sist0 & s->sien0) || (s->sist1 & s->sien1)) - level = 1; - s->istat0 |= LSI_ISTAT0_SIP; - } else { - s->istat0 &= ~LSI_ISTAT0_SIP; - } - if (s->istat0 & LSI_ISTAT0_INTF) - level = 1; - - if (level != last_level) { - DPRINTF("Update IRQ level %d dstat %02x sist %02x%02x\n", - level, s->dstat, s->sist1, s->sist0); - last_level = level; - } - pci_set_irq(d, level); - - if (!level && lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON)) { - DPRINTF("Handled IRQs & disconnected, looking for pending " - "processes\n"); - QTAILQ_FOREACH(p, &s->queue, next) { - if (p->pending) { - lsi_reselect(s, p); - break; - } - } - } -} - -/* Stop SCRIPTS execution and raise a SCSI interrupt. */ -static void lsi_script_scsi_interrupt(LSIState *s, int stat0, int stat1) -{ - uint32_t mask0; - uint32_t mask1; - - DPRINTF("SCSI Interrupt 0x%02x%02x prev 0x%02x%02x\n", - stat1, stat0, s->sist1, s->sist0); - s->sist0 |= stat0; - s->sist1 |= stat1; - /* Stop processor on fatal or unmasked interrupt. As a special hack - we don't stop processing when raising STO. Instead continue - execution and stop at the next insn that accesses the SCSI bus. */ - mask0 = s->sien0 | ~(LSI_SIST0_CMP | LSI_SIST0_SEL | LSI_SIST0_RSL); - mask1 = s->sien1 | ~(LSI_SIST1_GEN | LSI_SIST1_HTH); - mask1 &= ~LSI_SIST1_STO; - if (s->sist0 & mask0 || s->sist1 & mask1) { - lsi_stop_script(s); - } - lsi_update_irq(s); -} - -/* Stop SCRIPTS execution and raise a DMA interrupt. */ -static void lsi_script_dma_interrupt(LSIState *s, int stat) -{ - DPRINTF("DMA Interrupt 0x%x prev 0x%x\n", stat, s->dstat); - s->dstat |= stat; - lsi_update_irq(s); - lsi_stop_script(s); -} - -static inline void lsi_set_phase(LSIState *s, int phase) -{ - s->sstat1 = (s->sstat1 & ~PHASE_MASK) | phase; -} - -static void lsi_bad_phase(LSIState *s, int out, int new_phase) -{ - /* Trigger a phase mismatch. */ - if (s->ccntl0 & LSI_CCNTL0_ENPMJ) { - if ((s->ccntl0 & LSI_CCNTL0_PMJCTL)) { - s->dsp = out ? s->pmjad1 : s->pmjad2; - } else { - s->dsp = (s->scntl2 & LSI_SCNTL2_WSR ? s->pmjad2 : s->pmjad1); - } - DPRINTF("Data phase mismatch jump to %08x\n", s->dsp); - } else { - DPRINTF("Phase mismatch interrupt\n"); - lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0); - lsi_stop_script(s); - } - lsi_set_phase(s, new_phase); -} - - -/* Resume SCRIPTS execution after a DMA operation. */ -static void lsi_resume_script(LSIState *s) -{ - if (s->waiting != 2) { - s->waiting = 0; - lsi_execute_script(s); - } else { - s->waiting = 0; - } -} - -static void lsi_disconnect(LSIState *s) -{ - s->scntl1 &= ~LSI_SCNTL1_CON; - s->sstat1 &= ~PHASE_MASK; -} - -static void lsi_bad_selection(LSIState *s, uint32_t id) -{ - DPRINTF("Selected absent target %d\n", id); - lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO); - lsi_disconnect(s); -} - -/* Initiate a SCSI layer data transfer. */ -static void lsi_do_dma(LSIState *s, int out) -{ - PCIDevice *pci_dev; - uint32_t count; - dma_addr_t addr; - SCSIDevice *dev; - - assert(s->current); - if (!s->current->dma_len) { - /* Wait until data is available. */ - DPRINTF("DMA no data available\n"); - return; - } - - pci_dev = PCI_DEVICE(s); - dev = s->current->req->dev; - assert(dev); - - count = s->dbc; - if (count > s->current->dma_len) - count = s->current->dma_len; - - addr = s->dnad; - /* both 40 and Table Indirect 64-bit DMAs store upper bits in dnad64 */ - if (lsi_dma_40bit(s) || lsi_dma_ti64bit(s)) - addr |= ((uint64_t)s->dnad64 << 32); - else if (s->dbms) - addr |= ((uint64_t)s->dbms << 32); - else if (s->sbms) - addr |= ((uint64_t)s->sbms << 32); - - DPRINTF("DMA addr=0x" DMA_ADDR_FMT " len=%d\n", addr, count); - s->csbc += count; - s->dnad += count; - s->dbc -= count; - if (s->current->dma_buf == NULL) { - s->current->dma_buf = scsi_req_get_buf(s->current->req); - } - /* ??? Set SFBR to first data byte. */ - if (out) { - pci_dma_read(pci_dev, addr, s->current->dma_buf, count); - } else { - pci_dma_write(pci_dev, addr, s->current->dma_buf, count); - } - s->current->dma_len -= count; - if (s->current->dma_len == 0) { - s->current->dma_buf = NULL; - scsi_req_continue(s->current->req); - } else { - s->current->dma_buf += count; - lsi_resume_script(s); - } -} - - -/* Add a command to the queue. */ -static void lsi_queue_command(LSIState *s) -{ - lsi_request *p = s->current; - - DPRINTF("Queueing tag=0x%x\n", p->tag); - assert(s->current != NULL); - assert(s->current->dma_len == 0); - QTAILQ_INSERT_TAIL(&s->queue, s->current, next); - s->current = NULL; - - p->pending = 0; - p->out = (s->sstat1 & PHASE_MASK) == PHASE_DO; -} - -/* Queue a byte for a MSG IN phase. */ -static void lsi_add_msg_byte(LSIState *s, uint8_t data) -{ - if (s->msg_len >= LSI_MAX_MSGIN_LEN) { - BADF("MSG IN data too long\n"); - } else { - DPRINTF("MSG IN 0x%02x\n", data); - s->msg[s->msg_len++] = data; - } -} - -/* Perform reselection to continue a command. */ -static void lsi_reselect(LSIState *s, lsi_request *p) -{ - int id; - - assert(s->current == NULL); - QTAILQ_REMOVE(&s->queue, p, next); - s->current = p; - - id = (p->tag >> 8) & 0xf; - s->ssid = id | 0x80; - /* LSI53C700 Family Compatibility, see LSI53C895A 4-73 */ - if (!(s->dcntl & LSI_DCNTL_COM)) { - s->sfbr = 1 << (id & 0x7); - } - DPRINTF("Reselected target %d\n", id); - s->scntl1 |= LSI_SCNTL1_CON; - lsi_set_phase(s, PHASE_MI); - s->msg_action = p->out ? 2 : 3; - s->current->dma_len = p->pending; - lsi_add_msg_byte(s, 0x80); - if (s->current->tag & LSI_TAG_VALID) { - lsi_add_msg_byte(s, 0x20); - lsi_add_msg_byte(s, p->tag & 0xff); - } - - if (lsi_irq_on_rsl(s)) { - lsi_script_scsi_interrupt(s, LSI_SIST0_RSL, 0); - } -} - -static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag) -{ - lsi_request *p; - - QTAILQ_FOREACH(p, &s->queue, next) { - if (p->tag == tag) { - return p; - } - } - - return NULL; -} - -static void lsi_request_free(LSIState *s, lsi_request *p) -{ - if (p == s->current) { - s->current = NULL; - } else { - QTAILQ_REMOVE(&s->queue, p, next); - } - g_free(p); -} - -static void lsi_request_cancelled(SCSIRequest *req) -{ - LSIState *s = LSI53C895A(req->bus->qbus.parent); - lsi_request *p = req->hba_private; - - req->hba_private = NULL; - lsi_request_free(s, p); - scsi_req_unref(req); -} - -/* Record that data is available for a queued command. Returns zero if - the device was reselected, nonzero if the IO is deferred. */ -static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len) -{ - lsi_request *p = req->hba_private; - - if (p->pending) { - BADF("Multiple IO pending for request %p\n", p); - } - p->pending = len; - /* Reselect if waiting for it, or if reselection triggers an IRQ - and the bus is free. - Since no interrupt stacking is implemented in the emulation, it - is also required that there are no pending interrupts waiting - for service from the device driver. */ - if (s->waiting == 1 || - (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON) && - !(s->istat0 & (LSI_ISTAT0_SIP | LSI_ISTAT0_DIP)))) { - /* Reselect device. */ - lsi_reselect(s, p); - return 0; - } else { - DPRINTF("Queueing IO tag=0x%x\n", p->tag); - p->pending = len; - return 1; - } -} - - /* Callback to indicate that the SCSI layer has completed a command. */ -static void lsi_command_complete(SCSIRequest *req, uint32_t status, size_t resid) -{ - LSIState *s = LSI53C895A(req->bus->qbus.parent); - int out; - - out = (s->sstat1 & PHASE_MASK) == PHASE_DO; - DPRINTF("Command complete status=%d\n", (int)status); - s->status = status; - s->command_complete = 2; - if (s->waiting && s->dbc != 0) { - /* Raise phase mismatch for short transfers. */ - lsi_bad_phase(s, out, PHASE_ST); - } else { - lsi_set_phase(s, PHASE_ST); - } - - if (req->hba_private == s->current) { - req->hba_private = NULL; - lsi_request_free(s, s->current); - scsi_req_unref(req); - } - lsi_resume_script(s); -} - - /* Callback to indicate that the SCSI layer has completed a transfer. */ -static void lsi_transfer_data(SCSIRequest *req, uint32_t len) -{ - LSIState *s = LSI53C895A(req->bus->qbus.parent); - int out; - - assert(req->hba_private); - if (s->waiting == 1 || req->hba_private != s->current || - (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) { - if (lsi_queue_req(s, req, len)) { - return; - } - } - - out = (s->sstat1 & PHASE_MASK) == PHASE_DO; - - /* host adapter (re)connected */ - DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, len); - s->current->dma_len = len; - s->command_complete = 1; - if (s->waiting) { - if (s->waiting == 1 || s->dbc == 0) { - lsi_resume_script(s); - } else { - lsi_do_dma(s, out); - } - } -} - -static void lsi_do_command(LSIState *s) -{ - SCSIDevice *dev; - uint8_t buf[16]; - uint32_t id; - int n; - - DPRINTF("Send command len=%d\n", s->dbc); - if (s->dbc > 16) - s->dbc = 16; - pci_dma_read(PCI_DEVICE(s), s->dnad, buf, s->dbc); - s->sfbr = buf[0]; - s->command_complete = 0; - - id = (s->select_tag >> 8) & 0xf; - dev = scsi_device_find(&s->bus, 0, id, s->current_lun); - if (!dev) { - lsi_bad_selection(s, id); - return; - } - - assert(s->current == NULL); - s->current = g_new0(lsi_request, 1); - s->current->tag = s->select_tag; - s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun, buf, - s->current); - - n = scsi_req_enqueue(s->current->req); - if (n) { - if (n > 0) { - lsi_set_phase(s, PHASE_DI); - } else if (n < 0) { - lsi_set_phase(s, PHASE_DO); - } - scsi_req_continue(s->current->req); - } - if (!s->command_complete) { - if (n) { - /* Command did not complete immediately so disconnect. */ - lsi_add_msg_byte(s, 2); /* SAVE DATA POINTER */ - lsi_add_msg_byte(s, 4); /* DISCONNECT */ - /* wait data */ - lsi_set_phase(s, PHASE_MI); - s->msg_action = 1; - lsi_queue_command(s); - } else { - /* wait command complete */ - lsi_set_phase(s, PHASE_DI); - } - } -} - -static void lsi_do_status(LSIState *s) -{ - uint8_t status; - DPRINTF("Get status len=%d status=%d\n", s->dbc, s->status); - if (s->dbc != 1) - BADF("Bad Status move\n"); - s->dbc = 1; - status = s->status; - s->sfbr = status; - pci_dma_write(PCI_DEVICE(s), s->dnad, &status, 1); - lsi_set_phase(s, PHASE_MI); - s->msg_action = 1; - lsi_add_msg_byte(s, 0); /* COMMAND COMPLETE */ -} - -static void lsi_do_msgin(LSIState *s) -{ - int len; - DPRINTF("Message in len=%d/%d\n", s->dbc, s->msg_len); - s->sfbr = s->msg[0]; - len = s->msg_len; - if (len > s->dbc) - len = s->dbc; - pci_dma_write(PCI_DEVICE(s), s->dnad, s->msg, len); - /* Linux drivers rely on the last byte being in the SIDL. */ - s->sidl = s->msg[len - 1]; - s->msg_len -= len; - if (s->msg_len) { - memmove(s->msg, s->msg + len, s->msg_len); - } else { - /* ??? Check if ATN (not yet implemented) is asserted and maybe - switch to PHASE_MO. */ - switch (s->msg_action) { - case 0: - lsi_set_phase(s, PHASE_CMD); - break; - case 1: - lsi_disconnect(s); - break; - case 2: - lsi_set_phase(s, PHASE_DO); - break; - case 3: - lsi_set_phase(s, PHASE_DI); - break; - default: - abort(); - } - } -} - -/* Read the next byte during a MSGOUT phase. */ -static uint8_t lsi_get_msgbyte(LSIState *s) -{ - uint8_t data; - pci_dma_read(PCI_DEVICE(s), s->dnad, &data, 1); - s->dnad++; - s->dbc--; - return data; -} - -/* Skip the next n bytes during a MSGOUT phase. */ -static void lsi_skip_msgbytes(LSIState *s, unsigned int n) -{ - s->dnad += n; - s->dbc -= n; -} - -static void lsi_do_msgout(LSIState *s) -{ - uint8_t msg; - int len; - uint32_t current_tag; - lsi_request *current_req, *p, *p_next; - - if (s->current) { - current_tag = s->current->tag; - current_req = s->current; - } else { - current_tag = s->select_tag; - current_req = lsi_find_by_tag(s, current_tag); - } - - DPRINTF("MSG out len=%d\n", s->dbc); - while (s->dbc) { - msg = lsi_get_msgbyte(s); - s->sfbr = msg; - - switch (msg) { - case 0x04: - DPRINTF("MSG: Disconnect\n"); - lsi_disconnect(s); - break; - case 0x08: - DPRINTF("MSG: No Operation\n"); - lsi_set_phase(s, PHASE_CMD); - break; - case 0x01: - len = lsi_get_msgbyte(s); - msg = lsi_get_msgbyte(s); - (void)len; /* avoid a warning about unused variable*/ - DPRINTF("Extended message 0x%x (len %d)\n", msg, len); - switch (msg) { - case 1: - DPRINTF("SDTR (ignored)\n"); - lsi_skip_msgbytes(s, 2); - break; - case 3: - DPRINTF("WDTR (ignored)\n"); - lsi_skip_msgbytes(s, 1); - break; - default: - goto bad; - } - break; - case 0x20: /* SIMPLE queue */ - s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; - DPRINTF("SIMPLE queue tag=0x%x\n", s->select_tag & 0xff); - break; - case 0x21: /* HEAD of queue */ - BADF("HEAD queue not implemented\n"); - s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; - break; - case 0x22: /* ORDERED queue */ - BADF("ORDERED queue not implemented\n"); - s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; - break; - case 0x0d: - /* The ABORT TAG message clears the current I/O process only. */ - DPRINTF("MSG: ABORT TAG tag=0x%x\n", current_tag); - if (current_req) { - scsi_req_cancel(current_req->req); - } - lsi_disconnect(s); - break; - case 0x06: - case 0x0e: - case 0x0c: - /* The ABORT message clears all I/O processes for the selecting - initiator on the specified logical unit of the target. */ - if (msg == 0x06) { - DPRINTF("MSG: ABORT tag=0x%x\n", current_tag); - } - /* The CLEAR QUEUE message clears all I/O processes for all - initiators on the specified logical unit of the target. */ - if (msg == 0x0e) { - DPRINTF("MSG: CLEAR QUEUE tag=0x%x\n", current_tag); - } - /* The BUS DEVICE RESET message clears all I/O processes for all - initiators on all logical units of the target. */ - if (msg == 0x0c) { - DPRINTF("MSG: BUS DEVICE RESET tag=0x%x\n", current_tag); - } - - /* clear the current I/O process */ - if (s->current) { - scsi_req_cancel(s->current->req); - } - - /* As the current implemented devices scsi_disk and scsi_generic - only support one LUN, we don't need to keep track of LUNs. - Clearing I/O processes for other initiators could be possible - for scsi_generic by sending a SG_SCSI_RESET to the /dev/sgX - device, but this is currently not implemented (and seems not - to be really necessary). So let's simply clear all queued - commands for the current device: */ - QTAILQ_FOREACH_SAFE(p, &s->queue, next, p_next) { - if ((p->tag & 0x0000ff00) == (current_tag & 0x0000ff00)) { - scsi_req_cancel(p->req); - } - } - - lsi_disconnect(s); - break; - default: - if ((msg & 0x80) == 0) { - goto bad; - } - s->current_lun = msg & 7; - DPRINTF("Select LUN %d\n", s->current_lun); - lsi_set_phase(s, PHASE_CMD); - break; - } - } - return; -bad: - BADF("Unimplemented message 0x%02x\n", msg); - lsi_set_phase(s, PHASE_MI); - lsi_add_msg_byte(s, 7); /* MESSAGE REJECT */ - s->msg_action = 0; -} - -#define LSI_BUF_SIZE 4096 -static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count) -{ - PCIDevice *d = PCI_DEVICE(s); - int n; - uint8_t buf[LSI_BUF_SIZE]; - - DPRINTF("memcpy dest 0x%08x src 0x%08x count %d\n", dest, src, count); - while (count) { - n = (count > LSI_BUF_SIZE) ? LSI_BUF_SIZE : count; - pci_dma_read(d, src, buf, n); - pci_dma_write(d, dest, buf, n); - src += n; - dest += n; - count -= n; - } -} - -static void lsi_wait_reselect(LSIState *s) -{ - lsi_request *p; - - DPRINTF("Wait Reselect\n"); - - QTAILQ_FOREACH(p, &s->queue, next) { - if (p->pending) { - lsi_reselect(s, p); - break; - } - } - if (s->current == NULL) { - s->waiting = 1; - } -} - -static void lsi_execute_script(LSIState *s) -{ - PCIDevice *pci_dev = PCI_DEVICE(s); - uint32_t insn; - uint32_t addr, addr_high; - int opcode; - int insn_processed = 0; - - s->istat1 |= LSI_ISTAT1_SRUN; -again: - insn_processed++; - insn = read_dword(s, s->dsp); - if (!insn) { - /* If we receive an empty opcode increment the DSP by 4 bytes - instead of 8 and execute the next opcode at that location */ - s->dsp += 4; - goto again; - } - addr = read_dword(s, s->dsp + 4); - addr_high = 0; - DPRINTF("SCRIPTS dsp=%08x opcode %08x arg %08x\n", s->dsp, insn, addr); - s->dsps = addr; - s->dcmd = insn >> 24; - s->dsp += 8; - switch (insn >> 30) { - case 0: /* Block move. */ - if (s->sist1 & LSI_SIST1_STO) { - DPRINTF("Delayed select timeout\n"); - lsi_stop_script(s); - break; - } - s->dbc = insn & 0xffffff; - s->rbc = s->dbc; - /* ??? Set ESA. */ - s->ia = s->dsp - 8; - if (insn & (1 << 29)) { - /* Indirect addressing. */ - addr = read_dword(s, addr); - } else if (insn & (1 << 28)) { - uint32_t buf[2]; - int32_t offset; - /* Table indirect addressing. */ - - /* 32-bit Table indirect */ - offset = sextract32(addr, 0, 24); - pci_dma_read(pci_dev, s->dsa + offset, buf, 8); - /* byte count is stored in bits 0:23 only */ - s->dbc = cpu_to_le32(buf[0]) & 0xffffff; - s->rbc = s->dbc; - addr = cpu_to_le32(buf[1]); - - /* 40-bit DMA, upper addr bits [39:32] stored in first DWORD of - * table, bits [31:24] */ - if (lsi_dma_40bit(s)) - addr_high = cpu_to_le32(buf[0]) >> 24; - else if (lsi_dma_ti64bit(s)) { - int selector = (cpu_to_le32(buf[0]) >> 24) & 0x1f; - switch (selector) { - case 0 ... 0x0f: - /* offset index into scratch registers since - * TI64 mode can use registers C to R */ - addr_high = s->scratch[2 + selector]; - break; - case 0x10: - addr_high = s->mmrs; - break; - case 0x11: - addr_high = s->mmws; - break; - case 0x12: - addr_high = s->sfs; - break; - case 0x13: - addr_high = s->drs; - break; - case 0x14: - addr_high = s->sbms; - break; - case 0x15: - addr_high = s->dbms; - break; - default: - BADF("Illegal selector specified (0x%x > 0x15)" - " for 64-bit DMA block move", selector); - break; - } - } - } else if (lsi_dma_64bit(s)) { - /* fetch a 3rd dword if 64-bit direct move is enabled and - only if we're not doing table indirect or indirect addressing */ - s->dbms = read_dword(s, s->dsp); - s->dsp += 4; - s->ia = s->dsp - 12; - } - if ((s->sstat1 & PHASE_MASK) != ((insn >> 24) & 7)) { - DPRINTF("Wrong phase got %d expected %d\n", - s->sstat1 & PHASE_MASK, (insn >> 24) & 7); - lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0); - break; - } - s->dnad = addr; - s->dnad64 = addr_high; - switch (s->sstat1 & 0x7) { - case PHASE_DO: - s->waiting = 2; - lsi_do_dma(s, 1); - if (s->waiting) - s->waiting = 3; - break; - case PHASE_DI: - s->waiting = 2; - lsi_do_dma(s, 0); - if (s->waiting) - s->waiting = 3; - break; - case PHASE_CMD: - lsi_do_command(s); - break; - case PHASE_ST: - lsi_do_status(s); - break; - case PHASE_MO: - lsi_do_msgout(s); - break; - case PHASE_MI: - lsi_do_msgin(s); - break; - default: - BADF("Unimplemented phase %d\n", s->sstat1 & PHASE_MASK); - exit(1); - } - s->dfifo = s->dbc & 0xff; - s->ctest5 = (s->ctest5 & 0xfc) | ((s->dbc >> 8) & 3); - s->sbc = s->dbc; - s->rbc -= s->dbc; - s->ua = addr + s->dbc; - break; - - case 1: /* IO or Read/Write instruction. */ - opcode = (insn >> 27) & 7; - if (opcode < 5) { - uint32_t id; - - if (insn & (1 << 25)) { - id = read_dword(s, s->dsa + sextract32(insn, 0, 24)); - } else { - id = insn; - } - id = (id >> 16) & 0xf; - if (insn & (1 << 26)) { - addr = s->dsp + sextract32(addr, 0, 24); - } - s->dnad = addr; - switch (opcode) { - case 0: /* Select */ - s->sdid = id; - if (s->scntl1 & LSI_SCNTL1_CON) { - DPRINTF("Already reselected, jumping to alternative address\n"); - s->dsp = s->dnad; - break; - } - s->sstat0 |= LSI_SSTAT0_WOA; - s->scntl1 &= ~LSI_SCNTL1_IARB; - if (!scsi_device_find(&s->bus, 0, id, 0)) { - lsi_bad_selection(s, id); - break; - } - DPRINTF("Selected target %d%s\n", - id, insn & (1 << 3) ? " ATN" : ""); - /* ??? Linux drivers compain when this is set. Maybe - it only applies in low-level mode (unimplemented). - lsi_script_scsi_interrupt(s, LSI_SIST0_CMP, 0); */ - s->select_tag = id << 8; - s->scntl1 |= LSI_SCNTL1_CON; - if (insn & (1 << 3)) { - s->socl |= LSI_SOCL_ATN; - } - lsi_set_phase(s, PHASE_MO); - break; - case 1: /* Disconnect */ - DPRINTF("Wait Disconnect\n"); - s->scntl1 &= ~LSI_SCNTL1_CON; - break; - case 2: /* Wait Reselect */ - if (!lsi_irq_on_rsl(s)) { - lsi_wait_reselect(s); - } - break; - case 3: /* Set */ - DPRINTF("Set%s%s%s%s\n", - insn & (1 << 3) ? " ATN" : "", - insn & (1 << 6) ? " ACK" : "", - insn & (1 << 9) ? " TM" : "", - insn & (1 << 10) ? " CC" : ""); - if (insn & (1 << 3)) { - s->socl |= LSI_SOCL_ATN; - lsi_set_phase(s, PHASE_MO); - } - if (insn & (1 << 9)) { - BADF("Target mode not implemented\n"); - exit(1); - } - if (insn & (1 << 10)) - s->carry = 1; - break; - case 4: /* Clear */ - DPRINTF("Clear%s%s%s%s\n", - insn & (1 << 3) ? " ATN" : "", - insn & (1 << 6) ? " ACK" : "", - insn & (1 << 9) ? " TM" : "", - insn & (1 << 10) ? " CC" : ""); - if (insn & (1 << 3)) { - s->socl &= ~LSI_SOCL_ATN; - } - if (insn & (1 << 10)) - s->carry = 0; - break; - } - } else { - uint8_t op0; - uint8_t op1; - uint8_t data8; - int reg; - int operator; -#ifdef DEBUG_LSI - static const char *opcode_names[3] = - {"Write", "Read", "Read-Modify-Write"}; - static const char *operator_names[8] = - {"MOV", "SHL", "OR", "XOR", "AND", "SHR", "ADD", "ADC"}; -#endif - - reg = ((insn >> 16) & 0x7f) | (insn & 0x80); - data8 = (insn >> 8) & 0xff; - opcode = (insn >> 27) & 7; - operator = (insn >> 24) & 7; - DPRINTF("%s reg 0x%x %s data8=0x%02x sfbr=0x%02x%s\n", - opcode_names[opcode - 5], reg, - operator_names[operator], data8, s->sfbr, - (insn & (1 << 23)) ? " SFBR" : ""); - op0 = op1 = 0; - switch (opcode) { - case 5: /* From SFBR */ - op0 = s->sfbr; - op1 = data8; - break; - case 6: /* To SFBR */ - if (operator) - op0 = lsi_reg_readb(s, reg); - op1 = data8; - break; - case 7: /* Read-modify-write */ - if (operator) - op0 = lsi_reg_readb(s, reg); - if (insn & (1 << 23)) { - op1 = s->sfbr; - } else { - op1 = data8; - } - break; - } - - switch (operator) { - case 0: /* move */ - op0 = op1; - break; - case 1: /* Shift left */ - op1 = op0 >> 7; - op0 = (op0 << 1) | s->carry; - s->carry = op1; - break; - case 2: /* OR */ - op0 |= op1; - break; - case 3: /* XOR */ - op0 ^= op1; - break; - case 4: /* AND */ - op0 &= op1; - break; - case 5: /* SHR */ - op1 = op0 & 1; - op0 = (op0 >> 1) | (s->carry << 7); - s->carry = op1; - break; - case 6: /* ADD */ - op0 += op1; - s->carry = op0 < op1; - break; - case 7: /* ADC */ - op0 += op1 + s->carry; - if (s->carry) - s->carry = op0 <= op1; - else - s->carry = op0 < op1; - break; - } - - switch (opcode) { - case 5: /* From SFBR */ - case 7: /* Read-modify-write */ - lsi_reg_writeb(s, reg, op0); - break; - case 6: /* To SFBR */ - s->sfbr = op0; - break; - } - } - break; - - case 2: /* Transfer Control. */ - { - int cond; - int jmp; - - if ((insn & 0x002e0000) == 0) { - DPRINTF("NOP\n"); - break; - } - if (s->sist1 & LSI_SIST1_STO) { - DPRINTF("Delayed select timeout\n"); - lsi_stop_script(s); - break; - } - cond = jmp = (insn & (1 << 19)) != 0; - if (cond == jmp && (insn & (1 << 21))) { - DPRINTF("Compare carry %d\n", s->carry == jmp); - cond = s->carry != 0; - } - if (cond == jmp && (insn & (1 << 17))) { - DPRINTF("Compare phase %d %c= %d\n", - (s->sstat1 & PHASE_MASK), - jmp ? '=' : '!', - ((insn >> 24) & 7)); - cond = (s->sstat1 & PHASE_MASK) == ((insn >> 24) & 7); - } - if (cond == jmp && (insn & (1 << 18))) { - uint8_t mask; - - mask = (~insn >> 8) & 0xff; - DPRINTF("Compare data 0x%x & 0x%x %c= 0x%x\n", - s->sfbr, mask, jmp ? '=' : '!', insn & mask); - cond = (s->sfbr & mask) == (insn & mask); - } - if (cond == jmp) { - if (insn & (1 << 23)) { - /* Relative address. */ - addr = s->dsp + sextract32(addr, 0, 24); - } - switch ((insn >> 27) & 7) { - case 0: /* Jump */ - DPRINTF("Jump to 0x%08x\n", addr); - s->adder = addr; - s->dsp = addr; - break; - case 1: /* Call */ - DPRINTF("Call 0x%08x\n", addr); - s->temp = s->dsp; - s->dsp = addr; - break; - case 2: /* Return */ - DPRINTF("Return to 0x%08x\n", s->temp); - s->dsp = s->temp; - break; - case 3: /* Interrupt */ - DPRINTF("Interrupt 0x%08x\n", s->dsps); - if ((insn & (1 << 20)) != 0) { - s->istat0 |= LSI_ISTAT0_INTF; - lsi_update_irq(s); - } else { - lsi_script_dma_interrupt(s, LSI_DSTAT_SIR); - } - break; - default: - DPRINTF("Illegal transfer control\n"); - lsi_script_dma_interrupt(s, LSI_DSTAT_IID); - break; - } - } else { - DPRINTF("Control condition failed\n"); - } - } - break; - - case 3: - if ((insn & (1 << 29)) == 0) { - /* Memory move. */ - uint32_t dest; - /* ??? The docs imply the destination address is loaded into - the TEMP register. However the Linux drivers rely on - the value being presrved. */ - dest = read_dword(s, s->dsp); - s->dsp += 4; - lsi_memcpy(s, dest, addr, insn & 0xffffff); - } else { - uint8_t data[7]; - int reg; - int n; - int i; - - if (insn & (1 << 28)) { - addr = s->dsa + sextract32(addr, 0, 24); - } - n = (insn & 7); - reg = (insn >> 16) & 0xff; - if (insn & (1 << 24)) { - pci_dma_read(pci_dev, addr, data, n); - DPRINTF("Load reg 0x%x size %d addr 0x%08x = %08x\n", reg, n, - addr, *(int *)data); - for (i = 0; i < n; i++) { - lsi_reg_writeb(s, reg + i, data[i]); - } - } else { - DPRINTF("Store reg 0x%x size %d addr 0x%08x\n", reg, n, addr); - for (i = 0; i < n; i++) { - data[i] = lsi_reg_readb(s, reg + i); - } - pci_dma_write(pci_dev, addr, data, n); - } - } - } - if (insn_processed > 10000 && !s->waiting) { - /* Some windows drivers make the device spin waiting for a memory - location to change. If we have been executed a lot of code then - assume this is the case and force an unexpected device disconnect. - This is apparently sufficient to beat the drivers into submission. - */ - if (!(s->sien0 & LSI_SIST0_UDC)) - fprintf(stderr, "inf. loop with UDC masked\n"); - lsi_script_scsi_interrupt(s, LSI_SIST0_UDC, 0); - lsi_disconnect(s); - } else if (s->istat1 & LSI_ISTAT1_SRUN && !s->waiting) { - if (s->dcntl & LSI_DCNTL_SSM) { - lsi_script_dma_interrupt(s, LSI_DSTAT_SSI); - } else { - goto again; - } - } - DPRINTF("SCRIPTS execution stopped\n"); -} - -static uint8_t lsi_reg_readb(LSIState *s, int offset) -{ - uint8_t tmp; -#define CASE_GET_REG24(name, addr) \ - case addr: return s->name & 0xff; \ - case addr + 1: return (s->name >> 8) & 0xff; \ - case addr + 2: return (s->name >> 16) & 0xff; - -#define CASE_GET_REG32(name, addr) \ - case addr: return s->name & 0xff; \ - case addr + 1: return (s->name >> 8) & 0xff; \ - case addr + 2: return (s->name >> 16) & 0xff; \ - case addr + 3: return (s->name >> 24) & 0xff; - -#ifdef DEBUG_LSI_REG - DPRINTF("Read reg %x\n", offset); -#endif - switch (offset) { - case 0x00: /* SCNTL0 */ - return s->scntl0; - case 0x01: /* SCNTL1 */ - return s->scntl1; - case 0x02: /* SCNTL2 */ - return s->scntl2; - case 0x03: /* SCNTL3 */ - return s->scntl3; - case 0x04: /* SCID */ - return s->scid; - case 0x05: /* SXFER */ - return s->sxfer; - case 0x06: /* SDID */ - return s->sdid; - case 0x07: /* GPREG0 */ - return 0x7f; - case 0x08: /* Revision ID */ - return 0x00; - case 0x09: /* SOCL */ - return s->socl; - case 0xa: /* SSID */ - return s->ssid; - case 0xb: /* SBCL */ - /* ??? This is not correct. However it's (hopefully) only - used for diagnostics, so should be ok. */ - return 0; - case 0xc: /* DSTAT */ - tmp = s->dstat | LSI_DSTAT_DFE; - if ((s->istat0 & LSI_ISTAT0_INTF) == 0) - s->dstat = 0; - lsi_update_irq(s); - return tmp; - case 0x0d: /* SSTAT0 */ - return s->sstat0; - case 0x0e: /* SSTAT1 */ - return s->sstat1; - case 0x0f: /* SSTAT2 */ - return s->scntl1 & LSI_SCNTL1_CON ? 0 : 2; - CASE_GET_REG32(dsa, 0x10) - case 0x14: /* ISTAT0 */ - return s->istat0; - case 0x15: /* ISTAT1 */ - return s->istat1; - case 0x16: /* MBOX0 */ - return s->mbox0; - case 0x17: /* MBOX1 */ - return s->mbox1; - case 0x18: /* CTEST0 */ - return 0xff; - case 0x19: /* CTEST1 */ - return 0; - case 0x1a: /* CTEST2 */ - tmp = s->ctest2 | LSI_CTEST2_DACK | LSI_CTEST2_CM; - if (s->istat0 & LSI_ISTAT0_SIGP) { - s->istat0 &= ~LSI_ISTAT0_SIGP; - tmp |= LSI_CTEST2_SIGP; - } - return tmp; - case 0x1b: /* CTEST3 */ - return s->ctest3; - CASE_GET_REG32(temp, 0x1c) - case 0x20: /* DFIFO */ - return 0; - case 0x21: /* CTEST4 */ - return s->ctest4; - case 0x22: /* CTEST5 */ - return s->ctest5; - case 0x23: /* CTEST6 */ - return 0; - CASE_GET_REG24(dbc, 0x24) - case 0x27: /* DCMD */ - return s->dcmd; - CASE_GET_REG32(dnad, 0x28) - CASE_GET_REG32(dsp, 0x2c) - CASE_GET_REG32(dsps, 0x30) - CASE_GET_REG32(scratch[0], 0x34) - case 0x38: /* DMODE */ - return s->dmode; - case 0x39: /* DIEN */ - return s->dien; - case 0x3a: /* SBR */ - return s->sbr; - case 0x3b: /* DCNTL */ - return s->dcntl; - /* ADDER Output (Debug of relative jump address) */ - CASE_GET_REG32(adder, 0x3c) - case 0x40: /* SIEN0 */ - return s->sien0; - case 0x41: /* SIEN1 */ - return s->sien1; - case 0x42: /* SIST0 */ - tmp = s->sist0; - s->sist0 = 0; - lsi_update_irq(s); - return tmp; - case 0x43: /* SIST1 */ - tmp = s->sist1; - s->sist1 = 0; - lsi_update_irq(s); - return tmp; - case 0x46: /* MACNTL */ - return 0x0f; - case 0x47: /* GPCNTL0 */ - return 0x0f; - case 0x48: /* STIME0 */ - return s->stime0; - case 0x4a: /* RESPID0 */ - return s->respid0; - case 0x4b: /* RESPID1 */ - return s->respid1; - case 0x4d: /* STEST1 */ - return s->stest1; - case 0x4e: /* STEST2 */ - return s->stest2; - case 0x4f: /* STEST3 */ - return s->stest3; - case 0x50: /* SIDL */ - /* This is needed by the linux drivers. We currently only update it - during the MSG IN phase. */ - return s->sidl; - case 0x52: /* STEST4 */ - return 0xe0; - case 0x56: /* CCNTL0 */ - return s->ccntl0; - case 0x57: /* CCNTL1 */ - return s->ccntl1; - case 0x58: /* SBDL */ - /* Some drivers peek at the data bus during the MSG IN phase. */ - if ((s->sstat1 & PHASE_MASK) == PHASE_MI) - return s->msg[0]; - return 0; - case 0x59: /* SBDL high */ - return 0; - CASE_GET_REG32(mmrs, 0xa0) - CASE_GET_REG32(mmws, 0xa4) - CASE_GET_REG32(sfs, 0xa8) - CASE_GET_REG32(drs, 0xac) - CASE_GET_REG32(sbms, 0xb0) - CASE_GET_REG32(dbms, 0xb4) - CASE_GET_REG32(dnad64, 0xb8) - CASE_GET_REG32(pmjad1, 0xc0) - CASE_GET_REG32(pmjad2, 0xc4) - CASE_GET_REG32(rbc, 0xc8) - CASE_GET_REG32(ua, 0xcc) - CASE_GET_REG32(ia, 0xd4) - CASE_GET_REG32(sbc, 0xd8) - CASE_GET_REG32(csbc, 0xdc) - } - if (offset >= 0x5c && offset < 0xa0) { - int n; - int shift; - n = (offset - 0x58) >> 2; - shift = (offset & 3) * 8; - return (s->scratch[n] >> shift) & 0xff; - } - BADF("readb 0x%x\n", offset); - exit(1); -#undef CASE_GET_REG24 -#undef CASE_GET_REG32 -} - -static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) -{ -#define CASE_SET_REG24(name, addr) \ - case addr : s->name &= 0xffffff00; s->name |= val; break; \ - case addr + 1: s->name &= 0xffff00ff; s->name |= val << 8; break; \ - case addr + 2: s->name &= 0xff00ffff; s->name |= val << 16; break; - -#define CASE_SET_REG32(name, addr) \ - case addr : s->name &= 0xffffff00; s->name |= val; break; \ - case addr + 1: s->name &= 0xffff00ff; s->name |= val << 8; break; \ - case addr + 2: s->name &= 0xff00ffff; s->name |= val << 16; break; \ - case addr + 3: s->name &= 0x00ffffff; s->name |= val << 24; break; - -#ifdef DEBUG_LSI_REG - DPRINTF("Write reg %x = %02x\n", offset, val); -#endif - switch (offset) { - case 0x00: /* SCNTL0 */ - s->scntl0 = val; - if (val & LSI_SCNTL0_START) { - BADF("Start sequence not implemented\n"); - } - break; - case 0x01: /* SCNTL1 */ - s->scntl1 = val & ~LSI_SCNTL1_SST; - if (val & LSI_SCNTL1_IARB) { - BADF("Immediate Arbritration not implemented\n"); - } - if (val & LSI_SCNTL1_RST) { - if (!(s->sstat0 & LSI_SSTAT0_RST)) { - qbus_reset_all(&s->bus.qbus); - s->sstat0 |= LSI_SSTAT0_RST; - lsi_script_scsi_interrupt(s, LSI_SIST0_RST, 0); - } - } else { - s->sstat0 &= ~LSI_SSTAT0_RST; - } - break; - case 0x02: /* SCNTL2 */ - val &= ~(LSI_SCNTL2_WSR | LSI_SCNTL2_WSS); - s->scntl2 = val; - break; - case 0x03: /* SCNTL3 */ - s->scntl3 = val; - break; - case 0x04: /* SCID */ - s->scid = val; - break; - case 0x05: /* SXFER */ - s->sxfer = val; - break; - case 0x06: /* SDID */ - if ((s->ssid & 0x80) && (val & 0xf) != (s->ssid & 0xf)) { - BADF("Destination ID does not match SSID\n"); - } - s->sdid = val & 0xf; - break; - case 0x07: /* GPREG0 */ - break; - case 0x08: /* SFBR */ - /* The CPU is not allowed to write to this register. However the - SCRIPTS register move instructions are. */ - s->sfbr = val; - break; - case 0x0a: case 0x0b: - /* Openserver writes to these readonly registers on startup */ - return; - case 0x0c: case 0x0d: case 0x0e: case 0x0f: - /* Linux writes to these readonly registers on startup. */ - return; - CASE_SET_REG32(dsa, 0x10) - case 0x14: /* ISTAT0 */ - s->istat0 = (s->istat0 & 0x0f) | (val & 0xf0); - if (val & LSI_ISTAT0_ABRT) { - lsi_script_dma_interrupt(s, LSI_DSTAT_ABRT); - } - if (val & LSI_ISTAT0_INTF) { - s->istat0 &= ~LSI_ISTAT0_INTF; - lsi_update_irq(s); - } - if (s->waiting == 1 && val & LSI_ISTAT0_SIGP) { - DPRINTF("Woken by SIGP\n"); - s->waiting = 0; - s->dsp = s->dnad; - lsi_execute_script(s); - } - if (val & LSI_ISTAT0_SRST) { - qdev_reset_all(DEVICE(s)); - } - break; - case 0x16: /* MBOX0 */ - s->mbox0 = val; - break; - case 0x17: /* MBOX1 */ - s->mbox1 = val; - break; - case 0x18: /* CTEST0 */ - /* nothing to do */ - break; - case 0x1a: /* CTEST2 */ - s->ctest2 = val & LSI_CTEST2_PCICIE; - break; - case 0x1b: /* CTEST3 */ - s->ctest3 = val & 0x0f; - break; - CASE_SET_REG32(temp, 0x1c) - case 0x21: /* CTEST4 */ - if (val & 7) { - BADF("Unimplemented CTEST4-FBL 0x%x\n", val); - } - s->ctest4 = val; - break; - case 0x22: /* CTEST5 */ - if (val & (LSI_CTEST5_ADCK | LSI_CTEST5_BBCK)) { - BADF("CTEST5 DMA increment not implemented\n"); - } - s->ctest5 = val; - break; - CASE_SET_REG24(dbc, 0x24) - CASE_SET_REG32(dnad, 0x28) - case 0x2c: /* DSP[0:7] */ - s->dsp &= 0xffffff00; - s->dsp |= val; - break; - case 0x2d: /* DSP[8:15] */ - s->dsp &= 0xffff00ff; - s->dsp |= val << 8; - break; - case 0x2e: /* DSP[16:23] */ - s->dsp &= 0xff00ffff; - s->dsp |= val << 16; - break; - case 0x2f: /* DSP[24:31] */ - s->dsp &= 0x00ffffff; - s->dsp |= val << 24; - if ((s->dmode & LSI_DMODE_MAN) == 0 - && (s->istat1 & LSI_ISTAT1_SRUN) == 0) - lsi_execute_script(s); - break; - CASE_SET_REG32(dsps, 0x30) - CASE_SET_REG32(scratch[0], 0x34) - case 0x38: /* DMODE */ - if (val & (LSI_DMODE_SIOM | LSI_DMODE_DIOM)) { - BADF("IO mappings not implemented\n"); - } - s->dmode = val; - break; - case 0x39: /* DIEN */ - s->dien = val; - lsi_update_irq(s); - break; - case 0x3a: /* SBR */ - s->sbr = val; - break; - case 0x3b: /* DCNTL */ - s->dcntl = val & ~(LSI_DCNTL_PFF | LSI_DCNTL_STD); - if ((val & LSI_DCNTL_STD) && (s->istat1 & LSI_ISTAT1_SRUN) == 0) - lsi_execute_script(s); - break; - case 0x40: /* SIEN0 */ - s->sien0 = val; - lsi_update_irq(s); - break; - case 0x41: /* SIEN1 */ - s->sien1 = val; - lsi_update_irq(s); - break; - case 0x47: /* GPCNTL0 */ - break; - case 0x48: /* STIME0 */ - s->stime0 = val; - break; - case 0x49: /* STIME1 */ - if (val & 0xf) { - DPRINTF("General purpose timer not implemented\n"); - /* ??? Raising the interrupt immediately seems to be sufficient - to keep the FreeBSD driver happy. */ - lsi_script_scsi_interrupt(s, 0, LSI_SIST1_GEN); - } - break; - case 0x4a: /* RESPID0 */ - s->respid0 = val; - break; - case 0x4b: /* RESPID1 */ - s->respid1 = val; - break; - case 0x4d: /* STEST1 */ - s->stest1 = val; - break; - case 0x4e: /* STEST2 */ - if (val & 1) { - BADF("Low level mode not implemented\n"); - } - s->stest2 = val; - break; - case 0x4f: /* STEST3 */ - if (val & 0x41) { - BADF("SCSI FIFO test mode not implemented\n"); - } - s->stest3 = val; - break; - case 0x56: /* CCNTL0 */ - s->ccntl0 = val; - break; - case 0x57: /* CCNTL1 */ - s->ccntl1 = val; - break; - CASE_SET_REG32(mmrs, 0xa0) - CASE_SET_REG32(mmws, 0xa4) - CASE_SET_REG32(sfs, 0xa8) - CASE_SET_REG32(drs, 0xac) - CASE_SET_REG32(sbms, 0xb0) - CASE_SET_REG32(dbms, 0xb4) - CASE_SET_REG32(dnad64, 0xb8) - CASE_SET_REG32(pmjad1, 0xc0) - CASE_SET_REG32(pmjad2, 0xc4) - CASE_SET_REG32(rbc, 0xc8) - CASE_SET_REG32(ua, 0xcc) - CASE_SET_REG32(ia, 0xd4) - CASE_SET_REG32(sbc, 0xd8) - CASE_SET_REG32(csbc, 0xdc) - default: - if (offset >= 0x5c && offset < 0xa0) { - int n; - int shift; - n = (offset - 0x58) >> 2; - shift = (offset & 3) * 8; - s->scratch[n] = deposit32(s->scratch[n], shift, 8, val); - } else { - BADF("Unhandled writeb 0x%x = 0x%x\n", offset, val); - } - } -#undef CASE_SET_REG24 -#undef CASE_SET_REG32 -} - -static void lsi_mmio_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - LSIState *s = opaque; - - lsi_reg_writeb(s, addr & 0xff, val); -} - -static uint64_t lsi_mmio_read(void *opaque, hwaddr addr, - unsigned size) -{ - LSIState *s = opaque; - - return lsi_reg_readb(s, addr & 0xff); -} - -static const MemoryRegionOps lsi_mmio_ops = { - .read = lsi_mmio_read, - .write = lsi_mmio_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void lsi_ram_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - LSIState *s = opaque; - uint32_t newval; - uint32_t mask; - int shift; - - newval = s->script_ram[addr >> 2]; - shift = (addr & 3) * 8; - mask = ((uint64_t)1 << (size * 8)) - 1; - newval &= ~(mask << shift); - newval |= val << shift; - s->script_ram[addr >> 2] = newval; -} - -static uint64_t lsi_ram_read(void *opaque, hwaddr addr, - unsigned size) -{ - LSIState *s = opaque; - uint32_t val; - uint32_t mask; - - val = s->script_ram[addr >> 2]; - mask = ((uint64_t)1 << (size * 8)) - 1; - val >>= (addr & 3) * 8; - return val & mask; -} - -static const MemoryRegionOps lsi_ram_ops = { - .read = lsi_ram_read, - .write = lsi_ram_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t lsi_io_read(void *opaque, hwaddr addr, - unsigned size) -{ - LSIState *s = opaque; - return lsi_reg_readb(s, addr & 0xff); -} - -static void lsi_io_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - LSIState *s = opaque; - lsi_reg_writeb(s, addr & 0xff, val); -} - -static const MemoryRegionOps lsi_io_ops = { - .read = lsi_io_read, - .write = lsi_io_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void lsi_scsi_reset(DeviceState *dev) -{ - LSIState *s = LSI53C895A(dev); - - lsi_soft_reset(s); -} - -static void lsi_pre_save(void *opaque) -{ - LSIState *s = opaque; - - if (s->current) { - assert(s->current->dma_buf == NULL); - assert(s->current->dma_len == 0); - } - assert(QTAILQ_EMPTY(&s->queue)); -} - -static const VMStateDescription vmstate_lsi_scsi = { - .name = "lsiscsi", - .version_id = 0, - .minimum_version_id = 0, - .pre_save = lsi_pre_save, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, LSIState), - - VMSTATE_INT32(carry, LSIState), - VMSTATE_INT32(status, LSIState), - VMSTATE_INT32(msg_action, LSIState), - VMSTATE_INT32(msg_len, LSIState), - VMSTATE_BUFFER(msg, LSIState), - VMSTATE_INT32(waiting, LSIState), - - VMSTATE_UINT32(dsa, LSIState), - VMSTATE_UINT32(temp, LSIState), - VMSTATE_UINT32(dnad, LSIState), - VMSTATE_UINT32(dbc, LSIState), - VMSTATE_UINT8(istat0, LSIState), - VMSTATE_UINT8(istat1, LSIState), - VMSTATE_UINT8(dcmd, LSIState), - VMSTATE_UINT8(dstat, LSIState), - VMSTATE_UINT8(dien, LSIState), - VMSTATE_UINT8(sist0, LSIState), - VMSTATE_UINT8(sist1, LSIState), - VMSTATE_UINT8(sien0, LSIState), - VMSTATE_UINT8(sien1, LSIState), - VMSTATE_UINT8(mbox0, LSIState), - VMSTATE_UINT8(mbox1, LSIState), - VMSTATE_UINT8(dfifo, LSIState), - VMSTATE_UINT8(ctest2, LSIState), - VMSTATE_UINT8(ctest3, LSIState), - VMSTATE_UINT8(ctest4, LSIState), - VMSTATE_UINT8(ctest5, LSIState), - VMSTATE_UINT8(ccntl0, LSIState), - VMSTATE_UINT8(ccntl1, LSIState), - VMSTATE_UINT32(dsp, LSIState), - VMSTATE_UINT32(dsps, LSIState), - VMSTATE_UINT8(dmode, LSIState), - VMSTATE_UINT8(dcntl, LSIState), - VMSTATE_UINT8(scntl0, LSIState), - VMSTATE_UINT8(scntl1, LSIState), - VMSTATE_UINT8(scntl2, LSIState), - VMSTATE_UINT8(scntl3, LSIState), - VMSTATE_UINT8(sstat0, LSIState), - VMSTATE_UINT8(sstat1, LSIState), - VMSTATE_UINT8(scid, LSIState), - VMSTATE_UINT8(sxfer, LSIState), - VMSTATE_UINT8(socl, LSIState), - VMSTATE_UINT8(sdid, LSIState), - VMSTATE_UINT8(ssid, LSIState), - VMSTATE_UINT8(sfbr, LSIState), - VMSTATE_UINT8(stest1, LSIState), - VMSTATE_UINT8(stest2, LSIState), - VMSTATE_UINT8(stest3, LSIState), - VMSTATE_UINT8(sidl, LSIState), - VMSTATE_UINT8(stime0, LSIState), - VMSTATE_UINT8(respid0, LSIState), - VMSTATE_UINT8(respid1, LSIState), - VMSTATE_UINT32(mmrs, LSIState), - VMSTATE_UINT32(mmws, LSIState), - VMSTATE_UINT32(sfs, LSIState), - VMSTATE_UINT32(drs, LSIState), - VMSTATE_UINT32(sbms, LSIState), - VMSTATE_UINT32(dbms, LSIState), - VMSTATE_UINT32(dnad64, LSIState), - VMSTATE_UINT32(pmjad1, LSIState), - VMSTATE_UINT32(pmjad2, LSIState), - VMSTATE_UINT32(rbc, LSIState), - VMSTATE_UINT32(ua, LSIState), - VMSTATE_UINT32(ia, LSIState), - VMSTATE_UINT32(sbc, LSIState), - VMSTATE_UINT32(csbc, LSIState), - VMSTATE_BUFFER_UNSAFE(scratch, LSIState, 0, 18 * sizeof(uint32_t)), - VMSTATE_UINT8(sbr, LSIState), - - VMSTATE_BUFFER_UNSAFE(script_ram, LSIState, 0, 2048 * sizeof(uint32_t)), - VMSTATE_END_OF_LIST() - } -}; - -static const struct SCSIBusInfo lsi_scsi_info = { - .tcq = true, - .max_target = LSI_MAX_DEVS, - .max_lun = 0, /* LUN support is buggy */ - - .transfer_data = lsi_transfer_data, - .complete = lsi_command_complete, - .cancel = lsi_request_cancelled -}; - -static void lsi_scsi_realize(PCIDevice *dev, Error **errp) -{ - LSIState *s = LSI53C895A(dev); - DeviceState *d = DEVICE(dev); - uint8_t *pci_conf; - - pci_conf = dev->config; - - /* PCI latency timer = 255 */ - pci_conf[PCI_LATENCY_TIMER] = 0xff; - /* Interrupt pin A */ - pci_conf[PCI_INTERRUPT_PIN] = 0x01; - - memory_region_init_io(&s->mmio_io, OBJECT(s), &lsi_mmio_ops, s, - "lsi-mmio", 0x400); - memory_region_init_io(&s->ram_io, OBJECT(s), &lsi_ram_ops, s, - "lsi-ram", 0x2000); - memory_region_init_io(&s->io_io, OBJECT(s), &lsi_io_ops, s, - "lsi-io", 256); - - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_io); - pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio_io); - pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ram_io); - QTAILQ_INIT(&s->queue); - - scsi_bus_new(&s->bus, sizeof(s->bus), d, &lsi_scsi_info, NULL); - if (!d->hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, errp); - } -} - -static void lsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = lsi_scsi_realize; - k->vendor_id = PCI_VENDOR_ID_LSI_LOGIC; - k->device_id = PCI_DEVICE_ID_LSI_53C895A; - k->class_id = PCI_CLASS_STORAGE_SCSI; - k->subsystem_id = 0x1000; - dc->reset = lsi_scsi_reset; - dc->vmsd = &vmstate_lsi_scsi; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo lsi_info = { - .name = TYPE_LSI53C895A, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(LSIState), - .class_init = lsi_class_init, -}; - -static void lsi53c810_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->device_id = PCI_DEVICE_ID_LSI_53C810; -} - -static TypeInfo lsi53c810_info = { - .name = TYPE_LSI53C810, - .parent = TYPE_LSI53C895A, - .class_init = lsi53c810_class_init, -}; - -static void lsi53c895a_register_types(void) -{ - type_register_static(&lsi_info); - type_register_static(&lsi53c810_info); -} - -type_init(lsi53c895a_register_types) diff --git a/qemu/hw/scsi/megasas.c b/qemu/hw/scsi/megasas.c deleted file mode 100644 index a63a58155..000000000 --- a/qemu/hw/scsi/megasas.c +++ /dev/null @@ -1,2548 +0,0 @@ -/* - * QEMU MegaRAID SAS 8708EM2 Host Bus Adapter emulation - * Based on the linux driver code at drivers/scsi/megaraid - * - * Copyright (c) 2009-2012 Hannes Reinecke, SUSE Labs - * - * 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 . - */ - -#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 "hw/pci/msix.h" -#include "qemu/iov.h" -#include "hw/scsi/scsi.h" -#include "block/scsi.h" -#include "trace.h" - -#include "mfi.h" - -#define MEGASAS_VERSION_GEN1 "1.70" -#define MEGASAS_VERSION_GEN2 "1.80" -#define MEGASAS_MAX_FRAMES 2048 /* Firmware limit at 65535 */ -#define MEGASAS_DEFAULT_FRAMES 1000 /* Windows requires this */ -#define MEGASAS_GEN2_DEFAULT_FRAMES 1008 /* Windows requires this */ -#define MEGASAS_MAX_SGE 128 /* Firmware limit */ -#define MEGASAS_DEFAULT_SGE 80 -#define MEGASAS_MAX_SECTORS 0xFFFF /* No real limit */ -#define MEGASAS_MAX_ARRAYS 128 - -#define MEGASAS_HBA_SERIAL "QEMU123456" -#define NAA_LOCALLY_ASSIGNED_ID 0x3ULL -#define IEEE_COMPANY_LOCALLY_ASSIGNED 0x525400 - -#define MEGASAS_FLAG_USE_JBOD 0 -#define MEGASAS_MASK_USE_JBOD (1 << MEGASAS_FLAG_USE_JBOD) -#define MEGASAS_FLAG_USE_MSI 1 -#define MEGASAS_MASK_USE_MSI (1 << MEGASAS_FLAG_USE_MSI) -#define MEGASAS_FLAG_USE_MSIX 2 -#define MEGASAS_MASK_USE_MSIX (1 << MEGASAS_FLAG_USE_MSIX) -#define MEGASAS_FLAG_USE_QUEUE64 3 -#define MEGASAS_MASK_USE_QUEUE64 (1 << MEGASAS_FLAG_USE_QUEUE64) - -static const char *mfi_frame_desc[] = { - "MFI init", "LD Read", "LD Write", "LD SCSI", "PD SCSI", - "MFI Doorbell", "MFI Abort", "MFI SMP", "MFI Stop"}; - -typedef struct MegasasCmd { - uint32_t index; - uint16_t flags; - uint16_t count; - uint64_t context; - - hwaddr pa; - hwaddr pa_size; - union mfi_frame *frame; - SCSIRequest *req; - QEMUSGList qsg; - void *iov_buf; - size_t iov_size; - size_t iov_offset; - struct MegasasState *state; -} MegasasCmd; - -typedef struct MegasasState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - MemoryRegion mmio_io; - MemoryRegion port_io; - MemoryRegion queue_io; - uint32_t frame_hi; - - int fw_state; - uint32_t fw_sge; - uint32_t fw_cmds; - uint32_t flags; - int fw_luns; - int intr_mask; - int doorbell; - int busy; - int diag; - int adp_reset; - - MegasasCmd *event_cmd; - int event_locale; - int event_class; - int event_count; - int shutdown_event; - int boot_event; - - uint64_t sas_addr; - char *hba_serial; - - uint64_t reply_queue_pa; - void *reply_queue; - int reply_queue_len; - int reply_queue_head; - int reply_queue_tail; - uint64_t consumer_pa; - uint64_t producer_pa; - - MegasasCmd frames[MEGASAS_MAX_FRAMES]; - DECLARE_BITMAP(frame_map, MEGASAS_MAX_FRAMES); - SCSIBus bus; -} MegasasState; - -typedef struct MegasasBaseClass { - PCIDeviceClass parent_class; - const char *product_name; - const char *product_version; - int mmio_bar; - int ioport_bar; - int osts; -} MegasasBaseClass; - -#define TYPE_MEGASAS_BASE "megasas-base" -#define TYPE_MEGASAS_GEN1 "megasas" -#define TYPE_MEGASAS_GEN2 "megasas-gen2" - -#define MEGASAS(obj) \ - OBJECT_CHECK(MegasasState, (obj), TYPE_MEGASAS_BASE) - -#define MEGASAS_DEVICE_CLASS(oc) \ - OBJECT_CLASS_CHECK(MegasasBaseClass, (oc), TYPE_MEGASAS_BASE) -#define MEGASAS_DEVICE_GET_CLASS(oc) \ - OBJECT_GET_CLASS(MegasasBaseClass, (oc), TYPE_MEGASAS_BASE) - -#define MEGASAS_INTR_DISABLED_MASK 0xFFFFFFFF - -static bool megasas_intr_enabled(MegasasState *s) -{ - if ((s->intr_mask & MEGASAS_INTR_DISABLED_MASK) != - MEGASAS_INTR_DISABLED_MASK) { - return true; - } - return false; -} - -static bool megasas_use_queue64(MegasasState *s) -{ - return s->flags & MEGASAS_MASK_USE_QUEUE64; -} - -static bool megasas_use_msi(MegasasState *s) -{ - return s->flags & MEGASAS_MASK_USE_MSI; -} - -static bool megasas_use_msix(MegasasState *s) -{ - return s->flags & MEGASAS_MASK_USE_MSIX; -} - -static bool megasas_is_jbod(MegasasState *s) -{ - return s->flags & MEGASAS_MASK_USE_JBOD; -} - -static void megasas_frame_set_cmd_status(MegasasState *s, - unsigned long frame, uint8_t v) -{ - PCIDevice *pci = &s->parent_obj; - stb_pci_dma(pci, frame + offsetof(struct mfi_frame_header, cmd_status), v); -} - -static void megasas_frame_set_scsi_status(MegasasState *s, - unsigned long frame, uint8_t v) -{ - PCIDevice *pci = &s->parent_obj; - stb_pci_dma(pci, frame + offsetof(struct mfi_frame_header, scsi_status), v); -} - -/* - * Context is considered opaque, but the HBA firmware is running - * in little endian mode. So convert it to little endian, too. - */ -static uint64_t megasas_frame_get_context(MegasasState *s, - unsigned long frame) -{ - PCIDevice *pci = &s->parent_obj; - return ldq_le_pci_dma(pci, frame + offsetof(struct mfi_frame_header, context)); -} - -static bool megasas_frame_is_ieee_sgl(MegasasCmd *cmd) -{ - return cmd->flags & MFI_FRAME_IEEE_SGL; -} - -static bool megasas_frame_is_sgl64(MegasasCmd *cmd) -{ - return cmd->flags & MFI_FRAME_SGL64; -} - -static bool megasas_frame_is_sense64(MegasasCmd *cmd) -{ - return cmd->flags & MFI_FRAME_SENSE64; -} - -static uint64_t megasas_sgl_get_addr(MegasasCmd *cmd, - union mfi_sgl *sgl) -{ - uint64_t addr; - - if (megasas_frame_is_ieee_sgl(cmd)) { - addr = le64_to_cpu(sgl->sg_skinny->addr); - } else if (megasas_frame_is_sgl64(cmd)) { - addr = le64_to_cpu(sgl->sg64->addr); - } else { - addr = le32_to_cpu(sgl->sg32->addr); - } - return addr; -} - -static uint32_t megasas_sgl_get_len(MegasasCmd *cmd, - union mfi_sgl *sgl) -{ - uint32_t len; - - if (megasas_frame_is_ieee_sgl(cmd)) { - len = le32_to_cpu(sgl->sg_skinny->len); - } else if (megasas_frame_is_sgl64(cmd)) { - len = le32_to_cpu(sgl->sg64->len); - } else { - len = le32_to_cpu(sgl->sg32->len); - } - return len; -} - -static union mfi_sgl *megasas_sgl_next(MegasasCmd *cmd, - union mfi_sgl *sgl) -{ - uint8_t *next = (uint8_t *)sgl; - - if (megasas_frame_is_ieee_sgl(cmd)) { - next += sizeof(struct mfi_sg_skinny); - } else if (megasas_frame_is_sgl64(cmd)) { - next += sizeof(struct mfi_sg64); - } else { - next += sizeof(struct mfi_sg32); - } - - if (next >= (uint8_t *)cmd->frame + cmd->pa_size) { - return NULL; - } - return (union mfi_sgl *)next; -} - -static void megasas_soft_reset(MegasasState *s); - -static int megasas_map_sgl(MegasasState *s, MegasasCmd *cmd, union mfi_sgl *sgl) -{ - int i; - int iov_count = 0; - size_t iov_size = 0; - - cmd->flags = le16_to_cpu(cmd->frame->header.flags); - iov_count = cmd->frame->header.sge_count; - if (iov_count > MEGASAS_MAX_SGE) { - trace_megasas_iovec_sgl_overflow(cmd->index, iov_count, - MEGASAS_MAX_SGE); - return iov_count; - } - pci_dma_sglist_init(&cmd->qsg, PCI_DEVICE(s), iov_count); - for (i = 0; i < iov_count; i++) { - dma_addr_t iov_pa, iov_size_p; - - if (!sgl) { - trace_megasas_iovec_sgl_underflow(cmd->index, i); - goto unmap; - } - iov_pa = megasas_sgl_get_addr(cmd, sgl); - iov_size_p = megasas_sgl_get_len(cmd, sgl); - if (!iov_pa || !iov_size_p) { - trace_megasas_iovec_sgl_invalid(cmd->index, i, - iov_pa, iov_size_p); - goto unmap; - } - qemu_sglist_add(&cmd->qsg, iov_pa, iov_size_p); - sgl = megasas_sgl_next(cmd, sgl); - iov_size += (size_t)iov_size_p; - } - if (cmd->iov_size > iov_size) { - trace_megasas_iovec_overflow(cmd->index, iov_size, cmd->iov_size); - } else if (cmd->iov_size < iov_size) { - trace_megasas_iovec_underflow(cmd->iov_size, iov_size, cmd->iov_size); - } - cmd->iov_offset = 0; - return 0; -unmap: - qemu_sglist_destroy(&cmd->qsg); - return iov_count - i; -} - -static void megasas_unmap_sgl(MegasasCmd *cmd) -{ - qemu_sglist_destroy(&cmd->qsg); - cmd->iov_offset = 0; -} - -/* - * passthrough sense and io sense are at the same offset - */ -static int megasas_build_sense(MegasasCmd *cmd, uint8_t *sense_ptr, - uint8_t sense_len) -{ - PCIDevice *pcid = PCI_DEVICE(cmd->state); - uint32_t pa_hi = 0, pa_lo; - hwaddr pa; - - if (sense_len > cmd->frame->header.sense_len) { - sense_len = cmd->frame->header.sense_len; - } - if (sense_len) { - pa_lo = le32_to_cpu(cmd->frame->pass.sense_addr_lo); - if (megasas_frame_is_sense64(cmd)) { - pa_hi = le32_to_cpu(cmd->frame->pass.sense_addr_hi); - } - pa = ((uint64_t) pa_hi << 32) | pa_lo; - pci_dma_write(pcid, pa, sense_ptr, sense_len); - cmd->frame->header.sense_len = sense_len; - } - return sense_len; -} - -static void megasas_write_sense(MegasasCmd *cmd, SCSISense sense) -{ - uint8_t sense_buf[SCSI_SENSE_BUF_SIZE]; - uint8_t sense_len = 18; - - memset(sense_buf, 0, sense_len); - sense_buf[0] = 0xf0; - sense_buf[2] = sense.key; - sense_buf[7] = 10; - sense_buf[12] = sense.asc; - sense_buf[13] = sense.ascq; - megasas_build_sense(cmd, sense_buf, sense_len); -} - -static void megasas_copy_sense(MegasasCmd *cmd) -{ - uint8_t sense_buf[SCSI_SENSE_BUF_SIZE]; - uint8_t sense_len; - - sense_len = scsi_req_get_sense(cmd->req, sense_buf, - SCSI_SENSE_BUF_SIZE); - megasas_build_sense(cmd, sense_buf, sense_len); -} - -/* - * Format an INQUIRY CDB - */ -static int megasas_setup_inquiry(uint8_t *cdb, int pg, int len) -{ - memset(cdb, 0, 6); - cdb[0] = INQUIRY; - if (pg > 0) { - cdb[1] = 0x1; - cdb[2] = pg; - } - cdb[3] = (len >> 8) & 0xff; - cdb[4] = (len & 0xff); - return len; -} - -/* - * Encode lba and len into a READ_16/WRITE_16 CDB - */ -static void megasas_encode_lba(uint8_t *cdb, uint64_t lba, - uint32_t len, bool is_write) -{ - memset(cdb, 0x0, 16); - if (is_write) { - cdb[0] = WRITE_16; - } else { - cdb[0] = READ_16; - } - cdb[2] = (lba >> 56) & 0xff; - cdb[3] = (lba >> 48) & 0xff; - cdb[4] = (lba >> 40) & 0xff; - cdb[5] = (lba >> 32) & 0xff; - cdb[6] = (lba >> 24) & 0xff; - cdb[7] = (lba >> 16) & 0xff; - cdb[8] = (lba >> 8) & 0xff; - cdb[9] = (lba) & 0xff; - cdb[10] = (len >> 24) & 0xff; - cdb[11] = (len >> 16) & 0xff; - cdb[12] = (len >> 8) & 0xff; - cdb[13] = (len) & 0xff; -} - -/* - * Utility functions - */ -static uint64_t megasas_fw_time(void) -{ - struct tm curtime; - uint64_t bcd_time; - - qemu_get_timedate(&curtime, 0); - bcd_time = ((uint64_t)curtime.tm_sec & 0xff) << 48 | - ((uint64_t)curtime.tm_min & 0xff) << 40 | - ((uint64_t)curtime.tm_hour & 0xff) << 32 | - ((uint64_t)curtime.tm_mday & 0xff) << 24 | - ((uint64_t)curtime.tm_mon & 0xff) << 16 | - ((uint64_t)(curtime.tm_year + 1900) & 0xffff); - - return bcd_time; -} - -/* - * Default disk sata address - * 0x1221 is the magic number as - * present in real hardware, - * so use it here, too. - */ -static uint64_t megasas_get_sata_addr(uint16_t id) -{ - uint64_t addr = (0x1221ULL << 48); - return addr | ((uint64_t)id << 24); -} - -/* - * Frame handling - */ -static int megasas_next_index(MegasasState *s, int index, int limit) -{ - index++; - if (index == limit) { - index = 0; - } - return index; -} - -static MegasasCmd *megasas_lookup_frame(MegasasState *s, - hwaddr frame) -{ - MegasasCmd *cmd = NULL; - int num = 0, index; - - index = s->reply_queue_head; - - while (num < s->fw_cmds) { - if (s->frames[index].pa && s->frames[index].pa == frame) { - cmd = &s->frames[index]; - break; - } - index = megasas_next_index(s, index, s->fw_cmds); - num++; - } - - return cmd; -} - -static void megasas_unmap_frame(MegasasState *s, MegasasCmd *cmd) -{ - PCIDevice *p = PCI_DEVICE(s); - - pci_dma_unmap(p, cmd->frame, cmd->pa_size, 0, 0); - cmd->frame = NULL; - cmd->pa = 0; - clear_bit(cmd->index, s->frame_map); -} - -/* - * This absolutely needs to be locked if - * qemu ever goes multithreaded. - */ -static MegasasCmd *megasas_enqueue_frame(MegasasState *s, - hwaddr frame, uint64_t context, int count) -{ - PCIDevice *pcid = PCI_DEVICE(s); - MegasasCmd *cmd = NULL; - int frame_size = MFI_FRAME_SIZE * 16; - hwaddr frame_size_p = frame_size; - unsigned long index; - - index = 0; - while (index < s->fw_cmds) { - index = find_next_zero_bit(s->frame_map, s->fw_cmds, index); - if (!s->frames[index].pa) - break; - /* Busy frame found */ - trace_megasas_qf_mapped(index); - } - if (index >= s->fw_cmds) { - /* All frames busy */ - trace_megasas_qf_busy(frame); - return NULL; - } - cmd = &s->frames[index]; - set_bit(index, s->frame_map); - trace_megasas_qf_new(index, frame); - - cmd->pa = frame; - /* Map all possible frames */ - cmd->frame = pci_dma_map(pcid, frame, &frame_size_p, 0); - if (frame_size_p != frame_size) { - trace_megasas_qf_map_failed(cmd->index, (unsigned long)frame); - if (cmd->frame) { - megasas_unmap_frame(s, cmd); - } - s->event_count++; - return NULL; - } - cmd->pa_size = frame_size_p; - cmd->context = context; - if (!megasas_use_queue64(s)) { - cmd->context &= (uint64_t)0xFFFFFFFF; - } - cmd->count = count; - s->busy++; - - if (s->consumer_pa) { - s->reply_queue_tail = ldl_le_pci_dma(pcid, s->consumer_pa); - } - trace_megasas_qf_enqueue(cmd->index, cmd->count, cmd->context, - s->reply_queue_head, s->reply_queue_tail, s->busy); - - return cmd; -} - -static void megasas_complete_frame(MegasasState *s, uint64_t context) -{ - PCIDevice *pci_dev = PCI_DEVICE(s); - int tail, queue_offset; - - /* Decrement busy count */ - s->busy--; - if (s->reply_queue_pa) { - /* - * Put command on the reply queue. - * Context is opaque, but emulation is running in - * little endian. So convert it. - */ - if (megasas_use_queue64(s)) { - queue_offset = s->reply_queue_head * sizeof(uint64_t); - stq_le_pci_dma(pci_dev, s->reply_queue_pa + queue_offset, context); - } else { - queue_offset = s->reply_queue_head * sizeof(uint32_t); - stl_le_pci_dma(pci_dev, s->reply_queue_pa + queue_offset, context); - } - s->reply_queue_tail = ldl_le_pci_dma(pci_dev, s->consumer_pa); - trace_megasas_qf_complete(context, s->reply_queue_head, - s->reply_queue_tail, s->busy); - } - - if (megasas_intr_enabled(s)) { - /* Update reply queue pointer */ - s->reply_queue_tail = ldl_le_pci_dma(pci_dev, s->consumer_pa); - tail = s->reply_queue_head; - s->reply_queue_head = megasas_next_index(s, tail, s->fw_cmds); - trace_megasas_qf_update(s->reply_queue_head, s->reply_queue_tail, - s->busy); - stl_le_pci_dma(pci_dev, s->producer_pa, s->reply_queue_head); - /* Notify HBA */ - if (msix_enabled(pci_dev)) { - trace_megasas_msix_raise(0); - msix_notify(pci_dev, 0); - } else if (msi_enabled(pci_dev)) { - trace_megasas_msi_raise(0); - msi_notify(pci_dev, 0); - } else { - s->doorbell++; - if (s->doorbell == 1) { - trace_megasas_irq_raise(); - pci_irq_assert(pci_dev); - } - } - } else { - trace_megasas_qf_complete_noirq(context); - } -} - -static void megasas_reset_frames(MegasasState *s) -{ - int i; - MegasasCmd *cmd; - - for (i = 0; i < s->fw_cmds; i++) { - cmd = &s->frames[i]; - if (cmd->pa) { - megasas_unmap_frame(s, cmd); - } - } - bitmap_zero(s->frame_map, MEGASAS_MAX_FRAMES); -} - -static void megasas_abort_command(MegasasCmd *cmd) -{ - if (cmd->req) { - scsi_req_cancel(cmd->req); - cmd->req = NULL; - } -} - -static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd) -{ - PCIDevice *pcid = PCI_DEVICE(s); - uint32_t pa_hi, pa_lo; - hwaddr iq_pa, initq_size = sizeof(struct mfi_init_qinfo); - struct mfi_init_qinfo *initq = NULL; - uint32_t flags; - int ret = MFI_STAT_OK; - - if (s->reply_queue_pa) { - trace_megasas_initq_mapped(s->reply_queue_pa); - goto out; - } - pa_lo = le32_to_cpu(cmd->frame->init.qinfo_new_addr_lo); - pa_hi = le32_to_cpu(cmd->frame->init.qinfo_new_addr_hi); - iq_pa = (((uint64_t) pa_hi << 32) | pa_lo); - trace_megasas_init_firmware((uint64_t)iq_pa); - initq = pci_dma_map(pcid, iq_pa, &initq_size, 0); - if (!initq || initq_size != sizeof(*initq)) { - trace_megasas_initq_map_failed(cmd->index); - s->event_count++; - ret = MFI_STAT_MEMORY_NOT_AVAILABLE; - goto out; - } - s->reply_queue_len = le32_to_cpu(initq->rq_entries) & 0xFFFF; - if (s->reply_queue_len > s->fw_cmds) { - trace_megasas_initq_mismatch(s->reply_queue_len, s->fw_cmds); - s->event_count++; - ret = MFI_STAT_INVALID_PARAMETER; - goto out; - } - pa_lo = le32_to_cpu(initq->rq_addr_lo); - pa_hi = le32_to_cpu(initq->rq_addr_hi); - s->reply_queue_pa = ((uint64_t) pa_hi << 32) | pa_lo; - pa_lo = le32_to_cpu(initq->ci_addr_lo); - pa_hi = le32_to_cpu(initq->ci_addr_hi); - s->consumer_pa = ((uint64_t) pa_hi << 32) | pa_lo; - pa_lo = le32_to_cpu(initq->pi_addr_lo); - pa_hi = le32_to_cpu(initq->pi_addr_hi); - s->producer_pa = ((uint64_t) pa_hi << 32) | pa_lo; - s->reply_queue_head = ldl_le_pci_dma(pcid, s->producer_pa); - s->reply_queue_tail = ldl_le_pci_dma(pcid, s->consumer_pa); - flags = le32_to_cpu(initq->flags); - if (flags & MFI_QUEUE_FLAG_CONTEXT64) { - s->flags |= MEGASAS_MASK_USE_QUEUE64; - } - trace_megasas_init_queue((unsigned long)s->reply_queue_pa, - s->reply_queue_len, s->reply_queue_head, - s->reply_queue_tail, flags); - megasas_reset_frames(s); - s->fw_state = MFI_FWSTATE_OPERATIONAL; -out: - if (initq) { - pci_dma_unmap(pcid, initq, initq_size, 0, 0); - } - return ret; -} - -static int megasas_map_dcmd(MegasasState *s, MegasasCmd *cmd) -{ - dma_addr_t iov_pa, iov_size; - - cmd->flags = le16_to_cpu(cmd->frame->header.flags); - if (!cmd->frame->header.sge_count) { - trace_megasas_dcmd_zero_sge(cmd->index); - cmd->iov_size = 0; - return 0; - } else if (cmd->frame->header.sge_count > 1) { - trace_megasas_dcmd_invalid_sge(cmd->index, - cmd->frame->header.sge_count); - cmd->iov_size = 0; - return -1; - } - iov_pa = megasas_sgl_get_addr(cmd, &cmd->frame->dcmd.sgl); - iov_size = megasas_sgl_get_len(cmd, &cmd->frame->dcmd.sgl); - pci_dma_sglist_init(&cmd->qsg, PCI_DEVICE(s), 1); - qemu_sglist_add(&cmd->qsg, iov_pa, iov_size); - cmd->iov_size = iov_size; - return cmd->iov_size; -} - -static void megasas_finish_dcmd(MegasasCmd *cmd, uint32_t iov_size) -{ - trace_megasas_finish_dcmd(cmd->index, iov_size); - - if (cmd->frame->header.sge_count) { - qemu_sglist_destroy(&cmd->qsg); - } - if (iov_size > cmd->iov_size) { - if (megasas_frame_is_ieee_sgl(cmd)) { - cmd->frame->dcmd.sgl.sg_skinny->len = cpu_to_le32(iov_size); - } else if (megasas_frame_is_sgl64(cmd)) { - cmd->frame->dcmd.sgl.sg64->len = cpu_to_le32(iov_size); - } else { - cmd->frame->dcmd.sgl.sg32->len = cpu_to_le32(iov_size); - } - } - cmd->iov_size = 0; -} - -static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) -{ - PCIDevice *pci_dev = PCI_DEVICE(s); - PCIDeviceClass *pci_class = PCI_DEVICE_GET_CLASS(pci_dev); - MegasasBaseClass *base_class = MEGASAS_DEVICE_GET_CLASS(s); - struct mfi_ctrl_info info; - size_t dcmd_size = sizeof(info); - BusChild *kid; - int num_pd_disks = 0; - - memset(&info, 0x0, dcmd_size); - if (cmd->iov_size < dcmd_size) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - dcmd_size); - return MFI_STAT_INVALID_PARAMETER; - } - - info.pci.vendor = cpu_to_le16(pci_class->vendor_id); - info.pci.device = cpu_to_le16(pci_class->device_id); - info.pci.subvendor = cpu_to_le16(pci_class->subsystem_vendor_id); - info.pci.subdevice = cpu_to_le16(pci_class->subsystem_id); - - /* - * For some reason the firmware supports - * only up to 8 device ports. - * Despite supporting a far larger number - * of devices for the physical devices. - * So just display the first 8 devices - * in the device port list, independent - * of how many logical devices are actually - * present. - */ - info.host.type = MFI_INFO_HOST_PCIE; - info.device.type = MFI_INFO_DEV_SAS3G; - info.device.port_count = 8; - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *sdev = SCSI_DEVICE(kid->child); - uint16_t pd_id; - - if (num_pd_disks < 8) { - pd_id = ((sdev->id & 0xFF) << 8) | (sdev->lun & 0xFF); - info.device.port_addr[num_pd_disks] = - cpu_to_le64(megasas_get_sata_addr(pd_id)); - } - num_pd_disks++; - } - - 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_hw_version()); - memcpy(info.image_component[0].name, "APP", 3); - snprintf(info.image_component[0].version, 10, "%s-QEMU", - base_class->product_version); - memcpy(info.image_component[0].build_date, "Apr 1 2014", 11); - memcpy(info.image_component[0].build_time, "12:34:56", 8); - info.image_component_count = 1; - if (pci_dev->has_rom) { - uint8_t biosver[32]; - uint8_t *ptr; - - ptr = memory_region_get_ram_ptr(&pci_dev->rom); - memcpy(biosver, ptr + 0x41, 31); - memcpy(info.image_component[1].name, "BIOS", 4); - memcpy(info.image_component[1].version, biosver, - strlen((const char *)biosver)); - info.image_component_count++; - } - info.current_fw_time = cpu_to_le32(megasas_fw_time()); - info.max_arms = 32; - info.max_spans = 8; - info.max_arrays = MEGASAS_MAX_ARRAYS; - info.max_lds = MFI_MAX_LD; - info.max_cmds = cpu_to_le16(s->fw_cmds); - info.max_sg_elements = cpu_to_le16(s->fw_sge); - info.max_request_size = cpu_to_le32(MEGASAS_MAX_SECTORS); - if (!megasas_is_jbod(s)) - info.lds_present = cpu_to_le16(num_pd_disks); - info.pd_present = cpu_to_le16(num_pd_disks); - info.pd_disks_present = cpu_to_le16(num_pd_disks); - info.hw_present = cpu_to_le32(MFI_INFO_HW_NVRAM | - MFI_INFO_HW_MEM | - MFI_INFO_HW_FLASH); - info.memory_size = cpu_to_le16(512); - info.nvram_size = cpu_to_le16(32); - info.flash_size = cpu_to_le16(16); - info.raid_levels = cpu_to_le32(MFI_INFO_RAID_0); - info.adapter_ops = cpu_to_le32(MFI_INFO_AOPS_RBLD_RATE | - MFI_INFO_AOPS_SELF_DIAGNOSTIC | - MFI_INFO_AOPS_MIXED_ARRAY); - info.ld_ops = cpu_to_le32(MFI_INFO_LDOPS_DISK_CACHE_POLICY | - MFI_INFO_LDOPS_ACCESS_POLICY | - MFI_INFO_LDOPS_IO_POLICY | - MFI_INFO_LDOPS_WRITE_POLICY | - MFI_INFO_LDOPS_READ_POLICY); - info.max_strips_per_io = cpu_to_le16(s->fw_sge); - info.stripe_sz_ops.min = 3; - info.stripe_sz_ops.max = ctz32(MEGASAS_MAX_SECTORS + 1); - info.properties.pred_fail_poll_interval = cpu_to_le16(300); - info.properties.intr_throttle_cnt = cpu_to_le16(16); - info.properties.intr_throttle_timeout = cpu_to_le16(50); - info.properties.rebuild_rate = 30; - info.properties.patrol_read_rate = 30; - info.properties.bgi_rate = 30; - info.properties.cc_rate = 30; - info.properties.recon_rate = 30; - info.properties.cache_flush_interval = 4; - info.properties.spinup_drv_cnt = 2; - info.properties.spinup_delay = 6; - info.properties.ecc_bucket_size = 15; - info.properties.ecc_bucket_leak_rate = cpu_to_le16(1440); - info.properties.expose_encl_devices = 1; - info.properties.OnOffProperties = cpu_to_le32(MFI_CTRL_PROP_EnableJBOD); - info.pd_ops = cpu_to_le32(MFI_INFO_PDOPS_FORCE_ONLINE | - MFI_INFO_PDOPS_FORCE_OFFLINE); - info.pd_mix_support = cpu_to_le32(MFI_INFO_PDMIX_SAS | - MFI_INFO_PDMIX_SATA | - MFI_INFO_PDMIX_LD); - - cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg); - return MFI_STAT_OK; -} - -static int megasas_mfc_get_defaults(MegasasState *s, MegasasCmd *cmd) -{ - struct mfi_defaults info; - size_t dcmd_size = sizeof(struct mfi_defaults); - - memset(&info, 0x0, dcmd_size); - if (cmd->iov_size < dcmd_size) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - dcmd_size); - return MFI_STAT_INVALID_PARAMETER; - } - - info.sas_addr = cpu_to_le64(s->sas_addr); - info.stripe_size = 3; - info.flush_time = 4; - info.background_rate = 30; - info.allow_mix_in_enclosure = 1; - info.allow_mix_in_ld = 1; - info.direct_pd_mapping = 1; - /* Enable for BIOS support */ - info.bios_enumerate_lds = 1; - info.disable_ctrl_r = 1; - info.expose_enclosure_devices = 1; - info.disable_preboot_cli = 1; - info.cluster_disable = 1; - - cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg); - return MFI_STAT_OK; -} - -static int megasas_dcmd_get_bios_info(MegasasState *s, MegasasCmd *cmd) -{ - struct mfi_bios_data info; - size_t dcmd_size = sizeof(info); - - memset(&info, 0x0, dcmd_size); - if (cmd->iov_size < dcmd_size) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - dcmd_size); - return MFI_STAT_INVALID_PARAMETER; - } - info.continue_on_error = 1; - info.verbose = 1; - if (megasas_is_jbod(s)) { - info.expose_all_drives = 1; - } - - cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg); - return MFI_STAT_OK; -} - -static int megasas_dcmd_get_fw_time(MegasasState *s, MegasasCmd *cmd) -{ - uint64_t fw_time; - size_t dcmd_size = sizeof(fw_time); - - fw_time = cpu_to_le64(megasas_fw_time()); - - cmd->iov_size -= dma_buf_read((uint8_t *)&fw_time, dcmd_size, &cmd->qsg); - return MFI_STAT_OK; -} - -static int megasas_dcmd_set_fw_time(MegasasState *s, MegasasCmd *cmd) -{ - uint64_t fw_time; - - /* This is a dummy; setting of firmware time is not allowed */ - memcpy(&fw_time, cmd->frame->dcmd.mbox, sizeof(fw_time)); - - trace_megasas_dcmd_set_fw_time(cmd->index, fw_time); - fw_time = cpu_to_le64(megasas_fw_time()); - return MFI_STAT_OK; -} - -static int megasas_event_info(MegasasState *s, MegasasCmd *cmd) -{ - struct mfi_evt_log_state info; - size_t dcmd_size = sizeof(info); - - memset(&info, 0, dcmd_size); - - info.newest_seq_num = cpu_to_le32(s->event_count); - info.shutdown_seq_num = cpu_to_le32(s->shutdown_event); - info.boot_seq_num = cpu_to_le32(s->boot_event); - - cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg); - return MFI_STAT_OK; -} - -static int megasas_event_wait(MegasasState *s, MegasasCmd *cmd) -{ - union mfi_evt event; - - if (cmd->iov_size < sizeof(struct mfi_evt_detail)) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - sizeof(struct mfi_evt_detail)); - return MFI_STAT_INVALID_PARAMETER; - } - s->event_count = cpu_to_le32(cmd->frame->dcmd.mbox[0]); - event.word = cpu_to_le32(cmd->frame->dcmd.mbox[4]); - s->event_locale = event.members.locale; - s->event_class = event.members.class; - s->event_cmd = cmd; - /* Decrease busy count; event frame doesn't count here */ - s->busy--; - cmd->iov_size = sizeof(struct mfi_evt_detail); - return MFI_STAT_INVALID_STATUS; -} - -static int megasas_dcmd_pd_get_list(MegasasState *s, MegasasCmd *cmd) -{ - struct mfi_pd_list info; - size_t dcmd_size = sizeof(info); - BusChild *kid; - uint32_t offset, dcmd_limit, num_pd_disks = 0, max_pd_disks; - - memset(&info, 0, dcmd_size); - offset = 8; - dcmd_limit = offset + sizeof(struct mfi_pd_address); - if (cmd->iov_size < dcmd_limit) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - dcmd_limit); - return MFI_STAT_INVALID_PARAMETER; - } - - max_pd_disks = (cmd->iov_size - offset) / sizeof(struct mfi_pd_address); - if (max_pd_disks > MFI_MAX_SYS_PDS) { - max_pd_disks = MFI_MAX_SYS_PDS; - } - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *sdev = SCSI_DEVICE(kid->child); - uint16_t pd_id; - - if (num_pd_disks >= max_pd_disks) - break; - - pd_id = ((sdev->id & 0xFF) << 8) | (sdev->lun & 0xFF); - info.addr[num_pd_disks].device_id = cpu_to_le16(pd_id); - info.addr[num_pd_disks].encl_device_id = 0xFFFF; - info.addr[num_pd_disks].encl_index = 0; - info.addr[num_pd_disks].slot_number = sdev->id & 0xFF; - info.addr[num_pd_disks].scsi_dev_type = sdev->type; - info.addr[num_pd_disks].connect_port_bitmap = 0x1; - info.addr[num_pd_disks].sas_addr[0] = - cpu_to_le64(megasas_get_sata_addr(pd_id)); - num_pd_disks++; - offset += sizeof(struct mfi_pd_address); - } - trace_megasas_dcmd_pd_get_list(cmd->index, num_pd_disks, - max_pd_disks, offset); - - info.size = cpu_to_le32(offset); - info.count = cpu_to_le32(num_pd_disks); - - cmd->iov_size -= dma_buf_read((uint8_t *)&info, offset, &cmd->qsg); - return MFI_STAT_OK; -} - -static int megasas_dcmd_pd_list_query(MegasasState *s, MegasasCmd *cmd) -{ - uint16_t flags; - - /* mbox0 contains flags */ - flags = le16_to_cpu(cmd->frame->dcmd.mbox[0]); - trace_megasas_dcmd_pd_list_query(cmd->index, flags); - if (flags == MR_PD_QUERY_TYPE_ALL || - megasas_is_jbod(s)) { - return megasas_dcmd_pd_get_list(s, cmd); - } - - return MFI_STAT_OK; -} - -static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, - MegasasCmd *cmd) -{ - struct mfi_pd_info *info = cmd->iov_buf; - size_t dcmd_size = sizeof(struct mfi_pd_info); - uint64_t pd_size; - uint16_t pd_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF); - uint8_t cmdbuf[6]; - SCSIRequest *req; - size_t len, resid; - - if (!cmd->iov_buf) { - cmd->iov_buf = g_malloc0(dcmd_size); - info = cmd->iov_buf; - info->inquiry_data[0] = 0x7f; /* Force PQual 0x3, PType 0x1f */ - info->vpd_page83[0] = 0x7f; - megasas_setup_inquiry(cmdbuf, 0, sizeof(info->inquiry_data)); - req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); - if (!req) { - trace_megasas_dcmd_req_alloc_failed(cmd->index, - "PD get info std inquiry"); - g_free(cmd->iov_buf); - cmd->iov_buf = NULL; - return MFI_STAT_FLASH_ALLOC_FAIL; - } - trace_megasas_dcmd_internal_submit(cmd->index, - "PD get info std inquiry", lun); - len = scsi_req_enqueue(req); - if (len > 0) { - cmd->iov_size = len; - scsi_req_continue(req); - } - return MFI_STAT_INVALID_STATUS; - } else if (info->inquiry_data[0] != 0x7f && info->vpd_page83[0] == 0x7f) { - megasas_setup_inquiry(cmdbuf, 0x83, sizeof(info->vpd_page83)); - req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); - if (!req) { - trace_megasas_dcmd_req_alloc_failed(cmd->index, - "PD get info vpd inquiry"); - return MFI_STAT_FLASH_ALLOC_FAIL; - } - trace_megasas_dcmd_internal_submit(cmd->index, - "PD get info vpd inquiry", lun); - len = scsi_req_enqueue(req); - if (len > 0) { - cmd->iov_size = len; - scsi_req_continue(req); - } - return MFI_STAT_INVALID_STATUS; - } - /* Finished, set FW state */ - if ((info->inquiry_data[0] >> 5) == 0) { - if (megasas_is_jbod(cmd->state)) { - info->fw_state = cpu_to_le16(MFI_PD_STATE_SYSTEM); - } else { - info->fw_state = cpu_to_le16(MFI_PD_STATE_ONLINE); - } - } else { - info->fw_state = cpu_to_le16(MFI_PD_STATE_OFFLINE); - } - - info->ref.v.device_id = cpu_to_le16(pd_id); - info->state.ddf.pd_type = cpu_to_le16(MFI_PD_DDF_TYPE_IN_VD| - MFI_PD_DDF_TYPE_INTF_SAS); - blk_get_geometry(sdev->conf.blk, &pd_size); - info->raw_size = cpu_to_le64(pd_size); - info->non_coerced_size = cpu_to_le64(pd_size); - info->coerced_size = cpu_to_le64(pd_size); - info->encl_device_id = 0xFFFF; - info->slot_number = (sdev->id & 0xFF); - info->path_info.count = 1; - info->path_info.sas_addr[0] = - cpu_to_le64(megasas_get_sata_addr(pd_id)); - info->connected_port_bitmap = 0x1; - info->device_speed = 1; - info->link_speed = 1; - resid = dma_buf_read(cmd->iov_buf, dcmd_size, &cmd->qsg); - g_free(cmd->iov_buf); - cmd->iov_size = dcmd_size - resid; - cmd->iov_buf = NULL; - return MFI_STAT_OK; -} - -static int megasas_dcmd_pd_get_info(MegasasState *s, MegasasCmd *cmd) -{ - size_t dcmd_size = sizeof(struct mfi_pd_info); - uint16_t pd_id; - uint8_t target_id, lun_id; - SCSIDevice *sdev = NULL; - int retval = MFI_STAT_DEVICE_NOT_FOUND; - - if (cmd->iov_size < dcmd_size) { - return MFI_STAT_INVALID_PARAMETER; - } - - /* mbox0 has the ID */ - pd_id = le16_to_cpu(cmd->frame->dcmd.mbox[0]); - target_id = (pd_id >> 8) & 0xFF; - lun_id = pd_id & 0xFF; - sdev = scsi_device_find(&s->bus, 0, target_id, lun_id); - trace_megasas_dcmd_pd_get_info(cmd->index, pd_id); - - if (sdev) { - /* Submit inquiry */ - retval = megasas_pd_get_info_submit(sdev, pd_id, cmd); - } - - return retval; -} - -static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd) -{ - struct mfi_ld_list info; - size_t dcmd_size = sizeof(info), resid; - uint32_t num_ld_disks = 0, max_ld_disks; - uint64_t ld_size; - BusChild *kid; - - memset(&info, 0, dcmd_size); - if (cmd->iov_size > dcmd_size) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - dcmd_size); - return MFI_STAT_INVALID_PARAMETER; - } - - max_ld_disks = (cmd->iov_size - 8) / 16; - if (megasas_is_jbod(s)) { - max_ld_disks = 0; - } - if (max_ld_disks > MFI_MAX_LD) { - max_ld_disks = MFI_MAX_LD; - } - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *sdev = SCSI_DEVICE(kid->child); - - if (num_ld_disks >= max_ld_disks) { - break; - } - /* Logical device size is in blocks */ - blk_get_geometry(sdev->conf.blk, &ld_size); - info.ld_list[num_ld_disks].ld.v.target_id = sdev->id; - info.ld_list[num_ld_disks].state = MFI_LD_STATE_OPTIMAL; - info.ld_list[num_ld_disks].size = cpu_to_le64(ld_size); - num_ld_disks++; - } - info.ld_count = cpu_to_le32(num_ld_disks); - trace_megasas_dcmd_ld_get_list(cmd->index, num_ld_disks, max_ld_disks); - - resid = dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg); - cmd->iov_size = dcmd_size - resid; - return MFI_STAT_OK; -} - -static int megasas_dcmd_ld_list_query(MegasasState *s, MegasasCmd *cmd) -{ - uint16_t flags; - struct mfi_ld_targetid_list info; - size_t dcmd_size = sizeof(info), resid; - uint32_t num_ld_disks = 0, max_ld_disks = s->fw_luns; - BusChild *kid; - - /* mbox0 contains flags */ - flags = le16_to_cpu(cmd->frame->dcmd.mbox[0]); - trace_megasas_dcmd_ld_list_query(cmd->index, flags); - if (flags != MR_LD_QUERY_TYPE_ALL && - flags != MR_LD_QUERY_TYPE_EXPOSED_TO_HOST) { - max_ld_disks = 0; - } - - memset(&info, 0, dcmd_size); - if (cmd->iov_size < 12) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - dcmd_size); - return MFI_STAT_INVALID_PARAMETER; - } - dcmd_size = sizeof(uint32_t) * 2 + 3; - max_ld_disks = cmd->iov_size - dcmd_size; - if (megasas_is_jbod(s)) { - max_ld_disks = 0; - } - if (max_ld_disks > MFI_MAX_LD) { - max_ld_disks = MFI_MAX_LD; - } - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *sdev = SCSI_DEVICE(kid->child); - - if (num_ld_disks >= max_ld_disks) { - break; - } - info.targetid[num_ld_disks] = sdev->lun; - num_ld_disks++; - dcmd_size++; - } - info.ld_count = cpu_to_le32(num_ld_disks); - info.size = dcmd_size; - trace_megasas_dcmd_ld_get_list(cmd->index, num_ld_disks, max_ld_disks); - - resid = dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg); - cmd->iov_size = dcmd_size - resid; - return MFI_STAT_OK; -} - -static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun, - MegasasCmd *cmd) -{ - struct mfi_ld_info *info = cmd->iov_buf; - size_t dcmd_size = sizeof(struct mfi_ld_info); - uint8_t cdb[6]; - SCSIRequest *req; - ssize_t len, resid; - uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF); - uint64_t ld_size; - - if (!cmd->iov_buf) { - cmd->iov_buf = g_malloc0(dcmd_size); - info = cmd->iov_buf; - megasas_setup_inquiry(cdb, 0x83, sizeof(info->vpd_page83)); - req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd); - if (!req) { - trace_megasas_dcmd_req_alloc_failed(cmd->index, - "LD get info vpd inquiry"); - g_free(cmd->iov_buf); - cmd->iov_buf = NULL; - return MFI_STAT_FLASH_ALLOC_FAIL; - } - trace_megasas_dcmd_internal_submit(cmd->index, - "LD get info vpd inquiry", lun); - len = scsi_req_enqueue(req); - if (len > 0) { - cmd->iov_size = len; - scsi_req_continue(req); - } - return MFI_STAT_INVALID_STATUS; - } - - info->ld_config.params.state = MFI_LD_STATE_OPTIMAL; - info->ld_config.properties.ld.v.target_id = lun; - info->ld_config.params.stripe_size = 3; - info->ld_config.params.num_drives = 1; - info->ld_config.params.is_consistent = 1; - /* Logical device size is in blocks */ - blk_get_geometry(sdev->conf.blk, &ld_size); - info->size = cpu_to_le64(ld_size); - memset(info->ld_config.span, 0, sizeof(info->ld_config.span)); - info->ld_config.span[0].start_block = 0; - info->ld_config.span[0].num_blocks = info->size; - info->ld_config.span[0].array_ref = cpu_to_le16(sdev_id); - - resid = dma_buf_read(cmd->iov_buf, dcmd_size, &cmd->qsg); - g_free(cmd->iov_buf); - cmd->iov_size = dcmd_size - resid; - cmd->iov_buf = NULL; - return MFI_STAT_OK; -} - -static int megasas_dcmd_ld_get_info(MegasasState *s, MegasasCmd *cmd) -{ - struct mfi_ld_info info; - size_t dcmd_size = sizeof(info); - uint16_t ld_id; - uint32_t max_ld_disks = s->fw_luns; - SCSIDevice *sdev = NULL; - int retval = MFI_STAT_DEVICE_NOT_FOUND; - - if (cmd->iov_size < dcmd_size) { - return MFI_STAT_INVALID_PARAMETER; - } - - /* mbox0 has the ID */ - ld_id = le16_to_cpu(cmd->frame->dcmd.mbox[0]); - trace_megasas_dcmd_ld_get_info(cmd->index, ld_id); - - if (megasas_is_jbod(s)) { - return MFI_STAT_DEVICE_NOT_FOUND; - } - - if (ld_id < max_ld_disks) { - sdev = scsi_device_find(&s->bus, 0, ld_id, 0); - } - - if (sdev) { - retval = megasas_ld_get_info_submit(sdev, ld_id, cmd); - } - - return retval; -} - -static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd) -{ - uint8_t data[4096]; - struct mfi_config_data *info; - int num_pd_disks = 0, array_offset, ld_offset; - BusChild *kid; - - if (cmd->iov_size > 4096) { - return MFI_STAT_INVALID_PARAMETER; - } - - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - num_pd_disks++; - } - info = (struct mfi_config_data *)&data; - /* - * Array mapping: - * - One array per SCSI device - * - One logical drive per SCSI device - * spanning the entire device - */ - info->array_count = num_pd_disks; - info->array_size = sizeof(struct mfi_array) * num_pd_disks; - info->log_drv_count = num_pd_disks; - info->log_drv_size = sizeof(struct mfi_ld_config) * num_pd_disks; - info->spares_count = 0; - info->spares_size = sizeof(struct mfi_spare); - info->size = sizeof(struct mfi_config_data) + info->array_size + - info->log_drv_size; - if (info->size > 4096) { - return MFI_STAT_INVALID_PARAMETER; - } - - array_offset = sizeof(struct mfi_config_data); - ld_offset = array_offset + sizeof(struct mfi_array) * num_pd_disks; - - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - 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; - uint64_t pd_size; - int i; - - array = (struct mfi_array *)(data + array_offset); - blk_get_geometry(sdev->conf.blk, &pd_size); - array->size = cpu_to_le64(pd_size); - array->num_drives = 1; - array->array_ref = cpu_to_le16(sdev_id); - array->pd[0].ref.v.device_id = cpu_to_le16(sdev_id); - array->pd[0].ref.v.seq_num = 0; - array->pd[0].fw_state = MFI_PD_STATE_ONLINE; - array->pd[0].encl.pd = 0xFF; - array->pd[0].encl.slot = (sdev->id & 0xFF); - for (i = 1; i < MFI_MAX_ROW_SIZE; i++) { - array->pd[i].ref.v.device_id = 0xFFFF; - array->pd[i].ref.v.seq_num = 0; - array->pd[i].fw_state = MFI_PD_STATE_UNCONFIGURED_GOOD; - array->pd[i].encl.pd = 0xFF; - array->pd[i].encl.slot = 0xFF; - } - array_offset += sizeof(struct mfi_array); - ld = (struct mfi_ld_config *)(data + ld_offset); - memset(ld, 0, sizeof(struct mfi_ld_config)); - ld->properties.ld.v.target_id = sdev->id; - ld->properties.default_cache_policy = MR_LD_CACHE_READ_AHEAD | - MR_LD_CACHE_READ_ADAPTIVE; - ld->properties.current_cache_policy = MR_LD_CACHE_READ_AHEAD | - MR_LD_CACHE_READ_ADAPTIVE; - ld->params.state = MFI_LD_STATE_OPTIMAL; - ld->params.stripe_size = 3; - ld->params.num_drives = 1; - ld->params.span_depth = 1; - ld->params.is_consistent = 1; - ld->span[0].start_block = 0; - ld->span[0].num_blocks = cpu_to_le64(pd_size); - ld->span[0].array_ref = cpu_to_le16(sdev_id); - ld_offset += sizeof(struct mfi_ld_config); - } - - cmd->iov_size -= dma_buf_read((uint8_t *)data, info->size, &cmd->qsg); - return MFI_STAT_OK; -} - -static int megasas_dcmd_get_properties(MegasasState *s, MegasasCmd *cmd) -{ - struct mfi_ctrl_props info; - size_t dcmd_size = sizeof(info); - - memset(&info, 0x0, dcmd_size); - if (cmd->iov_size < dcmd_size) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - dcmd_size); - return MFI_STAT_INVALID_PARAMETER; - } - info.pred_fail_poll_interval = cpu_to_le16(300); - info.intr_throttle_cnt = cpu_to_le16(16); - info.intr_throttle_timeout = cpu_to_le16(50); - info.rebuild_rate = 30; - info.patrol_read_rate = 30; - info.bgi_rate = 30; - info.cc_rate = 30; - info.recon_rate = 30; - info.cache_flush_interval = 4; - info.spinup_drv_cnt = 2; - info.spinup_delay = 6; - info.ecc_bucket_size = 15; - info.ecc_bucket_leak_rate = cpu_to_le16(1440); - info.expose_encl_devices = 1; - - cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg); - return MFI_STAT_OK; -} - -static int megasas_cache_flush(MegasasState *s, MegasasCmd *cmd) -{ - blk_drain_all(); - return MFI_STAT_OK; -} - -static int megasas_ctrl_shutdown(MegasasState *s, MegasasCmd *cmd) -{ - s->fw_state = MFI_FWSTATE_READY; - return MFI_STAT_OK; -} - -/* Some implementations use CLUSTER RESET LD to simulate a device reset */ -static int megasas_cluster_reset_ld(MegasasState *s, MegasasCmd *cmd) -{ - uint16_t target_id; - int i; - - /* mbox0 contains the device index */ - target_id = le16_to_cpu(cmd->frame->dcmd.mbox[0]); - trace_megasas_dcmd_reset_ld(cmd->index, target_id); - for (i = 0; i < s->fw_cmds; i++) { - MegasasCmd *tmp_cmd = &s->frames[i]; - if (tmp_cmd->req && tmp_cmd->req->dev->id == target_id) { - SCSIDevice *d = tmp_cmd->req->dev; - qdev_reset_all(&d->qdev); - } - } - return MFI_STAT_OK; -} - -static int megasas_dcmd_set_properties(MegasasState *s, MegasasCmd *cmd) -{ - struct mfi_ctrl_props info; - size_t dcmd_size = sizeof(info); - - if (cmd->iov_size < dcmd_size) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - dcmd_size); - return MFI_STAT_INVALID_PARAMETER; - } - dma_buf_write((uint8_t *)&info, cmd->iov_size, &cmd->qsg); - trace_megasas_dcmd_unsupported(cmd->index, cmd->iov_size); - return MFI_STAT_OK; -} - -static int megasas_dcmd_dummy(MegasasState *s, MegasasCmd *cmd) -{ - trace_megasas_dcmd_dummy(cmd->index, cmd->iov_size); - return MFI_STAT_OK; -} - -static const struct dcmd_cmd_tbl_t { - int opcode; - const char *desc; - int (*func)(MegasasState *s, MegasasCmd *cmd); -} dcmd_cmd_tbl[] = { - { MFI_DCMD_CTRL_MFI_HOST_MEM_ALLOC, "CTRL_HOST_MEM_ALLOC", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_GET_INFO, "CTRL_GET_INFO", - megasas_ctrl_get_info }, - { MFI_DCMD_CTRL_GET_PROPERTIES, "CTRL_GET_PROPERTIES", - megasas_dcmd_get_properties }, - { MFI_DCMD_CTRL_SET_PROPERTIES, "CTRL_SET_PROPERTIES", - megasas_dcmd_set_properties }, - { MFI_DCMD_CTRL_ALARM_GET, "CTRL_ALARM_GET", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_ALARM_ENABLE, "CTRL_ALARM_ENABLE", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_ALARM_DISABLE, "CTRL_ALARM_DISABLE", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_ALARM_SILENCE, "CTRL_ALARM_SILENCE", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_ALARM_TEST, "CTRL_ALARM_TEST", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_EVENT_GETINFO, "CTRL_EVENT_GETINFO", - megasas_event_info }, - { MFI_DCMD_CTRL_EVENT_GET, "CTRL_EVENT_GET", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_EVENT_WAIT, "CTRL_EVENT_WAIT", - megasas_event_wait }, - { MFI_DCMD_CTRL_SHUTDOWN, "CTRL_SHUTDOWN", - megasas_ctrl_shutdown }, - { MFI_DCMD_HIBERNATE_STANDBY, "CTRL_STANDBY", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_GET_TIME, "CTRL_GET_TIME", - megasas_dcmd_get_fw_time }, - { MFI_DCMD_CTRL_SET_TIME, "CTRL_SET_TIME", - megasas_dcmd_set_fw_time }, - { MFI_DCMD_CTRL_BIOS_DATA_GET, "CTRL_BIOS_DATA_GET", - megasas_dcmd_get_bios_info }, - { MFI_DCMD_CTRL_FACTORY_DEFAULTS, "CTRL_FACTORY_DEFAULTS", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_MFC_DEFAULTS_GET, "CTRL_MFC_DEFAULTS_GET", - megasas_mfc_get_defaults }, - { MFI_DCMD_CTRL_MFC_DEFAULTS_SET, "CTRL_MFC_DEFAULTS_SET", - megasas_dcmd_dummy }, - { MFI_DCMD_CTRL_CACHE_FLUSH, "CTRL_CACHE_FLUSH", - megasas_cache_flush }, - { MFI_DCMD_PD_GET_LIST, "PD_GET_LIST", - megasas_dcmd_pd_get_list }, - { MFI_DCMD_PD_LIST_QUERY, "PD_LIST_QUERY", - megasas_dcmd_pd_list_query }, - { MFI_DCMD_PD_GET_INFO, "PD_GET_INFO", - megasas_dcmd_pd_get_info }, - { MFI_DCMD_PD_STATE_SET, "PD_STATE_SET", - megasas_dcmd_dummy }, - { MFI_DCMD_PD_REBUILD, "PD_REBUILD", - megasas_dcmd_dummy }, - { MFI_DCMD_PD_BLINK, "PD_BLINK", - megasas_dcmd_dummy }, - { MFI_DCMD_PD_UNBLINK, "PD_UNBLINK", - megasas_dcmd_dummy }, - { MFI_DCMD_LD_GET_LIST, "LD_GET_LIST", - megasas_dcmd_ld_get_list}, - { MFI_DCMD_LD_LIST_QUERY, "LD_LIST_QUERY", - megasas_dcmd_ld_list_query }, - { MFI_DCMD_LD_GET_INFO, "LD_GET_INFO", - megasas_dcmd_ld_get_info }, - { MFI_DCMD_LD_GET_PROP, "LD_GET_PROP", - megasas_dcmd_dummy }, - { MFI_DCMD_LD_SET_PROP, "LD_SET_PROP", - megasas_dcmd_dummy }, - { MFI_DCMD_LD_DELETE, "LD_DELETE", - megasas_dcmd_dummy }, - { MFI_DCMD_CFG_READ, "CFG_READ", - megasas_dcmd_cfg_read }, - { MFI_DCMD_CFG_ADD, "CFG_ADD", - megasas_dcmd_dummy }, - { MFI_DCMD_CFG_CLEAR, "CFG_CLEAR", - megasas_dcmd_dummy }, - { MFI_DCMD_CFG_FOREIGN_READ, "CFG_FOREIGN_READ", - megasas_dcmd_dummy }, - { MFI_DCMD_CFG_FOREIGN_IMPORT, "CFG_FOREIGN_IMPORT", - megasas_dcmd_dummy }, - { MFI_DCMD_BBU_STATUS, "BBU_STATUS", - megasas_dcmd_dummy }, - { MFI_DCMD_BBU_CAPACITY_INFO, "BBU_CAPACITY_INFO", - megasas_dcmd_dummy }, - { MFI_DCMD_BBU_DESIGN_INFO, "BBU_DESIGN_INFO", - megasas_dcmd_dummy }, - { MFI_DCMD_BBU_PROP_GET, "BBU_PROP_GET", - megasas_dcmd_dummy }, - { MFI_DCMD_CLUSTER, "CLUSTER", - megasas_dcmd_dummy }, - { MFI_DCMD_CLUSTER_RESET_ALL, "CLUSTER_RESET_ALL", - megasas_dcmd_dummy }, - { MFI_DCMD_CLUSTER_RESET_LD, "CLUSTER_RESET_LD", - megasas_cluster_reset_ld }, - { -1, NULL, NULL } -}; - -static int megasas_handle_dcmd(MegasasState *s, MegasasCmd *cmd) -{ - int opcode, len; - int retval = 0; - const struct dcmd_cmd_tbl_t *cmdptr = dcmd_cmd_tbl; - - opcode = le32_to_cpu(cmd->frame->dcmd.opcode); - trace_megasas_handle_dcmd(cmd->index, opcode); - len = megasas_map_dcmd(s, cmd); - if (len < 0) { - return MFI_STAT_MEMORY_NOT_AVAILABLE; - } - while (cmdptr->opcode != -1 && cmdptr->opcode != opcode) { - cmdptr++; - } - if (cmdptr->opcode == -1) { - trace_megasas_dcmd_unhandled(cmd->index, opcode, len); - retval = megasas_dcmd_dummy(s, cmd); - } else { - trace_megasas_dcmd_enter(cmd->index, cmdptr->desc, len); - retval = cmdptr->func(s, cmd); - } - if (retval != MFI_STAT_INVALID_STATUS) { - megasas_finish_dcmd(cmd, len); - } - return retval; -} - -static int megasas_finish_internal_dcmd(MegasasCmd *cmd, - SCSIRequest *req) -{ - int opcode; - int retval = MFI_STAT_OK; - int lun = req->lun; - - opcode = le32_to_cpu(cmd->frame->dcmd.opcode); - scsi_req_unref(req); - trace_megasas_dcmd_internal_finish(cmd->index, opcode, lun); - switch (opcode) { - case MFI_DCMD_PD_GET_INFO: - retval = megasas_pd_get_info_submit(req->dev, lun, cmd); - break; - case MFI_DCMD_LD_GET_INFO: - retval = megasas_ld_get_info_submit(req->dev, lun, cmd); - break; - default: - trace_megasas_dcmd_internal_invalid(cmd->index, opcode); - retval = MFI_STAT_INVALID_DCMD; - break; - } - if (retval != MFI_STAT_INVALID_STATUS) { - megasas_finish_dcmd(cmd, cmd->iov_size); - } - return retval; -} - -static int megasas_enqueue_req(MegasasCmd *cmd, bool is_write) -{ - int len; - - len = scsi_req_enqueue(cmd->req); - if (len < 0) { - len = -len; - } - if (len > 0) { - if (len > cmd->iov_size) { - if (is_write) { - trace_megasas_iov_write_overflow(cmd->index, len, - cmd->iov_size); - } else { - trace_megasas_iov_read_overflow(cmd->index, len, - cmd->iov_size); - } - } - if (len < cmd->iov_size) { - if (is_write) { - trace_megasas_iov_write_underflow(cmd->index, len, - cmd->iov_size); - } else { - trace_megasas_iov_read_underflow(cmd->index, len, - cmd->iov_size); - } - cmd->iov_size = len; - } - scsi_req_continue(cmd->req); - } - return len; -} - -static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd, - bool is_logical) -{ - uint8_t *cdb; - bool is_write; - struct SCSIDevice *sdev = NULL; - - cdb = cmd->frame->pass.cdb; - - if (is_logical) { - if (cmd->frame->header.target_id >= MFI_MAX_LD || - cmd->frame->header.lun_id != 0) { - trace_megasas_scsi_target_not_present( - mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical, - cmd->frame->header.target_id, cmd->frame->header.lun_id); - return MFI_STAT_DEVICE_NOT_FOUND; - } - } - sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id, - cmd->frame->header.lun_id); - - cmd->iov_size = le32_to_cpu(cmd->frame->header.data_len); - trace_megasas_handle_scsi(mfi_frame_desc[cmd->frame->header.frame_cmd], - is_logical, cmd->frame->header.target_id, - cmd->frame->header.lun_id, sdev, cmd->iov_size); - - if (!sdev || (megasas_is_jbod(s) && is_logical)) { - trace_megasas_scsi_target_not_present( - mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical, - cmd->frame->header.target_id, cmd->frame->header.lun_id); - return MFI_STAT_DEVICE_NOT_FOUND; - } - - if (cmd->frame->header.cdb_len > 16) { - trace_megasas_scsi_invalid_cdb_len( - mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical, - cmd->frame->header.target_id, cmd->frame->header.lun_id, - cmd->frame->header.cdb_len); - megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE)); - cmd->frame->header.scsi_status = CHECK_CONDITION; - s->event_count++; - return MFI_STAT_SCSI_DONE_WITH_ERROR; - } - - if (megasas_map_sgl(s, cmd, &cmd->frame->pass.sgl)) { - megasas_write_sense(cmd, SENSE_CODE(TARGET_FAILURE)); - cmd->frame->header.scsi_status = CHECK_CONDITION; - s->event_count++; - return MFI_STAT_SCSI_DONE_WITH_ERROR; - } - - cmd->req = scsi_req_new(sdev, cmd->index, - cmd->frame->header.lun_id, cdb, cmd); - if (!cmd->req) { - trace_megasas_scsi_req_alloc_failed( - mfi_frame_desc[cmd->frame->header.frame_cmd], - cmd->frame->header.target_id, cmd->frame->header.lun_id); - megasas_write_sense(cmd, SENSE_CODE(NO_SENSE)); - cmd->frame->header.scsi_status = BUSY; - s->event_count++; - return MFI_STAT_SCSI_DONE_WITH_ERROR; - } - - is_write = (cmd->req->cmd.mode == SCSI_XFER_TO_DEV); - if (cmd->iov_size) { - if (is_write) { - trace_megasas_scsi_write_start(cmd->index, cmd->iov_size); - } else { - trace_megasas_scsi_read_start(cmd->index, cmd->iov_size); - } - } else { - trace_megasas_scsi_nodata(cmd->index); - } - megasas_enqueue_req(cmd, is_write); - return MFI_STAT_INVALID_STATUS; -} - -static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd) -{ - uint32_t lba_count, lba_start_hi, lba_start_lo; - uint64_t lba_start; - bool is_write = (cmd->frame->header.frame_cmd == MFI_CMD_LD_WRITE); - uint8_t cdb[16]; - int len; - struct SCSIDevice *sdev = NULL; - - lba_count = le32_to_cpu(cmd->frame->io.header.data_len); - lba_start_lo = le32_to_cpu(cmd->frame->io.lba_lo); - lba_start_hi = le32_to_cpu(cmd->frame->io.lba_hi); - lba_start = ((uint64_t)lba_start_hi << 32) | lba_start_lo; - - if (cmd->frame->header.target_id < MFI_MAX_LD && - cmd->frame->header.lun_id == 0) { - sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id, - cmd->frame->header.lun_id); - } - - trace_megasas_handle_io(cmd->index, - mfi_frame_desc[cmd->frame->header.frame_cmd], - cmd->frame->header.target_id, - cmd->frame->header.lun_id, - (unsigned long)lba_start, (unsigned long)lba_count); - if (!sdev) { - trace_megasas_io_target_not_present(cmd->index, - mfi_frame_desc[cmd->frame->header.frame_cmd], - cmd->frame->header.target_id, cmd->frame->header.lun_id); - return MFI_STAT_DEVICE_NOT_FOUND; - } - - if (cmd->frame->header.cdb_len > 16) { - trace_megasas_scsi_invalid_cdb_len( - mfi_frame_desc[cmd->frame->header.frame_cmd], 1, - cmd->frame->header.target_id, cmd->frame->header.lun_id, - cmd->frame->header.cdb_len); - megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE)); - cmd->frame->header.scsi_status = CHECK_CONDITION; - s->event_count++; - return MFI_STAT_SCSI_DONE_WITH_ERROR; - } - - cmd->iov_size = lba_count * sdev->blocksize; - if (megasas_map_sgl(s, cmd, &cmd->frame->io.sgl)) { - megasas_write_sense(cmd, SENSE_CODE(TARGET_FAILURE)); - cmd->frame->header.scsi_status = CHECK_CONDITION; - s->event_count++; - return MFI_STAT_SCSI_DONE_WITH_ERROR; - } - - megasas_encode_lba(cdb, lba_start, lba_count, is_write); - cmd->req = scsi_req_new(sdev, cmd->index, - cmd->frame->header.lun_id, cdb, cmd); - if (!cmd->req) { - trace_megasas_scsi_req_alloc_failed( - mfi_frame_desc[cmd->frame->header.frame_cmd], - cmd->frame->header.target_id, cmd->frame->header.lun_id); - megasas_write_sense(cmd, SENSE_CODE(NO_SENSE)); - cmd->frame->header.scsi_status = BUSY; - s->event_count++; - return MFI_STAT_SCSI_DONE_WITH_ERROR; - } - len = megasas_enqueue_req(cmd, is_write); - if (len > 0) { - if (is_write) { - trace_megasas_io_write_start(cmd->index, lba_start, lba_count, len); - } else { - trace_megasas_io_read_start(cmd->index, lba_start, lba_count, len); - } - } - return MFI_STAT_INVALID_STATUS; -} - -static int megasas_finish_internal_command(MegasasCmd *cmd, - SCSIRequest *req, size_t resid) -{ - int retval = MFI_STAT_INVALID_CMD; - - if (cmd->frame->header.frame_cmd == MFI_CMD_DCMD) { - cmd->iov_size -= resid; - retval = megasas_finish_internal_dcmd(cmd, req); - } - return retval; -} - -static QEMUSGList *megasas_get_sg_list(SCSIRequest *req) -{ - MegasasCmd *cmd = req->hba_private; - - if (cmd->frame->header.frame_cmd == MFI_CMD_DCMD) { - return NULL; - } else { - return &cmd->qsg; - } -} - -static void megasas_xfer_complete(SCSIRequest *req, uint32_t len) -{ - MegasasCmd *cmd = req->hba_private; - uint8_t *buf; - uint32_t opcode; - - trace_megasas_io_complete(cmd->index, len); - - if (cmd->frame->header.frame_cmd != MFI_CMD_DCMD) { - scsi_req_continue(req); - return; - } - - buf = scsi_req_get_buf(req); - opcode = le32_to_cpu(cmd->frame->dcmd.opcode); - if (opcode == MFI_DCMD_PD_GET_INFO && cmd->iov_buf) { - struct mfi_pd_info *info = cmd->iov_buf; - - if (info->inquiry_data[0] == 0x7f) { - memset(info->inquiry_data, 0, sizeof(info->inquiry_data)); - memcpy(info->inquiry_data, buf, len); - } else if (info->vpd_page83[0] == 0x7f) { - memset(info->vpd_page83, 0, sizeof(info->vpd_page83)); - memcpy(info->vpd_page83, buf, len); - } - scsi_req_continue(req); - } else if (opcode == MFI_DCMD_LD_GET_INFO) { - struct mfi_ld_info *info = cmd->iov_buf; - - if (cmd->iov_buf) { - memcpy(info->vpd_page83, buf, sizeof(info->vpd_page83)); - scsi_req_continue(req); - } - } -} - -static void megasas_command_complete(SCSIRequest *req, uint32_t status, - size_t resid) -{ - MegasasCmd *cmd = req->hba_private; - uint8_t cmd_status = MFI_STAT_OK; - - trace_megasas_command_complete(cmd->index, status, resid); - - if (cmd->req != req) { - /* - * Internal command complete - */ - cmd_status = megasas_finish_internal_command(cmd, req, resid); - if (cmd_status == MFI_STAT_INVALID_STATUS) { - return; - } - } else { - req->status = status; - trace_megasas_scsi_complete(cmd->index, req->status, - cmd->iov_size, req->cmd.xfer); - if (req->status != GOOD) { - cmd_status = MFI_STAT_SCSI_DONE_WITH_ERROR; - } - if (req->status == CHECK_CONDITION) { - megasas_copy_sense(cmd); - } - - megasas_unmap_sgl(cmd); - cmd->frame->header.scsi_status = req->status; - scsi_req_unref(cmd->req); - cmd->req = NULL; - } - cmd->frame->header.cmd_status = cmd_status; - megasas_unmap_frame(cmd->state, cmd); - megasas_complete_frame(cmd->state, cmd->context); -} - -static void megasas_command_cancel(SCSIRequest *req) -{ - MegasasCmd *cmd = req->hba_private; - - if (cmd) { - megasas_abort_command(cmd); - } else { - scsi_req_unref(req); - } -} - -static int megasas_handle_abort(MegasasState *s, MegasasCmd *cmd) -{ - uint64_t abort_ctx = le64_to_cpu(cmd->frame->abort.abort_context); - hwaddr abort_addr, addr_hi, addr_lo; - MegasasCmd *abort_cmd; - - addr_hi = le32_to_cpu(cmd->frame->abort.abort_mfi_addr_hi); - addr_lo = le32_to_cpu(cmd->frame->abort.abort_mfi_addr_lo); - abort_addr = ((uint64_t)addr_hi << 32) | addr_lo; - - abort_cmd = megasas_lookup_frame(s, abort_addr); - if (!abort_cmd) { - trace_megasas_abort_no_cmd(cmd->index, abort_ctx); - s->event_count++; - return MFI_STAT_OK; - } - if (!megasas_use_queue64(s)) { - abort_ctx &= (uint64_t)0xFFFFFFFF; - } - if (abort_cmd->context != abort_ctx) { - trace_megasas_abort_invalid_context(cmd->index, abort_cmd->index, - abort_cmd->context); - s->event_count++; - return MFI_STAT_ABORT_NOT_POSSIBLE; - } - trace_megasas_abort_frame(cmd->index, abort_cmd->index); - megasas_abort_command(abort_cmd); - if (!s->event_cmd || abort_cmd != s->event_cmd) { - s->event_cmd = NULL; - } - s->event_count++; - return MFI_STAT_OK; -} - -static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr, - uint32_t frame_count) -{ - uint8_t frame_status = MFI_STAT_INVALID_CMD; - uint64_t frame_context; - MegasasCmd *cmd; - - /* - * Always read 64bit context, top bits will be - * masked out if required in megasas_enqueue_frame() - */ - frame_context = megasas_frame_get_context(s, frame_addr); - - cmd = megasas_enqueue_frame(s, frame_addr, frame_context, frame_count); - if (!cmd) { - /* reply queue full */ - trace_megasas_frame_busy(frame_addr); - megasas_frame_set_scsi_status(s, frame_addr, BUSY); - megasas_frame_set_cmd_status(s, frame_addr, MFI_STAT_SCSI_DONE_WITH_ERROR); - megasas_complete_frame(s, frame_context); - s->event_count++; - return; - } - switch (cmd->frame->header.frame_cmd) { - case MFI_CMD_INIT: - frame_status = megasas_init_firmware(s, cmd); - break; - case MFI_CMD_DCMD: - frame_status = megasas_handle_dcmd(s, cmd); - break; - case MFI_CMD_ABORT: - frame_status = megasas_handle_abort(s, cmd); - break; - case MFI_CMD_PD_SCSI_IO: - frame_status = megasas_handle_scsi(s, cmd, 0); - break; - case MFI_CMD_LD_SCSI_IO: - frame_status = megasas_handle_scsi(s, cmd, 1); - break; - case MFI_CMD_LD_READ: - case MFI_CMD_LD_WRITE: - frame_status = megasas_handle_io(s, cmd); - break; - default: - trace_megasas_unhandled_frame_cmd(cmd->index, - cmd->frame->header.frame_cmd); - s->event_count++; - break; - } - if (frame_status != MFI_STAT_INVALID_STATUS) { - if (cmd->frame) { - cmd->frame->header.cmd_status = frame_status; - } else { - megasas_frame_set_cmd_status(s, frame_addr, frame_status); - } - megasas_unmap_frame(s, cmd); - megasas_complete_frame(s, cmd->context); - } -} - -static uint64_t megasas_mmio_read(void *opaque, hwaddr addr, - unsigned size) -{ - MegasasState *s = opaque; - PCIDevice *pci_dev = PCI_DEVICE(s); - MegasasBaseClass *base_class = MEGASAS_DEVICE_GET_CLASS(s); - uint32_t retval = 0; - - switch (addr) { - case MFI_IDB: - retval = 0; - trace_megasas_mmio_readl("MFI_IDB", retval); - break; - case MFI_OMSG0: - case MFI_OSP0: - retval = (msix_present(pci_dev) ? MFI_FWSTATE_MSIX_SUPPORTED : 0) | - (s->fw_state & MFI_FWSTATE_MASK) | - ((s->fw_sge & 0xff) << 16) | - (s->fw_cmds & 0xFFFF); - trace_megasas_mmio_readl(addr == MFI_OMSG0 ? "MFI_OMSG0" : "MFI_OSP0", - retval); - break; - case MFI_OSTS: - if (megasas_intr_enabled(s) && s->doorbell) { - retval = base_class->osts; - } - trace_megasas_mmio_readl("MFI_OSTS", retval); - break; - case MFI_OMSK: - retval = s->intr_mask; - trace_megasas_mmio_readl("MFI_OMSK", retval); - break; - case MFI_ODCR0: - retval = s->doorbell ? 1 : 0; - trace_megasas_mmio_readl("MFI_ODCR0", retval); - break; - case MFI_DIAG: - retval = s->diag; - trace_megasas_mmio_readl("MFI_DIAG", retval); - break; - case MFI_OSP1: - retval = 15; - trace_megasas_mmio_readl("MFI_OSP1", retval); - break; - default: - trace_megasas_mmio_invalid_readl(addr); - break; - } - return retval; -} - -static int adp_reset_seq[] = {0x00, 0x04, 0x0b, 0x02, 0x07, 0x0d}; - -static void megasas_mmio_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - MegasasState *s = opaque; - PCIDevice *pci_dev = PCI_DEVICE(s); - uint64_t frame_addr; - uint32_t frame_count; - int i; - - switch (addr) { - case MFI_IDB: - trace_megasas_mmio_writel("MFI_IDB", val); - if (val & MFI_FWINIT_ABORT) { - /* Abort all pending cmds */ - for (i = 0; i < s->fw_cmds; i++) { - megasas_abort_command(&s->frames[i]); - } - } - if (val & MFI_FWINIT_READY) { - /* move to FW READY */ - megasas_soft_reset(s); - } - if (val & MFI_FWINIT_MFIMODE) { - /* discard MFIs */ - } - if (val & MFI_FWINIT_STOP_ADP) { - /* Terminal error, stop processing */ - s->fw_state = MFI_FWSTATE_FAULT; - } - break; - case MFI_OMSK: - trace_megasas_mmio_writel("MFI_OMSK", val); - s->intr_mask = val; - if (!megasas_intr_enabled(s) && - !msi_enabled(pci_dev) && - !msix_enabled(pci_dev)) { - trace_megasas_irq_lower(); - pci_irq_deassert(pci_dev); - } - if (megasas_intr_enabled(s)) { - if (msix_enabled(pci_dev)) { - trace_megasas_msix_enabled(0); - } else if (msi_enabled(pci_dev)) { - trace_megasas_msi_enabled(0); - } else { - trace_megasas_intr_enabled(); - } - } else { - trace_megasas_intr_disabled(); - megasas_soft_reset(s); - } - break; - case MFI_ODCR0: - trace_megasas_mmio_writel("MFI_ODCR0", val); - s->doorbell = 0; - if (megasas_intr_enabled(s)) { - if (!msix_enabled(pci_dev) && !msi_enabled(pci_dev)) { - trace_megasas_irq_lower(); - pci_irq_deassert(pci_dev); - } - } - break; - case MFI_IQPH: - trace_megasas_mmio_writel("MFI_IQPH", val); - /* Received high 32 bits of a 64 bit MFI frame address */ - s->frame_hi = val; - break; - case MFI_IQPL: - trace_megasas_mmio_writel("MFI_IQPL", val); - /* Received low 32 bits of a 64 bit MFI frame address */ - /* Fallthrough */ - case MFI_IQP: - if (addr == MFI_IQP) { - trace_megasas_mmio_writel("MFI_IQP", val); - /* Received 64 bit MFI frame address */ - s->frame_hi = 0; - } - frame_addr = (val & ~0x1F); - /* Add possible 64 bit offset */ - frame_addr |= ((uint64_t)s->frame_hi << 32); - s->frame_hi = 0; - frame_count = (val >> 1) & 0xF; - megasas_handle_frame(s, frame_addr, frame_count); - break; - case MFI_SEQ: - trace_megasas_mmio_writel("MFI_SEQ", val); - /* Magic sequence to start ADP reset */ - if (adp_reset_seq[s->adp_reset] == val) { - s->adp_reset++; - } else { - s->adp_reset = 0; - s->diag = 0; - } - if (s->adp_reset == 6) { - s->diag = MFI_DIAG_WRITE_ENABLE; - } - break; - case MFI_DIAG: - trace_megasas_mmio_writel("MFI_DIAG", val); - /* ADP reset */ - if ((s->diag & MFI_DIAG_WRITE_ENABLE) && - (val & MFI_DIAG_RESET_ADP)) { - s->diag |= MFI_DIAG_RESET_ADP; - megasas_soft_reset(s); - s->adp_reset = 0; - s->diag = 0; - } - break; - default: - trace_megasas_mmio_invalid_writel(addr, val); - break; - } -} - -static const MemoryRegionOps megasas_mmio_ops = { - .read = megasas_mmio_read, - .write = megasas_mmio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 8, - .max_access_size = 8, - } -}; - -static uint64_t megasas_port_read(void *opaque, hwaddr addr, - unsigned size) -{ - return megasas_mmio_read(opaque, addr & 0xff, size); -} - -static void megasas_port_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - megasas_mmio_write(opaque, addr & 0xff, val, size); -} - -static const MemoryRegionOps megasas_port_ops = { - .read = megasas_port_read, - .write = megasas_port_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - } -}; - -static uint64_t megasas_queue_read(void *opaque, hwaddr addr, - unsigned size) -{ - return 0; -} - -static void megasas_queue_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - return; -} - -static const MemoryRegionOps megasas_queue_ops = { - .read = megasas_queue_read, - .write = megasas_queue_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 8, - .max_access_size = 8, - } -}; - -static void megasas_soft_reset(MegasasState *s) -{ - int i; - MegasasCmd *cmd; - - trace_megasas_reset(s->fw_state); - for (i = 0; i < s->fw_cmds; i++) { - cmd = &s->frames[i]; - megasas_abort_command(cmd); - } - if (s->fw_state == MFI_FWSTATE_READY) { - BusChild *kid; - - /* - * The EFI firmware doesn't handle UA, - * so we need to clear the Power On/Reset UA - * after the initial reset. - */ - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *sdev = SCSI_DEVICE(kid->child); - - sdev->unit_attention = SENSE_CODE(NO_SENSE); - scsi_device_unit_attention_reported(sdev); - } - } - megasas_reset_frames(s); - s->reply_queue_len = s->fw_cmds; - s->reply_queue_pa = 0; - s->consumer_pa = 0; - s->producer_pa = 0; - s->fw_state = MFI_FWSTATE_READY; - s->doorbell = 0; - s->intr_mask = MEGASAS_INTR_DISABLED_MASK; - s->frame_hi = 0; - s->flags &= ~MEGASAS_MASK_USE_QUEUE64; - s->event_count++; - s->boot_event = s->event_count; -} - -static void megasas_scsi_reset(DeviceState *dev) -{ - MegasasState *s = MEGASAS(dev); - - megasas_soft_reset(s); -} - -static const VMStateDescription vmstate_megasas_gen1 = { - .name = "megasas", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, MegasasState), - VMSTATE_MSIX(parent_obj, MegasasState), - - VMSTATE_INT32(fw_state, MegasasState), - VMSTATE_INT32(intr_mask, MegasasState), - VMSTATE_INT32(doorbell, MegasasState), - VMSTATE_UINT64(reply_queue_pa, MegasasState), - VMSTATE_UINT64(consumer_pa, MegasasState), - VMSTATE_UINT64(producer_pa, MegasasState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_megasas_gen2 = { - .name = "megasas-gen2", - .version_id = 0, - .minimum_version_id = 0, - .minimum_version_id_old = 0, - .fields = (VMStateField[]) { - VMSTATE_PCIE_DEVICE(parent_obj, MegasasState), - VMSTATE_MSIX(parent_obj, MegasasState), - - VMSTATE_INT32(fw_state, MegasasState), - VMSTATE_INT32(intr_mask, MegasasState), - VMSTATE_INT32(doorbell, MegasasState), - VMSTATE_UINT64(reply_queue_pa, MegasasState), - VMSTATE_UINT64(consumer_pa, MegasasState), - VMSTATE_UINT64(producer_pa, MegasasState), - VMSTATE_END_OF_LIST() - } -}; - -static void megasas_scsi_uninit(PCIDevice *d) -{ - MegasasState *s = MEGASAS(d); - - if (megasas_use_msix(s)) { - msix_uninit(d, &s->mmio_io, &s->mmio_io); - } - if (megasas_use_msi(s)) { - msi_uninit(d); - } -} - -static const struct SCSIBusInfo megasas_scsi_info = { - .tcq = true, - .max_target = MFI_MAX_LD, - .max_lun = 255, - - .transfer_data = megasas_xfer_complete, - .get_sg_list = megasas_get_sg_list, - .complete = megasas_command_complete, - .cancel = megasas_command_cancel, -}; - -static void megasas_scsi_realize(PCIDevice *dev, Error **errp) -{ - DeviceState *d = DEVICE(dev); - MegasasState *s = MEGASAS(dev); - MegasasBaseClass *b = MEGASAS_DEVICE_GET_CLASS(s); - uint8_t *pci_conf; - int i, bar_type; - - pci_conf = dev->config; - - /* PCI latency timer = 0 */ - pci_conf[PCI_LATENCY_TIMER] = 0; - /* Interrupt pin 1 */ - pci_conf[PCI_INTERRUPT_PIN] = 0x01; - - memory_region_init_io(&s->mmio_io, OBJECT(s), &megasas_mmio_ops, s, - "megasas-mmio", 0x4000); - memory_region_init_io(&s->port_io, OBJECT(s), &megasas_port_ops, s, - "megasas-io", 256); - memory_region_init_io(&s->queue_io, OBJECT(s), &megasas_queue_ops, s, - "megasas-queue", 0x40000); - - if (megasas_use_msi(s) && - msi_init(dev, 0x50, 1, true, false)) { - s->flags &= ~MEGASAS_MASK_USE_MSI; - } - if (megasas_use_msix(s) && - msix_init(dev, 15, &s->mmio_io, b->mmio_bar, 0x2000, - &s->mmio_io, b->mmio_bar, 0x3800, 0x68)) { - s->flags &= ~MEGASAS_MASK_USE_MSIX; - } - if (pci_is_express(dev)) { - pcie_endpoint_cap_init(dev, 0xa0); - } - - bar_type = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64; - pci_register_bar(dev, b->ioport_bar, - PCI_BASE_ADDRESS_SPACE_IO, &s->port_io); - pci_register_bar(dev, b->mmio_bar, bar_type, &s->mmio_io); - pci_register_bar(dev, 3, bar_type, &s->queue_io); - - if (megasas_use_msix(s)) { - msix_vector_use(dev, 0); - } - - s->fw_state = MFI_FWSTATE_READY; - 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); - } - if (!s->hba_serial) { - s->hba_serial = g_strdup(MEGASAS_HBA_SERIAL); - } - if (s->fw_sge >= MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE) { - s->fw_sge = MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE; - } else if (s->fw_sge >= 128 - MFI_PASS_FRAME_SIZE) { - s->fw_sge = 128 - MFI_PASS_FRAME_SIZE; - } else { - s->fw_sge = 64 - MFI_PASS_FRAME_SIZE; - } - if (s->fw_cmds > MEGASAS_MAX_FRAMES) { - s->fw_cmds = MEGASAS_MAX_FRAMES; - } - trace_megasas_init(s->fw_sge, s->fw_cmds, - megasas_is_jbod(s) ? "jbod" : "raid"); - - if (megasas_is_jbod(s)) { - s->fw_luns = MFI_MAX_SYS_PDS; - } else { - s->fw_luns = MFI_MAX_LD; - } - s->producer_pa = 0; - s->consumer_pa = 0; - for (i = 0; i < s->fw_cmds; i++) { - s->frames[i].index = i; - s->frames[i].context = -1; - s->frames[i].pa = 0; - s->frames[i].state = s; - } - - scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), - &megasas_scsi_info, NULL); - if (!d->hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, errp); - } -} - -static Property megasas_properties_gen1[] = { - DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge, - MEGASAS_DEFAULT_SGE), - DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds, - MEGASAS_DEFAULT_FRAMES), - DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial), - DEFINE_PROP_UINT64("sas_address", MegasasState, sas_addr, 0), - DEFINE_PROP_BIT("use_msi", MegasasState, flags, - MEGASAS_FLAG_USE_MSI, false), - DEFINE_PROP_BIT("use_msix", MegasasState, flags, - MEGASAS_FLAG_USE_MSIX, false), - DEFINE_PROP_BIT("use_jbod", MegasasState, flags, - MEGASAS_FLAG_USE_JBOD, false), - DEFINE_PROP_END_OF_LIST(), -}; - -static Property megasas_properties_gen2[] = { - DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge, - MEGASAS_DEFAULT_SGE), - DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds, - MEGASAS_GEN2_DEFAULT_FRAMES), - DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial), - DEFINE_PROP_UINT64("sas_address", MegasasState, sas_addr, 0), - DEFINE_PROP_BIT("use_msi", MegasasState, flags, - MEGASAS_FLAG_USE_MSI, true), - DEFINE_PROP_BIT("use_msix", MegasasState, flags, - MEGASAS_FLAG_USE_MSIX, true), - DEFINE_PROP_BIT("use_jbod", MegasasState, flags, - MEGASAS_FLAG_USE_JBOD, false), - DEFINE_PROP_END_OF_LIST(), -}; - -typedef struct MegasasInfo { - const char *name; - const char *desc; - const char *product_name; - const char *product_version; - uint16_t device_id; - uint16_t subsystem_id; - int ioport_bar; - int mmio_bar; - bool is_express; - int osts; - const VMStateDescription *vmsd; - Property *props; -} MegasasInfo; - -static struct MegasasInfo megasas_devices[] = { - { - .name = TYPE_MEGASAS_GEN1, - .desc = "LSI MegaRAID SAS 1078", - .product_name = "LSI MegaRAID SAS 8708EM2", - .product_version = MEGASAS_VERSION_GEN1, - .device_id = PCI_DEVICE_ID_LSI_SAS1078, - .subsystem_id = 0x1013, - .ioport_bar = 2, - .mmio_bar = 0, - .osts = MFI_1078_RM | 1, - .is_express = false, - .vmsd = &vmstate_megasas_gen1, - .props = megasas_properties_gen1, - },{ - .name = TYPE_MEGASAS_GEN2, - .desc = "LSI MegaRAID SAS 2108", - .product_name = "LSI MegaRAID SAS 9260-8i", - .product_version = MEGASAS_VERSION_GEN2, - .device_id = PCI_DEVICE_ID_LSI_SAS0079, - .subsystem_id = 0x9261, - .ioport_bar = 0, - .mmio_bar = 1, - .osts = MFI_GEN2_RM, - .is_express = true, - .vmsd = &vmstate_megasas_gen2, - .props = megasas_properties_gen2, - } -}; - -static void megasas_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); - MegasasBaseClass *e = MEGASAS_DEVICE_CLASS(oc); - const MegasasInfo *info = data; - - pc->realize = megasas_scsi_realize; - pc->exit = megasas_scsi_uninit; - pc->vendor_id = PCI_VENDOR_ID_LSI_LOGIC; - pc->device_id = info->device_id; - pc->subsystem_vendor_id = PCI_VENDOR_ID_LSI_LOGIC; - pc->subsystem_id = info->subsystem_id; - pc->class_id = PCI_CLASS_STORAGE_RAID; - pc->is_express = info->is_express; - e->mmio_bar = info->mmio_bar; - e->ioport_bar = info->ioport_bar; - e->osts = info->osts; - e->product_name = info->product_name; - e->product_version = info->product_version; - dc->props = info->props; - dc->reset = megasas_scsi_reset; - dc->vmsd = info->vmsd; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->desc = info->desc; -} - -static const TypeInfo megasas_info = { - .name = TYPE_MEGASAS_BASE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(MegasasState), - .class_size = sizeof(MegasasBaseClass), - .abstract = true, -}; - -static void megasas_register_types(void) -{ - int i; - - type_register_static(&megasas_info); - for (i = 0; i < ARRAY_SIZE(megasas_devices); i++) { - const MegasasInfo *info = &megasas_devices[i]; - TypeInfo type_info = {}; - - type_info.name = info->name; - type_info.parent = TYPE_MEGASAS_BASE; - type_info.class_data = (void *)info; - type_info.class_init = megasas_class_init; - - type_register(&type_info); - } -} - -type_init(megasas_register_types) diff --git a/qemu/hw/scsi/mfi.h b/qemu/hw/scsi/mfi.h deleted file mode 100644 index 29d41775d..000000000 --- a/qemu/hw/scsi/mfi.h +++ /dev/null @@ -1,1272 +0,0 @@ -/* - * NetBSD header file, copied from - * http://gitorious.org/freebsd/freebsd/blobs/HEAD/sys/dev/mfi/mfireg.h - */ -/*- - * Copyright (c) 2006 IronPort Systems - * Copyright (c) 2007 LSI Corp. - * Copyright (c) 2007 Rajesh Prabhakaran. - * 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 the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef MFI_REG_H -#define MFI_REG_H - -/* - * MegaRAID SAS MFI firmware definitions - */ - -/* - * Start with the register set. All registers are 32 bits wide. - * The usual Intel IOP style setup. - */ -#define MFI_IMSG0 0x10 /* Inbound message 0 */ -#define MFI_IMSG1 0x14 /* Inbound message 1 */ -#define MFI_OMSG0 0x18 /* Outbound message 0 */ -#define MFI_OMSG1 0x1c /* Outbound message 1 */ -#define MFI_IDB 0x20 /* Inbound doorbell */ -#define MFI_ISTS 0x24 /* Inbound interrupt status */ -#define MFI_IMSK 0x28 /* Inbound interrupt mask */ -#define MFI_ODB 0x2c /* Outbound doorbell */ -#define MFI_OSTS 0x30 /* Outbound interrupt status */ -#define MFI_OMSK 0x34 /* Outbound interrupt mask */ -#define MFI_IQP 0x40 /* Inbound queue port */ -#define MFI_OQP 0x44 /* Outbound queue port */ - -/* - * 1078 specific related register - */ -#define MFI_ODR0 0x9c /* outbound doorbell register0 */ -#define MFI_ODCR0 0xa0 /* outbound doorbell clear register0 */ -#define MFI_OSP0 0xb0 /* outbound scratch pad0 */ -#define MFI_OSP1 0xb4 /* outbound scratch pad1 */ -#define MFI_IQPL 0xc0 /* Inbound queue port (low bytes) */ -#define MFI_IQPH 0xc4 /* Inbound queue port (high bytes) */ -#define MFI_DIAG 0xf8 /* Host diag */ -#define MFI_SEQ 0xfc /* Sequencer offset */ -#define MFI_1078_EIM 0x80000004 /* 1078 enable intrrupt mask */ -#define MFI_RMI 0x2 /* reply message interrupt */ -#define MFI_1078_RM 0x80000000 /* reply 1078 message interrupt */ -#define MFI_ODC 0x4 /* outbound doorbell change interrupt */ - -/* - * gen2 specific changes - */ -#define MFI_GEN2_EIM 0x00000005 /* gen2 enable interrupt mask */ -#define MFI_GEN2_RM 0x00000001 /* reply gen2 message interrupt */ - -/* - * skinny specific changes - */ -#define MFI_SKINNY_IDB 0x00 /* Inbound doorbell is at 0x00 for skinny */ -#define MFI_SKINNY_RM 0x00000001 /* reply skinny message interrupt */ - -/* Bits for MFI_OSTS */ -#define MFI_OSTS_INTR_VALID 0x00000002 - -/* - * Firmware state values. Found in OMSG0 during initialization. - */ -#define MFI_FWSTATE_MASK 0xf0000000 -#define MFI_FWSTATE_UNDEFINED 0x00000000 -#define MFI_FWSTATE_BB_INIT 0x10000000 -#define MFI_FWSTATE_FW_INIT 0x40000000 -#define MFI_FWSTATE_WAIT_HANDSHAKE 0x60000000 -#define MFI_FWSTATE_FW_INIT_2 0x70000000 -#define MFI_FWSTATE_DEVICE_SCAN 0x80000000 -#define MFI_FWSTATE_BOOT_MSG_PENDING 0x90000000 -#define MFI_FWSTATE_FLUSH_CACHE 0xa0000000 -#define MFI_FWSTATE_READY 0xb0000000 -#define MFI_FWSTATE_OPERATIONAL 0xc0000000 -#define MFI_FWSTATE_FAULT 0xf0000000 -#define MFI_FWSTATE_MAXSGL_MASK 0x00ff0000 -#define MFI_FWSTATE_MAXCMD_MASK 0x0000ffff -#define MFI_FWSTATE_MSIX_SUPPORTED 0x04000000 -#define MFI_FWSTATE_HOSTMEMREQD_MASK 0x08000000 - -/* - * Control bits to drive the card to ready state. These go into the IDB - * register. - */ -#define MFI_FWINIT_ABORT 0x00000001 /* Abort all pending commands */ -#define MFI_FWINIT_READY 0x00000002 /* Move from operational to ready */ -#define MFI_FWINIT_MFIMODE 0x00000004 /* unknown */ -#define MFI_FWINIT_CLEAR_HANDSHAKE 0x00000008 /* Respond to WAIT_HANDSHAKE */ -#define MFI_FWINIT_HOTPLUG 0x00000010 -#define MFI_FWINIT_STOP_ADP 0x00000020 /* Move to operational, stop */ -#define MFI_FWINIT_ADP_RESET 0x00000040 /* Reset ADP */ - -/* - * Control bits for the DIAG register - */ -#define MFI_DIAG_WRITE_ENABLE 0x00000080 -#define MFI_DIAG_RESET_ADP 0x00000004 - -/* MFI Commands */ -typedef enum { - MFI_CMD_INIT = 0x00, - MFI_CMD_LD_READ, - MFI_CMD_LD_WRITE, - MFI_CMD_LD_SCSI_IO, - MFI_CMD_PD_SCSI_IO, - MFI_CMD_DCMD, - MFI_CMD_ABORT, - MFI_CMD_SMP, - MFI_CMD_STP -} mfi_cmd_t; - -/* Direct commands */ -typedef enum { - MFI_DCMD_CTRL_MFI_HOST_MEM_ALLOC = 0x0100e100, - MFI_DCMD_CTRL_GET_INFO = 0x01010000, - MFI_DCMD_CTRL_GET_PROPERTIES = 0x01020100, - MFI_DCMD_CTRL_SET_PROPERTIES = 0x01020200, - MFI_DCMD_CTRL_ALARM = 0x01030000, - MFI_DCMD_CTRL_ALARM_GET = 0x01030100, - MFI_DCMD_CTRL_ALARM_ENABLE = 0x01030200, - MFI_DCMD_CTRL_ALARM_DISABLE = 0x01030300, - MFI_DCMD_CTRL_ALARM_SILENCE = 0x01030400, - MFI_DCMD_CTRL_ALARM_TEST = 0x01030500, - MFI_DCMD_CTRL_EVENT_GETINFO = 0x01040100, - MFI_DCMD_CTRL_EVENT_CLEAR = 0x01040200, - MFI_DCMD_CTRL_EVENT_GET = 0x01040300, - MFI_DCMD_CTRL_EVENT_COUNT = 0x01040400, - MFI_DCMD_CTRL_EVENT_WAIT = 0x01040500, - MFI_DCMD_CTRL_SHUTDOWN = 0x01050000, - MFI_DCMD_HIBERNATE_STANDBY = 0x01060000, - MFI_DCMD_CTRL_GET_TIME = 0x01080101, - MFI_DCMD_CTRL_SET_TIME = 0x01080102, - MFI_DCMD_CTRL_BIOS_DATA_GET = 0x010c0100, - MFI_DCMD_CTRL_BIOS_DATA_SET = 0x010c0200, - MFI_DCMD_CTRL_FACTORY_DEFAULTS = 0x010d0000, - MFI_DCMD_CTRL_MFC_DEFAULTS_GET = 0x010e0201, - MFI_DCMD_CTRL_MFC_DEFAULTS_SET = 0x010e0202, - MFI_DCMD_CTRL_CACHE_FLUSH = 0x01101000, - MFI_DCMD_PD_GET_LIST = 0x02010000, - MFI_DCMD_PD_LIST_QUERY = 0x02010100, - MFI_DCMD_PD_GET_INFO = 0x02020000, - MFI_DCMD_PD_STATE_SET = 0x02030100, - MFI_DCMD_PD_REBUILD = 0x02040100, - MFI_DCMD_PD_BLINK = 0x02070100, - MFI_DCMD_PD_UNBLINK = 0x02070200, - MFI_DCMD_LD_GET_LIST = 0x03010000, - MFI_DCMD_LD_LIST_QUERY = 0x03010100, - MFI_DCMD_LD_GET_INFO = 0x03020000, - MFI_DCMD_LD_GET_PROP = 0x03030000, - MFI_DCMD_LD_SET_PROP = 0x03040000, - MFI_DCMD_LD_DELETE = 0x03090000, - MFI_DCMD_CFG_READ = 0x04010000, - MFI_DCMD_CFG_ADD = 0x04020000, - MFI_DCMD_CFG_CLEAR = 0x04030000, - MFI_DCMD_CFG_FOREIGN_READ = 0x04060100, - MFI_DCMD_CFG_FOREIGN_IMPORT = 0x04060400, - MFI_DCMD_BBU_STATUS = 0x05010000, - MFI_DCMD_BBU_CAPACITY_INFO = 0x05020000, - MFI_DCMD_BBU_DESIGN_INFO = 0x05030000, - MFI_DCMD_BBU_PROP_GET = 0x05050100, - MFI_DCMD_CLUSTER = 0x08000000, - MFI_DCMD_CLUSTER_RESET_ALL = 0x08010100, - MFI_DCMD_CLUSTER_RESET_LD = 0x08010200 -} mfi_dcmd_t; - -/* Modifiers for MFI_DCMD_CTRL_FLUSHCACHE */ -#define MFI_FLUSHCACHE_CTRL 0x01 -#define MFI_FLUSHCACHE_DISK 0x02 - -/* Modifiers for MFI_DCMD_CTRL_SHUTDOWN */ -#define MFI_SHUTDOWN_SPINDOWN 0x01 - -/* - * MFI Frame flags - */ -typedef enum { - MFI_FRAME_DONT_POST_IN_REPLY_QUEUE = 0x0001, - MFI_FRAME_SGL64 = 0x0002, - MFI_FRAME_SENSE64 = 0x0004, - MFI_FRAME_DIR_WRITE = 0x0008, - MFI_FRAME_DIR_READ = 0x0010, - MFI_FRAME_IEEE_SGL = 0x0020, -} mfi_frame_flags; - -/* MFI Status codes */ -typedef enum { - MFI_STAT_OK = 0x00, - MFI_STAT_INVALID_CMD, - MFI_STAT_INVALID_DCMD, - MFI_STAT_INVALID_PARAMETER, - MFI_STAT_INVALID_SEQUENCE_NUMBER, - MFI_STAT_ABORT_NOT_POSSIBLE, - MFI_STAT_APP_HOST_CODE_NOT_FOUND, - MFI_STAT_APP_IN_USE, - MFI_STAT_APP_NOT_INITIALIZED, - MFI_STAT_ARRAY_INDEX_INVALID, - MFI_STAT_ARRAY_ROW_NOT_EMPTY, - MFI_STAT_CONFIG_RESOURCE_CONFLICT, - MFI_STAT_DEVICE_NOT_FOUND, - MFI_STAT_DRIVE_TOO_SMALL, - MFI_STAT_FLASH_ALLOC_FAIL, - MFI_STAT_FLASH_BUSY, - MFI_STAT_FLASH_ERROR = 0x10, - MFI_STAT_FLASH_IMAGE_BAD, - MFI_STAT_FLASH_IMAGE_INCOMPLETE, - MFI_STAT_FLASH_NOT_OPEN, - MFI_STAT_FLASH_NOT_STARTED, - MFI_STAT_FLUSH_FAILED, - MFI_STAT_HOST_CODE_NOT_FOUNT, - MFI_STAT_LD_CC_IN_PROGRESS, - MFI_STAT_LD_INIT_IN_PROGRESS, - MFI_STAT_LD_LBA_OUT_OF_RANGE, - MFI_STAT_LD_MAX_CONFIGURED, - MFI_STAT_LD_NOT_OPTIMAL, - MFI_STAT_LD_RBLD_IN_PROGRESS, - MFI_STAT_LD_RECON_IN_PROGRESS, - MFI_STAT_LD_WRONG_RAID_LEVEL, - MFI_STAT_MAX_SPARES_EXCEEDED, - MFI_STAT_MEMORY_NOT_AVAILABLE = 0x20, - MFI_STAT_MFC_HW_ERROR, - MFI_STAT_NO_HW_PRESENT, - MFI_STAT_NOT_FOUND, - MFI_STAT_NOT_IN_ENCL, - MFI_STAT_PD_CLEAR_IN_PROGRESS, - MFI_STAT_PD_TYPE_WRONG, - MFI_STAT_PR_DISABLED, - MFI_STAT_ROW_INDEX_INVALID, - MFI_STAT_SAS_CONFIG_INVALID_ACTION, - MFI_STAT_SAS_CONFIG_INVALID_DATA, - MFI_STAT_SAS_CONFIG_INVALID_PAGE, - MFI_STAT_SAS_CONFIG_INVALID_TYPE, - MFI_STAT_SCSI_DONE_WITH_ERROR, - MFI_STAT_SCSI_IO_FAILED, - MFI_STAT_SCSI_RESERVATION_CONFLICT, - MFI_STAT_SHUTDOWN_FAILED = 0x30, - MFI_STAT_TIME_NOT_SET, - MFI_STAT_WRONG_STATE, - MFI_STAT_LD_OFFLINE, - MFI_STAT_PEER_NOTIFICATION_REJECTED, - MFI_STAT_PEER_NOTIFICATION_FAILED, - MFI_STAT_RESERVATION_IN_PROGRESS, - MFI_STAT_I2C_ERRORS_DETECTED, - MFI_STAT_PCI_ERRORS_DETECTED, - MFI_STAT_DIAG_FAILED, - MFI_STAT_BOOT_MSG_PENDING, - MFI_STAT_FOREIGN_CONFIG_INCOMPLETE, - MFI_STAT_INVALID_SGL, - MFI_STAT_UNSUPPORTED_HW, - MFI_STAT_CC_SCHEDULE_DISABLED, - MFI_STAT_PD_COPYBACK_IN_PROGRESS, - MFI_STAT_MULTIPLE_PDS_IN_ARRAY = 0x40, - MFI_STAT_FW_DOWNLOAD_ERROR, - MFI_STAT_FEATURE_SECURITY_NOT_ENABLED, - MFI_STAT_LOCK_KEY_ALREADY_EXISTS, - MFI_STAT_LOCK_KEY_BACKUP_NOT_ALLOWED, - MFI_STAT_LOCK_KEY_VERIFY_NOT_ALLOWED, - MFI_STAT_LOCK_KEY_VERIFY_FAILED, - MFI_STAT_LOCK_KEY_REKEY_NOT_ALLOWED, - MFI_STAT_LOCK_KEY_INVALID, - MFI_STAT_LOCK_KEY_ESCROW_INVALID, - MFI_STAT_LOCK_KEY_BACKUP_REQUIRED, - MFI_STAT_SECURE_LD_EXISTS, - MFI_STAT_LD_SECURE_NOT_ALLOWED, - MFI_STAT_REPROVISION_NOT_ALLOWED, - MFI_STAT_PD_SECURITY_TYPE_WRONG, - MFI_STAT_LD_ENCRYPTION_TYPE_INVALID, - MFI_STAT_CONFIG_FDE_NON_FDE_MIX_NOT_ALLOWED = 0x50, - MFI_STAT_CONFIG_LD_ENCRYPTION_TYPE_MIX_NOT_ALLOWED, - MFI_STAT_SECRET_KEY_NOT_ALLOWED, - MFI_STAT_PD_HW_ERRORS_DETECTED, - MFI_STAT_LD_CACHE_PINNED, - MFI_STAT_POWER_STATE_SET_IN_PROGRESS, - MFI_STAT_POWER_STATE_SET_BUSY, - MFI_STAT_POWER_STATE_WRONG, - MFI_STAT_PR_NO_AVAILABLE_PD_FOUND, - MFI_STAT_CTRL_RESET_REQUIRED, - MFI_STAT_LOCK_KEY_EKM_NO_BOOT_AGENT, - MFI_STAT_SNAP_NO_SPACE, - MFI_STAT_SNAP_PARTIAL_FAILURE, - MFI_STAT_UPGRADE_KEY_INCOMPATIBLE, - MFI_STAT_PFK_INCOMPATIBLE, - MFI_STAT_PD_MAX_UNCONFIGURED, - MFI_STAT_IO_METRICS_DISABLED = 0x60, - MFI_STAT_AEC_NOT_STOPPED, - MFI_STAT_PI_TYPE_WRONG, - MFI_STAT_LD_PD_PI_INCOMPATIBLE, - MFI_STAT_PI_NOT_ENABLED, - MFI_STAT_LD_BLOCK_SIZE_MISMATCH, - MFI_STAT_INVALID_STATUS = 0xFF -} mfi_status_t; - -/* Event classes */ -typedef enum { - MFI_EVT_CLASS_DEBUG = -2, - MFI_EVT_CLASS_PROGRESS = -1, - MFI_EVT_CLASS_INFO = 0, - MFI_EVT_CLASS_WARNING = 1, - MFI_EVT_CLASS_CRITICAL = 2, - MFI_EVT_CLASS_FATAL = 3, - MFI_EVT_CLASS_DEAD = 4 -} mfi_evt_class_t; - -/* Event locales */ -typedef enum { - MFI_EVT_LOCALE_LD = 0x0001, - MFI_EVT_LOCALE_PD = 0x0002, - MFI_EVT_LOCALE_ENCL = 0x0004, - MFI_EVT_LOCALE_BBU = 0x0008, - MFI_EVT_LOCALE_SAS = 0x0010, - MFI_EVT_LOCALE_CTRL = 0x0020, - MFI_EVT_LOCALE_CONFIG = 0x0040, - MFI_EVT_LOCALE_CLUSTER = 0x0080, - MFI_EVT_LOCALE_ALL = 0xffff -} mfi_evt_locale_t; - -/* Event args */ -typedef enum { - MR_EVT_ARGS_NONE = 0x00, - MR_EVT_ARGS_CDB_SENSE, - MR_EVT_ARGS_LD, - MR_EVT_ARGS_LD_COUNT, - MR_EVT_ARGS_LD_LBA, - MR_EVT_ARGS_LD_OWNER, - MR_EVT_ARGS_LD_LBA_PD_LBA, - MR_EVT_ARGS_LD_PROG, - MR_EVT_ARGS_LD_STATE, - MR_EVT_ARGS_LD_STRIP, - MR_EVT_ARGS_PD, - MR_EVT_ARGS_PD_ERR, - MR_EVT_ARGS_PD_LBA, - MR_EVT_ARGS_PD_LBA_LD, - MR_EVT_ARGS_PD_PROG, - MR_EVT_ARGS_PD_STATE, - MR_EVT_ARGS_PCI, - MR_EVT_ARGS_RATE, - MR_EVT_ARGS_STR, - MR_EVT_ARGS_TIME, - MR_EVT_ARGS_ECC, - MR_EVT_ARGS_LD_PROP, - MR_EVT_ARGS_PD_SPARE, - MR_EVT_ARGS_PD_INDEX, - MR_EVT_ARGS_DIAG_PASS, - MR_EVT_ARGS_DIAG_FAIL, - MR_EVT_ARGS_PD_LBA_LBA, - MR_EVT_ARGS_PORT_PHY, - MR_EVT_ARGS_PD_MISSING, - MR_EVT_ARGS_PD_ADDRESS, - MR_EVT_ARGS_BITMAP, - MR_EVT_ARGS_CONNECTOR, - MR_EVT_ARGS_PD_PD, - MR_EVT_ARGS_PD_FRU, - MR_EVT_ARGS_PD_PATHINFO, - MR_EVT_ARGS_PD_POWER_STATE, - MR_EVT_ARGS_GENERIC, -} mfi_evt_args; - -/* Event codes */ -#define MR_EVT_CFG_CLEARED 0x0004 -#define MR_EVT_CTRL_SHUTDOWN 0x002a -#define MR_EVT_LD_STATE_CHANGE 0x0051 -#define MR_EVT_PD_INSERTED 0x005b -#define MR_EVT_PD_REMOVED 0x0070 -#define MR_EVT_PD_STATE_CHANGED 0x0072 -#define MR_EVT_LD_CREATED 0x008a -#define MR_EVT_LD_DELETED 0x008b -#define MR_EVT_FOREIGN_CFG_IMPORTED 0x00db -#define MR_EVT_LD_OFFLINE 0x00fc -#define MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED 0x0152 - -typedef enum { - MR_LD_CACHE_WRITE_BACK = 0x01, - MR_LD_CACHE_WRITE_ADAPTIVE = 0x02, - MR_LD_CACHE_READ_AHEAD = 0x04, - MR_LD_CACHE_READ_ADAPTIVE = 0x08, - MR_LD_CACHE_WRITE_CACHE_BAD_BBU = 0x10, - MR_LD_CACHE_ALLOW_WRITE_CACHE = 0x20, - MR_LD_CACHE_ALLOW_READ_CACHE = 0x40 -} mfi_ld_cache; - -typedef enum { - MR_PD_CACHE_UNCHANGED = 0, - MR_PD_CACHE_ENABLE = 1, - MR_PD_CACHE_DISABLE = 2 -} mfi_pd_cache; - -typedef enum { - MR_PD_QUERY_TYPE_ALL = 0, - MR_PD_QUERY_TYPE_STATE = 1, - MR_PD_QUERY_TYPE_POWER_STATE = 2, - MR_PD_QUERY_TYPE_MEDIA_TYPE = 3, - MR_PD_QUERY_TYPE_SPEED = 4, - MR_PD_QUERY_TYPE_EXPOSED_TO_HOST = 5, /*query for system drives */ -} mfi_pd_query_type; - -typedef enum { - MR_LD_QUERY_TYPE_ALL = 0, - MR_LD_QUERY_TYPE_EXPOSED_TO_HOST = 1, - MR_LD_QUERY_TYPE_USED_TGT_IDS = 2, - MR_LD_QUERY_TYPE_CLUSTER_ACCESS = 3, - MR_LD_QUERY_TYPE_CLUSTER_LOCALE = 4, -} mfi_ld_query_type; - -/* - * Other propertities and definitions - */ -#define MFI_MAX_PD_CHANNELS 2 -#define MFI_MAX_LD_CHANNELS 2 -#define MFI_MAX_CHANNELS (MFI_MAX_PD_CHANNELS + MFI_MAX_LD_CHANNELS) -#define MFI_MAX_CHANNEL_DEVS 128 -#define MFI_DEFAULT_ID -1 -#define MFI_MAX_LUN 8 -#define MFI_MAX_LD 64 - -#define MFI_FRAME_SIZE 64 -#define MFI_MBOX_SIZE 12 - -/* Firmware flashing can take 40s */ -#define MFI_POLL_TIMEOUT_SECS 50 - -/* Allow for speedier math calculations */ -#define MFI_SECTOR_LEN 512 - -/* Scatter Gather elements */ -struct mfi_sg32 { - uint32_t addr; - uint32_t len; -} QEMU_PACKED; - -struct mfi_sg64 { - uint64_t addr; - uint32_t len; -} QEMU_PACKED; - -struct mfi_sg_skinny { - uint64_t addr; - uint32_t len; - uint32_t flag; -} QEMU_PACKED; - -union mfi_sgl { - struct mfi_sg32 sg32[1]; - struct mfi_sg64 sg64[1]; - struct mfi_sg_skinny sg_skinny[1]; -} QEMU_PACKED; - -/* Message frames. All messages have a common header */ -struct mfi_frame_header { - uint8_t frame_cmd; - uint8_t sense_len; - uint8_t cmd_status; - uint8_t scsi_status; - uint8_t target_id; - uint8_t lun_id; - uint8_t cdb_len; - uint8_t sge_count; - uint64_t context; - uint16_t flags; - uint16_t timeout; - uint32_t data_len; -} QEMU_PACKED; - -struct mfi_init_frame { - struct mfi_frame_header header; - uint32_t qinfo_new_addr_lo; - uint32_t qinfo_new_addr_hi; - uint32_t qinfo_old_addr_lo; - uint32_t qinfo_old_addr_hi; - uint32_t reserved[6]; -}; - -#define MFI_IO_FRAME_SIZE 40 -struct mfi_io_frame { - struct mfi_frame_header header; - uint32_t sense_addr_lo; - uint32_t sense_addr_hi; - uint32_t lba_lo; - uint32_t lba_hi; - union mfi_sgl sgl; -} QEMU_PACKED; - -#define MFI_PASS_FRAME_SIZE 48 -struct mfi_pass_frame { - struct mfi_frame_header header; - uint32_t sense_addr_lo; - uint32_t sense_addr_hi; - uint8_t cdb[16]; - union mfi_sgl sgl; -} QEMU_PACKED; - -#define MFI_DCMD_FRAME_SIZE 40 -struct mfi_dcmd_frame { - struct mfi_frame_header header; - uint32_t opcode; - uint8_t mbox[MFI_MBOX_SIZE]; - union mfi_sgl sgl; -} QEMU_PACKED; - -struct mfi_abort_frame { - struct mfi_frame_header header; - uint64_t abort_context; - uint32_t abort_mfi_addr_lo; - uint32_t abort_mfi_addr_hi; - uint32_t reserved1[6]; -} QEMU_PACKED; - -struct mfi_smp_frame { - struct mfi_frame_header header; - uint64_t sas_addr; - union { - struct mfi_sg32 sg32[2]; - struct mfi_sg64 sg64[2]; - } sgl; -} QEMU_PACKED; - -struct mfi_stp_frame { - struct mfi_frame_header header; - uint16_t fis[10]; - uint32_t stp_flags; - union { - struct mfi_sg32 sg32[2]; - struct mfi_sg64 sg64[2]; - } sgl; -} QEMU_PACKED; - -union mfi_frame { - struct mfi_frame_header header; - struct mfi_init_frame init; - struct mfi_io_frame io; - struct mfi_pass_frame pass; - struct mfi_dcmd_frame dcmd; - struct mfi_abort_frame abort; - struct mfi_smp_frame smp; - struct mfi_stp_frame stp; - uint64_t raw[8]; - uint8_t bytes[MFI_FRAME_SIZE]; -}; - -#define MFI_SENSE_LEN 128 -struct mfi_sense { - uint8_t data[MFI_SENSE_LEN]; -}; - -#define MFI_QUEUE_FLAG_CONTEXT64 0x00000002 - -/* The queue init structure that is passed with the init message */ -struct mfi_init_qinfo { - uint32_t flags; - uint32_t rq_entries; - uint32_t rq_addr_lo; - uint32_t rq_addr_hi; - uint32_t pi_addr_lo; - uint32_t pi_addr_hi; - uint32_t ci_addr_lo; - uint32_t ci_addr_hi; -} QEMU_PACKED; - -/* Controller properties */ -struct mfi_ctrl_props { - uint16_t seq_num; - uint16_t pred_fail_poll_interval; - uint16_t intr_throttle_cnt; - uint16_t intr_throttle_timeout; - uint8_t rebuild_rate; - uint8_t patrol_read_rate; - uint8_t bgi_rate; - uint8_t cc_rate; - uint8_t recon_rate; - uint8_t cache_flush_interval; - uint8_t spinup_drv_cnt; - uint8_t spinup_delay; - uint8_t cluster_enable; - uint8_t coercion_mode; - uint8_t alarm_enable; - uint8_t disable_auto_rebuild; - uint8_t disable_battery_warn; - uint8_t ecc_bucket_size; - uint16_t ecc_bucket_leak_rate; - uint8_t restore_hotspare_on_insertion; - uint8_t expose_encl_devices; - uint8_t maintainPdFailHistory; - uint8_t disallowHostRequestReordering; - uint8_t abortCCOnError; - uint8_t loadBalanceMode; - uint8_t disableAutoDetectBackplane; - uint8_t snapVDSpace; - uint32_t OnOffProperties; -/* set TRUE to disable copyBack (0=copyback enabled) */ -#define MFI_CTRL_PROP_CopyBackDisabled (1 << 0) -#define MFI_CTRL_PROP_SMARTerEnabled (1 << 1) -#define MFI_CTRL_PROP_PRCorrectUnconfiguredAreas (1 << 2) -#define MFI_CTRL_PROP_UseFdeOnly (1 << 3) -#define MFI_CTRL_PROP_DisableNCQ (1 << 4) -#define MFI_CTRL_PROP_SSDSMARTerEnabled (1 << 5) -#define MFI_CTRL_PROP_SSDPatrolReadEnabled (1 << 6) -#define MFI_CTRL_PROP_EnableSpinDownUnconfigured (1 << 7) -#define MFI_CTRL_PROP_AutoEnhancedImport (1 << 8) -#define MFI_CTRL_PROP_EnableSecretKeyControl (1 << 9) -#define MFI_CTRL_PROP_DisableOnlineCtrlReset (1 << 10) -#define MFI_CTRL_PROP_AllowBootWithPinnedCache (1 << 11) -#define MFI_CTRL_PROP_DisableSpinDownHS (1 << 12) -#define MFI_CTRL_PROP_EnableJBOD (1 << 13) - - uint8_t autoSnapVDSpace; /* % of source LD to be - * reserved for auto snapshot - * in snapshot repository, for - * metadata and user data - * 1=5%, 2=10%, 3=15% and so on - */ - uint8_t viewSpace; /* snapshot writeable VIEWs - * capacity as a % of source LD - * capacity. 0=READ only - * 1=5%, 2=10%, 3=15% and so on - */ - uint16_t spinDownTime; /* # of idle minutes before device - * is spun down (0=use FW defaults) - */ - uint8_t reserved[24]; -} QEMU_PACKED; - -/* PCI information about the card. */ -struct mfi_info_pci { - uint16_t vendor; - uint16_t device; - uint16_t subvendor; - uint16_t subdevice; - uint8_t reserved[24]; -} QEMU_PACKED; - -/* Host (front end) interface information */ -struct mfi_info_host { - uint8_t type; -#define MFI_INFO_HOST_PCIX 0x01 -#define MFI_INFO_HOST_PCIE 0x02 -#define MFI_INFO_HOST_ISCSI 0x04 -#define MFI_INFO_HOST_SAS3G 0x08 - uint8_t reserved[6]; - uint8_t port_count; - uint64_t port_addr[8]; -} QEMU_PACKED; - -/* Device (back end) interface information */ -struct mfi_info_device { - uint8_t type; -#define MFI_INFO_DEV_SPI 0x01 -#define MFI_INFO_DEV_SAS3G 0x02 -#define MFI_INFO_DEV_SATA1 0x04 -#define MFI_INFO_DEV_SATA3G 0x08 -#define MFI_INFO_DEV_PCIE 0x10 - uint8_t reserved[6]; - uint8_t port_count; - uint64_t port_addr[8]; -} QEMU_PACKED; - -/* Firmware component information */ -struct mfi_info_component { - char name[8]; - char version[32]; - char build_date[16]; - char build_time[16]; -} QEMU_PACKED; - -/* Controller default settings */ -struct mfi_defaults { - uint64_t sas_addr; - uint8_t phy_polarity; - uint8_t background_rate; - uint8_t stripe_size; - uint8_t flush_time; - uint8_t write_back; - uint8_t read_ahead; - uint8_t cache_when_bbu_bad; - uint8_t cached_io; - uint8_t smart_mode; - uint8_t alarm_disable; - uint8_t coercion; - uint8_t zrc_config; - uint8_t dirty_led_shows_drive_activity; - uint8_t bios_continue_on_error; - uint8_t spindown_mode; - uint8_t allowed_device_types; - uint8_t allow_mix_in_enclosure; - uint8_t allow_mix_in_ld; - uint8_t allow_sata_in_cluster; - uint8_t max_chained_enclosures; - uint8_t disable_ctrl_r; - uint8_t enable_web_bios; - uint8_t phy_polarity_split; - uint8_t direct_pd_mapping; - uint8_t bios_enumerate_lds; - uint8_t restored_hot_spare_on_insertion; - uint8_t expose_enclosure_devices; - uint8_t maintain_pd_fail_history; - uint8_t disable_puncture; - uint8_t zero_based_enumeration; - uint8_t disable_preboot_cli; - uint8_t show_drive_led_on_activity; - uint8_t cluster_disable; - uint8_t sas_disable; - uint8_t auto_detect_backplane; - uint8_t fde_only; - uint8_t delay_during_post; - uint8_t resv[19]; -} QEMU_PACKED; - -/* Controller default settings */ -struct mfi_bios_data { - uint16_t boot_target_id; - uint8_t do_not_int_13; - uint8_t continue_on_error; - uint8_t verbose; - uint8_t geometry; - uint8_t expose_all_drives; - uint8_t reserved[56]; - uint8_t check_sum; -} QEMU_PACKED; - -/* SAS (?) controller info, returned from MFI_DCMD_CTRL_GETINFO. */ -struct mfi_ctrl_info { - struct mfi_info_pci pci; - struct mfi_info_host host; - struct mfi_info_device device; - - /* Firmware components that are present and active. */ - uint32_t image_check_word; - uint32_t image_component_count; - struct mfi_info_component image_component[8]; - - /* Firmware components that have been flashed but are inactive */ - uint32_t pending_image_component_count; - struct mfi_info_component pending_image_component[8]; - - uint8_t max_arms; - uint8_t max_spans; - uint8_t max_arrays; - uint8_t max_lds; - char product_name[80]; - char serial_number[32]; - uint32_t hw_present; -#define MFI_INFO_HW_BBU 0x01 -#define MFI_INFO_HW_ALARM 0x02 -#define MFI_INFO_HW_NVRAM 0x04 -#define MFI_INFO_HW_UART 0x08 -#define MFI_INFO_HW_MEM 0x10 -#define MFI_INFO_HW_FLASH 0x20 - uint32_t current_fw_time; - uint16_t max_cmds; - uint16_t max_sg_elements; - uint32_t max_request_size; - uint16_t lds_present; - uint16_t lds_degraded; - uint16_t lds_offline; - uint16_t pd_present; - uint16_t pd_disks_present; - uint16_t pd_disks_pred_failure; - uint16_t pd_disks_failed; - uint16_t nvram_size; - uint16_t memory_size; - uint16_t flash_size; - uint16_t ram_correctable_errors; - uint16_t ram_uncorrectable_errors; - uint8_t cluster_allowed; - uint8_t cluster_active; - uint16_t max_strips_per_io; - - uint32_t raid_levels; -#define MFI_INFO_RAID_0 0x01 -#define MFI_INFO_RAID_1 0x02 -#define MFI_INFO_RAID_5 0x04 -#define MFI_INFO_RAID_1E 0x08 -#define MFI_INFO_RAID_6 0x10 - - uint32_t adapter_ops; -#define MFI_INFO_AOPS_RBLD_RATE 0x0001 -#define MFI_INFO_AOPS_CC_RATE 0x0002 -#define MFI_INFO_AOPS_BGI_RATE 0x0004 -#define MFI_INFO_AOPS_RECON_RATE 0x0008 -#define MFI_INFO_AOPS_PATROL_RATE 0x0010 -#define MFI_INFO_AOPS_ALARM_CONTROL 0x0020 -#define MFI_INFO_AOPS_CLUSTER_SUPPORTED 0x0040 -#define MFI_INFO_AOPS_BBU 0x0080 -#define MFI_INFO_AOPS_SPANNING_ALLOWED 0x0100 -#define MFI_INFO_AOPS_DEDICATED_SPARES 0x0200 -#define MFI_INFO_AOPS_REVERTIBLE_SPARES 0x0400 -#define MFI_INFO_AOPS_FOREIGN_IMPORT 0x0800 -#define MFI_INFO_AOPS_SELF_DIAGNOSTIC 0x1000 -#define MFI_INFO_AOPS_MIXED_ARRAY 0x2000 -#define MFI_INFO_AOPS_GLOBAL_SPARES 0x4000 - - uint32_t ld_ops; -#define MFI_INFO_LDOPS_READ_POLICY 0x01 -#define MFI_INFO_LDOPS_WRITE_POLICY 0x02 -#define MFI_INFO_LDOPS_IO_POLICY 0x04 -#define MFI_INFO_LDOPS_ACCESS_POLICY 0x08 -#define MFI_INFO_LDOPS_DISK_CACHE_POLICY 0x10 - - struct { - uint8_t min; - uint8_t max; - uint8_t reserved[2]; - } QEMU_PACKED stripe_sz_ops; - - uint32_t pd_ops; -#define MFI_INFO_PDOPS_FORCE_ONLINE 0x01 -#define MFI_INFO_PDOPS_FORCE_OFFLINE 0x02 -#define MFI_INFO_PDOPS_FORCE_REBUILD 0x04 - - uint32_t pd_mix_support; -#define MFI_INFO_PDMIX_SAS 0x01 -#define MFI_INFO_PDMIX_SATA 0x02 -#define MFI_INFO_PDMIX_ENCL 0x04 -#define MFI_INFO_PDMIX_LD 0x08 -#define MFI_INFO_PDMIX_SATA_CLUSTER 0x10 - - uint8_t ecc_bucket_count; - uint8_t reserved2[11]; - struct mfi_ctrl_props properties; - char package_version[0x60]; - uint8_t pad[0x800 - 0x6a0]; -} QEMU_PACKED; - -/* keep track of an event. */ -union mfi_evt { - struct { - uint16_t locale; - uint8_t reserved; - int8_t class; - } members; - uint32_t word; -} QEMU_PACKED; - -/* event log state. */ -struct mfi_evt_log_state { - uint32_t newest_seq_num; - uint32_t oldest_seq_num; - uint32_t clear_seq_num; - uint32_t shutdown_seq_num; - uint32_t boot_seq_num; -} QEMU_PACKED; - -struct mfi_progress { - uint16_t progress; - uint16_t elapsed_seconds; -} QEMU_PACKED; - -struct mfi_evt_ld { - uint16_t target_id; - uint8_t ld_index; - uint8_t reserved; -} QEMU_PACKED; - -struct mfi_evt_pd { - uint16_t device_id; - uint8_t enclosure_index; - uint8_t slot_number; -} QEMU_PACKED; - -/* event detail, returned from MFI_DCMD_CTRL_EVENT_WAIT. */ -struct mfi_evt_detail { - uint32_t seq; - uint32_t time; - uint32_t code; - union mfi_evt class; - uint8_t arg_type; - uint8_t reserved1[15]; - - union { - struct { - struct mfi_evt_pd pd; - uint8_t cdb_len; - uint8_t sense_len; - uint8_t reserved[2]; - uint8_t cdb[16]; - uint8_t sense[64]; - } cdb_sense; - - struct mfi_evt_ld ld; - - struct { - struct mfi_evt_ld ld; - uint64_t count; - } ld_count; - - struct { - uint64_t lba; - struct mfi_evt_ld ld; - } ld_lba; - - struct { - struct mfi_evt_ld ld; - uint32_t pre_owner; - uint32_t new_owner; - } ld_owner; - - struct { - uint64_t ld_lba; - uint64_t pd_lba; - struct mfi_evt_ld ld; - struct mfi_evt_pd pd; - } ld_lba_pd_lba; - - struct { - struct mfi_evt_ld ld; - struct mfi_progress prog; - } ld_prog; - - struct { - struct mfi_evt_ld ld; - uint32_t prev_state; - uint32_t new_state; - } ld_state; - - struct { - uint64_t strip; - struct mfi_evt_ld ld; - } ld_strip; - - struct mfi_evt_pd pd; - - struct { - struct mfi_evt_pd pd; - uint32_t err; - } pd_err; - - struct { - uint64_t lba; - struct mfi_evt_pd pd; - } pd_lba; - - struct { - uint64_t lba; - struct mfi_evt_pd pd; - struct mfi_evt_ld ld; - } pd_lba_ld; - - struct { - struct mfi_evt_pd pd; - struct mfi_progress prog; - } pd_prog; - - struct { - struct mfi_evt_pd ld; - uint32_t prev_state; - uint32_t new_state; - } pd_state; - - struct { - uint16_t venderId; - uint16_t deviceId; - uint16_t subVenderId; - uint16_t subDeviceId; - } pci; - - uint32_t rate; - - char str[96]; - - struct { - uint32_t rtc; - uint16_t elapsedSeconds; - } time; - - struct { - uint32_t ecar; - uint32_t elog; - char str[64]; - } ecc; - - uint8_t b[96]; - uint16_t s[48]; - uint32_t w[24]; - uint64_t d[12]; - } args; - - char description[128]; -} QEMU_PACKED; - -struct mfi_evt_list { - uint32_t count; - uint32_t reserved; - struct mfi_evt_detail event[1]; -} QEMU_PACKED; - -union mfi_pd_ref { - struct { - uint16_t device_id; - uint16_t seq_num; - } v; - uint32_t ref; -} QEMU_PACKED; - -union mfi_pd_ddf_type { - struct { - uint16_t pd_type; -#define MFI_PD_DDF_TYPE_FORCED_PD_GUID (1 << 0) -#define MFI_PD_DDF_TYPE_IN_VD (1 << 1) -#define MFI_PD_DDF_TYPE_IS_GLOBAL_SPARE (1 << 2) -#define MFI_PD_DDF_TYPE_IS_SPARE (1 << 3) -#define MFI_PD_DDF_TYPE_IS_FOREIGN (1 << 4) -#define MFI_PD_DDF_TYPE_INTF_SPI (1 << 12) -#define MFI_PD_DDF_TYPE_INTF_SAS (1 << 13) -#define MFI_PD_DDF_TYPE_INTF_SATA1 (1 << 14) -#define MFI_PD_DDF_TYPE_INTF_SATA3G (1 << 15) - uint16_t reserved; - } ddf; - struct { - uint32_t reserved; - } non_disk; - uint32_t type; -} QEMU_PACKED; - -struct mfi_pd_progress { - uint32_t active; -#define PD_PROGRESS_ACTIVE_REBUILD (1 << 0) -#define PD_PROGRESS_ACTIVE_PATROL (1 << 1) -#define PD_PROGRESS_ACTIVE_CLEAR (1 << 2) - struct mfi_progress rbld; - struct mfi_progress patrol; - struct mfi_progress clear; - struct mfi_progress reserved[4]; -} QEMU_PACKED; - -struct mfi_pd_info { - union mfi_pd_ref ref; - uint8_t inquiry_data[96]; - uint8_t vpd_page83[64]; - uint8_t not_supported; - uint8_t scsi_dev_type; - uint8_t connected_port_bitmap; - uint8_t device_speed; - uint32_t media_err_count; - uint32_t other_err_count; - uint32_t pred_fail_count; - uint32_t last_pred_fail_event_seq_num; - uint16_t fw_state; - uint8_t disable_for_removal; - uint8_t link_speed; - union mfi_pd_ddf_type state; - struct { - uint8_t count; - uint8_t is_path_broken; - uint8_t reserved[6]; - uint64_t sas_addr[4]; - } path_info; - uint64_t raw_size; - uint64_t non_coerced_size; - uint64_t coerced_size; - uint16_t encl_device_id; - uint8_t encl_index; - uint8_t slot_number; - struct mfi_pd_progress prog_info; - uint8_t bad_block_table_full; - uint8_t unusable_in_current_config; - uint8_t vpd_page83_ext[64]; - uint8_t reserved[512-358]; -} QEMU_PACKED; - -struct mfi_pd_address { - uint16_t device_id; - uint16_t encl_device_id; - uint8_t encl_index; - uint8_t slot_number; - uint8_t scsi_dev_type; - uint8_t connect_port_bitmap; - uint64_t sas_addr[2]; -} QEMU_PACKED; - -#define MFI_MAX_SYS_PDS 240 -struct mfi_pd_list { - uint32_t size; - uint32_t count; - struct mfi_pd_address addr[MFI_MAX_SYS_PDS]; -} QEMU_PACKED; - -union mfi_ld_ref { - struct { - uint8_t target_id; - uint8_t reserved; - uint16_t seq; - } v; - uint32_t ref; -} QEMU_PACKED; - -struct mfi_ld_list { - uint32_t ld_count; - uint32_t reserved1; - struct { - union mfi_ld_ref ld; - uint8_t state; - uint8_t reserved2[3]; - uint64_t size; - } ld_list[MFI_MAX_LD]; -} QEMU_PACKED; - -struct mfi_ld_targetid_list { - uint32_t size; - uint32_t ld_count; - uint8_t pad[3]; - uint8_t targetid[MFI_MAX_LD]; -} QEMU_PACKED; - -enum mfi_ld_access { - MFI_LD_ACCESS_RW = 0, - MFI_LD_ACCSSS_RO = 2, - MFI_LD_ACCESS_BLOCKED = 3, -}; -#define MFI_LD_ACCESS_MASK 3 - -enum mfi_ld_state { - MFI_LD_STATE_OFFLINE = 0, - MFI_LD_STATE_PARTIALLY_DEGRADED = 1, - MFI_LD_STATE_DEGRADED = 2, - MFI_LD_STATE_OPTIMAL = 3 -}; - -enum mfi_syspd_state { - MFI_PD_STATE_UNCONFIGURED_GOOD = 0x00, - MFI_PD_STATE_UNCONFIGURED_BAD = 0x01, - MFI_PD_STATE_HOT_SPARE = 0x02, - MFI_PD_STATE_OFFLINE = 0x10, - MFI_PD_STATE_FAILED = 0x11, - MFI_PD_STATE_REBUILD = 0x14, - MFI_PD_STATE_ONLINE = 0x18, - MFI_PD_STATE_COPYBACK = 0x20, - MFI_PD_STATE_SYSTEM = 0x40 -}; - -struct mfi_ld_props { - union mfi_ld_ref ld; - char name[16]; - uint8_t default_cache_policy; - uint8_t access_policy; - uint8_t disk_cache_policy; - uint8_t current_cache_policy; - uint8_t no_bgi; - uint8_t reserved[7]; -} QEMU_PACKED; - -struct mfi_ld_params { - uint8_t primary_raid_level; - uint8_t raid_level_qualifier; - uint8_t secondary_raid_level; - uint8_t stripe_size; - uint8_t num_drives; - uint8_t span_depth; - uint8_t state; - uint8_t init_state; - uint8_t is_consistent; - uint8_t reserved[23]; -} QEMU_PACKED; - -struct mfi_ld_progress { - uint32_t active; -#define MFI_LD_PROGRESS_CC (1<<0) -#define MFI_LD_PROGRESS_BGI (1<<1) -#define MFI_LD_PROGRESS_FGI (1<<2) -#define MFI_LD_PORGRESS_RECON (1<<3) - struct mfi_progress cc; - struct mfi_progress bgi; - struct mfi_progress fgi; - struct mfi_progress recon; - struct mfi_progress reserved[4]; -} QEMU_PACKED; - -struct mfi_span { - uint64_t start_block; - uint64_t num_blocks; - uint16_t array_ref; - uint8_t reserved[6]; -} QEMU_PACKED; - -#define MFI_MAX_SPAN_DEPTH 8 -struct mfi_ld_config { - struct mfi_ld_props properties; - struct mfi_ld_params params; - struct mfi_span span[MFI_MAX_SPAN_DEPTH]; -} QEMU_PACKED; - -struct mfi_ld_info { - struct mfi_ld_config ld_config; - uint64_t size; - struct mfi_ld_progress progress; - uint16_t cluster_owner; - uint8_t reconstruct_active; - uint8_t reserved1[1]; - uint8_t vpd_page83[64]; - uint8_t reserved2[16]; -} QEMU_PACKED; - -union mfi_spare_type { - uint8_t flags; -#define MFI_SPARE_IS_DEDICATED (1 << 0) -#define MFI_SPARE_IS_REVERTABLE (1 << 1) -#define MFI_SPARE_IS_ENCL_AFFINITY (1 << 2) - uint8_t type; -} QEMU_PACKED; - -#define MFI_MAX_ARRAYS 16 -struct mfi_spare { - union mfi_pd_ref ref; - union mfi_spare_type spare_type; - uint8_t reserved[2]; - uint8_t array_count; - uint16_t array_refd[MFI_MAX_ARRAYS]; -} QEMU_PACKED; - -#define MFI_MAX_ROW_SIZE 32 -struct mfi_array { - uint64_t size; - uint8_t num_drives; - uint8_t reserved; - uint16_t array_ref; - uint8_t pad[20]; - struct { - union mfi_pd_ref ref; - uint16_t fw_state; /* enum mfi_syspd_state */ - struct { - uint8_t pd; - uint8_t slot; - } encl; - } pd[MFI_MAX_ROW_SIZE]; -} QEMU_PACKED; - -struct mfi_config_data { - uint32_t size; - uint16_t array_count; - uint16_t array_size; - uint16_t log_drv_count; - uint16_t log_drv_size; - uint16_t spares_count; - uint16_t spares_size; - uint8_t reserved[16]; - /* - struct mfi_array array[]; - struct mfi_ld_config ld[]; - struct mfi_spare spare[]; - */ -} QEMU_PACKED; - -#define MFI_SCSI_MAX_TARGETS 128 -#define MFI_SCSI_MAX_LUNS 8 -#define MFI_SCSI_INITIATOR_ID 255 -#define MFI_SCSI_MAX_CMDS 8 -#define MFI_SCSI_MAX_CDB_LEN 16 - -#endif /* MFI_REG_H */ diff --git a/qemu/hw/scsi/mpi.h b/qemu/hw/scsi/mpi.h deleted file mode 100644 index 0568e1950..000000000 --- a/qemu/hw/scsi/mpi.h +++ /dev/null @@ -1,1153 +0,0 @@ -/*- - * 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 deleted file mode 100644 index 707185469..000000000 --- a/qemu/hw/scsi/mptconfig.c +++ /dev/null @@ -1,905 +0,0 @@ -/* - * 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 deleted file mode 100644 index b7fe2a2a3..000000000 --- a/qemu/hw/scsi/mptendian.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * QEMU LSI SAS1068 Host Bus Adapter emulation - * Endianness conversion for MPI data structures - * - * Copyright (c) 2016 Red Hat, Inc. - * - * Authors: 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 . - */ - -#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 deleted file mode 100644 index 499c1465a..000000000 --- a/qemu/hw/scsi/mptsas.c +++ /dev/null @@ -1,1442 +0,0 @@ -/* - * 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 . - */ - -#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, ¬ifier->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, ¬ifier->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 deleted file mode 100644 index 595f81fb5..000000000 --- a/qemu/hw/scsi/mptsas.h +++ /dev/null @@ -1,100 +0,0 @@ -#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 deleted file mode 100644 index ad6f398c3..000000000 --- a/qemu/hw/scsi/scsi-bus.c +++ /dev/null @@ -1,2062 +0,0 @@ -#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" -#include "hw/qdev.h" -#include "sysemu/block-backend.h" -#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); -static void scsi_req_dequeue(SCSIRequest *req); -static uint8_t *scsi_target_alloc_buf(SCSIRequest *req, size_t len); -static void scsi_target_free_buf(SCSIRequest *req); - -static Property scsi_props[] = { - DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0), - DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1), - DEFINE_PROP_UINT32("lun", SCSIDevice, lun, -1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void scsi_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - - k->get_dev_path = scsibus_get_dev_path; - k->get_fw_dev_path = scsibus_get_fw_dev_path; - hc->unplug = qdev_simple_device_unplug_cb; -} - -static const TypeInfo scsi_bus_info = { - .name = TYPE_SCSI_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(SCSIBus), - .class_init = scsi_bus_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; -static int next_scsi_bus; - -static void scsi_device_realize(SCSIDevice *s, Error **errp) -{ - SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s); - if (sc->realize) { - sc->realize(s, errp); - } -} - -int scsi_bus_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf, - void *hba_private) -{ - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus); - int rc; - - assert(cmd->len == 0); - rc = scsi_req_parse_cdb(dev, cmd, buf); - if (bus->info->parse_cdb) { - rc = bus->info->parse_cdb(dev, cmd, buf, hba_private); - } - return rc; -} - -static SCSIRequest *scsi_device_alloc_req(SCSIDevice *s, uint32_t tag, uint32_t lun, - uint8_t *buf, void *hba_private) -{ - SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s); - if (sc->alloc_req) { - return sc->alloc_req(s, tag, lun, buf, hba_private); - } - - return NULL; -} - -void scsi_device_unit_attention_reported(SCSIDevice *s) -{ - SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s); - if (sc->unit_attention_reported) { - sc->unit_attention_reported(s); - } -} - -/* Create a scsi bus, and attach devices to it. */ -void scsi_bus_new(SCSIBus *bus, size_t bus_size, DeviceState *host, - const SCSIBusInfo *info, const char *bus_name) -{ - qbus_create_inplace(bus, bus_size, TYPE_SCSI_BUS, host, bus_name); - bus->busnr = next_scsi_bus++; - bus->info = info; - qbus_set_bus_hotplug_handler(BUS(bus), &error_abort); -} - -static void scsi_dma_restart_bh(void *opaque) -{ - SCSIDevice *s = opaque; - SCSIRequest *req, *next; - - qemu_bh_delete(s->bh); - s->bh = NULL; - - QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) { - scsi_req_ref(req); - if (req->retry) { - req->retry = false; - switch (req->cmd.mode) { - case SCSI_XFER_FROM_DEV: - case SCSI_XFER_TO_DEV: - scsi_req_continue(req); - break; - case SCSI_XFER_NONE: - scsi_req_dequeue(req); - scsi_req_enqueue(req); - break; - } - } - scsi_req_unref(req); - } -} - -void scsi_req_retry(SCSIRequest *req) -{ - /* No need to save a reference, because scsi_dma_restart_bh just - * looks at the request list. */ - req->retry = true; -} - -static void scsi_dma_restart_cb(void *opaque, int running, RunState state) -{ - SCSIDevice *s = opaque; - - if (!running) { - return; - } - if (!s->bh) { - 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); - } -} - -static void scsi_qdev_realize(DeviceState *qdev, Error **errp) -{ - SCSIDevice *dev = SCSI_DEVICE(qdev); - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus); - SCSIDevice *d; - Error *local_err = NULL; - - if (dev->channel > bus->info->max_channel) { - error_setg(errp, "bad scsi channel id: %d", dev->channel); - return; - } - if (dev->id != -1 && dev->id > bus->info->max_target) { - error_setg(errp, "bad scsi device id: %d", dev->id); - return; - } - if (dev->lun != -1 && dev->lun > bus->info->max_lun) { - error_setg(errp, "bad scsi device lun: %d", dev->lun); - return; - } - - if (dev->id == -1) { - int id = -1; - if (dev->lun == -1) { - dev->lun = 0; - } - do { - d = scsi_device_find(bus, dev->channel, ++id, dev->lun); - } while (d && d->lun == dev->lun && id < bus->info->max_target); - if (d && d->lun == dev->lun) { - error_setg(errp, "no free target"); - return; - } - dev->id = id; - } else if (dev->lun == -1) { - int lun = -1; - do { - d = scsi_device_find(bus, dev->channel, dev->id, ++lun); - } while (d && d->lun == lun && lun < bus->info->max_lun); - if (d && d->lun == lun) { - error_setg(errp, "no free lun"); - return; - } - dev->lun = lun; - } else { - d = scsi_device_find(bus, dev->channel, dev->id, dev->lun); - assert(d); - if (d->lun == dev->lun && dev != d) { - error_setg(errp, "lun already used by '%s'", d->qdev.id); - return; - } - } - - QTAILQ_INIT(&dev->requests); - scsi_device_realize(dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - dev->vmsentry = qemu_add_vm_change_state_handler(scsi_dma_restart_cb, - dev); -} - -static void scsi_qdev_unrealize(DeviceState *qdev, Error **errp) -{ - SCSIDevice *dev = SCSI_DEVICE(qdev); - - if (dev->vmsentry) { - qemu_del_vm_change_state_handler(dev->vmsentry); - } - - scsi_device_purge_requests(dev, SENSE_CODE(NO_SENSE)); - blockdev_mark_auto_del(dev->conf.blk); -} - -/* handle legacy '-drive if=scsi,...' cmd line args */ -SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, - int unit, bool removable, int bootindex, - const char *serial, Error **errp) -{ - const char *driver; - char *name; - DeviceState *dev; - Error *err = NULL; - - driver = blk_is_sg(blk) ? "scsi-generic" : "scsi-disk"; - dev = qdev_create(&bus->qbus, driver); - name = g_strdup_printf("legacy[%d]", unit); - object_property_add_child(OBJECT(bus), name, OBJECT(dev), NULL); - g_free(name); - - qdev_prop_set_uint32(dev, "scsi-id", unit); - if (bootindex >= 0) { - object_property_set_int(OBJECT(dev), bootindex, "bootindex", - &error_abort); - } - if (object_property_find(OBJECT(dev), "removable", NULL)) { - qdev_prop_set_bit(dev, "removable", removable); - } - if (serial && object_property_find(OBJECT(dev), "serial", NULL)) { - qdev_prop_set_string(dev, "serial", serial); - } - qdev_prop_set_drive(dev, "drive", blk, &err); - if (err) { - error_propagate(errp, err); - object_unparent(OBJECT(dev)); - return NULL; - } - object_property_set_bool(OBJECT(dev), true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - object_unparent(OBJECT(dev)); - return NULL; - } - return SCSI_DEVICE(dev); -} - -void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, Error **errp) -{ - Location loc; - DriveInfo *dinfo; - int unit; - Error *err = NULL; - - loc_push_none(&loc); - for (unit = 0; unit <= bus->info->max_target; unit++) { - dinfo = drive_get(IF_SCSI, bus->busnr, unit); - if (dinfo == NULL) { - continue; - } - qemu_opts_loc_restore(dinfo->opts); - scsi_bus_legacy_add_drive(bus, blk_by_legacy_dinfo(dinfo), - unit, false, -1, NULL, &err); - if (err != NULL) { - error_propagate(errp, err); - break; - } - } - loc_pop(&loc); -} - -static int32_t scsi_invalid_field(SCSIRequest *req, uint8_t *buf) -{ - scsi_req_build_sense(req, SENSE_CODE(INVALID_FIELD)); - scsi_req_complete(req, CHECK_CONDITION); - return 0; -} - -static const struct SCSIReqOps reqops_invalid_field = { - .size = sizeof(SCSIRequest), - .send_command = scsi_invalid_field -}; - -/* SCSIReqOps implementation for invalid commands. */ - -static int32_t scsi_invalid_command(SCSIRequest *req, uint8_t *buf) -{ - scsi_req_build_sense(req, SENSE_CODE(INVALID_OPCODE)); - scsi_req_complete(req, CHECK_CONDITION); - return 0; -} - -static const struct SCSIReqOps reqops_invalid_opcode = { - .size = sizeof(SCSIRequest), - .send_command = scsi_invalid_command -}; - -/* SCSIReqOps implementation for unit attention conditions. */ - -static int32_t scsi_unit_attention(SCSIRequest *req, uint8_t *buf) -{ - if (req->dev->unit_attention.key == UNIT_ATTENTION) { - scsi_req_build_sense(req, req->dev->unit_attention); - } else if (req->bus->unit_attention.key == UNIT_ATTENTION) { - scsi_req_build_sense(req, req->bus->unit_attention); - } - scsi_req_complete(req, CHECK_CONDITION); - return 0; -} - -static const struct SCSIReqOps reqops_unit_attention = { - .size = sizeof(SCSIRequest), - .send_command = scsi_unit_attention -}; - -/* SCSIReqOps implementation for REPORT LUNS and for commands sent to - an invalid LUN. */ - -typedef struct SCSITargetReq SCSITargetReq; - -struct SCSITargetReq { - SCSIRequest req; - int len; - uint8_t *buf; - int buf_len; -}; - -static void store_lun(uint8_t *outbuf, int lun) -{ - if (lun < 256) { - outbuf[1] = lun; - return; - } - outbuf[1] = (lun & 255); - outbuf[0] = (lun >> 8) | 0x40; -} - -static bool scsi_target_emulate_report_luns(SCSITargetReq *r) -{ - BusChild *kid; - int i, len, n; - int channel, id; - bool found_lun0; - - if (r->req.cmd.xfer < 16) { - return false; - } - if (r->req.cmd.buf[2] > 2) { - return false; - } - channel = r->req.dev->channel; - id = r->req.dev->id; - found_lun0 = false; - n = 0; - QTAILQ_FOREACH(kid, &r->req.bus->qbus.children, sibling) { - DeviceState *qdev = kid->child; - SCSIDevice *dev = SCSI_DEVICE(qdev); - - if (dev->channel == channel && dev->id == id) { - if (dev->lun == 0) { - found_lun0 = true; - } - n += 8; - } - } - if (!found_lun0) { - n += 8; - } - - scsi_target_alloc_buf(&r->req, n + 8); - - len = MIN(n + 8, r->req.cmd.xfer & ~7); - memset(r->buf, 0, len); - stl_be_p(&r->buf[0], n); - i = found_lun0 ? 8 : 16; - QTAILQ_FOREACH(kid, &r->req.bus->qbus.children, sibling) { - DeviceState *qdev = kid->child; - SCSIDevice *dev = SCSI_DEVICE(qdev); - - if (dev->channel == channel && dev->id == id) { - store_lun(&r->buf[i], dev->lun); - i += 8; - } - } - assert(i == n + 8); - r->len = len; - return true; -} - -static bool scsi_target_emulate_inquiry(SCSITargetReq *r) -{ - assert(r->req.dev->lun != r->req.lun); - - scsi_target_alloc_buf(&r->req, SCSI_INQUIRY_LEN); - - if (r->req.cmd.buf[1] & 0x2) { - /* Command support data - optional, not implemented */ - return false; - } - - if (r->req.cmd.buf[1] & 0x1) { - /* Vital product data */ - uint8_t page_code = r->req.cmd.buf[2]; - r->buf[r->len++] = page_code ; /* this page */ - r->buf[r->len++] = 0x00; - - switch (page_code) { - case 0x00: /* Supported page codes, mandatory */ - { - int pages; - pages = r->len++; - r->buf[r->len++] = 0x00; /* list of supported pages (this page) */ - r->buf[pages] = r->len - pages - 1; /* number of pages */ - break; - } - default: - return false; - } - /* done with EVPD */ - assert(r->len < r->buf_len); - r->len = MIN(r->req.cmd.xfer, r->len); - return true; - } - - /* Standard INQUIRY data */ - if (r->req.cmd.buf[2] != 0) { - return false; - } - - /* PAGE CODE == 0 */ - r->len = MIN(r->req.cmd.xfer, SCSI_INQUIRY_LEN); - memset(r->buf, 0, r->len); - if (r->req.lun != 0) { - r->buf[0] = TYPE_NO_LUN; - } else { - r->buf[0] = TYPE_NOT_PRESENT | TYPE_INACTIVE; - r->buf[2] = 5; /* Version */ - r->buf[3] = 2 | 0x10; /* HiSup, response data format */ - r->buf[4] = r->len - 5; /* Additional Length = (Len - 1) - 4 */ - 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_hw_version()); - } - return true; -} - -static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf) -{ - SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); - - switch (buf[0]) { - case REPORT_LUNS: - if (!scsi_target_emulate_report_luns(r)) { - goto illegal_request; - } - break; - case INQUIRY: - if (!scsi_target_emulate_inquiry(r)) { - goto illegal_request; - } - break; - case REQUEST_SENSE: - scsi_target_alloc_buf(&r->req, SCSI_SENSE_LEN); - r->len = scsi_device_get_sense(r->req.dev, r->buf, - MIN(req->cmd.xfer, r->buf_len), - (req->cmd.buf[1] & 1) == 0); - if (r->req.dev->sense_is_ua) { - scsi_device_unit_attention_reported(req->dev); - r->req.dev->sense_len = 0; - r->req.dev->sense_is_ua = false; - } - break; - case TEST_UNIT_READY: - break; - default: - scsi_req_build_sense(req, SENSE_CODE(LUN_NOT_SUPPORTED)); - scsi_req_complete(req, CHECK_CONDITION); - return 0; - illegal_request: - scsi_req_build_sense(req, SENSE_CODE(INVALID_FIELD)); - scsi_req_complete(req, CHECK_CONDITION); - return 0; - } - - if (!r->len) { - scsi_req_complete(req, GOOD); - } - return r->len; -} - -static void scsi_target_read_data(SCSIRequest *req) -{ - SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); - uint32_t n; - - n = r->len; - if (n > 0) { - r->len = 0; - scsi_req_data(&r->req, n); - } else { - scsi_req_complete(&r->req, GOOD); - } -} - -static uint8_t *scsi_target_get_buf(SCSIRequest *req) -{ - SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); - - return r->buf; -} - -static uint8_t *scsi_target_alloc_buf(SCSIRequest *req, size_t len) -{ - SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); - - r->buf = g_malloc(len); - r->buf_len = len; - - return r->buf; -} - -static void scsi_target_free_buf(SCSIRequest *req) -{ - SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); - - g_free(r->buf); -} - -static const struct SCSIReqOps reqops_target_command = { - .size = sizeof(SCSITargetReq), - .send_command = scsi_target_send_command, - .read_data = scsi_target_read_data, - .get_buf = scsi_target_get_buf, - .free_req = scsi_target_free_buf, -}; - - -SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d, - uint32_t tag, uint32_t lun, void *hba_private) -{ - SCSIRequest *req; - SCSIBus *bus = scsi_bus_from_device(d); - BusState *qbus = BUS(bus); - const int memset_off = offsetof(SCSIRequest, sense) - + sizeof(req->sense); - - req = g_malloc(reqops->size); - memset((uint8_t *)req + memset_off, 0, reqops->size - memset_off); - req->refcount = 1; - req->bus = bus; - req->dev = d; - req->tag = tag; - req->lun = lun; - req->hba_private = hba_private; - req->status = -1; - req->ops = reqops; - object_ref(OBJECT(d)); - object_ref(OBJECT(qbus->parent)); - notifier_list_init(&req->cancel_notifiers); - trace_scsi_req_alloc(req->dev->id, req->lun, req->tag); - return req; -} - -SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, - uint8_t *buf, void *hba_private) -{ - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus); - const SCSIReqOps *ops; - SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(d); - SCSIRequest *req; - SCSICommand cmd = { .len = 0 }; - int ret; - - if ((d->unit_attention.key == UNIT_ATTENTION || - bus->unit_attention.key == UNIT_ATTENTION) && - (buf[0] != INQUIRY && - buf[0] != REPORT_LUNS && - buf[0] != GET_CONFIGURATION && - buf[0] != GET_EVENT_STATUS_NOTIFICATION && - - /* - * If we already have a pending unit attention condition, - * report this one before triggering another one. - */ - !(buf[0] == REQUEST_SENSE && d->sense_is_ua))) { - ops = &reqops_unit_attention; - } else if (lun != d->lun || - buf[0] == REPORT_LUNS || - (buf[0] == REQUEST_SENSE && d->sense_len)) { - ops = &reqops_target_command; - } else { - ops = NULL; - } - - if (ops != NULL || !sc->parse_cdb) { - ret = scsi_req_parse_cdb(d, &cmd, buf); - } else { - ret = sc->parse_cdb(d, &cmd, buf, hba_private); - } - - if (ret != 0) { - trace_scsi_req_parse_bad(d->id, lun, tag, buf[0]); - req = scsi_req_alloc(&reqops_invalid_opcode, d, tag, lun, hba_private); - } else { - assert(cmd.len != 0); - trace_scsi_req_parsed(d->id, lun, tag, buf[0], - cmd.mode, cmd.xfer); - if (cmd.lba != -1) { - trace_scsi_req_parsed_lba(d->id, lun, tag, buf[0], - cmd.lba); - } - - if (cmd.xfer > INT32_MAX) { - req = scsi_req_alloc(&reqops_invalid_field, d, tag, lun, hba_private); - } else if (ops) { - req = scsi_req_alloc(ops, d, tag, lun, hba_private); - } else { - req = scsi_device_alloc_req(d, tag, lun, buf, hba_private); - } - } - - req->cmd = cmd; - req->resid = req->cmd.xfer; - - switch (buf[0]) { - case INQUIRY: - trace_scsi_inquiry(d->id, lun, tag, cmd.buf[1], cmd.buf[2]); - break; - case TEST_UNIT_READY: - trace_scsi_test_unit_ready(d->id, lun, tag); - break; - case REPORT_LUNS: - trace_scsi_report_luns(d->id, lun, tag); - break; - case REQUEST_SENSE: - trace_scsi_request_sense(d->id, lun, tag); - break; - default: - break; - } - - return req; -} - -uint8_t *scsi_req_get_buf(SCSIRequest *req) -{ - return req->ops->get_buf(req); -} - -static void scsi_clear_unit_attention(SCSIRequest *req) -{ - SCSISense *ua; - if (req->dev->unit_attention.key != UNIT_ATTENTION && - req->bus->unit_attention.key != UNIT_ATTENTION) { - return; - } - - /* - * If an INQUIRY command enters the enabled command state, - * the device server shall [not] clear any unit attention condition; - * See also MMC-6, paragraphs 6.5 and 6.6.2. - */ - if (req->cmd.buf[0] == INQUIRY || - req->cmd.buf[0] == GET_CONFIGURATION || - req->cmd.buf[0] == GET_EVENT_STATUS_NOTIFICATION) { - return; - } - - if (req->dev->unit_attention.key == UNIT_ATTENTION) { - ua = &req->dev->unit_attention; - } else { - ua = &req->bus->unit_attention; - } - - /* - * If a REPORT LUNS command enters the enabled command state, [...] - * the device server shall clear any pending unit attention condition - * with an additional sense code of REPORTED LUNS DATA HAS CHANGED. - */ - if (req->cmd.buf[0] == REPORT_LUNS && - !(ua->asc == SENSE_CODE(REPORTED_LUNS_CHANGED).asc && - ua->ascq == SENSE_CODE(REPORTED_LUNS_CHANGED).ascq)) { - return; - } - - *ua = SENSE_CODE(NO_SENSE); -} - -int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len) -{ - int ret; - - assert(len >= 14); - if (!req->sense_len) { - return 0; - } - - ret = scsi_build_sense(req->sense, req->sense_len, buf, len, true); - - /* - * FIXME: clearing unit attention conditions upon autosense should be done - * only if the UA_INTLCK_CTRL field in the Control mode page is set to 00b - * (SAM-5, 5.14). - * - * We assume UA_INTLCK_CTRL to be 00b for HBAs that support autosense, and - * 10b for HBAs that do not support it (do not call scsi_req_get_sense). - * Here we handle unit attention clearing for UA_INTLCK_CTRL == 00b. - */ - if (req->dev->sense_is_ua) { - scsi_device_unit_attention_reported(req->dev); - req->dev->sense_len = 0; - req->dev->sense_is_ua = false; - } - return ret; -} - -int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed) -{ - return scsi_build_sense(dev->sense, dev->sense_len, buf, len, fixed); -} - -void scsi_req_build_sense(SCSIRequest *req, SCSISense sense) -{ - trace_scsi_req_build_sense(req->dev->id, req->lun, req->tag, - sense.key, sense.asc, sense.ascq); - memset(req->sense, 0, 18); - req->sense[0] = 0x70; - req->sense[2] = sense.key; - req->sense[7] = 10; - req->sense[12] = sense.asc; - req->sense[13] = sense.ascq; - req->sense_len = 18; -} - -static void scsi_req_enqueue_internal(SCSIRequest *req) -{ - assert(!req->enqueued); - scsi_req_ref(req); - if (req->bus->info->get_sg_list) { - req->sg = req->bus->info->get_sg_list(req); - } else { - req->sg = NULL; - } - req->enqueued = true; - QTAILQ_INSERT_TAIL(&req->dev->requests, req, next); -} - -int32_t scsi_req_enqueue(SCSIRequest *req) -{ - int32_t rc; - - assert(!req->retry); - scsi_req_enqueue_internal(req); - scsi_req_ref(req); - rc = req->ops->send_command(req, req->cmd.buf); - scsi_req_unref(req); - return rc; -} - -static void scsi_req_dequeue(SCSIRequest *req) -{ - trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag); - req->retry = false; - if (req->enqueued) { - QTAILQ_REMOVE(&req->dev->requests, req, next); - req->enqueued = false; - scsi_req_unref(req); - } -} - -static int scsi_get_performance_length(int num_desc, int type, int data_type) -{ - /* MMC-6, paragraph 6.7. */ - switch (type) { - case 0: - if ((data_type & 3) == 0) { - /* Each descriptor is as in Table 295 - Nominal performance. */ - return 16 * num_desc + 8; - } else { - /* Each descriptor is as in Table 296 - Exceptions. */ - return 6 * num_desc + 8; - } - case 1: - case 4: - case 5: - return 8 * num_desc + 8; - case 2: - return 2048 * num_desc + 8; - case 3: - return 16 * num_desc + 8; - default: - return 8; - } -} - -static int ata_passthrough_xfer_unit(SCSIDevice *dev, uint8_t *buf) -{ - int byte_block = (buf[2] >> 2) & 0x1; - int type = (buf[2] >> 4) & 0x1; - int xfer_unit; - - if (byte_block) { - if (type) { - xfer_unit = dev->blocksize; - } else { - xfer_unit = 512; - } - } else { - xfer_unit = 1; - } - - return xfer_unit; -} - -static int ata_passthrough_12_xfer(SCSIDevice *dev, uint8_t *buf) -{ - int length = buf[2] & 0x3; - int xfer; - int unit = ata_passthrough_xfer_unit(dev, buf); - - switch (length) { - case 0: - case 3: /* USB-specific. */ - default: - xfer = 0; - break; - case 1: - xfer = buf[3]; - break; - case 2: - xfer = buf[4]; - break; - } - - return xfer * unit; -} - -static int ata_passthrough_16_xfer(SCSIDevice *dev, uint8_t *buf) -{ - int extend = buf[1] & 0x1; - int length = buf[2] & 0x3; - int xfer; - int unit = ata_passthrough_xfer_unit(dev, buf); - - switch (length) { - case 0: - case 3: /* USB-specific. */ - default: - xfer = 0; - break; - case 1: - xfer = buf[4]; - xfer |= (extend ? buf[3] << 8 : 0); - break; - case 2: - xfer = buf[6]; - xfer |= (extend ? buf[5] << 8 : 0); - break; - } - - return xfer * unit; -} - -uint32_t scsi_data_cdb_xfer(uint8_t *buf) -{ - if ((buf[0] >> 5) == 0 && buf[4] == 0) { - return 256; - } else { - return scsi_cdb_xfer(buf); - } -} - -uint32_t scsi_cdb_xfer(uint8_t *buf) -{ - switch (buf[0] >> 5) { - case 0: - return buf[4]; - break; - case 1: - case 2: - return lduw_be_p(&buf[7]); - break; - case 4: - return ldl_be_p(&buf[10]) & 0xffffffffULL; - break; - case 5: - return ldl_be_p(&buf[6]) & 0xffffffffULL; - break; - default: - return -1; - } -} - -static int scsi_req_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) -{ - cmd->xfer = scsi_cdb_xfer(buf); - switch (buf[0]) { - case TEST_UNIT_READY: - case REWIND: - case START_STOP: - case SET_CAPACITY: - case WRITE_FILEMARKS: - case WRITE_FILEMARKS_16: - case SPACE: - case RESERVE: - case RELEASE: - case ERASE: - case ALLOW_MEDIUM_REMOVAL: - case SEEK_10: - case SYNCHRONIZE_CACHE: - case SYNCHRONIZE_CACHE_16: - case LOCATE_16: - case LOCK_UNLOCK_CACHE: - case SET_CD_SPEED: - case SET_LIMITS: - case WRITE_LONG_10: - case UPDATE_BLOCK: - case RESERVE_TRACK: - case SET_READ_AHEAD: - case PRE_FETCH: - case PRE_FETCH_16: - case ALLOW_OVERWRITE: - cmd->xfer = 0; - break; - case VERIFY_10: - case VERIFY_12: - case VERIFY_16: - if ((buf[1] & 2) == 0) { - cmd->xfer = 0; - } else if ((buf[1] & 4) != 0) { - cmd->xfer = 1; - } - cmd->xfer *= dev->blocksize; - break; - case MODE_SENSE: - break; - case WRITE_SAME_10: - case WRITE_SAME_16: - cmd->xfer = dev->blocksize; - break; - case READ_CAPACITY_10: - cmd->xfer = 8; - break; - case READ_BLOCK_LIMITS: - cmd->xfer = 6; - break; - case SEND_VOLUME_TAG: - /* GPCMD_SET_STREAMING from multimedia commands. */ - if (dev->type == TYPE_ROM) { - cmd->xfer = buf[10] | (buf[9] << 8); - } else { - cmd->xfer = buf[9] | (buf[8] << 8); - } - break; - case WRITE_6: - /* length 0 means 256 blocks */ - if (cmd->xfer == 0) { - cmd->xfer = 256; - } - /* fall through */ - case WRITE_10: - case WRITE_VERIFY_10: - case WRITE_12: - case WRITE_VERIFY_12: - case WRITE_16: - case WRITE_VERIFY_16: - cmd->xfer *= dev->blocksize; - break; - case READ_6: - case READ_REVERSE: - /* length 0 means 256 blocks */ - if (cmd->xfer == 0) { - cmd->xfer = 256; - } - /* fall through */ - case READ_10: - case READ_12: - case READ_16: - cmd->xfer *= dev->blocksize; - break; - case FORMAT_UNIT: - /* MMC mandates the parameter list to be 12-bytes long. Parameters - * for block devices are restricted to the header right now. */ - if (dev->type == TYPE_ROM && (buf[1] & 16)) { - cmd->xfer = 12; - } else { - cmd->xfer = (buf[1] & 16) == 0 ? 0 : (buf[1] & 32 ? 8 : 4); - } - break; - case INQUIRY: - case RECEIVE_DIAGNOSTIC: - case SEND_DIAGNOSTIC: - cmd->xfer = buf[4] | (buf[3] << 8); - break; - case READ_CD: - case READ_BUFFER: - case WRITE_BUFFER: - case SEND_CUE_SHEET: - cmd->xfer = buf[8] | (buf[7] << 8) | (buf[6] << 16); - break; - case PERSISTENT_RESERVE_OUT: - cmd->xfer = ldl_be_p(&buf[5]) & 0xffffffffULL; - break; - case ERASE_12: - if (dev->type == TYPE_ROM) { - /* MMC command GET PERFORMANCE. */ - cmd->xfer = scsi_get_performance_length(buf[9] | (buf[8] << 8), - buf[10], buf[1] & 0x1f); - } - break; - case MECHANISM_STATUS: - case READ_DVD_STRUCTURE: - case SEND_DVD_STRUCTURE: - case MAINTENANCE_OUT: - case MAINTENANCE_IN: - if (dev->type == TYPE_ROM) { - /* GPCMD_REPORT_KEY and GPCMD_SEND_KEY from multi media commands */ - cmd->xfer = buf[9] | (buf[8] << 8); - } - break; - case ATA_PASSTHROUGH_12: - if (dev->type == TYPE_ROM) { - /* BLANK command of MMC */ - cmd->xfer = 0; - } else { - cmd->xfer = ata_passthrough_12_xfer(dev, buf); - } - break; - case ATA_PASSTHROUGH_16: - cmd->xfer = ata_passthrough_16_xfer(dev, buf); - break; - } - return 0; -} - -static int scsi_req_stream_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) -{ - switch (buf[0]) { - /* stream commands */ - case ERASE_12: - case ERASE_16: - cmd->xfer = 0; - break; - case READ_6: - case READ_REVERSE: - case RECOVER_BUFFERED_DATA: - case WRITE_6: - cmd->xfer = buf[4] | (buf[3] << 8) | (buf[2] << 16); - if (buf[1] & 0x01) { /* fixed */ - cmd->xfer *= dev->blocksize; - } - break; - case READ_16: - case READ_REVERSE_16: - case VERIFY_16: - case WRITE_16: - cmd->xfer = buf[14] | (buf[13] << 8) | (buf[12] << 16); - if (buf[1] & 0x01) { /* fixed */ - cmd->xfer *= dev->blocksize; - } - break; - case REWIND: - case LOAD_UNLOAD: - cmd->xfer = 0; - break; - case SPACE_16: - cmd->xfer = buf[13] | (buf[12] << 8); - break; - case READ_POSITION: - switch (buf[1] & 0x1f) /* operation code */ { - case SHORT_FORM_BLOCK_ID: - case SHORT_FORM_VENDOR_SPECIFIC: - cmd->xfer = 20; - break; - case LONG_FORM: - cmd->xfer = 32; - break; - case EXTENDED_FORM: - cmd->xfer = buf[8] | (buf[7] << 8); - break; - default: - return -1; - } - - break; - case FORMAT_UNIT: - cmd->xfer = buf[4] | (buf[3] << 8); - break; - /* generic commands */ - default: - return scsi_req_xfer(cmd, dev, buf); - } - return 0; -} - -static int scsi_req_medium_changer_xfer(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) -{ - switch (buf[0]) { - /* medium changer commands */ - case EXCHANGE_MEDIUM: - case INITIALIZE_ELEMENT_STATUS: - case INITIALIZE_ELEMENT_STATUS_WITH_RANGE: - case MOVE_MEDIUM: - case POSITION_TO_ELEMENT: - cmd->xfer = 0; - break; - case READ_ELEMENT_STATUS: - cmd->xfer = buf[9] | (buf[8] << 8) | (buf[7] << 16); - break; - - /* generic commands */ - default: - return scsi_req_xfer(cmd, dev, buf); - } - return 0; -} - - -static void scsi_cmd_xfer_mode(SCSICommand *cmd) -{ - if (!cmd->xfer) { - cmd->mode = SCSI_XFER_NONE; - return; - } - switch (cmd->buf[0]) { - case WRITE_6: - case WRITE_10: - case WRITE_VERIFY_10: - case WRITE_12: - case WRITE_VERIFY_12: - case WRITE_16: - case WRITE_VERIFY_16: - case VERIFY_10: - case VERIFY_12: - case VERIFY_16: - case COPY: - case COPY_VERIFY: - case COMPARE: - case CHANGE_DEFINITION: - case LOG_SELECT: - case MODE_SELECT: - case MODE_SELECT_10: - case SEND_DIAGNOSTIC: - case WRITE_BUFFER: - case FORMAT_UNIT: - case REASSIGN_BLOCKS: - case SEARCH_EQUAL: - case SEARCH_HIGH: - case SEARCH_LOW: - case UPDATE_BLOCK: - case WRITE_LONG_10: - case WRITE_SAME_10: - case WRITE_SAME_16: - case UNMAP: - case SEARCH_HIGH_12: - case SEARCH_EQUAL_12: - case SEARCH_LOW_12: - case MEDIUM_SCAN: - case SEND_VOLUME_TAG: - case SEND_CUE_SHEET: - case SEND_DVD_STRUCTURE: - case PERSISTENT_RESERVE_OUT: - case MAINTENANCE_OUT: - cmd->mode = SCSI_XFER_TO_DEV; - break; - case ATA_PASSTHROUGH_12: - case ATA_PASSTHROUGH_16: - /* T_DIR */ - cmd->mode = (cmd->buf[2] & 0x8) ? - SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV; - break; - default: - cmd->mode = SCSI_XFER_FROM_DEV; - break; - } -} - -static uint64_t scsi_cmd_lba(SCSICommand *cmd) -{ - uint8_t *buf = cmd->buf; - uint64_t lba; - - switch (buf[0] >> 5) { - case 0: - lba = ldl_be_p(&buf[0]) & 0x1fffff; - break; - case 1: - case 2: - case 5: - lba = ldl_be_p(&buf[2]) & 0xffffffffULL; - break; - case 4: - lba = ldq_be_p(&buf[2]); - break; - default: - lba = -1; - - } - return lba; -} - -int scsi_cdb_length(uint8_t *buf) { - int cdb_len; - - switch (buf[0] >> 5) { - case 0: - cdb_len = 6; - break; - case 1: - case 2: - cdb_len = 10; - break; - case 4: - cdb_len = 16; - break; - case 5: - cdb_len = 12; - break; - default: - cdb_len = -1; - } - return cdb_len; -} - -int scsi_req_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf) -{ - int rc; - int len; - - cmd->lba = -1; - len = scsi_cdb_length(buf); - if (len < 0) { - return -1; - } - - cmd->len = len; - switch (dev->type) { - case TYPE_TAPE: - rc = scsi_req_stream_xfer(cmd, dev, buf); - break; - case TYPE_MEDIUM_CHANGER: - rc = scsi_req_medium_changer_xfer(cmd, dev, buf); - break; - default: - rc = scsi_req_xfer(cmd, dev, buf); - break; - } - - if (rc != 0) - return rc; - - memcpy(cmd->buf, buf, cmd->len); - scsi_cmd_xfer_mode(cmd); - cmd->lba = scsi_cmd_lba(cmd); - return 0; -} - -void scsi_device_report_change(SCSIDevice *dev, SCSISense sense) -{ - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus); - - scsi_device_set_ua(dev, sense); - if (bus->info->change) { - bus->info->change(bus, dev, sense); - } -} - -/* - * Predefined sense codes - */ - -/* No sense data available */ -const struct SCSISense sense_code_NO_SENSE = { - .key = NO_SENSE , .asc = 0x00 , .ascq = 0x00 -}; - -/* LUN not ready, Manual intervention required */ -const struct SCSISense sense_code_LUN_NOT_READY = { - .key = NOT_READY, .asc = 0x04, .ascq = 0x03 -}; - -/* LUN not ready, Medium not present */ -const struct SCSISense sense_code_NO_MEDIUM = { - .key = NOT_READY, .asc = 0x3a, .ascq = 0x00 -}; - -/* LUN not ready, medium removal prevented */ -const struct SCSISense sense_code_NOT_READY_REMOVAL_PREVENTED = { - .key = NOT_READY, .asc = 0x53, .ascq = 0x02 -}; - -/* Hardware error, internal target failure */ -const struct SCSISense sense_code_TARGET_FAILURE = { - .key = HARDWARE_ERROR, .asc = 0x44, .ascq = 0x00 -}; - -/* Illegal request, invalid command operation code */ -const struct SCSISense sense_code_INVALID_OPCODE = { - .key = ILLEGAL_REQUEST, .asc = 0x20, .ascq = 0x00 -}; - -/* Illegal request, LBA out of range */ -const struct SCSISense sense_code_LBA_OUT_OF_RANGE = { - .key = ILLEGAL_REQUEST, .asc = 0x21, .ascq = 0x00 -}; - -/* Illegal request, Invalid field in CDB */ -const struct SCSISense sense_code_INVALID_FIELD = { - .key = ILLEGAL_REQUEST, .asc = 0x24, .ascq = 0x00 -}; - -/* Illegal request, Invalid field in parameter list */ -const struct SCSISense sense_code_INVALID_PARAM = { - .key = ILLEGAL_REQUEST, .asc = 0x26, .ascq = 0x00 -}; - -/* Illegal request, Parameter list length error */ -const struct SCSISense sense_code_INVALID_PARAM_LEN = { - .key = ILLEGAL_REQUEST, .asc = 0x1a, .ascq = 0x00 -}; - -/* Illegal request, LUN not supported */ -const struct SCSISense sense_code_LUN_NOT_SUPPORTED = { - .key = ILLEGAL_REQUEST, .asc = 0x25, .ascq = 0x00 -}; - -/* Illegal request, Saving parameters not supported */ -const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED = { - .key = ILLEGAL_REQUEST, .asc = 0x39, .ascq = 0x00 -}; - -/* Illegal request, Incompatible medium installed */ -const struct SCSISense sense_code_INCOMPATIBLE_FORMAT = { - .key = ILLEGAL_REQUEST, .asc = 0x30, .ascq = 0x00 -}; - -/* Illegal request, medium removal prevented */ -const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED = { - .key = ILLEGAL_REQUEST, .asc = 0x53, .ascq = 0x02 -}; - -/* Illegal request, Invalid Transfer Tag */ -const struct SCSISense sense_code_INVALID_TAG = { - .key = ILLEGAL_REQUEST, .asc = 0x4b, .ascq = 0x01 -}; - -/* Command aborted, I/O process terminated */ -const struct SCSISense sense_code_IO_ERROR = { - .key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06 -}; - -/* Command aborted, I_T Nexus loss occurred */ -const struct SCSISense sense_code_I_T_NEXUS_LOSS = { - .key = ABORTED_COMMAND, .asc = 0x29, .ascq = 0x07 -}; - -/* Command aborted, Logical Unit failure */ -const struct SCSISense sense_code_LUN_FAILURE = { - .key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01 -}; - -/* Command aborted, Overlapped Commands Attempted */ -const struct SCSISense sense_code_OVERLAPPED_COMMANDS = { - .key = ABORTED_COMMAND, .asc = 0x4e, .ascq = 0x00 -}; - -/* Unit attention, Capacity data has changed */ -const struct SCSISense sense_code_CAPACITY_CHANGED = { - .key = UNIT_ATTENTION, .asc = 0x2a, .ascq = 0x09 -}; - -/* Unit attention, Power on, reset or bus device reset occurred */ -const struct SCSISense sense_code_RESET = { - .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x00 -}; - -/* Unit attention, No medium */ -const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM = { - .key = UNIT_ATTENTION, .asc = 0x3a, .ascq = 0x00 -}; - -/* Unit attention, Medium may have changed */ -const struct SCSISense sense_code_MEDIUM_CHANGED = { - .key = UNIT_ATTENTION, .asc = 0x28, .ascq = 0x00 -}; - -/* Unit attention, Reported LUNs data has changed */ -const struct SCSISense sense_code_REPORTED_LUNS_CHANGED = { - .key = UNIT_ATTENTION, .asc = 0x3f, .ascq = 0x0e -}; - -/* Unit attention, Device internal reset */ -const struct SCSISense sense_code_DEVICE_INTERNAL_RESET = { - .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x04 -}; - -/* Data Protection, Write Protected */ -const struct SCSISense sense_code_WRITE_PROTECTED = { - .key = DATA_PROTECT, .asc = 0x27, .ascq = 0x00 -}; - -/* Data Protection, Space Allocation Failed Write Protect */ -const struct SCSISense sense_code_SPACE_ALLOC_FAILED = { - .key = DATA_PROTECT, .asc = 0x27, .ascq = 0x07 -}; - -/* - * scsi_build_sense - * - * Convert between fixed and descriptor sense buffers - */ -int scsi_build_sense(uint8_t *in_buf, int in_len, - uint8_t *buf, int len, bool fixed) -{ - bool fixed_in; - SCSISense sense; - if (!fixed && len < 8) { - return 0; - } - - if (in_len == 0) { - sense.key = NO_SENSE; - sense.asc = 0; - sense.ascq = 0; - } else { - fixed_in = (in_buf[0] & 2) == 0; - - if (fixed == fixed_in) { - memcpy(buf, in_buf, MIN(len, in_len)); - return MIN(len, in_len); - } - - if (fixed_in) { - sense.key = in_buf[2]; - sense.asc = in_buf[12]; - sense.ascq = in_buf[13]; - } else { - sense.key = in_buf[1]; - sense.asc = in_buf[2]; - sense.ascq = in_buf[3]; - } - } - - memset(buf, 0, len); - if (fixed) { - /* Return fixed format sense buffer */ - buf[0] = 0x70; - buf[2] = sense.key; - buf[7] = 10; - buf[12] = sense.asc; - buf[13] = sense.ascq; - return MIN(len, SCSI_SENSE_LEN); - } else { - /* Return descriptor format sense buffer */ - buf[0] = 0x72; - buf[1] = sense.key; - buf[2] = sense.asc; - buf[3] = sense.ascq; - return 8; - } -} - -const char *scsi_command_name(uint8_t cmd) -{ - static const char *names[] = { - [ TEST_UNIT_READY ] = "TEST_UNIT_READY", - [ REWIND ] = "REWIND", - [ REQUEST_SENSE ] = "REQUEST_SENSE", - [ FORMAT_UNIT ] = "FORMAT_UNIT", - [ READ_BLOCK_LIMITS ] = "READ_BLOCK_LIMITS", - [ REASSIGN_BLOCKS ] = "REASSIGN_BLOCKS/INITIALIZE ELEMENT STATUS", - /* LOAD_UNLOAD and INITIALIZE_ELEMENT_STATUS use the same operation code */ - [ READ_6 ] = "READ_6", - [ WRITE_6 ] = "WRITE_6", - [ SET_CAPACITY ] = "SET_CAPACITY", - [ READ_REVERSE ] = "READ_REVERSE", - [ WRITE_FILEMARKS ] = "WRITE_FILEMARKS", - [ SPACE ] = "SPACE", - [ INQUIRY ] = "INQUIRY", - [ RECOVER_BUFFERED_DATA ] = "RECOVER_BUFFERED_DATA", - [ MAINTENANCE_IN ] = "MAINTENANCE_IN", - [ MAINTENANCE_OUT ] = "MAINTENANCE_OUT", - [ MODE_SELECT ] = "MODE_SELECT", - [ RESERVE ] = "RESERVE", - [ RELEASE ] = "RELEASE", - [ COPY ] = "COPY", - [ ERASE ] = "ERASE", - [ MODE_SENSE ] = "MODE_SENSE", - [ START_STOP ] = "START_STOP/LOAD_UNLOAD", - /* LOAD_UNLOAD and START_STOP use the same operation code */ - [ RECEIVE_DIAGNOSTIC ] = "RECEIVE_DIAGNOSTIC", - [ SEND_DIAGNOSTIC ] = "SEND_DIAGNOSTIC", - [ ALLOW_MEDIUM_REMOVAL ] = "ALLOW_MEDIUM_REMOVAL", - [ READ_CAPACITY_10 ] = "READ_CAPACITY_10", - [ READ_10 ] = "READ_10", - [ WRITE_10 ] = "WRITE_10", - [ SEEK_10 ] = "SEEK_10/POSITION_TO_ELEMENT", - /* SEEK_10 and POSITION_TO_ELEMENT use the same operation code */ - [ WRITE_VERIFY_10 ] = "WRITE_VERIFY_10", - [ VERIFY_10 ] = "VERIFY_10", - [ SEARCH_HIGH ] = "SEARCH_HIGH", - [ SEARCH_EQUAL ] = "SEARCH_EQUAL", - [ SEARCH_LOW ] = "SEARCH_LOW", - [ SET_LIMITS ] = "SET_LIMITS", - [ PRE_FETCH ] = "PRE_FETCH/READ_POSITION", - /* READ_POSITION and PRE_FETCH use the same operation code */ - [ SYNCHRONIZE_CACHE ] = "SYNCHRONIZE_CACHE", - [ LOCK_UNLOCK_CACHE ] = "LOCK_UNLOCK_CACHE", - [ READ_DEFECT_DATA ] = "READ_DEFECT_DATA/INITIALIZE_ELEMENT_STATUS_WITH_RANGE", - /* READ_DEFECT_DATA and INITIALIZE_ELEMENT_STATUS_WITH_RANGE use the same operation code */ - [ MEDIUM_SCAN ] = "MEDIUM_SCAN", - [ COMPARE ] = "COMPARE", - [ COPY_VERIFY ] = "COPY_VERIFY", - [ WRITE_BUFFER ] = "WRITE_BUFFER", - [ READ_BUFFER ] = "READ_BUFFER", - [ UPDATE_BLOCK ] = "UPDATE_BLOCK", - [ READ_LONG_10 ] = "READ_LONG_10", - [ WRITE_LONG_10 ] = "WRITE_LONG_10", - [ CHANGE_DEFINITION ] = "CHANGE_DEFINITION", - [ WRITE_SAME_10 ] = "WRITE_SAME_10", - [ UNMAP ] = "UNMAP", - [ READ_TOC ] = "READ_TOC", - [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT", - [ SANITIZE ] = "SANITIZE", - [ GET_CONFIGURATION ] = "GET_CONFIGURATION", - [ LOG_SELECT ] = "LOG_SELECT", - [ LOG_SENSE ] = "LOG_SENSE", - [ MODE_SELECT_10 ] = "MODE_SELECT_10", - [ RESERVE_10 ] = "RESERVE_10", - [ RELEASE_10 ] = "RELEASE_10", - [ MODE_SENSE_10 ] = "MODE_SENSE_10", - [ PERSISTENT_RESERVE_IN ] = "PERSISTENT_RESERVE_IN", - [ PERSISTENT_RESERVE_OUT ] = "PERSISTENT_RESERVE_OUT", - [ WRITE_FILEMARKS_16 ] = "WRITE_FILEMARKS_16", - [ EXTENDED_COPY ] = "EXTENDED_COPY", - [ ATA_PASSTHROUGH_16 ] = "ATA_PASSTHROUGH_16", - [ ACCESS_CONTROL_IN ] = "ACCESS_CONTROL_IN", - [ ACCESS_CONTROL_OUT ] = "ACCESS_CONTROL_OUT", - [ READ_16 ] = "READ_16", - [ COMPARE_AND_WRITE ] = "COMPARE_AND_WRITE", - [ WRITE_16 ] = "WRITE_16", - [ WRITE_VERIFY_16 ] = "WRITE_VERIFY_16", - [ VERIFY_16 ] = "VERIFY_16", - [ PRE_FETCH_16 ] = "PRE_FETCH_16", - [ SYNCHRONIZE_CACHE_16 ] = "SPACE_16/SYNCHRONIZE_CACHE_16", - /* SPACE_16 and SYNCHRONIZE_CACHE_16 use the same operation code */ - [ LOCATE_16 ] = "LOCATE_16", - [ WRITE_SAME_16 ] = "ERASE_16/WRITE_SAME_16", - /* ERASE_16 and WRITE_SAME_16 use the same operation code */ - [ SERVICE_ACTION_IN_16 ] = "SERVICE_ACTION_IN_16", - [ WRITE_LONG_16 ] = "WRITE_LONG_16", - [ REPORT_LUNS ] = "REPORT_LUNS", - [ ATA_PASSTHROUGH_12 ] = "BLANK/ATA_PASSTHROUGH_12", - [ MOVE_MEDIUM ] = "MOVE_MEDIUM", - [ EXCHANGE_MEDIUM ] = "EXCHANGE MEDIUM", - [ READ_12 ] = "READ_12", - [ WRITE_12 ] = "WRITE_12", - [ ERASE_12 ] = "ERASE_12/GET_PERFORMANCE", - /* ERASE_12 and GET_PERFORMANCE use the same operation code */ - [ SERVICE_ACTION_IN_12 ] = "SERVICE_ACTION_IN_12", - [ WRITE_VERIFY_12 ] = "WRITE_VERIFY_12", - [ VERIFY_12 ] = "VERIFY_12", - [ SEARCH_HIGH_12 ] = "SEARCH_HIGH_12", - [ SEARCH_EQUAL_12 ] = "SEARCH_EQUAL_12", - [ SEARCH_LOW_12 ] = "SEARCH_LOW_12", - [ READ_ELEMENT_STATUS ] = "READ_ELEMENT_STATUS", - [ SEND_VOLUME_TAG ] = "SEND_VOLUME_TAG/SET_STREAMING", - /* SEND_VOLUME_TAG and SET_STREAMING use the same operation code */ - [ READ_CD ] = "READ_CD", - [ READ_DEFECT_DATA_12 ] = "READ_DEFECT_DATA_12", - [ READ_DVD_STRUCTURE ] = "READ_DVD_STRUCTURE", - [ RESERVE_TRACK ] = "RESERVE_TRACK", - [ SEND_CUE_SHEET ] = "SEND_CUE_SHEET", - [ SEND_DVD_STRUCTURE ] = "SEND_DVD_STRUCTURE", - [ SET_CD_SPEED ] = "SET_CD_SPEED", - [ SET_READ_AHEAD ] = "SET_READ_AHEAD", - [ ALLOW_OVERWRITE ] = "ALLOW_OVERWRITE", - [ MECHANISM_STATUS ] = "MECHANISM_STATUS", - [ GET_EVENT_STATUS_NOTIFICATION ] = "GET_EVENT_STATUS_NOTIFICATION", - [ READ_DISC_INFORMATION ] = "READ_DISC_INFORMATION", - }; - - if (cmd >= ARRAY_SIZE(names) || names[cmd] == NULL) - return "*UNKNOWN*"; - return names[cmd]; -} - -SCSIRequest *scsi_req_ref(SCSIRequest *req) -{ - assert(req->refcount > 0); - req->refcount++; - return req; -} - -void scsi_req_unref(SCSIRequest *req) -{ - assert(req->refcount > 0); - if (--req->refcount == 0) { - BusState *qbus = req->dev->qdev.parent_bus; - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, qbus); - - if (bus->info->free_request && req->hba_private) { - bus->info->free_request(bus, req->hba_private); - } - if (req->ops->free_req) { - req->ops->free_req(req); - } - object_unref(OBJECT(req->dev)); - object_unref(OBJECT(qbus->parent)); - g_free(req); - } -} - -/* Tell the device that we finished processing this chunk of I/O. It - will start the next chunk or complete the command. */ -void scsi_req_continue(SCSIRequest *req) -{ - if (req->io_canceled) { - trace_scsi_req_continue_canceled(req->dev->id, req->lun, req->tag); - return; - } - trace_scsi_req_continue(req->dev->id, req->lun, req->tag); - if (req->cmd.mode == SCSI_XFER_TO_DEV) { - req->ops->write_data(req); - } else { - req->ops->read_data(req); - } -} - -/* Called by the devices when data is ready for the HBA. The HBA should - start a DMA operation to read or fill the device's data buffer. - Once it completes, calling scsi_req_continue will restart I/O. */ -void scsi_req_data(SCSIRequest *req, int len) -{ - uint8_t *buf; - if (req->io_canceled) { - trace_scsi_req_data_canceled(req->dev->id, req->lun, req->tag, len); - return; - } - trace_scsi_req_data(req->dev->id, req->lun, req->tag, len); - assert(req->cmd.mode != SCSI_XFER_NONE); - if (!req->sg) { - req->resid -= len; - req->bus->info->transfer_data(req, len); - return; - } - - /* If the device calls scsi_req_data and the HBA specified a - * scatter/gather list, the transfer has to happen in a single - * step. */ - assert(!req->dma_started); - req->dma_started = true; - - buf = scsi_req_get_buf(req); - if (req->cmd.mode == SCSI_XFER_FROM_DEV) { - req->resid = dma_buf_read(buf, len, req->sg); - } else { - req->resid = dma_buf_write(buf, len, req->sg); - } - scsi_req_continue(req); -} - -void scsi_req_print(SCSIRequest *req) -{ - FILE *fp = stderr; - int i; - - fprintf(fp, "[%s id=%d] %s", - req->dev->qdev.parent_bus->name, - req->dev->id, - scsi_command_name(req->cmd.buf[0])); - for (i = 1; i < req->cmd.len; i++) { - fprintf(fp, " 0x%02x", req->cmd.buf[i]); - } - switch (req->cmd.mode) { - case SCSI_XFER_NONE: - fprintf(fp, " - none\n"); - break; - case SCSI_XFER_FROM_DEV: - fprintf(fp, " - from-dev len=%zd\n", req->cmd.xfer); - break; - case SCSI_XFER_TO_DEV: - fprintf(fp, " - to-dev len=%zd\n", req->cmd.xfer); - break; - default: - fprintf(fp, " - Oops\n"); - break; - } -} - -void scsi_req_complete(SCSIRequest *req, int status) -{ - assert(req->status == -1); - req->status = status; - - assert(req->sense_len <= sizeof(req->sense)); - if (status == GOOD) { - req->sense_len = 0; - } - - if (req->sense_len) { - memcpy(req->dev->sense, req->sense, req->sense_len); - req->dev->sense_len = req->sense_len; - req->dev->sense_is_ua = (req->ops == &reqops_unit_attention); - } else { - req->dev->sense_len = 0; - req->dev->sense_is_ua = false; - } - - /* - * Unit attention state is now stored in the device's sense buffer - * if the HBA didn't do autosense. Clear the pending unit attention - * flags. - */ - scsi_clear_unit_attention(req); - - scsi_req_ref(req); - scsi_req_dequeue(req); - req->bus->info->complete(req, req->status, req->resid); - - /* Cancelled requests might end up being completed instead of cancelled */ - notifier_list_notify(&req->cancel_notifiers, req); - scsi_req_unref(req); -} - -/* Called by the devices when the request is canceled. */ -void scsi_req_cancel_complete(SCSIRequest *req) -{ - assert(req->io_canceled); - if (req->bus->info->cancel) { - req->bus->info->cancel(req); - } - notifier_list_notify(&req->cancel_notifiers, req); - scsi_req_unref(req); -} - -/* Cancel @req asynchronously. @notifier is added to @req's cancellation - * notifier list, the bus will be notified the requests cancellation is - * completed. - * */ -void scsi_req_cancel_async(SCSIRequest *req, Notifier *notifier) -{ - trace_scsi_req_cancel(req->dev->id, req->lun, req->tag); - if (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; - if (req->aiocb) { - blk_aio_cancel_async(req->aiocb); - } else { - scsi_req_cancel_complete(req); - } -} - -void scsi_req_cancel(SCSIRequest *req) -{ - trace_scsi_req_cancel(req->dev->id, req->lun, req->tag); - 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; - if (req->aiocb) { - blk_aio_cancel(req->aiocb); - } else { - scsi_req_cancel_complete(req); - } -} - -static int scsi_ua_precedence(SCSISense sense) -{ - if (sense.key != UNIT_ATTENTION) { - return INT_MAX; - } - if (sense.asc == 0x29 && sense.ascq == 0x04) { - /* DEVICE INTERNAL RESET goes with POWER ON OCCURRED */ - return 1; - } else if (sense.asc == 0x3F && sense.ascq == 0x01) { - /* MICROCODE HAS BEEN CHANGED goes with SCSI BUS RESET OCCURRED */ - return 2; - } else if (sense.asc == 0x29 && (sense.ascq == 0x05 || sense.ascq == 0x06)) { - /* These two go with "all others". */ - ; - } else if (sense.asc == 0x29 && sense.ascq <= 0x07) { - /* POWER ON, RESET OR BUS DEVICE RESET OCCURRED = 0 - * POWER ON OCCURRED = 1 - * SCSI BUS RESET OCCURRED = 2 - * BUS DEVICE RESET FUNCTION OCCURRED = 3 - * I_T NEXUS LOSS OCCURRED = 7 - */ - return sense.ascq; - } else if (sense.asc == 0x2F && sense.ascq == 0x01) { - /* COMMANDS CLEARED BY POWER LOSS NOTIFICATION */ - return 8; - } - return (sense.asc << 8) | sense.ascq; -} - -void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense) -{ - int prec1, prec2; - if (sense.key != UNIT_ATTENTION) { - return; - } - trace_scsi_device_set_ua(sdev->id, sdev->lun, sense.key, - sense.asc, sense.ascq); - - /* - * Override a pre-existing unit attention condition, except for a more - * important reset condition. - */ - prec1 = scsi_ua_precedence(sdev->unit_attention); - prec2 = scsi_ua_precedence(sense); - if (prec2 < prec1) { - sdev->unit_attention = sense; - } -} - -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_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 = SCSI_DEVICE(dev); - DeviceState *hba = dev->parent_bus->parent; - char *id; - char *path; - - id = qdev_get_dev_path(hba); - if (id) { - path = g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun); - } else { - path = g_strdup_printf("%d:%d:%d", d->channel, d->id, d->lun); - } - g_free(id); - return path; -} - -static char *scsibus_get_fw_dev_path(DeviceState *dev) -{ - SCSIDevice *d = SCSI_DEVICE(dev); - return g_strdup_printf("channel@%x/%s@%x,%x", d->channel, - qdev_fw_name(dev), d->id, d->lun); -} - -SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun) -{ - BusChild *kid; - SCSIDevice *target_dev = NULL; - - QTAILQ_FOREACH_REVERSE(kid, &bus->qbus.children, ChildrenHead, sibling) { - DeviceState *qdev = kid->child; - SCSIDevice *dev = SCSI_DEVICE(qdev); - - if (dev->channel == channel && dev->id == id) { - if (dev->lun == lun) { - return dev; - } - target_dev = dev; - } - } - return target_dev; -} - -/* SCSI request list. For simplicity, pv points to the whole device */ - -static void put_scsi_requests(QEMUFile *f, void *pv, size_t size) -{ - SCSIDevice *s = pv; - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus); - SCSIRequest *req; - - QTAILQ_FOREACH(req, &s->requests, next) { - assert(!req->io_canceled); - assert(req->status == -1); - assert(req->enqueued); - - qemu_put_sbyte(f, req->retry ? 1 : 2); - qemu_put_buffer(f, req->cmd.buf, sizeof(req->cmd.buf)); - qemu_put_be32s(f, &req->tag); - qemu_put_be32s(f, &req->lun); - if (bus->info->save_request) { - bus->info->save_request(f, req); - } - if (req->ops->save_request) { - req->ops->save_request(f, req); - } - } - qemu_put_sbyte(f, 0); -} - -static int get_scsi_requests(QEMUFile *f, void *pv, size_t size) -{ - SCSIDevice *s = pv; - SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus); - int8_t sbyte; - - while ((sbyte = qemu_get_sbyte(f)) > 0) { - uint8_t buf[SCSI_CMD_BUF_SIZE]; - uint32_t tag; - uint32_t lun; - SCSIRequest *req; - - qemu_get_buffer(f, buf, sizeof(buf)); - qemu_get_be32s(f, &tag); - qemu_get_be32s(f, &lun); - req = scsi_req_new(s, tag, lun, buf, NULL); - req->retry = (sbyte == 1); - if (bus->info->load_request) { - req->hba_private = bus->info->load_request(f, req); - } - if (req->ops->load_request) { - req->ops->load_request(f, req); - } - - /* Just restart it later. */ - scsi_req_enqueue_internal(req); - - /* At this point, the request will be kept alive by the reference - * added by scsi_req_enqueue_internal, so we can release our reference. - * The HBA of course will add its own reference in the load_request - * callback if it needs to hold on the SCSIRequest. - */ - scsi_req_unref(req); - } - - return 0; -} - -static const VMStateInfo vmstate_info_scsi_requests = { - .name = "scsi-requests", - .get = get_scsi_requests, - .put = put_scsi_requests, -}; - -static bool scsi_sense_state_needed(void *opaque) -{ - SCSIDevice *s = opaque; - - return s->sense_len > SCSI_SENSE_BUF_SIZE_OLD; -} - -static const VMStateDescription vmstate_scsi_sense_state = { - .name = "SCSIDevice/sense", - .version_id = 1, - .minimum_version_id = 1, - .needed = scsi_sense_state_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8_SUB_ARRAY(sense, SCSIDevice, - SCSI_SENSE_BUF_SIZE_OLD, - SCSI_SENSE_BUF_SIZE - SCSI_SENSE_BUF_SIZE_OLD), - VMSTATE_END_OF_LIST() - } -}; - -const VMStateDescription vmstate_scsi_device = { - .name = "SCSIDevice", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(unit_attention.key, SCSIDevice), - VMSTATE_UINT8(unit_attention.asc, SCSIDevice), - VMSTATE_UINT8(unit_attention.ascq, SCSIDevice), - VMSTATE_BOOL(sense_is_ua, SCSIDevice), - VMSTATE_UINT8_SUB_ARRAY(sense, SCSIDevice, 0, SCSI_SENSE_BUF_SIZE_OLD), - VMSTATE_UINT32(sense_len, SCSIDevice), - { - .name = "requests", - .version_id = 0, - .field_exists = NULL, - .size = 0, /* ouch */ - .info = &vmstate_info_scsi_requests, - .flags = VMS_SINGLE, - .offset = 0, - }, - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_scsi_sense_state, - NULL - } -}; - -static void scsi_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - set_bit(DEVICE_CATEGORY_STORAGE, k->categories); - k->bus_type = TYPE_SCSI_BUS; - k->realize = scsi_qdev_realize; - k->unrealize = scsi_qdev_unrealize; - k->props = scsi_props; -} - -static void scsi_dev_instance_init(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - SCSIDevice *s = SCSI_DEVICE(dev); - - device_add_bootindex_property(obj, &s->conf.bootindex, - "bootindex", NULL, - &s->qdev, NULL); -} - -static const TypeInfo scsi_device_type_info = { - .name = TYPE_SCSI_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(SCSIDevice), - .abstract = true, - .class_size = sizeof(SCSIDeviceClass), - .class_init = scsi_device_class_init, - .instance_init = scsi_dev_instance_init, -}; - -static void scsi_register_types(void) -{ - type_register_static(&scsi_bus_info); - type_register_static(&scsi_device_type_info); -} - -type_init(scsi_register_types) diff --git a/qemu/hw/scsi/scsi-disk.c b/qemu/hw/scsi/scsi-disk.c deleted file mode 100644 index c3ce54a20..000000000 --- a/qemu/hw/scsi/scsi-disk.c +++ /dev/null @@ -1,2828 +0,0 @@ -/* - * SCSI Device emulation - * - * Copyright (c) 2006 CodeSourcery. - * Based on code by Fabrice Bellard - * - * Written by Paul Brook - * Modifications: - * 2009-Dec-12 Artyom Tarasenko : implemented stamdard inquiry for the case - * when the allocation length of CDB is smaller - * than 36. - * 2009-Oct-13 Artyom Tarasenko : implemented the block descriptor in the - * MODE SENSE response. - * - * This code is licensed under the LGPL. - * - * Note that this file only handles the SCSI architecture model and device - * commands. Emulation of interface/link layer protocols is handled by - * the host adapter emulator. - */ - -//#define DEBUG_SCSI - -#ifdef DEBUG_SCSI -#define DPRINTF(fmt, ...) \ -do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/error-report.h" -#include "hw/scsi/scsi.h" -#include "block/scsi.h" -#include "sysemu/sysemu.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "hw/block/block.h" -#include "sysemu/dma.h" -#include "qemu/cutils.h" - -#ifdef __linux -#include -#endif - -#define SCSI_WRITE_SAME_MAX 524288 -#define SCSI_DMA_BUF_SIZE 131072 -#define SCSI_MAX_INQUIRY_LEN 256 -#define SCSI_MAX_MODE_LEN 256 - -#define DEFAULT_DISCARD_GRANULARITY 4096 -#define DEFAULT_MAX_UNMAP_SIZE (1 << 30) /* 1 GB */ -#define DEFAULT_MAX_IO_SIZE INT_MAX /* 2 GB - 1 block */ - -typedef struct SCSIDiskState SCSIDiskState; - -typedef struct SCSIDiskReq { - SCSIRequest req; - /* Both sector and sector_count are in terms of qemu 512 byte blocks. */ - uint64_t sector; - uint32_t sector_count; - uint32_t buflen; - bool started; - struct iovec iov; - QEMUIOVector qiov; - BlockAcctCookie acct; -} SCSIDiskReq; - -#define SCSI_DISK_F_REMOVABLE 0 -#define SCSI_DISK_F_DPOFUA 1 -#define SCSI_DISK_F_NO_REMOVABLE_DEVOPS 2 - -struct SCSIDiskState -{ - SCSIDevice qdev; - uint32_t features; - bool media_changed; - bool media_event; - bool eject_request; - uint16_t port_index; - uint64_t max_unmap_size; - uint64_t max_io_size; - QEMUBH *bh; - char *version; - char *serial; - char *vendor; - char *product; - bool tray_open; - bool tray_locked; -}; - -static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed); - -static void scsi_free_request(SCSIRequest *req) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - - qemu_vfree(r->iov.iov_base); -} - -/* Helper function for command completion with sense. */ -static void scsi_check_condition(SCSIDiskReq *r, SCSISense sense) -{ - DPRINTF("Command complete tag=0x%x sense=%d/%d/%d\n", - r->req.tag, sense.key, sense.asc, sense.ascq); - scsi_req_build_sense(&r->req, sense); - scsi_req_complete(&r->req, CHECK_CONDITION); -} - -static uint32_t scsi_init_iovec(SCSIDiskReq *r, size_t size) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - - if (!r->iov.iov_base) { - r->buflen = size; - r->iov.iov_base = blk_blockalign(s->qdev.conf.blk, r->buflen); - } - r->iov.iov_len = MIN(r->sector_count * 512, r->buflen); - qemu_iovec_init_external(&r->qiov, &r->iov, 1); - return r->qiov.size / 512; -} - -static void scsi_disk_save_request(QEMUFile *f, SCSIRequest *req) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - - qemu_put_be64s(f, &r->sector); - qemu_put_be32s(f, &r->sector_count); - qemu_put_be32s(f, &r->buflen); - if (r->buflen) { - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len); - } else if (!req->retry) { - uint32_t len = r->iov.iov_len; - qemu_put_be32s(f, &len); - qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len); - } - } -} - -static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - - qemu_get_be64s(f, &r->sector); - qemu_get_be32s(f, &r->sector_count); - qemu_get_be32s(f, &r->buflen); - if (r->buflen) { - scsi_init_iovec(r, r->buflen); - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len); - } else if (!r->req.retry) { - uint32_t len; - qemu_get_be32s(f, &len); - r->iov.iov_len = len; - assert(r->iov.iov_len <= r->buflen); - qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len); - } - } - - qemu_iovec_init_external(&r->qiov, &r->iov, 1); -} - -static void scsi_aio_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 (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); - goto done; - } - - if (ret < 0) { - 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: - scsi_req_unref(&r->req); -} - -static bool scsi_is_cmd_fua(SCSICommand *cmd) -{ - switch (cmd->buf[0]) { - case READ_10: - case READ_12: - case READ_16: - case WRITE_10: - case WRITE_12: - case WRITE_16: - return (cmd->buf[1] & 8) != 0; - - case VERIFY_10: - case VERIFY_12: - case VERIFY_16: - case WRITE_VERIFY_10: - case WRITE_VERIFY_12: - case WRITE_VERIFY_16: - return true; - - case READ_6: - case WRITE_6: - default: - return false; - } -} - -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; - } - - if (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_aio_complete, r); - return; - } - - scsi_req_complete(&r->req, GOOD); - -done: - scsi_req_unref(&r->req); -} - -static void scsi_dma_complete_noio(SCSIDiskReq *r, int ret) -{ - 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, false)) { - goto done; - } - } - - r->sector += r->sector_count; - r->sector_count = 0; - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - scsi_write_do_fua(r); - return; - } else { - scsi_req_complete(&r->req, GOOD); - } - -done: - scsi_req_unref(&r->req); -} - -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); - 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) -{ - SCSIDiskReq *r = (SCSIDiskReq *)opaque; - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - int n; - - assert(r->req.aiocb != NULL); - 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, 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; - r->sector += n; - r->sector_count -= n; - scsi_req_data(&r->req, r->qiov.size); - -done: - scsi_req_unref(&r->req); -} - -/* Actually issue a read to the block device. */ -static void scsi_do_read(SCSIDiskReq *r, int ret) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - uint32_t n; - - 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, false)) { - goto done; - } - } - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - - if (r->req.sg) { - dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_READ); - r->req.resid -= r->req.sg->size; - r->req.aiocb = dma_blk_read(s->qdev.conf.blk, r->req.sg, r->sector, - scsi_dma_complete, r); - } else { - n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); - block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, - n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ); - r->req.aiocb = blk_aio_readv(s->qdev.conf.blk, r->sector, &r->qiov, n, - scsi_read_complete, r); - } - -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) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - bool first; - - DPRINTF("Read sector_count=%d\n", r->sector_count); - if (r->sector_count == 0) { - /* This also clears the sense buffer for REQUEST SENSE. */ - scsi_req_complete(&r->req, GOOD); - return; - } - - /* No data transfer may already be in progress */ - assert(r->req.aiocb == NULL); - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - DPRINTF("Data transfer direction invalid\n"); - scsi_read_complete(r, -EINVAL); - return; - } - - if (s->tray_open) { - scsi_read_complete(r, -ENOMEDIUM); - return; - } - - first = !r->started; - r->started = true; - 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_cb, r); - } else { - scsi_do_read(r, 0); - } -} - -/* - * scsi_handle_rw_error has two return values. 0 means that the error - * must be ignored, 1 means that the error has been processed and the - * caller should not do anything else for this request. Note that - * scsi_handle_rw_error always manages its reference counts, independent - * of the return value. - */ -static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed) -{ - 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)); - break; - case ENOMEM: - scsi_check_condition(r, SENSE_CODE(TARGET_FAILURE)); - break; - case EINVAL: - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - break; - case ENOSPC: - scsi_check_condition(r, SENSE_CODE(SPACE_ALLOC_FAILED)); - break; - default: - scsi_check_condition(r, SENSE_CODE(IO_ERROR)); - break; - } - } - blk_error_action(s->qdev.conf.blk, action, is_read, error); - if (action == BLOCK_ERROR_ACTION_STOP) { - scsi_req_retry(&r->req); - } - return action != BLOCK_ERROR_ACTION_IGNORE; -} - -static void scsi_write_complete_noio(SCSIDiskReq *r, int ret) -{ - uint32_t n; - - 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, false)) { - goto done; - } - } - - n = r->qiov.size / 512; - r->sector += n; - r->sector_count -= n; - if (r->sector_count == 0) { - scsi_write_do_fua(r); - return; - } else { - scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); - DPRINTF("Write complete tag=0x%x more=%zd\n", r->req.tag, r->qiov.size); - scsi_req_data(&r->req, r->qiov.size); - } - -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); - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - uint32_t n; - - /* No data transfer may already be in progress */ - assert(r->req.aiocb == NULL); - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - if (r->req.cmd.mode != SCSI_XFER_TO_DEV) { - DPRINTF("Data transfer direction invalid\n"); - 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_noio(r, 0); - return; - } - if (s->tray_open) { - scsi_write_complete_noio(r, -ENOMEDIUM); - return; - } - - if (r->req.cmd.buf[0] == VERIFY_10 || r->req.cmd.buf[0] == VERIFY_12 || - r->req.cmd.buf[0] == VERIFY_16) { - if (r->req.sg) { - scsi_dma_complete_noio(r, 0); - } else { - scsi_write_complete_noio(r, 0); - } - return; - } - - if (r->req.sg) { - dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_WRITE); - r->req.resid -= r->req.sg->size; - r->req.aiocb = dma_blk_write(s->qdev.conf.blk, r->req.sg, r->sector, - scsi_dma_complete, r); - } else { - n = r->qiov.size / 512; - block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, - n * BDRV_SECTOR_SIZE, BLOCK_ACCT_WRITE); - r->req.aiocb = blk_aio_writev(s->qdev.conf.blk, r->sector, &r->qiov, n, - scsi_write_complete, r); - } -} - -/* Return a pointer to the data buffer. */ -static uint8_t *scsi_get_buf(SCSIRequest *req) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - - return (uint8_t *)r->iov.iov_base; -} - -static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); - int buflen = 0; - int start; - - if (req->cmd.buf[1] & 0x1) { - /* Vital product data */ - uint8_t page_code = req->cmd.buf[2]; - - outbuf[buflen++] = s->qdev.type & 0x1f; - outbuf[buflen++] = page_code ; // this page - outbuf[buflen++] = 0x00; - outbuf[buflen++] = 0x00; - start = buflen; - - switch (page_code) { - case 0x00: /* Supported page codes, mandatory */ - { - DPRINTF("Inquiry EVPD[Supported pages] " - "buffer size %zd\n", req->cmd.xfer); - outbuf[buflen++] = 0x00; // list of supported pages (this page) - if (s->serial) { - outbuf[buflen++] = 0x80; // unit serial number - } - outbuf[buflen++] = 0x83; // device identification - if (s->qdev.type == TYPE_DISK) { - outbuf[buflen++] = 0xb0; // block limits - outbuf[buflen++] = 0xb2; // thin provisioning - } - break; - } - case 0x80: /* Device serial number, optional */ - { - int l; - - if (!s->serial) { - DPRINTF("Inquiry (EVPD[Serial number] not supported\n"); - return -1; - } - - l = strlen(s->serial); - if (l > 20) { - l = 20; - } - - DPRINTF("Inquiry EVPD[Serial number] " - "buffer size %zd\n", req->cmd.xfer); - memcpy(outbuf+buflen, s->serial, l); - buflen += l; - break; - } - - case 0x83: /* Device identification page, mandatory */ - { - const char *str = s->serial ?: blk_name(s->qdev.conf.blk); - int max_len = s->serial ? 20 : 255 - 8; - int id_len = strlen(str); - - if (id_len > max_len) { - id_len = max_len; - } - DPRINTF("Inquiry EVPD[Device identification] " - "buffer size %zd\n", req->cmd.xfer); - - outbuf[buflen++] = 0x2; // ASCII - outbuf[buflen++] = 0; // not officially assigned - outbuf[buflen++] = 0; // reserved - outbuf[buflen++] = id_len; // length of data following - memcpy(outbuf+buflen, str, id_len); - buflen += id_len; - - 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->qdev.wwn); - buflen += 8; - } - - 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->qdev.port_wwn); - buflen += 8; - } - - if (s->port_index) { - outbuf[buflen++] = 0x61; // SAS / Binary - outbuf[buflen++] = 0x94; // PIV / Target port / relative target port - outbuf[buflen++] = 0; // reserved - outbuf[buflen++] = 4; - stw_be_p(&outbuf[buflen + 2], s->port_index); - buflen += 4; - } - break; - } - case 0xb0: /* block limits */ - { - unsigned int unmap_sectors = - s->qdev.conf.discard_granularity / s->qdev.blocksize; - unsigned int min_io_size = - s->qdev.conf.min_io_size / s->qdev.blocksize; - unsigned int opt_io_size = - s->qdev.conf.opt_io_size / s->qdev.blocksize; - unsigned int max_unmap_sectors = - s->max_unmap_size / s->qdev.blocksize; - unsigned int max_io_sectors = - s->max_io_size / s->qdev.blocksize; - - if (s->qdev.type == TYPE_ROM) { - DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", - page_code); - return -1; - } - /* required VPD size with unmap support */ - buflen = 0x40; - memset(outbuf + 4, 0, buflen - 4); - - outbuf[4] = 0x1; /* wsnz */ - - /* optimal transfer length granularity */ - outbuf[6] = (min_io_size >> 8) & 0xff; - outbuf[7] = min_io_size & 0xff; - - /* maximum transfer length */ - outbuf[8] = (max_io_sectors >> 24) & 0xff; - outbuf[9] = (max_io_sectors >> 16) & 0xff; - outbuf[10] = (max_io_sectors >> 8) & 0xff; - outbuf[11] = max_io_sectors & 0xff; - - /* optimal transfer length */ - outbuf[12] = (opt_io_size >> 24) & 0xff; - outbuf[13] = (opt_io_size >> 16) & 0xff; - outbuf[14] = (opt_io_size >> 8) & 0xff; - outbuf[15] = opt_io_size & 0xff; - - /* max unmap LBA count, default is 1GB */ - outbuf[20] = (max_unmap_sectors >> 24) & 0xff; - outbuf[21] = (max_unmap_sectors >> 16) & 0xff; - outbuf[22] = (max_unmap_sectors >> 8) & 0xff; - outbuf[23] = max_unmap_sectors & 0xff; - - /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header. */ - outbuf[24] = 0; - outbuf[25] = 0; - outbuf[26] = 0; - outbuf[27] = 255; - - /* optimal unmap granularity */ - outbuf[28] = (unmap_sectors >> 24) & 0xff; - outbuf[29] = (unmap_sectors >> 16) & 0xff; - outbuf[30] = (unmap_sectors >> 8) & 0xff; - outbuf[31] = unmap_sectors & 0xff; - - /* max write same size */ - outbuf[36] = 0; - outbuf[37] = 0; - outbuf[38] = 0; - outbuf[39] = 0; - - outbuf[40] = (max_io_sectors >> 24) & 0xff; - outbuf[41] = (max_io_sectors >> 16) & 0xff; - outbuf[42] = (max_io_sectors >> 8) & 0xff; - outbuf[43] = max_io_sectors & 0xff; - break; - } - case 0xb2: /* thin provisioning */ - { - buflen = 8; - outbuf[4] = 0; - outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */ - outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1; - outbuf[7] = 0; - break; - } - default: - return -1; - } - /* done with EVPD */ - assert(buflen - start <= 255); - outbuf[start - 1] = buflen - start; - return buflen; - } - - /* Standard INQUIRY data */ - if (req->cmd.buf[2] != 0) { - return -1; - } - - /* PAGE CODE == 0 */ - buflen = req->cmd.xfer; - if (buflen > SCSI_MAX_INQUIRY_LEN) { - buflen = SCSI_MAX_INQUIRY_LEN; - } - - outbuf[0] = s->qdev.type & 0x1f; - outbuf[1] = (s->features & (1 << SCSI_DISK_F_REMOVABLE)) ? 0x80 : 0; - - strpadcpy((char *) &outbuf[16], 16, s->product, ' '); - strpadcpy((char *) &outbuf[8], 8, s->vendor, ' '); - - memset(&outbuf[32], 0, 4); - memcpy(&outbuf[32], s->version, MIN(4, strlen(s->version))); - /* - * We claim conformance to SPC-3, which is required for guests - * to ask for modern features like READ CAPACITY(16) or the - * block characteristics VPD page by default. Not all of SPC-3 - * is actually implemented, but we're good enough. - */ - outbuf[2] = 5; - outbuf[3] = 2 | 0x10; /* Format 2, HiSup */ - - if (buflen > 36) { - outbuf[4] = buflen - 5; /* Additional Length = (Len - 1) - 4 */ - } else { - /* If the allocation length of CDB is too small, - the additional length is not adjusted */ - outbuf[4] = 36 - 5; - } - - /* Sync data transfer and TCQ. */ - outbuf[7] = 0x10 | (req->bus->info->tcq ? 0x02 : 0); - return buflen; -} - -static inline bool media_is_dvd(SCSIDiskState *s) -{ - uint64_t nb_sectors; - if (s->qdev.type != TYPE_ROM) { - return false; - } - if (!blk_is_inserted(s->qdev.conf.blk)) { - return false; - } - if (s->tray_open) { - return false; - } - blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - return nb_sectors > CD_MAX_SECTORS; -} - -static inline bool media_is_cd(SCSIDiskState *s) -{ - uint64_t nb_sectors; - if (s->qdev.type != TYPE_ROM) { - return false; - } - if (!blk_is_inserted(s->qdev.conf.blk)) { - return false; - } - if (s->tray_open) { - return false; - } - blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - return nb_sectors <= CD_MAX_SECTORS; -} - -static int scsi_read_disc_information(SCSIDiskState *s, SCSIDiskReq *r, - uint8_t *outbuf) -{ - uint8_t type = r->req.cmd.buf[1] & 7; - - if (s->qdev.type != TYPE_ROM) { - return -1; - } - - /* Types 1/2 are only defined for Blu-Ray. */ - if (type != 0) { - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - return -1; - } - - memset(outbuf, 0, 34); - outbuf[1] = 32; - outbuf[2] = 0xe; /* last session complete, disc finalized */ - outbuf[3] = 1; /* first track on disc */ - outbuf[4] = 1; /* # of sessions */ - outbuf[5] = 1; /* first track of last session */ - outbuf[6] = 1; /* last track of last session */ - outbuf[7] = 0x20; /* unrestricted use */ - outbuf[8] = 0x00; /* CD-ROM or DVD-ROM */ - /* 9-10-11: most significant byte corresponding bytes 4-5-6 */ - /* 12-23: not meaningful for CD-ROM or DVD-ROM */ - /* 24-31: disc bar code */ - /* 32: disc application code */ - /* 33: number of OPC tables */ - - return 34; -} - -static int scsi_read_dvd_structure(SCSIDiskState *s, SCSIDiskReq *r, - uint8_t *outbuf) -{ - static const int rds_caps_size[5] = { - [0] = 2048 + 4, - [1] = 4 + 4, - [3] = 188 + 4, - [4] = 2048 + 4, - }; - - uint8_t media = r->req.cmd.buf[1]; - uint8_t layer = r->req.cmd.buf[6]; - uint8_t format = r->req.cmd.buf[7]; - int size = -1; - - if (s->qdev.type != TYPE_ROM) { - return -1; - } - if (media != 0) { - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - return -1; - } - - if (format != 0xff) { - if (s->tray_open || !blk_is_inserted(s->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); - return -1; - } - if (media_is_cd(s)) { - scsi_check_condition(r, SENSE_CODE(INCOMPATIBLE_FORMAT)); - return -1; - } - if (format >= ARRAY_SIZE(rds_caps_size)) { - return -1; - } - size = rds_caps_size[format]; - memset(outbuf, 0, size); - } - - switch (format) { - case 0x00: { - /* Physical format information */ - uint64_t nb_sectors; - if (layer != 0) { - goto fail; - } - blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - - outbuf[4] = 1; /* DVD-ROM, part version 1 */ - outbuf[5] = 0xf; /* 120mm disc, minimum rate unspecified */ - outbuf[6] = 1; /* one layer, read-only (per MMC-2 spec) */ - outbuf[7] = 0; /* default densities */ - - stl_be_p(&outbuf[12], (nb_sectors >> 2) - 1); /* end sector */ - stl_be_p(&outbuf[16], (nb_sectors >> 2) - 1); /* l0 end sector */ - break; - } - - case 0x01: /* DVD copyright information, all zeros */ - break; - - case 0x03: /* BCA information - invalid field for no BCA info */ - return -1; - - case 0x04: /* DVD disc manufacturing information, all zeros */ - break; - - case 0xff: { /* List capabilities */ - int i; - size = 4; - for (i = 0; i < ARRAY_SIZE(rds_caps_size); i++) { - if (!rds_caps_size[i]) { - continue; - } - outbuf[size] = i; - outbuf[size + 1] = 0x40; /* Not writable, readable */ - stw_be_p(&outbuf[size + 2], rds_caps_size[i]); - size += 4; - } - break; - } - - default: - return -1; - } - - /* Size of buffer, not including 2 byte size field */ - stw_be_p(outbuf, size - 2); - return size; - -fail: - return -1; -} - -static int scsi_event_status_media(SCSIDiskState *s, uint8_t *outbuf) -{ - uint8_t event_code, media_status; - - media_status = 0; - if (s->tray_open) { - media_status = MS_TRAY_OPEN; - } else if (blk_is_inserted(s->qdev.conf.blk)) { - media_status = MS_MEDIA_PRESENT; - } - - /* Event notification descriptor */ - event_code = MEC_NO_CHANGE; - if (media_status != MS_TRAY_OPEN) { - if (s->media_event) { - event_code = MEC_NEW_MEDIA; - s->media_event = false; - } else if (s->eject_request) { - event_code = MEC_EJECT_REQUESTED; - s->eject_request = false; - } - } - - outbuf[0] = event_code; - outbuf[1] = media_status; - - /* These fields are reserved, just clear them. */ - outbuf[2] = 0; - outbuf[3] = 0; - return 4; -} - -static int scsi_get_event_status_notification(SCSIDiskState *s, SCSIDiskReq *r, - uint8_t *outbuf) -{ - int size; - uint8_t *buf = r->req.cmd.buf; - uint8_t notification_class_request = buf[4]; - if (s->qdev.type != TYPE_ROM) { - return -1; - } - if ((buf[1] & 1) == 0) { - /* asynchronous */ - return -1; - } - - size = 4; - outbuf[0] = outbuf[1] = 0; - outbuf[3] = 1 << GESN_MEDIA; /* supported events */ - if (notification_class_request & (1 << GESN_MEDIA)) { - outbuf[2] = GESN_MEDIA; - size += scsi_event_status_media(s, &outbuf[size]); - } else { - outbuf[2] = 0x80; - } - stw_be_p(outbuf, size - 4); - return size; -} - -static int scsi_get_configuration(SCSIDiskState *s, uint8_t *outbuf) -{ - int current; - - if (s->qdev.type != TYPE_ROM) { - return -1; - } - - if (media_is_dvd(s)) { - current = MMC_PROFILE_DVD_ROM; - } else if (media_is_cd(s)) { - current = MMC_PROFILE_CD_ROM; - } else { - current = MMC_PROFILE_NONE; - } - - memset(outbuf, 0, 40); - stl_be_p(&outbuf[0], 36); /* Bytes after the data length field */ - stw_be_p(&outbuf[6], current); - /* outbuf[8] - outbuf[19]: Feature 0 - Profile list */ - outbuf[10] = 0x03; /* persistent, current */ - outbuf[11] = 8; /* two profiles */ - stw_be_p(&outbuf[12], MMC_PROFILE_DVD_ROM); - outbuf[14] = (current == MMC_PROFILE_DVD_ROM); - stw_be_p(&outbuf[16], MMC_PROFILE_CD_ROM); - outbuf[18] = (current == MMC_PROFILE_CD_ROM); - /* outbuf[20] - outbuf[31]: Feature 1 - Core feature */ - stw_be_p(&outbuf[20], 1); - outbuf[22] = 0x08 | 0x03; /* version 2, persistent, current */ - outbuf[23] = 8; - stl_be_p(&outbuf[24], 1); /* SCSI */ - outbuf[28] = 1; /* DBE = 1, mandatory */ - /* outbuf[32] - outbuf[39]: Feature 3 - Removable media feature */ - stw_be_p(&outbuf[32], 3); - outbuf[34] = 0x08 | 0x03; /* version 2, persistent, current */ - outbuf[35] = 4; - outbuf[36] = 0x39; /* tray, load=1, eject=1, unlocked at powerup, lock=1 */ - /* TODO: Random readable, CD read, DVD read, drive serial number, - power management */ - return 40; -} - -static int scsi_emulate_mechanism_status(SCSIDiskState *s, uint8_t *outbuf) -{ - if (s->qdev.type != TYPE_ROM) { - return -1; - } - memset(outbuf, 0, 8); - outbuf[5] = 1; /* CD-ROM */ - return 8; -} - -static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, - int page_control) -{ - static const int mode_sense_valid[0x3f] = { - [MODE_PAGE_HD_GEOMETRY] = (1 << TYPE_DISK), - [MODE_PAGE_FLEXIBLE_DISK_GEOMETRY] = (1 << TYPE_DISK), - [MODE_PAGE_CACHING] = (1 << TYPE_DISK) | (1 << TYPE_ROM), - [MODE_PAGE_R_W_ERROR] = (1 << TYPE_DISK) | (1 << TYPE_ROM), - [MODE_PAGE_AUDIO_CTL] = (1 << TYPE_ROM), - [MODE_PAGE_CAPABILITIES] = (1 << TYPE_ROM), - }; - - uint8_t *p = *p_outbuf + 2; - int length; - - if ((mode_sense_valid[page] & (1 << s->qdev.type)) == 0) { - return -1; - } - - /* - * If Changeable Values are requested, a mask denoting those mode parameters - * that are changeable shall be returned. As we currently don't support - * parameter changes via MODE_SELECT all bits are returned set to zero. - * The buffer was already menset to zero by the caller of this function. - * - * The offsets here are off by two compared to the descriptions in the - * SCSI specs, because those include a 2-byte header. This is unfortunate, - * but it is done so that offsets are consistent within our implementation - * of MODE SENSE and MODE SELECT. MODE SELECT has to deal with both - * 2-byte and 4-byte headers. - */ - switch (page) { - case MODE_PAGE_HD_GEOMETRY: - length = 0x16; - if (page_control == 1) { /* Changeable Values */ - break; - } - /* if a geometry hint is available, use it */ - p[0] = (s->qdev.conf.cyls >> 16) & 0xff; - p[1] = (s->qdev.conf.cyls >> 8) & 0xff; - p[2] = s->qdev.conf.cyls & 0xff; - p[3] = s->qdev.conf.heads & 0xff; - /* Write precomp start cylinder, disabled */ - p[4] = (s->qdev.conf.cyls >> 16) & 0xff; - p[5] = (s->qdev.conf.cyls >> 8) & 0xff; - p[6] = s->qdev.conf.cyls & 0xff; - /* Reduced current start cylinder, disabled */ - p[7] = (s->qdev.conf.cyls >> 16) & 0xff; - p[8] = (s->qdev.conf.cyls >> 8) & 0xff; - p[9] = s->qdev.conf.cyls & 0xff; - /* Device step rate [ns], 200ns */ - p[10] = 0; - p[11] = 200; - /* Landing zone cylinder */ - p[12] = 0xff; - p[13] = 0xff; - p[14] = 0xff; - /* Medium rotation rate [rpm], 5400 rpm */ - p[18] = (5400 >> 8) & 0xff; - p[19] = 5400 & 0xff; - break; - - case MODE_PAGE_FLEXIBLE_DISK_GEOMETRY: - length = 0x1e; - if (page_control == 1) { /* Changeable Values */ - break; - } - /* Transfer rate [kbit/s], 5Mbit/s */ - p[0] = 5000 >> 8; - p[1] = 5000 & 0xff; - /* if a geometry hint is available, use it */ - p[2] = s->qdev.conf.heads & 0xff; - p[3] = s->qdev.conf.secs & 0xff; - p[4] = s->qdev.blocksize >> 8; - p[6] = (s->qdev.conf.cyls >> 8) & 0xff; - p[7] = s->qdev.conf.cyls & 0xff; - /* Write precomp start cylinder, disabled */ - p[8] = (s->qdev.conf.cyls >> 8) & 0xff; - p[9] = s->qdev.conf.cyls & 0xff; - /* Reduced current start cylinder, disabled */ - p[10] = (s->qdev.conf.cyls >> 8) & 0xff; - p[11] = s->qdev.conf.cyls & 0xff; - /* Device step rate [100us], 100us */ - p[12] = 0; - p[13] = 1; - /* Device step pulse width [us], 1us */ - p[14] = 1; - /* Device head settle delay [100us], 100us */ - p[15] = 0; - p[16] = 1; - /* Motor on delay [0.1s], 0.1s */ - p[17] = 1; - /* Motor off delay [0.1s], 0.1s */ - p[18] = 1; - /* Medium rotation rate [rpm], 5400 rpm */ - p[26] = (5400 >> 8) & 0xff; - p[27] = 5400 & 0xff; - break; - - case MODE_PAGE_CACHING: - length = 0x12; - if (page_control == 1 || /* Changeable Values */ - blk_enable_write_cache(s->qdev.conf.blk)) { - p[0] = 4; /* WCE */ - } - break; - - case MODE_PAGE_R_W_ERROR: - length = 10; - if (page_control == 1) { /* Changeable Values */ - break; - } - p[0] = 0x80; /* Automatic Write Reallocation Enabled */ - if (s->qdev.type == TYPE_ROM) { - p[1] = 0x20; /* Read Retry Count */ - } - break; - - case MODE_PAGE_AUDIO_CTL: - length = 14; - break; - - case MODE_PAGE_CAPABILITIES: - length = 0x14; - if (page_control == 1) { /* Changeable Values */ - break; - } - - p[0] = 0x3b; /* CD-R & CD-RW read */ - p[1] = 0; /* Writing not supported */ - p[2] = 0x7f; /* Audio, composite, digital out, - mode 2 form 1&2, multi session */ - p[3] = 0xff; /* CD DA, DA accurate, RW supported, - RW corrected, C2 errors, ISRC, - UPC, Bar code */ - p[4] = 0x2d | (s->tray_locked ? 2 : 0); - /* Locking supported, jumper present, eject, tray */ - p[5] = 0; /* no volume & mute control, no - changer */ - p[6] = (50 * 176) >> 8; /* 50x read speed */ - p[7] = (50 * 176) & 0xff; - p[8] = 2 >> 8; /* Two volume levels */ - p[9] = 2 & 0xff; - p[10] = 2048 >> 8; /* 2M buffer */ - p[11] = 2048 & 0xff; - p[12] = (16 * 176) >> 8; /* 16x read speed current */ - p[13] = (16 * 176) & 0xff; - p[16] = (16 * 176) >> 8; /* 16x write speed */ - p[17] = (16 * 176) & 0xff; - p[18] = (16 * 176) >> 8; /* 16x write speed current */ - p[19] = (16 * 176) & 0xff; - break; - - default: - return -1; - } - - assert(length < 256); - (*p_outbuf)[0] = page; - (*p_outbuf)[1] = length; - *p_outbuf += length + 2; - return length + 2; -} - -static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - uint64_t nb_sectors; - bool dbd; - int page, buflen, ret, page_control; - uint8_t *p; - uint8_t dev_specific_param; - - dbd = (r->req.cmd.buf[1] & 0x8) != 0; - page = r->req.cmd.buf[2] & 0x3f; - page_control = (r->req.cmd.buf[2] & 0xc0) >> 6; - DPRINTF("Mode Sense(%d) (page %d, xfer %zd, page_control %d)\n", - (r->req.cmd.buf[0] == MODE_SENSE) ? 6 : 10, page, r->req.cmd.xfer, page_control); - memset(outbuf, 0, r->req.cmd.xfer); - p = outbuf; - - if (s->qdev.type == TYPE_DISK) { - dev_specific_param = s->features & (1 << SCSI_DISK_F_DPOFUA) ? 0x10 : 0; - if (blk_is_read_only(s->qdev.conf.blk)) { - dev_specific_param |= 0x80; /* Readonly. */ - } - } else { - /* MMC prescribes that CD/DVD drives have no block descriptors, - * and defines no device-specific parameter. */ - dev_specific_param = 0x00; - dbd = true; - } - - if (r->req.cmd.buf[0] == MODE_SENSE) { - p[1] = 0; /* Default media type. */ - p[2] = dev_specific_param; - p[3] = 0; /* Block descriptor length. */ - p += 4; - } else { /* MODE_SENSE_10 */ - p[2] = 0; /* Default media type. */ - p[3] = dev_specific_param; - p[6] = p[7] = 0; /* Block descriptor length. */ - p += 8; - } - - blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - if (!dbd && nb_sectors) { - if (r->req.cmd.buf[0] == MODE_SENSE) { - outbuf[3] = 8; /* Block descriptor length */ - } else { /* MODE_SENSE_10 */ - outbuf[7] = 8; /* Block descriptor length */ - } - nb_sectors /= (s->qdev.blocksize / 512); - if (nb_sectors > 0xffffff) { - nb_sectors = 0; - } - p[0] = 0; /* media density code */ - p[1] = (nb_sectors >> 16) & 0xff; - p[2] = (nb_sectors >> 8) & 0xff; - p[3] = nb_sectors & 0xff; - p[4] = 0; /* reserved */ - p[5] = 0; /* bytes 5-7 are the sector size in bytes */ - p[6] = s->qdev.blocksize >> 8; - p[7] = 0; - p += 8; - } - - if (page_control == 3) { - /* Saved Values */ - scsi_check_condition(r, SENSE_CODE(SAVING_PARAMS_NOT_SUPPORTED)); - return -1; - } - - if (page == 0x3f) { - for (page = 0; page <= 0x3e; page++) { - mode_sense_page(s, page, &p, page_control); - } - } else { - ret = mode_sense_page(s, page, &p, page_control); - if (ret == -1) { - return -1; - } - } - - buflen = p - outbuf; - /* - * The mode data length field specifies the length in bytes of the - * following data that is available to be transferred. The mode data - * length does not include itself. - */ - if (r->req.cmd.buf[0] == MODE_SENSE) { - outbuf[0] = buflen - 1; - } else { /* MODE_SENSE_10 */ - outbuf[0] = ((buflen - 2) >> 8) & 0xff; - outbuf[1] = (buflen - 2) & 0xff; - } - return buflen; -} - -static int scsi_disk_emulate_read_toc(SCSIRequest *req, uint8_t *outbuf) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); - int start_track, format, msf, toclen; - uint64_t nb_sectors; - - msf = req->cmd.buf[1] & 2; - format = req->cmd.buf[2] & 0xf; - start_track = req->cmd.buf[6]; - blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1); - nb_sectors /= s->qdev.blocksize / 512; - switch (format) { - case 0: - toclen = cdrom_read_toc(nb_sectors, outbuf, msf, start_track); - break; - case 1: - /* multi session : only a single session defined */ - toclen = 12; - memset(outbuf, 0, 12); - outbuf[1] = 0x0a; - outbuf[2] = 0x01; - outbuf[3] = 0x01; - break; - case 2: - toclen = cdrom_read_toc_raw(nb_sectors, outbuf, msf, start_track); - break; - default: - return -1; - } - return toclen; -} - -static int scsi_disk_emulate_start_stop(SCSIDiskReq *r) -{ - SCSIRequest *req = &r->req; - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); - bool start = req->cmd.buf[4] & 1; - bool loej = req->cmd.buf[4] & 2; /* load on start, eject on !start */ - int pwrcnd = req->cmd.buf[4] & 0xf0; - - if (pwrcnd) { - /* eject/load only happens for power condition == 0 */ - return 0; - } - - if ((s->features & (1 << SCSI_DISK_F_REMOVABLE)) && loej) { - if (!start && !s->tray_open && s->tray_locked) { - scsi_check_condition(r, - blk_is_inserted(s->qdev.conf.blk) - ? SENSE_CODE(ILLEGAL_REQ_REMOVAL_PREVENTED) - : SENSE_CODE(NOT_READY_REMOVAL_PREVENTED)); - return -1; - } - - if (s->tray_open != !start) { - blk_eject(s->qdev.conf.blk, !start); - s->tray_open = !start; - } - } - return 0; -} - -static void scsi_disk_emulate_read_data(SCSIRequest *req) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - int buflen = r->iov.iov_len; - - if (buflen) { - DPRINTF("Read buf_len=%d\n", buflen); - r->iov.iov_len = 0; - r->started = true; - scsi_req_data(&r->req, buflen); - return; - } - - /* This also clears the sense buffer for REQUEST SENSE. */ - scsi_req_complete(&r->req, GOOD); -} - -static int scsi_disk_check_mode_select(SCSIDiskState *s, int page, - uint8_t *inbuf, int inlen) -{ - uint8_t mode_current[SCSI_MAX_MODE_LEN]; - uint8_t mode_changeable[SCSI_MAX_MODE_LEN]; - uint8_t *p; - int len, expected_len, changeable_len, i; - - /* The input buffer does not include the page header, so it is - * off by 2 bytes. - */ - expected_len = inlen + 2; - if (expected_len > SCSI_MAX_MODE_LEN) { - return -1; - } - - p = mode_current; - memset(mode_current, 0, inlen + 2); - len = mode_sense_page(s, page, &p, 0); - if (len < 0 || len != expected_len) { - return -1; - } - - p = mode_changeable; - memset(mode_changeable, 0, inlen + 2); - changeable_len = mode_sense_page(s, page, &p, 1); - assert(changeable_len == len); - - /* Check that unchangeable bits are the same as what MODE SENSE - * would return. - */ - for (i = 2; i < len; i++) { - if (((mode_current[i] ^ inbuf[i - 2]) & ~mode_changeable[i]) != 0) { - return -1; - } - } - return 0; -} - -static void scsi_disk_apply_mode_select(SCSIDiskState *s, int page, uint8_t *p) -{ - switch (page) { - case MODE_PAGE_CACHING: - blk_set_enable_write_cache(s->qdev.conf.blk, (p[0] & 4) != 0); - break; - - default: - break; - } -} - -static int mode_select_pages(SCSIDiskReq *r, uint8_t *p, int len, bool change) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - - while (len > 0) { - int page, subpage, page_len; - - /* Parse both possible formats for the mode page headers. */ - page = p[0] & 0x3f; - if (p[0] & 0x40) { - if (len < 4) { - goto invalid_param_len; - } - subpage = p[1]; - page_len = lduw_be_p(&p[2]); - p += 4; - len -= 4; - } else { - if (len < 2) { - goto invalid_param_len; - } - subpage = 0; - page_len = p[1]; - p += 2; - len -= 2; - } - - if (subpage) { - goto invalid_param; - } - if (page_len > len) { - goto invalid_param_len; - } - - if (!change) { - if (scsi_disk_check_mode_select(s, page, p, page_len) < 0) { - goto invalid_param; - } - } else { - scsi_disk_apply_mode_select(s, page, p); - } - - p += page_len; - len -= page_len; - } - return 0; - -invalid_param: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM)); - return -1; - -invalid_param_len: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN)); - return -1; -} - -static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - uint8_t *p = inbuf; - int cmd = r->req.cmd.buf[0]; - int len = r->req.cmd.xfer; - int hdr_len = (cmd == MODE_SELECT ? 4 : 8); - int bd_len; - int pass; - - /* We only support PF=1, SP=0. */ - if ((r->req.cmd.buf[1] & 0x11) != 0x10) { - goto invalid_field; - } - - if (len < hdr_len) { - goto invalid_param_len; - } - - bd_len = (cmd == MODE_SELECT ? p[3] : lduw_be_p(&p[6])); - len -= hdr_len; - p += hdr_len; - if (len < bd_len) { - goto invalid_param_len; - } - if (bd_len != 0 && bd_len != 8) { - goto invalid_param; - } - - len -= bd_len; - p += bd_len; - - /* Ensure no change is made if there is an error! */ - for (pass = 0; pass < 2; pass++) { - if (mode_select_pages(r, p, len, pass == 1) < 0) { - assert(pass == 0); - return; - } - } - if (!blk_enable_write_cache(s->qdev.conf.blk)) { - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_aio_complete, r); - return; - } - - scsi_req_complete(&r->req, GOOD); - return; - -invalid_param: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM)); - return; - -invalid_param_len: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN)); - return; - -invalid_field: - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); -} - -static inline bool check_lba_range(SCSIDiskState *s, - uint64_t sector_num, uint32_t nb_sectors) -{ - /* - * The first line tests that no overflow happens when computing the last - * sector. The second line tests that the last accessed sector is in - * range. - * - * Careful, the computations should not underflow for nb_sectors == 0, - * and a 0-block read to the first LBA beyond the end of device is - * valid. - */ - return (sector_num <= sector_num + nb_sectors && - sector_num + nb_sectors <= s->qdev.max_lba + 1); -} - -typedef struct UnmapCBData { - SCSIDiskReq *r; - uint8_t *inbuf; - int count; -} UnmapCBData; - -static void scsi_unmap_complete(void *opaque, int ret); - -static void scsi_unmap_complete_noio(UnmapCBData *data, int ret) -{ - SCSIDiskReq *r = data->r; - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - uint64_t sector_num; - uint32_t nb_sectors; - - 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, false)) { - goto done; - } - } - - if (data->count > 0) { - sector_num = ldq_be_p(&data->inbuf[0]); - nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL; - if (!check_lba_range(s, sector_num, nb_sectors)) { - scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); - goto done; - } - - r->req.aiocb = blk_aio_discard(s->qdev.conf.blk, - sector_num * (s->qdev.blocksize / 512), - nb_sectors * (s->qdev.blocksize / 512), - scsi_unmap_complete, data); - data->count--; - data->inbuf += 16; - return; - } - - scsi_req_complete(&r->req, GOOD); - -done: - scsi_req_unref(&r->req); - 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); - uint8_t *p = inbuf; - int len = r->req.cmd.xfer; - UnmapCBData *data; - - /* Reject ANCHOR=1. */ - if (r->req.cmd.buf[1] & 0x1) { - goto invalid_field; - } - - if (len < 8) { - goto invalid_param_len; - } - if (len < lduw_be_p(&p[0]) + 2) { - goto invalid_param_len; - } - if (len < lduw_be_p(&p[2]) + 8) { - goto invalid_param_len; - } - if (lduw_be_p(&p[2]) & 15) { - goto invalid_param_len; - } - - if (blk_is_read_only(s->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); - return; - } - - data = g_new0(UnmapCBData, 1); - data->r = r; - data->inbuf = &p[8]; - data->count = lduw_be_p(&p[2]) >> 4; - - /* The matching unref is in scsi_unmap_complete, before data is freed. */ - scsi_req_ref(&r->req); - scsi_unmap_complete_noio(data, 0); - return; - -invalid_param_len: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN)); - return; - -invalid_field: - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); -} - -typedef struct WriteSameCBData { - SCSIDiskReq *r; - int64_t sector; - int nb_sectors; - QEMUIOVector qiov; - struct iovec iov; -} WriteSameCBData; - -static void scsi_write_same_complete(void *opaque, int ret) -{ - WriteSameCBData *data = opaque; - SCSIDiskReq *r = data->r; - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - - assert(r->req.aiocb != NULL); - 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, 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); - return; - } - - scsi_req_complete(&r->req, GOOD); - -done: - scsi_req_unref(&r->req); - qemu_vfree(data->iov.iov_base); - g_free(data); -} - -static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf) -{ - SCSIRequest *req = &r->req; - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); - uint32_t nb_sectors = scsi_data_cdb_xfer(r->req.cmd.buf); - WriteSameCBData *data; - uint8_t *buf; - int i; - - /* Fail if PBDATA=1 or LBDATA=1 or ANCHOR=1. */ - if (nb_sectors == 0 || (req->cmd.buf[1] & 0x16)) { - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - return; - } - - if (blk_is_read_only(s->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); - return; - } - if (!check_lba_range(s, r->req.cmd.lba, nb_sectors)) { - scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); - return; - } - - if (buffer_is_zero(inbuf, s->qdev.blocksize)) { - int flags = (req->cmd.buf[1] & 0x8) ? BDRV_REQ_MAY_UNMAP : 0; - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, - nb_sectors * s->qdev.blocksize, - BLOCK_ACCT_WRITE); - r->req.aiocb = blk_aio_write_zeroes(s->qdev.conf.blk, - r->req.cmd.lba * (s->qdev.blocksize / 512), - nb_sectors * (s->qdev.blocksize / 512), - flags, scsi_aio_complete, r); - return; - } - - data = g_new0(WriteSameCBData, 1); - data->r = r; - data->sector = r->req.cmd.lba * (s->qdev.blocksize / 512); - data->nb_sectors = nb_sectors * (s->qdev.blocksize / 512); - data->iov.iov_len = MIN(data->nb_sectors * 512, SCSI_WRITE_SAME_MAX); - data->iov.iov_base = buf = blk_blockalign(s->qdev.conf.blk, - data->iov.iov_len); - qemu_iovec_init_external(&data->qiov, &data->iov, 1); - - for (i = 0; i < data->iov.iov_len; i += s->qdev.blocksize) { - memcpy(&buf[i], inbuf, s->qdev.blocksize); - } - - scsi_req_ref(&r->req); - block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, - data->iov.iov_len, BLOCK_ACCT_WRITE); - r->req.aiocb = blk_aio_writev(s->qdev.conf.blk, data->sector, - &data->qiov, data->iov.iov_len / 512, - scsi_write_same_complete, data); -} - -static void scsi_disk_emulate_write_data(SCSIRequest *req) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - - if (r->iov.iov_len) { - int buflen = r->iov.iov_len; - DPRINTF("Write buf_len=%d\n", buflen); - r->iov.iov_len = 0; - scsi_req_data(&r->req, buflen); - return; - } - - switch (req->cmd.buf[0]) { - case MODE_SELECT: - case MODE_SELECT_10: - /* This also clears the sense buffer for REQUEST SENSE. */ - scsi_disk_emulate_mode_select(r, r->iov.iov_base); - break; - - case UNMAP: - scsi_disk_emulate_unmap(r, r->iov.iov_base); - break; - - case VERIFY_10: - case VERIFY_12: - case VERIFY_16: - if (r->req.status == -1) { - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - } - break; - - case WRITE_SAME_10: - case WRITE_SAME_16: - scsi_disk_emulate_write_same(r, r->iov.iov_base); - break; - - default: - abort(); - } -} - -static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); - uint64_t nb_sectors; - uint8_t *outbuf; - int buflen; - - switch (req->cmd.buf[0]) { - case INQUIRY: - case MODE_SENSE: - case MODE_SENSE_10: - case RESERVE: - case RESERVE_10: - case RELEASE: - case RELEASE_10: - case START_STOP: - case ALLOW_MEDIUM_REMOVAL: - case GET_CONFIGURATION: - case GET_EVENT_STATUS_NOTIFICATION: - case MECHANISM_STATUS: - case REQUEST_SENSE: - break; - - default: - if (s->tray_open || !blk_is_inserted(s->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); - return 0; - } - break; - } - - /* - * FIXME: we shouldn't return anything bigger than 4k, but the code - * requires the buffer to be as big as req->cmd.xfer in several - * places. So, do not allow CDBs with a very large ALLOCATION - * LENGTH. The real fix would be to modify scsi_read_data and - * dma_buf_read, so that they return data beyond the buflen - * as all zeros. - */ - if (req->cmd.xfer > 65536) { - goto illegal_request; - } - r->buflen = MAX(4096, req->cmd.xfer); - - if (!r->iov.iov_base) { - r->iov.iov_base = blk_blockalign(s->qdev.conf.blk, r->buflen); - } - - buflen = req->cmd.xfer; - outbuf = r->iov.iov_base; - memset(outbuf, 0, r->buflen); - switch (req->cmd.buf[0]) { - case TEST_UNIT_READY: - assert(!s->tray_open && blk_is_inserted(s->qdev.conf.blk)); - break; - case INQUIRY: - buflen = scsi_disk_emulate_inquiry(req, outbuf); - if (buflen < 0) { - goto illegal_request; - } - break; - case MODE_SENSE: - case MODE_SENSE_10: - buflen = scsi_disk_emulate_mode_sense(r, outbuf); - if (buflen < 0) { - goto illegal_request; - } - break; - case READ_TOC: - buflen = scsi_disk_emulate_read_toc(req, outbuf); - if (buflen < 0) { - goto illegal_request; - } - break; - case RESERVE: - if (req->cmd.buf[1] & 1) { - goto illegal_request; - } - break; - case RESERVE_10: - if (req->cmd.buf[1] & 3) { - goto illegal_request; - } - break; - case RELEASE: - if (req->cmd.buf[1] & 1) { - goto illegal_request; - } - break; - case RELEASE_10: - if (req->cmd.buf[1] & 3) { - goto illegal_request; - } - break; - case START_STOP: - if (scsi_disk_emulate_start_stop(r) < 0) { - return 0; - } - break; - case ALLOW_MEDIUM_REMOVAL: - s->tray_locked = req->cmd.buf[4] & 1; - blk_lock_medium(s->qdev.conf.blk, req->cmd.buf[4] & 1); - break; - case READ_CAPACITY_10: - /* The normal LEN field for this command is zero. */ - memset(outbuf, 0, 8); - blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - if (!nb_sectors) { - scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY)); - return 0; - } - if ((req->cmd.buf[8] & 1) == 0 && req->cmd.lba) { - goto illegal_request; - } - nb_sectors /= s->qdev.blocksize / 512; - /* Returned value is the address of the last sector. */ - nb_sectors--; - /* Remember the new size for read/write sanity checking. */ - s->qdev.max_lba = nb_sectors; - /* Clip to 2TB, instead of returning capacity modulo 2TB. */ - if (nb_sectors > UINT32_MAX) { - nb_sectors = UINT32_MAX; - } - outbuf[0] = (nb_sectors >> 24) & 0xff; - outbuf[1] = (nb_sectors >> 16) & 0xff; - outbuf[2] = (nb_sectors >> 8) & 0xff; - outbuf[3] = nb_sectors & 0xff; - outbuf[4] = 0; - outbuf[5] = 0; - outbuf[6] = s->qdev.blocksize >> 8; - outbuf[7] = 0; - break; - case REQUEST_SENSE: - /* Just return "NO SENSE". */ - buflen = scsi_build_sense(NULL, 0, outbuf, r->buflen, - (req->cmd.buf[1] & 1) == 0); - if (buflen < 0) { - goto illegal_request; - } - break; - case MECHANISM_STATUS: - buflen = scsi_emulate_mechanism_status(s, outbuf); - if (buflen < 0) { - goto illegal_request; - } - break; - case GET_CONFIGURATION: - buflen = scsi_get_configuration(s, outbuf); - if (buflen < 0) { - goto illegal_request; - } - break; - case GET_EVENT_STATUS_NOTIFICATION: - buflen = scsi_get_event_status_notification(s, r, outbuf); - if (buflen < 0) { - goto illegal_request; - } - break; - case READ_DISC_INFORMATION: - buflen = scsi_read_disc_information(s, r, outbuf); - if (buflen < 0) { - goto illegal_request; - } - break; - case READ_DVD_STRUCTURE: - buflen = scsi_read_dvd_structure(s, r, outbuf); - if (buflen < 0) { - goto illegal_request; - } - break; - case SERVICE_ACTION_IN_16: - /* Service Action In subcommands. */ - if ((req->cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) { - DPRINTF("SAI READ CAPACITY(16)\n"); - memset(outbuf, 0, req->cmd.xfer); - blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - if (!nb_sectors) { - scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY)); - return 0; - } - if ((req->cmd.buf[14] & 1) == 0 && req->cmd.lba) { - goto illegal_request; - } - nb_sectors /= s->qdev.blocksize / 512; - /* Returned value is the address of the last sector. */ - nb_sectors--; - /* Remember the new size for read/write sanity checking. */ - s->qdev.max_lba = nb_sectors; - outbuf[0] = (nb_sectors >> 56) & 0xff; - outbuf[1] = (nb_sectors >> 48) & 0xff; - outbuf[2] = (nb_sectors >> 40) & 0xff; - outbuf[3] = (nb_sectors >> 32) & 0xff; - outbuf[4] = (nb_sectors >> 24) & 0xff; - outbuf[5] = (nb_sectors >> 16) & 0xff; - outbuf[6] = (nb_sectors >> 8) & 0xff; - outbuf[7] = nb_sectors & 0xff; - outbuf[8] = 0; - outbuf[9] = 0; - outbuf[10] = s->qdev.blocksize >> 8; - outbuf[11] = 0; - outbuf[12] = 0; - outbuf[13] = get_physical_block_exp(&s->qdev.conf); - - /* set TPE bit if the format supports discard */ - if (s->qdev.conf.discard_granularity) { - outbuf[14] = 0x80; - } - - /* Protection, exponent and lowest lba field left blank. */ - break; - } - DPRINTF("Unsupported Service Action In\n"); - goto illegal_request; - case SYNCHRONIZE_CACHE: - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_aio_complete, r); - return 0; - case SEEK_10: - DPRINTF("Seek(10) (sector %" PRId64 ")\n", r->req.cmd.lba); - if (r->req.cmd.lba > s->qdev.max_lba) { - goto illegal_lba; - } - break; - case MODE_SELECT: - DPRINTF("Mode Select(6) (len %lu)\n", (long)r->req.cmd.xfer); - break; - case MODE_SELECT_10: - DPRINTF("Mode Select(10) (len %lu)\n", (long)r->req.cmd.xfer); - break; - case UNMAP: - DPRINTF("Unmap (len %lu)\n", (long)r->req.cmd.xfer); - break; - case VERIFY_10: - case VERIFY_12: - case VERIFY_16: - DPRINTF("Verify (bytchk %d)\n", (req->cmd.buf[1] >> 1) & 3); - if (req->cmd.buf[1] & 6) { - goto illegal_request; - } - break; - case WRITE_SAME_10: - case WRITE_SAME_16: - DPRINTF("WRITE SAME %d (len %lu)\n", - req->cmd.buf[0] == WRITE_SAME_10 ? 10 : 16, - (long)r->req.cmd.xfer); - break; - default: - DPRINTF("Unknown SCSI command (%2.2x=%s)\n", buf[0], - scsi_command_name(buf[0])); - scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); - return 0; - } - assert(!r->req.aiocb); - r->iov.iov_len = MIN(r->buflen, req->cmd.xfer); - if (r->iov.iov_len == 0) { - scsi_req_complete(&r->req, GOOD); - } - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - assert(r->iov.iov_len == req->cmd.xfer); - return -r->iov.iov_len; - } else { - return r->iov.iov_len; - } - -illegal_request: - if (r->req.status == -1) { - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - } - return 0; - -illegal_lba: - scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); - return 0; -} - -/* Execute a scsi command. Returns the length of the data expected by the - command. This will be Positive for data transfers from the device - (eg. disk reads), negative for transfers to the device (eg. disk writes), - and zero if the command does not transfer any data. */ - -static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf) -{ - SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); - uint32_t len; - uint8_t command; - - command = buf[0]; - - if (s->tray_open || !blk_is_inserted(s->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); - return 0; - } - - len = scsi_data_cdb_xfer(r->req.cmd.buf); - switch (command) { - case READ_6: - case READ_10: - case READ_12: - case READ_16: - DPRINTF("Read (sector %" PRId64 ", count %u)\n", r->req.cmd.lba, len); - if (r->req.cmd.buf[1] & 0xe0) { - goto illegal_request; - } - if (!check_lba_range(s, r->req.cmd.lba, len)) { - goto illegal_lba; - } - r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512); - r->sector_count = len * (s->qdev.blocksize / 512); - break; - case WRITE_6: - case WRITE_10: - case WRITE_12: - case WRITE_16: - case WRITE_VERIFY_10: - case WRITE_VERIFY_12: - case WRITE_VERIFY_16: - if (blk_is_read_only(s->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); - return 0; - } - DPRINTF("Write %s(sector %" PRId64 ", count %u)\n", - (command & 0xe) == 0xe ? "And Verify " : "", - r->req.cmd.lba, len); - if (r->req.cmd.buf[1] & 0xe0) { - goto illegal_request; - } - if (!check_lba_range(s, r->req.cmd.lba, len)) { - goto illegal_lba; - } - r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512); - r->sector_count = len * (s->qdev.blocksize / 512); - break; - default: - abort(); - illegal_request: - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - return 0; - illegal_lba: - scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); - return 0; - } - if (r->sector_count == 0) { - scsi_req_complete(&r->req, GOOD); - } - assert(r->iov.iov_len == 0); - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - return -r->sector_count * 512; - } else { - return r->sector_count * 512; - } -} - -static void scsi_disk_reset(DeviceState *dev) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev); - uint64_t nb_sectors; - - scsi_device_purge_requests(&s->qdev, SENSE_CODE(RESET)); - - blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - nb_sectors /= s->qdev.blocksize / 512; - if (nb_sectors) { - nb_sectors--; - } - s->qdev.max_lba = nb_sectors; - /* reset tray statuses */ - s->tray_locked = 0; - s->tray_open = 0; -} - -static void scsi_disk_resize_cb(void *opaque) -{ - SCSIDiskState *s = opaque; - - /* SPC lists this sense code as available only for - * direct-access devices. - */ - if (s->qdev.type == TYPE_DISK) { - scsi_device_report_change(&s->qdev, SENSE_CODE(CAPACITY_CHANGED)); - } -} - -static void scsi_cd_change_media_cb(void *opaque, bool load) -{ - SCSIDiskState *s = opaque; - - /* - * When a CD gets changed, we have to report an ejected state and - * then a loaded state to guests so that they detect tray - * open/close and media change events. Guests that do not use - * GET_EVENT_STATUS_NOTIFICATION to detect such tray open/close - * states rely on this behavior. - * - * media_changed governs the state machine used for unit attention - * report. media_event is used by GET EVENT STATUS NOTIFICATION. - */ - s->media_changed = load; - s->tray_open = !load; - scsi_device_set_ua(&s->qdev, SENSE_CODE(UNIT_ATTENTION_NO_MEDIUM)); - s->media_event = true; - s->eject_request = false; -} - -static void scsi_cd_eject_request_cb(void *opaque, bool force) -{ - SCSIDiskState *s = opaque; - - s->eject_request = true; - if (force) { - s->tray_locked = false; - } -} - -static bool scsi_cd_is_tray_open(void *opaque) -{ - return ((SCSIDiskState *)opaque)->tray_open; -} - -static bool scsi_cd_is_medium_locked(void *opaque) -{ - return ((SCSIDiskState *)opaque)->tray_locked; -} - -static const BlockDevOps scsi_disk_removable_block_ops = { - .change_media_cb = scsi_cd_change_media_cb, - .eject_request_cb = scsi_cd_eject_request_cb, - .is_tray_open = scsi_cd_is_tray_open, - .is_medium_locked = scsi_cd_is_medium_locked, - - .resize_cb = scsi_disk_resize_cb, -}; - -static const BlockDevOps scsi_disk_block_ops = { - .resize_cb = scsi_disk_resize_cb, -}; - -static void scsi_disk_unit_attention_reported(SCSIDevice *dev) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - if (s->media_changed) { - s->media_changed = false; - scsi_device_set_ua(&s->qdev, SENSE_CODE(MEDIUM_CHANGED)); - } -} - -static void scsi_realize(SCSIDevice *dev, Error **errp) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - Error *err = NULL; - - if (!s->qdev.conf.blk) { - error_setg(errp, "drive property not set"); - return; - } - - if (!(s->features & (1 << SCSI_DISK_F_REMOVABLE)) && - !blk_is_inserted(s->qdev.conf.blk)) { - error_setg(errp, "Device needs media, but drive is empty"); - return; - } - - blkconf_serial(&s->qdev.conf, &s->serial); - blkconf_blocksizes(&s->qdev.conf); - if (dev->type == TYPE_DISK) { - blkconf_geometry(&dev->conf, NULL, 65535, 255, 255, &err); - if (err) { - error_propagate(errp, err); - return; - } - } - - if (s->qdev.conf.discard_granularity == -1) { - s->qdev.conf.discard_granularity = - MAX(s->qdev.conf.logical_block_size, DEFAULT_DISCARD_GRANULARITY); - } - - if (!s->version) { - s->version = g_strdup(qemu_hw_version()); - } - if (!s->vendor) { - s->vendor = g_strdup("QEMU"); - } - - if (blk_is_sg(s->qdev.conf.blk)) { - error_setg(errp, "unwanted /dev/sg*"); - return; - } - - if ((s->features & (1 << SCSI_DISK_F_REMOVABLE)) && - !(s->features & (1 << SCSI_DISK_F_NO_REMOVABLE_DEVOPS))) { - blk_set_dev_ops(s->qdev.conf.blk, &scsi_disk_removable_block_ops, s); - } else { - blk_set_dev_ops(s->qdev.conf.blk, &scsi_disk_block_ops, s); - } - blk_set_guest_block_size(s->qdev.conf.blk, s->qdev.blocksize); - - blk_iostatus_enable(s->qdev.conf.blk); -} - -static void scsi_hd_realize(SCSIDevice *dev, Error **errp) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - /* can happen for devices without drive. The error message for missing - * backend will be issued in scsi_realize - */ - if (s->qdev.conf.blk) { - blkconf_blocksizes(&s->qdev.conf); - } - s->qdev.blocksize = s->qdev.conf.logical_block_size; - s->qdev.type = TYPE_DISK; - if (!s->product) { - s->product = g_strdup("QEMU HARDDISK"); - } - scsi_realize(&s->qdev, errp); -} - -static void scsi_cd_realize(SCSIDevice *dev, Error **errp) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - s->qdev.blocksize = 2048; - s->qdev.type = TYPE_ROM; - s->features |= 1 << SCSI_DISK_F_REMOVABLE; - if (!s->product) { - s->product = g_strdup("QEMU CD-ROM"); - } - scsi_realize(&s->qdev, errp); -} - -static void scsi_disk_realize(SCSIDevice *dev, Error **errp) -{ - DriveInfo *dinfo; - Error *local_err = NULL; - - if (!dev->conf.blk) { - scsi_realize(dev, &local_err); - assert(local_err); - error_propagate(errp, local_err); - return; - } - - dinfo = blk_legacy_dinfo(dev->conf.blk); - if (dinfo && dinfo->media_cd) { - scsi_cd_realize(dev, errp); - } else { - scsi_hd_realize(dev, errp); - } -} - -static const SCSIReqOps scsi_disk_emulate_reqops = { - .size = sizeof(SCSIDiskReq), - .free_req = scsi_free_request, - .send_command = scsi_disk_emulate_command, - .read_data = scsi_disk_emulate_read_data, - .write_data = scsi_disk_emulate_write_data, - .get_buf = scsi_get_buf, -}; - -static const SCSIReqOps scsi_disk_dma_reqops = { - .size = sizeof(SCSIDiskReq), - .free_req = scsi_free_request, - .send_command = scsi_disk_dma_command, - .read_data = scsi_read_data, - .write_data = scsi_write_data, - .get_buf = scsi_get_buf, - .load_request = scsi_disk_load_request, - .save_request = scsi_disk_save_request, -}; - -static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = { - [TEST_UNIT_READY] = &scsi_disk_emulate_reqops, - [INQUIRY] = &scsi_disk_emulate_reqops, - [MODE_SENSE] = &scsi_disk_emulate_reqops, - [MODE_SENSE_10] = &scsi_disk_emulate_reqops, - [START_STOP] = &scsi_disk_emulate_reqops, - [ALLOW_MEDIUM_REMOVAL] = &scsi_disk_emulate_reqops, - [READ_CAPACITY_10] = &scsi_disk_emulate_reqops, - [READ_TOC] = &scsi_disk_emulate_reqops, - [READ_DVD_STRUCTURE] = &scsi_disk_emulate_reqops, - [READ_DISC_INFORMATION] = &scsi_disk_emulate_reqops, - [GET_CONFIGURATION] = &scsi_disk_emulate_reqops, - [GET_EVENT_STATUS_NOTIFICATION] = &scsi_disk_emulate_reqops, - [MECHANISM_STATUS] = &scsi_disk_emulate_reqops, - [SERVICE_ACTION_IN_16] = &scsi_disk_emulate_reqops, - [REQUEST_SENSE] = &scsi_disk_emulate_reqops, - [SYNCHRONIZE_CACHE] = &scsi_disk_emulate_reqops, - [SEEK_10] = &scsi_disk_emulate_reqops, - [MODE_SELECT] = &scsi_disk_emulate_reqops, - [MODE_SELECT_10] = &scsi_disk_emulate_reqops, - [UNMAP] = &scsi_disk_emulate_reqops, - [WRITE_SAME_10] = &scsi_disk_emulate_reqops, - [WRITE_SAME_16] = &scsi_disk_emulate_reqops, - [VERIFY_10] = &scsi_disk_emulate_reqops, - [VERIFY_12] = &scsi_disk_emulate_reqops, - [VERIFY_16] = &scsi_disk_emulate_reqops, - - [READ_6] = &scsi_disk_dma_reqops, - [READ_10] = &scsi_disk_dma_reqops, - [READ_12] = &scsi_disk_dma_reqops, - [READ_16] = &scsi_disk_dma_reqops, - [WRITE_6] = &scsi_disk_dma_reqops, - [WRITE_10] = &scsi_disk_dma_reqops, - [WRITE_12] = &scsi_disk_dma_reqops, - [WRITE_16] = &scsi_disk_dma_reqops, - [WRITE_VERIFY_10] = &scsi_disk_dma_reqops, - [WRITE_VERIFY_12] = &scsi_disk_dma_reqops, - [WRITE_VERIFY_16] = &scsi_disk_dma_reqops, -}; - -static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, - uint8_t *buf, void *hba_private) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIRequest *req; - const SCSIReqOps *ops; - uint8_t command; - - command = buf[0]; - ops = scsi_disk_reqops_dispatch[command]; - if (!ops) { - ops = &scsi_disk_emulate_reqops; - } - req = scsi_req_alloc(ops, &s->qdev, tag, lun, hba_private); - -#ifdef DEBUG_SCSI - DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]); - { - int i; - for (i = 1; i < scsi_cdb_length(buf); i++) { - printf(" 0x%02x", buf[i]); - } - printf("\n"); - } -#endif - - return req; -} - -#ifdef __linux__ -static int get_device_type(SCSIDiskState *s) -{ - uint8_t cmd[16]; - uint8_t buf[36]; - uint8_t sensebuf[8]; - sg_io_hdr_t io_header; - int ret; - - memset(cmd, 0, sizeof(cmd)); - memset(buf, 0, sizeof(buf)); - cmd[0] = INQUIRY; - 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->qdev.conf.blk, SG_IO, &io_header); - if (ret < 0 || io_header.driver_status || io_header.host_status) { - return -1; - } - s->qdev.type = buf[0]; - if (buf[1] & 0x80) { - s->features |= 1 << SCSI_DISK_F_REMOVABLE; - } - return 0; -} - -static void scsi_block_realize(SCSIDevice *dev, Error **errp) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - int sg_version; - int rc; - - if (!s->qdev.conf.blk) { - error_setg(errp, "drive property not set"); - return; - } - - /* check we are using a driver managing SG_IO (version 3 and after) */ - rc = blk_ioctl(s->qdev.conf.blk, SG_GET_VERSION_NUM, &sg_version); - if (rc < 0) { - error_setg(errp, "cannot get SG_IO version number: %s. " - "Is this a SCSI device?", - strerror(-rc)); - return; - } - if (sg_version < 30000) { - error_setg(errp, "scsi generic interface too old"); - return; - } - - /* get device type from INQUIRY data */ - rc = get_device_type(s); - if (rc < 0) { - error_setg(errp, "INQUIRY failed"); - return; - } - - /* Make a guess for the block size, we'll fix it when the guest sends. - * READ CAPACITY. If they don't, they likely would assume these sizes - * anyway. (TODO: check in /sys). - */ - if (s->qdev.type == TYPE_ROM || s->qdev.type == TYPE_WORM) { - s->qdev.blocksize = 2048; - } else { - s->qdev.blocksize = 512; - } - - /* Makes the scsi-block device not removable by using HMP and QMP eject - * command. - */ - 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) -{ - switch (buf[0]) { - case READ_6: - case READ_10: - case READ_12: - case READ_16: - case VERIFY_10: - case VERIFY_12: - case VERIFY_16: - case WRITE_6: - case WRITE_10: - case WRITE_12: - case WRITE_16: - case WRITE_VERIFY_10: - case WRITE_VERIFY_12: - case WRITE_VERIFY_16: - /* If we are not using O_DIRECT, we might read stale data from the - * host cache if writes were made using other commands than these - * ones (such as WRITE SAME or EXTENDED COPY, etc.). So, without - * O_DIRECT everything must go through SG_IO. - */ - if (!(blk_get_flags(s->qdev.conf.blk) & BDRV_O_NOCACHE)) { - break; - } - - /* MMC writing cannot be done via pread/pwrite, because it sometimes - * involves writing beyond the maximum LBA or to negative LBA (lead-in). - * And once you do these writes, reading from the block device is - * unreliable, too. It is even possible that reads deliver random data - * from the host page cache (this is probably a Linux bug). - * - * We might use scsi_disk_dma_reqops as long as no writing commands are - * seen, but performance usually isn't paramount on optical media. So, - * just make scsi-block operate the same as scsi-generic for them. - */ - if (s->qdev.type != TYPE_ROM) { - return false; - } - break; - - default: - break; - } - - return true; -} - - -static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag, - uint32_t lun, uint8_t *buf, - void *hba_private) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - - if (scsi_block_is_passthrough(s, buf)) { - return scsi_req_alloc(&scsi_generic_req_ops, &s->qdev, tag, lun, - hba_private); - } else { - return scsi_req_alloc(&scsi_disk_dma_reqops, &s->qdev, tag, lun, - hba_private); - } -} - -static int scsi_block_parse_cdb(SCSIDevice *d, SCSICommand *cmd, - uint8_t *buf, void *hba_private) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - - if (scsi_block_is_passthrough(s, buf)) { - return scsi_bus_parse_cdb(&s->qdev, cmd, buf, hba_private); - } else { - return scsi_req_parse_cdb(&s->qdev, cmd, buf); - } -} - -#endif - -#define DEFINE_SCSI_DISK_PROPERTIES() \ - DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), \ - DEFINE_PROP_STRING("ver", SCSIDiskState, version), \ - DEFINE_PROP_STRING("serial", SCSIDiskState, serial), \ - DEFINE_PROP_STRING("vendor", SCSIDiskState, vendor), \ - DEFINE_PROP_STRING("product", SCSIDiskState, product) - -static Property scsi_hd_properties[] = { - DEFINE_SCSI_DISK_PROPERTIES(), - DEFINE_PROP_BIT("removable", SCSIDiskState, features, - SCSI_DISK_F_REMOVABLE, false), - DEFINE_PROP_BIT("dpofua", SCSIDiskState, features, - SCSI_DISK_F_DPOFUA, false), - 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), - DEFINE_PROP_UINT64("max_io_size", SCSIDiskState, max_io_size, - DEFAULT_MAX_IO_SIZE), - DEFINE_BLOCK_CHS_PROPERTIES(SCSIDiskState, qdev.conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription vmstate_scsi_disk_state = { - .name = "scsi-disk", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_SCSI_DEVICE(qdev, SCSIDiskState), - VMSTATE_BOOL(media_changed, SCSIDiskState), - VMSTATE_BOOL(media_event, SCSIDiskState), - VMSTATE_BOOL(eject_request, SCSIDiskState), - VMSTATE_BOOL(tray_open, SCSIDiskState), - VMSTATE_BOOL(tray_locked, SCSIDiskState), - VMSTATE_END_OF_LIST() - } -}; - -static void scsi_hd_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); - - sc->realize = scsi_hd_realize; - sc->alloc_req = scsi_new_request; - sc->unit_attention_reported = scsi_disk_unit_attention_reported; - dc->fw_name = "disk"; - dc->desc = "virtual SCSI disk"; - dc->reset = scsi_disk_reset; - dc->props = scsi_hd_properties; - dc->vmsd = &vmstate_scsi_disk_state; -} - -static const TypeInfo scsi_hd_info = { - .name = "scsi-hd", - .parent = TYPE_SCSI_DEVICE, - .instance_size = sizeof(SCSIDiskState), - .class_init = scsi_hd_class_initfn, -}; - -static Property scsi_cd_properties[] = { - DEFINE_SCSI_DISK_PROPERTIES(), - 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), - DEFINE_PROP_END_OF_LIST(), -}; - -static void scsi_cd_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); - - sc->realize = scsi_cd_realize; - sc->alloc_req = scsi_new_request; - sc->unit_attention_reported = scsi_disk_unit_attention_reported; - dc->fw_name = "disk"; - dc->desc = "virtual SCSI CD-ROM"; - dc->reset = scsi_disk_reset; - dc->props = scsi_cd_properties; - dc->vmsd = &vmstate_scsi_disk_state; -} - -static const TypeInfo scsi_cd_info = { - .name = "scsi-cd", - .parent = TYPE_SCSI_DEVICE, - .instance_size = sizeof(SCSIDiskState), - .class_init = scsi_cd_class_initfn, -}; - -#ifdef __linux__ -static Property scsi_block_properties[] = { - DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.blk), - DEFINE_PROP_END_OF_LIST(), -}; - -static void scsi_block_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); - - sc->realize = scsi_block_realize; - sc->alloc_req = scsi_block_new_request; - sc->parse_cdb = scsi_block_parse_cdb; - dc->fw_name = "disk"; - dc->desc = "SCSI block device passthrough"; - dc->reset = scsi_disk_reset; - dc->props = scsi_block_properties; - dc->vmsd = &vmstate_scsi_disk_state; -} - -static const TypeInfo scsi_block_info = { - .name = "scsi-block", - .parent = TYPE_SCSI_DEVICE, - .instance_size = sizeof(SCSIDiskState), - .class_init = scsi_block_class_initfn, -}; -#endif - -static Property scsi_disk_properties[] = { - DEFINE_SCSI_DISK_PROPERTIES(), - DEFINE_PROP_BIT("removable", SCSIDiskState, features, - SCSI_DISK_F_REMOVABLE, false), - DEFINE_PROP_BIT("dpofua", SCSIDiskState, features, - SCSI_DISK_F_DPOFUA, false), - 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), - DEFINE_PROP_UINT64("max_io_size", SCSIDiskState, max_io_size, - DEFAULT_MAX_IO_SIZE), - DEFINE_PROP_END_OF_LIST(), -}; - -static void scsi_disk_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); - - sc->realize = scsi_disk_realize; - sc->alloc_req = scsi_new_request; - sc->unit_attention_reported = scsi_disk_unit_attention_reported; - dc->fw_name = "disk"; - dc->desc = "virtual SCSI disk or CD-ROM (legacy)"; - dc->reset = scsi_disk_reset; - dc->props = scsi_disk_properties; - dc->vmsd = &vmstate_scsi_disk_state; -} - -static const TypeInfo scsi_disk_info = { - .name = "scsi-disk", - .parent = TYPE_SCSI_DEVICE, - .instance_size = sizeof(SCSIDiskState), - .class_init = scsi_disk_class_initfn, -}; - -static void scsi_disk_register_types(void) -{ - type_register_static(&scsi_hd_info); - type_register_static(&scsi_cd_info); -#ifdef __linux__ - type_register_static(&scsi_block_info); -#endif - type_register_static(&scsi_disk_info); -} - -type_init(scsi_disk_register_types) diff --git a/qemu/hw/scsi/scsi-generic.c b/qemu/hw/scsi/scsi-generic.c deleted file mode 100644 index 7459465f6..000000000 --- a/qemu/hw/scsi/scsi-generic.c +++ /dev/null @@ -1,616 +0,0 @@ -/* - * Generic SCSI Device support - * - * Copyright (c) 2007 Bull S.A.S. - * Based on code by Paul Brook - * Based on code by Fabrice Bellard - * - * Written by Laurent Vivier - * - * 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 "hw/scsi/scsi.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" - -#ifdef __linux__ - -//#define DEBUG_SCSI - -#ifdef DEBUG_SCSI -#define DPRINTF(fmt, ...) \ -do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - -#define BADF(fmt, ...) \ -do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0) - -#include -#include "block/scsi.h" - -#define SG_ERR_DRIVER_TIMEOUT 0x06 -#define SG_ERR_DRIVER_SENSE 0x08 - -#define SG_ERR_DID_OK 0x00 -#define SG_ERR_DID_NO_CONNECT 0x01 -#define SG_ERR_DID_BUS_BUSY 0x02 -#define SG_ERR_DID_TIME_OUT 0x03 - -#ifndef MAX_UINT -#define MAX_UINT ((unsigned int)-1) -#endif - -typedef struct SCSIGenericReq { - SCSIRequest req; - uint8_t *buf; - int buflen; - int len; - sg_io_hdr_t io_header; -} SCSIGenericReq; - -static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req) -{ - SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); - - qemu_put_sbe32s(f, &r->buflen); - if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) { - assert(!r->req.sg); - qemu_put_buffer(f, r->buf, r->req.cmd.xfer); - } -} - -static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req) -{ - SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); - - qemu_get_sbe32s(f, &r->buflen); - if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) { - assert(!r->req.sg); - qemu_get_buffer(f, r->buf, r->req.cmd.xfer); - } -} - -static void scsi_free_request(SCSIRequest *req) -{ - SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); - - g_free(r->buf); -} - -/* Helper function for command completion. */ -static void scsi_command_complete_noio(SCSIGenericReq *r, int ret) -{ - int status; - - assert(r->req.aiocb == NULL); - - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); - goto done; - } - if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { - r->req.sense_len = r->io_header.sb_len_wr; - } - - if (ret != 0) { - switch (ret) { - case -EDOM: - status = TASK_SET_FULL; - break; - case -ENOMEM: - status = CHECK_CONDITION; - scsi_req_build_sense(&r->req, SENSE_CODE(TARGET_FAILURE)); - break; - default: - status = CHECK_CONDITION; - scsi_req_build_sense(&r->req, SENSE_CODE(IO_ERROR)); - break; - } - } else { - if (r->io_header.host_status == SG_ERR_DID_NO_CONNECT || - r->io_header.host_status == SG_ERR_DID_BUS_BUSY || - r->io_header.host_status == SG_ERR_DID_TIME_OUT || - (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) { - status = BUSY; - BADF("Driver Timeout\n"); - } else if (r->io_header.host_status) { - status = CHECK_CONDITION; - scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS)); - } else if (r->io_header.status) { - status = r->io_header.status; - } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { - status = CHECK_CONDITION; - } else { - status = GOOD; - } - } - DPRINTF("Command complete 0x%p tag=0x%x status=%d\n", - r, r->req.tag, status); - - scsi_req_complete(&r->req, status); -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) -{ - r->io_header.interface_id = 'S'; - r->io_header.dxfer_direction = direction; - r->io_header.dxferp = r->buf; - r->io_header.dxfer_len = r->buflen; - r->io_header.cmdp = r->req.cmd.buf; - r->io_header.cmd_len = r->req.cmd.len; - r->io_header.mx_sb_len = sizeof(r->req.sense); - r->io_header.sbp = r->req.sense; - r->io_header.timeout = MAX_UINT; - r->io_header.usr_ptr = r; - r->io_header.flags |= SG_FLAG_DIRECT_IO; - - r->req.aiocb = blk_aio_ioctl(blk, SG_IO, &r->io_header, complete, r); - if (r->req.aiocb == NULL) { - return -EIO; - } - - return 0; -} - -static void scsi_read_complete(void * opaque, int ret) -{ - SCSIGenericReq *r = (SCSIGenericReq *)opaque; - 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_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_noio(r, 0); - return; - } - - /* 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. */ -static void scsi_read_data(SCSIRequest *req) -{ - SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); - SCSIDevice *s = r->req.dev; - int ret; - - DPRINTF("scsi_read_data 0x%x\n", req->tag); - - /* 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_noio(r, 0); - return; - } - - ret = execute_command(s->conf.blk, r, SG_DXFER_FROM_DEV, - scsi_read_complete); - if (ret < 0) { - scsi_command_complete_noio(r, ret); - } -} - -static void scsi_write_complete(void * opaque, int ret) -{ - SCSIGenericReq *r = (SCSIGenericReq *)opaque; - 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_noio(r, ret); - return; - } - - if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 && - s->type == TYPE_TAPE) { - s->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11]; - DPRINTF("block size %d\n", s->blocksize); - } - - scsi_command_complete_noio(r, ret); -} - -/* Write data to a scsi device. Returns nonzero on failure. - The transfer may complete asynchronously. */ -static void scsi_write_data(SCSIRequest *req) -{ - SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); - SCSIDevice *s = r->req.dev; - int ret; - - DPRINTF("scsi_write_data 0x%x\n", req->tag); - if (r->len == 0) { - r->len = r->buflen; - scsi_req_data(&r->req, r->len); - return; - } - - /* The request is used as the AIO opaque value, so add a ref. */ - 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_noio(r, ret); - } -} - -/* Return a pointer to the data buffer. */ -static uint8_t *scsi_get_buf(SCSIRequest *req) -{ - SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); - - return r->buf; -} - -/* Execute a scsi command. Returns the length of the data expected by the - command. This will be Positive for data transfers from the device - (eg. disk reads), negative for transfers to the device (eg. disk writes), - and zero if the command does not transfer any data. */ - -static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) -{ - SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); - SCSIDevice *s = r->req.dev; - int ret; - -#ifdef DEBUG_SCSI - { - int i; - for (i = 1; i < r->req.cmd.len; i++) { - printf(" 0x%02x", cmd[i]); - } - printf("\n"); - } -#endif - - if (r->req.cmd.xfer == 0) { - g_free(r->buf); - r->buflen = 0; - r->buf = NULL; - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - ret = execute_command(s->conf.blk, r, SG_DXFER_NONE, - scsi_command_complete); - if (ret < 0) { - scsi_command_complete_noio(r, ret); - return 0; - } - return 0; - } - - if (r->buflen != r->req.cmd.xfer) { - g_free(r->buf); - r->buf = g_malloc(r->req.cmd.xfer); - r->buflen = r->req.cmd.xfer; - } - - memset(r->buf, 0, r->buflen); - r->len = r->req.cmd.xfer; - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - r->len = 0; - return -r->req.cmd.xfer; - } else { - return r->req.cmd.xfer; - } -} - -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]; - uint8_t buf[12]; - uint8_t sensebuf[8]; - sg_io_hdr_t io_header; - int ret; - - memset(cmd, 0, sizeof(cmd)); - memset(buf, 0, sizeof(buf)); - cmd[0] = MODE_SENSE; - 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(blk, SG_IO, &io_header); - if (ret < 0 || io_header.driver_status || io_header.host_status) { - return -1; - } - return (buf[9] << 16) | (buf[10] << 8) | buf[11]; -} - -static void scsi_generic_reset(DeviceState *dev) -{ - SCSIDevice *s = SCSI_DEVICE(dev); - - scsi_device_purge_requests(s, SENSE_CODE(RESET)); -} - -static void scsi_generic_realize(SCSIDevice *s, Error **errp) -{ - int rc; - int sg_version; - struct sg_scsi_id scsiid; - - if (!s->conf.blk) { - error_setg(errp, "drive property not set"); - return; - } - - if (blk_get_on_error(s->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC) { - error_setg(errp, "Device doesn't support drive option werror"); - return; - } - if (blk_get_on_error(s->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) { - error_setg(errp, "Device doesn't support drive option rerror"); - return; - } - - /* check we are using a driver managing SG_IO (version 3 and after */ - rc = blk_ioctl(s->conf.blk, SG_GET_VERSION_NUM, &sg_version); - if (rc < 0) { - error_setg(errp, "cannot get SG_IO version number: %s. " - "Is this a SCSI device?", - strerror(-rc)); - return; - } - if (sg_version < 30000) { - error_setg(errp, "scsi generic interface too old"); - return; - } - - /* get LUN of the /dev/sg? */ - if (blk_ioctl(s->conf.blk, SG_GET_SCSI_ID, &scsiid)) { - error_setg(errp, "SG_GET_SCSI_ID ioctl failed"); - return; - } - - /* define device state */ - s->type = scsiid.scsi_type; - DPRINTF("device type %d\n", s->type); - - switch (s->type) { - case TYPE_TAPE: - s->blocksize = get_stream_blocksize(s->conf.blk); - if (s->blocksize == -1) { - s->blocksize = 0; - } - break; - - /* Make a guess for block devices, we'll fix it when the guest sends. - * READ CAPACITY. If they don't, they likely would assume these sizes - * anyway. (TODO: they could also send MODE SENSE). - */ - case TYPE_ROM: - case TYPE_WORM: - s->blocksize = 2048; - break; - default: - s->blocksize = 512; - break; - } - - DPRINTF("block size %d\n", s->blocksize); - - scsi_generic_read_device_identification(s); -} - -const SCSIReqOps scsi_generic_req_ops = { - .size = sizeof(SCSIGenericReq), - .free_req = scsi_free_request, - .send_command = scsi_send_command, - .read_data = scsi_read_data, - .write_data = scsi_write_data, - .get_buf = scsi_get_buf, - .load_request = scsi_generic_load_request, - .save_request = scsi_generic_save_request, -}; - -static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, - uint8_t *buf, void *hba_private) -{ - SCSIRequest *req; - - req = scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private); - return req; -} - -static Property scsi_generic_properties[] = { - DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.blk), - DEFINE_PROP_END_OF_LIST(), -}; - -static int scsi_generic_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, - uint8_t *buf, void *hba_private) -{ - return scsi_bus_parse_cdb(dev, cmd, buf, hba_private); -} - -static void scsi_generic_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); - - sc->realize = scsi_generic_realize; - sc->alloc_req = scsi_new_request; - sc->parse_cdb = scsi_generic_parse_cdb; - dc->fw_name = "disk"; - dc->desc = "pass through generic scsi device (/dev/sg*)"; - dc->reset = scsi_generic_reset; - dc->props = scsi_generic_properties; - dc->vmsd = &vmstate_scsi_device; -} - -static const TypeInfo scsi_generic_info = { - .name = "scsi-generic", - .parent = TYPE_SCSI_DEVICE, - .instance_size = sizeof(SCSIDevice), - .class_init = scsi_generic_class_initfn, -}; - -static void scsi_generic_register_types(void) -{ - type_register_static(&scsi_generic_info); -} - -type_init(scsi_generic_register_types) - -#endif /* __linux__ */ diff --git a/qemu/hw/scsi/spapr_vscsi.c b/qemu/hw/scsi/spapr_vscsi.c deleted file mode 100644 index b00edf7fd..000000000 --- a/qemu/hw/scsi/spapr_vscsi.c +++ /dev/null @@ -1,1304 +0,0 @@ -/* - * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator - * - * PAPR Virtual SCSI, aka ibmvscsi - * - * Copyright (c) 2010,2011 Benjamin Herrenschmidt, IBM Corporation. - * - * 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. - * - * TODO: - * - * - Cleanups :-) - * - Sort out better how to assign devices to VSCSI instances - * - Fix residual counts - * - 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" -#include "srp.h" -#include "hw/qdev.h" -#include "hw/ppc/spapr.h" -#include "hw/ppc/spapr_vio.h" -#include "viosrp.h" - -#include - -/*#define DEBUG_VSCSI*/ - -#ifdef DEBUG_VSCSI -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - -/* - * Virtual SCSI device - */ - -/* Random numbers */ -#define VSCSI_MAX_SECTORS 4096 -#define VSCSI_REQ_LIMIT 24 - -#define SRP_RSP_SENSE_DATA_LEN 18 - -#define SRP_REPORT_LUNS_WLUN 0xc10100000000000ULL - -typedef union vscsi_crq { - struct viosrp_crq s; - uint8_t raw[16]; -} vscsi_crq; - -typedef struct vscsi_req { - vscsi_crq crq; - union viosrp_iu iu; - - /* SCSI request tracking */ - SCSIRequest *sreq; - uint32_t qtag; /* qemu tag != srp tag */ - bool active; - bool writing; - bool dma_error; - uint32_t data_len; - uint32_t senselen; - uint8_t sense[SCSI_SENSE_BUF_SIZE]; - - /* RDMA related bits */ - uint8_t dma_fmt; - uint16_t local_desc; - uint16_t total_desc; - uint16_t cdb_offset; - uint16_t cur_desc_num; - uint16_t cur_desc_offset; -} vscsi_req; - -#define TYPE_VIO_SPAPR_VSCSI_DEVICE "spapr-vscsi" -#define VIO_SPAPR_VSCSI_DEVICE(obj) \ - OBJECT_CHECK(VSCSIState, (obj), TYPE_VIO_SPAPR_VSCSI_DEVICE) - -typedef struct { - VIOsPAPRDevice vdev; - SCSIBus bus; - vscsi_req reqs[VSCSI_REQ_LIMIT]; -} VSCSIState; - -static struct vscsi_req *vscsi_get_req(VSCSIState *s) -{ - vscsi_req *req; - int i; - - for (i = 0; i < VSCSI_REQ_LIMIT; i++) { - req = &s->reqs[i]; - if (!req->active) { - memset(req, 0, sizeof(*req)); - req->qtag = i; - req->active = 1; - return req; - } - } - return NULL; -} - -static struct vscsi_req *vscsi_find_req(VSCSIState *s, uint64_t srp_tag) -{ - vscsi_req *req; - int i; - - for (i = 0; i < VSCSI_REQ_LIMIT; i++) { - req = &s->reqs[i]; - if (req->iu.srp.cmd.tag == srp_tag) { - return req; - } - } - return NULL; -} - -static void vscsi_put_req(vscsi_req *req) -{ - if (req->sreq != NULL) { - scsi_req_unref(req->sreq); - } - req->sreq = NULL; - req->active = 0; -} - -static SCSIDevice *vscsi_device_find(SCSIBus *bus, uint64_t srp_lun, int *lun) -{ - int channel = 0, id = 0; - -retry: - switch (srp_lun >> 62) { - case 0: - if ((srp_lun >> 56) != 0) { - channel = (srp_lun >> 56) & 0x3f; - id = (srp_lun >> 48) & 0xff; - srp_lun <<= 16; - goto retry; - } - *lun = (srp_lun >> 48) & 0xff; - break; - - case 1: - *lun = (srp_lun >> 48) & 0x3fff; - break; - case 2: - channel = (srp_lun >> 53) & 0x7; - id = (srp_lun >> 56) & 0x3f; - *lun = (srp_lun >> 48) & 0x1f; - break; - case 3: - *lun = -1; - return NULL; - default: - abort(); - } - - return scsi_device_find(bus, channel, id, *lun); -} - -static int vscsi_send_iu(VSCSIState *s, vscsi_req *req, - uint64_t length, uint8_t format) -{ - long rc, rc1; - - /* First copy the SRP */ - rc = spapr_vio_dma_write(&s->vdev, req->crq.s.IU_data_ptr, - &req->iu, length); - if (rc) { - fprintf(stderr, "vscsi_send_iu: DMA write failure !\n"); - } - - req->crq.s.valid = 0x80; - req->crq.s.format = format; - req->crq.s.reserved = 0x00; - req->crq.s.timeout = cpu_to_be16(0x0000); - req->crq.s.IU_length = cpu_to_be16(length); - req->crq.s.IU_data_ptr = req->iu.srp.rsp.tag; /* right byte order */ - - if (rc == 0) { - req->crq.s.status = VIOSRP_OK; - } else { - req->crq.s.status = VIOSRP_ADAPTER_FAIL; - } - - rc1 = spapr_vio_send_crq(&s->vdev, req->crq.raw); - if (rc1) { - fprintf(stderr, "vscsi_send_iu: Error sending response\n"); - return rc1; - } - - return rc; -} - -static void vscsi_makeup_sense(VSCSIState *s, vscsi_req *req, - uint8_t key, uint8_t asc, uint8_t ascq) -{ - req->senselen = SRP_RSP_SENSE_DATA_LEN; - - /* Valid bit and 'current errors' */ - req->sense[0] = (0x1 << 7 | 0x70); - /* Sense key */ - req->sense[2] = key; - /* Additional sense length */ - req->sense[7] = 0xa; /* 10 bytes */ - /* Additional sense code */ - req->sense[12] = asc; - req->sense[13] = ascq; -} - -static int vscsi_send_rsp(VSCSIState *s, vscsi_req *req, - uint8_t status, int32_t res_in, int32_t res_out) -{ - union viosrp_iu *iu = &req->iu; - uint64_t tag = iu->srp.rsp.tag; - int total_len = sizeof(iu->srp.rsp); - uint8_t sol_not = iu->srp.cmd.sol_not; - - DPRINTF("VSCSI: Sending resp status: 0x%x, " - "res_in: %d, res_out: %d\n", status, res_in, res_out); - - memset(iu, 0, sizeof(struct srp_rsp)); - iu->srp.rsp.opcode = SRP_RSP; - iu->srp.rsp.req_lim_delta = cpu_to_be32(1); - iu->srp.rsp.tag = tag; - - /* Handle residuals */ - if (res_in < 0) { - iu->srp.rsp.flags |= SRP_RSP_FLAG_DIUNDER; - res_in = -res_in; - } else if (res_in) { - iu->srp.rsp.flags |= SRP_RSP_FLAG_DIOVER; - } - if (res_out < 0) { - iu->srp.rsp.flags |= SRP_RSP_FLAG_DOUNDER; - res_out = -res_out; - } else if (res_out) { - iu->srp.rsp.flags |= SRP_RSP_FLAG_DOOVER; - } - iu->srp.rsp.data_in_res_cnt = cpu_to_be32(res_in); - iu->srp.rsp.data_out_res_cnt = cpu_to_be32(res_out); - - /* We don't do response data */ - /* iu->srp.rsp.flags &= ~SRP_RSP_FLAG_RSPVALID; */ - iu->srp.rsp.resp_data_len = cpu_to_be32(0); - - /* Handle success vs. failure */ - iu->srp.rsp.status = status; - if (status) { - iu->srp.rsp.sol_not = (sol_not & 0x04) >> 2; - if (req->senselen) { - req->iu.srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID; - req->iu.srp.rsp.sense_data_len = cpu_to_be32(req->senselen); - memcpy(req->iu.srp.rsp.data, req->sense, req->senselen); - total_len += req->senselen; - } - } else { - iu->srp.rsp.sol_not = (sol_not & 0x02) >> 1; - } - - vscsi_send_iu(s, req, total_len, VIOSRP_SRP_FORMAT); - return 0; -} - -static inline struct srp_direct_buf vscsi_swap_desc(struct srp_direct_buf desc) -{ - desc.va = be64_to_cpu(desc.va); - desc.len = be32_to_cpu(desc.len); - return desc; -} - -static int vscsi_fetch_desc(VSCSIState *s, struct vscsi_req *req, - unsigned n, unsigned buf_offset, - struct srp_direct_buf *ret) -{ - struct srp_cmd *cmd = &req->iu.srp.cmd; - - switch (req->dma_fmt) { - case SRP_NO_DATA_DESC: { - DPRINTF("VSCSI: no data descriptor\n"); - return 0; - } - case SRP_DATA_DESC_DIRECT: { - memcpy(ret, cmd->add_data + req->cdb_offset, sizeof(*ret)); - assert(req->cur_desc_num == 0); - DPRINTF("VSCSI: direct segment\n"); - break; - } - case SRP_DATA_DESC_INDIRECT: { - struct srp_indirect_buf *tmp = (struct srp_indirect_buf *) - (cmd->add_data + req->cdb_offset); - if (n < req->local_desc) { - *ret = tmp->desc_list[n]; - DPRINTF("VSCSI: indirect segment local tag=0x%x desc#%d/%d\n", - req->qtag, n, req->local_desc); - - } else if (n < req->total_desc) { - int rc; - struct srp_direct_buf tbl_desc = vscsi_swap_desc(tmp->table_desc); - unsigned desc_offset = n * sizeof(struct srp_direct_buf); - - if (desc_offset >= tbl_desc.len) { - DPRINTF("VSCSI: #%d is ouf of range (%d bytes)\n", - n, desc_offset); - return -1; - } - rc = spapr_vio_dma_read(&s->vdev, tbl_desc.va + desc_offset, - ret, sizeof(struct srp_direct_buf)); - if (rc) { - DPRINTF("VSCSI: spapr_vio_dma_read -> %d reading ext_desc\n", - rc); - return -1; - } - DPRINTF("VSCSI: indirect segment ext. tag=0x%x desc#%d/%d { va=%"PRIx64" len=%x }\n", - req->qtag, n, req->total_desc, tbl_desc.va, tbl_desc.len); - } else { - DPRINTF("VSCSI: Out of descriptors !\n"); - return 0; - } - break; - } - default: - fprintf(stderr, "VSCSI: Unknown format %x\n", req->dma_fmt); - return -1; - } - - *ret = vscsi_swap_desc(*ret); - if (buf_offset > ret->len) { - DPRINTF(" offset=%x is out of a descriptor #%d boundary=%x\n", - buf_offset, req->cur_desc_num, ret->len); - return -1; - } - ret->va += buf_offset; - ret->len -= buf_offset; - - DPRINTF(" cur=%d offs=%x ret { va=%"PRIx64" len=%x }\n", - req->cur_desc_num, req->cur_desc_offset, ret->va, ret->len); - - return ret->len ? 1 : 0; -} - -static int vscsi_srp_direct_data(VSCSIState *s, vscsi_req *req, - uint8_t *buf, uint32_t len) -{ - struct srp_direct_buf md; - uint32_t llen; - int rc = 0; - - rc = vscsi_fetch_desc(s, req, req->cur_desc_num, req->cur_desc_offset, &md); - if (rc < 0) { - return -1; - } else if (rc == 0) { - return 0; - } - - llen = MIN(len, md.len); - if (llen) { - if (req->writing) { /* writing = to device = reading from memory */ - rc = spapr_vio_dma_read(&s->vdev, md.va, buf, llen); - } else { - rc = spapr_vio_dma_write(&s->vdev, md.va, buf, llen); - } - } - - if (rc) { - return -1; - } - req->cur_desc_offset += llen; - - return llen; -} - -static int vscsi_srp_indirect_data(VSCSIState *s, vscsi_req *req, - uint8_t *buf, uint32_t len) -{ - struct srp_direct_buf md; - int rc = 0; - uint32_t llen, total = 0; - - DPRINTF("VSCSI: indirect segment 0x%x bytes\n", len); - - /* While we have data ... */ - while (len) { - rc = vscsi_fetch_desc(s, req, req->cur_desc_num, req->cur_desc_offset, &md); - if (rc < 0) { - return -1; - } else if (rc == 0) { - break; - } - - /* Perform transfer */ - llen = MIN(len, md.len); - if (req->writing) { /* writing = to device = reading from memory */ - rc = spapr_vio_dma_read(&s->vdev, md.va, buf, llen); - } else { - rc = spapr_vio_dma_write(&s->vdev, md.va, buf, llen); - } - if (rc) { - DPRINTF("VSCSI: spapr_vio_dma_r/w(%d) -> %d\n", req->writing, rc); - break; - } - DPRINTF("VSCSI: data: %02x %02x %02x %02x...\n", - buf[0], buf[1], buf[2], buf[3]); - - len -= llen; - buf += llen; - - total += llen; - - /* Update current position in the current descriptor */ - req->cur_desc_offset += llen; - if (md.len == llen) { - /* Go to the next descriptor if the current one finished */ - ++req->cur_desc_num; - req->cur_desc_offset = 0; - } - } - - return rc ? -1 : total; -} - -static int vscsi_srp_transfer_data(VSCSIState *s, vscsi_req *req, - int writing, uint8_t *buf, uint32_t len) -{ - int err = 0; - - switch (req->dma_fmt) { - case SRP_NO_DATA_DESC: - DPRINTF("VSCSI: no data desc transfer, skipping 0x%x bytes\n", len); - break; - case SRP_DATA_DESC_DIRECT: - err = vscsi_srp_direct_data(s, req, buf, len); - break; - case SRP_DATA_DESC_INDIRECT: - err = vscsi_srp_indirect_data(s, req, buf, len); - break; - } - return err; -} - -/* Bits from linux srp */ -static int data_out_desc_size(struct srp_cmd *cmd) -{ - int size = 0; - uint8_t fmt = cmd->buf_fmt >> 4; - - switch (fmt) { - case SRP_NO_DATA_DESC: - break; - case SRP_DATA_DESC_DIRECT: - size = sizeof(struct srp_direct_buf); - break; - case SRP_DATA_DESC_INDIRECT: - size = sizeof(struct srp_indirect_buf) + - sizeof(struct srp_direct_buf)*cmd->data_out_desc_cnt; - break; - default: - break; - } - return size; -} - -static int vscsi_preprocess_desc(vscsi_req *req) -{ - struct srp_cmd *cmd = &req->iu.srp.cmd; - - req->cdb_offset = cmd->add_cdb_len & ~3; - - if (req->writing) { - req->dma_fmt = cmd->buf_fmt >> 4; - } else { - req->cdb_offset += data_out_desc_size(cmd); - req->dma_fmt = cmd->buf_fmt & ((1U << 4) - 1); - } - - switch (req->dma_fmt) { - case SRP_NO_DATA_DESC: - break; - case SRP_DATA_DESC_DIRECT: - req->total_desc = req->local_desc = 1; - break; - case SRP_DATA_DESC_INDIRECT: { - struct srp_indirect_buf *ind_tmp = (struct srp_indirect_buf *) - (cmd->add_data + req->cdb_offset); - - req->total_desc = be32_to_cpu(ind_tmp->table_desc.len) / - sizeof(struct srp_direct_buf); - req->local_desc = req->writing ? cmd->data_out_desc_cnt : - cmd->data_in_desc_cnt; - break; - } - default: - fprintf(stderr, - "vscsi_preprocess_desc: Unknown format %x\n", req->dma_fmt); - return -1; - } - - return 0; -} - -/* Callback to indicate that the SCSI layer has completed a transfer. */ -static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len) -{ - VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(sreq->bus->qbus.parent); - vscsi_req *req = sreq->hba_private; - uint8_t *buf; - int rc = 0; - - DPRINTF("VSCSI: SCSI xfer complete tag=0x%x len=0x%x, req=%p\n", - sreq->tag, len, req); - if (req == NULL) { - fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag); - return; - } - - if (len) { - buf = scsi_req_get_buf(sreq); - rc = vscsi_srp_transfer_data(s, req, req->writing, buf, len); - } - if (rc < 0) { - fprintf(stderr, "VSCSI: RDMA error rc=%d!\n", rc); - req->dma_error = true; - scsi_req_cancel(req->sreq); - return; - } - - /* Start next chunk */ - req->data_len -= rc; - scsi_req_continue(sreq); -} - -/* Callback to indicate that the SCSI layer has completed a transfer. */ -static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status, size_t resid) -{ - VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(sreq->bus->qbus.parent); - vscsi_req *req = sreq->hba_private; - int32_t res_in = 0, res_out = 0; - - DPRINTF("VSCSI: SCSI cmd complete, tag=0x%x status=0x%x, req=%p\n", - sreq->tag, status, req); - if (req == NULL) { - fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag); - return; - } - - if (status == CHECK_CONDITION) { - req->senselen = scsi_req_get_sense(req->sreq, req->sense, - sizeof(req->sense)); - DPRINTF("VSCSI: Sense data, %d bytes:\n", req->senselen); - DPRINTF(" %02x %02x %02x %02x %02x %02x %02x %02x\n", - req->sense[0], req->sense[1], req->sense[2], req->sense[3], - req->sense[4], req->sense[5], req->sense[6], req->sense[7]); - DPRINTF(" %02x %02x %02x %02x %02x %02x %02x %02x\n", - req->sense[8], req->sense[9], req->sense[10], req->sense[11], - req->sense[12], req->sense[13], req->sense[14], req->sense[15]); - } - - DPRINTF("VSCSI: Command complete err=%d\n", status); - if (status == 0) { - /* We handle overflows, not underflows for normal commands, - * but hopefully nobody cares - */ - if (req->writing) { - res_out = req->data_len; - } else { - res_in = req->data_len; - } - } - vscsi_send_rsp(s, req, status, res_in, res_out); - vscsi_put_req(req); -} - -static void vscsi_request_cancelled(SCSIRequest *sreq) -{ - vscsi_req *req = sreq->hba_private; - - if (req->dma_error) { - VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(sreq->bus->qbus.parent); - - vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0); - vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); - } - vscsi_put_req(req); -} - -static const VMStateDescription vmstate_spapr_vscsi_req = { - .name = "spapr_vscsi_req", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_BUFFER(crq.raw, vscsi_req), - VMSTATE_BUFFER(iu.srp.reserved, vscsi_req), - VMSTATE_UINT32(qtag, vscsi_req), - VMSTATE_BOOL(active, vscsi_req), - VMSTATE_UINT32(data_len, vscsi_req), - VMSTATE_BOOL(writing, vscsi_req), - VMSTATE_UINT32(senselen, vscsi_req), - VMSTATE_BUFFER(sense, vscsi_req), - VMSTATE_UINT8(dma_fmt, vscsi_req), - VMSTATE_UINT16(local_desc, vscsi_req), - VMSTATE_UINT16(total_desc, vscsi_req), - VMSTATE_UINT16(cdb_offset, vscsi_req), - /*Restart SCSI request from the beginning for now */ - /*VMSTATE_UINT16(cur_desc_num, vscsi_req), - VMSTATE_UINT16(cur_desc_offset, vscsi_req),*/ - VMSTATE_END_OF_LIST() - }, -}; - -static void vscsi_save_request(QEMUFile *f, SCSIRequest *sreq) -{ - vscsi_req *req = sreq->hba_private; - assert(req->active); - - vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL); - - DPRINTF("VSCSI: saving tag=%u, current desc#%d, offset=%x\n", - req->qtag, req->cur_desc_num, req->cur_desc_offset); -} - -static void *vscsi_load_request(QEMUFile *f, SCSIRequest *sreq) -{ - SCSIBus *bus = sreq->bus; - VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(bus->qbus.parent); - vscsi_req *req; - int rc; - - assert(sreq->tag < VSCSI_REQ_LIMIT); - req = &s->reqs[sreq->tag]; - assert(!req->active); - - memset(req, 0, sizeof(*req)); - rc = vmstate_load_state(f, &vmstate_spapr_vscsi_req, req, 1); - if (rc) { - fprintf(stderr, "VSCSI: failed loading request tag#%u\n", sreq->tag); - return NULL; - } - assert(req->active); - - req->sreq = scsi_req_ref(sreq); - - DPRINTF("VSCSI: restoring tag=%u, current desc#%d, offset=%x\n", - req->qtag, req->cur_desc_num, req->cur_desc_offset); - - return req; -} - -static void vscsi_process_login(VSCSIState *s, vscsi_req *req) -{ - union viosrp_iu *iu = &req->iu; - struct srp_login_rsp *rsp = &iu->srp.login_rsp; - uint64_t tag = iu->srp.rsp.tag; - - DPRINTF("VSCSI: Got login, sendin response !\n"); - - /* TODO handle case that requested size is wrong and - * buffer format is wrong - */ - memset(iu, 0, sizeof(struct srp_login_rsp)); - rsp->opcode = SRP_LOGIN_RSP; - /* Don't advertise quite as many request as we support to - * keep room for management stuff etc... - */ - rsp->req_lim_delta = cpu_to_be32(VSCSI_REQ_LIMIT-2); - rsp->tag = tag; - rsp->max_it_iu_len = cpu_to_be32(sizeof(union srp_iu)); - rsp->max_ti_iu_len = cpu_to_be32(sizeof(union srp_iu)); - /* direct and indirect */ - rsp->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT); - - vscsi_send_iu(s, req, sizeof(*rsp), VIOSRP_SRP_FORMAT); -} - -static void vscsi_inquiry_no_target(VSCSIState *s, vscsi_req *req) -{ - uint8_t *cdb = req->iu.srp.cmd.cdb; - uint8_t resp_data[36]; - int rc, len, alen; - - /* We dont do EVPD. Also check that page_code is 0 */ - if ((cdb[1] & 0x01) || cdb[2] != 0) { - /* Send INVALID FIELD IN CDB */ - vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x24, 0); - vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); - return; - } - alen = cdb[3]; - alen = (alen << 8) | cdb[4]; - len = MIN(alen, 36); - - /* Fake up inquiry using PQ=3 */ - memset(resp_data, 0, 36); - resp_data[0] = 0x7f; /* Not capable of supporting a device here */ - resp_data[2] = 0x06; /* SPS-4 */ - resp_data[3] = 0x02; /* Resp data format */ - resp_data[4] = 36 - 5; /* Additional length */ - resp_data[7] = 0x10; /* Sync transfers */ - memcpy(&resp_data[16], "QEMU EMPTY ", 16); - memcpy(&resp_data[8], "QEMU ", 8); - - req->writing = 0; - vscsi_preprocess_desc(req); - rc = vscsi_srp_transfer_data(s, req, 0, resp_data, len); - if (rc < 0) { - vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0); - vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); - } else { - vscsi_send_rsp(s, req, 0, 36 - rc, 0); - } -} - -static void vscsi_report_luns(VSCSIState *s, vscsi_req *req) -{ - BusChild *kid; - int i, len, n, rc; - uint8_t *resp_data; - bool found_lun0; - - n = 0; - found_lun0 = false; - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - SCSIDevice *dev = SCSI_DEVICE(kid->child); - - n += 8; - if (dev->channel == 0 && dev->id == 0 && dev->lun == 0) { - found_lun0 = true; - } - } - if (!found_lun0) { - n += 8; - } - len = n+8; - - resp_data = g_malloc0(len); - stl_be_p(resp_data, n); - i = found_lun0 ? 8 : 16; - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - DeviceState *qdev = kid->child; - SCSIDevice *dev = SCSI_DEVICE(qdev); - - if (dev->id == 0 && dev->channel == 0) { - resp_data[i] = 0; /* Use simple LUN for 0 (SAM5 4.7.7.1) */ - } else { - resp_data[i] = (2 << 6); /* Otherwise LUN addressing (4.7.7.4) */ - } - resp_data[i] |= dev->id; - resp_data[i+1] = (dev->channel << 5); - resp_data[i+1] |= dev->lun; - i += 8; - } - - vscsi_preprocess_desc(req); - rc = vscsi_srp_transfer_data(s, req, 0, resp_data, len); - g_free(resp_data); - if (rc < 0) { - vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0); - vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); - } else { - vscsi_send_rsp(s, req, 0, len - rc, 0); - } -} - -static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) -{ - union srp_iu *srp = &req->iu.srp; - SCSIDevice *sdev; - int n, lun; - - if ((srp->cmd.lun == 0 || be64_to_cpu(srp->cmd.lun) == SRP_REPORT_LUNS_WLUN) - && srp->cmd.cdb[0] == REPORT_LUNS) { - vscsi_report_luns(s, req); - return 0; - } - - sdev = vscsi_device_find(&s->bus, be64_to_cpu(srp->cmd.lun), &lun); - if (!sdev) { - DPRINTF("VSCSI: Command for lun %08" PRIx64 " with no drive\n", - be64_to_cpu(srp->cmd.lun)); - if (srp->cmd.cdb[0] == INQUIRY) { - vscsi_inquiry_no_target(s, req); - } else { - vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x24, 0x00); - vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); - } return 1; - } - - req->sreq = scsi_req_new(sdev, req->qtag, lun, srp->cmd.cdb, req); - n = scsi_req_enqueue(req->sreq); - - DPRINTF("VSCSI: Queued command tag 0x%x CMD 0x%x=%s LUN %d ret: %d\n", - req->qtag, srp->cmd.cdb[0], scsi_command_name(srp->cmd.cdb[0]), - lun, n); - - if (n) { - /* Transfer direction must be set before preprocessing the - * descriptors - */ - req->writing = (n < 1); - - /* Preprocess RDMA descriptors */ - vscsi_preprocess_desc(req); - - /* Get transfer direction and initiate transfer */ - if (n > 0) { - req->data_len = n; - } else if (n < 0) { - req->data_len = -n; - } - scsi_req_continue(req->sreq); - } - /* Don't touch req here, it may have been recycled already */ - - return 0; -} - -static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) -{ - union viosrp_iu *iu = &req->iu; - vscsi_req *tmpreq; - int i, lun = 0, resp = SRP_TSK_MGMT_COMPLETE; - SCSIDevice *d; - uint64_t tag = iu->srp.rsp.tag; - uint8_t sol_not = iu->srp.cmd.sol_not; - - fprintf(stderr, "vscsi_process_tsk_mgmt %02x\n", - iu->srp.tsk_mgmt.tsk_mgmt_func); - - d = vscsi_device_find(&s->bus, be64_to_cpu(req->iu.srp.tsk_mgmt.lun), &lun); - if (!d) { - resp = SRP_TSK_MGMT_FIELDS_INVALID; - } else { - switch (iu->srp.tsk_mgmt.tsk_mgmt_func) { - case SRP_TSK_ABORT_TASK: - if (d->lun != lun) { - resp = SRP_TSK_MGMT_FIELDS_INVALID; - break; - } - - tmpreq = vscsi_find_req(s, req->iu.srp.tsk_mgmt.task_tag); - if (tmpreq && tmpreq->sreq) { - assert(tmpreq->sreq->hba_private); - scsi_req_cancel(tmpreq->sreq); - } - break; - - case SRP_TSK_LUN_RESET: - if (d->lun != lun) { - resp = SRP_TSK_MGMT_FIELDS_INVALID; - break; - } - - qdev_reset_all(&d->qdev); - break; - - case SRP_TSK_ABORT_TASK_SET: - case SRP_TSK_CLEAR_TASK_SET: - if (d->lun != lun) { - resp = SRP_TSK_MGMT_FIELDS_INVALID; - break; - } - - for (i = 0; i < VSCSI_REQ_LIMIT; i++) { - tmpreq = &s->reqs[i]; - if (tmpreq->iu.srp.cmd.lun != req->iu.srp.tsk_mgmt.lun) { - continue; - } - if (!tmpreq->active || !tmpreq->sreq) { - continue; - } - assert(tmpreq->sreq->hba_private); - scsi_req_cancel(tmpreq->sreq); - } - break; - - case SRP_TSK_CLEAR_ACA: - resp = SRP_TSK_MGMT_NOT_SUPPORTED; - break; - - default: - resp = SRP_TSK_MGMT_FIELDS_INVALID; - break; - } - } - - /* Compose the response here as */ - memset(iu, 0, sizeof(struct srp_rsp) + 4); - iu->srp.rsp.opcode = SRP_RSP; - iu->srp.rsp.req_lim_delta = cpu_to_be32(1); - iu->srp.rsp.tag = tag; - iu->srp.rsp.flags |= SRP_RSP_FLAG_RSPVALID; - iu->srp.rsp.resp_data_len = cpu_to_be32(4); - if (resp) { - iu->srp.rsp.sol_not = (sol_not & 0x04) >> 2; - } else { - iu->srp.rsp.sol_not = (sol_not & 0x02) >> 1; - } - - iu->srp.rsp.status = GOOD; - iu->srp.rsp.data[3] = resp; - - vscsi_send_iu(s, req, sizeof(iu->srp.rsp) + 4, VIOSRP_SRP_FORMAT); - - return 1; -} - -static int vscsi_handle_srp_req(VSCSIState *s, vscsi_req *req) -{ - union srp_iu *srp = &req->iu.srp; - int done = 1; - uint8_t opcode = srp->rsp.opcode; - - switch (opcode) { - case SRP_LOGIN_REQ: - vscsi_process_login(s, req); - break; - case SRP_TSK_MGMT: - done = vscsi_process_tsk_mgmt(s, req); - break; - case SRP_CMD: - done = vscsi_queue_cmd(s, req); - break; - case SRP_LOGIN_RSP: - case SRP_I_LOGOUT: - case SRP_T_LOGOUT: - case SRP_RSP: - case SRP_CRED_REQ: - case SRP_CRED_RSP: - case SRP_AER_REQ: - case SRP_AER_RSP: - fprintf(stderr, "VSCSI: Unsupported opcode %02x\n", opcode); - break; - default: - fprintf(stderr, "VSCSI: Unknown type %02x\n", opcode); - } - - return done; -} - -static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req) -{ - struct viosrp_adapter_info *sinfo; - struct mad_adapter_info_data info; - int rc; - - sinfo = &req->iu.mad.adapter_info; - -#if 0 /* What for ? */ - rc = spapr_vio_dma_read(&s->vdev, be64_to_cpu(sinfo->buffer), - &info, be16_to_cpu(sinfo->common.length)); - if (rc) { - fprintf(stderr, "vscsi_send_adapter_info: DMA read failure !\n"); - } -#endif - memset(&info, 0, sizeof(info)); - strcpy(info.srp_version, SRP_VERSION); - memcpy(info.partition_name, "qemu", sizeof("qemu")); - info.partition_number = cpu_to_be32(0); - info.mad_version = cpu_to_be32(1); - info.os_type = cpu_to_be32(2); - info.port_max_txu[0] = cpu_to_be32(VSCSI_MAX_SECTORS << 9); - - rc = spapr_vio_dma_write(&s->vdev, be64_to_cpu(sinfo->buffer), - &info, be16_to_cpu(sinfo->common.length)); - if (rc) { - fprintf(stderr, "vscsi_send_adapter_info: DMA write failure !\n"); - } - - sinfo->common.status = rc ? cpu_to_be32(1) : 0; - - return vscsi_send_iu(s, req, sizeof(*sinfo), VIOSRP_MAD_FORMAT); -} - -static int vscsi_send_capabilities(VSCSIState *s, vscsi_req *req) -{ - struct viosrp_capabilities *vcap; - struct capabilities cap = { }; - uint16_t len, req_len; - uint64_t buffer; - int rc; - - vcap = &req->iu.mad.capabilities; - req_len = len = be16_to_cpu(vcap->common.length); - buffer = be64_to_cpu(vcap->buffer); - if (len > sizeof(cap)) { - fprintf(stderr, "vscsi_send_capabilities: capabilities size mismatch !\n"); - - /* - * Just read and populate the structure that is known. - * Zero rest of the structure. - */ - len = sizeof(cap); - } - rc = spapr_vio_dma_read(&s->vdev, buffer, &cap, len); - if (rc) { - fprintf(stderr, "vscsi_send_capabilities: DMA read failure !\n"); - } - - /* - * Current implementation does not suppport any migration or - * reservation capabilities. Construct the response telling the - * guest not to use them. - */ - cap.flags = 0; - cap.migration.ecl = 0; - cap.reserve.type = 0; - cap.migration.common.server_support = 0; - cap.reserve.common.server_support = 0; - - rc = spapr_vio_dma_write(&s->vdev, buffer, &cap, len); - if (rc) { - fprintf(stderr, "vscsi_send_capabilities: DMA write failure !\n"); - } - if (req_len > len) { - /* - * Being paranoid and lets not worry about the error code - * here. Actual write of the cap is done above. - */ - spapr_vio_dma_set(&s->vdev, (buffer + len), 0, (req_len - len)); - } - vcap->common.status = rc ? cpu_to_be32(1) : 0; - return vscsi_send_iu(s, req, sizeof(*vcap), VIOSRP_MAD_FORMAT); -} - -static int vscsi_handle_mad_req(VSCSIState *s, vscsi_req *req) -{ - union mad_iu *mad = &req->iu.mad; - bool request_handled = false; - uint64_t retlen = 0; - - switch (be32_to_cpu(mad->empty_iu.common.type)) { - case VIOSRP_EMPTY_IU_TYPE: - fprintf(stderr, "Unsupported EMPTY MAD IU\n"); - retlen = sizeof(mad->empty_iu); - break; - case VIOSRP_ERROR_LOG_TYPE: - fprintf(stderr, "Unsupported ERROR LOG MAD IU\n"); - retlen = sizeof(mad->error_log); - break; - case VIOSRP_ADAPTER_INFO_TYPE: - vscsi_send_adapter_info(s, req); - request_handled = true; - break; - case VIOSRP_HOST_CONFIG_TYPE: - retlen = sizeof(mad->host_config); - break; - case VIOSRP_CAPABILITIES_TYPE: - vscsi_send_capabilities(s, req); - request_handled = true; - break; - default: - fprintf(stderr, "VSCSI: Unknown MAD type %02x\n", - be32_to_cpu(mad->empty_iu.common.type)); - /* - * PAPR+ says that "The length field is set to the length - * of the data structure(s) used in the command". - * As we did not recognize the request type, put zero there. - */ - retlen = 0; - } - - if (!request_handled) { - mad->empty_iu.common.status = cpu_to_be16(VIOSRP_MAD_NOT_SUPPORTED); - vscsi_send_iu(s, req, retlen, VIOSRP_MAD_FORMAT); - } - - return 1; -} - -static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq) -{ - vscsi_req *req; - int done; - - req = vscsi_get_req(s); - if (req == NULL) { - fprintf(stderr, "VSCSI: Failed to get a request !\n"); - return; - } - - /* We only support a limited number of descriptors, we know - * the ibmvscsi driver uses up to 10 max, so it should fit - * in our 256 bytes IUs. If not we'll have to increase the size - * of the structure. - */ - if (crq->s.IU_length > sizeof(union viosrp_iu)) { - fprintf(stderr, "VSCSI: SRP IU too long (%d bytes) !\n", - crq->s.IU_length); - vscsi_put_req(req); - return; - } - - /* XXX Handle failure differently ? */ - if (spapr_vio_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->iu, - crq->s.IU_length)) { - fprintf(stderr, "vscsi_got_payload: DMA read failure !\n"); - vscsi_put_req(req); - return; - } - memcpy(&req->crq, crq, sizeof(vscsi_crq)); - - if (crq->s.format == VIOSRP_MAD_FORMAT) { - done = vscsi_handle_mad_req(s, req); - } else { - done = vscsi_handle_srp_req(s, req); - } - - if (done) { - vscsi_put_req(req); - } -} - - -static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data) -{ - VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(dev); - vscsi_crq crq; - - memcpy(crq.raw, crq_data, 16); - crq.s.timeout = be16_to_cpu(crq.s.timeout); - crq.s.IU_length = be16_to_cpu(crq.s.IU_length); - crq.s.IU_data_ptr = be64_to_cpu(crq.s.IU_data_ptr); - - DPRINTF("VSCSI: do_crq %02x %02x ...\n", crq.raw[0], crq.raw[1]); - - switch (crq.s.valid) { - case 0xc0: /* Init command/response */ - - /* Respond to initialization request */ - if (crq.s.format == 0x01) { - memset(crq.raw, 0, 16); - crq.s.valid = 0xc0; - crq.s.format = 0x02; - spapr_vio_send_crq(dev, crq.raw); - } - - /* Note that in hotplug cases, we might get a 0x02 - * as a result of us emitting the init request - */ - - break; - case 0xff: /* Link event */ - - /* Not handled for now */ - - break; - case 0x80: /* Payloads */ - switch (crq.s.format) { - case VIOSRP_SRP_FORMAT: /* AKA VSCSI request */ - case VIOSRP_MAD_FORMAT: /* AKA VSCSI response */ - vscsi_got_payload(s, &crq); - break; - case VIOSRP_OS400_FORMAT: - case VIOSRP_AIX_FORMAT: - case VIOSRP_LINUX_FORMAT: - case VIOSRP_INLINE_FORMAT: - fprintf(stderr, "vscsi_do_srq: Unsupported payload format %02x\n", - crq.s.format); - break; - default: - fprintf(stderr, "vscsi_do_srq: Unknown payload format %02x\n", - crq.s.format); - } - break; - default: - fprintf(stderr, "vscsi_do_crq: unknown CRQ %02x %02x ...\n", - crq.raw[0], crq.raw[1]); - }; - - return 0; -} - -static const struct SCSIBusInfo vscsi_scsi_info = { - .tcq = true, - .max_channel = 7, /* logical unit addressing format */ - .max_target = 63, - .max_lun = 31, - - .transfer_data = vscsi_transfer_data, - .complete = vscsi_command_complete, - .cancel = vscsi_request_cancelled, - .save_request = vscsi_save_request, - .load_request = vscsi_load_request, -}; - -static void spapr_vscsi_reset(VIOsPAPRDevice *dev) -{ - VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(dev); - int i; - - memset(s->reqs, 0, sizeof(s->reqs)); - for (i = 0; i < VSCSI_REQ_LIMIT; i++) { - s->reqs[i].qtag = i; - } -} - -static void spapr_vscsi_realize(VIOsPAPRDevice *dev, Error **errp) -{ - VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(dev); - - dev->crq.SendFunc = vscsi_do_crq; - - scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), - &vscsi_scsi_info, NULL); - if (!dev->qdev.hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, errp); - } -} - -void spapr_vscsi_create(VIOsPAPRBus *bus) -{ - DeviceState *dev; - - dev = qdev_create(&bus->bus, "spapr-vscsi"); - - qdev_init_nofail(dev); -} - -static int spapr_vscsi_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) -{ - int ret; - - ret = fdt_setprop_cell(fdt, node_off, "#address-cells", 2); - if (ret < 0) { - return ret; - } - - ret = fdt_setprop_cell(fdt, node_off, "#size-cells", 0); - if (ret < 0) { - return ret; - } - - return 0; -} - -static Property spapr_vscsi_properties[] = { - DEFINE_SPAPR_PROPERTIES(VSCSIState, vdev), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription vmstate_spapr_vscsi = { - .name = "spapr_vscsi", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_SPAPR_VIO(vdev, VSCSIState), - /* VSCSI state */ - /* ???? */ - - VMSTATE_END_OF_LIST() - }, -}; - -static void spapr_vscsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); - - k->realize = spapr_vscsi_realize; - k->reset = spapr_vscsi_reset; - k->devnode = spapr_vscsi_devnode; - k->dt_name = "v-scsi"; - k->dt_type = "vscsi"; - k->dt_compatible = "IBM,v-scsi"; - k->signal_mask = 0x00000001; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = spapr_vscsi_properties; - k->rtce_window_size = 0x10000000; - dc->vmsd = &vmstate_spapr_vscsi; -} - -static const TypeInfo spapr_vscsi_info = { - .name = TYPE_VIO_SPAPR_VSCSI_DEVICE, - .parent = TYPE_VIO_SPAPR_DEVICE, - .instance_size = sizeof(VSCSIState), - .class_init = spapr_vscsi_class_init, -}; - -static void spapr_vscsi_register_types(void) -{ - type_register_static(&spapr_vscsi_info); -} - -type_init(spapr_vscsi_register_types) diff --git a/qemu/hw/scsi/srp.h b/qemu/hw/scsi/srp.h deleted file mode 100644 index d27f31d2d..000000000 --- a/qemu/hw/scsi/srp.h +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (c) 2005 Cisco Systems. All rights reserved. - * - * This software is available to you under a choice of one of two - * licenses. You may choose to be licensed under the terms of the GNU - * General Public License (GPL) Version 2, available from the file - * COPYING in the main directory of this source tree, or the - * OpenIB.org BSD license below: - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -#ifndef SCSI_SRP_H -#define SCSI_SRP_H - -/* - * Structures and constants for the SCSI RDMA Protocol (SRP) as - * defined by the INCITS T10 committee. This file was written using - * draft Revision 16a of the SRP standard. - */ - -enum { - - SRP_LOGIN_REQ = 0x00, - SRP_TSK_MGMT = 0x01, - SRP_CMD = 0x02, - SRP_I_LOGOUT = 0x03, - SRP_LOGIN_RSP = 0xc0, - SRP_RSP = 0xc1, - SRP_LOGIN_REJ = 0xc2, - SRP_T_LOGOUT = 0x80, - SRP_CRED_REQ = 0x81, - SRP_AER_REQ = 0x82, - SRP_CRED_RSP = 0x41, - SRP_AER_RSP = 0x42 -}; - -enum { - SRP_BUF_FORMAT_DIRECT = 1 << 1, - SRP_BUF_FORMAT_INDIRECT = 1 << 2 -}; - -enum { - SRP_NO_DATA_DESC = 0, - SRP_DATA_DESC_DIRECT = 1, - SRP_DATA_DESC_INDIRECT = 2 -}; - -enum { - SRP_TSK_ABORT_TASK = 0x01, - SRP_TSK_ABORT_TASK_SET = 0x02, - SRP_TSK_CLEAR_TASK_SET = 0x04, - SRP_TSK_LUN_RESET = 0x08, - SRP_TSK_CLEAR_ACA = 0x40 -}; - -enum srp_login_rej_reason { - SRP_LOGIN_REJ_UNABLE_ESTABLISH_CHANNEL = 0x00010000, - SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES = 0x00010001, - SRP_LOGIN_REJ_REQ_IT_IU_LENGTH_TOO_LARGE = 0x00010002, - SRP_LOGIN_REJ_UNABLE_ASSOCIATE_CHANNEL = 0x00010003, - SRP_LOGIN_REJ_UNSUPPORTED_DESCRIPTOR_FMT = 0x00010004, - SRP_LOGIN_REJ_MULTI_CHANNEL_UNSUPPORTED = 0x00010005, - SRP_LOGIN_REJ_CHANNEL_LIMIT_REACHED = 0x00010006 -}; - -enum { - SRP_REV10_IB_IO_CLASS = 0xff00, - SRP_REV16A_IB_IO_CLASS = 0x0100 -}; - -enum { - SRP_TSK_MGMT_COMPLETE = 0x00, - SRP_TSK_MGMT_FIELDS_INVALID = 0x02, - SRP_TSK_MGMT_NOT_SUPPORTED = 0x04, - SRP_TSK_MGMT_FAILED = 0x05 -}; - -struct srp_direct_buf { - uint64_t va; - uint32_t key; - uint32_t len; -}; - -/* - * We need the packed attribute because the SRP spec puts the list of - * descriptors at an offset of 20, which is not aligned to the size of - * struct srp_direct_buf. The whole structure must be packed to avoid - * having the 20-byte structure padded to 24 bytes on 64-bit architectures. - */ -struct srp_indirect_buf { - struct srp_direct_buf table_desc; - uint32_t len; - struct srp_direct_buf desc_list[0]; -} QEMU_PACKED; - -enum { - SRP_MULTICHAN_SINGLE = 0, - SRP_MULTICHAN_MULTI = 1 -}; - -struct srp_login_req { - uint8_t opcode; - uint8_t reserved1[7]; - uint64_t tag; - uint32_t req_it_iu_len; - uint8_t reserved2[4]; - uint16_t req_buf_fmt; - uint8_t req_flags; - uint8_t reserved3[5]; - uint8_t initiator_port_id[16]; - uint8_t target_port_id[16]; -}; - -/* - * The SRP spec defines the size of the LOGIN_RSP structure to be 52 - * bytes, so it needs to be packed to avoid having it padded to 56 - * bytes on 64-bit architectures. - */ -struct srp_login_rsp { - uint8_t opcode; - uint8_t reserved1[3]; - uint32_t req_lim_delta; - uint64_t tag; - uint32_t max_it_iu_len; - uint32_t max_ti_iu_len; - uint16_t buf_fmt; - uint8_t rsp_flags; - uint8_t reserved2[25]; -} QEMU_PACKED; - -struct srp_login_rej { - uint8_t opcode; - uint8_t reserved1[3]; - uint32_t reason; - uint64_t tag; - uint8_t reserved2[8]; - uint16_t buf_fmt; - uint8_t reserved3[6]; -}; - -struct srp_i_logout { - uint8_t opcode; - uint8_t reserved[7]; - uint64_t tag; -}; - -struct srp_t_logout { - uint8_t opcode; - uint8_t sol_not; - uint8_t reserved[2]; - uint32_t reason; - uint64_t tag; -}; - -/* - * We need the packed attribute because the SRP spec only aligns the - * 8-byte LUN field to 4 bytes. - */ -struct srp_tsk_mgmt { - uint8_t opcode; - uint8_t sol_not; - uint8_t reserved1[6]; - uint64_t tag; - uint8_t reserved2[4]; - uint64_t lun; - uint8_t reserved3[2]; - uint8_t tsk_mgmt_func; - uint8_t reserved4; - uint64_t task_tag; - uint8_t reserved5[8]; -} QEMU_PACKED; - -/* - * We need the packed attribute because the SRP spec only aligns the - * 8-byte LUN field to 4 bytes. - */ -struct srp_cmd { - uint8_t opcode; - uint8_t sol_not; - uint8_t reserved1[3]; - uint8_t buf_fmt; - uint8_t data_out_desc_cnt; - uint8_t data_in_desc_cnt; - uint64_t tag; - uint8_t reserved2[4]; - uint64_t lun; - uint8_t reserved3; - uint8_t task_attr; - uint8_t reserved4; - uint8_t add_cdb_len; - uint8_t cdb[16]; - uint8_t add_data[0]; -} QEMU_PACKED; - -enum { - SRP_RSP_FLAG_RSPVALID = 1 << 0, - SRP_RSP_FLAG_SNSVALID = 1 << 1, - SRP_RSP_FLAG_DOOVER = 1 << 2, - SRP_RSP_FLAG_DOUNDER = 1 << 3, - SRP_RSP_FLAG_DIOVER = 1 << 4, - SRP_RSP_FLAG_DIUNDER = 1 << 5 -}; - -/* - * The SRP spec defines the size of the RSP structure to be 36 bytes, - * so it needs to be packed to avoid having it padded to 40 bytes on - * 64-bit architectures. - */ -struct srp_rsp { - uint8_t opcode; - uint8_t sol_not; - uint8_t reserved1[2]; - uint32_t req_lim_delta; - uint64_t tag; - uint8_t reserved2[2]; - uint8_t flags; - uint8_t status; - uint32_t data_out_res_cnt; - uint32_t data_in_res_cnt; - uint32_t sense_data_len; - uint32_t resp_data_len; - uint8_t data[0]; -} QEMU_PACKED; - -#endif /* SCSI_SRP_H */ diff --git a/qemu/hw/scsi/vhost-scsi.c b/qemu/hw/scsi/vhost-scsi.c deleted file mode 100644 index 9261d51da..000000000 --- a/qemu/hw/scsi/vhost-scsi.c +++ /dev/null @@ -1,352 +0,0 @@ -/* - * vhost_scsi host device - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Stefan Hajnoczi - * - * Changes for QEMU mainline + tcm_vhost kernel upstream: - * Nicholas Bellinger - * - * This work is licensed under the terms of the GNU LGPL, version 2 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include -#include "qemu/error-report.h" -#include "qemu/queue.h" -#include "monitor/monitor.h" -#include "migration/migration.h" -#include "hw/virtio/vhost-scsi.h" -#include "hw/virtio/vhost.h" -#include "hw/virtio/virtio-scsi.h" -#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[] = { - VIRTIO_F_NOTIFY_ON_EMPTY, - VIRTIO_RING_F_INDIRECT_DESC, - VIRTIO_RING_F_EVENT_IDX, - VIRTIO_SCSI_F_HOTPLUG, - VHOST_INVALID_FEATURE_BIT -}; - -static int vhost_scsi_set_endpoint(VHostSCSI *s) -{ - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - const VhostOps *vhost_ops = s->dev.vhost_ops; - struct vhost_scsi_target backend; - int ret; - - memset(&backend, 0, sizeof(backend)); - pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn); - ret = vhost_ops->vhost_scsi_set_endpoint(&s->dev, &backend); - if (ret < 0) { - return -errno; - } - return 0; -} - -static void vhost_scsi_clear_endpoint(VHostSCSI *s) -{ - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - struct vhost_scsi_target backend; - const VhostOps *vhost_ops = s->dev.vhost_ops; - - memset(&backend, 0, sizeof(backend)); - pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn); - vhost_ops->vhost_scsi_clear_endpoint(&s->dev, &backend); -} - -static int vhost_scsi_start(VHostSCSI *s) -{ - int ret, abi_version, i; - VirtIODevice *vdev = VIRTIO_DEVICE(s); - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - const VhostOps *vhost_ops = s->dev.vhost_ops; - - if (!k->set_guest_notifiers) { - error_report("binding does not support guest notifiers"); - return -ENOSYS; - } - - ret = vhost_ops->vhost_scsi_get_abi_version(&s->dev, &abi_version); - if (ret < 0) { - return -errno; - } - if (abi_version > VHOST_SCSI_ABI_VERSION) { - error_report("vhost-scsi: The running tcm_vhost kernel abi_version:" - " %d is greater than vhost_scsi userspace supports: %d, please" - " upgrade your version of QEMU", abi_version, - VHOST_SCSI_ABI_VERSION); - return -ENOSYS; - } - - ret = vhost_dev_enable_notifiers(&s->dev, vdev); - if (ret < 0) { - return ret; - } - - s->dev.acked_features = vdev->guest_features; - ret = vhost_dev_start(&s->dev, vdev); - if (ret < 0) { - error_report("Error start vhost dev"); - goto err_notifiers; - } - - ret = vhost_scsi_set_endpoint(s); - if (ret < 0) { - error_report("Error set vhost-scsi endpoint"); - goto err_vhost_stop; - } - - ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, true); - if (ret < 0) { - error_report("Error binding guest notifier"); - goto err_endpoint; - } - - /* guest_notifier_mask/pending not used yet, so just unmask - * everything here. virtio-pci will do the right thing by - * enabling/disabling irqfd. - */ - for (i = 0; i < s->dev.nvqs; i++) { - vhost_virtqueue_mask(&s->dev, vdev, s->dev.vq_index + i, false); - } - - return ret; - -err_endpoint: - vhost_scsi_clear_endpoint(s); -err_vhost_stop: - vhost_dev_stop(&s->dev, vdev); -err_notifiers: - vhost_dev_disable_notifiers(&s->dev, vdev); - return ret; -} - -static void vhost_scsi_stop(VHostSCSI *s) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(s); - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - int ret = 0; - - if (k->set_guest_notifiers) { - ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); - if (ret < 0) { - error_report("vhost guest notifier cleanup failed: %d", ret); - } - } - assert(ret >= 0); - - vhost_scsi_clear_endpoint(s); - vhost_dev_stop(&s->dev, vdev); - vhost_dev_disable_notifiers(&s->dev, vdev); -} - -static uint64_t vhost_scsi_get_features(VirtIODevice *vdev, - uint64_t features, - Error **errp) -{ - VHostSCSI *s = VHOST_SCSI(vdev); - - return vhost_get_features(&s->dev, kernel_feature_bits, features); -} - -static void vhost_scsi_set_config(VirtIODevice *vdev, - const uint8_t *config) -{ - VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config; - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); - - if ((uint32_t) virtio_ldl_p(vdev, &scsiconf->sense_size) != vs->sense_size || - (uint32_t) virtio_ldl_p(vdev, &scsiconf->cdb_size) != vs->cdb_size) { - error_report("vhost-scsi does not support changing the sense data and CDB sizes"); - exit(1); - } -} - -static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val) -{ - VHostSCSI *s = (VHostSCSI *)vdev; - bool start = (val & VIRTIO_CONFIG_S_DRIVER_OK); - - if (s->dev.started == start) { - return; - } - - if (start) { - int ret; - - ret = vhost_scsi_start(s); - if (ret < 0) { - error_report("virtio-scsi: unable to start vhost: %s", - strerror(-ret)); - - /* There is no userspace virtio-scsi fallback so exit */ - exit(1); - } - } else { - vhost_scsi_stop(s); - } -} - -static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) -{ -} - -static void vhost_scsi_realize(DeviceState *dev, Error **errp) -{ - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); - VHostSCSI *s = VHOST_SCSI(dev); - Error *err = NULL; - int vhostfd = -1; - int ret; - - if (!vs->conf.wwpn) { - error_setg(errp, "vhost-scsi: missing wwpn"); - return; - } - - if (vs->conf.vhostfd) { - vhostfd = monitor_fd_param(cur_mon, vs->conf.vhostfd, errp); - if (vhostfd == -1) { - error_prepend(errp, "vhost-scsi: unable to parse vhostfd: "); - return; - } - } else { - vhostfd = open("/dev/vhost-scsi", O_RDWR); - if (vhostfd < 0) { - error_setg(errp, "vhost-scsi: open vhost char device failed: %s", - strerror(errno)); - return; - } - } - - virtio_scsi_common_realize(dev, &err, vhost_dummy_handle_output, - vhost_dummy_handle_output, - vhost_dummy_handle_output); - if (err != NULL) { - error_propagate(errp, err); - close(vhostfd); - return; - } - - s->dev.nvqs = VHOST_SCSI_VQ_NUM_FIXED + vs->conf.num_queues; - s->dev.vqs = g_new(struct vhost_virtqueue, s->dev.nvqs); - s->dev.vq_index = 0; - s->dev.backend_features = 0; - - ret = vhost_dev_init(&s->dev, (void *)(uintptr_t)vhostfd, - VHOST_BACKEND_TYPE_KERNEL); - if (ret < 0) { - error_setg(errp, "vhost-scsi: vhost initialization failed: %s", - strerror(-ret)); - return; - } - - /* At present, channel and lun both are 0 for bootable vhost-scsi disk */ - s->channel = 0; - s->lun = 0; - /* Note: we can also get the minimum tpgt from kernel */ - s->target = vs->conf.boot_tpgt; - - error_setg(&s->migration_blocker, - "vhost-scsi does not support migration"); - migrate_add_blocker(s->migration_blocker); -} - -static void vhost_scsi_unrealize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostSCSI *s = VHOST_SCSI(dev); - - migrate_del_blocker(s->migration_blocker); - error_free(s->migration_blocker); - - /* 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); -} - -/* - * Implementation of an interface to adjust firmware path - * for the bootindex property handling. - */ -static char *vhost_scsi_get_fw_dev_path(FWPathProvider *p, BusState *bus, - DeviceState *dev) -{ - VHostSCSI *s = VHOST_SCSI(dev); - /* format: channel@channel/vhost-scsi@target,lun */ - return g_strdup_printf("/channel@%x/%s@%x,%x", s->channel, - qdev_fw_name(dev), s->target, s->lun); -} - -static Property vhost_scsi_properties[] = { - DEFINE_PROP_STRING("vhostfd", VHostSCSI, parent_obj.conf.vhostfd), - DEFINE_PROP_STRING("wwpn", VHostSCSI, parent_obj.conf.wwpn), - DEFINE_PROP_UINT32("boot_tpgt", VHostSCSI, parent_obj.conf.boot_tpgt, 0), - DEFINE_PROP_UINT32("num_queues", VHostSCSI, parent_obj.conf.num_queues, 1), - DEFINE_PROP_UINT32("max_sectors", VHostSCSI, parent_obj.conf.max_sectors, - 0xFFFF), - DEFINE_PROP_UINT32("cmd_per_lun", VHostSCSI, parent_obj.conf.cmd_per_lun, - 128), - DEFINE_PROP_END_OF_LIST(), -}; - -static void vhost_scsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(klass); - - dc->props = vhost_scsi_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - vdc->realize = vhost_scsi_realize; - vdc->unrealize = vhost_scsi_unrealize; - vdc->get_features = vhost_scsi_get_features; - vdc->set_config = vhost_scsi_set_config; - vdc->set_status = vhost_scsi_set_status; - fwc->get_dev_path = vhost_scsi_get_fw_dev_path; -} - -static void vhost_scsi_instance_init(Object *obj) -{ - VHostSCSI *dev = VHOST_SCSI(obj); - - device_add_bootindex_property(obj, &dev->bootindex, "bootindex", NULL, - DEVICE(dev), NULL); -} - -static const TypeInfo vhost_scsi_info = { - .name = TYPE_VHOST_SCSI, - .parent = TYPE_VIRTIO_SCSI_COMMON, - .instance_size = sizeof(VHostSCSI), - .class_init = vhost_scsi_class_init, - .instance_init = vhost_scsi_instance_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_FW_PATH_PROVIDER }, - { } - }, -}; - -static void virtio_register_types(void) -{ - type_register_static(&vhost_scsi_info); -} - -type_init(virtio_register_types) diff --git a/qemu/hw/scsi/viosrp.h b/qemu/hw/scsi/viosrp.h deleted file mode 100644 index d8e365db1..000000000 --- a/qemu/hw/scsi/viosrp.h +++ /dev/null @@ -1,216 +0,0 @@ -/*****************************************************************************/ -/* srp.h -- SCSI RDMA Protocol definitions */ -/* */ -/* Written By: Colin Devilbis, IBM Corporation */ -/* */ -/* Copyright (C) 2003 IBM Corporation */ -/* */ -/* 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, write to the Free Software */ -/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* */ -/* */ -/* This file contains structures and definitions for IBM RPA (RS/6000 */ -/* platform architecture) implementation of the SRP (SCSI RDMA Protocol) */ -/* standard. SRP is used on IBM iSeries and pSeries platforms to send SCSI */ -/* commands between logical partitions. */ -/* */ -/* SRP Information Units (IUs) are sent on a "Command/Response Queue" (CRQ) */ -/* between partitions. The definitions in this file are architected, */ -/* and cannot be changed without breaking compatibility with other versions */ -/* of Linux and other operating systems (AIX, OS/400) that talk this protocol*/ -/* between logical partitions */ -/*****************************************************************************/ -#ifndef PPC_VIOSRP_H -#define PPC_VIOSRP_H - -#define SRP_VERSION "16.a" -#define SRP_MAX_IU_LEN 256 -#define SRP_MAX_LOC_LEN 32 - -union srp_iu { - struct srp_login_req login_req; - struct srp_login_rsp login_rsp; - struct srp_login_rej login_rej; - struct srp_i_logout i_logout; - struct srp_t_logout t_logout; - struct srp_tsk_mgmt tsk_mgmt; - struct srp_cmd cmd; - struct srp_rsp rsp; - uint8_t reserved[SRP_MAX_IU_LEN]; -}; - -enum viosrp_crq_formats { - VIOSRP_SRP_FORMAT = 0x01, - VIOSRP_MAD_FORMAT = 0x02, - VIOSRP_OS400_FORMAT = 0x03, - VIOSRP_AIX_FORMAT = 0x04, - VIOSRP_LINUX_FORMAT = 0x06, - VIOSRP_INLINE_FORMAT = 0x07 -}; - -enum viosrp_crq_status { - VIOSRP_OK = 0x0, - VIOSRP_NONRECOVERABLE_ERR = 0x1, - VIOSRP_VIOLATES_MAX_XFER = 0x2, - VIOSRP_PARTNER_PANIC = 0x3, - VIOSRP_DEVICE_BUSY = 0x8, - VIOSRP_ADAPTER_FAIL = 0x10, - VIOSRP_OK2 = 0x99, -}; - -struct viosrp_crq { - uint8_t valid; /* used by RPA */ - uint8_t format; /* SCSI vs out-of-band */ - uint8_t reserved; - uint8_t status; /* non-scsi failure? (e.g. DMA failure) */ - uint16_t timeout; /* in seconds */ - uint16_t IU_length; /* in bytes */ - uint64_t IU_data_ptr; /* the TCE for transferring data */ -}; - -/* MADs are Management requests above and beyond the IUs defined in the SRP - * standard. - */ -enum viosrp_mad_types { - VIOSRP_EMPTY_IU_TYPE = 0x01, - VIOSRP_ERROR_LOG_TYPE = 0x02, - VIOSRP_ADAPTER_INFO_TYPE = 0x03, - VIOSRP_HOST_CONFIG_TYPE = 0x04, - VIOSRP_CAPABILITIES_TYPE = 0x05, - VIOSRP_ENABLE_FAST_FAIL = 0x08, -}; - -enum viosrp_mad_status { - VIOSRP_MAD_SUCCESS = 0x00, - VIOSRP_MAD_NOT_SUPPORTED = 0xF1, - VIOSRP_MAD_FAILED = 0xF7, -}; - -enum viosrp_capability_type { - MIGRATION_CAPABILITIES = 0x01, - RESERVATION_CAPABILITIES = 0x02, -}; - -enum viosrp_capability_support { - SERVER_DOES_NOT_SUPPORTS_CAP = 0x0, - SERVER_SUPPORTS_CAP = 0x01, - SERVER_CAP_DATA = 0x02, -}; - -enum viosrp_reserve_type { - CLIENT_RESERVE_SCSI_2 = 0x01, -}; - -enum viosrp_capability_flag { - CLIENT_MIGRATED = 0x01, - CLIENT_RECONNECT = 0x02, - CAP_LIST_SUPPORTED = 0x04, - CAP_LIST_DATA = 0x08, -}; - -/* - * Common MAD header - */ -struct mad_common { - uint32_t type; - uint16_t status; - uint16_t length; - uint64_t tag; -}; - -/* - * All SRP (and MAD) requests normally flow from the - * client to the server. There is no way for the server to send - * an asynchronous message back to the client. The Empty IU is used - * to hang out a meaningless request to the server so that it can respond - * asynchrouously with something like a SCSI AER - */ -struct viosrp_empty_iu { - struct mad_common common; - uint64_t buffer; - uint32_t port; -}; - -struct viosrp_error_log { - struct mad_common common; - uint64_t buffer; -}; - -struct viosrp_adapter_info { - struct mad_common common; - uint64_t buffer; -}; - -struct viosrp_host_config { - struct mad_common common; - uint64_t buffer; -}; - -struct viosrp_fast_fail { - struct mad_common common; -}; - -struct viosrp_capabilities { - struct mad_common common; - uint64_t buffer; -}; - -struct mad_capability_common { - uint32_t cap_type; - uint16_t length; - uint16_t server_support; -}; - -struct mad_reserve_cap { - struct mad_capability_common common; - uint32_t type; -}; - -struct mad_migration_cap { - struct mad_capability_common common; - uint32_t ecl; -}; - -struct capabilities { - uint32_t flags; - char name[SRP_MAX_LOC_LEN]; - char loc[SRP_MAX_LOC_LEN]; - struct mad_migration_cap migration; - struct mad_reserve_cap reserve; -}; - -union mad_iu { - struct viosrp_empty_iu empty_iu; - struct viosrp_error_log error_log; - struct viosrp_adapter_info adapter_info; - struct viosrp_host_config host_config; - struct viosrp_fast_fail fast_fail; - struct viosrp_capabilities capabilities; -}; - -union viosrp_iu { - union srp_iu srp; - union mad_iu mad; -}; - -struct mad_adapter_info_data { - char srp_version[8]; - char partition_name[96]; - uint32_t partition_number; - uint32_t mad_version; - uint32_t os_type; - uint32_t port_max_txu[8]; /* per-port maximum transfer */ -}; - -#endif diff --git a/qemu/hw/scsi/virtio-scsi-dataplane.c b/qemu/hw/scsi/virtio-scsi-dataplane.c deleted file mode 100644 index 1a49f1e4b..000000000 --- a/qemu/hw/scsi/virtio-scsi-dataplane.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Virtio SCSI dataplane - * - * Copyright Red Hat, Inc. 2014 - * - * Authors: - * Fam Zheng - * - * 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/virtio/virtio-scsi.h" -#include "qemu/error-report.h" -#include "sysemu/block-backend.h" -#include -#include -#include -#include "hw/virtio/virtio-access.h" - -/* Context: QEMU global mutex held */ -void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread) -{ - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - - assert(!s->ctx); - s->ctx = iothread_get_aio_context(vs->conf.iothread); - - /* Don't try if transport does not support notifiers. */ - if (!k->set_guest_notifiers || !k->set_host_notifier) { - fprintf(stderr, "virtio-scsi: Failed to set iothread " - "(transport does not support notifiers)"); - exit(1); - } -} - -static void virtio_scsi_data_plane_handle_cmd(VirtIODevice *vdev, - VirtQueue *vq) -{ - VirtIOSCSI *s = (VirtIOSCSI *)vdev; - - assert(s->ctx && s->dataplane_started); - virtio_scsi_handle_cmd_vq(s, vq); -} - -static void virtio_scsi_data_plane_handle_ctrl(VirtIODevice *vdev, - VirtQueue *vq) -{ - VirtIOSCSI *s = VIRTIO_SCSI(vdev); - - assert(s->ctx && s->dataplane_started); - virtio_scsi_handle_ctrl_vq(s, vq); -} - -static void virtio_scsi_data_plane_handle_event(VirtIODevice *vdev, - VirtQueue *vq) -{ - VirtIOSCSI *s = VIRTIO_SCSI(vdev); - - assert(s->ctx && s->dataplane_started); - virtio_scsi_handle_event_vq(s, vq); -} - -static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n, - void (*fn)(VirtIODevice *vdev, VirtQueue *vq)) -{ - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - int rc; - - /* 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; - } - - virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, fn); - return 0; -} - -void virtio_scsi_dataplane_notify(VirtIODevice *vdev, VirtIOSCSIReq *req) -{ - if (virtio_should_notify(vdev, req->vq)) { - event_notifier_set(virtio_queue_get_guest_notifier(req->vq)); - } -} - -/* assumes s->ctx held */ -static void virtio_scsi_clear_aio(VirtIOSCSI *s) -{ - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - int i; - - 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); - } -} - -/* Context: QEMU global mutex held */ -void virtio_scsi_dataplane_start(VirtIOSCSI *s) -{ - int i; - int rc; - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - - if (s->dataplane_started || - s->dataplane_starting || - s->dataplane_fenced || - s->ctx != iothread_get_aio_context(vs->conf.iothread)) { - return; - } - - s->dataplane_starting = true; - - /* Set up guest notifier (irq) */ - rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true); - if (rc != 0) { - fprintf(stderr, "virtio-scsi: Failed to set guest notifiers (%d), " - "ensure -enable-kvm is set\n", rc); - goto fail_guest_notifiers; - } - - aio_context_acquire(s->ctx); - rc = virtio_scsi_vring_init(s, vs->ctrl_vq, 0, - virtio_scsi_data_plane_handle_ctrl); - if (rc) { - goto fail_vrings; - } - rc = virtio_scsi_vring_init(s, vs->event_vq, 1, - virtio_scsi_data_plane_handle_event); - if (rc) { - goto fail_vrings; - } - for (i = 0; i < vs->conf.num_queues; i++) { - rc = virtio_scsi_vring_init(s, vs->cmd_vqs[i], i + 2, - virtio_scsi_data_plane_handle_cmd); - if (rc) { - goto fail_vrings; - } - } - - s->dataplane_starting = false; - s->dataplane_started = true; - aio_context_release(s->ctx); - return; - -fail_vrings: - virtio_scsi_clear_aio(s); - aio_context_release(s->ctx); - 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 */ -void virtio_scsi_dataplane_stop(VirtIOSCSI *s) -{ - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - 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; - s->dataplane_started = false; - return; - } - s->dataplane_stopping = true; - assert(s->ctx == iothread_get_aio_context(vs->conf.iothread)); - - aio_context_acquire(s->ctx); - - virtio_scsi_clear_aio(s); - - blk_drain_all(); /* ensure there are no in-flight requests */ - - aio_context_release(s->ctx); - - for (i = 0; i < vs->conf.num_queues + 2; i++) { - k->set_host_notifier(qbus->parent, i, false); - } - - /* Clean up guest notifier (irq) */ - k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); - s->dataplane_stopping = false; - s->dataplane_started = false; -} diff --git a/qemu/hw/scsi/virtio-scsi.c b/qemu/hw/scsi/virtio-scsi.c deleted file mode 100644 index 30415c6a9..000000000 --- a/qemu/hw/scsi/virtio-scsi.c +++ /dev/null @@ -1,1054 +0,0 @@ -/* - * Virtio SCSI HBA - * - * Copyright IBM, Corp. 2010 - * Copyright Red Hat, Inc. 2011 - * - * Authors: - * Stefan Hajnoczi - * Paolo Bonzini - * - * 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 "qapi/error.h" -#include "standard-headers/linux/virtio_ids.h" -#include "hw/virtio/virtio-scsi.h" -#include "qemu/error-report.h" -#include "qemu/iov.h" -#include "sysemu/block-backend.h" -#include -#include -#include -#include "hw/virtio/virtio-access.h" - -static inline int virtio_scsi_get_lun(uint8_t *lun) -{ - return ((lun[2] << 8) | lun[3]) & 0x3FFF; -} - -static inline SCSIDevice *virtio_scsi_device_find(VirtIOSCSI *s, uint8_t *lun) -{ - if (lun[0] != 1) { - return NULL; - } - if (lun[2] != 0 && !(lun[2] >= 0x40 && lun[2] < 0x80)) { - return NULL; - } - return scsi_device_find(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun)); -} - -void virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq, VirtIOSCSIReq *req) -{ - const size_t zero_skip = - offsetof(VirtIOSCSIReq, resp_iov) + sizeof(req->resp_iov); - - 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); -} - -void virtio_scsi_free_req(VirtIOSCSIReq *req) -{ - qemu_iovec_destroy(&req->resp_iov); - qemu_sglist_destroy(&req->qsgl); - g_free(req); -} - -static void virtio_scsi_complete_req(VirtIOSCSIReq *req) -{ - VirtIOSCSI *s = req->dev; - VirtQueue *vq = req->vq; - VirtIODevice *vdev = VIRTIO_DEVICE(s); - - qemu_iovec_from_buf(&req->resp_iov, 0, &req->resp, req->resp_size); - 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 { - virtio_notify(vdev, vq); - } - - if (req->sreq) { - req->sreq->hba_private = NULL; - scsi_req_unref(req->sreq); - } - virtio_scsi_free_req(req); -} - -static void virtio_scsi_bad_req(void) -{ - error_report("wrong size for virtio-scsi headers"); - exit(1); -} - -static size_t qemu_sgl_concat(VirtIOSCSIReq *req, struct iovec *iov, - hwaddr *addr, int num, size_t skip) -{ - QEMUSGList *qsgl = &req->qsgl; - size_t copied = 0; - - while (num) { - if (skip >= iov->iov_len) { - skip -= iov->iov_len; - } else { - qemu_sglist_add(qsgl, *addr + skip, iov->iov_len - skip); - copied += iov->iov_len - skip; - skip = 0; - } - iov++; - addr++; - num--; - } - - assert(skip == 0); - return copied; -} - -static int virtio_scsi_parse_req(VirtIOSCSIReq *req, - unsigned req_size, unsigned resp_size) -{ - VirtIODevice *vdev = (VirtIODevice *) req->dev; - size_t in_size, out_size; - - if (iov_to_buf(req->elem.out_sg, req->elem.out_num, 0, - &req->req, req_size) < req_size) { - return -EINVAL; - } - - if (qemu_iovec_concat_iov(&req->resp_iov, - req->elem.in_sg, req->elem.in_num, 0, - resp_size) < resp_size) { - return -EINVAL; - } - - req->resp_size = resp_size; - - /* Old BIOSes left some padding by mistake after the req_size/resp_size. - * As a workaround, always consider the first buffer as the virtio-scsi - * request/response, making the payload start at the second element - * of the iovec. - * - * The actual length of the response header, stored in req->resp_size, - * does not change. - * - * TODO: always disable this workaround for virtio 1.0 devices. - */ - if (!virtio_vdev_has_feature(vdev, VIRTIO_F_ANY_LAYOUT)) { - if (req->elem.out_num) { - req_size = req->elem.out_sg[0].iov_len; - } - if (req->elem.in_num) { - resp_size = req->elem.in_sg[0].iov_len; - } - } - - out_size = qemu_sgl_concat(req, req->elem.out_sg, - &req->elem.out_addr[0], req->elem.out_num, - req_size); - in_size = qemu_sgl_concat(req, req->elem.in_sg, - &req->elem.in_addr[0], req->elem.in_num, - resp_size); - - if (out_size && in_size) { - return -ENOTSUP; - } - - if (out_size) { - req->mode = SCSI_XFER_TO_DEV; - } else if (in_size) { - req->mode = SCSI_XFER_FROM_DEV; - } - - return 0; -} - -static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq) -{ - 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; -} - -static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq) -{ - VirtIOSCSIReq *req = sreq->hba_private; - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(req->dev); - uint32_t n = virtio_queue_get_id(req->vq) - 2; - - assert(n < vs->conf.num_queues); - qemu_put_be32s(f, &n); - qemu_put_virtqueue_element(f, &req->elem); -} - -static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq) -{ - SCSIBus *bus = sreq->bus; - VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - VirtIOSCSIReq *req; - uint32_t n; - - qemu_get_be32s(f, &n); - assert(n < vs->conf.num_queues); - 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) { - error_report("invalid SCSI request migration data"); - exit(1); - } - - scsi_req_ref(sreq); - req->sreq = sreq; - if (req->sreq->cmd.mode != SCSI_XFER_NONE) { - assert(req->sreq->cmd.mode == req->mode); - } - return req; -} - -typedef struct { - Notifier notifier; - VirtIOSCSIReq *tmf_req; -} VirtIOSCSICancelNotifier; - -static void virtio_scsi_cancel_notify(Notifier *notifier, void *data) -{ - VirtIOSCSICancelNotifier *n = container_of(notifier, - VirtIOSCSICancelNotifier, - notifier); - - if (--n->tmf_req->remaining == 0) { - virtio_scsi_complete_req(n->tmf_req); - } - g_free(n); -} - -/* Return 0 if the request is ready to be completed and return to guest; - * -EINPROGRESS if the request is submitted and will be completed later, in the - * case of async cancellation. */ -static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) -{ - SCSIDevice *d = virtio_scsi_device_find(s, req->req.tmf.lun); - SCSIRequest *r, *next; - BusChild *kid; - int target; - int ret = 0; - - if (s->dataplane_started && d) { - assert(blk_get_aio_context(d->conf.blk) == s->ctx); - } - /* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */ - req->resp.tmf.response = VIRTIO_SCSI_S_OK; - - virtio_tswap32s(VIRTIO_DEVICE(s), &req->req.tmf.subtype); - switch (req->req.tmf.subtype) { - case VIRTIO_SCSI_T_TMF_ABORT_TASK: - case VIRTIO_SCSI_T_TMF_QUERY_TASK: - if (!d) { - goto fail; - } - if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { - goto incorrect_lun; - } - QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { - VirtIOSCSIReq *cmd_req = r->hba_private; - if (cmd_req && cmd_req->req.cmd.tag == req->req.tmf.tag) { - 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->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) { - /* "If the specified command is present in the task set, then - * return a service response set to FUNCTION SUCCEEDED". - */ - req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; - } else { - VirtIOSCSICancelNotifier *notifier; - - req->remaining = 1; - notifier = g_new(VirtIOSCSICancelNotifier, 1); - notifier->tmf_req = req; - notifier->notifier.notify = virtio_scsi_cancel_notify; - scsi_req_cancel_async(r, ¬ifier->notifier); - ret = -EINPROGRESS; - } - } - break; - - case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET: - if (!d) { - goto fail; - } - if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { - goto incorrect_lun; - } - s->resetting++; - qdev_reset_all(&d->qdev); - s->resetting--; - break; - - case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET: - case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET: - case VIRTIO_SCSI_T_TMF_QUERY_TASK_SET: - if (!d) { - goto fail; - } - if (d->lun != virtio_scsi_get_lun(req->req.tmf.lun)) { - goto incorrect_lun; - } - - /* Add 1 to "remaining" until virtio_scsi_do_tmf returns. - * This way, if the bus starts calling back to the notifiers - * even before we finish the loop, virtio_scsi_cancel_notify - * will not complete the TMF too early. - */ - req->remaining = 1; - QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { - if (r->hba_private) { - if (req->req.tmf.subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) { - /* "If there is any command present in the task set, then - * return a service response set to FUNCTION SUCCEEDED". - */ - req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; - break; - } else { - VirtIOSCSICancelNotifier *notifier; - - req->remaining++; - notifier = g_new(VirtIOSCSICancelNotifier, 1); - notifier->notifier.notify = virtio_scsi_cancel_notify; - notifier->tmf_req = req; - scsi_req_cancel_async(r, ¬ifier->notifier); - } - } - } - if (--req->remaining > 0) { - ret = -EINPROGRESS; - } - break; - - case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: - target = req->req.tmf.lun[1]; - s->resetting++; - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - d = SCSI_DEVICE(kid->child); - if (d->channel == 0 && d->id == target) { - qdev_reset_all(&d->qdev); - } - } - s->resetting--; - break; - - case VIRTIO_SCSI_T_TMF_CLEAR_ACA: - default: - req->resp.tmf.response = VIRTIO_SCSI_S_FUNCTION_REJECTED; - break; - } - - return ret; - -incorrect_lun: - req->resp.tmf.response = VIRTIO_SCSI_S_INCORRECT_LUN; - return ret; - -fail: - req->resp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET; - return ret; -} - -static void virtio_scsi_handle_ctrl_req(VirtIOSCSI *s, VirtIOSCSIReq *req) -{ - VirtIODevice *vdev = (VirtIODevice *)s; - uint32_t type; - int r = 0; - - if (iov_to_buf(req->elem.out_sg, req->elem.out_num, 0, - &type, sizeof(type)) < sizeof(type)) { - virtio_scsi_bad_req(); - return; - } - - virtio_tswap32s(vdev, &type); - if (type == VIRTIO_SCSI_T_TMF) { - if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlTMFReq), - sizeof(VirtIOSCSICtrlTMFResp)) < 0) { - virtio_scsi_bad_req(); - } else { - r = virtio_scsi_do_tmf(s, req); - } - - } else if (type == VIRTIO_SCSI_T_AN_QUERY || - type == VIRTIO_SCSI_T_AN_SUBSCRIBE) { - if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlANReq), - sizeof(VirtIOSCSICtrlANResp)) < 0) { - virtio_scsi_bad_req(); - } else { - req->resp.an.event_actual = 0; - req->resp.an.response = VIRTIO_SCSI_S_OK; - } - } - if (r == 0) { - virtio_scsi_complete_req(req); - } else { - assert(r == -EINPROGRESS); - } -} - -void virtio_scsi_handle_ctrl_vq(VirtIOSCSI *s, VirtQueue *vq) -{ - VirtIOSCSIReq *req; - - 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 - * in virtio_scsi_command_complete. - */ - req->resp_size = sizeof(VirtIOSCSICmdResp); - virtio_scsi_complete_req(req); -} - -static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status, - size_t resid) -{ - VirtIOSCSIReq *req = r->hba_private; - uint8_t sense[SCSI_SENSE_BUF_SIZE]; - uint32_t sense_len; - VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); - - if (r->io_canceled) { - return; - } - - req->resp.cmd.response = VIRTIO_SCSI_S_OK; - req->resp.cmd.status = status; - if (req->resp.cmd.status == GOOD) { - req->resp.cmd.resid = virtio_tswap32(vdev, resid); - } else { - req->resp.cmd.resid = 0; - sense_len = scsi_req_get_sense(r, sense, sizeof(sense)); - sense_len = MIN(sense_len, req->resp_iov.size - sizeof(req->resp.cmd)); - qemu_iovec_from_buf(&req->resp_iov, sizeof(req->resp.cmd), - sense, sense_len); - req->resp.cmd.sense_len = virtio_tswap32(vdev, sense_len); - } - virtio_scsi_complete_cmd_req(req); -} - -static int virtio_scsi_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, - uint8_t *buf, void *hba_private) -{ - VirtIOSCSIReq *req = hba_private; - - if (cmd->len == 0) { - cmd->len = MIN(VIRTIO_SCSI_CDB_DEFAULT_SIZE, SCSI_CMD_BUF_SIZE); - memcpy(cmd->buf, buf, cmd->len); - } - - /* Extract the direction and mode directly from the request, for - * host device passthrough. - */ - cmd->xfer = req->qsgl.size; - cmd->mode = req->mode; - return 0; -} - -static QEMUSGList *virtio_scsi_get_sg_list(SCSIRequest *r) -{ - VirtIOSCSIReq *req = r->hba_private; - - return &req->qsgl; -} - -static void virtio_scsi_request_cancelled(SCSIRequest *r) -{ - VirtIOSCSIReq *req = r->hba_private; - - if (!req) { - return; - } - if (req->dev->resetting) { - req->resp.cmd.response = VIRTIO_SCSI_S_RESET; - } else { - req->resp.cmd.response = VIRTIO_SCSI_S_ABORTED; - } - virtio_scsi_complete_cmd_req(req); -} - -static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req) -{ - req->resp.cmd.response = VIRTIO_SCSI_S_FAILURE; - virtio_scsi_complete_cmd_req(req); -} - -static bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) -{ - VirtIOSCSICommon *vs = &s->parent_obj; - SCSIDevice *d; - int rc; - - rc = virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size, - sizeof(VirtIOSCSICmdResp) + vs->sense_size); - if (rc < 0) { - if (rc == -ENOTSUP) { - virtio_scsi_fail_cmd_req(req); - } else { - virtio_scsi_bad_req(); - } - return false; - } - - d = virtio_scsi_device_find(s, req->req.cmd.lun); - if (!d) { - req->resp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET; - virtio_scsi_complete_cmd_req(req); - return false; - } - if (s->dataplane_started) { - assert(blk_get_aio_context(d->conf.blk) == s->ctx); - } - req->sreq = scsi_req_new(d, req->req.cmd.tag, - virtio_scsi_get_lun(req->req.cmd.lun), - req->req.cmd.cdb, req); - - if (req->sreq->cmd.mode != SCSI_XFER_NONE - && (req->sreq->cmd.mode != req->mode || - req->sreq->cmd.xfer > req->qsgl.size)) { - req->resp.cmd.response = VIRTIO_SCSI_S_OVERRUN; - virtio_scsi_complete_cmd_req(req); - return false; - } - scsi_req_ref(req->sreq); - blk_io_plug(d->conf.blk); - return true; -} - -static void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req) -{ - SCSIRequest *sreq = req->sreq; - if (scsi_req_enqueue(sreq)) { - scsi_req_continue(sreq); - } - blk_io_unplug(sreq->dev->conf.blk); - scsi_req_unref(sreq); -} - -void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq) -{ - VirtIOSCSIReq *req, *next; - QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs); - - while ((req = virtio_scsi_pop_req(s, vq))) { - 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); - } -} - -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) -{ - VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config; - VirtIOSCSICommon *s = VIRTIO_SCSI_COMMON(vdev); - - virtio_stl_p(vdev, &scsiconf->num_queues, s->conf.num_queues); - virtio_stl_p(vdev, &scsiconf->seg_max, 128 - 2); - virtio_stl_p(vdev, &scsiconf->max_sectors, s->conf.max_sectors); - virtio_stl_p(vdev, &scsiconf->cmd_per_lun, s->conf.cmd_per_lun); - virtio_stl_p(vdev, &scsiconf->event_info_size, sizeof(VirtIOSCSIEvent)); - virtio_stl_p(vdev, &scsiconf->sense_size, s->sense_size); - virtio_stl_p(vdev, &scsiconf->cdb_size, s->cdb_size); - virtio_stw_p(vdev, &scsiconf->max_channel, VIRTIO_SCSI_MAX_CHANNEL); - virtio_stw_p(vdev, &scsiconf->max_target, VIRTIO_SCSI_MAX_TARGET); - virtio_stl_p(vdev, &scsiconf->max_lun, VIRTIO_SCSI_MAX_LUN); -} - -static void virtio_scsi_set_config(VirtIODevice *vdev, - const uint8_t *config) -{ - VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config; - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); - - if ((uint32_t) virtio_ldl_p(vdev, &scsiconf->sense_size) >= 65536 || - (uint32_t) virtio_ldl_p(vdev, &scsiconf->cdb_size) >= 256) { - error_report("bad data written to virtio-scsi configuration space"); - exit(1); - } - - vs->sense_size = virtio_ldl_p(vdev, &scsiconf->sense_size); - vs->cdb_size = virtio_ldl_p(vdev, &scsiconf->cdb_size); -} - -static uint64_t virtio_scsi_get_features(VirtIODevice *vdev, - uint64_t requested_features, - Error **errp) -{ - VirtIOSCSI *s = VIRTIO_SCSI(vdev); - - /* Firstly sync all virtio-scsi possible supported features */ - requested_features |= s->host_features; - return requested_features; -} - -static void virtio_scsi_reset(VirtIODevice *vdev) -{ - VirtIOSCSI *s = VIRTIO_SCSI(vdev); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); - - if (s->ctx) { - virtio_scsi_dataplane_stop(s); - } - s->resetting++; - qbus_reset_all(&s->bus.qbus); - s->resetting--; - - vs->sense_size = VIRTIO_SCSI_SENSE_DEFAULT_SIZE; - vs->cdb_size = VIRTIO_SCSI_CDB_DEFAULT_SIZE; - s->events_dropped = false; -} - -/* The device does not have anything to save beyond the virtio data. - * Request data is saved with callbacks from SCSI devices. - */ -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); -} - -static int virtio_scsi_load(QEMUFile *f, void *opaque, int version_id) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(opaque); - int ret; - - ret = virtio_load(vdev, f, version_id); - if (ret) { - return ret; - } - return 0; -} - -void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, - uint32_t event, uint32_t reason) -{ - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - VirtIOSCSIReq *req; - VirtIOSCSIEvent *evt; - VirtIODevice *vdev = VIRTIO_DEVICE(s); - - if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { - return; - } - - if (s->dataplane_started) { - assert(s->ctx); - aio_context_acquire(s->ctx); - } - - req = virtio_scsi_pop_req(s, vs->event_vq); - if (!req) { - s->events_dropped = true; - goto out; - } - - if (s->events_dropped) { - event |= VIRTIO_SCSI_T_EVENTS_MISSED; - s->events_dropped = false; - } - - if (virtio_scsi_parse_req(req, 0, sizeof(VirtIOSCSIEvent))) { - virtio_scsi_bad_req(); - } - - evt = &req->resp.event; - memset(evt, 0, sizeof(VirtIOSCSIEvent)); - evt->event = virtio_tswap32(vdev, event); - evt->reason = virtio_tswap32(vdev, reason); - if (!dev) { - assert(event == VIRTIO_SCSI_T_EVENTS_MISSED); - } else { - evt->lun[0] = 1; - evt->lun[1] = dev->id; - - /* Linux wants us to keep the same encoding we use for REPORT LUNS. */ - if (dev->lun >= 256) { - evt->lun[2] = (dev->lun >> 8) | 0x40; - } - evt->lun[3] = dev->lun & 0xFF; - } - virtio_scsi_complete_req(req); -out: - if (s->dataplane_started) { - aio_context_release(s->ctx); - } -} - -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) { - virtio_scsi_dataplane_start(s); - if (!s->dataplane_fenced) { - return; - } - } - virtio_scsi_handle_event_vq(s, vq); -} - -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_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) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); - VirtIOSCSI *s = VIRTIO_SCSI(vdev); - SCSIDevice *sd = SCSI_DEVICE(dev); - - 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; - } - blk_op_block_all(sd->conf.blk, s->blocker); - 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_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { - virtio_scsi_push_event(s, sd, - VIRTIO_SCSI_T_TRANSPORT_RESET, - VIRTIO_SCSI_EVT_RESET_RESCAN); - } -} - -static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); - VirtIOSCSI *s = VIRTIO_SCSI(vdev); - SCSIDevice *sd = SCSI_DEVICE(dev); - VirtIOSCSIBlkChangeNotifier *insert_notifier, *remove_notifier; - - 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); - } - - 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); -} - -static struct SCSIBusInfo virtio_scsi_scsi_info = { - .tcq = true, - .max_channel = VIRTIO_SCSI_MAX_CHANNEL, - .max_target = VIRTIO_SCSI_MAX_TARGET, - .max_lun = VIRTIO_SCSI_MAX_LUN, - - .complete = virtio_scsi_command_complete, - .cancel = virtio_scsi_request_cancelled, - .change = virtio_scsi_change, - .parse_cdb = virtio_scsi_parse_cdb, - .get_sg_list = virtio_scsi_get_sg_list, - .save_request = virtio_scsi_save_request, - .load_request = virtio_scsi_load_request, -}; - -void virtio_scsi_common_realize(DeviceState *dev, Error **errp, - HandleOutput ctrl, HandleOutput evt, - HandleOutput cmd) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIOSCSICommon *s = VIRTIO_SCSI_COMMON(dev); - int i; - - virtio_init(vdev, "virtio-scsi", VIRTIO_ID_SCSI, - sizeof(VirtIOSCSIConfig)); - - if (s->conf.num_queues == 0 || - s->conf.num_queues > VIRTIO_QUEUE_MAX - 2) { - error_setg(errp, "Invalid number of queues (= %" PRIu32 "), " - "must be a positive integer less than %d.", - s->conf.num_queues, VIRTIO_QUEUE_MAX - 2); - virtio_cleanup(vdev); - return; - } - s->cmd_vqs = g_new0(VirtQueue *, s->conf.num_queues); - s->sense_size = VIRTIO_SCSI_SENSE_DEFAULT_SIZE; - s->cdb_size = VIRTIO_SCSI_CDB_DEFAULT_SIZE; - - s->ctrl_vq = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, - ctrl); - s->event_vq = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, - evt); - for (i = 0; i < s->conf.num_queues; i++) { - s->cmd_vqs[i] = virtio_add_queue(vdev, VIRTIO_SCSI_VQ_SIZE, - cmd); - } - - if (s->conf.iothread) { - virtio_scsi_set_iothread(VIRTIO_SCSI(s), s->conf.iothread); - } -} - -static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIOSCSI *s = VIRTIO_SCSI(dev); - static int virtio_scsi_id; - Error *err = NULL; - - virtio_scsi_common_realize(dev, &err, virtio_scsi_handle_ctrl, - virtio_scsi_handle_event, - virtio_scsi_handle_cmd); - if (err != NULL) { - error_propagate(errp, err); - return; - } - - scsi_bus_new(&s->bus, sizeof(s->bus), dev, - &virtio_scsi_scsi_info, vdev->bus_name); - /* override default SCSI bus hotplug-handler, with virtio-scsi's one */ - qbus_set_hotplug_handler(BUS(&s->bus), dev, &error_abort); - - if (!dev->hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus, &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - } - - register_savevm(dev, "virtio-scsi", virtio_scsi_id++, 1, - virtio_scsi_save, virtio_scsi_load, s); - - 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) -{ - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(obj); - - object_property_add_link(obj, "iothread", TYPE_IOTHREAD, - (Object **)&vs->conf.iothread, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); -} - -void virtio_scsi_common_unrealize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); - - g_free(vs->cmd_vqs); - virtio_cleanup(vdev); -} - -static void virtio_scsi_device_unrealize(DeviceState *dev, Error **errp) -{ - VirtIOSCSI *s = VIRTIO_SCSI(dev); - - error_free(s->blocker); - - unregister_savevm(dev, "virtio-scsi", s); - virtio_scsi_common_unrealize(dev, errp); -} - -static Property virtio_scsi_properties[] = { - DEFINE_PROP_UINT32("num_queues", VirtIOSCSI, parent_obj.conf.num_queues, 1), - DEFINE_PROP_UINT32("max_sectors", VirtIOSCSI, parent_obj.conf.max_sectors, - 0xFFFF), - DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSI, parent_obj.conf.cmd_per_lun, - 128), - DEFINE_PROP_BIT("hotplug", VirtIOSCSI, host_features, - VIRTIO_SCSI_F_HOTPLUG, true), - DEFINE_PROP_BIT("param_change", VirtIOSCSI, host_features, - VIRTIO_SCSI_F_CHANGE, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_scsi_common_class_init(ObjectClass *klass, void *data) -{ - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - vdc->get_config = virtio_scsi_get_config; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static void virtio_scsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - - dc->props = virtio_scsi_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - vdc->realize = virtio_scsi_device_realize; - vdc->unrealize = virtio_scsi_device_unrealize; - vdc->set_config = virtio_scsi_set_config; - vdc->get_features = virtio_scsi_get_features; - vdc->reset = virtio_scsi_reset; - hc->plug = virtio_scsi_hotplug; - hc->unplug = virtio_scsi_hotunplug; -} - -static const TypeInfo virtio_scsi_common_info = { - .name = TYPE_VIRTIO_SCSI_COMMON, - .parent = TYPE_VIRTIO_DEVICE, - .instance_size = sizeof(VirtIOSCSICommon), - .abstract = true, - .class_init = virtio_scsi_common_class_init, -}; - -static const TypeInfo virtio_scsi_info = { - .name = TYPE_VIRTIO_SCSI, - .parent = TYPE_VIRTIO_SCSI_COMMON, - .instance_size = sizeof(VirtIOSCSI), - .instance_init = virtio_scsi_instance_init, - .class_init = virtio_scsi_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - -static void virtio_register_types(void) -{ - type_register_static(&virtio_scsi_common_info); - type_register_static(&virtio_scsi_info); -} - -type_init(virtio_register_types) diff --git a/qemu/hw/scsi/vmw_pvscsi.c b/qemu/hw/scsi/vmw_pvscsi.c deleted file mode 100644 index e690b4ec0..000000000 --- a/qemu/hw/scsi/vmw_pvscsi.c +++ /dev/null @@ -1,1305 +0,0 @@ -/* - * QEMU VMWARE PVSCSI paravirtual SCSI bus - * - * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) - * - * Developed by Daynix Computing LTD (http://www.daynix.com) - * - * Based on implementation by Paolo Bonzini - * http://lists.gnu.org/archive/html/qemu-devel/2011-08/msg00729.html - * - * Authors: - * Paolo Bonzini - * Dmitry Fleytman - * Yan Vugenfirer - * - * This work is licensed under the terms of the GNU GPL, version 2. - * See the COPYING file in the top-level directory. - * - * NOTE about MSI-X: - * MSI-X support has been removed for the moment because it leads Windows OS - * to crash on startup. The crash happens because Windows driver requires - * MSI-X shared memory to be part of the same BAR used for rings state - * registers, etc. This is not supported by QEMU infrastructure so separate - * BAR created from MSI-X purposes. Windows driver fails to deal with 2 BARs. - * - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/scsi/scsi.h" -#include -#include "hw/pci/msi.h" -#include "vmw_pvscsi.h" -#include "trace.h" - - -#define PVSCSI_USE_64BIT (true) -#define PVSCSI_PER_VECTOR_MASK (false) - -#define PVSCSI_MAX_DEVS (64) -#define PVSCSI_MSIX_NUM_VECTORS (1) - -#define PVSCSI_MAX_CMD_DATA_WORDS \ - (sizeof(PVSCSICmdDescSetupRings)/sizeof(uint32_t)) - -#define RS_GET_FIELD(m, field) \ - (ldl_le_pci_dma(&container_of(m, PVSCSIState, rings)->parent_obj, \ - (m)->rs_pa + offsetof(struct PVSCSIRingsState, field))) -#define RS_SET_FIELD(m, field, val) \ - (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; - uint32_t rxr_len_mask; - uint32_t msg_len_mask; - uint64_t req_ring_pages_pa[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; - uint64_t cmp_ring_pages_pa[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; - uint64_t msg_ring_pages_pa[PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES]; - uint64_t consumed_ptr; - uint64_t filled_cmp_ptr; - uint64_t filled_msg_ptr; -} PVSCSIRingInfo; - -typedef struct PVSCSISGState { - hwaddr elemAddr; - hwaddr dataAddr; - uint32_t resid; -} PVSCSISGState; - -typedef QTAILQ_HEAD(, PVSCSIRequest) PVSCSIRequestList; - -typedef struct { - PCIDevice parent_obj; - MemoryRegion io_space; - SCSIBus bus; - QEMUBH *completion_worker; - PVSCSIRequestList pending_queue; - PVSCSIRequestList completion_queue; - - uint64_t reg_interrupt_status; /* Interrupt status register value */ - uint64_t reg_interrupt_enabled; /* Interrupt mask register value */ - uint64_t reg_command_status; /* Command status register value */ - - /* Command data adoption mechanism */ - uint64_t curr_cmd; /* Last command arrived */ - uint32_t curr_cmd_data_cntr; /* Amount of data for last command */ - - /* Collector for current command data */ - uint32_t curr_cmd_data[PVSCSI_MAX_CMD_DATA_WORDS]; - - uint8_t rings_info_valid; /* Whether data rings initialized */ - uint8_t msg_ring_info_valid; /* Whether message ring initialized */ - uint8_t use_msg; /* Whether to use message ring */ - - uint8_t msi_used; /* Whether MSI support was installed successfully */ - - PVSCSIRingInfo rings; /* Data transfer rings manager */ - uint32_t resetting; /* Reset in progress */ - - uint32_t compat_flags; -} PVSCSIState; - -typedef struct PVSCSIRequest { - SCSIRequest *sreq; - PVSCSIState *dev; - uint8_t sense_key; - uint8_t completed; - int lun; - QEMUSGList sgl; - PVSCSISGState sg; - struct PVSCSIRingReqDesc req; - struct PVSCSIRingCmpDesc cmp; - QTAILQ_ENTRY(PVSCSIRequest) next; -} PVSCSIRequest; - -/* Integer binary logarithm */ -static int -pvscsi_log2(uint32_t input) -{ - int log = 0; - assert(input > 0); - while (input >> ++log) { - } - return log; -} - -static void -pvscsi_ring_init_data(PVSCSIRingInfo *m, PVSCSICmdDescSetupRings *ri) -{ - int i; - uint32_t txr_len_log2, rxr_len_log2; - uint32_t req_ring_size, cmp_ring_size; - m->rs_pa = ri->ringsStatePPN << VMW_PAGE_SHIFT; - - req_ring_size = ri->reqRingNumPages * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE; - cmp_ring_size = ri->cmpRingNumPages * PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE; - txr_len_log2 = pvscsi_log2(req_ring_size - 1); - rxr_len_log2 = pvscsi_log2(cmp_ring_size - 1); - - m->txr_len_mask = MASK(txr_len_log2); - m->rxr_len_mask = MASK(rxr_len_log2); - - m->consumed_ptr = 0; - m->filled_cmp_ptr = 0; - - for (i = 0; i < ri->reqRingNumPages; i++) { - m->req_ring_pages_pa[i] = ri->reqRingPPNs[i] << VMW_PAGE_SHIFT; - } - - for (i = 0; i < ri->cmpRingNumPages; i++) { - m->cmp_ring_pages_pa[i] = ri->cmpRingPPNs[i] << VMW_PAGE_SHIFT; - } - - RS_SET_FIELD(m, reqProdIdx, 0); - RS_SET_FIELD(m, reqConsIdx, 0); - RS_SET_FIELD(m, reqNumEntriesLog2, txr_len_log2); - - RS_SET_FIELD(m, cmpProdIdx, 0); - RS_SET_FIELD(m, cmpConsIdx, 0); - RS_SET_FIELD(m, cmpNumEntriesLog2, rxr_len_log2); - - trace_pvscsi_ring_init_data(txr_len_log2, rxr_len_log2); - - /* Flush ring state page changes */ - smp_wmb(); -} - -static void -pvscsi_ring_init_msg(PVSCSIRingInfo *m, PVSCSICmdDescSetupMsgRing *ri) -{ - int i; - uint32_t len_log2; - uint32_t ring_size; - - ring_size = ri->numPages * PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE; - len_log2 = pvscsi_log2(ring_size - 1); - - m->msg_len_mask = MASK(len_log2); - - m->filled_msg_ptr = 0; - - for (i = 0; i < ri->numPages; i++) { - m->msg_ring_pages_pa[i] = ri->ringPPNs[i] << VMW_PAGE_SHIFT; - } - - RS_SET_FIELD(m, msgProdIdx, 0); - RS_SET_FIELD(m, msgConsIdx, 0); - RS_SET_FIELD(m, msgNumEntriesLog2, len_log2); - - trace_pvscsi_ring_init_msg(len_log2); - - /* Flush ring state page changes */ - smp_wmb(); -} - -static void -pvscsi_ring_cleanup(PVSCSIRingInfo *mgr) -{ - mgr->rs_pa = 0; - mgr->txr_len_mask = 0; - mgr->rxr_len_mask = 0; - mgr->msg_len_mask = 0; - mgr->consumed_ptr = 0; - mgr->filled_cmp_ptr = 0; - mgr->filled_msg_ptr = 0; - memset(mgr->req_ring_pages_pa, 0, sizeof(mgr->req_ring_pages_pa)); - memset(mgr->cmp_ring_pages_pa, 0, sizeof(mgr->cmp_ring_pages_pa)); - memset(mgr->msg_ring_pages_pa, 0, sizeof(mgr->msg_ring_pages_pa)); -} - -static hwaddr -pvscsi_ring_pop_req_descr(PVSCSIRingInfo *mgr) -{ - uint32_t ready_ptr = RS_GET_FIELD(mgr, reqProdIdx); - - if (ready_ptr != mgr->consumed_ptr) { - uint32_t next_ready_ptr = - mgr->consumed_ptr++ & mgr->txr_len_mask; - uint32_t next_ready_page = - next_ready_ptr / PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE; - uint32_t inpage_idx = - next_ready_ptr % PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE; - - return mgr->req_ring_pages_pa[next_ready_page] + - inpage_idx * sizeof(PVSCSIRingReqDesc); - } else { - return 0; - } -} - -static void -pvscsi_ring_flush_req(PVSCSIRingInfo *mgr) -{ - RS_SET_FIELD(mgr, reqConsIdx, mgr->consumed_ptr); -} - -static hwaddr -pvscsi_ring_pop_cmp_descr(PVSCSIRingInfo *mgr) -{ - /* - * According to Linux driver code it explicitly verifies that number - * of requests being processed by device is less then the size of - * completion queue, so device may omit completion queue overflow - * conditions check. We assume that this is true for other (Windows) - * drivers as well. - */ - - uint32_t free_cmp_ptr = - mgr->filled_cmp_ptr++ & mgr->rxr_len_mask; - uint32_t free_cmp_page = - free_cmp_ptr / PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE; - uint32_t inpage_idx = - free_cmp_ptr % PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE; - return mgr->cmp_ring_pages_pa[free_cmp_page] + - inpage_idx * sizeof(PVSCSIRingCmpDesc); -} - -static hwaddr -pvscsi_ring_pop_msg_descr(PVSCSIRingInfo *mgr) -{ - uint32_t free_msg_ptr = - mgr->filled_msg_ptr++ & mgr->msg_len_mask; - uint32_t free_msg_page = - free_msg_ptr / PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE; - uint32_t inpage_idx = - free_msg_ptr % PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE; - return mgr->msg_ring_pages_pa[free_msg_page] + - inpage_idx * sizeof(PVSCSIRingMsgDesc); -} - -static void -pvscsi_ring_flush_cmp(PVSCSIRingInfo *mgr) -{ - /* Flush descriptor changes */ - smp_wmb(); - - trace_pvscsi_ring_flush_cmp(mgr->filled_cmp_ptr); - - RS_SET_FIELD(mgr, cmpProdIdx, mgr->filled_cmp_ptr); -} - -static bool -pvscsi_ring_msg_has_room(PVSCSIRingInfo *mgr) -{ - uint32_t prodIdx = RS_GET_FIELD(mgr, msgProdIdx); - uint32_t consIdx = RS_GET_FIELD(mgr, msgConsIdx); - - return (prodIdx - consIdx) < (mgr->msg_len_mask + 1); -} - -static void -pvscsi_ring_flush_msg(PVSCSIRingInfo *mgr) -{ - /* Flush descriptor changes */ - smp_wmb(); - - trace_pvscsi_ring_flush_msg(mgr->filled_msg_ptr); - - RS_SET_FIELD(mgr, msgProdIdx, mgr->filled_msg_ptr); -} - -static void -pvscsi_reset_state(PVSCSIState *s) -{ - s->curr_cmd = PVSCSI_CMD_FIRST; - s->curr_cmd_data_cntr = 0; - s->reg_command_status = PVSCSI_COMMAND_PROCESSING_SUCCEEDED; - s->reg_interrupt_status = 0; - pvscsi_ring_cleanup(&s->rings); - s->rings_info_valid = FALSE; - s->msg_ring_info_valid = FALSE; - QTAILQ_INIT(&s->pending_queue); - QTAILQ_INIT(&s->completion_queue); -} - -static void -pvscsi_update_irq_status(PVSCSIState *s) -{ - PCIDevice *d = PCI_DEVICE(s); - bool should_raise = s->reg_interrupt_enabled & s->reg_interrupt_status; - - trace_pvscsi_update_irq_level(should_raise, s->reg_interrupt_enabled, - s->reg_interrupt_status); - - if (s->msi_used && msi_enabled(d)) { - if (should_raise) { - trace_pvscsi_update_irq_msi(); - msi_notify(d, PVSCSI_VECTOR_COMPLETION); - } - return; - } - - pci_set_irq(d, !!should_raise); -} - -static void -pvscsi_raise_completion_interrupt(PVSCSIState *s) -{ - s->reg_interrupt_status |= PVSCSI_INTR_CMPL_0; - - /* Memory barrier to flush interrupt status register changes*/ - smp_wmb(); - - pvscsi_update_irq_status(s); -} - -static void -pvscsi_raise_message_interrupt(PVSCSIState *s) -{ - s->reg_interrupt_status |= PVSCSI_INTR_MSG_0; - - /* Memory barrier to flush interrupt status register changes*/ - smp_wmb(); - - pvscsi_update_irq_status(s); -} - -static void -pvscsi_cmp_ring_put(PVSCSIState *s, struct PVSCSIRingCmpDesc *cmp_desc) -{ - hwaddr cmp_descr_pa; - - cmp_descr_pa = pvscsi_ring_pop_cmp_descr(&s->rings); - trace_pvscsi_cmp_ring_put(cmp_descr_pa); - cpu_physical_memory_write(cmp_descr_pa, (void *)cmp_desc, - sizeof(*cmp_desc)); -} - -static void -pvscsi_msg_ring_put(PVSCSIState *s, struct PVSCSIRingMsgDesc *msg_desc) -{ - hwaddr msg_descr_pa; - - msg_descr_pa = pvscsi_ring_pop_msg_descr(&s->rings); - trace_pvscsi_msg_ring_put(msg_descr_pa); - cpu_physical_memory_write(msg_descr_pa, (void *)msg_desc, - sizeof(*msg_desc)); -} - -static void -pvscsi_process_completion_queue(void *opaque) -{ - PVSCSIState *s = opaque; - PVSCSIRequest *pvscsi_req; - bool has_completed = false; - - while (!QTAILQ_EMPTY(&s->completion_queue)) { - pvscsi_req = QTAILQ_FIRST(&s->completion_queue); - QTAILQ_REMOVE(&s->completion_queue, pvscsi_req, next); - pvscsi_cmp_ring_put(s, &pvscsi_req->cmp); - g_free(pvscsi_req); - has_completed = true; - } - - if (has_completed) { - pvscsi_ring_flush_cmp(&s->rings); - pvscsi_raise_completion_interrupt(s); - } -} - -static void -pvscsi_reset_adapter(PVSCSIState *s) -{ - s->resetting++; - qbus_reset_all_fn(&s->bus); - s->resetting--; - pvscsi_process_completion_queue(s); - assert(QTAILQ_EMPTY(&s->pending_queue)); - pvscsi_reset_state(s); -} - -static void -pvscsi_schedule_completion_processing(PVSCSIState *s) -{ - /* Try putting more complete requests on the ring. */ - if (!QTAILQ_EMPTY(&s->completion_queue)) { - qemu_bh_schedule(s->completion_worker); - } -} - -static void -pvscsi_complete_request(PVSCSIState *s, PVSCSIRequest *r) -{ - assert(!r->completed); - - trace_pvscsi_complete_request(r->cmp.context, r->cmp.dataLen, - r->sense_key); - if (r->sreq != NULL) { - scsi_req_unref(r->sreq); - r->sreq = NULL; - } - r->completed = 1; - QTAILQ_REMOVE(&s->pending_queue, r, next); - QTAILQ_INSERT_TAIL(&s->completion_queue, r, next); - pvscsi_schedule_completion_processing(s); -} - -static QEMUSGList *pvscsi_get_sg_list(SCSIRequest *r) -{ - PVSCSIRequest *req = r->hba_private; - - trace_pvscsi_get_sg_list(req->sgl.nsg, req->sgl.size); - - return &req->sgl; -} - -static void -pvscsi_get_next_sg_elem(PVSCSISGState *sg) -{ - struct PVSCSISGElement elem; - - cpu_physical_memory_read(sg->elemAddr, (void *)&elem, sizeof(elem)); - if ((elem.flags & ~PVSCSI_KNOWN_FLAGS) != 0) { - /* - * There is PVSCSI_SGE_FLAG_CHAIN_ELEMENT flag described in - * header file but its value is unknown. This flag requires - * additional processing, so we put warning here to catch it - * some day and make proper implementation - */ - trace_pvscsi_get_next_sg_elem(elem.flags); - } - - sg->elemAddr += sizeof(elem); - sg->dataAddr = elem.addr; - sg->resid = elem.length; -} - -static void -pvscsi_write_sense(PVSCSIRequest *r, uint8_t *sense, int len) -{ - r->cmp.senseLen = MIN(r->req.senseLen, len); - r->sense_key = sense[(sense[0] & 2) ? 1 : 2]; - cpu_physical_memory_write(r->req.senseAddr, sense, r->cmp.senseLen); -} - -static void -pvscsi_command_complete(SCSIRequest *req, uint32_t status, size_t resid) -{ - PVSCSIRequest *pvscsi_req = req->hba_private; - PVSCSIState *s; - - if (!pvscsi_req) { - trace_pvscsi_command_complete_not_found(req->tag); - return; - } - s = pvscsi_req->dev; - - if (resid) { - /* Short transfer. */ - trace_pvscsi_command_complete_data_run(); - pvscsi_req->cmp.hostStatus = BTSTAT_DATARUN; - } - - pvscsi_req->cmp.scsiStatus = status; - if (pvscsi_req->cmp.scsiStatus == CHECK_CONDITION) { - uint8_t sense[SCSI_SENSE_BUF_SIZE]; - int sense_len = - scsi_req_get_sense(pvscsi_req->sreq, sense, sizeof(sense)); - - trace_pvscsi_command_complete_sense_len(sense_len); - pvscsi_write_sense(pvscsi_req, sense, sense_len); - } - qemu_sglist_destroy(&pvscsi_req->sgl); - pvscsi_complete_request(s, pvscsi_req); -} - -static void -pvscsi_send_msg(PVSCSIState *s, SCSIDevice *dev, uint32_t msg_type) -{ - if (s->msg_ring_info_valid && pvscsi_ring_msg_has_room(&s->rings)) { - PVSCSIMsgDescDevStatusChanged msg = {0}; - - msg.type = msg_type; - msg.bus = dev->channel; - msg.target = dev->id; - msg.lun[1] = dev->lun; - - pvscsi_msg_ring_put(s, (PVSCSIRingMsgDesc *)&msg); - pvscsi_ring_flush_msg(&s->rings); - pvscsi_raise_message_interrupt(s); - } -} - -static void -pvscsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) -{ - PVSCSIState *s = PVSCSI(hotplug_dev); - - pvscsi_send_msg(s, SCSI_DEVICE(dev), PVSCSI_MSG_DEV_ADDED); -} - -static void -pvscsi_hot_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) -{ - PVSCSIState *s = PVSCSI(hotplug_dev); - - pvscsi_send_msg(s, SCSI_DEVICE(dev), PVSCSI_MSG_DEV_REMOVED); - qdev_simple_device_unplug_cb(hotplug_dev, dev, errp); -} - -static void -pvscsi_request_cancelled(SCSIRequest *req) -{ - PVSCSIRequest *pvscsi_req = req->hba_private; - PVSCSIState *s = pvscsi_req->dev; - - if (pvscsi_req->completed) { - return; - } - - if (pvscsi_req->dev->resetting) { - pvscsi_req->cmp.hostStatus = BTSTAT_BUSRESET; - } else { - pvscsi_req->cmp.hostStatus = BTSTAT_ABORTQUEUE; - } - - pvscsi_complete_request(s, pvscsi_req); -} - -static SCSIDevice* -pvscsi_device_find(PVSCSIState *s, int channel, int target, - uint8_t *requested_lun, uint8_t *target_lun) -{ - if (requested_lun[0] || requested_lun[2] || requested_lun[3] || - requested_lun[4] || requested_lun[5] || requested_lun[6] || - requested_lun[7] || (target > PVSCSI_MAX_DEVS)) { - return NULL; - } else { - *target_lun = requested_lun[1]; - return scsi_device_find(&s->bus, channel, target, *target_lun); - } -} - -static PVSCSIRequest * -pvscsi_queue_pending_descriptor(PVSCSIState *s, SCSIDevice **d, - struct PVSCSIRingReqDesc *descr) -{ - PVSCSIRequest *pvscsi_req; - uint8_t lun; - - pvscsi_req = g_malloc0(sizeof(*pvscsi_req)); - pvscsi_req->dev = s; - pvscsi_req->req = *descr; - pvscsi_req->cmp.context = pvscsi_req->req.context; - QTAILQ_INSERT_TAIL(&s->pending_queue, pvscsi_req, next); - - *d = pvscsi_device_find(s, descr->bus, descr->target, descr->lun, &lun); - if (*d) { - pvscsi_req->lun = lun; - } - - return pvscsi_req; -} - -static void -pvscsi_convert_sglist(PVSCSIRequest *r) -{ - int chunk_size; - uint64_t data_length = r->req.dataLen; - PVSCSISGState sg = r->sg; - while (data_length) { - while (!sg.resid) { - pvscsi_get_next_sg_elem(&sg); - trace_pvscsi_convert_sglist(r->req.context, r->sg.dataAddr, - r->sg.resid); - } - assert(data_length > 0); - chunk_size = MIN((unsigned) data_length, sg.resid); - if (chunk_size) { - qemu_sglist_add(&r->sgl, sg.dataAddr, chunk_size); - } - - sg.dataAddr += chunk_size; - data_length -= chunk_size; - sg.resid -= chunk_size; - } -} - -static void -pvscsi_build_sglist(PVSCSIState *s, PVSCSIRequest *r) -{ - PCIDevice *d = PCI_DEVICE(s); - - pci_dma_sglist_init(&r->sgl, d, 1); - if (r->req.flags & PVSCSI_FLAG_CMD_WITH_SG_LIST) { - pvscsi_convert_sglist(r); - } else { - qemu_sglist_add(&r->sgl, r->req.dataAddr, r->req.dataLen); - } -} - -static void -pvscsi_process_request_descriptor(PVSCSIState *s, - struct PVSCSIRingReqDesc *descr) -{ - SCSIDevice *d; - PVSCSIRequest *r = pvscsi_queue_pending_descriptor(s, &d, descr); - int64_t n; - - trace_pvscsi_process_req_descr(descr->cdb[0], descr->context); - - if (!d) { - r->cmp.hostStatus = BTSTAT_SELTIMEO; - trace_pvscsi_process_req_descr_unknown_device(); - pvscsi_complete_request(s, r); - return; - } - - if (descr->flags & PVSCSI_FLAG_CMD_WITH_SG_LIST) { - r->sg.elemAddr = descr->dataAddr; - } - - r->sreq = scsi_req_new(d, descr->context, r->lun, descr->cdb, r); - if (r->sreq->cmd.mode == SCSI_XFER_FROM_DEV && - (descr->flags & PVSCSI_FLAG_CMD_DIR_TODEVICE)) { - r->cmp.hostStatus = BTSTAT_BADMSG; - trace_pvscsi_process_req_descr_invalid_dir(); - scsi_req_cancel(r->sreq); - return; - } - if (r->sreq->cmd.mode == SCSI_XFER_TO_DEV && - (descr->flags & PVSCSI_FLAG_CMD_DIR_TOHOST)) { - r->cmp.hostStatus = BTSTAT_BADMSG; - trace_pvscsi_process_req_descr_invalid_dir(); - scsi_req_cancel(r->sreq); - return; - } - - pvscsi_build_sglist(s, r); - n = scsi_req_enqueue(r->sreq); - - if (n) { - scsi_req_continue(r->sreq); - } -} - -static void -pvscsi_process_io(PVSCSIState *s) -{ - PVSCSIRingReqDesc descr; - hwaddr next_descr_pa; - - assert(s->rings_info_valid); - while ((next_descr_pa = pvscsi_ring_pop_req_descr(&s->rings)) != 0) { - - /* Only read after production index verification */ - smp_rmb(); - - trace_pvscsi_process_io(next_descr_pa); - cpu_physical_memory_read(next_descr_pa, &descr, sizeof(descr)); - pvscsi_process_request_descriptor(s, &descr); - } - - pvscsi_ring_flush_req(&s->rings); -} - -static void -pvscsi_dbg_dump_tx_rings_config(PVSCSICmdDescSetupRings *rc) -{ - int i; - trace_pvscsi_tx_rings_ppn("Rings State", rc->ringsStatePPN); - - trace_pvscsi_tx_rings_num_pages("Request Ring", rc->reqRingNumPages); - for (i = 0; i < rc->reqRingNumPages; i++) { - trace_pvscsi_tx_rings_ppn("Request Ring", rc->reqRingPPNs[i]); - } - - trace_pvscsi_tx_rings_num_pages("Confirm Ring", rc->cmpRingNumPages); - for (i = 0; i < rc->cmpRingNumPages; i++) { - trace_pvscsi_tx_rings_ppn("Confirm Ring", rc->reqRingPPNs[i]); - } -} - -static uint64_t -pvscsi_on_cmd_config(PVSCSIState *s) -{ - trace_pvscsi_on_cmd_noimpl("PVSCSI_CMD_CONFIG"); - return PVSCSI_COMMAND_PROCESSING_FAILED; -} - -static uint64_t -pvscsi_on_cmd_unplug(PVSCSIState *s) -{ - trace_pvscsi_on_cmd_noimpl("PVSCSI_CMD_DEVICE_UNPLUG"); - return PVSCSI_COMMAND_PROCESSING_FAILED; -} - -static uint64_t -pvscsi_on_issue_scsi(PVSCSIState *s) -{ - trace_pvscsi_on_cmd_noimpl("PVSCSI_CMD_ISSUE_SCSI"); - return PVSCSI_COMMAND_PROCESSING_FAILED; -} - -static uint64_t -pvscsi_on_cmd_setup_rings(PVSCSIState *s) -{ - PVSCSICmdDescSetupRings *rc = - (PVSCSICmdDescSetupRings *) s->curr_cmd_data; - - trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_SETUP_RINGS"); - - pvscsi_dbg_dump_tx_rings_config(rc); - pvscsi_ring_init_data(&s->rings, rc); - s->rings_info_valid = TRUE; - return PVSCSI_COMMAND_PROCESSING_SUCCEEDED; -} - -static uint64_t -pvscsi_on_cmd_abort(PVSCSIState *s) -{ - PVSCSICmdDescAbortCmd *cmd = (PVSCSICmdDescAbortCmd *) s->curr_cmd_data; - PVSCSIRequest *r, *next; - - trace_pvscsi_on_cmd_abort(cmd->context, cmd->target); - - QTAILQ_FOREACH_SAFE(r, &s->pending_queue, next, next) { - if (r->req.context == cmd->context) { - break; - } - } - if (r) { - assert(!r->completed); - r->cmp.hostStatus = BTSTAT_ABORTQUEUE; - scsi_req_cancel(r->sreq); - } - - return PVSCSI_COMMAND_PROCESSING_SUCCEEDED; -} - -static uint64_t -pvscsi_on_cmd_unknown(PVSCSIState *s) -{ - trace_pvscsi_on_cmd_unknown_data(s->curr_cmd_data[0]); - return PVSCSI_COMMAND_PROCESSING_FAILED; -} - -static uint64_t -pvscsi_on_cmd_reset_device(PVSCSIState *s) -{ - uint8_t target_lun = 0; - struct PVSCSICmdDescResetDevice *cmd = - (struct PVSCSICmdDescResetDevice *) s->curr_cmd_data; - SCSIDevice *sdev; - - sdev = pvscsi_device_find(s, 0, cmd->target, cmd->lun, &target_lun); - - trace_pvscsi_on_cmd_reset_dev(cmd->target, (int) target_lun, sdev); - - if (sdev != NULL) { - s->resetting++; - device_reset(&sdev->qdev); - s->resetting--; - return PVSCSI_COMMAND_PROCESSING_SUCCEEDED; - } - - return PVSCSI_COMMAND_PROCESSING_FAILED; -} - -static uint64_t -pvscsi_on_cmd_reset_bus(PVSCSIState *s) -{ - trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_RESET_BUS"); - - s->resetting++; - qbus_reset_all_fn(&s->bus); - s->resetting--; - return PVSCSI_COMMAND_PROCESSING_SUCCEEDED; -} - -static uint64_t -pvscsi_on_cmd_setup_msg_ring(PVSCSIState *s) -{ - PVSCSICmdDescSetupMsgRing *rc = - (PVSCSICmdDescSetupMsgRing *) s->curr_cmd_data; - - trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_SETUP_MSG_RING"); - - if (!s->use_msg) { - return PVSCSI_COMMAND_PROCESSING_FAILED; - } - - if (s->rings_info_valid) { - pvscsi_ring_init_msg(&s->rings, rc); - s->msg_ring_info_valid = TRUE; - } - return sizeof(PVSCSICmdDescSetupMsgRing) / sizeof(uint32_t); -} - -static uint64_t -pvscsi_on_cmd_adapter_reset(PVSCSIState *s) -{ - trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_ADAPTER_RESET"); - - pvscsi_reset_adapter(s); - return PVSCSI_COMMAND_PROCESSING_SUCCEEDED; -} - -static const struct { - int data_size; - uint64_t (*handler_fn)(PVSCSIState *s); -} pvscsi_commands[] = { - [PVSCSI_CMD_FIRST] = { - .data_size = 0, - .handler_fn = pvscsi_on_cmd_unknown, - }, - - /* Not implemented, data size defined based on what arrives on windows */ - [PVSCSI_CMD_CONFIG] = { - .data_size = 6 * sizeof(uint32_t), - .handler_fn = pvscsi_on_cmd_config, - }, - - /* Command not implemented, data size is unknown */ - [PVSCSI_CMD_ISSUE_SCSI] = { - .data_size = 0, - .handler_fn = pvscsi_on_issue_scsi, - }, - - /* Command not implemented, data size is unknown */ - [PVSCSI_CMD_DEVICE_UNPLUG] = { - .data_size = 0, - .handler_fn = pvscsi_on_cmd_unplug, - }, - - [PVSCSI_CMD_SETUP_RINGS] = { - .data_size = sizeof(PVSCSICmdDescSetupRings), - .handler_fn = pvscsi_on_cmd_setup_rings, - }, - - [PVSCSI_CMD_RESET_DEVICE] = { - .data_size = sizeof(struct PVSCSICmdDescResetDevice), - .handler_fn = pvscsi_on_cmd_reset_device, - }, - - [PVSCSI_CMD_RESET_BUS] = { - .data_size = 0, - .handler_fn = pvscsi_on_cmd_reset_bus, - }, - - [PVSCSI_CMD_SETUP_MSG_RING] = { - .data_size = sizeof(PVSCSICmdDescSetupMsgRing), - .handler_fn = pvscsi_on_cmd_setup_msg_ring, - }, - - [PVSCSI_CMD_ADAPTER_RESET] = { - .data_size = 0, - .handler_fn = pvscsi_on_cmd_adapter_reset, - }, - - [PVSCSI_CMD_ABORT_CMD] = { - .data_size = sizeof(struct PVSCSICmdDescAbortCmd), - .handler_fn = pvscsi_on_cmd_abort, - }, -}; - -static void -pvscsi_do_command_processing(PVSCSIState *s) -{ - size_t bytes_arrived = s->curr_cmd_data_cntr * sizeof(uint32_t); - - assert(s->curr_cmd < PVSCSI_CMD_LAST); - if (bytes_arrived >= pvscsi_commands[s->curr_cmd].data_size) { - s->reg_command_status = pvscsi_commands[s->curr_cmd].handler_fn(s); - s->curr_cmd = PVSCSI_CMD_FIRST; - s->curr_cmd_data_cntr = 0; - } -} - -static void -pvscsi_on_command_data(PVSCSIState *s, uint32_t value) -{ - size_t bytes_arrived = s->curr_cmd_data_cntr * sizeof(uint32_t); - - assert(bytes_arrived < sizeof(s->curr_cmd_data)); - s->curr_cmd_data[s->curr_cmd_data_cntr++] = value; - - pvscsi_do_command_processing(s); -} - -static void -pvscsi_on_command(PVSCSIState *s, uint64_t cmd_id) -{ - if ((cmd_id > PVSCSI_CMD_FIRST) && (cmd_id < PVSCSI_CMD_LAST)) { - s->curr_cmd = cmd_id; - } else { - s->curr_cmd = PVSCSI_CMD_FIRST; - trace_pvscsi_on_cmd_unknown(cmd_id); - } - - s->curr_cmd_data_cntr = 0; - s->reg_command_status = PVSCSI_COMMAND_NOT_ENOUGH_DATA; - - pvscsi_do_command_processing(s); -} - -static void -pvscsi_io_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - PVSCSIState *s = opaque; - - switch (addr) { - case PVSCSI_REG_OFFSET_COMMAND: - pvscsi_on_command(s, val); - break; - - case PVSCSI_REG_OFFSET_COMMAND_DATA: - pvscsi_on_command_data(s, (uint32_t) val); - break; - - case PVSCSI_REG_OFFSET_INTR_STATUS: - trace_pvscsi_io_write("PVSCSI_REG_OFFSET_INTR_STATUS", val); - s->reg_interrupt_status &= ~val; - pvscsi_update_irq_status(s); - pvscsi_schedule_completion_processing(s); - break; - - case PVSCSI_REG_OFFSET_INTR_MASK: - trace_pvscsi_io_write("PVSCSI_REG_OFFSET_INTR_MASK", val); - s->reg_interrupt_enabled = val; - pvscsi_update_irq_status(s); - break; - - case PVSCSI_REG_OFFSET_KICK_NON_RW_IO: - trace_pvscsi_io_write("PVSCSI_REG_OFFSET_KICK_NON_RW_IO", val); - pvscsi_process_io(s); - break; - - case PVSCSI_REG_OFFSET_KICK_RW_IO: - trace_pvscsi_io_write("PVSCSI_REG_OFFSET_KICK_RW_IO", val); - pvscsi_process_io(s); - break; - - case PVSCSI_REG_OFFSET_DEBUG: - trace_pvscsi_io_write("PVSCSI_REG_OFFSET_DEBUG", val); - break; - - default: - trace_pvscsi_io_write_unknown(addr, size, val); - break; - } - -} - -static uint64_t -pvscsi_io_read(void *opaque, hwaddr addr, unsigned size) -{ - PVSCSIState *s = opaque; - - switch (addr) { - case PVSCSI_REG_OFFSET_INTR_STATUS: - trace_pvscsi_io_read("PVSCSI_REG_OFFSET_INTR_STATUS", - s->reg_interrupt_status); - return s->reg_interrupt_status; - - case PVSCSI_REG_OFFSET_INTR_MASK: - trace_pvscsi_io_read("PVSCSI_REG_OFFSET_INTR_MASK", - s->reg_interrupt_status); - return s->reg_interrupt_enabled; - - case PVSCSI_REG_OFFSET_COMMAND_STATUS: - trace_pvscsi_io_read("PVSCSI_REG_OFFSET_COMMAND_STATUS", - s->reg_interrupt_status); - return s->reg_command_status; - - default: - trace_pvscsi_io_read_unknown(addr, size); - return 0; - } -} - - -static bool -pvscsi_init_msi(PVSCSIState *s) -{ - int res; - PCIDevice *d = PCI_DEVICE(s); - - 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); - s->msi_used = false; - } else { - s->msi_used = true; - } - - return s->msi_used; -} - -static void -pvscsi_cleanup_msi(PVSCSIState *s) -{ - PCIDevice *d = PCI_DEVICE(s); - - if (s->msi_used) { - msi_uninit(d); - } -} - -static const MemoryRegionOps pvscsi_ops = { - .read = pvscsi_io_read, - .write = pvscsi_io_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const struct SCSIBusInfo pvscsi_scsi_info = { - .tcq = true, - .max_target = PVSCSI_MAX_DEVS, - .max_channel = 0, - .max_lun = 0, - - .get_sg_list = pvscsi_get_sg_list, - .complete = pvscsi_command_complete, - .cancel = pvscsi_request_cancelled, -}; - -static int -pvscsi_init(PCIDevice *pci_dev) -{ - PVSCSIState *s = PVSCSI(pci_dev); - - trace_pvscsi_state("init"); - - /* PCI subsystem ID, subsystem vendor ID, revision */ - if (PVSCSI_USE_OLD_PCI_CONFIGURATION(s)) { - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, 0x1000); - } else { - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, - PCI_VENDOR_ID_VMWARE); - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, - PCI_DEVICE_ID_VMWARE_PVSCSI); - pci_config_set_revision(pci_dev->config, 0x2); - } - - /* PCI latency timer = 255 */ - pci_dev->config[PCI_LATENCY_TIMER] = 0xff; - - /* Interrupt pin A */ - pci_config_set_interrupt_pin(pci_dev->config, 1); - - memory_region_init_io(&s->io_space, OBJECT(s), &pvscsi_ops, s, - "pvscsi-io", PVSCSI_MEM_SPACE_SIZE); - pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->io_space); - - 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); - return -ENOMEM; - } - - scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(pci_dev), - &pvscsi_scsi_info, NULL); - /* override default SCSI bus hotplug-handler, with pvscsi's one */ - qbus_set_hotplug_handler(BUS(&s->bus), DEVICE(s), &error_abort); - pvscsi_reset_state(s); - - return 0; -} - -static void -pvscsi_uninit(PCIDevice *pci_dev) -{ - PVSCSIState *s = PVSCSI(pci_dev); - - trace_pvscsi_state("uninit"); - qemu_bh_delete(s->completion_worker); - - pvscsi_cleanup_msi(s); -} - -static void -pvscsi_reset(DeviceState *dev) -{ - PCIDevice *d = PCI_DEVICE(dev); - PVSCSIState *s = PVSCSI(d); - - trace_pvscsi_state("reset"); - pvscsi_reset_adapter(s); -} - -static void -pvscsi_pre_save(void *opaque) -{ - PVSCSIState *s = (PVSCSIState *) opaque; - - trace_pvscsi_state("presave"); - - assert(QTAILQ_EMPTY(&s->pending_queue)); - assert(QTAILQ_EMPTY(&s->completion_queue)); -} - -static int -pvscsi_post_load(void *opaque, int version_id) -{ - trace_pvscsi_state("postload"); - 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, - .minimum_version_id = 0, - .pre_save = pvscsi_pre_save, - .post_load = pvscsi_post_load, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_TEST(parent_obj, PVSCSIState, - pvscsi_vmstate_test_pci_device, 0, - vmstate_pci_device, PCIDevice), - VMSTATE_UINT8(msi_used, PVSCSIState), - VMSTATE_UINT32(resetting, PVSCSIState), - VMSTATE_UINT64(reg_interrupt_status, PVSCSIState), - VMSTATE_UINT64(reg_interrupt_enabled, PVSCSIState), - VMSTATE_UINT64(reg_command_status, PVSCSIState), - VMSTATE_UINT64(curr_cmd, PVSCSIState), - VMSTATE_UINT32(curr_cmd_data_cntr, PVSCSIState), - VMSTATE_UINT32_ARRAY(curr_cmd_data, PVSCSIState, - ARRAY_SIZE(((PVSCSIState *)NULL)->curr_cmd_data)), - VMSTATE_UINT8(rings_info_valid, PVSCSIState), - VMSTATE_UINT8(msg_ring_info_valid, PVSCSIState), - VMSTATE_UINT8(use_msg, PVSCSIState), - - VMSTATE_UINT64(rings.rs_pa, PVSCSIState), - VMSTATE_UINT32(rings.txr_len_mask, PVSCSIState), - VMSTATE_UINT32(rings.rxr_len_mask, PVSCSIState), - VMSTATE_UINT64_ARRAY(rings.req_ring_pages_pa, PVSCSIState, - PVSCSI_SETUP_RINGS_MAX_NUM_PAGES), - VMSTATE_UINT64_ARRAY(rings.cmp_ring_pages_pa, PVSCSIState, - PVSCSI_SETUP_RINGS_MAX_NUM_PAGES), - VMSTATE_UINT64(rings.consumed_ptr, PVSCSIState), - 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; - k->exit = pvscsi_uninit; - k->vendor_id = PCI_VENDOR_ID_VMWARE; - 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; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - hc->unplug = pvscsi_hot_unplug; - hc->plug = pvscsi_hotplug; -} - -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[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - -static void -pvscsi_register_types(void) -{ - type_register_static(&pvscsi_info); -} - -type_init(pvscsi_register_types); diff --git a/qemu/hw/scsi/vmw_pvscsi.h b/qemu/hw/scsi/vmw_pvscsi.h deleted file mode 100644 index 17fcf6627..000000000 --- a/qemu/hw/scsi/vmw_pvscsi.h +++ /dev/null @@ -1,434 +0,0 @@ -/* - * VMware PVSCSI header file - * - * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved. - * - * 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; version 2 of the License and no 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, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Maintained by: Arvind Kumar - * - */ - -#ifndef VMW_PVSCSI_H -#define VMW_PVSCSI_H - -#define VMW_PAGE_SIZE (4096) -#define VMW_PAGE_SHIFT (12) - -#define MASK(n) ((1 << (n)) - 1) /* make an n-bit mask */ - -/* - * host adapter status/error codes - */ -enum HostBusAdapterStatus { - BTSTAT_SUCCESS = 0x00, /* CCB complete normally with no errors */ - BTSTAT_LINKED_COMMAND_COMPLETED = 0x0a, - BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG = 0x0b, - BTSTAT_DATA_UNDERRUN = 0x0c, - BTSTAT_SELTIMEO = 0x11, /* SCSI selection timeout */ - BTSTAT_DATARUN = 0x12, /* data overrun/underrun */ - BTSTAT_BUSFREE = 0x13, /* unexpected bus free */ - BTSTAT_INVPHASE = 0x14, /* invalid bus phase or sequence */ - /* requested by target */ - BTSTAT_LUNMISMATCH = 0x17, /* linked CCB has different LUN */ - /* from first CCB */ - BTSTAT_SENSFAILED = 0x1b, /* auto request sense failed */ - BTSTAT_TAGREJECT = 0x1c, /* SCSI II tagged queueing message */ - /* rejected by target */ - BTSTAT_BADMSG = 0x1d, /* unsupported message received by */ - /* the host adapter */ - BTSTAT_HAHARDWARE = 0x20, /* host adapter hardware failed */ - BTSTAT_NORESPONSE = 0x21, /* target did not respond to SCSI ATN, */ - /* sent a SCSI RST */ - BTSTAT_SENTRST = 0x22, /* host adapter asserted a SCSI RST */ - BTSTAT_RECVRST = 0x23, /* other SCSI devices asserted a SCSI RST */ - BTSTAT_DISCONNECT = 0x24, /* target device reconnected improperly */ - /* (w/o tag) */ - BTSTAT_BUSRESET = 0x25, /* host adapter issued BUS device reset */ - BTSTAT_ABORTQUEUE = 0x26, /* abort queue generated */ - BTSTAT_HASOFTWARE = 0x27, /* host adapter software error */ - BTSTAT_HATIMEOUT = 0x30, /* host adapter hardware timeout error */ - BTSTAT_SCSIPARITY = 0x34, /* SCSI parity error detected */ -}; - -/* - * Register offsets. - * - * These registers are accessible both via i/o space and mm i/o. - */ - -enum PVSCSIRegOffset { - PVSCSI_REG_OFFSET_COMMAND = 0x0, - PVSCSI_REG_OFFSET_COMMAND_DATA = 0x4, - PVSCSI_REG_OFFSET_COMMAND_STATUS = 0x8, - PVSCSI_REG_OFFSET_LAST_STS_0 = 0x100, - PVSCSI_REG_OFFSET_LAST_STS_1 = 0x104, - PVSCSI_REG_OFFSET_LAST_STS_2 = 0x108, - PVSCSI_REG_OFFSET_LAST_STS_3 = 0x10c, - PVSCSI_REG_OFFSET_INTR_STATUS = 0x100c, - PVSCSI_REG_OFFSET_INTR_MASK = 0x2010, - PVSCSI_REG_OFFSET_KICK_NON_RW_IO = 0x3014, - PVSCSI_REG_OFFSET_DEBUG = 0x3018, - PVSCSI_REG_OFFSET_KICK_RW_IO = 0x4018, -}; - -/* - * Virtual h/w commands. - */ - -enum PVSCSICommands { - PVSCSI_CMD_FIRST = 0, /* has to be first */ - - PVSCSI_CMD_ADAPTER_RESET = 1, - PVSCSI_CMD_ISSUE_SCSI = 2, - PVSCSI_CMD_SETUP_RINGS = 3, - PVSCSI_CMD_RESET_BUS = 4, - PVSCSI_CMD_RESET_DEVICE = 5, - PVSCSI_CMD_ABORT_CMD = 6, - PVSCSI_CMD_CONFIG = 7, - PVSCSI_CMD_SETUP_MSG_RING = 8, - PVSCSI_CMD_DEVICE_UNPLUG = 9, - - PVSCSI_CMD_LAST = 10 /* has to be last */ -}; - -#define PVSCSI_COMMAND_PROCESSING_SUCCEEDED (0) -#define PVSCSI_COMMAND_PROCESSING_FAILED (-1) -#define PVSCSI_COMMAND_NOT_ENOUGH_DATA (-2) - -/* - * Command descriptor for PVSCSI_CMD_RESET_DEVICE -- - */ - -struct PVSCSICmdDescResetDevice { - uint32_t target; - uint8_t lun[8]; -} QEMU_PACKED; - -typedef struct PVSCSICmdDescResetDevice PVSCSICmdDescResetDevice; - -/* - * Command descriptor for PVSCSI_CMD_ABORT_CMD -- - * - * - currently does not support specifying the LUN. - * - pad should be 0. - */ - -struct PVSCSICmdDescAbortCmd { - uint64_t context; - uint32_t target; - uint32_t pad; -} QEMU_PACKED; - -typedef struct PVSCSICmdDescAbortCmd PVSCSICmdDescAbortCmd; - -/* - * Command descriptor for PVSCSI_CMD_SETUP_RINGS -- - * - * Notes: - * - reqRingNumPages and cmpRingNumPages need to be power of two. - * - reqRingNumPages and cmpRingNumPages need to be different from 0, - * - reqRingNumPages and cmpRingNumPages need to be inferior to - * PVSCSI_SETUP_RINGS_MAX_NUM_PAGES. - */ - -#define PVSCSI_SETUP_RINGS_MAX_NUM_PAGES 32 -struct PVSCSICmdDescSetupRings { - uint32_t reqRingNumPages; - uint32_t cmpRingNumPages; - uint64_t ringsStatePPN; - uint64_t reqRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; - uint64_t cmpRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; -} QEMU_PACKED; - -typedef struct PVSCSICmdDescSetupRings PVSCSICmdDescSetupRings; - -/* - * Command descriptor for PVSCSI_CMD_SETUP_MSG_RING -- - * - * Notes: - * - this command was not supported in the initial revision of the h/w - * interface. Before using it, you need to check that it is supported by - * writing PVSCSI_CMD_SETUP_MSG_RING to the 'command' register, then - * immediately after read the 'command status' register: - * * a value of -1 means that the cmd is NOT supported, - * * a value != -1 means that the cmd IS supported. - * If it's supported the 'command status' register should return: - * sizeof(PVSCSICmdDescSetupMsgRing) / sizeof(uint32_t). - * - this command should be issued _after_ the usual SETUP_RINGS so that the - * RingsState page is already setup. If not, the command is a nop. - * - numPages needs to be a power of two, - * - numPages needs to be different from 0, - * - pad should be zero. - */ - -#define PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES 16 - -struct PVSCSICmdDescSetupMsgRing { - uint32_t numPages; - uint32_t pad; - uint64_t ringPPNs[PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES]; -} QEMU_PACKED; - -typedef struct PVSCSICmdDescSetupMsgRing PVSCSICmdDescSetupMsgRing; - -enum PVSCSIMsgType { - PVSCSI_MSG_DEV_ADDED = 0, - PVSCSI_MSG_DEV_REMOVED = 1, - PVSCSI_MSG_LAST = 2, -}; - -/* - * Msg descriptor. - * - * sizeof(struct PVSCSIRingMsgDesc) == 128. - * - * - type is of type enum PVSCSIMsgType. - * - the content of args depend on the type of event being delivered. - */ - -struct PVSCSIRingMsgDesc { - uint32_t type; - uint32_t args[31]; -} QEMU_PACKED; - -typedef struct PVSCSIRingMsgDesc PVSCSIRingMsgDesc; - -struct PVSCSIMsgDescDevStatusChanged { - uint32_t type; /* PVSCSI_MSG_DEV _ADDED / _REMOVED */ - uint32_t bus; - uint32_t target; - uint8_t lun[8]; - uint32_t pad[27]; -} QEMU_PACKED; - -typedef struct PVSCSIMsgDescDevStatusChanged PVSCSIMsgDescDevStatusChanged; - -/* - * Rings state. - * - * - the fields: - * . msgProdIdx, - * . msgConsIdx, - * . msgNumEntriesLog2, - * .. are only used once the SETUP_MSG_RING cmd has been issued. - * - 'pad' helps to ensure that the msg related fields are on their own - * cache-line. - */ - -struct PVSCSIRingsState { - uint32_t reqProdIdx; - uint32_t reqConsIdx; - uint32_t reqNumEntriesLog2; - - uint32_t cmpProdIdx; - uint32_t cmpConsIdx; - uint32_t cmpNumEntriesLog2; - - uint8_t pad[104]; - - uint32_t msgProdIdx; - uint32_t msgConsIdx; - uint32_t msgNumEntriesLog2; -} QEMU_PACKED; - -typedef struct PVSCSIRingsState PVSCSIRingsState; - -/* - * Request descriptor. - * - * sizeof(RingReqDesc) = 128 - * - * - context: is a unique identifier of a command. It could normally be any - * 64bit value, however we currently store it in the serialNumber variable - * of struct SCSI_Command, so we have the following restrictions due to the - * way this field is handled in the vmkernel storage stack: - * * this value can't be 0, - * * the upper 32bit need to be 0 since serialNumber is as a uint32_t. - * Currently tracked as PR 292060. - * - dataLen: contains the total number of bytes that need to be transferred. - * - dataAddr: - * * if PVSCSI_FLAG_CMD_WITH_SG_LIST is set: dataAddr is the PA of the first - * s/g table segment, each s/g segment is entirely contained on a single - * page of physical memory, - * * if PVSCSI_FLAG_CMD_WITH_SG_LIST is NOT set, then dataAddr is the PA of - * the buffer used for the DMA transfer, - * - flags: - * * PVSCSI_FLAG_CMD_WITH_SG_LIST: see dataAddr above, - * * PVSCSI_FLAG_CMD_DIR_NONE: no DMA involved, - * * PVSCSI_FLAG_CMD_DIR_TOHOST: transfer from device to main memory, - * * PVSCSI_FLAG_CMD_DIR_TODEVICE: transfer from main memory to device, - * * PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB: reserved to handle CDBs larger than - * 16bytes. To be specified. - * - vcpuHint: vcpuId of the processor that will be most likely waiting for the - * completion of the i/o. For guest OSes that use lowest priority message - * delivery mode (such as windows), we use this "hint" to deliver the - * completion action to the proper vcpu. For now, we can use the vcpuId of - * the processor that initiated the i/o as a likely candidate for the vcpu - * that will be waiting for the completion.. - * - bus should be 0: we currently only support bus 0 for now. - * - unused should be zero'd. - */ - -#define PVSCSI_FLAG_CMD_WITH_SG_LIST (1 << 0) -#define PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB (1 << 1) -#define PVSCSI_FLAG_CMD_DIR_NONE (1 << 2) -#define PVSCSI_FLAG_CMD_DIR_TOHOST (1 << 3) -#define PVSCSI_FLAG_CMD_DIR_TODEVICE (1 << 4) - -#define PVSCSI_KNOWN_FLAGS \ - (PVSCSI_FLAG_CMD_WITH_SG_LIST | \ - PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB | \ - PVSCSI_FLAG_CMD_DIR_NONE | \ - PVSCSI_FLAG_CMD_DIR_TOHOST | \ - PVSCSI_FLAG_CMD_DIR_TODEVICE) - -struct PVSCSIRingReqDesc { - uint64_t context; - uint64_t dataAddr; - uint64_t dataLen; - uint64_t senseAddr; - uint32_t senseLen; - uint32_t flags; - uint8_t cdb[16]; - uint8_t cdbLen; - uint8_t lun[8]; - uint8_t tag; - uint8_t bus; - uint8_t target; - uint8_t vcpuHint; - uint8_t unused[59]; -} QEMU_PACKED; - -typedef struct PVSCSIRingReqDesc PVSCSIRingReqDesc; - -/* - * Scatter-gather list management. - * - * As described above, when PVSCSI_FLAG_CMD_WITH_SG_LIST is set in the - * RingReqDesc.flags, then RingReqDesc.dataAddr is the PA of the first s/g - * table segment. - * - * - each segment of the s/g table contain a succession of struct - * PVSCSISGElement. - * - each segment is entirely contained on a single physical page of memory. - * - a "chain" s/g element has the flag PVSCSI_SGE_FLAG_CHAIN_ELEMENT set in - * PVSCSISGElement.flags and in this case: - * * addr is the PA of the next s/g segment, - * * length is undefined, assumed to be 0. - */ - -struct PVSCSISGElement { - uint64_t addr; - uint32_t length; - uint32_t flags; -} QEMU_PACKED; - -typedef struct PVSCSISGElement PVSCSISGElement; - -/* - * Completion descriptor. - * - * sizeof(RingCmpDesc) = 32 - * - * - context: identifier of the command. The same thing that was specified - * under "context" as part of struct RingReqDesc at initiation time, - * - dataLen: number of bytes transferred for the actual i/o operation, - * - senseLen: number of bytes written into the sense buffer, - * - hostStatus: adapter status, - * - scsiStatus: device status, - * - pad should be zero. - */ - -struct PVSCSIRingCmpDesc { - uint64_t context; - uint64_t dataLen; - uint32_t senseLen; - uint16_t hostStatus; - uint16_t scsiStatus; - uint32_t pad[2]; -} QEMU_PACKED; - -typedef struct PVSCSIRingCmpDesc PVSCSIRingCmpDesc; - -/* - * Interrupt status / IRQ bits. - */ - -#define PVSCSI_INTR_CMPL_0 (1 << 0) -#define PVSCSI_INTR_CMPL_1 (1 << 1) -#define PVSCSI_INTR_CMPL_MASK MASK(2) - -#define PVSCSI_INTR_MSG_0 (1 << 2) -#define PVSCSI_INTR_MSG_1 (1 << 3) -#define PVSCSI_INTR_MSG_MASK (MASK(2) << 2) - -#define PVSCSI_INTR_ALL_SUPPORTED MASK(4) - -/* - * Number of MSI-X vectors supported. - */ -#define PVSCSI_MAX_INTRS 24 - -/* - * Enumeration of supported MSI-X vectors - */ -#define PVSCSI_VECTOR_COMPLETION 0 - -/* - * Misc constants for the rings. - */ - -#define PVSCSI_MAX_NUM_PAGES_REQ_RING PVSCSI_SETUP_RINGS_MAX_NUM_PAGES -#define PVSCSI_MAX_NUM_PAGES_CMP_RING PVSCSI_SETUP_RINGS_MAX_NUM_PAGES -#define PVSCSI_MAX_NUM_PAGES_MSG_RING PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES - -#define PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE \ - (VMW_PAGE_SIZE / sizeof(struct PVSCSIRingReqDesc)) - -#define PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE \ - (VMW_PAGE_SIZE / sizeof(PVSCSIRingCmpDesc)) - -#define PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE \ - (VMW_PAGE_SIZE / sizeof(PVSCSIRingMsgDesc)) - -#define PVSCSI_MAX_REQ_QUEUE_DEPTH \ - (PVSCSI_MAX_NUM_PAGES_REQ_RING * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE) - -#define PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES 1 -#define PVSCSI_MEM_SPACE_INTR_STATUS_NUM_PAGES 1 -#define PVSCSI_MEM_SPACE_MISC_NUM_PAGES 2 -#define PVSCSI_MEM_SPACE_KICK_IO_NUM_PAGES 2 -#define PVSCSI_MEM_SPACE_MSIX_NUM_PAGES 2 - -enum PVSCSIMemSpace { - PVSCSI_MEM_SPACE_COMMAND_PAGE = 0, - PVSCSI_MEM_SPACE_INTR_STATUS_PAGE = 1, - PVSCSI_MEM_SPACE_MISC_PAGE = 2, - PVSCSI_MEM_SPACE_KICK_IO_PAGE = 4, - PVSCSI_MEM_SPACE_MSIX_TABLE_PAGE = 6, - PVSCSI_MEM_SPACE_MSIX_PBA_PAGE = 7, -}; - -#define PVSCSI_MEM_SPACE_NUM_PAGES \ - (PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES + \ - PVSCSI_MEM_SPACE_INTR_STATUS_NUM_PAGES + \ - PVSCSI_MEM_SPACE_MISC_NUM_PAGES + \ - PVSCSI_MEM_SPACE_KICK_IO_NUM_PAGES + \ - PVSCSI_MEM_SPACE_MSIX_NUM_PAGES) - -#define PVSCSI_MEM_SPACE_SIZE (PVSCSI_MEM_SPACE_NUM_PAGES * VMW_PAGE_SIZE) - -#endif /* VMW_PVSCSI_H */ diff --git a/qemu/hw/sd/Makefile.objs b/qemu/hw/sd/Makefile.objs deleted file mode 100644 index 31c83308f..000000000 --- a/qemu/hw/sd/Makefile.objs +++ /dev/null @@ -1,8 +0,0 @@ -common-obj-$(CONFIG_PL181) += pl181.o -common-obj-$(CONFIG_SSI_SD) += ssi-sd.o -common-obj-$(CONFIG_SD) += sd.o core.o -common-obj-$(CONFIG_SDHCI) += sdhci.o - -obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o -obj-$(CONFIG_OMAP) += omap_mmc.o -obj-$(CONFIG_PXA2XX) += pxa2xx_mmci.o diff --git a/qemu/hw/sd/core.c b/qemu/hw/sd/core.c deleted file mode 100644 index 14c2bdf27..000000000 --- a/qemu/hw/sd/core.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - * SD card bus interface code. - * - * Copyright (c) 2015 Linaro Limited - * - * Author: - * Peter Maydell - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2 or later, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#include "qemu/osdep.h" -#include "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 deleted file mode 100644 index c04ff02fa..000000000 --- a/qemu/hw/sd/milkymist-memcard.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * QEMU model of the Milkymist SD Card Controller. - * - * Copyright (c) 2010 Michael Walle - * - * 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 . - * - * - * Specification available at: - * http://www.milkymist.org/socdoc/memcard.pdf - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "sysemu/sysemu.h" -#include "trace.h" -#include "qemu/error-report.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "hw/sd/sd.h" - -enum { - ENABLE_CMD_TX = (1<<0), - ENABLE_CMD_RX = (1<<1), - ENABLE_DAT_TX = (1<<2), - ENABLE_DAT_RX = (1<<3), -}; - -enum { - PENDING_CMD_TX = (1<<0), - PENDING_CMD_RX = (1<<1), - PENDING_DAT_TX = (1<<2), - PENDING_DAT_RX = (1<<3), -}; - -enum { - START_CMD_TX = (1<<0), - START_DAT_RX = (1<<1), -}; - -enum { - R_CLK2XDIV = 0, - R_ENABLE, - R_PENDING, - R_START, - R_CMD, - R_DAT, - R_MAX -}; - -#define TYPE_MILKYMIST_MEMCARD "milkymist-memcard" -#define MILKYMIST_MEMCARD(obj) \ - OBJECT_CHECK(MilkymistMemcardState, (obj), TYPE_MILKYMIST_MEMCARD) - -struct MilkymistMemcardState { - SysBusDevice parent_obj; - - MemoryRegion regs_region; - SDState *card; - - int command_write_ptr; - int response_read_ptr; - int response_len; - int ignore_next_cmd; - int enabled; - uint8_t command[6]; - uint8_t response[17]; - uint32_t regs[R_MAX]; -}; -typedef struct MilkymistMemcardState MilkymistMemcardState; - -static void update_pending_bits(MilkymistMemcardState *s) -{ - /* transmits are instantaneous, thus tx pending bits are never set */ - s->regs[R_PENDING] = 0; - /* if rx is enabled the corresponding pending bits are always set */ - if (s->regs[R_ENABLE] & ENABLE_CMD_RX) { - s->regs[R_PENDING] |= PENDING_CMD_RX; - } - if (s->regs[R_ENABLE] & ENABLE_DAT_RX) { - s->regs[R_PENDING] |= PENDING_DAT_RX; - } -} - -static void memcard_sd_command(MilkymistMemcardState *s) -{ - SDRequest req; - - req.cmd = s->command[0] & 0x3f; - req.arg = (s->command[1] << 24) | (s->command[2] << 16) - | (s->command[3] << 8) | s->command[4]; - req.crc = s->command[5]; - - s->response[0] = req.cmd; - s->response_len = sd_do_command(s->card, &req, s->response+1); - s->response_read_ptr = 0; - - if (s->response_len == 16) { - /* R2 response */ - s->response[0] = 0x3f; - s->response_len += 1; - } else if (s->response_len == 4) { - /* no crc calculation, insert dummy byte */ - s->response[5] = 0; - s->response_len += 2; - } - - if (req.cmd == 0) { - /* next write is a dummy byte to clock the initialization of the sd - * card */ - s->ignore_next_cmd = 1; - } -} - -static uint64_t memcard_read(void *opaque, hwaddr addr, - unsigned size) -{ - MilkymistMemcardState *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_CMD: - if (!s->enabled) { - r = 0xff; - } else { - r = s->response[s->response_read_ptr++]; - if (s->response_read_ptr > s->response_len) { - error_report("milkymist_memcard: " - "read more cmd bytes than available. Clipping."); - s->response_read_ptr = 0; - } - } - break; - case R_DAT: - if (!s->enabled) { - r = 0xffffffff; - } else { - r = 0; - r |= sd_read_data(s->card) << 24; - r |= sd_read_data(s->card) << 16; - r |= sd_read_data(s->card) << 8; - r |= sd_read_data(s->card); - } - break; - case R_CLK2XDIV: - case R_ENABLE: - case R_PENDING: - case R_START: - r = s->regs[addr]; - break; - - default: - error_report("milkymist_memcard: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_milkymist_memcard_memory_read(addr << 2, r); - - return r; -} - -static void memcard_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - MilkymistMemcardState *s = opaque; - - trace_milkymist_memcard_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_PENDING: - /* clear rx pending bits */ - s->regs[R_PENDING] &= ~(value & (PENDING_CMD_RX | PENDING_DAT_RX)); - update_pending_bits(s); - break; - case R_CMD: - if (!s->enabled) { - break; - } - if (s->ignore_next_cmd) { - s->ignore_next_cmd = 0; - break; - } - s->command[s->command_write_ptr] = value & 0xff; - s->command_write_ptr = (s->command_write_ptr + 1) % 6; - if (s->command_write_ptr == 0) { - memcard_sd_command(s); - } - break; - case R_DAT: - if (!s->enabled) { - break; - } - sd_write_data(s->card, (value >> 24) & 0xff); - sd_write_data(s->card, (value >> 16) & 0xff); - sd_write_data(s->card, (value >> 8) & 0xff); - sd_write_data(s->card, value & 0xff); - break; - case R_ENABLE: - s->regs[addr] = value; - update_pending_bits(s); - break; - case R_CLK2XDIV: - case R_START: - s->regs[addr] = value; - break; - - default: - error_report("milkymist_memcard: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } -} - -static const MemoryRegionOps memcard_mmio_ops = { - .read = memcard_read, - .write = memcard_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void milkymist_memcard_reset(DeviceState *d) -{ - MilkymistMemcardState *s = MILKYMIST_MEMCARD(d); - int i; - - s->command_write_ptr = 0; - s->response_read_ptr = 0; - s->response_len = 0; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } -} - -static int milkymist_memcard_init(SysBusDevice *dev) -{ - MilkymistMemcardState *s = MILKYMIST_MEMCARD(dev); - DriveInfo *dinfo; - BlockBackend *blk; - - /* FIXME use a qdev drive property instead of drive_get_next() */ - dinfo = drive_get_next(IF_SD); - blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; - s->card = sd_init(blk, false); - if (s->card == NULL) { - return -1; - } - - s->enabled = blk && blk_is_inserted(blk); - - memory_region_init_io(&s->regs_region, OBJECT(s), &memcard_mmio_ops, s, - "milkymist-memcard", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); - - return 0; -} - -static const VMStateDescription vmstate_milkymist_memcard = { - .name = "milkymist-memcard", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32(command_write_ptr, MilkymistMemcardState), - VMSTATE_INT32(response_read_ptr, MilkymistMemcardState), - VMSTATE_INT32(response_len, MilkymistMemcardState), - VMSTATE_INT32(ignore_next_cmd, MilkymistMemcardState), - VMSTATE_INT32(enabled, MilkymistMemcardState), - VMSTATE_UINT8_ARRAY(command, MilkymistMemcardState, 6), - VMSTATE_UINT8_ARRAY(response, MilkymistMemcardState, 17), - VMSTATE_UINT32_ARRAY(regs, MilkymistMemcardState, R_MAX), - VMSTATE_END_OF_LIST() - } -}; - -static void milkymist_memcard_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = milkymist_memcard_init; - dc->reset = milkymist_memcard_reset; - dc->vmsd = &vmstate_milkymist_memcard; - /* Reason: init() method uses drive_get_next() */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo milkymist_memcard_info = { - .name = TYPE_MILKYMIST_MEMCARD, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MilkymistMemcardState), - .class_init = milkymist_memcard_class_init, -}; - -static void milkymist_memcard_register_types(void) -{ - type_register_static(&milkymist_memcard_info); -} - -type_init(milkymist_memcard_register_types) diff --git a/qemu/hw/sd/omap_mmc.c b/qemu/hw/sd/omap_mmc.c deleted file mode 100644 index e934cd365..000000000 --- a/qemu/hw/sd/omap_mmc.c +++ /dev/null @@ -1,647 +0,0 @@ -/* - * OMAP on-chip MMC/SD host emulation. - * - * Copyright (C) 2006-2007 Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/arm/omap.h" -#include "hw/sd/sd.h" - -struct omap_mmc_s { - qemu_irq irq; - qemu_irq *dma; - qemu_irq coverswitch; - MemoryRegion iomem; - omap_clk clk; - SDState *card; - uint16_t last_cmd; - uint16_t sdio; - uint16_t rsp[8]; - uint32_t arg; - int lines; - int dw; - int mode; - int enable; - int be; - int rev; - uint16_t status; - uint16_t mask; - uint8_t cto; - uint16_t dto; - int clkdiv; - uint16_t fifo[32]; - int fifo_start; - int fifo_len; - uint16_t blen; - uint16_t blen_counter; - uint16_t nblk; - uint16_t nblk_counter; - int tx_dma; - int rx_dma; - int af_level; - int ae_level; - - int ddir; - int transfer; - - int cdet_wakeup; - int cdet_enable; - int cdet_state; - qemu_irq cdet; -}; - -static void omap_mmc_interrupts_update(struct omap_mmc_s *s) -{ - qemu_set_irq(s->irq, !!(s->status & s->mask)); -} - -static void omap_mmc_fifolevel_update(struct omap_mmc_s *host) -{ - if (!host->transfer && !host->fifo_len) { - host->status &= 0xf3ff; - return; - } - - if (host->fifo_len > host->af_level && host->ddir) { - if (host->rx_dma) { - host->status &= 0xfbff; - qemu_irq_raise(host->dma[1]); - } else - host->status |= 0x0400; - } else { - host->status &= 0xfbff; - qemu_irq_lower(host->dma[1]); - } - - if (host->fifo_len < host->ae_level && !host->ddir) { - if (host->tx_dma) { - host->status &= 0xf7ff; - qemu_irq_raise(host->dma[0]); - } else - host->status |= 0x0800; - } else { - qemu_irq_lower(host->dma[0]); - host->status &= 0xf7ff; - } -} - -typedef enum { - sd_nore = 0, /* no response */ - sd_r1, /* normal response command */ - sd_r2, /* CID, CSD registers */ - sd_r3, /* OCR register */ - sd_r6 = 6, /* Published RCA response */ - sd_r1b = -1, -} sd_rsp_type_t; - -static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir, - sd_cmd_type_t type, int busy, sd_rsp_type_t resptype, int init) -{ - uint32_t rspstatus, mask; - int rsplen, timeout; - SDRequest request; - uint8_t response[16]; - - if (init && cmd == 0) { - host->status |= 0x0001; - return; - } - - if (resptype == sd_r1 && busy) - resptype = sd_r1b; - - if (type == sd_adtc) { - host->fifo_start = 0; - host->fifo_len = 0; - host->transfer = 1; - host->ddir = dir; - } else - host->transfer = 0; - timeout = 0; - mask = 0; - rspstatus = 0; - - request.cmd = cmd; - request.arg = host->arg; - request.crc = 0; /* FIXME */ - - rsplen = sd_do_command(host->card, &request, response); - - /* TODO: validate CRCs */ - switch (resptype) { - case sd_nore: - rsplen = 0; - break; - - case sd_r1: - case sd_r1b: - if (rsplen < 4) { - timeout = 1; - break; - } - rsplen = 4; - - mask = OUT_OF_RANGE | ADDRESS_ERROR | BLOCK_LEN_ERROR | - ERASE_SEQ_ERROR | ERASE_PARAM | WP_VIOLATION | - LOCK_UNLOCK_FAILED | COM_CRC_ERROR | ILLEGAL_COMMAND | - CARD_ECC_FAILED | CC_ERROR | SD_ERROR | - CID_CSD_OVERWRITE; - if (host->sdio & (1 << 13)) - mask |= AKE_SEQ_ERROR; - rspstatus = (response[0] << 24) | (response[1] << 16) | - (response[2] << 8) | (response[3] << 0); - break; - - case sd_r2: - if (rsplen < 16) { - timeout = 1; - break; - } - rsplen = 16; - break; - - case sd_r3: - if (rsplen < 4) { - timeout = 1; - break; - } - rsplen = 4; - - rspstatus = (response[0] << 24) | (response[1] << 16) | - (response[2] << 8) | (response[3] << 0); - if (rspstatus & 0x80000000) - host->status &= 0xe000; - else - host->status |= 0x1000; - break; - - case sd_r6: - if (rsplen < 4) { - timeout = 1; - break; - } - rsplen = 4; - - mask = 0xe000 | AKE_SEQ_ERROR; - rspstatus = (response[2] << 8) | (response[3] << 0); - } - - if (rspstatus & mask) - host->status |= 0x4000; - else - host->status &= 0xb000; - - if (rsplen) - for (rsplen = 0; rsplen < 8; rsplen ++) - host->rsp[~rsplen & 7] = response[(rsplen << 1) | 1] | - (response[(rsplen << 1) | 0] << 8); - - if (timeout) - host->status |= 0x0080; - else if (cmd == 12) - host->status |= 0x0005; /* Makes it more real */ - else - host->status |= 0x0001; -} - -static void omap_mmc_transfer(struct omap_mmc_s *host) -{ - uint8_t value; - - if (!host->transfer) - return; - - while (1) { - if (host->ddir) { - if (host->fifo_len > host->af_level) - break; - - value = sd_read_data(host->card); - host->fifo[(host->fifo_start + host->fifo_len) & 31] = value; - if (-- host->blen_counter) { - value = sd_read_data(host->card); - host->fifo[(host->fifo_start + host->fifo_len) & 31] |= - value << 8; - host->blen_counter --; - } - - host->fifo_len ++; - } else { - if (!host->fifo_len) - break; - - value = host->fifo[host->fifo_start] & 0xff; - sd_write_data(host->card, value); - if (-- host->blen_counter) { - value = host->fifo[host->fifo_start] >> 8; - sd_write_data(host->card, value); - host->blen_counter --; - } - - host->fifo_start ++; - host->fifo_len --; - host->fifo_start &= 31; - } - - if (host->blen_counter == 0) { - host->nblk_counter --; - host->blen_counter = host->blen; - - if (host->nblk_counter == 0) { - host->nblk_counter = host->nblk; - host->transfer = 0; - host->status |= 0x0008; - break; - } - } - } -} - -static void omap_mmc_update(void *opaque) -{ - struct omap_mmc_s *s = opaque; - omap_mmc_transfer(s); - omap_mmc_fifolevel_update(s); - omap_mmc_interrupts_update(s); -} - -void omap_mmc_reset(struct omap_mmc_s *host) -{ - host->last_cmd = 0; - memset(host->rsp, 0, sizeof(host->rsp)); - host->arg = 0; - host->dw = 0; - host->mode = 0; - host->enable = 0; - host->status = 0; - host->mask = 0; - host->cto = 0; - host->dto = 0; - host->fifo_len = 0; - host->blen = 0; - host->blen_counter = 0; - host->nblk = 0; - host->nblk_counter = 0; - host->tx_dma = 0; - host->rx_dma = 0; - host->ae_level = 0x00; - host->af_level = 0x1f; - host->transfer = 0; - host->cdet_wakeup = 0; - host->cdet_enable = 0; - qemu_set_irq(host->coverswitch, host->cdet_state); - host->clkdiv = 0; -} - -static uint64_t omap_mmc_read(void *opaque, hwaddr offset, - unsigned size) -{ - uint16_t i; - struct omap_mmc_s *s = (struct omap_mmc_s *) opaque; - - if (size != 2) { - return omap_badwidth_read16(opaque, offset); - } - - switch (offset) { - case 0x00: /* MMC_CMD */ - return s->last_cmd; - - case 0x04: /* MMC_ARGL */ - return s->arg & 0x0000ffff; - - case 0x08: /* MMC_ARGH */ - return s->arg >> 16; - - case 0x0c: /* MMC_CON */ - return (s->dw << 15) | (s->mode << 12) | (s->enable << 11) | - (s->be << 10) | s->clkdiv; - - case 0x10: /* MMC_STAT */ - return s->status; - - case 0x14: /* MMC_IE */ - return s->mask; - - case 0x18: /* MMC_CTO */ - return s->cto; - - case 0x1c: /* MMC_DTO */ - return s->dto; - - case 0x20: /* MMC_DATA */ - /* TODO: support 8-bit access */ - i = s->fifo[s->fifo_start]; - if (s->fifo_len == 0) { - printf("MMC: FIFO underrun\n"); - return i; - } - s->fifo_start ++; - s->fifo_len --; - s->fifo_start &= 31; - omap_mmc_transfer(s); - omap_mmc_fifolevel_update(s); - omap_mmc_interrupts_update(s); - return i; - - case 0x24: /* MMC_BLEN */ - return s->blen_counter; - - case 0x28: /* MMC_NBLK */ - return s->nblk_counter; - - case 0x2c: /* MMC_BUF */ - return (s->rx_dma << 15) | (s->af_level << 8) | - (s->tx_dma << 7) | s->ae_level; - - case 0x30: /* MMC_SPI */ - return 0x0000; - case 0x34: /* MMC_SDIO */ - return (s->cdet_wakeup << 2) | (s->cdet_enable) | s->sdio; - case 0x38: /* MMC_SYST */ - return 0x0000; - - case 0x3c: /* MMC_REV */ - return s->rev; - - case 0x40: /* MMC_RSP0 */ - case 0x44: /* MMC_RSP1 */ - case 0x48: /* MMC_RSP2 */ - case 0x4c: /* MMC_RSP3 */ - case 0x50: /* MMC_RSP4 */ - case 0x54: /* MMC_RSP5 */ - case 0x58: /* MMC_RSP6 */ - case 0x5c: /* MMC_RSP7 */ - return s->rsp[(offset - 0x40) >> 2]; - - /* OMAP2-specific */ - case 0x60: /* MMC_IOSR */ - case 0x64: /* MMC_SYSC */ - return 0; - case 0x68: /* MMC_SYSS */ - return 1; /* RSTD */ - } - - OMAP_BAD_REG(offset); - return 0; -} - -static void omap_mmc_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - int i; - struct omap_mmc_s *s = (struct omap_mmc_s *) opaque; - - if (size != 2) { - omap_badwidth_write16(opaque, offset, value); - return; - } - - switch (offset) { - case 0x00: /* MMC_CMD */ - if (!s->enable) - break; - - s->last_cmd = value; - for (i = 0; i < 8; i ++) - s->rsp[i] = 0x0000; - omap_mmc_command(s, value & 63, (value >> 15) & 1, - (sd_cmd_type_t) ((value >> 12) & 3), - (value >> 11) & 1, - (sd_rsp_type_t) ((value >> 8) & 7), - (value >> 7) & 1); - omap_mmc_update(s); - break; - - case 0x04: /* MMC_ARGL */ - s->arg &= 0xffff0000; - s->arg |= 0x0000ffff & value; - break; - - case 0x08: /* MMC_ARGH */ - s->arg &= 0x0000ffff; - s->arg |= value << 16; - break; - - case 0x0c: /* MMC_CON */ - s->dw = (value >> 15) & 1; - s->mode = (value >> 12) & 3; - s->enable = (value >> 11) & 1; - s->be = (value >> 10) & 1; - s->clkdiv = (value >> 0) & (s->rev >= 2 ? 0x3ff : 0xff); - if (s->mode != 0) - printf("SD mode %i unimplemented!\n", s->mode); - if (s->be != 0) - printf("SD FIFO byte sex unimplemented!\n"); - if (s->dw != 0 && s->lines < 4) - printf("4-bit SD bus enabled\n"); - if (!s->enable) - omap_mmc_reset(s); - break; - - case 0x10: /* MMC_STAT */ - s->status &= ~value; - omap_mmc_interrupts_update(s); - break; - - case 0x14: /* MMC_IE */ - s->mask = value & 0x7fff; - omap_mmc_interrupts_update(s); - break; - - case 0x18: /* MMC_CTO */ - s->cto = value & 0xff; - if (s->cto > 0xfd && s->rev <= 1) - printf("MMC: CTO of 0xff and 0xfe cannot be used!\n"); - break; - - case 0x1c: /* MMC_DTO */ - s->dto = value & 0xffff; - break; - - case 0x20: /* MMC_DATA */ - /* TODO: support 8-bit access */ - if (s->fifo_len == 32) - break; - s->fifo[(s->fifo_start + s->fifo_len) & 31] = value; - s->fifo_len ++; - omap_mmc_transfer(s); - omap_mmc_fifolevel_update(s); - omap_mmc_interrupts_update(s); - break; - - case 0x24: /* MMC_BLEN */ - s->blen = (value & 0x07ff) + 1; - s->blen_counter = s->blen; - break; - - case 0x28: /* MMC_NBLK */ - s->nblk = (value & 0x07ff) + 1; - s->nblk_counter = s->nblk; - s->blen_counter = s->blen; - break; - - case 0x2c: /* MMC_BUF */ - s->rx_dma = (value >> 15) & 1; - s->af_level = (value >> 8) & 0x1f; - s->tx_dma = (value >> 7) & 1; - s->ae_level = value & 0x1f; - - if (s->rx_dma) - s->status &= 0xfbff; - if (s->tx_dma) - s->status &= 0xf7ff; - omap_mmc_fifolevel_update(s); - omap_mmc_interrupts_update(s); - break; - - /* SPI, SDIO and TEST modes unimplemented */ - case 0x30: /* MMC_SPI (OMAP1 only) */ - break; - case 0x34: /* MMC_SDIO */ - s->sdio = value & (s->rev >= 2 ? 0xfbf3 : 0x2020); - s->cdet_wakeup = (value >> 9) & 1; - s->cdet_enable = (value >> 2) & 1; - break; - case 0x38: /* MMC_SYST */ - break; - - case 0x3c: /* MMC_REV */ - case 0x40: /* MMC_RSP0 */ - case 0x44: /* MMC_RSP1 */ - case 0x48: /* MMC_RSP2 */ - case 0x4c: /* MMC_RSP3 */ - case 0x50: /* MMC_RSP4 */ - case 0x54: /* MMC_RSP5 */ - case 0x58: /* MMC_RSP6 */ - case 0x5c: /* MMC_RSP7 */ - OMAP_RO_REG(offset); - break; - - /* OMAP2-specific */ - case 0x60: /* MMC_IOSR */ - if (value & 0xf) - printf("MMC: SDIO bits used!\n"); - break; - case 0x64: /* MMC_SYSC */ - if (value & (1 << 2)) /* SRTS */ - omap_mmc_reset(s); - break; - case 0x68: /* MMC_SYSS */ - OMAP_RO_REG(offset); - break; - - default: - OMAP_BAD_REG(offset); - } -} - -static const MemoryRegionOps omap_mmc_ops = { - .read = omap_mmc_read, - .write = omap_mmc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_mmc_cover_cb(void *opaque, int line, int level) -{ - struct omap_mmc_s *host = (struct omap_mmc_s *) opaque; - - if (!host->cdet_state && level) { - host->status |= 0x0002; - omap_mmc_interrupts_update(host); - if (host->cdet_wakeup) { - /* TODO: Assert wake-up */ - } - } - - if (host->cdet_state != level) { - qemu_set_irq(host->coverswitch, level); - host->cdet_state = level; - } -} - -struct omap_mmc_s *omap_mmc_init(hwaddr base, - MemoryRegion *sysmem, - BlockBackend *blk, - qemu_irq irq, qemu_irq dma[], omap_clk clk) -{ - struct omap_mmc_s *s = g_new0(struct omap_mmc_s, 1); - - s->irq = irq; - s->dma = dma; - s->clk = clk; - s->lines = 1; /* TODO: needs to be settable per-board */ - s->rev = 1; - - omap_mmc_reset(s); - - memory_region_init_io(&s->iomem, NULL, &omap_mmc_ops, s, "omap.mmc", 0x800); - memory_region_add_subregion(sysmem, base, &s->iomem); - - /* Instantiate the storage */ - s->card = sd_init(blk, false); - if (s->card == NULL) { - exit(1); - } - - return s; -} - -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 = g_new0(struct omap_mmc_s, 1); - - s->irq = irq; - s->dma = dma; - s->clk = fclk; - s->lines = 4; - s->rev = 2; - - omap_mmc_reset(s); - - memory_region_init_io(&s->iomem, NULL, &omap_mmc_ops, s, "omap.mmc", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &s->iomem); - - /* Instantiate the storage */ - s->card = sd_init(blk, false); - if (s->card == NULL) { - exit(1); - } - - s->cdet = qemu_allocate_irq(omap_mmc_cover_cb, s, 0); - sd_set_cb(s->card, NULL, s->cdet); - - return s; -} - -void omap_mmc_handlers(struct omap_mmc_s *s, qemu_irq ro, qemu_irq cover) -{ - if (s->cdet) { - sd_set_cb(s->card, ro, s->cdet); - s->coverswitch = cover; - qemu_set_irq(cover, s->cdet_state); - } else - sd_set_cb(s->card, ro, cover); -} - -void omap_mmc_enable(struct omap_mmc_s *s, int enable) -{ - sd_enable(s->card, enable); -} diff --git a/qemu/hw/sd/pl181.c b/qemu/hw/sd/pl181.c deleted file mode 100644 index e87abb205..000000000 --- a/qemu/hw/sd/pl181.c +++ /dev/null @@ -1,528 +0,0 @@ -/* - * Arm PrimeCell PL181 MultiMedia Card Interface - * - * Copyright (c) 2007 CodeSourcery. - * Written by Paul Brook - * - * 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/sd.h" - -//#define DEBUG_PL181 1 - -#ifdef DEBUG_PL181 -#define DPRINTF(fmt, ...) \ -do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - -#define PL181_FIFO_LEN 16 - -#define TYPE_PL181 "pl181" -#define PL181(obj) OBJECT_CHECK(PL181State, (obj), TYPE_PL181) - -typedef struct PL181State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - SDState *card; - uint32_t clock; - uint32_t power; - uint32_t cmdarg; - uint32_t cmd; - uint32_t datatimer; - uint32_t datalength; - uint32_t respcmd; - uint32_t response[4]; - uint32_t datactrl; - uint32_t datacnt; - uint32_t status; - uint32_t mask[2]; - 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 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 - */ - int32_t linux_hack; - uint32_t fifo[PL181_FIFO_LEN]; - qemu_irq irq[2]; - /* GPIO outputs for 'card is readonly' and 'card inserted' */ - qemu_irq cardstatus[2]; -} PL181State; - -static const VMStateDescription vmstate_pl181 = { - .name = "pl181", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(clock, PL181State), - VMSTATE_UINT32(power, PL181State), - VMSTATE_UINT32(cmdarg, PL181State), - VMSTATE_UINT32(cmd, PL181State), - VMSTATE_UINT32(datatimer, PL181State), - VMSTATE_UINT32(datalength, PL181State), - VMSTATE_UINT32(respcmd, PL181State), - VMSTATE_UINT32_ARRAY(response, PL181State, 4), - VMSTATE_UINT32(datactrl, PL181State), - VMSTATE_UINT32(datacnt, PL181State), - VMSTATE_UINT32(status, PL181State), - VMSTATE_UINT32_ARRAY(mask, PL181State, 2), - VMSTATE_INT32(fifo_pos, PL181State), - VMSTATE_INT32(fifo_len, PL181State), - VMSTATE_INT32(linux_hack, PL181State), - VMSTATE_UINT32_ARRAY(fifo, PL181State, PL181_FIFO_LEN), - VMSTATE_END_OF_LIST() - } -}; - -#define PL181_CMD_INDEX 0x3f -#define PL181_CMD_RESPONSE (1 << 6) -#define PL181_CMD_LONGRESP (1 << 7) -#define PL181_CMD_INTERRUPT (1 << 8) -#define PL181_CMD_PENDING (1 << 9) -#define PL181_CMD_ENABLE (1 << 10) - -#define PL181_DATA_ENABLE (1 << 0) -#define PL181_DATA_DIRECTION (1 << 1) -#define PL181_DATA_MODE (1 << 2) -#define PL181_DATA_DMAENABLE (1 << 3) - -#define PL181_STATUS_CMDCRCFAIL (1 << 0) -#define PL181_STATUS_DATACRCFAIL (1 << 1) -#define PL181_STATUS_CMDTIMEOUT (1 << 2) -#define PL181_STATUS_DATATIMEOUT (1 << 3) -#define PL181_STATUS_TXUNDERRUN (1 << 4) -#define PL181_STATUS_RXOVERRUN (1 << 5) -#define PL181_STATUS_CMDRESPEND (1 << 6) -#define PL181_STATUS_CMDSENT (1 << 7) -#define PL181_STATUS_DATAEND (1 << 8) -#define PL181_STATUS_DATABLOCKEND (1 << 10) -#define PL181_STATUS_CMDACTIVE (1 << 11) -#define PL181_STATUS_TXACTIVE (1 << 12) -#define PL181_STATUS_RXACTIVE (1 << 13) -#define PL181_STATUS_TXFIFOHALFEMPTY (1 << 14) -#define PL181_STATUS_RXFIFOHALFFULL (1 << 15) -#define PL181_STATUS_TXFIFOFULL (1 << 16) -#define PL181_STATUS_RXFIFOFULL (1 << 17) -#define PL181_STATUS_TXFIFOEMPTY (1 << 18) -#define PL181_STATUS_RXFIFOEMPTY (1 << 19) -#define PL181_STATUS_TXDATAAVLBL (1 << 20) -#define PL181_STATUS_RXDATAAVLBL (1 << 21) - -#define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \ - |PL181_STATUS_TXFIFOHALFEMPTY \ - |PL181_STATUS_TXFIFOFULL \ - |PL181_STATUS_TXFIFOEMPTY \ - |PL181_STATUS_TXDATAAVLBL) -#define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \ - |PL181_STATUS_RXFIFOHALFFULL \ - |PL181_STATUS_RXFIFOFULL \ - |PL181_STATUS_RXFIFOEMPTY \ - |PL181_STATUS_RXDATAAVLBL) - -static const unsigned char pl181_id[] = -{ 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; - -static void pl181_update(PL181State *s) -{ - int i; - for (i = 0; i < 2; i++) { - qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0); - } -} - -static void pl181_fifo_push(PL181State *s, uint32_t value) -{ - int n; - - if (s->fifo_len == PL181_FIFO_LEN) { - fprintf(stderr, "pl181: FIFO overflow\n"); - return; - } - n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1); - s->fifo_len++; - s->fifo[n] = value; - DPRINTF("FIFO push %08x\n", (int)value); -} - -static uint32_t pl181_fifo_pop(PL181State *s) -{ - uint32_t value; - - if (s->fifo_len == 0) { - fprintf(stderr, "pl181: FIFO underflow\n"); - return 0; - } - value = s->fifo[s->fifo_pos]; - s->fifo_len--; - s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1); - DPRINTF("FIFO pop %08x\n", (int)value); - return value; -} - -static void pl181_send_command(PL181State *s) -{ - SDRequest request; - uint8_t response[16]; - int rlen; - - request.cmd = s->cmd & PL181_CMD_INDEX; - request.arg = s->cmdarg; - DPRINTF("Command %d %08x\n", request.cmd, request.arg); - rlen = sd_do_command(s->card, &request, response); - if (rlen < 0) - goto error; - if (s->cmd & PL181_CMD_RESPONSE) { -#define RWORD(n) (((uint32_t)response[n] << 24) | (response[n + 1] << 16) \ - | (response[n + 2] << 8) | response[n + 3]) - if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP))) - goto error; - if (rlen != 4 && rlen != 16) - goto error; - s->response[0] = RWORD(0); - if (rlen == 4) { - s->response[1] = s->response[2] = s->response[3] = 0; - } else { - s->response[1] = RWORD(4); - s->response[2] = RWORD(8); - s->response[3] = RWORD(12) & ~1; - } - DPRINTF("Response received\n"); - s->status |= PL181_STATUS_CMDRESPEND; -#undef RWORD - } else { - DPRINTF("Command sent\n"); - s->status |= PL181_STATUS_CMDSENT; - } - return; - -error: - DPRINTF("Timeout\n"); - s->status |= PL181_STATUS_CMDTIMEOUT; -} - -/* Transfer data between the card and the FIFO. This is complicated by - the FIFO holding 32-bit words and the card taking data in single byte - chunks. FIFO bytes are transferred in little-endian order. */ - -static void pl181_fifo_run(PL181State *s) -{ - uint32_t bits; - uint32_t value = 0; - int n; - int is_read; - - is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0; - if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card)) - && !s->linux_hack) { - if (is_read) { - n = 0; - while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) { - value |= (uint32_t)sd_read_data(s->card) << (n * 8); - s->datacnt--; - n++; - if (n == 4) { - pl181_fifo_push(s, value); - n = 0; - value = 0; - } - } - if (n != 0) { - pl181_fifo_push(s, value); - } - } else { /* write */ - n = 0; - while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) { - if (n == 0) { - value = pl181_fifo_pop(s); - n = 4; - } - n--; - s->datacnt--; - sd_write_data(s->card, value & 0xff); - value >>= 8; - } - } - } - s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO); - if (s->datacnt == 0) { - s->status |= PL181_STATUS_DATAEND; - /* HACK: */ - s->status |= PL181_STATUS_DATABLOCKEND; - DPRINTF("Transfer Complete\n"); - } - if (s->datacnt == 0 && s->fifo_len == 0) { - s->datactrl &= ~PL181_DATA_ENABLE; - DPRINTF("Data engine idle\n"); - } else { - /* Update FIFO bits. */ - bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE; - if (s->fifo_len == 0) { - bits |= PL181_STATUS_TXFIFOEMPTY; - bits |= PL181_STATUS_RXFIFOEMPTY; - } else { - bits |= PL181_STATUS_TXDATAAVLBL; - bits |= PL181_STATUS_RXDATAAVLBL; - } - if (s->fifo_len == 16) { - bits |= PL181_STATUS_TXFIFOFULL; - bits |= PL181_STATUS_RXFIFOFULL; - } - if (s->fifo_len <= 8) { - bits |= PL181_STATUS_TXFIFOHALFEMPTY; - } - if (s->fifo_len >= 8) { - bits |= PL181_STATUS_RXFIFOHALFFULL; - } - if (s->datactrl & PL181_DATA_DIRECTION) { - bits &= PL181_STATUS_RX_FIFO; - } else { - bits &= PL181_STATUS_TX_FIFO; - } - s->status |= bits; - } -} - -static uint64_t pl181_read(void *opaque, hwaddr offset, - unsigned size) -{ - PL181State *s = (PL181State *)opaque; - uint32_t tmp; - - if (offset >= 0xfe0 && offset < 0x1000) { - return pl181_id[(offset - 0xfe0) >> 2]; - } - switch (offset) { - case 0x00: /* Power */ - return s->power; - case 0x04: /* Clock */ - return s->clock; - case 0x08: /* Argument */ - return s->cmdarg; - case 0x0c: /* Command */ - return s->cmd; - case 0x10: /* RespCmd */ - return s->respcmd; - case 0x14: /* Response0 */ - return s->response[0]; - case 0x18: /* Response1 */ - return s->response[1]; - case 0x1c: /* Response2 */ - return s->response[2]; - case 0x20: /* Response3 */ - return s->response[3]; - case 0x24: /* DataTimer */ - return s->datatimer; - case 0x28: /* DataLength */ - return s->datalength; - case 0x2c: /* DataCtrl */ - return s->datactrl; - case 0x30: /* DataCnt */ - return s->datacnt; - case 0x34: /* Status */ - tmp = s->status; - if (s->linux_hack) { - s->linux_hack = 0; - pl181_fifo_run(s); - pl181_update(s); - } - return tmp; - case 0x3c: /* Mask0 */ - return s->mask[0]; - case 0x40: /* Mask1 */ - return s->mask[1]; - case 0x48: /* FifoCnt */ - /* The documentation is somewhat vague about exactly what FifoCnt - does. On real hardware it appears to be when decrememnted - when a word is transferred between the FIFO and the serial - data engine. DataCnt is decremented after each byte is - transferred between the serial engine and the card. - We don't emulate this level of detail, so both can be the same. */ - tmp = (s->datacnt + 3) >> 2; - if (s->linux_hack) { - s->linux_hack = 0; - pl181_fifo_run(s); - pl181_update(s); - } - return tmp; - case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */ - case 0x90: case 0x94: case 0x98: case 0x9c: - case 0xa0: case 0xa4: case 0xa8: case 0xac: - case 0xb0: case 0xb4: case 0xb8: case 0xbc: - if (s->fifo_len == 0) { - qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n"); - return 0; - } else { - uint32_t value; - value = pl181_fifo_pop(s); - s->linux_hack = 1; - pl181_fifo_run(s); - pl181_update(s); - return value; - } - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pl181_read: Bad offset %x\n", (int)offset); - return 0; - } -} - -static void pl181_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PL181State *s = (PL181State *)opaque; - - switch (offset) { - case 0x00: /* Power */ - s->power = value & 0xff; - break; - case 0x04: /* Clock */ - s->clock = value & 0xff; - break; - case 0x08: /* Argument */ - s->cmdarg = value; - break; - case 0x0c: /* Command */ - s->cmd = value; - if (s->cmd & PL181_CMD_ENABLE) { - if (s->cmd & PL181_CMD_INTERRUPT) { - qemu_log_mask(LOG_UNIMP, - "pl181: Interrupt mode not implemented\n"); - } if (s->cmd & PL181_CMD_PENDING) { - qemu_log_mask(LOG_UNIMP, - "pl181: Pending commands not implemented\n"); - } else { - pl181_send_command(s); - pl181_fifo_run(s); - } - /* The command has completed one way or the other. */ - s->cmd &= ~PL181_CMD_ENABLE; - } - break; - case 0x24: /* DataTimer */ - s->datatimer = value; - break; - case 0x28: /* DataLength */ - s->datalength = value & 0xffff; - break; - case 0x2c: /* DataCtrl */ - s->datactrl = value & 0xff; - if (value & PL181_DATA_ENABLE) { - s->datacnt = s->datalength; - pl181_fifo_run(s); - } - break; - case 0x38: /* Clear */ - s->status &= ~(value & 0x7ff); - break; - case 0x3c: /* Mask0 */ - s->mask[0] = value; - break; - case 0x40: /* Mask1 */ - s->mask[1] = value; - break; - case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */ - case 0x90: case 0x94: case 0x98: case 0x9c: - case 0xa0: case 0xa4: case 0xa8: case 0xac: - case 0xb0: case 0xb4: case 0xb8: case 0xbc: - if (s->datacnt == 0) { - qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n"); - } else { - pl181_fifo_push(s, value); - pl181_fifo_run(s); - } - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pl181_write: Bad offset %x\n", (int)offset); - } - pl181_update(s); -} - -static const MemoryRegionOps pl181_ops = { - .read = pl181_read, - .write = pl181_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pl181_reset(DeviceState *d) -{ - PL181State *s = PL181(d); - - s->power = 0; - s->cmdarg = 0; - s->cmd = 0; - s->datatimer = 0; - s->datalength = 0; - s->respcmd = 0; - s->response[0] = 0; - s->response[1] = 0; - s->response[2] = 0; - s->response[3] = 0; - s->datatimer = 0; - s->datalength = 0; - s->datactrl = 0; - s->datacnt = 0; - s->status = 0; - s->linux_hack = 0; - s->mask[0] = 0; - s->mask[1] = 0; - - /* We can assume our GPIO outputs have been wired up now */ - sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]); -} - -static int pl181_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - PL181State *s = PL181(dev); - DriveInfo *dinfo; - - memory_region_init_io(&s->iomem, OBJECT(s), &pl181_ops, s, "pl181", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq[0]); - sysbus_init_irq(sbd, &s->irq[1]); - qdev_init_gpio_out(dev, s->cardstatus, 2); - /* FIXME use a qdev drive property instead of drive_get_next() */ - dinfo = drive_get_next(IF_SD); - s->card = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, false); - if (s->card == NULL) { - return -1; - } - - return 0; -} - -static void pl181_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - DeviceClass *k = DEVICE_CLASS(klass); - - sdc->init = pl181_init; - k->vmsd = &vmstate_pl181; - k->reset = pl181_reset; - /* Reason: init() method uses drive_get_next() */ - k->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo pl181_info = { - .name = TYPE_PL181, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PL181State), - .class_init = pl181_class_init, -}; - -static void pl181_register_types(void) -{ - type_register_static(&pl181_info); -} - -type_init(pl181_register_types) diff --git a/qemu/hw/sd/pxa2xx_mmci.c b/qemu/hw/sd/pxa2xx_mmci.c deleted file mode 100644 index 3deccf02c..000000000 --- a/qemu/hw/sd/pxa2xx_mmci.c +++ /dev/null @@ -1,598 +0,0 @@ -/* - * Intel XScale PXA255/270 MultiMediaCard/SD/SDIO Controller emulation. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GPLv2. - * - * 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/sysbus.h" -#include "hw/arm/pxa.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; - - BlockBackend *blk; - SDBus sdbus; - - uint32_t status; - uint32_t clkrt; - uint32_t spi; - uint32_t cmdat; - uint32_t resp_tout; - uint32_t read_tout; - int32_t blklen; - int32_t numblk; - uint32_t intmask; - uint32_t intreq; - int32_t cmd; - uint32_t arg; - - int32_t active; - int32_t bytesleft; - uint8_t tx_fifo[64]; - uint32_t tx_start; - uint32_t tx_len; - uint8_t rx_fifo[32]; - uint32_t rx_start; - uint32_t rx_len; - uint16_t resp_fifo[9]; - uint32_t resp_len; - - 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 */ -#define MMC_STAT 0x04 /* MMC Status register */ -#define MMC_CLKRT 0x08 /* MMC Clock Rate register */ -#define MMC_SPI 0x0c /* MMC SPI Mode register */ -#define MMC_CMDAT 0x10 /* MMC Command/Data register */ -#define MMC_RESTO 0x14 /* MMC Response Time-Out register */ -#define MMC_RDTO 0x18 /* MMC Read Time-Out register */ -#define MMC_BLKLEN 0x1c /* MMC Block Length register */ -#define MMC_NUMBLK 0x20 /* MMC Number of Blocks register */ -#define MMC_PRTBUF 0x24 /* MMC Buffer Partly Full register */ -#define MMC_I_MASK 0x28 /* MMC Interrupt Mask register */ -#define MMC_I_REG 0x2c /* MMC Interrupt Request register */ -#define MMC_CMD 0x30 /* MMC Command register */ -#define MMC_ARGH 0x34 /* MMC Argument High register */ -#define MMC_ARGL 0x38 /* MMC Argument Low register */ -#define MMC_RES 0x3c /* MMC Response FIFO */ -#define MMC_RXFIFO 0x40 /* MMC Receive FIFO */ -#define MMC_TXFIFO 0x44 /* MMC Transmit FIFO */ -#define MMC_RDWAIT 0x48 /* MMC RD_WAIT register */ -#define MMC_BLKS_REM 0x4c /* MMC Blocks Remaining register */ - -/* Bitfield masks */ -#define STRPCL_STOP_CLK (1 << 0) -#define STRPCL_STRT_CLK (1 << 1) -#define STAT_TOUT_RES (1 << 1) -#define STAT_CLK_EN (1 << 8) -#define STAT_DATA_DONE (1 << 11) -#define STAT_PRG_DONE (1 << 12) -#define STAT_END_CMDRES (1 << 13) -#define SPI_SPI_MODE (1 << 0) -#define CMDAT_RES_TYPE (3 << 0) -#define CMDAT_DATA_EN (1 << 2) -#define CMDAT_WR_RD (1 << 3) -#define CMDAT_DMA_EN (1 << 7) -#define CMDAT_STOP_TRAN (1 << 10) -#define INT_DATA_DONE (1 << 0) -#define INT_PRG_DONE (1 << 1) -#define INT_END_CMD (1 << 2) -#define INT_STOP_CMD (1 << 3) -#define INT_CLK_OFF (1 << 4) -#define INT_RXFIFO_REQ (1 << 5) -#define INT_TXFIFO_REQ (1 << 6) -#define INT_TINT (1 << 7) -#define INT_DAT_ERR (1 << 8) -#define INT_RES_ERR (1 << 9) -#define INT_RD_STALLED (1 << 10) -#define INT_SDIO_INT (1 << 11) -#define INT_SDIO_SACK (1 << 12) -#define PRTBUF_PRT_BUF (1 << 0) - -/* Route internal interrupt lines to the global IC and DMA */ -static void pxa2xx_mmci_int_update(PXA2xxMMCIState *s) -{ - uint32_t mask = s->intmask; - if (s->cmdat & CMDAT_DMA_EN) { - mask |= INT_RXFIFO_REQ | INT_TXFIFO_REQ; - - qemu_set_irq(s->rx_dma, !!(s->intreq & INT_RXFIFO_REQ)); - qemu_set_irq(s->tx_dma, !!(s->intreq & INT_TXFIFO_REQ)); - } - - qemu_set_irq(s->irq, !!(s->intreq & ~mask)); -} - -static void pxa2xx_mmci_fifo_update(PXA2xxMMCIState *s) -{ - if (!s->active) - return; - - if (s->cmdat & CMDAT_WR_RD) { - while (s->bytesleft && s->tx_len) { - sdbus_write_data(&s->sdbus, s->tx_fifo[s->tx_start++]); - s->tx_start &= 0x1f; - s->tx_len --; - s->bytesleft --; - } - if (s->bytesleft) - s->intreq |= INT_TXFIFO_REQ; - } else - while (s->bytesleft && s->rx_len < 32) { - s->rx_fifo[(s->rx_start + (s->rx_len ++)) & 0x1f] = - sdbus_read_data(&s->sdbus); - s->bytesleft --; - s->intreq |= INT_RXFIFO_REQ; - } - - if (!s->bytesleft) { - s->active = 0; - s->intreq |= INT_DATA_DONE; - s->status |= STAT_DATA_DONE; - - if (s->cmdat & CMDAT_WR_RD) { - s->intreq |= INT_PRG_DONE; - s->status |= STAT_PRG_DONE; - } - } - - pxa2xx_mmci_int_update(s); -} - -static void pxa2xx_mmci_wakequeues(PXA2xxMMCIState *s) -{ - int rsplen, i; - SDRequest request; - uint8_t response[16]; - - s->active = 1; - s->rx_len = 0; - s->tx_len = 0; - s->cmdreq = 0; - - request.cmd = s->cmd; - request.arg = s->arg; - request.crc = 0; /* FIXME */ - - rsplen = sdbus_do_command(&s->sdbus, &request, response); - s->intreq |= INT_END_CMD; - - memset(s->resp_fifo, 0, sizeof(s->resp_fifo)); - switch (s->cmdat & CMDAT_RES_TYPE) { -#define PXAMMCI_RESP(wd, value0, value1) \ - s->resp_fifo[(wd) + 0] |= (value0); \ - s->resp_fifo[(wd) + 1] |= (value1) << 8; - case 0: /* No response */ - goto complete; - - case 1: /* R1, R4, R5 or R6 */ - if (rsplen < 4) - goto timeout; - goto complete; - - case 2: /* R2 */ - if (rsplen < 16) - goto timeout; - goto complete; - - case 3: /* R3 */ - if (rsplen < 4) - goto timeout; - goto complete; - - complete: - for (i = 0; rsplen > 0; i ++, rsplen -= 2) { - PXAMMCI_RESP(i, response[i * 2], response[i * 2 + 1]); - } - s->status |= STAT_END_CMDRES; - - if (!(s->cmdat & CMDAT_DATA_EN)) - s->active = 0; - else - s->bytesleft = s->numblk * s->blklen; - - s->resp_len = 0; - break; - - timeout: - s->active = 0; - s->status |= STAT_TOUT_RES; - break; - } - - pxa2xx_mmci_fifo_update(s); -} - -static uint64_t pxa2xx_mmci_read(void *opaque, hwaddr offset, unsigned size) -{ - PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque; - uint32_t ret; - - switch (offset) { - case MMC_STRPCL: - return 0; - case MMC_STAT: - return s->status; - case MMC_CLKRT: - return s->clkrt; - case MMC_SPI: - return s->spi; - case MMC_CMDAT: - return s->cmdat; - case MMC_RESTO: - return s->resp_tout; - case MMC_RDTO: - return s->read_tout; - case MMC_BLKLEN: - return s->blklen; - case MMC_NUMBLK: - return s->numblk; - case MMC_PRTBUF: - return 0; - case MMC_I_MASK: - return s->intmask; - case MMC_I_REG: - return s->intreq; - case MMC_CMD: - return s->cmd | 0x40; - case MMC_ARGH: - return s->arg >> 16; - case MMC_ARGL: - return s->arg & 0xffff; - case MMC_RES: - if (s->resp_len < 9) - return s->resp_fifo[s->resp_len ++]; - return 0; - case MMC_RXFIFO: - ret = 0; - while (size-- && s->rx_len) { - ret |= s->rx_fifo[s->rx_start++] << (size << 3); - s->rx_start &= 0x1f; - s->rx_len --; - } - s->intreq &= ~INT_RXFIFO_REQ; - pxa2xx_mmci_fifo_update(s); - return ret; - case MMC_RDWAIT: - return 0; - case MMC_BLKS_REM: - return s->numblk; - default: - hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset); - } - - return 0; -} - -static void pxa2xx_mmci_write(void *opaque, - hwaddr offset, uint64_t value, unsigned size) -{ - PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque; - - switch (offset) { - case MMC_STRPCL: - if (value & STRPCL_STRT_CLK) { - s->status |= STAT_CLK_EN; - s->intreq &= ~INT_CLK_OFF; - - if (s->cmdreq && !(s->cmdat & CMDAT_STOP_TRAN)) { - s->status &= STAT_CLK_EN; - pxa2xx_mmci_wakequeues(s); - } - } - - if (value & STRPCL_STOP_CLK) { - s->status &= ~STAT_CLK_EN; - s->intreq |= INT_CLK_OFF; - s->active = 0; - } - - pxa2xx_mmci_int_update(s); - break; - - case MMC_CLKRT: - s->clkrt = value & 7; - break; - - case MMC_SPI: - s->spi = value & 0xf; - if (value & SPI_SPI_MODE) - printf("%s: attempted to use card in SPI mode\n", __FUNCTION__); - break; - - case MMC_CMDAT: - s->cmdat = value & 0x3dff; - s->active = 0; - s->cmdreq = 1; - if (!(value & CMDAT_STOP_TRAN)) { - s->status &= STAT_CLK_EN; - - if (s->status & STAT_CLK_EN) - pxa2xx_mmci_wakequeues(s); - } - - pxa2xx_mmci_int_update(s); - break; - - case MMC_RESTO: - s->resp_tout = value & 0x7f; - break; - - case MMC_RDTO: - s->read_tout = value & 0xffff; - break; - - case MMC_BLKLEN: - s->blklen = value & 0xfff; - break; - - case MMC_NUMBLK: - s->numblk = value & 0xffff; - break; - - case MMC_PRTBUF: - if (value & PRTBUF_PRT_BUF) { - s->tx_start ^= 32; - s->tx_len = 0; - } - pxa2xx_mmci_fifo_update(s); - break; - - case MMC_I_MASK: - s->intmask = value & 0x1fff; - pxa2xx_mmci_int_update(s); - break; - - case MMC_CMD: - s->cmd = value & 0x3f; - break; - - case MMC_ARGH: - s->arg &= 0x0000ffff; - s->arg |= value << 16; - break; - - case MMC_ARGL: - s->arg &= 0xffff0000; - s->arg |= value & 0x0000ffff; - break; - - case MMC_TXFIFO: - while (size-- && s->tx_len < 0x20) - s->tx_fifo[(s->tx_start + (s->tx_len ++)) & 0x1f] = - (value >> (size << 3)) & 0xff; - s->intreq &= ~INT_TXFIFO_REQ; - pxa2xx_mmci_fifo_update(s); - break; - - case MMC_RDWAIT: - case MMC_BLKS_REM: - break; - - default: - hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset); - } -} - -static const MemoryRegionOps pxa2xx_mmci_ops = { - .read = pxa2xx_mmci_read, - .write = pxa2xx_mmci_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem, - hwaddr base, - BlockBackend *blk, qemu_irq irq, - qemu_irq rx_dma, qemu_irq tx_dma) -{ - 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 void pxa2xx_mmci_set_inserted(DeviceState *dev, bool inserted) -{ - PXA2xxMMCIState *s = PXA2XX_MMCI(dev); - - qemu_set_irq(s->inserted, inserted); -} - -static void pxa2xx_mmci_set_readonly(DeviceState *dev, bool readonly) -{ - PXA2xxMMCIState *s = PXA2XX_MMCI(dev); - - qemu_set_irq(s->readonly, readonly); -} - -void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly, - qemu_irq coverswitch) -{ - 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)); -} - -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, obj, &pxa2xx_mmci_ops, s, - "pxa2xx-mmci", 0x00100000); - 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); - - qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), - TYPE_PXA2XX_MMCI_BUS, DEVICE(obj), "sd-bus"); -} - -static void pxa2xx_mmci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_pxa2xx_mmci; - dc->reset = pxa2xx_mmci_reset; -} - -static void pxa2xx_mmci_bus_class_init(ObjectClass *klass, void *data) -{ - 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 deleted file mode 100644 index b66e5d2db..000000000 --- a/qemu/hw/sd/sd.c +++ /dev/null @@ -1,1979 +0,0 @@ -/* - * SD Memory Card emulation as defined in the "SD Memory Card Physical - * layer specification, Version 1.10." - * - * Copyright (c) 2006 Andrzej Zaborowski - * Copyright (c) 2007 CodeSourcery - * - * 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 the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 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/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 - -#ifdef DEBUG_SD -#define DPRINTF(fmt, ...) \ -do { fprintf(stderr, "SD: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - -#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 */ - sd_r1, /* normal response command */ - sd_r2_i, /* CID register */ - sd_r2_s, /* CSD register */ - sd_r3, /* OCR register */ - sd_r6 = 6, /* Published RCA response */ - sd_r7, /* Operating voltage */ - sd_r1b = -1, - sd_illegal = -2, -} sd_rsp_type_t; - -enum SDCardModes { - sd_inactive, - sd_card_identification_mode, - sd_data_transfer_mode, -}; - -enum SDCardStates { - sd_inactive_state = -1, - sd_idle_state = 0, - sd_ready_state, - sd_identification_state, - sd_standby_state, - sd_transfer_state, - sd_sendingdata_state, - sd_receivingdata_state, - sd_programming_state, - sd_disconnect_state, -}; - -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]; - uint16_t rca; - uint32_t card_status; - uint8_t sd_status[64]; - uint32_t vhs; - bool wp_switch; - unsigned long *wp_groups; - 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]; - uint32_t pwd_len; - uint8_t function_group[6]; - - bool spi; - uint8_t current_cmd; - /* True if we will handle the next command as an ACMD. Note that this does - * *not* track the APP_CMD status bit! - */ - bool expecting_acmd; - uint32_t blk_written; - uint64_t data_start; - uint32_t data_offset; - uint8_t data[512]; - qemu_irq readonly_cb; - qemu_irq inserted_cb; - BlockBackend *blk; - uint8_t *buf; - - bool enable; -}; - -static void sd_set_mode(SDState *sd) -{ - switch (sd->state) { - case sd_inactive_state: - sd->mode = sd_inactive; - break; - - case sd_idle_state: - case sd_ready_state: - case sd_identification_state: - sd->mode = sd_card_identification_mode; - break; - - case sd_standby_state: - case sd_transfer_state: - case sd_sendingdata_state: - case sd_receivingdata_state: - case sd_programming_state: - case sd_disconnect_state: - sd->mode = sd_data_transfer_mode; - break; - } -} - -static const sd_cmd_type_t sd_cmd_type[64] = { - sd_bc, sd_none, sd_bcr, sd_bcr, sd_none, sd_none, sd_none, sd_ac, - sd_bcr, sd_ac, sd_ac, sd_adtc, sd_ac, sd_ac, sd_none, sd_ac, - sd_ac, sd_adtc, sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, - sd_adtc, sd_adtc, sd_adtc, sd_adtc, sd_ac, sd_ac, sd_adtc, sd_none, - sd_ac, sd_ac, sd_none, sd_none, sd_none, sd_none, sd_ac, sd_none, - sd_none, sd_none, sd_bc, sd_none, sd_none, sd_none, sd_none, sd_none, - sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_ac, - sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, -}; - -static const int sd_cmd_class[64] = { - 0, 0, 0, 0, 0, 9, 10, 0, 0, 0, 0, 1, 0, 0, 0, 0, - 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 6, 6, 6, 6, - 5, 5, 10, 10, 10, 10, 5, 9, 9, 9, 7, 7, 7, 7, 7, 7, - 7, 7, 10, 7, 9, 9, 9, 8, 8, 10, 8, 8, 8, 8, 8, 8, -}; - -static uint8_t sd_crc7(void *message, size_t width) -{ - int i, bit; - uint8_t shift_reg = 0x00; - uint8_t *msg = (uint8_t *) message; - - for (i = 0; i < width; i ++, msg ++) - for (bit = 7; bit >= 0; bit --) { - shift_reg <<= 1; - if ((shift_reg >> 7) ^ ((*msg >> bit) & 1)) - shift_reg ^= 0x89; - } - - return shift_reg; -} - -static uint16_t sd_crc16(void *message, size_t width) -{ - int i, bit; - uint16_t shift_reg = 0x0000; - uint16_t *msg = (uint16_t *) message; - width <<= 1; - - for (i = 0; i < width; i ++, msg ++) - for (bit = 15; bit >= 0; bit --) { - shift_reg <<= 1; - if ((shift_reg >> 15) ^ ((*msg >> bit) & 1)) - shift_reg ^= 0x1011; - } - - return shift_reg; -} - -static void sd_set_ocr(SDState *sd) -{ - /* 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) -{ - sd->scr[0] = 0x00; /* SCR Structure */ - sd->scr[1] = 0x2f; /* SD Security Support */ - sd->scr[2] = 0x00; - sd->scr[3] = 0x00; - sd->scr[4] = 0x00; - sd->scr[5] = 0x00; - sd->scr[6] = 0x00; - sd->scr[7] = 0x00; -} - -#define MID 0xaa -#define OID "XY" -#define PNM "QEMU!" -#define PRV 0x01 -#define MDT_YR 2006 -#define MDT_MON 2 - -static void sd_set_cid(SDState *sd) -{ - sd->cid[0] = MID; /* Fake card manufacturer ID (MID) */ - sd->cid[1] = OID[0]; /* OEM/Application ID (OID) */ - sd->cid[2] = OID[1]; - sd->cid[3] = PNM[0]; /* Fake product name (PNM) */ - sd->cid[4] = PNM[1]; - sd->cid[5] = PNM[2]; - sd->cid[6] = PNM[3]; - sd->cid[7] = PNM[4]; - sd->cid[8] = PRV; /* Fake product revision (PRV) */ - sd->cid[9] = 0xde; /* Fake serial number (PSN) */ - sd->cid[10] = 0xad; - sd->cid[11] = 0xbe; - sd->cid[12] = 0xef; - sd->cid[13] = 0x00 | /* Manufacture date (MDT) */ - ((MDT_YR - 2000) / 10); - sd->cid[14] = ((MDT_YR % 10) << 4) | MDT_MON; - sd->cid[15] = (sd_crc7(sd->cid, 15) << 1) | 1; -} - -#define HWBLOCK_SHIFT 9 /* 512 bytes */ -#define SECTOR_SHIFT 5 /* 16 kilobytes */ -#define WPGROUP_SHIFT 7 /* 2 megs */ -#define CMULT_SHIFT 9 /* 512 times HWBLOCK_SIZE */ -#define WPGROUP_SIZE (1 << (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) - -static const uint8_t sd_csd_rw_mask[16] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfe, -}; - -static void sd_set_csd(SDState *sd, uint64_t size) -{ - uint32_t csize = (size >> (CMULT_SHIFT + HWBLOCK_SHIFT)) - 1; - uint32_t sectsize = (1 << (SECTOR_SHIFT + 1)) - 1; - uint32_t wpsize = (1 << (WPGROUP_SHIFT + 1)) - 1; - - if (size <= 0x40000000) { /* Standard Capacity SD */ - sd->csd[0] = 0x00; /* CSD structure */ - sd->csd[1] = 0x26; /* Data read access-time-1 */ - sd->csd[2] = 0x00; /* Data read access-time-2 */ - sd->csd[3] = 0x5a; /* Max. data transfer rate */ - sd->csd[4] = 0x5f; /* Card Command Classes */ - sd->csd[5] = 0x50 | /* Max. read data block length */ - HWBLOCK_SHIFT; - sd->csd[6] = 0xe0 | /* Partial block for read allowed */ - ((csize >> 10) & 0x03); - sd->csd[7] = 0x00 | /* Device size */ - ((csize >> 2) & 0xff); - sd->csd[8] = 0x3f | /* Max. read current */ - ((csize << 6) & 0xc0); - sd->csd[9] = 0xfc | /* Max. write current */ - ((CMULT_SHIFT - 2) >> 1); - sd->csd[10] = 0x40 | /* Erase sector size */ - (((CMULT_SHIFT - 2) << 7) & 0x80) | (sectsize >> 1); - sd->csd[11] = 0x00 | /* Write protect group size */ - ((sectsize << 7) & 0x80) | wpsize; - sd->csd[12] = 0x90 | /* Write speed factor */ - (HWBLOCK_SHIFT >> 2); - sd->csd[13] = 0x20 | /* Max. write data block length */ - ((HWBLOCK_SHIFT << 6) & 0xc0); - sd->csd[14] = 0x00; /* File format group */ - sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1; - } else { /* SDHC */ - size /= 512 * 1024; - size -= 1; - sd->csd[0] = 0x40; - sd->csd[1] = 0x0e; - sd->csd[2] = 0x00; - sd->csd[3] = 0x32; - sd->csd[4] = 0x5b; - sd->csd[5] = 0x59; - sd->csd[6] = 0x00; - sd->csd[7] = (size >> 16) & 0xff; - sd->csd[8] = (size >> 8) & 0xff; - sd->csd[9] = (size & 0xff); - sd->csd[10] = 0x7f; - sd->csd[11] = 0x80; - sd->csd[12] = 0x0a; - sd->csd[13] = 0x40; - sd->csd[14] = 0x00; - sd->csd[15] = 0x00; - sd->ocr |= 1 << 30; /* High Capacity SD Memory Card */ - } -} - -static void sd_set_rca(SDState *sd) -{ - sd->rca += 0x4567; -} - -/* Card status bits, split by clear condition: - * A : According to the card current state - * B : Always related to the previous command - * C : Cleared by read - */ -#define CARD_STATUS_A 0x02004100 -#define CARD_STATUS_B 0x00c01e00 -#define CARD_STATUS_C 0xfd39a028 - -static void sd_set_cardstatus(SDState *sd) -{ - sd->card_status = 0x00000100; -} - -static void sd_set_sdstatus(SDState *sd) -{ - memset(sd->sd_status, 0, 64); -} - -static int sd_req_crc_validate(SDRequest *req) -{ - uint8_t buffer[5]; - buffer[0] = 0x40 | req->cmd; - buffer[1] = (req->arg >> 24) & 0xff; - buffer[2] = (req->arg >> 16) & 0xff; - buffer[3] = (req->arg >> 8) & 0xff; - buffer[4] = (req->arg >> 0) & 0xff; - return 0; - return sd_crc7(buffer, 5) != req->crc; /* TODO */ -} - -static void sd_response_r1_make(SDState *sd, uint8_t *response) -{ - uint32_t status = sd->card_status; - /* Clear the "clear on read" status bits */ - sd->card_status &= ~CARD_STATUS_C; - - response[0] = (status >> 24) & 0xff; - response[1] = (status >> 16) & 0xff; - response[2] = (status >> 8) & 0xff; - response[3] = (status >> 0) & 0xff; -} - -static void sd_response_r3_make(SDState *sd, uint8_t *response) -{ - response[0] = (sd->ocr >> 24) & 0xff; - response[1] = (sd->ocr >> 16) & 0xff; - response[2] = (sd->ocr >> 8) & 0xff; - response[3] = (sd->ocr >> 0) & 0xff; -} - -static void sd_response_r6_make(SDState *sd, uint8_t *response) -{ - uint16_t arg; - uint16_t status; - - arg = sd->rca; - status = ((sd->card_status >> 8) & 0xc000) | - ((sd->card_status >> 6) & 0x2000) | - (sd->card_status & 0x1fff); - sd->card_status &= ~(CARD_STATUS_C & 0xc81fff); - - response[0] = (arg >> 8) & 0xff; - response[1] = arg & 0xff; - response[2] = (status >> 8) & 0xff; - response[3] = status & 0xff; -} - -static void sd_response_r7_make(SDState *sd, uint8_t *response) -{ - response[0] = (sd->vhs >> 24) & 0xff; - response[1] = (sd->vhs >> 16) & 0xff; - response[2] = (sd->vhs >> 8) & 0xff; - response[3] = (sd->vhs >> 0) & 0xff; -} - -static inline uint64_t sd_addr_to_wpnum(uint64_t addr) -{ - return addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT); -} - -static void sd_reset(DeviceState *dev) -{ - SDState *sd = SD_CARD(dev); - uint64_t size; - uint64_t sect; - - if (sd->blk) { - blk_get_geometry(sd->blk, §); - } else { - sect = 0; - } - size = sect << 9; - - sect = sd_addr_to_wpnum(size) + 1; - - sd->state = sd_idle_state; - sd->rca = 0x0000; - sd_set_ocr(sd); - sd_set_scr(sd); - sd_set_cid(sd); - sd_set_csd(sd, size); - sd_set_cardstatus(sd); - sd_set_sdstatus(sd); - - 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); - memset(sd->function_group, 0, sizeof(sd->function_group)); - sd->erase_start = 0; - sd->erase_end = 0; - sd->size = size; - 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); - - 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); - } - } -} - -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), - VMSTATE_UINT8_ARRAY(cid, SDState, 16), - VMSTATE_UINT8_ARRAY(csd, SDState, 16), - VMSTATE_UINT16(rca, SDState), - VMSTATE_UINT32(card_status, SDState), - VMSTATE_PARTIAL_BUFFER(sd_status, SDState, 1), - 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), - VMSTATE_UINT32(pwd_len, SDState), - VMSTATE_UINT8_ARRAY(function_group, SDState, 6), - VMSTATE_UINT8(current_cmd, SDState), - VMSTATE_BOOL(expecting_acmd, SDState), - VMSTATE_UINT32(blk_written, SDState), - VMSTATE_UINT64(data_start, SDState), - VMSTATE_UINT32(data_offset, SDState), - VMSTATE_UINT8_ARRAY(data, SDState, 512), - VMSTATE_BUFFER_POINTER_UNSAFE(buf, SDState, 1, 512), - VMSTATE_BOOL(enable, SDState), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &sd_ocr_vmstate, - NULL - }, -}; - -/* Legacy initialization function for use by non-qdevified callers */ -SDState *sd_init(BlockBackend *blk, bool is_spi) -{ - 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; - } - 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; - } - - return SD_CARD(dev); -} - -void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert) -{ - sd->readonly_cb = readonly; - sd->inserted_cb = insert; - qemu_set_irq(readonly, sd->blk ? blk_is_read_only(sd->blk) : 0); - qemu_set_irq(insert, sd->blk ? blk_is_inserted(sd->blk) : 0); -} - -static void sd_erase(SDState *sd) -{ - int i; - uint64_t erase_start = sd->erase_start; - uint64_t erase_end = sd->erase_end; - - if (!sd->erase_start || !sd->erase_end) { - sd->card_status |= ERASE_SEQ_ERROR; - return; - } - - if (extract32(sd->ocr, OCR_CCS_BITN, 1)) { - /* High capacity memory card: erase units are 512 byte blocks */ - erase_start *= 512; - erase_end *= 512; - } - - erase_start = sd_addr_to_wpnum(erase_start); - erase_end = sd_addr_to_wpnum(erase_end); - sd->erase_start = 0; - sd->erase_end = 0; - sd->csd[14] |= 0x40; - - for (i = erase_start; i <= erase_end; i++) { - if (test_bit(i, sd->wp_groups)) { - sd->card_status |= WP_ERASE_SKIP; - } - } -} - -static uint32_t sd_wpbits(SDState *sd, uint64_t addr) -{ - uint32_t i, wpnum; - uint32_t ret = 0; - - wpnum = sd_addr_to_wpnum(addr); - - for (i = 0; i < 32; i++, wpnum++, addr += WPGROUP_SIZE) { - if (addr < sd->size && test_bit(wpnum, sd->wp_groups)) { - ret |= (1 << i); - } - } - - return ret; -} - -static void sd_function_switch(SDState *sd, uint32_t arg) -{ - int i, mode, new_func, crc; - mode = !!(arg & 0x80000000); - - sd->data[0] = 0x00; /* Maximum current consumption */ - sd->data[1] = 0x01; - sd->data[2] = 0x80; /* Supported group 6 functions */ - sd->data[3] = 0x01; - sd->data[4] = 0x80; /* Supported group 5 functions */ - sd->data[5] = 0x01; - sd->data[6] = 0x80; /* Supported group 4 functions */ - sd->data[7] = 0x01; - sd->data[8] = 0x80; /* Supported group 3 functions */ - sd->data[9] = 0x01; - sd->data[10] = 0x80; /* Supported group 2 functions */ - sd->data[11] = 0x43; - sd->data[12] = 0x80; /* Supported group 1 functions */ - sd->data[13] = 0x03; - for (i = 0; i < 6; i ++) { - new_func = (arg >> (i * 4)) & 0x0f; - if (mode && new_func != 0x0f) - sd->function_group[i] = new_func; - sd->data[14 + (i >> 1)] = new_func << ((i * 4) & 4); - } - memset(&sd->data[17], 0, 47); - crc = sd_crc16(sd->data, 64); - sd->data[65] = crc >> 8; - sd->data[66] = crc & 0xff; -} - -static inline bool sd_wp_addr(SDState *sd, uint64_t addr) -{ - return test_bit(sd_addr_to_wpnum(addr), sd->wp_groups); -} - -static void sd_lock_command(SDState *sd) -{ - int erase, lock, clr_pwd, set_pwd, pwd_len; - erase = !!(sd->data[0] & 0x08); - lock = sd->data[0] & 0x04; - clr_pwd = sd->data[0] & 0x02; - set_pwd = sd->data[0] & 0x01; - - if (sd->blk_len > 1) - pwd_len = sd->data[1]; - else - pwd_len = 0; - - if (erase) { - if (!(sd->card_status & CARD_IS_LOCKED) || sd->blk_len > 1 || - set_pwd || clr_pwd || lock || sd->wp_switch || - (sd->csd[14] & 0x20)) { - sd->card_status |= LOCK_UNLOCK_FAILED; - return; - } - bitmap_zero(sd->wp_groups, sd->wpgrps_size); - sd->csd[14] &= ~0x10; - sd->card_status &= ~CARD_IS_LOCKED; - sd->pwd_len = 0; - /* Erasing the entire card here! */ - fprintf(stderr, "SD: Card force-erased by CMD42\n"); - return; - } - - if (sd->blk_len < 2 + pwd_len || - pwd_len <= sd->pwd_len || - pwd_len > sd->pwd_len + 16) { - sd->card_status |= LOCK_UNLOCK_FAILED; - return; - } - - if (sd->pwd_len && memcmp(sd->pwd, sd->data + 2, sd->pwd_len)) { - sd->card_status |= LOCK_UNLOCK_FAILED; - return; - } - - pwd_len -= sd->pwd_len; - if ((pwd_len && !set_pwd) || - (clr_pwd && (set_pwd || lock)) || - (lock && !sd->pwd_len && !set_pwd) || - (!set_pwd && !clr_pwd && - (((sd->card_status & CARD_IS_LOCKED) && lock) || - (!(sd->card_status & CARD_IS_LOCKED) && !lock)))) { - sd->card_status |= LOCK_UNLOCK_FAILED; - return; - } - - if (set_pwd) { - memcpy(sd->pwd, sd->data + 2 + sd->pwd_len, pwd_len); - sd->pwd_len = pwd_len; - } - - if (clr_pwd) { - sd->pwd_len = 0; - } - - if (lock) - sd->card_status |= CARD_IS_LOCKED; - else - sd->card_status &= ~CARD_IS_LOCKED; -} - -static sd_rsp_type_t sd_normal_command(SDState *sd, - SDRequest req) -{ - uint32_t rca = 0x0000; - uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg; - - /* Not interpreting this as an app command */ - sd->card_status &= ~APP_CMD; - - 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) { - /* Basic commands (Class 0 and Class 1) */ - case 0: /* CMD0: GO_IDLE_STATE */ - switch (sd->state) { - case sd_inactive_state: - return sd->spi ? sd_r1 : sd_r0; - - default: - sd->state = sd_idle_state; - sd_reset(DEVICE(sd)); - return sd->spi ? sd_r1 : sd_r0; - } - break; - - case 1: /* CMD1: SEND_OP_CMD */ - if (!sd->spi) - goto bad_cmd; - - sd->state = sd_transfer_state; - return sd_r1; - - case 2: /* CMD2: ALL_SEND_CID */ - if (sd->spi) - goto bad_cmd; - switch (sd->state) { - case sd_ready_state: - sd->state = sd_identification_state; - return sd_r2_i; - - default: - break; - } - break; - - case 3: /* CMD3: SEND_RELATIVE_ADDR */ - if (sd->spi) - goto bad_cmd; - switch (sd->state) { - case sd_identification_state: - case sd_standby_state: - sd->state = sd_standby_state; - sd_set_rca(sd); - return sd_r6; - - default: - break; - } - break; - - case 4: /* CMD4: SEND_DSR */ - if (sd->spi) - goto bad_cmd; - switch (sd->state) { - case sd_standby_state: - break; - - default: - break; - } - break; - - case 5: /* CMD5: reserved for SDIO cards */ - return sd_illegal; - - case 6: /* CMD6: SWITCH_FUNCTION */ - if (sd->spi) - goto bad_cmd; - switch (sd->mode) { - case sd_data_transfer_mode: - sd_function_switch(sd, req.arg); - sd->state = sd_sendingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - - case 7: /* CMD7: SELECT/DESELECT_CARD */ - if (sd->spi) - goto bad_cmd; - switch (sd->state) { - case sd_standby_state: - if (sd->rca != rca) - return sd_r0; - - sd->state = sd_transfer_state; - return sd_r1b; - - case sd_transfer_state: - case sd_sendingdata_state: - if (sd->rca == rca) - break; - - sd->state = sd_standby_state; - return sd_r1b; - - case sd_disconnect_state: - if (sd->rca != rca) - return sd_r0; - - sd->state = sd_programming_state; - return sd_r1b; - - case sd_programming_state: - if (sd->rca == rca) - break; - - sd->state = sd_disconnect_state; - return sd_r1b; - - default: - break; - } - break; - - case 8: /* CMD8: SEND_IF_COND */ - /* Physical Layer Specification Version 2.00 command */ - switch (sd->state) { - case sd_idle_state: - sd->vhs = 0; - - /* No response if not exactly one VHS bit is set. */ - if (!(req.arg >> 8) || (req.arg >> (ctz32(req.arg & ~0xff) + 1))) { - return sd->spi ? sd_r7 : sd_r0; - } - - /* Accept. */ - sd->vhs = req.arg; - return sd_r7; - - default: - break; - } - break; - - case 9: /* CMD9: SEND_CSD */ - switch (sd->state) { - case sd_standby_state: - if (sd->rca != rca) - return sd_r0; - - return sd_r2_s; - - case sd_transfer_state: - if (!sd->spi) - break; - sd->state = sd_sendingdata_state; - memcpy(sd->data, sd->csd, 16); - sd->data_start = addr; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - - case 10: /* CMD10: SEND_CID */ - switch (sd->state) { - case sd_standby_state: - if (sd->rca != rca) - return sd_r0; - - return sd_r2_i; - - case sd_transfer_state: - if (!sd->spi) - break; - sd->state = sd_sendingdata_state; - memcpy(sd->data, sd->cid, 16); - sd->data_start = addr; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - - case 11: /* CMD11: READ_DAT_UNTIL_STOP */ - if (sd->spi) - goto bad_cmd; - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_sendingdata_state; - sd->data_start = req.arg; - sd->data_offset = 0; - - if (sd->data_start + sd->blk_len > sd->size) - sd->card_status |= ADDRESS_ERROR; - return sd_r0; - - default: - break; - } - break; - - case 12: /* CMD12: STOP_TRANSMISSION */ - switch (sd->state) { - case sd_sendingdata_state: - sd->state = sd_transfer_state; - return sd_r1b; - - case sd_receivingdata_state: - sd->state = sd_programming_state; - /* Bzzzzzzztt .... Operation complete. */ - sd->state = sd_transfer_state; - return sd_r1b; - - default: - break; - } - break; - - case 13: /* CMD13: SEND_STATUS */ - switch (sd->mode) { - case sd_data_transfer_mode: - if (sd->rca != rca) - return sd_r0; - - return sd_r1; - - default: - break; - } - break; - - case 15: /* CMD15: GO_INACTIVE_STATE */ - if (sd->spi) - goto bad_cmd; - switch (sd->mode) { - case sd_data_transfer_mode: - if (sd->rca != rca) - return sd_r0; - - sd->state = sd_inactive_state; - return sd_r0; - - default: - break; - } - break; - - /* Block read commands (Classs 2) */ - case 16: /* CMD16: SET_BLOCKLEN */ - switch (sd->state) { - case sd_transfer_state: - if (req.arg > (1 << HWBLOCK_SHIFT)) - sd->card_status |= BLOCK_LEN_ERROR; - else - sd->blk_len = req.arg; - - return sd_r1; - - default: - break; - } - break; - - case 17: /* CMD17: READ_SINGLE_BLOCK */ - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_sendingdata_state; - sd->data_start = addr; - sd->data_offset = 0; - - if (sd->data_start + sd->blk_len > sd->size) - sd->card_status |= ADDRESS_ERROR; - return sd_r1; - - default: - break; - } - break; - - case 18: /* CMD18: READ_MULTIPLE_BLOCK */ - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_sendingdata_state; - sd->data_start = addr; - sd->data_offset = 0; - - if (sd->data_start + sd->blk_len > sd->size) - sd->card_status |= ADDRESS_ERROR; - return sd_r1; - - default: - break; - } - 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) - goto unimplemented_cmd; - switch (sd->state) { - case sd_transfer_state: - /* Writing in SPI mode not implemented. */ - if (sd->spi) - break; - sd->state = sd_receivingdata_state; - sd->data_start = addr; - sd->data_offset = 0; - sd->blk_written = 0; - - if (sd->data_start + sd->blk_len > sd->size) - sd->card_status |= ADDRESS_ERROR; - if (sd_wp_addr(sd, sd->data_start)) - sd->card_status |= WP_VIOLATION; - if (sd->csd[14] & 0x30) - sd->card_status |= WP_VIOLATION; - return sd_r1; - - default: - break; - } - break; - - case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ - if (sd->spi) - goto unimplemented_cmd; - switch (sd->state) { - case sd_transfer_state: - /* Writing in SPI mode not implemented. */ - if (sd->spi) - break; - sd->state = sd_receivingdata_state; - sd->data_start = addr; - sd->data_offset = 0; - sd->blk_written = 0; - - if (sd->data_start + sd->blk_len > sd->size) - sd->card_status |= ADDRESS_ERROR; - if (sd_wp_addr(sd, sd->data_start)) - sd->card_status |= WP_VIOLATION; - if (sd->csd[14] & 0x30) - sd->card_status |= WP_VIOLATION; - return sd_r1; - - default: - break; - } - break; - - case 26: /* CMD26: PROGRAM_CID */ - if (sd->spi) - goto bad_cmd; - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_receivingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - - case 27: /* CMD27: PROGRAM_CSD */ - if (sd->spi) - goto unimplemented_cmd; - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_receivingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - - /* Write protection (Class 6) */ - case 28: /* CMD28: SET_WRITE_PROT */ - switch (sd->state) { - case sd_transfer_state: - if (addr >= sd->size) { - sd->card_status |= ADDRESS_ERROR; - return sd_r1b; - } - - sd->state = sd_programming_state; - set_bit(sd_addr_to_wpnum(addr), sd->wp_groups); - /* Bzzzzzzztt .... Operation complete. */ - sd->state = sd_transfer_state; - return sd_r1b; - - default: - break; - } - break; - - case 29: /* CMD29: CLR_WRITE_PROT */ - switch (sd->state) { - case sd_transfer_state: - if (addr >= sd->size) { - sd->card_status |= ADDRESS_ERROR; - return sd_r1b; - } - - sd->state = sd_programming_state; - clear_bit(sd_addr_to_wpnum(addr), sd->wp_groups); - /* Bzzzzzzztt .... Operation complete. */ - sd->state = sd_transfer_state; - return sd_r1b; - - default: - break; - } - break; - - case 30: /* CMD30: SEND_WRITE_PROT */ - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_sendingdata_state; - *(uint32_t *) sd->data = sd_wpbits(sd, req.arg); - sd->data_start = addr; - sd->data_offset = 0; - return sd_r1b; - - default: - break; - } - break; - - /* Erase commands (Class 5) */ - case 32: /* CMD32: ERASE_WR_BLK_START */ - switch (sd->state) { - case sd_transfer_state: - sd->erase_start = req.arg; - return sd_r1; - - default: - break; - } - break; - - case 33: /* CMD33: ERASE_WR_BLK_END */ - switch (sd->state) { - case sd_transfer_state: - sd->erase_end = req.arg; - return sd_r1; - - default: - break; - } - break; - - case 38: /* CMD38: ERASE */ - switch (sd->state) { - case sd_transfer_state: - if (sd->csd[14] & 0x30) { - sd->card_status |= WP_VIOLATION; - return sd_r1b; - } - - sd->state = sd_programming_state; - sd_erase(sd); - /* Bzzzzzzztt .... Operation complete. */ - sd->state = sd_transfer_state; - return sd_r1b; - - default: - break; - } - break; - - /* Lock card commands (Class 7) */ - case 42: /* CMD42: LOCK_UNLOCK */ - if (sd->spi) - goto unimplemented_cmd; - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_receivingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - - case 52: - case 53: - /* CMD52, CMD53: reserved for SDIO cards - * (see the SDIO Simplified Specification V2.0) - * Handle as illegal command but do not complain - * on stderr, as some OSes may use these in their - * probing for presence of an SDIO card. - */ - return sd_illegal; - - /* Application specific commands (Class 8) */ - case 55: /* CMD55: APP_CMD */ - if (sd->rca != rca) - return sd_r0; - - sd->expecting_acmd = true; - sd->card_status |= APP_CMD; - return sd_r1; - - case 56: /* CMD56: GEN_CMD */ - fprintf(stderr, "SD: GEN_CMD 0x%08x\n", req.arg); - - switch (sd->state) { - case sd_transfer_state: - sd->data_offset = 0; - if (req.arg & 1) - sd->state = sd_sendingdata_state; - else - sd->state = sd_receivingdata_state; - return sd_r1; - - default: - break; - } - break; - - default: - bad_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. */ - qemu_log_mask(LOG_UNIMP, "SD: CMD%i not implemented in SPI mode\n", - req.cmd); - return sd_illegal; - } - - qemu_log_mask(LOG_GUEST_ERROR, "SD: CMD%i in a wrong state\n", req.cmd); - return sd_illegal; -} - -static sd_rsp_type_t sd_app_command(SDState *sd, - SDRequest req) -{ - DPRINTF("ACMD%d 0x%08x\n", req.cmd, req.arg); - sd->card_status |= APP_CMD; - switch (req.cmd) { - case 6: /* ACMD6: SET_BUS_WIDTH */ - switch (sd->state) { - case sd_transfer_state: - sd->sd_status[0] &= 0x3f; - sd->sd_status[0] |= (req.arg & 0x03) << 6; - return sd_r1; - - default: - break; - } - break; - - case 13: /* ACMD13: SD_STATUS */ - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_sendingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - - case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ - switch (sd->state) { - case sd_transfer_state: - *(uint32_t *) sd->data = sd->blk_written; - - sd->state = sd_sendingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - - case 23: /* ACMD23: SET_WR_BLK_ERASE_COUNT */ - switch (sd->state) { - case sd_transfer_state: - return sd_r1; - - default: - break; - } - break; - - case 41: /* ACMD41: SD_APP_OP_COND */ - if (sd->spi) { - /* SEND_OP_CMD */ - sd->state = sd_transfer_state; - return sd_r1; - } - 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. - * - * 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) { - sd->state = sd_ready_state; - } - - return sd_r3; - - default: - break; - } - break; - - case 42: /* ACMD42: SET_CLR_CARD_DETECT */ - switch (sd->state) { - case sd_transfer_state: - /* Bringing in the 50KOhm pull-up resistor... Done. */ - return sd_r1; - - default: - break; - } - break; - - case 51: /* ACMD51: SEND_SCR */ - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_sendingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - - default: - /* Fall back to standard commands. */ - return sd_normal_command(sd, req); - } - - qemu_log_mask(LOG_GUEST_ERROR, "SD: ACMD%i in a wrong state\n", req.cmd); - return sd_illegal; -} - -static int cmd_valid_while_locked(SDState *sd, SDRequest *req) -{ - /* Valid commands in locked state: - * basic class (0) - * lock card class (7) - * CMD16 - * implicitly, the ACMD prefix CMD55 - * ACMD41 and ACMD42 - * Anything else provokes an "illegal command" response. - */ - if (sd->expecting_acmd) { - return req->cmd == 41 || req->cmd == 42; - } - if (req->cmd == 16 || req->cmd == 55) { - return 1; - } - return sd_cmd_class[req->cmd & 0x3F] == 0 - || sd_cmd_class[req->cmd & 0x3F] == 7; -} - -int sd_do_command(SDState *sd, SDRequest *req, - uint8_t *response) { - int last_state; - sd_rsp_type_t rtype; - int rsplen; - - if (!sd->blk || !blk_is_inserted(sd->blk) || !sd->enable) { - return 0; - } - - if (sd_req_crc_validate(req)) { - sd->card_status |= COM_CRC_ERROR; - rtype = sd_illegal; - goto send_response; - } - - if (sd->card_status & CARD_IS_LOCKED) { - if (!cmd_valid_while_locked(sd, req)) { - sd->card_status |= ILLEGAL_COMMAND; - sd->expecting_acmd = false; - qemu_log_mask(LOG_GUEST_ERROR, "SD: Card is locked\n"); - rtype = sd_illegal; - goto send_response; - } - } - - last_state = sd->state; - sd_set_mode(sd); - - if (sd->expecting_acmd) { - sd->expecting_acmd = false; - rtype = sd_app_command(sd, *req); - } else { - rtype = sd_normal_command(sd, *req); - } - - if (rtype == sd_illegal) { - sd->card_status |= ILLEGAL_COMMAND; - } else { - /* Valid command, we can update the 'state before command' bits. - * (Do this now so they appear in r1 responses.) - */ - sd->current_cmd = req->cmd; - sd->card_status &= ~CURRENT_STATE; - sd->card_status |= (last_state << 9); - } - -send_response: - switch (rtype) { - case sd_r1: - case sd_r1b: - sd_response_r1_make(sd, response); - rsplen = 4; - break; - - case sd_r2_i: - memcpy(response, sd->cid, sizeof(sd->cid)); - rsplen = 16; - break; - - case sd_r2_s: - memcpy(response, sd->csd, sizeof(sd->csd)); - rsplen = 16; - break; - - case sd_r3: - sd_response_r3_make(sd, response); - rsplen = 4; - break; - - case sd_r6: - sd_response_r6_make(sd, response); - rsplen = 4; - break; - - case sd_r7: - sd_response_r7_make(sd, response); - rsplen = 4; - break; - - case sd_r0: - case sd_illegal: - default: - rsplen = 0; - break; - } - - if (rtype != sd_illegal) { - /* Clear the "clear on valid command" status bits now we've - * sent any response - */ - sd->card_status &= ~CARD_STATUS_B; - } - -#ifdef DEBUG_SD - if (rsplen) { - int i; - DPRINTF("Response:"); - for (i = 0; i < rsplen; i++) - fprintf(stderr, " %02x", response[i]); - fprintf(stderr, " state %d\n", sd->state); - } else { - DPRINTF("No response %d\n", sd->state); - } -#endif - - return rsplen; -} - -static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len) -{ - uint64_t end = addr + len; - - DPRINTF("sd_blk_read: addr = 0x%08llx, len = %d\n", - (unsigned long long) addr, len); - if (!sd->blk || blk_read(sd->blk, addr >> 9, sd->buf, 1) < 0) { - fprintf(stderr, "sd_blk_read: read error on host side\n"); - return; - } - - if (end > (addr & ~511) + 512) { - memcpy(sd->data, sd->buf + (addr & 511), 512 - (addr & 511)); - - if (blk_read(sd->blk, end >> 9, sd->buf, 1) < 0) { - fprintf(stderr, "sd_blk_read: read error on host side\n"); - return; - } - memcpy(sd->data + 512 - (addr & 511), sd->buf, end & 511); - } else - memcpy(sd->data, sd->buf + (addr & 511), len); -} - -static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len) -{ - uint64_t end = addr + len; - - if ((addr & 511) || len < 512) - if (!sd->blk || blk_read(sd->blk, addr >> 9, sd->buf, 1) < 0) { - fprintf(stderr, "sd_blk_write: read error on host side\n"); - return; - } - - if (end > (addr & ~511) + 512) { - memcpy(sd->buf + (addr & 511), sd->data, 512 - (addr & 511)); - if (blk_write(sd->blk, addr >> 9, sd->buf, 1) < 0) { - fprintf(stderr, "sd_blk_write: write error on host side\n"); - return; - } - - if (blk_read(sd->blk, end >> 9, sd->buf, 1) < 0) { - fprintf(stderr, "sd_blk_write: read error on host side\n"); - return; - } - memcpy(sd->buf, sd->data + 512 - (addr & 511), end & 511); - if (blk_write(sd->blk, end >> 9, sd->buf, 1) < 0) { - fprintf(stderr, "sd_blk_write: write error on host side\n"); - } - } else { - memcpy(sd->buf + (addr & 511), sd->data, len); - if (!sd->blk || blk_write(sd->blk, addr >> 9, sd->buf, 1) < 0) { - fprintf(stderr, "sd_blk_write: write error on host side\n"); - } - } -} - -#define BLK_READ_BLOCK(a, len) sd_blk_read(sd, a, len) -#define BLK_WRITE_BLOCK(a, len) sd_blk_write(sd, a, len) -#define APP_READ_BLOCK(a, len) memset(sd->data, 0xec, len) -#define APP_WRITE_BLOCK(a, len) - -void sd_write_data(SDState *sd, uint8_t value) -{ - int i; - - if (!sd->blk || !blk_is_inserted(sd->blk) || !sd->enable) - return; - - if (sd->state != sd_receivingdata_state) { - qemu_log_mask(LOG_GUEST_ERROR, - "sd_write_data: not in Receiving-Data state\n"); - return; - } - - if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION)) - return; - - switch (sd->current_cmd) { - case 24: /* CMD24: WRITE_SINGLE_BLOCK */ - sd->data[sd->data_offset ++] = value; - if (sd->data_offset >= sd->blk_len) { - /* TODO: Check CRC before committing */ - sd->state = sd_programming_state; - BLK_WRITE_BLOCK(sd->data_start, sd->data_offset); - sd->blk_written ++; - sd->csd[14] |= 0x40; - /* Bzzzzzzztt .... Operation complete. */ - sd->state = sd_transfer_state; - } - break; - - case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ - if (sd->data_offset == 0) { - /* Start of the block - let's check the address is valid */ - if (sd->data_start + sd->blk_len > sd->size) { - sd->card_status |= ADDRESS_ERROR; - break; - } - if (sd_wp_addr(sd, sd->data_start)) { - sd->card_status |= WP_VIOLATION; - break; - } - } - sd->data[sd->data_offset++] = value; - if (sd->data_offset >= sd->blk_len) { - /* TODO: Check CRC before committing */ - sd->state = sd_programming_state; - BLK_WRITE_BLOCK(sd->data_start, sd->data_offset); - sd->blk_written++; - sd->data_start += sd->blk_len; - sd->data_offset = 0; - 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; - - case 26: /* CMD26: PROGRAM_CID */ - sd->data[sd->data_offset ++] = value; - if (sd->data_offset >= sizeof(sd->cid)) { - /* TODO: Check CRC before committing */ - sd->state = sd_programming_state; - for (i = 0; i < sizeof(sd->cid); i ++) - if ((sd->cid[i] | 0x00) != sd->data[i]) - sd->card_status |= CID_CSD_OVERWRITE; - - if (!(sd->card_status & CID_CSD_OVERWRITE)) - for (i = 0; i < sizeof(sd->cid); i ++) { - sd->cid[i] |= 0x00; - sd->cid[i] &= sd->data[i]; - } - /* Bzzzzzzztt .... Operation complete. */ - sd->state = sd_transfer_state; - } - break; - - case 27: /* CMD27: PROGRAM_CSD */ - sd->data[sd->data_offset ++] = value; - if (sd->data_offset >= sizeof(sd->csd)) { - /* TODO: Check CRC before committing */ - sd->state = sd_programming_state; - for (i = 0; i < sizeof(sd->csd); i ++) - if ((sd->csd[i] | sd_csd_rw_mask[i]) != - (sd->data[i] | sd_csd_rw_mask[i])) - sd->card_status |= CID_CSD_OVERWRITE; - - /* Copy flag (OTP) & Permanent write protect */ - if (sd->csd[14] & ~sd->data[14] & 0x60) - sd->card_status |= CID_CSD_OVERWRITE; - - if (!(sd->card_status & CID_CSD_OVERWRITE)) - for (i = 0; i < sizeof(sd->csd); i ++) { - sd->csd[i] |= sd_csd_rw_mask[i]; - sd->csd[i] &= sd->data[i]; - } - /* Bzzzzzzztt .... Operation complete. */ - sd->state = sd_transfer_state; - } - break; - - case 42: /* CMD42: LOCK_UNLOCK */ - sd->data[sd->data_offset ++] = value; - if (sd->data_offset >= sd->blk_len) { - /* TODO: Check CRC before committing */ - sd->state = sd_programming_state; - sd_lock_command(sd); - /* Bzzzzzzztt .... Operation complete. */ - sd->state = sd_transfer_state; - } - break; - - case 56: /* CMD56: GEN_CMD */ - sd->data[sd->data_offset ++] = value; - if (sd->data_offset >= sd->blk_len) { - APP_WRITE_BLOCK(sd->data_start, sd->data_offset); - sd->state = sd_transfer_state; - } - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, "sd_write_data: unknown command\n"); - break; - } -} - -uint8_t sd_read_data(SDState *sd) -{ - /* TODO: Append CRCs */ - uint8_t ret; - int io_len; - - if (!sd->blk || !blk_is_inserted(sd->blk) || !sd->enable) - return 0x00; - - if (sd->state != sd_sendingdata_state) { - qemu_log_mask(LOG_GUEST_ERROR, - "sd_read_data: not in Sending-Data state\n"); - return 0x00; - } - - if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION)) - return 0x00; - - io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len; - - switch (sd->current_cmd) { - case 6: /* CMD6: SWITCH_FUNCTION */ - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= 64) - sd->state = sd_transfer_state; - break; - - case 9: /* CMD9: SEND_CSD */ - case 10: /* CMD10: SEND_CID */ - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= 16) - sd->state = sd_transfer_state; - break; - - case 11: /* CMD11: READ_DAT_UNTIL_STOP */ - if (sd->data_offset == 0) - BLK_READ_BLOCK(sd->data_start, io_len); - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= io_len) { - sd->data_start += io_len; - sd->data_offset = 0; - if (sd->data_start + io_len > sd->size) { - sd->card_status |= ADDRESS_ERROR; - break; - } - } - break; - - case 13: /* ACMD13: SD_STATUS */ - ret = sd->sd_status[sd->data_offset ++]; - - if (sd->data_offset >= sizeof(sd->sd_status)) - sd->state = sd_transfer_state; - break; - - case 17: /* CMD17: READ_SINGLE_BLOCK */ - if (sd->data_offset == 0) - BLK_READ_BLOCK(sd->data_start, io_len); - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= io_len) - sd->state = sd_transfer_state; - break; - - case 18: /* CMD18: READ_MULTIPLE_BLOCK */ - if (sd->data_offset == 0) - BLK_READ_BLOCK(sd->data_start, io_len); - ret = sd->data[sd->data_offset ++]; - - 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; - } - } - break; - - case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= 4) - sd->state = sd_transfer_state; - break; - - case 30: /* CMD30: SEND_WRITE_PROT */ - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= 4) - sd->state = sd_transfer_state; - break; - - case 51: /* ACMD51: SEND_SCR */ - ret = sd->scr[sd->data_offset ++]; - - if (sd->data_offset >= sizeof(sd->scr)) - sd->state = sd_transfer_state; - break; - - case 56: /* CMD56: GEN_CMD */ - if (sd->data_offset == 0) - APP_READ_BLOCK(sd->data_start, sd->blk_len); - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= sd->blk_len) - sd->state = sd_transfer_state; - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, "sd_read_data: unknown command\n"); - return 0x00; - } - - return ret; -} - -bool sd_data_ready(SDState *sd) -{ - return sd->state == sd_sendingdata_state; -} - -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-internal.h b/qemu/hw/sd/sdhci-internal.h deleted file mode 100644 index 161177cf3..000000000 --- a/qemu/hw/sd/sdhci-internal.h +++ /dev/null @@ -1,232 +0,0 @@ -/* - * SD Association Host Standard Specification v2.0 controller emulation - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Mitsyanko Igor - * Peter A.G. Crosthwaite - * - * Based on MMC controller for Samsung S5PC1xx-based board emulation - * by Alexey Merkulov and Vladimir Monakhov. - * - * 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 . - */ -#ifndef SDHCI_INTERNAL_H -#define SDHCI_INTERNAL_H - -#include "hw/sd/sdhci.h" - -/* R/W SDMA System Address register 0x0 */ -#define SDHC_SYSAD 0x00 - -/* R/W Host DMA Buffer Boundary and Transfer Block Size Register 0x0 */ -#define SDHC_BLKSIZE 0x04 - -/* R/W Blocks count for current transfer 0x0 */ -#define SDHC_BLKCNT 0x06 - -/* R/W Command Argument Register 0x0 */ -#define SDHC_ARGUMENT 0x08 - -/* R/W Transfer Mode Setting Register 0x0 */ -#define SDHC_TRNMOD 0x0C -#define SDHC_TRNS_DMA 0x0001 -#define SDHC_TRNS_BLK_CNT_EN 0x0002 -#define SDHC_TRNS_ACMD12 0x0004 -#define SDHC_TRNS_READ 0x0010 -#define SDHC_TRNS_MULTI 0x0020 - -/* R/W Command Register 0x0 */ -#define SDHC_CMDREG 0x0E -#define SDHC_CMD_RSP_WITH_BUSY (3 << 0) -#define SDHC_CMD_DATA_PRESENT (1 << 5) -#define SDHC_CMD_SUSPEND (1 << 6) -#define SDHC_CMD_RESUME (1 << 7) -#define SDHC_CMD_ABORT ((1 << 6)|(1 << 7)) -#define SDHC_CMD_TYPE_MASK ((1 << 6)|(1 << 7)) -#define SDHC_COMMAND_TYPE(x) ((x) & SDHC_CMD_TYPE_MASK) - -/* ROC Response Register 0 0x0 */ -#define SDHC_RSPREG0 0x10 -/* ROC Response Register 1 0x0 */ -#define SDHC_RSPREG1 0x14 -/* ROC Response Register 2 0x0 */ -#define SDHC_RSPREG2 0x18 -/* ROC Response Register 3 0x0 */ -#define SDHC_RSPREG3 0x1C - -/* R/W Buffer Data Register 0x0 */ -#define SDHC_BDATA 0x20 - -/* R/ROC Present State Register 0x000A0000 */ -#define SDHC_PRNSTS 0x24 -#define SDHC_CMD_INHIBIT 0x00000001 -#define SDHC_DATA_INHIBIT 0x00000002 -#define SDHC_DAT_LINE_ACTIVE 0x00000004 -#define SDHC_DOING_WRITE 0x00000100 -#define SDHC_DOING_READ 0x00000200 -#define SDHC_SPACE_AVAILABLE 0x00000400 -#define SDHC_DATA_AVAILABLE 0x00000800 -#define SDHC_CARD_PRESENT 0x00010000 -#define SDHC_CARD_DETECT 0x00040000 -#define SDHC_WRITE_PROTECT 0x00080000 -#define TRANSFERRING_DATA(x) \ - ((x) & (SDHC_DOING_READ | SDHC_DOING_WRITE)) - -/* R/W Host control Register 0x0 */ -#define SDHC_HOSTCTL 0x28 -#define SDHC_CTRL_DMA_CHECK_MASK 0x18 -#define SDHC_CTRL_SDMA 0x00 -#define SDHC_CTRL_ADMA1_32 0x08 -#define SDHC_CTRL_ADMA2_32 0x10 -#define SDHC_CTRL_ADMA2_64 0x18 -#define SDHC_DMA_TYPE(x) ((x) & SDHC_CTRL_DMA_CHECK_MASK) - -/* R/W Power Control Register 0x0 */ -#define SDHC_PWRCON 0x29 -#define SDHC_POWER_ON (1 << 0) - -/* R/W Block Gap Control Register 0x0 */ -#define SDHC_BLKGAP 0x2A -#define SDHC_STOP_AT_GAP_REQ 0x01 -#define SDHC_CONTINUE_REQ 0x02 - -/* R/W WakeUp Control Register 0x0 */ -#define SDHC_WAKCON 0x2B -#define SDHC_WKUP_ON_INS (1 << 1) -#define SDHC_WKUP_ON_RMV (1 << 2) - -/* CLKCON */ -#define SDHC_CLKCON 0x2C -#define SDHC_CLOCK_INT_STABLE 0x0002 -#define SDHC_CLOCK_INT_EN 0x0001 -#define SDHC_CLOCK_SDCLK_EN (1 << 2) -#define SDHC_CLOCK_CHK_MASK 0x0007 -#define SDHC_CLOCK_IS_ON(x) \ - (((x) & SDHC_CLOCK_CHK_MASK) == SDHC_CLOCK_CHK_MASK) - -/* R/W Timeout Control Register 0x0 */ -#define SDHC_TIMEOUTCON 0x2E - -/* R/W Software Reset Register 0x0 */ -#define SDHC_SWRST 0x2F -#define SDHC_RESET_ALL 0x01 -#define SDHC_RESET_CMD 0x02 -#define SDHC_RESET_DATA 0x04 - -/* ROC/RW1C Normal Interrupt Status Register 0x0 */ -#define SDHC_NORINTSTS 0x30 -#define SDHC_NIS_ERR 0x8000 -#define SDHC_NIS_CMDCMP 0x0001 -#define SDHC_NIS_TRSCMP 0x0002 -#define SDHC_NIS_BLKGAP 0x0004 -#define SDHC_NIS_DMA 0x0008 -#define SDHC_NIS_WBUFRDY 0x0010 -#define SDHC_NIS_RBUFRDY 0x0020 -#define SDHC_NIS_INSERT 0x0040 -#define SDHC_NIS_REMOVE 0x0080 -#define SDHC_NIS_CARDINT 0x0100 - -/* ROC/RW1C Error Interrupt Status Register 0x0 */ -#define SDHC_ERRINTSTS 0x32 -#define SDHC_EIS_CMDTIMEOUT 0x0001 -#define SDHC_EIS_BLKGAP 0x0004 -#define SDHC_EIS_CMDIDX 0x0008 -#define SDHC_EIS_CMD12ERR 0x0100 -#define SDHC_EIS_ADMAERR 0x0200 - -/* R/W Normal Interrupt Status Enable Register 0x0 */ -#define SDHC_NORINTSTSEN 0x34 -#define SDHC_NISEN_CMDCMP 0x0001 -#define SDHC_NISEN_TRSCMP 0x0002 -#define SDHC_NISEN_DMA 0x0008 -#define SDHC_NISEN_WBUFRDY 0x0010 -#define SDHC_NISEN_RBUFRDY 0x0020 -#define SDHC_NISEN_INSERT 0x0040 -#define SDHC_NISEN_REMOVE 0x0080 -#define SDHC_NISEN_CARDINT 0x0100 - -/* R/W Error Interrupt Status Enable Register 0x0 */ -#define SDHC_ERRINTSTSEN 0x36 -#define SDHC_EISEN_CMDTIMEOUT 0x0001 -#define SDHC_EISEN_BLKGAP 0x0004 -#define SDHC_EISEN_CMDIDX 0x0008 -#define SDHC_EISEN_ADMAERR 0x0200 - -/* R/W Normal Interrupt Signal Enable Register 0x0 */ -#define SDHC_NORINTSIGEN 0x38 -#define SDHC_NORINTSIG_INSERT (1 << 6) -#define SDHC_NORINTSIG_REMOVE (1 << 7) - -/* R/W Error Interrupt Signal Enable Register 0x0 */ -#define SDHC_ERRINTSIGEN 0x3A - -/* ROC Auto CMD12 error status register 0x0 */ -#define SDHC_ACMD12ERRSTS 0x3C - -/* HWInit Capabilities Register 0x05E80080 */ -#define SDHC_CAPAREG 0x40 -#define SDHC_CAN_DO_DMA 0x00400000 -#define SDHC_CAN_DO_ADMA2 0x00080000 -#define SDHC_CAN_DO_ADMA1 0x00100000 -#define SDHC_64_BIT_BUS_SUPPORT (1 << 28) -#define SDHC_CAPAB_BLOCKSIZE(x) (((x) >> 16) & 0x3) - -/* HWInit Maximum Current Capabilities Register 0x0 */ -#define SDHC_MAXCURR 0x48 - -/* W Force Event Auto CMD12 Error Interrupt Register 0x0000 */ -#define SDHC_FEAER 0x50 -/* W Force Event Error Interrupt Register Error Interrupt 0x0000 */ -#define SDHC_FEERR 0x52 - -/* R/W ADMA Error Status Register 0x00 */ -#define SDHC_ADMAERR 0x54 -#define SDHC_ADMAERR_LENGTH_MISMATCH (1 << 2) -#define SDHC_ADMAERR_STATE_ST_STOP (0 << 0) -#define SDHC_ADMAERR_STATE_ST_FDS (1 << 0) -#define SDHC_ADMAERR_STATE_ST_TFR (3 << 0) -#define SDHC_ADMAERR_STATE_MASK (3 << 0) - -/* R/W ADMA System Address Register 0x00 */ -#define SDHC_ADMASYSADDR 0x58 -#define SDHC_ADMA_ATTR_SET_LEN (1 << 4) -#define SDHC_ADMA_ATTR_ACT_TRAN (1 << 5) -#define SDHC_ADMA_ATTR_ACT_LINK (3 << 4) -#define SDHC_ADMA_ATTR_INT (1 << 2) -#define SDHC_ADMA_ATTR_END (1 << 1) -#define SDHC_ADMA_ATTR_VALID (1 << 0) -#define SDHC_ADMA_ATTR_ACT_MASK ((1 << 4)|(1 << 5)) - -/* Slot interrupt status */ -#define SDHC_SLOT_INT_STATUS 0xFC - -/* HWInit Host Controller Version Register 0x0401 */ -#define SDHC_HCVER 0xFE -#define SD_HOST_SPECv2_VERS 0x2401 - -#define SDHC_REGISTERS_MAP_SIZE 0x100 -#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) - -enum { - sdhc_not_stopped = 0, /* normal SDHC state */ - sdhc_gap_read = 1, /* SDHC stopped at block gap during read operation */ - sdhc_gap_write = 2 /* SDHC stopped at block gap during write operation */ -}; - -extern const VMStateDescription sdhci_vmstate; - -#endif diff --git a/qemu/hw/sd/sdhci.c b/qemu/hw/sd/sdhci.c deleted file mode 100644 index d28b5871f..000000000 --- a/qemu/hw/sd/sdhci.c +++ /dev/null @@ -1,1394 +0,0 @@ -/* - * SD Association Host Standard Specification v2.0 controller emulation - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Mitsyanko Igor - * Peter A.G. Crosthwaite - * - * Based on MMC controller for Samsung S5PC1xx-based board emulation - * by Alexey Merkulov and Vladimir Monakhov. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "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-internal.h" - -/* host controller debug messages */ -#ifndef SDHC_DEBUG -#define SDHC_DEBUG 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. - * If not stated otherwise: - * 0 - not supported, 1 - supported, other - prohibited. - */ -#define SDHC_CAPAB_64BITBUS 0ul /* 64-bit System Bus Support */ -#define SDHC_CAPAB_18V 1ul /* Voltage support 1.8v */ -#define SDHC_CAPAB_30V 0ul /* Voltage support 3.0v */ -#define SDHC_CAPAB_33V 1ul /* Voltage support 3.3v */ -#define SDHC_CAPAB_SUSPRESUME 0ul /* Suspend/resume support */ -#define SDHC_CAPAB_SDMA 1ul /* SDMA support */ -#define SDHC_CAPAB_HIGHSPEED 1ul /* High speed support */ -#define SDHC_CAPAB_ADMA1 1ul /* ADMA1 support */ -#define SDHC_CAPAB_ADMA2 1ul /* ADMA2 support */ -/* Maximum host controller R/W buffers size - * Possible values: 512, 1024, 2048 bytes */ -#define SDHC_CAPAB_MAXBLOCKLENGTH 512ul -/* Maximum clock frequency for SDclock in MHz - * value in range 10-63 MHz, 0 - not defined */ -#define SDHC_CAPAB_BASECLKFREQ 52ul -#define SDHC_CAPAB_TOUNIT 1ul /* Timeout clock unit 0 - kHz, 1 - MHz */ -/* Timeout clock frequency 1-63, 0 - not defined */ -#define SDHC_CAPAB_TOCLKFREQ 52ul - -/* Now check all parameters and calculate CAPABILITIES REGISTER value */ -#if SDHC_CAPAB_64BITBUS > 1 || SDHC_CAPAB_18V > 1 || SDHC_CAPAB_30V > 1 || \ - SDHC_CAPAB_33V > 1 || SDHC_CAPAB_SUSPRESUME > 1 || SDHC_CAPAB_SDMA > 1 || \ - SDHC_CAPAB_HIGHSPEED > 1 || SDHC_CAPAB_ADMA2 > 1 || SDHC_CAPAB_ADMA1 > 1 ||\ - SDHC_CAPAB_TOUNIT > 1 -#error Capabilities features can have value 0 or 1 only! -#endif - -#if SDHC_CAPAB_MAXBLOCKLENGTH == 512 -#define MAX_BLOCK_LENGTH 0ul -#elif SDHC_CAPAB_MAXBLOCKLENGTH == 1024 -#define MAX_BLOCK_LENGTH 1ul -#elif SDHC_CAPAB_MAXBLOCKLENGTH == 2048 -#define MAX_BLOCK_LENGTH 2ul -#else -#error Max host controller block size can have value 512, 1024 or 2048 only! -#endif - -#if (SDHC_CAPAB_BASECLKFREQ > 0 && SDHC_CAPAB_BASECLKFREQ < 10) || \ - SDHC_CAPAB_BASECLKFREQ > 63 -#error SDclock frequency can have value in range 0, 10-63 only! -#endif - -#if SDHC_CAPAB_TOCLKFREQ > 63 -#error Timeout clock frequency can have value in range 0-63 only! -#endif - -#define SDHC_CAPAB_REG_DEFAULT \ - ((SDHC_CAPAB_64BITBUS << 28) | (SDHC_CAPAB_18V << 26) | \ - (SDHC_CAPAB_30V << 25) | (SDHC_CAPAB_33V << 24) | \ - (SDHC_CAPAB_SUSPRESUME << 23) | (SDHC_CAPAB_SDMA << 22) | \ - (SDHC_CAPAB_HIGHSPEED << 21) | (SDHC_CAPAB_ADMA1 << 20) | \ - (SDHC_CAPAB_ADMA2 << 19) | (MAX_BLOCK_LENGTH << 16) | \ - (SDHC_CAPAB_BASECLKFREQ << 8) | (SDHC_CAPAB_TOUNIT << 7) | \ - (SDHC_CAPAB_TOCLKFREQ)) - -#define MASKED_WRITE(reg, mask, val) (reg = (reg & (mask)) | (val)) - -static uint8_t sdhci_slotint(SDHCIState *s) -{ - return (s->norintsts & s->norintsigen) || (s->errintsts & s->errintsigen) || - ((s->norintsts & SDHC_NIS_INSERT) && (s->wakcon & SDHC_WKUP_ON_INS)) || - ((s->norintsts & SDHC_NIS_REMOVE) && (s->wakcon & SDHC_WKUP_ON_RMV)); -} - -static inline void sdhci_update_irq(SDHCIState *s) -{ - qemu_set_irq(s->irq, sdhci_slotint(s)); -} - -static void sdhci_raise_insertion_irq(void *opaque) -{ - SDHCIState *s = (SDHCIState *)opaque; - - if (s->norintsts & SDHC_NIS_REMOVE) { - timer_mod(s->insert_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + SDHC_INSERTION_DELAY); - } else { - s->prnsts = 0x1ff0000; - if (s->norintstsen & SDHC_NISEN_INSERT) { - s->norintsts |= SDHC_NIS_INSERT; - } - sdhci_update_irq(s); - } -} - -static void sdhci_set_inserted(DeviceState *dev, bool level) -{ - SDHCIState *s = (SDHCIState *)dev; - DPRINT_L1("Card state changed: %s!\n", level ? "insert" : "eject"); - - if ((s->norintsts & SDHC_NIS_REMOVE) && level) { - /* Give target some time to notice card ejection */ - timer_mod(s->insert_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + SDHC_INSERTION_DELAY); - } else { - if (level) { - s->prnsts = 0x1ff0000; - if (s->norintstsen & SDHC_NISEN_INSERT) { - s->norintsts |= SDHC_NIS_INSERT; - } - } else { - s->prnsts = 0x1fa0000; - s->pwrcon &= ~SDHC_POWER_ON; - s->clkcon &= ~SDHC_CLOCK_SDCLK_EN; - if (s->norintstsen & SDHC_NISEN_REMOVE) { - s->norintsts |= SDHC_NIS_REMOVE; - } - } - sdhci_update_irq(s); - } -} - -static void sdhci_set_readonly(DeviceState *dev, bool level) -{ - SDHCIState *s = (SDHCIState *)dev; - - if (level) { - s->prnsts &= ~SDHC_WRITE_PROTECT; - } else { - /* Write enabled */ - s->prnsts |= SDHC_WRITE_PROTECT; - } -} - -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 - * and assumed to always preserve their value, given to them during - * initialization */ - memset(&s->sdmasysad, 0, (uintptr_t)&s->capareg - (uintptr_t)&s->sdmasysad); - - /* 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); - -static void sdhci_send_command(SDHCIState *s) -{ - SDRequest request; - uint8_t response[16]; - int rlen; - - s->errintsts = 0; - s->acmd12errsts = 0; - request.cmd = s->cmdreg >> 8; - request.arg = s->argument; - DPRINT_L1("sending CMD%u ARG[0x%08x]\n", request.cmd, request.arg); - rlen = sdbus_do_command(&s->sdbus, &request, response); - - if (s->cmdreg & SDHC_CMD_RESPONSE) { - if (rlen == 4) { - s->rspreg[0] = (response[0] << 24) | (response[1] << 16) | - (response[2] << 8) | response[3]; - s->rspreg[1] = s->rspreg[2] = s->rspreg[3] = 0; - DPRINT_L1("Response: RSPREG[31..0]=0x%08x\n", s->rspreg[0]); - } else if (rlen == 16) { - s->rspreg[0] = (response[11] << 24) | (response[12] << 16) | - (response[13] << 8) | response[14]; - s->rspreg[1] = (response[7] << 24) | (response[8] << 16) | - (response[9] << 8) | response[10]; - s->rspreg[2] = (response[3] << 24) | (response[4] << 16) | - (response[5] << 8) | response[6]; - s->rspreg[3] = (response[0] << 16) | (response[1] << 8) | - response[2]; - DPRINT_L1("Response received:\n RSPREG[127..96]=0x%08x, RSPREG[95.." - "64]=0x%08x,\n RSPREG[63..32]=0x%08x, RSPREG[31..0]=0x%08x\n", - s->rspreg[3], s->rspreg[2], s->rspreg[1], s->rspreg[0]); - } else { - ERRPRINT("Timeout waiting for command response\n"); - if (s->errintstsen & SDHC_EISEN_CMDTIMEOUT) { - s->errintsts |= SDHC_EIS_CMDTIMEOUT; - s->norintsts |= SDHC_NIS_ERR; - } - } - - if ((s->norintstsen & SDHC_NISEN_TRSCMP) && - (s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY) { - s->norintsts |= SDHC_NIS_TRSCMP; - } - } - - if (s->norintstsen & SDHC_NISEN_CMDCMP) { - s->norintsts |= SDHC_NIS_CMDCMP; - } - - sdhci_update_irq(s); - - if (s->blksize && (s->cmdreg & SDHC_CMD_DATA_PRESENT)) { - s->data_count = 0; - sdhci_data_transfer(s); - } -} - -static void sdhci_end_transfer(SDHCIState *s) -{ - /* Automatically send CMD12 to stop transfer if AutoCMD12 enabled */ - if ((s->trnmod & SDHC_TRNS_ACMD12) != 0) { - SDRequest request; - uint8_t response[16]; - - request.cmd = 0x0C; - request.arg = 0; - DPRINT_L1("Automatically issue CMD%d %08x\n", request.cmd, request.arg); - 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]; - } - - s->prnsts &= ~(SDHC_DOING_READ | SDHC_DOING_WRITE | - SDHC_DAT_LINE_ACTIVE | SDHC_DATA_INHIBIT | - SDHC_SPACE_AVAILABLE | SDHC_DATA_AVAILABLE); - - if (s->norintstsen & SDHC_NISEN_TRSCMP) { - s->norintsts |= SDHC_NIS_TRSCMP; - } - - sdhci_update_irq(s); -} - -/* - * Programmed i/o data transfer - */ - -/* Fill host controller's read buffer with BLKSIZE bytes of data from card */ -static void sdhci_read_block_from_card(SDHCIState *s) -{ - int index = 0; - - if ((s->trnmod & SDHC_TRNS_MULTI) && - (s->trnmod & SDHC_TRNS_BLK_CNT_EN) && (s->blkcnt == 0)) { - return; - } - - for (index = 0; index < (s->blksize & 0x0fff); index++) { - s->fifo_buffer[index] = sdbus_read_data(&s->sdbus); - } - - /* New data now available for READ through Buffer Port Register */ - s->prnsts |= SDHC_DATA_AVAILABLE; - if (s->norintstsen & SDHC_NISEN_RBUFRDY) { - s->norintsts |= SDHC_NIS_RBUFRDY; - } - - /* Clear DAT line active status if that was the last block */ - if ((s->trnmod & SDHC_TRNS_MULTI) == 0 || - ((s->trnmod & SDHC_TRNS_MULTI) && s->blkcnt == 1)) { - s->prnsts &= ~SDHC_DAT_LINE_ACTIVE; - } - - /* If stop at block gap request was set and it's not the last block of - * data - generate Block Event interrupt */ - if (s->stopped_state == sdhc_gap_read && (s->trnmod & SDHC_TRNS_MULTI) && - s->blkcnt != 1) { - s->prnsts &= ~SDHC_DAT_LINE_ACTIVE; - if (s->norintstsen & SDHC_EISEN_BLKGAP) { - s->norintsts |= SDHC_EIS_BLKGAP; - } - } - - sdhci_update_irq(s); -} - -/* Read @size byte of data from host controller @s BUFFER DATA PORT register */ -static uint32_t sdhci_read_dataport(SDHCIState *s, unsigned size) -{ - uint32_t value = 0; - int i; - - /* first check that a valid data exists in host controller input buffer */ - if ((s->prnsts & SDHC_DATA_AVAILABLE) == 0) { - ERRPRINT("Trying to read from empty buffer\n"); - return 0; - } - - for (i = 0; i < size; i++) { - value |= s->fifo_buffer[s->data_count] << i * 8; - s->data_count++; - /* check if we've read all valid data (blksize bytes) from buffer */ - if ((s->data_count) >= (s->blksize & 0x0fff)) { - DPRINT_L2("All %u bytes of data have been read from input buffer\n", - s->data_count); - s->prnsts &= ~SDHC_DATA_AVAILABLE; /* no more data in a buffer */ - s->data_count = 0; /* next buff read must start at position [0] */ - - if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { - s->blkcnt--; - } - - /* if that was the last block of data */ - if ((s->trnmod & SDHC_TRNS_MULTI) == 0 || - ((s->trnmod & SDHC_TRNS_BLK_CNT_EN) && (s->blkcnt == 0)) || - /* stop at gap request */ - (s->stopped_state == sdhc_gap_read && - !(s->prnsts & SDHC_DAT_LINE_ACTIVE))) { - sdhci_end_transfer(s); - } else { /* if there are more data, read next block from card */ - sdhci_read_block_from_card(s); - } - break; - } - } - - return value; -} - -/* Write data from host controller FIFO to card */ -static void sdhci_write_block_to_card(SDHCIState *s) -{ - int index = 0; - - if (s->prnsts & SDHC_SPACE_AVAILABLE) { - if (s->norintstsen & SDHC_NISEN_WBUFRDY) { - s->norintsts |= SDHC_NIS_WBUFRDY; - } - sdhci_update_irq(s); - return; - } - - if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { - if (s->blkcnt == 0) { - return; - } else { - s->blkcnt--; - } - } - - for (index = 0; index < (s->blksize & 0x0fff); index++) { - sdbus_write_data(&s->sdbus, s->fifo_buffer[index]); - } - - /* Next data can be written through BUFFER DATORT register */ - s->prnsts |= SDHC_SPACE_AVAILABLE; - - /* Finish transfer if that was the last block of data */ - if ((s->trnmod & SDHC_TRNS_MULTI) == 0 || - ((s->trnmod & SDHC_TRNS_MULTI) && - (s->trnmod & SDHC_TRNS_BLK_CNT_EN) && (s->blkcnt == 0))) { - sdhci_end_transfer(s); - } else if (s->norintstsen & SDHC_NISEN_WBUFRDY) { - s->norintsts |= SDHC_NIS_WBUFRDY; - } - - /* Generate Block Gap Event if requested and if not the last block */ - if (s->stopped_state == sdhc_gap_write && (s->trnmod & SDHC_TRNS_MULTI) && - s->blkcnt > 0) { - s->prnsts &= ~SDHC_DOING_WRITE; - if (s->norintstsen & SDHC_EISEN_BLKGAP) { - s->norintsts |= SDHC_EIS_BLKGAP; - } - sdhci_end_transfer(s); - } - - sdhci_update_irq(s); -} - -/* Write @size bytes of @value data to host controller @s Buffer Data Port - * register */ -static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size) -{ - unsigned i; - - /* Check that there is free space left in a buffer */ - if (!(s->prnsts & SDHC_SPACE_AVAILABLE)) { - ERRPRINT("Can't write to data buffer: buffer full\n"); - return; - } - - for (i = 0; i < size; i++) { - s->fifo_buffer[s->data_count] = value & 0xFF; - s->data_count++; - value >>= 8; - if (s->data_count >= (s->blksize & 0x0fff)) { - DPRINT_L2("write buffer filled with %u bytes of data\n", - s->data_count); - s->data_count = 0; - s->prnsts &= ~SDHC_SPACE_AVAILABLE; - if (s->prnsts & SDHC_DOING_WRITE) { - sdhci_write_block_to_card(s); - } - } - } -} - -/* - * Single DMA data transfer - */ - -/* Multi block SDMA transfer */ -static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) -{ - bool page_aligned = false; - unsigned int n, begin; - const uint16_t block_size = s->blksize & 0x0fff; - uint32_t boundary_chk = 1 << (((s->blksize & 0xf000) >> 12) + 12); - uint32_t boundary_count = boundary_chk - (s->sdmasysad % boundary_chk); - - /* XXX: Some sd/mmc drivers (for example, u-boot-slp) do not account for - * possible stop at page boundary if initial address is not page aligned, - * allow them to work properly */ - if ((s->sdmasysad % boundary_chk) == 0) { - page_aligned = true; - } - - if (s->trnmod & SDHC_TRNS_READ) { - s->prnsts |= SDHC_DOING_READ | SDHC_DATA_INHIBIT | - SDHC_DAT_LINE_ACTIVE; - while (s->blkcnt) { - if (s->data_count == 0) { - for (n = 0; n < block_size; n++) { - s->fifo_buffer[n] = sdbus_read_data(&s->sdbus); - } - } - begin = s->data_count; - if (((boundary_count + begin) < block_size) && page_aligned) { - s->data_count = boundary_count + begin; - boundary_count = 0; - } else { - s->data_count = block_size; - boundary_count -= block_size - begin; - if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { - s->blkcnt--; - } - } - dma_memory_write(&address_space_memory, s->sdmasysad, - &s->fifo_buffer[begin], s->data_count - begin); - s->sdmasysad += s->data_count - begin; - if (s->data_count == block_size) { - s->data_count = 0; - } - if (page_aligned && boundary_count == 0) { - break; - } - } - } else { - s->prnsts |= SDHC_DOING_WRITE | SDHC_DATA_INHIBIT | - SDHC_DAT_LINE_ACTIVE; - while (s->blkcnt) { - begin = s->data_count; - if (((boundary_count + begin) < block_size) && page_aligned) { - s->data_count = boundary_count + begin; - boundary_count = 0; - } else { - s->data_count = block_size; - boundary_count -= block_size - begin; - } - dma_memory_read(&address_space_memory, s->sdmasysad, - &s->fifo_buffer[begin], s->data_count); - s->sdmasysad += s->data_count - begin; - if (s->data_count == block_size) { - for (n = 0; n < block_size; n++) { - sdbus_write_data(&s->sdbus, s->fifo_buffer[n]); - } - s->data_count = 0; - if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { - s->blkcnt--; - } - } - if (page_aligned && boundary_count == 0) { - break; - } - } - } - - if (s->blkcnt == 0) { - sdhci_end_transfer(s); - } else { - if (s->norintstsen & SDHC_NISEN_DMA) { - s->norintsts |= SDHC_NIS_DMA; - } - sdhci_update_irq(s); - } -} - -/* single block SDMA transfer */ - -static void sdhci_sdma_transfer_single_block(SDHCIState *s) -{ - int n; - uint32_t datacnt = s->blksize & 0x0fff; - - if (s->trnmod & SDHC_TRNS_READ) { - for (n = 0; n < datacnt; n++) { - s->fifo_buffer[n] = sdbus_read_data(&s->sdbus); - } - dma_memory_write(&address_space_memory, s->sdmasysad, s->fifo_buffer, - datacnt); - } else { - dma_memory_read(&address_space_memory, s->sdmasysad, s->fifo_buffer, - datacnt); - for (n = 0; n < datacnt; n++) { - sdbus_write_data(&s->sdbus, s->fifo_buffer[n]); - } - } - - if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { - s->blkcnt--; - } - - sdhci_end_transfer(s); -} - -typedef struct ADMADescr { - hwaddr addr; - uint16_t length; - uint8_t attr; - uint8_t incr; -} ADMADescr; - -static void get_adma_description(SDHCIState *s, ADMADescr *dscr) -{ - uint32_t adma1 = 0; - uint64_t adma2 = 0; - hwaddr entry_addr = (hwaddr)s->admasysaddr; - switch (SDHC_DMA_TYPE(s->hostctl)) { - case SDHC_CTRL_ADMA2_32: - dma_memory_read(&address_space_memory, entry_addr, (uint8_t *)&adma2, - sizeof(adma2)); - adma2 = le64_to_cpu(adma2); - /* The spec does not specify endianness of descriptor table. - * We currently assume that it is LE. - */ - dscr->addr = (hwaddr)extract64(adma2, 32, 32) & ~0x3ull; - dscr->length = (uint16_t)extract64(adma2, 16, 16); - dscr->attr = (uint8_t)extract64(adma2, 0, 7); - dscr->incr = 8; - break; - case SDHC_CTRL_ADMA1_32: - dma_memory_read(&address_space_memory, entry_addr, (uint8_t *)&adma1, - sizeof(adma1)); - adma1 = le32_to_cpu(adma1); - dscr->addr = (hwaddr)(adma1 & 0xFFFFF000); - dscr->attr = (uint8_t)extract32(adma1, 0, 7); - dscr->incr = 4; - if ((dscr->attr & SDHC_ADMA_ATTR_ACT_MASK) == SDHC_ADMA_ATTR_SET_LEN) { - dscr->length = (uint16_t)extract32(adma1, 12, 16); - } else { - dscr->length = 4096; - } - break; - case SDHC_CTRL_ADMA2_64: - dma_memory_read(&address_space_memory, entry_addr, - (uint8_t *)(&dscr->attr), 1); - dma_memory_read(&address_space_memory, entry_addr + 2, - (uint8_t *)(&dscr->length), 2); - dscr->length = le16_to_cpu(dscr->length); - dma_memory_read(&address_space_memory, entry_addr + 4, - (uint8_t *)(&dscr->addr), 8); - dscr->attr = le64_to_cpu(dscr->attr); - dscr->attr &= 0xfffffff8; - dscr->incr = 12; - break; - } -} - -/* Advanced DMA data transfer */ - -static void sdhci_do_adma(SDHCIState *s) -{ - unsigned int n, begin, length; - const uint16_t block_size = s->blksize & 0x0fff; - ADMADescr dscr; - int i; - - for (i = 0; i < SDHC_ADMA_DESCS_PER_DELAY; ++i) { - s->admaerr &= ~SDHC_ADMAERR_LENGTH_MISMATCH; - - get_adma_description(s, &dscr); - DPRINT_L2("ADMA loop: addr=" TARGET_FMT_plx ", len=%d, attr=%x\n", - dscr.addr, dscr.length, dscr.attr); - - if ((dscr.attr & SDHC_ADMA_ATTR_VALID) == 0) { - /* Indicate that error occurred in ST_FDS state */ - s->admaerr &= ~SDHC_ADMAERR_STATE_MASK; - s->admaerr |= SDHC_ADMAERR_STATE_ST_FDS; - - /* Generate ADMA error interrupt */ - if (s->errintstsen & SDHC_EISEN_ADMAERR) { - s->errintsts |= SDHC_EIS_ADMAERR; - s->norintsts |= SDHC_NIS_ERR; - } - - sdhci_update_irq(s); - return; - } - - length = dscr.length ? dscr.length : 65536; - - switch (dscr.attr & SDHC_ADMA_ATTR_ACT_MASK) { - case SDHC_ADMA_ATTR_ACT_TRAN: /* data transfer */ - - if (s->trnmod & SDHC_TRNS_READ) { - while (length) { - if (s->data_count == 0) { - for (n = 0; n < block_size; n++) { - s->fifo_buffer[n] = sdbus_read_data(&s->sdbus); - } - } - begin = s->data_count; - if ((length + begin) < block_size) { - s->data_count = length + begin; - length = 0; - } else { - s->data_count = block_size; - length -= block_size - begin; - } - dma_memory_write(&address_space_memory, dscr.addr, - &s->fifo_buffer[begin], - s->data_count - begin); - dscr.addr += s->data_count - begin; - if (s->data_count == block_size) { - s->data_count = 0; - if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { - s->blkcnt--; - if (s->blkcnt == 0) { - break; - } - } - } - } - } else { - while (length) { - begin = s->data_count; - if ((length + begin) < block_size) { - s->data_count = length + begin; - length = 0; - } else { - s->data_count = block_size; - length -= block_size - begin; - } - dma_memory_read(&address_space_memory, dscr.addr, - &s->fifo_buffer[begin], - s->data_count - begin); - dscr.addr += s->data_count - begin; - if (s->data_count == block_size) { - for (n = 0; n < block_size; n++) { - sdbus_write_data(&s->sdbus, s->fifo_buffer[n]); - } - s->data_count = 0; - if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { - s->blkcnt--; - if (s->blkcnt == 0) { - break; - } - } - } - } - } - s->admasysaddr += dscr.incr; - break; - case SDHC_ADMA_ATTR_ACT_LINK: /* link to next descriptor table */ - s->admasysaddr = dscr.addr; - DPRINT_L1("ADMA link: admasysaddr=0x%" PRIx64 "\n", - s->admasysaddr); - break; - default: - s->admasysaddr += dscr.incr; - break; - } - - if (dscr.attr & SDHC_ADMA_ATTR_INT) { - DPRINT_L1("ADMA interrupt: admasysaddr=0x%" PRIx64 "\n", - s->admasysaddr); - if (s->norintstsen & SDHC_NISEN_DMA) { - s->norintsts |= SDHC_NIS_DMA; - } - - sdhci_update_irq(s); - } - - /* ADMA transfer terminates if blkcnt == 0 or by END attribute */ - if (((s->trnmod & SDHC_TRNS_BLK_CNT_EN) && - (s->blkcnt == 0)) || (dscr.attr & SDHC_ADMA_ATTR_END)) { - DPRINT_L2("ADMA transfer completed\n"); - if (length || ((dscr.attr & SDHC_ADMA_ATTR_END) && - (s->trnmod & SDHC_TRNS_BLK_CNT_EN) && - s->blkcnt != 0)) { - ERRPRINT("SD/MMC host ADMA length mismatch\n"); - s->admaerr |= SDHC_ADMAERR_LENGTH_MISMATCH | - SDHC_ADMAERR_STATE_ST_TFR; - if (s->errintstsen & SDHC_EISEN_ADMAERR) { - ERRPRINT("Set ADMA error flag\n"); - s->errintsts |= SDHC_EIS_ADMAERR; - s->norintsts |= SDHC_NIS_ERR; - } - - sdhci_update_irq(s); - } - sdhci_end_transfer(s); - return; - } - - } - - /* we have unfinished business - reschedule to continue ADMA */ - timer_mod(s->transfer_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + SDHC_TRANSFER_DELAY); -} - -/* Perform data transfer according to controller configuration */ - -static void sdhci_data_transfer(void *opaque) -{ - SDHCIState *s = (SDHCIState *)opaque; - - if (s->trnmod & SDHC_TRNS_DMA) { - switch (SDHC_DMA_TYPE(s->hostctl)) { - case SDHC_CTRL_SDMA: - if ((s->trnmod & SDHC_TRNS_MULTI) && - (!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || s->blkcnt == 0)) { - break; - } - - if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) { - sdhci_sdma_transfer_single_block(s); - } else { - sdhci_sdma_transfer_multi_blocks(s); - } - - break; - case SDHC_CTRL_ADMA1_32: - if (!(s->capareg & SDHC_CAN_DO_ADMA1)) { - ERRPRINT("ADMA1 not supported\n"); - break; - } - - sdhci_do_adma(s); - break; - case SDHC_CTRL_ADMA2_32: - if (!(s->capareg & SDHC_CAN_DO_ADMA2)) { - ERRPRINT("ADMA2 not supported\n"); - break; - } - - sdhci_do_adma(s); - break; - case SDHC_CTRL_ADMA2_64: - if (!(s->capareg & SDHC_CAN_DO_ADMA2) || - !(s->capareg & SDHC_64_BIT_BUS_SUPPORT)) { - ERRPRINT("64 bit ADMA not supported\n"); - break; - } - - sdhci_do_adma(s); - break; - default: - ERRPRINT("Unsupported DMA type\n"); - break; - } - } else { - 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); - } else { - s->prnsts |= SDHC_DOING_WRITE | SDHC_DAT_LINE_ACTIVE | - SDHC_SPACE_AVAILABLE | SDHC_DATA_INHIBIT; - sdhci_write_block_to_card(s); - } - } -} - -static bool sdhci_can_issue_command(SDHCIState *s) -{ - 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 && - !(SDHC_COMMAND_TYPE(s->cmdreg) == SDHC_CMD_ABORT))))) { - return false; - } - - return true; -} - -/* The Buffer Data Port register must be accessed in sequential and - * continuous manner */ -static inline bool -sdhci_buff_access_is_sequential(SDHCIState *s, unsigned byte_num) -{ - if ((s->data_count & 0x3) != byte_num) { - ERRPRINT("Non-sequential access to Buffer Data Port register" - "is prohibited\n"); - return false; - } - return true; -} - -static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size) -{ - SDHCIState *s = (SDHCIState *)opaque; - uint32_t ret = 0; - - switch (offset & ~0x3) { - case SDHC_SYSAD: - ret = s->sdmasysad; - break; - case SDHC_BLKSIZE: - ret = s->blksize | (s->blkcnt << 16); - break; - case SDHC_ARGUMENT: - ret = s->argument; - break; - case SDHC_TRNMOD: - ret = s->trnmod | (s->cmdreg << 16); - break; - case SDHC_RSPREG0 ... SDHC_RSPREG3: - ret = s->rspreg[((offset & ~0x3) - SDHC_RSPREG0) >> 2]; - break; - case SDHC_BDATA: - if (sdhci_buff_access_is_sequential(s, offset - SDHC_BDATA)) { - ret = sdhci_read_dataport(s, size); - DPRINT_L2("read %ub: addr[0x%04x] -> %u(0x%x)\n", size, (int)offset, - ret, ret); - return ret; - } - break; - case SDHC_PRNSTS: - ret = s->prnsts; - break; - case SDHC_HOSTCTL: - ret = s->hostctl | (s->pwrcon << 8) | (s->blkgap << 16) | - (s->wakcon << 24); - break; - case SDHC_CLKCON: - ret = s->clkcon | (s->timeoutcon << 16); - break; - case SDHC_NORINTSTS: - ret = s->norintsts | (s->errintsts << 16); - break; - case SDHC_NORINTSTSEN: - ret = s->norintstsen | (s->errintstsen << 16); - break; - case SDHC_NORINTSIGEN: - ret = s->norintsigen | (s->errintsigen << 16); - break; - case SDHC_ACMD12ERRSTS: - ret = s->acmd12errsts; - break; - case SDHC_CAPAREG: - ret = s->capareg; - break; - case SDHC_MAXCURR: - ret = s->maxcurr; - break; - case SDHC_ADMAERR: - ret = s->admaerr; - break; - case SDHC_ADMASYSADDR: - ret = (uint32_t)s->admasysaddr; - break; - case SDHC_ADMASYSADDR + 4: - ret = (uint32_t)(s->admasysaddr >> 32); - break; - case SDHC_SLOT_INT_STATUS: - ret = (SD_HOST_SPECv2_VERS << 16) | sdhci_slotint(s); - break; - default: - ERRPRINT("bad %ub read: addr[0x%04x]\n", size, (int)offset); - break; - } - - ret >>= (offset & 0x3) * 8; - ret &= (1ULL << (size * 8)) - 1; - DPRINT_L2("read %ub: addr[0x%04x] -> %u(0x%x)\n", size, (int)offset, ret, ret); - return ret; -} - -static inline void sdhci_blkgap_write(SDHCIState *s, uint8_t value) -{ - if ((value & SDHC_STOP_AT_GAP_REQ) && (s->blkgap & SDHC_STOP_AT_GAP_REQ)) { - return; - } - s->blkgap = value & SDHC_STOP_AT_GAP_REQ; - - if ((value & SDHC_CONTINUE_REQ) && s->stopped_state && - (s->blkgap & SDHC_STOP_AT_GAP_REQ) == 0) { - if (s->stopped_state == sdhc_gap_read) { - s->prnsts |= SDHC_DAT_LINE_ACTIVE | SDHC_DOING_READ; - sdhci_read_block_from_card(s); - } else { - s->prnsts |= SDHC_DAT_LINE_ACTIVE | SDHC_DOING_WRITE; - sdhci_write_block_to_card(s); - } - s->stopped_state = sdhc_not_stopped; - } else if (!s->stopped_state && (value & SDHC_STOP_AT_GAP_REQ)) { - if (s->prnsts & SDHC_DOING_READ) { - s->stopped_state = sdhc_gap_read; - } else if (s->prnsts & SDHC_DOING_WRITE) { - s->stopped_state = sdhc_gap_write; - } - } -} - -static inline void sdhci_reset_write(SDHCIState *s, uint8_t value) -{ - switch (value) { - case SDHC_RESET_ALL: - sdhci_reset(s); - break; - case SDHC_RESET_CMD: - s->prnsts &= ~SDHC_CMD_INHIBIT; - s->norintsts &= ~SDHC_NIS_CMDCMP; - break; - case SDHC_RESET_DATA: - s->data_count = 0; - s->prnsts &= ~(SDHC_SPACE_AVAILABLE | SDHC_DATA_AVAILABLE | - SDHC_DOING_READ | SDHC_DOING_WRITE | - SDHC_DATA_INHIBIT | SDHC_DAT_LINE_ACTIVE); - s->blkgap &= ~(SDHC_STOP_AT_GAP_REQ | SDHC_CONTINUE_REQ); - s->stopped_state = sdhc_not_stopped; - s->norintsts &= ~(SDHC_NIS_WBUFRDY | SDHC_NIS_RBUFRDY | - SDHC_NIS_DMA | SDHC_NIS_TRSCMP | SDHC_NIS_BLKGAP); - break; - } -} - -static void -sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) -{ - SDHCIState *s = (SDHCIState *)opaque; - unsigned shift = 8 * (offset & 0x3); - uint32_t mask = ~(((1ULL << (size * 8)) - 1) << shift); - uint32_t value = val; - value <<= shift; - - switch (offset & ~0x3) { - case SDHC_SYSAD: - s->sdmasysad = (s->sdmasysad & mask) | value; - MASKED_WRITE(s->sdmasysad, mask, value); - /* Writing to last byte of sdmasysad might trigger transfer */ - if (!(mask & 0xFF000000) && TRANSFERRING_DATA(s->prnsts) && s->blkcnt && - s->blksize && SDHC_DMA_TYPE(s->hostctl) == SDHC_CTRL_SDMA) { - sdhci_sdma_transfer_multi_blocks(s); - } - break; - case SDHC_BLKSIZE: - if (!TRANSFERRING_DATA(s->prnsts)) { - 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); - break; - case SDHC_TRNMOD: - /* DMA can be enabled only if it is supported as indicated by - * capabilities register */ - if (!(s->capareg & SDHC_CAN_DO_DMA)) { - value &= ~SDHC_TRNS_DMA; - } - MASKED_WRITE(s->trnmod, mask, value); - MASKED_WRITE(s->cmdreg, mask >> 16, value >> 16); - - /* Writing to the upper byte of CMDREG triggers SD command generation */ - if ((mask & 0xFF000000) || !sdhci_can_issue_command(s)) { - break; - } - - sdhci_send_command(s); - break; - case SDHC_BDATA: - if (sdhci_buff_access_is_sequential(s, offset - SDHC_BDATA)) { - sdhci_write_dataport(s, value >> shift, size); - } - break; - case SDHC_HOSTCTL: - if (!(mask & 0xFF0000)) { - sdhci_blkgap_write(s, value >> 16); - } - MASKED_WRITE(s->hostctl, mask, value); - MASKED_WRITE(s->pwrcon, mask >> 8, value >> 8); - MASKED_WRITE(s->wakcon, mask >> 24, value >> 24); - if (!(s->prnsts & SDHC_CARD_PRESENT) || ((s->pwrcon >> 1) & 0x7) < 5 || - !(s->capareg & (1 << (31 - ((s->pwrcon >> 1) & 0x7))))) { - s->pwrcon &= ~SDHC_POWER_ON; - } - break; - case SDHC_CLKCON: - if (!(mask & 0xFF000000)) { - sdhci_reset_write(s, value >> 24); - } - MASKED_WRITE(s->clkcon, mask, value); - MASKED_WRITE(s->timeoutcon, mask >> 16, value >> 16); - if (s->clkcon & SDHC_CLOCK_INT_EN) { - s->clkcon |= SDHC_CLOCK_INT_STABLE; - } else { - s->clkcon &= ~SDHC_CLOCK_INT_STABLE; - } - break; - case SDHC_NORINTSTS: - if (s->norintstsen & SDHC_NISEN_CARDINT) { - value &= ~SDHC_NIS_CARDINT; - } - s->norintsts &= mask | ~value; - s->errintsts &= (mask >> 16) | ~(value >> 16); - if (s->errintsts) { - s->norintsts |= SDHC_NIS_ERR; - } else { - s->norintsts &= ~SDHC_NIS_ERR; - } - sdhci_update_irq(s); - break; - case SDHC_NORINTSTSEN: - MASKED_WRITE(s->norintstsen, mask, value); - MASKED_WRITE(s->errintstsen, mask >> 16, value >> 16); - s->norintsts &= s->norintstsen; - s->errintsts &= s->errintstsen; - if (s->errintsts) { - s->norintsts |= SDHC_NIS_ERR; - } 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: - MASKED_WRITE(s->norintsigen, mask, value); - MASKED_WRITE(s->errintsigen, mask >> 16, value >> 16); - sdhci_update_irq(s); - break; - case SDHC_ADMAERR: - MASKED_WRITE(s->admaerr, mask, value); - break; - case SDHC_ADMASYSADDR: - s->admasysaddr = (s->admasysaddr & (0xFFFFFFFF00000000ULL | - (uint64_t)mask)) | (uint64_t)value; - break; - case SDHC_ADMASYSADDR + 4: - s->admasysaddr = (s->admasysaddr & (0x00000000FFFFFFFFULL | - ((uint64_t)mask << 32))) | ((uint64_t)value << 32); - break; - case SDHC_FEAER: - s->acmd12errsts |= value; - s->errintsts |= (value >> 16) & s->errintstsen; - if (s->acmd12errsts) { - s->errintsts |= SDHC_EIS_CMD12ERR; - } - if (s->errintsts) { - s->norintsts |= SDHC_NIS_ERR; - } - sdhci_update_irq(s); - break; - default: - ERRPRINT("bad %ub write offset: addr[0x%04x] <- %u(0x%x)\n", - size, (int)offset, value >> shift, value >> shift); - break; - } - DPRINT_L2("write %ub: addr[0x%04x] <- %u(0x%x)\n", - size, (int)offset, value >> shift, value >> shift); -} - -static const MemoryRegionOps sdhci_mmio_ops = { - .read = sdhci_read, - .write = sdhci_write, - .valid = { - .min_access_size = 1, - .max_access_size = 4, - .unaligned = false - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static inline unsigned int sdhci_get_fifolen(SDHCIState *s) -{ - switch (SDHC_CAPAB_BLOCKSIZE(s->capareg)) { - case 0: - return 512; - case 1: - return 1024; - case 2: - return 2048; - default: - hw_error("SDHC: unsupported value for maximum block size\n"); - return 0; - } -} - -static void sdhci_initfn(SDHCIState *s) -{ - 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); -} - -static void sdhci_uninitfn(SDHCIState *s) -{ - timer_del(s->insert_timer); - timer_free(s->insert_timer); - timer_del(s->transfer_timer); - timer_free(s->transfer_timer); - qemu_free_irq(s->eject_cb); - qemu_free_irq(s->ro_cb); - - 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, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(sdmasysad, SDHCIState), - VMSTATE_UINT16(blksize, SDHCIState), - VMSTATE_UINT16(blkcnt, SDHCIState), - VMSTATE_UINT32(argument, SDHCIState), - VMSTATE_UINT16(trnmod, SDHCIState), - VMSTATE_UINT16(cmdreg, SDHCIState), - VMSTATE_UINT32_ARRAY(rspreg, SDHCIState, 4), - VMSTATE_UINT32(prnsts, SDHCIState), - VMSTATE_UINT8(hostctl, SDHCIState), - VMSTATE_UINT8(pwrcon, SDHCIState), - VMSTATE_UINT8(blkgap, SDHCIState), - VMSTATE_UINT8(wakcon, SDHCIState), - VMSTATE_UINT16(clkcon, SDHCIState), - VMSTATE_UINT8(timeoutcon, SDHCIState), - VMSTATE_UINT8(admaerr, SDHCIState), - VMSTATE_UINT16(norintsts, SDHCIState), - VMSTATE_UINT16(errintsts, SDHCIState), - VMSTATE_UINT16(norintstsen, SDHCIState), - VMSTATE_UINT16(errintstsen, SDHCIState), - VMSTATE_UINT16(norintsigen, SDHCIState), - VMSTATE_UINT16(errintsigen, SDHCIState), - VMSTATE_UINT16(acmd12errsts, SDHCIState), - VMSTATE_UINT16(data_count, SDHCIState), - VMSTATE_UINT64(admasysaddr, SDHCIState), - VMSTATE_UINT8(stopped_state, SDHCIState), - VMSTATE_VBUFFER_UINT32(fifo_buffer, SDHCIState, 1, NULL, 0, buf_maxsz), - 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_pci_properties[] = { - DEFINE_PROP_UINT32("capareg", SDHCIState, capareg, - SDHC_CAPAB_REG_DEFAULT), - DEFINE_PROP_UINT32("maxcurr", SDHCIState, maxcurr, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void sdhci_pci_realize(PCIDevice *dev, Error **errp) -{ - SDHCIState *s = PCI_SDHCI(dev); - dev->config[PCI_CLASS_PROG] = 0x01; /* Standard Host supported DMA */ - dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */ - sdhci_initfn(s); - s->buf_maxsz = sdhci_get_fifolen(s); - s->fifo_buffer = g_malloc0(s->buf_maxsz); - s->irq = pci_allocate_irq(dev); - memory_region_init_io(&s->iomem, OBJECT(s), &sdhci_mmio_ops, s, "sdhci", - SDHC_REGISTERS_MAP_SIZE); - pci_register_bar(dev, 0, 0, &s->iomem); -} - -static void sdhci_pci_exit(PCIDevice *dev) -{ - SDHCIState *s = PCI_SDHCI(dev); - sdhci_uninitfn(s); -} - -static void sdhci_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = sdhci_pci_realize; - k->exit = sdhci_pci_exit; - k->vendor_id = PCI_VENDOR_ID_REDHAT; - k->device_id = PCI_DEVICE_ID_REDHAT_SDHCI; - k->class_id = PCI_CLASS_SYSTEM_SDHCI; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->vmsd = &sdhci_vmstate; - dc->props = sdhci_pci_properties; - dc->reset = sdhci_poweron_reset; -} - -static const TypeInfo sdhci_pci_info = { - .name = TYPE_PCI_SDHCI, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(SDHCIState), - .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); -} - -static void sdhci_sysbus_finalize(Object *obj) -{ - SDHCIState *s = SYSBUS_SDHCI(obj); - sdhci_uninitfn(s); -} - -static void sdhci_sysbus_realize(DeviceState *dev, Error ** errp) -{ - SDHCIState *s = SYSBUS_SDHCI(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - - s->buf_maxsz = sdhci_get_fifolen(s); - s->fifo_buffer = g_malloc0(s->buf_maxsz); - sysbus_init_irq(sbd, &s->irq); - memory_region_init_io(&s->iomem, OBJECT(s), &sdhci_mmio_ops, s, "sdhci", - SDHC_REGISTERS_MAP_SIZE); - sysbus_init_mmio(sbd, &s->iomem); -} - -static void sdhci_sysbus_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &sdhci_vmstate; - dc->props = sdhci_sysbus_properties; - dc->realize = sdhci_sysbus_realize; - dc->reset = sdhci_poweron_reset; -} - -static const TypeInfo sdhci_sysbus_info = { - .name = TYPE_SYSBUS_SDHCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SDHCIState), - .instance_init = sdhci_sysbus_init, - .instance_finalize = sdhci_sysbus_finalize, - .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 deleted file mode 100644 index 075e4ed5d..000000000 --- a/qemu/hw/sd/ssi-sd.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - * SSI to SD card adapter. - * - * Copyright (c) 2007-2009 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include "qemu/osdep.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "hw/ssi/ssi.h" -#include "hw/sd/sd.h" - -//#define DEBUG_SSI_SD 1 - -#ifdef DEBUG_SSI_SD -#define DPRINTF(fmt, ...) \ -do { printf("ssi_sd: " fmt , ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "ssi_sd: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "ssi_sd: error: " fmt , ## __VA_ARGS__);} while (0) -#endif - -typedef enum { - SSI_SD_CMD, - SSI_SD_CMDARG, - SSI_SD_RESPONSE, - SSI_SD_DATA_START, - SSI_SD_DATA_READ, -} ssi_sd_mode; - -typedef struct { - SSISlave ssidev; - ssi_sd_mode mode; - int cmd; - uint8_t cmdarg[4]; - uint8_t response[5]; - int arglen; - int response_pos; - int stopping; - SDState *sd; -} ssi_sd_state; - -/* State word bits. */ -#define SSI_SDR_LOCKED 0x0001 -#define SSI_SDR_WP_ERASE 0x0002 -#define SSI_SDR_ERROR 0x0004 -#define SSI_SDR_CC_ERROR 0x0008 -#define SSI_SDR_ECC_FAILED 0x0010 -#define SSI_SDR_WP_VIOLATION 0x0020 -#define SSI_SDR_ERASE_PARAM 0x0040 -#define SSI_SDR_OUT_OF_RANGE 0x0080 -#define SSI_SDR_IDLE 0x0100 -#define SSI_SDR_ERASE_RESET 0x0200 -#define SSI_SDR_ILLEGAL_COMMAND 0x0400 -#define SSI_SDR_COM_CRC_ERROR 0x0800 -#define SSI_SDR_ERASE_SEQ_ERROR 0x1000 -#define SSI_SDR_ADDRESS_ERROR 0x2000 -#define SSI_SDR_PARAMETER_ERROR 0x4000 - -static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val) -{ - ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, dev); - - /* Special case: allow CMD12 (STOP TRANSMISSION) while reading data. */ - if (s->mode == SSI_SD_DATA_READ && val == 0x4d) { - s->mode = SSI_SD_CMD; - /* There must be at least one byte delay before the card responds. */ - s->stopping = 1; - } - - switch (s->mode) { - case SSI_SD_CMD: - if (val == 0xff) { - DPRINTF("NULL command\n"); - return 0xff; - } - s->cmd = val & 0x3f; - s->mode = SSI_SD_CMDARG; - s->arglen = 0; - return 0xff; - case SSI_SD_CMDARG: - if (s->arglen == 4) { - SDRequest request; - uint8_t longresp[16]; - /* FIXME: Check CRC. */ - request.cmd = s->cmd; - request.arg = (s->cmdarg[0] << 24) | (s->cmdarg[1] << 16) - | (s->cmdarg[2] << 8) | s->cmdarg[3]; - DPRINTF("CMD%d arg 0x%08x\n", s->cmd, request.arg); - s->arglen = sd_do_command(s->sd, &request, longresp); - if (s->arglen <= 0) { - s->arglen = 1; - s->response[0] = 4; - DPRINTF("SD command failed\n"); - } else if (s->cmd == 58) { - /* CMD58 returns R3 response (OCR) */ - DPRINTF("Returned OCR\n"); - s->arglen = 5; - s->response[0] = 1; - memcpy(&s->response[1], longresp, 4); - } else if (s->arglen != 4) { - BADF("Unexpected response to cmd %d\n", s->cmd); - /* Illegal command is about as near as we can get. */ - s->arglen = 1; - s->response[0] = 4; - } else { - /* All other commands return status. */ - uint32_t cardstatus; - uint16_t status; - /* CMD13 returns a 2-byte statuse work. Other commands - only return the first byte. */ - s->arglen = (s->cmd == 13) ? 2 : 1; - cardstatus = (longresp[0] << 24) | (longresp[1] << 16) - | (longresp[2] << 8) | longresp[3]; - status = 0; - if (((cardstatus >> 9) & 0xf) < 4) - status |= SSI_SDR_IDLE; - if (cardstatus & ERASE_RESET) - status |= SSI_SDR_ERASE_RESET; - if (cardstatus & ILLEGAL_COMMAND) - status |= SSI_SDR_ILLEGAL_COMMAND; - if (cardstatus & COM_CRC_ERROR) - status |= SSI_SDR_COM_CRC_ERROR; - if (cardstatus & ERASE_SEQ_ERROR) - status |= SSI_SDR_ERASE_SEQ_ERROR; - if (cardstatus & ADDRESS_ERROR) - status |= SSI_SDR_ADDRESS_ERROR; - if (cardstatus & CARD_IS_LOCKED) - status |= SSI_SDR_LOCKED; - if (cardstatus & (LOCK_UNLOCK_FAILED | WP_ERASE_SKIP)) - status |= SSI_SDR_WP_ERASE; - if (cardstatus & SD_ERROR) - status |= SSI_SDR_ERROR; - if (cardstatus & CC_ERROR) - status |= SSI_SDR_CC_ERROR; - if (cardstatus & CARD_ECC_FAILED) - status |= SSI_SDR_ECC_FAILED; - if (cardstatus & WP_VIOLATION) - status |= SSI_SDR_WP_VIOLATION; - if (cardstatus & ERASE_PARAM) - status |= SSI_SDR_ERASE_PARAM; - if (cardstatus & (OUT_OF_RANGE | CID_CSD_OVERWRITE)) - status |= SSI_SDR_OUT_OF_RANGE; - /* ??? Don't know what Parameter Error really means, so - assume it's set if the second byte is nonzero. */ - if (status & 0xff) - status |= SSI_SDR_PARAMETER_ERROR; - s->response[0] = status >> 8; - s->response[1] = status; - DPRINTF("Card status 0x%02x\n", status); - } - s->mode = SSI_SD_RESPONSE; - s->response_pos = 0; - } else { - s->cmdarg[s->arglen++] = val; - } - return 0xff; - case SSI_SD_RESPONSE: - if (s->stopping) { - s->stopping = 0; - return 0xff; - } - if (s->response_pos < s->arglen) { - DPRINTF("Response 0x%02x\n", s->response[s->response_pos]); - return s->response[s->response_pos++]; - } - if (sd_data_ready(s->sd)) { - DPRINTF("Data read\n"); - s->mode = SSI_SD_DATA_START; - } else { - DPRINTF("End of command\n"); - s->mode = SSI_SD_CMD; - } - return 0xff; - case SSI_SD_DATA_START: - DPRINTF("Start read block\n"); - s->mode = SSI_SD_DATA_READ; - return 0xfe; - case SSI_SD_DATA_READ: - val = sd_read_data(s->sd); - if (!sd_data_ready(s->sd)) { - DPRINTF("Data read end\n"); - s->mode = SSI_SD_CMD; - } - return val; - } - /* Should never happen. */ - return 0xff; -} - -static void ssi_sd_save(QEMUFile *f, void *opaque) -{ - SSISlave *ss = SSI_SLAVE(opaque); - ssi_sd_state *s = (ssi_sd_state *)opaque; - int i; - - qemu_put_be32(f, s->mode); - qemu_put_be32(f, s->cmd); - for (i = 0; i < 4; i++) - qemu_put_be32(f, s->cmdarg[i]); - for (i = 0; i < 5; i++) - qemu_put_be32(f, s->response[i]); - qemu_put_be32(f, s->arglen); - qemu_put_be32(f, s->response_pos); - qemu_put_be32(f, s->stopping); - - qemu_put_be32(f, ss->cs); -} - -static int ssi_sd_load(QEMUFile *f, void *opaque, int version_id) -{ - SSISlave *ss = SSI_SLAVE(opaque); - ssi_sd_state *s = (ssi_sd_state *)opaque; - int i; - - if (version_id != 1) - return -EINVAL; - - s->mode = qemu_get_be32(f); - s->cmd = qemu_get_be32(f); - for (i = 0; i < 4; i++) - s->cmdarg[i] = qemu_get_be32(f); - for (i = 0; i < 5; i++) - s->response[i] = qemu_get_be32(f); - s->arglen = qemu_get_be32(f); - if (s->mode == SSI_SD_CMDARG && - (s->arglen < 0 || s->arglen >= ARRAY_SIZE(s->cmdarg))) { - return -EINVAL; - } - s->response_pos = qemu_get_be32(f); - s->stopping = qemu_get_be32(f); - if (s->mode == SSI_SD_RESPONSE && - (s->response_pos < 0 || s->response_pos >= ARRAY_SIZE(s->response) || - (!s->stopping && s->arglen > ARRAY_SIZE(s->response)))) { - return -EINVAL; - } - - ss->cs = qemu_get_be32(f); - - return 0; -} - -static int ssi_sd_init(SSISlave *d) -{ - DeviceState *dev = DEVICE(d); - ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, d); - DriveInfo *dinfo; - - s->mode = SSI_SD_CMD; - /* FIXME use a qdev drive property instead of drive_get_next() */ - dinfo = drive_get_next(IF_SD); - s->sd = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, true); - if (s->sd == NULL) { - return -1; - } - register_savevm(dev, "ssi_sd", -1, 1, ssi_sd_save, ssi_sd_load, s); - return 0; -} - -static void ssi_sd_class_init(ObjectClass *klass, void *data) -{ - SSISlaveClass *k = SSI_SLAVE_CLASS(klass); - - k->init = ssi_sd_init; - k->transfer = ssi_sd_transfer; - k->cs_polarity = SSI_CS_LOW; -} - -static const TypeInfo ssi_sd_info = { - .name = "ssi-sd", - .parent = TYPE_SSI_SLAVE, - .instance_size = sizeof(ssi_sd_state), - .class_init = ssi_sd_class_init, -}; - -static void ssi_sd_register_types(void) -{ - type_register_static(&ssi_sd_info); -} - -type_init(ssi_sd_register_types) diff --git a/qemu/hw/sh4/Makefile.objs b/qemu/hw/sh4/Makefile.objs deleted file mode 100644 index 2393702c5..000000000 --- a/qemu/hw/sh4/Makefile.objs +++ /dev/null @@ -1,4 +0,0 @@ -obj-y += shix.o r2d.o - -obj-y += sh7750.o sh7750_regnames.o -obj-y += sh_pci.o diff --git a/qemu/hw/sh4/r2d.c b/qemu/hw/sh4/r2d.c deleted file mode 100644 index db373c70c..000000000 --- a/qemu/hw/sh4/r2d.c +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Renesas SH7751R R2D-PLUS emulation - * - * Copyright (c) 2007 Magnus Damm - * Copyright (c) 2008 Paul Mundt - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/sysbus.h" -#include "hw/hw.h" -#include "hw/sh4/sh.h" -#include "hw/devices.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" -#include "hw/pci/pci.h" -#include "net/net.h" -#include "sh7750_regs.h" -#include "hw/ide.h" -#include "hw/loader.h" -#include "hw/usb.h" -#include "hw/block/flash.h" -#include "sysemu/block-backend.h" -#include "exec/address-spaces.h" - -#define FLASH_BASE 0x00000000 -#define FLASH_SIZE 0x02000000 - -#define SDRAM_BASE 0x0c000000 /* Physical location of SDRAM: Area 3 */ -#define SDRAM_SIZE 0x04000000 - -#define SM501_VRAM_SIZE 0x800000 - -#define BOOT_PARAMS_OFFSET 0x0010000 -/* CONFIG_BOOT_LINK_OFFSET of Linux kernel */ -#define LINUX_LOAD_OFFSET 0x0800000 -#define INITRD_LOAD_OFFSET 0x1800000 - -#define PA_IRLMSK 0x00 -#define PA_POWOFF 0x30 -#define PA_VERREG 0x32 -#define PA_OUTPORT 0x36 - -typedef struct { - uint16_t bcr; - uint16_t irlmsk; - uint16_t irlmon; - uint16_t cfctl; - uint16_t cfpow; - uint16_t dispctl; - uint16_t sdmpow; - uint16_t rtcce; - uint16_t pcicd; - uint16_t voyagerrts; - uint16_t cfrst; - uint16_t admrts; - uint16_t extrst; - uint16_t cfcdintclr; - uint16_t keyctlclr; - uint16_t pad0; - uint16_t pad1; - uint16_t verreg; - uint16_t inport; - uint16_t outport; - uint16_t bverreg; - -/* output pin */ - qemu_irq irl; - MemoryRegion iomem; -} r2d_fpga_t; - -enum r2d_fpga_irq { - PCI_INTD, CF_IDE, CF_CD, PCI_INTC, SM501, KEY, RTC_A, RTC_T, - SDCARD, PCI_INTA, PCI_INTB, EXT, TP, - NR_IRQS -}; - -static const struct { short irl; uint16_t msk; } irqtab[NR_IRQS] = { - [CF_IDE] = { 1, 1<<9 }, - [CF_CD] = { 2, 1<<8 }, - [PCI_INTA] = { 9, 1<<14 }, - [PCI_INTB] = { 10, 1<<13 }, - [PCI_INTC] = { 3, 1<<12 }, - [PCI_INTD] = { 0, 1<<11 }, - [SM501] = { 4, 1<<10 }, - [KEY] = { 5, 1<<6 }, - [RTC_A] = { 6, 1<<5 }, - [RTC_T] = { 7, 1<<4 }, - [SDCARD] = { 8, 1<<7 }, - [EXT] = { 11, 1<<0 }, - [TP] = { 12, 1<<15 }, -}; - -static void update_irl(r2d_fpga_t *fpga) -{ - int i, irl = 15; - for (i = 0; i < NR_IRQS; i++) - if (fpga->irlmon & fpga->irlmsk & irqtab[i].msk) - if (irqtab[i].irl < irl) - irl = irqtab[i].irl; - qemu_set_irq(fpga->irl, irl ^ 15); -} - -static void r2d_fpga_irq_set(void *opaque, int n, int level) -{ - r2d_fpga_t *fpga = opaque; - if (level) - fpga->irlmon |= irqtab[n].msk; - else - fpga->irlmon &= ~irqtab[n].msk; - update_irl(fpga); -} - -static uint64_t r2d_fpga_read(void *opaque, hwaddr addr, unsigned int size) -{ - r2d_fpga_t *s = opaque; - - switch (addr) { - case PA_IRLMSK: - return s->irlmsk; - case PA_OUTPORT: - return s->outport; - case PA_POWOFF: - return 0x00; - case PA_VERREG: - return 0x10; - } - - return 0; -} - -static void -r2d_fpga_write(void *opaque, hwaddr addr, uint64_t value, unsigned int size) -{ - r2d_fpga_t *s = opaque; - - switch (addr) { - case PA_IRLMSK: - s->irlmsk = value; - update_irl(s); - break; - case PA_OUTPORT: - s->outport = value; - break; - case PA_POWOFF: - if (value & 1) { - qemu_system_shutdown_request(); - } - break; - case PA_VERREG: - /* Discard writes */ - break; - } -} - -static const MemoryRegionOps r2d_fpga_ops = { - .read = r2d_fpga_read, - .write = r2d_fpga_write, - .impl.min_access_size = 2, - .impl.max_access_size = 2, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static qemu_irq *r2d_fpga_init(MemoryRegion *sysmem, - hwaddr base, qemu_irq irl) -{ - r2d_fpga_t *s; - - s = g_malloc0(sizeof(r2d_fpga_t)); - - s->irl = irl; - - memory_region_init_io(&s->iomem, NULL, &r2d_fpga_ops, s, "r2d-fpga", 0x40); - memory_region_add_subregion(sysmem, base, &s->iomem); - return qemu_allocate_irqs(r2d_fpga_irq_set, s, NR_IRQS); -} - -typedef struct ResetData { - SuperHCPU *cpu; - uint32_t vector; -} ResetData; - -static void main_cpu_reset(void *opaque) -{ - ResetData *s = (ResetData *)opaque; - CPUSH4State *env = &s->cpu->env; - - cpu_reset(CPU(s->cpu)); - env->pc = s->vector; -} - -static struct QEMU_PACKED -{ - int mount_root_rdonly; - int ramdisk_flags; - int orig_root_dev; - int loader_type; - int initrd_start; - int initrd_size; - - char pad[232]; - - char kernel_cmdline[256]; -} boot_params; - -static void r2d_init(MachineState *machine) -{ - 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; - SuperHCPU *cpu; - CPUSH4State *env; - ResetData *reset_info; - struct SH7750State *s; - MemoryRegion *sdram = g_new(MemoryRegion, 1); - qemu_irq *irq; - DriveInfo *dinfo; - int i; - DeviceState *dev; - SysBusDevice *busdev; - MemoryRegion *address_space_mem = get_system_memory(); - PCIBus *pci_bus; - - if (cpu_model == NULL) { - cpu_model = "SH7751R"; - } - - cpu = cpu_sh4_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - env = &cpu->env; - - reset_info = g_malloc0(sizeof(ResetData)); - reset_info->cpu = cpu; - reset_info->vector = env->pc; - qemu_register_reset(main_cpu_reset, reset_info); - - /* Allocate memory space */ - 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 */ - s = sh7750_init(cpu, address_space_mem); - irq = r2d_fpga_init(address_space_mem, 0x04000000, sh7750_irl(s)); - - dev = qdev_create(NULL, "sh_pci"); - busdev = SYS_BUS_DEVICE(dev); - qdev_init_nofail(dev); - pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci")); - sysbus_mmio_map(busdev, 0, P4ADDR(0x1e200000)); - sysbus_mmio_map(busdev, 1, A7ADDR(0x1e200000)); - sysbus_connect_irq(busdev, 0, irq[PCI_INTA]); - sysbus_connect_irq(busdev, 1, irq[PCI_INTB]); - sysbus_connect_irq(busdev, 2, irq[PCI_INTC]); - sysbus_connect_irq(busdev, 3, irq[PCI_INTD]); - - sm501_init(address_space_mem, 0x10000000, SM501_VRAM_SIZE, - irq[SM501], serial_hds[2]); - - /* onboard CF (True IDE mode, Master only). */ - dinfo = drive_get(IF_IDE, 0, 0); - dev = qdev_create(NULL, "mmio-ide"); - busdev = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(busdev, 0, irq[CF_IDE]); - qdev_prop_set_uint32(dev, "shift", 1); - qdev_init_nofail(dev); - sysbus_mmio_map(busdev, 0, 0x14001000); - sysbus_mmio_map(busdev, 1, 0x1400080c); - mmio_ide_init_drives(dev, dinfo, NULL); - - /* onboard flash memory */ - dinfo = drive_get(IF_PFLASH, 0, 0); - pflash_cfi02_register(0x0, NULL, "r2d.flash", FLASH_SIZE, - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - (16 * 1024), FLASH_SIZE >> 16, - 1, 4, 0x0000, 0x0000, 0x0000, 0x0000, - 0x555, 0x2aa, 0); - - /* NIC: rtl8139 on-board, and 2 slots. */ - for (i = 0; i < nb_nics; i++) - pci_nic_init_nofail(&nd_table[i], pci_bus, - "rtl8139", i==0 ? "2" : NULL); - - /* USB keyboard */ - usb_create_simple(usb_bus_find(-1), "usb-kbd"); - - /* Todo: register on board registers */ - memset(&boot_params, 0, sizeof(boot_params)); - - if (kernel_filename) { - int kernel_size; - - kernel_size = load_image_targphys(kernel_filename, - SDRAM_BASE + LINUX_LOAD_OFFSET, - INITRD_LOAD_OFFSET - LINUX_LOAD_OFFSET); - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); - exit(1); - } - - /* initialization which should be done by firmware */ - address_space_stl(&address_space_memory, SH7750_BCR1, 1 << 3, - MEMTXATTRS_UNSPECIFIED, NULL); /* cs3 SDRAM */ - address_space_stw(&address_space_memory, SH7750_BCR2, 3 << (3 * 2), - MEMTXATTRS_UNSPECIFIED, NULL); /* cs3 32bit */ - reset_info->vector = (SDRAM_BASE + LINUX_LOAD_OFFSET) | 0xa0000000; /* Start from P2 area */ - } - - if (initrd_filename) { - int initrd_size; - - initrd_size = load_image_targphys(initrd_filename, - SDRAM_BASE + INITRD_LOAD_OFFSET, - SDRAM_SIZE - INITRD_LOAD_OFFSET); - - if (initrd_size < 0) { - fprintf(stderr, "qemu: could not load initrd '%s'\n", initrd_filename); - exit(1); - } - - /* initialization which should be done by firmware */ - boot_params.loader_type = tswap32(1); - boot_params.initrd_start = tswap32(INITRD_LOAD_OFFSET); - boot_params.initrd_size = tswap32(initrd_size); - } - - if (kernel_cmdline) { - /* I see no evidence that this .kernel_cmdline buffer requires - NUL-termination, so using strncpy should be ok. */ - strncpy(boot_params.kernel_cmdline, kernel_cmdline, - sizeof(boot_params.kernel_cmdline)); - } - - rom_add_blob_fixed("boot_params", &boot_params, sizeof(boot_params), - SDRAM_BASE + BOOT_PARAMS_OFFSET); -} - -static void r2d_machine_init(MachineClass *mc) -{ - mc->desc = "r2d-plus board"; - mc->init = r2d_init; -} - -DEFINE_MACHINE("r2d", r2d_machine_init) diff --git a/qemu/hw/sh4/sh7750.c b/qemu/hw/sh4/sh7750.c deleted file mode 100644 index a1ea760f6..000000000 --- a/qemu/hw/sh4/sh7750.c +++ /dev/null @@ -1,842 +0,0 @@ -/* - * SH7750 device - * - * Copyright (c) 2007 Magnus Damm - * Copyright (c) 2005 Samuel Tardieu - * - * 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/sh4/sh.h" -#include "sysemu/sysemu.h" -#include "sh7750_regs.h" -#include "sh7750_regnames.h" -#include "hw/sh4/sh_intc.h" -#include "cpu.h" -#include "exec/address-spaces.h" - -#define NB_DEVICES 4 - -typedef struct SH7750State { - MemoryRegion iomem; - MemoryRegion iomem_1f0; - MemoryRegion iomem_ff0; - MemoryRegion iomem_1f8; - MemoryRegion iomem_ff8; - MemoryRegion iomem_1fc; - MemoryRegion iomem_ffc; - MemoryRegion mmct_iomem; - /* CPU */ - SuperHCPU *cpu; - /* Peripheral frequency in Hz */ - uint32_t periph_freq; - /* SDRAM controller */ - uint32_t bcr1; - uint16_t bcr2; - uint16_t bcr3; - uint32_t bcr4; - uint16_t rfcr; - /* PCMCIA controller */ - uint16_t pcr; - /* IO ports */ - uint16_t gpioic; - uint32_t pctra; - uint32_t pctrb; - uint16_t portdira; /* Cached */ - uint16_t portpullupa; /* Cached */ - uint16_t portdirb; /* Cached */ - uint16_t portpullupb; /* Cached */ - uint16_t pdtra; - uint16_t pdtrb; - uint16_t periph_pdtra; /* Imposed by the peripherals */ - uint16_t periph_portdira; /* Direction seen from the peripherals */ - uint16_t periph_pdtrb; /* Imposed by the peripherals */ - uint16_t periph_portdirb; /* Direction seen from the peripherals */ - sh7750_io_device *devices[NB_DEVICES]; /* External peripherals */ - - /* Cache */ - uint32_t ccr; - - struct intc_desc intc; -} SH7750State; - -static inline int has_bcr3_and_bcr4(SH7750State * s) -{ - return s->cpu->env.features & SH_FEATURE_BCR3_AND_BCR4; -} -/********************************************************************** - I/O ports -**********************************************************************/ - -int sh7750_register_io_device(SH7750State * s, sh7750_io_device * device) -{ - int i; - - for (i = 0; i < NB_DEVICES; i++) { - if (s->devices[i] == NULL) { - s->devices[i] = device; - return 0; - } - } - return -1; -} - -static uint16_t portdir(uint32_t v) -{ -#define EVENPORTMASK(n) ((v & (1<<((n)<<1))) >> (n)) - return - EVENPORTMASK(15) | EVENPORTMASK(14) | EVENPORTMASK(13) | - EVENPORTMASK(12) | EVENPORTMASK(11) | EVENPORTMASK(10) | - EVENPORTMASK(9) | EVENPORTMASK(8) | EVENPORTMASK(7) | - EVENPORTMASK(6) | EVENPORTMASK(5) | EVENPORTMASK(4) | - EVENPORTMASK(3) | EVENPORTMASK(2) | EVENPORTMASK(1) | - EVENPORTMASK(0); -} - -static uint16_t portpullup(uint32_t v) -{ -#define ODDPORTMASK(n) ((v & (1<<(((n)<<1)+1))) >> (n)) - return - ODDPORTMASK(15) | ODDPORTMASK(14) | ODDPORTMASK(13) | - ODDPORTMASK(12) | ODDPORTMASK(11) | ODDPORTMASK(10) | - ODDPORTMASK(9) | ODDPORTMASK(8) | ODDPORTMASK(7) | ODDPORTMASK(6) | - ODDPORTMASK(5) | ODDPORTMASK(4) | ODDPORTMASK(3) | ODDPORTMASK(2) | - ODDPORTMASK(1) | ODDPORTMASK(0); -} - -static uint16_t porta_lines(SH7750State * s) -{ - return (s->portdira & s->pdtra) | /* CPU */ - (s->periph_portdira & s->periph_pdtra) | /* Peripherals */ - (~(s->portdira | s->periph_portdira) & s->portpullupa); /* Pullups */ -} - -static uint16_t portb_lines(SH7750State * s) -{ - return (s->portdirb & s->pdtrb) | /* CPU */ - (s->periph_portdirb & s->periph_pdtrb) | /* Peripherals */ - (~(s->portdirb | s->periph_portdirb) & s->portpullupb); /* Pullups */ -} - -static void gen_port_interrupts(SH7750State * s) -{ - /* XXXXX interrupts not generated */ -} - -static void porta_changed(SH7750State * s, uint16_t prev) -{ - uint16_t currenta, changes; - int i, r = 0; - -#if 0 - fprintf(stderr, "porta changed from 0x%04x to 0x%04x\n", - prev, porta_lines(s)); - fprintf(stderr, "pdtra=0x%04x, pctra=0x%08x\n", s->pdtra, s->pctra); -#endif - currenta = porta_lines(s); - if (currenta == prev) - return; - changes = currenta ^ prev; - - for (i = 0; i < NB_DEVICES; i++) { - if (s->devices[i] && (s->devices[i]->portamask_trigger & changes)) { - r |= s->devices[i]->port_change_cb(currenta, portb_lines(s), - &s->periph_pdtra, - &s->periph_portdira, - &s->periph_pdtrb, - &s->periph_portdirb); - } - } - - if (r) - gen_port_interrupts(s); -} - -static void portb_changed(SH7750State * s, uint16_t prev) -{ - uint16_t currentb, changes; - int i, r = 0; - - currentb = portb_lines(s); - if (currentb == prev) - return; - changes = currentb ^ prev; - - for (i = 0; i < NB_DEVICES; i++) { - if (s->devices[i] && (s->devices[i]->portbmask_trigger & changes)) { - r |= s->devices[i]->port_change_cb(portb_lines(s), currentb, - &s->periph_pdtra, - &s->periph_portdira, - &s->periph_pdtrb, - &s->periph_portdirb); - } - } - - if (r) - gen_port_interrupts(s); -} - -/********************************************************************** - Memory -**********************************************************************/ - -static void error_access(const char *kind, hwaddr addr) -{ - fprintf(stderr, "%s to %s (0x" TARGET_FMT_plx ") not supported\n", - kind, regname(addr), addr); -} - -static void ignore_access(const char *kind, hwaddr addr) -{ - fprintf(stderr, "%s to %s (0x" TARGET_FMT_plx ") ignored\n", - kind, regname(addr), addr); -} - -static uint32_t sh7750_mem_readb(void *opaque, hwaddr addr) -{ - switch (addr) { - default: - error_access("byte read", addr); - abort(); - } -} - -static uint32_t sh7750_mem_readw(void *opaque, hwaddr addr) -{ - SH7750State *s = opaque; - - switch (addr) { - case SH7750_BCR2_A7: - return s->bcr2; - case SH7750_BCR3_A7: - if(!has_bcr3_and_bcr4(s)) - error_access("word read", addr); - return s->bcr3; - case SH7750_FRQCR_A7: - return 0; - case SH7750_PCR_A7: - return s->pcr; - case SH7750_RFCR_A7: - fprintf(stderr, - "Read access to refresh count register, incrementing\n"); - return s->rfcr++; - case SH7750_PDTRA_A7: - return porta_lines(s); - case SH7750_PDTRB_A7: - return portb_lines(s); - case SH7750_RTCOR_A7: - case SH7750_RTCNT_A7: - case SH7750_RTCSR_A7: - ignore_access("word read", addr); - return 0; - default: - error_access("word read", addr); - abort(); - } -} - -static uint32_t sh7750_mem_readl(void *opaque, hwaddr addr) -{ - SH7750State *s = opaque; - SuperHCPUClass *scc; - - switch (addr) { - case SH7750_BCR1_A7: - return s->bcr1; - case SH7750_BCR4_A7: - if(!has_bcr3_and_bcr4(s)) - error_access("long read", addr); - return s->bcr4; - case SH7750_WCR1_A7: - case SH7750_WCR2_A7: - case SH7750_WCR3_A7: - case SH7750_MCR_A7: - ignore_access("long read", addr); - return 0; - case SH7750_MMUCR_A7: - return s->cpu->env.mmucr; - case SH7750_PTEH_A7: - return s->cpu->env.pteh; - case SH7750_PTEL_A7: - return s->cpu->env.ptel; - case SH7750_TTB_A7: - return s->cpu->env.ttb; - case SH7750_TEA_A7: - return s->cpu->env.tea; - case SH7750_TRA_A7: - return s->cpu->env.tra; - case SH7750_EXPEVT_A7: - return s->cpu->env.expevt; - case SH7750_INTEVT_A7: - return s->cpu->env.intevt; - case SH7750_CCR_A7: - return s->ccr; - case 0x1f000030: /* Processor version */ - scc = SUPERH_CPU_GET_CLASS(s->cpu); - return scc->pvr; - case 0x1f000040: /* Cache version */ - scc = SUPERH_CPU_GET_CLASS(s->cpu); - return scc->cvr; - case 0x1f000044: /* Processor revision */ - scc = SUPERH_CPU_GET_CLASS(s->cpu); - return scc->prr; - default: - error_access("long read", addr); - abort(); - } -} - -#define is_in_sdrmx(a, x) (a >= SH7750_SDMR ## x ## _A7 \ - && a <= (SH7750_SDMR ## x ## _A7 + SH7750_SDMR ## x ## _REGNB)) -static void sh7750_mem_writeb(void *opaque, hwaddr addr, - uint32_t mem_value) -{ - - if (is_in_sdrmx(addr, 2) || is_in_sdrmx(addr, 3)) { - ignore_access("byte write", addr); - return; - } - - error_access("byte write", addr); - abort(); -} - -static void sh7750_mem_writew(void *opaque, hwaddr addr, - uint32_t mem_value) -{ - SH7750State *s = opaque; - uint16_t temp; - - switch (addr) { - /* SDRAM controller */ - case SH7750_BCR2_A7: - s->bcr2 = mem_value; - return; - case SH7750_BCR3_A7: - if(!has_bcr3_and_bcr4(s)) - error_access("word write", addr); - s->bcr3 = mem_value; - return; - case SH7750_PCR_A7: - s->pcr = mem_value; - return; - case SH7750_RTCNT_A7: - case SH7750_RTCOR_A7: - case SH7750_RTCSR_A7: - ignore_access("word write", addr); - return; - /* IO ports */ - case SH7750_PDTRA_A7: - temp = porta_lines(s); - s->pdtra = mem_value; - porta_changed(s, temp); - return; - case SH7750_PDTRB_A7: - temp = portb_lines(s); - s->pdtrb = mem_value; - portb_changed(s, temp); - return; - case SH7750_RFCR_A7: - fprintf(stderr, "Write access to refresh count register\n"); - s->rfcr = mem_value; - return; - case SH7750_GPIOIC_A7: - s->gpioic = mem_value; - if (mem_value != 0) { - fprintf(stderr, "I/O interrupts not implemented\n"); - abort(); - } - return; - default: - error_access("word write", addr); - abort(); - } -} - -static void sh7750_mem_writel(void *opaque, hwaddr addr, - uint32_t mem_value) -{ - SH7750State *s = opaque; - uint16_t temp; - - switch (addr) { - /* SDRAM controller */ - case SH7750_BCR1_A7: - s->bcr1 = mem_value; - return; - case SH7750_BCR4_A7: - if(!has_bcr3_and_bcr4(s)) - error_access("long write", addr); - s->bcr4 = mem_value; - return; - case SH7750_WCR1_A7: - case SH7750_WCR2_A7: - case SH7750_WCR3_A7: - case SH7750_MCR_A7: - ignore_access("long write", addr); - return; - /* IO ports */ - case SH7750_PCTRA_A7: - temp = porta_lines(s); - s->pctra = mem_value; - s->portdira = portdir(mem_value); - s->portpullupa = portpullup(mem_value); - porta_changed(s, temp); - return; - case SH7750_PCTRB_A7: - temp = portb_lines(s); - s->pctrb = mem_value; - s->portdirb = portdir(mem_value); - s->portpullupb = portpullup(mem_value); - portb_changed(s, temp); - return; - case SH7750_MMUCR_A7: - if (mem_value & MMUCR_TI) { - cpu_sh4_invalidate_tlb(&s->cpu->env); - } - s->cpu->env.mmucr = mem_value & ~MMUCR_TI; - return; - case SH7750_PTEH_A7: - /* If asid changes, clear all registered tlb entries. */ - if ((s->cpu->env.pteh & 0xff) != (mem_value & 0xff)) { - tlb_flush(CPU(s->cpu), 1); - } - s->cpu->env.pteh = mem_value; - return; - case SH7750_PTEL_A7: - s->cpu->env.ptel = mem_value; - return; - case SH7750_PTEA_A7: - s->cpu->env.ptea = mem_value & 0x0000000f; - return; - case SH7750_TTB_A7: - s->cpu->env.ttb = mem_value; - return; - case SH7750_TEA_A7: - s->cpu->env.tea = mem_value; - return; - case SH7750_TRA_A7: - s->cpu->env.tra = mem_value & 0x000007ff; - return; - case SH7750_EXPEVT_A7: - s->cpu->env.expevt = mem_value & 0x000007ff; - return; - case SH7750_INTEVT_A7: - s->cpu->env.intevt = mem_value & 0x000007ff; - return; - case SH7750_CCR_A7: - s->ccr = mem_value; - return; - default: - error_access("long write", addr); - abort(); - } -} - -static const MemoryRegionOps sh7750_mem_ops = { - .old_mmio = { - .read = {sh7750_mem_readb, - sh7750_mem_readw, - sh7750_mem_readl }, - .write = {sh7750_mem_writeb, - sh7750_mem_writew, - sh7750_mem_writel }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* sh775x interrupt controller tables for sh_intc.c - * stolen from linux/arch/sh/kernel/cpu/sh4/setup-sh7750.c - */ - -enum { - UNUSED = 0, - - /* interrupt sources */ - IRL_0, IRL_1, IRL_2, IRL_3, IRL_4, IRL_5, IRL_6, IRL_7, - IRL_8, IRL_9, IRL_A, IRL_B, IRL_C, IRL_D, IRL_E, - IRL0, IRL1, IRL2, IRL3, - HUDI, GPIOI, - DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, DMAC_DMTE3, - DMAC_DMTE4, DMAC_DMTE5, DMAC_DMTE6, DMAC_DMTE7, - DMAC_DMAE, - PCIC0_PCISERR, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, - PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3, - TMU3, TMU4, TMU0, TMU1, TMU2_TUNI, TMU2_TICPI, - RTC_ATI, RTC_PRI, RTC_CUI, - SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI, - SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI, - WDT, - REF_RCMI, REF_ROVI, - - /* interrupt groups */ - DMAC, PCIC1, TMU2, RTC, SCI1, SCIF, REF, - /* irl bundle */ - IRL, - - NR_SOURCES, -}; - -static struct intc_vect vectors[] = { - INTC_VECT(HUDI, 0x600), INTC_VECT(GPIOI, 0x620), - INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420), - INTC_VECT(TMU2_TUNI, 0x440), INTC_VECT(TMU2_TICPI, 0x460), - INTC_VECT(RTC_ATI, 0x480), INTC_VECT(RTC_PRI, 0x4a0), - INTC_VECT(RTC_CUI, 0x4c0), - INTC_VECT(SCI1_ERI, 0x4e0), INTC_VECT(SCI1_RXI, 0x500), - INTC_VECT(SCI1_TXI, 0x520), INTC_VECT(SCI1_TEI, 0x540), - INTC_VECT(SCIF_ERI, 0x700), INTC_VECT(SCIF_RXI, 0x720), - INTC_VECT(SCIF_BRI, 0x740), INTC_VECT(SCIF_TXI, 0x760), - INTC_VECT(WDT, 0x560), - INTC_VECT(REF_RCMI, 0x580), INTC_VECT(REF_ROVI, 0x5a0), -}; - -static struct intc_group groups[] = { - INTC_GROUP(TMU2, TMU2_TUNI, TMU2_TICPI), - INTC_GROUP(RTC, RTC_ATI, RTC_PRI, RTC_CUI), - INTC_GROUP(SCI1, SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI), - INTC_GROUP(SCIF, SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI), - INTC_GROUP(REF, REF_RCMI, REF_ROVI), -}; - -static struct intc_prio_reg prio_registers[] = { - { 0xffd00004, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } }, - { 0xffd00008, 0, 16, 4, /* IPRB */ { WDT, REF, SCI1, 0 } }, - { 0xffd0000c, 0, 16, 4, /* IPRC */ { GPIOI, DMAC, SCIF, HUDI } }, - { 0xffd00010, 0, 16, 4, /* IPRD */ { IRL0, IRL1, IRL2, IRL3 } }, - { 0xfe080000, 0, 32, 4, /* INTPRI00 */ { 0, 0, 0, 0, - TMU4, TMU3, - PCIC1, PCIC0_PCISERR } }, -}; - -/* SH7750, SH7750S, SH7751 and SH7091 all have 4-channel DMA controllers */ - -static struct intc_vect vectors_dma4[] = { - INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660), - INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0), - INTC_VECT(DMAC_DMAE, 0x6c0), -}; - -static struct intc_group groups_dma4[] = { - INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, - DMAC_DMTE3, DMAC_DMAE), -}; - -/* SH7750R and SH7751R both have 8-channel DMA controllers */ - -static struct intc_vect vectors_dma8[] = { - INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660), - INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0), - INTC_VECT(DMAC_DMTE4, 0x780), INTC_VECT(DMAC_DMTE5, 0x7a0), - INTC_VECT(DMAC_DMTE6, 0x7c0), INTC_VECT(DMAC_DMTE7, 0x7e0), - INTC_VECT(DMAC_DMAE, 0x6c0), -}; - -static struct intc_group groups_dma8[] = { - INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, - DMAC_DMTE3, DMAC_DMTE4, DMAC_DMTE5, - DMAC_DMTE6, DMAC_DMTE7, DMAC_DMAE), -}; - -/* SH7750R, SH7751 and SH7751R all have two extra timer channels */ - -static struct intc_vect vectors_tmu34[] = { - INTC_VECT(TMU3, 0xb00), INTC_VECT(TMU4, 0xb80), -}; - -static struct intc_mask_reg mask_registers[] = { - { 0xfe080040, 0xfe080060, 32, /* INTMSK00 / INTMSKCLR00 */ - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, TMU4, TMU3, - PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, - PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, - PCIC1_PCIDMA3, PCIC0_PCISERR } }, -}; - -/* SH7750S, SH7750R, SH7751 and SH7751R all have IRLM priority registers */ - -static struct intc_vect vectors_irlm[] = { - INTC_VECT(IRL0, 0x240), INTC_VECT(IRL1, 0x2a0), - INTC_VECT(IRL2, 0x300), INTC_VECT(IRL3, 0x360), -}; - -/* SH7751 and SH7751R both have PCI */ - -static struct intc_vect vectors_pci[] = { - INTC_VECT(PCIC0_PCISERR, 0xa00), INTC_VECT(PCIC1_PCIERR, 0xae0), - INTC_VECT(PCIC1_PCIPWDWN, 0xac0), INTC_VECT(PCIC1_PCIPWON, 0xaa0), - INTC_VECT(PCIC1_PCIDMA0, 0xa80), INTC_VECT(PCIC1_PCIDMA1, 0xa60), - INTC_VECT(PCIC1_PCIDMA2, 0xa40), INTC_VECT(PCIC1_PCIDMA3, 0xa20), -}; - -static struct intc_group groups_pci[] = { - INTC_GROUP(PCIC1, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, - PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3), -}; - -static struct intc_vect vectors_irl[] = { - INTC_VECT(IRL_0, 0x200), - INTC_VECT(IRL_1, 0x220), - INTC_VECT(IRL_2, 0x240), - INTC_VECT(IRL_3, 0x260), - INTC_VECT(IRL_4, 0x280), - INTC_VECT(IRL_5, 0x2a0), - INTC_VECT(IRL_6, 0x2c0), - INTC_VECT(IRL_7, 0x2e0), - INTC_VECT(IRL_8, 0x300), - INTC_VECT(IRL_9, 0x320), - INTC_VECT(IRL_A, 0x340), - INTC_VECT(IRL_B, 0x360), - INTC_VECT(IRL_C, 0x380), - INTC_VECT(IRL_D, 0x3a0), - INTC_VECT(IRL_E, 0x3c0), -}; - -static struct intc_group groups_irl[] = { - INTC_GROUP(IRL, IRL_0, IRL_1, IRL_2, IRL_3, IRL_4, IRL_5, IRL_6, - IRL_7, IRL_8, IRL_9, IRL_A, IRL_B, IRL_C, IRL_D, IRL_E), -}; - -/********************************************************************** - Memory mapped cache and TLB -**********************************************************************/ - -#define MM_REGION_MASK 0x07000000 -#define MM_ICACHE_ADDR (0) -#define MM_ICACHE_DATA (1) -#define MM_ITLB_ADDR (2) -#define MM_ITLB_DATA (3) -#define MM_OCACHE_ADDR (4) -#define MM_OCACHE_DATA (5) -#define MM_UTLB_ADDR (6) -#define MM_UTLB_DATA (7) -#define MM_REGION_TYPE(addr) ((addr & MM_REGION_MASK) >> 24) - -static uint64_t invalid_read(void *opaque, hwaddr addr) -{ - abort(); - - return 0; -} - -static uint64_t sh7750_mmct_read(void *opaque, hwaddr addr, - unsigned size) -{ - SH7750State *s = opaque; - uint32_t ret = 0; - - if (size != 4) { - return invalid_read(opaque, addr); - } - - switch (MM_REGION_TYPE(addr)) { - case MM_ICACHE_ADDR: - case MM_ICACHE_DATA: - /* do nothing */ - break; - case MM_ITLB_ADDR: - ret = cpu_sh4_read_mmaped_itlb_addr(&s->cpu->env, addr); - break; - case MM_ITLB_DATA: - ret = cpu_sh4_read_mmaped_itlb_data(&s->cpu->env, addr); - break; - case MM_OCACHE_ADDR: - case MM_OCACHE_DATA: - /* do nothing */ - break; - case MM_UTLB_ADDR: - ret = cpu_sh4_read_mmaped_utlb_addr(&s->cpu->env, addr); - break; - case MM_UTLB_DATA: - ret = cpu_sh4_read_mmaped_utlb_data(&s->cpu->env, addr); - break; - default: - abort(); - } - - return ret; -} - -static void invalid_write(void *opaque, hwaddr addr, - uint64_t mem_value) -{ - abort(); -} - -static void sh7750_mmct_write(void *opaque, hwaddr addr, - uint64_t mem_value, unsigned size) -{ - SH7750State *s = opaque; - - if (size != 4) { - invalid_write(opaque, addr, mem_value); - } - - switch (MM_REGION_TYPE(addr)) { - case MM_ICACHE_ADDR: - case MM_ICACHE_DATA: - /* do nothing */ - break; - case MM_ITLB_ADDR: - cpu_sh4_write_mmaped_itlb_addr(&s->cpu->env, addr, mem_value); - break; - case MM_ITLB_DATA: - cpu_sh4_write_mmaped_itlb_data(&s->cpu->env, addr, mem_value); - abort(); - break; - case MM_OCACHE_ADDR: - case MM_OCACHE_DATA: - /* do nothing */ - break; - case MM_UTLB_ADDR: - cpu_sh4_write_mmaped_utlb_addr(&s->cpu->env, addr, mem_value); - break; - case MM_UTLB_DATA: - cpu_sh4_write_mmaped_utlb_data(&s->cpu->env, addr, mem_value); - break; - default: - abort(); - break; - } -} - -static const MemoryRegionOps sh7750_mmct_ops = { - .read = sh7750_mmct_read, - .write = sh7750_mmct_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) -{ - SH7750State *s; - - s = g_malloc0(sizeof(SH7750State)); - s->cpu = cpu; - s->periph_freq = 60000000; /* 60MHz */ - memory_region_init_io(&s->iomem, NULL, &sh7750_mem_ops, s, - "memory", 0x1fc01000); - - memory_region_init_alias(&s->iomem_1f0, NULL, "memory-1f0", - &s->iomem, 0x1f000000, 0x1000); - memory_region_add_subregion(sysmem, 0x1f000000, &s->iomem_1f0); - - memory_region_init_alias(&s->iomem_ff0, NULL, "memory-ff0", - &s->iomem, 0x1f000000, 0x1000); - memory_region_add_subregion(sysmem, 0xff000000, &s->iomem_ff0); - - memory_region_init_alias(&s->iomem_1f8, NULL, "memory-1f8", - &s->iomem, 0x1f800000, 0x1000); - memory_region_add_subregion(sysmem, 0x1f800000, &s->iomem_1f8); - - memory_region_init_alias(&s->iomem_ff8, NULL, "memory-ff8", - &s->iomem, 0x1f800000, 0x1000); - memory_region_add_subregion(sysmem, 0xff800000, &s->iomem_ff8); - - memory_region_init_alias(&s->iomem_1fc, NULL, "memory-1fc", - &s->iomem, 0x1fc00000, 0x1000); - memory_region_add_subregion(sysmem, 0x1fc00000, &s->iomem_1fc); - - memory_region_init_alias(&s->iomem_ffc, NULL, "memory-ffc", - &s->iomem, 0x1fc00000, 0x1000); - memory_region_add_subregion(sysmem, 0xffc00000, &s->iomem_ffc); - - memory_region_init_io(&s->mmct_iomem, NULL, &sh7750_mmct_ops, s, - "cache-and-tlb", 0x08000000); - memory_region_add_subregion(sysmem, 0xf0000000, &s->mmct_iomem); - - sh_intc_init(sysmem, &s->intc, NR_SOURCES, - _INTC_ARRAY(mask_registers), - _INTC_ARRAY(prio_registers)); - - sh_intc_register_sources(&s->intc, - _INTC_ARRAY(vectors), - _INTC_ARRAY(groups)); - - cpu->env.intc_handle = &s->intc; - - sh_serial_init(sysmem, 0x1fe00000, - 0, s->periph_freq, serial_hds[0], - s->intc.irqs[SCI1_ERI], - s->intc.irqs[SCI1_RXI], - s->intc.irqs[SCI1_TXI], - s->intc.irqs[SCI1_TEI], - NULL); - sh_serial_init(sysmem, 0x1fe80000, - SH_SERIAL_FEAT_SCIF, - s->periph_freq, serial_hds[1], - s->intc.irqs[SCIF_ERI], - s->intc.irqs[SCIF_RXI], - s->intc.irqs[SCIF_TXI], - NULL, - s->intc.irqs[SCIF_BRI]); - - tmu012_init(sysmem, 0x1fd80000, - TMU012_FEAT_TOCR | TMU012_FEAT_3CHAN | TMU012_FEAT_EXTCLK, - s->periph_freq, - s->intc.irqs[TMU0], - s->intc.irqs[TMU1], - s->intc.irqs[TMU2_TUNI], - s->intc.irqs[TMU2_TICPI]); - - if (cpu->env.id & (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7751)) { - sh_intc_register_sources(&s->intc, - _INTC_ARRAY(vectors_dma4), - _INTC_ARRAY(groups_dma4)); - } - - if (cpu->env.id & (SH_CPU_SH7750R | SH_CPU_SH7751R)) { - sh_intc_register_sources(&s->intc, - _INTC_ARRAY(vectors_dma8), - _INTC_ARRAY(groups_dma8)); - } - - if (cpu->env.id & (SH_CPU_SH7750R | SH_CPU_SH7751 | SH_CPU_SH7751R)) { - sh_intc_register_sources(&s->intc, - _INTC_ARRAY(vectors_tmu34), - NULL, 0); - tmu012_init(sysmem, 0x1e100000, 0, s->periph_freq, - s->intc.irqs[TMU3], - s->intc.irqs[TMU4], - NULL, NULL); - } - - if (cpu->env.id & (SH_CPU_SH7751_ALL)) { - sh_intc_register_sources(&s->intc, - _INTC_ARRAY(vectors_pci), - _INTC_ARRAY(groups_pci)); - } - - if (cpu->env.id & (SH_CPU_SH7750S | SH_CPU_SH7750R | SH_CPU_SH7751_ALL)) { - sh_intc_register_sources(&s->intc, - _INTC_ARRAY(vectors_irlm), - NULL, 0); - } - - sh_intc_register_sources(&s->intc, - _INTC_ARRAY(vectors_irl), - _INTC_ARRAY(groups_irl)); - return s; -} - -qemu_irq sh7750_irl(SH7750State *s) -{ - sh_intc_toggle_source(sh_intc_source(&s->intc, IRL), 1, 0); /* enable */ - return qemu_allocate_irq(sh_intc_set_irl, sh_intc_source(&s->intc, IRL), 0); -} diff --git a/qemu/hw/sh4/sh7750_regnames.c b/qemu/hw/sh4/sh7750_regnames.c deleted file mode 100644 index 34b4f99b8..000000000 --- a/qemu/hw/sh4/sh7750_regnames.c +++ /dev/null @@ -1,98 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sh4/sh.h" -#include "sh7750_regs.h" -#include "sh7750_regnames.h" - -#define REGNAME(r) {r, #r}, - -typedef struct { - uint32_t regaddr; - const char *regname; -} regname_t; - -static regname_t regnames[] = { - REGNAME(SH7750_PTEH_A7) - REGNAME(SH7750_PTEL_A7) - REGNAME(SH7750_PTEA_A7) - REGNAME(SH7750_TTB_A7) - REGNAME(SH7750_TEA_A7) - REGNAME(SH7750_MMUCR_A7) - REGNAME(SH7750_CCR_A7) - REGNAME(SH7750_QACR0_A7) - REGNAME(SH7750_QACR1_A7) - REGNAME(SH7750_TRA_A7) - REGNAME(SH7750_EXPEVT_A7) - REGNAME(SH7750_INTEVT_A7) - REGNAME(SH7750_STBCR_A7) - REGNAME(SH7750_STBCR2_A7) - REGNAME(SH7750_FRQCR_A7) - REGNAME(SH7750_WTCNT_A7) - REGNAME(SH7750_WTCSR_A7) - REGNAME(SH7750_R64CNT_A7) - REGNAME(SH7750_RSECCNT_A7) - REGNAME(SH7750_RMINCNT_A7) - REGNAME(SH7750_RHRCNT_A7) - REGNAME(SH7750_RWKCNT_A7) - REGNAME(SH7750_RDAYCNT_A7) - REGNAME(SH7750_RMONCNT_A7) - REGNAME(SH7750_RYRCNT_A7) - REGNAME(SH7750_RSECAR_A7) - REGNAME(SH7750_RMINAR_A7) - REGNAME(SH7750_RHRAR_A7) - REGNAME(SH7750_RWKAR_A7) - REGNAME(SH7750_RDAYAR_A7) - REGNAME(SH7750_RMONAR_A7) - REGNAME(SH7750_RCR1_A7) - REGNAME(SH7750_RCR2_A7) - REGNAME(SH7750_BCR1_A7) - REGNAME(SH7750_BCR2_A7) - REGNAME(SH7750_WCR1_A7) - REGNAME(SH7750_WCR2_A7) - REGNAME(SH7750_WCR3_A7) - REGNAME(SH7750_MCR_A7) - REGNAME(SH7750_PCR_A7) - REGNAME(SH7750_RTCSR_A7) - REGNAME(SH7750_RTCNT_A7) - REGNAME(SH7750_RTCOR_A7) - REGNAME(SH7750_RFCR_A7) - REGNAME(SH7750_SAR0_A7) - REGNAME(SH7750_SAR1_A7) - REGNAME(SH7750_SAR2_A7) - REGNAME(SH7750_SAR3_A7) - REGNAME(SH7750_DAR0_A7) - REGNAME(SH7750_DAR1_A7) - REGNAME(SH7750_DAR2_A7) - REGNAME(SH7750_DAR3_A7) - REGNAME(SH7750_DMATCR0_A7) - REGNAME(SH7750_DMATCR1_A7) - REGNAME(SH7750_DMATCR2_A7) - REGNAME(SH7750_DMATCR3_A7) - REGNAME(SH7750_CHCR0_A7) - REGNAME(SH7750_CHCR1_A7) - REGNAME(SH7750_CHCR2_A7) - REGNAME(SH7750_CHCR3_A7) - REGNAME(SH7750_DMAOR_A7) - REGNAME(SH7750_PCTRA_A7) - REGNAME(SH7750_PDTRA_A7) - REGNAME(SH7750_PCTRB_A7) - REGNAME(SH7750_PDTRB_A7) - REGNAME(SH7750_GPIOIC_A7) - REGNAME(SH7750_ICR_A7) - REGNAME(SH7750_BCR3_A7) - REGNAME(SH7750_BCR4_A7) - REGNAME(SH7750_SDMR2_A7) - REGNAME(SH7750_SDMR3_A7) {(uint32_t) - 1, NULL} -}; - -const char *regname(uint32_t addr) -{ - unsigned int i; - - for (i = 0; regnames[i].regaddr != (uint32_t) - 1; i++) { - if (regnames[i].regaddr == addr) - return regnames[i].regname; - } - - return ""; -} diff --git a/qemu/hw/sh4/sh7750_regnames.h b/qemu/hw/sh4/sh7750_regnames.h deleted file mode 100644 index 7463709b4..000000000 --- a/qemu/hw/sh4/sh7750_regnames.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _SH7750_REGNAMES_H -#define _SH7750_REGNAMES_H - -const char *regname(uint32_t addr); - -#endif /* _SH7750_REGNAMES_H */ diff --git a/qemu/hw/sh4/sh7750_regs.h b/qemu/hw/sh4/sh7750_regs.h deleted file mode 100644 index 534aa4840..000000000 --- a/qemu/hw/sh4/sh7750_regs.h +++ /dev/null @@ -1,1277 +0,0 @@ -/* - * SH-7750 memory-mapped registers - * This file based on information provided in the following document: - * "Hitachi SuperH (tm) RISC engine. SH7750 Series (SH7750, SH7750S) - * Hardware Manual" - * Document Number ADE-602-124C, Rev. 4.0, 4/21/00, Hitachi Ltd. - * - * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia - * Author: Alexandra Kossovsky - * Victor V. Vengerov - * - * The license and distribution terms for this file may be - * found in the file LICENSE in this distribution or at - * http://www.rtems.com/license/LICENSE. - * - * @(#) sh7750_regs.h,v 1.2.4.1 2003/09/04 18:46:00 joel Exp - */ - -#ifndef __SH7750_REGS_H__ -#define __SH7750_REGS_H__ - -/* - * All register has 2 addresses: in 0xff000000 - 0xffffffff (P4 address) and - * in 0x1f000000 - 0x1fffffff (area 7 address) - */ -#define SH7750_P4_BASE 0xff000000 /* Accessible only in - privileged mode */ -#define SH7750_A7_BASE 0x1f000000 /* Accessible only using TLB */ - -#define SH7750_P4_REG32(ofs) (SH7750_P4_BASE + (ofs)) -#define SH7750_A7_REG32(ofs) (SH7750_A7_BASE + (ofs)) - -/* - * MMU Registers - */ - -/* Page Table Entry High register - PTEH */ -#define SH7750_PTEH_REGOFS 0x000000 /* offset */ -#define SH7750_PTEH SH7750_P4_REG32(SH7750_PTEH_REGOFS) -#define SH7750_PTEH_A7 SH7750_A7_REG32(SH7750_PTEH_REGOFS) -#define SH7750_PTEH_VPN 0xfffffd00 /* Virtual page number */ -#define SH7750_PTEH_VPN_S 10 -#define SH7750_PTEH_ASID 0x000000ff /* Address space identifier */ -#define SH7750_PTEH_ASID_S 0 - -/* Page Table Entry Low register - PTEL */ -#define SH7750_PTEL_REGOFS 0x000004 /* offset */ -#define SH7750_PTEL SH7750_P4_REG32(SH7750_PTEL_REGOFS) -#define SH7750_PTEL_A7 SH7750_A7_REG32(SH7750_PTEL_REGOFS) -#define SH7750_PTEL_PPN 0x1ffffc00 /* Physical page number */ -#define SH7750_PTEL_PPN_S 10 -#define SH7750_PTEL_V 0x00000100 /* Validity (0-entry is invalid) */ -#define SH7750_PTEL_SZ1 0x00000080 /* Page size bit 1 */ -#define SH7750_PTEL_SZ0 0x00000010 /* Page size bit 0 */ -#define SH7750_PTEL_SZ_1KB 0x00000000 /* 1-kbyte page */ -#define SH7750_PTEL_SZ_4KB 0x00000010 /* 4-kbyte page */ -#define SH7750_PTEL_SZ_64KB 0x00000080 /* 64-kbyte page */ -#define SH7750_PTEL_SZ_1MB 0x00000090 /* 1-Mbyte page */ -#define SH7750_PTEL_PR 0x00000060 /* Protection Key Data */ -#define SH7750_PTEL_PR_ROPO 0x00000000 /* read-only in priv mode */ -#define SH7750_PTEL_PR_RWPO 0x00000020 /* read-write in priv mode */ -#define SH7750_PTEL_PR_ROPU 0x00000040 /* read-only in priv or user mode */ -#define SH7750_PTEL_PR_RWPU 0x00000060 /* read-write in priv or user mode */ -#define SH7750_PTEL_C 0x00000008 /* Cacheability - (0 - page not cacheable) */ -#define SH7750_PTEL_D 0x00000004 /* Dirty bit (1 - write has been - performed to a page) */ -#define SH7750_PTEL_SH 0x00000002 /* Share Status bit (1 - page are - shared by processes) */ -#define SH7750_PTEL_WT 0x00000001 /* Write-through bit, specifies the - cache write mode: - 0 - Copy-back mode - 1 - Write-through mode */ - -/* Page Table Entry Assistance register - PTEA */ -#define SH7750_PTEA_REGOFS 0x000034 /* offset */ -#define SH7750_PTEA SH7750_P4_REG32(SH7750_PTEA_REGOFS) -#define SH7750_PTEA_A7 SH7750_A7_REG32(SH7750_PTEA_REGOFS) -#define SH7750_PTEA_TC 0x00000008 /* Timing Control bit - 0 - use area 5 wait states - 1 - use area 6 wait states */ -#define SH7750_PTEA_SA 0x00000007 /* Space Attribute bits: */ -#define SH7750_PTEA_SA_UNDEF 0x00000000 /* 0 - undefined */ -#define SH7750_PTEA_SA_IOVAR 0x00000001 /* 1 - variable-size I/O space */ -#define SH7750_PTEA_SA_IO8 0x00000002 /* 2 - 8-bit I/O space */ -#define SH7750_PTEA_SA_IO16 0x00000003 /* 3 - 16-bit I/O space */ -#define SH7750_PTEA_SA_CMEM8 0x00000004 /* 4 - 8-bit common memory space */ -#define SH7750_PTEA_SA_CMEM16 0x00000005 /* 5 - 16-bit common memory space */ -#define SH7750_PTEA_SA_AMEM8 0x00000006 /* 6 - 8-bit attr memory space */ -#define SH7750_PTEA_SA_AMEM16 0x00000007 /* 7 - 16-bit attr memory space */ - - -/* Translation table base register */ -#define SH7750_TTB_REGOFS 0x000008 /* offset */ -#define SH7750_TTB SH7750_P4_REG32(SH7750_TTB_REGOFS) -#define SH7750_TTB_A7 SH7750_A7_REG32(SH7750_TTB_REGOFS) - -/* TLB exeption address register - TEA */ -#define SH7750_TEA_REGOFS 0x00000c /* offset */ -#define SH7750_TEA SH7750_P4_REG32(SH7750_TEA_REGOFS) -#define SH7750_TEA_A7 SH7750_A7_REG32(SH7750_TEA_REGOFS) - -/* MMU control register - MMUCR */ -#define SH7750_MMUCR_REGOFS 0x000010 /* offset */ -#define SH7750_MMUCR SH7750_P4_REG32(SH7750_MMUCR_REGOFS) -#define SH7750_MMUCR_A7 SH7750_A7_REG32(SH7750_MMUCR_REGOFS) -#define SH7750_MMUCR_AT 0x00000001 /* Address translation bit */ -#define SH7750_MMUCR_TI 0x00000004 /* TLB invalidate */ -#define SH7750_MMUCR_SV 0x00000100 /* Single Virtual Mode bit */ -#define SH7750_MMUCR_SQMD 0x00000200 /* Store Queue Mode bit */ -#define SH7750_MMUCR_URC 0x0000FC00 /* UTLB Replace Counter */ -#define SH7750_MMUCR_URC_S 10 -#define SH7750_MMUCR_URB 0x00FC0000 /* UTLB Replace Boundary */ -#define SH7750_MMUCR_URB_S 18 -#define SH7750_MMUCR_LRUI 0xFC000000 /* Least Recently Used ITLB */ -#define SH7750_MMUCR_LRUI_S 26 - - - - -/* - * Cache registers - * IC -- instructions cache - * OC -- operand cache - */ - -/* Cache Control Register - CCR */ -#define SH7750_CCR_REGOFS 0x00001c /* offset */ -#define SH7750_CCR SH7750_P4_REG32(SH7750_CCR_REGOFS) -#define SH7750_CCR_A7 SH7750_A7_REG32(SH7750_CCR_REGOFS) - -#define SH7750_CCR_IIX 0x00008000 /* IC index enable bit */ -#define SH7750_CCR_ICI 0x00000800 /* IC invalidation bit: - set it to clear IC */ -#define SH7750_CCR_ICE 0x00000100 /* IC enable bit */ -#define SH7750_CCR_OIX 0x00000080 /* OC index enable bit */ -#define SH7750_CCR_ORA 0x00000020 /* OC RAM enable bit - if you set OCE = 0, - you should set ORA = 0 */ -#define SH7750_CCR_OCI 0x00000008 /* OC invalidation bit */ -#define SH7750_CCR_CB 0x00000004 /* Copy-back bit for P1 area */ -#define SH7750_CCR_WT 0x00000002 /* Write-through bit for P0,U0,P3 area */ -#define SH7750_CCR_OCE 0x00000001 /* OC enable bit */ - -/* Queue address control register 0 - QACR0 */ -#define SH7750_QACR0_REGOFS 0x000038 /* offset */ -#define SH7750_QACR0 SH7750_P4_REG32(SH7750_QACR0_REGOFS) -#define SH7750_QACR0_A7 SH7750_A7_REG32(SH7750_QACR0_REGOFS) - -/* Queue address control register 1 - QACR1 */ -#define SH7750_QACR1_REGOFS 0x00003c /* offset */ -#define SH7750_QACR1 SH7750_P4_REG32(SH7750_QACR1_REGOFS) -#define SH7750_QACR1_A7 SH7750_A7_REG32(SH7750_QACR1_REGOFS) - - -/* - * Exeption-related registers - */ - -/* Immediate data for TRAPA instruction - TRA */ -#define SH7750_TRA_REGOFS 0x000020 /* offset */ -#define SH7750_TRA SH7750_P4_REG32(SH7750_TRA_REGOFS) -#define SH7750_TRA_A7 SH7750_A7_REG32(SH7750_TRA_REGOFS) - -#define SH7750_TRA_IMM 0x000003fd /* Immediate data operand */ -#define SH7750_TRA_IMM_S 2 - -/* Exeption event register - EXPEVT */ -#define SH7750_EXPEVT_REGOFS 0x000024 -#define SH7750_EXPEVT SH7750_P4_REG32(SH7750_EXPEVT_REGOFS) -#define SH7750_EXPEVT_A7 SH7750_A7_REG32(SH7750_EXPEVT_REGOFS) - -#define SH7750_EXPEVT_EX 0x00000fff /* Exeption code */ -#define SH7750_EXPEVT_EX_S 0 - -/* Interrupt event register */ -#define SH7750_INTEVT_REGOFS 0x000028 -#define SH7750_INTEVT SH7750_P4_REG32(SH7750_INTEVT_REGOFS) -#define SH7750_INTEVT_A7 SH7750_A7_REG32(SH7750_INTEVT_REGOFS) -#define SH7750_INTEVT_EX 0x00000fff /* Exeption code */ -#define SH7750_INTEVT_EX_S 0 - -/* - * Exception/interrupt codes - */ -#define SH7750_EVT_TO_NUM(evt) ((evt) >> 5) - -/* Reset exception category */ -#define SH7750_EVT_POWER_ON_RST 0x000 /* Power-on reset */ -#define SH7750_EVT_MANUAL_RST 0x020 /* Manual reset */ -#define SH7750_EVT_TLB_MULT_HIT 0x140 /* TLB multiple-hit exception */ - -/* General exception category */ -#define SH7750_EVT_USER_BREAK 0x1E0 /* User break */ -#define SH7750_EVT_IADDR_ERR 0x0E0 /* Instruction address error */ -#define SH7750_EVT_TLB_READ_MISS 0x040 /* ITLB miss exception / - DTLB miss exception (read) */ -#define SH7750_EVT_TLB_READ_PROTV 0x0A0 /* ITLB protection violation / - DTLB protection violation (read) */ -#define SH7750_EVT_ILLEGAL_INSTR 0x180 /* General Illegal Instruction - exception */ -#define SH7750_EVT_SLOT_ILLEGAL_INSTR 0x1A0 /* Slot Illegal Instruction - exception */ -#define SH7750_EVT_FPU_DISABLE 0x800 /* General FPU disable exception */ -#define SH7750_EVT_SLOT_FPU_DISABLE 0x820 /* Slot FPU disable exception */ -#define SH7750_EVT_DATA_READ_ERR 0x0E0 /* Data address error (read) */ -#define SH7750_EVT_DATA_WRITE_ERR 0x100 /* Data address error (write) */ -#define SH7750_EVT_DTLB_WRITE_MISS 0x060 /* DTLB miss exception (write) */ -#define SH7750_EVT_DTLB_WRITE_PROTV 0x0C0 /* DTLB protection violation - exception (write) */ -#define SH7750_EVT_FPU_EXCEPTION 0x120 /* FPU exception */ -#define SH7750_EVT_INITIAL_PGWRITE 0x080 /* Initial Page Write exception */ -#define SH7750_EVT_TRAPA 0x160 /* Unconditional trap (TRAPA) */ - -/* Interrupt exception category */ -#define SH7750_EVT_NMI 0x1C0 /* Non-maskable interrupt */ -#define SH7750_EVT_IRQ0 0x200 /* External Interrupt 0 */ -#define SH7750_EVT_IRQ1 0x220 /* External Interrupt 1 */ -#define SH7750_EVT_IRQ2 0x240 /* External Interrupt 2 */ -#define SH7750_EVT_IRQ3 0x260 /* External Interrupt 3 */ -#define SH7750_EVT_IRQ4 0x280 /* External Interrupt 4 */ -#define SH7750_EVT_IRQ5 0x2A0 /* External Interrupt 5 */ -#define SH7750_EVT_IRQ6 0x2C0 /* External Interrupt 6 */ -#define SH7750_EVT_IRQ7 0x2E0 /* External Interrupt 7 */ -#define SH7750_EVT_IRQ8 0x300 /* External Interrupt 8 */ -#define SH7750_EVT_IRQ9 0x320 /* External Interrupt 9 */ -#define SH7750_EVT_IRQA 0x340 /* External Interrupt A */ -#define SH7750_EVT_IRQB 0x360 /* External Interrupt B */ -#define SH7750_EVT_IRQC 0x380 /* External Interrupt C */ -#define SH7750_EVT_IRQD 0x3A0 /* External Interrupt D */ -#define SH7750_EVT_IRQE 0x3C0 /* External Interrupt E */ - -/* Peripheral Module Interrupts - Timer Unit (TMU) */ -#define SH7750_EVT_TUNI0 0x400 /* TMU Underflow Interrupt 0 */ -#define SH7750_EVT_TUNI1 0x420 /* TMU Underflow Interrupt 1 */ -#define SH7750_EVT_TUNI2 0x440 /* TMU Underflow Interrupt 2 */ -#define SH7750_EVT_TICPI2 0x460 /* TMU Input Capture Interrupt 2 */ - -/* Peripheral Module Interrupts - Real-Time Clock (RTC) */ -#define SH7750_EVT_RTC_ATI 0x480 /* Alarm Interrupt Request */ -#define SH7750_EVT_RTC_PRI 0x4A0 /* Periodic Interrupt Request */ -#define SH7750_EVT_RTC_CUI 0x4C0 /* Carry Interrupt Request */ - -/* Peripheral Module Interrupts - Serial Communication Interface (SCI) */ -#define SH7750_EVT_SCI_ERI 0x4E0 /* Receive Error */ -#define SH7750_EVT_SCI_RXI 0x500 /* Receive Data Register Full */ -#define SH7750_EVT_SCI_TXI 0x520 /* Transmit Data Register Empty */ -#define SH7750_EVT_SCI_TEI 0x540 /* Transmit End */ - -/* Peripheral Module Interrupts - Watchdog Timer (WDT) */ -#define SH7750_EVT_WDT_ITI 0x560 /* Interval Timer Interrupt - (used when WDT operates in - interval timer mode) */ - -/* Peripheral Module Interrupts - Memory Refresh Unit (REF) */ -#define SH7750_EVT_REF_RCMI 0x580 /* Compare-match Interrupt */ -#define SH7750_EVT_REF_ROVI 0x5A0 /* Refresh Counter Overflow - interrupt */ - -/* Peripheral Module Interrupts - Hitachi User Debug Interface (H-UDI) */ -#define SH7750_EVT_HUDI 0x600 /* UDI interrupt */ - -/* Peripheral Module Interrupts - General-Purpose I/O (GPIO) */ -#define SH7750_EVT_GPIO 0x620 /* GPIO Interrupt */ - -/* Peripheral Module Interrupts - DMA Controller (DMAC) */ -#define SH7750_EVT_DMAC_DMTE0 0x640 /* DMAC 0 Transfer End Interrupt */ -#define SH7750_EVT_DMAC_DMTE1 0x660 /* DMAC 1 Transfer End Interrupt */ -#define SH7750_EVT_DMAC_DMTE2 0x680 /* DMAC 2 Transfer End Interrupt */ -#define SH7750_EVT_DMAC_DMTE3 0x6A0 /* DMAC 3 Transfer End Interrupt */ -#define SH7750_EVT_DMAC_DMAE 0x6C0 /* DMAC Address Error Interrupt */ - -/* Peripheral Module Interrupts - Serial Communication Interface with FIFO */ -/* (SCIF) */ -#define SH7750_EVT_SCIF_ERI 0x700 /* Receive Error */ -#define SH7750_EVT_SCIF_RXI 0x720 /* Receive FIFO Data Full or - Receive Data ready interrupt */ -#define SH7750_EVT_SCIF_BRI 0x740 /* Break or overrun error */ -#define SH7750_EVT_SCIF_TXI 0x760 /* Transmit FIFO Data Empty */ - -/* - * Power Management - */ -#define SH7750_STBCR_REGOFS 0xC00004 /* offset */ -#define SH7750_STBCR SH7750_P4_REG32(SH7750_STBCR_REGOFS) -#define SH7750_STBCR_A7 SH7750_A7_REG32(SH7750_STBCR_REGOFS) - -#define SH7750_STBCR_STBY 0x80 /* Specifies a transition to standby mode: - 0 - Transition to SLEEP mode on SLEEP - 1 - Transition to STANDBY mode on SLEEP */ -#define SH7750_STBCR_PHZ 0x40 /* State of peripheral module pins in - standby mode: - 0 - normal state - 1 - high-impendance state */ - -#define SH7750_STBCR_PPU 0x20 /* Peripheral module pins pull-up controls */ -#define SH7750_STBCR_MSTP4 0x10 /* Stopping the clock supply to DMAC */ -#define SH7750_STBCR_DMAC_STP SH7750_STBCR_MSTP4 -#define SH7750_STBCR_MSTP3 0x08 /* Stopping the clock supply to SCIF */ -#define SH7750_STBCR_SCIF_STP SH7750_STBCR_MSTP3 -#define SH7750_STBCR_MSTP2 0x04 /* Stopping the clock supply to TMU */ -#define SH7750_STBCR_TMU_STP SH7750_STBCR_MSTP2 -#define SH7750_STBCR_MSTP1 0x02 /* Stopping the clock supply to RTC */ -#define SH7750_STBCR_RTC_STP SH7750_STBCR_MSTP1 -#define SH7750_STBCR_MSPT0 0x01 /* Stopping the clock supply to SCI */ -#define SH7750_STBCR_SCI_STP SH7750_STBCR_MSTP0 - -#define SH7750_STBCR_STBY 0x80 - - -#define SH7750_STBCR2_REGOFS 0xC00010 /* offset */ -#define SH7750_STBCR2 SH7750_P4_REG32(SH7750_STBCR2_REGOFS) -#define SH7750_STBCR2_A7 SH7750_A7_REG32(SH7750_STBCR2_REGOFS) - -#define SH7750_STBCR2_DSLP 0x80 /* Specifies transition to deep sleep mode: - 0 - transition to sleep or standby mode - as it is specified in STBY bit - 1 - transition to deep sleep mode on - execution of SLEEP instruction */ -#define SH7750_STBCR2_MSTP6 0x02 /* Stopping the clock supply to Store Queue - in the cache controller */ -#define SH7750_STBCR2_SQ_STP SH7750_STBCR2_MSTP6 -#define SH7750_STBCR2_MSTP5 0x01 /* Stopping the clock supply to the User - Break Controller (UBC) */ -#define SH7750_STBCR2_UBC_STP SH7750_STBCR2_MSTP5 - -/* - * Clock Pulse Generator (CPG) - */ -#define SH7750_FRQCR_REGOFS 0xC00000 /* offset */ -#define SH7750_FRQCR SH7750_P4_REG32(SH7750_FRQCR_REGOFS) -#define SH7750_FRQCR_A7 SH7750_A7_REG32(SH7750_FRQCR_REGOFS) - -#define SH7750_FRQCR_CKOEN 0x0800 /* Clock Output Enable - 0 - CKIO pin goes to HiZ/pullup - 1 - Clock is output from CKIO */ -#define SH7750_FRQCR_PLL1EN 0x0400 /* PLL circuit 1 enable */ -#define SH7750_FRQCR_PLL2EN 0x0200 /* PLL circuit 2 enable */ - -#define SH7750_FRQCR_IFC 0x01C0 /* CPU clock frequency division ratio: */ -#define SH7750_FRQCR_IFCDIV1 0x0000 /* 0 - * 1 */ -#define SH7750_FRQCR_IFCDIV2 0x0040 /* 1 - * 1/2 */ -#define SH7750_FRQCR_IFCDIV3 0x0080 /* 2 - * 1/3 */ -#define SH7750_FRQCR_IFCDIV4 0x00C0 /* 3 - * 1/4 */ -#define SH7750_FRQCR_IFCDIV6 0x0100 /* 4 - * 1/6 */ -#define SH7750_FRQCR_IFCDIV8 0x0140 /* 5 - * 1/8 */ - -#define SH7750_FRQCR_BFC 0x0038 /* Bus clock frequency division ratio: */ -#define SH7750_FRQCR_BFCDIV1 0x0000 /* 0 - * 1 */ -#define SH7750_FRQCR_BFCDIV2 0x0008 /* 1 - * 1/2 */ -#define SH7750_FRQCR_BFCDIV3 0x0010 /* 2 - * 1/3 */ -#define SH7750_FRQCR_BFCDIV4 0x0018 /* 3 - * 1/4 */ -#define SH7750_FRQCR_BFCDIV6 0x0020 /* 4 - * 1/6 */ -#define SH7750_FRQCR_BFCDIV8 0x0028 /* 5 - * 1/8 */ - -#define SH7750_FRQCR_PFC 0x0007 /* Peripheral module clock frequency - division ratio: */ -#define SH7750_FRQCR_PFCDIV2 0x0000 /* 0 - * 1/2 */ -#define SH7750_FRQCR_PFCDIV3 0x0001 /* 1 - * 1/3 */ -#define SH7750_FRQCR_PFCDIV4 0x0002 /* 2 - * 1/4 */ -#define SH7750_FRQCR_PFCDIV6 0x0003 /* 3 - * 1/6 */ -#define SH7750_FRQCR_PFCDIV8 0x0004 /* 4 - * 1/8 */ - -/* - * Watchdog Timer (WDT) - */ - -/* Watchdog Timer Counter register - WTCNT */ -#define SH7750_WTCNT_REGOFS 0xC00008 /* offset */ -#define SH7750_WTCNT SH7750_P4_REG32(SH7750_WTCNT_REGOFS) -#define SH7750_WTCNT_A7 SH7750_A7_REG32(SH7750_WTCNT_REGOFS) -#define SH7750_WTCNT_KEY 0x5A00 /* When WTCNT byte register written, - you have to set the upper byte to - 0x5A */ - -/* Watchdog Timer Control/Status register - WTCSR */ -#define SH7750_WTCSR_REGOFS 0xC0000C /* offset */ -#define SH7750_WTCSR SH7750_P4_REG32(SH7750_WTCSR_REGOFS) -#define SH7750_WTCSR_A7 SH7750_A7_REG32(SH7750_WTCSR_REGOFS) -#define SH7750_WTCSR_KEY 0xA500 /* When WTCSR byte register written, - you have to set the upper byte to - 0xA5 */ -#define SH7750_WTCSR_TME 0x80 /* Timer enable (1-upcount start) */ -#define SH7750_WTCSR_MODE 0x40 /* Timer Mode Select: */ -#define SH7750_WTCSR_MODE_WT 0x40 /* Watchdog Timer Mode */ -#define SH7750_WTCSR_MODE_IT 0x00 /* Interval Timer Mode */ -#define SH7750_WTCSR_RSTS 0x20 /* Reset Select: */ -#define SH7750_WTCSR_RST_MAN 0x20 /* Manual Reset */ -#define SH7750_WTCSR_RST_PWR 0x00 /* Power-on Reset */ -#define SH7750_WTCSR_WOVF 0x10 /* Watchdog Timer Overflow Flag */ -#define SH7750_WTCSR_IOVF 0x08 /* Interval Timer Overflow Flag */ -#define SH7750_WTCSR_CKS 0x07 /* Clock Select: */ -#define SH7750_WTCSR_CKS_DIV32 0x00 /* 1/32 of frequency divider 2 input */ -#define SH7750_WTCSR_CKS_DIV64 0x01 /* 1/64 */ -#define SH7750_WTCSR_CKS_DIV128 0x02 /* 1/128 */ -#define SH7750_WTCSR_CKS_DIV256 0x03 /* 1/256 */ -#define SH7750_WTCSR_CKS_DIV512 0x04 /* 1/512 */ -#define SH7750_WTCSR_CKS_DIV1024 0x05 /* 1/1024 */ -#define SH7750_WTCSR_CKS_DIV2048 0x06 /* 1/2048 */ -#define SH7750_WTCSR_CKS_DIV4096 0x07 /* 1/4096 */ - -/* - * Real-Time Clock (RTC) - */ -/* 64-Hz Counter Register (byte, read-only) - R64CNT */ -#define SH7750_R64CNT_REGOFS 0xC80000 /* offset */ -#define SH7750_R64CNT SH7750_P4_REG32(SH7750_R64CNT_REGOFS) -#define SH7750_R64CNT_A7 SH7750_A7_REG32(SH7750_R64CNT_REGOFS) - -/* Second Counter Register (byte, BCD-coded) - RSECCNT */ -#define SH7750_RSECCNT_REGOFS 0xC80004 /* offset */ -#define SH7750_RSECCNT SH7750_P4_REG32(SH7750_RSECCNT_REGOFS) -#define SH7750_RSECCNT_A7 SH7750_A7_REG32(SH7750_RSECCNT_REGOFS) - -/* Minute Counter Register (byte, BCD-coded) - RMINCNT */ -#define SH7750_RMINCNT_REGOFS 0xC80008 /* offset */ -#define SH7750_RMINCNT SH7750_P4_REG32(SH7750_RMINCNT_REGOFS) -#define SH7750_RMINCNT_A7 SH7750_A7_REG32(SH7750_RMINCNT_REGOFS) - -/* Hour Counter Register (byte, BCD-coded) - RHRCNT */ -#define SH7750_RHRCNT_REGOFS 0xC8000C /* offset */ -#define SH7750_RHRCNT SH7750_P4_REG32(SH7750_RHRCNT_REGOFS) -#define SH7750_RHRCNT_A7 SH7750_A7_REG32(SH7750_RHRCNT_REGOFS) - -/* Day-of-Week Counter Register (byte) - RWKCNT */ -#define SH7750_RWKCNT_REGOFS 0xC80010 /* offset */ -#define SH7750_RWKCNT SH7750_P4_REG32(SH7750_RWKCNT_REGOFS) -#define SH7750_RWKCNT_A7 SH7750_A7_REG32(SH7750_RWKCNT_REGOFS) - -#define SH7750_RWKCNT_SUN 0 /* Sunday */ -#define SH7750_RWKCNT_MON 1 /* Monday */ -#define SH7750_RWKCNT_TUE 2 /* Tuesday */ -#define SH7750_RWKCNT_WED 3 /* Wednesday */ -#define SH7750_RWKCNT_THU 4 /* Thursday */ -#define SH7750_RWKCNT_FRI 5 /* Friday */ -#define SH7750_RWKCNT_SAT 6 /* Saturday */ - -/* Day Counter Register (byte, BCD-coded) - RDAYCNT */ -#define SH7750_RDAYCNT_REGOFS 0xC80014 /* offset */ -#define SH7750_RDAYCNT SH7750_P4_REG32(SH7750_RDAYCNT_REGOFS) -#define SH7750_RDAYCNT_A7 SH7750_A7_REG32(SH7750_RDAYCNT_REGOFS) - -/* Month Counter Register (byte, BCD-coded) - RMONCNT */ -#define SH7750_RMONCNT_REGOFS 0xC80018 /* offset */ -#define SH7750_RMONCNT SH7750_P4_REG32(SH7750_RMONCNT_REGOFS) -#define SH7750_RMONCNT_A7 SH7750_A7_REG32(SH7750_RMONCNT_REGOFS) - -/* Year Counter Register (half, BCD-coded) - RYRCNT */ -#define SH7750_RYRCNT_REGOFS 0xC8001C /* offset */ -#define SH7750_RYRCNT SH7750_P4_REG32(SH7750_RYRCNT_REGOFS) -#define SH7750_RYRCNT_A7 SH7750_A7_REG32(SH7750_RYRCNT_REGOFS) - -/* Second Alarm Register (byte, BCD-coded) - RSECAR */ -#define SH7750_RSECAR_REGOFS 0xC80020 /* offset */ -#define SH7750_RSECAR SH7750_P4_REG32(SH7750_RSECAR_REGOFS) -#define SH7750_RSECAR_A7 SH7750_A7_REG32(SH7750_RSECAR_REGOFS) -#define SH7750_RSECAR_ENB 0x80 /* Second Alarm Enable */ - -/* Minute Alarm Register (byte, BCD-coded) - RMINAR */ -#define SH7750_RMINAR_REGOFS 0xC80024 /* offset */ -#define SH7750_RMINAR SH7750_P4_REG32(SH7750_RMINAR_REGOFS) -#define SH7750_RMINAR_A7 SH7750_A7_REG32(SH7750_RMINAR_REGOFS) -#define SH7750_RMINAR_ENB 0x80 /* Minute Alarm Enable */ - -/* Hour Alarm Register (byte, BCD-coded) - RHRAR */ -#define SH7750_RHRAR_REGOFS 0xC80028 /* offset */ -#define SH7750_RHRAR SH7750_P4_REG32(SH7750_RHRAR_REGOFS) -#define SH7750_RHRAR_A7 SH7750_A7_REG32(SH7750_RHRAR_REGOFS) -#define SH7750_RHRAR_ENB 0x80 /* Hour Alarm Enable */ - -/* Day-of-Week Alarm Register (byte) - RWKAR */ -#define SH7750_RWKAR_REGOFS 0xC8002C /* offset */ -#define SH7750_RWKAR SH7750_P4_REG32(SH7750_RWKAR_REGOFS) -#define SH7750_RWKAR_A7 SH7750_A7_REG32(SH7750_RWKAR_REGOFS) -#define SH7750_RWKAR_ENB 0x80 /* Day-of-week Alarm Enable */ - -#define SH7750_RWKAR_SUN 0 /* Sunday */ -#define SH7750_RWKAR_MON 1 /* Monday */ -#define SH7750_RWKAR_TUE 2 /* Tuesday */ -#define SH7750_RWKAR_WED 3 /* Wednesday */ -#define SH7750_RWKAR_THU 4 /* Thursday */ -#define SH7750_RWKAR_FRI 5 /* Friday */ -#define SH7750_RWKAR_SAT 6 /* Saturday */ - -/* Day Alarm Register (byte, BCD-coded) - RDAYAR */ -#define SH7750_RDAYAR_REGOFS 0xC80030 /* offset */ -#define SH7750_RDAYAR SH7750_P4_REG32(SH7750_RDAYAR_REGOFS) -#define SH7750_RDAYAR_A7 SH7750_A7_REG32(SH7750_RDAYAR_REGOFS) -#define SH7750_RDAYAR_ENB 0x80 /* Day Alarm Enable */ - -/* Month Counter Register (byte, BCD-coded) - RMONAR */ -#define SH7750_RMONAR_REGOFS 0xC80034 /* offset */ -#define SH7750_RMONAR SH7750_P4_REG32(SH7750_RMONAR_REGOFS) -#define SH7750_RMONAR_A7 SH7750_A7_REG32(SH7750_RMONAR_REGOFS) -#define SH7750_RMONAR_ENB 0x80 /* Month Alarm Enable */ - -/* RTC Control Register 1 (byte) - RCR1 */ -#define SH7750_RCR1_REGOFS 0xC80038 /* offset */ -#define SH7750_RCR1 SH7750_P4_REG32(SH7750_RCR1_REGOFS) -#define SH7750_RCR1_A7 SH7750_A7_REG32(SH7750_RCR1_REGOFS) -#define SH7750_RCR1_CF 0x80 /* Carry Flag */ -#define SH7750_RCR1_CIE 0x10 /* Carry Interrupt Enable */ -#define SH7750_RCR1_AIE 0x08 /* Alarm Interrupt Enable */ -#define SH7750_RCR1_AF 0x01 /* Alarm Flag */ - -/* RTC Control Register 2 (byte) - RCR2 */ -#define SH7750_RCR2_REGOFS 0xC8003C /* offset */ -#define SH7750_RCR2 SH7750_P4_REG32(SH7750_RCR2_REGOFS) -#define SH7750_RCR2_A7 SH7750_A7_REG32(SH7750_RCR2_REGOFS) -#define SH7750_RCR2_PEF 0x80 /* Periodic Interrupt Flag */ -#define SH7750_RCR2_PES 0x70 /* Periodic Interrupt Enable: */ -#define SH7750_RCR2_PES_DIS 0x00 /* Periodic Interrupt Disabled */ -#define SH7750_RCR2_PES_DIV256 0x10 /* Generated at 1/256 sec interval */ -#define SH7750_RCR2_PES_DIV64 0x20 /* Generated at 1/64 sec interval */ -#define SH7750_RCR2_PES_DIV16 0x30 /* Generated at 1/16 sec interval */ -#define SH7750_RCR2_PES_DIV4 0x40 /* Generated at 1/4 sec interval */ -#define SH7750_RCR2_PES_DIV2 0x50 /* Generated at 1/2 sec interval */ -#define SH7750_RCR2_PES_x1 0x60 /* Generated at 1 sec interval */ -#define SH7750_RCR2_PES_x2 0x70 /* Generated at 2 sec interval */ -#define SH7750_RCR2_RTCEN 0x08 /* RTC Crystal Oscillator is Operated */ -#define SH7750_RCR2_ADJ 0x04 /* 30-Second Adjastment */ -#define SH7750_RCR2_RESET 0x02 /* Frequency divider circuits are reset */ -#define SH7750_RCR2_START 0x01 /* 0 - sec, min, hr, day-of-week, month, - year counters are stopped - 1 - sec, min, hr, day-of-week, month, - year counters operate normally */ -/* - * Bus State Controller - BSC - */ -/* Bus Control Register 1 - BCR1 */ -#define SH7750_BCR1_REGOFS 0x800000 /* offset */ -#define SH7750_BCR1 SH7750_P4_REG32(SH7750_BCR1_REGOFS) -#define SH7750_BCR1_A7 SH7750_A7_REG32(SH7750_BCR1_REGOFS) -#define SH7750_BCR1_ENDIAN 0x80000000 /* Endianness (1 - little endian) */ -#define SH7750_BCR1_MASTER 0x40000000 /* Master/Slave mode (1-master) */ -#define SH7750_BCR1_A0MPX 0x20000000 /* Area 0 Memory Type (0-SRAM,1-MPX) */ -#define SH7750_BCR1_IPUP 0x02000000 /* Input Pin Pull-up Control: - 0 - pull-up resistor is on for - control input pins - 1 - pull-up resistor is off */ -#define SH7750_BCR1_OPUP 0x01000000 /* Output Pin Pull-up Control: - 0 - pull-up resistor is on for - control output pins - 1 - pull-up resistor is off */ -#define SH7750_BCR1_A1MBC 0x00200000 /* Area 1 SRAM Byte Control Mode: - 0 - Area 1 SRAM is set to - normal mode - 1 - Area 1 SRAM is set to byte - control mode */ -#define SH7750_BCR1_A4MBC 0x00100000 /* Area 4 SRAM Byte Control Mode: - 0 - Area 4 SRAM is set to - normal mode - 1 - Area 4 SRAM is set to byte - control mode */ -#define SH7750_BCR1_BREQEN 0x00080000 /* BREQ Enable: - 0 - External requests are not - accepted - 1 - External requests are - accepted */ -#define SH7750_BCR1_PSHR 0x00040000 /* Partial Sharing Bit: - 0 - Master Mode - 1 - Partial-sharing Mode */ -#define SH7750_BCR1_MEMMPX 0x00020000 /* Area 1 to 6 MPX Interface: - 0 - SRAM/burst ROM interface - 1 - MPX interface */ -#define SH7750_BCR1_HIZMEM 0x00008000 /* High Impendance Control. Specifies - the state of A[25:0], BS\, CSn\, - RD/WR\, CE2A\, CE2B\ in standby - mode and when bus is released: - 0 - signals go to High-Z mode - 1 - signals driven */ -#define SH7750_BCR1_HIZCNT 0x00004000 /* High Impendance Control. Specifies - the state of the RAS\, RAS2\, WEn\, - CASn\, DQMn, RD\, CASS\, FRAME\, - RD2\ signals in standby mode and - when bus is released: - 0 - signals go to High-Z mode - 1 - signals driven */ -#define SH7750_BCR1_A0BST 0x00003800 /* Area 0 Burst ROM Control */ -#define SH7750_BCR1_A0BST_SRAM 0x0000 /* Area 0 accessed as SRAM i/f */ -#define SH7750_BCR1_A0BST_ROM4 0x0800 /* Area 0 accessed as burst ROM - interface, 4 cosequtive access */ -#define SH7750_BCR1_A0BST_ROM8 0x1000 /* Area 0 accessed as burst ROM - interface, 8 cosequtive access */ -#define SH7750_BCR1_A0BST_ROM16 0x1800 /* Area 0 accessed as burst ROM - interface, 16 cosequtive access */ -#define SH7750_BCR1_A0BST_ROM32 0x2000 /* Area 0 accessed as burst ROM - interface, 32 cosequtive access */ - -#define SH7750_BCR1_A5BST 0x00000700 /* Area 5 Burst ROM Control */ -#define SH7750_BCR1_A5BST_SRAM 0x0000 /* Area 5 accessed as SRAM i/f */ -#define SH7750_BCR1_A5BST_ROM4 0x0100 /* Area 5 accessed as burst ROM - interface, 4 cosequtive access */ -#define SH7750_BCR1_A5BST_ROM8 0x0200 /* Area 5 accessed as burst ROM - interface, 8 cosequtive access */ -#define SH7750_BCR1_A5BST_ROM16 0x0300 /* Area 5 accessed as burst ROM - interface, 16 cosequtive access */ -#define SH7750_BCR1_A5BST_ROM32 0x0400 /* Area 5 accessed as burst ROM - interface, 32 cosequtive access */ - -#define SH7750_BCR1_A6BST 0x000000E0 /* Area 6 Burst ROM Control */ -#define SH7750_BCR1_A6BST_SRAM 0x0000 /* Area 6 accessed as SRAM i/f */ -#define SH7750_BCR1_A6BST_ROM4 0x0020 /* Area 6 accessed as burst ROM - interface, 4 cosequtive access */ -#define SH7750_BCR1_A6BST_ROM8 0x0040 /* Area 6 accessed as burst ROM - interface, 8 cosequtive access */ -#define SH7750_BCR1_A6BST_ROM16 0x0060 /* Area 6 accessed as burst ROM - interface, 16 cosequtive access */ -#define SH7750_BCR1_A6BST_ROM32 0x0080 /* Area 6 accessed as burst ROM - interface, 32 cosequtive access */ - -#define SH7750_BCR1_DRAMTP 0x001C /* Area 2 and 3 Memory Type */ -#define SH7750_BCR1_DRAMTP_2SRAM_3SRAM 0x0000 /* Area 2 and 3 are SRAM or MPX - interface. */ -#define SH7750_BCR1_DRAMTP_2SRAM_3SDRAM 0x0008 /* Area 2 - SRAM/MPX, Area 3 - - synchronous DRAM */ -#define SH7750_BCR1_DRAMTP_2SDRAM_3SDRAM 0x000C /* Area 2 and 3 are synchronous - DRAM interface */ -#define SH7750_BCR1_DRAMTP_2SRAM_3DRAM 0x0010 /* Area 2 - SRAM/MPX, Area 3 - - DRAM interface */ -#define SH7750_BCR1_DRAMTP_2DRAM_3DRAM 0x0014 /* Area 2 and 3 are DRAM - interface */ - -#define SH7750_BCR1_A56PCM 0x00000001 /* Area 5 and 6 Bus Type: - 0 - SRAM interface - 1 - PCMCIA interface */ - -/* Bus Control Register 2 (half) - BCR2 */ -#define SH7750_BCR2_REGOFS 0x800004 /* offset */ -#define SH7750_BCR2 SH7750_P4_REG32(SH7750_BCR2_REGOFS) -#define SH7750_BCR2_A7 SH7750_A7_REG32(SH7750_BCR2_REGOFS) - -#define SH7750_BCR2_A0SZ 0xC000 /* Area 0 Bus Width */ -#define SH7750_BCR2_A0SZ_S 14 -#define SH7750_BCR2_A6SZ 0x3000 /* Area 6 Bus Width */ -#define SH7750_BCR2_A6SZ_S 12 -#define SH7750_BCR2_A5SZ 0x0C00 /* Area 5 Bus Width */ -#define SH7750_BCR2_A5SZ_S 10 -#define SH7750_BCR2_A4SZ 0x0300 /* Area 4 Bus Width */ -#define SH7750_BCR2_A4SZ_S 8 -#define SH7750_BCR2_A3SZ 0x00C0 /* Area 3 Bus Width */ -#define SH7750_BCR2_A3SZ_S 6 -#define SH7750_BCR2_A2SZ 0x0030 /* Area 2 Bus Width */ -#define SH7750_BCR2_A2SZ_S 4 -#define SH7750_BCR2_A1SZ 0x000C /* Area 1 Bus Width */ -#define SH7750_BCR2_A1SZ_S 2 -#define SH7750_BCR2_SZ_64 0 /* 64 bits */ -#define SH7750_BCR2_SZ_8 1 /* 8 bits */ -#define SH7750_BCR2_SZ_16 2 /* 16 bits */ -#define SH7750_BCR2_SZ_32 3 /* 32 bits */ -#define SH7750_BCR2_PORTEN 0x0001 /* Port Function Enable : - 0 - D51-D32 are not used as a port - 1 - D51-D32 are used as a port */ - -/* Wait Control Register 1 - WCR1 */ -#define SH7750_WCR1_REGOFS 0x800008 /* offset */ -#define SH7750_WCR1 SH7750_P4_REG32(SH7750_WCR1_REGOFS) -#define SH7750_WCR1_A7 SH7750_A7_REG32(SH7750_WCR1_REGOFS) -#define SH7750_WCR1_DMAIW 0x70000000 /* DACK Device Inter-Cycle Idle - specification */ -#define SH7750_WCR1_DMAIW_S 28 -#define SH7750_WCR1_A6IW 0x07000000 /* Area 6 Inter-Cycle Idle spec. */ -#define SH7750_WCR1_A6IW_S 24 -#define SH7750_WCR1_A5IW 0x00700000 /* Area 5 Inter-Cycle Idle spec. */ -#define SH7750_WCR1_A5IW_S 20 -#define SH7750_WCR1_A4IW 0x00070000 /* Area 4 Inter-Cycle Idle spec. */ -#define SH7750_WCR1_A4IW_S 16 -#define SH7750_WCR1_A3IW 0x00007000 /* Area 3 Inter-Cycle Idle spec. */ -#define SH7750_WCR1_A3IW_S 12 -#define SH7750_WCR1_A2IW 0x00000700 /* Area 2 Inter-Cycle Idle spec. */ -#define SH7750_WCR1_A2IW_S 8 -#define SH7750_WCR1_A1IW 0x00000070 /* Area 1 Inter-Cycle Idle spec. */ -#define SH7750_WCR1_A1IW_S 4 -#define SH7750_WCR1_A0IW 0x00000007 /* Area 0 Inter-Cycle Idle spec. */ -#define SH7750_WCR1_A0IW_S 0 - -/* Wait Control Register 2 - WCR2 */ -#define SH7750_WCR2_REGOFS 0x80000C /* offset */ -#define SH7750_WCR2 SH7750_P4_REG32(SH7750_WCR2_REGOFS) -#define SH7750_WCR2_A7 SH7750_A7_REG32(SH7750_WCR2_REGOFS) - -#define SH7750_WCR2_A6W 0xE0000000 /* Area 6 Wait Control */ -#define SH7750_WCR2_A6W_S 29 -#define SH7750_WCR2_A6B 0x1C000000 /* Area 6 Burst Pitch */ -#define SH7750_WCR2_A6B_S 26 -#define SH7750_WCR2_A5W 0x03800000 /* Area 5 Wait Control */ -#define SH7750_WCR2_A5W_S 23 -#define SH7750_WCR2_A5B 0x00700000 /* Area 5 Burst Pitch */ -#define SH7750_WCR2_A5B_S 20 -#define SH7750_WCR2_A4W 0x000E0000 /* Area 4 Wait Control */ -#define SH7750_WCR2_A4W_S 17 -#define SH7750_WCR2_A3W 0x0000E000 /* Area 3 Wait Control */ -#define SH7750_WCR2_A3W_S 13 -#define SH7750_WCR2_A2W 0x00000E00 /* Area 2 Wait Control */ -#define SH7750_WCR2_A2W_S 9 -#define SH7750_WCR2_A1W 0x000001C0 /* Area 1 Wait Control */ -#define SH7750_WCR2_A1W_S 6 -#define SH7750_WCR2_A0W 0x00000038 /* Area 0 Wait Control */ -#define SH7750_WCR2_A0W_S 3 -#define SH7750_WCR2_A0B 0x00000007 /* Area 0 Burst Pitch */ -#define SH7750_WCR2_A0B_S 0 - -#define SH7750_WCR2_WS0 0 /* 0 wait states inserted */ -#define SH7750_WCR2_WS1 1 /* 1 wait states inserted */ -#define SH7750_WCR2_WS2 2 /* 2 wait states inserted */ -#define SH7750_WCR2_WS3 3 /* 3 wait states inserted */ -#define SH7750_WCR2_WS6 4 /* 6 wait states inserted */ -#define SH7750_WCR2_WS9 5 /* 9 wait states inserted */ -#define SH7750_WCR2_WS12 6 /* 12 wait states inserted */ -#define SH7750_WCR2_WS15 7 /* 15 wait states inserted */ - -#define SH7750_WCR2_BPWS0 0 /* 0 wait states inserted from 2nd access */ -#define SH7750_WCR2_BPWS1 1 /* 1 wait states inserted from 2nd access */ -#define SH7750_WCR2_BPWS2 2 /* 2 wait states inserted from 2nd access */ -#define SH7750_WCR2_BPWS3 3 /* 3 wait states inserted from 2nd access */ -#define SH7750_WCR2_BPWS4 4 /* 4 wait states inserted from 2nd access */ -#define SH7750_WCR2_BPWS5 5 /* 5 wait states inserted from 2nd access */ -#define SH7750_WCR2_BPWS6 6 /* 6 wait states inserted from 2nd access */ -#define SH7750_WCR2_BPWS7 7 /* 7 wait states inserted from 2nd access */ - -/* DRAM CAS\ Assertion Delay (area 3,2) */ -#define SH7750_WCR2_DRAM_CAS_ASW1 0 /* 1 cycle */ -#define SH7750_WCR2_DRAM_CAS_ASW2 1 /* 2 cycles */ -#define SH7750_WCR2_DRAM_CAS_ASW3 2 /* 3 cycles */ -#define SH7750_WCR2_DRAM_CAS_ASW4 3 /* 4 cycles */ -#define SH7750_WCR2_DRAM_CAS_ASW7 4 /* 7 cycles */ -#define SH7750_WCR2_DRAM_CAS_ASW10 5 /* 10 cycles */ -#define SH7750_WCR2_DRAM_CAS_ASW13 6 /* 13 cycles */ -#define SH7750_WCR2_DRAM_CAS_ASW16 7 /* 16 cycles */ - -/* SDRAM CAS\ Latency Cycles */ -#define SH7750_WCR2_SDRAM_CAS_LAT1 1 /* 1 cycle */ -#define SH7750_WCR2_SDRAM_CAS_LAT2 2 /* 2 cycles */ -#define SH7750_WCR2_SDRAM_CAS_LAT3 3 /* 3 cycles */ -#define SH7750_WCR2_SDRAM_CAS_LAT4 4 /* 4 cycles */ -#define SH7750_WCR2_SDRAM_CAS_LAT5 5 /* 5 cycles */ - -/* Wait Control Register 3 - WCR3 */ -#define SH7750_WCR3_REGOFS 0x800010 /* offset */ -#define SH7750_WCR3 SH7750_P4_REG32(SH7750_WCR3_REGOFS) -#define SH7750_WCR3_A7 SH7750_A7_REG32(SH7750_WCR3_REGOFS) - -#define SH7750_WCR3_A6S 0x04000000 /* Area 6 Write Strobe Setup time */ -#define SH7750_WCR3_A6H 0x03000000 /* Area 6 Data Hold Time */ -#define SH7750_WCR3_A6H_S 24 -#define SH7750_WCR3_A5S 0x00400000 /* Area 5 Write Strobe Setup time */ -#define SH7750_WCR3_A5H 0x00300000 /* Area 5 Data Hold Time */ -#define SH7750_WCR3_A5H_S 20 -#define SH7750_WCR3_A4S 0x00040000 /* Area 4 Write Strobe Setup time */ -#define SH7750_WCR3_A4H 0x00030000 /* Area 4 Data Hold Time */ -#define SH7750_WCR3_A4H_S 16 -#define SH7750_WCR3_A3S 0x00004000 /* Area 3 Write Strobe Setup time */ -#define SH7750_WCR3_A3H 0x00003000 /* Area 3 Data Hold Time */ -#define SH7750_WCR3_A3H_S 12 -#define SH7750_WCR3_A2S 0x00000400 /* Area 2 Write Strobe Setup time */ -#define SH7750_WCR3_A2H 0x00000300 /* Area 2 Data Hold Time */ -#define SH7750_WCR3_A2H_S 8 -#define SH7750_WCR3_A1S 0x00000040 /* Area 1 Write Strobe Setup time */ -#define SH7750_WCR3_A1H 0x00000030 /* Area 1 Data Hold Time */ -#define SH7750_WCR3_A1H_S 4 -#define SH7750_WCR3_A0S 0x00000004 /* Area 0 Write Strobe Setup time */ -#define SH7750_WCR3_A0H 0x00000003 /* Area 0 Data Hold Time */ -#define SH7750_WCR3_A0H_S 0 - -#define SH7750_WCR3_DHWS_0 0 /* 0 wait states data hold time */ -#define SH7750_WCR3_DHWS_1 1 /* 1 wait states data hold time */ -#define SH7750_WCR3_DHWS_2 2 /* 2 wait states data hold time */ -#define SH7750_WCR3_DHWS_3 3 /* 3 wait states data hold time */ - -#define SH7750_MCR_REGOFS 0x800014 /* offset */ -#define SH7750_MCR SH7750_P4_REG32(SH7750_MCR_REGOFS) -#define SH7750_MCR_A7 SH7750_A7_REG32(SH7750_MCR_REGOFS) - -#define SH7750_MCR_RASD 0x80000000 /* RAS Down mode */ -#define SH7750_MCR_MRSET 0x40000000 /* SDRAM Mode Register Set */ -#define SH7750_MCR_PALL 0x00000000 /* SDRAM Precharge All cmd. Mode */ -#define SH7750_MCR_TRC 0x38000000 /* RAS Precharge Time at End of - Refresh: */ -#define SH7750_MCR_TRC_0 0x00000000 /* 0 */ -#define SH7750_MCR_TRC_3 0x08000000 /* 3 */ -#define SH7750_MCR_TRC_6 0x10000000 /* 6 */ -#define SH7750_MCR_TRC_9 0x18000000 /* 9 */ -#define SH7750_MCR_TRC_12 0x20000000 /* 12 */ -#define SH7750_MCR_TRC_15 0x28000000 /* 15 */ -#define SH7750_MCR_TRC_18 0x30000000 /* 18 */ -#define SH7750_MCR_TRC_21 0x38000000 /* 21 */ - -#define SH7750_MCR_TCAS 0x00800000 /* CAS Negation Period */ -#define SH7750_MCR_TCAS_1 0x00000000 /* 1 */ -#define SH7750_MCR_TCAS_2 0x00800000 /* 2 */ - -#define SH7750_MCR_TPC 0x00380000 /* DRAM: RAS Precharge Period - SDRAM: minimum number of cycles - until the next bank active cmd - is output after precharging */ -#define SH7750_MCR_TPC_S 19 -#define SH7750_MCR_TPC_SDRAM_1 0x00000000 /* 1 cycle */ -#define SH7750_MCR_TPC_SDRAM_2 0x00080000 /* 2 cycles */ -#define SH7750_MCR_TPC_SDRAM_3 0x00100000 /* 3 cycles */ -#define SH7750_MCR_TPC_SDRAM_4 0x00180000 /* 4 cycles */ -#define SH7750_MCR_TPC_SDRAM_5 0x00200000 /* 5 cycles */ -#define SH7750_MCR_TPC_SDRAM_6 0x00280000 /* 6 cycles */ -#define SH7750_MCR_TPC_SDRAM_7 0x00300000 /* 7 cycles */ -#define SH7750_MCR_TPC_SDRAM_8 0x00380000 /* 8 cycles */ - -#define SH7750_MCR_RCD 0x00030000 /* DRAM: RAS-CAS Assertion Delay time - SDRAM: bank active-read/write cmd - delay time */ -#define SH7750_MCR_RCD_DRAM_2 0x00000000 /* DRAM delay 2 clocks */ -#define SH7750_MCR_RCD_DRAM_3 0x00010000 /* DRAM delay 3 clocks */ -#define SH7750_MCR_RCD_DRAM_4 0x00020000 /* DRAM delay 4 clocks */ -#define SH7750_MCR_RCD_DRAM_5 0x00030000 /* DRAM delay 5 clocks */ -#define SH7750_MCR_RCD_SDRAM_2 0x00010000 /* DRAM delay 2 clocks */ -#define SH7750_MCR_RCD_SDRAM_3 0x00020000 /* DRAM delay 3 clocks */ -#define SH7750_MCR_RCD_SDRAM_4 0x00030000 /* DRAM delay 4 clocks */ - -#define SH7750_MCR_TRWL 0x0000E000 /* SDRAM Write Precharge Delay */ -#define SH7750_MCR_TRWL_1 0x00000000 /* 1 */ -#define SH7750_MCR_TRWL_2 0x00002000 /* 2 */ -#define SH7750_MCR_TRWL_3 0x00004000 /* 3 */ -#define SH7750_MCR_TRWL_4 0x00006000 /* 4 */ -#define SH7750_MCR_TRWL_5 0x00008000 /* 5 */ - -#define SH7750_MCR_TRAS 0x00001C00 /* DRAM: CAS-Before-RAS Refresh RAS - asserting period - SDRAM: Command interval after - synchronous DRAM refresh */ -#define SH7750_MCR_TRAS_DRAM_2 0x00000000 /* 2 */ -#define SH7750_MCR_TRAS_DRAM_3 0x00000400 /* 3 */ -#define SH7750_MCR_TRAS_DRAM_4 0x00000800 /* 4 */ -#define SH7750_MCR_TRAS_DRAM_5 0x00000C00 /* 5 */ -#define SH7750_MCR_TRAS_DRAM_6 0x00001000 /* 6 */ -#define SH7750_MCR_TRAS_DRAM_7 0x00001400 /* 7 */ -#define SH7750_MCR_TRAS_DRAM_8 0x00001800 /* 8 */ -#define SH7750_MCR_TRAS_DRAM_9 0x00001C00 /* 9 */ - -#define SH7750_MCR_TRAS_SDRAM_TRC_4 0x00000000 /* 4 + TRC */ -#define SH7750_MCR_TRAS_SDRAM_TRC_5 0x00000400 /* 5 + TRC */ -#define SH7750_MCR_TRAS_SDRAM_TRC_6 0x00000800 /* 6 + TRC */ -#define SH7750_MCR_TRAS_SDRAM_TRC_7 0x00000C00 /* 7 + TRC */ -#define SH7750_MCR_TRAS_SDRAM_TRC_8 0x00001000 /* 8 + TRC */ -#define SH7750_MCR_TRAS_SDRAM_TRC_9 0x00001400 /* 9 + TRC */ -#define SH7750_MCR_TRAS_SDRAM_TRC_10 0x00001800 /* 10 + TRC */ -#define SH7750_MCR_TRAS_SDRAM_TRC_11 0x00001C00 /* 11 + TRC */ - -#define SH7750_MCR_BE 0x00000200 /* Burst Enable */ -#define SH7750_MCR_SZ 0x00000180 /* Memory Data Size */ -#define SH7750_MCR_SZ_64 0x00000000 /* 64 bits */ -#define SH7750_MCR_SZ_16 0x00000100 /* 16 bits */ -#define SH7750_MCR_SZ_32 0x00000180 /* 32 bits */ - -#define SH7750_MCR_AMX 0x00000078 /* Address Multiplexing */ -#define SH7750_MCR_AMX_S 3 -#define SH7750_MCR_AMX_DRAM_8BIT_COL 0x00000000 /* 8-bit column addr */ -#define SH7750_MCR_AMX_DRAM_9BIT_COL 0x00000008 /* 9-bit column addr */ -#define SH7750_MCR_AMX_DRAM_10BIT_COL 0x00000010 /* 10-bit column addr */ -#define SH7750_MCR_AMX_DRAM_11BIT_COL 0x00000018 /* 11-bit column addr */ -#define SH7750_MCR_AMX_DRAM_12BIT_COL 0x00000020 /* 12-bit column addr */ -/* See SH7750 Hardware Manual for SDRAM address multiplexor selection */ - -#define SH7750_MCR_RFSH 0x00000004 /* Refresh Control */ -#define SH7750_MCR_RMODE 0x00000002 /* Refresh Mode: */ -#define SH7750_MCR_RMODE_NORMAL 0x00000000 /* Normal Refresh Mode */ -#define SH7750_MCR_RMODE_SELF 0x00000002 /* Self-Refresh Mode */ -#define SH7750_MCR_RMODE_EDO 0x00000001 /* EDO Mode */ - -/* SDRAM Mode Set address */ -#define SH7750_SDRAM_MODE_A2_BASE 0xFF900000 -#define SH7750_SDRAM_MODE_A3_BASE 0xFF940000 -#define SH7750_SDRAM_MODE_A2_32BIT(x) (SH7750_SDRAM_MODE_A2_BASE + ((x) << 2)) -#define SH7750_SDRAM_MODE_A3_32BIT(x) (SH7750_SDRAM_MODE_A3_BASE + ((x) << 2)) -#define SH7750_SDRAM_MODE_A2_64BIT(x) (SH7750_SDRAM_MODE_A2_BASE + ((x) << 3)) -#define SH7750_SDRAM_MODE_A3_64BIT(x) (SH7750_SDRAM_MODE_A3_BASE + ((x) << 3)) - - -/* PCMCIA Control Register (half) - PCR */ -#define SH7750_PCR_REGOFS 0x800018 /* offset */ -#define SH7750_PCR SH7750_P4_REG32(SH7750_PCR_REGOFS) -#define SH7750_PCR_A7 SH7750_A7_REG32(SH7750_PCR_REGOFS) - -#define SH7750_PCR_A5PCW 0xC000 /* Area 5 PCMCIA Wait - Number of wait - states to be added to the number of - waits specified by WCR2 in a low-speed - PCMCIA wait cycle */ -#define SH7750_PCR_A5PCW_0 0x0000 /* 0 waits inserted */ -#define SH7750_PCR_A5PCW_15 0x4000 /* 15 waits inserted */ -#define SH7750_PCR_A5PCW_30 0x8000 /* 30 waits inserted */ -#define SH7750_PCR_A5PCW_50 0xC000 /* 50 waits inserted */ - -#define SH7750_PCR_A6PCW 0x3000 /* Area 6 PCMCIA Wait - Number of wait - states to be added to the number of - waits specified by WCR2 in a low-speed - PCMCIA wait cycle */ -#define SH7750_PCR_A6PCW_0 0x0000 /* 0 waits inserted */ -#define SH7750_PCR_A6PCW_15 0x1000 /* 15 waits inserted */ -#define SH7750_PCR_A6PCW_30 0x2000 /* 30 waits inserted */ -#define SH7750_PCR_A6PCW_50 0x3000 /* 50 waits inserted */ - -#define SH7750_PCR_A5TED 0x0E00 /* Area 5 Address-OE\/WE\ Assertion Delay, - delay time from address output to - OE\/WE\ assertion on the connected - PCMCIA interface */ -#define SH7750_PCR_A5TED_S 9 -#define SH7750_PCR_A6TED 0x01C0 /* Area 6 Address-OE\/WE\ Assertion Delay */ -#define SH7750_PCR_A6TED_S 6 - -#define SH7750_PCR_TED_0WS 0 /* 0 Waits inserted */ -#define SH7750_PCR_TED_1WS 1 /* 1 Waits inserted */ -#define SH7750_PCR_TED_2WS 2 /* 2 Waits inserted */ -#define SH7750_PCR_TED_3WS 3 /* 3 Waits inserted */ -#define SH7750_PCR_TED_6WS 4 /* 6 Waits inserted */ -#define SH7750_PCR_TED_9WS 5 /* 9 Waits inserted */ -#define SH7750_PCR_TED_12WS 6 /* 12 Waits inserted */ -#define SH7750_PCR_TED_15WS 7 /* 15 Waits inserted */ - -#define SH7750_PCR_A5TEH 0x0038 /* Area 5 OE\/WE\ Negation Address delay, - address hold delay time from OE\/WE\ - negation in a write on the connected - PCMCIA interface */ -#define SH7750_PCR_A5TEH_S 3 - -#define SH7750_PCR_A6TEH 0x0007 /* Area 6 OE\/WE\ Negation Address delay */ -#define SH7750_PCR_A6TEH_S 0 - -#define SH7750_PCR_TEH_0WS 0 /* 0 Waits inserted */ -#define SH7750_PCR_TEH_1WS 1 /* 1 Waits inserted */ -#define SH7750_PCR_TEH_2WS 2 /* 2 Waits inserted */ -#define SH7750_PCR_TEH_3WS 3 /* 3 Waits inserted */ -#define SH7750_PCR_TEH_6WS 4 /* 6 Waits inserted */ -#define SH7750_PCR_TEH_9WS 5 /* 9 Waits inserted */ -#define SH7750_PCR_TEH_12WS 6 /* 12 Waits inserted */ -#define SH7750_PCR_TEH_15WS 7 /* 15 Waits inserted */ - -/* Refresh Timer Control/Status Register (half) - RTSCR */ -#define SH7750_RTCSR_REGOFS 0x80001C /* offset */ -#define SH7750_RTCSR SH7750_P4_REG32(SH7750_RTCSR_REGOFS) -#define SH7750_RTCSR_A7 SH7750_A7_REG32(SH7750_RTCSR_REGOFS) - -#define SH7750_RTCSR_KEY 0xA500 /* RTCSR write key */ -#define SH7750_RTCSR_CMF 0x0080 /* Compare-Match Flag (indicates a - match between the refresh timer - counter and refresh time constant) */ -#define SH7750_RTCSR_CMIE 0x0040 /* Compare-Match Interrupt Enable */ -#define SH7750_RTCSR_CKS 0x0038 /* Refresh Counter Clock Selects */ -#define SH7750_RTCSR_CKS_DIS 0x0000 /* Clock Input Disabled */ -#define SH7750_RTCSR_CKS_CKIO_DIV4 0x0008 /* Bus Clock / 4 */ -#define SH7750_RTCSR_CKS_CKIO_DIV16 0x0010 /* Bus Clock / 16 */ -#define SH7750_RTCSR_CKS_CKIO_DIV64 0x0018 /* Bus Clock / 64 */ -#define SH7750_RTCSR_CKS_CKIO_DIV256 0x0020 /* Bus Clock / 256 */ -#define SH7750_RTCSR_CKS_CKIO_DIV1024 0x0028 /* Bus Clock / 1024 */ -#define SH7750_RTCSR_CKS_CKIO_DIV2048 0x0030 /* Bus Clock / 2048 */ -#define SH7750_RTCSR_CKS_CKIO_DIV4096 0x0038 /* Bus Clock / 4096 */ - -#define SH7750_RTCSR_OVF 0x0004 /* Refresh Count Overflow Flag */ -#define SH7750_RTCSR_OVIE 0x0002 /* Refresh Count Overflow Interrupt - Enable */ -#define SH7750_RTCSR_LMTS 0x0001 /* Refresh Count Overflow Limit Select */ -#define SH7750_RTCSR_LMTS_1024 0x0000 /* Count Limit is 1024 */ -#define SH7750_RTCSR_LMTS_512 0x0001 /* Count Limit is 512 */ - -/* Refresh Timer Counter (half) - RTCNT */ -#define SH7750_RTCNT_REGOFS 0x800020 /* offset */ -#define SH7750_RTCNT SH7750_P4_REG32(SH7750_RTCNT_REGOFS) -#define SH7750_RTCNT_A7 SH7750_A7_REG32(SH7750_RTCNT_REGOFS) - -#define SH7750_RTCNT_KEY 0xA500 /* RTCNT write key */ - -/* Refresh Time Constant Register (half) - RTCOR */ -#define SH7750_RTCOR_REGOFS 0x800024 /* offset */ -#define SH7750_RTCOR SH7750_P4_REG32(SH7750_RTCOR_REGOFS) -#define SH7750_RTCOR_A7 SH7750_A7_REG32(SH7750_RTCOR_REGOFS) - -#define SH7750_RTCOR_KEY 0xA500 /* RTCOR write key */ - -/* Refresh Count Register (half) - RFCR */ -#define SH7750_RFCR_REGOFS 0x800028 /* offset */ -#define SH7750_RFCR SH7750_P4_REG32(SH7750_RFCR_REGOFS) -#define SH7750_RFCR_A7 SH7750_A7_REG32(SH7750_RFCR_REGOFS) - -#define SH7750_RFCR_KEY 0xA400 /* RFCR write key */ - -/* Synchronous DRAM mode registers - SDMR */ -#define SH7750_SDMR2_REGOFS 0x900000 /* base offset */ -#define SH7750_SDMR2_REGNB 0x0FFC /* nb of register */ -#define SH7750_SDMR2 SH7750_P4_REG32(SH7750_SDMR2_REGOFS) -#define SH7750_SDMR2_A7 SH7750_A7_REG32(SH7750_SDMR2_REGOFS) - -#define SH7750_SDMR3_REGOFS 0x940000 /* offset */ -#define SH7750_SDMR3_REGNB 0x0FFC /* nb of register */ -#define SH7750_SDMR3 SH7750_P4_REG32(SH7750_SDMR3_REGOFS) -#define SH7750_SDMR3_A7 SH7750_A7_REG32(SH7750_SDMR3_REGOFS) - -/* - * Direct Memory Access Controller (DMAC) - */ - -/* DMA Source Address Register - SAR0, SAR1, SAR2, SAR3 */ -#define SH7750_SAR_REGOFS(n) (0xA00000 + ((n)*16)) /* offset */ -#define SH7750_SAR(n) SH7750_P4_REG32(SH7750_SAR_REGOFS(n)) -#define SH7750_SAR_A7(n) SH7750_A7_REG32(SH7750_SAR_REGOFS(n)) -#define SH7750_SAR0 SH7750_SAR(0) -#define SH7750_SAR1 SH7750_SAR(1) -#define SH7750_SAR2 SH7750_SAR(2) -#define SH7750_SAR3 SH7750_SAR(3) -#define SH7750_SAR0_A7 SH7750_SAR_A7(0) -#define SH7750_SAR1_A7 SH7750_SAR_A7(1) -#define SH7750_SAR2_A7 SH7750_SAR_A7(2) -#define SH7750_SAR3_A7 SH7750_SAR_A7(3) - -/* DMA Destination Address Register - DAR0, DAR1, DAR2, DAR3 */ -#define SH7750_DAR_REGOFS(n) (0xA00004 + ((n)*16)) /* offset */ -#define SH7750_DAR(n) SH7750_P4_REG32(SH7750_DAR_REGOFS(n)) -#define SH7750_DAR_A7(n) SH7750_A7_REG32(SH7750_DAR_REGOFS(n)) -#define SH7750_DAR0 SH7750_DAR(0) -#define SH7750_DAR1 SH7750_DAR(1) -#define SH7750_DAR2 SH7750_DAR(2) -#define SH7750_DAR3 SH7750_DAR(3) -#define SH7750_DAR0_A7 SH7750_DAR_A7(0) -#define SH7750_DAR1_A7 SH7750_DAR_A7(1) -#define SH7750_DAR2_A7 SH7750_DAR_A7(2) -#define SH7750_DAR3_A7 SH7750_DAR_A7(3) - -/* DMA Transfer Count Register - DMATCR0, DMATCR1, DMATCR2, DMATCR3 */ -#define SH7750_DMATCR_REGOFS(n) (0xA00008 + ((n)*16)) /* offset */ -#define SH7750_DMATCR(n) SH7750_P4_REG32(SH7750_DMATCR_REGOFS(n)) -#define SH7750_DMATCR_A7(n) SH7750_A7_REG32(SH7750_DMATCR_REGOFS(n)) -#define SH7750_DMATCR0_P4 SH7750_DMATCR(0) -#define SH7750_DMATCR1_P4 SH7750_DMATCR(1) -#define SH7750_DMATCR2_P4 SH7750_DMATCR(2) -#define SH7750_DMATCR3_P4 SH7750_DMATCR(3) -#define SH7750_DMATCR0_A7 SH7750_DMATCR_A7(0) -#define SH7750_DMATCR1_A7 SH7750_DMATCR_A7(1) -#define SH7750_DMATCR2_A7 SH7750_DMATCR_A7(2) -#define SH7750_DMATCR3_A7 SH7750_DMATCR_A7(3) - -/* DMA Channel Control Register - CHCR0, CHCR1, CHCR2, CHCR3 */ -#define SH7750_CHCR_REGOFS(n) (0xA0000C + ((n)*16)) /* offset */ -#define SH7750_CHCR(n) SH7750_P4_REG32(SH7750_CHCR_REGOFS(n)) -#define SH7750_CHCR_A7(n) SH7750_A7_REG32(SH7750_CHCR_REGOFS(n)) -#define SH7750_CHCR0 SH7750_CHCR(0) -#define SH7750_CHCR1 SH7750_CHCR(1) -#define SH7750_CHCR2 SH7750_CHCR(2) -#define SH7750_CHCR3 SH7750_CHCR(3) -#define SH7750_CHCR0_A7 SH7750_CHCR_A7(0) -#define SH7750_CHCR1_A7 SH7750_CHCR_A7(1) -#define SH7750_CHCR2_A7 SH7750_CHCR_A7(2) -#define SH7750_CHCR3_A7 SH7750_CHCR_A7(3) - -#define SH7750_CHCR_SSA 0xE0000000 /* Source Address Space Attribute */ -#define SH7750_CHCR_SSA_PCMCIA 0x00000000 /* Reserved in PCMCIA access */ -#define SH7750_CHCR_SSA_DYNBSZ 0x20000000 /* Dynamic Bus Sizing I/O space */ -#define SH7750_CHCR_SSA_IO8 0x40000000 /* 8-bit I/O space */ -#define SH7750_CHCR_SSA_IO16 0x60000000 /* 16-bit I/O space */ -#define SH7750_CHCR_SSA_CMEM8 0x80000000 /* 8-bit common memory space */ -#define SH7750_CHCR_SSA_CMEM16 0xA0000000 /* 16-bit common memory space */ -#define SH7750_CHCR_SSA_AMEM8 0xC0000000 /* 8-bit attribute memory space */ -#define SH7750_CHCR_SSA_AMEM16 0xE0000000 /* 16-bit attribute memory space */ - -#define SH7750_CHCR_STC 0x10000000 /* Source Address Wait Control Select, - specifies CS5 or CS6 space wait - control for PCMCIA access */ - -#define SH7750_CHCR_DSA 0x0E000000 /* Source Address Space Attribute */ -#define SH7750_CHCR_DSA_PCMCIA 0x00000000 /* Reserved in PCMCIA access */ -#define SH7750_CHCR_DSA_DYNBSZ 0x02000000 /* Dynamic Bus Sizing I/O space */ -#define SH7750_CHCR_DSA_IO8 0x04000000 /* 8-bit I/O space */ -#define SH7750_CHCR_DSA_IO16 0x06000000 /* 16-bit I/O space */ -#define SH7750_CHCR_DSA_CMEM8 0x08000000 /* 8-bit common memory space */ -#define SH7750_CHCR_DSA_CMEM16 0x0A000000 /* 16-bit common memory space */ -#define SH7750_CHCR_DSA_AMEM8 0x0C000000 /* 8-bit attribute memory space */ -#define SH7750_CHCR_DSA_AMEM16 0x0E000000 /* 16-bit attribute memory space */ - -#define SH7750_CHCR_DTC 0x01000000 /* Destination Address Wait Control - Select, specifies CS5 or CS6 - space wait control for PCMCIA - access */ - -#define SH7750_CHCR_DS 0x00080000 /* DREQ\ Select : */ -#define SH7750_CHCR_DS_LOWLVL 0x00000000 /* Low Level Detection */ -#define SH7750_CHCR_DS_FALL 0x00080000 /* Falling Edge Detection */ - -#define SH7750_CHCR_RL 0x00040000 /* Request Check Level: */ -#define SH7750_CHCR_RL_ACTH 0x00000000 /* DRAK is an active high out */ -#define SH7750_CHCR_RL_ACTL 0x00040000 /* DRAK is an active low out */ - -#define SH7750_CHCR_AM 0x00020000 /* Acknowledge Mode: */ -#define SH7750_CHCR_AM_RD 0x00000000 /* DACK is output in read cycle */ -#define SH7750_CHCR_AM_WR 0x00020000 /* DACK is output in write cycle */ - -#define SH7750_CHCR_AL 0x00010000 /* Acknowledge Level: */ -#define SH7750_CHCR_AL_ACTH 0x00000000 /* DACK is an active high out */ -#define SH7750_CHCR_AL_ACTL 0x00010000 /* DACK is an active low out */ - -#define SH7750_CHCR_DM 0x0000C000 /* Destination Address Mode: */ -#define SH7750_CHCR_DM_FIX 0x00000000 /* Destination Addr Fixed */ -#define SH7750_CHCR_DM_INC 0x00004000 /* Destination Addr Incremented */ -#define SH7750_CHCR_DM_DEC 0x00008000 /* Destination Addr Decremented */ - -#define SH7750_CHCR_SM 0x00003000 /* Source Address Mode: */ -#define SH7750_CHCR_SM_FIX 0x00000000 /* Source Addr Fixed */ -#define SH7750_CHCR_SM_INC 0x00001000 /* Source Addr Incremented */ -#define SH7750_CHCR_SM_DEC 0x00002000 /* Source Addr Decremented */ - -#define SH7750_CHCR_RS 0x00000F00 /* Request Source Select: */ -#define SH7750_CHCR_RS_ER_DA_EA_TO_EA 0x000 /* External Request, Dual Address - Mode (External Addr Space-> - External Addr Space) */ -#define SH7750_CHCR_RS_ER_SA_EA_TO_ED 0x200 /* External Request, Single - Address Mode (External Addr - Space -> External Device) */ -#define SH7750_CHCR_RS_ER_SA_ED_TO_EA 0x300 /* External Request, Single - Address Mode, (External - Device -> External Addr - Space) */ -#define SH7750_CHCR_RS_AR_EA_TO_EA 0x400 /* Auto-Request (External Addr - Space -> External Addr Space) */ - -#define SH7750_CHCR_RS_AR_EA_TO_OCP 0x500 /* Auto-Request (External Addr - Space -> On-chip Peripheral - Module) */ -#define SH7750_CHCR_RS_AR_OCP_TO_EA 0x600 /* Auto-Request (On-chip - Peripheral Module -> - External Addr Space */ -#define SH7750_CHCR_RS_SCITX_EA_TO_SC 0x800 /* SCI Transmit-Data-Empty intr - transfer request (external - address space -> SCTDR1) */ -#define SH7750_CHCR_RS_SCIRX_SC_TO_EA 0x900 /* SCI Receive-Data-Full intr - transfer request (SCRDR1 -> - External Addr Space) */ -#define SH7750_CHCR_RS_SCIFTX_EA_TO_SC 0xA00 /* SCIF Transmit-Data-Empty intr - transfer request (external - address space -> SCFTDR1) */ -#define SH7750_CHCR_RS_SCIFRX_SC_TO_EA 0xB00 /* SCIF Receive-Data-Full intr - transfer request (SCFRDR2 -> - External Addr Space) */ -#define SH7750_CHCR_RS_TMU2_EA_TO_EA 0xC00 /* TMU Channel 2 (input capture - interrupt), (external address - space -> external address - space) */ -#define SH7750_CHCR_RS_TMU2_EA_TO_OCP 0xD00 /* TMU Channel 2 (input capture - interrupt), (external address - space -> on-chip peripheral - module) */ -#define SH7750_CHCR_RS_TMU2_OCP_TO_EA 0xE00 /* TMU Channel 2 (input capture - interrupt), (on-chip - peripheral module -> external - address space) */ - -#define SH7750_CHCR_TM 0x00000080 /* Transmit mode: */ -#define SH7750_CHCR_TM_CSTEAL 0x00000000 /* Cycle Steal Mode */ -#define SH7750_CHCR_TM_BURST 0x00000080 /* Burst Mode */ - -#define SH7750_CHCR_TS 0x00000070 /* Transmit Size: */ -#define SH7750_CHCR_TS_QUAD 0x00000000 /* Quadword Size (64 bits) */ -#define SH7750_CHCR_TS_BYTE 0x00000010 /* Byte Size (8 bit) */ -#define SH7750_CHCR_TS_WORD 0x00000020 /* Word Size (16 bit) */ -#define SH7750_CHCR_TS_LONG 0x00000030 /* Longword Size (32 bit) */ -#define SH7750_CHCR_TS_BLOCK 0x00000040 /* 32-byte block transfer */ - -#define SH7750_CHCR_IE 0x00000004 /* Interrupt Enable */ -#define SH7750_CHCR_TE 0x00000002 /* Transfer End */ -#define SH7750_CHCR_DE 0x00000001 /* DMAC Enable */ - -/* DMA Operation Register - DMAOR */ -#define SH7750_DMAOR_REGOFS 0xA00040 /* offset */ -#define SH7750_DMAOR SH7750_P4_REG32(SH7750_DMAOR_REGOFS) -#define SH7750_DMAOR_A7 SH7750_A7_REG32(SH7750_DMAOR_REGOFS) - -#define SH7750_DMAOR_DDT 0x00008000 /* On-Demand Data Transfer Mode */ - -#define SH7750_DMAOR_PR 0x00000300 /* Priority Mode: */ -#define SH7750_DMAOR_PR_0123 0x00000000 /* CH0 > CH1 > CH2 > CH3 */ -#define SH7750_DMAOR_PR_0231 0x00000100 /* CH0 > CH2 > CH3 > CH1 */ -#define SH7750_DMAOR_PR_2013 0x00000200 /* CH2 > CH0 > CH1 > CH3 */ -#define SH7750_DMAOR_PR_RR 0x00000300 /* Round-robin mode */ - -#define SH7750_DMAOR_COD 0x00000010 /* Check Overrun for DREQ\ */ -#define SH7750_DMAOR_AE 0x00000004 /* Address Error flag */ -#define SH7750_DMAOR_NMIF 0x00000002 /* NMI Flag */ -#define SH7750_DMAOR_DME 0x00000001 /* DMAC Master Enable */ - -/* - * I/O Ports - */ -/* Port Control Register A - PCTRA */ -#define SH7750_PCTRA_REGOFS 0x80002C /* offset */ -#define SH7750_PCTRA SH7750_P4_REG32(SH7750_PCTRA_REGOFS) -#define SH7750_PCTRA_A7 SH7750_A7_REG32(SH7750_PCTRA_REGOFS) - -#define SH7750_PCTRA_PBPUP(n) 0 /* Bit n is pulled up */ -#define SH7750_PCTRA_PBNPUP(n) (1 << ((n)*2+1)) /* Bit n is not pulled up */ -#define SH7750_PCTRA_PBINP(n) 0 /* Bit n is an input */ -#define SH7750_PCTRA_PBOUT(n) (1 << ((n)*2)) /* Bit n is an output */ - -/* Port Data Register A - PDTRA(half) */ -#define SH7750_PDTRA_REGOFS 0x800030 /* offset */ -#define SH7750_PDTRA SH7750_P4_REG32(SH7750_PDTRA_REGOFS) -#define SH7750_PDTRA_A7 SH7750_A7_REG32(SH7750_PDTRA_REGOFS) - -#define SH7750_PDTRA_BIT(n) (1 << (n)) - -/* Port Control Register B - PCTRB */ -#define SH7750_PCTRB_REGOFS 0x800040 /* offset */ -#define SH7750_PCTRB SH7750_P4_REG32(SH7750_PCTRB_REGOFS) -#define SH7750_PCTRB_A7 SH7750_A7_REG32(SH7750_PCTRB_REGOFS) - -#define SH7750_PCTRB_PBPUP(n) 0 /* Bit n is pulled up */ -#define SH7750_PCTRB_PBNPUP(n) (1 << ((n-16)*2+1)) /* Bit n is not pulled up */ -#define SH7750_PCTRB_PBINP(n) 0 /* Bit n is an input */ -#define SH7750_PCTRB_PBOUT(n) (1 << ((n-16)*2)) /* Bit n is an output */ - -/* Port Data Register B - PDTRB(half) */ -#define SH7750_PDTRB_REGOFS 0x800044 /* offset */ -#define SH7750_PDTRB SH7750_P4_REG32(SH7750_PDTRB_REGOFS) -#define SH7750_PDTRB_A7 SH7750_A7_REG32(SH7750_PDTRB_REGOFS) - -#define SH7750_PDTRB_BIT(n) (1 << ((n)-16)) - -/* GPIO Interrupt Control Register - GPIOIC(half) */ -#define SH7750_GPIOIC_REGOFS 0x800048 /* offset */ -#define SH7750_GPIOIC SH7750_P4_REG32(SH7750_GPIOIC_REGOFS) -#define SH7750_GPIOIC_A7 SH7750_A7_REG32(SH7750_GPIOIC_REGOFS) - -#define SH7750_GPIOIC_PTIREN(n) (1 << (n)) /* Port n is used as a GPIO int */ - -/* - * Interrupt Controller - INTC - */ -/* Interrupt Control Register - ICR (half) */ -#define SH7750_ICR_REGOFS 0xD00000 /* offset */ -#define SH7750_ICR SH7750_P4_REG32(SH7750_ICR_REGOFS) -#define SH7750_ICR_A7 SH7750_A7_REG32(SH7750_ICR_REGOFS) - -#define SH7750_ICR_NMIL 0x8000 /* NMI Input Level */ -#define SH7750_ICR_MAI 0x4000 /* NMI Interrupt Mask */ - -#define SH7750_ICR_NMIB 0x0200 /* NMI Block Mode: */ -#define SH7750_ICR_NMIB_BLK 0x0000 /* NMI requests held pending while - SR.BL bit is set to 1 */ -#define SH7750_ICR_NMIB_NBLK 0x0200 /* NMI requests detected when SR.BL bit - set to 1 */ - -#define SH7750_ICR_NMIE 0x0100 /* NMI Edge Select: */ -#define SH7750_ICR_NMIE_FALL 0x0000 /* Interrupt request detected on falling - edge of NMI input */ -#define SH7750_ICR_NMIE_RISE 0x0100 /* Interrupt request detected on rising - edge of NMI input */ - -#define SH7750_ICR_IRLM 0x0080 /* IRL Pin Mode: */ -#define SH7750_ICR_IRLM_ENC 0x0000 /* IRL\ pins used as a level-encoded - interrupt requests */ -#define SH7750_ICR_IRLM_RAW 0x0080 /* IRL\ pins used as a four independent - interrupt requests */ - -/* - * User Break Controller registers - */ -#define SH7750_BARA 0x200000 /* Break address regiser A */ -#define SH7750_BAMRA 0x200004 /* Break address mask regiser A */ -#define SH7750_BBRA 0x200008 /* Break bus cycle regiser A */ -#define SH7750_BARB 0x20000c /* Break address regiser B */ -#define SH7750_BAMRB 0x200010 /* Break address mask regiser B */ -#define SH7750_BBRB 0x200014 /* Break bus cycle regiser B */ -#define SH7750_BASRB 0x000018 /* Break ASID regiser B */ -#define SH7750_BDRB 0x200018 /* Break data regiser B */ -#define SH7750_BDMRB 0x20001c /* Break data mask regiser B */ -#define SH7750_BRCR 0x200020 /* Break control register */ - -#define SH7750_BRCR_UDBE 0x0001 /* User break debug enable bit */ - -/* - * Missing in RTEMS, added for QEMU - */ -#define SH7750_BCR3_A7 0x1f800050 -#define SH7750_BCR4_A7 0x1e0a00f0 - -#endif diff --git a/qemu/hw/sh4/sh_pci.c b/qemu/hw/sh4/sh_pci.c deleted file mode 100644 index e820a3230..000000000 --- a/qemu/hw/sh4/sh_pci.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * SuperH on-chip PCIC emulation. - * - * Copyright (c) 2008 Takashi YOSHII - * - * 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/sysbus.h" -#include "hw/sh4/sh.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "qemu/bswap.h" -#include "exec/address-spaces.h" - -#define TYPE_SH_PCI_HOST_BRIDGE "sh_pci" - -#define SH_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(SHPCIState, (obj), TYPE_SH_PCI_HOST_BRIDGE) - -typedef struct SHPCIState { - PCIHostState parent_obj; - - PCIDevice *dev; - qemu_irq irq[4]; - MemoryRegion memconfig_p4; - MemoryRegion memconfig_a7; - MemoryRegion isa; - uint32_t par; - uint32_t mbr; - uint32_t iobr; -} SHPCIState; - -static void sh_pci_reg_write (void *p, hwaddr addr, uint64_t val, - unsigned size) -{ - SHPCIState *pcic = p; - PCIHostState *phb = PCI_HOST_BRIDGE(pcic); - - switch(addr) { - case 0 ... 0xfc: - cpu_to_le32w((uint32_t*)(pcic->dev->config + addr), val); - break; - case 0x1c0: - pcic->par = val; - break; - case 0x1c4: - pcic->mbr = val & 0xff000001; - break; - case 0x1c8: - if ((val & 0xfffc0000) != (pcic->iobr & 0xfffc0000)) { - memory_region_del_subregion(get_system_memory(), &pcic->isa); - pcic->iobr = val & 0xfffc0001; - memory_region_add_subregion(get_system_memory(), - pcic->iobr & 0xfffc0000, &pcic->isa); - } - break; - case 0x220: - pci_data_write(phb->bus, pcic->par, val, 4); - break; - } -} - -static uint64_t sh_pci_reg_read (void *p, hwaddr addr, - unsigned size) -{ - SHPCIState *pcic = p; - PCIHostState *phb = PCI_HOST_BRIDGE(pcic); - - switch(addr) { - case 0 ... 0xfc: - return le32_to_cpup((uint32_t*)(pcic->dev->config + addr)); - case 0x1c0: - return pcic->par; - case 0x1c4: - return pcic->mbr; - case 0x1c8: - return pcic->iobr; - case 0x220: - return pci_data_read(phb->bus, pcic->par, 4); - } - return 0; -} - -static const MemoryRegionOps sh_pci_reg_ops = { - .read = sh_pci_reg_read, - .write = sh_pci_reg_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static int sh_pci_map_irq(PCIDevice *d, int irq_num) -{ - return (d->devfn >> 3); -} - -static void sh_pci_set_irq(void *opaque, int irq_num, int level) -{ - qemu_irq *pic = opaque; - - qemu_set_irq(pic[irq_num], level); -} - -static int sh_pci_device_init(SysBusDevice *dev) -{ - PCIHostState *phb; - SHPCIState *s; - int i; - - s = SH_PCI_HOST_BRIDGE(dev); - phb = PCI_HOST_BRIDGE(s); - for (i = 0; i < 4; i++) { - sysbus_init_irq(dev, &s->irq[i]); - } - phb->bus = pci_register_bus(DEVICE(dev), "pci", - sh_pci_set_irq, sh_pci_map_irq, - s->irq, - get_system_memory(), - get_system_io(), - PCI_DEVFN(0, 0), 4, TYPE_PCI_BUS); - memory_region_init_io(&s->memconfig_p4, OBJECT(s), &sh_pci_reg_ops, s, - "sh_pci", 0x224); - memory_region_init_alias(&s->memconfig_a7, OBJECT(s), "sh_pci.2", - &s->memconfig_p4, 0, 0x224); - memory_region_init_alias(&s->isa, OBJECT(s), "sh_pci.isa", - get_system_io(), 0, 0x40000); - sysbus_init_mmio(dev, &s->memconfig_p4); - sysbus_init_mmio(dev, &s->memconfig_a7); - s->iobr = 0xfe240000; - memory_region_add_subregion(get_system_memory(), s->iobr, &s->isa); - - s->dev = pci_create_simple(phb->bus, PCI_DEVFN(0, 0), "sh_pci_host"); - return 0; -} - -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); -} - -static void sh_pci_host_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = sh_pci_host_realize; - k->vendor_id = PCI_VENDOR_ID_HITACHI; - k->device_id = PCI_DEVICE_ID_HITACHI_SH7751R; - /* - * PCI-facing part of the host bridge, not usable without the - * host-facing part, which can't be device_add'ed, yet. - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo sh_pci_host_info = { - .name = "sh_pci_host", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = sh_pci_host_class_init, -}; - -static void sh_pci_device_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = sh_pci_device_init; -} - -static const TypeInfo sh_pci_device_info = { - .name = TYPE_SH_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(SHPCIState), - .class_init = sh_pci_device_class_init, -}; - -static void sh_pci_register_types(void) -{ - type_register_static(&sh_pci_device_info); - type_register_static(&sh_pci_host_info); -} - -type_init(sh_pci_register_types) diff --git a/qemu/hw/sh4/shix.c b/qemu/hw/sh4/shix.c deleted file mode 100644 index ccc9e7589..000000000 --- a/qemu/hw/sh4/shix.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * SHIX 2.0 board description - * - * Copyright (c) 2005 Samuel Tardieu - * - * 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. - */ -/* - Shix 2.0 board by Alexis Polti, described at - http://perso.enst.fr/~polti/realisations/shix20/ - - 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" -#include "sysemu/qtest.h" -#include "hw/boards.h" -#include "hw/loader.h" -#include "exec/address-spaces.h" -#include "qemu/error-report.h" - -#define BIOS_FILENAME "shix_bios.bin" -#define BIOS_ADDRESS 0xA0000000 - -static void shix_init(MachineState *machine) -{ - const char *cpu_model = machine->cpu_model; - int ret; - SuperHCPU *cpu; - struct SH7750State *s; - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *rom = g_new(MemoryRegion, 1); - MemoryRegion *sdram = g_new(MemoryRegion, 2); - - if (!cpu_model) - cpu_model = "any"; - - cpu = cpu_sh4_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - - /* Allocate memory space */ - 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_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_fatal); - vmstate_register_ram_global(&sdram[1]); - memory_region_add_subregion(sysmem, 0x0c000000, &sdram[1]); - - /* Load BIOS in 0 (and access it through P2, 0xA0000000) */ - if (bios_name == NULL) - bios_name = BIOS_FILENAME; - ret = load_image_targphys(bios_name, 0, 0x4000); - if (ret < 0 && !qtest_enabled()) { - error_report("Could not load SHIX bios '%s'", bios_name); - exit(1); - } - - /* Register peripherals */ - s = sh7750_init(cpu, sysmem); - /* XXXXX Check success */ - tc58128_init(s, "shix_linux_nand.bin", NULL); -} - -static void shix_machine_init(MachineClass *mc) -{ - mc->desc = "shix card"; - mc->init = shix_init; - mc->is_default = 1; -} - -DEFINE_MACHINE("shix", shix_machine_init) diff --git a/qemu/hw/smbios/Makefile.objs b/qemu/hw/smbios/Makefile.objs deleted file mode 100644 index f69a92f96..000000000 --- a/qemu/hw/smbios/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -common-obj-$(CONFIG_SMBIOS) += smbios.o diff --git a/qemu/hw/smbios/smbios.c b/qemu/hw/smbios/smbios.c deleted file mode 100644 index cb8a11110..000000000 --- a/qemu/hw/smbios/smbios.c +++ /dev/null @@ -1,1112 +0,0 @@ -/* - * SMBIOS Support - * - * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. - * Copyright (C) 2013 Red Hat, Inc. - * - * Authors: - * Alex Williamson - * Markus Armbruster - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * 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/config-file.h" -#include "qemu/error-report.h" -#include "sysemu/sysemu.h" -#include "sysemu/cpus.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 { - uint16_t length; - uint8_t type; -} QEMU_PACKED; - -struct smbios_field { - struct smbios_header header; - uint8_t type; - uint16_t offset; - uint8_t data[]; -} QEMU_PACKED; - -struct smbios_table { - struct smbios_header header; - uint8_t data[]; -} QEMU_PACKED; - -#define SMBIOS_FIELD_ENTRY 0 -#define SMBIOS_TABLE_ENTRY 1 - -static uint8_t *smbios_entries; -static size_t smbios_entries_len; -static bool smbios_legacy = true; -static bool smbios_uuid_encoded = true; -/* end: legacy structures & constants for <= 2.0 machines */ - - -static uint8_t *smbios_tables; -static size_t smbios_tables_len; -static unsigned smbios_table_max; -static unsigned smbios_table_cnt; -static SmbiosEntryPointType smbios_ep_type = SMBIOS_ENTRY_POINT_21; - -static SmbiosEntryPoint ep; - -static int smbios_type4_count = 0; -static bool smbios_immutable; -static bool smbios_have_defaults; -static uint32_t smbios_cpuid_version, smbios_cpuid_features, smbios_smp_sockets; - -static DECLARE_BITMAP(have_binfile_bitmap, SMBIOS_MAX_TYPE+1); -static DECLARE_BITMAP(have_fields_bitmap, SMBIOS_MAX_TYPE+1); - -static struct { - const char *vendor, *version, *date; - bool have_major_minor, uefi; - uint8_t major, minor; -} type0; - -static struct { - const char *manufacturer, *product, *version, *serial, *sku, *family; - /* uuid is in qemu_uuid[] */ -} type1; - -static struct { - const char *manufacturer, *product, *version, *serial, *asset, *location; -} type2; - -static struct { - const char *manufacturer, *version, *serial, *asset, *sku; -} type3; - -static struct { - const char *sock_pfx, *manufacturer, *version, *serial, *asset, *part; -} type4; - -static struct { - const char *loc_pfx, *bank, *manufacturer, *serial, *asset, *part; - uint16_t speed; -} type17; - -static QemuOptsList qemu_smbios_opts = { - .name = "smbios", - .head = QTAILQ_HEAD_INITIALIZER(qemu_smbios_opts.head), - .desc = { - /* - * no elements => accept any params - * validation will happen later - */ - { /* end of list */ } - } -}; - -static const QemuOptDesc qemu_smbios_file_opts[] = { - { - .name = "file", - .type = QEMU_OPT_STRING, - .help = "binary file containing an SMBIOS element", - }, - { /* end of list */ } -}; - -static const QemuOptDesc qemu_smbios_type0_opts[] = { - { - .name = "type", - .type = QEMU_OPT_NUMBER, - .help = "SMBIOS element type", - },{ - .name = "vendor", - .type = QEMU_OPT_STRING, - .help = "vendor name", - },{ - .name = "version", - .type = QEMU_OPT_STRING, - .help = "version number", - },{ - .name = "date", - .type = QEMU_OPT_STRING, - .help = "release date", - },{ - .name = "release", - .type = QEMU_OPT_STRING, - .help = "revision number", - },{ - .name = "uefi", - .type = QEMU_OPT_BOOL, - .help = "uefi support", - }, - { /* end of list */ } -}; - -static const QemuOptDesc qemu_smbios_type1_opts[] = { - { - .name = "type", - .type = QEMU_OPT_NUMBER, - .help = "SMBIOS element type", - },{ - .name = "manufacturer", - .type = QEMU_OPT_STRING, - .help = "manufacturer name", - },{ - .name = "product", - .type = QEMU_OPT_STRING, - .help = "product name", - },{ - .name = "version", - .type = QEMU_OPT_STRING, - .help = "version number", - },{ - .name = "serial", - .type = QEMU_OPT_STRING, - .help = "serial number", - },{ - .name = "uuid", - .type = QEMU_OPT_STRING, - .help = "UUID", - },{ - .name = "sku", - .type = QEMU_OPT_STRING, - .help = "SKU number", - },{ - .name = "family", - .type = QEMU_OPT_STRING, - .help = "family name", - }, - { /* end of list */ } -}; - -static const QemuOptDesc qemu_smbios_type2_opts[] = { - { - .name = "type", - .type = QEMU_OPT_NUMBER, - .help = "SMBIOS element type", - },{ - .name = "manufacturer", - .type = QEMU_OPT_STRING, - .help = "manufacturer name", - },{ - .name = "product", - .type = QEMU_OPT_STRING, - .help = "product name", - },{ - .name = "version", - .type = QEMU_OPT_STRING, - .help = "version number", - },{ - .name = "serial", - .type = QEMU_OPT_STRING, - .help = "serial number", - },{ - .name = "asset", - .type = QEMU_OPT_STRING, - .help = "asset tag number", - },{ - .name = "location", - .type = QEMU_OPT_STRING, - .help = "location in chassis", - }, - { /* end of list */ } -}; - -static const QemuOptDesc qemu_smbios_type3_opts[] = { - { - .name = "type", - .type = QEMU_OPT_NUMBER, - .help = "SMBIOS element type", - },{ - .name = "manufacturer", - .type = QEMU_OPT_STRING, - .help = "manufacturer name", - },{ - .name = "version", - .type = QEMU_OPT_STRING, - .help = "version number", - },{ - .name = "serial", - .type = QEMU_OPT_STRING, - .help = "serial number", - },{ - .name = "asset", - .type = QEMU_OPT_STRING, - .help = "asset tag number", - },{ - .name = "sku", - .type = QEMU_OPT_STRING, - .help = "SKU number", - }, - { /* end of list */ } -}; - -static const QemuOptDesc qemu_smbios_type4_opts[] = { - { - .name = "type", - .type = QEMU_OPT_NUMBER, - .help = "SMBIOS element type", - },{ - .name = "sock_pfx", - .type = QEMU_OPT_STRING, - .help = "socket designation string prefix", - },{ - .name = "manufacturer", - .type = QEMU_OPT_STRING, - .help = "manufacturer name", - },{ - .name = "version", - .type = QEMU_OPT_STRING, - .help = "version number", - },{ - .name = "serial", - .type = QEMU_OPT_STRING, - .help = "serial number", - },{ - .name = "asset", - .type = QEMU_OPT_STRING, - .help = "asset tag number", - },{ - .name = "part", - .type = QEMU_OPT_STRING, - .help = "part number", - }, - { /* end of list */ } -}; - -static const QemuOptDesc qemu_smbios_type17_opts[] = { - { - .name = "type", - .type = QEMU_OPT_NUMBER, - .help = "SMBIOS element type", - },{ - .name = "loc_pfx", - .type = QEMU_OPT_STRING, - .help = "device locator string prefix", - },{ - .name = "bank", - .type = QEMU_OPT_STRING, - .help = "bank locator string", - },{ - .name = "manufacturer", - .type = QEMU_OPT_STRING, - .help = "manufacturer name", - },{ - .name = "serial", - .type = QEMU_OPT_STRING, - .help = "serial number", - },{ - .name = "asset", - .type = QEMU_OPT_STRING, - .help = "asset tag number", - },{ - .name = "part", - .type = QEMU_OPT_STRING, - .help = "part number", - },{ - .name = "speed", - .type = QEMU_OPT_NUMBER, - .help = "maximum capable speed", - }, - { /* end of list */ } -}; - -static void smbios_register_config(void) -{ - qemu_add_opts(&qemu_smbios_opts); -} - -opts_init(smbios_register_config); - -static void smbios_validate_table(void) -{ - uint32_t expect_t4_count = smbios_legacy ? smp_cpus : smbios_smp_sockets; - - if (smbios_type4_count && smbios_type4_count != expect_t4_count) { - error_report("Expected %d SMBIOS Type 4 tables, got %d instead", - expect_t4_count, smbios_type4_count); - exit(1); - } -} - - -/* legacy setup functions for <= 2.0 machines */ -static void smbios_add_field(int type, int offset, const void *data, size_t len) -{ - struct smbios_field *field; - - if (!smbios_entries) { - smbios_entries_len = sizeof(uint16_t); - smbios_entries = g_malloc0(smbios_entries_len); - } - smbios_entries = g_realloc(smbios_entries, smbios_entries_len + - sizeof(*field) + len); - field = (struct smbios_field *)(smbios_entries + smbios_entries_len); - field->header.type = SMBIOS_FIELD_ENTRY; - field->header.length = cpu_to_le16(sizeof(*field) + len); - - field->type = type; - field->offset = cpu_to_le16(offset); - memcpy(field->data, data, len); - - smbios_entries_len += sizeof(*field) + len; - (*(uint16_t *)smbios_entries) = - cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); -} - -static void smbios_maybe_add_str(int type, int offset, const char *data) -{ - if (data) { - smbios_add_field(type, offset, data, strlen(data) + 1); - } -} - -static void smbios_build_type_0_fields(void) -{ - smbios_maybe_add_str(0, offsetof(struct smbios_type_0, vendor_str), - type0.vendor); - smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_version_str), - type0.version); - smbios_maybe_add_str(0, offsetof(struct smbios_type_0, - bios_release_date_str), - type0.date); - if (type0.have_major_minor) { - smbios_add_field(0, offsetof(struct smbios_type_0, - system_bios_major_release), - &type0.major, 1); - smbios_add_field(0, offsetof(struct smbios_type_0, - system_bios_minor_release), - &type0.minor, 1); - } -} - -static void smbios_build_type_1_fields(void) -{ - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, manufacturer_str), - type1.manufacturer); - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, product_name_str), - type1.product); - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, version_str), - type1.version); - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, serial_number_str), - type1.serial); - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, sku_number_str), - type1.sku); - smbios_maybe_add_str(1, offsetof(struct smbios_type_1, family_str), - type1.family); - if (qemu_uuid_set) { - /* We don't encode the UUID in the "wire format" here because this - * function is for legacy mode and needs to keep the guest ABI, and - * because we don't know what's the SMBIOS version advertised by the - * BIOS. - */ - smbios_add_field(1, offsetof(struct smbios_type_1, uuid), - qemu_uuid, 16); - } -} - -uint8_t *smbios_get_table_legacy(size_t *length) -{ - if (!smbios_legacy) { - *length = 0; - return NULL; - } - - if (!smbios_immutable) { - smbios_build_type_0_fields(); - smbios_build_type_1_fields(); - smbios_validate_table(); - smbios_immutable = true; - } - *length = smbios_entries_len; - return smbios_entries; -} -/* end: legacy setup functions for <= 2.0 machines */ - - -static bool smbios_skip_table(uint8_t type, bool required_table) -{ - if (test_bit(type, have_binfile_bitmap)) { - return true; /* user provided their own binary blob(s) */ - } - if (test_bit(type, have_fields_bitmap)) { - return false; /* user provided fields via command line */ - } - if (smbios_have_defaults && required_table) { - return false; /* we're building tables, and this one's required */ - } - return true; -} - -#define SMBIOS_BUILD_TABLE_PRE(tbl_type, tbl_handle, tbl_required) \ - struct smbios_type_##tbl_type *t; \ - size_t t_off; /* table offset into smbios_tables */ \ - int str_index = 0; \ - do { \ - /* should we skip building this table ? */ \ - if (smbios_skip_table(tbl_type, tbl_required)) { \ - return; \ - } \ - \ - /* use offset of table t within smbios_tables */ \ - /* (pointer must be updated after each realloc) */ \ - t_off = smbios_tables_len; \ - smbios_tables_len += sizeof(*t); \ - smbios_tables = g_realloc(smbios_tables, smbios_tables_len); \ - t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off); \ - \ - t->header.type = tbl_type; \ - t->header.length = sizeof(*t); \ - t->header.handle = cpu_to_le16(tbl_handle); \ - } while (0) - -#define SMBIOS_TABLE_SET_STR(tbl_type, field, value) \ - do { \ - int len = (value != NULL) ? strlen(value) + 1 : 0; \ - if (len > 1) { \ - smbios_tables = g_realloc(smbios_tables, \ - smbios_tables_len + len); \ - memcpy(smbios_tables + smbios_tables_len, value, len); \ - smbios_tables_len += len; \ - /* update pointer post-realloc */ \ - t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off); \ - t->field = ++str_index; \ - } else { \ - t->field = 0; \ - } \ - } while (0) - -#define SMBIOS_BUILD_TABLE_POST \ - do { \ - size_t term_cnt, t_size; \ - \ - /* add '\0' terminator (add two if no strings defined) */ \ - term_cnt = (str_index == 0) ? 2 : 1; \ - smbios_tables = g_realloc(smbios_tables, \ - smbios_tables_len + term_cnt); \ - memset(smbios_tables + smbios_tables_len, 0, term_cnt); \ - smbios_tables_len += term_cnt; \ - \ - /* update smbios max. element size */ \ - t_size = smbios_tables_len - t_off; \ - if (t_size > smbios_table_max) { \ - smbios_table_max = t_size; \ - } \ - \ - /* update smbios element count */ \ - smbios_table_cnt++; \ - } while (0) - -static void smbios_build_type_0_table(void) -{ - SMBIOS_BUILD_TABLE_PRE(0, 0x000, false); /* optional, leave up to BIOS */ - - SMBIOS_TABLE_SET_STR(0, vendor_str, type0.vendor); - SMBIOS_TABLE_SET_STR(0, bios_version_str, type0.version); - - t->bios_starting_address_segment = cpu_to_le16(0xE800); /* from SeaBIOS */ - - SMBIOS_TABLE_SET_STR(0, bios_release_date_str, type0.date); - - t->bios_rom_size = 0; /* hardcoded in SeaBIOS with FIXME comment */ - - t->bios_characteristics = cpu_to_le64(0x08); /* Not supported */ - t->bios_characteristics_extension_bytes[0] = 0; - t->bios_characteristics_extension_bytes[1] = 0x14; /* TCD/SVVP | VM */ - if (type0.uefi) { - t->bios_characteristics_extension_bytes[1] |= 0x08; /* |= UEFI */ - } - - if (type0.have_major_minor) { - t->system_bios_major_release = type0.major; - t->system_bios_minor_release = type0.minor; - } else { - t->system_bios_major_release = 0; - t->system_bios_minor_release = 0; - } - - /* hardcoded in SeaBIOS */ - t->embedded_controller_major_release = 0xFF; - t->embedded_controller_minor_release = 0xFF; - - SMBIOS_BUILD_TABLE_POST; -} - -/* Encode UUID from the big endian encoding described on RFC4122 to the wire - * format specified by SMBIOS version 2.6. - */ -static void smbios_encode_uuid(struct smbios_uuid *uuid, const uint8_t *buf) -{ - memcpy(uuid, buf, 16); - if (smbios_uuid_encoded) { - uuid->time_low = bswap32(uuid->time_low); - uuid->time_mid = bswap16(uuid->time_mid); - uuid->time_hi_and_version = bswap16(uuid->time_hi_and_version); - } -} - -static void smbios_build_type_1_table(void) -{ - SMBIOS_BUILD_TABLE_PRE(1, 0x100, true); /* required */ - - SMBIOS_TABLE_SET_STR(1, manufacturer_str, type1.manufacturer); - SMBIOS_TABLE_SET_STR(1, product_name_str, type1.product); - SMBIOS_TABLE_SET_STR(1, version_str, type1.version); - SMBIOS_TABLE_SET_STR(1, serial_number_str, type1.serial); - if (qemu_uuid_set) { - smbios_encode_uuid(&t->uuid, qemu_uuid); - } else { - memset(&t->uuid, 0, 16); - } - t->wake_up_type = 0x06; /* power switch */ - SMBIOS_TABLE_SET_STR(1, sku_number_str, type1.sku); - SMBIOS_TABLE_SET_STR(1, family_str, type1.family); - - SMBIOS_BUILD_TABLE_POST; -} - -static void smbios_build_type_2_table(void) -{ - SMBIOS_BUILD_TABLE_PRE(2, 0x200, false); /* optional */ - - SMBIOS_TABLE_SET_STR(2, manufacturer_str, type2.manufacturer); - SMBIOS_TABLE_SET_STR(2, product_str, type2.product); - SMBIOS_TABLE_SET_STR(2, version_str, type2.version); - SMBIOS_TABLE_SET_STR(2, serial_number_str, type2.serial); - SMBIOS_TABLE_SET_STR(2, asset_tag_number_str, type2.asset); - t->feature_flags = 0x01; /* Motherboard */ - SMBIOS_TABLE_SET_STR(2, location_str, type2.location); - t->chassis_handle = cpu_to_le16(0x300); /* Type 3 (System enclosure) */ - t->board_type = 0x0A; /* Motherboard */ - t->contained_element_count = 0; - - SMBIOS_BUILD_TABLE_POST; -} - -static void smbios_build_type_3_table(void) -{ - SMBIOS_BUILD_TABLE_PRE(3, 0x300, true); /* required */ - - SMBIOS_TABLE_SET_STR(3, manufacturer_str, type3.manufacturer); - t->type = 0x01; /* Other */ - SMBIOS_TABLE_SET_STR(3, version_str, type3.version); - SMBIOS_TABLE_SET_STR(3, serial_number_str, type3.serial); - SMBIOS_TABLE_SET_STR(3, asset_tag_number_str, type3.asset); - t->boot_up_state = 0x03; /* Safe */ - t->power_supply_state = 0x03; /* Safe */ - t->thermal_state = 0x03; /* Safe */ - t->security_status = 0x02; /* Unknown */ - t->oem_defined = cpu_to_le32(0); - t->height = 0; - t->number_of_power_cords = 0; - t->contained_element_count = 0; - SMBIOS_TABLE_SET_STR(3, sku_number_str, type3.sku); - - SMBIOS_BUILD_TABLE_POST; -} - -static void smbios_build_type_4_table(unsigned instance) -{ - char sock_str[128]; - - SMBIOS_BUILD_TABLE_PRE(4, 0x400 + instance, true); /* required */ - - snprintf(sock_str, sizeof(sock_str), "%s%2x", type4.sock_pfx, instance); - SMBIOS_TABLE_SET_STR(4, socket_designation_str, sock_str); - t->processor_type = 0x03; /* CPU */ - t->processor_family = 0x01; /* Other */ - SMBIOS_TABLE_SET_STR(4, processor_manufacturer_str, type4.manufacturer); - t->processor_id[0] = cpu_to_le32(smbios_cpuid_version); - t->processor_id[1] = cpu_to_le32(smbios_cpuid_features); - SMBIOS_TABLE_SET_STR(4, processor_version_str, type4.version); - t->voltage = 0; - t->external_clock = cpu_to_le16(0); /* Unknown */ - /* SVVP requires max_speed and current_speed to not be unknown. */ - t->max_speed = cpu_to_le16(2000); /* 2000 MHz */ - t->current_speed = cpu_to_le16(2000); /* 2000 MHz */ - t->status = 0x41; /* Socket populated, CPU enabled */ - t->processor_upgrade = 0x01; /* Other */ - t->l1_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ - t->l2_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ - t->l3_cache_handle = cpu_to_le16(0xFFFF); /* N/A */ - SMBIOS_TABLE_SET_STR(4, serial_number_str, type4.serial); - SMBIOS_TABLE_SET_STR(4, asset_tag_number_str, type4.asset); - SMBIOS_TABLE_SET_STR(4, part_number_str, type4.part); - t->core_count = t->core_enabled = smp_cores; - t->thread_count = smp_threads; - t->processor_characteristics = cpu_to_le16(0x02); /* Unknown */ - t->processor_family2 = cpu_to_le16(0x01); /* Other */ - - SMBIOS_BUILD_TABLE_POST; - smbios_type4_count++; -} - -#define ONE_KB ((ram_addr_t)1 << 10) -#define ONE_MB ((ram_addr_t)1 << 20) -#define ONE_GB ((ram_addr_t)1 << 30) - -#define MAX_T16_STD_SZ 0x80000000 /* 2T in Kilobytes */ - -static void smbios_build_type_16_table(unsigned dimm_cnt) -{ - uint64_t size_kb; - - SMBIOS_BUILD_TABLE_PRE(16, 0x1000, true); /* required */ - - t->location = 0x01; /* Other */ - t->use = 0x03; /* System memory */ - t->error_correction = 0x06; /* Multi-bit ECC (for Microsoft, per SeaBIOS) */ - size_kb = QEMU_ALIGN_UP(ram_size, ONE_KB) / ONE_KB; - if (size_kb < MAX_T16_STD_SZ) { - t->maximum_capacity = cpu_to_le32(size_kb); - t->extended_maximum_capacity = cpu_to_le64(0); - } else { - t->maximum_capacity = cpu_to_le32(MAX_T16_STD_SZ); - t->extended_maximum_capacity = cpu_to_le64(ram_size); - } - t->memory_error_information_handle = cpu_to_le16(0xFFFE); /* Not provided */ - t->number_of_memory_devices = cpu_to_le16(dimm_cnt); - - SMBIOS_BUILD_TABLE_POST; -} - -#define MAX_T17_STD_SZ 0x7FFF /* (32G - 1M), in Megabytes */ -#define MAX_T17_EXT_SZ 0x80000000 /* 2P, in Megabytes */ - -static void smbios_build_type_17_table(unsigned instance, uint64_t size) -{ - char loc_str[128]; - uint64_t size_mb; - - SMBIOS_BUILD_TABLE_PRE(17, 0x1100 + instance, true); /* required */ - - t->physical_memory_array_handle = cpu_to_le16(0x1000); /* Type 16 above */ - t->memory_error_information_handle = cpu_to_le16(0xFFFE); /* Not provided */ - t->total_width = cpu_to_le16(0xFFFF); /* Unknown */ - t->data_width = cpu_to_le16(0xFFFF); /* Unknown */ - size_mb = QEMU_ALIGN_UP(size, ONE_MB) / ONE_MB; - if (size_mb < MAX_T17_STD_SZ) { - t->size = cpu_to_le16(size_mb); - t->extended_size = cpu_to_le32(0); - } else { - assert(size_mb < MAX_T17_EXT_SZ); - t->size = cpu_to_le16(MAX_T17_STD_SZ); - t->extended_size = cpu_to_le32(size_mb); - } - t->form_factor = 0x09; /* DIMM */ - t->device_set = 0; /* Not in a set */ - snprintf(loc_str, sizeof(loc_str), "%s %d", type17.loc_pfx, instance); - SMBIOS_TABLE_SET_STR(17, device_locator_str, loc_str); - SMBIOS_TABLE_SET_STR(17, bank_locator_str, type17.bank); - t->memory_type = 0x07; /* RAM */ - t->type_detail = cpu_to_le16(0x02); /* Other */ - t->speed = cpu_to_le16(type17.speed); - SMBIOS_TABLE_SET_STR(17, manufacturer_str, type17.manufacturer); - SMBIOS_TABLE_SET_STR(17, serial_number_str, type17.serial); - SMBIOS_TABLE_SET_STR(17, asset_tag_number_str, type17.asset); - SMBIOS_TABLE_SET_STR(17, part_number_str, type17.part); - t->attributes = 0; /* Unknown */ - t->configured_clock_speed = t->speed; /* reuse value for max speed */ - t->minimum_voltage = cpu_to_le16(0); /* Unknown */ - t->maximum_voltage = cpu_to_le16(0); /* Unknown */ - t->configured_voltage = cpu_to_le16(0); /* Unknown */ - - SMBIOS_BUILD_TABLE_POST; -} - -static void smbios_build_type_19_table(unsigned instance, - uint64_t start, uint64_t size) -{ - uint64_t end, start_kb, end_kb; - - SMBIOS_BUILD_TABLE_PRE(19, 0x1300 + instance, true); /* required */ - - end = start + size - 1; - assert(end > start); - start_kb = start / ONE_KB; - end_kb = end / ONE_KB; - if (start_kb < UINT32_MAX && end_kb < UINT32_MAX) { - t->starting_address = cpu_to_le32(start_kb); - t->ending_address = cpu_to_le32(end_kb); - t->extended_starting_address = - t->extended_ending_address = cpu_to_le64(0); - } else { - t->starting_address = t->ending_address = cpu_to_le32(UINT32_MAX); - t->extended_starting_address = cpu_to_le64(start); - t->extended_ending_address = cpu_to_le64(end); - } - t->memory_array_handle = cpu_to_le16(0x1000); /* Type 16 above */ - t->partition_width = 1; /* One device per row */ - - SMBIOS_BUILD_TABLE_POST; -} - -static void smbios_build_type_32_table(void) -{ - SMBIOS_BUILD_TABLE_PRE(32, 0x2000, true); /* required */ - - memset(t->reserved, 0, 6); - t->boot_status = 0; /* No errors detected */ - - SMBIOS_BUILD_TABLE_POST; -} - -static void smbios_build_type_127_table(void) -{ - SMBIOS_BUILD_TABLE_PRE(127, 0x7F00, true); /* required */ - SMBIOS_BUILD_TABLE_POST; -} - -void smbios_set_cpuid(uint32_t version, uint32_t features) -{ - smbios_cpuid_version = version; - smbios_cpuid_features = features; -} - -#define SMBIOS_SET_DEFAULT(field, value) \ - if (!field) { \ - field = value; \ - } - -void smbios_set_defaults(const char *manufacturer, const char *product, - const char *version, bool legacy_mode, - 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) { - g_free(smbios_tables); - /* in legacy mode, also complain if fields were given for types > 1 */ - if (find_next_bit(have_fields_bitmap, - SMBIOS_MAX_TYPE+1, 2) < SMBIOS_MAX_TYPE+1) { - error_report("can't process fields for smbios " - "types > 1 on machine versions < 2.1!"); - exit(1); - } - } else { - g_free(smbios_entries); - } - - SMBIOS_SET_DEFAULT(type1.manufacturer, manufacturer); - SMBIOS_SET_DEFAULT(type1.product, product); - SMBIOS_SET_DEFAULT(type1.version, version); - SMBIOS_SET_DEFAULT(type2.manufacturer, manufacturer); - SMBIOS_SET_DEFAULT(type2.product, product); - SMBIOS_SET_DEFAULT(type2.version, version); - SMBIOS_SET_DEFAULT(type3.manufacturer, manufacturer); - SMBIOS_SET_DEFAULT(type3.version, version); - SMBIOS_SET_DEFAULT(type4.sock_pfx, "CPU"); - SMBIOS_SET_DEFAULT(type4.manufacturer, manufacturer); - SMBIOS_SET_DEFAULT(type4.version, version); - SMBIOS_SET_DEFAULT(type17.loc_pfx, "DIMM"); - SMBIOS_SET_DEFAULT(type17.manufacturer, manufacturer); -} - -static void smbios_entry_point_setup(void) -{ - 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(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; - - if (smbios_legacy) { - *tables = *anchor = NULL; - *tables_len = *anchor_len = 0; - return; - } - - if (!smbios_immutable) { - smbios_build_type_0_table(); - smbios_build_type_1_table(); - smbios_build_type_2_table(); - smbios_build_type_3_table(); - - smbios_smp_sockets = DIV_ROUND_UP(smp_cpus, smp_cores * smp_threads); - assert(smbios_smp_sockets >= 1); - - for (i = 0; i < smbios_smp_sockets; i++) { - smbios_build_type_4_table(i); - } - -#define MAX_DIMM_SZ (16ll * ONE_GB) -#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ \ - : ((ram_size - 1) % MAX_DIMM_SZ) + 1) - - dimm_cnt = QEMU_ALIGN_UP(ram_size, MAX_DIMM_SZ) / MAX_DIMM_SZ; - - smbios_build_type_16_table(dimm_cnt); - - for (i = 0; i < dimm_cnt; i++) { - smbios_build_type_17_table(i, GET_DIMM_SZ); - } - - 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(); - smbios_build_type_127_table(); - - smbios_validate_table(); - smbios_entry_point_setup(); - smbios_immutable = true; - } - - /* return tables blob and entry point (anchor), and their sizes */ - *tables = smbios_tables; - *tables_len = smbios_tables_len; - *anchor = (uint8_t *)&ep; - - /* 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) -{ - const char *val = qemu_opt_get(opts, name); - - if (val) { - *dest = val; - } -} - -void smbios_entry_add(QemuOpts *opts) -{ - const char *val; - - assert(!smbios_immutable); - - val = qemu_opt_get(opts, "file"); - if (val) { - struct smbios_structure_header *header; - int size; - struct smbios_table *table; /* legacy mode only */ - - qemu_opts_validate(opts, qemu_smbios_file_opts, &error_fatal); - - size = get_image_size(val); - if (size == -1 || size < sizeof(struct smbios_structure_header)) { - error_report("Cannot read SMBIOS file %s", val); - exit(1); - } - - /* - * NOTE: standard double '\0' terminator expected, per smbios spec. - * (except in legacy mode, where the second '\0' is implicit and - * will be inserted by the BIOS). - */ - smbios_tables = g_realloc(smbios_tables, smbios_tables_len + size); - header = (struct smbios_structure_header *)(smbios_tables + - smbios_tables_len); - - if (load_image(val, (uint8_t *)header) != size) { - error_report("Failed to load SMBIOS file %s", val); - exit(1); - } - - if (test_bit(header->type, have_fields_bitmap)) { - error_report("can't load type %d struct, fields already specified!", - header->type); - exit(1); - } - set_bit(header->type, have_binfile_bitmap); - - if (header->type == 4) { - smbios_type4_count++; - } - - smbios_tables_len += size; - if (size > smbios_table_max) { - smbios_table_max = size; - } - smbios_table_cnt++; - - /* add a copy of the newly loaded blob to legacy smbios_entries */ - /* NOTE: This code runs before smbios_set_defaults(), so we don't - * yet know which mode (legacy vs. aggregate-table) will be - * required. We therefore add the binary blob to both legacy - * (smbios_entries) and aggregate (smbios_tables) tables, and - * delete the one we don't need from smbios_set_defaults(), - * once we know which machine version has been requested. - */ - if (!smbios_entries) { - smbios_entries_len = sizeof(uint16_t); - smbios_entries = g_malloc0(smbios_entries_len); - } - smbios_entries = g_realloc(smbios_entries, smbios_entries_len + - size + sizeof(*table)); - table = (struct smbios_table *)(smbios_entries + smbios_entries_len); - table->header.type = SMBIOS_TABLE_ENTRY; - table->header.length = cpu_to_le16(sizeof(*table) + size); - memcpy(table->data, header, size); - smbios_entries_len += sizeof(*table) + size; - (*(uint16_t *)smbios_entries) = - cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1); - /* end: add a copy of the newly loaded blob to legacy smbios_entries */ - - return; - } - - val = qemu_opt_get(opts, "type"); - if (val) { - unsigned long type = strtoul(val, NULL, 0); - - if (type > SMBIOS_MAX_TYPE) { - error_report("out of range!"); - exit(1); - } - - if (test_bit(type, have_binfile_bitmap)) { - error_report("can't add fields, binary file already loaded!"); - exit(1); - } - set_bit(type, have_fields_bitmap); - - switch (type) { - case 0: - 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"); - type0.uefi = qemu_opt_get_bool(opts, "uefi", false); - - val = qemu_opt_get(opts, "release"); - if (val) { - if (sscanf(val, "%hhu.%hhu", &type0.major, &type0.minor) != 2) { - error_report("Invalid release"); - exit(1); - } - type0.have_major_minor = true; - } - return; - case 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"); - save_opt(&type1.serial, opts, "serial"); - save_opt(&type1.sku, opts, "sku"); - save_opt(&type1.family, opts, "family"); - - val = qemu_opt_get(opts, "uuid"); - if (val) { - if (qemu_uuid_parse(val, qemu_uuid) != 0) { - error_report("Invalid UUID"); - exit(1); - } - qemu_uuid_set = true; - } - return; - case 2: - 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"); - save_opt(&type2.serial, opts, "serial"); - save_opt(&type2.asset, opts, "asset"); - save_opt(&type2.location, opts, "location"); - return; - case 3: - 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"); - save_opt(&type3.asset, opts, "asset"); - save_opt(&type3.sku, opts, "sku"); - return; - case 4: - 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"); - save_opt(&type4.serial, opts, "serial"); - save_opt(&type4.asset, opts, "asset"); - save_opt(&type4.part, opts, "part"); - return; - case 17: - 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"); - save_opt(&type17.serial, opts, "serial"); - save_opt(&type17.asset, opts, "asset"); - save_opt(&type17.part, opts, "part"); - type17.speed = qemu_opt_get_number(opts, "speed", 0); - return; - default: - error_report("Don't know how to build fields for SMBIOS type %ld", - type); - exit(1); - } - } - - error_report("Must specify type= or file="); - exit(1); -} diff --git a/qemu/hw/sparc/Makefile.objs b/qemu/hw/sparc/Makefile.objs deleted file mode 100644 index c987b5b5d..000000000 --- a/qemu/hw/sparc/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -obj-y += sun4m.o leon3.o diff --git a/qemu/hw/sparc/leon3.c b/qemu/hw/sparc/leon3.c deleted file mode 100644 index dbae41f3a..000000000 --- a/qemu/hw/sparc/leon3.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * QEMU Leon3 System Emulator - * - * Copyright (c) 2010-2011 AdaCore - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/hw.h" -#include "qemu/timer.h" -#include "hw/ptimer.h" -#include "sysemu/char.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "hw/boards.h" -#include "hw/loader.h" -#include "elf.h" -#include "trace.h" -#include "exec/address-spaces.h" - -#include "hw/sparc/grlib.h" - -/* Default system clock. */ -#define CPU_CLK (40 * 1000 * 1000) - -#define PROM_FILENAME "u-boot.bin" - -#define MAX_PILS 16 - -typedef struct ResetData { - SPARCCPU *cpu; - uint32_t entry; /* save kernel entry in case of reset */ - target_ulong sp; /* initial stack pointer */ -} ResetData; - -static void main_cpu_reset(void *opaque) -{ - ResetData *s = (ResetData *)opaque; - CPUState *cpu = CPU(s->cpu); - CPUSPARCState *env = &s->cpu->env; - - cpu_reset(cpu); - - cpu->halted = 0; - env->pc = s->entry; - env->npc = s->entry + 4; - env->regbase[6] = s->sp; -} - -void leon3_irq_ack(void *irq_manager, int intno) -{ - grlib_irqmp_ack((DeviceState *)irq_manager, intno); -} - -static void leon3_set_pil_in(void *opaque, uint32_t pil_in) -{ - CPUSPARCState *env = (CPUSPARCState *)opaque; - CPUState *cs; - - assert(env != NULL); - - env->pil_in = pil_in; - - if (env->pil_in && (env->interrupt_index == 0 || - (env->interrupt_index & ~15) == TT_EXTINT)) { - unsigned int i; - - for (i = 15; i > 0; i--) { - if (env->pil_in & (1 << i)) { - int old_interrupt = env->interrupt_index; - - env->interrupt_index = TT_EXTINT | i; - if (old_interrupt != env->interrupt_index) { - cs = CPU(sparc_env_get_cpu(env)); - trace_leon3_set_irq(i); - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } - break; - } - } - } else if (!env->pil_in && (env->interrupt_index & ~15) == TT_EXTINT) { - cs = CPU(sparc_env_get_cpu(env)); - trace_leon3_reset_irq(env->interrupt_index & 15); - env->interrupt_index = 0; - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } -} - -static void leon3_generic_hw_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; - SPARCCPU *cpu; - CPUSPARCState *env; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *prom = g_new(MemoryRegion, 1); - int ret; - char *filename; - qemu_irq *cpu_irqs = NULL; - int bios_size; - int prom_size; - ResetData *reset_info; - - /* Init CPU */ - if (!cpu_model) { - cpu_model = "LEON3"; - } - - cpu = cpu_sparc_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n"); - exit(1); - } - env = &cpu->env; - - cpu_sparc_set_id(env, 0); - - /* Reset data */ - reset_info = g_malloc0(sizeof(ResetData)); - reset_info->cpu = cpu; - reset_info->sp = 0x40000000 + ram_size; - qemu_register_reset(main_cpu_reset, reset_info); - - /* Allocate IRQ manager */ - grlib_irqmp_create(0x80000200, env, &cpu_irqs, MAX_PILS, &leon3_set_pil_in); - - env->qemu_irq_ack = leon3_irq_manager; - - /* Allocate RAM */ - if ((uint64_t)ram_size > (1UL << 30)) { - fprintf(stderr, - "qemu: Too much memory for this machine: %d, maximum 1G\n", - (unsigned int)(ram_size / (1024 * 1024))); - exit(1); - } - - memory_region_allocate_system_memory(ram, NULL, "leon3.ram", ram_size); - memory_region_add_subregion(address_space_mem, 0x40000000, ram); - - /* Allocate BIOS */ - prom_size = 8 * 1024 * 1024; /* 8Mb */ - 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); - - /* Load boot prom */ - if (bios_name == NULL) { - bios_name = PROM_FILENAME; - } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - - bios_size = get_image_size(filename); - - if (bios_size > prom_size) { - fprintf(stderr, "qemu: could not load prom '%s': file too big\n", - filename); - exit(1); - } - - if (bios_size > 0) { - ret = load_image_targphys(filename, 0x00000000, bios_size); - if (ret < 0 || ret > prom_size) { - fprintf(stderr, "qemu: could not load prom '%s'\n", filename); - exit(1); - } - } else if (kernel_filename == NULL && !qtest_enabled()) { - fprintf(stderr, "Can't read bios image %s\n", filename); - exit(1); - } - g_free(filename); - - /* Can directly load an application. */ - if (kernel_filename != NULL) { - long kernel_size; - uint64_t entry; - - kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, - 1 /* big endian */, EM_SPARC, 0, 0); - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); - exit(1); - } - if (bios_size <= 0) { - /* If there is no bios/monitor, start the application. */ - env->pc = entry; - env->npc = entry + 4; - reset_info->entry = entry; - } - } - - /* Allocate timers */ - grlib_gptimer_create(0x80000300, 2, CPU_CLK, cpu_irqs, 6); - - /* Allocate uart */ - if (serial_hds[0]) { - grlib_apbuart_create(0x80000100, serial_hds[0], cpu_irqs[3]); - } -} - -static void leon3_generic_machine_init(MachineClass *mc) -{ - mc->desc = "Leon-3 generic"; - mc->init = leon3_generic_hw_init; -} - -DEFINE_MACHINE("leon3_generic", leon3_generic_machine_init) diff --git a/qemu/hw/sparc/sun4m.c b/qemu/hw/sparc/sun4m.c deleted file mode 100644 index 7bfc00abc..000000000 --- a/qemu/hw/sparc/sun4m.c +++ /dev/null @@ -1,1572 +0,0 @@ -/* - * QEMU Sun4m & Sun4d & Sun4c System Emulator - * - * Copyright (c) 2003-2005 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/sysbus.h" -#include "qemu/error-report.h" -#include "qemu/timer.h" -#include "hw/sparc/sun4m.h" -#include "hw/timer/m48t59.h" -#include "hw/sparc/sparc32_dma.h" -#include "hw/block/fdc.h" -#include "sysemu/sysemu.h" -#include "net/net.h" -#include "hw/boards.h" -#include "hw/nvram/openbios_firmware_abi.h" -#include "hw/scsi/esp.h" -#include "hw/i386/pc.h" -#include "hw/isa/isa.h" -#include "hw/nvram/fw_cfg.h" -#include "hw/char/escc.h" -#include "hw/empty_slot.h" -#include "hw/loader.h" -#include "elf.h" -#include "sysemu/block-backend.h" -#include "trace.h" -#include "qemu/cutils.h" - -/* - * Sun4m architecture was used in the following machines: - * - * SPARCserver 6xxMP/xx - * SPARCclassic (SPARCclassic Server)(SPARCstation LC) (4/15), - * SPARCclassic X (4/10) - * SPARCstation LX/ZX (4/30) - * SPARCstation Voyager - * SPARCstation 10/xx, SPARCserver 10/xx - * SPARCstation 5, SPARCserver 5 - * SPARCstation 20/xx, SPARCserver 20 - * SPARCstation 4 - * - * See for example: http://www.sunhelp.org/faq/sunref1.html - */ - -#define KERNEL_LOAD_ADDR 0x00004000 -#define CMDLINE_ADDR 0x007ff000 -#define INITRD_LOAD_ADDR 0x00800000 -#define PROM_SIZE_MAX (1024 * 1024) -#define PROM_VADDR 0xffd00000 -#define PROM_FILENAME "openbios-sparc32" -#define CFG_ADDR 0xd00000510ULL -#define FW_CFG_SUN4M_DEPTH (FW_CFG_ARCH_LOCAL + 0x00) -#define FW_CFG_SUN4M_WIDTH (FW_CFG_ARCH_LOCAL + 0x01) -#define FW_CFG_SUN4M_HEIGHT (FW_CFG_ARCH_LOCAL + 0x02) - -#define MAX_CPUS 16 -#define MAX_PILS 16 -#define MAX_VSIMMS 4 - -#define ESCC_CLOCK 4915200 - -struct sun4m_hwdef { - hwaddr iommu_base, iommu_pad_base, iommu_pad_len, slavio_base; - hwaddr intctl_base, counter_base, nvram_base, ms_kb_base; - hwaddr serial_base, fd_base; - hwaddr afx_base, idreg_base, dma_base, esp_base, le_base; - hwaddr tcx_base, cs_base, apc_base, aux1_base, aux2_base; - hwaddr bpp_base, dbri_base, sx_base; - struct { - hwaddr reg_base, vram_base; - } vsimm[MAX_VSIMMS]; - hwaddr ecc_base; - uint64_t max_mem; - const char * const default_cpu_model; - uint32_t ecc_version; - uint32_t iommu_version; - uint16_t machine_id; - uint8_t nvram_machine_id; -}; - -void DMA_init(ISABus *bus, int high_page_enable) -{ -} - -static void fw_cfg_boot_set(void *opaque, const char *boot_device, - Error **errp) -{ - fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); -} - -static void nvram_init(Nvram *nvram, uint8_t *macaddr, - const char *cmdline, const char *boot_devices, - ram_addr_t RAM_size, uint32_t kernel_size, - int width, int height, int depth, - int nvram_machine_id, const char *arch) -{ - unsigned int i; - uint32_t start, end; - uint8_t image[0x1ff0]; - struct OpenBIOS_nvpart_v1 *part_header; - NvramClass *k = NVRAM_GET_CLASS(nvram); - - memset(image, '\0', sizeof(image)); - - start = 0; - - // OpenBIOS nvram variables - // Variable partition - part_header = (struct OpenBIOS_nvpart_v1 *)&image[start]; - part_header->signature = OPENBIOS_PART_SYSTEM; - pstrcpy(part_header->name, sizeof(part_header->name), "system"); - - end = start + sizeof(struct OpenBIOS_nvpart_v1); - for (i = 0; i < nb_prom_envs; i++) - end = OpenBIOS_set_var(image, end, prom_envs[i]); - - // End marker - image[end++] = '\0'; - - end = start + ((end - start + 15) & ~15); - OpenBIOS_finish_partition(part_header, end - start); - - // free partition - start = end; - part_header = (struct OpenBIOS_nvpart_v1 *)&image[start]; - part_header->signature = OPENBIOS_PART_FREE; - pstrcpy(part_header->name, sizeof(part_header->name), "free"); - - end = 0x1fd0; - OpenBIOS_finish_partition(part_header, end - start); - - Sun_init_header((struct Sun_nvram *)&image[0x1fd8], macaddr, - nvram_machine_id); - - for (i = 0; i < sizeof(image); i++) { - (k->write)(nvram, i, image[i]); - } -} - -static DeviceState *slavio_intctl; - -void sun4m_hmp_info_pic(Monitor *mon, const QDict *qdict) -{ - if (slavio_intctl) - slavio_pic_info(mon, slavio_intctl); -} - -void sun4m_hmp_info_irq(Monitor *mon, const QDict *qdict) -{ - if (slavio_intctl) - slavio_irq_info(mon, slavio_intctl); -} - -void cpu_check_irqs(CPUSPARCState *env) -{ - CPUState *cs; - - if (env->pil_in && (env->interrupt_index == 0 || - (env->interrupt_index & ~15) == TT_EXTINT)) { - unsigned int i; - - for (i = 15; i > 0; i--) { - if (env->pil_in & (1 << i)) { - int old_interrupt = env->interrupt_index; - - env->interrupt_index = TT_EXTINT | i; - if (old_interrupt != env->interrupt_index) { - cs = CPU(sparc_env_get_cpu(env)); - trace_sun4m_cpu_interrupt(i); - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } - break; - } - } - } else if (!env->pil_in && (env->interrupt_index & ~15) == TT_EXTINT) { - cs = CPU(sparc_env_get_cpu(env)); - trace_sun4m_cpu_reset_interrupt(env->interrupt_index & 15); - env->interrupt_index = 0; - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } -} - -static void cpu_kick_irq(SPARCCPU *cpu) -{ - CPUSPARCState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - cs->halted = 0; - cpu_check_irqs(env); - qemu_cpu_kick(cs); -} - -static void cpu_set_irq(void *opaque, int irq, int level) -{ - SPARCCPU *cpu = opaque; - CPUSPARCState *env = &cpu->env; - - if (level) { - trace_sun4m_cpu_set_irq_raise(irq); - env->pil_in |= 1 << irq; - cpu_kick_irq(cpu); - } else { - trace_sun4m_cpu_set_irq_lower(irq); - env->pil_in &= ~(1 << irq); - cpu_check_irqs(env); - } -} - -static void dummy_cpu_set_irq(void *opaque, int irq, int level) -{ -} - -static void main_cpu_reset(void *opaque) -{ - SPARCCPU *cpu = opaque; - CPUState *cs = CPU(cpu); - - cpu_reset(cs); - cs->halted = 0; -} - -static void secondary_cpu_reset(void *opaque) -{ - SPARCCPU *cpu = opaque; - CPUState *cs = CPU(cpu); - - cpu_reset(cs); - cs->halted = 1; -} - -static void cpu_halt_signal(void *opaque, int irq, int level) -{ - if (level && current_cpu) { - cpu_interrupt(current_cpu, CPU_INTERRUPT_HALT); - } -} - -static uint64_t translate_kernel_address(void *opaque, uint64_t addr) -{ - return addr - 0xf0000000ULL; -} - -static unsigned long sun4m_load_kernel(const char *kernel_filename, - const char *initrd_filename, - ram_addr_t RAM_size) -{ - int linux_boot; - unsigned int i; - long initrd_size, kernel_size; - uint8_t *ptr; - - linux_boot = (kernel_filename != NULL); - - kernel_size = 0; - if (linux_boot) { - int bswap_needed; - -#ifdef BSWAP_NEEDED - bswap_needed = 1; -#else - bswap_needed = 0; -#endif - kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - 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, - TARGET_PAGE_SIZE); - if (kernel_size < 0) - kernel_size = load_image_targphys(kernel_filename, - KERNEL_LOAD_ADDR, - RAM_size - KERNEL_LOAD_ADDR); - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); - exit(1); - } - - /* load initrd */ - initrd_size = 0; - if (initrd_filename) { - initrd_size = load_image_targphys(initrd_filename, - INITRD_LOAD_ADDR, - RAM_size - INITRD_LOAD_ADDR); - if (initrd_size < 0) { - fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", - initrd_filename); - exit(1); - } - } - if (initrd_size > 0) { - for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) { - ptr = rom_ptr(KERNEL_LOAD_ADDR + i); - if (ldl_p(ptr) == 0x48647253) { // HdrS - stl_p(ptr + 16, INITRD_LOAD_ADDR); - stl_p(ptr + 20, initrd_size); - break; - } - } - } - } - return kernel_size; -} - -static void *iommu_init(hwaddr addr, uint32_t version, qemu_irq irq) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_create(NULL, "iommu"); - qdev_prop_set_uint32(dev, "version", version); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, irq); - sysbus_mmio_map(s, 0, addr); - - return s; -} - -static void *sparc32_dma_init(hwaddr daddr, qemu_irq parent_irq, - void *iommu, qemu_irq *dev_irq, int is_ledma) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_create(NULL, "sparc32_dma"); - qdev_prop_set_ptr(dev, "iommu_opaque", iommu); - qdev_prop_set_uint32(dev, "is_ledma", is_ledma); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, parent_irq); - *dev_irq = qdev_get_gpio_in(dev, 0); - sysbus_mmio_map(s, 0, daddr); - - return s; -} - -static void lance_init(NICInfo *nd, hwaddr leaddr, - void *dma_opaque, qemu_irq irq) -{ - DeviceState *dev; - SysBusDevice *s; - qemu_irq reset; - - qemu_check_nic_model(&nd_table[0], "lance"); - - dev = qdev_create(NULL, "lance"); - qdev_set_nic_properties(dev, nd); - qdev_prop_set_ptr(dev, "dma", dma_opaque); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(s, 0, leaddr); - sysbus_connect_irq(s, 0, irq); - reset = qdev_get_gpio_in(dev, 0); - qdev_connect_gpio_out(dma_opaque, 0, reset); -} - -static DeviceState *slavio_intctl_init(hwaddr addr, - hwaddr addrg, - qemu_irq **parent_irq) -{ - DeviceState *dev; - SysBusDevice *s; - unsigned int i, j; - - dev = qdev_create(NULL, "slavio_intctl"); - qdev_init_nofail(dev); - - s = SYS_BUS_DEVICE(dev); - - for (i = 0; i < MAX_CPUS; i++) { - for (j = 0; j < MAX_PILS; j++) { - sysbus_connect_irq(s, i * MAX_PILS + j, parent_irq[i][j]); - } - } - sysbus_mmio_map(s, 0, addrg); - for (i = 0; i < MAX_CPUS; i++) { - sysbus_mmio_map(s, i + 1, addr + i * TARGET_PAGE_SIZE); - } - - return dev; -} - -#define SYS_TIMER_OFFSET 0x10000ULL -#define CPU_TIMER_OFFSET(cpu) (0x1000ULL * cpu) - -static void slavio_timer_init_all(hwaddr addr, qemu_irq master_irq, - qemu_irq *cpu_irqs, unsigned int num_cpus) -{ - DeviceState *dev; - SysBusDevice *s; - unsigned int i; - - dev = qdev_create(NULL, "slavio_timer"); - qdev_prop_set_uint32(dev, "num_cpus", num_cpus); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, master_irq); - sysbus_mmio_map(s, 0, addr + SYS_TIMER_OFFSET); - - for (i = 0; i < MAX_CPUS; i++) { - sysbus_mmio_map(s, i + 1, addr + (hwaddr)CPU_TIMER_OFFSET(i)); - sysbus_connect_irq(s, i + 1, cpu_irqs[i]); - } -} - -static qemu_irq slavio_system_powerdown; - -static void slavio_powerdown_req(Notifier *n, void *opaque) -{ - qemu_irq_raise(slavio_system_powerdown); -} - -static Notifier slavio_system_powerdown_notifier = { - .notify = slavio_powerdown_req -}; - -#define MISC_LEDS 0x01600000 -#define MISC_CFG 0x01800000 -#define MISC_DIAG 0x01a00000 -#define MISC_MDM 0x01b00000 -#define MISC_SYS 0x01f00000 - -static void slavio_misc_init(hwaddr base, - hwaddr aux1_base, - hwaddr aux2_base, qemu_irq irq, - qemu_irq fdc_tc) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_create(NULL, "slavio_misc"); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - if (base) { - /* 8 bit registers */ - /* Slavio control */ - sysbus_mmio_map(s, 0, base + MISC_CFG); - /* Diagnostics */ - sysbus_mmio_map(s, 1, base + MISC_DIAG); - /* Modem control */ - sysbus_mmio_map(s, 2, base + MISC_MDM); - /* 16 bit registers */ - /* ss600mp diag LEDs */ - sysbus_mmio_map(s, 3, base + MISC_LEDS); - /* 32 bit registers */ - /* System control */ - sysbus_mmio_map(s, 4, base + MISC_SYS); - } - if (aux1_base) { - /* AUX 1 (Misc System Functions) */ - sysbus_mmio_map(s, 5, aux1_base); - } - if (aux2_base) { - /* AUX 2 (Software Powerdown Control) */ - sysbus_mmio_map(s, 6, aux2_base); - } - sysbus_connect_irq(s, 0, irq); - sysbus_connect_irq(s, 1, fdc_tc); - slavio_system_powerdown = qdev_get_gpio_in(dev, 0); - qemu_register_powerdown_notifier(&slavio_system_powerdown_notifier); -} - -static void ecc_init(hwaddr base, qemu_irq irq, uint32_t version) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_create(NULL, "eccmemctl"); - qdev_prop_set_uint32(dev, "version", version); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, irq); - sysbus_mmio_map(s, 0, base); - if (version == 0) { // SS-600MP only - sysbus_mmio_map(s, 1, base + 0x1000); - } -} - -static void apc_init(hwaddr power_base, qemu_irq cpu_halt) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_create(NULL, "apc"); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - /* Power management (APC) XXX: not a Slavio device */ - sysbus_mmio_map(s, 0, power_base); - sysbus_connect_irq(s, 0, cpu_halt); -} - -static void tcx_init(hwaddr addr, qemu_irq irq, int vram_size, int width, - int height, int depth) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_create(NULL, "SUNW,tcx"); - qdev_prop_set_uint32(dev, "vram_size", vram_size); - qdev_prop_set_uint16(dev, "width", width); - qdev_prop_set_uint16(dev, "height", height); - qdev_prop_set_uint16(dev, "depth", depth); - qdev_prop_set_uint64(dev, "prom_addr", addr); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - - /* 10/ROM : FCode ROM */ - sysbus_mmio_map(s, 0, addr); - /* 2/STIP : Stipple */ - sysbus_mmio_map(s, 1, addr + 0x04000000ULL); - /* 3/BLIT : Blitter */ - sysbus_mmio_map(s, 2, addr + 0x06000000ULL); - /* 5/RSTIP : Raw Stipple */ - sysbus_mmio_map(s, 3, addr + 0x0c000000ULL); - /* 6/RBLIT : Raw Blitter */ - sysbus_mmio_map(s, 4, addr + 0x0e000000ULL); - /* 7/TEC : Transform Engine */ - sysbus_mmio_map(s, 5, addr + 0x00700000ULL); - /* 8/CMAP : DAC */ - sysbus_mmio_map(s, 6, addr + 0x00200000ULL); - /* 9/THC : */ - if (depth == 8) { - sysbus_mmio_map(s, 7, addr + 0x00300000ULL); - } else { - sysbus_mmio_map(s, 7, addr + 0x00301000ULL); - } - /* 11/DHC : */ - sysbus_mmio_map(s, 8, addr + 0x00240000ULL); - /* 12/ALT : */ - sysbus_mmio_map(s, 9, addr + 0x00280000ULL); - /* 0/DFB8 : 8-bit plane */ - sysbus_mmio_map(s, 10, addr + 0x00800000ULL); - /* 1/DFB24 : 24bit plane */ - sysbus_mmio_map(s, 11, addr + 0x02000000ULL); - /* 4/RDFB32: Raw framebuffer. Control plane */ - sysbus_mmio_map(s, 12, addr + 0x0a000000ULL); - /* 9/THC24bits : NetBSD writes here even with 8-bit display: dummy */ - if (depth == 8) { - sysbus_mmio_map(s, 13, addr + 0x00301000ULL); - } - - sysbus_connect_irq(s, 0, irq); -} - -static void cg3_init(hwaddr addr, qemu_irq irq, int vram_size, int width, - int height, int depth) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_create(NULL, "cgthree"); - qdev_prop_set_uint32(dev, "vram-size", vram_size); - qdev_prop_set_uint16(dev, "width", width); - qdev_prop_set_uint16(dev, "height", height); - qdev_prop_set_uint16(dev, "depth", depth); - qdev_prop_set_uint64(dev, "prom-addr", addr); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - - /* FCode ROM */ - sysbus_mmio_map(s, 0, addr); - /* DAC */ - sysbus_mmio_map(s, 1, addr + 0x400000ULL); - /* 8-bit plane */ - sysbus_mmio_map(s, 2, addr + 0x800000ULL); - - sysbus_connect_irq(s, 0, irq); -} - -/* NCR89C100/MACIO Internal ID register */ - -#define TYPE_MACIO_ID_REGISTER "macio_idreg" - -static const uint8_t idreg_data[] = { 0xfe, 0x81, 0x01, 0x03 }; - -static void idreg_init(hwaddr addr) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_create(NULL, TYPE_MACIO_ID_REGISTER); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - - sysbus_mmio_map(s, 0, addr); - cpu_physical_memory_write_rom(&address_space_memory, - addr, idreg_data, sizeof(idreg_data)); -} - -#define MACIO_ID_REGISTER(obj) \ - OBJECT_CHECK(IDRegState, (obj), TYPE_MACIO_ID_REGISTER) - -typedef struct IDRegState { - SysBusDevice parent_obj; - - MemoryRegion mem; -} IDRegState; - -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_fatal); - vmstate_register_ram_global(&s->mem); - memory_region_set_readonly(&s->mem, true); - sysbus_init_mmio(dev, &s->mem); - return 0; -} - -static void idreg_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = idreg_init1; -} - -static const TypeInfo idreg_info = { - .name = TYPE_MACIO_ID_REGISTER, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IDRegState), - .class_init = idreg_class_init, -}; - -#define TYPE_TCX_AFX "tcx_afx" -#define TCX_AFX(obj) OBJECT_CHECK(AFXState, (obj), TYPE_TCX_AFX) - -typedef struct AFXState { - SysBusDevice parent_obj; - - MemoryRegion mem; -} AFXState; - -/* SS-5 TCX AFX register */ -static void afx_init(hwaddr addr) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_create(NULL, TYPE_TCX_AFX); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - - sysbus_mmio_map(s, 0, addr); -} - -static int afx_init1(SysBusDevice *dev) -{ - AFXState *s = TCX_AFX(dev); - - 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; -} - -static void afx_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = afx_init1; -} - -static const TypeInfo afx_info = { - .name = TYPE_TCX_AFX, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(AFXState), - .class_init = afx_class_init, -}; - -#define TYPE_OPENPROM "openprom" -#define OPENPROM(obj) OBJECT_CHECK(PROMState, (obj), TYPE_OPENPROM) - -typedef struct PROMState { - SysBusDevice parent_obj; - - MemoryRegion prom; -} PROMState; - -/* Boot PROM (OpenBIOS) */ -static uint64_t translate_prom_address(void *opaque, uint64_t addr) -{ - hwaddr *base_addr = (hwaddr *)opaque; - return addr + *base_addr - PROM_VADDR; -} - -static void prom_init(hwaddr addr, const char *bios_name) -{ - DeviceState *dev; - SysBusDevice *s; - char *filename; - int ret; - - dev = qdev_create(NULL, TYPE_OPENPROM); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - - sysbus_mmio_map(s, 0, addr); - - /* load boot prom */ - if (bios_name == NULL) { - bios_name = PROM_FILENAME; - } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - ret = load_elf(filename, translate_prom_address, &addr, NULL, - NULL, NULL, 1, EM_SPARC, 0, 0); - if (ret < 0 || ret > PROM_SIZE_MAX) { - ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); - } - g_free(filename); - } else { - ret = -1; - } - if (ret < 0 || ret > PROM_SIZE_MAX) { - fprintf(stderr, "qemu: could not load prom '%s'\n", bios_name); - exit(1); - } -} - -static int prom_init1(SysBusDevice *dev) -{ - PROMState *s = OPENPROM(dev); - - memory_region_init_ram(&s->prom, OBJECT(s), "sun4m.prom", PROM_SIZE_MAX, - &error_fatal); - vmstate_register_ram_global(&s->prom); - memory_region_set_readonly(&s->prom, true); - sysbus_init_mmio(dev, &s->prom); - return 0; -} - -static Property prom_properties[] = { - {/* end of property list */}, -}; - -static void prom_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = prom_init1; - dc->props = prom_properties; -} - -static const TypeInfo prom_info = { - .name = TYPE_OPENPROM, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PROMState), - .class_init = prom_class_init, -}; - -#define TYPE_SUN4M_MEMORY "memory" -#define SUN4M_RAM(obj) OBJECT_CHECK(RamDevice, (obj), TYPE_SUN4M_MEMORY) - -typedef struct RamDevice { - SysBusDevice parent_obj; - - MemoryRegion ram; - uint64_t size; -} RamDevice; - -/* System RAM */ -static int ram_init1(SysBusDevice *dev) -{ - RamDevice *d = SUN4M_RAM(dev); - - memory_region_allocate_system_memory(&d->ram, OBJECT(d), "sun4m.ram", - d->size); - sysbus_init_mmio(dev, &d->ram); - return 0; -} - -static void ram_init(hwaddr addr, ram_addr_t RAM_size, - uint64_t max_mem) -{ - DeviceState *dev; - SysBusDevice *s; - RamDevice *d; - - /* allocate RAM */ - if ((uint64_t)RAM_size > max_mem) { - fprintf(stderr, - "qemu: Too much memory for this machine: %d, maximum %d\n", - (unsigned int)(RAM_size / (1024 * 1024)), - (unsigned int)(max_mem / (1024 * 1024))); - exit(1); - } - dev = qdev_create(NULL, "memory"); - s = SYS_BUS_DEVICE(dev); - - d = SUN4M_RAM(dev); - d->size = RAM_size; - qdev_init_nofail(dev); - - sysbus_mmio_map(s, 0, addr); -} - -static Property ram_properties[] = { - DEFINE_PROP_UINT64("size", RamDevice, size, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ram_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = ram_init1; - dc->props = ram_properties; -} - -static const TypeInfo ram_info = { - .name = TYPE_SUN4M_MEMORY, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(RamDevice), - .class_init = ram_class_init, -}; - -static void cpu_devinit(const char *cpu_model, unsigned int id, - uint64_t prom_addr, qemu_irq **cpu_irqs) -{ - CPUState *cs; - SPARCCPU *cpu; - CPUSPARCState *env; - - cpu = cpu_sparc_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n"); - exit(1); - } - env = &cpu->env; - - cpu_sparc_set_id(env, id); - if (id == 0) { - qemu_register_reset(main_cpu_reset, cpu); - } else { - qemu_register_reset(secondary_cpu_reset, cpu); - cs = CPU(cpu); - cs->halted = 1; - } - *cpu_irqs = qemu_allocate_irqs(cpu_set_irq, cpu, MAX_PILS); - env->prom_addr = prom_addr; -} - -static void dummy_fdc_tc(void *opaque, int irq, int level) -{ -} - -static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, - MachineState *machine) -{ - const char *cpu_model = machine->cpu_model; - unsigned int i; - void *iommu, *espdma, *ledma, *nvram; - qemu_irq *cpu_irqs[MAX_CPUS], slavio_irq[32], slavio_cpu_irq[MAX_CPUS], - espdma_irq, ledma_irq; - qemu_irq esp_reset, dma_enable; - qemu_irq fdc_tc; - unsigned long kernel_size; - DriveInfo *fd[MAX_FD]; - FWCfgState *fw_cfg; - unsigned int num_vsimms; - - /* init CPUs */ - if (!cpu_model) - cpu_model = hwdef->default_cpu_model; - - for(i = 0; i < smp_cpus; i++) { - cpu_devinit(cpu_model, i, hwdef->slavio_base, &cpu_irqs[i]); - } - - for (i = smp_cpus; i < MAX_CPUS; i++) - cpu_irqs[i] = qemu_allocate_irqs(dummy_cpu_set_irq, NULL, MAX_PILS); - - - /* set up devices */ - ram_init(0, machine->ram_size, hwdef->max_mem); - /* models without ECC don't trap when missing ram is accessed */ - if (!hwdef->ecc_base) { - empty_slot_init(machine->ram_size, hwdef->max_mem - machine->ram_size); - } - - prom_init(hwdef->slavio_base, bios_name); - - slavio_intctl = slavio_intctl_init(hwdef->intctl_base, - hwdef->intctl_base + 0x10000ULL, - cpu_irqs); - - for (i = 0; i < 32; i++) { - slavio_irq[i] = qdev_get_gpio_in(slavio_intctl, i); - } - for (i = 0; i < MAX_CPUS; i++) { - slavio_cpu_irq[i] = qdev_get_gpio_in(slavio_intctl, 32 + i); - } - - if (hwdef->idreg_base) { - idreg_init(hwdef->idreg_base); - } - - if (hwdef->afx_base) { - afx_init(hwdef->afx_base); - } - - iommu = iommu_init(hwdef->iommu_base, hwdef->iommu_version, - slavio_irq[30]); - - if (hwdef->iommu_pad_base) { - /* On the real hardware (SS-5, LX) the MMU is not padded, but aliased. - Software shouldn't use aliased addresses, neither should it crash - when does. Using empty_slot instead of aliasing can help with - debugging such accesses */ - empty_slot_init(hwdef->iommu_pad_base,hwdef->iommu_pad_len); - } - - espdma = sparc32_dma_init(hwdef->dma_base, slavio_irq[18], - iommu, &espdma_irq, 0); - - ledma = sparc32_dma_init(hwdef->dma_base + 16ULL, - slavio_irq[16], iommu, &ledma_irq, 1); - - if (graphic_depth != 8 && graphic_depth != 24) { - error_report("Unsupported depth: %d", graphic_depth); - exit (1); - } - num_vsimms = 0; - if (num_vsimms == 0) { - if (vga_interface_type == VGA_CG3) { - if (graphic_depth != 8) { - error_report("Unsupported depth: %d", graphic_depth); - exit(1); - } - - if (!(graphic_width == 1024 && graphic_height == 768) && - !(graphic_width == 1152 && graphic_height == 900)) { - error_report("Unsupported resolution: %d x %d", graphic_width, - graphic_height); - exit(1); - } - - /* sbus irq 5 */ - cg3_init(hwdef->tcx_base, slavio_irq[11], 0x00100000, - graphic_width, graphic_height, graphic_depth); - } else { - /* If no display specified, default to TCX */ - if (graphic_depth != 8 && graphic_depth != 24) { - error_report("Unsupported depth: %d", graphic_depth); - exit(1); - } - - if (!(graphic_width == 1024 && graphic_height == 768)) { - error_report("Unsupported resolution: %d x %d", - graphic_width, graphic_height); - exit(1); - } - - tcx_init(hwdef->tcx_base, slavio_irq[11], 0x00100000, - graphic_width, graphic_height, graphic_depth); - } - } - - for (i = num_vsimms; i < MAX_VSIMMS; i++) { - /* vsimm registers probed by OBP */ - if (hwdef->vsimm[i].reg_base) { - empty_slot_init(hwdef->vsimm[i].reg_base, 0x2000); - } - } - - if (hwdef->sx_base) { - empty_slot_init(hwdef->sx_base, 0x2000); - } - - lance_init(&nd_table[0], hwdef->le_base, ledma, ledma_irq); - - nvram = m48t59_init(slavio_irq[0], hwdef->nvram_base, 0, 0x2000, 1968, 8); - - slavio_timer_init_all(hwdef->counter_base, slavio_irq[19], slavio_cpu_irq, smp_cpus); - - slavio_serial_ms_kbd_init(hwdef->ms_kb_base, slavio_irq[14], - display_type == DT_NOGRAPHIC, ESCC_CLOCK, 1); - /* Slavio TTYA (base+4, Linux ttyS0) is the first QEMU serial device - Slavio TTYB (base+0, Linux ttyS1) is the second QEMU serial device */ - escc_init(hwdef->serial_base, slavio_irq[15], slavio_irq[15], - serial_hds[0], serial_hds[1], ESCC_CLOCK, 1); - - if (hwdef->apc_base) { - apc_init(hwdef->apc_base, qemu_allocate_irq(cpu_halt_signal, NULL, 0)); - } - - if (hwdef->fd_base) { - /* there is zero or one floppy drive */ - memset(fd, 0, sizeof(fd)); - fd[0] = drive_get(IF_FLOPPY, 0, 0); - sun4m_fdctrl_init(slavio_irq[22], hwdef->fd_base, fd, - &fdc_tc); - } else { - fdc_tc = qemu_allocate_irq(dummy_fdc_tc, NULL, 0); - } - - slavio_misc_init(hwdef->slavio_base, hwdef->aux1_base, hwdef->aux2_base, - slavio_irq[30], fdc_tc); - - if (drive_get_max_bus(IF_SCSI) > 0) { - fprintf(stderr, "qemu: too many SCSI bus\n"); - exit(1); - } - - esp_init(hwdef->esp_base, 2, - espdma_memory_read, espdma_memory_write, - espdma, espdma_irq, &esp_reset, &dma_enable); - - qdev_connect_gpio_out(espdma, 0, esp_reset); - qdev_connect_gpio_out(espdma, 1, dma_enable); - - if (hwdef->cs_base) { - sysbus_create_simple("SUNW,CS4231", hwdef->cs_base, - slavio_irq[5]); - } - - if (hwdef->dbri_base) { - /* ISDN chip with attached CS4215 audio codec */ - /* prom space */ - empty_slot_init(hwdef->dbri_base+0x1000, 0x30); - /* reg space */ - empty_slot_init(hwdef->dbri_base+0x10000, 0x100); - } - - if (hwdef->bpp_base) { - /* parallel port */ - empty_slot_init(hwdef->bpp_base, 0x20); - } - - kernel_size = sun4m_load_kernel(machine->kernel_filename, - machine->initrd_filename, - machine->ram_size); - - nvram_init(nvram, (uint8_t *)&nd_table[0].macaddr, machine->kernel_cmdline, - machine->boot_order, machine->ram_size, kernel_size, - graphic_width, graphic_height, graphic_depth, - hwdef->nvram_machine_id, "Sun4m"); - - if (hwdef->ecc_base) - ecc_init(hwdef->ecc_base, slavio_irq[28], - hwdef->ecc_version); - - fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2); - fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); - fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); - fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id); - fw_cfg_add_i16(fw_cfg, FW_CFG_SUN4M_DEPTH, graphic_depth); - fw_cfg_add_i16(fw_cfg, FW_CFG_SUN4M_WIDTH, graphic_width); - fw_cfg_add_i16(fw_cfg, FW_CFG_SUN4M_HEIGHT, graphic_height); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, KERNEL_LOAD_ADDR); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); - if (machine->kernel_cmdline) { - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR); - pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, - machine->kernel_cmdline); - fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, machine->kernel_cmdline); - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, - strlen(machine->kernel_cmdline) + 1); - } else { - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, 0); - } - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, INITRD_LOAD_ADDR); - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, 0); // not used - fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, machine->boot_order[0]); - qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); -} - -enum { - ss5_id = 32, - vger_id, - lx_id, - ss4_id, - scls_id, - sbook_id, - ss10_id = 64, - ss20_id, - ss600mp_id, -}; - -static const struct sun4m_hwdef sun4m_hwdefs[] = { - /* SS-5 */ - { - .iommu_base = 0x10000000, - .iommu_pad_base = 0x10004000, - .iommu_pad_len = 0x0fffb000, - .tcx_base = 0x50000000, - .cs_base = 0x6c000000, - .slavio_base = 0x70000000, - .ms_kb_base = 0x71000000, - .serial_base = 0x71100000, - .nvram_base = 0x71200000, - .fd_base = 0x71400000, - .counter_base = 0x71d00000, - .intctl_base = 0x71e00000, - .idreg_base = 0x78000000, - .dma_base = 0x78400000, - .esp_base = 0x78800000, - .le_base = 0x78c00000, - .apc_base = 0x6a000000, - .afx_base = 0x6e000000, - .aux1_base = 0x71900000, - .aux2_base = 0x71910000, - .nvram_machine_id = 0x80, - .machine_id = ss5_id, - .iommu_version = 0x05000000, - .max_mem = 0x10000000, - .default_cpu_model = "Fujitsu MB86904", - }, - /* SS-10 */ - { - .iommu_base = 0xfe0000000ULL, - .tcx_base = 0xe20000000ULL, - .slavio_base = 0xff0000000ULL, - .ms_kb_base = 0xff1000000ULL, - .serial_base = 0xff1100000ULL, - .nvram_base = 0xff1200000ULL, - .fd_base = 0xff1700000ULL, - .counter_base = 0xff1300000ULL, - .intctl_base = 0xff1400000ULL, - .idreg_base = 0xef0000000ULL, - .dma_base = 0xef0400000ULL, - .esp_base = 0xef0800000ULL, - .le_base = 0xef0c00000ULL, - .apc_base = 0xefa000000ULL, // XXX should not exist - .aux1_base = 0xff1800000ULL, - .aux2_base = 0xff1a01000ULL, - .ecc_base = 0xf00000000ULL, - .ecc_version = 0x10000000, // version 0, implementation 1 - .nvram_machine_id = 0x72, - .machine_id = ss10_id, - .iommu_version = 0x03000000, - .max_mem = 0xf00000000ULL, - .default_cpu_model = "TI SuperSparc II", - }, - /* SS-600MP */ - { - .iommu_base = 0xfe0000000ULL, - .tcx_base = 0xe20000000ULL, - .slavio_base = 0xff0000000ULL, - .ms_kb_base = 0xff1000000ULL, - .serial_base = 0xff1100000ULL, - .nvram_base = 0xff1200000ULL, - .counter_base = 0xff1300000ULL, - .intctl_base = 0xff1400000ULL, - .dma_base = 0xef0081000ULL, - .esp_base = 0xef0080000ULL, - .le_base = 0xef0060000ULL, - .apc_base = 0xefa000000ULL, // XXX should not exist - .aux1_base = 0xff1800000ULL, - .aux2_base = 0xff1a01000ULL, // XXX should not exist - .ecc_base = 0xf00000000ULL, - .ecc_version = 0x00000000, // version 0, implementation 0 - .nvram_machine_id = 0x71, - .machine_id = ss600mp_id, - .iommu_version = 0x01000000, - .max_mem = 0xf00000000ULL, - .default_cpu_model = "TI SuperSparc II", - }, - /* SS-20 */ - { - .iommu_base = 0xfe0000000ULL, - .tcx_base = 0xe20000000ULL, - .slavio_base = 0xff0000000ULL, - .ms_kb_base = 0xff1000000ULL, - .serial_base = 0xff1100000ULL, - .nvram_base = 0xff1200000ULL, - .fd_base = 0xff1700000ULL, - .counter_base = 0xff1300000ULL, - .intctl_base = 0xff1400000ULL, - .idreg_base = 0xef0000000ULL, - .dma_base = 0xef0400000ULL, - .esp_base = 0xef0800000ULL, - .le_base = 0xef0c00000ULL, - .bpp_base = 0xef4800000ULL, - .apc_base = 0xefa000000ULL, // XXX should not exist - .aux1_base = 0xff1800000ULL, - .aux2_base = 0xff1a01000ULL, - .dbri_base = 0xee0000000ULL, - .sx_base = 0xf80000000ULL, - .vsimm = { - { - .reg_base = 0x9c000000ULL, - .vram_base = 0xfc000000ULL - }, { - .reg_base = 0x90000000ULL, - .vram_base = 0xf0000000ULL - }, { - .reg_base = 0x94000000ULL - }, { - .reg_base = 0x98000000ULL - } - }, - .ecc_base = 0xf00000000ULL, - .ecc_version = 0x20000000, // version 0, implementation 2 - .nvram_machine_id = 0x72, - .machine_id = ss20_id, - .iommu_version = 0x13000000, - .max_mem = 0xf00000000ULL, - .default_cpu_model = "TI SuperSparc II", - }, - /* Voyager */ - { - .iommu_base = 0x10000000, - .tcx_base = 0x50000000, - .slavio_base = 0x70000000, - .ms_kb_base = 0x71000000, - .serial_base = 0x71100000, - .nvram_base = 0x71200000, - .fd_base = 0x71400000, - .counter_base = 0x71d00000, - .intctl_base = 0x71e00000, - .idreg_base = 0x78000000, - .dma_base = 0x78400000, - .esp_base = 0x78800000, - .le_base = 0x78c00000, - .apc_base = 0x71300000, // pmc - .aux1_base = 0x71900000, - .aux2_base = 0x71910000, - .nvram_machine_id = 0x80, - .machine_id = vger_id, - .iommu_version = 0x05000000, - .max_mem = 0x10000000, - .default_cpu_model = "Fujitsu MB86904", - }, - /* LX */ - { - .iommu_base = 0x10000000, - .iommu_pad_base = 0x10004000, - .iommu_pad_len = 0x0fffb000, - .tcx_base = 0x50000000, - .slavio_base = 0x70000000, - .ms_kb_base = 0x71000000, - .serial_base = 0x71100000, - .nvram_base = 0x71200000, - .fd_base = 0x71400000, - .counter_base = 0x71d00000, - .intctl_base = 0x71e00000, - .idreg_base = 0x78000000, - .dma_base = 0x78400000, - .esp_base = 0x78800000, - .le_base = 0x78c00000, - .aux1_base = 0x71900000, - .aux2_base = 0x71910000, - .nvram_machine_id = 0x80, - .machine_id = lx_id, - .iommu_version = 0x04000000, - .max_mem = 0x10000000, - .default_cpu_model = "TI MicroSparc I", - }, - /* SS-4 */ - { - .iommu_base = 0x10000000, - .tcx_base = 0x50000000, - .cs_base = 0x6c000000, - .slavio_base = 0x70000000, - .ms_kb_base = 0x71000000, - .serial_base = 0x71100000, - .nvram_base = 0x71200000, - .fd_base = 0x71400000, - .counter_base = 0x71d00000, - .intctl_base = 0x71e00000, - .idreg_base = 0x78000000, - .dma_base = 0x78400000, - .esp_base = 0x78800000, - .le_base = 0x78c00000, - .apc_base = 0x6a000000, - .aux1_base = 0x71900000, - .aux2_base = 0x71910000, - .nvram_machine_id = 0x80, - .machine_id = ss4_id, - .iommu_version = 0x05000000, - .max_mem = 0x10000000, - .default_cpu_model = "Fujitsu MB86904", - }, - /* SPARCClassic */ - { - .iommu_base = 0x10000000, - .tcx_base = 0x50000000, - .slavio_base = 0x70000000, - .ms_kb_base = 0x71000000, - .serial_base = 0x71100000, - .nvram_base = 0x71200000, - .fd_base = 0x71400000, - .counter_base = 0x71d00000, - .intctl_base = 0x71e00000, - .idreg_base = 0x78000000, - .dma_base = 0x78400000, - .esp_base = 0x78800000, - .le_base = 0x78c00000, - .apc_base = 0x6a000000, - .aux1_base = 0x71900000, - .aux2_base = 0x71910000, - .nvram_machine_id = 0x80, - .machine_id = scls_id, - .iommu_version = 0x05000000, - .max_mem = 0x10000000, - .default_cpu_model = "TI MicroSparc I", - }, - /* SPARCbook */ - { - .iommu_base = 0x10000000, - .tcx_base = 0x50000000, // XXX - .slavio_base = 0x70000000, - .ms_kb_base = 0x71000000, - .serial_base = 0x71100000, - .nvram_base = 0x71200000, - .fd_base = 0x71400000, - .counter_base = 0x71d00000, - .intctl_base = 0x71e00000, - .idreg_base = 0x78000000, - .dma_base = 0x78400000, - .esp_base = 0x78800000, - .le_base = 0x78c00000, - .apc_base = 0x6a000000, - .aux1_base = 0x71900000, - .aux2_base = 0x71910000, - .nvram_machine_id = 0x80, - .machine_id = sbook_id, - .iommu_version = 0x05000000, - .max_mem = 0x10000000, - .default_cpu_model = "TI MicroSparc I", - }, -}; - -/* SPARCstation 5 hardware initialisation */ -static void ss5_init(MachineState *machine) -{ - sun4m_hw_init(&sun4m_hwdefs[0], machine); -} - -/* SPARCstation 10 hardware initialisation */ -static void ss10_init(MachineState *machine) -{ - sun4m_hw_init(&sun4m_hwdefs[1], machine); -} - -/* SPARCserver 600MP hardware initialisation */ -static void ss600mp_init(MachineState *machine) -{ - sun4m_hw_init(&sun4m_hwdefs[2], machine); -} - -/* SPARCstation 20 hardware initialisation */ -static void ss20_init(MachineState *machine) -{ - sun4m_hw_init(&sun4m_hwdefs[3], machine); -} - -/* SPARCstation Voyager hardware initialisation */ -static void vger_init(MachineState *machine) -{ - sun4m_hw_init(&sun4m_hwdefs[4], machine); -} - -/* SPARCstation LX hardware initialisation */ -static void ss_lx_init(MachineState *machine) -{ - sun4m_hw_init(&sun4m_hwdefs[5], machine); -} - -/* SPARCstation 4 hardware initialisation */ -static void ss4_init(MachineState *machine) -{ - sun4m_hw_init(&sun4m_hwdefs[6], machine); -} - -/* SPARCClassic hardware initialisation */ -static void scls_init(MachineState *machine) -{ - sun4m_hw_init(&sun4m_hwdefs[7], machine); -} - -/* SPARCbook hardware initialisation */ -static void sbook_init(MachineState *machine) -{ - sun4m_hw_init(&sun4m_hwdefs[8], machine); -} - -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 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 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 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 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 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 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 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 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) -{ - type_register_static(&idreg_info); - type_register_static(&afx_info); - type_register_static(&prom_info); - type_register_static(&ram_info); - - 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) diff --git a/qemu/hw/sparc64/Makefile.objs b/qemu/hw/sparc64/Makefile.objs deleted file mode 100644 index a84cfe3ec..000000000 --- a/qemu/hw/sparc64/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -obj-y += sun4u.o diff --git a/qemu/hw/sparc64/sun4u.c b/qemu/hw/sparc64/sun4u.c deleted file mode 100644 index 3165e18eb..000000000 --- a/qemu/hw/sparc64/sun4u.c +++ /dev/null @@ -1,1010 +0,0 @@ -/* - * QEMU Sun4u/Sun4v System Emulator - * - * Copyright (c) 2005 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" -#include "hw/pci-host/apb.h" -#include "hw/i386/pc.h" -#include "hw/char/serial.h" -#include "hw/timer/m48t59.h" -#include "hw/block/fdc.h" -#include "net/net.h" -#include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" -#include "hw/nvram/openbios_firmware_abi.h" -#include "hw/nvram/fw_cfg.h" -#include "hw/sysbus.h" -#include "hw/ide.h" -#include "hw/loader.h" -#include "elf.h" -#include "sysemu/block-backend.h" -#include "exec/address-spaces.h" -#include "qemu/cutils.h" - -//#define DEBUG_IRQ -//#define DEBUG_EBUS -//#define DEBUG_TIMER - -#ifdef DEBUG_IRQ -#define CPUIRQ_DPRINTF(fmt, ...) \ - do { printf("CPUIRQ: " fmt , ## __VA_ARGS__); } while (0) -#else -#define CPUIRQ_DPRINTF(fmt, ...) -#endif - -#ifdef DEBUG_EBUS -#define EBUS_DPRINTF(fmt, ...) \ - do { printf("EBUS: " fmt , ## __VA_ARGS__); } while (0) -#else -#define EBUS_DPRINTF(fmt, ...) -#endif - -#ifdef DEBUG_TIMER -#define TIMER_DPRINTF(fmt, ...) \ - do { printf("TIMER: " fmt , ## __VA_ARGS__); } while (0) -#else -#define TIMER_DPRINTF(fmt, ...) -#endif - -#define KERNEL_LOAD_ADDR 0x00404000 -#define CMDLINE_ADDR 0x003ff000 -#define PROM_SIZE_MAX (4 * 1024 * 1024) -#define PROM_VADDR 0x000ffd00000ULL -#define APB_SPECIAL_BASE 0x1fe00000000ULL -#define APB_MEM_BASE 0x1ff00000000ULL -#define APB_PCI_IO_BASE (APB_SPECIAL_BASE + 0x02000000ULL) -#define PROM_FILENAME "openbios-sparc64" -#define NVRAM_SIZE 0x2000 -#define MAX_IDE_BUS 2 -#define BIOS_CFG_IOPORT 0x510 -#define FW_CFG_SPARC64_WIDTH (FW_CFG_ARCH_LOCAL + 0x00) -#define FW_CFG_SPARC64_HEIGHT (FW_CFG_ARCH_LOCAL + 0x01) -#define FW_CFG_SPARC64_DEPTH (FW_CFG_ARCH_LOCAL + 0x02) - -#define IVEC_MAX 0x40 - -#define TICK_MAX 0x7fffffffffffffffULL - -struct hwdef { - const char * const default_cpu_model; - uint16_t machine_id; - uint64_t prom_addr; - uint64_t console_serial_base; -}; - -typedef struct EbusState { - PCIDevice pci_dev; - MemoryRegion bar0; - MemoryRegion bar1; -} EbusState; - -void DMA_init(ISABus *bus, int high_page_enable) -{ -} - -static void fw_cfg_boot_set(void *opaque, const char *boot_device, - Error **errp) -{ - fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); -} - -static int sun4u_NVRAM_set_params(Nvram *nvram, uint16_t NVRAM_size, - const char *arch, ram_addr_t RAM_size, - const char *boot_devices, - uint32_t kernel_image, uint32_t kernel_size, - const char *cmdline, - uint32_t initrd_image, uint32_t initrd_size, - uint32_t NVRAM_image, - int width, int height, int depth, - const uint8_t *macaddr) -{ - unsigned int i; - uint32_t start, end; - uint8_t image[0x1ff0]; - struct OpenBIOS_nvpart_v1 *part_header; - NvramClass *k = NVRAM_GET_CLASS(nvram); - - memset(image, '\0', sizeof(image)); - - start = 0; - - // OpenBIOS nvram variables - // Variable partition - part_header = (struct OpenBIOS_nvpart_v1 *)&image[start]; - part_header->signature = OPENBIOS_PART_SYSTEM; - pstrcpy(part_header->name, sizeof(part_header->name), "system"); - - end = start + sizeof(struct OpenBIOS_nvpart_v1); - for (i = 0; i < nb_prom_envs; i++) - end = OpenBIOS_set_var(image, end, prom_envs[i]); - - // End marker - image[end++] = '\0'; - - end = start + ((end - start + 15) & ~15); - OpenBIOS_finish_partition(part_header, end - start); - - // free partition - start = end; - part_header = (struct OpenBIOS_nvpart_v1 *)&image[start]; - part_header->signature = OPENBIOS_PART_FREE; - pstrcpy(part_header->name, sizeof(part_header->name), "free"); - - end = 0x1fd0; - OpenBIOS_finish_partition(part_header, end - start); - - Sun_init_header((struct Sun_nvram *)&image[0x1fd8], macaddr, 0x80); - - for (i = 0; i < sizeof(image); i++) { - (k->write)(nvram, i, image[i]); - } - - return 0; -} - -static uint64_t sun4u_load_kernel(const char *kernel_filename, - const char *initrd_filename, - ram_addr_t RAM_size, uint64_t *initrd_size, - uint64_t *initrd_addr, uint64_t *kernel_addr, - uint64_t *kernel_entry) -{ - int linux_boot; - unsigned int i; - long kernel_size; - uint8_t *ptr; - uint64_t kernel_top; - - linux_boot = (kernel_filename != NULL); - - kernel_size = 0; - if (linux_boot) { - int bswap_needed; - -#ifdef BSWAP_NEEDED - bswap_needed = 1; -#else - bswap_needed = 0; -#endif - kernel_size = load_elf(kernel_filename, NULL, NULL, kernel_entry, - kernel_addr, &kernel_top, 1, EM_SPARCV9, 0, 0); - if (kernel_size < 0) { - *kernel_addr = KERNEL_LOAD_ADDR; - *kernel_entry = KERNEL_LOAD_ADDR; - kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR, - RAM_size - KERNEL_LOAD_ADDR, bswap_needed, - TARGET_PAGE_SIZE); - } - if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, - KERNEL_LOAD_ADDR, - RAM_size - KERNEL_LOAD_ADDR); - } - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); - exit(1); - } - /* load initrd above kernel */ - *initrd_size = 0; - if (initrd_filename) { - *initrd_addr = TARGET_PAGE_ALIGN(kernel_top); - - *initrd_size = load_image_targphys(initrd_filename, - *initrd_addr, - RAM_size - *initrd_addr); - if ((int)*initrd_size < 0) { - fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", - initrd_filename); - exit(1); - } - } - if (*initrd_size > 0) { - for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) { - ptr = rom_ptr(*kernel_addr + i); - if (ldl_p(ptr + 8) == 0x48647253) { /* HdrS */ - stl_p(ptr + 24, *initrd_addr + *kernel_addr); - stl_p(ptr + 28, *initrd_size); - break; - } - } - } - } - return kernel_size; -} - -void cpu_check_irqs(CPUSPARCState *env) -{ - CPUState *cs; - uint32_t pil = env->pil_in | - (env->softint & ~(SOFTINT_TIMER | SOFTINT_STIMER)); - - /* TT_IVEC has a higher priority (16) than TT_EXTINT (31..17) */ - if (env->ivec_status & 0x20) { - return; - } - cs = CPU(sparc_env_get_cpu(env)); - /* check if TM or SM in SOFTINT are set - setting these also causes interrupt 14 */ - if (env->softint & (SOFTINT_TIMER | SOFTINT_STIMER)) { - pil |= 1 << 14; - } - - /* The bit corresponding to psrpil is (1<< psrpil), the next bit - is (2 << psrpil). */ - if (pil < (2 << env->psrpil)){ - if (cs->interrupt_request & CPU_INTERRUPT_HARD) { - CPUIRQ_DPRINTF("Reset CPU IRQ (current interrupt %x)\n", - env->interrupt_index); - env->interrupt_index = 0; - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } - return; - } - - if (cpu_interrupts_enabled(env)) { - - unsigned int i; - - for (i = 15; i > env->psrpil; i--) { - if (pil & (1 << i)) { - int old_interrupt = env->interrupt_index; - int new_interrupt = TT_EXTINT | i; - - if (unlikely(env->tl > 0 && cpu_tsptr(env)->tt > new_interrupt - && ((cpu_tsptr(env)->tt & 0x1f0) == TT_EXTINT))) { - CPUIRQ_DPRINTF("Not setting CPU IRQ: TL=%d " - "current %x >= pending %x\n", - env->tl, cpu_tsptr(env)->tt, new_interrupt); - } else if (old_interrupt != new_interrupt) { - env->interrupt_index = new_interrupt; - CPUIRQ_DPRINTF("Set CPU IRQ %d old=%x new=%x\n", i, - old_interrupt, new_interrupt); - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } - break; - } - } - } else if (cs->interrupt_request & CPU_INTERRUPT_HARD) { - CPUIRQ_DPRINTF("Interrupts disabled, pil=%08x pil_in=%08x softint=%08x " - "current interrupt %x\n", - pil, env->pil_in, env->softint, env->interrupt_index); - env->interrupt_index = 0; - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } -} - -static void cpu_kick_irq(SPARCCPU *cpu) -{ - CPUState *cs = CPU(cpu); - CPUSPARCState *env = &cpu->env; - - cs->halted = 0; - cpu_check_irqs(env); - qemu_cpu_kick(cs); -} - -static void cpu_set_ivec_irq(void *opaque, int irq, int level) -{ - SPARCCPU *cpu = opaque; - CPUSPARCState *env = &cpu->env; - CPUState *cs; - - if (level) { - if (!(env->ivec_status & 0x20)) { - CPUIRQ_DPRINTF("Raise IVEC IRQ %d\n", irq); - cs = CPU(cpu); - cs->halted = 0; - env->interrupt_index = TT_IVEC; - env->ivec_status |= 0x20; - env->ivec_data[0] = (0x1f << 6) | irq; - env->ivec_data[1] = 0; - env->ivec_data[2] = 0; - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } - } else { - if (env->ivec_status & 0x20) { - CPUIRQ_DPRINTF("Lower IVEC IRQ %d\n", irq); - cs = CPU(cpu); - env->ivec_status &= ~0x20; - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } - } -} - -typedef struct ResetData { - SPARCCPU *cpu; - uint64_t prom_addr; -} ResetData; - -static CPUTimer *cpu_timer_create(const char *name, SPARCCPU *cpu, - QEMUBHFunc *cb, uint32_t frequency, - 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); - - return timer; -} - -static void cpu_timer_reset(CPUTimer *timer) -{ - timer->disabled = 1; - timer->clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - timer_del(timer->qtimer); -} - -static void main_cpu_reset(void *opaque) -{ - ResetData *s = (ResetData *)opaque; - CPUSPARCState *env = &s->cpu->env; - static unsigned int nr_resets; - - cpu_reset(CPU(s->cpu)); - - cpu_timer_reset(env->tick); - cpu_timer_reset(env->stick); - cpu_timer_reset(env->hstick); - - env->gregs[1] = 0; // Memory start - env->gregs[2] = ram_size; // Memory size - env->gregs[3] = 0; // Machine description XXX - if (nr_resets++ == 0) { - /* Power on reset */ - env->pc = s->prom_addr + 0x20ULL; - } else { - env->pc = s->prom_addr + 0x40ULL; - } - env->npc = env->pc + 4; -} - -static void tick_irq(void *opaque) -{ - SPARCCPU *cpu = opaque; - CPUSPARCState *env = &cpu->env; - - CPUTimer* timer = env->tick; - - if (timer->disabled) { - CPUIRQ_DPRINTF("tick_irq: softint disabled\n"); - return; - } else { - CPUIRQ_DPRINTF("tick: fire\n"); - } - - env->softint |= SOFTINT_TIMER; - cpu_kick_irq(cpu); -} - -static void stick_irq(void *opaque) -{ - SPARCCPU *cpu = opaque; - CPUSPARCState *env = &cpu->env; - - CPUTimer* timer = env->stick; - - if (timer->disabled) { - CPUIRQ_DPRINTF("stick_irq: softint disabled\n"); - return; - } else { - CPUIRQ_DPRINTF("stick: fire\n"); - } - - env->softint |= SOFTINT_STIMER; - cpu_kick_irq(cpu); -} - -static void hstick_irq(void *opaque) -{ - SPARCCPU *cpu = opaque; - CPUSPARCState *env = &cpu->env; - - CPUTimer* timer = env->hstick; - - if (timer->disabled) { - CPUIRQ_DPRINTF("hstick_irq: softint disabled\n"); - return; - } else { - CPUIRQ_DPRINTF("hstick: fire\n"); - } - - env->softint |= SOFTINT_STIMER; - cpu_kick_irq(cpu); -} - -static int64_t cpu_to_timer_ticks(int64_t cpu_ticks, uint32_t 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, NANOSECONDS_PER_SECOND); -} - -void cpu_tick_set_count(CPUTimer *timer, uint64_t count) -{ - 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 (npt %s) p=%p\n", - timer->name, real_count, - timer->npt ? "disabled" : "enabled", timer); - - timer->npt = npt_bit ? 1 : 0; - timer->clock_offset = vm_clock_offset; -} - -uint64_t cpu_tick_get_count(CPUTimer *timer) -{ - uint64_t real_count = timer_to_cpu_ticks( - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - timer->clock_offset, - timer->frequency); - - TIMER_DPRINTF("%s get_count count=0x%016lx (npt %s) p=%p\n", - timer->name, real_count, - timer->npt ? "disabled" : "enabled", timer); - - if (timer->npt) { - real_count |= timer->npt_mask; - } - - return real_count; -} - -void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit) -{ - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - uint64_t real_limit = limit & ~timer->disabled_mask; - timer->disabled = (limit & timer->disabled_mask) ? 1 : 0; - - int64_t expires = cpu_to_timer_ticks(real_limit, timer->frequency) + - timer->clock_offset; - - if (expires < now) { - expires = now + 1; - } - - TIMER_DPRINTF("%s set_limit limit=0x%016lx (%s) p=%p " - "called with limit=0x%016lx at 0x%016lx (delta=0x%016lx)\n", - timer->name, real_limit, - timer->disabled?"disabled":"enabled", - timer, limit, - timer_to_cpu_ticks(now - timer->clock_offset, - timer->frequency), - timer_to_cpu_ticks(expires - now, timer->frequency)); - - if (!real_limit) { - TIMER_DPRINTF("%s set_limit limit=ZERO - not starting timer\n", - timer->name); - timer_del(timer->qtimer); - } else if (timer->disabled) { - timer_del(timer->qtimer); - } else { - timer_mod(timer->qtimer, expires); - } -} - -static void isa_irq_handler(void *opaque, int n, int level) -{ - static const int isa_irq_to_ivec[16] = { - [1] = 0x29, /* keyboard */ - [4] = 0x2b, /* serial */ - [6] = 0x27, /* floppy */ - [7] = 0x22, /* parallel */ - [12] = 0x2a, /* mouse */ - }; - qemu_irq *irqs = opaque; - int ivec; - - assert(n < 16); - ivec = isa_irq_to_ivec[n]; - EBUS_DPRINTF("Set ISA IRQ %d level %d -> ivec 0x%x\n", n, level, ivec); - if (ivec) { - qemu_set_irq(irqs[ivec], level); - } -} - -/* EBUS (Eight bit bus) bridge */ -static ISABus * -pci_ebus_init(PCIBus *bus, int devfn, qemu_irq *irqs) -{ - qemu_irq *isa_irq; - PCIDevice *pci_dev; - ISABus *isa_bus; - - pci_dev = pci_create_simple(bus, devfn, "ebus"); - isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci_dev), "isa.0")); - isa_irq = qemu_allocate_irqs(isa_irq_handler, irqs, 16); - isa_bus_irqs(isa_bus, isa_irq); - return isa_bus; -} - -static void pci_ebus_realize(PCIDevice *pci_dev, Error **errp) -{ - EbusState *s = DO_UPCAST(EbusState, pci_dev, 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; - pci_dev->config[0x06] = 0xa0; // status = fast back-to-back, 66MHz, no error - pci_dev->config[0x07] = 0x03; // status = medium devsel - pci_dev->config[0x09] = 0x00; // programming i/f - pci_dev->config[0x0D] = 0x0a; // latency_timer - - memory_region_init_alias(&s->bar0, OBJECT(s), "bar0", get_system_io(), - 0, 0x1000000); - pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0); - 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); -} - -static void ebus_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = pci_ebus_realize; - k->vendor_id = PCI_VENDOR_ID_SUN; - k->device_id = PCI_DEVICE_ID_SUN_EBUS; - k->revision = 0x01; - k->class_id = PCI_CLASS_BRIDGE_OTHER; -} - -static const TypeInfo ebus_info = { - .name = "ebus", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(EbusState), - .class_init = ebus_class_init, -}; - -#define TYPE_OPENPROM "openprom" -#define OPENPROM(obj) OBJECT_CHECK(PROMState, (obj), TYPE_OPENPROM) - -typedef struct PROMState { - SysBusDevice parent_obj; - - MemoryRegion prom; -} PROMState; - -static uint64_t translate_prom_address(void *opaque, uint64_t addr) -{ - hwaddr *base_addr = (hwaddr *)opaque; - return addr + *base_addr - PROM_VADDR; -} - -/* Boot PROM (OpenBIOS) */ -static void prom_init(hwaddr addr, const char *bios_name) -{ - DeviceState *dev; - SysBusDevice *s; - char *filename; - int ret; - - dev = qdev_create(NULL, TYPE_OPENPROM); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - - sysbus_mmio_map(s, 0, addr); - - /* load boot prom */ - if (bios_name == NULL) { - bios_name = PROM_FILENAME; - } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - ret = load_elf(filename, translate_prom_address, &addr, - NULL, NULL, NULL, 1, EM_SPARCV9, 0, 0); - if (ret < 0 || ret > PROM_SIZE_MAX) { - ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); - } - g_free(filename); - } else { - ret = -1; - } - if (ret < 0 || ret > PROM_SIZE_MAX) { - fprintf(stderr, "qemu: could not load prom '%s'\n", bios_name); - exit(1); - } -} - -static int prom_init1(SysBusDevice *dev) -{ - PROMState *s = OPENPROM(dev); - - memory_region_init_ram(&s->prom, OBJECT(s), "sun4u.prom", PROM_SIZE_MAX, - &error_fatal); - vmstate_register_ram_global(&s->prom); - memory_region_set_readonly(&s->prom, true); - sysbus_init_mmio(dev, &s->prom); - return 0; -} - -static Property prom_properties[] = { - {/* end of property list */}, -}; - -static void prom_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = prom_init1; - dc->props = prom_properties; -} - -static const TypeInfo prom_info = { - .name = TYPE_OPENPROM, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PROMState), - .class_init = prom_class_init, -}; - - -#define TYPE_SUN4U_MEMORY "memory" -#define SUN4U_RAM(obj) OBJECT_CHECK(RamDevice, (obj), TYPE_SUN4U_MEMORY) - -typedef struct RamDevice { - SysBusDevice parent_obj; - - MemoryRegion ram; - uint64_t size; -} RamDevice; - -/* System RAM */ -static int ram_init1(SysBusDevice *dev) -{ - RamDevice *d = SUN4U_RAM(dev); - - memory_region_init_ram(&d->ram, OBJECT(d), "sun4u.ram", d->size, - &error_fatal); - vmstate_register_ram_global(&d->ram); - sysbus_init_mmio(dev, &d->ram); - return 0; -} - -static void ram_init(hwaddr addr, ram_addr_t RAM_size) -{ - DeviceState *dev; - SysBusDevice *s; - RamDevice *d; - - /* allocate RAM */ - dev = qdev_create(NULL, TYPE_SUN4U_MEMORY); - s = SYS_BUS_DEVICE(dev); - - d = SUN4U_RAM(dev); - d->size = RAM_size; - qdev_init_nofail(dev); - - sysbus_mmio_map(s, 0, addr); -} - -static Property ram_properties[] = { - DEFINE_PROP_UINT64("size", RamDevice, size, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ram_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = ram_init1; - dc->props = ram_properties; -} - -static const TypeInfo ram_info = { - .name = TYPE_SUN4U_MEMORY, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(RamDevice), - .class_init = ram_class_init, -}; - -static SPARCCPU *cpu_devinit(const char *cpu_model, const struct hwdef *hwdef) -{ - SPARCCPU *cpu; - CPUSPARCState *env; - ResetData *reset_info; - - uint32_t tick_frequency = 100*1000000; - uint32_t stick_frequency = 100*1000000; - uint32_t hstick_frequency = 100*1000000; - - if (cpu_model == NULL) { - cpu_model = hwdef->default_cpu_model; - } - cpu = cpu_sparc_init(cpu_model); - if (cpu == NULL) { - fprintf(stderr, "Unable to find Sparc CPU definition\n"); - exit(1); - } - env = &cpu->env; - - env->tick = cpu_timer_create("tick", cpu, tick_irq, - tick_frequency, TICK_INT_DIS, - TICK_NPT_MASK); - - env->stick = cpu_timer_create("stick", cpu, stick_irq, - stick_frequency, TICK_INT_DIS, - TICK_NPT_MASK); - - env->hstick = cpu_timer_create("hstick", cpu, hstick_irq, - hstick_frequency, TICK_INT_DIS, - TICK_NPT_MASK); - - reset_info = g_malloc0(sizeof(ResetData)); - reset_info->cpu = cpu; - reset_info->prom_addr = hwdef->prom_addr; - qemu_register_reset(main_cpu_reset, reset_info); - - return cpu; -} - -static void sun4uv_init(MemoryRegion *address_space_mem, - MachineState *machine, - const struct hwdef *hwdef) -{ - SPARCCPU *cpu; - Nvram *nvram; - unsigned int i; - uint64_t initrd_addr, initrd_size, kernel_addr, kernel_size, kernel_entry; - PCIBus *pci_bus, *pci_bus2, *pci_bus3; - ISABus *isa_bus; - SysBusDevice *s; - 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 */ - cpu = cpu_devinit(machine->cpu_model, hwdef); - - /* set up devices */ - ram_init(0, machine->ram_size); - - prom_init(hwdef->prom_addr, bios_name); - - ivec_irqs = qemu_allocate_irqs(cpu_set_ivec_irq, cpu, IVEC_MAX); - pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, ivec_irqs, &pci_bus2, - &pci_bus3, &pbm_irqs); - pci_vga_init(pci_bus); - - // XXX Should be pci_bus3 - isa_bus = pci_ebus_init(pci_bus, -1, pbm_irqs); - - i = 0; - if (hwdef->console_serial_base) { - serial_mm_init(address_space_mem, hwdef->console_serial_base, 0, - NULL, 115200, serial_hds[i], DEVICE_BIG_ENDIAN); - i++; - } - - serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS); - parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS); - - for(i = 0; i < nb_nics; i++) - pci_nic_init_nofail(&nd_table[i], pci_bus, "ne2k_pci", NULL); - - ide_drive_get(hd, ARRAY_SIZE(hd)); - - 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); - } - 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); - s = SYS_BUS_DEVICE(nvram); - memory_region_add_subregion(get_system_io(), 0x2000, - sysbus_mmio_get_region(s, 0)); - - initrd_size = 0; - initrd_addr = 0; - kernel_size = sun4u_load_kernel(machine->kernel_filename, - machine->initrd_filename, - ram_size, &initrd_size, &initrd_addr, - &kernel_addr, &kernel_entry); - - sun4u_NVRAM_set_params(nvram, NVRAM_SIZE, "Sun4u", machine->ram_size, - machine->boot_order, - kernel_addr, kernel_size, - machine->kernel_cmdline, - initrd_addr, initrd_size, - /* XXX: need an option to load a NVRAM image */ - 0, - graphic_width, graphic_height, graphic_depth, - (uint8_t *)&nd_table[0].macaddr); - - fw_cfg = fw_cfg_init_io(BIOS_CFG_IOPORT); - fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); - fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); - fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id); - fw_cfg_add_i64(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_entry); - fw_cfg_add_i64(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); - if (machine->kernel_cmdline) { - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, - strlen(machine->kernel_cmdline) + 1); - fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, machine->kernel_cmdline); - } else { - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, 0); - } - fw_cfg_add_i64(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); - fw_cfg_add_i64(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); - fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, machine->boot_order[0]); - - fw_cfg_add_i16(fw_cfg, FW_CFG_SPARC64_WIDTH, graphic_width); - fw_cfg_add_i16(fw_cfg, FW_CFG_SPARC64_HEIGHT, graphic_height); - fw_cfg_add_i16(fw_cfg, FW_CFG_SPARC64_DEPTH, graphic_depth); - - qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); -} - -enum { - sun4u_id = 0, - sun4v_id = 64, - niagara_id, -}; - -static const struct hwdef hwdefs[] = { - /* Sun4u generic PC-like machine */ - { - .default_cpu_model = "TI UltraSparc IIi", - .machine_id = sun4u_id, - .prom_addr = 0x1fff0000000ULL, - .console_serial_base = 0, - }, - /* Sun4v generic PC-like machine */ - { - .default_cpu_model = "Sun UltraSparc T1", - .machine_id = sun4v_id, - .prom_addr = 0x1fff0000000ULL, - .console_serial_base = 0, - }, - /* Sun4v generic Niagara machine */ - { - .default_cpu_model = "Sun UltraSparc T1", - .machine_id = niagara_id, - .prom_addr = 0xfff0000000ULL, - .console_serial_base = 0xfff0c2c000ULL, - }, -}; - -/* Sun4u hardware initialisation */ -static void sun4u_init(MachineState *machine) -{ - sun4uv_init(get_system_memory(), machine, &hwdefs[0]); -} - -/* Sun4v hardware initialisation */ -static void sun4v_init(MachineState *machine) -{ - sun4uv_init(get_system_memory(), machine, &hwdefs[1]); -} - -/* Niagara hardware initialisation */ -static void niagara_init(MachineState *machine) -{ - sun4uv_init(get_system_memory(), machine, &hwdefs[2]); -} - -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 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 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) -{ - type_register_static(&ebus_info); - type_register_static(&prom_info); - type_register_static(&ram_info); - - type_register_static(&sun4u_type); - type_register_static(&sun4v_type); - type_register_static(&niagara_type); -} - -type_init(sun4u_register_types) diff --git a/qemu/hw/ssi/Makefile.objs b/qemu/hw/ssi/Makefile.objs deleted file mode 100644 index 9555825ac..000000000 --- a/qemu/hw/ssi/Makefile.objs +++ /dev/null @@ -1,6 +0,0 @@ -common-obj-$(CONFIG_PL022) += pl022.o -common-obj-$(CONFIG_SSI) += ssi.o -common-obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o -common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o - -obj-$(CONFIG_OMAP) += omap_spi.o diff --git a/qemu/hw/ssi/omap_spi.c b/qemu/hw/ssi/omap_spi.c deleted file mode 100644 index 22034656b..000000000 --- a/qemu/hw/ssi/omap_spi.c +++ /dev/null @@ -1,374 +0,0 @@ -/* - * TI OMAP processor's Multichannel SPI emulation. - * - * Copyright (C) 2007-2009 Nokia Corporation - * - * Original code for OMAP2 by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version 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, 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" - -/* Multichannel SPI */ -struct omap_mcspi_s { - MemoryRegion iomem; - qemu_irq irq; - int chnum; - - uint32_t sysconfig; - uint32_t systest; - uint32_t irqst; - uint32_t irqen; - uint32_t wken; - uint32_t control; - - struct omap_mcspi_ch_s { - qemu_irq txdrq; - qemu_irq rxdrq; - uint32_t (*txrx)(void *opaque, uint32_t, int); - void *opaque; - - uint32_t tx; - uint32_t rx; - - uint32_t config; - uint32_t status; - uint32_t control; - } ch[4]; -}; - -static inline void omap_mcspi_interrupt_update(struct omap_mcspi_s *s) -{ - qemu_set_irq(s->irq, s->irqst & s->irqen); -} - -static inline void omap_mcspi_dmarequest_update(struct omap_mcspi_ch_s *ch) -{ - qemu_set_irq(ch->txdrq, - (ch->control & 1) && /* EN */ - (ch->config & (1 << 14)) && /* DMAW */ - (ch->status & (1 << 1)) && /* TXS */ - ((ch->config >> 12) & 3) != 1); /* TRM */ - qemu_set_irq(ch->rxdrq, - (ch->control & 1) && /* EN */ - (ch->config & (1 << 15)) && /* DMAW */ - (ch->status & (1 << 0)) && /* RXS */ - ((ch->config >> 12) & 3) != 2); /* TRM */ -} - -static void omap_mcspi_transfer_run(struct omap_mcspi_s *s, int chnum) -{ - struct omap_mcspi_ch_s *ch = s->ch + chnum; - - if (!(ch->control & 1)) /* EN */ - return; - if ((ch->status & (1 << 0)) && /* RXS */ - ((ch->config >> 12) & 3) != 2 && /* TRM */ - !(ch->config & (1 << 19))) /* TURBO */ - goto intr_update; - if ((ch->status & (1 << 1)) && /* TXS */ - ((ch->config >> 12) & 3) != 1) /* TRM */ - goto intr_update; - - if (!(s->control & 1) || /* SINGLE */ - (ch->config & (1 << 20))) { /* FORCE */ - if (ch->txrx) - ch->rx = ch->txrx(ch->opaque, ch->tx, /* WL */ - 1 + (0x1f & (ch->config >> 7))); - } - - ch->tx = 0; - ch->status |= 1 << 2; /* EOT */ - ch->status |= 1 << 1; /* TXS */ - if (((ch->config >> 12) & 3) != 2) /* TRM */ - ch->status |= 1 << 0; /* RXS */ - -intr_update: - if ((ch->status & (1 << 0)) && /* RXS */ - ((ch->config >> 12) & 3) != 2 && /* TRM */ - !(ch->config & (1 << 19))) /* TURBO */ - s->irqst |= 1 << (2 + 4 * chnum); /* RX_FULL */ - if ((ch->status & (1 << 1)) && /* TXS */ - ((ch->config >> 12) & 3) != 1) /* TRM */ - s->irqst |= 1 << (0 + 4 * chnum); /* TX_EMPTY */ - omap_mcspi_interrupt_update(s); - omap_mcspi_dmarequest_update(ch); -} - -void omap_mcspi_reset(struct omap_mcspi_s *s) -{ - int ch; - - s->sysconfig = 0; - s->systest = 0; - s->irqst = 0; - s->irqen = 0; - s->wken = 0; - s->control = 4; - - for (ch = 0; ch < 4; ch ++) { - s->ch[ch].config = 0x060000; - s->ch[ch].status = 2; /* TXS */ - s->ch[ch].control = 0; - - omap_mcspi_dmarequest_update(s->ch + ch); - } - - omap_mcspi_interrupt_update(s); -} - -static uint64_t omap_mcspi_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque; - int ch = 0; - uint32_t ret; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* MCSPI_REVISION */ - return 0x91; - - case 0x10: /* MCSPI_SYSCONFIG */ - return s->sysconfig; - - case 0x14: /* MCSPI_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x18: /* MCSPI_IRQSTATUS */ - return s->irqst; - - case 0x1c: /* MCSPI_IRQENABLE */ - return s->irqen; - - case 0x20: /* MCSPI_WAKEUPENABLE */ - return s->wken; - - case 0x24: /* MCSPI_SYST */ - return s->systest; - - case 0x28: /* MCSPI_MODULCTRL */ - return s->control; - - case 0x68: ch ++; - /* fall through */ - case 0x54: ch ++; - /* fall through */ - case 0x40: ch ++; - /* fall through */ - case 0x2c: /* MCSPI_CHCONF */ - return s->ch[ch].config; - - case 0x6c: ch ++; - /* fall through */ - case 0x58: ch ++; - /* fall through */ - case 0x44: ch ++; - /* fall through */ - case 0x30: /* MCSPI_CHSTAT */ - return s->ch[ch].status; - - case 0x70: ch ++; - /* fall through */ - case 0x5c: ch ++; - /* fall through */ - case 0x48: ch ++; - /* fall through */ - case 0x34: /* MCSPI_CHCTRL */ - return s->ch[ch].control; - - case 0x74: ch ++; - /* fall through */ - case 0x60: ch ++; - /* fall through */ - case 0x4c: ch ++; - /* fall through */ - case 0x38: /* MCSPI_TX */ - return s->ch[ch].tx; - - case 0x78: ch ++; - /* fall through */ - case 0x64: ch ++; - /* fall through */ - case 0x50: ch ++; - /* fall through */ - case 0x3c: /* MCSPI_RX */ - s->ch[ch].status &= ~(1 << 0); /* RXS */ - ret = s->ch[ch].rx; - omap_mcspi_transfer_run(s, ch); - return ret; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_mcspi_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque; - int ch = 0; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x00: /* MCSPI_REVISION */ - case 0x14: /* MCSPI_SYSSTATUS */ - case 0x30: /* MCSPI_CHSTAT0 */ - case 0x3c: /* MCSPI_RX0 */ - case 0x44: /* MCSPI_CHSTAT1 */ - case 0x50: /* MCSPI_RX1 */ - case 0x58: /* MCSPI_CHSTAT2 */ - case 0x64: /* MCSPI_RX2 */ - case 0x6c: /* MCSPI_CHSTAT3 */ - case 0x78: /* MCSPI_RX3 */ - OMAP_RO_REG(addr); - return; - - case 0x10: /* MCSPI_SYSCONFIG */ - if (value & (1 << 1)) /* SOFTRESET */ - omap_mcspi_reset(s); - s->sysconfig = value & 0x31d; - break; - - case 0x18: /* MCSPI_IRQSTATUS */ - if (!((s->control & (1 << 3)) && (s->systest & (1 << 11)))) { - s->irqst &= ~value; - omap_mcspi_interrupt_update(s); - } - break; - - case 0x1c: /* MCSPI_IRQENABLE */ - s->irqen = value & 0x1777f; - omap_mcspi_interrupt_update(s); - break; - - case 0x20: /* MCSPI_WAKEUPENABLE */ - s->wken = value & 1; - break; - - case 0x24: /* MCSPI_SYST */ - if (s->control & (1 << 3)) /* SYSTEM_TEST */ - if (value & (1 << 11)) { /* SSB */ - s->irqst |= 0x1777f; - omap_mcspi_interrupt_update(s); - } - s->systest = value & 0xfff; - break; - - case 0x28: /* MCSPI_MODULCTRL */ - if (value & (1 << 3)) /* SYSTEM_TEST */ - if (s->systest & (1 << 11)) { /* SSB */ - s->irqst |= 0x1777f; - omap_mcspi_interrupt_update(s); - } - s->control = value & 0xf; - break; - - case 0x68: ch ++; - /* fall through */ - case 0x54: ch ++; - /* fall through */ - case 0x40: ch ++; - /* fall through */ - case 0x2c: /* MCSPI_CHCONF */ - if ((value ^ s->ch[ch].config) & (3 << 14)) /* DMAR | DMAW */ - omap_mcspi_dmarequest_update(s->ch + ch); - if (((value >> 12) & 3) == 3) /* TRM */ - fprintf(stderr, "%s: invalid TRM value (3)\n", __FUNCTION__); - if (((value >> 7) & 0x1f) < 3) /* WL */ - fprintf(stderr, "%s: invalid WL value (%" PRIx64 ")\n", - __FUNCTION__, (value >> 7) & 0x1f); - s->ch[ch].config = value & 0x7fffff; - break; - - case 0x70: ch ++; - /* fall through */ - case 0x5c: ch ++; - /* fall through */ - case 0x48: ch ++; - /* fall through */ - case 0x34: /* MCSPI_CHCTRL */ - if (value & ~s->ch[ch].control & 1) { /* EN */ - s->ch[ch].control |= 1; - omap_mcspi_transfer_run(s, ch); - } else - s->ch[ch].control = value & 1; - break; - - case 0x74: ch ++; - /* fall through */ - case 0x60: ch ++; - /* fall through */ - case 0x4c: ch ++; - /* fall through */ - case 0x38: /* MCSPI_TX */ - s->ch[ch].tx = value; - s->ch[ch].status &= ~(1 << 1); /* TXS */ - omap_mcspi_transfer_run(s, ch); - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_mcspi_ops = { - .read = omap_mcspi_read, - .write = omap_mcspi_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -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 = g_new0(struct omap_mcspi_s, 1); - struct omap_mcspi_ch_s *ch = s->ch; - - s->irq = irq; - s->chnum = chnum; - while (chnum --) { - ch->txdrq = *drq ++; - ch->rxdrq = *drq ++; - ch ++; - } - omap_mcspi_reset(s); - - memory_region_init_io(&s->iomem, NULL, &omap_mcspi_ops, s, "omap.mcspi", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &s->iomem); - - return s; -} - -void omap_mcspi_attach(struct omap_mcspi_s *s, - uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque, - int chipselect) -{ - if (chipselect < 0 || chipselect >= s->chnum) - hw_error("%s: Bad chipselect %i\n", __FUNCTION__, chipselect); - - s->ch[chipselect].txrx = txrx; - s->ch[chipselect].opaque = opaque; -} diff --git a/qemu/hw/ssi/pl022.c b/qemu/hw/ssi/pl022.c deleted file mode 100644 index 564a0d36e..000000000 --- a/qemu/hw/ssi/pl022.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Arm PrimeCell PL022 Synchronous Serial Port - * - * Copyright (c) 2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "hw/ssi/ssi.h" - -//#define DEBUG_PL022 1 - -#ifdef DEBUG_PL022 -#define DPRINTF(fmt, ...) \ -do { printf("pl022: " fmt , ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__);} while (0) -#endif - -#define PL022_CR1_LBM 0x01 -#define PL022_CR1_SSE 0x02 -#define PL022_CR1_MS 0x04 -#define PL022_CR1_SDO 0x08 - -#define PL022_SR_TFE 0x01 -#define PL022_SR_TNF 0x02 -#define PL022_SR_RNE 0x04 -#define PL022_SR_RFF 0x08 -#define PL022_SR_BSY 0x10 - -#define PL022_INT_ROR 0x01 -#define PL022_INT_RT 0x04 -#define PL022_INT_RX 0x04 -#define PL022_INT_TX 0x08 - -#define TYPE_PL022 "pl022" -#define PL022(obj) OBJECT_CHECK(PL022State, (obj), TYPE_PL022) - -typedef struct PL022State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t cr0; - uint32_t cr1; - uint32_t bitmask; - uint32_t sr; - uint32_t cpsr; - uint32_t is; - uint32_t im; - /* The FIFO head points to the next empty entry. */ - int tx_fifo_head; - int rx_fifo_head; - int tx_fifo_len; - int rx_fifo_len; - uint16_t tx_fifo[8]; - uint16_t rx_fifo[8]; - qemu_irq irq; - SSIBus *ssi; -} PL022State; - -static const unsigned char pl022_id[8] = - { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; - -static void pl022_update(PL022State *s) -{ - s->sr = 0; - if (s->tx_fifo_len == 0) - s->sr |= PL022_SR_TFE; - if (s->tx_fifo_len != 8) - s->sr |= PL022_SR_TNF; - if (s->rx_fifo_len != 0) - s->sr |= PL022_SR_RNE; - if (s->rx_fifo_len == 8) - s->sr |= PL022_SR_RFF; - if (s->tx_fifo_len) - s->sr |= PL022_SR_BSY; - s->is = 0; - if (s->rx_fifo_len >= 4) - s->is |= PL022_INT_RX; - if (s->tx_fifo_len <= 4) - s->is |= PL022_INT_TX; - - qemu_set_irq(s->irq, (s->is & s->im) != 0); -} - -static void pl022_xfer(PL022State *s) -{ - int i; - int o; - int val; - - if ((s->cr1 & PL022_CR1_SSE) == 0) { - pl022_update(s); - DPRINTF("Disabled\n"); - return; - } - - DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len); - i = (s->tx_fifo_head - s->tx_fifo_len) & 7; - o = s->rx_fifo_head; - /* ??? We do not emulate the line speed. - This may break some applications. The are two problematic cases: - (a) A driver feeds data into the TX FIFO until it is full, - and only then drains the RX FIFO. On real hardware the CPU can - feed data fast enough that the RX fifo never gets chance to overflow. - (b) A driver transmits data, deliberately allowing the RX FIFO to - overflow because it ignores the RX data anyway. - - We choose to support (a) by stalling the transmit engine if it would - cause the RX FIFO to overflow. In practice much transmit-only code - falls into (a) because it flushes the RX FIFO to determine when - the transfer has completed. */ - while (s->tx_fifo_len && s->rx_fifo_len < 8) { - DPRINTF("xfer\n"); - val = s->tx_fifo[i]; - if (s->cr1 & PL022_CR1_LBM) { - /* Loopback mode. */ - } else { - val = ssi_transfer(s->ssi, val); - } - s->rx_fifo[o] = val & s->bitmask; - i = (i + 1) & 7; - o = (o + 1) & 7; - s->tx_fifo_len--; - s->rx_fifo_len++; - } - s->rx_fifo_head = o; - pl022_update(s); -} - -static uint64_t pl022_read(void *opaque, hwaddr offset, - unsigned size) -{ - PL022State *s = (PL022State *)opaque; - int val; - - if (offset >= 0xfe0 && offset < 0x1000) { - return pl022_id[(offset - 0xfe0) >> 2]; - } - switch (offset) { - case 0x00: /* CR0 */ - return s->cr0; - case 0x04: /* CR1 */ - return s->cr1; - case 0x08: /* DR */ - if (s->rx_fifo_len) { - val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7]; - DPRINTF("RX %02x\n", val); - s->rx_fifo_len--; - pl022_xfer(s); - } else { - val = 0; - } - return val; - case 0x0c: /* SR */ - return s->sr; - case 0x10: /* CPSR */ - return s->cpsr; - case 0x14: /* IMSC */ - return s->im; - case 0x18: /* RIS */ - return s->is; - case 0x1c: /* MIS */ - return s->im & s->is; - case 0x20: /* DMACR */ - /* Not implemented. */ - return 0; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pl022_read: Bad offset %x\n", (int)offset); - return 0; - } -} - -static void pl022_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PL022State *s = (PL022State *)opaque; - - switch (offset) { - case 0x00: /* CR0 */ - s->cr0 = value; - /* Clock rate and format are ignored. */ - s->bitmask = (1 << ((value & 15) + 1)) - 1; - break; - case 0x04: /* CR1 */ - s->cr1 = value; - if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE)) - == (PL022_CR1_MS | PL022_CR1_SSE)) { - BADF("SPI slave mode not implemented\n"); - } - pl022_xfer(s); - break; - case 0x08: /* DR */ - if (s->tx_fifo_len < 8) { - DPRINTF("TX %02x\n", (unsigned)value); - s->tx_fifo[s->tx_fifo_head] = value & s->bitmask; - s->tx_fifo_head = (s->tx_fifo_head + 1) & 7; - s->tx_fifo_len++; - pl022_xfer(s); - } - break; - case 0x10: /* CPSR */ - /* Prescaler. Ignored. */ - s->cpsr = value & 0xff; - break; - case 0x14: /* IMSC */ - s->im = value; - pl022_update(s); - break; - case 0x20: /* DMACR */ - if (value) { - qemu_log_mask(LOG_UNIMP, "pl022: DMA not implemented\n"); - } - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pl022_write: Bad offset %x\n", (int)offset); - } -} - -static void pl022_reset(PL022State *s) -{ - s->rx_fifo_len = 0; - s->tx_fifo_len = 0; - s->im = 0; - s->is = PL022_INT_TX; - s->sr = PL022_SR_TFE | PL022_SR_TNF; -} - -static const MemoryRegionOps pl022_ops = { - .read = pl022_read, - .write = pl022_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int pl022_post_load(void *opaque, int version_id) -{ - PL022State *s = opaque; - - if (s->tx_fifo_head < 0 || - s->tx_fifo_head >= ARRAY_SIZE(s->tx_fifo) || - s->rx_fifo_head < 0 || - s->rx_fifo_head >= ARRAY_SIZE(s->rx_fifo)) { - return -1; - } - return 0; -} - -static const VMStateDescription vmstate_pl022 = { - .name = "pl022_ssp", - .version_id = 1, - .minimum_version_id = 1, - .post_load = pl022_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(cr0, PL022State), - VMSTATE_UINT32(cr1, PL022State), - VMSTATE_UINT32(bitmask, PL022State), - VMSTATE_UINT32(sr, PL022State), - VMSTATE_UINT32(cpsr, PL022State), - VMSTATE_UINT32(is, PL022State), - VMSTATE_UINT32(im, PL022State), - VMSTATE_INT32(tx_fifo_head, PL022State), - VMSTATE_INT32(rx_fifo_head, PL022State), - VMSTATE_INT32(tx_fifo_len, PL022State), - VMSTATE_INT32(rx_fifo_len, PL022State), - VMSTATE_UINT16(tx_fifo[0], PL022State), - VMSTATE_UINT16(rx_fifo[0], PL022State), - VMSTATE_UINT16(tx_fifo[1], PL022State), - VMSTATE_UINT16(rx_fifo[1], PL022State), - VMSTATE_UINT16(tx_fifo[2], PL022State), - VMSTATE_UINT16(rx_fifo[2], PL022State), - VMSTATE_UINT16(tx_fifo[3], PL022State), - VMSTATE_UINT16(rx_fifo[3], PL022State), - VMSTATE_UINT16(tx_fifo[4], PL022State), - VMSTATE_UINT16(rx_fifo[4], PL022State), - VMSTATE_UINT16(tx_fifo[5], PL022State), - VMSTATE_UINT16(rx_fifo[5], PL022State), - VMSTATE_UINT16(tx_fifo[6], PL022State), - VMSTATE_UINT16(rx_fifo[6], PL022State), - VMSTATE_UINT16(tx_fifo[7], PL022State), - VMSTATE_UINT16(rx_fifo[7], PL022State), - VMSTATE_END_OF_LIST() - } -}; - -static int pl022_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - PL022State *s = PL022(dev); - - memory_region_init_io(&s->iomem, OBJECT(s), &pl022_ops, s, "pl022", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - s->ssi = ssi_create_bus(dev, "ssi"); - pl022_reset(s); - vmstate_register(dev, -1, &vmstate_pl022, s); - return 0; -} - -static void pl022_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = pl022_init; -} - -static const TypeInfo pl022_info = { - .name = TYPE_PL022, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PL022State), - .class_init = pl022_class_init, -}; - -static void pl022_register_types(void) -{ - type_register_static(&pl022_info); -} - -type_init(pl022_register_types) diff --git a/qemu/hw/ssi/ssi.c b/qemu/hw/ssi/ssi.c deleted file mode 100644 index 9791c0d94..000000000 --- a/qemu/hw/ssi/ssi.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - * QEMU Synchronous Serial Interface support - * - * Copyright (c) 2009 CodeSourcery. - * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com) - * Copyright (c) 2012 PetaLogix Pty Ltd. - * Written by Paul Brook - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include "qemu/osdep.h" -#include "hw/ssi/ssi.h" - -struct SSIBus { - BusState parent_obj; -}; - -#define TYPE_SSI_BUS "SSI" -#define SSI_BUS(obj) OBJECT_CHECK(SSIBus, (obj), TYPE_SSI_BUS) - -static const TypeInfo ssi_bus_info = { - .name = TYPE_SSI_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(SSIBus), -}; - -static void ssi_cs_default(void *opaque, int n, int level) -{ - SSISlave *s = SSI_SLAVE(opaque); - bool cs = !!level; - assert(n == 0); - if (s->cs != cs) { - SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s); - if (ssc->set_cs) { - ssc->set_cs(s, cs); - } - } - s->cs = cs; -} - -static uint32_t ssi_transfer_raw_default(SSISlave *dev, uint32_t val) -{ - SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(dev); - - if ((dev->cs && ssc->cs_polarity == SSI_CS_HIGH) || - (!dev->cs && ssc->cs_polarity == SSI_CS_LOW) || - ssc->cs_polarity == SSI_CS_NONE) { - return ssc->transfer(dev, val); - } - return 0; -} - -static int ssi_slave_init(DeviceState *dev) -{ - SSISlave *s = SSI_SLAVE(dev); - SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s); - - if (ssc->transfer_raw == ssi_transfer_raw_default && - ssc->cs_polarity != SSI_CS_NONE) { - qdev_init_gpio_in_named(dev, ssi_cs_default, SSI_GPIO_CS, 1); - } - - return ssc->init(s); -} - -static void ssi_slave_class_init(ObjectClass *klass, void *data) -{ - SSISlaveClass *ssc = SSI_SLAVE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->init = ssi_slave_init; - dc->bus_type = TYPE_SSI_BUS; - if (!ssc->transfer_raw) { - ssc->transfer_raw = ssi_transfer_raw_default; - } -} - -static const TypeInfo ssi_slave_info = { - .name = TYPE_SSI_SLAVE, - .parent = TYPE_DEVICE, - .class_init = ssi_slave_class_init, - .class_size = sizeof(SSISlaveClass), - .abstract = true, -}; - -DeviceState *ssi_create_slave_no_init(SSIBus *bus, const char *name) -{ - return qdev_create(BUS(bus), name); -} - -DeviceState *ssi_create_slave(SSIBus *bus, const char *name) -{ - DeviceState *dev = ssi_create_slave_no_init(bus, name); - - qdev_init_nofail(dev); - return dev; -} - -SSIBus *ssi_create_bus(DeviceState *parent, const char *name) -{ - BusState *bus; - bus = qbus_create(TYPE_SSI_BUS, parent, name); - return SSI_BUS(bus); -} - -uint32_t ssi_transfer(SSIBus *bus, uint32_t val) -{ - BusState *b = BUS(bus); - BusChild *kid; - SSISlaveClass *ssc; - uint32_t r = 0; - - QTAILQ_FOREACH(kid, &b->children, sibling) { - SSISlave *slave = SSI_SLAVE(kid->child); - ssc = SSI_SLAVE_GET_CLASS(slave); - r |= ssc->transfer_raw(slave, val); - } - - return r; -} - -const VMStateDescription vmstate_ssi_slave = { - .name = "SSISlave", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_BOOL(cs, SSISlave), - VMSTATE_END_OF_LIST() - } -}; - -static void ssi_slave_register_types(void) -{ - type_register_static(&ssi_bus_info); - type_register_static(&ssi_slave_info); -} - -type_init(ssi_slave_register_types) - -typedef struct SSIAutoConnectArg { - qemu_irq **cs_linep; - SSIBus *bus; -} SSIAutoConnectArg; - -static int ssi_auto_connect_slave(Object *child, void *opaque) -{ - SSIAutoConnectArg *arg = opaque; - SSISlave *dev = (SSISlave *)object_dynamic_cast(child, TYPE_SSI_SLAVE); - qemu_irq cs_line; - - if (!dev) { - return 0; - } - - cs_line = qdev_get_gpio_in_named(DEVICE(dev), SSI_GPIO_CS, 0); - qdev_set_parent_bus(DEVICE(dev), BUS(arg->bus)); - **arg->cs_linep = cs_line; - (*arg->cs_linep)++; - return 0; -} - -void ssi_auto_connect_slaves(DeviceState *parent, qemu_irq *cs_line, - SSIBus *bus) -{ - SSIAutoConnectArg arg = { - .cs_linep = &cs_line, - .bus = bus - }; - - object_child_foreach(OBJECT(parent), ssi_auto_connect_slave, &arg); -} diff --git a/qemu/hw/ssi/xilinx_spi.c b/qemu/hw/ssi/xilinx_spi.c deleted file mode 100644 index 33482f04d..000000000 --- a/qemu/hw/ssi/xilinx_spi.c +++ /dev/null @@ -1,391 +0,0 @@ -/* - * QEMU model of the Xilinx SPI Controller - * - * Copyright (C) 2010 Edgar E. Iglesias. - * Copyright (C) 2012 Peter A. G. Crosthwaite - * Copyright (C) 2012 PetaLogix - * - * 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/sysbus.h" -#include "sysemu/sysemu.h" -#include "qemu/log.h" -#include "qemu/fifo8.h" - -#include "hw/ssi/ssi.h" - -#ifdef XILINX_SPI_ERR_DEBUG -#define DB_PRINT(...) do { \ - fprintf(stderr, ": %s: ", __func__); \ - fprintf(stderr, ## __VA_ARGS__); \ - } while (0); -#else - #define DB_PRINT(...) -#endif - -#define R_DGIER (0x1c / 4) -#define R_DGIER_IE (1 << 31) - -#define R_IPISR (0x20 / 4) -#define IRQ_DRR_NOT_EMPTY (1 << (31 - 23)) -#define IRQ_DRR_OVERRUN (1 << (31 - 26)) -#define IRQ_DRR_FULL (1 << (31 - 27)) -#define IRQ_TX_FF_HALF_EMPTY (1 << 6) -#define IRQ_DTR_UNDERRUN (1 << 3) -#define IRQ_DTR_EMPTY (1 << (31 - 29)) - -#define R_IPIER (0x28 / 4) -#define R_SRR (0x40 / 4) -#define R_SPICR (0x60 / 4) -#define R_SPICR_TXFF_RST (1 << 5) -#define R_SPICR_RXFF_RST (1 << 6) -#define R_SPICR_MTI (1 << 8) - -#define R_SPISR (0x64 / 4) -#define SR_TX_FULL (1 << 3) -#define SR_TX_EMPTY (1 << 2) -#define SR_RX_FULL (1 << 1) -#define SR_RX_EMPTY (1 << 0) - -#define R_SPIDTR (0x68 / 4) -#define R_SPIDRR (0x6C / 4) -#define R_SPISSR (0x70 / 4) -#define R_TX_FF_OCY (0x74 / 4) -#define R_RX_FF_OCY (0x78 / 4) -#define R_MAX (0x7C / 4) - -#define FIFO_CAPACITY 256 - -#define TYPE_XILINX_SPI "xlnx.xps-spi" -#define XILINX_SPI(obj) OBJECT_CHECK(XilinxSPI, (obj), TYPE_XILINX_SPI) - -typedef struct XilinxSPI { - SysBusDevice parent_obj; - - MemoryRegion mmio; - - qemu_irq irq; - int irqline; - - uint8_t num_cs; - qemu_irq *cs_lines; - - SSIBus *spi; - - Fifo8 rx_fifo; - Fifo8 tx_fifo; - - uint32_t regs[R_MAX]; -} XilinxSPI; - -static void txfifo_reset(XilinxSPI *s) -{ - fifo8_reset(&s->tx_fifo); - - s->regs[R_SPISR] &= ~SR_TX_FULL; - s->regs[R_SPISR] |= SR_TX_EMPTY; -} - -static void rxfifo_reset(XilinxSPI *s) -{ - fifo8_reset(&s->rx_fifo); - - s->regs[R_SPISR] |= SR_RX_EMPTY; - s->regs[R_SPISR] &= ~SR_RX_FULL; -} - -static void xlx_spi_update_cs(XilinxSPI *s) -{ - int i; - - for (i = 0; i < s->num_cs; ++i) { - qemu_set_irq(s->cs_lines[i], !(~s->regs[R_SPISSR] & 1 << i)); - } -} - -static void xlx_spi_update_irq(XilinxSPI *s) -{ - uint32_t pending; - - s->regs[R_IPISR] |= - (!fifo8_is_empty(&s->rx_fifo) ? IRQ_DRR_NOT_EMPTY : 0) | - (fifo8_is_full(&s->rx_fifo) ? IRQ_DRR_FULL : 0); - - pending = s->regs[R_IPISR] & s->regs[R_IPIER]; - - pending = pending && (s->regs[R_DGIER] & R_DGIER_IE); - pending = !!pending; - - /* This call lies right in the data paths so don't call the - irq chain unless things really changed. */ - if (pending != s->irqline) { - s->irqline = pending; - DB_PRINT("irq_change of state %d ISR:%x IER:%X\n", - pending, s->regs[R_IPISR], s->regs[R_IPIER]); - qemu_set_irq(s->irq, pending); - } - -} - -static void xlx_spi_do_reset(XilinxSPI *s) -{ - memset(s->regs, 0, sizeof s->regs); - - rxfifo_reset(s); - txfifo_reset(s); - - s->regs[R_SPISSR] = ~0; - xlx_spi_update_irq(s); - xlx_spi_update_cs(s); -} - -static void xlx_spi_reset(DeviceState *d) -{ - xlx_spi_do_reset(XILINX_SPI(d)); -} - -static inline int spi_master_enabled(XilinxSPI *s) -{ - return !(s->regs[R_SPICR] & R_SPICR_MTI); -} - -static void spi_flush_txfifo(XilinxSPI *s) -{ - uint32_t tx; - uint32_t rx; - - while (!fifo8_is_empty(&s->tx_fifo)) { - tx = (uint32_t)fifo8_pop(&s->tx_fifo); - DB_PRINT("data tx:%x\n", tx); - rx = ssi_transfer(s->spi, tx); - DB_PRINT("data rx:%x\n", rx); - if (fifo8_is_full(&s->rx_fifo)) { - s->regs[R_IPISR] |= IRQ_DRR_OVERRUN; - } else { - fifo8_push(&s->rx_fifo, (uint8_t)rx); - if (fifo8_is_full(&s->rx_fifo)) { - s->regs[R_SPISR] |= SR_RX_FULL; - s->regs[R_IPISR] |= IRQ_DRR_FULL; - } - } - - s->regs[R_SPISR] &= ~SR_RX_EMPTY; - s->regs[R_SPISR] &= ~SR_TX_FULL; - s->regs[R_SPISR] |= SR_TX_EMPTY; - - s->regs[R_IPISR] |= IRQ_DTR_EMPTY; - s->regs[R_IPISR] |= IRQ_DRR_NOT_EMPTY; - } - -} - -static uint64_t -spi_read(void *opaque, hwaddr addr, unsigned int size) -{ - XilinxSPI *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_SPIDRR: - if (fifo8_is_empty(&s->rx_fifo)) { - DB_PRINT("Read from empty FIFO!\n"); - return 0xdeadbeef; - } - - s->regs[R_SPISR] &= ~SR_RX_FULL; - r = fifo8_pop(&s->rx_fifo); - if (fifo8_is_empty(&s->rx_fifo)) { - s->regs[R_SPISR] |= SR_RX_EMPTY; - } - break; - - case R_SPISR: - r = s->regs[addr]; - break; - - default: - if (addr < ARRAY_SIZE(s->regs)) { - r = s->regs[addr]; - } - break; - - } - DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr * 4, r); - xlx_spi_update_irq(s); - return r; -} - -static void -spi_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - XilinxSPI *s = opaque; - uint32_t value = val64; - - DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr, value); - addr >>= 2; - switch (addr) { - case R_SRR: - if (value != 0xa) { - DB_PRINT("Invalid write to SRR %x\n", value); - } else { - xlx_spi_do_reset(s); - } - break; - - case R_SPIDTR: - s->regs[R_SPISR] &= ~SR_TX_EMPTY; - fifo8_push(&s->tx_fifo, (uint8_t)value); - if (fifo8_is_full(&s->tx_fifo)) { - s->regs[R_SPISR] |= SR_TX_FULL; - } - if (!spi_master_enabled(s)) { - goto done; - } else { - DB_PRINT("DTR and master enabled\n"); - } - spi_flush_txfifo(s); - break; - - case R_SPISR: - DB_PRINT("Invalid write to SPISR %x\n", value); - break; - - case R_IPISR: - /* Toggle the bits. */ - s->regs[addr] ^= value; - break; - - /* Slave Select Register. */ - case R_SPISSR: - s->regs[addr] = value; - xlx_spi_update_cs(s); - break; - - case R_SPICR: - /* FIXME: reset irq and sr state to empty queues. */ - if (value & R_SPICR_RXFF_RST) { - rxfifo_reset(s); - } - - if (value & R_SPICR_TXFF_RST) { - txfifo_reset(s); - } - value &= ~(R_SPICR_RXFF_RST | R_SPICR_TXFF_RST); - s->regs[addr] = value; - - if (!(value & R_SPICR_MTI)) { - spi_flush_txfifo(s); - } - break; - - default: - if (addr < ARRAY_SIZE(s->regs)) { - s->regs[addr] = value; - } - break; - } - -done: - xlx_spi_update_irq(s); -} - -static const MemoryRegionOps spi_ops = { - .read = spi_read, - .write = spi_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static int xilinx_spi_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - XilinxSPI *s = XILINX_SPI(dev); - int i; - - DB_PRINT("\n"); - - s->spi = ssi_create_bus(dev, "spi"); - - sysbus_init_irq(sbd, &s->irq); - s->cs_lines = g_new0(qemu_irq, s->num_cs); - ssi_auto_connect_slaves(dev, s->cs_lines, s->spi); - for (i = 0; i < s->num_cs; ++i) { - sysbus_init_irq(sbd, &s->cs_lines[i]); - } - - memory_region_init_io(&s->mmio, OBJECT(s), &spi_ops, s, - "xilinx-spi", R_MAX * 4); - sysbus_init_mmio(sbd, &s->mmio); - - s->irqline = -1; - - fifo8_create(&s->tx_fifo, FIFO_CAPACITY); - fifo8_create(&s->rx_fifo, FIFO_CAPACITY); - - return 0; -} - -static const VMStateDescription vmstate_xilinx_spi = { - .name = "xilinx_spi", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_FIFO8(tx_fifo, XilinxSPI), - VMSTATE_FIFO8(rx_fifo, XilinxSPI), - VMSTATE_UINT32_ARRAY(regs, XilinxSPI, R_MAX), - VMSTATE_END_OF_LIST() - } -}; - -static Property xilinx_spi_properties[] = { - DEFINE_PROP_UINT8("num-ss-bits", XilinxSPI, num_cs, 1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void xilinx_spi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = xilinx_spi_init; - dc->reset = xlx_spi_reset; - dc->props = xilinx_spi_properties; - dc->vmsd = &vmstate_xilinx_spi; -} - -static const TypeInfo xilinx_spi_info = { - .name = TYPE_XILINX_SPI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(XilinxSPI), - .class_init = xilinx_spi_class_init, -}; - -static void xilinx_spi_register_types(void) -{ - type_register_static(&xilinx_spi_info); -} - -type_init(xilinx_spi_register_types) diff --git a/qemu/hw/ssi/xilinx_spips.c b/qemu/hw/ssi/xilinx_spips.c deleted file mode 100644 index e2b77dc3d..000000000 --- a/qemu/hw/ssi/xilinx_spips.c +++ /dev/null @@ -1,734 +0,0 @@ -/* - * QEMU model of the Xilinx Zynq SPI controller - * - * Copyright (c) 2012 Peter A. G. Crosthwaite - * - * 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/sysbus.h" -#include "sysemu/sysemu.h" -#include "hw/ptimer.h" -#include "qemu/log.h" -#include "qemu/fifo8.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 -#endif - -#define DB_PRINT_L(level, ...) do { \ - if (XILINX_SPIPS_ERR_DEBUG > (level)) { \ - fprintf(stderr, ": %s: ", __func__); \ - fprintf(stderr, ## __VA_ARGS__); \ - } \ -} while (0); - -/* config register */ -#define R_CONFIG (0x00 / 4) -#define IFMODE (1U << 31) -#define ENDIAN (1 << 26) -#define MODEFAIL_GEN_EN (1 << 17) -#define MAN_START_COM (1 << 16) -#define MAN_START_EN (1 << 15) -#define MANUAL_CS (1 << 14) -#define CS (0xF << 10) -#define CS_SHIFT (10) -#define PERI_SEL (1 << 9) -#define REF_CLK (1 << 8) -#define FIFO_WIDTH (3 << 6) -#define BAUD_RATE_DIV (7 << 3) -#define CLK_PH (1 << 2) -#define CLK_POL (1 << 1) -#define MODE_SEL (1 << 0) -#define R_CONFIG_RSVD (0x7bf40000) - -/* interrupt mechanism */ -#define R_INTR_STATUS (0x04 / 4) -#define R_INTR_EN (0x08 / 4) -#define R_INTR_DIS (0x0C / 4) -#define R_INTR_MASK (0x10 / 4) -#define IXR_TX_FIFO_UNDERFLOW (1 << 6) -#define IXR_RX_FIFO_FULL (1 << 5) -#define IXR_RX_FIFO_NOT_EMPTY (1 << 4) -#define IXR_TX_FIFO_FULL (1 << 3) -#define IXR_TX_FIFO_NOT_FULL (1 << 2) -#define IXR_TX_FIFO_MODE_FAIL (1 << 1) -#define IXR_RX_FIFO_OVERFLOW (1 << 0) -#define IXR_ALL ((IXR_TX_FIFO_UNDERFLOW<<1)-1) - -#define R_EN (0x14 / 4) -#define R_DELAY (0x18 / 4) -#define R_TX_DATA (0x1C / 4) -#define R_RX_DATA (0x20 / 4) -#define R_SLAVE_IDLE_COUNT (0x24 / 4) -#define R_TX_THRES (0x28 / 4) -#define R_RX_THRES (0x2C / 4) -#define R_TXD1 (0x80 / 4) -#define R_TXD2 (0x84 / 4) -#define R_TXD3 (0x88 / 4) - -#define R_LQSPI_CFG (0xa0 / 4) -#define R_LQSPI_CFG_RESET 0x03A002EB -#define LQSPI_CFG_LQ_MODE (1U << 31) -#define LQSPI_CFG_TWO_MEM (1 << 30) -#define LQSPI_CFG_SEP_BUS (1 << 30) -#define LQSPI_CFG_U_PAGE (1 << 28) -#define LQSPI_CFG_MODE_EN (1 << 25) -#define LQSPI_CFG_MODE_WIDTH 8 -#define LQSPI_CFG_MODE_SHIFT 16 -#define LQSPI_CFG_DUMMY_WIDTH 3 -#define LQSPI_CFG_DUMMY_SHIFT 8 -#define LQSPI_CFG_INST_CODE 0xFF - -#define R_LQSPI_STS (0xA4 / 4) -#define LQSPI_STS_WR_RECVD (1 << 1) - -#define R_MOD_ID (0xFC / 4) - -/* size of TXRX FIFOs */ -#define RXFF_A 32 -#define TXFF_A 32 - -#define RXFF_A_Q (64 * 4) -#define TXFF_A_Q (64 * 4) - -/* 16MB per linear region */ -#define LQSPI_ADDRESS_BITS 24 -/* Bite off 4k chunks at a time */ -#define LQSPI_CACHE_SIZE 1024 - -#define SNOOP_CHECKING 0xFF -#define SNOOP_NONE 0xFE -#define SNOOP_STRIPING 0 - -typedef enum { - READ = 0x3, - FAST_READ = 0xb, - DOR = 0x3b, - QOR = 0x6b, - DIOR = 0xbb, - QIOR = 0xeb, - - PP = 0x2, - DPP = 0xa2, - QPP = 0x32, -} FlashCMD; - -typedef struct { - XilinxSPIPS parent_obj; - - uint8_t lqspi_buf[LQSPI_CACHE_SIZE]; - hwaddr lqspi_cached_addr; -} XilinxQSPIPS; - -typedef struct XilinxSPIPSClass { - SysBusDeviceClass parent_class; - - const MemoryRegionOps *reg_ops; - - uint32_t rx_fifo_size; - uint32_t tx_fifo_size; -} XilinxSPIPSClass; - -static inline int num_effective_busses(XilinxSPIPS *s) -{ - return (s->regs[R_LQSPI_CFG] & LQSPI_CFG_SEP_BUS && - s->regs[R_LQSPI_CFG] & LQSPI_CFG_TWO_MEM) ? s->num_busses : 1; -} - -static inline bool xilinx_spips_cs_is_set(XilinxSPIPS *s, int i, int field) -{ - return ~field & (1 << i) && (s->regs[R_CONFIG] & MANUAL_CS - || !fifo8_is_empty(&s->tx_fifo)); -} - -static void xilinx_spips_update_cs_lines(XilinxSPIPS *s) -{ - int i, j; - bool found = false; - int field = s->regs[R_CONFIG] >> CS_SHIFT; - - for (i = 0; i < s->num_cs; i++) { - for (j = 0; j < num_effective_busses(s); j++) { - int upage = !!(s->regs[R_LQSPI_STS] & LQSPI_CFG_U_PAGE); - int cs_to_set = (j * s->num_cs + i + upage) % - (s->num_cs * s->num_busses); - - if (xilinx_spips_cs_is_set(s, i, field) && !found) { - DB_PRINT_L(0, "selecting slave %d\n", i); - qemu_set_irq(s->cs_lines[cs_to_set], 0); - } else { - DB_PRINT_L(0, "deselecting slave %d\n", i); - qemu_set_irq(s->cs_lines[cs_to_set], 1); - } - } - if (xilinx_spips_cs_is_set(s, i, field)) { - found = true; - } - } - if (!found) { - s->snoop_state = SNOOP_CHECKING; - DB_PRINT_L(1, "moving to snoop check state\n"); - } -} - -static void xilinx_spips_update_ixr(XilinxSPIPS *s) -{ - if (s->regs[R_LQSPI_CFG] & LQSPI_CFG_LQ_MODE) { - return; - } - /* These are set/cleared as they occur */ - s->regs[R_INTR_STATUS] &= (IXR_TX_FIFO_UNDERFLOW | IXR_RX_FIFO_OVERFLOW | - IXR_TX_FIFO_MODE_FAIL); - /* these are pure functions of fifo state, set them here */ - s->regs[R_INTR_STATUS] |= - (fifo8_is_full(&s->rx_fifo) ? IXR_RX_FIFO_FULL : 0) | - (s->rx_fifo.num >= s->regs[R_RX_THRES] ? IXR_RX_FIFO_NOT_EMPTY : 0) | - (fifo8_is_full(&s->tx_fifo) ? IXR_TX_FIFO_FULL : 0) | - (s->tx_fifo.num < s->regs[R_TX_THRES] ? IXR_TX_FIFO_NOT_FULL : 0); - /* drive external interrupt pin */ - int new_irqline = !!(s->regs[R_INTR_MASK] & s->regs[R_INTR_STATUS] & - IXR_ALL); - if (new_irqline != s->irqline) { - s->irqline = new_irqline; - qemu_set_irq(s->irq, s->irqline); - } -} - -static void xilinx_spips_reset(DeviceState *d) -{ - XilinxSPIPS *s = XILINX_SPIPS(d); - - int i; - for (i = 0; i < XLNX_SPIPS_R_MAX; i++) { - s->regs[i] = 0; - } - - fifo8_reset(&s->rx_fifo); - fifo8_reset(&s->rx_fifo); - /* non zero resets */ - s->regs[R_CONFIG] |= MODEFAIL_GEN_EN; - s->regs[R_SLAVE_IDLE_COUNT] = 0xFF; - s->regs[R_TX_THRES] = 1; - s->regs[R_RX_THRES] = 1; - /* FIXME: move magic number definition somewhere sensible */ - s->regs[R_MOD_ID] = 0x01090106; - s->regs[R_LQSPI_CFG] = R_LQSPI_CFG_RESET; - s->snoop_state = SNOOP_CHECKING; - xilinx_spips_update_ixr(s); - xilinx_spips_update_cs_lines(s); -} - -/* N way (num) in place bit striper. Lay out row wise bits (LSB to MSB) - * column wise (from element 0 to N-1). num is the length of x, and dir - * reverses the direction of the transform. Best illustrated by example: - * Each digit in the below array is a single bit (num == 3): - * - * {{ 76543210, } ----- stripe (dir == false) -----> {{ FCheb630, } - * { hgfedcba, } { GDAfc741, } - * { HGFEDCBA, }} <---- upstripe (dir == true) ----- { HEBgda52, }} - */ - -static inline void stripe8(uint8_t *x, int num, bool dir) -{ - uint8_t r[num]; - memset(r, 0, sizeof(uint8_t) * num); - int idx[2] = {0, 0}; - int bit[2] = {0, 0}; - int d = dir; - - for (idx[0] = 0; idx[0] < num; ++idx[0]) { - for (bit[0] = 0; bit[0] < 8; ++bit[0]) { - r[idx[d]] |= x[idx[!d]] & 1 << bit[!d] ? 1 << bit[d] : 0; - idx[1] = (idx[1] + 1) % num; - if (!idx[1]) { - bit[1]++; - } - } - } - memcpy(x, r, sizeof(uint8_t) * num); -} - -static void xilinx_spips_flush_txfifo(XilinxSPIPS *s) -{ - int debug_level = 0; - - for (;;) { - int i; - uint8_t tx = 0; - uint8_t tx_rx[num_effective_busses(s)]; - - if (fifo8_is_empty(&s->tx_fifo)) { - if (!(s->regs[R_LQSPI_CFG] & LQSPI_CFG_LQ_MODE)) { - s->regs[R_INTR_STATUS] |= IXR_TX_FIFO_UNDERFLOW; - } - xilinx_spips_update_ixr(s); - return; - } else if (s->snoop_state == SNOOP_STRIPING) { - for (i = 0; i < num_effective_busses(s); ++i) { - tx_rx[i] = fifo8_pop(&s->tx_fifo); - } - stripe8(tx_rx, num_effective_busses(s), false); - } else { - tx = fifo8_pop(&s->tx_fifo); - for (i = 0; i < num_effective_busses(s); ++i) { - tx_rx[i] = tx; - } - } - - for (i = 0; i < num_effective_busses(s); ++i) { - DB_PRINT_L(debug_level, "tx = %02x\n", tx_rx[i]); - tx_rx[i] = ssi_transfer(s->spi[i], (uint32_t)tx_rx[i]); - DB_PRINT_L(debug_level, "rx = %02x\n", tx_rx[i]); - } - - if (fifo8_is_full(&s->rx_fifo)) { - s->regs[R_INTR_STATUS] |= IXR_RX_FIFO_OVERFLOW; - DB_PRINT_L(0, "rx FIFO overflow"); - } else if (s->snoop_state == SNOOP_STRIPING) { - stripe8(tx_rx, num_effective_busses(s), true); - for (i = 0; i < num_effective_busses(s); ++i) { - fifo8_push(&s->rx_fifo, (uint8_t)tx_rx[i]); - } - } else { - fifo8_push(&s->rx_fifo, (uint8_t)tx_rx[0]); - } - - DB_PRINT_L(debug_level, "initial snoop state: %x\n", - (unsigned)s->snoop_state); - switch (s->snoop_state) { - case (SNOOP_CHECKING): - switch (tx) { /* new instruction code */ - case READ: /* 3 address bytes, no dummy bytes/cycles */ - case PP: - case DPP: - case QPP: - s->snoop_state = 3; - break; - case FAST_READ: /* 3 address bytes, 1 dummy byte */ - case DOR: - case QOR: - case DIOR: /* FIXME: these vary between vendor - set to spansion */ - s->snoop_state = 4; - break; - case QIOR: /* 3 address bytes, 2 dummy bytes */ - s->snoop_state = 6; - break; - default: - s->snoop_state = SNOOP_NONE; - } - break; - case (SNOOP_STRIPING): - case (SNOOP_NONE): - /* Once we hit the boring stuff - squelch debug noise */ - if (!debug_level) { - DB_PRINT_L(0, "squelching debug info ....\n"); - debug_level = 1; - } - break; - default: - s->snoop_state--; - } - DB_PRINT_L(debug_level, "final snoop state: %x\n", - (unsigned)s->snoop_state); - } -} - -static inline void rx_data_bytes(XilinxSPIPS *s, uint8_t *value, int max) -{ - int i; - - for (i = 0; i < max && !fifo8_is_empty(&s->rx_fifo); ++i) { - value[i] = fifo8_pop(&s->rx_fifo); - } -} - -static uint64_t xilinx_spips_read(void *opaque, hwaddr addr, - unsigned size) -{ - XilinxSPIPS *s = opaque; - uint32_t mask = ~0; - uint32_t ret; - uint8_t rx_buf[4]; - - addr >>= 2; - switch (addr) { - case R_CONFIG: - mask = ~(R_CONFIG_RSVD | MAN_START_COM); - break; - case R_INTR_STATUS: - ret = s->regs[addr] & IXR_ALL; - s->regs[addr] = 0; - DB_PRINT_L(0, "addr=" TARGET_FMT_plx " = %x\n", addr * 4, ret); - return ret; - case R_INTR_MASK: - mask = IXR_ALL; - break; - case R_EN: - mask = 0x1; - break; - case R_SLAVE_IDLE_COUNT: - mask = 0xFF; - break; - case R_MOD_ID: - mask = 0x01FFFFFF; - break; - case R_INTR_EN: - case R_INTR_DIS: - case R_TX_DATA: - mask = 0; - break; - case R_RX_DATA: - memset(rx_buf, 0, sizeof(rx_buf)); - rx_data_bytes(s, rx_buf, s->num_txrx_bytes); - ret = s->regs[R_CONFIG] & ENDIAN ? cpu_to_be32(*(uint32_t *)rx_buf) - : cpu_to_le32(*(uint32_t *)rx_buf); - DB_PRINT_L(0, "addr=" TARGET_FMT_plx " = %x\n", addr * 4, ret); - xilinx_spips_update_ixr(s); - return ret; - } - DB_PRINT_L(0, "addr=" TARGET_FMT_plx " = %x\n", addr * 4, - s->regs[addr] & mask); - return s->regs[addr] & mask; - -} - -static inline void tx_data_bytes(XilinxSPIPS *s, uint32_t value, int num) -{ - int i; - for (i = 0; i < num && !fifo8_is_full(&s->tx_fifo); ++i) { - if (s->regs[R_CONFIG] & ENDIAN) { - fifo8_push(&s->tx_fifo, (uint8_t)(value >> 24)); - value <<= 8; - } else { - fifo8_push(&s->tx_fifo, (uint8_t)value); - value >>= 8; - } - } -} - -static void xilinx_spips_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - int mask = ~0; - int man_start_com = 0; - XilinxSPIPS *s = opaque; - - DB_PRINT_L(0, "addr=" TARGET_FMT_plx " = %x\n", addr, (unsigned)value); - addr >>= 2; - switch (addr) { - case R_CONFIG: - mask = ~(R_CONFIG_RSVD | MAN_START_COM); - if (value & MAN_START_COM) { - man_start_com = 1; - } - break; - case R_INTR_STATUS: - mask = IXR_ALL; - s->regs[R_INTR_STATUS] &= ~(mask & value); - goto no_reg_update; - case R_INTR_DIS: - mask = IXR_ALL; - s->regs[R_INTR_MASK] &= ~(mask & value); - goto no_reg_update; - case R_INTR_EN: - mask = IXR_ALL; - s->regs[R_INTR_MASK] |= mask & value; - goto no_reg_update; - case R_EN: - mask = 0x1; - break; - case R_SLAVE_IDLE_COUNT: - mask = 0xFF; - break; - case R_RX_DATA: - case R_INTR_MASK: - case R_MOD_ID: - mask = 0; - break; - case R_TX_DATA: - tx_data_bytes(s, (uint32_t)value, s->num_txrx_bytes); - goto no_reg_update; - case R_TXD1: - tx_data_bytes(s, (uint32_t)value, 1); - goto no_reg_update; - case R_TXD2: - tx_data_bytes(s, (uint32_t)value, 2); - goto no_reg_update; - case R_TXD3: - tx_data_bytes(s, (uint32_t)value, 3); - goto no_reg_update; - } - s->regs[addr] = (s->regs[addr] & ~mask) | (value & mask); -no_reg_update: - xilinx_spips_update_cs_lines(s); - if ((man_start_com && s->regs[R_CONFIG] & MAN_START_EN) || - (fifo8_is_empty(&s->tx_fifo) && s->regs[R_CONFIG] & MAN_START_EN)) { - xilinx_spips_flush_txfifo(s); - } - xilinx_spips_update_cs_lines(s); - xilinx_spips_update_ixr(s); -} - -static const MemoryRegionOps spips_ops = { - .read = xilinx_spips_read, - .write = xilinx_spips_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void xilinx_qspips_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - XilinxQSPIPS *q = XILINX_QSPIPS(opaque); - - xilinx_spips_write(opaque, addr, value, size); - addr >>= 2; - - if (addr == R_LQSPI_CFG) { - q->lqspi_cached_addr = ~0ULL; - } -} - -static const MemoryRegionOps qspips_ops = { - .read = xilinx_spips_read, - .write = xilinx_qspips_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -#define LQSPI_CACHE_SIZE 1024 - -static uint64_t -lqspi_read(void *opaque, hwaddr addr, unsigned int size) -{ - int i; - XilinxQSPIPS *q = opaque; - XilinxSPIPS *s = opaque; - uint32_t ret; - - if (addr >= q->lqspi_cached_addr && - addr <= q->lqspi_cached_addr + LQSPI_CACHE_SIZE - 4) { - uint8_t *retp = &q->lqspi_buf[addr - q->lqspi_cached_addr]; - ret = cpu_to_le32(*(uint32_t *)retp); - DB_PRINT_L(1, "addr: %08x, data: %08x\n", (unsigned)addr, - (unsigned)ret); - return ret; - } else { - int flash_addr = (addr / num_effective_busses(s)); - int slave = flash_addr >> LQSPI_ADDRESS_BITS; - int cache_entry = 0; - uint32_t u_page_save = s->regs[R_LQSPI_STS] & ~LQSPI_CFG_U_PAGE; - - s->regs[R_LQSPI_STS] &= ~LQSPI_CFG_U_PAGE; - s->regs[R_LQSPI_STS] |= slave ? LQSPI_CFG_U_PAGE : 0; - - DB_PRINT_L(0, "config reg status: %08x\n", s->regs[R_LQSPI_CFG]); - - fifo8_reset(&s->tx_fifo); - fifo8_reset(&s->rx_fifo); - - /* instruction */ - DB_PRINT_L(0, "pushing read instruction: %02x\n", - (unsigned)(uint8_t)(s->regs[R_LQSPI_CFG] & - LQSPI_CFG_INST_CODE)); - fifo8_push(&s->tx_fifo, s->regs[R_LQSPI_CFG] & LQSPI_CFG_INST_CODE); - /* read address */ - DB_PRINT_L(0, "pushing read address %06x\n", flash_addr); - fifo8_push(&s->tx_fifo, (uint8_t)(flash_addr >> 16)); - fifo8_push(&s->tx_fifo, (uint8_t)(flash_addr >> 8)); - fifo8_push(&s->tx_fifo, (uint8_t)flash_addr); - /* mode bits */ - if (s->regs[R_LQSPI_CFG] & LQSPI_CFG_MODE_EN) { - fifo8_push(&s->tx_fifo, extract32(s->regs[R_LQSPI_CFG], - LQSPI_CFG_MODE_SHIFT, - LQSPI_CFG_MODE_WIDTH)); - } - /* dummy bytes */ - for (i = 0; i < (extract32(s->regs[R_LQSPI_CFG], LQSPI_CFG_DUMMY_SHIFT, - LQSPI_CFG_DUMMY_WIDTH)); ++i) { - DB_PRINT_L(0, "pushing dummy byte\n"); - fifo8_push(&s->tx_fifo, 0); - } - xilinx_spips_update_cs_lines(s); - xilinx_spips_flush_txfifo(s); - fifo8_reset(&s->rx_fifo); - - DB_PRINT_L(0, "starting QSPI data read\n"); - - while (cache_entry < LQSPI_CACHE_SIZE) { - for (i = 0; i < 64; ++i) { - tx_data_bytes(s, 0, 1); - } - xilinx_spips_flush_txfifo(s); - for (i = 0; i < 64; ++i) { - rx_data_bytes(s, &q->lqspi_buf[cache_entry++], 1); - } - } - - s->regs[R_LQSPI_STS] &= ~LQSPI_CFG_U_PAGE; - s->regs[R_LQSPI_STS] |= u_page_save; - xilinx_spips_update_cs_lines(s); - - q->lqspi_cached_addr = flash_addr * num_effective_busses(s); - return lqspi_read(opaque, addr, size); - } -} - -static const MemoryRegionOps lqspi_ops = { - .read = lqspi_read, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 4 - } -}; - -static void xilinx_spips_realize(DeviceState *dev, Error **errp) -{ - XilinxSPIPS *s = XILINX_SPIPS(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - XilinxSPIPSClass *xsc = XILINX_SPIPS_GET_CLASS(s); - int i; - - DB_PRINT_L(0, "realized spips\n"); - - s->spi = g_new(SSIBus *, s->num_busses); - for (i = 0; i < s->num_busses; ++i) { - char bus_name[16]; - snprintf(bus_name, 16, "spi%d", i); - s->spi[i] = ssi_create_bus(dev, bus_name); - } - - s->cs_lines = g_new0(qemu_irq, s->num_cs * s->num_busses); - ssi_auto_connect_slaves(DEVICE(s), s->cs_lines, s->spi[0]); - ssi_auto_connect_slaves(DEVICE(s), s->cs_lines, s->spi[1]); - sysbus_init_irq(sbd, &s->irq); - for (i = 0; i < s->num_cs * s->num_busses; ++i) { - sysbus_init_irq(sbd, &s->cs_lines[i]); - } - - memory_region_init_io(&s->iomem, OBJECT(s), xsc->reg_ops, s, - "spi", XLNX_SPIPS_R_MAX * 4); - sysbus_init_mmio(sbd, &s->iomem); - - s->irqline = -1; - - fifo8_create(&s->rx_fifo, xsc->rx_fifo_size); - fifo8_create(&s->tx_fifo, xsc->tx_fifo_size); -} - -static void xilinx_qspips_realize(DeviceState *dev, Error **errp) -{ - XilinxSPIPS *s = XILINX_SPIPS(dev); - XilinxQSPIPS *q = XILINX_QSPIPS(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - - DB_PRINT_L(0, "realized qspips\n"); - - s->num_busses = 2; - s->num_cs = 2; - s->num_txrx_bytes = 4; - - xilinx_spips_realize(dev, errp); - memory_region_init_io(&s->mmlqspi, OBJECT(s), &lqspi_ops, s, "lqspi", - (1 << LQSPI_ADDRESS_BITS) * 2); - sysbus_init_mmio(sbd, &s->mmlqspi); - - q->lqspi_cached_addr = ~0ULL; -} - -static int xilinx_spips_post_load(void *opaque, int version_id) -{ - xilinx_spips_update_ixr((XilinxSPIPS *)opaque); - xilinx_spips_update_cs_lines((XilinxSPIPS *)opaque); - return 0; -} - -static const VMStateDescription vmstate_xilinx_spips = { - .name = "xilinx_spips", - .version_id = 2, - .minimum_version_id = 2, - .post_load = xilinx_spips_post_load, - .fields = (VMStateField[]) { - VMSTATE_FIFO8(tx_fifo, XilinxSPIPS), - VMSTATE_FIFO8(rx_fifo, XilinxSPIPS), - VMSTATE_UINT32_ARRAY(regs, XilinxSPIPS, XLNX_SPIPS_R_MAX), - VMSTATE_UINT8(snoop_state, XilinxSPIPS), - VMSTATE_END_OF_LIST() - } -}; - -static Property xilinx_spips_properties[] = { - DEFINE_PROP_UINT8("num-busses", XilinxSPIPS, num_busses, 1), - DEFINE_PROP_UINT8("num-ss-bits", XilinxSPIPS, num_cs, 4), - DEFINE_PROP_UINT8("num-txrx-bytes", XilinxSPIPS, num_txrx_bytes, 1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void xilinx_qspips_class_init(ObjectClass *klass, void * data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - XilinxSPIPSClass *xsc = XILINX_SPIPS_CLASS(klass); - - dc->realize = xilinx_qspips_realize; - xsc->reg_ops = &qspips_ops; - xsc->rx_fifo_size = RXFF_A_Q; - xsc->tx_fifo_size = TXFF_A_Q; -} - -static void xilinx_spips_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - XilinxSPIPSClass *xsc = XILINX_SPIPS_CLASS(klass); - - dc->realize = xilinx_spips_realize; - dc->reset = xilinx_spips_reset; - dc->props = xilinx_spips_properties; - dc->vmsd = &vmstate_xilinx_spips; - - xsc->reg_ops = &spips_ops; - xsc->rx_fifo_size = RXFF_A; - xsc->tx_fifo_size = TXFF_A; -} - -static const TypeInfo xilinx_spips_info = { - .name = TYPE_XILINX_SPIPS, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(XilinxSPIPS), - .class_init = xilinx_spips_class_init, - .class_size = sizeof(XilinxSPIPSClass), -}; - -static const TypeInfo xilinx_qspips_info = { - .name = TYPE_XILINX_QSPIPS, - .parent = TYPE_XILINX_SPIPS, - .instance_size = sizeof(XilinxQSPIPS), - .class_init = xilinx_qspips_class_init, -}; - -static void xilinx_spips_register_types(void) -{ - type_register_static(&xilinx_spips_info); - type_register_static(&xilinx_qspips_info); -} - -type_init(xilinx_spips_register_types) diff --git a/qemu/hw/timer/Makefile.objs b/qemu/hw/timer/Makefile.objs deleted file mode 100644 index 003c14fa2..000000000 --- a/qemu/hw/timer/Makefile.objs +++ /dev/null @@ -1,35 +0,0 @@ -common-obj-$(CONFIG_ARM_TIMER) += arm_timer.o -common-obj-$(CONFIG_ARM_MPTIMER) += arm_mptimer.o -common-obj-$(CONFIG_A9_GTIMER) += a9gtimer.o -common-obj-$(CONFIG_CADENCE) += cadence_ttc.o -common-obj-$(CONFIG_DS1338) += ds1338.o -common-obj-$(CONFIG_HPET) += hpet.o -common-obj-$(CONFIG_I8254) += i8254_common.o i8254.o -common-obj-$(CONFIG_M48T59) += m48t59.o -common-obj-$(CONFIG_PL031) += pl031.o -common-obj-$(CONFIG_PUV3) += puv3_ost.o -common-obj-$(CONFIG_TWL92230) += twl92230.o -common-obj-$(CONFIG_XILINX) += xilinx_timer.o -common-obj-$(CONFIG_SLAVIO) += slavio_timer.o -common-obj-$(CONFIG_ETRAXFS) += etraxfs_timer.o -common-obj-$(CONFIG_GRLIB) += grlib_gptimer.o -common-obj-$(CONFIG_IMX) += imx_epit.o -common-obj-$(CONFIG_IMX) += imx_gpt.o -common-obj-$(CONFIG_LM32) += lm32_timer.o -common-obj-$(CONFIG_MILKYMIST) += milkymist-sysctl.o - -obj-$(CONFIG_EXYNOS4) += exynos4210_mct.o -obj-$(CONFIG_EXYNOS4) += exynos4210_pwm.o -obj-$(CONFIG_EXYNOS4) += exynos4210_rtc.o -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_DIGIC) += digic-timer.o - -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 deleted file mode 100644 index afe577c76..000000000 --- a/qemu/hw/timer/a9gtimer.c +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Global peripheral timer block for ARM A9MP - * - * (C) 2013 Xilinx Inc. - * - * Written by François LEGAL - * Written by Peter Crosthwaite - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "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 -#endif - -#define DB_PRINT_L(level, ...) do { \ - if (A9_GTIMER_ERR_DEBUG > (level)) { \ - fprintf(stderr, ": %s: ", __func__); \ - fprintf(stderr, ## __VA_ARGS__); \ - } \ -} while (0); - -#define DB_PRINT(...) DB_PRINT_L(0, ## __VA_ARGS__) - -static inline int a9_gtimer_get_current_cpu(A9GTimerState *s) -{ - if (current_cpu->cpu_index >= s->num_cpu) { - hw_error("a9gtimer: num-cpu %d but this cpu is %d!\n", - s->num_cpu, current_cpu->cpu_index); - } - return current_cpu->cpu_index; -} - -static inline uint64_t a9_gtimer_get_conv(A9GTimerState *s) -{ - uint64_t prescale = extract32(s->control, R_CONTROL_PRESCALER_SHIFT, - R_CONTROL_PRESCALER_LEN); - - return (prescale + 1) * 10; -} - -static A9GTimerUpdate a9_gtimer_get_update(A9GTimerState *s) -{ - A9GTimerUpdate ret; - - ret.now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ret.new = s->ref_counter + - (ret.now - s->cpu_ref_time) / a9_gtimer_get_conv(s); - return ret; -} - -static void a9_gtimer_update(A9GTimerState *s, bool sync) -{ - - A9GTimerUpdate update = a9_gtimer_get_update(s); - int i; - int64_t next_cdiff = 0; - - for (i = 0; i < s->num_cpu; ++i) { - A9GTimerPerCPU *gtb = &s->per_cpu[i]; - int64_t cdiff = 0; - - if ((s->control & R_CONTROL_TIMER_ENABLE) && - (gtb->control & R_CONTROL_COMP_ENABLE)) { - /* R2p0+, where the compare function is >= */ - while (gtb->compare < update.new) { - DB_PRINT("Compare event happened for CPU %d\n", i); - gtb->status = 1; - if (gtb->control & R_CONTROL_AUTO_INCREMENT) { - DB_PRINT("Auto incrementing timer compare by %" PRId32 "\n", - gtb->inc); - gtb->compare += gtb->inc; - } else { - break; - } - } - cdiff = (int64_t)gtb->compare - (int64_t)update.new + 1; - if (cdiff > 0 && (cdiff < next_cdiff || !next_cdiff)) { - next_cdiff = cdiff; - } - } - - qemu_set_irq(gtb->irq, - gtb->status && (gtb->control & R_CONTROL_IRQ_ENABLE)); - } - - timer_del(s->timer); - if (next_cdiff) { - DB_PRINT("scheduling qemu_timer to fire again in %" - PRIx64 " cycles\n", next_cdiff); - timer_mod(s->timer, update.now + next_cdiff * a9_gtimer_get_conv(s)); - } - - if (s->control & R_CONTROL_TIMER_ENABLE) { - s->counter = update.new; - } - - if (sync) { - s->cpu_ref_time = update.now; - s->ref_counter = s->counter; - } -} - -static void a9_gtimer_update_no_sync(void *opaque) -{ - A9GTimerState *s = A9_GTIMER(opaque); - - a9_gtimer_update(s, false); -} - -static uint64_t a9_gtimer_read(void *opaque, hwaddr addr, unsigned size) -{ - A9GTimerPerCPU *gtb = (A9GTimerPerCPU *)opaque; - A9GTimerState *s = gtb->parent; - A9GTimerUpdate update; - uint64_t ret = 0; - int shift = 0; - - switch (addr) { - case R_COUNTER_HI: - shift = 32; - /* fallthrough */ - case R_COUNTER_LO: - update = a9_gtimer_get_update(s); - ret = extract64(update.new, shift, 32); - break; - case R_CONTROL: - ret = s->control | gtb->control; - break; - case R_INTERRUPT_STATUS: - ret = gtb->status; - break; - case R_COMPARATOR_HI: - shift = 32; - /* fallthrough */ - case R_COMPARATOR_LO: - ret = extract64(gtb->compare, shift, 32); - break; - case R_AUTO_INCREMENT: - ret = gtb->inc; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "bad a9gtimer register: %x\n", - (unsigned)addr); - return 0; - } - - DB_PRINT("addr:%#x data:%#08" PRIx64 "\n", (unsigned)addr, ret); - return ret; -} - -static void a9_gtimer_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - A9GTimerPerCPU *gtb = (A9GTimerPerCPU *)opaque; - A9GTimerState *s = gtb->parent; - int shift = 0; - - DB_PRINT("addr:%#x data:%#08" PRIx64 "\n", (unsigned)addr, value); - - switch (addr) { - case R_COUNTER_HI: - shift = 32; - /* fallthrough */ - case R_COUNTER_LO: - /* - * Keep it simple - ARM docco explicitly says to disable timer before - * modding it, so dont bother trying to do all the difficult on the fly - * timer modifications - (if they even work in real hardware??). - */ - if (s->control & R_CONTROL_TIMER_ENABLE) { - qemu_log_mask(LOG_GUEST_ERROR, "Cannot mod running ARM gtimer\n"); - return; - } - s->counter = deposit64(s->counter, shift, 32, value); - return; - case R_CONTROL: - a9_gtimer_update(s, (value ^ s->control) & R_CONTROL_NEEDS_SYNC); - gtb->control = value & R_CONTROL_BANKED; - s->control = value & ~R_CONTROL_BANKED; - break; - case R_INTERRUPT_STATUS: - a9_gtimer_update(s, false); - gtb->status &= ~value; - break; - case R_COMPARATOR_HI: - shift = 32; - /* fallthrough */ - case R_COMPARATOR_LO: - a9_gtimer_update(s, false); - gtb->compare = deposit64(gtb->compare, shift, 32, value); - break; - case R_AUTO_INCREMENT: - gtb->inc = value; - return; - default: - return; - } - - a9_gtimer_update(s, false); -} - -/* Wrapper functions to implement the "read global timer for - * the current CPU" memory regions. - */ -static uint64_t a9_gtimer_this_read(void *opaque, hwaddr addr, - unsigned size) -{ - A9GTimerState *s = A9_GTIMER(opaque); - int id = a9_gtimer_get_current_cpu(s); - - /* no \n so concatenates with message from read fn */ - DB_PRINT("CPU:%d:", id); - - return a9_gtimer_read(&s->per_cpu[id], addr, size); -} - -static void a9_gtimer_this_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - A9GTimerState *s = A9_GTIMER(opaque); - int id = a9_gtimer_get_current_cpu(s); - - /* no \n so concatenates with message from write fn */ - DB_PRINT("CPU:%d:", id); - - a9_gtimer_write(&s->per_cpu[id], addr, value, size); -} - -static const MemoryRegionOps a9_gtimer_this_ops = { - .read = a9_gtimer_this_read, - .write = a9_gtimer_this_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const MemoryRegionOps a9_gtimer_ops = { - .read = a9_gtimer_read, - .write = a9_gtimer_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void a9_gtimer_reset(DeviceState *dev) -{ - A9GTimerState *s = A9_GTIMER(dev); - int i; - - s->counter = 0; - s->control = 0; - - for (i = 0; i < s->num_cpu; i++) { - A9GTimerPerCPU *gtb = &s->per_cpu[i]; - - gtb->control = 0; - gtb->status = 0; - gtb->compare = 0; - gtb->inc = 0; - } - a9_gtimer_update(s, false); -} - -static void a9_gtimer_realize(DeviceState *dev, Error **errp) -{ - A9GTimerState *s = A9_GTIMER(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - int i; - - if (s->num_cpu < 1 || s->num_cpu > A9_GTIMER_MAX_CPUS) { - error_setg(errp, "%s: num-cpu must be between 1 and %d", - __func__, A9_GTIMER_MAX_CPUS); - return; - } - - memory_region_init_io(&s->iomem, OBJECT(dev), &a9_gtimer_this_ops, s, - "a9gtimer shared", 0x20); - sysbus_init_mmio(sbd, &s->iomem); - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, a9_gtimer_update_no_sync, s); - - for (i = 0; i < s->num_cpu; i++) { - A9GTimerPerCPU *gtb = &s->per_cpu[i]; - - gtb->parent = s; - sysbus_init_irq(sbd, >b->irq); - memory_region_init_io(>b->iomem, OBJECT(dev), &a9_gtimer_ops, gtb, - "a9gtimer per cpu", 0x20); - sysbus_init_mmio(sbd, >b->iomem); - } -} - -static const VMStateDescription vmstate_a9_gtimer_per_cpu = { - .name = "arm.cortex-a9-global-timer.percpu", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(control, A9GTimerPerCPU), - VMSTATE_UINT64(compare, A9GTimerPerCPU), - VMSTATE_UINT32(status, A9GTimerPerCPU), - VMSTATE_UINT32(inc, A9GTimerPerCPU), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_a9_gtimer = { - .name = "arm.cortex-a9-global-timer", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_TIMER_PTR(timer, A9GTimerState), - VMSTATE_UINT64(counter, A9GTimerState), - VMSTATE_UINT64(ref_counter, A9GTimerState), - VMSTATE_UINT64(cpu_ref_time, A9GTimerState), - VMSTATE_STRUCT_VARRAY_UINT32(per_cpu, A9GTimerState, num_cpu, - 1, vmstate_a9_gtimer_per_cpu, - A9GTimerPerCPU), - VMSTATE_END_OF_LIST() - } -}; - -static Property a9_gtimer_properties[] = { - DEFINE_PROP_UINT32("num-cpu", A9GTimerState, num_cpu, 0), - DEFINE_PROP_END_OF_LIST() -}; - -static void a9_gtimer_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = a9_gtimer_realize; - dc->vmsd = &vmstate_a9_gtimer; - dc->reset = a9_gtimer_reset; - dc->props = a9_gtimer_properties; -} - -static const TypeInfo a9_gtimer_info = { - .name = TYPE_A9_GTIMER, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(A9GTimerState), - .class_init = a9_gtimer_class_init, -}; - -static void a9_gtimer_register_types(void) -{ - type_register_static(&a9_gtimer_info); -} - -type_init(a9_gtimer_register_types) diff --git a/qemu/hw/timer/allwinner-a10-pit.c b/qemu/hw/timer/allwinner-a10-pit.c deleted file mode 100644 index 51cdc98f3..000000000 --- a/qemu/hw/timer/allwinner-a10-pit.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Allwinner A10 timer device emulation - * - * Copyright (C) 2013 Li Guang - * Written by Li Guang - * - * 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. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "sysemu/sysemu.h" -#include "hw/timer/allwinner-a10-pit.h" - -static void a10_pit_update_irq(AwA10PITState *s) -{ - int i; - - for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) { - qemu_set_irq(s->irq[i], !!(s->irq_status & s->irq_enable & (1 << i))); - } -} - -static uint64_t a10_pit_read(void *opaque, hwaddr offset, unsigned size) -{ - AwA10PITState *s = AW_A10_PIT(opaque); - uint8_t index; - - switch (offset) { - case AW_A10_PIT_TIMER_IRQ_EN: - return s->irq_enable; - case AW_A10_PIT_TIMER_IRQ_ST: - return s->irq_status; - case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END: - index = offset & 0xf0; - index >>= 4; - index -= 1; - switch (offset & 0x0f) { - case AW_A10_PIT_TIMER_CONTROL: - return s->control[index]; - case AW_A10_PIT_TIMER_INTERVAL: - return s->interval[index]; - case AW_A10_PIT_TIMER_COUNT: - s->count[index] = ptimer_get_count(s->timer[index]); - return s->count[index]; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset 0x%x\n", __func__, (int)offset); - break; - } - case AW_A10_PIT_WDOG_CONTROL: - break; - case AW_A10_PIT_WDOG_MODE: - break; - case AW_A10_PIT_COUNT_LO: - return s->count_lo; - case AW_A10_PIT_COUNT_HI: - return s->count_hi; - case AW_A10_PIT_COUNT_CTL: - return s->count_ctl; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset 0x%x\n", __func__, (int)offset); - break; - } - - return 0; -} - -static void a10_pit_set_freq(AwA10PITState *s, int index) -{ - uint32_t prescaler, source, source_freq; - - prescaler = 1 << extract32(s->control[index], 4, 3); - source = extract32(s->control[index], 2, 2); - source_freq = s->clk_freq[source]; - - if (source_freq) { - ptimer_set_freq(s->timer[index], source_freq / prescaler); - } else { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid clock source %u\n", - __func__, source); - } -} - -static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value, - unsigned size) -{ - AwA10PITState *s = AW_A10_PIT(opaque); - uint8_t index; - - switch (offset) { - case AW_A10_PIT_TIMER_IRQ_EN: - s->irq_enable = value; - a10_pit_update_irq(s); - break; - case AW_A10_PIT_TIMER_IRQ_ST: - s->irq_status &= ~value; - a10_pit_update_irq(s); - break; - case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END: - index = offset & 0xf0; - index >>= 4; - index -= 1; - switch (offset & 0x0f) { - case AW_A10_PIT_TIMER_CONTROL: - s->control[index] = value; - a10_pit_set_freq(s, index); - if (s->control[index] & AW_A10_PIT_TIMER_RELOAD) { - ptimer_set_count(s->timer[index], s->interval[index]); - } - if (s->control[index] & AW_A10_PIT_TIMER_EN) { - int oneshot = 0; - if (s->control[index] & AW_A10_PIT_TIMER_MODE) { - oneshot = 1; - } - ptimer_run(s->timer[index], oneshot); - } else { - ptimer_stop(s->timer[index]); - } - break; - case AW_A10_PIT_TIMER_INTERVAL: - s->interval[index] = value; - ptimer_set_limit(s->timer[index], s->interval[index], 1); - break; - case AW_A10_PIT_TIMER_COUNT: - s->count[index] = value; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset 0x%x\n", __func__, (int)offset); - } - break; - case AW_A10_PIT_WDOG_CONTROL: - s->watch_dog_control = value; - break; - case AW_A10_PIT_WDOG_MODE: - s->watch_dog_mode = value; - break; - case AW_A10_PIT_COUNT_LO: - s->count_lo = value; - break; - case AW_A10_PIT_COUNT_HI: - s->count_hi = value; - break; - case AW_A10_PIT_COUNT_CTL: - s->count_ctl = value; - if (s->count_ctl & AW_A10_PIT_COUNT_RL_EN) { - uint64_t tmp_count = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - s->count_lo = tmp_count; - s->count_hi = tmp_count >> 32; - s->count_ctl &= ~AW_A10_PIT_COUNT_RL_EN; - } - if (s->count_ctl & AW_A10_PIT_COUNT_CLR_EN) { - s->count_lo = 0; - s->count_hi = 0; - s->count_ctl &= ~AW_A10_PIT_COUNT_CLR_EN; - } - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset 0x%x\n", __func__, (int)offset); - break; - } -} - -static const MemoryRegionOps a10_pit_ops = { - .read = a10_pit_read, - .write = a10_pit_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static Property a10_pit_properties[] = { - DEFINE_PROP_UINT32("clk0-freq", AwA10PITState, clk_freq[0], 0), - DEFINE_PROP_UINT32("clk1-freq", AwA10PITState, clk_freq[1], 0), - DEFINE_PROP_UINT32("clk2-freq", AwA10PITState, clk_freq[2], 0), - DEFINE_PROP_UINT32("clk3-freq", AwA10PITState, clk_freq[3], 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription vmstate_a10_pit = { - .name = "a10.pit", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(irq_enable, AwA10PITState), - VMSTATE_UINT32(irq_status, AwA10PITState), - VMSTATE_UINT32_ARRAY(control, AwA10PITState, AW_A10_PIT_TIMER_NR), - VMSTATE_UINT32_ARRAY(interval, AwA10PITState, AW_A10_PIT_TIMER_NR), - VMSTATE_UINT32_ARRAY(count, AwA10PITState, AW_A10_PIT_TIMER_NR), - VMSTATE_UINT32(watch_dog_mode, AwA10PITState), - VMSTATE_UINT32(watch_dog_control, AwA10PITState), - VMSTATE_UINT32(count_lo, AwA10PITState), - VMSTATE_UINT32(count_hi, AwA10PITState), - VMSTATE_UINT32(count_ctl, AwA10PITState), - VMSTATE_PTIMER_ARRAY(timer, AwA10PITState, AW_A10_PIT_TIMER_NR), - VMSTATE_END_OF_LIST() - } -}; - -static void a10_pit_reset(DeviceState *dev) -{ - AwA10PITState *s = AW_A10_PIT(dev); - uint8_t i; - - s->irq_enable = 0; - s->irq_status = 0; - a10_pit_update_irq(s); - - for (i = 0; i < 6; i++) { - s->control[i] = AW_A10_PIT_DEFAULT_CLOCK; - s->interval[i] = 0; - s->count[i] = 0; - ptimer_stop(s->timer[i]); - a10_pit_set_freq(s, i); - } - s->watch_dog_mode = 0; - s->watch_dog_control = 0; - s->count_lo = 0; - s->count_hi = 0; - s->count_ctl = 0; -} - -static void a10_pit_timer_cb(void *opaque) -{ - AwA10TimerContext *tc = opaque; - AwA10PITState *s = tc->container; - uint8_t i = tc->index; - - if (s->control[i] & AW_A10_PIT_TIMER_EN) { - s->irq_status |= 1 << i; - if (s->control[i] & AW_A10_PIT_TIMER_MODE) { - ptimer_stop(s->timer[i]); - s->control[i] &= ~AW_A10_PIT_TIMER_EN; - } - a10_pit_update_irq(s); - } -} - -static void a10_pit_init(Object *obj) -{ - AwA10PITState *s = AW_A10_PIT(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - QEMUBH * bh[AW_A10_PIT_TIMER_NR]; - uint8_t i; - - for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) { - sysbus_init_irq(sbd, &s->irq[i]); - } - memory_region_init_io(&s->iomem, OBJECT(s), &a10_pit_ops, s, - TYPE_AW_A10_PIT, 0x400); - sysbus_init_mmio(sbd, &s->iomem); - - for (i = 0; i < AW_A10_PIT_TIMER_NR; i++) { - AwA10TimerContext *tc = &s->timer_context[i]; - - tc->container = s; - tc->index = i; - bh[i] = qemu_bh_new(a10_pit_timer_cb, tc); - s->timer[i] = ptimer_init(bh[i]); - } -} - -static void a10_pit_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = a10_pit_reset; - dc->props = a10_pit_properties; - dc->desc = "allwinner a10 timer"; - dc->vmsd = &vmstate_a10_pit; -} - -static const TypeInfo a10_pit_info = { - .name = TYPE_AW_A10_PIT, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(AwA10PITState), - .instance_init = a10_pit_init, - .class_init = a10_pit_class_init, -}; - -static void a10_register_types(void) -{ - type_register_static(&a10_pit_info); -} - -type_init(a10_register_types); diff --git a/qemu/hw/timer/arm_mptimer.c b/qemu/hw/timer/arm_mptimer.c deleted file mode 100644 index d66bbf01b..000000000 --- a/qemu/hw/timer/arm_mptimer.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Private peripheral timer/watchdog blocks for ARM 11MPCore and A9MP - * - * Copyright (c) 2006-2007 CodeSourcery. - * Copyright (c) 2011 Linaro Limited - * Written by Paul Brook, 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 . - */ - -#include "qemu/osdep.h" -#include "hw/timer/arm_mptimer.h" -#include "qapi/error.h" -#include "qemu/timer.h" -#include "qom/cpu.h" - -/* This device implements the per-cpu private timer and watchdog block - * which is used in both the ARM11MPCore and Cortex-A9MP. - */ - -static inline int get_current_cpu(ARMMPTimerState *s) -{ - if (current_cpu->cpu_index >= s->num_cpu) { - hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n", - s->num_cpu, current_cpu->cpu_index); - } - return current_cpu->cpu_index; -} - -static inline void timerblock_update_irq(TimerBlock *tb) -{ - qemu_set_irq(tb->irq, tb->status && (tb->control & 4)); -} - -/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */ -static inline uint32_t timerblock_scale(TimerBlock *tb) -{ - return (((tb->control >> 8) & 0xff) + 1) * 10; -} - -static void timerblock_reload(TimerBlock *tb, int restart) -{ - if (tb->count == 0) { - return; - } - if (restart) { - tb->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - } - tb->tick += (int64_t)tb->count * timerblock_scale(tb); - timer_mod(tb->timer, tb->tick); -} - -static void timerblock_tick(void *opaque) -{ - TimerBlock *tb = (TimerBlock *)opaque; - tb->status = 1; - if (tb->control & 2) { - tb->count = tb->load; - timerblock_reload(tb, 0); - } else { - tb->count = 0; - } - timerblock_update_irq(tb); -} - -static uint64_t timerblock_read(void *opaque, hwaddr addr, - unsigned size) -{ - TimerBlock *tb = (TimerBlock *)opaque; - int64_t val; - switch (addr) { - case 0: /* Load */ - return tb->load; - case 4: /* Counter. */ - if (((tb->control & 1) == 0) || (tb->count == 0)) { - return 0; - } - /* Slow and ugly, but hopefully won't happen too often. */ - val = tb->tick - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - val /= timerblock_scale(tb); - if (val < 0) { - val = 0; - } - return val; - case 8: /* Control. */ - return tb->control; - case 12: /* Interrupt status. */ - return tb->status; - default: - return 0; - } -} - -static void timerblock_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - TimerBlock *tb = (TimerBlock *)opaque; - int64_t old; - switch (addr) { - case 0: /* Load */ - tb->load = value; - /* Fall through. */ - case 4: /* Counter. */ - if ((tb->control & 1) && tb->count) { - /* Cancel the previous timer. */ - timer_del(tb->timer); - } - tb->count = value; - if (tb->control & 1) { - timerblock_reload(tb, 1); - } - break; - case 8: /* Control. */ - old = tb->control; - tb->control = value; - if (value & 1) { - if ((old & 1) && (tb->count != 0)) { - /* Do nothing if timer is ticking right now. */ - break; - } - if (tb->control & 2) { - tb->count = tb->load; - } - timerblock_reload(tb, 1); - } else if (old & 1) { - /* Shutdown the timer. */ - timer_del(tb->timer); - } - break; - case 12: /* Interrupt status. */ - tb->status &= ~value; - timerblock_update_irq(tb); - break; - } -} - -/* Wrapper functions to implement the "read timer/watchdog for - * the current CPU" memory regions. - */ -static uint64_t arm_thistimer_read(void *opaque, hwaddr addr, - unsigned size) -{ - ARMMPTimerState *s = (ARMMPTimerState *)opaque; - int id = get_current_cpu(s); - return timerblock_read(&s->timerblock[id], addr, size); -} - -static void arm_thistimer_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - ARMMPTimerState *s = (ARMMPTimerState *)opaque; - int id = get_current_cpu(s); - timerblock_write(&s->timerblock[id], addr, value, size); -} - -static const MemoryRegionOps arm_thistimer_ops = { - .read = arm_thistimer_read, - .write = arm_thistimer_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const MemoryRegionOps timerblock_ops = { - .read = timerblock_read, - .write = timerblock_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void timerblock_reset(TimerBlock *tb) -{ - tb->count = 0; - tb->load = 0; - tb->control = 0; - tb->status = 0; - tb->tick = 0; - if (tb->timer) { - timer_del(tb->timer); - } -} - -static void arm_mptimer_reset(DeviceState *dev) -{ - ARMMPTimerState *s = ARM_MPTIMER(dev); - int i; - - for (i = 0; i < ARRAY_SIZE(s->timerblock); i++) { - timerblock_reset(&s->timerblock[i]); - } -} - -static void arm_mptimer_init(Object *obj) -{ - ARMMPTimerState *s = ARM_MPTIMER(obj); - - memory_region_init_io(&s->iomem, obj, &arm_thistimer_ops, s, - "arm_mptimer_timer", 0x20); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); -} - -static void arm_mptimer_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - ARMMPTimerState *s = ARM_MPTIMER(dev); - int i; - - if (s->num_cpu < 1 || s->num_cpu > 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" - * * region 1 is "timer for core 0" - * * region 2 is "timer for core 1" - * and so on. - * The outgoing interrupt lines are - * * timer for core 0 - * * timer for core 1 - * and so on. - */ - for (i = 0; i < s->num_cpu; i++) { - TimerBlock *tb = &s->timerblock[i]; - tb->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, timerblock_tick, tb); - sysbus_init_irq(sbd, &tb->irq); - memory_region_init_io(&tb->iomem, OBJECT(s), &timerblock_ops, tb, - "arm_mptimer_timerblock", 0x20); - sysbus_init_mmio(sbd, &tb->iomem); - } -} - -static const VMStateDescription vmstate_timerblock = { - .name = "arm_mptimer_timerblock", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32(count, TimerBlock), - VMSTATE_UINT32(load, TimerBlock), - VMSTATE_UINT32(control, TimerBlock), - VMSTATE_UINT32(status, TimerBlock), - VMSTATE_INT64(tick, TimerBlock), - VMSTATE_TIMER_PTR(timer, TimerBlock), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_arm_mptimer = { - .name = "arm_mptimer", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_VARRAY_UINT32(timerblock, ARMMPTimerState, num_cpu, - 2, vmstate_timerblock, TimerBlock), - VMSTATE_END_OF_LIST() - } -}; - -static Property arm_mptimer_properties[] = { - DEFINE_PROP_UINT32("num-cpu", ARMMPTimerState, num_cpu, 0), - DEFINE_PROP_END_OF_LIST() -}; - -static void arm_mptimer_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = arm_mptimer_realize; - dc->vmsd = &vmstate_arm_mptimer; - dc->reset = arm_mptimer_reset; - dc->props = arm_mptimer_properties; -} - -static const TypeInfo arm_mptimer_info = { - .name = TYPE_ARM_MPTIMER, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ARMMPTimerState), - .instance_init = arm_mptimer_init, - .class_init = arm_mptimer_class_init, -}; - -static void arm_mptimer_register_types(void) -{ - type_register_static(&arm_mptimer_info); -} - -type_init(arm_mptimer_register_types) diff --git a/qemu/hw/timer/arm_timer.c b/qemu/hw/timer/arm_timer.c deleted file mode 100644 index f1ede5f53..000000000 --- a/qemu/hw/timer/arm_timer.c +++ /dev/null @@ -1,409 +0,0 @@ -/* - * ARM PrimeCell Timer modules. - * - * Copyright (c) 2005-2006 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "qemu/timer.h" -#include "qemu-common.h" -#include "hw/qdev.h" -#include "hw/ptimer.h" -#include "qemu/main-loop.h" - -/* Common timer implementation. */ - -#define TIMER_CTRL_ONESHOT (1 << 0) -#define TIMER_CTRL_32BIT (1 << 1) -#define TIMER_CTRL_DIV1 (0 << 2) -#define TIMER_CTRL_DIV16 (1 << 2) -#define TIMER_CTRL_DIV256 (2 << 2) -#define TIMER_CTRL_IE (1 << 5) -#define TIMER_CTRL_PERIODIC (1 << 6) -#define TIMER_CTRL_ENABLE (1 << 7) - -typedef struct { - ptimer_state *timer; - uint32_t control; - uint32_t limit; - int freq; - int int_level; - qemu_irq irq; -} arm_timer_state; - -/* Check all active timers, and schedule the next timer interrupt. */ - -static void arm_timer_update(arm_timer_state *s) -{ - /* Update interrupts. */ - if (s->int_level && (s->control & TIMER_CTRL_IE)) { - qemu_irq_raise(s->irq); - } else { - qemu_irq_lower(s->irq); - } -} - -static uint32_t arm_timer_read(void *opaque, hwaddr offset) -{ - arm_timer_state *s = (arm_timer_state *)opaque; - - switch (offset >> 2) { - case 0: /* TimerLoad */ - case 6: /* TimerBGLoad */ - return s->limit; - case 1: /* TimerValue */ - return ptimer_get_count(s->timer); - case 2: /* TimerControl */ - return s->control; - case 4: /* TimerRIS */ - return s->int_level; - case 5: /* TimerMIS */ - if ((s->control & TIMER_CTRL_IE) == 0) - return 0; - return s->int_level; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset %x\n", __func__, (int)offset); - return 0; - } -} - -/* Reset the timer limit after settings have changed. */ -static void arm_timer_recalibrate(arm_timer_state *s, int reload) -{ - uint32_t limit; - - if ((s->control & (TIMER_CTRL_PERIODIC | TIMER_CTRL_ONESHOT)) == 0) { - /* Free running. */ - if (s->control & TIMER_CTRL_32BIT) - limit = 0xffffffff; - else - limit = 0xffff; - } else { - /* Periodic. */ - limit = s->limit; - } - ptimer_set_limit(s->timer, limit, reload); -} - -static void arm_timer_write(void *opaque, hwaddr offset, - uint32_t value) -{ - arm_timer_state *s = (arm_timer_state *)opaque; - int freq; - - switch (offset >> 2) { - case 0: /* TimerLoad */ - s->limit = value; - arm_timer_recalibrate(s, 1); - break; - case 1: /* TimerValue */ - /* ??? Linux seems to want to write to this readonly register. - Ignore it. */ - break; - case 2: /* TimerControl */ - if (s->control & TIMER_CTRL_ENABLE) { - /* Pause the timer if it is running. This may cause some - inaccuracy dure to rounding, but avoids a whole lot of other - messyness. */ - ptimer_stop(s->timer); - } - s->control = value; - freq = s->freq; - /* ??? Need to recalculate expiry time after changing divisor. */ - switch ((value >> 2) & 3) { - case 1: freq >>= 4; break; - case 2: freq >>= 8; break; - } - arm_timer_recalibrate(s, s->control & TIMER_CTRL_ENABLE); - ptimer_set_freq(s->timer, freq); - if (s->control & TIMER_CTRL_ENABLE) { - /* Restart the timer if still enabled. */ - ptimer_run(s->timer, (s->control & TIMER_CTRL_ONESHOT) != 0); - } - break; - case 3: /* TimerIntClr */ - s->int_level = 0; - break; - case 6: /* TimerBGLoad */ - s->limit = value; - arm_timer_recalibrate(s, 0); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset %x\n", __func__, (int)offset); - } - arm_timer_update(s); -} - -static void arm_timer_tick(void *opaque) -{ - arm_timer_state *s = (arm_timer_state *)opaque; - s->int_level = 1; - arm_timer_update(s); -} - -static const VMStateDescription vmstate_arm_timer = { - .name = "arm_timer", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(control, arm_timer_state), - VMSTATE_UINT32(limit, arm_timer_state), - VMSTATE_INT32(int_level, arm_timer_state), - VMSTATE_PTIMER(timer, arm_timer_state), - VMSTATE_END_OF_LIST() - } -}; - -static arm_timer_state *arm_timer_init(uint32_t freq) -{ - arm_timer_state *s; - QEMUBH *bh; - - s = (arm_timer_state *)g_malloc0(sizeof(arm_timer_state)); - s->freq = freq; - s->control = TIMER_CTRL_IE; - - bh = qemu_bh_new(arm_timer_tick, s); - s->timer = ptimer_init(bh); - vmstate_register(NULL, -1, &vmstate_arm_timer, s); - return s; -} - -/* ARM PrimeCell SP804 dual timer module. - * Docs at - * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0271d/index.html -*/ - -#define TYPE_SP804 "sp804" -#define SP804(obj) OBJECT_CHECK(SP804State, (obj), TYPE_SP804) - -typedef struct SP804State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - arm_timer_state *timer[2]; - uint32_t freq0, freq1; - int level[2]; - qemu_irq irq; -} SP804State; - -static const uint8_t sp804_ids[] = { - /* Timer ID */ - 0x04, 0x18, 0x14, 0, - /* PrimeCell ID */ - 0xd, 0xf0, 0x05, 0xb1 -}; - -/* Merge the IRQs from the two component devices. */ -static void sp804_set_irq(void *opaque, int irq, int level) -{ - SP804State *s = (SP804State *)opaque; - - s->level[irq] = level; - qemu_set_irq(s->irq, s->level[0] || s->level[1]); -} - -static uint64_t sp804_read(void *opaque, hwaddr offset, - unsigned size) -{ - SP804State *s = (SP804State *)opaque; - - if (offset < 0x20) { - return arm_timer_read(s->timer[0], offset); - } - if (offset < 0x40) { - return arm_timer_read(s->timer[1], offset - 0x20); - } - - /* TimerPeriphID */ - if (offset >= 0xfe0 && offset <= 0xffc) { - return sp804_ids[(offset - 0xfe0) >> 2]; - } - - switch (offset) { - /* Integration Test control registers, which we won't support */ - case 0xf00: /* TimerITCR */ - case 0xf04: /* TimerITOP (strictly write only but..) */ - qemu_log_mask(LOG_UNIMP, - "%s: integration test registers unimplemented\n", - __func__); - return 0; - } - - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset %x\n", __func__, (int)offset); - return 0; -} - -static void sp804_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - SP804State *s = (SP804State *)opaque; - - if (offset < 0x20) { - arm_timer_write(s->timer[0], offset, value); - return; - } - - if (offset < 0x40) { - arm_timer_write(s->timer[1], offset - 0x20, value); - return; - } - - /* Technically we could be writing to the Test Registers, but not likely */ - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %x\n", - __func__, (int)offset); -} - -static const MemoryRegionOps sp804_ops = { - .read = sp804_read, - .write = sp804_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_sp804 = { - .name = "sp804", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32_ARRAY(level, SP804State, 2), - VMSTATE_END_OF_LIST() - } -}; - -static void sp804_init(Object *obj) -{ - 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); -} - -/* Integrator/CP timer module. */ - -#define TYPE_INTEGRATOR_PIT "integrator_pit" -#define INTEGRATOR_PIT(obj) \ - OBJECT_CHECK(icp_pit_state, (obj), TYPE_INTEGRATOR_PIT) - -typedef struct { - SysBusDevice parent_obj; - - MemoryRegion iomem; - arm_timer_state *timer[3]; -} icp_pit_state; - -static uint64_t icp_pit_read(void *opaque, hwaddr offset, - unsigned size) -{ - icp_pit_state *s = (icp_pit_state *)opaque; - int n; - - /* ??? Don't know the PrimeCell ID for this device. */ - n = offset >> 8; - if (n > 2) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad timer %d\n", __func__, n); - return 0; - } - - return arm_timer_read(s->timer[n], offset & 0xff); -} - -static void icp_pit_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - icp_pit_state *s = (icp_pit_state *)opaque; - int n; - - n = offset >> 8; - if (n > 2) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad timer %d\n", __func__, n); - return; - } - - arm_timer_write(s->timer[n], offset & 0xff, value); -} - -static const MemoryRegionOps icp_pit_ops = { - .read = icp_pit_read, - .write = icp_pit_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void icp_pit_init(Object *obj) -{ - 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); - /* The other two timers run at 1MHz. */ - s->timer[1] = arm_timer_init(1000000); - s->timer[2] = arm_timer_init(1000000); - - sysbus_init_irq(dev, &s->timer[0]->irq); - sysbus_init_irq(dev, &s->timer[1]->irq); - sysbus_init_irq(dev, &s->timer[2]->irq); - - 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. */ -} - -static const TypeInfo icp_pit_info = { - .name = TYPE_INTEGRATOR_PIT, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(icp_pit_state), - .instance_init = icp_pit_init, -}; - -static Property sp804_properties[] = { - DEFINE_PROP_UINT32("freq0", SP804State, freq0, 1000000), - DEFINE_PROP_UINT32("freq1", SP804State, freq1, 1000000), - DEFINE_PROP_END_OF_LIST(), -}; - -static void sp804_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - - 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, -}; - -static void arm_timer_register_types(void) -{ - type_register_static(&icp_pit_info); - type_register_static(&sp804_info); -} - -type_init(arm_timer_register_types) diff --git a/qemu/hw/timer/aspeed_timer.c b/qemu/hw/timer/aspeed_timer.c deleted file mode 100644 index 51e8303cd..000000000 --- a/qemu/hw/timer/aspeed_timer.c +++ /dev/null @@ -1,449 +0,0 @@ -/* - * ASPEED AST2400 Timer - * - * Andrew Jeffery - * - * 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 deleted file mode 100644 index 03f5b9c20..000000000 --- a/qemu/hw/timer/cadence_ttc.c +++ /dev/null @@ -1,492 +0,0 @@ -/* - * Xilinx Zynq cadence TTC model - * - * Copyright (c) 2011 Xilinx Inc. - * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com) - * Copyright (c) 2012 PetaLogix Pty Ltd. - * Written By Haibing Ma - * M. Habib - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "qemu/timer.h" - -#ifdef CADENCE_TTC_ERR_DEBUG -#define DB_PRINT(...) do { \ - fprintf(stderr, ": %s: ", __func__); \ - fprintf(stderr, ## __VA_ARGS__); \ - } while (0); -#else - #define DB_PRINT(...) -#endif - -#define COUNTER_INTR_IV 0x00000001 -#define COUNTER_INTR_M1 0x00000002 -#define COUNTER_INTR_M2 0x00000004 -#define COUNTER_INTR_M3 0x00000008 -#define COUNTER_INTR_OV 0x00000010 -#define COUNTER_INTR_EV 0x00000020 - -#define COUNTER_CTRL_DIS 0x00000001 -#define COUNTER_CTRL_INT 0x00000002 -#define COUNTER_CTRL_DEC 0x00000004 -#define COUNTER_CTRL_MATCH 0x00000008 -#define COUNTER_CTRL_RST 0x00000010 - -#define CLOCK_CTRL_PS_EN 0x00000001 -#define CLOCK_CTRL_PS_V 0x0000001e - -typedef struct { - QEMUTimer *timer; - int freq; - - uint32_t reg_clock; - uint32_t reg_count; - uint32_t reg_value; - uint16_t reg_interval; - uint16_t reg_match[3]; - uint32_t reg_intr; - uint32_t reg_intr_en; - uint32_t reg_event_ctrl; - uint32_t reg_event; - - uint64_t cpu_time; - unsigned int cpu_time_valid; - - qemu_irq irq; -} CadenceTimerState; - -#define TYPE_CADENCE_TTC "cadence_ttc" -#define CADENCE_TTC(obj) \ - OBJECT_CHECK(CadenceTTCState, (obj), TYPE_CADENCE_TTC) - -typedef struct CadenceTTCState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - CadenceTimerState timer[3]; -} CadenceTTCState; - -static void cadence_timer_update(CadenceTimerState *s) -{ - qemu_set_irq(s->irq, !!(s->reg_intr & s->reg_intr_en)); -} - -static CadenceTimerState *cadence_timer_from_addr(void *opaque, - hwaddr offset) -{ - unsigned int index; - CadenceTTCState *s = (CadenceTTCState *)opaque; - - index = (offset >> 2) % 3; - - return &s->timer[index]; -} - -static uint64_t cadence_timer_get_ns(CadenceTimerState *s, uint64_t timer_steps) -{ - /* timer_steps has max value of 0x100000000. double check it - * (or overflow can happen below) */ - assert(timer_steps <= 1ULL << 32); - - uint64_t r = timer_steps * 1000000000ULL; - if (s->reg_clock & CLOCK_CTRL_PS_EN) { - r >>= 16 - (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1); - } else { - r >>= 16; - } - r /= (uint64_t)s->freq; - return r; -} - -static uint64_t cadence_timer_get_steps(CadenceTimerState *s, uint64_t ns) -{ - uint64_t to_divide = 1000000000ULL; - - uint64_t r = ns; - /* for very large intervals (> 8s) do some division first to stop - * overflow (costs some prescision) */ - while (r >= 8ULL << 30 && to_divide > 1) { - r /= 1000; - to_divide /= 1000; - } - r <<= 16; - /* keep early-dividing as needed */ - while (r >= 8ULL << 30 && to_divide > 1) { - r /= 1000; - to_divide /= 1000; - } - r *= (uint64_t)s->freq; - if (s->reg_clock & CLOCK_CTRL_PS_EN) { - r /= 1 << (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1); - } - - r /= to_divide; - return r; -} - -/* determine if x is in between a and b, exclusive of a, inclusive of b */ - -static inline int64_t is_between(int64_t x, int64_t a, int64_t b) -{ - if (a < b) { - return x > a && x <= b; - } - return x < a && x >= b; -} - -static void cadence_timer_run(CadenceTimerState *s) -{ - int i; - int64_t event_interval, next_value; - - assert(s->cpu_time_valid); /* cadence_timer_sync must be called first */ - - if (s->reg_count & COUNTER_CTRL_DIS) { - s->cpu_time_valid = 0; - return; - } - - { /* figure out what's going to happen next (rollover or match) */ - int64_t interval = (uint64_t)((s->reg_count & COUNTER_CTRL_INT) ? - (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16; - next_value = (s->reg_count & COUNTER_CTRL_DEC) ? -1ULL : interval; - for (i = 0; i < 3; ++i) { - int64_t cand = (uint64_t)s->reg_match[i] << 16; - if (is_between(cand, (uint64_t)s->reg_value, next_value)) { - next_value = cand; - } - } - } - DB_PRINT("next timer event value: %09llx\n", - (unsigned long long)next_value); - - event_interval = next_value - (int64_t)s->reg_value; - event_interval = (event_interval < 0) ? -event_interval : event_interval; - - timer_mod(s->timer, s->cpu_time + - cadence_timer_get_ns(s, event_interval)); -} - -static void cadence_timer_sync(CadenceTimerState *s) -{ - int i; - int64_t r, x; - int64_t interval = ((s->reg_count & COUNTER_CTRL_INT) ? - (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16; - uint64_t old_time = s->cpu_time; - - s->cpu_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - DB_PRINT("cpu time: %lld ns\n", (long long)old_time); - - if (!s->cpu_time_valid || old_time == s->cpu_time) { - s->cpu_time_valid = 1; - return; - } - - r = (int64_t)cadence_timer_get_steps(s, s->cpu_time - old_time); - x = (int64_t)s->reg_value + ((s->reg_count & COUNTER_CTRL_DEC) ? -r : r); - - for (i = 0; i < 3; ++i) { - int64_t m = (int64_t)s->reg_match[i] << 16; - if (m > interval) { - continue; - } - /* check to see if match event has occurred. check m +/- interval - * to account for match events in wrap around cases */ - if (is_between(m, s->reg_value, x) || - is_between(m + interval, s->reg_value, x) || - is_between(m - interval, s->reg_value, x)) { - s->reg_intr |= (2 << i); - } - } - if ((x < 0) || (x >= interval)) { - s->reg_intr |= (s->reg_count & COUNTER_CTRL_INT) ? - COUNTER_INTR_IV : COUNTER_INTR_OV; - } - while (x < 0) { - x += interval; - } - s->reg_value = (uint32_t)(x % interval); - cadence_timer_update(s); -} - -static void cadence_timer_tick(void *opaque) -{ - CadenceTimerState *s = opaque; - - DB_PRINT("\n"); - cadence_timer_sync(s); - cadence_timer_run(s); -} - -static uint32_t cadence_ttc_read_imp(void *opaque, hwaddr offset) -{ - CadenceTimerState *s = cadence_timer_from_addr(opaque, offset); - uint32_t value; - - cadence_timer_sync(s); - cadence_timer_run(s); - - switch (offset) { - case 0x00: /* clock control */ - case 0x04: - case 0x08: - return s->reg_clock; - - case 0x0c: /* counter control */ - case 0x10: - case 0x14: - return s->reg_count; - - case 0x18: /* counter value */ - case 0x1c: - case 0x20: - return (uint16_t)(s->reg_value >> 16); - - case 0x24: /* reg_interval counter */ - case 0x28: - case 0x2c: - return s->reg_interval; - - case 0x30: /* match 1 counter */ - case 0x34: - case 0x38: - return s->reg_match[0]; - - case 0x3c: /* match 2 counter */ - case 0x40: - case 0x44: - return s->reg_match[1]; - - case 0x48: /* match 3 counter */ - case 0x4c: - case 0x50: - return s->reg_match[2]; - - case 0x54: /* interrupt register */ - case 0x58: - case 0x5c: - /* cleared after read */ - value = s->reg_intr; - s->reg_intr = 0; - cadence_timer_update(s); - return value; - - case 0x60: /* interrupt enable */ - case 0x64: - case 0x68: - return s->reg_intr_en; - - case 0x6c: - case 0x70: - case 0x74: - return s->reg_event_ctrl; - - case 0x78: - case 0x7c: - case 0x80: - return s->reg_event; - - default: - return 0; - } -} - -static uint64_t cadence_ttc_read(void *opaque, hwaddr offset, - unsigned size) -{ - uint32_t ret = cadence_ttc_read_imp(opaque, offset); - - DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)ret); - return ret; -} - -static void cadence_ttc_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - CadenceTimerState *s = cadence_timer_from_addr(opaque, offset); - - DB_PRINT("addr: %08x data %08x\n", (unsigned)offset, (unsigned)value); - - cadence_timer_sync(s); - - switch (offset) { - case 0x00: /* clock control */ - case 0x04: - case 0x08: - s->reg_clock = value & 0x3F; - break; - - case 0x0c: /* counter control */ - case 0x10: - case 0x14: - if (value & COUNTER_CTRL_RST) { - s->reg_value = 0; - } - s->reg_count = value & 0x3f & ~COUNTER_CTRL_RST; - break; - - case 0x24: /* interval register */ - case 0x28: - case 0x2c: - s->reg_interval = value & 0xffff; - break; - - case 0x30: /* match register */ - case 0x34: - case 0x38: - s->reg_match[0] = value & 0xffff; - break; - - case 0x3c: /* match register */ - case 0x40: - case 0x44: - s->reg_match[1] = value & 0xffff; - break; - - case 0x48: /* match register */ - case 0x4c: - case 0x50: - s->reg_match[2] = value & 0xffff; - break; - - case 0x54: /* interrupt register */ - case 0x58: - case 0x5c: - break; - - case 0x60: /* interrupt enable */ - case 0x64: - case 0x68: - s->reg_intr_en = value & 0x3f; - break; - - case 0x6c: /* event control */ - case 0x70: - case 0x74: - s->reg_event_ctrl = value & 0x07; - break; - - default: - return; - } - - cadence_timer_run(s); - cadence_timer_update(s); -} - -static const MemoryRegionOps cadence_ttc_ops = { - .read = cadence_ttc_read, - .write = cadence_ttc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void cadence_timer_reset(CadenceTimerState *s) -{ - s->reg_count = 0x21; -} - -static void cadence_timer_init(uint32_t freq, CadenceTimerState *s) -{ - memset(s, 0, sizeof(CadenceTimerState)); - s->freq = freq; - - cadence_timer_reset(s); - - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cadence_timer_tick, s); -} - -static void cadence_ttc_init(Object *obj) -{ - CadenceTTCState *s = CADENCE_TTC(obj); - int i; - - for (i = 0; i < 3; ++i) { - cadence_timer_init(133000000, &s->timer[i]); - sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->timer[i].irq); - } - - memory_region_init_io(&s->iomem, obj, &cadence_ttc_ops, s, - "timer", 0x1000); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); -} - -static void cadence_timer_pre_save(void *opaque) -{ - cadence_timer_sync((CadenceTimerState *)opaque); -} - -static int cadence_timer_post_load(void *opaque, int version_id) -{ - CadenceTimerState *s = opaque; - - s->cpu_time_valid = 0; - cadence_timer_sync(s); - cadence_timer_run(s); - cadence_timer_update(s); - return 0; -} - -static const VMStateDescription vmstate_cadence_timer = { - .name = "cadence_timer", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = cadence_timer_pre_save, - .post_load = cadence_timer_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(reg_clock, CadenceTimerState), - VMSTATE_UINT32(reg_count, CadenceTimerState), - VMSTATE_UINT32(reg_value, CadenceTimerState), - VMSTATE_UINT16(reg_interval, CadenceTimerState), - VMSTATE_UINT16_ARRAY(reg_match, CadenceTimerState, 3), - VMSTATE_UINT32(reg_intr, CadenceTimerState), - VMSTATE_UINT32(reg_intr_en, CadenceTimerState), - VMSTATE_UINT32(reg_event_ctrl, CadenceTimerState), - VMSTATE_UINT32(reg_event, CadenceTimerState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_cadence_ttc = { - .name = "cadence_TTC", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(timer, CadenceTTCState, 3, 0, - vmstate_cadence_timer, - CadenceTimerState), - VMSTATE_END_OF_LIST() - } -}; - -static void cadence_ttc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_cadence_ttc; -} - -static const TypeInfo cadence_ttc_info = { - .name = TYPE_CADENCE_TTC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(CadenceTTCState), - .instance_init = cadence_ttc_init, - .class_init = cadence_ttc_class_init, -}; - -static void cadence_ttc_register_types(void) -{ - type_register_static(&cadence_ttc_info); -} - -type_init(cadence_ttc_register_types) diff --git a/qemu/hw/timer/digic-timer.c b/qemu/hw/timer/digic-timer.c deleted file mode 100644 index 5b97e1e1a..000000000 --- a/qemu/hw/timer/digic-timer.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * QEMU model of the Canon DIGIC timer block. - * - * Copyright (C) 2013 Antony Pavlov - * - * This model is based on reverse engineering efforts - * made by CHDK (http://chdk.wikia.com) and - * Magic Lantern (http://www.magiclantern.fm) projects - * contributors. - * - * See "Timer/Clock Module" docs here: - * http://magiclantern.wikia.com/wiki/Register_Map - * - * The QEMU model of the OSTimer in PKUnity SoC by Guan Xuetao - * is used as a template. - * - * 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. - * - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "hw/ptimer.h" -#include "qemu/main-loop.h" - -#include "hw/timer/digic-timer.h" - -static const VMStateDescription vmstate_digic_timer = { - .name = "digic.timer", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PTIMER(ptimer, DigicTimerState), - VMSTATE_UINT32(control, DigicTimerState), - VMSTATE_UINT32(relvalue, DigicTimerState), - VMSTATE_END_OF_LIST() - } -}; - -static void digic_timer_reset(DeviceState *dev) -{ - DigicTimerState *s = DIGIC_TIMER(dev); - - ptimer_stop(s->ptimer); - s->control = 0; - s->relvalue = 0; -} - -static uint64_t digic_timer_read(void *opaque, hwaddr offset, unsigned size) -{ - DigicTimerState *s = opaque; - uint64_t ret = 0; - - switch (offset) { - case DIGIC_TIMER_CONTROL: - ret = s->control; - break; - case DIGIC_TIMER_RELVALUE: - ret = s->relvalue; - break; - case DIGIC_TIMER_VALUE: - ret = ptimer_get_count(s->ptimer) & 0xffff; - break; - default: - qemu_log_mask(LOG_UNIMP, - "digic-timer: read access to unknown register 0x" - TARGET_FMT_plx, offset); - } - - return ret; -} - -static void digic_timer_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - DigicTimerState *s = opaque; - - switch (offset) { - case DIGIC_TIMER_CONTROL: - if (value & DIGIC_TIMER_CONTROL_RST) { - digic_timer_reset((DeviceState *)s); - break; - } - - if (value & DIGIC_TIMER_CONTROL_EN) { - ptimer_run(s->ptimer, 0); - } - - s->control = (uint32_t)value; - break; - - case DIGIC_TIMER_RELVALUE: - s->relvalue = extract32(value, 0, 16); - ptimer_set_limit(s->ptimer, s->relvalue, 1); - break; - - case DIGIC_TIMER_VALUE: - break; - - default: - qemu_log_mask(LOG_UNIMP, - "digic-timer: read access to unknown register 0x" - TARGET_FMT_plx, offset); - } -} - -static const MemoryRegionOps digic_timer_ops = { - .read = digic_timer_read, - .write = digic_timer_write, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void digic_timer_init(Object *obj) -{ - DigicTimerState *s = DIGIC_TIMER(obj); - - s->ptimer = ptimer_init(NULL); - - /* - * FIXME: there is no documentation on Digic timer - * frequency setup so let it always run at 1 MHz - */ - ptimer_set_freq(s->ptimer, 1 * 1000 * 1000); - - memory_region_init_io(&s->iomem, OBJECT(s), &digic_timer_ops, s, - TYPE_DIGIC_TIMER, 0x100); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); -} - -static void digic_timer_class_init(ObjectClass *klass, void *class_data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = digic_timer_reset; - dc->vmsd = &vmstate_digic_timer; -} - -static const TypeInfo digic_timer_info = { - .name = TYPE_DIGIC_TIMER, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(DigicTimerState), - .instance_init = digic_timer_init, - .class_init = digic_timer_class_init, -}; - -static void digic_timer_register_type(void) -{ - type_register_static(&digic_timer_info); -} - -type_init(digic_timer_register_type) diff --git a/qemu/hw/timer/ds1338.c b/qemu/hw/timer/ds1338.c deleted file mode 100644 index 0112949e2..000000000 --- a/qemu/hw/timer/ds1338.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - * MAXIM DS1338 I2C RTC+NVRAM - * - * Copyright (c) 2009 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#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. - */ -#define NVRAM_SIZE 64 - -/* Flags definitions */ -#define SECONDS_CH 0x80 -#define HOURS_12 0x40 -#define HOURS_PM 0x20 -#define CTRL_OSF 0x20 - -#define TYPE_DS1338 "ds1338" -#define DS1338(obj) OBJECT_CHECK(DS1338State, (obj), TYPE_DS1338) - -typedef struct DS1338State { - I2CSlave parent_obj; - - int64_t offset; - uint8_t wday_offset; - uint8_t nvram[NVRAM_SIZE]; - int32_t ptr; - bool addr_byte; -} DS1338State; - -static const VMStateDescription vmstate_ds1338 = { - .name = "ds1338", - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_I2C_SLAVE(parent_obj, DS1338State), - VMSTATE_INT64(offset, DS1338State), - VMSTATE_UINT8_V(wday_offset, DS1338State, 2), - VMSTATE_UINT8_ARRAY(nvram, DS1338State, NVRAM_SIZE), - VMSTATE_INT32(ptr, DS1338State), - VMSTATE_BOOL(addr_byte, DS1338State), - VMSTATE_END_OF_LIST() - } -}; - -static void capture_current_time(DS1338State *s) -{ - /* Capture the current time into the secondary registers - * which will be actually read by the data transfer operation. - */ - struct tm now; - qemu_get_timedate(&now, s->offset); - s->nvram[0] = to_bcd(now.tm_sec); - s->nvram[1] = to_bcd(now.tm_min); - if (s->nvram[2] & HOURS_12) { - int tmp = now.tm_hour; - if (tmp % 12 == 0) { - tmp += 12; - } - if (tmp <= 12) { - s->nvram[2] = HOURS_12 | to_bcd(tmp); - } else { - s->nvram[2] = HOURS_12 | HOURS_PM | to_bcd(tmp - 12); - } - } else { - s->nvram[2] = to_bcd(now.tm_hour); - } - s->nvram[3] = (now.tm_wday + s->wday_offset) % 7 + 1; - s->nvram[4] = to_bcd(now.tm_mday); - s->nvram[5] = to_bcd(now.tm_mon + 1); - s->nvram[6] = to_bcd(now.tm_year - 100); -} - -static void inc_regptr(DS1338State *s) -{ - /* The register pointer wraps around after 0x3F; wraparound - * causes the current time/date to be retransferred into - * the secondary registers. - */ - s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1); - if (!s->ptr) { - capture_current_time(s); - } -} - -static void ds1338_event(I2CSlave *i2c, enum i2c_event event) -{ - DS1338State *s = DS1338(i2c); - - switch (event) { - case I2C_START_RECV: - /* In h/w, capture happens on any START condition, not just a - * START_RECV, but there is no need to actually capture on - * START_SEND, because the guest can't get at that data - * without going through a START_RECV which would overwrite it. - */ - capture_current_time(s); - break; - case I2C_START_SEND: - s->addr_byte = true; - break; - default: - break; - } -} - -static int ds1338_recv(I2CSlave *i2c) -{ - DS1338State *s = DS1338(i2c); - uint8_t res; - - res = s->nvram[s->ptr]; - inc_regptr(s); - return res; -} - -static int ds1338_send(I2CSlave *i2c, uint8_t data) -{ - DS1338State *s = DS1338(i2c); - - if (s->addr_byte) { - s->ptr = data & (NVRAM_SIZE - 1); - s->addr_byte = false; - return 0; - } - if (s->ptr < 7) { - /* Time register. */ - struct tm now; - qemu_get_timedate(&now, s->offset); - switch(s->ptr) { - case 0: - /* TODO: Implement CH (stop) bit. */ - now.tm_sec = from_bcd(data & 0x7f); - break; - case 1: - now.tm_min = from_bcd(data & 0x7f); - break; - case 2: - if (data & HOURS_12) { - int tmp = from_bcd(data & (HOURS_PM - 1)); - if (data & HOURS_PM) { - tmp += 12; - } - if (tmp % 12 == 0) { - tmp -= 12; - } - now.tm_hour = tmp; - } else { - now.tm_hour = from_bcd(data & (HOURS_12 - 1)); - } - break; - case 3: - { - /* The day field is supposed to contain a value in - the range 1-7. Otherwise behavior is undefined. - */ - int user_wday = (data & 7) - 1; - s->wday_offset = (user_wday - now.tm_wday + 7) % 7; - } - break; - case 4: - now.tm_mday = from_bcd(data & 0x3f); - break; - case 5: - now.tm_mon = from_bcd(data & 0x1f) - 1; - break; - case 6: - now.tm_year = from_bcd(data) + 100; - break; - } - s->offset = qemu_timedate_diff(&now); - } else if (s->ptr == 7) { - /* Control register. */ - - /* Ensure bits 2, 3 and 6 will read back as zero. */ - data &= 0xB3; - - /* Attempting to write the OSF flag to logic 1 leaves the - value unchanged. */ - data = (data & ~CTRL_OSF) | (data & s->nvram[s->ptr] & CTRL_OSF); - - s->nvram[s->ptr] = data; - } else { - s->nvram[s->ptr] = data; - } - inc_regptr(s); - return 0; -} - -static int ds1338_init(I2CSlave *i2c) -{ - return 0; -} - -static void ds1338_reset(DeviceState *dev) -{ - DS1338State *s = DS1338(dev); - - /* The clock is running and synchronized with the host */ - s->offset = 0; - s->wday_offset = 0; - memset(s->nvram, 0, NVRAM_SIZE); - s->ptr = 0; - s->addr_byte = false; -} - -static void ds1338_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - - k->init = ds1338_init; - k->event = ds1338_event; - k->recv = ds1338_recv; - k->send = ds1338_send; - dc->reset = ds1338_reset; - dc->vmsd = &vmstate_ds1338; -} - -static const TypeInfo ds1338_info = { - .name = TYPE_DS1338, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(DS1338State), - .class_init = ds1338_class_init, -}; - -static void ds1338_register_types(void) -{ - type_register_static(&ds1338_info); -} - -type_init(ds1338_register_types) diff --git a/qemu/hw/timer/etraxfs_timer.c b/qemu/hw/timer/etraxfs_timer.c deleted file mode 100644 index 36d8f462c..000000000 --- a/qemu/hw/timer/etraxfs_timer.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - * QEMU ETRAX Timers - * - * Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB. - * - * 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/sysbus.h" -#include "sysemu/sysemu.h" -#include "qemu/timer.h" -#include "hw/ptimer.h" - -#define D(x) - -#define RW_TMR0_DIV 0x00 -#define R_TMR0_DATA 0x04 -#define RW_TMR0_CTRL 0x08 -#define RW_TMR1_DIV 0x10 -#define R_TMR1_DATA 0x14 -#define RW_TMR1_CTRL 0x18 -#define R_TIME 0x38 -#define RW_WD_CTRL 0x40 -#define R_WD_STAT 0x44 -#define RW_INTR_MASK 0x48 -#define RW_ACK_INTR 0x4c -#define R_INTR 0x50 -#define R_MASKED_INTR 0x54 - -#define TYPE_ETRAX_FS_TIMER "etraxfs,timer" -#define ETRAX_TIMER(obj) \ - OBJECT_CHECK(ETRAXTimerState, (obj), TYPE_ETRAX_FS_TIMER) - -typedef struct ETRAXTimerState { - SysBusDevice parent_obj; - - MemoryRegion mmio; - qemu_irq irq; - qemu_irq nmi; - - QEMUBH *bh_t0; - QEMUBH *bh_t1; - QEMUBH *bh_wd; - ptimer_state *ptimer_t0; - ptimer_state *ptimer_t1; - ptimer_state *ptimer_wd; - - int wd_hits; - - /* Control registers. */ - uint32_t rw_tmr0_div; - uint32_t r_tmr0_data; - uint32_t rw_tmr0_ctrl; - - uint32_t rw_tmr1_div; - uint32_t r_tmr1_data; - uint32_t rw_tmr1_ctrl; - - uint32_t rw_wd_ctrl; - - uint32_t rw_intr_mask; - uint32_t rw_ack_intr; - uint32_t r_intr; - uint32_t r_masked_intr; -} ETRAXTimerState; - -static uint64_t -timer_read(void *opaque, hwaddr addr, unsigned int size) -{ - ETRAXTimerState *t = opaque; - uint32_t r = 0; - - switch (addr) { - case R_TMR0_DATA: - r = ptimer_get_count(t->ptimer_t0); - break; - case R_TMR1_DATA: - r = ptimer_get_count(t->ptimer_t1); - break; - case R_TIME: - r = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 10; - break; - case RW_INTR_MASK: - r = t->rw_intr_mask; - break; - case R_MASKED_INTR: - r = t->r_intr & t->rw_intr_mask; - break; - default: - D(printf ("%s %x\n", __func__, addr)); - break; - } - return r; -} - -static void update_ctrl(ETRAXTimerState *t, int tnum) -{ - unsigned int op; - unsigned int freq; - unsigned int freq_hz; - unsigned int div; - uint32_t ctrl; - - ptimer_state *timer; - - if (tnum == 0) { - ctrl = t->rw_tmr0_ctrl; - div = t->rw_tmr0_div; - timer = t->ptimer_t0; - } else { - ctrl = t->rw_tmr1_ctrl; - div = t->rw_tmr1_div; - timer = t->ptimer_t1; - } - - - op = ctrl & 3; - freq = ctrl >> 2; - freq_hz = 32000000; - - switch (freq) - { - case 0: - case 1: - D(printf ("extern or disabled timer clock?\n")); - break; - case 4: freq_hz = 29493000; break; - case 5: freq_hz = 32000000; break; - case 6: freq_hz = 32768000; break; - case 7: freq_hz = 100000000; break; - default: - abort(); - break; - } - - D(printf ("freq_hz=%d div=%d\n", freq_hz, div)); - ptimer_set_freq(timer, freq_hz); - ptimer_set_limit(timer, div, 0); - - switch (op) - { - case 0: - /* Load. */ - ptimer_set_limit(timer, div, 1); - break; - case 1: - /* Hold. */ - ptimer_stop(timer); - break; - case 2: - /* Run. */ - ptimer_run(timer, 0); - break; - default: - abort(); - break; - } -} - -static void timer_update_irq(ETRAXTimerState *t) -{ - t->r_intr &= ~(t->rw_ack_intr); - t->r_masked_intr = t->r_intr & t->rw_intr_mask; - - D(printf("%s: masked_intr=%x\n", __func__, t->r_masked_intr)); - qemu_set_irq(t->irq, !!t->r_masked_intr); -} - -static void timer0_hit(void *opaque) -{ - ETRAXTimerState *t = opaque; - t->r_intr |= 1; - timer_update_irq(t); -} - -static void timer1_hit(void *opaque) -{ - ETRAXTimerState *t = opaque; - t->r_intr |= 2; - timer_update_irq(t); -} - -static void watchdog_hit(void *opaque) -{ - ETRAXTimerState *t = opaque; - if (t->wd_hits == 0) { - /* real hw gives a single tick before reseting but we are - a bit friendlier to compensate for our slower execution. */ - ptimer_set_count(t->ptimer_wd, 10); - ptimer_run(t->ptimer_wd, 1); - qemu_irq_raise(t->nmi); - } - else - qemu_system_reset_request(); - - t->wd_hits++; -} - -static inline void timer_watchdog_update(ETRAXTimerState *t, uint32_t value) -{ - unsigned int wd_en = t->rw_wd_ctrl & (1 << 8); - unsigned int wd_key = t->rw_wd_ctrl >> 9; - unsigned int wd_cnt = t->rw_wd_ctrl & 511; - unsigned int new_key = value >> 9 & ((1 << 7) - 1); - unsigned int new_cmd = (value >> 8) & 1; - - /* If the watchdog is enabled, they written key must match the - complement of the previous. */ - wd_key = ~wd_key & ((1 << 7) - 1); - - if (wd_en && wd_key != new_key) - return; - - D(printf("en=%d new_key=%x oldkey=%x cmd=%d cnt=%d\n", - wd_en, new_key, wd_key, new_cmd, wd_cnt)); - - if (t->wd_hits) - qemu_irq_lower(t->nmi); - - t->wd_hits = 0; - - ptimer_set_freq(t->ptimer_wd, 760); - if (wd_cnt == 0) - wd_cnt = 256; - ptimer_set_count(t->ptimer_wd, wd_cnt); - if (new_cmd) - ptimer_run(t->ptimer_wd, 1); - else - ptimer_stop(t->ptimer_wd); - - t->rw_wd_ctrl = value; -} - -static void -timer_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - ETRAXTimerState *t = opaque; - uint32_t value = val64; - - switch (addr) - { - case RW_TMR0_DIV: - t->rw_tmr0_div = value; - break; - case RW_TMR0_CTRL: - D(printf ("RW_TMR0_CTRL=%x\n", value)); - t->rw_tmr0_ctrl = value; - update_ctrl(t, 0); - break; - case RW_TMR1_DIV: - t->rw_tmr1_div = value; - break; - case RW_TMR1_CTRL: - D(printf ("RW_TMR1_CTRL=%x\n", value)); - t->rw_tmr1_ctrl = value; - update_ctrl(t, 1); - break; - case RW_INTR_MASK: - D(printf ("RW_INTR_MASK=%x\n", value)); - t->rw_intr_mask = value; - timer_update_irq(t); - break; - case RW_WD_CTRL: - timer_watchdog_update(t, value); - break; - case RW_ACK_INTR: - t->rw_ack_intr = value; - timer_update_irq(t); - t->rw_ack_intr = 0; - break; - default: - printf ("%s " TARGET_FMT_plx " %x\n", - __func__, addr, value); - break; - } -} - -static const MemoryRegionOps timer_ops = { - .read = timer_read, - .write = timer_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static void etraxfs_timer_reset(void *opaque) -{ - ETRAXTimerState *t = opaque; - - ptimer_stop(t->ptimer_t0); - ptimer_stop(t->ptimer_t1); - ptimer_stop(t->ptimer_wd); - t->rw_wd_ctrl = 0; - t->r_intr = 0; - t->rw_intr_mask = 0; - qemu_irq_lower(t->irq); -} - -static int etraxfs_timer_init(SysBusDevice *dev) -{ - ETRAXTimerState *t = ETRAX_TIMER(dev); - - t->bh_t0 = qemu_bh_new(timer0_hit, t); - t->bh_t1 = qemu_bh_new(timer1_hit, t); - t->bh_wd = qemu_bh_new(watchdog_hit, t); - t->ptimer_t0 = ptimer_init(t->bh_t0); - t->ptimer_t1 = ptimer_init(t->bh_t1); - t->ptimer_wd = ptimer_init(t->bh_wd); - - sysbus_init_irq(dev, &t->irq); - sysbus_init_irq(dev, &t->nmi); - - memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t, - "etraxfs-timer", 0x5c); - sysbus_init_mmio(dev, &t->mmio); - qemu_register_reset(etraxfs_timer_reset, t); - return 0; -} - -static void etraxfs_timer_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = etraxfs_timer_init; -} - -static const TypeInfo etraxfs_timer_info = { - .name = TYPE_ETRAX_FS_TIMER, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ETRAXTimerState), - .class_init = etraxfs_timer_class_init, -}; - -static void etraxfs_timer_register_types(void) -{ - type_register_static(&etraxfs_timer_info); -} - -type_init(etraxfs_timer_register_types) diff --git a/qemu/hw/timer/exynos4210_mct.c b/qemu/hw/timer/exynos4210_mct.c deleted file mode 100644 index ae69345f0..000000000 --- a/qemu/hw/timer/exynos4210_mct.c +++ /dev/null @@ -1,1480 +0,0 @@ -/* - * Samsung exynos4210 Multi Core timer - * - * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. - * All rights reserved. - * - * Evgeny Voevodin - * - * 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 . - */ - -/* - * Global Timer: - * - * Consists of two timers. First represents Free Running Counter and second - * is used to measure interval from FRC to nearest comparator. - * - * 0 UINT64_MAX - * | timer0 | - * | <-------------------------------------------------------------- | - * | --------------------------------------------frc---------------> | - * |______________________________________________|__________________| - * CMP0 CMP1 CMP2 | CMP3 - * __| |_ - * | timer1 | - * | -------------> | - * frc CMPx - * - * Problem: when implementing global timer as is, overflow arises. - * next_time = cur_time + period * count; - * period and count are 64 bits width. - * Lets arm timer for MCT_GT_COUNTER_STEP count and update internal G_CNT - * register during each event. - * - * Problem: both timers need to be implemented using MCT_XT_COUNTER_STEP because - * local timer contains two counters: TCNT and ICNT. TCNT == 0 -> ICNT--. - * IRQ is generated when ICNT riches zero. Implementation where TCNT == 0 - * generates IRQs suffers from too frequently events. Better to have one - * uint64_t counter equal to TCNT*ICNT and arm ptimer.c for a minimum(TCNT*ICNT, - * MCT_GT_COUNTER_STEP); (yes, if target tunes ICNT * TCNT to be too low values, - * 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" -#include "qemu-common.h" -#include "hw/ptimer.h" - -#include "hw/arm/exynos4210.h" - -//#define DEBUG_MCT - -#ifdef DEBUG_MCT -#define DPRINTF(fmt, ...) \ - do { fprintf(stdout, "MCT: [%24s:%5d] " fmt, __func__, __LINE__, \ - ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while (0) -#endif - -#define MCT_CFG 0x000 -#define G_CNT_L 0x100 -#define G_CNT_U 0x104 -#define G_CNT_WSTAT 0x110 -#define G_COMP0_L 0x200 -#define G_COMP0_U 0x204 -#define G_COMP0_ADD_INCR 0x208 -#define G_COMP1_L 0x210 -#define G_COMP1_U 0x214 -#define G_COMP1_ADD_INCR 0x218 -#define G_COMP2_L 0x220 -#define G_COMP2_U 0x224 -#define G_COMP2_ADD_INCR 0x228 -#define G_COMP3_L 0x230 -#define G_COMP3_U 0x234 -#define G_COMP3_ADD_INCR 0x238 -#define G_TCON 0x240 -#define G_INT_CSTAT 0x244 -#define G_INT_ENB 0x248 -#define G_WSTAT 0x24C -#define L0_TCNTB 0x300 -#define L0_TCNTO 0x304 -#define L0_ICNTB 0x308 -#define L0_ICNTO 0x30C -#define L0_FRCNTB 0x310 -#define L0_FRCNTO 0x314 -#define L0_TCON 0x320 -#define L0_INT_CSTAT 0x330 -#define L0_INT_ENB 0x334 -#define L0_WSTAT 0x340 -#define L1_TCNTB 0x400 -#define L1_TCNTO 0x404 -#define L1_ICNTB 0x408 -#define L1_ICNTO 0x40C -#define L1_FRCNTB 0x410 -#define L1_FRCNTO 0x414 -#define L1_TCON 0x420 -#define L1_INT_CSTAT 0x430 -#define L1_INT_ENB 0x434 -#define L1_WSTAT 0x440 - -#define MCT_CFG_GET_PRESCALER(x) ((x) & 0xFF) -#define MCT_CFG_GET_DIVIDER(x) (1 << ((x) >> 8 & 7)) - -#define GET_G_COMP_IDX(offset) (((offset) - G_COMP0_L) / 0x10) -#define GET_G_COMP_ADD_INCR_IDX(offset) (((offset) - G_COMP0_ADD_INCR) / 0x10) - -#define G_COMP_L(x) (G_COMP0_L + (x) * 0x10) -#define G_COMP_U(x) (G_COMP0_U + (x) * 0x10) - -#define G_COMP_ADD_INCR(x) (G_COMP0_ADD_INCR + (x) * 0x10) - -/* MCT bits */ -#define G_TCON_COMP_ENABLE(x) (1 << 2 * (x)) -#define G_TCON_AUTO_ICREMENT(x) (1 << (2 * (x) + 1)) -#define G_TCON_TIMER_ENABLE (1 << 8) - -#define G_INT_ENABLE(x) (1 << (x)) -#define G_INT_CSTAT_COMP(x) (1 << (x)) - -#define G_CNT_WSTAT_L 1 -#define G_CNT_WSTAT_U 2 - -#define G_WSTAT_COMP_L(x) (1 << 4 * (x)) -#define G_WSTAT_COMP_U(x) (1 << ((4 * (x)) + 1)) -#define G_WSTAT_COMP_ADDINCR(x) (1 << ((4 * (x)) + 2)) -#define G_WSTAT_TCON_WRITE (1 << 16) - -#define GET_L_TIMER_IDX(offset) ((((offset) & 0xF00) - L0_TCNTB) / 0x100) -#define GET_L_TIMER_CNT_REG_IDX(offset, lt_i) \ - (((offset) - (L0_TCNTB + 0x100 * (lt_i))) >> 2) - -#define L_ICNTB_MANUAL_UPDATE (1 << 31) - -#define L_TCON_TICK_START (1) -#define L_TCON_INT_START (1 << 1) -#define L_TCON_INTERVAL_MODE (1 << 2) -#define L_TCON_FRC_START (1 << 3) - -#define L_INT_CSTAT_INTCNT (1 << 0) -#define L_INT_CSTAT_FRCCNT (1 << 1) - -#define L_INT_INTENB_ICNTEIE (1 << 0) -#define L_INT_INTENB_FRCEIE (1 << 1) - -#define L_WSTAT_TCNTB_WRITE (1 << 0) -#define L_WSTAT_ICNTB_WRITE (1 << 1) -#define L_WSTAT_FRCCNTB_WRITE (1 << 2) -#define L_WSTAT_TCON_WRITE (1 << 3) - -enum LocalTimerRegCntIndexes { - L_REG_CNT_TCNTB, - L_REG_CNT_TCNTO, - L_REG_CNT_ICNTB, - L_REG_CNT_ICNTO, - L_REG_CNT_FRCCNTB, - L_REG_CNT_FRCCNTO, - - L_REG_CNT_AMOUNT -}; - -#define MCT_NIRQ 6 -#define MCT_SFR_SIZE 0x444 - -#define MCT_GT_CMP_NUM 4 - -#define MCT_GT_MAX_VAL UINT64_MAX - -#define MCT_GT_COUNTER_STEP 0x100000000ULL -#define MCT_LT_COUNTER_STEP 0x100000000ULL -#define MCT_LT_CNT_LOW_LIMIT 0x100 - -/* global timer */ -typedef struct { - qemu_irq irq[MCT_GT_CMP_NUM]; - - struct gregs { - uint64_t cnt; - uint32_t cnt_wstat; - uint32_t tcon; - uint32_t int_cstat; - uint32_t int_enb; - uint32_t wstat; - uint64_t comp[MCT_GT_CMP_NUM]; - uint32_t comp_add_incr[MCT_GT_CMP_NUM]; - } reg; - - uint64_t count; /* Value FRC was armed with */ - int32_t curr_comp; /* Current comparator FRC is running to */ - - ptimer_state *ptimer_frc; /* FRC timer */ - -} Exynos4210MCTGT; - -/* local timer */ -typedef struct { - int id; /* timer id */ - qemu_irq irq; /* local timer irq */ - - struct tick_timer { - uint32_t cnt_run; /* cnt timer is running */ - uint32_t int_run; /* int timer is running */ - - uint32_t last_icnto; - uint32_t last_tcnto; - uint32_t tcntb; /* initial value for TCNTB */ - uint32_t icntb; /* initial value for ICNTB */ - - /* for step mode */ - uint64_t distance; /* distance to count to the next event */ - uint64_t progress; /* progress when counting by steps */ - uint64_t count; /* count to arm timer with */ - - ptimer_state *ptimer_tick; /* timer for tick counter */ - } tick_timer; - - /* use ptimer.c to represent count down timer */ - - ptimer_state *ptimer_frc; /* timer for free running counter */ - - /* registers */ - struct lregs { - uint32_t cnt[L_REG_CNT_AMOUNT]; - uint32_t tcon; - uint32_t int_cstat; - uint32_t int_enb; - uint32_t wstat; - } reg; - -} Exynos4210MCTLT; - -#define TYPE_EXYNOS4210_MCT "exynos4210.mct" -#define EXYNOS4210_MCT(obj) \ - OBJECT_CHECK(Exynos4210MCTState, (obj), TYPE_EXYNOS4210_MCT) - -typedef struct Exynos4210MCTState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - - /* Registers */ - uint32_t reg_mct_cfg; - - Exynos4210MCTLT l_timer[2]; - Exynos4210MCTGT g_timer; - - uint32_t freq; /* all timers tick frequency, TCLK */ -} Exynos4210MCTState; - -/*** VMState ***/ -static const VMStateDescription vmstate_tick_timer = { - .name = "exynos4210.mct.tick_timer", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(cnt_run, struct tick_timer), - VMSTATE_UINT32(int_run, struct tick_timer), - VMSTATE_UINT32(last_icnto, struct tick_timer), - VMSTATE_UINT32(last_tcnto, struct tick_timer), - VMSTATE_UINT32(tcntb, struct tick_timer), - VMSTATE_UINT32(icntb, struct tick_timer), - VMSTATE_UINT64(distance, struct tick_timer), - VMSTATE_UINT64(progress, struct tick_timer), - VMSTATE_UINT64(count, struct tick_timer), - VMSTATE_PTIMER(ptimer_tick, struct tick_timer), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_lregs = { - .name = "exynos4210.mct.lregs", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(cnt, struct lregs, L_REG_CNT_AMOUNT), - VMSTATE_UINT32(tcon, struct lregs), - VMSTATE_UINT32(int_cstat, struct lregs), - VMSTATE_UINT32(int_enb, struct lregs), - VMSTATE_UINT32(wstat, struct lregs), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_exynos4210_mct_lt = { - .name = "exynos4210.mct.lt", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32(id, Exynos4210MCTLT), - VMSTATE_STRUCT(tick_timer, Exynos4210MCTLT, 0, - vmstate_tick_timer, - struct tick_timer), - VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTLT), - VMSTATE_STRUCT(reg, Exynos4210MCTLT, 0, - vmstate_lregs, - struct lregs), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_gregs = { - .name = "exynos4210.mct.lregs", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT64(cnt, struct gregs), - VMSTATE_UINT32(cnt_wstat, struct gregs), - VMSTATE_UINT32(tcon, struct gregs), - VMSTATE_UINT32(int_cstat, struct gregs), - VMSTATE_UINT32(int_enb, struct gregs), - VMSTATE_UINT32(wstat, struct gregs), - VMSTATE_UINT64_ARRAY(comp, struct gregs, MCT_GT_CMP_NUM), - VMSTATE_UINT32_ARRAY(comp_add_incr, struct gregs, - MCT_GT_CMP_NUM), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_exynos4210_mct_gt = { - .name = "exynos4210.mct.lt", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(reg, Exynos4210MCTGT, 0, vmstate_gregs, - struct gregs), - VMSTATE_UINT64(count, Exynos4210MCTGT), - VMSTATE_INT32(curr_comp, Exynos4210MCTGT), - VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTGT), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_exynos4210_mct_state = { - .name = "exynos4210.mct", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(reg_mct_cfg, Exynos4210MCTState), - VMSTATE_STRUCT_ARRAY(l_timer, Exynos4210MCTState, 2, 0, - vmstate_exynos4210_mct_lt, Exynos4210MCTLT), - VMSTATE_STRUCT(g_timer, Exynos4210MCTState, 0, - vmstate_exynos4210_mct_gt, Exynos4210MCTGT), - VMSTATE_UINT32(freq, Exynos4210MCTState), - VMSTATE_END_OF_LIST() - } -}; - -static void exynos4210_mct_update_freq(Exynos4210MCTState *s); - -/* - * Set counter of FRC global timer. - */ -static void exynos4210_gfrc_set_count(Exynos4210MCTGT *s, uint64_t count) -{ - s->count = count; - DPRINTF("global timer frc set count 0x%llx\n", count); - ptimer_set_count(s->ptimer_frc, count); -} - -/* - * Get counter of FRC global timer. - */ -static uint64_t exynos4210_gfrc_get_count(Exynos4210MCTGT *s) -{ - uint64_t count = 0; - count = ptimer_get_count(s->ptimer_frc); - count = s->count - count; - return s->reg.cnt + count; -} - -/* - * Stop global FRC timer - */ -static void exynos4210_gfrc_stop(Exynos4210MCTGT *s) -{ - DPRINTF("global timer frc stop\n"); - - ptimer_stop(s->ptimer_frc); -} - -/* - * Start global FRC timer - */ -static void exynos4210_gfrc_start(Exynos4210MCTGT *s) -{ - DPRINTF("global timer frc start\n"); - - ptimer_run(s->ptimer_frc, 1); -} - -/* - * Find next nearest Comparator. If current Comparator value equals to other - * Comparator value, skip them both - */ -static int32_t exynos4210_gcomp_find(Exynos4210MCTState *s) -{ - int res; - int i; - int enabled; - uint64_t min; - int min_comp_i; - uint64_t gfrc; - uint64_t distance; - uint64_t distance_min; - int comp_i; - - /* get gfrc count */ - gfrc = exynos4210_gfrc_get_count(&s->g_timer); - - min = UINT64_MAX; - distance_min = UINT64_MAX; - comp_i = MCT_GT_CMP_NUM; - min_comp_i = MCT_GT_CMP_NUM; - enabled = 0; - - /* lookup for nearest comparator */ - for (i = 0; i < MCT_GT_CMP_NUM; i++) { - - if (s->g_timer.reg.tcon & G_TCON_COMP_ENABLE(i)) { - - enabled = 1; - - if (s->g_timer.reg.comp[i] > gfrc) { - /* Comparator is upper then FRC */ - distance = s->g_timer.reg.comp[i] - gfrc; - - if (distance <= distance_min) { - distance_min = distance; - comp_i = i; - } - } else { - /* Comparator is below FRC, find the smallest */ - - if (s->g_timer.reg.comp[i] <= min) { - min = s->g_timer.reg.comp[i]; - min_comp_i = i; - } - } - } - } - - if (!enabled) { - /* All Comparators disabled */ - res = -1; - } else if (comp_i < MCT_GT_CMP_NUM) { - /* Found upper Comparator */ - res = comp_i; - } else { - /* All Comparators are below or equal to FRC */ - res = min_comp_i; - } - - DPRINTF("found comparator %d: comp 0x%llx distance 0x%llx, gfrc 0x%llx\n", - res, - s->g_timer.reg.comp[res], - distance_min, - gfrc); - - return res; -} - -/* - * Get distance to nearest Comparator - */ -static uint64_t exynos4210_gcomp_get_distance(Exynos4210MCTState *s, int32_t id) -{ - if (id == -1) { - /* no enabled Comparators, choose max distance */ - return MCT_GT_COUNTER_STEP; - } - if (s->g_timer.reg.comp[id] - s->g_timer.reg.cnt < MCT_GT_COUNTER_STEP) { - return s->g_timer.reg.comp[id] - s->g_timer.reg.cnt; - } else { - return MCT_GT_COUNTER_STEP; - } -} - -/* - * Restart global FRC timer - */ -static void exynos4210_gfrc_restart(Exynos4210MCTState *s) -{ - uint64_t distance; - - exynos4210_gfrc_stop(&s->g_timer); - - s->g_timer.curr_comp = exynos4210_gcomp_find(s); - - distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp); - - if (distance > MCT_GT_COUNTER_STEP || !distance) { - distance = MCT_GT_COUNTER_STEP; - } - - exynos4210_gfrc_set_count(&s->g_timer, distance); - exynos4210_gfrc_start(&s->g_timer); -} - -/* - * Raise global timer CMP IRQ - */ -static void exynos4210_gcomp_raise_irq(void *opaque, uint32_t id) -{ - Exynos4210MCTGT *s = opaque; - - /* If CSTAT is pending and IRQ is enabled */ - if ((s->reg.int_cstat & G_INT_CSTAT_COMP(id)) && - (s->reg.int_enb & G_INT_ENABLE(id))) { - DPRINTF("gcmp timer[%d] IRQ\n", id); - qemu_irq_raise(s->irq[id]); - } -} - -/* - * Lower global timer CMP IRQ - */ -static void exynos4210_gcomp_lower_irq(void *opaque, uint32_t id) -{ - Exynos4210MCTGT *s = opaque; - qemu_irq_lower(s->irq[id]); -} - -/* - * Global timer FRC event handler. - * Each event occurs when internal counter reaches counter + MCT_GT_COUNTER_STEP - * Every time we arm global FRC timer to count for MCT_GT_COUNTER_STEP value - */ -static void exynos4210_gfrc_event(void *opaque) -{ - Exynos4210MCTState *s = (Exynos4210MCTState *)opaque; - int i; - uint64_t distance; - - DPRINTF("\n"); - - s->g_timer.reg.cnt += s->g_timer.count; - - /* Process all comparators */ - for (i = 0; i < MCT_GT_CMP_NUM; i++) { - - if (s->g_timer.reg.cnt == s->g_timer.reg.comp[i]) { - /* reached nearest comparator */ - - s->g_timer.reg.int_cstat |= G_INT_CSTAT_COMP(i); - - /* Auto increment */ - if (s->g_timer.reg.tcon & G_TCON_AUTO_ICREMENT(i)) { - s->g_timer.reg.comp[i] += s->g_timer.reg.comp_add_incr[i]; - } - - /* IRQ */ - exynos4210_gcomp_raise_irq(&s->g_timer, i); - } - } - - /* Reload FRC to reach nearest comparator */ - s->g_timer.curr_comp = exynos4210_gcomp_find(s); - distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp); - if (distance > MCT_GT_COUNTER_STEP || !distance) { - distance = MCT_GT_COUNTER_STEP; - } - exynos4210_gfrc_set_count(&s->g_timer, distance); - - exynos4210_gfrc_start(&s->g_timer); -} - -/* - * Get counter of FRC local timer. - */ -static uint64_t exynos4210_lfrc_get_count(Exynos4210MCTLT *s) -{ - return ptimer_get_count(s->ptimer_frc); -} - -/* - * Set counter of FRC local timer. - */ -static void exynos4210_lfrc_update_count(Exynos4210MCTLT *s) -{ - if (!s->reg.cnt[L_REG_CNT_FRCCNTB]) { - ptimer_set_count(s->ptimer_frc, MCT_LT_COUNTER_STEP); - } else { - ptimer_set_count(s->ptimer_frc, s->reg.cnt[L_REG_CNT_FRCCNTB]); - } -} - -/* - * Start local FRC timer - */ -static void exynos4210_lfrc_start(Exynos4210MCTLT *s) -{ - ptimer_run(s->ptimer_frc, 1); -} - -/* - * Stop local FRC timer - */ -static void exynos4210_lfrc_stop(Exynos4210MCTLT *s) -{ - ptimer_stop(s->ptimer_frc); -} - -/* - * Local timer free running counter tick handler - */ -static void exynos4210_lfrc_event(void *opaque) -{ - Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque; - - /* local frc expired */ - - DPRINTF("\n"); - - s->reg.int_cstat |= L_INT_CSTAT_FRCCNT; - - /* update frc counter */ - exynos4210_lfrc_update_count(s); - - /* raise irq */ - if (s->reg.int_enb & L_INT_INTENB_FRCEIE) { - qemu_irq_raise(s->irq); - } - - /* we reached here, this means that timer is enabled */ - exynos4210_lfrc_start(s); -} - -static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s); -static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s); -static void exynos4210_ltick_recalc_count(struct tick_timer *s); - -/* - * Action on enabling local tick int timer - */ -static void exynos4210_ltick_int_start(struct tick_timer *s) -{ - if (!s->int_run) { - s->int_run = 1; - } -} - -/* - * Action on disabling local tick int timer - */ -static void exynos4210_ltick_int_stop(struct tick_timer *s) -{ - if (s->int_run) { - s->last_icnto = exynos4210_ltick_int_get_cnto(s); - s->int_run = 0; - } -} - -/* - * Get count for INT timer - */ -static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s) -{ - uint32_t icnto; - uint64_t remain; - uint64_t count; - uint64_t counted; - uint64_t cur_progress; - - count = ptimer_get_count(s->ptimer_tick); - if (count) { - /* timer is still counting, called not from event */ - counted = s->count - ptimer_get_count(s->ptimer_tick); - cur_progress = s->progress + counted; - } else { - /* timer expired earlier */ - cur_progress = s->progress; - } - - remain = s->distance - cur_progress; - - if (!s->int_run) { - /* INT is stopped. */ - icnto = s->last_icnto; - } else { - /* Both are counting */ - icnto = remain / s->tcntb; - } - - return icnto; -} - -/* - * Start local tick cnt timer. - */ -static void exynos4210_ltick_cnt_start(struct tick_timer *s) -{ - if (!s->cnt_run) { - - exynos4210_ltick_recalc_count(s); - ptimer_set_count(s->ptimer_tick, s->count); - ptimer_run(s->ptimer_tick, 1); - - s->cnt_run = 1; - } -} - -/* - * Stop local tick cnt timer. - */ -static void exynos4210_ltick_cnt_stop(struct tick_timer *s) -{ - if (s->cnt_run) { - - s->last_tcnto = exynos4210_ltick_cnt_get_cnto(s); - - if (s->int_run) { - exynos4210_ltick_int_stop(s); - } - - ptimer_stop(s->ptimer_tick); - - s->cnt_run = 0; - } -} - -/* - * Get counter for CNT timer - */ -static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s) -{ - uint32_t tcnto; - uint32_t icnto; - uint64_t remain; - uint64_t counted; - uint64_t count; - uint64_t cur_progress; - - count = ptimer_get_count(s->ptimer_tick); - if (count) { - /* timer is still counting, called not from event */ - counted = s->count - ptimer_get_count(s->ptimer_tick); - cur_progress = s->progress + counted; - } else { - /* timer expired earlier */ - cur_progress = s->progress; - } - - remain = s->distance - cur_progress; - - if (!s->cnt_run) { - /* Both are stopped. */ - tcnto = s->last_tcnto; - } else if (!s->int_run) { - /* INT counter is stopped, progress is by CNT timer */ - tcnto = remain % s->tcntb; - } else { - /* Both are counting */ - icnto = remain / s->tcntb; - if (icnto) { - tcnto = remain % (icnto * s->tcntb); - } else { - tcnto = remain % s->tcntb; - } - } - - return tcnto; -} - -/* - * Set new values of counters for CNT and INT timers - */ -static void exynos4210_ltick_set_cntb(struct tick_timer *s, uint32_t new_cnt, - uint32_t new_int) -{ - uint32_t cnt_stopped = 0; - uint32_t int_stopped = 0; - - if (s->cnt_run) { - exynos4210_ltick_cnt_stop(s); - cnt_stopped = 1; - } - - if (s->int_run) { - exynos4210_ltick_int_stop(s); - int_stopped = 1; - } - - s->tcntb = new_cnt + 1; - s->icntb = new_int + 1; - - if (cnt_stopped) { - exynos4210_ltick_cnt_start(s); - } - if (int_stopped) { - exynos4210_ltick_int_start(s); - } - -} - -/* - * Calculate new counter value for tick timer - */ -static void exynos4210_ltick_recalc_count(struct tick_timer *s) -{ - uint64_t to_count; - - if ((s->cnt_run && s->last_tcnto) || (s->int_run && s->last_icnto)) { - /* - * one or both timers run and not counted to the end; - * distance is not passed, recalculate with last_tcnto * last_icnto - */ - - if (s->last_tcnto) { - to_count = (uint64_t)s->last_tcnto * s->last_icnto; - } else { - to_count = s->last_icnto; - } - } else { - /* distance is passed, recalculate with tcnto * icnto */ - if (s->icntb) { - s->distance = (uint64_t)s->tcntb * s->icntb; - } else { - s->distance = s->tcntb; - } - - to_count = s->distance; - s->progress = 0; - } - - if (to_count > MCT_LT_COUNTER_STEP) { - /* count by step */ - s->count = MCT_LT_COUNTER_STEP; - } else { - s->count = to_count; - } -} - -/* - * Initialize tick_timer - */ -static void exynos4210_ltick_timer_init(struct tick_timer *s) -{ - exynos4210_ltick_int_stop(s); - exynos4210_ltick_cnt_stop(s); - - s->count = 0; - s->distance = 0; - s->progress = 0; - s->icntb = 0; - s->tcntb = 0; -} - -/* - * tick_timer event. - * Raises when abstract tick_timer expires. - */ -static void exynos4210_ltick_timer_event(struct tick_timer *s) -{ - s->progress += s->count; -} - -/* - * Local timer tick counter handler. - * Don't use reloaded timers. If timer counter = zero - * then handler called but after handler finished no - * timer reload occurs. - */ -static void exynos4210_ltick_event(void *opaque) -{ - Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque; - uint32_t tcnto; - uint32_t icnto; -#ifdef DEBUG_MCT - static uint64_t time1[2] = {0}; - static uint64_t time2[2] = {0}; -#endif - - /* Call tick_timer event handler, it will update its tcntb and icntb. */ - exynos4210_ltick_timer_event(&s->tick_timer); - - /* get tick_timer cnt */ - tcnto = exynos4210_ltick_cnt_get_cnto(&s->tick_timer); - - /* get tick_timer int */ - icnto = exynos4210_ltick_int_get_cnto(&s->tick_timer); - - /* raise IRQ if needed */ - if (!icnto && s->reg.tcon & L_TCON_INT_START) { - /* INT counter enabled and expired */ - - s->reg.int_cstat |= L_INT_CSTAT_INTCNT; - - /* raise interrupt if enabled */ - if (s->reg.int_enb & L_INT_INTENB_ICNTEIE) { -#ifdef DEBUG_MCT - time2[s->id] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - DPRINTF("local timer[%d] IRQ: %llx\n", s->id, - time2[s->id] - time1[s->id]); - time1[s->id] = time2[s->id]; -#endif - qemu_irq_raise(s->irq); - } - - /* reload ICNTB */ - if (s->reg.tcon & L_TCON_INTERVAL_MODE) { - exynos4210_ltick_set_cntb(&s->tick_timer, - s->reg.cnt[L_REG_CNT_TCNTB], - s->reg.cnt[L_REG_CNT_ICNTB]); - } - } else { - /* reload TCNTB */ - if (!tcnto) { - exynos4210_ltick_set_cntb(&s->tick_timer, - s->reg.cnt[L_REG_CNT_TCNTB], - icnto); - } - } - - /* start tick_timer cnt */ - exynos4210_ltick_cnt_start(&s->tick_timer); - - /* start tick_timer int */ - exynos4210_ltick_int_start(&s->tick_timer); -} - -/* update timer frequency */ -static void exynos4210_mct_update_freq(Exynos4210MCTState *s) -{ - uint32_t freq = s->freq; - s->freq = 24000000 / - ((MCT_CFG_GET_PRESCALER(s->reg_mct_cfg)+1) * - MCT_CFG_GET_DIVIDER(s->reg_mct_cfg)); - - if (freq != s->freq) { - DPRINTF("freq=%dHz\n", s->freq); - - /* global timer */ - ptimer_set_freq(s->g_timer.ptimer_frc, s->freq); - - /* local timer */ - ptimer_set_freq(s->l_timer[0].tick_timer.ptimer_tick, s->freq); - ptimer_set_freq(s->l_timer[0].ptimer_frc, s->freq); - ptimer_set_freq(s->l_timer[1].tick_timer.ptimer_tick, s->freq); - ptimer_set_freq(s->l_timer[1].ptimer_frc, s->freq); - } -} - -/* set defaul_timer values for all fields */ -static void exynos4210_mct_reset(DeviceState *d) -{ - Exynos4210MCTState *s = EXYNOS4210_MCT(d); - uint32_t i; - - s->reg_mct_cfg = 0; - - /* global timer */ - memset(&s->g_timer.reg, 0, sizeof(s->g_timer.reg)); - exynos4210_gfrc_stop(&s->g_timer); - - /* local timer */ - memset(s->l_timer[0].reg.cnt, 0, sizeof(s->l_timer[0].reg.cnt)); - memset(s->l_timer[1].reg.cnt, 0, sizeof(s->l_timer[1].reg.cnt)); - for (i = 0; i < 2; i++) { - s->l_timer[i].reg.int_cstat = 0; - s->l_timer[i].reg.int_enb = 0; - s->l_timer[i].reg.tcon = 0; - s->l_timer[i].reg.wstat = 0; - s->l_timer[i].tick_timer.count = 0; - s->l_timer[i].tick_timer.distance = 0; - s->l_timer[i].tick_timer.progress = 0; - ptimer_stop(s->l_timer[i].ptimer_frc); - - exynos4210_ltick_timer_init(&s->l_timer[i].tick_timer); - } - - exynos4210_mct_update_freq(s); - -} - -/* Multi Core Timer read */ -static uint64_t exynos4210_mct_read(void *opaque, hwaddr offset, - unsigned size) -{ - Exynos4210MCTState *s = (Exynos4210MCTState *)opaque; - int index; - int shift; - uint64_t count; - uint32_t value; - int lt_i; - - switch (offset) { - - case MCT_CFG: - value = s->reg_mct_cfg; - break; - - case G_CNT_L: case G_CNT_U: - shift = 8 * (offset & 0x4); - count = exynos4210_gfrc_get_count(&s->g_timer); - value = UINT32_MAX & (count >> shift); - DPRINTF("read FRC=0x%llx\n", count); - break; - - case G_CNT_WSTAT: - value = s->g_timer.reg.cnt_wstat; - break; - - case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3): - case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3): - index = GET_G_COMP_IDX(offset); - shift = 8 * (offset & 0x4); - value = UINT32_MAX & (s->g_timer.reg.comp[index] >> shift); - break; - - case G_TCON: - value = s->g_timer.reg.tcon; - break; - - case G_INT_CSTAT: - value = s->g_timer.reg.int_cstat; - break; - - case G_INT_ENB: - value = s->g_timer.reg.int_enb; - break; - case G_WSTAT: - value = s->g_timer.reg.wstat; - break; - - case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR: - case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR: - value = s->g_timer.reg.comp_add_incr[GET_G_COMP_ADD_INCR_IDX(offset)]; - break; - - /* Local timers */ - case L0_TCNTB: case L0_ICNTB: case L0_FRCNTB: - case L1_TCNTB: case L1_ICNTB: case L1_FRCNTB: - lt_i = GET_L_TIMER_IDX(offset); - index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); - value = s->l_timer[lt_i].reg.cnt[index]; - break; - - case L0_TCNTO: case L1_TCNTO: - lt_i = GET_L_TIMER_IDX(offset); - - value = exynos4210_ltick_cnt_get_cnto(&s->l_timer[lt_i].tick_timer); - DPRINTF("local timer[%d] read TCNTO %x\n", lt_i, value); - break; - - case L0_ICNTO: case L1_ICNTO: - lt_i = GET_L_TIMER_IDX(offset); - - value = exynos4210_ltick_int_get_cnto(&s->l_timer[lt_i].tick_timer); - DPRINTF("local timer[%d] read ICNTO %x\n", lt_i, value); - break; - - case L0_FRCNTO: case L1_FRCNTO: - lt_i = GET_L_TIMER_IDX(offset); - - value = exynos4210_lfrc_get_count(&s->l_timer[lt_i]); - - break; - - case L0_TCON: case L1_TCON: - lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; - value = s->l_timer[lt_i].reg.tcon; - break; - - case L0_INT_CSTAT: case L1_INT_CSTAT: - lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; - value = s->l_timer[lt_i].reg.int_cstat; - break; - - case L0_INT_ENB: case L1_INT_ENB: - lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; - value = s->l_timer[lt_i].reg.int_enb; - break; - - case L0_WSTAT: case L1_WSTAT: - lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100; - value = s->l_timer[lt_i].reg.wstat; - break; - - default: - hw_error("exynos4210.mct: bad read offset " - TARGET_FMT_plx "\n", offset); - break; - } - return value; -} - -/* MCT write */ -static void exynos4210_mct_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - Exynos4210MCTState *s = (Exynos4210MCTState *)opaque; - int index; /* index in buffer which represents register set */ - int shift; - int lt_i; - uint64_t new_frc; - uint32_t i; - uint32_t old_val; -#ifdef DEBUG_MCT - static uint32_t icntb_max[2] = {0}; - static uint32_t icntb_min[2] = {UINT32_MAX, UINT32_MAX}; - static uint32_t tcntb_max[2] = {0}; - static uint32_t tcntb_min[2] = {UINT32_MAX, UINT32_MAX}; -#endif - - new_frc = s->g_timer.reg.cnt; - - switch (offset) { - - case MCT_CFG: - s->reg_mct_cfg = value; - exynos4210_mct_update_freq(s); - break; - - case G_CNT_L: - case G_CNT_U: - if (offset == G_CNT_L) { - - DPRINTF("global timer write to reg.cntl %llx\n", value); - - new_frc = (s->g_timer.reg.cnt & (uint64_t)UINT32_MAX << 32) + value; - s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_L; - } - if (offset == G_CNT_U) { - - DPRINTF("global timer write to reg.cntu %llx\n", value); - - new_frc = (s->g_timer.reg.cnt & UINT32_MAX) + - ((uint64_t)value << 32); - s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_U; - } - - s->g_timer.reg.cnt = new_frc; - exynos4210_gfrc_restart(s); - break; - - case G_CNT_WSTAT: - s->g_timer.reg.cnt_wstat &= ~(value); - break; - - case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3): - case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3): - index = GET_G_COMP_IDX(offset); - shift = 8 * (offset & 0x4); - s->g_timer.reg.comp[index] = - (s->g_timer.reg.comp[index] & - (((uint64_t)UINT32_MAX << 32) >> shift)) + - (value << shift); - - DPRINTF("comparator %d write 0x%llx val << %d\n", index, value, shift); - - if (offset&0x4) { - s->g_timer.reg.wstat |= G_WSTAT_COMP_U(index); - } else { - s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index); - } - - exynos4210_gfrc_restart(s); - break; - - case G_TCON: - old_val = s->g_timer.reg.tcon; - s->g_timer.reg.tcon = value; - s->g_timer.reg.wstat |= G_WSTAT_TCON_WRITE; - - DPRINTF("global timer write to reg.g_tcon %llx\n", value); - - /* Start FRC if transition from disabled to enabled */ - if ((value & G_TCON_TIMER_ENABLE) > (old_val & - G_TCON_TIMER_ENABLE)) { - exynos4210_gfrc_start(&s->g_timer); - } - if ((value & G_TCON_TIMER_ENABLE) < (old_val & - G_TCON_TIMER_ENABLE)) { - exynos4210_gfrc_stop(&s->g_timer); - } - - /* Start CMP if transition from disabled to enabled */ - for (i = 0; i < MCT_GT_CMP_NUM; i++) { - if ((value & G_TCON_COMP_ENABLE(i)) != (old_val & - G_TCON_COMP_ENABLE(i))) { - exynos4210_gfrc_restart(s); - } - } - break; - - case G_INT_CSTAT: - s->g_timer.reg.int_cstat &= ~(value); - for (i = 0; i < MCT_GT_CMP_NUM; i++) { - if (value & G_INT_CSTAT_COMP(i)) { - exynos4210_gcomp_lower_irq(&s->g_timer, i); - } - } - break; - - case G_INT_ENB: - - /* Raise IRQ if transition from disabled to enabled and CSTAT pending */ - for (i = 0; i < MCT_GT_CMP_NUM; i++) { - if ((value & G_INT_ENABLE(i)) > (s->g_timer.reg.tcon & - G_INT_ENABLE(i))) { - if (s->g_timer.reg.int_cstat & G_INT_CSTAT_COMP(i)) { - exynos4210_gcomp_raise_irq(&s->g_timer, i); - } - } - - if ((value & G_INT_ENABLE(i)) < (s->g_timer.reg.tcon & - G_INT_ENABLE(i))) { - exynos4210_gcomp_lower_irq(&s->g_timer, i); - } - } - - DPRINTF("global timer INT enable %llx\n", value); - s->g_timer.reg.int_enb = value; - break; - - case G_WSTAT: - s->g_timer.reg.wstat &= ~(value); - break; - - case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR: - case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR: - index = GET_G_COMP_ADD_INCR_IDX(offset); - s->g_timer.reg.comp_add_incr[index] = value; - s->g_timer.reg.wstat |= G_WSTAT_COMP_ADDINCR(index); - break; - - /* Local timers */ - case L0_TCON: case L1_TCON: - lt_i = GET_L_TIMER_IDX(offset); - old_val = s->l_timer[lt_i].reg.tcon; - - s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCON_WRITE; - s->l_timer[lt_i].reg.tcon = value; - - /* Stop local CNT */ - if ((value & L_TCON_TICK_START) < - (old_val & L_TCON_TICK_START)) { - DPRINTF("local timer[%d] stop cnt\n", lt_i); - exynos4210_ltick_cnt_stop(&s->l_timer[lt_i].tick_timer); - } - - /* Stop local INT */ - if ((value & L_TCON_INT_START) < - (old_val & L_TCON_INT_START)) { - DPRINTF("local timer[%d] stop int\n", lt_i); - exynos4210_ltick_int_stop(&s->l_timer[lt_i].tick_timer); - } - - /* Start local CNT */ - if ((value & L_TCON_TICK_START) > - (old_val & L_TCON_TICK_START)) { - DPRINTF("local timer[%d] start cnt\n", lt_i); - exynos4210_ltick_cnt_start(&s->l_timer[lt_i].tick_timer); - } - - /* Start local INT */ - if ((value & L_TCON_INT_START) > - (old_val & L_TCON_INT_START)) { - DPRINTF("local timer[%d] start int\n", lt_i); - exynos4210_ltick_int_start(&s->l_timer[lt_i].tick_timer); - } - - /* Start or Stop local FRC if TCON changed */ - if ((value & L_TCON_FRC_START) > - (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) { - DPRINTF("local timer[%d] start frc\n", lt_i); - exynos4210_lfrc_start(&s->l_timer[lt_i]); - } - if ((value & L_TCON_FRC_START) < - (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) { - DPRINTF("local timer[%d] stop frc\n", lt_i); - exynos4210_lfrc_stop(&s->l_timer[lt_i]); - } - break; - - case L0_TCNTB: case L1_TCNTB: - - lt_i = GET_L_TIMER_IDX(offset); - index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); - - /* - * TCNTB is updated to internal register only after CNT expired. - * Due to this we should reload timer to nearest moment when CNT is - * expired and then in event handler update tcntb to new TCNTB value. - */ - exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, value, - s->l_timer[lt_i].tick_timer.icntb); - - s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCNTB_WRITE; - s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] = value; - -#ifdef DEBUG_MCT - if (tcntb_min[lt_i] > value) { - tcntb_min[lt_i] = value; - } - if (tcntb_max[lt_i] < value) { - tcntb_max[lt_i] = value; - } - DPRINTF("local timer[%d] TCNTB write %llx; max=%x, min=%x\n", - lt_i, value, tcntb_max[lt_i], tcntb_min[lt_i]); -#endif - break; - - case L0_ICNTB: case L1_ICNTB: - - lt_i = GET_L_TIMER_IDX(offset); - index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); - - s->l_timer[lt_i].reg.wstat |= L_WSTAT_ICNTB_WRITE; - s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = value & - ~L_ICNTB_MANUAL_UPDATE; - - /* - * We need to avoid too small values for TCNTB*ICNTB. If not, IRQ event - * could raise too fast disallowing QEMU to execute target code. - */ - if (s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] * - s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] < MCT_LT_CNT_LOW_LIMIT) { - if (!s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB]) { - s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = - MCT_LT_CNT_LOW_LIMIT; - } else { - s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = - MCT_LT_CNT_LOW_LIMIT / - s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB]; - } - } - - if (value & L_ICNTB_MANUAL_UPDATE) { - exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, - s->l_timer[lt_i].tick_timer.tcntb, - s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB]); - } - -#ifdef DEBUG_MCT - if (icntb_min[lt_i] > value) { - icntb_min[lt_i] = value; - } - if (icntb_max[lt_i] < value) { - icntb_max[lt_i] = value; - } -DPRINTF("local timer[%d] ICNTB write %llx; max=%x, min=%x\n\n", - lt_i, value, icntb_max[lt_i], icntb_min[lt_i]); -#endif -break; - - case L0_FRCNTB: case L1_FRCNTB: - - lt_i = GET_L_TIMER_IDX(offset); - index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i); - - DPRINTF("local timer[%d] FRCNTB write %llx\n", lt_i, value); - - s->l_timer[lt_i].reg.wstat |= L_WSTAT_FRCCNTB_WRITE; - s->l_timer[lt_i].reg.cnt[L_REG_CNT_FRCCNTB] = value; - - break; - - case L0_TCNTO: case L1_TCNTO: - case L0_ICNTO: case L1_ICNTO: - case L0_FRCNTO: case L1_FRCNTO: - fprintf(stderr, "\n[exynos4210.mct: write to RO register " - TARGET_FMT_plx "]\n\n", offset); - break; - - case L0_INT_CSTAT: case L1_INT_CSTAT: - lt_i = GET_L_TIMER_IDX(offset); - - DPRINTF("local timer[%d] CSTAT write %llx\n", lt_i, value); - - s->l_timer[lt_i].reg.int_cstat &= ~value; - if (!s->l_timer[lt_i].reg.int_cstat) { - qemu_irq_lower(s->l_timer[lt_i].irq); - } - break; - - case L0_INT_ENB: case L1_INT_ENB: - lt_i = GET_L_TIMER_IDX(offset); - old_val = s->l_timer[lt_i].reg.int_enb; - - /* Raise Local timer IRQ if cstat is pending */ - if ((value & L_INT_INTENB_ICNTEIE) > (old_val & L_INT_INTENB_ICNTEIE)) { - if (s->l_timer[lt_i].reg.int_cstat & L_INT_CSTAT_INTCNT) { - qemu_irq_raise(s->l_timer[lt_i].irq); - } - } - - s->l_timer[lt_i].reg.int_enb = value; - - break; - - case L0_WSTAT: case L1_WSTAT: - lt_i = GET_L_TIMER_IDX(offset); - - s->l_timer[lt_i].reg.wstat &= ~value; - break; - - default: - hw_error("exynos4210.mct: bad write offset " - TARGET_FMT_plx "\n", offset); - break; - } -} - -static const MemoryRegionOps exynos4210_mct_ops = { - .read = exynos4210_mct_read, - .write = exynos4210_mct_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* MCT init */ -static void exynos4210_mct_init(Object *obj) -{ - int i; - Exynos4210MCTState *s = EXYNOS4210_MCT(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); - QEMUBH *bh[2]; - - /* Global timer */ - bh[0] = qemu_bh_new(exynos4210_gfrc_event, s); - s->g_timer.ptimer_frc = ptimer_init(bh[0]); - memset(&s->g_timer.reg, 0, sizeof(struct gregs)); - - /* Local timers */ - for (i = 0; i < 2; i++) { - bh[0] = qemu_bh_new(exynos4210_ltick_event, &s->l_timer[i]); - bh[1] = qemu_bh_new(exynos4210_lfrc_event, &s->l_timer[i]); - s->l_timer[i].tick_timer.ptimer_tick = ptimer_init(bh[0]); - s->l_timer[i].ptimer_frc = ptimer_init(bh[1]); - s->l_timer[i].id = i; - } - - /* IRQs */ - for (i = 0; i < MCT_GT_CMP_NUM; i++) { - sysbus_init_irq(dev, &s->g_timer.irq[i]); - } - for (i = 0; i < 2; i++) { - sysbus_init_irq(dev, &s->l_timer[i].irq); - } - - memory_region_init_io(&s->iomem, obj, &exynos4210_mct_ops, s, - "exynos4210-mct", MCT_SFR_SIZE); - sysbus_init_mmio(dev, &s->iomem); -} - -static void exynos4210_mct_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = exynos4210_mct_reset; - dc->vmsd = &vmstate_exynos4210_mct_state; -} - -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, -}; - -static void exynos4210_mct_register_types(void) -{ - type_register_static(&exynos4210_mct_info); -} - -type_init(exynos4210_mct_register_types) diff --git a/qemu/hw/timer/exynos4210_pwm.c b/qemu/hw/timer/exynos4210_pwm.c deleted file mode 100644 index 0e9e2e9bf..000000000 --- a/qemu/hw/timer/exynos4210_pwm.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Samsung exynos4210 Pulse Width Modulation Timer - * - * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. - * All rights reserved. - * - * Evgeny Voevodin - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "qemu/timer.h" -#include "qemu-common.h" -#include "qemu/main-loop.h" -#include "hw/ptimer.h" - -#include "hw/arm/exynos4210.h" - -//#define DEBUG_PWM - -#ifdef DEBUG_PWM -#define DPRINTF(fmt, ...) \ - do { fprintf(stdout, "PWM: [%24s:%5d] " fmt, __func__, __LINE__, \ - ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while (0) -#endif - -#define EXYNOS4210_PWM_TIMERS_NUM 5 -#define EXYNOS4210_PWM_REG_MEM_SIZE 0x50 - -#define TCFG0 0x0000 -#define TCFG1 0x0004 -#define TCON 0x0008 -#define TCNTB0 0x000C -#define TCMPB0 0x0010 -#define TCNTO0 0x0014 -#define TCNTB1 0x0018 -#define TCMPB1 0x001C -#define TCNTO1 0x0020 -#define TCNTB2 0x0024 -#define TCMPB2 0x0028 -#define TCNTO2 0x002C -#define TCNTB3 0x0030 -#define TCMPB3 0x0034 -#define TCNTO3 0x0038 -#define TCNTB4 0x003C -#define TCNTO4 0x0040 -#define TINT_CSTAT 0x0044 - -#define TCNTB(x) (0xC * (x)) -#define TCMPB(x) (0xC * (x) + 1) -#define TCNTO(x) (0xC * (x) + 2) - -#define GET_PRESCALER(reg, x) (((reg) & (0xFF << (8 * (x)))) >> 8 * (x)) -#define GET_DIVIDER(reg, x) (1 << (((reg) & (0xF << (4 * (x)))) >> (4 * (x)))) - -/* - * Attention! Timer4 doesn't have OUTPUT_INVERTER, - * so Auto Reload bit is not accessible by macros! - */ -#define TCON_TIMER_BASE(x) (((x) ? 1 : 0) * 4 + 4 * (x)) -#define TCON_TIMER_START(x) (1 << (TCON_TIMER_BASE(x) + 0)) -#define TCON_TIMER_MANUAL_UPD(x) (1 << (TCON_TIMER_BASE(x) + 1)) -#define TCON_TIMER_OUTPUT_INV(x) (1 << (TCON_TIMER_BASE(x) + 2)) -#define TCON_TIMER_AUTO_RELOAD(x) (1 << (TCON_TIMER_BASE(x) + 3)) -#define TCON_TIMER4_AUTO_RELOAD (1 << 22) - -#define TINT_CSTAT_STATUS(x) (1 << (5 + (x))) -#define TINT_CSTAT_ENABLE(x) (1 << (x)) - -/* timer struct */ -typedef struct { - uint32_t id; /* timer id */ - qemu_irq irq; /* local timer irq */ - uint32_t freq; /* timer frequency */ - - /* use ptimer.c to represent count down timer */ - ptimer_state *ptimer; /* timer */ - - /* registers */ - uint32_t reg_tcntb; /* counter register buffer */ - uint32_t reg_tcmpb; /* compare register buffer */ - - struct Exynos4210PWMState *parent; - -} Exynos4210PWM; - -#define TYPE_EXYNOS4210_PWM "exynos4210.pwm" -#define EXYNOS4210_PWM(obj) \ - OBJECT_CHECK(Exynos4210PWMState, (obj), TYPE_EXYNOS4210_PWM) - -typedef struct Exynos4210PWMState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - - uint32_t reg_tcfg[2]; - uint32_t reg_tcon; - uint32_t reg_tint_cstat; - - Exynos4210PWM timer[EXYNOS4210_PWM_TIMERS_NUM]; - -} Exynos4210PWMState; - -/*** VMState ***/ -static const VMStateDescription vmstate_exynos4210_pwm = { - .name = "exynos4210.pwm.pwm", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(id, Exynos4210PWM), - VMSTATE_UINT32(freq, Exynos4210PWM), - VMSTATE_PTIMER(ptimer, Exynos4210PWM), - VMSTATE_UINT32(reg_tcntb, Exynos4210PWM), - VMSTATE_UINT32(reg_tcmpb, Exynos4210PWM), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_exynos4210_pwm_state = { - .name = "exynos4210.pwm", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(reg_tcfg, Exynos4210PWMState, 2), - VMSTATE_UINT32(reg_tcon, Exynos4210PWMState), - VMSTATE_UINT32(reg_tint_cstat, Exynos4210PWMState), - VMSTATE_STRUCT_ARRAY(timer, Exynos4210PWMState, - EXYNOS4210_PWM_TIMERS_NUM, 0, - vmstate_exynos4210_pwm, Exynos4210PWM), - VMSTATE_END_OF_LIST() - } -}; - -/* - * PWM update frequency - */ -static void exynos4210_pwm_update_freq(Exynos4210PWMState *s, uint32_t id) -{ - uint32_t freq; - freq = s->timer[id].freq; - if (id > 1) { - s->timer[id].freq = 24000000 / - ((GET_PRESCALER(s->reg_tcfg[0], 1) + 1) * - (GET_DIVIDER(s->reg_tcfg[1], id))); - } else { - s->timer[id].freq = 24000000 / - ((GET_PRESCALER(s->reg_tcfg[0], 0) + 1) * - (GET_DIVIDER(s->reg_tcfg[1], id))); - } - - if (freq != s->timer[id].freq) { - ptimer_set_freq(s->timer[id].ptimer, s->timer[id].freq); - DPRINTF("freq=%dHz\n", s->timer[id].freq); - } -} - -/* - * Counter tick handler - */ -static void exynos4210_pwm_tick(void *opaque) -{ - Exynos4210PWM *s = (Exynos4210PWM *)opaque; - Exynos4210PWMState *p = (Exynos4210PWMState *)s->parent; - uint32_t id = s->id; - bool cmp; - - DPRINTF("timer %d tick\n", id); - - /* set irq status */ - p->reg_tint_cstat |= TINT_CSTAT_STATUS(id); - - /* raise IRQ */ - if (p->reg_tint_cstat & TINT_CSTAT_ENABLE(id)) { - DPRINTF("timer %d IRQ\n", id); - qemu_irq_raise(p->timer[id].irq); - } - - /* reload timer */ - if (id != 4) { - cmp = p->reg_tcon & TCON_TIMER_AUTO_RELOAD(id); - } else { - cmp = p->reg_tcon & TCON_TIMER4_AUTO_RELOAD; - } - - if (cmp) { - DPRINTF("auto reload timer %d count to %x\n", id, - p->timer[id].reg_tcntb); - ptimer_set_count(p->timer[id].ptimer, p->timer[id].reg_tcntb); - ptimer_run(p->timer[id].ptimer, 1); - } else { - /* stop timer, set status to STOP, see Basic Timer Operation */ - p->reg_tcon &= ~TCON_TIMER_START(id); - ptimer_stop(p->timer[id].ptimer); - } -} - -/* - * PWM Read - */ -static uint64_t exynos4210_pwm_read(void *opaque, hwaddr offset, - unsigned size) -{ - Exynos4210PWMState *s = (Exynos4210PWMState *)opaque; - uint32_t value = 0; - int index; - - switch (offset) { - case TCFG0: case TCFG1: - index = (offset - TCFG0) >> 2; - value = s->reg_tcfg[index]; - break; - - case TCON: - value = s->reg_tcon; - break; - - case TCNTB0: case TCNTB1: - case TCNTB2: case TCNTB3: case TCNTB4: - index = (offset - TCNTB0) / 0xC; - value = s->timer[index].reg_tcntb; - break; - - case TCMPB0: case TCMPB1: - case TCMPB2: case TCMPB3: - index = (offset - TCMPB0) / 0xC; - value = s->timer[index].reg_tcmpb; - break; - - case TCNTO0: case TCNTO1: - case TCNTO2: case TCNTO3: case TCNTO4: - index = (offset == TCNTO4) ? 4 : (offset - TCNTO0) / 0xC; - value = ptimer_get_count(s->timer[index].ptimer); - break; - - case TINT_CSTAT: - value = s->reg_tint_cstat; - break; - - default: - fprintf(stderr, - "[exynos4210.pwm: bad read offset " TARGET_FMT_plx "]\n", - offset); - break; - } - return value; -} - -/* - * PWM Write - */ -static void exynos4210_pwm_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - Exynos4210PWMState *s = (Exynos4210PWMState *)opaque; - int index; - uint32_t new_val; - int i; - - switch (offset) { - case TCFG0: case TCFG1: - index = (offset - TCFG0) >> 2; - s->reg_tcfg[index] = value; - - /* update timers frequencies */ - for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { - exynos4210_pwm_update_freq(s, s->timer[i].id); - } - break; - - case TCON: - for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { - if ((value & TCON_TIMER_MANUAL_UPD(i)) > - (s->reg_tcon & TCON_TIMER_MANUAL_UPD(i))) { - /* - * TCNTB and TCMPB are loaded into TCNT and TCMP. - * Update timers. - */ - - /* this will start timer to run, this ok, because - * during processing start bit timer will be stopped - * if needed */ - ptimer_set_count(s->timer[i].ptimer, s->timer[i].reg_tcntb); - DPRINTF("set timer %d count to %x\n", i, - s->timer[i].reg_tcntb); - } - - if ((value & TCON_TIMER_START(i)) > - (s->reg_tcon & TCON_TIMER_START(i))) { - /* changed to start */ - ptimer_run(s->timer[i].ptimer, 1); - DPRINTF("run timer %d\n", i); - } - - if ((value & TCON_TIMER_START(i)) < - (s->reg_tcon & TCON_TIMER_START(i))) { - /* changed to stop */ - ptimer_stop(s->timer[i].ptimer); - DPRINTF("stop timer %d\n", i); - } - } - s->reg_tcon = value; - break; - - case TCNTB0: case TCNTB1: - case TCNTB2: case TCNTB3: case TCNTB4: - index = (offset - TCNTB0) / 0xC; - s->timer[index].reg_tcntb = value; - break; - - case TCMPB0: case TCMPB1: - case TCMPB2: case TCMPB3: - index = (offset - TCMPB0) / 0xC; - s->timer[index].reg_tcmpb = value; - break; - - case TINT_CSTAT: - new_val = (s->reg_tint_cstat & 0x3E0) + (0x1F & value); - new_val &= ~(0x3E0 & value); - - for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { - if ((new_val & TINT_CSTAT_STATUS(i)) < - (s->reg_tint_cstat & TINT_CSTAT_STATUS(i))) { - qemu_irq_lower(s->timer[i].irq); - } - } - - s->reg_tint_cstat = new_val; - break; - - default: - fprintf(stderr, - "[exynos4210.pwm: bad write offset " TARGET_FMT_plx "]\n", - offset); - break; - - } -} - -/* - * Set default values to timer fields and registers - */ -static void exynos4210_pwm_reset(DeviceState *d) -{ - Exynos4210PWMState *s = EXYNOS4210_PWM(d); - int i; - s->reg_tcfg[0] = 0x0101; - s->reg_tcfg[1] = 0x0; - s->reg_tcon = 0; - s->reg_tint_cstat = 0; - for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { - s->timer[i].reg_tcmpb = 0; - s->timer[i].reg_tcntb = 0; - - exynos4210_pwm_update_freq(s, s->timer[i].id); - ptimer_stop(s->timer[i].ptimer); - } -} - -static const MemoryRegionOps exynos4210_pwm_ops = { - .read = exynos4210_pwm_read, - .write = exynos4210_pwm_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* - * PWM timer initialization - */ -static void exynos4210_pwm_init(Object *obj) -{ - Exynos4210PWMState *s = EXYNOS4210_PWM(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); - int i; - QEMUBH *bh; - - for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) { - bh = qemu_bh_new(exynos4210_pwm_tick, &s->timer[i]); - sysbus_init_irq(dev, &s->timer[i].irq); - s->timer[i].ptimer = ptimer_init(bh); - s->timer[i].id = i; - s->timer[i].parent = 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); -} - -static void exynos4210_pwm_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = exynos4210_pwm_reset; - dc->vmsd = &vmstate_exynos4210_pwm_state; -} - -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, -}; - -static void exynos4210_pwm_register_types(void) -{ - type_register_static(&exynos4210_pwm_info); -} - -type_init(exynos4210_pwm_register_types) diff --git a/qemu/hw/timer/exynos4210_rtc.c b/qemu/hw/timer/exynos4210_rtc.c deleted file mode 100644 index da4dd451b..000000000 --- a/qemu/hw/timer/exynos4210_rtc.c +++ /dev/null @@ -1,595 +0,0 @@ -/* - * Samsung exynos4210 Real Time Clock - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * Ogurtsov Oleg - * - * 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 . - * - */ - -/* Description: - * Register RTCCON: - * CLKSEL Bit[1] not used - * 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" -#include "sysemu/sysemu.h" - -#include "hw/arm/exynos4210.h" - -#define DEBUG_RTC 0 - -#if DEBUG_RTC -#define DPRINTF(fmt, ...) \ - do { fprintf(stdout, "RTC: [%24s:%5d] " fmt, __func__, __LINE__, \ - ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while (0) -#endif - -#define EXYNOS4210_RTC_REG_MEM_SIZE 0x0100 - -#define INTP 0x0030 -#define RTCCON 0x0040 -#define TICCNT 0x0044 -#define RTCALM 0x0050 -#define ALMSEC 0x0054 -#define ALMMIN 0x0058 -#define ALMHOUR 0x005C -#define ALMDAY 0x0060 -#define ALMMON 0x0064 -#define ALMYEAR 0x0068 -#define BCDSEC 0x0070 -#define BCDMIN 0x0074 -#define BCDHOUR 0x0078 -#define BCDDAY 0x007C -#define BCDDAYWEEK 0x0080 -#define BCDMON 0x0084 -#define BCDYEAR 0x0088 -#define CURTICNT 0x0090 - -#define TICK_TIMER_ENABLE 0x0100 -#define TICNT_THRESHOLD 2 - - -#define RTC_ENABLE 0x0001 - -#define INTP_TICK_ENABLE 0x0001 -#define INTP_ALM_ENABLE 0x0002 - -#define ALARM_INT_ENABLE 0x0040 - -#define RTC_BASE_FREQ 32768 - -#define TYPE_EXYNOS4210_RTC "exynos4210.rtc" -#define EXYNOS4210_RTC(obj) \ - OBJECT_CHECK(Exynos4210RTCState, (obj), TYPE_EXYNOS4210_RTC) - -typedef struct Exynos4210RTCState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - - /* registers */ - uint32_t reg_intp; - uint32_t reg_rtccon; - uint32_t reg_ticcnt; - uint32_t reg_rtcalm; - uint32_t reg_almsec; - uint32_t reg_almmin; - uint32_t reg_almhour; - uint32_t reg_almday; - uint32_t reg_almmon; - uint32_t reg_almyear; - uint32_t reg_curticcnt; - - ptimer_state *ptimer; /* tick timer */ - ptimer_state *ptimer_1Hz; /* clock timer */ - uint32_t freq; - - qemu_irq tick_irq; /* Time Tick Generator irq */ - qemu_irq alm_irq; /* alarm irq */ - - struct tm current_tm; /* current time */ -} Exynos4210RTCState; - -#define TICCKSEL(value) ((value & (0x0F << 4)) >> 4) - -/*** VMState ***/ -static const VMStateDescription vmstate_exynos4210_rtc_state = { - .name = "exynos4210.rtc", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(reg_intp, Exynos4210RTCState), - VMSTATE_UINT32(reg_rtccon, Exynos4210RTCState), - VMSTATE_UINT32(reg_ticcnt, Exynos4210RTCState), - VMSTATE_UINT32(reg_rtcalm, Exynos4210RTCState), - VMSTATE_UINT32(reg_almsec, Exynos4210RTCState), - VMSTATE_UINT32(reg_almmin, Exynos4210RTCState), - VMSTATE_UINT32(reg_almhour, Exynos4210RTCState), - VMSTATE_UINT32(reg_almday, Exynos4210RTCState), - VMSTATE_UINT32(reg_almmon, Exynos4210RTCState), - VMSTATE_UINT32(reg_almyear, Exynos4210RTCState), - VMSTATE_UINT32(reg_curticcnt, Exynos4210RTCState), - VMSTATE_PTIMER(ptimer, Exynos4210RTCState), - VMSTATE_PTIMER(ptimer_1Hz, Exynos4210RTCState), - VMSTATE_UINT32(freq, Exynos4210RTCState), - VMSTATE_INT32(current_tm.tm_sec, Exynos4210RTCState), - VMSTATE_INT32(current_tm.tm_min, Exynos4210RTCState), - VMSTATE_INT32(current_tm.tm_hour, Exynos4210RTCState), - VMSTATE_INT32(current_tm.tm_wday, Exynos4210RTCState), - VMSTATE_INT32(current_tm.tm_mday, Exynos4210RTCState), - VMSTATE_INT32(current_tm.tm_mon, Exynos4210RTCState), - VMSTATE_INT32(current_tm.tm_year, Exynos4210RTCState), - VMSTATE_END_OF_LIST() - } -}; - -#define BCD3DIGITS(x) \ - ((uint32_t)to_bcd((uint8_t)(x % 100)) + \ - ((uint32_t)to_bcd((uint8_t)((x % 1000) / 100)) << 8)) - -static void check_alarm_raise(Exynos4210RTCState *s) -{ - unsigned int alarm_raise = 0; - struct tm stm = s->current_tm; - - if ((s->reg_rtcalm & 0x01) && - (to_bcd((uint8_t)stm.tm_sec) == (uint8_t)s->reg_almsec)) { - alarm_raise = 1; - } - if ((s->reg_rtcalm & 0x02) && - (to_bcd((uint8_t)stm.tm_min) == (uint8_t)s->reg_almmin)) { - alarm_raise = 1; - } - if ((s->reg_rtcalm & 0x04) && - (to_bcd((uint8_t)stm.tm_hour) == (uint8_t)s->reg_almhour)) { - alarm_raise = 1; - } - if ((s->reg_rtcalm & 0x08) && - (to_bcd((uint8_t)stm.tm_mday) == (uint8_t)s->reg_almday)) { - alarm_raise = 1; - } - if ((s->reg_rtcalm & 0x10) && - (to_bcd((uint8_t)stm.tm_mon) == (uint8_t)s->reg_almmon)) { - alarm_raise = 1; - } - if ((s->reg_rtcalm & 0x20) && - (BCD3DIGITS(stm.tm_year) == s->reg_almyear)) { - alarm_raise = 1; - } - - if (alarm_raise) { - DPRINTF("ALARM IRQ\n"); - /* set irq status */ - s->reg_intp |= INTP_ALM_ENABLE; - qemu_irq_raise(s->alm_irq); - } -} - -/* - * RTC update frequency - * Parameters: - * reg_value - current RTCCON register or his new value - */ -static void exynos4210_rtc_update_freq(Exynos4210RTCState *s, - uint32_t reg_value) -{ - uint32_t freq; - - freq = s->freq; - /* set frequncy for time generator */ - s->freq = RTC_BASE_FREQ / (1 << TICCKSEL(reg_value)); - - if (freq != s->freq) { - ptimer_set_freq(s->ptimer, s->freq); - DPRINTF("freq=%dHz\n", s->freq); - } -} - -/* month is between 0 and 11. */ -static int get_days_in_month(int month, int year) -{ - static const int days_tab[12] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 - }; - int d; - if ((unsigned)month >= 12) { - return 31; - } - d = days_tab[month]; - if (month == 1) { - if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) { - d++; - } - } - return d; -} - -/* update 'tm' to the next second */ -static void rtc_next_second(struct tm *tm) -{ - int days_in_month; - - tm->tm_sec++; - if ((unsigned)tm->tm_sec >= 60) { - tm->tm_sec = 0; - tm->tm_min++; - if ((unsigned)tm->tm_min >= 60) { - tm->tm_min = 0; - tm->tm_hour++; - if ((unsigned)tm->tm_hour >= 24) { - tm->tm_hour = 0; - /* next day */ - tm->tm_wday++; - if ((unsigned)tm->tm_wday >= 7) { - tm->tm_wday = 0; - } - days_in_month = get_days_in_month(tm->tm_mon, - tm->tm_year + 1900); - tm->tm_mday++; - if (tm->tm_mday < 1) { - tm->tm_mday = 1; - } else if (tm->tm_mday > days_in_month) { - tm->tm_mday = 1; - tm->tm_mon++; - if (tm->tm_mon >= 12) { - tm->tm_mon = 0; - tm->tm_year++; - } - } - } - } - } -} - -/* - * tick handler - */ -static void exynos4210_rtc_tick(void *opaque) -{ - Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; - - DPRINTF("TICK IRQ\n"); - /* set irq status */ - s->reg_intp |= INTP_TICK_ENABLE; - /* raise IRQ */ - qemu_irq_raise(s->tick_irq); - - /* restart timer */ - ptimer_set_count(s->ptimer, s->reg_ticcnt); - ptimer_run(s->ptimer, 1); -} - -/* - * 1Hz clock handler - */ -static void exynos4210_rtc_1Hz_tick(void *opaque) -{ - Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; - - rtc_next_second(&s->current_tm); - /* DPRINTF("1Hz tick\n"); */ - - /* raise IRQ */ - if (s->reg_rtcalm & ALARM_INT_ENABLE) { - check_alarm_raise(s); - } - - ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ); - ptimer_run(s->ptimer_1Hz, 1); -} - -/* - * RTC Read - */ -static uint64_t exynos4210_rtc_read(void *opaque, hwaddr offset, - unsigned size) -{ - uint32_t value = 0; - Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; - - switch (offset) { - case INTP: - value = s->reg_intp; - break; - case RTCCON: - value = s->reg_rtccon; - break; - case TICCNT: - value = s->reg_ticcnt; - break; - case RTCALM: - value = s->reg_rtcalm; - break; - case ALMSEC: - value = s->reg_almsec; - break; - case ALMMIN: - value = s->reg_almmin; - break; - case ALMHOUR: - value = s->reg_almhour; - break; - case ALMDAY: - value = s->reg_almday; - break; - case ALMMON: - value = s->reg_almmon; - break; - case ALMYEAR: - value = s->reg_almyear; - break; - - case BCDSEC: - value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_sec); - break; - case BCDMIN: - value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_min); - break; - case BCDHOUR: - value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_hour); - break; - case BCDDAYWEEK: - value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_wday); - break; - case BCDDAY: - value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mday); - break; - case BCDMON: - value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mon + 1); - break; - case BCDYEAR: - value = BCD3DIGITS(s->current_tm.tm_year); - break; - - case CURTICNT: - s->reg_curticcnt = ptimer_get_count(s->ptimer); - value = s->reg_curticcnt; - break; - - default: - fprintf(stderr, - "[exynos4210.rtc: bad read offset " TARGET_FMT_plx "]\n", - offset); - break; - } - return value; -} - -/* - * RTC Write - */ -static void exynos4210_rtc_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - Exynos4210RTCState *s = (Exynos4210RTCState *)opaque; - - switch (offset) { - case INTP: - if (value & INTP_ALM_ENABLE) { - qemu_irq_lower(s->alm_irq); - s->reg_intp &= (~INTP_ALM_ENABLE); - } - if (value & INTP_TICK_ENABLE) { - qemu_irq_lower(s->tick_irq); - s->reg_intp &= (~INTP_TICK_ENABLE); - } - break; - case RTCCON: - if (value & RTC_ENABLE) { - exynos4210_rtc_update_freq(s, value); - } - if ((value & RTC_ENABLE) > (s->reg_rtccon & RTC_ENABLE)) { - /* clock timer */ - ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ); - ptimer_run(s->ptimer_1Hz, 1); - DPRINTF("run clock timer\n"); - } - if ((value & RTC_ENABLE) < (s->reg_rtccon & RTC_ENABLE)) { - /* tick timer */ - ptimer_stop(s->ptimer); - /* clock timer */ - ptimer_stop(s->ptimer_1Hz); - DPRINTF("stop all timers\n"); - } - if (value & RTC_ENABLE) { - if ((value & TICK_TIMER_ENABLE) > - (s->reg_rtccon & TICK_TIMER_ENABLE) && - (s->reg_ticcnt)) { - ptimer_set_count(s->ptimer, s->reg_ticcnt); - ptimer_run(s->ptimer, 1); - DPRINTF("run tick timer\n"); - } - if ((value & TICK_TIMER_ENABLE) < - (s->reg_rtccon & TICK_TIMER_ENABLE)) { - ptimer_stop(s->ptimer); - } - } - s->reg_rtccon = value; - break; - case TICCNT: - if (value > TICNT_THRESHOLD) { - s->reg_ticcnt = value; - } else { - fprintf(stderr, - "[exynos4210.rtc: bad TICNT value %u ]\n", - (uint32_t)value); - } - break; - - case RTCALM: - s->reg_rtcalm = value; - break; - case ALMSEC: - s->reg_almsec = (value & 0x7f); - break; - case ALMMIN: - s->reg_almmin = (value & 0x7f); - break; - case ALMHOUR: - s->reg_almhour = (value & 0x3f); - break; - case ALMDAY: - s->reg_almday = (value & 0x3f); - break; - case ALMMON: - s->reg_almmon = (value & 0x1f); - break; - case ALMYEAR: - s->reg_almyear = (value & 0x0fff); - break; - - case BCDSEC: - if (s->reg_rtccon & RTC_ENABLE) { - s->current_tm.tm_sec = (int)from_bcd((uint8_t)value); - } - break; - case BCDMIN: - if (s->reg_rtccon & RTC_ENABLE) { - s->current_tm.tm_min = (int)from_bcd((uint8_t)value); - } - break; - case BCDHOUR: - if (s->reg_rtccon & RTC_ENABLE) { - s->current_tm.tm_hour = (int)from_bcd((uint8_t)value); - } - break; - case BCDDAYWEEK: - if (s->reg_rtccon & RTC_ENABLE) { - s->current_tm.tm_wday = (int)from_bcd((uint8_t)value); - } - break; - case BCDDAY: - if (s->reg_rtccon & RTC_ENABLE) { - s->current_tm.tm_mday = (int)from_bcd((uint8_t)value); - } - break; - case BCDMON: - if (s->reg_rtccon & RTC_ENABLE) { - s->current_tm.tm_mon = (int)from_bcd((uint8_t)value) - 1; - } - break; - case BCDYEAR: - if (s->reg_rtccon & RTC_ENABLE) { - /* 3 digits */ - s->current_tm.tm_year = (int)from_bcd((uint8_t)value) + - (int)from_bcd((uint8_t)((value >> 8) & 0x0f)) * 100; - } - break; - - default: - fprintf(stderr, - "[exynos4210.rtc: bad write offset " TARGET_FMT_plx "]\n", - offset); - break; - - } -} - -/* - * Set default values to timer fields and registers - */ -static void exynos4210_rtc_reset(DeviceState *d) -{ - Exynos4210RTCState *s = EXYNOS4210_RTC(d); - - qemu_get_timedate(&s->current_tm, 0); - - DPRINTF("Get time from host: %d-%d-%d %2d:%02d:%02d\n", - s->current_tm.tm_year, s->current_tm.tm_mon, s->current_tm.tm_mday, - s->current_tm.tm_hour, s->current_tm.tm_min, s->current_tm.tm_sec); - - s->reg_intp = 0; - s->reg_rtccon = 0; - s->reg_ticcnt = 0; - s->reg_rtcalm = 0; - s->reg_almsec = 0; - s->reg_almmin = 0; - s->reg_almhour = 0; - s->reg_almday = 0; - s->reg_almmon = 0; - s->reg_almyear = 0; - - s->reg_curticcnt = 0; - - exynos4210_rtc_update_freq(s, s->reg_rtccon); - ptimer_stop(s->ptimer); - ptimer_stop(s->ptimer_1Hz); -} - -static const MemoryRegionOps exynos4210_rtc_ops = { - .read = exynos4210_rtc_read, - .write = exynos4210_rtc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* - * RTC timer initialization - */ -static void exynos4210_rtc_init(Object *obj) -{ - Exynos4210RTCState *s = EXYNOS4210_RTC(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); - QEMUBH *bh; - - bh = qemu_bh_new(exynos4210_rtc_tick, s); - s->ptimer = ptimer_init(bh); - ptimer_set_freq(s->ptimer, RTC_BASE_FREQ); - exynos4210_rtc_update_freq(s, 0); - - bh = qemu_bh_new(exynos4210_rtc_1Hz_tick, s); - s->ptimer_1Hz = ptimer_init(bh); - ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ); - - sysbus_init_irq(dev, &s->alm_irq); - sysbus_init_irq(dev, &s->tick_irq); - - memory_region_init_io(&s->iomem, obj, &exynos4210_rtc_ops, s, - "exynos4210-rtc", EXYNOS4210_RTC_REG_MEM_SIZE); - sysbus_init_mmio(dev, &s->iomem); -} - -static void exynos4210_rtc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = exynos4210_rtc_reset; - dc->vmsd = &vmstate_exynos4210_rtc_state; -} - -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, -}; - -static void exynos4210_rtc_register_types(void) -{ - type_register_static(&exynos4210_rtc_info); -} - -type_init(exynos4210_rtc_register_types) diff --git a/qemu/hw/timer/grlib_gptimer.c b/qemu/hw/timer/grlib_gptimer.c deleted file mode 100644 index dd000f5af..000000000 --- a/qemu/hw/timer/grlib_gptimer.c +++ /dev/null @@ -1,412 +0,0 @@ -/* - * QEMU GRLIB GPTimer Emulator - * - * Copyright (c) 2010-2011 AdaCore - * - * 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/sysbus.h" -#include "qemu/timer.h" -#include "hw/ptimer.h" -#include "qemu/timer.h" -#include "qemu/main-loop.h" - -#include "trace.h" - -#define UNIT_REG_SIZE 16 /* Size of memory mapped regs for the unit */ -#define GPTIMER_REG_SIZE 16 /* Size of memory mapped regs for a GPTimer */ - -#define GPTIMER_MAX_TIMERS 8 - -/* GPTimer Config register fields */ -#define GPTIMER_ENABLE (1 << 0) -#define GPTIMER_RESTART (1 << 1) -#define GPTIMER_LOAD (1 << 2) -#define GPTIMER_INT_ENABLE (1 << 3) -#define GPTIMER_INT_PENDING (1 << 4) -#define GPTIMER_CHAIN (1 << 5) /* Not supported */ -#define GPTIMER_DEBUG_HALT (1 << 6) /* Not supported */ - -/* Memory mapped register offsets */ -#define SCALER_OFFSET 0x00 -#define SCALER_RELOAD_OFFSET 0x04 -#define CONFIG_OFFSET 0x08 -#define COUNTER_OFFSET 0x00 -#define COUNTER_RELOAD_OFFSET 0x04 -#define TIMER_BASE 0x10 - -#define TYPE_GRLIB_GPTIMER "grlib,gptimer" -#define GRLIB_GPTIMER(obj) \ - OBJECT_CHECK(GPTimerUnit, (obj), TYPE_GRLIB_GPTIMER) - -typedef struct GPTimer GPTimer; -typedef struct GPTimerUnit GPTimerUnit; - -struct GPTimer { - QEMUBH *bh; - struct ptimer_state *ptimer; - - qemu_irq irq; - int id; - GPTimerUnit *unit; - - /* registers */ - uint32_t counter; - uint32_t reload; - uint32_t config; -}; - -struct GPTimerUnit { - SysBusDevice parent_obj; - - MemoryRegion iomem; - - uint32_t nr_timers; /* Number of timers available */ - uint32_t freq_hz; /* System frequency */ - uint32_t irq_line; /* Base irq line */ - - GPTimer *timers; - - /* registers */ - uint32_t scaler; - uint32_t reload; - uint32_t config; -}; - -static void grlib_gptimer_enable(GPTimer *timer) -{ - assert(timer != NULL); - - - ptimer_stop(timer->ptimer); - - if (!(timer->config & GPTIMER_ENABLE)) { - /* Timer disabled */ - trace_grlib_gptimer_disabled(timer->id, timer->config); - return; - } - - /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at - underflow. Set count + 1 to simulate the GPTimer behavior. */ - - trace_grlib_gptimer_enable(timer->id, timer->counter); - - ptimer_set_count(timer->ptimer, (uint64_t)timer->counter + 1); - ptimer_run(timer->ptimer, 1); -} - -static void grlib_gptimer_restart(GPTimer *timer) -{ - assert(timer != NULL); - - trace_grlib_gptimer_restart(timer->id, timer->reload); - - timer->counter = timer->reload; - grlib_gptimer_enable(timer); -} - -static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler) -{ - int i = 0; - uint32_t value = 0; - - assert(unit != NULL); - - if (scaler > 0) { - value = unit->freq_hz / (scaler + 1); - } else { - value = unit->freq_hz; - } - - trace_grlib_gptimer_set_scaler(scaler, value); - - for (i = 0; i < unit->nr_timers; i++) { - ptimer_set_freq(unit->timers[i].ptimer, value); - } -} - -static void grlib_gptimer_hit(void *opaque) -{ - GPTimer *timer = opaque; - assert(timer != NULL); - - trace_grlib_gptimer_hit(timer->id); - - /* Timer expired */ - - if (timer->config & GPTIMER_INT_ENABLE) { - /* Set the pending bit (only unset by write in the config register) */ - timer->config |= GPTIMER_INT_PENDING; - qemu_irq_pulse(timer->irq); - } - - if (timer->config & GPTIMER_RESTART) { - grlib_gptimer_restart(timer); - } -} - -static uint64_t grlib_gptimer_read(void *opaque, hwaddr addr, - unsigned size) -{ - GPTimerUnit *unit = opaque; - hwaddr timer_addr; - int id; - uint32_t value = 0; - - addr &= 0xff; - - /* Unit registers */ - switch (addr) { - case SCALER_OFFSET: - trace_grlib_gptimer_readl(-1, addr, unit->scaler); - return unit->scaler; - - case SCALER_RELOAD_OFFSET: - trace_grlib_gptimer_readl(-1, addr, unit->reload); - return unit->reload; - - case CONFIG_OFFSET: - trace_grlib_gptimer_readl(-1, addr, unit->config); - return unit->config; - - default: - break; - } - - timer_addr = (addr % TIMER_BASE); - id = (addr - TIMER_BASE) / TIMER_BASE; - - if (id >= 0 && id < unit->nr_timers) { - - /* GPTimer registers */ - switch (timer_addr) { - case COUNTER_OFFSET: - value = ptimer_get_count(unit->timers[id].ptimer); - trace_grlib_gptimer_readl(id, addr, value); - return value; - - case COUNTER_RELOAD_OFFSET: - value = unit->timers[id].reload; - trace_grlib_gptimer_readl(id, addr, value); - return value; - - case CONFIG_OFFSET: - trace_grlib_gptimer_readl(id, addr, unit->timers[id].config); - return unit->timers[id].config; - - default: - break; - } - - } - - trace_grlib_gptimer_readl(-1, addr, 0); - return 0; -} - -static void grlib_gptimer_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - GPTimerUnit *unit = opaque; - hwaddr timer_addr; - int id; - - addr &= 0xff; - - /* Unit registers */ - switch (addr) { - case SCALER_OFFSET: - value &= 0xFFFF; /* clean up the value */ - unit->scaler = value; - trace_grlib_gptimer_writel(-1, addr, unit->scaler); - return; - - case SCALER_RELOAD_OFFSET: - value &= 0xFFFF; /* clean up the value */ - unit->reload = value; - trace_grlib_gptimer_writel(-1, addr, unit->reload); - grlib_gptimer_set_scaler(unit, value); - return; - - case CONFIG_OFFSET: - /* Read Only (disable timer freeze not supported) */ - trace_grlib_gptimer_writel(-1, addr, 0); - return; - - default: - break; - } - - timer_addr = (addr % TIMER_BASE); - id = (addr - TIMER_BASE) / TIMER_BASE; - - if (id >= 0 && id < unit->nr_timers) { - - /* GPTimer registers */ - switch (timer_addr) { - case COUNTER_OFFSET: - trace_grlib_gptimer_writel(id, addr, value); - unit->timers[id].counter = value; - grlib_gptimer_enable(&unit->timers[id]); - return; - - case COUNTER_RELOAD_OFFSET: - trace_grlib_gptimer_writel(id, addr, value); - unit->timers[id].reload = value; - return; - - case CONFIG_OFFSET: - trace_grlib_gptimer_writel(id, addr, value); - - if (value & GPTIMER_INT_PENDING) { - /* clear pending bit */ - value &= ~GPTIMER_INT_PENDING; - } else { - /* keep pending bit */ - value |= unit->timers[id].config & GPTIMER_INT_PENDING; - } - - unit->timers[id].config = value; - - /* gptimer_restart calls gptimer_enable, so if "enable" and "load" - bits are present, we just have to call restart. */ - - if (value & GPTIMER_LOAD) { - grlib_gptimer_restart(&unit->timers[id]); - } else if (value & GPTIMER_ENABLE) { - grlib_gptimer_enable(&unit->timers[id]); - } - - /* These fields must always be read as 0 */ - value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT); - - unit->timers[id].config = value; - return; - - default: - break; - } - - } - - trace_grlib_gptimer_writel(-1, addr, value); -} - -static const MemoryRegionOps grlib_gptimer_ops = { - .read = grlib_gptimer_read, - .write = grlib_gptimer_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void grlib_gptimer_reset(DeviceState *d) -{ - GPTimerUnit *unit = GRLIB_GPTIMER(d); - int i = 0; - - assert(unit != NULL); - - unit->scaler = 0; - unit->reload = 0; - - unit->config = unit->nr_timers; - unit->config |= unit->irq_line << 3; - unit->config |= 1 << 8; /* separate interrupt */ - unit->config |= 1 << 9; /* Disable timer freeze */ - - - for (i = 0; i < unit->nr_timers; i++) { - GPTimer *timer = &unit->timers[i]; - - timer->counter = 0; - timer->reload = 0; - timer->config = 0; - ptimer_stop(timer->ptimer); - ptimer_set_count(timer->ptimer, 0); - ptimer_set_freq(timer->ptimer, unit->freq_hz); - } -} - -static int grlib_gptimer_init(SysBusDevice *dev) -{ - GPTimerUnit *unit = GRLIB_GPTIMER(dev); - unsigned int i; - - assert(unit->nr_timers > 0); - assert(unit->nr_timers <= GPTIMER_MAX_TIMERS); - - unit->timers = g_malloc0(sizeof unit->timers[0] * unit->nr_timers); - - for (i = 0; i < unit->nr_timers; i++) { - GPTimer *timer = &unit->timers[i]; - - timer->unit = unit; - timer->bh = qemu_bh_new(grlib_gptimer_hit, timer); - timer->ptimer = ptimer_init(timer->bh); - timer->id = i; - - /* One IRQ line for each timer */ - sysbus_init_irq(dev, &timer->irq); - - ptimer_set_freq(timer->ptimer, unit->freq_hz); - } - - memory_region_init_io(&unit->iomem, OBJECT(unit), &grlib_gptimer_ops, - unit, "gptimer", - UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers); - - sysbus_init_mmio(dev, &unit->iomem); - return 0; -} - -static Property grlib_gptimer_properties[] = { - DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz, 40000000), - DEFINE_PROP_UINT32("irq-line", GPTimerUnit, irq_line, 8), - DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2), - DEFINE_PROP_END_OF_LIST(), -}; - -static void grlib_gptimer_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = grlib_gptimer_init; - dc->reset = grlib_gptimer_reset; - dc->props = grlib_gptimer_properties; -} - -static const TypeInfo grlib_gptimer_info = { - .name = TYPE_GRLIB_GPTIMER, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(GPTimerUnit), - .class_init = grlib_gptimer_class_init, -}; - -static void grlib_gptimer_register_types(void) -{ - type_register_static(&grlib_gptimer_info); -} - -type_init(grlib_gptimer_register_types) diff --git a/qemu/hw/timer/hpet.c b/qemu/hw/timer/hpet.c deleted file mode 100644 index a2c18b30c..000000000 --- a/qemu/hw/timer/hpet.c +++ /dev/null @@ -1,789 +0,0 @@ -/* - * High Precision Event Timer emulation - * - * Copyright (c) 2007 Alexander Graf - * Copyright (c) 2008 IBM Corporation - * - * Authors: Beth Kon - * - * 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 . - * - * ***************************************************************** - * - * 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" -#include "hw/sysbus.h" -#include "hw/timer/mc146818rtc.h" -#include "hw/timer/i8254.h" - -//#define HPET_DEBUG -#ifdef HPET_DEBUG -#define DPRINTF printf -#else -#define DPRINTF(...) -#endif - -#define HPET_MSI_SUPPORT 0 - -#define HPET(obj) OBJECT_CHECK(HPETState, (obj), TYPE_HPET) - -struct HPETState; -typedef struct HPETTimer { /* timers */ - uint8_t tn; /*timer number*/ - QEMUTimer *qemu_timer; - struct HPETState *state; - /* Memory-mapped, software visible timer registers */ - uint64_t config; /* configuration/cap */ - uint64_t cmp; /* comparator */ - uint64_t fsb; /* FSB route */ - /* Hidden register state */ - uint64_t period; /* Last value written to comparator */ - uint8_t wrap_flag; /* timer pop will indicate wrap for one-shot 32-bit - * mode. Next pop will be actual timer expiration. - */ -} HPETTimer; - -typedef struct HPETState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - uint64_t hpet_offset; - qemu_irq irqs[HPET_NUM_IRQ_ROUTES]; - uint32_t flags; - uint8_t rtc_irq_level; - qemu_irq pit_enabled; - uint8_t num_timers; - uint32_t intcap; - HPETTimer timer[HPET_MAX_TIMERS]; - - /* Memory-mapped, software visible registers */ - uint64_t capability; /* capabilities */ - uint64_t config; /* configuration */ - uint64_t isr; /* interrupt status reg */ - uint64_t hpet_counter; /* main counter */ - uint8_t hpet_id; /* instance id */ -} HPETState; - -static uint32_t hpet_in_legacy_mode(HPETState *s) -{ - return s->config & HPET_CFG_LEGACY; -} - -static uint32_t timer_int_route(struct HPETTimer *timer) -{ - return (timer->config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT; -} - -static uint32_t timer_fsb_route(HPETTimer *t) -{ - return t->config & HPET_TN_FSB_ENABLE; -} - -static uint32_t hpet_enabled(HPETState *s) -{ - return s->config & HPET_CFG_ENABLE; -} - -static uint32_t timer_is_periodic(HPETTimer *t) -{ - return t->config & HPET_TN_PERIODIC; -} - -static uint32_t timer_enabled(HPETTimer *t) -{ - return t->config & HPET_TN_ENABLE; -} - -static uint32_t hpet_time_after(uint64_t a, uint64_t b) -{ - return ((int32_t)(b - a) < 0); -} - -static uint32_t hpet_time_after64(uint64_t a, uint64_t b) -{ - return ((int64_t)(b - a) < 0); -} - -static uint64_t ticks_to_ns(uint64_t value) -{ - return value * HPET_CLK_PERIOD; -} - -static uint64_t ns_to_ticks(uint64_t value) -{ - return value / HPET_CLK_PERIOD; -} - -static uint64_t hpet_fixup_reg(uint64_t new, uint64_t old, uint64_t mask) -{ - new &= mask; - new |= old & ~mask; - return new; -} - -static int activating_bit(uint64_t old, uint64_t new, uint64_t mask) -{ - return (!(old & mask) && (new & mask)); -} - -static int deactivating_bit(uint64_t old, uint64_t new, uint64_t mask) -{ - return ((old & mask) && !(new & mask)); -} - -static uint64_t hpet_get_ticks(HPETState *s) -{ - return ns_to_ticks(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->hpet_offset); -} - -/* - * calculate diff between comparator value and current ticks - */ -static inline uint64_t hpet_calculate_diff(HPETTimer *t, uint64_t current) -{ - - if (t->config & HPET_TN_32BIT) { - uint32_t diff, cmp; - - cmp = (uint32_t)t->cmp; - diff = cmp - (uint32_t)current; - diff = (int32_t)diff > 0 ? diff : (uint32_t)1; - return (uint64_t)diff; - } else { - uint64_t diff, cmp; - - cmp = t->cmp; - diff = cmp - current; - diff = (int64_t)diff > 0 ? diff : (uint64_t)1; - return diff; - } -} - -static void update_irq(struct HPETTimer *timer, int set) -{ - uint64_t mask; - HPETState *s; - int route; - - if (timer->tn <= 1 && hpet_in_legacy_mode(timer->state)) { - /* if LegacyReplacementRoute bit is set, HPET specification requires - * timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC, - * timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC. - */ - route = (timer->tn == 0) ? 0 : RTC_ISA_IRQ; - } else { - route = timer_int_route(timer); - } - s = timer->state; - mask = 1 << timer->tn; - if (!set || !timer_enabled(timer) || !hpet_enabled(timer->state)) { - s->isr &= ~mask; - if (!timer_fsb_route(timer)) { - qemu_irq_lower(s->irqs[route]); - } - } else if (timer_fsb_route(timer)) { - address_space_stl_le(&address_space_memory, timer->fsb >> 32, - timer->fsb & 0xffffffff, MEMTXATTRS_UNSPECIFIED, - NULL); - } else if (timer->config & HPET_TN_TYPE_LEVEL) { - s->isr |= mask; - qemu_irq_raise(s->irqs[route]); - } else { - s->isr &= ~mask; - qemu_irq_pulse(s->irqs[route]); - } -} - -static void hpet_pre_save(void *opaque) -{ - HPETState *s = opaque; - - /* save current counter value */ - s->hpet_counter = hpet_get_ticks(s); -} - -static int hpet_pre_load(void *opaque) -{ - HPETState *s = opaque; - - /* version 1 only supports 3, later versions will load the actual value */ - s->num_timers = HPET_MIN_TIMERS; - return 0; -} - -static bool hpet_validate_num_timers(void *opaque, int version_id) -{ - HPETState *s = opaque; - - if (s->num_timers < HPET_MIN_TIMERS) { - return false; - } else if (s->num_timers > HPET_MAX_TIMERS) { - return false; - } - return true; -} - -static int hpet_post_load(void *opaque, int version_id) -{ - HPETState *s = opaque; - - /* Recalculate the offset between the main counter and guest time */ - s->hpet_offset = ticks_to_ns(s->hpet_counter) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - /* Push number of timers into capability returned via HPET_ID */ - s->capability &= ~HPET_ID_NUM_TIM_MASK; - s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT; - hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; - - /* Derive HPET_MSI_SUPPORT from the capability of the first timer. */ - s->flags &= ~(1 << HPET_MSI_SUPPORT); - if (s->timer[0].config & HPET_TN_FSB_CAP) { - s->flags |= 1 << HPET_MSI_SUPPORT; - } - return 0; -} - -static bool hpet_rtc_irq_level_needed(void *opaque) -{ - HPETState *s = opaque; - - return s->rtc_irq_level != 0; -} - -static const VMStateDescription vmstate_hpet_rtc_irq_level = { - .name = "hpet/rtc_irq_level", - .version_id = 1, - .minimum_version_id = 1, - .needed = hpet_rtc_irq_level_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(rtc_irq_level, HPETState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_hpet_timer = { - .name = "hpet_timer", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(tn, HPETTimer), - VMSTATE_UINT64(config, HPETTimer), - VMSTATE_UINT64(cmp, HPETTimer), - VMSTATE_UINT64(fsb, HPETTimer), - VMSTATE_UINT64(period, HPETTimer), - VMSTATE_UINT8(wrap_flag, HPETTimer), - VMSTATE_TIMER_PTR(qemu_timer, HPETTimer), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_hpet = { - .name = "hpet", - .version_id = 2, - .minimum_version_id = 1, - .pre_save = hpet_pre_save, - .pre_load = hpet_pre_load, - .post_load = hpet_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT64(config, HPETState), - VMSTATE_UINT64(isr, HPETState), - VMSTATE_UINT64(hpet_counter, HPETState), - VMSTATE_UINT8_V(num_timers, HPETState, 2), - VMSTATE_VALIDATE("num_timers in range", hpet_validate_num_timers), - VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0, - vmstate_hpet_timer, HPETTimer), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_hpet_rtc_irq_level, - NULL - } -}; - -/* - * timer expiration callback - */ -static void hpet_timer(void *opaque) -{ - HPETTimer *t = opaque; - uint64_t diff; - - uint64_t period = t->period; - uint64_t cur_tick = hpet_get_ticks(t->state); - - if (timer_is_periodic(t) && period != 0) { - if (t->config & HPET_TN_32BIT) { - while (hpet_time_after(cur_tick, t->cmp)) { - t->cmp = (uint32_t)(t->cmp + t->period); - } - } else { - while (hpet_time_after64(cur_tick, t->cmp)) { - t->cmp += period; - } - } - diff = hpet_calculate_diff(t, cur_tick); - timer_mod(t->qemu_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (int64_t)ticks_to_ns(diff)); - } else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) { - if (t->wrap_flag) { - diff = hpet_calculate_diff(t, cur_tick); - timer_mod(t->qemu_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (int64_t)ticks_to_ns(diff)); - t->wrap_flag = 0; - } - } - update_irq(t, 1); -} - -static void hpet_set_timer(HPETTimer *t) -{ - uint64_t diff; - uint32_t wrap_diff; /* how many ticks until we wrap? */ - uint64_t cur_tick = hpet_get_ticks(t->state); - - /* whenever new timer is being set up, make sure wrap_flag is 0 */ - t->wrap_flag = 0; - diff = hpet_calculate_diff(t, cur_tick); - - /* hpet spec says in one-shot 32-bit mode, generate an interrupt when - * counter wraps in addition to an interrupt with comparator match. - */ - if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) { - wrap_diff = 0xffffffff - (uint32_t)cur_tick; - if (wrap_diff < (uint32_t)diff) { - diff = wrap_diff; - t->wrap_flag = 1; - } - } - timer_mod(t->qemu_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (int64_t)ticks_to_ns(diff)); -} - -static void hpet_del_timer(HPETTimer *t) -{ - timer_del(t->qemu_timer); - update_irq(t, 0); -} - -#ifdef HPET_DEBUG -static uint32_t hpet_ram_readb(void *opaque, hwaddr addr) -{ - printf("qemu: hpet_read b at %" PRIx64 "\n", addr); - return 0; -} - -static uint32_t hpet_ram_readw(void *opaque, hwaddr addr) -{ - printf("qemu: hpet_read w at %" PRIx64 "\n", addr); - return 0; -} -#endif - -static uint64_t hpet_ram_read(void *opaque, hwaddr addr, - unsigned size) -{ - HPETState *s = opaque; - uint64_t cur_tick, index; - - DPRINTF("qemu: Enter hpet_ram_readl at %" PRIx64 "\n", addr); - index = addr; - /*address range of all TN regs*/ - if (index >= 0x100 && index <= 0x3ff) { - uint8_t timer_id = (addr - 0x100) / 0x20; - HPETTimer *timer = &s->timer[timer_id]; - - if (timer_id > s->num_timers) { - DPRINTF("qemu: timer id out of range\n"); - return 0; - } - - switch ((addr - 0x100) % 0x20) { - case HPET_TN_CFG: - return timer->config; - case HPET_TN_CFG + 4: // Interrupt capabilities - return timer->config >> 32; - case HPET_TN_CMP: // comparator register - return timer->cmp; - case HPET_TN_CMP + 4: - return timer->cmp >> 32; - case HPET_TN_ROUTE: - return timer->fsb; - case HPET_TN_ROUTE + 4: - return timer->fsb >> 32; - default: - DPRINTF("qemu: invalid hpet_ram_readl\n"); - break; - } - } else { - switch (index) { - case HPET_ID: - return s->capability; - case HPET_PERIOD: - return s->capability >> 32; - case HPET_CFG: - return s->config; - case HPET_CFG + 4: - DPRINTF("qemu: invalid HPET_CFG + 4 hpet_ram_readl\n"); - return 0; - case HPET_COUNTER: - if (hpet_enabled(s)) { - cur_tick = hpet_get_ticks(s); - } else { - cur_tick = s->hpet_counter; - } - DPRINTF("qemu: reading counter = %" PRIx64 "\n", cur_tick); - return cur_tick; - case HPET_COUNTER + 4: - if (hpet_enabled(s)) { - cur_tick = hpet_get_ticks(s); - } else { - cur_tick = s->hpet_counter; - } - DPRINTF("qemu: reading counter + 4 = %" PRIx64 "\n", cur_tick); - return cur_tick >> 32; - case HPET_STATUS: - return s->isr; - default: - DPRINTF("qemu: invalid hpet_ram_readl\n"); - break; - } - } - return 0; -} - -static void hpet_ram_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - int i; - HPETState *s = opaque; - uint64_t old_val, new_val, val, index; - - DPRINTF("qemu: Enter hpet_ram_writel at %" PRIx64 " = %#x\n", addr, value); - index = addr; - old_val = hpet_ram_read(opaque, addr, 4); - new_val = value; - - /*address range of all TN regs*/ - if (index >= 0x100 && index <= 0x3ff) { - uint8_t timer_id = (addr - 0x100) / 0x20; - HPETTimer *timer = &s->timer[timer_id]; - - DPRINTF("qemu: hpet_ram_writel timer_id = %#x\n", timer_id); - if (timer_id > s->num_timers) { - DPRINTF("qemu: timer id out of range\n"); - return; - } - switch ((addr - 0x100) % 0x20) { - case HPET_TN_CFG: - DPRINTF("qemu: hpet_ram_writel HPET_TN_CFG\n"); - if (activating_bit(old_val, new_val, HPET_TN_FSB_ENABLE)) { - update_irq(timer, 0); - } - val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK); - timer->config = (timer->config & 0xffffffff00000000ULL) | val; - if (new_val & HPET_TN_32BIT) { - timer->cmp = (uint32_t)timer->cmp; - timer->period = (uint32_t)timer->period; - } - if (activating_bit(old_val, new_val, HPET_TN_ENABLE) && - hpet_enabled(s)) { - hpet_set_timer(timer); - } else if (deactivating_bit(old_val, new_val, HPET_TN_ENABLE)) { - hpet_del_timer(timer); - } - break; - case HPET_TN_CFG + 4: // Interrupt capabilities - DPRINTF("qemu: invalid HPET_TN_CFG+4 write\n"); - break; - case HPET_TN_CMP: // comparator register - DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP\n"); - if (timer->config & HPET_TN_32BIT) { - new_val = (uint32_t)new_val; - } - if (!timer_is_periodic(timer) - || (timer->config & HPET_TN_SETVAL)) { - timer->cmp = (timer->cmp & 0xffffffff00000000ULL) | new_val; - } - if (timer_is_periodic(timer)) { - /* - * FIXME: Clamp period to reasonable min value? - * Clamp period to reasonable max value - */ - new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1; - timer->period = - (timer->period & 0xffffffff00000000ULL) | new_val; - } - timer->config &= ~HPET_TN_SETVAL; - if (hpet_enabled(s)) { - hpet_set_timer(timer); - } - break; - case HPET_TN_CMP + 4: // comparator register high order - DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP + 4\n"); - if (!timer_is_periodic(timer) - || (timer->config & HPET_TN_SETVAL)) { - timer->cmp = (timer->cmp & 0xffffffffULL) | new_val << 32; - } else { - /* - * FIXME: Clamp period to reasonable min value? - * Clamp period to reasonable max value - */ - new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1; - timer->period = - (timer->period & 0xffffffffULL) | new_val << 32; - } - timer->config &= ~HPET_TN_SETVAL; - if (hpet_enabled(s)) { - hpet_set_timer(timer); - } - break; - case HPET_TN_ROUTE: - timer->fsb = (timer->fsb & 0xffffffff00000000ULL) | new_val; - break; - case HPET_TN_ROUTE + 4: - timer->fsb = (new_val << 32) | (timer->fsb & 0xffffffff); - break; - default: - DPRINTF("qemu: invalid hpet_ram_writel\n"); - break; - } - return; - } else { - switch (index) { - case HPET_ID: - return; - case HPET_CFG: - val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); - s->config = (s->config & 0xffffffff00000000ULL) | val; - if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) { - /* Enable main counter and interrupt generation. */ - s->hpet_offset = - ticks_to_ns(s->hpet_counter) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - for (i = 0; i < s->num_timers; i++) { - if ((&s->timer[i])->cmp != ~0ULL) { - hpet_set_timer(&s->timer[i]); - } - } - } else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) { - /* Halt main counter and disable interrupt generation. */ - s->hpet_counter = hpet_get_ticks(s); - for (i = 0; i < s->num_timers; i++) { - hpet_del_timer(&s->timer[i]); - } - } - /* i8254 and RTC output pins are disabled - * when HPET is in legacy mode */ - if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) { - qemu_set_irq(s->pit_enabled, 0); - qemu_irq_lower(s->irqs[0]); - qemu_irq_lower(s->irqs[RTC_ISA_IRQ]); - } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) { - qemu_irq_lower(s->irqs[0]); - qemu_set_irq(s->pit_enabled, 1); - qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level); - } - break; - case HPET_CFG + 4: - DPRINTF("qemu: invalid HPET_CFG+4 write\n"); - break; - case HPET_STATUS: - val = new_val & s->isr; - for (i = 0; i < s->num_timers; i++) { - if (val & (1 << i)) { - update_irq(&s->timer[i], 0); - } - } - break; - case HPET_COUNTER: - if (hpet_enabled(s)) { - DPRINTF("qemu: Writing counter while HPET enabled!\n"); - } - s->hpet_counter = - (s->hpet_counter & 0xffffffff00000000ULL) | value; - DPRINTF("qemu: HPET counter written. ctr = %#x -> %" PRIx64 "\n", - value, s->hpet_counter); - break; - case HPET_COUNTER + 4: - if (hpet_enabled(s)) { - DPRINTF("qemu: Writing counter while HPET enabled!\n"); - } - s->hpet_counter = - (s->hpet_counter & 0xffffffffULL) | (((uint64_t)value) << 32); - DPRINTF("qemu: HPET counter + 4 written. ctr = %#x -> %" PRIx64 "\n", - value, s->hpet_counter); - break; - default: - DPRINTF("qemu: invalid hpet_ram_writel\n"); - break; - } - } -} - -static const MemoryRegionOps hpet_ram_ops = { - .read = hpet_ram_read, - .write = hpet_ram_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void hpet_reset(DeviceState *d) -{ - HPETState *s = HPET(d); - SysBusDevice *sbd = SYS_BUS_DEVICE(d); - int i; - - for (i = 0; i < s->num_timers; i++) { - HPETTimer *timer = &s->timer[i]; - - hpet_del_timer(timer); - timer->cmp = ~0ULL; - timer->config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP; - if (s->flags & (1 << HPET_MSI_SUPPORT)) { - timer->config |= HPET_TN_FSB_CAP; - } - /* advertise availability of ioapic int */ - timer->config |= (uint64_t)s->intcap << 32; - timer->period = 0ULL; - timer->wrap_flag = 0; - } - - qemu_set_irq(s->pit_enabled, 1); - s->hpet_counter = 0ULL; - s->hpet_offset = 0ULL; - s->config = 0ULL; - hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; - hpet_cfg.hpet[s->hpet_id].address = sbd->mmio[0].addr; - - /* to document that the RTC lowers its output on reset as well */ - s->rtc_irq_level = 0; -} - -static void hpet_handle_legacy_irq(void *opaque, int n, int level) -{ - HPETState *s = HPET(opaque); - - if (n == HPET_LEGACY_PIT_INT) { - if (!hpet_in_legacy_mode(s)) { - qemu_set_irq(s->irqs[0], level); - } - } else { - s->rtc_irq_level = level; - if (!hpet_in_legacy_mode(s)) { - qemu_set_irq(s->irqs[RTC_ISA_IRQ], level); - } - } -} - -static void hpet_init(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - HPETState *s = HPET(obj); - - /* HPET Area */ - memory_region_init_io(&s->iomem, obj, &hpet_ram_ops, s, "hpet", HPET_LEN); - sysbus_init_mmio(sbd, &s->iomem); -} - -static void hpet_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - HPETState *s = HPET(dev); - int i; - HPETTimer *timer; - - if (!s->intcap) { - error_printf("Hpet's intcap not initialized.\n"); - } - if (hpet_cfg.count == UINT8_MAX) { - /* first instance */ - hpet_cfg.count = 0; - } - - if (hpet_cfg.count == 8) { - error_setg(errp, "Only 8 instances of HPET is allowed"); - return; - } - - s->hpet_id = hpet_cfg.count++; - - for (i = 0; i < HPET_NUM_IRQ_ROUTES; i++) { - sysbus_init_irq(sbd, &s->irqs[i]); - } - - if (s->num_timers < HPET_MIN_TIMERS) { - s->num_timers = HPET_MIN_TIMERS; - } else if (s->num_timers > HPET_MAX_TIMERS) { - s->num_timers = HPET_MAX_TIMERS; - } - for (i = 0; i < HPET_MAX_TIMERS; i++) { - timer = &s->timer[i]; - timer->qemu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hpet_timer, timer); - timer->tn = i; - timer->state = s; - } - - /* 64-bit main counter; LegacyReplacementRoute. */ - s->capability = 0x8086a001ULL; - s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT; - 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); -} - -static Property hpet_device_properties[] = { - DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS), - DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false), - DEFINE_PROP_UINT32(HPET_INTCAP, HPETState, intcap, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void hpet_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = hpet_realize; - dc->reset = hpet_reset; - dc->vmsd = &vmstate_hpet; - dc->props = hpet_device_properties; -} - -static const TypeInfo hpet_device_info = { - .name = TYPE_HPET, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(HPETState), - .instance_init = hpet_init, - .class_init = hpet_device_class_init, -}; - -static void hpet_register_types(void) -{ - type_register_static(&hpet_device_info); -} - -type_init(hpet_register_types) diff --git a/qemu/hw/timer/i8254.c b/qemu/hw/timer/i8254.c deleted file mode 100644 index 5e61ad50a..000000000 --- a/qemu/hw/timer/i8254.c +++ /dev/null @@ -1,384 +0,0 @@ -/* - * QEMU 8253/8254 interval timer emulation - * - * Copyright (c) 2003-2004 Fabrice Bellard - * - * 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/i386/pc.h" -#include "hw/isa/isa.h" -#include "qemu/timer.h" -#include "hw/timer/i8254.h" -#include "hw/timer/i8254_internal.h" - -//#define DEBUG_PIT - -#define RW_STATE_LSB 1 -#define RW_STATE_MSB 2 -#define RW_STATE_WORD0 3 -#define RW_STATE_WORD1 4 - -#define PIT_CLASS(class) OBJECT_CLASS_CHECK(PITClass, (class), TYPE_I8254) -#define PIT_GET_CLASS(obj) OBJECT_GET_CLASS(PITClass, (obj), TYPE_I8254) - -typedef struct PITClass { - PITCommonClass parent_class; - - DeviceRealize parent_realize; -} PITClass; - -static void pit_irq_timer_update(PITChannelState *s, int64_t current_time); - -static int pit_get_count(PITChannelState *s) -{ - uint64_t d; - int counter; - - d = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->count_load_time, PIT_FREQ, - NANOSECONDS_PER_SECOND); - switch(s->mode) { - case 0: - case 1: - case 4: - case 5: - counter = (s->count - d) & 0xffff; - break; - case 3: - /* XXX: may be incorrect for odd counts */ - counter = s->count - ((2 * d) % s->count); - break; - default: - counter = s->count - (d % s->count); - break; - } - return counter; -} - -/* val must be 0 or 1 */ -static void pit_set_channel_gate(PITCommonState *s, PITChannelState *sc, - int val) -{ - switch (sc->mode) { - default: - case 0: - case 4: - /* XXX: just disable/enable counting */ - break; - case 1: - case 5: - if (sc->gate < val) { - /* restart counting on rising edge */ - sc->count_load_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - pit_irq_timer_update(sc, sc->count_load_time); - } - break; - case 2: - case 3: - if (sc->gate < val) { - /* restart counting on rising edge */ - sc->count_load_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - pit_irq_timer_update(sc, sc->count_load_time); - } - /* XXX: disable/enable counting */ - break; - } - sc->gate = val; -} - -static inline void pit_load_count(PITChannelState *s, int val) -{ - if (val == 0) - val = 0x10000; - s->count_load_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - s->count = val; - pit_irq_timer_update(s, s->count_load_time); -} - -/* if already latched, do not latch again */ -static void pit_latch_count(PITChannelState *s) -{ - if (!s->count_latched) { - s->latched_count = pit_get_count(s); - s->count_latched = s->rw_mode; - } -} - -static void pit_ioport_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - PITCommonState *pit = opaque; - int channel, access; - PITChannelState *s; - - addr &= 3; - if (addr == 3) { - channel = val >> 6; - if (channel == 3) { - /* read back command */ - for(channel = 0; channel < 3; channel++) { - s = &pit->channels[channel]; - if (val & (2 << channel)) { - if (!(val & 0x20)) { - pit_latch_count(s); - } - if (!(val & 0x10) && !s->status_latched) { - /* status latch */ - /* XXX: add BCD and null count */ - s->status = - (pit_get_out(s, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) << 7) | - (s->rw_mode << 4) | - (s->mode << 1) | - s->bcd; - s->status_latched = 1; - } - } - } - } else { - s = &pit->channels[channel]; - access = (val >> 4) & 3; - if (access == 0) { - pit_latch_count(s); - } else { - s->rw_mode = access; - s->read_state = access; - s->write_state = access; - - s->mode = (val >> 1) & 7; - s->bcd = val & 1; - /* XXX: update irq timer ? */ - } - } - } else { - s = &pit->channels[addr]; - switch(s->write_state) { - default: - case RW_STATE_LSB: - pit_load_count(s, val); - break; - case RW_STATE_MSB: - pit_load_count(s, val << 8); - break; - case RW_STATE_WORD0: - s->write_latch = val; - s->write_state = RW_STATE_WORD1; - break; - case RW_STATE_WORD1: - pit_load_count(s, s->write_latch | (val << 8)); - s->write_state = RW_STATE_WORD0; - break; - } - } -} - -static uint64_t pit_ioport_read(void *opaque, hwaddr addr, - unsigned size) -{ - PITCommonState *pit = opaque; - int ret, count; - PITChannelState *s; - - addr &= 3; - - if (addr == 3) { - /* Mode/Command register is write only, read is ignored */ - return 0; - } - - s = &pit->channels[addr]; - if (s->status_latched) { - s->status_latched = 0; - ret = s->status; - } else if (s->count_latched) { - switch(s->count_latched) { - default: - case RW_STATE_LSB: - ret = s->latched_count & 0xff; - s->count_latched = 0; - break; - case RW_STATE_MSB: - ret = s->latched_count >> 8; - s->count_latched = 0; - break; - case RW_STATE_WORD0: - ret = s->latched_count & 0xff; - s->count_latched = RW_STATE_MSB; - break; - } - } else { - switch(s->read_state) { - default: - case RW_STATE_LSB: - count = pit_get_count(s); - ret = count & 0xff; - break; - case RW_STATE_MSB: - count = pit_get_count(s); - ret = (count >> 8) & 0xff; - break; - case RW_STATE_WORD0: - count = pit_get_count(s); - ret = count & 0xff; - s->read_state = RW_STATE_WORD1; - break; - case RW_STATE_WORD1: - count = pit_get_count(s); - ret = (count >> 8) & 0xff; - s->read_state = RW_STATE_WORD0; - break; - } - } - return ret; -} - -static void pit_irq_timer_update(PITChannelState *s, int64_t current_time) -{ - int64_t expire_time; - int irq_level; - - if (!s->irq_timer || s->irq_disabled) { - return; - } - expire_time = pit_get_next_transition_time(s, current_time); - irq_level = pit_get_out(s, current_time); - qemu_set_irq(s->irq, irq_level); -#ifdef DEBUG_PIT - printf("irq_level=%d next_delay=%f\n", - irq_level, - (double)(expire_time - current_time) / NANOSECONDS_PER_SECOND); -#endif - s->next_transition_time = expire_time; - if (expire_time != -1) - timer_mod(s->irq_timer, expire_time); - else - timer_del(s->irq_timer); -} - -static void pit_irq_timer(void *opaque) -{ - PITChannelState *s = opaque; - - pit_irq_timer_update(s, s->next_transition_time); -} - -static void pit_reset(DeviceState *dev) -{ - PITCommonState *pit = PIT_COMMON(dev); - PITChannelState *s; - - pit_reset_common(pit); - - s = &pit->channels[0]; - if (!s->irq_disabled) { - timer_mod(s->irq_timer, s->next_transition_time); - } -} - -/* When HPET is operating in legacy mode, suppress the ignored timer IRQ, - * reenable it when legacy mode is left again. */ -static void pit_irq_control(void *opaque, int n, int enable) -{ - PITCommonState *pit = opaque; - PITChannelState *s = &pit->channels[0]; - - if (enable) { - s->irq_disabled = 0; - pit_irq_timer_update(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - } else { - s->irq_disabled = 1; - timer_del(s->irq_timer); - } -} - -static const MemoryRegionOps pit_ioport_ops = { - .read = pit_ioport_read, - .write = pit_ioport_write, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void pit_post_load(PITCommonState *s) -{ - PITChannelState *sc = &s->channels[0]; - - if (sc->next_transition_time != -1) { - timer_mod(sc->irq_timer, sc->next_transition_time); - } else { - timer_del(sc->irq_timer); - } -} - -static void pit_realizefn(DeviceState *dev, Error **errp) -{ - PITCommonState *pit = PIT_COMMON(dev); - PITClass *pc = PIT_GET_CLASS(dev); - PITChannelState *s; - - s = &pit->channels[0]; - /* the timer 0 is connected to an IRQ */ - s->irq_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pit_irq_timer, s); - qdev_init_gpio_out(dev, &s->irq, 1); - - memory_region_init_io(&pit->ioports, OBJECT(pit), &pit_ioport_ops, - pit, "pit", 4); - - qdev_init_gpio_in(dev, pit_irq_control, 1); - - pc->parent_realize(dev, errp); -} - -static Property pit_properties[] = { - DEFINE_PROP_UINT32("iobase", PITCommonState, iobase, -1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pit_class_initfn(ObjectClass *klass, void *data) -{ - PITClass *pc = PIT_CLASS(klass); - PITCommonClass *k = PIT_COMMON_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - pc->parent_realize = dc->realize; - dc->realize = pit_realizefn; - k->set_channel_gate = pit_set_channel_gate; - k->get_channel_info = pit_get_channel_info_common; - k->post_load = pit_post_load; - dc->reset = pit_reset; - dc->props = pit_properties; -} - -static const TypeInfo pit_info = { - .name = TYPE_I8254, - .parent = TYPE_PIT_COMMON, - .instance_size = sizeof(PITCommonState), - .class_init = pit_class_initfn, - .class_size = sizeof(PITClass), -}; - -static void pit_register_types(void) -{ - type_register_static(&pit_info); -} - -type_init(pit_register_types) diff --git a/qemu/hw/timer/i8254_common.c b/qemu/hw/timer/i8254_common.c deleted file mode 100644 index e18299a48..000000000 --- a/qemu/hw/timer/i8254_common.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * QEMU 8253/8254 - common bits of emulated and KVM kernel model - * - * Copyright (c) 2003-2004 Fabrice Bellard - * Copyright (c) 2012 Jan Kiszka, Siemens AG - * - * 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/i386/pc.h" -#include "hw/isa/isa.h" -#include "qemu/timer.h" -#include "hw/timer/i8254.h" -#include "hw/timer/i8254_internal.h" - -/* val must be 0 or 1 */ -void pit_set_gate(ISADevice *dev, int channel, int val) -{ - PITCommonState *pit = PIT_COMMON(dev); - PITChannelState *s = &pit->channels[channel]; - PITCommonClass *c = PIT_COMMON_GET_CLASS(pit); - - c->set_channel_gate(pit, s, val); -} - -/* get pit output bit */ -int pit_get_out(PITChannelState *s, int64_t current_time) -{ - uint64_t d; - int out; - - d = muldiv64(current_time - s->count_load_time, PIT_FREQ, - NANOSECONDS_PER_SECOND); - switch (s->mode) { - default: - case 0: - out = (d >= s->count); - break; - case 1: - out = (d < s->count); - break; - case 2: - if ((d % s->count) == 0 && d != 0) { - out = 1; - } else { - out = 0; - } - break; - case 3: - out = (d % s->count) < ((s->count + 1) >> 1); - break; - case 4: - case 5: - out = (d == s->count); - break; - } - return out; -} - -/* return -1 if no transition will occur. */ -int64_t pit_get_next_transition_time(PITChannelState *s, int64_t current_time) -{ - uint64_t d, next_time, base; - int period2; - - d = muldiv64(current_time - s->count_load_time, PIT_FREQ, - NANOSECONDS_PER_SECOND); - switch (s->mode) { - default: - case 0: - case 1: - if (d < s->count) { - next_time = s->count; - } else { - return -1; - } - break; - case 2: - base = (d / s->count) * s->count; - if ((d - base) == 0 && d != 0) { - next_time = base + s->count; - } else { - next_time = base + s->count + 1; - } - break; - case 3: - base = (d / s->count) * s->count; - period2 = ((s->count + 1) >> 1); - if ((d - base) < period2) { - next_time = base + period2; - } else { - next_time = base + s->count; - } - break; - case 4: - case 5: - if (d < s->count) { - next_time = s->count; - } else if (d == s->count) { - next_time = s->count + 1; - } else { - return -1; - } - break; - } - /* convert to timer units */ - 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 */ - if (next_time <= current_time) { - next_time = current_time + 1; - } - return next_time; -} - -void pit_get_channel_info_common(PITCommonState *s, PITChannelState *sc, - PITChannelInfo *info) -{ - info->gate = sc->gate; - info->mode = sc->mode; - info->initial_count = sc->count; - info->out = pit_get_out(sc, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); -} - -void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info) -{ - PITCommonState *pit = PIT_COMMON(dev); - PITChannelState *s = &pit->channels[channel]; - PITCommonClass *c = PIT_COMMON_GET_CLASS(pit); - - c->get_channel_info(pit, s, info); -} - -void pit_reset_common(PITCommonState *pit) -{ - PITChannelState *s; - int i; - - for (i = 0; i < 3; i++) { - s = &pit->channels[i]; - s->mode = 3; - s->gate = (i != 2); - s->count_load_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - s->count = 0x10000; - if (i == 0 && !s->irq_disabled) { - s->next_transition_time = - pit_get_next_transition_time(s, s->count_load_time); - } - } -} - -static void pit_common_realize(DeviceState *dev, Error **errp) -{ - ISADevice *isadev = ISA_DEVICE(dev); - PITCommonState *pit = PIT_COMMON(dev); - - isa_register_ioport(isadev, &pit->ioports, pit->iobase); - - qdev_set_legacy_instance_id(dev, pit->iobase, 2); -} - -static const VMStateDescription vmstate_pit_channel = { - .name = "pit channel", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_INT32(count, PITChannelState), - VMSTATE_UINT16(latched_count, PITChannelState), - VMSTATE_UINT8(count_latched, PITChannelState), - VMSTATE_UINT8(status_latched, PITChannelState), - VMSTATE_UINT8(status, PITChannelState), - VMSTATE_UINT8(read_state, PITChannelState), - VMSTATE_UINT8(write_state, PITChannelState), - VMSTATE_UINT8(write_latch, PITChannelState), - VMSTATE_UINT8(rw_mode, PITChannelState), - VMSTATE_UINT8(mode, PITChannelState), - VMSTATE_UINT8(bcd, PITChannelState), - VMSTATE_UINT8(gate, PITChannelState), - VMSTATE_INT64(count_load_time, PITChannelState), - VMSTATE_INT64(next_transition_time, PITChannelState), - VMSTATE_END_OF_LIST() - } -}; - -static int pit_load_old(QEMUFile *f, void *opaque, int version_id) -{ - PITCommonState *pit = opaque; - PITCommonClass *c = PIT_COMMON_GET_CLASS(pit); - PITChannelState *s; - int i; - - if (version_id != 1) { - return -EINVAL; - } - - for (i = 0; i < 3; i++) { - s = &pit->channels[i]; - s->count = qemu_get_be32(f); - qemu_get_be16s(f, &s->latched_count); - qemu_get_8s(f, &s->count_latched); - qemu_get_8s(f, &s->status_latched); - qemu_get_8s(f, &s->status); - qemu_get_8s(f, &s->read_state); - qemu_get_8s(f, &s->write_state); - qemu_get_8s(f, &s->write_latch); - qemu_get_8s(f, &s->rw_mode); - qemu_get_8s(f, &s->mode); - qemu_get_8s(f, &s->bcd); - qemu_get_8s(f, &s->gate); - s->count_load_time = qemu_get_be64(f); - s->irq_disabled = 0; - if (i == 0) { - s->next_transition_time = qemu_get_be64(f); - } - } - if (c->post_load) { - c->post_load(pit); - } - return 0; -} - -static void pit_dispatch_pre_save(void *opaque) -{ - PITCommonState *s = opaque; - PITCommonClass *c = PIT_COMMON_GET_CLASS(s); - - if (c->pre_save) { - c->pre_save(s); - } -} - -static int pit_dispatch_post_load(void *opaque, int version_id) -{ - PITCommonState *s = opaque; - PITCommonClass *c = PIT_COMMON_GET_CLASS(s); - - if (c->post_load) { - c->post_load(s); - } - return 0; -} - -static const VMStateDescription vmstate_pit_common = { - .name = "i8254", - .version_id = 3, - .minimum_version_id = 2, - .minimum_version_id_old = 1, - .load_state_old = pit_load_old, - .pre_save = pit_dispatch_pre_save, - .post_load = pit_dispatch_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32_V(channels[0].irq_disabled, PITCommonState, 3), - VMSTATE_STRUCT_ARRAY(channels, PITCommonState, 3, 2, - vmstate_pit_channel, PITChannelState), - VMSTATE_INT64(channels[0].next_transition_time, - PITCommonState), /* formerly irq_timer */ - VMSTATE_END_OF_LIST() - } -}; - -static void pit_common_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = pit_common_realize; - dc->vmsd = &vmstate_pit_common; - /* - * Reason: unlike ordinary ISA devices, the PIT may need to be - * wired to the HPET, and because of that, some wiring is always - * done by board code. - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo pit_common_type = { - .name = TYPE_PIT_COMMON, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(PITCommonState), - .class_size = sizeof(PITCommonClass), - .class_init = pit_common_class_init, - .abstract = true, -}; - -static void register_devices(void) -{ - type_register_static(&pit_common_type); -} - -type_init(register_devices); diff --git a/qemu/hw/timer/imx_epit.c b/qemu/hw/timer/imx_epit.c deleted file mode 100644 index f5836e21f..000000000 --- a/qemu/hw/timer/imx_epit.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * IMX EPIT Timer - * - * Copyright (c) 2008 OK Labs - * Copyright (c) 2011 NICTA Pty Ltd - * Originally written by Hans Jiang - * Updated by Peter Chubb - * Updated by Jean-Christophe Dubois - * - * This code is licensed under GPL version 2 or later. See - * the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "hw/timer/imx_epit.h" -#include "hw/misc/imx_ccm.h" -#include "qemu/main-loop.h" - -#ifndef DEBUG_IMX_EPIT -#define DEBUG_IMX_EPIT 0 -#endif - -#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) -{ - switch (reg) { - case 0: - return "CR"; - case 1: - return "SR"; - case 2: - return "LR"; - case 3: - return "CMP"; - case 4: - return "CNT"; - default: - return "[?]"; - } -} - -/* - * Exact clock frequencies vary from board to board. - * These are typical. - */ -static const IMXClk imx_epit_clocks[] = { - CLK_NONE, /* 00 disabled */ - CLK_IPG, /* 01 ipg_clk, ~532MHz */ - CLK_IPG_HIGH, /* 10 ipg_clk_highfreq */ - CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */ -}; - -/* - * Update interrupt status - */ -static void imx_epit_update_int(IMXEPITState *s) -{ - if (s->sr && (s->cr & CR_OCIEN) && (s->cr & CR_EN)) { - qemu_irq_raise(s->irq); - } else { - qemu_irq_lower(s->irq); - } -} - -static void imx_epit_set_freq(IMXEPITState *s) -{ - uint32_t clksrc; - uint32_t prescaler; - - clksrc = extract32(s->cr, CR_CLKSRC_SHIFT, 2); - prescaler = 1 + extract32(s->cr, CR_PRESCALE_SHIFT, 12); - - s->freq = imx_ccm_get_clock_frequency(s->ccm, - imx_epit_clocks[clksrc]) / prescaler; - - DPRINTF("Setting ptimer frequency to %u\n", s->freq); - - if (s->freq) { - ptimer_set_freq(s->timer_reload, s->freq); - ptimer_set_freq(s->timer_cmp, s->freq); - } -} - -static void imx_epit_reset(DeviceState *dev) -{ - IMXEPITState *s = IMX_EPIT(dev); - - /* - * Soft reset doesn't touch some bits; hard reset clears them - */ - s->cr &= (CR_EN|CR_ENMOD|CR_STOPEN|CR_DOZEN|CR_WAITEN|CR_DBGEN); - s->sr = 0; - s->lr = EPIT_TIMER_MAX; - s->cmp = 0; - s->cnt = 0; - /* stop both timers */ - ptimer_stop(s->timer_cmp); - ptimer_stop(s->timer_reload); - /* compute new frequency */ - imx_epit_set_freq(s); - /* init both timers to EPIT_TIMER_MAX */ - ptimer_set_limit(s->timer_cmp, EPIT_TIMER_MAX, 1); - ptimer_set_limit(s->timer_reload, EPIT_TIMER_MAX, 1); - if (s->freq && (s->cr & CR_EN)) { - /* if the timer is still enabled, restart it */ - ptimer_run(s->timer_reload, 0); - } -} - -static uint32_t imx_epit_update_count(IMXEPITState *s) -{ - s->cnt = ptimer_get_count(s->timer_reload); - - 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; - - switch (offset >> 2) { - case 0: /* Control Register */ - reg_value = s->cr; - break; - - case 1: /* Status Register */ - reg_value = s->sr; - break; - - case 2: /* LR - ticks*/ - reg_value = s->lr; - break; - - case 3: /* CMP */ - reg_value = s->cmp; - break; - - case 4: /* CNT */ - imx_epit_update_count(s); - reg_value = s->cnt; - break; - - default: - 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(offset >> 2), reg_value); - - return reg_value; -} - -static void imx_epit_reload_compare_timer(IMXEPITState *s) -{ - if ((s->cr & (CR_EN | CR_OCIEN)) == (CR_EN | CR_OCIEN)) { - /* if the compare feature is on and timers are running */ - uint32_t tmp = imx_epit_update_count(s); - uint64_t next; - if (tmp > s->cmp) { - /* It'll fire in this round of the timer */ - next = tmp - s->cmp; - } else { /* catch it next time around */ - next = tmp - s->cmp + ((s->cr & CR_RLD) ? EPIT_TIMER_MAX : s->lr); - } - ptimer_set_count(s->timer_cmp, next); - } -} - -static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value, - unsigned size) -{ - IMXEPITState *s = IMX_EPIT(opaque); - uint64_t oldcr; - - DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(offset >> 2), - (uint32_t)value); - - switch (offset >> 2) { - case 0: /* CR */ - - oldcr = s->cr; - s->cr = value & 0x03ffffff; - if (s->cr & CR_SWR) { - /* handle the reset */ - imx_epit_reset(DEVICE(s)); - } else { - imx_epit_set_freq(s); - } - - if (s->freq && (s->cr & CR_EN) && !(oldcr & CR_EN)) { - if (s->cr & CR_ENMOD) { - if (s->cr & CR_RLD) { - ptimer_set_limit(s->timer_reload, s->lr, 1); - ptimer_set_limit(s->timer_cmp, s->lr, 1); - } else { - ptimer_set_limit(s->timer_reload, EPIT_TIMER_MAX, 1); - ptimer_set_limit(s->timer_cmp, EPIT_TIMER_MAX, 1); - } - } - - imx_epit_reload_compare_timer(s); - ptimer_run(s->timer_reload, 0); - if (s->cr & CR_OCIEN) { - ptimer_run(s->timer_cmp, 0); - } else { - ptimer_stop(s->timer_cmp); - } - } else if (!(s->cr & CR_EN)) { - /* stop both timers */ - ptimer_stop(s->timer_reload); - ptimer_stop(s->timer_cmp); - } else if (s->cr & CR_OCIEN) { - if (!(oldcr & CR_OCIEN)) { - imx_epit_reload_compare_timer(s); - ptimer_run(s->timer_cmp, 0); - } - } else { - ptimer_stop(s->timer_cmp); - } - break; - - case 1: /* SR - ACK*/ - /* writing 1 to OCIF clear the OCIF bit */ - if (value & 0x01) { - s->sr = 0; - imx_epit_update_int(s); - } - break; - - case 2: /* LR - set ticks */ - s->lr = value; - - if (s->cr & CR_RLD) { - /* Also set the limit if the LRD bit is set */ - /* If IOVW bit is set then set the timer value */ - ptimer_set_limit(s->timer_reload, s->lr, s->cr & CR_IOVW); - ptimer_set_limit(s->timer_cmp, s->lr, 0); - } else if (s->cr & CR_IOVW) { - /* If IOVW bit is set then set the timer value */ - ptimer_set_count(s->timer_reload, s->lr); - } - - imx_epit_reload_compare_timer(s); - break; - - case 3: /* CMP */ - s->cmp = value; - - imx_epit_reload_compare_timer(s); - - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" - HWADDR_PRIx "\n", TYPE_IMX_EPIT, __func__, offset); - - break; - } -} -static void imx_epit_cmp(void *opaque) -{ - IMXEPITState *s = IMX_EPIT(opaque); - - DPRINTF("sr was %d\n", s->sr); - - s->sr = 1; - imx_epit_update_int(s); -} - -static const MemoryRegionOps imx_epit_ops = { - .read = imx_epit_read, - .write = imx_epit_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_imx_timer_epit = { - .name = TYPE_IMX_EPIT, - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32(cr, IMXEPITState), - VMSTATE_UINT32(sr, IMXEPITState), - VMSTATE_UINT32(lr, IMXEPITState), - VMSTATE_UINT32(cmp, IMXEPITState), - VMSTATE_UINT32(cnt, IMXEPITState), - VMSTATE_UINT32(freq, IMXEPITState), - VMSTATE_PTIMER(timer_reload, IMXEPITState), - VMSTATE_PTIMER(timer_cmp, IMXEPITState), - VMSTATE_END_OF_LIST() - } -}; - -static void imx_epit_realize(DeviceState *dev, Error **errp) -{ - IMXEPITState *s = IMX_EPIT(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - QEMUBH *bh; - - DPRINTF("\n"); - - sysbus_init_irq(sbd, &s->irq); - memory_region_init_io(&s->iomem, OBJECT(s), &imx_epit_ops, s, TYPE_IMX_EPIT, - 0x00001000); - sysbus_init_mmio(sbd, &s->iomem); - - s->timer_reload = ptimer_init(NULL); - - bh = qemu_bh_new(imx_epit_cmp, s); - s->timer_cmp = ptimer_init(bh); -} - -static void imx_epit_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = imx_epit_realize; - dc->reset = imx_epit_reset; - dc->vmsd = &vmstate_imx_timer_epit; - dc->desc = "i.MX periodic timer"; -} - -static const TypeInfo imx_epit_info = { - .name = TYPE_IMX_EPIT, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IMXEPITState), - .class_init = imx_epit_class_init, -}; - -static void imx_epit_register_types(void) -{ - type_register_static(&imx_epit_info); -} - -type_init(imx_epit_register_types) diff --git a/qemu/hw/timer/imx_gpt.c b/qemu/hw/timer/imx_gpt.c deleted file mode 100644 index ab2e213a1..000000000 --- a/qemu/hw/timer/imx_gpt.c +++ /dev/null @@ -1,467 +0,0 @@ -/* - * IMX GPT Timer - * - * Copyright (c) 2008 OK Labs - * Copyright (c) 2011 NICTA Pty Ltd - * Originally written by Hans Jiang - * Updated by Peter Chubb - * Updated by Jean-Christophe Dubois - * - * This code is licensed under GPL version 2 or later. See - * the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "hw/timer/imx_gpt.h" -#include "hw/misc/imx_ccm.h" -#include "qemu/main-loop.h" - -#ifndef DEBUG_IMX_GPT -#define DEBUG_IMX_GPT 0 -#endif - -#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) -{ - switch (reg) { - case 0: - return "CR"; - case 1: - return "PR"; - case 2: - return "SR"; - case 3: - return "IR"; - case 4: - return "OCR1"; - case 5: - return "OCR2"; - case 6: - return "OCR3"; - case 7: - return "ICR1"; - case 8: - return "ICR2"; - case 9: - return "CNT"; - default: - return "[?]"; - } -} - -static const VMStateDescription vmstate_imx_timer_gpt = { - .name = TYPE_IMX_GPT, - .version_id = 3, - .minimum_version_id = 3, - .fields = (VMStateField[]) { - VMSTATE_UINT32(cr, IMXGPTState), - VMSTATE_UINT32(pr, IMXGPTState), - VMSTATE_UINT32(sr, IMXGPTState), - VMSTATE_UINT32(ir, IMXGPTState), - VMSTATE_UINT32(ocr1, IMXGPTState), - VMSTATE_UINT32(ocr2, IMXGPTState), - VMSTATE_UINT32(ocr3, IMXGPTState), - VMSTATE_UINT32(icr1, IMXGPTState), - VMSTATE_UINT32(icr2, IMXGPTState), - VMSTATE_UINT32(cnt, IMXGPTState), - VMSTATE_UINT32(next_timeout, IMXGPTState), - VMSTATE_UINT32(next_int, IMXGPTState), - VMSTATE_UINT32(freq, IMXGPTState), - VMSTATE_PTIMER(timer, IMXGPTState), - VMSTATE_END_OF_LIST() - } -}; - -static const IMXClk imx_gpt_clocks[] = { - 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); - - 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 (s->freq) { - ptimer_set_freq(s->timer, s->freq); - } -} - -static void imx_gpt_update_int(IMXGPTState *s) -{ - if ((s->sr & s->ir) && (s->cr & GPT_CR_EN)) { - qemu_irq_raise(s->irq); - } else { - qemu_irq_lower(s->irq); - } -} - -static uint32_t imx_gpt_update_count(IMXGPTState *s) -{ - s->cnt = s->next_timeout - (uint32_t)ptimer_get_count(s->timer); - - return s->cnt; -} - -static inline uint32_t imx_gpt_find_limit(uint32_t count, uint32_t reg, - uint32_t timeout) -{ - if ((count < reg) && (timeout > reg)) { - timeout = reg; - } - - return timeout; -} - -static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event) -{ - uint32_t timeout = GPT_TIMER_MAX; - uint32_t count; - long long limit; - - if (!(s->cr & GPT_CR_EN)) { - /* if not enabled just return */ - return; - } - - /* update the count */ - count = imx_gpt_update_count(s); - - 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 = 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; - } - } - - /* now, find the next timeout related to count */ - - if (s->ir & GPT_IR_OF1IE) { - timeout = imx_gpt_find_limit(count, s->ocr1, timeout); - } - if (s->ir & GPT_IR_OF2IE) { - timeout = imx_gpt_find_limit(count, s->ocr2, timeout); - } - if (s->ir & GPT_IR_OF3IE) { - timeout = imx_gpt_find_limit(count, s->ocr3, timeout); - } - - /* find the next set of interrupts to raise for next timer event */ - - s->next_int = 0; - if ((s->ir & GPT_IR_OF1IE) && (timeout == s->ocr1)) { - s->next_int |= GPT_SR_OF1; - } - if ((s->ir & GPT_IR_OF2IE) && (timeout == s->ocr2)) { - s->next_int |= GPT_SR_OF2; - } - if ((s->ir & GPT_IR_OF3IE) && (timeout == s->ocr3)) { - s->next_int |= GPT_SR_OF3; - } - if ((s->ir & GPT_IR_ROVIE) && (timeout == GPT_TIMER_MAX)) { - s->next_int |= GPT_SR_ROV; - } - - /* the new range to count down from */ - limit = timeout - imx_gpt_update_count(s); - - if (limit < 0) { - /* - * if we reach here, then QEMU is running too slow and we pass the - * timeout limit while computing it. Let's deliver the interrupt - * and compute a new limit. - */ - s->sr |= s->next_int; - - imx_gpt_compute_next_timeout(s, event); - - imx_gpt_update_int(s); - } else { - /* New timeout value */ - s->next_timeout = timeout; - - /* reset the limit to the computed range */ - ptimer_set_limit(s->timer, limit, 1); - } -} - -static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size) -{ - IMXGPTState *s = IMX_GPT(opaque); - uint32_t reg_value = 0; - - switch (offset >> 2) { - case 0: /* Control Register */ - reg_value = s->cr; - break; - - case 1: /* prescaler */ - reg_value = s->pr; - break; - - case 2: /* Status Register */ - reg_value = s->sr; - break; - - case 3: /* Interrupt Register */ - reg_value = s->ir; - break; - - case 4: /* Output Compare Register 1 */ - reg_value = s->ocr1; - break; - - case 5: /* Output Compare Register 2 */ - reg_value = s->ocr2; - break; - - case 6: /* Output Compare Register 3 */ - reg_value = s->ocr3; - break; - - case 7: /* input Capture Register 1 */ - 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, "[%s]%s: icr2 feature is not implemented\n", - TYPE_IMX_GPT, __func__); - reg_value = s->icr2; - break; - - case 9: /* cnt */ - imx_gpt_update_count(s); - reg_value = s->cnt; - break; - - default: - 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(offset >> 2), reg_value); - - return reg_value; -} - -static void imx_gpt_reset(DeviceState *dev) -{ - IMXGPTState *s = IMX_GPT(dev); - - /* stop timer */ - ptimer_stop(s->timer); - - /* - * Soft reset doesn't touch some bits; hard reset clears them - */ - s->cr &= ~(GPT_CR_EN|GPT_CR_ENMOD|GPT_CR_STOPEN|GPT_CR_DOZEN| - GPT_CR_WAITEN|GPT_CR_DBGEN); - s->sr = 0; - s->pr = 0; - s->ir = 0; - s->cnt = 0; - s->ocr1 = GPT_TIMER_MAX; - s->ocr2 = GPT_TIMER_MAX; - s->ocr3 = GPT_TIMER_MAX; - s->icr1 = 0; - s->icr2 = 0; - - s->next_timeout = GPT_TIMER_MAX; - s->next_int = 0; - - /* compute new freq */ - imx_gpt_set_freq(s); - - /* reset the limit to GPT_TIMER_MAX */ - ptimer_set_limit(s->timer, GPT_TIMER_MAX, 1); - - /* if the timer is still enabled, restart it */ - if (s->freq && (s->cr & GPT_CR_EN)) { - ptimer_run(s->timer, 1); - } -} - -static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value, - unsigned size) -{ - IMXGPTState *s = IMX_GPT(opaque); - uint32_t oldreg; - - DPRINTF("(%s, value = 0x%08x)\n", imx_gpt_reg_name(offset >> 2), - (uint32_t)value); - - switch (offset >> 2) { - case 0: - oldreg = s->cr; - s->cr = value & ~0x7c14; - if (s->cr & GPT_CR_SWR) { /* force reset */ - /* handle the reset */ - imx_gpt_reset(DEVICE(s)); - } else { - /* set our freq, as the source might have changed */ - imx_gpt_set_freq(s); - - if ((oldreg ^ s->cr) & GPT_CR_EN) { - if (s->cr & GPT_CR_EN) { - if (s->cr & GPT_CR_ENMOD) { - s->next_timeout = GPT_TIMER_MAX; - ptimer_set_count(s->timer, GPT_TIMER_MAX); - imx_gpt_compute_next_timeout(s, false); - } - ptimer_run(s->timer, 1); - } else { - /* stop timer */ - ptimer_stop(s->timer); - } - } - } - break; - - case 1: /* Prescaler */ - s->pr = value & 0xfff; - imx_gpt_set_freq(s); - break; - - case 2: /* SR */ - s->sr &= ~(value & 0x3f); - imx_gpt_update_int(s); - break; - - case 3: /* IR -- interrupt register */ - s->ir = value & 0x3f; - imx_gpt_update_int(s); - - imx_gpt_compute_next_timeout(s, false); - - break; - - case 4: /* OCR1 -- output compare register */ - s->ocr1 = value; - - /* In non-freerun mode, reset count when this register is written */ - if (!(s->cr & GPT_CR_FRR)) { - s->next_timeout = GPT_TIMER_MAX; - ptimer_set_limit(s->timer, GPT_TIMER_MAX, 1); - } - - /* compute the new timeout */ - imx_gpt_compute_next_timeout(s, false); - - break; - - case 5: /* OCR2 -- output compare register */ - s->ocr2 = value; - - /* compute the new timeout */ - imx_gpt_compute_next_timeout(s, false); - - break; - - case 6: /* OCR3 -- output compare register */ - s->ocr3 = value; - - /* compute the new timeout */ - imx_gpt_compute_next_timeout(s, false); - - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" - HWADDR_PRIx "\n", TYPE_IMX_GPT, __func__, offset); - break; - } -} - -static void imx_gpt_timeout(void *opaque) -{ - IMXGPTState *s = IMX_GPT(opaque); - - DPRINTF("\n"); - - s->sr |= s->next_int; - s->next_int = 0; - - imx_gpt_compute_next_timeout(s, true); - - imx_gpt_update_int(s); - - if (s->freq && (s->cr & GPT_CR_EN)) { - ptimer_run(s->timer, 1); - } -} - -static const MemoryRegionOps imx_gpt_ops = { - .read = imx_gpt_read, - .write = imx_gpt_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - - -static void imx_gpt_realize(DeviceState *dev, Error **errp) -{ - IMXGPTState *s = IMX_GPT(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - QEMUBH *bh; - - sysbus_init_irq(sbd, &s->irq); - memory_region_init_io(&s->iomem, OBJECT(s), &imx_gpt_ops, s, TYPE_IMX_GPT, - 0x00001000); - sysbus_init_mmio(sbd, &s->iomem); - - bh = qemu_bh_new(imx_gpt_timeout, s); - s->timer = ptimer_init(bh); -} - -static void imx_gpt_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = imx_gpt_realize; - dc->reset = imx_gpt_reset; - dc->vmsd = &vmstate_imx_timer_gpt; - dc->desc = "i.MX general timer"; -} - -static const TypeInfo imx_gpt_info = { - .name = TYPE_IMX_GPT, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IMXGPTState), - .class_init = imx_gpt_class_init, -}; - -static void imx_gpt_register_types(void) -{ - type_register_static(&imx_gpt_info); -} - -type_init(imx_gpt_register_types) diff --git a/qemu/hw/timer/lm32_timer.c b/qemu/hw/timer/lm32_timer.c deleted file mode 100644 index 3198355aa..000000000 --- a/qemu/hw/timer/lm32_timer.c +++ /dev/null @@ -1,236 +0,0 @@ -/* - * QEMU model of the LatticeMico32 timer block. - * - * Copyright (c) 2010 Michael Walle - * - * 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 . - * - * - * Specification available at: - * http://www.latticesemi.com/documents/mico32timer.pdf - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "qemu/timer.h" -#include "hw/ptimer.h" -#include "qemu/error-report.h" -#include "qemu/main-loop.h" - -#define DEFAULT_FREQUENCY (50*1000000) - -enum { - R_SR = 0, - R_CR, - R_PERIOD, - R_SNAPSHOT, - R_MAX -}; - -enum { - SR_TO = (1 << 0), - SR_RUN = (1 << 1), -}; - -enum { - CR_ITO = (1 << 0), - CR_CONT = (1 << 1), - CR_START = (1 << 2), - CR_STOP = (1 << 3), -}; - -#define TYPE_LM32_TIMER "lm32-timer" -#define LM32_TIMER(obj) OBJECT_CHECK(LM32TimerState, (obj), TYPE_LM32_TIMER) - -struct LM32TimerState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - - QEMUBH *bh; - ptimer_state *ptimer; - - qemu_irq irq; - uint32_t freq_hz; - - uint32_t regs[R_MAX]; -}; -typedef struct LM32TimerState LM32TimerState; - -static void timer_update_irq(LM32TimerState *s) -{ - int state = (s->regs[R_SR] & SR_TO) && (s->regs[R_CR] & CR_ITO); - - trace_lm32_timer_irq_state(state); - qemu_set_irq(s->irq, state); -} - -static uint64_t timer_read(void *opaque, hwaddr addr, unsigned size) -{ - LM32TimerState *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_SR: - case R_CR: - case R_PERIOD: - r = s->regs[addr]; - break; - case R_SNAPSHOT: - r = (uint32_t)ptimer_get_count(s->ptimer); - break; - default: - error_report("lm32_timer: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_lm32_timer_memory_read(addr << 2, r); - return r; -} - -static void timer_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - LM32TimerState *s = opaque; - - trace_lm32_timer_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_SR: - s->regs[R_SR] &= ~SR_TO; - break; - case R_CR: - s->regs[R_CR] = value; - if (s->regs[R_CR] & CR_START) { - ptimer_run(s->ptimer, 1); - } - if (s->regs[R_CR] & CR_STOP) { - ptimer_stop(s->ptimer); - } - break; - case R_PERIOD: - s->regs[R_PERIOD] = value; - ptimer_set_count(s->ptimer, value); - break; - case R_SNAPSHOT: - error_report("lm32_timer: write access to read only register 0x" - TARGET_FMT_plx, addr << 2); - break; - default: - error_report("lm32_timer: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - timer_update_irq(s); -} - -static const MemoryRegionOps timer_ops = { - .read = timer_read, - .write = timer_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void timer_hit(void *opaque) -{ - LM32TimerState *s = opaque; - - trace_lm32_timer_hit(); - - s->regs[R_SR] |= SR_TO; - - if (s->regs[R_CR] & CR_CONT) { - ptimer_set_count(s->ptimer, s->regs[R_PERIOD]); - ptimer_run(s->ptimer, 1); - } - timer_update_irq(s); -} - -static void timer_reset(DeviceState *d) -{ - LM32TimerState *s = LM32_TIMER(d); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } - ptimer_stop(s->ptimer); -} - -static int lm32_timer_init(SysBusDevice *dev) -{ - LM32TimerState *s = LM32_TIMER(dev); - - sysbus_init_irq(dev, &s->irq); - - s->bh = qemu_bh_new(timer_hit, s); - s->ptimer = ptimer_init(s->bh); - ptimer_set_freq(s->ptimer, s->freq_hz); - - memory_region_init_io(&s->iomem, OBJECT(s), &timer_ops, s, - "timer", R_MAX * 4); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static const VMStateDescription vmstate_lm32_timer = { - .name = "lm32-timer", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PTIMER(ptimer, LM32TimerState), - VMSTATE_UINT32(freq_hz, LM32TimerState), - VMSTATE_UINT32_ARRAY(regs, LM32TimerState, R_MAX), - VMSTATE_END_OF_LIST() - } -}; - -static Property lm32_timer_properties[] = { - DEFINE_PROP_UINT32("frequency", LM32TimerState, freq_hz, DEFAULT_FREQUENCY), - DEFINE_PROP_END_OF_LIST(), -}; - -static void lm32_timer_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = lm32_timer_init; - dc->reset = timer_reset; - dc->vmsd = &vmstate_lm32_timer; - dc->props = lm32_timer_properties; -} - -static const TypeInfo lm32_timer_info = { - .name = TYPE_LM32_TIMER, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(LM32TimerState), - .class_init = lm32_timer_class_init, -}; - -static void lm32_timer_register_types(void) -{ - type_register_static(&lm32_timer_info); -} - -type_init(lm32_timer_register_types) diff --git a/qemu/hw/timer/m48t59.c b/qemu/hw/timer/m48t59.c deleted file mode 100644 index e46ca8839..000000000 --- a/qemu/hw/timer/m48t59.c +++ /dev/null @@ -1,947 +0,0 @@ -/* - * QEMU M48T59 and M48T08 NVRAM emulation for PPC PREP and Sparc platforms - * - * Copyright (c) 2003-2005, 2007 Jocelyn Mayer - * Copyright (c) 2013 Hervé Poussineau - * - * 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/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 - -#if defined(DEBUG_NVRAM) -#define NVRAM_PRINTF(fmt, ...) do { printf(fmt , ## __VA_ARGS__); } while (0) -#else -#define NVRAM_PRINTF(fmt, ...) do { } while (0) -#endif - -#define TYPE_M48TXX_SYS_BUS "sysbus-m48txx" -#define M48TXX_SYS_BUS_GET_CLASS(obj) \ - OBJECT_GET_CLASS(M48txxSysBusDeviceClass, (obj), TYPE_M48TXX_SYS_BUS) -#define M48TXX_SYS_BUS_CLASS(klass) \ - OBJECT_CLASS_CHECK(M48txxSysBusDeviceClass, (klass), TYPE_M48TXX_SYS_BUS) -#define M48TXX_SYS_BUS(obj) \ - OBJECT_CHECK(M48txxSysBusState, (obj), TYPE_M48TXX_SYS_BUS) - -#define TYPE_M48TXX_ISA "isa-m48txx" -#define M48TXX_ISA_GET_CLASS(obj) \ - OBJECT_GET_CLASS(M48txxISADeviceClass, (obj), TYPE_M48TXX_ISA) -#define M48TXX_ISA_CLASS(klass) \ - OBJECT_CLASS_CHECK(M48txxISADeviceClass, (klass), TYPE_M48TXX_ISA) -#define M48TXX_ISA(obj) \ - OBJECT_CHECK(M48txxISAState, (obj), TYPE_M48TXX_ISA) - -/* - * The M48T02, M48T08 and M48T59 chips are very similar. The newer '59 has - * alarm and a watchdog timer and related control registers. In the - * PPC platform there is also a nvram lock function. - */ - -typedef struct M48txxInfo { - const char *isa_name; - const char *sysbus_name; - uint32_t model; /* 2 = m48t02, 8 = m48t08, 59 = m48t59 */ - uint32_t size; -} M48txxInfo; - -/* - * Chipset docs: - * http://www.st.com/stonline/products/literature/ds/2410/m48t02.pdf - * http://www.st.com/stonline/products/literature/ds/2411/m48t08.pdf - * http://www.st.com/stonline/products/literature/od/7001/m48t59y.pdf - */ - -typedef struct M48t59State { - /* Hardware parameters */ - qemu_irq IRQ; - MemoryRegion iomem; - uint32_t size; - int32_t base_year; - /* RTC management */ - time_t time_offset; - time_t stop_time; - /* Alarm & watchdog */ - struct tm alarm; - QEMUTimer *alrm_timer; - QEMUTimer *wd_timer; - /* NVRAM storage */ - uint8_t *buffer; - /* Model parameters */ - uint32_t model; /* 2 = m48t02, 8 = m48t08, 59 = m48t59 */ - /* NVRAM storage */ - uint16_t addr; - uint8_t lock; -} M48t59State; - -typedef struct M48txxISAState { - ISADevice parent_obj; - M48t59State state; - uint32_t io_base; - MemoryRegion io; -} M48txxISAState; - -typedef struct M48txxISADeviceClass { - ISADeviceClass parent_class; - M48txxInfo info; -} M48txxISADeviceClass; - -typedef struct M48txxSysBusState { - SysBusDevice parent_obj; - M48t59State state; - MemoryRegion io; -} M48txxSysBusState; - -typedef struct M48txxSysBusDeviceClass { - SysBusDeviceClass parent_class; - M48txxInfo info; -} M48txxSysBusDeviceClass; - -static M48txxInfo m48txx_info[] = { - { - .sysbus_name = "sysbus-m48t02", - .model = 2, - .size = 0x800, - },{ - .sysbus_name = "sysbus-m48t08", - .model = 8, - .size = 0x2000, - },{ - .sysbus_name = "sysbus-m48t59", - .model = 59, - .size = 0x2000, - },{ - .isa_name = "isa-m48t59", - .model = 59, - .size = 0x2000, - } -}; - - -/* Fake timer functions */ - -/* Alarm management */ -static void alarm_cb (void *opaque) -{ - struct tm tm; - uint64_t next_time; - M48t59State *NVRAM = opaque; - - qemu_set_irq(NVRAM->IRQ, 1); - if ((NVRAM->buffer[0x1FF5] & 0x80) == 0 && - (NVRAM->buffer[0x1FF4] & 0x80) == 0 && - (NVRAM->buffer[0x1FF3] & 0x80) == 0 && - (NVRAM->buffer[0x1FF2] & 0x80) == 0) { - /* Repeat once a month */ - qemu_get_timedate(&tm, NVRAM->time_offset); - tm.tm_mon++; - if (tm.tm_mon == 13) { - tm.tm_mon = 1; - tm.tm_year++; - } - next_time = qemu_timedate_diff(&tm) - NVRAM->time_offset; - } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && - (NVRAM->buffer[0x1FF4] & 0x80) == 0 && - (NVRAM->buffer[0x1FF3] & 0x80) == 0 && - (NVRAM->buffer[0x1FF2] & 0x80) == 0) { - /* Repeat once a day */ - next_time = 24 * 60 * 60; - } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && - (NVRAM->buffer[0x1FF4] & 0x80) != 0 && - (NVRAM->buffer[0x1FF3] & 0x80) == 0 && - (NVRAM->buffer[0x1FF2] & 0x80) == 0) { - /* Repeat once an hour */ - next_time = 60 * 60; - } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 && - (NVRAM->buffer[0x1FF4] & 0x80) != 0 && - (NVRAM->buffer[0x1FF3] & 0x80) != 0 && - (NVRAM->buffer[0x1FF2] & 0x80) == 0) { - /* Repeat once a minute */ - next_time = 60; - } else { - /* Repeat once a second */ - next_time = 1; - } - timer_mod(NVRAM->alrm_timer, qemu_clock_get_ns(rtc_clock) + - next_time * 1000); - qemu_set_irq(NVRAM->IRQ, 0); -} - -static void set_alarm(M48t59State *NVRAM) -{ - int diff; - if (NVRAM->alrm_timer != NULL) { - timer_del(NVRAM->alrm_timer); - diff = qemu_timedate_diff(&NVRAM->alarm) - NVRAM->time_offset; - if (diff > 0) - timer_mod(NVRAM->alrm_timer, diff * 1000); - } -} - -/* RTC management helpers */ -static inline void get_time(M48t59State *NVRAM, struct tm *tm) -{ - qemu_get_timedate(tm, NVRAM->time_offset); -} - -static void set_time(M48t59State *NVRAM, struct tm *tm) -{ - NVRAM->time_offset = qemu_timedate_diff(tm); - set_alarm(NVRAM); -} - -/* Watchdog management */ -static void watchdog_cb (void *opaque) -{ - M48t59State *NVRAM = opaque; - - NVRAM->buffer[0x1FF0] |= 0x80; - if (NVRAM->buffer[0x1FF7] & 0x80) { - NVRAM->buffer[0x1FF7] = 0x00; - NVRAM->buffer[0x1FFC] &= ~0x40; - /* May it be a hw CPU Reset instead ? */ - qemu_system_reset_request(); - } else { - qemu_set_irq(NVRAM->IRQ, 1); - qemu_set_irq(NVRAM->IRQ, 0); - } -} - -static void set_up_watchdog(M48t59State *NVRAM, uint8_t value) -{ - uint64_t interval; /* in 1/16 seconds */ - - NVRAM->buffer[0x1FF0] &= ~0x80; - if (NVRAM->wd_timer != NULL) { - timer_del(NVRAM->wd_timer); - if (value != 0) { - interval = (1 << (2 * (value & 0x03))) * ((value >> 2) & 0x1F); - timer_mod(NVRAM->wd_timer, ((uint64_t)time(NULL) * 1000) + - ((interval * 1000) >> 4)); - } - } -} - -/* Direct access to NVRAM */ -static void m48t59_write(M48t59State *NVRAM, uint32_t addr, uint32_t val) -{ - struct tm tm; - int tmp; - - if (addr > 0x1FF8 && addr < 0x2000) - NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val); - - /* check for NVRAM access */ - if ((NVRAM->model == 2 && addr < 0x7f8) || - (NVRAM->model == 8 && addr < 0x1ff8) || - (NVRAM->model == 59 && addr < 0x1ff0)) { - goto do_write; - } - - /* TOD access */ - switch (addr) { - case 0x1FF0: - /* flags register : read-only */ - break; - case 0x1FF1: - /* unused */ - break; - case 0x1FF2: - /* alarm seconds */ - tmp = from_bcd(val & 0x7F); - if (tmp >= 0 && tmp <= 59) { - NVRAM->alarm.tm_sec = tmp; - NVRAM->buffer[0x1FF2] = val; - set_alarm(NVRAM); - } - break; - case 0x1FF3: - /* alarm minutes */ - tmp = from_bcd(val & 0x7F); - if (tmp >= 0 && tmp <= 59) { - NVRAM->alarm.tm_min = tmp; - NVRAM->buffer[0x1FF3] = val; - set_alarm(NVRAM); - } - break; - case 0x1FF4: - /* alarm hours */ - tmp = from_bcd(val & 0x3F); - if (tmp >= 0 && tmp <= 23) { - NVRAM->alarm.tm_hour = tmp; - NVRAM->buffer[0x1FF4] = val; - set_alarm(NVRAM); - } - break; - case 0x1FF5: - /* alarm date */ - tmp = from_bcd(val & 0x3F); - if (tmp != 0) { - NVRAM->alarm.tm_mday = tmp; - NVRAM->buffer[0x1FF5] = val; - set_alarm(NVRAM); - } - break; - case 0x1FF6: - /* interrupts */ - NVRAM->buffer[0x1FF6] = val; - break; - case 0x1FF7: - /* watchdog */ - NVRAM->buffer[0x1FF7] = val; - set_up_watchdog(NVRAM, val); - break; - case 0x1FF8: - case 0x07F8: - /* control */ - NVRAM->buffer[addr] = (val & ~0xA0) | 0x90; - break; - case 0x1FF9: - case 0x07F9: - /* seconds (BCD) */ - tmp = from_bcd(val & 0x7F); - if (tmp >= 0 && tmp <= 59) { - get_time(NVRAM, &tm); - tm.tm_sec = tmp; - set_time(NVRAM, &tm); - } - if ((val & 0x80) ^ (NVRAM->buffer[addr] & 0x80)) { - if (val & 0x80) { - NVRAM->stop_time = time(NULL); - } else { - NVRAM->time_offset += NVRAM->stop_time - time(NULL); - NVRAM->stop_time = 0; - } - } - NVRAM->buffer[addr] = val & 0x80; - break; - case 0x1FFA: - case 0x07FA: - /* minutes (BCD) */ - tmp = from_bcd(val & 0x7F); - if (tmp >= 0 && tmp <= 59) { - get_time(NVRAM, &tm); - tm.tm_min = tmp; - set_time(NVRAM, &tm); - } - break; - case 0x1FFB: - case 0x07FB: - /* hours (BCD) */ - tmp = from_bcd(val & 0x3F); - if (tmp >= 0 && tmp <= 23) { - get_time(NVRAM, &tm); - tm.tm_hour = tmp; - set_time(NVRAM, &tm); - } - break; - case 0x1FFC: - case 0x07FC: - /* day of the week / century */ - tmp = from_bcd(val & 0x07); - get_time(NVRAM, &tm); - tm.tm_wday = tmp; - set_time(NVRAM, &tm); - NVRAM->buffer[addr] = val & 0x40; - break; - case 0x1FFD: - case 0x07FD: - /* date (BCD) */ - tmp = from_bcd(val & 0x3F); - if (tmp != 0) { - get_time(NVRAM, &tm); - tm.tm_mday = tmp; - set_time(NVRAM, &tm); - } - break; - case 0x1FFE: - case 0x07FE: - /* month */ - tmp = from_bcd(val & 0x1F); - if (tmp >= 1 && tmp <= 12) { - get_time(NVRAM, &tm); - tm.tm_mon = tmp - 1; - set_time(NVRAM, &tm); - } - break; - case 0x1FFF: - case 0x07FF: - /* year */ - tmp = from_bcd(val); - if (tmp >= 0 && tmp <= 99) { - get_time(NVRAM, &tm); - tm.tm_year = from_bcd(val) + NVRAM->base_year - 1900; - set_time(NVRAM, &tm); - } - break; - default: - /* Check lock registers state */ - if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1)) - break; - if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2)) - break; - do_write: - if (addr < NVRAM->size) { - NVRAM->buffer[addr] = val & 0xFF; - } - break; - } -} - -static uint32_t m48t59_read(M48t59State *NVRAM, uint32_t addr) -{ - struct tm tm; - uint32_t retval = 0xFF; - - /* check for NVRAM access */ - if ((NVRAM->model == 2 && addr < 0x078f) || - (NVRAM->model == 8 && addr < 0x1ff8) || - (NVRAM->model == 59 && addr < 0x1ff0)) { - goto do_read; - } - - /* TOD access */ - switch (addr) { - case 0x1FF0: - /* flags register */ - goto do_read; - case 0x1FF1: - /* unused */ - retval = 0; - break; - case 0x1FF2: - /* alarm seconds */ - goto do_read; - case 0x1FF3: - /* alarm minutes */ - goto do_read; - case 0x1FF4: - /* alarm hours */ - goto do_read; - case 0x1FF5: - /* alarm date */ - goto do_read; - case 0x1FF6: - /* interrupts */ - goto do_read; - case 0x1FF7: - /* A read resets the watchdog */ - set_up_watchdog(NVRAM, NVRAM->buffer[0x1FF7]); - goto do_read; - case 0x1FF8: - case 0x07F8: - /* control */ - goto do_read; - case 0x1FF9: - case 0x07F9: - /* seconds (BCD) */ - get_time(NVRAM, &tm); - retval = (NVRAM->buffer[addr] & 0x80) | to_bcd(tm.tm_sec); - break; - case 0x1FFA: - case 0x07FA: - /* minutes (BCD) */ - get_time(NVRAM, &tm); - retval = to_bcd(tm.tm_min); - break; - case 0x1FFB: - case 0x07FB: - /* hours (BCD) */ - get_time(NVRAM, &tm); - retval = to_bcd(tm.tm_hour); - break; - case 0x1FFC: - case 0x07FC: - /* day of the week / century */ - get_time(NVRAM, &tm); - retval = NVRAM->buffer[addr] | tm.tm_wday; - break; - case 0x1FFD: - case 0x07FD: - /* date */ - get_time(NVRAM, &tm); - retval = to_bcd(tm.tm_mday); - break; - case 0x1FFE: - case 0x07FE: - /* month */ - get_time(NVRAM, &tm); - retval = to_bcd(tm.tm_mon + 1); - break; - case 0x1FFF: - case 0x07FF: - /* year */ - get_time(NVRAM, &tm); - retval = to_bcd((tm.tm_year + 1900 - NVRAM->base_year) % 100); - break; - default: - /* Check lock registers state */ - if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1)) - break; - if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2)) - break; - do_read: - if (addr < NVRAM->size) { - retval = NVRAM->buffer[addr]; - } - break; - } - if (addr > 0x1FF9 && addr < 0x2000) - NVRAM_PRINTF("%s: 0x%08x <= 0x%08x\n", __func__, addr, retval); - - return retval; -} - -static void m48t59_toggle_lock(M48t59State *NVRAM, int lock) -{ - NVRAM->lock ^= 1 << lock; -} - -/* IO access to NVRAM */ -static void NVRAM_writeb(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - M48t59State *NVRAM = opaque; - - NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val); - switch (addr) { - case 0: - NVRAM->addr &= ~0x00FF; - NVRAM->addr |= val; - break; - case 1: - NVRAM->addr &= ~0xFF00; - NVRAM->addr |= val << 8; - break; - case 3: - m48t59_write(NVRAM, NVRAM->addr, val); - NVRAM->addr = 0x0000; - break; - default: - break; - } -} - -static uint64_t NVRAM_readb(void *opaque, hwaddr addr, unsigned size) -{ - M48t59State *NVRAM = opaque; - uint32_t retval; - - switch (addr) { - case 3: - retval = m48t59_read(NVRAM, NVRAM->addr); - break; - default: - retval = -1; - break; - } - NVRAM_PRINTF("%s: 0x%08x <= 0x%08x\n", __func__, addr, retval); - - return retval; -} - -static void nvram_writeb (void *opaque, hwaddr addr, uint32_t value) -{ - M48t59State *NVRAM = opaque; - - m48t59_write(NVRAM, addr, value & 0xff); -} - -static void nvram_writew (void *opaque, hwaddr addr, uint32_t value) -{ - M48t59State *NVRAM = opaque; - - m48t59_write(NVRAM, addr, (value >> 8) & 0xff); - m48t59_write(NVRAM, addr + 1, value & 0xff); -} - -static void nvram_writel (void *opaque, hwaddr addr, uint32_t value) -{ - M48t59State *NVRAM = opaque; - - m48t59_write(NVRAM, addr, (value >> 24) & 0xff); - m48t59_write(NVRAM, addr + 1, (value >> 16) & 0xff); - m48t59_write(NVRAM, addr + 2, (value >> 8) & 0xff); - m48t59_write(NVRAM, addr + 3, value & 0xff); -} - -static uint32_t nvram_readb (void *opaque, hwaddr addr) -{ - M48t59State *NVRAM = opaque; - - return m48t59_read(NVRAM, addr); -} - -static uint32_t nvram_readw (void *opaque, hwaddr addr) -{ - M48t59State *NVRAM = opaque; - uint32_t retval; - - retval = m48t59_read(NVRAM, addr) << 8; - retval |= m48t59_read(NVRAM, addr + 1); - return retval; -} - -static uint32_t nvram_readl (void *opaque, hwaddr addr) -{ - M48t59State *NVRAM = opaque; - uint32_t retval; - - retval = m48t59_read(NVRAM, addr) << 24; - retval |= m48t59_read(NVRAM, addr + 1) << 16; - retval |= m48t59_read(NVRAM, addr + 2) << 8; - retval |= m48t59_read(NVRAM, addr + 3); - return retval; -} - -static const MemoryRegionOps nvram_ops = { - .old_mmio = { - .read = { nvram_readb, nvram_readw, nvram_readl, }, - .write = { nvram_writeb, nvram_writew, nvram_writel, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_m48t59 = { - .name = "m48t59", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(lock, M48t59State), - VMSTATE_UINT16(addr, M48t59State), - VMSTATE_VBUFFER_UINT32(buffer, M48t59State, 0, NULL, 0, size), - VMSTATE_END_OF_LIST() - } -}; - -static void m48t59_reset_common(M48t59State *NVRAM) -{ - NVRAM->addr = 0; - NVRAM->lock = 0; - if (NVRAM->alrm_timer != NULL) - timer_del(NVRAM->alrm_timer); - - if (NVRAM->wd_timer != NULL) - timer_del(NVRAM->wd_timer); -} - -static void m48t59_reset_isa(DeviceState *d) -{ - M48txxISAState *isa = M48TXX_ISA(d); - M48t59State *NVRAM = &isa->state; - - m48t59_reset_common(NVRAM); -} - -static void m48t59_reset_sysbus(DeviceState *d) -{ - M48txxSysBusState *sys = M48TXX_SYS_BUS(d); - M48t59State *NVRAM = &sys->state; - - m48t59_reset_common(NVRAM); -} - -static const MemoryRegionOps m48t59_io_ops = { - .read = NVRAM_readb, - .write = NVRAM_writeb, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -/* Initialisation routine */ -Nvram *m48t59_init(qemu_irq IRQ, hwaddr mem_base, - uint32_t io_base, uint16_t size, int base_year, - int model) -{ - DeviceState *dev; - SysBusDevice *s; - int i; - - for (i = 0; i < ARRAY_SIZE(m48txx_info); i++) { - if (!m48txx_info[i].sysbus_name || - m48txx_info[i].size != size || - m48txx_info[i].model != model) { - continue; - } - - dev = qdev_create(NULL, m48txx_info[i].sysbus_name); - qdev_prop_set_int32(dev, "base-year", base_year); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, IRQ); - if (io_base != 0) { - memory_region_add_subregion(get_system_io(), io_base, - sysbus_mmio_get_region(s, 1)); - } - if (mem_base != 0) { - sysbus_mmio_map(s, 0, mem_base); - } - - return NVRAM(s); - } - - assert(false); - return NULL; -} - -Nvram *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size, - int base_year, int model) -{ - DeviceState *dev; - int i; - - for (i = 0; i < ARRAY_SIZE(m48txx_info); i++) { - if (!m48txx_info[i].isa_name || - m48txx_info[i].size != size || - m48txx_info[i].model != model) { - continue; - } - - dev = DEVICE(isa_create(bus, m48txx_info[i].isa_name)); - qdev_prop_set_uint32(dev, "iobase", io_base); - qdev_prop_set_int32(dev, "base-year", base_year); - qdev_init_nofail(dev); - return NVRAM(dev); - } - - assert(false); - return NULL; -} - -static void m48t59_realize_common(M48t59State *s, Error **errp) -{ - s->buffer = g_malloc0(s->size); - if (s->model == 59) { - s->alrm_timer = timer_new_ns(rtc_clock, &alarm_cb, s); - s->wd_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &watchdog_cb, s); - } - qemu_get_timedate(&s->alarm, 0); - - vmstate_register(NULL, -1, &vmstate_m48t59, s); -} - -static void m48t59_isa_realize(DeviceState *dev, Error **errp) -{ - M48txxISADeviceClass *u = M48TXX_ISA_GET_CLASS(dev); - ISADevice *isadev = ISA_DEVICE(dev); - M48txxISAState *d = M48TXX_ISA(dev); - M48t59State *s = &d->state; - - s->model = u->info.model; - s->size = u->info.size; - isa_init_irq(isadev, &s->IRQ, 8); - m48t59_realize_common(s, errp); - memory_region_init_io(&d->io, OBJECT(dev), &m48t59_io_ops, s, "m48t59", 4); - if (d->io_base != 0) { - isa_register_ioport(isadev, &d->io, d->io_base); - } -} - -static int m48t59_init1(SysBusDevice *dev) -{ - M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_GET_CLASS(dev); - M48txxSysBusState *d = M48TXX_SYS_BUS(dev); - Object *o = OBJECT(dev); - M48t59State *s = &d->state; - Error *err = NULL; - - s->model = u->info.model; - s->size = u->info.size; - sysbus_init_irq(dev, &s->IRQ); - - memory_region_init_io(&s->iomem, o, &nvram_ops, s, "m48t59.nvram", - s->size); - memory_region_init_io(&d->io, o, &m48t59_io_ops, s, "m48t59", 4); - sysbus_init_mmio(dev, &s->iomem); - sysbus_init_mmio(dev, &d->io); - m48t59_realize_common(s, &err); - if (err != NULL) { - error_free(err); - return -1; - } - - return 0; -} - -static uint32_t m48txx_isa_read(Nvram *obj, uint32_t addr) -{ - M48txxISAState *d = M48TXX_ISA(obj); - return m48t59_read(&d->state, addr); -} - -static void m48txx_isa_write(Nvram *obj, uint32_t addr, uint32_t val) -{ - M48txxISAState *d = M48TXX_ISA(obj); - m48t59_write(&d->state, addr, val); -} - -static void m48txx_isa_toggle_lock(Nvram *obj, int lock) -{ - M48txxISAState *d = M48TXX_ISA(obj); - m48t59_toggle_lock(&d->state, lock); -} - -static Property m48t59_isa_properties[] = { - DEFINE_PROP_INT32("base-year", M48txxISAState, state.base_year, 0), - DEFINE_PROP_UINT32("iobase", M48txxISAState, io_base, 0x74), - DEFINE_PROP_END_OF_LIST(), -}; - -static void m48txx_isa_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - NvramClass *nc = NVRAM_CLASS(klass); - - dc->realize = m48t59_isa_realize; - dc->reset = m48t59_reset_isa; - dc->props = m48t59_isa_properties; - nc->read = m48txx_isa_read; - nc->write = m48txx_isa_write; - nc->toggle_lock = m48txx_isa_toggle_lock; -} - -static void m48txx_isa_concrete_class_init(ObjectClass *klass, void *data) -{ - M48txxISADeviceClass *u = M48TXX_ISA_CLASS(klass); - M48txxInfo *info = data; - - u->info = *info; -} - -static uint32_t m48txx_sysbus_read(Nvram *obj, uint32_t addr) -{ - M48txxSysBusState *d = M48TXX_SYS_BUS(obj); - return m48t59_read(&d->state, addr); -} - -static void m48txx_sysbus_write(Nvram *obj, uint32_t addr, uint32_t val) -{ - M48txxSysBusState *d = M48TXX_SYS_BUS(obj); - m48t59_write(&d->state, addr, val); -} - -static void m48txx_sysbus_toggle_lock(Nvram *obj, int lock) -{ - M48txxSysBusState *d = M48TXX_SYS_BUS(obj); - m48t59_toggle_lock(&d->state, lock); -} - -static Property m48t59_sysbus_properties[] = { - DEFINE_PROP_INT32("base-year", M48txxSysBusState, state.base_year, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void m48txx_sysbus_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - NvramClass *nc = NVRAM_CLASS(klass); - - k->init = m48t59_init1; - dc->reset = m48t59_reset_sysbus; - dc->props = m48t59_sysbus_properties; - nc->read = m48txx_sysbus_read; - nc->write = m48txx_sysbus_write; - nc->toggle_lock = m48txx_sysbus_toggle_lock; -} - -static void m48txx_sysbus_concrete_class_init(ObjectClass *klass, void *data) -{ - M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_CLASS(klass); - M48txxInfo *info = data; - - u->info = *info; -} - -static const TypeInfo nvram_info = { - .name = TYPE_NVRAM, - .parent = TYPE_INTERFACE, - .class_size = sizeof(NvramClass), -}; - -static const TypeInfo m48txx_sysbus_type_info = { - .name = TYPE_M48TXX_SYS_BUS, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(M48txxSysBusState), - .abstract = true, - .class_init = m48txx_sysbus_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_NVRAM }, - { } - } -}; - -static const TypeInfo m48txx_isa_type_info = { - .name = TYPE_M48TXX_ISA, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(M48txxISAState), - .abstract = true, - .class_init = m48txx_isa_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_NVRAM }, - { } - } -}; - -static void m48t59_register_types(void) -{ - TypeInfo sysbus_type_info = { - .parent = TYPE_M48TXX_SYS_BUS, - .class_size = sizeof(M48txxSysBusDeviceClass), - .class_init = m48txx_sysbus_concrete_class_init, - }; - TypeInfo isa_type_info = { - .parent = TYPE_M48TXX_ISA, - .class_size = sizeof(M48txxISADeviceClass), - .class_init = m48txx_isa_concrete_class_init, - }; - int i; - - type_register_static(&nvram_info); - type_register_static(&m48txx_sysbus_type_info); - type_register_static(&m48txx_isa_type_info); - - for (i = 0; i < ARRAY_SIZE(m48txx_info); i++) { - if (m48txx_info[i].sysbus_name) { - sysbus_type_info.name = m48txx_info[i].sysbus_name; - sysbus_type_info.class_data = &m48txx_info[i]; - type_register(&sysbus_type_info); - } - - if (m48txx_info[i].isa_name) { - isa_type_info.name = m48txx_info[i].isa_name; - isa_type_info.class_data = &m48txx_info[i]; - type_register(&isa_type_info); - } - } -} - -type_init(m48t59_register_types) diff --git a/qemu/hw/timer/mc146818rtc.c b/qemu/hw/timer/mc146818rtc.c deleted file mode 100644 index 2ac0fd3e4..000000000 --- a/qemu/hw/timer/mc146818rtc.c +++ /dev/null @@ -1,971 +0,0 @@ -/* - * QEMU MC146818 RTC emulation - * - * Copyright (c) 2003-2004 Fabrice Bellard - * - * 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 "config-target.h" -#include "qemu/cutils.h" -#include "qemu/bcd.h" -#include "hw/hw.h" -#include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "hw/timer/mc146818rtc.h" -#include "qapi/visitor.h" -#include "qapi-event.h" -#include "qmp-commands.h" - -#ifdef TARGET_I386 -#include "hw/i386/apic.h" -#endif - -//#define DEBUG_CMOS -//#define DEBUG_COALESCED - -#ifdef DEBUG_CMOS -# define CMOS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) -#else -# define CMOS_DPRINTF(format, ...) do { } while (0) -#endif - -#ifdef DEBUG_COALESCED -# define DPRINTF_C(format, ...) printf(format, ## __VA_ARGS__) -#else -# define DPRINTF_C(format, ...) do { } while (0) -#endif - -#define SEC_PER_MIN 60 -#define MIN_PER_HOUR 60 -#define SEC_PER_HOUR 3600 -#define HOUR_PER_DAY 24 -#define SEC_PER_DAY 86400 - -#define RTC_REINJECT_ON_ACK_COUNT 20 -#define RTC_CLOCK_RATE 32768 -#define UIP_HOLD_LENGTH (8 * NANOSECONDS_PER_SECOND / 32768) - -#define MC146818_RTC(obj) OBJECT_CHECK(RTCState, (obj), TYPE_MC146818_RTC) - -typedef struct RTCState { - ISADevice parent_obj; - - MemoryRegion io; - uint8_t cmos_data[128]; - uint8_t cmos_index; - int32_t base_year; - uint64_t base_rtc; - uint64_t last_update; - int64_t offset; - qemu_irq irq; - int it_shift; - /* periodic timer */ - QEMUTimer *periodic_timer; - int64_t next_periodic_time; - /* update-ended timer */ - QEMUTimer *update_timer; - uint64_t next_alarm_time; - uint16_t irq_reinject_on_ack_count; - uint32_t irq_coalesced; - uint32_t period; - QEMUTimer *coalesced_timer; - Notifier clock_reset_notifier; - LostTickPolicy lost_tick_policy; - Notifier suspend_notifier; - QLIST_ENTRY(RTCState) link; -} RTCState; - -static void rtc_set_time(RTCState *s); -static void rtc_update_time(RTCState *s); -static void rtc_set_cmos(RTCState *s, const struct tm *tm); -static inline int rtc_from_bcd(RTCState *s, int a); -static uint64_t get_next_alarm(RTCState *s); - -static inline bool rtc_running(RTCState *s) -{ - return (!(s->cmos_data[RTC_REG_B] & REG_B_SET) && - (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20); -} - -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; - return guest_rtc; -} - -#ifdef TARGET_I386 -static void rtc_coalesced_timer_update(RTCState *s) -{ - if (s->irq_coalesced == 0) { - timer_del(s->coalesced_timer); - } else { - /* 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, NANOSECONDS_PER_SECOND, RTC_CLOCK_RATE); - timer_mod(s->coalesced_timer, next_clock); - } -} - -static void rtc_coalesced_timer(void *opaque) -{ - RTCState *s = opaque; - - if (s->irq_coalesced != 0) { - apic_reset_irq_delivered(); - s->cmos_data[RTC_REG_C] |= 0xc0; - DPRINTF_C("cmos: injecting from timer\n"); - qemu_irq_raise(s->irq); - if (apic_get_irq_delivered()) { - s->irq_coalesced--; - DPRINTF_C("cmos: coalesced irqs decreased to %d\n", - s->irq_coalesced); - } - } - - rtc_coalesced_timer_update(s); -} -#endif - -/* handle periodic timer */ -static void periodic_timer_update(RTCState *s, int64_t current_time) -{ - int period_code, period; - int64_t cur_clock, next_irq_clock; - - period_code = s->cmos_data[RTC_REG_A] & 0x0f; - if (period_code != 0 - && (s->cmos_data[RTC_REG_B] & REG_B_PIE)) { - if (period_code <= 2) - period_code += 7; - /* period in 32 Khz cycles */ - period = 1 << (period_code - 1); -#ifdef TARGET_I386 - if (period != s->period) { - s->irq_coalesced = (s->irq_coalesced * s->period) / period; - DPRINTF_C("cmos: coalesced irqs scaled to %d\n", s->irq_coalesced); - } - s->period = period; -#endif - /* compute 32 khz clock */ - 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, NANOSECONDS_PER_SECOND, - RTC_CLOCK_RATE) + 1; - timer_mod(s->periodic_timer, s->next_periodic_time); - } else { -#ifdef TARGET_I386 - s->irq_coalesced = 0; -#endif - timer_del(s->periodic_timer); - } -} - -static void rtc_periodic_timer(void *opaque) -{ - RTCState *s = opaque; - - periodic_timer_update(s, s->next_periodic_time); - s->cmos_data[RTC_REG_C] |= REG_C_PF; - if (s->cmos_data[RTC_REG_B] & REG_B_PIE) { - s->cmos_data[RTC_REG_C] |= REG_C_IRQF; -#ifdef TARGET_I386 - if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { - if (s->irq_reinject_on_ack_count >= RTC_REINJECT_ON_ACK_COUNT) - s->irq_reinject_on_ack_count = 0; - apic_reset_irq_delivered(); - qemu_irq_raise(s->irq); - if (!apic_get_irq_delivered()) { - s->irq_coalesced++; - rtc_coalesced_timer_update(s); - DPRINTF_C("cmos: coalesced irqs increased to %d\n", - s->irq_coalesced); - } - } else -#endif - qemu_irq_raise(s->irq); - } -} - -/* handle update-ended timer */ -static void check_update_timer(RTCState *s) -{ - uint64_t next_update_time; - uint64_t guest_nsec; - int next_alarm_sec; - - /* From the data sheet: "Holding the dividers in reset prevents - * interrupts from operating, while setting the SET bit allows" - * them to occur. However, it will prevent an alarm interrupt - * from occurring, because the time of day is not updated. - */ - if ((s->cmos_data[RTC_REG_A] & 0x60) == 0x60) { - timer_del(s->update_timer); - return; - } - if ((s->cmos_data[RTC_REG_C] & REG_C_UF) && - (s->cmos_data[RTC_REG_B] & REG_B_SET)) { - timer_del(s->update_timer); - return; - } - if ((s->cmos_data[RTC_REG_C] & REG_C_UF) && - (s->cmos_data[RTC_REG_C] & REG_C_AF)) { - timer_del(s->update_timer); - return; - } - - guest_nsec = get_guest_rtc_ns(s) % NANOSECONDS_PER_SECOND; - /* if UF is clear, reprogram to next second */ - next_update_time = qemu_clock_get_ns(rtc_clock) - + NANOSECONDS_PER_SECOND - guest_nsec; - - /* Compute time of next alarm. One second is already accounted - * for in next_update_time. - */ - next_alarm_sec = get_next_alarm(s); - s->next_alarm_time = next_update_time + - (next_alarm_sec - 1) * NANOSECONDS_PER_SECOND; - - if (s->cmos_data[RTC_REG_C] & REG_C_UF) { - /* UF is set, but AF is clear. Program the timer to target - * the alarm time. */ - next_update_time = s->next_alarm_time; - } - if (next_update_time != timer_expire_time_ns(s->update_timer)) { - timer_mod(s->update_timer, next_update_time); - } -} - -static inline uint8_t convert_hour(RTCState *s, uint8_t hour) -{ - if (!(s->cmos_data[RTC_REG_B] & REG_B_24H)) { - hour %= 12; - if (s->cmos_data[RTC_HOURS] & 0x80) { - hour += 12; - } - } - return hour; -} - -static uint64_t get_next_alarm(RTCState *s) -{ - int32_t alarm_sec, alarm_min, alarm_hour, cur_hour, cur_min, cur_sec; - int32_t hour, min, sec; - - rtc_update_time(s); - - alarm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]); - alarm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]); - alarm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]); - alarm_hour = alarm_hour == -1 ? -1 : convert_hour(s, alarm_hour); - - cur_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]); - cur_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]); - cur_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS]); - cur_hour = convert_hour(s, cur_hour); - - if (alarm_hour == -1) { - alarm_hour = cur_hour; - if (alarm_min == -1) { - alarm_min = cur_min; - if (alarm_sec == -1) { - alarm_sec = cur_sec + 1; - } else if (cur_sec > alarm_sec) { - alarm_min++; - } - } else if (cur_min == alarm_min) { - if (alarm_sec == -1) { - alarm_sec = cur_sec + 1; - } else { - if (cur_sec > alarm_sec) { - alarm_hour++; - } - } - if (alarm_sec == SEC_PER_MIN) { - /* wrap to next hour, minutes is not in don't care mode */ - alarm_sec = 0; - alarm_hour++; - } - } else if (cur_min > alarm_min) { - alarm_hour++; - } - } else if (cur_hour == alarm_hour) { - if (alarm_min == -1) { - alarm_min = cur_min; - if (alarm_sec == -1) { - alarm_sec = cur_sec + 1; - } else if (cur_sec > alarm_sec) { - alarm_min++; - } - - if (alarm_sec == SEC_PER_MIN) { - alarm_sec = 0; - alarm_min++; - } - /* wrap to next day, hour is not in don't care mode */ - alarm_min %= MIN_PER_HOUR; - } else if (cur_min == alarm_min) { - if (alarm_sec == -1) { - alarm_sec = cur_sec + 1; - } - /* wrap to next day, hours+minutes not in don't care mode */ - alarm_sec %= SEC_PER_MIN; - } - } - - /* values that are still don't care fire at the next min/sec */ - if (alarm_min == -1) { - alarm_min = 0; - } - if (alarm_sec == -1) { - alarm_sec = 0; - } - - /* keep values in range */ - if (alarm_sec == SEC_PER_MIN) { - alarm_sec = 0; - alarm_min++; - } - if (alarm_min == MIN_PER_HOUR) { - alarm_min = 0; - alarm_hour++; - } - alarm_hour %= HOUR_PER_DAY; - - hour = alarm_hour - cur_hour; - min = hour * MIN_PER_HOUR + alarm_min - cur_min; - sec = min * SEC_PER_MIN + alarm_sec - cur_sec; - return sec <= 0 ? sec + SEC_PER_DAY : sec; -} - -static void rtc_update_timer(void *opaque) -{ - RTCState *s = opaque; - int32_t irqs = REG_C_UF; - int32_t new_irqs; - - assert((s->cmos_data[RTC_REG_A] & 0x60) != 0x60); - - /* UIP might have been latched, update time and clear it. */ - rtc_update_time(s); - s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; - - if (qemu_clock_get_ns(rtc_clock) >= s->next_alarm_time) { - irqs |= REG_C_AF; - if (s->cmos_data[RTC_REG_B] & REG_B_AIE) { - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_RTC); - } - } - - new_irqs = irqs & ~s->cmos_data[RTC_REG_C]; - s->cmos_data[RTC_REG_C] |= irqs; - if ((new_irqs & s->cmos_data[RTC_REG_B]) != 0) { - s->cmos_data[RTC_REG_C] |= REG_C_IRQF; - qemu_irq_raise(s->irq); - } - check_update_timer(s); -} - -static void cmos_ioport_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - RTCState *s = opaque; - - if ((addr & 1) == 0) { - s->cmos_index = data & 0x7f; - } else { - CMOS_DPRINTF("cmos: write index=0x%02x val=0x%02" PRIx64 "\n", - s->cmos_index, data); - switch(s->cmos_index) { - case RTC_SECONDS_ALARM: - case RTC_MINUTES_ALARM: - case RTC_HOURS_ALARM: - s->cmos_data[s->cmos_index] = data; - check_update_timer(s); - break; - case RTC_IBM_PS2_CENTURY_BYTE: - s->cmos_index = RTC_CENTURY; - /* fall through */ - case RTC_CENTURY: - case RTC_SECONDS: - case RTC_MINUTES: - case RTC_HOURS: - case RTC_DAY_OF_WEEK: - case RTC_DAY_OF_MONTH: - case RTC_MONTH: - case RTC_YEAR: - s->cmos_data[s->cmos_index] = data; - /* if in set mode, do not update the time */ - if (rtc_running(s)) { - rtc_set_time(s); - check_update_timer(s); - } - break; - case RTC_REG_A: - if ((data & 0x60) == 0x60) { - if (rtc_running(s)) { - rtc_update_time(s); - } - /* What happens to UIP when divider reset is enabled is - * unclear from the datasheet. Shouldn't matter much - * though. - */ - s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; - } else if (((s->cmos_data[RTC_REG_A] & 0x60) == 0x60) && - (data & 0x70) <= 0x20) { - /* when the divider reset is removed, the first update cycle - * begins one-half second later*/ - if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { - s->offset = 500000000; - rtc_set_time(s); - } - s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; - } - /* UIP bit is read only */ - s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) | - (s->cmos_data[RTC_REG_A] & REG_A_UIP); - periodic_timer_update(s, qemu_clock_get_ns(rtc_clock)); - check_update_timer(s); - break; - case RTC_REG_B: - if (data & REG_B_SET) { - /* update cmos to when the rtc was stopping */ - if (rtc_running(s)) { - rtc_update_time(s); - } - /* set mode: reset UIP mode */ - s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; - data &= ~REG_B_UIE; - } else { - /* if disabling set mode, update the time */ - if ((s->cmos_data[RTC_REG_B] & REG_B_SET) && - (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20) { - s->offset = get_guest_rtc_ns(s) % NANOSECONDS_PER_SECOND; - rtc_set_time(s); - } - } - /* if an interrupt flag is already set when the interrupt - * becomes enabled, raise an interrupt immediately. */ - if (data & s->cmos_data[RTC_REG_C] & REG_C_MASK) { - s->cmos_data[RTC_REG_C] |= REG_C_IRQF; - qemu_irq_raise(s->irq); - } else { - s->cmos_data[RTC_REG_C] &= ~REG_C_IRQF; - qemu_irq_lower(s->irq); - } - s->cmos_data[RTC_REG_B] = data; - periodic_timer_update(s, qemu_clock_get_ns(rtc_clock)); - check_update_timer(s); - break; - case RTC_REG_C: - case RTC_REG_D: - /* cannot write to them */ - break; - default: - s->cmos_data[s->cmos_index] = data; - break; - } - } -} - -static inline int rtc_to_bcd(RTCState *s, int a) -{ - if (s->cmos_data[RTC_REG_B] & REG_B_DM) { - return a; - } else { - return ((a / 10) << 4) | (a % 10); - } -} - -static inline int rtc_from_bcd(RTCState *s, int a) -{ - if ((a & 0xc0) == 0xc0) { - return -1; - } - if (s->cmos_data[RTC_REG_B] & REG_B_DM) { - return a; - } else { - return ((a >> 4) * 10) + (a & 0x0f); - } -} - -static void rtc_get_time(RTCState *s, struct tm *tm) -{ - tm->tm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]); - tm->tm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]); - tm->tm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f); - if (!(s->cmos_data[RTC_REG_B] & REG_B_24H)) { - tm->tm_hour %= 12; - if (s->cmos_data[RTC_HOURS] & 0x80) { - tm->tm_hour += 12; - } - } - tm->tm_wday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]) - 1; - tm->tm_mday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]); - tm->tm_mon = rtc_from_bcd(s, s->cmos_data[RTC_MONTH]) - 1; - tm->tm_year = - rtc_from_bcd(s, s->cmos_data[RTC_YEAR]) + s->base_year + - rtc_from_bcd(s, s->cmos_data[RTC_CENTURY]) * 100 - 1900; -} - -static QLIST_HEAD(, RTCState) rtc_devices = - QLIST_HEAD_INITIALIZER(rtc_devices); - -#ifdef TARGET_I386 -void qmp_rtc_reset_reinjection(Error **errp) -{ - RTCState *s; - - QLIST_FOREACH(s, &rtc_devices, link) { - s->irq_coalesced = 0; - } -} -#endif - -static void rtc_set_time(RTCState *s) -{ - struct tm tm; - - rtc_get_time(s, &tm); - s->base_rtc = mktimegm(&tm); - s->last_update = qemu_clock_get_ns(rtc_clock); - - qapi_event_send_rtc_change(qemu_timedate_diff(&tm), &error_abort); -} - -static void rtc_set_cmos(RTCState *s, const struct tm *tm) -{ - int year; - - s->cmos_data[RTC_SECONDS] = rtc_to_bcd(s, tm->tm_sec); - s->cmos_data[RTC_MINUTES] = rtc_to_bcd(s, tm->tm_min); - if (s->cmos_data[RTC_REG_B] & REG_B_24H) { - /* 24 hour format */ - s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, tm->tm_hour); - } else { - /* 12 hour format */ - int h = (tm->tm_hour % 12) ? tm->tm_hour % 12 : 12; - s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, h); - if (tm->tm_hour >= 12) - s->cmos_data[RTC_HOURS] |= 0x80; - } - s->cmos_data[RTC_DAY_OF_WEEK] = rtc_to_bcd(s, tm->tm_wday + 1); - s->cmos_data[RTC_DAY_OF_MONTH] = rtc_to_bcd(s, tm->tm_mday); - s->cmos_data[RTC_MONTH] = rtc_to_bcd(s, tm->tm_mon + 1); - year = tm->tm_year + 1900 - s->base_year; - s->cmos_data[RTC_YEAR] = rtc_to_bcd(s, year % 100); - s->cmos_data[RTC_CENTURY] = rtc_to_bcd(s, year / 100); -} - -static void rtc_update_time(RTCState *s) -{ - struct tm ret; - time_t guest_sec; - int64_t guest_nsec; - - guest_nsec = get_guest_rtc_ns(s); - guest_sec = guest_nsec / NANOSECONDS_PER_SECOND; - gmtime_r(&guest_sec, &ret); - - /* Is SET flag of Register B disabled? */ - if ((s->cmos_data[RTC_REG_B] & REG_B_SET) == 0) { - rtc_set_cmos(s, &ret); - } -} - -static int update_in_progress(RTCState *s) -{ - int64_t guest_nsec; - - if (!rtc_running(s)) { - return 0; - } - if (timer_pending(s->update_timer)) { - int64_t next_update_time = timer_expire_time_ns(s->update_timer); - /* Latch UIP until the timer expires. */ - if (qemu_clock_get_ns(rtc_clock) >= - (next_update_time - UIP_HOLD_LENGTH)) { - s->cmos_data[RTC_REG_A] |= REG_A_UIP; - return 1; - } - } - - guest_nsec = get_guest_rtc_ns(s); - /* UIP bit will be set at last 244us of every second. */ - if ((guest_nsec % NANOSECONDS_PER_SECOND) >= - (NANOSECONDS_PER_SECOND - UIP_HOLD_LENGTH)) { - return 1; - } - return 0; -} - -static uint64_t cmos_ioport_read(void *opaque, hwaddr addr, - unsigned size) -{ - RTCState *s = opaque; - int ret; - if ((addr & 1) == 0) { - return 0xff; - } else { - switch(s->cmos_index) { - case RTC_IBM_PS2_CENTURY_BYTE: - s->cmos_index = RTC_CENTURY; - /* fall through */ - case RTC_CENTURY: - case RTC_SECONDS: - case RTC_MINUTES: - case RTC_HOURS: - case RTC_DAY_OF_WEEK: - case RTC_DAY_OF_MONTH: - case RTC_MONTH: - case RTC_YEAR: - /* if not in set mode, calibrate cmos before - * reading*/ - if (rtc_running(s)) { - rtc_update_time(s); - } - ret = s->cmos_data[s->cmos_index]; - break; - case RTC_REG_A: - if (update_in_progress(s)) { - s->cmos_data[s->cmos_index] |= REG_A_UIP; - } else { - s->cmos_data[s->cmos_index] &= ~REG_A_UIP; - } - ret = s->cmos_data[s->cmos_index]; - break; - case RTC_REG_C: - ret = s->cmos_data[s->cmos_index]; - qemu_irq_lower(s->irq); - s->cmos_data[RTC_REG_C] = 0x00; - if (ret & (REG_C_UF | REG_C_AF)) { - check_update_timer(s); - } -#ifdef TARGET_I386 - if(s->irq_coalesced && - (s->cmos_data[RTC_REG_B] & REG_B_PIE) && - s->irq_reinject_on_ack_count < RTC_REINJECT_ON_ACK_COUNT) { - s->irq_reinject_on_ack_count++; - s->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_PF; - apic_reset_irq_delivered(); - DPRINTF_C("cmos: injecting on ack\n"); - qemu_irq_raise(s->irq); - if (apic_get_irq_delivered()) { - s->irq_coalesced--; - DPRINTF_C("cmos: coalesced irqs decreased to %d\n", - s->irq_coalesced); - } - } -#endif - break; - default: - ret = s->cmos_data[s->cmos_index]; - break; - } - CMOS_DPRINTF("cmos: read index=0x%02x val=0x%02x\n", - s->cmos_index, ret); - return ret; - } -} - -void rtc_set_memory(ISADevice *dev, int addr, int val) -{ - RTCState *s = MC146818_RTC(dev); - if (addr >= 0 && addr <= 127) - s->cmos_data[addr] = val; -} - -int rtc_get_memory(ISADevice *dev, int addr) -{ - RTCState *s = MC146818_RTC(dev); - assert(addr >= 0 && addr <= 127); - return s->cmos_data[addr]; -} - -static void rtc_set_date_from_host(ISADevice *dev) -{ - RTCState *s = MC146818_RTC(dev); - struct tm tm; - - qemu_get_timedate(&tm, 0); - - s->base_rtc = mktimegm(&tm); - s->last_update = qemu_clock_get_ns(rtc_clock); - s->offset = 0; - - /* set the CMOS date */ - rtc_set_cmos(s, &tm); -} - -static int rtc_post_load(void *opaque, int version_id) -{ - RTCState *s = opaque; - - if (version_id <= 2) { - rtc_set_time(s); - s->offset = 0; - check_update_timer(s); - } - - uint64_t now = qemu_clock_get_ns(rtc_clock); - if (now < s->next_periodic_time || - now > (s->next_periodic_time + get_max_clock_jump())) { - periodic_timer_update(s, qemu_clock_get_ns(rtc_clock)); - } - -#ifdef TARGET_I386 - if (version_id >= 2) { - if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { - rtc_coalesced_timer_update(s); - } - } -#endif - return 0; -} - -static bool rtc_irq_reinject_on_ack_count_needed(void *opaque) -{ - RTCState *s = (RTCState *)opaque; - return s->irq_reinject_on_ack_count != 0; -} - -static const VMStateDescription vmstate_rtc_irq_reinject_on_ack_count = { - .name = "mc146818rtc/irq_reinject_on_ack_count", - .version_id = 1, - .minimum_version_id = 1, - .needed = rtc_irq_reinject_on_ack_count_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT16(irq_reinject_on_ack_count, RTCState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_rtc = { - .name = "mc146818rtc", - .version_id = 3, - .minimum_version_id = 1, - .post_load = rtc_post_load, - .fields = (VMStateField[]) { - VMSTATE_BUFFER(cmos_data, RTCState), - VMSTATE_UINT8(cmos_index, RTCState), - VMSTATE_UNUSED(7*4), - VMSTATE_TIMER_PTR(periodic_timer, RTCState), - VMSTATE_INT64(next_periodic_time, RTCState), - VMSTATE_UNUSED(3*8), - VMSTATE_UINT32_V(irq_coalesced, RTCState, 2), - VMSTATE_UINT32_V(period, RTCState, 2), - VMSTATE_UINT64_V(base_rtc, RTCState, 3), - VMSTATE_UINT64_V(last_update, RTCState, 3), - VMSTATE_INT64_V(offset, RTCState, 3), - VMSTATE_TIMER_PTR_V(update_timer, RTCState, 3), - VMSTATE_UINT64_V(next_alarm_time, RTCState, 3), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_rtc_irq_reinject_on_ack_count, - NULL - } -}; - -static void rtc_notify_clock_reset(Notifier *notifier, void *data) -{ - RTCState *s = container_of(notifier, RTCState, clock_reset_notifier); - int64_t now = *(int64_t *)data; - - rtc_set_date_from_host(ISA_DEVICE(s)); - periodic_timer_update(s, now); - check_update_timer(s); -#ifdef TARGET_I386 - if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { - rtc_coalesced_timer_update(s); - } -#endif -} - -/* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE) - BIOS will read it and start S3 resume at POST Entry */ -static void rtc_notify_suspend(Notifier *notifier, void *data) -{ - RTCState *s = container_of(notifier, RTCState, suspend_notifier); - rtc_set_memory(ISA_DEVICE(s), 0xF, 0xFE); -} - -static void rtc_reset(void *opaque) -{ - RTCState *s = opaque; - - s->cmos_data[RTC_REG_B] &= ~(REG_B_PIE | REG_B_AIE | REG_B_SQWE); - s->cmos_data[RTC_REG_C] &= ~(REG_C_UF | REG_C_IRQF | REG_C_PF | REG_C_AF); - check_update_timer(s); - - qemu_irq_lower(s->irq); - -#ifdef TARGET_I386 - if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { - s->irq_coalesced = 0; - s->irq_reinject_on_ack_count = 0; - } -#endif -} - -static const MemoryRegionOps cmos_ops = { - .read = cmos_ioport_read, - .write = cmos_ioport_write, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void rtc_get_date(Object *obj, struct tm *current_tm, Error **errp) -{ - RTCState *s = MC146818_RTC(obj); - - rtc_update_time(s); - rtc_get_time(s, current_tm); -} - -static void rtc_realizefn(DeviceState *dev, Error **errp) -{ - ISADevice *isadev = ISA_DEVICE(dev); - RTCState *s = MC146818_RTC(dev); - int base = 0x70; - - s->cmos_data[RTC_REG_A] = 0x26; - s->cmos_data[RTC_REG_B] = 0x02; - s->cmos_data[RTC_REG_C] = 0x00; - s->cmos_data[RTC_REG_D] = 0x80; - - /* This is for historical reasons. The default base year qdev property - * was set to 2000 for most machine types before the century byte was - * implemented. - * - * This if statement means that the century byte will be always 0 - * (at least until 2079...) for base_year = 1980, but will be set - * correctly for base_year = 2000. - */ - if (s->base_year == 2000) { - s->base_year = 0; - } - - rtc_set_date_from_host(isadev); - -#ifdef TARGET_I386 - switch (s->lost_tick_policy) { - case LOST_TICK_POLICY_SLEW: - s->coalesced_timer = - timer_new_ns(rtc_clock, rtc_coalesced_timer, s); - break; - case LOST_TICK_POLICY_DISCARD: - break; - default: - error_setg(errp, "Invalid lost tick policy."); - return; - } -#endif - - s->periodic_timer = timer_new_ns(rtc_clock, rtc_periodic_timer, s); - s->update_timer = timer_new_ns(rtc_clock, rtc_update_timer, s); - check_update_timer(s); - - s->clock_reset_notifier.notify = rtc_notify_clock_reset; - qemu_clock_register_reset_notifier(rtc_clock, - &s->clock_reset_notifier); - - s->suspend_notifier.notify = rtc_notify_suspend; - qemu_register_suspend_notifier(&s->suspend_notifier); - - memory_region_init_io(&s->io, OBJECT(s), &cmos_ops, s, "rtc", 2); - isa_register_ioport(isadev, &s->io, base); - - qdev_set_legacy_instance_id(dev, base, 3); - qemu_register_reset(rtc_reset, s); - - object_property_add_tm(OBJECT(s), "date", rtc_get_date, NULL); - - object_property_add_alias(qdev_get_machine(), "rtc-time", - OBJECT(s), "date", NULL); -} - -ISADevice *rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq) -{ - DeviceState *dev; - ISADevice *isadev; - RTCState *s; - - isadev = isa_create(bus, TYPE_MC146818_RTC); - dev = DEVICE(isadev); - s = MC146818_RTC(isadev); - qdev_prop_set_int32(dev, "base_year", base_year); - qdev_init_nofail(dev); - if (intercept_irq) { - s->irq = intercept_irq; - } else { - isa_init_irq(isadev, &s->irq, RTC_ISA_IRQ); - } - QLIST_INSERT_HEAD(&rtc_devices, s, link); - - return isadev; -} - -static Property mc146818rtc_properties[] = { - DEFINE_PROP_INT32("base_year", RTCState, base_year, 1980), - DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", RTCState, - lost_tick_policy, LOST_TICK_POLICY_DISCARD), - DEFINE_PROP_END_OF_LIST(), -}; - -static void rtc_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = rtc_realizefn; - dc->vmsd = &vmstate_rtc; - dc->props = mc146818rtc_properties; - /* Reason: needs to be wired up by rtc_init() */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static void rtc_finalize(Object *obj) -{ - object_property_del(qdev_get_machine(), "rtc", NULL); -} - -static const TypeInfo mc146818rtc_info = { - .name = TYPE_MC146818_RTC, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(RTCState), - .class_init = rtc_class_initfn, - .instance_finalize = rtc_finalize, -}; - -static void mc146818rtc_register_types(void) -{ - type_register_static(&mc146818rtc_info); -} - -type_init(mc146818rtc_register_types) diff --git a/qemu/hw/timer/milkymist-sysctl.c b/qemu/hw/timer/milkymist-sysctl.c deleted file mode 100644 index 5f2948037..000000000 --- a/qemu/hw/timer/milkymist-sysctl.c +++ /dev/null @@ -1,342 +0,0 @@ -/* - * QEMU model of the Milkymist System Controller. - * - * Copyright (c) 2010-2012 Michael Walle - * - * 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 . - * - * - * Specification available at: - * http://www.milkymist.org/socdoc/sysctl.pdf - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "sysemu/sysemu.h" -#include "trace.h" -#include "qemu/timer.h" -#include "hw/ptimer.h" -#include "qemu/error-report.h" - -enum { - CTRL_ENABLE = (1<<0), - CTRL_AUTORESTART = (1<<1), -}; - -enum { - ICAP_READY = (1<<0), -}; - -enum { - R_GPIO_IN = 0, - R_GPIO_OUT, - R_GPIO_INTEN, - R_TIMER0_CONTROL = 4, - R_TIMER0_COMPARE, - R_TIMER0_COUNTER, - R_TIMER1_CONTROL = 8, - R_TIMER1_COMPARE, - R_TIMER1_COUNTER, - R_ICAP = 16, - R_DBG_SCRATCHPAD = 20, - R_DBG_WRITE_LOCK, - R_CLK_FREQUENCY = 29, - R_CAPABILITIES, - R_SYSTEM_ID, - R_MAX -}; - -#define TYPE_MILKYMIST_SYSCTL "milkymist-sysctl" -#define MILKYMIST_SYSCTL(obj) \ - OBJECT_CHECK(MilkymistSysctlState, (obj), TYPE_MILKYMIST_SYSCTL) - -struct MilkymistSysctlState { - SysBusDevice parent_obj; - - MemoryRegion regs_region; - - QEMUBH *bh0; - QEMUBH *bh1; - ptimer_state *ptimer0; - ptimer_state *ptimer1; - - uint32_t freq_hz; - uint32_t capabilities; - uint32_t systemid; - uint32_t strappings; - - uint32_t regs[R_MAX]; - - qemu_irq gpio_irq; - qemu_irq timer0_irq; - qemu_irq timer1_irq; -}; -typedef struct MilkymistSysctlState MilkymistSysctlState; - -static void sysctl_icap_write(MilkymistSysctlState *s, uint32_t value) -{ - trace_milkymist_sysctl_icap_write(value); - switch (value & 0xffff) { - case 0x000e: - qemu_system_shutdown_request(); - break; - } -} - -static uint64_t sysctl_read(void *opaque, hwaddr addr, - unsigned size) -{ - MilkymistSysctlState *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_TIMER0_COUNTER: - r = (uint32_t)ptimer_get_count(s->ptimer0); - /* milkymist timer counts up */ - r = s->regs[R_TIMER0_COMPARE] - r; - break; - case R_TIMER1_COUNTER: - r = (uint32_t)ptimer_get_count(s->ptimer1); - /* milkymist timer counts up */ - r = s->regs[R_TIMER1_COMPARE] - r; - break; - case R_GPIO_IN: - case R_GPIO_OUT: - case R_GPIO_INTEN: - case R_TIMER0_CONTROL: - case R_TIMER0_COMPARE: - case R_TIMER1_CONTROL: - case R_TIMER1_COMPARE: - case R_ICAP: - case R_DBG_SCRATCHPAD: - case R_DBG_WRITE_LOCK: - case R_CLK_FREQUENCY: - case R_CAPABILITIES: - case R_SYSTEM_ID: - r = s->regs[addr]; - break; - - default: - error_report("milkymist_sysctl: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_milkymist_sysctl_memory_read(addr << 2, r); - - return r; -} - -static void sysctl_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - MilkymistSysctlState *s = opaque; - - trace_milkymist_sysctl_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_GPIO_OUT: - case R_GPIO_INTEN: - case R_TIMER0_COUNTER: - case R_TIMER1_COUNTER: - case R_DBG_SCRATCHPAD: - s->regs[addr] = value; - break; - case R_TIMER0_COMPARE: - ptimer_set_limit(s->ptimer0, value, 0); - s->regs[addr] = value; - break; - case R_TIMER1_COMPARE: - ptimer_set_limit(s->ptimer1, value, 0); - s->regs[addr] = value; - break; - case R_TIMER0_CONTROL: - s->regs[addr] = value; - if (s->regs[R_TIMER0_CONTROL] & CTRL_ENABLE) { - trace_milkymist_sysctl_start_timer0(); - ptimer_set_count(s->ptimer0, - s->regs[R_TIMER0_COMPARE] - s->regs[R_TIMER0_COUNTER]); - ptimer_run(s->ptimer0, 0); - } else { - trace_milkymist_sysctl_stop_timer0(); - ptimer_stop(s->ptimer0); - } - break; - case R_TIMER1_CONTROL: - s->regs[addr] = value; - if (s->regs[R_TIMER1_CONTROL] & CTRL_ENABLE) { - trace_milkymist_sysctl_start_timer1(); - ptimer_set_count(s->ptimer1, - s->regs[R_TIMER1_COMPARE] - s->regs[R_TIMER1_COUNTER]); - ptimer_run(s->ptimer1, 0); - } else { - trace_milkymist_sysctl_stop_timer1(); - ptimer_stop(s->ptimer1); - } - break; - case R_ICAP: - sysctl_icap_write(s, value); - break; - case R_DBG_WRITE_LOCK: - s->regs[addr] = 1; - break; - case R_SYSTEM_ID: - qemu_system_reset_request(); - break; - - case R_GPIO_IN: - case R_CLK_FREQUENCY: - case R_CAPABILITIES: - error_report("milkymist_sysctl: write to read-only register 0x" - TARGET_FMT_plx, addr << 2); - break; - - default: - error_report("milkymist_sysctl: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } -} - -static const MemoryRegionOps sysctl_mmio_ops = { - .read = sysctl_read, - .write = sysctl_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void timer0_hit(void *opaque) -{ - MilkymistSysctlState *s = opaque; - - if (!(s->regs[R_TIMER0_CONTROL] & CTRL_AUTORESTART)) { - s->regs[R_TIMER0_CONTROL] &= ~CTRL_ENABLE; - trace_milkymist_sysctl_stop_timer0(); - ptimer_stop(s->ptimer0); - } - - trace_milkymist_sysctl_pulse_irq_timer0(); - qemu_irq_pulse(s->timer0_irq); -} - -static void timer1_hit(void *opaque) -{ - MilkymistSysctlState *s = opaque; - - if (!(s->regs[R_TIMER1_CONTROL] & CTRL_AUTORESTART)) { - s->regs[R_TIMER1_CONTROL] &= ~CTRL_ENABLE; - trace_milkymist_sysctl_stop_timer1(); - ptimer_stop(s->ptimer1); - } - - trace_milkymist_sysctl_pulse_irq_timer1(); - qemu_irq_pulse(s->timer1_irq); -} - -static void milkymist_sysctl_reset(DeviceState *d) -{ - MilkymistSysctlState *s = MILKYMIST_SYSCTL(d); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } - - ptimer_stop(s->ptimer0); - ptimer_stop(s->ptimer1); - - /* defaults */ - s->regs[R_ICAP] = ICAP_READY; - s->regs[R_SYSTEM_ID] = s->systemid; - s->regs[R_CLK_FREQUENCY] = s->freq_hz; - s->regs[R_CAPABILITIES] = s->capabilities; - s->regs[R_GPIO_IN] = s->strappings; -} - -static int milkymist_sysctl_init(SysBusDevice *dev) -{ - MilkymistSysctlState *s = MILKYMIST_SYSCTL(dev); - - sysbus_init_irq(dev, &s->gpio_irq); - sysbus_init_irq(dev, &s->timer0_irq); - sysbus_init_irq(dev, &s->timer1_irq); - - s->bh0 = qemu_bh_new(timer0_hit, s); - s->bh1 = qemu_bh_new(timer1_hit, s); - s->ptimer0 = ptimer_init(s->bh0); - s->ptimer1 = ptimer_init(s->bh1); - ptimer_set_freq(s->ptimer0, s->freq_hz); - ptimer_set_freq(s->ptimer1, s->freq_hz); - - memory_region_init_io(&s->regs_region, OBJECT(s), &sysctl_mmio_ops, s, - "milkymist-sysctl", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); - - return 0; -} - -static const VMStateDescription vmstate_milkymist_sysctl = { - .name = "milkymist-sysctl", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, MilkymistSysctlState, R_MAX), - VMSTATE_PTIMER(ptimer0, MilkymistSysctlState), - VMSTATE_PTIMER(ptimer1, MilkymistSysctlState), - VMSTATE_END_OF_LIST() - } -}; - -static Property milkymist_sysctl_properties[] = { - DEFINE_PROP_UINT32("frequency", MilkymistSysctlState, - freq_hz, 80000000), - DEFINE_PROP_UINT32("capabilities", MilkymistSysctlState, - capabilities, 0x00000000), - DEFINE_PROP_UINT32("systemid", MilkymistSysctlState, - systemid, 0x10014d31), - DEFINE_PROP_UINT32("gpio_strappings", MilkymistSysctlState, - strappings, 0x00000001), - DEFINE_PROP_END_OF_LIST(), -}; - -static void milkymist_sysctl_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = milkymist_sysctl_init; - dc->reset = milkymist_sysctl_reset; - dc->vmsd = &vmstate_milkymist_sysctl; - dc->props = milkymist_sysctl_properties; -} - -static const TypeInfo milkymist_sysctl_info = { - .name = TYPE_MILKYMIST_SYSCTL, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MilkymistSysctlState), - .class_init = milkymist_sysctl_class_init, -}; - -static void milkymist_sysctl_register_types(void) -{ - type_register_static(&milkymist_sysctl_info); -} - -type_init(milkymist_sysctl_register_types) diff --git a/qemu/hw/timer/omap_gptimer.c b/qemu/hw/timer/omap_gptimer.c deleted file mode 100644 index 3a4386304..000000000 --- a/qemu/hw/timer/omap_gptimer.c +++ /dev/null @@ -1,488 +0,0 @@ -/* - * TI OMAP2 general purpose timers emulation. - * - * Copyright (C) 2007-2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version 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 . - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "qemu/timer.h" -#include "hw/arm/omap.h" - -/* GP timers */ -struct omap_gp_timer_s { - MemoryRegion iomem; - qemu_irq irq; - qemu_irq wkup; - qemu_irq in; - qemu_irq out; - omap_clk clk; - QEMUTimer *timer; - QEMUTimer *match; - struct omap_target_agent_s *ta; - - int in_val; - int out_val; - int64_t time; - int64_t rate; - int64_t ticks_per_sec; - - int16_t config; - int status; - int it_ena; - int wu_ena; - int enable; - int inout; - int capt2; - int pt; - enum { - gpt_trigger_none, gpt_trigger_overflow, gpt_trigger_both - } trigger; - enum { - gpt_capture_none, gpt_capture_rising, - gpt_capture_falling, gpt_capture_both - } capture; - int scpwm; - int ce; - int pre; - int ptv; - int ar; - int st; - int posted; - uint32_t val; - uint32_t load_val; - uint32_t capture_val[2]; - uint32_t match_val; - int capt_num; - - uint16_t writeh; /* LSB */ - uint16_t readh; /* MSB */ -}; - -#define GPT_TCAR_IT (1 << 2) -#define GPT_OVF_IT (1 << 1) -#define GPT_MAT_IT (1 << 0) - -static inline void omap_gp_timer_intr(struct omap_gp_timer_s *timer, int it) -{ - if (timer->it_ena & it) { - if (!timer->status) - qemu_irq_raise(timer->irq); - - timer->status |= it; - /* Or are the status bits set even when masked? - * i.e. is masking applied before or after the status register? */ - } - - if (timer->wu_ena & it) - qemu_irq_pulse(timer->wkup); -} - -static inline void omap_gp_timer_out(struct omap_gp_timer_s *timer, int level) -{ - if (!timer->inout && timer->out_val != level) { - timer->out_val = level; - qemu_set_irq(timer->out, level); - } -} - -static inline uint32_t omap_gp_timer_read(struct omap_gp_timer_s *timer) -{ - uint64_t distance; - - if (timer->st && timer->rate) { - distance = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - timer->time; - distance = muldiv64(distance, timer->rate, timer->ticks_per_sec); - - if (distance >= 0xffffffff - timer->val) - return 0xffffffff; - else - return timer->val + distance; - } else - return timer->val; -} - -static inline void omap_gp_timer_sync(struct omap_gp_timer_s *timer) -{ - if (timer->st) { - timer->val = omap_gp_timer_read(timer); - timer->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - } -} - -static inline void omap_gp_timer_update(struct omap_gp_timer_s *timer) -{ - int64_t expires, matches; - - if (timer->st && timer->rate) { - expires = muldiv64(0x100000000ll - timer->val, - timer->ticks_per_sec, timer->rate); - timer_mod(timer->timer, timer->time + expires); - - if (timer->ce && timer->match_val >= timer->val) { - matches = muldiv64(timer->match_val - timer->val, - timer->ticks_per_sec, timer->rate); - timer_mod(timer->match, timer->time + matches); - } else - timer_del(timer->match); - } else { - timer_del(timer->timer); - timer_del(timer->match); - omap_gp_timer_out(timer, timer->scpwm); - } -} - -static inline void omap_gp_timer_trigger(struct omap_gp_timer_s *timer) -{ - if (timer->pt) - /* TODO in overflow-and-match mode if the first event to - * occur is the match, don't toggle. */ - omap_gp_timer_out(timer, !timer->out_val); - else - /* TODO inverted pulse on timer->out_val == 1? */ - qemu_irq_pulse(timer->out); -} - -static void omap_gp_timer_tick(void *opaque) -{ - struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; - - if (!timer->ar) { - timer->st = 0; - timer->val = 0; - } else { - timer->val = timer->load_val; - timer->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - } - - if (timer->trigger == gpt_trigger_overflow || - timer->trigger == gpt_trigger_both) - omap_gp_timer_trigger(timer); - - omap_gp_timer_intr(timer, GPT_OVF_IT); - omap_gp_timer_update(timer); -} - -static void omap_gp_timer_match(void *opaque) -{ - struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; - - if (timer->trigger == gpt_trigger_both) - omap_gp_timer_trigger(timer); - - omap_gp_timer_intr(timer, GPT_MAT_IT); -} - -static void omap_gp_timer_input(void *opaque, int line, int on) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - int trigger; - - switch (s->capture) { - default: - case gpt_capture_none: - trigger = 0; - break; - case gpt_capture_rising: - trigger = !s->in_val && on; - break; - case gpt_capture_falling: - trigger = s->in_val && !on; - break; - case gpt_capture_both: - trigger = (s->in_val == !on); - break; - } - s->in_val = on; - - if (s->inout && trigger && s->capt_num < 2) { - s->capture_val[s->capt_num] = omap_gp_timer_read(s); - - if (s->capt2 == s->capt_num ++) - omap_gp_timer_intr(s, GPT_TCAR_IT); - } -} - -static void omap_gp_timer_clk_update(void *opaque, int line, int on) -{ - struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; - - omap_gp_timer_sync(timer); - timer->rate = on ? omap_clk_getrate(timer->clk) : 0; - omap_gp_timer_update(timer); -} - -static void omap_gp_timer_clk_setup(struct omap_gp_timer_s *timer) -{ - omap_clk_adduser(timer->clk, - qemu_allocate_irq(omap_gp_timer_clk_update, timer, 0)); - timer->rate = omap_clk_getrate(timer->clk); -} - -void omap_gp_timer_reset(struct omap_gp_timer_s *s) -{ - s->config = 0x000; - s->status = 0; - s->it_ena = 0; - s->wu_ena = 0; - s->inout = 0; - s->capt2 = 0; - s->capt_num = 0; - s->pt = 0; - s->trigger = gpt_trigger_none; - s->capture = gpt_capture_none; - s->scpwm = 0; - s->ce = 0; - s->pre = 0; - s->ptv = 0; - s->ar = 0; - s->st = 0; - s->posted = 1; - s->val = 0x00000000; - s->load_val = 0x00000000; - s->capture_val[0] = 0x00000000; - s->capture_val[1] = 0x00000000; - s->match_val = 0x00000000; - omap_gp_timer_update(s); -} - -static uint32_t omap_gp_timer_readw(void *opaque, hwaddr addr) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - - switch (addr) { - case 0x00: /* TIDR */ - return 0x21; - - case 0x10: /* TIOCP_CFG */ - return s->config; - - case 0x14: /* TISTAT */ - /* ??? When's this bit reset? */ - return 1; /* RESETDONE */ - - case 0x18: /* TISR */ - return s->status; - - case 0x1c: /* TIER */ - return s->it_ena; - - case 0x20: /* TWER */ - return s->wu_ena; - - case 0x24: /* TCLR */ - return (s->inout << 14) | - (s->capt2 << 13) | - (s->pt << 12) | - (s->trigger << 10) | - (s->capture << 8) | - (s->scpwm << 7) | - (s->ce << 6) | - (s->pre << 5) | - (s->ptv << 2) | - (s->ar << 1) | - (s->st << 0); - - case 0x28: /* TCRR */ - return omap_gp_timer_read(s); - - case 0x2c: /* TLDR */ - return s->load_val; - - case 0x30: /* TTGR */ - return 0xffffffff; - - case 0x34: /* TWPS */ - return 0x00000000; /* No posted writes pending. */ - - case 0x38: /* TMAR */ - return s->match_val; - - case 0x3c: /* TCAR1 */ - return s->capture_val[0]; - - case 0x40: /* TSICR */ - return s->posted << 2; - - case 0x44: /* TCAR2 */ - return s->capture_val[1]; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static uint32_t omap_gp_timer_readh(void *opaque, hwaddr addr) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - uint32_t ret; - - if (addr & 2) - return s->readh; - else { - ret = omap_gp_timer_readw(opaque, addr); - s->readh = ret >> 16; - return ret & 0xffff; - } -} - -static void omap_gp_timer_write(void *opaque, hwaddr addr, - uint32_t value) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - - switch (addr) { - case 0x00: /* TIDR */ - case 0x14: /* TISTAT */ - case 0x34: /* TWPS */ - case 0x3c: /* TCAR1 */ - case 0x44: /* TCAR2 */ - OMAP_RO_REG(addr); - break; - - case 0x10: /* TIOCP_CFG */ - s->config = value & 0x33d; - if (((value >> 3) & 3) == 3) /* IDLEMODE */ - fprintf(stderr, "%s: illegal IDLEMODE value in TIOCP_CFG\n", - __FUNCTION__); - if (value & 2) /* SOFTRESET */ - omap_gp_timer_reset(s); - break; - - case 0x18: /* TISR */ - if (value & GPT_TCAR_IT) - s->capt_num = 0; - if (s->status && !(s->status &= ~value)) - qemu_irq_lower(s->irq); - break; - - case 0x1c: /* TIER */ - s->it_ena = value & 7; - break; - - case 0x20: /* TWER */ - s->wu_ena = value & 7; - break; - - case 0x24: /* TCLR */ - omap_gp_timer_sync(s); - s->inout = (value >> 14) & 1; - s->capt2 = (value >> 13) & 1; - s->pt = (value >> 12) & 1; - s->trigger = (value >> 10) & 3; - if (s->capture == gpt_capture_none && - ((value >> 8) & 3) != gpt_capture_none) - s->capt_num = 0; - s->capture = (value >> 8) & 3; - s->scpwm = (value >> 7) & 1; - s->ce = (value >> 6) & 1; - s->pre = (value >> 5) & 1; - s->ptv = (value >> 2) & 7; - s->ar = (value >> 1) & 1; - s->st = (value >> 0) & 1; - if (s->inout && s->trigger != gpt_trigger_none) - fprintf(stderr, "%s: GP timer pin must be an output " - "for this trigger mode\n", __FUNCTION__); - if (!s->inout && s->capture != gpt_capture_none) - fprintf(stderr, "%s: GP timer pin must be an input " - "for this capture mode\n", __FUNCTION__); - 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 = NANOSECONDS_PER_SECOND << (s->pre ? s->ptv + 1 : 0); - omap_gp_timer_update(s); - break; - - case 0x28: /* TCRR */ - s->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - s->val = value; - omap_gp_timer_update(s); - break; - - case 0x2c: /* TLDR */ - s->load_val = value; - break; - - case 0x30: /* TTGR */ - s->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - s->val = s->load_val; - omap_gp_timer_update(s); - break; - - case 0x38: /* TMAR */ - omap_gp_timer_sync(s); - s->match_val = value; - omap_gp_timer_update(s); - break; - - case 0x40: /* TSICR */ - s->posted = (value >> 2) & 1; - if (value & 2) /* How much exactly are we supposed to reset? */ - omap_gp_timer_reset(s); - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static void omap_gp_timer_writeh(void *opaque, hwaddr addr, - uint32_t value) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - - if (addr & 2) - omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh); - else - s->writeh = (uint16_t) value; -} - -static const MemoryRegionOps omap_gp_timer_ops = { - .old_mmio = { - .read = { - omap_badwidth_read32, - omap_gp_timer_readh, - omap_gp_timer_readw, - }, - .write = { - omap_badwidth_write32, - omap_gp_timer_writeh, - omap_gp_timer_write, - }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -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 = g_new0(struct omap_gp_timer_s, 1); - - s->ta = ta; - s->irq = irq; - s->clk = fclk; - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, omap_gp_timer_tick, s); - s->match = timer_new_ns(QEMU_CLOCK_VIRTUAL, omap_gp_timer_match, s); - s->in = qemu_allocate_irq(omap_gp_timer_input, s, 0); - omap_gp_timer_reset(s); - omap_gp_timer_clk_setup(s); - - memory_region_init_io(&s->iomem, NULL, &omap_gp_timer_ops, s, "omap.gptimer", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &s->iomem); - - return s; -} diff --git a/qemu/hw/timer/omap_synctimer.c b/qemu/hw/timer/omap_synctimer.c deleted file mode 100644 index 9ee651979..000000000 --- a/qemu/hw/timer/omap_synctimer.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * TI OMAP2 32kHz sync timer emulation. - * - * Copyright (C) 2007-2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version 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 . - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "qemu/timer.h" -#include "hw/arm/omap.h" -struct omap_synctimer_s { - MemoryRegion iomem; - uint32_t val; - uint16_t readh; -}; - -/* 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, - NANOSECONDS_PER_SECOND); -} - -void omap_synctimer_reset(struct omap_synctimer_s *s) -{ - s->val = omap_synctimer_read(s); -} - -static uint32_t omap_synctimer_readw(void *opaque, hwaddr addr) -{ - struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque; - - switch (addr) { - case 0x00: /* 32KSYNCNT_REV */ - return 0x21; - - case 0x10: /* CR */ - return omap_synctimer_read(s) - s->val; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static uint32_t omap_synctimer_readh(void *opaque, hwaddr addr) -{ - struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque; - uint32_t ret; - - if (addr & 2) - return s->readh; - else { - ret = omap_synctimer_readw(opaque, addr); - s->readh = ret >> 16; - return ret & 0xffff; - } -} - -static void omap_synctimer_write(void *opaque, hwaddr addr, - uint32_t value) -{ - OMAP_BAD_REG(addr); -} - -static const MemoryRegionOps omap_synctimer_ops = { - .old_mmio = { - .read = { - omap_badwidth_read32, - omap_synctimer_readh, - omap_synctimer_readw, - }, - .write = { - omap_badwidth_write32, - omap_synctimer_write, - omap_synctimer_write, - }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_synctimer_s *omap_synctimer_init(struct omap_target_agent_s *ta, - struct omap_mpu_state_s *mpu, omap_clk fclk, omap_clk iclk) -{ - struct omap_synctimer_s *s = g_malloc0(sizeof(*s)); - - omap_synctimer_reset(s); - memory_region_init_io(&s->iomem, NULL, &omap_synctimer_ops, s, "omap.synctimer", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &s->iomem); - - return s; -} diff --git a/qemu/hw/timer/pl031.c b/qemu/hw/timer/pl031.c deleted file mode 100644 index 38e0cb5ad..000000000 --- a/qemu/hw/timer/pl031.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * ARM AMBA PrimeCell PL031 RTC - * - * Copyright (c) 2007 CodeSourcery - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 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 "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "qemu/cutils.h" - -//#define DEBUG_PL031 - -#ifdef DEBUG_PL031 -#define DPRINTF(fmt, ...) \ -do { printf("pl031: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - -#define RTC_DR 0x00 /* Data read register */ -#define RTC_MR 0x04 /* Match register */ -#define RTC_LR 0x08 /* Data load register */ -#define RTC_CR 0x0c /* Control register */ -#define RTC_IMSC 0x10 /* Interrupt mask and set register */ -#define RTC_RIS 0x14 /* Raw interrupt status register */ -#define RTC_MIS 0x18 /* Masked interrupt status register */ -#define RTC_ICR 0x1c /* Interrupt clear register */ - -#define TYPE_PL031 "pl031" -#define PL031(obj) OBJECT_CHECK(PL031State, (obj), TYPE_PL031) - -typedef struct PL031State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - QEMUTimer *timer; - qemu_irq irq; - - /* Needed to preserve the tick_count across migration, even if the - * absolute value of the rtc_clock is different on the source and - * destination. - */ - uint32_t tick_offset_vmstate; - uint32_t tick_offset; - - uint32_t mr; - uint32_t lr; - uint32_t cr; - uint32_t im; - uint32_t is; -} PL031State; - -static const unsigned char pl031_id[] = { - 0x31, 0x10, 0x14, 0x00, /* Device ID */ - 0x0d, 0xf0, 0x05, 0xb1 /* Cell ID */ -}; - -static void pl031_update(PL031State *s) -{ - qemu_set_irq(s->irq, s->is & s->im); -} - -static void pl031_interrupt(void * opaque) -{ - PL031State *s = (PL031State *)opaque; - - s->is = 1; - DPRINTF("Alarm raised\n"); - pl031_update(s); -} - -static uint32_t pl031_get_count(PL031State *s) -{ - int64_t now = qemu_clock_get_ns(rtc_clock); - return s->tick_offset + now / NANOSECONDS_PER_SECOND; -} - -static void pl031_set_alarm(PL031State *s) -{ - uint32_t ticks; - - /* The timer wraps around. This subtraction also wraps in the same way, - and gives correct results when alarm < now_ticks. */ - ticks = s->mr - pl031_get_count(s); - DPRINTF("Alarm set in %ud ticks\n", ticks); - if (ticks == 0) { - timer_del(s->timer); - pl031_interrupt(s); - } else { - int64_t now = qemu_clock_get_ns(rtc_clock); - timer_mod(s->timer, now + (int64_t)ticks * NANOSECONDS_PER_SECOND); - } -} - -static uint64_t pl031_read(void *opaque, hwaddr offset, - unsigned size) -{ - PL031State *s = (PL031State *)opaque; - - if (offset >= 0xfe0 && offset < 0x1000) - return pl031_id[(offset - 0xfe0) >> 2]; - - switch (offset) { - case RTC_DR: - return pl031_get_count(s); - case RTC_MR: - return s->mr; - case RTC_IMSC: - return s->im; - case RTC_RIS: - return s->is; - case RTC_LR: - return s->lr; - case RTC_CR: - /* RTC is permanently enabled. */ - return 1; - case RTC_MIS: - return s->is & s->im; - case RTC_ICR: - qemu_log_mask(LOG_GUEST_ERROR, - "pl031: read of write-only register at offset 0x%x\n", - (int)offset); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pl031_read: Bad offset 0x%x\n", (int)offset); - break; - } - - return 0; -} - -static void pl031_write(void * opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PL031State *s = (PL031State *)opaque; - - - switch (offset) { - case RTC_LR: - s->tick_offset += value - pl031_get_count(s); - pl031_set_alarm(s); - break; - case RTC_MR: - s->mr = value; - pl031_set_alarm(s); - break; - case RTC_IMSC: - s->im = value & 1; - DPRINTF("Interrupt mask %d\n", s->im); - pl031_update(s); - break; - case RTC_ICR: - /* The PL031 documentation (DDI0224B) states that the interrupt is - cleared when bit 0 of the written value is set. However the - arm926e documentation (DDI0287B) states that the interrupt is - cleared when any value is written. */ - DPRINTF("Interrupt cleared"); - s->is = 0; - pl031_update(s); - break; - case RTC_CR: - /* Written value is ignored. */ - break; - - case RTC_DR: - case RTC_MIS: - case RTC_RIS: - qemu_log_mask(LOG_GUEST_ERROR, - "pl031: write to read-only register at offset 0x%x\n", - (int)offset); - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pl031_write: Bad offset 0x%x\n", (int)offset); - break; - } -} - -static const MemoryRegionOps pl031_ops = { - .read = pl031_read, - .write = pl031_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pl031_init(Object *obj) -{ - PL031State *s = PL031(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); - struct tm tm; - - 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) / NANOSECONDS_PER_SECOND; - - s->timer = timer_new_ns(rtc_clock, pl031_interrupt, s); -} - -static void pl031_pre_save(void *opaque) -{ - PL031State *s = 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 / NANOSECONDS_PER_SECOND; -} - -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 / NANOSECONDS_PER_SECOND; - pl031_set_alarm(s); - return 0; -} - -static const VMStateDescription vmstate_pl031 = { - .name = "pl031", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = pl031_pre_save, - .post_load = pl031_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(tick_offset_vmstate, PL031State), - VMSTATE_UINT32(mr, PL031State), - VMSTATE_UINT32(lr, PL031State), - VMSTATE_UINT32(cr, PL031State), - VMSTATE_UINT32(im, PL031State), - VMSTATE_UINT32(is, PL031State), - VMSTATE_END_OF_LIST() - } -}; - -static void pl031_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_pl031; -} - -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, -}; - -static void pl031_register_types(void) -{ - type_register_static(&pl031_info); -} - -type_init(pl031_register_types) diff --git a/qemu/hw/timer/puv3_ost.c b/qemu/hw/timer/puv3_ost.c deleted file mode 100644 index 93650b799..000000000 --- a/qemu/hw/timer/puv3_ost.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * OSTimer device simulation in PKUnity SoC - * - * Copyright (C) 2010-2012 Guan Xuetao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation, 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" - -#undef DEBUG_PUV3 -#include "hw/unicore32/puv3.h" - -#define TYPE_PUV3_OST "puv3_ost" -#define PUV3_OST(obj) OBJECT_CHECK(PUV3OSTState, (obj), TYPE_PUV3_OST) - -/* puv3 ostimer implementation. */ -typedef struct PUV3OSTState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - QEMUBH *bh; - qemu_irq irq; - ptimer_state *ptimer; - - uint32_t reg_OSMR0; - uint32_t reg_OSCR; - uint32_t reg_OSSR; - uint32_t reg_OIER; -} PUV3OSTState; - -static uint64_t puv3_ost_read(void *opaque, hwaddr offset, - unsigned size) -{ - PUV3OSTState *s = opaque; - uint32_t ret = 0; - - switch (offset) { - case 0x10: /* Counter Register */ - ret = s->reg_OSMR0 - (uint32_t)ptimer_get_count(s->ptimer); - break; - case 0x14: /* Status Register */ - ret = s->reg_OSSR; - break; - case 0x1c: /* Interrupt Enable Register */ - ret = s->reg_OIER; - break; - default: - DPRINTF("Bad offset %x\n", (int)offset); - } - DPRINTF("offset 0x%x, value 0x%x\n", offset, ret); - return ret; -} - -static void puv3_ost_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PUV3OSTState *s = opaque; - - DPRINTF("offset 0x%x, value 0x%x\n", offset, value); - switch (offset) { - case 0x00: /* Match Register 0 */ - s->reg_OSMR0 = value; - if (s->reg_OSMR0 > s->reg_OSCR) { - ptimer_set_count(s->ptimer, s->reg_OSMR0 - s->reg_OSCR); - } else { - ptimer_set_count(s->ptimer, s->reg_OSMR0 + - (0xffffffff - s->reg_OSCR)); - } - ptimer_run(s->ptimer, 2); - break; - case 0x14: /* Status Register */ - assert(value == 0); - if (s->reg_OSSR) { - s->reg_OSSR = value; - qemu_irq_lower(s->irq); - } - break; - case 0x1c: /* Interrupt Enable Register */ - s->reg_OIER = value; - break; - default: - DPRINTF("Bad offset %x\n", (int)offset); - } -} - -static const MemoryRegionOps puv3_ost_ops = { - .read = puv3_ost_read, - .write = puv3_ost_write, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void puv3_ost_tick(void *opaque) -{ - PUV3OSTState *s = opaque; - - DPRINTF("ost hit when ptimer counter from 0x%x to 0x%x!\n", - s->reg_OSCR, s->reg_OSMR0); - - s->reg_OSCR = s->reg_OSMR0; - if (s->reg_OIER) { - s->reg_OSSR = 1; - qemu_irq_raise(s->irq); - } -} - -static int puv3_ost_init(SysBusDevice *dev) -{ - PUV3OSTState *s = PUV3_OST(dev); - - s->reg_OIER = 0; - s->reg_OSSR = 0; - s->reg_OSMR0 = 0; - s->reg_OSCR = 0; - - sysbus_init_irq(dev, &s->irq); - - s->bh = qemu_bh_new(puv3_ost_tick, s); - s->ptimer = ptimer_init(s->bh); - ptimer_set_freq(s->ptimer, 50 * 1000 * 1000); - - memory_region_init_io(&s->iomem, OBJECT(s), &puv3_ost_ops, s, "puv3_ost", - PUV3_REGS_OFFSET); - sysbus_init_mmio(dev, &s->iomem); - - return 0; -} - -static void puv3_ost_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = puv3_ost_init; -} - -static const TypeInfo puv3_ost_info = { - .name = TYPE_PUV3_OST, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PUV3OSTState), - .class_init = puv3_ost_class_init, -}; - -static void puv3_ost_register_type(void) -{ - type_register_static(&puv3_ost_info); -} - -type_init(puv3_ost_register_type) diff --git a/qemu/hw/timer/pxa2xx_timer.c b/qemu/hw/timer/pxa2xx_timer.c deleted file mode 100644 index 59002b407..000000000 --- a/qemu/hw/timer/pxa2xx_timer.c +++ /dev/null @@ -1,605 +0,0 @@ -/* - * Intel XScale PXA255/270 OS Timers. - * - * Copyright (c) 2006 Openedhand Ltd. - * Copyright (c) 2006 Thorsten Zitterell - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "hw/arm/pxa.h" -#include "hw/sysbus.h" - -#define OSMR0 0x00 -#define OSMR1 0x04 -#define OSMR2 0x08 -#define OSMR3 0x0c -#define OSMR4 0x80 -#define OSMR5 0x84 -#define OSMR6 0x88 -#define OSMR7 0x8c -#define OSMR8 0x90 -#define OSMR9 0x94 -#define OSMR10 0x98 -#define OSMR11 0x9c -#define OSCR 0x10 /* OS Timer Count */ -#define OSCR4 0x40 -#define OSCR5 0x44 -#define OSCR6 0x48 -#define OSCR7 0x4c -#define OSCR8 0x50 -#define OSCR9 0x54 -#define OSCR10 0x58 -#define OSCR11 0x5c -#define OSSR 0x14 /* Timer status register */ -#define OWER 0x18 -#define OIER 0x1c /* Interrupt enable register 3-0 to E3-E0 */ -#define OMCR4 0xc0 /* OS Match Control registers */ -#define OMCR5 0xc4 -#define OMCR6 0xc8 -#define OMCR7 0xcc -#define OMCR8 0xd0 -#define OMCR9 0xd4 -#define OMCR10 0xd8 -#define OMCR11 0xdc -#define OSNR 0x20 - -#define PXA25X_FREQ 3686400 /* 3.6864 MHz */ -#define PXA27X_FREQ 3250000 /* 3.25 MHz */ - -static int pxa2xx_timer4_freq[8] = { - [0] = 0, - [1] = 32768, - [2] = 1000, - [3] = 1, - [4] = 1000000, - /* [5] is the "Externally supplied clock". Assign if necessary. */ - [5 ... 7] = 0, -}; - -#define TYPE_PXA2XX_TIMER "pxa2xx-timer" -#define PXA2XX_TIMER(obj) \ - OBJECT_CHECK(PXA2xxTimerInfo, (obj), TYPE_PXA2XX_TIMER) - -typedef struct PXA2xxTimerInfo PXA2xxTimerInfo; - -typedef struct { - uint32_t value; - qemu_irq irq; - QEMUTimer *qtimer; - int num; - PXA2xxTimerInfo *info; -} PXA2xxTimer0; - -typedef struct { - PXA2xxTimer0 tm; - int32_t oldclock; - int32_t clock; - uint64_t lastload; - uint32_t freq; - uint32_t control; -} PXA2xxTimer4; - -struct PXA2xxTimerInfo { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t flags; - - int32_t clock; - int32_t oldclock; - uint64_t lastload; - uint32_t freq; - PXA2xxTimer0 timer[4]; - uint32_t events; - uint32_t irq_enabled; - uint32_t reset3; - uint32_t snapshot; - - qemu_irq irq4; - PXA2xxTimer4 tm4[8]; -}; - -#define PXA2XX_TIMER_HAVE_TM4 0 - -static inline int pxa2xx_timer_has_tm4(PXA2xxTimerInfo *s) -{ - return s->flags & (1 << PXA2XX_TIMER_HAVE_TM4); -} - -static void pxa2xx_timer_update(void *opaque, uint64_t now_qemu) -{ - PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; - int i; - uint32_t now_vm; - uint64_t new_qemu; - - now_vm = s->clock + - 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), - NANOSECONDS_PER_SECOND, s->freq); - timer_mod(s->timer[i].qtimer, new_qemu); - } -} - -static void pxa2xx_timer_update4(void *opaque, uint64_t now_qemu, int n) -{ - PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; - uint32_t now_vm; - uint64_t new_qemu; - static const int counters[8] = { 0, 0, 0, 0, 4, 4, 6, 6 }; - int counter; - - if (s->tm4[n].control & (1 << 7)) - counter = n; - else - counter = counters[n]; - - if (!s->tm4[counter].freq) { - timer_del(s->tm4[n].tm.qtimer); - return; - } - - now_vm = s->tm4[counter].clock + muldiv64(now_qemu - - s->tm4[counter].lastload, - s->tm4[counter].freq, NANOSECONDS_PER_SECOND); - - new_qemu = now_qemu + muldiv64((uint32_t) (s->tm4[n].tm.value - now_vm), - NANOSECONDS_PER_SECOND, s->tm4[counter].freq); - timer_mod(s->tm4[n].tm.qtimer, new_qemu); -} - -static uint64_t pxa2xx_timer_read(void *opaque, hwaddr offset, - unsigned size) -{ - PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; - int tm = 0; - - switch (offset) { - case OSMR3: tm ++; - /* fall through */ - case OSMR2: tm ++; - /* fall through */ - case OSMR1: tm ++; - /* fall through */ - case OSMR0: - return s->timer[tm].value; - case OSMR11: tm ++; - /* fall through */ - case OSMR10: tm ++; - /* fall through */ - case OSMR9: tm ++; - /* fall through */ - case OSMR8: tm ++; - /* fall through */ - case OSMR7: tm ++; - /* fall through */ - case OSMR6: tm ++; - /* fall through */ - case OSMR5: tm ++; - /* fall through */ - case OSMR4: - if (!pxa2xx_timer_has_tm4(s)) - goto badreg; - return s->tm4[tm].tm.value; - case OSCR: - return s->clock + muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - - s->lastload, s->freq, NANOSECONDS_PER_SECOND); - case OSCR11: tm ++; - /* fall through */ - case OSCR10: tm ++; - /* fall through */ - case OSCR9: tm ++; - /* fall through */ - case OSCR8: tm ++; - /* fall through */ - case OSCR7: tm ++; - /* fall through */ - case OSCR6: tm ++; - /* fall through */ - case OSCR5: tm ++; - /* fall through */ - case OSCR4: - if (!pxa2xx_timer_has_tm4(s)) - goto badreg; - - if ((tm == 9 - 4 || tm == 11 - 4) && (s->tm4[tm].control & (1 << 9))) { - if (s->tm4[tm - 1].freq) - 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, 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, - NANOSECONDS_PER_SECOND); - case OIER: - return s->irq_enabled; - case OSSR: /* Status register */ - return s->events; - case OWER: - return s->reset3; - case OMCR11: tm ++; - /* fall through */ - case OMCR10: tm ++; - /* fall through */ - case OMCR9: tm ++; - /* fall through */ - case OMCR8: tm ++; - /* fall through */ - case OMCR7: tm ++; - /* fall through */ - case OMCR6: tm ++; - /* fall through */ - case OMCR5: tm ++; - /* fall through */ - case OMCR4: - if (!pxa2xx_timer_has_tm4(s)) - goto badreg; - return s->tm4[tm].control; - case OSNR: - return s->snapshot; - default: - badreg: - hw_error("pxa2xx_timer_read: Bad offset " REG_FMT "\n", offset); - } - - return 0; -} - -static void pxa2xx_timer_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - int i, tm = 0; - PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; - - switch (offset) { - case OSMR3: tm ++; - /* fall through */ - case OSMR2: tm ++; - /* fall through */ - case OSMR1: tm ++; - /* fall through */ - case OSMR0: - s->timer[tm].value = value; - pxa2xx_timer_update(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - break; - case OSMR11: tm ++; - /* fall through */ - case OSMR10: tm ++; - /* fall through */ - case OSMR9: tm ++; - /* fall through */ - case OSMR8: tm ++; - /* fall through */ - case OSMR7: tm ++; - /* fall through */ - case OSMR6: tm ++; - /* fall through */ - case OSMR5: tm ++; - /* fall through */ - case OSMR4: - if (!pxa2xx_timer_has_tm4(s)) - goto badreg; - s->tm4[tm].tm.value = value; - pxa2xx_timer_update4(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tm); - break; - case OSCR: - s->oldclock = s->clock; - s->lastload = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - s->clock = value; - pxa2xx_timer_update(s, s->lastload); - break; - case OSCR11: tm ++; - /* fall through */ - case OSCR10: tm ++; - /* fall through */ - case OSCR9: tm ++; - /* fall through */ - case OSCR8: tm ++; - /* fall through */ - case OSCR7: tm ++; - /* fall through */ - case OSCR6: tm ++; - /* fall through */ - case OSCR5: tm ++; - /* fall through */ - case OSCR4: - if (!pxa2xx_timer_has_tm4(s)) - goto badreg; - s->tm4[tm].oldclock = s->tm4[tm].clock; - s->tm4[tm].lastload = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - s->tm4[tm].clock = value; - pxa2xx_timer_update4(s, s->tm4[tm].lastload, tm); - break; - case OIER: - s->irq_enabled = value & 0xfff; - break; - case OSSR: /* Status register */ - value &= s->events; - s->events &= ~value; - for (i = 0; i < 4; i ++, value >>= 1) - if (value & 1) - qemu_irq_lower(s->timer[i].irq); - if (pxa2xx_timer_has_tm4(s) && !(s->events & 0xff0) && value) - qemu_irq_lower(s->irq4); - break; - case OWER: /* XXX: Reset on OSMR3 match? */ - s->reset3 = value; - break; - case OMCR7: tm ++; - /* fall through */ - case OMCR6: tm ++; - /* fall through */ - case OMCR5: tm ++; - /* fall through */ - case OMCR4: - if (!pxa2xx_timer_has_tm4(s)) - goto badreg; - s->tm4[tm].control = value & 0x0ff; - /* XXX Stop if running (shouldn't happen) */ - if ((value & (1 << 7)) || tm == 0) - s->tm4[tm].freq = pxa2xx_timer4_freq[value & 7]; - else { - s->tm4[tm].freq = 0; - pxa2xx_timer_update4(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tm); - } - break; - case OMCR11: tm ++; - /* fall through */ - case OMCR10: tm ++; - /* fall through */ - case OMCR9: tm ++; - /* fall through */ - case OMCR8: tm += 4; - if (!pxa2xx_timer_has_tm4(s)) - goto badreg; - s->tm4[tm].control = value & 0x3ff; - /* XXX Stop if running (shouldn't happen) */ - if ((value & (1 << 7)) || !(tm & 1)) - s->tm4[tm].freq = - pxa2xx_timer4_freq[(value & (1 << 8)) ? 0 : (value & 7)]; - else { - s->tm4[tm].freq = 0; - pxa2xx_timer_update4(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tm); - } - break; - default: - badreg: - hw_error("pxa2xx_timer_write: Bad offset " REG_FMT "\n", offset); - } -} - -static const MemoryRegionOps pxa2xx_timer_ops = { - .read = pxa2xx_timer_read, - .write = pxa2xx_timer_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pxa2xx_timer_tick(void *opaque) -{ - PXA2xxTimer0 *t = (PXA2xxTimer0 *) opaque; - PXA2xxTimerInfo *i = t->info; - - if (i->irq_enabled & (1 << t->num)) { - i->events |= 1 << t->num; - qemu_irq_raise(t->irq); - } - - if (t->num == 3) - if (i->reset3 & 1) { - i->reset3 = 0; - qemu_system_reset_request(); - } -} - -static void pxa2xx_timer_tick4(void *opaque) -{ - PXA2xxTimer4 *t = (PXA2xxTimer4 *) opaque; - PXA2xxTimerInfo *i = (PXA2xxTimerInfo *) t->tm.info; - - pxa2xx_timer_tick(&t->tm); - if (t->control & (1 << 3)) - t->clock = 0; - if (t->control & (1 << 6)) - pxa2xx_timer_update4(i, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), t->tm.num - 4); - if (i->events & 0xff0) - qemu_irq_raise(i->irq4); -} - -static int pxa25x_timer_post_load(void *opaque, int version_id) -{ - PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque; - int64_t now; - int i; - - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - pxa2xx_timer_update(s, now); - - if (pxa2xx_timer_has_tm4(s)) - for (i = 0; i < 8; i ++) - pxa2xx_timer_update4(s, now, i); - - return 0; -} - -static void pxa2xx_timer_init(Object *obj) -{ - PXA2xxTimerInfo *s = PXA2XX_TIMER(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); - - s->irq_enabled = 0; - s->oldclock = 0; - s->clock = 0; - 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(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]); - } - - if (s->flags & (1 << PXA2XX_TIMER_HAVE_TM4)) { - sysbus_init_irq(sbd, &s->irq4); - - for (i = 0; i < 8; i ++) { - s->tm4[i].tm.value = 0; - s->tm4[i].tm.info = s; - s->tm4[i].tm.num = i + 4; - 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]); - } - } -} - -static const VMStateDescription vmstate_pxa2xx_timer0_regs = { - .name = "pxa2xx_timer0", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32(value, PXA2xxTimer0), - VMSTATE_END_OF_LIST(), - }, -}; - -static const VMStateDescription vmstate_pxa2xx_timer4_regs = { - .name = "pxa2xx_timer4", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(tm, PXA2xxTimer4, 1, - vmstate_pxa2xx_timer0_regs, PXA2xxTimer0), - VMSTATE_INT32(oldclock, PXA2xxTimer4), - VMSTATE_INT32(clock, PXA2xxTimer4), - VMSTATE_UINT64(lastload, PXA2xxTimer4), - VMSTATE_UINT32(freq, PXA2xxTimer4), - VMSTATE_UINT32(control, PXA2xxTimer4), - VMSTATE_END_OF_LIST(), - }, -}; - -static bool pxa2xx_timer_has_tm4_test(void *opaque, int version_id) -{ - return pxa2xx_timer_has_tm4(opaque); -} - -static const VMStateDescription vmstate_pxa2xx_timer_regs = { - .name = "pxa2xx_timer", - .version_id = 1, - .minimum_version_id = 1, - .post_load = pxa25x_timer_post_load, - .fields = (VMStateField[]) { - VMSTATE_INT32(clock, PXA2xxTimerInfo), - VMSTATE_INT32(oldclock, PXA2xxTimerInfo), - VMSTATE_UINT64(lastload, PXA2xxTimerInfo), - VMSTATE_STRUCT_ARRAY(timer, PXA2xxTimerInfo, 4, 1, - vmstate_pxa2xx_timer0_regs, PXA2xxTimer0), - VMSTATE_UINT32(events, PXA2xxTimerInfo), - VMSTATE_UINT32(irq_enabled, PXA2xxTimerInfo), - VMSTATE_UINT32(reset3, PXA2xxTimerInfo), - VMSTATE_UINT32(snapshot, PXA2xxTimerInfo), - VMSTATE_STRUCT_ARRAY_TEST(tm4, PXA2xxTimerInfo, 8, - pxa2xx_timer_has_tm4_test, 0, - vmstate_pxa2xx_timer4_regs, PXA2xxTimer4), - VMSTATE_END_OF_LIST(), - } -}; - -static Property pxa25x_timer_dev_properties[] = { - DEFINE_PROP_UINT32("freq", PXA2xxTimerInfo, freq, PXA25X_FREQ), - DEFINE_PROP_BIT("tm4", PXA2xxTimerInfo, flags, - PXA2XX_TIMER_HAVE_TM4, false), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pxa25x_timer_dev_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->desc = "PXA25x timer"; - dc->props = pxa25x_timer_dev_properties; -} - -static const TypeInfo pxa25x_timer_dev_info = { - .name = "pxa25x-timer", - .parent = TYPE_PXA2XX_TIMER, - .instance_size = sizeof(PXA2xxTimerInfo), - .class_init = pxa25x_timer_dev_class_init, -}; - -static Property pxa27x_timer_dev_properties[] = { - DEFINE_PROP_UINT32("freq", PXA2xxTimerInfo, freq, PXA27X_FREQ), - DEFINE_PROP_BIT("tm4", PXA2xxTimerInfo, flags, - PXA2XX_TIMER_HAVE_TM4, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pxa27x_timer_dev_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->desc = "PXA27x timer"; - dc->props = pxa27x_timer_dev_properties; -} - -static const TypeInfo pxa27x_timer_dev_info = { - .name = "pxa27x-timer", - .parent = TYPE_PXA2XX_TIMER, - .instance_size = sizeof(PXA2xxTimerInfo), - .class_init = pxa27x_timer_dev_class_init, -}; - -static void pxa2xx_timer_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = pxa2xx_timer_realize; - dc->vmsd = &vmstate_pxa2xx_timer_regs; -} - -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, -}; - -static void pxa2xx_timer_register_types(void) -{ - type_register_static(&pxa2xx_timer_type_info); - type_register_static(&pxa25x_timer_dev_info); - type_register_static(&pxa27x_timer_dev_info); -} - -type_init(pxa2xx_timer_register_types) diff --git a/qemu/hw/timer/sh_timer.c b/qemu/hw/timer/sh_timer.c deleted file mode 100644 index 255b2fc91..000000000 --- a/qemu/hw/timer/sh_timer.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - * SuperH Timer modules. - * - * Copyright (c) 2007 Magnus Damm - * Based on arm_timer.c by Paul Brook - * Copyright (c) 2005-2006 CodeSourcery. - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/sh4/sh.h" -#include "qemu/timer.h" -#include "qemu/main-loop.h" -#include "exec/address-spaces.h" -#include "hw/ptimer.h" - -//#define DEBUG_TIMER - -#define TIMER_TCR_TPSC (7 << 0) -#define TIMER_TCR_CKEG (3 << 3) -#define TIMER_TCR_UNIE (1 << 5) -#define TIMER_TCR_ICPE (3 << 6) -#define TIMER_TCR_UNF (1 << 8) -#define TIMER_TCR_ICPF (1 << 9) -#define TIMER_TCR_RESERVED (0x3f << 10) - -#define TIMER_FEAT_CAPT (1 << 0) -#define TIMER_FEAT_EXTCLK (1 << 1) - -#define OFFSET_TCOR 0 -#define OFFSET_TCNT 1 -#define OFFSET_TCR 2 -#define OFFSET_TCPR 3 - -typedef struct { - ptimer_state *timer; - uint32_t tcnt; - uint32_t tcor; - uint32_t tcr; - uint32_t tcpr; - int freq; - int int_level; - int old_level; - int feat; - int enabled; - qemu_irq irq; -} sh_timer_state; - -/* Check all active timers, and schedule the next timer interrupt. */ - -static void sh_timer_update(sh_timer_state *s) -{ - int new_level = s->int_level && (s->tcr & TIMER_TCR_UNIE); - - if (new_level != s->old_level) - qemu_set_irq (s->irq, new_level); - - s->old_level = s->int_level; - s->int_level = new_level; -} - -static uint32_t sh_timer_read(void *opaque, hwaddr offset) -{ - sh_timer_state *s = (sh_timer_state *)opaque; - - switch (offset >> 2) { - case OFFSET_TCOR: - return s->tcor; - case OFFSET_TCNT: - return ptimer_get_count(s->timer); - case OFFSET_TCR: - return s->tcr | (s->int_level ? TIMER_TCR_UNF : 0); - case OFFSET_TCPR: - if (s->feat & TIMER_FEAT_CAPT) - return s->tcpr; - default: - hw_error("sh_timer_read: Bad offset %x\n", (int)offset); - return 0; - } -} - -static void sh_timer_write(void *opaque, hwaddr offset, - uint32_t value) -{ - sh_timer_state *s = (sh_timer_state *)opaque; - int freq; - - switch (offset >> 2) { - case OFFSET_TCOR: - s->tcor = value; - ptimer_set_limit(s->timer, s->tcor, 0); - break; - case OFFSET_TCNT: - s->tcnt = value; - ptimer_set_count(s->timer, s->tcnt); - break; - case OFFSET_TCR: - if (s->enabled) { - /* Pause the timer if it is running. This may cause some - inaccuracy dure to rounding, but avoids a whole lot of other - messyness. */ - ptimer_stop(s->timer); - } - freq = s->freq; - /* ??? Need to recalculate expiry time after changing divisor. */ - switch (value & TIMER_TCR_TPSC) { - case 0: freq >>= 2; break; - case 1: freq >>= 4; break; - case 2: freq >>= 6; break; - case 3: freq >>= 8; break; - case 4: freq >>= 10; break; - case 6: - case 7: if (s->feat & TIMER_FEAT_EXTCLK) break; - default: hw_error("sh_timer_write: Reserved TPSC value\n"); break; - } - switch ((value & TIMER_TCR_CKEG) >> 3) { - case 0: break; - case 1: - case 2: - case 3: if (s->feat & TIMER_FEAT_EXTCLK) break; - default: hw_error("sh_timer_write: Reserved CKEG value\n"); break; - } - switch ((value & TIMER_TCR_ICPE) >> 6) { - case 0: break; - case 2: - case 3: if (s->feat & TIMER_FEAT_CAPT) break; - default: hw_error("sh_timer_write: Reserved ICPE value\n"); break; - } - if ((value & TIMER_TCR_UNF) == 0) - s->int_level = 0; - - value &= ~TIMER_TCR_UNF; - - if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT))) - hw_error("sh_timer_write: Reserved ICPF value\n"); - - value &= ~TIMER_TCR_ICPF; /* capture not supported */ - - if (value & TIMER_TCR_RESERVED) - hw_error("sh_timer_write: Reserved TCR bits set\n"); - s->tcr = value; - ptimer_set_limit(s->timer, s->tcor, 0); - ptimer_set_freq(s->timer, freq); - if (s->enabled) { - /* Restart the timer if still enabled. */ - ptimer_run(s->timer, 0); - } - break; - case OFFSET_TCPR: - if (s->feat & TIMER_FEAT_CAPT) { - s->tcpr = value; - break; - } - default: - hw_error("sh_timer_write: Bad offset %x\n", (int)offset); - } - sh_timer_update(s); -} - -static void sh_timer_start_stop(void *opaque, int enable) -{ - sh_timer_state *s = (sh_timer_state *)opaque; - -#ifdef DEBUG_TIMER - printf("sh_timer_start_stop %d (%d)\n", enable, s->enabled); -#endif - - if (s->enabled && !enable) { - ptimer_stop(s->timer); - } - if (!s->enabled && enable) { - ptimer_run(s->timer, 0); - } - s->enabled = !!enable; - -#ifdef DEBUG_TIMER - printf("sh_timer_start_stop done %d\n", s->enabled); -#endif -} - -static void sh_timer_tick(void *opaque) -{ - sh_timer_state *s = (sh_timer_state *)opaque; - s->int_level = s->enabled; - sh_timer_update(s); -} - -static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq) -{ - sh_timer_state *s; - QEMUBH *bh; - - s = (sh_timer_state *)g_malloc0(sizeof(sh_timer_state)); - s->freq = freq; - s->feat = feat; - s->tcor = 0xffffffff; - s->tcnt = 0xffffffff; - s->tcpr = 0xdeadbeef; - s->tcr = 0; - s->enabled = 0; - s->irq = irq; - - bh = qemu_bh_new(sh_timer_tick, s); - s->timer = ptimer_init(bh); - - sh_timer_write(s, OFFSET_TCOR >> 2, s->tcor); - sh_timer_write(s, OFFSET_TCNT >> 2, s->tcnt); - sh_timer_write(s, OFFSET_TCPR >> 2, s->tcpr); - sh_timer_write(s, OFFSET_TCR >> 2, s->tcpr); - /* ??? Save/restore. */ - return s; -} - -typedef struct { - MemoryRegion iomem; - MemoryRegion iomem_p4; - MemoryRegion iomem_a7; - void *timer[3]; - int level[3]; - uint32_t tocr; - uint32_t tstr; - int feat; -} tmu012_state; - -static uint64_t tmu012_read(void *opaque, hwaddr offset, - unsigned size) -{ - tmu012_state *s = (tmu012_state *)opaque; - -#ifdef DEBUG_TIMER - printf("tmu012_read 0x%lx\n", (unsigned long) offset); -#endif - - if (offset >= 0x20) { - if (!(s->feat & TMU012_FEAT_3CHAN)) - hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); - return sh_timer_read(s->timer[2], offset - 0x20); - } - - if (offset >= 0x14) - return sh_timer_read(s->timer[1], offset - 0x14); - - if (offset >= 0x08) - return sh_timer_read(s->timer[0], offset - 0x08); - - if (offset == 4) - return s->tstr; - - if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) - return s->tocr; - - hw_error("tmu012_write: Bad offset %x\n", (int)offset); - return 0; -} - -static void tmu012_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - tmu012_state *s = (tmu012_state *)opaque; - -#ifdef DEBUG_TIMER - printf("tmu012_write 0x%lx 0x%08x\n", (unsigned long) offset, value); -#endif - - if (offset >= 0x20) { - if (!(s->feat & TMU012_FEAT_3CHAN)) - hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); - sh_timer_write(s->timer[2], offset - 0x20, value); - return; - } - - if (offset >= 0x14) { - sh_timer_write(s->timer[1], offset - 0x14, value); - return; - } - - if (offset >= 0x08) { - sh_timer_write(s->timer[0], offset - 0x08, value); - return; - } - - if (offset == 4) { - sh_timer_start_stop(s->timer[0], value & (1 << 0)); - sh_timer_start_stop(s->timer[1], value & (1 << 1)); - if (s->feat & TMU012_FEAT_3CHAN) - sh_timer_start_stop(s->timer[2], value & (1 << 2)); - else - if (value & (1 << 2)) - hw_error("tmu012_write: Bad channel\n"); - - s->tstr = value; - return; - } - - if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) { - s->tocr = value & (1 << 0); - } -} - -static const MemoryRegionOps tmu012_ops = { - .read = tmu012_read, - .write = tmu012_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -void tmu012_init(MemoryRegion *sysmem, hwaddr base, - int feat, uint32_t freq, - qemu_irq ch0_irq, qemu_irq ch1_irq, - qemu_irq ch2_irq0, qemu_irq ch2_irq1) -{ - tmu012_state *s; - int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0; - - s = (tmu012_state *)g_malloc0(sizeof(tmu012_state)); - s->feat = feat; - s->timer[0] = sh_timer_init(freq, timer_feat, ch0_irq); - s->timer[1] = sh_timer_init(freq, timer_feat, ch1_irq); - if (feat & TMU012_FEAT_3CHAN) - s->timer[2] = sh_timer_init(freq, timer_feat | TIMER_FEAT_CAPT, - ch2_irq0); /* ch2_irq1 not supported */ - - memory_region_init_io(&s->iomem, NULL, &tmu012_ops, s, - "timer", 0x100000000ULL); - - memory_region_init_alias(&s->iomem_p4, NULL, "timer-p4", - &s->iomem, 0, 0x1000); - memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4); - - memory_region_init_alias(&s->iomem_a7, NULL, "timer-a7", - &s->iomem, 0, 0x1000); - memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7); - /* ??? Save/restore. */ -} diff --git a/qemu/hw/timer/slavio_timer.c b/qemu/hw/timer/slavio_timer.c deleted file mode 100644 index fb3e08bed..000000000 --- a/qemu/hw/timer/slavio_timer.c +++ /dev/null @@ -1,435 +0,0 @@ -/* - * QEMU Sparc SLAVIO timer controller emulation - * - * Copyright (c) 2003-2005 Fabrice Bellard - * - * 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/sparc/sun4m.h" -#include "qemu/timer.h" -#include "hw/ptimer.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "qemu/main-loop.h" - -/* - * Registers of hardware timer in sun4m. - * - * This is the timer/counter part of chip STP2001 (Slave I/O), also - * produced as NCR89C105. See - * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt - * - * The 31-bit counter is incremented every 500ns by bit 9. Bits 8..0 - * are zero. Bit 31 is 1 when count has been reached. - * - * Per-CPU timers interrupt local CPU, system timer uses normal - * interrupt routing. - * - */ - -#define MAX_CPUS 16 - -typedef struct CPUTimerState { - qemu_irq irq; - ptimer_state *timer; - uint32_t count, counthigh, reached; - /* processor only */ - uint32_t run; - uint64_t limit; -} CPUTimerState; - -#define TYPE_SLAVIO_TIMER "slavio_timer" -#define SLAVIO_TIMER(obj) \ - OBJECT_CHECK(SLAVIO_TIMERState, (obj), TYPE_SLAVIO_TIMER) - -typedef struct SLAVIO_TIMERState { - SysBusDevice parent_obj; - - uint32_t num_cpus; - uint32_t cputimer_mode; - CPUTimerState cputimer[MAX_CPUS + 1]; -} SLAVIO_TIMERState; - -typedef struct TimerContext { - MemoryRegion iomem; - SLAVIO_TIMERState *s; - unsigned int timer_index; /* 0 for system, 1 ... MAX_CPUS for CPU timers */ -} TimerContext; - -#define SYS_TIMER_SIZE 0x14 -#define CPU_TIMER_SIZE 0x10 - -#define TIMER_LIMIT 0 -#define TIMER_COUNTER 1 -#define TIMER_COUNTER_NORST 2 -#define TIMER_STATUS 3 -#define TIMER_MODE 4 - -#define TIMER_COUNT_MASK32 0xfffffe00 -#define TIMER_LIMIT_MASK32 0x7fffffff -#define TIMER_MAX_COUNT64 0x7ffffffffffffe00ULL -#define TIMER_MAX_COUNT32 0x7ffffe00ULL -#define TIMER_REACHED 0x80000000 -#define TIMER_PERIOD 500ULL // 500ns -#define LIMIT_TO_PERIODS(l) (((l) >> 9) - 1) -#define PERIODS_TO_LIMIT(l) (((l) + 1) << 9) - -static int slavio_timer_is_user(TimerContext *tc) -{ - SLAVIO_TIMERState *s = tc->s; - unsigned int timer_index = tc->timer_index; - - return timer_index != 0 && (s->cputimer_mode & (1 << (timer_index - 1))); -} - -// Update count, set irq, update expire_time -// Convert from ptimer countdown units -static void slavio_timer_get_out(CPUTimerState *t) -{ - uint64_t count, limit; - - if (t->limit == 0) { /* free-run system or processor counter */ - limit = TIMER_MAX_COUNT32; - } else { - limit = t->limit; - } - count = limit - PERIODS_TO_LIMIT(ptimer_get_count(t->timer)); - - trace_slavio_timer_get_out(t->limit, t->counthigh, t->count); - t->count = count & TIMER_COUNT_MASK32; - t->counthigh = count >> 32; -} - -// timer callback -static void slavio_timer_irq(void *opaque) -{ - TimerContext *tc = opaque; - SLAVIO_TIMERState *s = tc->s; - CPUTimerState *t = &s->cputimer[tc->timer_index]; - - slavio_timer_get_out(t); - trace_slavio_timer_irq(t->counthigh, t->count); - /* if limit is 0 (free-run), there will be no match */ - if (t->limit != 0) { - t->reached = TIMER_REACHED; - } - /* there is no interrupt if user timer or free-run */ - if (!slavio_timer_is_user(tc) && t->limit != 0) { - qemu_irq_raise(t->irq); - } -} - -static uint64_t slavio_timer_mem_readl(void *opaque, hwaddr addr, - unsigned size) -{ - TimerContext *tc = opaque; - SLAVIO_TIMERState *s = tc->s; - uint32_t saddr, ret; - unsigned int timer_index = tc->timer_index; - CPUTimerState *t = &s->cputimer[timer_index]; - - saddr = addr >> 2; - switch (saddr) { - case TIMER_LIMIT: - // read limit (system counter mode) or read most signifying - // part of counter (user mode) - if (slavio_timer_is_user(tc)) { - // read user timer MSW - slavio_timer_get_out(t); - ret = t->counthigh | t->reached; - } else { - // read limit - // clear irq - qemu_irq_lower(t->irq); - t->reached = 0; - ret = t->limit & TIMER_LIMIT_MASK32; - } - break; - case TIMER_COUNTER: - // read counter and reached bit (system mode) or read lsbits - // of counter (user mode) - slavio_timer_get_out(t); - if (slavio_timer_is_user(tc)) { // read user timer LSW - ret = t->count & TIMER_MAX_COUNT64; - } else { // read limit - ret = (t->count & TIMER_MAX_COUNT32) | - t->reached; - } - break; - case TIMER_STATUS: - // only available in processor counter/timer - // read start/stop status - if (timer_index > 0) { - ret = t->run; - } else { - ret = 0; - } - break; - case TIMER_MODE: - // only available in system counter - // read user/system mode - ret = s->cputimer_mode; - break; - default: - trace_slavio_timer_mem_readl_invalid(addr); - ret = 0; - break; - } - trace_slavio_timer_mem_readl(addr, ret); - return ret; -} - -static void slavio_timer_mem_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - TimerContext *tc = opaque; - SLAVIO_TIMERState *s = tc->s; - uint32_t saddr; - unsigned int timer_index = tc->timer_index; - CPUTimerState *t = &s->cputimer[timer_index]; - - trace_slavio_timer_mem_writel(addr, val); - saddr = addr >> 2; - switch (saddr) { - case TIMER_LIMIT: - if (slavio_timer_is_user(tc)) { - uint64_t count; - - // set user counter MSW, reset counter - t->limit = TIMER_MAX_COUNT64; - t->counthigh = val & (TIMER_MAX_COUNT64 >> 32); - t->reached = 0; - count = ((uint64_t)t->counthigh << 32) | t->count; - trace_slavio_timer_mem_writel_limit(timer_index, count); - ptimer_set_count(t->timer, LIMIT_TO_PERIODS(t->limit - count)); - } else { - // set limit, reset counter - qemu_irq_lower(t->irq); - t->limit = val & TIMER_MAX_COUNT32; - if (t->timer) { - if (t->limit == 0) { /* free-run */ - ptimer_set_limit(t->timer, - LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1); - } else { - ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(t->limit), 1); - } - } - } - break; - case TIMER_COUNTER: - if (slavio_timer_is_user(tc)) { - uint64_t count; - - // set user counter LSW, reset counter - t->limit = TIMER_MAX_COUNT64; - t->count = val & TIMER_MAX_COUNT64; - t->reached = 0; - count = ((uint64_t)t->counthigh) << 32 | t->count; - trace_slavio_timer_mem_writel_limit(timer_index, count); - ptimer_set_count(t->timer, LIMIT_TO_PERIODS(t->limit - count)); - } else { - trace_slavio_timer_mem_writel_counter_invalid(); - } - break; - case TIMER_COUNTER_NORST: - // set limit without resetting counter - t->limit = val & TIMER_MAX_COUNT32; - if (t->limit == 0) { /* free-run */ - ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 0); - } else { - ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(t->limit), 0); - } - break; - case TIMER_STATUS: - if (slavio_timer_is_user(tc)) { - // start/stop user counter - if (val & 1) { - trace_slavio_timer_mem_writel_status_start(timer_index); - ptimer_run(t->timer, 0); - } else { - trace_slavio_timer_mem_writel_status_stop(timer_index); - ptimer_stop(t->timer); - } - } - t->run = val & 1; - break; - case TIMER_MODE: - if (timer_index == 0) { - unsigned int i; - - for (i = 0; i < s->num_cpus; i++) { - unsigned int processor = 1 << i; - CPUTimerState *curr_timer = &s->cputimer[i + 1]; - - // check for a change in timer mode for this processor - if ((val & processor) != (s->cputimer_mode & processor)) { - if (val & processor) { // counter -> user timer - qemu_irq_lower(curr_timer->irq); - // counters are always running - if (!curr_timer->run) { - ptimer_stop(curr_timer->timer); - } - // user timer limit is always the same - curr_timer->limit = TIMER_MAX_COUNT64; - ptimer_set_limit(curr_timer->timer, - LIMIT_TO_PERIODS(curr_timer->limit), - 1); - // set this processors user timer bit in config - // register - s->cputimer_mode |= processor; - trace_slavio_timer_mem_writel_mode_user(timer_index); - } else { // user timer -> counter - // start the counter - ptimer_run(curr_timer->timer, 0); - // clear this processors user timer bit in config - // register - s->cputimer_mode &= ~processor; - trace_slavio_timer_mem_writel_mode_counter(timer_index); - } - } - } - } else { - trace_slavio_timer_mem_writel_mode_invalid(); - } - break; - default: - trace_slavio_timer_mem_writel_invalid(addr); - break; - } -} - -static const MemoryRegionOps slavio_timer_mem_ops = { - .read = slavio_timer_mem_readl, - .write = slavio_timer_mem_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static const VMStateDescription vmstate_timer = { - .name ="timer", - .version_id = 3, - .minimum_version_id = 3, - .fields = (VMStateField[]) { - VMSTATE_UINT64(limit, CPUTimerState), - VMSTATE_UINT32(count, CPUTimerState), - VMSTATE_UINT32(counthigh, CPUTimerState), - VMSTATE_UINT32(reached, CPUTimerState), - VMSTATE_UINT32(run , CPUTimerState), - VMSTATE_PTIMER(timer, CPUTimerState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_slavio_timer = { - .name ="slavio_timer", - .version_id = 3, - .minimum_version_id = 3, - .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(cputimer, SLAVIO_TIMERState, MAX_CPUS + 1, 3, - vmstate_timer, CPUTimerState), - VMSTATE_END_OF_LIST() - } -}; - -static void slavio_timer_reset(DeviceState *d) -{ - SLAVIO_TIMERState *s = SLAVIO_TIMER(d); - unsigned int i; - CPUTimerState *curr_timer; - - for (i = 0; i <= MAX_CPUS; i++) { - curr_timer = &s->cputimer[i]; - curr_timer->limit = 0; - curr_timer->count = 0; - curr_timer->reached = 0; - if (i <= s->num_cpus) { - ptimer_set_limit(curr_timer->timer, - LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1); - ptimer_run(curr_timer->timer, 0); - curr_timer->run = 1; - } - } - s->cputimer_mode = 0; -} - -static int slavio_timer_init1(SysBusDevice *dev) -{ - SLAVIO_TIMERState *s = SLAVIO_TIMER(dev); - QEMUBH *bh; - unsigned int i; - TimerContext *tc; - - for (i = 0; i <= MAX_CPUS; i++) { - uint64_t size; - char timer_name[20]; - - tc = g_malloc0(sizeof(TimerContext)); - tc->s = s; - tc->timer_index = i; - - bh = qemu_bh_new(slavio_timer_irq, tc); - s->cputimer[i].timer = ptimer_init(bh); - ptimer_set_period(s->cputimer[i].timer, TIMER_PERIOD); - - size = i == 0 ? SYS_TIMER_SIZE : CPU_TIMER_SIZE; - snprintf(timer_name, sizeof(timer_name), "timer-%i", i); - memory_region_init_io(&tc->iomem, OBJECT(s), &slavio_timer_mem_ops, tc, - timer_name, size); - sysbus_init_mmio(dev, &tc->iomem); - - sysbus_init_irq(dev, &s->cputimer[i].irq); - } - - return 0; -} - -static Property slavio_timer_properties[] = { - DEFINE_PROP_UINT32("num_cpus", SLAVIO_TIMERState, num_cpus, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void slavio_timer_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = slavio_timer_init1; - dc->reset = slavio_timer_reset; - dc->vmsd = &vmstate_slavio_timer; - dc->props = slavio_timer_properties; -} - -static const TypeInfo slavio_timer_info = { - .name = TYPE_SLAVIO_TIMER, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SLAVIO_TIMERState), - .class_init = slavio_timer_class_init, -}; - -static void slavio_timer_register_types(void) -{ - type_register_static(&slavio_timer_info); -} - -type_init(slavio_timer_register_types) diff --git a/qemu/hw/timer/stm32f2xx_timer.c b/qemu/hw/timer/stm32f2xx_timer.c deleted file mode 100644 index 55dacbbe3..000000000 --- a/qemu/hw/timer/stm32f2xx_timer.c +++ /dev/null @@ -1,329 +0,0 @@ -/* - * STM32F2XX Timer - * - * Copyright (c) 2014 Alistair Francis - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/timer/stm32f2xx_timer.h" - -#ifndef STM_TIMER_ERR_DEBUG -#define STM_TIMER_ERR_DEBUG 0 -#endif - -#define DB_PRINT_L(lvl, fmt, args...) do { \ - if (STM_TIMER_ERR_DEBUG >= lvl) { \ - qemu_log("%s: " fmt, __func__, ## args); \ - } \ -} while (0); - -#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) - -static void stm32f2xx_timer_set_alarm(STM32F2XXTimerState *s, int64_t now); - -static void stm32f2xx_timer_interrupt(void *opaque) -{ - STM32F2XXTimerState *s = opaque; - - DB_PRINT("Interrupt\n"); - - if (s->tim_dier & TIM_DIER_UIE && s->tim_cr1 & TIM_CR1_CEN) { - s->tim_sr |= 1; - qemu_irq_pulse(s->irq); - stm32f2xx_timer_set_alarm(s, s->hit_time); - } -} - -static inline int64_t stm32f2xx_ns_to_ticks(STM32F2XXTimerState *s, int64_t t) -{ - return muldiv64(t, s->freq_hz, 1000000000ULL) / (s->tim_psc + 1); -} - -static void stm32f2xx_timer_set_alarm(STM32F2XXTimerState *s, int64_t now) -{ - uint64_t ticks; - int64_t now_ticks; - - if (s->tim_arr == 0) { - return; - } - - DB_PRINT("Alarm set at: 0x%x\n", s->tim_cr1); - - now_ticks = stm32f2xx_ns_to_ticks(s, now); - ticks = s->tim_arr - (now_ticks - s->tick_offset); - - DB_PRINT("Alarm set in %d ticks\n", (int) ticks); - - s->hit_time = muldiv64((ticks + (uint64_t) now_ticks) * (s->tim_psc + 1), - 1000000000ULL, s->freq_hz); - - timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->hit_time); - DB_PRINT("Wait Time: %" PRId64 " ticks\n", s->hit_time); -} - -static void stm32f2xx_timer_reset(DeviceState *dev) -{ - STM32F2XXTimerState *s = STM32F2XXTIMER(dev); - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - s->tim_cr1 = 0; - s->tim_cr2 = 0; - s->tim_smcr = 0; - s->tim_dier = 0; - s->tim_sr = 0; - s->tim_egr = 0; - s->tim_ccmr1 = 0; - s->tim_ccmr2 = 0; - s->tim_ccer = 0; - s->tim_psc = 0; - s->tim_arr = 0; - s->tim_ccr1 = 0; - s->tim_ccr2 = 0; - s->tim_ccr3 = 0; - s->tim_ccr4 = 0; - s->tim_dcr = 0; - s->tim_dmar = 0; - s->tim_or = 0; - - s->tick_offset = stm32f2xx_ns_to_ticks(s, now); -} - -static uint64_t stm32f2xx_timer_read(void *opaque, hwaddr offset, - unsigned size) -{ - STM32F2XXTimerState *s = opaque; - - DB_PRINT("Read 0x%"HWADDR_PRIx"\n", offset); - - switch (offset) { - case TIM_CR1: - return s->tim_cr1; - case TIM_CR2: - return s->tim_cr2; - case TIM_SMCR: - return s->tim_smcr; - case TIM_DIER: - return s->tim_dier; - case TIM_SR: - return s->tim_sr; - case TIM_EGR: - return s->tim_egr; - case TIM_CCMR1: - return s->tim_ccmr1; - case TIM_CCMR2: - return s->tim_ccmr2; - case TIM_CCER: - return s->tim_ccer; - case TIM_CNT: - return stm32f2xx_ns_to_ticks(s, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) - - s->tick_offset; - case TIM_PSC: - return s->tim_psc; - case TIM_ARR: - return s->tim_arr; - case TIM_CCR1: - return s->tim_ccr1; - case TIM_CCR2: - return s->tim_ccr2; - case TIM_CCR3: - return s->tim_ccr3; - case TIM_CCR4: - return s->tim_ccr4; - case TIM_DCR: - return s->tim_dcr; - case TIM_DMAR: - return s->tim_dmar; - case TIM_OR: - return s->tim_or; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, offset); - } - - return 0; -} - -static void stm32f2xx_timer_write(void *opaque, hwaddr offset, - uint64_t val64, unsigned size) -{ - STM32F2XXTimerState *s = opaque; - uint32_t value = val64; - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - uint32_t timer_val = 0; - - DB_PRINT("Write 0x%x, 0x%"HWADDR_PRIx"\n", value, offset); - - switch (offset) { - case TIM_CR1: - s->tim_cr1 = value; - return; - case TIM_CR2: - s->tim_cr2 = value; - return; - case TIM_SMCR: - s->tim_smcr = value; - return; - case TIM_DIER: - s->tim_dier = value; - return; - case TIM_SR: - /* This is set by hardware and cleared by software */ - s->tim_sr &= value; - return; - case TIM_EGR: - s->tim_egr = value; - if (s->tim_egr & TIM_EGR_UG) { - timer_val = 0; - break; - } - return; - case TIM_CCMR1: - s->tim_ccmr1 = value; - return; - case TIM_CCMR2: - s->tim_ccmr2 = value; - return; - case TIM_CCER: - s->tim_ccer = value; - return; - case TIM_PSC: - timer_val = stm32f2xx_ns_to_ticks(s, now) - s->tick_offset; - s->tim_psc = value; - value = timer_val; - break; - case TIM_CNT: - timer_val = value; - break; - case TIM_ARR: - s->tim_arr = value; - stm32f2xx_timer_set_alarm(s, now); - return; - case TIM_CCR1: - s->tim_ccr1 = value; - return; - case TIM_CCR2: - s->tim_ccr2 = value; - return; - case TIM_CCR3: - s->tim_ccr3 = value; - return; - case TIM_CCR4: - s->tim_ccr4 = value; - return; - case TIM_DCR: - s->tim_dcr = value; - return; - case TIM_DMAR: - s->tim_dmar = value; - return; - case TIM_OR: - s->tim_or = value; - return; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, offset); - return; - } - - /* This means that a register write has affected the timer in a way that - * requires a refresh of both tick_offset and the alarm. - */ - s->tick_offset = stm32f2xx_ns_to_ticks(s, now) - timer_val; - stm32f2xx_timer_set_alarm(s, now); -} - -static const MemoryRegionOps stm32f2xx_timer_ops = { - .read = stm32f2xx_timer_read, - .write = stm32f2xx_timer_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_stm32f2xx_timer = { - .name = TYPE_STM32F2XX_TIMER, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT64(tick_offset, STM32F2XXTimerState), - VMSTATE_UINT32(tim_cr1, STM32F2XXTimerState), - VMSTATE_UINT32(tim_cr2, STM32F2XXTimerState), - VMSTATE_UINT32(tim_smcr, STM32F2XXTimerState), - VMSTATE_UINT32(tim_dier, STM32F2XXTimerState), - VMSTATE_UINT32(tim_sr, STM32F2XXTimerState), - VMSTATE_UINT32(tim_egr, STM32F2XXTimerState), - VMSTATE_UINT32(tim_ccmr1, STM32F2XXTimerState), - VMSTATE_UINT32(tim_ccmr2, STM32F2XXTimerState), - VMSTATE_UINT32(tim_ccer, STM32F2XXTimerState), - VMSTATE_UINT32(tim_psc, STM32F2XXTimerState), - VMSTATE_UINT32(tim_arr, STM32F2XXTimerState), - VMSTATE_UINT32(tim_ccr1, STM32F2XXTimerState), - VMSTATE_UINT32(tim_ccr2, STM32F2XXTimerState), - VMSTATE_UINT32(tim_ccr3, STM32F2XXTimerState), - VMSTATE_UINT32(tim_ccr4, STM32F2XXTimerState), - VMSTATE_UINT32(tim_dcr, STM32F2XXTimerState), - VMSTATE_UINT32(tim_dmar, STM32F2XXTimerState), - VMSTATE_UINT32(tim_or, STM32F2XXTimerState), - VMSTATE_END_OF_LIST() - } -}; - -static Property stm32f2xx_timer_properties[] = { - DEFINE_PROP_UINT64("clock-frequency", struct STM32F2XXTimerState, - freq_hz, 1000000000), - DEFINE_PROP_END_OF_LIST(), -}; - -static void stm32f2xx_timer_init(Object *obj) -{ - STM32F2XXTimerState *s = STM32F2XXTIMER(obj); - - sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); - - memory_region_init_io(&s->iomem, obj, &stm32f2xx_timer_ops, s, - "stm32f2xx_timer", 0x4000); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); - - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, stm32f2xx_timer_interrupt, s); -} - -static void stm32f2xx_timer_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = stm32f2xx_timer_reset; - dc->props = stm32f2xx_timer_properties; - dc->vmsd = &vmstate_stm32f2xx_timer; -} - -static const TypeInfo stm32f2xx_timer_info = { - .name = TYPE_STM32F2XX_TIMER, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(STM32F2XXTimerState), - .instance_init = stm32f2xx_timer_init, - .class_init = stm32f2xx_timer_class_init, -}; - -static void stm32f2xx_timer_register_types(void) -{ - type_register_static(&stm32f2xx_timer_info); -} - -type_init(stm32f2xx_timer_register_types) diff --git a/qemu/hw/timer/twl92230.c b/qemu/hw/timer/twl92230.c deleted file mode 100644 index 7ba4e9a7c..000000000 --- a/qemu/hw/timer/twl92230.c +++ /dev/null @@ -1,889 +0,0 @@ -/* - * TI TWL92230C energy-management companion device for the OMAP24xx. - * Aka. Menelaus (N4200 MENELAUS1_V2.2) - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#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 - -#define TYPE_TWL92230 "twl92230" -#define TWL92230(obj) OBJECT_CHECK(MenelausState, (obj), TYPE_TWL92230) - -typedef struct MenelausState { - I2CSlave parent_obj; - - int firstbyte; - uint8_t reg; - - uint8_t vcore[5]; - uint8_t dcdc[3]; - uint8_t ldo[8]; - uint8_t sleep[2]; - uint8_t osc; - uint8_t detect; - uint16_t mask; - uint16_t status; - uint8_t dir; - uint8_t inputs; - uint8_t outputs; - uint8_t bbsms; - uint8_t pull[4]; - uint8_t mmc_ctrl[3]; - uint8_t mmc_debounce; - struct { - uint8_t ctrl; - uint16_t comp; - QEMUTimer *hz_tm; - int64_t next; - struct tm tm; - struct tm new; - struct tm alm; - int sec_offset; - int alm_sec; - int next_comp; - } rtc; - uint16_t rtc_next_vmstate; - qemu_irq out[4]; - uint8_t pwrbtn_state; -} MenelausState; - -static inline void menelaus_update(MenelausState *s) -{ - qemu_set_irq(s->out[3], s->status & ~s->mask); -} - -static inline void menelaus_rtc_start(MenelausState *s) -{ - s->rtc.next += qemu_clock_get_ms(rtc_clock); - timer_mod(s->rtc.hz_tm, s->rtc.next); -} - -static inline void menelaus_rtc_stop(MenelausState *s) -{ - timer_del(s->rtc.hz_tm); - s->rtc.next -= qemu_clock_get_ms(rtc_clock); - if (s->rtc.next < 1) - s->rtc.next = 1; -} - -static void menelaus_rtc_update(MenelausState *s) -{ - qemu_get_timedate(&s->rtc.tm, s->rtc.sec_offset); -} - -static void menelaus_alm_update(MenelausState *s) -{ - if ((s->rtc.ctrl & 3) == 3) - s->rtc.alm_sec = qemu_timedate_diff(&s->rtc.alm) - s->rtc.sec_offset; -} - -static void menelaus_rtc_hz(void *opaque) -{ - MenelausState *s = (MenelausState *) opaque; - - s->rtc.next_comp --; - s->rtc.alm_sec --; - s->rtc.next += 1000; - timer_mod(s->rtc.hz_tm, s->rtc.next); - if ((s->rtc.ctrl >> 3) & 3) { /* EVERY */ - menelaus_rtc_update(s); - if (((s->rtc.ctrl >> 3) & 3) == 1 && !s->rtc.tm.tm_sec) - s->status |= 1 << 8; /* RTCTMR */ - else if (((s->rtc.ctrl >> 3) & 3) == 2 && !s->rtc.tm.tm_min) - s->status |= 1 << 8; /* RTCTMR */ - else if (!s->rtc.tm.tm_hour) - s->status |= 1 << 8; /* RTCTMR */ - } else - s->status |= 1 << 8; /* RTCTMR */ - if ((s->rtc.ctrl >> 1) & 1) { /* RTC_AL_EN */ - if (s->rtc.alm_sec == 0) - s->status |= 1 << 9; /* RTCALM */ - /* TODO: wake-up */ - } - if (s->rtc.next_comp <= 0) { - s->rtc.next -= muldiv64((int16_t) s->rtc.comp, 1000, 0x8000); - s->rtc.next_comp = 3600; - } - menelaus_update(s); -} - -static void menelaus_reset(I2CSlave *i2c) -{ - MenelausState *s = TWL92230(i2c); - - s->reg = 0x00; - - s->vcore[0] = 0x0c; /* XXX: X-loader needs 0x8c? check! */ - s->vcore[1] = 0x05; - s->vcore[2] = 0x02; - s->vcore[3] = 0x0c; - s->vcore[4] = 0x03; - s->dcdc[0] = 0x33; /* Depends on wiring */ - s->dcdc[1] = 0x03; - s->dcdc[2] = 0x00; - s->ldo[0] = 0x95; - s->ldo[1] = 0x7e; - s->ldo[2] = 0x00; - s->ldo[3] = 0x00; /* Depends on wiring */ - s->ldo[4] = 0x03; /* Depends on wiring */ - s->ldo[5] = 0x00; - s->ldo[6] = 0x00; - s->ldo[7] = 0x00; - s->sleep[0] = 0x00; - s->sleep[1] = 0x00; - s->osc = 0x01; - s->detect = 0x09; - s->mask = 0x0fff; - s->status = 0; - s->dir = 0x07; - s->outputs = 0x00; - s->bbsms = 0x00; - s->pull[0] = 0x00; - s->pull[1] = 0x00; - s->pull[2] = 0x00; - s->pull[3] = 0x00; - s->mmc_ctrl[0] = 0x03; - s->mmc_ctrl[1] = 0xc0; - s->mmc_ctrl[2] = 0x00; - s->mmc_debounce = 0x05; - - if (s->rtc.ctrl & 1) - menelaus_rtc_stop(s); - s->rtc.ctrl = 0x00; - s->rtc.comp = 0x0000; - s->rtc.next = 1000; - s->rtc.sec_offset = 0; - s->rtc.next_comp = 1800; - s->rtc.alm_sec = 1800; - s->rtc.alm.tm_sec = 0x00; - s->rtc.alm.tm_min = 0x00; - s->rtc.alm.tm_hour = 0x00; - s->rtc.alm.tm_mday = 0x01; - s->rtc.alm.tm_mon = 0x00; - s->rtc.alm.tm_year = 2004; - menelaus_update(s); -} - -static void menelaus_gpio_set(void *opaque, int line, int level) -{ - MenelausState *s = (MenelausState *) opaque; - - if (line < 3) { - /* No interrupt generated */ - s->inputs &= ~(1 << line); - s->inputs |= level << line; - return; - } - - if (!s->pwrbtn_state && level) { - s->status |= 1 << 11; /* PSHBTN */ - menelaus_update(s); - } - s->pwrbtn_state = level; -} - -#define MENELAUS_REV 0x01 -#define MENELAUS_VCORE_CTRL1 0x02 -#define MENELAUS_VCORE_CTRL2 0x03 -#define MENELAUS_VCORE_CTRL3 0x04 -#define MENELAUS_VCORE_CTRL4 0x05 -#define MENELAUS_VCORE_CTRL5 0x06 -#define MENELAUS_DCDC_CTRL1 0x07 -#define MENELAUS_DCDC_CTRL2 0x08 -#define MENELAUS_DCDC_CTRL3 0x09 -#define MENELAUS_LDO_CTRL1 0x0a -#define MENELAUS_LDO_CTRL2 0x0b -#define MENELAUS_LDO_CTRL3 0x0c -#define MENELAUS_LDO_CTRL4 0x0d -#define MENELAUS_LDO_CTRL5 0x0e -#define MENELAUS_LDO_CTRL6 0x0f -#define MENELAUS_LDO_CTRL7 0x10 -#define MENELAUS_LDO_CTRL8 0x11 -#define MENELAUS_SLEEP_CTRL1 0x12 -#define MENELAUS_SLEEP_CTRL2 0x13 -#define MENELAUS_DEVICE_OFF 0x14 -#define MENELAUS_OSC_CTRL 0x15 -#define MENELAUS_DETECT_CTRL 0x16 -#define MENELAUS_INT_MASK1 0x17 -#define MENELAUS_INT_MASK2 0x18 -#define MENELAUS_INT_STATUS1 0x19 -#define MENELAUS_INT_STATUS2 0x1a -#define MENELAUS_INT_ACK1 0x1b -#define MENELAUS_INT_ACK2 0x1c -#define MENELAUS_GPIO_CTRL 0x1d -#define MENELAUS_GPIO_IN 0x1e -#define MENELAUS_GPIO_OUT 0x1f -#define MENELAUS_BBSMS 0x20 -#define MENELAUS_RTC_CTRL 0x21 -#define MENELAUS_RTC_UPDATE 0x22 -#define MENELAUS_RTC_SEC 0x23 -#define MENELAUS_RTC_MIN 0x24 -#define MENELAUS_RTC_HR 0x25 -#define MENELAUS_RTC_DAY 0x26 -#define MENELAUS_RTC_MON 0x27 -#define MENELAUS_RTC_YR 0x28 -#define MENELAUS_RTC_WKDAY 0x29 -#define MENELAUS_RTC_AL_SEC 0x2a -#define MENELAUS_RTC_AL_MIN 0x2b -#define MENELAUS_RTC_AL_HR 0x2c -#define MENELAUS_RTC_AL_DAY 0x2d -#define MENELAUS_RTC_AL_MON 0x2e -#define MENELAUS_RTC_AL_YR 0x2f -#define MENELAUS_RTC_COMP_MSB 0x30 -#define MENELAUS_RTC_COMP_LSB 0x31 -#define MENELAUS_S1_PULL_EN 0x32 -#define MENELAUS_S1_PULL_DIR 0x33 -#define MENELAUS_S2_PULL_EN 0x34 -#define MENELAUS_S2_PULL_DIR 0x35 -#define MENELAUS_MCT_CTRL1 0x36 -#define MENELAUS_MCT_CTRL2 0x37 -#define MENELAUS_MCT_CTRL3 0x38 -#define MENELAUS_MCT_PIN_ST 0x39 -#define MENELAUS_DEBOUNCE1 0x3a - -static uint8_t menelaus_read(void *opaque, uint8_t addr) -{ - MenelausState *s = (MenelausState *) opaque; - int reg = 0; - - switch (addr) { - case MENELAUS_REV: - return 0x22; - - case MENELAUS_VCORE_CTRL5: reg ++; - case MENELAUS_VCORE_CTRL4: reg ++; - case MENELAUS_VCORE_CTRL3: reg ++; - case MENELAUS_VCORE_CTRL2: reg ++; - case MENELAUS_VCORE_CTRL1: - return s->vcore[reg]; - - case MENELAUS_DCDC_CTRL3: reg ++; - case MENELAUS_DCDC_CTRL2: reg ++; - case MENELAUS_DCDC_CTRL1: - return s->dcdc[reg]; - - case MENELAUS_LDO_CTRL8: reg ++; - case MENELAUS_LDO_CTRL7: reg ++; - case MENELAUS_LDO_CTRL6: reg ++; - case MENELAUS_LDO_CTRL5: reg ++; - case MENELAUS_LDO_CTRL4: reg ++; - case MENELAUS_LDO_CTRL3: reg ++; - case MENELAUS_LDO_CTRL2: reg ++; - case MENELAUS_LDO_CTRL1: - return s->ldo[reg]; - - case MENELAUS_SLEEP_CTRL2: reg ++; - case MENELAUS_SLEEP_CTRL1: - return s->sleep[reg]; - - case MENELAUS_DEVICE_OFF: - return 0; - - case MENELAUS_OSC_CTRL: - return s->osc | (1 << 7); /* CLK32K_GOOD */ - - case MENELAUS_DETECT_CTRL: - return s->detect; - - case MENELAUS_INT_MASK1: - return (s->mask >> 0) & 0xff; - case MENELAUS_INT_MASK2: - return (s->mask >> 8) & 0xff; - - case MENELAUS_INT_STATUS1: - return (s->status >> 0) & 0xff; - case MENELAUS_INT_STATUS2: - return (s->status >> 8) & 0xff; - - case MENELAUS_INT_ACK1: - case MENELAUS_INT_ACK2: - return 0; - - case MENELAUS_GPIO_CTRL: - return s->dir; - case MENELAUS_GPIO_IN: - return s->inputs | (~s->dir & s->outputs); - case MENELAUS_GPIO_OUT: - return s->outputs; - - case MENELAUS_BBSMS: - return s->bbsms; - - case MENELAUS_RTC_CTRL: - return s->rtc.ctrl; - case MENELAUS_RTC_UPDATE: - return 0x00; - case MENELAUS_RTC_SEC: - menelaus_rtc_update(s); - return to_bcd(s->rtc.tm.tm_sec); - case MENELAUS_RTC_MIN: - menelaus_rtc_update(s); - return to_bcd(s->rtc.tm.tm_min); - case MENELAUS_RTC_HR: - menelaus_rtc_update(s); - if ((s->rtc.ctrl >> 2) & 1) /* MODE12_n24 */ - return to_bcd((s->rtc.tm.tm_hour % 12) + 1) | - (!!(s->rtc.tm.tm_hour >= 12) << 7); /* PM_nAM */ - else - return to_bcd(s->rtc.tm.tm_hour); - case MENELAUS_RTC_DAY: - menelaus_rtc_update(s); - return to_bcd(s->rtc.tm.tm_mday); - case MENELAUS_RTC_MON: - menelaus_rtc_update(s); - return to_bcd(s->rtc.tm.tm_mon + 1); - case MENELAUS_RTC_YR: - menelaus_rtc_update(s); - return to_bcd(s->rtc.tm.tm_year - 2000); - case MENELAUS_RTC_WKDAY: - menelaus_rtc_update(s); - return to_bcd(s->rtc.tm.tm_wday); - case MENELAUS_RTC_AL_SEC: - return to_bcd(s->rtc.alm.tm_sec); - case MENELAUS_RTC_AL_MIN: - return to_bcd(s->rtc.alm.tm_min); - case MENELAUS_RTC_AL_HR: - if ((s->rtc.ctrl >> 2) & 1) /* MODE12_n24 */ - return to_bcd((s->rtc.alm.tm_hour % 12) + 1) | - (!!(s->rtc.alm.tm_hour >= 12) << 7);/* AL_PM_nAM */ - else - return to_bcd(s->rtc.alm.tm_hour); - case MENELAUS_RTC_AL_DAY: - return to_bcd(s->rtc.alm.tm_mday); - case MENELAUS_RTC_AL_MON: - return to_bcd(s->rtc.alm.tm_mon + 1); - case MENELAUS_RTC_AL_YR: - return to_bcd(s->rtc.alm.tm_year - 2000); - case MENELAUS_RTC_COMP_MSB: - return (s->rtc.comp >> 8) & 0xff; - case MENELAUS_RTC_COMP_LSB: - return (s->rtc.comp >> 0) & 0xff; - - case MENELAUS_S1_PULL_EN: - return s->pull[0]; - case MENELAUS_S1_PULL_DIR: - return s->pull[1]; - case MENELAUS_S2_PULL_EN: - return s->pull[2]; - case MENELAUS_S2_PULL_DIR: - return s->pull[3]; - - case MENELAUS_MCT_CTRL3: reg ++; - case MENELAUS_MCT_CTRL2: reg ++; - case MENELAUS_MCT_CTRL1: - return s->mmc_ctrl[reg]; - case MENELAUS_MCT_PIN_ST: - /* TODO: return the real Card Detect */ - return 0; - case MENELAUS_DEBOUNCE1: - return s->mmc_debounce; - - default: -#ifdef VERBOSE - printf("%s: unknown register %02x\n", __FUNCTION__, addr); -#endif - break; - } - return 0; -} - -static void menelaus_write(void *opaque, uint8_t addr, uint8_t value) -{ - MenelausState *s = (MenelausState *) opaque; - int line; - int reg = 0; - struct tm tm; - - switch (addr) { - case MENELAUS_VCORE_CTRL1: - s->vcore[0] = (value & 0xe) | MIN(value & 0x1f, 0x12); - break; - case MENELAUS_VCORE_CTRL2: - s->vcore[1] = value; - break; - case MENELAUS_VCORE_CTRL3: - s->vcore[2] = MIN(value & 0x1f, 0x12); - break; - case MENELAUS_VCORE_CTRL4: - s->vcore[3] = MIN(value & 0x1f, 0x12); - break; - case MENELAUS_VCORE_CTRL5: - s->vcore[4] = value & 3; - /* XXX - * auto set to 3 on M_Active, nRESWARM - * auto set to 0 on M_WaitOn, M_Backup - */ - break; - - case MENELAUS_DCDC_CTRL1: - s->dcdc[0] = value & 0x3f; - break; - case MENELAUS_DCDC_CTRL2: - s->dcdc[1] = value & 0x07; - /* XXX - * auto set to 3 on M_Active, nRESWARM - * auto set to 0 on M_WaitOn, M_Backup - */ - break; - case MENELAUS_DCDC_CTRL3: - s->dcdc[2] = value & 0x07; - break; - - case MENELAUS_LDO_CTRL1: - s->ldo[0] = value; - break; - case MENELAUS_LDO_CTRL2: - s->ldo[1] = value & 0x7f; - /* XXX - * auto set to 0x7e on M_WaitOn, M_Backup - */ - break; - case MENELAUS_LDO_CTRL3: - s->ldo[2] = value & 3; - /* XXX - * auto set to 3 on M_Active, nRESWARM - * auto set to 0 on M_WaitOn, M_Backup - */ - break; - case MENELAUS_LDO_CTRL4: - s->ldo[3] = value & 3; - /* XXX - * auto set to 3 on M_Active, nRESWARM - * auto set to 0 on M_WaitOn, M_Backup - */ - break; - case MENELAUS_LDO_CTRL5: - s->ldo[4] = value & 3; - /* XXX - * auto set to 3 on M_Active, nRESWARM - * auto set to 0 on M_WaitOn, M_Backup - */ - break; - case MENELAUS_LDO_CTRL6: - s->ldo[5] = value & 3; - break; - case MENELAUS_LDO_CTRL7: - s->ldo[6] = value & 3; - break; - case MENELAUS_LDO_CTRL8: - s->ldo[7] = value & 3; - break; - - case MENELAUS_SLEEP_CTRL2: reg ++; - case MENELAUS_SLEEP_CTRL1: - s->sleep[reg] = value; - break; - - case MENELAUS_DEVICE_OFF: - if (value & 1) { - menelaus_reset(I2C_SLAVE(s)); - } - break; - - case MENELAUS_OSC_CTRL: - s->osc = value & 7; - break; - - case MENELAUS_DETECT_CTRL: - s->detect = value & 0x7f; - break; - - case MENELAUS_INT_MASK1: - s->mask &= 0xf00; - s->mask |= value << 0; - menelaus_update(s); - break; - case MENELAUS_INT_MASK2: - s->mask &= 0x0ff; - s->mask |= value << 8; - menelaus_update(s); - break; - - case MENELAUS_INT_ACK1: - s->status &= ~(((uint16_t) value) << 0); - menelaus_update(s); - break; - case MENELAUS_INT_ACK2: - s->status &= ~(((uint16_t) value) << 8); - menelaus_update(s); - break; - - case MENELAUS_GPIO_CTRL: - for (line = 0; line < 3; line ++) { - if (((s->dir ^ value) >> line) & 1) { - qemu_set_irq(s->out[line], - ((s->outputs & ~s->dir) >> line) & 1); - } - } - s->dir = value & 0x67; - break; - case MENELAUS_GPIO_OUT: - for (line = 0; line < 3; line ++) { - if ((((s->outputs ^ value) & ~s->dir) >> line) & 1) { - qemu_set_irq(s->out[line], (s->outputs >> line) & 1); - } - } - s->outputs = value & 0x07; - break; - - case MENELAUS_BBSMS: - s->bbsms = 0x0d; - break; - - case MENELAUS_RTC_CTRL: - if ((s->rtc.ctrl ^ value) & 1) { /* RTC_EN */ - if (value & 1) - menelaus_rtc_start(s); - else - menelaus_rtc_stop(s); - } - s->rtc.ctrl = value & 0x1f; - menelaus_alm_update(s); - break; - case MENELAUS_RTC_UPDATE: - menelaus_rtc_update(s); - memcpy(&tm, &s->rtc.tm, sizeof(tm)); - switch (value & 0xf) { - case 0: - break; - case 1: - tm.tm_sec = s->rtc.new.tm_sec; - break; - case 2: - tm.tm_min = s->rtc.new.tm_min; - break; - case 3: - if (s->rtc.new.tm_hour > 23) - goto rtc_badness; - tm.tm_hour = s->rtc.new.tm_hour; - break; - case 4: - if (s->rtc.new.tm_mday < 1) - goto rtc_badness; - /* TODO check range */ - tm.tm_mday = s->rtc.new.tm_mday; - break; - case 5: - if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11) - goto rtc_badness; - tm.tm_mon = s->rtc.new.tm_mon; - break; - case 6: - tm.tm_year = s->rtc.new.tm_year; - break; - case 7: - /* TODO set .tm_mday instead */ - tm.tm_wday = s->rtc.new.tm_wday; - break; - case 8: - if (s->rtc.new.tm_hour > 23) - goto rtc_badness; - if (s->rtc.new.tm_mday < 1) - goto rtc_badness; - if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11) - goto rtc_badness; - tm.tm_sec = s->rtc.new.tm_sec; - tm.tm_min = s->rtc.new.tm_min; - tm.tm_hour = s->rtc.new.tm_hour; - tm.tm_mday = s->rtc.new.tm_mday; - tm.tm_mon = s->rtc.new.tm_mon; - tm.tm_year = s->rtc.new.tm_year; - break; - rtc_badness: - default: - fprintf(stderr, "%s: bad RTC_UPDATE value %02x\n", - __FUNCTION__, value); - s->status |= 1 << 10; /* RTCERR */ - menelaus_update(s); - } - s->rtc.sec_offset = qemu_timedate_diff(&tm); - break; - case MENELAUS_RTC_SEC: - s->rtc.tm.tm_sec = from_bcd(value & 0x7f); - break; - case MENELAUS_RTC_MIN: - s->rtc.tm.tm_min = from_bcd(value & 0x7f); - break; - case MENELAUS_RTC_HR: - s->rtc.tm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */ - MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) : - from_bcd(value & 0x3f); - break; - case MENELAUS_RTC_DAY: - s->rtc.tm.tm_mday = from_bcd(value); - break; - case MENELAUS_RTC_MON: - s->rtc.tm.tm_mon = MAX(1, from_bcd(value)) - 1; - break; - case MENELAUS_RTC_YR: - s->rtc.tm.tm_year = 2000 + from_bcd(value); - break; - case MENELAUS_RTC_WKDAY: - s->rtc.tm.tm_mday = from_bcd(value); - break; - case MENELAUS_RTC_AL_SEC: - s->rtc.alm.tm_sec = from_bcd(value & 0x7f); - menelaus_alm_update(s); - break; - case MENELAUS_RTC_AL_MIN: - s->rtc.alm.tm_min = from_bcd(value & 0x7f); - menelaus_alm_update(s); - break; - case MENELAUS_RTC_AL_HR: - s->rtc.alm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */ - MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) : - from_bcd(value & 0x3f); - menelaus_alm_update(s); - break; - case MENELAUS_RTC_AL_DAY: - s->rtc.alm.tm_mday = from_bcd(value); - menelaus_alm_update(s); - break; - case MENELAUS_RTC_AL_MON: - s->rtc.alm.tm_mon = MAX(1, from_bcd(value)) - 1; - menelaus_alm_update(s); - break; - case MENELAUS_RTC_AL_YR: - s->rtc.alm.tm_year = 2000 + from_bcd(value); - menelaus_alm_update(s); - break; - case MENELAUS_RTC_COMP_MSB: - s->rtc.comp &= 0xff; - s->rtc.comp |= value << 8; - break; - case MENELAUS_RTC_COMP_LSB: - s->rtc.comp &= 0xff << 8; - s->rtc.comp |= value; - break; - - case MENELAUS_S1_PULL_EN: - s->pull[0] = value; - break; - case MENELAUS_S1_PULL_DIR: - s->pull[1] = value & 0x1f; - break; - case MENELAUS_S2_PULL_EN: - s->pull[2] = value; - break; - case MENELAUS_S2_PULL_DIR: - s->pull[3] = value & 0x1f; - break; - - case MENELAUS_MCT_CTRL1: - s->mmc_ctrl[0] = value & 0x7f; - break; - case MENELAUS_MCT_CTRL2: - s->mmc_ctrl[1] = value; - /* TODO update Card Detect interrupts */ - break; - case MENELAUS_MCT_CTRL3: - s->mmc_ctrl[2] = value & 0xf; - break; - case MENELAUS_DEBOUNCE1: - s->mmc_debounce = value & 0x3f; - break; - - default: -#ifdef VERBOSE - printf("%s: unknown register %02x\n", __FUNCTION__, addr); -#endif - } -} - -static void menelaus_event(I2CSlave *i2c, enum i2c_event event) -{ - MenelausState *s = TWL92230(i2c); - - if (event == I2C_START_SEND) - s->firstbyte = 1; -} - -static int menelaus_tx(I2CSlave *i2c, uint8_t data) -{ - MenelausState *s = TWL92230(i2c); - - /* Interpret register address byte */ - if (s->firstbyte) { - s->reg = data; - s->firstbyte = 0; - } else - menelaus_write(s, s->reg ++, data); - - return 0; -} - -static int menelaus_rx(I2CSlave *i2c) -{ - MenelausState *s = TWL92230(i2c); - - return menelaus_read(s, s->reg ++); -} - -/* Save restore 32 bit int as uint16_t - This is a Big hack, but it is how the old state did it. - Or we broke compatibility in the state, or we can't use struct tm - */ - -static int get_int32_as_uint16(QEMUFile *f, void *pv, size_t size) -{ - int *v = pv; - *v = qemu_get_be16(f); - return 0; -} - -static void put_int32_as_uint16(QEMUFile *f, void *pv, size_t size) -{ - int *v = pv; - qemu_put_be16(f, *v); -} - -static const VMStateInfo vmstate_hack_int32_as_uint16 = { - .name = "int32_as_uint16", - .get = get_int32_as_uint16, - .put = put_int32_as_uint16, -}; - -#define VMSTATE_UINT16_HACK(_f, _s) \ - VMSTATE_SINGLE(_f, _s, 0, vmstate_hack_int32_as_uint16, int32_t) - - -static const VMStateDescription vmstate_menelaus_tm = { - .name = "menelaus_tm", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT16_HACK(tm_sec, struct tm), - VMSTATE_UINT16_HACK(tm_min, struct tm), - VMSTATE_UINT16_HACK(tm_hour, struct tm), - VMSTATE_UINT16_HACK(tm_mday, struct tm), - VMSTATE_UINT16_HACK(tm_min, struct tm), - VMSTATE_UINT16_HACK(tm_year, struct tm), - VMSTATE_END_OF_LIST() - } -}; - -static void menelaus_pre_save(void *opaque) -{ - MenelausState *s = opaque; - /* Should be <= 1000 */ - s->rtc_next_vmstate = s->rtc.next - qemu_clock_get_ms(rtc_clock); -} - -static int menelaus_post_load(void *opaque, int version_id) -{ - MenelausState *s = opaque; - - if (s->rtc.ctrl & 1) /* RTC_EN */ - menelaus_rtc_stop(s); - - s->rtc.next = s->rtc_next_vmstate; - - menelaus_alm_update(s); - menelaus_update(s); - if (s->rtc.ctrl & 1) /* RTC_EN */ - menelaus_rtc_start(s); - return 0; -} - -static const VMStateDescription vmstate_menelaus = { - .name = "menelaus", - .version_id = 0, - .minimum_version_id = 0, - .pre_save = menelaus_pre_save, - .post_load = menelaus_post_load, - .fields = (VMStateField[]) { - VMSTATE_INT32(firstbyte, MenelausState), - VMSTATE_UINT8(reg, MenelausState), - VMSTATE_UINT8_ARRAY(vcore, MenelausState, 5), - VMSTATE_UINT8_ARRAY(dcdc, MenelausState, 3), - VMSTATE_UINT8_ARRAY(ldo, MenelausState, 8), - VMSTATE_UINT8_ARRAY(sleep, MenelausState, 2), - VMSTATE_UINT8(osc, MenelausState), - VMSTATE_UINT8(detect, MenelausState), - VMSTATE_UINT16(mask, MenelausState), - VMSTATE_UINT16(status, MenelausState), - VMSTATE_UINT8(dir, MenelausState), - VMSTATE_UINT8(inputs, MenelausState), - VMSTATE_UINT8(outputs, MenelausState), - VMSTATE_UINT8(bbsms, MenelausState), - VMSTATE_UINT8_ARRAY(pull, MenelausState, 4), - VMSTATE_UINT8_ARRAY(mmc_ctrl, MenelausState, 3), - VMSTATE_UINT8(mmc_debounce, MenelausState), - VMSTATE_UINT8(rtc.ctrl, MenelausState), - VMSTATE_UINT16(rtc.comp, MenelausState), - VMSTATE_UINT16(rtc_next_vmstate, MenelausState), - VMSTATE_STRUCT(rtc.new, MenelausState, 0, vmstate_menelaus_tm, - struct tm), - VMSTATE_STRUCT(rtc.alm, MenelausState, 0, vmstate_menelaus_tm, - struct tm), - VMSTATE_UINT8(pwrbtn_state, MenelausState), - VMSTATE_I2C_SLAVE(parent_obj, MenelausState), - VMSTATE_END_OF_LIST() - } -}; - -static int twl92230_init(I2CSlave *i2c) -{ - DeviceState *dev = DEVICE(i2c); - MenelausState *s = TWL92230(i2c); - - s->rtc.hz_tm = timer_new_ms(rtc_clock, menelaus_rtc_hz, s); - /* Three output pins plus one interrupt pin. */ - qdev_init_gpio_out(dev, s->out, 4); - - /* Three input pins plus one power-button pin. */ - qdev_init_gpio_in(dev, menelaus_gpio_set, 4); - - menelaus_reset(i2c); - - return 0; -} - -static void twl92230_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); - - sc->init = twl92230_init; - sc->event = menelaus_event; - sc->recv = menelaus_rx; - sc->send = menelaus_tx; - dc->vmsd = &vmstate_menelaus; -} - -static const TypeInfo twl92230_info = { - .name = TYPE_TWL92230, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(MenelausState), - .class_init = twl92230_class_init, -}; - -static void twl92230_register_types(void) -{ - type_register_static(&twl92230_info); -} - -type_init(twl92230_register_types) diff --git a/qemu/hw/timer/xilinx_timer.c b/qemu/hw/timer/xilinx_timer.c deleted file mode 100644 index 2ea970dc9..000000000 --- a/qemu/hw/timer/xilinx_timer.c +++ /dev/null @@ -1,266 +0,0 @@ -/* - * QEMU model of the Xilinx timer block. - * - * Copyright (c) 2009 Edgar E. Iglesias. - * - * 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/sysbus.h" -#include "hw/ptimer.h" -#include "qemu/log.h" -#include "qemu/main-loop.h" - -#define D(x) - -#define R_TCSR 0 -#define R_TLR 1 -#define R_TCR 2 -#define R_MAX 4 - -#define TCSR_MDT (1<<0) -#define TCSR_UDT (1<<1) -#define TCSR_GENT (1<<2) -#define TCSR_CAPT (1<<3) -#define TCSR_ARHT (1<<4) -#define TCSR_LOAD (1<<5) -#define TCSR_ENIT (1<<6) -#define TCSR_ENT (1<<7) -#define TCSR_TINT (1<<8) -#define TCSR_PWMA (1<<9) -#define TCSR_ENALL (1<<10) - -struct xlx_timer -{ - QEMUBH *bh; - ptimer_state *ptimer; - void *parent; - int nr; /* for debug. */ - - unsigned long timer_div; - - uint32_t regs[R_MAX]; -}; - -#define TYPE_XILINX_TIMER "xlnx.xps-timer" -#define XILINX_TIMER(obj) \ - OBJECT_CHECK(struct timerblock, (obj), TYPE_XILINX_TIMER) - -struct timerblock -{ - SysBusDevice parent_obj; - - MemoryRegion mmio; - qemu_irq irq; - uint8_t one_timer_only; - uint32_t freq_hz; - struct xlx_timer *timers; -}; - -static inline unsigned int num_timers(struct timerblock *t) -{ - return 2 - t->one_timer_only; -} - -static inline unsigned int timer_from_addr(hwaddr addr) -{ - /* Timers get a 4x32bit control reg area each. */ - return addr >> 2; -} - -static void timer_update_irq(struct timerblock *t) -{ - unsigned int i, irq = 0; - uint32_t csr; - - for (i = 0; i < num_timers(t); i++) { - csr = t->timers[i].regs[R_TCSR]; - irq |= (csr & TCSR_TINT) && (csr & TCSR_ENIT); - } - - /* All timers within the same slave share a single IRQ line. */ - qemu_set_irq(t->irq, !!irq); -} - -static uint64_t -timer_read(void *opaque, hwaddr addr, unsigned int size) -{ - struct timerblock *t = opaque; - struct xlx_timer *xt; - uint32_t r = 0; - unsigned int timer; - - addr >>= 2; - timer = timer_from_addr(addr); - xt = &t->timers[timer]; - /* Further decoding to address a specific timers reg. */ - addr &= 0x3; - switch (addr) - { - case R_TCR: - r = ptimer_get_count(xt->ptimer); - if (!(xt->regs[R_TCSR] & TCSR_UDT)) - r = ~r; - D(qemu_log("xlx_timer t=%d read counter=%x udt=%d\n", - timer, r, xt->regs[R_TCSR] & TCSR_UDT)); - break; - default: - if (addr < ARRAY_SIZE(xt->regs)) - r = xt->regs[addr]; - break; - - } - D(fprintf(stderr, "%s timer=%d %x=%x\n", __func__, timer, addr * 4, r)); - return r; -} - -static void timer_enable(struct xlx_timer *xt) -{ - uint64_t count; - - D(fprintf(stderr, "%s timer=%d down=%d\n", __func__, - xt->nr, xt->regs[R_TCSR] & TCSR_UDT)); - - ptimer_stop(xt->ptimer); - - if (xt->regs[R_TCSR] & TCSR_UDT) - count = xt->regs[R_TLR]; - else - count = ~0 - xt->regs[R_TLR]; - ptimer_set_limit(xt->ptimer, count, 1); - ptimer_run(xt->ptimer, 1); -} - -static void -timer_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - struct timerblock *t = opaque; - struct xlx_timer *xt; - unsigned int timer; - uint32_t value = val64; - - addr >>= 2; - timer = timer_from_addr(addr); - xt = &t->timers[timer]; - D(fprintf(stderr, "%s addr=%x val=%x (timer=%d off=%d)\n", - __func__, addr * 4, value, timer, addr & 3)); - /* Further decoding to address a specific timers reg. */ - addr &= 3; - switch (addr) - { - case R_TCSR: - if (value & TCSR_TINT) - value &= ~TCSR_TINT; - - xt->regs[addr] = value & 0x7ff; - if (value & TCSR_ENT) - timer_enable(xt); - break; - - default: - if (addr < ARRAY_SIZE(xt->regs)) - xt->regs[addr] = value; - break; - } - timer_update_irq(t); -} - -static const MemoryRegionOps timer_ops = { - .read = timer_read, - .write = timer_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static void timer_hit(void *opaque) -{ - struct xlx_timer *xt = opaque; - struct timerblock *t = xt->parent; - D(fprintf(stderr, "%s %d\n", __func__, xt->nr)); - xt->regs[R_TCSR] |= TCSR_TINT; - - if (xt->regs[R_TCSR] & TCSR_ARHT) - timer_enable(xt); - timer_update_irq(t); -} - -static void xilinx_timer_realize(DeviceState *dev, Error **errp) -{ - struct timerblock *t = XILINX_TIMER(dev); - unsigned int i; - - /* Init all the ptimers. */ - t->timers = g_malloc0(sizeof t->timers[0] * num_timers(t)); - for (i = 0; i < num_timers(t); i++) { - struct xlx_timer *xt = &t->timers[i]; - - xt->parent = t; - xt->nr = i; - xt->bh = qemu_bh_new(timer_hit, xt); - xt->ptimer = ptimer_init(xt->bh); - ptimer_set_freq(xt->ptimer, t->freq_hz); - } - - memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t, "xlnx.xps-timer", - R_MAX * 4 * num_timers(t)); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &t->mmio); -} - -static void xilinx_timer_init(Object *obj) -{ - struct timerblock *t = XILINX_TIMER(obj); - - /* All timers share a single irq line. */ - sysbus_init_irq(SYS_BUS_DEVICE(obj), &t->irq); -} - -static Property xilinx_timer_properties[] = { - DEFINE_PROP_UINT32("clock-frequency", struct timerblock, freq_hz, - 62 * 1000000), - DEFINE_PROP_UINT8("one-timer-only", struct timerblock, one_timer_only, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void xilinx_timer_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = xilinx_timer_realize; - dc->props = xilinx_timer_properties; -} - -static const TypeInfo xilinx_timer_info = { - .name = TYPE_XILINX_TIMER, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct timerblock), - .instance_init = xilinx_timer_init, - .class_init = xilinx_timer_class_init, -}; - -static void xilinx_timer_register_types(void) -{ - type_register_static(&xilinx_timer_info); -} - -type_init(xilinx_timer_register_types) diff --git a/qemu/hw/tpm/Makefile.objs b/qemu/hw/tpm/Makefile.objs deleted file mode 100644 index 64cecc3b6..000000000 --- a/qemu/hw/tpm/Makefile.objs +++ /dev/null @@ -1,2 +0,0 @@ -common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o -common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o diff --git a/qemu/hw/tpm/tpm_int.h b/qemu/hw/tpm/tpm_int.h deleted file mode 100644 index f2f285b3c..000000000 --- a/qemu/hw/tpm/tpm_int.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * TPM configuration - * - * Copyright (C) 2011-2013 IBM Corporation - * - * Authors: - * Stefan Berger - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ -#ifndef TPM_TPM_INT_H -#define TPM_TPM_INT_H - -#include "exec/memory.h" -#include "tpm_tis.h" - -/* overall state of the TPM interface */ -struct TPMState { - ISADevice busdev; - MemoryRegion mmio; - - union { - TPMTISEmuState tis; - } s; - - uint8_t locty_number; - TPMLocality *locty_data; - - char *backend; - TPMBackend *be_driver; - TPMVersion be_tpm_version; -}; - -#define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS) - -#define TPM_STANDARD_CMDLINE_OPTS \ - { \ - .name = "type", \ - .type = QEMU_OPT_STRING, \ - .help = "Type of TPM backend", \ - } - -struct tpm_req_hdr { - uint16_t tag; - uint32_t len; - uint32_t ordinal; -} QEMU_PACKED; - -struct tpm_resp_hdr { - uint16_t tag; - uint32_t len; - uint32_t errcode; -} QEMU_PACKED; - -#define TPM_TAG_RQU_COMMAND 0xc1 -#define TPM_TAG_RQU_AUTH1_COMMAND 0xc2 -#define TPM_TAG_RQU_AUTH2_COMMAND 0xc3 - -#define TPM_TAG_RSP_COMMAND 0xc4 -#define TPM_TAG_RSP_AUTH1_COMMAND 0xc5 -#define TPM_TAG_RSP_AUTH2_COMMAND 0xc6 - -#define TPM_FAIL 9 - -#define TPM_ORD_ContinueSelfTest 0x53 -#define TPM_ORD_GetTicks 0xf1 - - -/* TPM2 defines */ -#define TPM2_ST_NO_SESSIONS 0x8001 - -#define TPM2_CC_ReadClock 0x00000181 - -#endif /* TPM_TPM_INT_H */ diff --git a/qemu/hw/tpm/tpm_passthrough.c b/qemu/hw/tpm/tpm_passthrough.c deleted file mode 100644 index e88c0d20b..000000000 --- a/qemu/hw/tpm/tpm_passthrough.c +++ /dev/null @@ -1,566 +0,0 @@ -/* - * passthrough TPM driver - * - * Copyright (c) 2010 - 2013 IBM Corporation - * Authors: - * Stefan Berger - * - * Copyright (C) 2011 IAIK, Graz University of Technology - * Author: Andreas Niederl - * - * 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 - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/error-report.h" -#include "qemu/sockets.h" -#include "sysemu/tpm_backend.h" -#include "tpm_int.h" -#include "hw/hw.h" -#include "hw/i386/pc.h" -#include "sysemu/tpm_backend_int.h" -#include "tpm_tis.h" -#include "tpm_util.h" - -#define DEBUG_TPM 0 - -#define DPRINTF(fmt, ...) do { \ - if (DEBUG_TPM) { \ - fprintf(stderr, fmt, ## __VA_ARGS__); \ - } \ -} while (0); - -#define TYPE_TPM_PASSTHROUGH "tpm-passthrough" -#define TPM_PASSTHROUGH(obj) \ - OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH) - -static const TPMDriverOps tpm_passthrough_driver; - -/* data structures */ -typedef struct TPMPassthruThreadParams { - TPMState *tpm_state; - - TPMRecvDataCB *recv_data_callback; - TPMBackend *tb; -} TPMPassthruThreadParams; - -struct TPMPassthruState { - TPMBackend parent; - - TPMBackendThread tbt; - - TPMPassthruThreadParams tpm_thread_params; - - char *tpm_dev; - int tpm_fd; - bool tpm_executing; - bool tpm_op_canceled; - int cancel_fd; - bool had_startup_error; - - TPMVersion tpm_version; -}; - -typedef struct TPMPassthruState TPMPassthruState; - -#define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0" - -/* functions */ - -static void tpm_passthrough_cancel_cmd(TPMBackend *tb); - -static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t 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) -{ - 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) -{ - struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)buf; - - return be32_to_cpu(resp->len); -} - -/* - * Write an error message in the given output buffer. - */ -static void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len) -{ - if (out_len >= sizeof(struct tpm_resp_hdr)) { - struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out; - - resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND); - resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr)); - resp->errcode = cpu_to_be32(TPM_FAIL); - } -} - -static bool tpm_passthrough_is_selftest(const uint8_t *in, uint32_t in_len) -{ - struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in; - - if (in_len >= sizeof(*hdr)) { - return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest); - } - - return false; -} - -static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, - const uint8_t *in, uint32_t in_len, - uint8_t *out, uint32_t out_len, - bool *selftest_done) -{ - int ret; - bool is_selftest; - const struct tpm_resp_hdr *hdr; - - tpm_pt->tpm_op_canceled = false; - tpm_pt->tpm_executing = true; - *selftest_done = false; - - is_selftest = tpm_passthrough_is_selftest(in, in_len); - - ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len); - if (ret != in_len) { - if (!tpm_pt->tpm_op_canceled || - (tpm_pt->tpm_op_canceled && errno != ECANCELED)) { - error_report("tpm_passthrough: error while transmitting data " - "to TPM: %s (%i)", - strerror(errno), errno); - } - goto err_exit; - } - - tpm_pt->tpm_executing = false; - - ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len); - if (ret < 0) { - if (!tpm_pt->tpm_op_canceled || - (tpm_pt->tpm_op_canceled && errno != ECANCELED)) { - error_report("tpm_passthrough: error while reading data from " - "TPM: %s (%i)", - strerror(errno), errno); - } - } else if (ret < sizeof(struct tpm_resp_hdr) || - tpm_passthrough_get_size_from_buffer(out) != ret) { - ret = -1; - error_report("tpm_passthrough: received invalid response " - "packet from TPM"); - } - - if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) { - hdr = (struct tpm_resp_hdr *)out; - *selftest_done = (be32_to_cpu(hdr->errcode) == 0); - } - -err_exit: - if (ret < 0) { - tpm_write_fatal_error_response(out, out_len); - } - - tpm_pt->tpm_executing = false; - - return ret; -} - -static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt, - const TPMLocality *locty_data, - bool *selftest_done) -{ - return tpm_passthrough_unix_tx_bufs(tpm_pt, - locty_data->w_buffer.buffer, - locty_data->w_offset, - locty_data->r_buffer.buffer, - locty_data->r_buffer.size, - selftest_done); -} - -static void tpm_passthrough_worker_thread(gpointer data, - gpointer user_data) -{ - TPMPassthruThreadParams *thr_parms = user_data; - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb); - TPMBackendCmd cmd = (TPMBackendCmd)data; - bool selftest_done = false; - - DPRINTF("tpm_passthrough: processing command type %d\n", cmd); - - switch (cmd) { - case TPM_BACKEND_CMD_PROCESS_CMD: - tpm_passthrough_unix_transfer(tpm_pt, - thr_parms->tpm_state->locty_data, - &selftest_done); - - thr_parms->recv_data_callback(thr_parms->tpm_state, - thr_parms->tpm_state->locty_number, - selftest_done); - break; - case TPM_BACKEND_CMD_INIT: - case TPM_BACKEND_CMD_END: - case TPM_BACKEND_CMD_TPM_RESET: - /* nothing to do */ - break; - } -} - -/* - * Start the TPM (thread). If it had been started before, then terminate - * and start it again. - */ -static int tpm_passthrough_startup_tpm(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - /* terminate a running TPM */ - tpm_backend_thread_end(&tpm_pt->tbt); - - tpm_backend_thread_create(&tpm_pt->tbt, - tpm_passthrough_worker_thread, - &tpm_pt->tpm_thread_params); - - return 0; -} - -static void tpm_passthrough_reset(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n"); - - tpm_passthrough_cancel_cmd(tb); - - tpm_backend_thread_end(&tpm_pt->tbt); - - tpm_pt->had_startup_error = false; -} - -static int tpm_passthrough_init(TPMBackend *tb, TPMState *s, - TPMRecvDataCB *recv_data_cb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - tpm_pt->tpm_thread_params.tpm_state = s; - tpm_pt->tpm_thread_params.recv_data_callback = recv_data_cb; - tpm_pt->tpm_thread_params.tb = tb; - - return 0; -} - -static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb) -{ - return false; -} - -static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb, - uint8_t locty) -{ - /* only a TPM 2.0 will support this */ - return 0; -} - -static bool tpm_passthrough_get_startup_error(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - return tpm_pt->had_startup_error; -} - -static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb) -{ - size_t wanted_size = 4096; /* Linux tpm.c buffer size */ - - if (sb->size != wanted_size) { - sb->buffer = g_realloc(sb->buffer, wanted_size); - sb->size = wanted_size; - } - return sb->size; -} - -static void tpm_passthrough_deliver_request(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - tpm_backend_thread_deliver_request(&tpm_pt->tbt); -} - -static void tpm_passthrough_cancel_cmd(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - int n; - - /* - * As of Linux 3.7 the tpm_tis driver does not properly cancel - * commands on all TPM manufacturers' TPMs. - * Only cancel if we're busy so we don't cancel someone else's - * command, e.g., a command executed on the host. - */ - if (tpm_pt->tpm_executing) { - if (tpm_pt->cancel_fd >= 0) { - n = write(tpm_pt->cancel_fd, "-", 1); - if (n != 1) { - error_report("Canceling TPM command failed: %s", - strerror(errno)); - } else { - tpm_pt->tpm_op_canceled = true; - } - } else { - error_report("Cannot cancel TPM command due to missing " - "TPM sysfs cancel entry"); - } - } -} - -static const char *tpm_passthrough_create_desc(void) -{ - return "Passthrough TPM backend driver"; -} - -static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - return tpm_pt->tpm_version; -} - -/* - * Unless path or file descriptor set has been provided by user, - * determine the sysfs cancel file following kernel documentation - * in Documentation/ABI/stable/sysfs-class-tpm. - * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel - */ -static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - int fd = -1; - char *dev; - char path[PATH_MAX]; - - if (tb->cancel_path) { - fd = qemu_open(tb->cancel_path, O_WRONLY); - if (fd < 0) { - error_report("Could not open TPM cancel path : %s", - strerror(errno)); - } - return fd; - } - - dev = strrchr(tpm_pt->tpm_dev, '/'); - if (dev) { - dev++; - if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel", - dev) < sizeof(path)) { - fd = qemu_open(path, O_WRONLY); - if (fd >= 0) { - tb->cancel_path = g_strdup(path); - } else { - error_report("tpm_passthrough: Could not open TPM cancel " - "path %s : %s", path, strerror(errno)); - } - } - } else { - error_report("tpm_passthrough: Bad TPM device path %s", - tpm_pt->tpm_dev); - } - - return fd; -} - -static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - const char *value; - - value = qemu_opt_get(opts, "cancel-path"); - tb->cancel_path = g_strdup(value); - - value = qemu_opt_get(opts, "path"); - if (!value) { - value = TPM_PASSTHROUGH_DEFAULT_DEVICE; - } - - tpm_pt->tpm_dev = g_strdup(value); - - tb->path = g_strdup(tpm_pt->tpm_dev); - - tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR); - if (tpm_pt->tpm_fd < 0) { - error_report("Cannot access TPM device using '%s': %s", - tpm_pt->tpm_dev, strerror(errno)); - goto err_free_parameters; - } - - if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) { - error_report("'%s' is not a TPM device.", - tpm_pt->tpm_dev); - goto err_close_tpmdev; - } - - return 0; - - err_close_tpmdev: - qemu_close(tpm_pt->tpm_fd); - tpm_pt->tpm_fd = -1; - - err_free_parameters: - g_free(tb->path); - tb->path = NULL; - - g_free(tpm_pt->tpm_dev); - tpm_pt->tpm_dev = NULL; - - return 1; -} - -static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id) -{ - Object *obj = object_new(TYPE_TPM_PASSTHROUGH); - TPMBackend *tb = TPM_BACKEND(obj); - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - tb->id = g_strdup(id); - /* let frontend set the fe_model to proper value */ - tb->fe_model = -1; - - tb->ops = &tpm_passthrough_driver; - - if (tpm_passthrough_handle_device_opts(opts, tb)) { - goto err_exit; - } - - tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb); - if (tpm_pt->cancel_fd < 0) { - goto err_exit; - } - - return tb; - -err_exit: - g_free(tb->id); - - return NULL; -} - -static void tpm_passthrough_destroy(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - tpm_passthrough_cancel_cmd(tb); - - tpm_backend_thread_end(&tpm_pt->tbt); - - qemu_close(tpm_pt->tpm_fd); - qemu_close(tpm_pt->cancel_fd); - - g_free(tb->id); - g_free(tb->path); - g_free(tb->cancel_path); - g_free(tpm_pt->tpm_dev); -} - -static const QemuOptDesc tpm_passthrough_cmdline_opts[] = { - TPM_STANDARD_CMDLINE_OPTS, - { - .name = "cancel-path", - .type = QEMU_OPT_STRING, - .help = "Sysfs file entry for canceling TPM commands", - }, - { - .name = "path", - .type = QEMU_OPT_STRING, - .help = "Path to TPM device on the host", - }, - { /* end of list */ }, -}; - -static const TPMDriverOps tpm_passthrough_driver = { - .type = TPM_TYPE_PASSTHROUGH, - .opts = tpm_passthrough_cmdline_opts, - .desc = tpm_passthrough_create_desc, - .create = tpm_passthrough_create, - .destroy = tpm_passthrough_destroy, - .init = tpm_passthrough_init, - .startup_tpm = tpm_passthrough_startup_tpm, - .realloc_buffer = tpm_passthrough_realloc_buffer, - .reset = tpm_passthrough_reset, - .had_startup_error = tpm_passthrough_get_startup_error, - .deliver_request = tpm_passthrough_deliver_request, - .cancel_cmd = tpm_passthrough_cancel_cmd, - .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag, - .reset_tpm_established_flag = tpm_passthrough_reset_tpm_established_flag, - .get_tpm_version = tpm_passthrough_get_tpm_version, -}; - -static void tpm_passthrough_inst_init(Object *obj) -{ -} - -static void tpm_passthrough_inst_finalize(Object *obj) -{ -} - -static void tpm_passthrough_class_init(ObjectClass *klass, void *data) -{ - TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); - - tbc->ops = &tpm_passthrough_driver; -} - -static const TypeInfo tpm_passthrough_info = { - .name = TYPE_TPM_PASSTHROUGH, - .parent = TYPE_TPM_BACKEND, - .instance_size = sizeof(TPMPassthruState), - .class_init = tpm_passthrough_class_init, - .instance_init = tpm_passthrough_inst_init, - .instance_finalize = tpm_passthrough_inst_finalize, -}; - -static void tpm_passthrough_register(void) -{ - type_register_static(&tpm_passthrough_info); - tpm_register_driver(&tpm_passthrough_driver); -} - -type_init(tpm_passthrough_register) diff --git a/qemu/hw/tpm/tpm_tis.c b/qemu/hw/tpm/tpm_tis.c deleted file mode 100644 index 381e7266e..000000000 --- a/qemu/hw/tpm/tpm_tis.c +++ /dev/null @@ -1,1101 +0,0 @@ -/* - * tpm_tis.c - QEMU's TPM TIS interface emulator - * - * Copyright (C) 2006,2010-2013 IBM Corporation - * - * Authors: - * Stefan Berger - * David Safford - * - * Xen 4 support: Andrease Niederl - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - * Implementation of the TIS interface according to specs found at - * http://www.trustedcomputinggroup.org. This implementation currently - * supports version 1.3, 21 March 2013 - * In the developers menu choose the PC Client section then find the TIS - * specification. - * - * TPM TIS for TPM 2 implementation following TCG PC Client Platform - * 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" -#include "exec/address-spaces.h" -#include "hw/hw.h" -#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" - -#define DEBUG_TIS 0 - -#define DPRINTF(fmt, ...) do { \ - if (DEBUG_TIS) { \ - printf(fmt, ## __VA_ARGS__); \ - } \ -} while (0); - -/* whether the STS interrupt is supported */ -#define RAISE_STS_IRQ - -/* tis registers */ -#define TPM_TIS_REG_ACCESS 0x00 -#define TPM_TIS_REG_INT_ENABLE 0x08 -#define TPM_TIS_REG_INT_VECTOR 0x0c -#define TPM_TIS_REG_INT_STATUS 0x10 -#define TPM_TIS_REG_INTF_CAPABILITY 0x14 -#define TPM_TIS_REG_STS 0x18 -#define TPM_TIS_REG_DATA_FIFO 0x24 -#define TPM_TIS_REG_INTERFACE_ID 0x30 -#define TPM_TIS_REG_DATA_XFIFO 0x80 -#define TPM_TIS_REG_DATA_XFIFO_END 0xbc -#define TPM_TIS_REG_DID_VID 0xf00 -#define TPM_TIS_REG_RID 0xf04 - -/* vendor-specific registers */ -#define TPM_TIS_REG_DEBUG 0xf90 - -#define TPM_TIS_STS_TPM_FAMILY_MASK (0x3 << 26)/* TPM 2.0 */ -#define TPM_TIS_STS_TPM_FAMILY1_2 (0 << 26) /* TPM 2.0 */ -#define TPM_TIS_STS_TPM_FAMILY2_0 (1 << 26) /* TPM 2.0 */ -#define TPM_TIS_STS_RESET_ESTABLISHMENT_BIT (1 << 25) /* TPM 2.0 */ -#define TPM_TIS_STS_COMMAND_CANCEL (1 << 24) /* TPM 2.0 */ - -#define TPM_TIS_STS_VALID (1 << 7) -#define TPM_TIS_STS_COMMAND_READY (1 << 6) -#define TPM_TIS_STS_TPM_GO (1 << 5) -#define TPM_TIS_STS_DATA_AVAILABLE (1 << 4) -#define TPM_TIS_STS_EXPECT (1 << 3) -#define TPM_TIS_STS_SELFTEST_DONE (1 << 2) -#define TPM_TIS_STS_RESPONSE_RETRY (1 << 1) - -#define TPM_TIS_BURST_COUNT_SHIFT 8 -#define TPM_TIS_BURST_COUNT(X) \ - ((X) << TPM_TIS_BURST_COUNT_SHIFT) - -#define TPM_TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) -#define TPM_TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) -#define TPM_TIS_ACCESS_BEEN_SEIZED (1 << 4) -#define TPM_TIS_ACCESS_SEIZE (1 << 3) -#define TPM_TIS_ACCESS_PENDING_REQUEST (1 << 2) -#define TPM_TIS_ACCESS_REQUEST_USE (1 << 1) -#define TPM_TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) - -#define TPM_TIS_INT_ENABLED (1 << 31) -#define TPM_TIS_INT_DATA_AVAILABLE (1 << 0) -#define TPM_TIS_INT_STS_VALID (1 << 1) -#define TPM_TIS_INT_LOCALITY_CHANGED (1 << 2) -#define TPM_TIS_INT_COMMAND_READY (1 << 7) - -#define TPM_TIS_INT_POLARITY_MASK (3 << 3) -#define TPM_TIS_INT_POLARITY_LOW_LEVEL (1 << 3) - -#ifndef RAISE_STS_IRQ - -#define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \ - TPM_TIS_INT_DATA_AVAILABLE | \ - TPM_TIS_INT_COMMAND_READY) - -#else - -#define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \ - TPM_TIS_INT_DATA_AVAILABLE | \ - TPM_TIS_INT_STS_VALID | \ - TPM_TIS_INT_COMMAND_READY) - -#endif - -#define TPM_TIS_CAP_INTERFACE_VERSION1_3 (2 << 28) -#define TPM_TIS_CAP_INTERFACE_VERSION1_3_FOR_TPM2_0 (3 << 28) -#define TPM_TIS_CAP_DATA_TRANSFER_64B (3 << 9) -#define TPM_TIS_CAP_DATA_TRANSFER_LEGACY (0 << 9) -#define TPM_TIS_CAP_BURST_COUNT_DYNAMIC (0 << 8) -#define TPM_TIS_CAP_INTERRUPT_LOW_LEVEL (1 << 4) /* support is mandatory */ -#define TPM_TIS_CAPABILITIES_SUPPORTED1_3 \ - (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \ - TPM_TIS_CAP_BURST_COUNT_DYNAMIC | \ - TPM_TIS_CAP_DATA_TRANSFER_64B | \ - TPM_TIS_CAP_INTERFACE_VERSION1_3 | \ - TPM_TIS_INTERRUPTS_SUPPORTED) - -#define TPM_TIS_CAPABILITIES_SUPPORTED2_0 \ - (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \ - TPM_TIS_CAP_BURST_COUNT_DYNAMIC | \ - TPM_TIS_CAP_DATA_TRANSFER_64B | \ - TPM_TIS_CAP_INTERFACE_VERSION1_3_FOR_TPM2_0 | \ - TPM_TIS_INTERRUPTS_SUPPORTED) - -#define TPM_TIS_IFACE_ID_INTERFACE_TIS1_3 (0xf) /* TPM 2.0 */ -#define TPM_TIS_IFACE_ID_INTERFACE_FIFO (0x0) /* TPM 2.0 */ -#define TPM_TIS_IFACE_ID_INTERFACE_VER_FIFO (0 << 4) /* TPM 2.0 */ -#define TPM_TIS_IFACE_ID_CAP_5_LOCALITIES (1 << 8) /* TPM 2.0 */ -#define TPM_TIS_IFACE_ID_CAP_TIS_SUPPORTED (1 << 13) /* TPM 2.0 */ -#define TPM_TIS_IFACE_ID_INT_SEL_LOCK (1 << 19) /* TPM 2.0 */ - -#define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3 \ - (TPM_TIS_IFACE_ID_INTERFACE_TIS1_3 | \ - (~0u << 4)/* all of it is don't care */) - -/* if backend was a TPM 2.0: */ -#define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0 \ - (TPM_TIS_IFACE_ID_INTERFACE_FIFO | \ - TPM_TIS_IFACE_ID_INTERFACE_VER_FIFO | \ - TPM_TIS_IFACE_ID_CAP_5_LOCALITIES | \ - TPM_TIS_IFACE_ID_CAP_TIS_SUPPORTED) - -#define TPM_TIS_TPM_DID 0x0001 -#define TPM_TIS_TPM_VID PCI_VENDOR_ID_IBM -#define TPM_TIS_TPM_RID 0x0001 - -#define TPM_TIS_NO_DATA_BYTE 0xff - -/* local prototypes */ - -static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, - unsigned size); - -/* utility functions */ - -static uint8_t tpm_tis_locality_from_addr(hwaddr addr) -{ - return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7); -} - -static uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb) -{ - return be32_to_cpu(*(uint32_t *)&sb->buffer[2]); -} - -static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string) -{ -#ifdef DEBUG_TIS - uint32_t len, i; - - len = tpm_tis_get_size_from_buffer(sb); - DPRINTF("tpm_tis: %s length = %d\n", string, len); - for (i = 0; i < len; i++) { - if (i && !(i % 16)) { - DPRINTF("\n"); - } - DPRINTF("%.2X ", sb->buffer[i]); - } - DPRINTF("\n"); -#endif -} - -/* - * Set the given flags in the STS register by clearing the register but - * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting - * the new flags. - * - * The SELFTEST_DONE flag is acquired from the backend that determines it by - * peeking into TPM commands. - * - * A VM suspend/resume will preserve the flag by storing it into the VM - * device state, but the backend will not remember it when QEMU is started - * again. Therefore, we cache the flag here. Once set, it will not be unset - * except by a reset. - */ -static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags) -{ - l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK; - l->sts |= flags; -} - -/* - * Send a request to the TPM. - */ -static void tpm_tis_tpm_send(TPMState *s, uint8_t locty) -{ - TPMTISEmuState *tis = &s->s.tis; - - tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM"); - - s->locty_number = locty; - s->locty_data = &tis->loc[locty]; - - /* - * w_offset serves as length indicator for length of data; - * it's reset when the response comes back - */ - tis->loc[locty].state = TPM_TIS_STATE_EXECUTION; - - tpm_backend_deliver_request(s->be_driver); -} - -/* raise an interrupt if allowed */ -static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask) -{ - TPMTISEmuState *tis = &s->s.tis; - - if (!TPM_TIS_IS_VALID_LOCTY(locty)) { - return; - } - - if ((tis->loc[locty].inte & TPM_TIS_INT_ENABLED) && - (tis->loc[locty].inte & irqmask)) { - DPRINTF("tpm_tis: Raising IRQ for flag %08x\n", irqmask); - qemu_irq_raise(s->s.tis.irq); - tis->loc[locty].ints |= irqmask; - } -} - -static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty) -{ - uint8_t l; - - for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { - if (l == locty) { - continue; - } - if ((s->s.tis.loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) { - return 1; - } - } - - return 0; -} - -static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty) -{ - TPMTISEmuState *tis = &s->s.tis; - bool change = (s->s.tis.active_locty != new_active_locty); - bool is_seize; - uint8_t mask; - - if (change && TPM_TIS_IS_VALID_LOCTY(s->s.tis.active_locty)) { - is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) && - tis->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE; - - if (is_seize) { - mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY); - } else { - mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY| - TPM_TIS_ACCESS_REQUEST_USE); - } - /* reset flags on the old active locality */ - tis->loc[s->s.tis.active_locty].access &= mask; - - if (is_seize) { - tis->loc[tis->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED; - } - } - - tis->active_locty = new_active_locty; - - DPRINTF("tpm_tis: Active locality is now %d\n", s->s.tis.active_locty); - - if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) { - /* set flags on the new active locality */ - tis->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY; - tis->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE | - TPM_TIS_ACCESS_SEIZE); - } - - if (change) { - tpm_tis_raise_irq(s, tis->active_locty, TPM_TIS_INT_LOCALITY_CHANGED); - } -} - -/* abort -- this function switches the locality */ -static void tpm_tis_abort(TPMState *s, uint8_t locty) -{ - TPMTISEmuState *tis = &s->s.tis; - - tis->loc[locty].r_offset = 0; - tis->loc[locty].w_offset = 0; - - DPRINTF("tpm_tis: tis_abort: new active locality is %d\n", tis->next_locty); - - /* - * Need to react differently depending on who's aborting now and - * which locality will become active afterwards. - */ - if (tis->aborting_locty == tis->next_locty) { - tis->loc[tis->aborting_locty].state = TPM_TIS_STATE_READY; - tpm_tis_sts_set(&tis->loc[tis->aborting_locty], - TPM_TIS_STS_COMMAND_READY); - tpm_tis_raise_irq(s, tis->aborting_locty, TPM_TIS_INT_COMMAND_READY); - } - - /* locality after abort is another one than the current one */ - tpm_tis_new_active_locality(s, tis->next_locty); - - tis->next_locty = TPM_TIS_NO_LOCALITY; - /* nobody's aborting a command anymore */ - tis->aborting_locty = TPM_TIS_NO_LOCALITY; -} - -/* prepare aborting current command */ -static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty) -{ - TPMTISEmuState *tis = &s->s.tis; - uint8_t busy_locty; - - tis->aborting_locty = locty; - tis->next_locty = newlocty; /* locality after successful abort */ - - /* - * only abort a command using an interrupt if currently executing - * a command AND if there's a valid connection to the vTPM. - */ - for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) { - if (tis->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) { - /* - * request the backend to cancel. Some backends may not - * support it - */ - tpm_backend_cancel_cmd(s->be_driver); - return; - } - } - - tpm_tis_abort(s, locty); -} - -static void tpm_tis_receive_bh(void *opaque) -{ - TPMState *s = opaque; - TPMTISEmuState *tis = &s->s.tis; - uint8_t locty = s->locty_number; - - tpm_tis_sts_set(&tis->loc[locty], - TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE); - tis->loc[locty].state = TPM_TIS_STATE_COMPLETION; - tis->loc[locty].r_offset = 0; - tis->loc[locty].w_offset = 0; - - if (TPM_TIS_IS_VALID_LOCTY(tis->next_locty)) { - tpm_tis_abort(s, locty); - } - -#ifndef RAISE_STS_IRQ - tpm_tis_raise_irq(s, locty, TPM_TIS_INT_DATA_AVAILABLE); -#else - tpm_tis_raise_irq(s, locty, - TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID); -#endif -} - -/* - * Callback from the TPM to indicate that the response was received. - */ -static void tpm_tis_receive_cb(TPMState *s, uint8_t locty, - bool is_selftest_done) -{ - TPMTISEmuState *tis = &s->s.tis; - uint8_t l; - - assert(s->locty_number == locty); - - if (is_selftest_done) { - for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { - tis->loc[locty].sts |= TPM_TIS_STS_SELFTEST_DONE; - } - } - - qemu_bh_schedule(tis->bh); -} - -/* - * Read a byte of response data - */ -static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty) -{ - TPMTISEmuState *tis = &s->s.tis; - uint32_t ret = TPM_TIS_NO_DATA_BYTE; - uint16_t len; - - if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { - len = tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer); - - ret = tis->loc[locty].r_buffer.buffer[tis->loc[locty].r_offset++]; - if (tis->loc[locty].r_offset >= len) { - /* got last byte */ - tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_VALID); -#ifdef RAISE_STS_IRQ - tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); -#endif - } - DPRINTF("tpm_tis: tpm_tis_data_read byte 0x%02x [%d]\n", - ret, tis->loc[locty].r_offset-1); - } - - return ret; -} - -#ifdef DEBUG_TIS -static void tpm_tis_dump_state(void *opaque, hwaddr addr) -{ - static const unsigned regs[] = { - TPM_TIS_REG_ACCESS, - TPM_TIS_REG_INT_ENABLE, - TPM_TIS_REG_INT_VECTOR, - TPM_TIS_REG_INT_STATUS, - TPM_TIS_REG_INTF_CAPABILITY, - TPM_TIS_REG_STS, - TPM_TIS_REG_DID_VID, - TPM_TIS_REG_RID, - 0xfff}; - int idx; - uint8_t locty = tpm_tis_locality_from_addr(addr); - hwaddr base = addr & ~0xfff; - TPMState *s = opaque; - TPMTISEmuState *tis = &s->s.tis; - - DPRINTF("tpm_tis: active locality : %d\n" - "tpm_tis: state of locality %d : %d\n" - "tpm_tis: register dump:\n", - tis->active_locty, - locty, tis->loc[locty].state); - - for (idx = 0; regs[idx] != 0xfff; idx++) { - DPRINTF("tpm_tis: 0x%04x : 0x%08x\n", regs[idx], - (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4)); - } - - DPRINTF("tpm_tis: read offset : %d\n" - "tpm_tis: result buffer : ", - tis->loc[locty].r_offset); - for (idx = 0; - idx < tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer); - idx++) { - DPRINTF("%c%02x%s", - tis->loc[locty].r_offset == idx ? '>' : ' ', - tis->loc[locty].r_buffer.buffer[idx], - ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); - } - DPRINTF("\n" - "tpm_tis: write offset : %d\n" - "tpm_tis: request buffer: ", - tis->loc[locty].w_offset); - for (idx = 0; - idx < tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer); - idx++) { - DPRINTF("%c%02x%s", - tis->loc[locty].w_offset == idx ? '>' : ' ', - tis->loc[locty].w_buffer.buffer[idx], - ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); - } - DPRINTF("\n"); -} -#endif - -/* - * Read a register of the TIS interface - * See specs pages 33-63 for description of the registers - */ -static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, - unsigned size) -{ - TPMState *s = opaque; - TPMTISEmuState *tis = &s->s.tis; - uint16_t offset = addr & 0xffc; - uint8_t shift = (addr & 0x3) * 8; - uint32_t val = 0xffffffff; - uint8_t locty = tpm_tis_locality_from_addr(addr); - uint32_t avail; - uint8_t v; - - if (tpm_backend_had_startup_error(s->be_driver)) { - return val; - } - - switch (offset) { - case TPM_TIS_REG_ACCESS: - /* never show the SEIZE flag even though we use it internally */ - val = tis->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE; - /* the pending flag is always calculated */ - if (tpm_tis_check_request_use_except(s, locty)) { - val |= TPM_TIS_ACCESS_PENDING_REQUEST; - } - val |= !tpm_backend_get_tpm_established_flag(s->be_driver); - break; - case TPM_TIS_REG_INT_ENABLE: - val = tis->loc[locty].inte; - break; - case TPM_TIS_REG_INT_VECTOR: - val = tis->irq_num; - break; - case TPM_TIS_REG_INT_STATUS: - val = tis->loc[locty].ints; - break; - case TPM_TIS_REG_INTF_CAPABILITY: - switch (s->be_tpm_version) { - case TPM_VERSION_UNSPEC: - val = 0; - break; - case TPM_VERSION_1_2: - val = TPM_TIS_CAPABILITIES_SUPPORTED1_3; - break; - case TPM_VERSION_2_0: - val = TPM_TIS_CAPABILITIES_SUPPORTED2_0; - break; - } - break; - case TPM_TIS_REG_STS: - if (tis->active_locty == locty) { - if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { - val = TPM_TIS_BURST_COUNT( - tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer) - - tis->loc[locty].r_offset) | tis->loc[locty].sts; - } else { - avail = tis->loc[locty].w_buffer.size - - tis->loc[locty].w_offset; - /* - * byte-sized reads should not return 0x00 for 0x100 - * available bytes. - */ - if (size == 1 && avail > 0xff) { - avail = 0xff; - } - val = TPM_TIS_BURST_COUNT(avail) | tis->loc[locty].sts; - } - } - break; - case TPM_TIS_REG_DATA_FIFO: - case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: - if (tis->active_locty == locty) { - if (size > 4 - (addr & 0x3)) { - /* prevent access beyond FIFO */ - size = 4 - (addr & 0x3); - } - val = 0; - shift = 0; - while (size > 0) { - switch (tis->loc[locty].state) { - case TPM_TIS_STATE_COMPLETION: - v = tpm_tis_data_read(s, locty); - break; - default: - v = TPM_TIS_NO_DATA_BYTE; - break; - } - val |= (v << shift); - shift += 8; - size--; - } - shift = 0; /* no more adjustments */ - } - break; - case TPM_TIS_REG_INTERFACE_ID: - val = tis->loc[locty].iface_id; - break; - case TPM_TIS_REG_DID_VID: - val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID; - break; - case TPM_TIS_REG_RID: - val = TPM_TIS_TPM_RID; - break; -#ifdef DEBUG_TIS - case TPM_TIS_REG_DEBUG: - tpm_tis_dump_state(opaque, addr); - break; -#endif - } - - if (shift) { - val >>= shift; - } - - DPRINTF("tpm_tis: read.%u(%08x) = %08x\n", size, (int)addr, (int)val); - - return val; -} - -/* - * Write a value to a register of the TIS interface - * See specs pages 33-63 for description of the registers - */ -static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr, - uint64_t val, unsigned size, - bool hw_access) -{ - TPMState *s = opaque; - TPMTISEmuState *tis = &s->s.tis; - uint16_t off = addr & 0xffc; - uint8_t shift = (addr & 0x3) * 8; - uint8_t locty = tpm_tis_locality_from_addr(addr); - uint8_t active_locty, l; - int c, set_new_locty = 1; - uint16_t len; - uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0); - - DPRINTF("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (int)val); - - if (locty == 4 && !hw_access) { - DPRINTF("tpm_tis: Access to locality 4 only allowed from hardware\n"); - return; - } - - if (tpm_backend_had_startup_error(s->be_driver)) { - return; - } - - val &= mask; - - if (shift) { - val <<= shift; - mask <<= shift; - } - - mask ^= 0xffffffff; - - switch (off) { - case TPM_TIS_REG_ACCESS: - - if ((val & TPM_TIS_ACCESS_SEIZE)) { - val &= ~(TPM_TIS_ACCESS_REQUEST_USE | - TPM_TIS_ACCESS_ACTIVE_LOCALITY); - } - - active_locty = tis->active_locty; - - if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) { - /* give up locality if currently owned */ - if (tis->active_locty == locty) { - DPRINTF("tpm_tis: Releasing locality %d\n", locty); - - uint8_t newlocty = TPM_TIS_NO_LOCALITY; - /* anybody wants the locality ? */ - for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) { - if ((tis->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) { - DPRINTF("tpm_tis: Locality %d requests use.\n", c); - newlocty = c; - break; - } - } - DPRINTF("tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: " - "Next active locality: %d\n", - newlocty); - - if (TPM_TIS_IS_VALID_LOCTY(newlocty)) { - set_new_locty = 0; - tpm_tis_prep_abort(s, locty, newlocty); - } else { - active_locty = TPM_TIS_NO_LOCALITY; - } - } else { - /* not currently the owner; clear a pending request */ - tis->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE; - } - } - - if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) { - tis->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED; - } - - if ((val & TPM_TIS_ACCESS_SEIZE)) { - /* - * allow seize if a locality is active and the requesting - * locality is higher than the one that's active - * OR - * allow seize for requesting locality if no locality is - * active - */ - while ((TPM_TIS_IS_VALID_LOCTY(tis->active_locty) && - locty > tis->active_locty) || - !TPM_TIS_IS_VALID_LOCTY(tis->active_locty)) { - bool higher_seize = FALSE; - - /* already a pending SEIZE ? */ - if ((tis->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) { - break; - } - - /* check for ongoing seize by a higher locality */ - for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) { - if ((tis->loc[l].access & TPM_TIS_ACCESS_SEIZE)) { - higher_seize = TRUE; - break; - } - } - - if (higher_seize) { - break; - } - - /* cancel any seize by a lower locality */ - for (l = 0; l < locty - 1; l++) { - tis->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE; - } - - tis->loc[locty].access |= TPM_TIS_ACCESS_SEIZE; - DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: " - "Locality %d seized from locality %d\n", - locty, tis->active_locty); - DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n"); - set_new_locty = 0; - tpm_tis_prep_abort(s, tis->active_locty, locty); - break; - } - } - - if ((val & TPM_TIS_ACCESS_REQUEST_USE)) { - if (tis->active_locty != locty) { - if (TPM_TIS_IS_VALID_LOCTY(tis->active_locty)) { - tis->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE; - } else { - /* no locality active -> make this one active now */ - active_locty = locty; - } - } - } - - if (set_new_locty) { - tpm_tis_new_active_locality(s, active_locty); - } - - break; - case TPM_TIS_REG_INT_ENABLE: - if (tis->active_locty != locty) { - break; - } - - tis->loc[locty].inte &= mask; - tis->loc[locty].inte |= (val & (TPM_TIS_INT_ENABLED | - TPM_TIS_INT_POLARITY_MASK | - TPM_TIS_INTERRUPTS_SUPPORTED)); - break; - case TPM_TIS_REG_INT_VECTOR: - /* hard wired -- ignore */ - break; - case TPM_TIS_REG_INT_STATUS: - if (tis->active_locty != locty) { - break; - } - - /* clearing of interrupt flags */ - if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) && - (tis->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) { - tis->loc[locty].ints &= ~val; - if (tis->loc[locty].ints == 0) { - qemu_irq_lower(tis->irq); - DPRINTF("tpm_tis: Lowering IRQ\n"); - } - } - tis->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED); - break; - case TPM_TIS_REG_STS: - if (tis->active_locty != locty) { - break; - } - - if (s->be_tpm_version == TPM_VERSION_2_0) { - /* some flags that are only supported for TPM 2 */ - if (val & TPM_TIS_STS_COMMAND_CANCEL) { - if (tis->loc[locty].state == TPM_TIS_STATE_EXECUTION) { - /* - * request the backend to cancel. Some backends may not - * support it - */ - tpm_backend_cancel_cmd(s->be_driver); - } - } - - if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) { - if (locty == 3 || locty == 4) { - tpm_backend_reset_tpm_established_flag(s->be_driver, locty); - } - } - } - - val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO | - TPM_TIS_STS_RESPONSE_RETRY); - - if (val == TPM_TIS_STS_COMMAND_READY) { - switch (tis->loc[locty].state) { - - case TPM_TIS_STATE_READY: - tis->loc[locty].w_offset = 0; - tis->loc[locty].r_offset = 0; - break; - - case TPM_TIS_STATE_IDLE: - tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_COMMAND_READY); - tis->loc[locty].state = TPM_TIS_STATE_READY; - tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); - break; - - case TPM_TIS_STATE_EXECUTION: - case TPM_TIS_STATE_RECEPTION: - /* abort currently running command */ - DPRINTF("tpm_tis: %s: Initiating abort.\n", - __func__); - tpm_tis_prep_abort(s, locty, locty); - break; - - case TPM_TIS_STATE_COMPLETION: - tis->loc[locty].w_offset = 0; - tis->loc[locty].r_offset = 0; - /* shortcut to ready state with C/R set */ - tis->loc[locty].state = TPM_TIS_STATE_READY; - if (!(tis->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) { - tpm_tis_sts_set(&tis->loc[locty], - TPM_TIS_STS_COMMAND_READY); - tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); - } - tis->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE); - break; - - } - } else if (val == TPM_TIS_STS_TPM_GO) { - switch (tis->loc[locty].state) { - case TPM_TIS_STATE_RECEPTION: - if ((tis->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) { - tpm_tis_tpm_send(s, locty); - } - break; - default: - /* ignore */ - break; - } - } else if (val == TPM_TIS_STS_RESPONSE_RETRY) { - switch (tis->loc[locty].state) { - case TPM_TIS_STATE_COMPLETION: - tis->loc[locty].r_offset = 0; - tpm_tis_sts_set(&tis->loc[locty], - TPM_TIS_STS_VALID| - TPM_TIS_STS_DATA_AVAILABLE); - break; - default: - /* ignore */ - break; - } - } - break; - case TPM_TIS_REG_DATA_FIFO: - case TPM_TIS_REG_DATA_XFIFO ... TPM_TIS_REG_DATA_XFIFO_END: - /* data fifo */ - if (tis->active_locty != locty) { - break; - } - - if (tis->loc[locty].state == TPM_TIS_STATE_IDLE || - tis->loc[locty].state == TPM_TIS_STATE_EXECUTION || - tis->loc[locty].state == TPM_TIS_STATE_COMPLETION) { - /* drop the byte */ - } else { - DPRINTF("tpm_tis: Data to send to TPM: %08x (size=%d)\n", - (int)val, size); - if (tis->loc[locty].state == TPM_TIS_STATE_READY) { - tis->loc[locty].state = TPM_TIS_STATE_RECEPTION; - tpm_tis_sts_set(&tis->loc[locty], - TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); - } - - val >>= shift; - if (size > 4 - (addr & 0x3)) { - /* prevent access beyond FIFO */ - size = 4 - (addr & 0x3); - } - - while ((tis->loc[locty].sts & TPM_TIS_STS_EXPECT) && size > 0) { - if (tis->loc[locty].w_offset < tis->loc[locty].w_buffer.size) { - tis->loc[locty].w_buffer. - buffer[tis->loc[locty].w_offset++] = (uint8_t)val; - val >>= 8; - size--; - } else { - tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_VALID); - } - } - - /* check for complete packet */ - if (tis->loc[locty].w_offset > 5 && - (tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) { - /* we have a packet length - see if we have all of it */ -#ifdef RAISE_STS_IRQ - bool need_irq = !(tis->loc[locty].sts & TPM_TIS_STS_VALID); -#endif - len = tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer); - if (len > tis->loc[locty].w_offset) { - tpm_tis_sts_set(&tis->loc[locty], - TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID); - } else { - /* packet complete */ - tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_VALID); - } -#ifdef RAISE_STS_IRQ - if (need_irq) { - tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); - } -#endif - } - } - break; - case TPM_TIS_REG_INTERFACE_ID: - if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) { - for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { - tis->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK; - } - } - break; - } -} - -static void tpm_tis_mmio_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - tpm_tis_mmio_write_intern(opaque, addr, val, size, false); -} - -static const MemoryRegionOps tpm_tis_memory_ops = { - .read = tpm_tis_mmio_read, - .write = tpm_tis_mmio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 4, - }, -}; - -static int tpm_tis_do_startup_tpm(TPMState *s) -{ - return tpm_backend_startup_tpm(s->be_driver); -} - -/* - * Get the TPMVersion of the backend device being used - */ -TPMVersion tpm_tis_get_tpm_version(Object *obj) -{ - TPMState *s = TPM(obj); - - return tpm_backend_get_tpm_version(s->be_driver); -} - -/* - * This function is called when the machine starts, resets or due to - * S3 resume. - */ -static void tpm_tis_reset(DeviceState *dev) -{ - TPMState *s = TPM(dev); - TPMTISEmuState *tis = &s->s.tis; - int c; - - s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver); - - tpm_backend_reset(s->be_driver); - - tis->active_locty = TPM_TIS_NO_LOCALITY; - tis->next_locty = TPM_TIS_NO_LOCALITY; - tis->aborting_locty = TPM_TIS_NO_LOCALITY; - - for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) { - tis->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS; - switch (s->be_tpm_version) { - case TPM_VERSION_UNSPEC: - break; - case TPM_VERSION_1_2: - tis->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2; - tis->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3; - break; - case TPM_VERSION_2_0: - tis->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0; - tis->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0; - break; - } - tis->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL; - tis->loc[c].ints = 0; - tis->loc[c].state = TPM_TIS_STATE_IDLE; - - tis->loc[c].w_offset = 0; - tpm_backend_realloc_buffer(s->be_driver, &tis->loc[c].w_buffer); - tis->loc[c].r_offset = 0; - tpm_backend_realloc_buffer(s->be_driver, &tis->loc[c].r_buffer); - } - - tpm_tis_do_startup_tpm(s); -} - -static const VMStateDescription vmstate_tpm_tis = { - .name = "tpm", - .unmigratable = 1, -}; - -static Property tpm_tis_properties[] = { - DEFINE_PROP_UINT32("irq", TPMState, - s.tis.irq_num, TPM_TIS_IRQ), - DEFINE_PROP_STRING("tpmdev", TPMState, backend), - DEFINE_PROP_END_OF_LIST(), -}; - -static void tpm_tis_realizefn(DeviceState *dev, Error **errp) -{ - TPMState *s = TPM(dev); - TPMTISEmuState *tis = &s->s.tis; - - s->be_driver = qemu_find_tpm(s->backend); - if (!s->be_driver) { - error_setg(errp, "tpm_tis: backend driver with id %s could not be " - "found", s->backend); - return; - } - - s->be_driver->fe_model = TPM_MODEL_TPM_TIS; - - if (tpm_backend_init(s->be_driver, s, tpm_tis_receive_cb)) { - error_setg(errp, "tpm_tis: backend driver with id %s could not be " - "initialized", s->backend); - return; - } - - if (tis->irq_num > 15) { - error_setg(errp, "tpm_tis: IRQ %d for TPM TIS is outside valid range " - "of 0 to 15", tis->irq_num); - return; - } - - tis->bh = qemu_bh_new(tpm_tis_receive_bh, s); - - isa_init_irq(&s->busdev, &tis->irq, tis->irq_num); - - memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)), - TPM_TIS_ADDR_BASE, &s->mmio); -} - -static void tpm_tis_initfn(Object *obj) -{ - TPMState *s = TPM(obj); - - memory_region_init_io(&s->mmio, OBJECT(s), &tpm_tis_memory_ops, - s, "tpm-tis-mmio", - TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT); -} - -static void tpm_tis_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = tpm_tis_realizefn; - dc->props = tpm_tis_properties; - dc->reset = tpm_tis_reset; - dc->vmsd = &vmstate_tpm_tis; -} - -static const TypeInfo tpm_tis_info = { - .name = TYPE_TPM_TIS, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(TPMState), - .instance_init = tpm_tis_initfn, - .class_init = tpm_tis_class_init, -}; - -static void tpm_tis_register(void) -{ - type_register_static(&tpm_tis_info); - tpm_register_model(TPM_MODEL_TPM_TIS); -} - -type_init(tpm_tis_register) diff --git a/qemu/hw/tpm/tpm_tis.h b/qemu/hw/tpm/tpm_tis.h deleted file mode 100644 index a1df41fa2..000000000 --- a/qemu/hw/tpm/tpm_tis.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * tpm_tis.h - QEMU's TPM TIS interface emulator - * - * Copyright (C) 2006, 2010-2013 IBM Corporation - * - * Authors: - * Stefan Berger - * David Safford - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - * Implementation of the TIS interface according to specs found at - * http://www.trustedcomputinggroup.org - * - */ -#ifndef TPM_TPM_TIS_H -#define TPM_TPM_TIS_H - -#include "hw/isa/isa.h" -#include "hw/acpi/tpm.h" -#include "qemu-common.h" - -#define TPM_TIS_NUM_LOCALITIES 5 /* per spec */ -#define TPM_TIS_LOCALITY_SHIFT 12 -#define TPM_TIS_NO_LOCALITY 0xff - -#define TPM_TIS_IS_VALID_LOCTY(x) ((x) < TPM_TIS_NUM_LOCALITIES) - -#define TPM_TIS_BUFFER_MAX 4096 - -typedef enum { - TPM_TIS_STATE_IDLE = 0, - TPM_TIS_STATE_READY, - TPM_TIS_STATE_COMPLETION, - TPM_TIS_STATE_EXECUTION, - TPM_TIS_STATE_RECEPTION, -} TPMTISState; - -/* locality data -- all fields are persisted */ -typedef struct TPMLocality { - TPMTISState state; - uint8_t access; - uint32_t sts; - uint32_t iface_id; - uint32_t inte; - uint32_t ints; - - uint16_t w_offset; - uint16_t r_offset; - TPMSizedBuffer w_buffer; - TPMSizedBuffer r_buffer; -} TPMLocality; - -typedef struct TPMTISEmuState { - QEMUBH *bh; - uint32_t offset; - uint8_t buf[TPM_TIS_BUFFER_MAX]; - - uint8_t active_locty; - uint8_t aborting_locty; - uint8_t next_locty; - - TPMLocality loc[TPM_TIS_NUM_LOCALITIES]; - - qemu_irq irq; - uint32_t irq_num; -} TPMTISEmuState; - -#endif /* TPM_TPM_TIS_H */ diff --git a/qemu/hw/tpm/tpm_util.c b/qemu/hw/tpm/tpm_util.c deleted file mode 100644 index 7b3542972..000000000 --- a/qemu/hw/tpm/tpm_util.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * TPM utility functions - * - * Copyright (c) 2010 - 2015 IBM Corporation - * Authors: - * Stefan Berger - * - * 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 - */ - -#include "qemu/osdep.h" -#include "tpm_util.h" -#include "tpm_int.h" - -/* - * A basic test of a TPM device. We expect a well formatted response header - * (error response is fine) within one second. - */ -static int tpm_util_test(int fd, - unsigned char *request, - size_t requestlen, - uint16_t *return_tag) -{ - struct tpm_resp_hdr *resp; - fd_set readfds; - int n; - struct timeval tv = { - .tv_sec = 1, - .tv_usec = 0, - }; - unsigned char buf[1024]; - - n = write(fd, request, requestlen); - if (n < 0) { - return errno; - } - if (n != requestlen) { - return EFAULT; - } - - FD_ZERO(&readfds); - FD_SET(fd, &readfds); - - /* wait for a second */ - n = select(fd + 1, &readfds, NULL, NULL, &tv); - if (n != 1) { - return errno; - } - - n = read(fd, &buf, sizeof(buf)); - if (n < sizeof(struct tpm_resp_hdr)) { - return EFAULT; - } - - resp = (struct tpm_resp_hdr *)buf; - /* check the header */ - if (be32_to_cpu(resp->len) != n) { - return EBADMSG; - } - - *return_tag = be16_to_cpu(resp->tag); - - return 0; -} - -/* - * Probe for the TPM device in the back - * Returns 0 on success with the version of the probed TPM set, 1 on failure. - */ -int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version) -{ - /* - * Sending a TPM1.2 command to a TPM2 should return a TPM1.2 - * header (tag = 0xc4) and error code (TPM_BADTAG = 0x1e) - * - * Sending a TPM2 command to a TPM 2 will give a TPM 2 tag in the - * header. - * Sending a TPM2 command to a TPM 1.2 will give a TPM 1.2 tag - * in the header and an error code. - */ - const struct tpm_req_hdr test_req = { - .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), - .len = cpu_to_be32(sizeof(test_req)), - .ordinal = cpu_to_be32(TPM_ORD_GetTicks), - }; - - const struct tpm_req_hdr test_req_tpm2 = { - .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), - .len = cpu_to_be32(sizeof(test_req_tpm2)), - .ordinal = cpu_to_be32(TPM2_CC_ReadClock), - }; - uint16_t return_tag; - int ret; - - /* Send TPM 2 command */ - ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req_tpm2, - sizeof(test_req_tpm2), &return_tag); - /* TPM 2 would respond with a tag of TPM2_ST_NO_SESSIONS */ - if (!ret && return_tag == TPM2_ST_NO_SESSIONS) { - *tpm_version = TPM_VERSION_2_0; - return 0; - } - - /* Send TPM 1.2 command */ - ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req, - sizeof(test_req), &return_tag); - if (!ret && return_tag == TPM_TAG_RSP_COMMAND) { - *tpm_version = TPM_VERSION_1_2; - /* this is a TPM 1.2 */ - return 0; - } - - *tpm_version = TPM_VERSION_UNSPEC; - - return 1; -} diff --git a/qemu/hw/tpm/tpm_util.h b/qemu/hw/tpm/tpm_util.h deleted file mode 100644 index e7f354a52..000000000 --- a/qemu/hw/tpm/tpm_util.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * TPM utility functions - * - * Copyright (c) 2010 - 2015 IBM Corporation - * Authors: - * Stefan Berger - * - * 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 - */ -#ifndef TPM_TPM_UTILS_H -#define TPM_TPM_UTILS_H - -#include "sysemu/tpm_backend.h" - -int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version); - -#endif /* TPM_TPM_UTILS_H */ diff --git a/qemu/hw/tricore/Makefile.objs b/qemu/hw/tricore/Makefile.objs deleted file mode 100644 index 435e095cf..000000000 --- a/qemu/hw/tricore/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -obj-y += tricore_testboard.o diff --git a/qemu/hw/tricore/tricore_testboard.c b/qemu/hw/tricore/tricore_testboard.c deleted file mode 100644 index 8d3520f5b..000000000 --- a/qemu/hw/tricore/tricore_testboard.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * TriCore Baseboard System emulation. - * - * Copyright (c) 2013-2014 Bastian Koppelmann C-Lab/University Paderborn - * - * 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 . - */ - - -#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" -#include "sysemu/sysemu.h" -#include "hw/boards.h" -#include "hw/loader.h" -#include "sysemu/block-backend.h" -#include "exec/address-spaces.h" -#include "hw/block/flash.h" -#include "elf.h" -#include "hw/tricore/tricore.h" -#include "qemu/error-report.h" - - -/* Board init. */ - -static struct tricore_boot_info tricoretb_binfo; - -static void tricore_load_kernel(CPUTriCoreState *env) -{ - uint64_t entry; - long kernel_size; - - kernel_size = load_elf(tricoretb_binfo.kernel_filename, NULL, - NULL, (uint64_t *)&entry, NULL, - NULL, 0, - EM_TRICORE, 1, 0); - if (kernel_size <= 0) { - error_report("qemu: no kernel file '%s'", - tricoretb_binfo.kernel_filename); - exit(1); - } - env->PC = entry; - -} - -static void tricore_testboard_init(MachineState *machine, int board_id) -{ - TriCoreCPU *cpu; - CPUTriCoreState *env; - - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *ext_cram = g_new(MemoryRegion, 1); - MemoryRegion *ext_dram = g_new(MemoryRegion, 1); - MemoryRegion *int_cram = g_new(MemoryRegion, 1); - MemoryRegion *int_dram = g_new(MemoryRegion, 1); - MemoryRegion *pcp_data = g_new(MemoryRegion, 1); - MemoryRegion *pcp_text = g_new(MemoryRegion, 1); - - if (!machine->cpu_model) { - machine->cpu_model = "tc1796"; - } - cpu = cpu_tricore_init(machine->cpu_model); - if (!cpu) { - error_report("Unable to find CPU definition"); - exit(1); - } - env = &cpu->env; - 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_fatal); - vmstate_register_ram_global(ext_dram); - 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_fatal); - vmstate_register_ram_global(int_dram); - 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_fatal); - vmstate_register_ram_global(pcp_text); - - memory_region_add_subregion(sysmem, 0x80000000, ext_cram); - memory_region_add_subregion(sysmem, 0xa1000000, ext_dram); - memory_region_add_subregion(sysmem, 0xd4000000, int_cram); - memory_region_add_subregion(sysmem, 0xd0000000, int_dram); - memory_region_add_subregion(sysmem, 0xf0050000, pcp_data); - memory_region_add_subregion(sysmem, 0xf0060000, pcp_text); - - tricoretb_binfo.ram_size = machine->ram_size; - tricoretb_binfo.kernel_filename = machine->kernel_filename; - - if (machine->kernel_filename) { - tricore_load_kernel(env); - } -} - -static void tricoreboard_init(MachineState *machine) -{ - tricore_testboard_init(machine, 0x183); -} - -static void ttb_machine_init(MachineClass *mc) -{ - mc->desc = "a minimal TriCore board"; - mc->init = tricoreboard_init; - mc->is_default = 0; -} - -DEFINE_MACHINE("tricore_testboard", ttb_machine_init) diff --git a/qemu/hw/unicore32/Makefile.objs b/qemu/hw/unicore32/Makefile.objs deleted file mode 100644 index e0fd62852..000000000 --- a/qemu/hw/unicore32/Makefile.objs +++ /dev/null @@ -1,4 +0,0 @@ -# For UniCore32 machines and boards - -# PKUnity-v3 SoC and board information -obj-${CONFIG_PUV3} += puv3.o diff --git a/qemu/hw/unicore32/puv3.c b/qemu/hw/unicore32/puv3.c deleted file mode 100644 index 31cd17101..000000000 --- a/qemu/hw/unicore32/puv3.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Generic PKUnity SoC machine and board descriptor - * - * Copyright (C) 2010-2012 Guan Xuetao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation, or any later version. - * 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" -#include "exec/address-spaces.h" -#include "hw/sysbus.h" -#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 -#include "hw/unicore32/puv3.h" - -#define KERNEL_LOAD_ADDR 0x03000000 -#define KERNEL_MAX_SIZE 0x00800000 /* Just a guess */ - -static void puv3_intc_cpu_handler(void *opaque, int irq, int level) -{ - UniCore32CPU *cpu = opaque; - CPUState *cs = CPU(cpu); - - assert(irq == 0); - if (level) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } -} - -static void puv3_soc_init(CPUUniCore32State *env) -{ - qemu_irq cpu_intc, irqs[PUV3_IRQS_NR]; - DeviceState *dev; - MemoryRegion *i8042 = g_new(MemoryRegion, 1); - int i; - - /* Initialize interrupt controller */ - cpu_intc = qemu_allocate_irq(puv3_intc_cpu_handler, - uc32_env_get_cpu(env), 0); - dev = sysbus_create_simple("puv3_intc", PUV3_INTC_BASE, cpu_intc); - for (i = 0; i < PUV3_IRQS_NR; i++) { - irqs[i] = qdev_get_gpio_in(dev, i); - } - - /* Initialize minimal necessary devices for kernel booting */ - sysbus_create_simple("puv3_pm", PUV3_PM_BASE, NULL); - sysbus_create_simple("puv3_dma", PUV3_DMA_BASE, NULL); - sysbus_create_simple("puv3_ost", PUV3_OST_BASE, irqs[PUV3_IRQS_OST0]); - sysbus_create_varargs("puv3_gpio", PUV3_GPIO_BASE, - irqs[PUV3_IRQS_GPIOLOW0], irqs[PUV3_IRQS_GPIOLOW1], - irqs[PUV3_IRQS_GPIOLOW2], irqs[PUV3_IRQS_GPIOLOW3], - irqs[PUV3_IRQS_GPIOLOW4], irqs[PUV3_IRQS_GPIOLOW5], - irqs[PUV3_IRQS_GPIOLOW6], irqs[PUV3_IRQS_GPIOLOW7], - irqs[PUV3_IRQS_GPIOHIGH], NULL); - - /* Keyboard (i8042), mouse disabled for nographic */ - i8042_mm_init(irqs[PUV3_IRQS_PS2_KBD], NULL, i8042, PUV3_REGS_OFFSET, 4); - memory_region_add_subregion(get_system_memory(), PUV3_PS2_BASE, i8042); -} - -static void puv3_board_init(CPUUniCore32State *env, ram_addr_t ram_size) -{ - MemoryRegion *ram_memory = g_new(MemoryRegion, 1); - - /* SDRAM at address zero. */ - memory_region_init_ram(ram_memory, NULL, "puv3.ram", ram_size, - &error_fatal); - vmstate_register_ram_global(ram_memory); - memory_region_add_subregion(get_system_memory(), 0, ram_memory); -} - -static const GraphicHwOps no_ops; - -static void puv3_load_kernel(const char *kernel_filename) -{ - int size; - - if (kernel_filename == NULL && qtest_enabled()) { - return; - } - assert(kernel_filename != NULL); - - /* only zImage format supported */ - size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR, - KERNEL_MAX_SIZE); - if (size < 0) { - error_report("Load kernel error: '%s'", kernel_filename); - exit(1); - } - - /* cheat curses that we have a graphic console, only under ocd console */ - graphic_console_init(NULL, 0, &no_ops, NULL); -} - -static void puv3_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 *initrd_filename = machine->initrd_filename; - CPUUniCore32State *env; - UniCore32CPU *cpu; - - if (initrd_filename) { - error_report("Please use kernel built-in initramdisk"); - exit(1); - } - - if (!cpu_model) { - cpu_model = "UniCore-II"; - } - - cpu = uc32_cpu_init(cpu_model); - if (!cpu) { - error_report("Unable to find CPU definition"); - exit(1); - } - env = &cpu->env; - - puv3_soc_init(env); - puv3_board_init(env, ram_size); - puv3_load_kernel(kernel_filename); -} - -static void puv3_machine_init(MachineClass *mc) -{ - mc->desc = "PKUnity Version-3 based on UniCore32"; - mc->init = puv3_init; - mc->is_default = 1; -} - -DEFINE_MACHINE("puv3", puv3_machine_init) diff --git a/qemu/hw/usb/Makefile.objs b/qemu/hw/usb/Makefile.objs deleted file mode 100644 index 2717027d3..000000000 --- a/qemu/hw/usb/Makefile.objs +++ /dev/null @@ -1,40 +0,0 @@ -# usb subsystem core -common-obj-y += core.o combined-packet.o bus.o libhw.o -common-obj-$(CONFIG_USB) += desc.o desc-msos.o - -# usb host adapters -common-obj-$(CONFIG_USB_UHCI) += hcd-uhci.o -common-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o -common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o hcd-ehci-pci.o -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 -common-obj-$(CONFIG_USB_TABLET_WACOM) += dev-wacom.o -common-obj-$(CONFIG_USB_STORAGE_BOT) += dev-storage.o -common-obj-$(CONFIG_USB_STORAGE_UAS) += dev-uas.o -common-obj-$(CONFIG_USB_AUDIO) += dev-audio.o -common-obj-$(CONFIG_USB_SERIAL) += dev-serial.o -common-obj-$(CONFIG_USB_NETWORK) += dev-network.o -common-obj-$(CONFIG_USB_BLUETOOTH) += dev-bluetooth.o - -ifeq ($(CONFIG_USB_SMARTCARD),y) -common-obj-y += dev-smartcard-reader.o -common-obj-$(CONFIG_SMARTCARD) += ccid-card-passthru.o -common-obj-$(CONFIG_SMARTCARD) += ccid-card-emulated.o -endif - -ifeq ($(CONFIG_POSIX),y) -common-obj-$(CONFIG_USB_STORAGE_MTP) += dev-mtp.o -endif - -# usb redirection -common-obj-$(CONFIG_USB_REDIR) += redirect.o quirks.o - -# usb pass-through -common-obj-y += $(patsubst %,host-%.o,$(HOST_USB)) diff --git a/qemu/hw/usb/bus.c b/qemu/hw/usb/bus.c deleted file mode 100644 index 16c3461d9..000000000 --- a/qemu/hw/usb/bus.c +++ /dev/null @@ -1,763 +0,0 @@ -#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); - -static char *usb_get_dev_path(DeviceState *dev); -static char *usb_get_fw_dev_path(DeviceState *qdev); -static void usb_qdev_unrealize(DeviceState *qdev, Error **errp); - -static Property usb_props[] = { - DEFINE_PROP_STRING("port", USBDevice, port_path), - DEFINE_PROP_STRING("serial", USBDevice, serial), - DEFINE_PROP_BIT("full-path", USBDevice, flags, - USB_DEV_FLAG_FULL_PATH, true), - DEFINE_PROP_BIT("msos-desc", USBDevice, flags, - USB_DEV_FLAG_MSOS_DESC_ENABLE, true), - DEFINE_PROP_END_OF_LIST() -}; - -static void usb_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - - k->print_dev = usb_bus_dev_print; - k->get_dev_path = usb_get_dev_path; - k->get_fw_dev_path = usb_get_fw_dev_path; - hc->unplug = qdev_simple_device_unplug_cb; -} - -static const TypeInfo usb_bus_info = { - .name = TYPE_USB_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(USBBus), - .class_init = usb_bus_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - -static int next_usb_bus = 0; -static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses); - -static int usb_device_post_load(void *opaque, int version_id) -{ - USBDevice *dev = opaque; - - if (dev->state == USB_STATE_NOTATTACHED) { - dev->attached = 0; - } else { - dev->attached = 1; - } - if (dev->setup_index < 0 || - dev->setup_len < 0 || - dev->setup_index > dev->setup_len || - dev->setup_len > sizeof(dev->data_buf)) { - return -EINVAL; - } - return 0; -} - -const VMStateDescription vmstate_usb_device = { - .name = "USBDevice", - .version_id = 1, - .minimum_version_id = 1, - .post_load = usb_device_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(addr, USBDevice), - VMSTATE_INT32(state, USBDevice), - VMSTATE_INT32(remote_wakeup, USBDevice), - VMSTATE_INT32(setup_state, USBDevice), - VMSTATE_INT32(setup_len, USBDevice), - VMSTATE_INT32(setup_index, USBDevice), - VMSTATE_UINT8_ARRAY(setup_buf, USBDevice, 8), - VMSTATE_END_OF_LIST(), - } -}; - -void usb_bus_new(USBBus *bus, size_t bus_size, - USBBusOps *ops, DeviceState *host) -{ - qbus_create_inplace(bus, bus_size, TYPE_USB_BUS, host, NULL); - qbus_set_bus_hotplug_handler(BUS(bus), &error_abort); - bus->ops = ops; - bus->busnr = next_usb_bus++; - QTAILQ_INIT(&bus->free); - QTAILQ_INIT(&bus->used); - QTAILQ_INSERT_TAIL(&busses, bus, next); -} - -void usb_bus_release(USBBus *bus) -{ - assert(next_usb_bus > 0); - - QTAILQ_REMOVE(&busses, bus, next); -} - -USBBus *usb_bus_find(int busnr) -{ - USBBus *bus; - - if (-1 == busnr) - return QTAILQ_FIRST(&busses); - QTAILQ_FOREACH(bus, &busses, next) { - if (bus->busnr == busnr) - return bus; - } - return NULL; -} - -static void usb_device_realize(USBDevice *dev, Error **errp) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - - if (klass->realize) { - klass->realize(dev, errp); - } -} - -USBDevice *usb_device_find_device(USBDevice *dev, uint8_t addr) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->find_device) { - return klass->find_device(dev, addr); - } - return NULL; -} - -static void usb_device_handle_destroy(USBDevice *dev) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->handle_destroy) { - klass->handle_destroy(dev); - } -} - -void usb_device_cancel_packet(USBDevice *dev, USBPacket *p) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->cancel_packet) { - klass->cancel_packet(dev, p); - } -} - -void usb_device_handle_attach(USBDevice *dev) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->handle_attach) { - klass->handle_attach(dev); - } -} - -void usb_device_handle_reset(USBDevice *dev) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->handle_reset) { - klass->handle_reset(dev); - } -} - -void usb_device_handle_control(USBDevice *dev, USBPacket *p, int request, - int value, int index, int length, uint8_t *data) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->handle_control) { - klass->handle_control(dev, p, request, value, index, length, data); - } -} - -void usb_device_handle_data(USBDevice *dev, USBPacket *p) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->handle_data) { - klass->handle_data(dev, p); - } -} - -const char *usb_device_get_product_desc(USBDevice *dev) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - return klass->product_desc; -} - -const USBDesc *usb_device_get_usb_desc(USBDevice *dev) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (dev->usb_desc) { - return dev->usb_desc; - } - return klass->usb_desc; -} - -void usb_device_set_interface(USBDevice *dev, int interface, - int alt_old, int alt_new) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->set_interface) { - klass->set_interface(dev, interface, alt_old, alt_new); - } -} - -void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->flush_ep_queue) { - klass->flush_ep_queue(dev, ep); - } -} - -void usb_device_ep_stopped(USBDevice *dev, USBEndpoint *ep) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->ep_stopped) { - klass->ep_stopped(dev, ep); - } -} - -int usb_device_alloc_streams(USBDevice *dev, USBEndpoint **eps, int nr_eps, - int streams) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->alloc_streams) { - return klass->alloc_streams(dev, eps, nr_eps, streams); - } - return 0; -} - -void usb_device_free_streams(USBDevice *dev, USBEndpoint **eps, int nr_eps) -{ - USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); - if (klass->free_streams) { - klass->free_streams(dev, eps, nr_eps); - } -} - -static void usb_qdev_realize(DeviceState *qdev, Error **errp) -{ - USBDevice *dev = USB_DEVICE(qdev); - Error *local_err = NULL; - - pstrcpy(dev->product_desc, sizeof(dev->product_desc), - usb_device_get_product_desc(dev)); - dev->auto_attach = 1; - QLIST_INIT(&dev->strings); - usb_ep_init(dev); - - usb_claim_port(dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - usb_device_realize(dev, &local_err); - if (local_err) { - usb_release_port(dev); - error_propagate(errp, local_err); - return; - } - - if (dev->auto_attach) { - usb_device_attach(dev, &local_err); - if (local_err) { - usb_qdev_unrealize(qdev, NULL); - error_propagate(errp, local_err); - return; - } - } -} - -static void usb_qdev_unrealize(DeviceState *qdev, Error **errp) -{ - USBDevice *dev = USB_DEVICE(qdev); - - if (dev->attached) { - usb_device_detach(dev); - } - usb_device_handle_destroy(dev); - if (dev->port) { - usb_release_port(dev); - } -} - -typedef struct LegacyUSBFactory -{ - const char *name; - const char *usbdevice_name; - USBDevice *(*usbdevice_init)(USBBus *bus, const char *params); -} LegacyUSBFactory; - -static GSList *legacy_usb_factory; - -void usb_legacy_register(const char *typename, const char *usbdevice_name, - USBDevice *(*usbdevice_init)(USBBus *bus, - const char *params)) -{ - if (usbdevice_name) { - LegacyUSBFactory *f = g_malloc0(sizeof(*f)); - f->name = typename; - f->usbdevice_name = usbdevice_name; - f->usbdevice_init = usbdevice_init; - legacy_usb_factory = g_slist_append(legacy_usb_factory, f); - } -} - -USBDevice *usb_create(USBBus *bus, const char *name) -{ - DeviceState *dev; - - dev = qdev_create(&bus->qbus, name); - return USB_DEVICE(dev); -} - -static USBDevice *usb_try_create_simple(USBBus *bus, const char *name, - Error **errp) -{ - Error *err = NULL; - USBDevice *dev; - - dev = USB_DEVICE(qdev_try_create(&bus->qbus, name)); - if (!dev) { - error_setg(errp, "Failed to create USB device '%s'", name); - return NULL; - } - object_property_set_bool(OBJECT(dev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - error_prepend(errp, "Failed to initialize USB device '%s': ", - name); - object_unparent(OBJECT(dev)); - return NULL; - } - return dev; -} - -USBDevice *usb_create_simple(USBBus *bus, const char *name) -{ - return usb_try_create_simple(bus, name, &error_abort); -} - -static void usb_fill_port(USBPort *port, void *opaque, int index, - USBPortOps *ops, int speedmask) -{ - port->opaque = opaque; - port->index = index; - port->ops = ops; - port->speedmask = speedmask; - usb_port_location(port, NULL, index + 1); -} - -void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, - USBPortOps *ops, int speedmask) -{ - usb_fill_port(port, opaque, index, ops, speedmask); - QTAILQ_INSERT_TAIL(&bus->free, port, next); - bus->nfree++; -} - -void usb_register_companion(const char *masterbus, USBPort *ports[], - uint32_t portcount, uint32_t firstport, - void *opaque, USBPortOps *ops, int speedmask, - Error **errp) -{ - USBBus *bus; - int i; - - QTAILQ_FOREACH(bus, &busses, next) { - if (strcmp(bus->qbus.name, masterbus) == 0) { - break; - } - } - - if (!bus) { - error_setg(errp, "USB bus '%s' not found", masterbus); - return; - } - if (!bus->ops->register_companion) { - error_setg(errp, "Can't use USB bus '%s' as masterbus," - " it doesn't support companion controllers", - masterbus); - return; - } - - for (i = 0; i < portcount; i++) { - usb_fill_port(ports[i], opaque, i, ops, speedmask); - } - - bus->ops->register_companion(bus, ports, portcount, firstport, errp); -} - -void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr) -{ - if (upstream) { - snprintf(downstream->path, sizeof(downstream->path), "%s.%d", - upstream->path, portnr); - downstream->hubcount = upstream->hubcount + 1; - } else { - snprintf(downstream->path, sizeof(downstream->path), "%d", portnr); - downstream->hubcount = 0; - } -} - -void usb_unregister_port(USBBus *bus, USBPort *port) -{ - if (port->dev) { - object_unparent(OBJECT(port->dev)); - } - QTAILQ_REMOVE(&bus->free, port, next); - bus->nfree--; -} - -void usb_claim_port(USBDevice *dev, Error **errp) -{ - USBBus *bus = usb_bus_from_device(dev); - USBPort *port; - - assert(dev->port == NULL); - - if (dev->port_path) { - QTAILQ_FOREACH(port, &bus->free, next) { - if (strcmp(port->path, dev->port_path) == 0) { - break; - } - } - if (port == NULL) { - error_setg(errp, "usb port %s (bus %s) not found (in use?)", - dev->port_path, bus->qbus.name); - return; - } - } else { - if (bus->nfree == 1 && strcmp(object_get_typename(OBJECT(dev)), "usb-hub") != 0) { - /* Create a new hub and chain it on */ - usb_try_create_simple(bus, "usb-hub", NULL); - } - if (bus->nfree == 0) { - error_setg(errp, "tried to attach usb device %s to a bus " - "with no free ports", dev->product_desc); - return; - } - port = QTAILQ_FIRST(&bus->free); - } - trace_usb_port_claim(bus->busnr, port->path); - - QTAILQ_REMOVE(&bus->free, port, next); - bus->nfree--; - - dev->port = port; - port->dev = dev; - - QTAILQ_INSERT_TAIL(&bus->used, port, next); - bus->nused++; -} - -void usb_release_port(USBDevice *dev) -{ - USBBus *bus = usb_bus_from_device(dev); - USBPort *port = dev->port; - - assert(port != NULL); - trace_usb_port_release(bus->busnr, port->path); - - QTAILQ_REMOVE(&bus->used, port, next); - bus->nused--; - - dev->port = NULL; - port->dev = NULL; - - QTAILQ_INSERT_TAIL(&bus->free, port, next); - bus->nfree++; -} - -static void usb_mask_to_str(char *dest, size_t size, - unsigned int speedmask) -{ - static const struct { - unsigned int mask; - const char *name; - } speeds[] = { - { .mask = USB_SPEED_MASK_FULL, .name = "full" }, - { .mask = USB_SPEED_MASK_HIGH, .name = "high" }, - { .mask = USB_SPEED_MASK_SUPER, .name = "super" }, - }; - int i, pos = 0; - - for (i = 0; i < ARRAY_SIZE(speeds); i++) { - if (speeds[i].mask & speedmask) { - pos += snprintf(dest + pos, size - pos, "%s%s", - pos ? "+" : "", - speeds[i].name); - } - } -} - -void usb_check_attach(USBDevice *dev, Error **errp) -{ - USBBus *bus = usb_bus_from_device(dev); - USBPort *port = dev->port; - char devspeed[32], portspeed[32]; - - assert(port != NULL); - assert(!dev->attached); - usb_mask_to_str(devspeed, sizeof(devspeed), dev->speedmask); - usb_mask_to_str(portspeed, sizeof(portspeed), port->speedmask); - trace_usb_port_attach(bus->busnr, port->path, - devspeed, portspeed); - - if (!(port->speedmask & dev->speedmask)) { - error_setg(errp, "Warning: speed mismatch trying to attach" - " usb device \"%s\" (%s speed)" - " to bus \"%s\", port \"%s\" (%s speed)", - dev->product_desc, devspeed, - bus->qbus.name, port->path, portspeed); - return; - } -} - -void usb_device_attach(USBDevice *dev, Error **errp) -{ - USBPort *port = dev->port; - Error *local_err = NULL; - - usb_check_attach(dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - dev->attached++; - usb_attach(port); -} - -int usb_device_detach(USBDevice *dev) -{ - USBBus *bus = usb_bus_from_device(dev); - USBPort *port = dev->port; - - assert(port != NULL); - assert(dev->attached); - trace_usb_port_detach(bus->busnr, port->path); - - usb_detach(port); - dev->attached--; - return 0; -} - -int usb_device_delete_addr(int busnr, int addr) -{ - USBBus *bus; - USBPort *port; - USBDevice *dev; - - bus = usb_bus_find(busnr); - if (!bus) - return -1; - - QTAILQ_FOREACH(port, &bus->used, next) { - if (port->dev->addr == addr) - break; - } - if (!port) - return -1; - dev = port->dev; - - object_unparent(OBJECT(dev)); - return 0; -} - -static const char *usb_speed(unsigned int speed) -{ - static const char *txt[] = { - [ USB_SPEED_LOW ] = "1.5", - [ USB_SPEED_FULL ] = "12", - [ USB_SPEED_HIGH ] = "480", - [ USB_SPEED_SUPER ] = "5000", - }; - if (speed >= ARRAY_SIZE(txt)) - return "?"; - return txt[speed]; -} - -static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) -{ - USBDevice *dev = USB_DEVICE(qdev); - USBBus *bus = usb_bus_from_device(dev); - - monitor_printf(mon, "%*saddr %d.%d, port %s, speed %s, name %s%s\n", - indent, "", bus->busnr, dev->addr, - dev->port ? dev->port->path : "-", - usb_speed(dev->speed), dev->product_desc, - dev->attached ? ", attached" : ""); -} - -static char *usb_get_dev_path(DeviceState *qdev) -{ - USBDevice *dev = USB_DEVICE(qdev); - DeviceState *hcd = qdev->parent_bus->parent; - char *id = NULL; - - if (dev->flags & (1 << USB_DEV_FLAG_FULL_PATH)) { - id = qdev_get_dev_path(hcd); - } - if (id) { - char *ret = g_strdup_printf("%s/%s", id, dev->port->path); - g_free(id); - return ret; - } else { - return g_strdup(dev->port->path); - } -} - -static char *usb_get_fw_dev_path(DeviceState *qdev) -{ - USBDevice *dev = USB_DEVICE(qdev); - char *fw_path, *in; - ssize_t pos = 0, fw_len; - long nr; - - fw_len = 32 + strlen(dev->port->path) * 6; - fw_path = g_malloc(fw_len); - in = dev->port->path; - while (fw_len - pos > 0) { - nr = strtol(in, &in, 10); - if (in[0] == '.') { - /* some hub between root port and device */ - pos += snprintf(fw_path + pos, fw_len - pos, "hub@%lx/", nr); - in++; - } else { - /* the device itself */ - pos += snprintf(fw_path + pos, fw_len - pos, "%s@%lx", - qdev_fw_name(qdev), nr); - break; - } - } - return fw_path; -} - -void hmp_info_usb(Monitor *mon, const QDict *qdict) -{ - USBBus *bus; - USBDevice *dev; - USBPort *port; - - if (QTAILQ_EMPTY(&busses)) { - monitor_printf(mon, "USB support not enabled\n"); - return; - } - - QTAILQ_FOREACH(bus, &busses, next) { - QTAILQ_FOREACH(port, &bus->used, next) { - dev = port->dev; - if (!dev) - continue; - 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 ?: ""); - } - } -} - -/* handle legacy -usbdevice cmd line option */ -USBDevice *usbdevice_create(const char *cmdline) -{ - USBBus *bus = usb_bus_find(-1 /* any */); - LegacyUSBFactory *f = NULL; - Error *err = NULL; - GSList *i; - char driver[32]; - const char *params; - int len; - USBDevice *dev; - - params = strchr(cmdline,':'); - if (params) { - params++; - len = params - cmdline; - if (len > sizeof(driver)) - len = sizeof(driver); - pstrcpy(driver, len, cmdline); - } else { - params = ""; - pstrcpy(driver, sizeof(driver), cmdline); - } - - for (i = legacy_usb_factory; i; i = i->next) { - f = i->data; - if (strcmp(f->usbdevice_name, driver) == 0) { - break; - } - } - if (i == NULL) { -#if 0 - /* no error because some drivers are not converted (yet) */ - error_report("usbdevice %s not found", driver); -#endif - return NULL; - } - - if (!bus) { - error_report("Error: no usb bus to attach usbdevice %s, " - "please try -machine usb=on and check that " - "the machine model supports USB", driver); - return NULL; - } - - if (f->usbdevice_init) { - dev = f->usbdevice_init(bus, params); - } else { - if (*params) { - error_report("usbdevice %s accepts no params", driver); - return NULL; - } - dev = usb_create(bus, f->name); - } - if (!dev) { - error_report("Failed to create USB device '%s'", f->name); - return NULL; - } - object_property_set_bool(OBJECT(dev), true, "realized", &err); - if (err) { - error_reportf_err(err, "Failed to initialize USB device '%s': ", - f->name); - object_unparent(OBJECT(dev)); - return NULL; - } - return dev; -} - -static void usb_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - k->bus_type = TYPE_USB_BUS; - k->realize = usb_qdev_realize; - k->unrealize = usb_qdev_unrealize; - k->props = usb_props; -} - -static const TypeInfo usb_device_type_info = { - .name = TYPE_USB_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(USBDevice), - .abstract = true, - .class_size = sizeof(USBDeviceClass), - .class_init = usb_device_class_init, -}; - -static void usb_register_types(void) -{ - type_register_static(&usb_bus_info); - type_register_static(&usb_device_type_info); -} - -type_init(usb_register_types) diff --git a/qemu/hw/usb/ccid-card-emulated.c b/qemu/hw/usb/ccid-card-emulated.c deleted file mode 100644 index 3213f9f8a..000000000 --- a/qemu/hw/usb/ccid-card-emulated.c +++ /dev/null @@ -1,606 +0,0 @@ -/* - * CCID Card Device. Emulated card. - * - * Copyright (c) 2011 Red Hat. - * Written by Alon Levy. - * - * This code is licensed under the GNU LGPL, version 2 or later. - */ - -/* - * It can be used to provide access to the local hardware in a non exclusive - * way, or it can use certificates. It requires the usb-ccid bus. - * - * Usage 1: standard, mirror hardware reader+card: - * qemu .. -usb -device usb-ccid -device ccid-card-emulated - * - * Usage 2: use certificates, no hardware required - * one time: create the certificates: - * for i in 1 2 3; do - * certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=user$i" -n user$i - * done - * qemu .. -usb -device usb-ccid \ - * -device ccid-card-emulated,cert1=user1,cert2=user2,cert3=user3 - * - * If you use a non default db for the certificates you can specify it using - * the db parameter. - */ - -#include "qemu/osdep.h" -#include -#include -#include -#include - -#include "qemu/thread.h" -#include "sysemu/char.h" -#include "ccid.h" - -#define DPRINTF(card, lvl, fmt, ...) \ -do {\ - if (lvl <= card->debug) {\ - printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__);\ - } \ -} while (0) - - -#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" - -enum { - BACKEND_NSS_EMULATED = 1, - BACKEND_CERTIFICATES -}; - -#define DEFAULT_BACKEND BACKEND_NSS_EMULATED - -typedef struct EmulatedState EmulatedState; - -enum { - EMUL_READER_INSERT = 0, - EMUL_READER_REMOVE, - EMUL_CARD_INSERT, - EMUL_CARD_REMOVE, - EMUL_GUEST_APDU, - EMUL_RESPONSE_APDU, - EMUL_ERROR, -}; - -static const char *emul_event_to_string(uint32_t emul_event) -{ - switch (emul_event) { - case EMUL_READER_INSERT: - return "EMUL_READER_INSERT"; - case EMUL_READER_REMOVE: - return "EMUL_READER_REMOVE"; - case EMUL_CARD_INSERT: - return "EMUL_CARD_INSERT"; - case EMUL_CARD_REMOVE: - return "EMUL_CARD_REMOVE"; - case EMUL_GUEST_APDU: - return "EMUL_GUEST_APDU"; - case EMUL_RESPONSE_APDU: - return "EMUL_RESPONSE_APDU"; - case EMUL_ERROR: - return "EMUL_ERROR"; - } - return "UNKNOWN"; -} - -typedef struct EmulEvent { - QSIMPLEQ_ENTRY(EmulEvent) entry; - union { - struct { - uint32_t type; - } gen; - struct { - uint32_t type; - uint64_t code; - } error; - struct { - uint32_t type; - uint32_t len; - uint8_t data[]; - } data; - } p; -} EmulEvent; - -#define MAX_ATR_SIZE 40 -struct EmulatedState { - CCIDCardState base; - uint8_t debug; - char *backend_str; - uint32_t backend; - char *cert1; - char *cert2; - char *cert3; - char *db; - uint8_t atr[MAX_ATR_SIZE]; - uint8_t atr_length; - QSIMPLEQ_HEAD(event_list, EmulEvent) event_list; - QemuMutex event_list_mutex; - QemuThread event_thread_id; - VReader *reader; - QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list; - QemuMutex vreader_mutex; /* and guest_apdu_list mutex */ - QemuMutex handle_apdu_mutex; - QemuCond handle_apdu_cond; - EventNotifier notifier; - int quit_apdu_thread; - QemuThread apdu_thread_id; -}; - -static void emulated_apdu_from_guest(CCIDCardState *base, - const uint8_t *apdu, uint32_t len) -{ - EmulatedState *card = EMULATED_CCID_CARD(base); - EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len); - - assert(event); - event->p.data.type = EMUL_GUEST_APDU; - event->p.data.len = len; - memcpy(event->p.data.data, apdu, len); - qemu_mutex_lock(&card->vreader_mutex); - QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry); - qemu_mutex_unlock(&card->vreader_mutex); - qemu_mutex_lock(&card->handle_apdu_mutex); - qemu_cond_signal(&card->handle_apdu_cond); - qemu_mutex_unlock(&card->handle_apdu_mutex); -} - -static const uint8_t *emulated_get_atr(CCIDCardState *base, uint32_t *len) -{ - EmulatedState *card = EMULATED_CCID_CARD(base); - - *len = card->atr_length; - return card->atr; -} - -static void emulated_push_event(EmulatedState *card, EmulEvent *event) -{ - qemu_mutex_lock(&card->event_list_mutex); - QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry); - qemu_mutex_unlock(&card->event_list_mutex); - event_notifier_set(&card->notifier); -} - -static void emulated_push_type(EmulatedState *card, uint32_t type) -{ - EmulEvent *event = g_new(EmulEvent, 1); - - assert(event); - event->p.gen.type = type; - emulated_push_event(card, event); -} - -static void emulated_push_error(EmulatedState *card, uint64_t code) -{ - EmulEvent *event = g_new(EmulEvent, 1); - - assert(event); - event->p.error.type = EMUL_ERROR; - event->p.error.code = code; - emulated_push_event(card, event); -} - -static void emulated_push_data_type(EmulatedState *card, uint32_t type, - const uint8_t *data, uint32_t len) -{ - EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len); - - assert(event); - event->p.data.type = type; - event->p.data.len = len; - memcpy(event->p.data.data, data, len); - emulated_push_event(card, event); -} - -static void emulated_push_reader_insert(EmulatedState *card) -{ - emulated_push_type(card, EMUL_READER_INSERT); -} - -static void emulated_push_reader_remove(EmulatedState *card) -{ - emulated_push_type(card, EMUL_READER_REMOVE); -} - -static void emulated_push_card_insert(EmulatedState *card, - const uint8_t *atr, uint32_t len) -{ - emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len); -} - -static void emulated_push_card_remove(EmulatedState *card) -{ - emulated_push_type(card, EMUL_CARD_REMOVE); -} - -static void emulated_push_response_apdu(EmulatedState *card, - const uint8_t *apdu, uint32_t len) -{ - emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len); -} - -#define APDU_BUF_SIZE 270 -static void *handle_apdu_thread(void* arg) -{ - EmulatedState *card = arg; - uint8_t recv_data[APDU_BUF_SIZE]; - int recv_len; - VReaderStatus reader_status; - EmulEvent *event; - - while (1) { - qemu_mutex_lock(&card->handle_apdu_mutex); - qemu_cond_wait(&card->handle_apdu_cond, &card->handle_apdu_mutex); - qemu_mutex_unlock(&card->handle_apdu_mutex); - if (card->quit_apdu_thread) { - card->quit_apdu_thread = 0; /* debugging */ - break; - } - qemu_mutex_lock(&card->vreader_mutex); - while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) { - event = QSIMPLEQ_FIRST(&card->guest_apdu_list); - assert((unsigned long)event > 1000); - QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry); - if (event->p.data.type != EMUL_GUEST_APDU) { - DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n"); - g_free(event); - continue; - } - if (card->reader == NULL) { - DPRINTF(card, 1, "reader is NULL\n"); - g_free(event); - continue; - } - recv_len = sizeof(recv_data); - reader_status = vreader_xfr_bytes(card->reader, - event->p.data.data, event->p.data.len, - recv_data, &recv_len); - DPRINTF(card, 2, "got back apdu of length %d\n", recv_len); - if (reader_status == VREADER_OK) { - emulated_push_response_apdu(card, recv_data, recv_len); - } else { - emulated_push_error(card, reader_status); - } - g_free(event); - } - qemu_mutex_unlock(&card->vreader_mutex); - } - return NULL; -} - -static void *event_thread(void *arg) -{ - int atr_len = MAX_ATR_SIZE; - uint8_t atr[MAX_ATR_SIZE]; - VEvent *event = NULL; - EmulatedState *card = arg; - - while (1) { - const char *reader_name; - - event = vevent_wait_next_vevent(); - if (event == NULL || event->type == VEVENT_LAST) { - break; - } - if (event->type != VEVENT_READER_INSERT) { - if (card->reader == NULL && event->reader != NULL) { - /* Happens after device_add followed by card remove or insert. - * XXX: create synthetic add_reader events if vcard_emul_init - * already called, which happens if device_del and device_add - * are called */ - card->reader = vreader_reference(event->reader); - } else { - if (event->reader != card->reader) { - fprintf(stderr, - "ERROR: wrong reader: quiting event_thread\n"); - break; - } - } - } - switch (event->type) { - case VEVENT_READER_INSERT: - /* TODO: take a specific reader. i.e. track which reader - * we are seeing here, check it is the one we want (the first, - * or by a particular name), and ignore if we don't want it. - */ - reader_name = vreader_get_name(event->reader); - if (card->reader != NULL) { - DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n", - vreader_get_name(card->reader), reader_name); - qemu_mutex_lock(&card->vreader_mutex); - vreader_free(card->reader); - qemu_mutex_unlock(&card->vreader_mutex); - emulated_push_reader_remove(card); - } - qemu_mutex_lock(&card->vreader_mutex); - DPRINTF(card, 2, "READER INSERT %s\n", reader_name); - card->reader = vreader_reference(event->reader); - qemu_mutex_unlock(&card->vreader_mutex); - emulated_push_reader_insert(card); - break; - case VEVENT_READER_REMOVE: - DPRINTF(card, 2, " READER REMOVE: %s\n", - vreader_get_name(event->reader)); - qemu_mutex_lock(&card->vreader_mutex); - vreader_free(card->reader); - card->reader = NULL; - qemu_mutex_unlock(&card->vreader_mutex); - emulated_push_reader_remove(card); - break; - case VEVENT_CARD_INSERT: - /* get the ATR (intended as a response to a power on from the - * reader */ - atr_len = MAX_ATR_SIZE; - vreader_power_on(event->reader, atr, &atr_len); - card->atr_length = (uint8_t)atr_len; - DPRINTF(card, 2, " CARD INSERT\n"); - emulated_push_card_insert(card, atr, atr_len); - break; - case VEVENT_CARD_REMOVE: - DPRINTF(card, 2, " CARD REMOVE\n"); - emulated_push_card_remove(card); - break; - case VEVENT_LAST: /* quit */ - vevent_delete(event); - return NULL; - break; - default: - break; - } - vevent_delete(event); - } - return NULL; -} - -static void card_event_handler(EventNotifier *notifier) -{ - EmulatedState *card = container_of(notifier, EmulatedState, notifier); - EmulEvent *event, *next; - - event_notifier_test_and_clear(&card->notifier); - qemu_mutex_lock(&card->event_list_mutex); - QSIMPLEQ_FOREACH_SAFE(event, &card->event_list, entry, next) { - DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type)); - switch (event->p.gen.type) { - case EMUL_RESPONSE_APDU: - ccid_card_send_apdu_to_guest(&card->base, event->p.data.data, - event->p.data.len); - break; - case EMUL_READER_INSERT: - ccid_card_ccid_attach(&card->base); - break; - case EMUL_READER_REMOVE: - ccid_card_ccid_detach(&card->base); - break; - case EMUL_CARD_INSERT: - assert(event->p.data.len <= MAX_ATR_SIZE); - card->atr_length = event->p.data.len; - memcpy(card->atr, event->p.data.data, card->atr_length); - ccid_card_card_inserted(&card->base); - break; - case EMUL_CARD_REMOVE: - ccid_card_card_removed(&card->base); - break; - case EMUL_ERROR: - ccid_card_card_error(&card->base, event->p.error.code); - break; - default: - DPRINTF(card, 2, "unexpected event\n"); - break; - } - g_free(event); - } - QSIMPLEQ_INIT(&card->event_list); - qemu_mutex_unlock(&card->event_list_mutex); -} - -static int init_event_notifier(EmulatedState *card) -{ - if (event_notifier_init(&card->notifier, false) < 0) { - DPRINTF(card, 2, "event notifier creation failed\n"); - return -1; - } - event_notifier_set_handler(&card->notifier, false, card_event_handler); - return 0; -} - -#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb" -#define CERTIFICATES_ARGS_TEMPLATE\ - "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)" - -static int wrap_vcard_emul_init(VCardEmulOptions *options) -{ - static int called; - static int options_was_null; - - if (called) { - if ((options == NULL) != options_was_null) { - printf("%s: warning: running emulated with certificates" - " and emulated side by side is not supported\n", - __func__); - return VCARD_EMUL_FAIL; - } - vcard_emul_replay_insertion_events(); - return VCARD_EMUL_OK; - } - options_was_null = (options == NULL); - called = 1; - return vcard_emul_init(options); -} - -static int emulated_initialize_vcard_from_certificates(EmulatedState *card) -{ - char emul_args[200]; - VCardEmulOptions *options = NULL; - - snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE, - card->db ? card->db : CERTIFICATES_DEFAULT_DB, - card->cert1, card->cert2, card->cert3); - options = vcard_emul_options(emul_args); - if (options == NULL) { - printf("%s: warning: not using certificates due to" - " initialization error\n", __func__); - } - return wrap_vcard_emul_init(options); -} - -typedef struct EnumTable { - const char *name; - uint32_t value; -} EnumTable; - -static const EnumTable backend_enum_table[] = { - {BACKEND_NSS_EMULATED_NAME, BACKEND_NSS_EMULATED}, - {BACKEND_CERTIFICATES_NAME, BACKEND_CERTIFICATES}, - {NULL, 0}, -}; - -static uint32_t parse_enumeration(char *str, - const EnumTable *table, uint32_t not_found_value) -{ - uint32_t ret = not_found_value; - - if (str == NULL) - return 0; - - while (table->name != NULL) { - if (strcmp(table->name, str) == 0) { - ret = table->value; - break; - } - table++; - } - return ret; -} - -static int emulated_initfn(CCIDCardState *base) -{ - EmulatedState *card = EMULATED_CCID_CARD(base); - VCardEmulError ret; - const EnumTable *ptable; - - QSIMPLEQ_INIT(&card->event_list); - QSIMPLEQ_INIT(&card->guest_apdu_list); - qemu_mutex_init(&card->event_list_mutex); - qemu_mutex_init(&card->vreader_mutex); - qemu_mutex_init(&card->handle_apdu_mutex); - qemu_cond_init(&card->handle_apdu_cond); - card->reader = NULL; - card->quit_apdu_thread = 0; - if (init_event_notifier(card) < 0) { - return -1; - } - - card->backend = 0; - if (card->backend_str) { - card->backend = parse_enumeration(card->backend_str, - backend_enum_table, 0); - } - - if (card->backend == 0) { - printf("backend must be one of:\n"); - for (ptable = backend_enum_table; ptable->name != NULL; ++ptable) { - printf("%s\n", ptable->name); - } - return -1; - } - - /* TODO: a passthru backened that works on local machine. third card type?*/ - if (card->backend == BACKEND_CERTIFICATES) { - if (card->cert1 != NULL && card->cert2 != NULL && card->cert3 != NULL) { - ret = emulated_initialize_vcard_from_certificates(card); - } else { - printf("%s: you must provide all three certs for" - " 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", 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", - 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", TYPE_EMULATED_CCID); - return -1; - } - qemu_thread_create(&card->event_thread_id, "ccid/event", event_thread, - card, QEMU_THREAD_JOINABLE); - qemu_thread_create(&card->apdu_thread_id, "ccid/apdu", handle_apdu_thread, - card, QEMU_THREAD_JOINABLE); - return 0; -} - -static int emulated_exitfn(CCIDCardState *base) -{ - EmulatedState *card = EMULATED_CCID_CARD(base); - VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL); - - vevent_queue_vevent(vevent); /* stop vevent thread */ - qemu_thread_join(&card->event_thread_id); - - card->quit_apdu_thread = 1; /* stop handle_apdu thread */ - qemu_cond_signal(&card->handle_apdu_cond); - qemu_thread_join(&card->apdu_thread_id); - - /* threads exited, can destroy all condvars/mutexes */ - qemu_cond_destroy(&card->handle_apdu_cond); - qemu_mutex_destroy(&card->handle_apdu_mutex); - qemu_mutex_destroy(&card->vreader_mutex); - qemu_mutex_destroy(&card->event_list_mutex); - return 0; -} - -static Property emulated_card_properties[] = { - DEFINE_PROP_STRING("backend", EmulatedState, backend_str), - DEFINE_PROP_STRING("cert1", EmulatedState, cert1), - DEFINE_PROP_STRING("cert2", EmulatedState, cert2), - DEFINE_PROP_STRING("cert3", EmulatedState, cert3), - DEFINE_PROP_STRING("db", EmulatedState, db), - DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void emulated_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - CCIDCardClass *cc = CCID_CARD_CLASS(klass); - - cc->initfn = emulated_initfn; - cc->exitfn = emulated_exitfn; - cc->get_atr = emulated_get_atr; - cc->apdu_from_guest = emulated_apdu_from_guest; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - dc->desc = "emulated smartcard"; - dc->props = emulated_card_properties; -} - -static const TypeInfo emulated_card_info = { - .name = TYPE_EMULATED_CCID, - .parent = TYPE_CCID_CARD, - .instance_size = sizeof(EmulatedState), - .class_init = emulated_class_initfn, -}; - -static void ccid_card_emulated_register_types(void) -{ - type_register_static(&emulated_card_info); -} - -type_init(ccid_card_emulated_register_types) diff --git a/qemu/hw/usb/ccid-card-passthru.c b/qemu/hw/usb/ccid-card-passthru.c deleted file mode 100644 index c0e90e501..000000000 --- a/qemu/hw/usb/ccid-card-passthru.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * CCID Passthru Card Device emulation - * - * Copyright (c) 2011 Red Hat. - * Written by Alon Levy. - * - * This work is licensed under the terms of the GNU GPL, version 2.1 or later. - * 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 "cacard/vscard_common.h" - -#define DPRINTF(card, lvl, fmt, ...) \ -do { \ - if (lvl <= card->debug) { \ - printf("ccid-card-passthru: " fmt , ## __VA_ARGS__); \ - } \ -} while (0) - -#define D_WARN 1 -#define D_INFO 2 -#define D_MORE_INFO 3 -#define D_VERBOSE 4 - -/* TODO: do we still need this? */ -static const uint8_t DEFAULT_ATR[] = { -/* - * From some example somewhere - * 0x3B, 0xB0, 0x18, 0x00, 0xD1, 0x81, 0x05, 0xB1, 0x40, 0x38, 0x1F, 0x03, 0x28 - */ - -/* From an Athena smart card */ - 0x3B, 0xD5, 0x18, 0xFF, 0x80, 0x91, 0xFE, 0x1F, 0xC3, 0x80, 0x73, 0xC8, 0x21, - 0x13, 0x08 -}; - -#define VSCARD_IN_SIZE 65536 - -/* maximum size of ATR - from 7816-3 */ -#define MAX_ATR_SIZE 40 - -typedef struct PassthruState PassthruState; - -struct PassthruState { - CCIDCardState base; - CharDriverState *cs; - uint8_t vscard_in_data[VSCARD_IN_SIZE]; - uint32_t vscard_in_pos; - uint32_t vscard_in_hdr; - uint8_t atr[MAX_ATR_SIZE]; - uint8_t atr_length; - 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. - */ - -static void ccid_card_vscard_send_msg(PassthruState *s, - VSCMsgType type, uint32_t reader_id, - const uint8_t *payload, uint32_t length) -{ - VSCMsgHeader scr_msg_header; - - scr_msg_header.type = htonl(type); - scr_msg_header.reader_id = htonl(reader_id); - scr_msg_header.length = htonl(length); - qemu_chr_fe_write(s->cs, (uint8_t *)&scr_msg_header, sizeof(VSCMsgHeader)); - qemu_chr_fe_write(s->cs, payload, length); -} - -static void ccid_card_vscard_send_apdu(PassthruState *s, - const uint8_t *apdu, uint32_t length) -{ - ccid_card_vscard_send_msg( - s, VSC_APDU, VSCARD_MINIMAL_READER_ID, apdu, length); -} - -static void ccid_card_vscard_send_error(PassthruState *s, - uint32_t reader_id, VSCErrorCode code) -{ - VSCMsgError msg = {.code = htonl(code)}; - - ccid_card_vscard_send_msg( - s, VSC_Error, reader_id, (uint8_t *)&msg, sizeof(msg)); -} - -static void ccid_card_vscard_send_init(PassthruState *s) -{ - VSCMsgInit msg = { - .version = htonl(VSCARD_VERSION), - .magic = VSCARD_MAGIC, - .capabilities = {0} - }; - - ccid_card_vscard_send_msg(s, VSC_Init, VSCARD_UNDEFINED_READER_ID, - (uint8_t *)&msg, sizeof(msg)); -} - -static int ccid_card_vscard_can_read(void *opaque) -{ - PassthruState *card = opaque; - - return VSCARD_IN_SIZE >= card->vscard_in_pos ? - VSCARD_IN_SIZE - card->vscard_in_pos : 0; -} - -static void ccid_card_vscard_handle_init( - PassthruState *card, VSCMsgHeader *hdr, VSCMsgInit *init) -{ - uint32_t *capabilities; - int num_capabilities; - int i; - - capabilities = init->capabilities; - num_capabilities = - 1 + ((hdr->length - sizeof(VSCMsgInit)) / sizeof(uint32_t)); - init->version = ntohl(init->version); - for (i = 0 ; i < num_capabilities; ++i) { - capabilities[i] = ntohl(capabilities[i]); - } - if (init->magic != VSCARD_MAGIC) { - error_report("wrong magic"); - /* we can't disconnect the chardev */ - } - if (init->version != VSCARD_VERSION) { - DPRINTF(card, D_WARN, - "got version %d, have %d", init->version, VSCARD_VERSION); - } - /* future handling of capabilities, none exist atm */ - ccid_card_vscard_send_init(card); -} - -static int check_atr(PassthruState *card, uint8_t *data, int len) -{ - int historical_length, opt_bytes; - int td_count = 0; - int td; - - if (len < 2) { - return 0; - } - historical_length = data[1] & 0xf; - opt_bytes = 0; - if (data[0] != 0x3b && data[0] != 0x3f) { - DPRINTF(card, D_WARN, "atr's T0 is 0x%X, not in {0x3b, 0x3f}\n", - data[0]); - return 0; - } - td_count = 0; - td = data[1] >> 4; - while (td && td_count < 2 && opt_bytes + historical_length + 2 < len) { - td_count++; - if (td & 0x1) { - opt_bytes++; - } - if (td & 0x2) { - opt_bytes++; - } - if (td & 0x4) { - opt_bytes++; - } - if (td & 0x8) { - opt_bytes++; - td = data[opt_bytes + 2] >> 4; - } - } - if (len < 2 + historical_length + opt_bytes) { - DPRINTF(card, D_WARN, - "atr too short: len %d, but historical_len %d, T1 0x%X\n", - len, historical_length, data[1]); - return 0; - } - if (len > 2 + historical_length + opt_bytes) { - DPRINTF(card, D_WARN, - "atr too long: len %d, but hist/opt %d/%d, T1 0x%X\n", - len, historical_length, opt_bytes, data[1]); - /* let it through */ - } - DPRINTF(card, D_VERBOSE, - "atr passes check: %d total length, %d historical, %d optional\n", - len, historical_length, opt_bytes); - - return 1; -} - -static void ccid_card_vscard_handle_message(PassthruState *card, - VSCMsgHeader *scr_msg_header) -{ - uint8_t *data = (uint8_t *)&scr_msg_header[1]; - - switch (scr_msg_header->type) { - case VSC_ATR: - DPRINTF(card, D_INFO, "VSC_ATR %d\n", scr_msg_header->length); - if (scr_msg_header->length > MAX_ATR_SIZE) { - error_report("ATR size exceeds spec, ignoring"); - ccid_card_vscard_send_error(card, scr_msg_header->reader_id, - VSC_GENERAL_ERROR); - break; - } - if (!check_atr(card, data, scr_msg_header->length)) { - error_report("ATR is inconsistent, ignoring"); - ccid_card_vscard_send_error(card, scr_msg_header->reader_id, - VSC_GENERAL_ERROR); - break; - } - memcpy(card->atr, data, scr_msg_header->length); - card->atr_length = scr_msg_header->length; - ccid_card_card_inserted(&card->base); - ccid_card_vscard_send_error(card, scr_msg_header->reader_id, - VSC_SUCCESS); - break; - case VSC_APDU: - ccid_card_send_apdu_to_guest( - &card->base, data, scr_msg_header->length); - break; - case VSC_CardRemove: - DPRINTF(card, D_INFO, "VSC_CardRemove\n"); - ccid_card_card_removed(&card->base); - ccid_card_vscard_send_error(card, - scr_msg_header->reader_id, VSC_SUCCESS); - break; - case VSC_Init: - ccid_card_vscard_handle_init( - card, scr_msg_header, (VSCMsgInit *)data); - break; - case VSC_Error: - ccid_card_card_error(&card->base, *(uint32_t *)data); - break; - case VSC_ReaderAdd: - if (ccid_card_ccid_attach(&card->base) < 0) { - ccid_card_vscard_send_error(card, VSCARD_UNDEFINED_READER_ID, - VSC_CANNOT_ADD_MORE_READERS); - } else { - ccid_card_vscard_send_error(card, VSCARD_MINIMAL_READER_ID, - VSC_SUCCESS); - } - break; - case VSC_ReaderRemove: - ccid_card_ccid_detach(&card->base); - ccid_card_vscard_send_error(card, - scr_msg_header->reader_id, VSC_SUCCESS); - break; - default: - printf("usb-ccid: chardev: unexpected message of type %X\n", - scr_msg_header->type); - ccid_card_vscard_send_error(card, scr_msg_header->reader_id, - VSC_GENERAL_ERROR); - } -} - -static void ccid_card_vscard_drop_connection(PassthruState *card) -{ - qemu_chr_delete(card->cs); - card->vscard_in_pos = card->vscard_in_hdr = 0; -} - -static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size) -{ - PassthruState *card = opaque; - VSCMsgHeader *hdr; - - if (card->vscard_in_pos + size > VSCARD_IN_SIZE) { - error_report( - "no room for data: pos %d + size %d > %d. dropping connection.", - card->vscard_in_pos, size, VSCARD_IN_SIZE); - ccid_card_vscard_drop_connection(card); - return; - } - assert(card->vscard_in_pos < VSCARD_IN_SIZE); - assert(card->vscard_in_hdr < VSCARD_IN_SIZE); - memcpy(card->vscard_in_data + card->vscard_in_pos, buf, size); - card->vscard_in_pos += size; - hdr = (VSCMsgHeader *)(card->vscard_in_data + card->vscard_in_hdr); - - while ((card->vscard_in_pos - card->vscard_in_hdr >= sizeof(VSCMsgHeader)) - &&(card->vscard_in_pos - card->vscard_in_hdr >= - sizeof(VSCMsgHeader) + ntohl(hdr->length))) { - hdr->reader_id = ntohl(hdr->reader_id); - hdr->length = ntohl(hdr->length); - hdr->type = ntohl(hdr->type); - ccid_card_vscard_handle_message(card, hdr); - card->vscard_in_hdr += hdr->length + sizeof(VSCMsgHeader); - hdr = (VSCMsgHeader *)(card->vscard_in_data + card->vscard_in_hdr); - } - if (card->vscard_in_hdr == card->vscard_in_pos) { - card->vscard_in_pos = card->vscard_in_hdr = 0; - } -} - -static void ccid_card_vscard_event(void *opaque, int event) -{ - PassthruState *card = opaque; - - switch (event) { - case CHR_EVENT_BREAK: - card->vscard_in_pos = card->vscard_in_hdr = 0; - break; - case CHR_EVENT_FOCUS: - break; - case CHR_EVENT_OPENED: - DPRINTF(card, D_INFO, "%s: CHR_EVENT_OPENED\n", __func__); - break; - } -} - -/* End VSCard handling */ - -static void passthru_apdu_from_guest( - CCIDCardState *base, const uint8_t *apdu, uint32_t len) -{ - PassthruState *card = PASSTHRU_CCID_CARD(base); - - if (!card->cs) { - printf("ccid-passthru: no chardev, discarding apdu length %d\n", len); - return; - } - ccid_card_vscard_send_apdu(card, apdu, len); -} - -static const uint8_t *passthru_get_atr(CCIDCardState *base, uint32_t *len) -{ - PassthruState *card = PASSTHRU_CCID_CARD(base); - - *len = card->atr_length; - return card->atr; -} - -static int passthru_initfn(CCIDCardState *base) -{ - PassthruState *card = PASSTHRU_CCID_CARD(base); - - card->vscard_in_pos = 0; - card->vscard_in_hdr = 0; - if (card->cs) { - DPRINTF(card, D_INFO, "initing chardev\n"); - qemu_chr_add_handlers(card->cs, - ccid_card_vscard_can_read, - ccid_card_vscard_read, - ccid_card_vscard_event, card); - ccid_card_vscard_send_init(card); - } else { - error_report("missing chardev"); - return -1; - } - card->debug = parse_debug_env("QEMU_CCID_PASSTHRU_DEBUG", D_VERBOSE, - card->debug); - assert(sizeof(DEFAULT_ATR) <= MAX_ATR_SIZE); - memcpy(card->atr, DEFAULT_ATR, sizeof(DEFAULT_ATR)); - card->atr_length = sizeof(DEFAULT_ATR); - return 0; -} - -static int passthru_exitfn(CCIDCardState *base) -{ - return 0; -} - -static VMStateDescription passthru_vmstate = { - .name = "ccid-card-passthru", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_BUFFER(vscard_in_data, PassthruState), - VMSTATE_UINT32(vscard_in_pos, PassthruState), - VMSTATE_UINT32(vscard_in_hdr, PassthruState), - VMSTATE_BUFFER(atr, PassthruState), - VMSTATE_UINT8(atr_length, PassthruState), - VMSTATE_END_OF_LIST() - } -}; - -static Property passthru_card_properties[] = { - DEFINE_PROP_CHR("chardev", PassthruState, cs), - DEFINE_PROP_UINT8("debug", PassthruState, debug, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void passthru_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - CCIDCardClass *cc = CCID_CARD_CLASS(klass); - - cc->initfn = passthru_initfn; - cc->exitfn = passthru_exitfn; - cc->get_atr = passthru_get_atr; - cc->apdu_from_guest = passthru_apdu_from_guest; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - dc->desc = "passthrough smartcard"; - dc->vmsd = &passthru_vmstate; - dc->props = passthru_card_properties; -} - -static const TypeInfo passthru_card_info = { - .name = TYPE_CCID_PASSTHRU, - .parent = TYPE_CCID_CARD, - .instance_size = sizeof(PassthruState), - .class_init = passthru_class_initfn, -}; - -static void ccid_card_passthru_register_types(void) -{ - type_register_static(&passthru_card_info); -} - -type_init(ccid_card_passthru_register_types) diff --git a/qemu/hw/usb/ccid.h b/qemu/hw/usb/ccid.h deleted file mode 100644 index 9334da8ac..000000000 --- a/qemu/hw/usb/ccid.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * CCID Passthru Card Device emulation - * - * Copyright (c) 2011 Red Hat. - * Written by Alon Levy. - * - * This code is licensed under the GNU LGPL, version 2 or later. - */ - -#ifndef CCID_H -#define CCID_H - -#include "hw/qdev.h" - -typedef struct CCIDCardState CCIDCardState; -typedef struct CCIDCardInfo CCIDCardInfo; - -#define TYPE_CCID_CARD "ccid-card" -#define CCID_CARD(obj) \ - OBJECT_CHECK(CCIDCardState, (obj), TYPE_CCID_CARD) -#define CCID_CARD_CLASS(klass) \ - OBJECT_CLASS_CHECK(CCIDCardClass, (klass), TYPE_CCID_CARD) -#define CCID_CARD_GET_CLASS(obj) \ - OBJECT_GET_CLASS(CCIDCardClass, (obj), TYPE_CCID_CARD) - -/* - * callbacks to be used by the CCID device (hw/usb-ccid.c) to call - * into the smartcard device (hw/ccid-card-*.c) - */ -typedef struct CCIDCardClass { - DeviceClass parent_class; - const uint8_t *(*get_atr)(CCIDCardState *card, uint32_t *len); - void (*apdu_from_guest)(CCIDCardState *card, - const uint8_t *apdu, - uint32_t len); - int (*exitfn)(CCIDCardState *card); - int (*initfn)(CCIDCardState *card); -} CCIDCardClass; - -/* - * state of the CCID Card device (i.e. hw/ccid-card-*.c) - */ -struct CCIDCardState { - DeviceState qdev; - uint32_t slot; /* For future use with multiple slot reader. */ -}; - -/* - * API for smartcard calling the CCID device (used by hw/ccid-card-*.c) - */ -void ccid_card_send_apdu_to_guest(CCIDCardState *card, - uint8_t *apdu, - uint32_t len); -void ccid_card_card_removed(CCIDCardState *card); -void ccid_card_card_inserted(CCIDCardState *card); -void ccid_card_card_error(CCIDCardState *card, uint64_t error); - -/* - * support guest visible insertion/removal of ccid devices based on actual - * devices connected/removed. Called by card implementation (passthru, local) - */ -int ccid_card_ccid_attach(CCIDCardState *card); -void ccid_card_ccid_detach(CCIDCardState *card); - -#endif /* CCID_H */ diff --git a/qemu/hw/usb/combined-packet.c b/qemu/hw/usb/combined-packet.c deleted file mode 100644 index 48cac87f6..000000000 --- a/qemu/hw/usb/combined-packet.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * QEMU USB packet combining code (for input pipelining) - * - * Copyright(c) 2012 Red Hat, Inc. - * - * Red Hat Authors: - * Hans de Goede - * - * 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 General Public License - * along with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "hw/usb.h" -#include "qemu/iov.h" -#include "trace.h" - -static void usb_combined_packet_add(USBCombinedPacket *combined, USBPacket *p) -{ - qemu_iovec_concat(&combined->iov, &p->iov, 0, p->iov.size); - QTAILQ_INSERT_TAIL(&combined->packets, p, combined_entry); - p->combined = combined; -} - -/* Note will free combined when the last packet gets removed */ -static void usb_combined_packet_remove(USBCombinedPacket *combined, - USBPacket *p) -{ - assert(p->combined == combined); - p->combined = NULL; - QTAILQ_REMOVE(&combined->packets, p, combined_entry); - if (QTAILQ_EMPTY(&combined->packets)) { - qemu_iovec_destroy(&combined->iov); - g_free(combined); - } -} - -/* Also handles completion of non combined packets for pipelined input eps */ -void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p) -{ - USBCombinedPacket *combined = p->combined; - USBEndpoint *ep = p->ep; - USBPacket *next; - int status, actual_length; - bool short_not_ok, done = false; - - if (combined == NULL) { - usb_packet_complete_one(dev, p); - goto leave; - } - - assert(combined->first == p && p == QTAILQ_FIRST(&combined->packets)); - - status = combined->first->status; - actual_length = combined->first->actual_length; - short_not_ok = QTAILQ_LAST(&combined->packets, packets_head)->short_not_ok; - - QTAILQ_FOREACH_SAFE(p, &combined->packets, combined_entry, next) { - if (!done) { - /* Distribute data over uncombined packets */ - if (actual_length >= p->iov.size) { - p->actual_length = p->iov.size; - } else { - /* Send short or error packet to complete the transfer */ - p->actual_length = actual_length; - done = true; - } - /* Report status on the last packet */ - if (done || next == NULL) { - p->status = status; - } else { - p->status = USB_RET_SUCCESS; - } - p->short_not_ok = short_not_ok; - /* Note will free combined when the last packet gets removed! */ - usb_combined_packet_remove(combined, p); - usb_packet_complete_one(dev, p); - actual_length -= p->actual_length; - } else { - /* Remove any leftover packets from the queue */ - p->status = USB_RET_REMOVE_FROM_QUEUE; - /* Note will free combined on the last packet! */ - dev->port->ops->complete(dev->port, p); - } - } - /* Do not use combined here, it has been freed! */ -leave: - /* Check if there are packets in the queue waiting for our completion */ - usb_ep_combine_input_packets(ep); -} - -/* May only be called for combined packets! */ -void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p) -{ - USBCombinedPacket *combined = p->combined; - assert(combined != NULL); - USBPacket *first = p->combined->first; - - /* Note will free combined on the last packet! */ - usb_combined_packet_remove(combined, p); - if (p == first) { - usb_device_cancel_packet(dev, p); - } -} - -/* - * Large input transfers can get split into multiple input packets, this - * function recombines them, removing the short_not_ok checks which all but - * the last packet of such splits transfers have, thereby allowing input - * transfer pipelining (which we cannot do on short_not_ok transfers) - */ -void usb_ep_combine_input_packets(USBEndpoint *ep) -{ - USBPacket *p, *u, *next, *prev = NULL, *first = NULL; - USBPort *port = ep->dev->port; - int totalsize; - - assert(ep->pipeline); - assert(ep->pid == USB_TOKEN_IN); - - QTAILQ_FOREACH_SAFE(p, &ep->queue, queue, next) { - /* Empty the queue on a halt */ - if (ep->halted) { - p->status = USB_RET_REMOVE_FROM_QUEUE; - port->ops->complete(port, p); - continue; - } - - /* Skip packets already submitted to the device */ - if (p->state == USB_PACKET_ASYNC) { - prev = p; - continue; - } - usb_packet_check_state(p, USB_PACKET_QUEUED); - - /* - * If the previous (combined) packet has the short_not_ok flag set - * stop, as we must not submit packets to the device after a transfer - * ending with short_not_ok packet. - */ - if (prev && prev->short_not_ok) { - break; - } - - if (first) { - if (first->combined == NULL) { - USBCombinedPacket *combined = g_new0(USBCombinedPacket, 1); - - combined->first = first; - QTAILQ_INIT(&combined->packets); - qemu_iovec_init(&combined->iov, 2); - usb_combined_packet_add(combined, first); - } - usb_combined_packet_add(first->combined, p); - } else { - first = p; - } - - /* Is this packet the last one of a (combined) transfer? */ - totalsize = (p->combined) ? p->combined->iov.size : p->iov.size; - if ((p->iov.size % ep->max_packet_size) != 0 || !p->short_not_ok || - next == NULL || - /* Work around for Linux usbfs bulk splitting + migration */ - (totalsize == 16348 && p->int_req)) { - usb_device_handle_data(ep->dev, first); - assert(first->status == USB_RET_ASYNC); - if (first->combined) { - QTAILQ_FOREACH(u, &first->combined->packets, combined_entry) { - usb_packet_set_state(u, USB_PACKET_ASYNC); - } - } else { - usb_packet_set_state(first, USB_PACKET_ASYNC); - } - first = NULL; - prev = p; - } - } -} diff --git a/qemu/hw/usb/core.c b/qemu/hw/usb/core.c deleted file mode 100644 index 45fa00c51..000000000 --- a/qemu/hw/usb/core.c +++ /dev/null @@ -1,795 +0,0 @@ -/* - * QEMU USB emulation - * - * Copyright (c) 2005 Fabrice Bellard - * - * 2008 Generic packet handler rewrite by Max Krasnyansky - * - * 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 "qemu-common.h" -#include "hw/usb.h" -#include "qemu/iov.h" -#include "trace.h" - -void usb_pick_speed(USBPort *port) -{ - static const int speeds[] = { - USB_SPEED_SUPER, - USB_SPEED_HIGH, - USB_SPEED_FULL, - USB_SPEED_LOW, - }; - USBDevice *udev = port->dev; - int i; - - for (i = 0; i < ARRAY_SIZE(speeds); i++) { - if ((udev->speedmask & (1 << speeds[i])) && - (port->speedmask & (1 << speeds[i]))) { - udev->speed = speeds[i]; - return; - } - } -} - -void usb_attach(USBPort *port) -{ - USBDevice *dev = port->dev; - - assert(dev != NULL); - assert(dev->attached); - assert(dev->state == USB_STATE_NOTATTACHED); - usb_pick_speed(port); - port->ops->attach(port); - dev->state = USB_STATE_ATTACHED; - usb_device_handle_attach(dev); -} - -void usb_detach(USBPort *port) -{ - USBDevice *dev = port->dev; - - assert(dev != NULL); - assert(dev->state != USB_STATE_NOTATTACHED); - port->ops->detach(port); - dev->state = USB_STATE_NOTATTACHED; -} - -void usb_port_reset(USBPort *port) -{ - USBDevice *dev = port->dev; - - assert(dev != NULL); - usb_detach(port); - usb_attach(port); - usb_device_reset(dev); -} - -void usb_device_reset(USBDevice *dev) -{ - if (dev == NULL || !dev->attached) { - return; - } - dev->remote_wakeup = 0; - dev->addr = 0; - dev->state = USB_STATE_DEFAULT; - usb_device_handle_reset(dev); -} - -void usb_wakeup(USBEndpoint *ep, unsigned int stream) -{ - USBDevice *dev = ep->dev; - USBBus *bus = usb_bus_from_device(dev); - - if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) { - dev->port->ops->wakeup(dev->port); - } - if (bus->ops->wakeup_endpoint) { - bus->ops->wakeup_endpoint(bus, ep, stream); - } -} - -/**********************/ - -/* generic USB device helpers (you are not forced to use them when - writing your USB device driver, but they help handling the - protocol) -*/ - -#define SETUP_STATE_IDLE 0 -#define SETUP_STATE_SETUP 1 -#define SETUP_STATE_DATA 2 -#define SETUP_STATE_ACK 3 -#define SETUP_STATE_PARAM 4 - -static void do_token_setup(USBDevice *s, USBPacket *p) -{ - int request, value, index; - - if (p->iov.size != 8) { - p->status = USB_RET_STALL; - return; - } - - 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]; - 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]; - index = (s->setup_buf[5] << 8) | s->setup_buf[4]; - - if (s->setup_buf[0] & USB_DIR_IN) { - usb_device_handle_control(s, p, request, value, index, - s->setup_len, s->data_buf); - if (p->status == USB_RET_ASYNC) { - s->setup_state = SETUP_STATE_SETUP; - } - if (p->status != USB_RET_SUCCESS) { - return; - } - - if (p->actual_length < s->setup_len) { - s->setup_len = p->actual_length; - } - s->setup_state = SETUP_STATE_DATA; - } else { - if (s->setup_len == 0) - s->setup_state = SETUP_STATE_ACK; - else - s->setup_state = SETUP_STATE_DATA; - } - - p->actual_length = 8; -} - -static void do_token_in(USBDevice *s, USBPacket *p) -{ - int request, value, index; - - assert(p->ep->nr == 0); - - 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)) { - usb_device_handle_control(s, p, request, value, index, - s->setup_len, s->data_buf); - if (p->status == USB_RET_ASYNC) { - return; - } - s->setup_state = SETUP_STATE_IDLE; - p->actual_length = 0; - } - break; - - case SETUP_STATE_DATA: - if (s->setup_buf[0] & USB_DIR_IN) { - int len = s->setup_len - s->setup_index; - if (len > p->iov.size) { - len = p->iov.size; - } - usb_packet_copy(p, s->data_buf + s->setup_index, len); - s->setup_index += len; - if (s->setup_index >= s->setup_len) { - s->setup_state = SETUP_STATE_ACK; - } - return; - } - s->setup_state = SETUP_STATE_IDLE; - p->status = USB_RET_STALL; - break; - - default: - p->status = USB_RET_STALL; - } -} - -static void do_token_out(USBDevice *s, USBPacket *p) -{ - assert(p->ep->nr == 0); - - switch(s->setup_state) { - case SETUP_STATE_ACK: - if (s->setup_buf[0] & USB_DIR_IN) { - s->setup_state = SETUP_STATE_IDLE; - /* transfer OK */ - } else { - /* ignore additional output */ - } - break; - - case SETUP_STATE_DATA: - if (!(s->setup_buf[0] & USB_DIR_IN)) { - int len = s->setup_len - s->setup_index; - if (len > p->iov.size) { - len = p->iov.size; - } - usb_packet_copy(p, s->data_buf + s->setup_index, len); - s->setup_index += len; - if (s->setup_index >= s->setup_len) { - s->setup_state = SETUP_STATE_ACK; - } - return; - } - s->setup_state = SETUP_STATE_IDLE; - p->status = USB_RET_STALL; - break; - - default: - p->status = USB_RET_STALL; - } -} - -static void do_parameter(USBDevice *s, USBPacket *p) -{ - int i, request, value, index; - - for (i = 0; i < 8; i++) { - s->setup_buf[i] = p->parameter >> (i*8); - } - - s->setup_state = SETUP_STATE_PARAM; - s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; - s->setup_index = 0; - - 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]; - - 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 (p->pid == USB_TOKEN_OUT) { - usb_packet_copy(p, s->data_buf, s->setup_len); - } - - usb_device_handle_control(s, p, request, value, index, - s->setup_len, s->data_buf); - if (p->status == USB_RET_ASYNC) { - return; - } - - if (p->actual_length < s->setup_len) { - s->setup_len = p->actual_length; - } - if (p->pid == USB_TOKEN_IN) { - p->actual_length = 0; - usb_packet_copy(p, s->data_buf, s->setup_len); - } -} - -/* ctrl complete function for devices which use usb_generic_handle_packet and - may return USB_RET_ASYNC from their handle_control callback. Device code - which does this *must* call this function instead of the normal - usb_packet_complete to complete their async control packets. */ -void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p) -{ - if (p->status < 0) { - s->setup_state = SETUP_STATE_IDLE; - } - - switch (s->setup_state) { - case SETUP_STATE_SETUP: - if (p->actual_length < s->setup_len) { - s->setup_len = p->actual_length; - } - s->setup_state = SETUP_STATE_DATA; - p->actual_length = 8; - break; - - case SETUP_STATE_ACK: - s->setup_state = SETUP_STATE_IDLE; - p->actual_length = 0; - break; - - case SETUP_STATE_PARAM: - if (p->actual_length < s->setup_len) { - s->setup_len = p->actual_length; - } - if (p->pid == USB_TOKEN_IN) { - p->actual_length = 0; - usb_packet_copy(p, s->data_buf, s->setup_len); - } - break; - - default: - break; - } - usb_packet_complete(s, p); -} - -USBDevice *usb_find_device(USBPort *port, uint8_t addr) -{ - USBDevice *dev = port->dev; - - if (dev == NULL || !dev->attached || dev->state != USB_STATE_DEFAULT) { - return NULL; - } - if (dev->addr == addr) { - return dev; - } - return usb_device_find_device(dev, addr); -} - -static void usb_process_one(USBPacket *p) -{ - USBDevice *dev = p->ep->dev; - - /* - * Handlers expect status to be initialized to USB_RET_SUCCESS, but it - * can be USB_RET_NAK here from a previous usb_process_one() call, - * or USB_RET_ASYNC from going through usb_queue_one(). - */ - p->status = USB_RET_SUCCESS; - - if (p->ep->nr == 0) { - /* control pipe */ - if (p->parameter) { - do_parameter(dev, p); - return; - } - switch (p->pid) { - case USB_TOKEN_SETUP: - do_token_setup(dev, p); - break; - case USB_TOKEN_IN: - do_token_in(dev, p); - break; - case USB_TOKEN_OUT: - do_token_out(dev, p); - break; - default: - p->status = USB_RET_STALL; - } - } else { - /* data pipe */ - usb_device_handle_data(dev, p); - } -} - -static void usb_queue_one(USBPacket *p) -{ - usb_packet_set_state(p, USB_PACKET_QUEUED); - QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue); - p->status = USB_RET_ASYNC; -} - -/* Hand over a packet to a device for processing. p->status == - USB_RET_ASYNC indicates the processing isn't finished yet, the - driver will call usb_packet_complete() when done processing it. */ -void usb_handle_packet(USBDevice *dev, USBPacket *p) -{ - if (dev == NULL) { - p->status = USB_RET_NODEV; - return; - } - assert(dev == p->ep->dev); - assert(dev->state == USB_STATE_DEFAULT); - usb_packet_check_state(p, USB_PACKET_SETUP); - assert(p->ep != NULL); - - /* Submitting a new packet clears halt */ - if (p->ep->halted) { - assert(QTAILQ_EMPTY(&p->ep->queue)); - p->ep->halted = false; - } - - if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline || p->stream) { - usb_process_one(p); - if (p->status == USB_RET_ASYNC) { - /* hcd drivers cannot handle async for isoc */ - assert(p->ep->type != USB_ENDPOINT_XFER_ISOC); - /* using async for interrupt packets breaks migration */ - assert(p->ep->type != USB_ENDPOINT_XFER_INT || - (dev->flags & (1 << USB_DEV_FLAG_IS_HOST))); - usb_packet_set_state(p, USB_PACKET_ASYNC); - QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue); - } else if (p->status == USB_RET_ADD_TO_QUEUE) { - usb_queue_one(p); - } else { - /* - * When pipelining is enabled usb-devices must always return async, - * otherwise packets can complete out of order! - */ - assert(p->stream || !p->ep->pipeline || - QTAILQ_EMPTY(&p->ep->queue)); - if (p->status != USB_RET_NAK) { - usb_packet_set_state(p, USB_PACKET_COMPLETE); - } - } - } else { - usb_queue_one(p); - } -} - -void usb_packet_complete_one(USBDevice *dev, USBPacket *p) -{ - USBEndpoint *ep = p->ep; - - assert(p->stream || QTAILQ_FIRST(&ep->queue) == p); - assert(p->status != USB_RET_ASYNC && p->status != USB_RET_NAK); - - if (p->status != USB_RET_SUCCESS || - (p->short_not_ok && (p->actual_length < p->iov.size))) { - ep->halted = true; - } - usb_packet_set_state(p, USB_PACKET_COMPLETE); - QTAILQ_REMOVE(&ep->queue, p, queue); - dev->port->ops->complete(dev->port, p); -} - -/* Notify the controller that an async packet is complete. This should only - be called for packets previously deferred by returning USB_RET_ASYNC from - handle_packet. */ -void usb_packet_complete(USBDevice *dev, USBPacket *p) -{ - USBEndpoint *ep = p->ep; - - usb_packet_check_state(p, USB_PACKET_ASYNC); - usb_packet_complete_one(dev, p); - - while (!QTAILQ_EMPTY(&ep->queue)) { - p = QTAILQ_FIRST(&ep->queue); - if (ep->halted) { - /* Empty the queue on a halt */ - p->status = USB_RET_REMOVE_FROM_QUEUE; - dev->port->ops->complete(dev->port, p); - continue; - } - if (p->state == USB_PACKET_ASYNC) { - break; - } - usb_packet_check_state(p, USB_PACKET_QUEUED); - usb_process_one(p); - if (p->status == USB_RET_ASYNC) { - usb_packet_set_state(p, USB_PACKET_ASYNC); - break; - } - usb_packet_complete_one(ep->dev, p); - } -} - -/* Cancel an active packet. The packed must have been deferred by - returning USB_RET_ASYNC from handle_packet, and not yet - completed. */ -void usb_cancel_packet(USBPacket * p) -{ - bool callback = (p->state == USB_PACKET_ASYNC); - assert(usb_packet_is_inflight(p)); - usb_packet_set_state(p, USB_PACKET_CANCELED); - QTAILQ_REMOVE(&p->ep->queue, p, queue); - if (callback) { - usb_device_cancel_packet(p->ep->dev, p); - } -} - - -void usb_packet_init(USBPacket *p) -{ - qemu_iovec_init(&p->iov, 1); -} - -static const char *usb_packet_state_name(USBPacketState state) -{ - static const char *name[] = { - [USB_PACKET_UNDEFINED] = "undef", - [USB_PACKET_SETUP] = "setup", - [USB_PACKET_QUEUED] = "queued", - [USB_PACKET_ASYNC] = "async", - [USB_PACKET_COMPLETE] = "complete", - [USB_PACKET_CANCELED] = "canceled", - }; - if (state < ARRAY_SIZE(name)) { - return name[state]; - } - return "INVALID"; -} - -void usb_packet_check_state(USBPacket *p, USBPacketState expected) -{ - USBDevice *dev; - USBBus *bus; - - if (p->state == expected) { - return; - } - dev = p->ep->dev; - bus = usb_bus_from_device(dev); - trace_usb_packet_state_fault(bus->busnr, dev->port->path, p->ep->nr, p, - usb_packet_state_name(p->state), - usb_packet_state_name(expected)); - assert(!"usb packet state check failed"); -} - -void usb_packet_set_state(USBPacket *p, USBPacketState state) -{ - if (p->ep) { - USBDevice *dev = p->ep->dev; - USBBus *bus = usb_bus_from_device(dev); - trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr, p, - usb_packet_state_name(p->state), - usb_packet_state_name(state)); - } else { - trace_usb_packet_state_change(-1, "", -1, p, - usb_packet_state_name(p->state), - usb_packet_state_name(state)); - } - p->state = state; -} - -void usb_packet_setup(USBPacket *p, int pid, - USBEndpoint *ep, unsigned int stream, - uint64_t id, bool short_not_ok, bool int_req) -{ - assert(!usb_packet_is_inflight(p)); - assert(p->iov.iov != NULL); - p->id = id; - p->pid = pid; - p->ep = ep; - p->stream = stream; - p->status = USB_RET_SUCCESS; - p->actual_length = 0; - p->parameter = 0; - p->short_not_ok = short_not_ok; - p->int_req = int_req; - p->combined = NULL; - qemu_iovec_reset(&p->iov); - usb_packet_set_state(p, USB_PACKET_SETUP); -} - -void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len) -{ - qemu_iovec_add(&p->iov, ptr, len); -} - -void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes) -{ - QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov; - - assert(p->actual_length >= 0); - assert(p->actual_length + bytes <= iov->size); - switch (p->pid) { - case USB_TOKEN_SETUP: - case USB_TOKEN_OUT: - iov_to_buf(iov->iov, iov->niov, p->actual_length, ptr, bytes); - break; - case USB_TOKEN_IN: - iov_from_buf(iov->iov, iov->niov, p->actual_length, ptr, bytes); - break; - default: - fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid); - abort(); - } - p->actual_length += bytes; -} - -void usb_packet_skip(USBPacket *p, size_t bytes) -{ - QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov; - - assert(p->actual_length >= 0); - assert(p->actual_length + bytes <= iov->size); - if (p->pid == USB_TOKEN_IN) { - iov_memset(iov->iov, iov->niov, p->actual_length, 0, bytes); - } - p->actual_length += bytes; -} - -size_t usb_packet_size(USBPacket *p) -{ - return p->combined ? p->combined->iov.size : p->iov.size; -} - -void usb_packet_cleanup(USBPacket *p) -{ - assert(!usb_packet_is_inflight(p)); - qemu_iovec_destroy(&p->iov); -} - -void usb_ep_reset(USBDevice *dev) -{ - int ep; - - dev->ep_ctl.nr = 0; - dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL; - dev->ep_ctl.ifnum = 0; - dev->ep_ctl.max_packet_size = 64; - dev->ep_ctl.max_streams = 0; - dev->ep_ctl.dev = dev; - dev->ep_ctl.pipeline = false; - for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { - dev->ep_in[ep].nr = ep + 1; - dev->ep_out[ep].nr = ep + 1; - dev->ep_in[ep].pid = USB_TOKEN_IN; - dev->ep_out[ep].pid = USB_TOKEN_OUT; - dev->ep_in[ep].type = USB_ENDPOINT_XFER_INVALID; - dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID; - dev->ep_in[ep].ifnum = USB_INTERFACE_INVALID; - dev->ep_out[ep].ifnum = USB_INTERFACE_INVALID; - dev->ep_in[ep].max_packet_size = 0; - dev->ep_out[ep].max_packet_size = 0; - dev->ep_in[ep].max_streams = 0; - dev->ep_out[ep].max_streams = 0; - dev->ep_in[ep].dev = dev; - dev->ep_out[ep].dev = dev; - dev->ep_in[ep].pipeline = false; - dev->ep_out[ep].pipeline = false; - } -} - -void usb_ep_init(USBDevice *dev) -{ - int ep; - - usb_ep_reset(dev); - QTAILQ_INIT(&dev->ep_ctl.queue); - for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { - QTAILQ_INIT(&dev->ep_in[ep].queue); - QTAILQ_INIT(&dev->ep_out[ep].queue); - } -} - -void usb_ep_dump(USBDevice *dev) -{ - static const char *tname[] = { - [USB_ENDPOINT_XFER_CONTROL] = "control", - [USB_ENDPOINT_XFER_ISOC] = "isoc", - [USB_ENDPOINT_XFER_BULK] = "bulk", - [USB_ENDPOINT_XFER_INT] = "int", - }; - int ifnum, ep, first; - - fprintf(stderr, "Device \"%s\", config %d\n", - dev->product_desc, dev->configuration); - for (ifnum = 0; ifnum < 16; ifnum++) { - first = 1; - for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { - if (dev->ep_in[ep].type != USB_ENDPOINT_XFER_INVALID && - dev->ep_in[ep].ifnum == ifnum) { - if (first) { - first = 0; - fprintf(stderr, " Interface %d, alternative %d\n", - ifnum, dev->altsetting[ifnum]); - } - fprintf(stderr, " Endpoint %d, IN, %s, %d max\n", ep, - tname[dev->ep_in[ep].type], - dev->ep_in[ep].max_packet_size); - } - if (dev->ep_out[ep].type != USB_ENDPOINT_XFER_INVALID && - dev->ep_out[ep].ifnum == ifnum) { - if (first) { - first = 0; - fprintf(stderr, " Interface %d, alternative %d\n", - ifnum, dev->altsetting[ifnum]); - } - fprintf(stderr, " Endpoint %d, OUT, %s, %d max\n", ep, - tname[dev->ep_out[ep].type], - dev->ep_out[ep].max_packet_size); - } - } - } - fprintf(stderr, "--\n"); -} - -struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep) -{ - struct USBEndpoint *eps; - - if (dev == NULL) { - return NULL; - } - eps = (pid == USB_TOKEN_IN) ? dev->ep_in : dev->ep_out; - if (ep == 0) { - return &dev->ep_ctl; - } - assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT); - assert(ep > 0 && ep <= USB_MAX_ENDPOINTS); - return eps + ep - 1; -} - -uint8_t usb_ep_get_type(USBDevice *dev, int pid, int ep) -{ - struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); - return uep->type; -} - -void usb_ep_set_type(USBDevice *dev, int pid, int ep, uint8_t type) -{ - struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); - uep->type = type; -} - -void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum) -{ - struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); - uep->ifnum = ifnum; -} - -void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep, - uint16_t raw) -{ - struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); - int size, microframes; - - size = raw & 0x7ff; - switch ((raw >> 11) & 3) { - case 1: - microframes = 2; - break; - case 2: - microframes = 3; - break; - default: - microframes = 1; - break; - } - uep->max_packet_size = size * microframes; -} - -void usb_ep_set_max_streams(USBDevice *dev, int pid, int ep, uint8_t raw) -{ - struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); - int MaxStreams; - - MaxStreams = raw & 0x1f; - if (MaxStreams) { - uep->max_streams = 1 << MaxStreams; - } else { - uep->max_streams = 0; - } -} - -void usb_ep_set_halted(USBDevice *dev, int pid, int ep, bool halted) -{ - struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); - uep->halted = halted; -} - -USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep, - uint64_t id) -{ - struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); - USBPacket *p; - - QTAILQ_FOREACH(p, &uep->queue, queue) { - if (p->id == id) { - return p; - } - } - - return NULL; -} diff --git a/qemu/hw/usb/desc-msos.c b/qemu/hw/usb/desc-msos.c deleted file mode 100644 index 365291981..000000000 --- a/qemu/hw/usb/desc-msos.c +++ /dev/null @@ -1,239 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" - -/* - * Microsoft OS Descriptors - * - * Windows tries to fetch some special descriptors with informations - * specifically for windows. Presence is indicated using a special - * string @ index 0xee. There are two kinds of descriptors: - * - * compatid descriptor - * Used to bind drivers, if usb class isn't specific enougth. - * Used for PTP/MTP for example (both share the same usb class). - * - * properties descriptor - * Does carry registry entries. They show up in - * HLM\SYSTEM\CurrentControlSet\Enum\USB\\\Device Parameters - * - * Note that Windows caches the stuff it got in the registry, so when - * playing with this you have to delete registry subtrees to make - * windows query the device again: - * HLM\SYSTEM\CurrentControlSet\Control\usbflags - * HLM\SYSTEM\CurrentControlSet\Enum\USB - * Windows will complain it can't delete entries on the second one. - * It has deleted everything it had permissions too, which is enouth - * as this includes "Device Parameters". - * - * http://msdn.microsoft.com/en-us/library/windows/hardware/ff537430.aspx - * - */ - -/* ------------------------------------------------------------------ */ - -typedef struct msos_compat_hdr { - uint32_t dwLength; - uint8_t bcdVersion_lo; - uint8_t bcdVersion_hi; - uint8_t wIndex_lo; - uint8_t wIndex_hi; - uint8_t bCount; - uint8_t reserved[7]; -} QEMU_PACKED msos_compat_hdr; - -typedef struct msos_compat_func { - uint8_t bFirstInterfaceNumber; - uint8_t reserved_1; - char compatibleId[8]; - uint8_t subCompatibleId[8]; - uint8_t reserved_2[6]; -} QEMU_PACKED msos_compat_func; - -static int usb_desc_msos_compat(const USBDesc *desc, uint8_t *dest) -{ - msos_compat_hdr *hdr = (void *)dest; - msos_compat_func *func; - int length = sizeof(*hdr); - int count = 0; - - func = (void *)(dest + length); - func->bFirstInterfaceNumber = 0; - func->reserved_1 = 0x01; - if (desc->msos->CompatibleID) { - snprintf(func->compatibleId, sizeof(func->compatibleId), - "%s", desc->msos->CompatibleID); - } - length += sizeof(*func); - count++; - - hdr->dwLength = cpu_to_le32(length); - hdr->bcdVersion_lo = 0x00; - hdr->bcdVersion_hi = 0x01; - hdr->wIndex_lo = 0x04; - hdr->wIndex_hi = 0x00; - hdr->bCount = count; - return length; -} - -/* ------------------------------------------------------------------ */ - -typedef struct msos_prop_hdr { - uint32_t dwLength; - uint8_t bcdVersion_lo; - uint8_t bcdVersion_hi; - uint8_t wIndex_lo; - uint8_t wIndex_hi; - uint8_t wCount_lo; - uint8_t wCount_hi; -} QEMU_PACKED msos_prop_hdr; - -typedef struct msos_prop { - uint32_t dwLength; - uint32_t dwPropertyDataType; - uint8_t dwPropertyNameLength_lo; - uint8_t dwPropertyNameLength_hi; - uint8_t bPropertyName[]; -} QEMU_PACKED msos_prop; - -typedef struct msos_prop_data { - uint32_t dwPropertyDataLength; - uint8_t bPropertyData[]; -} QEMU_PACKED msos_prop_data; - -typedef enum msos_prop_type { - MSOS_REG_SZ = 1, - MSOS_REG_EXPAND_SZ = 2, - MSOS_REG_BINARY = 3, - MSOS_REG_DWORD_LE = 4, - MSOS_REG_DWORD_BE = 5, - MSOS_REG_LINK = 6, - MSOS_REG_MULTI_SZ = 7, -} msos_prop_type; - -static int usb_desc_msos_prop_name(struct msos_prop *prop, - const wchar_t *name) -{ - int length = wcslen(name) + 1; - int i; - - prop->dwPropertyNameLength_lo = usb_lo(length*2); - prop->dwPropertyNameLength_hi = usb_hi(length*2); - for (i = 0; i < length; i++) { - prop->bPropertyName[i*2] = usb_lo(name[i]); - prop->bPropertyName[i*2+1] = usb_hi(name[i]); - } - return length*2; -} - -static int usb_desc_msos_prop_str(uint8_t *dest, msos_prop_type type, - const wchar_t *name, const wchar_t *value) -{ - struct msos_prop *prop = (void *)dest; - struct msos_prop_data *data; - int length = sizeof(*prop); - int i, vlen = wcslen(value) + 1; - - prop->dwPropertyDataType = cpu_to_le32(type); - length += usb_desc_msos_prop_name(prop, name); - data = (void *)(dest + length); - - data->dwPropertyDataLength = cpu_to_le32(vlen*2); - length += sizeof(*prop); - - for (i = 0; i < vlen; i++) { - data->bPropertyData[i*2] = usb_lo(value[i]); - data->bPropertyData[i*2+1] = usb_hi(value[i]); - } - length += vlen*2; - - prop->dwLength = cpu_to_le32(length); - return length; -} - -static int usb_desc_msos_prop_dword(uint8_t *dest, const wchar_t *name, - uint32_t value) -{ - struct msos_prop *prop = (void *)dest; - struct msos_prop_data *data; - int length = sizeof(*prop); - - prop->dwPropertyDataType = cpu_to_le32(MSOS_REG_DWORD_LE); - length += usb_desc_msos_prop_name(prop, name); - data = (void *)(dest + length); - - data->dwPropertyDataLength = cpu_to_le32(4); - data->bPropertyData[0] = (value) & 0xff; - data->bPropertyData[1] = (value >> 8) & 0xff; - data->bPropertyData[2] = (value >> 16) & 0xff; - data->bPropertyData[3] = (value >> 24) & 0xff; - length += sizeof(*prop) + 4; - - prop->dwLength = cpu_to_le32(length); - return length; -} - -static int usb_desc_msos_prop(const USBDesc *desc, uint8_t *dest) -{ - msos_prop_hdr *hdr = (void *)dest; - int length = sizeof(*hdr); - int count = 0; - - if (desc->msos->Label) { - /* - * Given as example in the specs. Havn't figured yet where - * this label shows up in the windows gui. - */ - length += usb_desc_msos_prop_str(dest+length, MSOS_REG_SZ, - L"Label", desc->msos->Label); - count++; - } - - if (desc->msos->SelectiveSuspendEnabled) { - /* - * Signaling remote wakeup capability in the standard usb - * descriptors isn't enouth to make windows actually use it. - * This is the "Yes, we really mean it" registy entry to flip - * the switch in the windows drivers. - */ - length += usb_desc_msos_prop_dword(dest+length, - L"SelectiveSuspendEnabled", 1); - count++; - } - - hdr->dwLength = cpu_to_le32(length); - hdr->bcdVersion_lo = 0x00; - hdr->bcdVersion_hi = 0x01; - hdr->wIndex_lo = 0x05; - hdr->wIndex_hi = 0x00; - hdr->wCount_lo = usb_lo(count); - hdr->wCount_hi = usb_hi(count); - return length; -} - -/* ------------------------------------------------------------------ */ - -int usb_desc_msos(const USBDesc *desc, USBPacket *p, - int index, uint8_t *dest, size_t len) -{ - void *buf = g_malloc0(4096); - int length = 0; - - switch (index) { - case 0x0004: - length = usb_desc_msos_compat(desc, buf); - break; - case 0x0005: - length = usb_desc_msos_prop(desc, buf); - break; - } - - if (length > len) { - length = len; - } - memcpy(dest, buf, length); - g_free(buf); - - p->actual_length = length; - return 0; -} diff --git a/qemu/hw/usb/desc.c b/qemu/hw/usb/desc.c deleted file mode 100644 index adb026e43..000000000 --- a/qemu/hw/usb/desc.c +++ /dev/null @@ -1,804 +0,0 @@ -#include "qemu/osdep.h" - -#include "hw/usb.h" -#include "hw/usb/desc.h" -#include "trace.h" - -/* ------------------------------------------------------------------ */ - -int usb_desc_device(const USBDescID *id, const USBDescDevice *dev, - bool msos, uint8_t *dest, size_t len) -{ - uint8_t bLength = 0x12; - USBDescriptor *d = (void *)dest; - - if (len < bLength) { - return -1; - } - - d->bLength = bLength; - d->bDescriptorType = USB_DT_DEVICE; - - if (msos && dev->bcdUSB < 0x0200) { - /* - * Version 2.0+ required for microsoft os descriptors to work. - * Done this way so msos-desc compat property will handle both - * the version and the new descriptors being present. - */ - d->u.device.bcdUSB_lo = usb_lo(0x0200); - d->u.device.bcdUSB_hi = usb_hi(0x0200); - } else { - d->u.device.bcdUSB_lo = usb_lo(dev->bcdUSB); - d->u.device.bcdUSB_hi = usb_hi(dev->bcdUSB); - } - d->u.device.bDeviceClass = dev->bDeviceClass; - d->u.device.bDeviceSubClass = dev->bDeviceSubClass; - d->u.device.bDeviceProtocol = dev->bDeviceProtocol; - d->u.device.bMaxPacketSize0 = dev->bMaxPacketSize0; - - d->u.device.idVendor_lo = usb_lo(id->idVendor); - d->u.device.idVendor_hi = usb_hi(id->idVendor); - d->u.device.idProduct_lo = usb_lo(id->idProduct); - d->u.device.idProduct_hi = usb_hi(id->idProduct); - d->u.device.bcdDevice_lo = usb_lo(id->bcdDevice); - d->u.device.bcdDevice_hi = usb_hi(id->bcdDevice); - d->u.device.iManufacturer = id->iManufacturer; - d->u.device.iProduct = id->iProduct; - d->u.device.iSerialNumber = id->iSerialNumber; - - d->u.device.bNumConfigurations = dev->bNumConfigurations; - - return bLength; -} - -int usb_desc_device_qualifier(const USBDescDevice *dev, - uint8_t *dest, size_t len) -{ - uint8_t bLength = 0x0a; - USBDescriptor *d = (void *)dest; - - if (len < bLength) { - return -1; - } - - d->bLength = bLength; - d->bDescriptorType = USB_DT_DEVICE_QUALIFIER; - - d->u.device_qualifier.bcdUSB_lo = usb_lo(dev->bcdUSB); - d->u.device_qualifier.bcdUSB_hi = usb_hi(dev->bcdUSB); - d->u.device_qualifier.bDeviceClass = dev->bDeviceClass; - d->u.device_qualifier.bDeviceSubClass = dev->bDeviceSubClass; - d->u.device_qualifier.bDeviceProtocol = dev->bDeviceProtocol; - d->u.device_qualifier.bMaxPacketSize0 = dev->bMaxPacketSize0; - d->u.device_qualifier.bNumConfigurations = dev->bNumConfigurations; - d->u.device_qualifier.bReserved = 0; - - return bLength; -} - -int usb_desc_config(const USBDescConfig *conf, int flags, - uint8_t *dest, size_t len) -{ - uint8_t bLength = 0x09; - uint16_t wTotalLength = 0; - USBDescriptor *d = (void *)dest; - int i, rc; - - if (len < bLength) { - return -1; - } - - d->bLength = bLength; - d->bDescriptorType = USB_DT_CONFIG; - - d->u.config.bNumInterfaces = conf->bNumInterfaces; - d->u.config.bConfigurationValue = conf->bConfigurationValue; - d->u.config.iConfiguration = conf->iConfiguration; - d->u.config.bmAttributes = conf->bmAttributes; - d->u.config.bMaxPower = conf->bMaxPower; - wTotalLength += bLength; - - /* handle grouped interfaces if any */ - for (i = 0; i < conf->nif_groups; i++) { - rc = usb_desc_iface_group(&(conf->if_groups[i]), flags, - dest + wTotalLength, - len - wTotalLength); - if (rc < 0) { - return rc; - } - wTotalLength += rc; - } - - /* handle normal (ungrouped / no IAD) interfaces if any */ - for (i = 0; i < conf->nif; i++) { - rc = usb_desc_iface(conf->ifs + i, flags, - dest + wTotalLength, len - wTotalLength); - if (rc < 0) { - return rc; - } - wTotalLength += rc; - } - - d->u.config.wTotalLength_lo = usb_lo(wTotalLength); - d->u.config.wTotalLength_hi = usb_hi(wTotalLength); - return wTotalLength; -} - -int usb_desc_iface_group(const USBDescIfaceAssoc *iad, int flags, - uint8_t *dest, size_t len) -{ - int pos = 0; - int i = 0; - - /* handle interface association descriptor */ - uint8_t bLength = 0x08; - - if (len < bLength) { - return -1; - } - - dest[0x00] = bLength; - dest[0x01] = USB_DT_INTERFACE_ASSOC; - dest[0x02] = iad->bFirstInterface; - dest[0x03] = iad->bInterfaceCount; - dest[0x04] = iad->bFunctionClass; - dest[0x05] = iad->bFunctionSubClass; - dest[0x06] = iad->bFunctionProtocol; - dest[0x07] = iad->iFunction; - pos += bLength; - - /* handle associated interfaces in this group */ - for (i = 0; i < iad->nif; i++) { - int rc = usb_desc_iface(&(iad->ifs[i]), flags, dest + pos, len - pos); - if (rc < 0) { - return rc; - } - pos += rc; - } - - return pos; -} - -int usb_desc_iface(const USBDescIface *iface, int flags, - uint8_t *dest, size_t len) -{ - uint8_t bLength = 0x09; - int i, rc, pos = 0; - USBDescriptor *d = (void *)dest; - - if (len < bLength) { - return -1; - } - - d->bLength = bLength; - d->bDescriptorType = USB_DT_INTERFACE; - - d->u.interface.bInterfaceNumber = iface->bInterfaceNumber; - d->u.interface.bAlternateSetting = iface->bAlternateSetting; - d->u.interface.bNumEndpoints = iface->bNumEndpoints; - d->u.interface.bInterfaceClass = iface->bInterfaceClass; - d->u.interface.bInterfaceSubClass = iface->bInterfaceSubClass; - d->u.interface.bInterfaceProtocol = iface->bInterfaceProtocol; - d->u.interface.iInterface = iface->iInterface; - pos += bLength; - - for (i = 0; i < iface->ndesc; i++) { - rc = usb_desc_other(iface->descs + i, dest + pos, len - pos); - if (rc < 0) { - return rc; - } - pos += rc; - } - - for (i = 0; i < iface->bNumEndpoints; i++) { - rc = usb_desc_endpoint(iface->eps + i, flags, dest + pos, len - pos); - if (rc < 0) { - return rc; - } - pos += rc; - } - - return pos; -} - -int usb_desc_endpoint(const USBDescEndpoint *ep, int flags, - uint8_t *dest, size_t len) -{ - uint8_t bLength = ep->is_audio ? 0x09 : 0x07; - uint8_t extralen = ep->extra ? ep->extra[0] : 0; - uint8_t superlen = (flags & USB_DESC_FLAG_SUPER) ? 0x06 : 0; - USBDescriptor *d = (void *)dest; - - if (len < bLength + extralen + superlen) { - return -1; - } - - d->bLength = bLength; - d->bDescriptorType = USB_DT_ENDPOINT; - - d->u.endpoint.bEndpointAddress = ep->bEndpointAddress; - d->u.endpoint.bmAttributes = ep->bmAttributes; - d->u.endpoint.wMaxPacketSize_lo = usb_lo(ep->wMaxPacketSize); - d->u.endpoint.wMaxPacketSize_hi = usb_hi(ep->wMaxPacketSize); - d->u.endpoint.bInterval = ep->bInterval; - if (ep->is_audio) { - d->u.endpoint.bRefresh = ep->bRefresh; - d->u.endpoint.bSynchAddress = ep->bSynchAddress; - } - - if (superlen) { - USBDescriptor *d = (void *)(dest + bLength); - - d->bLength = 0x06; - d->bDescriptorType = USB_DT_ENDPOINT_COMPANION; - - d->u.super_endpoint.bMaxBurst = ep->bMaxBurst; - d->u.super_endpoint.bmAttributes = ep->bmAttributes_super; - d->u.super_endpoint.wBytesPerInterval_lo = - usb_lo(ep->wBytesPerInterval); - d->u.super_endpoint.wBytesPerInterval_hi = - usb_hi(ep->wBytesPerInterval); - } - - if (ep->extra) { - memcpy(dest + bLength + superlen, ep->extra, extralen); - } - - return bLength + extralen + superlen; -} - -int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len) -{ - int bLength = desc->length ? desc->length : desc->data[0]; - - if (len < bLength) { - return -1; - } - - memcpy(dest, desc->data, bLength); - return bLength; -} - -static int usb_desc_cap_usb2_ext(const USBDesc *desc, uint8_t *dest, size_t len) -{ - uint8_t bLength = 0x07; - USBDescriptor *d = (void *)dest; - - if (len < bLength) { - return -1; - } - - d->bLength = bLength; - d->bDescriptorType = USB_DT_DEVICE_CAPABILITY; - d->u.cap.bDevCapabilityType = USB_DEV_CAP_USB2_EXT; - - d->u.cap.u.usb2_ext.bmAttributes_1 = (1 << 1); /* LPM */ - d->u.cap.u.usb2_ext.bmAttributes_2 = 0; - d->u.cap.u.usb2_ext.bmAttributes_3 = 0; - d->u.cap.u.usb2_ext.bmAttributes_4 = 0; - - return bLength; -} - -static int usb_desc_cap_super(const USBDesc *desc, uint8_t *dest, size_t len) -{ - uint8_t bLength = 0x0a; - USBDescriptor *d = (void *)dest; - - if (len < bLength) { - return -1; - } - - d->bLength = bLength; - d->bDescriptorType = USB_DT_DEVICE_CAPABILITY; - d->u.cap.bDevCapabilityType = USB_DEV_CAP_SUPERSPEED; - - d->u.cap.u.super.bmAttributes = 0; - d->u.cap.u.super.wSpeedsSupported_lo = 0; - d->u.cap.u.super.wSpeedsSupported_hi = 0; - d->u.cap.u.super.bFunctionalitySupport = 0; - d->u.cap.u.super.bU1DevExitLat = 0x0a; - d->u.cap.u.super.wU2DevExitLat_lo = 0x20; - d->u.cap.u.super.wU2DevExitLat_hi = 0; - - if (desc->full) { - d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 1); - d->u.cap.u.super.bFunctionalitySupport = 1; - } - if (desc->high) { - d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 2); - if (!d->u.cap.u.super.bFunctionalitySupport) { - d->u.cap.u.super.bFunctionalitySupport = 2; - } - } - if (desc->super) { - d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 3); - if (!d->u.cap.u.super.bFunctionalitySupport) { - d->u.cap.u.super.bFunctionalitySupport = 3; - } - } - - return bLength; -} - -static int usb_desc_bos(const USBDesc *desc, uint8_t *dest, size_t len) -{ - uint8_t bLength = 0x05; - uint16_t wTotalLength = 0; - uint8_t bNumDeviceCaps = 0; - USBDescriptor *d = (void *)dest; - int rc; - - if (len < bLength) { - return -1; - } - - d->bLength = bLength; - d->bDescriptorType = USB_DT_BOS; - - wTotalLength += bLength; - - if (desc->high != NULL) { - rc = usb_desc_cap_usb2_ext(desc, dest + wTotalLength, - len - wTotalLength); - if (rc < 0) { - return rc; - } - wTotalLength += rc; - bNumDeviceCaps++; - } - - if (desc->super != NULL) { - rc = usb_desc_cap_super(desc, dest + wTotalLength, - len - wTotalLength); - if (rc < 0) { - return rc; - } - wTotalLength += rc; - bNumDeviceCaps++; - } - - d->u.bos.wTotalLength_lo = usb_lo(wTotalLength); - d->u.bos.wTotalLength_hi = usb_hi(wTotalLength); - d->u.bos.bNumDeviceCaps = bNumDeviceCaps; - return wTotalLength; -} - -/* ------------------------------------------------------------------ */ - -static void usb_desc_ep_init(USBDevice *dev) -{ - const USBDescIface *iface; - int i, e, pid, ep; - - usb_ep_init(dev); - for (i = 0; i < dev->ninterfaces; i++) { - iface = dev->ifaces[i]; - if (iface == NULL) { - continue; - } - for (e = 0; e < iface->bNumEndpoints; e++) { - pid = (iface->eps[e].bEndpointAddress & USB_DIR_IN) ? - USB_TOKEN_IN : USB_TOKEN_OUT; - ep = iface->eps[e].bEndpointAddress & 0x0f; - usb_ep_set_type(dev, pid, ep, iface->eps[e].bmAttributes & 0x03); - usb_ep_set_ifnum(dev, pid, ep, iface->bInterfaceNumber); - usb_ep_set_max_packet_size(dev, pid, ep, - iface->eps[e].wMaxPacketSize); - usb_ep_set_max_streams(dev, pid, ep, - iface->eps[e].bmAttributes_super); - } - } -} - -static const USBDescIface *usb_desc_find_interface(USBDevice *dev, - int nif, int alt) -{ - const USBDescIface *iface; - int g, i; - - if (!dev->config) { - return NULL; - } - for (g = 0; g < dev->config->nif_groups; g++) { - for (i = 0; i < dev->config->if_groups[g].nif; i++) { - iface = &dev->config->if_groups[g].ifs[i]; - if (iface->bInterfaceNumber == nif && - iface->bAlternateSetting == alt) { - return iface; - } - } - } - for (i = 0; i < dev->config->nif; i++) { - iface = &dev->config->ifs[i]; - if (iface->bInterfaceNumber == nif && - iface->bAlternateSetting == alt) { - return iface; - } - } - return NULL; -} - -static int usb_desc_set_interface(USBDevice *dev, int index, int value) -{ - const USBDescIface *iface; - int old; - - iface = usb_desc_find_interface(dev, index, value); - if (iface == NULL) { - return -1; - } - - old = dev->altsetting[index]; - dev->altsetting[index] = value; - dev->ifaces[index] = iface; - usb_desc_ep_init(dev); - - if (old != value) { - usb_device_set_interface(dev, index, old, value); - } - return 0; -} - -static int usb_desc_set_config(USBDevice *dev, int value) -{ - int i; - - if (value == 0) { - dev->configuration = 0; - dev->ninterfaces = 0; - dev->config = NULL; - } else { - for (i = 0; i < dev->device->bNumConfigurations; i++) { - if (dev->device->confs[i].bConfigurationValue == value) { - dev->configuration = value; - dev->ninterfaces = dev->device->confs[i].bNumInterfaces; - dev->config = dev->device->confs + i; - assert(dev->ninterfaces <= USB_MAX_INTERFACES); - } - } - if (i < dev->device->bNumConfigurations) { - return -1; - } - } - - for (i = 0; i < dev->ninterfaces; i++) { - usb_desc_set_interface(dev, i, 0); - } - for (; i < USB_MAX_INTERFACES; i++) { - dev->altsetting[i] = 0; - dev->ifaces[i] = NULL; - } - - return 0; -} - -static void usb_desc_setdefaults(USBDevice *dev) -{ - const USBDesc *desc = usb_device_get_usb_desc(dev); - - assert(desc != NULL); - switch (dev->speed) { - case USB_SPEED_LOW: - case USB_SPEED_FULL: - dev->device = desc->full; - break; - case USB_SPEED_HIGH: - dev->device = desc->high; - break; - case USB_SPEED_SUPER: - dev->device = desc->super; - break; - } - usb_desc_set_config(dev, 0); -} - -void usb_desc_init(USBDevice *dev) -{ - const USBDesc *desc = usb_device_get_usb_desc(dev); - - assert(desc != NULL); - dev->speed = USB_SPEED_FULL; - dev->speedmask = 0; - if (desc->full) { - dev->speedmask |= USB_SPEED_MASK_FULL; - } - if (desc->high) { - dev->speedmask |= USB_SPEED_MASK_HIGH; - } - if (desc->super) { - dev->speedmask |= USB_SPEED_MASK_SUPER; - } - if (desc->msos && (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_ENABLE))) { - dev->flags |= (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE); - usb_desc_set_string(dev, 0xee, "MSFT100Q"); - } - usb_desc_setdefaults(dev); -} - -void usb_desc_attach(USBDevice *dev) -{ - usb_desc_setdefaults(dev); -} - -void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str) -{ - USBDescString *s; - - QLIST_FOREACH(s, &dev->strings, next) { - if (s->index == index) { - break; - } - } - if (s == NULL) { - s = g_malloc0(sizeof(*s)); - s->index = index; - QLIST_INSERT_HEAD(&dev->strings, s, next); - } - g_free(s->str); - s->str = g_strdup(str); -} - -/* - * This function creates a serial number for a usb device. - * The serial number should: - * (a) Be unique within the virtual machine. - * (b) Be constant, so you don't get a new one each - * time the guest is started. - * So we are using the physical location to generate a serial number - * from it. It has three pieces: First a fixed, device-specific - * prefix. Second the device path of the host controller (which is - * the pci address in most cases). Third the physical port path. - * Results in serial numbers like this: "314159-0000:00:1d.7-3". - */ -void usb_desc_create_serial(USBDevice *dev) -{ - DeviceState *hcd = dev->qdev.parent_bus->parent; - const USBDesc *desc = usb_device_get_usb_desc(dev); - int index = desc->id.iSerialNumber; - char serial[64]; - char *path; - int dst; - - if (dev->serial) { - /* 'serial' usb bus property has priority if present */ - usb_desc_set_string(dev, index, dev->serial); - return; - } - - assert(index != 0 && desc->str[index] != NULL); - dst = snprintf(serial, sizeof(serial), "%s", desc->str[index]); - path = qdev_get_dev_path(hcd); - if (path) { - dst += snprintf(serial+dst, sizeof(serial)-dst, "-%s", path); - } - dst += snprintf(serial+dst, sizeof(serial)-dst, "-%s", dev->port->path); - usb_desc_set_string(dev, index, serial); -} - -const char *usb_desc_get_string(USBDevice *dev, uint8_t index) -{ - USBDescString *s; - - QLIST_FOREACH(s, &dev->strings, next) { - if (s->index == index) { - return s->str; - } - } - return NULL; -} - -int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len) -{ - uint8_t bLength, pos, i; - const char *str; - - if (len < 4) { - return -1; - } - - if (index == 0) { - /* language ids */ - dest[0] = 4; - dest[1] = USB_DT_STRING; - dest[2] = 0x09; - dest[3] = 0x04; - return 4; - } - - str = usb_desc_get_string(dev, index); - if (str == NULL) { - str = usb_device_get_usb_desc(dev)->str[index]; - if (str == NULL) { - return 0; - } - } - - bLength = strlen(str) * 2 + 2; - dest[0] = bLength; - dest[1] = USB_DT_STRING; - i = 0; pos = 2; - while (pos+1 < bLength && pos+1 < len) { - dest[pos++] = str[i++]; - dest[pos++] = 0; - } - return pos; -} - -int usb_desc_get_descriptor(USBDevice *dev, USBPacket *p, - int value, uint8_t *dest, size_t len) -{ - bool msos = (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE)); - const USBDesc *desc = usb_device_get_usb_desc(dev); - const USBDescDevice *other_dev; - uint8_t buf[256]; - uint8_t type = value >> 8; - uint8_t index = value & 0xff; - int flags, ret = -1; - - if (dev->speed == USB_SPEED_HIGH) { - other_dev = usb_device_get_usb_desc(dev)->full; - } else { - other_dev = usb_device_get_usb_desc(dev)->high; - } - - flags = 0; - if (dev->device->bcdUSB >= 0x0300) { - flags |= USB_DESC_FLAG_SUPER; - } - - switch(type) { - case USB_DT_DEVICE: - ret = usb_desc_device(&desc->id, dev->device, msos, buf, sizeof(buf)); - trace_usb_desc_device(dev->addr, len, ret); - break; - case USB_DT_CONFIG: - if (index < dev->device->bNumConfigurations) { - ret = usb_desc_config(dev->device->confs + index, flags, - buf, sizeof(buf)); - } - trace_usb_desc_config(dev->addr, index, len, ret); - break; - case USB_DT_STRING: - ret = usb_desc_string(dev, index, buf, sizeof(buf)); - trace_usb_desc_string(dev->addr, index, len, ret); - break; - case USB_DT_DEVICE_QUALIFIER: - if (other_dev != NULL) { - ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf)); - } - trace_usb_desc_device_qualifier(dev->addr, len, ret); - break; - case USB_DT_OTHER_SPEED_CONFIG: - if (other_dev != NULL && index < other_dev->bNumConfigurations) { - ret = usb_desc_config(other_dev->confs + index, flags, - buf, sizeof(buf)); - buf[0x01] = USB_DT_OTHER_SPEED_CONFIG; - } - trace_usb_desc_other_speed_config(dev->addr, index, len, ret); - break; - case USB_DT_BOS: - ret = usb_desc_bos(desc, buf, sizeof(buf)); - trace_usb_desc_bos(dev->addr, len, ret); - break; - - case USB_DT_DEBUG: - /* ignore silently */ - break; - - default: - fprintf(stderr, "%s: %d unknown type %d (len %zd)\n", __FUNCTION__, - dev->addr, type, len); - break; - } - - if (ret > 0) { - if (ret > len) { - ret = len; - } - memcpy(dest, buf, ret); - p->actual_length = ret; - ret = 0; - } - return ret; -} - -int usb_desc_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) -{ - bool msos = (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE)); - const USBDesc *desc = usb_device_get_usb_desc(dev); - int ret = -1; - - assert(desc != NULL); - switch(request) { - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - trace_usb_set_addr(dev->addr); - ret = 0; - break; - - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - ret = usb_desc_get_descriptor(dev, p, value, data, length); - break; - - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - /* - * 9.4.2: 0 should be returned if the device is unconfigured, otherwise - * the non zero value of bConfigurationValue. - */ - data[0] = dev->config ? dev->config->bConfigurationValue : 0; - p->actual_length = 1; - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - ret = usb_desc_set_config(dev, value); - trace_usb_set_config(dev->addr, value, ret); - break; - - case DeviceRequest | USB_REQ_GET_STATUS: { - const USBDescConfig *config = dev->config ? - dev->config : &dev->device->confs[0]; - - data[0] = 0; - /* - * Default state: Device behavior when this request is received while - * the device is in the Default state is not specified. - * We return the same value that a configured device would return if - * it used the first configuration. - */ - if (config->bmAttributes & USB_CFG_ATT_SELFPOWER) { - data[0] |= 1 << USB_DEVICE_SELF_POWERED; - } - if (dev->remote_wakeup) { - data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP; - } - data[1] = 0x00; - p->actual_length = 2; - ret = 0; - break; - } - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - ret = 0; - } - trace_usb_clear_device_feature(dev->addr, value, ret); - break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - ret = 0; - } - trace_usb_set_device_feature(dev->addr, value, ret); - break; - - case InterfaceRequest | USB_REQ_GET_INTERFACE: - if (index < 0 || index >= dev->ninterfaces) { - break; - } - data[0] = dev->altsetting[index]; - p->actual_length = 1; - ret = 0; - break; - case InterfaceOutRequest | USB_REQ_SET_INTERFACE: - ret = usb_desc_set_interface(dev, index, value); - trace_usb_set_interface(dev->addr, index, value, ret); - break; - - case VendorDeviceRequest | 'Q': - if (msos) { - ret = usb_desc_msos(desc, p, index, data, length); - trace_usb_desc_msos(dev->addr, index, length, ret); - } - break; - case VendorInterfaceRequest | 'Q': - if (msos) { - ret = usb_desc_msos(desc, p, index, data, length); - trace_usb_desc_msos(dev->addr, index, length, ret); - } - break; - - } - return ret; -} diff --git a/qemu/hw/usb/desc.h b/qemu/hw/usb/desc.h deleted file mode 100644 index 4d81c68e0..000000000 --- a/qemu/hw/usb/desc.h +++ /dev/null @@ -1,244 +0,0 @@ -#ifndef QEMU_HW_USB_DESC_H -#define QEMU_HW_USB_DESC_H - -#include - -/* binary representation */ -typedef struct USBDescriptor { - uint8_t bLength; - uint8_t bDescriptorType; - union { - struct { - uint8_t bcdUSB_lo; - uint8_t bcdUSB_hi; - uint8_t bDeviceClass; - uint8_t bDeviceSubClass; - uint8_t bDeviceProtocol; - uint8_t bMaxPacketSize0; - uint8_t idVendor_lo; - uint8_t idVendor_hi; - uint8_t idProduct_lo; - uint8_t idProduct_hi; - uint8_t bcdDevice_lo; - uint8_t bcdDevice_hi; - uint8_t iManufacturer; - uint8_t iProduct; - uint8_t iSerialNumber; - uint8_t bNumConfigurations; - } device; - struct { - uint8_t bcdUSB_lo; - uint8_t bcdUSB_hi; - uint8_t bDeviceClass; - uint8_t bDeviceSubClass; - uint8_t bDeviceProtocol; - uint8_t bMaxPacketSize0; - uint8_t bNumConfigurations; - uint8_t bReserved; - } device_qualifier; - struct { - uint8_t wTotalLength_lo; - uint8_t wTotalLength_hi; - uint8_t bNumInterfaces; - uint8_t bConfigurationValue; - uint8_t iConfiguration; - uint8_t bmAttributes; - uint8_t bMaxPower; - } config; - struct { - uint8_t bInterfaceNumber; - uint8_t bAlternateSetting; - uint8_t bNumEndpoints; - uint8_t bInterfaceClass; - uint8_t bInterfaceSubClass; - uint8_t bInterfaceProtocol; - uint8_t iInterface; - } interface; - struct { - uint8_t bEndpointAddress; - uint8_t bmAttributes; - uint8_t wMaxPacketSize_lo; - uint8_t wMaxPacketSize_hi; - uint8_t bInterval; - uint8_t bRefresh; /* only audio ep */ - uint8_t bSynchAddress; /* only audio ep */ - } endpoint; - struct { - uint8_t bMaxBurst; - uint8_t bmAttributes; - uint8_t wBytesPerInterval_lo; - uint8_t wBytesPerInterval_hi; - } super_endpoint; - struct { - uint8_t wTotalLength_lo; - uint8_t wTotalLength_hi; - uint8_t bNumDeviceCaps; - } bos; - struct { - uint8_t bDevCapabilityType; - union { - struct { - uint8_t bmAttributes_1; - uint8_t bmAttributes_2; - uint8_t bmAttributes_3; - uint8_t bmAttributes_4; - } usb2_ext; - struct { - uint8_t bmAttributes; - uint8_t wSpeedsSupported_lo; - uint8_t wSpeedsSupported_hi; - uint8_t bFunctionalitySupport; - uint8_t bU1DevExitLat; - uint8_t wU2DevExitLat_lo; - uint8_t wU2DevExitLat_hi; - } super; - } u; - } cap; - } u; -} QEMU_PACKED USBDescriptor; - -struct USBDescID { - uint16_t idVendor; - uint16_t idProduct; - uint16_t bcdDevice; - uint8_t iManufacturer; - uint8_t iProduct; - uint8_t iSerialNumber; -}; - -struct USBDescDevice { - uint16_t bcdUSB; - uint8_t bDeviceClass; - uint8_t bDeviceSubClass; - uint8_t bDeviceProtocol; - uint8_t bMaxPacketSize0; - uint8_t bNumConfigurations; - - const USBDescConfig *confs; -}; - -struct USBDescConfig { - uint8_t bNumInterfaces; - uint8_t bConfigurationValue; - uint8_t iConfiguration; - uint8_t bmAttributes; - uint8_t bMaxPower; - - /* grouped interfaces */ - uint8_t nif_groups; - const USBDescIfaceAssoc *if_groups; - - /* "normal" interfaces */ - uint8_t nif; - const USBDescIface *ifs; -}; - -/* conceptually an Interface Association Descriptor, and releated interfaces */ -struct USBDescIfaceAssoc { - uint8_t bFirstInterface; - uint8_t bInterfaceCount; - uint8_t bFunctionClass; - uint8_t bFunctionSubClass; - uint8_t bFunctionProtocol; - uint8_t iFunction; - - uint8_t nif; - const USBDescIface *ifs; -}; - -struct USBDescIface { - uint8_t bInterfaceNumber; - uint8_t bAlternateSetting; - uint8_t bNumEndpoints; - uint8_t bInterfaceClass; - uint8_t bInterfaceSubClass; - uint8_t bInterfaceProtocol; - uint8_t iInterface; - - uint8_t ndesc; - USBDescOther *descs; - USBDescEndpoint *eps; -}; - -struct USBDescEndpoint { - uint8_t bEndpointAddress; - uint8_t bmAttributes; - uint16_t wMaxPacketSize; - uint8_t bInterval; - uint8_t bRefresh; - uint8_t bSynchAddress; - - uint8_t is_audio; /* has bRefresh + bSynchAddress */ - uint8_t *extra; - - /* superspeed endpoint companion */ - uint8_t bMaxBurst; - uint8_t bmAttributes_super; - uint16_t wBytesPerInterval; -}; - -struct USBDescOther { - uint8_t length; - const uint8_t *data; -}; - -struct USBDescMSOS { - const char *CompatibleID; - const wchar_t *Label; - bool SelectiveSuspendEnabled; -}; - -typedef const char *USBDescStrings[256]; - -struct USBDesc { - USBDescID id; - const USBDescDevice *full; - const USBDescDevice *high; - const USBDescDevice *super; - const char* const *str; - const USBDescMSOS *msos; -}; - -#define USB_DESC_FLAG_SUPER (1 << 1) - -/* little helpers */ -static inline uint8_t usb_lo(uint16_t val) -{ - return val & 0xff; -} - -static inline uint8_t usb_hi(uint16_t val) -{ - return (val >> 8) & 0xff; -} - -/* generate usb packages from structs */ -int usb_desc_device(const USBDescID *id, const USBDescDevice *dev, - bool msos, uint8_t *dest, size_t len); -int usb_desc_device_qualifier(const USBDescDevice *dev, - uint8_t *dest, size_t len); -int usb_desc_config(const USBDescConfig *conf, int flags, - uint8_t *dest, size_t len); -int usb_desc_iface_group(const USBDescIfaceAssoc *iad, int flags, - uint8_t *dest, size_t len); -int usb_desc_iface(const USBDescIface *iface, int flags, - uint8_t *dest, size_t len); -int usb_desc_endpoint(const USBDescEndpoint *ep, int flags, - uint8_t *dest, size_t len); -int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len); -int usb_desc_msos(const USBDesc *desc, USBPacket *p, - int index, uint8_t *dest, size_t len); - -/* control message emulation helpers */ -void usb_desc_init(USBDevice *dev); -void usb_desc_attach(USBDevice *dev); -void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str); -void usb_desc_create_serial(USBDevice *dev); -const char *usb_desc_get_string(USBDevice *dev, uint8_t index); -int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len); -int usb_desc_get_descriptor(USBDevice *dev, USBPacket *p, - int value, uint8_t *dest, size_t len); -int usb_desc_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data); - -#endif /* QEMU_HW_USB_DESC_H */ diff --git a/qemu/hw/usb/dev-audio.c b/qemu/hw/usb/dev-audio.c deleted file mode 100644 index 87cab0a3d..000000000 --- a/qemu/hw/usb/dev-audio.c +++ /dev/null @@ -1,703 +0,0 @@ -/* - * QEMU USB audio device - * - * written by: - * H. Peter Anvin - * Gerd Hoffmann - * - * lousely based on usb net device code which is: - * - * Copyright (c) 2006 Thomas Sailer - * Copyright (c) 2008 Andrzej Zaborowski - * - * 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 "qemu-common.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" -#include "hw/hw.h" -#include "audio/audio.h" - -#define USBAUDIO_VENDOR_NUM 0x46f4 /* CRC16() of "QEMU" */ -#define USBAUDIO_PRODUCT_NUM 0x0002 - -#define DEV_CONFIG_VALUE 1 /* The one and only */ - -/* Descriptor subtypes for AC interfaces */ -#define DST_AC_HEADER 1 -#define DST_AC_INPUT_TERMINAL 2 -#define DST_AC_OUTPUT_TERMINAL 3 -#define DST_AC_FEATURE_UNIT 6 -/* Descriptor subtypes for AS interfaces */ -#define DST_AS_GENERAL 1 -#define DST_AS_FORMAT_TYPE 2 -/* Descriptor subtypes for endpoints */ -#define DST_EP_GENERAL 1 - -enum usb_audio_strings { - STRING_NULL, - STRING_MANUFACTURER, - STRING_PRODUCT, - STRING_SERIALNUMBER, - STRING_CONFIG, - STRING_USBAUDIO_CONTROL, - STRING_INPUT_TERMINAL, - STRING_FEATURE_UNIT, - STRING_OUTPUT_TERMINAL, - STRING_NULL_STREAM, - STRING_REAL_STREAM, -}; - -static const USBDescStrings usb_audio_stringtable = { - [STRING_MANUFACTURER] = "QEMU", - [STRING_PRODUCT] = "QEMU USB Audio", - [STRING_SERIALNUMBER] = "1", - [STRING_CONFIG] = "Audio Configuration", - [STRING_USBAUDIO_CONTROL] = "Audio Device", - [STRING_INPUT_TERMINAL] = "Audio Output Pipe", - [STRING_FEATURE_UNIT] = "Audio Output Volume Control", - [STRING_OUTPUT_TERMINAL] = "Audio Output Terminal", - [STRING_NULL_STREAM] = "Audio Output - Disabled", - [STRING_REAL_STREAM] = "Audio Output - 48 kHz Stereo", -}; - -#define U16(x) ((x) & 0xff), (((x) >> 8) & 0xff) -#define U24(x) U16(x), (((x) >> 16) & 0xff) -#define U32(x) U24(x), (((x) >> 24) & 0xff) - -/* - * A Basic Audio Device uses these specific values - */ -#define USBAUDIO_PACKET_SIZE 192 -#define USBAUDIO_SAMPLE_RATE 48000 -#define USBAUDIO_PACKET_INTERVAL 1 - -static const USBDescIface desc_iface[] = { - { - .bInterfaceNumber = 0, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL, - .bInterfaceProtocol = 0x04, - .iInterface = STRING_USBAUDIO_CONTROL, - .ndesc = 4, - .descs = (USBDescOther[]) { - { - /* Headphone Class-Specific AC Interface Header Descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - DST_AC_HEADER, /* u8 bDescriptorSubtype */ - U16(0x0100), /* u16 bcdADC */ - U16(0x2b), /* u16 wTotalLength */ - 0x01, /* u8 bInCollection */ - 0x01, /* u8 baInterfaceNr */ - } - },{ - /* Generic Stereo Input Terminal ID1 Descriptor */ - .data = (uint8_t[]) { - 0x0c, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - DST_AC_INPUT_TERMINAL, /* u8 bDescriptorSubtype */ - 0x01, /* u8 bTerminalID */ - U16(0x0101), /* u16 wTerminalType */ - 0x00, /* u8 bAssocTerminal */ - 0x02, /* u16 bNrChannels */ - U16(0x0003), /* u16 wChannelConfig */ - 0x00, /* u8 iChannelNames */ - STRING_INPUT_TERMINAL, /* u8 iTerminal */ - } - },{ - /* Generic Stereo Feature Unit ID2 Descriptor */ - .data = (uint8_t[]) { - 0x0d, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - DST_AC_FEATURE_UNIT, /* u8 bDescriptorSubtype */ - 0x02, /* u8 bUnitID */ - 0x01, /* u8 bSourceID */ - 0x02, /* u8 bControlSize */ - U16(0x0001), /* u16 bmaControls(0) */ - U16(0x0002), /* u16 bmaControls(1) */ - U16(0x0002), /* u16 bmaControls(2) */ - STRING_FEATURE_UNIT, /* u8 iFeature */ - } - },{ - /* Headphone Ouptut Terminal ID3 Descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - DST_AC_OUTPUT_TERMINAL, /* u8 bDescriptorSubtype */ - 0x03, /* u8 bUnitID */ - U16(0x0301), /* u16 wTerminalType (SPK) */ - 0x00, /* u8 bAssocTerminal */ - 0x02, /* u8 bSourceID */ - STRING_OUTPUT_TERMINAL, /* u8 iTerminal */ - } - } - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 0, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, - .iInterface = STRING_NULL_STREAM, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 1, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, - .iInterface = STRING_REAL_STREAM, - .ndesc = 2, - .descs = (USBDescOther[]) { - { - /* Headphone Class-specific AS General Interface Descriptor */ - .data = (uint8_t[]) { - 0x07, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - DST_AS_GENERAL, /* u8 bDescriptorSubtype */ - 0x01, /* u8 bTerminalLink */ - 0x00, /* u8 bDelay */ - 0x01, 0x00, /* u16 wFormatTag */ - } - },{ - /* Headphone Type I Format Type Descriptor */ - .data = (uint8_t[]) { - 0x0b, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - DST_AS_FORMAT_TYPE, /* u8 bDescriptorSubtype */ - 0x01, /* u8 bFormatType */ - 0x02, /* u8 bNrChannels */ - 0x02, /* u8 bSubFrameSize */ - 0x10, /* u8 bBitResolution */ - 0x01, /* u8 bSamFreqType */ - U24(USBAUDIO_SAMPLE_RATE), /* u24 tSamFreq */ - } - } - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | 0x01, - .bmAttributes = 0x0d, - .wMaxPacketSize = USBAUDIO_PACKET_SIZE, - .bInterval = 1, - .is_audio = 1, - /* Stereo Headphone Class-specific - AS Audio Data Endpoint Descriptor */ - .extra = (uint8_t[]) { - 0x07, /* u8 bLength */ - USB_DT_CS_ENDPOINT, /* u8 bDescriptorType */ - DST_EP_GENERAL, /* u8 bDescriptorSubtype */ - 0x00, /* u8 bmAttributes */ - 0x00, /* u8 bLockDelayUnits */ - U16(0x0000), /* u16 wLockDelay */ - }, - }, - } - } -}; - -static const USBDescDevice desc_device = { - .bcdUSB = 0x0100, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 2, - .bConfigurationValue = DEV_CONFIG_VALUE, - .iConfiguration = STRING_CONFIG, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .bMaxPower = 0x32, - .nif = ARRAY_SIZE(desc_iface), - .ifs = desc_iface, - }, - }, -}; - -static const USBDesc desc_audio = { - .id = { - .idVendor = USBAUDIO_VENDOR_NUM, - .idProduct = USBAUDIO_PRODUCT_NUM, - .bcdDevice = 0, - .iManufacturer = STRING_MANUFACTURER, - .iProduct = STRING_PRODUCT, - .iSerialNumber = STRING_SERIALNUMBER, - }, - .full = &desc_device, - .str = usb_audio_stringtable, -}; - -/* - * A USB audio device supports an arbitrary number of alternate - * interface settings for each interface. Each corresponds to a block - * diagram of parameterized blocks. This can thus refer to things like - * number of channels, data rates, or in fact completely different - * block diagrams. Alternative setting 0 is always the null block diagram, - * which is used by a disabled device. - */ -enum usb_audio_altset { - ALTSET_OFF = 0x00, /* No endpoint */ - ALTSET_ON = 0x01, /* Single endpoint */ -}; - -/* - * Class-specific control requests - */ -#define CR_SET_CUR 0x01 -#define CR_GET_CUR 0x81 -#define CR_SET_MIN 0x02 -#define CR_GET_MIN 0x82 -#define CR_SET_MAX 0x03 -#define CR_GET_MAX 0x83 -#define CR_SET_RES 0x04 -#define CR_GET_RES 0x84 -#define CR_SET_MEM 0x05 -#define CR_GET_MEM 0x85 -#define CR_GET_STAT 0xff - -/* - * Feature Unit Control Selectors - */ -#define MUTE_CONTROL 0x01 -#define VOLUME_CONTROL 0x02 -#define BASS_CONTROL 0x03 -#define MID_CONTROL 0x04 -#define TREBLE_CONTROL 0x05 -#define GRAPHIC_EQUALIZER_CONTROL 0x06 -#define AUTOMATIC_GAIN_CONTROL 0x07 -#define DELAY_CONTROL 0x08 -#define BASS_BOOST_CONTROL 0x09 -#define LOUDNESS_CONTROL 0x0a - -/* - * buffering - */ - -struct streambuf { - uint8_t *data; - uint32_t size; - uint32_t prod; - uint32_t cons; -}; - -static void streambuf_init(struct streambuf *buf, uint32_t size) -{ - g_free(buf->data); - buf->size = size - (size % USBAUDIO_PACKET_SIZE); - buf->data = g_malloc(buf->size); - buf->prod = 0; - buf->cons = 0; -} - -static void streambuf_fini(struct streambuf *buf) -{ - g_free(buf->data); - buf->data = NULL; -} - -static int streambuf_put(struct streambuf *buf, USBPacket *p) -{ - uint32_t free = buf->size - (buf->prod - buf->cons); - - if (!free) { - return 0; - } - assert(free >= USBAUDIO_PACKET_SIZE); - usb_packet_copy(p, buf->data + (buf->prod % buf->size), - USBAUDIO_PACKET_SIZE); - buf->prod += USBAUDIO_PACKET_SIZE; - return USBAUDIO_PACKET_SIZE; -} - -static uint8_t *streambuf_get(struct streambuf *buf) -{ - uint32_t used = buf->prod - buf->cons; - uint8_t *data; - - if (!used) { - return NULL; - } - assert(used >= USBAUDIO_PACKET_SIZE); - data = buf->data + (buf->cons % buf->size); - buf->cons += USBAUDIO_PACKET_SIZE; - return data; -} - -typedef struct USBAudioState { - /* qemu interfaces */ - USBDevice dev; - QEMUSoundCard card; - - /* state */ - struct { - enum usb_audio_altset altset; - struct audsettings as; - SWVoiceOut *voice; - bool mute; - uint8_t vol[2]; - struct streambuf buf; - } out; - - /* properties */ - uint32_t debug; - uint32_t buffer; -} USBAudioState; - -#define TYPE_USB_AUDIO "usb-audio" -#define USB_AUDIO(obj) OBJECT_CHECK(USBAudioState, (obj), TYPE_USB_AUDIO) - -static void output_callback(void *opaque, int avail) -{ - USBAudioState *s = opaque; - uint8_t *data; - - for (;;) { - if (avail < USBAUDIO_PACKET_SIZE) { - return; - } - data = streambuf_get(&s->out.buf); - if (!data) { - return; - } - AUD_write(s->out.voice, data, USBAUDIO_PACKET_SIZE); - avail -= USBAUDIO_PACKET_SIZE; - } -} - -static int usb_audio_set_output_altset(USBAudioState *s, int altset) -{ - switch (altset) { - case ALTSET_OFF: - streambuf_init(&s->out.buf, s->buffer); - AUD_set_active_out(s->out.voice, false); - break; - case ALTSET_ON: - AUD_set_active_out(s->out.voice, true); - break; - default: - return -1; - } - - if (s->debug) { - fprintf(stderr, "usb-audio: set interface %d\n", altset); - } - s->out.altset = altset; - return 0; -} - -/* - * Note: we arbitrarily map the volume control range onto -inf..+8 dB - */ -#define ATTRIB_ID(cs, attrib, idif) \ - (((cs) << 24) | ((attrib) << 16) | (idif)) - -static int usb_audio_get_control(USBAudioState *s, uint8_t attrib, - uint16_t cscn, uint16_t idif, - int length, uint8_t *data) -{ - uint8_t cs = cscn >> 8; - uint8_t cn = cscn - 1; /* -1 for the non-present master control */ - uint32_t aid = ATTRIB_ID(cs, attrib, idif); - int ret = USB_RET_STALL; - - switch (aid) { - case ATTRIB_ID(MUTE_CONTROL, CR_GET_CUR, 0x0200): - data[0] = s->out.mute; - ret = 1; - break; - case ATTRIB_ID(VOLUME_CONTROL, CR_GET_CUR, 0x0200): - if (cn < 2) { - uint16_t vol = (s->out.vol[cn] * 0x8800 + 127) / 255 + 0x8000; - data[0] = vol; - data[1] = vol >> 8; - ret = 2; - } - break; - case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MIN, 0x0200): - if (cn < 2) { - data[0] = 0x01; - data[1] = 0x80; - ret = 2; - } - break; - case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MAX, 0x0200): - if (cn < 2) { - data[0] = 0x00; - data[1] = 0x08; - ret = 2; - } - break; - case ATTRIB_ID(VOLUME_CONTROL, CR_GET_RES, 0x0200): - if (cn < 2) { - data[0] = 0x88; - data[1] = 0x00; - ret = 2; - } - break; - } - - return ret; -} -static int usb_audio_set_control(USBAudioState *s, uint8_t attrib, - uint16_t cscn, uint16_t idif, - int length, uint8_t *data) -{ - uint8_t cs = cscn >> 8; - uint8_t cn = cscn - 1; /* -1 for the non-present master control */ - uint32_t aid = ATTRIB_ID(cs, attrib, idif); - int ret = USB_RET_STALL; - bool set_vol = false; - - switch (aid) { - case ATTRIB_ID(MUTE_CONTROL, CR_SET_CUR, 0x0200): - s->out.mute = data[0] & 1; - set_vol = true; - ret = 0; - break; - case ATTRIB_ID(VOLUME_CONTROL, CR_SET_CUR, 0x0200): - if (cn < 2) { - uint16_t vol = data[0] + (data[1] << 8); - - if (s->debug) { - fprintf(stderr, "usb-audio: vol %04x\n", (uint16_t)vol); - } - - vol -= 0x8000; - vol = (vol * 255 + 0x4400) / 0x8800; - if (vol > 255) { - vol = 255; - } - - s->out.vol[cn] = vol; - set_vol = true; - ret = 0; - } - break; - } - - if (set_vol) { - if (s->debug) { - fprintf(stderr, "usb-audio: mute %d, lvol %3d, rvol %3d\n", - s->out.mute, s->out.vol[0], s->out.vol[1]); - } - AUD_set_volume_out(s->out.voice, s->out.mute, - s->out.vol[0], s->out.vol[1]); - } - - return ret; -} - -static void usb_audio_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, - int length, uint8_t *data) -{ - USBAudioState *s = USB_AUDIO(dev); - int ret = 0; - - if (s->debug) { - fprintf(stderr, "usb-audio: control transaction: " - "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n", - request, value, index, length); - } - - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - switch (request) { - case ClassInterfaceRequest | CR_GET_CUR: - case ClassInterfaceRequest | CR_GET_MIN: - case ClassInterfaceRequest | CR_GET_MAX: - case ClassInterfaceRequest | CR_GET_RES: - ret = usb_audio_get_control(s, request & 0xff, value, index, - length, data); - if (ret < 0) { - if (s->debug) { - fprintf(stderr, "usb-audio: fail: get control\n"); - } - goto fail; - } - p->actual_length = ret; - break; - - case ClassInterfaceOutRequest | CR_SET_CUR: - case ClassInterfaceOutRequest | CR_SET_MIN: - case ClassInterfaceOutRequest | CR_SET_MAX: - case ClassInterfaceOutRequest | CR_SET_RES: - ret = usb_audio_set_control(s, request & 0xff, value, index, - length, data); - if (ret < 0) { - if (s->debug) { - fprintf(stderr, "usb-audio: fail: set control\n"); - } - goto fail; - } - break; - - default: -fail: - if (s->debug) { - fprintf(stderr, "usb-audio: failed control transaction: " - "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n", - request, value, index, length); - } - p->status = USB_RET_STALL; - break; - } -} - -static void usb_audio_set_interface(USBDevice *dev, int iface, - int old, int value) -{ - USBAudioState *s = USB_AUDIO(dev); - - if (iface == 1) { - usb_audio_set_output_altset(s, value); - } -} - -static void usb_audio_handle_reset(USBDevice *dev) -{ - USBAudioState *s = USB_AUDIO(dev); - - if (s->debug) { - fprintf(stderr, "usb-audio: reset\n"); - } - usb_audio_set_output_altset(s, ALTSET_OFF); -} - -static void usb_audio_handle_dataout(USBAudioState *s, USBPacket *p) -{ - if (s->out.altset == ALTSET_OFF) { - p->status = USB_RET_STALL; - return; - } - - streambuf_put(&s->out.buf, p); - if (p->actual_length < p->iov.size && s->debug > 1) { - fprintf(stderr, "usb-audio: output overrun (%zd bytes)\n", - p->iov.size - p->actual_length); - } -} - -static void usb_audio_handle_data(USBDevice *dev, USBPacket *p) -{ - USBAudioState *s = (USBAudioState *) dev; - - if (p->pid == USB_TOKEN_OUT && p->ep->nr == 1) { - usb_audio_handle_dataout(s, p); - return; - } - - p->status = USB_RET_STALL; - if (s->debug) { - fprintf(stderr, "usb-audio: failed data transaction: " - "pid 0x%x ep 0x%x len 0x%zx\n", - p->pid, p->ep->nr, p->iov.size); - } -} - -static void usb_audio_handle_destroy(USBDevice *dev) -{ - USBAudioState *s = USB_AUDIO(dev); - - if (s->debug) { - fprintf(stderr, "usb-audio: destroy\n"); - } - - usb_audio_set_output_altset(s, ALTSET_OFF); - AUD_close_out(&s->card, s->out.voice); - AUD_remove_card(&s->card); - - streambuf_fini(&s->out.buf); -} - -static void usb_audio_realize(USBDevice *dev, Error **errp) -{ - USBAudioState *s = USB_AUDIO(dev); - - usb_desc_create_serial(dev); - usb_desc_init(dev); - s->dev.opaque = s; - AUD_register_card(TYPE_USB_AUDIO, &s->card); - - s->out.altset = ALTSET_OFF; - s->out.mute = false; - s->out.vol[0] = 240; /* 0 dB */ - s->out.vol[1] = 240; /* 0 dB */ - s->out.as.freq = USBAUDIO_SAMPLE_RATE; - s->out.as.nchannels = 2; - s->out.as.fmt = AUD_FMT_S16; - s->out.as.endianness = 0; - streambuf_init(&s->out.buf, s->buffer); - - s->out.voice = AUD_open_out(&s->card, s->out.voice, TYPE_USB_AUDIO, - s, output_callback, &s->out.as); - AUD_set_volume_out(s->out.voice, s->out.mute, s->out.vol[0], s->out.vol[1]); - AUD_set_active_out(s->out.voice, 0); -} - -static const VMStateDescription vmstate_usb_audio = { - .name = TYPE_USB_AUDIO, - .unmigratable = 1, -}; - -static Property usb_audio_properties[] = { - DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0), - DEFINE_PROP_UINT32("buffer", USBAudioState, buffer, - 32 * USBAUDIO_PACKET_SIZE), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_audio_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *k = USB_DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_usb_audio; - dc->props = usb_audio_properties; - set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - k->product_desc = "QEMU USB Audio Interface"; - k->usb_desc = &desc_audio; - k->realize = usb_audio_realize; - k->handle_reset = usb_audio_handle_reset; - k->handle_control = usb_audio_handle_control; - k->handle_data = usb_audio_handle_data; - k->handle_destroy = usb_audio_handle_destroy; - k->set_interface = usb_audio_set_interface; -} - -static const TypeInfo usb_audio_info = { - .name = TYPE_USB_AUDIO, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBAudioState), - .class_init = usb_audio_class_init, -}; - -static void usb_audio_register_types(void) -{ - type_register_static(&usb_audio_info); - usb_legacy_register(TYPE_USB_AUDIO, "audio", NULL); -} - -type_init(usb_audio_register_types) diff --git a/qemu/hw/usb/dev-bluetooth.c b/qemu/hw/usb/dev-bluetooth.c deleted file mode 100644 index 91a4a0b8b..000000000 --- a/qemu/hw/usb/dev-bluetooth.c +++ /dev/null @@ -1,580 +0,0 @@ -/* - * QEMU Bluetooth HCI USB Transport Layer v1.0 - * - * Copyright (C) 2007 OpenMoko, Inc. - * Copyright (C) 2008 Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/error-report.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" -#include "sysemu/bt.h" -#include "hw/bt.h" - -struct USBBtState { - USBDevice dev; - struct HCIInfo *hci; - USBEndpoint *intr; - - int config; - -#define CFIFO_LEN_MASK 255 -#define DFIFO_LEN_MASK 4095 - struct usb_hci_in_fifo_s { - uint8_t data[(DFIFO_LEN_MASK + 1) * 2]; - struct { - uint8_t *data; - int len; - } fifo[CFIFO_LEN_MASK + 1]; - int dstart, dlen, dsize, start, len; - } evt, acl, sco; - - struct usb_hci_out_fifo_s { - uint8_t data[4096]; - int len; - } outcmd, outacl, outsco; -}; - -#define TYPE_USB_BT "usb-bt-dongle" -#define USB_BT(obj) OBJECT_CHECK(struct USBBtState, (obj), TYPE_USB_BT) - -#define USB_EVT_EP 1 -#define USB_ACL_EP 2 -#define USB_SCO_EP 3 - -enum { - STR_MANUFACTURER = 1, - STR_SERIALNUMBER, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = "QEMU", - [STR_SERIALNUMBER] = "1", -}; - -static const USBDescIface desc_iface_bluetooth[] = { - { - .bInterfaceNumber = 0, - .bNumEndpoints = 3, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | USB_EVT_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 0x10, - .bInterval = 0x02, - }, - { - .bEndpointAddress = USB_DIR_OUT | USB_ACL_EP, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 0x40, - .bInterval = 0x0a, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_ACL_EP, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 0x40, - .bInterval = 0x0a, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0, - .bInterval = 0x01, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 1, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x09, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x09, - .bInterval = 0x01, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 2, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x11, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x11, - .bInterval = 0x01, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 3, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x19, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x19, - .bInterval = 0x01, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 4, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x21, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x21, - .bInterval = 0x01, - }, - }, - },{ - .bInterfaceNumber = 1, - .bAlternateSetting = 5, - .bNumEndpoints = 2, - .bInterfaceClass = 0xe0, /* Wireless */ - .bInterfaceSubClass = 0x01, /* Radio Frequency */ - .bInterfaceProtocol = 0x01, /* Bluetooth */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x31, - .bInterval = 0x01, - }, - { - .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = 0x31, - .bInterval = 0x01, - }, - }, - } -}; - -static const USBDescDevice desc_device_bluetooth = { - .bcdUSB = 0x0110, - .bDeviceClass = 0xe0, /* Wireless */ - .bDeviceSubClass = 0x01, /* Radio Frequency */ - .bDeviceProtocol = 0x01, /* Bluetooth */ - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 2, - .bConfigurationValue = 1, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .bMaxPower = 0, - .nif = ARRAY_SIZE(desc_iface_bluetooth), - .ifs = desc_iface_bluetooth, - }, - }, -}; - -static const USBDesc desc_bluetooth = { - .id = { - .idVendor = 0x0a12, - .idProduct = 0x0001, - .bcdDevice = 0x1958, - .iManufacturer = STR_MANUFACTURER, - .iProduct = 0, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_bluetooth, - .str = desc_strings, -}; - -static void usb_bt_fifo_reset(struct usb_hci_in_fifo_s *fifo) -{ - fifo->dstart = 0; - fifo->dlen = 0; - fifo->dsize = DFIFO_LEN_MASK + 1; - fifo->start = 0; - fifo->len = 0; -} - -static void usb_bt_fifo_enqueue(struct usb_hci_in_fifo_s *fifo, - const uint8_t *data, int len) -{ - int off = fifo->dstart + fifo->dlen; - uint8_t *buf; - - fifo->dlen += len; - if (off <= DFIFO_LEN_MASK) { - if (off + len > DFIFO_LEN_MASK + 1 && - (fifo->dsize = off + len) > (DFIFO_LEN_MASK + 1) * 2) { - fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len); - exit(-1); - } - buf = fifo->data + off; - } else { - if (fifo->dlen > fifo->dsize) { - fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len); - exit(-1); - } - buf = fifo->data + off - fifo->dsize; - } - - off = (fifo->start + fifo->len ++) & CFIFO_LEN_MASK; - fifo->fifo[off].data = memcpy(buf, data, len); - fifo->fifo[off].len = len; -} - -static inline void usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo, - USBPacket *p) -{ - int len; - - assert(fifo->len != 0); - - len = MIN(p->iov.size, fifo->fifo[fifo->start].len); - usb_packet_copy(p, fifo->fifo[fifo->start].data, len); - if (len == p->iov.size) { - fifo->fifo[fifo->start].len -= len; - fifo->fifo[fifo->start].data += len; - } else { - fifo->start ++; - fifo->start &= CFIFO_LEN_MASK; - fifo->len --; - } - - fifo->dstart += len; - fifo->dlen -= len; - if (fifo->dstart >= fifo->dsize) { - fifo->dstart = 0; - fifo->dsize = DFIFO_LEN_MASK + 1; - } -} - -static inline void usb_bt_fifo_out_enqueue(struct USBBtState *s, - struct usb_hci_out_fifo_s *fifo, - void (*send)(struct HCIInfo *, const uint8_t *, int), - int (*complete)(const uint8_t *, int), - USBPacket *p) -{ - usb_packet_copy(p, fifo->data + fifo->len, p->iov.size); - fifo->len += p->iov.size; - if (complete(fifo->data, fifo->len)) { - send(s->hci, fifo->data, fifo->len); - fifo->len = 0; - } - - /* TODO: do we need to loop? */ -} - -static int usb_bt_hci_cmd_complete(const uint8_t *data, int len) -{ - len -= HCI_COMMAND_HDR_SIZE; - return len >= 0 && - len >= ((struct hci_command_hdr *) data)->plen; -} - -static int usb_bt_hci_acl_complete(const uint8_t *data, int len) -{ - len -= HCI_ACL_HDR_SIZE; - return len >= 0 && - len >= le16_to_cpu(((struct hci_acl_hdr *) data)->dlen); -} - -static int usb_bt_hci_sco_complete(const uint8_t *data, int len) -{ - len -= HCI_SCO_HDR_SIZE; - return len >= 0 && - len >= ((struct hci_sco_hdr *) data)->dlen; -} - -static void usb_bt_handle_reset(USBDevice *dev) -{ - struct USBBtState *s = (struct USBBtState *) dev->opaque; - - usb_bt_fifo_reset(&s->evt); - usb_bt_fifo_reset(&s->acl); - usb_bt_fifo_reset(&s->sco); - s->outcmd.len = 0; - s->outacl.len = 0; - s->outsco.len = 0; -} - -static void usb_bt_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) -{ - struct USBBtState *s = (struct USBBtState *) dev->opaque; - int ret; - - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - switch (request) { - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - s->config = 0; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - s->config = 1; - usb_bt_fifo_reset(&s->evt); - usb_bt_fifo_reset(&s->acl); - usb_bt_fifo_reset(&s->sco); - break; - } - return; - } - - switch (request) { - case InterfaceRequest | USB_REQ_GET_STATUS: - case EndpointRequest | USB_REQ_GET_STATUS: - data[0] = 0x00; - data[1] = 0x00; - p->actual_length = 2; - break; - case InterfaceOutRequest | USB_REQ_CLEAR_FEATURE: - case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: - goto fail; - case InterfaceOutRequest | USB_REQ_SET_FEATURE: - case EndpointOutRequest | USB_REQ_SET_FEATURE: - goto fail; - break; - case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8): - if (s->config) - usb_bt_fifo_out_enqueue(s, &s->outcmd, s->hci->cmd_send, - usb_bt_hci_cmd_complete, p); - break; - default: - fail: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_bt_handle_data(USBDevice *dev, USBPacket *p) -{ - struct USBBtState *s = (struct USBBtState *) dev->opaque; - - if (!s->config) - goto fail; - - switch (p->pid) { - case USB_TOKEN_IN: - switch (p->ep->nr) { - case USB_EVT_EP: - if (s->evt.len == 0) { - p->status = USB_RET_NAK; - break; - } - usb_bt_fifo_dequeue(&s->evt, p); - break; - - case USB_ACL_EP: - if (s->evt.len == 0) { - p->status = USB_RET_STALL; - break; - } - usb_bt_fifo_dequeue(&s->acl, p); - break; - - case USB_SCO_EP: - if (s->evt.len == 0) { - p->status = USB_RET_STALL; - break; - } - usb_bt_fifo_dequeue(&s->sco, p); - break; - - default: - goto fail; - } - break; - - case USB_TOKEN_OUT: - switch (p->ep->nr) { - case USB_ACL_EP: - usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send, - usb_bt_hci_acl_complete, p); - break; - - case USB_SCO_EP: - usb_bt_fifo_out_enqueue(s, &s->outsco, s->hci->sco_send, - usb_bt_hci_sco_complete, p); - break; - - default: - goto fail; - } - break; - - default: - fail: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_bt_out_hci_packet_event(void *opaque, - const uint8_t *data, int len) -{ - struct USBBtState *s = (struct USBBtState *) opaque; - - if (s->evt.len == 0) { - usb_wakeup(s->intr, 0); - } - usb_bt_fifo_enqueue(&s->evt, data, len); -} - -static void usb_bt_out_hci_packet_acl(void *opaque, - const uint8_t *data, int len) -{ - struct USBBtState *s = (struct USBBtState *) opaque; - - usb_bt_fifo_enqueue(&s->acl, data, len); -} - -static void usb_bt_handle_destroy(USBDevice *dev) -{ - struct USBBtState *s = (struct USBBtState *) dev->opaque; - - s->hci->opaque = NULL; - s->hci->evt_recv = NULL; - s->hci->acl_recv = NULL; -} - -static void usb_bt_realize(USBDevice *dev, Error **errp) -{ - struct USBBtState *s = USB_BT(dev); - - usb_desc_create_serial(dev); - usb_desc_init(dev); - s->dev.opaque = s; - if (!s->hci) { - s->hci = bt_new_hci(qemu_find_bt_vlan(0)); - } - s->hci->opaque = s; - s->hci->evt_recv = usb_bt_out_hci_packet_event; - s->hci->acl_recv = usb_bt_out_hci_packet_acl; - usb_bt_handle_reset(&s->dev); - s->intr = usb_ep_get(dev, USB_TOKEN_IN, USB_EVT_EP); -} - -static USBDevice *usb_bt_init(USBBus *bus, const char *cmdline) -{ - USBDevice *dev; - struct USBBtState *s; - HCIInfo *hci; - const char *name = TYPE_USB_BT; - - if (*cmdline) { - hci = hci_init(cmdline); - } else { - hci = bt_new_hci(qemu_find_bt_vlan(0)); - } - if (!hci) - return NULL; - - dev = usb_create(bus, name); - s = USB_BT(dev); - s->hci = hci; - return dev; -} - -static const VMStateDescription vmstate_usb_bt = { - .name = "usb-bt", - .unmigratable = 1, -}; - -static void usb_bt_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_bt_realize; - uc->product_desc = "QEMU BT dongle"; - uc->usb_desc = &desc_bluetooth; - uc->handle_reset = usb_bt_handle_reset; - uc->handle_control = usb_bt_handle_control; - uc->handle_data = usb_bt_handle_data; - uc->handle_destroy = usb_bt_handle_destroy; - dc->vmsd = &vmstate_usb_bt; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); -} - -static const TypeInfo bt_info = { - .name = TYPE_USB_BT, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(struct USBBtState), - .class_init = usb_bt_class_initfn, -}; - -static void usb_bt_register_types(void) -{ - type_register_static(&bt_info); - usb_legacy_register(TYPE_USB_BT, "bt", usb_bt_init); -} - -type_init(usb_bt_register_types) diff --git a/qemu/hw/usb/dev-hid.c b/qemu/hw/usb/dev-hid.c deleted file mode 100644 index 24d05f76f..000000000 --- a/qemu/hw/usb/dev-hid.c +++ /dev/null @@ -1,883 +0,0 @@ -/* - * QEMU USB HID devices - * - * Copyright (c) 2005 Fabrice Bellard - * Copyright (c) 2007 OpenMoko, Inc. (andrew@openedhand.com) - * - * 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 "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" - -/* HID interface requests */ -#define GET_REPORT 0xa101 -#define GET_IDLE 0xa102 -#define GET_PROTOCOL 0xa103 -#define SET_REPORT 0x2109 -#define SET_IDLE 0x210a -#define SET_PROTOCOL 0x210b - -/* HID descriptor types */ -#define USB_DT_HID 0x21 -#define USB_DT_REPORT 0x22 -#define USB_DT_PHY 0x23 - -typedef struct USBHIDState { - USBDevice dev; - USBEndpoint *intr; - HIDState hid; - uint32_t usb_version; - char *display; - uint32_t head; -} USBHIDState; - -#define TYPE_USB_HID "usb-hid" -#define USB_HID(obj) OBJECT_CHECK(USBHIDState, (obj), TYPE_USB_HID) - -enum { - STR_MANUFACTURER = 1, - STR_PRODUCT_MOUSE, - STR_PRODUCT_TABLET, - STR_PRODUCT_KEYBOARD, - STR_SERIALNUMBER, - STR_CONFIG_MOUSE, - STR_CONFIG_TABLET, - STR_CONFIG_KEYBOARD, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = "QEMU", - [STR_PRODUCT_MOUSE] = "QEMU USB Mouse", - [STR_PRODUCT_TABLET] = "QEMU USB Tablet", - [STR_PRODUCT_KEYBOARD] = "QEMU USB Keyboard", - [STR_SERIALNUMBER] = "42", /* == remote wakeup works */ - [STR_CONFIG_MOUSE] = "HID Mouse", - [STR_CONFIG_TABLET] = "HID Tablet", - [STR_CONFIG_KEYBOARD] = "HID Keyboard", -}; - -static const USBDescIface desc_iface_mouse = { - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceSubClass = 0x01, /* boot */ - .bInterfaceProtocol = 0x02, - .ndesc = 1, - .descs = (USBDescOther[]) { - { - /* HID descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - USB_DT_HID, /* u8 bDescriptorType */ - 0x01, 0x00, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - USB_DT_REPORT, /* u8 type: Report */ - 52, 0, /* u16 len */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 4, - .bInterval = 0x0a, - }, - }, -}; - -static const USBDescIface desc_iface_mouse2 = { - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceSubClass = 0x01, /* boot */ - .bInterfaceProtocol = 0x02, - .ndesc = 1, - .descs = (USBDescOther[]) { - { - /* HID descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - USB_DT_HID, /* u8 bDescriptorType */ - 0x01, 0x00, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - USB_DT_REPORT, /* u8 type: Report */ - 52, 0, /* u16 len */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 4, - .bInterval = 7, /* 2 ^ (8-1) * 125 usecs = 8 ms */ - }, - }, -}; - -static const USBDescIface desc_iface_tablet = { - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceProtocol = 0x02, - .ndesc = 1, - .descs = (USBDescOther[]) { - { - /* HID descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - USB_DT_HID, /* u8 bDescriptorType */ - 0x01, 0x00, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - USB_DT_REPORT, /* u8 type: Report */ - 74, 0, /* u16 len */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 8, - .bInterval = 0x0a, - }, - }, -}; - -static const USBDescIface desc_iface_tablet2 = { - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceProtocol = 0x02, - .ndesc = 1, - .descs = (USBDescOther[]) { - { - /* HID descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - USB_DT_HID, /* u8 bDescriptorType */ - 0x01, 0x00, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - USB_DT_REPORT, /* u8 type: Report */ - 74, 0, /* u16 len */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 8, - .bInterval = 4, /* 2 ^ (4-1) * 125 usecs = 1 ms */ - }, - }, -}; - -static const USBDescIface desc_iface_keyboard = { - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceSubClass = 0x01, /* boot */ - .bInterfaceProtocol = 0x01, /* keyboard */ - .ndesc = 1, - .descs = (USBDescOther[]) { - { - /* HID descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - USB_DT_HID, /* u8 bDescriptorType */ - 0x11, 0x01, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - USB_DT_REPORT, /* u8 type: Report */ - 0x3f, 0, /* u16 len */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 8, - .bInterval = 0x0a, - }, - }, -}; - -static const USBDescIface desc_iface_keyboard2 = { - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceSubClass = 0x01, /* boot */ - .bInterfaceProtocol = 0x01, /* keyboard */ - .ndesc = 1, - .descs = (USBDescOther[]) { - { - /* HID descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - USB_DT_HID, /* u8 bDescriptorType */ - 0x11, 0x01, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - USB_DT_REPORT, /* u8 type: Report */ - 0x3f, 0, /* u16 len */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 8, - .bInterval = 7, /* 2 ^ (8-1) * 125 usecs = 8 ms */ - }, - }, -}; - -static const USBDescDevice desc_device_mouse = { - .bcdUSB = 0x0100, - .bMaxPacketSize0 = 8, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_MOUSE, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, - .bMaxPower = 50, - .nif = 1, - .ifs = &desc_iface_mouse, - }, - }, -}; - -static const USBDescDevice desc_device_mouse2 = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_MOUSE, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, - .bMaxPower = 50, - .nif = 1, - .ifs = &desc_iface_mouse2, - }, - }, -}; - -static const USBDescDevice desc_device_tablet = { - .bcdUSB = 0x0100, - .bMaxPacketSize0 = 8, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_TABLET, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, - .bMaxPower = 50, - .nif = 1, - .ifs = &desc_iface_tablet, - }, - }, -}; - -static const USBDescDevice desc_device_tablet2 = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_TABLET, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, - .bMaxPower = 50, - .nif = 1, - .ifs = &desc_iface_tablet2, - }, - }, -}; - -static const USBDescDevice desc_device_keyboard = { - .bcdUSB = 0x0100, - .bMaxPacketSize0 = 8, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_KEYBOARD, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, - .bMaxPower = 50, - .nif = 1, - .ifs = &desc_iface_keyboard, - }, - }, -}; - -static const USBDescDevice desc_device_keyboard2 = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_KEYBOARD, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, - .bMaxPower = 50, - .nif = 1, - .ifs = &desc_iface_keyboard2, - }, - }, -}; - -static const USBDescMSOS desc_msos_suspend = { - .SelectiveSuspendEnabled = true, -}; - -static const USBDesc desc_mouse = { - .id = { - .idVendor = 0x0627, - .idProduct = 0x0001, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT_MOUSE, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_mouse, - .str = desc_strings, - .msos = &desc_msos_suspend, -}; - -static const USBDesc desc_mouse2 = { - .id = { - .idVendor = 0x0627, - .idProduct = 0x0001, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT_MOUSE, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_mouse, - .high = &desc_device_mouse2, - .str = desc_strings, - .msos = &desc_msos_suspend, -}; - -static const USBDesc desc_tablet = { - .id = { - .idVendor = 0x0627, - .idProduct = 0x0001, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT_TABLET, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_tablet, - .str = desc_strings, - .msos = &desc_msos_suspend, -}; - -static const USBDesc desc_tablet2 = { - .id = { - .idVendor = 0x0627, - .idProduct = 0x0001, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT_TABLET, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_tablet, - .high = &desc_device_tablet2, - .str = desc_strings, - .msos = &desc_msos_suspend, -}; - -static const USBDesc desc_keyboard = { - .id = { - .idVendor = 0x0627, - .idProduct = 0x0001, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT_KEYBOARD, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_keyboard, - .str = desc_strings, - .msos = &desc_msos_suspend, -}; - -static const USBDesc desc_keyboard2 = { - .id = { - .idVendor = 0x0627, - .idProduct = 0x0001, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT_KEYBOARD, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_keyboard, - .high = &desc_device_keyboard2, - .str = desc_strings, - .msos = &desc_msos_suspend, -}; - -static const uint8_t qemu_mouse_hid_report_descriptor[] = { - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x02, /* Usage (Mouse) */ - 0xa1, 0x01, /* Collection (Application) */ - 0x09, 0x01, /* Usage (Pointer) */ - 0xa1, 0x00, /* Collection (Physical) */ - 0x05, 0x09, /* Usage Page (Button) */ - 0x19, 0x01, /* Usage Minimum (1) */ - 0x29, 0x03, /* Usage Maximum (3) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0x95, 0x03, /* Report Count (3) */ - 0x75, 0x01, /* Report Size (1) */ - 0x81, 0x02, /* Input (Data, Variable, Absolute) */ - 0x95, 0x01, /* Report Count (1) */ - 0x75, 0x05, /* Report Size (5) */ - 0x81, 0x01, /* Input (Constant) */ - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x30, /* Usage (X) */ - 0x09, 0x31, /* Usage (Y) */ - 0x09, 0x38, /* Usage (Wheel) */ - 0x15, 0x81, /* Logical Minimum (-0x7f) */ - 0x25, 0x7f, /* Logical Maximum (0x7f) */ - 0x75, 0x08, /* Report Size (8) */ - 0x95, 0x03, /* Report Count (3) */ - 0x81, 0x06, /* Input (Data, Variable, Relative) */ - 0xc0, /* End Collection */ - 0xc0, /* End Collection */ -}; - -static const uint8_t qemu_tablet_hid_report_descriptor[] = { - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x01, /* Usage (Pointer) */ - 0xa1, 0x01, /* Collection (Application) */ - 0x09, 0x01, /* Usage (Pointer) */ - 0xa1, 0x00, /* Collection (Physical) */ - 0x05, 0x09, /* Usage Page (Button) */ - 0x19, 0x01, /* Usage Minimum (1) */ - 0x29, 0x03, /* Usage Maximum (3) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0x95, 0x03, /* Report Count (3) */ - 0x75, 0x01, /* Report Size (1) */ - 0x81, 0x02, /* Input (Data, Variable, Absolute) */ - 0x95, 0x01, /* Report Count (1) */ - 0x75, 0x05, /* Report Size (5) */ - 0x81, 0x01, /* Input (Constant) */ - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x30, /* Usage (X) */ - 0x09, 0x31, /* Usage (Y) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x26, 0xff, 0x7f, /* Logical Maximum (0x7fff) */ - 0x35, 0x00, /* Physical Minimum (0) */ - 0x46, 0xff, 0x7f, /* Physical Maximum (0x7fff) */ - 0x75, 0x10, /* Report Size (16) */ - 0x95, 0x02, /* Report Count (2) */ - 0x81, 0x02, /* Input (Data, Variable, Absolute) */ - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x38, /* Usage (Wheel) */ - 0x15, 0x81, /* Logical Minimum (-0x7f) */ - 0x25, 0x7f, /* Logical Maximum (0x7f) */ - 0x35, 0x00, /* Physical Minimum (same as logical) */ - 0x45, 0x00, /* Physical Maximum (same as logical) */ - 0x75, 0x08, /* Report Size (8) */ - 0x95, 0x01, /* Report Count (1) */ - 0x81, 0x06, /* Input (Data, Variable, Relative) */ - 0xc0, /* End Collection */ - 0xc0, /* End Collection */ -}; - -static const uint8_t qemu_keyboard_hid_report_descriptor[] = { - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x06, /* Usage (Keyboard) */ - 0xa1, 0x01, /* Collection (Application) */ - 0x75, 0x01, /* Report Size (1) */ - 0x95, 0x08, /* Report Count (8) */ - 0x05, 0x07, /* Usage Page (Key Codes) */ - 0x19, 0xe0, /* Usage Minimum (224) */ - 0x29, 0xe7, /* Usage Maximum (231) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0x81, 0x02, /* Input (Data, Variable, Absolute) */ - 0x95, 0x01, /* Report Count (1) */ - 0x75, 0x08, /* Report Size (8) */ - 0x81, 0x01, /* Input (Constant) */ - 0x95, 0x05, /* Report Count (5) */ - 0x75, 0x01, /* Report Size (1) */ - 0x05, 0x08, /* Usage Page (LEDs) */ - 0x19, 0x01, /* Usage Minimum (1) */ - 0x29, 0x05, /* Usage Maximum (5) */ - 0x91, 0x02, /* Output (Data, Variable, Absolute) */ - 0x95, 0x01, /* Report Count (1) */ - 0x75, 0x03, /* Report Size (3) */ - 0x91, 0x01, /* Output (Constant) */ - 0x95, 0x06, /* Report Count (6) */ - 0x75, 0x08, /* Report Size (8) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0xff, /* Logical Maximum (255) */ - 0x05, 0x07, /* Usage Page (Key Codes) */ - 0x19, 0x00, /* Usage Minimum (0) */ - 0x29, 0xff, /* Usage Maximum (255) */ - 0x81, 0x00, /* Input (Data, Array) */ - 0xc0, /* End Collection */ -}; - -static void usb_hid_changed(HIDState *hs) -{ - USBHIDState *us = container_of(hs, USBHIDState, hid); - - usb_wakeup(us->intr, 0); -} - -static void usb_hid_handle_reset(USBDevice *dev) -{ - USBHIDState *us = USB_HID(dev); - - hid_reset(&us->hid); -} - -static void usb_hid_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) -{ - USBHIDState *us = USB_HID(dev); - HIDState *hs = &us->hid; - int ret; - - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - switch (request) { - /* hid specific requests */ - case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: - switch (value >> 8) { - case 0x22: - if (hs->kind == HID_MOUSE) { - memcpy(data, qemu_mouse_hid_report_descriptor, - sizeof(qemu_mouse_hid_report_descriptor)); - p->actual_length = sizeof(qemu_mouse_hid_report_descriptor); - } else if (hs->kind == HID_TABLET) { - memcpy(data, qemu_tablet_hid_report_descriptor, - sizeof(qemu_tablet_hid_report_descriptor)); - p->actual_length = sizeof(qemu_tablet_hid_report_descriptor); - } else if (hs->kind == HID_KEYBOARD) { - memcpy(data, qemu_keyboard_hid_report_descriptor, - sizeof(qemu_keyboard_hid_report_descriptor)); - p->actual_length = sizeof(qemu_keyboard_hid_report_descriptor); - } - break; - default: - goto fail; - } - break; - case GET_REPORT: - if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { - p->actual_length = hid_pointer_poll(hs, data, length); - } else if (hs->kind == HID_KEYBOARD) { - p->actual_length = hid_keyboard_poll(hs, data, length); - } - break; - case SET_REPORT: - if (hs->kind == HID_KEYBOARD) { - p->actual_length = hid_keyboard_write(hs, data, length); - } else { - goto fail; - } - break; - case GET_PROTOCOL: - if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) { - goto fail; - } - data[0] = hs->protocol; - p->actual_length = 1; - break; - case SET_PROTOCOL: - if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) { - goto fail; - } - hs->protocol = value; - break; - case GET_IDLE: - data[0] = hs->idle; - p->actual_length = 1; - break; - case SET_IDLE: - hs->idle = (uint8_t) (value >> 8); - hid_set_next_idle(hs); - if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { - hid_pointer_activate(hs); - } - break; - default: - fail: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_hid_handle_data(USBDevice *dev, USBPacket *p) -{ - USBHIDState *us = USB_HID(dev); - HIDState *hs = &us->hid; - uint8_t buf[p->iov.size]; - int len = 0; - - switch (p->pid) { - case USB_TOKEN_IN: - if (p->ep->nr == 1) { - if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { - hid_pointer_activate(hs); - } - if (!hid_has_events(hs)) { - p->status = USB_RET_NAK; - return; - } - hid_set_next_idle(hs); - if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { - len = hid_pointer_poll(hs, buf, p->iov.size); - } else if (hs->kind == HID_KEYBOARD) { - len = hid_keyboard_poll(hs, buf, p->iov.size); - } - usb_packet_copy(p, buf, len); - } else { - goto fail; - } - break; - case USB_TOKEN_OUT: - default: - fail: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_hid_handle_destroy(USBDevice *dev) -{ - USBHIDState *us = USB_HID(dev); - - hid_free(&us->hid); -} - -static void usb_hid_initfn(USBDevice *dev, int kind, - const USBDesc *usb1, const USBDesc *usb2, - Error **errp) -{ - USBHIDState *us = USB_HID(dev); - switch (us->usb_version) { - case 1: - dev->usb_desc = usb1; - break; - case 2: - dev->usb_desc = usb2; - break; - default: - dev->usb_desc = NULL; - } - if (!dev->usb_desc) { - error_setg(errp, "Invalid usb version %d for usb hid device", - us->usb_version); - return; - } - - if (dev->serial) { - usb_desc_set_string(dev, STR_SERIALNUMBER, dev->serial); - } - usb_desc_init(dev); - us->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); - hid_init(&us->hid, kind, usb_hid_changed); - if (us->display && us->hid.s) { - qemu_input_handler_bind(us->hid.s, us->display, us->head, NULL); - } -} - -static void usb_tablet_realize(USBDevice *dev, Error **errp) -{ - - usb_hid_initfn(dev, HID_TABLET, &desc_tablet, &desc_tablet2, errp); -} - -static void usb_mouse_realize(USBDevice *dev, Error **errp) -{ - usb_hid_initfn(dev, HID_MOUSE, &desc_mouse, &desc_mouse2, errp); -} - -static void usb_keyboard_realize(USBDevice *dev, Error **errp) -{ - usb_hid_initfn(dev, HID_KEYBOARD, &desc_keyboard, &desc_keyboard2, errp); -} - -static int usb_ptr_post_load(void *opaque, int version_id) -{ - USBHIDState *s = opaque; - - if (s->dev.remote_wakeup) { - hid_pointer_activate(&s->hid); - } - return 0; -} - -static const VMStateDescription vmstate_usb_ptr = { - .name = "usb-ptr", - .version_id = 1, - .minimum_version_id = 1, - .post_load = usb_ptr_post_load, - .fields = (VMStateField[]) { - VMSTATE_USB_DEVICE(dev, USBHIDState), - VMSTATE_HID_POINTER_DEVICE(hid, USBHIDState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_usb_kbd = { - .name = "usb-kbd", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_USB_DEVICE(dev, USBHIDState), - VMSTATE_HID_KEYBOARD_DEVICE(hid, USBHIDState), - VMSTATE_END_OF_LIST() - } -}; - -static void usb_hid_class_initfn(ObjectClass *klass, void *data) -{ - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->handle_reset = usb_hid_handle_reset; - uc->handle_control = usb_hid_handle_control; - uc->handle_data = usb_hid_handle_data; - uc->handle_destroy = usb_hid_handle_destroy; - uc->handle_attach = usb_desc_attach; -} - -static const TypeInfo usb_hid_type_info = { - .name = TYPE_USB_HID, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBHIDState), - .abstract = true, - .class_init = usb_hid_class_initfn, -}; - -static Property usb_tablet_properties[] = { - DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), - DEFINE_PROP_STRING("display", USBHIDState, display), - DEFINE_PROP_UINT32("head", USBHIDState, head, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_tablet_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_tablet_realize; - uc->product_desc = "QEMU USB Tablet"; - dc->vmsd = &vmstate_usb_ptr; - dc->props = usb_tablet_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo usb_tablet_info = { - .name = "usb-tablet", - .parent = TYPE_USB_HID, - .class_init = usb_tablet_class_initfn, -}; - -static Property usb_mouse_properties[] = { - DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_mouse_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_mouse_realize; - uc->product_desc = "QEMU USB Mouse"; - dc->vmsd = &vmstate_usb_ptr; - dc->props = usb_mouse_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo usb_mouse_info = { - .name = "usb-mouse", - .parent = TYPE_USB_HID, - .class_init = usb_mouse_class_initfn, -}; - -static Property usb_keyboard_properties[] = { - DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), - DEFINE_PROP_STRING("display", USBHIDState, display), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_keyboard_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_keyboard_realize; - uc->product_desc = "QEMU USB Keyboard"; - dc->vmsd = &vmstate_usb_kbd; - dc->props = usb_keyboard_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo usb_keyboard_info = { - .name = "usb-kbd", - .parent = TYPE_USB_HID, - .class_init = usb_keyboard_class_initfn, -}; - -static void usb_hid_register_types(void) -{ - type_register_static(&usb_hid_type_info); - type_register_static(&usb_tablet_info); - usb_legacy_register("usb-tablet", "tablet", NULL); - type_register_static(&usb_mouse_info); - usb_legacy_register("usb-mouse", "mouse", NULL); - type_register_static(&usb_keyboard_info); - usb_legacy_register("usb-kbd", "keyboard", NULL); -} - -type_init(usb_hid_register_types) diff --git a/qemu/hw/usb/dev-hub.c b/qemu/hw/usb/dev-hub.c deleted file mode 100644 index a33f21cb3..000000000 --- a/qemu/hw/usb/dev-hub.c +++ /dev/null @@ -1,596 +0,0 @@ -/* - * QEMU USB HUB emulation - * - * Copyright (c) 2005 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "trace.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" -#include "qemu/error-report.h" - -#define NUM_PORTS 8 - -typedef struct USBHubPort { - USBPort port; - uint16_t wPortStatus; - uint16_t wPortChange; -} USBHubPort; - -typedef struct USBHubState { - USBDevice dev; - USBEndpoint *intr; - USBHubPort ports[NUM_PORTS]; -} USBHubState; - -#define TYPE_USB_HUB "usb-hub" -#define USB_HUB(obj) OBJECT_CHECK(USBHubState, (obj), TYPE_USB_HUB) - -#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE) -#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE) -#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR) -#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS) -#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS) -#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE) -#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE) - -#define PORT_STAT_CONNECTION 0x0001 -#define PORT_STAT_ENABLE 0x0002 -#define PORT_STAT_SUSPEND 0x0004 -#define PORT_STAT_OVERCURRENT 0x0008 -#define PORT_STAT_RESET 0x0010 -#define PORT_STAT_POWER 0x0100 -#define PORT_STAT_LOW_SPEED 0x0200 -#define PORT_STAT_HIGH_SPEED 0x0400 -#define PORT_STAT_TEST 0x0800 -#define PORT_STAT_INDICATOR 0x1000 - -#define PORT_STAT_C_CONNECTION 0x0001 -#define PORT_STAT_C_ENABLE 0x0002 -#define PORT_STAT_C_SUSPEND 0x0004 -#define PORT_STAT_C_OVERCURRENT 0x0008 -#define PORT_STAT_C_RESET 0x0010 - -#define PORT_CONNECTION 0 -#define PORT_ENABLE 1 -#define PORT_SUSPEND 2 -#define PORT_OVERCURRENT 3 -#define PORT_RESET 4 -#define PORT_POWER 8 -#define PORT_LOWSPEED 9 -#define PORT_HIGHSPEED 10 -#define PORT_C_CONNECTION 16 -#define PORT_C_ENABLE 17 -#define PORT_C_SUSPEND 18 -#define PORT_C_OVERCURRENT 19 -#define PORT_C_RESET 20 -#define PORT_TEST 21 -#define PORT_INDICATOR 22 - -/* same as Linux kernel root hubs */ - -enum { - STR_MANUFACTURER = 1, - STR_PRODUCT, - STR_SERIALNUMBER, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = "QEMU", - [STR_PRODUCT] = "QEMU USB Hub", - [STR_SERIALNUMBER] = "314159", -}; - -static const USBDescIface desc_iface_hub = { - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_HUB, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 1 + (NUM_PORTS + 7) / 8, - .bInterval = 0xff, - }, - } -}; - -static const USBDescDevice desc_device_hub = { - .bcdUSB = 0x0110, - .bDeviceClass = USB_CLASS_HUB, - .bMaxPacketSize0 = 8, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER | - USB_CFG_ATT_WAKEUP, - .nif = 1, - .ifs = &desc_iface_hub, - }, - }, -}; - -static const USBDesc desc_hub = { - .id = { - .idVendor = 0x0409, - .idProduct = 0x55aa, - .bcdDevice = 0x0101, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_hub, - .str = desc_strings, -}; - -static const uint8_t qemu_hub_hub_descriptor[] = -{ - 0x00, /* u8 bLength; patched in later */ - 0x29, /* u8 bDescriptorType; Hub-descriptor */ - 0x00, /* u8 bNbrPorts; (patched later) */ - 0x0a, /* u16 wHubCharacteristics; */ - 0x00, /* (per-port OC, no power switching) */ - 0x01, /* u8 bPwrOn2pwrGood; 2ms */ - 0x00 /* u8 bHubContrCurrent; 0 mA */ - - /* DeviceRemovable and PortPwrCtrlMask patched in later */ -}; - -static void usb_hub_attach(USBPort *port1) -{ - USBHubState *s = port1->opaque; - USBHubPort *port = &s->ports[port1->index]; - - trace_usb_hub_attach(s->dev.addr, port1->index + 1); - port->wPortStatus |= PORT_STAT_CONNECTION; - port->wPortChange |= PORT_STAT_C_CONNECTION; - if (port->port.dev->speed == USB_SPEED_LOW) { - port->wPortStatus |= PORT_STAT_LOW_SPEED; - } else { - port->wPortStatus &= ~PORT_STAT_LOW_SPEED; - } - usb_wakeup(s->intr, 0); -} - -static void usb_hub_detach(USBPort *port1) -{ - USBHubState *s = port1->opaque; - USBHubPort *port = &s->ports[port1->index]; - - trace_usb_hub_detach(s->dev.addr, port1->index + 1); - usb_wakeup(s->intr, 0); - - /* Let upstream know the device on this port is gone */ - s->dev.port->ops->child_detach(s->dev.port, port1->dev); - - port->wPortStatus &= ~PORT_STAT_CONNECTION; - port->wPortChange |= PORT_STAT_C_CONNECTION; - if (port->wPortStatus & PORT_STAT_ENABLE) { - port->wPortStatus &= ~PORT_STAT_ENABLE; - port->wPortChange |= PORT_STAT_C_ENABLE; - } - usb_wakeup(s->intr, 0); -} - -static void usb_hub_child_detach(USBPort *port1, USBDevice *child) -{ - USBHubState *s = port1->opaque; - - /* Pass along upstream */ - s->dev.port->ops->child_detach(s->dev.port, child); -} - -static void usb_hub_wakeup(USBPort *port1) -{ - USBHubState *s = port1->opaque; - USBHubPort *port = &s->ports[port1->index]; - - if (port->wPortStatus & PORT_STAT_SUSPEND) { - port->wPortChange |= PORT_STAT_C_SUSPEND; - usb_wakeup(s->intr, 0); - } -} - -static void usb_hub_complete(USBPort *port, USBPacket *packet) -{ - USBHubState *s = port->opaque; - - /* - * Just pass it along upstream for now. - * - * If we ever implement usb 2.0 split transactions this will - * become a little more complicated ... - * - * Can't use usb_packet_complete() here because packet->owner is - * cleared already, go call the ->complete() callback directly - * instead. - */ - s->dev.port->ops->complete(s->dev.port, packet); -} - -static USBDevice *usb_hub_find_device(USBDevice *dev, uint8_t addr) -{ - USBHubState *s = USB_HUB(dev); - USBHubPort *port; - USBDevice *downstream; - int i; - - for (i = 0; i < NUM_PORTS; i++) { - port = &s->ports[i]; - if (!(port->wPortStatus & PORT_STAT_ENABLE)) { - continue; - } - downstream = usb_find_device(&port->port, addr); - if (downstream != NULL) { - return downstream; - } - } - return NULL; -} - -static void usb_hub_handle_reset(USBDevice *dev) -{ - USBHubState *s = USB_HUB(dev); - USBHubPort *port; - int i; - - trace_usb_hub_reset(s->dev.addr); - for (i = 0; i < NUM_PORTS; i++) { - port = s->ports + i; - port->wPortStatus = PORT_STAT_POWER; - port->wPortChange = 0; - if (port->port.dev && port->port.dev->attached) { - port->wPortStatus |= PORT_STAT_CONNECTION; - port->wPortChange |= PORT_STAT_C_CONNECTION; - if (port->port.dev->speed == USB_SPEED_LOW) { - port->wPortStatus |= PORT_STAT_LOW_SPEED; - } - } - } -} - -static const char *feature_name(int feature) -{ - static const char *name[] = { - [PORT_CONNECTION] = "connection", - [PORT_ENABLE] = "enable", - [PORT_SUSPEND] = "suspend", - [PORT_OVERCURRENT] = "overcurrent", - [PORT_RESET] = "reset", - [PORT_POWER] = "power", - [PORT_LOWSPEED] = "lowspeed", - [PORT_HIGHSPEED] = "highspeed", - [PORT_C_CONNECTION] = "change connection", - [PORT_C_ENABLE] = "change enable", - [PORT_C_SUSPEND] = "change suspend", - [PORT_C_OVERCURRENT] = "change overcurrent", - [PORT_C_RESET] = "change reset", - [PORT_TEST] = "test", - [PORT_INDICATOR] = "indicator", - }; - if (feature < 0 || feature >= ARRAY_SIZE(name)) { - return "?"; - } - return name[feature] ?: "?"; -} - -static void usb_hub_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) -{ - USBHubState *s = (USBHubState *)dev; - int ret; - - trace_usb_hub_control(s->dev.addr, request, value, index, length); - - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - switch(request) { - case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == 0 && index != 0x81) { /* clear ep halt */ - goto fail; - } - break; - /* usb specific requests */ - case GetHubStatus: - data[0] = 0; - data[1] = 0; - data[2] = 0; - data[3] = 0; - p->actual_length = 4; - break; - case GetPortStatus: - { - unsigned int n = index - 1; - USBHubPort *port; - if (n >= NUM_PORTS) { - goto fail; - } - port = &s->ports[n]; - trace_usb_hub_get_port_status(s->dev.addr, index, - port->wPortStatus, - port->wPortChange); - data[0] = port->wPortStatus; - data[1] = port->wPortStatus >> 8; - data[2] = port->wPortChange; - data[3] = port->wPortChange >> 8; - p->actual_length = 4; - } - break; - case SetHubFeature: - case ClearHubFeature: - if (value != 0 && value != 1) { - goto fail; - } - break; - case SetPortFeature: - { - unsigned int n = index - 1; - USBHubPort *port; - USBDevice *dev; - - trace_usb_hub_set_port_feature(s->dev.addr, index, - feature_name(value)); - - if (n >= NUM_PORTS) { - goto fail; - } - port = &s->ports[n]; - dev = port->port.dev; - switch(value) { - case PORT_SUSPEND: - port->wPortStatus |= PORT_STAT_SUSPEND; - break; - case PORT_RESET: - if (dev && dev->attached) { - usb_device_reset(dev); - port->wPortChange |= PORT_STAT_C_RESET; - /* set enable bit */ - port->wPortStatus |= PORT_STAT_ENABLE; - usb_wakeup(s->intr, 0); - } - break; - case PORT_POWER: - break; - default: - goto fail; - } - } - break; - case ClearPortFeature: - { - unsigned int n = index - 1; - USBHubPort *port; - - trace_usb_hub_clear_port_feature(s->dev.addr, index, - feature_name(value)); - - if (n >= NUM_PORTS) { - goto fail; - } - port = &s->ports[n]; - switch(value) { - case PORT_ENABLE: - port->wPortStatus &= ~PORT_STAT_ENABLE; - break; - case PORT_C_ENABLE: - port->wPortChange &= ~PORT_STAT_C_ENABLE; - break; - case PORT_SUSPEND: - port->wPortStatus &= ~PORT_STAT_SUSPEND; - break; - case PORT_C_SUSPEND: - port->wPortChange &= ~PORT_STAT_C_SUSPEND; - break; - case PORT_C_CONNECTION: - port->wPortChange &= ~PORT_STAT_C_CONNECTION; - break; - case PORT_C_OVERCURRENT: - port->wPortChange &= ~PORT_STAT_C_OVERCURRENT; - break; - case PORT_C_RESET: - port->wPortChange &= ~PORT_STAT_C_RESET; - break; - default: - goto fail; - } - } - break; - case GetHubDescriptor: - { - unsigned int n, limit, var_hub_size = 0; - memcpy(data, qemu_hub_hub_descriptor, - sizeof(qemu_hub_hub_descriptor)); - data[2] = NUM_PORTS; - - /* fill DeviceRemovable bits */ - limit = ((NUM_PORTS + 1 + 7) / 8) + 7; - for (n = 7; n < limit; n++) { - data[n] = 0x00; - var_hub_size++; - } - - /* fill PortPwrCtrlMask bits */ - limit = limit + ((NUM_PORTS + 7) / 8); - for (;n < limit; n++) { - data[n] = 0xff; - var_hub_size++; - } - - p->actual_length = sizeof(qemu_hub_hub_descriptor) + var_hub_size; - data[0] = p->actual_length; - break; - } - default: - fail: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_hub_handle_data(USBDevice *dev, USBPacket *p) -{ - USBHubState *s = (USBHubState *)dev; - - switch(p->pid) { - case USB_TOKEN_IN: - if (p->ep->nr == 1) { - USBHubPort *port; - unsigned int status; - uint8_t buf[4]; - int i, n; - n = (NUM_PORTS + 1 + 7) / 8; - if (p->iov.size == 1) { /* FreeBSD workaround */ - n = 1; - } else if (n > p->iov.size) { - p->status = USB_RET_BABBLE; - return; - } - status = 0; - for(i = 0; i < NUM_PORTS; i++) { - port = &s->ports[i]; - if (port->wPortChange) - status |= (1 << (i + 1)); - } - if (status != 0) { - trace_usb_hub_status_report(s->dev.addr, status); - for(i = 0; i < n; i++) { - buf[i] = status >> (8 * i); - } - usb_packet_copy(p, buf, n); - } else { - p->status = USB_RET_NAK; /* usb11 11.13.1 */ - } - } else { - goto fail; - } - break; - case USB_TOKEN_OUT: - default: - fail: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_hub_handle_destroy(USBDevice *dev) -{ - USBHubState *s = (USBHubState *)dev; - int i; - - for (i = 0; i < NUM_PORTS; i++) { - usb_unregister_port(usb_bus_from_device(dev), - &s->ports[i].port); - } -} - -static USBPortOps usb_hub_port_ops = { - .attach = usb_hub_attach, - .detach = usb_hub_detach, - .child_detach = usb_hub_child_detach, - .wakeup = usb_hub_wakeup, - .complete = usb_hub_complete, -}; - -static void usb_hub_realize(USBDevice *dev, Error **errp) -{ - USBHubState *s = USB_HUB(dev); - USBHubPort *port; - int i; - - if (dev->port->hubcount == 5) { - error_setg(errp, "usb hub chain too deep"); - return; - } - - usb_desc_create_serial(dev); - usb_desc_init(dev); - s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); - for (i = 0; i < NUM_PORTS; i++) { - port = &s->ports[i]; - usb_register_port(usb_bus_from_device(dev), - &port->port, s, i, &usb_hub_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - usb_port_location(&port->port, dev->port, i+1); - } - usb_hub_handle_reset(dev); -} - -static const VMStateDescription vmstate_usb_hub_port = { - .name = "usb-hub-port", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT16(wPortStatus, USBHubPort), - VMSTATE_UINT16(wPortChange, USBHubPort), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_usb_hub = { - .name = "usb-hub", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_USB_DEVICE(dev, USBHubState), - VMSTATE_STRUCT_ARRAY(ports, USBHubState, NUM_PORTS, 0, - vmstate_usb_hub_port, USBHubPort), - VMSTATE_END_OF_LIST() - } -}; - -static void usb_hub_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_hub_realize; - uc->product_desc = "QEMU USB Hub"; - uc->usb_desc = &desc_hub; - uc->find_device = usb_hub_find_device; - uc->handle_reset = usb_hub_handle_reset; - uc->handle_control = usb_hub_handle_control; - uc->handle_data = usb_hub_handle_data; - uc->handle_destroy = usb_hub_handle_destroy; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->fw_name = "hub"; - dc->vmsd = &vmstate_usb_hub; -} - -static const TypeInfo hub_info = { - .name = TYPE_USB_HUB, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBHubState), - .class_init = usb_hub_class_initfn, -}; - -static void usb_hub_register_types(void) -{ - type_register_static(&hub_info); -} - -type_init(usb_hub_register_types) diff --git a/qemu/hw/usb/dev-mtp.c b/qemu/hw/usb/dev-mtp.c deleted file mode 100644 index bda84a64b..000000000 --- a/qemu/hw/usb/dev-mtp.c +++ /dev/null @@ -1,1414 +0,0 @@ -/* - * Media Transfer Protocol implementation, backed by host filesystem. - * - * Copyright Red Hat, Inc 2014 - * - * Author: - * Gerd Hoffmann - * - * This code is licensed under the GPL v2 or later. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include -#include - -#include -#ifdef CONFIG_INOTIFY1 -#include -#include "qapi/error.h" -#include "qemu/main-loop.h" -#endif - -#include "qemu-common.h" -#include "qemu/iov.h" -#include "trace.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" - -/* ----------------------------------------------------------------------- */ - -enum mtp_container_type { - TYPE_COMMAND = 1, - TYPE_DATA = 2, - TYPE_RESPONSE = 3, - TYPE_EVENT = 4, -}; - -enum mtp_code { - /* command codes */ - CMD_GET_DEVICE_INFO = 0x1001, - CMD_OPEN_SESSION = 0x1002, - CMD_CLOSE_SESSION = 0x1003, - CMD_GET_STORAGE_IDS = 0x1004, - CMD_GET_STORAGE_INFO = 0x1005, - CMD_GET_NUM_OBJECTS = 0x1006, - CMD_GET_OBJECT_HANDLES = 0x1007, - CMD_GET_OBJECT_INFO = 0x1008, - CMD_GET_OBJECT = 0x1009, - CMD_GET_PARTIAL_OBJECT = 0x101b, - - /* response codes */ - RES_OK = 0x2001, - RES_GENERAL_ERROR = 0x2002, - RES_SESSION_NOT_OPEN = 0x2003, - RES_INVALID_TRANSACTION_ID = 0x2004, - RES_OPERATION_NOT_SUPPORTED = 0x2005, - RES_PARAMETER_NOT_SUPPORTED = 0x2006, - RES_INCOMPLETE_TRANSFER = 0x2007, - RES_INVALID_STORAGE_ID = 0x2008, - RES_INVALID_OBJECT_HANDLE = 0x2009, - RES_SPEC_BY_FORMAT_UNSUPPORTED = 0x2014, - RES_INVALID_PARENT_OBJECT = 0x201a, - RES_INVALID_PARAMETER = 0x201d, - RES_SESSION_ALREADY_OPEN = 0x201e, - - /* 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 { - uint32_t length; - uint16_t type; - uint16_t code; - uint32_t trans; -} QEMU_PACKED mtp_container; - -/* ----------------------------------------------------------------------- */ - -typedef struct MTPState MTPState; -typedef struct MTPControl MTPControl; -typedef struct MTPData MTPData; -typedef struct MTPObject MTPObject; - -enum { - EP_DATA_IN = 1, - EP_DATA_OUT, - 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; - int argc; - uint32_t argv[5]; -}; - -struct MTPData { - uint16_t code; - uint32_t trans; - uint32_t offset; - uint32_t length; - uint32_t alloc; - uint8_t *data; - bool first; - int fd; -}; - -struct MTPObject { - uint32_t handle; - uint16_t format; - char *name; - char *path; - struct stat stat; -#ifdef CONFIG_INOTIFY1 - /* inotify watch cookie */ - int watchfd; -#endif - MTPObject *parent; - uint32_t nchildren; - QLIST_HEAD(, MTPObject) children; - QLIST_ENTRY(MTPObject) list; - bool have_children; - QTAILQ_ENTRY(MTPObject) next; -}; - -struct MTPState { - USBDevice dev; - char *root; - char *desc; - uint32_t flags; - - MTPData *data_in; - MTPData *data_out; - MTPControl *result; - uint32_t session; - 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" -#define USB_MTP(obj) OBJECT_CHECK(MTPState, (obj), TYPE_USB_MTP) - -#define QEMU_STORAGE_ID 0x00010001 - -#define MTP_FLAG_WRITABLE 0 - -#define FLAG_SET(_mtp, _flag) ((_mtp)->flags & (1 << (_flag))) - -/* ----------------------------------------------------------------------- */ - -#define MTP_MANUFACTURER "QEMU" -#define MTP_PRODUCT "QEMU filesharing" - -enum { - STR_MANUFACTURER = 1, - STR_PRODUCT, - STR_SERIALNUMBER, - STR_MTP, - STR_CONFIG_FULL, - STR_CONFIG_HIGH, - STR_CONFIG_SUPER, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = MTP_MANUFACTURER, - [STR_PRODUCT] = MTP_PRODUCT, - [STR_SERIALNUMBER] = "34617", - [STR_MTP] = "MTP", - [STR_CONFIG_FULL] = "Full speed config (usb 1.1)", - [STR_CONFIG_HIGH] = "High speed config (usb 2.0)", - [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)", -}; - -static const USBDescIface desc_iface_full = { - .bInterfaceNumber = 0, - .bNumEndpoints = 3, - .bInterfaceClass = USB_CLASS_STILL_IMAGE, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x01, - .iInterface = STR_MTP, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | EP_DATA_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 64, - },{ - .bEndpointAddress = USB_DIR_OUT | EP_DATA_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 64, - },{ - .bEndpointAddress = USB_DIR_IN | EP_EVENT, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 64, - .bInterval = 0x0a, - }, - } -}; - -static const USBDescDevice desc_device_full = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 8, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_FULL, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, - .bMaxPower = 2, - .nif = 1, - .ifs = &desc_iface_full, - }, - }, -}; - -static const USBDescIface desc_iface_high = { - .bInterfaceNumber = 0, - .bNumEndpoints = 3, - .bInterfaceClass = USB_CLASS_STILL_IMAGE, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x01, - .iInterface = STR_MTP, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | EP_DATA_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 512, - },{ - .bEndpointAddress = USB_DIR_OUT | EP_DATA_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 512, - },{ - .bEndpointAddress = USB_DIR_IN | EP_EVENT, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 64, - .bInterval = 0x0a, - }, - } -}; - -static const USBDescDevice desc_device_high = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_HIGH, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, - .bMaxPower = 2, - .nif = 1, - .ifs = &desc_iface_high, - }, - }, -}; - -static const USBDescMSOS desc_msos = { - .CompatibleID = "MTP", - .SelectiveSuspendEnabled = true, -}; - -static const USBDesc desc = { - .id = { - .idVendor = 0x46f4, /* CRC16() of "QEMU" */ - .idProduct = 0x0004, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_full, - .high = &desc_device_high, - .str = desc_strings, - .msos = &desc_msos, -}; - -/* ----------------------------------------------------------------------- */ - -static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle, - MTPObject *parent, char *name) -{ - MTPObject *o = g_new0(MTPObject, 1); - - if (name[0] == '.') { - goto ignore; - } - - o->handle = handle; - o->parent = parent; - o->name = g_strdup(name); - if (parent == NULL) { - o->path = g_strdup(name); - } else { - o->path = g_strdup_printf("%s/%s", parent->path, name); - } - - if (lstat(o->path, &o->stat) != 0) { - goto ignore; - } - if (S_ISREG(o->stat.st_mode)) { - o->format = FMT_UNDEFINED_OBJECT; - } else if (S_ISDIR(o->stat.st_mode)) { - o->format = FMT_ASSOCIATION; - } else { - goto ignore; - } - - if (access(o->path, R_OK) != 0) { - goto ignore; - } - - trace_usb_mtp_object_alloc(s->dev.addr, o->handle, o->path); - - QTAILQ_INSERT_TAIL(&s->objects, o, next); - return o; - -ignore: - g_free(o->name); - g_free(o->path); - g_free(o); - return NULL; -} - -static void usb_mtp_object_free(MTPState *s, MTPObject *o) -{ - MTPObject *iter; - - if (!o) { - return; - } - - trace_usb_mtp_object_free(s->dev.addr, o->handle, o->path); - - QTAILQ_REMOVE(&s->objects, o, next); - 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->name); - g_free(o->path); - g_free(o); -} - -static MTPObject *usb_mtp_object_lookup(MTPState *s, uint32_t handle) -{ - MTPObject *o; - - QTAILQ_FOREACH(o, &s->objects, next) { - if (o->handle == handle) { - return o; - } - } - 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; - DIR *dir; - - if (o->have_children) { - return; - } - o->have_children = true; - - dir = opendir(o->path); - 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) { - usb_mtp_add_child(s, o, entry->d_name); - } - closedir(dir); -} - -/* ----------------------------------------------------------------------- */ - -static MTPData *usb_mtp_data_alloc(MTPControl *c) -{ - MTPData *data = g_new0(MTPData, 1); - - data->code = c->code; - data->trans = c->trans; - data->fd = -1; - data->first = true; - return data; -} - -static void usb_mtp_data_free(MTPData *data) -{ - if (data == NULL) { - return; - } - if (data->fd != -1) { - close(data->fd); - } - g_free(data->data); - g_free(data); -} - -static void usb_mtp_realloc(MTPData *data, uint32_t bytes) -{ - if (data->length + bytes <= data->alloc) { - return; - } - data->alloc = (data->length + bytes + 0xff) & ~0xff; - data->data = g_realloc(data->data, data->alloc); -} - -static void usb_mtp_add_u8(MTPData *data, uint8_t val) -{ - usb_mtp_realloc(data, 1); - data->data[data->length++] = val; -} - -static void usb_mtp_add_u16(MTPData *data, uint16_t val) -{ - usb_mtp_realloc(data, 2); - data->data[data->length++] = (val >> 0) & 0xff; - data->data[data->length++] = (val >> 8) & 0xff; -} - -static void usb_mtp_add_u32(MTPData *data, uint32_t val) -{ - usb_mtp_realloc(data, 4); - data->data[data->length++] = (val >> 0) & 0xff; - data->data[data->length++] = (val >> 8) & 0xff; - data->data[data->length++] = (val >> 16) & 0xff; - data->data[data->length++] = (val >> 24) & 0xff; -} - -static void usb_mtp_add_u64(MTPData *data, uint64_t val) -{ - usb_mtp_realloc(data, 8); - data->data[data->length++] = (val >> 0) & 0xff; - data->data[data->length++] = (val >> 8) & 0xff; - data->data[data->length++] = (val >> 16) & 0xff; - data->data[data->length++] = (val >> 24) & 0xff; - data->data[data->length++] = (val >> 32) & 0xff; - data->data[data->length++] = (val >> 40) & 0xff; - data->data[data->length++] = (val >> 48) & 0xff; - data->data[data->length++] = (val >> 56) & 0xff; -} - -static void usb_mtp_add_u16_array(MTPData *data, uint32_t len, - const uint16_t *vals) -{ - int i; - - usb_mtp_add_u32(data, len); - for (i = 0; i < len; i++) { - usb_mtp_add_u16(data, vals[i]); - } -} - -static void usb_mtp_add_u32_array(MTPData *data, uint32_t len, - const uint32_t *vals) -{ - int i; - - usb_mtp_add_u32(data, len); - for (i = 0; i < len; i++) { - usb_mtp_add_u32(data, vals[i]); - } -} - -static void usb_mtp_add_wstr(MTPData *data, const wchar_t *str) -{ - uint32_t len = wcslen(str); - int i; - - if (len > 0) { - len++; /* include terminating L'\0' */ - } - - usb_mtp_add_u8(data, len); - for (i = 0; i < len; i++) { - usb_mtp_add_u16(data, str[i]); - } -} - -static void usb_mtp_add_str(MTPData *data, const char *str) -{ - uint32_t len = strlen(str)+1; - wchar_t *wstr = g_new(wchar_t, len); - size_t ret; - - ret = mbstowcs(wstr, str, len); - if (ret == -1) { - usb_mtp_add_wstr(data, L"Oops"); - } else { - usb_mtp_add_wstr(data, wstr); - } - - g_free(wstr); -} - -static void usb_mtp_add_time(MTPData *data, time_t time) -{ - char buf[16]; - struct tm tm; - - gmtime_r(&time, &tm); - strftime(buf, sizeof(buf), "%Y%m%dT%H%M%S", &tm); - usb_mtp_add_str(data, buf); -} - -/* ----------------------------------------------------------------------- */ - -static void usb_mtp_queue_result(MTPState *s, uint16_t code, uint32_t trans, - int argc, uint32_t arg0, uint32_t arg1) -{ - MTPControl *c = g_new0(MTPControl, 1); - - c->code = code; - c->trans = trans; - c->argc = argc; - if (argc > 0) { - c->argv[0] = arg0; - } - if (argc > 1) { - c->argv[1] = arg1; - } - - assert(s->result == NULL); - s->result = c; -} - -/* ----------------------------------------------------------------------- */ - -static MTPData *usb_mtp_get_device_info(MTPState *s, MTPControl *c) -{ - static const uint16_t ops[] = { - CMD_GET_DEVICE_INFO, - CMD_OPEN_SESSION, - CMD_CLOSE_SESSION, - CMD_GET_STORAGE_IDS, - CMD_GET_STORAGE_INFO, - CMD_GET_NUM_OBJECTS, - CMD_GET_OBJECT_HANDLES, - CMD_GET_OBJECT_INFO, - CMD_GET_OBJECT, - CMD_GET_PARTIAL_OBJECT, - }; - static const uint16_t fmt[] = { - FMT_UNDEFINED_OBJECT, - FMT_ASSOCIATION, - }; - MTPData *d = usb_mtp_data_alloc(c); - - trace_usb_mtp_op_get_device_info(s->dev.addr); - - usb_mtp_add_u16(d, 100); - usb_mtp_add_u32(d, 0xffffffff); - usb_mtp_add_u16(d, 0x0101); - usb_mtp_add_wstr(d, L""); - usb_mtp_add_u16(d, 0x0000); - - usb_mtp_add_u16_array(d, ARRAY_SIZE(ops), ops); - usb_mtp_add_u16_array(d, 0, NULL); - usb_mtp_add_u16_array(d, 0, NULL); - usb_mtp_add_u16_array(d, 0, NULL); - usb_mtp_add_u16_array(d, ARRAY_SIZE(fmt), fmt); - - usb_mtp_add_wstr(d, L"" MTP_MANUFACTURER); - usb_mtp_add_wstr(d, L"" MTP_PRODUCT); - usb_mtp_add_wstr(d, L"0.1"); - usb_mtp_add_wstr(d, L"0123456789abcdef0123456789abcdef"); - - return d; -} - -static MTPData *usb_mtp_get_storage_ids(MTPState *s, MTPControl *c) -{ - static const uint32_t ids[] = { - QEMU_STORAGE_ID, - }; - MTPData *d = usb_mtp_data_alloc(c); - - trace_usb_mtp_op_get_storage_ids(s->dev.addr); - - usb_mtp_add_u32_array(d, ARRAY_SIZE(ids), ids); - - return d; -} - -static MTPData *usb_mtp_get_storage_info(MTPState *s, MTPControl *c) -{ - MTPData *d = usb_mtp_data_alloc(c); - struct statvfs buf; - int rc; - - trace_usb_mtp_op_get_storage_info(s->dev.addr); - - if (FLAG_SET(s, MTP_FLAG_WRITABLE)) { - usb_mtp_add_u16(d, 0x0003); - usb_mtp_add_u16(d, 0x0002); - usb_mtp_add_u16(d, 0x0000); - } else { - usb_mtp_add_u16(d, 0x0001); - usb_mtp_add_u16(d, 0x0002); - usb_mtp_add_u16(d, 0x0001); - } - - rc = statvfs(s->root, &buf); - if (rc == 0) { - usb_mtp_add_u64(d, (uint64_t)buf.f_frsize * buf.f_blocks); - usb_mtp_add_u64(d, (uint64_t)buf.f_bavail * buf.f_blocks); - usb_mtp_add_u32(d, buf.f_ffree); - } else { - usb_mtp_add_u64(d, 0xffffffff); - usb_mtp_add_u64(d, 0xffffffff); - usb_mtp_add_u32(d, 0xffffffff); - } - - usb_mtp_add_str(d, s->desc); - usb_mtp_add_wstr(d, L"123456789abcdef"); - return d; -} - -static MTPData *usb_mtp_get_object_handles(MTPState *s, MTPControl *c, - MTPObject *o) -{ - MTPData *d = usb_mtp_data_alloc(c); - uint32_t i = 0, handles[o->nchildren]; - MTPObject *iter; - - trace_usb_mtp_op_get_object_handles(s->dev.addr, o->handle, o->path); - - 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; -} - -static MTPData *usb_mtp_get_object_info(MTPState *s, MTPControl *c, - MTPObject *o) -{ - MTPData *d = usb_mtp_data_alloc(c); - - trace_usb_mtp_op_get_object_info(s->dev.addr, o->handle, o->path); - - usb_mtp_add_u32(d, QEMU_STORAGE_ID); - usb_mtp_add_u16(d, o->format); - usb_mtp_add_u16(d, 0); - usb_mtp_add_u32(d, o->stat.st_size); - - usb_mtp_add_u16(d, 0); - usb_mtp_add_u32(d, 0); - usb_mtp_add_u32(d, 0); - usb_mtp_add_u32(d, 0); - usb_mtp_add_u32(d, 0); - usb_mtp_add_u32(d, 0); - usb_mtp_add_u32(d, 0); - - if (o->parent) { - usb_mtp_add_u32(d, o->parent->handle); - } else { - usb_mtp_add_u32(d, 0); - } - if (o->format == FMT_ASSOCIATION) { - usb_mtp_add_u16(d, 0x0001); - usb_mtp_add_u32(d, 0x00000001); - usb_mtp_add_u32(d, 0); - } else { - usb_mtp_add_u16(d, 0); - usb_mtp_add_u32(d, 0); - usb_mtp_add_u32(d, 0); - } - - usb_mtp_add_str(d, o->name); - usb_mtp_add_time(d, o->stat.st_ctime); - usb_mtp_add_time(d, o->stat.st_mtime); - usb_mtp_add_wstr(d, L""); - - return d; -} - -static MTPData *usb_mtp_get_object(MTPState *s, MTPControl *c, - MTPObject *o) -{ - MTPData *d = usb_mtp_data_alloc(c); - - trace_usb_mtp_op_get_object(s->dev.addr, o->handle, o->path); - - d->fd = open(o->path, O_RDONLY); - if (d->fd == -1) { - usb_mtp_data_free(d); - return NULL; - } - d->length = o->stat.st_size; - d->alloc = 512; - d->data = g_malloc(d->alloc); - return d; -} - -static MTPData *usb_mtp_get_partial_object(MTPState *s, MTPControl *c, - MTPObject *o) -{ - MTPData *d = usb_mtp_data_alloc(c); - off_t offset; - - trace_usb_mtp_op_get_partial_object(s->dev.addr, o->handle, o->path, - c->argv[1], c->argv[2]); - - d->fd = open(o->path, O_RDONLY); - if (d->fd == -1) { - usb_mtp_data_free(d); - return NULL; - } - - offset = c->argv[1]; - if (offset > o->stat.st_size) { - offset = o->stat.st_size; - } - if (lseek(d->fd, offset, SEEK_SET) < 0) { - usb_mtp_data_free(d); - return NULL; - } - - d->length = c->argv[2]; - if (d->length > o->stat.st_size - offset) { - d->length = o->stat.st_size - offset; - } - - return d; -} - -static void usb_mtp_command(MTPState *s, MTPControl *c) -{ - MTPData *data_in = NULL; - MTPObject *o; - uint32_t nres = 0, res0 = 0; - - /* sanity checks */ - if (c->code >= CMD_CLOSE_SESSION && s->session == 0) { - usb_mtp_queue_result(s, RES_SESSION_NOT_OPEN, - c->trans, 0, 0, 0); - return; - } - - /* process commands */ - switch (c->code) { - case CMD_GET_DEVICE_INFO: - data_in = usb_mtp_get_device_info(s, c); - break; - case CMD_OPEN_SESSION: - if (s->session) { - usb_mtp_queue_result(s, RES_SESSION_ALREADY_OPEN, - c->trans, 1, s->session, 0); - return; - } - if (c->argv[0] == 0) { - usb_mtp_queue_result(s, RES_INVALID_PARAMETER, - c->trans, 0, 0, 0); - return; - } - 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; - case CMD_GET_STORAGE_IDS: - data_in = usb_mtp_get_storage_ids(s, c); - break; - case CMD_GET_STORAGE_INFO: - if (c->argv[0] != QEMU_STORAGE_ID && - c->argv[0] != 0xffffffff) { - usb_mtp_queue_result(s, RES_INVALID_STORAGE_ID, - c->trans, 0, 0, 0); - return; - } - data_in = usb_mtp_get_storage_info(s, c); - break; - case CMD_GET_NUM_OBJECTS: - case CMD_GET_OBJECT_HANDLES: - if (c->argv[0] != QEMU_STORAGE_ID && - c->argv[0] != 0xffffffff) { - usb_mtp_queue_result(s, RES_INVALID_STORAGE_ID, - c->trans, 0, 0, 0); - return; - } - if (c->argv[1] != 0x00000000) { - usb_mtp_queue_result(s, RES_SPEC_BY_FORMAT_UNSUPPORTED, - c->trans, 0, 0, 0); - return; - } - if (c->argv[2] == 0x00000000 || - c->argv[2] == 0xffffffff) { - o = QTAILQ_FIRST(&s->objects); - } else { - o = usb_mtp_object_lookup(s, c->argv[2]); - } - if (o == NULL) { - usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); - return; - } - if (o->format != FMT_ASSOCIATION) { - usb_mtp_queue_result(s, RES_INVALID_PARENT_OBJECT, - c->trans, 0, 0, 0); - return; - } - usb_mtp_object_readdir(s, o); - if (c->code == CMD_GET_NUM_OBJECTS) { - trace_usb_mtp_op_get_num_objects(s->dev.addr, o->handle, o->path); - nres = 1; - res0 = o->nchildren; - } else { - data_in = usb_mtp_get_object_handles(s, c, o); - } - break; - case CMD_GET_OBJECT_INFO: - o = usb_mtp_object_lookup(s, c->argv[0]); - if (o == NULL) { - usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); - return; - } - data_in = usb_mtp_get_object_info(s, c, o); - break; - case CMD_GET_OBJECT: - o = usb_mtp_object_lookup(s, c->argv[0]); - if (o == NULL) { - usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); - return; - } - if (o->format == FMT_ASSOCIATION) { - usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); - return; - } - data_in = usb_mtp_get_object(s, c, o); - if (data_in == NULL) { - usb_mtp_queue_result(s, RES_GENERAL_ERROR, - c->trans, 0, 0, 0); - return; - } - break; - case CMD_GET_PARTIAL_OBJECT: - o = usb_mtp_object_lookup(s, c->argv[0]); - if (o == NULL) { - usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); - return; - } - if (o->format == FMT_ASSOCIATION) { - usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); - return; - } - data_in = usb_mtp_get_partial_object(s, c, o); - if (data_in == NULL) { - usb_mtp_queue_result(s, RES_GENERAL_ERROR, - c->trans, 0, 0, 0); - return; - } - nres = 1; - res0 = data_in->length; - break; - default: - trace_usb_mtp_op_unknown(s->dev.addr, c->code); - usb_mtp_queue_result(s, RES_OPERATION_NOT_SUPPORTED, - c->trans, 0, 0, 0); - return; - } - - /* return results on success */ - if (data_in) { - assert(s->data_in == NULL); - s->data_in = data_in; - } - usb_mtp_queue_result(s, RES_OK, c->trans, nres, res0, 0); -} - -/* ----------------------------------------------------------------------- */ - -static void usb_mtp_handle_reset(USBDevice *dev) -{ - MTPState *s = USB_MTP(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; - usb_mtp_data_free(s->data_out); - s->data_out = NULL; - g_free(s->result); - s->result = NULL; -} - -static void usb_mtp_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, - int length, uint8_t *data) -{ - int ret; - - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - trace_usb_mtp_stall(dev->addr, "unknown control request"); - p->status = USB_RET_STALL; -} - -static void usb_mtp_cancel_packet(USBDevice *dev, USBPacket *p) -{ - /* we don't use async packets, so this should never be called */ - fprintf(stderr, "%s\n", __func__); -} - -static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p) -{ - MTPState *s = USB_MTP(dev); - MTPControl cmd; - mtp_container container; - uint32_t params[5]; - int i, rc; - - switch (p->ep->nr) { - case EP_DATA_IN: - if (s->data_out != NULL) { - /* guest bug */ - trace_usb_mtp_stall(s->dev.addr, "awaiting data-out"); - p->status = USB_RET_STALL; - return; - } - if (p->iov.size < sizeof(container)) { - trace_usb_mtp_stall(s->dev.addr, "packet too small"); - p->status = USB_RET_STALL; - return; - } - if (s->data_in != NULL) { - MTPData *d = s->data_in; - int dlen = d->length - d->offset; - if (d->first) { - trace_usb_mtp_data_in(s->dev.addr, d->trans, d->length); - container.length = cpu_to_le32(d->length + sizeof(container)); - container.type = cpu_to_le16(TYPE_DATA); - container.code = cpu_to_le16(d->code); - container.trans = cpu_to_le32(d->trans); - usb_packet_copy(p, &container, sizeof(container)); - d->first = false; - if (dlen > p->iov.size - sizeof(container)) { - dlen = p->iov.size - sizeof(container); - } - } else { - if (dlen > p->iov.size) { - dlen = p->iov.size; - } - } - if (d->fd == -1) { - usb_packet_copy(p, d->data + d->offset, dlen); - } else { - if (d->alloc < p->iov.size) { - d->alloc = p->iov.size; - d->data = g_realloc(d->data, d->alloc); - } - rc = read(d->fd, d->data, dlen); - if (rc != dlen) { - memset(d->data, 0, dlen); - s->result->code = RES_INCOMPLETE_TRANSFER; - } - usb_packet_copy(p, d->data, dlen); - } - d->offset += dlen; - if (d->offset == d->length) { - usb_mtp_data_free(s->data_in); - s->data_in = NULL; - } - } else if (s->result != NULL) { - MTPControl *r = s->result; - int length = sizeof(container) + r->argc * sizeof(uint32_t); - if (r->code == RES_OK) { - trace_usb_mtp_success(s->dev.addr, r->trans, - (r->argc > 0) ? r->argv[0] : 0, - (r->argc > 1) ? r->argv[1] : 0); - } else { - trace_usb_mtp_error(s->dev.addr, r->code, r->trans, - (r->argc > 0) ? r->argv[0] : 0, - (r->argc > 1) ? r->argv[1] : 0); - } - container.length = cpu_to_le32(length); - container.type = cpu_to_le16(TYPE_RESPONSE); - container.code = cpu_to_le16(r->code); - container.trans = cpu_to_le32(r->trans); - for (i = 0; i < r->argc; i++) { - params[i] = cpu_to_le32(r->argv[i]); - } - usb_packet_copy(p, &container, sizeof(container)); - usb_packet_copy(p, ¶ms, length - sizeof(container)); - g_free(s->result); - s->result = NULL; - } - break; - case EP_DATA_OUT: - if (p->iov.size < sizeof(container)) { - trace_usb_mtp_stall(s->dev.addr, "packet too small"); - p->status = USB_RET_STALL; - return; - } - usb_packet_copy(p, &container, sizeof(container)); - switch (le16_to_cpu(container.type)) { - case TYPE_COMMAND: - if (s->data_in || s->data_out || s->result) { - trace_usb_mtp_stall(s->dev.addr, "transaction inflight"); - p->status = USB_RET_STALL; - return; - } - cmd.code = le16_to_cpu(container.code); - cmd.argc = (le32_to_cpu(container.length) - sizeof(container)) - / sizeof(uint32_t); - cmd.trans = le32_to_cpu(container.trans); - if (cmd.argc > ARRAY_SIZE(cmd.argv)) { - cmd.argc = ARRAY_SIZE(cmd.argv); - } - if (p->iov.size < sizeof(container) + cmd.argc * sizeof(uint32_t)) { - trace_usb_mtp_stall(s->dev.addr, "packet too small"); - p->status = USB_RET_STALL; - return; - } - usb_packet_copy(p, ¶ms, cmd.argc * sizeof(uint32_t)); - for (i = 0; i < cmd.argc; i++) { - cmd.argv[i] = le32_to_cpu(params[i]); - } - trace_usb_mtp_command(s->dev.addr, cmd.code, cmd.trans, - (cmd.argc > 0) ? cmd.argv[0] : 0, - (cmd.argc > 1) ? cmd.argv[1] : 0, - (cmd.argc > 2) ? cmd.argv[2] : 0, - (cmd.argc > 3) ? cmd.argv[3] : 0, - (cmd.argc > 4) ? cmd.argv[4] : 0); - usb_mtp_command(s, &cmd); - break; - default: - /* not needed as long as the mtp device is read-only */ - p->status = USB_RET_STALL; - return; - } - 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: - trace_usb_mtp_stall(s->dev.addr, "invalid endpoint"); - p->status = USB_RET_STALL; - return; - } - - if (p->actual_length == 0) { - trace_usb_mtp_nak(s->dev.addr, p->ep->nr); - p->status = USB_RET_NAK; - return; - } else { - trace_usb_mtp_xfer(s->dev.addr, p->ep->nr, p->actual_length, - p->iov.size); - return; - } -} - -static void usb_mtp_realize(USBDevice *dev, Error **errp) -{ - MTPState *s = USB_MTP(dev); - - usb_desc_create_serial(dev); - usb_desc_init(dev); - QTAILQ_INIT(&s->objects); - if (s->desc == NULL) { - if (s->root == NULL) { - error_setg(errp, "usb-mtp: x-root property must be configured"); - return; - } - s->desc = strrchr(s->root, '/'); - if (s->desc && s->desc[0]) { - s->desc = g_strdup(s->desc + 1); - } else { - s->desc = g_strdup("none"); - } - } -} - -static const VMStateDescription vmstate_usb_mtp = { - .name = "usb-mtp", - .unmigratable = 1, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_USB_DEVICE(dev, MTPState), - VMSTATE_END_OF_LIST() - } -}; - -static Property mtp_properties[] = { - DEFINE_PROP_STRING("x-root", MTPState, root), - DEFINE_PROP_STRING("desc", MTPState, desc), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_mtp_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_mtp_realize; - uc->product_desc = "QEMU USB MTP"; - uc->usb_desc = &desc; - uc->cancel_packet = usb_mtp_cancel_packet; - uc->handle_attach = usb_desc_attach; - uc->handle_reset = usb_mtp_handle_reset; - uc->handle_control = usb_mtp_handle_control; - uc->handle_data = usb_mtp_handle_data; - dc->fw_name = "mtp"; - dc->vmsd = &vmstate_usb_mtp; - dc->props = mtp_properties; -} - -static TypeInfo mtp_info = { - .name = TYPE_USB_MTP, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(MTPState), - .class_init = usb_mtp_class_initfn, -}; - -static void usb_mtp_register_types(void) -{ - type_register_static(&mtp_info); -} - -type_init(usb_mtp_register_types) diff --git a/qemu/hw/usb/dev-network.c b/qemu/hw/usb/dev-network.c deleted file mode 100644 index 74306b58e..000000000 --- a/qemu/hw/usb/dev-network.c +++ /dev/null @@ -1,1455 +0,0 @@ -/* - * QEMU USB Net devices - * - * Copyright (c) 2006 Thomas Sailer - * Copyright (c) 2008 Andrzej Zaborowski - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" -#include "net/net.h" -#include "qemu/error-report.h" -#include "qemu/queue.h" -#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. - * It's for devices with only CDC Ethernet configurations. - */ -#define CDC_VENDOR_NUM 0x0525 /* NetChip */ -#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ -/* For hardware that can talk RNDIS and either of the above protocols, - * use this ID ... the windows INF files will know it. - */ -#define RNDIS_VENDOR_NUM 0x0525 /* NetChip */ -#define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */ - -enum usbstring_idx { - STRING_MANUFACTURER = 1, - STRING_PRODUCT, - STRING_ETHADDR, - STRING_DATA, - STRING_CONTROL, - STRING_RNDIS_CONTROL, - STRING_CDC, - STRING_SUBSET, - STRING_RNDIS, - STRING_SERIALNUMBER, -}; - -#define DEV_CONFIG_VALUE 1 /* CDC or a subset */ -#define DEV_RNDIS_CONFIG_VALUE 2 /* RNDIS; optional */ - -#define USB_CDC_SUBCLASS_ACM 0x02 -#define USB_CDC_SUBCLASS_ETHERNET 0x06 - -#define USB_CDC_PROTO_NONE 0 -#define USB_CDC_ACM_PROTO_VENDOR 0xff - -#define USB_CDC_HEADER_TYPE 0x00 /* header_desc */ -#define USB_CDC_CALL_MANAGEMENT_TYPE 0x01 /* call_mgmt_descriptor */ -#define USB_CDC_ACM_TYPE 0x02 /* acm_descriptor */ -#define USB_CDC_UNION_TYPE 0x06 /* union_desc */ -#define USB_CDC_ETHERNET_TYPE 0x0f /* ether_desc */ - -#define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00 -#define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01 -#define USB_CDC_REQ_SET_LINE_CODING 0x20 -#define USB_CDC_REQ_GET_LINE_CODING 0x21 -#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 -#define USB_CDC_REQ_SEND_BREAK 0x23 -#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40 -#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41 -#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42 -#define USB_CDC_SET_ETHERNET_PACKET_FILTER 0x43 -#define USB_CDC_GET_ETHERNET_STATISTIC 0x44 - -#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ -#define STATUS_BYTECOUNT 16 /* 8 byte header + data */ - -#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ - -static const USBDescStrings usb_net_stringtable = { - [STRING_MANUFACTURER] = "QEMU", - [STRING_PRODUCT] = "RNDIS/QEMU USB Network Device", - [STRING_ETHADDR] = "400102030405", - [STRING_DATA] = "QEMU USB Net Data Interface", - [STRING_CONTROL] = "QEMU USB Net Control Interface", - [STRING_RNDIS_CONTROL] = "QEMU USB Net RNDIS Control Interface", - [STRING_CDC] = "QEMU USB Net CDC", - [STRING_SUBSET] = "QEMU USB Net Subset", - [STRING_RNDIS] = "QEMU USB Net RNDIS", - [STRING_SERIALNUMBER] = "1", -}; - -static const USBDescIface desc_iface_rndis[] = { - { - /* RNDIS Control Interface */ - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, - .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, - .iInterface = STRING_RNDIS_CONTROL, - .ndesc = 4, - .descs = (USBDescOther[]) { - { - /* Header Descriptor */ - .data = (uint8_t[]) { - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */ - 0x10, 0x01, /* le16 bcdCDC */ - }, - },{ - /* Call Management Descriptor */ - .data = (uint8_t[]) { - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_CALL_MANAGEMENT_TYPE, /* u8 bDescriptorSubType */ - 0x00, /* u8 bmCapabilities */ - 0x01, /* u8 bDataInterface */ - }, - },{ - /* ACM Descriptor */ - .data = (uint8_t[]) { - 0x04, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_ACM_TYPE, /* u8 bDescriptorSubType */ - 0x00, /* u8 bmCapabilities */ - }, - },{ - /* Union Descriptor */ - .data = (uint8_t[]) { - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */ - 0x00, /* u8 bMasterInterface0 */ - 0x01, /* u8 bSlaveInterface0 */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = STATUS_BYTECOUNT, - .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, - }, - } - },{ - /* RNDIS Data Interface */ - .bInterfaceNumber = 1, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_CDC_DATA, - .iInterface = STRING_DATA, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x02, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 0x40, - },{ - .bEndpointAddress = USB_DIR_OUT | 0x02, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 0x40, - } - } - } -}; - -static const USBDescIface desc_iface_cdc[] = { - { - /* CDC Control Interface */ - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, - .bInterfaceProtocol = USB_CDC_PROTO_NONE, - .iInterface = STRING_CONTROL, - .ndesc = 3, - .descs = (USBDescOther[]) { - { - /* Header Descriptor */ - .data = (uint8_t[]) { - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */ - 0x10, 0x01, /* le16 bcdCDC */ - }, - },{ - /* Union Descriptor */ - .data = (uint8_t[]) { - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */ - 0x00, /* u8 bMasterInterface0 */ - 0x01, /* u8 bSlaveInterface0 */ - }, - },{ - /* Ethernet Descriptor */ - .data = (uint8_t[]) { - 0x0d, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_ETHERNET_TYPE, /* u8 bDescriptorSubType */ - STRING_ETHADDR, /* u8 iMACAddress */ - 0x00, 0x00, 0x00, 0x00, /* le32 bmEthernetStatistics */ - ETH_FRAME_LEN & 0xff, - ETH_FRAME_LEN >> 8, /* le16 wMaxSegmentSize */ - 0x00, 0x00, /* le16 wNumberMCFilters */ - 0x00, /* u8 bNumberPowerFilters */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = STATUS_BYTECOUNT, - .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, - }, - } - },{ - /* CDC Data Interface (off) */ - .bInterfaceNumber = 1, - .bAlternateSetting = 0, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_CDC_DATA, - },{ - /* CDC Data Interface */ - .bInterfaceNumber = 1, - .bAlternateSetting = 1, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_CDC_DATA, - .iInterface = STRING_DATA, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x02, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 0x40, - },{ - .bEndpointAddress = USB_DIR_OUT | 0x02, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 0x40, - } - } - } -}; - -static const USBDescDevice desc_device_net = { - .bcdUSB = 0x0200, - .bDeviceClass = USB_CLASS_COMM, - .bMaxPacketSize0 = 0x40, - .bNumConfigurations = 2, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 2, - .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE, - .iConfiguration = STRING_RNDIS, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .bMaxPower = 0x32, - .nif = ARRAY_SIZE(desc_iface_rndis), - .ifs = desc_iface_rndis, - },{ - .bNumInterfaces = 2, - .bConfigurationValue = DEV_CONFIG_VALUE, - .iConfiguration = STRING_CDC, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .bMaxPower = 0x32, - .nif = ARRAY_SIZE(desc_iface_cdc), - .ifs = desc_iface_cdc, - } - }, -}; - -static const USBDesc desc_net = { - .id = { - .idVendor = RNDIS_VENDOR_NUM, - .idProduct = RNDIS_PRODUCT_NUM, - .bcdDevice = 0, - .iManufacturer = STRING_MANUFACTURER, - .iProduct = STRING_PRODUCT, - .iSerialNumber = STRING_SERIALNUMBER, - }, - .full = &desc_device_net, - .str = usb_net_stringtable, -}; - -/* - * RNDIS Definitions - in theory not specific to USB. - */ -#define RNDIS_MAXIMUM_FRAME_SIZE 1518 -#define RNDIS_MAX_TOTAL_SIZE 1558 - -/* Remote NDIS Versions */ -#define RNDIS_MAJOR_VERSION 1 -#define RNDIS_MINOR_VERSION 0 - -/* Status Values */ -#define RNDIS_STATUS_SUCCESS 0x00000000U /* Success */ -#define RNDIS_STATUS_FAILURE 0xc0000001U /* Unspecified error */ -#define RNDIS_STATUS_INVALID_DATA 0xc0010015U /* Invalid data */ -#define RNDIS_STATUS_NOT_SUPPORTED 0xc00000bbU /* Unsupported request */ -#define RNDIS_STATUS_MEDIA_CONNECT 0x4001000bU /* Device connected */ -#define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000cU /* Device disconnected */ - -/* Message Set for Connectionless (802.3) Devices */ -enum { - RNDIS_PACKET_MSG = 1, - RNDIS_INITIALIZE_MSG = 2, /* Initialize device */ - RNDIS_HALT_MSG = 3, - RNDIS_QUERY_MSG = 4, - RNDIS_SET_MSG = 5, - RNDIS_RESET_MSG = 6, - RNDIS_INDICATE_STATUS_MSG = 7, - RNDIS_KEEPALIVE_MSG = 8, -}; - -/* Message completion */ -enum { - RNDIS_INITIALIZE_CMPLT = 0x80000002U, - RNDIS_QUERY_CMPLT = 0x80000004U, - RNDIS_SET_CMPLT = 0x80000005U, - RNDIS_RESET_CMPLT = 0x80000006U, - RNDIS_KEEPALIVE_CMPLT = 0x80000008U, -}; - -/* Device Flags */ -enum { - RNDIS_DF_CONNECTIONLESS = 1, - RNDIS_DF_CONNECTIONORIENTED = 2, -}; - -#define RNDIS_MEDIUM_802_3 0x00000000U - -/* from drivers/net/sk98lin/h/skgepnmi.h */ -#define OID_PNP_CAPABILITIES 0xfd010100 -#define OID_PNP_SET_POWER 0xfd010101 -#define OID_PNP_QUERY_POWER 0xfd010102 -#define OID_PNP_ADD_WAKE_UP_PATTERN 0xfd010103 -#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xfd010104 -#define OID_PNP_ENABLE_WAKE_UP 0xfd010106 - -typedef uint32_t le32; - -typedef struct rndis_init_msg_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; - le32 MajorVersion; - le32 MinorVersion; - le32 MaxTransferSize; -} rndis_init_msg_type; - -typedef struct rndis_init_cmplt_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; - le32 Status; - le32 MajorVersion; - le32 MinorVersion; - le32 DeviceFlags; - le32 Medium; - le32 MaxPacketsPerTransfer; - le32 MaxTransferSize; - le32 PacketAlignmentFactor; - le32 AFListOffset; - le32 AFListSize; -} rndis_init_cmplt_type; - -typedef struct rndis_halt_msg_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; -} rndis_halt_msg_type; - -typedef struct rndis_query_msg_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; - le32 OID; - le32 InformationBufferLength; - le32 InformationBufferOffset; - le32 DeviceVcHandle; -} rndis_query_msg_type; - -typedef struct rndis_query_cmplt_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; - le32 Status; - le32 InformationBufferLength; - le32 InformationBufferOffset; -} rndis_query_cmplt_type; - -typedef struct rndis_set_msg_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; - le32 OID; - le32 InformationBufferLength; - le32 InformationBufferOffset; - le32 DeviceVcHandle; -} rndis_set_msg_type; - -typedef struct rndis_set_cmplt_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; - le32 Status; -} rndis_set_cmplt_type; - -typedef struct rndis_reset_msg_type { - le32 MessageType; - le32 MessageLength; - le32 Reserved; -} rndis_reset_msg_type; - -typedef struct rndis_reset_cmplt_type { - le32 MessageType; - le32 MessageLength; - le32 Status; - le32 AddressingReset; -} rndis_reset_cmplt_type; - -typedef struct rndis_indicate_status_msg_type { - le32 MessageType; - le32 MessageLength; - le32 Status; - le32 StatusBufferLength; - le32 StatusBufferOffset; -} rndis_indicate_status_msg_type; - -typedef struct rndis_keepalive_msg_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; -} rndis_keepalive_msg_type; - -typedef struct rndis_keepalive_cmplt_type { - le32 MessageType; - le32 MessageLength; - le32 RequestID; - le32 Status; -} rndis_keepalive_cmplt_type; - -struct rndis_packet_msg_type { - le32 MessageType; - le32 MessageLength; - le32 DataOffset; - le32 DataLength; - le32 OOBDataOffset; - le32 OOBDataLength; - le32 NumOOBDataElements; - le32 PerPacketInfoOffset; - le32 PerPacketInfoLength; - le32 VcHandle; - le32 Reserved; -}; - -struct rndis_config_parameter { - le32 ParameterNameOffset; - le32 ParameterNameLength; - le32 ParameterType; - le32 ParameterValueOffset; - le32 ParameterValueLength; -}; - -/* implementation specific */ -enum rndis_state -{ - RNDIS_UNINITIALIZED, - RNDIS_INITIALIZED, - RNDIS_DATA_INITIALIZED, -}; - -/* from ndis.h */ -enum ndis_oid { - /* Required Object IDs (OIDs) */ - OID_GEN_SUPPORTED_LIST = 0x00010101, - OID_GEN_HARDWARE_STATUS = 0x00010102, - OID_GEN_MEDIA_SUPPORTED = 0x00010103, - OID_GEN_MEDIA_IN_USE = 0x00010104, - OID_GEN_MAXIMUM_LOOKAHEAD = 0x00010105, - OID_GEN_MAXIMUM_FRAME_SIZE = 0x00010106, - OID_GEN_LINK_SPEED = 0x00010107, - OID_GEN_TRANSMIT_BUFFER_SPACE = 0x00010108, - OID_GEN_RECEIVE_BUFFER_SPACE = 0x00010109, - OID_GEN_TRANSMIT_BLOCK_SIZE = 0x0001010a, - OID_GEN_RECEIVE_BLOCK_SIZE = 0x0001010b, - OID_GEN_VENDOR_ID = 0x0001010c, - OID_GEN_VENDOR_DESCRIPTION = 0x0001010d, - OID_GEN_CURRENT_PACKET_FILTER = 0x0001010e, - OID_GEN_CURRENT_LOOKAHEAD = 0x0001010f, - OID_GEN_DRIVER_VERSION = 0x00010110, - OID_GEN_MAXIMUM_TOTAL_SIZE = 0x00010111, - OID_GEN_PROTOCOL_OPTIONS = 0x00010112, - OID_GEN_MAC_OPTIONS = 0x00010113, - OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114, - OID_GEN_MAXIMUM_SEND_PACKETS = 0x00010115, - OID_GEN_VENDOR_DRIVER_VERSION = 0x00010116, - OID_GEN_SUPPORTED_GUIDS = 0x00010117, - OID_GEN_NETWORK_LAYER_ADDRESSES = 0x00010118, - OID_GEN_TRANSPORT_HEADER_OFFSET = 0x00010119, - OID_GEN_MACHINE_NAME = 0x0001021a, - OID_GEN_RNDIS_CONFIG_PARAMETER = 0x0001021b, - OID_GEN_VLAN_ID = 0x0001021c, - - /* Optional OIDs */ - OID_GEN_MEDIA_CAPABILITIES = 0x00010201, - OID_GEN_PHYSICAL_MEDIUM = 0x00010202, - - /* Required statistics OIDs */ - OID_GEN_XMIT_OK = 0x00020101, - OID_GEN_RCV_OK = 0x00020102, - OID_GEN_XMIT_ERROR = 0x00020103, - OID_GEN_RCV_ERROR = 0x00020104, - OID_GEN_RCV_NO_BUFFER = 0x00020105, - - /* Optional statistics OIDs */ - OID_GEN_DIRECTED_BYTES_XMIT = 0x00020201, - OID_GEN_DIRECTED_FRAMES_XMIT = 0x00020202, - OID_GEN_MULTICAST_BYTES_XMIT = 0x00020203, - OID_GEN_MULTICAST_FRAMES_XMIT = 0x00020204, - OID_GEN_BROADCAST_BYTES_XMIT = 0x00020205, - OID_GEN_BROADCAST_FRAMES_XMIT = 0x00020206, - OID_GEN_DIRECTED_BYTES_RCV = 0x00020207, - OID_GEN_DIRECTED_FRAMES_RCV = 0x00020208, - OID_GEN_MULTICAST_BYTES_RCV = 0x00020209, - OID_GEN_MULTICAST_FRAMES_RCV = 0x0002020a, - OID_GEN_BROADCAST_BYTES_RCV = 0x0002020b, - OID_GEN_BROADCAST_FRAMES_RCV = 0x0002020c, - OID_GEN_RCV_CRC_ERROR = 0x0002020d, - OID_GEN_TRANSMIT_QUEUE_LENGTH = 0x0002020e, - OID_GEN_GET_TIME_CAPS = 0x0002020f, - OID_GEN_GET_NETCARD_TIME = 0x00020210, - OID_GEN_NETCARD_LOAD = 0x00020211, - OID_GEN_DEVICE_PROFILE = 0x00020212, - OID_GEN_INIT_TIME_MS = 0x00020213, - OID_GEN_RESET_COUNTS = 0x00020214, - OID_GEN_MEDIA_SENSE_COUNTS = 0x00020215, - OID_GEN_FRIENDLY_NAME = 0x00020216, - OID_GEN_MINIPORT_INFO = 0x00020217, - OID_GEN_RESET_VERIFY_PARAMETERS = 0x00020218, - - /* IEEE 802.3 (Ethernet) OIDs */ - OID_802_3_PERMANENT_ADDRESS = 0x01010101, - OID_802_3_CURRENT_ADDRESS = 0x01010102, - OID_802_3_MULTICAST_LIST = 0x01010103, - OID_802_3_MAXIMUM_LIST_SIZE = 0x01010104, - OID_802_3_MAC_OPTIONS = 0x01010105, - OID_802_3_RCV_ERROR_ALIGNMENT = 0x01020101, - OID_802_3_XMIT_ONE_COLLISION = 0x01020102, - OID_802_3_XMIT_MORE_COLLISIONS = 0x01020103, - OID_802_3_XMIT_DEFERRED = 0x01020201, - OID_802_3_XMIT_MAX_COLLISIONS = 0x01020202, - OID_802_3_RCV_OVERRUN = 0x01020203, - OID_802_3_XMIT_UNDERRUN = 0x01020204, - OID_802_3_XMIT_HEARTBEAT_FAILURE = 0x01020205, - OID_802_3_XMIT_TIMES_CRS_LOST = 0x01020206, - OID_802_3_XMIT_LATE_COLLISIONS = 0x01020207, -}; - -static const uint32_t oid_supported_list[] = -{ - /* the general stuff */ - OID_GEN_SUPPORTED_LIST, - OID_GEN_HARDWARE_STATUS, - OID_GEN_MEDIA_SUPPORTED, - OID_GEN_MEDIA_IN_USE, - OID_GEN_MAXIMUM_FRAME_SIZE, - OID_GEN_LINK_SPEED, - OID_GEN_TRANSMIT_BLOCK_SIZE, - OID_GEN_RECEIVE_BLOCK_SIZE, - OID_GEN_VENDOR_ID, - OID_GEN_VENDOR_DESCRIPTION, - OID_GEN_VENDOR_DRIVER_VERSION, - OID_GEN_CURRENT_PACKET_FILTER, - OID_GEN_MAXIMUM_TOTAL_SIZE, - OID_GEN_MEDIA_CONNECT_STATUS, - OID_GEN_PHYSICAL_MEDIUM, - - /* the statistical stuff */ - OID_GEN_XMIT_OK, - OID_GEN_RCV_OK, - OID_GEN_XMIT_ERROR, - OID_GEN_RCV_ERROR, - OID_GEN_RCV_NO_BUFFER, - - /* IEEE 802.3 */ - /* the general stuff */ - OID_802_3_PERMANENT_ADDRESS, - OID_802_3_CURRENT_ADDRESS, - OID_802_3_MULTICAST_LIST, - OID_802_3_MAC_OPTIONS, - OID_802_3_MAXIMUM_LIST_SIZE, - - /* the statistical stuff */ - OID_802_3_RCV_ERROR_ALIGNMENT, - OID_802_3_XMIT_ONE_COLLISION, - OID_802_3_XMIT_MORE_COLLISIONS, -}; - -#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA (1 << 0) -#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED (1 << 1) -#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND (1 << 2) -#define NDIS_MAC_OPTION_NO_LOOPBACK (1 << 3) -#define NDIS_MAC_OPTION_FULL_DUPLEX (1 << 4) -#define NDIS_MAC_OPTION_EOTX_INDICATION (1 << 5) -#define NDIS_MAC_OPTION_8021P_PRIORITY (1 << 6) - -struct rndis_response { - QTAILQ_ENTRY(rndis_response) entries; - uint32_t length; - uint8_t buf[0]; -}; - -typedef struct USBNetState { - USBDevice dev; - - enum rndis_state rndis_state; - uint32_t medium; - uint32_t speed; - uint32_t media_state; - uint16_t filter; - uint32_t vendorid; - - unsigned int out_ptr; - uint8_t out_buf[2048]; - - unsigned int in_ptr, in_len; - uint8_t in_buf[2048]; - - USBEndpoint *intr; - - char usbstring_mac[13]; - NICState *nic; - NICConf conf; - QTAILQ_HEAD(rndis_resp_head, rndis_response) rndis_resp; -} USBNetState; - -#define TYPE_USB_NET "usb-net" -#define USB_NET(obj) OBJECT_CHECK(USBNetState, (obj), TYPE_USB_NET) - -static int is_rndis(USBNetState *s) -{ - return s->dev.config ? - s->dev.config->bConfigurationValue == DEV_RNDIS_CONFIG_VALUE : 0; -} - -static int ndis_query(USBNetState *s, uint32_t oid, - uint8_t *inbuf, unsigned int inlen, uint8_t *outbuf, - size_t outlen) -{ - unsigned int i; - - switch (oid) { - /* general oids (table 4-1) */ - /* mandatory */ - case OID_GEN_SUPPORTED_LIST: - for (i = 0; i < ARRAY_SIZE(oid_supported_list); i++) - ((le32 *) outbuf)[i] = cpu_to_le32(oid_supported_list[i]); - return sizeof(oid_supported_list); - - /* mandatory */ - case OID_GEN_HARDWARE_STATUS: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_MEDIA_SUPPORTED: - *((le32 *) outbuf) = cpu_to_le32(s->medium); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_MEDIA_IN_USE: - *((le32 *) outbuf) = cpu_to_le32(s->medium); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_MAXIMUM_FRAME_SIZE: - *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_LINK_SPEED: - *((le32 *) outbuf) = cpu_to_le32(s->speed); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_TRANSMIT_BLOCK_SIZE: - *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_RECEIVE_BLOCK_SIZE: - *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_VENDOR_ID: - *((le32 *) outbuf) = cpu_to_le32(s->vendorid); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_VENDOR_DESCRIPTION: - pstrcpy((char *)outbuf, outlen, "QEMU USB RNDIS Net"); - return strlen((char *)outbuf) + 1; - - case OID_GEN_VENDOR_DRIVER_VERSION: - *((le32 *) outbuf) = cpu_to_le32(1); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_CURRENT_PACKET_FILTER: - *((le32 *) outbuf) = cpu_to_le32(s->filter); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_MAXIMUM_TOTAL_SIZE: - *((le32 *) outbuf) = cpu_to_le32(RNDIS_MAX_TOTAL_SIZE); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_MEDIA_CONNECT_STATUS: - *((le32 *) outbuf) = cpu_to_le32(s->media_state); - return sizeof(le32); - - case OID_GEN_PHYSICAL_MEDIUM: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - case OID_GEN_MAC_OPTIONS: - *((le32 *) outbuf) = cpu_to_le32( - NDIS_MAC_OPTION_RECEIVE_SERIALIZED | - NDIS_MAC_OPTION_FULL_DUPLEX); - return sizeof(le32); - - /* statistics OIDs (table 4-2) */ - /* mandatory */ - case OID_GEN_XMIT_OK: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_RCV_OK: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_XMIT_ERROR: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_RCV_ERROR: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - /* mandatory */ - case OID_GEN_RCV_NO_BUFFER: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - /* ieee802.3 OIDs (table 4-3) */ - /* mandatory */ - case OID_802_3_PERMANENT_ADDRESS: - memcpy(outbuf, s->conf.macaddr.a, 6); - return 6; - - /* mandatory */ - case OID_802_3_CURRENT_ADDRESS: - memcpy(outbuf, s->conf.macaddr.a, 6); - return 6; - - /* mandatory */ - case OID_802_3_MULTICAST_LIST: - *((le32 *) outbuf) = cpu_to_le32(0xe0000000); - return sizeof(le32); - - /* mandatory */ - case OID_802_3_MAXIMUM_LIST_SIZE: - *((le32 *) outbuf) = cpu_to_le32(1); - return sizeof(le32); - - case OID_802_3_MAC_OPTIONS: - return 0; - - /* ieee802.3 statistics OIDs (table 4-4) */ - /* mandatory */ - case OID_802_3_RCV_ERROR_ALIGNMENT: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - /* mandatory */ - case OID_802_3_XMIT_ONE_COLLISION: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - /* mandatory */ - case OID_802_3_XMIT_MORE_COLLISIONS: - *((le32 *) outbuf) = cpu_to_le32(0); - return sizeof(le32); - - default: - fprintf(stderr, "usbnet: unknown OID 0x%08x\n", oid); - return 0; - } - return -1; -} - -static int ndis_set(USBNetState *s, uint32_t oid, - uint8_t *inbuf, unsigned int inlen) -{ - switch (oid) { - case OID_GEN_CURRENT_PACKET_FILTER: - s->filter = le32_to_cpup((le32 *) inbuf); - if (s->filter) { - s->rndis_state = RNDIS_DATA_INITIALIZED; - } else { - s->rndis_state = RNDIS_INITIALIZED; - } - return 0; - - case OID_802_3_MULTICAST_LIST: - return 0; - } - return -1; -} - -static int rndis_get_response(USBNetState *s, uint8_t *buf) -{ - int ret = 0; - struct rndis_response *r = s->rndis_resp.tqh_first; - - if (!r) - return ret; - - QTAILQ_REMOVE(&s->rndis_resp, r, entries); - ret = r->length; - memcpy(buf, r->buf, r->length); - g_free(r); - - return ret; -} - -static void *rndis_queue_response(USBNetState *s, unsigned int length) -{ - struct rndis_response *r = - g_malloc0(sizeof(struct rndis_response) + length); - - if (QTAILQ_EMPTY(&s->rndis_resp)) { - usb_wakeup(s->intr, 0); - } - - QTAILQ_INSERT_TAIL(&s->rndis_resp, r, entries); - r->length = length; - - return &r->buf[0]; -} - -static void rndis_clear_responsequeue(USBNetState *s) -{ - struct rndis_response *r; - - while ((r = s->rndis_resp.tqh_first)) { - QTAILQ_REMOVE(&s->rndis_resp, r, entries); - g_free(r); - } -} - -static int rndis_init_response(USBNetState *s, rndis_init_msg_type *buf) -{ - rndis_init_cmplt_type *resp = - rndis_queue_response(s, sizeof(rndis_init_cmplt_type)); - - if (!resp) - return USB_RET_STALL; - - resp->MessageType = cpu_to_le32(RNDIS_INITIALIZE_CMPLT); - resp->MessageLength = cpu_to_le32(sizeof(rndis_init_cmplt_type)); - resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); - resp->MajorVersion = cpu_to_le32(RNDIS_MAJOR_VERSION); - resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION); - resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS); - resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3); - resp->MaxPacketsPerTransfer = cpu_to_le32(1); - resp->MaxTransferSize = cpu_to_le32(ETH_FRAME_LEN + - sizeof(struct rndis_packet_msg_type) + 22); - resp->PacketAlignmentFactor = cpu_to_le32(0); - resp->AFListOffset = cpu_to_le32(0); - resp->AFListSize = cpu_to_le32(0); - return 0; -} - -static int rndis_query_response(USBNetState *s, - rndis_query_msg_type *buf, unsigned int length) -{ - rndis_query_cmplt_type *resp; - /* oid_supported_list is the largest data reply */ - uint8_t infobuf[sizeof(oid_supported_list)]; - uint32_t bufoffs, buflen; - int infobuflen; - unsigned int resplen; - - bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8; - buflen = le32_to_cpu(buf->InformationBufferLength); - 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, - sizeof(infobuf)); - resplen = sizeof(rndis_query_cmplt_type) + - ((infobuflen < 0) ? 0 : infobuflen); - resp = rndis_queue_response(s, resplen); - if (!resp) - return USB_RET_STALL; - - resp->MessageType = cpu_to_le32(RNDIS_QUERY_CMPLT); - resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - resp->MessageLength = cpu_to_le32(resplen); - - if (infobuflen < 0) { - /* OID not supported */ - resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED); - resp->InformationBufferLength = cpu_to_le32(0); - resp->InformationBufferOffset = cpu_to_le32(0); - return 0; - } - - resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); - resp->InformationBufferOffset = - cpu_to_le32(infobuflen ? sizeof(rndis_query_cmplt_type) - 8 : 0); - resp->InformationBufferLength = cpu_to_le32(infobuflen); - memcpy(resp + 1, infobuf, infobuflen); - - return 0; -} - -static int rndis_set_response(USBNetState *s, - rndis_set_msg_type *buf, unsigned int length) -{ - rndis_set_cmplt_type *resp = - rndis_queue_response(s, sizeof(rndis_set_cmplt_type)); - uint32_t bufoffs, buflen; - int ret; - - if (!resp) - return USB_RET_STALL; - - bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8; - buflen = le32_to_cpu(buf->InformationBufferLength); - 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); - resp->MessageType = cpu_to_le32(RNDIS_SET_CMPLT); - resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - resp->MessageLength = cpu_to_le32(sizeof(rndis_set_cmplt_type)); - if (ret < 0) { - /* OID not supported */ - resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED); - return 0; - } - resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); - - return 0; -} - -static int rndis_reset_response(USBNetState *s, rndis_reset_msg_type *buf) -{ - rndis_reset_cmplt_type *resp = - rndis_queue_response(s, sizeof(rndis_reset_cmplt_type)); - - if (!resp) - return USB_RET_STALL; - - resp->MessageType = cpu_to_le32(RNDIS_RESET_CMPLT); - resp->MessageLength = cpu_to_le32(sizeof(rndis_reset_cmplt_type)); - resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); - resp->AddressingReset = cpu_to_le32(1); /* reset information */ - - return 0; -} - -static int rndis_keepalive_response(USBNetState *s, - rndis_keepalive_msg_type *buf) -{ - rndis_keepalive_cmplt_type *resp = - rndis_queue_response(s, sizeof(rndis_keepalive_cmplt_type)); - - if (!resp) - return USB_RET_STALL; - - resp->MessageType = cpu_to_le32(RNDIS_KEEPALIVE_CMPLT); - resp->MessageLength = cpu_to_le32(sizeof(rndis_keepalive_cmplt_type)); - resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS); - - return 0; -} - -/* Prepare to receive the next packet */ -static void usb_net_reset_in_buf(USBNetState *s) -{ - s->in_ptr = s->in_len = 0; - qemu_flush_queued_packets(qemu_get_queue(s->nic)); -} - -static int rndis_parse(USBNetState *s, uint8_t *data, int length) -{ - uint32_t msg_type; - le32 *tmp = (le32 *) data; - - msg_type = le32_to_cpup(tmp); - - switch (msg_type) { - case RNDIS_INITIALIZE_MSG: - s->rndis_state = RNDIS_INITIALIZED; - return rndis_init_response(s, (rndis_init_msg_type *) data); - - case RNDIS_HALT_MSG: - s->rndis_state = RNDIS_UNINITIALIZED; - return 0; - - case RNDIS_QUERY_MSG: - return rndis_query_response(s, (rndis_query_msg_type *) data, length); - - case RNDIS_SET_MSG: - return rndis_set_response(s, (rndis_set_msg_type *) data, length); - - case RNDIS_RESET_MSG: - rndis_clear_responsequeue(s); - s->out_ptr = 0; - usb_net_reset_in_buf(s); - return rndis_reset_response(s, (rndis_reset_msg_type *) data); - - case RNDIS_KEEPALIVE_MSG: - /* For USB: host does this every 5 seconds */ - return rndis_keepalive_response(s, (rndis_keepalive_msg_type *) data); - } - - return USB_RET_STALL; -} - -static void usb_net_handle_reset(USBDevice *dev) -{ -} - -static void usb_net_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) -{ - USBNetState *s = (USBNetState *) dev; - int ret; - - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - switch(request) { - case ClassInterfaceOutRequest | USB_CDC_SEND_ENCAPSULATED_COMMAND: - if (!is_rndis(s) || value || index != 0) { - goto fail; - } -#ifdef TRAFFIC_DEBUG - { - unsigned int i; - fprintf(stderr, "SEND_ENCAPSULATED_COMMAND:"); - for (i = 0; i < length; i++) { - if (!(i & 15)) - fprintf(stderr, "\n%04x:", i); - fprintf(stderr, " %02x", data[i]); - } - fprintf(stderr, "\n\n"); - } -#endif - ret = rndis_parse(s, data, length); - if (ret < 0) { - p->status = ret; - } - break; - - case ClassInterfaceRequest | USB_CDC_GET_ENCAPSULATED_RESPONSE: - if (!is_rndis(s) || value || index != 0) { - goto fail; - } - p->actual_length = rndis_get_response(s, data); - if (p->actual_length == 0) { - data[0] = 0; - p->actual_length = 1; - } -#ifdef TRAFFIC_DEBUG - { - unsigned int i; - fprintf(stderr, "GET_ENCAPSULATED_RESPONSE:"); - for (i = 0; i < p->actual_length; i++) { - if (!(i & 15)) - fprintf(stderr, "\n%04x:", i); - fprintf(stderr, " %02x", data[i]); - } - fprintf(stderr, "\n\n"); - } -#endif - break; - - default: - fail: - fprintf(stderr, "usbnet: failed control transaction: " - "request 0x%x value 0x%x index 0x%x length 0x%x\n", - request, value, index, length); - p->status = USB_RET_STALL; - break; - } -} - -static void usb_net_handle_statusin(USBNetState *s, USBPacket *p) -{ - le32 buf[2]; - - if (p->iov.size < 8) { - p->status = USB_RET_STALL; - return; - } - - buf[0] = cpu_to_le32(1); - buf[1] = cpu_to_le32(0); - usb_packet_copy(p, buf, 8); - if (!s->rndis_resp.tqh_first) { - p->status = USB_RET_NAK; - } - -#ifdef TRAFFIC_DEBUG - fprintf(stderr, "usbnet: interrupt poll len %zu return %d", - p->iov.size, p->status); - iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->status); -#endif -} - -static void usb_net_handle_datain(USBNetState *s, USBPacket *p) -{ - int len; - - if (s->in_ptr > s->in_len) { - usb_net_reset_in_buf(s); - p->status = USB_RET_NAK; - return; - } - if (!s->in_len) { - p->status = USB_RET_NAK; - return; - } - len = s->in_len - s->in_ptr; - if (len > p->iov.size) { - len = p->iov.size; - } - usb_packet_copy(p, &s->in_buf[s->in_ptr], len); - s->in_ptr += len; - if (s->in_ptr >= s->in_len && - (is_rndis(s) || (s->in_len & (64 - 1)) || !len)) { - /* no short packet necessary */ - usb_net_reset_in_buf(s); - } - -#ifdef TRAFFIC_DEBUG - fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, len); - iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", len); -#endif -} - -static void usb_net_handle_dataout(USBNetState *s, USBPacket *p) -{ - int sz = sizeof(s->out_buf) - s->out_ptr; - struct rndis_packet_msg_type *msg = - (struct rndis_packet_msg_type *) s->out_buf; - uint32_t len; - -#ifdef TRAFFIC_DEBUG - fprintf(stderr, "usbnet: data out len %zu\n", p->iov.size); - iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->iov.size); -#endif - - if (sz > p->iov.size) { - sz = p->iov.size; - } - usb_packet_copy(p, &s->out_buf[s->out_ptr], sz); - s->out_ptr += sz; - - if (!is_rndis(s)) { - if (p->iov.size < 64) { - qemu_send_packet(qemu_get_queue(s->nic), s->out_buf, s->out_ptr); - s->out_ptr = 0; - } - return; - } - len = le32_to_cpu(msg->MessageLength); - if (s->out_ptr < 8 || s->out_ptr < len) { - return; - } - 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 < 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); -} - -static void usb_net_handle_data(USBDevice *dev, USBPacket *p) -{ - USBNetState *s = (USBNetState *) dev; - - switch(p->pid) { - case USB_TOKEN_IN: - switch (p->ep->nr) { - case 1: - usb_net_handle_statusin(s, p); - break; - - case 2: - usb_net_handle_datain(s, p); - break; - - default: - goto fail; - } - break; - - case USB_TOKEN_OUT: - switch (p->ep->nr) { - case 2: - usb_net_handle_dataout(s, p); - break; - - default: - goto fail; - } - break; - - default: - fail: - p->status = USB_RET_STALL; - break; - } - - if (p->status == USB_RET_STALL) { - fprintf(stderr, "usbnet: failed data transaction: " - "pid 0x%x ep 0x%x len 0x%zx\n", - p->pid, p->ep->nr, p->iov.size); - } -} - -static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t size) -{ - USBNetState *s = qemu_get_nic_opaque(nc); - uint8_t *in_buf = s->in_buf; - size_t total_size = size; - - if (!s->dev.config) { - return -1; - } - - if (is_rndis(s)) { - if (s->rndis_state != RNDIS_DATA_INITIALIZED) { - return -1; - } - total_size += sizeof(struct rndis_packet_msg_type); - } - if (total_size > sizeof(s->in_buf)) { - return -1; - } - - /* Only accept packet if input buffer is empty */ - if (s->in_len > 0) { - return 0; - } - - if (is_rndis(s)) { - struct rndis_packet_msg_type *msg; - - msg = (struct rndis_packet_msg_type *)in_buf; - memset(msg, 0, sizeof(struct rndis_packet_msg_type)); - msg->MessageType = cpu_to_le32(RNDIS_PACKET_MSG); - msg->MessageLength = cpu_to_le32(size + sizeof(*msg)); - msg->DataOffset = cpu_to_le32(sizeof(*msg) - 8); - msg->DataLength = cpu_to_le32(size); - /* msg->OOBDataOffset; - * msg->OOBDataLength; - * msg->NumOOBDataElements; - * msg->PerPacketInfoOffset; - * msg->PerPacketInfoLength; - * msg->VcHandle; - * msg->Reserved; - */ - in_buf += sizeof(*msg); - } - - memcpy(in_buf, buf, size); - s->in_len = total_size; - s->in_ptr = 0; - return size; -} - -static void usbnet_cleanup(NetClientState *nc) -{ - USBNetState *s = qemu_get_nic_opaque(nc); - - s->nic = NULL; -} - -static void usb_net_handle_destroy(USBDevice *dev) -{ - USBNetState *s = (USBNetState *) dev; - - /* TODO: remove the nd_table[] entry */ - rndis_clear_responsequeue(s); - qemu_del_nic(s->nic); -} - -static NetClientInfo net_usbnet_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .receive = usbnet_receive, - .cleanup = usbnet_cleanup, -}; - -static void usb_net_realize(USBDevice *dev, Error **errrp) -{ - USBNetState *s = USB_NET(dev); - - usb_desc_create_serial(dev); - usb_desc_init(dev); - - s->rndis_state = RNDIS_UNINITIALIZED; - QTAILQ_INIT(&s->rndis_resp); - - s->medium = 0; /* NDIS_MEDIUM_802_3 */ - s->speed = 1000000; /* 100MBps, in 100Bps units */ - s->media_state = 0; /* NDIS_MEDIA_STATE_CONNECTED */; - s->filter = 0; - s->vendorid = 0x1234; - s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); - - qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->nic = qemu_new_nic(&net_usbnet_info, &s->conf, - object_get_typename(OBJECT(s)), s->dev.qdev.id, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - snprintf(s->usbstring_mac, sizeof(s->usbstring_mac), - "%02x%02x%02x%02x%02x%02x", - 0x40, - s->conf.macaddr.a[1], - s->conf.macaddr.a[2], - s->conf.macaddr.a[3], - s->conf.macaddr.a[4], - s->conf.macaddr.a[5]); - usb_desc_set_string(dev, STRING_ETHADDR, s->usbstring_mac); -} - -static void usb_net_instance_init(Object *obj) -{ - USBDevice *dev = USB_DEVICE(obj); - USBNetState *s = USB_NET(dev); - - device_add_bootindex_property(obj, &s->conf.bootindex, - "bootindex", "/ethernet-phy@0", - &dev->qdev, NULL); -} - -static USBDevice *usb_net_init(USBBus *bus, const char *cmdline) -{ - Error *local_err = NULL; - USBDevice *dev; - QemuOpts *opts; - int idx; - - opts = qemu_opts_parse_noisily(qemu_find_opts("net"), cmdline, false); - if (!opts) { - return NULL; - } - qemu_opt_set(opts, "type", "nic", &error_abort); - qemu_opt_set(opts, "model", "usb", &error_abort); - - idx = net_client_init(opts, 0, &local_err); - if (local_err) { - error_report_err(local_err); - return NULL; - } - - dev = usb_create(bus, "usb-net"); - qdev_set_nic_properties(&dev->qdev, &nd_table[idx]); - return dev; -} - -static const VMStateDescription vmstate_usb_net = { - .name = "usb-net", - .unmigratable = 1, -}; - -static Property net_properties[] = { - DEFINE_NIC_PROPERTIES(USBNetState, conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_net_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_net_realize; - uc->product_desc = "QEMU USB Network Interface"; - uc->usb_desc = &desc_net; - uc->handle_reset = usb_net_handle_reset; - uc->handle_control = usb_net_handle_control; - uc->handle_data = usb_net_handle_data; - uc->handle_destroy = usb_net_handle_destroy; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->fw_name = "network"; - dc->vmsd = &vmstate_usb_net; - dc->props = net_properties; -} - -static const TypeInfo net_info = { - .name = TYPE_USB_NET, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBNetState), - .class_init = usb_net_class_initfn, - .instance_init = usb_net_instance_init, -}; - -static void usb_net_register_types(void) -{ - type_register_static(&net_info); - usb_legacy_register(TYPE_USB_NET, "net", usb_net_init); -} - -type_init(usb_net_register_types) diff --git a/qemu/hw/usb/dev-serial.c b/qemu/hw/usb/dev-serial.c deleted file mode 100644 index ba8538e60..000000000 --- a/qemu/hw/usb/dev-serial.c +++ /dev/null @@ -1,652 +0,0 @@ -/* - * FTDI FT232BM Device emulation - * - * Copyright (c) 2006 CodeSourcery. - * Copyright (c) 2008 Samuel Thibault - * Written by Paul Brook, reused for FTDI by Samuel Thibault - * - * 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" -#include "sysemu/char.h" - -//#define DEBUG_Serial - -#ifdef DEBUG_Serial -#define DPRINTF(fmt, ...) \ -do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - -#define RECV_BUF 384 - -/* Commands */ -#define FTDI_RESET 0 -#define FTDI_SET_MDM_CTRL 1 -#define FTDI_SET_FLOW_CTRL 2 -#define FTDI_SET_BAUD 3 -#define FTDI_SET_DATA 4 -#define FTDI_GET_MDM_ST 5 -#define FTDI_SET_EVENT_CHR 6 -#define FTDI_SET_ERROR_CHR 7 -#define FTDI_SET_LATENCY 9 -#define FTDI_GET_LATENCY 10 - -#define DeviceOutVendor ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) -#define DeviceInVendor ((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8) - -/* RESET */ - -#define FTDI_RESET_SIO 0 -#define FTDI_RESET_RX 1 -#define FTDI_RESET_TX 2 - -/* SET_MDM_CTRL */ - -#define FTDI_DTR 1 -#define FTDI_SET_DTR (FTDI_DTR << 8) -#define FTDI_RTS 2 -#define FTDI_SET_RTS (FTDI_RTS << 8) - -/* SET_FLOW_CTRL */ - -#define FTDI_RTS_CTS_HS 1 -#define FTDI_DTR_DSR_HS 2 -#define FTDI_XON_XOFF_HS 4 - -/* SET_DATA */ - -#define FTDI_PARITY (0x7 << 8) -#define FTDI_ODD (0x1 << 8) -#define FTDI_EVEN (0x2 << 8) -#define FTDI_MARK (0x3 << 8) -#define FTDI_SPACE (0x4 << 8) - -#define FTDI_STOP (0x3 << 11) -#define FTDI_STOP1 (0x0 << 11) -#define FTDI_STOP15 (0x1 << 11) -#define FTDI_STOP2 (0x2 << 11) - -/* GET_MDM_ST */ -/* TODO: should be sent every 40ms */ -#define FTDI_CTS (1<<4) // CTS line status -#define FTDI_DSR (1<<5) // DSR line status -#define FTDI_RI (1<<6) // RI line status -#define FTDI_RLSD (1<<7) // Receive Line Signal Detect - -/* Status */ - -#define FTDI_DR (1<<0) // Data Ready -#define FTDI_OE (1<<1) // Overrun Err -#define FTDI_PE (1<<2) // Parity Err -#define FTDI_FE (1<<3) // Framing Err -#define FTDI_BI (1<<4) // Break Interrupt -#define FTDI_THRE (1<<5) // Transmitter Holding Register -#define FTDI_TEMT (1<<6) // Transmitter Empty -#define FTDI_FIFO (1<<7) // Error in FIFO - -typedef struct { - USBDevice dev; - uint8_t recv_buf[RECV_BUF]; - uint16_t recv_ptr; - uint16_t recv_used; - uint8_t event_chr; - uint8_t error_chr; - uint8_t event_trigger; - QEMUSerialSetParams params; - int latency; /* ms */ - CharDriverState *cs; -} USBSerialState; - -#define TYPE_USB_SERIAL "usb-serial-dev" -#define USB_SERIAL_DEV(obj) OBJECT_CHECK(USBSerialState, (obj), TYPE_USB_SERIAL) - -enum { - STR_MANUFACTURER = 1, - STR_PRODUCT_SERIAL, - STR_PRODUCT_BRAILLE, - STR_SERIALNUMBER, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = "QEMU", - [STR_PRODUCT_SERIAL] = "QEMU USB SERIAL", - [STR_PRODUCT_BRAILLE] = "QEMU USB BAUM BRAILLE", - [STR_SERIALNUMBER] = "1", -}; - -static const USBDescIface desc_iface0 = { - .bInterfaceNumber = 0, - .bNumEndpoints = 2, - .bInterfaceClass = 0xff, - .bInterfaceSubClass = 0xff, - .bInterfaceProtocol = 0xff, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 64, - },{ - .bEndpointAddress = USB_DIR_OUT | 0x02, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 64, - }, - } -}; - -static const USBDescDevice desc_device = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 8, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .bmAttributes = USB_CFG_ATT_ONE, - .bMaxPower = 50, - .nif = 1, - .ifs = &desc_iface0, - }, - }, -}; - -static const USBDesc desc_serial = { - .id = { - .idVendor = 0x0403, - .idProduct = 0x6001, - .bcdDevice = 0x0400, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT_SERIAL, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device, - .str = desc_strings, -}; - -static const USBDesc desc_braille = { - .id = { - .idVendor = 0x0403, - .idProduct = 0xfe72, - .bcdDevice = 0x0400, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT_BRAILLE, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device, - .str = desc_strings, -}; - -static void usb_serial_reset(USBSerialState *s) -{ - /* TODO: Set flow control to none */ - s->event_chr = 0x0d; - s->event_trigger = 0; - s->recv_ptr = 0; - s->recv_used = 0; - /* TODO: purge in char driver */ -} - -static void usb_serial_handle_reset(USBDevice *dev) -{ - USBSerialState *s = (USBSerialState *)dev; - - DPRINTF("Reset\n"); - - usb_serial_reset(s); - /* TODO: Reset char device, send BREAK? */ -} - -static uint8_t usb_get_modem_lines(USBSerialState *s) -{ - int flags; - uint8_t ret; - - if (qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) - return FTDI_CTS|FTDI_DSR|FTDI_RLSD; - - ret = 0; - if (flags & CHR_TIOCM_CTS) - ret |= FTDI_CTS; - if (flags & CHR_TIOCM_DSR) - ret |= FTDI_DSR; - if (flags & CHR_TIOCM_RI) - ret |= FTDI_RI; - if (flags & CHR_TIOCM_CAR) - ret |= FTDI_RLSD; - - return ret; -} - -static void usb_serial_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) -{ - USBSerialState *s = (USBSerialState *)dev; - int ret; - - DPRINTF("got control %x, value %x\n",request, value); - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - switch (request) { - case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: - break; - - /* Class specific requests. */ - case DeviceOutVendor | FTDI_RESET: - switch (value) { - case FTDI_RESET_SIO: - usb_serial_reset(s); - break; - case FTDI_RESET_RX: - s->recv_ptr = 0; - s->recv_used = 0; - /* TODO: purge from char device */ - break; - case FTDI_RESET_TX: - /* TODO: purge from char device */ - break; - } - break; - case DeviceOutVendor | FTDI_SET_MDM_CTRL: - { - static int flags; - qemu_chr_fe_ioctl(s->cs,CHR_IOCTL_SERIAL_GET_TIOCM, &flags); - if (value & FTDI_SET_RTS) { - if (value & FTDI_RTS) - flags |= CHR_TIOCM_RTS; - else - flags &= ~CHR_TIOCM_RTS; - } - if (value & FTDI_SET_DTR) { - if (value & FTDI_DTR) - flags |= CHR_TIOCM_DTR; - else - flags &= ~CHR_TIOCM_DTR; - } - qemu_chr_fe_ioctl(s->cs,CHR_IOCTL_SERIAL_SET_TIOCM, &flags); - break; - } - case DeviceOutVendor | FTDI_SET_FLOW_CTRL: - /* TODO: ioctl */ - break; - case DeviceOutVendor | FTDI_SET_BAUD: { - static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 }; - int subdivisor8 = subdivisors8[((value & 0xc000) >> 14) - | ((index & 1) << 2)]; - int divisor = value & 0x3fff; - - /* chip special cases */ - if (divisor == 1 && subdivisor8 == 0) - subdivisor8 = 4; - if (divisor == 0 && subdivisor8 == 0) - divisor = 1; - - s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8); - qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); - break; - } - case DeviceOutVendor | FTDI_SET_DATA: - switch (value & FTDI_PARITY) { - case 0: - s->params.parity = 'N'; - break; - case FTDI_ODD: - s->params.parity = 'O'; - break; - case FTDI_EVEN: - s->params.parity = 'E'; - break; - default: - DPRINTF("unsupported parity %d\n", value & FTDI_PARITY); - goto fail; - } - switch (value & FTDI_STOP) { - case FTDI_STOP1: - s->params.stop_bits = 1; - break; - case FTDI_STOP2: - s->params.stop_bits = 2; - break; - default: - DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP); - goto fail; - } - qemu_chr_fe_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params); - /* TODO: TX ON/OFF */ - break; - case DeviceInVendor | FTDI_GET_MDM_ST: - data[0] = usb_get_modem_lines(s) | 1; - data[1] = 0; - p->actual_length = 2; - break; - case DeviceOutVendor | FTDI_SET_EVENT_CHR: - /* TODO: handle it */ - s->event_chr = value; - break; - case DeviceOutVendor | FTDI_SET_ERROR_CHR: - /* TODO: handle it */ - s->error_chr = value; - break; - case DeviceOutVendor | FTDI_SET_LATENCY: - s->latency = value; - break; - case DeviceInVendor | FTDI_GET_LATENCY: - data[0] = s->latency; - p->actual_length = 1; - break; - default: - fail: - DPRINTF("got unsupported/bogus control %x, value %x\n", request, value); - p->status = USB_RET_STALL; - break; - } -} - -static void usb_serial_handle_data(USBDevice *dev, USBPacket *p) -{ - USBSerialState *s = (USBSerialState *)dev; - uint8_t devep = p->ep->nr; - struct iovec *iov; - uint8_t header[2]; - int i, first_len, len; - - switch (p->pid) { - case USB_TOKEN_OUT: - if (devep != 2) - goto fail; - for (i = 0; i < p->iov.niov; i++) { - iov = p->iov.iov + i; - qemu_chr_fe_write(s->cs, iov->iov_base, iov->iov_len); - } - p->actual_length = p->iov.size; - break; - - case USB_TOKEN_IN: - if (devep != 1) - goto fail; - first_len = RECV_BUF - s->recv_ptr; - len = p->iov.size; - if (len <= 2) { - p->status = USB_RET_NAK; - break; - } - header[0] = usb_get_modem_lines(s) | 1; - /* We do not have the uart details */ - /* handle serial break */ - if (s->event_trigger && s->event_trigger & FTDI_BI) { - s->event_trigger &= ~FTDI_BI; - header[1] = FTDI_BI; - usb_packet_copy(p, header, 2); - break; - } else { - header[1] = 0; - } - len -= 2; - if (len > s->recv_used) - len = s->recv_used; - if (!len) { - p->status = USB_RET_NAK; - break; - } - if (first_len > len) - first_len = len; - usb_packet_copy(p, header, 2); - usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len); - if (len > first_len) - usb_packet_copy(p, s->recv_buf, len - first_len); - s->recv_used -= len; - s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; - break; - - default: - DPRINTF("Bad token\n"); - fail: - p->status = USB_RET_STALL; - break; - } -} - -static int usb_serial_can_read(void *opaque) -{ - USBSerialState *s = opaque; - - if (!s->dev.attached) { - return 0; - } - return RECV_BUF - s->recv_used; -} - -static void usb_serial_read(void *opaque, const uint8_t *buf, int size) -{ - USBSerialState *s = opaque; - int first_size, start; - - /* room in the buffer? */ - if (size > (RECV_BUF - s->recv_used)) - size = RECV_BUF - s->recv_used; - - start = s->recv_ptr + s->recv_used; - if (start < RECV_BUF) { - /* copy data to end of buffer */ - first_size = RECV_BUF - start; - if (first_size > size) - first_size = size; - - memcpy(s->recv_buf + start, buf, first_size); - - /* wrap around to front if needed */ - if (size > first_size) - memcpy(s->recv_buf, buf + first_size, size - first_size); - } else { - start -= RECV_BUF; - memcpy(s->recv_buf + start, buf, size); - } - s->recv_used += size; -} - -static void usb_serial_event(void *opaque, int event) -{ - USBSerialState *s = opaque; - - switch (event) { - case CHR_EVENT_BREAK: - s->event_trigger |= FTDI_BI; - break; - case CHR_EVENT_FOCUS: - break; - case CHR_EVENT_OPENED: - if (!s->dev.attached) { - usb_device_attach(&s->dev, &error_abort); - } - break; - case CHR_EVENT_CLOSED: - if (s->dev.attached) { - usb_device_detach(&s->dev); - } - break; - } -} - -static void usb_serial_realize(USBDevice *dev, Error **errp) -{ - USBSerialState *s = USB_SERIAL_DEV(dev); - Error *local_err = NULL; - - usb_desc_create_serial(dev); - usb_desc_init(dev); - dev->auto_attach = 0; - - if (!s->cs) { - error_setg(errp, "Property chardev is required"); - return; - } - - usb_check_attach(dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - qemu_chr_add_handlers(s->cs, usb_serial_can_read, usb_serial_read, - usb_serial_event, s); - usb_serial_handle_reset(dev); - - if (s->cs->be_open && !dev->attached) { - usb_device_attach(dev, &error_abort); - } -} - -static USBDevice *usb_serial_init(USBBus *bus, const char *filename) -{ - USBDevice *dev; - CharDriverState *cdrv; - uint32_t vendorid = 0, productid = 0; - char label[32]; - static int index; - - while (*filename && *filename != ':') { - const char *p; - char *e; - if (strstart(filename, "vendorid=", &p)) { - vendorid = strtol(p, &e, 16); - if (e == p || (*e && *e != ',' && *e != ':')) { - error_report("bogus vendor ID %s", p); - return NULL; - } - filename = e; - } else if (strstart(filename, "productid=", &p)) { - productid = strtol(p, &e, 16); - if (e == p || (*e && *e != ',' && *e != ':')) { - error_report("bogus product ID %s", p); - return NULL; - } - filename = e; - } else { - error_report("unrecognized serial USB option %s", filename); - return NULL; - } - while(*filename == ',') - filename++; - } - if (!*filename) { - error_report("character device specification needed"); - return NULL; - } - filename++; - - snprintf(label, sizeof(label), "usbserial%d", index++); - cdrv = qemu_chr_new(label, filename, NULL); - if (!cdrv) - return NULL; - - dev = usb_create(bus, "usb-serial"); - qdev_prop_set_chr(&dev->qdev, "chardev", cdrv); - if (vendorid) - qdev_prop_set_uint16(&dev->qdev, "vendorid", vendorid); - if (productid) - qdev_prop_set_uint16(&dev->qdev, "productid", productid); - return dev; -} - -static USBDevice *usb_braille_init(USBBus *bus, const char *unused) -{ - USBDevice *dev; - CharDriverState *cdrv; - - cdrv = qemu_chr_new("braille", "braille", NULL); - if (!cdrv) - return NULL; - - dev = usb_create(bus, "usb-braille"); - qdev_prop_set_chr(&dev->qdev, "chardev", cdrv); - return dev; -} - -static const VMStateDescription vmstate_usb_serial = { - .name = "usb-serial", - .unmigratable = 1, -}; - -static Property serial_properties[] = { - DEFINE_PROP_CHR("chardev", USBSerialState, cs), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_serial_dev_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_serial_realize; - uc->handle_reset = usb_serial_handle_reset; - uc->handle_control = usb_serial_handle_control; - uc->handle_data = usb_serial_handle_data; - dc->vmsd = &vmstate_usb_serial; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo usb_serial_dev_type_info = { - .name = TYPE_USB_SERIAL, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBSerialState), - .abstract = true, - .class_init = usb_serial_dev_class_init, -}; - -static void usb_serial_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->product_desc = "QEMU USB Serial"; - uc->usb_desc = &desc_serial; - dc->props = serial_properties; -} - -static const TypeInfo serial_info = { - .name = "usb-serial", - .parent = TYPE_USB_SERIAL, - .class_init = usb_serial_class_initfn, -}; - -static Property braille_properties[] = { - DEFINE_PROP_CHR("chardev", USBSerialState, cs), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_braille_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->product_desc = "QEMU USB Braille"; - uc->usb_desc = &desc_braille; - dc->props = braille_properties; -} - -static const TypeInfo braille_info = { - .name = "usb-braille", - .parent = TYPE_USB_SERIAL, - .class_init = usb_braille_class_initfn, -}; - -static void usb_serial_register_types(void) -{ - type_register_static(&usb_serial_dev_type_info); - type_register_static(&serial_info); - usb_legacy_register("usb-serial", "serial", usb_serial_init); - type_register_static(&braille_info); - usb_legacy_register("usb-braille", "braille", usb_braille_init); -} - -type_init(usb_serial_register_types) diff --git a/qemu/hw/usb/dev-smartcard-reader.c b/qemu/hw/usb/dev-smartcard-reader.c deleted file mode 100644 index af4b85135..000000000 --- a/qemu/hw/usb/dev-smartcard-reader.c +++ /dev/null @@ -1,1507 +0,0 @@ -/* - * Copyright (C) 2011 Red Hat, Inc. - * - * CCID Device emulation - * - * Written by Alon Levy, with contributions from Robert Relyea. - * - * Based on usb-serial.c, see its copyright and attributions below. - * - * This work is licensed under the terms of the GNU GPL, version 2.1 or later. - * See the COPYING file in the top-level directory. - * ------- (original copyright & attribution for usb-serial.c below) -------- - * Copyright (c) 2006 CodeSourcery. - * Copyright (c) 2008 Samuel Thibault - * Written by Paul Brook, reused for FTDI by Samuel Thibault, - */ - -/* - * References: - * - * CCID Specification Revision 1.1 April 22nd 2005 - * "Universal Serial Bus, Device Class: Smart Card" - * Specification for Integrated Circuit(s) Cards Interface Devices - * - * Endianness note: from the spec (1.3) - * "Fields that are larger than a byte are stored in little endian" - * - * KNOWN BUGS - * 1. remove/insert can sometimes result in removed state instead of inserted. - * This is a result of the following: - * symptom: dmesg shows ERMOTEIO (-121), pcscd shows -99. This can happen - * when a short packet is sent, as seen in uhci-usb.c, resulting from a urb - * from the guest requesting SPD and us returning a smaller packet. - * 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" -#include "hw/usb/desc.h" - -#include "ccid.h" - -#define DPRINTF(s, lvl, fmt, ...) \ -do { \ - if (lvl <= s->debug) { \ - printf("usb-ccid: " fmt , ## __VA_ARGS__); \ - } \ -} while (0) - -#define D_WARN 1 -#define D_INFO 2 -#define D_MORE_INFO 3 -#define D_VERBOSE 4 - -#define CCID_DEV_NAME "usb-ccid" -#define USB_CCID_DEV(obj) OBJECT_CHECK(USBCCIDState, (obj), CCID_DEV_NAME) -/* - * The two options for variable sized buffers: - * make them constant size, for large enough constant, - * or handle the migration complexity - VMState doesn't handle this case. - * sizes are expected never to be exceeded, unless guest misbehaves. - */ -#define BULK_OUT_DATA_SIZE 65536 -#define PENDING_ANSWERS_NUM 128 - -#define BULK_IN_BUF_SIZE 384 -#define BULK_IN_PENDING_NUM 8 - -#define CCID_MAX_PACKET_SIZE 64 - -#define CCID_CONTROL_ABORT 0x1 -#define CCID_CONTROL_GET_CLOCK_FREQUENCIES 0x2 -#define CCID_CONTROL_GET_DATA_RATES 0x3 - -#define CCID_PRODUCT_DESCRIPTION "QEMU USB CCID" -#define CCID_VENDOR_DESCRIPTION "QEMU" -#define CCID_INTERFACE_NAME "CCID Interface" -#define CCID_SERIAL_NUMBER_STRING "1" -/* - * Using Gemplus Vendor and Product id - * Effect on various drivers: - * usbccid.sys (winxp, others untested) is a class driver so it doesn't care. - * linux has a number of class drivers, but openct filters based on - * vendor/product (/etc/openct.conf under fedora), hence Gemplus. - */ -#define CCID_VENDOR_ID 0x08e6 -#define CCID_PRODUCT_ID 0x4433 -#define CCID_DEVICE_VERSION 0x0000 - -/* - * BULK_OUT messages from PC to Reader - * Defined in CCID Rev 1.1 6.1 (page 26) - */ -#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn 0x62 -#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff 0x63 -#define CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus 0x65 -#define CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock 0x6f -#define CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters 0x6c -#define CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters 0x6d -#define CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters 0x61 -#define CCID_MESSAGE_TYPE_PC_to_RDR_Escape 0x6b -#define CCID_MESSAGE_TYPE_PC_to_RDR_IccClock 0x6e -#define CCID_MESSAGE_TYPE_PC_to_RDR_T0APDU 0x6a -#define CCID_MESSAGE_TYPE_PC_to_RDR_Secure 0x69 -#define CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical 0x71 -#define CCID_MESSAGE_TYPE_PC_to_RDR_Abort 0x72 -#define CCID_MESSAGE_TYPE_PC_to_RDR_SetDataRateAndClockFrequency 0x73 - -/* - * BULK_IN messages from Reader to PC - * Defined in CCID Rev 1.1 6.2 (page 48) - */ -#define CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock 0x80 -#define CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus 0x81 -#define CCID_MESSAGE_TYPE_RDR_to_PC_Parameters 0x82 -#define CCID_MESSAGE_TYPE_RDR_to_PC_Escape 0x83 -#define CCID_MESSAGE_TYPE_RDR_to_PC_DataRateAndClockFrequency 0x84 - -/* - * INTERRUPT_IN messages from Reader to PC - * Defined in CCID Rev 1.1 6.3 (page 56) - */ -#define CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange 0x50 -#define CCID_MESSAGE_TYPE_RDR_to_PC_HardwareError 0x51 - -/* - * Endpoints for CCID - addresses are up to us to decide. - * To support slot insertion and removal we must have an interrupt in ep - * in addition we need a bulk in and bulk out ep - * 5.2, page 20 - */ -#define CCID_INT_IN_EP 1 -#define CCID_BULK_IN_EP 2 -#define CCID_BULK_OUT_EP 3 - -/* bmSlotICCState masks */ -#define SLOT_0_STATE_MASK 1 -#define SLOT_0_CHANGED_MASK 2 - -/* Status codes that go in bStatus (see 6.2.6) */ -enum { - ICC_STATUS_PRESENT_ACTIVE = 0, - ICC_STATUS_PRESENT_INACTIVE, - ICC_STATUS_NOT_PRESENT -}; - -enum { - COMMAND_STATUS_NO_ERROR = 0, - COMMAND_STATUS_FAILED, - COMMAND_STATUS_TIME_EXTENSION_REQUIRED -}; - -/* Error codes that go in bError (see 6.2.6) */ -enum { - ERROR_CMD_NOT_SUPPORTED = 0, - ERROR_CMD_ABORTED = -1, - ERROR_ICC_MUTE = -2, - ERROR_XFR_PARITY_ERROR = -3, - ERROR_XFR_OVERRUN = -4, - ERROR_HW_ERROR = -5, -}; - -/* 6.2.6 RDR_to_PC_SlotStatus definitions */ -enum { - CLOCK_STATUS_RUNNING = 0, - /* - * 0 - Clock Running, 1 - Clock stopped in State L, 2 - H, - * 3 - unknown state. rest are RFU - */ -}; - -typedef struct QEMU_PACKED CCID_Header { - uint8_t bMessageType; - uint32_t dwLength; - uint8_t bSlot; - uint8_t bSeq; -} CCID_Header; - -typedef struct QEMU_PACKED CCID_BULK_IN { - CCID_Header hdr; - uint8_t bStatus; /* Only used in BULK_IN */ - uint8_t bError; /* Only used in BULK_IN */ -} CCID_BULK_IN; - -typedef struct QEMU_PACKED CCID_SlotStatus { - CCID_BULK_IN b; - uint8_t bClockStatus; -} CCID_SlotStatus; - -typedef struct QEMU_PACKED CCID_T0ProtocolDataStructure { - uint8_t bmFindexDindex; - uint8_t bmTCCKST0; - uint8_t bGuardTimeT0; - uint8_t bWaitingIntegerT0; - uint8_t bClockStop; -} CCID_T0ProtocolDataStructure; - -typedef struct QEMU_PACKED CCID_T1ProtocolDataStructure { - uint8_t bmFindexDindex; - uint8_t bmTCCKST1; - uint8_t bGuardTimeT1; - uint8_t bWaitingIntegerT1; - uint8_t bClockStop; - uint8_t bIFSC; - uint8_t bNadValue; -} CCID_T1ProtocolDataStructure; - -typedef union CCID_ProtocolDataStructure { - CCID_T0ProtocolDataStructure t0; - CCID_T1ProtocolDataStructure t1; - uint8_t data[7]; /* must be = max(sizeof(t0), sizeof(t1)) */ -} CCID_ProtocolDataStructure; - -typedef struct QEMU_PACKED CCID_Parameter { - CCID_BULK_IN b; - uint8_t bProtocolNum; - CCID_ProtocolDataStructure abProtocolDataStructure; -} CCID_Parameter; - -typedef struct QEMU_PACKED CCID_DataBlock { - CCID_BULK_IN b; - uint8_t bChainParameter; - uint8_t abData[0]; -} CCID_DataBlock; - -/* 6.1.4 PC_to_RDR_XfrBlock */ -typedef struct QEMU_PACKED CCID_XferBlock { - CCID_Header hdr; - uint8_t bBWI; /* Block Waiting Timeout */ - uint16_t wLevelParameter; /* XXX currently unused */ - uint8_t abData[0]; -} CCID_XferBlock; - -typedef struct QEMU_PACKED CCID_IccPowerOn { - CCID_Header hdr; - uint8_t bPowerSelect; - uint16_t abRFU; -} CCID_IccPowerOn; - -typedef struct QEMU_PACKED CCID_IccPowerOff { - CCID_Header hdr; - uint16_t abRFU; -} CCID_IccPowerOff; - -typedef struct QEMU_PACKED CCID_SetParameters { - CCID_Header hdr; - uint8_t bProtocolNum; - uint16_t abRFU; - CCID_ProtocolDataStructure abProtocolDataStructure; -} CCID_SetParameters; - -typedef struct CCID_Notify_Slot_Change { - uint8_t bMessageType; /* CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange */ - uint8_t bmSlotICCState; -} CCID_Notify_Slot_Change; - -/* used for DataBlock response to XferBlock */ -typedef struct Answer { - uint8_t slot; - uint8_t seq; -} Answer; - -/* pending BULK_IN messages */ -typedef struct BulkIn { - uint8_t data[BULK_IN_BUF_SIZE]; - uint32_t len; - uint32_t pos; -} BulkIn; - -enum { - MIGRATION_NONE, - MIGRATION_MIGRATED, -}; - -typedef struct CCIDBus { - BusState qbus; -} CCIDBus; - -/* - * powered - defaults to true, changed by PowerOn/PowerOff messages - */ -typedef struct USBCCIDState { - USBDevice dev; - USBEndpoint *intr; - USBEndpoint *bulk; - CCIDBus bus; - CCIDCardState *card; - BulkIn bulk_in_pending[BULK_IN_PENDING_NUM]; /* circular */ - uint32_t bulk_in_pending_start; - uint32_t bulk_in_pending_end; /* first free */ - uint32_t bulk_in_pending_num; - BulkIn *current_bulk_in; - uint8_t bulk_out_data[BULK_OUT_DATA_SIZE]; - uint32_t bulk_out_pos; - uint64_t last_answer_error; - Answer pending_answers[PENDING_ANSWERS_NUM]; - uint32_t pending_answers_start; - uint32_t pending_answers_end; - uint32_t pending_answers_num; - uint8_t bError; - uint8_t bmCommandStatus; - uint8_t bProtocolNum; - CCID_ProtocolDataStructure abProtocolDataStructure; - uint32_t ulProtocolDataStructureSize; - uint32_t state_vmstate; - uint32_t migration_target_ip; - uint16_t migration_target_port; - uint8_t migration_state; - uint8_t bmSlotICCState; - uint8_t powered; - uint8_t notify_slot_change; - uint8_t debug; -} USBCCIDState; - -/* - * CCID Spec chapter 4: CCID uses a standard device descriptor per Chapter 9, - * "USB Device Framework", section 9.6.1, in the Universal Serial Bus - * Specification. - * - * This device implemented based on the spec and with an Athena Smart Card - * Reader as reference: - * 0dc3:1004 Athena Smartcard Solutions, Inc. - */ - -static const uint8_t qemu_ccid_descriptor[] = { - /* Smart Card Device Class Descriptor */ - 0x36, /* u8 bLength; */ - 0x21, /* u8 bDescriptorType; Functional */ - 0x10, 0x01, /* u16 bcdCCID; CCID Specification Release Number. */ - 0x00, /* - * u8 bMaxSlotIndex; The index of the highest available - * slot on this device. All slots are consecutive starting - * at 00h. - */ - 0x07, /* u8 bVoltageSupport; 01h - 5.0v, 02h - 3.0, 03 - 1.8 */ - - 0x00, 0x00, /* u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/ - 0x01, 0x00, /* PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */ - /* u32 dwDefaultClock; in kHZ (0x0fa0 is 4 MHz) */ - 0xa0, 0x0f, 0x00, 0x00, - /* u32 dwMaximumClock; */ - 0x00, 0x00, 0x01, 0x00, - 0x00, /* u8 bNumClockSupported; * - * 0 means just the default and max. */ - /* u32 dwDataRate ;bps. 9600 == 00002580h */ - 0x80, 0x25, 0x00, 0x00, - /* u32 dwMaxDataRate ; 11520 bps == 0001C200h */ - 0x00, 0xC2, 0x01, 0x00, - 0x00, /* u8 bNumDataRatesSupported; 00 means all rates between - * default and max */ - /* u32 dwMaxIFSD; * - * maximum IFSD supported by CCID for protocol * - * T=1 (Maximum seen from various cards) */ - 0xfe, 0x00, 0x00, 0x00, - /* u32 dwSyncProtocols; 1 - 2-wire, 2 - 3-wire, 4 - I2C */ - 0x00, 0x00, 0x00, 0x00, - /* u32 dwMechanical; 0 - no special characteristics. */ - 0x00, 0x00, 0x00, 0x00, - /* - * u32 dwFeatures; - * 0 - No special characteristics - * + 2 Automatic parameter configuration based on ATR data - * + 4 Automatic activation of ICC on inserting - * + 8 Automatic ICC voltage selection - * + 10 Automatic ICC clock frequency change - * + 20 Automatic baud rate change - * + 40 Automatic parameters negotiation made by the CCID - * + 80 automatic PPS made by the CCID - * 100 CCID can set ICC in clock stop mode - * 200 NAD value other then 00 accepted (T=1 protocol) - * + 400 Automatic IFSD exchange as first exchange (T=1) - * One of the following only: - * + 10000 TPDU level exchanges with CCID - * 20000 Short APDU level exchange with CCID - * 40000 Short and Extended APDU level exchange with CCID - * - * 100000 USB Wake up signaling supported on card - * insertion and removal. Must set bit 5 in bmAttributes - * in Configuration descriptor if 100000 is set. - */ - 0xfe, 0x04, 0x01, 0x00, - /* - * u32 dwMaxCCIDMessageLength; For extended APDU in - * [261 + 10 , 65544 + 10]. Otherwise the minimum is - * wMaxPacketSize of the Bulk-OUT endpoint - */ - 0x12, 0x00, 0x01, 0x00, - 0xFF, /* - * u8 bClassGetResponse; Significant only for CCID that - * offers an APDU level for exchanges. Indicates the - * default class value used by the CCID when it sends a - * Get Response command to perform the transportation of - * an APDU by T=0 protocol - * FFh indicates that the CCID echos the class of the APDU. - */ - 0xFF, /* - * u8 bClassEnvelope; EAPDU only. Envelope command for - * T=0 - */ - 0x00, 0x00, /* - * u16 wLcdLayout; XXYY Number of lines (XX) and chars per - * line for LCD display used for PIN entry. 0000 - no LCD - */ - 0x01, /* - * u8 bPINSupport; 01h PIN Verification, - * 02h PIN Modification - */ - 0x01, /* u8 bMaxCCIDBusySlots; */ -}; - -enum { - STR_MANUFACTURER = 1, - STR_PRODUCT, - STR_SERIALNUMBER, - STR_INTERFACE, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = "QEMU", - [STR_PRODUCT] = "QEMU USB CCID", - [STR_SERIALNUMBER] = "1", - [STR_INTERFACE] = "CCID Interface", -}; - -static const USBDescIface desc_iface0 = { - .bInterfaceNumber = 0, - .bNumEndpoints = 3, - .bInterfaceClass = USB_CLASS_CSCID, - .bInterfaceSubClass = USB_SUBCLASS_UNDEFINED, - .bInterfaceProtocol = 0x00, - .iInterface = STR_INTERFACE, - .ndesc = 1, - .descs = (USBDescOther[]) { - { - /* smartcard descriptor */ - .data = qemu_ccid_descriptor, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | CCID_INT_IN_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .bInterval = 255, - .wMaxPacketSize = 64, - },{ - .bEndpointAddress = USB_DIR_IN | CCID_BULK_IN_EP, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 64, - },{ - .bEndpointAddress = USB_DIR_OUT | CCID_BULK_OUT_EP, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 64, - }, - } -}; - -static const USBDescDevice desc_device = { - .bcdUSB = 0x0110, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER | - USB_CFG_ATT_WAKEUP, - .bMaxPower = 50, - .nif = 1, - .ifs = &desc_iface0, - }, - }, -}; - -static const USBDesc desc_ccid = { - .id = { - .idVendor = CCID_VENDOR_ID, - .idProduct = CCID_PRODUCT_ID, - .bcdDevice = CCID_DEVICE_VERSION, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device, - .str = desc_strings, -}; - -static const uint8_t *ccid_card_get_atr(CCIDCardState *card, uint32_t *len) -{ - CCIDCardClass *cc = CCID_CARD_GET_CLASS(card); - - if (cc->get_atr) { - return cc->get_atr(card, len); - } - return NULL; -} - -static void ccid_card_apdu_from_guest(CCIDCardState *card, - const uint8_t *apdu, - uint32_t len) -{ - CCIDCardClass *cc = CCID_CARD_GET_CLASS(card); - - if (cc->apdu_from_guest) { - cc->apdu_from_guest(card, apdu, len); - } -} - -static int ccid_card_exitfn(CCIDCardState *card) -{ - CCIDCardClass *cc = CCID_CARD_GET_CLASS(card); - - if (cc->exitfn) { - return cc->exitfn(card); - } - return 0; -} - -static int ccid_card_initfn(CCIDCardState *card) -{ - CCIDCardClass *cc = CCID_CARD_GET_CLASS(card); - - if (cc->initfn) { - return cc->initfn(card); - } - return 0; -} - -static bool ccid_has_pending_answers(USBCCIDState *s) -{ - return s->pending_answers_num > 0; -} - -static void ccid_clear_pending_answers(USBCCIDState *s) -{ - s->pending_answers_num = 0; - s->pending_answers_start = 0; - s->pending_answers_end = 0; -} - -static void ccid_print_pending_answers(USBCCIDState *s) -{ - Answer *answer; - int i, count; - - DPRINTF(s, D_VERBOSE, "usb-ccid: pending answers:"); - if (!ccid_has_pending_answers(s)) { - DPRINTF(s, D_VERBOSE, " empty\n"); - return; - } - for (i = s->pending_answers_start, count = s->pending_answers_num ; - count > 0; count--, i++) { - answer = &s->pending_answers[i % PENDING_ANSWERS_NUM]; - if (count == 1) { - DPRINTF(s, D_VERBOSE, "%d:%d\n", answer->slot, answer->seq); - } else { - DPRINTF(s, D_VERBOSE, "%d:%d,", answer->slot, answer->seq); - } - } -} - -static void ccid_add_pending_answer(USBCCIDState *s, CCID_Header *hdr) -{ - Answer *answer; - - assert(s->pending_answers_num < PENDING_ANSWERS_NUM); - s->pending_answers_num++; - answer = - &s->pending_answers[(s->pending_answers_end++) % PENDING_ANSWERS_NUM]; - answer->slot = hdr->bSlot; - answer->seq = hdr->bSeq; - ccid_print_pending_answers(s); -} - -static void ccid_remove_pending_answer(USBCCIDState *s, - uint8_t *slot, uint8_t *seq) -{ - Answer *answer; - - assert(s->pending_answers_num > 0); - s->pending_answers_num--; - answer = - &s->pending_answers[(s->pending_answers_start++) % PENDING_ANSWERS_NUM]; - *slot = answer->slot; - *seq = answer->seq; - ccid_print_pending_answers(s); -} - -static void ccid_bulk_in_clear(USBCCIDState *s) -{ - s->bulk_in_pending_start = 0; - s->bulk_in_pending_end = 0; - s->bulk_in_pending_num = 0; -} - -static void ccid_bulk_in_release(USBCCIDState *s) -{ - assert(s->current_bulk_in != NULL); - s->current_bulk_in->pos = 0; - s->current_bulk_in = NULL; -} - -static void ccid_bulk_in_get(USBCCIDState *s) -{ - if (s->current_bulk_in != NULL || s->bulk_in_pending_num == 0) { - return; - } - assert(s->bulk_in_pending_num > 0); - s->bulk_in_pending_num--; - s->current_bulk_in = - &s->bulk_in_pending[(s->bulk_in_pending_start++) % BULK_IN_PENDING_NUM]; -} - -static void *ccid_reserve_recv_buf(USBCCIDState *s, uint16_t len) -{ - BulkIn *bulk_in; - - DPRINTF(s, D_VERBOSE, "%s: QUEUE: reserve %d bytes\n", __func__, len); - - /* look for an existing element */ - if (len > BULK_IN_BUF_SIZE) { - DPRINTF(s, D_WARN, "usb-ccid.c: %s: len larger then max (%d>%d). " - "discarding message.\n", - __func__, len, BULK_IN_BUF_SIZE); - return NULL; - } - if (s->bulk_in_pending_num >= BULK_IN_PENDING_NUM) { - DPRINTF(s, D_WARN, "usb-ccid.c: %s: No free bulk_in buffers. " - "discarding message.\n", __func__); - return NULL; - } - bulk_in = - &s->bulk_in_pending[(s->bulk_in_pending_end++) % BULK_IN_PENDING_NUM]; - s->bulk_in_pending_num++; - bulk_in->len = len; - return bulk_in->data; -} - -static void ccid_reset(USBCCIDState *s) -{ - ccid_bulk_in_clear(s); - ccid_clear_pending_answers(s); -} - -static void ccid_detach(USBCCIDState *s) -{ - ccid_reset(s); -} - -static void ccid_handle_reset(USBDevice *dev) -{ - USBCCIDState *s = USB_CCID_DEV(dev); - - DPRINTF(s, 1, "Reset\n"); - - ccid_reset(s); -} - -static const char *ccid_control_to_str(USBCCIDState *s, int request) -{ - switch (request) { - /* generic - should be factored out if there are other debugees */ - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - return "(generic) set address"; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - return "(generic) get descriptor"; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - return "(generic) get configuration"; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - return "(generic) set configuration"; - case DeviceRequest | USB_REQ_GET_STATUS: - return "(generic) get status"; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - return "(generic) clear feature"; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - return "(generic) set_feature"; - case InterfaceRequest | USB_REQ_GET_INTERFACE: - return "(generic) get interface"; - case InterfaceOutRequest | USB_REQ_SET_INTERFACE: - return "(generic) set interface"; - /* class requests */ - case ClassInterfaceOutRequest | CCID_CONTROL_ABORT: - return "ABORT"; - case ClassInterfaceRequest | CCID_CONTROL_GET_CLOCK_FREQUENCIES: - return "GET_CLOCK_FREQUENCIES"; - case ClassInterfaceRequest | CCID_CONTROL_GET_DATA_RATES: - return "GET_DATA_RATES"; - } - return "unknown"; -} - -static void ccid_handle_control(USBDevice *dev, USBPacket *p, int request, - int value, int index, int length, uint8_t *data) -{ - USBCCIDState *s = USB_CCID_DEV(dev); - int ret; - - DPRINTF(s, 1, "%s: got control %s (%x), value %x\n", __func__, - ccid_control_to_str(s, request), request, value); - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - switch (request) { - /* Class specific requests. */ - case ClassInterfaceOutRequest | CCID_CONTROL_ABORT: - DPRINTF(s, 1, "ccid_control abort UNIMPLEMENTED\n"); - p->status = USB_RET_STALL; - break; - case ClassInterfaceRequest | CCID_CONTROL_GET_CLOCK_FREQUENCIES: - DPRINTF(s, 1, "ccid_control get clock frequencies UNIMPLEMENTED\n"); - p->status = USB_RET_STALL; - break; - case ClassInterfaceRequest | CCID_CONTROL_GET_DATA_RATES: - DPRINTF(s, 1, "ccid_control get data rates UNIMPLEMENTED\n"); - p->status = USB_RET_STALL; - break; - default: - DPRINTF(s, 1, "got unsupported/bogus control %x, value %x\n", - request, value); - p->status = USB_RET_STALL; - break; - } -} - -static bool ccid_card_inserted(USBCCIDState *s) -{ - return s->bmSlotICCState & SLOT_0_STATE_MASK; -} - -static uint8_t ccid_card_status(USBCCIDState *s) -{ - return ccid_card_inserted(s) - ? (s->powered ? - ICC_STATUS_PRESENT_ACTIVE - : ICC_STATUS_PRESENT_INACTIVE - ) - : ICC_STATUS_NOT_PRESENT; -} - -static uint8_t ccid_calc_status(USBCCIDState *s) -{ - /* - * page 55, 6.2.6, calculation of bStatus from bmICCStatus and - * bmCommandStatus - */ - uint8_t ret = ccid_card_status(s) | (s->bmCommandStatus << 6); - DPRINTF(s, D_VERBOSE, "%s: status = %d\n", __func__, ret); - return ret; -} - -static void ccid_reset_error_status(USBCCIDState *s) -{ - s->bError = ERROR_CMD_NOT_SUPPORTED; - s->bmCommandStatus = COMMAND_STATUS_NO_ERROR; -} - -static void ccid_write_slot_status(USBCCIDState *s, CCID_Header *recv) -{ - CCID_SlotStatus *h = ccid_reserve_recv_buf(s, sizeof(CCID_SlotStatus)); - if (h == NULL) { - return; - } - h->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus; - h->b.hdr.dwLength = 0; - h->b.hdr.bSlot = recv->bSlot; - h->b.hdr.bSeq = recv->bSeq; - h->b.bStatus = ccid_calc_status(s); - h->b.bError = s->bError; - h->bClockStatus = CLOCK_STATUS_RUNNING; - ccid_reset_error_status(s); - usb_wakeup(s->bulk, 0); -} - -static void ccid_write_parameters(USBCCIDState *s, CCID_Header *recv) -{ - CCID_Parameter *h; - uint32_t len = s->ulProtocolDataStructureSize; - - h = ccid_reserve_recv_buf(s, sizeof(CCID_Parameter) + len); - if (h == NULL) { - return; - } - h->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_Parameters; - h->b.hdr.dwLength = 0; - h->b.hdr.bSlot = recv->bSlot; - h->b.hdr.bSeq = recv->bSeq; - h->b.bStatus = ccid_calc_status(s); - h->b.bError = s->bError; - h->bProtocolNum = s->bProtocolNum; - h->abProtocolDataStructure = s->abProtocolDataStructure; - ccid_reset_error_status(s); - usb_wakeup(s->bulk, 0); -} - -static void ccid_write_data_block(USBCCIDState *s, uint8_t slot, uint8_t seq, - const uint8_t *data, uint32_t len) -{ - CCID_DataBlock *p = ccid_reserve_recv_buf(s, sizeof(*p) + len); - - if (p == NULL) { - return; - } - p->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock; - p->b.hdr.dwLength = cpu_to_le32(len); - p->b.hdr.bSlot = slot; - p->b.hdr.bSeq = seq; - p->b.bStatus = ccid_calc_status(s); - p->b.bError = s->bError; - if (p->b.bError) { - DPRINTF(s, D_VERBOSE, "error %d\n", p->b.bError); - } - memcpy(p->abData, data, len); - ccid_reset_error_status(s); - usb_wakeup(s->bulk, 0); -} - -static void ccid_report_error_failed(USBCCIDState *s, uint8_t error) -{ - s->bmCommandStatus = COMMAND_STATUS_FAILED; - s->bError = error; -} - -static void ccid_write_data_block_answer(USBCCIDState *s, - const uint8_t *data, uint32_t len) -{ - uint8_t seq; - uint8_t slot; - - if (!ccid_has_pending_answers(s)) { - DPRINTF(s, D_WARN, "error: no pending answer to return to guest\n"); - ccid_report_error_failed(s, ERROR_ICC_MUTE); - return; - } - ccid_remove_pending_answer(s, &slot, &seq); - ccid_write_data_block(s, slot, seq, data, len); -} - -static uint8_t atr_get_protocol_num(const uint8_t *atr, uint32_t len) -{ - int i; - - if (len < 2 || !(atr[1] & 0x80)) { - /* too short or TD1 not included */ - return 0; /* T=0, default */ - } - i = 1 + !!(atr[1] & 0x10) + !!(atr[1] & 0x20) + !!(atr[1] & 0x40); - i += !!(atr[1] & 0x80); - return atr[i] & 0x0f; -} - -static void ccid_write_data_block_atr(USBCCIDState *s, CCID_Header *recv) -{ - const uint8_t *atr = NULL; - uint32_t len = 0; - uint8_t atr_protocol_num; - CCID_T0ProtocolDataStructure *t0 = &s->abProtocolDataStructure.t0; - CCID_T1ProtocolDataStructure *t1 = &s->abProtocolDataStructure.t1; - - if (s->card) { - atr = ccid_card_get_atr(s->card, &len); - } - atr_protocol_num = atr_get_protocol_num(atr, len); - DPRINTF(s, D_VERBOSE, "%s: atr contains protocol=%d\n", __func__, - atr_protocol_num); - /* set parameters from ATR - see spec page 109 */ - s->bProtocolNum = (atr_protocol_num <= 1 ? atr_protocol_num - : s->bProtocolNum); - switch (atr_protocol_num) { - case 0: - /* TODO: unimplemented ATR T0 parameters */ - t0->bmFindexDindex = 0; - t0->bmTCCKST0 = 0; - t0->bGuardTimeT0 = 0; - t0->bWaitingIntegerT0 = 0; - t0->bClockStop = 0; - break; - case 1: - /* TODO: unimplemented ATR T1 parameters */ - t1->bmFindexDindex = 0; - t1->bmTCCKST1 = 0; - t1->bGuardTimeT1 = 0; - t1->bWaitingIntegerT1 = 0; - t1->bClockStop = 0; - t1->bIFSC = 0; - t1->bNadValue = 0; - break; - default: - DPRINTF(s, D_WARN, "%s: error: unsupported ATR protocol %d\n", - __func__, atr_protocol_num); - } - ccid_write_data_block(s, recv->bSlot, recv->bSeq, atr, len); -} - -static void ccid_set_parameters(USBCCIDState *s, CCID_Header *recv) -{ - CCID_SetParameters *ph = (CCID_SetParameters *) recv; - uint32_t protocol_num = ph->bProtocolNum & 3; - - if (protocol_num != 0 && protocol_num != 1) { - ccid_report_error_failed(s, ERROR_CMD_NOT_SUPPORTED); - return; - } - s->bProtocolNum = protocol_num; - s->abProtocolDataStructure = ph->abProtocolDataStructure; -} - -/* - * must be 5 bytes for T=0, 7 bytes for T=1 - * See page 52 - */ -static const CCID_ProtocolDataStructure defaultProtocolDataStructure = { - .t1 = { - .bmFindexDindex = 0x77, - .bmTCCKST1 = 0x00, - .bGuardTimeT1 = 0x00, - .bWaitingIntegerT1 = 0x00, - .bClockStop = 0x00, - .bIFSC = 0xfe, - .bNadValue = 0x00, - } -}; - -static void ccid_reset_parameters(USBCCIDState *s) -{ - s->bProtocolNum = 0; /* T=0 */ - s->abProtocolDataStructure = defaultProtocolDataStructure; -} - -/* NOTE: only a single slot is supported (SLOT_0) */ -static void ccid_on_slot_change(USBCCIDState *s, bool full) -{ - /* RDR_to_PC_NotifySlotChange, 6.3.1 page 56 */ - uint8_t current = s->bmSlotICCState; - if (full) { - s->bmSlotICCState |= SLOT_0_STATE_MASK; - } else { - s->bmSlotICCState &= ~SLOT_0_STATE_MASK; - } - if (current != s->bmSlotICCState) { - s->bmSlotICCState |= SLOT_0_CHANGED_MASK; - } - s->notify_slot_change = true; - usb_wakeup(s->intr, 0); -} - -static void ccid_write_data_block_error( - USBCCIDState *s, uint8_t slot, uint8_t seq) -{ - ccid_write_data_block(s, slot, seq, NULL, 0); -} - -static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock *recv) -{ - uint32_t len; - - if (ccid_card_status(s) != ICC_STATUS_PRESENT_ACTIVE) { - DPRINTF(s, 1, - "usb-ccid: not sending apdu to client, no card connected\n"); - ccid_write_data_block_error(s, recv->hdr.bSlot, recv->hdr.bSeq); - return; - } - len = le32_to_cpu(recv->hdr.dwLength); - DPRINTF(s, 1, "%s: seq %d, len %d\n", __func__, - recv->hdr.bSeq, len); - ccid_add_pending_answer(s, (CCID_Header *)recv); - if (s->card) { - ccid_card_apdu_from_guest(s->card, recv->abData, len); - } else { - DPRINTF(s, D_WARN, "warning: discarded apdu\n"); - } -} - -static const char *ccid_message_type_to_str(uint8_t type) -{ - switch (type) { - case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn: return "IccPowerOn"; - case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff: return "IccPowerOff"; - case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus: return "GetSlotStatus"; - case CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock: return "XfrBlock"; - case CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters: return "GetParameters"; - case CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters: return "ResetParameters"; - case CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters: return "SetParameters"; - case CCID_MESSAGE_TYPE_PC_to_RDR_Escape: return "Escape"; - case CCID_MESSAGE_TYPE_PC_to_RDR_IccClock: return "IccClock"; - case CCID_MESSAGE_TYPE_PC_to_RDR_T0APDU: return "T0APDU"; - case CCID_MESSAGE_TYPE_PC_to_RDR_Secure: return "Secure"; - case CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical: return "Mechanical"; - case CCID_MESSAGE_TYPE_PC_to_RDR_Abort: return "Abort"; - case CCID_MESSAGE_TYPE_PC_to_RDR_SetDataRateAndClockFrequency: - return "SetDataRateAndClockFrequency"; - } - return "unknown"; -} - -static void ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p) -{ - CCID_Header *ccid_header; - - if (p->iov.size + s->bulk_out_pos > BULK_OUT_DATA_SIZE) { - p->status = USB_RET_STALL; - return; - } - ccid_header = (CCID_Header *)s->bulk_out_data; - usb_packet_copy(p, s->bulk_out_data + s->bulk_out_pos, p->iov.size); - s->bulk_out_pos += p->iov.size; - if (p->iov.size == CCID_MAX_PACKET_SIZE) { - DPRINTF(s, D_VERBOSE, - "usb-ccid: bulk_in: expecting more packets (%zd/%d)\n", - p->iov.size, ccid_header->dwLength); - return; - } - if (s->bulk_out_pos < 10) { - DPRINTF(s, 1, - "%s: bad USB_TOKEN_OUT length, should be at least 10 bytes\n", - __func__); - } else { - DPRINTF(s, D_MORE_INFO, "%s %x %s\n", __func__, - ccid_header->bMessageType, - ccid_message_type_to_str(ccid_header->bMessageType)); - switch (ccid_header->bMessageType) { - case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus: - ccid_write_slot_status(s, ccid_header); - break; - case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn: - DPRINTF(s, 1, "%s: PowerOn: %d\n", __func__, - ((CCID_IccPowerOn *)(ccid_header))->bPowerSelect); - s->powered = true; - if (!ccid_card_inserted(s)) { - ccid_report_error_failed(s, ERROR_ICC_MUTE); - } - /* atr is written regardless of error. */ - ccid_write_data_block_atr(s, ccid_header); - break; - case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff: - ccid_reset_error_status(s); - s->powered = false; - ccid_write_slot_status(s, ccid_header); - break; - case CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock: - ccid_on_apdu_from_guest(s, (CCID_XferBlock *)s->bulk_out_data); - break; - case CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters: - ccid_reset_error_status(s); - ccid_set_parameters(s, ccid_header); - ccid_write_parameters(s, ccid_header); - break; - case CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters: - ccid_reset_error_status(s); - ccid_reset_parameters(s); - ccid_write_parameters(s, ccid_header); - break; - case CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters: - ccid_reset_error_status(s); - ccid_write_parameters(s, ccid_header); - break; - case CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical: - ccid_report_error_failed(s, 0); - ccid_write_slot_status(s, ccid_header); - break; - default: - DPRINTF(s, 1, - "handle_data: ERROR: unhandled message type %Xh\n", - ccid_header->bMessageType); - /* - * The caller is expecting the device to respond, tell it we - * don't support the operation. - */ - ccid_report_error_failed(s, ERROR_CMD_NOT_SUPPORTED); - ccid_write_slot_status(s, ccid_header); - break; - } - } - s->bulk_out_pos = 0; -} - -static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p) -{ - int len = 0; - - ccid_bulk_in_get(s); - if (s->current_bulk_in != NULL) { - len = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, - p->iov.size); - usb_packet_copy(p, s->current_bulk_in->data + - s->current_bulk_in->pos, len); - s->current_bulk_in->pos += len; - if (s->current_bulk_in->pos == s->current_bulk_in->len) { - ccid_bulk_in_release(s); - } - } else { - /* return when device has no data - usb 2.0 spec Table 8-4 */ - p->status = USB_RET_NAK; - } - if (len) { - DPRINTF(s, D_MORE_INFO, - "%s: %zd/%d req/act to guest (BULK_IN)\n", - __func__, p->iov.size, len); - } - if (len < p->iov.size) { - DPRINTF(s, 1, - "%s: returning short (EREMOTEIO) %d < %zd\n", - __func__, len, p->iov.size); - } -} - -static void ccid_handle_data(USBDevice *dev, USBPacket *p) -{ - USBCCIDState *s = USB_CCID_DEV(dev); - uint8_t buf[2]; - - switch (p->pid) { - case USB_TOKEN_OUT: - ccid_handle_bulk_out(s, p); - break; - - case USB_TOKEN_IN: - switch (p->ep->nr) { - case CCID_BULK_IN_EP: - ccid_bulk_in_copy_to_guest(s, p); - break; - case CCID_INT_IN_EP: - if (s->notify_slot_change) { - /* page 56, RDR_to_PC_NotifySlotChange */ - buf[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange; - buf[1] = s->bmSlotICCState; - usb_packet_copy(p, buf, 2); - s->notify_slot_change = false; - s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK; - DPRINTF(s, D_INFO, - "handle_data: int_in: notify_slot_change %X, " - "requested len %zd\n", - s->bmSlotICCState, p->iov.size); - } else { - p->status = USB_RET_NAK; - } - break; - default: - DPRINTF(s, 1, "Bad endpoint\n"); - p->status = USB_RET_STALL; - break; - } - break; - default: - DPRINTF(s, 1, "Bad token\n"); - p->status = USB_RET_STALL; - break; - } -} - -static void ccid_handle_destroy(USBDevice *dev) -{ - USBCCIDState *s = USB_CCID_DEV(dev); - - ccid_bulk_in_clear(s); -} - -static void ccid_flush_pending_answers(USBCCIDState *s) -{ - while (ccid_has_pending_answers(s)) { - ccid_write_data_block_answer(s, NULL, 0); - } -} - -static Answer *ccid_peek_next_answer(USBCCIDState *s) -{ - return s->pending_answers_num == 0 - ? NULL - : &s->pending_answers[s->pending_answers_start % PENDING_ANSWERS_NUM]; -} - -static Property ccid_props[] = { - DEFINE_PROP_UINT32("slot", struct CCIDCardState, slot, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -#define TYPE_CCID_BUS "ccid-bus" -#define CCID_BUS(obj) OBJECT_CHECK(CCIDBus, (obj), TYPE_CCID_BUS) - -static const TypeInfo ccid_bus_info = { - .name = TYPE_CCID_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(CCIDBus), -}; - -void ccid_card_send_apdu_to_guest(CCIDCardState *card, - uint8_t *apdu, uint32_t len) -{ - DeviceState *qdev = DEVICE(card); - USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent); - USBCCIDState *s = USB_CCID_DEV(dev); - Answer *answer; - - if (!ccid_has_pending_answers(s)) { - DPRINTF(s, 1, "CCID ERROR: got an APDU without pending answers\n"); - return; - } - s->bmCommandStatus = COMMAND_STATUS_NO_ERROR; - answer = ccid_peek_next_answer(s); - if (answer == NULL) { - DPRINTF(s, D_WARN, "%s: error: unexpected lack of answer\n", __func__); - ccid_report_error_failed(s, ERROR_HW_ERROR); - return; - } - DPRINTF(s, 1, "APDU returned to guest %d (answer seq %d, slot %d)\n", - len, answer->seq, answer->slot); - ccid_write_data_block_answer(s, apdu, len); -} - -void ccid_card_card_removed(CCIDCardState *card) -{ - DeviceState *qdev = DEVICE(card); - USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent); - USBCCIDState *s = USB_CCID_DEV(dev); - - ccid_on_slot_change(s, false); - ccid_flush_pending_answers(s); - ccid_reset(s); -} - -int ccid_card_ccid_attach(CCIDCardState *card) -{ - DeviceState *qdev = DEVICE(card); - USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent); - USBCCIDState *s = USB_CCID_DEV(dev); - - DPRINTF(s, 1, "CCID Attach\n"); - if (s->migration_state == MIGRATION_MIGRATED) { - s->migration_state = MIGRATION_NONE; - } - return 0; -} - -void ccid_card_ccid_detach(CCIDCardState *card) -{ - DeviceState *qdev = DEVICE(card); - USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent); - USBCCIDState *s = USB_CCID_DEV(dev); - - DPRINTF(s, 1, "CCID Detach\n"); - if (ccid_card_inserted(s)) { - ccid_on_slot_change(s, false); - } - ccid_detach(s); -} - -void ccid_card_card_error(CCIDCardState *card, uint64_t error) -{ - DeviceState *qdev = DEVICE(card); - USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent); - USBCCIDState *s = USB_CCID_DEV(dev); - - s->bmCommandStatus = COMMAND_STATUS_FAILED; - s->last_answer_error = error; - DPRINTF(s, 1, "VSC_Error: %" PRIX64 "\n", s->last_answer_error); - /* TODO: these errors should be more verbose and propagated to the guest.*/ - /* - * We flush all pending answers on CardRemove message in ccid-card-passthru, - * so check that first to not trigger abort - */ - if (ccid_has_pending_answers(s)) { - ccid_write_data_block_answer(s, NULL, 0); - } -} - -void ccid_card_card_inserted(CCIDCardState *card) -{ - DeviceState *qdev = DEVICE(card); - USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent); - USBCCIDState *s = USB_CCID_DEV(dev); - - s->bmCommandStatus = COMMAND_STATUS_NO_ERROR; - ccid_flush_pending_answers(s); - ccid_on_slot_change(s, true); -} - -static int ccid_card_exit(DeviceState *qdev) -{ - int ret = 0; - CCIDCardState *card = CCID_CARD(qdev); - USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent); - USBCCIDState *s = USB_CCID_DEV(dev); - - if (ccid_card_inserted(s)) { - ccid_card_card_removed(card); - } - ret = ccid_card_exitfn(card); - s->card = NULL; - return ret; -} - -static int ccid_card_init(DeviceState *qdev) -{ - CCIDCardState *card = CCID_CARD(qdev); - USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent); - USBCCIDState *s = USB_CCID_DEV(dev); - int ret = 0; - - if (card->slot != 0) { - error_report("Warning: usb-ccid supports one slot, can't add %d", - card->slot); - return -1; - } - if (s->card != NULL) { - error_report("Warning: usb-ccid card already full, not adding"); - return -1; - } - ret = ccid_card_initfn(card); - if (ret == 0) { - s->card = card; - } - return ret; -} - -static void ccid_realize(USBDevice *dev, Error **errp) -{ - USBCCIDState *s = USB_CCID_DEV(dev); - - usb_desc_create_serial(dev); - usb_desc_init(dev); - qbus_create_inplace(&s->bus, sizeof(s->bus), TYPE_CCID_BUS, DEVICE(dev), - NULL); - qbus_set_hotplug_handler(BUS(&s->bus), DEVICE(dev), &error_abort); - s->intr = usb_ep_get(dev, USB_TOKEN_IN, CCID_INT_IN_EP); - s->bulk = usb_ep_get(dev, USB_TOKEN_IN, CCID_BULK_IN_EP); - s->card = NULL; - s->migration_state = MIGRATION_NONE; - s->migration_target_ip = 0; - s->migration_target_port = 0; - s->dev.speed = USB_SPEED_FULL; - s->dev.speedmask = USB_SPEED_MASK_FULL; - s->notify_slot_change = false; - s->powered = true; - s->pending_answers_num = 0; - s->last_answer_error = 0; - s->bulk_in_pending_start = 0; - s->bulk_in_pending_end = 0; - s->current_bulk_in = NULL; - ccid_reset_error_status(s); - s->bulk_out_pos = 0; - ccid_reset_parameters(s); - ccid_reset(s); - s->debug = parse_debug_env("QEMU_CCID_DEBUG", D_VERBOSE, s->debug); -} - -static int ccid_post_load(void *opaque, int version_id) -{ - USBCCIDState *s = opaque; - - /* - * This must be done after usb_device_attach, which sets state to ATTACHED, - * while it must be DEFAULT in order to accept packets (like it is after - * reset, but reset will reset our addr and call our reset handler which - * may change state, and we don't want to do that when migrating). - */ - s->dev.state = s->state_vmstate; - return 0; -} - -static void ccid_pre_save(void *opaque) -{ - USBCCIDState *s = opaque; - - s->state_vmstate = s->dev.state; - if (s->dev.attached) { - /* - * Migrating an open device, ignore reconnection CHR_EVENT to avoid an - * erroneous detach. - */ - s->migration_state = MIGRATION_MIGRATED; - } -} - -static VMStateDescription bulk_in_vmstate = { - .name = "CCID BulkIn state", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_BUFFER(data, BulkIn), - VMSTATE_UINT32(len, BulkIn), - VMSTATE_UINT32(pos, BulkIn), - VMSTATE_END_OF_LIST() - } -}; - -static VMStateDescription answer_vmstate = { - .name = "CCID Answer state", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(slot, Answer), - VMSTATE_UINT8(seq, Answer), - VMSTATE_END_OF_LIST() - } -}; - -static VMStateDescription usb_device_vmstate = { - .name = "usb_device", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(addr, USBDevice), - VMSTATE_BUFFER(setup_buf, USBDevice), - VMSTATE_BUFFER(data_buf, USBDevice), - VMSTATE_END_OF_LIST() - } -}; - -static VMStateDescription ccid_vmstate = { - .name = "usb-ccid", - .version_id = 1, - .minimum_version_id = 1, - .post_load = ccid_post_load, - .pre_save = ccid_pre_save, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(dev, USBCCIDState, 1, usb_device_vmstate, USBDevice), - VMSTATE_UINT8(debug, USBCCIDState), - VMSTATE_BUFFER(bulk_out_data, USBCCIDState), - VMSTATE_UINT32(bulk_out_pos, USBCCIDState), - VMSTATE_UINT8(bmSlotICCState, USBCCIDState), - VMSTATE_UINT8(powered, USBCCIDState), - VMSTATE_UINT8(notify_slot_change, USBCCIDState), - VMSTATE_UINT64(last_answer_error, USBCCIDState), - VMSTATE_UINT8(bError, USBCCIDState), - VMSTATE_UINT8(bmCommandStatus, USBCCIDState), - VMSTATE_UINT8(bProtocolNum, USBCCIDState), - VMSTATE_BUFFER(abProtocolDataStructure.data, USBCCIDState), - VMSTATE_UINT32(ulProtocolDataStructureSize, USBCCIDState), - VMSTATE_STRUCT_ARRAY(bulk_in_pending, USBCCIDState, - BULK_IN_PENDING_NUM, 1, bulk_in_vmstate, BulkIn), - VMSTATE_UINT32(bulk_in_pending_start, USBCCIDState), - VMSTATE_UINT32(bulk_in_pending_end, USBCCIDState), - VMSTATE_STRUCT_ARRAY(pending_answers, USBCCIDState, - PENDING_ANSWERS_NUM, 1, answer_vmstate, Answer), - VMSTATE_UINT32(pending_answers_num, USBCCIDState), - VMSTATE_UINT8(migration_state, USBCCIDState), - VMSTATE_UINT32(state_vmstate, USBCCIDState), - VMSTATE_END_OF_LIST() - } -}; - -static Property ccid_properties[] = { - DEFINE_PROP_UINT8("debug", USBCCIDState, debug, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ccid_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - - uc->realize = ccid_realize; - uc->product_desc = "QEMU USB CCID"; - uc->usb_desc = &desc_ccid; - uc->handle_reset = ccid_handle_reset; - uc->handle_control = ccid_handle_control; - uc->handle_data = ccid_handle_data; - uc->handle_destroy = ccid_handle_destroy; - dc->desc = "CCID Rev 1.1 smartcard reader"; - dc->vmsd = &ccid_vmstate; - dc->props = ccid_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - hc->unplug = qdev_simple_device_unplug_cb; -} - -static const TypeInfo ccid_info = { - .name = CCID_DEV_NAME, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBCCIDState), - .class_init = ccid_class_initfn, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - } -}; - -static void ccid_card_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - k->bus_type = TYPE_CCID_BUS; - k->init = ccid_card_init; - k->exit = ccid_card_exit; - k->props = ccid_props; -} - -static const TypeInfo ccid_card_type_info = { - .name = TYPE_CCID_CARD, - .parent = TYPE_DEVICE, - .instance_size = sizeof(CCIDCardState), - .abstract = true, - .class_size = sizeof(CCIDCardClass), - .class_init = ccid_card_class_init, -}; - -static void ccid_register_types(void) -{ - type_register_static(&ccid_bus_info); - type_register_static(&ccid_card_type_info); - type_register_static(&ccid_info); - usb_legacy_register(CCID_DEV_NAME, "ccid", NULL); -} - -type_init(ccid_register_types) diff --git a/qemu/hw/usb/dev-storage.c b/qemu/hw/usb/dev-storage.c deleted file mode 100644 index 248a58045..000000000 --- a/qemu/hw/usb/dev-storage.c +++ /dev/null @@ -1,872 +0,0 @@ -/* - * USB Mass Storage Device emulation - * - * Copyright (c) 2006 CodeSourcery. - * Written by Paul Brook - * - * 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" -#include "qemu/config-file.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" -#include "hw/scsi/scsi.h" -#include "ui/console.h" -#include "monitor/monitor.h" -#include "sysemu/sysemu.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "qapi/visitor.h" -#include "qemu/cutils.h" - -//#define DEBUG_MSD - -#ifdef DEBUG_MSD -#define DPRINTF(fmt, ...) \ -do { printf("usb-msd: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - -/* USB requests. */ -#define MassStorageReset 0xff -#define GetMaxLun 0xfe - -enum USBMSDMode { - USB_MSDM_CBW, /* Command Block. */ - USB_MSDM_DATAOUT, /* Transfer data to device. */ - USB_MSDM_DATAIN, /* Transfer data from device. */ - USB_MSDM_CSW /* Command Status. */ -}; - -struct usb_msd_csw { - uint32_t sig; - uint32_t tag; - uint32_t residue; - uint8_t status; -}; - -typedef struct { - USBDevice dev; - enum USBMSDMode mode; - uint32_t scsi_off; - uint32_t scsi_len; - uint32_t data_len; - struct usb_msd_csw csw; - SCSIRequest *req; - SCSIBus bus; - /* For async completion. */ - USBPacket *packet; - /* usb-storage only */ - BlockConf conf; - uint32_t removable; - SCSIDevice *scsi_dev; -} MSDState; - -#define TYPE_USB_STORAGE "usb-storage-dev" -#define USB_STORAGE_DEV(obj) OBJECT_CHECK(MSDState, (obj), TYPE_USB_STORAGE) - -struct usb_msd_cbw { - uint32_t sig; - uint32_t tag; - uint32_t data_len; - uint8_t flags; - uint8_t lun; - uint8_t cmd_len; - uint8_t cmd[16]; -}; - -enum { - STR_MANUFACTURER = 1, - STR_PRODUCT, - STR_SERIALNUMBER, - STR_CONFIG_FULL, - STR_CONFIG_HIGH, - STR_CONFIG_SUPER, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = "QEMU", - [STR_PRODUCT] = "QEMU USB HARDDRIVE", - [STR_SERIALNUMBER] = "1", - [STR_CONFIG_FULL] = "Full speed config (usb 1.1)", - [STR_CONFIG_HIGH] = "High speed config (usb 2.0)", - [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)", -}; - -static const USBDescIface desc_iface_full = { - .bInterfaceNumber = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_MASS_STORAGE, - .bInterfaceSubClass = 0x06, /* SCSI */ - .bInterfaceProtocol = 0x50, /* Bulk */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 64, - },{ - .bEndpointAddress = USB_DIR_OUT | 0x02, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 64, - }, - } -}; - -static const USBDescDevice desc_device_full = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 8, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_FULL, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .nif = 1, - .ifs = &desc_iface_full, - }, - }, -}; - -static const USBDescIface desc_iface_high = { - .bInterfaceNumber = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_MASS_STORAGE, - .bInterfaceSubClass = 0x06, /* SCSI */ - .bInterfaceProtocol = 0x50, /* Bulk */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 512, - },{ - .bEndpointAddress = USB_DIR_OUT | 0x02, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 512, - }, - } -}; - -static const USBDescDevice desc_device_high = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_HIGH, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .nif = 1, - .ifs = &desc_iface_high, - }, - }, -}; - -static const USBDescIface desc_iface_super = { - .bInterfaceNumber = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_MASS_STORAGE, - .bInterfaceSubClass = 0x06, /* SCSI */ - .bInterfaceProtocol = 0x50, /* Bulk */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 1024, - .bMaxBurst = 15, - },{ - .bEndpointAddress = USB_DIR_OUT | 0x02, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 1024, - .bMaxBurst = 15, - }, - } -}; - -static const USBDescDevice desc_device_super = { - .bcdUSB = 0x0300, - .bMaxPacketSize0 = 9, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_SUPER, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .nif = 1, - .ifs = &desc_iface_super, - }, - }, -}; - -static const USBDesc desc = { - .id = { - .idVendor = 0x46f4, /* CRC16() of "QEMU" */ - .idProduct = 0x0001, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_full, - .high = &desc_device_high, - .super = &desc_device_super, - .str = desc_strings, -}; - -static void usb_msd_copy_data(MSDState *s, USBPacket *p) -{ - uint32_t len; - len = p->iov.size - p->actual_length; - if (len > s->scsi_len) - len = s->scsi_len; - usb_packet_copy(p, scsi_req_get_buf(s->req) + s->scsi_off, len); - s->scsi_len -= len; - s->scsi_off += len; - s->data_len -= len; - if (s->scsi_len == 0 || s->data_len == 0) { - scsi_req_continue(s->req); - } -} - -static void usb_msd_send_status(MSDState *s, USBPacket *p) -{ - int len; - - DPRINTF("Command status %d tag 0x%x, len %zd\n", - s->csw.status, le32_to_cpu(s->csw.tag), p->iov.size); - - assert(s->csw.sig == cpu_to_le32(0x53425355)); - len = MIN(sizeof(s->csw), p->iov.size); - usb_packet_copy(p, &s->csw, len); - memset(&s->csw, 0, sizeof(s->csw)); -} - -static void usb_msd_packet_complete(MSDState *s) -{ - USBPacket *p = s->packet; - - /* Set s->packet to NULL before calling usb_packet_complete - because another request may be issued before - usb_packet_complete returns. */ - DPRINTF("Packet complete %p\n", p); - s->packet = NULL; - usb_packet_complete(&s->dev, p); -} - -static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) -{ - MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); - USBPacket *p = s->packet; - - assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV)); - s->scsi_len = len; - s->scsi_off = 0; - if (p) { - usb_msd_copy_data(s, p); - p = s->packet; - if (p && p->actual_length == p->iov.size) { - p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ - usb_msd_packet_complete(s); - } - } -} - -static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t resid) -{ - MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); - USBPacket *p = s->packet; - - DPRINTF("Command complete %d tag 0x%x\n", status, req->tag); - - s->csw.sig = cpu_to_le32(0x53425355); - s->csw.tag = cpu_to_le32(req->tag); - s->csw.residue = cpu_to_le32(s->data_len); - s->csw.status = status != 0; - - if (s->packet) { - if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) { - /* A deferred packet with no write data remaining must be - the status read packet. */ - usb_msd_send_status(s, p); - s->mode = USB_MSDM_CBW; - } else if (s->mode == USB_MSDM_CSW) { - usb_msd_send_status(s, p); - s->mode = USB_MSDM_CBW; - } else { - if (s->data_len) { - int len = (p->iov.size - p->actual_length); - usb_packet_skip(p, len); - s->data_len -= len; - } - if (s->data_len == 0) { - s->mode = USB_MSDM_CSW; - } - } - p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ - usb_msd_packet_complete(s); - } else if (s->data_len == 0) { - s->mode = USB_MSDM_CSW; - } - scsi_req_unref(req); - s->req = NULL; -} - -static void usb_msd_request_cancelled(SCSIRequest *req) -{ - MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); - - if (req == s->req) { - scsi_req_unref(s->req); - s->req = NULL; - s->scsi_len = 0; - } -} - -static void usb_msd_handle_reset(USBDevice *dev) -{ - MSDState *s = (MSDState *)dev; - - DPRINTF("Reset\n"); - if (s->req) { - scsi_req_cancel(s->req); - } - assert(s->req == NULL); - - if (s->packet) { - s->packet->status = USB_RET_STALL; - usb_msd_packet_complete(s); - } - - s->mode = USB_MSDM_CBW; -} - -static void usb_msd_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) -{ - MSDState *s = (MSDState *)dev; - SCSIDevice *scsi_dev; - int ret, maxlun; - - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - switch (request) { - case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: - break; - /* Class specific requests. */ - case ClassInterfaceOutRequest | MassStorageReset: - /* Reset state ready for the next CBW. */ - s->mode = USB_MSDM_CBW; - break; - case ClassInterfaceRequest | GetMaxLun: - maxlun = 0; - for (;;) { - scsi_dev = scsi_device_find(&s->bus, 0, 0, maxlun+1); - if (scsi_dev == NULL) { - break; - } - if (scsi_dev->lun != maxlun+1) { - break; - } - maxlun++; - } - DPRINTF("MaxLun %d\n", maxlun); - data[0] = maxlun; - p->actual_length = 1; - break; - default: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p) -{ - MSDState *s = USB_STORAGE_DEV(dev); - - assert(s->packet == p); - s->packet = NULL; - - if (s->req) { - scsi_req_cancel(s->req); - } -} - -static void usb_msd_handle_data(USBDevice *dev, USBPacket *p) -{ - MSDState *s = (MSDState *)dev; - uint32_t tag; - struct usb_msd_cbw cbw; - uint8_t devep = p->ep->nr; - SCSIDevice *scsi_dev; - uint32_t len; - - switch (p->pid) { - case USB_TOKEN_OUT: - if (devep != 2) - goto fail; - - switch (s->mode) { - case USB_MSDM_CBW: - if (p->iov.size != 31) { - error_report("usb-msd: Bad CBW size"); - goto fail; - } - usb_packet_copy(p, &cbw, 31); - if (le32_to_cpu(cbw.sig) != 0x43425355) { - error_report("usb-msd: Bad signature %08x", - le32_to_cpu(cbw.sig)); - goto fail; - } - DPRINTF("Command on LUN %d\n", cbw.lun); - scsi_dev = scsi_device_find(&s->bus, 0, 0, cbw.lun); - if (scsi_dev == NULL) { - error_report("usb-msd: Bad LUN %d", cbw.lun); - goto fail; - } - tag = le32_to_cpu(cbw.tag); - s->data_len = le32_to_cpu(cbw.data_len); - if (s->data_len == 0) { - s->mode = USB_MSDM_CSW; - } else if (cbw.flags & 0x80) { - s->mode = USB_MSDM_DATAIN; - } else { - s->mode = USB_MSDM_DATAOUT; - } - DPRINTF("Command tag 0x%x flags %08x len %d data %d\n", - tag, cbw.flags, cbw.cmd_len, s->data_len); - assert(le32_to_cpu(s->csw.residue) == 0); - s->scsi_len = 0; - s->req = scsi_req_new(scsi_dev, tag, cbw.lun, cbw.cmd, NULL); -#ifdef DEBUG_MSD - scsi_req_print(s->req); -#endif - len = scsi_req_enqueue(s->req); - if (len) { - scsi_req_continue(s->req); - } - break; - - case USB_MSDM_DATAOUT: - DPRINTF("Data out %zd/%d\n", p->iov.size, s->data_len); - if (p->iov.size > s->data_len) { - goto fail; - } - - if (s->scsi_len) { - usb_msd_copy_data(s, p); - } - if (le32_to_cpu(s->csw.residue)) { - int len = p->iov.size - p->actual_length; - if (len) { - usb_packet_skip(p, len); - s->data_len -= len; - if (s->data_len == 0) { - s->mode = USB_MSDM_CSW; - } - } - } - if (p->actual_length < p->iov.size) { - DPRINTF("Deferring packet %p [wait data-out]\n", p); - s->packet = p; - p->status = USB_RET_ASYNC; - } - break; - - default: - DPRINTF("Unexpected write (len %zd)\n", p->iov.size); - goto fail; - } - break; - - case USB_TOKEN_IN: - if (devep != 1) - goto fail; - - switch (s->mode) { - case USB_MSDM_DATAOUT: - if (s->data_len != 0 || p->iov.size < 13) { - goto fail; - } - /* Waiting for SCSI write to complete. */ - s->packet = p; - p->status = USB_RET_ASYNC; - break; - - case USB_MSDM_CSW: - if (p->iov.size < 13) { - goto fail; - } - - if (s->req) { - /* still in flight */ - DPRINTF("Deferring packet %p [wait status]\n", p); - s->packet = p; - p->status = USB_RET_ASYNC; - } else { - usb_msd_send_status(s, p); - s->mode = USB_MSDM_CBW; - } - break; - - case USB_MSDM_DATAIN: - DPRINTF("Data in %zd/%d, scsi_len %d\n", - p->iov.size, s->data_len, s->scsi_len); - if (s->scsi_len) { - usb_msd_copy_data(s, p); - } - if (le32_to_cpu(s->csw.residue)) { - int len = p->iov.size - p->actual_length; - if (len) { - usb_packet_skip(p, len); - s->data_len -= len; - if (s->data_len == 0) { - s->mode = USB_MSDM_CSW; - } - } - } - if (p->actual_length < p->iov.size) { - DPRINTF("Deferring packet %p [wait data-in]\n", p); - s->packet = p; - p->status = USB_RET_ASYNC; - } - break; - - default: - DPRINTF("Unexpected read (len %zd)\n", p->iov.size); - goto fail; - } - break; - - default: - DPRINTF("Bad token\n"); - fail: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_msd_password_cb(void *opaque, int err) -{ - MSDState *s = opaque; - Error *local_err = NULL; - - if (!err) { - usb_device_attach(&s->dev, &local_err); - } - - if (local_err) { - error_report_err(local_err); - qdev_unplug(&s->dev.qdev, NULL); - } -} - -static void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req) -{ - MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); - - /* nothing to load, just store req in our state struct */ - assert(s->req == NULL); - scsi_req_ref(req); - s->req = req; - return NULL; -} - -static const struct SCSIBusInfo usb_msd_scsi_info_storage = { - .tcq = false, - .max_target = 0, - .max_lun = 0, - - .transfer_data = usb_msd_transfer_data, - .complete = usb_msd_command_complete, - .cancel = usb_msd_request_cancelled, - .load_request = usb_msd_load_request, -}; - -static const struct SCSIBusInfo usb_msd_scsi_info_bot = { - .tcq = false, - .max_target = 0, - .max_lun = 15, - - .transfer_data = usb_msd_transfer_data, - .complete = usb_msd_command_complete, - .cancel = usb_msd_request_cancelled, - .load_request = usb_msd_load_request, -}; - -static void usb_msd_realize_storage(USBDevice *dev, Error **errp) -{ - MSDState *s = USB_STORAGE_DEV(dev); - BlockBackend *blk = s->conf.blk; - SCSIDevice *scsi_dev; - Error *err = NULL; - - if (!blk) { - error_setg(errp, "drive property not set"); - return; - } - - 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; - } - } - } - - blkconf_serial(&s->conf, &dev->serial); - blkconf_blocksizes(&s->conf); - - /* - * Hack alert: this pretends to be a block device, but it's really - * a SCSI bus that can serve only a single device, which it - * creates automatically. But first it needs to detach from its - * blockdev, or else scsi_bus_legacy_add_drive() dies when it - * attaches again. - * - * The hack is probably a bad idea. - */ - blk_detach_dev(blk, &s->dev.qdev); - s->conf.blk = NULL; - - usb_desc_create_serial(dev); - usb_desc_init(dev); - scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), - &usb_msd_scsi_info_storage, NULL); - scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable, - s->conf.bootindex, dev->serial, - &err); - if (!scsi_dev) { - error_propagate(errp, err); - return; - } - usb_msd_handle_reset(dev); - s->scsi_dev = scsi_dev; -} - -static void usb_msd_realize_bot(USBDevice *dev, Error **errp) -{ - MSDState *s = USB_STORAGE_DEV(dev); - - usb_desc_create_serial(dev); - usb_desc_init(dev); - scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), - &usb_msd_scsi_info_bot, NULL); - usb_msd_handle_reset(dev); -} - -static USBDevice *usb_msd_init(USBBus *bus, const char *filename) -{ - static int nr=0; - Error *err = NULL; - char id[8]; - QemuOpts *opts; - DriveInfo *dinfo; - USBDevice *dev; - const char *p1; - char fmt[32]; - - /* parse -usbdevice disk: syntax into drive opts */ - do { - snprintf(id, sizeof(id), "usb%d", nr++); - opts = qemu_opts_create(qemu_find_opts("drive"), id, 1, NULL); - } while (!opts); - - p1 = strchr(filename, ':'); - if (p1++) { - const char *p2; - - if (strstart(filename, "format=", &p2)) { - int len = MIN(p1 - p2, sizeof(fmt)); - pstrcpy(fmt, len, p2); - qemu_opt_set(opts, "format", fmt, &error_abort); - } else if (*filename != ':') { - error_report("unrecognized USB mass-storage option %s", filename); - return NULL; - } - filename = p1; - } - if (!*filename) { - error_report("block device specification needed"); - return NULL; - } - qemu_opt_set(opts, "file", filename, &error_abort); - qemu_opt_set(opts, "if", "none", &error_abort); - - /* create host drive */ - dinfo = drive_new(opts, 0); - if (!dinfo) { - qemu_opts_del(opts); - return NULL; - } - - /* create guest device */ - dev = usb_create(bus, "usb-storage"); - qdev_prop_set_drive(&dev->qdev, "drive", blk_by_legacy_dinfo(dinfo), - &err); - if (err) { - error_report_err(err); - object_unparent(OBJECT(dev)); - return NULL; - } - return dev; -} - -static const VMStateDescription vmstate_usb_msd = { - .name = "usb-storage", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_USB_DEVICE(dev, MSDState), - VMSTATE_UINT32(mode, MSDState), - VMSTATE_UINT32(scsi_len, MSDState), - VMSTATE_UINT32(scsi_off, MSDState), - VMSTATE_UINT32(data_len, MSDState), - VMSTATE_UINT32(csw.sig, MSDState), - VMSTATE_UINT32(csw.tag, MSDState), - VMSTATE_UINT32(csw.residue, MSDState), - VMSTATE_UINT8(csw.status, MSDState), - VMSTATE_END_OF_LIST() - } -}; - -static Property msd_properties[] = { - DEFINE_BLOCK_PROPERTIES(MSDState, conf), - DEFINE_PROP_BIT("removable", MSDState, removable, 0, false), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_msd_class_initfn_common(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->product_desc = "QEMU USB MSD"; - uc->usb_desc = &desc; - uc->cancel_packet = usb_msd_cancel_io; - uc->handle_attach = usb_desc_attach; - uc->handle_reset = usb_msd_handle_reset; - uc->handle_control = usb_msd_handle_control; - uc->handle_data = usb_msd_handle_data; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->fw_name = "storage"; - dc->vmsd = &vmstate_usb_msd; -} - -static void usb_msd_class_initfn_storage(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_msd_realize_storage; - dc->props = msd_properties; -} - -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, name, &s->conf.bootindex, 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, name, &boot_index, &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; - - if (s->scsi_dev) { - object_property_set_int(OBJECT(s->scsi_dev), boot_index, "bootindex", - &error_abort); - } - -out: - if (local_err) { - error_propagate(errp, local_err); - } -} - -static const TypeInfo usb_storage_dev_type_info = { - .name = TYPE_USB_STORAGE, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(MSDState), - .abstract = true, - .class_init = usb_msd_class_initfn_common, -}; - -static void usb_msd_instance_init(Object *obj) -{ - object_property_add(obj, "bootindex", "int32", - usb_msd_get_bootindex, - usb_msd_set_bootindex, NULL, NULL, NULL); - object_property_set_int(obj, -1, "bootindex", NULL); -} - -static void usb_msd_class_initfn_bot(ObjectClass *klass, void *data) -{ - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - uc->realize = usb_msd_realize_bot; - dc->hotpluggable = false; -} - -static const TypeInfo msd_info = { - .name = "usb-storage", - .parent = TYPE_USB_STORAGE, - .class_init = usb_msd_class_initfn_storage, - .instance_init = usb_msd_instance_init, -}; - -static const TypeInfo bot_info = { - .name = "usb-bot", - .parent = TYPE_USB_STORAGE, - .class_init = usb_msd_class_initfn_bot, -}; - -static void usb_msd_register_types(void) -{ - type_register_static(&usb_storage_dev_type_info); - type_register_static(&msd_info); - type_register_static(&bot_info); - usb_legacy_register("usb-storage", "disk", usb_msd_init); -} - -type_init(usb_msd_register_types) diff --git a/qemu/hw/usb/dev-uas.c b/qemu/hw/usb/dev-uas.c deleted file mode 100644 index 0678b1b05..000000000 --- a/qemu/hw/usb/dev-uas.c +++ /dev/null @@ -1,961 +0,0 @@ -/* - * UAS (USB Attached SCSI) emulation - * - * Copyright Red Hat, Inc. 2012 - * - * Author: Gerd Hoffmann - * - * 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/option.h" -#include "qemu/config-file.h" -#include "trace.h" -#include "qemu/error-report.h" - -#include "hw/usb.h" -#include "hw/usb/desc.h" -#include "hw/scsi/scsi.h" -#include "block/scsi.h" - -/* --------------------------------------------------------------------- */ - -#define UAS_UI_COMMAND 0x01 -#define UAS_UI_SENSE 0x03 -#define UAS_UI_RESPONSE 0x04 -#define UAS_UI_TASK_MGMT 0x05 -#define UAS_UI_READ_READY 0x06 -#define UAS_UI_WRITE_READY 0x07 - -#define UAS_RC_TMF_COMPLETE 0x00 -#define UAS_RC_INVALID_INFO_UNIT 0x02 -#define UAS_RC_TMF_NOT_SUPPORTED 0x04 -#define UAS_RC_TMF_FAILED 0x05 -#define UAS_RC_TMF_SUCCEEDED 0x08 -#define UAS_RC_INCORRECT_LUN 0x09 -#define UAS_RC_OVERLAPPED_TAG 0x0a - -#define UAS_TMF_ABORT_TASK 0x01 -#define UAS_TMF_ABORT_TASK_SET 0x02 -#define UAS_TMF_CLEAR_TASK_SET 0x04 -#define UAS_TMF_LOGICAL_UNIT_RESET 0x08 -#define UAS_TMF_I_T_NEXUS_RESET 0x10 -#define UAS_TMF_CLEAR_ACA 0x40 -#define UAS_TMF_QUERY_TASK 0x80 -#define UAS_TMF_QUERY_TASK_SET 0x81 -#define UAS_TMF_QUERY_ASYNC_EVENT 0x82 - -#define UAS_PIPE_ID_COMMAND 0x01 -#define UAS_PIPE_ID_STATUS 0x02 -#define UAS_PIPE_ID_DATA_IN 0x03 -#define UAS_PIPE_ID_DATA_OUT 0x04 - -typedef struct { - uint8_t id; - uint8_t reserved; - uint16_t tag; -} QEMU_PACKED uas_iu_header; - -typedef struct { - uint8_t prio_taskattr; /* 6:3 priority, 2:0 task attribute */ - uint8_t reserved_1; - uint8_t add_cdb_length; /* 7:2 additional adb length (dwords) */ - uint8_t reserved_2; - uint64_t lun; - uint8_t cdb[16]; - uint8_t add_cdb[]; -} QEMU_PACKED uas_iu_command; - -typedef struct { - uint16_t status_qualifier; - uint8_t status; - uint8_t reserved[7]; - uint16_t sense_length; - uint8_t sense_data[18]; -} QEMU_PACKED uas_iu_sense; - -typedef struct { - uint8_t add_response_info[3]; - uint8_t response_code; -} QEMU_PACKED uas_iu_response; - -typedef struct { - uint8_t function; - uint8_t reserved; - uint16_t task_tag; - uint64_t lun; -} QEMU_PACKED uas_iu_task_mgmt; - -typedef struct { - uas_iu_header hdr; - union { - uas_iu_command command; - uas_iu_sense sense; - uas_iu_task_mgmt task; - uas_iu_response response; - }; -} QEMU_PACKED uas_iu; - -/* --------------------------------------------------------------------- */ - -#define UAS_STREAM_BM_ATTR 4 -#define UAS_MAX_STREAMS (1 << UAS_STREAM_BM_ATTR) - -typedef struct UASDevice UASDevice; -typedef struct UASRequest UASRequest; -typedef struct UASStatus UASStatus; - -struct UASDevice { - USBDevice dev; - SCSIBus bus; - QEMUBH *status_bh; - QTAILQ_HEAD(, UASStatus) results; - QTAILQ_HEAD(, UASRequest) requests; - - /* properties */ - uint32_t requestlog; - - /* usb 2.0 only */ - USBPacket *status2; - UASRequest *datain2; - UASRequest *dataout2; - - /* usb 3.0 only */ - USBPacket *data3[UAS_MAX_STREAMS + 1]; - USBPacket *status3[UAS_MAX_STREAMS + 1]; -}; - -#define TYPE_USB_UAS "usb-uas" -#define USB_UAS(obj) OBJECT_CHECK(UASDevice, (obj), TYPE_USB_UAS) - -struct UASRequest { - uint16_t tag; - uint64_t lun; - UASDevice *uas; - SCSIDevice *dev; - SCSIRequest *req; - USBPacket *data; - bool data_async; - bool active; - bool complete; - uint32_t buf_off; - uint32_t buf_size; - uint32_t data_off; - uint32_t data_size; - QTAILQ_ENTRY(UASRequest) next; -}; - -struct UASStatus { - uint32_t stream; - uas_iu status; - uint32_t length; - QTAILQ_ENTRY(UASStatus) next; -}; - -/* --------------------------------------------------------------------- */ - -enum { - STR_MANUFACTURER = 1, - STR_PRODUCT, - STR_SERIALNUMBER, - STR_CONFIG_HIGH, - STR_CONFIG_SUPER, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = "QEMU", - [STR_PRODUCT] = "USB Attached SCSI HBA", - [STR_SERIALNUMBER] = "27842", - [STR_CONFIG_HIGH] = "High speed config (usb 2.0)", - [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)", -}; - -static const USBDescIface desc_iface_high = { - .bInterfaceNumber = 0, - .bNumEndpoints = 4, - .bInterfaceClass = USB_CLASS_MASS_STORAGE, - .bInterfaceSubClass = 0x06, /* SCSI */ - .bInterfaceProtocol = 0x62, /* UAS */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_COMMAND, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 512, - .extra = (uint8_t[]) { - 0x04, /* u8 bLength */ - 0x24, /* u8 bDescriptorType */ - UAS_PIPE_ID_COMMAND, - 0x00, /* u8 bReserved */ - }, - },{ - .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_STATUS, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 512, - .extra = (uint8_t[]) { - 0x04, /* u8 bLength */ - 0x24, /* u8 bDescriptorType */ - UAS_PIPE_ID_STATUS, - 0x00, /* u8 bReserved */ - }, - },{ - .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_DATA_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 512, - .extra = (uint8_t[]) { - 0x04, /* u8 bLength */ - 0x24, /* u8 bDescriptorType */ - UAS_PIPE_ID_DATA_IN, - 0x00, /* u8 bReserved */ - }, - },{ - .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_DATA_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 512, - .extra = (uint8_t[]) { - 0x04, /* u8 bLength */ - 0x24, /* u8 bDescriptorType */ - UAS_PIPE_ID_DATA_OUT, - 0x00, /* u8 bReserved */ - }, - }, - } -}; - -static const USBDescIface desc_iface_super = { - .bInterfaceNumber = 0, - .bNumEndpoints = 4, - .bInterfaceClass = USB_CLASS_MASS_STORAGE, - .bInterfaceSubClass = 0x06, /* SCSI */ - .bInterfaceProtocol = 0x62, /* UAS */ - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_COMMAND, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 1024, - .bMaxBurst = 15, - .extra = (uint8_t[]) { - 0x04, /* u8 bLength */ - 0x24, /* u8 bDescriptorType */ - UAS_PIPE_ID_COMMAND, - 0x00, /* u8 bReserved */ - }, - },{ - .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_STATUS, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 1024, - .bMaxBurst = 15, - .bmAttributes_super = UAS_STREAM_BM_ATTR, - .extra = (uint8_t[]) { - 0x04, /* u8 bLength */ - 0x24, /* u8 bDescriptorType */ - UAS_PIPE_ID_STATUS, - 0x00, /* u8 bReserved */ - }, - },{ - .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_DATA_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 1024, - .bMaxBurst = 15, - .bmAttributes_super = UAS_STREAM_BM_ATTR, - .extra = (uint8_t[]) { - 0x04, /* u8 bLength */ - 0x24, /* u8 bDescriptorType */ - UAS_PIPE_ID_DATA_IN, - 0x00, /* u8 bReserved */ - }, - },{ - .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_DATA_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = 1024, - .bMaxBurst = 15, - .bmAttributes_super = UAS_STREAM_BM_ATTR, - .extra = (uint8_t[]) { - 0x04, /* u8 bLength */ - 0x24, /* u8 bDescriptorType */ - UAS_PIPE_ID_DATA_OUT, - 0x00, /* u8 bReserved */ - }, - }, - } -}; - -static const USBDescDevice desc_device_high = { - .bcdUSB = 0x0200, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_HIGH, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .nif = 1, - .ifs = &desc_iface_high, - }, - }, -}; - -static const USBDescDevice desc_device_super = { - .bcdUSB = 0x0300, - .bMaxPacketSize0 = 64, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG_SUPER, - .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER, - .nif = 1, - .ifs = &desc_iface_super, - }, - }, -}; - -static const USBDesc desc = { - .id = { - .idVendor = 0x46f4, /* CRC16() of "QEMU" */ - .idProduct = 0x0003, - .bcdDevice = 0, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT, - .iSerialNumber = STR_SERIALNUMBER, - }, - .high = &desc_device_high, - .super = &desc_device_super, - .str = desc_strings, -}; - -/* --------------------------------------------------------------------- */ - -static bool uas_using_streams(UASDevice *uas) -{ - return uas->dev.speed == USB_SPEED_SUPER; -} - -/* --------------------------------------------------------------------- */ - -static UASStatus *usb_uas_alloc_status(UASDevice *uas, uint8_t id, uint16_t tag) -{ - UASStatus *st = g_new0(UASStatus, 1); - - st->status.hdr.id = id; - st->status.hdr.tag = cpu_to_be16(tag); - st->length = sizeof(uas_iu_header); - if (uas_using_streams(uas)) { - st->stream = tag; - } - return st; -} - -static void usb_uas_send_status_bh(void *opaque) -{ - UASDevice *uas = opaque; - UASStatus *st; - USBPacket *p; - - while ((st = QTAILQ_FIRST(&uas->results)) != NULL) { - if (uas_using_streams(uas)) { - p = uas->status3[st->stream]; - uas->status3[st->stream] = NULL; - } else { - p = uas->status2; - uas->status2 = NULL; - } - if (p == NULL) { - break; - } - - usb_packet_copy(p, &st->status, st->length); - QTAILQ_REMOVE(&uas->results, st, next); - g_free(st); - - p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ - usb_packet_complete(&uas->dev, p); - } -} - -static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length) -{ - USBPacket *p = uas_using_streams(uas) ? - uas->status3[st->stream] : uas->status2; - - st->length += length; - QTAILQ_INSERT_TAIL(&uas->results, st, next); - if (p) { - /* - * Just schedule bh make sure any in-flight data transaction - * is finished before completing (sending) the status packet. - */ - qemu_bh_schedule(uas->status_bh); - } else { - USBEndpoint *ep = usb_ep_get(&uas->dev, USB_TOKEN_IN, - UAS_PIPE_ID_STATUS); - usb_wakeup(ep, st->stream); - } -} - -static void usb_uas_queue_response(UASDevice *uas, uint16_t tag, uint8_t code) -{ - UASStatus *st = usb_uas_alloc_status(uas, UAS_UI_RESPONSE, tag); - - trace_usb_uas_response(uas->dev.addr, tag, code); - st->status.response.response_code = code; - usb_uas_queue_status(uas, st, sizeof(uas_iu_response)); -} - -static void usb_uas_queue_sense(UASRequest *req, uint8_t status) -{ - UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_SENSE, req->tag); - int len, slen = 0; - - trace_usb_uas_sense(req->uas->dev.addr, req->tag, status); - st->status.sense.status = status; - st->status.sense.status_qualifier = cpu_to_be16(0); - if (status != GOOD) { - slen = scsi_req_get_sense(req->req, st->status.sense.sense_data, - sizeof(st->status.sense.sense_data)); - st->status.sense.sense_length = cpu_to_be16(slen); - } - len = sizeof(uas_iu_sense) - sizeof(st->status.sense.sense_data) + slen; - usb_uas_queue_status(req->uas, st, len); -} - -static void usb_uas_queue_fake_sense(UASDevice *uas, uint16_t tag, - struct SCSISense sense) -{ - UASStatus *st = usb_uas_alloc_status(uas, UAS_UI_SENSE, tag); - int len, slen = 0; - - st->status.sense.status = CHECK_CONDITION; - st->status.sense.status_qualifier = cpu_to_be16(0); - st->status.sense.sense_data[0] = 0x70; - st->status.sense.sense_data[2] = sense.key; - st->status.sense.sense_data[7] = 10; - st->status.sense.sense_data[12] = sense.asc; - st->status.sense.sense_data[13] = sense.ascq; - slen = 18; - len = sizeof(uas_iu_sense) - sizeof(st->status.sense.sense_data) + slen; - usb_uas_queue_status(uas, st, len); -} - -static void usb_uas_queue_read_ready(UASRequest *req) -{ - UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_READ_READY, - req->tag); - - trace_usb_uas_read_ready(req->uas->dev.addr, req->tag); - usb_uas_queue_status(req->uas, st, 0); -} - -static void usb_uas_queue_write_ready(UASRequest *req) -{ - UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_WRITE_READY, - req->tag); - - trace_usb_uas_write_ready(req->uas->dev.addr, req->tag); - usb_uas_queue_status(req->uas, st, 0); -} - -/* --------------------------------------------------------------------- */ - -static int usb_uas_get_lun(uint64_t lun64) -{ - return (lun64 >> 48) & 0xff; -} - -static SCSIDevice *usb_uas_get_dev(UASDevice *uas, uint64_t lun64) -{ - if ((lun64 >> 56) != 0x00) { - return NULL; - } - return scsi_device_find(&uas->bus, 0, 0, usb_uas_get_lun(lun64)); -} - -static void usb_uas_complete_data_packet(UASRequest *req) -{ - USBPacket *p; - - if (!req->data_async) { - return; - } - p = req->data; - req->data = NULL; - req->data_async = false; - p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ - usb_packet_complete(&req->uas->dev, p); -} - -static void usb_uas_copy_data(UASRequest *req) -{ - uint32_t length; - - length = MIN(req->buf_size - req->buf_off, - req->data->iov.size - req->data->actual_length); - trace_usb_uas_xfer_data(req->uas->dev.addr, req->tag, length, - req->data->actual_length, req->data->iov.size, - req->buf_off, req->buf_size); - usb_packet_copy(req->data, scsi_req_get_buf(req->req) + req->buf_off, - length); - req->buf_off += length; - req->data_off += length; - - if (req->data->actual_length == req->data->iov.size) { - usb_uas_complete_data_packet(req); - } - if (req->buf_size && req->buf_off == req->buf_size) { - req->buf_off = 0; - req->buf_size = 0; - scsi_req_continue(req->req); - } -} - -static void usb_uas_start_next_transfer(UASDevice *uas) -{ - UASRequest *req; - - if (uas_using_streams(uas)) { - return; - } - - QTAILQ_FOREACH(req, &uas->requests, next) { - if (req->active || req->complete) { - continue; - } - if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain2 == NULL) { - uas->datain2 = req; - usb_uas_queue_read_ready(req); - req->active = true; - return; - } - if (req->req->cmd.mode == SCSI_XFER_TO_DEV && uas->dataout2 == NULL) { - uas->dataout2 = req; - usb_uas_queue_write_ready(req); - req->active = true; - return; - } - } -} - -static UASRequest *usb_uas_alloc_request(UASDevice *uas, uas_iu *iu) -{ - UASRequest *req; - - req = g_new0(UASRequest, 1); - req->uas = uas; - req->tag = be16_to_cpu(iu->hdr.tag); - req->lun = be64_to_cpu(iu->command.lun); - req->dev = usb_uas_get_dev(req->uas, req->lun); - return req; -} - -static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv) -{ - UASRequest *req = priv; - UASDevice *uas = req->uas; - - if (req == uas->datain2) { - uas->datain2 = NULL; - } - if (req == uas->dataout2) { - uas->dataout2 = NULL; - } - QTAILQ_REMOVE(&uas->requests, req, next); - g_free(req); - usb_uas_start_next_transfer(uas); -} - -static UASRequest *usb_uas_find_request(UASDevice *uas, uint16_t tag) -{ - UASRequest *req; - - QTAILQ_FOREACH(req, &uas->requests, next) { - if (req->tag == tag) { - return req; - } - } - return NULL; -} - -static void usb_uas_scsi_transfer_data(SCSIRequest *r, uint32_t len) -{ - UASRequest *req = r->hba_private; - - trace_usb_uas_scsi_data(req->uas->dev.addr, req->tag, len); - req->buf_off = 0; - req->buf_size = len; - if (req->data) { - usb_uas_copy_data(req); - } else { - usb_uas_start_next_transfer(req->uas); - } -} - -static void usb_uas_scsi_command_complete(SCSIRequest *r, - uint32_t status, size_t resid) -{ - UASRequest *req = r->hba_private; - - trace_usb_uas_scsi_complete(req->uas->dev.addr, req->tag, status, resid); - req->complete = true; - if (req->data) { - usb_uas_complete_data_packet(req); - } - usb_uas_queue_sense(req, status); - scsi_req_unref(req->req); -} - -static void usb_uas_scsi_request_cancelled(SCSIRequest *r) -{ - UASRequest *req = r->hba_private; - - /* FIXME: queue notification to status pipe? */ - scsi_req_unref(req->req); -} - -static const struct SCSIBusInfo usb_uas_scsi_info = { - .tcq = true, - .max_target = 0, - .max_lun = 255, - - .transfer_data = usb_uas_scsi_transfer_data, - .complete = usb_uas_scsi_command_complete, - .cancel = usb_uas_scsi_request_cancelled, - .free_request = usb_uas_scsi_free_request, -}; - -/* --------------------------------------------------------------------- */ - -static void usb_uas_handle_reset(USBDevice *dev) -{ - UASDevice *uas = USB_UAS(dev); - UASRequest *req, *nreq; - UASStatus *st, *nst; - - trace_usb_uas_reset(dev->addr); - QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) { - scsi_req_cancel(req->req); - } - QTAILQ_FOREACH_SAFE(st, &uas->results, next, nst) { - QTAILQ_REMOVE(&uas->results, st, next); - g_free(st); - } -} - -static void usb_uas_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) -{ - int ret; - - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - error_report("%s: unhandled control request", __func__); - p->status = USB_RET_STALL; -} - -static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p) -{ - UASDevice *uas = USB_UAS(dev); - UASRequest *req, *nreq; - int i; - - if (uas->status2 == p) { - uas->status2 = NULL; - qemu_bh_cancel(uas->status_bh); - return; - } - if (uas_using_streams(uas)) { - for (i = 0; i <= UAS_MAX_STREAMS; i++) { - if (uas->status3[i] == p) { - uas->status3[i] = NULL; - return; - } - if (uas->data3[i] == p) { - uas->data3[i] = NULL; - return; - } - } - } - QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) { - if (req->data == p) { - req->data = NULL; - return; - } - } - assert(!"canceled usb packet not found"); -} - -static void usb_uas_command(UASDevice *uas, uas_iu *iu) -{ - UASRequest *req; - uint32_t len; - uint16_t tag = be16_to_cpu(iu->hdr.tag); - - if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) { - goto invalid_tag; - } - req = usb_uas_find_request(uas, tag); - if (req) { - goto overlapped_tag; - } - req = usb_uas_alloc_request(uas, iu); - if (req->dev == NULL) { - goto bad_target; - } - - trace_usb_uas_command(uas->dev.addr, req->tag, - usb_uas_get_lun(req->lun), - req->lun >> 32, req->lun & 0xffffffff); - QTAILQ_INSERT_TAIL(&uas->requests, req, next); - if (uas_using_streams(uas) && uas->data3[req->tag] != NULL) { - req->data = uas->data3[req->tag]; - req->data_async = true; - uas->data3[req->tag] = NULL; - } - - req->req = scsi_req_new(req->dev, req->tag, - usb_uas_get_lun(req->lun), - iu->command.cdb, req); - if (uas->requestlog) { - scsi_req_print(req->req); - } - len = scsi_req_enqueue(req->req); - if (len) { - req->data_size = len; - scsi_req_continue(req->req); - } - return; - -invalid_tag: - usb_uas_queue_fake_sense(uas, tag, sense_code_INVALID_TAG); - return; - -overlapped_tag: - usb_uas_queue_fake_sense(uas, tag, sense_code_OVERLAPPED_COMMANDS); - return; - -bad_target: - usb_uas_queue_fake_sense(uas, tag, sense_code_LUN_NOT_SUPPORTED); - g_free(req); -} - -static void usb_uas_task(UASDevice *uas, uas_iu *iu) -{ - uint16_t tag = be16_to_cpu(iu->hdr.tag); - uint64_t lun64 = be64_to_cpu(iu->task.lun); - SCSIDevice *dev = usb_uas_get_dev(uas, lun64); - int lun = usb_uas_get_lun(lun64); - UASRequest *req; - uint16_t task_tag; - - if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) { - goto invalid_tag; - } - req = usb_uas_find_request(uas, be16_to_cpu(iu->hdr.tag)); - if (req) { - goto overlapped_tag; - } - if (dev == NULL) { - goto incorrect_lun; - } - - switch (iu->task.function) { - case UAS_TMF_ABORT_TASK: - task_tag = be16_to_cpu(iu->task.task_tag); - trace_usb_uas_tmf_abort_task(uas->dev.addr, tag, task_tag); - req = usb_uas_find_request(uas, task_tag); - if (req && req->dev == dev) { - scsi_req_cancel(req->req); - } - usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE); - break; - - case UAS_TMF_LOGICAL_UNIT_RESET: - trace_usb_uas_tmf_logical_unit_reset(uas->dev.addr, tag, lun); - qdev_reset_all(&dev->qdev); - usb_uas_queue_response(uas, tag, UAS_RC_TMF_COMPLETE); - break; - - default: - trace_usb_uas_tmf_unsupported(uas->dev.addr, tag, iu->task.function); - usb_uas_queue_response(uas, tag, UAS_RC_TMF_NOT_SUPPORTED); - break; - } - return; - -invalid_tag: - usb_uas_queue_response(uas, tag, UAS_RC_INVALID_INFO_UNIT); - return; - -overlapped_tag: - usb_uas_queue_response(uas, req->tag, UAS_RC_OVERLAPPED_TAG); - return; - -incorrect_lun: - usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN); -} - -static void usb_uas_handle_data(USBDevice *dev, USBPacket *p) -{ - UASDevice *uas = USB_UAS(dev); - uas_iu iu; - UASStatus *st; - UASRequest *req; - int length; - - switch (p->ep->nr) { - case UAS_PIPE_ID_COMMAND: - length = MIN(sizeof(iu), p->iov.size); - usb_packet_copy(p, &iu, length); - switch (iu.hdr.id) { - case UAS_UI_COMMAND: - usb_uas_command(uas, &iu); - break; - case UAS_UI_TASK_MGMT: - usb_uas_task(uas, &iu); - break; - default: - error_report("%s: unknown command iu: id 0x%x", - __func__, iu.hdr.id); - p->status = USB_RET_STALL; - break; - } - break; - case UAS_PIPE_ID_STATUS: - if (p->stream) { - QTAILQ_FOREACH(st, &uas->results, next) { - if (st->stream == p->stream) { - break; - } - } - if (st == NULL) { - assert(uas->status3[p->stream] == NULL); - uas->status3[p->stream] = p; - p->status = USB_RET_ASYNC; - break; - } - } else { - st = QTAILQ_FIRST(&uas->results); - if (st == NULL) { - assert(uas->status2 == NULL); - uas->status2 = p; - p->status = USB_RET_ASYNC; - break; - } - } - usb_packet_copy(p, &st->status, st->length); - QTAILQ_REMOVE(&uas->results, st, next); - g_free(st); - break; - case UAS_PIPE_ID_DATA_IN: - case UAS_PIPE_ID_DATA_OUT: - if (p->stream) { - req = usb_uas_find_request(uas, p->stream); - } else { - req = (p->ep->nr == UAS_PIPE_ID_DATA_IN) - ? uas->datain2 : uas->dataout2; - } - if (req == NULL) { - if (p->stream) { - assert(uas->data3[p->stream] == NULL); - uas->data3[p->stream] = p; - p->status = USB_RET_ASYNC; - break; - } else { - error_report("%s: no inflight request", __func__); - p->status = USB_RET_STALL; - break; - } - } - scsi_req_ref(req->req); - req->data = p; - usb_uas_copy_data(req); - if (p->actual_length == p->iov.size || req->complete) { - req->data = NULL; - } else { - req->data_async = true; - p->status = USB_RET_ASYNC; - } - scsi_req_unref(req->req); - usb_uas_start_next_transfer(uas); - break; - default: - error_report("%s: invalid endpoint %d", __func__, p->ep->nr); - p->status = USB_RET_STALL; - break; - } -} - -static void usb_uas_handle_destroy(USBDevice *dev) -{ - UASDevice *uas = USB_UAS(dev); - - qemu_bh_delete(uas->status_bh); -} - -static void usb_uas_realize(USBDevice *dev, Error **errp) -{ - UASDevice *uas = USB_UAS(dev); - - usb_desc_create_serial(dev); - usb_desc_init(dev); - - QTAILQ_INIT(&uas->results); - QTAILQ_INIT(&uas->requests); - uas->status_bh = qemu_bh_new(usb_uas_send_status_bh, uas); - - scsi_bus_new(&uas->bus, sizeof(uas->bus), DEVICE(dev), - &usb_uas_scsi_info, NULL); -} - -static const VMStateDescription vmstate_usb_uas = { - .name = "usb-uas", - .unmigratable = 1, - .fields = (VMStateField[]) { - VMSTATE_USB_DEVICE(dev, UASDevice), - VMSTATE_END_OF_LIST() - } -}; - -static Property uas_properties[] = { - DEFINE_PROP_UINT32("log-scsi-req", UASDevice, requestlog, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_uas_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_uas_realize; - uc->product_desc = desc_strings[STR_PRODUCT]; - uc->usb_desc = &desc; - uc->cancel_packet = usb_uas_cancel_io; - uc->handle_attach = usb_desc_attach; - uc->handle_reset = usb_uas_handle_reset; - uc->handle_control = usb_uas_handle_control; - uc->handle_data = usb_uas_handle_data; - uc->handle_destroy = usb_uas_handle_destroy; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->fw_name = "storage"; - dc->vmsd = &vmstate_usb_uas; - dc->props = uas_properties; -} - -static const TypeInfo uas_info = { - .name = TYPE_USB_UAS, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(UASDevice), - .class_init = usb_uas_class_initfn, -}; - -static void usb_uas_register_types(void) -{ - type_register_static(&uas_info); -} - -type_init(usb_uas_register_types) diff --git a/qemu/hw/usb/dev-wacom.c b/qemu/hw/usb/dev-wacom.c deleted file mode 100644 index c4702dbba..000000000 --- a/qemu/hw/usb/dev-wacom.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Wacom PenPartner USB tablet emulation. - * - * Copyright (c) 2006 Openedhand Ltd. - * Author: Andrzej Zaborowski - * - * Based on hw/usb-hid.c: - * Copyright (c) 2005 Fabrice Bellard - * - * 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 "ui/console.h" -#include "hw/usb.h" -#include "hw/usb/desc.h" - -/* Interface requests */ -#define WACOM_GET_REPORT 0x2101 -#define WACOM_SET_REPORT 0x2109 - -/* HID interface requests */ -#define HID_GET_REPORT 0xa101 -#define HID_GET_IDLE 0xa102 -#define HID_GET_PROTOCOL 0xa103 -#define HID_SET_IDLE 0x210a -#define HID_SET_PROTOCOL 0x210b - -typedef struct USBWacomState { - USBDevice dev; - USBEndpoint *intr; - QEMUPutMouseEntry *eh_entry; - int dx, dy, dz, buttons_state; - int x, y; - int mouse_grabbed; - enum { - WACOM_MODE_HID = 1, - WACOM_MODE_WACOM = 2, - } mode; - uint8_t idle; - int changed; -} USBWacomState; - -#define TYPE_USB_WACOM "usb-wacom-tablet" -#define USB_WACOM(obj) OBJECT_CHECK(USBWacomState, (obj), TYPE_USB_WACOM) - -enum { - STR_MANUFACTURER = 1, - STR_PRODUCT, - STR_SERIALNUMBER, -}; - -static const USBDescStrings desc_strings = { - [STR_MANUFACTURER] = "QEMU", - [STR_PRODUCT] = "Wacom PenPartner", - [STR_SERIALNUMBER] = "1", -}; - -static const USBDescIface desc_iface_wacom = { - .bInterfaceNumber = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceSubClass = 0x01, /* boot */ - .bInterfaceProtocol = 0x02, - .ndesc = 1, - .descs = (USBDescOther[]) { - { - /* HID descriptor */ - .data = (uint8_t[]) { - 0x09, /* u8 bLength */ - 0x21, /* u8 bDescriptorType */ - 0x01, 0x10, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - 0x22, /* u8 type: Report */ - 0x6e, 0, /* u16 len */ - }, - }, - }, - .eps = (USBDescEndpoint[]) { - { - .bEndpointAddress = USB_DIR_IN | 0x01, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = 8, - .bInterval = 0x0a, - }, - }, -}; - -static const USBDescDevice desc_device_wacom = { - .bcdUSB = 0x0110, - .bMaxPacketSize0 = 8, - .bNumConfigurations = 1, - .confs = (USBDescConfig[]) { - { - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .bmAttributes = USB_CFG_ATT_ONE, - .bMaxPower = 40, - .nif = 1, - .ifs = &desc_iface_wacom, - }, - }, -}; - -static const USBDesc desc_wacom = { - .id = { - .idVendor = 0x056a, - .idProduct = 0x0000, - .bcdDevice = 0x4210, - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT, - .iSerialNumber = STR_SERIALNUMBER, - }, - .full = &desc_device_wacom, - .str = desc_strings, -}; - -static void usb_mouse_event(void *opaque, - int dx1, int dy1, int dz1, int buttons_state) -{ - USBWacomState *s = opaque; - - s->dx += dx1; - s->dy += dy1; - s->dz += dz1; - s->buttons_state = buttons_state; - s->changed = 1; - usb_wakeup(s->intr, 0); -} - -static void usb_wacom_event(void *opaque, - int x, int y, int dz, int buttons_state) -{ - USBWacomState *s = opaque; - - /* scale to Penpartner resolution */ - s->x = (x * 5040 / 0x7FFF); - s->y = (y * 3780 / 0x7FFF); - s->dz += dz; - s->buttons_state = buttons_state; - s->changed = 1; - usb_wakeup(s->intr, 0); -} - -static inline int int_clamp(int val, int vmin, int vmax) -{ - if (val < vmin) - return vmin; - else if (val > vmax) - return vmax; - else - return val; -} - -static int usb_mouse_poll(USBWacomState *s, uint8_t *buf, int len) -{ - int dx, dy, dz, b, l; - - if (!s->mouse_grabbed) { - s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s, 0, - "QEMU PenPartner tablet"); - qemu_activate_mouse_event_handler(s->eh_entry); - s->mouse_grabbed = 1; - } - - dx = int_clamp(s->dx, -128, 127); - dy = int_clamp(s->dy, -128, 127); - dz = int_clamp(s->dz, -128, 127); - - s->dx -= dx; - s->dy -= dy; - s->dz -= dz; - - b = 0; - if (s->buttons_state & MOUSE_EVENT_LBUTTON) - b |= 0x01; - if (s->buttons_state & MOUSE_EVENT_RBUTTON) - b |= 0x02; - if (s->buttons_state & MOUSE_EVENT_MBUTTON) - b |= 0x04; - - buf[0] = b; - buf[1] = dx; - buf[2] = dy; - l = 3; - if (len >= 4) { - buf[3] = dz; - l = 4; - } - return l; -} - -static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len) -{ - int b; - - if (!s->mouse_grabbed) { - s->eh_entry = qemu_add_mouse_event_handler(usb_wacom_event, s, 1, - "QEMU PenPartner tablet"); - qemu_activate_mouse_event_handler(s->eh_entry); - s->mouse_grabbed = 1; - } - - b = 0; - if (s->buttons_state & MOUSE_EVENT_LBUTTON) - b |= 0x01; - if (s->buttons_state & MOUSE_EVENT_RBUTTON) - b |= 0x40; - if (s->buttons_state & MOUSE_EVENT_MBUTTON) - b |= 0x20; /* eraser */ - - if (len < 7) - return 0; - - buf[0] = s->mode; - buf[5] = 0x00 | (b & 0xf0); - buf[1] = s->x & 0xff; - buf[2] = s->x >> 8; - buf[3] = s->y & 0xff; - buf[4] = s->y >> 8; - if (b & 0x3f) { - buf[6] = 0; - } else { - buf[6] = (unsigned char) -127; - } - - return 7; -} - -static void usb_wacom_handle_reset(USBDevice *dev) -{ - USBWacomState *s = (USBWacomState *) dev; - - s->dx = 0; - s->dy = 0; - s->dz = 0; - s->x = 0; - s->y = 0; - s->buttons_state = 0; - s->mode = WACOM_MODE_HID; -} - -static void usb_wacom_handle_control(USBDevice *dev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) -{ - USBWacomState *s = (USBWacomState *) dev; - int ret; - - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; - } - - switch (request) { - case WACOM_SET_REPORT: - if (s->mouse_grabbed) { - qemu_remove_mouse_event_handler(s->eh_entry); - s->mouse_grabbed = 0; - } - s->mode = data[0]; - break; - case WACOM_GET_REPORT: - data[0] = 0; - data[1] = s->mode; - p->actual_length = 2; - break; - /* USB HID requests */ - case HID_GET_REPORT: - if (s->mode == WACOM_MODE_HID) - p->actual_length = usb_mouse_poll(s, data, length); - else if (s->mode == WACOM_MODE_WACOM) - p->actual_length = usb_wacom_poll(s, data, length); - break; - case HID_GET_IDLE: - data[0] = s->idle; - p->actual_length = 1; - break; - case HID_SET_IDLE: - s->idle = (uint8_t) (value >> 8); - break; - default: - p->status = USB_RET_STALL; - break; - } -} - -static void usb_wacom_handle_data(USBDevice *dev, USBPacket *p) -{ - USBWacomState *s = (USBWacomState *) dev; - uint8_t buf[p->iov.size]; - int len = 0; - - switch (p->pid) { - case USB_TOKEN_IN: - if (p->ep->nr == 1) { - if (!(s->changed || s->idle)) { - p->status = USB_RET_NAK; - return; - } - s->changed = 0; - if (s->mode == WACOM_MODE_HID) - len = usb_mouse_poll(s, buf, p->iov.size); - else if (s->mode == WACOM_MODE_WACOM) - len = usb_wacom_poll(s, buf, p->iov.size); - usb_packet_copy(p, buf, len); - break; - } - /* Fall through. */ - case USB_TOKEN_OUT: - default: - p->status = USB_RET_STALL; - } -} - -static void usb_wacom_handle_destroy(USBDevice *dev) -{ - USBWacomState *s = (USBWacomState *) dev; - - if (s->mouse_grabbed) { - qemu_remove_mouse_event_handler(s->eh_entry); - s->mouse_grabbed = 0; - } -} - -static void usb_wacom_realize(USBDevice *dev, Error **errp) -{ - USBWacomState *s = USB_WACOM(dev); - usb_desc_create_serial(dev); - usb_desc_init(dev); - s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); - s->changed = 1; -} - -static const VMStateDescription vmstate_usb_wacom = { - .name = "usb-wacom", - .unmigratable = 1, -}; - -static void usb_wacom_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->product_desc = "QEMU PenPartner Tablet"; - uc->usb_desc = &desc_wacom; - uc->realize = usb_wacom_realize; - uc->handle_reset = usb_wacom_handle_reset; - uc->handle_control = usb_wacom_handle_control; - uc->handle_data = usb_wacom_handle_data; - uc->handle_destroy = usb_wacom_handle_destroy; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - dc->desc = "QEMU PenPartner Tablet"; - dc->vmsd = &vmstate_usb_wacom; -} - -static const TypeInfo wacom_info = { - .name = TYPE_USB_WACOM, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBWacomState), - .class_init = usb_wacom_class_init, -}; - -static void usb_wacom_register_types(void) -{ - type_register_static(&wacom_info); - usb_legacy_register(TYPE_USB_WACOM, "wacom-tablet", NULL); -} - -type_init(usb_wacom_register_types) diff --git a/qemu/hw/usb/hcd-ehci-pci.c b/qemu/hw/usb/hcd-ehci-pci.c deleted file mode 100644 index 56577051e..000000000 --- a/qemu/hw/usb/hcd-ehci-pci.c +++ /dev/null @@ -1,272 +0,0 @@ -/* - * QEMU USB EHCI Emulation - * - * 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 General Public License - * along with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/usb/hcd-ehci.h" -#include "qemu/range.h" - -typedef struct EHCIPCIInfo { - const char *name; - uint16_t vendor_id; - uint16_t device_id; - uint8_t revision; - bool companion; -} EHCIPCIInfo; - -static void usb_ehci_pci_realize(PCIDevice *dev, Error **errp) -{ - EHCIPCIState *i = PCI_EHCI(dev); - EHCIState *s = &i->ehci; - uint8_t *pci_conf = dev->config; - - pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20); - - /* capabilities pointer */ - pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00); - /* pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50); */ - - pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); /* interrupt pin D */ - pci_set_byte(&pci_conf[PCI_MIN_GNT], 0); - pci_set_byte(&pci_conf[PCI_MAX_LAT], 0); - - /* pci_conf[0x50] = 0x01; *//* power management caps */ - - pci_set_byte(&pci_conf[USB_SBRN], USB_RELEASE_2); /* release # (2.1.4) */ - pci_set_byte(&pci_conf[0x61], 0x20); /* frame length adjustment (2.1.5) */ - pci_set_word(&pci_conf[0x62], 0x00); /* port wake up capability (2.1.6) */ - - pci_conf[0x64] = 0x00; - pci_conf[0x65] = 0x00; - pci_conf[0x66] = 0x00; - pci_conf[0x67] = 0x00; - pci_conf[0x68] = 0x01; - pci_conf[0x69] = 0x00; - pci_conf[0x6a] = 0x00; - pci_conf[0x6b] = 0x00; /* USBLEGSUP */ - pci_conf[0x6c] = 0x00; - pci_conf[0x6d] = 0x00; - pci_conf[0x6e] = 0x00; - pci_conf[0x6f] = 0xc0; /* USBLEFCTLSTS */ - - s->irq = pci_allocate_irq(dev); - s->as = pci_get_address_space(dev); - - usb_ehci_realize(s, DEVICE(dev), NULL); - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); -} - -static void usb_ehci_pci_init(Object *obj) -{ - DeviceClass *dc = OBJECT_GET_CLASS(DeviceClass, obj, TYPE_DEVICE); - EHCIPCIState *i = PCI_EHCI(obj); - EHCIState *s = &i->ehci; - - s->caps[0x09] = 0x68; /* EECP */ - - s->capsbase = 0x00; - s->opregbase = 0x20; - s->portscbase = 0x44; - s->portnr = NB_PORTS; - - if (!dc->hotpluggable) { - s->companion_enable = true; - } - - usb_ehci_init(s, DEVICE(obj)); -} - -static void usb_ehci_pci_exit(PCIDevice *dev) -{ - EHCIPCIState *i = PCI_EHCI(dev); - EHCIState *s = &i->ehci; - - usb_ehci_unrealize(s, DEVICE(dev), NULL); - - g_free(s->irq); - s->irq = NULL; -} - -static void usb_ehci_pci_reset(DeviceState *dev) -{ - PCIDevice *pci_dev = PCI_DEVICE(dev); - EHCIPCIState *i = PCI_EHCI(pci_dev); - EHCIState *s = &i->ehci; - - ehci_reset(s); -} - -static void usb_ehci_pci_write_config(PCIDevice *dev, uint32_t addr, - uint32_t val, int l) -{ - EHCIPCIState *i = PCI_EHCI(dev); - bool busmaster; - - pci_default_write_config(dev, addr, val, l); - - if (!range_covers_byte(addr, l, PCI_COMMAND)) { - return; - } - busmaster = pci_get_word(dev->config + PCI_COMMAND) & PCI_COMMAND_MASTER; - i->ehci.as = busmaster ? pci_get_address_space(dev) : &address_space_memory; -} - -static Property ehci_pci_properties[] = { - DEFINE_PROP_UINT32("maxframes", EHCIPCIState, ehci.maxframes, 128), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription vmstate_ehci_pci = { - .name = "ehci", - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(pcidev, EHCIPCIState), - VMSTATE_STRUCT(ehci, EHCIPCIState, 2, vmstate_ehci, EHCIState), - VMSTATE_END_OF_LIST() - } -}; - -static void ehci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = usb_ehci_pci_realize; - k->exit = usb_ehci_pci_exit; - k->class_id = PCI_CLASS_SERIAL_USB; - k->config_write = usb_ehci_pci_write_config; - dc->vmsd = &vmstate_ehci_pci; - dc->props = ehci_pci_properties; - dc->reset = usb_ehci_pci_reset; -} - -static const TypeInfo ehci_pci_type_info = { - .name = TYPE_PCI_EHCI, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(EHCIPCIState), - .instance_init = usb_ehci_pci_init, - .abstract = true, - .class_init = ehci_class_init, -}; - -static void ehci_data_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - EHCIPCIInfo *i = data; - - k->vendor_id = i->vendor_id; - k->device_id = i->device_id; - k->revision = i->revision; - set_bit(DEVICE_CATEGORY_USB, dc->categories); - if (i->companion) { - dc->hotpluggable = false; - } -} - -static struct EHCIPCIInfo ehci_pci_info[] = { - { - .name = "usb-ehci", - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801D, /* ich4 */ - .revision = 0x10, - },{ - .name = "ich9-usb-ehci1", /* 00:1d.7 */ - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1, - .revision = 0x03, - .companion = true, - },{ - .name = "ich9-usb-ehci2", /* 00:1a.7 */ - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI2, - .revision = 0x03, - .companion = true, - } -}; - -static void ehci_pci_register_types(void) -{ - TypeInfo ehci_type_info = { - .parent = TYPE_PCI_EHCI, - .class_init = ehci_data_class_init, - }; - int i; - - type_register_static(&ehci_pci_type_info); - - for (i = 0; i < ARRAY_SIZE(ehci_pci_info); i++) { - ehci_type_info.name = ehci_pci_info[i].name; - ehci_type_info.class_data = ehci_pci_info + i; - type_register(&ehci_type_info); - } -} - -type_init(ehci_pci_register_types) - -struct ehci_companions { - const char *name; - int func; - int port; -}; - -static const struct ehci_companions ich9_1d[] = { - { .name = "ich9-usb-uhci1", .func = 0, .port = 0 }, - { .name = "ich9-usb-uhci2", .func = 1, .port = 2 }, - { .name = "ich9-usb-uhci3", .func = 2, .port = 4 }, -}; - -static const struct ehci_companions ich9_1a[] = { - { .name = "ich9-usb-uhci4", .func = 0, .port = 0 }, - { .name = "ich9-usb-uhci5", .func = 1, .port = 2 }, - { .name = "ich9-usb-uhci6", .func = 2, .port = 4 }, -}; - -int ehci_create_ich9_with_companions(PCIBus *bus, int slot) -{ - const struct ehci_companions *comp; - PCIDevice *ehci, *uhci; - BusState *usbbus; - const char *name; - int i; - - switch (slot) { - case 0x1d: - name = "ich9-usb-ehci1"; - comp = ich9_1d; - break; - case 0x1a: - name = "ich9-usb-ehci2"; - comp = ich9_1a; - break; - default: - return -1; - } - - ehci = pci_create_multifunction(bus, PCI_DEVFN(slot, 7), true, name); - qdev_init_nofail(&ehci->qdev); - usbbus = QLIST_FIRST(&ehci->qdev.child_bus); - - for (i = 0; i < 3; i++) { - uhci = pci_create_multifunction(bus, PCI_DEVFN(slot, comp[i].func), - true, comp[i].name); - qdev_prop_set_string(&uhci->qdev, "masterbus", usbbus->name); - qdev_prop_set_uint32(&uhci->qdev, "firstport", comp[i].port); - qdev_init_nofail(&uhci->qdev); - } - return 0; -} diff --git a/qemu/hw/usb/hcd-ehci-sysbus.c b/qemu/hw/usb/hcd-ehci-sysbus.c deleted file mode 100644 index 6c20604d0..000000000 --- a/qemu/hw/usb/hcd-ehci-sysbus.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * QEMU USB EHCI Emulation - * - * 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 General Public License - * along with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/usb/hcd-ehci.h" - -static const VMStateDescription vmstate_ehci_sysbus = { - .name = "ehci-sysbus", - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(ehci, EHCISysBusState, 2, vmstate_ehci, EHCIState), - VMSTATE_END_OF_LIST() - } -}; - -static Property ehci_sysbus_properties[] = { - DEFINE_PROP_UINT32("maxframes", EHCISysBusState, ehci.maxframes, 128), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_ehci_sysbus_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *d = SYS_BUS_DEVICE(dev); - EHCISysBusState *i = SYS_BUS_EHCI(dev); - EHCIState *s = &i->ehci; - - usb_ehci_realize(s, dev, errp); - sysbus_init_irq(d, &s->irq); -} - -static void usb_ehci_sysbus_reset(DeviceState *dev) -{ - SysBusDevice *d = SYS_BUS_DEVICE(dev); - EHCISysBusState *i = SYS_BUS_EHCI(d); - EHCIState *s = &i->ehci; - - ehci_reset(s); -} - -static void ehci_sysbus_init(Object *obj) -{ - SysBusDevice *d = SYS_BUS_DEVICE(obj); - EHCISysBusState *i = SYS_BUS_EHCI(obj); - SysBusEHCIClass *sec = SYS_BUS_EHCI_GET_CLASS(obj); - EHCIState *s = &i->ehci; - - s->capsbase = sec->capsbase; - s->opregbase = sec->opregbase; - s->portscbase = sec->portscbase; - s->portnr = sec->portnr; - s->as = &address_space_memory; - - usb_ehci_init(s, DEVICE(obj)); - sysbus_init_mmio(d, &s->mem); -} - -static void ehci_sysbus_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(klass); - - sec->portscbase = 0x44; - sec->portnr = NB_PORTS; - - dc->realize = usb_ehci_sysbus_realize; - dc->vmsd = &vmstate_ehci_sysbus; - dc->props = ehci_sysbus_properties; - dc->reset = usb_ehci_sysbus_reset; - set_bit(DEVICE_CATEGORY_USB, dc->categories); -} - -static const TypeInfo ehci_type_info = { - .name = TYPE_SYS_BUS_EHCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(EHCISysBusState), - .instance_init = ehci_sysbus_init, - .abstract = true, - .class_init = ehci_sysbus_class_init, - .class_size = sizeof(SysBusEHCIClass), -}; - -static void ehci_xlnx_class_init(ObjectClass *oc, void *data) -{ - SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); - DeviceClass *dc = DEVICE_CLASS(oc); - - set_bit(DEVICE_CATEGORY_USB, dc->categories); - sec->capsbase = 0x100; - sec->opregbase = 0x140; -} - -static const TypeInfo ehci_xlnx_type_info = { - .name = "xlnx,ps7-usb", - .parent = TYPE_SYS_BUS_EHCI, - .class_init = ehci_xlnx_class_init, -}; - -static void ehci_exynos4210_class_init(ObjectClass *oc, void *data) -{ - SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); - DeviceClass *dc = DEVICE_CLASS(oc); - - sec->capsbase = 0x0; - sec->opregbase = 0x10; - set_bit(DEVICE_CATEGORY_USB, dc->categories); -} - -static const TypeInfo ehci_exynos4210_type_info = { - .name = TYPE_EXYNOS4210_EHCI, - .parent = TYPE_SYS_BUS_EHCI, - .class_init = ehci_exynos4210_class_init, -}; - -static void ehci_tegra2_class_init(ObjectClass *oc, void *data) -{ - SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); - DeviceClass *dc = DEVICE_CLASS(oc); - - sec->capsbase = 0x100; - sec->opregbase = 0x140; - set_bit(DEVICE_CATEGORY_USB, dc->categories); -} - -static const TypeInfo ehci_tegra2_type_info = { - .name = TYPE_TEGRA2_EHCI, - .parent = TYPE_SYS_BUS_EHCI, - .class_init = ehci_tegra2_class_init, -}; - -/* - * Faraday FUSBH200 USB 2.0 EHCI - */ - -/** - * FUSBH200EHCIRegs: - * @FUSBH200_REG_EOF_ASTR: EOF/Async. Sleep Timer Register - * @FUSBH200_REG_BMCSR: Bus Monitor Control/Status Register - */ -enum FUSBH200EHCIRegs { - FUSBH200_REG_EOF_ASTR = 0x34, - FUSBH200_REG_BMCSR = 0x40, -}; - -static uint64_t fusbh200_ehci_read(void *opaque, hwaddr addr, unsigned size) -{ - EHCIState *s = opaque; - hwaddr off = s->opregbase + s->portscbase + 4 * s->portnr + addr; - - switch (off) { - case FUSBH200_REG_EOF_ASTR: - return 0x00000041; - case FUSBH200_REG_BMCSR: - /* High-Speed, VBUS valid, interrupt level-high active */ - return (2 << 9) | (1 << 8) | (1 << 3); - } - - return 0; -} - -static void fusbh200_ehci_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ -} - -static const MemoryRegionOps fusbh200_ehci_mmio_ops = { - .read = fusbh200_ehci_read, - .write = fusbh200_ehci_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void fusbh200_ehci_init(Object *obj) -{ - EHCISysBusState *i = SYS_BUS_EHCI(obj); - FUSBH200EHCIState *f = FUSBH200_EHCI(obj); - EHCIState *s = &i->ehci; - - memory_region_init_io(&f->mem_vendor, OBJECT(f), &fusbh200_ehci_mmio_ops, s, - "fusbh200", 0x4c); - memory_region_add_subregion(&s->mem, - s->opregbase + s->portscbase + 4 * s->portnr, - &f->mem_vendor); -} - -static void fusbh200_ehci_class_init(ObjectClass *oc, void *data) -{ - SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); - DeviceClass *dc = DEVICE_CLASS(oc); - - sec->capsbase = 0x0; - sec->opregbase = 0x10; - sec->portscbase = 0x20; - sec->portnr = 1; - set_bit(DEVICE_CATEGORY_USB, dc->categories); -} - -static const TypeInfo ehci_fusbh200_type_info = { - .name = TYPE_FUSBH200_EHCI, - .parent = TYPE_SYS_BUS_EHCI, - .instance_size = sizeof(FUSBH200EHCIState), - .instance_init = fusbh200_ehci_init, - .class_init = fusbh200_ehci_class_init, -}; - -static void ehci_sysbus_register_types(void) -{ - type_register_static(&ehci_type_info); - type_register_static(&ehci_xlnx_type_info); - type_register_static(&ehci_exynos4210_type_info); - type_register_static(&ehci_tegra2_type_info); - type_register_static(&ehci_fusbh200_type_info); -} - -type_init(ehci_sysbus_register_types) diff --git a/qemu/hw/usb/hcd-ehci.c b/qemu/hw/usb/hcd-ehci.c deleted file mode 100644 index 43a8f7abc..000000000 --- a/qemu/hw/usb/hcd-ehci.c +++ /dev/null @@ -1,2549 +0,0 @@ -/* - * QEMU USB EHCI Emulation - * - * Copyright(c) 2008 Emutex Ltd. (address@hidden) - * Copyright(c) 2011-2012 Red Hat, Inc. - * - * Red Hat Authors: - * Gerd Hoffmann - * Hans de Goede - * - * EHCI project was started by Mark Burkley, with contributions by - * Niels de Vos. David S. Ahern continued working on it. Kevin Wolf, - * Jan Kiszka and Vincent Palatin contributed bugfixes. - * - * - * 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 General Public License - * along with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/usb/ehci-regs.h" -#include "hw/usb/hcd-ehci.h" -#include "trace.h" - -#define FRAME_TIMER_FREQ 1000 -#define FRAME_TIMER_NS (NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ) -#define UFRAME_TIMER_NS (FRAME_TIMER_NS / 8) - -#define NB_MAXINTRATE 8 // Max rate at which controller issues ints -#define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction -#define MAX_QH 100 // Max allowable queue heads in a chain -#define MIN_UFR_PER_TICK 24 /* Min frames to process when catching up */ -#define PERIODIC_ACTIVE 512 /* Micro-frames */ - -/* Internal periodic / asynchronous schedule state machine states - */ -typedef enum { - EST_INACTIVE = 1000, - EST_ACTIVE, - EST_EXECUTING, - EST_SLEEPING, - /* The following states are internal to the state machine function - */ - EST_WAITLISTHEAD, - EST_FETCHENTRY, - EST_FETCHQH, - EST_FETCHITD, - EST_FETCHSITD, - EST_ADVANCEQUEUE, - EST_FETCHQTD, - EST_EXECUTE, - EST_WRITEBACK, - EST_HORIZONTALQH -} EHCI_STATES; - -/* macros for accessing fields within next link pointer entry */ -#define NLPTR_GET(x) ((x) & 0xffffffe0) -#define NLPTR_TYPE_GET(x) (((x) >> 1) & 3) -#define NLPTR_TBIT(x) ((x) & 1) // 1=invalid, 0=valid - -/* link pointer types */ -#define NLPTR_TYPE_ITD 0 // isoc xfer descriptor -#define NLPTR_TYPE_QH 1 // queue head -#define NLPTR_TYPE_STITD 2 // split xaction, isoc xfer descriptor -#define NLPTR_TYPE_FSTN 3 // frame span traversal node - -#define SET_LAST_RUN_CLOCK(s) \ - (s)->last_run_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - -/* nifty macros from Arnon's EHCI version */ -#define get_field(data, field) \ - (((data) & field##_MASK) >> field##_SH) - -#define set_field(data, newval, field) do { \ - uint32_t val = *data; \ - val &= ~ field##_MASK; \ - val |= ((newval) << field##_SH) & field##_MASK; \ - *data = val; \ - } while(0) - -static const char *ehci_state_names[] = { - [EST_INACTIVE] = "INACTIVE", - [EST_ACTIVE] = "ACTIVE", - [EST_EXECUTING] = "EXECUTING", - [EST_SLEEPING] = "SLEEPING", - [EST_WAITLISTHEAD] = "WAITLISTHEAD", - [EST_FETCHENTRY] = "FETCH ENTRY", - [EST_FETCHQH] = "FETCH QH", - [EST_FETCHITD] = "FETCH ITD", - [EST_ADVANCEQUEUE] = "ADVANCEQUEUE", - [EST_FETCHQTD] = "FETCH QTD", - [EST_EXECUTE] = "EXECUTE", - [EST_WRITEBACK] = "WRITEBACK", - [EST_HORIZONTALQH] = "HORIZONTALQH", -}; - -static const char *ehci_mmio_names[] = { - [USBCMD] = "USBCMD", - [USBSTS] = "USBSTS", - [USBINTR] = "USBINTR", - [FRINDEX] = "FRINDEX", - [PERIODICLISTBASE] = "P-LIST BASE", - [ASYNCLISTADDR] = "A-LIST ADDR", - [CONFIGFLAG] = "CONFIGFLAG", -}; - -static int ehci_state_executing(EHCIQueue *q); -static int ehci_state_writeback(EHCIQueue *q); -static int ehci_state_advqueue(EHCIQueue *q); -static int ehci_fill_queue(EHCIPacket *p); -static void ehci_free_packet(EHCIPacket *p); - -static const char *nr2str(const char **n, size_t len, uint32_t nr) -{ - if (nr < len && n[nr] != NULL) { - return n[nr]; - } else { - return "unknown"; - } -} - -static const char *state2str(uint32_t state) -{ - return nr2str(ehci_state_names, ARRAY_SIZE(ehci_state_names), state); -} - -static const char *addr2str(hwaddr addr) -{ - return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr); -} - -static void ehci_trace_usbsts(uint32_t mask, int state) -{ - /* interrupts */ - if (mask & USBSTS_INT) { - trace_usb_ehci_usbsts("INT", state); - } - if (mask & USBSTS_ERRINT) { - trace_usb_ehci_usbsts("ERRINT", state); - } - if (mask & USBSTS_PCD) { - trace_usb_ehci_usbsts("PCD", state); - } - if (mask & USBSTS_FLR) { - trace_usb_ehci_usbsts("FLR", state); - } - if (mask & USBSTS_HSE) { - trace_usb_ehci_usbsts("HSE", state); - } - if (mask & USBSTS_IAA) { - trace_usb_ehci_usbsts("IAA", state); - } - - /* status */ - if (mask & USBSTS_HALT) { - trace_usb_ehci_usbsts("HALT", state); - } - if (mask & USBSTS_REC) { - trace_usb_ehci_usbsts("REC", state); - } - if (mask & USBSTS_PSS) { - trace_usb_ehci_usbsts("PSS", state); - } - if (mask & USBSTS_ASS) { - trace_usb_ehci_usbsts("ASS", state); - } -} - -static inline void ehci_set_usbsts(EHCIState *s, int mask) -{ - if ((s->usbsts & mask) == mask) { - return; - } - ehci_trace_usbsts(mask, 1); - s->usbsts |= mask; -} - -static inline void ehci_clear_usbsts(EHCIState *s, int mask) -{ - if ((s->usbsts & mask) == 0) { - return; - } - ehci_trace_usbsts(mask, 0); - s->usbsts &= ~mask; -} - -/* update irq line */ -static inline void ehci_update_irq(EHCIState *s) -{ - int level = 0; - - if ((s->usbsts & USBINTR_MASK) & s->usbintr) { - level = 1; - } - - trace_usb_ehci_irq(level, s->frindex, s->usbsts, s->usbintr); - qemu_set_irq(s->irq, level); -} - -/* flag interrupt condition */ -static inline void ehci_raise_irq(EHCIState *s, int intr) -{ - if (intr & (USBSTS_PCD | USBSTS_FLR | USBSTS_HSE)) { - s->usbsts |= intr; - ehci_update_irq(s); - } else { - s->usbsts_pending |= intr; - } -} - -/* - * Commit pending interrupts (added via ehci_raise_irq), - * at the rate allowed by "Interrupt Threshold Control". - */ -static inline void ehci_commit_irq(EHCIState *s) -{ - uint32_t itc; - - if (!s->usbsts_pending) { - return; - } - if (s->usbsts_frindex > s->frindex) { - return; - } - - itc = (s->usbcmd >> 16) & 0xff; - s->usbsts |= s->usbsts_pending; - s->usbsts_pending = 0; - s->usbsts_frindex = s->frindex + itc; - ehci_update_irq(s); -} - -static void ehci_update_halt(EHCIState *s) -{ - if (s->usbcmd & USBCMD_RUNSTOP) { - ehci_clear_usbsts(s, USBSTS_HALT); - } else { - if (s->astate == EST_INACTIVE && s->pstate == EST_INACTIVE) { - ehci_set_usbsts(s, USBSTS_HALT); - } - } -} - -static void ehci_set_state(EHCIState *s, int async, int state) -{ - if (async) { - trace_usb_ehci_state("async", state2str(state)); - s->astate = state; - if (s->astate == EST_INACTIVE) { - ehci_clear_usbsts(s, USBSTS_ASS); - ehci_update_halt(s); - } else { - ehci_set_usbsts(s, USBSTS_ASS); - } - } else { - trace_usb_ehci_state("periodic", state2str(state)); - s->pstate = state; - if (s->pstate == EST_INACTIVE) { - ehci_clear_usbsts(s, USBSTS_PSS); - ehci_update_halt(s); - } else { - ehci_set_usbsts(s, USBSTS_PSS); - } - } -} - -static int ehci_get_state(EHCIState *s, int async) -{ - return async ? s->astate : s->pstate; -} - -static void ehci_set_fetch_addr(EHCIState *s, int async, uint32_t addr) -{ - if (async) { - s->a_fetch_addr = addr; - } else { - s->p_fetch_addr = addr; - } -} - -static int ehci_get_fetch_addr(EHCIState *s, int async) -{ - return async ? s->a_fetch_addr : s->p_fetch_addr; -} - -static void ehci_trace_qh(EHCIQueue *q, hwaddr addr, EHCIqh *qh) -{ - /* need three here due to argument count limits */ - trace_usb_ehci_qh_ptrs(q, addr, qh->next, - qh->current_qtd, qh->next_qtd, qh->altnext_qtd); - trace_usb_ehci_qh_fields(addr, - get_field(qh->epchar, QH_EPCHAR_RL), - get_field(qh->epchar, QH_EPCHAR_MPLEN), - get_field(qh->epchar, QH_EPCHAR_EPS), - get_field(qh->epchar, QH_EPCHAR_EP), - get_field(qh->epchar, QH_EPCHAR_DEVADDR)); - trace_usb_ehci_qh_bits(addr, - (bool)(qh->epchar & QH_EPCHAR_C), - (bool)(qh->epchar & QH_EPCHAR_H), - (bool)(qh->epchar & QH_EPCHAR_DTC), - (bool)(qh->epchar & QH_EPCHAR_I)); -} - -static void ehci_trace_qtd(EHCIQueue *q, hwaddr addr, EHCIqtd *qtd) -{ - /* need three here due to argument count limits */ - trace_usb_ehci_qtd_ptrs(q, addr, qtd->next, qtd->altnext); - trace_usb_ehci_qtd_fields(addr, - get_field(qtd->token, QTD_TOKEN_TBYTES), - get_field(qtd->token, QTD_TOKEN_CPAGE), - get_field(qtd->token, QTD_TOKEN_CERR), - get_field(qtd->token, QTD_TOKEN_PID)); - trace_usb_ehci_qtd_bits(addr, - (bool)(qtd->token & QTD_TOKEN_IOC), - (bool)(qtd->token & QTD_TOKEN_ACTIVE), - (bool)(qtd->token & QTD_TOKEN_HALT), - (bool)(qtd->token & QTD_TOKEN_BABBLE), - (bool)(qtd->token & QTD_TOKEN_XACTERR)); -} - -static void ehci_trace_itd(EHCIState *s, hwaddr addr, EHCIitd *itd) -{ - trace_usb_ehci_itd(addr, itd->next, - get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT), - get_field(itd->bufptr[2], ITD_BUFPTR_MULT), - get_field(itd->bufptr[0], ITD_BUFPTR_EP), - get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR)); -} - -static void ehci_trace_sitd(EHCIState *s, hwaddr addr, - EHCIsitd *sitd) -{ - trace_usb_ehci_sitd(addr, sitd->next, - (bool)(sitd->results & SITD_RESULTS_ACTIVE)); -} - -static void ehci_trace_guest_bug(EHCIState *s, const char *message) -{ - trace_usb_ehci_guest_bug(message); - fprintf(stderr, "ehci warning: %s\n", message); -} - -static inline bool ehci_enabled(EHCIState *s) -{ - return s->usbcmd & USBCMD_RUNSTOP; -} - -static inline bool ehci_async_enabled(EHCIState *s) -{ - return ehci_enabled(s) && (s->usbcmd & USBCMD_ASE); -} - -static inline bool ehci_periodic_enabled(EHCIState *s) -{ - return ehci_enabled(s) && (s->usbcmd & USBCMD_PSE); -} - -/* Get an array of dwords from main memory */ -static inline int get_dwords(EHCIState *ehci, uint32_t addr, - uint32_t *buf, int num) -{ - int i; - - if (!ehci->as) { - ehci_raise_irq(ehci, USBSTS_HSE); - ehci->usbcmd &= ~USBCMD_RUNSTOP; - trace_usb_ehci_dma_error(); - return -1; - } - - for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - dma_memory_read(ehci->as, addr, buf, sizeof(*buf)); - *buf = le32_to_cpu(*buf); - } - - return num; -} - -/* Put an array of dwords in to main memory */ -static inline int put_dwords(EHCIState *ehci, uint32_t addr, - uint32_t *buf, int num) -{ - int i; - - if (!ehci->as) { - ehci_raise_irq(ehci, USBSTS_HSE); - ehci->usbcmd &= ~USBCMD_RUNSTOP; - trace_usb_ehci_dma_error(); - return -1; - } - - for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - uint32_t tmp = cpu_to_le32(*buf); - dma_memory_write(ehci->as, addr, &tmp, sizeof(tmp)); - } - - return num; -} - -static int ehci_get_pid(EHCIqtd *qtd) -{ - switch (get_field(qtd->token, QTD_TOKEN_PID)) { - case 0: - return USB_TOKEN_OUT; - case 1: - return USB_TOKEN_IN; - case 2: - return USB_TOKEN_SETUP; - default: - fprintf(stderr, "bad token\n"); - return 0; - } -} - -static bool ehci_verify_qh(EHCIQueue *q, EHCIqh *qh) -{ - uint32_t devaddr = get_field(qh->epchar, QH_EPCHAR_DEVADDR); - uint32_t endp = get_field(qh->epchar, QH_EPCHAR_EP); - if ((devaddr != get_field(q->qh.epchar, QH_EPCHAR_DEVADDR)) || - (endp != get_field(q->qh.epchar, QH_EPCHAR_EP)) || - (qh->current_qtd != q->qh.current_qtd) || - (q->async && qh->next_qtd != q->qh.next_qtd) || - (memcmp(&qh->altnext_qtd, &q->qh.altnext_qtd, - 7 * sizeof(uint32_t)) != 0) || - (q->dev != NULL && q->dev->addr != devaddr)) { - return false; - } else { - return true; - } -} - -static bool ehci_verify_qtd(EHCIPacket *p, EHCIqtd *qtd) -{ - if (p->qtdaddr != p->queue->qtdaddr || - (p->queue->async && !NLPTR_TBIT(p->qtd.next) && - (p->qtd.next != qtd->next)) || - (!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd->altnext)) || - p->qtd.token != qtd->token || - p->qtd.bufptr[0] != qtd->bufptr[0]) { - return false; - } else { - return true; - } -} - -static bool ehci_verify_pid(EHCIQueue *q, EHCIqtd *qtd) -{ - int ep = get_field(q->qh.epchar, QH_EPCHAR_EP); - int pid = ehci_get_pid(qtd); - - /* Note the pid changing is normal for ep 0 (the control ep) */ - if (q->last_pid && ep != 0 && pid != q->last_pid) { - return false; - } else { - return true; - } -} - -/* Finish executing and writeback a packet outside of the regular - fetchqh -> fetchqtd -> execute -> writeback cycle */ -static void ehci_writeback_async_complete_packet(EHCIPacket *p) -{ - EHCIQueue *q = p->queue; - EHCIqtd qtd; - EHCIqh qh; - int state; - - /* Verify the qh + qtd, like we do when going through fetchqh & fetchqtd */ - get_dwords(q->ehci, NLPTR_GET(q->qhaddr), - (uint32_t *) &qh, sizeof(EHCIqh) >> 2); - get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), - (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2); - if (!ehci_verify_qh(q, &qh) || !ehci_verify_qtd(p, &qtd)) { - p->async = EHCI_ASYNC_INITIALIZED; - ehci_free_packet(p); - return; - } - - state = ehci_get_state(q->ehci, q->async); - ehci_state_executing(q); - ehci_state_writeback(q); /* Frees the packet! */ - if (!(q->qh.token & QTD_TOKEN_HALT)) { - ehci_state_advqueue(q); - } - ehci_set_state(q->ehci, q->async, state); -} - -/* packet management */ - -static EHCIPacket *ehci_alloc_packet(EHCIQueue *q) -{ - EHCIPacket *p; - - p = g_new0(EHCIPacket, 1); - p->queue = q; - usb_packet_init(&p->packet); - QTAILQ_INSERT_TAIL(&q->packets, p, next); - trace_usb_ehci_packet_action(p->queue, p, "alloc"); - return p; -} - -static void ehci_free_packet(EHCIPacket *p) -{ - if (p->async == EHCI_ASYNC_FINISHED && - !(p->queue->qh.token & QTD_TOKEN_HALT)) { - ehci_writeback_async_complete_packet(p); - return; - } - trace_usb_ehci_packet_action(p->queue, p, "free"); - if (p->async == EHCI_ASYNC_INFLIGHT) { - usb_cancel_packet(&p->packet); - } - if (p->async == EHCI_ASYNC_FINISHED && - p->packet.status == USB_RET_SUCCESS) { - fprintf(stderr, - "EHCI: Dropping completed packet from halted %s ep %02X\n", - (p->pid == USB_TOKEN_IN) ? "in" : "out", - get_field(p->queue->qh.epchar, QH_EPCHAR_EP)); - } - if (p->async != EHCI_ASYNC_NONE) { - usb_packet_unmap(&p->packet, &p->sgl); - qemu_sglist_destroy(&p->sgl); - } - QTAILQ_REMOVE(&p->queue->packets, p, next); - usb_packet_cleanup(&p->packet); - g_free(p); -} - -/* queue management */ - -static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async) -{ - EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; - EHCIQueue *q; - - q = g_malloc0(sizeof(*q)); - q->ehci = ehci; - q->qhaddr = addr; - q->async = async; - QTAILQ_INIT(&q->packets); - QTAILQ_INSERT_HEAD(head, q, next); - trace_usb_ehci_queue_action(q, "alloc"); - return q; -} - -static void ehci_queue_stopped(EHCIQueue *q) -{ - int endp = get_field(q->qh.epchar, QH_EPCHAR_EP); - - if (!q->last_pid || !q->dev) { - return; - } - - usb_device_ep_stopped(q->dev, usb_ep_get(q->dev, q->last_pid, endp)); -} - -static int ehci_cancel_queue(EHCIQueue *q) -{ - EHCIPacket *p; - int packets = 0; - - p = QTAILQ_FIRST(&q->packets); - if (p == NULL) { - goto leave; - } - - trace_usb_ehci_queue_action(q, "cancel"); - do { - ehci_free_packet(p); - packets++; - } while ((p = QTAILQ_FIRST(&q->packets)) != NULL); - -leave: - ehci_queue_stopped(q); - return packets; -} - -static int ehci_reset_queue(EHCIQueue *q) -{ - int packets; - - trace_usb_ehci_queue_action(q, "reset"); - packets = ehci_cancel_queue(q); - q->dev = NULL; - q->qtdaddr = 0; - q->last_pid = 0; - return packets; -} - -static void ehci_free_queue(EHCIQueue *q, const char *warn) -{ - EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues; - int cancelled; - - trace_usb_ehci_queue_action(q, "free"); - cancelled = ehci_cancel_queue(q); - if (warn && cancelled > 0) { - ehci_trace_guest_bug(q->ehci, warn); - } - QTAILQ_REMOVE(head, q, next); - g_free(q); -} - -static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr, - int async) -{ - EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; - EHCIQueue *q; - - QTAILQ_FOREACH(q, head, next) { - if (addr == q->qhaddr) { - return q; - } - } - return NULL; -} - -static void ehci_queues_rip_unused(EHCIState *ehci, int async) -{ - EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; - const char *warn = async ? "guest unlinked busy QH" : NULL; - uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4; - EHCIQueue *q, *tmp; - - QTAILQ_FOREACH_SAFE(q, head, next, tmp) { - if (q->seen) { - q->seen = 0; - q->ts = ehci->last_run_ns; - continue; - } - if (ehci->last_run_ns < q->ts + maxage) { - continue; - } - ehci_free_queue(q, warn); - } -} - -static void ehci_queues_rip_unseen(EHCIState *ehci, int async) -{ - EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; - EHCIQueue *q, *tmp; - - QTAILQ_FOREACH_SAFE(q, head, next, tmp) { - if (!q->seen) { - ehci_free_queue(q, NULL); - } - } -} - -static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async) -{ - EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; - EHCIQueue *q, *tmp; - - QTAILQ_FOREACH_SAFE(q, head, next, tmp) { - if (q->dev != dev) { - continue; - } - ehci_free_queue(q, NULL); - } -} - -static void ehci_queues_rip_all(EHCIState *ehci, int async) -{ - EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; - const char *warn = async ? "guest stopped busy async schedule" : NULL; - EHCIQueue *q, *tmp; - - QTAILQ_FOREACH_SAFE(q, head, next, tmp) { - ehci_free_queue(q, warn); - } -} - -/* Attach or detach a device on root hub */ - -static void ehci_attach(USBPort *port) -{ - EHCIState *s = port->opaque; - uint32_t *portsc = &s->portsc[port->index]; - const char *owner = (*portsc & PORTSC_POWNER) ? "comp" : "ehci"; - - trace_usb_ehci_port_attach(port->index, owner, port->dev->product_desc); - - if (*portsc & PORTSC_POWNER) { - USBPort *companion = s->companion_ports[port->index]; - companion->dev = port->dev; - companion->ops->attach(companion); - return; - } - - *portsc |= PORTSC_CONNECT; - *portsc |= PORTSC_CSC; - - ehci_raise_irq(s, USBSTS_PCD); -} - -static void ehci_detach(USBPort *port) -{ - EHCIState *s = port->opaque; - uint32_t *portsc = &s->portsc[port->index]; - const char *owner = (*portsc & PORTSC_POWNER) ? "comp" : "ehci"; - - trace_usb_ehci_port_detach(port->index, owner); - - if (*portsc & PORTSC_POWNER) { - USBPort *companion = s->companion_ports[port->index]; - companion->ops->detach(companion); - companion->dev = NULL; - /* - * EHCI spec 4.2.2: "When a disconnect occurs... On the event, - * the port ownership is returned immediately to the EHCI controller." - */ - *portsc &= ~PORTSC_POWNER; - return; - } - - ehci_queues_rip_device(s, port->dev, 0); - ehci_queues_rip_device(s, port->dev, 1); - - *portsc &= ~(PORTSC_CONNECT|PORTSC_PED|PORTSC_SUSPEND); - *portsc |= PORTSC_CSC; - - ehci_raise_irq(s, USBSTS_PCD); -} - -static void ehci_child_detach(USBPort *port, USBDevice *child) -{ - EHCIState *s = port->opaque; - uint32_t portsc = s->portsc[port->index]; - - if (portsc & PORTSC_POWNER) { - USBPort *companion = s->companion_ports[port->index]; - companion->ops->child_detach(companion, child); - return; - } - - ehci_queues_rip_device(s, child, 0); - ehci_queues_rip_device(s, child, 1); -} - -static void ehci_wakeup(USBPort *port) -{ - EHCIState *s = port->opaque; - uint32_t *portsc = &s->portsc[port->index]; - - if (*portsc & PORTSC_POWNER) { - USBPort *companion = s->companion_ports[port->index]; - if (companion->ops->wakeup) { - companion->ops->wakeup(companion); - } - return; - } - - if (*portsc & PORTSC_SUSPEND) { - trace_usb_ehci_port_wakeup(port->index); - *portsc |= PORTSC_FPRES; - ehci_raise_irq(s, USBSTS_PCD); - } - - qemu_bh_schedule(s->async_bh); -} - -static void ehci_register_companion(USBBus *bus, USBPort *ports[], - uint32_t portcount, uint32_t firstport, - Error **errp) -{ - EHCIState *s = container_of(bus, EHCIState, bus); - uint32_t i; - - if (firstport + portcount > NB_PORTS) { - error_setg(errp, "firstport must be between 0 and %u", - NB_PORTS - portcount); - return; - } - - for (i = 0; i < portcount; i++) { - if (s->companion_ports[firstport + i]) { - error_setg(errp, "firstport %u asks for ports %u-%u," - " but port %u has a companion assigned already", - firstport, firstport, firstport + portcount - 1, - firstport + i); - return; - } - } - - for (i = 0; i < portcount; i++) { - s->companion_ports[firstport + i] = ports[i]; - s->ports[firstport + i].speedmask |= - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL; - /* Ensure devs attached before the initial reset go to the companion */ - s->portsc[firstport + i] = PORTSC_POWNER; - } - - s->companion_count++; - s->caps[0x05] = (s->companion_count << 4) | portcount; -} - -static void ehci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep, - unsigned int stream) -{ - EHCIState *s = container_of(bus, EHCIState, bus); - uint32_t portsc = s->portsc[ep->dev->port->index]; - - if (portsc & PORTSC_POWNER) { - return; - } - - s->periodic_sched_active = PERIODIC_ACTIVE; - qemu_bh_schedule(s->async_bh); -} - -static USBDevice *ehci_find_device(EHCIState *ehci, uint8_t addr) -{ - USBDevice *dev; - USBPort *port; - int i; - - for (i = 0; i < NB_PORTS; i++) { - port = &ehci->ports[i]; - if (!(ehci->portsc[i] & PORTSC_PED)) { - DPRINTF("Port %d not enabled\n", i); - continue; - } - dev = usb_find_device(port, addr); - if (dev != NULL) { - return dev; - } - } - return NULL; -} - -/* 4.1 host controller initialization */ -void ehci_reset(void *opaque) -{ - EHCIState *s = opaque; - int i; - USBDevice *devs[NB_PORTS]; - - trace_usb_ehci_reset(); - - /* - * Do the detach before touching portsc, so that it correctly gets send to - * us or to our companion based on PORTSC_POWNER before the reset. - */ - for(i = 0; i < NB_PORTS; i++) { - devs[i] = s->ports[i].dev; - if (devs[i] && devs[i]->attached) { - usb_detach(&s->ports[i]); - } - } - - memset(&s->opreg, 0x00, sizeof(s->opreg)); - memset(&s->portsc, 0x00, sizeof(s->portsc)); - - s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH; - s->usbsts = USBSTS_HALT; - s->usbsts_pending = 0; - s->usbsts_frindex = 0; - ehci_update_irq(s); - - s->astate = EST_INACTIVE; - s->pstate = EST_INACTIVE; - - for(i = 0; i < NB_PORTS; i++) { - if (s->companion_ports[i]) { - s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER; - } else { - s->portsc[i] = PORTSC_PPOWER; - } - if (devs[i] && devs[i]->attached) { - usb_attach(&s->ports[i]); - usb_device_reset(devs[i]); - } - } - ehci_queues_rip_all(s, 0); - ehci_queues_rip_all(s, 1); - timer_del(s->frame_timer); - qemu_bh_cancel(s->async_bh); -} - -static uint64_t ehci_caps_read(void *ptr, hwaddr addr, - unsigned size) -{ - EHCIState *s = ptr; - 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) -{ - EHCIState *s = ptr; - uint32_t val; - - switch (addr) { - case FRINDEX: - /* Round down to mult of 8, else it can go backwards on migration */ - val = s->frindex & ~7; - break; - default: - val = s->opreg[addr >> 2]; - } - - trace_usb_ehci_opreg_read(addr + s->opregbase, addr2str(addr), val); - return val; -} - -static uint64_t ehci_port_read(void *ptr, hwaddr addr, - unsigned size) -{ - EHCIState *s = ptr; - uint32_t val; - - val = s->portsc[addr >> 2]; - trace_usb_ehci_portsc_read(addr + s->portscbase, addr >> 2, val); - return val; -} - -static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner) -{ - USBDevice *dev = s->ports[port].dev; - uint32_t *portsc = &s->portsc[port]; - uint32_t orig; - - if (s->companion_ports[port] == NULL) - return; - - owner = owner & PORTSC_POWNER; - orig = *portsc & PORTSC_POWNER; - - if (!(owner ^ orig)) { - return; - } - - if (dev && dev->attached) { - usb_detach(&s->ports[port]); - } - - *portsc &= ~PORTSC_POWNER; - *portsc |= owner; - - if (dev && dev->attached) { - usb_attach(&s->ports[port]); - } -} - -static void ehci_port_write(void *ptr, hwaddr addr, - uint64_t val, unsigned size) -{ - EHCIState *s = ptr; - int port = addr >> 2; - uint32_t *portsc = &s->portsc[port]; - uint32_t old = *portsc; - USBDevice *dev = s->ports[port].dev; - - trace_usb_ehci_portsc_write(addr + s->portscbase, addr >> 2, val); - - /* Clear rwc bits */ - *portsc &= ~(val & PORTSC_RWC_MASK); - /* The guest may clear, but not set the PED bit */ - *portsc &= val | ~PORTSC_PED; - /* POWNER is masked out by RO_MASK as it is RO when we've no companion */ - handle_port_owner_write(s, port, val); - /* And finally apply RO_MASK */ - val &= PORTSC_RO_MASK; - - if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) { - trace_usb_ehci_port_reset(port, 1); - } - - if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) { - trace_usb_ehci_port_reset(port, 0); - if (dev && dev->attached) { - usb_port_reset(&s->ports[port]); - *portsc &= ~PORTSC_CSC; - } - - /* - * Table 2.16 Set the enable bit(and enable bit change) to indicate - * to SW that this port has a high speed device attached - */ - if (dev && dev->attached && (dev->speedmask & USB_SPEED_MASK_HIGH)) { - val |= PORTSC_PED; - } - } - - if ((val & PORTSC_SUSPEND) && !(*portsc & PORTSC_SUSPEND)) { - trace_usb_ehci_port_suspend(port); - } - if (!(val & PORTSC_FPRES) && (*portsc & PORTSC_FPRES)) { - trace_usb_ehci_port_resume(port); - val &= ~PORTSC_SUSPEND; - } - - *portsc &= ~PORTSC_RO_MASK; - *portsc |= val; - trace_usb_ehci_portsc_change(addr + s->portscbase, addr >> 2, *portsc, old); -} - -static void ehci_opreg_write(void *ptr, hwaddr addr, - uint64_t val, unsigned size) -{ - EHCIState *s = ptr; - uint32_t *mmio = s->opreg + (addr >> 2); - uint32_t old = *mmio; - int i; - - trace_usb_ehci_opreg_write(addr + s->opregbase, addr2str(addr), val); - - switch (addr) { - case USBCMD: - if (val & USBCMD_HCRESET) { - ehci_reset(s); - val = s->usbcmd; - break; - } - - /* not supporting dynamic frame list size at the moment */ - if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) { - fprintf(stderr, "attempt to set frame list size -- value %d\n", - (int)val & USBCMD_FLS); - val &= ~USBCMD_FLS; - } - - if (val & USBCMD_IAAD) { - /* - * Process IAAD immediately, otherwise the Linux IAAD watchdog may - * trigger and re-use a qh without us seeing the unlink. - */ - s->async_stepdown = 0; - qemu_bh_schedule(s->async_bh); - trace_usb_ehci_doorbell_ring(); - } - - if (((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & val) != - ((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & s->usbcmd)) { - if (s->pstate == EST_INACTIVE) { - SET_LAST_RUN_CLOCK(s); - } - s->usbcmd = val; /* Set usbcmd for ehci_update_halt() */ - ehci_update_halt(s); - s->async_stepdown = 0; - qemu_bh_schedule(s->async_bh); - } - break; - - case USBSTS: - val &= USBSTS_RO_MASK; // bits 6 through 31 are RO - ehci_clear_usbsts(s, val); // bits 0 through 5 are R/WC - val = s->usbsts; - ehci_update_irq(s); - break; - - case USBINTR: - val &= USBINTR_MASK; - if (ehci_enabled(s) && (USBSTS_FLR & val)) { - qemu_bh_schedule(s->async_bh); - } - break; - - case FRINDEX: - val &= 0x00003fff; /* frindex is 14bits */ - s->usbsts_frindex = val; - break; - - case CONFIGFLAG: - val &= 0x1; - if (val) { - for(i = 0; i < NB_PORTS; i++) - handle_port_owner_write(s, i, 0); - } - break; - - case PERIODICLISTBASE: - if (ehci_periodic_enabled(s)) { - fprintf(stderr, - "ehci: PERIODIC list base register set while periodic schedule\n" - " is enabled and HC is enabled\n"); - } - break; - - case ASYNCLISTADDR: - if (ehci_async_enabled(s)) { - fprintf(stderr, - "ehci: ASYNC list address register set while async schedule\n" - " is enabled and HC is enabled\n"); - } - break; - } - - *mmio = val; - trace_usb_ehci_opreg_change(addr + s->opregbase, addr2str(addr), - *mmio, old); -} - -/* - * Write the qh back to guest physical memory. This step isn't - * in the EHCI spec but we need to do it since we don't share - * physical memory with our guest VM. - * - * The first three dwords are read-only for the EHCI, so skip them - * when writing back the qh. - */ -static void ehci_flush_qh(EHCIQueue *q) -{ - uint32_t *qh = (uint32_t *) &q->qh; - uint32_t dwords = sizeof(EHCIqh) >> 2; - uint32_t addr = NLPTR_GET(q->qhaddr); - - put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3); -} - -// 4.10.2 - -static int ehci_qh_do_overlay(EHCIQueue *q) -{ - EHCIPacket *p = QTAILQ_FIRST(&q->packets); - int i; - int dtoggle; - int ping; - int eps; - int reload; - - assert(p != NULL); - assert(p->qtdaddr == q->qtdaddr); - - // remember values in fields to preserve in qh after overlay - - dtoggle = q->qh.token & QTD_TOKEN_DTOGGLE; - ping = q->qh.token & QTD_TOKEN_PING; - - q->qh.current_qtd = p->qtdaddr; - q->qh.next_qtd = p->qtd.next; - q->qh.altnext_qtd = p->qtd.altnext; - q->qh.token = p->qtd.token; - - - eps = get_field(q->qh.epchar, QH_EPCHAR_EPS); - if (eps == EHCI_QH_EPS_HIGH) { - q->qh.token &= ~QTD_TOKEN_PING; - q->qh.token |= ping; - } - - reload = get_field(q->qh.epchar, QH_EPCHAR_RL); - set_field(&q->qh.altnext_qtd, reload, QH_ALTNEXT_NAKCNT); - - for (i = 0; i < 5; i++) { - q->qh.bufptr[i] = p->qtd.bufptr[i]; - } - - if (!(q->qh.epchar & QH_EPCHAR_DTC)) { - // preserve QH DT bit - q->qh.token &= ~QTD_TOKEN_DTOGGLE; - q->qh.token |= dtoggle; - } - - q->qh.bufptr[1] &= ~BUFPTR_CPROGMASK_MASK; - q->qh.bufptr[2] &= ~BUFPTR_FRAMETAG_MASK; - - ehci_flush_qh(q); - - return 0; -} - -static int ehci_init_transfer(EHCIPacket *p) -{ - uint32_t cpage, offset, bytes, plen; - dma_addr_t page; - - cpage = get_field(p->qtd.token, QTD_TOKEN_CPAGE); - bytes = get_field(p->qtd.token, QTD_TOKEN_TBYTES); - offset = p->qtd.bufptr[0] & ~QTD_BUFPTR_MASK; - qemu_sglist_init(&p->sgl, p->queue->ehci->device, 5, p->queue->ehci->as); - - while (bytes > 0) { - if (cpage > 4) { - fprintf(stderr, "cpage out of range (%d)\n", cpage); - return -1; - } - - page = p->qtd.bufptr[cpage] & QTD_BUFPTR_MASK; - page += offset; - plen = bytes; - if (plen > 4096 - offset) { - plen = 4096 - offset; - offset = 0; - cpage++; - } - - qemu_sglist_add(&p->sgl, page, plen); - bytes -= plen; - } - return 0; -} - -static void ehci_finish_transfer(EHCIQueue *q, int len) -{ - uint32_t cpage, offset; - - if (len > 0) { - /* update cpage & offset */ - cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); - offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK; - - offset += len; - cpage += offset >> QTD_BUFPTR_SH; - offset &= ~QTD_BUFPTR_MASK; - - set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE); - q->qh.bufptr[0] &= QTD_BUFPTR_MASK; - q->qh.bufptr[0] |= offset; - } -} - -static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) -{ - EHCIPacket *p; - EHCIState *s = port->opaque; - uint32_t portsc = s->portsc[port->index]; - - if (portsc & PORTSC_POWNER) { - USBPort *companion = s->companion_ports[port->index]; - companion->ops->complete(companion, packet); - return; - } - - p = container_of(packet, EHCIPacket, packet); - assert(p->async == EHCI_ASYNC_INFLIGHT); - - if (packet->status == USB_RET_REMOVE_FROM_QUEUE) { - trace_usb_ehci_packet_action(p->queue, p, "remove"); - ehci_free_packet(p); - return; - } - - trace_usb_ehci_packet_action(p->queue, p, "wakeup"); - p->async = EHCI_ASYNC_FINISHED; - - if (!p->queue->async) { - s->periodic_sched_active = PERIODIC_ACTIVE; - } - qemu_bh_schedule(s->async_bh); -} - -static void ehci_execute_complete(EHCIQueue *q) -{ - EHCIPacket *p = QTAILQ_FIRST(&q->packets); - uint32_t tbytes; - - assert(p != NULL); - assert(p->qtdaddr == q->qtdaddr); - assert(p->async == EHCI_ASYNC_INITIALIZED || - p->async == EHCI_ASYNC_FINISHED); - - DPRINTF("execute_complete: qhaddr 0x%x, next 0x%x, qtdaddr 0x%x, " - "status %d, actual_length %d\n", - q->qhaddr, q->qh.next, q->qtdaddr, - p->packet.status, p->packet.actual_length); - - switch (p->packet.status) { - case USB_RET_SUCCESS: - break; - case USB_RET_IOERROR: - case USB_RET_NODEV: - q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR); - set_field(&q->qh.token, 0, QTD_TOKEN_CERR); - ehci_raise_irq(q->ehci, USBSTS_ERRINT); - break; - case USB_RET_STALL: - q->qh.token |= QTD_TOKEN_HALT; - ehci_raise_irq(q->ehci, USBSTS_ERRINT); - break; - case USB_RET_NAK: - set_field(&q->qh.altnext_qtd, 0, QH_ALTNEXT_NAKCNT); - return; /* We're not done yet with this transaction */ - case USB_RET_BABBLE: - q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE); - ehci_raise_irq(q->ehci, USBSTS_ERRINT); - break; - default: - /* should not be triggerable */ - fprintf(stderr, "USB invalid response %d\n", p->packet.status); - g_assert_not_reached(); - break; - } - - /* TODO check 4.12 for splits */ - tbytes = get_field(q->qh.token, QTD_TOKEN_TBYTES); - if (tbytes && p->pid == USB_TOKEN_IN) { - tbytes -= p->packet.actual_length; - if (tbytes) { - /* 4.15.1.2 must raise int on a short input packet */ - ehci_raise_irq(q->ehci, USBSTS_INT); - if (q->async) { - q->ehci->int_req_by_async = true; - } - } - } else { - tbytes = 0; - } - DPRINTF("updating tbytes to %d\n", tbytes); - set_field(&q->qh.token, tbytes, QTD_TOKEN_TBYTES); - - ehci_finish_transfer(q, p->packet.actual_length); - usb_packet_unmap(&p->packet, &p->sgl); - qemu_sglist_destroy(&p->sgl); - p->async = EHCI_ASYNC_NONE; - - q->qh.token ^= QTD_TOKEN_DTOGGLE; - q->qh.token &= ~QTD_TOKEN_ACTIVE; - - if (q->qh.token & QTD_TOKEN_IOC) { - ehci_raise_irq(q->ehci, USBSTS_INT); - if (q->async) { - q->ehci->int_req_by_async = true; - } - } -} - -/* 4.10.3 returns "again" */ -static int ehci_execute(EHCIPacket *p, const char *action) -{ - USBEndpoint *ep; - int endp; - bool spd; - - assert(p->async == EHCI_ASYNC_NONE || - p->async == EHCI_ASYNC_INITIALIZED); - - if (!(p->qtd.token & QTD_TOKEN_ACTIVE)) { - fprintf(stderr, "Attempting to execute inactive qtd\n"); - return -1; - } - - if (get_field(p->qtd.token, QTD_TOKEN_TBYTES) > BUFF_SIZE) { - ehci_trace_guest_bug(p->queue->ehci, - "guest requested more bytes than allowed"); - return -1; - } - - if (!ehci_verify_pid(p->queue, &p->qtd)) { - ehci_queue_stopped(p->queue); /* Mark the ep in the prev dir stopped */ - } - p->pid = ehci_get_pid(&p->qtd); - p->queue->last_pid = p->pid; - endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP); - ep = usb_ep_get(p->queue->dev, p->pid, endp); - - if (p->async == EHCI_ASYNC_NONE) { - if (ehci_init_transfer(p) != 0) { - return -1; - } - - spd = (p->pid == USB_TOKEN_IN && NLPTR_TBIT(p->qtd.altnext) == 0); - usb_packet_setup(&p->packet, p->pid, ep, 0, p->qtdaddr, spd, - (p->qtd.token & QTD_TOKEN_IOC) != 0); - usb_packet_map(&p->packet, &p->sgl); - p->async = EHCI_ASYNC_INITIALIZED; - } - - trace_usb_ehci_packet_action(p->queue, p, action); - usb_handle_packet(p->queue->dev, &p->packet); - DPRINTF("submit: qh 0x%x next 0x%x qtd 0x%x pid 0x%x len %zd endp 0x%x " - "status %d actual_length %d\n", p->queue->qhaddr, p->qtd.next, - p->qtdaddr, p->pid, p->packet.iov.size, endp, p->packet.status, - p->packet.actual_length); - - if (p->packet.actual_length > BUFF_SIZE) { - fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n"); - return -1; - } - - return 1; -} - -/* 4.7.2 - */ - -static int ehci_process_itd(EHCIState *ehci, - EHCIitd *itd, - uint32_t addr) -{ - USBDevice *dev; - USBEndpoint *ep; - uint32_t i, len, pid, dir, devaddr, endp; - uint32_t pg, off, ptr1, ptr2, max, mult; - - ehci->periodic_sched_active = PERIODIC_ACTIVE; - - dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION); - devaddr = get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR); - endp = get_field(itd->bufptr[0], ITD_BUFPTR_EP); - max = get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT); - mult = get_field(itd->bufptr[2], ITD_BUFPTR_MULT); - - for(i = 0; i < 8; i++) { - if (itd->transact[i] & ITD_XACT_ACTIVE) { - pg = get_field(itd->transact[i], ITD_XACT_PGSEL); - off = itd->transact[i] & ITD_XACT_OFFSET_MASK; - len = get_field(itd->transact[i], ITD_XACT_LENGTH); - - if (len > max * mult) { - len = max * mult; - } - 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); - qemu_sglist_add(&ehci->isgl, ptr2, len2); - } else { - qemu_sglist_add(&ehci->isgl, ptr1 + off, len); - } - - pid = dir ? USB_TOKEN_IN : USB_TOKEN_OUT; - - dev = ehci_find_device(ehci, devaddr); - ep = usb_ep_get(dev, pid, endp); - if (ep && ep->type == USB_ENDPOINT_XFER_ISOC) { - usb_packet_setup(&ehci->ipacket, pid, ep, 0, addr, false, - (itd->transact[i] & ITD_XACT_IOC) != 0); - usb_packet_map(&ehci->ipacket, &ehci->isgl); - usb_handle_packet(dev, &ehci->ipacket); - usb_packet_unmap(&ehci->ipacket, &ehci->isgl); - } else { - DPRINTF("ISOCH: attempt to addess non-iso endpoint\n"); - ehci->ipacket.status = USB_RET_NAK; - ehci->ipacket.actual_length = 0; - } - qemu_sglist_destroy(&ehci->isgl); - - switch (ehci->ipacket.status) { - case USB_RET_SUCCESS: - break; - default: - fprintf(stderr, "Unexpected iso usb result: %d\n", - ehci->ipacket.status); - /* Fall through */ - case USB_RET_IOERROR: - case USB_RET_NODEV: - /* 3.3.2: XACTERR is only allowed on IN transactions */ - if (dir) { - itd->transact[i] |= ITD_XACT_XACTERR; - ehci_raise_irq(ehci, USBSTS_ERRINT); - } - break; - case USB_RET_BABBLE: - itd->transact[i] |= ITD_XACT_BABBLE; - ehci_raise_irq(ehci, USBSTS_ERRINT); - break; - case USB_RET_NAK: - /* no data for us, so do a zero-length transfer */ - ehci->ipacket.actual_length = 0; - break; - } - if (!dir) { - set_field(&itd->transact[i], len - ehci->ipacket.actual_length, - ITD_XACT_LENGTH); /* OUT */ - } else { - set_field(&itd->transact[i], ehci->ipacket.actual_length, - ITD_XACT_LENGTH); /* IN */ - } - if (itd->transact[i] & ITD_XACT_IOC) { - ehci_raise_irq(ehci, USBSTS_INT); - } - itd->transact[i] &= ~ITD_XACT_ACTIVE; - } - } - return 0; -} - - -/* This state is the entry point for asynchronous schedule - * processing. Entry here consitutes a EHCI start event state (4.8.5) - */ -static int ehci_state_waitlisthead(EHCIState *ehci, int async) -{ - EHCIqh qh; - int i = 0; - int again = 0; - uint32_t entry = ehci->asynclistaddr; - - /* set reclamation flag at start event (4.8.6) */ - if (async) { - ehci_set_usbsts(ehci, USBSTS_REC); - } - - ehci_queues_rip_unused(ehci, async); - - /* Find the head of the list (4.9.1.1) */ - for(i = 0; i < MAX_QH; i++) { - if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &qh, - sizeof(EHCIqh) >> 2) < 0) { - return 0; - } - ehci_trace_qh(NULL, NLPTR_GET(entry), &qh); - - if (qh.epchar & QH_EPCHAR_H) { - if (async) { - entry |= (NLPTR_TYPE_QH << 1); - } - - ehci_set_fetch_addr(ehci, async, entry); - ehci_set_state(ehci, async, EST_FETCHENTRY); - again = 1; - goto out; - } - - entry = qh.next; - if (entry == ehci->asynclistaddr) { - break; - } - } - - /* no head found for list. */ - - ehci_set_state(ehci, async, EST_ACTIVE); - -out: - return again; -} - - -/* This state is the entry point for periodic schedule processing as - * well as being a continuation state for async processing. - */ -static int ehci_state_fetchentry(EHCIState *ehci, int async) -{ - int again = 0; - uint32_t entry = ehci_get_fetch_addr(ehci, async); - - if (NLPTR_TBIT(entry)) { - ehci_set_state(ehci, async, EST_ACTIVE); - goto out; - } - - /* section 4.8, only QH in async schedule */ - if (async && (NLPTR_TYPE_GET(entry) != NLPTR_TYPE_QH)) { - fprintf(stderr, "non queue head request in async schedule\n"); - return -1; - } - - switch (NLPTR_TYPE_GET(entry)) { - case NLPTR_TYPE_QH: - ehci_set_state(ehci, async, EST_FETCHQH); - again = 1; - break; - - case NLPTR_TYPE_ITD: - ehci_set_state(ehci, async, EST_FETCHITD); - again = 1; - break; - - case NLPTR_TYPE_STITD: - ehci_set_state(ehci, async, EST_FETCHSITD); - again = 1; - break; - - default: - /* TODO: handle FSTN type */ - fprintf(stderr, "FETCHENTRY: entry at %X is of type %d " - "which is not supported yet\n", entry, NLPTR_TYPE_GET(entry)); - return -1; - } - -out: - return again; -} - -static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) -{ - uint32_t entry; - EHCIQueue *q; - EHCIqh qh; - - entry = ehci_get_fetch_addr(ehci, async); - q = ehci_find_queue_by_qh(ehci, entry, async); - if (q == NULL) { - q = ehci_alloc_queue(ehci, entry, async); - } - - q->seen++; - if (q->seen > 1) { - /* we are going in circles -- stop processing */ - ehci_set_state(ehci, async, EST_ACTIVE); - q = NULL; - goto out; - } - - if (get_dwords(ehci, NLPTR_GET(q->qhaddr), - (uint32_t *) &qh, sizeof(EHCIqh) >> 2) < 0) { - q = NULL; - goto out; - } - ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &qh); - - /* - * The overlay area of the qh should never be changed by the guest, - * except when idle, in which case the reset is a nop. - */ - if (!ehci_verify_qh(q, &qh)) { - if (ehci_reset_queue(q) > 0) { - ehci_trace_guest_bug(ehci, "guest updated active QH"); - } - } - q->qh = qh; - - q->transact_ctr = get_field(q->qh.epcap, QH_EPCAP_MULT); - if (q->transact_ctr == 0) { /* Guest bug in some versions of windows */ - q->transact_ctr = 4; - } - - if (q->dev == NULL) { - q->dev = ehci_find_device(q->ehci, - get_field(q->qh.epchar, QH_EPCHAR_DEVADDR)); - } - - if (async && (q->qh.epchar & QH_EPCHAR_H)) { - - /* EHCI spec version 1.0 Section 4.8.3 & 4.10.1 */ - if (ehci->usbsts & USBSTS_REC) { - ehci_clear_usbsts(ehci, USBSTS_REC); - } else { - DPRINTF("FETCHQH: QH 0x%08x. H-bit set, reclamation status reset" - " - done processing\n", q->qhaddr); - ehci_set_state(ehci, async, EST_ACTIVE); - q = NULL; - goto out; - } - } - -#if EHCI_DEBUG - if (q->qhaddr != q->qh.next) { - DPRINTF("FETCHQH: QH 0x%08x (h %x halt %x active %x) next 0x%08x\n", - q->qhaddr, - q->qh.epchar & QH_EPCHAR_H, - q->qh.token & QTD_TOKEN_HALT, - q->qh.token & QTD_TOKEN_ACTIVE, - q->qh.next); - } -#endif - - if (q->qh.token & QTD_TOKEN_HALT) { - ehci_set_state(ehci, async, EST_HORIZONTALQH); - - } else if ((q->qh.token & QTD_TOKEN_ACTIVE) && - (NLPTR_TBIT(q->qh.current_qtd) == 0)) { - q->qtdaddr = q->qh.current_qtd; - ehci_set_state(ehci, async, EST_FETCHQTD); - - } else { - /* EHCI spec version 1.0 Section 4.10.2 */ - ehci_set_state(ehci, async, EST_ADVANCEQUEUE); - } - -out: - return q; -} - -static int ehci_state_fetchitd(EHCIState *ehci, int async) -{ - uint32_t entry; - EHCIitd itd; - - assert(!async); - entry = ehci_get_fetch_addr(ehci, async); - - if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd, - sizeof(EHCIitd) >> 2) < 0) { - return -1; - } - ehci_trace_itd(ehci, entry, &itd); - - if (ehci_process_itd(ehci, &itd, entry) != 0) { - return -1; - } - - put_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd, - sizeof(EHCIitd) >> 2); - ehci_set_fetch_addr(ehci, async, itd.next); - ehci_set_state(ehci, async, EST_FETCHENTRY); - - return 1; -} - -static int ehci_state_fetchsitd(EHCIState *ehci, int async) -{ - uint32_t entry; - EHCIsitd sitd; - - assert(!async); - entry = ehci_get_fetch_addr(ehci, async); - - if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *)&sitd, - sizeof(EHCIsitd) >> 2) < 0) { - return 0; - } - ehci_trace_sitd(ehci, entry, &sitd); - - if (!(sitd.results & SITD_RESULTS_ACTIVE)) { - /* siTD is not active, nothing to do */; - } else { - /* TODO: split transfers are not implemented */ - fprintf(stderr, "WARNING: Skipping active siTD\n"); - } - - ehci_set_fetch_addr(ehci, async, sitd.next); - ehci_set_state(ehci, async, EST_FETCHENTRY); - return 1; -} - -/* Section 4.10.2 - paragraph 3 */ -static int ehci_state_advqueue(EHCIQueue *q) -{ -#if 0 - /* TO-DO: 4.10.2 - paragraph 2 - * if I-bit is set to 1 and QH is not active - * go to horizontal QH - */ - if (I-bit set) { - ehci_set_state(ehci, async, EST_HORIZONTALQH); - goto out; - } -#endif - - /* - * want data and alt-next qTD is valid - */ - if (((q->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) && - (NLPTR_TBIT(q->qh.altnext_qtd) == 0)) { - q->qtdaddr = q->qh.altnext_qtd; - ehci_set_state(q->ehci, q->async, EST_FETCHQTD); - - /* - * next qTD is valid - */ - } else if (NLPTR_TBIT(q->qh.next_qtd) == 0) { - q->qtdaddr = q->qh.next_qtd; - ehci_set_state(q->ehci, q->async, EST_FETCHQTD); - - /* - * no valid qTD, try next QH - */ - } else { - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - } - - return 1; -} - -/* Section 4.10.2 - paragraph 4 */ -static int ehci_state_fetchqtd(EHCIQueue *q) -{ - EHCIqtd qtd; - EHCIPacket *p; - int again = 1; - - if (get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd, - sizeof(EHCIqtd) >> 2) < 0) { - return 0; - } - ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd); - - p = QTAILQ_FIRST(&q->packets); - if (p != NULL) { - if (!ehci_verify_qtd(p, &qtd)) { - ehci_cancel_queue(q); - if (qtd.token & QTD_TOKEN_ACTIVE) { - ehci_trace_guest_bug(q->ehci, "guest updated active qTD"); - } - p = NULL; - } else { - p->qtd = qtd; - ehci_qh_do_overlay(q); - } - } - - if (!(qtd.token & QTD_TOKEN_ACTIVE)) { - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - } else if (p != NULL) { - switch (p->async) { - case EHCI_ASYNC_NONE: - case EHCI_ASYNC_INITIALIZED: - /* Not yet executed (MULT), or previously nacked (int) packet */ - ehci_set_state(q->ehci, q->async, EST_EXECUTE); - break; - case EHCI_ASYNC_INFLIGHT: - /* Check if the guest has added new tds to the queue */ - again = ehci_fill_queue(QTAILQ_LAST(&q->packets, pkts_head)); - /* Unfinished async handled packet, go horizontal */ - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - break; - case EHCI_ASYNC_FINISHED: - /* Complete executing of the packet */ - ehci_set_state(q->ehci, q->async, EST_EXECUTING); - break; - } - } else { - p = ehci_alloc_packet(q); - p->qtdaddr = q->qtdaddr; - p->qtd = qtd; - ehci_set_state(q->ehci, q->async, EST_EXECUTE); - } - - return again; -} - -static int ehci_state_horizqh(EHCIQueue *q) -{ - int again = 0; - - if (ehci_get_fetch_addr(q->ehci, q->async) != q->qh.next) { - ehci_set_fetch_addr(q->ehci, q->async, q->qh.next); - ehci_set_state(q->ehci, q->async, EST_FETCHENTRY); - again = 1; - } else { - ehci_set_state(q->ehci, q->async, EST_ACTIVE); - } - - return again; -} - -/* Returns "again" */ -static int ehci_fill_queue(EHCIPacket *p) -{ - USBEndpoint *ep = p->packet.ep; - EHCIQueue *q = p->queue; - EHCIqtd qtd = p->qtd; - uint32_t qtdaddr; - - for (;;) { - if (NLPTR_TBIT(qtd.next) != 0) { - break; - } - qtdaddr = qtd.next; - /* - * Detect circular td lists, Windows creates these, counting on the - * active bit going low after execution to make the queue stop. - */ - QTAILQ_FOREACH(p, &q->packets, next) { - if (p->qtdaddr == qtdaddr) { - goto leave; - } - } - if (get_dwords(q->ehci, NLPTR_GET(qtdaddr), - (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2) < 0) { - return -1; - } - ehci_trace_qtd(q, NLPTR_GET(qtdaddr), &qtd); - if (!(qtd.token & QTD_TOKEN_ACTIVE)) { - break; - } - if (!ehci_verify_pid(q, &qtd)) { - ehci_trace_guest_bug(q->ehci, "guest queued token with wrong pid"); - break; - } - p = ehci_alloc_packet(q); - p->qtdaddr = qtdaddr; - p->qtd = qtd; - if (ehci_execute(p, "queue") == -1) { - return -1; - } - assert(p->packet.status == USB_RET_ASYNC); - p->async = EHCI_ASYNC_INFLIGHT; - } -leave: - usb_device_flush_ep_queue(ep->dev, ep); - return 1; -} - -static int ehci_state_execute(EHCIQueue *q) -{ - EHCIPacket *p = QTAILQ_FIRST(&q->packets); - int again = 0; - - assert(p != NULL); - assert(p->qtdaddr == q->qtdaddr); - - if (ehci_qh_do_overlay(q) != 0) { - return -1; - } - - // TODO verify enough time remains in the uframe as in 4.4.1.1 - // TODO write back ptr to async list when done or out of time - - /* 4.10.3, bottom of page 82, go horizontal on transaction counter == 0 */ - if (!q->async && q->transact_ctr == 0) { - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - again = 1; - goto out; - } - - if (q->async) { - ehci_set_usbsts(q->ehci, USBSTS_REC); - } - - again = ehci_execute(p, "process"); - if (again == -1) { - goto out; - } - if (p->packet.status == USB_RET_ASYNC) { - ehci_flush_qh(q); - trace_usb_ehci_packet_action(p->queue, p, "async"); - p->async = EHCI_ASYNC_INFLIGHT; - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - if (q->async) { - again = ehci_fill_queue(p); - } else { - again = 1; - } - goto out; - } - - ehci_set_state(q->ehci, q->async, EST_EXECUTING); - again = 1; - -out: - return again; -} - -static int ehci_state_executing(EHCIQueue *q) -{ - EHCIPacket *p = QTAILQ_FIRST(&q->packets); - - assert(p != NULL); - assert(p->qtdaddr == q->qtdaddr); - - ehci_execute_complete(q); - - /* 4.10.3 */ - if (!q->async && q->transact_ctr > 0) { - q->transact_ctr--; - } - - /* 4.10.5 */ - if (p->packet.status == USB_RET_NAK) { - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - } else { - ehci_set_state(q->ehci, q->async, EST_WRITEBACK); - } - - ehci_flush_qh(q); - return 1; -} - - -static int ehci_state_writeback(EHCIQueue *q) -{ - EHCIPacket *p = QTAILQ_FIRST(&q->packets); - uint32_t *qtd, addr; - int again = 0; - - /* Write back the QTD from the QH area */ - assert(p != NULL); - assert(p->qtdaddr == q->qtdaddr); - - ehci_trace_qtd(q, NLPTR_GET(p->qtdaddr), (EHCIqtd *) &q->qh.next_qtd); - qtd = (uint32_t *) &q->qh.next_qtd; - addr = NLPTR_GET(p->qtdaddr); - put_dwords(q->ehci, addr + 2 * sizeof(uint32_t), qtd + 2, 2); - ehci_free_packet(p); - - /* - * EHCI specs say go horizontal here. - * - * We can also advance the queue here for performance reasons. We - * need to take care to only take that shortcut in case we've - * processed the qtd just written back without errors, i.e. halt - * bit is clear. - */ - if (q->qh.token & QTD_TOKEN_HALT) { - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - again = 1; - } else { - ehci_set_state(q->ehci, q->async, EST_ADVANCEQUEUE); - again = 1; - } - return again; -} - -/* - * This is the state machine that is common to both async and periodic - */ - -static void ehci_advance_state(EHCIState *ehci, int async) -{ - EHCIQueue *q = NULL; - int itd_count = 0; - int again; - - do { - switch(ehci_get_state(ehci, async)) { - case EST_WAITLISTHEAD: - again = ehci_state_waitlisthead(ehci, async); - break; - - case EST_FETCHENTRY: - again = ehci_state_fetchentry(ehci, async); - break; - - case EST_FETCHQH: - q = ehci_state_fetchqh(ehci, async); - if (q != NULL) { - assert(q->async == async); - again = 1; - } else { - again = 0; - } - break; - - 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: - assert(q != NULL); - again = ehci_state_advqueue(q); - break; - - case EST_FETCHQTD: - assert(q != NULL); - again = ehci_state_fetchqtd(q); - break; - - case EST_HORIZONTALQH: - assert(q != NULL); - again = ehci_state_horizqh(q); - break; - - case EST_EXECUTE: - assert(q != NULL); - again = ehci_state_execute(q); - if (async) { - ehci->async_stepdown = 0; - } - break; - - case EST_EXECUTING: - assert(q != NULL); - if (async) { - ehci->async_stepdown = 0; - } - again = ehci_state_executing(q); - break; - - case EST_WRITEBACK: - assert(q != NULL); - again = ehci_state_writeback(q); - if (!async) { - ehci->periodic_sched_active = PERIODIC_ACTIVE; - } - break; - - default: - fprintf(stderr, "Bad state!\n"); - again = -1; - g_assert_not_reached(); - break; - } - - 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; - } - } - while (again); -} - -static void ehci_advance_async_state(EHCIState *ehci) -{ - const int async = 1; - - switch(ehci_get_state(ehci, async)) { - case EST_INACTIVE: - if (!ehci_async_enabled(ehci)) { - break; - } - ehci_set_state(ehci, async, EST_ACTIVE); - // No break, fall through to ACTIVE - - case EST_ACTIVE: - if (!ehci_async_enabled(ehci)) { - ehci_queues_rip_all(ehci, async); - ehci_set_state(ehci, async, EST_INACTIVE); - break; - } - - /* make sure guest has acknowledged the doorbell interrupt */ - /* TO-DO: is this really needed? */ - if (ehci->usbsts & USBSTS_IAA) { - DPRINTF("IAA status bit still set.\n"); - break; - } - - /* check that address register has been set */ - if (ehci->asynclistaddr == 0) { - break; - } - - ehci_set_state(ehci, async, EST_WAITLISTHEAD); - ehci_advance_state(ehci, async); - - /* If the doorbell is set, the guest wants to make a change to the - * schedule. The host controller needs to release cached data. - * (section 4.8.2) - */ - if (ehci->usbcmd & USBCMD_IAAD) { - /* Remove all unseen qhs from the async qhs queue */ - ehci_queues_rip_unseen(ehci, async); - trace_usb_ehci_doorbell_ack(); - ehci->usbcmd &= ~USBCMD_IAAD; - ehci_raise_irq(ehci, USBSTS_IAA); - } - break; - - default: - /* this should only be due to a developer mistake */ - fprintf(stderr, "ehci: Bad asynchronous state %d. " - "Resetting to active\n", ehci->astate); - g_assert_not_reached(); - } -} - -static void ehci_advance_periodic_state(EHCIState *ehci) -{ - uint32_t entry; - uint32_t list; - const int async = 0; - - // 4.6 - - switch(ehci_get_state(ehci, async)) { - case EST_INACTIVE: - if (!(ehci->frindex & 7) && ehci_periodic_enabled(ehci)) { - ehci_set_state(ehci, async, EST_ACTIVE); - // No break, fall through to ACTIVE - } else - break; - - case EST_ACTIVE: - if (!(ehci->frindex & 7) && !ehci_periodic_enabled(ehci)) { - ehci_queues_rip_all(ehci, async); - ehci_set_state(ehci, async, EST_INACTIVE); - break; - } - - list = ehci->periodiclistbase & 0xfffff000; - /* check that register has been set */ - if (list == 0) { - break; - } - list |= ((ehci->frindex & 0x1ff8) >> 1); - - if (get_dwords(ehci, list, &entry, 1) < 0) { - break; - } - - DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n", - ehci->frindex / 8, list, entry); - ehci_set_fetch_addr(ehci, async,entry); - ehci_set_state(ehci, async, EST_FETCHENTRY); - ehci_advance_state(ehci, async); - ehci_queues_rip_unused(ehci, async); - break; - - default: - /* this should only be due to a developer mistake */ - fprintf(stderr, "ehci: Bad periodic state %d. " - "Resetting to active\n", ehci->pstate); - g_assert_not_reached(); - } -} - -static void ehci_update_frindex(EHCIState *ehci, int uframes) -{ - int i; - - if (!ehci_enabled(ehci) && ehci->pstate == EST_INACTIVE) { - return; - } - - for (i = 0; i < uframes; i++) { - ehci->frindex++; - - if (ehci->frindex == 0x00002000) { - ehci_raise_irq(ehci, USBSTS_FLR); - } - - if (ehci->frindex == 0x00004000) { - ehci_raise_irq(ehci, USBSTS_FLR); - ehci->frindex = 0; - if (ehci->usbsts_frindex >= 0x00004000) { - ehci->usbsts_frindex -= 0x00004000; - } else { - ehci->usbsts_frindex = 0; - } - } - } -} - -static void ehci_frame_timer(void *opaque) -{ - EHCIState *ehci = opaque; - int need_timer = 0; - int64_t expire_time, t_now; - uint64_t ns_elapsed; - int uframes, skipped_uframes; - int i; - - t_now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - ns_elapsed = t_now - ehci->last_run_ns; - uframes = ns_elapsed / UFRAME_TIMER_NS; - - if (ehci_periodic_enabled(ehci) || ehci->pstate != EST_INACTIVE) { - need_timer++; - - if (uframes > (ehci->maxframes * 8)) { - skipped_uframes = uframes - (ehci->maxframes * 8); - ehci_update_frindex(ehci, skipped_uframes); - ehci->last_run_ns += UFRAME_TIMER_NS * skipped_uframes; - uframes -= skipped_uframes; - DPRINTF("WARNING - EHCI skipped %d uframes\n", skipped_uframes); - } - - for (i = 0; i < uframes; i++) { - /* - * If we're running behind schedule, we should not catch up - * too fast, as that will make some guests unhappy: - * 1) We must process a minimum of MIN_UFR_PER_TICK frames, - * otherwise we will never catch up - * 2) Process frames until the guest has requested an irq (IOC) - */ - if (i >= MIN_UFR_PER_TICK) { - ehci_commit_irq(ehci); - if ((ehci->usbsts & USBINTR_MASK) & ehci->usbintr) { - break; - } - } - if (ehci->periodic_sched_active) { - ehci->periodic_sched_active--; - } - ehci_update_frindex(ehci, 1); - if ((ehci->frindex & 7) == 0) { - ehci_advance_periodic_state(ehci); - } - ehci->last_run_ns += UFRAME_TIMER_NS; - } - } else { - ehci->periodic_sched_active = 0; - ehci_update_frindex(ehci, uframes); - ehci->last_run_ns += UFRAME_TIMER_NS * uframes; - } - - if (ehci->periodic_sched_active) { - ehci->async_stepdown = 0; - } else if (ehci->async_stepdown < ehci->maxframes / 2) { - ehci->async_stepdown++; - } - - /* Async is not inside loop since it executes everything it can once - * called - */ - if (ehci_async_enabled(ehci) || ehci->astate != EST_INACTIVE) { - need_timer++; - ehci_advance_async_state(ehci); - } - - ehci_commit_irq(ehci); - if (ehci->usbsts_pending) { - need_timer++; - ehci->async_stepdown = 0; - } - - if (ehci_enabled(ehci) && (ehci->usbintr & USBSTS_FLR)) { - need_timer++; - } - - if (need_timer) { - /* 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 + - NANOSECONDS_PER_SECOND / (FRAME_TIMER_FREQ * 4); - ehci->int_req_by_async = false; - } else { - expire_time = t_now + (NANOSECONDS_PER_SECOND - * (ehci->async_stepdown+1) / FRAME_TIMER_FREQ); - } - timer_mod(ehci->frame_timer, expire_time); - } -} - -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, - .impl.max_access_size = 1, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps ehci_mmio_opreg_ops = { - .read = ehci_opreg_read, - .write = ehci_opreg_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps ehci_mmio_port_ops = { - .read = ehci_port_read, - .write = ehci_port_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static USBPortOps ehci_port_ops = { - .attach = ehci_attach, - .detach = ehci_detach, - .child_detach = ehci_child_detach, - .wakeup = ehci_wakeup, - .complete = ehci_async_complete_packet, -}; - -static USBBusOps ehci_bus_ops_companion = { - .register_companion = ehci_register_companion, - .wakeup_endpoint = ehci_wakeup_endpoint, -}; -static USBBusOps ehci_bus_ops_standalone = { - .wakeup_endpoint = ehci_wakeup_endpoint, -}; - -static void usb_ehci_pre_save(void *opaque) -{ - EHCIState *ehci = opaque; - uint32_t new_frindex; - - /* Round down frindex to a multiple of 8 for migration compatibility */ - new_frindex = ehci->frindex & ~7; - ehci->last_run_ns -= (ehci->frindex - new_frindex) * UFRAME_TIMER_NS; - ehci->frindex = new_frindex; -} - -static int usb_ehci_post_load(void *opaque, int version_id) -{ - EHCIState *s = opaque; - int i; - - for (i = 0; i < NB_PORTS; i++) { - USBPort *companion = s->companion_ports[i]; - if (companion == NULL) { - continue; - } - if (s->portsc[i] & PORTSC_POWNER) { - companion->dev = s->ports[i].dev; - } else { - companion->dev = NULL; - } - } - - return 0; -} - -static void usb_ehci_vm_state_change(void *opaque, int running, RunState state) -{ - EHCIState *ehci = opaque; - - /* - * We don't migrate the EHCIQueue-s, instead we rebuild them for the - * schedule in guest memory. We must do the rebuilt ASAP, so that - * USB-devices which have async handled packages have a packet in the - * ep queue to match the completion with. - */ - if (state == RUN_STATE_RUNNING) { - ehci_advance_async_state(ehci); - } - - /* - * The schedule rebuilt from guest memory could cause the migration dest - * to miss a QH unlink, and fail to cancel packets, since the unlinked QH - * will never have existed on the destination. Therefor we must flush the - * async schedule on savevm to catch any not yet noticed unlinks. - */ - if (state == RUN_STATE_SAVE_VM) { - ehci_advance_async_state(ehci); - ehci_queues_rip_unseen(ehci, 1); - } -} - -const VMStateDescription vmstate_ehci = { - .name = "ehci-core", - .version_id = 2, - .minimum_version_id = 1, - .pre_save = usb_ehci_pre_save, - .post_load = usb_ehci_post_load, - .fields = (VMStateField[]) { - /* mmio registers */ - VMSTATE_UINT32(usbcmd, EHCIState), - VMSTATE_UINT32(usbsts, EHCIState), - VMSTATE_UINT32_V(usbsts_pending, EHCIState, 2), - VMSTATE_UINT32_V(usbsts_frindex, EHCIState, 2), - VMSTATE_UINT32(usbintr, EHCIState), - VMSTATE_UINT32(frindex, EHCIState), - VMSTATE_UINT32(ctrldssegment, EHCIState), - VMSTATE_UINT32(periodiclistbase, EHCIState), - VMSTATE_UINT32(asynclistaddr, EHCIState), - VMSTATE_UINT32(configflag, EHCIState), - VMSTATE_UINT32(portsc[0], EHCIState), - VMSTATE_UINT32(portsc[1], EHCIState), - VMSTATE_UINT32(portsc[2], EHCIState), - VMSTATE_UINT32(portsc[3], EHCIState), - VMSTATE_UINT32(portsc[4], EHCIState), - VMSTATE_UINT32(portsc[5], EHCIState), - /* frame timer */ - VMSTATE_TIMER_PTR(frame_timer, EHCIState), - VMSTATE_UINT64(last_run_ns, EHCIState), - VMSTATE_UINT32(async_stepdown, EHCIState), - /* schedule state */ - VMSTATE_UINT32(astate, EHCIState), - VMSTATE_UINT32(pstate, EHCIState), - VMSTATE_UINT32(a_fetch_addr, EHCIState), - VMSTATE_UINT32(p_fetch_addr, EHCIState), - VMSTATE_END_OF_LIST() - } -}; - -void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp) -{ - int i; - - if (s->portnr > NB_PORTS) { - error_setg(errp, "Too many ports! Max. port number is %d.", - NB_PORTS); - return; - } - - usb_bus_new(&s->bus, sizeof(s->bus), s->companion_enable ? - &ehci_bus_ops_companion : &ehci_bus_ops_standalone, dev); - for (i = 0; i < s->portnr; i++) { - usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops, - USB_SPEED_MASK_HIGH); - s->ports[i].dev = 0; - } - - s->frame_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ehci_frame_timer, s); - s->async_bh = qemu_bh_new(ehci_frame_timer, s); - s->device = dev; - - s->vmstate = qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s); -} - -void usb_ehci_unrealize(EHCIState *s, DeviceState *dev, Error **errp) -{ - trace_usb_ehci_unrealize(); - - if (s->frame_timer) { - timer_del(s->frame_timer); - timer_free(s->frame_timer); - s->frame_timer = NULL; - } - if (s->async_bh) { - qemu_bh_delete(s->async_bh); - } - - ehci_queues_rip_all(s, 0); - ehci_queues_rip_all(s, 1); - - memory_region_del_subregion(&s->mem, &s->mem_caps); - memory_region_del_subregion(&s->mem, &s->mem_opreg); - memory_region_del_subregion(&s->mem, &s->mem_ports); - - usb_bus_release(&s->bus); - - if (s->vmstate) { - qemu_del_vm_change_state_handler(s->vmstate); - } -} - -void usb_ehci_init(EHCIState *s, DeviceState *dev) -{ - /* 2.2 host controller interface version */ - s->caps[0x00] = (uint8_t)(s->opregbase - s->capsbase); - s->caps[0x01] = 0x00; - s->caps[0x02] = 0x00; - s->caps[0x03] = 0x01; /* HC version */ - s->caps[0x04] = s->portnr; /* Number of downstream ports */ - s->caps[0x05] = 0x00; /* No companion ports at present */ - s->caps[0x06] = 0x00; - s->caps[0x07] = 0x00; - s->caps[0x08] = 0x80; /* We can cache whole frame, no 64-bit */ - s->caps[0x0a] = 0x00; - s->caps[0x0b] = 0x00; - - QTAILQ_INIT(&s->aqueues); - QTAILQ_INIT(&s->pqueues); - usb_packet_init(&s->ipacket); - - memory_region_init(&s->mem, OBJECT(dev), "ehci", MMIO_SIZE); - memory_region_init_io(&s->mem_caps, OBJECT(dev), &ehci_mmio_caps_ops, s, - "capabilities", CAPA_SIZE); - memory_region_init_io(&s->mem_opreg, OBJECT(dev), &ehci_mmio_opreg_ops, s, - "operational", s->portscbase); - memory_region_init_io(&s->mem_ports, OBJECT(dev), &ehci_mmio_port_ops, s, - "ports", 4 * s->portnr); - - memory_region_add_subregion(&s->mem, s->capsbase, &s->mem_caps); - memory_region_add_subregion(&s->mem, s->opregbase, &s->mem_opreg); - memory_region_add_subregion(&s->mem, s->opregbase + s->portscbase, - &s->mem_ports); -} - -/* - * vim: expandtab ts=4 - */ diff --git a/qemu/hw/usb/hcd-ehci.h b/qemu/hw/usb/hcd-ehci.h deleted file mode 100644 index 30218423c..000000000 --- a/qemu/hw/usb/hcd-ehci.h +++ /dev/null @@ -1,383 +0,0 @@ -/* - * QEMU USB EHCI Emulation - * - * 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 General Public License - * along with this program; if not, see . - */ -#ifndef HW_USB_EHCI_H -#define HW_USB_EHCI_H 1 - -#include "hw/hw.h" -#include "qemu/timer.h" -#include "hw/usb.h" -#include "sysemu/dma.h" -#include "sysemu/sysemu.h" -#include "hw/pci/pci.h" -#include "hw/sysbus.h" - -#ifndef EHCI_DEBUG -#define EHCI_DEBUG 0 -#endif - -#if EHCI_DEBUG -#define DPRINTF printf -#else -#define DPRINTF(...) -#endif - -#define MMIO_SIZE 0x1000 -#define CAPA_SIZE 0x10 - -#define NB_PORTS 6 /* Max. Number of downstream ports */ - -typedef struct EHCIPacket EHCIPacket; -typedef struct EHCIQueue EHCIQueue; -typedef struct EHCIState EHCIState; - -/* EHCI spec version 1.0 Section 3.3 - */ -typedef struct EHCIitd { - uint32_t next; - - uint32_t transact[8]; -#define ITD_XACT_ACTIVE (1 << 31) -#define ITD_XACT_DBERROR (1 << 30) -#define ITD_XACT_BABBLE (1 << 29) -#define ITD_XACT_XACTERR (1 << 28) -#define ITD_XACT_LENGTH_MASK 0x0fff0000 -#define ITD_XACT_LENGTH_SH 16 -#define ITD_XACT_IOC (1 << 15) -#define ITD_XACT_PGSEL_MASK 0x00007000 -#define ITD_XACT_PGSEL_SH 12 -#define ITD_XACT_OFFSET_MASK 0x00000fff - - uint32_t bufptr[7]; -#define ITD_BUFPTR_MASK 0xfffff000 -#define ITD_BUFPTR_SH 12 -#define ITD_BUFPTR_EP_MASK 0x00000f00 -#define ITD_BUFPTR_EP_SH 8 -#define ITD_BUFPTR_DEVADDR_MASK 0x0000007f -#define ITD_BUFPTR_DEVADDR_SH 0 -#define ITD_BUFPTR_DIRECTION (1 << 11) -#define ITD_BUFPTR_MAXPKT_MASK 0x000007ff -#define ITD_BUFPTR_MAXPKT_SH 0 -#define ITD_BUFPTR_MULT_MASK 0x00000003 -#define ITD_BUFPTR_MULT_SH 0 -} EHCIitd; - -/* EHCI spec version 1.0 Section 3.4 - */ -typedef struct EHCIsitd { - uint32_t next; /* Standard next link pointer */ - uint32_t epchar; -#define SITD_EPCHAR_IO (1 << 31) -#define SITD_EPCHAR_PORTNUM_MASK 0x7f000000 -#define SITD_EPCHAR_PORTNUM_SH 24 -#define SITD_EPCHAR_HUBADD_MASK 0x007f0000 -#define SITD_EPCHAR_HUBADDR_SH 16 -#define SITD_EPCHAR_EPNUM_MASK 0x00000f00 -#define SITD_EPCHAR_EPNUM_SH 8 -#define SITD_EPCHAR_DEVADDR_MASK 0x0000007f - - uint32_t uframe; -#define SITD_UFRAME_CMASK_MASK 0x0000ff00 -#define SITD_UFRAME_CMASK_SH 8 -#define SITD_UFRAME_SMASK_MASK 0x000000ff - - uint32_t results; -#define SITD_RESULTS_IOC (1 << 31) -#define SITD_RESULTS_PGSEL (1 << 30) -#define SITD_RESULTS_TBYTES_MASK 0x03ff0000 -#define SITD_RESULTS_TYBYTES_SH 16 -#define SITD_RESULTS_CPROGMASK_MASK 0x0000ff00 -#define SITD_RESULTS_CPROGMASK_SH 8 -#define SITD_RESULTS_ACTIVE (1 << 7) -#define SITD_RESULTS_ERR (1 << 6) -#define SITD_RESULTS_DBERR (1 << 5) -#define SITD_RESULTS_BABBLE (1 << 4) -#define SITD_RESULTS_XACTERR (1 << 3) -#define SITD_RESULTS_MISSEDUF (1 << 2) -#define SITD_RESULTS_SPLITXSTATE (1 << 1) - - uint32_t bufptr[2]; -#define SITD_BUFPTR_MASK 0xfffff000 -#define SITD_BUFPTR_CURROFF_MASK 0x00000fff -#define SITD_BUFPTR_TPOS_MASK 0x00000018 -#define SITD_BUFPTR_TPOS_SH 3 -#define SITD_BUFPTR_TCNT_MASK 0x00000007 - - uint32_t backptr; /* Standard next link pointer */ -} EHCIsitd; - -/* EHCI spec version 1.0 Section 3.5 - */ -typedef struct EHCIqtd { - uint32_t next; /* Standard next link pointer */ - uint32_t altnext; /* Standard next link pointer */ - uint32_t token; -#define QTD_TOKEN_DTOGGLE (1 << 31) -#define QTD_TOKEN_TBYTES_MASK 0x7fff0000 -#define QTD_TOKEN_TBYTES_SH 16 -#define QTD_TOKEN_IOC (1 << 15) -#define QTD_TOKEN_CPAGE_MASK 0x00007000 -#define QTD_TOKEN_CPAGE_SH 12 -#define QTD_TOKEN_CERR_MASK 0x00000c00 -#define QTD_TOKEN_CERR_SH 10 -#define QTD_TOKEN_PID_MASK 0x00000300 -#define QTD_TOKEN_PID_SH 8 -#define QTD_TOKEN_ACTIVE (1 << 7) -#define QTD_TOKEN_HALT (1 << 6) -#define QTD_TOKEN_DBERR (1 << 5) -#define QTD_TOKEN_BABBLE (1 << 4) -#define QTD_TOKEN_XACTERR (1 << 3) -#define QTD_TOKEN_MISSEDUF (1 << 2) -#define QTD_TOKEN_SPLITXSTATE (1 << 1) -#define QTD_TOKEN_PING (1 << 0) - - uint32_t bufptr[5]; /* Standard buffer pointer */ -#define QTD_BUFPTR_MASK 0xfffff000 -#define QTD_BUFPTR_SH 12 -} EHCIqtd; - -/* EHCI spec version 1.0 Section 3.6 - */ -typedef struct EHCIqh { - uint32_t next; /* Standard next link pointer */ - - /* endpoint characteristics */ - uint32_t epchar; -#define QH_EPCHAR_RL_MASK 0xf0000000 -#define QH_EPCHAR_RL_SH 28 -#define QH_EPCHAR_C (1 << 27) -#define QH_EPCHAR_MPLEN_MASK 0x07FF0000 -#define QH_EPCHAR_MPLEN_SH 16 -#define QH_EPCHAR_H (1 << 15) -#define QH_EPCHAR_DTC (1 << 14) -#define QH_EPCHAR_EPS_MASK 0x00003000 -#define QH_EPCHAR_EPS_SH 12 -#define EHCI_QH_EPS_FULL 0 -#define EHCI_QH_EPS_LOW 1 -#define EHCI_QH_EPS_HIGH 2 -#define EHCI_QH_EPS_RESERVED 3 - -#define QH_EPCHAR_EP_MASK 0x00000f00 -#define QH_EPCHAR_EP_SH 8 -#define QH_EPCHAR_I (1 << 7) -#define QH_EPCHAR_DEVADDR_MASK 0x0000007f -#define QH_EPCHAR_DEVADDR_SH 0 - - /* endpoint capabilities */ - uint32_t epcap; -#define QH_EPCAP_MULT_MASK 0xc0000000 -#define QH_EPCAP_MULT_SH 30 -#define QH_EPCAP_PORTNUM_MASK 0x3f800000 -#define QH_EPCAP_PORTNUM_SH 23 -#define QH_EPCAP_HUBADDR_MASK 0x007f0000 -#define QH_EPCAP_HUBADDR_SH 16 -#define QH_EPCAP_CMASK_MASK 0x0000ff00 -#define QH_EPCAP_CMASK_SH 8 -#define QH_EPCAP_SMASK_MASK 0x000000ff -#define QH_EPCAP_SMASK_SH 0 - - uint32_t current_qtd; /* Standard next link pointer */ - uint32_t next_qtd; /* Standard next link pointer */ - uint32_t altnext_qtd; -#define QH_ALTNEXT_NAKCNT_MASK 0x0000001e -#define QH_ALTNEXT_NAKCNT_SH 1 - - uint32_t token; /* Same as QTD token */ - uint32_t bufptr[5]; /* Standard buffer pointer */ -#define BUFPTR_CPROGMASK_MASK 0x000000ff -#define BUFPTR_FRAMETAG_MASK 0x0000001f -#define BUFPTR_SBYTES_MASK 0x00000fe0 -#define BUFPTR_SBYTES_SH 5 -} EHCIqh; - -/* EHCI spec version 1.0 Section 3.7 - */ -typedef struct EHCIfstn { - uint32_t next; /* Standard next link pointer */ - uint32_t backptr; /* Standard next link pointer */ -} EHCIfstn; - -enum async_state { - EHCI_ASYNC_NONE = 0, - EHCI_ASYNC_INITIALIZED, - EHCI_ASYNC_INFLIGHT, - EHCI_ASYNC_FINISHED, -}; - -struct EHCIPacket { - EHCIQueue *queue; - QTAILQ_ENTRY(EHCIPacket) next; - - EHCIqtd qtd; /* copy of current QTD (being worked on) */ - uint32_t qtdaddr; /* address QTD read from */ - - USBPacket packet; - QEMUSGList sgl; - int pid; - enum async_state async; -}; - -struct EHCIQueue { - EHCIState *ehci; - QTAILQ_ENTRY(EHCIQueue) next; - uint32_t seen; - uint64_t ts; - int async; - int transact_ctr; - - /* cached data from guest - needs to be flushed - * when guest removes an entry (doorbell, handshake sequence) - */ - EHCIqh qh; /* copy of current QH (being worked on) */ - uint32_t qhaddr; /* address QH read from */ - uint32_t qtdaddr; /* address QTD read from */ - int last_pid; /* pid of last packet executed */ - USBDevice *dev; - QTAILQ_HEAD(pkts_head, EHCIPacket) packets; -}; - -typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead; - -struct EHCIState { - USBBus bus; - DeviceState *device; - qemu_irq irq; - MemoryRegion mem; - AddressSpace *as; - MemoryRegion mem_caps; - MemoryRegion mem_opreg; - MemoryRegion mem_ports; - int companion_count; - bool companion_enable; - uint16_t capsbase; - uint16_t opregbase; - uint16_t portscbase; - uint16_t portnr; - - /* properties */ - uint32_t maxframes; - - /* - * EHCI spec version 1.0 Section 2.3 - * Host Controller Operational Registers - */ - uint8_t caps[CAPA_SIZE]; - union { - uint32_t opreg[0x44/sizeof(uint32_t)]; - struct { - uint32_t usbcmd; - uint32_t usbsts; - uint32_t usbintr; - uint32_t frindex; - uint32_t ctrldssegment; - uint32_t periodiclistbase; - uint32_t asynclistaddr; - uint32_t notused[9]; - uint32_t configflag; - }; - }; - uint32_t portsc[NB_PORTS]; - - /* - * Internal states, shadow registers, etc - */ - QEMUTimer *frame_timer; - QEMUBH *async_bh; - uint32_t astate; /* Current state in asynchronous schedule */ - uint32_t pstate; /* Current state in periodic schedule */ - USBPort ports[NB_PORTS]; - USBPort *companion_ports[NB_PORTS]; - uint32_t usbsts_pending; - uint32_t usbsts_frindex; - EHCIQueueHead aqueues; - EHCIQueueHead pqueues; - - /* which address to look at next */ - uint32_t a_fetch_addr; - uint32_t p_fetch_addr; - - USBPacket ipacket; - QEMUSGList isgl; - - uint64_t last_run_ns; - uint32_t async_stepdown; - uint32_t periodic_sched_active; - bool int_req_by_async; - VMChangeStateEntry *vmstate; -}; - -extern const VMStateDescription vmstate_ehci; - -void usb_ehci_init(EHCIState *s, DeviceState *dev); -void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp); -void usb_ehci_unrealize(EHCIState *s, DeviceState *dev, Error **errp); -void ehci_reset(void *opaque); - -#define TYPE_PCI_EHCI "pci-ehci-usb" -#define PCI_EHCI(obj) OBJECT_CHECK(EHCIPCIState, (obj), TYPE_PCI_EHCI) - -typedef struct EHCIPCIState { - /*< private >*/ - PCIDevice pcidev; - /*< public >*/ - - EHCIState ehci; -} EHCIPCIState; - - -#define TYPE_SYS_BUS_EHCI "sysbus-ehci-usb" -#define TYPE_EXYNOS4210_EHCI "exynos4210-ehci-usb" -#define TYPE_TEGRA2_EHCI "tegra2-ehci-usb" -#define TYPE_FUSBH200_EHCI "fusbh200-ehci-usb" - -#define SYS_BUS_EHCI(obj) \ - OBJECT_CHECK(EHCISysBusState, (obj), TYPE_SYS_BUS_EHCI) -#define SYS_BUS_EHCI_CLASS(class) \ - OBJECT_CLASS_CHECK(SysBusEHCIClass, (class), TYPE_SYS_BUS_EHCI) -#define SYS_BUS_EHCI_GET_CLASS(obj) \ - OBJECT_GET_CLASS(SysBusEHCIClass, (obj), TYPE_SYS_BUS_EHCI) - -typedef struct EHCISysBusState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - EHCIState ehci; -} EHCISysBusState; - -typedef struct SysBusEHCIClass { - /*< private >*/ - SysBusDeviceClass parent_class; - /*< public >*/ - - uint16_t capsbase; - uint16_t opregbase; - uint16_t portscbase; - uint16_t portnr; -} SysBusEHCIClass; - -#define FUSBH200_EHCI(obj) \ - OBJECT_CHECK(FUSBH200EHCIState, (obj), TYPE_FUSBH200_EHCI) - -typedef struct FUSBH200EHCIState { - /*< private >*/ - EHCISysBusState parent_obj; - /*< public >*/ - - MemoryRegion mem_vendor; -} FUSBH200EHCIState; - -#endif diff --git a/qemu/hw/usb/hcd-musb.c b/qemu/hw/usb/hcd-musb.c deleted file mode 100644 index 27d9d0bd8..000000000 --- a/qemu/hw/usb/hcd-musb.c +++ /dev/null @@ -1,1553 +0,0 @@ -/* - * "Inventra" High-speed Dual-Role Controller (MUSB-HDRC), Mentor Graphics, - * USB2.0 OTG compliant core used in various chips. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - * 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" -#include "hw/irq.h" -#include "hw/hw.h" - -/* Common USB registers */ -#define MUSB_HDRC_FADDR 0x00 /* 8-bit */ -#define MUSB_HDRC_POWER 0x01 /* 8-bit */ - -#define MUSB_HDRC_INTRTX 0x02 /* 16-bit */ -#define MUSB_HDRC_INTRRX 0x04 -#define MUSB_HDRC_INTRTXE 0x06 -#define MUSB_HDRC_INTRRXE 0x08 -#define MUSB_HDRC_INTRUSB 0x0a /* 8 bit */ -#define MUSB_HDRC_INTRUSBE 0x0b /* 8 bit */ -#define MUSB_HDRC_FRAME 0x0c /* 16-bit */ -#define MUSB_HDRC_INDEX 0x0e /* 8 bit */ -#define MUSB_HDRC_TESTMODE 0x0f /* 8 bit */ - -/* Per-EP registers in indexed mode */ -#define MUSB_HDRC_EP_IDX 0x10 /* 8-bit */ - -/* EP FIFOs */ -#define MUSB_HDRC_FIFO 0x20 - -/* Additional Control Registers */ -#define MUSB_HDRC_DEVCTL 0x60 /* 8 bit */ - -/* These are indexed */ -#define MUSB_HDRC_TXFIFOSZ 0x62 /* 8 bit (see masks) */ -#define MUSB_HDRC_RXFIFOSZ 0x63 /* 8 bit (see masks) */ -#define MUSB_HDRC_TXFIFOADDR 0x64 /* 16 bit offset shifted right 3 */ -#define MUSB_HDRC_RXFIFOADDR 0x66 /* 16 bit offset shifted right 3 */ - -/* Some more registers */ -#define MUSB_HDRC_VCTRL 0x68 /* 8 bit */ -#define MUSB_HDRC_HWVERS 0x6c /* 8 bit */ - -/* Added in HDRC 1.9(?) & MHDRC 1.4 */ -/* ULPI pass-through */ -#define MUSB_HDRC_ULPI_VBUSCTL 0x70 -#define MUSB_HDRC_ULPI_REGDATA 0x74 -#define MUSB_HDRC_ULPI_REGADDR 0x75 -#define MUSB_HDRC_ULPI_REGCTL 0x76 - -/* Extended config & PHY control */ -#define MUSB_HDRC_ENDCOUNT 0x78 /* 8 bit */ -#define MUSB_HDRC_DMARAMCFG 0x79 /* 8 bit */ -#define MUSB_HDRC_PHYWAIT 0x7a /* 8 bit */ -#define MUSB_HDRC_PHYVPLEN 0x7b /* 8 bit */ -#define MUSB_HDRC_HS_EOF1 0x7c /* 8 bit, units of 546.1 us */ -#define MUSB_HDRC_FS_EOF1 0x7d /* 8 bit, units of 533.3 ns */ -#define MUSB_HDRC_LS_EOF1 0x7e /* 8 bit, units of 1.067 us */ - -/* Per-EP BUSCTL registers */ -#define MUSB_HDRC_BUSCTL 0x80 - -/* Per-EP registers in flat mode */ -#define MUSB_HDRC_EP 0x100 - -/* offsets to registers in flat model */ -#define MUSB_HDRC_TXMAXP 0x00 /* 16 bit apparently */ -#define MUSB_HDRC_TXCSR 0x02 /* 16 bit apparently */ -#define MUSB_HDRC_CSR0 MUSB_HDRC_TXCSR /* re-used for EP0 */ -#define MUSB_HDRC_RXMAXP 0x04 /* 16 bit apparently */ -#define MUSB_HDRC_RXCSR 0x06 /* 16 bit apparently */ -#define MUSB_HDRC_RXCOUNT 0x08 /* 16 bit apparently */ -#define MUSB_HDRC_COUNT0 MUSB_HDRC_RXCOUNT /* re-used for EP0 */ -#define MUSB_HDRC_TXTYPE 0x0a /* 8 bit apparently */ -#define MUSB_HDRC_TYPE0 MUSB_HDRC_TXTYPE /* re-used for EP0 */ -#define MUSB_HDRC_TXINTERVAL 0x0b /* 8 bit apparently */ -#define MUSB_HDRC_NAKLIMIT0 MUSB_HDRC_TXINTERVAL /* re-used for EP0 */ -#define MUSB_HDRC_RXTYPE 0x0c /* 8 bit apparently */ -#define MUSB_HDRC_RXINTERVAL 0x0d /* 8 bit apparently */ -#define MUSB_HDRC_FIFOSIZE 0x0f /* 8 bit apparently */ -#define MUSB_HDRC_CONFIGDATA MGC_O_HDRC_FIFOSIZE /* re-used for EP0 */ - -/* "Bus control" registers */ -#define MUSB_HDRC_TXFUNCADDR 0x00 -#define MUSB_HDRC_TXHUBADDR 0x02 -#define MUSB_HDRC_TXHUBPORT 0x03 - -#define MUSB_HDRC_RXFUNCADDR 0x04 -#define MUSB_HDRC_RXHUBADDR 0x06 -#define MUSB_HDRC_RXHUBPORT 0x07 - -/* - * MUSBHDRC Register bit masks - */ - -/* POWER */ -#define MGC_M_POWER_ISOUPDATE 0x80 -#define MGC_M_POWER_SOFTCONN 0x40 -#define MGC_M_POWER_HSENAB 0x20 -#define MGC_M_POWER_HSMODE 0x10 -#define MGC_M_POWER_RESET 0x08 -#define MGC_M_POWER_RESUME 0x04 -#define MGC_M_POWER_SUSPENDM 0x02 -#define MGC_M_POWER_ENSUSPEND 0x01 - -/* INTRUSB */ -#define MGC_M_INTR_SUSPEND 0x01 -#define MGC_M_INTR_RESUME 0x02 -#define MGC_M_INTR_RESET 0x04 -#define MGC_M_INTR_BABBLE 0x04 -#define MGC_M_INTR_SOF 0x08 -#define MGC_M_INTR_CONNECT 0x10 -#define MGC_M_INTR_DISCONNECT 0x20 -#define MGC_M_INTR_SESSREQ 0x40 -#define MGC_M_INTR_VBUSERROR 0x80 /* FOR SESSION END */ -#define MGC_M_INTR_EP0 0x01 /* FOR EP0 INTERRUPT */ - -/* DEVCTL */ -#define MGC_M_DEVCTL_BDEVICE 0x80 -#define MGC_M_DEVCTL_FSDEV 0x40 -#define MGC_M_DEVCTL_LSDEV 0x20 -#define MGC_M_DEVCTL_VBUS 0x18 -#define MGC_S_DEVCTL_VBUS 3 -#define MGC_M_DEVCTL_HM 0x04 -#define MGC_M_DEVCTL_HR 0x02 -#define MGC_M_DEVCTL_SESSION 0x01 - -/* TESTMODE */ -#define MGC_M_TEST_FORCE_HOST 0x80 -#define MGC_M_TEST_FIFO_ACCESS 0x40 -#define MGC_M_TEST_FORCE_FS 0x20 -#define MGC_M_TEST_FORCE_HS 0x10 -#define MGC_M_TEST_PACKET 0x08 -#define MGC_M_TEST_K 0x04 -#define MGC_M_TEST_J 0x02 -#define MGC_M_TEST_SE0_NAK 0x01 - -/* CSR0 */ -#define MGC_M_CSR0_FLUSHFIFO 0x0100 -#define MGC_M_CSR0_TXPKTRDY 0x0002 -#define MGC_M_CSR0_RXPKTRDY 0x0001 - -/* CSR0 in Peripheral mode */ -#define MGC_M_CSR0_P_SVDSETUPEND 0x0080 -#define MGC_M_CSR0_P_SVDRXPKTRDY 0x0040 -#define MGC_M_CSR0_P_SENDSTALL 0x0020 -#define MGC_M_CSR0_P_SETUPEND 0x0010 -#define MGC_M_CSR0_P_DATAEND 0x0008 -#define MGC_M_CSR0_P_SENTSTALL 0x0004 - -/* CSR0 in Host mode */ -#define MGC_M_CSR0_H_NO_PING 0x0800 -#define MGC_M_CSR0_H_WR_DATATOGGLE 0x0400 /* set to allow setting: */ -#define MGC_M_CSR0_H_DATATOGGLE 0x0200 /* data toggle control */ -#define MGC_M_CSR0_H_NAKTIMEOUT 0x0080 -#define MGC_M_CSR0_H_STATUSPKT 0x0040 -#define MGC_M_CSR0_H_REQPKT 0x0020 -#define MGC_M_CSR0_H_ERROR 0x0010 -#define MGC_M_CSR0_H_SETUPPKT 0x0008 -#define MGC_M_CSR0_H_RXSTALL 0x0004 - -/* CONFIGDATA */ -#define MGC_M_CONFIGDATA_MPRXE 0x80 /* auto bulk pkt combining */ -#define MGC_M_CONFIGDATA_MPTXE 0x40 /* auto bulk pkt splitting */ -#define MGC_M_CONFIGDATA_BIGENDIAN 0x20 -#define MGC_M_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */ -#define MGC_M_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */ -#define MGC_M_CONFIGDATA_DYNFIFO 0x04 /* dynamic FIFO sizing */ -#define MGC_M_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */ -#define MGC_M_CONFIGDATA_UTMIDW 0x01 /* Width, 0 => 8b, 1 => 16b */ - -/* TXCSR in Peripheral and Host mode */ -#define MGC_M_TXCSR_AUTOSET 0x8000 -#define MGC_M_TXCSR_ISO 0x4000 -#define MGC_M_TXCSR_MODE 0x2000 -#define MGC_M_TXCSR_DMAENAB 0x1000 -#define MGC_M_TXCSR_FRCDATATOG 0x0800 -#define MGC_M_TXCSR_DMAMODE 0x0400 -#define MGC_M_TXCSR_CLRDATATOG 0x0040 -#define MGC_M_TXCSR_FLUSHFIFO 0x0008 -#define MGC_M_TXCSR_FIFONOTEMPTY 0x0002 -#define MGC_M_TXCSR_TXPKTRDY 0x0001 - -/* TXCSR in Peripheral mode */ -#define MGC_M_TXCSR_P_INCOMPTX 0x0080 -#define MGC_M_TXCSR_P_SENTSTALL 0x0020 -#define MGC_M_TXCSR_P_SENDSTALL 0x0010 -#define MGC_M_TXCSR_P_UNDERRUN 0x0004 - -/* TXCSR in Host mode */ -#define MGC_M_TXCSR_H_WR_DATATOGGLE 0x0200 -#define MGC_M_TXCSR_H_DATATOGGLE 0x0100 -#define MGC_M_TXCSR_H_NAKTIMEOUT 0x0080 -#define MGC_M_TXCSR_H_RXSTALL 0x0020 -#define MGC_M_TXCSR_H_ERROR 0x0004 - -/* RXCSR in Peripheral and Host mode */ -#define MGC_M_RXCSR_AUTOCLEAR 0x8000 -#define MGC_M_RXCSR_DMAENAB 0x2000 -#define MGC_M_RXCSR_DISNYET 0x1000 -#define MGC_M_RXCSR_DMAMODE 0x0800 -#define MGC_M_RXCSR_INCOMPRX 0x0100 -#define MGC_M_RXCSR_CLRDATATOG 0x0080 -#define MGC_M_RXCSR_FLUSHFIFO 0x0010 -#define MGC_M_RXCSR_DATAERROR 0x0008 -#define MGC_M_RXCSR_FIFOFULL 0x0002 -#define MGC_M_RXCSR_RXPKTRDY 0x0001 - -/* RXCSR in Peripheral mode */ -#define MGC_M_RXCSR_P_ISO 0x4000 -#define MGC_M_RXCSR_P_SENTSTALL 0x0040 -#define MGC_M_RXCSR_P_SENDSTALL 0x0020 -#define MGC_M_RXCSR_P_OVERRUN 0x0004 - -/* RXCSR in Host mode */ -#define MGC_M_RXCSR_H_AUTOREQ 0x4000 -#define MGC_M_RXCSR_H_WR_DATATOGGLE 0x0400 -#define MGC_M_RXCSR_H_DATATOGGLE 0x0200 -#define MGC_M_RXCSR_H_RXSTALL 0x0040 -#define MGC_M_RXCSR_H_REQPKT 0x0020 -#define MGC_M_RXCSR_H_ERROR 0x0004 - -/* HUBADDR */ -#define MGC_M_HUBADDR_MULTI_TT 0x80 - -/* ULPI: Added in HDRC 1.9(?) & MHDRC 1.4 */ -#define MGC_M_ULPI_VBCTL_USEEXTVBUSIND 0x02 -#define MGC_M_ULPI_VBCTL_USEEXTVBUS 0x01 -#define MGC_M_ULPI_REGCTL_INT_ENABLE 0x08 -#define MGC_M_ULPI_REGCTL_READNOTWRITE 0x04 -#define MGC_M_ULPI_REGCTL_COMPLETE 0x02 -#define MGC_M_ULPI_REGCTL_REG 0x01 - -/* #define MUSB_DEBUG */ - -#ifdef MUSB_DEBUG -#define TRACE(fmt,...) fprintf(stderr, "%s@%d: " fmt "\n", __FUNCTION__, \ - __LINE__, ##__VA_ARGS__) -#else -#define TRACE(...) -#endif - - -static void musb_attach(USBPort *port); -static void musb_detach(USBPort *port); -static void musb_child_detach(USBPort *port, USBDevice *child); -static void musb_schedule_cb(USBPort *port, USBPacket *p); -static void musb_async_cancel_device(MUSBState *s, USBDevice *dev); - -static USBPortOps musb_port_ops = { - .attach = musb_attach, - .detach = musb_detach, - .child_detach = musb_child_detach, - .complete = musb_schedule_cb, -}; - -static USBBusOps musb_bus_ops = { -}; - -typedef struct MUSBPacket MUSBPacket; -typedef struct MUSBEndPoint MUSBEndPoint; - -struct MUSBPacket { - USBPacket p; - MUSBEndPoint *ep; - int dir; -}; - -struct MUSBEndPoint { - uint16_t faddr[2]; - uint8_t haddr[2]; - uint8_t hport[2]; - uint16_t csr[2]; - uint16_t maxp[2]; - uint16_t rxcount; - uint8_t type[2]; - uint8_t interval[2]; - uint8_t config; - uint8_t fifosize; - int timeout[2]; /* Always in microframes */ - - uint8_t *buf[2]; - int fifolen[2]; - int fifostart[2]; - int fifoaddr[2]; - MUSBPacket packey[2]; - int status[2]; - int ext_size[2]; - - /* For callbacks' use */ - int epnum; - int interrupt[2]; - MUSBState *musb; - USBCallback *delayed_cb[2]; - QEMUTimer *intv_timer[2]; -}; - -struct MUSBState { - qemu_irq irqs[musb_irq_max]; - USBBus bus; - USBPort port; - - int idx; - uint8_t devctl; - uint8_t power; - uint8_t faddr; - - uint8_t intr; - uint8_t mask; - uint16_t tx_intr; - uint16_t tx_mask; - uint16_t rx_intr; - uint16_t rx_mask; - - int setup_len; - int session; - - uint8_t buf[0x8000]; - - /* Duplicating the world since 2008!... probably we should have 32 - * logical, single endpoints instead. */ - MUSBEndPoint ep[16]; -}; - -void musb_reset(MUSBState *s) -{ - int i; - - s->faddr = 0x00; - s->devctl = 0; - s->power = MGC_M_POWER_HSENAB; - s->tx_intr = 0x0000; - s->rx_intr = 0x0000; - s->tx_mask = 0xffff; - s->rx_mask = 0xffff; - s->intr = 0x00; - s->mask = 0x06; - s->idx = 0; - - s->setup_len = 0; - s->session = 0; - memset(s->buf, 0, sizeof(s->buf)); - - /* TODO: _DW */ - s->ep[0].config = MGC_M_CONFIGDATA_SOFTCONE | MGC_M_CONFIGDATA_DYNFIFO; - for (i = 0; i < 16; i ++) { - s->ep[i].fifosize = 64; - s->ep[i].maxp[0] = 0x40; - s->ep[i].maxp[1] = 0x40; - s->ep[i].musb = s; - s->ep[i].epnum = i; - usb_packet_init(&s->ep[i].packey[0].p); - usb_packet_init(&s->ep[i].packey[1].p); - } -} - -struct MUSBState *musb_init(DeviceState *parent_device, int gpio_base) -{ - MUSBState *s = g_malloc0(sizeof(*s)); - int i; - - for (i = 0; i < musb_irq_max; i++) { - s->irqs[i] = qdev_get_gpio_in(parent_device, gpio_base + i); - } - - musb_reset(s); - - usb_bus_new(&s->bus, sizeof(s->bus), &musb_bus_ops, parent_device); - usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - - return s; -} - -static void musb_vbus_set(MUSBState *s, int level) -{ - if (level) - s->devctl |= 3 << MGC_S_DEVCTL_VBUS; - else - s->devctl &= ~MGC_M_DEVCTL_VBUS; - - qemu_set_irq(s->irqs[musb_set_vbus], level); -} - -static void musb_intr_set(MUSBState *s, int line, int level) -{ - if (!level) { - s->intr &= ~(1 << line); - qemu_irq_lower(s->irqs[line]); - } else if (s->mask & (1 << line)) { - s->intr |= 1 << line; - qemu_irq_raise(s->irqs[line]); - } -} - -static void musb_tx_intr_set(MUSBState *s, int line, int level) -{ - if (!level) { - s->tx_intr &= ~(1 << line); - if (!s->tx_intr) - qemu_irq_lower(s->irqs[musb_irq_tx]); - } else if (s->tx_mask & (1 << line)) { - s->tx_intr |= 1 << line; - qemu_irq_raise(s->irqs[musb_irq_tx]); - } -} - -static void musb_rx_intr_set(MUSBState *s, int line, int level) -{ - if (line) { - if (!level) { - s->rx_intr &= ~(1 << line); - if (!s->rx_intr) - qemu_irq_lower(s->irqs[musb_irq_rx]); - } else if (s->rx_mask & (1 << line)) { - s->rx_intr |= 1 << line; - qemu_irq_raise(s->irqs[musb_irq_rx]); - } - } else - musb_tx_intr_set(s, line, level); -} - -uint32_t musb_core_intr_get(MUSBState *s) -{ - return (s->rx_intr << 15) | s->tx_intr; -} - -void musb_core_intr_clear(MUSBState *s, uint32_t mask) -{ - if (s->rx_intr) { - s->rx_intr &= mask >> 15; - if (!s->rx_intr) - qemu_irq_lower(s->irqs[musb_irq_rx]); - } - - if (s->tx_intr) { - s->tx_intr &= mask & 0xffff; - if (!s->tx_intr) - qemu_irq_lower(s->irqs[musb_irq_tx]); - } -} - -void musb_set_size(MUSBState *s, int epnum, int size, int is_tx) -{ - s->ep[epnum].ext_size[!is_tx] = size; - s->ep[epnum].fifostart[0] = 0; - s->ep[epnum].fifostart[1] = 0; - s->ep[epnum].fifolen[0] = 0; - s->ep[epnum].fifolen[1] = 0; -} - -static void musb_session_update(MUSBState *s, int prev_dev, int prev_sess) -{ - int detect_prev = prev_dev && prev_sess; - int detect = !!s->port.dev && s->session; - - if (detect && !detect_prev) { - /* Let's skip the ID pin sense and VBUS sense formalities and - * and signal a successful SRP directly. This should work at least - * for the Linux driver stack. */ - musb_intr_set(s, musb_irq_connect, 1); - - if (s->port.dev->speed == USB_SPEED_LOW) { - s->devctl &= ~MGC_M_DEVCTL_FSDEV; - s->devctl |= MGC_M_DEVCTL_LSDEV; - } else { - s->devctl |= MGC_M_DEVCTL_FSDEV; - s->devctl &= ~MGC_M_DEVCTL_LSDEV; - } - - /* A-mode? */ - s->devctl &= ~MGC_M_DEVCTL_BDEVICE; - - /* Host-mode bit? */ - s->devctl |= MGC_M_DEVCTL_HM; -#if 1 - musb_vbus_set(s, 1); -#endif - } else if (!detect && detect_prev) { -#if 1 - musb_vbus_set(s, 0); -#endif - } -} - -/* Attach or detach a device on our only port. */ -static void musb_attach(USBPort *port) -{ - MUSBState *s = (MUSBState *) port->opaque; - - musb_intr_set(s, musb_irq_vbus_request, 1); - musb_session_update(s, 0, s->session); -} - -static void musb_detach(USBPort *port) -{ - MUSBState *s = (MUSBState *) port->opaque; - - musb_async_cancel_device(s, port->dev); - - musb_intr_set(s, musb_irq_disconnect, 1); - musb_session_update(s, 1, s->session); -} - -static void musb_child_detach(USBPort *port, USBDevice *child) -{ - MUSBState *s = (MUSBState *) port->opaque; - - musb_async_cancel_device(s, child); -} - -static void musb_cb_tick0(void *opaque) -{ - MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - - ep->delayed_cb[0](&ep->packey[0].p, opaque); -} - -static void musb_cb_tick1(void *opaque) -{ - MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - - ep->delayed_cb[1](&ep->packey[1].p, opaque); -} - -#define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0) - -static void musb_schedule_cb(USBPort *port, USBPacket *packey) -{ - MUSBPacket *p = container_of(packey, MUSBPacket, p); - MUSBEndPoint *ep = p->ep; - int dir = p->dir; - int timeout = 0; - - if (ep->status[dir] == USB_RET_NAK) - timeout = ep->timeout[dir]; - else if (ep->interrupt[dir]) - timeout = 8; - else { - musb_cb_tick(ep); - return; - } - - if (!ep->intv_timer[dir]) - 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, NANOSECONDS_PER_SECOND, 8000)); -} - -static int musb_timeout(int ttype, int speed, int val) -{ -#if 1 - return val << 3; -#endif - - switch (ttype) { - case USB_ENDPOINT_XFER_CONTROL: - if (val < 2) - return 0; - else if (speed == USB_SPEED_HIGH) - return 1 << (val - 1); - else - return 8 << (val - 1); - - case USB_ENDPOINT_XFER_INT: - if (speed == USB_SPEED_HIGH) - if (val < 2) - return 0; - else - return 1 << (val - 1); - else - return val << 3; - - case USB_ENDPOINT_XFER_BULK: - case USB_ENDPOINT_XFER_ISOC: - if (val < 2) - return 0; - else if (speed == USB_SPEED_HIGH) - return 1 << (val - 1); - else - return 8 << (val - 1); - /* TODO: what with low-speed Bulk and Isochronous? */ - } - - hw_error("bad interval\n"); -} - -static void musb_packet(MUSBState *s, MUSBEndPoint *ep, - int epnum, int pid, int len, USBCallback cb, int dir) -{ - USBDevice *dev; - USBEndpoint *uep; - int idx = epnum && dir; - int id; - int ttype; - - /* ep->type[0,1] contains: - * in bits 7:6 the speed (0 - invalid, 1 - high, 2 - full, 3 - slow) - * in bits 5:4 the transfer type (BULK / INT) - * in bits 3:0 the EP num - */ - ttype = epnum ? (ep->type[idx] >> 4) & 3 : 0; - - ep->timeout[dir] = musb_timeout(ttype, - ep->type[idx] >> 6, ep->interval[idx]); - ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT; - ep->delayed_cb[dir] = cb; - - /* A wild guess on the FADDR semantics... */ - dev = usb_find_device(&s->port, ep->faddr[idx]); - uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf); - id = pid; - if (uep) { - id |= (dev->addr << 16) | (uep->nr << 8); - } - usb_packet_setup(&ep->packey[dir].p, pid, uep, 0, id, false, true); - usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len); - ep->packey[dir].ep = ep; - ep->packey[dir].dir = dir; - - usb_handle_packet(dev, &ep->packey[dir].p); - - if (ep->packey[dir].p.status == USB_RET_ASYNC) { - usb_device_flush_ep_queue(dev, uep); - ep->status[dir] = len; - return; - } - - if (ep->packey[dir].p.status == USB_RET_SUCCESS) { - ep->status[dir] = ep->packey[dir].p.actual_length; - } else { - ep->status[dir] = ep->packey[dir].p.status; - } - musb_schedule_cb(&s->port, &ep->packey[dir].p); -} - -static void musb_tx_packet_complete(USBPacket *packey, void *opaque) -{ - /* Unfortunately we can't use packey->devep because that's the remote - * endpoint number and may be different than our local. */ - MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - int epnum = ep->epnum; - MUSBState *s = ep->musb; - - ep->fifostart[0] = 0; - ep->fifolen[0] = 0; -#ifdef CLEAR_NAK - if (ep->status[0] != USB_RET_NAK) { -#endif - if (epnum) - ep->csr[0] &= ~(MGC_M_TXCSR_FIFONOTEMPTY | MGC_M_TXCSR_TXPKTRDY); - else - ep->csr[0] &= ~MGC_M_CSR0_TXPKTRDY; -#ifdef CLEAR_NAK - } -#endif - - /* Clear all of the error bits first */ - if (epnum) - ep->csr[0] &= ~(MGC_M_TXCSR_H_ERROR | MGC_M_TXCSR_H_RXSTALL | - MGC_M_TXCSR_H_NAKTIMEOUT); - else - ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL | - MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING); - - if (ep->status[0] == USB_RET_STALL) { - /* Command not supported by target! */ - ep->status[0] = 0; - - if (epnum) - ep->csr[0] |= MGC_M_TXCSR_H_RXSTALL; - else - ep->csr[0] |= MGC_M_CSR0_H_RXSTALL; - } - - if (ep->status[0] == USB_RET_NAK) { - ep->status[0] = 0; - - /* NAK timeouts are only generated in Bulk transfers and - * Data-errors in Isochronous. */ - if (ep->interrupt[0]) { - return; - } - - if (epnum) - ep->csr[0] |= MGC_M_TXCSR_H_NAKTIMEOUT; - else - ep->csr[0] |= MGC_M_CSR0_H_NAKTIMEOUT; - } - - if (ep->status[0] < 0) { - if (ep->status[0] == USB_RET_BABBLE) - musb_intr_set(s, musb_irq_rst_babble, 1); - - /* Pretend we've tried three times already and failed (in - * case of USB_TOKEN_SETUP). */ - if (epnum) - ep->csr[0] |= MGC_M_TXCSR_H_ERROR; - else - ep->csr[0] |= MGC_M_CSR0_H_ERROR; - - musb_tx_intr_set(s, epnum, 1); - return; - } - /* TODO: check len for over/underruns of an OUT packet? */ - -#ifdef SETUPLEN_HACK - if (!epnum && ep->packey[0].pid == USB_TOKEN_SETUP) - s->setup_len = ep->packey[0].data[6]; -#endif - - /* In DMA mode: if no error, assert DMA request for this EP, - * and skip the interrupt. */ - musb_tx_intr_set(s, epnum, 1); -} - -static void musb_rx_packet_complete(USBPacket *packey, void *opaque) -{ - /* Unfortunately we can't use packey->devep because that's the remote - * endpoint number and may be different than our local. */ - MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - int epnum = ep->epnum; - MUSBState *s = ep->musb; - - ep->fifostart[1] = 0; - ep->fifolen[1] = 0; - -#ifdef CLEAR_NAK - if (ep->status[1] != USB_RET_NAK) { -#endif - ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; - if (!epnum) - ep->csr[0] &= ~MGC_M_CSR0_H_REQPKT; -#ifdef CLEAR_NAK - } -#endif - - /* Clear all of the imaginable error bits first */ - ep->csr[1] &= ~(MGC_M_RXCSR_H_ERROR | MGC_M_RXCSR_H_RXSTALL | - MGC_M_RXCSR_DATAERROR); - if (!epnum) - ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL | - MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING); - - if (ep->status[1] == USB_RET_STALL) { - ep->status[1] = 0; - - ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL; - if (!epnum) - ep->csr[0] |= MGC_M_CSR0_H_RXSTALL; - } - - if (ep->status[1] == USB_RET_NAK) { - ep->status[1] = 0; - - /* NAK timeouts are only generated in Bulk transfers and - * Data-errors in Isochronous. */ - if (ep->interrupt[1]) { - musb_packet(s, ep, epnum, USB_TOKEN_IN, - packey->iov.size, musb_rx_packet_complete, 1); - return; - } - - ep->csr[1] |= MGC_M_RXCSR_DATAERROR; - if (!epnum) - ep->csr[0] |= MGC_M_CSR0_H_NAKTIMEOUT; - } - - if (ep->status[1] < 0) { - if (ep->status[1] == USB_RET_BABBLE) { - musb_intr_set(s, musb_irq_rst_babble, 1); - return; - } - - /* Pretend we've tried three times already and failed (in - * case of a control transfer). */ - ep->csr[1] |= MGC_M_RXCSR_H_ERROR; - if (!epnum) - ep->csr[0] |= MGC_M_CSR0_H_ERROR; - - musb_rx_intr_set(s, epnum, 1); - return; - } - /* TODO: check len for over/underruns of an OUT packet? */ - /* TODO: perhaps make use of e->ext_size[1] here. */ - - if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) { - ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY; - if (!epnum) - ep->csr[0] |= MGC_M_CSR0_RXPKTRDY; - - ep->rxcount = ep->status[1]; /* XXX: MIN(packey->len, ep->maxp[1]); */ - /* In DMA mode: assert DMA request for this EP */ - } - - /* Only if DMA has not been asserted */ - musb_rx_intr_set(s, epnum, 1); -} - -static void musb_async_cancel_device(MUSBState *s, USBDevice *dev) -{ - int ep, dir; - - for (ep = 0; ep < 16; ep++) { - for (dir = 0; dir < 2; dir++) { - if (!usb_packet_is_inflight(&s->ep[ep].packey[dir].p) || - s->ep[ep].packey[dir].p.ep->dev != dev) { - continue; - } - usb_cancel_packet(&s->ep[ep].packey[dir].p); - /* status updates needed here? */ - } - } -} - -static void musb_tx_rdy(MUSBState *s, int epnum) -{ - MUSBEndPoint *ep = s->ep + epnum; - int pid; - int total, valid = 0; - TRACE("start %d, len %d", ep->fifostart[0], ep->fifolen[0] ); - ep->fifostart[0] += ep->fifolen[0]; - ep->fifolen[0] = 0; - - /* XXX: how's the total size of the packet retrieved exactly in - * the generic case? */ - total = ep->maxp[0] & 0x3ff; - - if (ep->ext_size[0]) { - total = ep->ext_size[0]; - ep->ext_size[0] = 0; - valid = 1; - } - - /* If the packet is not fully ready yet, wait for a next segment. */ - if (epnum && (ep->fifostart[0]) < total) - return; - - if (!valid) - total = ep->fifostart[0]; - - pid = USB_TOKEN_OUT; - if (!epnum && (ep->csr[0] & MGC_M_CSR0_H_SETUPPKT)) { - pid = USB_TOKEN_SETUP; - if (total != 8) { - TRACE("illegal SETUPPKT length of %i bytes", total); - } - /* Controller should retry SETUP packets three times on errors - * but it doesn't make sense for us to do that. */ - } - - musb_packet(s, ep, epnum, pid, total, musb_tx_packet_complete, 0); -} - -static void musb_rx_req(MUSBState *s, int epnum) -{ - MUSBEndPoint *ep = s->ep + epnum; - int total; - - /* If we already have a packet, which didn't fit into the - * 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */ - if (ep->packey[1].p.pid == USB_TOKEN_IN && ep->status[1] >= 0 && - (ep->fifostart[1]) + ep->rxcount < - ep->packey[1].p.iov.size) { - TRACE("0x%08x, %d", ep->fifostart[1], ep->rxcount ); - ep->fifostart[1] += ep->rxcount; - ep->fifolen[1] = 0; - - ep->rxcount = MIN(ep->packey[0].p.iov.size - (ep->fifostart[1]), - ep->maxp[1]); - - ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; - if (!epnum) - ep->csr[0] &= ~MGC_M_CSR0_H_REQPKT; - - /* Clear all of the error bits first */ - ep->csr[1] &= ~(MGC_M_RXCSR_H_ERROR | MGC_M_RXCSR_H_RXSTALL | - MGC_M_RXCSR_DATAERROR); - if (!epnum) - ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL | - MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING); - - ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY; - if (!epnum) - ep->csr[0] |= MGC_M_CSR0_RXPKTRDY; - musb_rx_intr_set(s, epnum, 1); - return; - } - - /* The driver sets maxp[1] to 64 or less because it knows the hardware - * FIFO is this deep. Bigger packets get split in - * usb_generic_handle_packet but we can also do the splitting locally - * for performance. It turns out we can also have a bigger FIFO and - * ignore the limit set in ep->maxp[1]. The Linux MUSB driver deals - * OK with single packets of even 32KB and we avoid splitting, however - * usb_msd.c sometimes sends a packet bigger than what Linux expects - * (e.g. 8192 bytes instead of 4096) and we get an OVERRUN. Splitting - * hides this overrun from Linux. Up to 4096 everything is fine - * though. Currently this is disabled. - * - * XXX: mind ep->fifosize. */ - total = MIN(ep->maxp[1] & 0x3ff, sizeof(s->buf)); - -#ifdef SETUPLEN_HACK - /* Why should *we* do that instead of Linux? */ - if (!epnum) { - if (ep->packey[0].p.devaddr == 2) { - total = MIN(s->setup_len, 8); - } else { - total = MIN(s->setup_len, 64); - } - s->setup_len -= total; - } -#endif - - musb_packet(s, ep, epnum, USB_TOKEN_IN, total, musb_rx_packet_complete, 1); -} - -static uint8_t musb_read_fifo(MUSBEndPoint *ep) -{ - uint8_t value; - if (ep->fifolen[1] >= 64) { - /* We have a FIFO underrun */ - TRACE("EP%d FIFO is now empty, stop reading", ep->epnum); - return 0x00000000; - } - /* In DMA mode clear RXPKTRDY and set REQPKT automatically - * (if AUTOREQ is set) */ - - ep->csr[1] &= ~MGC_M_RXCSR_FIFOFULL; - value=ep->buf[1][ep->fifostart[1] + ep->fifolen[1] ++]; - TRACE("EP%d 0x%02x, %d", ep->epnum, value, ep->fifolen[1] ); - return value; -} - -static void musb_write_fifo(MUSBEndPoint *ep, uint8_t value) -{ - TRACE("EP%d = %02x", ep->epnum, value); - if (ep->fifolen[0] >= 64) { - /* We have a FIFO overrun */ - TRACE("EP%d FIFO exceeded 64 bytes, stop feeding data", ep->epnum); - return; - } - - ep->buf[0][ep->fifostart[0] + ep->fifolen[0] ++] = value; - ep->csr[0] |= MGC_M_TXCSR_FIFONOTEMPTY; -} - -static void musb_ep_frame_cancel(MUSBEndPoint *ep, int dir) -{ - if (ep->intv_timer[dir]) - timer_del(ep->intv_timer[dir]); -} - -/* Bus control */ -static uint8_t musb_busctl_readb(void *opaque, int ep, int addr) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - /* For USB2.0 HS hubs only */ - case MUSB_HDRC_TXHUBADDR: - return s->ep[ep].haddr[0]; - case MUSB_HDRC_TXHUBPORT: - return s->ep[ep].hport[0]; - case MUSB_HDRC_RXHUBADDR: - return s->ep[ep].haddr[1]; - case MUSB_HDRC_RXHUBPORT: - return s->ep[ep].hport[1]; - - default: - TRACE("unknown register 0x%02x", addr); - return 0x00; - }; -} - -static void musb_busctl_writeb(void *opaque, int ep, int addr, uint8_t value) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXFUNCADDR: - s->ep[ep].faddr[0] = value; - break; - case MUSB_HDRC_RXFUNCADDR: - s->ep[ep].faddr[1] = value; - break; - case MUSB_HDRC_TXHUBADDR: - s->ep[ep].haddr[0] = value; - break; - case MUSB_HDRC_TXHUBPORT: - s->ep[ep].hport[0] = value; - break; - case MUSB_HDRC_RXHUBADDR: - s->ep[ep].haddr[1] = value; - break; - case MUSB_HDRC_RXHUBPORT: - s->ep[ep].hport[1] = value; - break; - - default: - TRACE("unknown register 0x%02x", addr); - break; - }; -} - -static uint16_t musb_busctl_readh(void *opaque, int ep, int addr) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXFUNCADDR: - return s->ep[ep].faddr[0]; - case MUSB_HDRC_RXFUNCADDR: - return s->ep[ep].faddr[1]; - - default: - return musb_busctl_readb(s, ep, addr) | - (musb_busctl_readb(s, ep, addr | 1) << 8); - }; -} - -static void musb_busctl_writeh(void *opaque, int ep, int addr, uint16_t value) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXFUNCADDR: - s->ep[ep].faddr[0] = value; - break; - case MUSB_HDRC_RXFUNCADDR: - s->ep[ep].faddr[1] = value; - break; - - default: - musb_busctl_writeb(s, ep, addr, value & 0xff); - musb_busctl_writeb(s, ep, addr | 1, value >> 8); - }; -} - -/* Endpoint control */ -static uint8_t musb_ep_readb(void *opaque, int ep, int addr) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXTYPE: - return s->ep[ep].type[0]; - case MUSB_HDRC_TXINTERVAL: - return s->ep[ep].interval[0]; - case MUSB_HDRC_RXTYPE: - return s->ep[ep].type[1]; - case MUSB_HDRC_RXINTERVAL: - return s->ep[ep].interval[1]; - case (MUSB_HDRC_FIFOSIZE & ~1): - return 0x00; - case MUSB_HDRC_FIFOSIZE: - return ep ? s->ep[ep].fifosize : s->ep[ep].config; - case MUSB_HDRC_RXCOUNT: - return s->ep[ep].rxcount; - - default: - TRACE("unknown register 0x%02x", addr); - return 0x00; - }; -} - -static void musb_ep_writeb(void *opaque, int ep, int addr, uint8_t value) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXTYPE: - s->ep[ep].type[0] = value; - break; - case MUSB_HDRC_TXINTERVAL: - s->ep[ep].interval[0] = value; - musb_ep_frame_cancel(&s->ep[ep], 0); - break; - case MUSB_HDRC_RXTYPE: - s->ep[ep].type[1] = value; - break; - case MUSB_HDRC_RXINTERVAL: - s->ep[ep].interval[1] = value; - musb_ep_frame_cancel(&s->ep[ep], 1); - break; - case (MUSB_HDRC_FIFOSIZE & ~1): - break; - case MUSB_HDRC_FIFOSIZE: - TRACE("somebody messes with fifosize (now %i bytes)", value); - s->ep[ep].fifosize = value; - break; - default: - TRACE("unknown register 0x%02x", addr); - break; - }; -} - -static uint16_t musb_ep_readh(void *opaque, int ep, int addr) -{ - MUSBState *s = (MUSBState *) opaque; - uint16_t ret; - - switch (addr) { - case MUSB_HDRC_TXMAXP: - return s->ep[ep].maxp[0]; - case MUSB_HDRC_TXCSR: - return s->ep[ep].csr[0]; - case MUSB_HDRC_RXMAXP: - return s->ep[ep].maxp[1]; - case MUSB_HDRC_RXCSR: - ret = s->ep[ep].csr[1]; - - /* TODO: This and other bits probably depend on - * ep->csr[1] & MGC_M_RXCSR_AUTOCLEAR. */ - if (s->ep[ep].csr[1] & MGC_M_RXCSR_AUTOCLEAR) - s->ep[ep].csr[1] &= ~MGC_M_RXCSR_RXPKTRDY; - - return ret; - case MUSB_HDRC_RXCOUNT: - return s->ep[ep].rxcount; - - default: - return musb_ep_readb(s, ep, addr) | - (musb_ep_readb(s, ep, addr | 1) << 8); - }; -} - -static void musb_ep_writeh(void *opaque, int ep, int addr, uint16_t value) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXMAXP: - s->ep[ep].maxp[0] = value; - break; - case MUSB_HDRC_TXCSR: - if (ep) { - s->ep[ep].csr[0] &= value & 0xa6; - s->ep[ep].csr[0] |= value & 0xff59; - } else { - s->ep[ep].csr[0] &= value & 0x85; - s->ep[ep].csr[0] |= value & 0xf7a; - } - - musb_ep_frame_cancel(&s->ep[ep], 0); - - if ((ep && (value & MGC_M_TXCSR_FLUSHFIFO)) || - (!ep && (value & MGC_M_CSR0_FLUSHFIFO))) { - s->ep[ep].fifolen[0] = 0; - s->ep[ep].fifostart[0] = 0; - if (ep) - s->ep[ep].csr[0] &= - ~(MGC_M_TXCSR_FIFONOTEMPTY | MGC_M_TXCSR_TXPKTRDY); - else - s->ep[ep].csr[0] &= - ~(MGC_M_CSR0_TXPKTRDY | MGC_M_CSR0_RXPKTRDY); - } - if ( - (ep && -#ifdef CLEAR_NAK - (value & MGC_M_TXCSR_TXPKTRDY) && - !(value & MGC_M_TXCSR_H_NAKTIMEOUT)) || -#else - (value & MGC_M_TXCSR_TXPKTRDY)) || -#endif - (!ep && -#ifdef CLEAR_NAK - (value & MGC_M_CSR0_TXPKTRDY) && - !(value & MGC_M_CSR0_H_NAKTIMEOUT))) -#else - (value & MGC_M_CSR0_TXPKTRDY))) -#endif - musb_tx_rdy(s, ep); - if (!ep && - (value & MGC_M_CSR0_H_REQPKT) && -#ifdef CLEAR_NAK - !(value & (MGC_M_CSR0_H_NAKTIMEOUT | - MGC_M_CSR0_RXPKTRDY))) -#else - !(value & MGC_M_CSR0_RXPKTRDY)) -#endif - musb_rx_req(s, ep); - break; - - case MUSB_HDRC_RXMAXP: - s->ep[ep].maxp[1] = value; - break; - case MUSB_HDRC_RXCSR: - /* (DMA mode only) */ - if ( - (value & MGC_M_RXCSR_H_AUTOREQ) && - !(value & MGC_M_RXCSR_RXPKTRDY) && - (s->ep[ep].csr[1] & MGC_M_RXCSR_RXPKTRDY)) - value |= MGC_M_RXCSR_H_REQPKT; - - s->ep[ep].csr[1] &= 0x102 | (value & 0x4d); - s->ep[ep].csr[1] |= value & 0xfeb0; - - musb_ep_frame_cancel(&s->ep[ep], 1); - - if (value & MGC_M_RXCSR_FLUSHFIFO) { - s->ep[ep].fifolen[1] = 0; - s->ep[ep].fifostart[1] = 0; - s->ep[ep].csr[1] &= ~(MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY); - /* If double buffering and we have two packets ready, flush - * only the first one and set up the fifo at the second packet. */ - } -#ifdef CLEAR_NAK - if ((value & MGC_M_RXCSR_H_REQPKT) && !(value & MGC_M_RXCSR_DATAERROR)) -#else - if (value & MGC_M_RXCSR_H_REQPKT) -#endif - musb_rx_req(s, ep); - break; - case MUSB_HDRC_RXCOUNT: - s->ep[ep].rxcount = value; - break; - - default: - musb_ep_writeb(s, ep, addr, value & 0xff); - musb_ep_writeb(s, ep, addr | 1, value >> 8); - }; -} - -/* Generic control */ -static uint32_t musb_readb(void *opaque, hwaddr addr) -{ - MUSBState *s = (MUSBState *) opaque; - int ep, i; - uint8_t ret; - - switch (addr) { - case MUSB_HDRC_FADDR: - return s->faddr; - case MUSB_HDRC_POWER: - return s->power; - case MUSB_HDRC_INTRUSB: - ret = s->intr; - for (i = 0; i < sizeof(ret) * 8; i ++) - if (ret & (1 << i)) - musb_intr_set(s, i, 0); - return ret; - case MUSB_HDRC_INTRUSBE: - return s->mask; - case MUSB_HDRC_INDEX: - return s->idx; - case MUSB_HDRC_TESTMODE: - return 0x00; - - case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf): - return musb_ep_readb(s, s->idx, addr & 0xf); - - case MUSB_HDRC_DEVCTL: - return s->devctl; - - case MUSB_HDRC_TXFIFOSZ: - case MUSB_HDRC_RXFIFOSZ: - case MUSB_HDRC_VCTRL: - /* TODO */ - return 0x00; - - case MUSB_HDRC_HWVERS: - return (1 << 10) | 400; - - case (MUSB_HDRC_VCTRL | 1): - case (MUSB_HDRC_HWVERS | 1): - case (MUSB_HDRC_DEVCTL | 1): - return 0x00; - - case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f): - ep = (addr >> 3) & 0xf; - return musb_busctl_readb(s, ep, addr & 0x7); - - case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff): - ep = (addr >> 4) & 0xf; - return musb_ep_readb(s, ep, addr & 0xf); - - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - return musb_read_fifo(s->ep + ep); - - default: - TRACE("unknown register 0x%02x", (int) addr); - return 0x00; - }; -} - -static void musb_writeb(void *opaque, hwaddr addr, uint32_t value) -{ - MUSBState *s = (MUSBState *) opaque; - int ep; - - switch (addr) { - case MUSB_HDRC_FADDR: - s->faddr = value & 0x7f; - break; - case MUSB_HDRC_POWER: - s->power = (value & 0xef) | (s->power & 0x10); - /* MGC_M_POWER_RESET is also read-only in Peripheral Mode */ - if ((value & MGC_M_POWER_RESET) && s->port.dev) { - usb_device_reset(s->port.dev); - /* Negotiate high-speed operation if MGC_M_POWER_HSENAB is set. */ - if ((value & MGC_M_POWER_HSENAB) && - s->port.dev->speed == USB_SPEED_HIGH) - s->power |= MGC_M_POWER_HSMODE; /* Success */ - /* Restart frame counting. */ - } - if (value & MGC_M_POWER_SUSPENDM) { - /* When all transfers finish, suspend and if MGC_M_POWER_ENSUSPEND - * is set, also go into low power mode. Frame counting stops. */ - /* XXX: Cleared when the interrupt register is read */ - } - if (value & MGC_M_POWER_RESUME) { - /* Wait 20ms and signal resuming on the bus. Frame counting - * restarts. */ - } - break; - case MUSB_HDRC_INTRUSB: - break; - case MUSB_HDRC_INTRUSBE: - s->mask = value & 0xff; - break; - case MUSB_HDRC_INDEX: - s->idx = value & 0xf; - break; - case MUSB_HDRC_TESTMODE: - break; - - case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf): - musb_ep_writeb(s, s->idx, addr & 0xf, value); - break; - - case MUSB_HDRC_DEVCTL: - s->session = !!(value & MGC_M_DEVCTL_SESSION); - musb_session_update(s, - !!s->port.dev, - !!(s->devctl & MGC_M_DEVCTL_SESSION)); - - /* It seems this is the only R/W bit in this register? */ - s->devctl &= ~MGC_M_DEVCTL_SESSION; - s->devctl |= value & MGC_M_DEVCTL_SESSION; - break; - - case MUSB_HDRC_TXFIFOSZ: - case MUSB_HDRC_RXFIFOSZ: - case MUSB_HDRC_VCTRL: - /* TODO */ - break; - - case (MUSB_HDRC_VCTRL | 1): - case (MUSB_HDRC_DEVCTL | 1): - break; - - case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f): - ep = (addr >> 3) & 0xf; - musb_busctl_writeb(s, ep, addr & 0x7, value); - break; - - case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff): - ep = (addr >> 4) & 0xf; - musb_ep_writeb(s, ep, addr & 0xf, value); - break; - - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - musb_write_fifo(s->ep + ep, value & 0xff); - break; - - default: - TRACE("unknown register 0x%02x", (int) addr); - break; - }; -} - -static uint32_t musb_readh(void *opaque, hwaddr addr) -{ - MUSBState *s = (MUSBState *) opaque; - int ep, i; - uint16_t ret; - - switch (addr) { - case MUSB_HDRC_INTRTX: - ret = s->tx_intr; - /* Auto clear */ - for (i = 0; i < sizeof(ret) * 8; i ++) - if (ret & (1 << i)) - musb_tx_intr_set(s, i, 0); - return ret; - case MUSB_HDRC_INTRRX: - ret = s->rx_intr; - /* Auto clear */ - for (i = 0; i < sizeof(ret) * 8; i ++) - if (ret & (1 << i)) - musb_rx_intr_set(s, i, 0); - return ret; - case MUSB_HDRC_INTRTXE: - return s->tx_mask; - case MUSB_HDRC_INTRRXE: - return s->rx_mask; - - case MUSB_HDRC_FRAME: - /* TODO */ - return 0x0000; - case MUSB_HDRC_TXFIFOADDR: - return s->ep[s->idx].fifoaddr[0]; - case MUSB_HDRC_RXFIFOADDR: - return s->ep[s->idx].fifoaddr[1]; - - case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf): - return musb_ep_readh(s, s->idx, addr & 0xf); - - case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f): - ep = (addr >> 3) & 0xf; - return musb_busctl_readh(s, ep, addr & 0x7); - - case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff): - ep = (addr >> 4) & 0xf; - return musb_ep_readh(s, ep, addr & 0xf); - - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - return (musb_read_fifo(s->ep + ep) | musb_read_fifo(s->ep + ep) << 8); - - default: - return musb_readb(s, addr) | (musb_readb(s, addr | 1) << 8); - }; -} - -static void musb_writeh(void *opaque, hwaddr addr, uint32_t value) -{ - MUSBState *s = (MUSBState *) opaque; - int ep; - - switch (addr) { - case MUSB_HDRC_INTRTXE: - s->tx_mask = value; - /* XXX: the masks seem to apply on the raising edge like with - * edge-triggered interrupts, thus no need to update. I may be - * wrong though. */ - break; - case MUSB_HDRC_INTRRXE: - s->rx_mask = value; - break; - - case MUSB_HDRC_FRAME: - /* TODO */ - break; - case MUSB_HDRC_TXFIFOADDR: - s->ep[s->idx].fifoaddr[0] = value; - s->ep[s->idx].buf[0] = - s->buf + ((value << 3) & 0x7ff ); - break; - case MUSB_HDRC_RXFIFOADDR: - s->ep[s->idx].fifoaddr[1] = value; - s->ep[s->idx].buf[1] = - s->buf + ((value << 3) & 0x7ff); - break; - - case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf): - musb_ep_writeh(s, s->idx, addr & 0xf, value); - break; - - case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f): - ep = (addr >> 3) & 0xf; - musb_busctl_writeh(s, ep, addr & 0x7, value); - break; - - case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff): - ep = (addr >> 4) & 0xf; - musb_ep_writeh(s, ep, addr & 0xf, value); - break; - - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - musb_write_fifo(s->ep + ep, value & 0xff); - musb_write_fifo(s->ep + ep, (value >> 8) & 0xff); - break; - - default: - musb_writeb(s, addr, value & 0xff); - musb_writeb(s, addr | 1, value >> 8); - }; -} - -static uint32_t musb_readw(void *opaque, hwaddr addr) -{ - MUSBState *s = (MUSBState *) opaque; - int ep; - - switch (addr) { - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - return ( musb_read_fifo(s->ep + ep) | - musb_read_fifo(s->ep + ep) << 8 | - musb_read_fifo(s->ep + ep) << 16 | - musb_read_fifo(s->ep + ep) << 24 ); - default: - TRACE("unknown register 0x%02x", (int) addr); - return 0x00000000; - }; -} - -static void musb_writew(void *opaque, hwaddr addr, uint32_t value) -{ - MUSBState *s = (MUSBState *) opaque; - int ep; - - switch (addr) { - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - musb_write_fifo(s->ep + ep, value & 0xff); - musb_write_fifo(s->ep + ep, (value >> 8 ) & 0xff); - musb_write_fifo(s->ep + ep, (value >> 16) & 0xff); - musb_write_fifo(s->ep + ep, (value >> 24) & 0xff); - break; - default: - TRACE("unknown register 0x%02x", (int) addr); - break; - }; -} - -CPUReadMemoryFunc * const musb_read[] = { - musb_readb, - musb_readh, - musb_readw, -}; - -CPUWriteMemoryFunc * const musb_write[] = { - musb_writeb, - musb_writeh, - musb_writew, -}; diff --git a/qemu/hw/usb/hcd-ohci.c b/qemu/hw/usb/hcd-ohci.c deleted file mode 100644 index ffab561cf..000000000 --- a/qemu/hw/usb/hcd-ohci.c +++ /dev/null @@ -1,2164 +0,0 @@ -/* - * QEMU USB OHCI Emulation - * Copyright (c) 2004 Gianni Tedesco - * Copyright (c) 2006 CodeSourcery - * Copyright (c) 2006 Openedhand Ltd. - * - * 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 . - * - * TODO: - * o Isochronous transfers - * o Allocate bandwidth in frames properly - * o Disable timers when nothing needs to be done, or remove timer usage - * all together. - * 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" -#include "hw/sysbus.h" -#include "hw/qdev-dma.h" -#include "trace.h" - -/* This causes frames to occur 1000x slower */ -//#define OHCI_TIME_WARP 1 - -/* Number of Downstream Ports on the root hub. */ - -#define OHCI_MAX_PORTS 15 - -static int64_t usb_frame_time; -static int64_t usb_bit_time; - -typedef struct OHCIPort { - USBPort port; - uint32_t ctrl; -} OHCIPort; - -typedef struct { - USBBus bus; - qemu_irq irq; - MemoryRegion mem; - AddressSpace *as; - int num_ports; - const char *name; - - QEMUTimer *eof_timer; - int64_t sof_time; - - /* OHCI state */ - /* Control partition */ - uint32_t ctl, status; - uint32_t intr_status; - uint32_t intr; - - /* memory pointer partition */ - uint32_t hcca; - uint32_t ctrl_head, ctrl_cur; - uint32_t bulk_head, bulk_cur; - uint32_t per_cur; - uint32_t done; - int32_t done_count; - - /* Frame counter partition */ - uint16_t fsmps; - uint8_t fit; - uint16_t fi; - uint8_t frt; - uint16_t frame_number; - uint16_t padding; - uint32_t pstart; - uint32_t lst; - - /* Root Hub partition */ - uint32_t rhdesc_a, rhdesc_b; - uint32_t rhstatus; - OHCIPort rhport[OHCI_MAX_PORTS]; - - /* PXA27x Non-OHCI events */ - uint32_t hstatus; - uint32_t hmask; - uint32_t hreset; - uint32_t htest; - - /* SM501 local memory offset */ - dma_addr_t localmem_base; - - /* Active packets. */ - uint32_t old_ctl; - USBPacket usb_packet; - uint8_t usb_buf[8192]; - uint32_t async_td; - bool async_complete; - -} OHCIState; - -/* Host Controller Communications Area */ -struct ohci_hcca { - uint32_t intr[32]; - uint16_t frame, pad; - uint32_t done; -}; -#define HCCA_WRITEBACK_OFFSET offsetof(struct ohci_hcca, frame) -#define HCCA_WRITEBACK_SIZE 8 /* frame, pad, done */ - -#define ED_WBACK_OFFSET offsetof(struct ohci_ed, head) -#define ED_WBACK_SIZE 4 - -static void ohci_bus_stop(OHCIState *ohci); -static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev); - -/* Bitfields for the first word of an Endpoint Desciptor. */ -#define OHCI_ED_FA_SHIFT 0 -#define OHCI_ED_FA_MASK (0x7f<> OHCI_##field##_SHIFT) - -#define OHCI_SET_BM(val, field, newval) do { \ - val &= ~OHCI_##field##_MASK; \ - val |= ((newval) << OHCI_##field##_SHIFT) & OHCI_##field##_MASK; \ - } while(0) - -/* endpoint descriptor */ -struct ohci_ed { - uint32_t flags; - uint32_t tail; - uint32_t head; - uint32_t next; -}; - -/* General transfer descriptor */ -struct ohci_td { - uint32_t flags; - uint32_t cbp; - uint32_t next; - uint32_t be; -}; - -/* Isochronous transfer descriptor */ -struct ohci_iso_td { - uint32_t flags; - uint32_t bp; - uint32_t next; - uint32_t be; - uint16_t offset[8]; -}; - -#define USB_HZ 12000000 - -/* OHCI Local stuff */ -#define OHCI_CTL_CBSR ((1<<0)|(1<<1)) -#define OHCI_CTL_PLE (1<<2) -#define OHCI_CTL_IE (1<<3) -#define OHCI_CTL_CLE (1<<4) -#define OHCI_CTL_BLE (1<<5) -#define OHCI_CTL_HCFS ((1<<6)|(1<<7)) -#define OHCI_USB_RESET 0x00 -#define OHCI_USB_RESUME 0x40 -#define OHCI_USB_OPERATIONAL 0x80 -#define OHCI_USB_SUSPEND 0xc0 -#define OHCI_CTL_IR (1<<8) -#define OHCI_CTL_RWC (1<<9) -#define OHCI_CTL_RWE (1<<10) - -#define OHCI_STATUS_HCR (1<<0) -#define OHCI_STATUS_CLF (1<<1) -#define OHCI_STATUS_BLF (1<<2) -#define OHCI_STATUS_OCR (1<<3) -#define OHCI_STATUS_SOC ((1<<6)|(1<<7)) - -#define OHCI_INTR_SO (1U<<0) /* Scheduling overrun */ -#define OHCI_INTR_WD (1U<<1) /* HcDoneHead writeback */ -#define OHCI_INTR_SF (1U<<2) /* Start of frame */ -#define OHCI_INTR_RD (1U<<3) /* Resume detect */ -#define OHCI_INTR_UE (1U<<4) /* Unrecoverable error */ -#define OHCI_INTR_FNO (1U<<5) /* Frame number overflow */ -#define OHCI_INTR_RHSC (1U<<6) /* Root hub status change */ -#define OHCI_INTR_OC (1U<<30) /* Ownership change */ -#define OHCI_INTR_MIE (1U<<31) /* Master Interrupt Enable */ - -#define OHCI_HCCA_SIZE 0x100 -#define OHCI_HCCA_MASK 0xffffff00 - -#define OHCI_EDPTR_MASK 0xfffffff0 - -#define OHCI_FMI_FI 0x00003fff -#define OHCI_FMI_FSMPS 0xffff0000 -#define OHCI_FMI_FIT 0x80000000 - -#define OHCI_FR_RT (1U<<31) - -#define OHCI_LS_THRESH 0x628 - -#define OHCI_RHA_RW_MASK 0x00000000 /* Mask of supported features. */ -#define OHCI_RHA_PSM (1<<8) -#define OHCI_RHA_NPS (1<<9) -#define OHCI_RHA_DT (1<<10) -#define OHCI_RHA_OCPM (1<<11) -#define OHCI_RHA_NOCP (1<<12) -#define OHCI_RHA_POTPGT_MASK 0xff000000 - -#define OHCI_RHS_LPS (1U<<0) -#define OHCI_RHS_OCI (1U<<1) -#define OHCI_RHS_DRWE (1U<<15) -#define OHCI_RHS_LPSC (1U<<16) -#define OHCI_RHS_OCIC (1U<<17) -#define OHCI_RHS_CRWE (1U<<31) - -#define OHCI_PORT_CCS (1<<0) -#define OHCI_PORT_PES (1<<1) -#define OHCI_PORT_PSS (1<<2) -#define OHCI_PORT_POCI (1<<3) -#define OHCI_PORT_PRS (1<<4) -#define OHCI_PORT_PPS (1<<8) -#define OHCI_PORT_LSDA (1<<9) -#define OHCI_PORT_CSC (1<<16) -#define OHCI_PORT_PESC (1<<17) -#define OHCI_PORT_PSSC (1<<18) -#define OHCI_PORT_OCIC (1<<19) -#define OHCI_PORT_PRSC (1<<20) -#define OHCI_PORT_WTC (OHCI_PORT_CSC|OHCI_PORT_PESC|OHCI_PORT_PSSC \ - |OHCI_PORT_OCIC|OHCI_PORT_PRSC) - -#define OHCI_TD_DIR_SETUP 0x0 -#define OHCI_TD_DIR_OUT 0x1 -#define OHCI_TD_DIR_IN 0x2 -#define OHCI_TD_DIR_RESERVED 0x3 - -#define OHCI_CC_NOERROR 0x0 -#define OHCI_CC_CRC 0x1 -#define OHCI_CC_BITSTUFFING 0x2 -#define OHCI_CC_DATATOGGLEMISMATCH 0x3 -#define OHCI_CC_STALL 0x4 -#define OHCI_CC_DEVICENOTRESPONDING 0x5 -#define OHCI_CC_PIDCHECKFAILURE 0x6 -#define OHCI_CC_UNDEXPETEDPID 0x7 -#define OHCI_CC_DATAOVERRUN 0x8 -#define OHCI_CC_DATAUNDERRUN 0x9 -#define OHCI_CC_BUFFEROVERRUN 0xc -#define OHCI_CC_BUFFERUNDERRUN 0xd - -#define OHCI_HRESET_FSBIR (1 << 0) - -static void ohci_die(OHCIState *ohci); - -/* Update IRQ levels */ -static inline void ohci_intr_update(OHCIState *ohci) -{ - int level = 0; - - if ((ohci->intr & OHCI_INTR_MIE) && - (ohci->intr_status & ohci->intr)) - level = 1; - - qemu_set_irq(ohci->irq, level); -} - -/* Set an interrupt */ -static inline void ohci_set_interrupt(OHCIState *ohci, uint32_t intr) -{ - ohci->intr_status |= intr; - ohci_intr_update(ohci); -} - -/* Attach or detach a device on a root hub port. */ -static void ohci_attach(USBPort *port1) -{ - OHCIState *s = port1->opaque; - OHCIPort *port = &s->rhport[port1->index]; - uint32_t old_state = port->ctrl; - - /* set connect status */ - port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC; - - /* update speed */ - if (port->port.dev->speed == USB_SPEED_LOW) { - port->ctrl |= OHCI_PORT_LSDA; - } else { - port->ctrl &= ~OHCI_PORT_LSDA; - } - - /* notify of remote-wakeup */ - if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) { - ohci_set_interrupt(s, OHCI_INTR_RD); - } - - trace_usb_ohci_port_attach(port1->index); - - if (old_state != port->ctrl) { - ohci_set_interrupt(s, OHCI_INTR_RHSC); - } -} - -static void ohci_detach(USBPort *port1) -{ - OHCIState *s = port1->opaque; - OHCIPort *port = &s->rhport[port1->index]; - uint32_t old_state = port->ctrl; - - ohci_async_cancel_device(s, port1->dev); - - /* set connect status */ - if (port->ctrl & OHCI_PORT_CCS) { - port->ctrl &= ~OHCI_PORT_CCS; - port->ctrl |= OHCI_PORT_CSC; - } - /* disable port */ - if (port->ctrl & OHCI_PORT_PES) { - port->ctrl &= ~OHCI_PORT_PES; - port->ctrl |= OHCI_PORT_PESC; - } - trace_usb_ohci_port_detach(port1->index); - - if (old_state != port->ctrl) { - ohci_set_interrupt(s, OHCI_INTR_RHSC); - } -} - -static void ohci_wakeup(USBPort *port1) -{ - OHCIState *s = port1->opaque; - OHCIPort *port = &s->rhport[port1->index]; - uint32_t intr = 0; - if (port->ctrl & OHCI_PORT_PSS) { - trace_usb_ohci_port_wakeup(port1->index); - port->ctrl |= OHCI_PORT_PSSC; - port->ctrl &= ~OHCI_PORT_PSS; - intr = OHCI_INTR_RHSC; - } - /* Note that the controller can be suspended even if this port is not */ - if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) { - trace_usb_ohci_remote_wakeup(s->name); - /* This is the one state transition the controller can do by itself */ - s->ctl &= ~OHCI_CTL_HCFS; - s->ctl |= OHCI_USB_RESUME; - /* In suspend mode only ResumeDetected is possible, not RHSC: - * see the OHCI spec 5.1.2.3. - */ - intr = OHCI_INTR_RD; - } - ohci_set_interrupt(s, intr); -} - -static void ohci_child_detach(USBPort *port1, USBDevice *child) -{ - OHCIState *s = port1->opaque; - - ohci_async_cancel_device(s, child); -} - -static USBDevice *ohci_find_device(OHCIState *ohci, uint8_t addr) -{ - USBDevice *dev; - int i; - - for (i = 0; i < ohci->num_ports; i++) { - if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) { - continue; - } - dev = usb_find_device(&ohci->rhport[i].port, addr); - if (dev != NULL) { - return dev; - } - } - return NULL; -} - -static void ohci_stop_endpoints(OHCIState *ohci) -{ - USBDevice *dev; - int i, j; - - for (i = 0; i < ohci->num_ports; i++) { - dev = ohci->rhport[i].port.dev; - if (dev && dev->attached) { - usb_device_ep_stopped(dev, &dev->ep_ctl); - for (j = 0; j < USB_MAX_ENDPOINTS; j++) { - usb_device_ep_stopped(dev, &dev->ep_in[j]); - usb_device_ep_stopped(dev, &dev->ep_out[j]); - } - } - } -} - -static void ohci_roothub_reset(OHCIState *ohci) -{ - OHCIPort *port; - int i; - - ohci_bus_stop(ohci); - 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; - ohci->intr = OHCI_INTR_MIE; - - ohci->hcca = 0; - ohci->ctrl_head = ohci->ctrl_cur = 0; - ohci->bulk_head = ohci->bulk_cur = 0; - ohci->per_cur = 0; - ohci->done = 0; - ohci->done_count = 7; - - /* FSMPS is marked TBD in OCHI 1.0, what gives ffs? - * I took the value linux sets ... - */ - ohci->fsmps = 0x2778; - ohci->fi = 0x2edf; - ohci->fit = 0; - ohci->frt = 0; - ohci->frame_number = 0; - ohci->pstart = 0; - ohci->lst = OHCI_LS_THRESH; -} - -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 */ -static inline int get_dwords(OHCIState *ohci, - dma_addr_t addr, uint32_t *buf, int num) -{ - int i; - - addr += ohci->localmem_base; - - for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - if (dma_memory_read(ohci->as, addr, buf, sizeof(*buf))) { - return -1; - } - *buf = le32_to_cpu(*buf); - } - - return 0; -} - -/* Put an array of dwords in to main memory */ -static inline int put_dwords(OHCIState *ohci, - dma_addr_t addr, uint32_t *buf, int num) -{ - int i; - - addr += ohci->localmem_base; - - for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - uint32_t tmp = cpu_to_le32(*buf); - if (dma_memory_write(ohci->as, addr, &tmp, sizeof(tmp))) { - return -1; - } - } - - return 0; -} - -/* Get an array of words from main memory */ -static inline int get_words(OHCIState *ohci, - dma_addr_t addr, uint16_t *buf, int num) -{ - int i; - - addr += ohci->localmem_base; - - for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - if (dma_memory_read(ohci->as, addr, buf, sizeof(*buf))) { - return -1; - } - *buf = le16_to_cpu(*buf); - } - - return 0; -} - -/* Put an array of words in to main memory */ -static inline int put_words(OHCIState *ohci, - dma_addr_t addr, uint16_t *buf, int num) -{ - int i; - - addr += ohci->localmem_base; - - for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - uint16_t tmp = cpu_to_le16(*buf); - if (dma_memory_write(ohci->as, addr, &tmp, sizeof(tmp))) { - return -1; - } - } - - return 0; -} - -static inline int ohci_read_ed(OHCIState *ohci, - dma_addr_t addr, struct ohci_ed *ed) -{ - return get_dwords(ohci, addr, (uint32_t *)ed, sizeof(*ed) >> 2); -} - -static inline int ohci_read_td(OHCIState *ohci, - dma_addr_t addr, struct ohci_td *td) -{ - return get_dwords(ohci, addr, (uint32_t *)td, sizeof(*td) >> 2); -} - -static inline int ohci_read_iso_td(OHCIState *ohci, - dma_addr_t addr, struct ohci_iso_td *td) -{ - return get_dwords(ohci, addr, (uint32_t *)td, 4) || - get_words(ohci, addr + 16, td->offset, 8); -} - -static inline int ohci_read_hcca(OHCIState *ohci, - dma_addr_t addr, struct ohci_hcca *hcca) -{ - return dma_memory_read(ohci->as, addr + ohci->localmem_base, - hcca, sizeof(*hcca)); -} - -static inline int ohci_put_ed(OHCIState *ohci, - dma_addr_t addr, struct ohci_ed *ed) -{ - /* ed->tail is under control of the HCD. - * Since just ed->head is changed by HC, just write back this - */ - - return put_dwords(ohci, addr + ED_WBACK_OFFSET, - (uint32_t *)((char *)ed + ED_WBACK_OFFSET), - ED_WBACK_SIZE >> 2); -} - -static inline int ohci_put_td(OHCIState *ohci, - dma_addr_t addr, struct ohci_td *td) -{ - return put_dwords(ohci, addr, (uint32_t *)td, sizeof(*td) >> 2); -} - -static inline int ohci_put_iso_td(OHCIState *ohci, - dma_addr_t addr, struct ohci_iso_td *td) -{ - return put_dwords(ohci, addr, (uint32_t *)td, 4) || - put_words(ohci, addr + 16, td->offset, 8); -} - -static inline int ohci_put_hcca(OHCIState *ohci, - dma_addr_t addr, struct ohci_hcca *hcca) -{ - return dma_memory_write(ohci->as, - addr + ohci->localmem_base + HCCA_WRITEBACK_OFFSET, - (char *)hcca + HCCA_WRITEBACK_OFFSET, - HCCA_WRITEBACK_SIZE); -} - -/* Read/Write the contents of a TD from/to main memory. */ -static int ohci_copy_td(OHCIState *ohci, struct ohci_td *td, - uint8_t *buf, int len, DMADirection dir) -{ - dma_addr_t ptr, n; - - ptr = td->cbp; - n = 0x1000 - (ptr & 0xfff); - if (n > len) - n = len; - - if (dma_memory_rw(ohci->as, ptr + ohci->localmem_base, buf, n, dir)) { - return -1; - } - if (n == len) { - return 0; - } - ptr = td->be & ~0xfffu; - buf += n; - if (dma_memory_rw(ohci->as, ptr + ohci->localmem_base, buf, - len - n, dir)) { - return -1; - } - return 0; -} - -/* Read/Write the contents of an ISO TD from/to main memory. */ -static int ohci_copy_iso_td(OHCIState *ohci, - uint32_t start_addr, uint32_t end_addr, - uint8_t *buf, int len, DMADirection dir) -{ - dma_addr_t ptr, n; - - ptr = start_addr; - n = 0x1000 - (ptr & 0xfff); - if (n > len) - n = len; - - if (dma_memory_rw(ohci->as, ptr + ohci->localmem_base, buf, n, dir)) { - return -1; - } - if (n == len) { - return 0; - } - ptr = end_addr & ~0xfffu; - buf += n; - if (dma_memory_rw(ohci->as, ptr + ohci->localmem_base, buf, - len - n, dir)) { - return -1; - } - return 0; -} - -static void ohci_process_lists(OHCIState *ohci, int completion); - -static void ohci_async_complete_packet(USBPort *port, USBPacket *packet) -{ - OHCIState *ohci = container_of(packet, OHCIState, usb_packet); - - trace_usb_ohci_async_complete(); - ohci->async_complete = true; - ohci_process_lists(ohci, 1); -} - -#define USUB(a, b) ((int16_t)((uint16_t)(a) - (uint16_t)(b))) - -static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, - int completion) -{ - int dir; - size_t len = 0; - const char *str = NULL; - int pid; - int ret; - int i; - USBDevice *dev; - USBEndpoint *ep; - struct ohci_iso_td iso_td; - uint32_t addr; - uint16_t starting_frame; - int16_t relative_frame_number; - int frame_count; - uint32_t start_offset, next_offset, end_offset = 0; - uint32_t start_addr, end_addr; - - addr = ed->head & OHCI_DPTR_MASK; - - if (ohci_read_iso_td(ohci, addr, &iso_td)) { - trace_usb_ohci_iso_td_read_failed(addr); - ohci_die(ohci); - return 0; - } - - starting_frame = OHCI_BM(iso_td.flags, TD_SF); - frame_count = OHCI_BM(iso_td.flags, TD_FC); - relative_frame_number = USUB(ohci->frame_number, starting_frame); - - trace_usb_ohci_iso_td_head( - ed->head & OHCI_DPTR_MASK, ed->tail & OHCI_DPTR_MASK, - iso_td.flags, iso_td.bp, iso_td.next, iso_td.be, - ohci->frame_number, starting_frame, - frame_count, relative_frame_number); - trace_usb_ohci_iso_td_head_offset( - iso_td.offset[0], iso_td.offset[1], - iso_td.offset[2], iso_td.offset[3], - iso_td.offset[4], iso_td.offset[5], - iso_td.offset[6], iso_td.offset[7]); - - if (relative_frame_number < 0) { - trace_usb_ohci_iso_td_relative_frame_number_neg(relative_frame_number); - return 1; - } else if (relative_frame_number > frame_count) { - /* ISO TD expired - retire the TD to the Done Queue and continue with - the next ISO TD of the same ED */ - trace_usb_ohci_iso_td_relative_frame_number_big(relative_frame_number, - frame_count); - OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_DATAOVERRUN); - ed->head &= ~OHCI_DPTR_MASK; - ed->head |= (iso_td.next & OHCI_DPTR_MASK); - iso_td.next = ohci->done; - ohci->done = addr; - i = OHCI_BM(iso_td.flags, TD_DI); - if (i < ohci->done_count) - ohci->done_count = i; - if (ohci_put_iso_td(ohci, addr, &iso_td)) { - ohci_die(ohci); - return 1; - } - return 0; - } - - dir = OHCI_BM(ed->flags, ED_D); - switch (dir) { - case OHCI_TD_DIR_IN: - str = "in"; - pid = USB_TOKEN_IN; - break; - case OHCI_TD_DIR_OUT: - str = "out"; - pid = USB_TOKEN_OUT; - break; - case OHCI_TD_DIR_SETUP: - str = "setup"; - pid = USB_TOKEN_SETUP; - break; - default: - trace_usb_ohci_iso_td_bad_direction(dir); - return 1; - } - - if (!iso_td.bp || !iso_td.be) { - trace_usb_ohci_iso_td_bad_bp_be(iso_td.bp, iso_td.be); - return 1; - } - - start_offset = iso_td.offset[relative_frame_number]; - next_offset = iso_td.offset[relative_frame_number + 1]; - - if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) || - ((relative_frame_number < frame_count) && - !(OHCI_BM(next_offset, TD_PSW_CC) & 0xe))) { - trace_usb_ohci_iso_td_bad_cc_not_accessed(start_offset, next_offset); - return 1; - } - - if ((relative_frame_number < frame_count) && (start_offset > next_offset)) { - trace_usb_ohci_iso_td_bad_cc_overrun(start_offset, next_offset); - return 1; - } - - if ((start_offset & 0x1000) == 0) { - start_addr = (iso_td.bp & OHCI_PAGE_MASK) | - (start_offset & OHCI_OFFSET_MASK); - } else { - start_addr = (iso_td.be & OHCI_PAGE_MASK) | - (start_offset & OHCI_OFFSET_MASK); - } - - if (relative_frame_number < frame_count) { - end_offset = next_offset - 1; - if ((end_offset & 0x1000) == 0) { - end_addr = (iso_td.bp & OHCI_PAGE_MASK) | - (end_offset & OHCI_OFFSET_MASK); - } else { - end_addr = (iso_td.be & OHCI_PAGE_MASK) | - (end_offset & OHCI_OFFSET_MASK); - } - } else { - /* Last packet in the ISO TD */ - end_addr = iso_td.be; - } - - if ((start_addr & OHCI_PAGE_MASK) != (end_addr & OHCI_PAGE_MASK)) { - len = (end_addr & OHCI_OFFSET_MASK) + 0x1001 - - (start_addr & OHCI_OFFSET_MASK); - } else { - len = end_addr - start_addr + 1; - } - - if (len && dir != OHCI_TD_DIR_IN) { - if (ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, len, - DMA_DIRECTION_TO_DEVICE)) { - ohci_die(ohci); - return 1; - } - } - - if (!completion) { - bool int_req = relative_frame_number == frame_count && - OHCI_BM(iso_td.flags, TD_DI) == 0; - dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); - ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); - usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, false, int_req); - usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); - usb_handle_packet(dev, &ohci->usb_packet); - if (ohci->usb_packet.status == USB_RET_ASYNC) { - usb_device_flush_ep_queue(dev, ep); - return 1; - } - } - if (ohci->usb_packet.status == USB_RET_SUCCESS) { - ret = ohci->usb_packet.actual_length; - } else { - ret = ohci->usb_packet.status; - } - - trace_usb_ohci_iso_td_so(start_offset, end_offset, start_addr, end_addr, - str, len, ret); - - /* Writeback */ - if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) { - /* IN transfer succeeded */ - if (ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, ret, - DMA_DIRECTION_FROM_DEVICE)) { - ohci_die(ohci); - return 1; - } - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, - OHCI_CC_NOERROR); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, ret); - } else if (dir == OHCI_TD_DIR_OUT && ret == len) { - /* OUT transfer succeeded */ - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, - OHCI_CC_NOERROR); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, 0); - } else { - if (ret > (ssize_t) len) { - trace_usb_ohci_iso_td_data_overrun(ret, len); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, - OHCI_CC_DATAOVERRUN); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, - len); - } else if (ret >= 0) { - trace_usb_ohci_iso_td_data_underrun(ret); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, - OHCI_CC_DATAUNDERRUN); - } else { - switch (ret) { - case USB_RET_IOERROR: - case USB_RET_NODEV: - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, - OHCI_CC_DEVICENOTRESPONDING); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, - 0); - break; - case USB_RET_NAK: - case USB_RET_STALL: - trace_usb_ohci_iso_td_nak(ret); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, - OHCI_CC_STALL); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, - 0); - break; - default: - trace_usb_ohci_iso_td_bad_response(ret); - OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC, - OHCI_CC_UNDEXPETEDPID); - break; - } - } - } - - if (relative_frame_number == frame_count) { - /* Last data packet of ISO TD - retire the TD to the Done Queue */ - OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_NOERROR); - ed->head &= ~OHCI_DPTR_MASK; - ed->head |= (iso_td.next & OHCI_DPTR_MASK); - iso_td.next = ohci->done; - ohci->done = addr; - i = OHCI_BM(iso_td.flags, TD_DI); - if (i < ohci->done_count) - ohci->done_count = i; - } - if (ohci_put_iso_td(ohci, addr, &iso_td)) { - ohci_die(ohci); - } - return 1; -} - -#ifdef trace_event_get_state -static void ohci_td_pkt(const char *msg, const uint8_t *buf, size_t len) -{ - bool print16 = !!trace_event_get_state(TRACE_USB_OHCI_TD_PKT_SHORT); - bool printall = !!trace_event_get_state(TRACE_USB_OHCI_TD_PKT_FULL); - const int width = 16; - int i; - char tmp[3 * width + 1]; - char *p = tmp; - - if (!printall && !print16) { - return; - } - - for (i = 0; ; i++) { - if (i && (!(i % width) || (i == len))) { - if (!printall) { - trace_usb_ohci_td_pkt_short(msg, tmp); - break; - } - trace_usb_ohci_td_pkt_full(msg, tmp); - p = tmp; - *p = 0; - } - if (i == len) { - break; - } - - p += sprintf(p, " %.2x", buf[i]); - } -} -#else -static void ohci_td_pkt(const char *msg, const uint8_t *buf, size_t len) -{ -} -#endif - -/* Service a transport descriptor. - Returns nonzero to terminate processing of this endpoint. */ - -static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) -{ - int dir; - size_t len = 0, pktlen = 0; - const char *str = NULL; - int pid; - int ret; - int i; - USBDevice *dev; - USBEndpoint *ep; - struct ohci_td td; - uint32_t addr; - int flag_r; - int completion; - - addr = ed->head & OHCI_DPTR_MASK; - /* See if this TD has already been submitted to the device. */ - completion = (addr == ohci->async_td); - if (completion && !ohci->async_complete) { - trace_usb_ohci_td_skip_async(); - return 1; - } - if (ohci_read_td(ohci, addr, &td)) { - trace_usb_ohci_td_read_error(addr); - ohci_die(ohci); - return 0; - } - - dir = OHCI_BM(ed->flags, ED_D); - switch (dir) { - case OHCI_TD_DIR_OUT: - case OHCI_TD_DIR_IN: - /* Same value. */ - break; - default: - dir = OHCI_BM(td.flags, TD_DP); - break; - } - - switch (dir) { - case OHCI_TD_DIR_IN: - str = "in"; - pid = USB_TOKEN_IN; - break; - case OHCI_TD_DIR_OUT: - str = "out"; - pid = USB_TOKEN_OUT; - break; - case OHCI_TD_DIR_SETUP: - str = "setup"; - pid = USB_TOKEN_SETUP; - break; - default: - trace_usb_ohci_td_bad_direction(dir); - return 1; - } - if (td.cbp && td.be) { - if ((td.cbp & 0xfffff000) != (td.be & 0xfffff000)) { - len = (td.be & 0xfff) + 0x1001 - (td.cbp & 0xfff); - } else { - len = (td.be - td.cbp) + 1; - } - - pktlen = len; - if (len && dir != OHCI_TD_DIR_IN) { - /* The endpoint may not allow us to transfer it all now */ - pktlen = (ed->flags & OHCI_ED_MPS_MASK) >> OHCI_ED_MPS_SHIFT; - if (pktlen > len) { - pktlen = len; - } - if (!completion) { - if (ohci_copy_td(ohci, &td, ohci->usb_buf, pktlen, - DMA_DIRECTION_TO_DEVICE)) { - ohci_die(ohci); - } - } - } - } - - flag_r = (td.flags & OHCI_TD_R) != 0; - trace_usb_ohci_td_pkt_hdr(addr, (int64_t)pktlen, (int64_t)len, str, - flag_r, td.cbp, td.be); - ohci_td_pkt("OUT", ohci->usb_buf, pktlen); - - if (completion) { - ohci->async_td = 0; - ohci->async_complete = false; - } else { - if (ohci->async_td) { - /* ??? The hardware should allow one active packet per - endpoint. We only allow one active packet per controller. - This should be sufficient as long as devices respond in a - timely manner. - */ - trace_usb_ohci_td_too_many_pending(); - return 1; - } - dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); - ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); - usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, !flag_r, - OHCI_BM(td.flags, TD_DI) == 0); - usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen); - usb_handle_packet(dev, &ohci->usb_packet); - trace_usb_ohci_td_packet_status(ohci->usb_packet.status); - - if (ohci->usb_packet.status == USB_RET_ASYNC) { - usb_device_flush_ep_queue(dev, ep); - ohci->async_td = addr; - return 1; - } - } - if (ohci->usb_packet.status == USB_RET_SUCCESS) { - ret = ohci->usb_packet.actual_length; - } else { - ret = ohci->usb_packet.status; - } - - if (ret >= 0) { - if (dir == OHCI_TD_DIR_IN) { - if (ohci_copy_td(ohci, &td, ohci->usb_buf, ret, - DMA_DIRECTION_FROM_DEVICE)) { - ohci_die(ohci); - } - ohci_td_pkt("IN", ohci->usb_buf, pktlen); - } else { - ret = pktlen; - } - } - - /* Writeback */ - if (ret == pktlen || (dir == OHCI_TD_DIR_IN && ret >= 0 && flag_r)) { - /* Transmission succeeded. */ - if (ret == len) { - td.cbp = 0; - } else { - if ((td.cbp & 0xfff) + ret > 0xfff) { - td.cbp = (td.be & ~0xfff) + ((td.cbp + ret) & 0xfff); - } else { - td.cbp += ret; - } - } - td.flags |= OHCI_TD_T1; - td.flags ^= OHCI_TD_T0; - OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_NOERROR); - OHCI_SET_BM(td.flags, TD_EC, 0); - - if ((dir != OHCI_TD_DIR_IN) && (ret != len)) { - /* Partial packet transfer: TD not ready to retire yet */ - goto exit_no_retire; - } - - /* Setting ED_C is part of the TD retirement process */ - ed->head &= ~OHCI_ED_C; - if (td.flags & OHCI_TD_T0) - ed->head |= OHCI_ED_C; - } else { - if (ret >= 0) { - trace_usb_ohci_td_underrun(); - OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAUNDERRUN); - } else { - switch (ret) { - case USB_RET_IOERROR: - case USB_RET_NODEV: - trace_usb_ohci_td_dev_error(); - OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING); - break; - case USB_RET_NAK: - trace_usb_ohci_td_nak(); - return 1; - case USB_RET_STALL: - trace_usb_ohci_td_stall(); - OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_STALL); - break; - case USB_RET_BABBLE: - trace_usb_ohci_td_babble(); - OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAOVERRUN); - break; - default: - trace_usb_ohci_td_bad_device_response(ret); - OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_UNDEXPETEDPID); - OHCI_SET_BM(td.flags, TD_EC, 3); - break; - } - } - ed->head |= OHCI_ED_H; - } - - /* Retire this TD */ - ed->head &= ~OHCI_DPTR_MASK; - ed->head |= td.next & OHCI_DPTR_MASK; - td.next = ohci->done; - ohci->done = addr; - i = OHCI_BM(td.flags, TD_DI); - if (i < ohci->done_count) - ohci->done_count = i; -exit_no_retire: - if (ohci_put_td(ohci, addr, &td)) { - ohci_die(ohci); - return 1; - } - return OHCI_BM(td.flags, TD_CC) != OHCI_CC_NOERROR; -} - -/* Service an endpoint list. Returns nonzero if active TD were found. */ -static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) -{ - struct ohci_ed ed; - uint32_t next_ed; - uint32_t cur; - int active; - - active = 0; - - if (head == 0) - return 0; - - for (cur = head; cur; cur = next_ed) { - if (ohci_read_ed(ohci, cur, &ed)) { - trace_usb_ohci_ed_read_error(cur); - ohci_die(ohci); - return 0; - } - - next_ed = ed.next & OHCI_DPTR_MASK; - - if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) { - uint32_t addr; - /* Cancel pending packets for ED that have been paused. */ - addr = ed.head & OHCI_DPTR_MASK; - if (ohci->async_td && addr == ohci->async_td) { - usb_cancel_packet(&ohci->usb_packet); - ohci->async_td = 0; - usb_device_ep_stopped(ohci->usb_packet.ep->dev, - ohci->usb_packet.ep); - } - continue; - } - - while ((ed.head & OHCI_DPTR_MASK) != ed.tail) { - trace_usb_ohci_ed_pkt(cur, (ed.head & OHCI_ED_H) != 0, - (ed.head & OHCI_ED_C) != 0, ed.head & OHCI_DPTR_MASK, - ed.tail & OHCI_DPTR_MASK, ed.next & OHCI_DPTR_MASK); - trace_usb_ohci_ed_pkt_flags( - OHCI_BM(ed.flags, ED_FA), OHCI_BM(ed.flags, ED_EN), - OHCI_BM(ed.flags, ED_D), (ed.flags & OHCI_ED_S)!= 0, - (ed.flags & OHCI_ED_K) != 0, (ed.flags & OHCI_ED_F) != 0, - OHCI_BM(ed.flags, ED_MPS)); - - active = 1; - - if ((ed.flags & OHCI_ED_F) == 0) { - if (ohci_service_td(ohci, &ed)) - break; - } else { - /* Handle isochronous endpoints */ - if (ohci_service_iso_td(ohci, &ed, completion)) - break; - } - } - - if (ohci_put_ed(ohci, cur, &ed)) { - ohci_die(ohci); - return 0; - } - } - - return active; -} - -/* 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); -} - -/* Process Control and Bulk lists. */ -static void ohci_process_lists(OHCIState *ohci, int completion) -{ - if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) { - if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) { - trace_usb_ohci_process_lists(ohci->ctrl_head, ohci->ctrl_cur); - } - if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) { - ohci->ctrl_cur = 0; - ohci->status &= ~OHCI_STATUS_CLF; - } - } - - if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) { - if (!ohci_service_ed_list(ohci, ohci->bulk_head, completion)) { - ohci->bulk_cur = 0; - ohci->status &= ~OHCI_STATUS_BLF; - } - } -} - -/* Do frame processing on frame boundary */ -static void ohci_frame_boundary(void *opaque) -{ - OHCIState *ohci = opaque; - struct ohci_hcca hcca; - - if (ohci_read_hcca(ohci, ohci->hcca, &hcca)) { - trace_usb_ohci_hcca_read_error(ohci->hcca); - ohci_die(ohci); - return; - } - - /* Process all the lists at the end of the frame */ - if (ohci->ctl & OHCI_CTL_PLE) { - int n; - - n = ohci->frame_number & 0x1f; - ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]), 0); - } - - /* Cancel all pending packets if either of the lists has been disabled. */ - if (ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) { - if (ohci->async_td) { - usb_cancel_packet(&ohci->usb_packet); - ohci->async_td = 0; - } - ohci_stop_endpoints(ohci); - } - ohci->old_ctl = ohci->ctl; - ohci_process_lists(ohci, 0); - - /* Stop if UnrecoverableError happened or ohci_sof will crash */ - if (ohci->intr_status & OHCI_INTR_UE) { - return; - } - - /* Frame boundary, so do EOF stuf here */ - ohci->frt = ohci->fit; - - /* Increment frame number and take care of endianness. */ - ohci->frame_number = (ohci->frame_number + 1) & 0xffff; - hcca.frame = cpu_to_le16(ohci->frame_number); - - if (ohci->done_count == 0 && !(ohci->intr_status & OHCI_INTR_WD)) { - if (!ohci->done) - abort(); - if (ohci->intr & ohci->intr_status) - ohci->done |= 1; - hcca.done = cpu_to_le32(ohci->done); - ohci->done = 0; - ohci->done_count = 7; - ohci_set_interrupt(ohci, OHCI_INTR_WD); - } - - if (ohci->done_count != 7 && ohci->done_count != 0) - ohci->done_count--; - - /* Do SOF stuff here */ - ohci_sof(ohci); - - /* Writeback HCCA */ - if (ohci_put_hcca(ohci, ohci->hcca, &hcca)) { - ohci_die(ohci); - } -} - -/* Start sending SOF tokens across the USB bus, lists are processed in - * next frame - */ -static int ohci_bus_start(OHCIState *ohci) -{ - trace_usb_ohci_start(ohci->name); - - /* 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; -} - -/* Stop sending SOF tokens on the bus */ -static void ohci_bus_stop(OHCIState *ohci) -{ - trace_usb_ohci_stop(ohci->name); - timer_del(ohci->eof_timer); -} - -/* Sets a flag in a port status register but only set it if the port is - * connected, if not set ConnectStatusChange flag. If flag is enabled - * return 1. - */ -static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val) -{ - int ret = 1; - - /* writing a 0 has no effect */ - if (val == 0) - return 0; - - /* If CurrentConnectStatus is cleared we set - * ConnectStatusChange - */ - if (!(ohci->rhport[i].ctrl & OHCI_PORT_CCS)) { - ohci->rhport[i].ctrl |= OHCI_PORT_CSC; - if (ohci->rhstatus & OHCI_RHS_DRWE) { - /* TODO: CSC is a wakeup event */ - } - return 0; - } - - if (ohci->rhport[i].ctrl & val) - ret = 0; - - /* set the bit */ - ohci->rhport[i].ctrl |= val; - - return ret; -} - -/* Set the frame interval - frame interval toggle is manipulated by the hcd only */ -static void ohci_set_frame_interval(OHCIState *ohci, uint16_t val) -{ - val &= OHCI_FMI_FI; - - if (val != ohci->fi) { - trace_usb_ohci_set_frame_interval(ohci->name, ohci->fi, ohci->fi); - } - - ohci->fi = val; -} - -static void ohci_port_power(OHCIState *ohci, int i, int p) -{ - if (p) { - ohci->rhport[i].ctrl |= OHCI_PORT_PPS; - } else { - ohci->rhport[i].ctrl &= ~(OHCI_PORT_PPS| - OHCI_PORT_CCS| - OHCI_PORT_PSS| - OHCI_PORT_PRS); - } -} - -/* Set HcControlRegister */ -static void ohci_set_ctl(OHCIState *ohci, uint32_t val) -{ - uint32_t old_state; - uint32_t new_state; - - old_state = ohci->ctl & OHCI_CTL_HCFS; - ohci->ctl = val; - new_state = ohci->ctl & OHCI_CTL_HCFS; - - /* no state change */ - if (old_state == new_state) - return; - - trace_usb_ohci_set_ctl(ohci->name, new_state); - switch (new_state) { - case OHCI_USB_OPERATIONAL: - ohci_bus_start(ohci); - 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_roothub_reset(ohci); - break; - } -} - -static uint32_t ohci_get_frame_remaining(OHCIState *ohci) -{ - uint16_t fr; - int64_t tks; - - if ((ohci->ctl & OHCI_CTL_HCFS) != OHCI_USB_OPERATIONAL) - return (ohci->frt << 31); - - /* Being in USB operational state guarnatees sof_time was - * set already. - */ - tks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - ohci->sof_time; - - /* avoid muldiv if possible */ - if (tks >= usb_frame_time) - return (ohci->frt << 31); - - tks = muldiv64(1, tks, usb_bit_time); - fr = (uint16_t)(ohci->fi - tks); - - return (ohci->frt << 31) | fr; -} - - -/* Set root hub status */ -static void ohci_set_hub_status(OHCIState *ohci, uint32_t val) -{ - uint32_t old_state; - - old_state = ohci->rhstatus; - - /* write 1 to clear OCIC */ - if (val & OHCI_RHS_OCIC) - ohci->rhstatus &= ~OHCI_RHS_OCIC; - - if (val & OHCI_RHS_LPS) { - int i; - - for (i = 0; i < ohci->num_ports; i++) - ohci_port_power(ohci, i, 0); - trace_usb_ohci_hub_power_down(); - } - - if (val & OHCI_RHS_LPSC) { - int i; - - for (i = 0; i < ohci->num_ports; i++) - ohci_port_power(ohci, i, 1); - trace_usb_ohci_hub_power_up(); - } - - if (val & OHCI_RHS_DRWE) - ohci->rhstatus |= OHCI_RHS_DRWE; - - if (val & OHCI_RHS_CRWE) - ohci->rhstatus &= ~OHCI_RHS_DRWE; - - if (old_state != ohci->rhstatus) - ohci_set_interrupt(ohci, OHCI_INTR_RHSC); -} - -/* Set root hub port status */ -static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val) -{ - uint32_t old_state; - OHCIPort *port; - - port = &ohci->rhport[portnum]; - old_state = port->ctrl; - - /* Write to clear CSC, PESC, PSSC, OCIC, PRSC */ - if (val & OHCI_PORT_WTC) - port->ctrl &= ~(val & OHCI_PORT_WTC); - - if (val & OHCI_PORT_CCS) - port->ctrl &= ~OHCI_PORT_PES; - - ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PES); - - if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PSS)) { - trace_usb_ohci_port_suspend(portnum); - } - - if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) { - trace_usb_ohci_port_reset(portnum); - usb_device_reset(port->port.dev); - port->ctrl &= ~OHCI_PORT_PRS; - /* ??? Should this also set OHCI_PORT_PESC. */ - port->ctrl |= OHCI_PORT_PES | OHCI_PORT_PRSC; - } - - /* Invert order here to ensure in ambiguous case, device is - * powered up... - */ - if (val & OHCI_PORT_LSDA) - ohci_port_power(ohci, portnum, 0); - if (val & OHCI_PORT_PPS) - ohci_port_power(ohci, portnum, 1); - - if (old_state != port->ctrl) - ohci_set_interrupt(ohci, OHCI_INTR_RHSC); -} - -static uint64_t ohci_mem_read(void *opaque, - hwaddr addr, - unsigned size) -{ - OHCIState *ohci = opaque; - uint32_t retval; - - /* Only aligned reads are allowed on OHCI */ - if (addr & 3) { - trace_usb_ohci_mem_read_unaligned(addr); - return 0xffffffff; - } else if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) { - /* HcRhPortStatus */ - retval = ohci->rhport[(addr - 0x54) >> 2].ctrl | OHCI_PORT_PPS; - } else { - switch (addr >> 2) { - case 0: /* HcRevision */ - retval = 0x10; - break; - - case 1: /* HcControl */ - retval = ohci->ctl; - break; - - case 2: /* HcCommandStatus */ - retval = ohci->status; - break; - - case 3: /* HcInterruptStatus */ - retval = ohci->intr_status; - break; - - case 4: /* HcInterruptEnable */ - case 5: /* HcInterruptDisable */ - retval = ohci->intr; - break; - - case 6: /* HcHCCA */ - retval = ohci->hcca; - break; - - case 7: /* HcPeriodCurrentED */ - retval = ohci->per_cur; - break; - - case 8: /* HcControlHeadED */ - retval = ohci->ctrl_head; - break; - - case 9: /* HcControlCurrentED */ - retval = ohci->ctrl_cur; - break; - - case 10: /* HcBulkHeadED */ - retval = ohci->bulk_head; - break; - - case 11: /* HcBulkCurrentED */ - retval = ohci->bulk_cur; - break; - - case 12: /* HcDoneHead */ - retval = ohci->done; - break; - - case 13: /* HcFmInterretval */ - retval = (ohci->fit << 31) | (ohci->fsmps << 16) | (ohci->fi); - break; - - case 14: /* HcFmRemaining */ - retval = ohci_get_frame_remaining(ohci); - break; - - case 15: /* HcFmNumber */ - retval = ohci->frame_number; - break; - - case 16: /* HcPeriodicStart */ - retval = ohci->pstart; - break; - - case 17: /* HcLSThreshold */ - retval = ohci->lst; - break; - - case 18: /* HcRhDescriptorA */ - retval = ohci->rhdesc_a; - break; - - case 19: /* HcRhDescriptorB */ - retval = ohci->rhdesc_b; - break; - - case 20: /* HcRhStatus */ - retval = ohci->rhstatus; - break; - - /* PXA27x specific registers */ - case 24: /* HcStatus */ - retval = ohci->hstatus & ohci->hmask; - break; - - case 25: /* HcHReset */ - retval = ohci->hreset; - break; - - case 26: /* HcHInterruptEnable */ - retval = ohci->hmask; - break; - - case 27: /* HcHInterruptTest */ - retval = ohci->htest; - break; - - default: - trace_usb_ohci_mem_read_bad_offset(addr); - retval = 0xffffffff; - } - } - - return retval; -} - -static void ohci_mem_write(void *opaque, - hwaddr addr, - uint64_t val, - unsigned size) -{ - OHCIState *ohci = opaque; - - /* Only aligned reads are allowed on OHCI */ - if (addr & 3) { - trace_usb_ohci_mem_write_unaligned(addr); - return; - } - - if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) { - /* HcRhPortStatus */ - ohci_port_set_status(ohci, (addr - 0x54) >> 2, val); - return; - } - - switch (addr >> 2) { - case 1: /* HcControl */ - ohci_set_ctl(ohci, val); - break; - - case 2: /* HcCommandStatus */ - /* SOC is read-only */ - val = (val & ~OHCI_STATUS_SOC); - - /* Bits written as '0' remain unchanged in the register */ - ohci->status |= val; - - if (ohci->status & OHCI_STATUS_HCR) - ohci_soft_reset(ohci); - break; - - case 3: /* HcInterruptStatus */ - ohci->intr_status &= ~val; - ohci_intr_update(ohci); - break; - - case 4: /* HcInterruptEnable */ - ohci->intr |= val; - ohci_intr_update(ohci); - break; - - case 5: /* HcInterruptDisable */ - ohci->intr &= ~val; - ohci_intr_update(ohci); - break; - - case 6: /* HcHCCA */ - ohci->hcca = val & OHCI_HCCA_MASK; - break; - - case 7: /* HcPeriodCurrentED */ - /* Ignore writes to this read-only register, Linux does them */ - break; - - case 8: /* HcControlHeadED */ - ohci->ctrl_head = val & OHCI_EDPTR_MASK; - break; - - case 9: /* HcControlCurrentED */ - ohci->ctrl_cur = val & OHCI_EDPTR_MASK; - break; - - case 10: /* HcBulkHeadED */ - ohci->bulk_head = val & OHCI_EDPTR_MASK; - break; - - case 11: /* HcBulkCurrentED */ - ohci->bulk_cur = val & OHCI_EDPTR_MASK; - break; - - case 13: /* HcFmInterval */ - ohci->fsmps = (val & OHCI_FMI_FSMPS) >> 16; - ohci->fit = (val & OHCI_FMI_FIT) >> 31; - ohci_set_frame_interval(ohci, val); - break; - - case 15: /* HcFmNumber */ - break; - - case 16: /* HcPeriodicStart */ - ohci->pstart = val & 0xffff; - break; - - case 17: /* HcLSThreshold */ - ohci->lst = val & 0xffff; - break; - - case 18: /* HcRhDescriptorA */ - ohci->rhdesc_a &= ~OHCI_RHA_RW_MASK; - ohci->rhdesc_a |= val & OHCI_RHA_RW_MASK; - break; - - case 19: /* HcRhDescriptorB */ - break; - - case 20: /* HcRhStatus */ - ohci_set_hub_status(ohci, val); - break; - - /* PXA27x specific registers */ - case 24: /* HcStatus */ - ohci->hstatus &= ~(val & ohci->hmask); - break; - - case 25: /* HcHReset */ - ohci->hreset = val & ~OHCI_HRESET_FSBIR; - if (val & OHCI_HRESET_FSBIR) - ohci_hard_reset(ohci); - break; - - case 26: /* HcHInterruptEnable */ - ohci->hmask = val; - break; - - case 27: /* HcHInterruptTest */ - ohci->htest = val; - break; - - default: - trace_usb_ohci_mem_write_bad_offset(addr); - break; - } -} - -static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev) -{ - if (ohci->async_td && - usb_packet_is_inflight(&ohci->usb_packet) && - ohci->usb_packet.ep->dev == dev) { - usb_cancel_packet(&ohci->usb_packet); - ohci->async_td = 0; - } -} - -static const MemoryRegionOps ohci_mem_ops = { - .read = ohci_mem_read, - .write = ohci_mem_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static USBPortOps ohci_port_ops = { - .attach = ohci_attach, - .detach = ohci_detach, - .child_detach = ohci_child_detach, - .wakeup = ohci_wakeup, - .complete = ohci_async_complete_packet, -}; - -static USBBusOps ohci_bus_ops = { -}; - -static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, - int num_ports, dma_addr_t localmem_base, - char *masterbus, uint32_t firstport, - AddressSpace *as, Error **errp) -{ - Error *err = NULL; - int i; - - ohci->as = as; - - if (usb_frame_time == 0) { -#ifdef OHCI_TIME_WARP - usb_frame_time = NANOSECONDS_PER_SECOND; - usb_bit_time = NANOSECONDS_PER_SECOND / (USB_HZ / 1000); -#else - 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; - } -#endif - trace_usb_ohci_init_time(usb_frame_time, usb_bit_time); - } - - ohci->num_ports = num_ports; - if (masterbus) { - USBPort *ports[OHCI_MAX_PORTS]; - for(i = 0; i < num_ports; i++) { - ports[i] = &ohci->rhport[i].port; - } - usb_register_companion(masterbus, ports, num_ports, - firstport, ohci, &ohci_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL, - &err); - if (err) { - error_propagate(errp, err); - return; - } - } else { - usb_bus_new(&ohci->bus, sizeof(ohci->bus), &ohci_bus_ops, dev); - for (i = 0; i < num_ports; i++) { - usb_register_port(&ohci->bus, &ohci->rhport[i].port, - ohci, i, &ohci_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - } - } - - memory_region_init_io(&ohci->mem, OBJECT(dev), &ohci_mem_ops, - ohci, "ohci", 256); - ohci->localmem_base = localmem_base; - - ohci->name = object_get_typename(OBJECT(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" -#define PCI_OHCI(obj) OBJECT_CHECK(OHCIPCIState, (obj), TYPE_PCI_OHCI) - -typedef struct { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - OHCIState state; - char *masterbus; - uint32_t num_ports; - uint32_t firstport; -} OHCIPCIState; - -/** A typical O/EHCI will stop operating, set itself into error state - * (which can be queried by MMIO) and will set PERR in its config - * space to signal that it got an error - */ -static void ohci_die(OHCIState *ohci) -{ - OHCIPCIState *dev = container_of(ohci, OHCIPCIState, state); - - trace_usb_ohci_die(); - - ohci_set_interrupt(ohci, OHCI_INTR_UE); - ohci_bus_stop(ohci); - pci_set_word(dev->parent_obj.config + PCI_STATUS, - PCI_STATUS_DETECTED_PARITY); -} - -static void usb_ohci_realize_pci(PCIDevice *dev, Error **errp) -{ - Error *err = NULL; - OHCIPCIState *ohci = PCI_OHCI(dev); - - dev->config[PCI_CLASS_PROG] = 0x10; /* OHCI */ - dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */ - - usb_ohci_init(&ohci->state, DEVICE(dev), ohci->num_ports, 0, - ohci->masterbus, ohci->firstport, - pci_get_address_space(dev), &err); - if (err) { - error_propagate(errp, err); - return; - } - - ohci->state.irq = pci_allocate_irq(dev); - pci_register_bar(dev, 0, 0, &ohci->state.mem); -} - -static void usb_ohci_exit(PCIDevice *dev) -{ - OHCIPCIState *ohci = PCI_OHCI(dev); - OHCIState *s = &ohci->state; - - trace_usb_ohci_exit(s->name); - ohci_bus_stop(s); - - if (s->async_td) { - usb_cancel_packet(&s->usb_packet); - s->async_td = 0; - } - ohci_stop_endpoints(s); - - 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) -{ - PCIDevice *dev = PCI_DEVICE(d); - OHCIPCIState *ohci = PCI_OHCI(dev); - OHCIState *s = &ohci->state; - - ohci_hard_reset(s); -} - -#define TYPE_SYSBUS_OHCI "sysbus-ohci" -#define SYSBUS_OHCI(obj) OBJECT_CHECK(OHCISysBusState, (obj), TYPE_SYSBUS_OHCI) - -typedef struct { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - OHCIState ohci; - uint32_t num_ports; - dma_addr_t dma_offset; -} OHCISysBusState; - -static void ohci_realize_pxa(DeviceState *dev, Error **errp) -{ - OHCISysBusState *s = SYSBUS_OHCI(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - - /* Cannot fail as we pass NULL for masterbus */ - usb_ohci_init(&s->ohci, dev, s->num_ports, s->dma_offset, NULL, 0, - &address_space_memory, &error_abort); - sysbus_init_irq(sbd, &s->ohci.irq); - sysbus_init_mmio(sbd, &s->ohci.mem); -} - -static void usb_ohci_reset_sysbus(DeviceState *dev) -{ - OHCISysBusState *s = SYSBUS_OHCI(dev); - OHCIState *ohci = &s->ohci; - - ohci_hard_reset(ohci); -} - -static Property ohci_pci_properties[] = { - DEFINE_PROP_STRING("masterbus", OHCIPCIState, masterbus), - DEFINE_PROP_UINT32("num-ports", OHCIPCIState, num_ports, 3), - DEFINE_PROP_UINT32("firstport", OHCIPCIState, firstport, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription vmstate_ohci_state_port = { - .name = "ohci-core/port", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(ctrl, OHCIPort), - VMSTATE_END_OF_LIST() - }, -}; - -static bool ohci_eof_timer_needed(void *opaque) -{ - OHCIState *ohci = opaque; - - 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, - .needed = ohci_eof_timer_needed, - .fields = (VMStateField[]) { - VMSTATE_TIMER_PTR(eof_timer, OHCIState), - VMSTATE_END_OF_LIST() - }, -}; - -static const VMStateDescription vmstate_ohci_state = { - .name = "ohci-core", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT64(sof_time, OHCIState), - VMSTATE_UINT32(ctl, OHCIState), - VMSTATE_UINT32(status, OHCIState), - VMSTATE_UINT32(intr_status, OHCIState), - VMSTATE_UINT32(intr, OHCIState), - VMSTATE_UINT32(hcca, OHCIState), - VMSTATE_UINT32(ctrl_head, OHCIState), - VMSTATE_UINT32(ctrl_cur, OHCIState), - VMSTATE_UINT32(bulk_head, OHCIState), - VMSTATE_UINT32(bulk_cur, OHCIState), - VMSTATE_UINT32(per_cur, OHCIState), - VMSTATE_UINT32(done, OHCIState), - VMSTATE_INT32(done_count, OHCIState), - VMSTATE_UINT16(fsmps, OHCIState), - VMSTATE_UINT8(fit, OHCIState), - VMSTATE_UINT16(fi, OHCIState), - VMSTATE_UINT8(frt, OHCIState), - VMSTATE_UINT16(frame_number, OHCIState), - VMSTATE_UINT16(padding, OHCIState), - VMSTATE_UINT32(pstart, OHCIState), - VMSTATE_UINT32(lst, OHCIState), - VMSTATE_UINT32(rhdesc_a, OHCIState), - VMSTATE_UINT32(rhdesc_b, OHCIState), - VMSTATE_UINT32(rhstatus, OHCIState), - VMSTATE_STRUCT_ARRAY(rhport, OHCIState, OHCI_MAX_PORTS, 0, - vmstate_ohci_state_port, OHCIPort), - VMSTATE_UINT32(hstatus, OHCIState), - VMSTATE_UINT32(hmask, OHCIState), - VMSTATE_UINT32(hreset, OHCIState), - VMSTATE_UINT32(htest, OHCIState), - VMSTATE_UINT32(old_ctl, OHCIState), - VMSTATE_UINT8_ARRAY(usb_buf, OHCIState, 8192), - VMSTATE_UINT32(async_td, OHCIState), - VMSTATE_BOOL(async_complete, OHCIState), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_ohci_eof_timer, - NULL - } -}; - -static const VMStateDescription vmstate_ohci = { - .name = "ohci", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(parent_obj, OHCIPCIState), - VMSTATE_STRUCT(state, OHCIPCIState, 1, vmstate_ohci_state, OHCIState), - VMSTATE_END_OF_LIST() - } -}; - -static void ohci_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = usb_ohci_realize_pci; - k->exit = usb_ohci_exit; - k->vendor_id = PCI_VENDOR_ID_APPLE; - k->device_id = PCI_DEVICE_ID_APPLE_IPID_USB; - k->class_id = PCI_CLASS_SERIAL_USB; - set_bit(DEVICE_CATEGORY_USB, dc->categories); - dc->desc = "Apple USB Controller"; - dc->props = ohci_pci_properties; - dc->hotpluggable = false; - dc->vmsd = &vmstate_ohci; - dc->reset = usb_ohci_reset_pci; -} - -static const TypeInfo ohci_pci_info = { - .name = TYPE_PCI_OHCI, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(OHCIPCIState), - .class_init = ohci_pci_class_init, -}; - -static Property ohci_sysbus_properties[] = { - DEFINE_PROP_UINT32("num-ports", OHCISysBusState, num_ports, 3), - DEFINE_PROP_DMAADDR("dma-offset", OHCISysBusState, dma_offset, 3), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ohci_sysbus_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = ohci_realize_pxa; - set_bit(DEVICE_CATEGORY_USB, dc->categories); - dc->desc = "OHCI USB Controller"; - dc->props = ohci_sysbus_properties; - dc->reset = usb_ohci_reset_sysbus; -} - -static const TypeInfo ohci_sysbus_info = { - .name = TYPE_SYSBUS_OHCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(OHCISysBusState), - .class_init = ohci_sysbus_class_init, -}; - -static void ohci_register_types(void) -{ - type_register_static(&ohci_pci_info); - type_register_static(&ohci_sysbus_info); -} - -type_init(ohci_register_types) diff --git a/qemu/hw/usb/hcd-uhci.c b/qemu/hw/usb/hcd-uhci.c deleted file mode 100644 index ca72a80f2..000000000 --- a/qemu/hw/usb/hcd-uhci.c +++ /dev/null @@ -1,1435 +0,0 @@ -/* - * USB UHCI controller emulation - * - * Copyright (c) 2005 Fabrice Bellard - * - * Copyright (c) 2008 Max Krasnyansky - * Magor rewrite of the UHCI data structures parser and frame processor - * Support for fully async operation and multiple outstanding transactions - * - * 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/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" -#include "trace.h" -#include "qemu/main-loop.h" - -#define FRAME_TIMER_FREQ 1000 - -#define FRAME_MAX_LOOPS 256 - -/* Must be large enough to handle 10 frame delay for initial isoc requests */ -#define QH_VALID 32 - -#define MAX_FRAMES_PER_TICK (QH_VALID / 2) - -#define NB_PORTS 2 - -enum { - TD_RESULT_STOP_FRAME = 10, - TD_RESULT_COMPLETE, - TD_RESULT_NEXT_QH, - TD_RESULT_ASYNC_START, - TD_RESULT_ASYNC_CONT, -}; - -typedef struct UHCIState UHCIState; -typedef struct UHCIAsync UHCIAsync; -typedef struct UHCIQueue UHCIQueue; -typedef struct UHCIInfo UHCIInfo; -typedef struct UHCIPCIDeviceClass UHCIPCIDeviceClass; - -struct UHCIInfo { - const char *name; - uint16_t vendor_id; - uint16_t device_id; - uint8_t revision; - uint8_t irq_pin; - void (*realize)(PCIDevice *dev, Error **errp); - bool unplug; -}; - -struct UHCIPCIDeviceClass { - PCIDeviceClass parent_class; - UHCIInfo info; -}; - -/* - * Pending async transaction. - * 'packet' must be the first field because completion - * handler does "(UHCIAsync *) pkt" cast. - */ - -struct UHCIAsync { - USBPacket packet; - uint8_t static_buf[64]; /* 64 bytes is enough, except for isoc packets */ - uint8_t *buf; - UHCIQueue *queue; - QTAILQ_ENTRY(UHCIAsync) next; - uint32_t td_addr; - uint8_t done; -}; - -struct UHCIQueue { - uint32_t qh_addr; - uint32_t token; - UHCIState *uhci; - USBEndpoint *ep; - QTAILQ_ENTRY(UHCIQueue) next; - QTAILQ_HEAD(asyncs_head, UHCIAsync) asyncs; - int8_t valid; -}; - -typedef struct UHCIPort { - USBPort port; - uint16_t ctrl; -} UHCIPort; - -struct UHCIState { - PCIDevice dev; - MemoryRegion io_bar; - USBBus bus; /* Note unused when we're a companion controller */ - uint16_t cmd; /* cmd register */ - uint16_t status; - uint16_t intr; /* interrupt enable register */ - uint16_t frnum; /* frame number */ - uint32_t fl_base_addr; /* frame list base address */ - uint8_t sof_timing; - uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */ - int64_t expire_time; - QEMUTimer *frame_timer; - QEMUBH *bh; - uint32_t frame_bytes; - uint32_t frame_bandwidth; - bool completions_only; - UHCIPort ports[NB_PORTS]; - - /* Interrupts that should be raised at the end of the current frame. */ - uint32_t pending_int_mask; - - /* Active packets */ - QTAILQ_HEAD(, UHCIQueue) queues; - uint8_t num_ports_vmstate; - - /* Properties */ - char *masterbus; - uint32_t firstport; - uint32_t maxframes; -}; - -typedef struct UHCI_TD { - uint32_t link; - uint32_t ctrl; /* see TD_CTRL_xxx */ - uint32_t token; - uint32_t buffer; -} UHCI_TD; - -typedef struct UHCI_QH { - uint32_t link; - uint32_t el_link; -} UHCI_QH; - -static void uhci_async_cancel(UHCIAsync *async); -static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td); -static void uhci_resume(void *opaque); - -#define TYPE_UHCI "pci-uhci-usb" -#define UHCI(obj) OBJECT_CHECK(UHCIState, (obj), TYPE_UHCI) - -static inline int32_t uhci_queue_token(UHCI_TD *td) -{ - if ((td->token & (0xf << 15)) == 0) { - /* ctrl ep, cover ep and dev, not pid! */ - return td->token & 0x7ff00; - } else { - /* covers ep, dev, pid -> identifies the endpoint */ - return td->token & 0x7ffff; - } -} - -static UHCIQueue *uhci_queue_new(UHCIState *s, uint32_t qh_addr, UHCI_TD *td, - USBEndpoint *ep) -{ - UHCIQueue *queue; - - queue = g_new0(UHCIQueue, 1); - queue->uhci = s; - queue->qh_addr = qh_addr; - queue->token = uhci_queue_token(td); - queue->ep = ep; - QTAILQ_INIT(&queue->asyncs); - QTAILQ_INSERT_HEAD(&s->queues, queue, next); - queue->valid = QH_VALID; - trace_usb_uhci_queue_add(queue->token); - return queue; -} - -static void uhci_queue_free(UHCIQueue *queue, const char *reason) -{ - UHCIState *s = queue->uhci; - UHCIAsync *async; - - while (!QTAILQ_EMPTY(&queue->asyncs)) { - async = QTAILQ_FIRST(&queue->asyncs); - uhci_async_cancel(async); - } - usb_device_ep_stopped(queue->ep->dev, queue->ep); - - trace_usb_uhci_queue_del(queue->token, reason); - QTAILQ_REMOVE(&s->queues, queue, next); - g_free(queue); -} - -static UHCIQueue *uhci_queue_find(UHCIState *s, UHCI_TD *td) -{ - uint32_t token = uhci_queue_token(td); - UHCIQueue *queue; - - QTAILQ_FOREACH(queue, &s->queues, next) { - if (queue->token == token) { - return queue; - } - } - return NULL; -} - -static bool uhci_queue_verify(UHCIQueue *queue, uint32_t qh_addr, UHCI_TD *td, - uint32_t td_addr, bool queuing) -{ - UHCIAsync *first = QTAILQ_FIRST(&queue->asyncs); - uint32_t queue_token_addr = (queue->token >> 8) & 0x7f; - - return queue->qh_addr == qh_addr && - queue->token == uhci_queue_token(td) && - queue_token_addr == queue->ep->dev->addr && - (queuing || !(td->ctrl & TD_CTRL_ACTIVE) || first == NULL || - first->td_addr == td_addr); -} - -static UHCIAsync *uhci_async_alloc(UHCIQueue *queue, uint32_t td_addr) -{ - UHCIAsync *async = g_new0(UHCIAsync, 1); - - async->queue = queue; - async->td_addr = td_addr; - usb_packet_init(&async->packet); - trace_usb_uhci_packet_add(async->queue->token, async->td_addr); - - return async; -} - -static void uhci_async_free(UHCIAsync *async) -{ - trace_usb_uhci_packet_del(async->queue->token, async->td_addr); - usb_packet_cleanup(&async->packet); - if (async->buf != async->static_buf) { - g_free(async->buf); - } - g_free(async); -} - -static void uhci_async_link(UHCIAsync *async) -{ - UHCIQueue *queue = async->queue; - QTAILQ_INSERT_TAIL(&queue->asyncs, async, next); - trace_usb_uhci_packet_link_async(async->queue->token, async->td_addr); -} - -static void uhci_async_unlink(UHCIAsync *async) -{ - UHCIQueue *queue = async->queue; - QTAILQ_REMOVE(&queue->asyncs, async, next); - trace_usb_uhci_packet_unlink_async(async->queue->token, async->td_addr); -} - -static void uhci_async_cancel(UHCIAsync *async) -{ - uhci_async_unlink(async); - trace_usb_uhci_packet_cancel(async->queue->token, async->td_addr, - async->done); - if (!async->done) - usb_cancel_packet(&async->packet); - uhci_async_free(async); -} - -/* - * Mark all outstanding async packets as invalid. - * This is used for canceling them when TDs are removed by the HCD. - */ -static void uhci_async_validate_begin(UHCIState *s) -{ - UHCIQueue *queue; - - QTAILQ_FOREACH(queue, &s->queues, next) { - queue->valid--; - } -} - -/* - * Cancel async packets that are no longer valid - */ -static void uhci_async_validate_end(UHCIState *s) -{ - UHCIQueue *queue, *n; - - QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) { - if (!queue->valid) { - uhci_queue_free(queue, "validate-end"); - } - } -} - -static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev) -{ - UHCIQueue *queue, *n; - - QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) { - if (queue->ep->dev == dev) { - uhci_queue_free(queue, "cancel-device"); - } - } -} - -static void uhci_async_cancel_all(UHCIState *s) -{ - UHCIQueue *queue, *nq; - - QTAILQ_FOREACH_SAFE(queue, &s->queues, next, nq) { - uhci_queue_free(queue, "cancel-all"); - } -} - -static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t td_addr) -{ - UHCIQueue *queue; - UHCIAsync *async; - - QTAILQ_FOREACH(queue, &s->queues, next) { - QTAILQ_FOREACH(async, &queue->asyncs, next) { - if (async->td_addr == td_addr) { - return async; - } - } - } - return NULL; -} - -static void uhci_update_irq(UHCIState *s) -{ - int level; - if (((s->status2 & 1) && (s->intr & (1 << 2))) || - ((s->status2 & 2) && (s->intr & (1 << 3))) || - ((s->status & UHCI_STS_USBERR) && (s->intr & (1 << 0))) || - ((s->status & UHCI_STS_RD) && (s->intr & (1 << 1))) || - (s->status & UHCI_STS_HSERR) || - (s->status & UHCI_STS_HCPERR)) { - level = 1; - } else { - level = 0; - } - pci_set_irq(&s->dev, level); -} - -static void uhci_reset(DeviceState *dev) -{ - PCIDevice *d = PCI_DEVICE(dev); - UHCIState *s = UHCI(d); - uint8_t *pci_conf; - int i; - UHCIPort *port; - - trace_usb_uhci_reset(); - - pci_conf = s->dev.config; - - pci_conf[0x6a] = 0x01; /* usb clock */ - pci_conf[0x6b] = 0x00; - s->cmd = 0; - s->status = UHCI_STS_HCHALTED; - s->status2 = 0; - s->intr = 0; - s->fl_base_addr = 0; - s->sof_timing = 64; - - for(i = 0; i < NB_PORTS; i++) { - port = &s->ports[i]; - port->ctrl = 0x0080; - if (port->port.dev && port->port.dev->attached) { - usb_port_reset(&port->port); - } - } - - uhci_async_cancel_all(s); - qemu_bh_cancel(s->bh); - uhci_update_irq(s); -} - -static const VMStateDescription vmstate_uhci_port = { - .name = "uhci port", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT16(ctrl, UHCIPort), - VMSTATE_END_OF_LIST() - } -}; - -static int uhci_post_load(void *opaque, int version_id) -{ - UHCIState *s = opaque; - - if (version_id < 2) { - s->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ); - } - return 0; -} - -static const VMStateDescription vmstate_uhci = { - .name = "uhci", - .version_id = 3, - .minimum_version_id = 1, - .post_load = uhci_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, UHCIState), - VMSTATE_UINT8_EQUAL(num_ports_vmstate, UHCIState), - VMSTATE_STRUCT_ARRAY(ports, UHCIState, NB_PORTS, 1, - vmstate_uhci_port, UHCIPort), - VMSTATE_UINT16(cmd, UHCIState), - VMSTATE_UINT16(status, UHCIState), - VMSTATE_UINT16(intr, UHCIState), - VMSTATE_UINT16(frnum, UHCIState), - VMSTATE_UINT32(fl_base_addr, UHCIState), - VMSTATE_UINT8(sof_timing, UHCIState), - VMSTATE_UINT8(status2, UHCIState), - VMSTATE_TIMER_PTR(frame_timer, UHCIState), - VMSTATE_INT64_V(expire_time, UHCIState, 2), - VMSTATE_UINT32_V(pending_int_mask, UHCIState, 3), - VMSTATE_END_OF_LIST() - } -}; - -static void uhci_port_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - UHCIState *s = opaque; - - trace_usb_uhci_mmio_writew(addr, val); - - switch(addr) { - case 0x00: - if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) { - /* start frame processing */ - trace_usb_uhci_schedule_start(); - s->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (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)) { - s->status |= UHCI_STS_HCHALTED; - } - if (val & UHCI_CMD_GRESET) { - UHCIPort *port; - int i; - - /* send reset on the USB bus */ - for(i = 0; i < NB_PORTS; i++) { - port = &s->ports[i]; - usb_device_reset(port->port.dev); - } - uhci_reset(DEVICE(s)); - return; - } - if (val & UHCI_CMD_HCRESET) { - uhci_reset(DEVICE(s)); - return; - } - s->cmd = val; - if (val & UHCI_CMD_EGSM) { - if ((s->ports[0].ctrl & UHCI_PORT_RD) || - (s->ports[1].ctrl & UHCI_PORT_RD)) { - uhci_resume(s); - } - } - break; - case 0x02: - s->status &= ~val; - /* XXX: the chip spec is not coherent, so we add a hidden - register to distinguish between IOC and SPD */ - if (val & UHCI_STS_USBINT) - s->status2 = 0; - uhci_update_irq(s); - break; - case 0x04: - s->intr = val; - uhci_update_irq(s); - break; - case 0x06: - if (s->status & UHCI_STS_HCHALTED) - s->frnum = val & 0x7ff; - break; - case 0x08: - s->fl_base_addr &= 0xffff0000; - s->fl_base_addr |= val & ~0xfff; - break; - case 0x0a: - s->fl_base_addr &= 0x0000ffff; - s->fl_base_addr |= (val << 16); - break; - case 0x0c: - s->sof_timing = val & 0xff; - break; - case 0x10 ... 0x1f: - { - UHCIPort *port; - USBDevice *dev; - int n; - - n = (addr >> 1) & 7; - if (n >= NB_PORTS) - return; - port = &s->ports[n]; - dev = port->port.dev; - if (dev && dev->attached) { - /* port reset */ - if ( (val & UHCI_PORT_RESET) && - !(port->ctrl & UHCI_PORT_RESET) ) { - usb_device_reset(dev); - } - } - port->ctrl &= UHCI_PORT_READ_ONLY; - /* enabled may only be set if a device is connected */ - if (!(port->ctrl & UHCI_PORT_CCS)) { - val &= ~UHCI_PORT_EN; - } - port->ctrl |= (val & ~UHCI_PORT_READ_ONLY); - /* some bits are reset when a '1' is written to them */ - port->ctrl &= ~(val & UHCI_PORT_WRITE_CLEAR); - } - break; - } -} - -static uint64_t uhci_port_read(void *opaque, hwaddr addr, unsigned size) -{ - UHCIState *s = opaque; - uint32_t val; - - switch(addr) { - case 0x00: - val = s->cmd; - break; - case 0x02: - val = s->status; - break; - case 0x04: - val = s->intr; - break; - case 0x06: - val = s->frnum; - break; - case 0x08: - val = s->fl_base_addr & 0xffff; - break; - case 0x0a: - val = (s->fl_base_addr >> 16) & 0xffff; - break; - case 0x0c: - val = s->sof_timing; - break; - case 0x10 ... 0x1f: - { - UHCIPort *port; - int n; - n = (addr >> 1) & 7; - if (n >= NB_PORTS) - goto read_default; - port = &s->ports[n]; - val = port->ctrl; - } - break; - default: - read_default: - val = 0xff7f; /* disabled port */ - break; - } - - trace_usb_uhci_mmio_readw(addr, val); - - return val; -} - -/* signal resume if controller suspended */ -static void uhci_resume (void *opaque) -{ - UHCIState *s = (UHCIState *)opaque; - - if (!s) - return; - - if (s->cmd & UHCI_CMD_EGSM) { - s->cmd |= UHCI_CMD_FGR; - s->status |= UHCI_STS_RD; - uhci_update_irq(s); - } -} - -static void uhci_attach(USBPort *port1) -{ - UHCIState *s = port1->opaque; - UHCIPort *port = &s->ports[port1->index]; - - /* set connect status */ - port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC; - - /* update speed */ - if (port->port.dev->speed == USB_SPEED_LOW) { - port->ctrl |= UHCI_PORT_LSDA; - } else { - port->ctrl &= ~UHCI_PORT_LSDA; - } - - uhci_resume(s); -} - -static void uhci_detach(USBPort *port1) -{ - UHCIState *s = port1->opaque; - UHCIPort *port = &s->ports[port1->index]; - - uhci_async_cancel_device(s, port1->dev); - - /* set connect status */ - if (port->ctrl & UHCI_PORT_CCS) { - port->ctrl &= ~UHCI_PORT_CCS; - port->ctrl |= UHCI_PORT_CSC; - } - /* disable port */ - if (port->ctrl & UHCI_PORT_EN) { - port->ctrl &= ~UHCI_PORT_EN; - port->ctrl |= UHCI_PORT_ENC; - } - - uhci_resume(s); -} - -static void uhci_child_detach(USBPort *port1, USBDevice *child) -{ - UHCIState *s = port1->opaque; - - uhci_async_cancel_device(s, child); -} - -static void uhci_wakeup(USBPort *port1) -{ - UHCIState *s = port1->opaque; - UHCIPort *port = &s->ports[port1->index]; - - if (port->ctrl & UHCI_PORT_SUSPEND && !(port->ctrl & UHCI_PORT_RD)) { - port->ctrl |= UHCI_PORT_RD; - uhci_resume(s); - } -} - -static USBDevice *uhci_find_device(UHCIState *s, uint8_t addr) -{ - USBDevice *dev; - int i; - - for (i = 0; i < NB_PORTS; i++) { - UHCIPort *port = &s->ports[i]; - if (!(port->ctrl & UHCI_PORT_EN)) { - continue; - } - dev = usb_find_device(&port->port, addr); - if (dev != NULL) { - return dev; - } - } - return NULL; -} - -static void uhci_read_td(UHCIState *s, UHCI_TD *td, uint32_t link) -{ - pci_dma_read(&s->dev, link & ~0xf, td, sizeof(*td)); - le32_to_cpus(&td->link); - le32_to_cpus(&td->ctrl); - le32_to_cpus(&td->token); - le32_to_cpus(&td->buffer); -} - -static int uhci_handle_td_error(UHCIState *s, UHCI_TD *td, uint32_t td_addr, - int status, uint32_t *int_mask) -{ - uint32_t queue_token = uhci_queue_token(td); - int ret; - - switch (status) { - case USB_RET_NAK: - td->ctrl |= TD_CTRL_NAK; - return TD_RESULT_NEXT_QH; - - case USB_RET_STALL: - td->ctrl |= TD_CTRL_STALL; - trace_usb_uhci_packet_complete_stall(queue_token, td_addr); - ret = TD_RESULT_NEXT_QH; - break; - - case USB_RET_BABBLE: - td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; - /* frame interrupted */ - trace_usb_uhci_packet_complete_babble(queue_token, td_addr); - ret = TD_RESULT_STOP_FRAME; - break; - - case USB_RET_IOERROR: - case USB_RET_NODEV: - default: - td->ctrl |= TD_CTRL_TIMEOUT; - td->ctrl &= ~(3 << TD_CTRL_ERROR_SHIFT); - trace_usb_uhci_packet_complete_error(queue_token, td_addr); - ret = TD_RESULT_NEXT_QH; - break; - } - - td->ctrl &= ~TD_CTRL_ACTIVE; - s->status |= UHCI_STS_USBERR; - if (td->ctrl & TD_CTRL_IOC) { - *int_mask |= 0x01; - } - uhci_update_irq(s); - return ret; -} - -static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask) -{ - int len = 0, max_len; - uint8_t pid; - - max_len = ((td->token >> 21) + 1) & 0x7ff; - pid = td->token & 0xff; - - if (td->ctrl & TD_CTRL_IOS) - td->ctrl &= ~TD_CTRL_ACTIVE; - - if (async->packet.status != USB_RET_SUCCESS) { - return uhci_handle_td_error(s, td, async->td_addr, - async->packet.status, int_mask); - } - - len = async->packet.actual_length; - td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); - - /* The NAK bit may have been set by a previous frame, so clear it - here. The docs are somewhat unclear, but win2k relies on this - behavior. */ - td->ctrl &= ~(TD_CTRL_ACTIVE | TD_CTRL_NAK); - if (td->ctrl & TD_CTRL_IOC) - *int_mask |= 0x01; - - if (pid == USB_TOKEN_IN) { - pci_dma_write(&s->dev, td->buffer, async->buf, len); - if ((td->ctrl & TD_CTRL_SPD) && len < max_len) { - *int_mask |= 0x02; - /* short packet: do not update QH */ - trace_usb_uhci_packet_complete_shortxfer(async->queue->token, - async->td_addr); - return TD_RESULT_NEXT_QH; - } - } - - /* success */ - trace_usb_uhci_packet_complete_success(async->queue->token, - async->td_addr); - return TD_RESULT_COMPLETE; -} - -static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, - UHCI_TD *td, uint32_t td_addr, uint32_t *int_mask) -{ - int ret, max_len; - bool spd; - bool queuing = (q != NULL); - uint8_t pid = td->token & 0xff; - 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); - q = async->queue; - } else { - uhci_queue_free(async->queue, "guest re-used pending td"); - async = NULL; - } - } - - if (q == NULL) { - q = uhci_queue_find(s, td); - if (q && !uhci_queue_verify(q, qh_addr, td, td_addr, queuing)) { - uhci_queue_free(q, "guest re-used qh"); - q = NULL; - } - } - - if (q) { - q->valid = QH_VALID; - } - - /* Is active ? */ - if (!(td->ctrl & TD_CTRL_ACTIVE)) { - if (async) { - /* Guest marked a pending td non-active, cancel the queue */ - uhci_queue_free(async->queue, "pending td non-active"); - } - /* - * ehci11d spec page 22: "Even if the Active bit in the TD is already - * cleared when the TD is fetched ... an IOC interrupt is generated" - */ - if (td->ctrl & TD_CTRL_IOC) { - *int_mask |= 0x01; - } - 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 - to consume completed packages then, just leave them - in async state */ - return TD_RESULT_ASYNC_CONT; - } - if (!async->done) { - UHCI_TD last_td; - UHCIAsync *last = QTAILQ_LAST(&async->queue->asyncs, asyncs_head); - /* - * While we are waiting for the current td to complete, the guest - * may have added more tds to the queue. Note we re-read the td - * rather then caching it, as we want to see guest made changes! - */ - uhci_read_td(s, &last_td, last->td_addr); - uhci_queue_fill(async->queue, &last_td); - - return TD_RESULT_ASYNC_CONT; - } - uhci_async_unlink(async); - goto done; - } - - if (s->completions_only) { - return TD_RESULT_ASYNC_CONT; - } - - /* Allocate new packet */ - if (q == NULL) { - USBDevice *dev = uhci_find_device(s, (td->token >> 8) & 0x7f); - USBEndpoint *ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf); - - if (ep == NULL) { - return uhci_handle_td_error(s, td, td_addr, USB_RET_NODEV, - int_mask); - } - q = uhci_queue_new(s, qh_addr, td, ep); - } - async = uhci_async_alloc(q, td_addr); - - max_len = ((td->token >> 21) + 1) & 0x7ff; - spd = (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) != 0); - usb_packet_setup(&async->packet, pid, q->ep, 0, td_addr, spd, - (td->ctrl & TD_CTRL_IOC) != 0); - if (max_len <= sizeof(async->static_buf)) { - async->buf = async->static_buf; - } else { - async->buf = g_malloc(max_len); - } - usb_packet_addbuf(&async->packet, async->buf, max_len); - - switch(pid) { - case USB_TOKEN_OUT: - case USB_TOKEN_SETUP: - pci_dma_read(&s->dev, td->buffer, async->buf, max_len); - usb_handle_packet(q->ep->dev, &async->packet); - if (async->packet.status == USB_RET_SUCCESS) { - async->packet.actual_length = max_len; - } - break; - - case USB_TOKEN_IN: - usb_handle_packet(q->ep->dev, &async->packet); - break; - - default: - abort(); /* Never to execute */ - } - - if (async->packet.status == USB_RET_ASYNC) { - uhci_async_link(async); - if (!queuing) { - uhci_queue_fill(q, td); - } - return TD_RESULT_ASYNC_START; - } - -done: - ret = uhci_complete_td(s, td, async, int_mask); - uhci_async_free(async); - return ret; -} - -static void uhci_async_complete(USBPort *port, USBPacket *packet) -{ - UHCIAsync *async = container_of(packet, UHCIAsync, packet); - UHCIState *s = async->queue->uhci; - - if (packet->status == USB_RET_REMOVE_FROM_QUEUE) { - uhci_async_cancel(async); - return; - } - - async->done = 1; - /* Force processing of this packet *now*, needed for migration */ - s->completions_only = true; - qemu_bh_schedule(s->bh); -} - -static int is_valid(uint32_t link) -{ - return (link & 1) == 0; -} - -static int is_qh(uint32_t link) -{ - return (link & 2) != 0; -} - -static int depth_first(uint32_t link) -{ - return (link & 4) != 0; -} - -/* QH DB used for detecting QH loops */ -#define UHCI_MAX_QUEUES 128 -typedef struct { - uint32_t addr[UHCI_MAX_QUEUES]; - int count; -} QhDb; - -static void qhdb_reset(QhDb *db) -{ - db->count = 0; -} - -/* Add QH to DB. Returns 1 if already present or DB is full. */ -static int qhdb_insert(QhDb *db, uint32_t addr) -{ - int i; - for (i = 0; i < db->count; i++) - if (db->addr[i] == addr) - return 1; - - if (db->count >= UHCI_MAX_QUEUES) - return 1; - - db->addr[db->count++] = addr; - return 0; -} - -static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td) -{ - uint32_t int_mask = 0; - uint32_t plink = td->link; - UHCI_TD ptd; - int ret; - - while (is_valid(plink)) { - uhci_read_td(q->uhci, &ptd, plink); - if (!(ptd.ctrl & TD_CTRL_ACTIVE)) { - break; - } - if (uhci_queue_token(&ptd) != q->token) { - break; - } - trace_usb_uhci_td_queue(plink & ~0xf, ptd.ctrl, ptd.token); - ret = uhci_handle_td(q->uhci, q, q->qh_addr, &ptd, plink, &int_mask); - if (ret == TD_RESULT_ASYNC_CONT) { - break; - } - assert(ret == TD_RESULT_ASYNC_START); - assert(int_mask == 0); - plink = ptd.link; - } - usb_device_flush_ep_queue(q->ep->dev, q->ep); -} - -static void uhci_process_frame(UHCIState *s) -{ - uint32_t frame_addr, link, old_td_ctrl, val, int_mask; - uint32_t curr_qh, td_count = 0; - int cnt, ret; - UHCI_TD td; - UHCI_QH qh; - QhDb qhdb; - - frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2); - - pci_dma_read(&s->dev, frame_addr, &link, 4); - le32_to_cpus(&link); - - int_mask = 0; - curr_qh = 0; - - qhdb_reset(&qhdb); - - for (cnt = FRAME_MAX_LOOPS; is_valid(link) && cnt; cnt--) { - if (!s->completions_only && s->frame_bytes >= s->frame_bandwidth) { - /* We've reached the usb 1.1 bandwidth, which is - 1280 bytes/frame, stop processing */ - trace_usb_uhci_frame_stop_bandwidth(); - break; - } - if (is_qh(link)) { - /* QH */ - trace_usb_uhci_qh_load(link & ~0xf); - - if (qhdb_insert(&qhdb, link)) { - /* - * We're going in circles. Which is not a bug because - * HCD is allowed to do that as part of the BW management. - * - * Stop processing here if no transaction has been done - * since we've been here last time. - */ - if (td_count == 0) { - trace_usb_uhci_frame_loop_stop_idle(); - break; - } else { - trace_usb_uhci_frame_loop_continue(); - td_count = 0; - qhdb_reset(&qhdb); - qhdb_insert(&qhdb, link); - } - } - - pci_dma_read(&s->dev, link & ~0xf, &qh, sizeof(qh)); - le32_to_cpus(&qh.link); - le32_to_cpus(&qh.el_link); - - if (!is_valid(qh.el_link)) { - /* QH w/o elements */ - curr_qh = 0; - link = qh.link; - } else { - /* QH with elements */ - curr_qh = link; - link = qh.el_link; - } - continue; - } - - /* TD */ - uhci_read_td(s, &td, link); - trace_usb_uhci_td_load(curr_qh & ~0xf, link & ~0xf, td.ctrl, td.token); - - old_td_ctrl = td.ctrl; - ret = uhci_handle_td(s, NULL, curr_qh, &td, link, &int_mask); - if (old_td_ctrl != td.ctrl) { - /* update the status bits of the TD */ - val = cpu_to_le32(td.ctrl); - pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val)); - } - - switch (ret) { - case TD_RESULT_STOP_FRAME: /* interrupted frame */ - goto out; - - case TD_RESULT_NEXT_QH: - case TD_RESULT_ASYNC_CONT: - trace_usb_uhci_td_nextqh(curr_qh & ~0xf, link & ~0xf); - link = curr_qh ? qh.link : td.link; - continue; - - case TD_RESULT_ASYNC_START: - trace_usb_uhci_td_async(curr_qh & ~0xf, link & ~0xf); - link = curr_qh ? qh.link : td.link; - continue; - - case TD_RESULT_COMPLETE: - trace_usb_uhci_td_complete(curr_qh & ~0xf, link & ~0xf); - link = td.link; - td_count++; - s->frame_bytes += (td.ctrl & 0x7ff) + 1; - - if (curr_qh) { - /* update QH element link */ - qh.el_link = link; - val = cpu_to_le32(qh.el_link); - pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val)); - - if (!depth_first(link)) { - /* done with this QH */ - curr_qh = 0; - link = qh.link; - } - } - break; - - default: - assert(!"unknown return code"); - } - - /* go to the next entry */ - } - -out: - s->pending_int_mask |= int_mask; -} - -static void uhci_bh(void *opaque) -{ - UHCIState *s = opaque; - uhci_process_frame(s); -} - -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 = NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ; - - s->completions_only = false; - qemu_bh_cancel(s->bh); - - if (!(s->cmd & UHCI_CMD_RS)) { - /* Full stop */ - trace_usb_uhci_schedule_stop(); - timer_del(s->frame_timer); - uhci_async_cancel_all(s); - /* set hchalted bit in status - UHCI11D 2.1.2 */ - s->status |= UHCI_STS_HCHALTED; - return; - } - - /* We still store expire_time in our state, for migration */ - t_last_run = s->expire_time - frame_t; - t_now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - /* Process up to MAX_FRAMES_PER_TICK frames */ - frames = (t_now - t_last_run) / frame_t; - if (frames > s->maxframes) { - int skipped = frames - s->maxframes; - s->expire_time += skipped * frame_t; - s->frnum = (s->frnum + skipped) & 0x7ff; - frames -= skipped; - } - if (frames > MAX_FRAMES_PER_TICK) { - frames = MAX_FRAMES_PER_TICK; - } - - for (i = 0; i < frames; i++) { - s->frame_bytes = 0; - trace_usb_uhci_frame_start(s->frnum); - uhci_async_validate_begin(s); - uhci_process_frame(s); - uhci_async_validate_end(s); - /* The spec says frnum is the frame currently being processed, and - * the guest must look at frnum - 1 on interrupt, so inc frnum now */ - s->frnum = (s->frnum + 1) & 0x7ff; - s->expire_time += frame_t; - } - - /* Complete the previous frame(s) */ - if (s->pending_int_mask) { - s->status2 |= s->pending_int_mask; - s->status |= UHCI_STS_USBINT; - uhci_update_irq(s); - } - s->pending_int_mask = 0; - - timer_mod(s->frame_timer, t_now + frame_t); -} - -static const MemoryRegionOps uhci_ioport_ops = { - .read = uhci_port_read, - .write = uhci_port_write, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .impl.min_access_size = 2, - .impl.max_access_size = 2, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static USBPortOps uhci_port_ops = { - .attach = uhci_attach, - .detach = uhci_detach, - .child_detach = uhci_child_detach, - .wakeup = uhci_wakeup, - .complete = uhci_async_complete, -}; - -static USBBusOps uhci_bus_ops = { -}; - -static void usb_uhci_common_realize(PCIDevice *dev, Error **errp) -{ - Error *err = NULL; - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); - UHCIPCIDeviceClass *u = container_of(pc, UHCIPCIDeviceClass, parent_class); - UHCIState *s = UHCI(dev); - uint8_t *pci_conf = s->dev.config; - int i; - - pci_conf[PCI_CLASS_PROG] = 0x00; - /* TODO: reset value should be 0. */ - pci_conf[USB_SBRN] = USB_RELEASE_1; // release number - - pci_config_set_interrupt_pin(pci_conf, u->info.irq_pin + 1); - - if (s->masterbus) { - USBPort *ports[NB_PORTS]; - for(i = 0; i < NB_PORTS; i++) { - ports[i] = &s->ports[i].port; - } - usb_register_companion(s->masterbus, ports, NB_PORTS, - s->firstport, s, &uhci_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL, - &err); - if (err) { - error_propagate(errp, err); - return; - } - } else { - usb_bus_new(&s->bus, sizeof(s->bus), &uhci_bus_ops, DEVICE(dev)); - for (i = 0; i < NB_PORTS; i++) { - usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - } - } - s->bh = qemu_bh_new(uhci_bh, s); - s->frame_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, uhci_frame_timer, s); - s->num_ports_vmstate = NB_PORTS; - QTAILQ_INIT(&s->queues); - - memory_region_init_io(&s->io_bar, OBJECT(s), &uhci_ioport_ops, s, - "uhci", 0x20); - - /* Use region 4 for consistency with real hardware. BSD guests seem - to rely on this. */ - pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar); -} - -static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp) -{ - UHCIState *s = UHCI(dev); - uint8_t *pci_conf = s->dev.config; - - /* USB misc control 1/2 */ - pci_set_long(pci_conf + 0x40,0x00001000); - /* PM capability */ - pci_set_long(pci_conf + 0x80,0x00020001); - /* USB legacy support */ - pci_set_long(pci_conf + 0xc0,0x00002000); - - usb_uhci_common_realize(dev, errp); -} - -static void usb_uhci_exit(PCIDevice *dev) -{ - UHCIState *s = UHCI(dev); - - trace_usb_uhci_exit(); - - if (s->frame_timer) { - timer_del(s->frame_timer); - timer_free(s->frame_timer); - s->frame_timer = NULL; - } - - if (s->bh) { - qemu_bh_delete(s->bh); - } - - uhci_async_cancel_all(s); - - if (!s->masterbus) { - usb_bus_release(&s->bus); - } -} - -static Property uhci_properties_companion[] = { - DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), - DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), - DEFINE_PROP_UINT32("bandwidth", UHCIState, frame_bandwidth, 1280), - DEFINE_PROP_UINT32("maxframes", UHCIState, maxframes, 128), - DEFINE_PROP_END_OF_LIST(), -}; -static Property uhci_properties_standalone[] = { - DEFINE_PROP_UINT32("bandwidth", UHCIState, frame_bandwidth, 1280), - DEFINE_PROP_UINT32("maxframes", UHCIState, maxframes, 128), - DEFINE_PROP_END_OF_LIST(), -}; - -static void uhci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->class_id = PCI_CLASS_SERIAL_USB; - dc->vmsd = &vmstate_uhci; - dc->reset = uhci_reset; - set_bit(DEVICE_CATEGORY_USB, dc->categories); -} - -static const TypeInfo uhci_pci_type_info = { - .name = TYPE_UHCI, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(UHCIState), - .class_size = sizeof(UHCIPCIDeviceClass), - .abstract = true, - .class_init = uhci_class_init, -}; - -static void uhci_data_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - UHCIPCIDeviceClass *u = container_of(k, UHCIPCIDeviceClass, parent_class); - UHCIInfo *info = data; - - k->realize = info->realize ? info->realize : usb_uhci_common_realize; - k->exit = info->unplug ? usb_uhci_exit : NULL; - k->vendor_id = info->vendor_id; - k->device_id = info->device_id; - k->revision = info->revision; - if (!info->unplug) { - /* uhci controllers in companion setups can't be hotplugged */ - dc->hotpluggable = false; - dc->props = uhci_properties_companion; - } else { - dc->props = uhci_properties_standalone; - } - u->info = *info; -} - -static UHCIInfo uhci_info[] = { - { - .name = "piix3-usb-uhci", - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82371SB_2, - .revision = 0x01, - .irq_pin = 3, - .unplug = true, - },{ - .name = "piix4-usb-uhci", - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82371AB_2, - .revision = 0x01, - .irq_pin = 3, - .unplug = true, - },{ - .name = "vt82c686b-usb-uhci", - .vendor_id = PCI_VENDOR_ID_VIA, - .device_id = PCI_DEVICE_ID_VIA_UHCI, - .revision = 0x01, - .irq_pin = 3, - .realize = usb_uhci_vt82c686b_realize, - .unplug = true, - },{ - .name = "ich9-usb-uhci1", /* 00:1d.0 */ - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1, - .revision = 0x03, - .irq_pin = 0, - .unplug = false, - },{ - .name = "ich9-usb-uhci2", /* 00:1d.1 */ - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2, - .revision = 0x03, - .irq_pin = 1, - .unplug = false, - },{ - .name = "ich9-usb-uhci3", /* 00:1d.2 */ - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3, - .revision = 0x03, - .irq_pin = 2, - .unplug = false, - },{ - .name = "ich9-usb-uhci4", /* 00:1a.0 */ - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI4, - .revision = 0x03, - .irq_pin = 0, - .unplug = false, - },{ - .name = "ich9-usb-uhci5", /* 00:1a.1 */ - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI5, - .revision = 0x03, - .irq_pin = 1, - .unplug = false, - },{ - .name = "ich9-usb-uhci6", /* 00:1a.2 */ - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI6, - .revision = 0x03, - .irq_pin = 2, - .unplug = false, - } -}; - -static void uhci_register_types(void) -{ - TypeInfo uhci_type_info = { - .parent = TYPE_UHCI, - .class_init = uhci_data_class_init, - }; - int i; - - type_register_static(&uhci_pci_type_info); - - for (i = 0; i < ARRAY_SIZE(uhci_info); i++) { - uhci_type_info.name = uhci_info[i].name; - uhci_type_info.class_data = uhci_info + i; - type_register(&uhci_type_info); - } -} - -type_init(uhci_register_types) diff --git a/qemu/hw/usb/hcd-xhci.c b/qemu/hw/usb/hcd-xhci.c deleted file mode 100644 index bcde8a2f4..000000000 --- a/qemu/hw/usb/hcd-xhci.c +++ /dev/null @@ -1,3917 +0,0 @@ -/* - * USB xHCI controller emulation - * - * Copyright (c) 2011 Securiforest - * Date: 2011-05-11 ; Author: Hector Martin - * Based on usb-ohci.c, emulates Renesas NEC USB 3.0 - * - * 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 . - */ -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "qemu/timer.h" -#include "hw/usb.h" -#include "hw/pci/pci.h" -#include "hw/pci/msi.h" -#include "hw/pci/msix.h" -#include "trace.h" - -//#define DEBUG_XHCI -//#define DEBUG_DATA - -#ifdef DEBUG_XHCI -#define DPRINTF(...) fprintf(stderr, __VA_ARGS__) -#else -#define DPRINTF(...) do {} while (0) -#endif -#define FIXME(_msg) do { fprintf(stderr, "FIXME %s:%d %s\n", \ - __func__, __LINE__, _msg); abort(); } while (0) - -#define MAXPORTS_2 15 -#define MAXPORTS_3 15 - -#define MAXPORTS (MAXPORTS_2+MAXPORTS_3) -#define MAXSLOTS 64 -#define MAXINTRS 16 - -#define TD_QUEUE 24 - -/* Very pessimistic, let's hope it's enough for all cases */ -#define EV_QUEUE (((3*TD_QUEUE)+16)*MAXSLOTS) -/* Do not deliver ER Full events. NEC's driver does some things not bound - * to the specs when it gets them */ -#define ER_FULL_HACK - -#define LEN_CAP 0x40 -#define LEN_OPER (0x400 + 0x10 * MAXPORTS) -#define LEN_RUNTIME ((MAXINTRS + 1) * 0x20) -#define LEN_DOORBELL ((MAXSLOTS + 1) * 0x20) - -#define OFF_OPER LEN_CAP -#define OFF_RUNTIME 0x1000 -#define OFF_DOORBELL 0x2000 -#define OFF_MSIX_TABLE 0x3000 -#define OFF_MSIX_PBA 0x3800 -/* must be power of 2 */ -#define LEN_REGS 0x4000 - -#if (OFF_OPER + LEN_OPER) > OFF_RUNTIME -#error Increase OFF_RUNTIME -#endif -#if (OFF_RUNTIME + LEN_RUNTIME) > OFF_DOORBELL -#error Increase OFF_DOORBELL -#endif -#if (OFF_DOORBELL + LEN_DOORBELL) > LEN_REGS -# error Increase LEN_REGS -#endif - -/* bit definitions */ -#define USBCMD_RS (1<<0) -#define USBCMD_HCRST (1<<1) -#define USBCMD_INTE (1<<2) -#define USBCMD_HSEE (1<<3) -#define USBCMD_LHCRST (1<<7) -#define USBCMD_CSS (1<<8) -#define USBCMD_CRS (1<<9) -#define USBCMD_EWE (1<<10) -#define USBCMD_EU3S (1<<11) - -#define USBSTS_HCH (1<<0) -#define USBSTS_HSE (1<<2) -#define USBSTS_EINT (1<<3) -#define USBSTS_PCD (1<<4) -#define USBSTS_SSS (1<<8) -#define USBSTS_RSS (1<<9) -#define USBSTS_SRE (1<<10) -#define USBSTS_CNR (1<<11) -#define USBSTS_HCE (1<<12) - - -#define PORTSC_CCS (1<<0) -#define PORTSC_PED (1<<1) -#define PORTSC_OCA (1<<3) -#define PORTSC_PR (1<<4) -#define PORTSC_PLS_SHIFT 5 -#define PORTSC_PLS_MASK 0xf -#define PORTSC_PP (1<<9) -#define PORTSC_SPEED_SHIFT 10 -#define PORTSC_SPEED_MASK 0xf -#define PORTSC_SPEED_FULL (1<<10) -#define PORTSC_SPEED_LOW (2<<10) -#define PORTSC_SPEED_HIGH (3<<10) -#define PORTSC_SPEED_SUPER (4<<10) -#define PORTSC_PIC_SHIFT 14 -#define PORTSC_PIC_MASK 0x3 -#define PORTSC_LWS (1<<16) -#define PORTSC_CSC (1<<17) -#define PORTSC_PEC (1<<18) -#define PORTSC_WRC (1<<19) -#define PORTSC_OCC (1<<20) -#define PORTSC_PRC (1<<21) -#define PORTSC_PLC (1<<22) -#define PORTSC_CEC (1<<23) -#define PORTSC_CAS (1<<24) -#define PORTSC_WCE (1<<25) -#define PORTSC_WDE (1<<26) -#define PORTSC_WOE (1<<27) -#define PORTSC_DR (1<<30) -#define PORTSC_WPR (1<<31) - -#define CRCR_RCS (1<<0) -#define CRCR_CS (1<<1) -#define CRCR_CA (1<<2) -#define CRCR_CRR (1<<3) - -#define IMAN_IP (1<<0) -#define IMAN_IE (1<<1) - -#define ERDP_EHB (1<<3) - -#define TRB_SIZE 16 -typedef struct XHCITRB { - uint64_t parameter; - uint32_t status; - uint32_t control; - dma_addr_t addr; - bool ccs; -} XHCITRB; - -enum { - PLS_U0 = 0, - PLS_U1 = 1, - PLS_U2 = 2, - PLS_U3 = 3, - PLS_DISABLED = 4, - PLS_RX_DETECT = 5, - PLS_INACTIVE = 6, - PLS_POLLING = 7, - PLS_RECOVERY = 8, - PLS_HOT_RESET = 9, - PLS_COMPILANCE_MODE = 10, - PLS_TEST_MODE = 11, - PLS_RESUME = 15, -}; - -typedef enum TRBType { - TRB_RESERVED = 0, - TR_NORMAL, - TR_SETUP, - TR_DATA, - TR_STATUS, - TR_ISOCH, - TR_LINK, - TR_EVDATA, - TR_NOOP, - CR_ENABLE_SLOT, - CR_DISABLE_SLOT, - CR_ADDRESS_DEVICE, - CR_CONFIGURE_ENDPOINT, - CR_EVALUATE_CONTEXT, - CR_RESET_ENDPOINT, - CR_STOP_ENDPOINT, - CR_SET_TR_DEQUEUE, - CR_RESET_DEVICE, - CR_FORCE_EVENT, - CR_NEGOTIATE_BW, - CR_SET_LATENCY_TOLERANCE, - CR_GET_PORT_BANDWIDTH, - CR_FORCE_HEADER, - CR_NOOP, - ER_TRANSFER = 32, - ER_COMMAND_COMPLETE, - ER_PORT_STATUS_CHANGE, - ER_BANDWIDTH_REQUEST, - ER_DOORBELL, - ER_HOST_CONTROLLER, - ER_DEVICE_NOTIFICATION, - ER_MFINDEX_WRAP, - /* vendor specific bits */ - CR_VENDOR_VIA_CHALLENGE_RESPONSE = 48, - CR_VENDOR_NEC_FIRMWARE_REVISION = 49, - CR_VENDOR_NEC_CHALLENGE_RESPONSE = 50, -} TRBType; - -#define CR_LINK TR_LINK - -typedef enum TRBCCode { - CC_INVALID = 0, - CC_SUCCESS, - CC_DATA_BUFFER_ERROR, - CC_BABBLE_DETECTED, - CC_USB_TRANSACTION_ERROR, - CC_TRB_ERROR, - CC_STALL_ERROR, - CC_RESOURCE_ERROR, - CC_BANDWIDTH_ERROR, - CC_NO_SLOTS_ERROR, - CC_INVALID_STREAM_TYPE_ERROR, - CC_SLOT_NOT_ENABLED_ERROR, - CC_EP_NOT_ENABLED_ERROR, - CC_SHORT_PACKET, - CC_RING_UNDERRUN, - CC_RING_OVERRUN, - CC_VF_ER_FULL, - CC_PARAMETER_ERROR, - CC_BANDWIDTH_OVERRUN, - CC_CONTEXT_STATE_ERROR, - CC_NO_PING_RESPONSE_ERROR, - CC_EVENT_RING_FULL_ERROR, - CC_INCOMPATIBLE_DEVICE_ERROR, - CC_MISSED_SERVICE_ERROR, - CC_COMMAND_RING_STOPPED, - CC_COMMAND_ABORTED, - CC_STOPPED, - CC_STOPPED_LENGTH_INVALID, - CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR = 29, - CC_ISOCH_BUFFER_OVERRUN = 31, - CC_EVENT_LOST_ERROR, - CC_UNDEFINED_ERROR, - CC_INVALID_STREAM_ID_ERROR, - CC_SECONDARY_BANDWIDTH_ERROR, - CC_SPLIT_TRANSACTION_ERROR -} TRBCCode; - -#define TRB_C (1<<0) -#define TRB_TYPE_SHIFT 10 -#define TRB_TYPE_MASK 0x3f -#define TRB_TYPE(t) (((t).control >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK) - -#define TRB_EV_ED (1<<2) - -#define TRB_TR_ENT (1<<1) -#define TRB_TR_ISP (1<<2) -#define TRB_TR_NS (1<<3) -#define TRB_TR_CH (1<<4) -#define TRB_TR_IOC (1<<5) -#define TRB_TR_IDT (1<<6) -#define TRB_TR_TBC_SHIFT 7 -#define TRB_TR_TBC_MASK 0x3 -#define TRB_TR_BEI (1<<9) -#define TRB_TR_TLBPC_SHIFT 16 -#define TRB_TR_TLBPC_MASK 0xf -#define TRB_TR_FRAMEID_SHIFT 20 -#define TRB_TR_FRAMEID_MASK 0x7ff -#define TRB_TR_SIA (1<<31) - -#define TRB_TR_DIR (1<<16) - -#define TRB_CR_SLOTID_SHIFT 24 -#define TRB_CR_SLOTID_MASK 0xff -#define TRB_CR_EPID_SHIFT 16 -#define TRB_CR_EPID_MASK 0x1f - -#define TRB_CR_BSR (1<<9) -#define TRB_CR_DC (1<<9) - -#define TRB_LK_TC (1<<1) - -#define TRB_INTR_SHIFT 22 -#define TRB_INTR_MASK 0x3ff -#define TRB_INTR(t) (((t).status >> TRB_INTR_SHIFT) & TRB_INTR_MASK) - -#define EP_TYPE_MASK 0x7 -#define EP_TYPE_SHIFT 3 - -#define EP_STATE_MASK 0x7 -#define EP_DISABLED (0<<0) -#define EP_RUNNING (1<<0) -#define EP_HALTED (2<<0) -#define EP_STOPPED (3<<0) -#define EP_ERROR (4<<0) - -#define SLOT_STATE_MASK 0x1f -#define SLOT_STATE_SHIFT 27 -#define SLOT_STATE(s) (((s)>>SLOT_STATE_SHIFT)&SLOT_STATE_MASK) -#define SLOT_ENABLED 0 -#define SLOT_DEFAULT 1 -#define SLOT_ADDRESSED 2 -#define SLOT_CONFIGURED 3 - -#define SLOT_CONTEXT_ENTRIES_MASK 0x1f -#define SLOT_CONTEXT_ENTRIES_SHIFT 27 - -typedef struct XHCIState XHCIState; -typedef struct XHCIStreamContext XHCIStreamContext; -typedef struct XHCIEPContext XHCIEPContext; - -#define get_field(data, field) \ - (((data) >> field##_SHIFT) & field##_MASK) - -#define set_field(data, newval, field) do { \ - uint32_t val = *data; \ - val &= ~(field##_MASK << field##_SHIFT); \ - val |= ((newval) & field##_MASK) << field##_SHIFT; \ - *data = val; \ - } while (0) - -typedef enum EPType { - ET_INVALID = 0, - ET_ISO_OUT, - ET_BULK_OUT, - ET_INTR_OUT, - ET_CONTROL, - ET_ISO_IN, - ET_BULK_IN, - ET_INTR_IN, -} EPType; - -typedef struct XHCIRing { - dma_addr_t dequeue; - bool ccs; -} XHCIRing; - -typedef struct XHCIPort { - XHCIState *xhci; - uint32_t portsc; - uint32_t portnr; - USBPort *uport; - uint32_t speedmask; - char name[16]; - MemoryRegion mem; -} XHCIPort; - -typedef struct XHCITransfer { - XHCIState *xhci; - USBPacket packet; - QEMUSGList sgl; - bool running_async; - bool running_retry; - bool complete; - bool int_req; - unsigned int iso_pkts; - unsigned int slotid; - unsigned int epid; - unsigned int streamid; - bool in_xfer; - bool iso_xfer; - bool timed_xfer; - - unsigned int trb_count; - unsigned int trb_alloced; - XHCITRB *trbs; - - TRBCCode status; - - unsigned int pkts; - unsigned int pktsize; - unsigned int cur_pkt; - - uint64_t mfindex_kick; -} XHCITransfer; - -struct XHCIStreamContext { - dma_addr_t pctx; - unsigned int sct; - XHCIRing ring; -}; - -struct XHCIEPContext { - XHCIState *xhci; - unsigned int slotid; - unsigned int epid; - - XHCIRing ring; - unsigned int next_xfer; - unsigned int comp_xfer; - XHCITransfer transfers[TD_QUEUE]; - XHCITransfer *retry; - EPType type; - dma_addr_t pctx; - unsigned int max_psize; - uint32_t state; - - /* streams */ - unsigned int max_pstreams; - bool lsa; - unsigned int nr_pstreams; - XHCIStreamContext *pstreams; - - /* iso xfer scheduling */ - unsigned int interval; - int64_t mfindex_last; - QEMUTimer *kick_timer; -}; - -typedef struct XHCISlot { - bool enabled; - bool addressed; - dma_addr_t ctx; - USBPort *uport; - XHCIEPContext * eps[31]; -} XHCISlot; - -typedef struct XHCIEvent { - TRBType type; - TRBCCode ccode; - uint64_t ptr; - uint32_t length; - uint32_t flags; - uint8_t slotid; - uint8_t epid; -} XHCIEvent; - -typedef struct XHCIInterrupter { - uint32_t iman; - uint32_t imod; - uint32_t erstsz; - uint32_t erstba_low; - uint32_t erstba_high; - uint32_t erdp_low; - uint32_t erdp_high; - - bool msix_used, er_pcs, er_full; - - dma_addr_t er_start; - uint32_t er_size; - unsigned int er_ep_idx; - - XHCIEvent ev_buffer[EV_QUEUE]; - unsigned int ev_buffer_put; - unsigned int ev_buffer_get; - -} XHCIInterrupter; - -struct XHCIState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - USBBus bus; - MemoryRegion mem; - MemoryRegion mem_cap; - MemoryRegion mem_oper; - MemoryRegion mem_runtime; - MemoryRegion mem_doorbell; - - /* properties */ - uint32_t numports_2; - uint32_t numports_3; - uint32_t numintrs; - uint32_t numslots; - uint32_t flags; - uint32_t max_pstreams_mask; - - /* Operational Registers */ - uint32_t usbcmd; - uint32_t usbsts; - uint32_t dnctrl; - uint32_t crcr_low; - uint32_t crcr_high; - uint32_t dcbaap_low; - uint32_t dcbaap_high; - uint32_t config; - - USBPort uports[MAX(MAXPORTS_2, MAXPORTS_3)]; - XHCIPort ports[MAXPORTS]; - XHCISlot slots[MAXSLOTS]; - uint32_t numports; - - /* Runtime Registers */ - int64_t mfindex_start; - QEMUTimer *mfwrap_timer; - XHCIInterrupter intr[MAXINTRS]; - - XHCIRing cmd_ring; -}; - -#define TYPE_XHCI "nec-usb-xhci" - -#define XHCI(obj) \ - OBJECT_CHECK(XHCIState, (obj), TYPE_XHCI) - -typedef struct XHCIEvRingSeg { - uint32_t addr_low; - uint32_t addr_high; - uint32_t size; - uint32_t rsvd; -} XHCIEvRingSeg; - -enum xhci_flags { - XHCI_FLAG_USE_MSI = 1, - XHCI_FLAG_USE_MSI_X, - XHCI_FLAG_SS_FIRST, - XHCI_FLAG_FORCE_PCIE_ENDCAP, - XHCI_FLAG_ENABLE_STREAMS, -}; - -static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, - unsigned int epid, unsigned int streamid); -static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, - unsigned int epid); -static void xhci_xfer_report(XHCITransfer *xfer); -static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v); -static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v); -static USBEndpoint *xhci_epid_to_usbep(XHCIState *xhci, - unsigned int slotid, unsigned int epid); - -static const char *TRBType_names[] = { - [TRB_RESERVED] = "TRB_RESERVED", - [TR_NORMAL] = "TR_NORMAL", - [TR_SETUP] = "TR_SETUP", - [TR_DATA] = "TR_DATA", - [TR_STATUS] = "TR_STATUS", - [TR_ISOCH] = "TR_ISOCH", - [TR_LINK] = "TR_LINK", - [TR_EVDATA] = "TR_EVDATA", - [TR_NOOP] = "TR_NOOP", - [CR_ENABLE_SLOT] = "CR_ENABLE_SLOT", - [CR_DISABLE_SLOT] = "CR_DISABLE_SLOT", - [CR_ADDRESS_DEVICE] = "CR_ADDRESS_DEVICE", - [CR_CONFIGURE_ENDPOINT] = "CR_CONFIGURE_ENDPOINT", - [CR_EVALUATE_CONTEXT] = "CR_EVALUATE_CONTEXT", - [CR_RESET_ENDPOINT] = "CR_RESET_ENDPOINT", - [CR_STOP_ENDPOINT] = "CR_STOP_ENDPOINT", - [CR_SET_TR_DEQUEUE] = "CR_SET_TR_DEQUEUE", - [CR_RESET_DEVICE] = "CR_RESET_DEVICE", - [CR_FORCE_EVENT] = "CR_FORCE_EVENT", - [CR_NEGOTIATE_BW] = "CR_NEGOTIATE_BW", - [CR_SET_LATENCY_TOLERANCE] = "CR_SET_LATENCY_TOLERANCE", - [CR_GET_PORT_BANDWIDTH] = "CR_GET_PORT_BANDWIDTH", - [CR_FORCE_HEADER] = "CR_FORCE_HEADER", - [CR_NOOP] = "CR_NOOP", - [ER_TRANSFER] = "ER_TRANSFER", - [ER_COMMAND_COMPLETE] = "ER_COMMAND_COMPLETE", - [ER_PORT_STATUS_CHANGE] = "ER_PORT_STATUS_CHANGE", - [ER_BANDWIDTH_REQUEST] = "ER_BANDWIDTH_REQUEST", - [ER_DOORBELL] = "ER_DOORBELL", - [ER_HOST_CONTROLLER] = "ER_HOST_CONTROLLER", - [ER_DEVICE_NOTIFICATION] = "ER_DEVICE_NOTIFICATION", - [ER_MFINDEX_WRAP] = "ER_MFINDEX_WRAP", - [CR_VENDOR_VIA_CHALLENGE_RESPONSE] = "CR_VENDOR_VIA_CHALLENGE_RESPONSE", - [CR_VENDOR_NEC_FIRMWARE_REVISION] = "CR_VENDOR_NEC_FIRMWARE_REVISION", - [CR_VENDOR_NEC_CHALLENGE_RESPONSE] = "CR_VENDOR_NEC_CHALLENGE_RESPONSE", -}; - -static const char *TRBCCode_names[] = { - [CC_INVALID] = "CC_INVALID", - [CC_SUCCESS] = "CC_SUCCESS", - [CC_DATA_BUFFER_ERROR] = "CC_DATA_BUFFER_ERROR", - [CC_BABBLE_DETECTED] = "CC_BABBLE_DETECTED", - [CC_USB_TRANSACTION_ERROR] = "CC_USB_TRANSACTION_ERROR", - [CC_TRB_ERROR] = "CC_TRB_ERROR", - [CC_STALL_ERROR] = "CC_STALL_ERROR", - [CC_RESOURCE_ERROR] = "CC_RESOURCE_ERROR", - [CC_BANDWIDTH_ERROR] = "CC_BANDWIDTH_ERROR", - [CC_NO_SLOTS_ERROR] = "CC_NO_SLOTS_ERROR", - [CC_INVALID_STREAM_TYPE_ERROR] = "CC_INVALID_STREAM_TYPE_ERROR", - [CC_SLOT_NOT_ENABLED_ERROR] = "CC_SLOT_NOT_ENABLED_ERROR", - [CC_EP_NOT_ENABLED_ERROR] = "CC_EP_NOT_ENABLED_ERROR", - [CC_SHORT_PACKET] = "CC_SHORT_PACKET", - [CC_RING_UNDERRUN] = "CC_RING_UNDERRUN", - [CC_RING_OVERRUN] = "CC_RING_OVERRUN", - [CC_VF_ER_FULL] = "CC_VF_ER_FULL", - [CC_PARAMETER_ERROR] = "CC_PARAMETER_ERROR", - [CC_BANDWIDTH_OVERRUN] = "CC_BANDWIDTH_OVERRUN", - [CC_CONTEXT_STATE_ERROR] = "CC_CONTEXT_STATE_ERROR", - [CC_NO_PING_RESPONSE_ERROR] = "CC_NO_PING_RESPONSE_ERROR", - [CC_EVENT_RING_FULL_ERROR] = "CC_EVENT_RING_FULL_ERROR", - [CC_INCOMPATIBLE_DEVICE_ERROR] = "CC_INCOMPATIBLE_DEVICE_ERROR", - [CC_MISSED_SERVICE_ERROR] = "CC_MISSED_SERVICE_ERROR", - [CC_COMMAND_RING_STOPPED] = "CC_COMMAND_RING_STOPPED", - [CC_COMMAND_ABORTED] = "CC_COMMAND_ABORTED", - [CC_STOPPED] = "CC_STOPPED", - [CC_STOPPED_LENGTH_INVALID] = "CC_STOPPED_LENGTH_INVALID", - [CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR] - = "CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR", - [CC_ISOCH_BUFFER_OVERRUN] = "CC_ISOCH_BUFFER_OVERRUN", - [CC_EVENT_LOST_ERROR] = "CC_EVENT_LOST_ERROR", - [CC_UNDEFINED_ERROR] = "CC_UNDEFINED_ERROR", - [CC_INVALID_STREAM_ID_ERROR] = "CC_INVALID_STREAM_ID_ERROR", - [CC_SECONDARY_BANDWIDTH_ERROR] = "CC_SECONDARY_BANDWIDTH_ERROR", - [CC_SPLIT_TRANSACTION_ERROR] = "CC_SPLIT_TRANSACTION_ERROR", -}; - -static const char *ep_state_names[] = { - [EP_DISABLED] = "disabled", - [EP_RUNNING] = "running", - [EP_HALTED] = "halted", - [EP_STOPPED] = "stopped", - [EP_ERROR] = "error", -}; - -static const char *lookup_name(uint32_t index, const char **list, uint32_t llen) -{ - if (index >= llen || list[index] == NULL) { - return "???"; - } - return list[index]; -} - -static const char *trb_name(XHCITRB *trb) -{ - return lookup_name(TRB_TYPE(*trb), TRBType_names, - ARRAY_SIZE(TRBType_names)); -} - -static const char *event_name(XHCIEvent *event) -{ - return lookup_name(event->ccode, TRBCCode_names, - ARRAY_SIZE(TRBCCode_names)); -} - -static const char *ep_state_name(uint32_t state) -{ - return lookup_name(state, ep_state_names, - ARRAY_SIZE(ep_state_names)); -} - -static bool xhci_get_flag(XHCIState *xhci, enum xhci_flags bit) -{ - return xhci->flags & (1 << bit); -} - -static uint64_t xhci_mfindex_get(XHCIState *xhci) -{ - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - return (now - xhci->mfindex_start) / 125000; -} - -static void xhci_mfwrap_update(XHCIState *xhci) -{ - const uint32_t bits = USBCMD_RS | USBCMD_EWE; - uint32_t mfindex, left; - int64_t now; - - if ((xhci->usbcmd & bits) == bits) { - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - mfindex = ((now - xhci->mfindex_start) / 125000) & 0x3fff; - left = 0x4000 - mfindex; - timer_mod(xhci->mfwrap_timer, now + left * 125000); - } else { - timer_del(xhci->mfwrap_timer); - } -} - -static void xhci_mfwrap_timer(void *opaque) -{ - XHCIState *xhci = opaque; - XHCIEvent wrap = { ER_MFINDEX_WRAP, CC_SUCCESS }; - - xhci_event(xhci, &wrap, 0); - xhci_mfwrap_update(xhci); -} - -static inline dma_addr_t xhci_addr64(uint32_t low, uint32_t high) -{ - if (sizeof(dma_addr_t) == 4) { - return low; - } else { - return low | (((dma_addr_t)high << 16) << 16); - } -} - -static inline dma_addr_t xhci_mask64(uint64_t addr) -{ - if (sizeof(dma_addr_t) == 4) { - return addr & 0xffffffff; - } else { - return addr; - } -} - -static inline void xhci_dma_read_u32s(XHCIState *xhci, dma_addr_t addr, - uint32_t *buf, size_t len) -{ - int i; - - assert((len % sizeof(uint32_t)) == 0); - - pci_dma_read(PCI_DEVICE(xhci), addr, buf, len); - - for (i = 0; i < (len / sizeof(uint32_t)); i++) { - buf[i] = le32_to_cpu(buf[i]); - } -} - -static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr, - uint32_t *buf, size_t len) -{ - int i; - 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 < n; i++) { - tmp[i] = cpu_to_le32(buf[i]); - } - pci_dma_write(PCI_DEVICE(xhci), addr, tmp, len); -} - -static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport) -{ - int index; - - if (!uport->dev) { - return NULL; - } - switch (uport->dev->speed) { - case USB_SPEED_LOW: - case USB_SPEED_FULL: - case USB_SPEED_HIGH: - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - index = uport->index + xhci->numports_3; - } else { - index = uport->index; - } - break; - case USB_SPEED_SUPER: - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - index = uport->index; - } else { - index = uport->index + xhci->numports_2; - } - break; - default: - return NULL; - } - return &xhci->ports[index]; -} - -static void xhci_intx_update(XHCIState *xhci) -{ - PCIDevice *pci_dev = PCI_DEVICE(xhci); - int level = 0; - - if (msix_enabled(pci_dev) || - msi_enabled(pci_dev)) { - return; - } - - if (xhci->intr[0].iman & IMAN_IP && - xhci->intr[0].iman & IMAN_IE && - xhci->usbcmd & USBCMD_INTE) { - level = 1; - } - - trace_usb_xhci_irq_intx(level); - pci_set_irq(pci_dev, level); -} - -static void xhci_msix_update(XHCIState *xhci, int v) -{ - PCIDevice *pci_dev = PCI_DEVICE(xhci); - bool enabled; - - if (!msix_enabled(pci_dev)) { - return; - } - - enabled = xhci->intr[v].iman & IMAN_IE; - if (enabled == xhci->intr[v].msix_used) { - return; - } - - if (enabled) { - trace_usb_xhci_irq_msix_use(v); - msix_vector_use(pci_dev, v); - xhci->intr[v].msix_used = true; - } else { - trace_usb_xhci_irq_msix_unuse(v); - msix_vector_unuse(pci_dev, v); - xhci->intr[v].msix_used = false; - } -} - -static void xhci_intr_raise(XHCIState *xhci, int v) -{ - PCIDevice *pci_dev = PCI_DEVICE(xhci); - - xhci->intr[v].erdp_low |= ERDP_EHB; - xhci->intr[v].iman |= IMAN_IP; - xhci->usbsts |= USBSTS_EINT; - - if (!(xhci->intr[v].iman & IMAN_IE)) { - return; - } - - if (!(xhci->usbcmd & USBCMD_INTE)) { - return; - } - - if (msix_enabled(pci_dev)) { - trace_usb_xhci_irq_msix(v); - msix_notify(pci_dev, v); - return; - } - - if (msi_enabled(pci_dev)) { - trace_usb_xhci_irq_msi(v); - msi_notify(pci_dev, v); - return; - } - - if (v == 0) { - trace_usb_xhci_irq_intx(1); - pci_irq_assert(pci_dev); - } -} - -static inline int xhci_running(XHCIState *xhci) -{ - return !(xhci->usbsts & USBSTS_HCH) && !xhci->intr[0].er_full; -} - -static void xhci_die(XHCIState *xhci) -{ - xhci->usbsts |= USBSTS_HCE; - DPRINTF("xhci: asserted controller error\n"); -} - -static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v) -{ - PCIDevice *pci_dev = PCI_DEVICE(xhci); - XHCIInterrupter *intr = &xhci->intr[v]; - XHCITRB ev_trb; - dma_addr_t addr; - - ev_trb.parameter = cpu_to_le64(event->ptr); - ev_trb.status = cpu_to_le32(event->length | (event->ccode << 24)); - ev_trb.control = (event->slotid << 24) | (event->epid << 16) | - event->flags | (event->type << TRB_TYPE_SHIFT); - if (intr->er_pcs) { - ev_trb.control |= TRB_C; - } - ev_trb.control = cpu_to_le32(ev_trb.control); - - trace_usb_xhci_queue_event(v, intr->er_ep_idx, trb_name(&ev_trb), - event_name(event), ev_trb.parameter, - ev_trb.status, ev_trb.control); - - addr = intr->er_start + TRB_SIZE*intr->er_ep_idx; - pci_dma_write(pci_dev, addr, &ev_trb, TRB_SIZE); - - intr->er_ep_idx++; - if (intr->er_ep_idx >= intr->er_size) { - intr->er_ep_idx = 0; - intr->er_pcs = !intr->er_pcs; - } -} - -static void xhci_events_update(XHCIState *xhci, int v) -{ - XHCIInterrupter *intr = &xhci->intr[v]; - dma_addr_t erdp; - unsigned int dp_idx; - bool do_irq = 0; - - if (xhci->usbsts & USBSTS_HCH) { - return; - } - - erdp = xhci_addr64(intr->erdp_low, intr->erdp_high); - if (erdp < intr->er_start || - erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) { - DPRINTF("xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp); - DPRINTF("xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n", - v, intr->er_start, intr->er_size); - xhci_die(xhci); - return; - } - dp_idx = (erdp - intr->er_start) / TRB_SIZE; - assert(dp_idx < intr->er_size); - - /* NEC didn't read section 4.9.4 of the spec (v1.0 p139 top Note) and thus - * deadlocks when the ER is full. Hack it by holding off events until - * the driver decides to free at least half of the ring */ - if (intr->er_full) { - int er_free = dp_idx - intr->er_ep_idx; - if (er_free <= 0) { - er_free += intr->er_size; - } - if (er_free < (intr->er_size/2)) { - DPRINTF("xhci_events_update(): event ring still " - "more than half full (hack)\n"); - return; - } - } - - while (intr->ev_buffer_put != intr->ev_buffer_get) { - assert(intr->er_full); - if (((intr->er_ep_idx+1) % intr->er_size) == dp_idx) { - DPRINTF("xhci_events_update(): event ring full again\n"); -#ifndef ER_FULL_HACK - XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR}; - xhci_write_event(xhci, &full, v); -#endif - do_irq = 1; - break; - } - XHCIEvent *event = &intr->ev_buffer[intr->ev_buffer_get]; - xhci_write_event(xhci, event, v); - intr->ev_buffer_get++; - do_irq = 1; - if (intr->ev_buffer_get == EV_QUEUE) { - intr->ev_buffer_get = 0; - } - } - - if (do_irq) { - xhci_intr_raise(xhci, v); - } - - if (intr->er_full && intr->ev_buffer_put == intr->ev_buffer_get) { - DPRINTF("xhci_events_update(): event ring no longer full\n"); - intr->er_full = 0; - } -} - -static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v) -{ - XHCIInterrupter *intr; - dma_addr_t erdp; - unsigned int dp_idx; - - if (v >= xhci->numintrs) { - DPRINTF("intr nr out of range (%d >= %d)\n", v, xhci->numintrs); - return; - } - intr = &xhci->intr[v]; - - if (intr->er_full) { - DPRINTF("xhci_event(): ER full, queueing\n"); - if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) { - DPRINTF("xhci: event queue full, dropping event!\n"); - return; - } - intr->ev_buffer[intr->ev_buffer_put++] = *event; - if (intr->ev_buffer_put == EV_QUEUE) { - intr->ev_buffer_put = 0; - } - return; - } - - erdp = xhci_addr64(intr->erdp_low, intr->erdp_high); - if (erdp < intr->er_start || - erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) { - DPRINTF("xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp); - DPRINTF("xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n", - v, intr->er_start, intr->er_size); - xhci_die(xhci); - return; - } - - dp_idx = (erdp - intr->er_start) / TRB_SIZE; - assert(dp_idx < intr->er_size); - - if ((intr->er_ep_idx+1) % intr->er_size == dp_idx) { - DPRINTF("xhci_event(): ER full, queueing\n"); -#ifndef ER_FULL_HACK - XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR}; - xhci_write_event(xhci, &full); -#endif - intr->er_full = 1; - if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) { - DPRINTF("xhci: event queue full, dropping event!\n"); - return; - } - intr->ev_buffer[intr->ev_buffer_put++] = *event; - if (intr->ev_buffer_put == EV_QUEUE) { - intr->ev_buffer_put = 0; - } - } else { - xhci_write_event(xhci, event, v); - } - - xhci_intr_raise(xhci, v); -} - -static void xhci_ring_init(XHCIState *xhci, XHCIRing *ring, - dma_addr_t base) -{ - ring->dequeue = base; - ring->ccs = 1; -} - -static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb, - dma_addr_t *addr) -{ - PCIDevice *pci_dev = PCI_DEVICE(xhci); - - while (1) { - TRBType type; - pci_dma_read(pci_dev, ring->dequeue, trb, TRB_SIZE); - trb->addr = ring->dequeue; - trb->ccs = ring->ccs; - le64_to_cpus(&trb->parameter); - le32_to_cpus(&trb->status); - le32_to_cpus(&trb->control); - - trace_usb_xhci_fetch_trb(ring->dequeue, trb_name(trb), - trb->parameter, trb->status, trb->control); - - if ((trb->control & TRB_C) != ring->ccs) { - return 0; - } - - type = TRB_TYPE(*trb); - - if (type != TR_LINK) { - if (addr) { - *addr = ring->dequeue; - } - ring->dequeue += TRB_SIZE; - return type; - } else { - ring->dequeue = xhci_mask64(trb->parameter); - if (trb->control & TRB_LK_TC) { - ring->ccs = !ring->ccs; - } - } - } -} - -static int xhci_ring_chain_length(XHCIState *xhci, const XHCIRing *ring) -{ - PCIDevice *pci_dev = PCI_DEVICE(xhci); - XHCITRB trb; - int length = 0; - dma_addr_t dequeue = ring->dequeue; - bool ccs = ring->ccs; - /* hack to bundle together the two/three TDs that make a setup transfer */ - bool control_td_set = 0; - - while (1) { - TRBType type; - pci_dma_read(pci_dev, dequeue, &trb, TRB_SIZE); - le64_to_cpus(&trb.parameter); - le32_to_cpus(&trb.status); - le32_to_cpus(&trb.control); - - if ((trb.control & TRB_C) != ccs) { - return -length; - } - - type = TRB_TYPE(trb); - - if (type == TR_LINK) { - dequeue = xhci_mask64(trb.parameter); - if (trb.control & TRB_LK_TC) { - ccs = !ccs; - } - continue; - } - - length += 1; - dequeue += TRB_SIZE; - - if (type == TR_SETUP) { - control_td_set = 1; - } else if (type == TR_STATUS) { - control_td_set = 0; - } - - if (!control_td_set && !(trb.control & TRB_TR_CH)) { - return length; - } - } -} - -static void xhci_er_reset(XHCIState *xhci, int v) -{ - XHCIInterrupter *intr = &xhci->intr[v]; - XHCIEvRingSeg seg; - - if (intr->erstsz == 0) { - /* disabled */ - intr->er_start = 0; - intr->er_size = 0; - return; - } - /* cache the (sole) event ring segment location */ - if (intr->erstsz != 1) { - DPRINTF("xhci: invalid value for ERSTSZ: %d\n", intr->erstsz); - xhci_die(xhci); - return; - } - dma_addr_t erstba = xhci_addr64(intr->erstba_low, intr->erstba_high); - pci_dma_read(PCI_DEVICE(xhci), erstba, &seg, sizeof(seg)); - le32_to_cpus(&seg.addr_low); - le32_to_cpus(&seg.addr_high); - le32_to_cpus(&seg.size); - if (seg.size < 16 || seg.size > 4096) { - DPRINTF("xhci: invalid value for segment size: %d\n", seg.size); - xhci_die(xhci); - return; - } - intr->er_start = xhci_addr64(seg.addr_low, seg.addr_high); - intr->er_size = seg.size; - - intr->er_ep_idx = 0; - intr->er_pcs = 1; - intr->er_full = 0; - - DPRINTF("xhci: event ring[%d]:" DMA_ADDR_FMT " [%d]\n", - v, intr->er_start, intr->er_size); -} - -static void xhci_run(XHCIState *xhci) -{ - trace_usb_xhci_run(); - xhci->usbsts &= ~USBSTS_HCH; - xhci->mfindex_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); -} - -static void xhci_stop(XHCIState *xhci) -{ - trace_usb_xhci_stop(); - xhci->usbsts |= USBSTS_HCH; - xhci->crcr_low &= ~CRCR_CRR; -} - -static XHCIStreamContext *xhci_alloc_stream_contexts(unsigned count, - dma_addr_t base) -{ - XHCIStreamContext *stctx; - unsigned int i; - - stctx = g_new0(XHCIStreamContext, count); - for (i = 0; i < count; i++) { - stctx[i].pctx = base + i * 16; - stctx[i].sct = -1; - } - return stctx; -} - -static void xhci_reset_streams(XHCIEPContext *epctx) -{ - unsigned int i; - - for (i = 0; i < epctx->nr_pstreams; i++) { - epctx->pstreams[i].sct = -1; - } -} - -static void xhci_alloc_streams(XHCIEPContext *epctx, dma_addr_t base) -{ - assert(epctx->pstreams == NULL); - epctx->nr_pstreams = 2 << epctx->max_pstreams; - epctx->pstreams = xhci_alloc_stream_contexts(epctx->nr_pstreams, base); -} - -static void xhci_free_streams(XHCIEPContext *epctx) -{ - assert(epctx->pstreams != NULL); - - g_free(epctx->pstreams); - epctx->pstreams = NULL; - epctx->nr_pstreams = 0; -} - -static int xhci_epmask_to_eps_with_streams(XHCIState *xhci, - unsigned int slotid, - uint32_t epmask, - XHCIEPContext **epctxs, - USBEndpoint **eps) -{ - XHCISlot *slot; - XHCIEPContext *epctx; - USBEndpoint *ep; - int i, j; - - assert(slotid >= 1 && slotid <= xhci->numslots); - - slot = &xhci->slots[slotid - 1]; - - for (i = 2, j = 0; i <= 31; i++) { - if (!(epmask & (1u << i))) { - continue; - } - - epctx = slot->eps[i - 1]; - ep = xhci_epid_to_usbep(xhci, slotid, i); - if (!epctx || !epctx->nr_pstreams || !ep) { - continue; - } - - if (epctxs) { - epctxs[j] = epctx; - } - eps[j++] = ep; - } - return j; -} - -static void xhci_free_device_streams(XHCIState *xhci, unsigned int slotid, - uint32_t epmask) -{ - USBEndpoint *eps[30]; - int nr_eps; - - nr_eps = xhci_epmask_to_eps_with_streams(xhci, slotid, epmask, NULL, eps); - if (nr_eps) { - usb_device_free_streams(eps[0]->dev, eps, nr_eps); - } -} - -static TRBCCode xhci_alloc_device_streams(XHCIState *xhci, unsigned int slotid, - uint32_t epmask) -{ - XHCIEPContext *epctxs[30]; - USBEndpoint *eps[30]; - int i, r, nr_eps, req_nr_streams, dev_max_streams; - - nr_eps = xhci_epmask_to_eps_with_streams(xhci, slotid, epmask, epctxs, - eps); - if (nr_eps == 0) { - return CC_SUCCESS; - } - - req_nr_streams = epctxs[0]->nr_pstreams; - dev_max_streams = eps[0]->max_streams; - - for (i = 1; i < nr_eps; i++) { - /* - * HdG: I don't expect these to ever trigger, but if they do we need - * to come up with another solution, ie group identical endpoints - * together and make an usb_device_alloc_streams call per group. - */ - if (epctxs[i]->nr_pstreams != req_nr_streams) { - FIXME("guest streams config not identical for all eps"); - return CC_RESOURCE_ERROR; - } - if (eps[i]->max_streams != dev_max_streams) { - FIXME("device streams config not identical for all eps"); - return CC_RESOURCE_ERROR; - } - } - - /* - * max-streams in both the device descriptor and in the controller is a - * power of 2. But stream id 0 is reserved, so if a device can do up to 4 - * streams the guest will ask for 5 rounded up to the next power of 2 which - * becomes 8. For emulated devices usb_device_alloc_streams is a nop. - * - * For redirected devices however this is an issue, as there we must ask - * the real xhci controller to alloc streams, and the host driver for the - * real xhci controller will likely disallow allocating more streams then - * the device can handle. - * - * So we limit the requested nr_streams to the maximum number the device - * can handle. - */ - if (req_nr_streams > dev_max_streams) { - req_nr_streams = dev_max_streams; - } - - r = usb_device_alloc_streams(eps[0]->dev, eps, nr_eps, req_nr_streams); - if (r != 0) { - DPRINTF("xhci: alloc streams failed\n"); - return CC_RESOURCE_ERROR; - } - - return CC_SUCCESS; -} - -static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx, - unsigned int streamid, - uint32_t *cc_error) -{ - XHCIStreamContext *sctx; - dma_addr_t base; - uint32_t ctx[2], sct; - - assert(streamid != 0); - if (epctx->lsa) { - if (streamid >= epctx->nr_pstreams) { - *cc_error = CC_INVALID_STREAM_ID_ERROR; - return NULL; - } - sctx = epctx->pstreams + streamid; - } else { - FIXME("secondary streams not implemented yet"); - } - - if (sctx->sct == -1) { - xhci_dma_read_u32s(epctx->xhci, sctx->pctx, ctx, sizeof(ctx)); - sct = (ctx[0] >> 1) & 0x07; - if (epctx->lsa && sct != 1) { - *cc_error = CC_INVALID_STREAM_TYPE_ERROR; - return NULL; - } - sctx->sct = sct; - base = xhci_addr64(ctx[0] & ~0xf, ctx[1]); - xhci_ring_init(epctx->xhci, &sctx->ring, base); - } - return sctx; -} - -static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx, - XHCIStreamContext *sctx, uint32_t state) -{ - XHCIRing *ring = NULL; - uint32_t ctx[5]; - uint32_t ctx2[2]; - - xhci_dma_read_u32s(xhci, epctx->pctx, ctx, sizeof(ctx)); - ctx[0] &= ~EP_STATE_MASK; - ctx[0] |= state; - - /* update ring dequeue ptr */ - if (epctx->nr_pstreams) { - if (sctx != NULL) { - ring = &sctx->ring; - xhci_dma_read_u32s(xhci, sctx->pctx, ctx2, sizeof(ctx2)); - ctx2[0] &= 0xe; - ctx2[0] |= sctx->ring.dequeue | sctx->ring.ccs; - ctx2[1] = (sctx->ring.dequeue >> 16) >> 16; - xhci_dma_write_u32s(xhci, sctx->pctx, ctx2, sizeof(ctx2)); - } - } else { - ring = &epctx->ring; - } - if (ring) { - ctx[2] = ring->dequeue | ring->ccs; - ctx[3] = (ring->dequeue >> 16) >> 16; - - DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n", - epctx->pctx, state, ctx[3], ctx[2]); - } - - xhci_dma_write_u32s(xhci, epctx->pctx, ctx, sizeof(ctx)); - if (epctx->state != state) { - trace_usb_xhci_ep_state(epctx->slotid, epctx->epid, - ep_state_name(epctx->state), - ep_state_name(state)); - } - epctx->state = state; -} - -static void xhci_ep_kick_timer(void *opaque) -{ - XHCIEPContext *epctx = opaque; - xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid, 0); -} - -static XHCIEPContext *xhci_alloc_epctx(XHCIState *xhci, - unsigned int slotid, - unsigned int epid) -{ - XHCIEPContext *epctx; - int i; - - epctx = g_new0(XHCIEPContext, 1); - epctx->xhci = xhci; - epctx->slotid = slotid; - epctx->epid = epid; - - for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) { - epctx->transfers[i].xhci = xhci; - epctx->transfers[i].slotid = slotid; - epctx->transfers[i].epid = epid; - usb_packet_init(&epctx->transfers[i].packet); - } - epctx->kick_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_ep_kick_timer, epctx); - - return epctx; -} - -static void xhci_init_epctx(XHCIEPContext *epctx, - dma_addr_t pctx, uint32_t *ctx) -{ - dma_addr_t dequeue; - - dequeue = xhci_addr64(ctx[2] & ~0xf, ctx[3]); - - epctx->type = (ctx[1] >> EP_TYPE_SHIFT) & EP_TYPE_MASK; - epctx->pctx = pctx; - epctx->max_psize = ctx[1]>>16; - epctx->max_psize *= 1+((ctx[1]>>8)&0xff); - epctx->max_pstreams = (ctx[0] >> 10) & epctx->xhci->max_pstreams_mask; - epctx->lsa = (ctx[0] >> 15) & 1; - if (epctx->max_pstreams) { - xhci_alloc_streams(epctx, dequeue); - } else { - xhci_ring_init(epctx->xhci, &epctx->ring, dequeue); - epctx->ring.ccs = ctx[2] & 1; - } - - epctx->interval = 1 << ((ctx[0] >> 16) & 0xff); -} - -static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, - unsigned int epid, dma_addr_t pctx, - uint32_t *ctx) -{ - XHCISlot *slot; - XHCIEPContext *epctx; - - trace_usb_xhci_ep_enable(slotid, epid); - assert(slotid >= 1 && slotid <= xhci->numslots); - assert(epid >= 1 && epid <= 31); - - slot = &xhci->slots[slotid-1]; - if (slot->eps[epid-1]) { - xhci_disable_ep(xhci, slotid, epid); - } - - epctx = xhci_alloc_epctx(xhci, slotid, epid); - slot->eps[epid-1] = epctx; - xhci_init_epctx(epctx, pctx, ctx); - - DPRINTF("xhci: endpoint %d.%d type is %d, max transaction (burst) " - "size is %d\n", epid/2, epid%2, epctx->type, epctx->max_psize); - - epctx->mfindex_last = 0; - - epctx->state = EP_RUNNING; - ctx[0] &= ~EP_STATE_MASK; - ctx[0] |= EP_RUNNING; - - return CC_SUCCESS; -} - -static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report) -{ - int killed = 0; - - if (report && (t->running_async || t->running_retry)) { - t->status = report; - xhci_xfer_report(t); - } - - if (t->running_async) { - usb_cancel_packet(&t->packet); - t->running_async = 0; - killed = 1; - } - if (t->running_retry) { - XHCIEPContext *epctx = t->xhci->slots[t->slotid-1].eps[t->epid-1]; - if (epctx) { - epctx->retry = NULL; - timer_del(epctx->kick_timer); - } - t->running_retry = 0; - killed = 1; - } - g_free(t->trbs); - - t->trbs = NULL; - t->trb_count = t->trb_alloced = 0; - - return killed; -} - -static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, - unsigned int epid, TRBCCode report) -{ - XHCISlot *slot; - XHCIEPContext *epctx; - int i, xferi, killed = 0; - USBEndpoint *ep = NULL; - assert(slotid >= 1 && slotid <= xhci->numslots); - assert(epid >= 1 && epid <= 31); - - DPRINTF("xhci_ep_nuke_xfers(%d, %d)\n", slotid, epid); - - slot = &xhci->slots[slotid-1]; - - if (!slot->eps[epid-1]) { - return 0; - } - - epctx = slot->eps[epid-1]; - - xferi = epctx->next_xfer; - for (i = 0; i < TD_QUEUE; i++) { - killed += xhci_ep_nuke_one_xfer(&epctx->transfers[xferi], report); - if (killed) { - report = 0; /* Only report once */ - } - epctx->transfers[xferi].packet.ep = NULL; - xferi = (xferi + 1) % TD_QUEUE; - } - - ep = xhci_epid_to_usbep(xhci, slotid, epid); - if (ep) { - usb_device_ep_stopped(ep->dev, ep); - } - return killed; -} - -static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, - unsigned int epid) -{ - XHCISlot *slot; - XHCIEPContext *epctx; - int i; - - trace_usb_xhci_ep_disable(slotid, epid); - assert(slotid >= 1 && slotid <= xhci->numslots); - assert(epid >= 1 && epid <= 31); - - slot = &xhci->slots[slotid-1]; - - if (!slot->eps[epid-1]) { - DPRINTF("xhci: slot %d ep %d already disabled\n", slotid, epid); - return CC_SUCCESS; - } - - xhci_ep_nuke_xfers(xhci, slotid, epid, 0); - - epctx = slot->eps[epid-1]; - - if (epctx->nr_pstreams) { - xhci_free_streams(epctx); - } - - for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) { - usb_packet_cleanup(&epctx->transfers[i].packet); - } - - xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED); - - timer_free(epctx->kick_timer); - g_free(epctx); - slot->eps[epid-1] = NULL; - - return CC_SUCCESS; -} - -static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid, - unsigned int epid) -{ - XHCISlot *slot; - XHCIEPContext *epctx; - - trace_usb_xhci_ep_stop(slotid, epid); - assert(slotid >= 1 && slotid <= xhci->numslots); - - if (epid < 1 || epid > 31) { - DPRINTF("xhci: bad ep %d\n", epid); - return CC_TRB_ERROR; - } - - slot = &xhci->slots[slotid-1]; - - if (!slot->eps[epid-1]) { - DPRINTF("xhci: slot %d ep %d not enabled\n", slotid, epid); - return CC_EP_NOT_ENABLED_ERROR; - } - - if (xhci_ep_nuke_xfers(xhci, slotid, epid, CC_STOPPED) > 0) { - DPRINTF("xhci: FIXME: endpoint stopped w/ xfers running, " - "data might be lost\n"); - } - - epctx = slot->eps[epid-1]; - - xhci_set_ep_state(xhci, epctx, NULL, EP_STOPPED); - - if (epctx->nr_pstreams) { - xhci_reset_streams(epctx); - } - - return CC_SUCCESS; -} - -static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid, - unsigned int epid) -{ - XHCISlot *slot; - XHCIEPContext *epctx; - - trace_usb_xhci_ep_reset(slotid, epid); - assert(slotid >= 1 && slotid <= xhci->numslots); - - if (epid < 1 || epid > 31) { - DPRINTF("xhci: bad ep %d\n", epid); - return CC_TRB_ERROR; - } - - slot = &xhci->slots[slotid-1]; - - if (!slot->eps[epid-1]) { - DPRINTF("xhci: slot %d ep %d not enabled\n", slotid, epid); - return CC_EP_NOT_ENABLED_ERROR; - } - - epctx = slot->eps[epid-1]; - - if (epctx->state != EP_HALTED) { - DPRINTF("xhci: reset EP while EP %d not halted (%d)\n", - epid, epctx->state); - return CC_CONTEXT_STATE_ERROR; - } - - if (xhci_ep_nuke_xfers(xhci, slotid, epid, 0) > 0) { - DPRINTF("xhci: FIXME: endpoint reset w/ xfers running, " - "data might be lost\n"); - } - - if (!xhci->slots[slotid-1].uport || - !xhci->slots[slotid-1].uport->dev || - !xhci->slots[slotid-1].uport->dev->attached) { - return CC_USB_TRANSACTION_ERROR; - } - - xhci_set_ep_state(xhci, epctx, NULL, EP_STOPPED); - - if (epctx->nr_pstreams) { - xhci_reset_streams(epctx); - } - - return CC_SUCCESS; -} - -static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid, - unsigned int epid, unsigned int streamid, - uint64_t pdequeue) -{ - XHCISlot *slot; - XHCIEPContext *epctx; - XHCIStreamContext *sctx; - dma_addr_t dequeue; - - assert(slotid >= 1 && slotid <= xhci->numslots); - - if (epid < 1 || epid > 31) { - DPRINTF("xhci: bad ep %d\n", epid); - return CC_TRB_ERROR; - } - - trace_usb_xhci_ep_set_dequeue(slotid, epid, streamid, pdequeue); - dequeue = xhci_mask64(pdequeue); - - slot = &xhci->slots[slotid-1]; - - if (!slot->eps[epid-1]) { - DPRINTF("xhci: slot %d ep %d not enabled\n", slotid, epid); - return CC_EP_NOT_ENABLED_ERROR; - } - - epctx = slot->eps[epid-1]; - - if (epctx->state != EP_STOPPED) { - DPRINTF("xhci: set EP dequeue pointer while EP %d not stopped\n", epid); - return CC_CONTEXT_STATE_ERROR; - } - - if (epctx->nr_pstreams) { - uint32_t err; - sctx = xhci_find_stream(epctx, streamid, &err); - if (sctx == NULL) { - return err; - } - xhci_ring_init(xhci, &sctx->ring, dequeue & ~0xf); - sctx->ring.ccs = dequeue & 1; - } else { - sctx = NULL; - xhci_ring_init(xhci, &epctx->ring, dequeue & ~0xF); - epctx->ring.ccs = dequeue & 1; - } - - xhci_set_ep_state(xhci, epctx, sctx, EP_STOPPED); - - return CC_SUCCESS; -} - -static int xhci_xfer_create_sgl(XHCITransfer *xfer, int in_xfer) -{ - XHCIState *xhci = xfer->xhci; - int i; - - xfer->int_req = false; - pci_dma_sglist_init(&xfer->sgl, PCI_DEVICE(xhci), xfer->trb_count); - for (i = 0; i < xfer->trb_count; i++) { - XHCITRB *trb = &xfer->trbs[i]; - dma_addr_t addr; - unsigned int chunk = 0; - - if (trb->control & TRB_TR_IOC) { - xfer->int_req = true; - } - - switch (TRB_TYPE(*trb)) { - case TR_DATA: - if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) { - DPRINTF("xhci: data direction mismatch for TR_DATA\n"); - goto err; - } - /* fallthrough */ - case TR_NORMAL: - case TR_ISOCH: - addr = xhci_mask64(trb->parameter); - chunk = trb->status & 0x1ffff; - if (trb->control & TRB_TR_IDT) { - if (chunk > 8 || in_xfer) { - DPRINTF("xhci: invalid immediate data TRB\n"); - goto err; - } - qemu_sglist_add(&xfer->sgl, trb->addr, chunk); - } else { - qemu_sglist_add(&xfer->sgl, addr, chunk); - } - break; - } - } - - return 0; - -err: - qemu_sglist_destroy(&xfer->sgl); - xhci_die(xhci); - return -1; -} - -static void xhci_xfer_unmap(XHCITransfer *xfer) -{ - usb_packet_unmap(&xfer->packet, &xfer->sgl); - qemu_sglist_destroy(&xfer->sgl); -} - -static void xhci_xfer_report(XHCITransfer *xfer) -{ - uint32_t edtla = 0; - unsigned int left; - bool reported = 0; - bool shortpkt = 0; - XHCIEvent event = {ER_TRANSFER, CC_SUCCESS}; - XHCIState *xhci = xfer->xhci; - int i; - - left = xfer->packet.actual_length; - - for (i = 0; i < xfer->trb_count; i++) { - XHCITRB *trb = &xfer->trbs[i]; - unsigned int chunk = 0; - - switch (TRB_TYPE(*trb)) { - case TR_DATA: - case TR_NORMAL: - case TR_ISOCH: - chunk = trb->status & 0x1ffff; - if (chunk > left) { - chunk = left; - if (xfer->status == CC_SUCCESS) { - shortpkt = 1; - } - } - left -= chunk; - edtla += chunk; - break; - case TR_STATUS: - reported = 0; - shortpkt = 0; - break; - } - - if (!reported && ((trb->control & TRB_TR_IOC) || - (shortpkt && (trb->control & TRB_TR_ISP)) || - (xfer->status != CC_SUCCESS && left == 0))) { - event.slotid = xfer->slotid; - event.epid = xfer->epid; - event.length = (trb->status & 0x1ffff) - chunk; - event.flags = 0; - event.ptr = trb->addr; - if (xfer->status == CC_SUCCESS) { - event.ccode = shortpkt ? CC_SHORT_PACKET : CC_SUCCESS; - } else { - event.ccode = xfer->status; - } - if (TRB_TYPE(*trb) == TR_EVDATA) { - event.ptr = trb->parameter; - event.flags |= TRB_EV_ED; - event.length = edtla & 0xffffff; - DPRINTF("xhci_xfer_data: EDTLA=%d\n", event.length); - edtla = 0; - } - xhci_event(xhci, &event, TRB_INTR(*trb)); - reported = 1; - if (xfer->status != CC_SUCCESS) { - return; - } - } - - switch (TRB_TYPE(*trb)) { - case TR_SETUP: - reported = 0; - shortpkt = 0; - break; - } - - } -} - -static void xhci_stall_ep(XHCITransfer *xfer) -{ - XHCIState *xhci = xfer->xhci; - XHCISlot *slot = &xhci->slots[xfer->slotid-1]; - XHCIEPContext *epctx = slot->eps[xfer->epid-1]; - uint32_t err; - XHCIStreamContext *sctx; - - if (epctx->nr_pstreams) { - sctx = xhci_find_stream(epctx, xfer->streamid, &err); - if (sctx == NULL) { - return; - } - sctx->ring.dequeue = xfer->trbs[0].addr; - sctx->ring.ccs = xfer->trbs[0].ccs; - xhci_set_ep_state(xhci, epctx, sctx, EP_HALTED); - } else { - epctx->ring.dequeue = xfer->trbs[0].addr; - epctx->ring.ccs = xfer->trbs[0].ccs; - xhci_set_ep_state(xhci, epctx, NULL, EP_HALTED); - } -} - -static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, - XHCIEPContext *epctx); - -static int xhci_setup_packet(XHCITransfer *xfer) -{ - XHCIState *xhci = xfer->xhci; - USBEndpoint *ep; - int dir; - - dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT; - - if (xfer->packet.ep) { - ep = xfer->packet.ep; - } else { - ep = xhci_epid_to_usbep(xhci, xfer->slotid, xfer->epid); - if (!ep) { - DPRINTF("xhci: slot %d has no device\n", - xfer->slotid); - return -1; - } - } - - xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */ - usb_packet_setup(&xfer->packet, dir, ep, xfer->streamid, - xfer->trbs[0].addr, false, xfer->int_req); - usb_packet_map(&xfer->packet, &xfer->sgl); - DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n", - xfer->packet.pid, ep->dev->addr, ep->nr); - return 0; -} - -static int xhci_complete_packet(XHCITransfer *xfer) -{ - if (xfer->packet.status == USB_RET_ASYNC) { - trace_usb_xhci_xfer_async(xfer); - xfer->running_async = 1; - xfer->running_retry = 0; - xfer->complete = 0; - return 0; - } else if (xfer->packet.status == USB_RET_NAK) { - trace_usb_xhci_xfer_nak(xfer); - xfer->running_async = 0; - xfer->running_retry = 1; - xfer->complete = 0; - return 0; - } else { - xfer->running_async = 0; - xfer->running_retry = 0; - xfer->complete = 1; - xhci_xfer_unmap(xfer); - } - - if (xfer->packet.status == USB_RET_SUCCESS) { - trace_usb_xhci_xfer_success(xfer, xfer->packet.actual_length); - xfer->status = CC_SUCCESS; - xhci_xfer_report(xfer); - return 0; - } - - /* error */ - trace_usb_xhci_xfer_error(xfer, xfer->packet.status); - switch (xfer->packet.status) { - case USB_RET_NODEV: - case USB_RET_IOERROR: - xfer->status = CC_USB_TRANSACTION_ERROR; - xhci_xfer_report(xfer); - xhci_stall_ep(xfer); - break; - case USB_RET_STALL: - xfer->status = CC_STALL_ERROR; - xhci_xfer_report(xfer); - xhci_stall_ep(xfer); - break; - case USB_RET_BABBLE: - xfer->status = CC_BABBLE_DETECTED; - xhci_xfer_report(xfer); - xhci_stall_ep(xfer); - break; - default: - DPRINTF("%s: FIXME: status = %d\n", __func__, - xfer->packet.status); - FIXME("unhandled USB_RET_*"); - } - return 0; -} - -static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) -{ - XHCITRB *trb_setup, *trb_status; - uint8_t bmRequestType; - - trb_setup = &xfer->trbs[0]; - trb_status = &xfer->trbs[xfer->trb_count-1]; - - trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, xfer->streamid); - - /* at most one Event Data TRB allowed after STATUS */ - if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) { - trb_status--; - } - - /* do some sanity checks */ - if (TRB_TYPE(*trb_setup) != TR_SETUP) { - DPRINTF("xhci: ep0 first TD not SETUP: %d\n", - TRB_TYPE(*trb_setup)); - return -1; - } - if (TRB_TYPE(*trb_status) != TR_STATUS) { - DPRINTF("xhci: ep0 last TD not STATUS: %d\n", - TRB_TYPE(*trb_status)); - return -1; - } - if (!(trb_setup->control & TRB_TR_IDT)) { - DPRINTF("xhci: Setup TRB doesn't have IDT set\n"); - return -1; - } - if ((trb_setup->status & 0x1ffff) != 8) { - DPRINTF("xhci: Setup TRB has bad length (%d)\n", - (trb_setup->status & 0x1ffff)); - return -1; - } - - bmRequestType = trb_setup->parameter; - - xfer->in_xfer = bmRequestType & USB_DIR_IN; - xfer->iso_xfer = false; - xfer->timed_xfer = false; - - if (xhci_setup_packet(xfer) < 0) { - return -1; - } - xfer->packet.parameter = trb_setup->parameter; - - usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); - - xhci_complete_packet(xfer); - if (!xfer->running_async && !xfer->running_retry) { - xhci_kick_ep(xhci, xfer->slotid, xfer->epid, 0); - } - return 0; -} - -static void xhci_calc_intr_kick(XHCIState *xhci, XHCITransfer *xfer, - XHCIEPContext *epctx, uint64_t mfindex) -{ - uint64_t asap = ((mfindex + epctx->interval - 1) & - ~(epctx->interval-1)); - uint64_t kick = epctx->mfindex_last + epctx->interval; - - assert(epctx->interval != 0); - xfer->mfindex_kick = MAX(asap, kick); -} - -static void xhci_calc_iso_kick(XHCIState *xhci, XHCITransfer *xfer, - XHCIEPContext *epctx, uint64_t mfindex) -{ - if (xfer->trbs[0].control & TRB_TR_SIA) { - uint64_t asap = ((mfindex + epctx->interval - 1) & - ~(epctx->interval-1)); - if (asap >= epctx->mfindex_last && - asap <= epctx->mfindex_last + epctx->interval * 4) { - xfer->mfindex_kick = epctx->mfindex_last + epctx->interval; - } else { - xfer->mfindex_kick = asap; - } - } else { - xfer->mfindex_kick = ((xfer->trbs[0].control >> TRB_TR_FRAMEID_SHIFT) - & TRB_TR_FRAMEID_MASK) << 3; - xfer->mfindex_kick |= mfindex & ~0x3fff; - if (xfer->mfindex_kick + 0x100 < mfindex) { - xfer->mfindex_kick += 0x4000; - } - } -} - -static void xhci_check_intr_iso_kick(XHCIState *xhci, XHCITransfer *xfer, - XHCIEPContext *epctx, uint64_t mfindex) -{ - if (xfer->mfindex_kick > mfindex) { - timer_mod(epctx->kick_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (xfer->mfindex_kick - mfindex) * 125000); - xfer->running_retry = 1; - } else { - epctx->mfindex_last = xfer->mfindex_kick; - timer_del(epctx->kick_timer); - xfer->running_retry = 0; - } -} - - -static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx) -{ - uint64_t mfindex; - - DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid); - - xfer->in_xfer = epctx->type>>2; - - switch(epctx->type) { - case ET_INTR_OUT: - case ET_INTR_IN: - xfer->pkts = 0; - xfer->iso_xfer = false; - xfer->timed_xfer = true; - mfindex = xhci_mfindex_get(xhci); - xhci_calc_intr_kick(xhci, xfer, epctx, mfindex); - xhci_check_intr_iso_kick(xhci, xfer, epctx, mfindex); - if (xfer->running_retry) { - return -1; - } - break; - case ET_BULK_OUT: - case ET_BULK_IN: - xfer->pkts = 0; - xfer->iso_xfer = false; - xfer->timed_xfer = false; - break; - case ET_ISO_OUT: - case ET_ISO_IN: - xfer->pkts = 1; - xfer->iso_xfer = true; - xfer->timed_xfer = true; - mfindex = xhci_mfindex_get(xhci); - xhci_calc_iso_kick(xhci, xfer, epctx, mfindex); - xhci_check_intr_iso_kick(xhci, xfer, epctx, mfindex); - if (xfer->running_retry) { - return -1; - } - break; - default: - trace_usb_xhci_unimplemented("endpoint type", epctx->type); - return -1; - } - - if (xhci_setup_packet(xfer) < 0) { - return -1; - } - usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); - - xhci_complete_packet(xfer); - if (!xfer->running_async && !xfer->running_retry) { - xhci_kick_ep(xhci, xfer->slotid, xfer->epid, xfer->streamid); - } - return 0; -} - -static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx) -{ - trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, xfer->streamid); - return xhci_submit(xhci, xfer, epctx); -} - -static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, - unsigned int epid, unsigned int streamid) -{ - XHCIStreamContext *stctx; - XHCIEPContext *epctx; - XHCIRing *ring; - USBEndpoint *ep = NULL; - uint64_t mfindex; - int length; - int i; - - trace_usb_xhci_ep_kick(slotid, epid, streamid); - assert(slotid >= 1 && slotid <= xhci->numslots); - assert(epid >= 1 && epid <= 31); - - if (!xhci->slots[slotid-1].enabled) { - DPRINTF("xhci: xhci_kick_ep for disabled slot %d\n", slotid); - return; - } - epctx = xhci->slots[slotid-1].eps[epid-1]; - if (!epctx) { - DPRINTF("xhci: xhci_kick_ep for disabled endpoint %d,%d\n", - epid, slotid); - return; - } - - /* If the device has been detached, but the guest has not noticed this - yet the 2 above checks will succeed, but we must NOT continue */ - if (!xhci->slots[slotid - 1].uport || - !xhci->slots[slotid - 1].uport->dev || - !xhci->slots[slotid - 1].uport->dev->attached) { - return; - } - - if (epctx->retry) { - XHCITransfer *xfer = epctx->retry; - - trace_usb_xhci_xfer_retry(xfer); - assert(xfer->running_retry); - if (xfer->timed_xfer) { - /* time to kick the transfer? */ - mfindex = xhci_mfindex_get(xhci); - xhci_check_intr_iso_kick(xhci, xfer, epctx, mfindex); - if (xfer->running_retry) { - return; - } - xfer->timed_xfer = 0; - xfer->running_retry = 1; - } - if (xfer->iso_xfer) { - /* retry iso transfer */ - if (xhci_setup_packet(xfer) < 0) { - return; - } - usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); - assert(xfer->packet.status != USB_RET_NAK); - xhci_complete_packet(xfer); - } else { - /* retry nak'ed transfer */ - if (xhci_setup_packet(xfer) < 0) { - return; - } - usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); - if (xfer->packet.status == USB_RET_NAK) { - return; - } - xhci_complete_packet(xfer); - } - assert(!xfer->running_retry); - epctx->retry = NULL; - } - - if (epctx->state == EP_HALTED) { - DPRINTF("xhci: ep halted, not running schedule\n"); - return; - } - - - if (epctx->nr_pstreams) { - uint32_t err; - stctx = xhci_find_stream(epctx, streamid, &err); - if (stctx == NULL) { - return; - } - ring = &stctx->ring; - xhci_set_ep_state(xhci, epctx, stctx, EP_RUNNING); - } else { - ring = &epctx->ring; - streamid = 0; - xhci_set_ep_state(xhci, epctx, NULL, EP_RUNNING); - } - assert(ring->dequeue != 0); - - while (1) { - XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer]; - if (xfer->running_async || xfer->running_retry) { - break; - } - length = xhci_ring_chain_length(xhci, ring); - if (length < 0) { - break; - } else if (length == 0) { - break; - } - if (xfer->trbs && xfer->trb_alloced < length) { - xfer->trb_count = 0; - xfer->trb_alloced = 0; - g_free(xfer->trbs); - xfer->trbs = NULL; - } - if (!xfer->trbs) { - xfer->trbs = g_new(XHCITRB, length); - xfer->trb_alloced = length; - } - xfer->trb_count = length; - - for (i = 0; i < length; i++) { - assert(xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL)); - } - xfer->streamid = streamid; - - if (epid == 1) { - if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) { - epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE; - } else { - DPRINTF("xhci: error firing CTL transfer\n"); - } - } else { - if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) { - epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE; - } else { - if (!xfer->timed_xfer) { - DPRINTF("xhci: error firing data transfer\n"); - } - } - } - - if (epctx->state == EP_HALTED) { - break; - } - if (xfer->running_retry) { - DPRINTF("xhci: xfer nacked, stopping schedule\n"); - epctx->retry = xfer; - break; - } - } - - ep = xhci_epid_to_usbep(xhci, slotid, epid); - if (ep) { - usb_device_flush_ep_queue(ep->dev, ep); - } -} - -static TRBCCode xhci_enable_slot(XHCIState *xhci, unsigned int slotid) -{ - trace_usb_xhci_slot_enable(slotid); - assert(slotid >= 1 && slotid <= xhci->numslots); - xhci->slots[slotid-1].enabled = 1; - xhci->slots[slotid-1].uport = NULL; - memset(xhci->slots[slotid-1].eps, 0, sizeof(XHCIEPContext*)*31); - - return CC_SUCCESS; -} - -static TRBCCode xhci_disable_slot(XHCIState *xhci, unsigned int slotid) -{ - int i; - - trace_usb_xhci_slot_disable(slotid); - assert(slotid >= 1 && slotid <= xhci->numslots); - - for (i = 1; i <= 31; i++) { - if (xhci->slots[slotid-1].eps[i-1]) { - xhci_disable_ep(xhci, slotid, i); - } - } - - xhci->slots[slotid-1].enabled = 0; - xhci->slots[slotid-1].addressed = 0; - xhci->slots[slotid-1].uport = NULL; - return CC_SUCCESS; -} - -static USBPort *xhci_lookup_uport(XHCIState *xhci, uint32_t *slot_ctx) -{ - USBPort *uport; - char path[32]; - int i, pos, port; - - port = (slot_ctx[1]>>16) & 0xFF; - if (port < 1 || port > xhci->numports) { - return NULL; - } - port = xhci->ports[port-1].uport->index+1; - pos = snprintf(path, sizeof(path), "%d", port); - for (i = 0; i < 5; i++) { - port = (slot_ctx[0] >> 4*i) & 0x0f; - if (!port) { - break; - } - pos += snprintf(path + pos, sizeof(path) - pos, ".%d", port); - } - - QTAILQ_FOREACH(uport, &xhci->bus.used, next) { - if (strcmp(uport->path, path) == 0) { - return uport; - } - } - return NULL; -} - -static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, - uint64_t pictx, bool bsr) -{ - XHCISlot *slot; - USBPort *uport; - USBDevice *dev; - dma_addr_t ictx, octx, dcbaap; - uint64_t poctx; - uint32_t ictl_ctx[2]; - uint32_t slot_ctx[4]; - uint32_t ep0_ctx[5]; - int i; - TRBCCode res; - - assert(slotid >= 1 && slotid <= xhci->numslots); - - dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high); - poctx = ldq_le_pci_dma(PCI_DEVICE(xhci), dcbaap + 8 * slotid); - ictx = xhci_mask64(pictx); - octx = xhci_mask64(poctx); - - DPRINTF("xhci: input context at "DMA_ADDR_FMT"\n", ictx); - DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx); - - xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx)); - - if (ictl_ctx[0] != 0x0 || ictl_ctx[1] != 0x3) { - DPRINTF("xhci: invalid input context control %08x %08x\n", - ictl_ctx[0], ictl_ctx[1]); - return CC_TRB_ERROR; - } - - xhci_dma_read_u32s(xhci, ictx+32, slot_ctx, sizeof(slot_ctx)); - xhci_dma_read_u32s(xhci, ictx+64, ep0_ctx, sizeof(ep0_ctx)); - - DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n", - slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); - - DPRINTF("xhci: input ep0 context: %08x %08x %08x %08x %08x\n", - ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]); - - uport = xhci_lookup_uport(xhci, slot_ctx); - if (uport == NULL) { - DPRINTF("xhci: port not found\n"); - return CC_TRB_ERROR; - } - trace_usb_xhci_slot_address(slotid, uport->path); - - dev = uport->dev; - if (!dev || !dev->attached) { - DPRINTF("xhci: port %s not connected\n", uport->path); - return CC_USB_TRANSACTION_ERROR; - } - - for (i = 0; i < xhci->numslots; i++) { - if (i == slotid-1) { - continue; - } - if (xhci->slots[i].uport == uport) { - DPRINTF("xhci: port %s already assigned to slot %d\n", - uport->path, i+1); - return CC_TRB_ERROR; - } - } - - slot = &xhci->slots[slotid-1]; - slot->uport = uport; - slot->ctx = octx; - - if (bsr) { - slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT; - } else { - USBPacket p; - uint8_t buf[1]; - - slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slotid; - usb_device_reset(dev); - memset(&p, 0, sizeof(p)); - usb_packet_addbuf(&p, buf, sizeof(buf)); - usb_packet_setup(&p, USB_TOKEN_OUT, - usb_ep_get(dev, USB_TOKEN_OUT, 0), 0, - 0, false, false); - usb_device_handle_control(dev, &p, - DeviceOutRequest | USB_REQ_SET_ADDRESS, - slotid, 0, 0, NULL); - assert(p.status != USB_RET_ASYNC); - } - - res = xhci_enable_ep(xhci, slotid, 1, octx+32, ep0_ctx); - - DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", - slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); - DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n", - ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]); - - xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); - xhci_dma_write_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx)); - - xhci->slots[slotid-1].addressed = 1; - return res; -} - - -static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid, - uint64_t pictx, bool dc) -{ - dma_addr_t ictx, octx; - uint32_t ictl_ctx[2]; - uint32_t slot_ctx[4]; - uint32_t islot_ctx[4]; - uint32_t ep_ctx[5]; - int i; - TRBCCode res; - - trace_usb_xhci_slot_configure(slotid); - assert(slotid >= 1 && slotid <= xhci->numslots); - - ictx = xhci_mask64(pictx); - octx = xhci->slots[slotid-1].ctx; - - DPRINTF("xhci: input context at "DMA_ADDR_FMT"\n", ictx); - DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx); - - if (dc) { - for (i = 2; i <= 31; i++) { - if (xhci->slots[slotid-1].eps[i-1]) { - xhci_disable_ep(xhci, slotid, i); - } - } - - xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); - slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT); - slot_ctx[3] |= SLOT_ADDRESSED << SLOT_STATE_SHIFT; - DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", - slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); - xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); - - return CC_SUCCESS; - } - - xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx)); - - if ((ictl_ctx[0] & 0x3) != 0x0 || (ictl_ctx[1] & 0x3) != 0x1) { - DPRINTF("xhci: invalid input context control %08x %08x\n", - ictl_ctx[0], ictl_ctx[1]); - return CC_TRB_ERROR; - } - - xhci_dma_read_u32s(xhci, ictx+32, islot_ctx, sizeof(islot_ctx)); - xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); - - if (SLOT_STATE(slot_ctx[3]) < SLOT_ADDRESSED) { - DPRINTF("xhci: invalid slot state %08x\n", slot_ctx[3]); - return CC_CONTEXT_STATE_ERROR; - } - - xhci_free_device_streams(xhci, slotid, ictl_ctx[0] | ictl_ctx[1]); - - for (i = 2; i <= 31; i++) { - if (ictl_ctx[0] & (1<= 1 && slotid <= xhci->numslots); - - ictx = xhci_mask64(pictx); - octx = xhci->slots[slotid-1].ctx; - - DPRINTF("xhci: input context at "DMA_ADDR_FMT"\n", ictx); - DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx); - - xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx)); - - if (ictl_ctx[0] != 0x0 || ictl_ctx[1] & ~0x3) { - DPRINTF("xhci: invalid input context control %08x %08x\n", - ictl_ctx[0], ictl_ctx[1]); - return CC_TRB_ERROR; - } - - if (ictl_ctx[1] & 0x1) { - xhci_dma_read_u32s(xhci, ictx+32, islot_ctx, sizeof(islot_ctx)); - - DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n", - islot_ctx[0], islot_ctx[1], islot_ctx[2], islot_ctx[3]); - - xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); - - slot_ctx[1] &= ~0xFFFF; /* max exit latency */ - slot_ctx[1] |= islot_ctx[1] & 0xFFFF; - slot_ctx[2] &= ~0xFF00000; /* interrupter target */ - slot_ctx[2] |= islot_ctx[2] & 0xFF000000; - - DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", - slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); - - xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); - } - - if (ictl_ctx[1] & 0x2) { - xhci_dma_read_u32s(xhci, ictx+64, iep0_ctx, sizeof(iep0_ctx)); - - DPRINTF("xhci: input ep0 context: %08x %08x %08x %08x %08x\n", - iep0_ctx[0], iep0_ctx[1], iep0_ctx[2], - iep0_ctx[3], iep0_ctx[4]); - - xhci_dma_read_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx)); - - ep0_ctx[1] &= ~0xFFFF0000; /* max packet size*/ - ep0_ctx[1] |= iep0_ctx[1] & 0xFFFF0000; - - DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n", - ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]); - - xhci_dma_write_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx)); - } - - return CC_SUCCESS; -} - -static TRBCCode xhci_reset_slot(XHCIState *xhci, unsigned int slotid) -{ - uint32_t slot_ctx[4]; - dma_addr_t octx; - int i; - - trace_usb_xhci_slot_reset(slotid); - assert(slotid >= 1 && slotid <= xhci->numslots); - - octx = xhci->slots[slotid-1].ctx; - - DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx); - - for (i = 2; i <= 31; i++) { - if (xhci->slots[slotid-1].eps[i-1]) { - xhci_disable_ep(xhci, slotid, i); - } - } - - xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); - slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT); - slot_ctx[3] |= SLOT_DEFAULT << SLOT_STATE_SHIFT; - DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", - slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); - xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx)); - - return CC_SUCCESS; -} - -static unsigned int xhci_get_slot(XHCIState *xhci, XHCIEvent *event, XHCITRB *trb) -{ - unsigned int slotid; - slotid = (trb->control >> TRB_CR_SLOTID_SHIFT) & TRB_CR_SLOTID_MASK; - if (slotid < 1 || slotid > xhci->numslots) { - DPRINTF("xhci: bad slot id %d\n", slotid); - event->ccode = CC_TRB_ERROR; - return 0; - } else if (!xhci->slots[slotid-1].enabled) { - DPRINTF("xhci: slot id %d not enabled\n", slotid); - event->ccode = CC_SLOT_NOT_ENABLED_ERROR; - return 0; - } - return slotid; -} - -/* cleanup slot state on usb device detach */ -static void xhci_detach_slot(XHCIState *xhci, USBPort *uport) -{ - int slot, ep; - - for (slot = 0; slot < xhci->numslots; slot++) { - if (xhci->slots[slot].uport == uport) { - break; - } - } - if (slot == xhci->numslots) { - return; - } - - for (ep = 0; ep < 31; ep++) { - if (xhci->slots[slot].eps[ep]) { - xhci_ep_nuke_xfers(xhci, slot + 1, ep + 1, 0); - } - } - xhci->slots[slot].uport = NULL; -} - -static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx) -{ - dma_addr_t ctx; - uint8_t bw_ctx[xhci->numports+1]; - - DPRINTF("xhci_get_port_bandwidth()\n"); - - ctx = xhci_mask64(pctx); - - DPRINTF("xhci: bandwidth context at "DMA_ADDR_FMT"\n", ctx); - - /* TODO: actually implement real values here */ - bw_ctx[0] = 0; - memset(&bw_ctx[1], 80, xhci->numports); /* 80% */ - pci_dma_write(PCI_DEVICE(xhci), ctx, bw_ctx, sizeof(bw_ctx)); - - return CC_SUCCESS; -} - -static uint32_t rotl(uint32_t v, unsigned count) -{ - count &= 31; - return (v << count) | (v >> (32 - count)); -} - - -static uint32_t xhci_nec_challenge(uint32_t hi, uint32_t lo) -{ - uint32_t val; - val = rotl(lo - 0x49434878, 32 - ((hi>>8) & 0x1F)); - val += rotl(lo + 0x49434878, hi & 0x1F); - val -= rotl(hi ^ 0x49434878, (lo >> 16) & 0x1F); - return ~val; -} - -static void xhci_via_challenge(XHCIState *xhci, uint64_t addr) -{ - PCIDevice *pci_dev = PCI_DEVICE(xhci); - uint32_t buf[8]; - uint32_t obuf[8]; - dma_addr_t paddr = xhci_mask64(addr); - - pci_dma_read(pci_dev, paddr, &buf, 32); - - memcpy(obuf, buf, sizeof(obuf)); - - if ((buf[0] & 0xff) == 2) { - obuf[0] = 0x49932000 + 0x54dc200 * buf[2] + 0x7429b578 * buf[3]; - obuf[0] |= (buf[2] * buf[3]) & 0xff; - obuf[1] = 0x0132bb37 + 0xe89 * buf[2] + 0xf09 * buf[3]; - obuf[2] = 0x0066c2e9 + 0x2091 * buf[2] + 0x19bd * buf[3]; - obuf[3] = 0xd5281342 + 0x2cc9691 * buf[2] + 0x2367662 * buf[3]; - obuf[4] = 0x0123c75c + 0x1595 * buf[2] + 0x19ec * buf[3]; - obuf[5] = 0x00f695de + 0x26fd * buf[2] + 0x3e9 * buf[3]; - obuf[6] = obuf[2] ^ obuf[3] ^ 0x29472956; - obuf[7] = obuf[2] ^ obuf[3] ^ 0x65866593; - } - - pci_dma_write(pci_dev, paddr, &obuf, 32); -} - -static void xhci_process_commands(XHCIState *xhci) -{ - XHCITRB trb; - TRBType type; - XHCIEvent event = {ER_COMMAND_COMPLETE, CC_SUCCESS}; - dma_addr_t addr; - unsigned int i, slotid = 0; - - DPRINTF("xhci_process_commands()\n"); - if (!xhci_running(xhci)) { - DPRINTF("xhci_process_commands() called while xHC stopped or paused\n"); - return; - } - - xhci->crcr_low |= CRCR_CRR; - - while ((type = xhci_ring_fetch(xhci, &xhci->cmd_ring, &trb, &addr))) { - event.ptr = addr; - switch (type) { - case CR_ENABLE_SLOT: - for (i = 0; i < xhci->numslots; i++) { - if (!xhci->slots[i].enabled) { - break; - } - } - if (i >= xhci->numslots) { - DPRINTF("xhci: no device slots available\n"); - event.ccode = CC_NO_SLOTS_ERROR; - } else { - slotid = i+1; - event.ccode = xhci_enable_slot(xhci, slotid); - } - break; - case CR_DISABLE_SLOT: - slotid = xhci_get_slot(xhci, &event, &trb); - if (slotid) { - event.ccode = xhci_disable_slot(xhci, slotid); - } - break; - case CR_ADDRESS_DEVICE: - slotid = xhci_get_slot(xhci, &event, &trb); - if (slotid) { - event.ccode = xhci_address_slot(xhci, slotid, trb.parameter, - trb.control & TRB_CR_BSR); - } - break; - case CR_CONFIGURE_ENDPOINT: - slotid = xhci_get_slot(xhci, &event, &trb); - if (slotid) { - event.ccode = xhci_configure_slot(xhci, slotid, trb.parameter, - trb.control & TRB_CR_DC); - } - break; - case CR_EVALUATE_CONTEXT: - slotid = xhci_get_slot(xhci, &event, &trb); - if (slotid) { - event.ccode = xhci_evaluate_slot(xhci, slotid, trb.parameter); - } - break; - case CR_STOP_ENDPOINT: - slotid = xhci_get_slot(xhci, &event, &trb); - if (slotid) { - unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT) - & TRB_CR_EPID_MASK; - event.ccode = xhci_stop_ep(xhci, slotid, epid); - } - break; - case CR_RESET_ENDPOINT: - slotid = xhci_get_slot(xhci, &event, &trb); - if (slotid) { - unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT) - & TRB_CR_EPID_MASK; - event.ccode = xhci_reset_ep(xhci, slotid, epid); - } - break; - case CR_SET_TR_DEQUEUE: - slotid = xhci_get_slot(xhci, &event, &trb); - if (slotid) { - unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT) - & TRB_CR_EPID_MASK; - unsigned int streamid = (trb.status >> 16) & 0xffff; - event.ccode = xhci_set_ep_dequeue(xhci, slotid, - epid, streamid, - trb.parameter); - } - break; - case CR_RESET_DEVICE: - slotid = xhci_get_slot(xhci, &event, &trb); - if (slotid) { - event.ccode = xhci_reset_slot(xhci, slotid); - } - break; - case CR_GET_PORT_BANDWIDTH: - event.ccode = xhci_get_port_bandwidth(xhci, trb.parameter); - break; - case CR_VENDOR_VIA_CHALLENGE_RESPONSE: - xhci_via_challenge(xhci, trb.parameter); - break; - case CR_VENDOR_NEC_FIRMWARE_REVISION: - event.type = 48; /* NEC reply */ - event.length = 0x3025; - break; - case CR_VENDOR_NEC_CHALLENGE_RESPONSE: - { - uint32_t chi = trb.parameter >> 32; - uint32_t clo = trb.parameter; - uint32_t val = xhci_nec_challenge(chi, clo); - event.length = val & 0xFFFF; - event.epid = val >> 16; - slotid = val >> 24; - event.type = 48; /* NEC reply */ - } - break; - default: - trace_usb_xhci_unimplemented("command", type); - event.ccode = CC_TRB_ERROR; - break; - } - event.slotid = slotid; - xhci_event(xhci, &event, 0); - } -} - -static bool xhci_port_have_device(XHCIPort *port) -{ - if (!port->uport->dev || !port->uport->dev->attached) { - return false; /* no device present */ - } - if (!((1 << port->uport->dev->speed) & port->speedmask)) { - return false; /* speed mismatch */ - } - return true; -} - -static void xhci_port_notify(XHCIPort *port, uint32_t bits) -{ - XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, - port->portnr << 24 }; - - if ((port->portsc & bits) == bits) { - return; - } - trace_usb_xhci_port_notify(port->portnr, bits); - port->portsc |= bits; - if (!xhci_running(port->xhci)) { - return; - } - xhci_event(port->xhci, &ev, 0); -} - -static void xhci_port_update(XHCIPort *port, int is_detach) -{ - uint32_t pls = PLS_RX_DETECT; - - port->portsc = PORTSC_PP; - if (!is_detach && xhci_port_have_device(port)) { - port->portsc |= PORTSC_CCS; - switch (port->uport->dev->speed) { - case USB_SPEED_LOW: - port->portsc |= PORTSC_SPEED_LOW; - pls = PLS_POLLING; - break; - case USB_SPEED_FULL: - port->portsc |= PORTSC_SPEED_FULL; - pls = PLS_POLLING; - break; - case USB_SPEED_HIGH: - port->portsc |= PORTSC_SPEED_HIGH; - pls = PLS_POLLING; - break; - case USB_SPEED_SUPER: - port->portsc |= PORTSC_SPEED_SUPER; - port->portsc |= PORTSC_PED; - pls = PLS_U0; - break; - } - } - set_field(&port->portsc, pls, PORTSC_PLS); - trace_usb_xhci_port_link(port->portnr, pls); - xhci_port_notify(port, PORTSC_CSC); -} - -static void xhci_port_reset(XHCIPort *port, bool warm_reset) -{ - trace_usb_xhci_port_reset(port->portnr, warm_reset); - - if (!xhci_port_have_device(port)) { - return; - } - - usb_device_reset(port->uport->dev); - - switch (port->uport->dev->speed) { - case USB_SPEED_SUPER: - if (warm_reset) { - port->portsc |= PORTSC_WRC; - } - /* fall through */ - case USB_SPEED_LOW: - case USB_SPEED_FULL: - case USB_SPEED_HIGH: - set_field(&port->portsc, PLS_U0, PORTSC_PLS); - trace_usb_xhci_port_link(port->portnr, PLS_U0); - port->portsc |= PORTSC_PED; - break; - } - - port->portsc &= ~PORTSC_PR; - xhci_port_notify(port, PORTSC_PRC); -} - -static void xhci_reset(DeviceState *dev) -{ - XHCIState *xhci = XHCI(dev); - int i; - - trace_usb_xhci_reset(); - if (!(xhci->usbsts & USBSTS_HCH)) { - DPRINTF("xhci: reset while running!\n"); - } - - xhci->usbcmd = 0; - xhci->usbsts = USBSTS_HCH; - xhci->dnctrl = 0; - xhci->crcr_low = 0; - xhci->crcr_high = 0; - xhci->dcbaap_low = 0; - xhci->dcbaap_high = 0; - xhci->config = 0; - - for (i = 0; i < xhci->numslots; i++) { - xhci_disable_slot(xhci, i+1); - } - - for (i = 0; i < xhci->numports; i++) { - xhci_port_update(xhci->ports + i, 0); - } - - for (i = 0; i < xhci->numintrs; i++) { - xhci->intr[i].iman = 0; - xhci->intr[i].imod = 0; - xhci->intr[i].erstsz = 0; - xhci->intr[i].erstba_low = 0; - xhci->intr[i].erstba_high = 0; - xhci->intr[i].erdp_low = 0; - xhci->intr[i].erdp_high = 0; - xhci->intr[i].msix_used = 0; - - xhci->intr[i].er_ep_idx = 0; - xhci->intr[i].er_pcs = 1; - xhci->intr[i].er_full = 0; - xhci->intr[i].ev_buffer_put = 0; - xhci->intr[i].ev_buffer_get = 0; - } - - xhci->mfindex_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - xhci_mfwrap_update(xhci); -} - -static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size) -{ - XHCIState *xhci = ptr; - uint32_t ret; - - switch (reg) { - case 0x00: /* HCIVERSION, CAPLENGTH */ - ret = 0x01000000 | LEN_CAP; - break; - case 0x04: /* HCSPARAMS 1 */ - ret = ((xhci->numports_2+xhci->numports_3)<<24) - | (xhci->numintrs<<8) | xhci->numslots; - break; - case 0x08: /* HCSPARAMS 2 */ - ret = 0x0000000f; - break; - case 0x0c: /* HCSPARAMS 3 */ - ret = 0x00000000; - break; - case 0x10: /* HCCPARAMS */ - if (sizeof(dma_addr_t) == 4) { - ret = 0x00080000 | (xhci->max_pstreams_mask << 12); - } else { - ret = 0x00080001 | (xhci->max_pstreams_mask << 12); - } - break; - case 0x14: /* DBOFF */ - ret = OFF_DOORBELL; - break; - case 0x18: /* RTSOFF */ - ret = OFF_RUNTIME; - break; - - /* extended capabilities */ - case 0x20: /* Supported Protocol:00 */ - ret = 0x02000402; /* USB 2.0 */ - break; - case 0x24: /* Supported Protocol:04 */ - ret = 0x20425355; /* "USB " */ - break; - case 0x28: /* Supported Protocol:08 */ - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - ret = (xhci->numports_2<<8) | (xhci->numports_3+1); - } else { - ret = (xhci->numports_2<<8) | 1; - } - break; - case 0x2c: /* Supported Protocol:0c */ - ret = 0x00000000; /* reserved */ - break; - case 0x30: /* Supported Protocol:00 */ - ret = 0x03000002; /* USB 3.0 */ - break; - case 0x34: /* Supported Protocol:04 */ - ret = 0x20425355; /* "USB " */ - break; - case 0x38: /* Supported Protocol:08 */ - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - ret = (xhci->numports_3<<8) | 1; - } else { - ret = (xhci->numports_3<<8) | (xhci->numports_2+1); - } - break; - case 0x3c: /* Supported Protocol:0c */ - ret = 0x00000000; /* reserved */ - break; - default: - trace_usb_xhci_unimplemented("cap read", reg); - ret = 0; - } - - trace_usb_xhci_cap_read(reg, ret); - return ret; -} - -static uint64_t xhci_port_read(void *ptr, hwaddr reg, unsigned size) -{ - XHCIPort *port = ptr; - uint32_t ret; - - switch (reg) { - case 0x00: /* PORTSC */ - ret = port->portsc; - break; - case 0x04: /* PORTPMSC */ - case 0x08: /* PORTLI */ - ret = 0; - break; - case 0x0c: /* reserved */ - default: - trace_usb_xhci_unimplemented("port read", reg); - ret = 0; - } - - trace_usb_xhci_port_read(port->portnr, reg, ret); - return ret; -} - -static void xhci_port_write(void *ptr, hwaddr reg, - uint64_t val, unsigned size) -{ - XHCIPort *port = ptr; - uint32_t portsc, notify; - - trace_usb_xhci_port_write(port->portnr, reg, val); - - switch (reg) { - case 0x00: /* PORTSC */ - /* write-1-to-start bits */ - if (val & PORTSC_WPR) { - xhci_port_reset(port, true); - break; - } - if (val & PORTSC_PR) { - xhci_port_reset(port, false); - break; - } - - portsc = port->portsc; - notify = 0; - /* write-1-to-clear bits*/ - portsc &= ~(val & (PORTSC_CSC|PORTSC_PEC|PORTSC_WRC|PORTSC_OCC| - PORTSC_PRC|PORTSC_PLC|PORTSC_CEC)); - if (val & PORTSC_LWS) { - /* overwrite PLS only when LWS=1 */ - uint32_t old_pls = get_field(port->portsc, PORTSC_PLS); - uint32_t new_pls = get_field(val, PORTSC_PLS); - switch (new_pls) { - case PLS_U0: - if (old_pls != PLS_U0) { - set_field(&portsc, new_pls, PORTSC_PLS); - trace_usb_xhci_port_link(port->portnr, new_pls); - notify = PORTSC_PLC; - } - break; - case PLS_U3: - if (old_pls < PLS_U3) { - set_field(&portsc, new_pls, PORTSC_PLS); - trace_usb_xhci_port_link(port->portnr, new_pls); - } - break; - case PLS_RESUME: - /* windows does this for some reason, don't spam stderr */ - break; - default: - DPRINTF("%s: ignore pls write (old %d, new %d)\n", - __func__, old_pls, new_pls); - break; - } - } - /* read/write bits */ - portsc &= ~(PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE); - portsc |= (val & (PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE)); - port->portsc = portsc; - if (notify) { - xhci_port_notify(port, notify); - } - break; - case 0x04: /* PORTPMSC */ - case 0x08: /* PORTLI */ - default: - trace_usb_xhci_unimplemented("port write", reg); - } -} - -static uint64_t xhci_oper_read(void *ptr, hwaddr reg, unsigned size) -{ - XHCIState *xhci = ptr; - uint32_t ret; - - switch (reg) { - case 0x00: /* USBCMD */ - ret = xhci->usbcmd; - break; - case 0x04: /* USBSTS */ - ret = xhci->usbsts; - break; - case 0x08: /* PAGESIZE */ - ret = 1; /* 4KiB */ - break; - case 0x14: /* DNCTRL */ - ret = xhci->dnctrl; - break; - case 0x18: /* CRCR low */ - ret = xhci->crcr_low & ~0xe; - break; - case 0x1c: /* CRCR high */ - ret = xhci->crcr_high; - break; - case 0x30: /* DCBAAP low */ - ret = xhci->dcbaap_low; - break; - case 0x34: /* DCBAAP high */ - ret = xhci->dcbaap_high; - break; - case 0x38: /* CONFIG */ - ret = xhci->config; - break; - default: - trace_usb_xhci_unimplemented("oper read", reg); - ret = 0; - } - - trace_usb_xhci_oper_read(reg, ret); - return ret; -} - -static void xhci_oper_write(void *ptr, hwaddr reg, - uint64_t val, unsigned size) -{ - XHCIState *xhci = ptr; - DeviceState *d = DEVICE(ptr); - - trace_usb_xhci_oper_write(reg, val); - - switch (reg) { - case 0x00: /* USBCMD */ - if ((val & USBCMD_RS) && !(xhci->usbcmd & USBCMD_RS)) { - xhci_run(xhci); - } else if (!(val & USBCMD_RS) && (xhci->usbcmd & USBCMD_RS)) { - xhci_stop(xhci); - } - if (val & USBCMD_CSS) { - /* save state */ - xhci->usbsts &= ~USBSTS_SRE; - } - if (val & USBCMD_CRS) { - /* restore state */ - xhci->usbsts |= USBSTS_SRE; - } - xhci->usbcmd = val & 0xc0f; - xhci_mfwrap_update(xhci); - if (val & USBCMD_HCRST) { - xhci_reset(d); - } - xhci_intx_update(xhci); - break; - - case 0x04: /* USBSTS */ - /* these bits are write-1-to-clear */ - xhci->usbsts &= ~(val & (USBSTS_HSE|USBSTS_EINT|USBSTS_PCD|USBSTS_SRE)); - xhci_intx_update(xhci); - break; - - case 0x14: /* DNCTRL */ - xhci->dnctrl = val & 0xffff; - break; - case 0x18: /* CRCR low */ - xhci->crcr_low = (val & 0xffffffcf) | (xhci->crcr_low & CRCR_CRR); - break; - case 0x1c: /* CRCR high */ - xhci->crcr_high = val; - if (xhci->crcr_low & (CRCR_CA|CRCR_CS) && (xhci->crcr_low & CRCR_CRR)) { - XHCIEvent event = {ER_COMMAND_COMPLETE, CC_COMMAND_RING_STOPPED}; - xhci->crcr_low &= ~CRCR_CRR; - xhci_event(xhci, &event, 0); - DPRINTF("xhci: command ring stopped (CRCR=%08x)\n", xhci->crcr_low); - } else { - dma_addr_t base = xhci_addr64(xhci->crcr_low & ~0x3f, val); - xhci_ring_init(xhci, &xhci->cmd_ring, base); - } - xhci->crcr_low &= ~(CRCR_CA | CRCR_CS); - break; - case 0x30: /* DCBAAP low */ - xhci->dcbaap_low = val & 0xffffffc0; - break; - case 0x34: /* DCBAAP high */ - xhci->dcbaap_high = val; - break; - case 0x38: /* CONFIG */ - xhci->config = val & 0xff; - break; - default: - trace_usb_xhci_unimplemented("oper write", reg); - } -} - -static uint64_t xhci_runtime_read(void *ptr, hwaddr reg, - unsigned size) -{ - XHCIState *xhci = ptr; - uint32_t ret = 0; - - if (reg < 0x20) { - switch (reg) { - case 0x00: /* MFINDEX */ - ret = xhci_mfindex_get(xhci) & 0x3fff; - break; - default: - trace_usb_xhci_unimplemented("runtime read", reg); - break; - } - } else { - int v = (reg - 0x20) / 0x20; - XHCIInterrupter *intr = &xhci->intr[v]; - switch (reg & 0x1f) { - case 0x00: /* IMAN */ - ret = intr->iman; - break; - case 0x04: /* IMOD */ - ret = intr->imod; - break; - case 0x08: /* ERSTSZ */ - ret = intr->erstsz; - break; - case 0x10: /* ERSTBA low */ - ret = intr->erstba_low; - break; - case 0x14: /* ERSTBA high */ - ret = intr->erstba_high; - break; - case 0x18: /* ERDP low */ - ret = intr->erdp_low; - break; - case 0x1c: /* ERDP high */ - ret = intr->erdp_high; - break; - } - } - - trace_usb_xhci_runtime_read(reg, ret); - return ret; -} - -static void xhci_runtime_write(void *ptr, hwaddr reg, - uint64_t val, unsigned size) -{ - XHCIState *xhci = ptr; - int v = (reg - 0x20) / 0x20; - XHCIInterrupter *intr = &xhci->intr[v]; - trace_usb_xhci_runtime_write(reg, val); - - if (reg < 0x20) { - trace_usb_xhci_unimplemented("runtime write", reg); - return; - } - - switch (reg & 0x1f) { - case 0x00: /* IMAN */ - if (val & IMAN_IP) { - intr->iman &= ~IMAN_IP; - } - intr->iman &= ~IMAN_IE; - intr->iman |= val & IMAN_IE; - if (v == 0) { - xhci_intx_update(xhci); - } - xhci_msix_update(xhci, v); - break; - case 0x04: /* IMOD */ - intr->imod = val; - break; - case 0x08: /* ERSTSZ */ - intr->erstsz = val & 0xffff; - break; - case 0x10: /* ERSTBA low */ - /* XXX NEC driver bug: it doesn't align this to 64 bytes - intr->erstba_low = val & 0xffffffc0; */ - intr->erstba_low = val & 0xfffffff0; - break; - case 0x14: /* ERSTBA high */ - intr->erstba_high = val; - xhci_er_reset(xhci, v); - break; - case 0x18: /* ERDP low */ - if (val & ERDP_EHB) { - intr->erdp_low &= ~ERDP_EHB; - } - intr->erdp_low = (val & ~ERDP_EHB) | (intr->erdp_low & ERDP_EHB); - break; - case 0x1c: /* ERDP high */ - intr->erdp_high = val; - xhci_events_update(xhci, v); - break; - default: - trace_usb_xhci_unimplemented("oper write", reg); - } -} - -static uint64_t xhci_doorbell_read(void *ptr, hwaddr reg, - unsigned size) -{ - /* doorbells always read as 0 */ - trace_usb_xhci_doorbell_read(reg, 0); - return 0; -} - -static void xhci_doorbell_write(void *ptr, hwaddr reg, - uint64_t val, unsigned size) -{ - XHCIState *xhci = ptr; - unsigned int epid, streamid; - - trace_usb_xhci_doorbell_write(reg, val); - - if (!xhci_running(xhci)) { - DPRINTF("xhci: wrote doorbell while xHC stopped or paused\n"); - return; - } - - reg >>= 2; - - if (reg == 0) { - if (val == 0) { - xhci_process_commands(xhci); - } else { - DPRINTF("xhci: bad doorbell 0 write: 0x%x\n", - (uint32_t)val); - } - } else { - epid = val & 0xff; - streamid = (val >> 16) & 0xffff; - if (reg > xhci->numslots) { - DPRINTF("xhci: bad doorbell %d\n", (int)reg); - } else if (epid > 31) { - DPRINTF("xhci: bad doorbell %d write: 0x%x\n", - (int)reg, (uint32_t)val); - } else { - xhci_kick_ep(xhci, reg, epid, streamid); - } - } -} - -static void xhci_cap_write(void *opaque, hwaddr addr, uint64_t val, - unsigned width) -{ - /* nothing */ -} - -static const MemoryRegionOps xhci_cap_ops = { - .read = xhci_cap_read, - .write = xhci_cap_write, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .impl.min_access_size = 4, - .impl.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps xhci_oper_ops = { - .read = xhci_oper_read, - .write = xhci_oper_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps xhci_port_ops = { - .read = xhci_port_read, - .write = xhci_port_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps xhci_runtime_ops = { - .read = xhci_runtime_read, - .write = xhci_runtime_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps xhci_doorbell_ops = { - .read = xhci_doorbell_read, - .write = xhci_doorbell_write, - .valid.min_access_size = 4, - .valid.max_access_size = 4, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void xhci_attach(USBPort *usbport) -{ - XHCIState *xhci = usbport->opaque; - XHCIPort *port = xhci_lookup_port(xhci, usbport); - - xhci_port_update(port, 0); -} - -static void xhci_detach(USBPort *usbport) -{ - XHCIState *xhci = usbport->opaque; - XHCIPort *port = xhci_lookup_port(xhci, usbport); - - xhci_detach_slot(xhci, usbport); - xhci_port_update(port, 1); -} - -static void xhci_wakeup(USBPort *usbport) -{ - XHCIState *xhci = usbport->opaque; - XHCIPort *port = xhci_lookup_port(xhci, usbport); - - if (get_field(port->portsc, PORTSC_PLS) != PLS_U3) { - return; - } - set_field(&port->portsc, PLS_RESUME, PORTSC_PLS); - xhci_port_notify(port, PORTSC_PLC); -} - -static void xhci_complete(USBPort *port, USBPacket *packet) -{ - XHCITransfer *xfer = container_of(packet, XHCITransfer, packet); - - if (packet->status == USB_RET_REMOVE_FROM_QUEUE) { - xhci_ep_nuke_one_xfer(xfer, 0); - return; - } - xhci_complete_packet(xfer); - xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid, xfer->streamid); -} - -static void xhci_child_detach(USBPort *uport, USBDevice *child) -{ - USBBus *bus = usb_bus_from_device(child); - XHCIState *xhci = container_of(bus, XHCIState, bus); - - xhci_detach_slot(xhci, child->port); -} - -static USBPortOps xhci_uport_ops = { - .attach = xhci_attach, - .detach = xhci_detach, - .wakeup = xhci_wakeup, - .complete = xhci_complete, - .child_detach = xhci_child_detach, -}; - -static int xhci_find_epid(USBEndpoint *ep) -{ - if (ep->nr == 0) { - return 1; - } - if (ep->pid == USB_TOKEN_IN) { - return ep->nr * 2 + 1; - } else { - return ep->nr * 2; - } -} - -static USBEndpoint *xhci_epid_to_usbep(XHCIState *xhci, - unsigned int slotid, unsigned int epid) -{ - assert(slotid >= 1 && slotid <= xhci->numslots); - - if (!xhci->slots[slotid - 1].uport) { - return NULL; - } - - return usb_ep_get(xhci->slots[slotid - 1].uport->dev, - (epid & 1) ? USB_TOKEN_IN : USB_TOKEN_OUT, epid >> 1); -} - -static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep, - unsigned int stream) -{ - XHCIState *xhci = container_of(bus, XHCIState, bus); - int slotid; - - DPRINTF("%s\n", __func__); - slotid = ep->dev->addr; - if (slotid == 0 || !xhci->slots[slotid-1].enabled) { - DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr); - return; - } - xhci_kick_ep(xhci, slotid, xhci_find_epid(ep), stream); -} - -static USBBusOps xhci_bus_ops = { - .wakeup_endpoint = xhci_wakeup_endpoint, -}; - -static void usb_xhci_init(XHCIState *xhci) -{ - DeviceState *dev = DEVICE(xhci); - XHCIPort *port; - int i, usbports, speedmask; - - xhci->usbsts = USBSTS_HCH; - - if (xhci->numports_2 > MAXPORTS_2) { - xhci->numports_2 = MAXPORTS_2; - } - if (xhci->numports_3 > MAXPORTS_3) { - xhci->numports_3 = MAXPORTS_3; - } - usbports = MAX(xhci->numports_2, xhci->numports_3); - xhci->numports = xhci->numports_2 + xhci->numports_3; - - usb_bus_new(&xhci->bus, sizeof(xhci->bus), &xhci_bus_ops, dev); - - for (i = 0; i < usbports; i++) { - speedmask = 0; - if (i < xhci->numports_2) { - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - port = &xhci->ports[i + xhci->numports_3]; - port->portnr = i + 1 + xhci->numports_3; - } else { - port = &xhci->ports[i]; - port->portnr = i + 1; - } - port->uport = &xhci->uports[i]; - port->speedmask = - USB_SPEED_MASK_LOW | - USB_SPEED_MASK_FULL | - USB_SPEED_MASK_HIGH; - snprintf(port->name, sizeof(port->name), "usb2 port #%d", i+1); - speedmask |= port->speedmask; - } - if (i < xhci->numports_3) { - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - port = &xhci->ports[i]; - port->portnr = i + 1; - } else { - port = &xhci->ports[i + xhci->numports_2]; - port->portnr = i + 1 + xhci->numports_2; - } - port->uport = &xhci->uports[i]; - port->speedmask = USB_SPEED_MASK_SUPER; - snprintf(port->name, sizeof(port->name), "usb3 port #%d", i+1); - speedmask |= port->speedmask; - } - usb_register_port(&xhci->bus, &xhci->uports[i], xhci, i, - &xhci_uport_ops, speedmask); - } -} - -static void usb_xhci_realize(struct PCIDevice *dev, Error **errp) -{ - int i, ret; - - XHCIState *xhci = XHCI(dev); - - dev->config[PCI_CLASS_PROG] = 0x30; /* xHCI */ - dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */ - dev->config[PCI_CACHE_LINE_SIZE] = 0x10; - dev->config[0x60] = 0x30; /* release number */ - - usb_xhci_init(xhci); - - if (xhci->numintrs > MAXINTRS) { - xhci->numintrs = MAXINTRS; - } - while (xhci->numintrs & (xhci->numintrs - 1)) { /* ! power of 2 */ - xhci->numintrs++; - } - if (xhci->numintrs < 1) { - xhci->numintrs = 1; - } - if (xhci->numslots > MAXSLOTS) { - xhci->numslots = MAXSLOTS; - } - if (xhci->numslots < 1) { - xhci->numslots = 1; - } - if (xhci_get_flag(xhci, XHCI_FLAG_ENABLE_STREAMS)) { - xhci->max_pstreams_mask = 7; /* == 256 primary streams */ - } else { - xhci->max_pstreams_mask = 0; - } - - xhci->mfwrap_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_mfwrap_timer, xhci); - - memory_region_init(&xhci->mem, OBJECT(xhci), "xhci", LEN_REGS); - memory_region_init_io(&xhci->mem_cap, OBJECT(xhci), &xhci_cap_ops, xhci, - "capabilities", LEN_CAP); - memory_region_init_io(&xhci->mem_oper, OBJECT(xhci), &xhci_oper_ops, xhci, - "operational", 0x400); - memory_region_init_io(&xhci->mem_runtime, OBJECT(xhci), &xhci_runtime_ops, xhci, - "runtime", LEN_RUNTIME); - memory_region_init_io(&xhci->mem_doorbell, OBJECT(xhci), &xhci_doorbell_ops, xhci, - "doorbell", LEN_DOORBELL); - - memory_region_add_subregion(&xhci->mem, 0, &xhci->mem_cap); - memory_region_add_subregion(&xhci->mem, OFF_OPER, &xhci->mem_oper); - memory_region_add_subregion(&xhci->mem, OFF_RUNTIME, &xhci->mem_runtime); - memory_region_add_subregion(&xhci->mem, OFF_DOORBELL, &xhci->mem_doorbell); - - for (i = 0; i < xhci->numports; i++) { - XHCIPort *port = &xhci->ports[i]; - uint32_t offset = OFF_OPER + 0x400 + 0x10 * i; - port->xhci = xhci; - memory_region_init_io(&port->mem, OBJECT(xhci), &xhci_port_ops, port, - port->name, 0x10); - memory_region_add_subregion(&xhci->mem, offset, &port->mem); - } - - pci_register_bar(dev, 0, - PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64, - &xhci->mem); - - if (pci_bus_is_express(dev->bus) || - xhci_get_flag(xhci, XHCI_FLAG_FORCE_PCIE_ENDCAP)) { - ret = pcie_endpoint_cap_init(dev, 0xa0); - assert(ret >= 0); - } - - if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI)) { - msi_init(dev, 0x70, xhci->numintrs, true, false); - } - if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI_X)) { - msix_init(dev, xhci->numintrs, - &xhci->mem, 0, OFF_MSIX_TABLE, - &xhci->mem, 0, OFF_MSIX_PBA, - 0x90); - } -} - -static void usb_xhci_exit(PCIDevice *dev) -{ - int i; - XHCIState *xhci = XHCI(dev); - - trace_usb_xhci_exit(); - - for (i = 0; i < xhci->numslots; i++) { - xhci_disable_slot(xhci, i + 1); - } - - if (xhci->mfwrap_timer) { - timer_del(xhci->mfwrap_timer); - timer_free(xhci->mfwrap_timer); - xhci->mfwrap_timer = NULL; - } - - memory_region_del_subregion(&xhci->mem, &xhci->mem_cap); - memory_region_del_subregion(&xhci->mem, &xhci->mem_oper); - memory_region_del_subregion(&xhci->mem, &xhci->mem_runtime); - memory_region_del_subregion(&xhci->mem, &xhci->mem_doorbell); - - for (i = 0; i < xhci->numports; i++) { - XHCIPort *port = &xhci->ports[i]; - memory_region_del_subregion(&xhci->mem, &port->mem); - } - - /* destroy msix memory region */ - if (dev->msix_table && dev->msix_pba - && dev->msix_entry_used) { - memory_region_del_subregion(&xhci->mem, &dev->msix_table_mmio); - memory_region_del_subregion(&xhci->mem, &dev->msix_pba_mmio); - } - - usb_bus_release(&xhci->bus); -} - -static int usb_xhci_post_load(void *opaque, int version_id) -{ - XHCIState *xhci = opaque; - PCIDevice *pci_dev = PCI_DEVICE(xhci); - XHCISlot *slot; - XHCIEPContext *epctx; - dma_addr_t dcbaap, pctx; - uint32_t slot_ctx[4]; - uint32_t ep_ctx[5]; - int slotid, epid, state, intr; - - dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high); - - for (slotid = 1; slotid <= xhci->numslots; slotid++) { - slot = &xhci->slots[slotid-1]; - if (!slot->addressed) { - continue; - } - slot->ctx = - xhci_mask64(ldq_le_pci_dma(pci_dev, dcbaap + 8 * slotid)); - xhci_dma_read_u32s(xhci, slot->ctx, slot_ctx, sizeof(slot_ctx)); - slot->uport = xhci_lookup_uport(xhci, slot_ctx); - if (!slot->uport) { - /* should not happen, but may trigger on guest bugs */ - slot->enabled = 0; - slot->addressed = 0; - continue; - } - assert(slot->uport && slot->uport->dev); - - for (epid = 1; epid <= 31; epid++) { - pctx = slot->ctx + 32 * epid; - xhci_dma_read_u32s(xhci, pctx, ep_ctx, sizeof(ep_ctx)); - state = ep_ctx[0] & EP_STATE_MASK; - if (state == EP_DISABLED) { - continue; - } - epctx = xhci_alloc_epctx(xhci, slotid, epid); - slot->eps[epid-1] = epctx; - xhci_init_epctx(epctx, pctx, ep_ctx); - epctx->state = state; - if (state == EP_RUNNING) { - /* kick endpoint after vmload is finished */ - timer_mod(epctx->kick_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - } - } - } - - for (intr = 0; intr < xhci->numintrs; intr++) { - if (xhci->intr[intr].msix_used) { - msix_vector_use(pci_dev, intr); - } else { - msix_vector_unuse(pci_dev, intr); - } - } - - return 0; -} - -static const VMStateDescription vmstate_xhci_ring = { - .name = "xhci-ring", - .version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT64(dequeue, XHCIRing), - VMSTATE_BOOL(ccs, XHCIRing), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_xhci_port = { - .name = "xhci-port", - .version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(portsc, XHCIPort), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_xhci_slot = { - .name = "xhci-slot", - .version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_BOOL(enabled, XHCISlot), - VMSTATE_BOOL(addressed, XHCISlot), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_xhci_event = { - .name = "xhci-event", - .version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(type, XHCIEvent), - VMSTATE_UINT32(ccode, XHCIEvent), - VMSTATE_UINT64(ptr, XHCIEvent), - VMSTATE_UINT32(length, XHCIEvent), - VMSTATE_UINT32(flags, XHCIEvent), - VMSTATE_UINT8(slotid, XHCIEvent), - VMSTATE_UINT8(epid, XHCIEvent), - VMSTATE_END_OF_LIST() - } -}; - -static bool xhci_er_full(void *opaque, int version_id) -{ - struct XHCIInterrupter *intr = opaque; - return intr->er_full; -} - -static const VMStateDescription vmstate_xhci_intr = { - .name = "xhci-intr", - .version_id = 1, - .fields = (VMStateField[]) { - /* registers */ - VMSTATE_UINT32(iman, XHCIInterrupter), - VMSTATE_UINT32(imod, XHCIInterrupter), - VMSTATE_UINT32(erstsz, XHCIInterrupter), - VMSTATE_UINT32(erstba_low, XHCIInterrupter), - VMSTATE_UINT32(erstba_high, XHCIInterrupter), - VMSTATE_UINT32(erdp_low, XHCIInterrupter), - VMSTATE_UINT32(erdp_high, XHCIInterrupter), - - /* state */ - VMSTATE_BOOL(msix_used, XHCIInterrupter), - VMSTATE_BOOL(er_pcs, XHCIInterrupter), - VMSTATE_UINT64(er_start, XHCIInterrupter), - VMSTATE_UINT32(er_size, XHCIInterrupter), - VMSTATE_UINT32(er_ep_idx, XHCIInterrupter), - - /* event queue (used if ring is full) */ - VMSTATE_BOOL(er_full, XHCIInterrupter), - VMSTATE_UINT32_TEST(ev_buffer_put, XHCIInterrupter, xhci_er_full), - VMSTATE_UINT32_TEST(ev_buffer_get, XHCIInterrupter, xhci_er_full), - VMSTATE_STRUCT_ARRAY_TEST(ev_buffer, XHCIInterrupter, EV_QUEUE, - xhci_er_full, 1, - vmstate_xhci_event, XHCIEvent), - - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_xhci = { - .name = "xhci", - .version_id = 1, - .post_load = usb_xhci_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCIE_DEVICE(parent_obj, XHCIState), - VMSTATE_MSIX(parent_obj, XHCIState), - - VMSTATE_STRUCT_VARRAY_UINT32(ports, XHCIState, numports, 1, - vmstate_xhci_port, XHCIPort), - VMSTATE_STRUCT_VARRAY_UINT32(slots, XHCIState, numslots, 1, - vmstate_xhci_slot, XHCISlot), - VMSTATE_STRUCT_VARRAY_UINT32(intr, XHCIState, numintrs, 1, - vmstate_xhci_intr, XHCIInterrupter), - - /* Operational Registers */ - VMSTATE_UINT32(usbcmd, XHCIState), - VMSTATE_UINT32(usbsts, XHCIState), - VMSTATE_UINT32(dnctrl, XHCIState), - VMSTATE_UINT32(crcr_low, XHCIState), - VMSTATE_UINT32(crcr_high, XHCIState), - VMSTATE_UINT32(dcbaap_low, XHCIState), - VMSTATE_UINT32(dcbaap_high, XHCIState), - VMSTATE_UINT32(config, XHCIState), - - /* Runtime Registers & state */ - VMSTATE_INT64(mfindex_start, XHCIState), - VMSTATE_TIMER_PTR(mfwrap_timer, XHCIState), - VMSTATE_STRUCT(cmd_ring, XHCIState, 1, vmstate_xhci_ring, XHCIRing), - - VMSTATE_END_OF_LIST() - } -}; - -static Property xhci_properties[] = { - DEFINE_PROP_BIT("msi", XHCIState, flags, XHCI_FLAG_USE_MSI, true), - DEFINE_PROP_BIT("msix", XHCIState, flags, XHCI_FLAG_USE_MSI_X, true), - DEFINE_PROP_BIT("superspeed-ports-first", - XHCIState, flags, XHCI_FLAG_SS_FIRST, true), - DEFINE_PROP_BIT("force-pcie-endcap", XHCIState, flags, - XHCI_FLAG_FORCE_PCIE_ENDCAP, false), - DEFINE_PROP_BIT("streams", XHCIState, flags, - XHCI_FLAG_ENABLE_STREAMS, true), - DEFINE_PROP_UINT32("intrs", XHCIState, numintrs, MAXINTRS), - DEFINE_PROP_UINT32("slots", XHCIState, numslots, MAXSLOTS), - DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4), - DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4), - DEFINE_PROP_END_OF_LIST(), -}; - -static void xhci_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_xhci; - dc->props = xhci_properties; - dc->reset = xhci_reset; - set_bit(DEVICE_CATEGORY_USB, dc->categories); - k->realize = usb_xhci_realize; - k->exit = usb_xhci_exit; - k->vendor_id = PCI_VENDOR_ID_NEC; - k->device_id = PCI_DEVICE_ID_NEC_UPD720200; - k->class_id = PCI_CLASS_SERIAL_USB; - k->revision = 0x03; - k->is_express = 1; -} - -static const TypeInfo xhci_info = { - .name = TYPE_XHCI, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(XHCIState), - .class_init = xhci_class_init, -}; - -static void xhci_register_types(void) -{ - type_register_static(&xhci_info); -} - -type_init(xhci_register_types) diff --git a/qemu/hw/usb/host-legacy.c b/qemu/hw/usb/host-legacy.c deleted file mode 100644 index 3b57e21b5..000000000 --- a/qemu/hw/usb/host-legacy.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Linux host USB redirector - * - * Copyright (c) 2005 Fabrice Bellard - * - * Copyright (c) 2008 Max Krasnyansky - * Support for host device auto connect & disconnect - * Major rewrite to support fully async operation - * - * Copyright 2008 TJ - * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition - * to the legacy /proc/bus/usb USB device discovery and handling - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "hw/usb.h" -#include "hw/usb/host.h" - -/* - * Autoconnect filter - * Format: - * auto:bus:dev[:vid:pid] - * auto:bus.dev[:vid:pid] - * - * bus - bus number (dec, * means any) - * dev - device number (dec, * means any) - * vid - vendor id (hex, * means any) - * pid - product id (hex, * means any) - * - * See 'lsusb' output. - */ -static int parse_filter(const char *spec, struct USBAutoFilter *f) -{ - enum { BUS, DEV, VID, PID, DONE }; - const char *p = spec; - int i; - - f->bus_num = 0; - f->addr = 0; - f->vendor_id = 0; - f->product_id = 0; - - for (i = BUS; i < DONE; i++) { - p = strpbrk(p, ":."); - if (!p) { - break; - } - p++; - - if (*p == '*') { - continue; - } - switch (i) { - case BUS: - f->bus_num = strtol(p, NULL, 10); - break; - case DEV: - f->addr = strtol(p, NULL, 10); - break; - case VID: - f->vendor_id = strtol(p, NULL, 16); - break; - case PID: - f->product_id = strtol(p, NULL, 16); - break; - } - } - - if (i < DEV) { - fprintf(stderr, "husb: invalid auto filter spec %s\n", spec); - return -1; - } - - return 0; -} - -USBDevice *usb_host_device_open(USBBus *bus, const char *devname) -{ - struct USBAutoFilter filter; - USBDevice *dev; - char *p; - - dev = usb_create(bus, "usb-host"); - - if (strstr(devname, "auto:")) { - if (parse_filter(devname, &filter) < 0) { - goto fail; - } - } else { - p = strchr(devname, '.'); - if (p) { - filter.bus_num = strtoul(devname, NULL, 0); - filter.addr = strtoul(p + 1, NULL, 0); - filter.vendor_id = 0; - filter.product_id = 0; - } else { - p = strchr(devname, ':'); - if (p) { - filter.bus_num = 0; - filter.addr = 0; - filter.vendor_id = strtoul(devname, NULL, 16); - filter.product_id = strtoul(p + 1, NULL, 16); - } else { - goto fail; - } - } - } - - qdev_prop_set_uint32(&dev->qdev, "hostbus", filter.bus_num); - qdev_prop_set_uint32(&dev->qdev, "hostaddr", filter.addr); - qdev_prop_set_uint32(&dev->qdev, "vendorid", filter.vendor_id); - qdev_prop_set_uint32(&dev->qdev, "productid", filter.product_id); - return dev; - -fail: - object_unparent(OBJECT(dev)); - return NULL; -} - -static void usb_host_register_types(void) -{ - usb_legacy_register("usb-host", "host", usb_host_device_open); -} - -type_init(usb_host_register_types) diff --git a/qemu/hw/usb/host-libusb.c b/qemu/hw/usb/host-libusb.c deleted file mode 100644 index 6458a9448..000000000 --- a/qemu/hw/usb/host-libusb.c +++ /dev/null @@ -1,1688 +0,0 @@ -/* - * Linux host USB redirector - * - * Copyright (c) 2005 Fabrice Bellard - * - * Copyright (c) 2008 Max Krasnyansky - * Support for host device auto connect & disconnect - * Major rewrite to support fully async operation - * - * Copyright 2008 TJ - * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition - * to the legacy /proc/bus/usb USB device discovery and handling - * - * (c) 2012 Gerd Hoffmann - * Completely rewritten to use libusb instead of usbfs ioctls. - * - * 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 -#include - -#include "qapi/error.h" -#include "qemu-common.h" -#include "monitor/monitor.h" -#include "qemu/error-report.h" -#include "sysemu/sysemu.h" -#include "trace.h" - -#include "hw/usb.h" - -/* ------------------------------------------------------------------------ */ - -#define TYPE_USB_HOST_DEVICE "usb-host" -#define USB_HOST_DEVICE(obj) \ - OBJECT_CHECK(USBHostDevice, (obj), TYPE_USB_HOST_DEVICE) - -typedef struct USBHostDevice USBHostDevice; -typedef struct USBHostRequest USBHostRequest; -typedef struct USBHostIsoXfer USBHostIsoXfer; -typedef struct USBHostIsoRing USBHostIsoRing; - -struct USBAutoFilter { - uint32_t bus_num; - uint32_t addr; - char *port; - uint32_t vendor_id; - uint32_t product_id; -}; - -enum USBHostDeviceOptions { - USB_HOST_OPT_PIPELINE, -}; - -struct USBHostDevice { - USBDevice parent_obj; - - /* properties */ - struct USBAutoFilter match; - int32_t bootindex; - uint32_t iso_urb_count; - uint32_t iso_urb_frames; - uint32_t options; - uint32_t loglevel; - - /* state */ - QTAILQ_ENTRY(USBHostDevice) next; - int seen, errcount; - int bus_num; - int addr; - char port[16]; - - libusb_device *dev; - libusb_device_handle *dh; - struct libusb_device_descriptor ddesc; - - struct { - bool detached; - bool claimed; - } ifs[USB_MAX_INTERFACES]; - - /* callbacks & friends */ - QEMUBH *bh_nodev; - QEMUBH *bh_postld; - Notifier exit; - - /* request queues */ - QTAILQ_HEAD(, USBHostRequest) requests; - QTAILQ_HEAD(, USBHostIsoRing) isorings; -}; - -struct USBHostRequest { - USBHostDevice *host; - USBPacket *p; - bool in; - struct libusb_transfer *xfer; - unsigned char *buffer; - unsigned char *cbuf; - unsigned int clen; - bool usb3ep0quirk; - QTAILQ_ENTRY(USBHostRequest) next; -}; - -struct USBHostIsoXfer { - USBHostIsoRing *ring; - struct libusb_transfer *xfer; - bool copy_complete; - unsigned int packet; - QTAILQ_ENTRY(USBHostIsoXfer) next; -}; - -struct USBHostIsoRing { - USBHostDevice *host; - USBEndpoint *ep; - QTAILQ_HEAD(, USBHostIsoXfer) unused; - QTAILQ_HEAD(, USBHostIsoXfer) inflight; - QTAILQ_HEAD(, USBHostIsoXfer) copy; - QTAILQ_ENTRY(USBHostIsoRing) next; -}; - -static QTAILQ_HEAD(, USBHostDevice) hostdevs = - QTAILQ_HEAD_INITIALIZER(hostdevs); - -static void usb_host_auto_check(void *unused); -static void usb_host_release_interfaces(USBHostDevice *s); -static void usb_host_nodev(USBHostDevice *s); -static void usb_host_detach_kernel(USBHostDevice *s); -static void usb_host_attach_kernel(USBHostDevice *s); - -/* ------------------------------------------------------------------------ */ - -#ifndef LIBUSB_LOG_LEVEL_WARNING /* older libusb didn't define these */ -#define LIBUSB_LOG_LEVEL_WARNING 2 -#endif - -/* ------------------------------------------------------------------------ */ - -#define CONTROL_TIMEOUT 10000 /* 10 sec */ -#define BULK_TIMEOUT 0 /* unlimited */ -#define INTR_TIMEOUT 0 /* unlimited */ - -#if LIBUSBX_API_VERSION >= 0x01000103 -# define HAVE_STREAMS 1 -#endif - -static const char *speed_name[] = { - [LIBUSB_SPEED_UNKNOWN] = "?", - [LIBUSB_SPEED_LOW] = "1.5", - [LIBUSB_SPEED_FULL] = "12", - [LIBUSB_SPEED_HIGH] = "480", - [LIBUSB_SPEED_SUPER] = "5000", -}; - -static const unsigned int speed_map[] = { - [LIBUSB_SPEED_LOW] = USB_SPEED_LOW, - [LIBUSB_SPEED_FULL] = USB_SPEED_FULL, - [LIBUSB_SPEED_HIGH] = USB_SPEED_HIGH, - [LIBUSB_SPEED_SUPER] = USB_SPEED_SUPER, -}; - -static const unsigned int status_map[] = { - [LIBUSB_TRANSFER_COMPLETED] = USB_RET_SUCCESS, - [LIBUSB_TRANSFER_ERROR] = USB_RET_IOERROR, - [LIBUSB_TRANSFER_TIMED_OUT] = USB_RET_IOERROR, - [LIBUSB_TRANSFER_CANCELLED] = USB_RET_IOERROR, - [LIBUSB_TRANSFER_STALL] = USB_RET_STALL, - [LIBUSB_TRANSFER_NO_DEVICE] = USB_RET_NODEV, - [LIBUSB_TRANSFER_OVERFLOW] = USB_RET_BABBLE, -}; - -static const char *err_names[] = { - [-LIBUSB_ERROR_IO] = "IO", - [-LIBUSB_ERROR_INVALID_PARAM] = "INVALID_PARAM", - [-LIBUSB_ERROR_ACCESS] = "ACCESS", - [-LIBUSB_ERROR_NO_DEVICE] = "NO_DEVICE", - [-LIBUSB_ERROR_NOT_FOUND] = "NOT_FOUND", - [-LIBUSB_ERROR_BUSY] = "BUSY", - [-LIBUSB_ERROR_TIMEOUT] = "TIMEOUT", - [-LIBUSB_ERROR_OVERFLOW] = "OVERFLOW", - [-LIBUSB_ERROR_PIPE] = "PIPE", - [-LIBUSB_ERROR_INTERRUPTED] = "INTERRUPTED", - [-LIBUSB_ERROR_NO_MEM] = "NO_MEM", - [-LIBUSB_ERROR_NOT_SUPPORTED] = "NOT_SUPPORTED", - [-LIBUSB_ERROR_OTHER] = "OTHER", -}; - -static libusb_context *ctx; -static uint32_t loglevel; - -static void usb_host_handle_fd(void *opaque) -{ - struct timeval tv = { 0, 0 }; - libusb_handle_events_timeout(ctx, &tv); -} - -static void usb_host_add_fd(int fd, short events, void *user_data) -{ - qemu_set_fd_handler(fd, - (events & POLLIN) ? usb_host_handle_fd : NULL, - (events & POLLOUT) ? usb_host_handle_fd : NULL, - ctx); -} - -static void usb_host_del_fd(int fd, void *user_data) -{ - qemu_set_fd_handler(fd, NULL, NULL, NULL); -} - -static int usb_host_init(void) -{ - const struct libusb_pollfd **poll; - int i, rc; - - if (ctx) { - return 0; - } - rc = libusb_init(&ctx); - if (rc != 0) { - return -1; - } - libusb_set_debug(ctx, loglevel); - - libusb_set_pollfd_notifiers(ctx, usb_host_add_fd, - usb_host_del_fd, - ctx); - poll = libusb_get_pollfds(ctx); - if (poll) { - for (i = 0; poll[i] != NULL; i++) { - usb_host_add_fd(poll[i]->fd, poll[i]->events, ctx); - } - } - free(poll); - return 0; -} - -static int usb_host_get_port(libusb_device *dev, char *port, size_t len) -{ - uint8_t path[7]; - size_t off; - int rc, i; - -#if LIBUSBX_API_VERSION >= 0x01000102 - rc = libusb_get_port_numbers(dev, path, 7); -#else - rc = libusb_get_port_path(ctx, dev, path, 7); -#endif - if (rc < 0) { - return 0; - } - off = snprintf(port, len, "%d", path[0]); - for (i = 1; i < rc; i++) { - off += snprintf(port+off, len-off, ".%d", path[i]); - } - return off; -} - -static void usb_host_libusb_error(const char *func, int rc) -{ - const char *errname; - - if (rc >= 0) { - return; - } - - if (-rc < ARRAY_SIZE(err_names) && err_names[-rc]) { - errname = err_names[-rc]; - } else { - errname = "?"; - } - error_report("%s: %d [%s]", func, rc, errname); -} - -/* ------------------------------------------------------------------------ */ - -static bool usb_host_use_combining(USBEndpoint *ep) -{ - int type; - - if (!ep->pipeline) { - return false; - } - if (ep->pid != USB_TOKEN_IN) { - return false; - } - type = usb_ep_get_type(ep->dev, ep->pid, ep->nr); - if (type != USB_ENDPOINT_XFER_BULK) { - return false; - } - return true; -} - -/* ------------------------------------------------------------------------ */ - -static USBHostRequest *usb_host_req_alloc(USBHostDevice *s, USBPacket *p, - bool in, size_t bufsize) -{ - USBHostRequest *r = g_new0(USBHostRequest, 1); - - r->host = s; - r->p = p; - r->in = in; - r->xfer = libusb_alloc_transfer(0); - if (bufsize) { - r->buffer = g_malloc(bufsize); - } - QTAILQ_INSERT_TAIL(&s->requests, r, next); - return r; -} - -static void usb_host_req_free(USBHostRequest *r) -{ - if (r->host) { - QTAILQ_REMOVE(&r->host->requests, r, next); - } - libusb_free_transfer(r->xfer); - g_free(r->buffer); - g_free(r); -} - -static USBHostRequest *usb_host_req_find(USBHostDevice *s, USBPacket *p) -{ - USBHostRequest *r; - - QTAILQ_FOREACH(r, &s->requests, next) { - if (r->p == p) { - return r; - } - } - return NULL; -} - -static void usb_host_req_complete_ctrl(struct libusb_transfer *xfer) -{ - USBHostRequest *r = xfer->user_data; - USBHostDevice *s = r->host; - bool disconnect = (xfer->status == LIBUSB_TRANSFER_NO_DEVICE); - - if (r->p == NULL) { - goto out; /* request was canceled */ - } - - r->p->status = status_map[xfer->status]; - r->p->actual_length = xfer->actual_length; - if (r->in && xfer->actual_length) { - memcpy(r->cbuf, r->buffer + 8, xfer->actual_length); - - /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices - * to work redirected to a not superspeed capable hcd */ - if (r->usb3ep0quirk && xfer->actual_length >= 18 && - r->cbuf[7] == 9) { - r->cbuf[7] = 64; - } - } - trace_usb_host_req_complete(s->bus_num, s->addr, r->p, - r->p->status, r->p->actual_length); - usb_generic_async_ctrl_complete(USB_DEVICE(s), r->p); - -out: - usb_host_req_free(r); - if (disconnect) { - usb_host_nodev(s); - } -} - -static void usb_host_req_complete_data(struct libusb_transfer *xfer) -{ - USBHostRequest *r = xfer->user_data; - USBHostDevice *s = r->host; - bool disconnect = (xfer->status == LIBUSB_TRANSFER_NO_DEVICE); - - if (r->p == NULL) { - goto out; /* request was canceled */ - } - - r->p->status = status_map[xfer->status]; - if (r->in && xfer->actual_length) { - usb_packet_copy(r->p, r->buffer, xfer->actual_length); - } - trace_usb_host_req_complete(s->bus_num, s->addr, r->p, - r->p->status, r->p->actual_length); - if (usb_host_use_combining(r->p->ep)) { - usb_combined_input_packet_complete(USB_DEVICE(s), r->p); - } else { - usb_packet_complete(USB_DEVICE(s), r->p); - } - -out: - usb_host_req_free(r); - if (disconnect) { - usb_host_nodev(s); - } -} - -static void usb_host_req_abort(USBHostRequest *r) -{ - USBHostDevice *s = r->host; - bool inflight = (r->p && r->p->state == USB_PACKET_ASYNC); - - if (inflight) { - r->p->status = USB_RET_NODEV; - trace_usb_host_req_complete(s->bus_num, s->addr, r->p, - r->p->status, r->p->actual_length); - if (r->p->ep->nr == 0) { - usb_generic_async_ctrl_complete(USB_DEVICE(s), r->p); - } else { - usb_packet_complete(USB_DEVICE(s), r->p); - } - r->p = NULL; - } - - QTAILQ_REMOVE(&r->host->requests, r, next); - r->host = NULL; - - if (inflight) { - libusb_cancel_transfer(r->xfer); - } -} - -/* ------------------------------------------------------------------------ */ - -static void usb_host_req_complete_iso(struct libusb_transfer *transfer) -{ - USBHostIsoXfer *xfer = transfer->user_data; - - if (!xfer) { - /* USBHostIsoXfer released while inflight */ - g_free(transfer->buffer); - libusb_free_transfer(transfer); - return; - } - - QTAILQ_REMOVE(&xfer->ring->inflight, xfer, next); - if (QTAILQ_EMPTY(&xfer->ring->inflight)) { - USBHostDevice *s = xfer->ring->host; - trace_usb_host_iso_stop(s->bus_num, s->addr, xfer->ring->ep->nr); - } - 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); - } -} - -static USBHostIsoRing *usb_host_iso_alloc(USBHostDevice *s, USBEndpoint *ep) -{ - USBHostIsoRing *ring = g_new0(USBHostIsoRing, 1); - USBHostIsoXfer *xfer; - /* FIXME: check interval (for now assume one xfer per frame) */ - int packets = s->iso_urb_frames; - int i; - - ring->host = s; - ring->ep = ep; - QTAILQ_INIT(&ring->unused); - QTAILQ_INIT(&ring->inflight); - QTAILQ_INIT(&ring->copy); - QTAILQ_INSERT_TAIL(&s->isorings, ring, next); - - for (i = 0; i < s->iso_urb_count; i++) { - xfer = g_new0(USBHostIsoXfer, 1); - xfer->ring = ring; - xfer->xfer = libusb_alloc_transfer(packets); - xfer->xfer->dev_handle = s->dh; - xfer->xfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; - - xfer->xfer->endpoint = ring->ep->nr; - if (ring->ep->pid == USB_TOKEN_IN) { - xfer->xfer->endpoint |= USB_DIR_IN; - } - xfer->xfer->callback = usb_host_req_complete_iso; - xfer->xfer->user_data = xfer; - - xfer->xfer->num_iso_packets = packets; - xfer->xfer->length = ring->ep->max_packet_size * packets; - xfer->xfer->buffer = g_malloc0(xfer->xfer->length); - - QTAILQ_INSERT_TAIL(&ring->unused, xfer, next); - } - - return ring; -} - -static USBHostIsoRing *usb_host_iso_find(USBHostDevice *s, USBEndpoint *ep) -{ - USBHostIsoRing *ring; - - QTAILQ_FOREACH(ring, &s->isorings, next) { - if (ring->ep == ep) { - return ring; - } - } - return NULL; -} - -static void usb_host_iso_reset_xfer(USBHostIsoXfer *xfer) -{ - libusb_set_iso_packet_lengths(xfer->xfer, - xfer->ring->ep->max_packet_size); - xfer->packet = 0; - xfer->copy_complete = false; -} - -static void usb_host_iso_free_xfer(USBHostIsoXfer *xfer, bool inflight) -{ - if (inflight) { - xfer->xfer->user_data = NULL; - } else { - g_free(xfer->xfer->buffer); - libusb_free_transfer(xfer->xfer); - } - g_free(xfer); -} - -static void usb_host_iso_free(USBHostIsoRing *ring) -{ - USBHostIsoXfer *xfer; - - while ((xfer = QTAILQ_FIRST(&ring->inflight)) != NULL) { - QTAILQ_REMOVE(&ring->inflight, xfer, next); - usb_host_iso_free_xfer(xfer, true); - } - while ((xfer = QTAILQ_FIRST(&ring->unused)) != NULL) { - QTAILQ_REMOVE(&ring->unused, xfer, next); - usb_host_iso_free_xfer(xfer, false); - } - while ((xfer = QTAILQ_FIRST(&ring->copy)) != NULL) { - QTAILQ_REMOVE(&ring->copy, xfer, next); - usb_host_iso_free_xfer(xfer, false); - } - - QTAILQ_REMOVE(&ring->host->isorings, ring, next); - g_free(ring); -} - -static void usb_host_iso_free_all(USBHostDevice *s) -{ - USBHostIsoRing *ring; - - while ((ring = QTAILQ_FIRST(&s->isorings)) != NULL) { - usb_host_iso_free(ring); - } -} - -static bool usb_host_iso_data_copy(USBHostIsoXfer *xfer, USBPacket *p) -{ - unsigned int psize; - unsigned char *buf; - - buf = libusb_get_iso_packet_buffer_simple(xfer->xfer, xfer->packet); - if (p->pid == USB_TOKEN_OUT) { - psize = p->iov.size; - if (psize > xfer->ring->ep->max_packet_size) { - /* should not happen (guest bug) */ - psize = xfer->ring->ep->max_packet_size; - } - xfer->xfer->iso_packet_desc[xfer->packet].length = psize; - } else { - psize = xfer->xfer->iso_packet_desc[xfer->packet].actual_length; - if (psize > p->iov.size) { - /* should not happen (guest bug) */ - psize = p->iov.size; - } - } - usb_packet_copy(p, buf, psize); - xfer->packet++; - xfer->copy_complete = (xfer->packet == xfer->xfer->num_iso_packets); - return xfer->copy_complete; -} - -static void usb_host_iso_data_in(USBHostDevice *s, USBPacket *p) -{ - USBHostIsoRing *ring; - USBHostIsoXfer *xfer; - bool disconnect = false; - int rc; - - ring = usb_host_iso_find(s, p->ep); - if (ring == NULL) { - ring = usb_host_iso_alloc(s, p->ep); - } - - /* copy data to guest */ - xfer = QTAILQ_FIRST(&ring->copy); - if (xfer != NULL) { - if (usb_host_iso_data_copy(xfer, p)) { - QTAILQ_REMOVE(&ring->copy, xfer, next); - QTAILQ_INSERT_TAIL(&ring->unused, xfer, next); - } - } - - /* submit empty bufs to host */ - while ((xfer = QTAILQ_FIRST(&ring->unused)) != NULL) { - QTAILQ_REMOVE(&ring->unused, xfer, next); - usb_host_iso_reset_xfer(xfer); - rc = libusb_submit_transfer(xfer->xfer); - if (rc != 0) { - usb_host_libusb_error("libusb_submit_transfer [iso]", rc); - QTAILQ_INSERT_TAIL(&ring->unused, xfer, next); - if (rc == LIBUSB_ERROR_NO_DEVICE) { - disconnect = true; - } - break; - } - if (QTAILQ_EMPTY(&ring->inflight)) { - trace_usb_host_iso_start(s->bus_num, s->addr, p->ep->nr); - } - QTAILQ_INSERT_TAIL(&ring->inflight, xfer, next); - } - - if (disconnect) { - usb_host_nodev(s); - } -} - -static void usb_host_iso_data_out(USBHostDevice *s, USBPacket *p) -{ - USBHostIsoRing *ring; - USBHostIsoXfer *xfer; - bool disconnect = false; - int rc, filled = 0; - - ring = usb_host_iso_find(s, p->ep); - if (ring == NULL) { - ring = usb_host_iso_alloc(s, p->ep); - } - - /* copy data from guest */ - xfer = QTAILQ_FIRST(&ring->copy); - while (xfer != NULL && xfer->copy_complete) { - filled++; - xfer = QTAILQ_NEXT(xfer, next); - } - if (xfer == NULL) { - xfer = QTAILQ_FIRST(&ring->unused); - if (xfer == NULL) { - trace_usb_host_iso_out_of_bufs(s->bus_num, s->addr, p->ep->nr); - return; - } - QTAILQ_REMOVE(&ring->unused, xfer, next); - usb_host_iso_reset_xfer(xfer); - QTAILQ_INSERT_TAIL(&ring->copy, xfer, next); - } - usb_host_iso_data_copy(xfer, p); - - if (QTAILQ_EMPTY(&ring->inflight)) { - /* wait until half of our buffers are filled - before kicking the iso out stream */ - if (filled*2 < s->iso_urb_count) { - return; - } - } - - /* submit filled bufs to host */ - while ((xfer = QTAILQ_FIRST(&ring->copy)) != NULL && - xfer->copy_complete) { - QTAILQ_REMOVE(&ring->copy, xfer, next); - rc = libusb_submit_transfer(xfer->xfer); - if (rc != 0) { - usb_host_libusb_error("libusb_submit_transfer [iso]", rc); - QTAILQ_INSERT_TAIL(&ring->unused, xfer, next); - if (rc == LIBUSB_ERROR_NO_DEVICE) { - disconnect = true; - } - break; - } - if (QTAILQ_EMPTY(&ring->inflight)) { - trace_usb_host_iso_start(s->bus_num, s->addr, p->ep->nr); - } - QTAILQ_INSERT_TAIL(&ring->inflight, xfer, next); - } - - if (disconnect) { - usb_host_nodev(s); - } -} - -/* ------------------------------------------------------------------------ */ - -static void usb_host_speed_compat(USBHostDevice *s) -{ - USBDevice *udev = USB_DEVICE(s); - struct libusb_config_descriptor *conf; - const struct libusb_interface_descriptor *intf; - const struct libusb_endpoint_descriptor *endp; -#ifdef HAVE_STREAMS - struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp; -#endif - bool compat_high = true; - bool compat_full = true; - uint8_t type; - int rc, c, i, a, e; - - for (c = 0;; c++) { - rc = libusb_get_config_descriptor(s->dev, c, &conf); - if (rc != 0) { - break; - } - for (i = 0; i < conf->bNumInterfaces; i++) { - for (a = 0; a < conf->interface[i].num_altsetting; a++) { - intf = &conf->interface[i].altsetting[a]; - for (e = 0; e < intf->bNumEndpoints; e++) { - endp = &intf->endpoint[e]; - type = endp->bmAttributes & 0x3; - switch (type) { - case 0x01: /* ISO */ - compat_full = false; - compat_high = false; - break; - case 0x02: /* BULK */ -#ifdef HAVE_STREAMS - rc = libusb_get_ss_endpoint_companion_descriptor - (ctx, endp, &endp_ss_comp); - if (rc == LIBUSB_SUCCESS) { - libusb_free_ss_endpoint_companion_descriptor - (endp_ss_comp); - compat_full = false; - compat_high = false; - } -#endif - break; - case 0x03: /* INTERRUPT */ - if (endp->wMaxPacketSize > 64) { - compat_full = false; - } - if (endp->wMaxPacketSize > 1024) { - compat_high = false; - } - break; - } - } - } - } - libusb_free_config_descriptor(conf); - } - - udev->speedmask = (1 << udev->speed); - if (udev->speed == USB_SPEED_SUPER && compat_high) { - udev->speedmask |= USB_SPEED_MASK_HIGH; - } - if (udev->speed == USB_SPEED_SUPER && compat_full) { - udev->speedmask |= USB_SPEED_MASK_FULL; - } - if (udev->speed == USB_SPEED_HIGH && compat_full) { - udev->speedmask |= USB_SPEED_MASK_FULL; - } -} - -static void usb_host_ep_update(USBHostDevice *s) -{ - static const char *tname[] = { - [USB_ENDPOINT_XFER_CONTROL] = "control", - [USB_ENDPOINT_XFER_ISOC] = "isoc", - [USB_ENDPOINT_XFER_BULK] = "bulk", - [USB_ENDPOINT_XFER_INT] = "int", - }; - USBDevice *udev = USB_DEVICE(s); - struct libusb_config_descriptor *conf; - const struct libusb_interface_descriptor *intf; - const struct libusb_endpoint_descriptor *endp; -#ifdef HAVE_STREAMS - struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp; -#endif - uint8_t devep, type; - int pid, ep; - int rc, i, e; - - usb_ep_reset(udev); - rc = libusb_get_active_config_descriptor(s->dev, &conf); - if (rc != 0) { - return; - } - trace_usb_host_parse_config(s->bus_num, s->addr, - conf->bConfigurationValue, true); - - for (i = 0; i < conf->bNumInterfaces; i++) { - assert(udev->altsetting[i] < conf->interface[i].num_altsetting); - intf = &conf->interface[i].altsetting[udev->altsetting[i]]; - trace_usb_host_parse_interface(s->bus_num, s->addr, - intf->bInterfaceNumber, - intf->bAlternateSetting, true); - for (e = 0; e < intf->bNumEndpoints; e++) { - endp = &intf->endpoint[e]; - - devep = endp->bEndpointAddress; - pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT; - ep = devep & 0xf; - type = endp->bmAttributes & 0x3; - - if (ep == 0) { - trace_usb_host_parse_error(s->bus_num, s->addr, - "invalid endpoint address"); - return; - } - if (usb_ep_get_type(udev, pid, ep) != USB_ENDPOINT_XFER_INVALID) { - trace_usb_host_parse_error(s->bus_num, s->addr, - "duplicate endpoint address"); - return; - } - - trace_usb_host_parse_endpoint(s->bus_num, s->addr, ep, - (devep & USB_DIR_IN) ? "in" : "out", - tname[type], true); - usb_ep_set_max_packet_size(udev, pid, ep, - endp->wMaxPacketSize); - usb_ep_set_type(udev, pid, ep, type); - usb_ep_set_ifnum(udev, pid, ep, i); - usb_ep_set_halted(udev, pid, ep, 0); -#ifdef HAVE_STREAMS - if (type == LIBUSB_TRANSFER_TYPE_BULK && - libusb_get_ss_endpoint_companion_descriptor(ctx, endp, - &endp_ss_comp) == LIBUSB_SUCCESS) { - usb_ep_set_max_streams(udev, pid, ep, - endp_ss_comp->bmAttributes); - libusb_free_ss_endpoint_companion_descriptor(endp_ss_comp); - } -#endif - } - } - - libusb_free_config_descriptor(conf); -} - -static int usb_host_open(USBHostDevice *s, libusb_device *dev) -{ - USBDevice *udev = USB_DEVICE(s); - int bus_num = libusb_get_bus_number(dev); - int addr = libusb_get_device_address(dev); - int rc; - Error *local_err = NULL; - - trace_usb_host_open_started(bus_num, addr); - - if (s->dh != NULL) { - goto fail; - } - rc = libusb_open(dev, &s->dh); - if (rc != 0) { - goto fail; - } - - s->dev = dev; - s->bus_num = bus_num; - s->addr = addr; - - usb_host_detach_kernel(s); - - libusb_get_device_descriptor(dev, &s->ddesc); - usb_host_get_port(s->dev, s->port, sizeof(s->port)); - - usb_ep_init(udev); - usb_host_ep_update(s); - - udev->speed = speed_map[libusb_get_device_speed(dev)]; - usb_host_speed_compat(s); - - if (s->ddesc.iProduct) { - libusb_get_string_descriptor_ascii(s->dh, s->ddesc.iProduct, - (unsigned char *)udev->product_desc, - sizeof(udev->product_desc)); - } else { - snprintf(udev->product_desc, sizeof(udev->product_desc), - "host:%d.%d", bus_num, addr); - } - - usb_device_attach(udev, &local_err); - if (local_err) { - error_report_err(local_err); - goto fail; - } - - trace_usb_host_open_success(bus_num, addr); - return 0; - -fail: - trace_usb_host_open_failure(bus_num, addr); - if (s->dh != NULL) { - usb_host_release_interfaces(s); - libusb_reset_device(s->dh); - usb_host_attach_kernel(s); - libusb_close(s->dh); - s->dh = NULL; - s->dev = NULL; - } - return -1; -} - -static void usb_host_abort_xfers(USBHostDevice *s) -{ - USBHostRequest *r, *rtmp; - - QTAILQ_FOREACH_SAFE(r, &s->requests, next, rtmp) { - usb_host_req_abort(r); - } -} - -static int usb_host_close(USBHostDevice *s) -{ - USBDevice *udev = USB_DEVICE(s); - - if (s->dh == NULL) { - return -1; - } - - trace_usb_host_close(s->bus_num, s->addr); - - usb_host_abort_xfers(s); - usb_host_iso_free_all(s); - - if (udev->attached) { - usb_device_detach(udev); - } - - usb_host_release_interfaces(s); - libusb_reset_device(s->dh); - usb_host_attach_kernel(s); - libusb_close(s->dh); - s->dh = NULL; - s->dev = NULL; - - usb_host_auto_check(NULL); - return 0; -} - -static void usb_host_nodev_bh(void *opaque) -{ - USBHostDevice *s = opaque; - usb_host_close(s); -} - -static void usb_host_nodev(USBHostDevice *s) -{ - if (!s->bh_nodev) { - s->bh_nodev = qemu_bh_new(usb_host_nodev_bh, s); - } - qemu_bh_schedule(s->bh_nodev); -} - -static void usb_host_exit_notifier(struct Notifier *n, void *data) -{ - USBHostDevice *s = container_of(n, USBHostDevice, exit); - - if (s->dh) { - usb_host_release_interfaces(s); - usb_host_attach_kernel(s); - } -} - -static void usb_host_realize(USBDevice *udev, Error **errp) -{ - USBHostDevice *s = USB_HOST_DEVICE(udev); - - if (s->match.vendor_id > 0xffff) { - error_setg(errp, "vendorid out of range"); - return; - } - if (s->match.product_id > 0xffff) { - error_setg(errp, "productid out of range"); - return; - } - if (s->match.addr > 127) { - error_setg(errp, "hostaddr out of range"); - return; - } - - loglevel = s->loglevel; - udev->flags |= (1 << USB_DEV_FLAG_IS_HOST); - udev->auto_attach = 0; - QTAILQ_INIT(&s->requests); - QTAILQ_INIT(&s->isorings); - - s->exit.notify = usb_host_exit_notifier; - qemu_add_exit_notifier(&s->exit); - - QTAILQ_INSERT_TAIL(&hostdevs, s, next); - usb_host_auto_check(NULL); -} - -static void usb_host_instance_init(Object *obj) -{ - USBDevice *udev = USB_DEVICE(obj); - USBHostDevice *s = USB_HOST_DEVICE(udev); - - device_add_bootindex_property(obj, &s->bootindex, - "bootindex", NULL, - &udev->qdev, NULL); -} - -static void usb_host_handle_destroy(USBDevice *udev) -{ - USBHostDevice *s = USB_HOST_DEVICE(udev); - - qemu_remove_exit_notifier(&s->exit); - QTAILQ_REMOVE(&hostdevs, s, next); - usb_host_close(s); -} - -static void usb_host_cancel_packet(USBDevice *udev, USBPacket *p) -{ - USBHostDevice *s = USB_HOST_DEVICE(udev); - USBHostRequest *r; - - if (p->combined) { - usb_combined_packet_cancel(udev, p); - return; - } - - trace_usb_host_req_canceled(s->bus_num, s->addr, p); - - r = usb_host_req_find(s, p); - if (r && r->p) { - r->p = NULL; /* mark as dead */ - libusb_cancel_transfer(r->xfer); - } -} - -static void usb_host_detach_kernel(USBHostDevice *s) -{ - struct libusb_config_descriptor *conf; - int rc, i; - - rc = libusb_get_active_config_descriptor(s->dev, &conf); - if (rc != 0) { - return; - } - for (i = 0; i < conf->bNumInterfaces; i++) { - rc = libusb_kernel_driver_active(s->dh, i); - usb_host_libusb_error("libusb_kernel_driver_active", rc); - if (rc != 1) { - continue; - } - trace_usb_host_detach_kernel(s->bus_num, s->addr, i); - rc = libusb_detach_kernel_driver(s->dh, i); - usb_host_libusb_error("libusb_detach_kernel_driver", rc); - s->ifs[i].detached = true; - } - libusb_free_config_descriptor(conf); -} - -static void usb_host_attach_kernel(USBHostDevice *s) -{ - struct libusb_config_descriptor *conf; - int rc, i; - - rc = libusb_get_active_config_descriptor(s->dev, &conf); - if (rc != 0) { - return; - } - for (i = 0; i < conf->bNumInterfaces; i++) { - if (!s->ifs[i].detached) { - continue; - } - trace_usb_host_attach_kernel(s->bus_num, s->addr, i); - libusb_attach_kernel_driver(s->dh, i); - s->ifs[i].detached = false; - } - libusb_free_config_descriptor(conf); -} - -static int usb_host_claim_interfaces(USBHostDevice *s, int configuration) -{ - USBDevice *udev = USB_DEVICE(s); - struct libusb_config_descriptor *conf; - int rc, i; - - for (i = 0; i < USB_MAX_INTERFACES; i++) { - udev->altsetting[i] = 0; - } - udev->ninterfaces = 0; - udev->configuration = 0; - - usb_host_detach_kernel(s); - - rc = libusb_get_active_config_descriptor(s->dev, &conf); - if (rc != 0) { - if (rc == LIBUSB_ERROR_NOT_FOUND) { - /* address state - ignore */ - return USB_RET_SUCCESS; - } - return USB_RET_STALL; - } - - for (i = 0; i < conf->bNumInterfaces; i++) { - trace_usb_host_claim_interface(s->bus_num, s->addr, configuration, i); - rc = libusb_claim_interface(s->dh, i); - usb_host_libusb_error("libusb_claim_interface", rc); - if (rc != 0) { - return USB_RET_STALL; - } - s->ifs[i].claimed = true; - } - - udev->ninterfaces = conf->bNumInterfaces; - udev->configuration = configuration; - - libusb_free_config_descriptor(conf); - return USB_RET_SUCCESS; -} - -static void usb_host_release_interfaces(USBHostDevice *s) -{ - USBDevice *udev = USB_DEVICE(s); - int i, rc; - - for (i = 0; i < udev->ninterfaces; i++) { - if (!s->ifs[i].claimed) { - continue; - } - trace_usb_host_release_interface(s->bus_num, s->addr, i); - rc = libusb_release_interface(s->dh, i); - usb_host_libusb_error("libusb_release_interface", rc); - s->ifs[i].claimed = false; - } -} - -static void usb_host_set_address(USBHostDevice *s, int addr) -{ - USBDevice *udev = USB_DEVICE(s); - - trace_usb_host_set_address(s->bus_num, s->addr, addr); - udev->addr = addr; -} - -static void usb_host_set_config(USBHostDevice *s, int config, USBPacket *p) -{ - int rc; - - trace_usb_host_set_config(s->bus_num, s->addr, config); - - usb_host_release_interfaces(s); - rc = libusb_set_configuration(s->dh, config); - if (rc != 0) { - usb_host_libusb_error("libusb_set_configuration", rc); - p->status = USB_RET_STALL; - if (rc == LIBUSB_ERROR_NO_DEVICE) { - usb_host_nodev(s); - } - return; - } - p->status = usb_host_claim_interfaces(s, config); - if (p->status != USB_RET_SUCCESS) { - return; - } - usb_host_ep_update(s); -} - -static void usb_host_set_interface(USBHostDevice *s, int iface, int alt, - USBPacket *p) -{ - USBDevice *udev = USB_DEVICE(s); - int rc; - - trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt); - - usb_host_iso_free_all(s); - - if (iface >= USB_MAX_INTERFACES) { - p->status = USB_RET_STALL; - return; - } - - rc = libusb_set_interface_alt_setting(s->dh, iface, alt); - if (rc != 0) { - usb_host_libusb_error("libusb_set_interface_alt_setting", rc); - p->status = USB_RET_STALL; - if (rc == LIBUSB_ERROR_NO_DEVICE) { - usb_host_nodev(s); - } - return; - } - - udev->altsetting[iface] = alt; - usb_host_ep_update(s); -} - -static void usb_host_handle_control(USBDevice *udev, USBPacket *p, - int request, int value, int index, - int length, uint8_t *data) -{ - USBHostDevice *s = USB_HOST_DEVICE(udev); - USBHostRequest *r; - int rc; - - trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index); - - if (s->dh == NULL) { - p->status = USB_RET_NODEV; - trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status); - return; - } - - switch (request) { - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - usb_host_set_address(s, value); - trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status); - return; - - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - usb_host_set_config(s, value & 0xff, p); - trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status); - return; - - case InterfaceOutRequest | USB_REQ_SET_INTERFACE: - usb_host_set_interface(s, index, value, p); - trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status); - return; - - case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == 0) { /* clear halt */ - int pid = (index & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT; - libusb_clear_halt(s->dh, index); - usb_ep_set_halted(udev, pid, index & 0x0f, 0); - trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status); - return; - } - } - - r = usb_host_req_alloc(s, p, (request >> 8) & USB_DIR_IN, length + 8); - r->cbuf = data; - r->clen = length; - memcpy(r->buffer, udev->setup_buf, 8); - if (!r->in) { - memcpy(r->buffer + 8, r->cbuf, r->clen); - } - - /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices - * to work redirected to a not superspeed capable hcd */ - if ((udev->speedmask & USB_SPEED_MASK_SUPER) && - !(udev->port->speedmask & USB_SPEED_MASK_SUPER) && - request == 0x8006 && value == 0x100 && index == 0) { - r->usb3ep0quirk = true; - } - - libusb_fill_control_transfer(r->xfer, s->dh, r->buffer, - usb_host_req_complete_ctrl, r, - CONTROL_TIMEOUT); - rc = libusb_submit_transfer(r->xfer); - if (rc != 0) { - p->status = USB_RET_NODEV; - trace_usb_host_req_complete(s->bus_num, s->addr, p, - p->status, p->actual_length); - if (rc == LIBUSB_ERROR_NO_DEVICE) { - usb_host_nodev(s); - } - return; - } - - p->status = USB_RET_ASYNC; -} - -static void usb_host_handle_data(USBDevice *udev, USBPacket *p) -{ - USBHostDevice *s = USB_HOST_DEVICE(udev); - USBHostRequest *r; - size_t size; - int ep, rc; - - if (usb_host_use_combining(p->ep) && p->state == USB_PACKET_SETUP) { - p->status = USB_RET_ADD_TO_QUEUE; - return; - } - - trace_usb_host_req_data(s->bus_num, s->addr, p, - p->pid == USB_TOKEN_IN, - p->ep->nr, p->iov.size); - - if (s->dh == NULL) { - p->status = USB_RET_NODEV; - trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status); - return; - } - if (p->ep->halted) { - p->status = USB_RET_STALL; - trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status); - return; - } - - switch (usb_ep_get_type(udev, p->pid, p->ep->nr)) { - case USB_ENDPOINT_XFER_BULK: - size = usb_packet_size(p); - r = usb_host_req_alloc(s, p, p->pid == USB_TOKEN_IN, size); - if (!r->in) { - usb_packet_copy(p, r->buffer, size); - } - ep = p->ep->nr | (r->in ? USB_DIR_IN : 0); - if (p->stream) { -#ifdef HAVE_STREAMS - libusb_fill_bulk_stream_transfer(r->xfer, s->dh, ep, p->stream, - r->buffer, size, - usb_host_req_complete_data, r, - BULK_TIMEOUT); -#else - usb_host_req_free(r); - p->status = USB_RET_STALL; - return; -#endif - } else { - libusb_fill_bulk_transfer(r->xfer, s->dh, ep, - r->buffer, size, - usb_host_req_complete_data, r, - BULK_TIMEOUT); - } - break; - case USB_ENDPOINT_XFER_INT: - r = usb_host_req_alloc(s, p, p->pid == USB_TOKEN_IN, p->iov.size); - if (!r->in) { - usb_packet_copy(p, r->buffer, p->iov.size); - } - ep = p->ep->nr | (r->in ? USB_DIR_IN : 0); - libusb_fill_interrupt_transfer(r->xfer, s->dh, ep, - r->buffer, p->iov.size, - usb_host_req_complete_data, r, - INTR_TIMEOUT); - break; - case USB_ENDPOINT_XFER_ISOC: - if (p->pid == USB_TOKEN_IN) { - usb_host_iso_data_in(s, p); - } else { - usb_host_iso_data_out(s, p); - } - trace_usb_host_req_complete(s->bus_num, s->addr, p, - p->status, p->actual_length); - return; - default: - p->status = USB_RET_STALL; - trace_usb_host_req_complete(s->bus_num, s->addr, p, - p->status, p->actual_length); - return; - } - - rc = libusb_submit_transfer(r->xfer); - if (rc != 0) { - p->status = USB_RET_NODEV; - trace_usb_host_req_complete(s->bus_num, s->addr, p, - p->status, p->actual_length); - if (rc == LIBUSB_ERROR_NO_DEVICE) { - usb_host_nodev(s); - } - return; - } - - p->status = USB_RET_ASYNC; -} - -static void usb_host_flush_ep_queue(USBDevice *dev, USBEndpoint *ep) -{ - if (usb_host_use_combining(ep)) { - usb_ep_combine_input_packets(ep); - } -} - -static void usb_host_handle_reset(USBDevice *udev) -{ - USBHostDevice *s = USB_HOST_DEVICE(udev); - int rc; - - trace_usb_host_reset(s->bus_num, s->addr); - - rc = libusb_reset_device(s->dh); - if (rc != 0) { - usb_host_nodev(s); - } -} - -static int usb_host_alloc_streams(USBDevice *udev, USBEndpoint **eps, - int nr_eps, int streams) -{ -#ifdef HAVE_STREAMS - USBHostDevice *s = USB_HOST_DEVICE(udev); - unsigned char endpoints[30]; - int i, rc; - - for (i = 0; i < nr_eps; i++) { - endpoints[i] = eps[i]->nr; - if (eps[i]->pid == USB_TOKEN_IN) { - endpoints[i] |= 0x80; - } - } - rc = libusb_alloc_streams(s->dh, streams, endpoints, nr_eps); - if (rc < 0) { - usb_host_libusb_error("libusb_alloc_streams", rc); - } else if (rc != streams) { - error_report("libusb_alloc_streams: got less streams " - "then requested %d < %d", rc, streams); - } - - return (rc == streams) ? 0 : -1; -#else - error_report("libusb_alloc_streams: error not implemented"); - return -1; -#endif -} - -static void usb_host_free_streams(USBDevice *udev, USBEndpoint **eps, - int nr_eps) -{ -#ifdef HAVE_STREAMS - USBHostDevice *s = USB_HOST_DEVICE(udev); - unsigned char endpoints[30]; - int i; - - for (i = 0; i < nr_eps; i++) { - endpoints[i] = eps[i]->nr; - if (eps[i]->pid == USB_TOKEN_IN) { - endpoints[i] |= 0x80; - } - } - libusb_free_streams(s->dh, endpoints, nr_eps); -#endif -} - -/* - * This is *NOT* about restoring state. We have absolutely no idea - * what state the host device is in at the moment and whenever it is - * still present in the first place. Attemping to contine where we - * left off is impossible. - * - * 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. - * - * As the device removal will change the state of other devices (usb - * host controller, most likely interrupt controller too) we have to - * wait with it until *all* vmstate is loaded. Thus post_load just - * kicks a bottom half which then does the actual work. - */ -static void usb_host_post_load_bh(void *opaque) -{ - USBHostDevice *dev = opaque; - USBDevice *udev = USB_DEVICE(dev); - - if (dev->dh != NULL) { - usb_host_close(dev); - } - if (udev->attached) { - usb_device_detach(udev); - } - usb_host_auto_check(NULL); -} - -static int usb_host_post_load(void *opaque, int version_id) -{ - USBHostDevice *dev = opaque; - - if (!dev->bh_postld) { - dev->bh_postld = qemu_bh_new(usb_host_post_load_bh, dev); - } - qemu_bh_schedule(dev->bh_postld); - return 0; -} - -static const VMStateDescription vmstate_usb_host = { - .name = "usb-host", - .version_id = 1, - .minimum_version_id = 1, - .post_load = usb_host_post_load, - .fields = (VMStateField[]) { - VMSTATE_USB_DEVICE(parent_obj, USBHostDevice), - VMSTATE_END_OF_LIST() - } -}; - -static Property usb_host_dev_properties[] = { - DEFINE_PROP_UINT32("hostbus", USBHostDevice, match.bus_num, 0), - DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr, 0), - DEFINE_PROP_STRING("hostport", USBHostDevice, match.port), - DEFINE_PROP_UINT32("vendorid", USBHostDevice, match.vendor_id, 0), - DEFINE_PROP_UINT32("productid", USBHostDevice, match.product_id, 0), - DEFINE_PROP_UINT32("isobufs", USBHostDevice, iso_urb_count, 4), - DEFINE_PROP_UINT32("isobsize", USBHostDevice, iso_urb_frames, 32), - DEFINE_PROP_UINT32("loglevel", USBHostDevice, loglevel, - LIBUSB_LOG_LEVEL_WARNING), - DEFINE_PROP_BIT("pipeline", USBHostDevice, options, - USB_HOST_OPT_PIPELINE, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usb_host_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - - uc->realize = usb_host_realize; - uc->product_desc = "USB Host Device"; - uc->cancel_packet = usb_host_cancel_packet; - uc->handle_data = usb_host_handle_data; - uc->handle_control = usb_host_handle_control; - uc->handle_reset = usb_host_handle_reset; - uc->handle_destroy = usb_host_handle_destroy; - uc->flush_ep_queue = usb_host_flush_ep_queue; - uc->alloc_streams = usb_host_alloc_streams; - uc->free_streams = usb_host_free_streams; - dc->vmsd = &vmstate_usb_host; - dc->props = usb_host_dev_properties; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); -} - -static TypeInfo usb_host_dev_info = { - .name = TYPE_USB_HOST_DEVICE, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBHostDevice), - .class_init = usb_host_class_initfn, - .instance_init = usb_host_instance_init, -}; - -static void usb_host_register_types(void) -{ - type_register_static(&usb_host_dev_info); -} - -type_init(usb_host_register_types) - -/* ------------------------------------------------------------------------ */ - -static QEMUTimer *usb_auto_timer; -static VMChangeStateEntry *usb_vmstate; - -static void usb_host_vm_state(void *unused, int running, RunState state) -{ - if (running) { - usb_host_auto_check(unused); - } -} - -static void usb_host_auto_check(void *unused) -{ - struct USBHostDevice *s; - struct USBAutoFilter *f; - libusb_device **devs = NULL; - struct libusb_device_descriptor ddesc; - int unconnected = 0; - int i, n; - - if (usb_host_init() != 0) { - return; - } - - if (runstate_is_running()) { - n = libusb_get_device_list(ctx, &devs); - for (i = 0; i < n; i++) { - if (libusb_get_device_descriptor(devs[i], &ddesc) != 0) { - continue; - } - if (ddesc.bDeviceClass == LIBUSB_CLASS_HUB) { - continue; - } - QTAILQ_FOREACH(s, &hostdevs, next) { - f = &s->match; - if (f->bus_num > 0 && - f->bus_num != libusb_get_bus_number(devs[i])) { - continue; - } - if (f->addr > 0 && - f->addr != libusb_get_device_address(devs[i])) { - continue; - } - if (f->port != NULL) { - char port[16] = "-"; - usb_host_get_port(devs[i], port, sizeof(port)); - if (strcmp(f->port, port) != 0) { - continue; - } - } - if (f->vendor_id > 0 && - f->vendor_id != ddesc.idVendor) { - continue; - } - if (f->product_id > 0 && - f->product_id != ddesc.idProduct) { - continue; - } - - /* We got a match */ - s->seen++; - if (s->errcount >= 3) { - continue; - } - if (s->dh != NULL) { - continue; - } - if (usb_host_open(s, devs[i]) < 0) { - s->errcount++; - continue; - } - break; - } - } - libusb_free_device_list(devs, 1); - - QTAILQ_FOREACH(s, &hostdevs, next) { - if (s->dh == NULL) { - unconnected++; - } - if (s->seen == 0) { - if (s->dh) { - usb_host_close(s); - } - s->errcount = 0; - } - s->seen = 0; - } - -#if 0 - if (unconnected == 0) { - /* nothing to watch */ - if (usb_auto_timer) { - timer_del(usb_auto_timer); - trace_usb_host_auto_scan_disabled(); - } - return; - } -#endif - } - - if (!usb_vmstate) { - usb_vmstate = qemu_add_vm_change_state_handler(usb_host_vm_state, NULL); - } - if (!usb_auto_timer) { - usb_auto_timer = timer_new_ms(QEMU_CLOCK_REALTIME, usb_host_auto_check, NULL); - if (!usb_auto_timer) { - return; - } - trace_usb_host_auto_scan_enabled(); - } - timer_mod(usb_auto_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 2000); -} - -void hmp_info_usbhost(Monitor *mon, const QDict *qdict) -{ - libusb_device **devs = NULL; - struct libusb_device_descriptor ddesc; - char port[16]; - int i, n; - - if (usb_host_init() != 0) { - return; - } - - n = libusb_get_device_list(ctx, &devs); - for (i = 0; i < n; i++) { - if (libusb_get_device_descriptor(devs[i], &ddesc) != 0) { - continue; - } - if (ddesc.bDeviceClass == LIBUSB_CLASS_HUB) { - continue; - } - usb_host_get_port(devs[i], port, sizeof(port)); - monitor_printf(mon, " Bus %d, Addr %d, Port %s, Speed %s Mb/s\n", - libusb_get_bus_number(devs[i]), - libusb_get_device_address(devs[i]), - port, - speed_name[libusb_get_device_speed(devs[i])]); - monitor_printf(mon, " Class %02x:", ddesc.bDeviceClass); - monitor_printf(mon, " USB device %04x:%04x", - ddesc.idVendor, ddesc.idProduct); - if (ddesc.iProduct) { - libusb_device_handle *handle; - if (libusb_open(devs[i], &handle) == 0) { - unsigned char name[64] = ""; - libusb_get_string_descriptor_ascii(handle, - ddesc.iProduct, - name, sizeof(name)); - libusb_close(handle); - monitor_printf(mon, ", %s", name); - } - } - monitor_printf(mon, "\n"); - } - libusb_free_device_list(devs, 1); -} diff --git a/qemu/hw/usb/host-stub.c b/qemu/hw/usb/host-stub.c deleted file mode 100644 index 6ba65a1f6..000000000 --- a/qemu/hw/usb/host-stub.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Stub host USB redirector - * - * Copyright (c) 2005 Fabrice Bellard - * - * Copyright (c) 2008 Max Krasnyansky - * Support for host device auto connect & disconnect - * Major rewrite to support fully async operation - * - * Copyright 2008 TJ - * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition - * to the legacy /proc/bus/usb USB device discovery and handling - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "ui/console.h" -#include "hw/usb.h" -#include "monitor/monitor.h" - -void hmp_info_usbhost(Monitor *mon, const QDict *qdict) -{ - monitor_printf(mon, "USB host devices not supported\n"); -} - -/* XXX: modify configure to compile the right host driver */ -USBDevice *usb_host_device_open(USBBus *bus, const char *devname) -{ - return NULL; -} diff --git a/qemu/hw/usb/host.h b/qemu/hw/usb/host.h deleted file mode 100644 index 048ff3b48..000000000 --- a/qemu/hw/usb/host.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Linux host USB redirector - * - * Copyright (c) 2005 Fabrice Bellard - * - * Copyright (c) 2008 Max Krasnyansky - * Support for host device auto connect & disconnect - * Major rewrite to support fully async operation - * - * Copyright 2008 TJ - * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition - * to the legacy /proc/bus/usb USB device discovery and handling - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef QEMU_USB_HOST_H -#define QEMU_USB_HOST_H - -struct USBAutoFilter { - uint32_t bus_num; - uint32_t addr; - char *port; - uint32_t vendor_id; - uint32_t product_id; -}; - -#endif /* QEMU_USB_HOST_H */ diff --git a/qemu/hw/usb/libhw.c b/qemu/hw/usb/libhw.c deleted file mode 100644 index 73cdf0c97..000000000 --- a/qemu/hw/usb/libhw.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * QEMU USB emulation, libhw bits. - * - * 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 "qemu-common.h" -#include "hw/hw.h" -#include "hw/usb.h" -#include "sysemu/dma.h" - -int usb_packet_map(USBPacket *p, QEMUSGList *sgl) -{ - DMADirection dir = (p->pid == USB_TOKEN_IN) ? - DMA_DIRECTION_FROM_DEVICE : DMA_DIRECTION_TO_DEVICE; - void *mem; - int i; - - for (i = 0; i < sgl->nsg; i++) { - dma_addr_t base = sgl->sg[i].base; - dma_addr_t len = sgl->sg[i].len; - - while (len) { - dma_addr_t xlen = len; - mem = dma_memory_map(sgl->as, base, &xlen, dir); - if (!mem) { - goto err; - } - if (xlen > len) { - xlen = len; - } - qemu_iovec_add(&p->iov, mem, xlen); - len -= xlen; - base += xlen; - } - } - return 0; - -err: - usb_packet_unmap(p, sgl); - return -1; -} - -void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl) -{ - DMADirection dir = (p->pid == USB_TOKEN_IN) ? - DMA_DIRECTION_FROM_DEVICE : DMA_DIRECTION_TO_DEVICE; - int i; - - for (i = 0; i < p->iov.niov; i++) { - dma_memory_unmap(sgl->as, p->iov.iov[i].iov_base, - p->iov.iov[i].iov_len, dir, - p->iov.iov[i].iov_len); - } -} diff --git a/qemu/hw/usb/quirks-ftdi-ids.h b/qemu/hw/usb/quirks-ftdi-ids.h deleted file mode 100644 index 57c12ef66..000000000 --- a/qemu/hw/usb/quirks-ftdi-ids.h +++ /dev/null @@ -1,1255 +0,0 @@ -/* - * vendor/product IDs (VID/PID) of devices using FTDI USB serial converters. - * Please keep numerically sorted within individual areas, thanks! - * - * Philipp Gühring - pg@futureware.at - added the Device ID of the USB relais - * from Rudolf Gugler - * - */ - - -/**********************************/ -/***** devices using FTDI VID *****/ -/**********************************/ - - -#define FTDI_VID 0x0403 /* Vendor Id */ - - -/*** "original" FTDI device PIDs ***/ - -#define FTDI_8U232AM_PID 0x6001 /* Similar device to SIO above */ -#define FTDI_8U232AM_ALT_PID 0x6006 /* FTDI's alternate PID for above */ -#define FTDI_8U2232C_PID 0x6010 /* Dual channel device */ -#define FTDI_4232H_PID 0x6011 /* Quad channel hi-speed device */ -#define FTDI_232H_PID 0x6014 /* Single channel hi-speed device */ -#define FTDI_FTX_PID 0x6015 /* FT-X series (FT201X, FT230X, FT231X, etc) */ -#define FTDI_SIO_PID 0x8372 /* Product Id SIO application of 8U100AX */ -#define FTDI_232RL_PID 0xFBFA /* Product ID for FT232RL */ - - -/*** third-party PIDs (using FTDI_VID) ***/ - -#define FTDI_LUMEL_PD12_PID 0x6002 - -/* - * Marvell OpenRD Base, Client - * http://www.open-rd.org - * OpenRD Base, Client use VID 0x0403 - */ -#define MARVELL_OPENRD_PID 0x9e90 - -/* www.candapter.com Ewert Energy Systems CANdapter device */ -#define FTDI_CANDAPTER_PID 0x9F80 /* Product Id */ - -/* - * Texas Instruments XDS100v2 JTAG / BeagleBone A3 - * http://processors.wiki.ti.com/index.php/XDS100 - * http://beagleboard.org/bone - */ -#define TI_XDS100V2_PID 0xa6d0 - -#define FTDI_NXTCAM_PID 0xABB8 /* NXTCam for Mindstorms NXT */ - -/* US Interface Navigator (http://www.usinterface.com/) */ -#define FTDI_USINT_CAT_PID 0xb810 /* Navigator CAT and 2nd PTT lines */ -#define FTDI_USINT_WKEY_PID 0xb811 /* Navigator WKEY and FSK lines */ -#define FTDI_USINT_RS232_PID 0xb812 /* Navigator RS232 and CONFIG lines */ - -/* OOCDlink by Joern Kaipf - * (http://www.joernonline.de/) */ -#define FTDI_OOCDLINK_PID 0xbaf8 /* Amontec JTAGkey */ - -/* Luminary Micro Stellaris Boards, VID = FTDI_VID */ -/* FTDI 2332C Dual channel device, side A=245 FIFO (JTAG), Side B=RS232 UART */ -#define LMI_LM3S_DEVEL_BOARD_PID 0xbcd8 -#define LMI_LM3S_EVAL_BOARD_PID 0xbcd9 -#define LMI_LM3S_ICDI_BOARD_PID 0xbcda - -#define FTDI_TURTELIZER_PID 0xBDC8 /* JTAG/RS-232 adapter by egnite GmbH */ - -/* OpenDCC (www.opendcc.de) product id */ -#define FTDI_OPENDCC_PID 0xBFD8 -#define FTDI_OPENDCC_SNIFFER_PID 0xBFD9 -#define FTDI_OPENDCC_THROTTLE_PID 0xBFDA -#define FTDI_OPENDCC_GATEWAY_PID 0xBFDB -#define FTDI_OPENDCC_GBM_PID 0xBFDC - -/* NZR SEM 16+ USB (http://www.nzr.de) */ -#define FTDI_NZR_SEM_USB_PID 0xC1E0 /* NZR SEM-LOG16+ */ - -/* - * RR-CirKits LocoBuffer USB (http://www.rr-cirkits.com) - */ -#define FTDI_RRCIRKITS_LOCOBUFFER_PID 0xc7d0 /* LocoBuffer USB */ - -/* DMX4ALL DMX Interfaces */ -#define FTDI_DMX4ALL 0xC850 - -/* - * ASK.fr devices - */ -#define FTDI_ASK_RDR400_PID 0xC991 /* ASK RDR 400 series card reader */ - -/* www.starting-point-systems.com µChameleon device */ -#define FTDI_MICRO_CHAMELEON_PID 0xCAA0 /* Product Id */ - -/* - * Tactrix OpenPort (ECU) devices. - * OpenPort 1.3M submitted by Donour Sizemore. - * OpenPort 1.3S and 1.3U submitted by Ian Abbott. - */ -#define FTDI_TACTRIX_OPENPORT_13M_PID 0xCC48 /* OpenPort 1.3 Mitsubishi */ -#define FTDI_TACTRIX_OPENPORT_13S_PID 0xCC49 /* OpenPort 1.3 Subaru */ -#define FTDI_TACTRIX_OPENPORT_13U_PID 0xCC4A /* OpenPort 1.3 Universal */ - -#define FTDI_DISTORTEC_JTAG_LOCK_PICK_PID 0xCFF8 - -/* SCS HF Radio Modems PID's (http://www.scs-ptc.com) */ -/* the VID is the standard ftdi vid (FTDI_VID) */ -#define FTDI_SCS_DEVICE_0_PID 0xD010 /* SCS PTC-IIusb */ -#define FTDI_SCS_DEVICE_1_PID 0xD011 /* SCS Tracker / DSP TNC */ -#define FTDI_SCS_DEVICE_2_PID 0xD012 -#define FTDI_SCS_DEVICE_3_PID 0xD013 -#define FTDI_SCS_DEVICE_4_PID 0xD014 -#define FTDI_SCS_DEVICE_5_PID 0xD015 -#define FTDI_SCS_DEVICE_6_PID 0xD016 -#define FTDI_SCS_DEVICE_7_PID 0xD017 - -/* iPlus device */ -#define FTDI_IPLUS_PID 0xD070 /* Product Id */ -#define FTDI_IPLUS2_PID 0xD071 /* Product Id */ - -/* - * Gamma Scout (http://gamma-scout.com/). Submitted by rsc@runtux.com. - */ -#define FTDI_GAMMA_SCOUT_PID 0xD678 /* Gamma Scout online */ - -/* Propox devices */ -#define FTDI_PROPOX_JTAGCABLEII_PID 0xD738 -#define FTDI_PROPOX_ISPCABLEIII_PID 0xD739 - -/* Lenz LI-USB Computer Interface. */ -#define FTDI_LENZ_LIUSB_PID 0xD780 - -/* Vardaan Enterprises Serial Interface VEUSB422R3 */ -#define FTDI_VARDAAN_PID 0xF070 - -/* - * Xsens Technologies BV products (http://www.xsens.com). - */ -#define XSENS_CONVERTER_0_PID 0xD388 -#define XSENS_CONVERTER_1_PID 0xD389 -#define XSENS_CONVERTER_2_PID 0xD38A -#define XSENS_CONVERTER_3_PID 0xD38B -#define XSENS_CONVERTER_4_PID 0xD38C -#define XSENS_CONVERTER_5_PID 0xD38D -#define XSENS_CONVERTER_6_PID 0xD38E -#define XSENS_CONVERTER_7_PID 0xD38F - -/* - * NDI (www.ndigital.com) product ids - */ -#define FTDI_NDI_HUC_PID 0xDA70 /* NDI Host USB Converter */ -#define FTDI_NDI_SPECTRA_SCU_PID 0xDA71 /* NDI Spectra SCU */ -#define FTDI_NDI_FUTURE_2_PID 0xDA72 /* NDI future device #2 */ -#define FTDI_NDI_FUTURE_3_PID 0xDA73 /* NDI future device #3 */ -#define FTDI_NDI_AURORA_SCU_PID 0xDA74 /* NDI Aurora SCU */ - -/* - * ChamSys Limited (www.chamsys.co.uk) USB wing/interface product IDs - */ -#define FTDI_CHAMSYS_24_MASTER_WING_PID 0xDAF8 -#define FTDI_CHAMSYS_PC_WING_PID 0xDAF9 -#define FTDI_CHAMSYS_USB_DMX_PID 0xDAFA -#define FTDI_CHAMSYS_MIDI_TIMECODE_PID 0xDAFB -#define FTDI_CHAMSYS_MINI_WING_PID 0xDAFC -#define FTDI_CHAMSYS_MAXI_WING_PID 0xDAFD -#define FTDI_CHAMSYS_MEDIA_WING_PID 0xDAFE -#define FTDI_CHAMSYS_WING_PID 0xDAFF - -/* - * Westrex International devices submitted by Cory Lee - */ -#define FTDI_WESTREX_MODEL_777_PID 0xDC00 /* Model 777 */ -#define FTDI_WESTREX_MODEL_8900F_PID 0xDC01 /* Model 8900F */ - -/* - * ACG Identification Technologies GmbH products (http://www.acg.de/). - * Submitted by anton -at- goto10 -dot- org. - */ -#define FTDI_ACG_HFDUAL_PID 0xDD20 /* HF Dual ISO Reader (RFID) */ - -/* - * Definitions for Artemis astronomical USB based cameras - * Check it at http://www.artemisccd.co.uk/ - */ -#define FTDI_ARTEMIS_PID 0xDF28 /* All Artemis Cameras */ - -/* - * Definitions for ATIK Instruments astronomical USB based cameras - * Check it at http://www.atik-instruments.com/ - */ -#define FTDI_ATIK_ATK16_PID 0xDF30 /* ATIK ATK-16 Grayscale Camera */ -#define FTDI_ATIK_ATK16C_PID 0xDF32 /* ATIK ATK-16C Colour Camera */ -#define FTDI_ATIK_ATK16HR_PID 0xDF31 /* ATIK ATK-16HR Grayscale Camera */ -#define FTDI_ATIK_ATK16HRC_PID 0xDF33 /* ATIK ATK-16HRC Colour Camera */ -#define FTDI_ATIK_ATK16IC_PID 0xDF35 /* ATIK ATK-16IC Grayscale Camera */ - -/* - * Yost Engineering, Inc. products (www.yostengineering.com). - * PID 0xE050 submitted by Aaron Prose. - */ -#define FTDI_YEI_SERVOCENTER31_PID 0xE050 /* YEI ServoCenter3.1 USB */ - -/* - * ELV USB devices submitted by Christian Abt of ELV (www.elv.de). - * All of these devices use FTDI's vendor ID (0x0403). - * Further IDs taken from ELV Windows .inf file. - * - * The previously included PID for the UO 100 module was incorrect. - * In fact, that PID was for ELV's UR 100 USB-RS232 converter (0xFB58). - * - * Armin Laeuger originally sent the PID for the UM 100 module. - */ -#define FTDI_ELV_USR_PID 0xE000 /* ELV Universal-Sound-Recorder */ -#define FTDI_ELV_MSM1_PID 0xE001 /* ELV Mini-Sound-Modul */ -#define FTDI_ELV_KL100_PID 0xE002 /* ELV Kfz-Leistungsmesser KL 100 */ -#define FTDI_ELV_WS550_PID 0xE004 /* WS 550 */ -#define FTDI_ELV_EC3000_PID 0xE006 /* ENERGY CONTROL 3000 USB */ -#define FTDI_ELV_WS888_PID 0xE008 /* WS 888 */ -#define FTDI_ELV_TWS550_PID 0xE009 /* Technoline WS 550 */ -#define FTDI_ELV_FEM_PID 0xE00A /* Funk Energie Monitor */ -#define FTDI_ELV_FHZ1300PC_PID 0xE0E8 /* FHZ 1300 PC */ -#define FTDI_ELV_WS500_PID 0xE0E9 /* PC-Wetterstation (WS 500) */ -#define FTDI_ELV_HS485_PID 0xE0EA /* USB to RS-485 adapter */ -#define FTDI_ELV_UMS100_PID 0xE0EB /* ELV USB Master-Slave Schaltsteckdose UMS 100 */ -#define FTDI_ELV_TFD128_PID 0xE0EC /* ELV Temperatur-Feuchte-Datenlogger TFD 128 */ -#define FTDI_ELV_FM3RX_PID 0xE0ED /* ELV Messwertuebertragung FM3 RX */ -#define FTDI_ELV_WS777_PID 0xE0EE /* Conrad WS 777 */ -#define FTDI_ELV_EM1010PC_PID 0xE0EF /* Energy monitor EM 1010 PC */ -#define FTDI_ELV_CSI8_PID 0xE0F0 /* Computer-Schalt-Interface (CSI 8) */ -#define FTDI_ELV_EM1000DL_PID 0xE0F1 /* PC-Datenlogger fuer Energiemonitor (EM 1000 DL) */ -#define FTDI_ELV_PCK100_PID 0xE0F2 /* PC-Kabeltester (PCK 100) */ -#define FTDI_ELV_RFP500_PID 0xE0F3 /* HF-Leistungsmesser (RFP 500) */ -#define FTDI_ELV_FS20SIG_PID 0xE0F4 /* Signalgeber (FS 20 SIG) */ -#define FTDI_ELV_UTP8_PID 0xE0F5 /* ELV UTP 8 */ -#define FTDI_ELV_WS300PC_PID 0xE0F6 /* PC-Wetterstation (WS 300 PC) */ -#define FTDI_ELV_WS444PC_PID 0xE0F7 /* Conrad WS 444 PC */ -#define FTDI_PHI_FISCO_PID 0xE40B /* PHI Fisco USB to Serial cable */ -#define FTDI_ELV_UAD8_PID 0xF068 /* USB-AD-Wandler (UAD 8) */ -#define FTDI_ELV_UDA7_PID 0xF069 /* USB-DA-Wandler (UDA 7) */ -#define FTDI_ELV_USI2_PID 0xF06A /* USB-Schrittmotoren-Interface (USI 2) */ -#define FTDI_ELV_T1100_PID 0xF06B /* Thermometer (T 1100) */ -#define FTDI_ELV_PCD200_PID 0xF06C /* PC-Datenlogger (PCD 200) */ -#define FTDI_ELV_ULA200_PID 0xF06D /* USB-LCD-Ansteuerung (ULA 200) */ -#define FTDI_ELV_ALC8500_PID 0xF06E /* ALC 8500 Expert */ -#define FTDI_ELV_FHZ1000PC_PID 0xF06F /* FHZ 1000 PC */ -#define FTDI_ELV_UR100_PID 0xFB58 /* USB-RS232-Umsetzer (UR 100) */ -#define FTDI_ELV_UM100_PID 0xFB5A /* USB-Modul UM 100 */ -#define FTDI_ELV_UO100_PID 0xFB5B /* USB-Modul UO 100 */ -/* Additional ELV PIDs that default to using the FTDI D2XX drivers on - * MS Windows, rather than the FTDI Virtual Com Port drivers. - * Maybe these will be easier to use with the libftdi/libusb user-space - * drivers, or possibly the Comedi drivers in some cases. */ -#define FTDI_ELV_CLI7000_PID 0xFB59 /* Computer-Light-Interface (CLI 7000) */ -#define FTDI_ELV_PPS7330_PID 0xFB5C /* Processor-Power-Supply (PPS 7330) */ -#define FTDI_ELV_TFM100_PID 0xFB5D /* Temperatur-Feuchte-Messgeraet (TFM 100) */ -#define FTDI_ELV_UDF77_PID 0xFB5E /* USB DCF Funkuhr (UDF 77) */ -#define FTDI_ELV_UIO88_PID 0xFB5F /* USB-I/O Interface (UIO 88) */ - -/* - * EVER Eco Pro UPS (http://www.ever.com.pl/) - */ - -#define EVER_ECO_PRO_CDS 0xe520 /* RS-232 converter */ - -/* - * Active Robots product ids. - */ -#define FTDI_ACTIVE_ROBOTS_PID 0xE548 /* USB comms board */ - -/* Pyramid Computer GmbH */ -#define FTDI_PYRAMID_PID 0xE6C8 /* Pyramid Appliance Display */ - -/* www.elsterelectricity.com Elster Unicom III Optical Probe */ -#define FTDI_ELSTER_UNICOM_PID 0xE700 /* Product Id */ - -/* - * Gude Analog- und Digitalsysteme GmbH - */ -#define FTDI_GUDEADS_E808_PID 0xE808 -#define FTDI_GUDEADS_E809_PID 0xE809 -#define FTDI_GUDEADS_E80A_PID 0xE80A -#define FTDI_GUDEADS_E80B_PID 0xE80B -#define FTDI_GUDEADS_E80C_PID 0xE80C -#define FTDI_GUDEADS_E80D_PID 0xE80D -#define FTDI_GUDEADS_E80E_PID 0xE80E -#define FTDI_GUDEADS_E80F_PID 0xE80F -#define FTDI_GUDEADS_E888_PID 0xE888 /* Expert ISDN Control USB */ -#define FTDI_GUDEADS_E889_PID 0xE889 /* USB RS-232 OptoBridge */ -#define FTDI_GUDEADS_E88A_PID 0xE88A -#define FTDI_GUDEADS_E88B_PID 0xE88B -#define FTDI_GUDEADS_E88C_PID 0xE88C -#define FTDI_GUDEADS_E88D_PID 0xE88D -#define FTDI_GUDEADS_E88E_PID 0xE88E -#define FTDI_GUDEADS_E88F_PID 0xE88F - -/* - * Eclo (http://www.eclo.pt/) product IDs. - * PID 0xEA90 submitted by Martin Grill. - */ -#define FTDI_ECLO_COM_1WIRE_PID 0xEA90 /* COM to 1-Wire USB adaptor */ - -/* TNC-X USB-to-packet-radio adapter, versions prior to 3.0 (DLP module) */ -#define FTDI_TNC_X_PID 0xEBE0 - -/* - * Teratronik product ids. - * Submitted by O. Wölfelschneider. - */ -#define FTDI_TERATRONIK_VCP_PID 0xEC88 /* Teratronik device (preferring VCP driver on windows) */ -#define FTDI_TERATRONIK_D2XX_PID 0xEC89 /* Teratronik device (preferring D2XX driver on windows) */ - -/* Rig Expert Ukraine devices */ -#define FTDI_REU_TINY_PID 0xED22 /* RigExpert Tiny */ - -/* - * Hameg HO820 and HO870 interface (using VID 0x0403) - */ -#define HAMEG_HO820_PID 0xed74 -#define HAMEG_HO730_PID 0xed73 -#define HAMEG_HO720_PID 0xed72 -#define HAMEG_HO870_PID 0xed71 - -/* - * MaxStream devices www.maxstream.net - */ -#define FTDI_MAXSTREAM_PID 0xEE18 /* Xbee PKG-U Module */ - -/* - * microHAM product IDs (http://www.microham.com). - * Submitted by Justin Burket (KL1RL) - * and Mike Studer (K6EEP) . - * Ian Abbott added a few more from the driver INF file. - */ -#define FTDI_MHAM_KW_PID 0xEEE8 /* USB-KW interface */ -#define FTDI_MHAM_YS_PID 0xEEE9 /* USB-YS interface */ -#define FTDI_MHAM_Y6_PID 0xEEEA /* USB-Y6 interface */ -#define FTDI_MHAM_Y8_PID 0xEEEB /* USB-Y8 interface */ -#define FTDI_MHAM_IC_PID 0xEEEC /* USB-IC interface */ -#define FTDI_MHAM_DB9_PID 0xEEED /* USB-DB9 interface */ -#define FTDI_MHAM_RS232_PID 0xEEEE /* USB-RS232 interface */ -#define FTDI_MHAM_Y9_PID 0xEEEF /* USB-Y9 interface */ - -/* Domintell products http://www.domintell.com */ -#define FTDI_DOMINTELL_DGQG_PID 0xEF50 /* Master */ -#define FTDI_DOMINTELL_DUSB_PID 0xEF51 /* DUSB01 module */ - -/* - * The following are the values for the Perle Systems - * UltraPort USB serial converters - */ -#define FTDI_PERLE_ULTRAPORT_PID 0xF0C0 /* Perle UltraPort Product Id */ - -/* Sprog II (Andrew Crosland's SprogII DCC interface) */ -#define FTDI_SPROG_II 0xF0C8 - -/* an infrared receiver for user access control with IR tags */ -#define FTDI_PIEGROUP_PID 0xF208 /* Product Id */ - -/* ACT Solutions HomePro ZWave interface - (http://www.act-solutions.com/HomePro-Product-Matrix.html) */ -#define FTDI_ACTZWAVE_PID 0xF2D0 - -/* - * 4N-GALAXY.DE PIDs for CAN-USB, USB-RS232, USB-RS422, USB-RS485, - * USB-TTY aktiv, USB-TTY passiv. Some PIDs are used by several devices - * and I'm not entirely sure which are used by which. - */ -#define FTDI_4N_GALAXY_DE_1_PID 0xF3C0 -#define FTDI_4N_GALAXY_DE_2_PID 0xF3C1 -#define FTDI_4N_GALAXY_DE_3_PID 0xF3C2 - -/* - * Linx Technologies product ids - */ -#define LINX_SDMUSBQSS_PID 0xF448 /* Linx SDM-USB-QS-S */ -#define LINX_MASTERDEVEL2_PID 0xF449 /* Linx Master Development 2.0 */ -#define LINX_FUTURE_0_PID 0xF44A /* Linx future device */ -#define LINX_FUTURE_1_PID 0xF44B /* Linx future device */ -#define LINX_FUTURE_2_PID 0xF44C /* Linx future device */ - -/* - * Oceanic product ids - */ -#define FTDI_OCEANIC_PID 0xF460 /* Oceanic dive instrument */ - -/* - * SUUNTO product ids - */ -#define FTDI_SUUNTO_SPORTS_PID 0xF680 /* Suunto Sports instrument */ - -/* USB-UIRT - An infrared receiver and transmitter using the 8U232AM chip */ -/* http://www.usbuirt.com/ */ -#define FTDI_USB_UIRT_PID 0xF850 /* Product Id */ - -/* CCS Inc. ICDU/ICDU40 product ID - - * the FT232BM is used in an in-circuit-debugger unit for PIC16's/PIC18's */ -#define FTDI_CCSICDU20_0_PID 0xF9D0 -#define FTDI_CCSICDU40_1_PID 0xF9D1 -#define FTDI_CCSMACHX_2_PID 0xF9D2 -#define FTDI_CCSLOAD_N_GO_3_PID 0xF9D3 -#define FTDI_CCSICDU64_4_PID 0xF9D4 -#define FTDI_CCSPRIME8_5_PID 0xF9D5 - -/* - * The following are the values for the Matrix Orbital LCD displays, - * which are the FT232BM ( similar to the 8U232AM ) - */ -#define FTDI_MTXORB_0_PID 0xFA00 /* Matrix Orbital Product Id */ -#define FTDI_MTXORB_1_PID 0xFA01 /* Matrix Orbital Product Id */ -#define FTDI_MTXORB_2_PID 0xFA02 /* Matrix Orbital Product Id */ -#define FTDI_MTXORB_3_PID 0xFA03 /* Matrix Orbital Product Id */ -#define FTDI_MTXORB_4_PID 0xFA04 /* Matrix Orbital Product Id */ -#define FTDI_MTXORB_5_PID 0xFA05 /* Matrix Orbital Product Id */ -#define FTDI_MTXORB_6_PID 0xFA06 /* Matrix Orbital Product Id */ - -/* - * Home Electronics (www.home-electro.com) USB gadgets - */ -#define FTDI_HE_TIRA1_PID 0xFA78 /* Tira-1 IR transceiver */ - -/* Inside Accesso contactless reader (http://www.insidecontactless.com/) */ -#define INSIDE_ACCESSO 0xFAD0 - -/* - * ThorLabs USB motor drivers - */ -#define FTDI_THORLABS_PID 0xfaf0 /* ThorLabs USB motor drivers */ - -/* - * Protego product ids - */ -#define PROTEGO_SPECIAL_1 0xFC70 /* special/unknown device */ -#define PROTEGO_R2X0 0xFC71 /* R200-USB TRNG unit (R210, R220, and R230) */ -#define PROTEGO_SPECIAL_3 0xFC72 /* special/unknown device */ -#define PROTEGO_SPECIAL_4 0xFC73 /* special/unknown device */ - -/* - * Sony Ericsson product ids - */ -#define FTDI_DSS20_PID 0xFC82 /* DSS-20 Sync Station for Sony Ericsson P800 */ -#define FTDI_URBAN_0_PID 0xFC8A /* Sony Ericsson Urban, uart #0 */ -#define FTDI_URBAN_1_PID 0xFC8B /* Sony Ericsson Urban, uart #1 */ - -/* www.irtrans.de device */ -#define FTDI_IRTRANS_PID 0xFC60 /* Product Id */ - -/* - * RM Michaelides CANview USB (http://www.rmcan.com) (FTDI_VID) - * CAN fieldbus interface adapter, added by port GmbH www.port.de) - * Ian Abbott changed the macro names for consistency. - */ -#define FTDI_RM_CANVIEW_PID 0xfd60 /* Product Id */ -/* www.thoughttechnology.com/ TT-USB provide with procomp use ftdi_sio */ -#define FTDI_TTUSB_PID 0xFF20 /* Product Id */ - -#define FTDI_USBX_707_PID 0xF857 /* ADSTech IR Blaster USBX-707 (FTDI_VID) */ - -#define FTDI_RELAIS_PID 0xFA10 /* Relais device from Rudolf Gugler */ - -/* - * PCDJ use ftdi based dj-controllers. The following PID is - * for their DAC-2 device http://www.pcdjhardware.com/DAC2.asp - * (the VID is the standard ftdi vid (FTDI_VID), PID sent by Wouter Paesen) - */ -#define FTDI_PCDJ_DAC2_PID 0xFA88 - -#define FTDI_R2000KU_TRUE_RNG 0xFB80 /* R2000KU TRUE RNG (FTDI_VID) */ - -/* - * DIEBOLD BCS SE923 (FTDI_VID) - */ -#define DIEBOLD_BCS_SE923_PID 0xfb99 - -/* www.crystalfontz.com devices - * - thanx for providing free devices for evaluation ! - * they use the ftdi chipset for the USB interface - * and the vendor id is the same - */ -#define FTDI_XF_632_PID 0xFC08 /* 632: 16x2 Character Display */ -#define FTDI_XF_634_PID 0xFC09 /* 634: 20x4 Character Display */ -#define FTDI_XF_547_PID 0xFC0A /* 547: Two line Display */ -#define FTDI_XF_633_PID 0xFC0B /* 633: 16x2 Character Display with Keys */ -#define FTDI_XF_631_PID 0xFC0C /* 631: 20x2 Character Display */ -#define FTDI_XF_635_PID 0xFC0D /* 635: 20x4 Character Display */ -#define FTDI_XF_640_PID 0xFC0E /* 640: Two line Display */ -#define FTDI_XF_642_PID 0xFC0F /* 642: Two line Display */ - -/* - * Video Networks Limited / Homechoice in the UK use an ftdi-based device - * for their 1Mb broadband internet service. The following PID is exhibited - * by the usb device supplied (the VID is the standard ftdi vid (FTDI_VID) - */ -#define FTDI_VNHCPCUSB_D_PID 0xfe38 /* Product Id */ - -/* AlphaMicro Components AMC-232USB01 device (FTDI_VID) */ -#define FTDI_AMC232_PID 0xFF00 /* Product Id */ - -/* - * IBS elektronik product ids (FTDI_VID) - * Submitted by Thomas Schleusener - */ -#define FTDI_IBS_US485_PID 0xff38 /* IBS US485 (USB<-->RS422/485 interface) */ -#define FTDI_IBS_PICPRO_PID 0xff39 /* IBS PIC-Programmer */ -#define FTDI_IBS_PCMCIA_PID 0xff3a /* IBS Card reader for PCMCIA SRAM-cards */ -#define FTDI_IBS_PK1_PID 0xff3b /* IBS PK1 - Particel counter */ -#define FTDI_IBS_RS232MON_PID 0xff3c /* IBS RS232 - Monitor */ -#define FTDI_IBS_APP70_PID 0xff3d /* APP 70 (dust monitoring system) */ -#define FTDI_IBS_PEDO_PID 0xff3e /* IBS PEDO-Modem (RF modem 868.35 MHz) */ -#define FTDI_IBS_PROD_PID 0xff3f /* future device */ -/* www.canusb.com Lawicel CANUSB device (FTDI_VID) */ -#define FTDI_CANUSB_PID 0xFFA8 /* Product Id */ - -/* - * TavIR AVR product ids (FTDI_VID) - */ -#define FTDI_TAVIR_STK500_PID 0xFA33 /* STK500 AVR programmer */ - -/* - * TIAO product ids (FTDI_VID) - * http://www.tiaowiki.com/w/Main_Page - */ -#define FTDI_TIAO_UMPA_PID 0x8a98 /* TIAO/DIYGADGET USB Multi-Protocol Adapter */ - - -/********************************/ -/** third-party VID/PID combos **/ -/********************************/ - - - -/* - * Atmel STK541 - */ -#define ATMEL_VID 0x03eb /* Vendor ID */ -#define STK541_PID 0x2109 /* Zigbee Controller */ - -/* - * Blackfin gnICE JTAG - * http://docs.blackfin.uclinux.org/doku.php?id=hw:jtag:gnice - */ -#define ADI_VID 0x0456 -#define ADI_GNICE_PID 0xF000 -#define ADI_GNICEPLUS_PID 0xF001 - -/* - * Microchip Technology, Inc. - * - * MICROCHIP_VID (0x04D8) and MICROCHIP_USB_BOARD_PID (0x000A) are - * used by single function CDC ACM class based firmware demo - * applications. The VID/PID has also been used in firmware - * emulating FTDI serial chips by: - * Hornby Elite - Digital Command Control Console - * http://www.hornby.com/hornby-dcc/controllers/ - */ -#define MICROCHIP_VID 0x04D8 -#define MICROCHIP_USB_BOARD_PID 0x000A /* CDC RS-232 Emulation Demo */ - -/* - * RATOC REX-USB60F - */ -#define RATOC_VENDOR_ID 0x0584 -#define RATOC_PRODUCT_ID_USB60F 0xb020 - -/* - * Acton Research Corp. - */ -#define ACTON_VID 0x0647 /* Vendor ID */ -#define ACTON_SPECTRAPRO_PID 0x0100 - -/* - * Contec products (http://www.contec.com) - * Submitted by Daniel Sangorrin - */ -#define CONTEC_VID 0x06CE /* Vendor ID */ -#define CONTEC_COM1USBH_PID 0x8311 /* COM-1(USB)H */ - -/* - * Definitions for B&B Electronics products. - */ -#define BANDB_VID 0x0856 /* B&B Electronics Vendor ID */ -#define BANDB_USOTL4_PID 0xAC01 /* USOTL4 Isolated RS-485 Converter */ -#define BANDB_USTL4_PID 0xAC02 /* USTL4 RS-485 Converter */ -#define BANDB_USO9ML2_PID 0xAC03 /* USO9ML2 Isolated RS-232 Converter */ -#define BANDB_USOPTL4_PID 0xAC11 -#define BANDB_USPTL4_PID 0xAC12 -#define BANDB_USO9ML2DR_2_PID 0xAC16 -#define BANDB_USO9ML2DR_PID 0xAC17 -#define BANDB_USOPTL4DR2_PID 0xAC18 /* USOPTL4R-2 2-port Isolated RS-232 Converter */ -#define BANDB_USOPTL4DR_PID 0xAC19 -#define BANDB_485USB9F_2W_PID 0xAC25 -#define BANDB_485USB9F_4W_PID 0xAC26 -#define BANDB_232USB9M_PID 0xAC27 -#define BANDB_485USBTB_2W_PID 0xAC33 -#define BANDB_485USBTB_4W_PID 0xAC34 -#define BANDB_TTL5USB9M_PID 0xAC49 -#define BANDB_TTL3USB9M_PID 0xAC50 -#define BANDB_ZZ_PROG1_USB_PID 0xBA02 - -/* - * Intrepid Control Systems (http://www.intrepidcs.com/) ValueCAN and NeoVI - */ -#define INTREPID_VID 0x093C -#define INTREPID_VALUECAN_PID 0x0601 -#define INTREPID_NEOVI_PID 0x0701 - -/* - * Definitions for ID TECH (www.idt-net.com) devices - */ -#define IDTECH_VID 0x0ACD /* ID TECH Vendor ID */ -#define IDTECH_IDT1221U_PID 0x0300 /* IDT1221U USB to RS-232 adapter */ - -/* - * Definitions for Omnidirectional Control Technology, Inc. devices - */ -#define OCT_VID 0x0B39 /* OCT vendor ID */ -/* Note: OCT US101 is also rebadged as Dick Smith Electronics (NZ) XH6381 */ -/* Also rebadged as Dick Smith Electronics (Aus) XH6451 */ -/* Also rebadged as SIIG Inc. model US2308 hardware version 1 */ -#define OCT_DK201_PID 0x0103 /* OCT DK201 USB docking station */ -#define OCT_US101_PID 0x0421 /* OCT US101 USB to RS-232 */ - -/* - * Definitions for Icom Inc. devices - */ -#define ICOM_VID 0x0C26 /* Icom vendor ID */ -/* Note: ID-1 is a communications tranceiver for HAM-radio operators */ -#define ICOM_ID_1_PID 0x0004 /* ID-1 USB to RS-232 */ -/* Note: OPC is an Optional cable to connect an Icom Tranceiver */ -#define ICOM_OPC_U_UC_PID 0x0018 /* OPC-478UC, OPC-1122U cloning cable */ -/* Note: ID-RP* devices are Icom Repeater Devices for HAM-radio */ -#define ICOM_ID_RP2C1_PID 0x0009 /* ID-RP2C Asset 1 to RS-232 */ -#define ICOM_ID_RP2C2_PID 0x000A /* ID-RP2C Asset 2 to RS-232 */ -#define ICOM_ID_RP2D_PID 0x000B /* ID-RP2D configuration port*/ -#define ICOM_ID_RP2VT_PID 0x000C /* ID-RP2V Transmit config port */ -#define ICOM_ID_RP2VR_PID 0x000D /* ID-RP2V Receive config port */ -#define ICOM_ID_RP4KVT_PID 0x0010 /* ID-RP4000V Transmit config port */ -#define ICOM_ID_RP4KVR_PID 0x0011 /* ID-RP4000V Receive config port */ -#define ICOM_ID_RP2KVT_PID 0x0012 /* ID-RP2000V Transmit config port */ -#define ICOM_ID_RP2KVR_PID 0x0013 /* ID-RP2000V Receive config port */ - -/* - * GN Otometrics (http://www.otometrics.com) - * Submitted by Ville Sundberg. - */ -#define GN_OTOMETRICS_VID 0x0c33 /* Vendor ID */ -#define AURICAL_USB_PID 0x0010 /* Aurical USB Audiometer */ - -/* - * The following are the values for the Sealevel SeaLINK+ adapters. - * (Original list sent by Tuan Hoang. Ian Abbott renamed the macros and - * removed some PIDs that don't seem to match any existing products.) - */ -#define SEALEVEL_VID 0x0c52 /* Sealevel Vendor ID */ -#define SEALEVEL_2101_PID 0x2101 /* SeaLINK+232 (2101/2105) */ -#define SEALEVEL_2102_PID 0x2102 /* SeaLINK+485 (2102) */ -#define SEALEVEL_2103_PID 0x2103 /* SeaLINK+232I (2103) */ -#define SEALEVEL_2104_PID 0x2104 /* SeaLINK+485I (2104) */ -#define SEALEVEL_2106_PID 0x9020 /* SeaLINK+422 (2106) */ -#define SEALEVEL_2201_1_PID 0x2211 /* SeaPORT+2/232 (2201) Port 1 */ -#define SEALEVEL_2201_2_PID 0x2221 /* SeaPORT+2/232 (2201) Port 2 */ -#define SEALEVEL_2202_1_PID 0x2212 /* SeaPORT+2/485 (2202) Port 1 */ -#define SEALEVEL_2202_2_PID 0x2222 /* SeaPORT+2/485 (2202) Port 2 */ -#define SEALEVEL_2203_1_PID 0x2213 /* SeaPORT+2 (2203) Port 1 */ -#define SEALEVEL_2203_2_PID 0x2223 /* SeaPORT+2 (2203) Port 2 */ -#define SEALEVEL_2401_1_PID 0x2411 /* SeaPORT+4/232 (2401) Port 1 */ -#define SEALEVEL_2401_2_PID 0x2421 /* SeaPORT+4/232 (2401) Port 2 */ -#define SEALEVEL_2401_3_PID 0x2431 /* SeaPORT+4/232 (2401) Port 3 */ -#define SEALEVEL_2401_4_PID 0x2441 /* SeaPORT+4/232 (2401) Port 4 */ -#define SEALEVEL_2402_1_PID 0x2412 /* SeaPORT+4/485 (2402) Port 1 */ -#define SEALEVEL_2402_2_PID 0x2422 /* SeaPORT+4/485 (2402) Port 2 */ -#define SEALEVEL_2402_3_PID 0x2432 /* SeaPORT+4/485 (2402) Port 3 */ -#define SEALEVEL_2402_4_PID 0x2442 /* SeaPORT+4/485 (2402) Port 4 */ -#define SEALEVEL_2403_1_PID 0x2413 /* SeaPORT+4 (2403) Port 1 */ -#define SEALEVEL_2403_2_PID 0x2423 /* SeaPORT+4 (2403) Port 2 */ -#define SEALEVEL_2403_3_PID 0x2433 /* SeaPORT+4 (2403) Port 3 */ -#define SEALEVEL_2403_4_PID 0x2443 /* SeaPORT+4 (2403) Port 4 */ -#define SEALEVEL_2801_1_PID 0X2811 /* SeaLINK+8/232 (2801) Port 1 */ -#define SEALEVEL_2801_2_PID 0X2821 /* SeaLINK+8/232 (2801) Port 2 */ -#define SEALEVEL_2801_3_PID 0X2831 /* SeaLINK+8/232 (2801) Port 3 */ -#define SEALEVEL_2801_4_PID 0X2841 /* SeaLINK+8/232 (2801) Port 4 */ -#define SEALEVEL_2801_5_PID 0X2851 /* SeaLINK+8/232 (2801) Port 5 */ -#define SEALEVEL_2801_6_PID 0X2861 /* SeaLINK+8/232 (2801) Port 6 */ -#define SEALEVEL_2801_7_PID 0X2871 /* SeaLINK+8/232 (2801) Port 7 */ -#define SEALEVEL_2801_8_PID 0X2881 /* SeaLINK+8/232 (2801) Port 8 */ -#define SEALEVEL_2802_1_PID 0X2812 /* SeaLINK+8/485 (2802) Port 1 */ -#define SEALEVEL_2802_2_PID 0X2822 /* SeaLINK+8/485 (2802) Port 2 */ -#define SEALEVEL_2802_3_PID 0X2832 /* SeaLINK+8/485 (2802) Port 3 */ -#define SEALEVEL_2802_4_PID 0X2842 /* SeaLINK+8/485 (2802) Port 4 */ -#define SEALEVEL_2802_5_PID 0X2852 /* SeaLINK+8/485 (2802) Port 5 */ -#define SEALEVEL_2802_6_PID 0X2862 /* SeaLINK+8/485 (2802) Port 6 */ -#define SEALEVEL_2802_7_PID 0X2872 /* SeaLINK+8/485 (2802) Port 7 */ -#define SEALEVEL_2802_8_PID 0X2882 /* SeaLINK+8/485 (2802) Port 8 */ -#define SEALEVEL_2803_1_PID 0X2813 /* SeaLINK+8 (2803) Port 1 */ -#define SEALEVEL_2803_2_PID 0X2823 /* SeaLINK+8 (2803) Port 2 */ -#define SEALEVEL_2803_3_PID 0X2833 /* SeaLINK+8 (2803) Port 3 */ -#define SEALEVEL_2803_4_PID 0X2843 /* SeaLINK+8 (2803) Port 4 */ -#define SEALEVEL_2803_5_PID 0X2853 /* SeaLINK+8 (2803) Port 5 */ -#define SEALEVEL_2803_6_PID 0X2863 /* SeaLINK+8 (2803) Port 6 */ -#define SEALEVEL_2803_7_PID 0X2873 /* SeaLINK+8 (2803) Port 7 */ -#define SEALEVEL_2803_8_PID 0X2883 /* SeaLINK+8 (2803) Port 8 */ -#define SEALEVEL_2803R_1_PID 0Xa02a /* SeaLINK+8 (2803-ROHS) Port 1+2 */ -#define SEALEVEL_2803R_2_PID 0Xa02b /* SeaLINK+8 (2803-ROHS) Port 3+4 */ -#define SEALEVEL_2803R_3_PID 0Xa02c /* SeaLINK+8 (2803-ROHS) Port 5+6 */ -#define SEALEVEL_2803R_4_PID 0Xa02d /* SeaLINK+8 (2803-ROHS) Port 7+8 */ - -/* - * JETI SPECTROMETER SPECBOS 1201 - * http://www.jeti.com/cms/index.php/instruments/other-instruments/specbos-2101 - */ -#define JETI_VID 0x0c6c -#define JETI_SPC1201_PID 0x04b2 - -/* - * FTDI USB UART chips used in construction projects from the - * Elektor Electronics magazine (http://www.elektor.com/) - */ -#define ELEKTOR_VID 0x0C7D -#define ELEKTOR_FT323R_PID 0x0005 /* RFID-Reader, issue 09-2006 */ - -/* - * Posiflex inc retail equipment (http://www.posiflex.com.tw) - */ -#define POSIFLEX_VID 0x0d3a /* Vendor ID */ -#define POSIFLEX_PP7000_PID 0x0300 /* PP-7000II thermal printer */ - -/* - * The following are the values for two KOBIL chipcard terminals. - */ -#define KOBIL_VID 0x0d46 /* KOBIL Vendor ID */ -#define KOBIL_CONV_B1_PID 0x2020 /* KOBIL Konverter for B1 */ -#define KOBIL_CONV_KAAN_PID 0x2021 /* KOBIL_Konverter for KAAN */ - -#define FTDI_NF_RIC_VID 0x0DCD /* Vendor Id */ -#define FTDI_NF_RIC_PID 0x0001 /* Product Id */ - -/* - * Falcom Wireless Communications GmbH - */ -#define FALCOM_VID 0x0F94 /* Vendor Id */ -#define FALCOM_TWIST_PID 0x0001 /* Falcom Twist USB GPRS modem */ -#define FALCOM_SAMBA_PID 0x0005 /* Falcom Samba USB GPRS modem */ - -/* Larsen and Brusgaard AltiTrack/USBtrack */ -#define LARSENBRUSGAARD_VID 0x0FD8 -#define LB_ALTITRACK_PID 0x0001 - -/* - * TTi (Thurlby Thandar Instruments) - */ -#define TTI_VID 0x103E /* Vendor Id */ -#define TTI_QL355P_PID 0x03E8 /* TTi QL355P power supply */ - -/* Interbiometrics USB I/O Board */ -/* Developed for Interbiometrics by Rudolf Gugler */ -#define INTERBIOMETRICS_VID 0x1209 -#define INTERBIOMETRICS_IOBOARD_PID 0x1002 -#define INTERBIOMETRICS_MINI_IOBOARD_PID 0x1006 - -/* - * Testo products (http://www.testo.com/) - * Submitted by Colin Leroy - */ -#define TESTO_VID 0x128D -#define TESTO_USB_INTERFACE_PID 0x0001 - -/* - * Mobility Electronics products. - */ -#define MOBILITY_VID 0x1342 -#define MOBILITY_USB_SERIAL_PID 0x0202 /* EasiDock USB 200 serial */ - -/* - * FIC / OpenMoko, Inc. http://wiki.openmoko.org/wiki/Neo1973_Debug_Board_v3 - * Submitted by Harald Welte - */ -#define FIC_VID 0x1457 -#define FIC_NEO1973_DEBUG_PID 0x5118 - -/* Olimex */ -#define OLIMEX_VID 0x15BA -#define OLIMEX_ARM_USB_OCD_PID 0x0003 -#define OLIMEX_ARM_USB_OCD_H_PID 0x002b - -/* - * Telldus Technologies - */ -#define TELLDUS_VID 0x1781 /* Vendor ID */ -#define TELLDUS_TELLSTICK_PID 0x0C30 /* RF control dongle 433 MHz using FT232RL */ - -/* - * RT Systems programming cables for various ham radios - */ -#define RTSYSTEMS_VID 0x2100 /* Vendor ID */ -#define RTSYSTEMS_SERIAL_VX7_PID 0x9e52 /* Serial converter for VX-7 Radios using FT232RL */ -#define RTSYSTEMS_CT29B_PID 0x9e54 /* CT29B Radio Cable */ -#define RTSYSTEMS_RTS01_PID 0x9e57 /* USB-RTS01 Radio Cable */ - - -/* - * Physik Instrumente - * http://www.physikinstrumente.com/en/products/ - */ -/* These two devices use the VID of FTDI */ -#define PI_C865_PID 0xe0a0 /* PI C-865 Piezomotor Controller */ -#define PI_C857_PID 0xe0a1 /* PI Encoder Trigger Box */ - -#define PI_VID 0x1a72 /* Vendor ID */ -#define PI_C866_PID 0x1000 /* PI C-866 Piezomotor Controller */ -#define PI_C663_PID 0x1001 /* PI C-663 Mercury-Step */ -#define PI_C725_PID 0x1002 /* PI C-725 Piezomotor Controller */ -#define PI_E517_PID 0x1005 /* PI E-517 Digital Piezo Controller Operation Module */ -#define PI_C863_PID 0x1007 /* PI C-863 */ -#define PI_E861_PID 0x1008 /* PI E-861 Piezomotor Controller */ -#define PI_C867_PID 0x1009 /* PI C-867 Piezomotor Controller */ -#define PI_E609_PID 0x100D /* PI E-609 Digital Piezo Controller */ -#define PI_E709_PID 0x100E /* PI E-709 Digital Piezo Controller */ -#define PI_100F_PID 0x100F /* PI Digital Piezo Controller */ -#define PI_1011_PID 0x1011 /* PI Digital Piezo Controller */ -#define PI_1012_PID 0x1012 /* PI Motion Controller */ -#define PI_1013_PID 0x1013 /* PI Motion Controller */ -#define PI_1014_PID 0x1014 /* PI Device */ -#define PI_1015_PID 0x1015 /* PI Device */ -#define PI_1016_PID 0x1016 /* PI Digital Servo Module */ - -/* - * Kondo Kagaku Co.Ltd. - * http://www.kondo-robot.com/EN - */ -#define KONDO_VID 0x165c -#define KONDO_USB_SERIAL_PID 0x0002 - -/* - * Bayer Ascensia Contour blood glucose meter USB-converter cable. - * http://winglucofacts.com/cables/ - */ -#define BAYER_VID 0x1A79 -#define BAYER_CONTOUR_CABLE_PID 0x6001 - -/* - * The following are the values for the Matrix Orbital FTDI Range - * Anything in this range will use an FT232RL. - */ -#define MTXORB_VID 0x1B3D -#define MTXORB_FTDI_RANGE_0100_PID 0x0100 -#define MTXORB_FTDI_RANGE_0101_PID 0x0101 -#define MTXORB_FTDI_RANGE_0102_PID 0x0102 -#define MTXORB_FTDI_RANGE_0103_PID 0x0103 -#define MTXORB_FTDI_RANGE_0104_PID 0x0104 -#define MTXORB_FTDI_RANGE_0105_PID 0x0105 -#define MTXORB_FTDI_RANGE_0106_PID 0x0106 -#define MTXORB_FTDI_RANGE_0107_PID 0x0107 -#define MTXORB_FTDI_RANGE_0108_PID 0x0108 -#define MTXORB_FTDI_RANGE_0109_PID 0x0109 -#define MTXORB_FTDI_RANGE_010A_PID 0x010A -#define MTXORB_FTDI_RANGE_010B_PID 0x010B -#define MTXORB_FTDI_RANGE_010C_PID 0x010C -#define MTXORB_FTDI_RANGE_010D_PID 0x010D -#define MTXORB_FTDI_RANGE_010E_PID 0x010E -#define MTXORB_FTDI_RANGE_010F_PID 0x010F -#define MTXORB_FTDI_RANGE_0110_PID 0x0110 -#define MTXORB_FTDI_RANGE_0111_PID 0x0111 -#define MTXORB_FTDI_RANGE_0112_PID 0x0112 -#define MTXORB_FTDI_RANGE_0113_PID 0x0113 -#define MTXORB_FTDI_RANGE_0114_PID 0x0114 -#define MTXORB_FTDI_RANGE_0115_PID 0x0115 -#define MTXORB_FTDI_RANGE_0116_PID 0x0116 -#define MTXORB_FTDI_RANGE_0117_PID 0x0117 -#define MTXORB_FTDI_RANGE_0118_PID 0x0118 -#define MTXORB_FTDI_RANGE_0119_PID 0x0119 -#define MTXORB_FTDI_RANGE_011A_PID 0x011A -#define MTXORB_FTDI_RANGE_011B_PID 0x011B -#define MTXORB_FTDI_RANGE_011C_PID 0x011C -#define MTXORB_FTDI_RANGE_011D_PID 0x011D -#define MTXORB_FTDI_RANGE_011E_PID 0x011E -#define MTXORB_FTDI_RANGE_011F_PID 0x011F -#define MTXORB_FTDI_RANGE_0120_PID 0x0120 -#define MTXORB_FTDI_RANGE_0121_PID 0x0121 -#define MTXORB_FTDI_RANGE_0122_PID 0x0122 -#define MTXORB_FTDI_RANGE_0123_PID 0x0123 -#define MTXORB_FTDI_RANGE_0124_PID 0x0124 -#define MTXORB_FTDI_RANGE_0125_PID 0x0125 -#define MTXORB_FTDI_RANGE_0126_PID 0x0126 -#define MTXORB_FTDI_RANGE_0127_PID 0x0127 -#define MTXORB_FTDI_RANGE_0128_PID 0x0128 -#define MTXORB_FTDI_RANGE_0129_PID 0x0129 -#define MTXORB_FTDI_RANGE_012A_PID 0x012A -#define MTXORB_FTDI_RANGE_012B_PID 0x012B -#define MTXORB_FTDI_RANGE_012C_PID 0x012C -#define MTXORB_FTDI_RANGE_012D_PID 0x012D -#define MTXORB_FTDI_RANGE_012E_PID 0x012E -#define MTXORB_FTDI_RANGE_012F_PID 0x012F -#define MTXORB_FTDI_RANGE_0130_PID 0x0130 -#define MTXORB_FTDI_RANGE_0131_PID 0x0131 -#define MTXORB_FTDI_RANGE_0132_PID 0x0132 -#define MTXORB_FTDI_RANGE_0133_PID 0x0133 -#define MTXORB_FTDI_RANGE_0134_PID 0x0134 -#define MTXORB_FTDI_RANGE_0135_PID 0x0135 -#define MTXORB_FTDI_RANGE_0136_PID 0x0136 -#define MTXORB_FTDI_RANGE_0137_PID 0x0137 -#define MTXORB_FTDI_RANGE_0138_PID 0x0138 -#define MTXORB_FTDI_RANGE_0139_PID 0x0139 -#define MTXORB_FTDI_RANGE_013A_PID 0x013A -#define MTXORB_FTDI_RANGE_013B_PID 0x013B -#define MTXORB_FTDI_RANGE_013C_PID 0x013C -#define MTXORB_FTDI_RANGE_013D_PID 0x013D -#define MTXORB_FTDI_RANGE_013E_PID 0x013E -#define MTXORB_FTDI_RANGE_013F_PID 0x013F -#define MTXORB_FTDI_RANGE_0140_PID 0x0140 -#define MTXORB_FTDI_RANGE_0141_PID 0x0141 -#define MTXORB_FTDI_RANGE_0142_PID 0x0142 -#define MTXORB_FTDI_RANGE_0143_PID 0x0143 -#define MTXORB_FTDI_RANGE_0144_PID 0x0144 -#define MTXORB_FTDI_RANGE_0145_PID 0x0145 -#define MTXORB_FTDI_RANGE_0146_PID 0x0146 -#define MTXORB_FTDI_RANGE_0147_PID 0x0147 -#define MTXORB_FTDI_RANGE_0148_PID 0x0148 -#define MTXORB_FTDI_RANGE_0149_PID 0x0149 -#define MTXORB_FTDI_RANGE_014A_PID 0x014A -#define MTXORB_FTDI_RANGE_014B_PID 0x014B -#define MTXORB_FTDI_RANGE_014C_PID 0x014C -#define MTXORB_FTDI_RANGE_014D_PID 0x014D -#define MTXORB_FTDI_RANGE_014E_PID 0x014E -#define MTXORB_FTDI_RANGE_014F_PID 0x014F -#define MTXORB_FTDI_RANGE_0150_PID 0x0150 -#define MTXORB_FTDI_RANGE_0151_PID 0x0151 -#define MTXORB_FTDI_RANGE_0152_PID 0x0152 -#define MTXORB_FTDI_RANGE_0153_PID 0x0153 -#define MTXORB_FTDI_RANGE_0154_PID 0x0154 -#define MTXORB_FTDI_RANGE_0155_PID 0x0155 -#define MTXORB_FTDI_RANGE_0156_PID 0x0156 -#define MTXORB_FTDI_RANGE_0157_PID 0x0157 -#define MTXORB_FTDI_RANGE_0158_PID 0x0158 -#define MTXORB_FTDI_RANGE_0159_PID 0x0159 -#define MTXORB_FTDI_RANGE_015A_PID 0x015A -#define MTXORB_FTDI_RANGE_015B_PID 0x015B -#define MTXORB_FTDI_RANGE_015C_PID 0x015C -#define MTXORB_FTDI_RANGE_015D_PID 0x015D -#define MTXORB_FTDI_RANGE_015E_PID 0x015E -#define MTXORB_FTDI_RANGE_015F_PID 0x015F -#define MTXORB_FTDI_RANGE_0160_PID 0x0160 -#define MTXORB_FTDI_RANGE_0161_PID 0x0161 -#define MTXORB_FTDI_RANGE_0162_PID 0x0162 -#define MTXORB_FTDI_RANGE_0163_PID 0x0163 -#define MTXORB_FTDI_RANGE_0164_PID 0x0164 -#define MTXORB_FTDI_RANGE_0165_PID 0x0165 -#define MTXORB_FTDI_RANGE_0166_PID 0x0166 -#define MTXORB_FTDI_RANGE_0167_PID 0x0167 -#define MTXORB_FTDI_RANGE_0168_PID 0x0168 -#define MTXORB_FTDI_RANGE_0169_PID 0x0169 -#define MTXORB_FTDI_RANGE_016A_PID 0x016A -#define MTXORB_FTDI_RANGE_016B_PID 0x016B -#define MTXORB_FTDI_RANGE_016C_PID 0x016C -#define MTXORB_FTDI_RANGE_016D_PID 0x016D -#define MTXORB_FTDI_RANGE_016E_PID 0x016E -#define MTXORB_FTDI_RANGE_016F_PID 0x016F -#define MTXORB_FTDI_RANGE_0170_PID 0x0170 -#define MTXORB_FTDI_RANGE_0171_PID 0x0171 -#define MTXORB_FTDI_RANGE_0172_PID 0x0172 -#define MTXORB_FTDI_RANGE_0173_PID 0x0173 -#define MTXORB_FTDI_RANGE_0174_PID 0x0174 -#define MTXORB_FTDI_RANGE_0175_PID 0x0175 -#define MTXORB_FTDI_RANGE_0176_PID 0x0176 -#define MTXORB_FTDI_RANGE_0177_PID 0x0177 -#define MTXORB_FTDI_RANGE_0178_PID 0x0178 -#define MTXORB_FTDI_RANGE_0179_PID 0x0179 -#define MTXORB_FTDI_RANGE_017A_PID 0x017A -#define MTXORB_FTDI_RANGE_017B_PID 0x017B -#define MTXORB_FTDI_RANGE_017C_PID 0x017C -#define MTXORB_FTDI_RANGE_017D_PID 0x017D -#define MTXORB_FTDI_RANGE_017E_PID 0x017E -#define MTXORB_FTDI_RANGE_017F_PID 0x017F -#define MTXORB_FTDI_RANGE_0180_PID 0x0180 -#define MTXORB_FTDI_RANGE_0181_PID 0x0181 -#define MTXORB_FTDI_RANGE_0182_PID 0x0182 -#define MTXORB_FTDI_RANGE_0183_PID 0x0183 -#define MTXORB_FTDI_RANGE_0184_PID 0x0184 -#define MTXORB_FTDI_RANGE_0185_PID 0x0185 -#define MTXORB_FTDI_RANGE_0186_PID 0x0186 -#define MTXORB_FTDI_RANGE_0187_PID 0x0187 -#define MTXORB_FTDI_RANGE_0188_PID 0x0188 -#define MTXORB_FTDI_RANGE_0189_PID 0x0189 -#define MTXORB_FTDI_RANGE_018A_PID 0x018A -#define MTXORB_FTDI_RANGE_018B_PID 0x018B -#define MTXORB_FTDI_RANGE_018C_PID 0x018C -#define MTXORB_FTDI_RANGE_018D_PID 0x018D -#define MTXORB_FTDI_RANGE_018E_PID 0x018E -#define MTXORB_FTDI_RANGE_018F_PID 0x018F -#define MTXORB_FTDI_RANGE_0190_PID 0x0190 -#define MTXORB_FTDI_RANGE_0191_PID 0x0191 -#define MTXORB_FTDI_RANGE_0192_PID 0x0192 -#define MTXORB_FTDI_RANGE_0193_PID 0x0193 -#define MTXORB_FTDI_RANGE_0194_PID 0x0194 -#define MTXORB_FTDI_RANGE_0195_PID 0x0195 -#define MTXORB_FTDI_RANGE_0196_PID 0x0196 -#define MTXORB_FTDI_RANGE_0197_PID 0x0197 -#define MTXORB_FTDI_RANGE_0198_PID 0x0198 -#define MTXORB_FTDI_RANGE_0199_PID 0x0199 -#define MTXORB_FTDI_RANGE_019A_PID 0x019A -#define MTXORB_FTDI_RANGE_019B_PID 0x019B -#define MTXORB_FTDI_RANGE_019C_PID 0x019C -#define MTXORB_FTDI_RANGE_019D_PID 0x019D -#define MTXORB_FTDI_RANGE_019E_PID 0x019E -#define MTXORB_FTDI_RANGE_019F_PID 0x019F -#define MTXORB_FTDI_RANGE_01A0_PID 0x01A0 -#define MTXORB_FTDI_RANGE_01A1_PID 0x01A1 -#define MTXORB_FTDI_RANGE_01A2_PID 0x01A2 -#define MTXORB_FTDI_RANGE_01A3_PID 0x01A3 -#define MTXORB_FTDI_RANGE_01A4_PID 0x01A4 -#define MTXORB_FTDI_RANGE_01A5_PID 0x01A5 -#define MTXORB_FTDI_RANGE_01A6_PID 0x01A6 -#define MTXORB_FTDI_RANGE_01A7_PID 0x01A7 -#define MTXORB_FTDI_RANGE_01A8_PID 0x01A8 -#define MTXORB_FTDI_RANGE_01A9_PID 0x01A9 -#define MTXORB_FTDI_RANGE_01AA_PID 0x01AA -#define MTXORB_FTDI_RANGE_01AB_PID 0x01AB -#define MTXORB_FTDI_RANGE_01AC_PID 0x01AC -#define MTXORB_FTDI_RANGE_01AD_PID 0x01AD -#define MTXORB_FTDI_RANGE_01AE_PID 0x01AE -#define MTXORB_FTDI_RANGE_01AF_PID 0x01AF -#define MTXORB_FTDI_RANGE_01B0_PID 0x01B0 -#define MTXORB_FTDI_RANGE_01B1_PID 0x01B1 -#define MTXORB_FTDI_RANGE_01B2_PID 0x01B2 -#define MTXORB_FTDI_RANGE_01B3_PID 0x01B3 -#define MTXORB_FTDI_RANGE_01B4_PID 0x01B4 -#define MTXORB_FTDI_RANGE_01B5_PID 0x01B5 -#define MTXORB_FTDI_RANGE_01B6_PID 0x01B6 -#define MTXORB_FTDI_RANGE_01B7_PID 0x01B7 -#define MTXORB_FTDI_RANGE_01B8_PID 0x01B8 -#define MTXORB_FTDI_RANGE_01B9_PID 0x01B9 -#define MTXORB_FTDI_RANGE_01BA_PID 0x01BA -#define MTXORB_FTDI_RANGE_01BB_PID 0x01BB -#define MTXORB_FTDI_RANGE_01BC_PID 0x01BC -#define MTXORB_FTDI_RANGE_01BD_PID 0x01BD -#define MTXORB_FTDI_RANGE_01BE_PID 0x01BE -#define MTXORB_FTDI_RANGE_01BF_PID 0x01BF -#define MTXORB_FTDI_RANGE_01C0_PID 0x01C0 -#define MTXORB_FTDI_RANGE_01C1_PID 0x01C1 -#define MTXORB_FTDI_RANGE_01C2_PID 0x01C2 -#define MTXORB_FTDI_RANGE_01C3_PID 0x01C3 -#define MTXORB_FTDI_RANGE_01C4_PID 0x01C4 -#define MTXORB_FTDI_RANGE_01C5_PID 0x01C5 -#define MTXORB_FTDI_RANGE_01C6_PID 0x01C6 -#define MTXORB_FTDI_RANGE_01C7_PID 0x01C7 -#define MTXORB_FTDI_RANGE_01C8_PID 0x01C8 -#define MTXORB_FTDI_RANGE_01C9_PID 0x01C9 -#define MTXORB_FTDI_RANGE_01CA_PID 0x01CA -#define MTXORB_FTDI_RANGE_01CB_PID 0x01CB -#define MTXORB_FTDI_RANGE_01CC_PID 0x01CC -#define MTXORB_FTDI_RANGE_01CD_PID 0x01CD -#define MTXORB_FTDI_RANGE_01CE_PID 0x01CE -#define MTXORB_FTDI_RANGE_01CF_PID 0x01CF -#define MTXORB_FTDI_RANGE_01D0_PID 0x01D0 -#define MTXORB_FTDI_RANGE_01D1_PID 0x01D1 -#define MTXORB_FTDI_RANGE_01D2_PID 0x01D2 -#define MTXORB_FTDI_RANGE_01D3_PID 0x01D3 -#define MTXORB_FTDI_RANGE_01D4_PID 0x01D4 -#define MTXORB_FTDI_RANGE_01D5_PID 0x01D5 -#define MTXORB_FTDI_RANGE_01D6_PID 0x01D6 -#define MTXORB_FTDI_RANGE_01D7_PID 0x01D7 -#define MTXORB_FTDI_RANGE_01D8_PID 0x01D8 -#define MTXORB_FTDI_RANGE_01D9_PID 0x01D9 -#define MTXORB_FTDI_RANGE_01DA_PID 0x01DA -#define MTXORB_FTDI_RANGE_01DB_PID 0x01DB -#define MTXORB_FTDI_RANGE_01DC_PID 0x01DC -#define MTXORB_FTDI_RANGE_01DD_PID 0x01DD -#define MTXORB_FTDI_RANGE_01DE_PID 0x01DE -#define MTXORB_FTDI_RANGE_01DF_PID 0x01DF -#define MTXORB_FTDI_RANGE_01E0_PID 0x01E0 -#define MTXORB_FTDI_RANGE_01E1_PID 0x01E1 -#define MTXORB_FTDI_RANGE_01E2_PID 0x01E2 -#define MTXORB_FTDI_RANGE_01E3_PID 0x01E3 -#define MTXORB_FTDI_RANGE_01E4_PID 0x01E4 -#define MTXORB_FTDI_RANGE_01E5_PID 0x01E5 -#define MTXORB_FTDI_RANGE_01E6_PID 0x01E6 -#define MTXORB_FTDI_RANGE_01E7_PID 0x01E7 -#define MTXORB_FTDI_RANGE_01E8_PID 0x01E8 -#define MTXORB_FTDI_RANGE_01E9_PID 0x01E9 -#define MTXORB_FTDI_RANGE_01EA_PID 0x01EA -#define MTXORB_FTDI_RANGE_01EB_PID 0x01EB -#define MTXORB_FTDI_RANGE_01EC_PID 0x01EC -#define MTXORB_FTDI_RANGE_01ED_PID 0x01ED -#define MTXORB_FTDI_RANGE_01EE_PID 0x01EE -#define MTXORB_FTDI_RANGE_01EF_PID 0x01EF -#define MTXORB_FTDI_RANGE_01F0_PID 0x01F0 -#define MTXORB_FTDI_RANGE_01F1_PID 0x01F1 -#define MTXORB_FTDI_RANGE_01F2_PID 0x01F2 -#define MTXORB_FTDI_RANGE_01F3_PID 0x01F3 -#define MTXORB_FTDI_RANGE_01F4_PID 0x01F4 -#define MTXORB_FTDI_RANGE_01F5_PID 0x01F5 -#define MTXORB_FTDI_RANGE_01F6_PID 0x01F6 -#define MTXORB_FTDI_RANGE_01F7_PID 0x01F7 -#define MTXORB_FTDI_RANGE_01F8_PID 0x01F8 -#define MTXORB_FTDI_RANGE_01F9_PID 0x01F9 -#define MTXORB_FTDI_RANGE_01FA_PID 0x01FA -#define MTXORB_FTDI_RANGE_01FB_PID 0x01FB -#define MTXORB_FTDI_RANGE_01FC_PID 0x01FC -#define MTXORB_FTDI_RANGE_01FD_PID 0x01FD -#define MTXORB_FTDI_RANGE_01FE_PID 0x01FE -#define MTXORB_FTDI_RANGE_01FF_PID 0x01FF - - - -/* - * The Mobility Lab (TML) - * Submitted by Pierre Castella - */ -#define TML_VID 0x1B91 /* Vendor ID */ -#define TML_USB_SERIAL_PID 0x0064 /* USB - Serial Converter */ - -/* Alti-2 products http://www.alti-2.com */ -#define ALTI2_VID 0x1BC9 -#define ALTI2_N3_PID 0x6001 /* Neptune 3 */ - -/* - * Ionics PlugComputer - */ -#define IONICS_VID 0x1c0c -#define IONICS_PLUGCOMPUTER_PID 0x0102 - -/* - * Dresden Elektronik Sensor Terminal Board - */ -#define DE_VID 0x1cf1 /* Vendor ID */ -#define STB_PID 0x0001 /* Sensor Terminal Board */ -#define WHT_PID 0x0004 /* Wireless Handheld Terminal */ - -/* - * STMicroelectonics - */ -#define ST_VID 0x0483 -#define ST_STMCLT1030_PID 0x3747 /* ST Micro Connect Lite STMCLT1030 */ - -/* - * Papouch products (http://www.papouch.com/) - * Submitted by Folkert van Heusden - */ - -#define PAPOUCH_VID 0x5050 /* Vendor ID */ -#define PAPOUCH_SB485_PID 0x0100 /* Papouch SB485 USB-485/422 Converter */ -#define PAPOUCH_AP485_PID 0x0101 /* AP485 USB-RS485 Converter */ -#define PAPOUCH_SB422_PID 0x0102 /* Papouch SB422 USB-RS422 Converter */ -#define PAPOUCH_SB485_2_PID 0x0103 /* Papouch SB485 USB-485/422 Converter */ -#define PAPOUCH_AP485_2_PID 0x0104 /* AP485 USB-RS485 Converter */ -#define PAPOUCH_SB422_2_PID 0x0105 /* Papouch SB422 USB-RS422 Converter */ -#define PAPOUCH_SB485S_PID 0x0106 /* Papouch SB485S USB-485/422 Converter */ -#define PAPOUCH_SB485C_PID 0x0107 /* Papouch SB485C USB-485/422 Converter */ -#define PAPOUCH_LEC_PID 0x0300 /* LEC USB Converter */ -#define PAPOUCH_SB232_PID 0x0301 /* Papouch SB232 USB-RS232 Converter */ -#define PAPOUCH_TMU_PID 0x0400 /* TMU USB Thermometer */ -#define PAPOUCH_IRAMP_PID 0x0500 /* Papouch IRAmp Duplex */ -#define PAPOUCH_DRAK5_PID 0x0700 /* Papouch DRAK5 */ -#define PAPOUCH_QUIDO8x8_PID 0x0800 /* Papouch Quido 8/8 Module */ -#define PAPOUCH_QUIDO4x4_PID 0x0900 /* Papouch Quido 4/4 Module */ -#define PAPOUCH_QUIDO2x2_PID 0x0a00 /* Papouch Quido 2/2 Module */ -#define PAPOUCH_QUIDO10x1_PID 0x0b00 /* Papouch Quido 10/1 Module */ -#define PAPOUCH_QUIDO30x3_PID 0x0c00 /* Papouch Quido 30/3 Module */ -#define PAPOUCH_QUIDO60x3_PID 0x0d00 /* Papouch Quido 60(100)/3 Module */ -#define PAPOUCH_QUIDO2x16_PID 0x0e00 /* Papouch Quido 2/16 Module */ -#define PAPOUCH_QUIDO3x32_PID 0x0f00 /* Papouch Quido 3/32 Module */ -#define PAPOUCH_DRAK6_PID 0x1000 /* Papouch DRAK6 */ -#define PAPOUCH_UPSUSB_PID 0x8000 /* Papouch UPS-USB adapter */ -#define PAPOUCH_MU_PID 0x8001 /* MU controller */ -#define PAPOUCH_SIMUKEY_PID 0x8002 /* Papouch SimuKey */ -#define PAPOUCH_AD4USB_PID 0x8003 /* AD4USB Measurement Module */ -#define PAPOUCH_GMUX_PID 0x8004 /* Papouch GOLIATH MUX */ -#define PAPOUCH_GMSR_PID 0x8005 /* Papouch GOLIATH MSR */ - -/* - * Marvell SheevaPlug - */ -#define MARVELL_VID 0x9e88 -#define MARVELL_SHEEVAPLUG_PID 0x9e8f - -/* - * Evolution Robotics products (http://www.evolution.com/). - * Submitted by Shawn M. Lavelle. - */ -#define EVOLUTION_VID 0xDEEE /* Vendor ID */ -#define EVOLUTION_ER1_PID 0x0300 /* ER1 Control Module */ -#define EVO_8U232AM_PID 0x02FF /* Evolution robotics RCM2 (FT232AM)*/ -#define EVO_HYBRID_PID 0x0302 /* Evolution robotics RCM4 PID (FT232BM)*/ -#define EVO_RCM4_PID 0x0303 /* Evolution robotics RCM4 PID */ - -/* - * MJS Gadgets HD Radio / XM Radio / Sirius Radio interfaces (using VID 0x0403) - */ -#define MJSG_GENERIC_PID 0x9378 -#define MJSG_SR_RADIO_PID 0x9379 -#define MJSG_XM_RADIO_PID 0x937A -#define MJSG_HD_RADIO_PID 0x937C - -/* - * D.O.Tec products (http://www.directout.eu) - */ -#define FTDI_DOTEC_PID 0x9868 - -/* - * Xverve Signalyzer tools (http://www.signalyzer.com/) - */ -#define XVERVE_SIGNALYZER_ST_PID 0xBCA0 -#define XVERVE_SIGNALYZER_SLITE_PID 0xBCA1 -#define XVERVE_SIGNALYZER_SH2_PID 0xBCA2 -#define XVERVE_SIGNALYZER_SH4_PID 0xBCA4 - -/* - * Segway Robotic Mobility Platform USB interface (using VID 0x0403) - * Submitted by John G. Rogers - */ -#define SEGWAY_RMP200_PID 0xe729 - - -/* - * Accesio USB Data Acquisition products (http://www.accesio.com/) - */ -#define ACCESIO_COM4SM_PID 0xD578 - -/* www.sciencescope.co.uk educational dataloggers */ -#define FTDI_SCIENCESCOPE_LOGBOOKML_PID 0xFF18 -#define FTDI_SCIENCESCOPE_LS_LOGBOOK_PID 0xFF1C -#define FTDI_SCIENCESCOPE_HS_LOGBOOK_PID 0xFF1D - -/* - * Milkymist One JTAG/Serial - */ -#define QIHARDWARE_VID 0x20B7 -#define MILKYMISTONE_JTAGSERIAL_PID 0x0713 - -/* - * CTI GmbH RS485 Converter http://www.cti-lean.com/ - */ -/* USB-485-Mini*/ -#define FTDI_CTI_MINI_PID 0xF608 -/* USB-Nano-485*/ -#define FTDI_CTI_NANO_PID 0xF60B - -/* - * ZeitControl cardsystems GmbH rfid-readers http://zeitconrol.de - */ -/* TagTracer MIFARE*/ -#define FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID 0xF7C0 - -/* - * Rainforest Automation - */ -/* ZigBee controller */ -#define FTDI_RF_R106 0x8A28 - -/* - * Product: HCP HIT GPRS modem - * Manufacturer: HCP d.o.o. - * ATI command output: Cinterion MC55i - */ -#define FTDI_CINTERION_MC55I_PID 0xA951 diff --git a/qemu/hw/usb/quirks-pl2303-ids.h b/qemu/hw/usb/quirks-pl2303-ids.h deleted file mode 100644 index 8dbdb46ff..000000000 --- a/qemu/hw/usb/quirks-pl2303-ids.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Prolific PL2303 USB to serial adaptor driver header file - * - * 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. - * - */ - -#define BENQ_VENDOR_ID 0x04a5 -#define BENQ_PRODUCT_ID_S81 0x4027 - -#define PL2303_VENDOR_ID 0x067b -#define PL2303_PRODUCT_ID 0x2303 -#define PL2303_PRODUCT_ID_RSAQ2 0x04bb -#define PL2303_PRODUCT_ID_DCU11 0x1234 -#define PL2303_PRODUCT_ID_PHAROS 0xaaa0 -#define PL2303_PRODUCT_ID_RSAQ3 0xaaa2 -#define PL2303_PRODUCT_ID_ALDIGA 0x0611 -#define PL2303_PRODUCT_ID_MMX 0x0612 -#define PL2303_PRODUCT_ID_GPRS 0x0609 -#define PL2303_PRODUCT_ID_HCR331 0x331a -#define PL2303_PRODUCT_ID_MOTOROLA 0x0307 - -#define ATEN_VENDOR_ID 0x0557 -#define ATEN_VENDOR_ID2 0x0547 -#define ATEN_PRODUCT_ID 0x2008 - -#define IODATA_VENDOR_ID 0x04bb -#define IODATA_PRODUCT_ID 0x0a03 -#define IODATA_PRODUCT_ID_RSAQ5 0x0a0e - -#define ELCOM_VENDOR_ID 0x056e -#define ELCOM_PRODUCT_ID 0x5003 -#define ELCOM_PRODUCT_ID_UCSGT 0x5004 - -#define ITEGNO_VENDOR_ID 0x0eba -#define ITEGNO_PRODUCT_ID 0x1080 -#define ITEGNO_PRODUCT_ID_2080 0x2080 - -#define MA620_VENDOR_ID 0x0df7 -#define MA620_PRODUCT_ID 0x0620 - -#define RATOC_VENDOR_ID 0x0584 -#define RATOC_PRODUCT_ID 0xb000 - -#define TRIPP_VENDOR_ID 0x2478 -#define TRIPP_PRODUCT_ID 0x2008 - -#define RADIOSHACK_VENDOR_ID 0x1453 -#define RADIOSHACK_PRODUCT_ID 0x4026 - -#define DCU10_VENDOR_ID 0x0731 -#define DCU10_PRODUCT_ID 0x0528 - -#define SITECOM_VENDOR_ID 0x6189 -#define SITECOM_PRODUCT_ID 0x2068 - -/* Alcatel OT535/735 USB cable */ -#define ALCATEL_VENDOR_ID 0x11f7 -#define ALCATEL_PRODUCT_ID 0x02df - -/* Samsung I330 phone cradle */ -#define SAMSUNG_VENDOR_ID 0x04e8 -#define SAMSUNG_PRODUCT_ID 0x8001 - -#define SIEMENS_VENDOR_ID 0x11f5 -#define SIEMENS_PRODUCT_ID_SX1 0x0001 -#define SIEMENS_PRODUCT_ID_X65 0x0003 -#define SIEMENS_PRODUCT_ID_X75 0x0004 -#define SIEMENS_PRODUCT_ID_EF81 0x0005 - -#define SYNTECH_VENDOR_ID 0x0745 -#define SYNTECH_PRODUCT_ID 0x0001 - -/* Nokia CA-42 Cable */ -#define NOKIA_CA42_VENDOR_ID 0x078b -#define NOKIA_CA42_PRODUCT_ID 0x1234 - -/* CA-42 CLONE Cable www.ca-42.com chipset: Prolific Technology Inc */ -#define CA_42_CA42_VENDOR_ID 0x10b5 -#define CA_42_CA42_PRODUCT_ID 0xac70 - -#define SAGEM_VENDOR_ID 0x079b -#define SAGEM_PRODUCT_ID 0x0027 - -/* Leadtek GPS 9531 (ID 0413:2101) */ -#define LEADTEK_VENDOR_ID 0x0413 -#define LEADTEK_9531_PRODUCT_ID 0x2101 - -/* USB GSM cable from Speed Dragon Multimedia, Ltd */ -#define SPEEDDRAGON_VENDOR_ID 0x0e55 -#define SPEEDDRAGON_PRODUCT_ID 0x110b - -/* DATAPILOT Universal-2 Phone Cable */ -#define DATAPILOT_U2_VENDOR_ID 0x0731 -#define DATAPILOT_U2_PRODUCT_ID 0x2003 - -/* Belkin "F5U257" Serial Adapter */ -#define BELKIN_VENDOR_ID 0x050d -#define BELKIN_PRODUCT_ID 0x0257 - -/* Alcor Micro Corp. USB 2.0 TO RS-232 */ -#define ALCOR_VENDOR_ID 0x058F -#define ALCOR_PRODUCT_ID 0x9720 - -/* Willcom WS002IN Data Driver (by NetIndex Inc.) */ -#define WS002IN_VENDOR_ID 0x11f6 -#define WS002IN_PRODUCT_ID 0x2001 - -/* Corega CG-USBRS232R Serial Adapter */ -#define COREGA_VENDOR_ID 0x07aa -#define COREGA_PRODUCT_ID 0x002a - -/* Y.C. Cable U.S.A., Inc - USB to RS-232 */ -#define YCCABLE_VENDOR_ID 0x05ad -#define YCCABLE_PRODUCT_ID 0x0fba - -/* "Superial" USB - Serial */ -#define SUPERIAL_VENDOR_ID 0x5372 -#define SUPERIAL_PRODUCT_ID 0x2303 - -/* Hewlett-Packard LD220-HP POS Pole Display */ -#define HP_VENDOR_ID 0x03f0 -#define HP_LD220_PRODUCT_ID 0x3524 - -/* Cressi Edy (diving computer) PC interface */ -#define CRESSI_VENDOR_ID 0x04b8 -#define CRESSI_EDY_PRODUCT_ID 0x0521 - -/* Zeagle dive computer interface */ -#define ZEAGLE_VENDOR_ID 0x04b8 -#define ZEAGLE_N2ITION3_PRODUCT_ID 0x0522 - -/* Sony, USB data cable for CMD-Jxx mobile phones */ -#define SONY_VENDOR_ID 0x054c -#define SONY_QN3USB_PRODUCT_ID 0x0437 - -/* Sanwa KB-USB2 multimeter cable (ID: 11ad:0001) */ -#define SANWA_VENDOR_ID 0x11ad -#define SANWA_PRODUCT_ID 0x0001 - -/* ADLINK ND-6530 RS232,RS485 and RS422 adapter */ -#define ADLINK_VENDOR_ID 0x0b63 -#define ADLINK_ND6530_PRODUCT_ID 0x6530 - -/* SMART USB Serial Adapter */ -#define SMART_VENDOR_ID 0x0b8c -#define SMART_PRODUCT_ID 0x2303 diff --git a/qemu/hw/usb/quirks.c b/qemu/hw/usb/quirks.c deleted file mode 100644 index 38a9c5634..000000000 --- a/qemu/hw/usb/quirks.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * USB quirk handling - * - * Copyright (c) 2012 Red Hat, Inc. - * - * Red Hat Authors: - * Hans de Goede - * - * 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. - */ - -#include "qemu/osdep.h" -#include "quirks.h" -#include "hw/usb.h" - -static bool usb_id_match(const struct usb_device_id *ids, - uint16_t vendor_id, uint16_t product_id, - uint8_t interface_class, uint8_t interface_subclass, - uint8_t interface_protocol) { - int i; - - for (i = 0; ids[i].vendor_id != -1; i++) { - if (ids[i].vendor_id == vendor_id && - ids[i].product_id == product_id && - (ids[i].interface_class == -1 || - (ids[i].interface_class == interface_class && - ids[i].interface_subclass == interface_subclass && - ids[i].interface_protocol == interface_protocol))) { - return true; - } - } - return false; -} - -int usb_get_quirks(uint16_t vendor_id, uint16_t product_id, - uint8_t interface_class, uint8_t interface_subclass, - uint8_t interface_protocol) -{ - int quirks = 0; - - if (usb_id_match(usbredir_raw_serial_ids, vendor_id, product_id, - interface_class, interface_subclass, interface_protocol)) { - quirks |= USB_QUIRK_BUFFER_BULK_IN; - } - if (usb_id_match(usbredir_ftdi_serial_ids, vendor_id, product_id, - interface_class, interface_subclass, interface_protocol)) { - quirks |= USB_QUIRK_BUFFER_BULK_IN | USB_QUIRK_IS_FTDI; - } - - return quirks; -} diff --git a/qemu/hw/usb/quirks.h b/qemu/hw/usb/quirks.h deleted file mode 100644 index 8dc606552..000000000 --- a/qemu/hw/usb/quirks.h +++ /dev/null @@ -1,910 +0,0 @@ -/* - * USB quirk handling - * - * Copyright (c) 2012 Red Hat, Inc. - * - * Red Hat Authors: - * Hans de Goede - * - * 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. - */ - -/* 1 on 1 copy of linux/drivers/usb/serial/ftdi_sio_ids.h */ -#include "quirks-ftdi-ids.h" -/* 1 on 1 copy of linux/drivers/usb/serial/pl2303.h */ -#include "quirks-pl2303-ids.h" - -struct usb_device_id { - int vendor_id; - int product_id; - int interface_class; - int interface_subclass; - int interface_protocol; -}; - -#define USB_DEVICE(vendor, product) \ - .vendor_id = vendor, .product_id = product, .interface_class = -1, - -#define USB_DEVICE_AND_INTERFACE_INFO(vend, prod, iclass, isubclass, iproto) \ - .vendor_id = vend, .product_id = prod, .interface_class = iclass, \ - .interface_subclass = isubclass, .interface_protocol = iproto - -static const struct usb_device_id usbredir_raw_serial_ids[] = { - /* - * Silicon Laboratories CP210x USB to RS232 serial adapter ids - * copied from linux/drivers/usb/serial/cp210x.c - * - * Copyright (C) 2005 Craig Shelley (craig@microtron.org.uk) - */ - { USB_DEVICE(0x045B, 0x0053) }, /* Renesas RX610 RX-Stick */ - { USB_DEVICE(0x0471, 0x066A) }, /* AKTAKOM ACE-1001 cable */ - { USB_DEVICE(0x0489, 0xE000) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */ - { USB_DEVICE(0x0489, 0xE003) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */ - { USB_DEVICE(0x0745, 0x1000) }, /* CipherLab USB CCD Barcode Scanner 1000 */ - { USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */ - { USB_DEVICE(0x08FD, 0x000A) }, /* Digianswer A/S , ZigBee/802.15.4 MAC Device */ - { USB_DEVICE(0x0BED, 0x1100) }, /* MEI (TM) Cashflow-SC Bill/Voucher Acceptor */ - { USB_DEVICE(0x0BED, 0x1101) }, /* MEI series 2000 Combo Acceptor */ - { USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */ - { USB_DEVICE(0x0FCF, 0x1004) }, /* Dynastream ANT2USB */ - { USB_DEVICE(0x0FCF, 0x1006) }, /* Dynastream ANT development board */ - { USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */ - { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */ - { USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */ - { USB_DEVICE(0x10C4, 0x0F91) }, /* Vstabi */ - { USB_DEVICE(0x10C4, 0x1101) }, /* Arkham Technology DS101 Bus Monitor */ - { USB_DEVICE(0x10C4, 0x1601) }, /* Arkham Technology DS101 Adapter */ - { USB_DEVICE(0x10C4, 0x800A) }, /* SPORTident BSM7-D-USB main station */ - { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */ - { USB_DEVICE(0x10C4, 0x8044) }, /* Cygnal Debug Adapter */ - { USB_DEVICE(0x10C4, 0x804E) }, /* Software Bisque Paramount ME build-in converter */ - { USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */ - { USB_DEVICE(0x10C4, 0x8054) }, /* Enfora GSM2228 */ - { USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */ - { USB_DEVICE(0x10C4, 0x806F) }, /* IMS USB to RS422 Converter Cable */ - { USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */ - { USB_DEVICE(0x10C4, 0x80C4) }, /* Cygnal Integrated Products, Inc., Optris infrared thermometer */ - { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */ - { USB_DEVICE(0x10C4, 0x80DD) }, /* Tracient RFID */ - { USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */ - { USB_DEVICE(0x10C4, 0x8115) }, /* Arygon NFC/Mifare Reader */ - { USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */ - { USB_DEVICE(0x10C4, 0x813F) }, /* Tams Master Easy Control */ - { USB_DEVICE(0x10C4, 0x814A) }, /* West Mountain Radio RIGblaster P&P */ - { USB_DEVICE(0x10C4, 0x814B) }, /* West Mountain Radio RIGtalk */ - { USB_DEVICE(0x10C4, 0x8156) }, /* B&G H3000 link cable */ - { USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */ - { USB_DEVICE(0x10C4, 0x815F) }, /* Timewave HamLinkUSB */ - { USB_DEVICE(0x10C4, 0x818B) }, /* AVIT Research USB to TTL */ - { USB_DEVICE(0x10C4, 0x819F) }, /* MJS USB Toslink Switcher */ - { USB_DEVICE(0x10C4, 0x81A6) }, /* ThinkOptics WavIt */ - { USB_DEVICE(0x10C4, 0x81A9) }, /* Multiplex RC Interface */ - { USB_DEVICE(0x10C4, 0x81AC) }, /* MSD Dash Hawk */ - { USB_DEVICE(0x10C4, 0x81AD) }, /* INSYS USB Modem */ - { USB_DEVICE(0x10C4, 0x81C8) }, /* Lipowsky Industrie Elektronik GmbH, Baby-JTAG */ - { USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */ - { USB_DEVICE(0x10C4, 0x81E7) }, /* Aerocomm Radio */ - { USB_DEVICE(0x10C4, 0x81E8) }, /* Zephyr Bioharness */ - { USB_DEVICE(0x10C4, 0x81F2) }, /* C1007 HF band RFID controller */ - { USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */ - { USB_DEVICE(0x10C4, 0x822B) }, /* Modem EDGE(GSM) Comander 2 */ - { USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demonstration module */ - { USB_DEVICE(0x10C4, 0x8293) }, /* Telegesis ETRX2USB */ - { USB_DEVICE(0x10C4, 0x82F9) }, /* Procyon AVS */ - { USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */ - { USB_DEVICE(0x10C4, 0x8382) }, /* Cygnal Integrated Products, Inc. */ - { USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */ - { USB_DEVICE(0x10C4, 0x83D8) }, /* DekTec DTA Plus VHF/UHF Booster/Attenuator */ - { USB_DEVICE(0x10C4, 0x8411) }, /* Kyocera GPS Module */ - { USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */ - { USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */ - { USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */ - { USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */ - { USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */ - { USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */ - { USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */ - { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ - { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ - { USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */ - { USB_DEVICE(0x10C4, 0xEA80) }, /* Silicon Labs factory default */ - { USB_DEVICE(0x10C4, 0xEA71) }, /* Infinity GPS-MIC-1 Radio Monophone */ - { USB_DEVICE(0x10C4, 0xF001) }, /* Elan Digital Systems USBscope50 */ - { USB_DEVICE(0x10C4, 0xF002) }, /* Elan Digital Systems USBwave12 */ - { USB_DEVICE(0x10C4, 0xF003) }, /* Elan Digital Systems USBpulse100 */ - { USB_DEVICE(0x10C4, 0xF004) }, /* Elan Digital Systems USBcount50 */ - { USB_DEVICE(0x10C5, 0xEA61) }, /* Silicon Labs MobiData GPRS USB Modem */ - { USB_DEVICE(0x10CE, 0xEA6A) }, /* Silicon Labs MobiData GPRS USB Modem 100EU */ - { USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */ - { USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */ - { USB_DEVICE(0x166A, 0x0201) }, /* Clipsal 5500PACA C-Bus Pascal Automation Controller */ - { USB_DEVICE(0x166A, 0x0301) }, /* Clipsal 5800PC C-Bus Wireless PC Interface */ - { USB_DEVICE(0x166A, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */ - { USB_DEVICE(0x166A, 0x0304) }, /* Clipsal 5000CT2 C-Bus Black and White Touchscreen */ - { USB_DEVICE(0x166A, 0x0305) }, /* Clipsal C-5000CT2 C-Bus Spectrum Colour Touchscreen */ - { USB_DEVICE(0x166A, 0x0401) }, /* Clipsal L51xx C-Bus Architectural Dimmer */ - { USB_DEVICE(0x166A, 0x0101) }, /* Clipsal 5560884 C-Bus Multi-room Audio Matrix Switcher */ - { USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */ - { USB_DEVICE(0x16DC, 0x0010) }, /* W-IE-NE-R Plein & Baus GmbH PL512 Power Supply */ - { USB_DEVICE(0x16DC, 0x0011) }, /* W-IE-NE-R Plein & Baus GmbH RCM Remote Control for MARATON Power Supply */ - { USB_DEVICE(0x16DC, 0x0012) }, /* W-IE-NE-R Plein & Baus GmbH MPOD Multi Channel Power Supply */ - { USB_DEVICE(0x16DC, 0x0015) }, /* W-IE-NE-R Plein & Baus GmbH CML Control, Monitoring and Data Logger */ - { USB_DEVICE(0x17A8, 0x0001) }, /* Kamstrup Optical Eye/3-wire */ - { USB_DEVICE(0x17A8, 0x0005) }, /* Kamstrup M-Bus Master MultiPort 250D */ - { USB_DEVICE(0x17F4, 0xAAAA) }, /* Wavesense Jazz blood glucose meter */ - { USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */ - { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */ - { USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */ - { USB_DEVICE(0x1E29, 0x0102) }, /* Festo CPX-USB */ - { USB_DEVICE(0x1E29, 0x0501) }, /* Festo CMSP */ - { USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */ - { USB_DEVICE(0x3195, 0xF280) }, /* Link Instruments MSO-28 */ - { USB_DEVICE(0x3195, 0xF281) }, /* Link Instruments MSO-28 */ - { USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */ - - /* - * Prolific pl2303 USB to RS232 serial adapter ids - * copied from linux/drivers/usb/serial/pl2303.c - * - * Copyright (C) 2001-2007 Greg Kroah-Hartman (greg@kroah.com) - * Copyright (C) 2003 IBM Corp. - */ - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_DCU11) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ3) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ALDIGA) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MMX) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GPRS) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_HCR331) }, - { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MOTOROLA) }, - { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, - { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, - { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, - { USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) }, - { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) }, - { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) }, - { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) }, - { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID_2080) }, - { USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) }, - { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) }, - { USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) }, - { USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) }, - { USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) }, - { USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) }, - { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_ID) }, - { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_ID) }, - { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_SX1) }, - { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65) }, - { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X75) }, - { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_EF81) }, - { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_ID_S81) }, /* Benq/Siemens S81 */ - { USB_DEVICE(SYNTECH_VENDOR_ID, SYNTECH_PRODUCT_ID) }, - { USB_DEVICE(NOKIA_CA42_VENDOR_ID, NOKIA_CA42_PRODUCT_ID) }, - { USB_DEVICE(CA_42_CA42_VENDOR_ID, CA_42_CA42_PRODUCT_ID) }, - { USB_DEVICE(SAGEM_VENDOR_ID, SAGEM_PRODUCT_ID) }, - { USB_DEVICE(LEADTEK_VENDOR_ID, LEADTEK_9531_PRODUCT_ID) }, - { USB_DEVICE(SPEEDDRAGON_VENDOR_ID, SPEEDDRAGON_PRODUCT_ID) }, - { USB_DEVICE(DATAPILOT_U2_VENDOR_ID, DATAPILOT_U2_PRODUCT_ID) }, - { USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) }, - { USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) }, - { USB_DEVICE(WS002IN_VENDOR_ID, WS002IN_PRODUCT_ID) }, - { USB_DEVICE(COREGA_VENDOR_ID, COREGA_PRODUCT_ID) }, - { USB_DEVICE(YCCABLE_VENDOR_ID, YCCABLE_PRODUCT_ID) }, - { USB_DEVICE(SUPERIAL_VENDOR_ID, SUPERIAL_PRODUCT_ID) }, - { USB_DEVICE(HP_VENDOR_ID, HP_LD220_PRODUCT_ID) }, - { USB_DEVICE(CRESSI_VENDOR_ID, CRESSI_EDY_PRODUCT_ID) }, - { USB_DEVICE(ZEAGLE_VENDOR_ID, ZEAGLE_N2ITION3_PRODUCT_ID) }, - { USB_DEVICE(SONY_VENDOR_ID, SONY_QN3USB_PRODUCT_ID) }, - { USB_DEVICE(SANWA_VENDOR_ID, SANWA_PRODUCT_ID) }, - { USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530_PRODUCT_ID) }, - { USB_DEVICE(SMART_VENDOR_ID, SMART_PRODUCT_ID) }, - - { USB_DEVICE(-1, -1) } /* Terminating Entry */ -}; - -static const struct usb_device_id usbredir_ftdi_serial_ids[] = { - /* - * FTDI USB to RS232 serial adapter ids - * copied from linux/drivers/usb/serial/ftdi_sio.c - * - * Copyright (C) 2009 - 2010 - * Johan Hovold (jhovold@gmail.com) - * Copyright (C) 1999 - 2001 - * Greg Kroah-Hartman (greg@kroah.com) - * Bill Ryder (bryder@sgi.com) - * Copyright (C) 2002 - * Kuba Ober (kuba@mareimbrium.org) - */ - { USB_DEVICE(FTDI_VID, FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CTI_MINI_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CTI_NANO_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CANDAPTER_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_NXTCAM_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_0_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_1_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_3_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_4_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_5_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_6_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_7_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_USINT_CAT_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_USINT_WKEY_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_USINT_RS232_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ACTZWAVE_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IRTRANS_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IPLUS_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IPLUS2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_DMX4ALL) }, - { USB_DEVICE(FTDI_VID, FTDI_SIO_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_232RL_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_4232H_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_232H_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_FTX_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MICRO_CHAMELEON_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_SNIFFER_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_THROTTLE_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_GATEWAY_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_GBM_PID) }, - { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) }, - { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_MINI_IOBOARD_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SPROG_II) }, - { USB_DEVICE(FTDI_VID, FTDI_LENZ_LIUSB_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_XF_632_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_XF_634_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_XF_547_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_XF_633_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_XF_631_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_XF_635_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_XF_640_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_XF_642_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_DSS20_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_URBAN_0_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_URBAN_1_PID) }, - { USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_VNHCPCUSB_D_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MTXORB_0_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MTXORB_1_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MTXORB_2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MTXORB_3_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MTXORB_4_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MTXORB_5_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MTXORB_6_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_R2000KU_TRUE_RNG) }, - { USB_DEVICE(FTDI_VID, FTDI_VARDAAN_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0100_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0101_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0102_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0103_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0104_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0105_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0106_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0107_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0108_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0109_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0110_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0111_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0112_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0113_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0114_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0115_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0116_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0117_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0118_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0119_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0120_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0121_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0122_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0123_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0124_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0125_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0126_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0127_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0128_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0129_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0130_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0131_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0132_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0133_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0134_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0135_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0136_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0137_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0138_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0139_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0140_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0141_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0142_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0143_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0144_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0145_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0146_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0147_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0148_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0149_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0150_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0151_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0152_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0153_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0154_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0155_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0156_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0157_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0158_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0159_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0160_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0161_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0162_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0163_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0164_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0165_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0166_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0167_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0168_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0169_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0170_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0171_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0172_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0173_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0174_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0175_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0176_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0177_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0178_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0179_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0180_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0181_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0182_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0183_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0184_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0185_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0186_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0187_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0188_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0189_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0190_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0191_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0192_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0193_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0194_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0195_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0196_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0197_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0198_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0199_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019A_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019B_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019C_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019D_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019E_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019F_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A0_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A1_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A2_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A3_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A4_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A5_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A6_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A7_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A8_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A9_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AA_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AB_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AC_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AD_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AE_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AF_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B0_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B1_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B2_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B3_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B4_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B5_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B6_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B7_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B8_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B9_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BA_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BB_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BC_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BD_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BE_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BF_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C0_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C1_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C2_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C3_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C4_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C5_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C6_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C7_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C8_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C9_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CA_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CB_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CC_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CD_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CE_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CF_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D0_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D1_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D2_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D3_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D4_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D5_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D6_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D7_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D8_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D9_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DA_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DB_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DC_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DD_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DE_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DF_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E0_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E1_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E2_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E3_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E4_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E5_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E6_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E7_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E8_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E9_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EA_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EB_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EC_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01ED_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EE_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EF_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F0_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F1_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F2_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F3_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F4_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F5_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F6_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F7_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F8_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F9_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FA_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FB_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FC_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FD_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FE_PID) }, - { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FF_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TNC_X_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_USBX_707_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2101_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2104_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2106_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2202_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2202_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2203_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2203_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_3_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_4_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_3_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_4_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_3_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_4_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_3_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_4_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_5_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_6_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_7_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_8_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_3_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_4_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_5_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_6_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_7_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_8_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_3_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_4_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_5_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_6_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_7_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_8_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_1_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_2_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_3_PID) }, - { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_4_PID) }, - { USB_DEVICE(IDTECH_VID, IDTECH_IDT1221U_PID) }, - { USB_DEVICE(OCT_VID, OCT_US101_PID) }, - { USB_DEVICE(OCT_VID, OCT_DK201_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_HE_TIRA1_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID) }, - { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_1) }, - { USB_DEVICE(FTDI_VID, PROTEGO_R2X0) }, - { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_3) }, - { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_4) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E808_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E809_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80A_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80B_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80C_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80D_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80E_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80F_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E888_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E889_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88A_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88B_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88C_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88D_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88E_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88F_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UO100_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UM100_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UR100_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_ALC8500_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_PYRAMID_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1000PC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IBS_US485_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IBS_PICPRO_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IBS_PCMCIA_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IBS_PK1_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IBS_RS232MON_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IBS_APP70_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TAVIR_STK500_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TIAO_UMPA_PID) }, - /* - * ELV devices: - */ - { USB_DEVICE(FTDI_VID, FTDI_ELV_USR_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_MSM1_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_KL100_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_WS550_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_EC3000_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_WS888_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_TWS550_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_FEM_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_CLI7000_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_PPS7330_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_TFM100_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UDF77_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UIO88_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UAD8_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UDA7_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_USI2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_T1100_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_PCD200_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_ULA200_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_CSI8_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_EM1000DL_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_PCK100_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_RFP500_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_FS20SIG_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UTP8_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_WS300PC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_WS444PC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1300PC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_EM1010PC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_WS500_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_HS485_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_UMS100_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_TFD128_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_FM3RX_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELV_WS777_PID) }, - { USB_DEVICE(FTDI_VID, LINX_SDMUSBQSS_PID) }, - { USB_DEVICE(FTDI_VID, LINX_MASTERDEVEL2_PID) }, - { USB_DEVICE(FTDI_VID, LINX_FUTURE_0_PID) }, - { USB_DEVICE(FTDI_VID, LINX_FUTURE_1_PID) }, - { USB_DEVICE(FTDI_VID, LINX_FUTURE_2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CCSICDU20_0_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CCSICDU40_1_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CCSMACHX_2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CCSLOAD_N_GO_3_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CCSICDU64_4_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CCSPRIME8_5_PID) }, - { USB_DEVICE(FTDI_VID, INSIDE_ACCESSO) }, - { USB_DEVICE(INTREPID_VID, INTREPID_VALUECAN_PID) }, - { USB_DEVICE(INTREPID_VID, INTREPID_NEOVI_PID) }, - { USB_DEVICE(FALCOM_VID, FALCOM_TWIST_PID) }, - { USB_DEVICE(FALCOM_VID, FALCOM_SAMBA_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SUUNTO_SPORTS_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_OCEANIC_PID) }, - { USB_DEVICE(TTI_VID, TTI_QL355P_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_RM_CANVIEW_PID) }, - { USB_DEVICE(ACTON_VID, ACTON_SPECTRAPRO_PID) }, - { USB_DEVICE(CONTEC_VID, CONTEC_COM1USBH_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USO9ML2_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USOPTL4_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USPTL4_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USO9ML2DR_2_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USO9ML2DR_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USOPTL4DR2_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_USOPTL4DR_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_485USB9F_2W_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_485USB9F_4W_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_232USB9M_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_485USBTB_2W_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_485USBTB_4W_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_TTL5USB9M_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_TTL3USB9M_PID) }, - { USB_DEVICE(BANDB_VID, BANDB_ZZ_PROG1_USB_PID) }, - { USB_DEVICE(FTDI_VID, EVER_ECO_PRO_CDS) }, - { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_1_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_3_PID) }, - { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_0_PID) }, - { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_1_PID) }, - { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_2_PID) }, - { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_3_PID) }, - { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_4_PID) }, - { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_5_PID) }, - { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_6_PID) }, - { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_7_PID) }, - { USB_DEVICE(MOBILITY_VID, MOBILITY_USB_SERIAL_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ACTIVE_ROBOTS_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MHAM_KW_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MHAM_YS_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MHAM_Y6_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MHAM_Y8_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MHAM_IC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MHAM_DB9_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MHAM_RS232_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MHAM_Y9_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TERATRONIK_VCP_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TERATRONIK_D2XX_PID) }, - { USB_DEVICE(EVOLUTION_VID, EVOLUTION_ER1_PID) }, - { USB_DEVICE(EVOLUTION_VID, EVO_HYBRID_PID) }, - { USB_DEVICE(EVOLUTION_VID, EVO_RCM4_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ARTEMIS_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16C_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16HR_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16HRC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16IC_PID) }, - { USB_DEVICE(KOBIL_VID, KOBIL_CONV_B1_PID) }, - { USB_DEVICE(KOBIL_VID, KOBIL_CONV_KAAN_PID) }, - { USB_DEVICE(POSIFLEX_VID, POSIFLEX_PP7000_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TTUSB_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ECLO_COM_1WIRE_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_777_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_8900F_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_PCDJ_DAC2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_RRCIRKITS_LOCOBUFFER_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ASK_RDR400_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_NZR_SEM_USB_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_1_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_OPC_U_UC_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP2C1_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP2C2_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP2D_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP2VT_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP2VR_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP4KVT_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP4KVR_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP2KVT_PID) }, - { USB_DEVICE(ICOM_VID, ICOM_ID_RP2KVR_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ACG_HFDUAL_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_YEI_SERVOCENTER31_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_THORLABS_PID) }, - { USB_DEVICE(TESTO_VID, TESTO_USB_INTERFACE_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_GAMMA_SCOUT_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13M_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) }, - { USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_NDI_HUC_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_NDI_SPECTRA_SCU_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_2_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_3_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_NDI_AURORA_SCU_PID) }, - { USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) }, - { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_SERIAL_VX7_PID) }, - { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_CT29B_PID) }, - { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_RTS01_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_MAXSTREAM_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_PHI_FISCO_PID) }, - { USB_DEVICE(TML_VID, TML_USB_SERIAL_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_ELSTER_UNICOM_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_PROPOX_JTAGCABLEII_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_PROPOX_ISPCABLEIII_PID) }, - { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID) }, - { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID) }, - { USB_DEVICE(FIC_VID, FIC_NEO1973_DEBUG_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID) }, - { USB_DEVICE(FTDI_VID, LMI_LM3S_DEVEL_BOARD_PID) }, - { USB_DEVICE(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID) }, - { USB_DEVICE(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TURTELIZER_PID) }, - { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) }, - { USB_DEVICE(FTDI_VID, FTDI_REU_TINY_PID) }, - - /* Papouch devices based on FTDI chip */ - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB485_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_AP485_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB422_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB485_2_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_AP485_2_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB422_2_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB485S_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB485C_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_LEC_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB232_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_TMU_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_IRAMP_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_DRAK5_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO8x8_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO4x4_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO2x2_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO10x1_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO30x3_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO60x3_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO2x16_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO3x32_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_DRAK6_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_UPSUSB_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_MU_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SIMUKEY_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_AD4USB_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_GMUX_PID) }, - { USB_DEVICE(PAPOUCH_VID, PAPOUCH_GMSR_PID) }, - - { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DGQG_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DUSB_PID) }, - { USB_DEVICE(ALTI2_VID, ALTI2_N3_PID) }, - { USB_DEVICE(FTDI_VID, DIEBOLD_BCS_SE923_PID) }, - { USB_DEVICE(ATMEL_VID, STK541_PID) }, - { USB_DEVICE(DE_VID, STB_PID) }, - { USB_DEVICE(DE_VID, WHT_PID) }, - { USB_DEVICE(ADI_VID, ADI_GNICE_PID) }, - { USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID) }, - { USB_DEVICE_AND_INTERFACE_INFO(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID, - 0xff, 0xff, 0x00) }, - { USB_DEVICE(JETI_VID, JETI_SPC1201_PID) }, - { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID) }, - { USB_DEVICE(LARSENBRUSGAARD_VID, LB_ALTITRACK_PID) }, - { USB_DEVICE(GN_OTOMETRICS_VID, AURICAL_USB_PID) }, - { USB_DEVICE(FTDI_VID, PI_C865_PID) }, - { USB_DEVICE(FTDI_VID, PI_C857_PID) }, - { USB_DEVICE(PI_VID, PI_C866_PID) }, - { USB_DEVICE(PI_VID, PI_C663_PID) }, - { USB_DEVICE(PI_VID, PI_C725_PID) }, - { USB_DEVICE(PI_VID, PI_E517_PID) }, - { USB_DEVICE(PI_VID, PI_C863_PID) }, - { USB_DEVICE(PI_VID, PI_E861_PID) }, - { USB_DEVICE(PI_VID, PI_C867_PID) }, - { USB_DEVICE(PI_VID, PI_E609_PID) }, - { USB_DEVICE(PI_VID, PI_E709_PID) }, - { USB_DEVICE(PI_VID, PI_100F_PID) }, - { USB_DEVICE(PI_VID, PI_1011_PID) }, - { USB_DEVICE(PI_VID, PI_1012_PID) }, - { USB_DEVICE(PI_VID, PI_1013_PID) }, - { USB_DEVICE(PI_VID, PI_1014_PID) }, - { USB_DEVICE(PI_VID, PI_1015_PID) }, - { USB_DEVICE(PI_VID, PI_1016_PID) }, - { USB_DEVICE(KONDO_VID, KONDO_USB_SERIAL_PID) }, - { USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) }, - { USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID) }, - { USB_DEVICE(FTDI_VID, TI_XDS100V2_PID) }, - { USB_DEVICE(FTDI_VID, HAMEG_HO820_PID) }, - { USB_DEVICE(FTDI_VID, HAMEG_HO720_PID) }, - { USB_DEVICE(FTDI_VID, HAMEG_HO730_PID) }, - { USB_DEVICE(FTDI_VID, HAMEG_HO870_PID) }, - { USB_DEVICE(FTDI_VID, MJSG_GENERIC_PID) }, - { USB_DEVICE(FTDI_VID, MJSG_SR_RADIO_PID) }, - { USB_DEVICE(FTDI_VID, MJSG_HD_RADIO_PID) }, - { USB_DEVICE(FTDI_VID, MJSG_XM_RADIO_PID) }, - { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_ST_PID) }, - { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SLITE_PID) }, - { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH2_PID) }, - { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH4_PID) }, - { USB_DEVICE(FTDI_VID, SEGWAY_RMP200_PID) }, - { USB_DEVICE(FTDI_VID, ACCESIO_COM4SM_PID) }, - { USB_DEVICE(IONICS_VID, IONICS_PLUGCOMPUTER_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_24_MASTER_WING_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_PC_WING_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_USB_DMX_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MIDI_TIMECODE_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MINI_WING_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MAXI_WING_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MEDIA_WING_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_WING_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LOGBOOKML_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LS_LOGBOOK_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_HS_LOGBOOK_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_CINTERION_MC55I_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_DOTEC_PID) }, - { USB_DEVICE(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID) }, - { USB_DEVICE(ST_VID, ST_STMCLT1030_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_RF_R106) }, - { USB_DEVICE(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) }, - - { USB_DEVICE(-1, -1) } /* Terminating Entry */ -}; - -#undef USB_DEVICE -#undef USB_DEVICE_AND_INTERFACE_INFO diff --git a/qemu/hw/usb/redirect.c b/qemu/hw/usb/redirect.c deleted file mode 100644 index 8d8054037..000000000 --- a/qemu/hw/usb/redirect.c +++ /dev/null @@ -1,2526 +0,0 @@ -/* - * USB redirector usb-guest - * - * Copyright (c) 2011-2012 Red Hat, Inc. - * - * Red Hat Authors: - * Hans de Goede - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu-common.h" -#include "qemu/timer.h" -#include "sysemu/sysemu.h" -#include "qapi/qmp/qerror.h" -#include "qemu/error-report.h" -#include "qemu/iov.h" -#include "sysemu/char.h" - -#include -#include - -#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)) -#define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f)) -#define USBEP2I(usb_ep) (((usb_ep)->pid == USB_TOKEN_IN) ? \ - ((usb_ep)->nr | 0x10) : ((usb_ep)->nr)) -#define I2USBEP(d, i) (usb_ep_get(&(d)->dev, \ - ((i) & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT, \ - (i) & 0x0f)) - -#ifndef USBREDIR_VERSION /* This is not defined in older usbredir versions */ -#define USBREDIR_VERSION 0 -#endif - -typedef struct USBRedirDevice USBRedirDevice; - -/* Struct to hold buffered packets */ -struct buf_packet { - uint8_t *data; - void *free_on_destroy; - uint16_t len; - uint16_t offset; - uint8_t status; - QTAILQ_ENTRY(buf_packet)next; -}; - -struct endp_data { - USBRedirDevice *dev; - uint8_t type; - uint8_t interval; - uint8_t interface; /* bInterfaceNumber this ep belongs to */ - uint16_t max_packet_size; /* In bytes, not wMaxPacketSize format !! */ - uint32_t max_streams; - uint8_t iso_started; - uint8_t iso_error; /* For reporting iso errors to the HC */ - uint8_t interrupt_started; - uint8_t interrupt_error; - uint8_t bulk_receiving_enabled; - uint8_t bulk_receiving_started; - uint8_t bufpq_prefilled; - uint8_t bufpq_dropping_packets; - QTAILQ_HEAD(, buf_packet) bufpq; - int32_t bufpq_size; - int32_t bufpq_target_size; - USBPacket *pending_async_packet; -}; - -struct PacketIdQueueEntry { - uint64_t id; - QTAILQ_ENTRY(PacketIdQueueEntry)next; -}; - -struct PacketIdQueue { - USBRedirDevice *dev; - const char *name; - QTAILQ_HEAD(, PacketIdQueueEntry) head; - int size; -}; - -struct USBRedirDevice { - USBDevice dev; - /* Properties */ - CharDriverState *cs; - uint8_t debug; - char *filter_str; - int32_t bootindex; - /* Data passed from chardev the fd_read cb to the usbredirparser read cb */ - const uint8_t *read_buf; - int read_buf_size; - /* Active chardev-watch-tag */ - guint watch; - /* For async handling of close / reject */ - QEMUBH *chardev_close_bh; - QEMUBH *device_reject_bh; - /* To delay the usb attach in case of quick chardev close + open */ - QEMUTimer *attach_timer; - int64_t next_attach_time; - struct usbredirparser *parser; - struct endp_data endpoint[MAX_ENDPOINTS]; - struct PacketIdQueue cancelled; - struct PacketIdQueue already_in_flight; - void (*buffered_bulk_in_complete)(USBRedirDevice *, USBPacket *, uint8_t); - /* Data for device filtering */ - struct usb_redir_device_connect_header device_info; - struct usb_redir_interface_info_header interface_info; - struct usbredirfilter_rule *filter_rules; - int filter_rules_count; - int compatible_speedmask; -}; - -#define TYPE_USB_REDIR "usb-redir" -#define USB_REDIRECT(obj) OBJECT_CHECK(USBRedirDevice, (obj), TYPE_USB_REDIR) - -static void usbredir_hello(void *priv, struct usb_redir_hello_header *h); -static void usbredir_device_connect(void *priv, - struct usb_redir_device_connect_header *device_connect); -static void usbredir_device_disconnect(void *priv); -static void usbredir_interface_info(void *priv, - struct usb_redir_interface_info_header *interface_info); -static void usbredir_ep_info(void *priv, - struct usb_redir_ep_info_header *ep_info); -static void usbredir_configuration_status(void *priv, uint64_t id, - struct usb_redir_configuration_status_header *configuration_status); -static void usbredir_alt_setting_status(void *priv, uint64_t id, - struct usb_redir_alt_setting_status_header *alt_setting_status); -static void usbredir_iso_stream_status(void *priv, uint64_t id, - struct usb_redir_iso_stream_status_header *iso_stream_status); -static void usbredir_interrupt_receiving_status(void *priv, uint64_t id, - struct usb_redir_interrupt_receiving_status_header - *interrupt_receiving_status); -static void usbredir_bulk_streams_status(void *priv, uint64_t id, - struct usb_redir_bulk_streams_status_header *bulk_streams_status); -static void usbredir_bulk_receiving_status(void *priv, uint64_t id, - struct usb_redir_bulk_receiving_status_header *bulk_receiving_status); -static void usbredir_control_packet(void *priv, uint64_t id, - struct usb_redir_control_packet_header *control_packet, - uint8_t *data, int data_len); -static void usbredir_bulk_packet(void *priv, uint64_t id, - struct usb_redir_bulk_packet_header *bulk_packet, - uint8_t *data, int data_len); -static void usbredir_iso_packet(void *priv, uint64_t id, - struct usb_redir_iso_packet_header *iso_packet, - uint8_t *data, int data_len); -static void usbredir_interrupt_packet(void *priv, uint64_t id, - struct usb_redir_interrupt_packet_header *interrupt_header, - uint8_t *data, int data_len); -static void usbredir_buffered_bulk_packet(void *priv, uint64_t id, - struct usb_redir_buffered_bulk_packet_header *buffered_bulk_packet, - uint8_t *data, int data_len); - -static void usbredir_handle_status(USBRedirDevice *dev, USBPacket *p, - int status); - -#define VERSION "qemu usb-redir guest " QEMU_VERSION - -/* - * Logging stuff - */ - -#define ERROR(...) \ - do { \ - if (dev->debug >= usbredirparser_error) { \ - error_report("usb-redir error: " __VA_ARGS__); \ - } \ - } while (0) -#define WARNING(...) \ - do { \ - if (dev->debug >= usbredirparser_warning) { \ - error_report("usb-redir warning: " __VA_ARGS__); \ - } \ - } while (0) -#define INFO(...) \ - do { \ - if (dev->debug >= usbredirparser_info) { \ - error_report("usb-redir: " __VA_ARGS__); \ - } \ - } while (0) -#define DPRINTF(...) \ - do { \ - if (dev->debug >= usbredirparser_debug) { \ - error_report("usb-redir: " __VA_ARGS__); \ - } \ - } while (0) -#define DPRINTF2(...) \ - do { \ - if (dev->debug >= usbredirparser_debug_data) { \ - error_report("usb-redir: " __VA_ARGS__); \ - } \ - } while (0) - -static void usbredir_log(void *priv, int level, const char *msg) -{ - USBRedirDevice *dev = priv; - - if (dev->debug < level) { - return; - } - - error_report("%s", msg); -} - -static void usbredir_log_data(USBRedirDevice *dev, const char *desc, - const uint8_t *data, int len) -{ - int i, j, n; - - if (dev->debug < usbredirparser_debug_data) { - return; - } - - for (i = 0; i < len; i += j) { - char buf[128]; - - n = sprintf(buf, "%s", desc); - for (j = 0; j < 8 && i + j < len; j++) { - n += sprintf(buf + n, " %02X", data[i + j]); - } - error_report("%s", buf); - } -} - -/* - * usbredirparser io functions - */ - -static int usbredir_read(void *priv, uint8_t *data, int count) -{ - USBRedirDevice *dev = priv; - - if (dev->read_buf_size < count) { - count = dev->read_buf_size; - } - - memcpy(data, dev->read_buf, count); - - dev->read_buf_size -= count; - if (dev->read_buf_size) { - dev->read_buf += count; - } else { - dev->read_buf = NULL; - } - - return count; -} - -static gboolean usbredir_write_unblocked(GIOChannel *chan, GIOCondition cond, - void *opaque) -{ - USBRedirDevice *dev = opaque; - - dev->watch = 0; - usbredirparser_do_write(dev->parser); - - return FALSE; -} - -static int usbredir_write(void *priv, uint8_t *data, int count) -{ - USBRedirDevice *dev = priv; - int r; - - if (!dev->cs->be_open) { - return 0; - } - - /* Don't send new data to the chardev until our state is fully synced */ - if (!runstate_check(RUN_STATE_RUNNING)) { - return 0; - } - - r = qemu_chr_fe_write(dev->cs, data, count); - if (r < count) { - if (!dev->watch) { - dev->watch = qemu_chr_fe_add_watch(dev->cs, G_IO_OUT|G_IO_HUP, - usbredir_write_unblocked, dev); - } - if (r < 0) { - r = 0; - } - } - return r; -} - -/* - * Cancelled and buffered packets helpers - */ - -static void packet_id_queue_init(struct PacketIdQueue *q, - USBRedirDevice *dev, const char *name) -{ - q->dev = dev; - q->name = name; - QTAILQ_INIT(&q->head); - q->size = 0; -} - -static void packet_id_queue_add(struct PacketIdQueue *q, uint64_t id) -{ - USBRedirDevice *dev = q->dev; - struct PacketIdQueueEntry *e; - - DPRINTF("adding packet id %"PRIu64" to %s queue\n", id, q->name); - - e = g_new0(struct PacketIdQueueEntry, 1); - e->id = id; - QTAILQ_INSERT_TAIL(&q->head, e, next); - q->size++; -} - -static int packet_id_queue_remove(struct PacketIdQueue *q, uint64_t id) -{ - USBRedirDevice *dev = q->dev; - struct PacketIdQueueEntry *e; - - QTAILQ_FOREACH(e, &q->head, next) { - if (e->id == id) { - DPRINTF("removing packet id %"PRIu64" from %s queue\n", - id, q->name); - QTAILQ_REMOVE(&q->head, e, next); - q->size--; - g_free(e); - return 1; - } - } - return 0; -} - -static void packet_id_queue_empty(struct PacketIdQueue *q) -{ - USBRedirDevice *dev = q->dev; - struct PacketIdQueueEntry *e, *next_e; - - DPRINTF("removing %d packet-ids from %s queue\n", q->size, q->name); - - QTAILQ_FOREACH_SAFE(e, &q->head, next, next_e) { - QTAILQ_REMOVE(&q->head, e, next); - g_free(e); - } - q->size = 0; -} - -static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) -{ - USBRedirDevice *dev = USB_REDIRECT(udev); - int i = USBEP2I(p->ep); - - if (p->combined) { - usb_combined_packet_cancel(udev, p); - return; - } - - if (dev->endpoint[i].pending_async_packet) { - assert(dev->endpoint[i].pending_async_packet == p); - dev->endpoint[i].pending_async_packet = NULL; - return; - } - - packet_id_queue_add(&dev->cancelled, p->id); - usbredirparser_send_cancel_data_packet(dev->parser, p->id); - usbredirparser_do_write(dev->parser); -} - -static int usbredir_is_cancelled(USBRedirDevice *dev, uint64_t id) -{ - if (!dev->dev.attached) { - return 1; /* Treat everything as cancelled after a disconnect */ - } - return packet_id_queue_remove(&dev->cancelled, id); -} - -static void usbredir_fill_already_in_flight_from_ep(USBRedirDevice *dev, - struct USBEndpoint *ep) -{ - static USBPacket *p; - - /* async handled packets for bulk receiving eps do not count as inflight */ - if (dev->endpoint[USBEP2I(ep)].bulk_receiving_started) { - return; - } - - QTAILQ_FOREACH(p, &ep->queue, queue) { - /* Skip combined packets, except for the first */ - if (p->combined && p != p->combined->first) { - continue; - } - if (p->state == USB_PACKET_ASYNC) { - packet_id_queue_add(&dev->already_in_flight, p->id); - } - } -} - -static void usbredir_fill_already_in_flight(USBRedirDevice *dev) -{ - int ep; - struct USBDevice *udev = &dev->dev; - - usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_ctl); - - for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) { - usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_in[ep]); - usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_out[ep]); - } -} - -static int usbredir_already_in_flight(USBRedirDevice *dev, uint64_t id) -{ - return packet_id_queue_remove(&dev->already_in_flight, id); -} - -static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev, - uint8_t ep, uint64_t id) -{ - USBPacket *p; - - if (usbredir_is_cancelled(dev, id)) { - return NULL; - } - - p = usb_ep_find_packet_by_id(&dev->dev, - (ep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT, - ep & 0x0f, id); - if (p == NULL) { - ERROR("could not find packet with id %"PRIu64"\n", id); - } - return p; -} - -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; - - if (!dev->endpoint[EP2I(ep)].bufpq_dropping_packets && - dev->endpoint[EP2I(ep)].bufpq_size > - 2 * dev->endpoint[EP2I(ep)].bufpq_target_size) { - DPRINTF("bufpq overflow, dropping packets ep %02X\n", ep); - dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 1; - } - /* Since we're interupting the stream anyways, drop enough packets to get - back to our target buffer size */ - if (dev->endpoint[EP2I(ep)].bufpq_dropping_packets) { - if (dev->endpoint[EP2I(ep)].bufpq_size > - dev->endpoint[EP2I(ep)].bufpq_target_size) { - free(data); - return -1; - } - dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0; - } - - bufp = g_new(struct buf_packet, 1); - bufp->data = data; - bufp->len = len; - bufp->offset = 0; - bufp->status = status; - 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, - uint8_t ep) -{ - QTAILQ_REMOVE(&dev->endpoint[EP2I(ep)].bufpq, bufp, next); - dev->endpoint[EP2I(ep)].bufpq_size--; - free(bufp->free_on_destroy); - g_free(bufp); -} - -static void usbredir_free_bufpq(USBRedirDevice *dev, uint8_t ep) -{ - struct buf_packet *buf, *buf_next; - - QTAILQ_FOREACH_SAFE(buf, &dev->endpoint[EP2I(ep)].bufpq, next, buf_next) { - bufp_free(dev, buf, ep); - } -} - -/* - * USBDevice callbacks - */ - -static void usbredir_handle_reset(USBDevice *udev) -{ - USBRedirDevice *dev = USB_REDIRECT(udev); - - DPRINTF("reset device\n"); - usbredirparser_send_reset(dev->parser); - usbredirparser_do_write(dev->parser); -} - -static void usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, - uint8_t ep) -{ - int status, len; - if (!dev->endpoint[EP2I(ep)].iso_started && - !dev->endpoint[EP2I(ep)].iso_error) { - struct usb_redir_start_iso_stream_header start_iso = { - .endpoint = ep, - }; - int pkts_per_sec; - - if (dev->dev.speed == USB_SPEED_HIGH) { - pkts_per_sec = 8000 / dev->endpoint[EP2I(ep)].interval; - } else { - pkts_per_sec = 1000 / dev->endpoint[EP2I(ep)].interval; - } - /* Testing has shown that we need circa 60 ms buffer */ - dev->endpoint[EP2I(ep)].bufpq_target_size = (pkts_per_sec * 60) / 1000; - - /* Aim for approx 100 interrupts / second on the client to - balance latency and interrupt load */ - start_iso.pkts_per_urb = pkts_per_sec / 100; - if (start_iso.pkts_per_urb < 1) { - start_iso.pkts_per_urb = 1; - } else if (start_iso.pkts_per_urb > 32) { - start_iso.pkts_per_urb = 32; - } - - start_iso.no_urbs = (dev->endpoint[EP2I(ep)].bufpq_target_size + - start_iso.pkts_per_urb - 1) / - start_iso.pkts_per_urb; - /* Output endpoints pre-fill only 1/2 of the packets, keeping the rest - as overflow buffer. Also see the usbredir protocol documentation */ - if (!(ep & USB_DIR_IN)) { - start_iso.no_urbs *= 2; - } - if (start_iso.no_urbs > 16) { - start_iso.no_urbs = 16; - } - - /* No id, we look at the ep when receiving a status back */ - usbredirparser_send_start_iso_stream(dev->parser, 0, &start_iso); - usbredirparser_do_write(dev->parser); - DPRINTF("iso stream started pkts/sec %d pkts/urb %d urbs %d ep %02X\n", - pkts_per_sec, start_iso.pkts_per_urb, start_iso.no_urbs, ep); - dev->endpoint[EP2I(ep)].iso_started = 1; - dev->endpoint[EP2I(ep)].bufpq_prefilled = 0; - dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0; - } - - if (ep & USB_DIR_IN) { - struct buf_packet *isop; - - if (dev->endpoint[EP2I(ep)].iso_started && - !dev->endpoint[EP2I(ep)].bufpq_prefilled) { - if (dev->endpoint[EP2I(ep)].bufpq_size < - dev->endpoint[EP2I(ep)].bufpq_target_size) { - return; - } - dev->endpoint[EP2I(ep)].bufpq_prefilled = 1; - } - - isop = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq); - if (isop == NULL) { - DPRINTF("iso-token-in ep %02X, no isop, iso_error: %d\n", - ep, dev->endpoint[EP2I(ep)].iso_error); - /* Re-fill the buffer */ - dev->endpoint[EP2I(ep)].bufpq_prefilled = 0; - /* Check iso_error for stream errors, otherwise its an underrun */ - status = dev->endpoint[EP2I(ep)].iso_error; - dev->endpoint[EP2I(ep)].iso_error = 0; - p->status = status ? USB_RET_IOERROR : USB_RET_SUCCESS; - return; - } - DPRINTF2("iso-token-in ep %02X status %d len %d queue-size: %d\n", ep, - isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size); - - status = isop->status; - len = isop->len; - if (len > p->iov.size) { - ERROR("received iso data is larger then packet ep %02X (%d > %d)\n", - ep, len, (int)p->iov.size); - len = p->iov.size; - status = usb_redir_babble; - } - usb_packet_copy(p, isop->data, len); - bufp_free(dev, isop, ep); - usbredir_handle_status(dev, p, status); - } else { - /* If the stream was not started because of a pending error don't - send the packet to the usb-host */ - if (dev->endpoint[EP2I(ep)].iso_started) { - struct usb_redir_iso_packet_header iso_packet = { - .endpoint = ep, - .length = p->iov.size - }; - uint8_t buf[p->iov.size]; - /* No id, we look at the ep when receiving a status back */ - usb_packet_copy(p, buf, p->iov.size); - usbredirparser_send_iso_packet(dev->parser, 0, &iso_packet, - buf, p->iov.size); - usbredirparser_do_write(dev->parser); - } - status = dev->endpoint[EP2I(ep)].iso_error; - dev->endpoint[EP2I(ep)].iso_error = 0; - DPRINTF2("iso-token-out ep %02X status %d len %zd\n", ep, status, - p->iov.size); - usbredir_handle_status(dev, p, status); - } -} - -static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep) -{ - struct usb_redir_stop_iso_stream_header stop_iso_stream = { - .endpoint = ep - }; - if (dev->endpoint[EP2I(ep)].iso_started) { - usbredirparser_send_stop_iso_stream(dev->parser, 0, &stop_iso_stream); - DPRINTF("iso stream stopped ep %02X\n", ep); - dev->endpoint[EP2I(ep)].iso_started = 0; - } - dev->endpoint[EP2I(ep)].iso_error = 0; - usbredir_free_bufpq(dev, ep); -} - -/* - * The usb-host may poll the endpoint faster then our guest, resulting in lots - * of smaller bulkp-s. The below buffered_bulk_in_complete* functions combine - * data from multiple bulkp-s into a single packet, avoiding bufpq overflows. - */ -static void usbredir_buffered_bulk_add_data_to_packet(USBRedirDevice *dev, - struct buf_packet *bulkp, int count, USBPacket *p, uint8_t ep) -{ - usb_packet_copy(p, bulkp->data + bulkp->offset, count); - bulkp->offset += count; - if (bulkp->offset == bulkp->len) { - /* Store status in the last packet with data from this bulkp */ - usbredir_handle_status(dev, p, bulkp->status); - bufp_free(dev, bulkp, ep); - } -} - -static void usbredir_buffered_bulk_in_complete_raw(USBRedirDevice *dev, - USBPacket *p, uint8_t ep) -{ - struct buf_packet *bulkp; - int count; - - while ((bulkp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq)) && - p->actual_length < p->iov.size && p->status == USB_RET_SUCCESS) { - count = bulkp->len - bulkp->offset; - if (count > (p->iov.size - p->actual_length)) { - count = p->iov.size - p->actual_length; - } - usbredir_buffered_bulk_add_data_to_packet(dev, bulkp, count, p, ep); - } -} - -static void usbredir_buffered_bulk_in_complete_ftdi(USBRedirDevice *dev, - USBPacket *p, uint8_t ep) -{ - const int maxp = dev->endpoint[EP2I(ep)].max_packet_size; - uint8_t header[2] = { 0, 0 }; - struct buf_packet *bulkp; - int count; - - while ((bulkp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq)) && - p->actual_length < p->iov.size && p->status == USB_RET_SUCCESS) { - if (bulkp->len < 2) { - WARNING("malformed ftdi bulk in packet\n"); - bufp_free(dev, bulkp, ep); - continue; - } - - if ((p->actual_length % maxp) == 0) { - usb_packet_copy(p, bulkp->data, 2); - memcpy(header, bulkp->data, 2); - } else { - if (bulkp->data[0] != header[0] || bulkp->data[1] != header[1]) { - break; /* Different header, add to next packet */ - } - } - - if (bulkp->offset == 0) { - bulkp->offset = 2; /* Skip header */ - } - count = bulkp->len - bulkp->offset; - /* Must repeat the header at maxp interval */ - if (count > (maxp - (p->actual_length % maxp))) { - count = maxp - (p->actual_length % maxp); - } - usbredir_buffered_bulk_add_data_to_packet(dev, bulkp, count, p, ep); - } -} - -static void usbredir_buffered_bulk_in_complete(USBRedirDevice *dev, - USBPacket *p, uint8_t ep) -{ - p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ - dev->buffered_bulk_in_complete(dev, p, ep); - DPRINTF("bulk-token-in ep %02X status %d len %d id %"PRIu64"\n", - ep, p->status, p->actual_length, p->id); -} - -static void usbredir_handle_buffered_bulk_in_data(USBRedirDevice *dev, - USBPacket *p, uint8_t ep) -{ - /* Input bulk endpoint, buffered packet input */ - if (!dev->endpoint[EP2I(ep)].bulk_receiving_started) { - int bpt; - struct usb_redir_start_bulk_receiving_header start = { - .endpoint = ep, - .stream_id = 0, - .no_transfers = 5, - }; - /* Round bytes_per_transfer up to a multiple of max_packet_size */ - bpt = 512 + dev->endpoint[EP2I(ep)].max_packet_size - 1; - bpt /= dev->endpoint[EP2I(ep)].max_packet_size; - bpt *= dev->endpoint[EP2I(ep)].max_packet_size; - start.bytes_per_transfer = bpt; - /* No id, we look at the ep when receiving a status back */ - usbredirparser_send_start_bulk_receiving(dev->parser, 0, &start); - usbredirparser_do_write(dev->parser); - DPRINTF("bulk receiving started bytes/transfer %u count %d ep %02X\n", - start.bytes_per_transfer, start.no_transfers, ep); - dev->endpoint[EP2I(ep)].bulk_receiving_started = 1; - /* We don't really want to drop bulk packets ever, but - having some upper limit to how much we buffer is good. */ - dev->endpoint[EP2I(ep)].bufpq_target_size = 5000; - dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0; - } - - if (QTAILQ_EMPTY(&dev->endpoint[EP2I(ep)].bufpq)) { - DPRINTF("bulk-token-in ep %02X, no bulkp\n", ep); - assert(dev->endpoint[EP2I(ep)].pending_async_packet == NULL); - dev->endpoint[EP2I(ep)].pending_async_packet = p; - p->status = USB_RET_ASYNC; - return; - } - usbredir_buffered_bulk_in_complete(dev, p, ep); -} - -static void usbredir_stop_bulk_receiving(USBRedirDevice *dev, uint8_t ep) -{ - struct usb_redir_stop_bulk_receiving_header stop_bulk = { - .endpoint = ep, - .stream_id = 0, - }; - if (dev->endpoint[EP2I(ep)].bulk_receiving_started) { - usbredirparser_send_stop_bulk_receiving(dev->parser, 0, &stop_bulk); - DPRINTF("bulk receiving stopped ep %02X\n", ep); - dev->endpoint[EP2I(ep)].bulk_receiving_started = 0; - } - usbredir_free_bufpq(dev, ep); -} - -static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, - uint8_t ep) -{ - struct usb_redir_bulk_packet_header bulk_packet; - size_t size = usb_packet_size(p); - const int maxp = dev->endpoint[EP2I(ep)].max_packet_size; - - if (usbredir_already_in_flight(dev, p->id)) { - p->status = USB_RET_ASYNC; - return; - } - - if (dev->endpoint[EP2I(ep)].bulk_receiving_enabled) { - if (size != 0 && (size % maxp) == 0) { - usbredir_handle_buffered_bulk_in_data(dev, p, ep); - return; - } - WARNING("bulk recv invalid size %zd ep %02x, disabling\n", size, ep); - assert(dev->endpoint[EP2I(ep)].pending_async_packet == NULL); - usbredir_stop_bulk_receiving(dev, ep); - dev->endpoint[EP2I(ep)].bulk_receiving_enabled = 0; - } - - DPRINTF("bulk-out ep %02X stream %u len %zd id %"PRIu64"\n", - ep, p->stream, size, p->id); - - bulk_packet.endpoint = ep; - bulk_packet.length = size; - bulk_packet.stream_id = p->stream; - bulk_packet.length_high = size >> 16; - assert(bulk_packet.length_high == 0 || - usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_32bits_bulk_length)); - - if (ep & USB_DIR_IN) { - usbredirparser_send_bulk_packet(dev->parser, p->id, - &bulk_packet, NULL, 0); - } else { - uint8_t buf[size]; - usb_packet_copy(p, buf, size); - usbredir_log_data(dev, "bulk data out:", buf, size); - usbredirparser_send_bulk_packet(dev->parser, p->id, - &bulk_packet, buf, size); - } - usbredirparser_do_write(dev->parser); - p->status = USB_RET_ASYNC; -} - -static void usbredir_handle_interrupt_in_data(USBRedirDevice *dev, - USBPacket *p, uint8_t ep) -{ - /* Input interrupt endpoint, buffered packet input */ - struct buf_packet *intp; - int status, len; - - if (!dev->endpoint[EP2I(ep)].interrupt_started && - !dev->endpoint[EP2I(ep)].interrupt_error) { - struct usb_redir_start_interrupt_receiving_header start_int = { - .endpoint = ep, - }; - /* No id, we look at the ep when receiving a status back */ - usbredirparser_send_start_interrupt_receiving(dev->parser, 0, - &start_int); - usbredirparser_do_write(dev->parser); - DPRINTF("interrupt recv started ep %02X\n", ep); - dev->endpoint[EP2I(ep)].interrupt_started = 1; - /* We don't really want to drop interrupt packets ever, but - having some upper limit to how much we buffer is good. */ - dev->endpoint[EP2I(ep)].bufpq_target_size = 1000; - dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0; - } - - intp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq); - if (intp == NULL) { - DPRINTF2("interrupt-token-in ep %02X, no intp\n", ep); - /* Check interrupt_error for stream errors */ - status = dev->endpoint[EP2I(ep)].interrupt_error; - dev->endpoint[EP2I(ep)].interrupt_error = 0; - if (status) { - usbredir_handle_status(dev, p, status); - } else { - p->status = USB_RET_NAK; - } - return; - } - DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep, - intp->status, intp->len); - - status = intp->status; - len = intp->len; - if (len > p->iov.size) { - ERROR("received int data is larger then packet ep %02X\n", ep); - len = p->iov.size; - status = usb_redir_babble; - } - usb_packet_copy(p, intp->data, len); - bufp_free(dev, intp, ep); - usbredir_handle_status(dev, p, status); -} - -/* - * Handle interrupt out data, the usbredir protocol expects us to do this - * async, so that it can report back a completion status. But guests will - * expect immediate completion for an interrupt endpoint, and handling this - * async causes migration issues. So we report success directly, counting - * on the fact that output interrupt packets normally always succeed. - */ -static void usbredir_handle_interrupt_out_data(USBRedirDevice *dev, - USBPacket *p, uint8_t ep) -{ - struct usb_redir_interrupt_packet_header interrupt_packet; - uint8_t buf[p->iov.size]; - - DPRINTF("interrupt-out ep %02X len %zd id %"PRIu64"\n", ep, - p->iov.size, p->id); - - interrupt_packet.endpoint = ep; - interrupt_packet.length = p->iov.size; - - usb_packet_copy(p, buf, p->iov.size); - usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size); - usbredirparser_send_interrupt_packet(dev->parser, p->id, - &interrupt_packet, buf, p->iov.size); - usbredirparser_do_write(dev->parser); -} - -static void usbredir_stop_interrupt_receiving(USBRedirDevice *dev, - uint8_t ep) -{ - struct usb_redir_stop_interrupt_receiving_header stop_interrupt_recv = { - .endpoint = ep - }; - if (dev->endpoint[EP2I(ep)].interrupt_started) { - usbredirparser_send_stop_interrupt_receiving(dev->parser, 0, - &stop_interrupt_recv); - DPRINTF("interrupt recv stopped ep %02X\n", ep); - dev->endpoint[EP2I(ep)].interrupt_started = 0; - } - dev->endpoint[EP2I(ep)].interrupt_error = 0; - usbredir_free_bufpq(dev, ep); -} - -static void usbredir_handle_data(USBDevice *udev, USBPacket *p) -{ - USBRedirDevice *dev = USB_REDIRECT(udev); - uint8_t ep; - - ep = p->ep->nr; - if (p->pid == USB_TOKEN_IN) { - ep |= USB_DIR_IN; - } - - switch (dev->endpoint[EP2I(ep)].type) { - case USB_ENDPOINT_XFER_CONTROL: - ERROR("handle_data called for control transfer on ep %02X\n", ep); - p->status = USB_RET_NAK; - break; - case USB_ENDPOINT_XFER_BULK: - if (p->state == USB_PACKET_SETUP && p->pid == USB_TOKEN_IN && - p->ep->pipeline) { - p->status = USB_RET_ADD_TO_QUEUE; - break; - } - usbredir_handle_bulk_data(dev, p, ep); - break; - case USB_ENDPOINT_XFER_ISOC: - usbredir_handle_iso_data(dev, p, ep); - break; - case USB_ENDPOINT_XFER_INT: - if (ep & USB_DIR_IN) { - usbredir_handle_interrupt_in_data(dev, p, ep); - } else { - usbredir_handle_interrupt_out_data(dev, p, ep); - } - break; - default: - ERROR("handle_data ep %02X has unknown type %d\n", ep, - dev->endpoint[EP2I(ep)].type); - p->status = USB_RET_NAK; - } -} - -static void usbredir_flush_ep_queue(USBDevice *dev, USBEndpoint *ep) -{ - if (ep->pid == USB_TOKEN_IN && ep->pipeline) { - usb_ep_combine_input_packets(ep); - } -} - -static void usbredir_stop_ep(USBRedirDevice *dev, int i) -{ - uint8_t ep = I2EP(i); - - switch (dev->endpoint[i].type) { - case USB_ENDPOINT_XFER_BULK: - if (ep & USB_DIR_IN) { - usbredir_stop_bulk_receiving(dev, ep); - } - break; - case USB_ENDPOINT_XFER_ISOC: - usbredir_stop_iso_stream(dev, ep); - break; - case USB_ENDPOINT_XFER_INT: - if (ep & USB_DIR_IN) { - usbredir_stop_interrupt_receiving(dev, ep); - } - break; - } - usbredir_free_bufpq(dev, ep); -} - -static void usbredir_ep_stopped(USBDevice *udev, USBEndpoint *uep) -{ - USBRedirDevice *dev = USB_REDIRECT(udev); - - usbredir_stop_ep(dev, USBEP2I(uep)); - usbredirparser_do_write(dev->parser); -} - -static void usbredir_set_config(USBRedirDevice *dev, USBPacket *p, - int config) -{ - struct usb_redir_set_configuration_header set_config; - int i; - - DPRINTF("set config %d id %"PRIu64"\n", config, p->id); - - for (i = 0; i < MAX_ENDPOINTS; i++) { - usbredir_stop_ep(dev, i); - } - - set_config.configuration = config; - usbredirparser_send_set_configuration(dev->parser, p->id, &set_config); - usbredirparser_do_write(dev->parser); - p->status = USB_RET_ASYNC; -} - -static void usbredir_get_config(USBRedirDevice *dev, USBPacket *p) -{ - DPRINTF("get config id %"PRIu64"\n", p->id); - - usbredirparser_send_get_configuration(dev->parser, p->id); - usbredirparser_do_write(dev->parser); - p->status = USB_RET_ASYNC; -} - -static void usbredir_set_interface(USBRedirDevice *dev, USBPacket *p, - int interface, int alt) -{ - struct usb_redir_set_alt_setting_header set_alt; - int i; - - DPRINTF("set interface %d alt %d id %"PRIu64"\n", interface, alt, p->id); - - for (i = 0; i < MAX_ENDPOINTS; i++) { - if (dev->endpoint[i].interface == interface) { - usbredir_stop_ep(dev, i); - } - } - - set_alt.interface = interface; - set_alt.alt = alt; - usbredirparser_send_set_alt_setting(dev->parser, p->id, &set_alt); - usbredirparser_do_write(dev->parser); - p->status = USB_RET_ASYNC; -} - -static void usbredir_get_interface(USBRedirDevice *dev, USBPacket *p, - int interface) -{ - struct usb_redir_get_alt_setting_header get_alt; - - DPRINTF("get interface %d id %"PRIu64"\n", interface, p->id); - - get_alt.interface = interface; - usbredirparser_send_get_alt_setting(dev->parser, p->id, &get_alt); - usbredirparser_do_write(dev->parser); - p->status = USB_RET_ASYNC; -} - -static void usbredir_handle_control(USBDevice *udev, USBPacket *p, - int request, int value, int index, int length, uint8_t *data) -{ - USBRedirDevice *dev = USB_REDIRECT(udev); - struct usb_redir_control_packet_header control_packet; - - if (usbredir_already_in_flight(dev, p->id)) { - p->status = USB_RET_ASYNC; - return; - } - - /* Special cases for certain standard device requests */ - switch (request) { - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - DPRINTF("set address %d\n", value); - dev->dev.addr = value; - return; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - usbredir_set_config(dev, p, value & 0xff); - return; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - usbredir_get_config(dev, p); - return; - case InterfaceOutRequest | USB_REQ_SET_INTERFACE: - usbredir_set_interface(dev, p, index, value); - return; - case InterfaceRequest | USB_REQ_GET_INTERFACE: - usbredir_get_interface(dev, p, index); - return; - } - - /* Normal ctrl requests, note request is (bRequestType << 8) | bRequest */ - DPRINTF( - "ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %"PRIu64"\n", - request >> 8, request & 0xff, value, index, length, p->id); - - control_packet.request = request & 0xFF; - control_packet.requesttype = request >> 8; - control_packet.endpoint = control_packet.requesttype & USB_DIR_IN; - control_packet.value = value; - control_packet.index = index; - control_packet.length = length; - - if (control_packet.requesttype & USB_DIR_IN) { - usbredirparser_send_control_packet(dev->parser, p->id, - &control_packet, NULL, 0); - } else { - usbredir_log_data(dev, "ctrl data out:", data, length); - usbredirparser_send_control_packet(dev->parser, p->id, - &control_packet, data, length); - } - usbredirparser_do_write(dev->parser); - p->status = USB_RET_ASYNC; -} - -static int usbredir_alloc_streams(USBDevice *udev, USBEndpoint **eps, - int nr_eps, int streams) -{ - USBRedirDevice *dev = USB_REDIRECT(udev); -#if USBREDIR_VERSION >= 0x000700 - struct usb_redir_alloc_bulk_streams_header alloc_streams; - int i; - - if (!usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_bulk_streams)) { - ERROR("peer does not support streams\n"); - goto reject; - } - - if (streams == 0) { - ERROR("request to allocate 0 streams\n"); - return -1; - } - - alloc_streams.no_streams = streams; - alloc_streams.endpoints = 0; - for (i = 0; i < nr_eps; i++) { - alloc_streams.endpoints |= 1 << USBEP2I(eps[i]); - } - usbredirparser_send_alloc_bulk_streams(dev->parser, 0, &alloc_streams); - usbredirparser_do_write(dev->parser); - - return 0; -#else - ERROR("usbredir_alloc_streams not implemented\n"); - goto reject; -#endif -reject: - ERROR("streams are not available, disconnecting\n"); - qemu_bh_schedule(dev->device_reject_bh); - return -1; -} - -static void usbredir_free_streams(USBDevice *udev, USBEndpoint **eps, - int nr_eps) -{ -#if USBREDIR_VERSION >= 0x000700 - USBRedirDevice *dev = USB_REDIRECT(udev); - struct usb_redir_free_bulk_streams_header free_streams; - int i; - - if (!usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_bulk_streams)) { - return; - } - - free_streams.endpoints = 0; - for (i = 0; i < nr_eps; i++) { - free_streams.endpoints |= 1 << USBEP2I(eps[i]); - } - usbredirparser_send_free_bulk_streams(dev->parser, 0, &free_streams); - usbredirparser_do_write(dev->parser); -#endif -} - -/* - * Close events can be triggered by usbredirparser_do_write which gets called - * from within the USBDevice data / control packet callbacks and doing a - * usb_detach from within these callbacks is not a good idea. - * - * So we use a bh handler to take care of close events. - */ -static void usbredir_chardev_close_bh(void *opaque) -{ - USBRedirDevice *dev = opaque; - - qemu_bh_cancel(dev->device_reject_bh); - usbredir_device_disconnect(dev); - - if (dev->parser) { - DPRINTF("destroying usbredirparser\n"); - usbredirparser_destroy(dev->parser); - dev->parser = NULL; - } - if (dev->watch) { - g_source_remove(dev->watch); - dev->watch = 0; - } -} - -static void usbredir_create_parser(USBRedirDevice *dev) -{ - uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, }; - int flags = 0; - - DPRINTF("creating usbredirparser\n"); - - dev->parser = qemu_oom_check(usbredirparser_create()); - dev->parser->priv = dev; - dev->parser->log_func = usbredir_log; - dev->parser->read_func = usbredir_read; - dev->parser->write_func = usbredir_write; - dev->parser->hello_func = usbredir_hello; - dev->parser->device_connect_func = usbredir_device_connect; - dev->parser->device_disconnect_func = usbredir_device_disconnect; - dev->parser->interface_info_func = usbredir_interface_info; - dev->parser->ep_info_func = usbredir_ep_info; - dev->parser->configuration_status_func = usbredir_configuration_status; - dev->parser->alt_setting_status_func = usbredir_alt_setting_status; - dev->parser->iso_stream_status_func = usbredir_iso_stream_status; - dev->parser->interrupt_receiving_status_func = - usbredir_interrupt_receiving_status; - dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status; - dev->parser->bulk_receiving_status_func = usbredir_bulk_receiving_status; - dev->parser->control_packet_func = usbredir_control_packet; - dev->parser->bulk_packet_func = usbredir_bulk_packet; - dev->parser->iso_packet_func = usbredir_iso_packet; - dev->parser->interrupt_packet_func = usbredir_interrupt_packet; - dev->parser->buffered_bulk_packet_func = usbredir_buffered_bulk_packet; - dev->read_buf = NULL; - dev->read_buf_size = 0; - - usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version); - usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); - usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size); - usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids); - usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length); - usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving); -#if USBREDIR_VERSION >= 0x000700 - usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams); -#endif - - if (runstate_check(RUN_STATE_INMIGRATE)) { - flags |= usbredirparser_fl_no_hello; - } - usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE, - flags); - usbredirparser_do_write(dev->parser); -} - -static void usbredir_reject_device(USBRedirDevice *dev) -{ - usbredir_device_disconnect(dev); - if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) { - usbredirparser_send_filter_reject(dev->parser); - usbredirparser_do_write(dev->parser); - } -} - -/* - * We may need to reject the device when the hcd calls alloc_streams, doing - * an usb_detach from within a hcd call is not a good idea, hence this bh. - */ -static void usbredir_device_reject_bh(void *opaque) -{ - USBRedirDevice *dev = opaque; - - usbredir_reject_device(dev); -} - -static void usbredir_do_attach(void *opaque) -{ - USBRedirDevice *dev = opaque; - Error *local_err = NULL; - - /* In order to work properly with XHCI controllers we need these caps */ - if ((dev->dev.port->speedmask & USB_SPEED_MASK_SUPER) && !( - usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_ep_info_max_packet_size) && - usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_32bits_bulk_length) && - usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_64bits_ids))) { - ERROR("usb-redir-host lacks capabilities needed for use with XHCI\n"); - usbredir_reject_device(dev); - return; - } - - usb_device_attach(&dev->dev, &local_err); - if (local_err) { - error_report_err(local_err); - WARNING("rejecting device due to speed mismatch\n"); - usbredir_reject_device(dev); - } -} - -/* - * chardev callbacks - */ - -static int usbredir_chardev_can_read(void *opaque) -{ - USBRedirDevice *dev = opaque; - - if (!dev->parser) { - WARNING("chardev_can_read called on non open chardev!\n"); - return 0; - } - - /* Don't read new data from the chardev until our state is fully synced */ - if (!runstate_check(RUN_STATE_RUNNING)) { - return 0; - } - - /* usbredir_parser_do_read will consume *all* data we give it */ - return 1024 * 1024; -} - -static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size) -{ - USBRedirDevice *dev = opaque; - - /* No recursion allowed! */ - assert(dev->read_buf == NULL); - - dev->read_buf = buf; - dev->read_buf_size = size; - - usbredirparser_do_read(dev->parser); - /* Send any acks, etc. which may be queued now */ - usbredirparser_do_write(dev->parser); -} - -static void usbredir_chardev_event(void *opaque, int event) -{ - USBRedirDevice *dev = opaque; - - switch (event) { - case CHR_EVENT_OPENED: - DPRINTF("chardev open\n"); - /* Make sure any pending closes are handled (no-op if none pending) */ - usbredir_chardev_close_bh(dev); - qemu_bh_cancel(dev->chardev_close_bh); - usbredir_create_parser(dev); - break; - case CHR_EVENT_CLOSED: - DPRINTF("chardev close\n"); - qemu_bh_schedule(dev->chardev_close_bh); - break; - } -} - -/* - * init + destroy - */ - -static void usbredir_vm_state_change(void *priv, int running, RunState state) -{ - USBRedirDevice *dev = priv; - - if (state == RUN_STATE_RUNNING && dev->parser != NULL) { - usbredirparser_do_write(dev->parser); /* Flush any pending writes */ - } -} - -static void usbredir_init_endpoints(USBRedirDevice *dev) -{ - int i; - - usb_ep_init(&dev->dev); - memset(dev->endpoint, 0, sizeof(dev->endpoint)); - for (i = 0; i < MAX_ENDPOINTS; i++) { - dev->endpoint[i].dev = dev; - QTAILQ_INIT(&dev->endpoint[i].bufpq); - } -} - -static void usbredir_realize(USBDevice *udev, Error **errp) -{ - USBRedirDevice *dev = USB_REDIRECT(udev); - int i; - - if (dev->cs == NULL) { - error_setg(errp, QERR_MISSING_PARAMETER, "chardev"); - return; - } - - if (dev->filter_str) { - i = usbredirfilter_string_to_rules(dev->filter_str, ":", "|", - &dev->filter_rules, - &dev->filter_rules_count); - if (i) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "filter", - "a usb device filter string"); - return; - } - } - - dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev); - dev->device_reject_bh = qemu_bh_new(usbredir_device_reject_bh, dev); - dev->attach_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, usbredir_do_attach, dev); - - packet_id_queue_init(&dev->cancelled, dev, "cancelled"); - packet_id_queue_init(&dev->already_in_flight, dev, "already-in-flight"); - usbredir_init_endpoints(dev); - - /* We'll do the attach once we receive the speed from the usb-host */ - udev->auto_attach = 0; - - /* Will be cleared during setup when we find conflicts */ - dev->compatible_speedmask = USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH; - - /* Let the backend know we are ready */ - qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read, - usbredir_chardev_read, usbredir_chardev_event, dev); - - qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev); -} - -static void usbredir_cleanup_device_queues(USBRedirDevice *dev) -{ - int i; - - packet_id_queue_empty(&dev->cancelled); - packet_id_queue_empty(&dev->already_in_flight); - for (i = 0; i < MAX_ENDPOINTS; i++) { - usbredir_free_bufpq(dev, I2EP(i)); - } -} - -static void usbredir_handle_destroy(USBDevice *udev) -{ - USBRedirDevice *dev = USB_REDIRECT(udev); - - qemu_chr_delete(dev->cs); - dev->cs = NULL; - /* Note must be done after qemu_chr_close, as that causes a close event */ - qemu_bh_delete(dev->chardev_close_bh); - qemu_bh_delete(dev->device_reject_bh); - - timer_del(dev->attach_timer); - timer_free(dev->attach_timer); - - usbredir_cleanup_device_queues(dev); - - if (dev->parser) { - usbredirparser_destroy(dev->parser); - } - if (dev->watch) { - g_source_remove(dev->watch); - } - - free(dev->filter_rules); -} - -static int usbredir_check_filter(USBRedirDevice *dev) -{ - if (dev->interface_info.interface_count == NO_INTERFACE_INFO) { - ERROR("No interface info for device\n"); - goto error; - } - - if (dev->filter_rules) { - if (!usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_connect_device_version)) { - ERROR("Device filter specified and peer does not have the " - "connect_device_version capability\n"); - goto error; - } - - if (usbredirfilter_check( - dev->filter_rules, - dev->filter_rules_count, - dev->device_info.device_class, - dev->device_info.device_subclass, - dev->device_info.device_protocol, - dev->interface_info.interface_class, - dev->interface_info.interface_subclass, - dev->interface_info.interface_protocol, - dev->interface_info.interface_count, - dev->device_info.vendor_id, - dev->device_info.product_id, - dev->device_info.device_version_bcd, - 0) != 0) { - goto error; - } - } - - return 0; - -error: - usbredir_reject_device(dev); - return -1; -} - -static void usbredir_check_bulk_receiving(USBRedirDevice *dev) -{ - int i, j, quirks; - - if (!usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_bulk_receiving)) { - return; - } - - for (i = EP2I(USB_DIR_IN); i < MAX_ENDPOINTS; i++) { - dev->endpoint[i].bulk_receiving_enabled = 0; - } - for (i = 0; i < dev->interface_info.interface_count; i++) { - quirks = usb_get_quirks(dev->device_info.vendor_id, - dev->device_info.product_id, - dev->interface_info.interface_class[i], - dev->interface_info.interface_subclass[i], - dev->interface_info.interface_protocol[i]); - if (!(quirks & USB_QUIRK_BUFFER_BULK_IN)) { - continue; - } - if (quirks & USB_QUIRK_IS_FTDI) { - dev->buffered_bulk_in_complete = - usbredir_buffered_bulk_in_complete_ftdi; - } else { - dev->buffered_bulk_in_complete = - usbredir_buffered_bulk_in_complete_raw; - } - - for (j = EP2I(USB_DIR_IN); j < MAX_ENDPOINTS; j++) { - if (dev->endpoint[j].interface == - dev->interface_info.interface[i] && - dev->endpoint[j].type == USB_ENDPOINT_XFER_BULK && - dev->endpoint[j].max_packet_size != 0) { - dev->endpoint[j].bulk_receiving_enabled = 1; - /* - * With buffering pipelining is not necessary. Also packet - * combining and bulk in buffering don't play nice together! - */ - I2USBEP(dev, j)->pipeline = false; - break; /* Only buffer for the first ep of each intf */ - } - } - } -} - -/* - * usbredirparser packet complete callbacks - */ - -static void usbredir_handle_status(USBRedirDevice *dev, USBPacket *p, - int status) -{ - switch (status) { - case usb_redir_success: - p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ - break; - case usb_redir_stall: - p->status = USB_RET_STALL; - break; - case usb_redir_cancelled: - /* - * When the usbredir-host unredirects a device, it will report a status - * of cancelled for all pending packets, followed by a disconnect msg. - */ - p->status = USB_RET_IOERROR; - break; - case usb_redir_inval: - WARNING("got invalid param error from usb-host?\n"); - p->status = USB_RET_IOERROR; - break; - case usb_redir_babble: - p->status = USB_RET_BABBLE; - break; - case usb_redir_ioerror: - case usb_redir_timeout: - default: - p->status = USB_RET_IOERROR; - } -} - -static void usbredir_hello(void *priv, struct usb_redir_hello_header *h) -{ - USBRedirDevice *dev = priv; - - /* Try to send the filter info now that we've the usb-host's caps */ - if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter) && - dev->filter_rules) { - usbredirparser_send_filter_filter(dev->parser, dev->filter_rules, - dev->filter_rules_count); - usbredirparser_do_write(dev->parser); - } -} - -static void usbredir_device_connect(void *priv, - struct usb_redir_device_connect_header *device_connect) -{ - USBRedirDevice *dev = priv; - const char *speed; - - if (timer_pending(dev->attach_timer) || dev->dev.attached) { - ERROR("Received device connect while already connected\n"); - return; - } - - switch (device_connect->speed) { - case usb_redir_speed_low: - speed = "low speed"; - dev->dev.speed = USB_SPEED_LOW; - dev->compatible_speedmask &= ~USB_SPEED_MASK_FULL; - dev->compatible_speedmask &= ~USB_SPEED_MASK_HIGH; - break; - case usb_redir_speed_full: - speed = "full speed"; - dev->dev.speed = USB_SPEED_FULL; - dev->compatible_speedmask &= ~USB_SPEED_MASK_HIGH; - break; - case usb_redir_speed_high: - speed = "high speed"; - dev->dev.speed = USB_SPEED_HIGH; - break; - case usb_redir_speed_super: - speed = "super speed"; - dev->dev.speed = USB_SPEED_SUPER; - break; - default: - speed = "unknown speed"; - dev->dev.speed = USB_SPEED_FULL; - } - - if (usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_connect_device_version)) { - INFO("attaching %s device %04x:%04x version %d.%d class %02x\n", - speed, device_connect->vendor_id, device_connect->product_id, - ((device_connect->device_version_bcd & 0xf000) >> 12) * 10 + - ((device_connect->device_version_bcd & 0x0f00) >> 8), - ((device_connect->device_version_bcd & 0x00f0) >> 4) * 10 + - ((device_connect->device_version_bcd & 0x000f) >> 0), - device_connect->device_class); - } else { - INFO("attaching %s device %04x:%04x class %02x\n", speed, - device_connect->vendor_id, device_connect->product_id, - device_connect->device_class); - } - - dev->dev.speedmask = (1 << dev->dev.speed) | dev->compatible_speedmask; - dev->device_info = *device_connect; - - if (usbredir_check_filter(dev)) { - WARNING("Device %04x:%04x rejected by device filter, not attaching\n", - device_connect->vendor_id, device_connect->product_id); - return; - } - - usbredir_check_bulk_receiving(dev); - timer_mod(dev->attach_timer, dev->next_attach_time); -} - -static void usbredir_device_disconnect(void *priv) -{ - USBRedirDevice *dev = priv; - - /* Stop any pending attaches */ - timer_del(dev->attach_timer); - - if (dev->dev.attached) { - DPRINTF("detaching device\n"); - usb_device_detach(&dev->dev); - /* - * Delay next usb device attach to give the guest a chance to see - * see the detach / attach in case of quick close / open succession - */ - dev->next_attach_time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 200; - } - - /* Reset state so that the next dev connected starts with a clean slate */ - usbredir_cleanup_device_queues(dev); - usbredir_init_endpoints(dev); - dev->interface_info.interface_count = NO_INTERFACE_INFO; - dev->dev.addr = 0; - dev->dev.speed = 0; - dev->compatible_speedmask = USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH; -} - -static void usbredir_interface_info(void *priv, - struct usb_redir_interface_info_header *interface_info) -{ - USBRedirDevice *dev = priv; - - dev->interface_info = *interface_info; - - /* - * If we receive interface info after the device has already been - * connected (ie on a set_config), re-check interface dependent things. - */ - if (timer_pending(dev->attach_timer) || dev->dev.attached) { - usbredir_check_bulk_receiving(dev); - if (usbredir_check_filter(dev)) { - ERROR("Device no longer matches filter after interface info " - "change, disconnecting!\n"); - } - } -} - -static void usbredir_mark_speed_incompatible(USBRedirDevice *dev, int speed) -{ - dev->compatible_speedmask &= ~(1 << speed); - dev->dev.speedmask = (1 << dev->dev.speed) | dev->compatible_speedmask; -} - -static void usbredir_set_pipeline(USBRedirDevice *dev, struct USBEndpoint *uep) -{ - if (uep->type != USB_ENDPOINT_XFER_BULK) { - return; - } - if (uep->pid == USB_TOKEN_OUT) { - uep->pipeline = true; - } - if (uep->pid == USB_TOKEN_IN && uep->max_packet_size != 0 && - usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_32bits_bulk_length)) { - uep->pipeline = true; - } -} - -static void usbredir_setup_usb_eps(USBRedirDevice *dev) -{ - struct USBEndpoint *usb_ep; - int i; - - for (i = 0; i < MAX_ENDPOINTS; i++) { - usb_ep = I2USBEP(dev, i); - usb_ep->type = dev->endpoint[i].type; - usb_ep->ifnum = dev->endpoint[i].interface; - usb_ep->max_packet_size = dev->endpoint[i].max_packet_size; - usb_ep->max_streams = dev->endpoint[i].max_streams; - usbredir_set_pipeline(dev, usb_ep); - } -} - -static void usbredir_ep_info(void *priv, - struct usb_redir_ep_info_header *ep_info) -{ - USBRedirDevice *dev = priv; - int i; - - for (i = 0; i < MAX_ENDPOINTS; i++) { - dev->endpoint[i].type = ep_info->type[i]; - dev->endpoint[i].interval = ep_info->interval[i]; - dev->endpoint[i].interface = ep_info->interface[i]; - if (usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_ep_info_max_packet_size)) { - dev->endpoint[i].max_packet_size = ep_info->max_packet_size[i]; - } -#if USBREDIR_VERSION >= 0x000700 - if (usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_bulk_streams)) { - dev->endpoint[i].max_streams = ep_info->max_streams[i]; - } -#endif - switch (dev->endpoint[i].type) { - case usb_redir_type_invalid: - break; - case usb_redir_type_iso: - usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL); - usbredir_mark_speed_incompatible(dev, USB_SPEED_HIGH); - /* Fall through */ - case usb_redir_type_interrupt: - if (!usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_ep_info_max_packet_size) || - ep_info->max_packet_size[i] > 64) { - usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL); - } - if (!usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_ep_info_max_packet_size) || - ep_info->max_packet_size[i] > 1024) { - usbredir_mark_speed_incompatible(dev, USB_SPEED_HIGH); - } - if (dev->endpoint[i].interval == 0) { - ERROR("Received 0 interval for isoc or irq endpoint\n"); - usbredir_reject_device(dev); - return; - } - /* Fall through */ - case usb_redir_type_control: - case usb_redir_type_bulk: - DPRINTF("ep: %02X type: %d interface: %d\n", I2EP(i), - dev->endpoint[i].type, dev->endpoint[i].interface); - break; - default: - ERROR("Received invalid endpoint type\n"); - usbredir_reject_device(dev); - return; - } - } - /* The new ep info may have caused a speed incompatibility, recheck */ - if (dev->dev.attached && - !(dev->dev.port->speedmask & dev->dev.speedmask)) { - ERROR("Device no longer matches speed after endpoint info change, " - "disconnecting!\n"); - usbredir_reject_device(dev); - return; - } - usbredir_setup_usb_eps(dev); - usbredir_check_bulk_receiving(dev); -} - -static void usbredir_configuration_status(void *priv, uint64_t id, - struct usb_redir_configuration_status_header *config_status) -{ - USBRedirDevice *dev = priv; - USBPacket *p; - - DPRINTF("set config status %d config %d id %"PRIu64"\n", - config_status->status, config_status->configuration, id); - - p = usbredir_find_packet_by_id(dev, 0, id); - if (p) { - if (dev->dev.setup_buf[0] & USB_DIR_IN) { - dev->dev.data_buf[0] = config_status->configuration; - p->actual_length = 1; - } - usbredir_handle_status(dev, p, config_status->status); - usb_generic_async_ctrl_complete(&dev->dev, p); - } -} - -static void usbredir_alt_setting_status(void *priv, uint64_t id, - struct usb_redir_alt_setting_status_header *alt_setting_status) -{ - USBRedirDevice *dev = priv; - USBPacket *p; - - DPRINTF("alt status %d intf %d alt %d id: %"PRIu64"\n", - alt_setting_status->status, alt_setting_status->interface, - alt_setting_status->alt, id); - - p = usbredir_find_packet_by_id(dev, 0, id); - if (p) { - if (dev->dev.setup_buf[0] & USB_DIR_IN) { - dev->dev.data_buf[0] = alt_setting_status->alt; - p->actual_length = 1; - } - usbredir_handle_status(dev, p, alt_setting_status->status); - usb_generic_async_ctrl_complete(&dev->dev, p); - } -} - -static void usbredir_iso_stream_status(void *priv, uint64_t id, - struct usb_redir_iso_stream_status_header *iso_stream_status) -{ - USBRedirDevice *dev = priv; - uint8_t ep = iso_stream_status->endpoint; - - DPRINTF("iso status %d ep %02X id %"PRIu64"\n", iso_stream_status->status, - ep, id); - - if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].iso_started) { - return; - } - - dev->endpoint[EP2I(ep)].iso_error = iso_stream_status->status; - if (iso_stream_status->status == usb_redir_stall) { - DPRINTF("iso stream stopped by peer ep %02X\n", ep); - dev->endpoint[EP2I(ep)].iso_started = 0; - } -} - -static void usbredir_interrupt_receiving_status(void *priv, uint64_t id, - struct usb_redir_interrupt_receiving_status_header - *interrupt_receiving_status) -{ - USBRedirDevice *dev = priv; - uint8_t ep = interrupt_receiving_status->endpoint; - - DPRINTF("interrupt recv status %d ep %02X id %"PRIu64"\n", - interrupt_receiving_status->status, ep, id); - - if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].interrupt_started) { - return; - } - - dev->endpoint[EP2I(ep)].interrupt_error = - interrupt_receiving_status->status; - if (interrupt_receiving_status->status == usb_redir_stall) { - DPRINTF("interrupt receiving stopped by peer ep %02X\n", ep); - dev->endpoint[EP2I(ep)].interrupt_started = 0; - } -} - -static void usbredir_bulk_streams_status(void *priv, uint64_t id, - struct usb_redir_bulk_streams_status_header *bulk_streams_status) -{ -#if USBREDIR_VERSION >= 0x000700 - USBRedirDevice *dev = priv; - - if (bulk_streams_status->status == usb_redir_success) { - DPRINTF("bulk streams status %d eps %08x\n", - bulk_streams_status->status, bulk_streams_status->endpoints); - } else { - ERROR("bulk streams %s failed status %d eps %08x\n", - (bulk_streams_status->no_streams == 0) ? "free" : "alloc", - bulk_streams_status->status, bulk_streams_status->endpoints); - ERROR("usb-redir-host does not provide streams, disconnecting\n"); - usbredir_reject_device(dev); - } -#endif -} - -static void usbredir_bulk_receiving_status(void *priv, uint64_t id, - struct usb_redir_bulk_receiving_status_header *bulk_receiving_status) -{ - USBRedirDevice *dev = priv; - uint8_t ep = bulk_receiving_status->endpoint; - - DPRINTF("bulk recv status %d ep %02X id %"PRIu64"\n", - bulk_receiving_status->status, ep, id); - - if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].bulk_receiving_started) { - return; - } - - if (bulk_receiving_status->status == usb_redir_stall) { - DPRINTF("bulk receiving stopped by peer ep %02X\n", ep); - dev->endpoint[EP2I(ep)].bulk_receiving_started = 0; - } -} - -static void usbredir_control_packet(void *priv, uint64_t id, - struct usb_redir_control_packet_header *control_packet, - uint8_t *data, int data_len) -{ - USBRedirDevice *dev = priv; - USBPacket *p; - int len = control_packet->length; - - DPRINTF("ctrl-in status %d len %d id %"PRIu64"\n", control_packet->status, - len, id); - - /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices - * to work redirected to a not superspeed capable hcd */ - if (dev->dev.speed == USB_SPEED_SUPER && - !((dev->dev.port->speedmask & USB_SPEED_MASK_SUPER)) && - control_packet->requesttype == 0x80 && - control_packet->request == 6 && - control_packet->value == 0x100 && control_packet->index == 0 && - data_len >= 18 && data[7] == 9) { - data[7] = 64; - } - - p = usbredir_find_packet_by_id(dev, 0, id); - if (p) { - usbredir_handle_status(dev, p, control_packet->status); - if (data_len > 0) { - usbredir_log_data(dev, "ctrl data in:", data, data_len); - if (data_len > sizeof(dev->dev.data_buf)) { - ERROR("ctrl buffer too small (%d > %zu)\n", - data_len, sizeof(dev->dev.data_buf)); - p->status = USB_RET_STALL; - data_len = len = sizeof(dev->dev.data_buf); - } - memcpy(dev->dev.data_buf, data, data_len); - } - p->actual_length = len; - usb_generic_async_ctrl_complete(&dev->dev, p); - } - free(data); -} - -static void usbredir_bulk_packet(void *priv, uint64_t id, - struct usb_redir_bulk_packet_header *bulk_packet, - uint8_t *data, int data_len) -{ - USBRedirDevice *dev = priv; - uint8_t ep = bulk_packet->endpoint; - int len = (bulk_packet->length_high << 16) | bulk_packet->length; - USBPacket *p; - - DPRINTF("bulk-in status %d ep %02X stream %u len %d id %"PRIu64"\n", - bulk_packet->status, ep, bulk_packet->stream_id, len, id); - - p = usbredir_find_packet_by_id(dev, ep, id); - if (p) { - size_t size = usb_packet_size(p); - usbredir_handle_status(dev, p, bulk_packet->status); - if (data_len > 0) { - usbredir_log_data(dev, "bulk data in:", data, data_len); - if (data_len > size) { - ERROR("bulk got more data then requested (%d > %zd)\n", - data_len, p->iov.size); - p->status = USB_RET_BABBLE; - data_len = len = size; - } - usb_packet_copy(p, data, data_len); - } - p->actual_length = len; - if (p->pid == USB_TOKEN_IN && p->ep->pipeline) { - usb_combined_input_packet_complete(&dev->dev, p); - } else { - usb_packet_complete(&dev->dev, p); - } - } - free(data); -} - -static void usbredir_iso_packet(void *priv, uint64_t id, - struct usb_redir_iso_packet_header *iso_packet, - uint8_t *data, int data_len) -{ - USBRedirDevice *dev = priv; - uint8_t ep = iso_packet->endpoint; - - DPRINTF2("iso-in status %d ep %02X len %d id %"PRIu64"\n", - iso_packet->status, ep, data_len, id); - - if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_ISOC) { - ERROR("received iso packet for non iso endpoint %02X\n", ep); - free(data); - return; - } - - if (dev->endpoint[EP2I(ep)].iso_started == 0) { - DPRINTF("received iso packet for non started stream ep %02X\n", ep); - free(data); - return; - } - - /* bufp_alloc also adds the packet to the ep queue */ - bufp_alloc(dev, data, data_len, iso_packet->status, ep, data); -} - -static void usbredir_interrupt_packet(void *priv, uint64_t id, - struct usb_redir_interrupt_packet_header *interrupt_packet, - uint8_t *data, int data_len) -{ - USBRedirDevice *dev = priv; - uint8_t ep = interrupt_packet->endpoint; - - DPRINTF("interrupt-in status %d ep %02X len %d id %"PRIu64"\n", - interrupt_packet->status, ep, data_len, id); - - if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_INT) { - ERROR("received int packet for non interrupt endpoint %02X\n", ep); - free(data); - return; - } - - if (ep & USB_DIR_IN) { - if (dev->endpoint[EP2I(ep)].interrupt_started == 0) { - DPRINTF("received int packet while not started ep %02X\n", ep); - free(data); - return; - } - - if (QTAILQ_EMPTY(&dev->endpoint[EP2I(ep)].bufpq)) { - usb_wakeup(usb_ep_get(&dev->dev, USB_TOKEN_IN, ep & 0x0f), 0); - } - - /* bufp_alloc also adds the packet to the ep queue */ - bufp_alloc(dev, data, data_len, interrupt_packet->status, ep, data); - } else { - /* - * We report output interrupt packets as completed directly upon - * submission, so all we can do here if one failed is warn. - */ - if (interrupt_packet->status) { - WARNING("interrupt output failed status %d ep %02X id %"PRIu64"\n", - interrupt_packet->status, ep, id); - } - } -} - -static void usbredir_buffered_bulk_packet(void *priv, uint64_t id, - struct usb_redir_buffered_bulk_packet_header *buffered_bulk_packet, - uint8_t *data, int data_len) -{ - USBRedirDevice *dev = priv; - uint8_t status, ep = buffered_bulk_packet->endpoint; - void *free_on_destroy; - int i, len; - - DPRINTF("buffered-bulk-in status %d ep %02X len %d id %"PRIu64"\n", - buffered_bulk_packet->status, ep, data_len, id); - - if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_BULK) { - ERROR("received buffered-bulk packet for non bulk ep %02X\n", ep); - free(data); - return; - } - - if (dev->endpoint[EP2I(ep)].bulk_receiving_started == 0) { - DPRINTF("received buffered-bulk packet on not started ep %02X\n", ep); - free(data); - return; - } - - /* Data must be in maxp chunks for buffered_bulk_add_*_data_to_packet */ - len = dev->endpoint[EP2I(ep)].max_packet_size; - 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 */ - r = bufp_alloc(dev, data + i, len, status, ep, free_on_destroy); - if (r) { - break; - } - } - - if (dev->endpoint[EP2I(ep)].pending_async_packet) { - USBPacket *p = dev->endpoint[EP2I(ep)].pending_async_packet; - dev->endpoint[EP2I(ep)].pending_async_packet = NULL; - usbredir_buffered_bulk_in_complete(dev, p, ep); - usb_packet_complete(&dev->dev, p); - } -} - -/* - * Migration code - */ - -static void usbredir_pre_save(void *priv) -{ - USBRedirDevice *dev = priv; - - usbredir_fill_already_in_flight(dev); -} - -static int usbredir_post_load(void *priv, int version_id) -{ - USBRedirDevice *dev = priv; - - if (dev->parser == NULL) { - return 0; - } - - switch (dev->device_info.speed) { - case usb_redir_speed_low: - dev->dev.speed = USB_SPEED_LOW; - break; - case usb_redir_speed_full: - dev->dev.speed = USB_SPEED_FULL; - break; - case usb_redir_speed_high: - dev->dev.speed = USB_SPEED_HIGH; - break; - case usb_redir_speed_super: - dev->dev.speed = USB_SPEED_SUPER; - break; - default: - dev->dev.speed = USB_SPEED_FULL; - } - dev->dev.speedmask = (1 << dev->dev.speed); - - usbredir_setup_usb_eps(dev); - usbredir_check_bulk_receiving(dev); - - return 0; -} - -/* For usbredirparser migration */ -static void usbredir_put_parser(QEMUFile *f, void *priv, size_t unused) -{ - USBRedirDevice *dev = priv; - uint8_t *data; - int len; - - if (dev->parser == NULL) { - qemu_put_be32(f, 0); - return; - } - - usbredirparser_serialize(dev->parser, &data, &len); - qemu_oom_check(data); - - qemu_put_be32(f, len); - qemu_put_buffer(f, data, len); - - free(data); -} - -static int usbredir_get_parser(QEMUFile *f, void *priv, size_t unused) -{ - USBRedirDevice *dev = priv; - uint8_t *data; - int len, ret; - - len = qemu_get_be32(f); - if (len == 0) { - return 0; - } - - /* - * If our chardev is not open already at this point the usbredir connection - * has been broken (non seamless migration, or restore from disk). - * - * In this case create a temporary parser to receive the migration data, - * and schedule the close_bh to report the device as disconnected to the - * guest and to destroy the parser again. - */ - if (dev->parser == NULL) { - WARNING("usb-redir connection broken during migration\n"); - usbredir_create_parser(dev); - qemu_bh_schedule(dev->chardev_close_bh); - } - - data = g_malloc(len); - qemu_get_buffer(f, data, len); - - ret = usbredirparser_unserialize(dev->parser, data, len); - - g_free(data); - - return ret; -} - -static const VMStateInfo usbredir_parser_vmstate_info = { - .name = "usb-redir-parser", - .put = usbredir_put_parser, - .get = usbredir_get_parser, -}; - - -/* For buffered packets (iso/irq) queue migration */ -static void usbredir_put_bufpq(QEMUFile *f, void *priv, size_t unused) -{ - struct endp_data *endp = priv; - USBRedirDevice *dev = endp->dev; - struct buf_packet *bufp; - int len, i = 0; - - qemu_put_be32(f, endp->bufpq_size); - QTAILQ_FOREACH(bufp, &endp->bufpq, next) { - len = bufp->len - bufp->offset; - DPRINTF("put_bufpq %d/%d len %d status %d\n", i + 1, endp->bufpq_size, - len, bufp->status); - qemu_put_be32(f, len); - qemu_put_be32(f, bufp->status); - qemu_put_buffer(f, bufp->data + bufp->offset, len); - i++; - } - assert(i == endp->bufpq_size); -} - -static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused) -{ - struct endp_data *endp = priv; - USBRedirDevice *dev = endp->dev; - struct buf_packet *bufp; - int i; - - endp->bufpq_size = qemu_get_be32(f); - for (i = 0; i < endp->bufpq_size; i++) { - bufp = g_new(struct buf_packet, 1); - bufp->len = qemu_get_be32(f); - bufp->status = qemu_get_be32(f); - bufp->offset = 0; - bufp->data = qemu_oom_check(malloc(bufp->len)); /* regular malloc! */ - bufp->free_on_destroy = bufp->data; - qemu_get_buffer(f, bufp->data, bufp->len); - QTAILQ_INSERT_TAIL(&endp->bufpq, bufp, next); - DPRINTF("get_bufpq %d/%d len %d status %d\n", i + 1, endp->bufpq_size, - bufp->len, bufp->status); - } - return 0; -} - -static const VMStateInfo usbredir_ep_bufpq_vmstate_info = { - .name = "usb-redir-bufpq", - .put = usbredir_put_bufpq, - .get = usbredir_get_bufpq, -}; - - -/* For endp_data migration */ -static bool usbredir_bulk_receiving_needed(void *priv) -{ - struct endp_data *endp = priv; - - return endp->bulk_receiving_started; -} - -static const VMStateDescription usbredir_bulk_receiving_vmstate = { - .name = "usb-redir-ep/bulk-receiving", - .version_id = 1, - .minimum_version_id = 1, - .needed = usbredir_bulk_receiving_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(bulk_receiving_started, struct endp_data), - VMSTATE_END_OF_LIST() - } -}; - -static bool usbredir_stream_needed(void *priv) -{ - struct endp_data *endp = priv; - - return endp->max_streams; -} - -static const VMStateDescription usbredir_stream_vmstate = { - .name = "usb-redir-ep/stream-state", - .version_id = 1, - .minimum_version_id = 1, - .needed = usbredir_stream_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT32(max_streams, struct endp_data), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription usbredir_ep_vmstate = { - .name = "usb-redir-ep", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(type, struct endp_data), - VMSTATE_UINT8(interval, struct endp_data), - VMSTATE_UINT8(interface, struct endp_data), - VMSTATE_UINT16(max_packet_size, struct endp_data), - VMSTATE_UINT8(iso_started, struct endp_data), - VMSTATE_UINT8(iso_error, struct endp_data), - VMSTATE_UINT8(interrupt_started, struct endp_data), - VMSTATE_UINT8(interrupt_error, struct endp_data), - VMSTATE_UINT8(bufpq_prefilled, struct endp_data), - VMSTATE_UINT8(bufpq_dropping_packets, struct endp_data), - { - .name = "bufpq", - .version_id = 0, - .field_exists = NULL, - .size = 0, - .info = &usbredir_ep_bufpq_vmstate_info, - .flags = VMS_SINGLE, - .offset = 0, - }, - VMSTATE_INT32(bufpq_target_size, struct endp_data), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &usbredir_bulk_receiving_vmstate, - &usbredir_stream_vmstate, - NULL - } -}; - - -/* For PacketIdQueue migration */ -static void usbredir_put_packet_id_q(QEMUFile *f, void *priv, size_t unused) -{ - struct PacketIdQueue *q = priv; - USBRedirDevice *dev = q->dev; - struct PacketIdQueueEntry *e; - int remain = q->size; - - DPRINTF("put_packet_id_q %s size %d\n", q->name, q->size); - qemu_put_be32(f, q->size); - QTAILQ_FOREACH(e, &q->head, next) { - qemu_put_be64(f, e->id); - remain--; - } - assert(remain == 0); -} - -static int usbredir_get_packet_id_q(QEMUFile *f, void *priv, size_t unused) -{ - struct PacketIdQueue *q = priv; - USBRedirDevice *dev = q->dev; - int i, size; - uint64_t id; - - size = qemu_get_be32(f); - DPRINTF("get_packet_id_q %s size %d\n", q->name, size); - for (i = 0; i < size; i++) { - id = qemu_get_be64(f); - packet_id_queue_add(q, id); - } - assert(q->size == size); - return 0; -} - -static const VMStateInfo usbredir_ep_packet_id_q_vmstate_info = { - .name = "usb-redir-packet-id-q", - .put = usbredir_put_packet_id_q, - .get = usbredir_get_packet_id_q, -}; - -static const VMStateDescription usbredir_ep_packet_id_queue_vmstate = { - .name = "usb-redir-packet-id-queue", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - { - .name = "queue", - .version_id = 0, - .field_exists = NULL, - .size = 0, - .info = &usbredir_ep_packet_id_q_vmstate_info, - .flags = VMS_SINGLE, - .offset = 0, - }, - VMSTATE_END_OF_LIST() - } -}; - - -/* For usb_redir_device_connect_header migration */ -static const VMStateDescription usbredir_device_info_vmstate = { - .name = "usb-redir-device-info", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT8(speed, struct usb_redir_device_connect_header), - VMSTATE_UINT8(device_class, struct usb_redir_device_connect_header), - VMSTATE_UINT8(device_subclass, struct usb_redir_device_connect_header), - VMSTATE_UINT8(device_protocol, struct usb_redir_device_connect_header), - VMSTATE_UINT16(vendor_id, struct usb_redir_device_connect_header), - VMSTATE_UINT16(product_id, struct usb_redir_device_connect_header), - VMSTATE_UINT16(device_version_bcd, - struct usb_redir_device_connect_header), - VMSTATE_END_OF_LIST() - } -}; - - -/* For usb_redir_interface_info_header migration */ -static const VMStateDescription usbredir_interface_info_vmstate = { - .name = "usb-redir-interface-info", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(interface_count, - struct usb_redir_interface_info_header), - VMSTATE_UINT8_ARRAY(interface, - struct usb_redir_interface_info_header, 32), - VMSTATE_UINT8_ARRAY(interface_class, - struct usb_redir_interface_info_header, 32), - VMSTATE_UINT8_ARRAY(interface_subclass, - struct usb_redir_interface_info_header, 32), - VMSTATE_UINT8_ARRAY(interface_protocol, - struct usb_redir_interface_info_header, 32), - VMSTATE_END_OF_LIST() - } -}; - - -/* And finally the USBRedirDevice vmstate itself */ -static const VMStateDescription usbredir_vmstate = { - .name = "usb-redir", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = usbredir_pre_save, - .post_load = usbredir_post_load, - .fields = (VMStateField[]) { - VMSTATE_USB_DEVICE(dev, USBRedirDevice), - VMSTATE_TIMER_PTR(attach_timer, USBRedirDevice), - { - .name = "parser", - .version_id = 0, - .field_exists = NULL, - .size = 0, - .info = &usbredir_parser_vmstate_info, - .flags = VMS_SINGLE, - .offset = 0, - }, - VMSTATE_STRUCT_ARRAY(endpoint, USBRedirDevice, MAX_ENDPOINTS, 1, - usbredir_ep_vmstate, struct endp_data), - VMSTATE_STRUCT(cancelled, USBRedirDevice, 1, - usbredir_ep_packet_id_queue_vmstate, - struct PacketIdQueue), - VMSTATE_STRUCT(already_in_flight, USBRedirDevice, 1, - usbredir_ep_packet_id_queue_vmstate, - struct PacketIdQueue), - VMSTATE_STRUCT(device_info, USBRedirDevice, 1, - usbredir_device_info_vmstate, - struct usb_redir_device_connect_header), - VMSTATE_STRUCT(interface_info, USBRedirDevice, 1, - usbredir_interface_info_vmstate, - struct usb_redir_interface_info_header), - VMSTATE_END_OF_LIST() - } -}; - -static Property usbredir_properties[] = { - DEFINE_PROP_CHR("chardev", USBRedirDevice, cs), - DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, usbredirparser_warning), - DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str), - DEFINE_PROP_END_OF_LIST(), -}; - -static void usbredir_class_initfn(ObjectClass *klass, void *data) -{ - USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - uc->realize = usbredir_realize; - uc->product_desc = "USB Redirection Device"; - uc->handle_destroy = usbredir_handle_destroy; - uc->cancel_packet = usbredir_cancel_packet; - uc->handle_reset = usbredir_handle_reset; - uc->handle_data = usbredir_handle_data; - uc->handle_control = usbredir_handle_control; - uc->flush_ep_queue = usbredir_flush_ep_queue; - uc->ep_stopped = usbredir_ep_stopped; - uc->alloc_streams = usbredir_alloc_streams; - uc->free_streams = usbredir_free_streams; - dc->vmsd = &usbredir_vmstate; - dc->props = usbredir_properties; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static void usbredir_instance_init(Object *obj) -{ - USBDevice *udev = USB_DEVICE(obj); - USBRedirDevice *dev = USB_REDIRECT(udev); - - device_add_bootindex_property(obj, &dev->bootindex, - "bootindex", NULL, - &udev->qdev, NULL); -} - -static const TypeInfo usbredir_dev_info = { - .name = TYPE_USB_REDIR, - .parent = TYPE_USB_DEVICE, - .instance_size = sizeof(USBRedirDevice), - .class_init = usbredir_class_initfn, - .instance_init = usbredir_instance_init, -}; - -static void usbredir_register_types(void) -{ - type_register_static(&usbredir_dev_info); -} - -type_init(usbredir_register_types) diff --git a/qemu/hw/usb/tusb6010.c b/qemu/hw/usb/tusb6010.c deleted file mode 100644 index 8f593a6fd..000000000 --- a/qemu/hw/usb/tusb6010.c +++ /dev/null @@ -1,817 +0,0 @@ -/* - * Texas Instruments TUSB6010 emulation. - * Based on reverse-engineering of a linux driver. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/timer.h" -#include "hw/usb.h" -#include "hw/arm/omap.h" -#include "hw/irq.h" -#include "hw/devices.h" -#include "hw/sysbus.h" - -#define TYPE_TUSB6010 "tusb6010" -#define TUSB(obj) OBJECT_CHECK(TUSBState, (obj), TYPE_TUSB6010) - -typedef struct TUSBState { - SysBusDevice parent_obj; - - MemoryRegion iomem[2]; - qemu_irq irq; - MUSBState *musb; - QEMUTimer *otg_timer; - QEMUTimer *pwr_timer; - - int power; - uint32_t scratch; - uint16_t test_reset; - uint32_t prcm_config; - uint32_t prcm_mngmt; - uint16_t otg_status; - uint32_t dev_config; - int host_mode; - uint32_t intr; - uint32_t intr_ok; - uint32_t mask; - uint32_t usbip_intr; - uint32_t usbip_mask; - uint32_t gpio_intr; - uint32_t gpio_mask; - uint32_t gpio_config; - uint32_t dma_intr; - uint32_t dma_mask; - uint32_t dma_map; - uint32_t dma_config; - uint32_t ep0_config; - uint32_t rx_config[15]; - uint32_t tx_config[15]; - uint32_t wkup_mask; - uint32_t pullup[2]; - uint32_t control_config; - uint32_t otg_timer_val; -} TUSBState; - -#define TUSB_DEVCLOCK 60000000 /* 60 MHz */ - -#define TUSB_VLYNQ_CTRL 0x004 - -/* Mentor Graphics OTG core registers. */ -#define TUSB_BASE_OFFSET 0x400 - -/* FIFO registers, 32-bit. */ -#define TUSB_FIFO_BASE 0x600 - -/* Device System & Control registers, 32-bit. */ -#define TUSB_SYS_REG_BASE 0x800 - -#define TUSB_DEV_CONF (TUSB_SYS_REG_BASE + 0x000) -#define TUSB_DEV_CONF_USB_HOST_MODE (1 << 16) -#define TUSB_DEV_CONF_PROD_TEST_MODE (1 << 15) -#define TUSB_DEV_CONF_SOFT_ID (1 << 1) -#define TUSB_DEV_CONF_ID_SEL (1 << 0) - -#define TUSB_PHY_OTG_CTRL_ENABLE (TUSB_SYS_REG_BASE + 0x004) -#define TUSB_PHY_OTG_CTRL (TUSB_SYS_REG_BASE + 0x008) -#define TUSB_PHY_OTG_CTRL_WRPROTECT (0xa5 << 24) -#define TUSB_PHY_OTG_CTRL_O_ID_PULLUP (1 << 23) -#define TUSB_PHY_OTG_CTRL_O_VBUS_DET_EN (1 << 19) -#define TUSB_PHY_OTG_CTRL_O_SESS_END_EN (1 << 18) -#define TUSB_PHY_OTG_CTRL_TESTM2 (1 << 17) -#define TUSB_PHY_OTG_CTRL_TESTM1 (1 << 16) -#define TUSB_PHY_OTG_CTRL_TESTM0 (1 << 15) -#define TUSB_PHY_OTG_CTRL_TX_DATA2 (1 << 14) -#define TUSB_PHY_OTG_CTRL_TX_GZ2 (1 << 13) -#define TUSB_PHY_OTG_CTRL_TX_ENABLE2 (1 << 12) -#define TUSB_PHY_OTG_CTRL_DM_PULLDOWN (1 << 11) -#define TUSB_PHY_OTG_CTRL_DP_PULLDOWN (1 << 10) -#define TUSB_PHY_OTG_CTRL_OSC_EN (1 << 9) -#define TUSB_PHY_OTG_CTRL_PHYREF_CLK(v) (((v) & 3) << 7) -#define TUSB_PHY_OTG_CTRL_PD (1 << 6) -#define TUSB_PHY_OTG_CTRL_PLL_ON (1 << 5) -#define TUSB_PHY_OTG_CTRL_EXT_RPU (1 << 4) -#define TUSB_PHY_OTG_CTRL_PWR_GOOD (1 << 3) -#define TUSB_PHY_OTG_CTRL_RESET (1 << 2) -#define TUSB_PHY_OTG_CTRL_SUSPENDM (1 << 1) -#define TUSB_PHY_OTG_CTRL_CLK_MODE (1 << 0) - -/* OTG status register */ -#define TUSB_DEV_OTG_STAT (TUSB_SYS_REG_BASE + 0x00c) -#define TUSB_DEV_OTG_STAT_PWR_CLK_GOOD (1 << 8) -#define TUSB_DEV_OTG_STAT_SESS_END (1 << 7) -#define TUSB_DEV_OTG_STAT_SESS_VALID (1 << 6) -#define TUSB_DEV_OTG_STAT_VBUS_VALID (1 << 5) -#define TUSB_DEV_OTG_STAT_VBUS_SENSE (1 << 4) -#define TUSB_DEV_OTG_STAT_ID_STATUS (1 << 3) -#define TUSB_DEV_OTG_STAT_HOST_DISCON (1 << 2) -#define TUSB_DEV_OTG_STAT_LINE_STATE (3 << 0) -#define TUSB_DEV_OTG_STAT_DP_ENABLE (1 << 1) -#define TUSB_DEV_OTG_STAT_DM_ENABLE (1 << 0) - -#define TUSB_DEV_OTG_TIMER (TUSB_SYS_REG_BASE + 0x010) -#define TUSB_DEV_OTG_TIMER_ENABLE (1 << 31) -#define TUSB_DEV_OTG_TIMER_VAL(v) ((v) & 0x07ffffff) -#define TUSB_PRCM_REV (TUSB_SYS_REG_BASE + 0x014) - -/* PRCM configuration register */ -#define TUSB_PRCM_CONF (TUSB_SYS_REG_BASE + 0x018) -#define TUSB_PRCM_CONF_SFW_CPEN (1 << 24) -#define TUSB_PRCM_CONF_SYS_CLKSEL(v) (((v) & 3) << 16) - -/* PRCM management register */ -#define TUSB_PRCM_MNGMT (TUSB_SYS_REG_BASE + 0x01c) -#define TUSB_PRCM_MNGMT_SRP_FIX_TMR(v) (((v) & 0xf) << 25) -#define TUSB_PRCM_MNGMT_SRP_FIX_EN (1 << 24) -#define TUSB_PRCM_MNGMT_VBUS_VAL_TMR(v) (((v) & 0xf) << 20) -#define TUSB_PRCM_MNGMT_VBUS_VAL_FLT_EN (1 << 19) -#define TUSB_PRCM_MNGMT_DFT_CLK_DIS (1 << 18) -#define TUSB_PRCM_MNGMT_VLYNQ_CLK_DIS (1 << 17) -#define TUSB_PRCM_MNGMT_OTG_SESS_END_EN (1 << 10) -#define TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN (1 << 9) -#define TUSB_PRCM_MNGMT_OTG_ID_PULLUP (1 << 8) -#define TUSB_PRCM_MNGMT_15_SW_EN (1 << 4) -#define TUSB_PRCM_MNGMT_33_SW_EN (1 << 3) -#define TUSB_PRCM_MNGMT_5V_CPEN (1 << 2) -#define TUSB_PRCM_MNGMT_PM_IDLE (1 << 1) -#define TUSB_PRCM_MNGMT_DEV_IDLE (1 << 0) - -/* Wake-up source clear and mask registers */ -#define TUSB_PRCM_WAKEUP_SOURCE (TUSB_SYS_REG_BASE + 0x020) -#define TUSB_PRCM_WAKEUP_CLEAR (TUSB_SYS_REG_BASE + 0x028) -#define TUSB_PRCM_WAKEUP_MASK (TUSB_SYS_REG_BASE + 0x02c) -#define TUSB_PRCM_WAKEUP_RESERVED_BITS (0xffffe << 13) -#define TUSB_PRCM_WGPIO_7 (1 << 12) -#define TUSB_PRCM_WGPIO_6 (1 << 11) -#define TUSB_PRCM_WGPIO_5 (1 << 10) -#define TUSB_PRCM_WGPIO_4 (1 << 9) -#define TUSB_PRCM_WGPIO_3 (1 << 8) -#define TUSB_PRCM_WGPIO_2 (1 << 7) -#define TUSB_PRCM_WGPIO_1 (1 << 6) -#define TUSB_PRCM_WGPIO_0 (1 << 5) -#define TUSB_PRCM_WHOSTDISCON (1 << 4) /* Host disconnect */ -#define TUSB_PRCM_WBUS (1 << 3) /* USB bus resume */ -#define TUSB_PRCM_WNORCS (1 << 2) /* NOR chip select */ -#define TUSB_PRCM_WVBUS (1 << 1) /* OTG PHY VBUS */ -#define TUSB_PRCM_WID (1 << 0) /* OTG PHY ID detect */ - -#define TUSB_PULLUP_1_CTRL (TUSB_SYS_REG_BASE + 0x030) -#define TUSB_PULLUP_2_CTRL (TUSB_SYS_REG_BASE + 0x034) -#define TUSB_INT_CTRL_REV (TUSB_SYS_REG_BASE + 0x038) -#define TUSB_INT_CTRL_CONF (TUSB_SYS_REG_BASE + 0x03c) -#define TUSB_USBIP_INT_SRC (TUSB_SYS_REG_BASE + 0x040) -#define TUSB_USBIP_INT_SET (TUSB_SYS_REG_BASE + 0x044) -#define TUSB_USBIP_INT_CLEAR (TUSB_SYS_REG_BASE + 0x048) -#define TUSB_USBIP_INT_MASK (TUSB_SYS_REG_BASE + 0x04c) -#define TUSB_DMA_INT_SRC (TUSB_SYS_REG_BASE + 0x050) -#define TUSB_DMA_INT_SET (TUSB_SYS_REG_BASE + 0x054) -#define TUSB_DMA_INT_CLEAR (TUSB_SYS_REG_BASE + 0x058) -#define TUSB_DMA_INT_MASK (TUSB_SYS_REG_BASE + 0x05c) -#define TUSB_GPIO_INT_SRC (TUSB_SYS_REG_BASE + 0x060) -#define TUSB_GPIO_INT_SET (TUSB_SYS_REG_BASE + 0x064) -#define TUSB_GPIO_INT_CLEAR (TUSB_SYS_REG_BASE + 0x068) -#define TUSB_GPIO_INT_MASK (TUSB_SYS_REG_BASE + 0x06c) - -/* NOR flash interrupt source registers */ -#define TUSB_INT_SRC (TUSB_SYS_REG_BASE + 0x070) -#define TUSB_INT_SRC_SET (TUSB_SYS_REG_BASE + 0x074) -#define TUSB_INT_SRC_CLEAR (TUSB_SYS_REG_BASE + 0x078) -#define TUSB_INT_MASK (TUSB_SYS_REG_BASE + 0x07c) -#define TUSB_INT_SRC_TXRX_DMA_DONE (1 << 24) -#define TUSB_INT_SRC_USB_IP_CORE (1 << 17) -#define TUSB_INT_SRC_OTG_TIMEOUT (1 << 16) -#define TUSB_INT_SRC_VBUS_SENSE_CHNG (1 << 15) -#define TUSB_INT_SRC_ID_STATUS_CHNG (1 << 14) -#define TUSB_INT_SRC_DEV_WAKEUP (1 << 13) -#define TUSB_INT_SRC_DEV_READY (1 << 12) -#define TUSB_INT_SRC_USB_IP_TX (1 << 9) -#define TUSB_INT_SRC_USB_IP_RX (1 << 8) -#define TUSB_INT_SRC_USB_IP_VBUS_ERR (1 << 7) -#define TUSB_INT_SRC_USB_IP_VBUS_REQ (1 << 6) -#define TUSB_INT_SRC_USB_IP_DISCON (1 << 5) -#define TUSB_INT_SRC_USB_IP_CONN (1 << 4) -#define TUSB_INT_SRC_USB_IP_SOF (1 << 3) -#define TUSB_INT_SRC_USB_IP_RST_BABBLE (1 << 2) -#define TUSB_INT_SRC_USB_IP_RESUME (1 << 1) -#define TUSB_INT_SRC_USB_IP_SUSPEND (1 << 0) - -#define TUSB_GPIO_REV (TUSB_SYS_REG_BASE + 0x080) -#define TUSB_GPIO_CONF (TUSB_SYS_REG_BASE + 0x084) -#define TUSB_DMA_CTRL_REV (TUSB_SYS_REG_BASE + 0x100) -#define TUSB_DMA_REQ_CONF (TUSB_SYS_REG_BASE + 0x104) -#define TUSB_EP0_CONF (TUSB_SYS_REG_BASE + 0x108) -#define TUSB_EP_IN_SIZE (TUSB_SYS_REG_BASE + 0x10c) -#define TUSB_DMA_EP_MAP (TUSB_SYS_REG_BASE + 0x148) -#define TUSB_EP_OUT_SIZE (TUSB_SYS_REG_BASE + 0x14c) -#define TUSB_EP_MAX_PACKET_SIZE_OFFSET (TUSB_SYS_REG_BASE + 0x188) -#define TUSB_SCRATCH_PAD (TUSB_SYS_REG_BASE + 0x1c4) -#define TUSB_WAIT_COUNT (TUSB_SYS_REG_BASE + 0x1c8) -#define TUSB_PROD_TEST_RESET (TUSB_SYS_REG_BASE + 0x1d8) - -#define TUSB_DIDR1_LO (TUSB_SYS_REG_BASE + 0x1f8) -#define TUSB_DIDR1_HI (TUSB_SYS_REG_BASE + 0x1fc) - -/* Device System & Control register bitfields */ -#define TUSB_INT_CTRL_CONF_INT_RLCYC(v) (((v) & 0x7) << 18) -#define TUSB_INT_CTRL_CONF_INT_POLARITY (1 << 17) -#define TUSB_INT_CTRL_CONF_INT_MODE (1 << 16) -#define TUSB_GPIO_CONF_DMAREQ(v) (((v) & 0x3f) << 24) -#define TUSB_DMA_REQ_CONF_BURST_SIZE(v) (((v) & 3) << 26) -#define TUSB_DMA_REQ_CONF_DMA_RQ_EN(v) (((v) & 0x3f) << 20) -#define TUSB_DMA_REQ_CONF_DMA_RQ_ASR(v) (((v) & 0xf) << 16) -#define TUSB_EP0_CONFIG_SW_EN (1 << 8) -#define TUSB_EP0_CONFIG_DIR_TX (1 << 7) -#define TUSB_EP0_CONFIG_XFR_SIZE(v) ((v) & 0x7f) -#define TUSB_EP_CONFIG_SW_EN (1 << 31) -#define TUSB_EP_CONFIG_XFR_SIZE(v) ((v) & 0x7fffffff) -#define TUSB_PROD_TEST_RESET_VAL 0xa596 - -static void tusb_intr_update(TUSBState *s) -{ - if (s->control_config & TUSB_INT_CTRL_CONF_INT_POLARITY) - qemu_set_irq(s->irq, s->intr & ~s->mask & s->intr_ok); - else - qemu_set_irq(s->irq, (!(s->intr & ~s->mask)) & s->intr_ok); -} - -static void tusb_usbip_intr_update(TUSBState *s) -{ - /* TX interrupt in the MUSB */ - if (s->usbip_intr & 0x0000ffff & ~s->usbip_mask) - s->intr |= TUSB_INT_SRC_USB_IP_TX; - else - s->intr &= ~TUSB_INT_SRC_USB_IP_TX; - - /* RX interrupt in the MUSB */ - if (s->usbip_intr & 0xffff0000 & ~s->usbip_mask) - s->intr |= TUSB_INT_SRC_USB_IP_RX; - else - s->intr &= ~TUSB_INT_SRC_USB_IP_RX; - - /* XXX: What about TUSB_INT_SRC_USB_IP_CORE? */ - - tusb_intr_update(s); -} - -static void tusb_dma_intr_update(TUSBState *s) -{ - if (s->dma_intr & ~s->dma_mask) - s->intr |= TUSB_INT_SRC_TXRX_DMA_DONE; - else - s->intr &= ~TUSB_INT_SRC_TXRX_DMA_DONE; - - tusb_intr_update(s); -} - -static void tusb_gpio_intr_update(TUSBState *s) -{ - /* TODO: How is this signalled? */ -} - -static uint32_t tusb_async_readb(void *opaque, hwaddr addr) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - return musb_read[0](s->musb, addr & 0x1ff); - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - return musb_read[0](s->musb, 0x20 + ((addr >> 3) & 0x3c)); - } - - printf("%s: unknown register at %03x\n", - __FUNCTION__, (int) (addr & 0xfff)); - return 0; -} - -static uint32_t tusb_async_readh(void *opaque, hwaddr addr) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - return musb_read[1](s->musb, addr & 0x1ff); - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - return musb_read[1](s->musb, 0x20 + ((addr >> 3) & 0x3c)); - } - - printf("%s: unknown register at %03x\n", - __FUNCTION__, (int) (addr & 0xfff)); - return 0; -} - -static uint32_t tusb_async_readw(void *opaque, hwaddr addr) -{ - TUSBState *s = (TUSBState *) opaque; - int offset = addr & 0xfff; - int epnum; - uint32_t ret; - - switch (offset) { - case TUSB_DEV_CONF: - return s->dev_config; - - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - return musb_read[2](s->musb, offset & 0x1ff); - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - return musb_read[2](s->musb, 0x20 + ((addr >> 3) & 0x3c)); - - case TUSB_PHY_OTG_CTRL_ENABLE: - case TUSB_PHY_OTG_CTRL: - return 0x00; /* TODO */ - - case TUSB_DEV_OTG_STAT: - ret = s->otg_status; -#if 0 - if (!(s->prcm_mngmt & TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN)) - ret &= ~TUSB_DEV_OTG_STAT_VBUS_VALID; -#endif - return ret; - case TUSB_DEV_OTG_TIMER: - return s->otg_timer_val; - - case TUSB_PRCM_REV: - return 0x20; - case TUSB_PRCM_CONF: - return s->prcm_config; - case TUSB_PRCM_MNGMT: - return s->prcm_mngmt; - case TUSB_PRCM_WAKEUP_SOURCE: - case TUSB_PRCM_WAKEUP_CLEAR: /* TODO: What does this one return? */ - return 0x00000000; - case TUSB_PRCM_WAKEUP_MASK: - return s->wkup_mask; - - case TUSB_PULLUP_1_CTRL: - return s->pullup[0]; - case TUSB_PULLUP_2_CTRL: - return s->pullup[1]; - - case TUSB_INT_CTRL_REV: - return 0x20; - case TUSB_INT_CTRL_CONF: - return s->control_config; - - case TUSB_USBIP_INT_SRC: - case TUSB_USBIP_INT_SET: /* TODO: What do these two return? */ - case TUSB_USBIP_INT_CLEAR: - return s->usbip_intr; - case TUSB_USBIP_INT_MASK: - return s->usbip_mask; - - case TUSB_DMA_INT_SRC: - case TUSB_DMA_INT_SET: /* TODO: What do these two return? */ - case TUSB_DMA_INT_CLEAR: - return s->dma_intr; - case TUSB_DMA_INT_MASK: - return s->dma_mask; - - case TUSB_GPIO_INT_SRC: /* TODO: What do these two return? */ - case TUSB_GPIO_INT_SET: - case TUSB_GPIO_INT_CLEAR: - return s->gpio_intr; - case TUSB_GPIO_INT_MASK: - return s->gpio_mask; - - case TUSB_INT_SRC: - case TUSB_INT_SRC_SET: /* TODO: What do these two return? */ - case TUSB_INT_SRC_CLEAR: - return s->intr; - case TUSB_INT_MASK: - return s->mask; - - case TUSB_GPIO_REV: - return 0x30; - case TUSB_GPIO_CONF: - return s->gpio_config; - - case TUSB_DMA_CTRL_REV: - return 0x30; - case TUSB_DMA_REQ_CONF: - return s->dma_config; - case TUSB_EP0_CONF: - return s->ep0_config; - case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b): - epnum = (offset - TUSB_EP_IN_SIZE) >> 2; - return s->tx_config[epnum]; - case TUSB_DMA_EP_MAP: - return s->dma_map; - case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b): - epnum = (offset - TUSB_EP_OUT_SIZE) >> 2; - return s->rx_config[epnum]; - case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... - (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): - return 0x00000000; /* TODO */ - case TUSB_WAIT_COUNT: - return 0x00; /* TODO */ - - case TUSB_SCRATCH_PAD: - return s->scratch; - - case TUSB_PROD_TEST_RESET: - return s->test_reset; - - /* DIE IDs */ - case TUSB_DIDR1_LO: - return 0xa9453c59; - case TUSB_DIDR1_HI: - return 0x54059adf; - } - - printf("%s: unknown register at %03x\n", __FUNCTION__, offset); - return 0; -} - -static void tusb_async_writeb(void *opaque, hwaddr addr, - uint32_t value) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - musb_write[0](s->musb, addr & 0x1ff, value); - break; - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - musb_write[0](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); - break; - - default: - printf("%s: unknown register at %03x\n", - __FUNCTION__, (int) (addr & 0xfff)); - return; - } -} - -static void tusb_async_writeh(void *opaque, hwaddr addr, - uint32_t value) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - musb_write[1](s->musb, addr & 0x1ff, value); - break; - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - musb_write[1](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); - break; - - default: - printf("%s: unknown register at %03x\n", - __FUNCTION__, (int) (addr & 0xfff)); - return; - } -} - -static void tusb_async_writew(void *opaque, hwaddr addr, - uint32_t value) -{ - TUSBState *s = (TUSBState *) opaque; - int offset = addr & 0xfff; - int epnum; - - switch (offset) { - case TUSB_VLYNQ_CTRL: - break; - - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - musb_write[2](s->musb, offset & 0x1ff, value); - break; - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - musb_write[2](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); - break; - - case TUSB_DEV_CONF: - s->dev_config = value; - s->host_mode = (value & TUSB_DEV_CONF_USB_HOST_MODE); - if (value & TUSB_DEV_CONF_PROD_TEST_MODE) - hw_error("%s: Product Test mode not allowed\n", __FUNCTION__); - break; - - case TUSB_PHY_OTG_CTRL_ENABLE: - case TUSB_PHY_OTG_CTRL: - return; /* TODO */ - case TUSB_DEV_OTG_TIMER: - s->otg_timer_val = value; - 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), - NANOSECONDS_PER_SECOND, TUSB_DEVCLOCK)); - else - timer_del(s->otg_timer); - break; - - case TUSB_PRCM_CONF: - s->prcm_config = value; - break; - case TUSB_PRCM_MNGMT: - s->prcm_mngmt = value; - break; - case TUSB_PRCM_WAKEUP_CLEAR: - break; - case TUSB_PRCM_WAKEUP_MASK: - s->wkup_mask = value; - break; - - case TUSB_PULLUP_1_CTRL: - s->pullup[0] = value; - break; - case TUSB_PULLUP_2_CTRL: - s->pullup[1] = value; - break; - case TUSB_INT_CTRL_CONF: - s->control_config = value; - tusb_intr_update(s); - break; - - case TUSB_USBIP_INT_SET: - s->usbip_intr |= value; - tusb_usbip_intr_update(s); - break; - case TUSB_USBIP_INT_CLEAR: - s->usbip_intr &= ~value; - tusb_usbip_intr_update(s); - musb_core_intr_clear(s->musb, ~value); - break; - case TUSB_USBIP_INT_MASK: - s->usbip_mask = value; - tusb_usbip_intr_update(s); - break; - - case TUSB_DMA_INT_SET: - s->dma_intr |= value; - tusb_dma_intr_update(s); - break; - case TUSB_DMA_INT_CLEAR: - s->dma_intr &= ~value; - tusb_dma_intr_update(s); - break; - case TUSB_DMA_INT_MASK: - s->dma_mask = value; - tusb_dma_intr_update(s); - break; - - case TUSB_GPIO_INT_SET: - s->gpio_intr |= value; - tusb_gpio_intr_update(s); - break; - case TUSB_GPIO_INT_CLEAR: - s->gpio_intr &= ~value; - tusb_gpio_intr_update(s); - break; - case TUSB_GPIO_INT_MASK: - s->gpio_mask = value; - tusb_gpio_intr_update(s); - break; - - case TUSB_INT_SRC_SET: - s->intr |= value; - tusb_intr_update(s); - break; - case TUSB_INT_SRC_CLEAR: - s->intr &= ~value; - tusb_intr_update(s); - break; - case TUSB_INT_MASK: - s->mask = value; - tusb_intr_update(s); - break; - - case TUSB_GPIO_CONF: - s->gpio_config = value; - break; - case TUSB_DMA_REQ_CONF: - s->dma_config = value; - break; - case TUSB_EP0_CONF: - s->ep0_config = value & 0x1ff; - musb_set_size(s->musb, 0, TUSB_EP0_CONFIG_XFR_SIZE(value), - value & TUSB_EP0_CONFIG_DIR_TX); - break; - case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b): - epnum = (offset - TUSB_EP_IN_SIZE) >> 2; - s->tx_config[epnum] = value; - musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 1); - break; - case TUSB_DMA_EP_MAP: - s->dma_map = value; - break; - case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b): - epnum = (offset - TUSB_EP_OUT_SIZE) >> 2; - s->rx_config[epnum] = value; - musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 0); - break; - case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... - (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): - return; /* TODO */ - case TUSB_WAIT_COUNT: - return; /* TODO */ - - case TUSB_SCRATCH_PAD: - s->scratch = value; - break; - - case TUSB_PROD_TEST_RESET: - s->test_reset = value; - break; - - default: - printf("%s: unknown register at %03x\n", __FUNCTION__, offset); - return; - } -} - -static const MemoryRegionOps tusb_async_ops = { - .old_mmio = { - .read = { tusb_async_readb, tusb_async_readh, tusb_async_readw, }, - .write = { tusb_async_writeb, tusb_async_writeh, tusb_async_writew, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void tusb_otg_tick(void *opaque) -{ - TUSBState *s = (TUSBState *) opaque; - - s->otg_timer_val = 0; - s->intr |= TUSB_INT_SRC_OTG_TIMEOUT; - tusb_intr_update(s); -} - -static void tusb_power_tick(void *opaque) -{ - TUSBState *s = (TUSBState *) opaque; - - if (s->power) { - s->intr_ok = ~0; - tusb_intr_update(s); - } -} - -static void tusb_musb_core_intr(void *opaque, int source, int level) -{ - TUSBState *s = (TUSBState *) opaque; - uint16_t otg_status = s->otg_status; - - switch (source) { - case musb_set_vbus: - if (level) - otg_status |= TUSB_DEV_OTG_STAT_VBUS_VALID; - else - otg_status &= ~TUSB_DEV_OTG_STAT_VBUS_VALID; - - /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_VBUS_DET_EN set? */ - /* XXX: only if TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN set? */ - if (s->otg_status != otg_status) { - s->otg_status = otg_status; - s->intr |= TUSB_INT_SRC_VBUS_SENSE_CHNG; - tusb_intr_update(s); - } - break; - - case musb_set_session: - /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_SESS_END_EN set? */ - /* XXX: only if TUSB_PRCM_MNGMT_OTG_SESS_END_EN set? */ - if (level) { - s->otg_status |= TUSB_DEV_OTG_STAT_SESS_VALID; - s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_END; - } else { - s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_VALID; - s->otg_status |= TUSB_DEV_OTG_STAT_SESS_END; - } - - /* XXX: some IRQ or anything? */ - break; - - case musb_irq_tx: - case musb_irq_rx: - s->usbip_intr = musb_core_intr_get(s->musb); - /* Fall through. */ - default: - if (level) - s->intr |= 1 << source; - else - s->intr &= ~(1 << source); - tusb_intr_update(s); - break; - } -} - -static void tusb6010_power(TUSBState *s, int on) -{ - if (!on) { - s->power = 0; - } else if (!s->power && on) { - s->power = 1; - /* 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) + - NANOSECONDS_PER_SECOND / 2); - } -} - -static void tusb6010_irq(void *opaque, int source, int level) -{ - if (source) { - tusb_musb_core_intr(opaque, source - 1, level); - } else { - tusb6010_power(opaque, level); - } -} - -static void tusb6010_reset(DeviceState *dev) -{ - TUSBState *s = TUSB(dev); - int i; - - s->test_reset = TUSB_PROD_TEST_RESET_VAL; - s->host_mode = 0; - s->dev_config = 0; - s->otg_status = 0; /* !TUSB_DEV_OTG_STAT_ID_STATUS means host mode */ - s->power = 0; - s->mask = 0xffffffff; - s->intr = 0x00000000; - s->otg_timer_val = 0; - s->scratch = 0; - s->prcm_config = 0; - s->prcm_mngmt = 0; - s->intr_ok = 0; - s->usbip_intr = 0; - s->usbip_mask = 0; - s->gpio_intr = 0; - s->gpio_mask = 0; - s->gpio_config = 0; - s->dma_intr = 0; - s->dma_mask = 0; - s->dma_map = 0; - s->dma_config = 0; - s->ep0_config = 0; - s->wkup_mask = 0; - s->pullup[0] = s->pullup[1] = 0; - s->control_config = 0; - for (i = 0; i < 15; i++) { - s->rx_config[i] = s->tx_config[i] = 0; - } - musb_reset(s->musb); -} - -static int tusb6010_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - TUSBState *s = TUSB(dev); - - s->otg_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tusb_otg_tick, s); - s->pwr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tusb_power_tick, s); - memory_region_init_io(&s->iomem[1], OBJECT(s), &tusb_async_ops, s, - "tusb-async", UINT32_MAX); - sysbus_init_mmio(sbd, &s->iomem[0]); - sysbus_init_mmio(sbd, &s->iomem[1]); - sysbus_init_irq(sbd, &s->irq); - qdev_init_gpio_in(dev, tusb6010_irq, musb_irq_max + 1); - s->musb = musb_init(dev, 1); - return 0; -} - -static void tusb6010_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = tusb6010_init; - dc->reset = tusb6010_reset; -} - -static const TypeInfo tusb6010_info = { - .name = TYPE_TUSB6010, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(TUSBState), - .class_init = tusb6010_class_init, -}; - -static void tusb6010_register_types(void) -{ - type_register_static(&tusb6010_info); -} - -type_init(tusb6010_register_types) diff --git a/qemu/hw/vfio/Makefile.objs b/qemu/hw/vfio/Makefile.objs deleted file mode 100644 index ceddbb8f9..000000000 --- a/qemu/hw/vfio/Makefile.objs +++ /dev/null @@ -1,7 +0,0 @@ -ifeq ($(CONFIG_LINUX), y) -obj-$(CONFIG_SOFTMMU) += common.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 deleted file mode 100644 index 2c60310cf..000000000 --- a/qemu/hw/vfio/amd-xgbe.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * AMD XGBE VFIO device - * - * Copyright Linaro Limited, 2015 - * - * Authors: - * Eric Auger - * - * 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 deleted file mode 100644 index bb15d588e..000000000 --- a/qemu/hw/vfio/calxeda-xgmac.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * calxeda xgmac VFIO device - * - * Copyright Linaro Limited, 2014 - * - * Authors: - * Eric Auger - * - * 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-calxeda-xgmac.h" - -static void calxeda_xgmac_realize(DeviceState *dev, Error **errp) -{ - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev); - VFIOCalxedaXgmacDeviceClass *k = VFIO_CALXEDA_XGMAC_DEVICE_GET_CLASS(dev); - - vdev->compat = g_strdup("calxeda,hb-xgmac"); - - k->parent_realize(dev, errp); -} - -static const VMStateDescription vfio_platform_calxeda_xgmac_vmstate = { - .name = TYPE_VFIO_CALXEDA_XGMAC, - .unmigratable = 1, -}; - -static void vfio_calxeda_xgmac_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VFIOCalxedaXgmacDeviceClass *vcxc = - VFIO_CALXEDA_XGMAC_DEVICE_CLASS(klass); - vcxc->parent_realize = dc->realize; - dc->realize = calxeda_xgmac_realize; - dc->desc = "VFIO Calxeda XGMAC"; - dc->vmsd = &vfio_platform_calxeda_xgmac_vmstate; -} - -static const TypeInfo vfio_calxeda_xgmac_dev_info = { - .name = TYPE_VFIO_CALXEDA_XGMAC, - .parent = TYPE_VFIO_PLATFORM, - .instance_size = sizeof(VFIOCalxedaXgmacDevice), - .class_init = vfio_calxeda_xgmac_class_init, - .class_size = sizeof(VFIOCalxedaXgmacDeviceClass), -}; - -static void register_calxeda_xgmac_dev_type(void) -{ - type_register_static(&vfio_calxeda_xgmac_dev_info); -} - -type_init(register_calxeda_xgmac_dev_type) diff --git a/qemu/hw/vfio/common.c b/qemu/hw/vfio/common.c deleted file mode 100644 index f27db36fb..000000000 --- a/qemu/hw/vfio/common.c +++ /dev/null @@ -1,1192 +0,0 @@ -/* - * generic functions used by VFIO devices - * - * Copyright Red Hat, Inc. 2012 - * - * Authors: - * Alex Williamson - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Based on qemu-kvm device-assignment: - * Adapted for KVM by Qumranet. - * Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com) - * Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com) - * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com) - * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com) - * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com) - */ - -#include "qemu/osdep.h" -#include -#include -#include - -#include "hw/vfio/vfio-common.h" -#include "hw/vfio/vfio.h" -#include "exec/address-spaces.h" -#include "exec/memory.h" -#include "hw/hw.h" -#include "qemu/error-report.h" -#include "sysemu/kvm.h" -#include "trace.h" - -struct vfio_group_head vfio_group_list = - QLIST_HEAD_INITIALIZER(vfio_group_list); -struct vfio_as_head vfio_address_spaces = - QLIST_HEAD_INITIALIZER(vfio_address_spaces); - -#ifdef CONFIG_KVM -/* - * We have a single VFIO pseudo device per KVM VM. Once created it lives - * for the life of the VM. Closing the file descriptor only drops our - * reference to it and the device's reference to kvm. Therefore once - * initialized, this file descriptor is only released on QEMU exit and - * we'll re-use it should another vfio device be attached before then. - */ -static int vfio_kvm_device_fd = -1; -#endif - -/* - * Common VFIO interrupt disable - */ -void vfio_disable_irqindex(VFIODevice *vbasedev, int index) -{ - struct vfio_irq_set irq_set = { - .argsz = sizeof(irq_set), - .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER, - .index = index, - .start = 0, - .count = 0, - }; - - ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); -} - -void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index) -{ - struct vfio_irq_set irq_set = { - .argsz = sizeof(irq_set), - .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK, - .index = index, - .start = 0, - .count = 1, - }; - - ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); -} - -void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index) -{ - struct vfio_irq_set irq_set = { - .argsz = sizeof(irq_set), - .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_MASK, - .index = index, - .start = 0, - .count = 1, - }; - - ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); -} - -/* - * IO Port/MMIO - Beware of the endians, VFIO is always little endian - */ -void vfio_region_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VFIORegion *region = opaque; - VFIODevice *vbasedev = region->vbasedev; - union { - uint8_t byte; - uint16_t word; - uint32_t dword; - uint64_t qword; - } buf; - - switch (size) { - case 1: - buf.byte = data; - break; - case 2: - buf.word = cpu_to_le16(data); - break; - case 4: - buf.dword = cpu_to_le32(data); - break; - default: - hw_error("vfio: unsupported write size, %d bytes", size); - break; - } - - if (pwrite(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) { - error_report("%s(%s:region%d+0x%"HWADDR_PRIx", 0x%"PRIx64 - ",%d) failed: %m", - __func__, vbasedev->name, region->nr, - addr, data, size); - } - - trace_vfio_region_write(vbasedev->name, region->nr, addr, data, size); - - /* - * A read or write to a BAR always signals an INTx EOI. This will - * do nothing if not pending (including not in INTx mode). We assume - * that a BAR access is in response to an interrupt and that BAR - * accesses will service the interrupt. Unfortunately, we don't know - * which access will service the interrupt, so we're potentially - * getting quite a few host interrupts per guest interrupt. - */ - vbasedev->ops->vfio_eoi(vbasedev); -} - -uint64_t vfio_region_read(void *opaque, - hwaddr addr, unsigned size) -{ - VFIORegion *region = opaque; - VFIODevice *vbasedev = region->vbasedev; - union { - uint8_t byte; - uint16_t word; - uint32_t dword; - uint64_t qword; - } buf; - uint64_t data = 0; - - if (pread(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) { - error_report("%s(%s:region%d+0x%"HWADDR_PRIx", %d) failed: %m", - __func__, vbasedev->name, region->nr, - addr, size); - return (uint64_t)-1; - } - switch (size) { - case 1: - data = buf.byte; - break; - case 2: - data = le16_to_cpu(buf.word); - break; - case 4: - data = le32_to_cpu(buf.dword); - break; - default: - hw_error("vfio: unsupported read size, %d bytes", size); - break; - } - - trace_vfio_region_read(vbasedev->name, region->nr, addr, size, data); - - /* Same as write above */ - vbasedev->ops->vfio_eoi(vbasedev); - - return data; -} - -const MemoryRegionOps vfio_region_ops = { - .read = vfio_region_read, - .write = vfio_region_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -/* - * DMA - Mapping and unmapping for the "type1" IOMMU interface used on x86 - */ -static int vfio_dma_unmap(VFIOContainer *container, - hwaddr iova, ram_addr_t size) -{ - struct vfio_iommu_type1_dma_unmap unmap = { - .argsz = sizeof(unmap), - .flags = 0, - .iova = iova, - .size = size, - }; - - if (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) { - error_report("VFIO_UNMAP_DMA: %d", -errno); - return -errno; - } - - return 0; -} - -static int vfio_dma_map(VFIOContainer *container, hwaddr iova, - ram_addr_t size, void *vaddr, bool readonly) -{ - struct vfio_iommu_type1_dma_map map = { - .argsz = sizeof(map), - .flags = VFIO_DMA_MAP_FLAG_READ, - .vaddr = (__u64)(uintptr_t)vaddr, - .iova = iova, - .size = size, - }; - - if (!readonly) { - map.flags |= VFIO_DMA_MAP_FLAG_WRITE; - } - - /* - * 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 VGA ROM space. - */ - if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map) == 0 || - (errno == EBUSY && vfio_dma_unmap(container, iova, size) == 0 && - ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map) == 0)) { - return 0; - } - - error_report("VFIO_MAP_DMA: %d", -errno); - return -errno; -} - -static bool vfio_listener_skipped_section(MemoryRegionSection *section) -{ - return (!memory_region_is_ram(section->mr) && - !memory_region_is_iommu(section->mr)) || - /* - * Sizing an enabled 64-bit BAR can cause spurious mappings to - * addresses in the upper part of the 64-bit address space. These - * are never accessed by the CPU and beyond the address width of - * some IOMMU hardware. TODO: VFIO should tell us the IOMMU width. - */ - section->offset_within_address_space & (1ULL << 63); -} - -static void vfio_iommu_map_notify(Notifier *n, void *data) -{ - VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n); - VFIOContainer *container = giommu->container; - IOMMUTLBEntry *iotlb = data; - MemoryRegion *mr; - hwaddr xlat; - hwaddr len = iotlb->addr_mask + 1; - void *vaddr; - int ret; - - trace_vfio_iommu_map_notify(iotlb->iova, - iotlb->iova + iotlb->addr_mask); - - /* - * The IOMMU TLB entry we have just covers translation through - * this IOMMU to its immediate target. We need to translate - * it the rest of the way through to memory. - */ - rcu_read_lock(); - mr = address_space_translate(&address_space_memory, - iotlb->translated_addr, - &xlat, &len, iotlb->perm & IOMMU_WO); - if (!memory_region_is_ram(mr)) { - error_report("iommu map to non memory area %"HWADDR_PRIx"", - xlat); - goto out; - } - /* - * Translation truncates length to the IOMMU page size, - * check that it did not truncate too much. - */ - if (len & iotlb->addr_mask) { - error_report("iommu has granularity incompatible with target AS"); - goto out; - } - - if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) { - vaddr = memory_region_get_ram_ptr(mr) + xlat; - ret = vfio_dma_map(container, iotlb->iova, - iotlb->addr_mask + 1, vaddr, - !(iotlb->perm & IOMMU_WO) || mr->readonly); - if (ret) { - error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx", %p) = %d (%m)", - container, iotlb->iova, - iotlb->addr_mask + 1, vaddr, ret); - } - } else { - ret = vfio_dma_unmap(container, iotlb->iova, iotlb->addr_mask + 1); - if (ret) { - error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx") = %d (%m)", - container, iotlb->iova, - iotlb->addr_mask + 1, ret); - } - } -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, listener); - hwaddr iova, end; - Int128 llend, llsize; - void *vaddr; - int ret; - - if (vfio_listener_skipped_section(section)) { - trace_vfio_listener_region_add_skip( - section->offset_within_address_space, - section->offset_within_address_space + - int128_get64(int128_sub(section->size, int128_one()))); - return; - } - - if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) != - (section->offset_within_region & ~TARGET_PAGE_MASK))) { - error_report("%s received unaligned region", __func__); - return; - } - - iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); - llend = int128_make64(section->offset_within_address_space); - llend = int128_add(llend, section->size); - llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK)); - - 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, end); - /* - * FIXME: We should do some checking to see if the - * capabilities of the host VFIO IOMMU are adequate to model - * the guest IOMMU - * - * FIXME: For VFIO iommu types which have KVM acceleration to - * avoid bouncing all map/unmaps through qemu this way, this - * would be the right place to wire that up (tell the KVM - * device emulation the VFIO iommu handles to use). - */ - 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 */ - - 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, vaddr); - - llsize = int128_sub(llend, int128_make64(iova)); - - 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, int128_get64(llsize), vaddr, ret); - goto fail; - } - - 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, listener); - hwaddr iova, end; - int ret; - - if (vfio_listener_skipped_section(section)) { - trace_vfio_listener_region_del_skip( - section->offset_within_address_space, - section->offset_within_address_space + - int128_get64(int128_sub(section->size, int128_one()))); - return; - } - - if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) != - (section->offset_within_region & ~TARGET_PAGE_MASK))) { - error_report("%s received unaligned region", __func__); - return; - } - - if (memory_region_is_iommu(section->mr)) { - VFIOGuestIOMMU *giommu; - - QLIST_FOREACH(giommu, &container->giommu_list, giommu_next) { - if (giommu->iommu == section->mr) { - memory_region_unregister_iommu_notifier(&giommu->n); - QLIST_REMOVE(giommu, giommu_next); - g_free(giommu); - break; - } - } - - /* - * FIXME: We assume the one big unmap below is adequate to - * remove any individual page mappings in the IOMMU which - * might have been copied into VFIO. This works for a page table - * based IOMMU where a big unmap flattens a large range of IO-PTEs. - * That may not be true for all IOMMU types. - */ - } - - iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); - end = (section->offset_within_address_space + int128_get64(section->size)) & - TARGET_PAGE_MASK; - - if (iova >= end) { - return; - } - - trace_vfio_listener_region_del(iova, end - 1); - - ret = vfio_dma_unmap(container, iova, end - iova); - memory_region_unref(section->mr); - if (ret) { - error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx") = %d (%m)", - container, iova, end - iova, ret); - } -} - -static const MemoryListener vfio_memory_listener = { - .region_add = vfio_listener_region_add, - .region_del = vfio_listener_region_del, -}; - -static void vfio_listener_release(VFIOContainer *container) -{ - memory_listener_unregister(&container->listener); -} - -int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, - int index, const char *name) -{ - struct vfio_region_info *info; - int ret; - - ret = vfio_get_region_info(vbasedev, index, &info); - if (ret) { - return ret; - } - - region->vbasedev = vbasedev; - region->flags = info->flags; - region->size = info->size; - region->fd_offset = info->offset; - region->nr = index; - - if (region->size) { - region->mem = g_new0(MemoryRegion, 1); - memory_region_init_io(region->mem, obj, &vfio_region_ops, - region, name, region->size); - - if (!vbasedev->no_mmap && - region->flags & VFIO_REGION_INFO_FLAG_MMAP && - !(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; - } - - 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, ®ion->mmaps[i].mem); - munmap(region->mmaps[i].mmap, region->mmaps[i].size); - object_unparent(OBJECT(®ion->mmaps[i].mem)); - region->mmaps[i].mmap = NULL; - } - - return ret; - } - - name = g_strdup_printf("%s mmaps[%d]", - memory_region_name(region->mem), i); - memory_region_init_ram_ptr(®ion->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(®ion->mmaps[i].mem); - memory_region_add_subregion(region->mem, region->mmaps[i].offset, - ®ion->mmaps[i].mem); - - trace_vfio_region_mmap(memory_region_name(®ion->mmaps[i].mem), - region->mmaps[i].offset, - region->mmaps[i].offset + - region->mmaps[i].size - 1); - } - - return 0; -} - -void vfio_region_exit(VFIORegion *region) -{ - int i; - - if (!region->mem) { - return; - } - - for (i = 0; i < region->nr_mmaps; i++) { - if (region->mmaps[i].mmap) { - memory_region_del_subregion(region->mem, ®ion->mmaps[i].mem); - } - } - - trace_vfio_region_exit(region->vbasedev->name, region->nr); -} - -void vfio_region_finalize(VFIORegion *region) -{ - int i; - - if (!region->mem) { - return; - } - - for (i = 0; i < region->nr_mmaps; i++) { - if (region->mmaps[i].mmap) { - munmap(region->mmaps[i].mmap, region->mmaps[i].size); - object_unparent(OBJECT(®ion->mmaps[i].mem)); - } - } - - object_unparent(OBJECT(region->mem)); - - g_free(region->mem); - g_free(region->mmaps); - - trace_vfio_region_finalize(region->vbasedev->name, region->nr); -} - -void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled) -{ - int i; - - if (!region->mem) { - return; - } - - for (i = 0; i < region->nr_mmaps; i++) { - if (region->mmaps[i].mmap) { - memory_region_set_enabled(®ion->mmaps[i].mem, enabled); - } - } - - trace_vfio_region_mmaps_set_enabled(memory_region_name(region->mem), - enabled); -} - -void vfio_reset_handler(void *opaque) -{ - VFIOGroup *group; - VFIODevice *vbasedev; - - QLIST_FOREACH(group, &vfio_group_list, next) { - QLIST_FOREACH(vbasedev, &group->device_list, next) { - vbasedev->ops->vfio_compute_needs_reset(vbasedev); - } - } - - QLIST_FOREACH(group, &vfio_group_list, next) { - QLIST_FOREACH(vbasedev, &group->device_list, next) { - if (vbasedev->needs_reset) { - vbasedev->ops->vfio_hot_reset_multi(vbasedev); - } - } - } -} - -static void vfio_kvm_device_add_group(VFIOGroup *group) -{ -#ifdef CONFIG_KVM - struct kvm_device_attr attr = { - .group = KVM_DEV_VFIO_GROUP, - .attr = KVM_DEV_VFIO_GROUP_ADD, - .addr = (uint64_t)(unsigned long)&group->fd, - }; - - if (!kvm_enabled()) { - return; - } - - if (vfio_kvm_device_fd < 0) { - struct kvm_create_device cd = { - .type = KVM_DEV_TYPE_VFIO, - }; - - if (kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd)) { - error_report("Failed to create KVM VFIO device: %m"); - return; - } - - vfio_kvm_device_fd = cd.fd; - } - - if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr)) { - error_report("Failed to add group %d to KVM VFIO device: %m", - group->groupid); - } -#endif -} - -static void vfio_kvm_device_del_group(VFIOGroup *group) -{ -#ifdef CONFIG_KVM - struct kvm_device_attr attr = { - .group = KVM_DEV_VFIO_GROUP, - .attr = KVM_DEV_VFIO_GROUP_DEL, - .addr = (uint64_t)(unsigned long)&group->fd, - }; - - if (vfio_kvm_device_fd < 0) { - return; - } - - if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr)) { - error_report("Failed to remove group %d from KVM VFIO device: %m", - group->groupid); - } -#endif -} - -static VFIOAddressSpace *vfio_get_address_space(AddressSpace *as) -{ - VFIOAddressSpace *space; - - QLIST_FOREACH(space, &vfio_address_spaces, list) { - if (space->as == as) { - return space; - } - } - - /* No suitable VFIOAddressSpace, create a new one */ - space = g_malloc0(sizeof(*space)); - space->as = as; - QLIST_INIT(&space->containers); - - QLIST_INSERT_HEAD(&vfio_address_spaces, space, list); - - return space; -} - -static void vfio_put_address_space(VFIOAddressSpace *space) -{ - if (QLIST_EMPTY(&space->containers)) { - QLIST_REMOVE(space, list); - g_free(space); - } -} - -static int vfio_connect_container(VFIOGroup *group, AddressSpace *as) -{ - VFIOContainer *container; - int ret, fd; - VFIOAddressSpace *space; - - space = vfio_get_address_space(as); - - QLIST_FOREACH(container, &space->containers, next) { - if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) { - group->container = container; - QLIST_INSERT_HEAD(&container->group_list, group, container_next); - return 0; - } - } - - fd = qemu_open("/dev/vfio/vfio", O_RDWR); - if (fd < 0) { - error_report("vfio: failed to open /dev/vfio/vfio: %m"); - ret = -errno; - goto put_space_exit; - } - - ret = ioctl(fd, VFIO_GET_API_VERSION); - if (ret != VFIO_API_VERSION) { - error_report("vfio: supported vfio version: %d, " - "reported version: %d", VFIO_API_VERSION, ret); - ret = -EINVAL; - goto close_fd_exit; - } - - container = g_malloc0(sizeof(*container)); - container->space = space; - container->fd = fd; - 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) { - error_report("vfio: failed to set group container: %m"); - ret = -errno; - goto free_container_exit; - } - - ret = ioctl(fd, VFIO_SET_IOMMU, - v2 ? VFIO_TYPE1v2_IOMMU : VFIO_TYPE1_IOMMU); - if (ret) { - error_report("vfio: failed to set iommu for container: %m"); - ret = -errno; - goto free_container_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; - } - } 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"); - ret = -errno; - goto free_container_exit; - } - ret = ioctl(fd, VFIO_SET_IOMMU, VFIO_SPAPR_TCE_IOMMU); - if (ret) { - error_report("vfio: failed to set iommu for container: %m"); - ret = -errno; - goto free_container_exit; - } - - /* - * The host kernel code implementing VFIO_IOMMU_DISABLE is called - * when container fd is closed so we do not call it explicitly - * in this file. - */ - ret = ioctl(fd, VFIO_IOMMU_ENABLE); - if (ret) { - error_report("vfio: failed to enable container: %m"); - ret = -errno; - goto free_container_exit; - } - - /* - * 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); - - group->container = container; - QLIST_INSERT_HEAD(&container->group_list, group, container_next); - - return 0; -listener_release_exit: - vfio_listener_release(container); - -free_container_exit: - g_free(container); - -close_fd_exit: - close(fd); - -put_space_exit: - vfio_put_address_space(space); - - return ret; -} - -static void vfio_disconnect_container(VFIOGroup *group) -{ - VFIOContainer *container = group->container; - - if (ioctl(group->fd, VFIO_GROUP_UNSET_CONTAINER, &container->fd)) { - error_report("vfio: error disconnecting group %d from container", - group->groupid); - } - - QLIST_REMOVE(group, container_next); - group->container = NULL; - - if (QLIST_EMPTY(&container->group_list)) { - VFIOAddressSpace *space = container->space; - VFIOGuestIOMMU *giommu, *tmp; - - vfio_listener_release(container); - QLIST_REMOVE(container, next); - - QLIST_FOREACH_SAFE(giommu, &container->giommu_list, giommu_next, tmp) { - memory_region_unregister_iommu_notifier(&giommu->n); - QLIST_REMOVE(giommu, giommu_next); - g_free(giommu); - } - - trace_vfio_disconnect_container(container->fd); - close(container->fd); - g_free(container); - - vfio_put_address_space(space); - } -} - -VFIOGroup *vfio_get_group(int groupid, AddressSpace *as) -{ - VFIOGroup *group; - char path[32]; - struct vfio_group_status status = { .argsz = sizeof(status) }; - - QLIST_FOREACH(group, &vfio_group_list, next) { - if (group->groupid == groupid) { - /* Found it. Now is it already in the right context? */ - if (group->container->space->as == as) { - return group; - } else { - error_report("vfio: group %d used in multiple address spaces", - group->groupid); - return NULL; - } - } - } - - group = g_malloc0(sizeof(*group)); - - snprintf(path, sizeof(path), "/dev/vfio/%d", groupid); - group->fd = qemu_open(path, O_RDWR); - if (group->fd < 0) { - error_report("vfio: error opening %s: %m", path); - goto free_group_exit; - } - - if (ioctl(group->fd, VFIO_GROUP_GET_STATUS, &status)) { - error_report("vfio: error getting group status: %m"); - goto close_fd_exit; - } - - if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) { - error_report("vfio: error, group %d is not viable, please ensure " - "all devices within the iommu_group are bound to their " - "vfio bus driver.", groupid); - goto close_fd_exit; - } - - group->groupid = groupid; - QLIST_INIT(&group->device_list); - - if (vfio_connect_container(group, as)) { - error_report("vfio: failed to setup container for group %d", groupid); - goto close_fd_exit; - } - - if (QLIST_EMPTY(&vfio_group_list)) { - qemu_register_reset(vfio_reset_handler, NULL); - } - - QLIST_INSERT_HEAD(&vfio_group_list, group, next); - - vfio_kvm_device_add_group(group); - - return group; - -close_fd_exit: - close(group->fd); - -free_group_exit: - g_free(group); - - return NULL; -} - -void vfio_put_group(VFIOGroup *group) -{ - if (!group || !QLIST_EMPTY(&group->device_list)) { - return; - } - - vfio_kvm_device_del_group(group); - vfio_disconnect_container(group); - QLIST_REMOVE(group, next); - trace_vfio_put_group(group->fd); - close(group->fd); - g_free(group); - - if (QLIST_EMPTY(&vfio_group_list)) { - qemu_unregister_reset(vfio_reset_handler, NULL); - } -} - -int vfio_get_device(VFIOGroup *group, const char *name, - VFIODevice *vbasedev) -{ - struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) }; - int ret, fd; - - fd = ioctl(group->fd, VFIO_GROUP_GET_DEVICE_FD, name); - if (fd < 0) { - error_report("vfio: error getting device %s from group %d: %m", - name, group->groupid); - error_printf("Verify all devices in group %d are bound to vfio- " - "or pci-stub and not already in use\n", group->groupid); - return fd; - } - - ret = ioctl(fd, VFIO_DEVICE_GET_INFO, &dev_info); - if (ret) { - error_report("vfio: error getting device info: %m"); - close(fd); - return ret; - } - - vbasedev->fd = fd; - vbasedev->group = group; - QLIST_INSERT_HEAD(&group->device_list, vbasedev, next); - - vbasedev->num_irqs = dev_info.num_irqs; - vbasedev->num_regions = dev_info.num_regions; - vbasedev->flags = dev_info.flags; - - trace_vfio_get_device(name, dev_info.flags, dev_info.num_regions, - dev_info.num_irqs); - - vbasedev->reset_works = !!(dev_info.flags & VFIO_DEVICE_FLAGS_RESET); - return 0; -} - -void vfio_put_base_device(VFIODevice *vbasedev) -{ - if (!vbasedev->group) { - return; - } - QLIST_REMOVE(vbasedev, next); - vbasedev->group = NULL; - trace_vfio_put_base_device(vbasedev->fd); - close(vbasedev->fd); -} - -int vfio_get_region_info(VFIODevice *vbasedev, int index, - struct vfio_region_info **info) -{ - size_t argsz = sizeof(struct vfio_region_info); - - *info = g_malloc0(argsz); - - (*info)->index = index; - (*info)->argsz = argsz; - - if (ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, *info)) { - g_free(*info); - return -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; - } - - if (QLIST_NEXT(QLIST_FIRST(&container->group_list), container_next)) { - return false; - } - - return true; -} - -static int vfio_eeh_container_op(VFIOContainer *container, uint32_t op) -{ - 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; - } - - 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 deleted file mode 100644 index 49ecf1172..000000000 --- a/qemu/hw/vfio/pci-quirks.c +++ /dev/null @@ -1,1205 +0,0 @@ -/* - * device quirks for PCI devices - * - * Copyright Red Hat, Inc. 2012-2015 - * - * Authors: - * Alex Williamson - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - */ - -#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 deleted file mode 100644 index d091d8cf0..000000000 --- a/qemu/hw/vfio/pci.c +++ /dev/null @@ -1,2734 +0,0 @@ -/* - * vfio based device assignment support - * - * Copyright Red Hat, Inc. 2012 - * - * Authors: - * Alex Williamson - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Based on qemu-kvm device-assignment: - * Adapted for KVM by Qumranet. - * Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com) - * Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com) - * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com) - * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com) - * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com) - */ - -#include "qemu/osdep.h" -#include -#include -#include - -#include "hw/pci/msi.h" -#include "hw/pci/msix.h" -#include "hw/pci/pci_bridge.h" -#include "qemu/error-report.h" -#include "qemu/range.h" -#include "sysemu/kvm.h" -#include "sysemu/sysemu.h" -#include "pci.h" -#include "trace.h" - -#define MSIX_CAP_LENGTH 12 - -static void vfio_disable_interrupts(VFIOPCIDevice *vdev); -static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled); - -/* - * Disabling BAR mmaping can be slow, but toggling it around INTx can - * also be a huge overhead. We try to get the best of both worlds by - * waiting until an interrupt to disable mmaps (subsequent transitions - * to the same state are effectively no overhead). If the interrupt has - * been serviced and the time gap is long enough, we re-enable mmaps for - * performance. This works well for things like graphics cards, which - * may not use their interrupt at all and are penalized to an unusable - * level by read/write BAR traps. Other devices, like NICs, have more - * regular interrupts and see much better latency by staying in non-mmap - * mode. We therefore set the default mmap_timeout such that a ping - * is just enough to keep the mmap disabled. Users can experiment with - * other options with the x-intx-mmap-timeout-ms parameter (a value of - * zero disables the timer). - */ -static void vfio_intx_mmap_enable(void *opaque) -{ - VFIOPCIDevice *vdev = opaque; - - if (vdev->intx.pending) { - timer_mod(vdev->intx.mmap_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + vdev->intx.mmap_timeout); - return; - } - - vfio_mmap_set_enabled(vdev, true); -} - -static void vfio_intx_interrupt(void *opaque) -{ - VFIOPCIDevice *vdev = opaque; - - if (!event_notifier_test_and_clear(&vdev->intx.interrupt)) { - return; - } - - trace_vfio_intx_interrupt(vdev->vbasedev.name, 'A' + vdev->intx.pin); - - vdev->intx.pending = true; - pci_irq_assert(&vdev->pdev); - vfio_mmap_set_enabled(vdev, false); - if (vdev->intx.mmap_timeout) { - timer_mod(vdev->intx.mmap_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + vdev->intx.mmap_timeout); - } -} - -static void vfio_intx_eoi(VFIODevice *vbasedev) -{ - VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); - - if (!vdev->intx.pending) { - return; - } - - 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_intx_enable_kvm(VFIOPCIDevice *vdev) -{ -#ifdef CONFIG_KVM - struct kvm_irqfd irqfd = { - .fd = event_notifier_get_fd(&vdev->intx.interrupt), - .gsi = vdev->intx.route.irq, - .flags = KVM_IRQFD_FLAG_RESAMPLE, - }; - struct vfio_irq_set *irq_set; - int ret, argsz; - int32_t *pfd; - - if (vdev->no_kvm_intx || !kvm_irqfds_enabled() || - vdev->intx.route.mode != PCI_INTX_ENABLED || - !kvm_resamplefds_enabled()) { - return; - } - - /* Get to a known interrupt state */ - qemu_set_fd_handler(irqfd.fd, NULL, NULL, vdev); - vfio_mask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); - vdev->intx.pending = false; - pci_irq_deassert(&vdev->pdev); - - /* Get an eventfd for resample/unmask */ - if (event_notifier_init(&vdev->intx.unmask, 0)) { - error_report("vfio: Error: event_notifier_init failed eoi"); - goto fail; - } - - /* KVM triggers it, VFIO listens for it */ - irqfd.resamplefd = event_notifier_get_fd(&vdev->intx.unmask); - - if (kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd)) { - error_report("vfio: Error: Failed to setup resample irqfd: %m"); - goto fail_irqfd; - } - - argsz = sizeof(*irq_set) + sizeof(*pfd); - - irq_set = g_malloc0(argsz); - irq_set->argsz = argsz; - irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_UNMASK; - irq_set->index = VFIO_PCI_INTX_IRQ_INDEX; - irq_set->start = 0; - irq_set->count = 1; - pfd = (int32_t *)&irq_set->data; - - *pfd = irqfd.resamplefd; - - ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set); - g_free(irq_set); - if (ret) { - error_report("vfio: Error: Failed to setup INTx unmask fd: %m"); - goto fail_vfio; - } - - /* Let'em rip */ - vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); - - vdev->intx.kvm_accel = true; - - trace_vfio_intx_enable_kvm(vdev->vbasedev.name); - - return; - -fail_vfio: - irqfd.flags = KVM_IRQFD_FLAG_DEASSIGN; - kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd); -fail_irqfd: - event_notifier_cleanup(&vdev->intx.unmask); -fail: - qemu_set_fd_handler(irqfd.fd, vfio_intx_interrupt, NULL, vdev); - vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); -#endif -} - -static void vfio_intx_disable_kvm(VFIOPCIDevice *vdev) -{ -#ifdef CONFIG_KVM - struct kvm_irqfd irqfd = { - .fd = event_notifier_get_fd(&vdev->intx.interrupt), - .gsi = vdev->intx.route.irq, - .flags = KVM_IRQFD_FLAG_DEASSIGN, - }; - - if (!vdev->intx.kvm_accel) { - return; - } - - /* - * Get to a known state, hardware masked, QEMU ready to accept new - * interrupts, QEMU IRQ de-asserted. - */ - vfio_mask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); - vdev->intx.pending = false; - pci_irq_deassert(&vdev->pdev); - - /* Tell KVM to stop listening for an INTx irqfd */ - if (kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd)) { - error_report("vfio: Error: Failed to disable INTx irqfd: %m"); - } - - /* We only need to close the eventfd for VFIO to cleanup the kernel side */ - event_notifier_cleanup(&vdev->intx.unmask); - - /* QEMU starts listening for interrupt events. */ - qemu_set_fd_handler(irqfd.fd, vfio_intx_interrupt, NULL, vdev); - - vdev->intx.kvm_accel = false; - - /* If we've missed an event, let it re-fire through QEMU */ - vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); - - trace_vfio_intx_disable_kvm(vdev->vbasedev.name); -#endif -} - -static void vfio_intx_update(PCIDevice *pdev) -{ - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); - PCIINTxRoute route; - - if (vdev->interrupt != VFIO_INT_INTx) { - return; - } - - route = pci_device_route_intx_to_irq(&vdev->pdev, vdev->intx.pin); - - if (!pci_intx_route_changed(&vdev->intx.route, &route)) { - return; /* Nothing changed */ - } - - trace_vfio_intx_update(vdev->vbasedev.name, - vdev->intx.route.irq, route.irq); - - vfio_intx_disable_kvm(vdev); - - vdev->intx.route = route; - - if (route.mode != PCI_INTX_ENABLED) { - return; - } - - vfio_intx_enable_kvm(vdev); - - /* Re-enable the interrupt in cased we missed an EOI */ - vfio_intx_eoi(&vdev->vbasedev); -} - -static int vfio_intx_enable(VFIOPCIDevice *vdev) -{ - uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1); - int ret, argsz; - struct vfio_irq_set *irq_set; - int32_t *pfd; - - if (!pin) { - return 0; - } - - vfio_disable_interrupts(vdev); - - vdev->intx.pin = pin - 1; /* Pin A (1) -> irq[0] */ - pci_config_set_interrupt_pin(vdev->pdev.config, pin); - -#ifdef CONFIG_KVM - /* - * Only conditional to avoid generating error messages on platforms - * where we won't actually use the result anyway. - */ - if (kvm_irqfds_enabled() && kvm_resamplefds_enabled()) { - vdev->intx.route = pci_device_route_intx_to_irq(&vdev->pdev, - vdev->intx.pin); - } -#endif - - ret = event_notifier_init(&vdev->intx.interrupt, 0); - if (ret) { - error_report("vfio: Error: event_notifier_init failed"); - return ret; - } - - argsz = sizeof(*irq_set) + sizeof(*pfd); - - irq_set = g_malloc0(argsz); - irq_set->argsz = argsz; - irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER; - irq_set->index = VFIO_PCI_INTX_IRQ_INDEX; - irq_set->start = 0; - irq_set->count = 1; - pfd = (int32_t *)&irq_set->data; - - *pfd = event_notifier_get_fd(&vdev->intx.interrupt); - qemu_set_fd_handler(*pfd, vfio_intx_interrupt, NULL, vdev); - - ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set); - g_free(irq_set); - if (ret) { - error_report("vfio: Error: Failed to setup INTx fd: %m"); - qemu_set_fd_handler(*pfd, NULL, NULL, vdev); - event_notifier_cleanup(&vdev->intx.interrupt); - return -errno; - } - - vfio_intx_enable_kvm(vdev); - - vdev->interrupt = VFIO_INT_INTx; - - trace_vfio_intx_enable(vdev->vbasedev.name); - - return 0; -} - -static void vfio_intx_disable(VFIOPCIDevice *vdev) -{ - int fd; - - timer_del(vdev->intx.mmap_timer); - vfio_intx_disable_kvm(vdev); - vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); - vdev->intx.pending = false; - pci_irq_deassert(&vdev->pdev); - vfio_mmap_set_enabled(vdev, true); - - fd = event_notifier_get_fd(&vdev->intx.interrupt); - qemu_set_fd_handler(fd, NULL, NULL, vdev); - event_notifier_cleanup(&vdev->intx.interrupt); - - vdev->interrupt = VFIO_INT_NONE; - - trace_vfio_intx_disable(vdev->vbasedev.name); -} - -/* - * MSI/X - */ -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; - } - - if (vdev->interrupt == VFIO_INT_MSIX) { - 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) { - 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); - notify(&vdev->pdev, nr); -} - -static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) -{ - struct vfio_irq_set *irq_set; - int ret = 0, i, argsz; - int32_t *fds; - - argsz = sizeof(*irq_set) + (vdev->nr_vectors * sizeof(*fds)); - - irq_set = g_malloc0(argsz); - irq_set->argsz = argsz; - irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER; - irq_set->index = msix ? VFIO_PCI_MSIX_IRQ_INDEX : VFIO_PCI_MSI_IRQ_INDEX; - irq_set->start = 0; - irq_set->count = vdev->nr_vectors; - fds = (int32_t *)&irq_set->data; - - for (i = 0; i < vdev->nr_vectors; i++) { - int fd = -1; - - /* - * MSI vs MSI-X - The guest has direct access to MSI mask and pending - * bits, therefore we always use the KVM signaling path when setup. - * MSI-X mask and pending bits are emulated, so we want to use the - * KVM signaling path only when configured and unmasked. - */ - if (vdev->msi_vectors[i].use) { - if (vdev->msi_vectors[i].virq < 0 || - (msix && msix_is_masked(&vdev->pdev, i))) { - fd = event_notifier_get_fd(&vdev->msi_vectors[i].interrupt); - } else { - fd = event_notifier_get_fd(&vdev->msi_vectors[i].kvm_interrupt); - } - } - - fds[i] = fd; - } - - ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set); - - g_free(irq_set); - - return ret; -} - -static void vfio_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, - MSIMessage *msg, bool msix) -{ - int virq; - - if ((msix && vdev->no_kvm_msix) || (!msix && vdev->no_kvm_msi) || !msg) { - return; - } - - if (event_notifier_init(&vector->kvm_interrupt, 0)) { - return; - } - - virq = kvm_irqchip_add_msi_route(kvm_state, *msg, &vdev->pdev); - if (virq < 0) { - event_notifier_cleanup(&vector->kvm_interrupt); - return; - } - - if (kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, &vector->kvm_interrupt, - NULL, virq) < 0) { - kvm_irqchip_release_virq(kvm_state, virq); - event_notifier_cleanup(&vector->kvm_interrupt); - return; - } - - vector->virq = virq; -} - -static void vfio_remove_kvm_msi_virq(VFIOMSIVector *vector) -{ - kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, &vector->kvm_interrupt, - vector->virq); - kvm_irqchip_release_virq(kvm_state, vector->virq); - vector->virq = -1; - event_notifier_cleanup(&vector->kvm_interrupt); -} - -static void vfio_update_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage msg, - PCIDevice *pdev) -{ - kvm_irqchip_update_msi_route(kvm_state, vector->virq, msg, pdev); -} - -static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, - MSIMessage *msg, IOHandler *handler) -{ - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); - VFIOMSIVector *vector; - int ret; - - trace_vfio_msix_vector_do_use(vdev->vbasedev.name, nr); - - vector = &vdev->msi_vectors[nr]; - - if (!vector->use) { - vector->vdev = vdev; - vector->virq = -1; - if (event_notifier_init(&vector->interrupt, 0)) { - error_report("vfio: Error: event_notifier_init failed"); - } - vector->use = true; - msix_vector_use(pdev, nr); - } - - qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), - handler, NULL, vector); - - /* - * Attempt to enable route through KVM irqchip, - * default to userspace handling if unavailable. - */ - if (vector->virq >= 0) { - if (!msg) { - vfio_remove_kvm_msi_virq(vector); - } else { - vfio_update_kvm_msi_virq(vector, *msg, pdev); - } - } else { - vfio_add_kvm_msi_virq(vdev, vector, msg, true); - } - - /* - * We don't want to have the host allocate all possible MSI vectors - * for a device if they're not in use, so we shutdown and incrementally - * increase them as needed. - */ - if (vdev->nr_vectors < nr + 1) { - vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX); - vdev->nr_vectors = nr + 1; - ret = vfio_enable_vectors(vdev, true); - if (ret) { - error_report("vfio: failed to enable vectors, %d", ret); - } - } else { - int argsz; - struct vfio_irq_set *irq_set; - int32_t *pfd; - - argsz = sizeof(*irq_set) + sizeof(*pfd); - - irq_set = g_malloc0(argsz); - irq_set->argsz = argsz; - irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | - VFIO_IRQ_SET_ACTION_TRIGGER; - irq_set->index = VFIO_PCI_MSIX_IRQ_INDEX; - irq_set->start = nr; - irq_set->count = 1; - pfd = (int32_t *)&irq_set->data; - - if (vector->virq >= 0) { - *pfd = event_notifier_get_fd(&vector->kvm_interrupt); - } else { - *pfd = event_notifier_get_fd(&vector->interrupt); - } - - ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set); - g_free(irq_set); - if (ret) { - error_report("vfio: failed to modify vector, %d", ret); - } - } - - /* 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; -} - -static int vfio_msix_vector_use(PCIDevice *pdev, - unsigned int nr, MSIMessage msg) -{ - return vfio_msix_vector_do_use(pdev, nr, &msg, vfio_msi_interrupt); -} - -static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) -{ - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); - VFIOMSIVector *vector = &vdev->msi_vectors[nr]; - - trace_vfio_msix_vector_release(vdev->vbasedev.name, nr); - - /* - * There are still old guests that mask and unmask vectors on every - * interrupt. If we're using QEMU bypass with a KVM irqfd, leave all of - * the KVM setup in place, simply switch VFIO to use the non-bypass - * eventfd. We'll then fire the interrupt through QEMU and the MSI-X - * core will mask the interrupt and set pending bits, allowing it to - * be re-asserted on unmask. Nothing to do if already using QEMU mode. - */ - if (vector->virq >= 0) { - int argsz; - struct vfio_irq_set *irq_set; - int32_t *pfd; - - argsz = sizeof(*irq_set) + sizeof(*pfd); - - irq_set = g_malloc0(argsz); - irq_set->argsz = argsz; - irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | - VFIO_IRQ_SET_ACTION_TRIGGER; - irq_set->index = VFIO_PCI_MSIX_IRQ_INDEX; - irq_set->start = nr; - irq_set->count = 1; - pfd = (int32_t *)&irq_set->data; - - *pfd = event_notifier_get_fd(&vector->interrupt); - - ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set); - - g_free(irq_set); - } -} - -static void vfio_msix_enable(VFIOPCIDevice *vdev) -{ - vfio_disable_interrupts(vdev); - - vdev->msi_vectors = g_new0(VFIOMSIVector, vdev->msix->entries); - - vdev->interrupt = VFIO_INT_MSIX; - - /* - * Some communication channels between VF & PF or PF & fw rely on the - * physical state of the device and expect that enabling MSI-X from the - * guest enables the same on the host. When our guest is Linux, the - * guest driver call to pci_enable_msix() sets the enabling bit in the - * MSI-X capability, but leaves the vector table masked. We therefore - * can't rely on a vector_use callback (from request_irq() in the guest) - * to switch the physical device into MSI-X mode because that may come a - * long time after pci_enable_msix(). This code enables vector 0 with - * triggering to userspace, then immediately release the vector, leaving - * the physical device with no vectors enabled, but MSI-X enabled, just - * like the guest view. - */ - vfio_msix_vector_do_use(&vdev->pdev, 0, NULL, NULL); - vfio_msix_vector_release(&vdev->pdev, 0); - - if (msix_set_vector_notifiers(&vdev->pdev, vfio_msix_vector_use, - vfio_msix_vector_release, NULL)) { - error_report("vfio: msix_set_vector_notifiers failed"); - } - - trace_vfio_msix_enable(vdev->vbasedev.name); -} - -static void vfio_msi_enable(VFIOPCIDevice *vdev) -{ - int ret, i; - - vfio_disable_interrupts(vdev); - - vdev->nr_vectors = msi_nr_vectors_allocated(&vdev->pdev); -retry: - vdev->msi_vectors = g_new0(VFIOMSIVector, vdev->nr_vectors); - - for (i = 0; i < vdev->nr_vectors; i++) { - VFIOMSIVector *vector = &vdev->msi_vectors[i]; - MSIMessage msg = msi_get_message(&vdev->pdev, i); - - vector->vdev = vdev; - vector->virq = -1; - vector->use = true; - - if (event_notifier_init(&vector->interrupt, 0)) { - error_report("vfio: Error: event_notifier_init failed"); - } - - qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), - vfio_msi_interrupt, NULL, vector); - - /* - * Attempt to enable route through KVM irqchip, - * default to userspace handling if unavailable. - */ - vfio_add_kvm_msi_virq(vdev, vector, &msg, false); - } - - /* Set interrupt type prior to possible interrupts */ - vdev->interrupt = VFIO_INT_MSI; - - ret = vfio_enable_vectors(vdev, false); - if (ret) { - if (ret < 0) { - error_report("vfio: Error: Failed to setup MSI fds: %m"); - } else if (ret != vdev->nr_vectors) { - error_report("vfio: Error: Failed to enable %d " - "MSI vectors, retry with %d", vdev->nr_vectors, ret); - } - - for (i = 0; i < vdev->nr_vectors; i++) { - VFIOMSIVector *vector = &vdev->msi_vectors[i]; - if (vector->virq >= 0) { - vfio_remove_kvm_msi_virq(vector); - } - qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), - NULL, NULL, NULL); - event_notifier_cleanup(&vector->interrupt); - } - - g_free(vdev->msi_vectors); - - if (ret > 0 && ret != vdev->nr_vectors) { - vdev->nr_vectors = ret; - goto retry; - } - vdev->nr_vectors = 0; - - /* - * Failing to setup MSI doesn't really fall within any specification. - * Let's try leaving interrupts disabled and hope the guest figures - * out to fall back to INTx for this device. - */ - error_report("vfio: Error: Failed to enable MSI"); - vdev->interrupt = VFIO_INT_NONE; - - return; - } - - trace_vfio_msi_enable(vdev->vbasedev.name, vdev->nr_vectors); -} - -static void vfio_msi_disable_common(VFIOPCIDevice *vdev) -{ - int i; - - for (i = 0; i < vdev->nr_vectors; i++) { - VFIOMSIVector *vector = &vdev->msi_vectors[i]; - if (vdev->msi_vectors[i].use) { - if (vector->virq >= 0) { - vfio_remove_kvm_msi_virq(vector); - } - qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), - NULL, NULL, NULL); - event_notifier_cleanup(&vector->interrupt); - } - } - - g_free(vdev->msi_vectors); - vdev->msi_vectors = NULL; - vdev->nr_vectors = 0; - vdev->interrupt = VFIO_INT_NONE; - - vfio_intx_enable(vdev); -} - -static void vfio_msix_disable(VFIOPCIDevice *vdev) -{ - int i; - - msix_unset_vector_notifiers(&vdev->pdev); - - /* - * MSI-X will only release vectors if MSI-X is still enabled on the - * device, check through the rest and release it ourselves if necessary. - */ - for (i = 0; i < vdev->nr_vectors; i++) { - if (vdev->msi_vectors[i].use) { - vfio_msix_vector_release(&vdev->pdev, i); - msix_vector_unuse(&vdev->pdev, i); - } - } - - if (vdev->nr_vectors) { - vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX); - } - - vfio_msi_disable_common(vdev); - - memset(vdev->msix->pending, 0, - BITS_TO_LONGS(vdev->msix->entries) * sizeof(unsigned long)); - - trace_vfio_msix_disable(vdev->vbasedev.name); -} - -static void vfio_msi_disable(VFIOPCIDevice *vdev) -{ - vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSI_IRQ_INDEX); - vfio_msi_disable_common(vdev); - - trace_vfio_msi_disable(vdev->vbasedev.name); -} - -static void vfio_update_msi(VFIOPCIDevice *vdev) -{ - int i; - - for (i = 0; i < vdev->nr_vectors; i++) { - VFIOMSIVector *vector = &vdev->msi_vectors[i]; - MSIMessage msg; - - if (!vector->use || vector->virq < 0) { - continue; - } - - msg = msi_get_message(&vdev->pdev, i); - vfio_update_kvm_msi_virq(vector, msg, &vdev->pdev); - } -} - -static void vfio_pci_load_rom(VFIOPCIDevice *vdev) -{ - struct vfio_region_info *reg_info; - uint64_t size; - off_t off = 0; - ssize_t bytes; - - if (vfio_get_region_info(&vdev->vbasedev, - VFIO_PCI_ROM_REGION_INDEX, ®_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); - - 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; - error_report("vfio-pci: Cannot read device rom at " - "%s", vdev->vbasedev.name); - 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"); - return; - } - - vdev->rom = g_malloc(size); - memset(vdev->rom, 0xff, size); - - while (size) { - bytes = pread(vdev->vbasedev.fd, vdev->rom + off, - size, vdev->rom_offset + off); - if (bytes == 0) { - break; - } else if (bytes > 0) { - off += bytes; - size -= bytes; - } else { - if (errno == EINTR || errno == EAGAIN) { - continue; - } - error_report("vfio: Error reading device ROM: %m"); - 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) -{ - VFIOPCIDevice *vdev = opaque; - union { - uint8_t byte; - uint16_t word; - uint32_t dword; - uint64_t qword; - } val; - uint64_t data = 0; - - /* Load the ROM lazily when the guest tries to read it */ - if (unlikely(!vdev->rom && !vdev->rom_read_failed)) { - vfio_pci_load_rom(vdev); - } - - memcpy(&val, vdev->rom + addr, - (addr < vdev->rom_size) ? MIN(size, vdev->rom_size - addr) : 0); - - switch (size) { - case 1: - data = val.byte; - break; - case 2: - data = le16_to_cpu(val.word); - break; - case 4: - data = le32_to_cpu(val.dword); - break; - default: - hw_error("vfio: unsupported read size, %d bytes\n", size); - break; - } - - trace_vfio_rom_read(vdev->vbasedev.name, addr, size, data); - - return data; -} - -static void vfio_rom_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ -} - -static const MemoryRegionOps vfio_rom_ops = { - .read = vfio_rom_read, - .write = vfio_rom_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -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; - 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 %s is known to cause system instability issues during option rom execution. Proceeding anyway since user specified romfile\n", - vdev->vbasedev.name); - } - return; - } - - /* - * Use the same size ROM BAR as the physical device. The contents - * will get filled in later when the guest tries to read it. - */ - if (pread(fd, &orig, 4, offset) != 4 || - pwrite(fd, &size, 4, offset) != 4 || - pread(fd, &size, 4, offset) != 4 || - pwrite(fd, &orig, 4, offset) != 4) { - error_report("%s(%s) failed: %m", __func__, vdev->vbasedev.name); - return; - } - - size = ~(le32_to_cpu(size) & PCI_ROM_ADDRESS_MASK) + 1; - - if (!size) { - return; - } - - if (vfio_blacklist_opt_rom(vdev)) { - if (dev->opts && qemu_opt_get(dev->opts, "rombar")) { - 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 %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); - - 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); - - vdev->pdev.has_rom = true; - vdev->rom_read_failed = false; -} - -void vfio_vga_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VFIOVGARegion *region = opaque; - VFIOVGA *vga = container_of(region, VFIOVGA, region[region->nr]); - union { - uint8_t byte; - uint16_t word; - uint32_t dword; - uint64_t qword; - } buf; - off_t offset = vga->fd_offset + region->offset + addr; - - switch (size) { - case 1: - buf.byte = data; - break; - case 2: - buf.word = cpu_to_le16(data); - break; - case 4: - buf.dword = cpu_to_le32(data); - break; - default: - hw_error("vfio: unsupported write size, %d bytes", size); - break; - } - - if (pwrite(vga->fd, &buf, size, offset) != size) { - error_report("%s(,0x%"HWADDR_PRIx", 0x%"PRIx64", %d) failed: %m", - __func__, region->offset + addr, data, size); - } - - trace_vfio_vga_write(region->offset + addr, data, size); -} - -uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size) -{ - VFIOVGARegion *region = opaque; - VFIOVGA *vga = container_of(region, VFIOVGA, region[region->nr]); - union { - uint8_t byte; - uint16_t word; - uint32_t dword; - uint64_t qword; - } buf; - uint64_t data = 0; - off_t offset = vga->fd_offset + region->offset + addr; - - if (pread(vga->fd, &buf, size, offset) != size) { - error_report("%s(,0x%"HWADDR_PRIx", %d) failed: %m", - __func__, region->offset + addr, size); - return (uint64_t)-1; - } - - switch (size) { - case 1: - data = buf.byte; - break; - case 2: - data = le16_to_cpu(buf.word); - break; - case 4: - data = le32_to_cpu(buf.dword); - break; - default: - hw_error("vfio: unsupported read size, %d bytes", size); - break; - } - - trace_vfio_vga_read(region->offset + addr, size, data); - - return data; -} - -static const MemoryRegionOps vfio_vga_ops = { - .read = vfio_vga_read, - .write = vfio_vga_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -/* - * PCI config space - */ -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; - - memcpy(&emu_bits, vdev->emulated_config_bits + addr, len); - emu_bits = le32_to_cpu(emu_bits); - - if (emu_bits) { - emu_val = pci_default_read_config(pdev, addr, len); - } - - if (~emu_bits & (0xffffffffU >> (32 - len * 8))) { - ssize_t ret; - - ret = pread(vdev->vbasedev.fd, &phys_val, len, - vdev->config_offset + addr); - if (ret != 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); - } - - val = (emu_val & emu_bits) | (phys_val & ~emu_bits); - - trace_vfio_pci_read_config(vdev->vbasedev.name, addr, len, val); - - return val; -} - -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); - - trace_vfio_pci_write_config(vdev->vbasedev.name, addr, val, len); - - /* 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(%s, 0x%x, 0x%x, 0x%x) failed: %m", - __func__, vdev->vbasedev.name, addr, val, len); - } - - /* MSI/MSI-X Enabling/Disabling */ - if (pdev->cap_present & QEMU_PCI_CAP_MSI && - ranges_overlap(addr, len, pdev->msi_cap, vdev->msi_cap_size)) { - int is_enabled, was_enabled = msi_enabled(pdev); - - pci_default_write_config(pdev, addr, val, len); - - is_enabled = msi_enabled(pdev); - - if (!was_enabled) { - if (is_enabled) { - vfio_msi_enable(vdev); - } - } else { - if (!is_enabled) { - vfio_msi_disable(vdev); - } else { - vfio_update_msi(vdev); - } - } - } else if (pdev->cap_present & QEMU_PCI_CAP_MSIX && - ranges_overlap(addr, len, pdev->msix_cap, MSIX_CAP_LENGTH)) { - int is_enabled, was_enabled = msix_enabled(pdev); - - pci_default_write_config(pdev, addr, val, len); - - is_enabled = msix_enabled(pdev); - - if (!was_enabled && is_enabled) { - vfio_msix_enable(vdev); - } else if (was_enabled && !is_enabled) { - vfio_msix_disable(vdev); - } - } else { - /* Write everything to QEMU to keep emulated bits correct */ - pci_default_write_config(pdev, addr, val, len); - } -} - -/* - * Interrupt setup - */ -static void vfio_disable_interrupts(VFIOPCIDevice *vdev) -{ - /* - * More complicated than it looks. Disabling MSI/X transitions the - * device to INTx mode (if supported). Therefore we need to first - * disable MSI/X and then cleanup by disabling INTx. - */ - if (vdev->interrupt == VFIO_INT_MSIX) { - vfio_msix_disable(vdev); - } else if (vdev->interrupt == VFIO_INT_MSI) { - vfio_msi_disable(vdev); - } - - if (vdev->interrupt == VFIO_INT_INTx) { - vfio_intx_disable(vdev); - } -} - -static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos) -{ - uint16_t ctrl; - bool msi_64bit, msi_maskbit; - int ret, entries; - - if (pread(vdev->vbasedev.fd, &ctrl, sizeof(ctrl), - vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) { - return -errno; - } - ctrl = le16_to_cpu(ctrl); - - msi_64bit = !!(ctrl & PCI_MSI_FLAGS_64BIT); - msi_maskbit = !!(ctrl & PCI_MSI_FLAGS_MASKBIT); - entries = 1 << ((ctrl & PCI_MSI_FLAGS_QMASK) >> 1); - - trace_vfio_msi_setup(vdev->vbasedev.name, pos); - - ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit); - if (ret < 0) { - if (ret == -ENOTSUP) { - return 0; - } - error_report("vfio: msi_init failed"); - return ret; - } - vdev->msi_cap_size = 0xa + (msi_maskbit ? 0xa : 0) + (msi_64bit ? 0x4 : 0); - - 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(®ion->mmaps[1], ®ion->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 - * MemoryRegion for the BAR. In order to setup the BAR and not - * attempt to mmap the MSI-X table area, which VFIO won't allow, we - * need to first look for where the MSI-X table lives. So we - * unfortunately split MSI-X setup across two functions. - */ -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) { - return 0; - } - - if (pread(fd, &ctrl, sizeof(ctrl), - vdev->config_offset + pos + PCI_MSIX_FLAGS) != sizeof(ctrl)) { - return -errno; - } - - if (pread(fd, &table, sizeof(table), - vdev->config_offset + pos + PCI_MSIX_TABLE) != sizeof(table)) { - return -errno; - } - - if (pread(fd, &pba, sizeof(pba), - vdev->config_offset + pos + PCI_MSIX_PBA) != sizeof(pba)) { - return -errno; - } - - ctrl = le16_to_cpu(ctrl); - table = le32_to_cpu(table); - pba = le32_to_cpu(pba); - - 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 (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 (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_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_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->msix->table_bar, vdev->msix->table_offset, - vdev->bars[vdev->msix->pba_bar].region.mem, - vdev->msix->pba_bar, vdev->msix->pba_offset, pos); - if (ret < 0) { - if (ret == -ENOTSUP) { - return 0; - } - error_report("vfio: msix_init failed"); - 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; -} - -static void vfio_teardown_msi(VFIOPCIDevice *vdev) -{ - msi_uninit(&vdev->pdev); - - if (vdev->msix) { - msix_uninit(&vdev->pdev, - vdev->bars[vdev->msix->table_bar].region.mem, - vdev->bars[vdev->msix->pba_bar].region.mem); - g_free(vdev->msix->pending); - } -} - -/* - * Resource setup - */ -static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled) -{ - int i; - - for (i = 0; i < PCI_ROM_SLOT; i++) { - vfio_region_mmaps_set_enabled(&vdev->bars[i].region, enabled); - } -} - -static void vfio_bar_setup(VFIOPCIDevice *vdev, int nr) -{ - VFIOBAR *bar = &vdev->bars[nr]; - - uint32_t pci_bar; - uint8_t type; - int ret; - - /* Skip both unimplemented BARs and the upper half of 64bit BARS. */ - if (!bar->region.size) { - return; - } - - /* 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)); - if (ret != sizeof(pci_bar)) { - error_report("vfio: Failed to read BAR %d (%m)", nr); - return; - } - - pci_bar = le32_to_cpu(pci_bar); - bar->ioport = (pci_bar & PCI_BASE_ADDRESS_SPACE_IO); - bar->mem64 = bar->ioport ? 0 : (pci_bar & PCI_BASE_ADDRESS_MEM_TYPE_64); - type = pci_bar & (bar->ioport ? ~PCI_BASE_ADDRESS_IO_MASK : - ~PCI_BASE_ADDRESS_MEM_MASK); - - 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_bars_setup(VFIOPCIDevice *vdev) -{ - int i; - - for (i = 0; i < PCI_ROM_SLOT; i++) { - vfio_bar_setup(vdev, i); - } - - 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], - "vfio-vga-mmio@0xa0000", - QEMU_PCI_VGA_MEM_SIZE); - 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], - "vfio-vga-io@0x3b0", - QEMU_PCI_VGA_IO_LO_SIZE); - 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], - "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); - vfio_vga_quirk_setup(vdev); - } -} - -static void vfio_bars_exit(VFIOPCIDevice *vdev) -{ - int i; - - for (i = 0; i < PCI_ROM_SLOT; i++) { - vfio_bar_quirk_exit(vdev, i); - vfio_region_exit(&vdev->bars[i].region); - } - - if (vdev->vga) { - pci_unregister_vga(&vdev->pdev); - vfio_vga_quirk_exit(vdev); - } -} - -static void vfio_bars_finalize(VFIOPCIDevice *vdev) -{ - int i; - - for (i = 0; i < PCI_ROM_SLOT; i++) { - vfio_bar_quirk_finalize(vdev, i); - vfio_region_finalize(&vdev->bars[i].region); - } - - 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); - } -} - -/* - * General setup - */ -static uint8_t vfio_std_cap_max_size(PCIDevice *pdev, uint8_t pos) -{ - uint8_t tmp; - uint16_t next = PCI_CONFIG_SPACE_SIZE; - - for (tmp = pdev->config[PCI_CAPABILITY_LIST]; tmp; - tmp = pdev->config[tmp + PCI_CAP_LIST_NEXT]) { - if (tmp > pos && tmp < next) { - next = tmp; - } - } - - return next - pos; -} - -static void vfio_set_word_bits(uint8_t *buf, uint16_t val, uint16_t mask) -{ - pci_set_word(buf, (pci_get_word(buf) & ~mask) | val); -} - -static void vfio_add_emulated_word(VFIOPCIDevice *vdev, int pos, - uint16_t val, uint16_t mask) -{ - vfio_set_word_bits(vdev->pdev.config + pos, val, mask); - vfio_set_word_bits(vdev->pdev.wmask + pos, ~mask, mask); - vfio_set_word_bits(vdev->emulated_config_bits + pos, mask, mask); -} - -static void vfio_set_long_bits(uint8_t *buf, uint32_t val, uint32_t mask) -{ - pci_set_long(buf, (pci_get_long(buf) & ~mask) | val); -} - -static void vfio_add_emulated_long(VFIOPCIDevice *vdev, int pos, - uint32_t val, uint32_t mask) -{ - vfio_set_long_bits(vdev->pdev.config + pos, val, mask); - vfio_set_long_bits(vdev->pdev.wmask + pos, ~mask, mask); - vfio_set_long_bits(vdev->emulated_config_bits + pos, mask, mask); -} - -static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size) -{ - uint16_t flags; - uint8_t type; - - flags = pci_get_word(vdev->pdev.config + pos + PCI_CAP_FLAGS); - type = (flags & PCI_EXP_FLAGS_TYPE) >> 4; - - if (type != PCI_EXP_TYPE_ENDPOINT && - type != PCI_EXP_TYPE_LEG_END && - type != PCI_EXP_TYPE_RC_END) { - - error_report("vfio: Assignment of PCIe type 0x%x " - "devices is not currently supported", type); - return -EINVAL; - } - - if (!pci_bus_is_express(vdev->pdev.bus)) { - PCIBus *bus = vdev->pdev.bus; - PCIDevice *bridge; - - /* - * 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 - * Endpoints, which changes the type and clears the LNK & LNK2 fields. - */ - if (type == PCI_EXP_TYPE_ENDPOINT) { - vfio_add_emulated_word(vdev, pos + PCI_CAP_FLAGS, - PCI_EXP_TYPE_RC_END << 4, - PCI_EXP_FLAGS_TYPE); - - /* Link Capabilities, Status, and Control goes away */ - if (size > PCI_EXP_LNKCTL) { - vfio_add_emulated_long(vdev, pos + PCI_EXP_LNKCAP, 0, ~0); - vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKCTL, 0, ~0); - vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKSTA, 0, ~0); - -#ifndef PCI_EXP_LNKCAP2 -#define PCI_EXP_LNKCAP2 44 -#endif -#ifndef PCI_EXP_LNKSTA2 -#define PCI_EXP_LNKSTA2 50 -#endif - /* Link 2 Capabilities, Status, and Control goes away */ - if (size > PCI_EXP_LNKCAP2) { - vfio_add_emulated_long(vdev, pos + PCI_EXP_LNKCAP2, 0, ~0); - vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKCTL2, 0, ~0); - vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKSTA2, 0, ~0); - } - } - - } else if (type == PCI_EXP_TYPE_LEG_END) { - /* - * Legacy endpoints don't belong on the root complex. Windows - * seems to be happier with devices if we skip the capability. - */ - return 0; - } - - } else { - /* - * Convert Root Complex Integrated Endpoints to regular endpoints. - * These devices don't support LNK/LNK2 capabilities, so make them up. - */ - if (type == PCI_EXP_TYPE_RC_END) { - vfio_add_emulated_word(vdev, pos + PCI_CAP_FLAGS, - PCI_EXP_TYPE_ENDPOINT << 4, - PCI_EXP_FLAGS_TYPE); - vfio_add_emulated_long(vdev, pos + PCI_EXP_LNKCAP, - PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25, ~0); - vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKCTL, 0, ~0); - } - - /* Mark the Link Status bits as emulated to allow virtual negotiation */ - vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKSTA, - pci_get_word(vdev->pdev.config + pos + - PCI_EXP_LNKSTA), - PCI_EXP_LNKCAP_MLW | PCI_EXP_LNKCAP_SLS); - } - - pos = pci_add_capability(&vdev->pdev, PCI_CAP_ID_EXP, pos, size); - if (pos >= 0) { - vdev->pdev.exp.exp_cap = pos; - } - - return pos; -} - -static void vfio_check_pcie_flr(VFIOPCIDevice *vdev, uint8_t pos) -{ - uint32_t cap = pci_get_long(vdev->pdev.config + pos + PCI_EXP_DEVCAP); - - if (cap & PCI_EXP_DEVCAP_FLR) { - trace_vfio_check_pcie_flr(vdev->vbasedev.name); - vdev->has_flr = true; - } -} - -static void vfio_check_pm_reset(VFIOPCIDevice *vdev, uint8_t pos) -{ - uint16_t csr = pci_get_word(vdev->pdev.config + pos + PCI_PM_CTRL); - - if (!(csr & PCI_PM_CTRL_NO_SOFT_RESET)) { - trace_vfio_check_pm_reset(vdev->vbasedev.name); - vdev->has_pm_reset = true; - } -} - -static void vfio_check_af_flr(VFIOPCIDevice *vdev, uint8_t pos) -{ - uint8_t cap = pci_get_byte(vdev->pdev.config + pos + PCI_AF_CAP); - - if ((cap & PCI_AF_CAP_TP) && (cap & PCI_AF_CAP_FLR)) { - trace_vfio_check_af_flr(vdev->vbasedev.name); - vdev->has_flr = true; - } -} - -static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos) -{ - PCIDevice *pdev = &vdev->pdev; - uint8_t cap_id, next, size; - int ret; - - cap_id = pdev->config[pos]; - next = pdev->config[pos + PCI_CAP_LIST_NEXT]; - - /* - * If it becomes important to configure capabilities to their actual - * size, use this as the default when it's something we don't recognize. - * Since QEMU doesn't actually handle many of the config accesses, - * exact size doesn't seem worthwhile. - */ - size = vfio_std_cap_max_size(pdev, 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-calculate size above as cached config space - * will be changed as we unwind the stack. - */ - if (next) { - ret = vfio_add_std_cap(vdev, next); - if (ret) { - return ret; - } - } else { - /* Begin the rebuild, use QEMU emulated list bits */ - pdev->config[PCI_CAPABILITY_LIST] = 0; - vdev->emulated_config_bits[PCI_CAPABILITY_LIST] = 0xff; - vdev->emulated_config_bits[PCI_STATUS] |= PCI_STATUS_CAP_LIST; - } - - /* Use emulated next pointer to allow dropping caps */ - pci_set_byte(vdev->emulated_config_bits + pos + PCI_CAP_LIST_NEXT, 0xff); - - switch (cap_id) { - case PCI_CAP_ID_MSI: - 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_msix_setup(vdev, pos); - break; - case PCI_CAP_ID_PM: - vfio_check_pm_reset(vdev, pos); - vdev->pm_cap = pos; - ret = pci_add_capability(pdev, cap_id, pos, size); - break; - case PCI_CAP_ID_AF: - vfio_check_af_flr(vdev, pos); - ret = pci_add_capability(pdev, cap_id, pos, size); - break; - default: - ret = pci_add_capability(pdev, cap_id, pos, size); - break; - } - - if (ret < 0) { - 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; - } - - return 0; -} - -static int vfio_add_capabilities(VFIOPCIDevice *vdev) -{ - PCIDevice *pdev = &vdev->pdev; - - if (!(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST) || - !pdev->config[PCI_CAPABILITY_LIST]) { - return 0; /* Nothing to add */ - } - - return vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST]); -} - -static void vfio_pci_pre_reset(VFIOPCIDevice *vdev) -{ - PCIDevice *pdev = &vdev->pdev; - uint16_t cmd; - - vfio_disable_interrupts(vdev); - - /* Make sure the device is in D0 */ - if (vdev->pm_cap) { - uint16_t pmcsr; - uint8_t state; - - pmcsr = vfio_pci_read_config(pdev, vdev->pm_cap + PCI_PM_CTRL, 2); - state = pmcsr & PCI_PM_CTRL_STATE_MASK; - if (state) { - pmcsr &= ~PCI_PM_CTRL_STATE_MASK; - vfio_pci_write_config(pdev, vdev->pm_cap + PCI_PM_CTRL, pmcsr, 2); - /* vfio handles the necessary delay here */ - pmcsr = vfio_pci_read_config(pdev, vdev->pm_cap + PCI_PM_CTRL, 2); - state = pmcsr & PCI_PM_CTRL_STATE_MASK; - if (state) { - error_report("vfio: Unable to power on device, stuck in D%d", - state); - } - } - } - - /* - * Stop any ongoing DMA by disconecting I/O, MMIO, and bus master. - * Also put INTx Disable in known state. - */ - cmd = vfio_pci_read_config(pdev, PCI_COMMAND, 2); - cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | - PCI_COMMAND_INTX_DISABLE); - vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2); -} - -static void vfio_pci_post_reset(VFIOPCIDevice *vdev) -{ - vfio_intx_enable(vdev); -} - -static bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name) -{ - 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) -{ - VFIOGroup *group; - struct vfio_pci_hot_reset_info *info; - struct vfio_pci_dependent_device *devices; - struct vfio_pci_hot_reset *reset; - int32_t *fds; - int ret, i, count; - bool multi = false; - - trace_vfio_pci_hot_reset(vdev->vbasedev.name, single ? "one" : "multi"); - - vfio_pci_pre_reset(vdev); - vdev->vbasedev.needs_reset = false; - - info = g_malloc0(sizeof(*info)); - info->argsz = sizeof(*info); - - ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, info); - if (ret && errno != ENOSPC) { - ret = -errno; - if (!vdev->has_pm_reset) { - error_report("vfio: Cannot reset device %s, " - "no available reset mechanism.", vdev->vbasedev.name); - } - goto out_single; - } - - count = info->count; - info = g_realloc(info, sizeof(*info) + (count * sizeof(*devices))); - info->argsz = sizeof(*info) + (count * sizeof(*devices)); - devices = &info->devices[0]; - - ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, info); - if (ret) { - ret = -errno; - error_report("vfio: hot reset info failed: %m"); - goto out_single; - } - - trace_vfio_pci_hot_reset_has_dep_devices(vdev->vbasedev.name); - - /* Verify that we have all the groups required */ - for (i = 0; i < info->count; i++) { - PCIHostDeviceAddress host; - VFIOPCIDevice *tmp; - VFIODevice *vbasedev_iter; - - host.domain = devices[i].segment; - host.bus = devices[i].bus; - host.slot = PCI_SLOT(devices[i].devfn); - host.function = PCI_FUNC(devices[i].devfn); - - 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->vbasedev.name)) { - continue; - } - - QLIST_FOREACH(group, &vfio_group_list, next) { - if (group->groupid == devices[i].group_id) { - break; - } - } - - if (!group) { - if (!vdev->has_pm_reset) { - error_report("vfio: Cannot reset device %s, " - "depends on group %d which is not owned.", - vdev->vbasedev.name, devices[i].group_id); - } - ret = -EPERM; - goto out; - } - - /* Prep dependent devices for reset and clear our marker. */ - QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { - if (vbasedev_iter->type != VFIO_DEVICE_TYPE_PCI) { - continue; - } - tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev); - if (vfio_pci_host_match(&host, tmp->vbasedev.name)) { - if (single) { - ret = -EINVAL; - goto out_single; - } - vfio_pci_pre_reset(tmp); - tmp->vbasedev.needs_reset = false; - multi = true; - break; - } - } - } - - if (!single && !multi) { - ret = -EINVAL; - goto out_single; - } - - /* Determine how many group fds need to be passed */ - count = 0; - QLIST_FOREACH(group, &vfio_group_list, next) { - for (i = 0; i < info->count; i++) { - if (group->groupid == devices[i].group_id) { - count++; - break; - } - } - } - - reset = g_malloc0(sizeof(*reset) + (count * sizeof(*fds))); - reset->argsz = sizeof(*reset) + (count * sizeof(*fds)); - fds = &reset->group_fds[0]; - - /* Fill in group fds */ - QLIST_FOREACH(group, &vfio_group_list, next) { - for (i = 0; i < info->count; i++) { - if (group->groupid == devices[i].group_id) { - fds[reset->count++] = group->fd; - break; - } - } - } - - /* Bus reset! */ - ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_PCI_HOT_RESET, reset); - g_free(reset); - - trace_vfio_pci_hot_reset_result(vdev->vbasedev.name, - ret ? "%m" : "Success"); - -out: - /* Re-enable INTx on affected devices */ - for (i = 0; i < info->count; i++) { - PCIHostDeviceAddress host; - VFIOPCIDevice *tmp; - VFIODevice *vbasedev_iter; - - host.domain = devices[i].segment; - host.bus = devices[i].bus; - host.slot = PCI_SLOT(devices[i].devfn); - host.function = PCI_FUNC(devices[i].devfn); - - if (vfio_pci_host_match(&host, vdev->vbasedev.name)) { - continue; - } - - QLIST_FOREACH(group, &vfio_group_list, next) { - if (group->groupid == devices[i].group_id) { - break; - } - } - - if (!group) { - break; - } - - QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { - if (vbasedev_iter->type != VFIO_DEVICE_TYPE_PCI) { - continue; - } - tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev); - if (vfio_pci_host_match(&host, tmp->vbasedev.name)) { - vfio_pci_post_reset(tmp); - break; - } - } - } -out_single: - vfio_pci_post_reset(vdev); - g_free(info); - - return ret; -} - -/* - * We want to differentiate hot reset of mulitple in-use devices vs hot reset - * of a single in-use device. VFIO_DEVICE_RESET will already handle the case - * of doing hot resets when there is only a single device per bus. The in-use - * here refers to how many VFIODevices are affected. A hot reset that affects - * multiple devices, but only a single in-use device, means that we can call - * it from our bus ->reset() callback since the extent is effectively a single - * device. This allows us to make use of it in the hotplug path. When there - * are multiple in-use devices, we can only trigger the hot reset during a - * system reset and thus from our reset handler. We separate _one vs _multi - * here so that we don't overlap and do a double reset on the system reset - * path where both our reset handler and ->reset() callback are used. Calling - * _one() will only do a hot reset for the one in-use devices case, calling - * _multi() will do nothing if a _one() would have been sufficient. - */ -static int vfio_pci_hot_reset_one(VFIOPCIDevice *vdev) -{ - return vfio_pci_hot_reset(vdev, true); -} - -static int vfio_pci_hot_reset_multi(VFIODevice *vbasedev) -{ - VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); - return vfio_pci_hot_reset(vdev, false); -} - -static void vfio_pci_compute_needs_reset(VFIODevice *vbasedev) -{ - VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); - if (!vbasedev->reset_works || (!vdev->has_flr && vdev->has_pm_reset)) { - vbasedev->needs_reset = true; - } -} - -static VFIODeviceOps vfio_pci_ops = { - .vfio_compute_needs_reset = vfio_pci_compute_needs_reset, - .vfio_hot_reset_multi = vfio_pci_hot_reset_multi, - .vfio_eoi = vfio_intx_eoi, -}; - -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, ®_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; - struct vfio_irq_info irq_info = { .argsz = sizeof(irq_info) }; - int i, ret = -1; - - /* Sanity check device */ - if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PCI)) { - error_report("vfio: Um, this isn't a PCI device"); - goto error; - } - - if (vbasedev->num_regions < VFIO_PCI_CONFIG_REGION_INDEX + 1) { - error_report("vfio: unexpected number of io regions %u", - vbasedev->num_regions); - goto error; - } - - if (vbasedev->num_irqs < VFIO_PCI_MSIX_IRQ_INDEX + 1) { - error_report("vfio: unexpected number of irqs %u", vbasedev->num_irqs); - goto error; - } - - for (i = VFIO_PCI_BAR0_REGION_INDEX; i < VFIO_PCI_ROM_REGION_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); - - if (ret) { - error_report("vfio: Error getting region %d info: %m", i); - goto error; - } - - QLIST_INIT(&vdev->bars[i].quirks); - } - - ret = vfio_get_region_info(vbasedev, - VFIO_PCI_CONFIG_REGION_INDEX, ®_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); - - 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; - - g_free(reg_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; - } - } - - irq_info.index = VFIO_PCI_ERR_IRQ_INDEX; - - ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info); - if (ret) { - /* This can fail for an old kernel or legacy PCI dev */ - trace_vfio_populate_device_get_irq_info_failure(); - ret = 0; - } else if (irq_info.count == 1) { - vdev->pci_aer = true; - } else { - error_report("vfio: %s " - "Could not enable error recovery for the device", - vbasedev->name); - } - -error: - return ret; -} - -static void vfio_put_device(VFIOPCIDevice *vdev) -{ - g_free(vdev->vbasedev.name); - g_free(vdev->msix); - - vfio_put_base_device(&vdev->vbasedev); -} - -static void vfio_err_notifier_handler(void *opaque) -{ - VFIOPCIDevice *vdev = opaque; - - if (!event_notifier_test_and_clear(&vdev->err_notifier)) { - return; - } - - /* - * TBD. Retrieve the error details and decide what action - * needs to be taken. One of the actions could be to pass - * the error to the guest and have the guest driver recover - * from the error. This requires that PCIe capabilities be - * exposed to the guest. For now, we just terminate the - * guest to contain the error. - */ - - 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); -} - -/* - * Registers error notifier for devices supporting error recovery. - * If we encounter a failure in this function, we report an error - * and continue after disabling error recovery support for the - * device. - */ -static void vfio_register_err_notifier(VFIOPCIDevice *vdev) -{ - int ret; - int argsz; - struct vfio_irq_set *irq_set; - int32_t *pfd; - - if (!vdev->pci_aer) { - return; - } - - if (event_notifier_init(&vdev->err_notifier, 0)) { - error_report("vfio: Unable to init event notifier for error detection"); - vdev->pci_aer = false; - return; - } - - argsz = sizeof(*irq_set) + sizeof(*pfd); - - irq_set = g_malloc0(argsz); - irq_set->argsz = argsz; - irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | - VFIO_IRQ_SET_ACTION_TRIGGER; - irq_set->index = VFIO_PCI_ERR_IRQ_INDEX; - irq_set->start = 0; - irq_set->count = 1; - pfd = (int32_t *)&irq_set->data; - - *pfd = event_notifier_get_fd(&vdev->err_notifier); - qemu_set_fd_handler(*pfd, vfio_err_notifier_handler, NULL, vdev); - - ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set); - if (ret) { - error_report("vfio: Failed to set up error notification"); - qemu_set_fd_handler(*pfd, NULL, NULL, vdev); - event_notifier_cleanup(&vdev->err_notifier); - vdev->pci_aer = false; - } - g_free(irq_set); -} - -static void vfio_unregister_err_notifier(VFIOPCIDevice *vdev) -{ - int argsz; - struct vfio_irq_set *irq_set; - int32_t *pfd; - int ret; - - if (!vdev->pci_aer) { - return; - } - - argsz = sizeof(*irq_set) + sizeof(*pfd); - - irq_set = g_malloc0(argsz); - irq_set->argsz = argsz; - irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | - VFIO_IRQ_SET_ACTION_TRIGGER; - irq_set->index = VFIO_PCI_ERR_IRQ_INDEX; - irq_set->start = 0; - irq_set->count = 1; - pfd = (int32_t *)&irq_set->data; - *pfd = -1; - - ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set); - if (ret) { - error_report("vfio: Failed to de-assign error fd: %m"); - } - g_free(irq_set); - qemu_set_fd_handler(event_notifier_get_fd(&vdev->err_notifier), - NULL, NULL, vdev); - event_notifier_cleanup(&vdev->err_notifier); -} - -static void vfio_req_notifier_handler(void *opaque) -{ - VFIOPCIDevice *vdev = opaque; - - if (!event_notifier_test_and_clear(&vdev->req_notifier)) { - return; - } - - qdev_unplug(&vdev->pdev.qdev, NULL); -} - -static void vfio_register_req_notifier(VFIOPCIDevice *vdev) -{ - struct vfio_irq_info irq_info = { .argsz = sizeof(irq_info), - .index = VFIO_PCI_REQ_IRQ_INDEX }; - int argsz; - struct vfio_irq_set *irq_set; - int32_t *pfd; - - if (!(vdev->features & VFIO_FEATURE_ENABLE_REQ)) { - return; - } - - if (ioctl(vdev->vbasedev.fd, - VFIO_DEVICE_GET_IRQ_INFO, &irq_info) < 0 || irq_info.count < 1) { - return; - } - - if (event_notifier_init(&vdev->req_notifier, 0)) { - error_report("vfio: Unable to init event notifier for device request"); - return; - } - - argsz = sizeof(*irq_set) + sizeof(*pfd); - - irq_set = g_malloc0(argsz); - irq_set->argsz = argsz; - irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | - VFIO_IRQ_SET_ACTION_TRIGGER; - irq_set->index = VFIO_PCI_REQ_IRQ_INDEX; - irq_set->start = 0; - irq_set->count = 1; - pfd = (int32_t *)&irq_set->data; - - *pfd = event_notifier_get_fd(&vdev->req_notifier); - qemu_set_fd_handler(*pfd, vfio_req_notifier_handler, NULL, vdev); - - if (ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set)) { - error_report("vfio: Failed to set up device request notification"); - qemu_set_fd_handler(*pfd, NULL, NULL, vdev); - event_notifier_cleanup(&vdev->req_notifier); - } else { - vdev->req_enabled = true; - } - - g_free(irq_set); -} - -static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) -{ - int argsz; - struct vfio_irq_set *irq_set; - int32_t *pfd; - - if (!vdev->req_enabled) { - return; - } - - argsz = sizeof(*irq_set) + sizeof(*pfd); - - irq_set = g_malloc0(argsz); - irq_set->argsz = argsz; - irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | - VFIO_IRQ_SET_ACTION_TRIGGER; - irq_set->index = VFIO_PCI_REQ_IRQ_INDEX; - irq_set->start = 0; - irq_set->count = 1; - pfd = (int32_t *)&irq_set->data; - *pfd = -1; - - if (ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set)) { - error_report("vfio: Failed to de-assign device request fd: %m"); - } - g_free(irq_set); - qemu_set_fd_handler(event_notifier_get_fd(&vdev->req_notifier), - NULL, NULL, vdev); - event_notifier_cleanup(&vdev->req_notifier); - - vdev->req_enabled = false; -} - -static int vfio_initfn(PCIDevice *pdev) -{ - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); - VFIODevice *vbasedev_iter; - VFIOGroup *group; - char *tmp, group_path[PATH_MAX], *group_name; - ssize_t len; - struct stat st; - int groupid; - int ret; - - 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; - - tmp = g_strdup_printf("%s/iommu_group", vdev->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; - } - - group_path[len] = 0; - - group_name = basename(group_path); - if (sscanf(group_name, "%d", &groupid) != 1) { - error_report("vfio: error reading %s: %m", group_path); - return -errno; - } - - trace_vfio_initfn(vdev->vbasedev.name, groupid); - - group = vfio_get_group(groupid, pci_device_iommu_address_space(pdev)); - if (!group) { - error_report("vfio: failed to get group %d", groupid); - return -ENOENT; - } - - 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", - vdev->vbasedev.name); - vfio_put_group(group); - return -EBUSY; - } - } - - ret = vfio_get_device(group, vdev->vbasedev.name, &vdev->vbasedev); - if (ret) { - error_report("vfio: failed to get device %s", vdev->vbasedev.name); - vfio_put_group(group); - return ret; - } - - ret = vfio_populate_device(vdev); - if (ret) { - return ret; - } - - /* Get a copy of config space */ - ret = pread(vdev->vbasedev.fd, vdev->pdev.config, - MIN(pci_config_size(&vdev->pdev), vdev->config_size), - vdev->config_offset); - if (ret < (int)MIN(pci_config_size(&vdev->pdev), vdev->config_size)) { - ret = ret < 0 ? -errno : -EFAULT; - error_report("vfio: Failed to read device config space"); - return ret; - } - - /* vfio emulates a lot for us, but some bits need extra love */ - vdev->emulated_config_bits = g_malloc0(vdev->config_size); - - /* QEMU can choose to expose the ROM or not */ - memset(vdev->emulated_config_bits + PCI_ROM_ADDRESS, 0xff, 4); - - /* - * 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; - - /* Restore or clear multifunction, this is always controlled by QEMU */ - if (vdev->pdev.cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { - vdev->pdev.config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; - } else { - vdev->pdev.config[PCI_HEADER_TYPE] &= ~PCI_HEADER_TYPE_MULTI_FUNCTION; - } - - /* - * Clear host resource mapping info. If we choose not to register a - * BAR, such as might be the case with the option ROM, we can get - * confusing, unwritable, residual addresses from the host here. - */ - memset(&vdev->pdev.config[PCI_BASE_ADDRESS_0], 0, 24); - memset(&vdev->pdev.config[PCI_ROM_ADDRESS], 0, 4); - - vfio_pci_size_rom(vdev); - - ret = vfio_msix_early_setup(vdev); - if (ret) { - return ret; - } - - vfio_bars_setup(vdev); - - ret = vfio_add_capabilities(vdev); - if (ret) { - goto out_teardown; - } - - /* QEMU emulates all of MSI & MSIX */ - if (pdev->cap_present & QEMU_PCI_CAP_MSIX) { - memset(vdev->emulated_config_bits + pdev->msix_cap, 0xff, - MSIX_CAP_LENGTH); - } - - if (pdev->cap_present & QEMU_PCI_CAP_MSI) { - memset(vdev->emulated_config_bits + pdev->msi_cap, 0xff, - vdev->msi_cap_size); - } - - if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) { - vdev->intx.mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, - vfio_intx_mmap_enable, vdev); - pci_device_set_intx_routing_notifier(&vdev->pdev, vfio_intx_update); - ret = vfio_intx_enable(vdev); - if (ret) { - goto out_teardown; - } - } - - vfio_register_err_notifier(vdev); - vfio_register_req_notifier(vdev); - vfio_setup_resetfn_quirk(vdev); - - return 0; - -out_teardown: - pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); - vfio_teardown_msi(vdev); - vfio_bars_exit(vdev); - return ret; -} - -static void vfio_instance_finalize(Object *obj) -{ - PCIDevice *pci_dev = PCI_DEVICE(obj); - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pci_dev); - VFIOGroup *group = vdev->vbasedev.group; - - vfio_bars_finalize(vdev); - g_free(vdev->emulated_config_bits); - g_free(vdev->rom); - vfio_put_device(vdev); - vfio_put_group(group); -} - -static void vfio_exitfn(PCIDevice *pdev) -{ - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); - - vfio_unregister_req_notifier(vdev); - vfio_unregister_err_notifier(vdev); - pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); - vfio_disable_interrupts(vdev); - if (vdev->intx.mmap_timer) { - timer_free(vdev->intx.mmap_timer); - } - vfio_teardown_msi(vdev); - vfio_bars_exit(vdev); -} - -static void vfio_pci_reset(DeviceState *dev) -{ - PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, dev); - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); - - trace_vfio_pci_reset(vdev->vbasedev.name); - - vfio_pci_pre_reset(vdev); - - if (vdev->resetfn && !vdev->resetfn(vdev)) { - goto post_reset; - } - - if (vdev->vbasedev.reset_works && - (vdev->has_flr || !vdev->has_pm_reset) && - !ioctl(vdev->vbasedev.fd, VFIO_DEVICE_RESET)) { - trace_vfio_pci_reset_flr(vdev->vbasedev.name); - goto post_reset; - } - - /* See if we can do our own bus reset */ - if (!vfio_pci_hot_reset_one(vdev)) { - goto post_reset; - } - - /* If nothing else works and the device supports PM reset, use it */ - if (vdev->vbasedev.reset_works && vdev->has_pm_reset && - !ioctl(vdev->vbasedev.fd, VFIO_DEVICE_RESET)) { - trace_vfio_pci_reset_pm(vdev->vbasedev.name); - goto post_reset; - } - -post_reset: - vfio_pci_post_reset(vdev); -} - -static void vfio_instance_init(Object *obj) -{ - PCIDevice *pci_dev = PCI_DEVICE(obj); - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, PCI_DEVICE(obj)); - - device_add_bootindex_property(obj, &vdev->bootindex, - "bootindex", NULL, - &pci_dev->qdev, NULL); -} - -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-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), - * DEFINE_PROP_STRING("vfiogroupfd, VFIOPCIDevice, vfiogroupfd_name), - */ - DEFINE_PROP_END_OF_LIST(), -}; - -static const VMStateDescription vfio_pci_vmstate = { - .name = "vfio-pci", - .unmigratable = 1, -}; - -static void vfio_pci_dev_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass); - - dc->reset = vfio_pci_reset; - dc->props = vfio_pci_dev_properties; - dc->vmsd = &vfio_pci_vmstate; - dc->desc = "VFIO-based PCI device assignment"; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - pdc->init = vfio_initfn; - pdc->exit = vfio_exitfn; - pdc->config_read = vfio_pci_read_config; - pdc->config_write = vfio_pci_write_config; - pdc->is_express = 1; /* We might be */ -} - -static const TypeInfo vfio_pci_dev_info = { - .name = "vfio-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VFIOPCIDevice), - .class_init = vfio_pci_dev_class_init, - .instance_init = vfio_instance_init, - .instance_finalize = vfio_instance_finalize, -}; - -static void register_vfio_pci_dev_type(void) -{ - type_register_static(&vfio_pci_dev_info); -} - -type_init(register_vfio_pci_dev_type) diff --git a/qemu/hw/vfio/pci.h b/qemu/hw/vfio/pci.h deleted file mode 100644 index 3976f6854..000000000 --- a/qemu/hw/vfio/pci.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - * vfio based device assignment support - PCI devices - * - * Copyright Red Hat, Inc. 2012-2015 - * - * Authors: - * Alex Williamson - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - */ -#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 deleted file mode 100644 index 1798a00a3..000000000 --- a/qemu/hw/vfio/platform.c +++ /dev/null @@ -1,705 +0,0 @@ -/* - * vfio based device assignment support - platform devices - * - * Copyright Linaro Limited, 2014 - * - * Authors: - * Kim Phillips - * Eric Auger - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Based on vfio based PCI device assignment support: - * Copyright Red Hat, Inc. 2012 - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include -#include - -#include "hw/vfio/vfio-platform.h" -#include "qemu/error-report.h" -#include "qemu/range.h" -#include "sysemu/sysemu.h" -#include "exec/memory.h" -#include "qemu/queue.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "hw/platform-bus.h" -#include "sysemu/kvm.h" - -/* - * 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 - * @vbasedev: the VFIO device handle - * @info: irq info struct retrieved from VFIO driver - */ -static VFIOINTp *vfio_init_intp(VFIODevice *vbasedev, - struct vfio_irq_info info) -{ - int ret; - VFIOPlatformDevice *vdev = - container_of(vbasedev, VFIOPlatformDevice, vbasedev); - SysBusDevice *sbdev = SYS_BUS_DEVICE(vdev); - VFIOINTp *intp; - - intp = g_malloc0(sizeof(*intp)); - intp->vdev = vdev; - intp->pin = info.index; - intp->flags = info.flags; - intp->state = VFIO_IRQ_INACTIVE; - intp->kvm_accel = false; - - sysbus_init_irq(sbdev, &intp->qemuirq); - - /* Get an eventfd for trigger */ - 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; - } - 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); - return intp; -} - -/** - * vfio_set_trigger_eventfd - set VFIO eventfd handling - * - * @intp: IRQ struct handle - * @handler: handler to be called on eventfd signaling - * - * Setup VFIO signaling and attach an optional user-side handler - * to the eventfd - */ -static int vfio_set_trigger_eventfd(VFIOINTp *intp, - eventfd_user_side_handler_t handler) -{ - VFIODevice *vbasedev = &intp->vdev->vbasedev; - struct vfio_irq_set *irq_set; - int argsz, ret; - int32_t *pfd; - - argsz = sizeof(*irq_set) + sizeof(*pfd); - irq_set = g_malloc0(argsz); - irq_set->argsz = argsz; - irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER; - irq_set->index = intp->pin; - irq_set->start = 0; - irq_set->count = 1; - pfd = (int32_t *)&irq_set->data; - *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); - if (ret < 0) { - error_report("vfio: Failed to set trigger eventfd: %m"); - qemu_set_fd_handler(*pfd, NULL, NULL, NULL); - } - return ret; -} - -/* - * Functions only used when eventfds are handled on user-side - * ie. without irqfd - */ - -/** - * vfio_mmap_set_enabled - enable/disable the fast path mode - * @vdev: the VFIO platform device - * @enabled: the target mmap state - * - * enabled = true ~ fast path = MMIO region is mmaped (no KVM TRAP); - * enabled = false ~ slow path = MMIO region is trapped and region callbacks - * are called; slow path enables to trap the device IRQ status register reset -*/ - -static void vfio_mmap_set_enabled(VFIOPlatformDevice *vdev, bool enabled) -{ - int i; - - for (i = 0; i < vdev->vbasedev.num_regions; i++) { - vfio_region_mmaps_set_enabled(vdev->regions[i], enabled); - } -} - -/** - * vfio_intp_mmap_enable - timer function, restores the fast path - * if there is no more active IRQ - * @opaque: actually points to the VFIO platform device - * - * Called on mmap timer timout, this function checks whether the - * IRQ is still active and if not, restores the fast path. - * by construction a single eventfd is handled at a time. - * if the IRQ is still active, the timer is re-programmed. - */ -static void vfio_intp_mmap_enable(void *opaque) -{ - VFIOINTp *tmp; - VFIOPlatformDevice *vdev = (VFIOPlatformDevice *)opaque; - - qemu_mutex_lock(&vdev->intp_mutex); - QLIST_FOREACH(tmp, &vdev->intp_list, next) { - if (tmp->state == VFIO_IRQ_ACTIVE) { - trace_vfio_platform_intp_mmap_enable(tmp->pin); - /* re-program the timer to check active status later */ - timer_mod(vdev->mmap_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + - vdev->mmap_timeout); - qemu_mutex_unlock(&vdev->intp_mutex); - return; - } - } - vfio_mmap_set_enabled(vdev, true); - qemu_mutex_unlock(&vdev->intp_mutex); -} - -/** - * vfio_intp_inject_pending_lockheld - Injects a pending IRQ - * @opaque: opaque pointer, in practice the VFIOINTp handle - * - * The function is called on a previous IRQ completion, from - * vfio_platform_eoi, while the intp_mutex is locked. - * Also in such situation, the slow path already is set and - * the mmap timer was already programmed. - */ -static void vfio_intp_inject_pending_lockheld(VFIOINTp *intp) -{ - trace_vfio_platform_intp_inject_pending_lockheld(intp->pin, - event_notifier_get_fd(intp->interrupt)); - - intp->state = VFIO_IRQ_ACTIVE; - - /* trigger the virtual IRQ */ - qemu_set_irq(intp->qemuirq, 1); -} - -/** - * vfio_intp_interrupt - The user-side eventfd handler - * @opaque: opaque pointer which in practice is the VFIOINTp handle - * - * the function is entered in event handler context: - * the vIRQ is injected into the guest if there is no other active - * or pending IRQ. - */ -static void vfio_intp_interrupt(VFIOINTp *intp) -{ - int ret; - VFIOINTp *tmp; - VFIOPlatformDevice *vdev = intp->vdev; - bool delay_handling = false; - - qemu_mutex_lock(&vdev->intp_mutex); - if (intp->state == VFIO_IRQ_INACTIVE) { - QLIST_FOREACH(tmp, &vdev->intp_list, next) { - if (tmp->state == VFIO_IRQ_ACTIVE || - tmp->state == VFIO_IRQ_PENDING) { - delay_handling = true; - break; - } - } - } - if (delay_handling) { - /* - * the new IRQ gets a pending status and is pushed in - * the pending queue - */ - intp->state = VFIO_IRQ_PENDING; - 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); - qemu_mutex_unlock(&vdev->intp_mutex); - return; - } - - trace_vfio_platform_intp_interrupt(intp->pin, - event_notifier_get_fd(intp->interrupt)); - - ret = event_notifier_test_and_clear(intp->interrupt); - if (!ret) { - error_report("Error when clearing fd=%d (ret = %d)", - event_notifier_get_fd(intp->interrupt), ret); - } - - intp->state = VFIO_IRQ_ACTIVE; - - /* sets slow path */ - vfio_mmap_set_enabled(vdev, false); - - /* trigger the virtual IRQ */ - qemu_set_irq(intp->qemuirq, 1); - - /* - * Schedule the mmap timer which will restore fastpath when no IRQ - * is active anymore - */ - if (vdev->mmap_timeout) { - timer_mod(vdev->mmap_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + - vdev->mmap_timeout); - } - qemu_mutex_unlock(&vdev->intp_mutex); -} - -/** - * vfio_platform_eoi - IRQ completion routine - * @vbasedev: the VFIO device handle - * - * De-asserts the active virtual IRQ and unmasks the physical IRQ - * (effective for level sensitive IRQ auto-masked by the VFIO driver). - * Then it handles next pending IRQ if any. - * eoi function is called on the first access to any MMIO region - * after an IRQ was triggered, trapped since slow path was set. - * It is assumed this access corresponds to the IRQ status - * register reset. With such a mechanism, a single IRQ can be - * handled at a time since there is no way to know which IRQ - * was completed by the guest (we would need additional details - * about the IRQ status register mask). - */ -static void vfio_platform_eoi(VFIODevice *vbasedev) -{ - VFIOINTp *intp; - VFIOPlatformDevice *vdev = - container_of(vbasedev, VFIOPlatformDevice, vbasedev); - - qemu_mutex_lock(&vdev->intp_mutex); - 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)); - intp->state = VFIO_IRQ_INACTIVE; - - /* deassert the virtual IRQ */ - qemu_set_irq(intp->qemuirq, 0); - - if (vfio_irq_is_automasked(intp)) { - /* unmasks the physical level-sensitive IRQ */ - vfio_unmask_single_irqindex(vbasedev, intp->pin); - } - - /* a single IRQ can be active at a time */ - break; - } - } - /* in case there are pending IRQs, handle the first one */ - if (!QSIMPLEQ_EMPTY(&vdev->pending_intp_queue)) { - intp = QSIMPLEQ_FIRST(&vdev->pending_intp_queue); - vfio_intp_inject_pending_lockheld(intp); - QSIMPLEQ_REMOVE_HEAD(&vdev->pending_intp_queue, pqnext); - } - qemu_mutex_unlock(&vdev->intp_mutex); -} - -/** - * vfio_start_eventfd_injection - starts the virtual IRQ injection using - * user-side handled eventfds - * @sbdev: the sysbus device handle - * @irq: the qemu irq handle - */ - -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: failed to start eventfd signaling for IRQ %d: %m", - intp->pin); - abort(); - } -} - -/* - * Functions used for irqfd - */ - -/** - * vfio_set_resample_eventfd - sets the resamplefd for an IRQ - * @intp: the IRQ struct handle - * programs the VFIO driver to unmask this IRQ when the - * intp->unmask eventfd is triggered - */ -static int vfio_set_resample_eventfd(VFIOINTp *intp) -{ - VFIODevice *vbasedev = &intp->vdev->vbasedev; - struct vfio_irq_set *irq_set; - int argsz, ret; - int32_t *pfd; - - argsz = sizeof(*irq_set) + sizeof(*pfd); - irq_set = g_malloc0(argsz); - irq_set->argsz = argsz; - irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_UNMASK; - irq_set->index = intp->pin; - irq_set->start = 0; - irq_set->count = 1; - pfd = (int32_t *)&irq_set->data; - *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); - if (ret < 0) { - error_report("vfio: Failed to set resample eventfd: %m"); - } - 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); - VFIOINTp *intp; - - if (!kvm_irqfds_enabled() || !kvm_resamplefds_enabled() || - !vdev->irqfd_allowed) { - goto fail_irqfd; - } - - QLIST_FOREACH(intp, &vdev->intp_list, next) { - if (intp->qemuirq == irq) { - break; - } - } - assert(intp); - - 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_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)); - } - - intp->kvm_accel = true; - - return; -fail_vfio: - 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(sbdev, irq); - return; -} - -/* VFIO skeleton */ - -static void vfio_platform_compute_needs_reset(VFIODevice *vbasedev) -{ - vbasedev->needs_reset = true; -} - -/* not implemented yet */ -static int vfio_platform_hot_reset_multi(VFIODevice *vbasedev) -{ - return -1; -} - -/** - * vfio_populate_device - Allocate and populate MMIO region - * and IRQ structs according to driver returned information - * @vbasedev: the VFIO device handle - * - */ -static int vfio_populate_device(VFIODevice *vbasedev) -{ - VFIOINTp *intp, *tmp; - int i, ret = -1; - VFIOPlatformDevice *vdev = - container_of(vbasedev, VFIOPlatformDevice, vbasedev); - - if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PLATFORM)) { - error_report("vfio: Um, this isn't a platform device"); - return ret; - } - - vdev->regions = g_new0(VFIORegion *, vbasedev->num_regions); - - for (i = 0; i < vbasedev->num_regions; i++) { - char *name = g_strdup_printf("VFIO %s region %d\n", vbasedev->name, i); - - 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; - } - } - - vdev->mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, - vfio_intp_mmap_enable, vdev); - - QSIMPLEQ_INIT(&vdev->pending_intp_queue); - - for (i = 0; i < vbasedev->num_irqs; i++) { - struct vfio_irq_info irq = { .argsz = sizeof(irq) }; - - irq.index = i; - ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq); - if (ret) { - error_printf("vfio: error getting device %s irq info", - vbasedev->name); - goto irq_err; - } else { - trace_vfio_platform_populate_interrupts(irq.index, - irq.count, - irq.flags); - intp = vfio_init_intp(vbasedev, irq); - if (!intp) { - error_report("vfio: Error installing IRQ %d up", i); - goto irq_err; - } - } - } - return 0; -irq_err: - timer_del(vdev->mmap_timer); - QLIST_FOREACH_SAFE(intp, &vdev->intp_list, next, tmp) { - QLIST_REMOVE(intp, next); - g_free(intp); - } -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); - return ret; -} - -/* specialized functions for VFIO Platform devices */ -static VFIODeviceOps vfio_platform_ops = { - .vfio_compute_needs_reset = vfio_platform_compute_needs_reset, - .vfio_hot_reset_multi = vfio_platform_hot_reset_multi, - .vfio_eoi = vfio_platform_eoi, -}; - -/** - * vfio_base_device_init - perform preliminary VFIO setup - * @vbasedev: the VFIO device handle - * - * Implement the VFIO command sequence that allows to discover - * assigned device resources: group extraction, device - * fd retrieval, resource query. - * Precondition: the device name must be initialized - */ -static int vfio_base_device_init(VFIODevice *vbasedev) -{ - VFIOGroup *group; - VFIODevice *vbasedev_iter; - char *tmp, group_path[PATH_MAX], *group_name; - ssize_t len; - struct stat st; - int groupid; - int ret; - - /* @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; - } - - vbasedev->sysfsdev = g_strdup_printf("/sys/bus/platform/devices/%s", - vbasedev->name); - } - - if (stat(vbasedev->sysfsdev, &st) < 0) { - error_report("vfio: error: no such host device: %s", - vbasedev->sysfsdev); - return -errno; - } - - 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; - } - - group_path[len] = 0; - - group_name = basename(group_path); - if (sscanf(group_name, "%d", &groupid) != 1) { - error_report("vfio: error reading %s: %m", group_path); - return -errno; - } - - trace_vfio_platform_base_device_init(vbasedev->name, groupid); - - group = vfio_get_group(groupid, &address_space_memory); - if (!group) { - error_report("vfio: failed to get group %d", groupid); - return -ENOENT; - } - - 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", - vbasedev->name); - vfio_put_group(group); - return -EBUSY; - } - } - ret = vfio_get_device(group, vbasedev->name, vbasedev); - if (ret) { - 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", vbasedev->name); - vfio_put_group(group); - } - - return ret; -} - -/** - * vfio_platform_realize - the device realize function - * @dev: device state pointer - * @errp: error - * - * initialize the device, its memory regions and IRQ structures - * IRQ are started separately - */ -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; - int i, ret; - - vbasedev->type = VFIO_DEVICE_TYPE_PLATFORM; - vbasedev->ops = &vfio_platform_ops; - - trace_vfio_platform_realize(vbasedev->sysfsdev ? - vbasedev->sysfsdev : vbasedev->name, - vdev->compat); - - ret = vfio_base_device_init(vbasedev); - if (ret) { - error_setg(errp, "vfio: vfio_base_device_init failed for %s", - vbasedev->name); - return; - } - - for (i = 0; i < vbasedev->num_regions; i++) { - 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); - } -} - -static const VMStateDescription vfio_platform_vmstate = { - .name = TYPE_VFIO_PLATFORM, - .unmigratable = 1, -}; - -static Property vfio_platform_dev_properties[] = { - DEFINE_PROP_STRING("host", VFIOPlatformDevice, vbasedev.name), - 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), - DEFINE_PROP_END_OF_LIST(), -}; - -static void vfio_platform_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); - - dc->realize = vfio_platform_realize; - dc->props = vfio_platform_dev_properties; - dc->vmsd = &vfio_platform_vmstate; - dc->desc = "VFIO-based platform device assignment"; - sbc->connect_irq_notifier = vfio_start_irqfd_injection; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo vfio_platform_dev_info = { - .name = TYPE_VFIO_PLATFORM, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(VFIOPlatformDevice), - .class_init = vfio_platform_class_init, - .class_size = sizeof(VFIOPlatformDeviceClass), - .abstract = true, -}; - -static void register_vfio_platform_dev_type(void) -{ - type_register_static(&vfio_platform_dev_info); -} - -type_init(register_vfio_platform_dev_type) diff --git a/qemu/hw/virtio/Makefile.objs b/qemu/hw/virtio/Makefile.objs deleted file mode 100644 index 3e2b175da..000000000 --- a/qemu/hw/virtio/Makefile.objs +++ /dev/null @@ -1,7 +0,0 @@ -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-y += virtio.o virtio-balloon.o -obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o diff --git a/qemu/hw/virtio/vhost-backend.c b/qemu/hw/virtio/vhost-backend.c deleted file mode 100644 index b35890289..000000000 --- a/qemu/hw/virtio/vhost-backend.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * vhost-backend - * - * Copyright (c) 2013 Virtual Open Systems Sarl. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#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 - -static int vhost_kernel_call(struct vhost_dev *dev, unsigned long int request, - void *arg) -{ - int fd = (uintptr_t) dev->opaque; - - assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_KERNEL); - - return ioctl(fd, request, arg); -} - -static int vhost_kernel_init(struct vhost_dev *dev, void *opaque) -{ - assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_KERNEL); - - dev->opaque = opaque; - - return 0; -} - -static int vhost_kernel_cleanup(struct vhost_dev *dev) -{ - int fd = (uintptr_t) dev->opaque; - - assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_KERNEL); - - 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_backend_init = vhost_kernel_init, - .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) -{ - int r = 0; - - switch (backend_type) { - case VHOST_BACKEND_TYPE_KERNEL: - dev->vhost_ops = &kernel_ops; - break; - case VHOST_BACKEND_TYPE_USER: - dev->vhost_ops = &user_ops; - break; - default: - error_report("Unknown vhost backend type"); - r = -1; - } - - return r; -} diff --git a/qemu/hw/virtio/vhost-user.c b/qemu/hw/virtio/vhost-user.c deleted file mode 100644 index 5914e8510..000000000 --- a/qemu/hw/virtio/vhost-user.c +++ /dev/null @@ -1,657 +0,0 @@ -/* - * vhost-user - * - * Copyright (c) 2013 Virtual Open Systems Sarl. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#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 -#include -#include -#include - -#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, - VHOST_USER_GET_FEATURES = 1, - VHOST_USER_SET_FEATURES = 2, - VHOST_USER_SET_OWNER = 3, - VHOST_USER_RESET_OWNER = 4, - VHOST_USER_SET_MEM_TABLE = 5, - VHOST_USER_SET_LOG_BASE = 6, - VHOST_USER_SET_LOG_FD = 7, - VHOST_USER_SET_VRING_NUM = 8, - VHOST_USER_SET_VRING_ADDR = 9, - VHOST_USER_SET_VRING_BASE = 10, - VHOST_USER_GET_VRING_BASE = 11, - 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; - -typedef struct VhostUserMemoryRegion { - uint64_t guest_phys_addr; - uint64_t memory_size; - uint64_t userspace_addr; - uint64_t mmap_offset; -} VhostUserMemoryRegion; - -typedef struct VhostUserMemory { - uint32_t nregions; - uint32_t padding; - VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS]; -} VhostUserMemory; - -typedef struct VhostUserLog { - uint64_t mmap_size; - uint64_t mmap_offset; -} VhostUserLog; - -typedef struct VhostUserMsg { - VhostUserRequest request; - -#define VHOST_USER_VERSION_MASK (0x3) -#define VHOST_USER_REPLY_MASK (0x1<<2) - uint32_t flags; - uint32_t size; /* the following payload size */ - union { -#define VHOST_USER_VRING_IDX_MASK (0xff) -#define VHOST_USER_VRING_NOFD_MASK (0x1<<8) - uint64_t u64; - struct vhost_vring_state state; - struct vhost_vring_addr addr; - VhostUserMemory memory; - VhostUserLog log; - } payload; -} QEMU_PACKED VhostUserMsg; - -static VhostUserMsg m __attribute__ ((unused)); -#define VHOST_USER_HDR_SIZE (sizeof(m.request) \ - + sizeof(m.flags) \ - + sizeof(m.size)) - -#define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE) - -/* The version of the protocol we support */ -#define VHOST_USER_VERSION (0x1) - -static bool ioeventfd_enabled(void) -{ - return kvm_enabled() && kvm_eventfds_enabled(); -} - -static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) -{ - CharDriverState *chr = dev->opaque; - uint8_t *p = (uint8_t *) msg; - int r, size = VHOST_USER_HDR_SIZE; - - r = qemu_chr_fe_read_all(chr, p, size); - if (r != size) { - error_report("Failed to read msg header. Read %d instead of %d." - " Original request %d.", r, size, msg->request); - goto fail; - } - - /* validate received flags */ - if (msg->flags != (VHOST_USER_REPLY_MASK | VHOST_USER_VERSION)) { - error_report("Failed to read msg header." - " Flags 0x%x instead of 0x%x.", msg->flags, - VHOST_USER_REPLY_MASK | VHOST_USER_VERSION); - goto fail; - } - - /* validate message size is sane */ - if (msg->size > VHOST_USER_PAYLOAD_SIZE) { - error_report("Failed to read msg header." - " Size %d exceeds the maximum %zu.", msg->size, - VHOST_USER_PAYLOAD_SIZE); - goto fail; - } - - if (msg->size) { - p += VHOST_USER_HDR_SIZE; - size = msg->size; - r = qemu_chr_fe_read_all(chr, p, size); - if (r != size) { - error_report("Failed to read msg payload." - " Read %d instead of %d.", r, msg->size); - goto fail; - } - } - - return 0; - -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); - } - - return qemu_chr_fe_write_all(chr, (const uint8_t *) msg, size) == size ? - 0 : -1; -} - -static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base, - struct vhost_log *log) -{ - int fds[VHOST_MEMORY_MAX_NREGIONS]; - 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), - }; - - if (shmfd && log->fd != -1) { - fds[fd_num++] = log->fd; - } - - vhost_user_write(dev, &msg, fds, fd_num); - - if (shmfd) { - msg.size = 0; - if (vhost_user_read(dev, &msg) < 0) { - return 0; - } - - 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; - } - } - - 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; - } - } - - 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; - } - - 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 (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.size != sizeof(msg.payload.state)) { - error_report("Received bad msg size."); - return -1; - } - - *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; -} - -static int vhost_user_cleanup(struct vhost_dev *dev) -{ - assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); - - dev->opaque = 0; - - 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_backend_init = vhost_user_init, - .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 deleted file mode 100644 index 440071815..000000000 --- a/qemu/hw/virtio/vhost.c +++ /dev/null @@ -1,1301 +0,0 @@ -/* - * vhost support - * - * Copyright Red Hat, Inc. 2010 - * - * Authors: - * Michael S. Tsirkin - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * 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/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 -#include "exec/address-spaces.h" -#include "hw/virtio/virtio-bus.h" -#include "hw/virtio/virtio-access.h" -#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, - uint64_t mfirst, uint64_t mlast, - uint64_t rfirst, uint64_t rlast) -{ - vhost_log_chunk_t *log = dev->log->log; - - uint64_t start = MAX(mfirst, rfirst); - uint64_t end = MIN(mlast, rlast); - vhost_log_chunk_t *from = log + start / VHOST_LOG_CHUNK; - vhost_log_chunk_t *to = log + end / VHOST_LOG_CHUNK + 1; - uint64_t addr = (start / VHOST_LOG_CHUNK) * VHOST_LOG_CHUNK; - - if (end < start) { - return; - } - assert(end / VHOST_LOG_CHUNK < dev->log_size); - assert(start / VHOST_LOG_CHUNK < dev->log_size); - - for (;from < to; ++from) { - vhost_log_chunk_t log; - /* We first check with non-atomic: much cheaper, - * and we expect non-dirty to be the common case. */ - if (!*from) { - addr += VHOST_LOG_CHUNK; - continue; - } - /* Data must be read atomically. We don't really need barrier semantics - * but it's easier to use atomic_* than roll our own. */ - log = atomic_xchg(from, 0); - while (log) { - int bit = ctzl(log); - hwaddr page_addr; - hwaddr section_offset; - hwaddr mr_offset; - page_addr = addr + bit * VHOST_LOG_PAGE; - section_offset = page_addr - section->offset_within_address_space; - mr_offset = section_offset + section->offset_within_region; - memory_region_set_dirty(section->mr, mr_offset, VHOST_LOG_PAGE); - log &= ~(0x1ull << bit); - } - addr += VHOST_LOG_CHUNK; - } -} - -static int vhost_sync_dirty_bitmap(struct vhost_dev *dev, - MemoryRegionSection *section, - hwaddr first, - hwaddr last) -{ - int i; - hwaddr start_addr; - hwaddr end_addr; - - if (!dev->log_enabled || !dev->started) { - return 0; - } - start_addr = section->offset_within_address_space; - end_addr = range_get_last(start_addr, int128_get64(section->size)); - start_addr = MAX(first, start_addr); - end_addr = MIN(last, end_addr); - - for (i = 0; i < dev->mem->nregions; ++i) { - struct vhost_memory_region *reg = dev->mem->regions + i; - vhost_dev_sync_region(dev, section, start_addr, end_addr, - reg->guest_phys_addr, - range_get_last(reg->guest_phys_addr, - reg->memory_size)); - } - for (i = 0; i < dev->nvqs; ++i) { - struct vhost_virtqueue *vq = dev->vqs + i; - vhost_dev_sync_region(dev, section, start_addr, end_addr, vq->used_phys, - range_get_last(vq->used_phys, vq->used_size)); - } - return 0; -} - -static void vhost_log_sync(MemoryListener *listener, - MemoryRegionSection *section) -{ - struct vhost_dev *dev = container_of(listener, struct vhost_dev, - memory_listener); - vhost_sync_dirty_bitmap(dev, section, 0x0, ~0x0ULL); -} - -static void vhost_log_sync_range(struct vhost_dev *dev, - hwaddr first, hwaddr last) -{ - int i; - /* FIXME: this is N^2 in number of sections */ - for (i = 0; i < dev->n_mem_sections; ++i) { - MemoryRegionSection *section = &dev->mem_sections[i]; - vhost_sync_dirty_bitmap(dev, section, first, last); - } -} - -/* Assign/unassign. Keep an unsorted array of non-overlapping - * memory regions in dev->mem. */ -static void vhost_dev_unassign_memory(struct vhost_dev *dev, - uint64_t start_addr, - uint64_t size) -{ - int from, to, n = dev->mem->nregions; - /* Track overlapping/split regions for sanity checking. */ - int overlap_start = 0, overlap_end = 0, overlap_middle = 0, split = 0; - - for (from = 0, to = 0; from < n; ++from, ++to) { - struct vhost_memory_region *reg = dev->mem->regions + to; - uint64_t reglast; - uint64_t memlast; - uint64_t change; - - /* clone old region */ - if (to != from) { - memcpy(reg, dev->mem->regions + from, sizeof *reg); - } - - /* No overlap is simple */ - if (!ranges_overlap(reg->guest_phys_addr, reg->memory_size, - start_addr, size)) { - continue; - } - - /* Split only happens if supplied region - * is in the middle of an existing one. Thus it can not - * overlap with any other existing region. */ - assert(!split); - - reglast = range_get_last(reg->guest_phys_addr, reg->memory_size); - memlast = range_get_last(start_addr, size); - - /* Remove whole region */ - if (start_addr <= reg->guest_phys_addr && memlast >= reglast) { - --dev->mem->nregions; - --to; - ++overlap_middle; - continue; - } - - /* Shrink region */ - if (memlast >= reglast) { - reg->memory_size = start_addr - reg->guest_phys_addr; - assert(reg->memory_size); - assert(!overlap_end); - ++overlap_end; - continue; - } - - /* Shift region */ - if (start_addr <= reg->guest_phys_addr) { - change = memlast + 1 - reg->guest_phys_addr; - reg->memory_size -= change; - reg->guest_phys_addr += change; - reg->userspace_addr += change; - assert(reg->memory_size); - assert(!overlap_start); - ++overlap_start; - continue; - } - - /* This only happens if supplied region - * is in the middle of an existing one. Thus it can not - * overlap with any other existing region. */ - assert(!overlap_start); - assert(!overlap_end); - assert(!overlap_middle); - /* Split region: shrink first part, shift second part. */ - memcpy(dev->mem->regions + n, reg, sizeof *reg); - reg->memory_size = start_addr - reg->guest_phys_addr; - assert(reg->memory_size); - change = memlast + 1 - reg->guest_phys_addr; - reg = dev->mem->regions + n; - reg->memory_size -= change; - assert(reg->memory_size); - reg->guest_phys_addr += change; - reg->userspace_addr += change; - /* Never add more than 1 region */ - assert(dev->mem->nregions == n); - ++dev->mem->nregions; - ++split; - } -} - -/* Called after unassign, so no regions overlap the given range. */ -static void vhost_dev_assign_memory(struct vhost_dev *dev, - uint64_t start_addr, - uint64_t size, - uint64_t uaddr) -{ - int from, to; - struct vhost_memory_region *merged = NULL; - for (from = 0, to = 0; from < dev->mem->nregions; ++from, ++to) { - struct vhost_memory_region *reg = dev->mem->regions + to; - uint64_t prlast, urlast; - uint64_t pmlast, umlast; - uint64_t s, e, u; - - /* clone old region */ - if (to != from) { - memcpy(reg, dev->mem->regions + from, sizeof *reg); - } - prlast = range_get_last(reg->guest_phys_addr, reg->memory_size); - pmlast = range_get_last(start_addr, size); - urlast = range_get_last(reg->userspace_addr, reg->memory_size); - umlast = range_get_last(uaddr, size); - - /* check for overlapping regions: should never happen. */ - assert(prlast < start_addr || pmlast < reg->guest_phys_addr); - /* Not an adjacent or overlapping region - do not merge. */ - if ((prlast + 1 != start_addr || urlast + 1 != uaddr) && - (pmlast + 1 != reg->guest_phys_addr || - umlast + 1 != reg->userspace_addr)) { - 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); - } else { - merged = reg; - } - u = MIN(uaddr, reg->userspace_addr); - s = MIN(start_addr, reg->guest_phys_addr); - e = MAX(pmlast, prlast); - uaddr = merged->userspace_addr = u; - start_addr = merged->guest_phys_addr = s; - size = merged->memory_size = e - s + 1; - assert(merged->memory_size); - } - - if (!merged) { - struct vhost_memory_region *reg = dev->mem->regions + to; - memset(reg, 0, sizeof *reg); - reg->memory_size = size; - assert(reg->memory_size); - reg->guest_phys_addr = start_addr; - reg->userspace_addr = uaddr; - ++to; - } - assert(to <= dev->mem->nregions + 1); - dev->mem->nregions = to; -} - -static uint64_t vhost_get_log_size(struct vhost_dev *dev) -{ - uint64_t log_size = 0; - int i; - for (i = 0; i < dev->mem->nregions; ++i) { - struct vhost_memory_region *reg = dev->mem->regions + i; - uint64_t last = range_get_last(reg->guest_phys_addr, - reg->memory_size); - log_size = MAX(log_size, last / VHOST_LOG_CHUNK + 1); - } - for (i = 0; i < dev->nvqs; ++i) { - struct vhost_virtqueue *vq = dev->vqs + i; - uint64_t last = vq->used_phys + vq->used_size - 1; - log_size = MAX(log_size, last / VHOST_LOG_CHUNK + 1); - } - return log_size; -} - -static struct vhost_log *vhost_log_alloc(uint64_t size, bool share) -{ - 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, bool share) -{ - 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 { - ++log->refcnt; - } - - return log; -} - -static void vhost_log_put(struct vhost_dev *dev, bool sync) -{ - struct vhost_log *log = dev->log; - - if (!log) { - return; - } - - --log->refcnt; - if (log->refcnt == 0) { - /* Sync only the range covered by the old log */ - 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 bool vhost_dev_log_is_shared(struct vhost_dev *dev) -{ - 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; - - /* 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; - dev->log_size = size; -} - -static int vhost_verify_ring_mappings(struct vhost_dev *dev, - uint64_t start_addr, - uint64_t size) -{ - int i; - int r = 0; - - for (i = 0; !r && i < dev->nvqs; ++i) { - struct vhost_virtqueue *vq = dev->vqs + i; - hwaddr l; - void *p; - - if (!ranges_overlap(start_addr, size, vq->ring_phys, vq->ring_size)) { - continue; - } - l = vq->ring_size; - p = cpu_physical_memory_map(vq->ring_phys, &l, 1); - if (!p || l != vq->ring_size) { - fprintf(stderr, "Unable to map ring buffer for ring %d\n", i); - r = -ENOMEM; - } - if (p != vq->ring) { - fprintf(stderr, "Ring buffer relocated for ring %d\n", i); - r = -EBUSY; - } - cpu_physical_memory_unmap(p, l, 0, 0); - } - return r; -} - -static struct vhost_memory_region *vhost_dev_find_reg(struct vhost_dev *dev, - uint64_t start_addr, - uint64_t size) -{ - int i, n = dev->mem->nregions; - for (i = 0; i < n; ++i) { - struct vhost_memory_region *reg = dev->mem->regions + i; - if (ranges_overlap(reg->guest_phys_addr, reg->memory_size, - start_addr, size)) { - return reg; - } - } - return NULL; -} - -static bool vhost_dev_cmp_memory(struct vhost_dev *dev, - uint64_t start_addr, - uint64_t size, - uint64_t uaddr) -{ - struct vhost_memory_region *reg = vhost_dev_find_reg(dev, start_addr, size); - uint64_t reglast; - uint64_t memlast; - - if (!reg) { - return true; - } - - reglast = range_get_last(reg->guest_phys_addr, reg->memory_size); - memlast = range_get_last(start_addr, size); - - /* Need to extend region? */ - if (start_addr < reg->guest_phys_addr || memlast > reglast) { - return true; - } - /* userspace_addr changed? */ - return uaddr != reg->userspace_addr + start_addr - reg->guest_phys_addr; -} - -static void vhost_set_memory(MemoryListener *listener, - MemoryRegionSection *section, - bool add) -{ - struct vhost_dev *dev = container_of(listener, struct vhost_dev, - memory_listener); - hwaddr start_addr = section->offset_within_address_space; - ram_addr_t size = int128_get64(section->size); - bool log_dirty = - memory_region_get_dirty_log_mask(section->mr) & ~(1 << DIRTY_MEMORY_MIGRATION); - int s = offsetof(struct vhost_memory, regions) + - (dev->mem->nregions + 1) * sizeof dev->mem->regions[0]; - void *ram; - - dev->mem = g_realloc(dev->mem, s); - - if (log_dirty) { - add = false; - } - - assert(size); - - /* Optimize no-change case. At least cirrus_vga does this a lot at this time. */ - ram = memory_region_get_ram_ptr(section->mr) + section->offset_within_region; - if (add) { - if (!vhost_dev_cmp_memory(dev, start_addr, size, (uintptr_t)ram)) { - /* Region exists with same address. Nothing to do. */ - return; - } - } else { - if (!vhost_dev_find_reg(dev, start_addr, size)) { - /* Removing region that we don't access. Nothing to do. */ - return; - } - } - - vhost_dev_unassign_memory(dev, start_addr, size); - if (add) { - /* Add given mapping, merging adjacent regions if any */ - vhost_dev_assign_memory(dev, start_addr, size, (uintptr_t)ram); - } else { - /* Remove old mapping for this memory, if any. */ - vhost_dev_unassign_memory(dev, start_addr, size); - } - 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) -{ - return memory_region_is_ram(section->mr); -} - -static void vhost_begin(MemoryListener *listener) -{ - struct vhost_dev *dev = container_of(listener, struct vhost_dev, - memory_listener); - dev->mem_changed_end_addr = 0; - dev->mem_changed_start_addr = -1; -} - -static void vhost_commit(MemoryListener *listener) -{ - struct vhost_dev *dev = container_of(listener, struct vhost_dev, - memory_listener); - hwaddr start_addr = 0; - ram_addr_t size = 0; - uint64_t log_size; - int r; - - if (!dev->memory_changed) { - return; - } - if (!dev->started) { - return; - } - if (dev->mem_changed_start_addr > dev->mem_changed_end_addr) { - return; - } - - if (dev->started) { - start_addr = dev->mem_changed_start_addr; - size = dev->mem_changed_end_addr - dev->mem_changed_start_addr + 1; - - r = vhost_verify_ring_mappings(dev, start_addr, size); - assert(r >= 0); - } - - if (!dev->log_enabled) { - r = dev->vhost_ops->vhost_set_mem_table(dev, dev->mem); - assert(r >= 0); - dev->memory_changed = false; - return; - } - log_size = vhost_get_log_size(dev); - /* We allocate an extra 4K bytes to log, - * to reduce the * number of reallocations. */ -#define VHOST_LOG_BUFFER (0x1000 / sizeof *dev->log) - /* To log more, must increase log size before table update. */ - if (dev->log_size < log_size) { - vhost_dev_log_resize(dev, log_size + VHOST_LOG_BUFFER); - } - 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) { - vhost_dev_log_resize(dev, log_size); - } - dev->memory_changed = false; -} - -static void vhost_region_add(MemoryListener *listener, - MemoryRegionSection *section) -{ - struct vhost_dev *dev = container_of(listener, struct vhost_dev, - memory_listener); - - if (!vhost_section(section)) { - return; - } - - ++dev->n_mem_sections; - dev->mem_sections = g_renew(MemoryRegionSection, dev->mem_sections, - dev->n_mem_sections); - dev->mem_sections[dev->n_mem_sections - 1] = *section; - memory_region_ref(section->mr); - vhost_set_memory(listener, section, true); -} - -static void vhost_region_del(MemoryListener *listener, - MemoryRegionSection *section) -{ - struct vhost_dev *dev = container_of(listener, struct vhost_dev, - memory_listener); - int i; - - if (!vhost_section(section)) { - return; - } - - vhost_set_memory(listener, section, false); - memory_region_unref(section->mr); - for (i = 0; i < dev->n_mem_sections; ++i) { - if (dev->mem_sections[i].offset_within_address_space - == section->offset_within_address_space) { - --dev->n_mem_sections; - memmove(&dev->mem_sections[i], &dev->mem_sections[i+1], - (dev->n_mem_sections - i) * sizeof(*dev->mem_sections)); - break; - } - } -} - -static void vhost_region_nop(MemoryListener *listener, - MemoryRegionSection *section) -{ -} - -static int vhost_virtqueue_set_addr(struct vhost_dev *dev, - struct vhost_virtqueue *vq, - unsigned idx, bool enable_log) -{ - struct vhost_vring_addr addr = { - .index = idx, - .desc_user_addr = (uint64_t)(unsigned long)vq->desc, - .avail_user_addr = (uint64_t)(unsigned long)vq->avail, - .used_user_addr = (uint64_t)(unsigned long)vq->used, - .log_guest_addr = vq->used_phys, - .flags = enable_log ? (1 << VHOST_VRING_F_LOG) : 0, - }; - int r = dev->vhost_ops->vhost_set_vring_addr(dev, &addr); - if (r < 0) { - return -errno; - } - return 0; -} - -static int vhost_dev_set_features(struct vhost_dev *dev, bool enable_log) -{ - uint64_t features = dev->acked_features; - int r; - if (enable_log) { - features |= 0x1ULL << VHOST_F_LOG_ALL; - } - 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, idx; - r = vhost_dev_set_features(dev, enable_log); - if (r < 0) { - goto err_features; - } - for (i = 0; i < dev->nvqs; ++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; - } - } - return 0; -err_vq: - for (; i >= 0; --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); - } - t = vhost_dev_set_features(dev, dev->log_enabled); - assert(t >= 0); -err_features: - return r; -} - -static int vhost_migration_log(MemoryListener *listener, int enable) -{ - struct vhost_dev *dev = container_of(listener, struct vhost_dev, - memory_listener); - int r; - if (!!enable == dev->log_enabled) { - return 0; - } - if (!dev->started) { - dev->log_enabled = enable; - return 0; - } - if (!enable) { - r = vhost_dev_set_log(dev, false); - if (r < 0) { - return r; - } - vhost_log_put(dev, false); - dev->log = NULL; - dev->log_size = 0; - } else { - vhost_dev_log_resize(dev, vhost_get_log_size(dev)); - r = vhost_dev_set_log(dev, true); - if (r < 0) { - return r; - } - } - dev->log_enabled = enable; - return 0; -} - -static void vhost_log_global_start(MemoryListener *listener) -{ - int r; - - r = vhost_migration_log(listener, true); - if (r < 0) { - abort(); - } -} - -static void vhost_log_global_stop(MemoryListener *listener) -{ - int r; - - r = vhost_migration_log(listener, false); - if (r < 0) { - abort(); - } -} - -static void vhost_log_start(MemoryListener *listener, - MemoryRegionSection *section, - int old, int new) -{ - /* FIXME: implement */ -} - -static void vhost_log_stop(MemoryListener *listener, - MemoryRegionSection *section, - int old, int new) -{ - /* 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) -{ - struct vhost_vring_state s = { - .index = vhost_vq_index, - .num = is_big_endian - }; - - if (!dev->vhost_ops->vhost_set_vring_endian(dev, &s)) { - return 0; - } - - if (errno == ENOTTY) { - error_report("vhost does not support cross-endian"); - return -ENOSYS; - } - - return -errno; -} - -static int vhost_virtqueue_start(struct vhost_dev *dev, - struct VirtIODevice *vdev, - struct vhost_virtqueue *vq, - unsigned idx) -{ - hwaddr s, l, a; - int r; - int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx); - struct vhost_vring_file file = { - .index = vhost_vq_index - }; - struct vhost_vring_state state = { - .index = vhost_vq_index - }; - struct VirtQueue *vvq = virtio_get_queue(vdev, idx); - - - vq->num = state.num = virtio_queue_get_num(vdev, idx); - 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_set_vring_base(dev, &state); - if (r) { - return -errno; - } - - if (vhost_needs_vring_endian(vdev)) { - r = vhost_virtqueue_set_vring_endian_legacy(dev, - virtio_is_big_endian(vdev), - vhost_vq_index); - if (r) { - return -errno; - } - } - - s = l = virtio_queue_get_desc_size(vdev, idx); - a = virtio_queue_get_desc_addr(vdev, idx); - vq->desc = cpu_physical_memory_map(a, &l, 0); - if (!vq->desc || l != s) { - r = -ENOMEM; - goto fail_alloc_desc; - } - s = l = virtio_queue_get_avail_size(vdev, idx); - a = virtio_queue_get_avail_addr(vdev, idx); - vq->avail = cpu_physical_memory_map(a, &l, 0); - if (!vq->avail || l != s) { - r = -ENOMEM; - goto fail_alloc_avail; - } - vq->used_size = s = l = virtio_queue_get_used_size(vdev, idx); - vq->used_phys = a = virtio_queue_get_used_addr(vdev, idx); - vq->used = cpu_physical_memory_map(a, &l, 1); - if (!vq->used || l != s) { - r = -ENOMEM; - goto fail_alloc_used; - } - - vq->ring_size = s = l = virtio_queue_get_ring_size(vdev, idx); - vq->ring_phys = a = virtio_queue_get_ring_addr(vdev, idx); - vq->ring = cpu_physical_memory_map(a, &l, 1); - if (!vq->ring || l != s) { - r = -ENOMEM; - goto fail_alloc_ring; - } - - r = vhost_virtqueue_set_addr(dev, vq, vhost_vq_index, dev->log_enabled); - if (r < 0) { - r = -errno; - goto fail_alloc; - } - - file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq)); - r = dev->vhost_ops->vhost_set_vring_kick(dev, &file); - if (r) { - r = -errno; - goto fail_kick; - } - - /* 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: -fail_alloc: - cpu_physical_memory_unmap(vq->ring, virtio_queue_get_ring_size(vdev, idx), - 0, 0); -fail_alloc_ring: - cpu_physical_memory_unmap(vq->used, virtio_queue_get_used_size(vdev, idx), - 0, 0); -fail_alloc_used: - cpu_physical_memory_unmap(vq->avail, virtio_queue_get_avail_size(vdev, idx), - 0, 0); -fail_alloc_avail: - cpu_physical_memory_unmap(vq->desc, virtio_queue_get_desc_size(vdev, idx), - 0, 0); -fail_alloc_desc: - return r; -} - -static void vhost_virtqueue_stop(struct vhost_dev *dev, - struct VirtIODevice *vdev, - struct vhost_virtqueue *vq, - unsigned idx) -{ - int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx); - struct vhost_vring_state state = { - .index = vhost_vq_index, - }; - int r; - - 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); - } - virtio_queue_set_last_avail_idx(vdev, idx, state.num); - virtio_queue_invalidate_signalled_used(vdev, idx); - - /* In the cross-endian case, we need to reset the vring endianness to - * native as legacy devices expect so by default. - */ - if (vhost_needs_vring_endian(vdev)) { - r = vhost_virtqueue_set_vring_endian_legacy(dev, - !virtio_is_big_endian(vdev), - vhost_vq_index); - if (r < 0) { - error_report("failed to reset vring endianness"); - } - } - - assert (r >= 0); - cpu_physical_memory_unmap(vq->ring, virtio_queue_get_ring_size(vdev, idx), - 0, virtio_queue_get_ring_size(vdev, idx)); - cpu_physical_memory_unmap(vq->used, virtio_queue_get_used_size(vdev, idx), - 1, virtio_queue_get_used_size(vdev, idx)); - cpu_physical_memory_unmap(vq->avail, virtio_queue_get_avail_size(vdev, idx), - 0, virtio_queue_get_avail_size(vdev, idx)); - cpu_physical_memory_unmap(vq->desc, virtio_queue_get_desc_size(vdev, idx), - 0, virtio_queue_get_desc_size(vdev, idx)); -} - -static void vhost_eventfd_add(MemoryListener *listener, - MemoryRegionSection *section, - bool match_data, uint64_t data, EventNotifier *e) -{ -} - -static void vhost_eventfd_del(MemoryListener *listener, - MemoryRegionSection *section, - bool match_data, uint64_t data, EventNotifier *e) -{ -} - -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 = vhost_vq_index, - }; - int r = event_notifier_init(&vq->masked_notifier, 0); - if (r < 0) { - return r; - } - - file.fd = event_notifier_get_fd(&vq->masked_notifier); - r = dev->vhost_ops->vhost_set_vring_call(dev, &file); - if (r) { - r = -errno; - goto fail_call; - } - return 0; -fail_call: - event_notifier_cleanup(&vq->masked_notifier); - return r; -} - -static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq) -{ - event_notifier_cleanup(&vq->masked_notifier); -} - -int vhost_dev_init(struct vhost_dev *hdev, void *opaque, - VhostBackendType backend_type) -{ - 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; - } - - if (hdev->vhost_ops->vhost_backend_init(hdev, opaque) < 0) { - close((uintptr_t)opaque); - return -errno; - } - - 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_get_features(hdev, &features); - if (r < 0) { - goto fail; - } - - for (i = 0; i < hdev->nvqs; ++i) { - r = vhost_virtqueue_init(hdev, hdev->vqs + i, hdev->vq_index + i); - if (r < 0) { - goto fail_vq; - } - } - hdev->features = features; - - hdev->memory_listener = (MemoryListener) { - .begin = vhost_begin, - .commit = vhost_commit, - .region_add = vhost_region_add, - .region_del = vhost_region_del, - .region_nop = vhost_region_nop, - .log_start = vhost_log_start, - .log_stop = vhost_log_stop, - .log_sync = vhost_log_sync, - .log_global_start = vhost_log_global_start, - .log_global_stop = vhost_log_global_stop, - .eventfd_add = vhost_eventfd_add, - .eventfd_del = vhost_eventfd_del, - .priority = 10 - }; - - 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; - hdev->log = NULL; - hdev->log_size = 0; - hdev->log_enabled = false; - hdev->started = false; - hdev->memory_changed = false; - memory_listener_register(&hdev->memory_listener, &address_space_memory); - return 0; -fail_vq: - while (--i >= 0) { - vhost_virtqueue_cleanup(hdev->vqs + i); - } -fail: - r = -errno; - hdev->vhost_ops->vhost_backend_cleanup(hdev); - QLIST_REMOVE(hdev, entry); - return r; -} - -void vhost_dev_cleanup(struct vhost_dev *hdev) -{ - int i; - for (i = 0; i < hdev->nvqs; ++i) { - vhost_virtqueue_cleanup(hdev->vqs + i); - } - memory_listener_unregister(&hdev->memory_listener); - if (hdev->migration_blocker) { - migrate_del_blocker(hdev->migration_blocker); - error_free(hdev->migration_blocker); - } - 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. - * Start processing them in vhost in kernel. - */ -int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) -{ - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - VirtioBusState *vbus = VIRTIO_BUS(qbus); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); - int i, r, e; - if (!k->set_host_notifier) { - fprintf(stderr, "binding does not support host notifiers\n"); - r = -ENOSYS; - goto fail; - } - - for (i = 0; i < hdev->nvqs; ++i) { - r = k->set_host_notifier(qbus->parent, hdev->vq_index + i, true); - if (r < 0) { - fprintf(stderr, "vhost VQ %d notifier binding failed: %d\n", i, -r); - goto fail_vq; - } - } - - return 0; -fail_vq: - while (--i >= 0) { - e = k->set_host_notifier(qbus->parent, hdev->vq_index + i, false); - if (e < 0) { - fprintf(stderr, "vhost VQ %d notifier cleanup error: %d\n", i, -r); - fflush(stderr); - } - assert (e >= 0); - } -fail: - return r; -} - -/* Stop processing guest IO notifications in vhost. - * Start processing them in qemu. - * This might actually run the qemu handlers right away, - * so virtio in qemu must be completely setup when this is called. - */ -void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) -{ - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - VirtioBusState *vbus = VIRTIO_BUS(qbus); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); - int i, r; - - for (i = 0; i < hdev->nvqs; ++i) { - r = k->set_host_notifier(qbus->parent, hdev->vq_index + i, false); - if (r < 0) { - fprintf(stderr, "vhost VQ %d notifier cleanup failed: %d\n", i, -r); - fflush(stderr); - } - assert (r >= 0); - } -} - -/* Test and clear event pending status. - * Should be called after unmask to avoid losing events. - */ -bool vhost_virtqueue_pending(struct vhost_dev *hdev, int n) -{ - struct vhost_virtqueue *vq = hdev->vqs + n - hdev->vq_index; - assert(n >= hdev->vq_index && n < hdev->vq_index + hdev->nvqs); - return event_notifier_test_and_clear(&vq->masked_notifier); -} - -/* Mask/unmask events from this vq. */ -void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, - bool mask) -{ - struct VirtQueue *vvq = virtio_get_queue(vdev, n); - int r, index = n - hdev->vq_index; - struct vhost_vring_file file; - - 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)); - } - - file.index = hdev->vhost_ops->vhost_get_vq_index(hdev, n); - r = hdev->vhost_ops->vhost_set_vring_call(hdev, &file); - assert(r >= 0); -} - -uint64_t vhost_get_features(struct vhost_dev *hdev, const int *feature_bits, - uint64_t features) -{ - const int *bit = feature_bits; - while (*bit != VHOST_INVALID_FEATURE_BIT) { - uint64_t bit_mask = (1ULL << *bit); - if (!(hdev->features & bit_mask)) { - features &= ~bit_mask; - } - bit++; - } - return features; -} - -void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits, - uint64_t features) -{ - const int *bit = feature_bits; - while (*bit != VHOST_INVALID_FEATURE_BIT) { - uint64_t bit_mask = (1ULL << *bit); - if (features & bit_mask) { - hdev->acked_features |= bit_mask; - } - bit++; - } -} - -/* Host notifiers must be enabled at this point. */ -int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) -{ - int i, r; - - hdev->started = true; - - r = vhost_dev_set_features(hdev, hdev->log_enabled); - if (r < 0) { - goto fail_features; - } - r = hdev->vhost_ops->vhost_set_mem_table(hdev, hdev->mem); - if (r < 0) { - r = -errno; - goto fail_mem; - } - for (i = 0; i < hdev->nvqs; ++i) { - r = vhost_virtqueue_start(hdev, - vdev, - hdev->vqs + i, - hdev->vq_index + i); - if (r < 0) { - goto fail_vq; - } - } - - if (hdev->log_enabled) { - uint64_t log_base; - - hdev->log_size = vhost_get_log_size(hdev); - 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_set_log_base(hdev, - hdev->log_size ? log_base : 0, - hdev->log); - if (r < 0) { - r = -errno; - goto fail_log; - } - } - - return 0; -fail_log: - vhost_log_put(hdev, false); -fail_vq: - while (--i >= 0) { - vhost_virtqueue_stop(hdev, - vdev, - hdev->vqs + i, - hdev->vq_index + i); - } - i = hdev->nvqs; -fail_mem: -fail_features: - - hdev->started = false; - return r; -} - -/* Host notifiers must be enabled at this point. */ -void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) -{ - int i; - - for (i = 0; i < hdev->nvqs; ++i) { - vhost_virtqueue_stop(hdev, - vdev, - hdev->vqs + i, - hdev->vq_index + i); - } - - vhost_log_put(hdev, true); - hdev->started = false; - hdev->log = NULL; - hdev->log_size = 0; -} - diff --git a/qemu/hw/virtio/virtio-balloon.c b/qemu/hw/virtio/virtio-balloon.c deleted file mode 100644 index 9dbe68179..000000000 --- a/qemu/hw/virtio/virtio-balloon.c +++ /dev/null @@ -1,535 +0,0 @@ -/* - * Virtio Balloon Device - * - * Copyright IBM, Corp. 2008 - * Copyright (C) 2011 Red Hat, Inc. - * Copyright (C) 2011 Amit Shah - * - * Authors: - * Anthony Liguori - * - * 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/iov.h" -#include "qemu/timer.h" -#include "qemu-common.h" -#include "hw/virtio/virtio.h" -#include "hw/i386/pc.h" -#include "sysemu/balloon.h" -#include "hw/virtio/virtio-balloon.h" -#include "sysemu/kvm.h" -#include "exec/address-spaces.h" -#include "qapi/visitor.h" -#include "qapi-event.h" -#include "trace.h" - -#if defined(__linux__) -#include -#endif - -#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 (!qemu_balloon_is_inhibited() && (!kvm_enabled() || - kvm_has_sync_mmu())) { - qemu_madvise(addr, BALLOON_PAGE_SIZE, - deflate ? QEMU_MADV_WILLNEED : QEMU_MADV_DONTNEED); - } -#endif -} - -static const char *balloon_stat_names[] = { - [VIRTIO_BALLOON_S_SWAP_IN] = "stat-swap-in", - [VIRTIO_BALLOON_S_SWAP_OUT] = "stat-swap-out", - [VIRTIO_BALLOON_S_MAJFLT] = "stat-major-faults", - [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 -}; - -/* - * reset_stats - Mark all items in the stats array as unset - * - * This function needs to be called at device initialization and before - * updating to a set of newly-generated stats. This will ensure that no - * stale values stick around in case the guest reports a subset of the supported - * statistics. - */ -static inline void reset_stats(VirtIOBalloon *dev) -{ - int i; - for (i = 0; i < VIRTIO_BALLOON_S_NR; dev->stats[i++] = -1); -} - -static bool balloon_stats_supported(const VirtIOBalloon *s) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(s); - return virtio_vdev_has_feature(vdev, VIRTIO_BALLOON_F_STATS_VQ); -} - -static bool balloon_stats_enabled(const VirtIOBalloon *s) -{ - return s->stats_poll_interval > 0; -} - -static void balloon_stats_destroy_timer(VirtIOBalloon *s) -{ - if (balloon_stats_enabled(s)) { - timer_del(s->stats_timer); - timer_free(s->stats_timer); - s->stats_timer = NULL; - s->stats_poll_interval = 0; - } -} - -static void balloon_stats_change_timer(VirtIOBalloon *s, int64_t secs) -{ - timer_mod(s->stats_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + secs * 1000); -} - -static void balloon_stats_poll_cb(void *opaque) -{ - VirtIOBalloon *s = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(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); - virtio_notify(vdev, s->svq); - g_free(s->stats_vq_elem); - s->stats_vq_elem = NULL; -} - -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, name, NULL, 0, &err); - if (err) { - goto out; - } - visit_type_int(v, "last-update", &s->stats_last_update, &err); - if (err) { - goto out_end; - } - - visit_start_struct(v, "stats", NULL, 0, &err); - if (err) { - goto out_end; - } - 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; - visit_end_struct(v, &err); - -out_end: - error_propagate(errp, err); - err = NULL; - visit_end_struct(v, &err); -out: - error_propagate(errp, err); -} - -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, name, &s->stats_poll_interval, errp); -} - -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, name, &value, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - if (value < 0) { - error_setg(errp, "timer value must be greater than zero"); - return; - } - - if (value > UINT32_MAX) { - error_setg(errp, "timer value is too big"); - return; - } - - if (value == s->stats_poll_interval) { - return; - } - - if (value == 0) { - /* timer=0 disables the timer */ - balloon_stats_destroy_timer(s); - return; - } - - if (balloon_stats_enabled(s)) { - /* timer interval change */ - s->stats_poll_interval = value; - balloon_stats_change_timer(s, value); - return; - } - - /* create a new timer */ - g_assert(s->stats_timer == NULL); - s->stats_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, balloon_stats_poll_cb, s); - s->stats_poll_interval = value; - balloon_stats_change_timer(s, 0); -} - -static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIOBalloon *s = VIRTIO_BALLOON(vdev); - VirtQueueElement *elem; - MemoryRegionSection section; - - 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) { - ram_addr_t pa; - ram_addr_t addr; - int p = virtio_ldl_p(vdev, &pfn); - - pa = (ram_addr_t) p << VIRTIO_BALLOON_PFN_SHIFT; - offset += 4; - - /* FIXME: remove get_system_memory(), but how? */ - section = memory_region_find(get_system_memory(), pa, 1); - if (!int128_nz(section.size) || !memory_region_is_ram(section.mr)) - continue; - - trace_virtio_balloon_handle_output(memory_region_name(section.mr), - pa); - /* Using memory_region_get_ram_ptr is bending the rules a bit, but - should be OK because we only want a single page. */ - addr = section.offset_within_region; - balloon_page(memory_region_get_ram_ptr(section.mr) + addr, - !!(vq == s->dvq)); - memory_region_unref(section.mr); - } - - 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; - VirtIOBalloonStat stat; - size_t offset = 0; - qemu_timeval tv; - - 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). - */ - reset_stats(s); - - while (iov_to_buf(elem->out_sg, elem->out_num, offset, &stat, sizeof(stat)) - == sizeof(stat)) { - uint16_t tag = virtio_tswap16(vdev, stat.tag); - uint64_t val = virtio_tswap64(vdev, stat.val); - - offset += sizeof(stat); - if (tag < VIRTIO_BALLOON_S_NR) - s->stats[tag] = val; - } - s->stats_vq_offset = offset; - - if (qemu_gettimeofday(&tv) < 0) { - fprintf(stderr, "warning: %s: failed to get time of day\n", __func__); - goto out; - } - - s->stats_last_update = tv.tv_sec; - -out: - if (balloon_stats_enabled(s)) { - balloon_stats_change_timer(s, s->stats_poll_interval); - } -} - -static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data) -{ - VirtIOBalloon *dev = VIRTIO_BALLOON(vdev); - struct virtio_balloon_config config; - - config.num_pages = cpu_to_le32(dev->num_pages); - config.actual = cpu_to_le32(dev->actual); - - trace_virtio_balloon_get_config(config.num_pages, config.actual); - 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) -{ - VirtIOBalloon *dev = VIRTIO_BALLOON(vdev); - struct virtio_balloon_config config; - uint32_t oldactual = dev->actual; - ram_addr_t vm_ram_size = get_current_ram_size(); - - memcpy(&config, config_data, sizeof(struct virtio_balloon_config)); - dev->actual = le32_to_cpu(config.actual); - if (dev->actual != oldactual) { - qapi_event_send_balloon_change(vm_ram_size - - ((ram_addr_t) dev->actual << VIRTIO_BALLOON_PFN_SHIFT), - &error_abort); - } - trace_virtio_balloon_set_config(dev->actual, oldactual); -} - -static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f, - Error **errp) -{ - VirtIOBalloon *dev = VIRTIO_BALLOON(vdev); - f |= dev->host_features; - virtio_add_feature(&f, VIRTIO_BALLOON_F_STATS_VQ); - return f; -} - -static void virtio_balloon_stat(void *opaque, BalloonInfo *info) -{ - VirtIOBalloon *dev = opaque; - info->actual = get_current_ram_size() - ((uint64_t) dev->actual << - VIRTIO_BALLOON_PFN_SHIFT); -} - -static void virtio_balloon_to_target(void *opaque, ram_addr_t target) -{ - VirtIOBalloon *dev = VIRTIO_BALLOON(opaque); - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - ram_addr_t vm_ram_size = get_current_ram_size(); - - if (target > vm_ram_size) { - target = vm_ram_size; - } - if (target) { - dev->num_pages = (vm_ram_size - target) >> VIRTIO_BALLOON_PFN_SHIFT; - virtio_notify_config(vdev); - } - trace_virtio_balloon_to_target(target, dev->num_pages); -} - -static void virtio_balloon_save(QEMUFile *f, void *opaque) -{ - virtio_save(VIRTIO_DEVICE(opaque), f); -} - -static void virtio_balloon_save_device(VirtIODevice *vdev, QEMUFile *f) -{ - VirtIOBalloon *s = VIRTIO_BALLOON(vdev); - - qemu_put_be32(f, s->num_pages); - qemu_put_be32(f, s->actual); -} - -static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id) -{ - if (version_id != 1) - return -EINVAL; - - return virtio_load(VIRTIO_DEVICE(opaque), f, version_id); -} - -static int virtio_balloon_load_device(VirtIODevice *vdev, QEMUFile *f, - int version_id) -{ - VirtIOBalloon *s = VIRTIO_BALLOON(vdev); - - 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; -} - -static void virtio_balloon_device_realize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIOBalloon *s = VIRTIO_BALLOON(dev); - int ret; - - virtio_init(vdev, "virtio-balloon", VIRTIO_ID_BALLOON, - sizeof(struct virtio_balloon_config)); - - ret = qemu_add_balloon_handler(virtio_balloon_to_target, - virtio_balloon_stat, s); - - if (ret < 0) { - error_setg(errp, "Only one balloon device is supported"); - virtio_cleanup(vdev); - return; - } - - s->ivq = virtio_add_queue(vdev, 128, virtio_balloon_handle_output); - s->dvq = virtio_add_queue(vdev, 128, virtio_balloon_handle_output); - s->svq = virtio_add_queue(vdev, 128, virtio_balloon_receive_stats); - - reset_stats(s); - - register_savevm(dev, "virtio-balloon", -1, 1, - virtio_balloon_save, virtio_balloon_load, s); -} - -static void virtio_balloon_device_unrealize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIOBalloon *s = VIRTIO_BALLOON(dev); - - balloon_stats_destroy_timer(s); - qemu_remove_balloon_handler(s); - unregister_savevm(dev, "virtio-balloon", s); - 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); - - object_property_add(obj, "guest-stats", "guest statistics", - balloon_stats_get_all, NULL, NULL, s, NULL); - - object_property_add(obj, "guest-stats-polling-interval", "int", - balloon_stats_get_poll_interval, - balloon_stats_set_poll_interval, - NULL, s, NULL); -} - -static Property virtio_balloon_properties[] = { - DEFINE_PROP_BIT("deflate-on-oom", VirtIOBalloon, host_features, - VIRTIO_BALLOON_F_DEFLATE_ON_OOM, false), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_balloon_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - - dc->props = virtio_balloon_properties; - 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; - vdc->save = virtio_balloon_save_device; - vdc->load = virtio_balloon_load_device; -} - -static const TypeInfo virtio_balloon_info = { - .name = TYPE_VIRTIO_BALLOON, - .parent = TYPE_VIRTIO_DEVICE, - .instance_size = sizeof(VirtIOBalloon), - .instance_init = virtio_balloon_instance_init, - .class_init = virtio_balloon_class_init, -}; - -static void virtio_register_types(void) -{ - type_register_static(&virtio_balloon_info); -} - -type_init(virtio_register_types) diff --git a/qemu/hw/virtio/virtio-bus.c b/qemu/hw/virtio/virtio-bus.c deleted file mode 100644 index 574f0e23f..000000000 --- a/qemu/hw/virtio/virtio-bus.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * VirtioBus - * - * Copyright (C) 2012 : GreenSocs Ltd - * http://www.greensocs.com/ , email: info@greensocs.com - * - * Developed by : - * Frederic Konrad - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "qemu/error-report.h" -#include "hw/qdev.h" -#include "hw/virtio/virtio-bus.h" -#include "hw/virtio/virtio.h" - -/* #define DEBUG_VIRTIO_BUS */ - -#ifdef DEBUG_VIRTIO_BUS -#define DPRINTF(fmt, ...) \ -do { printf("virtio_bus: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do { } while (0) -#endif - -/* A VirtIODevice is being plugged */ -void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) -{ - DeviceState *qdev = DEVICE(vdev); - BusState *qbus = BUS(qdev_get_parent_bus(qdev)); - VirtioBusState *bus = VIRTIO_BUS(qbus); - VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); - - DPRINTF("%s: plug device.\n", qbus->name); - - if (klass->device_plugged != NULL) { - klass->device_plugged(qbus->parent, errp); - } - - /* Get the features of the plugged device. */ - 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 */ -void virtio_bus_reset(VirtioBusState *bus) -{ - VirtIODevice *vdev = virtio_bus_get_device(bus); - - DPRINTF("%s: reset device.\n", BUS(bus)->name); - if (vdev != NULL) { - virtio_reset(vdev); - } -} - -/* A VirtIODevice is being unplugged */ -void virtio_bus_device_unplugged(VirtIODevice *vdev) -{ - DeviceState *qdev = DEVICE(vdev); - BusState *qbus = BUS(qdev_get_parent_bus(qdev)); - VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(qbus); - - DPRINTF("%s: remove device.\n", qbus->name); - - if (vdev != NULL) { - if (klass->device_unplugged != NULL) { - klass->device_unplugged(qbus->parent); - } - } -} - -/* Get the device id of the plugged device. */ -uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus) -{ - VirtIODevice *vdev = virtio_bus_get_device(bus); - assert(vdev != NULL); - return vdev->device_id; -} - -/* Get the config_len field of the plugged device. */ -size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus) -{ - VirtIODevice *vdev = virtio_bus_get_device(bus); - assert(vdev != NULL); - return vdev->config_len; -} - -/* Get bad features of the plugged device. */ -uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus) -{ - VirtIODevice *vdev = virtio_bus_get_device(bus); - VirtioDeviceClass *k; - - assert(vdev != NULL); - k = VIRTIO_DEVICE_GET_CLASS(vdev); - if (k->bad_features != NULL) { - return k->bad_features(vdev); - } else { - return 0; - } -} - -/* Get config of the plugged device. */ -void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config) -{ - VirtIODevice *vdev = virtio_bus_get_device(bus); - VirtioDeviceClass *k; - - assert(vdev != NULL); - k = VIRTIO_DEVICE_GET_CLASS(vdev); - if (k->get_config != NULL) { - k->get_config(vdev, config); - } -} - -/* Set config of the plugged device. */ -void virtio_bus_set_vdev_config(VirtioBusState *bus, uint8_t *config) -{ - VirtIODevice *vdev = virtio_bus_get_device(bus); - VirtioDeviceClass *k; - - assert(vdev != NULL); - k = VIRTIO_DEVICE_GET_CLASS(vdev); - if (k->set_config != NULL) { - k->set_config(vdev, config); - } -} - -static char *virtio_bus_get_dev_path(DeviceState *dev) -{ - BusState *bus = qdev_get_parent_bus(dev); - DeviceState *proxy = DEVICE(bus->parent); - return qdev_get_dev_path(proxy); -} - -static char *virtio_bus_get_fw_dev_path(DeviceState *dev) -{ - return NULL; -} - -static void virtio_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *bus_class = BUS_CLASS(klass); - bus_class->get_dev_path = virtio_bus_get_dev_path; - bus_class->get_fw_dev_path = virtio_bus_get_fw_dev_path; -} - -static const TypeInfo virtio_bus_info = { - .name = TYPE_VIRTIO_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(VirtioBusState), - .abstract = true, - .class_size = sizeof(VirtioBusClass), - .class_init = virtio_bus_class_init -}; - -static void virtio_register_types(void) -{ - type_register_static(&virtio_bus_info); -} - -type_init(virtio_register_types) diff --git a/qemu/hw/virtio/virtio-mmio.c b/qemu/hw/virtio/virtio-mmio.c deleted file mode 100644 index d4cd91f8c..000000000 --- a/qemu/hw/virtio/virtio-mmio.c +++ /dev/null @@ -1,580 +0,0 @@ -/* - * Virtio MMIO bindings - * - * Copyright (c) 2011 Linaro Limited - * - * Author: - * Peter Maydell - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "hw/sysbus.h" -#include "hw/virtio/virtio.h" -#include "qemu/host-utils.h" -#include "sysemu/kvm.h" -#include "hw/virtio/virtio-bus.h" -#include "qemu/error-report.h" - -/* #define DEBUG_VIRTIO_MMIO */ - -#ifdef DEBUG_VIRTIO_MMIO - -#define DPRINTF(fmt, ...) \ -do { printf("virtio_mmio: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while (0) -#endif - -/* QOM macros */ -/* virtio-mmio-bus */ -#define TYPE_VIRTIO_MMIO_BUS "virtio-mmio-bus" -#define VIRTIO_MMIO_BUS(obj) \ - OBJECT_CHECK(VirtioBusState, (obj), TYPE_VIRTIO_MMIO_BUS) -#define VIRTIO_MMIO_BUS_GET_CLASS(obj) \ - OBJECT_GET_CLASS(VirtioBusClass, (obj), TYPE_VIRTIO_MMIO_BUS) -#define VIRTIO_MMIO_BUS_CLASS(klass) \ - OBJECT_CLASS_CHECK(VirtioBusClass, (klass), TYPE_VIRTIO_MMIO_BUS) - -/* virtio-mmio */ -#define TYPE_VIRTIO_MMIO "virtio-mmio" -#define VIRTIO_MMIO(obj) \ - OBJECT_CHECK(VirtIOMMIOProxy, (obj), TYPE_VIRTIO_MMIO) - -/* Memory mapped register offsets */ -#define VIRTIO_MMIO_MAGIC 0x0 -#define VIRTIO_MMIO_VERSION 0x4 -#define VIRTIO_MMIO_DEVICEID 0x8 -#define VIRTIO_MMIO_VENDORID 0xc -#define VIRTIO_MMIO_HOSTFEATURES 0x10 -#define VIRTIO_MMIO_HOSTFEATURESSEL 0x14 -#define VIRTIO_MMIO_GUESTFEATURES 0x20 -#define VIRTIO_MMIO_GUESTFEATURESSEL 0x24 -#define VIRTIO_MMIO_GUESTPAGESIZE 0x28 -#define VIRTIO_MMIO_QUEUESEL 0x30 -#define VIRTIO_MMIO_QUEUENUMMAX 0x34 -#define VIRTIO_MMIO_QUEUENUM 0x38 -#define VIRTIO_MMIO_QUEUEALIGN 0x3c -#define VIRTIO_MMIO_QUEUEPFN 0x40 -#define VIRTIO_MMIO_QUEUENOTIFY 0x50 -#define VIRTIO_MMIO_INTERRUPTSTATUS 0x60 -#define VIRTIO_MMIO_INTERRUPTACK 0x64 -#define VIRTIO_MMIO_STATUS 0x70 -/* Device specific config space starts here */ -#define VIRTIO_MMIO_CONFIG 0x100 - -#define VIRT_MAGIC 0x74726976 /* 'virt' */ -#define VIRT_VERSION 1 -#define VIRT_VENDOR 0x554D4551 /* 'QEMU' */ - -typedef struct { - /* Generic */ - SysBusDevice parent_obj; - MemoryRegion iomem; - qemu_irq irq; - /* Guest accessible state needing migration and reset */ - uint32_t host_features_sel; - uint32_t guest_features_sel; - uint32_t guest_page_shift; - /* virtio-bus */ - VirtioBusState bus; - bool ioeventfd_disabled; - bool ioeventfd_started; -} VirtIOMMIOProxy; - -static int virtio_mmio_set_host_notifier_internal(VirtIOMMIOProxy *proxy, - int n, bool assign, - bool set_handler) -{ - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - VirtQueue *vq = virtio_get_queue(vdev, n); - EventNotifier *notifier = virtio_queue_get_host_notifier(vq); - int r = 0; - - if (assign) { - r = event_notifier_init(notifier, 1); - if (r < 0) { - error_report("%s: unable to init event notifier: %d", - __func__, r); - return r; - } - virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler); - memory_region_add_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUENOTIFY, 4, - true, n, notifier); - } else { - memory_region_del_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUENOTIFY, 4, - true, n, notifier); - virtio_queue_set_host_notifier_fd_handler(vq, false, false); - event_notifier_cleanup(notifier); - } - return r; -} - -static void virtio_mmio_start_ioeventfd(VirtIOMMIOProxy *proxy) -{ - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - int n, r; - - if (!kvm_eventfds_enabled() || - proxy->ioeventfd_disabled || - proxy->ioeventfd_started) { - return; - } - - for (n = 0; n < VIRTIO_QUEUE_MAX; n++) { - if (!virtio_queue_get_num(vdev, n)) { - continue; - } - - r = virtio_mmio_set_host_notifier_internal(proxy, n, true, true); - if (r < 0) { - goto assign_error; - } - } - proxy->ioeventfd_started = true; - return; - -assign_error: - while (--n >= 0) { - if (!virtio_queue_get_num(vdev, n)) { - continue; - } - - r = virtio_mmio_set_host_notifier_internal(proxy, n, false, false); - assert(r >= 0); - } - proxy->ioeventfd_started = false; - error_report("%s: failed. Fallback to a userspace (slower).", __func__); -} - -static void virtio_mmio_stop_ioeventfd(VirtIOMMIOProxy *proxy) -{ - int r; - int n; - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - - if (!proxy->ioeventfd_started) { - return; - } - - for (n = 0; n < VIRTIO_QUEUE_MAX; n++) { - if (!virtio_queue_get_num(vdev, n)) { - continue; - } - - r = virtio_mmio_set_host_notifier_internal(proxy, n, false, false); - assert(r >= 0); - } - proxy->ioeventfd_started = false; -} - -static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size) -{ - VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque; - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - - DPRINTF("virtio_mmio_read offset 0x%x\n", (int)offset); - - if (!vdev) { - /* If no backend is present, we treat most registers as - * read-as-zero, except for the magic number, version and - * vendor ID. This is not strictly sanctioned by the virtio - * spec, but it allows us to provide transports with no backend - * plugged in which don't confuse Linux's virtio code: the - * probe won't complain about the bad magic number, but the - * device ID of zero means no backend will claim it. - */ - switch (offset) { - case VIRTIO_MMIO_MAGIC: - return VIRT_MAGIC; - case VIRTIO_MMIO_VERSION: - return VIRT_VERSION; - case VIRTIO_MMIO_VENDORID: - return VIRT_VENDOR; - default: - return 0; - } - } - - if (offset >= VIRTIO_MMIO_CONFIG) { - offset -= VIRTIO_MMIO_CONFIG; - switch (size) { - case 1: - return virtio_config_readb(vdev, offset); - case 2: - return virtio_config_readw(vdev, offset); - case 4: - return virtio_config_readl(vdev, offset); - default: - abort(); - } - } - if (size != 4) { - DPRINTF("wrong size access to register!\n"); - return 0; - } - switch (offset) { - case VIRTIO_MMIO_MAGIC: - return VIRT_MAGIC; - case VIRTIO_MMIO_VERSION: - return VIRT_VERSION; - case VIRTIO_MMIO_DEVICEID: - return vdev->device_id; - case VIRTIO_MMIO_VENDORID: - return VIRT_VENDOR; - case VIRTIO_MMIO_HOSTFEATURES: - if (proxy->host_features_sel) { - return 0; - } - return vdev->host_features; - case VIRTIO_MMIO_QUEUENUMMAX: - if (!virtio_queue_get_num(vdev, vdev->queue_sel)) { - return 0; - } - return VIRTQUEUE_MAX_SIZE; - case VIRTIO_MMIO_QUEUEPFN: - return virtio_queue_get_addr(vdev, vdev->queue_sel) - >> proxy->guest_page_shift; - case VIRTIO_MMIO_INTERRUPTSTATUS: - return vdev->isr; - case VIRTIO_MMIO_STATUS: - return vdev->status; - case VIRTIO_MMIO_HOSTFEATURESSEL: - case VIRTIO_MMIO_GUESTFEATURES: - case VIRTIO_MMIO_GUESTFEATURESSEL: - case VIRTIO_MMIO_GUESTPAGESIZE: - case VIRTIO_MMIO_QUEUESEL: - case VIRTIO_MMIO_QUEUENUM: - case VIRTIO_MMIO_QUEUEALIGN: - case VIRTIO_MMIO_QUEUENOTIFY: - case VIRTIO_MMIO_INTERRUPTACK: - DPRINTF("read of write-only register\n"); - return 0; - default: - DPRINTF("bad register offset\n"); - return 0; - } - return 0; -} - -static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, - unsigned size) -{ - VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque; - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - - DPRINTF("virtio_mmio_write offset 0x%x value 0x%" PRIx64 "\n", - (int)offset, value); - - if (!vdev) { - /* If no backend is present, we just make all registers - * write-ignored. This allows us to provide transports with - * no backend plugged in. - */ - return; - } - - if (offset >= VIRTIO_MMIO_CONFIG) { - offset -= VIRTIO_MMIO_CONFIG; - switch (size) { - case 1: - virtio_config_writeb(vdev, offset, value); - break; - case 2: - virtio_config_writew(vdev, offset, value); - break; - case 4: - virtio_config_writel(vdev, offset, value); - break; - default: - abort(); - } - return; - } - if (size != 4) { - DPRINTF("wrong size access to register!\n"); - return; - } - switch (offset) { - case VIRTIO_MMIO_HOSTFEATURESSEL: - proxy->host_features_sel = value; - break; - case VIRTIO_MMIO_GUESTFEATURES: - if (!proxy->guest_features_sel) { - virtio_set_features(vdev, value); - } - break; - case VIRTIO_MMIO_GUESTFEATURESSEL: - proxy->guest_features_sel = value; - break; - case VIRTIO_MMIO_GUESTPAGESIZE: - proxy->guest_page_shift = ctz32(value); - if (proxy->guest_page_shift > 31) { - proxy->guest_page_shift = 0; - } - DPRINTF("guest page size %" PRIx64 " shift %d\n", value, - proxy->guest_page_shift); - break; - case VIRTIO_MMIO_QUEUESEL: - if (value < VIRTIO_QUEUE_MAX) { - vdev->queue_sel = value; - } - break; - case VIRTIO_MMIO_QUEUENUM: - DPRINTF("mmio_queue write %d max %d\n", (int)value, VIRTQUEUE_MAX_SIZE); - virtio_queue_set_num(vdev, vdev->queue_sel, value); - /* Note: only call this function for legacy devices */ - virtio_queue_update_rings(vdev, vdev->queue_sel); - break; - case VIRTIO_MMIO_QUEUEALIGN: - /* Note: this is only valid for legacy devices */ - virtio_queue_set_align(vdev, vdev->queue_sel, value); - break; - case VIRTIO_MMIO_QUEUEPFN: - if (value == 0) { - virtio_reset(vdev); - } else { - virtio_queue_set_addr(vdev, vdev->queue_sel, - value << proxy->guest_page_shift); - } - break; - case VIRTIO_MMIO_QUEUENOTIFY: - if (value < VIRTIO_QUEUE_MAX) { - virtio_queue_notify(vdev, value); - } - break; - case VIRTIO_MMIO_INTERRUPTACK: - vdev->isr &= ~value; - virtio_update_irq(vdev); - break; - case VIRTIO_MMIO_STATUS: - if (!(value & VIRTIO_CONFIG_S_DRIVER_OK)) { - virtio_mmio_stop_ioeventfd(proxy); - } - - virtio_set_status(vdev, value & 0xff); - - if (value & VIRTIO_CONFIG_S_DRIVER_OK) { - virtio_mmio_start_ioeventfd(proxy); - } - - if (vdev->status == 0) { - virtio_reset(vdev); - } - break; - case VIRTIO_MMIO_MAGIC: - case VIRTIO_MMIO_VERSION: - case VIRTIO_MMIO_DEVICEID: - case VIRTIO_MMIO_VENDORID: - case VIRTIO_MMIO_HOSTFEATURES: - case VIRTIO_MMIO_QUEUENUMMAX: - case VIRTIO_MMIO_INTERRUPTSTATUS: - DPRINTF("write to readonly register\n"); - break; - - default: - DPRINTF("bad register offset\n"); - } -} - -static const MemoryRegionOps virtio_mem_ops = { - .read = virtio_mmio_read, - .write = virtio_mmio_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void virtio_mmio_update_irq(DeviceState *opaque, uint16_t vector) -{ - VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - int level; - - if (!vdev) { - return; - } - level = (vdev->isr != 0); - DPRINTF("virtio_mmio setting IRQ %d\n", level); - qemu_set_irq(proxy->irq, level); -} - -static int virtio_mmio_load_config(DeviceState *opaque, QEMUFile *f) -{ - VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); - - proxy->host_features_sel = qemu_get_be32(f); - proxy->guest_features_sel = qemu_get_be32(f); - proxy->guest_page_shift = qemu_get_be32(f); - return 0; -} - -static void virtio_mmio_save_config(DeviceState *opaque, QEMUFile *f) -{ - VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); - - qemu_put_be32(f, proxy->host_features_sel); - qemu_put_be32(f, proxy->guest_features_sel); - qemu_put_be32(f, proxy->guest_page_shift); -} - -static void virtio_mmio_reset(DeviceState *d) -{ - VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); - - virtio_mmio_stop_ioeventfd(proxy); - virtio_bus_reset(&proxy->bus); - proxy->host_features_sel = 0; - proxy->guest_features_sel = 0; - proxy->guest_page_shift = 0; -} - -static int virtio_mmio_set_guest_notifier(DeviceState *d, int n, bool assign, - bool with_irqfd) -{ - VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); - VirtQueue *vq = virtio_get_queue(vdev, n); - EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); - - if (assign) { - int r = event_notifier_init(notifier, 0); - if (r < 0) { - return r; - } - virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd); - } else { - virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd); - event_notifier_cleanup(notifier); - } - - if (vdc->guest_notifier_mask) { - vdc->guest_notifier_mask(vdev, n, !assign); - } - - return 0; -} - -static int virtio_mmio_set_guest_notifiers(DeviceState *d, int nvqs, - bool assign) -{ - VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - /* TODO: need to check if kvm-arm supports irqfd */ - bool with_irqfd = false; - int r, n; - - nvqs = MIN(nvqs, VIRTIO_QUEUE_MAX); - - for (n = 0; n < nvqs; n++) { - if (!virtio_queue_get_num(vdev, n)) { - break; - } - - r = virtio_mmio_set_guest_notifier(d, n, assign, with_irqfd); - if (r < 0) { - goto assign_error; - } - } - - return 0; - -assign_error: - /* We get here on assignment failure. Recover by undoing for VQs 0 .. n. */ - assert(assign); - while (--n >= 0) { - virtio_mmio_set_guest_notifier(d, n, !assign, false); - } - return r; -} - -static int virtio_mmio_set_host_notifier(DeviceState *opaque, int n, - bool assign) -{ - VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); - - /* Stop using ioeventfd for virtqueue kick if the device starts using host - * notifiers. This makes it easy to avoid stepping on each others' toes. - */ - proxy->ioeventfd_disabled = assign; - if (assign) { - virtio_mmio_stop_ioeventfd(proxy); - } - /* We don't need to start here: it's not needed because backend - * currently only stops on status change away from ok, - * reset, vmstop and such. If we do add code to start here, - * need to check vmstate, device state etc. */ - return virtio_mmio_set_host_notifier_internal(proxy, n, assign, false); -} - -/* virtio-mmio device */ - -static void virtio_mmio_realizefn(DeviceState *d, Error **errp) -{ - VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); - SysBusDevice *sbd = SYS_BUS_DEVICE(d); - - qbus_create_inplace(&proxy->bus, sizeof(proxy->bus), TYPE_VIRTIO_MMIO_BUS, - d, NULL); - sysbus_init_irq(sbd, &proxy->irq); - memory_region_init_io(&proxy->iomem, OBJECT(d), &virtio_mem_ops, proxy, - TYPE_VIRTIO_MMIO, 0x200); - sysbus_init_mmio(sbd, &proxy->iomem); -} - -static void virtio_mmio_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = virtio_mmio_realizefn; - dc->reset = virtio_mmio_reset; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo virtio_mmio_info = { - .name = TYPE_VIRTIO_MMIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(VirtIOMMIOProxy), - .class_init = virtio_mmio_class_init, -}; - -/* virtio-mmio-bus. */ - -static void virtio_mmio_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *bus_class = BUS_CLASS(klass); - VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); - - k->notify = virtio_mmio_update_irq; - k->save_config = virtio_mmio_save_config; - k->load_config = virtio_mmio_load_config; - k->set_host_notifier = virtio_mmio_set_host_notifier; - k->set_guest_notifiers = virtio_mmio_set_guest_notifiers; - k->has_variable_vring_alignment = true; - bus_class->max_dev = 1; -} - -static const TypeInfo virtio_mmio_bus_info = { - .name = TYPE_VIRTIO_MMIO_BUS, - .parent = TYPE_VIRTIO_BUS, - .instance_size = sizeof(VirtioBusState), - .class_init = virtio_mmio_bus_class_init, -}; - -static void virtio_mmio_register_types(void) -{ - type_register_static(&virtio_mmio_bus_info); - type_register_static(&virtio_mmio_info); -} - -type_init(virtio_mmio_register_types) diff --git a/qemu/hw/virtio/virtio-pci.c b/qemu/hw/virtio/virtio-pci.c deleted file mode 100644 index bfedbbf17..000000000 --- a/qemu/hw/virtio/virtio-pci.c +++ /dev/null @@ -1,2534 +0,0 @@ -/* - * Virtio PCI Bindings - * - * Copyright IBM, Corp. 2007 - * Copyright (c) 2009 CodeSourcery - * - * Authors: - * Anthony Liguori - * Paul Brook - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * 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 "standard-headers/linux/virtio_pci.h" -#include "hw/virtio/virtio.h" -#include "hw/virtio/virtio-blk.h" -#include "hw/virtio/virtio-net.h" -#include "hw/virtio/virtio-serial.h" -#include "hw/virtio/virtio-scsi.h" -#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" -#include "hw/loader.h" -#include "sysemu/kvm.h" -#include "sysemu/block-backend.h" -#include "virtio-pci.h" -#include "qemu/range.h" -#include "hw/virtio/virtio-bus.h" -#include "qapi/visitor.h" - -#define VIRTIO_PCI_REGION_SIZE(dev) VIRTIO_PCI_CONFIG_OFF(msix_present(dev)) - -#undef VIRTIO_PCI_CONFIG - -/* The remaining space is defined by each driver as the per-driver - * configuration space */ -#define VIRTIO_PCI_CONFIG_SIZE(dev) VIRTIO_PCI_CONFIG_OFF(msix_enabled(dev)) - -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. */ -static inline VirtIOPCIProxy *to_virtio_pci_proxy(DeviceState *d) -{ - return container_of(d, VirtIOPCIProxy, pci_dev.qdev); -} - -/* DeviceState to VirtIOPCIProxy. Note: used on datapath, - * be careful and test performance if you change this. - */ -static inline VirtIOPCIProxy *to_virtio_pci_proxy_fast(DeviceState *d) -{ - return container_of(d, VirtIOPCIProxy, pci_dev.qdev); -} - -static void virtio_pci_notify(DeviceState *d, uint16_t vector) -{ - VirtIOPCIProxy *proxy = to_virtio_pci_proxy_fast(d); - - if (msix_enabled(&proxy->pci_dev)) - msix_notify(&proxy->pci_dev, vector); - else { - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - pci_set_irq(&proxy->pci_dev, vdev->isr & 1); - } -} - -static void virtio_pci_save_config(DeviceState *d, QEMUFile *f) -{ - VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - - pci_device_save(&proxy->pci_dev, f); - msix_save(&proxy->pci_dev, f); - if (msix_present(&proxy->pci_dev)) - 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); - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - - if (msix_present(&proxy->pci_dev)) - qemu_put_be16(f, virtio_queue_vector(vdev, n)); -} - -static int virtio_pci_load_config(DeviceState *d, QEMUFile *f) -{ - VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - - int ret; - ret = pci_device_load(&proxy->pci_dev, f); - if (ret) { - return ret; - } - msix_unuse_all_vectors(&proxy->pci_dev); - msix_load(&proxy->pci_dev, f); - if (msix_present(&proxy->pci_dev)) { - qemu_get_be16s(f, &vdev->config_vector); - } else { - vdev->config_vector = VIRTIO_NO_VECTOR; - } - if (vdev->config_vector != VIRTIO_NO_VECTOR) { - return msix_vector_use(&proxy->pci_dev, vdev->config_vector); - } - return 0; -} - -static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f) -{ - VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - - uint16_t vector; - if (msix_present(&proxy->pci_dev)) { - qemu_get_be16s(f, &vector); - } else { - vector = VIRTIO_NO_VECTOR; - } - virtio_queue_set_vector(vdev, n, vector); - if (vector != VIRTIO_NO_VECTOR) { - return msix_vector_use(&proxy->pci_dev, vector); - } - - return 0; -} - -#define QEMU_VIRTIO_PCI_QUEUE_MEM_MULT 0x1000 - -static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy, - int n, bool assign, bool set_handler) -{ - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - VirtQueue *vq = virtio_get_queue(vdev, n); - 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); - hwaddr legacy_addr = VIRTIO_PCI_QUEUE_NOTIFY; - int r = 0; - - if (assign) { - r = event_notifier_init(notifier, 1); - if (r < 0) { - error_report("%s: unable to init event notifier: %d", - __func__, r); - return r; - } - virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler); - if (modern) { - 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, - true, n, notifier); - } - } else { - if (modern) { - 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, - true, n, notifier); - } - virtio_queue_set_host_notifier_fd_handler(vq, false, false); - event_notifier_cleanup(notifier); - } - return r; -} - -static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy) -{ - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - int n, r; - - if (!(proxy->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD) || - proxy->ioeventfd_disabled || - proxy->ioeventfd_started) { - return; - } - - for (n = 0; n < VIRTIO_QUEUE_MAX; n++) { - if (!virtio_queue_get_num(vdev, n)) { - continue; - } - - r = virtio_pci_set_host_notifier_internal(proxy, n, true, true); - if (r < 0) { - goto assign_error; - } - } - proxy->ioeventfd_started = true; - return; - -assign_error: - while (--n >= 0) { - if (!virtio_queue_get_num(vdev, n)) { - continue; - } - - r = virtio_pci_set_host_notifier_internal(proxy, n, false, false); - assert(r >= 0); - } - proxy->ioeventfd_started = false; - error_report("%s: failed. Fallback to a userspace (slower).", __func__); -} - -static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy) -{ - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - int r; - int n; - - if (!proxy->ioeventfd_started) { - return; - } - - for (n = 0; n < VIRTIO_QUEUE_MAX; n++) { - if (!virtio_queue_get_num(vdev, n)) { - continue; - } - - r = virtio_pci_set_host_notifier_internal(proxy, n, false, false); - assert(r >= 0); - } - proxy->ioeventfd_started = false; -} - -static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) -{ - VirtIOPCIProxy *proxy = opaque; - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - hwaddr pa; - - switch (addr) { - case VIRTIO_PCI_GUEST_FEATURES: - /* Guest does not negotiate properly? We have to assume nothing. */ - if (val & (1 << VIRTIO_F_BAD_FEATURE)) { - val = virtio_bus_get_vdev_bad_features(&proxy->bus); - } - virtio_set_features(vdev, val); - break; - case VIRTIO_PCI_QUEUE_PFN: - pa = (hwaddr)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT; - if (pa == 0) { - virtio_pci_reset(DEVICE(proxy)); - } - else - virtio_queue_set_addr(vdev, vdev->queue_sel, pa); - break; - case VIRTIO_PCI_QUEUE_SEL: - if (val < VIRTIO_QUEUE_MAX) - vdev->queue_sel = val; - break; - case VIRTIO_PCI_QUEUE_NOTIFY: - if (val < VIRTIO_QUEUE_MAX) { - virtio_queue_notify(vdev, val); - } - break; - case VIRTIO_PCI_STATUS: - if (!(val & VIRTIO_CONFIG_S_DRIVER_OK)) { - virtio_pci_stop_ioeventfd(proxy); - } - - virtio_set_status(vdev, val & 0xFF); - - if (val & VIRTIO_CONFIG_S_DRIVER_OK) { - virtio_pci_start_ioeventfd(proxy); - } - - if (vdev->status == 0) { - virtio_pci_reset(DEVICE(proxy)); - } - - /* Linux before 2.6.34 drives the device without enabling - the PCI device bus master bit. Enable it automatically - for the guest. This is a PCI spec violation but so is - initiating DMA with bus master bit clear. */ - if (val == (VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER)) { - pci_default_write_config(&proxy->pci_dev, PCI_COMMAND, - proxy->pci_dev.config[PCI_COMMAND] | - PCI_COMMAND_MASTER, 1); - } - break; - case VIRTIO_MSI_CONFIG_VECTOR: - msix_vector_unuse(&proxy->pci_dev, vdev->config_vector); - /* Make it possible for guest to discover an error took place. */ - if (msix_vector_use(&proxy->pci_dev, val) < 0) - val = VIRTIO_NO_VECTOR; - vdev->config_vector = val; - break; - case VIRTIO_MSI_QUEUE_VECTOR: - msix_vector_unuse(&proxy->pci_dev, - virtio_queue_vector(vdev, vdev->queue_sel)); - /* Make it possible for guest to discover an error took place. */ - if (msix_vector_use(&proxy->pci_dev, val) < 0) - val = VIRTIO_NO_VECTOR; - virtio_queue_set_vector(vdev, vdev->queue_sel, val); - break; - default: - error_report("%s: unexpected address 0x%x value 0x%x", - __func__, addr, val); - break; - } -} - -static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr) -{ - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - uint32_t ret = 0xFFFFFFFF; - - switch (addr) { - case VIRTIO_PCI_HOST_FEATURES: - ret = vdev->host_features; - break; - case VIRTIO_PCI_GUEST_FEATURES: - ret = vdev->guest_features; - break; - case VIRTIO_PCI_QUEUE_PFN: - ret = virtio_queue_get_addr(vdev, vdev->queue_sel) - >> VIRTIO_PCI_QUEUE_ADDR_SHIFT; - break; - case VIRTIO_PCI_QUEUE_NUM: - ret = virtio_queue_get_num(vdev, vdev->queue_sel); - break; - case VIRTIO_PCI_QUEUE_SEL: - ret = vdev->queue_sel; - break; - case VIRTIO_PCI_STATUS: - ret = vdev->status; - break; - case VIRTIO_PCI_ISR: - /* reading from the ISR also clears it. */ - ret = vdev->isr; - vdev->isr = 0; - pci_irq_deassert(&proxy->pci_dev); - break; - case VIRTIO_MSI_CONFIG_VECTOR: - ret = vdev->config_vector; - break; - case VIRTIO_MSI_QUEUE_VECTOR: - ret = virtio_queue_vector(vdev, vdev->queue_sel); - break; - default: - break; - } - - return ret; -} - -static uint64_t virtio_pci_config_read(void *opaque, hwaddr addr, - unsigned size) -{ - VirtIOPCIProxy *proxy = opaque; - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - uint32_t config = VIRTIO_PCI_CONFIG_SIZE(&proxy->pci_dev); - uint64_t val = 0; - if (addr < config) { - return virtio_ioport_read(proxy, addr); - } - addr -= config; - - switch (size) { - case 1: - val = virtio_config_readb(vdev, addr); - break; - case 2: - val = virtio_config_readw(vdev, addr); - if (virtio_is_big_endian(vdev)) { - val = bswap16(val); - } - break; - case 4: - val = virtio_config_readl(vdev, addr); - if (virtio_is_big_endian(vdev)) { - val = bswap32(val); - } - break; - } - return val; -} - -static void virtio_pci_config_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - VirtIOPCIProxy *proxy = opaque; - uint32_t config = VIRTIO_PCI_CONFIG_SIZE(&proxy->pci_dev); - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - if (addr < config) { - virtio_ioport_write(proxy, addr, val); - return; - } - addr -= config; - /* - * Virtio-PCI is odd. Ioports are LE but config space is target native - * endian. - */ - switch (size) { - case 1: - virtio_config_writeb(vdev, addr, val); - break; - case 2: - if (virtio_is_big_endian(vdev)) { - val = bswap16(val); - } - virtio_config_writew(vdev, addr, val); - break; - case 4: - if (virtio_is_big_endian(vdev)) { - val = bswap32(val); - } - virtio_config_writel(vdev, addr, val); - break; - } -} - -static const MemoryRegionOps virtio_pci_config_ops = { - .read = virtio_pci_config_read, - .write = virtio_pci_config_write, - .impl = { - .min_access_size = 1, - .max_access_size = 4, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -/* Below are generic functions to do memcpy from/to an address space, - * without byteswaps, with input validation. - * - * As regular address_space_* APIs all do some kind of byteswap at least for - * some host/target combinations, we are forced to explicitly convert to a - * known-endianness integer value. - * It doesn't really matter which endian format to go through, so the code - * below selects the endian that causes the least amount of work on the given - * host. - * - * Note: host pointer must be aligned. - */ -static -void virtio_address_space_write(AddressSpace *as, hwaddr addr, - const uint8_t *buf, int len) -{ - uint32_t val; - - /* address_space_* APIs assume an aligned address. - * As address is under guest control, handle illegal values. - */ - addr &= ~(len - 1); - - /* Make sure caller aligned buf properly */ - assert(!(((uintptr_t)buf) & (len - 1))); - - switch (len) { - case 1: - val = pci_get_byte(buf); - address_space_stb(as, addr, val, MEMTXATTRS_UNSPECIFIED, NULL); - break; - case 2: - val = pci_get_word(buf); - address_space_stw_le(as, addr, val, MEMTXATTRS_UNSPECIFIED, NULL); - break; - case 4: - val = pci_get_long(buf); - address_space_stl_le(as, addr, val, MEMTXATTRS_UNSPECIFIED, NULL); - break; - default: - /* As length is under guest control, handle illegal values. */ - break; - } -} - -static void -virtio_address_space_read(AddressSpace *as, hwaddr addr, uint8_t *buf, int len) -{ - uint32_t val; - - /* address_space_* APIs assume an aligned address. - * As address is under guest control, handle illegal values. - */ - addr &= ~(len - 1); - - /* Make sure caller aligned buf properly */ - assert(!(((uintptr_t)buf) & (len - 1))); - - switch (len) { - case 1: - val = address_space_ldub(as, addr, MEMTXATTRS_UNSPECIFIED, NULL); - pci_set_byte(buf, val); - break; - case 2: - val = address_space_lduw_le(as, addr, MEMTXATTRS_UNSPECIFIED, NULL); - pci_set_word(buf, val); - break; - case 4: - val = address_space_ldl_le(as, addr, MEMTXATTRS_UNSPECIFIED, NULL); - pci_set_long(buf, val); - break; - default: - /* As length is under guest control, handle illegal values. */ - break; - } -} - -static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, - uint32_t val, int len) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - struct virtio_pci_cfg_cap *cfg; - - pci_default_write_config(pci_dev, address, val, len); - - if (range_covers_byte(address, len, PCI_COMMAND) && - !(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { - virtio_pci_stop_ioeventfd(proxy); - virtio_set_status(vdev, vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK); - } - - if (proxy->config_cap && - ranges_overlap(address, len, proxy->config_cap + offsetof(struct virtio_pci_cfg_cap, - pci_cfg_data), - sizeof cfg->pci_cfg_data)) { - uint32_t off; - uint32_t len; - - cfg = (void *)(proxy->pci_dev.config + proxy->config_cap); - off = le32_to_cpu(cfg->cap.offset); - len = le32_to_cpu(cfg->cap.length); - - if (len == 1 || len == 2 || len == 4) { - assert(len <= sizeof cfg->pci_cfg_data); - virtio_address_space_write(&proxy->modern_as, off, - cfg->pci_cfg_data, len); - } - } -} - -static uint32_t virtio_read_config(PCIDevice *pci_dev, - uint32_t address, int len) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - struct virtio_pci_cfg_cap *cfg; - - if (proxy->config_cap && - ranges_overlap(address, len, proxy->config_cap + offsetof(struct virtio_pci_cfg_cap, - pci_cfg_data), - sizeof cfg->pci_cfg_data)) { - uint32_t off; - uint32_t len; - - cfg = (void *)(proxy->pci_dev.config + proxy->config_cap); - off = le32_to_cpu(cfg->cap.offset); - len = le32_to_cpu(cfg->cap.length); - - if (len == 1 || len == 2 || len == 4) { - assert(len <= sizeof cfg->pci_cfg_data); - virtio_address_space_read(&proxy->modern_as, off, - cfg->pci_cfg_data, len); - } - } - - return pci_default_read_config(pci_dev, address, len); -} - -static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy, - unsigned int queue_no, - unsigned int vector, - MSIMessage msg) -{ - VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; - int ret; - - if (irqfd->users == 0) { - ret = kvm_irqchip_add_msi_route(kvm_state, msg, &proxy->pci_dev); - if (ret < 0) { - return ret; - } - irqfd->virq = ret; - } - irqfd->users++; - return 0; -} - -static void kvm_virtio_pci_vq_vector_release(VirtIOPCIProxy *proxy, - unsigned int vector) -{ - VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; - if (--irqfd->users == 0) { - kvm_irqchip_release_virq(kvm_state, irqfd->virq); - } -} - -static int kvm_virtio_pci_irqfd_use(VirtIOPCIProxy *proxy, - unsigned int queue_no, - unsigned int vector) -{ - VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - VirtQueue *vq = virtio_get_queue(vdev, queue_no); - EventNotifier *n = virtio_queue_get_guest_notifier(vq); - int ret; - ret = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, irqfd->virq); - return ret; -} - -static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy, - unsigned int queue_no, - unsigned int vector) -{ - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - VirtQueue *vq = virtio_get_queue(vdev, queue_no); - EventNotifier *n = virtio_queue_get_guest_notifier(vq); - VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; - int ret; - - ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, irqfd->virq); - assert(ret == 0); -} - -static int kvm_virtio_pci_vector_use(VirtIOPCIProxy *proxy, int nvqs) -{ - PCIDevice *dev = &proxy->pci_dev; - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - unsigned int vector; - int ret, queue_no; - MSIMessage msg; - - for (queue_no = 0; queue_no < nvqs; queue_no++) { - if (!virtio_queue_get_num(vdev, queue_no)) { - break; - } - vector = virtio_queue_vector(vdev, queue_no); - if (vector >= msix_nr_vectors_allocated(dev)) { - continue; - } - msg = msix_get_message(dev, vector); - ret = kvm_virtio_pci_vq_vector_use(proxy, queue_no, vector, msg); - if (ret < 0) { - goto undo; - } - /* If guest supports masking, set up irqfd now. - * Otherwise, delay until unmasked in the frontend. - */ - 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); - goto undo; - } - } - } - return 0; - -undo: - while (--queue_no >= 0) { - vector = virtio_queue_vector(vdev, queue_no); - if (vector >= msix_nr_vectors_allocated(dev)) { - continue; - } - 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); - } - return ret; -} - -static void kvm_virtio_pci_vector_release(VirtIOPCIProxy *proxy, int nvqs) -{ - PCIDevice *dev = &proxy->pci_dev; - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - unsigned int vector; - int queue_no; - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - - for (queue_no = 0; queue_no < nvqs; queue_no++) { - if (!virtio_queue_get_num(vdev, queue_no)) { - break; - } - vector = virtio_queue_vector(vdev, queue_no); - if (vector >= msix_nr_vectors_allocated(dev)) { - continue; - } - /* If guest supports masking, clean up irqfd now. - * Otherwise, it was cleaned when masked in the frontend. - */ - 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); - } -} - -static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy, - unsigned int queue_no, - unsigned int vector, - MSIMessage msg) -{ - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - VirtQueue *vq = virtio_get_queue(vdev, queue_no); - EventNotifier *n = virtio_queue_get_guest_notifier(vq); - VirtIOIRQFD *irqfd; - int ret = 0; - - 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, - &proxy->pci_dev); - if (ret < 0) { - return ret; - } - } - } - - /* If guest supports masking, irqfd is already setup, unmask it. - * Otherwise, set it up now. - */ - 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 && - k->guest_notifier_pending(vdev, queue_no)) { - event_notifier_set(n); - } - } else { - ret = kvm_virtio_pci_irqfd_use(proxy, queue_no, vector); - } - return ret; -} - -static void virtio_pci_vq_vector_mask(VirtIOPCIProxy *proxy, - unsigned int queue_no, - unsigned int vector) -{ - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - - /* If guest supports masking, keep irqfd but mask it. - * Otherwise, clean it up now. - */ - 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); - } -} - -static int virtio_pci_vector_unmask(PCIDevice *dev, unsigned vector, - MSIMessage msg) -{ - VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev); - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - VirtQueue *vq = virtio_vector_first_queue(vdev, vector); - int ret, index, unmasked = 0; - - while (vq) { - index = virtio_get_queue_index(vq); - if (!virtio_queue_get_num(vdev, index)) { - break; - } - if (index < proxy->nvqs_with_notifiers) { - ret = virtio_pci_vq_vector_unmask(proxy, index, vector, msg); - if (ret < 0) { - goto undo; - } - ++unmasked; - } - vq = virtio_vector_next_queue(vq); - } - - return 0; - -undo: - vq = virtio_vector_first_queue(vdev, vector); - while (vq && unmasked >= 0) { - index = virtio_get_queue_index(vq); - if (index < proxy->nvqs_with_notifiers) { - virtio_pci_vq_vector_mask(proxy, index, vector); - --unmasked; - } - vq = virtio_vector_next_queue(vq); - } - return ret; -} - -static void virtio_pci_vector_mask(PCIDevice *dev, unsigned vector) -{ - VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev); - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - VirtQueue *vq = virtio_vector_first_queue(vdev, vector); - int index; - - while (vq) { - index = virtio_get_queue_index(vq); - if (!virtio_queue_get_num(vdev, index)) { - break; - } - if (index < proxy->nvqs_with_notifiers) { - virtio_pci_vq_vector_mask(proxy, index, vector); - } - vq = virtio_vector_next_queue(vq); - } -} - -static void virtio_pci_vector_poll(PCIDevice *dev, - unsigned int vector_start, - unsigned int vector_end) -{ - VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev); - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - int queue_no; - unsigned int vector; - EventNotifier *notifier; - VirtQueue *vq; - - for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) { - if (!virtio_queue_get_num(vdev, queue_no)) { - break; - } - vector = virtio_queue_vector(vdev, queue_no); - if (vector < vector_start || vector >= vector_end || - !msix_is_masked(dev, vector)) { - continue; - } - vq = virtio_get_queue(vdev, queue_no); - notifier = virtio_queue_get_guest_notifier(vq); - if (k->guest_notifier_pending) { - if (k->guest_notifier_pending(vdev, queue_no)) { - msix_set_pending(dev, vector); - } - } else if (event_notifier_test_and_clear(notifier)) { - msix_set_pending(dev, vector); - } - } -} - -static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign, - bool with_irqfd) -{ - VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); - VirtQueue *vq = virtio_get_queue(vdev, n); - EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); - - if (assign) { - int r = event_notifier_init(notifier, 0); - if (r < 0) { - return r; - } - virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd); - } else { - virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd); - event_notifier_cleanup(notifier); - } - - if (!msix_enabled(&proxy->pci_dev) && - vdev->use_guest_notifier_mask && - vdc->guest_notifier_mask) { - vdc->guest_notifier_mask(vdev, n, !assign); - } - - return 0; -} - -static bool virtio_pci_query_guest_notifiers(DeviceState *d) -{ - VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - return msix_enabled(&proxy->pci_dev); -} - -static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign) -{ - VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - int r, n; - bool with_irqfd = msix_enabled(&proxy->pci_dev) && - kvm_msi_via_irqfd_enabled(); - - nvqs = MIN(nvqs, VIRTIO_QUEUE_MAX); - - /* When deassigning, pass a consistent nvqs value - * to avoid leaking notifiers. - */ - assert(assign || nvqs == proxy->nvqs_with_notifiers); - - proxy->nvqs_with_notifiers = nvqs; - - /* Must unset vector notifier while guest notifier is still assigned */ - if ((proxy->vector_irqfd || k->guest_notifier_mask) && !assign) { - msix_unset_vector_notifiers(&proxy->pci_dev); - if (proxy->vector_irqfd) { - kvm_virtio_pci_vector_release(proxy, nvqs); - g_free(proxy->vector_irqfd); - proxy->vector_irqfd = NULL; - } - } - - for (n = 0; n < nvqs; n++) { - if (!virtio_queue_get_num(vdev, n)) { - break; - } - - r = virtio_pci_set_guest_notifier(d, n, assign, with_irqfd); - if (r < 0) { - goto assign_error; - } - } - - /* Must set vector notifier after guest notifier has been assigned */ - if ((with_irqfd || k->guest_notifier_mask) && assign) { - if (with_irqfd) { - proxy->vector_irqfd = - g_malloc0(sizeof(*proxy->vector_irqfd) * - msix_nr_vectors_allocated(&proxy->pci_dev)); - r = kvm_virtio_pci_vector_use(proxy, nvqs); - if (r < 0) { - goto assign_error; - } - } - r = msix_set_vector_notifiers(&proxy->pci_dev, - virtio_pci_vector_unmask, - virtio_pci_vector_mask, - virtio_pci_vector_poll); - if (r < 0) { - goto notifiers_error; - } - } - - return 0; - -notifiers_error: - if (with_irqfd) { - assert(assign); - kvm_virtio_pci_vector_release(proxy, nvqs); - } - -assign_error: - /* We get here on assignment failure. Recover by undoing for VQs 0 .. n. */ - assert(assign); - while (--n >= 0) { - virtio_pci_set_guest_notifier(d, n, !assign, with_irqfd); - } - return r; -} - -static int virtio_pci_set_host_notifier(DeviceState *d, int n, bool assign) -{ - VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - - /* Stop using ioeventfd for virtqueue kick if the device starts using host - * notifiers. This makes it easy to avoid stepping on each others' toes. - */ - proxy->ioeventfd_disabled = assign; - if (assign) { - virtio_pci_stop_ioeventfd(proxy); - } - /* We don't need to start here: it's not needed because backend - * currently only stops on status change away from ok, - * reset, vmstop and such. If we do add code to start here, - * need to check vmstate, device state etc. */ - return virtio_pci_set_host_notifier_internal(proxy, n, assign, false); -} - -static void virtio_pci_vmstate_change(DeviceState *d, bool running) -{ - VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - - if (running) { - /* Old QEMU versions did not set bus master enable on status write. - * Detect DRIVER set and enable it. - */ - if ((proxy->flags & VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION) && - (vdev->status & VIRTIO_CONFIG_S_DRIVER) && - !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { - pci_default_write_config(&proxy->pci_dev, PCI_COMMAND, - proxy->pci_dev.config[PCI_COMMAND] | - PCI_COMMAND_MASTER, 1); - } - virtio_pci_start_ioeventfd(proxy); - } else { - virtio_pci_stop_ioeventfd(proxy); - } -} - -#ifdef CONFIG_VIRTFS -static void virtio_9p_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - V9fsPCIState *dev = VIRTIO_9P_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static Property virtio_9p_pci_properties[] = { - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_9p_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - - k->realize = virtio_9p_pci_realize; - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_9P; - pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; - pcidev_k->class_id = 0x2; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = virtio_9p_pci_properties; -} - -static void virtio_9p_pci_instance_init(Object *obj) -{ - V9fsPCIState *dev = VIRTIO_9P_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_9P); -} - -static const TypeInfo virtio_9p_pci_info = { - .name = TYPE_VIRTIO_9P_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(V9fsPCIState), - .instance_init = virtio_9p_pci_instance_init, - .class_init = virtio_9p_pci_class_init, -}; -#endif /* CONFIG_VIRTFS */ - -/* - * virtio-pci: This is the PCIDevice which has a virtio-pci-bus. - */ - -static int virtio_pci_query_nvectors(DeviceState *d) -{ - VirtIOPCIProxy *proxy = VIRTIO_PCI(d); - - return proxy->nvectors; -} - -static int virtio_pci_add_mem_cap(VirtIOPCIProxy *proxy, - struct virtio_pci_cap *cap) -{ - PCIDevice *dev = &proxy->pci_dev; - int offset; - - offset = pci_add_capability(dev, PCI_CAP_ID_VNDR, 0, cap->cap_len); - assert(offset > 0); - - assert(cap->cap_len >= sizeof *cap); - memcpy(dev->config + offset + PCI_CAP_FLAGS, &cap->cap_len, - cap->cap_len - PCI_CAP_FLAGS); - - return offset; -} - -static uint64_t virtio_pci_common_read(void *opaque, hwaddr addr, - unsigned size) -{ - VirtIOPCIProxy *proxy = opaque; - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - uint32_t val = 0; - int i; - - switch (addr) { - case VIRTIO_PCI_COMMON_DFSELECT: - val = proxy->dfselect; - break; - case VIRTIO_PCI_COMMON_DF: - if (proxy->dfselect <= 1) { - val = (vdev->host_features & ~VIRTIO_LEGACY_FEATURES) >> - (32 * proxy->dfselect); - } - break; - case VIRTIO_PCI_COMMON_GFSELECT: - val = proxy->gfselect; - break; - case VIRTIO_PCI_COMMON_GF: - if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features)) { - val = proxy->guest_features[proxy->gfselect]; - } - break; - case VIRTIO_PCI_COMMON_MSIX: - val = vdev->config_vector; - break; - case VIRTIO_PCI_COMMON_NUMQ: - for (i = 0; i < VIRTIO_QUEUE_MAX; ++i) { - if (virtio_queue_get_num(vdev, i)) { - val = i + 1; - } - } - break; - case VIRTIO_PCI_COMMON_STATUS: - val = vdev->status; - break; - case VIRTIO_PCI_COMMON_CFGGENERATION: - val = vdev->generation; - break; - case VIRTIO_PCI_COMMON_Q_SELECT: - val = vdev->queue_sel; - break; - case VIRTIO_PCI_COMMON_Q_SIZE: - val = virtio_queue_get_num(vdev, vdev->queue_sel); - break; - case VIRTIO_PCI_COMMON_Q_MSIX: - val = virtio_queue_vector(vdev, vdev->queue_sel); - break; - case VIRTIO_PCI_COMMON_Q_ENABLE: - val = proxy->vqs[vdev->queue_sel].enabled; - break; - case VIRTIO_PCI_COMMON_Q_NOFF: - /* Simply map queues in order */ - val = vdev->queue_sel; - break; - case VIRTIO_PCI_COMMON_Q_DESCLO: - val = proxy->vqs[vdev->queue_sel].desc[0]; - break; - case VIRTIO_PCI_COMMON_Q_DESCHI: - val = proxy->vqs[vdev->queue_sel].desc[1]; - break; - case VIRTIO_PCI_COMMON_Q_AVAILLO: - val = proxy->vqs[vdev->queue_sel].avail[0]; - break; - case VIRTIO_PCI_COMMON_Q_AVAILHI: - val = proxy->vqs[vdev->queue_sel].avail[1]; - break; - case VIRTIO_PCI_COMMON_Q_USEDLO: - val = proxy->vqs[vdev->queue_sel].used[0]; - break; - case VIRTIO_PCI_COMMON_Q_USEDHI: - val = proxy->vqs[vdev->queue_sel].used[1]; - break; - default: - val = 0; - } - - return val; -} - -static void virtio_pci_common_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - VirtIOPCIProxy *proxy = opaque; - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - - switch (addr) { - case VIRTIO_PCI_COMMON_DFSELECT: - proxy->dfselect = val; - break; - case VIRTIO_PCI_COMMON_GFSELECT: - proxy->gfselect = val; - break; - case VIRTIO_PCI_COMMON_GF: - if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features)) { - proxy->guest_features[proxy->gfselect] = val; - virtio_set_features(vdev, - (((uint64_t)proxy->guest_features[1]) << 32) | - proxy->guest_features[0]); - } - break; - case VIRTIO_PCI_COMMON_MSIX: - msix_vector_unuse(&proxy->pci_dev, vdev->config_vector); - /* Make it possible for guest to discover an error took place. */ - if (msix_vector_use(&proxy->pci_dev, val) < 0) { - val = VIRTIO_NO_VECTOR; - } - vdev->config_vector = val; - break; - case VIRTIO_PCI_COMMON_STATUS: - if (!(val & VIRTIO_CONFIG_S_DRIVER_OK)) { - virtio_pci_stop_ioeventfd(proxy); - } - - virtio_set_status(vdev, val & 0xFF); - - if (val & VIRTIO_CONFIG_S_DRIVER_OK) { - virtio_pci_start_ioeventfd(proxy); - } - - if (vdev->status == 0) { - virtio_pci_reset(DEVICE(proxy)); - } - - break; - case VIRTIO_PCI_COMMON_Q_SELECT: - if (val < VIRTIO_QUEUE_MAX) { - vdev->queue_sel = val; - } - break; - case VIRTIO_PCI_COMMON_Q_SIZE: - proxy->vqs[vdev->queue_sel].num = val; - break; - case VIRTIO_PCI_COMMON_Q_MSIX: - msix_vector_unuse(&proxy->pci_dev, - virtio_queue_vector(vdev, vdev->queue_sel)); - /* Make it possible for guest to discover an error took place. */ - if (msix_vector_use(&proxy->pci_dev, val) < 0) { - val = VIRTIO_NO_VECTOR; - } - virtio_queue_set_vector(vdev, vdev->queue_sel, val); - break; - case VIRTIO_PCI_COMMON_Q_ENABLE: - /* TODO: need a way to put num back on reset. */ - virtio_queue_set_num(vdev, vdev->queue_sel, - proxy->vqs[vdev->queue_sel].num); - virtio_queue_set_rings(vdev, vdev->queue_sel, - ((uint64_t)proxy->vqs[vdev->queue_sel].desc[1]) << 32 | - proxy->vqs[vdev->queue_sel].desc[0], - ((uint64_t)proxy->vqs[vdev->queue_sel].avail[1]) << 32 | - 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; - break; - case VIRTIO_PCI_COMMON_Q_DESCHI: - proxy->vqs[vdev->queue_sel].desc[1] = val; - break; - case VIRTIO_PCI_COMMON_Q_AVAILLO: - proxy->vqs[vdev->queue_sel].avail[0] = val; - break; - case VIRTIO_PCI_COMMON_Q_AVAILHI: - proxy->vqs[vdev->queue_sel].avail[1] = val; - break; - case VIRTIO_PCI_COMMON_Q_USEDLO: - proxy->vqs[vdev->queue_sel].used[0] = val; - break; - case VIRTIO_PCI_COMMON_Q_USEDHI: - proxy->vqs[vdev->queue_sel].used[1] = val; - break; - default: - break; - } -} - - -static uint64_t virtio_pci_notify_read(void *opaque, hwaddr addr, - unsigned size) -{ - return 0; -} - -static void virtio_pci_notify_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - VirtIODevice *vdev = opaque; - unsigned queue = addr / QEMU_VIRTIO_PCI_QUEUE_MEM_MULT; - - if (queue < VIRTIO_QUEUE_MAX) { - virtio_queue_notify(vdev, queue); - } -} - -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) -{ - VirtIOPCIProxy *proxy = opaque; - VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); - uint64_t val = vdev->isr; - - vdev->isr = 0; - pci_irq_deassert(&proxy->pci_dev); - - return val; -} - -static void virtio_pci_isr_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ -} - -static uint64_t virtio_pci_device_read(void *opaque, hwaddr addr, - unsigned size) -{ - VirtIODevice *vdev = opaque; - uint64_t val = 0; - - switch (size) { - case 1: - val = virtio_config_modern_readb(vdev, addr); - break; - case 2: - val = virtio_config_modern_readw(vdev, addr); - break; - case 4: - val = virtio_config_modern_readl(vdev, addr); - break; - } - return val; -} - -static void virtio_pci_device_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - VirtIODevice *vdev = opaque; - switch (size) { - case 1: - virtio_config_modern_writeb(vdev, addr, val); - break; - case 2: - virtio_config_modern_writew(vdev, addr, val); - break; - case 4: - virtio_config_modern_writel(vdev, addr, val); - break; - } -} - -static void virtio_pci_modern_regions_init(VirtIOPCIProxy *proxy) -{ - static const MemoryRegionOps common_ops = { - .read = virtio_pci_common_read, - .write = virtio_pci_common_write, - .impl = { - .min_access_size = 1, - .max_access_size = 4, - }, - .endianness = DEVICE_LITTLE_ENDIAN, - }; - static const MemoryRegionOps isr_ops = { - .read = virtio_pci_isr_read, - .write = virtio_pci_isr_write, - .impl = { - .min_access_size = 1, - .max_access_size = 4, - }, - .endianness = DEVICE_LITTLE_ENDIAN, - }; - static const MemoryRegionOps device_ops = { - .read = virtio_pci_device_read, - .write = virtio_pci_device_write, - .impl = { - .min_access_size = 1, - .max_access_size = 4, - }, - .endianness = DEVICE_LITTLE_ENDIAN, - }; - static const MemoryRegionOps notify_ops = { - .read = virtio_pci_notify_read, - .write = virtio_pci_notify_write, - .impl = { - .min_access_size = 1, - .max_access_size = 4, - }, - .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, - proxy, - "virtio-pci-common", - proxy->common.size); - - memory_region_init_io(&proxy->isr.mr, OBJECT(proxy), - &isr_ops, - proxy, - "virtio-pci-isr", - proxy->isr.size); - - memory_region_init_io(&proxy->device.mr, OBJECT(proxy), - &device_ops, - virtio_bus_get_device(&proxy->bus), - "virtio-pci-device", - proxy->device.size); - - memory_region_init_io(&proxy->notify.mr, OBJECT(proxy), - ¬ify_ops, - virtio_bus_get_device(&proxy->bus), - "virtio-pci-notify", - proxy->notify.size); - - memory_region_init_io(&proxy->notify_pio.mr, OBJECT(proxy), - ¬ify_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, - MemoryRegion *mr, - uint8_t bar) -{ - memory_region_add_subregion(mr, region->offset, ®ion->mr); - - cap->cfg_type = region->type; - 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_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, - ®ion->mr); -} - -static void virtio_pci_modern_io_region_unmap(VirtIOPCIProxy *proxy, - VirtIOPCIRegion *region) -{ - memory_region_del_subregion(&proxy->io_bar, - ®ion->mr); -} - -/* This is called by virtio-bus just after the device is plugged. */ -static void virtio_pci_device_plugged(DeviceState *d, Error **errp) -{ - VirtIOPCIProxy *proxy = VIRTIO_PCI(d); - 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); - - config = proxy->pci_dev.config; - if (proxy->class_code) { - pci_config_set_class(config, proxy->class_code); - } - - if (legacy) { - /* legacy and transitional */ - pci_set_word(config + PCI_SUBSYSTEM_VENDOR_ID, - pci_get_word(config + PCI_VENDOR_ID)); - pci_set_word(config + PCI_SUBSYSTEM_ID, virtio_bus_get_vdev_id(bus)); - } else { - /* pure virtio-1.0 */ - pci_set_word(config + PCI_VENDOR_ID, - PCI_VENDOR_ID_REDHAT_QUMRANET); - pci_set_word(config + PCI_DEVICE_ID, - 0x1040 + virtio_bus_get_vdev_id(bus)); - pci_config_set_revision(config, 1); - } - config[PCI_INTERRUPT_PIN] = 1; - - - if (modern) { - struct virtio_pci_cap cap = { - .cap_len = sizeof cap, - }; - struct virtio_pci_notify_cap notify = { - .cap.cap_len = sizeof notify, - .notify_off_multiplier = - cpu_to_le32(QEMU_VIRTIO_PCI_QUEUE_MEM_MULT), - }; - struct virtio_pci_cfg_cap cfg = { - .cap.cap_len = sizeof cfg, - .cap.cfg_type = VIRTIO_PCI_CAP_PCI_CFG, - }; - struct virtio_pci_notify_cap notify_pio = { - .cap.cap_len = sizeof notify, - .notify_off_multiplier = cpu_to_le32(0x0), - }; - - 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_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, ¬ify.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, - ¬ify_pio.cap); - } - - pci_register_bar(&proxy->pci_dev, proxy->modern_mem_bar, - PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_PREFETCH | - PCI_BASE_ADDRESS_MEM_TYPE_64, - &proxy->modern_bar); - - proxy->config_cap = virtio_pci_add_mem_cap(proxy, &cfg.cap); - cfg_mask = (void *)(proxy->pci_dev.wmask + proxy->config_cap); - pci_set_byte(&cfg_mask->cap.bar, ~0x0); - pci_set_long((uint8_t *)&cfg_mask->cap.offset, ~0x0); - pci_set_long((uint8_t *)&cfg_mask->cap.length, ~0x0); - pci_set_long(cfg_mask->pci_cfg_data, ~0x0); - } - - 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; - proxy->pci_dev.config_read = virtio_read_config; - - if (legacy) { - size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev) - + virtio_bus_get_vdev_config_len(bus); - size = pow2ceil(size); - - memory_region_init_io(&proxy->bar, OBJECT(proxy), - &virtio_pci_config_ops, - proxy, "virtio-pci", size); - - pci_register_bar(&proxy->pci_dev, proxy->legacy_io_bar, - PCI_BASE_ADDRESS_SPACE_IO, &proxy->bar); - } - - if (!kvm_has_many_ioeventfds()) { - proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; - } - - virtio_add_feature(&vdev->host_features, VIRTIO_F_BAD_FEATURE); -} - -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_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); - } - } -} - -static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) -{ - VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev); - VirtioPCIClass *k = VIRTIO_PCI_GET_CLASS(pci_dev); - - /* - * virtio pci bar layout used by default. - * subclasses can re-arrange things if needed. - * - * region 0 -- virtio legacy io bar - * region 1 -- msi-x bar - * region 4+5 -- virtio modern memory (64bit) bar - * - */ - proxy->legacy_io_bar = 0; - proxy->msix_bar = 1; - proxy->modern_io_bar = 2; - proxy->modern_mem_bar = 4; - - proxy->common.offset = 0x0; - proxy->common.size = 0x1000; - proxy->common.type = VIRTIO_PCI_CAP_COMMON_CFG; - - proxy->isr.offset = 0x1000; - proxy->isr.size = 0x1000; - proxy->isr.type = VIRTIO_PCI_CAP_ISR_CFG; - - proxy->device.offset = 0x2000; - proxy->device.size = 0x1000; - proxy->device.type = VIRTIO_PCI_CAP_DEVICE_CFG; - - proxy->notify.offset = 0x3000; - proxy->notify.size = - 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 * - VIRTIO_QUEUE_MAX); - - memory_region_init_alias(&proxy->modern_cfg, - OBJECT(proxy), - "virtio-pci-cfg", - &proxy->modern_bar, - 0, - memory_region_size(&proxy->modern_bar)); - - 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); - } -} - -static void virtio_pci_exit(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev); - - msix_uninit_exclusive_bar(pci_dev); - address_space_destroy(&proxy->modern_as); -} - -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[] = { - DEFINE_PROP_BIT("virtio-pci-bus-master-bug-migration", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT, false), - DEFINE_PROP_BIT("disable-legacy", VirtIOPCIProxy, flags, - 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; - k->exit = virtio_pci_exit; - 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; -} - -static const TypeInfo virtio_pci_info = { - .name = TYPE_VIRTIO_PCI, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VirtIOPCIProxy), - .class_init = virtio_pci_class_init, - .class_size = sizeof(VirtioPCIClass), - .abstract = true, -}; - -/* virtio-blk-pci */ - -static Property virtio_blk_pci_properties[] = { - DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void virtio_blk_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = virtio_blk_pci_properties; - k->realize = virtio_blk_pci_realize; - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK; - pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; - pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; -} - -static void virtio_blk_pci_instance_init(Object *obj) -{ - VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(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 const TypeInfo virtio_blk_pci_info = { - .name = TYPE_VIRTIO_BLK_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VirtIOBlkPCI), - .instance_init = virtio_blk_pci_instance_init, - .class_init = virtio_blk_pci_class_init, -}; - -/* virtio-scsi-pci */ - -static Property virtio_scsi_pci_properties[] = { - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, - DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); - DeviceState *proxy = DEVICE(vpci_dev); - char *bus_name; - - if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { - vpci_dev->nvectors = vs->conf.num_queues + 3; - } - - /* - * For command line compatibility, this sets the virtio-scsi-device bus - * name as before. - */ - if (proxy->id) { - bus_name = g_strdup_printf("%s.0", proxy->id); - virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name); - g_free(bus_name); - } - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void virtio_scsi_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - - k->realize = virtio_scsi_pci_realize; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = virtio_scsi_pci_properties; - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI; - pcidev_k->revision = 0x00; - pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; -} - -static void virtio_scsi_pci_instance_init(Object *obj) -{ - VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_SCSI); - object_property_add_alias(obj, "iothread", OBJECT(&dev->vdev), "iothread", - &error_abort); -} - -static const TypeInfo virtio_scsi_pci_info = { - .name = TYPE_VIRTIO_SCSI_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VirtIOSCSIPCI), - .instance_init = virtio_scsi_pci_instance_init, - .class_init = virtio_scsi_pci_class_init, -}; - -/* vhost-scsi-pci */ - -#ifdef CONFIG_VHOST_SCSI -static Property vhost_scsi_pci_properties[] = { - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, - DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), -}; - -static void vhost_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - VHostSCSIPCI *dev = VHOST_SCSI_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); - - if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { - vpci_dev->nvectors = vs->conf.num_queues + 3; - } - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void vhost_scsi_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - k->realize = vhost_scsi_pci_realize; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = vhost_scsi_pci_properties; - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI; - pcidev_k->revision = 0x00; - pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; -} - -static void vhost_scsi_pci_instance_init(Object *obj) -{ - VHostSCSIPCI *dev = VHOST_SCSI_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VHOST_SCSI); - object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), - "bootindex", &error_abort); -} - -static const TypeInfo vhost_scsi_pci_info = { - .name = TYPE_VHOST_SCSI_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VHostSCSIPCI), - .instance_init = vhost_scsi_pci_instance_init, - .class_init = vhost_scsi_pci_class_init, -}; -#endif - -/* virtio-balloon-pci */ - -static Property virtio_balloon_pci_properties[] = { - DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_balloon_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - if (vpci_dev->class_code != PCI_CLASS_OTHERS && - vpci_dev->class_code != PCI_CLASS_MEMORY_RAM) { /* qemu < 1.1 */ - vpci_dev->class_code = PCI_CLASS_OTHERS; - } - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - k->realize = virtio_balloon_pci_realize; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->props = virtio_balloon_pci_properties; - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BALLOON; - pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; - pcidev_k->class_id = PCI_CLASS_OTHERS; -} - -static void virtio_balloon_pci_instance_init(Object *obj) -{ - VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_BALLOON); - object_property_add_alias(obj, "guest-stats", OBJECT(&dev->vdev), - "guest-stats", &error_abort); - object_property_add_alias(obj, "guest-stats-polling-interval", - OBJECT(&dev->vdev), - "guest-stats-polling-interval", &error_abort); -} - -static const TypeInfo virtio_balloon_pci_info = { - .name = TYPE_VIRTIO_BALLOON_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VirtIOBalloonPCI), - .instance_init = virtio_balloon_pci_instance_init, - .class_init = virtio_balloon_pci_class_init, -}; - -/* virtio-serial-pci */ - -static void virtio_serial_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - VirtIOSerialPCI *dev = VIRTIO_SERIAL_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - DeviceState *proxy = DEVICE(vpci_dev); - char *bus_name; - - if (vpci_dev->class_code != PCI_CLASS_COMMUNICATION_OTHER && - vpci_dev->class_code != PCI_CLASS_DISPLAY_OTHER && /* qemu 0.10 */ - vpci_dev->class_code != PCI_CLASS_OTHERS) { /* qemu-kvm */ - vpci_dev->class_code = PCI_CLASS_COMMUNICATION_OTHER; - } - - /* backwards-compatibility with machines that were created with - DEV_NVECTORS_UNSPECIFIED */ - if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { - vpci_dev->nvectors = dev->vdev.serial.max_virtserial_ports + 1; - } - - /* - * For command line compatibility, this sets the virtio-serial-device bus - * name as before. - */ - if (proxy->id) { - bus_name = g_strdup_printf("%s.0", proxy->id); - virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name); - g_free(bus_name); - } - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static Property virtio_serial_pci_properties[] = { - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), - DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_serial_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - k->realize = virtio_serial_pci_realize; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - dc->props = virtio_serial_pci_properties; - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_CONSOLE; - pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; - pcidev_k->class_id = PCI_CLASS_COMMUNICATION_OTHER; -} - -static void virtio_serial_pci_instance_init(Object *obj) -{ - VirtIOSerialPCI *dev = VIRTIO_SERIAL_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_SERIAL); -} - -static const TypeInfo virtio_serial_pci_info = { - .name = TYPE_VIRTIO_SERIAL_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VirtIOSerialPCI), - .instance_init = virtio_serial_pci_instance_init, - .class_init = virtio_serial_pci_class_init, -}; - -/* virtio-net-pci */ - -static Property virtio_net_properties[] = { - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_net_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - DeviceState *qdev = DEVICE(vpci_dev); - VirtIONetPCI *dev = VIRTIO_NET_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - virtio_net_set_netclient_name(&dev->vdev, qdev->id, - object_get_typename(OBJECT(qdev))); - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void virtio_net_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass); - - k->romfile = "efi-virtio.rom"; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = PCI_DEVICE_ID_VIRTIO_NET; - k->revision = VIRTIO_PCI_ABI_VERSION; - k->class_id = PCI_CLASS_NETWORK_ETHERNET; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->props = virtio_net_properties; - vpciklass->realize = virtio_net_pci_realize; -} - -static void virtio_net_pci_instance_init(Object *obj) -{ - VirtIONetPCI *dev = VIRTIO_NET_PCI(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 const TypeInfo virtio_net_pci_info = { - .name = TYPE_VIRTIO_NET_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VirtIONetPCI), - .instance_init = virtio_net_pci_instance_init, - .class_init = virtio_net_pci_class_init, -}; - -/* virtio-rng-pci */ - -static void virtio_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - VirtIORngPCI *vrng = VIRTIO_RNG_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&vrng->vdev); - Error *err = NULL; - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - - object_property_set_link(OBJECT(vrng), - OBJECT(vrng->vdev.conf.rng), "rng", - NULL); -} - -static void virtio_rng_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - - k->realize = virtio_rng_pci_realize; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_RNG; - pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; - pcidev_k->class_id = PCI_CLASS_OTHERS; -} - -static void virtio_rng_initfn(Object *obj) -{ - VirtIORngPCI *dev = VIRTIO_RNG_PCI(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 const TypeInfo virtio_rng_pci_info = { - .name = TYPE_VIRTIO_RNG_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VirtIORngPCI), - .instance_init = virtio_rng_initfn, - .class_init = virtio_rng_pci_class_init, -}; - -/* virtio-input-pci */ - -static Property virtio_input_pci_properties[] = { - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_input_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - VirtIOInputPCI *vinput = VIRTIO_INPUT_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&vinput->vdev); - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - /* force virtio-1.0 */ - vpci_dev->flags &= ~VIRTIO_PCI_FLAG_DISABLE_MODERN; - vpci_dev->flags |= VIRTIO_PCI_FLAG_DISABLE_LEGACY; - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void virtio_input_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - - dc->props = virtio_input_pci_properties; - k->realize = virtio_input_pci_realize; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - - pcidev_k->class_id = PCI_CLASS_INPUT_OTHER; -} - -static void virtio_input_hid_kbd_pci_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - - pcidev_k->class_id = PCI_CLASS_INPUT_KEYBOARD; -} - -static void virtio_input_hid_mouse_pci_class_init(ObjectClass *klass, - void *data) -{ - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - - pcidev_k->class_id = PCI_CLASS_INPUT_MOUSE; -} - -static void virtio_keyboard_initfn(Object *obj) -{ - VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_KEYBOARD); -} - -static void virtio_mouse_initfn(Object *obj) -{ - VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_MOUSE); -} - -static void virtio_tablet_initfn(Object *obj) -{ - VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_TABLET); -} - -static const TypeInfo virtio_input_pci_info = { - .name = TYPE_VIRTIO_INPUT_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VirtIOInputPCI), - .class_init = virtio_input_pci_class_init, - .abstract = true, -}; - -static const TypeInfo virtio_input_hid_pci_info = { - .name = TYPE_VIRTIO_INPUT_HID_PCI, - .parent = TYPE_VIRTIO_INPUT_PCI, - .instance_size = sizeof(VirtIOInputHIDPCI), - .abstract = true, -}; - -static const TypeInfo virtio_keyboard_pci_info = { - .name = TYPE_VIRTIO_KEYBOARD_PCI, - .parent = TYPE_VIRTIO_INPUT_HID_PCI, - .class_init = virtio_input_hid_kbd_pci_class_init, - .instance_size = sizeof(VirtIOInputHIDPCI), - .instance_init = virtio_keyboard_initfn, -}; - -static const TypeInfo virtio_mouse_pci_info = { - .name = TYPE_VIRTIO_MOUSE_PCI, - .parent = TYPE_VIRTIO_INPUT_HID_PCI, - .class_init = virtio_input_hid_mouse_pci_class_init, - .instance_size = sizeof(VirtIOInputHIDPCI), - .instance_init = virtio_mouse_initfn, -}; - -static const TypeInfo virtio_tablet_pci_info = { - .name = TYPE_VIRTIO_TABLET_PCI, - .parent = TYPE_VIRTIO_INPUT_HID_PCI, - .instance_size = sizeof(VirtIOInputHIDPCI), - .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 */ - -static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size, - VirtIOPCIProxy *dev) -{ - DeviceState *qdev = DEVICE(dev); - char virtio_bus_name[] = "virtio-bus"; - - qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_PCI_BUS, qdev, - virtio_bus_name); -} - -static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *bus_class = BUS_CLASS(klass); - VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); - bus_class->max_dev = 1; - k->notify = virtio_pci_notify; - k->save_config = virtio_pci_save_config; - 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; - k->vmstate_change = virtio_pci_vmstate_change; - k->device_plugged = virtio_pci_device_plugged; - k->device_unplugged = virtio_pci_device_unplugged; - k->query_nvectors = virtio_pci_query_nvectors; -} - -static const TypeInfo virtio_pci_bus_info = { - .name = TYPE_VIRTIO_PCI_BUS, - .parent = TYPE_VIRTIO_BUS, - .instance_size = sizeof(VirtioPCIBusState), - .class_init = virtio_pci_bus_class_init, -}; - -static void virtio_pci_register_types(void) -{ - type_register_static(&virtio_rng_pci_info); - type_register_static(&virtio_input_pci_info); - type_register_static(&virtio_input_hid_pci_info); - 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 - type_register_static(&virtio_9p_pci_info); -#endif - type_register_static(&virtio_blk_pci_info); - type_register_static(&virtio_scsi_pci_info); - type_register_static(&virtio_balloon_pci_info); - type_register_static(&virtio_serial_pci_info); - type_register_static(&virtio_net_pci_info); -#ifdef CONFIG_VHOST_SCSI - type_register_static(&vhost_scsi_pci_info); -#endif -} - -type_init(virtio_pci_register_types) diff --git a/qemu/hw/virtio/virtio-pci.h b/qemu/hw/virtio/virtio-pci.h deleted file mode 100644 index e4548c2f9..000000000 --- a/qemu/hw/virtio/virtio-pci.h +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Virtio PCI Bindings - * - * Copyright IBM, Corp. 2007 - * Copyright (c) 2009 CodeSourcery - * - * Authors: - * Anthony Liguori - * Paul Brook - * - * 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_PCI_H -#define QEMU_VIRTIO_PCI_H - -#include "hw/pci/msi.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-balloon.h" -#include "hw/virtio/virtio-bus.h" -#include "hw/virtio/virtio-input.h" -#include "hw/virtio/virtio-gpu.h" -#ifdef CONFIG_VIRTFS -#include "hw/9pfs/virtio-9p.h" -#endif -#ifdef CONFIG_VHOST_SCSI -#include "hw/virtio/vhost-scsi.h" -#endif - -typedef struct VirtIOPCIProxy VirtIOPCIProxy; -typedef struct VirtIOBlkPCI VirtIOBlkPCI; -typedef struct VirtIOSCSIPCI VirtIOSCSIPCI; -typedef struct VirtIOBalloonPCI VirtIOBalloonPCI; -typedef struct VirtIOSerialPCI VirtIOSerialPCI; -typedef struct VirtIONetPCI VirtIONetPCI; -typedef struct VHostSCSIPCI VHostSCSIPCI; -typedef struct VirtIORngPCI VirtIORngPCI; -typedef struct VirtIOInputPCI VirtIOInputPCI; -typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI; -typedef struct VirtIOInputHostPCI VirtIOInputHostPCI; -typedef struct VirtIOGPUPCI VirtIOGPUPCI; - -/* virtio-pci-bus */ - -typedef struct VirtioBusState VirtioPCIBusState; -typedef struct VirtioBusClass VirtioPCIBusClass; - -#define TYPE_VIRTIO_PCI_BUS "virtio-pci-bus" -#define VIRTIO_PCI_BUS(obj) \ - OBJECT_CHECK(VirtioPCIBusState, (obj), TYPE_VIRTIO_PCI_BUS) -#define VIRTIO_PCI_BUS_GET_CLASS(obj) \ - OBJECT_GET_CLASS(VirtioPCIBusClass, obj, TYPE_VIRTIO_PCI_BUS) -#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 \ - (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 (1 << VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT) - -/* virtio version flags */ -#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; - int virq; - unsigned int users; -} VirtIOIRQFD; - -/* - * virtio-pci: This is the PCIDevice which has a virtio-pci-bus. - */ -#define TYPE_VIRTIO_PCI "virtio-pci" -#define VIRTIO_PCI_GET_CLASS(obj) \ - OBJECT_GET_CLASS(VirtioPCIClass, obj, TYPE_VIRTIO_PCI) -#define VIRTIO_PCI_CLASS(klass) \ - OBJECT_CLASS_CHECK(VirtioPCIClass, klass, TYPE_VIRTIO_PCI) -#define VIRTIO_PCI(obj) \ - OBJECT_CHECK(VirtIOPCIProxy, (obj), TYPE_VIRTIO_PCI) - -typedef struct VirtioPCIClass { - PCIDeviceClass parent_class; - DeviceRealize parent_dc_realize; - void (*realize)(VirtIOPCIProxy *vpci_dev, Error **errp); -} VirtioPCIClass; - -typedef struct VirtIOPCIRegion { - MemoryRegion mr; - uint32_t offset; - uint32_t size; - 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; - VirtIOPCIRegion common; - 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; - uint32_t class_code; - uint32_t nvectors; - uint32_t dfselect; - uint32_t gfselect; - uint32_t guest_features[2]; - VirtIOPCIQueue vqs[VIRTIO_QUEUE_MAX]; - - bool ioeventfd_disabled; - bool ioeventfd_started; - VirtIOIRQFD *vector_irqfd; - int nvqs_with_notifiers; - VirtioBusState bus; -}; - - -/* - * virtio-scsi-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_SCSI_PCI "virtio-scsi-pci" -#define VIRTIO_SCSI_PCI(obj) \ - OBJECT_CHECK(VirtIOSCSIPCI, (obj), TYPE_VIRTIO_SCSI_PCI) - -struct VirtIOSCSIPCI { - VirtIOPCIProxy parent_obj; - VirtIOSCSI vdev; -}; - -#ifdef CONFIG_VHOST_SCSI -/* - * vhost-scsi-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VHOST_SCSI_PCI "vhost-scsi-pci" -#define VHOST_SCSI_PCI(obj) \ - OBJECT_CHECK(VHostSCSIPCI, (obj), TYPE_VHOST_SCSI_PCI) - -struct VHostSCSIPCI { - VirtIOPCIProxy parent_obj; - VHostSCSI vdev; -}; -#endif - -/* - * virtio-blk-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_BLK_PCI "virtio-blk-pci" -#define VIRTIO_BLK_PCI(obj) \ - OBJECT_CHECK(VirtIOBlkPCI, (obj), TYPE_VIRTIO_BLK_PCI) - -struct VirtIOBlkPCI { - VirtIOPCIProxy parent_obj; - VirtIOBlock vdev; -}; - -/* - * virtio-balloon-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_BALLOON_PCI "virtio-balloon-pci" -#define VIRTIO_BALLOON_PCI(obj) \ - OBJECT_CHECK(VirtIOBalloonPCI, (obj), TYPE_VIRTIO_BALLOON_PCI) - -struct VirtIOBalloonPCI { - VirtIOPCIProxy parent_obj; - VirtIOBalloon vdev; -}; - -/* - * virtio-serial-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_SERIAL_PCI "virtio-serial-pci" -#define VIRTIO_SERIAL_PCI(obj) \ - OBJECT_CHECK(VirtIOSerialPCI, (obj), TYPE_VIRTIO_SERIAL_PCI) - -struct VirtIOSerialPCI { - VirtIOPCIProxy parent_obj; - VirtIOSerial vdev; -}; - -/* - * virtio-net-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_NET_PCI "virtio-net-pci" -#define VIRTIO_NET_PCI(obj) \ - OBJECT_CHECK(VirtIONetPCI, (obj), TYPE_VIRTIO_NET_PCI) - -struct VirtIONetPCI { - VirtIOPCIProxy parent_obj; - VirtIONet vdev; -}; - -/* - * virtio-9p-pci: This extends VirtioPCIProxy. - */ - -#ifdef CONFIG_VIRTFS - -#define TYPE_VIRTIO_9P_PCI "virtio-9p-pci" -#define VIRTIO_9P_PCI(obj) \ - OBJECT_CHECK(V9fsPCIState, (obj), TYPE_VIRTIO_9P_PCI) - -typedef struct V9fsPCIState { - VirtIOPCIProxy parent_obj; - V9fsVirtioState vdev; -} V9fsPCIState; - -#endif - -/* - * virtio-rng-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_RNG_PCI "virtio-rng-pci" -#define VIRTIO_RNG_PCI(obj) \ - OBJECT_CHECK(VirtIORngPCI, (obj), TYPE_VIRTIO_RNG_PCI) - -struct VirtIORngPCI { - VirtIOPCIProxy parent_obj; - VirtIORNG vdev; -}; - -/* - * virtio-input-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_INPUT_PCI "virtio-input-pci" -#define VIRTIO_INPUT_PCI(obj) \ - OBJECT_CHECK(VirtIOInputPCI, (obj), TYPE_VIRTIO_INPUT_PCI) - -struct VirtIOInputPCI { - VirtIOPCIProxy parent_obj; - VirtIOInput vdev; -}; - -#define TYPE_VIRTIO_INPUT_HID_PCI "virtio-input-hid-pci" -#define TYPE_VIRTIO_KEYBOARD_PCI "virtio-keyboard-pci" -#define TYPE_VIRTIO_MOUSE_PCI "virtio-mouse-pci" -#define TYPE_VIRTIO_TABLET_PCI "virtio-tablet-pci" -#define VIRTIO_INPUT_HID_PCI(obj) \ - OBJECT_CHECK(VirtIOInputHIDPCI, (obj), TYPE_VIRTIO_INPUT_HID_PCI) - -struct VirtIOInputHIDPCI { - VirtIOPCIProxy parent_obj; - 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) - -struct VirtIOInputHostPCI { - VirtIOPCIProxy parent_obj; - VirtIOInputHost vdev; -}; - -#endif - -/* - * virtio-gpu-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci" -#define VIRTIO_GPU_PCI(obj) \ - OBJECT_CHECK(VirtIOGPUPCI, (obj), TYPE_VIRTIO_GPU_PCI) - -struct VirtIOGPUPCI { - VirtIOPCIProxy parent_obj; - VirtIOGPU vdev; -}; - -/* Virtio ABI version, if we increment this, we break the guest driver. */ -#define VIRTIO_PCI_ABI_VERSION 0 - -#endif diff --git a/qemu/hw/virtio/virtio-rng.c b/qemu/hw/virtio/virtio-rng.c deleted file mode 100644 index 6b991a764..000000000 --- a/qemu/hw/virtio/virtio-rng.c +++ /dev/null @@ -1,278 +0,0 @@ -/* - * A virtio device implementing a hardware random number generator. - * - * Copyright 2012 Red Hat, Inc. - * Copyright 2012 Amit Shah - * - * 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 "qapi/error.h" -#include "qemu/iov.h" -#include "hw/qdev.h" -#include "hw/virtio/virtio.h" -#include "hw/virtio/virtio-rng.h" -#include "sysemu/rng.h" -#include "qom/object_interfaces.h" -#include "trace.h" - -static bool is_guest_ready(VirtIORNG *vrng) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(vrng); - if (virtio_queue_ready(vrng->vq) - && (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { - return true; - } - trace_virtio_rng_guest_not_ready(vrng); - return false; -} - -static size_t get_request_size(VirtQueue *vq, unsigned quota) -{ - unsigned int in, out; - - virtqueue_get_avail_bytes(vq, &in, &out, quota, 0); - return in; -} - -static void virtio_rng_process(VirtIORNG *vrng); - -/* Send data from a char device over to the guest */ -static void chr_read(void *opaque, const void *buf, size_t size) -{ - VirtIORNG *vrng = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(vrng); - VirtQueueElement *elem; - size_t len; - int offset; - - if (!is_guest_ready(vrng)) { - return; - } - - vrng->quota_remaining -= size; - - offset = 0; - while (offset < size) { - elem = virtqueue_pop(vrng->vq, sizeof(VirtQueueElement)); - if (!elem) { - break; - } - len = iov_from_buf(elem->in_sg, elem->in_num, - 0, buf + offset, size - offset); - offset += 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) -{ - size_t size; - unsigned quota; - - if (!is_guest_ready(vrng)) { - return; - } - - if (vrng->activate_timer) { - timer_mod(vrng->rate_limit_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + vrng->conf.period_ms); - vrng->activate_timer = false; - } - - if (vrng->quota_remaining < 0) { - quota = 0; - } else { - quota = MIN((uint64_t)vrng->quota_remaining, (uint64_t)UINT32_MAX); - } - size = get_request_size(vrng->vq, quota); - - trace_virtio_rng_request(vrng, size, quota); - - size = MIN(vrng->quota_remaining, size); - if (size) { - rng_backend_request_entropy(vrng->rng, size, chr_read, vrng); - } -} - -static void handle_input(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIORNG *vrng = VIRTIO_RNG(vdev); - virtio_rng_process(vrng); -} - -static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp) -{ - return f; -} - -static void virtio_rng_save(QEMUFile *f, void *opaque) -{ - VirtIODevice *vdev = opaque; - - virtio_save(vdev, f); -} - -static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id) -{ - VirtIORNG *vrng = opaque; - int ret; - - if (version_id != 1) { - return -EINVAL; - } - ret = virtio_load(VIRTIO_DEVICE(vrng), f, version_id); - if (ret != 0) { - return ret; - } - - /* We may have an element ready but couldn't process it due to a quota - * limit. Make sure to try again after live migration when the quota may - * have been reset. - */ - virtio_rng_process(vrng); - - return 0; -} - -static void check_rate_limit(void *opaque) -{ - VirtIORNG *vrng = opaque; - - vrng->quota_remaining = vrng->conf.max_bytes; - virtio_rng_process(vrng); - vrng->activate_timer = true; -} - -static void virtio_rng_device_realize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIORNG *vrng = VIRTIO_RNG(dev); - Error *local_err = NULL; - - if (vrng->conf.period_ms <= 0) { - error_setg(errp, "'period' parameter expects a positive integer"); - return; - } - - /* Workaround: Property parsing does not enforce unsigned integers, - * So this is a hack to reject such numbers. */ - if (vrng->conf.max_bytes > INT64_MAX) { - error_setg(errp, "'max-bytes' parameter must be non-negative, " - "and less than 2^63"); - return; - } - - if (vrng->conf.rng == NULL) { - vrng->conf.default_backend = RNG_RANDOM(object_new(TYPE_RNG_RANDOM)); - - user_creatable_complete(OBJECT(vrng->conf.default_backend), - &local_err); - if (local_err) { - error_propagate(errp, local_err); - object_unref(OBJECT(vrng->conf.default_backend)); - return; - } - - object_property_add_child(OBJECT(dev), - "default-backend", - OBJECT(vrng->conf.default_backend), - NULL); - - /* The child property took a reference, we can safely drop ours now */ - object_unref(OBJECT(vrng->conf.default_backend)); - - object_property_set_link(OBJECT(dev), - OBJECT(vrng->conf.default_backend), - "rng", NULL); - } - - vrng->rng = vrng->conf.rng; - if (vrng->rng == NULL) { - error_setg(errp, "'rng' parameter expects a valid object"); - return; - } - - virtio_init(vdev, "virtio-rng", VIRTIO_ID_RNG, 0); - - vrng->vq = virtio_add_queue(vdev, 8, handle_input); - vrng->quota_remaining = vrng->conf.max_bytes; - vrng->rate_limit_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, - check_rate_limit, vrng); - vrng->activate_timer = true; - register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save, - virtio_rng_load, vrng); -} - -static void virtio_rng_device_unrealize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIORNG *vrng = VIRTIO_RNG(dev); - - timer_del(vrng->rate_limit_timer); - timer_free(vrng->rate_limit_timer); - unregister_savevm(dev, "virtio-rng", vrng); - virtio_cleanup(vdev); -} - -static Property virtio_rng_properties[] = { - /* Set a default rate limit of 2^47 bytes per minute or roughly 2TB/s. If - * you have an entropy source capable of generating more entropy than this - * and you can pass it through via virtio-rng, then hats off to you. Until - * then, this is unlimited for all practical purposes. - */ - DEFINE_PROP_UINT64("max-bytes", VirtIORNG, conf.max_bytes, INT64_MAX), - DEFINE_PROP_UINT32("period", VirtIORNG, conf.period_ms, 1 << 16), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_rng_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - - dc->props = virtio_rng_properties; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - vdc->realize = virtio_rng_device_realize; - vdc->unrealize = virtio_rng_device_unrealize; - vdc->get_features = get_features; -} - -static void virtio_rng_initfn(Object *obj) -{ - VirtIORNG *vrng = VIRTIO_RNG(obj); - - object_property_add_link(obj, "rng", TYPE_RNG_BACKEND, - (Object **)&vrng->conf.rng, - qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); -} - -static const TypeInfo virtio_rng_info = { - .name = TYPE_VIRTIO_RNG, - .parent = TYPE_VIRTIO_DEVICE, - .instance_size = sizeof(VirtIORNG), - .instance_init = virtio_rng_initfn, - .class_init = virtio_rng_class_init, -}; - -static void virtio_register_types(void) -{ - type_register_static(&virtio_rng_info); -} - -type_init(virtio_register_types) diff --git a/qemu/hw/virtio/virtio.c b/qemu/hw/virtio/virtio.c deleted file mode 100644 index 30ede3d1c..000000000 --- a/qemu/hw/virtio/virtio.c +++ /dev/null @@ -1,1926 +0,0 @@ -/* - * Virtio Support - * - * Copyright IBM, Corp. 2007 - * - * Authors: - * Anthony Liguori - * - * 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 "qapi/error.h" -#include "qemu-common.h" -#include "cpu.h" -#include "trace.h" -#include "exec/address-spaces.h" -#include "qemu/error-report.h" -#include "hw/virtio/virtio.h" -#include "qemu/atomic.h" -#include "hw/virtio/virtio-bus.h" -#include "migration/migration.h" -#include "hw/virtio/virtio-access.h" - -/* - * The alignment to use between consumer and producer parts of vring. - * x86 pagesize again. This is the default, used by transports like PCI - * which don't provide a means for the guest to tell the host the alignment. - */ -#define VIRTIO_PCI_VRING_ALIGN 4096 - -typedef struct VRingDesc -{ - uint64_t addr; - uint32_t len; - uint16_t flags; - uint16_t next; -} VRingDesc; - -typedef struct VRingAvail -{ - uint16_t flags; - uint16_t idx; - uint16_t ring[0]; -} VRingAvail; - -typedef struct VRingUsedElem -{ - uint32_t id; - uint32_t len; -} VRingUsedElem; - -typedef struct VRingUsed -{ - uint16_t flags; - uint16_t idx; - VRingUsedElem ring[0]; -} VRingUsed; - -typedef struct VRing -{ - unsigned int num; - unsigned int num_default; - unsigned int align; - hwaddr desc; - hwaddr avail; - hwaddr used; -} 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; - - /* Last used index value we have signalled on */ - bool signalled_used_valid; - - /* Notification enabled? */ - bool notification; - - uint16_t queue_index; - - int inuse; - - 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; - QLIST_ENTRY(VirtQueue) node; -}; - -/* virt queue functions */ -void virtio_queue_update_rings(VirtIODevice *vdev, int n) -{ - VRing *vring = &vdev->vq[n].vring; - - if (!vring->desc) { - /* not yet setup -> nothing to do */ - return; - } - vring->avail = vring->desc + vring->num * sizeof(VRingDesc); - vring->used = vring_align(vring->avail + - offsetof(VRingAvail, ring[vring->num]), - vring->align); -} - -static void vring_desc_read(VirtIODevice *vdev, VRingDesc *desc, - hwaddr desc_pa, int i) -{ - 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) -{ - hwaddr pa; - pa = vq->vring.avail + offsetof(VRingAvail, flags); - return virtio_lduw_phys(vq->vdev, pa); -} - -static inline uint16_t vring_avail_idx(VirtQueue *vq) -{ - hwaddr pa; - pa = vq->vring.avail + offsetof(VRingAvail, idx); - 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) -{ - hwaddr pa; - pa = vq->vring.avail + offsetof(VRingAvail, ring[i]); - return virtio_lduw_phys(vq->vdev, pa); -} - -static inline uint16_t vring_get_used_event(VirtQueue *vq) -{ - return vring_avail_ring(vq, vq->vring.num); -} - -static inline void vring_used_write(VirtQueue *vq, VRingUsedElem *uelem, - int i) -{ - hwaddr pa; - 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) -{ - hwaddr pa; - pa = vq->vring.used + offsetof(VRingUsed, idx); - return virtio_lduw_phys(vq->vdev, pa); -} - -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) -{ - VirtIODevice *vdev = vq->vdev; - hwaddr pa; - pa = vq->vring.used + offsetof(VRingUsed, flags); - virtio_stw_phys(vdev, pa, virtio_lduw_phys(vdev, pa) | mask); -} - -static inline void vring_used_flags_unset_bit(VirtQueue *vq, int mask) -{ - VirtIODevice *vdev = vq->vdev; - hwaddr pa; - pa = vq->vring.used + offsetof(VRingUsed, flags); - virtio_stw_phys(vdev, pa, virtio_lduw_phys(vdev, pa) & ~mask); -} - -static inline void vring_set_avail_event(VirtQueue *vq, uint16_t val) -{ - hwaddr pa; - if (!vq->notification) { - return; - } - pa = vq->vring.used + offsetof(VRingUsed, ring[vq->vring.num]); - virtio_stw_phys(vq->vdev, pa, val); -} - -void virtio_queue_set_notification(VirtQueue *vq, int enable) -{ - vq->notification = enable; - 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); - } else { - vring_used_flags_set_bit(vq, VRING_USED_F_NO_NOTIFY); - } - if (enable) { - /* Expose avail event/used flags before caller checks the avail idx. */ - smp_mb(); - } -} - -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; -} - -static void virtqueue_unmap_sg(VirtQueue *vq, const VirtQueueElement *elem, - unsigned int len) -{ - unsigned int offset; - int i; - - offset = 0; - for (i = 0; i < elem->in_num; i++) { - size_t size = MIN(len - offset, elem->in_sg[i].iov_len); - - cpu_physical_memory_unmap(elem->in_sg[i].iov_base, - elem->in_sg[i].iov_len, - 1, size); - - offset += size; - } - - for (i = 0; i < elem->out_num; i++) - cpu_physical_memory_unmap(elem->out_sg[i].iov_base, - elem->out_sg[i].iov_len, - 0, elem->out_sg[i].iov_len); -} - -void virtqueue_discard(VirtQueue *vq, const VirtQueueElement *elem, - unsigned int len) -{ - vq->last_avail_idx--; - virtqueue_unmap_sg(vq, elem, 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) -{ - uint16_t old, new; - /* Make sure buffer is written before we update index. */ - smp_wmb(); - trace_virtqueue_flush(vq, count); - old = vq->used_idx; - new = old + count; - vring_used_idx_set(vq, new); - vq->inuse -= count; - if (unlikely((int16_t)(new - vq->signalled_used) < (uint16_t)(new - old))) - vq->signalled_used_valid = false; -} - -void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem, - unsigned int len) -{ - virtqueue_fill(vq, elem, len, 0); - virtqueue_flush(vq, 1); -} - -static int virtqueue_num_heads(VirtQueue *vq, unsigned int idx) -{ - uint16_t num_heads = vring_avail_idx(vq) - 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, vq->shadow_avail_idx); - exit(1); - } - /* On success, callers read a descriptor at vq->last_avail_idx. - * Make sure descriptor read does not bypass avail index read. */ - if (num_heads) { - smp_rmb(); - } - - return num_heads; -} - -static unsigned int virtqueue_get_head(VirtQueue *vq, unsigned int idx) -{ - unsigned int head; - - /* Grab the next descriptor number they're advertising, and increment - * the index we've seen. */ - head = vring_avail_ring(vq, idx % vq->vring.num); - - /* If their number is silly, that's a fatal mistake. */ - if (head >= vq->vring.num) { - error_report("Guest says index %u is available", head); - exit(1); - } - - return head; -} - -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 (!(desc->flags & VRING_DESC_F_NEXT)) { - return max; - } - - /* Check they're not leading us off end of descriptors. */ - next = desc->next; - /* Make sure compiler knows to grab that: we don't want it changing! */ - smp_wmb(); - - if (next >= max) { - error_report("Desc next is %u", next); - exit(1); - } - - vring_desc_read(vdev, desc, desc_pa, next); - return next; -} - -void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, - unsigned int *out_bytes, - unsigned max_in_bytes, unsigned max_out_bytes) -{ - unsigned int idx; - unsigned int total_bufs, in_total, out_total; - - idx = vq->last_avail_idx; - - total_bufs = in_total = out_total = 0; - while (virtqueue_num_heads(vq, idx)) { - VirtIODevice *vdev = vq->vdev; - unsigned int max, num_bufs, indirect = 0; - VRingDesc desc; - hwaddr desc_pa; - int i; - - max = vq->vring.num; - num_bufs = total_bufs; - i = virtqueue_get_head(vq, idx++); - desc_pa = vq->vring.desc; - 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); - } - - /* If we've got too many, that implies a descriptor loop. */ - if (num_bufs >= max) { - error_report("Looped descriptor"); - exit(1); - } - - /* loop over the indirect descriptor table */ - indirect = 1; - max = desc.len / sizeof(VRingDesc); - desc_pa = desc.addr; - num_bufs = i = 0; - vring_desc_read(vdev, &desc, desc_pa, i); - } - - do { - /* If we've got too many, that implies a descriptor loop. */ - if (++num_bufs > max) { - error_report("Looped descriptor"); - exit(1); - } - - if (desc.flags & VRING_DESC_F_WRITE) { - in_total += desc.len; - } else { - out_total += desc.len; - } - if (in_total >= max_in_bytes && out_total >= max_out_bytes) { - goto done; - } - } while ((i = virtqueue_read_next_desc(vdev, &desc, desc_pa, max)) != max); - - if (!indirect) - total_bufs = num_bufs; - else - total_bufs++; - } -done: - if (in_bytes) { - *in_bytes = in_total; - } - if (out_bytes) { - *out_bytes = out_total; - } -} - -int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, - unsigned int out_bytes) -{ - unsigned int in_total, out_total; - - virtqueue_get_avail_bytes(vq, &in_total, &out_total, in_bytes, out_bytes); - return in_bytes <= in_total && out_bytes <= out_total; -} - -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; - - /* 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++) { - len = sg[i].iov_len; - sg[i].iov_base = cpu_physical_memory_map(addr[i], &len, is_write); - 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); - } - } -} - -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 (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. */ - out_num = in_num = 0; - - max = vq->vring.num; - - i = head = virtqueue_get_head(vq, vq->last_avail_idx++); - if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { - vring_set_avail_event(vq, vq->last_avail_idx); - } - - 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 = desc.len / sizeof(VRingDesc); - desc_pa = desc.addr; - i = 0; - vring_desc_read(vdev, &desc, desc_pa, i); - } - - /* Collect all the descriptors */ - do { - 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 (in_num) { - error_report("Incorrect order for descriptors"); - exit(1); - } - virtqueue_map_desc(&out_num, addr, iov, - VIRTQUEUE_MAX_SIZE, false, desc.addr, desc.len); - } - - /* If we've got too many, that implies a descriptor loop. */ - if ((in_num + out_num) > max) { - error_report("Looped descriptor"); - exit(1); - } - } 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; -} - -/* 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 */ -static void virtio_notify_vector(VirtIODevice *vdev, uint16_t vector) -{ - BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - - if (k->notify) { - k->notify(qbus->parent, vector); - } -} - -void virtio_update_irq(VirtIODevice *vdev) -{ - virtio_notify_vector(vdev, VIRTIO_NO_VECTOR); -} - -static int virtio_validate_features(VirtIODevice *vdev) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - - if (k->validate_features) { - return k->validate_features(vdev); - } else { - return 0; - } -} - -int virtio_set_status(VirtIODevice *vdev, uint8_t val) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - trace_virtio_set_status(vdev, val); - - 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); - - if (ret) { - return ret; - } - } - } - if (k->set_status) { - k->set_status(vdev, val); - } - vdev->status = val; - return 0; -} - -bool target_words_bigendian(void); -static enum virtio_device_endian virtio_default_endian(void) -{ - if (target_words_bigendian()) { - return VIRTIO_DEVICE_ENDIAN_BIG; - } else { - return VIRTIO_DEVICE_ENDIAN_LITTLE; - } -} - -static enum virtio_device_endian virtio_current_cpu_endian(void) -{ - CPUClass *cc = CPU_GET_CLASS(current_cpu); - - if (cc->virtio_is_big_endian(current_cpu)) { - return VIRTIO_DEVICE_ENDIAN_BIG; - } else { - return VIRTIO_DEVICE_ENDIAN_LITTLE; - } -} - -void virtio_reset(void *opaque) -{ - VirtIODevice *vdev = opaque; - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - int i; - - virtio_set_status(vdev, 0); - if (current_cpu) { - /* Guest initiated reset */ - vdev->device_endian = virtio_current_cpu_endian(); - } else { - /* System reset */ - vdev->device_endian = virtio_default_endian(); - } - - if (k->reset) { - k->reset(vdev); - } - - vdev->guest_features = 0; - vdev->queue_sel = 0; - vdev->status = 0; - vdev->isr = 0; - vdev->config_vector = VIRTIO_NO_VECTOR; - virtio_notify_vector(vdev, vdev->config_vector); - - for(i = 0; i < VIRTIO_QUEUE_MAX; i++) { - vdev->vq[i].vring.desc = 0; - 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; - } -} - -uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint8_t val; - - if (addr + sizeof(val) > vdev->config_len) { - return (uint32_t)-1; - } - - k->get_config(vdev, vdev->config); - - val = ldub_p(vdev->config + addr); - return val; -} - -uint32_t virtio_config_readw(VirtIODevice *vdev, uint32_t addr) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint16_t val; - - if (addr + sizeof(val) > vdev->config_len) { - return (uint32_t)-1; - } - - k->get_config(vdev, vdev->config); - - val = lduw_p(vdev->config + addr); - return val; -} - -uint32_t virtio_config_readl(VirtIODevice *vdev, uint32_t addr) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint32_t val; - - if (addr + sizeof(val) > vdev->config_len) { - return (uint32_t)-1; - } - - k->get_config(vdev, vdev->config); - - val = ldl_p(vdev->config + addr); - return val; -} - -void virtio_config_writeb(VirtIODevice *vdev, uint32_t addr, uint32_t data) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint8_t val = data; - - if (addr + sizeof(val) > vdev->config_len) { - return; - } - - stb_p(vdev->config + addr, val); - - if (k->set_config) { - k->set_config(vdev, vdev->config); - } -} - -void virtio_config_writew(VirtIODevice *vdev, uint32_t addr, uint32_t data) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint16_t val = data; - - if (addr + sizeof(val) > vdev->config_len) { - return; - } - - stw_p(vdev->config + addr, val); - - if (k->set_config) { - k->set_config(vdev, vdev->config); - } -} - -void virtio_config_writel(VirtIODevice *vdev, uint32_t addr, uint32_t data) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint32_t val = data; - - if (addr + sizeof(val) > vdev->config_len) { - return; - } - - stl_p(vdev->config + addr, val); - - if (k->set_config) { - k->set_config(vdev, vdev->config); - } -} - -uint32_t virtio_config_modern_readb(VirtIODevice *vdev, uint32_t addr) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint8_t val; - - if (addr + sizeof(val) > vdev->config_len) { - return (uint32_t)-1; - } - - k->get_config(vdev, vdev->config); - - val = ldub_p(vdev->config + addr); - return val; -} - -uint32_t virtio_config_modern_readw(VirtIODevice *vdev, uint32_t addr) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint16_t val; - - if (addr + sizeof(val) > vdev->config_len) { - return (uint32_t)-1; - } - - k->get_config(vdev, vdev->config); - - val = lduw_le_p(vdev->config + addr); - return val; -} - -uint32_t virtio_config_modern_readl(VirtIODevice *vdev, uint32_t addr) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint32_t val; - - if (addr + sizeof(val) > vdev->config_len) { - return (uint32_t)-1; - } - - k->get_config(vdev, vdev->config); - - val = ldl_le_p(vdev->config + addr); - return val; -} - -void virtio_config_modern_writeb(VirtIODevice *vdev, - uint32_t addr, uint32_t data) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint8_t val = data; - - if (addr + sizeof(val) > vdev->config_len) { - return; - } - - stb_p(vdev->config + addr, val); - - if (k->set_config) { - k->set_config(vdev, vdev->config); - } -} - -void virtio_config_modern_writew(VirtIODevice *vdev, - uint32_t addr, uint32_t data) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint16_t val = data; - - if (addr + sizeof(val) > vdev->config_len) { - return; - } - - stw_le_p(vdev->config + addr, val); - - if (k->set_config) { - k->set_config(vdev, vdev->config); - } -} - -void virtio_config_modern_writel(VirtIODevice *vdev, - uint32_t addr, uint32_t data) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint32_t val = data; - - if (addr + sizeof(val) > vdev->config_len) { - return; - } - - stl_le_p(vdev->config + addr, val); - - if (k->set_config) { - k->set_config(vdev, vdev->config); - } -} - -void virtio_queue_set_addr(VirtIODevice *vdev, int n, hwaddr addr) -{ - vdev->vq[n].vring.desc = addr; - virtio_queue_update_rings(vdev, n); -} - -hwaddr virtio_queue_get_addr(VirtIODevice *vdev, int n) -{ - return vdev->vq[n].vring.desc; -} - -void virtio_queue_set_rings(VirtIODevice *vdev, int n, hwaddr desc, - hwaddr avail, hwaddr used) -{ - vdev->vq[n].vring.desc = desc; - vdev->vq[n].vring.avail = avail; - vdev->vq[n].vring.used = used; -} - -void virtio_queue_set_num(VirtIODevice *vdev, int n, int num) -{ - /* Don't allow guest to flip queue between existent and - * nonexistent states, or to set it to an invalid size. - */ - if (!!num != !!vdev->vq[n].vring.num || - num > VIRTQUEUE_MAX_SIZE || - num < 0) { - return; - } - vdev->vq[n].vring.num = num; -} - -VirtQueue *virtio_vector_first_queue(VirtIODevice *vdev, uint16_t vector) -{ - return QLIST_FIRST(&vdev->vector_queues[vector]); -} - -VirtQueue *virtio_vector_next_queue(VirtQueue *vq) -{ - return QLIST_NEXT(vq, node); -} - -int virtio_queue_get_num(VirtIODevice *vdev, int n) -{ - return vdev->vq[n].vring.num; -} - -int virtio_get_num_queues(VirtIODevice *vdev) -{ - int i; - - for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - if (!virtio_queue_get_num(vdev, i)) { - break; - } - } - - return i; -} - -int virtio_queue_get_id(VirtQueue *vq) -{ - VirtIODevice *vdev = vq->vdev; - assert(vq >= &vdev->vq[0] && vq < &vdev->vq[VIRTIO_QUEUE_MAX]); - return vq - &vdev->vq[0]; -} - -void virtio_queue_set_align(VirtIODevice *vdev, int n, int align) -{ - BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - - /* virtio-1 compliant devices cannot change the alignment */ - if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { - error_report("tried to modify queue alignment for virtio-1 device"); - return; - } - /* Check that the transport told us it was going to do this - * (so a buggy transport will immediately assert rather than - * silently failing to migrate this state) - */ - assert(k->has_variable_vring_alignment); - - vdev->vq[n].vring.align = align; - virtio_queue_update_rings(vdev, n); -} - -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; - - trace_virtio_queue_notify(vdev, vq - vdev->vq, vq); - vq->handle_output(vdev, vq); - } -} - -void virtio_queue_notify(VirtIODevice *vdev, int n) -{ - virtio_queue_notify_vq(&vdev->vq[n]); -} - -uint16_t virtio_queue_vector(VirtIODevice *vdev, int n) -{ - return n < VIRTIO_QUEUE_MAX ? vdev->vq[n].vector : - VIRTIO_NO_VECTOR; -} - -void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector) -{ - VirtQueue *vq = &vdev->vq[n]; - - if (n < VIRTIO_QUEUE_MAX) { - if (vdev->vector_queues && - vdev->vq[n].vector != VIRTIO_NO_VECTOR) { - QLIST_REMOVE(vq, node); - } - vdev->vq[n].vector = vector; - if (vdev->vector_queues && - vector != VIRTIO_NO_VECTOR) { - QLIST_INSERT_HEAD(&vdev->vector_queues[vector], vq, node); - } - } -} - -VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, - void (*handle_output)(VirtIODevice *, VirtQueue *)) -{ - int i; - - for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - if (vdev->vq[i].vring.num == 0) - break; - } - - if (i == VIRTIO_QUEUE_MAX || queue_size > VIRTQUEUE_MAX_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]; -} - -void virtio_del_queue(VirtIODevice *vdev, int n) -{ - if (n < 0 || n >= VIRTIO_QUEUE_MAX) { - abort(); - } - - vdev->vq[n].vring.num = 0; - vdev->vq[n].vring.num_default = 0; -} - -void virtio_irq(VirtQueue *vq) -{ - trace_virtio_irq(vq); - vq->vdev->isr |= 0x01; - virtio_notify_vector(vq->vdev, vq->vector); -} - -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_vdev_has_feature(vdev, VIRTIO_F_NOTIFY_ON_EMPTY) && - !vq->inuse && virtio_queue_empty(vq)) { - return true; - } - - 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 = vq->used_idx; - return !v || vring_need_event(vring_get_used_event(vq), new, old); -} - -void virtio_notify(VirtIODevice *vdev, VirtQueue *vq) -{ - if (!virtio_should_notify(vdev, vq)) { - return; - } - - trace_virtio_notify(vdev, vq); - vdev->isr |= 0x01; - virtio_notify_vector(vdev, vq->vector); -} - -void virtio_notify_config(VirtIODevice *vdev) -{ - if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) - return; - - vdev->isr |= 0x03; - vdev->generation++; - virtio_notify_vector(vdev, vdev->config_vector); -} - -static bool virtio_device_endian_needed(void *opaque) -{ - VirtIODevice *vdev = opaque; - - assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN); - 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. */ - return vdev->device_endian != VIRTIO_DEVICE_ENDIAN_LITTLE; -} - -static bool virtio_64bit_features_needed(void *opaque) -{ - VirtIODevice *vdev = opaque; - - return (vdev->host_features >> 32) != 0; -} - -static bool virtio_virtqueue_needed(void *opaque) -{ - VirtIODevice *vdev = opaque; - - return virtio_host_has_feature(vdev, VIRTIO_F_VERSION_1); -} - -static bool virtio_ringsize_needed(void *opaque) -{ - VirtIODevice *vdev = opaque; - int i; - - for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - if (vdev->vq[i].vring.num != vdev->vq[i].vring.num_default) { - return true; - } - } - return false; -} - -static bool virtio_extra_state_needed(void *opaque) -{ - VirtIODevice *vdev = opaque; - BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - - return k->has_extra_state && - k->has_extra_state(qbus->parent); -} - -static const VMStateDescription vmstate_virtqueue = { - .name = "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 = { - .name = "virtio/virtqueues", - .version_id = 1, - .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 = "extra_state", - .version_id = 0, - .field_exists = NULL, - .size = 0, - .info = &vmstate_info_extra_state, - .flags = VMS_SINGLE, - .offset = 0, - }, - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_virtio_device_endian = { - .name = "virtio/device_endian", - .version_id = 1, - .minimum_version_id = 1, - .needed = &virtio_device_endian_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(device_endian, VirtIODevice), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_virtio_64bit_features = { - .name = "virtio/64bit_features", - .version_id = 1, - .minimum_version_id = 1, - .needed = &virtio_64bit_features_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT64(guest_features, VirtIODevice), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_virtio = { - .name = "virtio", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_virtio_device_endian, - &vmstate_virtio_64bit_features, - &vmstate_virtio_virtqueues, - &vmstate_virtio_ringsize, - &vmstate_virtio_extra_state, - NULL - } -}; - -void virtio_save(VirtIODevice *vdev, QEMUFile *f) -{ - BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); - uint32_t guest_features_lo = (vdev->guest_features & 0xffffffff); - int i; - - if (k->save_config) { - k->save_config(qbus->parent, f); - } - - qemu_put_8s(f, &vdev->status); - qemu_put_8s(f, &vdev->isr); - qemu_put_be16s(f, &vdev->queue_sel); - qemu_put_be32s(f, &guest_features_lo); - qemu_put_be32(f, vdev->config_len); - qemu_put_buffer(f, vdev->config, vdev->config_len); - - for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - if (vdev->vq[i].vring.num == 0) - break; - } - - qemu_put_be32(f, i); - - for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - if (vdev->vq[i].vring.num == 0) - break; - - qemu_put_be32(f, vdev->vq[i].vring.num); - if (k->has_variable_vring_alignment) { - qemu_put_be32(f, vdev->vq[i].vring.align); - } - /* XXX virtio-1 devices */ - qemu_put_be64(f, vdev->vq[i].vring.desc); - qemu_put_be16s(f, &vdev->vq[i].last_avail_idx); - if (k->save_queue) { - k->save_queue(qbus->parent, i, f); - } - } - - if (vdc->save != NULL) { - vdc->save(vdev, f); - } - - /* Subsections */ - vmstate_save_state(f, &vmstate_virtio, vdev, NULL); -} - -static int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val) -{ - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - bool bad = (val & ~(vdev->host_features)) != 0; - - val &= vdev->host_features; - if (k->set_features) { - k->set_features(vdev, val); - } - vdev->guest_features = val; - return bad ? -1 : 0; -} - -int virtio_set_features(VirtIODevice *vdev, uint64_t val) -{ - /* - * The driver must not attempt to set features after feature negotiation - * has finished. - */ - if (vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) { - return -EINVAL; - } - return virtio_set_features_nocheck(vdev, val); -} - -int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) -{ - int i, ret; - int32_t config_len; - uint32_t num; - uint32_t features; - BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); - - /* - * We poison the endianness to ensure it does not get used before - * subsections have been loaded. - */ - vdev->device_endian = VIRTIO_DEVICE_ENDIAN_UNKNOWN; - - if (k->load_config) { - ret = k->load_config(qbus->parent, f); - if (ret) - return ret; - } - - qemu_get_8s(f, &vdev->status); - qemu_get_8s(f, &vdev->isr); - qemu_get_be16s(f, &vdev->queue_sel); - if (vdev->queue_sel >= VIRTIO_QUEUE_MAX) { - return -1; - } - qemu_get_be32s(f, &features); - - config_len = qemu_get_be32(f); - - /* - * There are cases where the incoming config can be bigger or smaller - * than what we have; so load what we have space for, and skip - * any excess that's in the stream. - */ - qemu_get_buffer(f, vdev->config, MIN(config_len, vdev->config_len)); - - while (config_len > vdev->config_len) { - qemu_get_byte(f); - config_len--; - } - - num = qemu_get_be32(f); - - if (num > VIRTIO_QUEUE_MAX) { - error_report("Invalid number of virtqueues: 0x%x", num); - return -1; - } - - for (i = 0; i < num; i++) { - vdev->vq[i].vring.num = qemu_get_be32(f); - if (k->has_variable_vring_alignment) { - vdev->vq[i].vring.align = qemu_get_be32(f); - } - vdev->vq[i].vring.desc = qemu_get_be64(f); - qemu_get_be16s(f, &vdev->vq[i].last_avail_idx); - vdev->vq[i].signalled_used_valid = false; - vdev->vq[i].notification = true; - - if (vdev->vq[i].vring.desc) { - /* XXX virtio-1 devices */ - virtio_queue_update_rings(vdev, i); - } else if (vdev->vq[i].last_avail_idx) { - error_report("VQ %d address 0x0 " - "inconsistent with Host index 0x%x", - i, vdev->vq[i].last_avail_idx); - return -1; - } - if (k->load_queue) { - ret = k->load_queue(qbus->parent, i, f); - if (ret) - return ret; - } - } - - virtio_notify_vector(vdev, VIRTIO_NO_VECTOR); - - if (vdc->load != NULL) { - ret = vdc->load(vdev, f, version_id); - if (ret) { - return ret; - } - } - - /* Subsections */ - ret = vmstate_load_state(f, &vmstate_virtio, vdev, 1); - if (ret) { - return ret; - } - - if (vdev->device_endian == VIRTIO_DEVICE_ENDIAN_UNKNOWN) { - vdev->device_endian = virtio_default_endian(); - } - - if (virtio_64bit_features_needed(vdev)) { - /* - * Subsection load filled vdev->guest_features. Run them - * through virtio_set_features to sanity-check them against - * host_features. - */ - uint64_t features64 = vdev->guest_features; - if (virtio_set_features_nocheck(vdev, features64) < 0) { - error_report("Features 0x%" PRIx64 " unsupported. " - "Allowed features: 0x%" PRIx64, - features64, vdev->host_features); - return -1; - } - } else { - if (virtio_set_features_nocheck(vdev, features) < 0) { - error_report("Features 0x%x unsupported. " - "Allowed features: 0x%" PRIx64, - features, vdev->host_features); - return -1; - } - } - - for (i = 0; i < num; i++) { - if (vdev->vq[i].vring.desc) { - uint16_t nheads; - nheads = vring_avail_idx(&vdev->vq[i]) - vdev->vq[i].last_avail_idx; - /* Check it isn't doing strange things with descriptor numbers. */ - if (nheads > vdev->vq[i].vring.num) { - error_report("VQ %d size 0x%x Guest index 0x%x " - "inconsistent with Host index 0x%x: delta 0x%x", - i, vdev->vq[i].vring.num, - vring_avail_idx(&vdev->vq[i]), - 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]); - } - } - - return 0; -} - -void virtio_cleanup(VirtIODevice *vdev) -{ - qemu_del_vm_change_state_handler(vdev->vmstate); - g_free(vdev->config); - g_free(vdev->vq); - g_free(vdev->vector_queues); -} - -static void virtio_vmstate_change(void *opaque, int running, RunState state) -{ - VirtIODevice *vdev = opaque; - BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - bool backend_run = running && (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK); - vdev->vm_running = running; - - if (backend_run) { - virtio_set_status(vdev, vdev->status); - } - - if (k->vmstate_change) { - k->vmstate_change(qbus->parent, backend_run); - } - - if (!backend_run) { - virtio_set_status(vdev, vdev->status); - } -} - -void virtio_instance_init_common(Object *proxy_obj, void *data, - size_t vdev_size, const char *vdev_name) -{ - DeviceState *vdev = data; - - object_initialize(vdev, vdev_size, vdev_name); - object_property_add_child(proxy_obj, "virtio-backend", OBJECT(vdev), NULL); - object_unref(OBJECT(vdev)); - qdev_alias_all_properties(vdev, proxy_obj); -} - -void virtio_init(VirtIODevice *vdev, const char *name, - uint16_t device_id, size_t config_size) -{ - BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - int i; - int nvectors = k->query_nvectors ? k->query_nvectors(qbus->parent) : 0; - - if (nvectors) { - vdev->vector_queues = - g_malloc0(sizeof(*vdev->vector_queues) * nvectors); - } - - vdev->device_id = device_id; - vdev->status = 0; - vdev->isr = 0; - vdev->queue_sel = 0; - vdev->config_vector = VIRTIO_NO_VECTOR; - vdev->vq = g_malloc0(sizeof(VirtQueue) * VIRTIO_QUEUE_MAX); - vdev->vm_running = runstate_is_running(); - for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - vdev->vq[i].vector = VIRTIO_NO_VECTOR; - vdev->vq[i].vdev = vdev; - vdev->vq[i].queue_index = i; - } - - vdev->name = name; - vdev->config_len = config_size; - if (vdev->config_len) { - vdev->config = g_malloc0(config_size); - } else { - vdev->config = NULL; - } - 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) -{ - return vdev->vq[n].vring.desc; -} - -hwaddr virtio_queue_get_avail_addr(VirtIODevice *vdev, int n) -{ - return vdev->vq[n].vring.avail; -} - -hwaddr virtio_queue_get_used_addr(VirtIODevice *vdev, int n) -{ - return vdev->vq[n].vring.used; -} - -hwaddr virtio_queue_get_ring_addr(VirtIODevice *vdev, int n) -{ - return vdev->vq[n].vring.desc; -} - -hwaddr virtio_queue_get_desc_size(VirtIODevice *vdev, int n) -{ - return sizeof(VRingDesc) * vdev->vq[n].vring.num; -} - -hwaddr virtio_queue_get_avail_size(VirtIODevice *vdev, int n) -{ - return offsetof(VRingAvail, ring) + - sizeof(uint16_t) * vdev->vq[n].vring.num; -} - -hwaddr virtio_queue_get_used_size(VirtIODevice *vdev, int n) -{ - return offsetof(VRingUsed, ring) + - sizeof(VRingUsedElem) * vdev->vq[n].vring.num; -} - -hwaddr virtio_queue_get_ring_size(VirtIODevice *vdev, int n) -{ - return vdev->vq[n].vring.used - vdev->vq[n].vring.desc + - virtio_queue_get_used_size(vdev, n); -} - -uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n) -{ - return vdev->vq[n].last_avail_idx; -} - -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) -{ - vdev->vq[n].signalled_used_valid = false; -} - -VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n) -{ - return vdev->vq + n; -} - -uint16_t virtio_get_queue_index(VirtQueue *vq) -{ - return vq->queue_index; -} - -static void virtio_queue_guest_notifier_read(EventNotifier *n) -{ - VirtQueue *vq = container_of(n, VirtQueue, guest_notifier); - if (event_notifier_test_and_clear(n)) { - virtio_irq(vq); - } -} - -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, false, - virtio_queue_guest_notifier_read); - } else { - event_notifier_set_handler(&vq->guest_notifier, false, NULL); - } - if (!assign) { - /* Test and clear notifier before closing it, - * in case poll callback didn't have time to run. */ - virtio_queue_guest_notifier_read(&vq->guest_notifier); - } -} - -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); - if (event_notifier_test_and_clear(n)) { - virtio_queue_notify_vq(vq); - } -} - -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, true, - virtio_queue_host_notifier_read); - } else { - event_notifier_set_handler(&vq->host_notifier, true, NULL); - } - if (!assign) { - /* Test and clear notifier before after disabling event, - * in case poll callback didn't have time to run. */ - virtio_queue_host_notifier_read(&vq->host_notifier); - } -} - -EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq) -{ - return &vq->host_notifier; -} - -void virtio_device_set_child_bus_name(VirtIODevice *vdev, char *bus_name) -{ - g_free(vdev->bus_name); - vdev->bus_name = g_strdup(bus_name); -} - -static void virtio_device_realize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(dev); - Error *err = NULL; - - if (vdc->realize != NULL) { - vdc->realize(dev, &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - } - - virtio_bus_device_plugged(vdev, &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } -} - -static void virtio_device_unrealize(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(dev); - Error *err = NULL; - - virtio_bus_device_unplugged(vdev); - - if (vdc->unrealize != NULL) { - vdc->unrealize(dev, &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - } - - g_free(vdev->bus_name); - vdev->bus_name = NULL; -} - -static Property virtio_properties[] = { - DEFINE_VIRTIO_COMMON_FEATURES(VirtIODevice, host_features), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_device_class_init(ObjectClass *klass, void *data) -{ - /* Set the default value here. */ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = virtio_device_realize; - dc->unrealize = virtio_device_unrealize; - dc->bus_type = TYPE_VIRTIO_BUS; - dc->props = virtio_properties; -} - -static const TypeInfo virtio_device_info = { - .name = TYPE_VIRTIO_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(VirtIODevice), - .class_init = virtio_device_class_init, - .abstract = true, - .class_size = sizeof(VirtioDeviceClass), -}; - -static void virtio_register_types(void) -{ - type_register_static(&virtio_device_info); -} - -type_init(virtio_register_types) diff --git a/qemu/hw/watchdog/Makefile.objs b/qemu/hw/watchdog/Makefile.objs deleted file mode 100644 index 72e3ffd93..000000000 --- a/qemu/hw/watchdog/Makefile.objs +++ /dev/null @@ -1,4 +0,0 @@ -common-obj-y += watchdog.o -common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o -common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o -common-obj-$(CONFIG_WDT_DIAG288) += wdt_diag288.o diff --git a/qemu/hw/watchdog/watchdog.c b/qemu/hw/watchdog/watchdog.c deleted file mode 100644 index bbf3646ba..000000000 --- a/qemu/hw/watchdog/watchdog.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Virtual hardware watchdog. - * - * Copyright (C) 2009 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 . - * - * By Richard W.M. Jones (rjones@redhat.com). - */ - -#include "qemu/osdep.h" -#include "qemu/option.h" -#include "qemu/config-file.h" -#include "qemu/queue.h" -#include "qapi/qmp/types.h" -#include "sysemu/sysemu.h" -#include "sysemu/watchdog.h" -#include "qapi-event.h" -#include "hw/nmi.h" -#include "qemu/help_option.h" - -static int watchdog_action = WDT_RESET; -static QLIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list; - -void watchdog_add_model(WatchdogTimerModel *model) -{ - QLIST_INSERT_HEAD(&watchdog_list, model, entry); -} - -/* Returns: - * 0 = continue - * 1 = exit program with error - * 2 = exit program without error - */ -int select_watchdog(const char *p) -{ - WatchdogTimerModel *model; - QemuOpts *opts; - - /* -watchdog ? lists available devices and exits cleanly. */ - if (is_help_option(p)) { - QLIST_FOREACH(model, &watchdog_list, entry) { - fprintf(stderr, "\t%s\t%s\n", - model->wdt_name, model->wdt_description); - } - return 2; - } - - QLIST_FOREACH(model, &watchdog_list, entry) { - if (strcasecmp(model->wdt_name, p) == 0) { - /* add the device */ - opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, - &error_abort); - qemu_opt_set(opts, "driver", p, &error_abort); - return 0; - } - } - - fprintf(stderr, "Unknown -watchdog device. Supported devices are:\n"); - QLIST_FOREACH(model, &watchdog_list, entry) { - fprintf(stderr, "\t%s\t%s\n", - model->wdt_name, model->wdt_description); - } - return 1; -} - -int select_watchdog_action(const char *p) -{ - if (strcasecmp(p, "reset") == 0) - watchdog_action = WDT_RESET; - else if (strcasecmp(p, "shutdown") == 0) - watchdog_action = WDT_SHUTDOWN; - else if (strcasecmp(p, "poweroff") == 0) - watchdog_action = WDT_POWEROFF; - else if (strcasecmp(p, "pause") == 0) - watchdog_action = WDT_PAUSE; - else if (strcasecmp(p, "debug") == 0) - watchdog_action = WDT_DEBUG; - else if (strcasecmp(p, "none") == 0) - watchdog_action = WDT_NONE; - else if (strcasecmp(p, "inject-nmi") == 0) - watchdog_action = WDT_NMI; - else - return -1; - - 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. - */ -void watchdog_perform_action(void) -{ - switch (watchdog_action) { - case WDT_RESET: /* same as 'system_reset' in monitor */ - qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_RESET, &error_abort); - qemu_system_reset_request(); - break; - - case WDT_SHUTDOWN: /* same as 'system_powerdown' in monitor */ - qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_SHUTDOWN, &error_abort); - qemu_system_powerdown_request(); - break; - - case WDT_POWEROFF: /* same as 'quit' command in monitor */ - qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_POWEROFF, &error_abort); - exit(0); - - case WDT_PAUSE: /* same as 'stop' command in monitor */ - /* In a timer callback, when vm_stop calls qemu_clock_enable - * you would get a deadlock. Bypass the problem. - */ - qemu_system_vmstop_request_prepare(); - qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_PAUSE, &error_abort); - qemu_system_vmstop_request(RUN_STATE_WATCHDOG); - break; - - case WDT_DEBUG: - qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_DEBUG, &error_abort); - fprintf(stderr, "watchdog: timer fired\n"); - break; - - case WDT_NONE: - qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_NONE, &error_abort); - break; - - case WDT_NMI: - qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_INJECT_NMI, - &error_abort); - inject_nmi(); - break; - } -} diff --git a/qemu/hw/watchdog/wdt_diag288.c b/qemu/hw/watchdog/wdt_diag288.c deleted file mode 100644 index f54a35a0e..000000000 --- a/qemu/hw/watchdog/wdt_diag288.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * watchdog device diag288 support - * - * Copyright IBM, Corp. 2015 - * - * Authors: - * Xu Wang - * - * 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 "sysemu/watchdog.h" -#include "hw/sysbus.h" -#include "qemu/timer.h" -#include "hw/watchdog/wdt_diag288.h" - -static WatchdogTimerModel model = { - .wdt_name = TYPE_WDT_DIAG288, - .wdt_description = "diag288 device for s390x platform", -}; - -static const VMStateDescription vmstate_diag288 = { - .name = "vmstate_diag288", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_TIMER_PTR(timer, DIAG288State), - VMSTATE_BOOL(enabled, DIAG288State), - VMSTATE_END_OF_LIST() - } -}; - -static void wdt_diag288_reset(DeviceState *dev) -{ - DIAG288State *diag288 = DIAG288(dev); - - diag288->enabled = false; - timer_del(diag288->timer); -} - -static void diag288_reset(void *opaque) -{ - DeviceState *diag288 = opaque; - - wdt_diag288_reset(diag288); -} - -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(); -} - -static int wdt_diag288_handle_timer(DIAG288State *diag288, - uint64_t func, uint64_t timeout) -{ - switch (func) { - case WDT_DIAG288_INIT: - diag288->enabled = true; - /* fall through */ - case WDT_DIAG288_CHANGE: - if (!diag288->enabled) { - return -1; - } - timer_mod(diag288->timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - timeout * NANOSECONDS_PER_SECOND); - break; - case WDT_DIAG288_CANCEL: - if (!diag288->enabled) { - return -1; - } - diag288->enabled = false; - timer_del(diag288->timer); - break; - default: - return -1; - } - - return 0; -} - -static void wdt_diag288_realize(DeviceState *dev, Error **errp) -{ - DIAG288State *diag288 = DIAG288(dev); - - qemu_register_reset(diag288_reset, diag288); - diag288->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, diag288_timer_expired, - dev); -} - -static void wdt_diag288_unrealize(DeviceState *dev, Error **errp) -{ - DIAG288State *diag288 = DIAG288(dev); - - timer_del(diag288->timer); - timer_free(diag288->timer); -} - -static void wdt_diag288_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - DIAG288Class *diag288 = DIAG288_CLASS(klass); - - dc->realize = wdt_diag288_realize; - dc->unrealize = wdt_diag288_unrealize; - dc->reset = wdt_diag288_reset; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->vmsd = &vmstate_diag288; - diag288->handle_timer = wdt_diag288_handle_timer; -} - -static const TypeInfo wdt_diag288_info = { - .class_init = wdt_diag288_class_init, - .parent = TYPE_DEVICE, - .name = TYPE_WDT_DIAG288, - .instance_size = sizeof(DIAG288State), - .class_size = sizeof(DIAG288Class), -}; - -static void wdt_diag288_register_types(void) -{ - watchdog_add_model(&model); - type_register_static(&wdt_diag288_info); -} - -type_init(wdt_diag288_register_types) diff --git a/qemu/hw/watchdog/wdt_i6300esb.c b/qemu/hw/watchdog/wdt_i6300esb.c deleted file mode 100644 index a83d95121..000000000 --- a/qemu/hw/watchdog/wdt_i6300esb.c +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Virtual hardware watchdog. - * - * Copyright (C) 2009 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 . - * - * By Richard W.M. Jones (rjones@redhat.com). - */ - -#include "qemu/osdep.h" - -#include "qemu-common.h" -#include "qemu/timer.h" -#include "sysemu/watchdog.h" -#include "hw/hw.h" -#include "hw/pci/pci.h" - -/*#define I6300ESB_DEBUG 1*/ - -#ifdef I6300ESB_DEBUG -#define i6300esb_debug(fs,...) \ - fprintf(stderr,"i6300esb: %s: "fs,__func__,##__VA_ARGS__) -#else -#define i6300esb_debug(fs,...) -#endif - -/* PCI configuration registers */ -#define ESB_CONFIG_REG 0x60 /* Config register */ -#define ESB_LOCK_REG 0x68 /* WDT lock register */ - -/* Memory mapped registers (offset from base address) */ -#define ESB_TIMER1_REG 0x00 /* Timer1 value after each reset */ -#define ESB_TIMER2_REG 0x04 /* Timer2 value after each reset */ -#define ESB_GINTSR_REG 0x08 /* General Interrupt Status Register */ -#define ESB_RELOAD_REG 0x0c /* Reload register */ - -/* Lock register bits */ -#define ESB_WDT_FUNC (0x01 << 2) /* Watchdog functionality */ -#define ESB_WDT_ENABLE (0x01 << 1) /* Enable WDT */ -#define ESB_WDT_LOCK (0x01 << 0) /* Lock (nowayout) */ - -/* Config register bits */ -#define ESB_WDT_REBOOT (0x01 << 5) /* Enable reboot on timeout */ -#define ESB_WDT_FREQ (0x01 << 2) /* Decrement frequency */ -#define ESB_WDT_INTTYPE (0x11 << 0) /* Interrupt type on timer1 timeout */ - -/* Reload register bits */ -#define ESB_WDT_RELOAD (0x01 << 8) /* prevent timeout */ - -/* Magic constants */ -#define ESB_UNLOCK1 0x80 /* Step 1 to unlock reset registers */ -#define ESB_UNLOCK2 0x86 /* Step 2 to unlock reset registers */ - -/* Device state. */ -struct I6300State { - PCIDevice dev; - MemoryRegion io_mem; - - int reboot_enabled; /* "Reboot" on timer expiry. The real action - * performed depends on the -watchdog-action - * param passed on QEMU command line. - */ - int clock_scale; /* Clock scale. */ -#define CLOCK_SCALE_1KHZ 0 -#define CLOCK_SCALE_1MHZ 1 - - int int_type; /* Interrupt type generated. */ -#define INT_TYPE_IRQ 0 /* APIC 1, INT 10 */ -#define INT_TYPE_SMI 2 -#define INT_TYPE_DISABLED 3 - - int free_run; /* If true, reload timer on expiry. */ - int locked; /* If true, enabled field cannot be changed. */ - int enabled; /* If true, watchdog is enabled. */ - - QEMUTimer *timer; /* The actual watchdog timer. */ - - uint32_t timer1_preload; /* Values preloaded into timer1, timer2. */ - uint32_t timer2_preload; - int stage; /* Stage (1 or 2). */ - - int unlock_state; /* Guest writes 0x80, 0x86 to unlock the - * registers, and we transition through - * states 0 -> 1 -> 2 when this happens. - */ - - int previous_reboot_flag; /* If the watchdog caused the previous - * reboot, this flag will be set. - */ -}; - -typedef struct I6300State I6300State; - -#define TYPE_WATCHDOG_I6300ESB_DEVICE "i6300esb" -#define WATCHDOG_I6300ESB_DEVICE(obj) \ - OBJECT_CHECK(I6300State, (obj), TYPE_WATCHDOG_I6300ESB_DEVICE) - -/* This function is called when the watchdog has either been enabled - * (hence it starts counting down) or has been keep-alived. - */ -static void i6300esb_restart_timer(I6300State *d, int stage) -{ - int64_t timeout; - - if (!d->enabled) - return; - - d->stage = stage; - - if (d->stage <= 1) - timeout = d->timer1_preload; - else - timeout = d->timer2_preload; - - if (d->clock_scale == CLOCK_SCALE_1KHZ) - timeout <<= 15; - else - timeout <<= 5; - - /* 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); - - timer_mod(d->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + timeout); -} - -/* This is called when the guest disables the watchdog. */ -static void i6300esb_disable_timer(I6300State *d) -{ - i6300esb_debug("timer disabled\n"); - - timer_del(d->timer); -} - -static void i6300esb_reset(DeviceState *dev) -{ - PCIDevice *pdev = PCI_DEVICE(dev); - I6300State *d = WATCHDOG_I6300ESB_DEVICE(pdev); - - i6300esb_debug("I6300State = %p\n", d); - - i6300esb_disable_timer(d); - - /* NB: Don't change d->previous_reboot_flag in this function. */ - - d->reboot_enabled = 1; - d->clock_scale = CLOCK_SCALE_1KHZ; - d->int_type = INT_TYPE_IRQ; - d->free_run = 0; - d->locked = 0; - d->enabled = 0; - d->timer1_preload = 0xfffff; - d->timer2_preload = 0xfffff; - d->stage = 1; - d->unlock_state = 0; -} - -/* This function is called when the watchdog expires. Note that - * the hardware has two timers, and so expiry happens in two stages. - * If d->stage == 1 then we perform the first stage action (usually, - * sending an interrupt) and then restart the timer again for the - * second stage. If the second stage expires then the watchdog - * really has run out. - */ -static void i6300esb_timer_expired(void *vp) -{ - I6300State *d = vp; - - i6300esb_debug("stage %d\n", d->stage); - - if (d->stage == 1) { - /* What to do at the end of stage 1? */ - switch (d->int_type) { - case INT_TYPE_IRQ: - fprintf(stderr, "i6300esb_timer_expired: I would send APIC 1 INT 10 here if I knew how (XXX)\n"); - break; - case INT_TYPE_SMI: - fprintf(stderr, "i6300esb_timer_expired: I would send SMI here if I knew how (XXX)\n"); - break; - } - - /* Start the second stage. */ - i6300esb_restart_timer(d, 2); - } else { - /* Second stage expired, reboot for real. */ - if (d->reboot_enabled) { - d->previous_reboot_flag = 1; - watchdog_perform_action(); /* This reboots, exits, etc */ - i6300esb_reset(&d->dev.qdev); - } - - /* In "free running mode" we start stage 1 again. */ - if (d->free_run) - i6300esb_restart_timer(d, 1); - } -} - -static void i6300esb_config_write(PCIDevice *dev, uint32_t addr, - uint32_t data, int len) -{ - I6300State *d = WATCHDOG_I6300ESB_DEVICE(dev); - int old; - - i6300esb_debug("addr = %x, data = %x, len = %d\n", addr, data, len); - - if (addr == ESB_CONFIG_REG && len == 2) { - d->reboot_enabled = (data & ESB_WDT_REBOOT) == 0; - d->clock_scale = - (data & ESB_WDT_FREQ) != 0 ? CLOCK_SCALE_1MHZ : CLOCK_SCALE_1KHZ; - d->int_type = (data & ESB_WDT_INTTYPE); - } else if (addr == ESB_LOCK_REG && len == 1) { - if (!d->locked) { - d->locked = (data & ESB_WDT_LOCK) != 0; - d->free_run = (data & ESB_WDT_FUNC) != 0; - old = d->enabled; - d->enabled = (data & ESB_WDT_ENABLE) != 0; - if (!old && d->enabled) /* Enabled transitioned from 0 -> 1 */ - i6300esb_restart_timer(d, 1); - else if (!d->enabled) - i6300esb_disable_timer(d); - } - } else { - pci_default_write_config(dev, addr, data, len); - } -} - -static uint32_t i6300esb_config_read(PCIDevice *dev, uint32_t addr, int len) -{ - I6300State *d = WATCHDOG_I6300ESB_DEVICE(dev); - uint32_t data; - - i6300esb_debug ("addr = %x, len = %d\n", addr, len); - - if (addr == ESB_CONFIG_REG && len == 2) { - data = - (d->reboot_enabled ? 0 : ESB_WDT_REBOOT) | - (d->clock_scale == CLOCK_SCALE_1MHZ ? ESB_WDT_FREQ : 0) | - d->int_type; - return data; - } else if (addr == ESB_LOCK_REG && len == 1) { - data = - (d->free_run ? ESB_WDT_FUNC : 0) | - (d->locked ? ESB_WDT_LOCK : 0) | - (d->enabled ? ESB_WDT_ENABLE : 0); - return data; - } else { - return pci_default_read_config(dev, addr, len); - } -} - -static uint32_t i6300esb_mem_readb(void *vp, hwaddr addr) -{ - i6300esb_debug ("addr = %x\n", (int) addr); - - return 0; -} - -static uint32_t i6300esb_mem_readw(void *vp, hwaddr addr) -{ - uint32_t data = 0; - I6300State *d = vp; - - i6300esb_debug("addr = %x\n", (int) addr); - - if (addr == 0xc) { - /* The previous reboot flag is really bit 9, but there is - * a bug in the Linux driver where it thinks it's bit 12. - * Set both. - */ - data = d->previous_reboot_flag ? 0x1200 : 0; - } - - return data; -} - -static uint32_t i6300esb_mem_readl(void *vp, hwaddr addr) -{ - i6300esb_debug("addr = %x\n", (int) addr); - - return 0; -} - -static void i6300esb_mem_writeb(void *vp, hwaddr addr, uint32_t val) -{ - I6300State *d = vp; - - i6300esb_debug("addr = %x, val = %x\n", (int) addr, val); - - if (addr == 0xc && val == 0x80) - d->unlock_state = 1; - else if (addr == 0xc && val == 0x86 && d->unlock_state == 1) - d->unlock_state = 2; -} - -static void i6300esb_mem_writew(void *vp, hwaddr addr, uint32_t val) -{ - I6300State *d = vp; - - i6300esb_debug("addr = %x, val = %x\n", (int) addr, val); - - if (addr == 0xc && val == 0x80) - d->unlock_state = 1; - else if (addr == 0xc && val == 0x86 && d->unlock_state == 1) - d->unlock_state = 2; - else { - if (d->unlock_state == 2) { - if (addr == 0xc) { - if ((val & 0x100) != 0) - /* This is the "ping" from the userspace watchdog in - * the guest ... - */ - i6300esb_restart_timer(d, 1); - - /* Setting bit 9 resets the previous reboot flag. - * There's a bug in the Linux driver where it sets - * bit 12 instead. - */ - if ((val & 0x200) != 0 || (val & 0x1000) != 0) { - d->previous_reboot_flag = 0; - } - } - - d->unlock_state = 0; - } - } -} - -static void i6300esb_mem_writel(void *vp, hwaddr addr, uint32_t val) -{ - I6300State *d = vp; - - i6300esb_debug ("addr = %x, val = %x\n", (int) addr, val); - - if (addr == 0xc && val == 0x80) - d->unlock_state = 1; - else if (addr == 0xc && val == 0x86 && d->unlock_state == 1) - d->unlock_state = 2; - else { - if (d->unlock_state == 2) { - if (addr == 0) - d->timer1_preload = val & 0xfffff; - else if (addr == 4) - d->timer2_preload = val & 0xfffff; - - d->unlock_state = 0; - } - } -} - -static const MemoryRegionOps i6300esb_ops = { - .old_mmio = { - .read = { - i6300esb_mem_readb, - i6300esb_mem_readw, - i6300esb_mem_readl, - }, - .write = { - i6300esb_mem_writeb, - i6300esb_mem_writew, - i6300esb_mem_writel, - }, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const VMStateDescription vmstate_i6300esb = { - .name = "i6300esb_wdt", - /* With this VMSD's introduction, version_id/minimum_version_id were - * erroneously set to sizeof(I6300State), causing a somewhat random - * version_id to be set for every build. This eventually broke - * migration. - * - * To correct this without breaking old->new migration for older - * versions of QEMU, we've set version_id to a value high enough - * to exceed all past values of sizeof(I6300State) across various - * build environments, and have reset minimum_version_id to 1, - * since this VMSD has never changed and thus can accept all past - * versions. - * - * For future changes we can treat these values as we normally would. - */ - .version_id = 10000, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, I6300State), - VMSTATE_INT32(reboot_enabled, I6300State), - VMSTATE_INT32(clock_scale, I6300State), - VMSTATE_INT32(int_type, I6300State), - VMSTATE_INT32(free_run, I6300State), - VMSTATE_INT32(locked, I6300State), - VMSTATE_INT32(enabled, I6300State), - VMSTATE_TIMER_PTR(timer, I6300State), - VMSTATE_UINT32(timer1_preload, I6300State), - VMSTATE_UINT32(timer2_preload, I6300State), - VMSTATE_INT32(stage, I6300State), - VMSTATE_INT32(unlock_state, I6300State), - VMSTATE_INT32(previous_reboot_flag, I6300State), - VMSTATE_END_OF_LIST() - } -}; - -static void i6300esb_realize(PCIDevice *dev, Error **errp) -{ - I6300State *d = WATCHDOG_I6300ESB_DEVICE(dev); - - i6300esb_debug("I6300State = %p\n", d); - - d->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, i6300esb_timer_expired, d); - d->previous_reboot_flag = 0; - - memory_region_init_io(&d->io_mem, OBJECT(d), &i6300esb_ops, d, - "i6300esb", 0x10); - pci_register_bar(&d->dev, 0, 0, &d->io_mem); - /* qemu_register_coalesced_mmio (addr, 0x10); ? */ -} - -static WatchdogTimerModel model = { - .wdt_name = "i6300esb", - .wdt_description = "Intel 6300ESB", -}; - -static void i6300esb_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->config_read = i6300esb_config_read; - k->config_write = i6300esb_config_write; - k->realize = i6300esb_realize; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_ESB_9; - k->class_id = PCI_CLASS_SYSTEM_OTHER; - dc->reset = i6300esb_reset; - dc->vmsd = &vmstate_i6300esb; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo i6300esb_info = { - .name = TYPE_WATCHDOG_I6300ESB_DEVICE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(I6300State), - .class_init = i6300esb_class_init, -}; - -static void i6300esb_register_types(void) -{ - watchdog_add_model(&model); - type_register_static(&i6300esb_info); -} - -type_init(i6300esb_register_types) diff --git a/qemu/hw/watchdog/wdt_ib700.c b/qemu/hw/watchdog/wdt_ib700.c deleted file mode 100644 index 532afe89e..000000000 --- a/qemu/hw/watchdog/wdt_ib700.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Virtual hardware watchdog. - * - * Copyright (C) 2009 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 . - * - * By Richard W.M. Jones (rjones@redhat.com). - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/timer.h" -#include "sysemu/watchdog.h" -#include "hw/hw.h" -#include "hw/isa/isa.h" -#include "hw/i386/pc.h" - -/*#define IB700_DEBUG 1*/ - -#ifdef IB700_DEBUG -#define ib700_debug(fs,...) \ - fprintf(stderr,"ib700: %s: "fs,__func__,##__VA_ARGS__) -#else -#define ib700_debug(fs,...) -#endif - -#define TYPE_IB700 "ib700" -#define IB700(obj) OBJECT_CHECK(IB700State, (obj), TYPE_IB700) - -typedef struct IB700state { - ISADevice parent_obj; - - QEMUTimer *timer; - - PortioList port_list; -} IB700State; - -/* This is the timer. We use a global here because the watchdog - * code ensures there is only one watchdog (it is located at a fixed, - * unchangeable IO port, so there could only ever be one anyway). - */ - -/* A write to this register enables the timer. */ -static void ib700_write_enable_reg(void *vp, uint32_t addr, uint32_t data) -{ - IB700State *s = vp; - static int time_map[] = { - 30, 28, 26, 24, 22, 20, 18, 16, - 14, 12, 10, 8, 6, 4, 2, 0 - }; - int64_t timeout; - - ib700_debug("addr = %x, data = %x\n", addr, data); - - timeout = (int64_t) time_map[data & 0xF] * NANOSECONDS_PER_SECOND; - timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + timeout); -} - -/* A write (of any value) to this register disables the timer. */ -static void ib700_write_disable_reg(void *vp, uint32_t addr, uint32_t data) -{ - IB700State *s = vp; - - ib700_debug("addr = %x, data = %x\n", addr, data); - - timer_del(s->timer); -} - -/* This is called when the watchdog expires. */ -static void ib700_timer_expired(void *vp) -{ - IB700State *s = vp; - - ib700_debug("watchdog expired\n"); - - watchdog_perform_action(); - timer_del(s->timer); -} - -static const VMStateDescription vmstate_ib700 = { - .name = "ib700_wdt", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_TIMER_PTR(timer, IB700State), - VMSTATE_END_OF_LIST() - } -}; - -static const MemoryRegionPortio wdt_portio_list[] = { - { 0x441, 2, 1, .write = ib700_write_disable_reg, }, - { 0x443, 2, 1, .write = ib700_write_enable_reg, }, - PORTIO_END_OF_LIST(), -}; - -static void wdt_ib700_realize(DeviceState *dev, Error **errp) -{ - IB700State *s = IB700(dev); - - ib700_debug("watchdog init\n"); - - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ib700_timer_expired, s); - - portio_list_init(&s->port_list, OBJECT(s), wdt_portio_list, s, "ib700"); - portio_list_add(&s->port_list, isa_address_space_io(&s->parent_obj), 0); -} - -static void wdt_ib700_reset(DeviceState *dev) -{ - IB700State *s = IB700(dev); - - ib700_debug("watchdog reset\n"); - - timer_del(s->timer); -} - -static WatchdogTimerModel model = { - .wdt_name = "ib700", - .wdt_description = "iBASE 700", -}; - -static void wdt_ib700_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = wdt_ib700_realize; - dc->reset = wdt_ib700_reset; - dc->vmsd = &vmstate_ib700; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo wdt_ib700_info = { - .name = TYPE_IB700, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(IB700State), - .class_init = wdt_ib700_class_init, -}; - -static void wdt_ib700_register_types(void) -{ - watchdog_add_model(&model); - type_register_static(&wdt_ib700_info); -} - -type_init(wdt_ib700_register_types) diff --git a/qemu/hw/xen/Makefile.objs b/qemu/hw/xen/Makefile.objs deleted file mode 100644 index d3670940b..000000000 --- a/qemu/hw/xen/Makefile.objs +++ /dev/null @@ -1,5 +0,0 @@ -# xen backend driver support -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_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 deleted file mode 100644 index eed8cc88e..000000000 --- a/qemu/hw/xen/xen-host-pci-device.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Copyright (C) 2011 Citrix Ltd. - * - * 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 "qapi/error.h" -#include "qemu-common.h" -#include "qemu/cutils.h" -#include "xen-host-pci-device.h" - -#define XEN_HOST_PCI_MAX_EXT_CAP \ - ((PCIE_CONFIG_SPACE_SIZE - PCI_CONFIG_SPACE_SIZE) / (PCI_CAP_SIZEOF + 4)) - -#ifdef XEN_HOST_PCI_DEVICE_DEBUG -# define XEN_HOST_PCI_LOG(f, a...) fprintf(stderr, "%s: " f, __func__, ##a) -#else -# define XEN_HOST_PCI_LOG(f, a...) (void)0 -#endif - -/* - * from linux/ioport.h - * IO resources have these defined flags. - */ -#define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */ - -#define IORESOURCE_TYPE_BITS 0x00000f00 /* Resource type */ -#define IORESOURCE_IO 0x00000100 -#define IORESOURCE_MEM 0x00000200 - -#define IORESOURCE_PREFETCH 0x00001000 /* No side effects */ -#define IORESOURCE_MEM_64 0x00100000 - -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); - 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 void xen_host_pci_get_resource(XenHostPCIDevice *d, Error **errp) -{ - int i, rc, fd; - char path[PATH_MAX]; - char buf[XEN_HOST_PCI_RESOURCE_BUFFER_SIZE]; - unsigned long long start, end, flags, size; - char *endptr, *s; - uint8_t type; - - xen_host_pci_sysfs_path(d, "resource", path, sizeof(path)); - - fd = open(path, O_RDONLY); - if (fd == -1) { - error_setg_file_open(errp, errno, path); - return; - } - - do { - rc = read(fd, &buf, sizeof(buf) - 1); - if (rc < 0 && errno != EINTR) { - error_setg_errno(errp, errno, "read err"); - goto out; - } - } while (rc < 0); - buf[rc] = 0; - - s = buf; - for (i = 0; i < PCI_NUM_REGIONS; i++) { - type = 0; - - start = strtoll(s, &endptr, 16); - if (*endptr != ' ' || s == endptr) { - break; - } - s = endptr + 1; - end = strtoll(s, &endptr, 16); - if (*endptr != ' ' || s == endptr) { - break; - } - s = endptr + 1; - flags = strtoll(s, &endptr, 16); - if (*endptr != '\n' || s == endptr) { - break; - } - s = endptr + 1; - - if (start) { - size = end - start + 1; - } else { - size = 0; - } - - if (flags & IORESOURCE_IO) { - type |= XEN_HOST_PCI_REGION_TYPE_IO; - } - if (flags & IORESOURCE_MEM) { - type |= XEN_HOST_PCI_REGION_TYPE_MEM; - } - if (flags & IORESOURCE_PREFETCH) { - type |= XEN_HOST_PCI_REGION_TYPE_PREFETCH; - } - if (flags & IORESOURCE_MEM_64) { - type |= XEN_HOST_PCI_REGION_TYPE_MEM_64; - } - - if (i < PCI_ROM_SLOT) { - d->io_regions[i].base_addr = start; - d->io_regions[i].size = size; - d->io_regions[i].type = type; - d->io_regions[i].bus_flags = flags & IORESOURCE_BITS; - } else { - d->rom.base_addr = start; - d->rom.size = size; - d->rom.type = type; - d->rom.bus_flags = flags & IORESOURCE_BITS; - } - } - - if (i != PCI_NUM_REGIONS) { - error_setg(errp, "Invalid format or input too short: %s", buf); - } - -out: - close(fd); -} - -/* This size should be enough to read a long from a file */ -#define XEN_HOST_PCI_GET_VALUE_BUFFER_SIZE 22 -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; - const char *endptr; - - xen_host_pci_sysfs_path(d, name, path, sizeof(path)); - - fd = open(path, O_RDONLY); - if (fd == -1) { - error_setg_file_open(errp, errno, path); - return; - } - - do { - rc = read(fd, &buf, sizeof(buf) - 1); - if (rc < 0 && errno != EINTR) { - error_setg_errno(errp, errno, "read err"); - goto out; - } - } while (rc < 0); - - buf[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); -} - -static inline void xen_host_pci_get_hex_value(XenHostPCIDevice *d, - const char *name, - unsigned int *pvalue, - Error **errp) -{ - xen_host_pci_get_value(d, name, pvalue, 16, errp); -} - -static inline void xen_host_pci_get_dec_value(XenHostPCIDevice *d, - const char *name, - unsigned int *pvalue, - Error **errp) -{ - xen_host_pci_get_value(d, name, pvalue, 10, errp); -} - -static bool xen_host_pci_dev_is_virtfn(XenHostPCIDevice *d) -{ - char path[PATH_MAX]; - struct stat buf; - - xen_host_pci_sysfs_path(d, "physfn", path, sizeof(path)); - - return !stat(path, &buf); -} - -static void xen_host_pci_config_open(XenHostPCIDevice *d, Error **errp) -{ - char path[PATH_MAX]; - - xen_host_pci_sysfs_path(d, "config", path, sizeof(path)); - - d->config_fd = open(path, O_RDWR); - if (d->config_fd == -1) { - error_setg_file_open(errp, errno, path); - } -} - -static int xen_host_pci_config_read(XenHostPCIDevice *d, - int pos, void *buf, int len) -{ - int rc; - - do { - rc = pread(d->config_fd, buf, len, pos); - } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); - if (rc != len) { - return -errno; - } - return 0; -} - -static int xen_host_pci_config_write(XenHostPCIDevice *d, - int pos, const void *buf, int len) -{ - int rc; - - do { - rc = pwrite(d->config_fd, buf, len, pos); - } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); - if (rc != len) { - return -errno; - } - return 0; -} - - -int xen_host_pci_get_byte(XenHostPCIDevice *d, int pos, uint8_t *p) -{ - uint8_t buf; - int rc = xen_host_pci_config_read(d, pos, &buf, 1); - if (!rc) { - *p = buf; - } - return rc; -} - -int xen_host_pci_get_word(XenHostPCIDevice *d, int pos, uint16_t *p) -{ - uint16_t buf; - int rc = xen_host_pci_config_read(d, pos, &buf, 2); - if (!rc) { - *p = le16_to_cpu(buf); - } - return rc; -} - -int xen_host_pci_get_long(XenHostPCIDevice *d, int pos, uint32_t *p) -{ - uint32_t buf; - int rc = xen_host_pci_config_read(d, pos, &buf, 4); - if (!rc) { - *p = le32_to_cpu(buf); - } - return rc; -} - -int xen_host_pci_get_block(XenHostPCIDevice *d, int pos, uint8_t *buf, int len) -{ - return xen_host_pci_config_read(d, pos, buf, len); -} - -int xen_host_pci_set_byte(XenHostPCIDevice *d, int pos, uint8_t data) -{ - return xen_host_pci_config_write(d, pos, &data, 1); -} - -int xen_host_pci_set_word(XenHostPCIDevice *d, int pos, uint16_t data) -{ - data = cpu_to_le16(data); - return xen_host_pci_config_write(d, pos, &data, 2); -} - -int xen_host_pci_set_long(XenHostPCIDevice *d, int pos, uint32_t data) -{ - data = cpu_to_le32(data); - return xen_host_pci_config_write(d, pos, &data, 4); -} - -int xen_host_pci_set_block(XenHostPCIDevice *d, int pos, uint8_t *buf, int len) -{ - return xen_host_pci_config_write(d, pos, buf, len); -} - -int xen_host_pci_find_ext_cap_offset(XenHostPCIDevice *d, uint32_t cap) -{ - uint32_t header = 0; - int max_cap = XEN_HOST_PCI_MAX_EXT_CAP; - int pos = PCI_CONFIG_SPACE_SIZE; - - do { - if (xen_host_pci_get_long(d, pos, &header)) { - break; - } - /* - * If we have no capabilities, this is indicated by cap ID, - * cap version and next pointer all being 0. - */ - if (header == 0) { - break; - } - - if (PCI_EXT_CAP_ID(header) == cap) { - return pos; - } - - pos = PCI_EXT_CAP_NEXT(header); - if (pos < PCI_CONFIG_SPACE_SIZE) { - break; - } - - max_cap--; - } while (max_cap > 0); - - return -1; -} - -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; - Error *err = NULL; - - d->config_fd = -1; - d->domain = domain; - d->bus = bus; - d->dev = dev; - d->func = func; - - xen_host_pci_config_open(d, &err); - if (err) { - goto error; - } - - xen_host_pci_get_resource(d, &err); - if (err) { - goto error; - } - - xen_host_pci_get_hex_value(d, "vendor", &v, &err); - if (err) { - goto error; - } - d->vendor_id = v; - - xen_host_pci_get_hex_value(d, "device", &v, &err); - if (err) { - goto error; - } - d->device_id = v; - - 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; - -error: - error_propagate(errp, err); - - if (d->config_fd >= 0) { - close(d->config_fd); - d->config_fd = -1; - } -} - -bool xen_host_pci_device_closed(XenHostPCIDevice *d) -{ - return d->config_fd == -1; -} - -void xen_host_pci_device_put(XenHostPCIDevice *d) -{ - if (d->config_fd >= 0) { - close(d->config_fd); - d->config_fd = -1; - } -} diff --git a/qemu/hw/xen/xen-host-pci-device.h b/qemu/hw/xen/xen-host-pci-device.h deleted file mode 100644 index 6acf36e13..000000000 --- a/qemu/hw/xen/xen-host-pci-device.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef XEN_HOST_PCI_DEVICE_H -#define XEN_HOST_PCI_DEVICE_H - -#include "hw/pci/pci.h" - -enum { - XEN_HOST_PCI_REGION_TYPE_IO = 1 << 1, - XEN_HOST_PCI_REGION_TYPE_MEM = 1 << 2, - XEN_HOST_PCI_REGION_TYPE_PREFETCH = 1 << 3, - XEN_HOST_PCI_REGION_TYPE_MEM_64 = 1 << 4, -}; - -typedef struct XenHostPCIIORegion { - pcibus_t base_addr; - pcibus_t size; - uint8_t type; - uint8_t bus_flags; /* Bus-specific bits */ -} XenHostPCIIORegion; - -typedef struct XenHostPCIDevice { - uint16_t domain; - uint8_t bus; - uint8_t dev; - uint8_t func; - - uint16_t vendor_id; - uint16_t device_id; - uint32_t class_code; - int irq; - - XenHostPCIIORegion io_regions[PCI_NUM_REGIONS - 1]; - XenHostPCIIORegion rom; - - bool is_virtfn; - - int config_fd; -} XenHostPCIDevice; - -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); -int xen_host_pci_get_long(XenHostPCIDevice *d, int pos, uint32_t *p); -int xen_host_pci_get_block(XenHostPCIDevice *d, int pos, uint8_t *buf, - int len); -int xen_host_pci_set_byte(XenHostPCIDevice *d, int pos, uint8_t data); -int xen_host_pci_set_word(XenHostPCIDevice *d, int pos, uint16_t data); -int xen_host_pci_set_long(XenHostPCIDevice *d, int pos, uint32_t data); -int xen_host_pci_set_block(XenHostPCIDevice *d, int pos, uint8_t *buf, - int len); - -int xen_host_pci_find_ext_cap_offset(XenHostPCIDevice *s, uint32_t cap); - -#endif /* !XEN_HOST_PCI_DEVICE_H_ */ diff --git a/qemu/hw/xen/xen_backend.c b/qemu/hw/xen/xen_backend.c deleted file mode 100644 index 60575ad38..000000000 --- a/qemu/hw/xen/xen_backend.c +++ /dev/null @@ -1,802 +0,0 @@ -/* - * xen backend driver infrastructure - * (c) 2008 Gerd Hoffmann - * - * 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; under version 2 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 . - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -/* - * TODO: add some xenbus / xenstore concepts overview here. - */ - -#include "qemu/osdep.h" -#include -#include - -#include "hw/hw.h" -#include "sysemu/char.h" -#include "qemu/log.h" -#include "hw/xen/xen_backend.h" - -#include - -/* ------------------------------------------------------------- */ - -/* public */ -xc_interface *xen_xc = NULL; -xenforeignmemory_handle *xen_fmem = NULL; -struct xs_handle *xenstore = NULL; -const char *xen_protocol; - -/* private */ -static QTAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = QTAILQ_HEAD_INITIALIZER(xendevs); -static int debug = 0; - -/* ------------------------------------------------------------- */ - -int xenstore_write_str(const char *base, const char *node, const char *val) -{ - char abspath[XEN_BUFSIZE]; - - snprintf(abspath, sizeof(abspath), "%s/%s", base, node); - if (!xs_write(xenstore, 0, abspath, val, strlen(val))) { - return -1; - } - return 0; -} - -char *xenstore_read_str(const char *base, const char *node) -{ - char abspath[XEN_BUFSIZE]; - unsigned int len; - char *str, *ret = NULL; - - snprintf(abspath, sizeof(abspath), "%s/%s", base, node); - str = xs_read(xenstore, 0, abspath, &len); - if (str != NULL) { - /* move to qemu-allocated memory to make sure - * callers can savely g_free() stuff. */ - ret = g_strdup(str); - free(str); - } - return ret; -} - -int xenstore_write_int(const char *base, const char *node, int ival) -{ - char val[12]; - - snprintf(val, sizeof(val), "%d", ival); - return xenstore_write_str(base, node, val); -} - -int xenstore_write_int64(const char *base, const char *node, int64_t ival) -{ - char val[21]; - - snprintf(val, sizeof(val), "%"PRId64, ival); - return xenstore_write_str(base, node, val); -} - -int xenstore_read_int(const char *base, const char *node, int *ival) -{ - char *val; - int rc = -1; - - val = xenstore_read_str(base, node); - if (val && 1 == sscanf(val, "%d", ival)) { - rc = 0; - } - g_free(val); - return rc; -} - -int xenstore_read_uint64(const char *base, const char *node, uint64_t *uval) -{ - char *val; - int rc = -1; - - val = xenstore_read_str(base, node); - if (val && 1 == sscanf(val, "%"SCNu64, uval)) { - rc = 0; - } - g_free(val); - return rc; -} - -int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val) -{ - return xenstore_write_str(xendev->be, node, val); -} - -int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival) -{ - return xenstore_write_int(xendev->be, node, ival); -} - -int xenstore_write_be_int64(struct XenDevice *xendev, const char *node, int64_t ival) -{ - return xenstore_write_int64(xendev->be, node, ival); -} - -char *xenstore_read_be_str(struct XenDevice *xendev, const char *node) -{ - return xenstore_read_str(xendev->be, node); -} - -int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival) -{ - return xenstore_read_int(xendev->be, node, ival); -} - -char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node) -{ - return xenstore_read_str(xendev->fe, node); -} - -int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival) -{ - return xenstore_read_int(xendev->fe, node, ival); -} - -int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node, uint64_t *uval) -{ - return xenstore_read_uint64(xendev->fe, node, uval); -} - -/* ------------------------------------------------------------- */ - -const char *xenbus_strstate(enum xenbus_state state) -{ - static const char *const name[] = { - [ XenbusStateUnknown ] = "Unknown", - [ XenbusStateInitialising ] = "Initialising", - [ XenbusStateInitWait ] = "InitWait", - [ XenbusStateInitialised ] = "Initialised", - [ XenbusStateConnected ] = "Connected", - [ XenbusStateClosing ] = "Closing", - [ XenbusStateClosed ] = "Closed", - }; - return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID"; -} - -int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) -{ - int rc; - - rc = xenstore_write_be_int(xendev, "state", state); - if (rc < 0) { - return rc; - } - xen_be_printf(xendev, 1, "backend state: %s -> %s\n", - xenbus_strstate(xendev->be_state), xenbus_strstate(state)); - xendev->be_state = state; - return 0; -} - -/* ------------------------------------------------------------- */ - -struct XenDevice *xen_be_find_xendev(const char *type, int dom, int dev) -{ - struct XenDevice *xendev; - - QTAILQ_FOREACH(xendev, &xendevs, next) { - if (xendev->dom != dom) { - continue; - } - if (xendev->dev != dev) { - continue; - } - if (strcmp(xendev->type, type) != 0) { - continue; - } - return xendev; - } - return NULL; -} - -/* - * get xen backend device, allocate a new one if it doesn't exist. - */ -static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, - struct XenDevOps *ops) -{ - struct XenDevice *xendev; - - xendev = xen_be_find_xendev(type, dom, dev); - if (xendev) { - return xendev; - } - - /* init new xendev */ - xendev = g_malloc0(ops->size); - xendev->type = type; - xendev->dom = dom; - xendev->dev = dev; - xendev->ops = ops; - - snprintf(xendev->be, sizeof(xendev->be), "backend/%s/%d/%d", - xendev->type, xendev->dom, xendev->dev); - snprintf(xendev->name, sizeof(xendev->name), "%s-%d", - xendev->type, xendev->dev); - - xendev->debug = debug; - xendev->local_port = -1; - - 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(xenevtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC); - - if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { - xendev->gnttabdev = xengnttab_open(NULL, 0); - if (xendev->gnttabdev == NULL) { - xen_be_printf(NULL, 0, "can't open gnttab device\n"); - xenevtchn_close(xendev->evtchndev); - g_free(xendev); - return NULL; - } - } else { - xendev->gnttabdev = NULL; - } - - QTAILQ_INSERT_TAIL(&xendevs, xendev, next); - - if (xendev->ops->alloc) { - xendev->ops->alloc(xendev); - } - - return xendev; -} - -/* - * release xen backend device. - */ -static struct XenDevice *xen_be_del_xendev(int dom, int dev) -{ - struct XenDevice *xendev, *xnext; - - /* - * This is pretty much like QTAILQ_FOREACH(xendev, &xendevs, next) but - * we save the next pointer in xnext because we might free xendev. - */ - xnext = xendevs.tqh_first; - while (xnext) { - xendev = xnext; - xnext = xendev->next.tqe_next; - - if (xendev->dom != dom) { - continue; - } - if (xendev->dev != dev && dev != -1) { - continue; - } - - if (xendev->ops->free) { - xendev->ops->free(xendev); - } - - if (xendev->fe) { - char token[XEN_BUFSIZE]; - snprintf(token, sizeof(token), "fe:%p", xendev); - xs_unwatch(xenstore, xendev->fe, token); - g_free(xendev->fe); - } - - if (xendev->evtchndev != NULL) { - xenevtchn_close(xendev->evtchndev); - } - if (xendev->gnttabdev != NULL) { - xengnttab_close(xendev->gnttabdev); - } - - QTAILQ_REMOVE(&xendevs, xendev, next); - g_free(xendev); - } - return NULL; -} - -/* - * Sync internal data structures on xenstore updates. - * Node specifies the changed field. node = NULL means - * update all fields (used for initialization). - */ -static void xen_be_backend_changed(struct XenDevice *xendev, const char *node) -{ - if (node == NULL || strcmp(node, "online") == 0) { - if (xenstore_read_be_int(xendev, "online", &xendev->online) == -1) { - xendev->online = 0; - } - } - - if (node) { - xen_be_printf(xendev, 2, "backend update: %s\n", node); - if (xendev->ops->backend_changed) { - xendev->ops->backend_changed(xendev, node); - } - } -} - -static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node) -{ - int fe_state; - - if (node == NULL || strcmp(node, "state") == 0) { - if (xenstore_read_fe_int(xendev, "state", &fe_state) == -1) { - fe_state = XenbusStateUnknown; - } - if (xendev->fe_state != fe_state) { - xen_be_printf(xendev, 1, "frontend state: %s -> %s\n", - xenbus_strstate(xendev->fe_state), - xenbus_strstate(fe_state)); - } - xendev->fe_state = fe_state; - } - if (node == NULL || strcmp(node, "protocol") == 0) { - g_free(xendev->protocol); - xendev->protocol = xenstore_read_fe_str(xendev, "protocol"); - if (xendev->protocol) { - xen_be_printf(xendev, 1, "frontend protocol: %s\n", xendev->protocol); - } - } - - if (node) { - xen_be_printf(xendev, 2, "frontend update: %s\n", node); - if (xendev->ops->frontend_changed) { - xendev->ops->frontend_changed(xendev, node); - } - } -} - -/* ------------------------------------------------------------- */ -/* Check for possible state transitions and perform them. */ - -/* - * Initial xendev setup. Read frontend path, register watch for it. - * Should succeed once xend finished setting up the backend device. - * - * Also sets initial state (-> Initializing) when done. Which - * only affects the xendev->be_state variable as xenbus should - * already be put into that state by xend. - */ -static int xen_be_try_setup(struct XenDevice *xendev) -{ - char token[XEN_BUFSIZE]; - int be_state; - - if (xenstore_read_be_int(xendev, "state", &be_state) == -1) { - xen_be_printf(xendev, 0, "reading backend state failed\n"); - return -1; - } - - if (be_state != XenbusStateInitialising) { - xen_be_printf(xendev, 0, "initial backend state is wrong (%s)\n", - xenbus_strstate(be_state)); - return -1; - } - - xendev->fe = xenstore_read_be_str(xendev, "frontend"); - if (xendev->fe == NULL) { - xen_be_printf(xendev, 0, "reading frontend path failed\n"); - return -1; - } - - /* setup frontend watch */ - snprintf(token, sizeof(token), "fe:%p", xendev); - if (!xs_watch(xenstore, xendev->fe, token)) { - xen_be_printf(xendev, 0, "watching frontend path (%s) failed\n", - xendev->fe); - return -1; - } - xen_be_set_state(xendev, XenbusStateInitialising); - - xen_be_backend_changed(xendev, NULL); - xen_be_frontend_changed(xendev, NULL); - return 0; -} - -/* - * Try initialize xendev. Prepare everything the backend can do - * without synchronizing with the frontend. Fakes hotplug-status. No - * hotplug involved here because this is about userspace drivers, thus - * there are kernel backend devices which could invoke hotplug. - * - * Goes to InitWait on success. - */ -static int xen_be_try_init(struct XenDevice *xendev) -{ - int rc = 0; - - if (!xendev->online) { - xen_be_printf(xendev, 1, "not online\n"); - return -1; - } - - if (xendev->ops->init) { - rc = xendev->ops->init(xendev); - } - if (rc != 0) { - xen_be_printf(xendev, 1, "init() failed\n"); - return rc; - } - - xenstore_write_be_str(xendev, "hotplug-status", "connected"); - xen_be_set_state(xendev, XenbusStateInitWait); - return 0; -} - -/* - * Try to initialise xendev. Depends on the frontend being ready - * for it (shared ring and evtchn info in xenstore, state being - * Initialised or Connected). - * - * Goes to Connected on success. - */ -static int xen_be_try_initialise(struct XenDevice *xendev) -{ - int rc = 0; - - if (xendev->fe_state != XenbusStateInitialised && - xendev->fe_state != XenbusStateConnected) { - if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { - xen_be_printf(xendev, 2, "frontend not ready, ignoring\n"); - } else { - xen_be_printf(xendev, 2, "frontend not ready (yet)\n"); - return -1; - } - } - - if (xendev->ops->initialise) { - rc = xendev->ops->initialise(xendev); - } - if (rc != 0) { - xen_be_printf(xendev, 0, "initialise() failed\n"); - return rc; - } - - xen_be_set_state(xendev, XenbusStateConnected); - return 0; -} - -/* - * Try to let xendev know that it is connected. Depends on the - * frontend being Connected. Note that this may be called more - * than once since the backend state is not modified. - */ -static void xen_be_try_connected(struct XenDevice *xendev) -{ - if (!xendev->ops->connected) { - return; - } - - if (xendev->fe_state != XenbusStateConnected) { - if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { - xen_be_printf(xendev, 2, "frontend not ready, ignoring\n"); - } else { - xen_be_printf(xendev, 2, "frontend not ready (yet)\n"); - return; - } - } - - xendev->ops->connected(xendev); -} - -/* - * Teardown connection. - * - * Goes to Closed when done. - */ -static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state) -{ - if (xendev->be_state != XenbusStateClosing && - xendev->be_state != XenbusStateClosed && - xendev->ops->disconnect) { - xendev->ops->disconnect(xendev); - } - if (xendev->be_state != state) { - xen_be_set_state(xendev, state); - } -} - -/* - * Try to reset xendev, for reconnection by another frontend instance. - */ -static int xen_be_try_reset(struct XenDevice *xendev) -{ - if (xendev->fe_state != XenbusStateInitialising) { - return -1; - } - - xen_be_printf(xendev, 1, "device reset (for re-connect)\n"); - xen_be_set_state(xendev, XenbusStateInitialising); - return 0; -} - -/* - * state change dispatcher function - */ -void xen_be_check_state(struct XenDevice *xendev) -{ - int rc = 0; - - /* frontend may request shutdown from almost anywhere */ - if (xendev->fe_state == XenbusStateClosing || - xendev->fe_state == XenbusStateClosed) { - xen_be_disconnect(xendev, xendev->fe_state); - return; - } - - /* check for possible backend state transitions */ - for (;;) { - switch (xendev->be_state) { - case XenbusStateUnknown: - rc = xen_be_try_setup(xendev); - break; - case XenbusStateInitialising: - rc = xen_be_try_init(xendev); - break; - case XenbusStateInitWait: - rc = xen_be_try_initialise(xendev); - break; - case XenbusStateConnected: - /* xendev->be_state doesn't change */ - xen_be_try_connected(xendev); - rc = -1; - break; - case XenbusStateClosed: - rc = xen_be_try_reset(xendev); - break; - default: - rc = -1; - } - if (rc != 0) { - break; - } - } -} - -/* ------------------------------------------------------------- */ - -static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops) -{ - struct XenDevice *xendev; - char path[XEN_BUFSIZE], token[XEN_BUFSIZE]; - char **dev = NULL; - unsigned int cdev, j; - - /* setup watch */ - snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops); - snprintf(path, sizeof(path), "backend/%s/%d", type, dom); - if (!xs_watch(xenstore, path, token)) { - xen_be_printf(NULL, 0, "xen be: watching backend path (%s) failed\n", path); - return -1; - } - - /* look for backends */ - dev = xs_directory(xenstore, 0, path, &cdev); - if (!dev) { - return 0; - } - for (j = 0; j < cdev; j++) { - xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops); - if (xendev == NULL) { - continue; - } - xen_be_check_state(xendev); - } - free(dev); - return 0; -} - -static void xenstore_update_be(char *watch, char *type, int dom, - struct XenDevOps *ops) -{ - struct XenDevice *xendev; - char path[XEN_BUFSIZE], *bepath; - unsigned int len, dev; - - len = snprintf(path, sizeof(path), "backend/%s/%d", type, dom); - if (strncmp(path, watch, len) != 0) { - return; - } - if (sscanf(watch+len, "/%u/%255s", &dev, path) != 2) { - strcpy(path, ""); - if (sscanf(watch+len, "/%u", &dev) != 1) { - dev = -1; - } - } - if (dev == -1) { - return; - } - - xendev = xen_be_get_xendev(type, dom, dev, ops); - if (xendev != NULL) { - bepath = xs_read(xenstore, 0, xendev->be, &len); - if (bepath == NULL) { - xen_be_del_xendev(dom, dev); - } else { - free(bepath); - xen_be_backend_changed(xendev, path); - xen_be_check_state(xendev); - } - } -} - -static void xenstore_update_fe(char *watch, struct XenDevice *xendev) -{ - char *node; - unsigned int len; - - len = strlen(xendev->fe); - if (strncmp(xendev->fe, watch, len) != 0) { - return; - } - if (watch[len] != '/') { - return; - } - node = watch + len + 1; - - xen_be_frontend_changed(xendev, node); - xen_be_check_state(xendev); -} - -static void xenstore_update(void *unused) -{ - char **vec = NULL; - intptr_t type, ops, ptr; - unsigned int dom, count; - - vec = xs_read_watch(xenstore, &count); - if (vec == NULL) { - goto cleanup; - } - - if (sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR, - &type, &dom, &ops) == 3) { - xenstore_update_be(vec[XS_WATCH_PATH], (void*)type, dom, (void*)ops); - } - if (sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr) == 1) { - xenstore_update_fe(vec[XS_WATCH_PATH], (void*)ptr); - } - -cleanup: - free(vec); -} - -static void xen_be_evtchn_event(void *opaque) -{ - struct XenDevice *xendev = opaque; - evtchn_port_t port; - - port = xenevtchn_pending(xendev->evtchndev); - if (port != xendev->local_port) { - xen_be_printf(xendev, 0, - "xenevtchn_pending returned %d (expected %d)\n", - port, xendev->local_port); - return; - } - xenevtchn_unmask(xendev->evtchndev, port); - - if (xendev->ops->event) { - xendev->ops->event(xendev); - } -} - -/* -------------------------------------------------------------------- */ - -int xen_be_init(void) -{ - xenstore = xs_daemon_open(); - if (!xenstore) { - xen_be_printf(NULL, 0, "can't connect to xenstored\n"); - return -1; - } - - qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL); - - if (xen_xc == NULL || xen_fmem == NULL) { - /* Check if xen_init() have been called */ - goto err; - } - return 0; - -err: - qemu_set_fd_handler(xs_fileno(xenstore), NULL, NULL, NULL); - xs_daemon_close(xenstore); - xenstore = NULL; - - return -1; -} - -int xen_be_register(const char *type, struct XenDevOps *ops) -{ - return xenstore_scan(type, xen_domid, ops); -} - -int xen_be_bind_evtchn(struct XenDevice *xendev) -{ - if (xendev->local_port != -1) { - return 0; - } - xendev->local_port = xenevtchn_bind_interdomain - (xendev->evtchndev, xendev->dom, xendev->remote_port); - if (xendev->local_port == -1) { - 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(xenevtchn_fd(xendev->evtchndev), - xen_be_evtchn_event, NULL, xendev); - return 0; -} - -void xen_be_unbind_evtchn(struct XenDevice *xendev) -{ - if (xendev->local_port == -1) { - return; - } - 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 xenevtchn_notify(xendev->evtchndev, xendev->local_port); -} - -/* - * msg_level: - * 0 == errors (stderr + logfile). - * 1 == informative debug messages (logfile only). - * 2 == noisy debug messages (logfile only). - * 3 == will flood your log (logfile only). - */ -void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) -{ - va_list args; - - if (xendev) { - if (msg_level > xendev->debug) { - return; - } - qemu_log("xen be: %s: ", xendev->name); - if (msg_level == 0) { - fprintf(stderr, "xen be: %s: ", xendev->name); - } - } else { - if (msg_level > debug) { - return; - } - qemu_log("xen be core: "); - if (msg_level == 0) { - fprintf(stderr, "xen be core: "); - } - } - va_start(args, fmt); - qemu_log_vprintf(fmt, args); - va_end(args); - if (msg_level == 0) { - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - } - qemu_log_flush(); -} diff --git a/qemu/hw/xen/xen_devconfig.c b/qemu/hw/xen/xen_devconfig.c deleted file mode 100644 index 1f30fe4f5..000000000 --- a/qemu/hw/xen/xen_devconfig.c +++ /dev/null @@ -1,176 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/xen/xen_backend.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" - -/* ------------------------------------------------------------- */ - -struct xs_dirs { - char *xs_dir; - QTAILQ_ENTRY(xs_dirs) list; -}; -static QTAILQ_HEAD(xs_dirs_head, xs_dirs) xs_cleanup = QTAILQ_HEAD_INITIALIZER(xs_cleanup); - -static void xen_config_cleanup_dir(char *dir) -{ - struct xs_dirs *d; - - d = g_malloc(sizeof(*d)); - d->xs_dir = dir; - QTAILQ_INSERT_TAIL(&xs_cleanup, d, list); -} - -void xen_config_cleanup(void) -{ - struct xs_dirs *d; - - QTAILQ_FOREACH(d, &xs_cleanup, list) { - xs_rm(xenstore, 0, d->xs_dir); - } -} - -/* ------------------------------------------------------------- */ - -static int xen_config_dev_mkdir(char *dev, int p) -{ - struct xs_permissions perms[2] = {{ - .id = 0, /* set owner: dom0 */ - },{ - .id = xen_domid, - .perms = p, - }}; - - if (!xs_mkdir(xenstore, 0, dev)) { - xen_be_printf(NULL, 0, "xs_mkdir %s: failed\n", dev); - return -1; - } - xen_config_cleanup_dir(g_strdup(dev)); - - if (!xs_set_permissions(xenstore, 0, dev, perms, 2)) { - xen_be_printf(NULL, 0, "xs_set_permissions %s: failed\n", dev); - return -1; - } - return 0; -} - -static int xen_config_dev_dirs(const char *ftype, const char *btype, int vdev, - char *fe, char *be, int len) -{ - char *dom; - - dom = xs_get_domain_path(xenstore, xen_domid); - snprintf(fe, len, "%s/device/%s/%d", dom, ftype, vdev); - free(dom); - - dom = xs_get_domain_path(xenstore, 0); - snprintf(be, len, "%s/backend/%s/%d/%d", dom, btype, xen_domid, vdev); - free(dom); - - xen_config_dev_mkdir(fe, XS_PERM_READ | XS_PERM_WRITE); - xen_config_dev_mkdir(be, XS_PERM_READ); - return 0; -} - -static int xen_config_dev_all(char *fe, char *be) -{ - /* frontend */ - if (xen_protocol) - xenstore_write_str(fe, "protocol", xen_protocol); - - xenstore_write_int(fe, "state", XenbusStateInitialising); - xenstore_write_int(fe, "backend-id", 0); - xenstore_write_str(fe, "backend", be); - - /* backend */ - xenstore_write_str(be, "domain", qemu_name ? qemu_name : "no-name"); - xenstore_write_int(be, "online", 1); - xenstore_write_int(be, "state", XenbusStateInitialising); - xenstore_write_int(be, "frontend-id", xen_domid); - xenstore_write_str(be, "frontend", fe); - - return 0; -} - -/* ------------------------------------------------------------- */ - -int xen_config_dev_blk(DriveInfo *disk) -{ - char fe[256], be[256], device_name[32]; - int vdev = 202 * 256 + 16 * disk->unit; - int cdrom = disk->media_cd; - const char *devtype = cdrom ? "cdrom" : "disk"; - const char *mode = cdrom ? "r" : "w"; - const char *filename = qemu_opt_get(disk->opts, "file"); - - snprintf(device_name, sizeof(device_name), "xvd%c", 'a' + disk->unit); - xen_be_printf(NULL, 1, "config disk %d [%s]: %s\n", - disk->unit, device_name, filename); - xen_config_dev_dirs("vbd", "qdisk", vdev, fe, be, sizeof(fe)); - - /* frontend */ - xenstore_write_int(fe, "virtual-device", vdev); - xenstore_write_str(fe, "device-type", devtype); - - /* backend */ - xenstore_write_str(be, "dev", device_name); - xenstore_write_str(be, "type", "file"); - xenstore_write_str(be, "params", filename); - xenstore_write_str(be, "mode", mode); - - /* common stuff */ - return xen_config_dev_all(fe, be); -} - -int xen_config_dev_nic(NICInfo *nic) -{ - char fe[256], be[256]; - char mac[20]; - int vlan_id = -1; - - net_hub_id_for_client(nic->netdev, &vlan_id); - snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", - nic->macaddr.a[0], nic->macaddr.a[1], nic->macaddr.a[2], - nic->macaddr.a[3], nic->macaddr.a[4], nic->macaddr.a[5]); - xen_be_printf(NULL, 1, "config nic %d: mac=\"%s\"\n", vlan_id, mac); - xen_config_dev_dirs("vif", "qnic", vlan_id, fe, be, sizeof(fe)); - - /* frontend */ - xenstore_write_int(fe, "handle", vlan_id); - xenstore_write_str(fe, "mac", mac); - - /* backend */ - xenstore_write_int(be, "handle", vlan_id); - xenstore_write_str(be, "mac", mac); - - /* common stuff */ - return xen_config_dev_all(fe, be); -} - -int xen_config_dev_vfb(int vdev, const char *type) -{ - char fe[256], be[256]; - - xen_config_dev_dirs("vfb", "vfb", vdev, fe, be, sizeof(fe)); - - /* backend */ - xenstore_write_str(be, "type", type); - - /* common stuff */ - return xen_config_dev_all(fe, be); -} - -int xen_config_dev_vkbd(int vdev) -{ - char fe[256], be[256]; - - xen_config_dev_dirs("vkbd", "vkbd", vdev, fe, be, sizeof(fe)); - return xen_config_dev_all(fe, be); -} - -int xen_config_dev_console(int vdev) -{ - char fe[256], be[256]; - - xen_config_dev_dirs("console", "console", vdev, fe, be, sizeof(fe)); - return xen_config_dev_all(fe, be); -} diff --git a/qemu/hw/xen/xen_pt.c b/qemu/hw/xen/xen_pt.c deleted file mode 100644 index f593b046e..000000000 --- a/qemu/hw/xen/xen_pt.c +++ /dev/null @@ -1,974 +0,0 @@ -/* - * Copyright (c) 2007, Neocleus Corporation. - * Copyright (c) 2007, Intel Corporation. - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Alex Novik - * Allen Kay - * Guy Zana - * - * This file implements direct PCI assignment to a HVM guest - */ - -/* - * Interrupt Disable policy: - * - * INTx interrupt: - * Initialize(register_real_device) - * Map INTx(xc_physdev_map_pirq): - * - * - Set real Interrupt Disable bit to '1'. - * - Set machine_irq and assigned_device->machine_irq to '0'. - * * Don't bind INTx. - * - * Bind INTx(xc_domain_bind_pt_pci_irq): - * - * - Set real Interrupt Disable bit to '1'. - * - Unmap INTx. - * - Decrement xen_pt_mapped_machine_irq[machine_irq] - * - Set assigned_device->machine_irq to '0'. - * - * Write to Interrupt Disable bit by guest software(xen_pt_cmd_reg_write) - * Write '0' - * - Set real bit to '0' if assigned_device->machine_irq isn't '0'. - * - * Write '1' - * - Set real bit to '1'. - * - * MSI interrupt: - * Initialize MSI register(xen_pt_msi_setup, xen_pt_msi_update) - * Bind MSI(xc_domain_update_msi_irq) - * - * - Unmap MSI. - * - Set dev->msi->pirq to '-1'. - * - * MSI-X interrupt: - * Initialize MSI-X register(xen_pt_msix_update_one) - * Bind MSI-X(xc_domain_update_msi_irq) - * - * - Unmap MSI-X. - * - Set entry->pirq to '-1'. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include - -#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" -#include "exec/address-spaces.h" - -#define XEN_PT_NR_IRQS (256) -static uint8_t xen_pt_mapped_machine_irq[XEN_PT_NR_IRQS] = {0}; - -void xen_pt_log(const PCIDevice *d, const char *f, ...) -{ - va_list ap; - - va_start(ap, f); - if (d) { - fprintf(stderr, "[%02x:%02x.%d] ", pci_bus_num(d->bus), - PCI_SLOT(d->devfn), PCI_FUNC(d->devfn)); - } - vfprintf(stderr, f, ap); - va_end(ap); -} - -/* Config Space */ - -static int xen_pt_pci_config_access_check(PCIDevice *d, uint32_t addr, int len) -{ - /* check offset range */ - if (addr >= 0xFF) { - XEN_PT_ERR(d, "Failed to access register with offset exceeding 0xFF. " - "(addr: 0x%02x, len: %d)\n", addr, len); - return -1; - } - - /* check read size */ - if ((len != 1) && (len != 2) && (len != 4)) { - XEN_PT_ERR(d, "Failed to access register with invalid access length. " - "(addr: 0x%02x, len: %d)\n", addr, len); - return -1; - } - - /* check offset alignment */ - if (addr & (len - 1)) { - XEN_PT_ERR(d, "Failed to access register with invalid access size " - "alignment. (addr: 0x%02x, len: %d)\n", addr, len); - return -1; - } - - return 0; -} - -int xen_pt_bar_offset_to_index(uint32_t offset) -{ - int index = 0; - - /* check Exp ROM BAR */ - if (offset == PCI_ROM_ADDRESS) { - return PCI_ROM_SLOT; - } - - /* calculate BAR index */ - index = (offset - PCI_BASE_ADDRESS_0) >> 2; - if (index >= PCI_NUM_REGIONS) { - return -1; - } - - return index; -} - -static uint32_t xen_pt_pci_read_config(PCIDevice *d, uint32_t addr, int len) -{ - XenPCIPassthroughState *s = XEN_PT_DEVICE(d); - uint32_t val = 0; - XenPTRegGroup *reg_grp_entry = NULL; - XenPTReg *reg_entry = NULL; - int rc = 0; - int emul_len = 0; - uint32_t find_addr = addr; - - if (xen_pt_pci_config_access_check(d, addr, len)) { - goto exit; - } - - /* find register group entry */ - reg_grp_entry = xen_pt_find_reg_grp(s, addr); - if (reg_grp_entry) { - /* check 0-Hardwired register group */ - if (reg_grp_entry->reg_grp->grp_type == XEN_PT_GRP_TYPE_HARDWIRED) { - /* no need to emulate, just return 0 */ - val = 0; - goto exit; - } - } - - /* read I/O device register value */ - rc = xen_host_pci_get_block(&s->real_device, addr, (uint8_t *)&val, len); - if (rc < 0) { - XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc); - memset(&val, 0xff, len); - } - - /* just return the I/O device register value for - * passthrough type register group */ - if (reg_grp_entry == NULL) { - goto exit; - } - - /* adjust the read value to appropriate CFC-CFF window */ - val <<= (addr & 3) << 3; - emul_len = len; - - /* loop around the guest requested size */ - while (emul_len > 0) { - /* find register entry to be emulated */ - reg_entry = xen_pt_find_reg(reg_grp_entry, find_addr); - if (reg_entry) { - XenPTRegInfo *reg = reg_entry->reg; - uint32_t real_offset = reg_grp_entry->base_offset + reg->offset; - uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3); - uint8_t *ptr_val = NULL; - - valid_mask <<= (find_addr - real_offset) << 3; - ptr_val = (uint8_t *)&val + (real_offset & 3); - - /* do emulation based on register size */ - switch (reg->size) { - case 1: - if (reg->u.b.read) { - rc = reg->u.b.read(s, reg_entry, ptr_val, valid_mask); - } - break; - case 2: - if (reg->u.w.read) { - rc = reg->u.w.read(s, reg_entry, - (uint16_t *)ptr_val, valid_mask); - } - break; - case 4: - if (reg->u.dw.read) { - rc = reg->u.dw.read(s, reg_entry, - (uint32_t *)ptr_val, valid_mask); - } - break; - } - - if (rc < 0) { - xen_shutdown_fatal_error("Internal error: Invalid read " - "emulation. (%s, rc: %d)\n", - __func__, rc); - return 0; - } - - /* calculate next address to find */ - emul_len -= reg->size; - if (emul_len > 0) { - find_addr = real_offset + reg->size; - } - } else { - /* nothing to do with passthrough type register, - * continue to find next byte */ - emul_len--; - find_addr++; - } - } - - /* need to shift back before returning them to pci bus emulator */ - val >>= ((addr & 3) << 3); - -exit: - XEN_PT_LOG_CONFIG(d, addr, val, len); - return val; -} - -static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr, - uint32_t val, int len) -{ - XenPCIPassthroughState *s = XEN_PT_DEVICE(d); - int index = 0; - XenPTRegGroup *reg_grp_entry = NULL; - int rc = 0; - uint32_t read_val = 0, wb_mask; - int emul_len = 0; - XenPTReg *reg_entry = NULL; - uint32_t find_addr = addr; - XenPTRegInfo *reg = NULL; - bool wp_flag = false; - - if (xen_pt_pci_config_access_check(d, addr, len)) { - return; - } - - XEN_PT_LOG_CONFIG(d, addr, val, len); - - /* check unused BAR register */ - index = xen_pt_bar_offset_to_index(addr); - if ((index >= 0) && (val != 0)) { - uint32_t chk = val; - - if (index == PCI_ROM_SLOT) - chk |= (uint32_t)~PCI_ROM_ADDRESS_MASK; - - if ((chk != XEN_PT_BAR_ALLF) && - (s->bases[index].bar_flag == XEN_PT_BAR_FLAG_UNUSED)) { - XEN_PT_WARN(d, "Guest attempt to set address to unused " - "Base Address Register. (addr: 0x%02x, len: %d)\n", - addr, len); - } - } - - /* find register group entry */ - reg_grp_entry = xen_pt_find_reg_grp(s, addr); - if (reg_grp_entry) { - /* check 0-Hardwired register group */ - if (reg_grp_entry->reg_grp->grp_type == XEN_PT_GRP_TYPE_HARDWIRED) { - /* ignore silently */ - XEN_PT_WARN(d, "Access to 0-Hardwired register. " - "(addr: 0x%02x, len: %d)\n", addr, len); - return; - } - } - - rc = xen_host_pci_get_block(&s->real_device, addr, - (uint8_t *)&read_val, len); - if (rc < 0) { - XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc); - memset(&read_val, 0xff, len); - wb_mask = 0; - } else { - wb_mask = 0xFFFFFFFF >> ((4 - len) << 3); - } - - /* pass directly to the real device for passthrough type register group */ - if (reg_grp_entry == NULL) { - if (!s->permissive) { - wb_mask = 0; - wp_flag = true; - } - goto out; - } - - memory_region_transaction_begin(); - pci_default_write_config(d, addr, val, len); - - /* adjust the read and write value to appropriate CFC-CFF window */ - read_val <<= (addr & 3) << 3; - val <<= (addr & 3) << 3; - emul_len = len; - - /* loop around the guest requested size */ - while (emul_len > 0) { - /* find register entry to be emulated */ - reg_entry = xen_pt_find_reg(reg_grp_entry, find_addr); - if (reg_entry) { - reg = reg_entry->reg; - uint32_t real_offset = reg_grp_entry->base_offset + reg->offset; - uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3); - uint8_t *ptr_val = NULL; - uint32_t wp_mask = reg->emu_mask | reg->ro_mask; - - valid_mask <<= (find_addr - real_offset) << 3; - ptr_val = (uint8_t *)&val + (real_offset & 3); - if (!s->permissive) { - wp_mask |= reg->res_mask; - } - if (wp_mask == (0xFFFFFFFF >> ((4 - reg->size) << 3))) { - wb_mask &= ~((wp_mask >> ((find_addr - real_offset) << 3)) - << ((len - emul_len) << 3)); - } - - /* do emulation based on register size */ - switch (reg->size) { - case 1: - if (reg->u.b.write) { - rc = reg->u.b.write(s, reg_entry, ptr_val, - read_val >> ((real_offset & 3) << 3), - valid_mask); - } - break; - case 2: - if (reg->u.w.write) { - rc = reg->u.w.write(s, reg_entry, (uint16_t *)ptr_val, - (read_val >> ((real_offset & 3) << 3)), - valid_mask); - } - break; - case 4: - if (reg->u.dw.write) { - rc = reg->u.dw.write(s, reg_entry, (uint32_t *)ptr_val, - (read_val >> ((real_offset & 3) << 3)), - valid_mask); - } - break; - } - - if (rc < 0) { - xen_shutdown_fatal_error("Internal error: Invalid write" - " emulation. (%s, rc: %d)\n", - __func__, rc); - return; - } - - /* calculate next address to find */ - emul_len -= reg->size; - if (emul_len > 0) { - find_addr = real_offset + reg->size; - } - } else { - /* nothing to do with passthrough type register, - * continue to find next byte */ - if (!s->permissive) { - wb_mask &= ~(0xff << ((len - emul_len) << 3)); - /* Unused BARs will make it here, but we don't want to issue - * warnings for writes to them (bogus writes get dealt with - * above). - */ - if (index < 0) { - wp_flag = true; - } - } - emul_len--; - find_addr++; - } - } - - /* need to shift back before passing them to xen_host_pci_set_block. */ - val >>= (addr & 3) << 3; - - memory_region_transaction_commit(); - -out: - if (wp_flag && !s->permissive_warned) { - s->permissive_warned = true; - xen_pt_log(d, "Write-back to unknown field 0x%02x (partially) inhibited (0x%0*x)\n", - addr, len * 2, wb_mask); - xen_pt_log(d, "If the device doesn't work, try enabling permissive mode\n"); - xen_pt_log(d, "(unsafe) and if it helps report the problem to xen-devel\n"); - } - for (index = 0; wb_mask; index += len) { - /* unknown regs are passed through */ - while (!(wb_mask & 0xff)) { - index++; - wb_mask >>= 8; - } - len = 0; - do { - len++; - wb_mask >>= 8; - } while (wb_mask & 0xff); - rc = xen_host_pci_set_block(&s->real_device, addr + index, - (uint8_t *)&val + index, len); - - if (rc < 0) { - XEN_PT_ERR(d, "xen_host_pci_set_block failed. return value: %d.\n", rc); - } - } -} - -/* register regions */ - -static uint64_t xen_pt_bar_read(void *o, hwaddr addr, - unsigned size) -{ - PCIDevice *d = o; - /* if this function is called, that probably means that there is a - * misconfiguration of the IOMMU. */ - XEN_PT_ERR(d, "Should not read BAR through QEMU. @0x"TARGET_FMT_plx"\n", - addr); - return 0; -} -static void xen_pt_bar_write(void *o, hwaddr addr, uint64_t val, - unsigned size) -{ - PCIDevice *d = o; - /* Same comment as xen_pt_bar_read function */ - XEN_PT_ERR(d, "Should not write BAR through QEMU. @0x"TARGET_FMT_plx"\n", - addr); -} - -static const MemoryRegionOps ops = { - .endianness = DEVICE_NATIVE_ENDIAN, - .read = xen_pt_bar_read, - .write = xen_pt_bar_write, -}; - -static int xen_pt_register_regions(XenPCIPassthroughState *s, uint16_t *cmd) -{ - int i = 0; - XenHostPCIDevice *d = &s->real_device; - - /* Register PIO/MMIO BARs */ - for (i = 0; i < PCI_ROM_SLOT; i++) { - XenHostPCIIORegion *r = &d->io_regions[i]; - uint8_t type; - - if (r->base_addr == 0 || r->size == 0) { - continue; - } - - s->bases[i].access.u = r->base_addr; - - if (r->type & XEN_HOST_PCI_REGION_TYPE_IO) { - type = PCI_BASE_ADDRESS_SPACE_IO; - *cmd |= PCI_COMMAND_IO; - } else { - type = PCI_BASE_ADDRESS_SPACE_MEMORY; - if (r->type & XEN_HOST_PCI_REGION_TYPE_PREFETCH) { - type |= PCI_BASE_ADDRESS_MEM_PREFETCH; - } - if (r->type & XEN_HOST_PCI_REGION_TYPE_MEM_64) { - type |= PCI_BASE_ADDRESS_MEM_TYPE_64; - } - *cmd |= PCI_COMMAND_MEMORY; - } - - memory_region_init_io(&s->bar[i], OBJECT(s), &ops, &s->dev, - "xen-pci-pt-bar", r->size); - pci_register_bar(&s->dev, i, type, &s->bar[i]); - - XEN_PT_LOG(&s->dev, "IO region %i registered (size=0x%08"PRIx64 - " base_addr=0x%08"PRIx64" type: %#x)\n", - i, r->size, r->base_addr, type); - } - - /* Register expansion ROM address */ - if (d->rom.base_addr && d->rom.size) { - uint32_t bar_data = 0; - - /* Re-set BAR reported by OS, otherwise ROM can't be read. */ - if (xen_host_pci_get_long(d, PCI_ROM_ADDRESS, &bar_data)) { - return 0; - } - if ((bar_data & PCI_ROM_ADDRESS_MASK) == 0) { - bar_data |= d->rom.base_addr & PCI_ROM_ADDRESS_MASK; - xen_host_pci_set_long(d, PCI_ROM_ADDRESS, bar_data); - } - - s->bases[PCI_ROM_SLOT].access.maddr = d->rom.base_addr; - - memory_region_init_io(&s->rom, OBJECT(s), &ops, &s->dev, - "xen-pci-pt-rom", d->rom.size); - pci_register_bar(&s->dev, PCI_ROM_SLOT, PCI_BASE_ADDRESS_MEM_PREFETCH, - &s->rom); - - XEN_PT_LOG(&s->dev, "Expansion ROM registered (size=0x%08"PRIx64 - " base_addr=0x%08"PRIx64")\n", - d->rom.size, d->rom.base_addr); - } - - xen_pt_register_vga_regions(d); - return 0; -} - -/* region mapping */ - -static int xen_pt_bar_from_region(XenPCIPassthroughState *s, MemoryRegion *mr) -{ - int i = 0; - - for (i = 0; i < PCI_NUM_REGIONS - 1; i++) { - if (mr == &s->bar[i]) { - return i; - } - } - if (mr == &s->rom) { - return PCI_ROM_SLOT; - } - return -1; -} - -/* - * This function checks if an io_region overlaps an io_region from another - * device. The io_region to check is provided with (addr, size and type) - * A callback can be provided and will be called for every region that is - * overlapped. - * The return value indicates if the region is overlappsed */ -struct CheckBarArgs { - XenPCIPassthroughState *s; - pcibus_t addr; - pcibus_t size; - uint8_t type; - bool rc; -}; -static void xen_pt_check_bar_overlap(PCIBus *bus, PCIDevice *d, void *opaque) -{ - struct CheckBarArgs *arg = opaque; - XenPCIPassthroughState *s = arg->s; - uint8_t type = arg->type; - int i; - - if (d->devfn == s->dev.devfn) { - return; - } - - /* xxx: This ignores bridges. */ - for (i = 0; i < PCI_NUM_REGIONS; i++) { - const PCIIORegion *r = &d->io_regions[i]; - - if (!r->size) { - continue; - } - if ((type & PCI_BASE_ADDRESS_SPACE_IO) - != (r->type & PCI_BASE_ADDRESS_SPACE_IO)) { - continue; - } - - if (ranges_overlap(arg->addr, arg->size, r->addr, r->size)) { - XEN_PT_WARN(&s->dev, - "Overlapped to device [%02x:%02x.%d] Region: %i" - " (addr: %#"FMT_PCIBUS", len: %#"FMT_PCIBUS")\n", - pci_bus_num(bus), PCI_SLOT(d->devfn), - PCI_FUNC(d->devfn), i, r->addr, r->size); - arg->rc = true; - } - } -} - -static void xen_pt_region_update(XenPCIPassthroughState *s, - MemoryRegionSection *sec, bool adding) -{ - PCIDevice *d = &s->dev; - MemoryRegion *mr = sec->mr; - int bar = -1; - int rc; - int op = adding ? DPCI_ADD_MAPPING : DPCI_REMOVE_MAPPING; - struct CheckBarArgs args = { - .s = s, - .addr = sec->offset_within_address_space, - .size = int128_get64(sec->size), - .rc = false, - }; - - bar = xen_pt_bar_from_region(s, mr); - if (bar == -1 && (!s->msix || &s->msix->mmio != mr)) { - return; - } - - if (s->msix && &s->msix->mmio == mr) { - if (adding) { - s->msix->mmio_base_addr = sec->offset_within_address_space; - rc = xen_pt_msix_update_remap(s, s->msix->bar_index); - } - return; - } - - args.type = d->io_regions[bar].type; - pci_for_each_device(d->bus, pci_bus_num(d->bus), - xen_pt_check_bar_overlap, &args); - if (args.rc) { - XEN_PT_WARN(d, "Region: %d (addr: %#"FMT_PCIBUS - ", len: %#"FMT_PCIBUS") is overlapped.\n", - bar, sec->offset_within_address_space, - int128_get64(sec->size)); - } - - if (d->io_regions[bar].type & PCI_BASE_ADDRESS_SPACE_IO) { - uint32_t guest_port = sec->offset_within_address_space; - uint32_t machine_port = s->bases[bar].access.pio_base; - uint32_t size = int128_get64(sec->size); - rc = xc_domain_ioport_mapping(xen_xc, xen_domid, - guest_port, machine_port, size, - op); - if (rc) { - XEN_PT_ERR(d, "%s ioport mapping failed! (err: %i)\n", - adding ? "create new" : "remove old", errno); - } - } else { - pcibus_t guest_addr = sec->offset_within_address_space; - pcibus_t machine_addr = s->bases[bar].access.maddr - + sec->offset_within_region; - pcibus_t size = int128_get64(sec->size); - rc = xc_domain_memory_mapping(xen_xc, xen_domid, - XEN_PFN(guest_addr + XC_PAGE_SIZE - 1), - XEN_PFN(machine_addr + XC_PAGE_SIZE - 1), - XEN_PFN(size + XC_PAGE_SIZE - 1), - op); - if (rc) { - XEN_PT_ERR(d, "%s mem mapping failed! (err: %i)\n", - adding ? "create new" : "remove old", errno); - } - } -} - -static void xen_pt_region_add(MemoryListener *l, MemoryRegionSection *sec) -{ - XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState, - memory_listener); - - memory_region_ref(sec->mr); - xen_pt_region_update(s, sec, true); -} - -static void xen_pt_region_del(MemoryListener *l, MemoryRegionSection *sec) -{ - XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState, - memory_listener); - - xen_pt_region_update(s, sec, false); - memory_region_unref(sec->mr); -} - -static void xen_pt_io_region_add(MemoryListener *l, MemoryRegionSection *sec) -{ - XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState, - io_listener); - - memory_region_ref(sec->mr); - xen_pt_region_update(s, sec, true); -} - -static void xen_pt_io_region_del(MemoryListener *l, MemoryRegionSection *sec) -{ - XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState, - io_listener); - - xen_pt_region_update(s, sec, false); - memory_region_unref(sec->mr); -} - -static const MemoryListener xen_pt_memory_listener = { - .region_add = xen_pt_region_add, - .region_del = xen_pt_region_del, - .priority = 10, -}; - -static const MemoryListener xen_pt_io_listener = { - .region_add = xen_pt_io_region_add, - .region_del = xen_pt_io_region_del, - .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 void xen_pt_realize(PCIDevice *d, Error **errp) -{ - XenPCIPassthroughState *s = XEN_PT_DEVICE(d); - 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" - " to devfn %#x\n", - s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function, - s->dev.devfn); - - 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; - if (s->is_virtfn) { - XEN_PT_LOG(d, "%04x:%02x:%02x.%d is a SR-IOV Virtual Function\n", - s->real_device.domain, s->real_device.bus, - s->real_device.dev, s->real_device.func); - } - - /* Initialize virtualized PCI configuration (Extended 256 Bytes) */ - 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 */ - 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 */ - 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) { - 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; - machine_irq = 0; - s->machine_irq = 0; - } else { - machine_irq = pirq; - s->machine_irq = pirq; - xen_pt_mapped_machine_irq[machine_irq]++; - } - - /* bind machine_irq to device */ - if (machine_irq != 0) { - uint8_t e_intx = xen_pt_pci_intx(s); - - rc = xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, machine_irq, - pci_bus_num(d->bus), - PCI_SLOT(d->devfn), - e_intx); - if (rc < 0) { - 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; - xen_pt_mapped_machine_irq[machine_irq]--; - - if (xen_pt_mapped_machine_irq[machine_irq] == 0) { - if (xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq)) { - error_setg_errno(errp, errno, "Unmapping of machine" - " interrupt %u failed", machine_irq); - } - } - s->machine_irq = 0; - } - } - -out: - if (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", - s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function); - - return; - -err_out: - for (i = 0; i < PCI_ROM_SLOT; i++) { - object_unparent(OBJECT(&s->bar[i])); - } - object_unparent(OBJECT(&s->rom)); - - xen_pt_destroy(d); - assert(rc); -} - -static void xen_pt_unregister_device(PCIDevice *d) -{ - xen_pt_destroy(d); -} - -static Property xen_pci_passthrough_properties[] = { - DEFINE_PROP_PCI_HOST_DEVADDR("hostaddr", XenPCIPassthroughState, hostaddr), - DEFINE_PROP_BOOL("permissive", XenPCIPassthroughState, permissive, false), - DEFINE_PROP_END_OF_LIST(), -}; - -static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - 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; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->desc = "Assign an host PCI device with Xen"; - 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, -}; - -static void xen_pci_passthrough_register_types(void) -{ - type_register_static(&xen_pci_passthrough_info); -} - -type_init(xen_pci_passthrough_register_types) diff --git a/qemu/hw/xen/xen_pt.h b/qemu/hw/xen/xen_pt.h deleted file mode 100644 index c2f8e1fc2..000000000 --- a/qemu/hw/xen/xen_pt.h +++ /dev/null @@ -1,335 +0,0 @@ -#ifndef XEN_PT_H -#define XEN_PT_H - -#include "qemu-common.h" -#include "hw/xen/xen_common.h" -#include "hw/pci/pci.h" -#include "xen-host-pci-device.h" - -void xen_pt_log(const PCIDevice *d, const char *f, ...) GCC_FMT_ATTR(2, 3); - -#define XEN_PT_ERR(d, _f, _a...) xen_pt_log(d, "%s: Error: "_f, __func__, ##_a) - -#ifdef XEN_PT_LOGGING_ENABLED -# define XEN_PT_LOG(d, _f, _a...) xen_pt_log(d, "%s: " _f, __func__, ##_a) -# define XEN_PT_WARN(d, _f, _a...) \ - xen_pt_log(d, "%s: Warning: "_f, __func__, ##_a) -#else -# define XEN_PT_LOG(d, _f, _a...) -# define XEN_PT_WARN(d, _f, _a...) -#endif - -#ifdef XEN_PT_DEBUG_PCI_CONFIG_ACCESS -# define XEN_PT_LOG_CONFIG(d, addr, val, len) \ - xen_pt_log(d, "%s: address=0x%04x val=0x%08x len=%d\n", \ - __func__, addr, val, len) -#else -# define XEN_PT_LOG_CONFIG(d, addr, val, len) -#endif - - -/* Helper */ -#define XEN_PFN(x) ((x) >> XC_PAGE_SHIFT) - -typedef const struct XenPTRegInfo XenPTRegInfo; -typedef struct XenPTReg XenPTReg; - -typedef struct XenPCIPassthroughState XenPCIPassthroughState; - -#define TYPE_XEN_PT_DEVICE "xen-pci-passthrough" -#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, - uint32_t *data); -typedef int (*xen_pt_conf_dword_write) - (XenPCIPassthroughState *, XenPTReg *cfg_entry, - uint32_t *val, uint32_t dev_value, uint32_t valid_mask); -typedef int (*xen_pt_conf_word_write) - (XenPCIPassthroughState *, XenPTReg *cfg_entry, - uint16_t *val, uint16_t dev_value, uint16_t valid_mask); -typedef int (*xen_pt_conf_byte_write) - (XenPCIPassthroughState *, XenPTReg *cfg_entry, - uint8_t *val, uint8_t dev_value, uint8_t valid_mask); -typedef int (*xen_pt_conf_dword_read) - (XenPCIPassthroughState *, XenPTReg *cfg_entry, - uint32_t *val, uint32_t valid_mask); -typedef int (*xen_pt_conf_word_read) - (XenPCIPassthroughState *, XenPTReg *cfg_entry, - uint16_t *val, uint16_t valid_mask); -typedef int (*xen_pt_conf_byte_read) - (XenPCIPassthroughState *, XenPTReg *cfg_entry, - uint8_t *val, uint8_t valid_mask); - -#define XEN_PT_BAR_ALLF 0xFFFFFFFF -#define XEN_PT_BAR_UNMAPPED (-1) - -#define XEN_PCI_CAP_MAX 48 - -#define XEN_PCI_INTEL_OPREGION 0xfc - -typedef enum { - XEN_PT_GRP_TYPE_HARDWIRED = 0, /* 0 Hardwired reg group */ - XEN_PT_GRP_TYPE_EMU, /* emul reg group */ -} XenPTRegisterGroupType; - -typedef enum { - XEN_PT_BAR_FLAG_MEM = 0, /* Memory type BAR */ - XEN_PT_BAR_FLAG_IO, /* I/O type BAR */ - XEN_PT_BAR_FLAG_UPPER, /* upper 64bit BAR */ - XEN_PT_BAR_FLAG_UNUSED, /* unused BAR */ -} XenPTBarFlag; - - -typedef struct XenPTRegion { - /* BAR flag */ - XenPTBarFlag bar_flag; - /* Translation of the emulated address */ - union { - uint64_t maddr; - uint64_t pio_base; - uint64_t u; - } access; -} XenPTRegion; - -/* XenPTRegInfo declaration - * - only for emulated register (either a part or whole bit). - * - for passthrough register that need special behavior (like interacting with - * other component), set emu_mask to all 0 and specify r/w func properly. - * - do NOT use ALL F for init_val, otherwise the tbl will not be registered. - */ - -/* emulated register information */ -struct XenPTRegInfo { - uint32_t offset; - uint32_t size; - uint32_t init_val; - /* reg reserved field mask (ON:reserved, OFF:defined) */ - 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; - /* read/write function pointer - * for double_word/word/byte size */ - union { - struct { - xen_pt_conf_dword_write write; - xen_pt_conf_dword_read read; - } dw; - struct { - xen_pt_conf_word_write write; - xen_pt_conf_word_read read; - } w; - struct { - xen_pt_conf_byte_write write; - xen_pt_conf_byte_read read; - } b; - } u; -}; - -/* emulated register management */ -struct XenPTReg { - QLIST_ENTRY(XenPTReg) entries; - XenPTRegInfo *reg; - union { - uint8_t *byte; - uint16_t *half_word; - uint32_t *word; - } ptr; /* pointer to dev.config. */ -}; - -typedef const struct XenPTRegGroupInfo XenPTRegGroupInfo; - -/* emul reg group size initialize method */ -typedef int (*xen_pt_reg_size_init_fn) - (XenPCIPassthroughState *, XenPTRegGroupInfo *, - uint32_t base_offset, uint8_t *size); - -/* emulated register group information */ -struct XenPTRegGroupInfo { - uint8_t grp_id; - XenPTRegisterGroupType grp_type; - uint8_t grp_size; - xen_pt_reg_size_init_fn size_init; - XenPTRegInfo *emu_regs; -}; - -/* emul register group management table */ -typedef struct XenPTRegGroup { - QLIST_ENTRY(XenPTRegGroup) entries; - XenPTRegGroupInfo *reg_grp; - uint32_t base_offset; - uint8_t size; - QLIST_HEAD(, XenPTReg) reg_tbl_list; -} XenPTRegGroup; - - -#define XEN_PT_UNASSIGNED_PIRQ (-1) -typedef struct XenPTMSI { - uint16_t flags; - uint32_t addr_lo; /* guest message address */ - uint32_t addr_hi; /* guest message upper address */ - uint16_t data; /* guest message data */ - uint32_t ctrl_offset; /* saved control offset */ - int pirq; /* guest pirq corresponding */ - bool initialized; /* when guest MSI is initialized */ - bool mapped; /* when pirq is mapped */ -} XenPTMSI; - -typedef struct XenPTMSIXEntry { - int pirq; - uint64_t addr; - uint32_t data; - uint32_t latch[4]; - bool updated; /* indicate whether MSI ADDR or DATA is updated */ -} XenPTMSIXEntry; -typedef struct XenPTMSIX { - uint32_t ctrl_offset; - bool enabled; - bool maskall; - int total_entries; - int bar_index; - uint64_t table_base; - uint32_t table_offset_adjust; /* page align mmap */ - uint64_t mmio_base_addr; - MemoryRegion mmio; - void *phys_iomem_base; - XenPTMSIXEntry msix_entry[0]; -} XenPTMSIX; - -struct XenPCIPassthroughState { - PCIDevice dev; - - PCIHostDeviceAddress hostaddr; - bool is_virtfn; - bool permissive; - bool permissive_warned; - XenHostPCIDevice real_device; - XenPTRegion bases[PCI_NUM_REGIONS]; /* Access regions */ - QLIST_HEAD(, XenPTRegGroup) reg_grps; - - uint32_t machine_irq; - - XenPTMSI *msi; - XenPTMSIX *msix; - - MemoryRegion bar[PCI_NUM_REGIONS - 1]; - MemoryRegion rom; - - MemoryListener memory_listener; - MemoryListener io_listener; - bool listener_set; -}; - -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); -int xen_pt_bar_offset_to_index(uint32_t offset); - -static inline pcibus_t xen_pt_get_emul_size(XenPTBarFlag flag, pcibus_t r_size) -{ - /* align resource size (memory type only) */ - if (flag == XEN_PT_BAR_FLAG_MEM) { - return (r_size + XC_PAGE_SIZE - 1) & XC_PAGE_MASK; - } else { - return r_size; - } -} - -/* INTx */ -/* The PCI Local Bus Specification, Rev. 3.0, - * Section 6.2.4 Miscellaneous Registers, pp 223 - * outlines 5 valid values for the interrupt pin (intx). - * 0: For devices (or device functions) that don't use an interrupt in - * 1: INTA# - * 2: INTB# - * 3: INTC# - * 4: INTD# - * - * Xen uses the following 4 values for intx - * 0: INTA# - * 1: INTB# - * 2: INTC# - * 3: INTD# - * - * Observing that these list of values are not the same, xen_pt_pci_read_intx() - * uses the following mapping from hw to xen values. - * This seems to reflect the current usage within Xen. - * - * PCI hardware | Xen | Notes - * ----------------+-----+---------------------------------------------------- - * 0 | 0 | No interrupt - * 1 | 0 | INTA# - * 2 | 1 | INTB# - * 3 | 2 | INTC# - * 4 | 3 | INTD# - * any other value | 0 | This should never happen, log error message - */ - -static inline uint8_t xen_pt_pci_read_intx(XenPCIPassthroughState *s) -{ - uint8_t v = 0; - xen_host_pci_get_byte(&s->real_device, PCI_INTERRUPT_PIN, &v); - return v; -} - -static inline uint8_t xen_pt_pci_intx(XenPCIPassthroughState *s) -{ - uint8_t r_val = xen_pt_pci_read_intx(s); - - XEN_PT_LOG(&s->dev, "intx=%i\n", r_val); - if (r_val < 1 || r_val > 4) { - XEN_PT_LOG(&s->dev, "Interrupt pin read from hardware is out of range:" - " 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; - } - - return r_val; -} - -/* MSI/MSI-X */ -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); - -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 deleted file mode 100644 index 9869ffda0..000000000 --- a/qemu/hw/xen/xen_pt_config_init.c +++ /dev/null @@ -1,2090 +0,0 @@ -/* - * Copyright (c) 2007, Neocleus Corporation. - * Copyright (c) 2007, Intel Corporation. - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Alex Novik - * Allen Kay - * Guy Zana - * - * 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" - -#define XEN_PT_MERGE_VALUE(value, data, val_mask) \ - (((value) & (val_mask)) | ((data) & ~(val_mask))) - -#define XEN_PT_INVALID_REG 0xFFFFFFFF /* invalid register value */ - -/* prototype */ - -static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, - uint32_t real_offset, uint32_t *data); - - -/* helper */ - -/* A return value of 1 means the capability should NOT be exposed to guest. */ -static int xen_pt_hide_dev_cap(const XenHostPCIDevice *d, uint8_t grp_id) -{ - switch (grp_id) { - case PCI_CAP_ID_EXP: - /* The PCI Express Capability Structure of the VF of Intel 82599 10GbE - * Controller looks trivial, e.g., the PCI Express Capabilities - * Register is 0. We should not try to expose it to guest. - * - * The datasheet is available at - * http://download.intel.com/design/network/datashts/82599_datasheet.pdf - * - * See 'Table 9.7. VF PCIe Configuration Space' of the datasheet, the - * PCI Express Capability Structure of the VF of Intel 82599 10GbE - * Controller looks trivial, e.g., the PCI Express Capabilities - * Register is 0, so the Capability Version is 0 and - * xen_pt_pcie_size_init() would fail. - */ - if (d->vendor_id == PCI_VENDOR_ID_INTEL && - d->device_id == PCI_DEVICE_ID_INTEL_82599_SFP_VF) { - return 1; - } - break; - } - return 0; -} - -/* find emulate register group entry */ -XenPTRegGroup *xen_pt_find_reg_grp(XenPCIPassthroughState *s, uint32_t address) -{ - XenPTRegGroup *entry = NULL; - - /* find register group entry */ - QLIST_FOREACH(entry, &s->reg_grps, entries) { - /* check address */ - if ((entry->base_offset <= address) - && ((entry->base_offset + entry->size) > address)) { - return entry; - } - } - - /* group entry not found */ - return NULL; -} - -/* find emulate register entry */ -XenPTReg *xen_pt_find_reg(XenPTRegGroup *reg_grp, uint32_t address) -{ - XenPTReg *reg_entry = NULL; - XenPTRegInfo *reg = NULL; - uint32_t real_offset = 0; - - /* find register entry */ - QLIST_FOREACH(reg_entry, ®_grp->reg_tbl_list, entries) { - reg = reg_entry->reg; - real_offset = reg_grp->base_offset + reg->offset; - /* check address */ - if ((real_offset <= address) - && ((real_offset + reg->size) > address)) { - return reg_entry; - } - } - - return NULL; -} - -static uint32_t get_throughable_mask(const XenPCIPassthroughState *s, - XenPTRegInfo *reg, uint32_t valid_mask) -{ - uint32_t throughable_mask = ~(reg->emu_mask | reg->ro_mask); - - if (!s->permissive) { - throughable_mask &= ~reg->res_mask; - } - - return throughable_mask & valid_mask; -} - -/**************** - * general register functions - */ - -/* register initialization function */ - -static int xen_pt_common_reg_init(XenPCIPassthroughState *s, - XenPTRegInfo *reg, uint32_t real_offset, - uint32_t *data) -{ - *data = reg->init_val; - return 0; -} - -/* Read register functions */ - -static int xen_pt_byte_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, - uint8_t *value, uint8_t valid_mask) -{ - 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, *data, ~valid_emu_mask); - - return 0; -} -static int xen_pt_word_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, - uint16_t *value, uint16_t valid_mask) -{ - 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, *data, ~valid_emu_mask); - - return 0; -} -static int xen_pt_long_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, - uint32_t *value, uint32_t valid_mask) -{ - 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, *data, ~valid_emu_mask); - - return 0; -} - -/* Write register functions */ - -static int xen_pt_byte_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, - uint8_t *val, uint8_t dev_value, - uint8_t valid_mask) -{ - 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; - *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 & ~reg->rw1c_mask, - throughable_mask); - - return 0; -} -static int xen_pt_word_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); - uint16_t *data = cfg_entry->ptr.half_word; - - /* modify emulate register */ - writable_mask = reg->emu_mask & ~reg->ro_mask & valid_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 & ~reg->rw1c_mask, - throughable_mask); - - return 0; -} -static int xen_pt_long_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, - uint32_t *val, uint32_t dev_value, - uint32_t valid_mask) -{ - 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; - *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 & ~reg->rw1c_mask, - throughable_mask); - - return 0; -} - - -/* XenPTRegInfo declaration - * - only for emulated register (either a part or whole bit). - * - for passthrough register that need special behavior (like interacting with - * other component), set emu_mask to all 0 and specify r/w func properly. - * - do NOT use ALL F for init_val, otherwise the tbl will not be registered. - */ - -/******************** - * Header Type0 - */ - -static int xen_pt_vendor_reg_init(XenPCIPassthroughState *s, - XenPTRegInfo *reg, uint32_t real_offset, - uint32_t *data) -{ - *data = s->real_device.vendor_id; - return 0; -} -static int xen_pt_device_reg_init(XenPCIPassthroughState *s, - XenPTRegInfo *reg, uint32_t real_offset, - uint32_t *data) -{ - *data = s->real_device.device_id; - return 0; -} -static int xen_pt_status_reg_init(XenPCIPassthroughState *s, - XenPTRegInfo *reg, uint32_t real_offset, - uint32_t *data) -{ - XenPTRegGroup *reg_grp_entry = NULL; - XenPTReg *reg_entry = NULL; - uint32_t reg_field = 0; - - /* find Header register group */ - reg_grp_entry = xen_pt_find_reg_grp(s, PCI_CAPABILITY_LIST); - if (reg_grp_entry) { - /* find Capabilities Pointer register */ - reg_entry = xen_pt_find_reg(reg_grp_entry, PCI_CAPABILITY_LIST); - if (reg_entry) { - /* check Capabilities Pointer register */ - if (*reg_entry->ptr.half_word) { - reg_field |= PCI_STATUS_CAP_LIST; - } else { - reg_field &= ~PCI_STATUS_CAP_LIST; - } - } else { - xen_shutdown_fatal_error("Internal error: Couldn't find XenPTReg*" - " for Capabilities Pointer register." - " (%s)\n", __func__); - return -1; - } - } else { - xen_shutdown_fatal_error("Internal error: Couldn't find XenPTRegGroup" - " for Header. (%s)\n", __func__); - return -1; - } - - *data = reg_field; - return 0; -} -static int xen_pt_header_type_reg_init(XenPCIPassthroughState *s, - XenPTRegInfo *reg, uint32_t real_offset, - uint32_t *data) -{ - /* read PCI_HEADER_TYPE */ - *data = reg->init_val | 0x80; - return 0; -} - -/* initialize Interrupt Pin register */ -static int xen_pt_irqpin_reg_init(XenPCIPassthroughState *s, - XenPTRegInfo *reg, uint32_t real_offset, - uint32_t *data) -{ - *data = xen_pt_pci_read_intx(s); - return 0; -} - -/* Command register */ -static int xen_pt_cmd_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); - uint16_t *data = cfg_entry->ptr.half_word; - - /* modify emulate register */ - writable_mask = ~reg->ro_mask & valid_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) { - throughable_mask |= PCI_COMMAND_INTX_DISABLE; - } else { - if (s->machine_irq) { - throughable_mask |= PCI_COMMAND_INTX_DISABLE; - } - } - - *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); - - return 0; -} - -/* BAR */ -#define XEN_PT_BAR_MEM_RO_MASK 0x0000000F /* BAR ReadOnly mask(Memory) */ -#define XEN_PT_BAR_MEM_EMU_MASK 0xFFFFFFF0 /* BAR emul mask(Memory) */ -#define XEN_PT_BAR_IO_RO_MASK 0x00000003 /* BAR ReadOnly mask(I/O) */ -#define XEN_PT_BAR_IO_EMU_MASK 0xFFFFFFFC /* BAR emul mask(I/O) */ - -static bool is_64bit_bar(PCIIORegion *r) -{ - return !!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64); -} - -static uint64_t xen_pt_get_bar_size(PCIIORegion *r) -{ - if (is_64bit_bar(r)) { - uint64_t size64; - size64 = (r + 1)->size; - size64 <<= 32; - size64 += r->size; - return size64; - } - return r->size; -} - -static XenPTBarFlag xen_pt_bar_reg_parse(XenPCIPassthroughState *s, - int index) -{ - PCIDevice *d = &s->dev; - XenPTRegion *region = NULL; - PCIIORegion *r; - - /* check 64bit BAR */ - if ((0 < index) && (index < PCI_ROM_SLOT)) { - int type = s->real_device.io_regions[index - 1].type; - - if ((type & XEN_HOST_PCI_REGION_TYPE_MEM) - && (type & XEN_HOST_PCI_REGION_TYPE_MEM_64)) { - region = &s->bases[index - 1]; - if (region->bar_flag != XEN_PT_BAR_FLAG_UPPER) { - return XEN_PT_BAR_FLAG_UPPER; - } - } - } - - /* check unused BAR */ - r = &d->io_regions[index]; - if (!xen_pt_get_bar_size(r)) { - return XEN_PT_BAR_FLAG_UNUSED; - } - - /* for ExpROM BAR */ - if (index == PCI_ROM_SLOT) { - return XEN_PT_BAR_FLAG_MEM; - } - - /* check BAR I/O indicator */ - if (s->real_device.io_regions[index].type & XEN_HOST_PCI_REGION_TYPE_IO) { - return XEN_PT_BAR_FLAG_IO; - } else { - return XEN_PT_BAR_FLAG_MEM; - } -} - -static inline uint32_t base_address_with_flags(XenHostPCIIORegion *hr) -{ - if (hr->type & XEN_HOST_PCI_REGION_TYPE_IO) { - return hr->base_addr | (hr->bus_flags & ~PCI_BASE_ADDRESS_IO_MASK); - } else { - return hr->base_addr | (hr->bus_flags & ~PCI_BASE_ADDRESS_MEM_MASK); - } -} - -static int xen_pt_bar_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, - uint32_t real_offset, uint32_t *data) -{ - uint32_t reg_field = 0; - int index; - - index = xen_pt_bar_offset_to_index(reg->offset); - if (index < 0 || index >= PCI_NUM_REGIONS) { - XEN_PT_ERR(&s->dev, "Internal error: Invalid BAR index [%d].\n", index); - return -1; - } - - /* set BAR flag */ - s->bases[index].bar_flag = xen_pt_bar_reg_parse(s, index); - if (s->bases[index].bar_flag == XEN_PT_BAR_FLAG_UNUSED) { - reg_field = XEN_PT_INVALID_REG; - } - - *data = reg_field; - return 0; -} -static int xen_pt_bar_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, - uint32_t *value, uint32_t valid_mask) -{ - XenPTRegInfo *reg = cfg_entry->reg; - uint32_t valid_emu_mask = 0; - uint32_t bar_emu_mask = 0; - int index; - - /* get BAR index */ - index = xen_pt_bar_offset_to_index(reg->offset); - if (index < 0 || index >= PCI_NUM_REGIONS - 1) { - XEN_PT_ERR(&s->dev, "Internal error: Invalid BAR index [%d].\n", index); - return -1; - } - - /* use fixed-up value from kernel sysfs */ - *value = base_address_with_flags(&s->real_device.io_regions[index]); - - /* set emulate mask depend on BAR flag */ - switch (s->bases[index].bar_flag) { - case XEN_PT_BAR_FLAG_MEM: - bar_emu_mask = XEN_PT_BAR_MEM_EMU_MASK; - break; - case XEN_PT_BAR_FLAG_IO: - bar_emu_mask = XEN_PT_BAR_IO_EMU_MASK; - break; - case XEN_PT_BAR_FLAG_UPPER: - bar_emu_mask = XEN_PT_BAR_ALLF; - break; - default: - break; - } - - /* emulate BAR */ - valid_emu_mask = bar_emu_mask & valid_mask; - *value = XEN_PT_MERGE_VALUE(*value, *cfg_entry->ptr.word, ~valid_emu_mask); - - return 0; -} -static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, - uint32_t *val, uint32_t dev_value, - uint32_t valid_mask) -{ - XenPTRegInfo *reg = cfg_entry->reg; - XenPTRegion *base = NULL; - PCIDevice *d = &s->dev; - const PCIIORegion *r; - uint32_t writable_mask = 0; - uint32_t bar_emu_mask = 0; - 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) { - XEN_PT_ERR(d, "Internal error: Invalid BAR index [%d].\n", index); - return -1; - } - - r = &d->io_regions[index]; - base = &s->bases[index]; - r_size = xen_pt_get_emul_size(base->bar_flag, r->size); - - /* set emulate mask and read-only mask values depend on the BAR flag */ - switch (s->bases[index].bar_flag) { - case XEN_PT_BAR_FLAG_MEM: - bar_emu_mask = XEN_PT_BAR_MEM_EMU_MASK; - if (!r_size) { - /* low 32 bits mask for 64 bit bars */ - bar_ro_mask = XEN_PT_BAR_ALLF; - } else { - bar_ro_mask = XEN_PT_BAR_MEM_RO_MASK | (r_size - 1); - } - break; - case XEN_PT_BAR_FLAG_IO: - bar_emu_mask = XEN_PT_BAR_IO_EMU_MASK; - bar_ro_mask = XEN_PT_BAR_IO_RO_MASK | (r_size - 1); - break; - case XEN_PT_BAR_FLAG_UPPER: - bar_emu_mask = XEN_PT_BAR_ALLF; - bar_ro_mask = r_size ? r_size - 1 : 0; - break; - default: - break; - } - - /* modify emulate register */ - writable_mask = bar_emu_mask & ~bar_ro_mask & valid_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) { - case XEN_PT_BAR_FLAG_UPPER: - case XEN_PT_BAR_FLAG_MEM: - /* nothing to do */ - break; - case XEN_PT_BAR_FLAG_IO: - /* nothing to do */ - break; - default: - break; - } - - /* create value for writing to I/O device register */ - *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0); - - return 0; -} - -/* write Exp ROM BAR */ -static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s, - XenPTReg *cfg_entry, uint32_t *val, - uint32_t dev_value, uint32_t valid_mask) -{ - XenPTRegInfo *reg = cfg_entry->reg; - XenPTRegion *base = NULL; - PCIDevice *d = (PCIDevice *)&s->dev; - uint32_t writable_mask = 0; - 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]; - /* align memory type resource size */ - r_size = xen_pt_get_emul_size(base->bar_flag, r_size); - - /* set emulate mask and read-only mask */ - bar_ro_mask = (reg->ro_mask | (r_size - 1)) & ~PCI_ROM_ADDRESS_ENABLE; - - /* modify emulate register */ - writable_mask = ~bar_ro_mask & valid_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); - - 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 */ - { - .offset = PCI_VENDOR_ID, - .size = 2, - .init_val = 0x0000, - .ro_mask = 0xFFFF, - .emu_mask = 0xFFFF, - .init = xen_pt_vendor_reg_init, - .u.w.read = xen_pt_word_reg_read, - .u.w.write = xen_pt_word_reg_write, - }, - /* Device ID reg */ - { - .offset = PCI_DEVICE_ID, - .size = 2, - .init_val = 0x0000, - .ro_mask = 0xFFFF, - .emu_mask = 0xFFFF, - .init = xen_pt_device_reg_init, - .u.w.read = xen_pt_word_reg_read, - .u.w.write = xen_pt_word_reg_write, - }, - /* Command reg */ - { - .offset = PCI_COMMAND, - .size = 2, - .init_val = 0x0000, - .res_mask = 0xF880, - .emu_mask = 0x0743, - .init = xen_pt_common_reg_init, - .u.w.read = xen_pt_word_reg_read, - .u.w.write = xen_pt_cmd_reg_write, - }, - /* Capabilities Pointer reg */ - { - .offset = PCI_CAPABILITY_LIST, - .size = 1, - .init_val = 0x00, - .ro_mask = 0xFF, - .emu_mask = 0xFF, - .init = xen_pt_ptr_reg_init, - .u.b.read = xen_pt_byte_reg_read, - .u.b.write = xen_pt_byte_reg_write, - }, - /* Status reg */ - /* use emulated Cap Ptr value to initialize, - * so need to be declared after Cap Ptr reg - */ - { - .offset = PCI_STATUS, - .size = 2, - .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, - .u.w.write = xen_pt_word_reg_write, - }, - /* Cache Line Size reg */ - { - .offset = PCI_CACHE_LINE_SIZE, - .size = 1, - .init_val = 0x00, - .ro_mask = 0x00, - .emu_mask = 0xFF, - .init = xen_pt_common_reg_init, - .u.b.read = xen_pt_byte_reg_read, - .u.b.write = xen_pt_byte_reg_write, - }, - /* Latency Timer reg */ - { - .offset = PCI_LATENCY_TIMER, - .size = 1, - .init_val = 0x00, - .ro_mask = 0x00, - .emu_mask = 0xFF, - .init = xen_pt_common_reg_init, - .u.b.read = xen_pt_byte_reg_read, - .u.b.write = xen_pt_byte_reg_write, - }, - /* Header Type reg */ - { - .offset = PCI_HEADER_TYPE, - .size = 1, - .init_val = 0x00, - .ro_mask = 0xFF, - .emu_mask = 0x00, - .init = xen_pt_header_type_reg_init, - .u.b.read = xen_pt_byte_reg_read, - .u.b.write = xen_pt_byte_reg_write, - }, - /* Interrupt Line reg */ - { - .offset = PCI_INTERRUPT_LINE, - .size = 1, - .init_val = 0x00, - .ro_mask = 0x00, - .emu_mask = 0xFF, - .init = xen_pt_common_reg_init, - .u.b.read = xen_pt_byte_reg_read, - .u.b.write = xen_pt_byte_reg_write, - }, - /* Interrupt Pin reg */ - { - .offset = PCI_INTERRUPT_PIN, - .size = 1, - .init_val = 0x00, - .ro_mask = 0xFF, - .emu_mask = 0xFF, - .init = xen_pt_irqpin_reg_init, - .u.b.read = xen_pt_byte_reg_read, - .u.b.write = xen_pt_byte_reg_write, - }, - /* BAR 0 reg */ - /* mask of BAR need to be decided later, depends on IO/MEM type */ - { - .offset = PCI_BASE_ADDRESS_0, - .size = 4, - .init_val = 0x00000000, - .init = xen_pt_bar_reg_init, - .u.dw.read = xen_pt_bar_reg_read, - .u.dw.write = xen_pt_bar_reg_write, - }, - /* BAR 1 reg */ - { - .offset = PCI_BASE_ADDRESS_1, - .size = 4, - .init_val = 0x00000000, - .init = xen_pt_bar_reg_init, - .u.dw.read = xen_pt_bar_reg_read, - .u.dw.write = xen_pt_bar_reg_write, - }, - /* BAR 2 reg */ - { - .offset = PCI_BASE_ADDRESS_2, - .size = 4, - .init_val = 0x00000000, - .init = xen_pt_bar_reg_init, - .u.dw.read = xen_pt_bar_reg_read, - .u.dw.write = xen_pt_bar_reg_write, - }, - /* BAR 3 reg */ - { - .offset = PCI_BASE_ADDRESS_3, - .size = 4, - .init_val = 0x00000000, - .init = xen_pt_bar_reg_init, - .u.dw.read = xen_pt_bar_reg_read, - .u.dw.write = xen_pt_bar_reg_write, - }, - /* BAR 4 reg */ - { - .offset = PCI_BASE_ADDRESS_4, - .size = 4, - .init_val = 0x00000000, - .init = xen_pt_bar_reg_init, - .u.dw.read = xen_pt_bar_reg_read, - .u.dw.write = xen_pt_bar_reg_write, - }, - /* BAR 5 reg */ - { - .offset = PCI_BASE_ADDRESS_5, - .size = 4, - .init_val = 0x00000000, - .init = xen_pt_bar_reg_init, - .u.dw.read = xen_pt_bar_reg_read, - .u.dw.write = xen_pt_bar_reg_write, - }, - /* Expansion ROM BAR reg */ - { - .offset = PCI_ROM_ADDRESS, - .size = 4, - .init_val = 0x00000000, - .ro_mask = ~PCI_ROM_ADDRESS_MASK & ~PCI_ROM_ADDRESS_ENABLE, - .emu_mask = (uint32_t)PCI_ROM_ADDRESS_MASK, - .init = xen_pt_bar_reg_init, - .u.dw.read = xen_pt_long_reg_read, - .u.dw.write = xen_pt_exp_rom_bar_reg_write, - }, - { - .size = 0, - }, -}; - - -/********************************* - * Vital Product Data Capability - */ - -/* Vital Product Data Capability Structure reg static information table */ -static XenPTRegInfo xen_pt_emu_reg_vpd[] = { - { - .offset = PCI_CAP_LIST_NEXT, - .size = 1, - .init_val = 0x00, - .ro_mask = 0xFF, - .emu_mask = 0xFF, - .init = xen_pt_ptr_reg_init, - .u.b.read = xen_pt_byte_reg_read, - .u.b.write = xen_pt_byte_reg_write, - }, - { - .offset = PCI_VPD_ADDR, - .size = 2, - .ro_mask = 0x0003, - .emu_mask = 0x0003, - .init = xen_pt_common_reg_init, - .u.w.read = xen_pt_word_reg_read, - .u.w.write = xen_pt_word_reg_write, - }, - { - .size = 0, - }, -}; - - -/************************************** - * Vendor Specific Capability - */ - -/* Vendor Specific Capability Structure reg static information table */ -static XenPTRegInfo xen_pt_emu_reg_vendor[] = { - { - .offset = PCI_CAP_LIST_NEXT, - .size = 1, - .init_val = 0x00, - .ro_mask = 0xFF, - .emu_mask = 0xFF, - .init = xen_pt_ptr_reg_init, - .u.b.read = xen_pt_byte_reg_read, - .u.b.write = xen_pt_byte_reg_write, - }, - { - .size = 0, - }, -}; - - -/***************************** - * PCI Express Capability - */ - -static inline uint8_t get_capability_version(XenPCIPassthroughState *s, - uint32_t offset) -{ - 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 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 */ -static int xen_pt_linkctrl_reg_init(XenPCIPassthroughState *s, - XenPTRegInfo *reg, uint32_t real_offset, - uint32_t *data) -{ - uint8_t cap_ver = get_capability_version(s, real_offset - reg->offset); - uint8_t dev_type = get_device_type(s, real_offset - reg->offset); - - /* no need to initialize in case of Root Complex Integrated Endpoint - * with cap_ver 1.x - */ - if ((dev_type == PCI_EXP_TYPE_RC_END) && (cap_ver == 1)) { - *data = XEN_PT_INVALID_REG; - } - - *data = reg->init_val; - return 0; -} -/* initialize Device Control 2 register */ -static int xen_pt_devctrl2_reg_init(XenPCIPassthroughState *s, - XenPTRegInfo *reg, uint32_t real_offset, - uint32_t *data) -{ - uint8_t cap_ver = get_capability_version(s, real_offset - reg->offset); - - /* no need to initialize in case of cap_ver 1.x */ - if (cap_ver == 1) { - *data = XEN_PT_INVALID_REG; - } - - *data = reg->init_val; - return 0; -} -/* initialize Link Control 2 register */ -static int xen_pt_linkctrl2_reg_init(XenPCIPassthroughState *s, - XenPTRegInfo *reg, uint32_t real_offset, - uint32_t *data) -{ - uint8_t cap_ver = get_capability_version(s, real_offset - reg->offset); - uint32_t reg_field = 0; - - /* no need to initialize in case of cap_ver 1.x */ - if (cap_ver == 1) { - reg_field = XEN_PT_INVALID_REG; - } else { - /* set Supported Link Speed */ - 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; - } - - *data = reg_field; - return 0; -} - -/* PCI Express Capability Structure reg static information table */ -static XenPTRegInfo xen_pt_emu_reg_pcie[] = { - /* Next Pointer reg */ - { - .offset = PCI_CAP_LIST_NEXT, - .size = 1, - .init_val = 0x00, - .ro_mask = 0xFF, - .emu_mask = 0xFF, - .init = xen_pt_ptr_reg_init, - .u.b.read = xen_pt_byte_reg_read, - .u.b.write = xen_pt_byte_reg_write, - }, - /* Device Capabilities reg */ - { - .offset = PCI_EXP_DEVCAP, - .size = 4, - .init_val = 0x00000000, - .ro_mask = 0xFFFFFFFF, - .emu_mask = 0x10000000, - .init = xen_pt_common_reg_init, - .u.dw.read = xen_pt_long_reg_read, - .u.dw.write = xen_pt_long_reg_write, - }, - /* Device Control reg */ - { - .offset = PCI_EXP_DEVCTL, - .size = 2, - .init_val = 0x2810, - .ro_mask = 0x8400, - .emu_mask = 0xFFFF, - .init = xen_pt_common_reg_init, - .u.w.read = xen_pt_word_reg_read, - .u.w.write = xen_pt_word_reg_write, - }, - /* Device Status reg */ - { - .offset = PCI_EXP_DEVSTA, - .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, - }, - /* Link Control reg */ - { - .offset = PCI_EXP_LNKCTL, - .size = 2, - .init_val = 0x0000, - .ro_mask = 0xFC34, - .emu_mask = 0xFFFF, - .init = xen_pt_linkctrl_reg_init, - .u.w.read = xen_pt_word_reg_read, - .u.w.write = xen_pt_word_reg_write, - }, - /* Link Status reg */ - { - .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, - }, - /* Device Control 2 reg */ - { - .offset = 0x28, - .size = 2, - .init_val = 0x0000, - .ro_mask = 0xFFE0, - .emu_mask = 0xFFFF, - .init = xen_pt_devctrl2_reg_init, - .u.w.read = xen_pt_word_reg_read, - .u.w.write = xen_pt_word_reg_write, - }, - /* Link Control 2 reg */ - { - .offset = 0x30, - .size = 2, - .init_val = 0x0000, - .ro_mask = 0xE040, - .emu_mask = 0xFFFF, - .init = xen_pt_linkctrl2_reg_init, - .u.w.read = xen_pt_word_reg_read, - .u.w.write = xen_pt_word_reg_write, - }, - { - .size = 0, - }, -}; - - -/********************************* - * Power Management Capability - */ - -/* Power Management Capability reg static information table */ -static XenPTRegInfo xen_pt_emu_reg_pm[] = { - /* Next Pointer reg */ - { - .offset = PCI_CAP_LIST_NEXT, - .size = 1, - .init_val = 0x00, - .ro_mask = 0xFF, - .emu_mask = 0xFF, - .init = xen_pt_ptr_reg_init, - .u.b.read = xen_pt_byte_reg_read, - .u.b.write = xen_pt_byte_reg_write, - }, - /* Power Management Capabilities reg */ - { - .offset = PCI_CAP_FLAGS, - .size = 2, - .init_val = 0x0000, - .ro_mask = 0xFFFF, - .emu_mask = 0xF9C8, - .init = xen_pt_common_reg_init, - .u.w.read = xen_pt_word_reg_read, - .u.w.write = xen_pt_word_reg_write, - }, - /* PCI Power Management Control/Status reg */ - { - .offset = PCI_PM_CTRL, - .size = 2, - .init_val = 0x0008, - .res_mask = 0x00F0, - .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_word_reg_write, - }, - { - .size = 0, - }, -}; - - -/******************************** - * MSI Capability - */ - -/* Helper */ -#define xen_pt_msi_check_type(offset, flags, what) \ - ((offset) == ((flags) & PCI_MSI_FLAGS_64BIT ? \ - PCI_MSI_##what##_64 : PCI_MSI_##what##_32)) - -/* Message Control register */ -static int xen_pt_msgctrl_reg_init(XenPCIPassthroughState *s, - XenPTRegInfo *reg, uint32_t real_offset, - uint32_t *data) -{ - XenPTMSI *msi = s->msi; - uint16_t reg_field; - int rc; - - /* use I/O device register's value as initial value */ - rc = xen_host_pci_get_word(&s->real_device, real_offset, ®_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, - reg_field & ~PCI_MSI_FLAGS_ENABLE); - } - msi->flags |= reg_field; - msi->ctrl_offset = real_offset; - msi->initialized = false; - msi->mapped = false; - - *data = reg->init_val; - return 0; -} -static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s, - XenPTReg *cfg_entry, uint16_t *val, - uint16_t dev_value, uint16_t valid_mask) -{ - XenPTRegInfo *reg = cfg_entry->reg; - 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) { - XEN_PT_WARN(&s->dev, "Tries to set more than 1 vector ctrl %x\n", *val); - } - - /* modify emulate register */ - writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - *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); - - /* update MSI */ - if (*val & PCI_MSI_FLAGS_ENABLE) { - /* setup MSI pirq for the first time */ - if (!msi->initialized) { - /* Init physical one */ - 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 - * QEMU can go on running. - * Guest MSI would be actually not working. - */ - *val &= ~PCI_MSI_FLAGS_ENABLE; - 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 (register: %x)!\n", *val); - return 0; - } - msi->initialized = true; - msi->mapped = true; - } - msi->flags |= PCI_MSI_FLAGS_ENABLE; - } else if (msi->mapped) { - xen_pt_msi_disable(s); - } - - return 0; -} - -/* initialize Message Upper Address register */ -static int xen_pt_msgaddr64_reg_init(XenPCIPassthroughState *s, - XenPTRegInfo *reg, uint32_t real_offset, - uint32_t *data) -{ - /* no need to initialize in case of 32 bit type */ - if (!(s->msi->flags & PCI_MSI_FLAGS_64BIT)) { - *data = XEN_PT_INVALID_REG; - } else { - *data = reg->init_val; - } - - return 0; -} -/* this function will be called twice (for 32 bit and 64 bit type) */ -/* initialize Message Data register */ -static int xen_pt_msgdata_reg_init(XenPCIPassthroughState *s, - XenPTRegInfo *reg, uint32_t real_offset, - uint32_t *data) -{ - uint32_t flags = s->msi->flags; - uint32_t offset = reg->offset; - - /* check the offset whether matches the type or not */ - if (xen_pt_msi_check_type(offset, flags, DATA)) { - *data = reg->init_val; - } else { - *data = XEN_PT_INVALID_REG; - } - return 0; -} - -/* this function will be called twice (for 32 bit and 64 bit type) */ -/* initialize Mask register */ -static int xen_pt_mask_reg_init(XenPCIPassthroughState *s, - XenPTRegInfo *reg, uint32_t real_offset, - uint32_t *data) -{ - uint32_t flags = s->msi->flags; - - /* check the offset whether matches the type or not */ - if (!(flags & PCI_MSI_FLAGS_MASKBIT)) { - *data = XEN_PT_INVALID_REG; - } else if (xen_pt_msi_check_type(reg->offset, flags, MASK)) { - *data = reg->init_val; - } else { - *data = XEN_PT_INVALID_REG; - } - return 0; -} - -/* this function will be called twice (for 32 bit and 64 bit type) */ -/* initialize Pending register */ -static int xen_pt_pending_reg_init(XenPCIPassthroughState *s, - XenPTRegInfo *reg, uint32_t real_offset, - uint32_t *data) -{ - uint32_t flags = s->msi->flags; - - /* check the offset whether matches the type or not */ - if (!(flags & PCI_MSI_FLAGS_MASKBIT)) { - *data = XEN_PT_INVALID_REG; - } else if (xen_pt_msi_check_type(reg->offset, flags, PENDING)) { - *data = reg->init_val; - } else { - *data = XEN_PT_INVALID_REG; - } - return 0; -} - -/* write Message Address register */ -static int xen_pt_msgaddr32_reg_write(XenPCIPassthroughState *s, - XenPTReg *cfg_entry, uint32_t *val, - uint32_t dev_value, uint32_t valid_mask) -{ - XenPTRegInfo *reg = cfg_entry->reg; - uint32_t writable_mask = 0; - 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; - *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 (*data != old_addr) { - if (s->msi->mapped) { - xen_pt_msi_update(s); - } - } - - return 0; -} -/* write Message Upper Address register */ -static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s, - XenPTReg *cfg_entry, uint32_t *val, - uint32_t dev_value, uint32_t valid_mask) -{ - XenPTRegInfo *reg = cfg_entry->reg; - uint32_t writable_mask = 0; - 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)) { - XEN_PT_ERR(&s->dev, - "Can't write to the upper address without 64 bit support\n"); - return -1; - } - - /* modify emulate register */ - writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); - /* update the msi_info too */ - 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 (*data != old_addr) { - if (s->msi->mapped) { - xen_pt_msi_update(s); - } - } - - return 0; -} - - -/* this function will be called twice (for 32 bit and 64 bit type) */ -/* write Message Data register */ -static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s, - XenPTReg *cfg_entry, uint16_t *val, - uint16_t dev_value, uint16_t valid_mask) -{ - XenPTRegInfo *reg = cfg_entry->reg; - XenPTMSI *msi = s->msi; - uint16_t writable_mask = 0; - 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)) { - /* exit I/O emulator */ - XEN_PT_ERR(&s->dev, "the offset does not match the 32/64 bit type!\n"); - return -1; - } - - /* modify emulate register */ - writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; - *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask); - /* update the msi_info too */ - msi->data = *data; - - /* create value for writing to I/O device register */ - *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0); - - /* update MSI */ - if (*data != old_data) { - if (msi->mapped) { - xen_pt_msi_update(s); - } - } - - return 0; -} - -/* MSI Capability Structure reg static information table */ -static XenPTRegInfo xen_pt_emu_reg_msi[] = { - /* Next Pointer reg */ - { - .offset = PCI_CAP_LIST_NEXT, - .size = 1, - .init_val = 0x00, - .ro_mask = 0xFF, - .emu_mask = 0xFF, - .init = xen_pt_ptr_reg_init, - .u.b.read = xen_pt_byte_reg_read, - .u.b.write = xen_pt_byte_reg_write, - }, - /* Message Control reg */ - { - .offset = PCI_MSI_FLAGS, - .size = 2, - .init_val = 0x0000, - .res_mask = 0xFE00, - .ro_mask = 0x018E, - .emu_mask = 0x017E, - .init = xen_pt_msgctrl_reg_init, - .u.w.read = xen_pt_word_reg_read, - .u.w.write = xen_pt_msgctrl_reg_write, - }, - /* Message Address reg */ - { - .offset = PCI_MSI_ADDRESS_LO, - .size = 4, - .init_val = 0x00000000, - .ro_mask = 0x00000003, - .emu_mask = 0xFFFFFFFF, - .init = xen_pt_common_reg_init, - .u.dw.read = xen_pt_long_reg_read, - .u.dw.write = xen_pt_msgaddr32_reg_write, - }, - /* Message Upper Address reg (if PCI_MSI_FLAGS_64BIT set) */ - { - .offset = PCI_MSI_ADDRESS_HI, - .size = 4, - .init_val = 0x00000000, - .ro_mask = 0x00000000, - .emu_mask = 0xFFFFFFFF, - .init = xen_pt_msgaddr64_reg_init, - .u.dw.read = xen_pt_long_reg_read, - .u.dw.write = xen_pt_msgaddr64_reg_write, - }, - /* Message Data reg (16 bits of data for 32-bit devices) */ - { - .offset = PCI_MSI_DATA_32, - .size = 2, - .init_val = 0x0000, - .ro_mask = 0x0000, - .emu_mask = 0xFFFF, - .init = xen_pt_msgdata_reg_init, - .u.w.read = xen_pt_word_reg_read, - .u.w.write = xen_pt_msgdata_reg_write, - }, - /* Message Data reg (16 bits of data for 64-bit devices) */ - { - .offset = PCI_MSI_DATA_64, - .size = 2, - .init_val = 0x0000, - .ro_mask = 0x0000, - .emu_mask = 0xFFFF, - .init = xen_pt_msgdata_reg_init, - .u.w.read = xen_pt_word_reg_read, - .u.w.write = xen_pt_msgdata_reg_write, - }, - /* Mask reg (if PCI_MSI_FLAGS_MASKBIT set, for 32-bit devices) */ - { - .offset = PCI_MSI_MASK_32, - .size = 4, - .init_val = 0x00000000, - .ro_mask = 0xFFFFFFFF, - .emu_mask = 0xFFFFFFFF, - .init = xen_pt_mask_reg_init, - .u.dw.read = xen_pt_long_reg_read, - .u.dw.write = xen_pt_long_reg_write, - }, - /* Mask reg (if PCI_MSI_FLAGS_MASKBIT set, for 64-bit devices) */ - { - .offset = PCI_MSI_MASK_64, - .size = 4, - .init_val = 0x00000000, - .ro_mask = 0xFFFFFFFF, - .emu_mask = 0xFFFFFFFF, - .init = xen_pt_mask_reg_init, - .u.dw.read = xen_pt_long_reg_read, - .u.dw.write = xen_pt_long_reg_write, - }, - /* Pending reg (if PCI_MSI_FLAGS_MASKBIT set, for 32-bit devices) */ - { - .offset = PCI_MSI_MASK_32 + 4, - .size = 4, - .init_val = 0x00000000, - .ro_mask = 0xFFFFFFFF, - .emu_mask = 0x00000000, - .init = xen_pt_pending_reg_init, - .u.dw.read = xen_pt_long_reg_read, - .u.dw.write = xen_pt_long_reg_write, - }, - /* Pending reg (if PCI_MSI_FLAGS_MASKBIT set, for 64-bit devices) */ - { - .offset = PCI_MSI_MASK_64 + 4, - .size = 4, - .init_val = 0x00000000, - .ro_mask = 0xFFFFFFFF, - .emu_mask = 0x00000000, - .init = xen_pt_pending_reg_init, - .u.dw.read = xen_pt_long_reg_read, - .u.dw.write = xen_pt_long_reg_write, - }, - { - .size = 0, - }, -}; - - -/************************************** - * MSI-X Capability - */ - -/* Message Control register for MSI-X */ -static int xen_pt_msixctrl_reg_init(XenPCIPassthroughState *s, - XenPTRegInfo *reg, uint32_t real_offset, - uint32_t *data) -{ - uint16_t reg_field; - int rc; - - /* use I/O device register's value as initial value */ - rc = xen_host_pci_get_word(&s->real_device, real_offset, ®_field); - if (rc) { - return rc; - } - if (reg_field & PCI_MSIX_FLAGS_ENABLE) { - 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); - } - - s->msix->ctrl_offset = real_offset; - - *data = reg->init_val; - return 0; -} -static int xen_pt_msixctrl_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); - 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; - *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); - - /* update MSI-X */ - if ((*val & PCI_MSIX_FLAGS_ENABLE) - && !(*val & PCI_MSIX_FLAGS_MASKALL)) { - xen_pt_msix_update(s); - } else if (!(*val & PCI_MSIX_FLAGS_ENABLE) && s->msix->enabled) { - 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) { - XEN_PT_LOG(&s->dev, "%s MSI-X\n", - s->msix->enabled ? "enable" : "disable"); - } - - return 0; -} - -/* MSI-X Capability Structure reg static information table */ -static XenPTRegInfo xen_pt_emu_reg_msix[] = { - /* Next Pointer reg */ - { - .offset = PCI_CAP_LIST_NEXT, - .size = 1, - .init_val = 0x00, - .ro_mask = 0xFF, - .emu_mask = 0xFF, - .init = xen_pt_ptr_reg_init, - .u.b.read = xen_pt_byte_reg_read, - .u.b.write = xen_pt_byte_reg_write, - }, - /* Message Control reg */ - { - .offset = PCI_MSI_FLAGS, - .size = 2, - .init_val = 0x0000, - .res_mask = 0x3800, - .ro_mask = 0x07FF, - .emu_mask = 0x0000, - .init = xen_pt_msixctrl_reg_init, - .u.w.read = xen_pt_word_reg_read, - .u.w.write = xen_pt_msixctrl_reg_write, - }, - { - .size = 0, - }, -}; - -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 - */ - -/* capability structure register group size functions */ - -static int xen_pt_reg_grp_size_init(XenPCIPassthroughState *s, - const XenPTRegGroupInfo *grp_reg, - uint32_t base_offset, uint8_t *size) -{ - *size = grp_reg->grp_size; - return 0; -} -/* get Vendor Specific Capability Structure register group size */ -static int xen_pt_vendor_size_init(XenPCIPassthroughState *s, - const XenPTRegGroupInfo *grp_reg, - uint32_t base_offset, uint8_t *size) -{ - 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, - const XenPTRegGroupInfo *grp_reg, - uint32_t base_offset, uint8_t *size) -{ - PCIDevice *d = &s->dev; - uint8_t version = get_capability_version(s, base_offset); - uint8_t type = get_device_type(s, base_offset); - uint8_t pcie_size = 0; - - - /* calculate size depend on capability version and device/port type */ - /* in case of PCI Express Base Specification Rev 1.x */ - if (version == 1) { - /* The PCI Express Capabilities, Device Capabilities, and Device - * Status/Control registers are required for all PCI Express devices. - * The Link Capabilities and Link Status/Control are required for all - * Endpoints that are not Root Complex Integrated Endpoints. Endpoints - * are not required to implement registers other than those listed - * above and terminate the capability structure. - */ - switch (type) { - case PCI_EXP_TYPE_ENDPOINT: - case PCI_EXP_TYPE_LEG_END: - pcie_size = 0x14; - break; - case PCI_EXP_TYPE_RC_END: - /* has no link */ - pcie_size = 0x0C; - break; - /* only EndPoint passthrough is supported */ - case PCI_EXP_TYPE_ROOT_PORT: - case PCI_EXP_TYPE_UPSTREAM: - case PCI_EXP_TYPE_DOWNSTREAM: - case PCI_EXP_TYPE_PCI_BRIDGE: - case PCI_EXP_TYPE_PCIE_BRIDGE: - case PCI_EXP_TYPE_RC_EC: - default: - XEN_PT_ERR(d, "Unsupported device/port type %#x.\n", type); - return -1; - } - } - /* in case of PCI Express Base Specification Rev 2.0 */ - else if (version == 2) { - switch (type) { - case PCI_EXP_TYPE_ENDPOINT: - case PCI_EXP_TYPE_LEG_END: - case PCI_EXP_TYPE_RC_END: - /* For Functions that do not implement the registers, - * these spaces must be hardwired to 0b. - */ - pcie_size = 0x3C; - break; - /* only EndPoint passthrough is supported */ - case PCI_EXP_TYPE_ROOT_PORT: - case PCI_EXP_TYPE_UPSTREAM: - case PCI_EXP_TYPE_DOWNSTREAM: - case PCI_EXP_TYPE_PCI_BRIDGE: - case PCI_EXP_TYPE_PCIE_BRIDGE: - case PCI_EXP_TYPE_RC_EC: - default: - XEN_PT_ERR(d, "Unsupported device/port type %#x.\n", type); - return -1; - } - } else { - XEN_PT_ERR(d, "Unsupported capability version %#x.\n", version); - return -1; - } - - *size = pcie_size; - return 0; -} -/* get MSI Capability Structure register group size */ -static int xen_pt_msi_size_init(XenPCIPassthroughState *s, - const XenPTRegGroupInfo *grp_reg, - uint32_t base_offset, uint8_t *size) -{ - uint16_t msg_ctrl = 0; - uint8_t msi_size = 0xa; - int rc; - - 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; - } - if (msg_ctrl & PCI_MSI_FLAGS_MASKBIT) { - msi_size += 10; - } - - s->msi = g_new0(XenPTMSI, 1); - s->msi->pirq = XEN_PT_UNASSIGNED_PIRQ; - - *size = msi_size; - return 0; -} -/* get MSI-X Capability Structure register group size */ -static int xen_pt_msix_size_init(XenPCIPassthroughState *s, - const XenPTRegGroupInfo *grp_reg, - uint32_t base_offset, uint8_t *size) -{ - int rc = 0; - - rc = xen_pt_msix_init(s, base_offset); - - if (rc < 0) { - XEN_PT_ERR(&s->dev, "Internal error: Invalid xen_pt_msix_init.\n"); - return rc; - } - - *size = grp_reg->grp_size; - return 0; -} - - -static const XenPTRegGroupInfo xen_pt_emu_reg_grps[] = { - /* Header Type0 reg group */ - { - .grp_id = 0xFF, - .grp_type = XEN_PT_GRP_TYPE_EMU, - .grp_size = 0x40, - .size_init = xen_pt_reg_grp_size_init, - .emu_regs = xen_pt_emu_reg_header0, - }, - /* PCI PowerManagement Capability reg group */ - { - .grp_id = PCI_CAP_ID_PM, - .grp_type = XEN_PT_GRP_TYPE_EMU, - .grp_size = PCI_PM_SIZEOF, - .size_init = xen_pt_reg_grp_size_init, - .emu_regs = xen_pt_emu_reg_pm, - }, - /* AGP Capability Structure reg group */ - { - .grp_id = PCI_CAP_ID_AGP, - .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, - .grp_size = 0x30, - .size_init = xen_pt_reg_grp_size_init, - }, - /* Vital Product Data Capability Structure reg group */ - { - .grp_id = PCI_CAP_ID_VPD, - .grp_type = XEN_PT_GRP_TYPE_EMU, - .grp_size = 0x08, - .size_init = xen_pt_reg_grp_size_init, - .emu_regs = xen_pt_emu_reg_vpd, - }, - /* Slot Identification reg group */ - { - .grp_id = PCI_CAP_ID_SLOTID, - .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, - .grp_size = 0x04, - .size_init = xen_pt_reg_grp_size_init, - }, - /* MSI Capability Structure reg group */ - { - .grp_id = PCI_CAP_ID_MSI, - .grp_type = XEN_PT_GRP_TYPE_EMU, - .grp_size = 0xFF, - .size_init = xen_pt_msi_size_init, - .emu_regs = xen_pt_emu_reg_msi, - }, - /* PCI-X Capabilities List Item reg group */ - { - .grp_id = PCI_CAP_ID_PCIX, - .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, - .grp_size = 0x18, - .size_init = xen_pt_reg_grp_size_init, - }, - /* Vendor Specific Capability Structure reg group */ - { - .grp_id = PCI_CAP_ID_VNDR, - .grp_type = XEN_PT_GRP_TYPE_EMU, - .grp_size = 0xFF, - .size_init = xen_pt_vendor_size_init, - .emu_regs = xen_pt_emu_reg_vendor, - }, - /* SHPC Capability List Item reg group */ - { - .grp_id = PCI_CAP_ID_SHPC, - .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, - .grp_size = 0x08, - .size_init = xen_pt_reg_grp_size_init, - }, - /* Subsystem ID and Subsystem Vendor ID Capability List Item reg group */ - { - .grp_id = PCI_CAP_ID_SSVID, - .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, - .grp_size = 0x08, - .size_init = xen_pt_reg_grp_size_init, - }, - /* AGP 8x Capability Structure reg group */ - { - .grp_id = PCI_CAP_ID_AGP3, - .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, - .grp_size = 0x30, - .size_init = xen_pt_reg_grp_size_init, - }, - /* PCI Express Capability Structure reg group */ - { - .grp_id = PCI_CAP_ID_EXP, - .grp_type = XEN_PT_GRP_TYPE_EMU, - .grp_size = 0xFF, - .size_init = xen_pt_pcie_size_init, - .emu_regs = xen_pt_emu_reg_pcie, - }, - /* MSI-X Capability Structure reg group */ - { - .grp_id = PCI_CAP_ID_MSIX, - .grp_type = XEN_PT_GRP_TYPE_EMU, - .grp_size = 0x0C, - .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, - }, -}; - -/* initialize Capabilities Pointer or Next Pointer register */ -static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s, - XenPTRegInfo *reg, uint32_t real_offset, - uint32_t *data) -{ - int i, rc; - uint8_t reg_field; - uint8_t cap_id = 0; - - rc = xen_host_pci_get_byte(&s->real_device, real_offset, ®_field); - if (rc) { - return rc; - } - /* find capability offset */ - while (reg_field) { - for (i = 0; xen_pt_emu_reg_grps[i].grp_size != 0; i++) { - if (xen_pt_hide_dev_cap(&s->real_device, - xen_pt_emu_reg_grps[i].grp_id)) { - continue; - } - - 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; - } - /* ignore the 0 hardwired capability, find next one */ - break; - } - } - - /* next capability */ - rc = xen_host_pci_get_byte(&s->real_device, - reg_field + PCI_CAP_LIST_NEXT, ®_field); - if (rc) { - return rc; - } - } - -out: - *data = reg_field; - return 0; -} - - -/************* - * Main - */ - -static uint8_t find_cap_offset(XenPCIPassthroughState *s, uint8_t cap) -{ - uint8_t id; - unsigned max_cap = XEN_PCI_CAP_MAX; - uint8_t pos = PCI_CAPABILITY_LIST; - uint8_t status = 0; - - if (xen_host_pci_get_byte(&s->real_device, PCI_STATUS, &status)) { - return 0; - } - if ((status & PCI_STATUS_CAP_LIST) == 0) { - return 0; - } - - while (max_cap--) { - if (xen_host_pci_get_byte(&s->real_device, pos, &pos)) { - break; - } - if (pos < PCI_CONFIG_HEADER_SIZE) { - break; - } - - pos &= ~3; - if (xen_host_pci_get_byte(&s->real_device, - pos + PCI_CAP_LIST_ID, &id)) { - break; - } - - if (id == 0xff) { - break; - } - if (id == cap) { - return pos; - } - - pos += PCI_CAP_LIST_NEXT; - } - return 0; -} - -static void xen_pt_config_reg_init(XenPCIPassthroughState *s, - XenPTRegGroup *reg_grp, XenPTRegInfo *reg, - Error **errp) -{ - XenPTReg *reg_entry; - uint32_t data = 0; - int rc = 0; - - reg_entry = g_new0(XenPTReg, 1); - 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); - error_setg(errp, "Init emulate register fail"); - return; - } - if (data == XEN_PT_INVALID_REG) { - /* free unused BAR register entry */ - g_free(reg_entry); - 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 pointer to the data. */ - reg_entry->ptr.byte = s->dev.config + offset; - - } - /* list add register entry */ - QLIST_INSERT_HEAD(®_grp->reg_tbl_list, reg_entry, entries); -} - -void xen_pt_config_init(XenPCIPassthroughState *s, Error **errp) -{ - int i, rc; - Error *err = NULL; - - QLIST_INIT(&s->reg_grps); - - for (i = 0; xen_pt_emu_reg_grps[i].grp_size != 0; i++) { - uint32_t reg_grp_offset = 0; - XenPTRegGroup *reg_grp_entry = NULL; - - 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; - } - - reg_grp_offset = find_cap_offset(s, xen_pt_emu_reg_grps[i].grp_id); - - if (!reg_grp_offset) { - continue; - } - } - - /* - * 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(®_grp_entry->reg_tbl_list); - QLIST_INSERT_HEAD(&s->reg_grps, reg_grp_entry, entries); - - reg_grp_entry->base_offset = reg_grp_offset; - reg_grp_entry->reg_grp = xen_pt_emu_reg_grps + i; - if (xen_pt_emu_reg_grps[i].size_init) { - /* get register group size */ - rc = xen_pt_emu_reg_grps[i].size_init(s, reg_grp_entry->reg_grp, - reg_grp_offset, - ®_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; - } - } - - if (xen_pt_emu_reg_grps[i].grp_type == XEN_PT_GRP_TYPE_EMU) { - 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++) { - 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; - } - } - } - } - } -} - -/* delete all emulate register */ -void xen_pt_config_delete(XenPCIPassthroughState *s) -{ - struct XenPTRegGroup *reg_group, *next_grp; - struct XenPTReg *reg, *next_reg; - - /* free MSI/MSI-X info table */ - if (s->msix) { - 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) { - /* free all register entry */ - QLIST_FOREACH_SAFE(reg, ®_group->reg_tbl_list, entries, next_reg) { - QLIST_REMOVE(reg, entries); - g_free(reg); - } - - QLIST_REMOVE(reg_group, entries); - g_free(reg_group); - } -} diff --git a/qemu/hw/xen/xen_pt_graphics.c b/qemu/hw/xen/xen_pt_graphics.c deleted file mode 100644 index 0f4c8d77e..000000000 --- a/qemu/hw/xen/xen_pt_graphics.c +++ /dev/null @@ -1,275 +0,0 @@ -/* - * 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 deleted file mode 100644 index 9a16f2bff..000000000 --- a/qemu/hw/xen/xen_pt_msi.c +++ /dev/null @@ -1,635 +0,0 @@ -/* - * Copyright (c) 2007, Intel Corporation. - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Jiang Yunhong - * - * This file implements direct PCI assignment to a HVM guest - */ - -#include "qemu/osdep.h" -#include - -#include "hw/xen/xen_backend.h" -#include "xen_pt.h" -#include "hw/i386/apic-msidef.h" - - -#define XEN_PT_AUTO_ASSIGN -1 - -/* shift count for gflags */ -#define XEN_PT_GFLAGS_SHIFT_DEST_ID 0 -#define XEN_PT_GFLAGS_SHIFT_RH 8 -#define XEN_PT_GFLAGS_SHIFT_DM 9 -#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 - */ - -static inline uint8_t msi_vector(uint32_t data) -{ - return (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT; -} - -static inline uint8_t msi_dest_id(uint32_t addr) -{ - return (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT; -} - -static inline uint32_t msi_ext_dest_id(uint32_t addr_hi) -{ - return addr_hi & 0xffffff00; -} - -static uint32_t msi_gflags(uint32_t data, uint64_t addr) -{ - uint32_t result = 0; - int rh, dm, dest_id, deliv_mode, trig_mode; - - rh = (addr >> MSI_ADDR_REDIRECTION_SHIFT) & 0x1; - dm = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1; - dest_id = msi_dest_id(addr); - deliv_mode = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7; - trig_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1; - - result = dest_id | (rh << XEN_PT_GFLAGS_SHIFT_RH) - | (dm << XEN_PT_GFLAGS_SHIFT_DM) - | (deliv_mode << XEN_PT_GFLAGSSHIFT_DELIV_MODE) - | (trig_mode << XEN_PT_GFLAGSSHIFT_TRG_MODE); - - return result; -} - -static inline uint64_t msi_addr64(XenPTMSI *msi) -{ - return (uint64_t)msi->addr_hi << 32 | msi->addr_lo; -} - -static int msi_msix_enable(XenPCIPassthroughState *s, - uint32_t address, - uint16_t flag, - bool enable) -{ - uint16_t val = 0; - int rc; - - if (!address) { - return -1; - } - - 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; - } - 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, - uint64_t addr, - uint32_t data, - int *ppirq, - bool is_msix, - int msix_entry, - bool is_not_mapped) -{ - uint8_t gvec = msi_vector(data); - int rc = 0; - - assert((!is_msix && msix_entry == 0) || is_msix); - - 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, - * try the emulated path */ - *ppirq = XEN_PT_UNASSIGNED_PIRQ; - } else { - XEN_PT_LOG(&s->dev, "requested pirq %d for MSI%s" - " (vec: %#x, entry: %#x)\n", - *ppirq, is_msix ? "-X" : "", gvec, msix_entry); - } - } - - if (is_not_mapped) { - uint64_t table_base = 0; - - if (is_msix) { - table_base = s->msix->table_base; - } - - rc = xc_physdev_map_pirq_msi(xen_xc, xen_domid, XEN_PT_AUTO_ASSIGN, - ppirq, PCI_DEVFN(s->real_device.dev, - s->real_device.func), - s->real_device.bus, - msix_entry, table_base); - if (rc) { - XEN_PT_ERR(&s->dev, - "Mapping of MSI%s (err: %i, vec: %#x, entry %#x)\n", - is_msix ? "-X" : "", errno, gvec, msix_entry); - return rc; - } - } - - return 0; -} -static int msi_msix_update(XenPCIPassthroughState *s, - uint64_t addr, - uint32_t data, - int pirq, - bool is_msix, - int msix_entry, - int *old_pirq) -{ - PCIDevice *d = &s->dev; - uint8_t gvec = msi_vector(data); - uint32_t gflags = msi_gflags(data, addr); - int rc = 0; - uint64_t table_addr = 0; - - XEN_PT_LOG(d, "Updating MSI%s with pirq %d gvec %#x gflags %#x" - " (entry: %#x)\n", - is_msix ? "-X" : "", pirq, gvec, gflags, msix_entry); - - if (is_msix) { - table_addr = s->msix->mmio_base_addr; - } - - rc = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec, - pirq, gflags, table_addr); - - if (rc) { - XEN_PT_ERR(d, "Updating of MSI%s failed. (err: %d)\n", - is_msix ? "-X" : "", errno); - - if (xc_physdev_unmap_pirq(xen_xc, xen_domid, *old_pirq)) { - XEN_PT_ERR(d, "Unmapping of MSI%s pirq %d failed. (err: %d)\n", - is_msix ? "-X" : "", *old_pirq, errno); - } - *old_pirq = XEN_PT_UNASSIGNED_PIRQ; - } - return rc; -} - -static int msi_msix_disable(XenPCIPassthroughState *s, - uint64_t addr, - uint32_t data, - int pirq, - bool is_msix, - bool is_binded) -{ - PCIDevice *d = &s->dev; - uint8_t gvec = msi_vector(data); - uint32_t gflags = msi_gflags(data, addr); - int rc = 0; - - if (pirq == XEN_PT_UNASSIGNED_PIRQ) { - return 0; - } - - if (is_binded) { - XEN_PT_LOG(d, "Unbind MSI%s with pirq %d, gvec %#x\n", - is_msix ? "-X" : "", pirq, gvec); - rc = xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec, pirq, gflags); - if (rc) { - XEN_PT_ERR(d, "Unbinding of MSI%s failed. (err: %d, pirq: %d, gvec: %#x)\n", - is_msix ? "-X" : "", errno, pirq, gvec); - return rc; - } - } - - XEN_PT_LOG(d, "Unmap MSI%s pirq %d\n", is_msix ? "-X" : "", pirq); - rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, pirq); - if (rc) { - XEN_PT_ERR(d, "Unmapping of MSI%s pirq %d failed. (err: %i)\n", - is_msix ? "-X" : "", pirq, errno); - return rc; - } - - return 0; -} - -/* - * MSI virtualization functions - */ - -static int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool enable) -{ - XEN_PT_LOG(&s->dev, "%s MSI.\n", enable ? "enabling" : "disabling"); - - if (!s->msi) { - return -1; - } - - return msi_msix_enable(s, s->msi->ctrl_offset, PCI_MSI_FLAGS_ENABLE, - enable); -} - -/* setup physical msi, but don't enable it */ -int xen_pt_msi_setup(XenPCIPassthroughState *s) -{ - int pirq = XEN_PT_UNASSIGNED_PIRQ; - int rc = 0; - XenPTMSI *msi = s->msi; - - if (msi->initialized) { - XEN_PT_ERR(&s->dev, - "Setup physical MSI when it has been properly initialized.\n"); - return -1; - } - - rc = msi_msix_setup(s, msi_addr64(msi), msi->data, &pirq, false, 0, true); - if (rc) { - return rc; - } - - if (pirq < 0) { - XEN_PT_ERR(&s->dev, "Invalid pirq number: %d.\n", pirq); - return -1; - } - - msi->pirq = pirq; - XEN_PT_LOG(&s->dev, "MSI mapped with pirq %d.\n", pirq); - - return 0; -} - -int xen_pt_msi_update(XenPCIPassthroughState *s) -{ - XenPTMSI *msi = s->msi; - return msi_msix_update(s, msi_addr64(msi), msi->data, msi->pirq, - false, 0, &msi->pirq); -} - -void xen_pt_msi_disable(XenPCIPassthroughState *s) -{ - XenPTMSI *msi = s->msi; - - if (!msi) { - return; - } - - (void)xen_pt_msi_set_enable(s, false); - - msi_msix_disable(s, msi_addr64(msi), msi->data, msi->pirq, false, - msi->initialized); - - /* clear msi info */ - msi->flags &= ~PCI_MSI_FLAGS_ENABLE; - msi->initialized = false; - msi->mapped = false; - msi->pirq = XEN_PT_UNASSIGNED_PIRQ; -} - -/* - * MSI-X virtualization functions - */ - -static int msix_set_enable(XenPCIPassthroughState *s, bool enabled) -{ - XEN_PT_LOG(&s->dev, "%s MSI-X.\n", enabled ? "enabling" : "disabling"); - - if (!s->msix) { - return -1; - } - - return msi_msix_enable(s, s->msix->ctrl_offset, PCI_MSIX_FLAGS_ENABLE, - enabled); -} - -static int xen_pt_msix_update_one(XenPCIPassthroughState *s, int entry_nr, - uint32_t vec_ctrl) -{ - XenPTMSIXEntry *entry = NULL; - int pirq; - int rc; - - if (entry_nr < 0 || entry_nr >= s->msix->total_entries) { - return -EINVAL; - } - - entry = &s->msix->msix_entry[entry_nr]; - - if (!entry->updated) { - return 0; - } - - 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) { - return rc; - } - if (entry->pirq == XEN_PT_UNASSIGNED_PIRQ) { - entry->pirq = pirq; - } - - rc = msi_msix_update(s, entry->addr, entry->data, pirq, true, - entry_nr, &entry->pirq); - - if (!rc) { - entry->updated = false; - } - - return rc; -} - -int xen_pt_msix_update(XenPCIPassthroughState *s) -{ - XenPTMSIX *msix = s->msix; - int i; - - for (i = 0; i < msix->total_entries; i++) { - xen_pt_msix_update_one(s, i, msix->msix_entry[i].latch(VECTOR_CTRL)); - } - - return 0; -} - -void xen_pt_msix_disable(XenPCIPassthroughState *s) -{ - int i = 0; - - msix_set_enable(s, false); - - for (i = 0; i < s->msix->total_entries; i++) { - XenPTMSIXEntry *entry = &s->msix->msix_entry[i]; - - msi_msix_disable(s, entry->addr, entry->data, entry->pirq, true, true); - - /* clear MSI-X info */ - entry->pirq = XEN_PT_UNASSIGNED_PIRQ; - entry->updated = false; - } -} - -int xen_pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index) -{ - XenPTMSIXEntry *entry; - int i, ret; - - if (!(s->msix && s->msix->bar_index == bar_index)) { - return 0; - } - - for (i = 0; i < s->msix->total_entries; i++) { - entry = &s->msix->msix_entry[i]; - if (entry->pirq != XEN_PT_UNASSIGNED_PIRQ) { - ret = xc_domain_unbind_pt_irq(xen_xc, xen_domid, entry->pirq, - PT_IRQ_TYPE_MSI, 0, 0, 0, 0); - if (ret) { - XEN_PT_ERR(&s->dev, "unbind MSI-X entry %d failed (err: %d)\n", - entry->pirq, errno); - } - entry->updated = true; - } - } - return xen_pt_msix_update(s); -} - -static uint32_t get_entry_value(XenPTMSIXEntry *e, int offset) -{ - assert(!(offset % sizeof(*e->latch))); - return e->latch[offset / sizeof(*e->latch)]; -} - -static void set_entry_value(XenPTMSIXEntry *e, int offset, uint32_t val) -{ - assert(!(offset % sizeof(*e->latch))); - e->latch[offset / sizeof(*e->latch)] = val; -} - -static void pci_msix_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - XenPCIPassthroughState *s = opaque; - XenPTMSIX *msix = s->msix; - XenPTMSIXEntry *entry; - unsigned int entry_nr, offset; - - entry_nr = addr / PCI_MSIX_ENTRY_SIZE; - if (entry_nr >= msix->total_entries) { - return; - } - entry = &msix->msix_entry[entry_nr]; - offset = addr % PCI_MSIX_ENTRY_SIZE; - - if (offset != PCI_MSIX_ENTRY_VECTOR_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; - xen_pt_msix_update_one(s, entry_nr, *vec_ctrl); - } - - set_entry_value(entry, offset, val); -} - -static uint64_t pci_msix_read(void *opaque, hwaddr addr, - unsigned size) -{ - XenPCIPassthroughState *s = opaque; - XenPTMSIX *msix = s->msix; - int entry_nr, offset; - - entry_nr = addr / PCI_MSIX_ENTRY_SIZE; - if (entry_nr < 0) { - XEN_PT_ERR(&s->dev, "asked MSI-X entry '%i' invalid!\n", entry_nr); - return 0; - } - - offset = addr % PCI_MSIX_ENTRY_SIZE; - - if (addr < msix->total_entries * PCI_MSIX_ENTRY_SIZE) { - return get_entry_value(&msix->msix_entry[entry_nr], offset); - } else { - /* Pending Bit Array (PBA) */ - return *(uint32_t *)(msix->phys_iomem_base + 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, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .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) -{ - uint8_t id = 0; - uint16_t control = 0; - uint32_t table_off = 0; - int i, total_entries, bar_index; - XenHostPCIDevice *hd = &s->real_device; - PCIDevice *d = &s->dev; - int fd = -1; - XenPTMSIX *msix = NULL; - int rc = 0; - - rc = xen_host_pci_get_byte(hd, base + PCI_CAP_LIST_ID, &id); - if (rc) { - return rc; - } - - if (id != PCI_CAP_ID_MSIX) { - XEN_PT_ERR(d, "Invalid id %#x base %#x\n", id, base); - return -1; - } - - xen_host_pci_get_word(hd, base + PCI_MSIX_FLAGS, &control); - total_entries = control & PCI_MSIX_FLAGS_QSIZE; - total_entries += 1; - - s->msix = g_malloc0(sizeof (XenPTMSIX) - + total_entries * sizeof (XenPTMSIXEntry)); - msix = s->msix; - - msix->total_entries = total_entries; - for (i = 0; i < total_entries; i++) { - msix->msix_entry[i].pirq = XEN_PT_UNASSIGNED_PIRQ; - } - - memory_region_init_io(&msix->mmio, OBJECT(s), &pci_msix_ops, - s, "xen-pci-pt-msix", - (total_entries * PCI_MSIX_ENTRY_SIZE - + XC_PAGE_SIZE - 1) - & XC_PAGE_MASK); - - xen_host_pci_get_long(hd, base + PCI_MSIX_TABLE, &table_off); - bar_index = msix->bar_index = table_off & PCI_MSIX_FLAGS_BIRMASK; - table_off = table_off & ~PCI_MSIX_FLAGS_BIRMASK; - msix->table_base = s->real_device.io_regions[bar_index].base_addr; - XEN_PT_LOG(d, "get MSI-X table BAR base 0x%"PRIx64"\n", msix->table_base); - - fd = open("/dev/mem", O_RDWR); - if (fd == -1) { - rc = -errno; - XEN_PT_ERR(d, "Can't open /dev/mem: %s\n", strerror(errno)); - goto error_out; - } - XEN_PT_LOG(d, "table_off = %#x, total_entries = %d\n", - table_off, total_entries); - msix->table_offset_adjust = table_off & 0x0fff; - msix->phys_iomem_base = - mmap(NULL, - total_entries * PCI_MSIX_ENTRY_SIZE + msix->table_offset_adjust, - PROT_READ, - MAP_SHARED | MAP_LOCKED, - fd, - msix->table_base + table_off - msix->table_offset_adjust); - close(fd); - if (msix->phys_iomem_base == MAP_FAILED) { - rc = -errno; - XEN_PT_ERR(d, "Can't map physical MSI-X table: %s\n", strerror(errno)); - goto error_out; - } - msix->phys_iomem_base = (char *)msix->phys_iomem_base - + msix->table_offset_adjust; - - XEN_PT_LOG(d, "mapping physical MSI-X table to %p\n", - msix->phys_iomem_base); - - memory_region_add_subregion_overlap(&s->bar[bar_index], table_off, - &msix->mmio, - 2); /* Priority: pci default + 1 */ - - return 0; - -error_out: - g_free(s->msix); - s->msix = NULL; - return rc; -} - -void xen_pt_msix_unmap(XenPCIPassthroughState *s) -{ - XenPTMSIX *msix = s->msix; - - if (!msix) { - return; - } - - /* unmap the MSI-X memory mapped register area */ - if (msix->phys_iomem_base) { - XEN_PT_LOG(&s->dev, "unmapping physical MSI-X table from %p\n", - msix->phys_iomem_base); - munmap(msix->phys_iomem_base, msix->total_entries * PCI_MSIX_ENTRY_SIZE - + msix->table_offset_adjust); - } - - 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 deleted file mode 100644 index bbf5873fd..000000000 --- a/qemu/hw/xenpv/Makefile.objs +++ /dev/null @@ -1,4 +0,0 @@ -# Xen PV machine support -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 deleted file mode 100644 index 5a9f5ac80..000000000 --- a/qemu/hw/xenpv/xen_domainbuild.c +++ /dev/null @@ -1,302 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/xen/xen_backend.h" -#include "xen_domainbuild.h" -#include "qemu/timer.h" -#include "qemu/log.h" - -#include - -static int xenstore_domain_mkdir(char *path) -{ - struct xs_permissions perms_ro[] = {{ - .id = 0, /* set owner: dom0 */ - },{ - .id = xen_domid, - .perms = XS_PERM_READ, - }}; - struct xs_permissions perms_rw[] = {{ - .id = 0, /* set owner: dom0 */ - },{ - .id = xen_domid, - .perms = XS_PERM_READ | XS_PERM_WRITE, - }}; - const char *writable[] = { "device", "control", "error", NULL }; - char subpath[256]; - int i; - - if (!xs_mkdir(xenstore, 0, path)) { - fprintf(stderr, "%s: xs_mkdir %s: failed\n", __FUNCTION__, path); - return -1; - } - if (!xs_set_permissions(xenstore, 0, path, perms_ro, 2)) { - fprintf(stderr, "%s: xs_set_permissions failed\n", __FUNCTION__); - return -1; - } - - for (i = 0; writable[i]; i++) { - snprintf(subpath, sizeof(subpath), "%s/%s", path, writable[i]); - if (!xs_mkdir(xenstore, 0, subpath)) { - fprintf(stderr, "%s: xs_mkdir %s: failed\n", __FUNCTION__, subpath); - return -1; - } - if (!xs_set_permissions(xenstore, 0, subpath, perms_rw, 2)) { - fprintf(stderr, "%s: xs_set_permissions failed\n", __FUNCTION__); - return -1; - } - } - return 0; -} - -int xenstore_domain_init1(const char *kernel, const char *ramdisk, - const char *cmdline) -{ - char *dom, uuid_string[42], vm[256], path[256]; - int i; - - snprintf(uuid_string, sizeof(uuid_string), UUID_FMT, - qemu_uuid[0], qemu_uuid[1], qemu_uuid[2], qemu_uuid[3], - qemu_uuid[4], qemu_uuid[5], qemu_uuid[6], qemu_uuid[7], - qemu_uuid[8], qemu_uuid[9], qemu_uuid[10], qemu_uuid[11], - qemu_uuid[12], qemu_uuid[13], qemu_uuid[14], qemu_uuid[15]); - dom = xs_get_domain_path(xenstore, xen_domid); - snprintf(vm, sizeof(vm), "/vm/%s", uuid_string); - - xenstore_domain_mkdir(dom); - - xenstore_write_str(vm, "image/ostype", "linux"); - if (kernel) - xenstore_write_str(vm, "image/kernel", kernel); - if (ramdisk) - xenstore_write_str(vm, "image/ramdisk", ramdisk); - if (cmdline) - xenstore_write_str(vm, "image/cmdline", cmdline); - - /* name + id */ - xenstore_write_str(vm, "name", qemu_name ? qemu_name : "no-name"); - xenstore_write_str(vm, "uuid", uuid_string); - xenstore_write_str(dom, "name", qemu_name ? qemu_name : "no-name"); - xenstore_write_int(dom, "domid", xen_domid); - xenstore_write_str(dom, "vm", vm); - - /* memory */ - xenstore_write_int(dom, "memory/target", ram_size >> 10); // kB - xenstore_write_int(vm, "memory", ram_size >> 20); // MB - xenstore_write_int(vm, "maxmem", ram_size >> 20); // MB - - /* cpus */ - for (i = 0; i < smp_cpus; i++) { - snprintf(path, sizeof(path), "cpu/%d/availability",i); - xenstore_write_str(dom, path, "online"); - } - xenstore_write_int(vm, "vcpu_avail", smp_cpus); - xenstore_write_int(vm, "vcpus", smp_cpus); - - /* vnc password */ - xenstore_write_str(vm, "vncpassword", "" /* FIXME */); - - free(dom); - return 0; -} - -int xenstore_domain_init2(int xenstore_port, int xenstore_mfn, - int console_port, int console_mfn) -{ - char *dom; - - dom = xs_get_domain_path(xenstore, xen_domid); - - /* signal new domain */ - xs_introduce_domain(xenstore, - xen_domid, - xenstore_mfn, - xenstore_port); - - /* xenstore */ - xenstore_write_int(dom, "store/ring-ref", xenstore_mfn); - xenstore_write_int(dom, "store/port", xenstore_port); - - /* console */ - xenstore_write_str(dom, "console/type", "ioemu"); - xenstore_write_int(dom, "console/limit", 128 * 1024); - xenstore_write_int(dom, "console/ring-ref", console_mfn); - xenstore_write_int(dom, "console/port", console_port); - xen_config_dev_console(0); - - free(dom); - return 0; -} - -/* ------------------------------------------------------------- */ - -static QEMUTimer *xen_poll; - -/* check domain state once per second */ -static void xen_domain_poll(void *opaque) -{ - struct xc_dominfo info; - int rc; - - rc = xc_domain_getinfo(xen_xc, xen_domid, 1, &info); - if ((rc != 1) || (info.domid != xen_domid)) { - qemu_log("xen: domain %d is gone\n", xen_domid); - goto quit; - } - if (info.dying) { - qemu_log("xen: domain %d is dying (%s%s)\n", xen_domid, - info.crashed ? "crashed" : "", - info.shutdown ? "shutdown" : ""); - goto quit; - } - - timer_mod(xen_poll, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000); - return; - -quit: - qemu_system_shutdown_request(); -} - -static int xen_domain_watcher(void) -{ - int qemu_running = 1; - int fd[2], i, n, rc; - char byte; - - if (pipe(fd) != 0) { - qemu_log("%s: Huh? pipe error: %s\n", __FUNCTION__, strerror(errno)); - return -1; - } - if (fork() != 0) - return 0; /* not child */ - - /* close all file handles, except stdio/out/err, - * our watch pipe and the xen interface handle */ - n = getdtablesize(); - for (i = 3; i < n; i++) { - if (i == fd[0]) - 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); - - /* wait for qemu exiting */ - while (qemu_running) { - rc = read(fd[0], &byte, 1); - switch (rc) { - case -1: - if (errno == EINTR) - continue; - qemu_log("%s: Huh? read error: %s\n", __FUNCTION__, strerror(errno)); - qemu_running = 0; - break; - case 0: - /* EOF -> qemu exited */ - qemu_running = 0; - break; - default: - qemu_log("%s: Huh? data on the watch pipe?\n", __FUNCTION__); - break; - } - } - - /* cleanup */ - qemu_log("%s: destroy domain %d\n", __FUNCTION__, xen_domid); - xc_domain_destroy(xen_xc, xen_domid); - _exit(0); -} - -/* normal cleanup */ -static void xen_domain_cleanup(void) -{ - char *dom; - - dom = xs_get_domain_path(xenstore, xen_domid); - if (dom) { - xs_rm(xenstore, 0, dom); - free(dom); - } - xs_release_domain(xenstore, xen_domid); -} - -int xen_domain_build_pv(const char *kernel, const char *ramdisk, - const char *cmdline) -{ - uint32_t ssidref = 0; - uint32_t flags = 0; - xen_domain_handle_t uuid; - unsigned int xenstore_port = 0, console_port = 0; - unsigned long xenstore_mfn = 0, console_mfn = 0; - int rc; - - memcpy(uuid, qemu_uuid, sizeof(uuid)); - rc = xen_domain_create(xen_xc, ssidref, uuid, flags, &xen_domid); - if (rc < 0) { - fprintf(stderr, "xen: xc_domain_create() failed\n"); - goto err; - } - qemu_log("xen: created domain %d\n", xen_domid); - atexit(xen_domain_cleanup); - if (xen_domain_watcher() == -1) { - goto err; - } - - xenstore_domain_init1(kernel, ramdisk, cmdline); - - rc = xc_domain_max_vcpus(xen_xc, xen_domid, smp_cpus); - if (rc < 0) { - fprintf(stderr, "xen: xc_domain_max_vcpus() failed\n"); - goto err; - } - -#if 0 - rc = xc_domain_setcpuweight(xen_xc, xen_domid, 256); - if (rc < 0) { - fprintf(stderr, "xen: xc_domain_setcpuweight() failed\n"); - goto err; - } -#endif - - rc = xc_domain_setmaxmem(xen_xc, xen_domid, ram_size >> 10); - if (rc < 0) { - fprintf(stderr, "xen: xc_domain_setmaxmem() failed\n"); - goto err; - } - - xenstore_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0); - console_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0); - - rc = xc_linux_build(xen_xc, xen_domid, ram_size >> 20, - kernel, ramdisk, cmdline, - 0, flags, - xenstore_port, &xenstore_mfn, - console_port, &console_mfn); - if (rc < 0) { - fprintf(stderr, "xen: xc_linux_build() failed\n"); - goto err; - } - - xenstore_domain_init2(xenstore_port, xenstore_mfn, - console_port, console_mfn); - - qemu_log("xen: unpausing domain %d\n", xen_domid); - rc = xc_domain_unpause(xen_xc, xen_domid); - if (rc < 0) { - fprintf(stderr, "xen: xc_domain_unpause() failed\n"); - goto err; - } - - xen_poll = timer_new_ms(QEMU_CLOCK_REALTIME, xen_domain_poll, NULL); - timer_mod(xen_poll, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000); - return 0; - -err: - return -1; -} diff --git a/qemu/hw/xenpv/xen_domainbuild.h b/qemu/hw/xenpv/xen_domainbuild.h deleted file mode 100644 index 29a91ea7b..000000000 --- a/qemu/hw/xenpv/xen_domainbuild.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef QEMU_HW_XEN_DOMAINBUILD_H -#define QEMU_HW_XEN_DOMAINBUILD_H 1 - -#include "hw/xen/xen_common.h" - -int xenstore_domain_init1(const char *kernel, const char *ramdisk, - const char *cmdline); -int xenstore_domain_init2(int xenstore_port, int xenstore_mfn, - int console_port, int console_mfn); -int xen_domain_build_pv(const char *kernel, const char *ramdisk, - const char *cmdline); - -#endif /* QEMU_HW_XEN_DOMAINBUILD_H */ diff --git a/qemu/hw/xenpv/xen_machine_pv.c b/qemu/hw/xenpv/xen_machine_pv.c deleted file mode 100644 index fc1353599..000000000 --- a/qemu/hw/xenpv/xen_machine_pv.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * QEMU Xen PV Machine - * - * Copyright (c) 2007 Red Hat - * - * 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/boards.h" -#include "hw/xen/xen_backend.h" -#include "xen_domainbuild.h" -#include "sysemu/block-backend.h" - -static void xen_init_pv(MachineState *machine) -{ - DriveInfo *dinfo; - int i; - - /* Initialize backend core & drivers */ - if (xen_be_init() != 0) { - fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); - exit(1); - } - - switch (xen_mode) { - case XEN_ATTACH: - /* nothing to do, xend handles everything */ - break; -#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); - xen_be_register("vkbd", &xen_kbdmouse_ops); - xen_be_register("vfb", &xen_framebuffer_ops); - xen_be_register("qdisk", &xen_blkdev_ops); - xen_be_register("qnic", &xen_netdev_ops); - - /* configure framebuffer */ - if (xenfb_enabled) { - xen_config_dev_vfb(0, "vnc"); - xen_config_dev_vkbd(0); - } - - /* configure disks */ - for (i = 0; i < 16; i++) { - dinfo = drive_get(IF_XEN, 0, i); - if (!dinfo) - continue; - xen_config_dev_blk(dinfo); - } - - /* configure nics */ - for (i = 0; i < nb_nics; i++) { - if (!nd_table[i].model || 0 != strcmp(nd_table[i].model, "xen")) - continue; - xen_config_dev_nic(nd_table + i); - } - - /* config cleanup hook */ - atexit(xen_config_cleanup); - - /* setup framebuffer */ - xen_init_display(xen_domid); -} - -static void xenpv_machine_init(MachineClass *mc) -{ - mc->desc = "Xen Para-virtualized PC"; - mc->init = xen_init_pv; - mc->max_cpus = 1; - mc->default_machine_opts = "accel=xen"; -} - -DEFINE_MACHINE("xenpv", xenpv_machine_init) diff --git a/qemu/hw/xtensa/Makefile.objs b/qemu/hw/xtensa/Makefile.objs deleted file mode 100644 index cb77dc379..000000000 --- a/qemu/hw/xtensa/Makefile.objs +++ /dev/null @@ -1,3 +0,0 @@ -obj-y += pic_cpu.o -obj-y += sim.o -obj-y += xtfpga.o diff --git a/qemu/hw/xtensa/bootparam.h b/qemu/hw/xtensa/bootparam.h deleted file mode 100644 index 955f4e86e..000000000 --- a/qemu/hw/xtensa/bootparam.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef HW_XTENSA_BOOTPARAM -#define HW_XTENSA_BOOTPARAM - -#define BP_TAG_COMMAND_LINE 0x1001 /* command line (0-terminated string)*/ -#define BP_TAG_INITRD 0x1002 /* ramdisk addr and size (bp_meminfo) */ -#define BP_TAG_MEMORY 0x1003 /* memory addr and size (bp_meminfo) */ -#define BP_TAG_SERIAL_BAUDRATE 0x1004 /* baud rate of current console. */ -#define BP_TAG_SERIAL_PORT 0x1005 /* serial device of current console */ -#define BP_TAG_FDT 0x1006 /* flat device tree addr */ - -#define BP_TAG_FIRST 0x7B0B /* first tag with a version number */ -#define BP_TAG_LAST 0x7E0B /* last tag */ - -typedef struct BpTag { - uint16_t tag; - uint16_t size; -} BpTag; - -typedef struct BpMemInfo { - uint32_t type; - uint32_t start; - uint32_t end; -} BpMemInfo; - -#define MEMORY_TYPE_CONVENTIONAL 0x1000 -#define MEMORY_TYPE_NONE 0x2000 - -static inline size_t get_tag_size(size_t data_size) -{ - return data_size + sizeof(BpTag) + 4; -} - -static inline ram_addr_t put_tag(ram_addr_t addr, uint16_t tag, - size_t size, const void *data) -{ - BpTag bp_tag = { - .tag = tswap16(tag), - .size = tswap16((size + 3) & ~3), - }; - - cpu_physical_memory_write(addr, &bp_tag, sizeof(bp_tag)); - addr += sizeof(bp_tag); - cpu_physical_memory_write(addr, data, size); - addr += (size + 3) & ~3; - - return addr; -} - -#endif diff --git a/qemu/hw/xtensa/pic_cpu.c b/qemu/hw/xtensa/pic_cpu.c deleted file mode 100644 index c835bd009..000000000 --- a/qemu/hw/xtensa/pic_cpu.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the Open Source and Linux Lab 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 AUTHOR 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 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "qemu/log.h" -#include "qemu/timer.h" - -void xtensa_advance_ccount(CPUXtensaState *env, uint32_t d) -{ - uint32_t old_ccount = env->sregs[CCOUNT] + 1; - - env->sregs[CCOUNT] += d; - - if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) { - int i; - for (i = 0; i < env->config->nccompare; ++i) { - if (env->sregs[CCOMPARE + i] - old_ccount < d) { - xtensa_timer_irq(env, i, 1); - } - } - } -} - -void check_interrupts(CPUXtensaState *env) -{ - CPUState *cs = CPU(xtensa_env_get_cpu(env)); - int minlevel = xtensa_get_cintlevel(env); - uint32_t int_set_enabled = env->sregs[INTSET] & env->sregs[INTENABLE]; - int level; - - /* If the CPU is halted advance CCOUNT according to the QEMU_CLOCK_VIRTUAL time - * elapsed since the moment when it was advanced last time. - */ - if (cs->halted) { - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - xtensa_advance_ccount(env, - muldiv64(now - env->halt_clock, - env->config->clock_freq_khz, 1000000)); - env->halt_clock = now; - } - for (level = env->config->nlevel; level > minlevel; --level) { - if (env->config->level_mask[level] & int_set_enabled) { - env->pending_irq_level = level; - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - qemu_log_mask(CPU_LOG_INT, - "%s level = %d, cintlevel = %d, " - "pc = %08x, a0 = %08x, ps = %08x, " - "intset = %08x, intenable = %08x, " - "ccount = %08x\n", - __func__, level, xtensa_get_cintlevel(env), - env->pc, env->regs[0], env->sregs[PS], - env->sregs[INTSET], env->sregs[INTENABLE], - env->sregs[CCOUNT]); - return; - } - } - env->pending_irq_level = 0; - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); -} - -static void xtensa_set_irq(void *opaque, int irq, int active) -{ - CPUXtensaState *env = opaque; - - if (irq >= env->config->ninterrupt) { - qemu_log("%s: bad IRQ %d\n", __func__, irq); - } else { - uint32_t irq_bit = 1 << irq; - - if (active) { - env->sregs[INTSET] |= irq_bit; - } else if (env->config->interrupt[irq].inttype == INTTYPE_LEVEL) { - env->sregs[INTSET] &= ~irq_bit; - } - - check_interrupts(env); - } -} - -void xtensa_timer_irq(CPUXtensaState *env, uint32_t id, uint32_t active) -{ - qemu_set_irq(env->irq_inputs[env->config->timerint[id]], active); -} - -void xtensa_rearm_ccompare_timer(CPUXtensaState *env) -{ - int i; - uint32_t wake_ccount = env->sregs[CCOUNT] - 1; - - for (i = 0; i < env->config->nccompare; ++i) { - if (env->sregs[CCOMPARE + i] - env->sregs[CCOUNT] < - wake_ccount - env->sregs[CCOUNT]) { - wake_ccount = env->sregs[CCOMPARE + i]; - } - } - env->wake_ccount = wake_ccount; - timer_mod(env->ccompare_timer, env->halt_clock + - muldiv64(wake_ccount - env->sregs[CCOUNT], - 1000000, env->config->clock_freq_khz)); -} - -static void xtensa_ccompare_cb(void *opaque) -{ - XtensaCPU *cpu = opaque; - CPUXtensaState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - if (cs->halted) { - env->halt_clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - xtensa_advance_ccount(env, env->wake_ccount - env->sregs[CCOUNT]); - if (!cpu_has_work(cs)) { - env->sregs[CCOUNT] = env->wake_ccount + 1; - xtensa_rearm_ccompare_timer(env); - } - } -} - -void xtensa_irq_init(CPUXtensaState *env) -{ - XtensaCPU *cpu = xtensa_env_get_cpu(env); - - env->irq_inputs = (void **)qemu_allocate_irqs( - xtensa_set_irq, env, env->config->ninterrupt); - if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT) && - env->config->nccompare > 0) { - env->ccompare_timer = - timer_new_ns(QEMU_CLOCK_VIRTUAL, &xtensa_ccompare_cb, cpu); - } -} - -void *xtensa_get_extint(CPUXtensaState *env, unsigned extint) -{ - if (extint < env->config->nextint) { - unsigned irq = env->config->extint[extint]; - return env->irq_inputs[irq]; - } else { - qemu_log("%s: trying to acquire invalid external interrupt %d\n", - __func__, extint); - return NULL; - } -} diff --git a/qemu/hw/xtensa/sim.c b/qemu/hw/xtensa/sim.c deleted file mode 100644 index 5e9400426..000000000 --- a/qemu/hw/xtensa/sim.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the Open Source and Linux Lab 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 AUTHOR 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 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" -#include "elf.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" -#include "qemu/error-report.h" - -static uint64_t translate_phys_addr(void *opaque, uint64_t addr) -{ - XtensaCPU *cpu = opaque; - - return cpu_get_phys_page_debug(CPU(cpu), addr); -} - -static void sim_reset(void *opaque) -{ - XtensaCPU *cpu = opaque; - - cpu_reset(CPU(cpu)); -} - -static void xtensa_sim_init(MachineState *machine) -{ - XtensaCPU *cpu = NULL; - CPUXtensaState *env = NULL; - MemoryRegion *ram, *rom; - ram_addr_t ram_size = machine->ram_size; - const char *cpu_model = machine->cpu_model; - const char *kernel_filename = machine->kernel_filename; - int n; - - if (!cpu_model) { - cpu_model = XTENSA_DEFAULT_CPU_MODEL; - } - - for (n = 0; n < smp_cpus; n++) { - cpu = cpu_xtensa_init(cpu_model); - if (cpu == NULL) { - error_report("unable to find CPU definition '%s'", - cpu_model); - exit(EXIT_FAILURE); - } - env = &cpu->env; - - env->sregs[PRID] = n; - qemu_register_reset(sim_reset, cpu); - /* Need MMU initialized prior to ELF loading, - * so that ELF gets loaded into virtual addresses - */ - sim_reset(cpu); - } - - ram = g_malloc(sizeof(*ram)); - 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_fatal); - vmstate_register_ram_global(rom); - memory_region_add_subregion(get_system_memory(), 0xfe000000, rom); - - if (kernel_filename) { - uint64_t elf_entry; - uint64_t elf_lowaddr; -#ifdef TARGET_WORDS_BIGENDIAN - int success = load_elf(kernel_filename, translate_phys_addr, cpu, - &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, EM_XTENSA, 0, 0); -#endif - if (success > 0) { - env->pc = elf_entry; - } - } -} - -static void xtensa_sim_machine_init(MachineClass *mc) -{ - mc->desc = "sim machine (" XTENSA_DEFAULT_CPU_MODEL ")"; - mc->is_default = true; - mc->init = xtensa_sim_init; - mc->max_cpus = 4; -} - -DEFINE_MACHINE("sim", xtensa_sim_machine_init) diff --git a/qemu/hw/xtensa/xtfpga.c b/qemu/hw/xtensa/xtfpga.c deleted file mode 100644 index 2d117369a..000000000 --- a/qemu/hw/xtensa/xtfpga.c +++ /dev/null @@ -1,516 +0,0 @@ -/* - * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the Open Source and Linux Lab 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 AUTHOR 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 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" -#include "elf.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" -#include "hw/char/serial.h" -#include "net/net.h" -#include "hw/sysbus.h" -#include "hw/block/flash.h" -#include "sysemu/block-backend.h" -#include "sysemu/char.h" -#include "sysemu/device_tree.h" -#include "qemu/error-report.h" -#include "bootparam.h" - -typedef struct LxBoardDesc { - hwaddr flash_base; - size_t flash_size; - size_t flash_boot_base; - size_t flash_sector_size; - size_t sram_size; -} LxBoardDesc; - -typedef struct Lx60FpgaState { - MemoryRegion iomem; - uint32_t leds; - uint32_t switches; -} Lx60FpgaState; - -static void lx60_fpga_reset(void *opaque) -{ - Lx60FpgaState *s = opaque; - - s->leds = 0; - s->switches = 0; -} - -static uint64_t lx60_fpga_read(void *opaque, hwaddr addr, - unsigned size) -{ - Lx60FpgaState *s = opaque; - - switch (addr) { - case 0x0: /*build date code*/ - return 0x09272011; - - case 0x4: /*processor clock frequency, Hz*/ - return 10000000; - - case 0x8: /*LEDs (off = 0, on = 1)*/ - return s->leds; - - case 0xc: /*DIP switches (off = 0, on = 1)*/ - return s->switches; - } - return 0; -} - -static void lx60_fpga_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - Lx60FpgaState *s = opaque; - - switch (addr) { - case 0x8: /*LEDs (off = 0, on = 1)*/ - s->leds = val; - break; - - case 0x10: /*board reset*/ - if (val == 0xdead) { - qemu_system_reset_request(); - } - break; - } -} - -static const MemoryRegionOps lx60_fpga_ops = { - .read = lx60_fpga_read, - .write = lx60_fpga_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static Lx60FpgaState *lx60_fpga_init(MemoryRegion *address_space, - hwaddr base) -{ - Lx60FpgaState *s = g_malloc(sizeof(Lx60FpgaState)); - - memory_region_init_io(&s->iomem, NULL, &lx60_fpga_ops, s, - "lx60.fpga", 0x10000); - memory_region_add_subregion(address_space, base, &s->iomem); - lx60_fpga_reset(s); - qemu_register_reset(lx60_fpga_reset, s); - return s; -} - -static void lx60_net_init(MemoryRegion *address_space, - hwaddr base, - hwaddr descriptors, - hwaddr buffers, - qemu_irq irq, NICInfo *nd) -{ - DeviceState *dev; - SysBusDevice *s; - MemoryRegion *ram; - - dev = qdev_create(NULL, "open_eth"); - qdev_set_nic_properties(dev, nd); - qdev_init_nofail(dev); - - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, irq); - memory_region_add_subregion(address_space, base, - sysbus_mmio_get_region(s, 0)); - memory_region_add_subregion(address_space, descriptors, - sysbus_mmio_get_region(s, 1)); - - ram = g_malloc(sizeof(*ram)); - 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; - - return cpu_get_phys_page_debug(CPU(cpu), addr); -} - -static void lx60_reset(void *opaque) -{ - XtensaCPU *cpu = opaque; - - cpu_reset(CPU(cpu)); -} - -static uint64_t lx60_io_read(void *opaque, hwaddr addr, - unsigned size) -{ - return 0; -} - -static void lx60_io_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ -} - -static const MemoryRegionOps lx60_io_ops = { - .read = lx60_io_read, - .write = lx60_io_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void lx_init(const LxBoardDesc *board, MachineState *machine) -{ -#ifdef TARGET_WORDS_BIGENDIAN - int be = 1; -#else - int be = 0; -#endif - MemoryRegion *system_memory = get_system_memory(); - XtensaCPU *cpu = NULL; - CPUXtensaState *env = NULL; - MemoryRegion *ram, *rom, *system_io; - DriveInfo *dinfo; - pflash_t *flash = NULL; - QemuOpts *machine_opts = qemu_get_machine_opts(); - const char *cpu_model = machine->cpu_model; - const char *kernel_filename = qemu_opt_get(machine_opts, "kernel"); - const char *kernel_cmdline = qemu_opt_get(machine_opts, "append"); - const char *dtb_filename = qemu_opt_get(machine_opts, "dtb"); - const char *initrd_filename = qemu_opt_get(machine_opts, "initrd"); - int n; - - if (!cpu_model) { - cpu_model = XTENSA_DEFAULT_CPU_MODEL; - } - - for (n = 0; n < smp_cpus; n++) { - cpu = cpu_xtensa_init(cpu_model); - if (cpu == NULL) { - error_report("unable to find CPU definition '%s'", - cpu_model); - exit(EXIT_FAILURE); - } - env = &cpu->env; - - env->sregs[PRID] = n; - qemu_register_reset(lx60_reset, cpu); - /* Need MMU initialized prior to ELF loading, - * so that ELF gets loaded into virtual addresses - */ - cpu_reset(CPU(cpu)); - } - - ram = g_malloc(sizeof(*ram)); - memory_region_init_ram(ram, NULL, "lx60.dram", machine->ram_size, - &error_fatal); - vmstate_register_ram_global(ram); - memory_region_add_subregion(system_memory, 0, ram); - - system_io = g_malloc(sizeof(*system_io)); - memory_region_init_io(system_io, NULL, &lx60_io_ops, NULL, "lx60.io", - 224 * 1024 * 1024); - memory_region_add_subregion(system_memory, 0xf0000000, system_io); - lx60_fpga_init(system_io, 0x0d020000); - if (nd_table[0].used) { - lx60_net_init(system_io, 0x0d030000, 0x0d030400, 0x0d800000, - xtensa_get_extint(env, 1), nd_table); - } - - if (!serial_hds[0]) { - serial_hds[0] = qemu_chr_new("serial0", "null", NULL); - } - - serial_mm_init(system_io, 0x0d050020, 2, xtensa_get_extint(env, 0), - 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); - - dinfo = drive_get(IF_PFLASH, 0, 0); - if (dinfo) { - flash = xtfpga_flash_init(system_io, board, dinfo, be); - } - - /* Use presence of kernel file name as 'boot from SRAM' switch. */ - if (kernel_filename) { - uint32_t entry_point = env->pc; - size_t bp_size = 3 * get_tag_size(0); /* first/last and memory tags */ - uint32_t tagptr = 0xfe000000 + board->sram_size; - uint32_t cur_tagptr; - BpMemInfo memory_location = { - .type = tswap32(MEMORY_TYPE_CONVENTIONAL), - .start = tswap32(0), - .end = tswap32(machine->ram_size), - }; - uint32_t lowmem_end = machine->ram_size < 0x08000000 ? - machine->ram_size : 0x08000000; - uint32_t cur_lowmem = QEMU_ALIGN_UP(lowmem_end / 2, 4096); - - rom = g_malloc(sizeof(*rom)); - memory_region_init_ram(rom, NULL, "lx60.sram", board->sram_size, - &error_fatal); - vmstate_register_ram_global(rom); - memory_region_add_subregion(system_memory, 0xfe000000, rom); - - if (kernel_cmdline) { - bp_size += get_tag_size(strlen(kernel_cmdline) + 1); - } - if (dtb_filename) { - bp_size += get_tag_size(sizeof(uint32_t)); - } - if (initrd_filename) { - bp_size += get_tag_size(sizeof(BpMemInfo)); - } - - /* Put kernel bootparameters to the end of that SRAM */ - tagptr = (tagptr - bp_size) & ~0xff; - cur_tagptr = put_tag(tagptr, BP_TAG_FIRST, 0, NULL); - cur_tagptr = put_tag(cur_tagptr, BP_TAG_MEMORY, - sizeof(memory_location), &memory_location); - - if (kernel_cmdline) { - cur_tagptr = put_tag(cur_tagptr, BP_TAG_COMMAND_LINE, - strlen(kernel_cmdline) + 1, kernel_cmdline); - } - if (dtb_filename) { - int fdt_size; - void *fdt = load_device_tree(dtb_filename, &fdt_size); - uint32_t dtb_addr = tswap32(cur_lowmem); - - if (!fdt) { - error_report("could not load DTB '%s'", dtb_filename); - exit(EXIT_FAILURE); - } - - cpu_physical_memory_write(cur_lowmem, fdt, fdt_size); - cur_tagptr = put_tag(cur_tagptr, BP_TAG_FDT, - sizeof(dtb_addr), &dtb_addr); - cur_lowmem = QEMU_ALIGN_UP(cur_lowmem + fdt_size, 4096); - } - if (initrd_filename) { - BpMemInfo initrd_location = { 0 }; - int initrd_size = load_ramdisk(initrd_filename, cur_lowmem, - lowmem_end - cur_lowmem); - - if (initrd_size < 0) { - initrd_size = load_image_targphys(initrd_filename, - cur_lowmem, - lowmem_end - cur_lowmem); - } - if (initrd_size < 0) { - error_report("could not load initrd '%s'", initrd_filename); - exit(EXIT_FAILURE); - } - initrd_location.start = tswap32(cur_lowmem); - initrd_location.end = tswap32(cur_lowmem + initrd_size); - cur_tagptr = put_tag(cur_tagptr, BP_TAG_INITRD, - sizeof(initrd_location), &initrd_location); - cur_lowmem = QEMU_ALIGN_UP(cur_lowmem + initrd_size, 4096); - } - cur_tagptr = put_tag(cur_tagptr, BP_TAG_LAST, 0, NULL); - env->regs[2] = tagptr; - - uint64_t elf_entry; - uint64_t elf_lowaddr; - int success = load_elf(kernel_filename, translate_phys_addr, cpu, - &elf_entry, &elf_lowaddr, NULL, be, EM_XTENSA, 0, 0); - if (success > 0) { - entry_point = elf_entry; - } else { - hwaddr ep; - int is_linux; - success = load_uimage(kernel_filename, &ep, NULL, &is_linux, - translate_phys_addr, cpu); - if (success > 0 && is_linux) { - entry_point = ep; - } else { - error_report("could not load kernel '%s'", - kernel_filename); - exit(EXIT_FAILURE); - } - } - if (entry_point != env->pc) { - static const uint8_t jx_a0[] = { -#ifdef TARGET_WORDS_BIGENDIAN - 0x0a, 0, 0, -#else - 0xa0, 0, 0, -#endif - }; - env->regs[0] = entry_point; - cpu_physical_memory_write(env->pc, jx_a0, sizeof(jx_a0)); - } - } else { - if (flash) { - MemoryRegion *flash_mr = pflash_cfi01_get_memory(flash); - MemoryRegion *flash_io = g_malloc(sizeof(*flash_io)); - - memory_region_init_alias(flash_io, NULL, "lx60.flash", - flash_mr, board->flash_boot_base, - board->flash_size - board->flash_boot_base < 0x02000000 ? - board->flash_size - board->flash_boot_base : 0x02000000); - memory_region_add_subregion(system_memory, 0xfe000000, - flash_io); - } - } -} - -static void xtensa_lx60_init(MachineState *machine) -{ - static const LxBoardDesc lx60_board = { - .flash_base = 0x08000000, - .flash_size = 0x00400000, - .flash_sector_size = 0x10000, - .sram_size = 0x20000, - }; - lx_init(&lx60_board, machine); -} - -static void xtensa_lx200_init(MachineState *machine) -{ - static const LxBoardDesc lx200_board = { - .flash_base = 0x08000000, - .flash_size = 0x01000000, - .flash_sector_size = 0x20000, - .sram_size = 0x2000000, - }; - lx_init(&lx200_board, machine); -} - -static void xtensa_ml605_init(MachineState *machine) -{ - static const LxBoardDesc ml605_board = { - .flash_base = 0x08000000, - .flash_size = 0x01000000, - .flash_sector_size = 0x20000, - .sram_size = 0x2000000, - }; - lx_init(&ml605_board, machine); -} - -static void xtensa_kc705_init(MachineState *machine) -{ - static const LxBoardDesc kc705_board = { - .flash_base = 0x00000000, - .flash_size = 0x08000000, - .flash_boot_base = 0x06000000, - .flash_sector_size = 0x20000, - .sram_size = 0x2000000, - }; - lx_init(&kc705_board, machine); -} - -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 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 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 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) -{ - type_register_static(&xtensa_lx60_type); - type_register_static(&xtensa_lx200_type); - type_register_static(&xtensa_ml605_type); - type_register_static(&xtensa_kc705_type); -} - -type_init(xtensa_lx_machines_init) -- cgit 1.2.3-korg